diff --git a/.changeset/beige-bikes-accept.md b/.changeset/beige-bikes-accept.md new file mode 100644 index 000000000..2a41e8c00 --- /dev/null +++ b/.changeset/beige-bikes-accept.md @@ -0,0 +1,5 @@ +--- +"@rnx-kit/metro-resolver-symlinks": patch +--- + +Get available platforms from disk instead of using a hard-coded list diff --git a/.changeset/eight-terms-promise.md b/.changeset/eight-terms-promise.md new file mode 100644 index 000000000..334b90dfb --- /dev/null +++ b/.changeset/eight-terms-promise.md @@ -0,0 +1,5 @@ +--- +"@rnx-kit/tools-react-native": patch +--- + +Added a function to get available React Native platforms diff --git a/.changeset/heavy-rocks-relax.md b/.changeset/heavy-rocks-relax.md new file mode 100644 index 000000000..9de0c9e14 --- /dev/null +++ b/.changeset/heavy-rocks-relax.md @@ -0,0 +1,5 @@ +--- +"@rnx-kit/typescript-react-native-resolver": patch +--- + +Get available platforms from disk instead of using a hard-coded list diff --git a/packages/metro-resolver-symlinks/src/resolver.ts b/packages/metro-resolver-symlinks/src/resolver.ts index 02f17ade5..ee514ded6 100644 --- a/packages/metro-resolver-symlinks/src/resolver.ts +++ b/packages/metro-resolver-symlinks/src/resolver.ts @@ -1,5 +1,5 @@ import { isFileModuleRef, parseModuleRef } from "@rnx-kit/tools-node"; -import { AVAILABLE_PLATFORMS } from "@rnx-kit/tools-react-native"; +import { getAvailablePlatforms } from "@rnx-kit/tools-react-native"; import * as path from "path"; import type { MetroResolver, ModuleResolver } from "./types"; @@ -33,7 +33,7 @@ export const remapReactNativeModule: ModuleResolver = ( moduleName, platform ) => { - const platformImpl = AVAILABLE_PLATFORMS[platform]; + const platformImpl = getAvailablePlatforms()[platform]; if (platformImpl) { if (moduleName === "react-native") { return platformImpl; diff --git a/packages/metro-resolver-symlinks/test/__fixtures__/remap-platforms/node_modules/@office-iss/react-native-win32/package.json b/packages/metro-resolver-symlinks/test/__fixtures__/remap-platforms/node_modules/@office-iss/react-native-win32/package.json new file mode 100644 index 000000000..c2649c6c7 --- /dev/null +++ b/packages/metro-resolver-symlinks/test/__fixtures__/remap-platforms/node_modules/@office-iss/react-native-win32/package.json @@ -0,0 +1,4 @@ +{ + "name": "@office-iss/react-native-win32", + "version": "0.0.0-dev" +} diff --git a/packages/metro-resolver-symlinks/test/__fixtures__/remap-platforms/node_modules/@office-iss/react-native-win32/react-native.config.js b/packages/metro-resolver-symlinks/test/__fixtures__/remap-platforms/node_modules/@office-iss/react-native-win32/react-native.config.js new file mode 100644 index 000000000..5b7f24705 --- /dev/null +++ b/packages/metro-resolver-symlinks/test/__fixtures__/remap-platforms/node_modules/@office-iss/react-native-win32/react-native.config.js @@ -0,0 +1,10 @@ +module.exports = { + platforms: { + win32: { + linkConfig: () => null, + projectConfig: (projectRoot, projectParams) => null, + dependencyConfig: (projectRoot, dependencyParams) => null, + npmPackageName: "@office-iss/react-native-win32", + }, + }, +}; diff --git a/packages/metro-resolver-symlinks/test/__fixtures__/remap-platforms/node_modules/react-native-macos/package.json b/packages/metro-resolver-symlinks/test/__fixtures__/remap-platforms/node_modules/react-native-macos/package.json new file mode 100644 index 000000000..9036896d1 --- /dev/null +++ b/packages/metro-resolver-symlinks/test/__fixtures__/remap-platforms/node_modules/react-native-macos/package.json @@ -0,0 +1,4 @@ +{ + "name": "react-native-macos", + "version": "0.0.0-dev" +} diff --git a/packages/metro-resolver-symlinks/test/__fixtures__/remap-platforms/node_modules/react-native-macos/react-native.config.js b/packages/metro-resolver-symlinks/test/__fixtures__/remap-platforms/node_modules/react-native-macos/react-native.config.js new file mode 100644 index 000000000..699df167d --- /dev/null +++ b/packages/metro-resolver-symlinks/test/__fixtures__/remap-platforms/node_modules/react-native-macos/react-native.config.js @@ -0,0 +1,11 @@ +module.exports = { + platforms: { + ios: {}, + android: {}, + macos: { + projectConfig: () => null, + dependencyConfig: () => null, + npmPackageName: "react-native-macos", + }, + }, +}; diff --git a/packages/metro-resolver-symlinks/test/__fixtures__/remap-platforms/node_modules/react-native-windows/package.json b/packages/metro-resolver-symlinks/test/__fixtures__/remap-platforms/node_modules/react-native-windows/package.json new file mode 100644 index 000000000..2569f3f04 --- /dev/null +++ b/packages/metro-resolver-symlinks/test/__fixtures__/remap-platforms/node_modules/react-native-windows/package.json @@ -0,0 +1,4 @@ +{ + "name": "react-native-windows", + "version": "0.0.0-dev" +} diff --git a/packages/metro-resolver-symlinks/test/__fixtures__/remap-platforms/node_modules/react-native-windows/react-native.config.js b/packages/metro-resolver-symlinks/test/__fixtures__/remap-platforms/node_modules/react-native-windows/react-native.config.js new file mode 100644 index 000000000..794a78f2e --- /dev/null +++ b/packages/metro-resolver-symlinks/test/__fixtures__/remap-platforms/node_modules/react-native-windows/react-native.config.js @@ -0,0 +1,8 @@ +module.exports = { + platforms: { + windows: { + linkConfig: () => null, + npmPackageName: "react-native-windows", + }, + }, +}; diff --git a/packages/metro-resolver-symlinks/test/__fixtures__/remap-platforms/package.json b/packages/metro-resolver-symlinks/test/__fixtures__/remap-platforms/package.json new file mode 100644 index 000000000..30125f7f1 --- /dev/null +++ b/packages/metro-resolver-symlinks/test/__fixtures__/remap-platforms/package.json @@ -0,0 +1,10 @@ +{ + "name": "@rnx-kit/metro-resolver-symlinks/remap-tests", + "version": "0.0.0-dev", + "dependencies": { + "@office-iss/react-native-win32": "0.0.0-dev", + "react-native": "0.0.0-dev", + "react-native-macos": "0.0.0-dev", + "react-native-windows": "0.0.0-dev" + } +} diff --git a/packages/metro-resolver-symlinks/test/index.test.ts b/packages/metro-resolver-symlinks/test/index.test.ts index 324de9bfa..36b76bb01 100644 --- a/packages/metro-resolver-symlinks/test/index.test.ts +++ b/packages/metro-resolver-symlinks/test/index.test.ts @@ -1,4 +1,3 @@ -import { AVAILABLE_PLATFORMS } from "@rnx-kit/tools-react-native"; import * as path from "path"; import { getMetroResolver, @@ -6,6 +5,12 @@ import { resolveModulePath, } from "../src/resolver"; +const AVAILABLE_PLATFORMS = { + macos: "react-native-macos", + win32: "@office-iss/react-native-win32", + windows: "react-native-windows", +}; + function useFixture(name: string): string { return path.join(__dirname, "__fixtures__", name); } @@ -32,7 +37,7 @@ describe("getMetroResolver", () => { test("throws if `metro-resolver` cannot be found", () => { const cwd = process.cwd(); - const root = cwd.substr(0, cwd.indexOf(path.sep) + 1); + const root = cwd.substring(0, cwd.indexOf(path.sep) + 1); expect(() => getMetroResolver(root)(context, "", null)).toThrowError( "Cannot find module" ); @@ -44,6 +49,16 @@ describe("remapReactNativeModule", () => { originModulePath: "", }; + const currentDir = process.cwd(); + + beforeAll(() => { + process.chdir(useFixture("remap-platforms")); + }); + + afterAll(() => { + process.chdir(currentDir); + }); + test("remaps `react-native` if platform is supported", () => { expect(remapReactNativeModule(context, "terminator", "macos")).toBe( "terminator" diff --git a/packages/tools-react-native/README.md b/packages/tools-react-native/README.md index 64cce0207..7bc74ac3f 100644 --- a/packages/tools-react-native/README.md +++ b/packages/tools-react-native/README.md @@ -22,10 +22,12 @@ import * from "@rnx-kit/tools-react-native/platform"; | -------- | ------------ | ----------------------------------------- | | platform | AllPlatforms | List of supported react-native platforms. | -| Category | Function | Description | -| -------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------------- | -| platform | `expandPlatformExtensions(platform, extensions)` | Returns a list of extensions that should be tried for the target platform in prioritized order. | -| platform | `parsePlatform(val)` | Parse a string to ensure it maps to a valid react-native platform. | -| platform | `platformExtensions(platform)` | Returns file extensions that can be mapped to the target platform. | +| Category | Function | Description | +| -------- | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------- | +| platform | `expandPlatformExtensions(platform, extensions)` | Returns a list of extensions that should be tried for the target platform in prioritized order. | +| platform | `getAvailablePlatforms(startDir)` | Returns a map of available React Native platforms. The result is cached. | +| platform | `getAvailablePlatformsUncached(startDir, platformMap)` | Returns a map of available React Native platforms. The result is NOT cached. | +| platform | `parsePlatform(val)` | Parse a string to ensure it maps to a valid react-native platform. | +| platform | `platformExtensions(platform)` | Returns file extensions that can be mapped to the target platform. | diff --git a/packages/tools-react-native/src/index.ts b/packages/tools-react-native/src/index.ts index 2078d0807..c1911de63 100644 --- a/packages/tools-react-native/src/index.ts +++ b/packages/tools-react-native/src/index.ts @@ -1,6 +1,7 @@ export { - AVAILABLE_PLATFORMS, expandPlatformExtensions, + getAvailablePlatforms, + getAvailablePlatformsUncached, parsePlatform, platformExtensions, } from "./platform"; diff --git a/packages/tools-react-native/src/platform.ts b/packages/tools-react-native/src/platform.ts index ba29db335..e073d3f8b 100644 --- a/packages/tools-react-native/src/platform.ts +++ b/packages/tools-react-native/src/platform.ts @@ -1,17 +1,11 @@ +import * as fs from "fs"; +import * as path from "path"; + /** * List of supported react-native platforms. */ export type AllPlatforms = "ios" | "android" | "windows" | "win32" | "macos"; -// TODO: `react-native config` is too slow. Hard-coding this list until we can -// figure out a better solution. -// See https://github.com/microsoft/rnx-kit/issues/925 -export const AVAILABLE_PLATFORMS: Record = { - macos: "react-native-macos", - win32: "@office-iss/react-native-win32", - windows: "react-native-windows", -}; - /** * Returns a list of extensions that should be tried for the target platform in * prioritized order. @@ -34,6 +28,69 @@ export function expandPlatformExtensions( return expanded; } +/** + * Returns a map of available React Native platforms. The result is cached. + * @privateRemarks is-arrow-function + * @param startDir The directory to look for react-native platforms from + * @returns A platform-to-npm-package map, excluding "core" platforms. + */ +export const getAvailablePlatforms = (() => { + let platformMap: Record | undefined = undefined; + return (startDir: string = process.cwd()) => { + if (!platformMap) { + platformMap = getAvailablePlatformsUncached(startDir); + } + return platformMap; + }; +})(); + +/** + * Returns a map of available React Native platforms. The result is NOT cached. + * @param startDir The directory to look for react-native platforms from + * @param platformMap A platform-to-npm-package map of known packages + * @returns A platform-to-npm-package map, excluding "core" platforms. + */ +export function getAvailablePlatformsUncached( + startDir = process.cwd(), + platformMap: Record = { android: "", ios: "" } +): Record { + const packageJson = path.join(startDir, "package.json"); + if (!fs.existsSync(packageJson)) { + const parent = path.dirname(startDir); + return parent === startDir + ? platformMap + : getAvailablePlatformsUncached(path.dirname(startDir), platformMap); + } + + const resolveOptions = { paths: [startDir] }; + const { dependencies, devDependencies } = require(packageJson); + [ + ...(dependencies ? Object.keys(dependencies) : []), + ...(devDependencies ? Object.keys(devDependencies) : []), + ].forEach((pkgName) => { + const pkgPath = path.dirname( + require.resolve(`${pkgName}/package.json`, resolveOptions) + ); + + const configPath = path.join(pkgPath, "react-native.config.js"); + if (fs.existsSync(configPath)) { + const { platforms } = require(configPath); + if (platforms) { + Object.keys(platforms).forEach((platform) => { + if (typeof platformMap[platform] === "undefined") { + const { npmPackageName } = platforms[platform]; + if (npmPackageName) { + platformMap[platform] = npmPackageName; + } + } + }); + } + } + }); + + return platformMap; +} + /** * Returns file extensions that can be mapped to the target platform. * @param platform The platform to retrieve extensions for diff --git a/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/@office-iss/react-native-win32/package.json b/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/@office-iss/react-native-win32/package.json new file mode 100644 index 000000000..c2649c6c7 --- /dev/null +++ b/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/@office-iss/react-native-win32/package.json @@ -0,0 +1,4 @@ +{ + "name": "@office-iss/react-native-win32", + "version": "0.0.0-dev" +} diff --git a/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/@office-iss/react-native-win32/react-native.config.js b/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/@office-iss/react-native-win32/react-native.config.js new file mode 100644 index 000000000..5b7f24705 --- /dev/null +++ b/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/@office-iss/react-native-win32/react-native.config.js @@ -0,0 +1,10 @@ +module.exports = { + platforms: { + win32: { + linkConfig: () => null, + projectConfig: (projectRoot, projectParams) => null, + dependencyConfig: (projectRoot, dependencyParams) => null, + npmPackageName: "@office-iss/react-native-win32", + }, + }, +}; diff --git a/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/react-native-codegen/package.json b/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/react-native-codegen/package.json new file mode 100644 index 000000000..d0bdeb9b5 --- /dev/null +++ b/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/react-native-codegen/package.json @@ -0,0 +1,4 @@ +{ + "name": "react-native-codegen", + "version": "0.0.0-dev" +} diff --git a/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/react-native-macos/package.json b/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/react-native-macos/package.json new file mode 100644 index 000000000..9036896d1 --- /dev/null +++ b/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/react-native-macos/package.json @@ -0,0 +1,4 @@ +{ + "name": "react-native-macos", + "version": "0.0.0-dev" +} diff --git a/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/react-native-macos/react-native.config.js b/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/react-native-macos/react-native.config.js new file mode 100644 index 000000000..699df167d --- /dev/null +++ b/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/react-native-macos/react-native.config.js @@ -0,0 +1,11 @@ +module.exports = { + platforms: { + ios: {}, + android: {}, + macos: { + projectConfig: () => null, + dependencyConfig: () => null, + npmPackageName: "react-native-macos", + }, + }, +}; diff --git a/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/react-native-tvos/package.json b/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/react-native-tvos/package.json new file mode 100644 index 000000000..7f834aac3 --- /dev/null +++ b/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/react-native-tvos/package.json @@ -0,0 +1,4 @@ +{ + "name": "react-native-tvos", + "version": "0.0.0-dev" +} diff --git a/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/react-native-tvos/react-native.config.js b/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/react-native-tvos/react-native.config.js new file mode 100644 index 000000000..dc8f60852 --- /dev/null +++ b/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/react-native-tvos/react-native.config.js @@ -0,0 +1,6 @@ +module.exports = { + platforms: { + ios: {}, + android: {}, + }, +}; diff --git a/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/react-native-windows/package.json b/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/react-native-windows/package.json new file mode 100644 index 000000000..2569f3f04 --- /dev/null +++ b/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/react-native-windows/package.json @@ -0,0 +1,4 @@ +{ + "name": "react-native-windows", + "version": "0.0.0-dev" +} diff --git a/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/react-native-windows/react-native.config.js b/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/react-native-windows/react-native.config.js new file mode 100644 index 000000000..794a78f2e --- /dev/null +++ b/packages/tools-react-native/test/__fixtures__/available-platforms/node_modules/react-native-windows/react-native.config.js @@ -0,0 +1,8 @@ +module.exports = { + platforms: { + windows: { + linkConfig: () => null, + npmPackageName: "react-native-windows", + }, + }, +}; diff --git a/packages/tools-react-native/test/__fixtures__/available-platforms/package.json b/packages/tools-react-native/test/__fixtures__/available-platforms/package.json new file mode 100644 index 000000000..fd40752e4 --- /dev/null +++ b/packages/tools-react-native/test/__fixtures__/available-platforms/package.json @@ -0,0 +1,12 @@ +{ + "name": "@rnx-kit/tools-react-native/available-platform-tests", + "version": "0.0.0-dev", + "dependencies": { + "@office-iss/react-native-win32": "0.0.0-dev", + "react-native": "0.0.0-dev", + "react-native-codegen": "0.0.0-dev", + "react-native-macos": "0.0.0-dev", + "react-native-tvos": "0.0.0-dev", + "react-native-windows": "0.0.0-dev" + } +} diff --git a/packages/tools-react-native/test/platform.test.ts b/packages/tools-react-native/test/platform.test.ts index b4261008d..148ca36f2 100644 --- a/packages/tools-react-native/test/platform.test.ts +++ b/packages/tools-react-native/test/platform.test.ts @@ -1,5 +1,7 @@ +import * as path from "path"; import { expandPlatformExtensions, + getAvailablePlatformsUncached, parsePlatform, platformExtensions, } from "../src/platform"; @@ -26,6 +28,47 @@ describe("React Native > Platform", () => { ]); }); + test("getAvailablePlatformsUncached() returns available platforms", () => { + const fixture = path.join(__dirname, "__fixtures__", "available-platforms"); + expect(getAvailablePlatformsUncached(fixture)).toMatchInlineSnapshot(` + Object { + "android": "", + "ios": "", + "macos": "react-native-macos", + "win32": "@office-iss/react-native-win32", + "windows": "react-native-windows", + } + `); + }); + + test("getAvailablePlatformsUncached() finds package root", () => { + const fixture = path.join( + __dirname, + "__fixtures__", + "available-platforms", + "node_modules", + "react-native" + ); + expect(getAvailablePlatformsUncached(fixture)).toMatchInlineSnapshot(` + Object { + "android": "", + "ios": "", + "macos": "react-native-macos", + "win32": "@office-iss/react-native-win32", + "windows": "react-native-windows", + } + `); + }); + + test("getAvailablePlatformsUncached() handles 'missing' package root", () => { + expect(getAvailablePlatformsUncached()).toMatchInlineSnapshot(` + Object { + "android": "", + "ios": "", + } + `); + }); + test("parsePlatform() succeeds for all known platforms", () => { expect(parsePlatform("ios")).toEqual("ios"); expect(parsePlatform("android")).toEqual("android"); diff --git a/packages/typescript-react-native-resolver/src/host.ts b/packages/typescript-react-native-resolver/src/host.ts index f4d72845b..8ba746a89 100644 --- a/packages/typescript-react-native-resolver/src/host.ts +++ b/packages/typescript-react-native-resolver/src/host.ts @@ -87,6 +87,7 @@ export function changeHostToUseReactNativeResolver({ (e) => `.${e}` // prepend a '.' to each name to make it a file extension ), replaceReactNativePackageName: createReactNativePackageNameReplacer( + host.getCurrentDirectory(), platform, disableReactNativePackageSubstitution, log diff --git a/packages/typescript-react-native-resolver/src/react-native-package-name.ts b/packages/typescript-react-native-resolver/src/react-native-package-name.ts index 0896d1740..40dde0c55 100644 --- a/packages/typescript-react-native-resolver/src/react-native-package-name.ts +++ b/packages/typescript-react-native-resolver/src/react-native-package-name.ts @@ -1,4 +1,4 @@ -import { AVAILABLE_PLATFORMS } from "@rnx-kit/tools-react-native"; +import { getAvailablePlatforms } from "@rnx-kit/tools-react-native"; import type { ResolverLog } from "./log"; const DEFAULT_PACKAGE_NAME = "react-native"; @@ -8,17 +8,19 @@ const DEFAULT_PACKAGE_NAME = "react-native"; * a reference to the target platform's react-native package. This only * happens when targeting an out-of-tree platform like Windows or MacOS. * + * @param currentDirectory Current directory - used to find available React Native platforms * @param platform Target platform * @param disableReactNativePackageSubstitution Flag controling whether or not the returned function has an effect * @param log Resolver log * @returns Function which replaces a 'react-native' module reference, or a function which has no effect if module replacement is not needed or disabled */ export function createReactNativePackageNameReplacer( + currentDirectory: string, platform: string, disableReactNativePackageSubstitution: boolean, log: ResolverLog ): (m: string) => string { - const platformPackageName = AVAILABLE_PLATFORMS[platform]; + const platformPackageName = getAvailablePlatforms(currentDirectory)[platform]; if (!platformPackageName || disableReactNativePackageSubstitution) { return (m: string) => m; } diff --git a/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/@office-iss/react-native-win32/package.json b/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/@office-iss/react-native-win32/package.json new file mode 100644 index 000000000..c2649c6c7 --- /dev/null +++ b/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/@office-iss/react-native-win32/package.json @@ -0,0 +1,4 @@ +{ + "name": "@office-iss/react-native-win32", + "version": "0.0.0-dev" +} diff --git a/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/@office-iss/react-native-win32/react-native.config.js b/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/@office-iss/react-native-win32/react-native.config.js new file mode 100644 index 000000000..5b7f24705 --- /dev/null +++ b/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/@office-iss/react-native-win32/react-native.config.js @@ -0,0 +1,10 @@ +module.exports = { + platforms: { + win32: { + linkConfig: () => null, + projectConfig: (projectRoot, projectParams) => null, + dependencyConfig: (projectRoot, dependencyParams) => null, + npmPackageName: "@office-iss/react-native-win32", + }, + }, +}; diff --git a/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/react-native-codegen/package.json b/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/react-native-codegen/package.json new file mode 100644 index 000000000..d0bdeb9b5 --- /dev/null +++ b/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/react-native-codegen/package.json @@ -0,0 +1,4 @@ +{ + "name": "react-native-codegen", + "version": "0.0.0-dev" +} diff --git a/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/react-native-macos/package.json b/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/react-native-macos/package.json new file mode 100644 index 000000000..9036896d1 --- /dev/null +++ b/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/react-native-macos/package.json @@ -0,0 +1,4 @@ +{ + "name": "react-native-macos", + "version": "0.0.0-dev" +} diff --git a/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/react-native-macos/react-native.config.js b/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/react-native-macos/react-native.config.js new file mode 100644 index 000000000..699df167d --- /dev/null +++ b/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/react-native-macos/react-native.config.js @@ -0,0 +1,11 @@ +module.exports = { + platforms: { + ios: {}, + android: {}, + macos: { + projectConfig: () => null, + dependencyConfig: () => null, + npmPackageName: "react-native-macos", + }, + }, +}; diff --git a/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/react-native-tvos/package.json b/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/react-native-tvos/package.json new file mode 100644 index 000000000..7f834aac3 --- /dev/null +++ b/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/react-native-tvos/package.json @@ -0,0 +1,4 @@ +{ + "name": "react-native-tvos", + "version": "0.0.0-dev" +} diff --git a/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/react-native-tvos/react-native.config.js b/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/react-native-tvos/react-native.config.js new file mode 100644 index 000000000..dc8f60852 --- /dev/null +++ b/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/react-native-tvos/react-native.config.js @@ -0,0 +1,6 @@ +module.exports = { + platforms: { + ios: {}, + android: {}, + }, +}; diff --git a/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/react-native-windows/package.json b/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/react-native-windows/package.json new file mode 100644 index 000000000..2569f3f04 --- /dev/null +++ b/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/react-native-windows/package.json @@ -0,0 +1,4 @@ +{ + "name": "react-native-windows", + "version": "0.0.0-dev" +} diff --git a/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/react-native-windows/react-native.config.js b/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/react-native-windows/react-native.config.js new file mode 100644 index 000000000..794a78f2e --- /dev/null +++ b/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/node_modules/react-native-windows/react-native.config.js @@ -0,0 +1,8 @@ +module.exports = { + platforms: { + windows: { + linkConfig: () => null, + npmPackageName: "react-native-windows", + }, + }, +}; diff --git a/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/package.json b/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/package.json new file mode 100644 index 000000000..fd40752e4 --- /dev/null +++ b/packages/typescript-react-native-resolver/test/__fixtures__/react-native-package-name-test/package.json @@ -0,0 +1,12 @@ +{ + "name": "@rnx-kit/tools-react-native/available-platform-tests", + "version": "0.0.0-dev", + "dependencies": { + "@office-iss/react-native-win32": "0.0.0-dev", + "react-native": "0.0.0-dev", + "react-native-codegen": "0.0.0-dev", + "react-native-macos": "0.0.0-dev", + "react-native-tvos": "0.0.0-dev", + "react-native-windows": "0.0.0-dev" + } +} diff --git a/packages/typescript-react-native-resolver/test/host.test.ts b/packages/typescript-react-native-resolver/test/host.test.ts index 60e9dd47c..a3724848c 100644 --- a/packages/typescript-react-native-resolver/test/host.test.ts +++ b/packages/typescript-react-native-resolver/test/host.test.ts @@ -19,17 +19,19 @@ import { } from "../src/extension"; describe("Host > changeHostToUseReactNativeResolver", () => { - const mockFileExists = jest.fn(); - const mockReadFile = jest.fn(); const mockDirectoryExists = jest.fn(); - const mockRealpath = jest.fn(); + const mockFileExists = jest.fn(); + const mockGetCurrentDirectory = jest.fn(); const mockGetDirectories = jest.fn(); + const mockReadFile = jest.fn(); + const mockRealpath = jest.fn(); const host = { - fileExists: mockFileExists, - readFile: mockReadFile, directoryExists: mockDirectoryExists, - realpath: mockRealpath, + fileExists: mockFileExists, + getCurrentDirectory: mockGetCurrentDirectory, getDirectories: mockGetDirectories, + readFile: mockReadFile, + realpath: mockRealpath, } as unknown as ts.CompilerHost; afterEach(() => { diff --git a/packages/typescript-react-native-resolver/test/react-native-package-name.test.ts b/packages/typescript-react-native-resolver/test/react-native-package-name.test.ts index 2db5d13fa..123e99cee 100644 --- a/packages/typescript-react-native-resolver/test/react-native-package-name.test.ts +++ b/packages/typescript-react-native-resolver/test/react-native-package-name.test.ts @@ -1,9 +1,17 @@ +import * as path from "path"; import { ResolverLog, ResolverLogMode } from "../src/log"; import { createReactNativePackageNameReplacer } from "../src/react-native-package-name"; +const fixture = path.join( + __dirname, + "__fixtures__", + "react-native-package-name-test" +); + describe("React-Native Package Name > createReactNativePackageNameReplacer > Disabled", () => { test("returns the input module without substitution", () => { const replacer = createReactNativePackageNameReplacer( + fixture, "windows", true, new ResolverLog(ResolverLogMode.Never) @@ -16,6 +24,7 @@ describe("React-Native Package Name > createReactNativePackageNameReplacer > Dis describe("React-Native Package Name > createReactNativePackageNameReplacer > In-tree Platform", () => { test("returns the input module without substitution", () => { const replacer = createReactNativePackageNameReplacer( + fixture, "ios", false, new ResolverLog(ResolverLogMode.Never) @@ -32,6 +41,7 @@ describe("React-Native Package Name > createReactNativePackageNameReplacer > Out }; const replacer = createReactNativePackageNameReplacer( + fixture, "windows", false, resolverLog as ResolverLog diff --git a/scripts/src/commands/updateApiReadme.js b/scripts/src/commands/updateApiReadme.js index e83bf8866..0531d2988 100644 --- a/scripts/src/commands/updateApiReadme.js +++ b/scripts/src/commands/updateApiReadme.js @@ -73,20 +73,46 @@ function findSourceFiles() { * @returns {string} */ function getExportedName(node) { - switch (node.declaration?.type) { + const declaration = node.declaration; + switch (declaration?.type) { case "FunctionDeclaration": case "TSInterfaceDeclaration": case "TSTypeAliasDeclaration": - if (!isIdentifier(node.declaration.id)) { + if (!isIdentifier(declaration.id)) { // TODO: Unnamed functions are currently unsupported return ""; } - return node.declaration.id.name; + return declaration.id.name; + + case "VariableDeclaration": { + if (isArrowFunction(node)) { + const decl = declaration.declarations[0]; + if (isIdentifier(decl.id)) { + return decl.id.name; + } + } + return ""; + } + default: return ""; } } +/** + * Returns whether the node represents a memoized function. + * @param {import("@babel/types").ExportNamedDeclaration} node + * @returns {boolean} + */ +function isArrowFunction(node) { + return Boolean( + node.declaration?.type === "VariableDeclaration" && + findLastBlockComment(node.leadingComments)?.value?.includes( + "@privateRemarks is-arrow-function" + ) + ); +} + /** * @param {import("@microsoft/tsdoc").DocNode} docNode * @returns {string} @@ -213,6 +239,14 @@ function updateApiReadme() { .map(renderParamNode) .join(", ")})\``; } + if (isArrowFunction(node)) { + const comment = findLastBlockComment(node.leadingComments); + return `\`${name}(${comment?.value + ?.split("@param ") + ?.map((part) => part.substring(0, part.indexOf(" "))) + ?.slice(1) + ?.join(", ")})\``; + } return name; })(); @@ -232,7 +266,10 @@ function updateApiReadme() { const summary = renderDocNode(result.docComment.summarySection); const description = extractBrief(summary); - if (isFunctionDeclaration(node.declaration)) { + if ( + isFunctionDeclaration(node.declaration) || + isArrowFunction(node) + ) { exportedFunctions.push([category, identifier, description]); } else { exportedTypes.push([category, identifier, description]);