Skip to content

Commit

Permalink
[Bugfix] Ensure stability of filename cache-keys
Browse files Browse the repository at this point in the history
`JSON.stringify(structure)` isn’t inherently stable as it relies on
various internal details of how `structure` was created.

As written, if a given babel configuration is create in an dynamic
manner, it is possible for babel-loader to have spurious cache misses.

To address this, we can use one of the many stable stringify
alternatives.

For this PR I have selected
[fast-stable-stringify](https://www.npmjs.com/package/fast-stable-stringify)
for that task, as it appears both popular and it’s benchmarks look
  promising.

This PR does not explicitly include tests, as testing this is both
tricky to test in this context, and the important tests are contained
within fast-stable-stringify itself.
  • Loading branch information
stefanpenner authored and JLHwung committed Jul 11, 2024
1 parent 06c3ad8 commit ca2c30f
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 19 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"node": ">= 14.15.0"
},
"dependencies": {
"fast-stable-stringify": "^1.0.0",
"find-cache-dir": "^4.0.0",
"schema-utils": "^4.0.0"
},
Expand Down
5 changes: 2 additions & 3 deletions src/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ try {

const gunzip = promisify(zlib.gunzip);
const gzip = promisify(zlib.gzip);
const stringify = require("fast-stable-stringify");

/**
* Read the contents from the compressed file.
Expand Down Expand Up @@ -70,9 +71,7 @@ const write = async function (filename, compress, result) {
const filename = function (source, identifier, options) {
const hash = crypto.createHash(hashType);

const contents = JSON.stringify({ source, options, identifier });

hash.update(contents);
hash.update(stringify({ source, options, identifier }));

return hash.digest("hex") + ".json";
};
Expand Down
3 changes: 2 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const schema = require("./schema");

const { isAbsolute } = require("path");
const validateOptions = require("schema-utils").validate;
const stringify = require("fast-stable-stringify");

function subscribe(subscriber, metadata, context) {
if (context[subscriber]) {
Expand Down Expand Up @@ -174,7 +175,7 @@ async function loader(source, inputSourceMap, overrides) {

const {
cacheDirectory = null,
cacheIdentifier = JSON.stringify({
cacheIdentifier = stringify({
options,
"@babel/core": transform.version,
"@babel/loader": version,
Expand Down
2 changes: 1 addition & 1 deletion test/cache.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -323,5 +323,5 @@ test("should allow to specify the .babelrc file", async t => {
t.deepEqual(multiStats.stats[1].compilation.warnings, []);

const files = fs.readdirSync(t.context.cacheDirectory);
t.true(files.length === 2);
t.true(files.length === 1);
});
46 changes: 32 additions & 14 deletions yarn.lock
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
# This file is generated by running "yarn install" inside your project.
# Manual changes might be lost - proceed with caution!

__metadata:
version: 6
cacheKey: 8
Expand Down Expand Up @@ -2279,17 +2276,31 @@ __metadata:
"@babel/eslint-parser": ^7.23.3
"@babel/preset-env": ^7.23.3
ava: ^3.13.0
c8: ^8.0.0
eslint: ^9.6.0
eslint-config-prettier: ^9.1.0
eslint-plugin-prettier: ^5.1.3
find-cache-dir: ^4.0.0
globals: ^15.8.0
husky: ^8.0.3
lint-staged: ^13.2.3
prettier: ^3.0.0
schema-utils: ^4.0.0
webpack: ^5.89.0
babel-eslint: ^10.0.1
babel-plugin-istanbul: ^6.0.0
babel-plugin-react-intl: ^8.2.15
cross-env: ^7.0.2
eslint: ^7.13.0
eslint-config-babel: ^9.0.0
eslint-config-prettier: ^6.3.0
eslint-plugin-flowtype: ^5.2.0
eslint-plugin-prettier: ^3.0.0
fast-stable-stringify: ^1.0.0
find-cache-dir: ^3.3.1
husky: ^4.3.0
lint-staged: ^10.5.1
loader-utils: ^1.4.0
make-dir: ^3.1.0
nyc: ^15.1.0
pnp-webpack-plugin: ^1.6.4
prettier: ^2.1.2
react: ^17.0.1
react-intl: ^5.9.4
react-intl-webpack-plugin: ^0.3.0
rimraf: ^3.0.0
schema-utils: ^2.6.5
semver: 7.3.2
webpack: ^5.34.0
peerDependencies:
"@babel/core": ^7.12.0
webpack: ">=5"
Expand Down Expand Up @@ -3555,6 +3566,13 @@ __metadata:
languageName: node
linkType: hard

"fast-stable-stringify@npm:^1.0.0":
version: 1.0.0
resolution: "fast-stable-stringify@npm:1.0.0"
checksum: 972291ed8b8a1a1e13130c91062852dc9142dceed14dc1655c1d0aebc07adcf8a60678252dd1410ec0cceddd3842c87cfea2a937ee3043dfaa6068c3f578f515
languageName: node
linkType: hard

"fastq@npm:^1.6.0":
version: 1.17.1
resolution: "fastq@npm:1.17.1"
Expand Down

0 comments on commit ca2c30f

Please sign in to comment.