@newos/cli
Version:
Command-line interface for the NewOS
169 lines • 6.92 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.register = exports.generateSignature = void 0;
const inquirer_1 = __importDefault(require("inquirer"));
const prompt_1 = require("./prompts/prompt");
const telemetry_1 = __importDefault(require("./telemetry"));
function generateSignature(name, args) {
return [name, ...args.map(a => `[${a.name}${a.variadic ? '...' : ''}]`)].join(' ');
}
exports.generateSignature = generateSignature;
function register(program, spec, getAction) {
validateSpec(spec);
const signature = generateSignature(spec.name, spec.args);
const command = program
.command(signature, undefined, { noHelp: true })
.description(spec.description)
.action(async (...actionArgs) => {
const { preAction, action } = await getAction();
const [cmd, params] = getCommandParams(...actionArgs);
const abort = await (preAction === null || preAction === void 0 ? void 0 : preAction(params));
if (abort) {
return abort();
}
await promptOrValidateAll(cmd, spec, params);
telemetry_1.default.report(cmd.name(), params, !!params.interactive);
await action(params);
process.exit(0);
});
for (const opt of spec.options) {
command.option(opt.format, opt.description, opt.default);
}
}
exports.register = register;
// Unifies both options and positional arguments under the same interface of name + Param.
function* allParams(cmd, spec) {
var _a;
for (const opt of spec.options) {
// An option's name is determined by Commander by removing the leading
// dashes and camel-casing the flag. In order to avoid accidental
// differences in the algorithm we look it up in the command and use
// whatever Commander says.
const name = (_a = cmd.options.find(o => o.flags === opt.format)) === null || _a === void 0 ? void 0 : _a.attributeName();
if (name === undefined) {
// Just a sanity check. Should never happen.
throw new Error('Could not find option: ' + opt.format);
}
yield [name, opt];
}
for (const arg of spec.args) {
yield [arg.name, arg];
}
}
async function promptOrValidateAll(cmd, spec, params) {
var _a;
for (const [name, param] of allParams(cmd, spec)) {
if (param.variadic === true) {
await promptOrValidateVariadic(name, param, params);
}
else {
await promptOrValidateSimple(name, param, params);
}
await ((_a = param.after) === null || _a === void 0 ? void 0 : _a.call(param, params));
}
}
async function promptOrValidateVariadic(name, param, params) {
var _a, _b;
const values = params[name];
if (!Array.isArray(values)) {
throw new Error(`Expected multiple values for ${name}`);
}
const details = await ((_a = param.details) === null || _a === void 0 ? void 0 : _a.call(param, params));
if (details) {
if (values.length === 0 && ((_b = details === null || details === void 0 ? void 0 : details[0]) === null || _b === void 0 ? void 0 : _b.prompt) !== undefined) {
const values = [];
for (const d of details) {
if (!params.interactive || prompt_1.DISABLE_INTERACTIVITY) {
throw new Error(`Missing required parameters ${name}`);
}
values.push(await askQuestion(name, d));
}
params[name] = values;
}
else {
if (details.length !== values.length) {
throw new Error(`Expected ${details.length} values for ${name} but got ${values.length}`);
}
for (const [i, d] of details.entries()) {
throwIfInvalid(values[i], name, d);
}
}
}
}
async function promptOrValidateSimple(name, param, params) {
var _a;
const value = params[name];
const details = await ((_a = param.details) === null || _a === void 0 ? void 0 : _a.call(param, params));
if (details) {
if (value === undefined && (details === null || details === void 0 ? void 0 : details.prompt) !== undefined) {
if (!params.interactive || prompt_1.DISABLE_INTERACTIVITY) {
throw new Error(`Missing required parameter ${name}`);
}
params[name] = await askQuestion(name, details);
}
else {
throwIfInvalid(value, name, details);
}
}
}
async function askQuestion(name, details) {
var _a;
const { prompt, choices, validationError } = details;
const type = (_a = details.promptType) !== null && _a !== void 0 ? _a : (choices === undefined ? 'input' : 'list');
const validate = value => { var _a; return (_a = validationError === null || validationError === void 0 ? void 0 : validationError(value)) !== null && _a !== void 0 ? _a : true; };
const answers = await inquirer_1.default.prompt({
name: 'question',
type,
message: prompt,
choices,
validate,
default: details.preselect,
});
const value = answers.question;
// Inquirer doesn't run validations for confirm (yes/no) prompts, but we do
// want to validate because in some cases only one answer is acceptable.
// (e.g. --migrate-manifest)
if (type === 'confirm' && validationError) {
const error = validationError(value);
if (error) {
throw new Error(error);
}
}
return value;
}
function throwIfInvalid(value, name, details) {
const { validationError, choices } = details !== null && details !== void 0 ? details : {};
let error;
if (validationError) {
error = validationError(value);
}
else if (choices) {
if (!choices.some(c => (typeof c === 'object' && 'value' in c ? c.value === value : c === value))) {
error = `Invalid ${name} '${value}'`;
}
}
if (error) {
throw new Error(error);
}
}
// Converts the arguments that Commander passes to an action into an object
// where the key-value pairs correspond to positional arguments and options,
// and extracts the Command object.
function getCommandParams(...args) {
const cmd = args.pop();
const params = Object.assign({}, cmd.opts());
for (let i = 0; i < cmd._args.length; i++) {
params[cmd._args[i].name] = args[i];
}
return [cmd, params];
}
function validateSpec(spec) {
const firstVariadic = spec.args.findIndex(arg => arg.variadic);
if (firstVariadic !== -1 && firstVariadic < spec.args.length - 1) {
throw new Error('Only the last positional argument can be variadic');
}
}
//# sourceMappingURL=register-command.js.map