webpack-contrib / file-loader

File Loader
MIT License
1.86k stars 255 forks source link

asset/resource hash in name #397

Closed sajcics closed 3 years ago

sajcics commented 3 years ago

Hi. When I use file-loader to load images my html can't find relative image path because of type: asset/resource which generates [hash][ext][query] by default.

Here is webpack config:

module.exports = {
  entry: {
    home: path.resolve(__dirname, 'src/home.js'),
    shared: path.resolve(__dirname, 'src/shared),
    ....
  }
  plugins: {
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      filename: 'home.html',
      template: path.resolve(__dirname, 'src/pages/home.html'),
      chunks: ['shared', 'home'],
      hash: true, 
    }),
    ....
  },
  output: {
    filename: 'bundles/[name].[contenthash].js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/',
  },
  module: {
    rules: [
      { test: /\.js$/, exclude: '/node_modules/', loader: 'babel-loader' },
      {
        test: /\.(svg|png|jpg|jpeg|gif)$/i,
        type: 'asset/resource',
        use: [{
          loader: 'file-loader',
          options: {
            outputPath: 'assets/images/', // where to put images
            publicPath: '/src/assets', // where to find images
            esModule: false,
          },
        }],
        exclude: '/node_modules/',
      },
    ]
  },
   optimization: {
    minimize: true,
    runtimeChunk: true,
    splitChunks: {
      chunks: 'all',
    },
  },
}

Here is my project structure:

dist
  -- assets
     ---css
     ---images
     ---fonts
  -- bundles
  ....html files...

src 
  -- assets
    --- css
    --- fonts
    --- images
  -- pages
  home.js
  ...other entry files...

1) image is created inside dist/assets/images/1eaeb1151521.png by file loader because of type: asset/resource (for example hashed image cat.png ) 2) insde DOM when I request image with <img src="assets/images/cat.png" /> 3) and also import in entry file as import '../assets/images/cat.png' (because I use lozad for lazy-loading and image responsiveness

4) I want to achieve: file loader will figure out how to match [name].[ext] with [hash][ext][query]

but I can't figure out how to reach to step 4 without overriding filename (keep hashed). any ideas?

alexander-akait commented 3 years ago

You should not use file-loader with asset/resource, you don't need this loader with webpack anymore

alexander-akait commented 3 years ago

I will update docs in near future

sajcics commented 3 years ago

@alexander-akait but if I don't use file-loader for images then my webpack don't know where to look for images. even if I add alias and have image path ~assets/images/cat.png then browser keeps looking at localhost:8080/assets/css/~assets/images/cat.png (image defined in DOM) or localhost:8080/assets/images/cat.png (image defined in CSS). I have defined public path to /.

Alias definition:

resolve: {
    alias: {
      assets: path.resolve(__dirname, 'src/assets'),
    },
 }

so, do you have any suggestions how to load images successfully without file-loader, image name is hashed and then webpack can match path with hashed one?

alexander-akait commented 3 years ago

Expected, webpack@5 use publicPath: 'auto' by default and you don't need provide public path (if you don't use CDN)

I don't understand your problem, please clarify, don't use ~

alexander-akait commented 3 years ago

If you followed the issue template and filled in all the fields, you would get an answer and a solution long ago, I want to show for the future how formal things is help to other developers

alexander-akait commented 3 years ago

Custom output path https://webpack.js.org/guides/asset-modules/#custom-output-filename (same like outputPath), you don't need publicPath here

sajcics commented 3 years ago

liby commented 3 years ago

@alexander-akait Hello, I still don't understand one thing, please tell me.

DEPREACTED for v5: please consider migrating to asset modules.

I have replaced file loader with asset modules, but I can't find how to set publicPath in asset modules.

Expected, webpack@5 use publicPath: 'auto' by default and you don't need provide public path (if you don't use CDN)

You said that in webpack@5, don't need to set publicPath if not using a CDN. So what should I do if I use a CDN? Continue to use file loader?

In other words, is the following configuration equal before and after migration?

Use file loader:

file.rule.js ```js module.exports = { test: /\.(png|jpg|gif|ttf|eot|svg|woff(2)?)(\?[a-z0-9=&.]+)?$/, loader: "file-loader", options: { name: "[name].[contenthash].[ext]", outputPath: "img", publicPath: "/img", } }; ```

Use asset modules:

webpack.config.js ```js module.exports = { ... output: { path: distPath, filename: (chunkData) => { if (chunkData.chunk.name === entryName.devtools) { // Only used in local and development which no cache header return "assets/[name].js"; } return "assets/[name].[contenthash].js"; } }, module: { rules: [ { test: /\.(png|jpg|gif|ttf|eot|svg|woff(2)?)(\?[a-z0-9=&.]+)?$/, type: 'asset/resource', generator: { filename: 'img/[name].[contenthash].[ext]' } } ] } }; ```

I set publicPath:"/img" when using the file loader, will using webpack default settings after migrating to the asset module have any other impact? Are you sure it's okay not to set publicPath here?

The images in the current project are displayed normally. But path like https://example.com/assets/... /img/demo.79e0ee9763e097c6d113.svg looks like an odd path URL.

image

alexander-akait commented 3 years ago

Why do you need CDN publicPath only for assets?

liby commented 3 years ago

@alexander-akait Currently there is no such need, just saw you mention "if you don't use CDN", curious to ask.

So in webpack5, things like https://example.com/assets/... /img/demo.79e0ee9763e097c6d113.svg will not have side effects, right? It can be parsed correctly by any browser.

alexander-akait commented 3 years ago

Yes, but why you need /../?

liby commented 3 years ago

@alexander-akait This is exactly why I said the URL address is strange. I always thought this was the behavior of Webpack@5, configured as follows.

module.exports = {
  ...
  output: {
    path: distPath,
    filename: (chunkData) => {
      if (chunkData.chunk.name === entryName.devtools) {
        // Only used in local and development which no cache header
        return "assets/[name].js";
      }
      return "assets/[name].[contenthash].js";
    }
  },
  module: {
    rules: [
      {
        test: /\.(png|jpg|gif|ttf|eot|svg|woff(2)?)(\?[a-z0-9=&.]+)?$/,
        type: 'asset/resource',
        generator: {
         filename: 'img/[name].[contenthash].[ext]'
       }
      }
    ]
  }
};

Does it look like it's to make the img and asset directories the same level?