UNPKG

@rnx-kit/tools-react-native

Version:

A collection of supplemental react-native functions and types

201 lines (178 loc) 5.65 kB
import { findPackageDependencyDir } from "@rnx-kit/tools-node/package"; import * as fs from "node:fs"; import * as path from "node:path"; import { readReactNativeConfig } from "./context"; /** * List of supported react-native platforms. */ export type AllPlatforms = | "android" | "ios" | "macos" | "win32" | "windows" | "visionos"; // Possible values for AllPlatforms const allValues: AllPlatforms[] = [ "android", "ios", "macos", "win32", "windows", "visionos", ]; /** * Returns a list of extensions that should be tried for the target platform in * prioritized order. * @param platform The platform to expand platform extensions for * @param extensions A list of extensions to expand * @returns A list of extensions */ export function expandPlatformExtensions( platform: string, extensions: readonly string[] ): string[] { const platforms = platformExtensions(platform); const expanded = platforms.reduce<string[]>((expanded, platform) => { expanded.push(...extensions.map((ext) => `.${platform}${ext}`)); return expanded; }, []); expanded.push(...extensions); return expanded; } /** * Get the module suffixes array for a given platform, suitable for use with TypeScript's moduleSuffixes setting * in the form of ['.ios', '.native', ''] or ['.windows', '.win', '.native', ''] or similar * * @param platform platform to get module suffixes for * @param appendEmpty finish the suffixes with an empty entry, required for typescript usage * @returns an array of suffixes to try to match a module to in order of priority */ export function getModuleSuffixes( platform: AllPlatforms, appendEmpty = true ): string[] { const extensions = platformExtensions(platform).map((ext) => `.${ext}`); if (appendEmpty) { extensions.push(""); } return extensions; } /** * 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<string, string> = { android: "", ios: "" } ): typeof platformMap { 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 { dependencies, peerDependencies, devDependencies } = JSON.parse( fs.readFileSync(packageJson, { encoding: "utf-8" }) ); const packages = new Set<string>( dependencies ? Object.keys(dependencies) : [] ); for (const deps of [peerDependencies, devDependencies]) { if (deps) { for (const pkg of Object.keys(deps)) { packages.add(pkg); } } } const recordPlatformPackage = (pkgPath: string | undefined) => { if (!pkgPath) { return; } const manifest = readReactNativeConfig(pkgPath, startDir); if (!manifest) { return; } const { platforms } = manifest; if (!platforms || typeof platforms !== "object") { return; } for (const [platform, info] of Object.entries(platforms)) { if (typeof platformMap[platform] === "undefined") { const { npmPackageName } = info; if (npmPackageName) { platformMap[platform] = npmPackageName; } } } }; recordPlatformPackage(startDir); const options = { startDir }; packages.forEach((pkgName) => { const pkgPath = findPackageDependencyDir(pkgName, options); if (pkgPath) { recordPlatformPackage(pkgPath); } }); return platformMap; } /** * Returns a map of available React Native platforms. The result is cached. * @param startDir The directory to look for react-native platforms from * @returns A platform-to-npm-package map, excluding "core" platforms. */ export const getAvailablePlatforms = (() => { const isTesting = Boolean(process.env.NODE_TEST_CONTEXT) || process.env.NODE_ENV === "test"; let platformMap: Record<string, string> | undefined = undefined; return (startDir = process.cwd()) => { if (!platformMap || isTesting) { platformMap = getAvailablePlatformsUncached(startDir); } return platformMap; }; })(); /** * Returns file extensions that can be mapped to the target platform. * @param platform The platform to retrieve extensions for * @returns Valid extensions for specified platform */ export function platformExtensions(platform: string): string[] { switch (platform) { case "win32": case "windows": return [platform, "win", "native"]; default: return [platform, "native"]; } } /** * @returns the given string as a platform value or undefined if it is not a valid platform. */ export function tryParsePlatform(val: string): AllPlatforms | undefined { return allValues.includes(val as AllPlatforms) ? (val as AllPlatforms) : undefined; } /** * Parse a string to ensure it maps to a valid react-native platform. * * @param val Input string * @returns React-native platform name. Throws `Error` on failure. */ export function parsePlatform(val: string): AllPlatforms { const platform = tryParsePlatform(val); if (!platform) { throw new Error(`Unknown platform '${val}'`); } return platform; } /** * @returns List of all supported react-native platforms. */ export function platformValues(): readonly AllPlatforms[] { return allValues; }