Skip to content

Commit e81b707

Browse files
committed
fix: support loading ESM config files
1 parent 0eaf227 commit e81b707

File tree

3 files changed

+47
-43
lines changed

3 files changed

+47
-43
lines changed

test/windows/generateSolution.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,22 @@ describe("generateSolution()", () => {
2727
process.chdir(cwd);
2828
});
2929

30-
it("exits if destination path is missing/invalid", () => {
31-
equal(generateSolution("", options), "Missing or invalid destination path");
30+
it("exits if destination path is missing/invalid", async () => {
31+
equal(await generateSolution("", options), "Missing or invalid destination path");
3232
});
3333

34-
it("exits if 'package.json' folder cannot be found", () => {
35-
equal(generateSolution("test", options), "Could not find 'package.json'");
34+
it("exits if 'package.json' folder cannot be found", async () => {
35+
equal(await generateSolution("test", options), "Could not find 'package.json'");
3636
});
3737

38-
it("exits if 'react-native-windows' folder cannot be found", () => {
38+
it("exits if 'react-native-windows' folder cannot be found", async () => {
3939
setMockFiles({
4040
[path.resolve("", "package.json")]: testManifest,
4141
[path.resolve("", "node_modules", ".bin")]: "directory",
4242
});
4343

4444
equal(
45-
generateSolution("test", options),
45+
await generateSolution("test", options),
4646
"Could not find 'react-native-windows'"
4747
);
4848
});

windows/project.mjs

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// @ts-check
2+
import { resolveCommunityCLI } from "@rnx-kit/tools-react-native/context";
23
import { XMLParser } from "fast-xml-parser";
34
import * as nodefs from "node:fs";
45
import * as path from "node:path";
@@ -10,19 +11,21 @@ import {
1011
memo,
1112
readJSONFile,
1213
readTextFile,
13-
requireTransitive,
1414
toVersionNumber,
1515
v,
1616
} from "../scripts/helpers.js";
1717
import * as colors from "../scripts/utils/colors.mjs";
1818

1919
/**
20-
* @typedef {import("../scripts/types.js").AppManifest} AppManifest
21-
* @typedef {import("../scripts/types.js").AppxBundle} AppxBundle
22-
* @typedef {import("../scripts/types.js").AssetItems} AssetItems;
23-
* @typedef {import("../scripts/types.js").Assets} Assets;
24-
* @typedef {import("../scripts/types.js").MSBuildProjectOptions} MSBuildProjectOptions;
25-
* @typedef {import("../scripts/types.js").ProjectInfo} ProjectInfo;
20+
* @import { Config } from "@react-native-community/cli-types"
21+
* @import {
22+
* AppManifest,
23+
* AppxBundle,
24+
* AssetItems,
25+
* Assets,
26+
* MSBuildProjectOptions,
27+
* ProjectInfo,
28+
* } from "../scripts/types.js"
2629
*/
2730

2831
const uniqueFilterIdentifier = "e48dc53e-40b1-40cb-970a-f89935452892";
@@ -93,18 +96,22 @@ function generateCertificateItems(
9396

9497
/**
9598
* Equivalent to invoking `react-native config`.
96-
* @param {string} rnWindowsPath
99+
* @type {(projectRoot: string) => Promise<Config>}
97100
*/
98-
export const loadReactNativeConfig = memo((rnWindowsPath) => {
99-
/** @type {import("@react-native-community/cli")} */
100-
const { loadConfig } = requireTransitive(
101-
["@react-native-community/cli"],
102-
rnWindowsPath
103-
);
104-
// The signature of `loadConfig` changed in 14.0.0:
105-
// https://github.com/react-native-community/cli/commit/b787c89edb781bb788576cd615d2974fc81402fc
106-
// @ts-expect-error TS2345: Argument of type X is not assignable to parameter of type Y
107-
return loadConfig.length === 1 ? loadConfig({}) : loadConfig();
101+
export const loadReactNativeConfig = memo((projectRoot) => {
102+
const rncli = resolveCommunityCLI(projectRoot);
103+
return import(rncli).then(async (cli) => {
104+
const { loadConfig, loadConfigAsync } = cli?.default ?? cli;
105+
if (!loadConfigAsync) {
106+
// The signature of `loadConfig` changed in 14.0.0:
107+
// https://github.com/react-native-community/cli/commit/b787c89edb781bb788576cd615d2974fc81402fc
108+
return loadConfig.length === 1
109+
? loadConfig({ projectRoot })
110+
: loadConfig();
111+
}
112+
113+
return await loadConfigAsync({ projectRoot });
114+
});
108115
});
109116

110117
/**
@@ -204,18 +211,17 @@ function generateContentItems(
204211
* is a workaround until `react-native-windows` autolinking adds support.
205212
*
206213
* @see {@link https://github.com/microsoft/react-native-windows/issues/9578}
207-
* @param {string} rnWindowsPath
208-
* @returns {[string, string][]}
214+
* @param {string} projectRoot
215+
* @returns {Promise<[string, string][]>}
209216
*/
210-
function getNuGetDependencies(rnWindowsPath, fs = nodefs) {
217+
async function getNuGetDependencies(projectRoot, fs = nodefs) {
211218
const pkgJson = findNearest("package.json", undefined, fs);
212219
if (!pkgJson) {
213220
return [];
214221
}
215222

216-
const dependencies = Object.values(
217-
loadReactNativeConfig(rnWindowsPath).dependencies
218-
);
223+
const config = await loadReactNativeConfig(projectRoot);
224+
const dependencies = Object.values(config.dependencies);
219225

220226
const xml = new XMLParser({
221227
ignoreAttributes: false,
@@ -400,9 +406,9 @@ export function getBundleResources(manifestFilePath, fs = nodefs) {
400406
* @param {MSBuildProjectOptions} options
401407
* @param {string} rnWindowsPath
402408
* @param {string} destPath
403-
* @returns {ProjectInfo}
409+
* @returns {Promise<ProjectInfo>}
404410
*/
405-
export function projectInfo(
411+
export async function projectInfo(
406412
{ useFabric, useNuGet },
407413
rnWindowsPath,
408414
destPath,
@@ -421,7 +427,7 @@ export function projectInfo(
421427
version,
422428
versionNumber,
423429
bundle: getBundleResources(findNearest("app.json", destPath, fs), fs),
424-
nugetDependencies: getNuGetDependencies(rnWindowsPath),
430+
nugetDependencies: await getNuGetDependencies(destPath),
425431
useExperimentalNuGet: newArch || useNuGet,
426432
useFabric: newArch,
427433
};

windows/test-app.mjs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ import { loadReactNativeConfig, projectInfo } from "./project.mjs";
1818
import { configureForUWP } from "./uwp.mjs";
1919
import { configureForWin32 } from "./win32.mjs";
2020

21-
/**
22-
* @typedef {import("../scripts/types.js").MSBuildProjectOptions} MSBuildProjectOptions;
23-
*/
21+
/** @import { MSBuildProjectOptions } from "../scripts/types.js" */
2422

2523
const templateView = {
2624
packageGuidUpper: "{B44CEAD7-FBFF-4A17-95EB-FF5434BBD79D}", // .wapproj
@@ -120,9 +118,9 @@ export async function copyAndReplace(
120118
* Generates Visual Studio solution.
121119
* @param {string} destPath Destination path.
122120
* @param {MSBuildProjectOptions} options
123-
* @returns {string | undefined} An error message; `undefined` otherwise.
121+
* @returns {Promise<string | undefined>} An error message; `undefined` otherwise.
124122
*/
125-
export function generateSolution(destPath, options, fs = nodefs) {
123+
export async function generateSolution(destPath, options, fs = nodefs) {
126124
if (!destPath) {
127125
return "Missing or invalid destination path";
128126
}
@@ -143,7 +141,7 @@ export function generateSolution(destPath, options, fs = nodefs) {
143141
return "Could not find 'react-native-windows'";
144142
}
145143

146-
const info = projectInfo(options, rnWindowsPath, destPath, fs);
144+
const info = await projectInfo(options, rnWindowsPath, destPath, fs);
147145
const { projDir, projectFileName, projectFiles, solutionTemplatePath } =
148146
info.useFabric ? configureForWin32(info) : configureForUWP(info);
149147

@@ -278,10 +276,10 @@ export function generateSolution(destPath, options, fs = nodefs) {
278276
if (options.autolink) {
279277
const projectRoot = path.resolve(path.dirname(projectManifest));
280278
Promise.all(copyTasks)
281-
.then(() => {
279+
.then(async () => {
282280
// `react-native config` is cached by `@react-native-community/cli`. We
283281
// need to manually regenerate the Windows project config and inject it.
284-
const config = loadReactNativeConfig(rnWindowsPath);
282+
const config = await loadReactNativeConfig(projectRoot);
285283
config.project.windows = config.platforms.windows.projectConfig(
286284
projectRoot,
287285
{
@@ -336,15 +334,15 @@ if (isMain(import.meta.url)) {
336334
default: false,
337335
},
338336
},
339-
({
337+
async ({
340338
"project-directory": projectDirectory,
341339
autolink,
342340
"use-fabric": useFabric,
343341
"use-hermes": useHermes,
344342
"use-nuget": useNuGet,
345343
}) => {
346344
const options = { autolink, useFabric, useHermes, useNuGet };
347-
const error = generateSolution(path.resolve(projectDirectory), options);
345+
const error = await generateSolution(path.resolve(projectDirectory), options);
348346
if (error) {
349347
console.error(error);
350348
process.exitCode = 1;

0 commit comments

Comments
 (0)