Skip to content

Commit aa634df

Browse files
authored
fix: fix configure-test-app crashing on 0.75 (#2190)
1 parent d92bd16 commit aa634df

File tree

9 files changed

+161
-118
lines changed

9 files changed

+161
-118
lines changed

eslint.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ module.exports = [
4545
{
4646
patterns: [
4747
{
48-
group: ["[a-z]*", "!./*", "!node:*"],
48+
group: ["[a-z]*", "!./*", "!./utils/*", "!node:*"],
4949
message:
5050
"External dependencies are not allowed in this file because it needs to be runnable before install.",
5151
},

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"scripts/parseargs.mjs",
5454
"scripts/schema.mjs",
5555
"scripts/template.mjs",
56+
"scripts/utils/npm.mjs",
5657
"test-app.gradle",
5758
"test_app.rb",
5859
"visionos",

scripts/configure.mjs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
serialize,
2929
settingsGradle,
3030
} from "./template.mjs";
31+
import { downloadPackage } from "./utils/npm.mjs";
3132

3233
/**
3334
* @typedef {import("./types.js").Configuration} Configuration
@@ -64,6 +65,25 @@ export function error(message) {
6465
console.error(colors.red(`[!] ${message}`));
6566
}
6667

68+
/**
69+
* @param {string} targetVersion
70+
* @returns {Promise<string | undefined>}
71+
*/
72+
async function findTemplateDir(targetVersion) {
73+
if (toVersionNumber(targetVersion) < v(0, 75, 0)) {
74+
// Let `getConfig` try to find the template inside `react-native`
75+
return undefined;
76+
}
77+
78+
const [major, minor = 0] = targetVersion.split(".");
79+
const output = await downloadPackage(
80+
"@react-native-community/template",
81+
`${major}.${minor}`,
82+
true
83+
);
84+
return path.join(output, "template");
85+
}
86+
6787
/**
6888
* Merges specified configurations.
6989
* @param {Configuration} lhs
@@ -731,19 +751,21 @@ if (isMain(import.meta.url)) {
731751
default: platformChoices,
732752
},
733753
},
734-
({
754+
async ({
735755
_: { [0]: name },
736756
flatten,
737757
force,
738758
init,
739759
package: packagePath,
740760
platforms,
741761
}) => {
762+
const targetVersion = getPackageVersion("react-native");
742763
process.exitCode = configure({
743764
name: typeof name === "string" && name ? name : getAppName(packagePath),
744765
packagePath,
766+
templatePath: await findTemplateDir(targetVersion),
745767
testAppPath: fileURLToPath(new URL("..", import.meta.url)),
746-
targetVersion: getPackageVersion("react-native"),
768+
targetVersion,
747769
platforms: validatePlatforms(platforms),
748770
flatten,
749771
force,

scripts/helpers.js

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,6 @@ const nodefs = require("node:fs");
55
const path = require("node:path");
66
const { fileURLToPath } = require("node:url");
77

8-
const npmRegistryBaseURL = "https://registry.npmjs.org/";
9-
10-
/**
11-
* Fetches package metadata from the npm registry.
12-
* @param {string} pkg
13-
* @param {string=} distTag
14-
*/
15-
function fetchPackageMetadata(pkg, distTag) {
16-
const init = {
17-
headers: {
18-
Accept:
19-
"application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*",
20-
},
21-
};
22-
const url = distTag
23-
? npmRegistryBaseURL + pkg + "/" + distTag
24-
: npmRegistryBaseURL + pkg;
25-
return fetch(url, init).then((res) => res.json());
26-
}
27-
288
/**
299
* Finds the specified file using Node module resolution.
3010
* @param {string} file
@@ -171,13 +151,11 @@ function getPackageVersion(module, startDir = process.cwd(), fs = nodefs) {
171151
return version;
172152
}
173153

174-
exports.fetchPackageMetadata = fetchPackageMetadata;
175154
exports.findFile = findFile;
176155
exports.findNearest = findNearest;
177156
exports.getPackageVersion = getPackageVersion;
178157
exports.isMain = isMain;
179158
exports.memo = memo;
180-
exports.npmRegistryBaseURL = npmRegistryBaseURL;
181159
exports.readJSONFile = readJSONFile;
182160
exports.readTextFile = readTextFile;
183161
exports.requireTransitive = requireTransitive;

scripts/init.mjs

Lines changed: 2 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,14 @@
11
#!/usr/bin/env node
22
// @ts-check
3-
import { spawnSync } from "node:child_process";
4-
import * as fs from "node:fs";
5-
import * as https from "node:https";
63
import { createRequire } from "node:module";
7-
import * as os from "node:os";
84
import * as path from "node:path";
95
import { fileURLToPath } from "node:url";
106
import prompts from "prompts";
117
import * as colors from "./colors.mjs";
128
import { configure, getDefaultPlatformPackageName } from "./configure.mjs";
13-
import {
14-
fetchPackageMetadata,
15-
memo,
16-
readJSONFile,
17-
toVersionNumber,
18-
v,
19-
} from "./helpers.js";
9+
import { memo, readJSONFile, toVersionNumber, v } from "./helpers.js";
2010
import { parseArgs } from "./parseargs.mjs";
21-
22-
/**
23-
* Invokes `tar xf`.
24-
* @param {string} archive
25-
*/
26-
function untar(archive) {
27-
const args = ["xf", archive];
28-
const options = { cwd: path.dirname(archive) };
29-
const result = spawnSync("tar", args, options);
30-
31-
// If we run `tar` from Git Bash with a Windows path, it will fail with:
32-
//
33-
// tar: Cannot connect to C: resolve failed
34-
//
35-
// GNU Tar assumes archives with a colon in the file name are on another
36-
// machine. See also
37-
// https://www.gnu.org/software/tar/manual/html_section/file.html.
38-
if (
39-
process.platform === "win32" &&
40-
result.stderr.toString().includes("tar: Cannot connect to")
41-
) {
42-
args.push("--force-local");
43-
return spawnSync("tar", args, options);
44-
}
45-
46-
return result;
47-
}
48-
49-
/**
50-
* Fetches the tarball URL for the specified package and version.
51-
* @param {string} pkg
52-
* @param {string} version
53-
* @returns {Promise<string>}
54-
*/
55-
async function fetchPackageTarballURL(pkg, version) {
56-
const info = await fetchPackageMetadata(pkg);
57-
const specific = info.versions[version];
58-
if (specific) {
59-
return specific.dist.tarball;
60-
}
61-
62-
const versions = Object.keys(info.versions);
63-
for (let i = versions.length - 1; i >= 0; --i) {
64-
const v = versions[i];
65-
if (v.startsWith(version)) {
66-
return info.versions[v].dist.tarball;
67-
}
68-
}
69-
70-
throw new Error(`No match found for '${pkg}@${version}'`);
71-
}
11+
import { downloadPackage, fetchPackageMetadata } from "./utils/npm.mjs";
7212

7313
/**
7414
* Returns the installed `react-native` manifest, if present.
@@ -165,35 +105,6 @@ async function getVersion(platforms) {
165105
return target;
166106
}
167107

168-
/**
169-
* Downloads the specified npm package.
170-
* @param {string} pkg
171-
* @param {string} version
172-
* @returns {Promise<string>}
173-
*/
174-
async function downloadPackage(pkg, version) {
175-
const url = await fetchPackageTarballURL(pkg, version);
176-
console.log(`Downloading ${path.basename(url)}...`);
177-
178-
return new Promise((resolve, reject) => {
179-
https
180-
.get(url, (res) => {
181-
const tmpDir = path.join(os.tmpdir(), "react-native-test-app");
182-
fs.mkdirSync(tmpDir, { recursive: true });
183-
184-
const dest = path.join(tmpDir, path.basename(url));
185-
const fh = fs.createWriteStream(dest);
186-
res.pipe(fh);
187-
fh.on("finish", () => {
188-
fh.close();
189-
untar(dest);
190-
resolve(path.join(tmpDir, "package"));
191-
});
192-
})
193-
.on("error", (err) => reject(err));
194-
});
195-
}
196-
197108
/**
198109
* Returns the React Native version and path to the template.
199110
* @param {import("./types.js").Platform[]} platforms

scripts/set-react-version.mjs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,13 @@ import { promises as fs } from "node:fs";
88
import * as os from "node:os";
99
import * as path from "node:path";
1010
import {
11-
fetchPackageMetadata,
1211
isMain,
13-
npmRegistryBaseURL,
1412
readJSONFile,
1513
readTextFile,
1614
toVersionNumber,
1715
v,
1816
} from "./helpers.js";
17+
import { fetchPackageMetadata, npmRegistryBaseURL } from "./utils/npm.mjs";
1918

2019
/**
2120
* @typedef {import("./types.js").Manifest} Manifest

scripts/test-matrix.mjs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,23 @@ if (platforms.length === 0) {
325325
"ios"
326326
);
327327
})
328+
.then(() => {
329+
showBanner(`Reconfigure existing app`);
330+
$(
331+
PACKAGE_MANAGER,
332+
"configure-test-app",
333+
"-p",
334+
"android",
335+
"-p",
336+
"ios",
337+
"-p",
338+
"macos",
339+
"-p",
340+
"visionos",
341+
"-p",
342+
"windows"
343+
);
344+
})
328345
.then(() => {
329346
showBanner(green("✔ Pass"));
330347
});

scripts/utils/npm.mjs

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// @ts-check
2+
import { spawnSync } from "node:child_process";
3+
import * as fs from "node:fs";
4+
import * as https from "node:https";
5+
import * as os from "node:os";
6+
import * as path from "node:path";
7+
8+
export const npmRegistryBaseURL = "https://registry.npmjs.org/";
9+
10+
/**
11+
* Invokes `tar xf`.
12+
* @param {string} archive
13+
*/
14+
function untar(archive) {
15+
const args = ["xf", archive];
16+
const options = { cwd: path.dirname(archive) };
17+
const result = spawnSync("tar", args, options);
18+
19+
// If we run `tar` from Git Bash with a Windows path, it will fail with:
20+
//
21+
// tar: Cannot connect to C: resolve failed
22+
//
23+
// GNU Tar assumes archives with a colon in the file name are on another
24+
// machine. See also
25+
// https://www.gnu.org/software/tar/manual/html_section/file.html.
26+
if (
27+
process.platform === "win32" &&
28+
result.stderr.toString().includes("tar: Cannot connect to")
29+
) {
30+
args.push("--force-local");
31+
return spawnSync("tar", args, options);
32+
}
33+
34+
return result;
35+
}
36+
37+
/**
38+
* Fetches package metadata from the npm registry.
39+
* @param {string} pkg
40+
* @param {string=} distTag
41+
*/
42+
export function fetchPackageMetadata(pkg, distTag) {
43+
const init = {
44+
headers: {
45+
Accept:
46+
"application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*",
47+
},
48+
};
49+
const url = distTag
50+
? npmRegistryBaseURL + pkg + "/" + distTag
51+
: npmRegistryBaseURL + pkg;
52+
return fetch(url, init).then((res) => res.json());
53+
}
54+
55+
/**
56+
* Fetches the tarball URL for the specified package and version.
57+
* @param {string} pkg
58+
* @param {string} version
59+
* @returns {Promise<string>}
60+
*/
61+
async function fetchPackageTarballURL(pkg, version) {
62+
const info = await fetchPackageMetadata(pkg);
63+
const specific = info.versions[version];
64+
if (specific) {
65+
return specific.dist.tarball;
66+
}
67+
68+
const versions = Object.keys(info.versions);
69+
for (let i = versions.length - 1; i >= 0; --i) {
70+
const v = versions[i];
71+
if (v.startsWith(version)) {
72+
return info.versions[v].dist.tarball;
73+
}
74+
}
75+
76+
throw new Error(`No match found for '${pkg}@${version}'`);
77+
}
78+
79+
/**
80+
* Downloads the specified npm package.
81+
* @param {string} pkg
82+
* @param {string} version
83+
* @param {boolean} useCache
84+
* @returns {Promise<string>}
85+
*/
86+
export async function downloadPackage(pkg, version, useCache = false) {
87+
const url = await fetchPackageTarballURL(pkg, version);
88+
89+
const tmpDir = path.join(os.tmpdir(), `react-native-test-app-${version}`);
90+
const dest = path.join(tmpDir, path.basename(url));
91+
const unpackedDir = path.join(tmpDir, "package");
92+
93+
if (useCache && fs.existsSync(dest) && fs.existsSync(unpackedDir)) {
94+
return Promise.resolve(unpackedDir);
95+
}
96+
97+
console.log(`Downloading ${path.basename(url)}...`);
98+
99+
return new Promise((resolve, reject) => {
100+
https
101+
.get(url, (res) => {
102+
fs.mkdirSync(tmpDir, { recursive: true });
103+
104+
const fh = fs.createWriteStream(dest);
105+
res.pipe(fh);
106+
fh.on("finish", () => {
107+
fh.close();
108+
untar(dest);
109+
resolve(unpackedDir);
110+
});
111+
})
112+
.on("error", (err) => reject(err));
113+
});
114+
}

test/pack.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ describe("npm pack", () => {
205205
"scripts/parseargs.mjs",
206206
"scripts/schema.mjs",
207207
"scripts/template.mjs",
208+
"scripts/utils/npm.mjs",
208209
"test-app.gradle",
209210
"test_app.rb",
210211
"visionos/ReactTestApp.xcodeproj/project.pbxproj",

0 commit comments

Comments
 (0)