Skip to content

Commit 71f0572

Browse files
committed
fix: when using integrity occurs ERROR in RealContentHashPlugin in serv/watch mode after changes
1 parent fbf7414 commit 71f0572

10 files changed

Lines changed: 115 additions & 52 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Change log
22

3+
## 3.17.4 (2024-08-16)
4+
5+
- fix: when using integrity occurs ERROR in RealContentHashPlugin in serv/watch mode after changes in dynamic imported JS files or after adding new import file
6+
37
## 3.17.3 (2024-08-09)
48

59
- fix: in dev mode imports SCSS in JS when in the same file is inlined another SCSS file via `?inline` query, #102

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "html-bundler-webpack-plugin",
3-
"version": "3.17.3",
3+
"version": "3.17.4",
44
"description": "HTML bundler plugin for webpack handles a template as an entry point, extracts CSS and JS from their sources referenced in HTML, supports template engines like Eta, EJS, Handlebars, Nunjucks.",
55
"keywords": [
66
"html",

src/Plugin/Extras/Integrity.js

Lines changed: 69 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,17 @@ class Integrity {
2727

2828
this.options = options;
2929
this.chunkChildChunksMap = new WeakMap();
30-
this.referencePlaceholders = new Map();
31-
this.placeholderByChunkId = new Map();
3230
this.chunkByChunkId = new Map();
31+
this.templateByChunkId = new Map();
32+
this.placeholderByChunkId = new Map();
33+
this.Template = null;
3334
}
3435

3536
apply(compiler) {
3637
const { pluginName } = this;
38+
const { Template } = compiler.webpack;
39+
40+
this.Template = Template;
3741

3842
compiler.hooks.afterPlugins.tap(pluginName, (compiler) => {
3943
compiler.hooks.thisCompilation.tap({ name: pluginName, stage: -10000 }, (compilation) => {
@@ -69,12 +73,12 @@ class Integrity {
6973
this.isRealContentHash = this.options.isRealContentHash();
7074

7175
// dynamically import a JS file
72-
mainTemplate.hooks.jsonpScript.tap(pluginName, (source) => this.addReference('script', source));
73-
mainTemplate.hooks.linkPreload.tap(pluginName, (source) => this.addReference('link', source));
74-
mainTemplate.hooks.localVars.tap(pluginName, this.setReferencePlaceholder.bind(this));
76+
mainTemplate.hooks.jsonpScript.tap(pluginName, (source) => this.getTemplateByTag('script', source));
77+
mainTemplate.hooks.linkPreload.tap(pluginName, (source) => this.getTemplateByTag('link', source));
78+
mainTemplate.hooks.localVars.tap(pluginName, this.getTemplateByChunk.bind(this));
7579

7680
compilation.hooks.beforeRuntimeRequirements.tap(pluginName, () => {
77-
this.placeholderByChunkId.clear();
81+
this.templateByChunkId.clear();
7882
});
7983

8084
compilation.hooks.processAssets.tap(
@@ -111,6 +115,9 @@ class Integrity {
111115
return chunk ? Integrity.getIntegrity(this.compilation, content[0], chunk) : undefined;
112116
}
113117

118+
/**
119+
* @param {Object} assets
120+
*/
114121
processAssets(assets) {
115122
const { compilation, isRealContentHash } = this;
116123
const { compiler } = compilation;
@@ -122,18 +129,15 @@ class Integrity {
122129
continue;
123130
}
124131

125-
let childChunks = this.chunkChildChunksMap.get(chunk);
126-
if (!childChunks) {
127-
childChunks = chunk.getAllAsyncChunks();
128-
this.chunkChildChunksMap.set(chunk, childChunks);
129-
}
132+
const childChunks = this.getChildChunks(chunk);
130133

131134
for (const childChunk of childChunks) {
132135
if (childChunk.id == null) {
133136
continue;
134137
}
135138

136139
// TODO: find the use case when childChunk.files.size > 1
140+
// the size of childChunk.files is always 1
137141
const childChunkFile = [...childChunk.files][0];
138142
const placeholder = this.placeholderByChunkId.get(childChunk.id);
139143

@@ -143,16 +147,22 @@ class Integrity {
143147
childChunkFile,
144148
(source) => source,
145149
(assetInfo) => {
146-
return assetInfo
147-
? {
148-
...assetInfo,
149-
contenthash: Array.isArray(assetInfo.contenthash)
150-
? [...new Set([...assetInfo.contenthash, placeholder])]
151-
: assetInfo.contenthash
152-
? [...new Set([assetInfo.contenthash, placeholder])]
153-
: placeholder,
154-
}
155-
: undefined;
150+
if (!assetInfo) {
151+
return undefined;
152+
}
153+
154+
let contenthash = placeholder;
155+
156+
if (Array.isArray(assetInfo.contenthash)) {
157+
contenthash = [...new Set([...assetInfo.contenthash, placeholder])];
158+
} else if (assetInfo.contenthash) {
159+
contenthash = [...new Set([assetInfo.contenthash, placeholder])];
160+
}
161+
162+
return {
163+
...assetInfo,
164+
contenthash,
165+
};
156166
}
157167
);
158168
} else {
@@ -172,13 +182,28 @@ class Integrity {
172182
}
173183

174184
/**
175-
* Add the reference of integrity hashes into a tag object.
185+
* @param {Chunk} chunk The webpack chunk.
186+
* @return {Set<Chunk>} The child chunks.
187+
*/
188+
getChildChunks(chunk) {
189+
let childChunks = this.chunkChildChunksMap.get(chunk);
190+
191+
if (!childChunks) {
192+
childChunks = chunk.getAllAsyncChunks();
193+
this.chunkChildChunksMap.set(chunk, childChunks);
194+
}
195+
196+
return childChunks;
197+
}
198+
199+
/**
200+
* Create the integrity template by the tag.
176201
*
177202
* @param {string} tagName
178203
* @param {string} source
179204
* @return {string}
180205
*/
181-
addReference = (tagName, source) => {
206+
getTemplateByTag = (tagName, source) => {
182207
const { compilation, pluginName } = this;
183208
const { Template } = compilation.compiler.webpack;
184209
const { crossOriginLoading } = compilation.outputOptions;
@@ -191,40 +216,42 @@ class Integrity {
191216
};
192217

193218
/**
194-
* Set the placeholder in the hash reference using the hash of a chunk file.
219+
* Create the integrity template by the chunk.
220+
*
221+
* Saves the placeholder in the hash reference using the hash of a chunk file.
195222
* When the asset is processed, the placeholder will be replaced
196223
* with real integrity hash of the processed asset.
197224
*
198225
* @param {string} source
199226
* @param {Chunk} chunk
200227
* @return {string}
201228
*/
202-
setReferencePlaceholder(source, chunk) {
203-
const { Template } = this.compilation.compiler.webpack;
204-
205-
if (this.referencePlaceholders.has(chunk.id)) {
206-
return this.referencePlaceholders.get(chunk.id);
229+
getTemplateByChunk(source, chunk) {
230+
if (this.templateByChunkId.has(chunk.id)) {
231+
return this.templateByChunkId.get(chunk.id);
207232
}
208233

209-
const childChunks = chunk.getAllAsyncChunks();
210-
this.chunkChildChunksMap.set(chunk, childChunks);
234+
const childChunks = this.getChildChunks(chunk);
235+
236+
if (childChunks.size < 1) {
237+
return source;
238+
}
211239

212240
const placeholders = {};
213241
for (const childChunk of childChunks) {
214-
const placeholder = getPlaceholder(childChunk.id);
242+
let placeholder = this.placeholderByChunkId.get(childChunk.id);
243+
if (!placeholder) {
244+
placeholder = getPlaceholder(childChunk.id);
245+
this.placeholderByChunkId.set(childChunk.id, placeholder);
246+
}
215247
placeholders[childChunk.id] = placeholder;
216-
this.placeholderByChunkId.set(childChunk.id, placeholder);
217248
this.chunkByChunkId.set(childChunk.id, childChunk);
218249
}
219250

220-
if (Object.keys(placeholders).length > 0) {
221-
const refTemplate = Template.asString([source, `${hashesReference} = ${JSON.stringify(placeholders)};`]);
222-
this.referencePlaceholders.set(chunk.id, refTemplate);
223-
224-
return refTemplate;
225-
}
251+
const template = this.Template.asString([source, `${hashesReference} = ${JSON.stringify(placeholders)};`]);
252+
this.templateByChunkId.set(chunk.id, template);
226253

227-
return source;
254+
return template;
228255
}
229256

230257
/**
@@ -320,10 +347,10 @@ class Integrity {
320347
*/
321348
const getPlaceholder = (chunkId) => {
322349
// the prefix must be exact 7 chars, the same length as a hash function name, e.g. 'sha256-'
323-
const placeholderPrefix = '___TMP-';
350+
const prefix = 'xxxxxx-';
324351
const hash = Integrity.computeIntegrity(chunkId);
325352

326-
return placeholderPrefix + hash.slice(placeholderPrefix.length);
353+
return prefix + hash.slice(prefix.length);
327354
};
328355

329356
module.exports = Integrity;

test/cases/integrity-dynamic-chunks-import-assets-dev/expected/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<title>Dev</title>
55
<!-- load source script -->
66
<link href="main.css" rel="stylesheet" integrity="sha384-P3uRZYJvNVccCqk2iQVGvwDZOYfrwSq1FHv6Ahsf4lBoPPD3ODc0xdEdeom27+CM" crossorigin="anonymous">
7-
<script src="main.6180b06e.js" defer="defer" integrity="sha384-Z4FhgIhPOjwFF+fQqBxQN+0fSOAaqGSti1rzQNtVlipdTz8Ruwa+NFI68hsRe75h" crossorigin="anonymous"></script>
7+
<script src="main.b2802b44.js" defer="defer" integrity="sha384-0oaEWvg3ko+CNViLGy2MgqEQe65Jbi+2m0/2Q0Mmx5UOsruQcchdOezxnn1odKxD" crossorigin="anonymous"></script>
88
</head>
99
<body>
1010
<h1>Hello World!</h1>

test/cases/integrity-dynamic-chunks-import-assets-dev/expected/main.6180b06e.js renamed to test/cases/integrity-dynamic-chunks-import-assets-dev/expected/main.b2802b44.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)