script-launcher
Version:
Enhance your package.json scripts with features like: menus, functions, arrays, concurrency and many more.
305 lines (304 loc) • 11.8 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.launchMenu = void 0;
const config_loader_1 = require("./config-loader");
const executor_1 = require("./executor");
const common_1 = require("./common");
const elements_1 = require("prompts/lib/elements");
function launchMenu(environment, settings, config, args, interactive, timeout, menuConfirm, confirm, limit, dry, testmode) {
return __awaiter(this, void 0, void 0, function* () {
try {
const defaultChoice = config.options.menu.defaultChoice.split(':');
let script = {
name: config.options.menu.defaultChoice,
inline: false,
multiple: false,
parameters: {},
arguments: [],
script: config.options.menu.defaultScript,
timedout: false
};
const shell = config_loader_1.Config.evaluateShellOption(config.options.script.shell, true);
if (interactive || !script.script) {
const defaultSelect = args.length > 0 ? args[0] : config.options.menu.defaultSelect;
const pageSize = config.options.menu.pageSize;
let autoIndex = 0;
if (defaultSelect) {
const items = defaultSelect.split(':');
const length = items.length < defaultChoice.length ? items.length : defaultChoice.length;
for (let index = 0; index < length; index++) {
defaultChoice[index] = items[index];
}
autoIndex = length;
}
script = yield timeoutMenu(config.menu, pageSize, defaultChoice, timeout, autoIndex);
if (!script.timedout && menuConfirm && !(yield (0, common_1.confirmPrompt)('Are you sure', undefined, true))) {
return {
startTime: process.hrtime(),
exitCode: 0
};
}
console.log();
}
else {
console.log(common_1.Colors.Bold +
'Auto menu: ' +
common_1.Colors.Dim +
script.name.padEnd(28) +
common_1.Colors.Normal +
common_1.Colors.Dim +
' (Use the menu by running:' +
common_1.Colors.Bold +
' npm start menu' +
common_1.Colors.Normal +
common_1.Colors.Dim +
')' +
common_1.Colors.Normal);
}
if (!script.name.startsWith('menu:'))
script.name = 'menu:' + script.name;
const command = getStartCommand(script.script, config.scripts);
if (command && environment.npm_lifecycle_event === 'start') {
console.log(common_1.Colors.Bold + 'Executing: ' + common_1.Colors.Dim + 'npm start ' + script.script + common_1.Colors.Normal);
console.log();
}
const executor = new executor_1.Executor(shell, environment, settings, config.scripts, config.options.glob, confirm, limit, dry, testmode);
script.arguments = args;
return {
startTime: executor.startTime,
exitCode: yield executor.execute(script)
};
}
catch (_a) {
return {
startTime: [0, 0],
exitCode: 0
};
}
});
}
exports.launchMenu = launchMenu;
function getStartCommand(script, scripts) {
const result = [];
if (script.concurrent)
return null;
if (script.sequential)
return null;
if (script['concurrent-then'])
return null;
if (script['sequential-then'])
return null;
if (script['concurrent-else'])
return null;
if (script['sequential-else'])
return null;
if (script instanceof Array)
result.push(...script);
if (typeof script === 'string')
result.push(script);
if (script instanceof String)
result.push(script.toString());
if (result.length !== 1 || scripts.find(result[0]).length === 0)
return null;
return result[0];
}
function getMenuItem(label) {
const match = label.match(/(.*?)\:(.*)$/);
if (match) {
return {
name: match[1],
help: match[2]
};
}
return {
name: label,
help: ''
};
}
function findMenuItem(menu, name) {
for (const [label, value] of Object.entries(menu)) {
const item = getMenuItem(label);
if (item.name === name)
return value;
}
return null;
}
function createChoices(menu) {
const choices = [];
const menuHelp = {};
const menuItems = {};
for (const [name, value] of Object.entries(menu)) {
if (name !== 'description') {
const item = getMenuItem(name);
menuHelp[item.name] = item.help;
menuItems[item.name] = value;
}
}
for (const [name, value] of Object.entries(menuItems)) {
if (name === 'separator' && typeof value === 'string') {
// Separator not supported by prompts
}
else {
if (Object.keys(value).length !== 0) {
choices.push({
title: name,
value: name,
description: menuHelp[name]
});
}
}
}
return choices;
}
function getDefaultScript(menu, choices) {
let index = 0;
do {
const filtered = Object.entries(menu).filter(([key, value]) => key !== 'description' && key !== 'separator' && !!value && Object.entries(value).length > 0);
const choice = choices[index++];
let result = filtered.find(([label, value]) => getMenuItem(label).name === choice);
if (result === undefined)
result = filtered[0];
if (result === undefined)
return '';
const [key, value] = result;
if (typeof value === 'string')
return value;
if (Array.isArray(value))
return value;
menu = value;
} while (index < 30);
return '';
}
function timeoutMenu(menu, pageSize, defaultChoice, timeout, autoIndex) {
return __awaiter(this, void 0, void 0, function* () {
let menuPromise;
let timeoutId = null;
let currentTimeout = timeout;
if (currentTimeout > 0) {
timeoutId = setInterval(() => {
if (--currentTimeout > 0) {
process.stdout.write('\x1b[s'); // Save cursor position
console.info();
process.stdout.write(common_1.Colors.Bold + 'Auto select in: ' + common_1.Colors.Normal + currentTimeout);
process.stdout.write('\x1b[u'); // Restore cursor position
return;
}
clearTimeout(timeoutId);
timeoutId = null;
menuPromise.close();
}, 1000);
const listener = (char, key) => {
clearTimeout(timeoutId);
if (currentTimeout !== timeout) {
process.stdout.write('\x1b[s'); // Save cursor position
console.info();
process.stdout.write('\x1b[K');
process.stdout.write('\x1b[u'); // Restore cursor position
}
process.stdin.removeListener('keypress', listener);
};
process.stdin.on('keypress', listener);
}
try {
menuPromise = promptMenu(menu, pageSize, defaultChoice, [], autoIndex);
const scriptInfo = yield menuPromise;
if (scriptInfo === null) {
return {
name: defaultChoice.join(':'),
inline: false,
multiple: false,
parameters: {},
arguments: [],
script: getDefaultScript(menu, defaultChoice),
timedout: true
};
}
return Object.assign(Object.assign({}, scriptInfo), {
timedout: false
});
}
finally {
if (timeoutId)
clearTimeout(timeoutId);
}
});
}
function promptMenu(menu, pageSize, defaults, choice, autoIndex) {
const choices = createChoices(menu);
let close = false;
defaults = [...defaults];
if (choices.length === 0) {
console.log('Nothing to do: Menu not available and no action specified.');
throw new Error('Nothing to do: Menu not available and no action specified.');
}
let initialIndex = choices.findIndex(item => item.title === defaults[0]);
if (initialIndex === -1)
initialIndex = 0;
const selectMenu = new elements_1.SelectPrompt({
type: 'select',
name: 'value',
message: 'Select' + (menu.description ? ' ' + menu.description : ''),
initial: initialIndex,
choices: choices
});
const menuPromise = (0, common_1.toPromise)(selectMenu);
if (autoIndex > 0)
selectMenu.submit();
const resultPromise = menuPromise.then(answer => {
if (close || answer.length === 0)
return null;
const menuItem = findMenuItem(menu, answer[0]);
if (menuItem === null) {
const message = 'Menu item not found!';
console.log(common_1.Colors.Bold + common_1.Colors.Red + message + common_1.Colors.Normal);
throw new Error(message);
}
choice.push(answer[0]);
defaults.shift();
if (!isMenuObject(menuItem)) {
return {
name: choice.join(':'),
inline: false,
circular: false,
multiple: false,
parameters: {},
arguments: [],
script: menuItem
};
}
return promptMenu(menuItem, pageSize, defaults, choice, autoIndex - 1);
});
resultPromise.close = () => {
close = true;
selectMenu.submit();
};
return resultPromise;
}
function isMenuObject(object) {
if (object instanceof Array)
return false;
if (typeof object === 'string')
return false;
if (object.concurrent && object.concurrent instanceof Array)
return false;
if (object.sequential && object.sequential instanceof Array)
return false;
if (object['concurrent-then'] && object['concurrent-then'] instanceof Array)
return false;
if (object['sequential-then'] && object['sequential-then'] instanceof Array)
return false;
if (object['concurrent-else'] && object['concurrent-else'] instanceof Array)
return false;
if (object['sequential-else'] && object['sequential-else'] instanceof Array)
return false;
return true;
}