@semo/cli
Version:
A command line tools dispatcher
341 lines • 13.6 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());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.handler = exports.builder = exports.desc = exports.aliases = exports.command = exports.plugin = void 0;
const repl_1 = __importDefault(require("repl"));
const path_1 = __importDefault(require("path"));
const core_1 = require("@semo/core");
const yargs_parser_1 = __importDefault(require("yargs-parser"));
const fs_extra_1 = __importDefault(require("fs-extra"));
let r; // repl instance
let v; // yargs argv
const importPackage = (name, force = false) => {
return core_1.Utils.importPackage(name, 'repl-package-cache', true, force);
};
const reload = () => __awaiter(void 0, void 0, void 0, function* () {
const scriptName = v.scriptName || 'semo';
let pluginsReturn = yield core_1.Utils.invokeHook(`${scriptName}:repl`, core_1.Utils._.isBoolean(v.hook)
? {
reload: true,
mode: 'group',
}
: {
mode: 'group',
include: core_1.Utils.splitComma(v.hook),
reload: true,
});
pluginsReturn = core_1.Utils._.omitBy(pluginsReturn, core_1.Utils._.isEmpty);
r.context.Semo.hooks = core_1.Utils.formatRcOptions(pluginsReturn);
if (v.extract && v.extract.length > 0) {
v.extract.forEach(keyPath => {
r.context = Object.assign(r.context, core_1.Utils._.get(r.context, keyPath) || {});
});
}
const hookReplCommands = yield core_1.Utils.invokeHook('semo:repl_command');
Object.keys(hookReplCommands)
.filter(command => {
return ![
'break',
'clear',
'editor',
'exit',
'help',
'history',
'load',
'reload',
'save',
].includes(command);
})
.forEach(command => {
r.defineCommand(command, hookReplCommands[command]);
});
console.log(core_1.Utils.success('Hooked files reloaded.'));
});
const extract = (obj, keys = []) => {
const keysCast = core_1.Utils._.castArray(keys);
Object.keys(obj).forEach(key => {
if (keys.length === 0 || keysCast.includes(key)) {
Object.defineProperty(r.context, key, { value: obj[key] });
}
});
};
const corepl = (cli) => {
const originalEval = cli.eval;
// @ts-ignore
cli.eval = function coEval(cmd, context, filename, callback) {
if (cmd.match(/^await\s+/) ||
(cmd.match(/.*?await\s+/) && cmd.match(/^\s*\{/))) {
if (cmd.match(/=/)) {
cmd = '(async function() { (' + cmd + ') })()';
}
else {
cmd = '(async function() { let _ = ' + cmd + '; return _;})()';
}
}
else if (cmd.match(/\W*await\s+/)) {
cmd =
'(async function() { (' +
cmd.replace(/^\s*(var|let|const)\s+/, '') +
') })()';
}
function done(val) {
return callback(null, val);
}
originalEval.call(cli, cmd, context, filename, function (err, res) {
if (err || !res || typeof res.then !== 'function') {
return callback(err, res);
}
else {
return res.then(done, callback);
}
});
};
return cli;
};
exports.plugin = 'semo';
exports.command = 'repl [replFile]';
exports.aliases = 'r';
exports.desc = 'Play with REPL';
function openRepl(context) {
return __awaiter(this, void 0, void 0, function* () {
const { Semo } = context;
const argv = Semo.argv;
r = repl_1.default.start({
prompt: argv.prompt,
ignoreUndefined: true,
});
r.defineCommand('reload', {
help: 'Reload hooked files',
action(name) {
return __awaiter(this, void 0, void 0, function* () {
this.clearBufferedCommand();
try {
yield reload();
}
catch (e) {
core_1.Utils.error(e.message);
}
this.displayPrompt();
});
},
});
r.defineCommand('shell', {
help: 'Execute shell commands',
action(cmd) {
return __awaiter(this, void 0, void 0, function* () {
this.clearBufferedCommand();
try {
core_1.Utils.exec(cmd);
}
catch (e) { }
this.displayPrompt();
});
},
});
const requireAction = function (input) {
return __awaiter(this, void 0, void 0, function* () {
// @ts-ignore
this.clearBufferedCommand();
try {
const opts = (0, yargs_parser_1.default)(input);
const packages = {};
for (const part of opts._) {
const split = part.split(':');
if (split.length === 1) {
packages[part] = part;
}
else if (split.length > 1) {
packages[split[0]] = split[1];
}
}
for (const pack in packages) {
const imported = importPackage(pack);
Object.defineProperty(r.context, packages[pack], { value: imported });
}
}
catch (e) { }
// @ts-ignore
this.displayPrompt();
});
};
// Add require and import command
r.defineCommand('require', {
help: 'Require npm packages',
action: requireAction,
});
r.defineCommand('import', {
help: 'import npm packages',
action: requireAction,
});
const hookReplCommands = yield core_1.Utils.invokeHook('semo:repl_command');
Object.keys(hookReplCommands)
.filter(command => {
return ![
'break',
'clear',
'editor',
'exit',
'help',
'history',
'load',
'reload',
'save',
].includes(command);
})
.forEach(command => {
r.defineCommand(command, hookReplCommands[command]);
});
const Home = process.env.HOME + `/.${argv.scriptName}`;
fs_extra_1.default.ensureDirSync(Home);
if (!core_1.Utils.fileExistsSyncCache(Home)) {
core_1.Utils.exec(`mkdir -p ${Home}`);
}
core_1.Utils.replHistory(r, `${Home}/.${argv.scriptName}_repl_history`);
// @ts-ignore
// context即为REPL中的上下文环境
r.context = Object.assign(r.context, context);
r.context.Semo.repl = r;
corepl(r);
});
}
const builder = function (yargs) {
yargs.option('hook', {
describe: 'If or not load all plugins repl hook',
});
yargs.option('prompt', {
describe: 'Prompt for input. default is >>>',
});
yargs.option('extract', {
describe: 'Auto extract k/v from Semo object by key path',
});
yargs.option('import', {
describe: 'import package, same as require option, e.g. --import=lodash:_',
});
yargs.option('require', {
describe: 'require package, same as import option, e.g. --require=lodash:_',
alias: 'r',
});
};
exports.builder = builder;
const handler = function (argv) {
return __awaiter(this, void 0, void 0, function* () {
const VERSION = argv.$semo.VERSION;
const scriptName = argv.scriptName || 'semo';
argv.hook = core_1.Utils.pluginConfig('repl.hook', core_1.Utils.pluginConfig('hook', false));
argv.prompt = core_1.Utils.pluginConfig('repl.prompt', core_1.Utils.pluginConfig('prompt', '>>> '));
argv.extract = core_1.Utils.pluginConfig('repl.extract', core_1.Utils.pluginConfig('extract', ''));
argv.require = core_1.Utils.pluginConfig('repl.require', []).concat(core_1.Utils.pluginConfig('require', []));
argv.import = core_1.Utils.pluginConfig('repl.import', []).concat(core_1.Utils.pluginConfig('import', []));
const requiredPackages = core_1.Utils._.castArray(argv.require);
const importedPackages = core_1.Utils._.castArray(argv.import);
const concatPackages = core_1.Utils._.chain(requiredPackages)
.concat(importedPackages)
.uniq()
.filter()
.value();
const packages = {};
concatPackages.forEach(item => {
const splited = item.split(':');
if (splited.length === 1) {
packages[item] = item;
}
else {
packages[splited[0]] = splited[1];
}
});
if (core_1.Utils._.isString(argv.extract)) {
argv.extract = core_1.Utils._.castArray(argv.extract);
}
v = argv;
try {
let context = Object.assign({ await: true }, {
Semo: {
VERSION,
Utils: core_1.Utils,
argv,
import: importPackage,
require: importPackage,
extract,
reload,
run: core_1.Utils.run,
},
});
for (const pack in packages) {
context[packages[pack]] = importPackage(pack);
}
if (argv.hook) {
let pluginsReturn = yield core_1.Utils.invokeHook(`${scriptName}:repl`, core_1.Utils._.isBoolean(argv.hook)
? {
mode: 'group',
}
: {
include: core_1.Utils.splitComma(argv.hook),
mode: 'group',
});
pluginsReturn = core_1.Utils._.omitBy(pluginsReturn, core_1.Utils._.isEmpty);
const shortenKeysPluginsReturn = {};
Object.keys(pluginsReturn).forEach(plugin => {
let newKey = plugin;
const prefix = scriptName + '-plugin-';
if (plugin.indexOf(prefix) > -1) {
newKey = plugin.substring(prefix.length);
}
shortenKeysPluginsReturn[newKey] = pluginsReturn[plugin];
});
context.Semo.hooks = core_1.Utils.formatRcOptions(shortenKeysPluginsReturn) || {};
}
if (core_1.Utils._.isArray(argv.extract)) {
if (argv.extract && argv.extract.length > 0) {
argv.extract.forEach(keyPath => {
context = Object.assign(context, core_1.Utils._.get(context.Semo.hooks, keyPath) || {});
});
}
}
else {
Object.keys(argv.extract).forEach(key => {
const extractKeys = core_1.Utils._.castArray(argv.extract[key]);
extractKeys.forEach(extractKey => {
const splitExtractKey = extractKey.split('.');
const finalExtractKey = splitExtractKey[splitExtractKey.length - 1];
context[finalExtractKey] = core_1.Utils._.get(context.Semo.hooks, `${key}.${extractKey}`);
});
});
}
if (argv.replFile) {
const replFilePath = path_1.default.resolve(process.cwd(), argv.replFile);
if (argv.replFile && fs_extra_1.default.existsSync(replFilePath)) {
try {
const replRequired = require(replFilePath);
if (replRequired.handler &&
core_1.Utils._.isFunction(replRequired.handler)) {
yield replRequired.handler(argv, context);
}
else if (core_1.Utils._.isFunction(replRequired)) {
yield replRequired(argv, context);
}
}
catch (e) { }
}
}
yield openRepl(context);
return false;
}
catch (e) {
core_1.Utils.error(e.stack);
}
return true;
});
};
exports.handler = handler;
//# sourceMappingURL=repl.js.map