Skip to content

Commit 99e2c04

Browse files
authored
fix(windows): support loading ESM config files (#2342)
1 parent d1f2290 commit 99e2c04

File tree

3 files changed

+41
-32
lines changed

3 files changed

+41
-32
lines changed

test/windows/generateSolution.test.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,28 @@ 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(
32+
await generateSolution("", options),
33+
"Missing or invalid destination path"
34+
);
3235
});
3336

34-
it("exits if 'package.json' folder cannot be found", () => {
35-
equal(generateSolution("test", options), "Could not find 'package.json'");
37+
it("exits if 'package.json' folder cannot be found", async () => {
38+
equal(
39+
await generateSolution("test", options),
40+
"Could not find 'package.json'"
41+
);
3642
});
3743

38-
it("exits if 'react-native-windows' folder cannot be found", () => {
44+
it("exits if 'react-native-windows' folder cannot be found", async () => {
3945
setMockFiles({
4046
[path.resolve("", "package.json")]: testManifest,
4147
[path.resolve("", "node_modules", ".bin")]: "directory",
4248
});
4349

4450
equal(
45-
generateSolution("test", options),
51+
await generateSolution("test", options),
4652
"Could not find 'react-native-windows'"
4753
);
4854
});

windows/project.mjs

Lines changed: 21 additions & 19 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,13 +11,13 @@ 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+
* @import { Config } from "@react-native-community/cli-types"
2021
* @import {
2122
* AppManifest,
2223
* AppxBundle,
@@ -95,18 +96,20 @@ function generateCertificateItems(
9596

9697
/**
9798
* Equivalent to invoking `react-native config`.
98-
* @param {string} rnWindowsPath
99+
* @type {(rnWindowsPath: string) => Promise<Config>}
99100
*/
100101
export const loadReactNativeConfig = memo((rnWindowsPath) => {
101-
/** @type {import("@react-native-community/cli")} */
102-
const { loadConfig } = requireTransitive(
103-
["@react-native-community/cli"],
104-
rnWindowsPath
105-
);
106-
// The signature of `loadConfig` changed in 14.0.0:
107-
// https://github.com/react-native-community/cli/commit/b787c89edb781bb788576cd615d2974fc81402fc
108-
// @ts-expect-error TS2345: Argument of type X is not assignable to parameter of type Y
109-
return loadConfig.length === 1 ? loadConfig({}) : loadConfig();
102+
const rncli = "file://" + resolveCommunityCLI(rnWindowsPath);
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 ? loadConfig({}) : loadConfig();
109+
}
110+
111+
return await loadConfigAsync({});
112+
});
110113
});
111114

112115
/**
@@ -207,17 +210,16 @@ function generateContentItems(
207210
*
208211
* @see {@link https://github.com/microsoft/react-native-windows/issues/9578}
209212
* @param {string} rnWindowsPath
210-
* @returns {[string, string][]}
213+
* @returns {Promise<[string, string][]>}
211214
*/
212-
function getNuGetDependencies(rnWindowsPath, fs = nodefs) {
215+
async function getNuGetDependencies(rnWindowsPath, fs = nodefs) {
213216
const pkgJson = findNearest("package.json", undefined, fs);
214217
if (!pkgJson) {
215218
return [];
216219
}
217220

218-
const dependencies = Object.values(
219-
loadReactNativeConfig(rnWindowsPath).dependencies
220-
);
221+
const config = await loadReactNativeConfig(rnWindowsPath);
222+
const dependencies = Object.values(config.dependencies);
221223

222224
const xml = new XMLParser({
223225
ignoreAttributes: false,
@@ -402,9 +404,9 @@ export function getBundleResources(manifestFilePath, fs = nodefs) {
402404
* @param {MSBuildProjectOptions} options
403405
* @param {string} rnWindowsPath
404406
* @param {string} destPath
405-
* @returns {ProjectInfo}
407+
* @returns {Promise<ProjectInfo>}
406408
*/
407-
export function projectInfo(
409+
export async function projectInfo(
408410
{ useFabric, useNuGet },
409411
rnWindowsPath,
410412
destPath,
@@ -423,7 +425,7 @@ export function projectInfo(
423425
version,
424426
versionNumber,
425427
bundle: getBundleResources(findNearest("app.json", destPath, fs), fs),
426-
nugetDependencies: getNuGetDependencies(rnWindowsPath),
428+
nugetDependencies: await getNuGetDependencies(rnWindowsPath),
427429
useExperimentalNuGet: newArch || useNuGet,
428430
useFabric: newArch,
429431
};

windows/test-app.mjs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,9 @@ export async function copyAndReplace(
118118
* Generates Visual Studio solution.
119119
* @param {string} destPath Destination path.
120120
* @param {MSBuildProjectOptions} options
121-
* @returns {string | undefined} An error message; `undefined` otherwise.
121+
* @returns {Promise<string | undefined>} An error message; `undefined` otherwise.
122122
*/
123-
export function generateSolution(destPath, options, fs = nodefs) {
123+
export async function generateSolution(destPath, options, fs = nodefs) {
124124
if (!destPath) {
125125
return "Missing or invalid destination path";
126126
}
@@ -141,7 +141,7 @@ export function generateSolution(destPath, options, fs = nodefs) {
141141
return "Could not find 'react-native-windows'";
142142
}
143143

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

@@ -276,10 +276,10 @@ export function generateSolution(destPath, options, fs = nodefs) {
276276
if (options.autolink) {
277277
const projectRoot = path.resolve(path.dirname(projectManifest));
278278
Promise.all(copyTasks)
279-
.then(() => {
279+
.then(async () => {
280280
// `react-native config` is cached by `@react-native-community/cli`. We
281281
// need to manually regenerate the Windows project config and inject it.
282-
const config = loadReactNativeConfig(rnWindowsPath);
282+
const config = await loadReactNativeConfig(rnWindowsPath);
283283
config.project.windows = config.platforms.windows.projectConfig(
284284
projectRoot,
285285
{
@@ -334,15 +334,16 @@ if (isMain(import.meta.url)) {
334334
default: false,
335335
},
336336
},
337-
({
337+
async ({
338338
"project-directory": projectDirectory,
339339
autolink,
340340
"use-fabric": useFabric,
341341
"use-hermes": useHermes,
342342
"use-nuget": useNuGet,
343343
}) => {
344344
const options = { autolink, useFabric, useHermes, useNuGet };
345-
const error = generateSolution(path.resolve(projectDirectory), options);
345+
const destPath = path.resolve(projectDirectory);
346+
const error = await generateSolution(destPath, options);
346347
if (error) {
347348
console.error(error);
348349
process.exitCode = 1;

0 commit comments

Comments
 (0)