UNPKG

react-native-test-app

Version:

react-native-test-app provides a test app for all supported platforms as a package

289 lines (264 loc) 7.97 kB
#!/usr/bin/env node // @ts-check import { createRequire } from "node:module"; import * as path from "node:path"; import { URL, fileURLToPath } from "node:url"; import prompts from "prompts"; import { configure, getDefaultPlatformPackageName, validatePlatforms, } from "./configure.mjs"; import { memo, readJSONFile } from "./helpers.js"; import * as colors from "./utils/colors.mjs"; import { downloadPackage, fetchPackageMetadata } from "./utils/npm.mjs"; import { parseArgs } from "./utils/parseargs.mjs"; /** * Returns the installed `react-native` manifest, if present. * @returns {string | null} */ const getInstalledReactNativeManifest = memo(() => { const require = createRequire(import.meta.url); const options = { paths: [process.cwd()] }; try { return require.resolve("react-native/package.json", options); } catch (_) { return null; } }); /** * Returns the installed `react-native` version, if present. * @returns {string | null} */ const getInstalledVersion = memo(() => { const manifestPath = getInstalledReactNativeManifest(); if (manifestPath) { const { version } = readJSONFile(manifestPath); if (typeof version === "string") { return version; } } return null; }); /** * Returns the desired `react-native` version. * * Checks the following in order: * * - Command line flag, e.g. `--version 0.81` * - Currently installed `react-native` version * - Latest version from npm * * @param {import("./types.js").Platform[]} platforms * @returns {Promise<string>} */ async function getVersion(platforms) { const index = process.argv.lastIndexOf("--version"); if (index >= 0) { const m = process.argv[index + 1].match(/(\d+\.\d+[-.0-9a-z]*)/); if (!m) { throw new Error( "Expected version number of the form <major>.<minor>.<patch>-<prerelease> (where patch and prerelease are optional)" ); } return m[1]; } /** @type {(version: string, reason: string) => void} */ const logVersion = (version, reason) => { const bVersionFlag = colors.bold("--version"); const bTarget = colors.bold(version); console.log( `Using ${bTarget} because ${reason} (use ${bVersionFlag} to specify another version)` ); }; const version = getInstalledVersion(); if (version) { logVersion(version, "the current project uses it"); return version; } console.log("No version was specified; fetching available versions..."); let maxSupportedVersion = Number.MAX_VALUE; for (const p of platforms) { const pkgName = getDefaultPlatformPackageName(p); if (!pkgName) { continue; } const info = await fetchPackageMetadata(pkgName, "latest"); const [major, minor] = info.version.split("."); const v = Number(major) * 1000 + Number(minor); if (v < maxSupportedVersion) { maxSupportedVersion = v; } } const major = Math.trunc(maxSupportedVersion / 1000); const minor = maxSupportedVersion % 1000; const target = major + "." + minor; logVersion(target, "it supports all specified platforms"); return target; } /** * Returns the React Native version and path to the template. * @param {import("./types.js").Platform[]} platforms * @returns {Promise<[string, string]>} */ async function fetchTemplate(platforms) { const version = await getVersion(platforms); const output = await downloadPackage( "@react-native-community/template", version ); return [version, path.join(output, "template")]; } /** * @param {import("./types.js").Platform} platform * @returns {string} */ function instructionsFor(platform) { switch (platform) { case "android": return [ ` • Build and run the ${colors.bold("Android")} app`, "", "\tnpm run android", "", "", ].join("\n"); case "ios": case "macos": case "visionos": return [ ` • Build and run the ${colors.bold(platform.slice(0, -2) + "OS")} app`, "", `\tpod install --project-directory=${platform}`, `\tnpm run ${platform}`, "", "", ].join("\n"); case "windows": return [ ` • Build and run the ${colors.bold("Windows")} app`, "", "\tnpx install-windows-test-app", "\tnpm run windows", "", "", ].join("\n"); } throw new Error(`Unknown platform: ${platform}`); } function main() { return new Promise((resolve) => { parseArgs( "Initializes a new app project from template", { name: { description: "Name of the app", type: "string", }, platform: { description: "Platform to configure; can be specified multiple times e.g., `-p android -p ios`", type: "string", multiple: true, short: "p", }, destination: { description: "Destination path for the app", type: "string", }, version: { description: "React Native version", type: "string", short: "v", }, }, async (args) => { prompts.override({ name: args.name, platforms: typeof args.platform === "string" ? [args.platform] : args.platform, packagePath: args.destination, }); /** * @type {{ * name?: string; * packagePath?: string; * platforms?: import("./types.js").Platform[]; * }} */ const { name, packagePath, platforms } = await prompts([ { type: "text", name: "name", message: "What is the name of your app?", initial: "Example", validate: Boolean, }, { type: "multiselect", name: "platforms", message: "Which platforms do you need test apps for?", choices: [ { title: "Android", value: "android", selected: true }, { title: "iOS", value: "ios", selected: true }, { title: "macOS", value: "macos", selected: true }, { title: "visionOS", value: "visionos", selected: false }, { title: "Windows", value: "windows", selected: true }, ], min: 1, }, { type: "text", name: "packagePath", message: "Where should we create the new project?", initial: "example", validate: Boolean, }, ]); if (!name || !packagePath || !platforms) { resolve(1); return; } const [targetVersion, templatePath] = await fetchTemplate(platforms); const result = configure({ name, packagePath, templatePath, testAppPath: fileURLToPath(new URL("..", import.meta.url)), targetVersion, platforms: validatePlatforms(platforms), force: true, init: true, }); console.log( [ "", "Your React Native project is now ready.", "", "Quick start instructions:", "", " • Install npm dependencies using your preferred package manager (the following instructions are for npm):", "", `\tcd ${packagePath}`, "\tnpm install", "", "", ...platforms.sort().map((platform) => instructionsFor(platform)), " • Start the dev server", "", "\tnpm run start", "", "", "Check out the wiki for more information:", "\thttps://github.com/microsoft/react-native-test-app/wiki/Quick-Start", "", ].join("\n") ); resolve(result); } ); }); } main().then((result) => { process.exitCode = result; });