We'd like to have two outputs from Webpack - our entire app with all of its dependencies, and a single different page with only one dependency (that isn't shared by the main app).
It seems the way to do this is to leverage the entry
property of a Webpack config. However, that's not enough, as we also use HtmlWebpackPlugin to output our HTML file with the build.js
that Webpack compiled dynamically added (as well as compiled LESS, etc). According to the HtmlWebpackPlugin docs:
If you have multiple Webpack entry points, they will all be included with script tags in the generated HTML.
That won't work for us, so I need to leverage their filterChunks
option. This GitHub issue response states it most succinctly:
module.exports = {
entry: {
'page1': './apps/page1/scripts/main.js',
'page2': './apps/page2/src/main.js'
},
output: {
path: __dirname,
filename: "apps/[name]/build/bundle.js"
},
plugins: [
new HtmlWebpackPlugin({
inject: false,
chunks: ['page1'],
filename: 'apps/page1/build/index.html'
}),
new HtmlWebpackPlugin({
inject: false,
chunks: ['page2'],
filename: 'apps/page2/build/index.html'
})
]
};
(in the HtmlWebpackPlugin docs, this is under the "filtering chunks" section)
So, I modified our code like so:
module.exports = {
entry: {
app: './public/js/ide.js',
resetPassword: './public/js/reset_password.js'
},
output: {
path: path.resolve(__dirname, '../build'),
filename: '[name].js',
publicPath: '/'
},
...
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'public/html/ide.html',
inject: true,
chunks: ['app']
}),
new HtmlWebpackPlugin({
filename: 'reset_password.html',
template: 'public/html/reset_password.html',
inject: true,
chunks: ['resetPassword']
}),
],
}
Now, when I rebuild the project (just trying with WebpackDevServer for now) and navigate to /index.html
, I can see in the network tab the massive bundle file, the contents of index.html
(based off the ide.html
template), as well as requests for various external resources. However, no actual JavaScript will run (say, a console.log in ide.js
). All the HTML in the file shows.
For reset_password.html
, all HTML shows, and the reset_password.js
file shows, but none of the javascript within runs.
How can I ensure the JavaScript in my entry
files runs?
EDIT: I have gotten ide.js
working, because I hadn't realized the following was a "chunk":
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
chunks: 'all'
}
}
}
},
So, I added vendor
to the index.html
HtmlWebpackPlugin chunks
property. Now, it looks like this:
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'public/html/ide.html',
inject: true,
chunks: ['app', 'vendor']
}),
reset_password
doesn't need anything in the node_modules folder, and this also doesn't explain why no JavaScript at all would run inside of ide.js
, so I'm still quite confused. Also, reset_password
is still non-functional.
EDIT2: Looking through the apparently attached reset_password.js
file when I load reset_password.html
, I can see this line
eval("\n\nconsole.log('DRAGONHELLO');//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vLi9wdWJsaWMvanMvcmVzZXRfcGFzc3dvcmQuanM/ZjY5ZSJdLCJuYW1lcyI6WyJjb25zb2xlIiwibG9nIl0sIm1hcHBpbmdzIjoiOztBQUNBQSxRQUFRQyxHQUFSLENBQVksYUFBWiIsImZpbGUiOiIuL3B1YmxpYy9qcy9yZXNldF9wYXNzd29yZC5qcy5qcyIsInNvdXJjZXNDb250ZW50IjpbIlxuY29uc29sZS5sb2coJ0RSQUdPTkhFTExPJylcbiJdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///./public/js/reset_password.js\n");
So, clearly my console.log('DRAGONHELLO')
is "seen", but I have no idea why it isn't running.
EDIT3: Adding vendor
to chunks
for reset_password.html
causes the JavaScript to run, but I have no idea why, and this is non-ideal because the whole point of the exercise was to have two different bundles, one which was very minimal and didn't need all of our node_modules
.
EDIT4: I ran Webpack with profile:true
, and I can see I'm not getting the "Chunk Names" wrong:
js/app.3d18b43294ebd54ed083.js 1.34 MiB 0 [emitted] [big] app
js/resetPassword.198485be2b163cc258ed.js 1.02 KiB 1 [emitted] resetPassword
js/2.e7f92193ea3c611a0b36.js 2.23 MiB 2 [emitted] [big] vendor
js/app.3d18b43294ebd54ed083.js.map 2.71 MiB 0 [emitted] app
js/resetPassword.198485be2b163cc258ed.js.map 4.57 KiB 1 [emitted] resetPassword
js/2.e7f92193ea3c611a0b36.js.map 7.12 MiB 2 [emitted] vendor
EDIT5: I tried both
module.exports = {
//...
optimization: {
runtimeChunk: {
name: entrypoint => `runtime~${entrypoint.name}`
}
}
};
and
module.exports = {
//...
optimization: {
runtimeChunk: true
}
};
Based on PlayMa256's comment and the webpack docs on runtimeChunk. Neither caused the JavaScript to execute.
This is a multipart issue.
First, there is a bug in Html-Webpack-Plugin that makes it incompatible with Webpack4 and multiple entrypoints. It must be upgraded to v4.0.0-alpha.2 at least to work.
Second, in the new version, you needn't use use optimization.splitChunks.cacheGroups
to manually separate out node_modules. Doing optimization.splitChunks.chunks = 'all'
is enough to result in a given entrypoint only getting in its vendors-app-{{chunk_name}}
chunk the node_modules it actually import
s.
So if you do
optimization: {
splitChunks: {
chunks: 'all'
},
},
Combined with
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'public/html/ide.html',
inject: true,
chunks: ['app']
}),
new HtmlWebpackPlugin({
filename: 'reset_password.html',
template: 'public/html/reset_password.html',
inject: true,
chunks: ['resetPassword']
}),
]
Combined with
entry: {
app: './public/js/ide.js',
resetPassword: './public/js/reset_password.js'
},
Then, your webpack output will have
Unless you have no imports
in your resetPassword.js
file, in which case it will look like
More information, images, and conversation at https://github.com/jantimon/html-webpack-plugin/issues/1053
Note that chunksSortMode:
is no longer a valid option on the HtmlWebpackPlugin object for the newest version, it is apparently done by default in webpack4.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With