UNPKG

@rnx-kit/tools-react-native

Version:

A collection of supplemental react-native functions and types

160 lines (137 loc) 4.61 kB
import type { loadConfig } from "@react-native-community/cli"; import type { Config } from "@react-native-community/cli-types"; import { findPackageDependencyDir, readPackage, } from "@rnx-kit/tools-node/package"; import { spawnSync } from "node:child_process"; import * as fs from "node:fs"; import * as path from "node:path"; import { getCurrentState, getSavedState, loadConfigFromCache, saveConfigToCache, } from "./cache"; // As of 0.76, `@react-native-community/cli` is no longer a dependency of // `react-native`. Consumers have to take a direct dependency on CLI instead. const RN_CLI_DECOUPLED = 76; export const REACT_NATIVE_CONFIG_FILES = [ "react-native.config.ts", "react-native.config.mjs", "react-native.config.cjs", "react-native.config.js", ]; function toNumber(version: string): number { const [major, minor = 0] = version.split("."); return Number(major) * 1000 + Number(minor); } function findStartDir(root: string, reactNativePath = ""): string { const reactNative = reactNativePath || findPackageDependencyDir("react-native", { startDir: root, resolveSymlinks: true, }); if (!reactNative) { return root; } const { version } = readPackage(reactNative); return toNumber(version) < RN_CLI_DECOUPLED ? reactNative : root; } function getConfigOrState(projectRoot: string): Config | string { const state = getCurrentState(projectRoot); if (state === getSavedState(projectRoot)) { const config = loadConfigFromCache(projectRoot); if (config) { return config; } } return state; } function makeLoadConfigOptions(fn: typeof loadConfig, projectRoot: string) { return fn.length === 1 ? { projectRoot } : (projectRoot as unknown as { projectRoot: string }); } /** * Finds path to `@react-native-community/cli`. * @param root Project root * @param reactNativePath Path to `react-native`, if known */ export function resolveCommunityCLI( root: string, reactNativePath = "" ): string { const startDir = findStartDir(root, reactNativePath); return require.resolve("@react-native-community/cli", { paths: [startDir] }); } /** * Equivalent to calling `loadConfig()` from `@react-native-community/cli`, but * the result is cached for faster subsequent accesses. * @param projectRoot Project root; defaults to current working directory */ export function loadContext(projectRoot = process.cwd()): Config { const state = getConfigOrState(projectRoot); if (typeof state !== "string") { return state; } const rncli = resolveCommunityCLI(projectRoot); const { loadConfig } = require(rncli); const options = makeLoadConfigOptions(loadConfig, projectRoot); const config = loadConfig(options); saveConfigToCache(projectRoot, state, config); return config; } /** * Equivalent to calling `loadConfigAsync()` (with fallback to `loadConfig()`) * from `@react-native-community/cli`, but the result is cached for faster * subsequent accesses. * @param projectRoot Project root; defaults to current working directory */ export async function loadContextAsync( projectRoot = process.cwd() ): Promise<Config> { const state = getConfigOrState(projectRoot); if (typeof state !== "string") { return state; } const rncli = resolveCommunityCLI(projectRoot); const { loadConfig, loadConfigAsync } = require(rncli); const options = makeLoadConfigOptions(loadConfig, projectRoot); if (!loadConfigAsync) { const config = loadConfig(options); saveConfigToCache(projectRoot, state, config); return config; } const config = await loadConfigAsync(options); saveConfigToCache(projectRoot, state, config); return config; } export function readReactNativeConfig( packageDir: string, cwd = process.cwd() ): Record<string, unknown> | undefined { for (const configFile of REACT_NATIVE_CONFIG_FILES) { const configPath = path.join(packageDir, configFile); if (fs.existsSync(configPath)) { const url = process.platform === "win32" ? `file://${configPath.replaceAll("\\", "/")}` : configPath; const args = [ "--no-warnings", "--eval", `import("${url}").then((config) => console.log(JSON.stringify(config.default ?? config)));`, ]; const { stderr, stdout } = spawnSync(process.argv0, args, { cwd }); const errors = stderr?.toString().trim(); if (errors) { console.error(`${configPath}: ${errors}`); } const json = stdout?.toString().trim(); return json ? JSON.parse(json) : undefined; } } return undefined; }