Skip to content

Commit 3f604a4

Browse files
committed
fix: the same chunkId is overwritten after using mini-css-extract-plugin
1 parent 1008584 commit 3f604a4

File tree

7 files changed

+96
-10
lines changed

7 files changed

+96
-10
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
import("./style.module.css").then((module) => {
1+
import(/* webpackChunkName: "style" */ "./style.module.css").then((module) => {
22
console.log(module["default"] ? "ok" : "error");
33
});

examples/mini-css-extract-plugin/webpack.config.js

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@ const { SubresourceIntegrityPlugin } = require("webpack-subresource-integrity");
22
const HtmlWebpackPlugin = require("html-webpack-plugin");
33
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
44
const { RunInPuppeteerPlugin } = require("wsi-test-helper");
5+
const expect = require("expect");
6+
const fs = require("fs");
7+
const path = require("path");
58

69
module.exports = {
10+
mode: "none",
711
entry: {
812
index: "./index.js",
913
},
@@ -12,14 +16,33 @@ module.exports = {
1216
// Options similar to the same options in webpackOptions.output
1317
// both options are optional
1418
filename: "[name].css",
15-
chunkFilename: "[id].css",
19+
chunkFilename: "[name].css",
1620
}),
1721
new SubresourceIntegrityPlugin({
1822
hashFuncNames: ["sha256", "sha384"],
1923
enabled: true,
2024
}),
2125
new HtmlWebpackPlugin(),
2226
new RunInPuppeteerPlugin(),
27+
{
28+
apply: (compiler) => {
29+
compiler.hooks.done.tap("wsi-test", (stats) => {
30+
expect(stats.compilation.warnings).toEqual([]);
31+
expect(stats.compilation.errors).toEqual([]);
32+
const cssIntegrity = stats
33+
.toJson()
34+
.assets.find((asset) => asset.name === "style.css").integrity;
35+
const source = fs.readFileSync(
36+
path.join(__dirname, "./dist/runtime.js"),
37+
"utf-8"
38+
);
39+
const sriManifest = JSON.parse(
40+
source.match(/__webpack_require__.sriHashes = ({.+});/)?.[1] || "{}"
41+
);
42+
expect(sriManifest["style_css/mini-extract"]).toEqual(cssIntegrity);
43+
});
44+
},
45+
},
2346
],
2447
output: {
2548
crossOriginLoading: "anonymous",
@@ -44,4 +67,10 @@ module.exports = {
4467
},
4568
],
4669
},
70+
optimization: {
71+
chunkIds: "named",
72+
runtimeChunk: {
73+
name: "runtime",
74+
},
75+
},
4776
};

webpack-subresource-integrity/README.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,20 @@ default), the `integrity` attribute will be set automatically. The
5959
`output.crossOriginLoading` webpack option. There is nothing else to
6060
be done.
6161

62+
#### With MiniCssExtractPlugin
63+
64+
Currently, developers can only add integrity to link elements via the insert method.
65+
66+
```js
67+
new MiniCssExtractPlugin({
68+
insert: (link) => {
69+
link.integrity =
70+
__webpack_require__.sriHashes[chunkId + "_css/mini-extract"];
71+
document.head.appendChild(link);
72+
},
73+
})
74+
```
75+
6276
#### With HtmlWebpackPlugin({ inject: false })
6377

6478
When you use html-webpack-plugin with `inject: false`, you are
@@ -221,7 +235,7 @@ With Webpack and long-term caching this means using `[contenthash]` (with
221235
`[contenthash]` with `realContentHash` disabled, or using a different type of
222236
hash placeholder (such as `[chunkhash]`) provides weaker guarantees, which is
223237
why this plugin will output a warning in these cases. See [issue
224-
#162](https://github.com/waysact/webpack-subresource-integrity/issues/162)
238+
# 162](https://github.com/waysact/webpack-subresource-integrity/issues/162)
225239
for more information.
226240

227241
### Proxies
@@ -251,7 +265,7 @@ tags, but preloading with SRI doesn't work as expected in current
251265
Chrome versions. The resource will be loaded twice, defeating the
252266
purpose of preloading. This problem doesn't appear to exist in
253267
Firefox or Safari. See [issue
254-
#111](https://github.com/waysact/webpack-subresource-integrity/issues/111)
268+
# 111](https://github.com/waysact/webpack-subresource-integrity/issues/111)
255269
for more information.
256270

257271
### Browser support
@@ -275,7 +289,7 @@ using a tool such as [`http-server`](https://github.com/indexzero/http-server).
275289
### Safari 13 (and earlier versions) and Assets that Require Cookies
276290

277291
As detailed in [Webpack Issue
278-
#6972](https://github.com/webpack/webpack/issues/6972), the `crossOrigin`
292+
# 6972](https://github.com/webpack/webpack/issues/6972), the `crossOrigin`
279293
attribute can break loading of assets in Safari versions prior to 14 in certain
280294
edge cases due to a browser bug. Since SRI requires the `crossOrigin` attribute
281295
to be set, you may run into this case even when source URL is same-origin with

webpack-subresource-integrity/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,13 +169,13 @@ export class SubresourceIntegrityPlugin {
169169
? plugin.getChildChunksToAddToChunkManifest(chunk)
170170
: findChunks(chunk);
171171
const includedChunks = chunk.getChunkMaps(false).hash;
172-
173172
if (Object.keys(includedChunks).length > 0) {
174173
return compilation.compiler.webpack.Template.asString([
175174
source,
176175
`${sriHashVariableReference} = ` +
177176
JSON.stringify(
178177
generateSriHashPlaceholders(
178+
compilation,
179179
Array.from(allChunks).filter(
180180
(depChunk) =>
181181
depChunk.id !== null &&
@@ -201,6 +201,7 @@ export class SubresourceIntegrityPlugin {
201201
chunk,
202202
new AddLazySriRuntimeModule(
203203
generateSriHashPlaceholders(
204+
compilation,
204205
childChunks,
205206
this.options.hashFuncNames
206207
),

webpack-subresource-integrity/src/plugin.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
tryGetSource,
3333
replaceInSource,
3434
usesAnyHash,
35+
normalizeChunkId,
3536
} from "./util";
3637
import { getChunkToManifestMap } from "./manifest";
3738
import { AssetIntegrity } from "./integrity";
@@ -176,7 +177,10 @@ export class Plugin {
176177

177178
if (childChunk.id !== null) {
178179
this.hashByPlaceholder.set(
179-
makePlaceholder(this.options.hashFuncNames, childChunk.id),
180+
makePlaceholder(
181+
this.options.hashFuncNames,
182+
normalizeChunkId(sourcePath, childChunk, this.compilation)
183+
),
180184
integrity
181185
);
182186
}

webpack-subresource-integrity/src/util.ts

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,23 @@
66
*/
77

88
import { createHash } from "crypto";
9-
import type { AssetInfo, Chunk, Compilation, Compiler, sources } from "webpack";
9+
import type {
10+
AssetInfo,
11+
Chunk,
12+
Compilation,
13+
Compiler,
14+
sources,
15+
Module,
16+
} from "webpack";
1017
import { sep } from "path";
1118
import type { HtmlTagObject } from "./types";
1219

1320
export type ChunkGroup = ReturnType<Compilation["addChunkInGroup"]>;
1421

1522
export const sriHashVariableReference = "__webpack_require__.sriHashes";
1623

24+
export const miniCssExtractType = "css/mini-extract";
25+
1726
export function assert(value: unknown, message: string): asserts value {
1827
if (!value) {
1928
throw new Error(message);
@@ -112,13 +121,28 @@ export function notNil<TValue>(
112121
return value !== null && value !== undefined;
113122
}
114123

124+
export function hasMiniCssExtractModlue(modules: Module[]) {
125+
return modules.find((module) => module.type === miniCssExtractType);
126+
}
127+
115128
export function generateSriHashPlaceholders(
129+
compilation: Compilation,
116130
chunks: Iterable<Chunk>,
117131
hashFuncNames: [string, ...string[]]
118132
): Record<string, string> {
119133
return Array.from(chunks).reduce((sriHashes, depChunk: Chunk) => {
120134
if (depChunk.id) {
121-
sriHashes[depChunk.id] = makePlaceholder(hashFuncNames, depChunk.id);
135+
const modules = compilation.chunkGraph.getChunkModules(depChunk);
136+
const containCssModule = hasMiniCssExtractModlue(modules);
137+
if (containCssModule) {
138+
sriHashes[`${depChunk.id}_${miniCssExtractType}`] = makePlaceholder(
139+
hashFuncNames,
140+
`${depChunk.id}_${miniCssExtractType}`
141+
);
142+
}
143+
if (!containCssModule || (containCssModule && modules.length > 1)) {
144+
sriHashes[depChunk.id] = makePlaceholder(hashFuncNames, depChunk.id);
145+
}
122146
}
123147
return sriHashes;
124148
}, {} as { [key: string]: string });
@@ -291,3 +315,17 @@ export function hasOwnProperty<X extends object, Y extends PropertyKey>(
291315
): obj is X & Record<Y, unknown> {
292316
return Object.prototype.hasOwnProperty.call(obj, prop);
293317
}
318+
319+
export const normalizeChunkId = (
320+
sourcePath: string,
321+
chunk: Chunk,
322+
compilation: Compilation
323+
) => {
324+
if (
325+
sourcePath.endsWith(".css") &&
326+
hasMiniCssExtractModlue(compilation.chunkGraph.getChunkModules(chunk))
327+
) {
328+
return `${chunk.id}_${miniCssExtractType}`;
329+
}
330+
return chunk.id as string | number;
331+
};

yarn.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10073,4 +10073,4 @@ __metadata:
1007310073
z-schema: bin/z-schema
1007410074
checksum: 8a1d66817ae4384dc3f63311f0cccaadd95cc9640eaade5fd3fbf91aa80d6bb82fb95d9b9171fa82ac371a0155b32b7f5f77bbe84dabaca611b66f74c628f0b8
1007510075
languageName: node
10076-
linkType: hard
10076+
linkType: hard

0 commit comments

Comments
 (0)