Skip to content

Commit

Permalink
fix: fix configure-test-app crashing on 0.75 (#2190)
Browse files Browse the repository at this point in the history
  • Loading branch information
tido64 authored Aug 21, 2024
1 parent d92bd16 commit aa634df
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 118 deletions.
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ module.exports = [
{
patterns: [
{
group: ["[a-z]*", "!./*", "!node:*"],
group: ["[a-z]*", "!./*", "!./utils/*", "!node:*"],
message:
"External dependencies are not allowed in this file because it needs to be runnable before install.",
},
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"scripts/parseargs.mjs",
"scripts/schema.mjs",
"scripts/template.mjs",
"scripts/utils/npm.mjs",
"test-app.gradle",
"test_app.rb",
"visionos",
Expand Down
26 changes: 24 additions & 2 deletions scripts/configure.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
serialize,
settingsGradle,
} from "./template.mjs";
import { downloadPackage } from "./utils/npm.mjs";

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

/**
* @param {string} targetVersion
* @returns {Promise<string | undefined>}
*/
async function findTemplateDir(targetVersion) {
if (toVersionNumber(targetVersion) < v(0, 75, 0)) {
// Let `getConfig` try to find the template inside `react-native`
return undefined;
}

const [major, minor = 0] = targetVersion.split(".");
const output = await downloadPackage(
"@react-native-community/template",
`${major}.${minor}`,
true
);
return path.join(output, "template");
}

/**
* Merges specified configurations.
* @param {Configuration} lhs
Expand Down Expand Up @@ -731,19 +751,21 @@ if (isMain(import.meta.url)) {
default: platformChoices,
},
},
({
async ({
_: { [0]: name },
flatten,
force,
init,
package: packagePath,
platforms,
}) => {
const targetVersion = getPackageVersion("react-native");
process.exitCode = configure({
name: typeof name === "string" && name ? name : getAppName(packagePath),
packagePath,
templatePath: await findTemplateDir(targetVersion),
testAppPath: fileURLToPath(new URL("..", import.meta.url)),
targetVersion: getPackageVersion("react-native"),
targetVersion,
platforms: validatePlatforms(platforms),
flatten,
force,
Expand Down
22 changes: 0 additions & 22 deletions scripts/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,6 @@ const nodefs = require("node:fs");
const path = require("node:path");
const { fileURLToPath } = require("node:url");

const npmRegistryBaseURL = "https://registry.npmjs.org/";

/**
* Fetches package metadata from the npm registry.
* @param {string} pkg
* @param {string=} distTag
*/
function fetchPackageMetadata(pkg, distTag) {
const init = {
headers: {
Accept:
"application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*",
},
};
const url = distTag
? npmRegistryBaseURL + pkg + "/" + distTag
: npmRegistryBaseURL + pkg;
return fetch(url, init).then((res) => res.json());
}

/**
* Finds the specified file using Node module resolution.
* @param {string} file
Expand Down Expand Up @@ -171,13 +151,11 @@ function getPackageVersion(module, startDir = process.cwd(), fs = nodefs) {
return version;
}

exports.fetchPackageMetadata = fetchPackageMetadata;
exports.findFile = findFile;
exports.findNearest = findNearest;
exports.getPackageVersion = getPackageVersion;
exports.isMain = isMain;
exports.memo = memo;
exports.npmRegistryBaseURL = npmRegistryBaseURL;
exports.readJSONFile = readJSONFile;
exports.readTextFile = readTextFile;
exports.requireTransitive = requireTransitive;
Expand Down
93 changes: 2 additions & 91 deletions scripts/init.mjs
Original file line number Diff line number Diff line change
@@ -1,74 +1,14 @@
#!/usr/bin/env node
// @ts-check
import { spawnSync } from "node:child_process";
import * as fs from "node:fs";
import * as https from "node:https";
import { createRequire } from "node:module";
import * as os from "node:os";
import * as path from "node:path";
import { fileURLToPath } from "node:url";
import prompts from "prompts";
import * as colors from "./colors.mjs";
import { configure, getDefaultPlatformPackageName } from "./configure.mjs";
import {
fetchPackageMetadata,
memo,
readJSONFile,
toVersionNumber,
v,
} from "./helpers.js";
import { memo, readJSONFile, toVersionNumber, v } from "./helpers.js";
import { parseArgs } from "./parseargs.mjs";

/**
* Invokes `tar xf`.
* @param {string} archive
*/
function untar(archive) {
const args = ["xf", archive];
const options = { cwd: path.dirname(archive) };
const result = spawnSync("tar", args, options);

// If we run `tar` from Git Bash with a Windows path, it will fail with:
//
// tar: Cannot connect to C: resolve failed
//
// GNU Tar assumes archives with a colon in the file name are on another
// machine. See also
// https://www.gnu.org/software/tar/manual/html_section/file.html.
if (
process.platform === "win32" &&
result.stderr.toString().includes("tar: Cannot connect to")
) {
args.push("--force-local");
return spawnSync("tar", args, options);
}

return result;
}

/**
* Fetches the tarball URL for the specified package and version.
* @param {string} pkg
* @param {string} version
* @returns {Promise<string>}
*/
async function fetchPackageTarballURL(pkg, version) {
const info = await fetchPackageMetadata(pkg);
const specific = info.versions[version];
if (specific) {
return specific.dist.tarball;
}

const versions = Object.keys(info.versions);
for (let i = versions.length - 1; i >= 0; --i) {
const v = versions[i];
if (v.startsWith(version)) {
return info.versions[v].dist.tarball;
}
}

throw new Error(`No match found for '${pkg}@${version}'`);
}
import { downloadPackage, fetchPackageMetadata } from "./utils/npm.mjs";

/**
* Returns the installed `react-native` manifest, if present.
Expand Down Expand Up @@ -165,35 +105,6 @@ async function getVersion(platforms) {
return target;
}

/**
* Downloads the specified npm package.
* @param {string} pkg
* @param {string} version
* @returns {Promise<string>}
*/
async function downloadPackage(pkg, version) {
const url = await fetchPackageTarballURL(pkg, version);
console.log(`Downloading ${path.basename(url)}...`);

return new Promise((resolve, reject) => {
https
.get(url, (res) => {
const tmpDir = path.join(os.tmpdir(), "react-native-test-app");
fs.mkdirSync(tmpDir, { recursive: true });

const dest = path.join(tmpDir, path.basename(url));
const fh = fs.createWriteStream(dest);
res.pipe(fh);
fh.on("finish", () => {
fh.close();
untar(dest);
resolve(path.join(tmpDir, "package"));
});
})
.on("error", (err) => reject(err));
});
}

/**
* Returns the React Native version and path to the template.
* @param {import("./types.js").Platform[]} platforms
Expand Down
3 changes: 1 addition & 2 deletions scripts/set-react-version.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@ import { promises as fs } from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
import {
fetchPackageMetadata,
isMain,
npmRegistryBaseURL,
readJSONFile,
readTextFile,
toVersionNumber,
v,
} from "./helpers.js";
import { fetchPackageMetadata, npmRegistryBaseURL } from "./utils/npm.mjs";

/**
* @typedef {import("./types.js").Manifest} Manifest
Expand Down
17 changes: 17 additions & 0 deletions scripts/test-matrix.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,23 @@ if (platforms.length === 0) {
"ios"
);
})
.then(() => {
showBanner(`Reconfigure existing app`);
$(
PACKAGE_MANAGER,
"configure-test-app",
"-p",
"android",
"-p",
"ios",
"-p",
"macos",
"-p",
"visionos",
"-p",
"windows"
);
})
.then(() => {
showBanner(green("✔ Pass"));
});
Expand Down
114 changes: 114 additions & 0 deletions scripts/utils/npm.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// @ts-check
import { spawnSync } from "node:child_process";
import * as fs from "node:fs";
import * as https from "node:https";
import * as os from "node:os";
import * as path from "node:path";

export const npmRegistryBaseURL = "https://registry.npmjs.org/";

/**
* Invokes `tar xf`.
* @param {string} archive
*/
function untar(archive) {
const args = ["xf", archive];
const options = { cwd: path.dirname(archive) };
const result = spawnSync("tar", args, options);

// If we run `tar` from Git Bash with a Windows path, it will fail with:
//
// tar: Cannot connect to C: resolve failed
//
// GNU Tar assumes archives with a colon in the file name are on another
// machine. See also
// https://www.gnu.org/software/tar/manual/html_section/file.html.
if (
process.platform === "win32" &&
result.stderr.toString().includes("tar: Cannot connect to")
) {
args.push("--force-local");
return spawnSync("tar", args, options);
}

return result;
}

/**
* Fetches package metadata from the npm registry.
* @param {string} pkg
* @param {string=} distTag
*/
export function fetchPackageMetadata(pkg, distTag) {
const init = {
headers: {
Accept:
"application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*",
},
};
const url = distTag
? npmRegistryBaseURL + pkg + "/" + distTag
: npmRegistryBaseURL + pkg;
return fetch(url, init).then((res) => res.json());
}

/**
* Fetches the tarball URL for the specified package and version.
* @param {string} pkg
* @param {string} version
* @returns {Promise<string>}
*/
async function fetchPackageTarballURL(pkg, version) {
const info = await fetchPackageMetadata(pkg);
const specific = info.versions[version];
if (specific) {
return specific.dist.tarball;
}

const versions = Object.keys(info.versions);
for (let i = versions.length - 1; i >= 0; --i) {
const v = versions[i];
if (v.startsWith(version)) {
return info.versions[v].dist.tarball;
}
}

throw new Error(`No match found for '${pkg}@${version}'`);
}

/**
* Downloads the specified npm package.
* @param {string} pkg
* @param {string} version
* @param {boolean} useCache
* @returns {Promise<string>}
*/
export async function downloadPackage(pkg, version, useCache = false) {
const url = await fetchPackageTarballURL(pkg, version);

const tmpDir = path.join(os.tmpdir(), `react-native-test-app-${version}`);
const dest = path.join(tmpDir, path.basename(url));
const unpackedDir = path.join(tmpDir, "package");

if (useCache && fs.existsSync(dest) && fs.existsSync(unpackedDir)) {
return Promise.resolve(unpackedDir);
}

console.log(`Downloading ${path.basename(url)}...`);

return new Promise((resolve, reject) => {
https
.get(url, (res) => {
fs.mkdirSync(tmpDir, { recursive: true });

const fh = fs.createWriteStream(dest);
res.pipe(fh);
fh.on("finish", () => {
fh.close();
untar(dest);
resolve(unpackedDir);
});
})
.on("error", (err) => reject(err));
});
}
1 change: 1 addition & 0 deletions test/pack.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ describe("npm pack", () => {
"scripts/parseargs.mjs",
"scripts/schema.mjs",
"scripts/template.mjs",
"scripts/utils/npm.mjs",
"test-app.gradle",
"test_app.rb",
"visionos/ReactTestApp.xcodeproj/project.pbxproj",
Expand Down

0 comments on commit aa634df

Please sign in to comment.