Skip to content

Commit

Permalink
fix(tools-react-native): support loading ESM config files (#3422)
Browse files Browse the repository at this point in the history
  • Loading branch information
tido64 authored Nov 19, 2024
1 parent ac1cfa3 commit 1ae018a
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 27 deletions.
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

0 comments on commit 1ae018a

Please sign in to comment.