Skip to content

Commit

Permalink
fix: the same chunkId is overwritten after using mini-css-extract-plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
msidolphin committed Oct 13, 2023
1 parent 1008584 commit af4f3a3
Show file tree
Hide file tree
Showing 9 changed files with 219 additions and 8 deletions.
20 changes: 17 additions & 3 deletions webpack-subresource-integrity/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,20 @@ default), the `integrity` attribute will be set automatically. The
`output.crossOriginLoading` webpack option. There is nothing else to
be done.

#### With MiniCssExtractPlugin

Currently, developers can only add integrity to link elements via the insert method.

```js
new MiniCssExtractPlugin({
insert: (link) => {
link.integrity =
__webpack_require__.sriHashes[chunkId + "_css/mini-extract"];
document.head.appendChild(link);
},
})
```

#### With HtmlWebpackPlugin({ inject: false })

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

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

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

As detailed in [Webpack Issue
#6972](https://github.com/webpack/webpack/issues/6972), the `crossOrigin`
# 6972](https://github.com/webpack/webpack/issues/6972), the `crossOrigin`
attribute can break loading of assets in Safari versions prior to 14 in certain
edge cases due to a browser bug. Since SRI requires the `crossOrigin` attribute
to be set, you may run into this case even when source URL is same-origin with
Expand Down
2 changes: 2 additions & 0 deletions webpack-subresource-integrity/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,12 @@
"@types/rimraf": "^3.0.2",
"@types/tmp": "^0.2.3",
"cross-spawn": "^7.0.3",
"css-loader": "^6.8.1",
"html-webpack-plugin": ">= 5.0.0-beta.1",
"jest": "^28.1.3",
"lodash": "^4.17.21",
"memfs": "^3.4.1",
"mini-css-extract-plugin": "^2.7.6",
"nyc": "*",
"rimraf": "^3.0.2",
"tapable": "^2.2.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
body {
background: red;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import("./index.css");
55 changes: 55 additions & 0 deletions webpack-subresource-integrity/src/__tests__/unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
import { resolve } from "path";
import webpack, { Compiler, Compilation, Configuration, Chunk } from "webpack";
import HtmlWebpackPlugin from "html-webpack-plugin";
import MiniCssExtractPlugin from "mini-css-extract-plugin";
import tmp from "tmp-promise";
import fs from "fs";
import { SubresourceIntegrityPlugin } from "..";
import type { SubresourceIntegrityPluginOptions } from "..";
import { assert } from "../util";
Expand Down Expand Up @@ -312,3 +315,55 @@ test("errors with unresolved integrity", async () => {
new RegExp("contains unresolved integrity placeholders")
);
});

test("should contain integrity when using mini-css-extract-plugin", async () => {
const plugin = new SubresourceIntegrityPlugin({
hashFuncNames: ["sha256"],
enabled: true,
hashLoading: "eager",
});
const tmpDir = await tmp.dir({ unsafeCleanup: true });
await runCompilation(
webpack({
mode: "none",
entry: resolve(
__dirname,
"./__fixtures__/mini-css-extract-plugin/src/index.js"
),
output: {
path: tmpDir.path,
filename: "[name].js",
crossOriginLoading: "anonymous",
},
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
],
},
plugins: [
plugin,
new MiniCssExtractPlugin({
filename: "[name].css",
}),
],
optimization: {
runtimeChunk: {
name: "runtime",
},
},
})
);
const source = fs.readFileSync(`${tmpDir.path}/runtime.js`, "utf-8");
const sriManifest = JSON.parse(
source.match(/__webpack_require__.sriHashes = ({.+});/)?.[1] || "{}"
);
let hasMiniCssExtractInfo = false;
Object.keys(sriManifest).forEach((chunk) => {
hasMiniCssExtractInfo = chunk.endsWith("css/mini-extract");
expect(sriManifest[chunk]).toMatch(/^sha256-\S+$/);
});
expect(hasMiniCssExtractInfo).toBeTruthy();
});
3 changes: 2 additions & 1 deletion webpack-subresource-integrity/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,13 +169,13 @@ export class SubresourceIntegrityPlugin {
? plugin.getChildChunksToAddToChunkManifest(chunk)
: findChunks(chunk);
const includedChunks = chunk.getChunkMaps(false).hash;

if (Object.keys(includedChunks).length > 0) {
return compilation.compiler.webpack.Template.asString([
source,
`${sriHashVariableReference} = ` +
JSON.stringify(
generateSriHashPlaceholders(
compilation,
Array.from(allChunks).filter(
(depChunk) =>
depChunk.id !== null &&
Expand All @@ -201,6 +201,7 @@ export class SubresourceIntegrityPlugin {
chunk,
new AddLazySriRuntimeModule(
generateSriHashPlaceholders(
compilation,
childChunks,
this.options.hashFuncNames
),
Expand Down
6 changes: 5 additions & 1 deletion webpack-subresource-integrity/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
tryGetSource,
replaceInSource,
usesAnyHash,
normalizeChunkId,
} from "./util";
import { getChunkToManifestMap } from "./manifest";
import { AssetIntegrity } from "./integrity";
Expand Down Expand Up @@ -176,7 +177,10 @@ export class Plugin {

if (childChunk.id !== null) {
this.hashByPlaceholder.set(
makePlaceholder(this.options.hashFuncNames, childChunk.id),
makePlaceholder(
this.options.hashFuncNames,
normalizeChunkId(sourcePath, childChunk)
),
integrity
);
}
Expand Down
19 changes: 19 additions & 0 deletions webpack-subresource-integrity/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export type ChunkGroup = ReturnType<Compilation["addChunkInGroup"]>;

export const sriHashVariableReference = "__webpack_require__.sriHashes";

export const miniCssExtractType = "css/mini-extract";

export function assert(value: unknown, message: string): asserts value {
if (!value) {
throw new Error(message);
Expand Down Expand Up @@ -113,11 +115,21 @@ export function notNil<TValue>(
}

export function generateSriHashPlaceholders(
compilation: Compilation,
chunks: Iterable<Chunk>,
hashFuncNames: [string, ...string[]]
): Record<string, string> {
return Array.from(chunks).reduce((sriHashes, depChunk: Chunk) => {
if (depChunk.id) {
const hasMiniCssExtractFile = compilation.chunkGraph
.getChunkModules(depChunk)
.find((module) => module.type === miniCssExtractType);
if (hasMiniCssExtractFile) {
sriHashes[`${depChunk.id}_${miniCssExtractType}`] = makePlaceholder(
hashFuncNames,
`${depChunk.id}_${miniCssExtractType}`
);
}
sriHashes[depChunk.id] = makePlaceholder(hashFuncNames, depChunk.id);
}
return sriHashes;
Expand Down Expand Up @@ -291,3 +303,10 @@ export function hasOwnProperty<X extends object, Y extends PropertyKey>(
): obj is X & Record<Y, unknown> {
return Object.prototype.hasOwnProperty.call(obj, prop);
}

export const normalizeChunkId = (sourcePath: string, chunk: Chunk) => {
if (sourcePath.endsWith(".css") && chunk.contentHash[miniCssExtractType]) {
return `${chunk.id}_${miniCssExtractType}`;
}
return chunk.id as string | number;
};

0 comments on commit af4f3a3

Please sign in to comment.