1

I think I am Webpacking wrong. In my Webpack config I have a module rule to load files when they are encountered in the scss files, and I am also copying over a ton of other images using the CopyPlugin.

The problem is that when I run Webpack in development with "watch" set to true, the file-loader will delete everything in the dist folder but then copy over the images that are referenced in the SCSS files. So everything else that was copied via the CopyPlugin gets deleted.

Should I be handling the images referenced in my SCSS/JS a different way. Ideally I would be ignoring all the references to these things in the the SCSS/JS and just copy the files manually via the CopyPlugin

Here is my config:

module.exports = {
  mode: 'development',
  watch: true,
  entry: {
    frontend: './_src/entry.js',
  },
  output: {
    filename: 'js/[name].js',
    path: path.resolve(__dirname, paths.distRoot),
  },
  module: {
    rules: [
      // js
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          },
        },
      },
      // css
      {
        test: /\.css$/,
        use: [
          { loader: MiniCssExtractPlugin.loader },
          { loader: 'css-loader' },
        ],
      },
      // scss
      {
        test: /\.scss$/,
        use: [
          { loader: MiniCssExtractPlugin.loader },
          // css 
          {
            loader: 'css-loader',
            options: { sourceMap: true },
          },
          // autoprefix things
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              sourceMap: true,
              plugins: [
                autoprefixer({ browsers: ['last 2 version'] }),
              ],
            },
          },
          // sass
          {
            loader: 'sass-loader',
            options: {
              includePaths: [
                paths.npmRoot + '/font-awesome',
                paths.srcRoot + '/scss',
                paths.npmRoot,
              ],
            },
          },
        ],
      },
      // fonts
      {
        test: /\.(woff(2)?|ttf|eot|svg|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        loader: 'file-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'fonts',
          publicPath: '../fonts',
        },
      },
      // images
      {
        test: /\.(jpg|png|svg|gif)$/,
        loader: 'url-loader',
        options: {
          limit: 1,
          name: '[name].[ext]',
          outputPath: 'img',
          publicPath: '../img',
        },
      },
    ],
  },
  plugins: [
    new CleanWebpackPlugin(),
    new webpack.HotModuleReplacementPlugin(),
    new MiniCssExtractPlugin({
      filename: 'css/[name].css',
    }),
    new CopyPlugin([
      {
        from: path.resolve(__dirname, paths.srcRoot + '/img'),
        to: path.resolve(__dirname, paths.distRoot + '/img'),
        copyUnmodified: true,
      },
    ]),
    new (webpack.optimize.OccurenceOrderPlugin || webpack.optimize.OccurrenceOrderPlugin)(),
  ],
};
rugbert
  • 11,603
  • 9
  • 39
  • 68
  • 1
    Why do you need CopyWebpackPlugin at all? I think you don't need it. You already handled images with url loader and it will load your images to the js output file as base64. Also i cant see file-loader in your configuration. Even if you use file-loader, you dont need copywebpackplugin. – nickbullock Nov 19 '19 at 08:50
  • Because IIRC they file-loader and url-loader will only work when they encounter the images in JS or SCSS. However, some of my images are not _directly_ referenced in those files but can instead be toggled with user input. Thus, those "optional" images never get handled. – rugbert Nov 20 '19 at 18:43

1 Answers1

0

You got it right. Webpack can't get any knowledge about images you didn't referenced directly in js/scss. Some thoughts how to handle images like that:

1) Copy images with copy-webpack-plugin, but don't handle it by any loader. Or use plugin and loader, but set different output paths for it. how to set output path with file loader:

{
        test: /\.(png|jpe?g|gif)$/i,
        loader: 'file-loader',
        options: {
          name: 'my-super-assets/[path][name].[ext]', 
          // OR outputPath: 'my-super-assets'
        },
},

will force your images to my-super-assets folder
and for copy-webpack-plugin

new CopyPlugin([
      {
        from: path.resolve(__dirname, paths.srcRoot + '/img'),
        to: path.resolve(__dirname, paths.distRoot + '/another-super-assets''),
        copyUnmodified: true,
      },
    ]),

2) Load images dynamically on user input with dynamic imports
some simple html/js code how to use it:

<input id="input" type="checkbox">

document.querySelector("#input").addEventListener('change', event => {
  if(event.target.checked) {
    // it will be handled with loader you choose, url-loader or file loader for example
    // or you can write import to js file, which have multiple imports to images already
    import("./assets/foo.svg").then((image) => // image is ready to use)
  }
})

PS: i think the problem you described about webpack with watch mode will not happen with webpack-dev-server (it use in-memory filesystem)

nickbullock
  • 6,424
  • 9
  • 27