Skip to content

Commit af4f3a3

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

File tree

9 files changed

+219
-8
lines changed

9 files changed

+219
-8
lines changed

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/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,12 @@
5151
"@types/rimraf": "^3.0.2",
5252
"@types/tmp": "^0.2.3",
5353
"cross-spawn": "^7.0.3",
54+
"css-loader": "^6.8.1",
5455
"html-webpack-plugin": ">= 5.0.0-beta.1",
5556
"jest": "^28.1.3",
5657
"lodash": "^4.17.21",
5758
"memfs": "^3.4.1",
59+
"mini-css-extract-plugin": "^2.7.6",
5860
"nyc": "*",
5961
"rimraf": "^3.0.2",
6062
"tapable": "^2.2.1",
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
body {
2+
background: red;
3+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import("./index.css");

webpack-subresource-integrity/src/__tests__/unit.test.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
import { resolve } from "path";
99
import webpack, { Compiler, Compilation, Configuration, Chunk } from "webpack";
1010
import HtmlWebpackPlugin from "html-webpack-plugin";
11+
import MiniCssExtractPlugin from "mini-css-extract-plugin";
12+
import tmp from "tmp-promise";
13+
import fs from "fs";
1114
import { SubresourceIntegrityPlugin } from "..";
1215
import type { SubresourceIntegrityPluginOptions } from "..";
1316
import { assert } from "../util";
@@ -312,3 +315,55 @@ test("errors with unresolved integrity", async () => {
312315
new RegExp("contains unresolved integrity placeholders")
313316
);
314317
});
318+
319+
test("should contain integrity when using mini-css-extract-plugin", async () => {
320+
const plugin = new SubresourceIntegrityPlugin({
321+
hashFuncNames: ["sha256"],
322+
enabled: true,
323+
hashLoading: "eager",
324+
});
325+
const tmpDir = await tmp.dir({ unsafeCleanup: true });
326+
await runCompilation(
327+
webpack({
328+
mode: "none",
329+
entry: resolve(
330+
__dirname,
331+
"./__fixtures__/mini-css-extract-plugin/src/index.js"
332+
),
333+
output: {
334+
path: tmpDir.path,
335+
filename: "[name].js",
336+
crossOriginLoading: "anonymous",
337+
},
338+
module: {
339+
rules: [
340+
{
341+
test: /\.css$/,
342+
use: [MiniCssExtractPlugin.loader, "css-loader"],
343+
},
344+
],
345+
},
346+
plugins: [
347+
plugin,
348+
new MiniCssExtractPlugin({
349+
filename: "[name].css",
350+
}),
351+
],
352+
optimization: {
353+
runtimeChunk: {
354+
name: "runtime",
355+
},
356+
},
357+
})
358+
);
359+
const source = fs.readFileSync(`${tmpDir.path}/runtime.js`, "utf-8");
360+
const sriManifest = JSON.parse(
361+
source.match(/__webpack_require__.sriHashes = ({.+});/)?.[1] || "{}"
362+
);
363+
let hasMiniCssExtractInfo = false;
364+
Object.keys(sriManifest).forEach((chunk) => {
365+
hasMiniCssExtractInfo = chunk.endsWith("css/mini-extract");
366+
expect(sriManifest[chunk]).toMatch(/^sha256-\S+$/);
367+
});
368+
expect(hasMiniCssExtractInfo).toBeTruthy();
369+
});

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)
183+
),
180184
integrity
181185
);
182186
}

webpack-subresource-integrity/src/util.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ export type ChunkGroup = ReturnType<Compilation["addChunkInGroup"]>;
1414

1515
export const sriHashVariableReference = "__webpack_require__.sriHashes";
1616

17+
export const miniCssExtractType = "css/mini-extract";
18+
1719
export function assert(value: unknown, message: string): asserts value {
1820
if (!value) {
1921
throw new Error(message);
@@ -113,11 +115,21 @@ export function notNil<TValue>(
113115
}
114116

115117
export function generateSriHashPlaceholders(
118+
compilation: Compilation,
116119
chunks: Iterable<Chunk>,
117120
hashFuncNames: [string, ...string[]]
118121
): Record<string, string> {
119122
return Array.from(chunks).reduce((sriHashes, depChunk: Chunk) => {
120123
if (depChunk.id) {
124+
const hasMiniCssExtractFile = compilation.chunkGraph
125+
.getChunkModules(depChunk)
126+
.find((module) => module.type === miniCssExtractType);
127+
if (hasMiniCssExtractFile) {
128+
sriHashes[`${depChunk.id}_${miniCssExtractType}`] = makePlaceholder(
129+
hashFuncNames,
130+
`${depChunk.id}_${miniCssExtractType}`
131+
);
132+
}
121133
sriHashes[depChunk.id] = makePlaceholder(hashFuncNames, depChunk.id);
122134
}
123135
return sriHashes;
@@ -291,3 +303,10 @@ export function hasOwnProperty<X extends object, Y extends PropertyKey>(
291303
): obj is X & Record<Y, unknown> {
292304
return Object.prototype.hasOwnProperty.call(obj, prop);
293305
}
306+
307+
export const normalizeChunkId = (sourcePath: string, chunk: Chunk) => {
308+
if (sourcePath.endsWith(".css") && chunk.contentHash[miniCssExtractType]) {
309+
return `${chunk.id}_${miniCssExtractType}`;
310+
}
311+
return chunk.id as string | number;
312+
};

0 commit comments

Comments
 (0)