Skip to content

Commit

Permalink
fix: if used splitChunks.chunks option then this options will be re…
Browse files Browse the repository at this point in the history
…moved and will be displayed a warning
  • Loading branch information
webdiscus committed Nov 27, 2024
1 parent dcd0ea5 commit f3896e3
Show file tree
Hide file tree
Showing 20 changed files with 246 additions and 14 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Change log

## 4.5.1 (2024-11-28)

- fix: if used `splitChunks.chunks` option then this options will be removed and will be displayed a warning
This option make no sense, because we will split only scripts.
- docs: update readme

## 4.5.0 (2024-11-25)

- feat: add limited support the HMR for styles imported in JavaScript files
Expand Down
32 changes: 22 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,8 @@ module.exports = {
> Don't use Webpack's `output.filename`, hold all relevant settings in one place - in plugin options.\
> Both places have the same effect, but `js.filename` has priority over `output.filename`.
For `splitChunks` see [How to configure splitChunks](#recipe-split-chunks).

No additional template loader is required. The plugin handles templates with base `EJS`-like syntax automatically.
The default templating engine is [Eta](https://eta.js.org).

Expand Down Expand Up @@ -537,7 +539,7 @@ See [boilerplate](https://github.com/webdiscus/webpack-html-scss-boilerplate)
- [How to process a PHP template](#recipe-preprocessor-php)
- [How to pass data into multiple templates](#recipe-pass-data-to-templates)
- [How to use some different template engines](#recipe-diff-templates)
- [How to config `splitChunks`](#recipe-split-chunks)
- [How to configure `splitChunks`](#recipe-split-chunks)
- [How to keep package name for **split chunks** from **node_modules**](#recipe-split-chunks-keep-module-name)
- [How to split CSS files](#recipe-split-css)
2. [Problems & Solutions](#solutions)
Expand Down Expand Up @@ -6251,20 +6253,21 @@ module.exports = {
<a id="recipe-split-chunks" name="recipe-split-chunks"></a>
### How to config `splitChunks`
### How to configure `splitChunks`
Webpack tries to split every entry file, include template files, which completely breaks the compilation process in the plugin.
To avoid this issue, you must specify which scripts should be split, using `optimization.splitChunks.cacheGroups`:
```js
```diff
module.exports = {
optimization: {
splitChunks: {
- chunks: 'all', // <= DO NOT use this here
cacheGroups: {
scripts: {
test: /\.(js|ts)$/, // <= IMPORTANT: split only script files
chunks: 'all',
+ chunks: 'all', // <= DEFINE it here only
},
},
},
Expand All @@ -6276,6 +6279,17 @@ module.exports = {
>
> In the `test` option must be specified all extensions of scripts which should be split.
> ⚠️ **Warning**
>
> DO NOT use the `chunks: 'all'` option globally!
>
> The `splitChunks.chunks: 'all'` option splits all types of chunks, but it make no sense, because we need split only scripts.
> Templates, CSS, images and other assets can't be split.
>
> Define `chunks: 'all'` only in a cache group where is specified the `test` option for your scripts.
>
> ‼️ The `splitChunks.chunks` option will be automatically removed, because some assets can't be resolved or output files may be corrupted!
See details by [splitChunks.cacheGroups](https://webpack.js.org/plugins/split-chunks-plugin/#splitchunkscachegroups).
For example, in a template are used the scripts and styles from `node_modules`:
Expand All @@ -6299,7 +6313,7 @@ For example, in a template are used the scripts and styles from `node_modules`:
> In the generated HTML, all script tags remain in their original places, and the split chunks will be added there
> in the order in which Webpack generated them.
In this use case the `optimization.cacheGroups.{cacheGroup}.test` option must match exactly only JS files from `node_modules`:
In this use case the `optimization.splitChunks.cacheGroups.{cacheGroup}.test` option must match exactly only JS files from `node_modules`:
```js
module.exports = {
Expand All @@ -6309,8 +6323,7 @@ module.exports = {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/].+\.(js|ts)$/, // <= IMPORTANT: split only script files
name: 'vendor',
chunks: 'all',
chunks: 'all', // <= DEFINE it here only
},
},
},
Expand All @@ -6332,7 +6345,7 @@ module.exports = {
### How to keep package name for split chunks from node_modules
To save split chunks under a custom name use `optimization.cacheGroups.{cacheGroup}.name` as function.
To save split chunks under a custom name use `optimization.splitChunks.cacheGroups.{cacheGroup}.name` as function.
For example, many node modules are imported in the `main.js`:
Expand Down Expand Up @@ -6376,12 +6389,11 @@ module.exports = {
optimization: {
runtimeChunk: true,
splitChunks: {
// chunks: 'all', // DO NOT use it here, otherwise the compiled pages will be corrupted
maxSize: 1000000, // split chunks bigger than 100KB, defaults is 20KB
cacheGroups: {
app: {
test: /\.(js|ts)$/, // <= IMPORTANT: split only script files
chunks: 'all', // <= use it only in cache groups
chunks: 'all', // <= define it only in a cache group
name({ context }, chunks, groupName) {
// save split chunks of the node module under package name
if (/[\\/]node_modules[\\/]/.test(context)) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "html-bundler-webpack-plugin",
"version": "4.5.0",
"version": "4.5.1",
"description": "HTML Bundler Plugin for Webpack renders HTML templates containing source files of scripts, styles, images. Supports template engines: Eta, EJS, Handlebars, Nunjucks, Pug, TwigJS. Alternative to html-webpack-plugin.",
"keywords": [
"html",
Expand Down
10 changes: 10 additions & 0 deletions src/Plugin/AssetCompiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -916,6 +916,16 @@ class AssetCompiler {

this.collection.addEntry(entry);

// reserved solution
// problem: if used `splitChunks.chunks` then, some assets may not found in chunkModules
// solution: the `chunks` option must be defined in `splitChunks.cacheGroups.{cacheGroup}.chunks` only
// see the test `resolve-image-in-multipages-splitChunks`
// for (const [key, module] of this.compilation._modules.entries()) {
// if (key.startsWith(ASSET_MODULE_TYPE_RESOURCE)) {
// this.assetResource.saveData(module);
// }
// }

for (const module of chunkModules) {
const { buildInfo, resource, resourceResolveData } = module;
const { isScript, isImportedStyle, isCSSStyleSheet } = resourceResolveData?._bundlerPluginMeta || {};
Expand Down
11 changes: 10 additions & 1 deletion src/Plugin/Collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -288,17 +288,24 @@ class Collection {
const splitChunkIds = new Set();
const chunkCache = new Map();

//console.log('### chunks: ', chunks);
//console.log('### assets: ', assets); // 'img/apple.02a7c382.png': CachedSource
//console.log('### namedChunkGroups: ', namedChunkGroups);
//console.log('### chunkGraph: ', chunkGraph.moduleGraph._moduleMap);

for (let [resource, { type, name, entries }] of this.assets) {
if (type !== Collection.type.script) continue;

const entrypoint = namedChunkGroups.get(name);

//console.log('### auxiliaryFiles: ', { name, entrypoint });

// prevent error when in watch mode after removing a script in the template
if (!entrypoint) continue;

const chunkFiles = new Set();

for (const { id, files } of entrypoint.chunks) {
for (const { id, files, auxiliaryFiles } of entrypoint.chunks) {
for (const file of files) {
const info = assetsInfo.get(file);

Expand All @@ -311,6 +318,8 @@ class Collection {
if (isJavascript && info.hotModuleReplacement !== true) chunkFiles.add(file);
}
splitChunkIds.add(id);

//console.log('### auxiliaryFiles: ', { id, files, auxiliaryFiles });
}

const hasSplitChunks = chunkFiles.size > 1;
Expand Down
31 changes: 31 additions & 0 deletions src/Plugin/Messages/Warnings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const { greenBright, redBright, reset, black } = require('ansis');
const { outToConsole } = require('../../Common/Helpers');
const Config = require('../../Common/Config');

const { pluginLabel } = Config.get();
const headerWarning = `\n${reset.black.bgYellow` ${pluginLabel} ${black.bg(227)` WARNING `}`}\n`;

const optionSplitChunksChunksAllWarning = () => {
const message = `DO NOT use the ${redBright`chunks: 'all'`} option globally!
The ${redBright`splitChunks.chunks`} option is automatically deleted, because it corrupts output files generated by this plugin!
${black.bgYellowBright` SOLUTION `}
Define the ${greenBright`chunks`} option in ${greenBright`'splitChunks.cacheGroups.{group}.chunks'`}, e.g.:
splitChunks: {
${redBright`- chunks: 'all',`} // <= DO NOT use this here
cacheGroups: {
myGroup: {
test: /\\.(js|ts)$/, // <= IMPORTANT: split only script files
${greenBright`+ chunks: 'all',`} // <= DEFINE it here only
},
},
},
`;

outToConsole(headerWarning + message);
};

module.exports = {
optionSplitChunksChunksAllWarning,
};
11 changes: 10 additions & 1 deletion src/Plugin/Option.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const path = require('path');
const { isWin, isFunction, pathToPosix } = require('../Common/Helpers');
const { postprocessException, beforeEmitException } = require('./Messages/Exception');
const { optionSplitChunksChunksAllWarning } = require('./Messages/Warnings');

const Preprocessor = require('../Loader/Preprocessor');
const PluginService = require('../Plugin/PluginService');
Expand Down Expand Up @@ -99,8 +100,16 @@ class Option {
* @param {Object} compiler The Webpack compiler.
*/
initWebpack(compiler) {
const options = compiler.options;
const { entry, js, css } = this.options;
const options = compiler.options;
const splitChunks = options?.optimization?.splitChunks?.chunks;

if (splitChunks && splitChunks === 'all') {
//
delete options.optimization.splitChunks.chunks;
optionSplitChunksChunksAllWarning();
//console.log('*** options: ', options.optimization.splitChunks);
}

this.compiler = compiler;
this.assetEntry = this.pluginContext.assetEntry;
Expand Down
9 changes: 9 additions & 0 deletions test/cases/_warnings/msg-warning-splitChunks/src/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
</head>
<body>
<h1>Hello World!</h1>
</body>
</html>
36 changes: 36 additions & 0 deletions test/cases/_warnings/msg-warning-splitChunks/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const path = require('path');
const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');

module.exports = {
mode: 'production',

output: {
path: path.join(__dirname, 'dist/'),
},

plugins: [
new HtmlBundlerPlugin({
entry: {
index: './src/index.html',
},
}),
],

optimization: {
splitChunks: {
chunks: 'all', // <= DO NOT use this here, it doesn't make sense because we will split only scripts in a group
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/].+\.(js|ts)$/, // <= IMPORTANT: split only script files
name: 'vendor',
chunks: 'all', // <= DEFINE `chunks: 'all'` only in a cache group
},
default: {
minChunks: 2,
reuseExistingChunk: true,
enforce: true,
},
},
},
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<html>
<head>
</head>
<body>
<h1>About</h1>
<!-- test resolving the same image in diff templates: ERROR -->
<img src="img/apple.02a7c382.png" />
<!-- test resolving the diff image in diff templates: OK -->
<img src="img/pear.6b9b072a.png" />
</body>
</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<html>
<head>
</head>
<body>
<h1>Home</h1>
<!-- test resolving the same image in diff templates -->
<img src="img/apple.02a7c382.png" />
</body>
</html>
11 changes: 11 additions & 0 deletions test/cases/resolve-image-in-multipages-splitChunks/src/about.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<html>
<head>
</head>
<body>
<h1>About</h1>
<!-- test resolving the same image in diff templates: ERROR -->
<img src="@images/apple.png" />
<!-- test resolving the diff image in diff templates: OK -->
<img src="@images/pear.png" />
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<html>
<head>
</head>
<body>
<h1>Home</h1>
<!-- test resolving the same image in diff templates -->
<img src="@images/apple.png" />
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('>> main.js');
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
const path = require('path');
const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');

module.exports = {
mode: 'production',

output: {
path: path.join(__dirname, 'dist/'),
},

resolve: {
alias: {
'@images': path.join(__dirname, '../../fixtures/images'),
},
},

plugins: [
new HtmlBundlerPlugin({
entry: {
index: './src/home.html',
about: './src/about.html',
},

verbose: true,
}),
],

module: {
rules: [
{
test: /\.css$/,
use: ['css-loader'],
},
{
test: /\.(png|jpe?g|webp|ico|svg)$/,
type: 'asset/resource',
generator: {
filename: 'img/[name].[hash:8][ext]',
},
},
],
},

optimization: {
splitChunks: {
chunks: 'all', // <= DO NOT use this here, it doesn't make sense because we will split only scripts in a group
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/].+\.(js|ts)$/, // <= IMPORTANT: split only script files
name: 'vendor',
chunks: 'all', // <= DEFINE `chunks: 'all'` only in a cache group
},
default: {
minChunks: 2,
reuseExistingChunk: true,
enforce: true,
},
},
},
},
};
Loading

0 comments on commit f3896e3

Please sign in to comment.