From e99dfca8a8a262636c19a76b432e7e05e47ebc27 Mon Sep 17 00:00:00 2001 From: Sverre Johansen Date: Thu, 12 Mar 2026 14:49:35 +0100 Subject: [PATCH 1/4] fix(babel-plugin-import-path-remapper): remap packages with exports field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, `findMainSourceFile` bailed out entirely when a package had an `exports` field, preventing lib/ → src/ remapping. This caused Metro to load pre-built files instead of source, breaking localization in development for packages that use both `exports` and `declareString`. Now extract the main entry point from `exports["."]` when `main` is not available, and proceed with remapping as usual. --- .../src/index.js | 21 +++++++++----- .../@rnx-kit/example/lib/index.js | 1 + .../@rnx-kit/example/package.json | 12 ++++++++ .../@rnx-kit/example/src/index.ts | 1 + .../babel-plugin-import-path-remapper.test.ts | 28 +++++++++++++++++++ 5 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports/node_modules/@rnx-kit/example/lib/index.js create mode 100644 packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports/node_modules/@rnx-kit/example/package.json create mode 100644 packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports/node_modules/@rnx-kit/example/src/index.ts diff --git a/packages/babel-plugin-import-path-remapper/src/index.js b/packages/babel-plugin-import-path-remapper/src/index.js index 7d0e4e56fc..f7dd4fcc5c 100644 --- a/packages/babel-plugin-import-path-remapper/src/index.js +++ b/packages/babel-plugin-import-path-remapper/src/index.js @@ -45,25 +45,32 @@ function findMainSourceFile(sourcePath, requester, customRemap) { resolveOptions ); - const { main, exports } = readPackage(manifestPath); - if (exports) { - // Skip packages that declare entry points - return; + const { main, exports: pkgExports } = readPackage(manifestPath); + + // Determine the main entry point from `main` or `exports["."]` + let mainEntry = typeof main === "string" ? main : undefined; + if (!mainEntry && pkgExports) { + const root = pkgExports["."]; + if (typeof root === "string") { + mainEntry = root; + } else if (root && typeof root === "object") { + mainEntry = root.default || root.require || root.import; + } } if (customRemap) { return customRemap( sourcePath, - typeof main === "string" ? main : undefined, + typeof mainEntry === "string" ? mainEntry : undefined, requester ); } - if (typeof main !== "string") { + if (typeof mainEntry !== "string") { return; } - const remappedPath = `${sourcePath}/${main.replace( + const remappedPath = `${sourcePath}/${mainEntry.replace( /^(?:\.\/)?lib\/(.*)\.js/, "src/$1.ts" )}`; diff --git a/packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports/node_modules/@rnx-kit/example/lib/index.js b/packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports/node_modules/@rnx-kit/example/lib/index.js new file mode 100644 index 0000000000..f053ebf797 --- /dev/null +++ b/packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports/node_modules/@rnx-kit/example/lib/index.js @@ -0,0 +1 @@ +module.exports = {}; diff --git a/packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports/node_modules/@rnx-kit/example/package.json b/packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports/node_modules/@rnx-kit/example/package.json new file mode 100644 index 0000000000..203648cf3b --- /dev/null +++ b/packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports/node_modules/@rnx-kit/example/package.json @@ -0,0 +1,12 @@ +{ + "name": "@rnx-kit/example", + "version": "0.0.0-dev", + "main": "lib/index.js", + "exports": { + ".": { + "types": "./lib/index.d.ts", + "default": "./lib/index.js" + }, + "./package.json": "./package.json" + } +} diff --git a/packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports/node_modules/@rnx-kit/example/src/index.ts b/packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports/node_modules/@rnx-kit/example/src/index.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports/node_modules/@rnx-kit/example/src/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/babel-plugin-import-path-remapper/test/babel-plugin-import-path-remapper.test.ts b/packages/babel-plugin-import-path-remapper/test/babel-plugin-import-path-remapper.test.ts index 40d0b24e81..cbef55b609 100644 --- a/packages/babel-plugin-import-path-remapper/test/babel-plugin-import-path-remapper.test.ts +++ b/packages/babel-plugin-import-path-remapper/test/babel-plugin-import-path-remapper.test.ts @@ -161,4 +161,32 @@ describe("@rnx-kit/babel-plugin-import-path-remapper", () => { `import(/* webpackChunkName: "example" */"@rnx-kit/example/__mocks__/lib/index");` ); }); + + it("remaps barrel import for package with exports field", () => { + process.chdir("test/__fixtures__/with-exports"); + equal( + transform(`import { A } from "@rnx-kit/example";`), + `import { A } from "@rnx-kit/example/src/index.ts";` + ); + }); + + it("remaps barrel require for package with exports field", () => { + process.chdir("test/__fixtures__/with-exports"); + equal( + transform(`require("@rnx-kit/example");`), + `require("@rnx-kit/example/src/index.ts");` + ); + }); + + it("uses custom remap for package with exports field", () => { + process.chdir("test/__fixtures__/with-exports"); + equal( + transform(`import { A } from "@rnx-kit/example";`, { + test: isRNXKit, + remap: (moduleName: string, path: string) => + `${moduleName}/__mocks__/${path}`, + }), + `import { A } from "@rnx-kit/example/__mocks__/lib/index.js";` + ); + }); }); From 687782e0ad2ed338b886a7d2aee99af9db171166 Mon Sep 17 00:00:00 2001 From: Sverre Johansen Date: Thu, 12 Mar 2026 14:58:56 +0100 Subject: [PATCH 2/4] Add changeset for babel-plugin-import-path-remapper fix --- .changeset/fix-remapper-exports.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fix-remapper-exports.md diff --git a/.changeset/fix-remapper-exports.md b/.changeset/fix-remapper-exports.md new file mode 100644 index 0000000000..489378dc0d --- /dev/null +++ b/.changeset/fix-remapper-exports.md @@ -0,0 +1,5 @@ +--- +"@rnx-kit/babel-plugin-import-path-remapper": patch +--- + +Fix lib/ → src/ remapping for packages with an `exports` field in package.json. Previously, packages declaring `exports` were skipped entirely, causing bundlers to load pre-built files instead of source during development. From a70619d4a549a46b513b4e182e89f67d2393168e Mon Sep 17 00:00:00 2001 From: Sverre Johansen Date: Mon, 20 Apr 2026 16:31:01 +0200 Subject: [PATCH 3/4] fix: prefer TypeScript source entries in exports["."] resolution --- .../src/index.js | 6 +++++- .../@rnx-kit/dts-example/lib/index.js | 2 ++ .../@rnx-kit/dts-example/package.json | 11 +++++++++++ .../@rnx-kit/ts-example/lib/index.js | 2 ++ .../@rnx-kit/ts-example/package.json | 12 ++++++++++++ .../@rnx-kit/ts-example/src/index.ts | 1 + .../babel-plugin-import-path-remapper.test.ts | 16 ++++++++++++++++ 7 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports-dts/node_modules/@rnx-kit/dts-example/lib/index.js create mode 100644 packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports-dts/node_modules/@rnx-kit/dts-example/package.json create mode 100644 packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports-ts/node_modules/@rnx-kit/ts-example/lib/index.js create mode 100644 packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports-ts/node_modules/@rnx-kit/ts-example/package.json create mode 100644 packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports-ts/node_modules/@rnx-kit/ts-example/src/index.ts diff --git a/packages/babel-plugin-import-path-remapper/src/index.js b/packages/babel-plugin-import-path-remapper/src/index.js index f7dd4fcc5c..7777a8c581 100644 --- a/packages/babel-plugin-import-path-remapper/src/index.js +++ b/packages/babel-plugin-import-path-remapper/src/index.js @@ -54,7 +54,11 @@ function findMainSourceFile(sourcePath, requester, customRemap) { if (typeof root === "string") { mainEntry = root; } else if (root && typeof root === "object") { - mainEntry = root.default || root.require || root.import; + // Prefer a TypeScript source entry (.ts/.tsx but not .d.ts/.d.tsx) + const tsEntry = Object.values(root).find( + (v) => typeof v === "string" && /\.tsx?$/.test(v) && !/\.d\.tsx?$/.test(v) + ); + mainEntry = tsEntry || root.default || root.require || root.import; } } diff --git a/packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports-dts/node_modules/@rnx-kit/dts-example/lib/index.js b/packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports-dts/node_modules/@rnx-kit/dts-example/lib/index.js new file mode 100644 index 0000000000..f422d6b00c --- /dev/null +++ b/packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports-dts/node_modules/@rnx-kit/dts-example/lib/index.js @@ -0,0 +1,2 @@ +"use strict"; +exports.A = 0; diff --git a/packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports-dts/node_modules/@rnx-kit/dts-example/package.json b/packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports-dts/node_modules/@rnx-kit/dts-example/package.json new file mode 100644 index 0000000000..108ac2bf6e --- /dev/null +++ b/packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports-dts/node_modules/@rnx-kit/dts-example/package.json @@ -0,0 +1,11 @@ +{ + "name": "@rnx-kit/dts-example", + "version": "0.0.0-dev", + "exports": { + ".": { + "types": "./lib/index.d.ts", + "default": "./lib/index.js" + }, + "./package.json": "./package.json" + } +} diff --git a/packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports-ts/node_modules/@rnx-kit/ts-example/lib/index.js b/packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports-ts/node_modules/@rnx-kit/ts-example/lib/index.js new file mode 100644 index 0000000000..f422d6b00c --- /dev/null +++ b/packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports-ts/node_modules/@rnx-kit/ts-example/lib/index.js @@ -0,0 +1,2 @@ +"use strict"; +exports.A = 0; diff --git a/packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports-ts/node_modules/@rnx-kit/ts-example/package.json b/packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports-ts/node_modules/@rnx-kit/ts-example/package.json new file mode 100644 index 0000000000..22735ab299 --- /dev/null +++ b/packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports-ts/node_modules/@rnx-kit/ts-example/package.json @@ -0,0 +1,12 @@ +{ + "name": "@rnx-kit/ts-example", + "version": "0.0.0-dev", + "exports": { + ".": { + "types": "./lib/index.d.ts", + "source": "./src/index.ts", + "default": "./lib/index.js" + }, + "./package.json": "./package.json" + } +} diff --git a/packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports-ts/node_modules/@rnx-kit/ts-example/src/index.ts b/packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports-ts/node_modules/@rnx-kit/ts-example/src/index.ts new file mode 100644 index 0000000000..94f49c3e07 --- /dev/null +++ b/packages/babel-plugin-import-path-remapper/test/__fixtures__/with-exports-ts/node_modules/@rnx-kit/ts-example/src/index.ts @@ -0,0 +1 @@ +export const A = 0; diff --git a/packages/babel-plugin-import-path-remapper/test/babel-plugin-import-path-remapper.test.ts b/packages/babel-plugin-import-path-remapper/test/babel-plugin-import-path-remapper.test.ts index cbef55b609..7285c2970d 100644 --- a/packages/babel-plugin-import-path-remapper/test/babel-plugin-import-path-remapper.test.ts +++ b/packages/babel-plugin-import-path-remapper/test/babel-plugin-import-path-remapper.test.ts @@ -189,4 +189,20 @@ describe("@rnx-kit/babel-plugin-import-path-remapper", () => { `import { A } from "@rnx-kit/example/__mocks__/lib/index.js";` ); }); + + it("prefers TypeScript source entry in exports over default", () => { + process.chdir("test/__fixtures__/with-exports-ts"); + equal( + transform(`import { A } from "@rnx-kit/ts-example";`), + `import { A } from "@rnx-kit/ts-example/./src/index.ts";` + ); + }); + + it("does not pick .d.ts entries as the main source", () => { + process.chdir("test/__fixtures__/with-exports-dts"); + equal( + transform(`import { A } from "@rnx-kit/dts-example";`), + `import { A } from "@rnx-kit/dts-example/src/index.ts";` + ); + }); }); From e79dc968d6f7d07b70d5e573a1345de6a4185a4a Mon Sep 17 00:00:00 2001 From: Sverre Johansen Date: Thu, 23 Apr 2026 22:09:16 +0200 Subject: [PATCH 4/4] fix: simplify .d.ts exclusion regex --- packages/babel-plugin-import-path-remapper/src/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/babel-plugin-import-path-remapper/src/index.js b/packages/babel-plugin-import-path-remapper/src/index.js index 7777a8c581..2b5ec14345 100644 --- a/packages/babel-plugin-import-path-remapper/src/index.js +++ b/packages/babel-plugin-import-path-remapper/src/index.js @@ -54,9 +54,9 @@ function findMainSourceFile(sourcePath, requester, customRemap) { if (typeof root === "string") { mainEntry = root; } else if (root && typeof root === "object") { - // Prefer a TypeScript source entry (.ts/.tsx but not .d.ts/.d.tsx) + // Prefer a TypeScript source entry (.ts/.tsx but not .d.ts) const tsEntry = Object.values(root).find( - (v) => typeof v === "string" && /\.tsx?$/.test(v) && !/\.d\.tsx?$/.test(v) + (v) => typeof v === "string" && /\.tsx?$/.test(v) && !/\.d\.ts$/.test(v) ); mainEntry = tsEntry || root.default || root.require || root.import; }