From 8bb047d10fdc5226c1fbf6909b49fe34fce87655 Mon Sep 17 00:00:00 2001 From: Cristian Ceban <58264709+CristiCeban@users.noreply.github.com> Date: Fri, 9 Oct 2026 17:43:55 +0300 Subject: [PATCH] fix(deps): declare babel syntax typescript --- .changeset/declare-babel-syntax-typescript.md | 8 +++++++ apps/tester-federation-v2/package.json | 1 - .../test/public-environment.test.cjs | 1 + packages/repack/package.json | 2 ++ .../babelSwcLoader/__tests__/utils.test.ts | 23 +++++++++++++++++++ .../src/loaders/babelSwcLoader/utils.ts | 8 +++++-- .../getCodegenTransformRules.test.ts | 22 ++++++++++++++++++ .../src/utils/getCodegenTransformRules.ts | 10 +++++--- pnpm-lock.yaml | 6 ++--- .../src/loaders/react-native-parser.test.ts | 11 +++++---- 10 files changed, 78 insertions(+), 14 deletions(-) create mode 100644 .changeset/declare-babel-syntax-typescript.md create mode 100644 packages/repack/src/loaders/babelSwcLoader/__tests__/utils.test.ts create mode 100644 packages/repack/src/utils/__tests__/getCodegenTransformRules.test.ts diff --git a/.changeset/declare-babel-syntax-typescript.md b/.changeset/declare-babel-syntax-typescript.md new file mode 100644 index 000000000..542a3cb65 --- /dev/null +++ b/.changeset/declare-babel-syntax-typescript.md @@ -0,0 +1,8 @@ +--- +"@callstack/repack": patch +--- + +Fix `Cannot find module '@babel/plugin-syntax-typescript'` when bundling TypeScript with `babel-swc-loader` or the codegen rules. Re.Pack passed the plugin to Babel by name, so it only resolved when the project happened to have it installed. With pnpm, builds often worked only through the `NODE_PATH` set by script shims and failed when bundling from Xcode or Gradle. + +- `@babel/plugin-syntax-typescript` is now a dependency of Re.Pack and is passed to Babel as a resolved path. +- `@babel/core` is now declared as a peer dependency, since Re.Pack's loaders import it. diff --git a/apps/tester-federation-v2/package.json b/apps/tester-federation-v2/package.json index d80e53f5a..2ee236ef8 100644 --- a/apps/tester-federation-v2/package.json +++ b/apps/tester-federation-v2/package.json @@ -25,7 +25,6 @@ }, "devDependencies": { "@babel/core": "^7.29.7", - "@babel/plugin-syntax-typescript": "7.25.4", "@react-native-community/cli": "catalog:testers", "@react-native-community/cli-platform-android": "catalog:testers", "@react-native-community/cli-platform-ios": "catalog:testers", diff --git a/packages/repack-expo/test/public-environment.test.cjs b/packages/repack-expo/test/public-environment.test.cjs index a289ba3c4..5fee278f8 100644 --- a/packages/repack-expo/test/public-environment.test.cjs +++ b/packages/repack-expo/test/public-environment.test.cjs @@ -232,6 +232,7 @@ test('resolves Expo development and production dotenv precedence without exposin const projectRoot = createEnvironmentProject({ '.env': [ 'EXPO_PUBLIC_VALUE=base', + // biome-ignore lint/suspicious/noTemplateCurlyInString: literal dotenv variable expansion under test 'EXPO_PUBLIC_EXPANDED=${SHARED_SUFFIX}-base', 'PRIVATE_TEST_SECRET=private-value-that-must-not-escape', ].join('\n'), diff --git a/packages/repack/package.json b/packages/repack/package.json index 45dc76ff7..9c86cccb5 100644 --- a/packages/repack/package.json +++ b/packages/repack/package.json @@ -65,6 +65,7 @@ "clang-format:android": "find android/src/ -iname \"*.h\" -o -iname \"*.cpp\" | xargs clang-format -i" }, "peerDependencies": { + "@babel/core": "^7.20.0", "@module-federation/enhanced": ">=0.6.10", "@rspack/core": ">=1", "react-native": ">=0.74", @@ -82,6 +83,7 @@ } }, "dependencies": { + "@babel/plugin-syntax-typescript": "^7.25.4", "@callstack/repack-dev-server": "workspace:*", "@discoveryjs/json-ext": "^0.5.7", "@rspack/plugin-react-refresh": "1.0.0", diff --git a/packages/repack/src/loaders/babelSwcLoader/__tests__/utils.test.ts b/packages/repack/src/loaders/babelSwcLoader/__tests__/utils.test.ts new file mode 100644 index 000000000..4c45284a0 --- /dev/null +++ b/packages/repack/src/loaders/babelSwcLoader/__tests__/utils.test.ts @@ -0,0 +1,23 @@ +import { getExtraBabelPlugins } from '../utils.js'; + +const syntaxTypeScriptPlugin = require.resolve( + '@babel/plugin-syntax-typescript' +); + +describe('getExtraBabelPlugins', () => { + it('adds the resolved TS syntax plugin for .ts files', () => { + expect(getExtraBabelPlugins('/virtual/file.ts')).toEqual([ + [syntaxTypeScriptPlugin, { isTSX: false, allowNamespaces: true }], + ]); + }); + + it('adds the resolved TSX syntax plugin for .tsx files', () => { + expect(getExtraBabelPlugins('/virtual/file.tsx')).toEqual([ + [syntaxTypeScriptPlugin, { isTSX: true, allowNamespaces: true }], + ]); + }); + + it('adds no plugins for other files', () => { + expect(getExtraBabelPlugins('/virtual/file.js')).toEqual([]); + }); +}); diff --git a/packages/repack/src/loaders/babelSwcLoader/utils.ts b/packages/repack/src/loaders/babelSwcLoader/utils.ts index a6f67b0d4..6d0fe840b 100644 --- a/packages/repack/src/loaders/babelSwcLoader/utils.ts +++ b/packages/repack/src/loaders/babelSwcLoader/utils.ts @@ -10,6 +10,10 @@ import { importDefaultESM } from '../../helpers/index.js'; type Swc = (typeof experiments)['swc']; type Logger = ReturnType; +const syntaxTypeScriptPlugin = require.resolve( + '@babel/plugin-syntax-typescript' +); + export function isTypeScriptSource(fileName: string) { return !!fileName && fileName.endsWith('.ts'); } @@ -34,12 +38,12 @@ export function getExtraBabelPlugins(filename: string) { // which includes the syntax-typescript plugin if (isTypeScriptSource(filename)) { extraBabelPlugins.push([ - '@babel/plugin-syntax-typescript', + syntaxTypeScriptPlugin, { isTSX: false, allowNamespaces: true }, ]); } else if (isTSXSource(filename)) { extraBabelPlugins.push([ - '@babel/plugin-syntax-typescript', + syntaxTypeScriptPlugin, { isTSX: true, allowNamespaces: true }, ]); } diff --git a/packages/repack/src/utils/__tests__/getCodegenTransformRules.test.ts b/packages/repack/src/utils/__tests__/getCodegenTransformRules.test.ts new file mode 100644 index 000000000..e07dddccf --- /dev/null +++ b/packages/repack/src/utils/__tests__/getCodegenTransformRules.test.ts @@ -0,0 +1,22 @@ +import { getCodegenTransformRules } from '../getCodegenTransformRules.js'; + +const syntaxTypeScriptPlugin = require.resolve( + '@babel/plugin-syntax-typescript' +); + +describe('getCodegenTransformRules', () => { + const [rule] = getCodegenTransformRules(); + const { plugins, overrides } = rule.use.options; + const getOverridePlugins = (file: string) => + overrides.find((override) => override.test.test(file))?.plugins; + + it('references the resolved TS syntax plugin', () => { + expect(plugins).toContainEqual([syntaxTypeScriptPlugin, false]); + expect(getOverridePlugins('NativeFoo.ts')).toEqual([ + [syntaxTypeScriptPlugin, { isTSX: false, allowNamespaces: true }], + ]); + expect(getOverridePlugins('FooNativeComponent.tsx')).toEqual([ + [syntaxTypeScriptPlugin, { isTSX: true, allowNamespaces: true }], + ]); + }); +}); diff --git a/packages/repack/src/utils/getCodegenTransformRules.ts b/packages/repack/src/utils/getCodegenTransformRules.ts index 8f53d5bbf..5775b4b0f 100644 --- a/packages/repack/src/utils/getCodegenTransformRules.ts +++ b/packages/repack/src/utils/getCodegenTransformRules.ts @@ -5,6 +5,10 @@ * @returns Array of module rules */ export function getCodegenTransformRules() { + const syntaxTypeScriptPlugin = require.resolve( + '@babel/plugin-syntax-typescript' + ); + return [ { type: 'javascript/auto', @@ -21,7 +25,7 @@ export function getCodegenTransformRules() { }, plugins: [ '@callstack/repack/babel-plugin-syntax-react-native', - ['@babel/plugin-syntax-typescript', false], + [syntaxTypeScriptPlugin, false], '@react-native/babel-plugin-codegen', ], // config merging reference: https://babeljs.io/docs/options#pluginpreset-entries @@ -30,7 +34,7 @@ export function getCodegenTransformRules() { test: /\.ts$/, plugins: [ [ - '@babel/plugin-syntax-typescript', + syntaxTypeScriptPlugin, { isTSX: false, allowNamespaces: true }, ], ], @@ -39,7 +43,7 @@ export function getCodegenTransformRules() { test: /\.tsx$/, plugins: [ [ - '@babel/plugin-syntax-typescript', + syntaxTypeScriptPlugin, { isTSX: true, allowNamespaces: true }, ], ], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e5ac9444d..188c30a4b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -564,9 +564,6 @@ importers: '@babel/core': specifier: ^7.29.7 version: 7.29.7 - '@babel/plugin-syntax-typescript': - specifier: 7.25.4 - version: 7.25.4(@babel/core@7.29.7) '@react-native-community/cli': specifier: catalog:testers version: 20.2.0(typescript@7.0.2) @@ -796,6 +793,9 @@ importers: packages/repack: dependencies: + '@babel/plugin-syntax-typescript': + specifier: ^7.25.4 + version: 7.25.4(@babel/core@7.29.7) '@callstack/repack-dev-server': specifier: workspace:* version: link:../dev-server diff --git a/tests/integration/src/loaders/react-native-parser.test.ts b/tests/integration/src/loaders/react-native-parser.test.ts index 8ded65e30..8244a2c51 100644 --- a/tests/integration/src/loaders/react-native-parser.test.ts +++ b/tests/integration/src/loaders/react-native-parser.test.ts @@ -87,12 +87,13 @@ function createFixtureProject( linkPackage(projectRoot, '@callstack/repack', repackRoot); linkPackage(projectRoot, '@babel/runtime', babelRuntimeRoot); - for (const name of [ + linkPackage( + projectRoot, '@react-native/babel-plugin-codegen', - '@babel/plugin-syntax-typescript', - ]) { - linkPackage(projectRoot, name, resolvePresetPackage(name)); - } + resolvePresetPackage('@react-native/babel-plugin-codegen') + ); + // '@babel/plugin-syntax-typescript' is intentionally not linked: Re.Pack + // resolves it from its own dependencies, as with isolated (e.g. pnpm) installs. // Webpack needs a resolvable SWC to exercise the SWC path instead of Babel. linkPackage(projectRoot, '@swc/core', swcRoot); }