runok
Version:
NPM scripts on steroids! Replace your scripts with pure JS
98 lines (97 loc) • 3.62 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const program = require('commander');
const signale = require('signale');
const babelParser = require('@babel/parser');
const fs = require('fs');
const path = require('path');
let parser = require('parse-function')();
program.usage('<command> [options]');
program.version(1);
function default_1(commands) {
const exportCommand = program.command('export:npm');
exportCommand.description('Exports runok scripts to package.json');
exportCommand.action(exportFn);
Object.keys(commands).forEach(fnName => {
let fnBody = commands[fnName].toString();
fnBody = `async function ${fnBody.trim().replace(/^async\s/, '')}`;
let refl;
try {
refl = parser.parse(fnBody);
}
catch (err) {
throw new Error(`Can't parse ${fnName}:\n----\n${fnBody}\n----\n\n${err}`);
}
const defaults = refl.defaults;
const options = defaults.opts || defaults.options || null;
delete defaults.opts;
delete defaults.options;
delete refl.args.opts;
delete refl.args.options;
const args = refl.args.map(arg => {
if (defaults[arg])
return `[${arg}]`;
return `<${arg}>`;
});
const command = program.command(`${prepareCommandName(fnName)} ${args.join(' ')}`);
if (options) {
const parsedOptions = eval(`(${options})`);
Object.keys(parsedOptions).forEach(opt => {
const value = parsedOptions[opt];
if (value === false || value === null)
command.option(`--${opt}`);
command.option(`--${opt} [${opt}]`);
});
}
command.description(parseComments(fnBody).trim());
command.action(commands[fnName].bind(commands));
});
program.on('command:*', (cmd) => {
console.log(`\nUnknown command ${cmd}\n`);
program.outputHelp();
});
if (process.argv.length <= 2) {
program.outputHelp();
}
program.parse(process.argv);
}
exports.default = default_1;
function prepareCommandName(name) {
name = name.split(/(?=[A-Z])/).join('-').toLowerCase();
return name.replace('-', ':');
}
function parseComments(source) {
const ast = babelParser.parse(source);
const comments = ast.comments.map(c => c.value);
return comments[0] || '';
}
function exportFn() {
if (!fs.existsSync('runok.js')) {
signale.error('runok.js file not found, can\'t export its commands.');
return false;
}
if (!fs.existsSync('package.json')) {
signale.error('package.json now found, can\'t set scripts.');
return false;
}
const pkg = JSON.parse(fs.readFileSync('package.json').toString());
if (!pkg.scripts) {
pkg.scripts = {};
}
const tasks = Object.keys(require(path.join(process.cwd(), 'runok.js')));
// cleanup from previously exported
for (let s in pkg.scripts) {
if (pkg[s] && pkg[s].startsWith('./runok.js'))
delete pkg[s];
}
const scripts = Object.fromEntries(tasks.map(prepareCommandName).map(k => [k, './runok.js ' + k]));
pkg.scripts = { ...pkg.scripts, ...scripts };
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 4));
console.log('Added scripts:');
console.log();
Object.keys(scripts).forEach(k => console.log(' npm run ' + k));
console.log();
signale.info('package.json updated');
signale.info(`${Object.keys(scripts).length} scripts exported`);
return true;
}