diff --git a/.changeset/tough-owls-collect.md b/.changeset/tough-owls-collect.md new file mode 100644 index 000000000..df90ca52a --- /dev/null +++ b/.changeset/tough-owls-collect.md @@ -0,0 +1,5 @@ +--- +"@rnx-kit/metro-resolver-symlinks": patch +--- + +Reuse `findMetroPath` from `@rnx-kit/tools-react-native` diff --git a/packages/metro-resolver-symlinks/src/helper.ts b/packages/metro-resolver-symlinks/src/helper.ts index 95b810816..956157456 100644 --- a/packages/metro-resolver-symlinks/src/helper.ts +++ b/packages/metro-resolver-symlinks/src/helper.ts @@ -1,7 +1,5 @@ -import { - findPackageDependencyDir, - readPackage, -} from "@rnx-kit/tools-node/package"; +import { findPackageDependencyDir } from "@rnx-kit/tools-node/package"; +import { findMetroPath } from "@rnx-kit/tools-react-native/metro"; export function resolveFrom( moduleName: string, @@ -13,31 +11,6 @@ export function resolveFrom( }); } -export function ensureResolveFrom( - moduleName: string, - startDir: string -): string { - const p = resolveFrom(moduleName, startDir); - if (!p) { - throw new Error(`Cannot find module '${moduleName}'`); - } - return p; -} - -/** - * Returns the directory in which `metro` and its dependencies were installed. - * @param fromDir The directory to start searching from - */ -export function getMetroSearchPath(fromDir = process.cwd()): string { - const rnPath = ensureResolveFrom("react-native", fromDir); - const rncliPath = ensureResolveFrom("@react-native-community/cli", rnPath); - - const { dependencies = {} } = readPackage(rncliPath); - return "@react-native-community/cli-plugin-metro" in dependencies - ? ensureResolveFrom("@react-native-community/cli-plugin-metro", rncliPath) - : rncliPath; -} - /** * Imports specified module starting from the installation directory of the * currently used `metro` version. @@ -49,13 +22,22 @@ export function requireModuleFromMetro( moduleName: string, fromDir = process.cwd() ): R { - try { - const startDir = getMetroSearchPath(fromDir); - const metroModulePath = ensureResolveFrom(moduleName, startDir); - return require(metroModulePath); - } catch (_) { + const startDir = findMetroPath(fromDir); + if (!startDir) { + throw new Error("Cannot find module 'metro'"); + } + + const modulePath = resolveFrom(moduleName, startDir); + if (!modulePath) { throw new Error( - `Cannot find module '${moduleName}'. This probably means that '@rnx-kit/metro-resolver-symlinks' is not compatible with the version of 'metro' that you are currently using. Please update to the latest version and try again. If the issue still persists after the update, please file a bug at https://github.com/microsoft/rnx-kit/issues.` + `Cannot find module '${moduleName}'. This probably means that ` + + "'@rnx-kit/metro-resolver-symlinks' is not compatible with the " + + "version of 'metro' that you are currently using. Please update to " + + "the latest version and try again. If the issue still persists after " + + "the update, please file a bug at " + + "https://github.com/microsoft/rnx-kit/issues." ); } + + return require(modulePath); } diff --git a/packages/metro-resolver-symlinks/src/utils/patchMetro.ts b/packages/metro-resolver-symlinks/src/utils/patchMetro.ts index 0415c1ad0..787a34001 100644 --- a/packages/metro-resolver-symlinks/src/utils/patchMetro.ts +++ b/packages/metro-resolver-symlinks/src/utils/patchMetro.ts @@ -1,6 +1,6 @@ +import { findMetroPath } from "@rnx-kit/tools-react-native/metro"; import * as fs from "fs"; import * as path from "path"; -import { ensureResolveFrom, getMetroSearchPath } from "../helper"; import type { Options } from "../types"; function fileExists(path: string): boolean { @@ -9,7 +9,7 @@ function fileExists(path: string): boolean { } function importMetroModule(path: string) { - const metroPath = ensureResolveFrom("metro", getMetroSearchPath()); + const metroPath = findMetroPath(); const modulePath = metroPath + path; try { return require(modulePath); diff --git a/packages/metro-resolver-symlinks/test/__fixtures__/metro-resolver-duplicates/node_modules/@react-native-community/cli/node_modules/metro/index.js b/packages/metro-resolver-symlinks/test/__fixtures__/metro-resolver-duplicates/node_modules/@react-native-community/cli/node_modules/metro/index.js new file mode 100644 index 000000000..b552f04c1 --- /dev/null +++ b/packages/metro-resolver-symlinks/test/__fixtures__/metro-resolver-duplicates/node_modules/@react-native-community/cli/node_modules/metro/index.js @@ -0,0 +1 @@ +exports.resolve = () => __dirname; diff --git a/packages/metro-resolver-symlinks/test/__fixtures__/metro-resolver-duplicates/node_modules/@react-native-community/cli/node_modules/metro/package.json b/packages/metro-resolver-symlinks/test/__fixtures__/metro-resolver-duplicates/node_modules/@react-native-community/cli/node_modules/metro/package.json new file mode 100644 index 000000000..21ac4b390 --- /dev/null +++ b/packages/metro-resolver-symlinks/test/__fixtures__/metro-resolver-duplicates/node_modules/@react-native-community/cli/node_modules/metro/package.json @@ -0,0 +1,5 @@ +{ + "name": "metro", + "version": "1.0.0", + "main": "index.js" +} diff --git a/packages/metro-resolver-symlinks/test/__fixtures__/pnpm/node_modules/.pnpm/@react-native-community+cli-plugin-metro@7.0.3/node_modules/metro b/packages/metro-resolver-symlinks/test/__fixtures__/pnpm/node_modules/.pnpm/@react-native-community+cli-plugin-metro@7.0.3/node_modules/metro new file mode 120000 index 000000000..6e3eeeceb --- /dev/null +++ b/packages/metro-resolver-symlinks/test/__fixtures__/pnpm/node_modules/.pnpm/@react-native-community+cli-plugin-metro@7.0.3/node_modules/metro @@ -0,0 +1 @@ +../../metro@0.67.0/node_modules/metro \ No newline at end of file diff --git a/packages/metro-resolver-symlinks/test/__fixtures__/pnpm/node_modules/.pnpm/@react-native-community+cli-plugin-metro@7.0.3/node_modules/metro-resolver b/packages/metro-resolver-symlinks/test/__fixtures__/pnpm/node_modules/.pnpm/metro@0.67.0/node_modules/metro-resolver similarity index 100% rename from packages/metro-resolver-symlinks/test/__fixtures__/pnpm/node_modules/.pnpm/@react-native-community+cli-plugin-metro@7.0.3/node_modules/metro-resolver rename to packages/metro-resolver-symlinks/test/__fixtures__/pnpm/node_modules/.pnpm/metro@0.67.0/node_modules/metro-resolver diff --git a/packages/metro-resolver-symlinks/test/__fixtures__/pnpm/node_modules/.pnpm/metro@0.67.0/node_modules/metro/index.js b/packages/metro-resolver-symlinks/test/__fixtures__/pnpm/node_modules/.pnpm/metro@0.67.0/node_modules/metro/index.js new file mode 100644 index 000000000..b552f04c1 --- /dev/null +++ b/packages/metro-resolver-symlinks/test/__fixtures__/pnpm/node_modules/.pnpm/metro@0.67.0/node_modules/metro/index.js @@ -0,0 +1 @@ +exports.resolve = () => __dirname; diff --git a/packages/metro-resolver-symlinks/test/__fixtures__/pnpm/node_modules/.pnpm/metro@0.67.0/node_modules/metro/package.json b/packages/metro-resolver-symlinks/test/__fixtures__/pnpm/node_modules/.pnpm/metro@0.67.0/node_modules/metro/package.json new file mode 100644 index 000000000..ae254a2b8 --- /dev/null +++ b/packages/metro-resolver-symlinks/test/__fixtures__/pnpm/node_modules/.pnpm/metro@0.67.0/node_modules/metro/package.json @@ -0,0 +1,5 @@ +{ + "name": "metro", + "version": "0.67.0", + "main": "index.js" +} diff --git a/packages/metro-resolver-symlinks/test/resolver.test.ts b/packages/metro-resolver-symlinks/test/resolver.test.ts index cbd8e240c..978daba28 100644 --- a/packages/metro-resolver-symlinks/test/resolver.test.ts +++ b/packages/metro-resolver-symlinks/test/resolver.test.ts @@ -1,5 +1,5 @@ -import * as os from "os"; -import * as path from "path"; +import * as os from "node:os"; +import * as path from "node:path"; import { requireModuleFromMetro } from "../src/helper"; import { remapReactNativeModule, resolveModulePath } from "../src/resolver"; import { useFixture } from "./fixtures"; @@ -12,17 +12,17 @@ const AVAILABLE_PLATFORMS = { const nixOnlyTest = os.platform() === "win32" ? test.skip : test; -function getMetroResolver(fromDir: string) { - return requireModuleFromMetro( - "metro-resolver", - fromDir - ).resolve; -} - -describe("getMetroResolver", () => { +describe("requireModuleFromMetro", () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const context: any = {}; + function getMetroResolver(fromDir: string) { + return requireModuleFromMetro( + "metro-resolver", + fromDir + ).resolve; + } + test("returns `metro-resolver` installed by `react-native`", () => { const p = useFixture("metro-resolver-duplicates"); expect(getMetroResolver(p)(context, "", null)).toEqual( diff --git a/packages/tools-react-native/src/metro.ts b/packages/tools-react-native/src/metro.ts index e838f9be5..a05d8e406 100644 --- a/packages/tools-react-native/src/metro.ts +++ b/packages/tools-react-native/src/metro.ts @@ -3,7 +3,7 @@ import { readPackage, } from "@rnx-kit/tools-node/package"; -function resolveDependency(name: string, startDir: string): string | undefined { +function resolveFrom(name: string, startDir: string): string | undefined { return findPackageDependencyDir(name, { startDir, resolveSymlinks: true, @@ -16,21 +16,21 @@ function resolveDependency(name: string, startDir: string): string | undefined { * @returns The path to the Metro installation; `undefined` if Metro could not be found */ export function findMetroPath(projectRoot = process.cwd()): string | undefined { - const rnPath = resolveDependency("react-native", projectRoot); + const rnPath = resolveFrom("react-native", projectRoot); if (!rnPath) { return undefined; } - const cliPath = resolveDependency("@react-native-community/cli", rnPath); + const cliPath = resolveFrom("@react-native-community/cli", rnPath); if (!cliPath) { return undefined; } - const cliMetroPath = resolveDependency( + const cliMetroPath = resolveFrom( "@react-native-community/cli-plugin-metro", cliPath ); - return resolveDependency("metro", cliMetroPath || cliPath); + return resolveFrom("metro", cliMetroPath || cliPath); } /**