Search code examples
angularjswebpackpathrelative-pathwebpack-3

Images GETs throw 404 error in production using Webpack and AngularJS


I have this conundrum I can't solve after many tryouts:

I'm unable to load static images in production;

everything is fine while developing (npm run serve), even when serving files from dist (npm run serve:dist)

Versions

Webpack: 3.12.0

file-loader: 1.1.11

url-loader: 1.1.2

AngularJS: 1.6.9

While developing:

My index.html contains this line and webpack won't load without

<base href="/"></base>

while my AngularJS config block contains

$locationProvider.hashPrefix('');

I use an URL like http://localhost:3000/#/appName/listingComponent/users

and I'm able to see every image using the ng-src (sometimes it's dynamically binded) like this

<img ng-src="app/images/image1.png">

the folder structure is like this

ui/
├── conf/
│   ├── browsersync-dist.conf.js
│   ├── browsersync.conf.js
│   ├── gulp.conf.js
│   ├── webpack-dist.conf.js
│   └── webpack.conf.js
├── gulp_tasks/
│   ├── browsersync.js
│   ├── misc.js
│   └── webpack.js
├── node_modules/
├── src/
│   ├── app/
│   │   ├── config.js  //global variables
│   │   ├── favicon.ico
│   │   ├── index.html
│   │   ├── index.js
│   │   ├── index.less
│   │   ├── images/ <--
│   │   ├── actual_app_folders/
│   │   └── ...
├── gulpfile.js
└── package.json

In my webpack-dist.js I have the following

module.exports = {
  module: {
    loaders: [
      {
        test: /\.json$/,
        loaders: [
          'json-loader'
        ]
      },
      {
        test: /\.js$/,
        exclude: [
          /node_modules/
        ],
        loader: 'babel-loader',
        options: {
          presets: ['es2015']
        }
      },
      {
        test: /\.js$/,
        exclude: [
          /node_modules/
        ],
        loaders: [
          'eslint-loader'
        ],
        enforce: 'pre'
      },
      {
        test: /\.(css|less)$/,
        exclude: '/node_modules/roboto-fontface/',
        loaders: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: 'css-loader?minimize!less-loader!postcss-loader'
        })
      },
      {
        test: /\.html$/,
        loaders: [
          'html-loader'
        ]
      },
      {
        test: /\.(jpe?g|png|gif|svg|eot|woff|ttf|svg|woff2)$/,
        loader: 'url-loader?name=[name].[ext]'
      }
    ]
  },
  plugins: [
    new webpack.NoEmitOnErrorsPlugin(),
    FailPlugin,
    new HtmlWebpackPlugin({
      template: conf.path.src('/app/index.html'),
      inject: true,
      chunksSortMode: 'dependency'
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: ['vendors', 'config'],
      minChunks: Infinity
    }),
    new ExtractTextPlugin('index-[contenthash].css'),
    new webpack.LoaderOptionsPlugin({
      options: {
        postcss: () => [autoprefixer]
      }
    })
  ],
  output: {
    path: conf.paths.dist,
    filename: function(output) {
      return output['chunk']['name'] === 'config' ? '[name].js' : '[name]-[hash].js';
    },
    chunkFilename: '[name]-[hash].js'
  },
  entry: {
    app: `./${conf.path.src('/app/index')}`,
    config: `./${conf.path.src('/app/config')}`,
    vendors: Object.keys(pkg.dependencies).concat(['webpack-material-design-icons'])
  }
};

IN PRODUCTION

The build process adds the /customer_company_name/our_company_name/ prefix to the path, which becomes

http://<customer_domain>:<domain_port>/customer_company_name/our_company_name/#/appName/listingComponent/users

while the folder structure is like this

our_company_name/
├── app/
│   ├── images/ <--
│   │   ├── image1.png
│   │   └── image2.png
├── app-<random_number>.js
├── config.js
├── favicon.ico
├── index.html
├── index-<random_number>.css
└── vendors.js

THE PROBLEM

Now every time there's a pic to be shown, the browser gets a 404 error like this

GET http://<customer_domain>:<domain_port>/app/images/image1.png 404 (Not Found)

while the request should be made like this

GET http://<customer_domain>:<domain_port>/customer_company_name/our_company_name/index.js

like every other file in the application does.

What I tried

So far nothing of the following helped:

  1. Change base tag upon build process in the following <base href="/customer_company_name/our_company_name/"></base> (this is currently retained)
  2. Explicitly requiring the pictures together with every other file in the index.js require('./images/ODM_trasp48px.png'); (this is currently retained)
  3. Using publicPath in webpack output.publicPath: '/customer_company_name/our_company_name/' and also output.publicPath: '/'
  4. Using file-loader instead of url-loader (this is currently retained)
  5. Using file-loader with options: { useRelativePath: true }
  6. Using file-loader with options: { publicPath: /customer_company_name/our_company_name/ }

Can you help me lads, please?


Solution

  • SOLUTION Let's start with

    output.publicPath: '/customer_company_name/our_company_name/'

    this will take care of rewriting ng-/src paths correctly as explained here.

    We won't need the html-loader to parse ng-/src, the only thing needed is to always use ng-src, or the build won't be successful.

    Eventually, there's no need of <base> tag and neither the initial slash in every path used to refer to images.

    I'll update this answer as I improve the webpack-dist.conf.js/the whole project for the better.


    No answer given worked per se, hence I'll try combinations of them since some suggestions look legit.

    Eventually, if I won't be able to solve this, I'll try to drop the production path prefix /customer_company_name/our_company_name/.