From 2d2011c7ae0145369ad226417e9ecc3bf6df7890 Mon Sep 17 00:00:00 2001 From: Rob Hogan Date: Fri, 26 Sep 2025 08:13:44 -0700 Subject: [PATCH] Invalidate transform cache on update of/local changes to `@react-native/babel-preset` (#53867) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53867 `react-native/metro-babel-transformer` implements `getCacheKey` for Metro, but does not vary the cache key on changes to `react-native/babel-preset`, one of its dependencies, leading to false positive cache hits (changes not being reflected). This is particularly a problem in development, although it potentially also leads to issues in the unusual case that `react-native/babel-preset` is updated via npm, but no other package is. This adds a `getCacheKey` to `react-native/babel-preset` and incorporates it into `react-native/metro-babel-transformer`'s `getCacheKey`. It's based on `package.json#version` in production and a hash of package contents in development (`version.endsWith('-main')`). Changelog: [General][Fixed] Invalidate transform cache when `react-native/babel-preset` is updated Reviewed By: huntie Differential Revision: D82893894 fbshipit-source-id: 9bbc034eaf68ee0e5b3078fb2fe8f30caa7f11c9 --- .../react-native-babel-preset/src/index.js | 30 +++++++++++++++++++ .../src/index.js | 3 +- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/packages/react-native-babel-preset/src/index.js b/packages/react-native-babel-preset/src/index.js index 1d46f1ceef0..d2b5fc98721 100644 --- a/packages/react-native-babel-preset/src/index.js +++ b/packages/react-native-babel-preset/src/index.js @@ -10,11 +10,41 @@ 'use strict'; +const {version: packageVersion} = require('../package.json'); const main = require('./configs/main'); module.exports = function (babel, options) { return main(options); }; +let cacheKey; +module.exports.getCacheKey = () => { + if (cacheKey != null) { + // Memoize + return cacheKey; + } + if (!packageVersion.endsWith('-main')) { + // Assume the integrity of package contents for published npm releases. + cacheKey = packageVersion; + return cacheKey; + } + // For anyone working with a `-main` version, contents may vary over time + // even though the version does not. Hash the relevant contents of this + // package. Lazy-load dependencies we only need on this slow path. + const {createHash} = require('crypto'); + const {readFileSync} = require('fs'); + const key = createHash('md5'); + [ + readFileSync(__filename), + readFileSync(require.resolve('./configs/main.js')), + readFileSync(require.resolve('./configs/hmr.js')), + readFileSync(require.resolve('./configs/lazy-imports.js')), + readFileSync(require.resolve('./passthrough-syntax-plugins.js')), + readFileSync(require.resolve('./plugin-warn-on-deep-imports.js')), + ].forEach(part => key.update(part)); + cacheKey = key.digest('hex'); + return cacheKey; +}; + module.exports.getPreset = main.getPreset; module.exports.passthroughSyntaxPlugins = require('./passthrough-syntax-plugins'); diff --git a/packages/react-native-babel-transformer/src/index.js b/packages/react-native-babel-transformer/src/index.js index 8ba7c3afe3d..d5e541c370a 100644 --- a/packages/react-native-babel-transformer/src/index.js +++ b/packages/react-native-babel-transformer/src/index.js @@ -23,13 +23,14 @@ import type { */ const {parseSync, transformFromAstSync} = require('@babel/core'); +const {getCacheKey: getPresetCacheKey} = require('@react-native/babel-preset'); const makeHMRConfig = require('@react-native/babel-preset/src/configs/hmr'); const crypto = require('crypto'); const fs = require('fs'); const nullthrows = require('nullthrows'); const path = require('path'); -const cacheKeyParts = [fs.readFileSync(__filename)]; +const cacheKeyParts = [getPresetCacheKey(), fs.readFileSync(__filename)]; // TS detection conditions copied from @react-native/babel-preset function isTypeScriptSource(fileName /*: string */) {