UNPKG

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
"use strict"; 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; }