diff --git a/packages/react-native-babel-preset/package.json b/packages/react-native-babel-preset/package.json index 759195dbf1c3..df72068b03f1 100644 --- a/packages/react-native-babel-preset/package.json +++ b/packages/react-native-babel-preset/package.json @@ -36,6 +36,7 @@ "@babel/plugin-transform-async-to-generator": "^7.24.7", "@babel/plugin-transform-block-scoping": "^7.25.0", "@babel/plugin-transform-class-properties": "^7.25.4", + "@babel/plugin-transform-class-static-block": "^7.26.0", "@babel/plugin-transform-classes": "^7.25.4", "@babel/plugin-transform-destructuring": "^7.24.8", "@babel/plugin-transform-flow-strip-types": "^7.25.2", diff --git a/packages/react-native-babel-preset/src/__tests__/transform-snapshot-test.js b/packages/react-native-babel-preset/src/__tests__/transform-snapshot-test.js index 6de3d2e63a27..9d9d2606b99e 100644 --- a/packages/react-native-babel-preset/src/__tests__/transform-snapshot-test.js +++ b/packages/react-native-babel-preset/src/__tests__/transform-snapshot-test.js @@ -568,6 +568,84 @@ describe('react-native-babel-preset transform snapshots', () => { expect(result).toContain('_classPrivateFieldLooseKey'); }); + it('transforms class static blocks without unstable_preserveClassPrivate', () => { + const code = ` + class Config { + static value = 0; + static { + Config.value = 2; + } + } + `; + const result = transformCode(code, { + dev: false, + unstable_transformProfile: 'hermes-stable', + }); + expect(result).not.toContain('static {'); + expect(result).toContain('Config.value = 2'); + }); + + it('transforms class static blocks when the profile lowers classes', () => { + const code = ` + class Config { + static value = 0; + static { + Config.value = 2; + } + } + `; + const result = transformCode(code, { + dev: false, + unstable_transformProfile: 'hermes-legacy', + customTransformOptions: { + unstable_preserveClassPrivate: true, + }, + }); + expect(result).not.toContain('static {'); + expect(result).toContain('Config.value = 2'); + }); + + it('transforms class static blocks that use private fields and super', () => { + const code = ` + class Base { + static base = 1; + } + class Config extends Base { + static #secret = 1; + static value = 0; + static { + Config.value = Config.#secret + super.base; + } + } + `; + const result = transformCode(code, { + dev: false, + unstable_transformProfile: 'hermes-stable', + }); + expect(result).not.toContain('static {'); + expect(result).not.toContain('#secret'); + }); + + it('preserves class static blocks with unstable_preserveClassPrivate', () => { + const code = ` + class Config { + static #secret = 1; + static { + Config.value = Config.#secret; + } + } + `; + const result = transformCode(code, { + dev: false, + unstable_transformProfile: 'hermes-stable', + customTransformOptions: { + unstable_preserveClassPrivate: true, + }, + }); + expect(result).toContain('static {'); + expect(result).toContain('#secret'); + }); + it('preserves async/await with unstable_preserveAsync', () => { const code = ` async function fetchData() { diff --git a/packages/react-native-babel-preset/src/configs/main.js b/packages/react-native-babel-preset/src/configs/main.js index 33a216fd791b..80b598972c01 100644 --- a/packages/react-native-babel-preset/src/configs/main.js +++ b/packages/react-native-babel-preset/src/configs/main.js @@ -280,6 +280,7 @@ const getPreset = (src, options, babel) => { ...(preserveClassPrivate ? [] : [ + [require('@babel/plugin-transform-class-static-block')], [require('@babel/plugin-transform-private-methods'), {loose}], [ require('@babel/plugin-transform-private-property-in-object'),