react-native-test-app
Version:
react-native-test-app provides a test app for all supported platforms as a package
154 lines (140 loc) • 3.53 kB
JavaScript
// @ts-check
"use strict";
/**
* @typedef {{
* description: string;
* type: "string" | "boolean";
* multiple?: boolean;
* short?: string;
* default?: string | boolean | string[];
* }} Option;
*
* @typedef {{ [key: string]: Option }} Options;
*/
/**
* @template {Option} O
* @typedef {O extends { type: "boolean" }
* ? boolean
* : O extends { type: "string", multiple: true }
* ? string[]
* : string
* } InferredOptionType<O>;
*/
/**
* @template {Options} O
* @typedef {{ [key in keyof O]: InferredOptionType<O[key]> }} InferredOptionTypes<O>;
*/
/**
* @template {Options} O
* @typedef {InferredOptionTypes<O> & { _: string[] }} Args;
*/
/**
* @typedef {{
* string: string[];
* boolean: string[];
* alias: Record<string, string>;
* default: Record<string, string | boolean | string[]>;
* }} MinimistOptions;
*/
/**
* Generates help message.
* @param {string} description
* @param {Record<string, { short?: string; description: string; }>} options
* @returns {string}
*/
function formatHelp(description, options) {
const flags = Object.entries(options);
const indent = " ";
const minWidth =
Math.max(...flags.map(([flag]) => flag.length)) + indent.length * 2;
// @ts-expect-error This expression is not callable
const ui = require("cliui")();
for (const [flag, config] of flags) {
ui.div(
{ text: "", width: 2 },
{ text: config.short ? `-${config.short},` : "", width: 4 },
{ text: `--${flag}`, width: minWidth + 2 },
{ text: config.description }
);
}
const script = require("path").basename(process.argv[1]);
return [
`usage: ${script} [options]`,
"",
description,
"",
"Options:",
ui.toString(),
"",
].join("\n");
}
/**
* Converts options to the format required by minimist.
* @param {Options} options
* @returns {MinimistOptions}
*/
function minimistOptions(options) {
/** @type {MinimistOptions} */
const minimist = {
string: [],
boolean: [],
alias: {},
default: {},
};
for (const [flag, option] of Object.entries(options)) {
if (option.type === "boolean") {
minimist.boolean.push(flag);
} else {
minimist.string.push(flag);
}
if (option.short) {
minimist.alias[option.short] = flag;
}
if (option.default) {
minimist.default[flag] = option.default;
}
}
return minimist;
}
/**
* Parses command line arguments.
*
* Note: In the future, we can replace minimist with `util.parseArgs`.
*
* @see {@link https://nodejs.org/api/util.html#utilparseargsconfig}
*
* @template {Options} O
* @param {string} description
* @param {O} options
* @param {(args: Args<O>) => void} callback
*/
function parseArgs(description, options, callback) {
const mergedOptions = {
help: {
description: "Show this help message",
type: "boolean",
short: "h",
default: false,
},
version: {
description: "Show version number",
type: "boolean",
short: "v",
default: false,
},
...options,
};
const args = require("minimist")(
process.argv.slice(2),
minimistOptions(options)
);
if (args["help"]) {
console.log(formatHelp(description, mergedOptions));
} else if (args["version"]) {
const { name, version } = require("../package.json");
console.log(`${name} ${version}`);
} else {
callback(/** @type {Args<O>} */ (args));
}
}
exports.parseArgs = parseArgs;