Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(tools-react-native): support loading ESM config files #3422

Merged
merged 1 commit into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fifty-ads-drive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rnx-kit/tools-react-native": patch
---

Add support for loading ESM config files
25 changes: 13 additions & 12 deletions packages/tools-react-native/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,18 @@ import * as platformTools from "@rnx-kit/tools-react-native/platform";
| -------- | ------------ | ----------------------------------------- |
| platform | AllPlatforms | List of supported react-native platforms. |

| Category | Function | Description |
| -------- | ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- |
| context | `loadContext(root)` | Equivalent to calling `loadConfig()` from `@react-native-community/cli`, but the result is cached for faster subsequent accesses. |
| context | `resolveCommunityCLI(root, reactNativePath)` | Finds path to `@react-native-community/cli`. |
| metro | `findMetroPath(projectRoot)` | Finds the installation path of Metro. |
| metro | `getMetroVersion(projectRoot)` | Returns Metro version number. |
| metro | `requireModuleFromMetro(moduleName, fromDir)` | Imports specified module starting from the installation directory of the currently used `metro` version. |
| 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. |
| Category | Function | Description |
| -------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| context | `loadContext(projectRoot)` | Equivalent to calling `loadConfig()` from `@react-native-community/cli`, but the result is cached for faster subsequent accesses. |
| context | `loadContextAsync(projectRoot)` | Equivalent to calling `loadConfigAsync()` (with fallback to `loadConfig()`) from `@react-native-community/cli`, but the result is cached for faster subsequent accesses. |
| context | `resolveCommunityCLI(root, reactNativePath)` | Finds path to `@react-native-community/cli`. |
| metro | `findMetroPath(projectRoot)` | Finds the installation path of Metro. |
| metro | `getMetroVersion(projectRoot)` | Returns Metro version number. |
| metro | `requireModuleFromMetro(moduleName, fromDir)` | Imports specified module starting from the installation directory of the currently used `metro` version. |
| 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. |

<!-- @rnx-kit/api end -->
70 changes: 56 additions & 14 deletions packages/tools-react-native/src/context.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { loadConfig } from "@react-native-community/cli";
import type { Config } from "@react-native-community/cli-types";
import {
findPackageDependencyDir,
Expand Down Expand Up @@ -34,6 +35,24 @@ function findStartDir(root: string, reactNativePath = ""): string {
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
Expand All @@ -50,26 +69,49 @@ export function resolveCommunityCLI(
/**
* Equivalent to calling `loadConfig()` from `@react-native-community/cli`, but
* the result is cached for faster subsequent accesses.
* @param root Project root; defaults to current working directory
* @param projectRoot Project root; defaults to current working directory
*/
export function loadContext(root = process.cwd()): Config {
const state = getCurrentState(root);
if (state === getSavedState(root)) {
const config = loadConfigFromCache(root);
if (config) {
return config;
}
export function loadContext(projectRoot = process.cwd()): Config {
const state = getConfigOrState(projectRoot);
if (typeof state !== "string") {
return state;
}

const rncli = resolveCommunityCLI(root);
const rncli = resolveCommunityCLI(projectRoot);
const { loadConfig } = require(rncli);

const config: Config =
loadConfig.length === 1
? loadConfig({ projectRoot: root })
: loadConfig(root);
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);

saveConfigToCache(root, state, config);
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;
}
2 changes: 1 addition & 1 deletion packages/tools-react-native/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { loadContext, resolveCommunityCLI } from "./context";
export { loadContext, loadContextAsync, resolveCommunityCLI } from "./context";
export {
findMetroPath,
getMetroVersion,
Expand Down
Loading