Roots Discourse

Module build failed: ModuleNotFoundError: Module not found: Error: Can't resolve

Hi guys,

I am almost desperate :frowning:
I am at the time to deploy my first sage theme BUT I am stuck at “BUILD” stage :frowning:

I could work on localhost at ease with no prob, but now I cannot build the dist folder due to an error:

Module build failed: ModuleNotFoundError: Module not found: Error: Can’t resolve ‘…/…/icons/cursor-link.png’ in ‘/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/resources/assets/styles’
at factoryCallback (/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/webpack/lib/Compilation.js:276:40)
at factory (/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/webpack/lib/NormalModuleFactory.js:235:20)
at resolver (/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/webpack/lib/NormalModuleFactory.js:60:20)
at asyncLib.parallel (/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/webpack/lib/NormalModuleFactory.js:127:20)
at /Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/async/dist/async.js:3888:9
at /Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/async/dist/async.js:473:16
at iteratorCallback (/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/async/dist/async.js:1062:13)
at /Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/async/dist/async.js:969:16
at /Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/async/dist/async.js:3885:13
at resolvers.normal.resolve (/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/webpack/lib/NormalModuleFactory.js:119:22)
at onError (/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/enhanced-resolve/lib/Resolver.js:65:10)
at loggingCallbackWrapper (/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/enhanced-resolve/lib/createInnerCallback.js:31:19)
at runAfter (/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/enhanced-resolve/lib/Resolver.js:158:4)
at innerCallback (/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/enhanced-resolve/lib/Resolver.js:146:3)
at loggingCallbackWrapper (/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/enhanced-resolve/lib/createInnerCallback.js:31:19)
at next (/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/tapable/lib/Tapable.js:252:11)
at /Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/enhanced-resolve/lib/UnsafeCachePlugin.js:40:4
at loggingCallbackWrapper (/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/enhanced-resolve/lib/createInnerCallback.js:31:19)
at runAfter (/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/enhanced-resolve/lib/Resolver.js:158:4)
at innerCallback (/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/enhanced-resolve/lib/Resolver.js:146:3)
at loggingCallbackWrapper (/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/enhanced-resolve/lib/createInnerCallback.js:31:19)
at next (/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/tapable/lib/Tapable.js:252:11)
at innerCallback (/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/enhanced-resolve/lib/Resolver.js:144:11)
at loggingCallbackWrapper (/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/enhanced-resolve/lib/createInnerCallback.js:31:19)
at next (/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/tapable/lib/Tapable.js:249:35)
at resolver.doResolve.createInnerCallback (/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/enhanced-resolve/lib/DescriptionFilePlugin.js:44:6)
at loggingCallbackWrapper (/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/enhanced-resolve/lib/createInnerCallback.js:31:19)
at afterInnerCallback (/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/enhanced-resolve/lib/Resolver.js:168:10)
at loggingCallbackWrapper (/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/enhanced-resolve/lib/createInnerCallback.js:31:19)
at next (/Users/matteo/Sites/atravisio.localhost.dev/web/app/themes/av-2021/node_modules/tapable/lib/Tapable.js:252:11)
2 assets
error Command failed with exit code 2.

but the file is loaded correctly in “start” mode :frowning:
and I can work locally with no problems :frowning:

Check the file sizes and if you are using background images, that you are using the background-image css property. The build does not like large files and inline all css background images as base64. If possible you can manually use your cursor-link.png as an svg and manually convert as base64 using https://yoksel.github.io/url-encoder/.

Sage build does not handle image links in CSS backgroundsvery well and could use some improvements

it is just something like:

cursor: url("../../icons/cursor-link.png"), auto;
    cursor: -webkit-image-set(
        url("../../icons/cursor-link.png") 1x,
        url("../../icons/cursor-link@2x.png") 2x
    ), auto;

Does the style have a period before the class name? And do you have the option of using svg? You would then not need the other versions of the image, since the svg is vector.

it also looks like image-set is not widely supported>
https://caniuse.com/css-image-set

hello again, finally could come back with some kind of “error perimetering”

the issue arises for ANY kind of linked image in SCSS:

let’s see this: background-image: url(../../images/logo-chiaro@2x.png);

this is I’d say, physiological, as the development folder structure is

/assets/
  | – /styles/
  | –– /subfolders/
  | – /images/

I figured it out, that, the build fails according to this https://roots.io/docs/sage/9.x/compiling-assets/#theme-assets

therefore, IF I manually update each path to the “build 2 be” version: background-image: url(../images/logo-chiaro@2x.png);
as if they were ALREADY build in dist folder, it works.

is that possible, that the compiling can’t manage the path update on its own and it’s on me?

Sage 9 has issues with file size. url-loader will attempt to base64 and inline the image in your CSS. if the file size is larger than the limits on your Webpack config, the build will fail. Not sure what the sub-folder is, but if it was not configured to be watch, it will most likely fail.

Ok,

So let’s say images in at least 4 cases out of 6, are reasonably small, few kB, how am I supposed to configure webpack to watch the folder?

It’s quite odd I have to manually update all the CSS path, don’t you think?
What if there are dozens of images?

That should have already been configured when you setup sage. I don’t think I understand your last question.

I was quoting you :slight_smile:

which should be the file in charge of building css images?
I want to double check wether anything is correctly configured…

Can you share your config?

yep :slight_smile:

but which file? :smiley:

webpack.config.js

web/app/themes/your-theme/resources/assets/build/webpack.config.js

here you go sir! :slight_smile:

    'use strict'; // eslint-disable-line

const webpack = require('webpack');
const merge = require('webpack-merge');
const CleanPlugin = require('clean-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
//const StyleLintPlugin = require('stylelint-webpack-plugin');
const CopyGlobsPlugin = require('copy-globs-webpack-plugin');
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');

const desire = require('./util/desire');
const config = require('./config');

const assetsFilenames = (config.enabled.cacheBusting) ? config.cacheBusting : '[name]';

let webpackConfig = {
  context: config.paths.assets,
  entry: config.entry,
  devtool: (config.enabled.sourceMaps ? '#source-map' : undefined),
  output: {
    path: config.paths.dist,
    publicPath: config.publicPath,
    filename: `scripts/${assetsFilenames}.js`,
  },
  stats: {
    hash: false,
    version: false,
    timings: false,
    children: false,
    errors: false,
    errorDetails: false,
    warnings: false,
    chunks: false,
    modules: false,
    reasons: false,
    source: false,
    publicPath: false,
  },
  module: {
    rules: [
      {
        enforce: 'pre',
        test: /\.js$/,
        include: config.paths.assets,
        use: 'eslint',
      },
      {
        enforce: 'pre',
        test: /\.(js|s?[ca]ss)$/,
        include: config.paths.assets,
        loader: 'import-glob',
      },
      {
        test: /\.js$/,
        exclude: [/node_modules(?![/|\\](bootstrap|foundation-sites))/],
        use: [
          { loader: 'cache' },
          { loader: 'buble', options: { objectAssign: 'Object.assign' } },
        ],
      },
      {
        test: /\.css$/,
        include: config.paths.assets,
        use: ExtractTextPlugin.extract({
          fallback: 'style',
          use: [
            { loader: 'cache' },
            { loader: 'css', options: { sourceMap: config.enabled.sourceMaps } },
            {
              loader: 'postcss', options: {
                config: { path: __dirname, ctx: config },
                sourceMap: config.enabled.sourceMaps,
              },
            },
          ],
        }),
      },
      {
        test: /\.scss$/,
        include: config.paths.assets,
        use: ExtractTextPlugin.extract({
          fallback: 'style',
          use: [
            { loader: 'cache' },
            { loader: 'css', options: { sourceMap: config.enabled.sourceMaps } },
            {
              loader: 'postcss', options: {
                config: { path: __dirname, ctx: config },
                sourceMap: config.enabled.sourceMaps,
              },
            },
            { loader: 'resolve-url', options: { sourceMap: config.enabled.sourceMaps } },
            {
              loader: 'sass', options: {
                sourceMap: config.enabled.sourceMaps,
                sourceComments: true,
              },
            },
          ],
        }),
      },
      {
        test: /\.(ttf|otf|eot|woff2?|png|jpe?g|gif|svg|ico)$/,
        include: config.paths.assets,
        loader: 'url',
        options: {
          limit: 4096,
          name: `[path]${assetsFilenames}.[ext]`,
        },
      },
      {
        test: /\.(ttf|otf|eot|woff2?|png|jpe?g|gif|svg|ico)$/,
        include: /node_modules/,
        loader: 'url',
        options: {
          limit: 4096,
          outputPath: 'vendor/',
          name: `${config.cacheBusting}.[ext]`,
        },
      },
    ],
  },
  resolve: {
    modules: [
      config.paths.assets,
      'node_modules',
    ],
    enforceExtension: false,
  },
  resolveLoader: {
    moduleExtensions: ['-loader'],
  },
  externals: {
    jquery: 'jQuery',
  },
  plugins: [
    new CleanPlugin([config.paths.dist], {
      root: config.paths.root,
      verbose: false,
    }),
    /**
     * It would be nice to switch to copy-webpack-plugin, but
     * unfortunately it doesn't provide a reliable way of
     * tracking the before/after file names
     */
    new CopyGlobsPlugin({
      pattern: config.copy,
      output: `[path]${assetsFilenames}.[ext]`,
      manifest: config.manifest,
    }),
    new ExtractTextPlugin({
      filename: `styles/${assetsFilenames}.css`,
      allChunks: true,
      disable: (config.enabled.watcher),
    }),
    new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery',
      'window.jQuery': 'jquery',
      Popper: 'popper.js/dist/umd/popper.js',
    }),
    new webpack.LoaderOptionsPlugin({
      minimize: config.enabled.optimize,
      debug: config.enabled.watcher,
      stats: { colors: true },
    }),
    new webpack.LoaderOptionsPlugin({
      test: /\.s?css$/,
      options: {
        output: { path: config.paths.dist },
        context: config.paths.assets,
      },
    }),
    new webpack.LoaderOptionsPlugin({
      test: /\.js$/,
      options: {
        eslint: { failOnWarning: false, failOnError: false },
      },
    }),
    new FriendlyErrorsWebpackPlugin(),
  ],
};

/* eslint-disable global-require */ /** Let's only load dependencies as needed */

if (config.enabled.optimize) {
  webpackConfig = merge(webpackConfig, require('./webpack.config.optimize'));
}

if (config.env.production) {
  webpackConfig.plugins.push(new webpack.NoEmitOnErrorsPlugin());
}

if (config.enabled.cacheBusting) {
  const WebpackAssetsManifest = require('webpack-assets-manifest');

  webpackConfig.plugins.push(
    new WebpackAssetsManifest({
      output: 'assets.json',
      space: 2,
      writeToDisk: false,
      assets: config.manifest,
      replacer: require('./util/assetManifestsFormatter'),
    })
  );
}

if (config.enabled.watcher) {
  webpackConfig.entry = require('./util/addHotMiddleware')(webpackConfig.entry);
  webpackConfig = merge(webpackConfig, require('./webpack.config.watch'));
}

/**
 * During installation via sage-installer (i.e. composer create-project) some
 * presets may generate a preset specific config (webpack.config.preset.js) to
 * override some of the default options set here. We use webpack-merge to merge
 * them in. If you need to modify Sage's default webpack config, we recommend
 * that you modify this file directly, instead of creating your own preset
 * file, as there are limitations to using webpack-merge which can hinder your
 * ability to change certain options.
 */
module.exports = merge.smartStrategy({
  'module.loaders': 'replace',
})(webpackConfig, desire(`${__dirname}/webpack.config.preset`));

Try increasing your limits to: 5096. Any png or svg that is same or below that number it will try to convert to base64 the image in your css file.