From 6fb3ede67b1d645797f950f66e233d593471e0bf Mon Sep 17 00:00:00 2001 From: Stefan Penner Date: Wed, 9 Jun 2021 17:40:45 -0600 Subject: [PATCH] [Bugfix] Ensure stability of filename cache-keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `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-json-stable-stringify](https://github.com/epoberezkin/fast-json-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-json-stable-stringify itself. --- package.json | 1 + src/cache.js | 3 ++- src/index.js | 3 ++- yarn.lock | 3 ++- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 2338859e..6c94a8db 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "node": ">= 8.9" }, "dependencies": { + "fast-json-stable-stringify": "^2.1.0", "find-cache-dir": "^3.3.1", "loader-utils": "^1.4.0", "make-dir": "^3.1.0", diff --git a/src/cache.js b/src/cache.js index 7f7721ff..c7f3a23b 100644 --- a/src/cache.js +++ b/src/cache.js @@ -24,6 +24,7 @@ const writeFile = promisify(fs.writeFile); const gunzip = promisify(zlib.gunzip); const gzip = promisify(zlib.gzip); const makeDir = require("make-dir"); +const stringify = require("fast-json-stable-stringify"); /** * Read the contents from the compressed file. @@ -65,7 +66,7 @@ const write = async function (filename, compress, result) { const filename = function (source, identifier, options) { const hash = crypto.createHash("md4"); - const contents = JSON.stringify({ source, options, identifier }); + const contents = stringify({ source, options, identifier }); hash.update(contents); diff --git a/src/index.js b/src/index.js index 07eec2a1..1a1eb87a 100644 --- a/src/index.js +++ b/src/index.js @@ -28,6 +28,7 @@ const schema = require("./schema"); const { isAbsolute } = require("path"); const loaderUtils = require("loader-utils"); const validateOptions = require("schema-utils"); +const stringify = require("fast-json-stable-stringify"); function subscribe(subscriber, metadata, context) { if (context[subscriber]) { @@ -186,7 +187,7 @@ async function loader(source, inputSourceMap, overrides) { const { cacheDirectory = null, - cacheIdentifier = JSON.stringify({ + cacheIdentifier = stringify({ options, "@babel/core": transform.version, "@babel/loader": version, diff --git a/yarn.lock b/yarn.lock index b10e011e..e1776bd2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2140,6 +2140,7 @@ __metadata: eslint-config-prettier: ^6.3.0 eslint-plugin-flowtype: ^5.2.0 eslint-plugin-prettier: ^3.0.0 + fast-json-stable-stringify: ^2.1.0 find-cache-dir: ^3.3.1 husky: ^4.3.0 lint-staged: ^10.5.1 @@ -3711,7 +3712,7 @@ __metadata: languageName: node linkType: hard -"fast-json-stable-stringify@npm:^2.0.0": +"fast-json-stable-stringify@npm:^2.0.0, fast-json-stable-stringify@npm:^2.1.0": version: 2.1.0 resolution: "fast-json-stable-stringify@npm:2.1.0" checksum: 7df3fabfe445d65953b2d9d9d3958bd895438b215a40fb87dae8b2165c5169a897785eb5d51e6cf0eb03523af756e3d82ea01083f6ac6341fe16db532fee3016