Html-webpack-plugin: Why does html-webpack-plugin append slash to the public path?

Created on 3 Jun 2017  路  12Comments  路  Source: jantimon/html-webpack-plugin

I am trying to generate assets url in a specific format, the urls should be like this ?path=application.js instead of just application.js. So I configured publicPath to be ?path=:

module.exports = {
    entry: './index.ts',
    output: {
        filename: 'application.js',
        path: path.resolve(__dirname, 'dist'),
        publicPath: '?path='
    },
    module: {
        // ...
    },
    plugins: [
            new HtmlWebpackPlugin({ template: "index.placeholder.html" })
    ]
}

And I see it works for other loaders, for example ExtractTextPlugin generates css like this:

@font-face {
  font-family: 'Glyphicons Halflings';
  src: url(?path=f4769f9bdb7466be65088239c12046d1.eot); /* correct url here */
}

But HtmlWebpackPlugin adds the '/' after publicPath (application.css transforms to `/application.css``):

<link href="?path=/application.css" rel="stylesheet"></head>

Here it is in the code:

if (publicPath && publicPath.substr(-1) !== '/') {
    publicPath += '/';
}

https://github.com/jantimon/html-webpack-plugin/blob/2d5c42ff18985958b4991eaac615ec9ac467715f/index.js#L99

Is it correct behavior? It looks strange that different plugins treat publicPath in a different ways...

bug wontfix

Most helpful comment

@BurkovBA This is a different issue...

I suggest you do:

output: {
     path: path.join(__dirname, 'dist'),
     publicPath: '/',
     filename: 'static/app.[hash:7].js'
   },

All 12 comments

That definitely look like a bug

Although, I believe the code you pointed to, only impact favicon

Would you mind sending us a PR with a failing unit test (or even better with a fix ;) ), so we can have a better look at it

I'm trying to build my project so it can be run in a non-root path. For example, my app is going to be served at this url: https://www.example.com/somewhere/

So I use this setting in my config:

  output: {
    path: path.join(__dirname, 'dist'),
    publicPath: './',
    filename: 'myapp.[hash].bundle.js'
  },

This plugin injects my javascript as

<script type="text/javascript" src="/myapp.c4769857294680bef977.bundle.js"></script>

So that means it can't load the javascript because it's looking at
https://www.example.com/myapp.c4769857294680bef977.bundle.js
instead of
https://www.example.com/somewhere/myapp.c4769857294680bef977.bundle.js

The weird part is that when I run the dev server it fails because of the extra '/' character. But when I just run webpack to build my production files it doesn't add the extra '/', so it works fine.

I worked around this problem by using an environment variable to tell the difference between dev and prod:

  output: {
    path: path.join(__dirname, 'dist'),
    publicPath: (process.env.NODE_ENV==='dev'?'/':'./'),
    filename: 'myapp.[hash].bundle.js'
  },

Then in my package.json I add the environment variable to the respective scripts:

"scripts":{
"dev-server": "NODE_ENV=dev webpack-dev-server --host=0.0.0.0 --content-base dist/ --history-api-fallback",
"build": "NODE_ENV=production npm-run-all lint validate clean --parallel copy:fonts buildwp",
"buildwp": "webpack"
}

@mnebuerquo I don't thing that the same issue, just set publicPath to ''

Have a similar issue:

In production I'm serving my static assets, generated by Webpack, from聽/static/聽path, e.g.聽https://example.com/static/app[hash:7].js. I want to reproduce that behavior in development server.

Thing is, I generate聽index.html聽with injected statics with聽HtmlWebpackPlugin. And when I set聽devServer.publicPath = '/static/', it starts serving my index.html from聽localhost:8080:/static/, which is completely undesired.

webpack.config.js:

module.exports = {
   ...
   output: {
     path: path.join(__dirname, 'dist'),
     publicPath: '/static/',
     filename: 'app.[hash:7].js'
   },
   plugins: [
     new HtmlWebpackPlugin({
       inject: "body",
       template: "src/index.html",
       filename: "index.html"
     }),
   ],
   ...
   devServer: {
     publicPath: '/static/',
     contentBase: './dist',
     hot: true   },
 } 

How do I make webpack-dev-server serve index.html from聽localhost:8080/, but serve the static assets from聽localhost:8080/static/...?

@BurkovBA This is a different issue...

I suggest you do:

output: {
     path: path.join(__dirname, 'dist'),
     publicPath: '/',
     filename: 'static/app.[hash:7].js'
   },

@mastilver

Ugly hack, but it works. Thanks! =)

I believe, this needs to be fixed, though - the way I see it, index.html isn't supposed to be affected by publicPath. Or are we supposed to use externals for it?

I'll submit a unit-test to BasicSpec.js.

I think I misunderstood your issue, because I don't believe it's an issue

Submitting a failing test, is the best way for us to understand an issue so go ahead ;)

@mastilver you see, there is a small inconsistency between devServer and build modes:

Such config that uses publicPath works while build, but not while dev mode:

   output: {
     path: 'dist/bundles',
     publicPath: '/bundles/'
   },
   devServer: {
     publicPath: '/bundles/'
   },
   plugins: [
            new HtmlWebpackPlugin({ 
                filename: '../index.html' // make index.html be placed in `dist`, not `dist/bundles`
            })
    ]

Whle build chunks are placed to dist/bundles and index.html is placed in dist, so everything works.

But while serving with devServer, bundles are served from /bundle path, but index.html is not available at desired /index.html path.

As you mentioned, there is a workaround, without involving non-standard (non-root) publishPath:

output: {
     path: path.join(__dirname, 'dist'),
     publicPath: '/',
     filename: 'bundles/[name].js'
   },

This may work, but I think HtmlPlugin should allow consistently use standard publicPath option in build and dev modes.

I got this same problem and setting: webpackConfig.output.publicPath = '' solved my problem.

It was intentional but it did not consider query strings or hash fragments

This issue had no activity for at least half a year. It's subject to automatic issue closing if there is no activity in the next 15 days.

Was this page helpful?
0 / 5 - 0 ratings