mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-25 21:34:12 +01:00
Current file header is : /*! showdown v 2.0.0-alpha1 - 25-09-2018 */ Official 2.0.0-alpha doesn't match neither on content nor date :( Considering date we would be at 1.8.7, but it is also quite different (and has vulnerabilities) In consequence switching to 2.0.0.
450 lines
19 KiB
JavaScript
450 lines
19 KiB
JavaScript
import { assertNotStrictEqual, } from './typings/common-types.js';
|
|
import { isPromise } from './utils/is-promise.js';
|
|
import { applyMiddleware, commandMiddlewareFactory, } from './middleware.js';
|
|
import { parseCommand } from './parse-command.js';
|
|
import { isYargsInstance, } from './yargs-factory.js';
|
|
import { maybeAsyncResult } from './utils/maybe-async-result.js';
|
|
import whichModule from './utils/which-module.js';
|
|
const DEFAULT_MARKER = /(^\*)|(^\$0)/;
|
|
export class CommandInstance {
|
|
constructor(usage, validation, globalMiddleware, shim) {
|
|
this.requireCache = new Set();
|
|
this.handlers = {};
|
|
this.aliasMap = {};
|
|
this.frozens = [];
|
|
this.shim = shim;
|
|
this.usage = usage;
|
|
this.globalMiddleware = globalMiddleware;
|
|
this.validation = validation;
|
|
}
|
|
addDirectory(dir, req, callerFile, opts) {
|
|
opts = opts || {};
|
|
if (typeof opts.recurse !== 'boolean')
|
|
opts.recurse = false;
|
|
if (!Array.isArray(opts.extensions))
|
|
opts.extensions = ['js'];
|
|
const parentVisit = typeof opts.visit === 'function' ? opts.visit : (o) => o;
|
|
opts.visit = (obj, joined, filename) => {
|
|
const visited = parentVisit(obj, joined, filename);
|
|
if (visited) {
|
|
if (this.requireCache.has(joined))
|
|
return visited;
|
|
else
|
|
this.requireCache.add(joined);
|
|
this.addHandler(visited);
|
|
}
|
|
return visited;
|
|
};
|
|
this.shim.requireDirectory({ require: req, filename: callerFile }, dir, opts);
|
|
}
|
|
addHandler(cmd, description, builder, handler, commandMiddleware, deprecated) {
|
|
let aliases = [];
|
|
const middlewares = commandMiddlewareFactory(commandMiddleware);
|
|
handler = handler || (() => { });
|
|
if (Array.isArray(cmd)) {
|
|
if (isCommandAndAliases(cmd)) {
|
|
[cmd, ...aliases] = cmd;
|
|
}
|
|
else {
|
|
for (const command of cmd) {
|
|
this.addHandler(command);
|
|
}
|
|
}
|
|
}
|
|
else if (isCommandHandlerDefinition(cmd)) {
|
|
let command = Array.isArray(cmd.command) || typeof cmd.command === 'string'
|
|
? cmd.command
|
|
: this.moduleName(cmd);
|
|
if (cmd.aliases)
|
|
command = [].concat(command).concat(cmd.aliases);
|
|
this.addHandler(command, this.extractDesc(cmd), cmd.builder, cmd.handler, cmd.middlewares, cmd.deprecated);
|
|
return;
|
|
}
|
|
else if (isCommandBuilderDefinition(builder)) {
|
|
this.addHandler([cmd].concat(aliases), description, builder.builder, builder.handler, builder.middlewares, builder.deprecated);
|
|
return;
|
|
}
|
|
if (typeof cmd === 'string') {
|
|
const parsedCommand = parseCommand(cmd);
|
|
aliases = aliases.map(alias => parseCommand(alias).cmd);
|
|
let isDefault = false;
|
|
const parsedAliases = [parsedCommand.cmd].concat(aliases).filter(c => {
|
|
if (DEFAULT_MARKER.test(c)) {
|
|
isDefault = true;
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
if (parsedAliases.length === 0 && isDefault)
|
|
parsedAliases.push('$0');
|
|
if (isDefault) {
|
|
parsedCommand.cmd = parsedAliases[0];
|
|
aliases = parsedAliases.slice(1);
|
|
cmd = cmd.replace(DEFAULT_MARKER, parsedCommand.cmd);
|
|
}
|
|
aliases.forEach(alias => {
|
|
this.aliasMap[alias] = parsedCommand.cmd;
|
|
});
|
|
if (description !== false) {
|
|
this.usage.command(cmd, description, isDefault, aliases, deprecated);
|
|
}
|
|
this.handlers[parsedCommand.cmd] = {
|
|
original: cmd,
|
|
description,
|
|
handler,
|
|
builder: builder || {},
|
|
middlewares,
|
|
deprecated,
|
|
demanded: parsedCommand.demanded,
|
|
optional: parsedCommand.optional,
|
|
};
|
|
if (isDefault)
|
|
this.defaultCommand = this.handlers[parsedCommand.cmd];
|
|
}
|
|
}
|
|
getCommandHandlers() {
|
|
return this.handlers;
|
|
}
|
|
getCommands() {
|
|
return Object.keys(this.handlers).concat(Object.keys(this.aliasMap));
|
|
}
|
|
hasDefaultCommand() {
|
|
return !!this.defaultCommand;
|
|
}
|
|
runCommand(command, yargs, parsed, commandIndex, helpOnly, helpOrVersionSet) {
|
|
const commandHandler = this.handlers[command] ||
|
|
this.handlers[this.aliasMap[command]] ||
|
|
this.defaultCommand;
|
|
const currentContext = yargs.getInternalMethods().getContext();
|
|
const parentCommands = currentContext.commands.slice();
|
|
const isDefaultCommand = !command;
|
|
if (command) {
|
|
currentContext.commands.push(command);
|
|
currentContext.fullCommands.push(commandHandler.original);
|
|
}
|
|
const builderResult = this.applyBuilderUpdateUsageAndParse(isDefaultCommand, commandHandler, yargs, parsed.aliases, parentCommands, commandIndex, helpOnly, helpOrVersionSet);
|
|
return isPromise(builderResult)
|
|
? builderResult.then(result => this.applyMiddlewareAndGetResult(isDefaultCommand, commandHandler, result.innerArgv, currentContext, helpOnly, result.aliases, yargs))
|
|
: this.applyMiddlewareAndGetResult(isDefaultCommand, commandHandler, builderResult.innerArgv, currentContext, helpOnly, builderResult.aliases, yargs);
|
|
}
|
|
applyBuilderUpdateUsageAndParse(isDefaultCommand, commandHandler, yargs, aliases, parentCommands, commandIndex, helpOnly, helpOrVersionSet) {
|
|
const builder = commandHandler.builder;
|
|
let innerYargs = yargs;
|
|
if (isCommandBuilderCallback(builder)) {
|
|
yargs.getInternalMethods().getUsageInstance().freeze();
|
|
const builderOutput = builder(yargs.getInternalMethods().reset(aliases), helpOrVersionSet);
|
|
if (isPromise(builderOutput)) {
|
|
return builderOutput.then(output => {
|
|
innerYargs = isYargsInstance(output) ? output : yargs;
|
|
return this.parseAndUpdateUsage(isDefaultCommand, commandHandler, innerYargs, parentCommands, commandIndex, helpOnly);
|
|
});
|
|
}
|
|
}
|
|
else if (isCommandBuilderOptionDefinitions(builder)) {
|
|
yargs.getInternalMethods().getUsageInstance().freeze();
|
|
innerYargs = yargs.getInternalMethods().reset(aliases);
|
|
Object.keys(commandHandler.builder).forEach(key => {
|
|
innerYargs.option(key, builder[key]);
|
|
});
|
|
}
|
|
return this.parseAndUpdateUsage(isDefaultCommand, commandHandler, innerYargs, parentCommands, commandIndex, helpOnly);
|
|
}
|
|
parseAndUpdateUsage(isDefaultCommand, commandHandler, innerYargs, parentCommands, commandIndex, helpOnly) {
|
|
if (isDefaultCommand)
|
|
innerYargs.getInternalMethods().getUsageInstance().unfreeze(true);
|
|
if (this.shouldUpdateUsage(innerYargs)) {
|
|
innerYargs
|
|
.getInternalMethods()
|
|
.getUsageInstance()
|
|
.usage(this.usageFromParentCommandsCommandHandler(parentCommands, commandHandler), commandHandler.description);
|
|
}
|
|
const innerArgv = innerYargs
|
|
.getInternalMethods()
|
|
.runYargsParserAndExecuteCommands(null, undefined, true, commandIndex, helpOnly);
|
|
return isPromise(innerArgv)
|
|
? innerArgv.then(argv => ({
|
|
aliases: innerYargs.parsed.aliases,
|
|
innerArgv: argv,
|
|
}))
|
|
: {
|
|
aliases: innerYargs.parsed.aliases,
|
|
innerArgv: innerArgv,
|
|
};
|
|
}
|
|
shouldUpdateUsage(yargs) {
|
|
return (!yargs.getInternalMethods().getUsageInstance().getUsageDisabled() &&
|
|
yargs.getInternalMethods().getUsageInstance().getUsage().length === 0);
|
|
}
|
|
usageFromParentCommandsCommandHandler(parentCommands, commandHandler) {
|
|
const c = DEFAULT_MARKER.test(commandHandler.original)
|
|
? commandHandler.original.replace(DEFAULT_MARKER, '').trim()
|
|
: commandHandler.original;
|
|
const pc = parentCommands.filter(c => {
|
|
return !DEFAULT_MARKER.test(c);
|
|
});
|
|
pc.push(c);
|
|
return `$0 ${pc.join(' ')}`;
|
|
}
|
|
handleValidationAndGetResult(isDefaultCommand, commandHandler, innerArgv, currentContext, aliases, yargs, middlewares, positionalMap) {
|
|
if (!yargs.getInternalMethods().getHasOutput()) {
|
|
const validation = yargs
|
|
.getInternalMethods()
|
|
.runValidation(aliases, positionalMap, yargs.parsed.error, isDefaultCommand);
|
|
innerArgv = maybeAsyncResult(innerArgv, result => {
|
|
validation(result);
|
|
return result;
|
|
});
|
|
}
|
|
if (commandHandler.handler && !yargs.getInternalMethods().getHasOutput()) {
|
|
yargs.getInternalMethods().setHasOutput();
|
|
const populateDoubleDash = !!yargs.getOptions().configuration['populate--'];
|
|
yargs
|
|
.getInternalMethods()
|
|
.postProcess(innerArgv, populateDoubleDash, false, false);
|
|
innerArgv = applyMiddleware(innerArgv, yargs, middlewares, false);
|
|
innerArgv = maybeAsyncResult(innerArgv, result => {
|
|
const handlerResult = commandHandler.handler(result);
|
|
return isPromise(handlerResult)
|
|
? handlerResult.then(() => result)
|
|
: result;
|
|
});
|
|
if (!isDefaultCommand) {
|
|
yargs.getInternalMethods().getUsageInstance().cacheHelpMessage();
|
|
}
|
|
if (isPromise(innerArgv) &&
|
|
!yargs.getInternalMethods().hasParseCallback()) {
|
|
innerArgv.catch(error => {
|
|
try {
|
|
yargs.getInternalMethods().getUsageInstance().fail(null, error);
|
|
}
|
|
catch (_err) {
|
|
}
|
|
});
|
|
}
|
|
}
|
|
if (!isDefaultCommand) {
|
|
currentContext.commands.pop();
|
|
currentContext.fullCommands.pop();
|
|
}
|
|
return innerArgv;
|
|
}
|
|
applyMiddlewareAndGetResult(isDefaultCommand, commandHandler, innerArgv, currentContext, helpOnly, aliases, yargs) {
|
|
let positionalMap = {};
|
|
if (helpOnly)
|
|
return innerArgv;
|
|
if (!yargs.getInternalMethods().getHasOutput()) {
|
|
positionalMap = this.populatePositionals(commandHandler, innerArgv, currentContext, yargs);
|
|
}
|
|
const middlewares = this.globalMiddleware
|
|
.getMiddleware()
|
|
.slice(0)
|
|
.concat(commandHandler.middlewares);
|
|
const maybePromiseArgv = applyMiddleware(innerArgv, yargs, middlewares, true);
|
|
return isPromise(maybePromiseArgv)
|
|
? maybePromiseArgv.then(resolvedInnerArgv => this.handleValidationAndGetResult(isDefaultCommand, commandHandler, resolvedInnerArgv, currentContext, aliases, yargs, middlewares, positionalMap))
|
|
: this.handleValidationAndGetResult(isDefaultCommand, commandHandler, maybePromiseArgv, currentContext, aliases, yargs, middlewares, positionalMap);
|
|
}
|
|
populatePositionals(commandHandler, argv, context, yargs) {
|
|
argv._ = argv._.slice(context.commands.length);
|
|
const demanded = commandHandler.demanded.slice(0);
|
|
const optional = commandHandler.optional.slice(0);
|
|
const positionalMap = {};
|
|
this.validation.positionalCount(demanded.length, argv._.length);
|
|
while (demanded.length) {
|
|
const demand = demanded.shift();
|
|
this.populatePositional(demand, argv, positionalMap);
|
|
}
|
|
while (optional.length) {
|
|
const maybe = optional.shift();
|
|
this.populatePositional(maybe, argv, positionalMap);
|
|
}
|
|
argv._ = context.commands.concat(argv._.map(a => '' + a));
|
|
this.postProcessPositionals(argv, positionalMap, this.cmdToParseOptions(commandHandler.original), yargs);
|
|
return positionalMap;
|
|
}
|
|
populatePositional(positional, argv, positionalMap) {
|
|
const cmd = positional.cmd[0];
|
|
if (positional.variadic) {
|
|
positionalMap[cmd] = argv._.splice(0).map(String);
|
|
}
|
|
else {
|
|
if (argv._.length)
|
|
positionalMap[cmd] = [String(argv._.shift())];
|
|
}
|
|
}
|
|
cmdToParseOptions(cmdString) {
|
|
const parseOptions = {
|
|
array: [],
|
|
default: {},
|
|
alias: {},
|
|
demand: {},
|
|
};
|
|
const parsed = parseCommand(cmdString);
|
|
parsed.demanded.forEach(d => {
|
|
const [cmd, ...aliases] = d.cmd;
|
|
if (d.variadic) {
|
|
parseOptions.array.push(cmd);
|
|
parseOptions.default[cmd] = [];
|
|
}
|
|
parseOptions.alias[cmd] = aliases;
|
|
parseOptions.demand[cmd] = true;
|
|
});
|
|
parsed.optional.forEach(o => {
|
|
const [cmd, ...aliases] = o.cmd;
|
|
if (o.variadic) {
|
|
parseOptions.array.push(cmd);
|
|
parseOptions.default[cmd] = [];
|
|
}
|
|
parseOptions.alias[cmd] = aliases;
|
|
});
|
|
return parseOptions;
|
|
}
|
|
postProcessPositionals(argv, positionalMap, parseOptions, yargs) {
|
|
const options = Object.assign({}, yargs.getOptions());
|
|
options.default = Object.assign(parseOptions.default, options.default);
|
|
for (const key of Object.keys(parseOptions.alias)) {
|
|
options.alias[key] = (options.alias[key] || []).concat(parseOptions.alias[key]);
|
|
}
|
|
options.array = options.array.concat(parseOptions.array);
|
|
options.config = {};
|
|
const unparsed = [];
|
|
Object.keys(positionalMap).forEach(key => {
|
|
positionalMap[key].map(value => {
|
|
if (options.configuration['unknown-options-as-args'])
|
|
options.key[key] = true;
|
|
unparsed.push(`--${key}`);
|
|
unparsed.push(value);
|
|
});
|
|
});
|
|
if (!unparsed.length)
|
|
return;
|
|
const config = Object.assign({}, options.configuration, {
|
|
'populate--': false,
|
|
});
|
|
const parsed = this.shim.Parser.detailed(unparsed, Object.assign({}, options, {
|
|
configuration: config,
|
|
}));
|
|
if (parsed.error) {
|
|
yargs
|
|
.getInternalMethods()
|
|
.getUsageInstance()
|
|
.fail(parsed.error.message, parsed.error);
|
|
}
|
|
else {
|
|
const positionalKeys = Object.keys(positionalMap);
|
|
Object.keys(positionalMap).forEach(key => {
|
|
positionalKeys.push(...parsed.aliases[key]);
|
|
});
|
|
Object.keys(parsed.argv).forEach(key => {
|
|
if (positionalKeys.includes(key)) {
|
|
if (!positionalMap[key])
|
|
positionalMap[key] = parsed.argv[key];
|
|
if (!this.isInConfigs(yargs, key) &&
|
|
!this.isDefaulted(yargs, key) &&
|
|
Object.prototype.hasOwnProperty.call(argv, key) &&
|
|
Object.prototype.hasOwnProperty.call(parsed.argv, key) &&
|
|
(Array.isArray(argv[key]) || Array.isArray(parsed.argv[key]))) {
|
|
argv[key] = [].concat(argv[key], parsed.argv[key]);
|
|
}
|
|
else {
|
|
argv[key] = parsed.argv[key];
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
isDefaulted(yargs, key) {
|
|
const { default: defaults } = yargs.getOptions();
|
|
return (Object.prototype.hasOwnProperty.call(defaults, key) ||
|
|
Object.prototype.hasOwnProperty.call(defaults, this.shim.Parser.camelCase(key)));
|
|
}
|
|
isInConfigs(yargs, key) {
|
|
const { configObjects } = yargs.getOptions();
|
|
return (configObjects.some(c => Object.prototype.hasOwnProperty.call(c, key)) ||
|
|
configObjects.some(c => Object.prototype.hasOwnProperty.call(c, this.shim.Parser.camelCase(key))));
|
|
}
|
|
runDefaultBuilderOn(yargs) {
|
|
if (!this.defaultCommand)
|
|
return;
|
|
if (this.shouldUpdateUsage(yargs)) {
|
|
const commandString = DEFAULT_MARKER.test(this.defaultCommand.original)
|
|
? this.defaultCommand.original
|
|
: this.defaultCommand.original.replace(/^[^[\]<>]*/, '$0 ');
|
|
yargs
|
|
.getInternalMethods()
|
|
.getUsageInstance()
|
|
.usage(commandString, this.defaultCommand.description);
|
|
}
|
|
const builder = this.defaultCommand.builder;
|
|
if (isCommandBuilderCallback(builder)) {
|
|
return builder(yargs, true);
|
|
}
|
|
else if (!isCommandBuilderDefinition(builder)) {
|
|
Object.keys(builder).forEach(key => {
|
|
yargs.option(key, builder[key]);
|
|
});
|
|
}
|
|
return undefined;
|
|
}
|
|
moduleName(obj) {
|
|
const mod = whichModule(obj);
|
|
if (!mod)
|
|
throw new Error(`No command name given for module: ${this.shim.inspect(obj)}`);
|
|
return this.commandFromFilename(mod.filename);
|
|
}
|
|
commandFromFilename(filename) {
|
|
return this.shim.path.basename(filename, this.shim.path.extname(filename));
|
|
}
|
|
extractDesc({ describe, description, desc }) {
|
|
for (const test of [describe, description, desc]) {
|
|
if (typeof test === 'string' || test === false)
|
|
return test;
|
|
assertNotStrictEqual(test, true, this.shim);
|
|
}
|
|
return false;
|
|
}
|
|
freeze() {
|
|
this.frozens.push({
|
|
handlers: this.handlers,
|
|
aliasMap: this.aliasMap,
|
|
defaultCommand: this.defaultCommand,
|
|
});
|
|
}
|
|
unfreeze() {
|
|
const frozen = this.frozens.pop();
|
|
assertNotStrictEqual(frozen, undefined, this.shim);
|
|
({
|
|
handlers: this.handlers,
|
|
aliasMap: this.aliasMap,
|
|
defaultCommand: this.defaultCommand,
|
|
} = frozen);
|
|
}
|
|
reset() {
|
|
this.handlers = {};
|
|
this.aliasMap = {};
|
|
this.defaultCommand = undefined;
|
|
this.requireCache = new Set();
|
|
return this;
|
|
}
|
|
}
|
|
export function command(usage, validation, globalMiddleware, shim) {
|
|
return new CommandInstance(usage, validation, globalMiddleware, shim);
|
|
}
|
|
export function isCommandBuilderDefinition(builder) {
|
|
return (typeof builder === 'object' &&
|
|
!!builder.builder &&
|
|
typeof builder.handler === 'function');
|
|
}
|
|
function isCommandAndAliases(cmd) {
|
|
return cmd.every(c => typeof c === 'string');
|
|
}
|
|
export function isCommandBuilderCallback(builder) {
|
|
return typeof builder === 'function';
|
|
}
|
|
function isCommandBuilderOptionDefinitions(builder) {
|
|
return typeof builder === 'object';
|
|
}
|
|
export function isCommandHandlerDefinition(cmd) {
|
|
return typeof cmd === 'object' && !Array.isArray(cmd);
|
|
}
|