5

I have a react project in which I'm using webpack 3.10.0 with file loader 1.1.6 to move some images and json files into my distribution folder.

Webpack emits the files as expected, but react receives the wrong urls for my json and images assets

My markup looks like:

<img src="../images/helloworld_a49183cf48e4a0f12aa2124fe2ed9d3a.png" 
alt="Hello World!!">

but should be:

<img src="/assets/images/helloworld_a49183cf48e4a0f12aa2124fe2ed9d3a.png" 
alt="Hello World!!">

My folder structure looks like below: C:. ├───dist │ └───assets │ ├───css │ ├───data │ ├───images │ └───js ├───src │ ├───css │ ├───data │ ├───images │ └───js │ ├───actions │ ├───components │ ├───containers │ └───reducers └───tests

I thought file-loader publicPath should take care of this, but it seems I'm either misunderstanding or something is wrong with my configuration. Can someone be so kind to help me out?

webpack.config.js

var path = require('path');
const ExtractCSS = require("extract-text-webpack-plugin");

module.exports = {
  entry: [
    './src/js/index.js'
  ],
  output: {
    path: path.resolve(__dirname, 'dist/assets/js'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['env','react']
          }
        }
      },
      {
        test: /\.scss$/,
        loader: ExtractCSS.extract('css-loader!sass-loader')
      },
      {
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              name (file) {
                /*if (env === 'development') {
                  return '[path][name].[ext]'
              }*/
                return '[name]_[hash].[ext]'
              },
              publicPath: '/assets/images/',
              outputPath: '../images/'
            }  
          }
        ]
      },
      {
        test: /\.json$/,

        use: [
          {
            loader: 'file-loader',
            options: {
              name (file) {
                /*if (env === 'development') {
                  return '[path][name].[ext]'
              }*/
                return '[name].[ext]'
              },
              publicPath: '/assets/data/',
              outputPath: '../data/'
            }  
          }
        ]
      }
    ]
  },
  plugins: [
    new ExtractCSS('../css/app.css', {
        allChunks: true
    }) 
  ],
  resolve: {
    extensions: ['.js', '.jsx']
  },
  //devtool: 'eval-source-map',
  devServer: {
    historyApiFallback: true,
    contentBase: './dist/'
  }
};
Mark Priem
  • 367
  • 2
  • 4
  • 16
  • Comment for a new Webpack 5 world: the built-in asset management will handle fonts well. See https://bytepursuits.com/using-webpack-5-with-font-awesome-5. – ron May 07 '22 at 19:16

3 Answers3

3

So, instead of

{
    test: /\.(png|jpg|gif)$/,
    use: [
      {
        loader: 'file-loader',
        options: {
          name (file) {
            /*if (env === 'development') {
              return '[path][name].[ext]'
          }*/
            return '[name]_[hash].[ext]'
          },
          publicPath: '/assets/images/',
          outputPath: '../images/'
        }  
      }
    ]
  },

You could try something like

{
    test: /\.(png|jpg|gif)$/,
    use: [
      {
        loader: 'file-loader',
        options: {
          name (file) {
            /*if (env === 'development') {
              return '[path][name].[ext]'
          }*/
            return '[name]_[hash].[ext]'
          },
          publicPath: function(url) {
              return url.replace(../, '/assets/')
          },
        }  
      }
    ]
  },
mu_sa
  • 2,685
  • 10
  • 39
  • 58
  • Thanks for the suggestion. It did not work unfortunately. In your example there is no outputpath so the files will end up in the same directory as my bundle. I added the outputpath and the emitted files end up in the correct folder. with out without outputpath still the img src tag reflects the wrong path. – Mark Priem Feb 01 '18 at 14:28
  • I wrapped the first argument of url.replace with quotes by mistake. Now I have removed. Probably that was the reason it didnt work. Anyways glad that you got a solution now – mu_sa Feb 01 '18 at 14:32
  • Thanks but you misunderstood me. I still don't have it working. I created a repo if you want to try: https://github.com/mpriem/BrokenWebPack I really appreciate your help! – Mark Priem Feb 01 '18 at 14:43
1

You can also configure your output object to be a level above and then specify only the name of your files:

  output: {
    path: path.resolve(__dirname, 'dist/assets'),
    filename: 'js/bundle.js'
  },

And for example for the json:

{
    test: /\.json$/,
    use: [
        {
            loader: 'file-loader',
            options: {
                name: 'data/[name].[ext]',
            }
      }
    ]
}
ChrisR
  • 3,922
  • 1
  • 14
  • 24
  • Thanks! I'm going to give it a try. Feels like a workaround though :). Shouldn't file-loader be able to do what I want? – Mark Priem Feb 01 '18 at 14:53
  • Yes, but it does what you want. You specified `../images/` and your link starts with `../images/[...]` so it works. – ChrisR Feb 01 '18 at 15:10
  • 1
    so what is publicPath for then? AFAIK the outputPath should drive the parh in which the files are emitted, and publicPath should be returned to your import or include function. – Mark Priem Feb 01 '18 at 16:59
1

This morning after my machine had rebooted, the issue changed?!!? (caching issue??!)... Instead of ignoring publicPath it now concatenated publicPath with outputPath. Same issue as explained here: https://github.com/webpack-contrib/file-loader/issues/228

Reverting back to 0.10.1 resolves my issue.

Thanks for everyone who tried to help!

Mark Priem
  • 367
  • 2
  • 4
  • 16