Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix the same chunkId is overwritten after using mini-css-extract-plugin #225

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/mini-css-extract-plugin/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import("./style.module.css").then((module) => {
import(/* webpackChunkName: "style" */ "./style.module.css").then((module) => {
console.log(module["default"] ? "ok" : "error");
});
31 changes: 30 additions & 1 deletion examples/mini-css-extract-plugin/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ const { SubresourceIntegrityPlugin } = require("webpack-subresource-integrity");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { RunInPuppeteerPlugin } = require("wsi-test-helper");
const expect = require("expect");
const fs = require("fs");
const path = require("path");

module.exports = {
mode: "none",
entry: {
index: "./index.js",
},
Expand All @@ -12,14 +16,33 @@ module.exports = {
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: "[name].css",
chunkFilename: "[id].css",
chunkFilename: "[name].css",
}),
new SubresourceIntegrityPlugin({
hashFuncNames: ["sha256", "sha384"],
enabled: true,
}),
new HtmlWebpackPlugin(),
new RunInPuppeteerPlugin(),
{
apply: (compiler) => {
compiler.hooks.done.tap("wsi-test", (stats) => {
expect(stats.compilation.warnings).toEqual([]);
expect(stats.compilation.errors).toEqual([]);
const cssIntegrity = stats
.toJson()
.assets.find((asset) => asset.name === "style.css").integrity;
const source = fs.readFileSync(
path.join(__dirname, "./dist/runtime.js"),
"utf-8"
);
const sriManifest = JSON.parse(
source.match(/__webpack_require__.sriHashes = ({.+});/)?.[1] || "{}"
);
expect(sriManifest["style_css/mini-extract"]).toEqual(cssIntegrity);
});
},
},
],
output: {
crossOriginLoading: "anonymous",
Expand All @@ -44,4 +67,10 @@ module.exports = {
},
],
},
optimization: {
chunkIds: "named",
runtimeChunk: {
name: "runtime",
},
},
};
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
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, this.compilation)
),
integrity
);
}
Expand Down
42 changes: 40 additions & 2 deletions webpack-subresource-integrity/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,23 @@
*/

import { createHash } from "crypto";
import type { AssetInfo, Chunk, Compilation, Compiler, sources } from "webpack";
import type {
AssetInfo,
Chunk,
Compilation,
Compiler,
sources,
Module,
} from "webpack";
import { sep } from "path";
import type { HtmlTagObject } from "./types";

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 @@ -112,13 +121,28 @@ export function notNil<TValue>(
return value !== null && value !== undefined;
}

export function hasMiniCssExtractModlue(modules: Module[]) {
return modules.find((module) => module.type === miniCssExtractType);
}

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) {
sriHashes[depChunk.id] = makePlaceholder(hashFuncNames, depChunk.id);
const modules = compilation.chunkGraph.getChunkModules(depChunk);
const containCssModule = hasMiniCssExtractModlue(modules);
if (containCssModule) {
sriHashes[`${depChunk.id}_${miniCssExtractType}`] = makePlaceholder(
hashFuncNames,
`${depChunk.id}_${miniCssExtractType}`
);
}
if (!containCssModule || (containCssModule && modules.length > 1)) {
sriHashes[depChunk.id] = makePlaceholder(hashFuncNames, depChunk.id);
}
}
return sriHashes;
}, {} as { [key: string]: string });
Expand Down Expand Up @@ -291,3 +315,17 @@ 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,
compilation: Compilation
) => {
if (
sourcePath.endsWith(".css") &&
hasMiniCssExtractModlue(compilation.chunkGraph.getChunkModules(chunk))
) {
return `${chunk.id}_${miniCssExtractType}`;
}
return chunk.id as string | number;
};
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10073,4 +10073,4 @@ __metadata:
z-schema: bin/z-schema
checksum: 8a1d66817ae4384dc3f63311f0cccaadd95cc9640eaade5fd3fbf91aa80d6bb82fb95d9b9171fa82ac371a0155b32b7f5f77bbe84dabaca611b66f74c628f0b8
languageName: node
linkType: hard
linkType: hard