Skip to content

Commit

Permalink
Add lazyHashes option to define integrity hashes only in direct par…
Browse files Browse the repository at this point in the history
…ents of assets (#172)

Also use Node 12 tsconfig base, as the "engine" field in package.json requires.
  • Loading branch information
MLoughry committed Jan 9, 2022
1 parent 3d93750 commit fc7cf19
Show file tree
Hide file tree
Showing 31 changed files with 930 additions and 82 deletions.
10 changes: 6 additions & 4 deletions examples/hwp-custom-template/webpack.config.js
Expand Up @@ -40,14 +40,16 @@ module.exports = {

const jsIntegrity = stats
.toJson()
.assets.find((asset) => asset.name === "subdir/bundle.js")
.integrity;
.assets.find(
(asset) => asset.name === "subdir/bundle.js"
).integrity;
expect(jsIntegrity).toMatch(/^sha/);

const cssIntegrity = stats
.toJson()
.assets.find((asset) => asset.name === "subdir/styles.css")
.integrity;
.assets.find(
(asset) => asset.name === "subdir/styles.css"
).integrity;
expect(cssIntegrity).toMatch(/^sha/);

return new Promise((resolve, reject) => {
Expand Down
5 changes: 5 additions & 0 deletions examples/lazy-hashes-cycles/1.js
@@ -0,0 +1,5 @@
import("./2.js");
import("./leaf.js");
export default {
chunk: 1,
};
4 changes: 4 additions & 0 deletions examples/lazy-hashes-cycles/2.js
@@ -0,0 +1,4 @@
import("./3.js");
export default {
chunk: 2,
};
4 changes: 4 additions & 0 deletions examples/lazy-hashes-cycles/3.js
@@ -0,0 +1,4 @@
import("./1.js");
export default {
chunk: 3,
};
3 changes: 3 additions & 0 deletions examples/lazy-hashes-cycles/README.md
@@ -0,0 +1,3 @@
# Sourcemap and code splitting

Test case for lazy hashes where there is a chunk dependency cycle
3 changes: 3 additions & 0 deletions examples/lazy-hashes-cycles/index.js
@@ -0,0 +1,3 @@
import("./1.js");
import("./2.js");
console.log("ok");
3 changes: 3 additions & 0 deletions examples/lazy-hashes-cycles/leaf.js
@@ -0,0 +1,3 @@
export default {
chunk: "leaf",
};
16 changes: 16 additions & 0 deletions examples/lazy-hashes-cycles/package.json
@@ -0,0 +1,16 @@
{
"name": "lazy-hashes-cycles",
"description": "Test case for lazy hashes where there is a chunk dependency cycle",
"version": "1.0.0",
"license": "MIT",
"private": true,
"devDependencies": {
"expect": "^26.6.2",
"html-webpack-plugin": ">= 5.0.0-beta.1",
"nyc": "*",
"webpack": "^5.44.0",
"webpack-cli": "4",
"webpack-subresource-integrity": "*",
"wsi-test-helper": "*"
}
}
75 changes: 75 additions & 0 deletions examples/lazy-hashes-cycles/webpack.config.js
@@ -0,0 +1,75 @@
const { SubresourceIntegrityPlugin } = require("webpack-subresource-integrity");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { readFileSync } = require("fs");
const { join } = require("path");
const expect = require("expect");

module.exports = {
entry: {
index: "./index.js",
},
output: {
crossOriginLoading: "anonymous",
},
plugins: [
new SubresourceIntegrityPlugin({
enabled: true,
hashLoading: "lazy",
}),
new HtmlWebpackPlugin(),
{
apply: (compiler) => {
compiler.hooks.done.tap("wsi-test", (stats) => {
if (stats && stats.hasErrors()) {
throw new Error(
stats
.toJson()
.errors.map((error) => error.message)
.join(", ")
);
}
function getSriHashes(chunkName, isEntry) {
const fileContent = readFileSync(
join(__dirname, "dist", `${chunkName}.js`),
"utf-8"
);
const sriRegex = new RegExp(
`${
isEntry
? "(\\w+|__webpack_require__)\\.sriHashes="
: "Object.assign\\((\\w+|__webpack_require__)\\.sriHashes,"
}(?<sriHashJson>\{.*?\})`
);
const regexMatch = sriRegex.exec(fileContent);
const sriHashJson = regexMatch
? regexMatch.groups.sriHashJson
: null;
if (!sriHashJson) {
return null;
}
try {
// The hashes are not *strict* JSON, since they can have numerical keys
return JSON.parse(
sriHashJson.replace(/\d+(?=:)/g, (num) => `"${num}"`)
);
} catch (err) {
throw new Error(
`Could not parse SRI hashes \n\t${sriHashJson}\n in asset: ${err}`
);
}
}

const indexHashes = getSriHashes("index", true);
expect(Object.keys(indexHashes).length).toEqual(3);

expect(
stats
.toJson()
.assets.filter(({ name }) => /\.js$/.test(name))
.every(({ integrity }) => !!integrity)
).toEqual(true);
});
},
},
],
};
4 changes: 4 additions & 0 deletions examples/lazy-hashes-multiple-parents/1.js
@@ -0,0 +1,4 @@
import("./leaf.js");
export default {
chunk: 1,
};
4 changes: 4 additions & 0 deletions examples/lazy-hashes-multiple-parents/2.js
@@ -0,0 +1,4 @@
import("./leaf.js");
export default {
chunk: 2,
};
3 changes: 3 additions & 0 deletions examples/lazy-hashes-multiple-parents/README.md
@@ -0,0 +1,3 @@
# Sourcemap and code splitting

Test case for sourcemap and code splitting
3 changes: 3 additions & 0 deletions examples/lazy-hashes-multiple-parents/index.js
@@ -0,0 +1,3 @@
import("./1.js");
import("./2.js");
console.log("ok");
3 changes: 3 additions & 0 deletions examples/lazy-hashes-multiple-parents/leaf.js
@@ -0,0 +1,3 @@
export default {
chunk: "leaf",
};
16 changes: 16 additions & 0 deletions examples/lazy-hashes-multiple-parents/package.json
@@ -0,0 +1,16 @@
{
"name": "lazy-hashes-multiple-parents",
"description": "Test case for lazy hashes where a chunk has multiple parents",
"version": "1.0.0",
"license": "MIT",
"private": true,
"devDependencies": {
"expect": "^26.6.2",
"html-webpack-plugin": ">= 5.0.0-beta.1",
"nyc": "*",
"webpack": "^5.44.0",
"webpack-cli": "4",
"webpack-subresource-integrity": "*",
"wsi-test-helper": "*"
}
}
86 changes: 86 additions & 0 deletions examples/lazy-hashes-multiple-parents/webpack.config.js
@@ -0,0 +1,86 @@
const { SubresourceIntegrityPlugin } = require("webpack-subresource-integrity");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { readFileSync } = require("fs");
const { join } = require("path");
const expect = require("expect");

module.exports = {
entry: {
index: "./index.js",
},
output: {
crossOriginLoading: "anonymous",
},
plugins: [
new SubresourceIntegrityPlugin({
enabled: true,
hashLoading: "lazy",
}),
new HtmlWebpackPlugin(),
{
apply: (compiler) => {
compiler.hooks.done.tap("wsi-test", (stats) => {
if (stats && stats.hasErrors()) {
throw new Error(
stats
.toJson()
.errors.map((error) => error.message)
.join(", ")
);
}
function getSriHashes(chunkName, isEntry) {
const fileContent = readFileSync(
join(__dirname, "dist", `${chunkName}.js`),
"utf-8"
);
const sriRegex = new RegExp(
`${
isEntry
? "(\\w+|__webpack_require__)\\.sriHashes="
: "Object.assign\\((\\w+|__webpack_require__)\\.sriHashes,"
}(?<sriHashJson>\{.*?\})`
);
const regexMatch = sriRegex.exec(fileContent);
const sriHashJson = regexMatch
? regexMatch.groups.sriHashJson
: null;
if (!sriHashJson) {
return null;
}
try {
// The hashes are not *strict* JSON, since they can have numerical keys
return JSON.parse(
sriHashJson.replace(/\d+(?=:)/g, (num) => `"${num}"`)
);
} catch (err) {
throw new Error(
`Could not parse SRI hashes \n\t${sriHashJson}\n in asset: ${err}`
);
}
}

const indexHashes = getSriHashes("index", true);
expect(Object.keys(indexHashes).length).toEqual(2);

const chunkHashes = Object.fromEntries(
Object.keys(indexHashes).map((chunkId) => [
chunkId,
getSriHashes(chunkId, false),
])
);

for (const [, intermediateChunkHash] of Object.entries(chunkHashes)) {
expect(Object.keys(intermediateChunkHash).length).toEqual(1);
}

expect(
stats
.toJson()
.assets.filter(({ name }) => /\.js$/.test(name))
.every(({ integrity }) => !!integrity)
).toEqual(true);
});
},
},
],
};
4 changes: 4 additions & 0 deletions examples/lazy-hashes-simple/1.js
@@ -0,0 +1,4 @@
import("./2.js");
export default {
chunk: 1,
};
3 changes: 3 additions & 0 deletions examples/lazy-hashes-simple/2.js
@@ -0,0 +1,3 @@
export default {
chunk: 2,
};
3 changes: 3 additions & 0 deletions examples/lazy-hashes-simple/README.md
@@ -0,0 +1,3 @@
# Sourcemap and code splitting

Simple test case for lazy hashes
2 changes: 2 additions & 0 deletions examples/lazy-hashes-simple/index.js
@@ -0,0 +1,2 @@
import("./1.js");
console.log("ok");
16 changes: 16 additions & 0 deletions examples/lazy-hashes-simple/package.json
@@ -0,0 +1,16 @@
{
"name": "lazy-hashes-simple",
"description": "Simple test case for lazy hashes",
"version": "1.0.0",
"license": "MIT",
"private": true,
"devDependencies": {
"expect": "^26.6.2",
"html-webpack-plugin": ">= 5.0.0-beta.1",
"nyc": "*",
"webpack": "^5.44.0",
"webpack-cli": "4",
"webpack-subresource-integrity": "*",
"wsi-test-helper": "*"
}
}
82 changes: 82 additions & 0 deletions examples/lazy-hashes-simple/webpack.config.js
@@ -0,0 +1,82 @@
const { SubresourceIntegrityPlugin } = require("webpack-subresource-integrity");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { readFileSync } = require("fs");
const { join } = require("path");
const expect = require("expect");

module.exports = {
entry: {
index: "./index.js",
},
output: {
crossOriginLoading: "anonymous",
},
// optimization: {minimize: false},
plugins: [
new SubresourceIntegrityPlugin({
enabled: true,
hashLoading: "lazy",
}),
new HtmlWebpackPlugin(),
{
apply: (compiler) => {
compiler.hooks.done.tap("wsi-test", (stats) => {
if (stats && stats.hasErrors()) {
throw new Error(
stats
.toJson()
.errors.map((error) => error.message)
.join(", ")
);
}
function getSriHashes(chunkName, isEntry) {
const fileContent = readFileSync(
join(__dirname, "dist", `${chunkName}.js`),
"utf-8"
);
const sriRegex = new RegExp(
`${
isEntry
? "(\\w+|__webpack_require__)\\.sriHashes="
: "Object.assign\\((\\w+|__webpack_require__)\\.sriHashes,"
}(?<sriHashJson>\{.*?\})`
);
const regexMatch = sriRegex.exec(fileContent);
const sriHashJson = regexMatch
? regexMatch.groups.sriHashJson
: null;
if (!sriHashJson) {
return null;
}
try {
// The hashes are not *strict* JSON, since they can have numerical keys
return JSON.parse(
sriHashJson.replace(/\d+(?=:)/g, (num) => `"${num}"`)
);
} catch (err) {
throw new Error(
`Could not parse SRI hashes \n\t${sriHashJson}\n in asset: ${err}`
);
}
}

const indexHashes = getSriHashes("index", true);
expect(Object.keys(indexHashes).length).toEqual(1);

const _1jsHashes = getSriHashes(Object.keys(indexHashes)[0], false);
expect(Object.keys(_1jsHashes).length).toEqual(1);

const _2jsHashes = getSriHashes(Object.keys(_1jsHashes)[0], false);
expect(_2jsHashes).toEqual(null);

expect(
stats
.toJson()
.assets.filter(({ name }) => /\.js$/.test(name))
.every(({ integrity }) => !!integrity)
).toEqual(true);
});
},
},
],
};

0 comments on commit fc7cf19

Please sign in to comment.