Skip to content

Commit

Permalink
feat: make init command standalone (#1167)
Browse files Browse the repository at this point in the history
  • Loading branch information
tido64 committed Nov 29, 2022
1 parent bbc610e commit 252afb5
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 17 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"main": "scripts/configure.js",
"bin": {
"configure-test-app": "scripts/configure.js",
"init": "scripts/init.js",
"init-test-app": "scripts/init.js",
"install-windows-test-app": "windows/test-app.js"
},
Expand Down
13 changes: 8 additions & 5 deletions scripts/configure.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const semver = require("semver");
* @typedef {{
* name: string;
* packagePath: string;
* templatePath?: string;
* testAppPath: string;
* targetVersion: string;
* platforms: Platform[];
Expand Down Expand Up @@ -407,13 +408,15 @@ const getConfig = (() => {
typeof configuration === "undefined" ||
"JEST_WORKER_ID" in process.env // skip caching when testing
) {
const { name, testAppPath, flatten, init } = params;
const { name, templatePath, testAppPath, flatten, init } = params;
const projectPathFlag = flatten ? " --project-path ." : "";
const testAppRelPath = projectRelativePath(params);
const templateDir = path.relative(
process.cwd(),
path.dirname(require.resolve("react-native/template/package.json"))
);
const templateDir =
templatePath ||
path.relative(
process.cwd(),
path.dirname(require.resolve("react-native/template/package.json"))
);
configuration = {
common: {
files: {
Expand Down
172 changes: 160 additions & 12 deletions scripts/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,155 @@
// @ts-check
"use strict";

(async () => {
const { version: targetVersion } = require("react-native/package.json");
const { configure } = require("./configure");
const { spawnSync } = require("child_process");
const path = require("path");

/**
* @template T
* @param {() => T | null} fn
* @returns {() => T | null}
*/
function memo(fn) {
/** @type {T | null} */
let result;
return () => {
if (result === undefined) {
result = fn();
}
return result;
};
}

/**
* Invokes `npm`.
* @param {...string} args
*/
function npm(...args) {
const { error, stderr, stdout } = spawnSync("npm", args, {
encoding: "utf-8",
});
if (!stdout) {
if (stderr) {
console.error(stderr);
}
throw error;
}
return stdout.trim();
}

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

/**
* Returns the installed `react-native` manifest, if present.
* @returns {string | null}
*/
const getInstalledReactNativeManifest = memo(() => {
try {
const options = { paths: [process.cwd()] };
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 } = require(manifestPath);
return version;
}

return null;
});

/**
* Returns the desired `react-native` version.
*
* Checks the following in order:
*
* - Command line flag, e.g. `--version 0.70`
* - Currently installed `react-native` version
* - Latest version from npm
*
* @returns {string}
*/
function getVersion() {
const index = process.argv.lastIndexOf("--version");
if (index >= 0) {
return process.argv[index + 1];
}

const version = getInstalledVersion();
if (version) {
return version;
}

return npm("view", "react-native", "version");
}

/**
* Returns the React Native version and path to the template.
* @returns {Promise<[string] | [string, string]>}
*/
function getTemplate() {
return new Promise((resolve, reject) => {
const version = getVersion();
if (getInstalledVersion() === version) {
const rnManifest = getInstalledReactNativeManifest();
if (rnManifest) {
resolve([version, path.join(path.dirname(rnManifest), "template")]);
return;
}
}

// `npm view` may return an array if there are multiple versions matching
// `version`. If there is only one match, the return type is a string.
const tarballs = JSON.parse(
npm("view", "--json", `react-native@${version}`, "dist.tarball")
);
const url = Array.isArray(tarballs)
? tarballs[tarballs.length - 1]
: tarballs;

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

require("https")
.get(url, (res) => {
const fs = require("fs");
const os = require("os");

const tmpDir = path.join(os.tmpdir(), "react-native-test-app");
fs.mkdirSync(tmpDir, { recursive: true });

const dest = path.join(tmpDir, path.basename(url));
const file = fs.createWriteStream(dest);
res.pipe(file);
file.on("finish", () => {
file.close();

untar(dest);

const template = path.join(tmpDir, "package", "template");
resolve([version, template]);
});
})
.on("error", (err) => {
reject(err);
});
});
}

async function main() {
/**
* @type {{
* name?: string;
Expand All @@ -17,8 +162,8 @@
{
type: "text",
name: "name",
message: "What is the name of your test app?",
initial: "TestApp",
message: "What is the name of your app?",
initial: "Example",
validate: Boolean,
},
{
Expand All @@ -43,22 +188,25 @@
]);

if (!name || !packagePath || !platforms) {
process.exit(1);
return 1;
}

const path = require("path");
const { configure } = require("./configure");

const result = configure({
const [targetVersion, templatePath] = await getTemplate();
return configure({
name,
packagePath,
templatePath,
testAppPath: path.resolve(__dirname, ".."),
targetVersion,
platforms,
flatten: true,
force: true,
init: true,
});
if (result !== 0) {
process.exit(result);
}
})();
}

main().then((result) => {
process.exitCode = result;
});
1 change: 1 addition & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10942,6 +10942,7 @@ fsevents@^2.3.2:
optional: true
bin:
configure-test-app: scripts/configure.js
init: scripts/init.js
init-test-app: scripts/init.js
install-windows-test-app: windows/test-app.js
languageName: unknown
Expand Down

0 comments on commit 252afb5

Please sign in to comment.