UNPKG

@repka-kit/ts

Version:

Next generation build tools for monorepo: lint, bundle and package your TypeScript projects

1,686 lines (1,490 loc) 1.22 MB
import { g as getDefaultExportFromCjs, e as bold, f as bgBlack, w as white, r as red, p as pipeline, b as buildForNode, h as blue, a as declarations, $, j as line, k as ensureTsConfigExists, m as green, i as integrationTest, y as yellow, l as lint, u as unitTest } from './chunk.6XFfMluC.js'; import require$$0 from 'node:events'; import require$$1, { spawn } from 'node:child_process'; import require$$2, { dirname, join, resolve, basename, extname, relative } from 'node:path'; import require$$3 from 'node:fs'; import require$$4 from 'node:process'; import { readdir, stat, readFile, writeFile, mkdir } from 'node:fs/promises'; import { l as logger, s as spawnToPromise } from './chunk.2EX25_CG.js'; import 'node:assert'; import { b as binPath, m as moduleRootDirectory } from './chunk.gqGVJwT_.js'; import fg from 'fast-glob'; import { a as spawnOutput, b as spawnResult, s as spawnOutputConditional } from './chunk.qmcfxrpG.js'; import 'node:url'; import { r as readMonorepoPackagesGlobs, b as readPackageJson, o as ourPackageJson, c as eslint$1, d as ensureEslintRootConfigExists, f as ensureEslintTsConfigExists } from './chunk.7OqoG9sS.js'; import { dump } from 'js-yaml'; import dedent from 'dedent'; import prompts from 'prompts'; import '@rollup/plugin-virtual'; import './chunk.PjcLlUr0.js'; import '@rollup/pluginutils'; import 'magic-string'; import 'rollup'; import 'rollup-plugin-generate-package-json'; import '@rollup/plugin-commonjs'; import '@rollup/plugin-json'; import '@rollup/plugin-node-resolve'; import 'rollup-plugin-analyzer'; import 'esbuild'; import './chunk.dnsVc6Jo.js'; import './chunk.Ide2Qyoh.js'; import './chunk.gSglsk4Z.js'; import './chunk.1WcKPfKf.js'; import 'node:perf_hooks'; function filterTruthy(array) { return array.filter(Boolean); } function memoizeFunction(functionName, opts) { const resultByArgs = opts.cache ?? /* @__PURE__ */ new Map(); const pendingPromises = /* @__PURE__ */ new Map(); const cachedFn = async (...args) => { const key = opts.keyFromArgs(...args); const existing = resultByArgs.get(key); if (existing) { return existing; } const pending = pendingPromises.get(key); if (pending) { return pending; } try { const dataPromise = opts.memoizeFn(...args); pendingPromises.set(key, dataPromise); const data = await dataPromise; resultByArgs.set(key, data); return data; } finally { pendingPromises.delete(key); } }; const result = { [functionName]: cachedFn, entries: () => resultByArgs.entries(), isPending: (...args) => { const key = opts.keyFromArgs(...args); return pendingPromises.has(key); }, has: (...args) => { const key = opts.keyFromArgs(...args); return resultByArgs.has(key); }, get: (...args) => { const key = opts.keyFromArgs(...args); return resultByArgs.get(key); }, set: (...args) => ({ value: (value) => { const key = opts.keyFromArgs(...args); resultByArgs.set(key, value); } }) }; return result; } class UnreachableError extends Error { constructor(value) { super(`${String(value)} is not handled`); Error.captureStackTrace(this, UnreachableError); this.name = "UnreachableError"; } } var commander$1 = {exports: {}}; var argument = {}; var error = {}; /** * CommanderError class * @class */ let CommanderError$2 = class CommanderError extends Error { /** * Constructs the CommanderError class * @param {number} exitCode suggested exit code which could be used with process.exit * @param {string} code an id string representing the error * @param {string} message human-readable description of the error * @constructor */ constructor(exitCode, code, message) { super(message); // properly capture stack trace in Node.js Error.captureStackTrace(this, this.constructor); this.name = this.constructor.name; this.code = code; this.exitCode = exitCode; this.nestedError = undefined; } }; /** * InvalidArgumentError class * @class */ let InvalidArgumentError$3 = class InvalidArgumentError extends CommanderError$2 { /** * Constructs the InvalidArgumentError class * @param {string} [message] explanation of why argument is invalid * @constructor */ constructor(message) { super(1, 'commander.invalidArgument', message); // properly capture stack trace in Node.js Error.captureStackTrace(this, this.constructor); this.name = this.constructor.name; } }; error.CommanderError = CommanderError$2; error.InvalidArgumentError = InvalidArgumentError$3; const { InvalidArgumentError: InvalidArgumentError$2 } = error; let Argument$2 = class Argument { /** * Initialize a new command argument with the given name and description. * The default is that the argument is required, and you can explicitly * indicate this with <> around the name. Put [] around the name for an optional argument. * * @param {string} name * @param {string} [description] */ constructor(name, description) { this.description = description || ''; this.variadic = false; this.parseArg = undefined; this.defaultValue = undefined; this.defaultValueDescription = undefined; this.argChoices = undefined; switch (name[0]) { case '<': // e.g. <required> this.required = true; this._name = name.slice(1, -1); break; case '[': // e.g. [optional] this.required = false; this._name = name.slice(1, -1); break; default: this.required = true; this._name = name; break; } if (this._name.length > 3 && this._name.slice(-3) === '...') { this.variadic = true; this._name = this._name.slice(0, -3); } } /** * Return argument name. * * @return {string} */ name() { return this._name; } /** * @api private */ _concatValue(value, previous) { if (previous === this.defaultValue || !Array.isArray(previous)) { return [value]; } return previous.concat(value); } /** * Set the default value, and optionally supply the description to be displayed in the help. * * @param {*} value * @param {string} [description] * @return {Argument} */ default(value, description) { this.defaultValue = value; this.defaultValueDescription = description; return this; } /** * Set the custom handler for processing CLI command arguments into argument values. * * @param {Function} [fn] * @return {Argument} */ argParser(fn) { this.parseArg = fn; return this; } /** * Only allow argument value to be one of choices. * * @param {string[]} values * @return {Argument} */ choices(values) { this.argChoices = values.slice(); this.parseArg = (arg, previous) => { if (!this.argChoices.includes(arg)) { throw new InvalidArgumentError$2(`Allowed choices are ${this.argChoices.join(', ')}.`); } if (this.variadic) { return this._concatValue(arg, previous); } return arg; }; return this; } /** * Make argument required. */ argRequired() { this.required = true; return this; } /** * Make argument optional. */ argOptional() { this.required = false; return this; } }; /** * Takes an argument and returns its human readable equivalent for help usage. * * @param {Argument} arg * @return {string} * @api private */ function humanReadableArgName$2(arg) { const nameOutput = arg.name() + (arg.variadic === true ? '...' : ''); return arg.required ? '<' + nameOutput + '>' : '[' + nameOutput + ']'; } argument.Argument = Argument$2; argument.humanReadableArgName = humanReadableArgName$2; var command = {}; var help = {}; const { humanReadableArgName: humanReadableArgName$1 } = argument; /** * TypeScript import types for JSDoc, used by Visual Studio Code IntelliSense and `npm run typescript-checkJS` * https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#import-types * @typedef { import("./argument.js").Argument } Argument * @typedef { import("./command.js").Command } Command * @typedef { import("./option.js").Option } Option */ // Although this is a class, methods are static in style to allow override using subclass or just functions. let Help$2 = class Help { constructor() { this.helpWidth = undefined; this.sortSubcommands = false; this.sortOptions = false; this.showGlobalOptions = false; } /** * Get an array of the visible subcommands. Includes a placeholder for the implicit help command, if there is one. * * @param {Command} cmd * @returns {Command[]} */ visibleCommands(cmd) { const visibleCommands = cmd.commands.filter(cmd => !cmd._hidden); if (cmd._hasImplicitHelpCommand()) { // Create a command matching the implicit help command. const [, helpName, helpArgs] = cmd._helpCommandnameAndArgs.match(/([^ ]+) *(.*)/); const helpCommand = cmd.createCommand(helpName) .helpOption(false); helpCommand.description(cmd._helpCommandDescription); if (helpArgs) helpCommand.arguments(helpArgs); visibleCommands.push(helpCommand); } if (this.sortSubcommands) { visibleCommands.sort((a, b) => { // @ts-ignore: overloaded return type return a.name().localeCompare(b.name()); }); } return visibleCommands; } /** * Compare options for sort. * * @param {Option} a * @param {Option} b * @returns number */ compareOptions(a, b) { const getSortKey = (option) => { // WYSIWYG for order displayed in help. Short used for comparison if present. No special handling for negated. return option.short ? option.short.replace(/^-/, '') : option.long.replace(/^--/, ''); }; return getSortKey(a).localeCompare(getSortKey(b)); } /** * Get an array of the visible options. Includes a placeholder for the implicit help option, if there is one. * * @param {Command} cmd * @returns {Option[]} */ visibleOptions(cmd) { const visibleOptions = cmd.options.filter((option) => !option.hidden); // Implicit help const showShortHelpFlag = cmd._hasHelpOption && cmd._helpShortFlag && !cmd._findOption(cmd._helpShortFlag); const showLongHelpFlag = cmd._hasHelpOption && !cmd._findOption(cmd._helpLongFlag); if (showShortHelpFlag || showLongHelpFlag) { let helpOption; if (!showShortHelpFlag) { helpOption = cmd.createOption(cmd._helpLongFlag, cmd._helpDescription); } else if (!showLongHelpFlag) { helpOption = cmd.createOption(cmd._helpShortFlag, cmd._helpDescription); } else { helpOption = cmd.createOption(cmd._helpFlags, cmd._helpDescription); } visibleOptions.push(helpOption); } if (this.sortOptions) { visibleOptions.sort(this.compareOptions); } return visibleOptions; } /** * Get an array of the visible global options. (Not including help.) * * @param {Command} cmd * @returns {Option[]} */ visibleGlobalOptions(cmd) { if (!this.showGlobalOptions) return []; const globalOptions = []; for (let ancestorCmd = cmd.parent; ancestorCmd; ancestorCmd = ancestorCmd.parent) { const visibleOptions = ancestorCmd.options.filter((option) => !option.hidden); globalOptions.push(...visibleOptions); } if (this.sortOptions) { globalOptions.sort(this.compareOptions); } return globalOptions; } /** * Get an array of the arguments if any have a description. * * @param {Command} cmd * @returns {Argument[]} */ visibleArguments(cmd) { // Side effect! Apply the legacy descriptions before the arguments are displayed. if (cmd._argsDescription) { cmd.registeredArguments.forEach(argument => { argument.description = argument.description || cmd._argsDescription[argument.name()] || ''; }); } // If there are any arguments with a description then return all the arguments. if (cmd.registeredArguments.find(argument => argument.description)) { return cmd.registeredArguments; } return []; } /** * Get the command term to show in the list of subcommands. * * @param {Command} cmd * @returns {string} */ subcommandTerm(cmd) { // Legacy. Ignores custom usage string, and nested commands. const args = cmd.registeredArguments.map(arg => humanReadableArgName$1(arg)).join(' '); return cmd._name + (cmd._aliases[0] ? '|' + cmd._aliases[0] : '') + (cmd.options.length ? ' [options]' : '') + // simplistic check for non-help option (args ? ' ' + args : ''); } /** * Get the option term to show in the list of options. * * @param {Option} option * @returns {string} */ optionTerm(option) { return option.flags; } /** * Get the argument term to show in the list of arguments. * * @param {Argument} argument * @returns {string} */ argumentTerm(argument) { return argument.name(); } /** * Get the longest command term length. * * @param {Command} cmd * @param {Help} helper * @returns {number} */ longestSubcommandTermLength(cmd, helper) { return helper.visibleCommands(cmd).reduce((max, command) => { return Math.max(max, helper.subcommandTerm(command).length); }, 0); } /** * Get the longest option term length. * * @param {Command} cmd * @param {Help} helper * @returns {number} */ longestOptionTermLength(cmd, helper) { return helper.visibleOptions(cmd).reduce((max, option) => { return Math.max(max, helper.optionTerm(option).length); }, 0); } /** * Get the longest global option term length. * * @param {Command} cmd * @param {Help} helper * @returns {number} */ longestGlobalOptionTermLength(cmd, helper) { return helper.visibleGlobalOptions(cmd).reduce((max, option) => { return Math.max(max, helper.optionTerm(option).length); }, 0); } /** * Get the longest argument term length. * * @param {Command} cmd * @param {Help} helper * @returns {number} */ longestArgumentTermLength(cmd, helper) { return helper.visibleArguments(cmd).reduce((max, argument) => { return Math.max(max, helper.argumentTerm(argument).length); }, 0); } /** * Get the command usage to be displayed at the top of the built-in help. * * @param {Command} cmd * @returns {string} */ commandUsage(cmd) { // Usage let cmdName = cmd._name; if (cmd._aliases[0]) { cmdName = cmdName + '|' + cmd._aliases[0]; } let ancestorCmdNames = ''; for (let ancestorCmd = cmd.parent; ancestorCmd; ancestorCmd = ancestorCmd.parent) { ancestorCmdNames = ancestorCmd.name() + ' ' + ancestorCmdNames; } return ancestorCmdNames + cmdName + ' ' + cmd.usage(); } /** * Get the description for the command. * * @param {Command} cmd * @returns {string} */ commandDescription(cmd) { // @ts-ignore: overloaded return type return cmd.description(); } /** * Get the subcommand summary to show in the list of subcommands. * (Fallback to description for backwards compatibility.) * * @param {Command} cmd * @returns {string} */ subcommandDescription(cmd) { // @ts-ignore: overloaded return type return cmd.summary() || cmd.description(); } /** * Get the option description to show in the list of options. * * @param {Option} option * @return {string} */ optionDescription(option) { const extraInfo = []; if (option.argChoices) { extraInfo.push( // use stringify to match the display of the default value `choices: ${option.argChoices.map((choice) => JSON.stringify(choice)).join(', ')}`); } if (option.defaultValue !== undefined) { // default for boolean and negated more for programmer than end user, // but show true/false for boolean option as may be for hand-rolled env or config processing. const showDefault = option.required || option.optional || (option.isBoolean() && typeof option.defaultValue === 'boolean'); if (showDefault) { extraInfo.push(`default: ${option.defaultValueDescription || JSON.stringify(option.defaultValue)}`); } } // preset for boolean and negated are more for programmer than end user if (option.presetArg !== undefined && option.optional) { extraInfo.push(`preset: ${JSON.stringify(option.presetArg)}`); } if (option.envVar !== undefined) { extraInfo.push(`env: ${option.envVar}`); } if (extraInfo.length > 0) { return `${option.description} (${extraInfo.join(', ')})`; } return option.description; } /** * Get the argument description to show in the list of arguments. * * @param {Argument} argument * @return {string} */ argumentDescription(argument) { const extraInfo = []; if (argument.argChoices) { extraInfo.push( // use stringify to match the display of the default value `choices: ${argument.argChoices.map((choice) => JSON.stringify(choice)).join(', ')}`); } if (argument.defaultValue !== undefined) { extraInfo.push(`default: ${argument.defaultValueDescription || JSON.stringify(argument.defaultValue)}`); } if (extraInfo.length > 0) { const extraDescripton = `(${extraInfo.join(', ')})`; if (argument.description) { return `${argument.description} ${extraDescripton}`; } return extraDescripton; } return argument.description; } /** * Generate the built-in help text. * * @param {Command} cmd * @param {Help} helper * @returns {string} */ formatHelp(cmd, helper) { const termWidth = helper.padWidth(cmd, helper); const helpWidth = helper.helpWidth || 80; const itemIndentWidth = 2; const itemSeparatorWidth = 2; // between term and description function formatItem(term, description) { if (description) { const fullText = `${term.padEnd(termWidth + itemSeparatorWidth)}${description}`; return helper.wrap(fullText, helpWidth - itemIndentWidth, termWidth + itemSeparatorWidth); } return term; } function formatList(textArray) { return textArray.join('\n').replace(/^/gm, ' '.repeat(itemIndentWidth)); } // Usage let output = [`Usage: ${helper.commandUsage(cmd)}`, '']; // Description const commandDescription = helper.commandDescription(cmd); if (commandDescription.length > 0) { output = output.concat([helper.wrap(commandDescription, helpWidth, 0), '']); } // Arguments const argumentList = helper.visibleArguments(cmd).map((argument) => { return formatItem(helper.argumentTerm(argument), helper.argumentDescription(argument)); }); if (argumentList.length > 0) { output = output.concat(['Arguments:', formatList(argumentList), '']); } // Options const optionList = helper.visibleOptions(cmd).map((option) => { return formatItem(helper.optionTerm(option), helper.optionDescription(option)); }); if (optionList.length > 0) { output = output.concat(['Options:', formatList(optionList), '']); } if (this.showGlobalOptions) { const globalOptionList = helper.visibleGlobalOptions(cmd).map((option) => { return formatItem(helper.optionTerm(option), helper.optionDescription(option)); }); if (globalOptionList.length > 0) { output = output.concat(['Global Options:', formatList(globalOptionList), '']); } } // Commands const commandList = helper.visibleCommands(cmd).map((cmd) => { return formatItem(helper.subcommandTerm(cmd), helper.subcommandDescription(cmd)); }); if (commandList.length > 0) { output = output.concat(['Commands:', formatList(commandList), '']); } return output.join('\n'); } /** * Calculate the pad width from the maximum term length. * * @param {Command} cmd * @param {Help} helper * @returns {number} */ padWidth(cmd, helper) { return Math.max( helper.longestOptionTermLength(cmd, helper), helper.longestGlobalOptionTermLength(cmd, helper), helper.longestSubcommandTermLength(cmd, helper), helper.longestArgumentTermLength(cmd, helper) ); } /** * Wrap the given string to width characters per line, with lines after the first indented. * Do not wrap if insufficient room for wrapping (minColumnWidth), or string is manually formatted. * * @param {string} str * @param {number} width * @param {number} indent * @param {number} [minColumnWidth=40] * @return {string} * */ wrap(str, width, indent, minColumnWidth = 40) { // Full \s characters, minus the linefeeds. const indents = ' \\f\\t\\v\u00a0\u1680\u2000-\u200a\u202f\u205f\u3000\ufeff'; // Detect manually wrapped and indented strings by searching for line break followed by spaces. const manualIndent = new RegExp(`[\\n][${indents}]+`); if (str.match(manualIndent)) return str; // Do not wrap if not enough room for a wrapped column of text (as could end up with a word per line). const columnWidth = width - indent; if (columnWidth < minColumnWidth) return str; const leadingStr = str.slice(0, indent); const columnText = str.slice(indent).replace('\r\n', '\n'); const indentString = ' '.repeat(indent); const zeroWidthSpace = '\u200B'; const breaks = `\\s${zeroWidthSpace}`; // Match line end (so empty lines don't collapse), // or as much text as will fit in column, or excess text up to first break. const regex = new RegExp(`\n|.{1,${columnWidth - 1}}([${breaks}]|$)|[^${breaks}]+?([${breaks}]|$)`, 'g'); const lines = columnText.match(regex) || []; return leadingStr + lines.map((line, i) => { if (line === '\n') return ''; // preserve empty lines return ((i > 0) ? indentString : '') + line.trimEnd(); }).join('\n'); } }; help.Help = Help$2; var option = {}; const { InvalidArgumentError: InvalidArgumentError$1 } = error; let Option$2 = class Option { /** * Initialize a new `Option` with the given `flags` and `description`. * * @param {string} flags * @param {string} [description] */ constructor(flags, description) { this.flags = flags; this.description = description || ''; this.required = flags.includes('<'); // A value must be supplied when the option is specified. this.optional = flags.includes('['); // A value is optional when the option is specified. // variadic test ignores <value,...> et al which might be used to describe custom splitting of single argument this.variadic = /\w\.\.\.[>\]]$/.test(flags); // The option can take multiple values. this.mandatory = false; // The option must have a value after parsing, which usually means it must be specified on command line. const optionFlags = splitOptionFlags$1(flags); this.short = optionFlags.shortFlag; this.long = optionFlags.longFlag; this.negate = false; if (this.long) { this.negate = this.long.startsWith('--no-'); } this.defaultValue = undefined; this.defaultValueDescription = undefined; this.presetArg = undefined; this.envVar = undefined; this.parseArg = undefined; this.hidden = false; this.argChoices = undefined; this.conflictsWith = []; this.implied = undefined; } /** * Set the default value, and optionally supply the description to be displayed in the help. * * @param {*} value * @param {string} [description] * @return {Option} */ default(value, description) { this.defaultValue = value; this.defaultValueDescription = description; return this; } /** * Preset to use when option used without option-argument, especially optional but also boolean and negated. * The custom processing (parseArg) is called. * * @example * new Option('--color').default('GREYSCALE').preset('RGB'); * new Option('--donate [amount]').preset('20').argParser(parseFloat); * * @param {*} arg * @return {Option} */ preset(arg) { this.presetArg = arg; return this; } /** * Add option name(s) that conflict with this option. * An error will be displayed if conflicting options are found during parsing. * * @example * new Option('--rgb').conflicts('cmyk'); * new Option('--js').conflicts(['ts', 'jsx']); * * @param {string | string[]} names * @return {Option} */ conflicts(names) { this.conflictsWith = this.conflictsWith.concat(names); return this; } /** * Specify implied option values for when this option is set and the implied options are not. * * The custom processing (parseArg) is not called on the implied values. * * @example * program * .addOption(new Option('--log', 'write logging information to file')) * .addOption(new Option('--trace', 'log extra details').implies({ log: 'trace.txt' })); * * @param {Object} impliedOptionValues * @return {Option} */ implies(impliedOptionValues) { let newImplied = impliedOptionValues; if (typeof impliedOptionValues === 'string') { // string is not documented, but easy mistake and we can do what user probably intended. newImplied = { [impliedOptionValues]: true }; } this.implied = Object.assign(this.implied || {}, newImplied); return this; } /** * Set environment variable to check for option value. * * An environment variable is only used if when processed the current option value is * undefined, or the source of the current value is 'default' or 'config' or 'env'. * * @param {string} name * @return {Option} */ env(name) { this.envVar = name; return this; } /** * Set the custom handler for processing CLI option arguments into option values. * * @param {Function} [fn] * @return {Option} */ argParser(fn) { this.parseArg = fn; return this; } /** * Whether the option is mandatory and must have a value after parsing. * * @param {boolean} [mandatory=true] * @return {Option} */ makeOptionMandatory(mandatory = true) { this.mandatory = !!mandatory; return this; } /** * Hide option in help. * * @param {boolean} [hide=true] * @return {Option} */ hideHelp(hide = true) { this.hidden = !!hide; return this; } /** * @api private */ _concatValue(value, previous) { if (previous === this.defaultValue || !Array.isArray(previous)) { return [value]; } return previous.concat(value); } /** * Only allow option value to be one of choices. * * @param {string[]} values * @return {Option} */ choices(values) { this.argChoices = values.slice(); this.parseArg = (arg, previous) => { if (!this.argChoices.includes(arg)) { throw new InvalidArgumentError$1(`Allowed choices are ${this.argChoices.join(', ')}.`); } if (this.variadic) { return this._concatValue(arg, previous); } return arg; }; return this; } /** * Return option name. * * @return {string} */ name() { if (this.long) { return this.long.replace(/^--/, ''); } return this.short.replace(/^-/, ''); } /** * Return option name, in a camelcase format that can be used * as a object attribute key. * * @return {string} * @api private */ attributeName() { return camelcase(this.name().replace(/^no-/, '')); } /** * Check if `arg` matches the short or long flag. * * @param {string} arg * @return {boolean} * @api private */ is(arg) { return this.short === arg || this.long === arg; } /** * Return whether a boolean option. * * Options are one of boolean, negated, required argument, or optional argument. * * @return {boolean} * @api private */ isBoolean() { return !this.required && !this.optional && !this.negate; } }; /** * This class is to make it easier to work with dual options, without changing the existing * implementation. We support separate dual options for separate positive and negative options, * like `--build` and `--no-build`, which share a single option value. This works nicely for some * use cases, but is tricky for others where we want separate behaviours despite * the single shared option value. */ let DualOptions$1 = class DualOptions { /** * @param {Option[]} options */ constructor(options) { this.positiveOptions = new Map(); this.negativeOptions = new Map(); this.dualOptions = new Set(); options.forEach(option => { if (option.negate) { this.negativeOptions.set(option.attributeName(), option); } else { this.positiveOptions.set(option.attributeName(), option); } }); this.negativeOptions.forEach((value, key) => { if (this.positiveOptions.has(key)) { this.dualOptions.add(key); } }); } /** * Did the value come from the option, and not from possible matching dual option? * * @param {*} value * @param {Option} option * @returns {boolean} */ valueFromOption(value, option) { const optionKey = option.attributeName(); if (!this.dualOptions.has(optionKey)) return true; // Use the value to deduce if (probably) came from the option. const preset = this.negativeOptions.get(optionKey).presetArg; const negativeValue = (preset !== undefined) ? preset : false; return option.negate === (negativeValue === value); } }; /** * Convert string from kebab-case to camelCase. * * @param {string} str * @return {string} * @api private */ function camelcase(str) { return str.split('-').reduce((str, word) => { return str + word[0].toUpperCase() + word.slice(1); }); } /** * Split the short and long flag out of something like '-m,--mixed <value>' * * @api private */ function splitOptionFlags$1(flags) { let shortFlag; let longFlag; // Use original very loose parsing to maintain backwards compatibility for now, // which allowed for example unintended `-sw, --short-word` [sic]. const flagParts = flags.split(/[ |,]+/); if (flagParts.length > 1 && !/^[[<]/.test(flagParts[1])) shortFlag = flagParts.shift(); longFlag = flagParts.shift(); // Add support for lone short flag without significantly changing parsing! if (!shortFlag && /^-[^-]$/.test(longFlag)) { shortFlag = longFlag; longFlag = undefined; } return { shortFlag, longFlag }; } option.Option = Option$2; option.splitOptionFlags = splitOptionFlags$1; option.DualOptions = DualOptions$1; var suggestSimilar$2 = {}; const maxDistance = 3; function editDistance(a, b) { // https://en.wikipedia.org/wiki/Damerau–Levenshtein_distance // Calculating optimal string alignment distance, no substring is edited more than once. // (Simple implementation.) // Quick early exit, return worst case. if (Math.abs(a.length - b.length) > maxDistance) return Math.max(a.length, b.length); // distance between prefix substrings of a and b const d = []; // pure deletions turn a into empty string for (let i = 0; i <= a.length; i++) { d[i] = [i]; } // pure insertions turn empty string into b for (let j = 0; j <= b.length; j++) { d[0][j] = j; } // fill matrix for (let j = 1; j <= b.length; j++) { for (let i = 1; i <= a.length; i++) { let cost = 1; if (a[i - 1] === b[j - 1]) { cost = 0; } else { cost = 1; } d[i][j] = Math.min( d[i - 1][j] + 1, // deletion d[i][j - 1] + 1, // insertion d[i - 1][j - 1] + cost // substitution ); // transposition if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) { d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + 1); } } } return d[a.length][b.length]; } /** * Find close matches, restricted to same number of edits. * * @param {string} word * @param {string[]} candidates * @returns {string} */ function suggestSimilar$1(word, candidates) { if (!candidates || candidates.length === 0) return ''; // remove possible duplicates candidates = Array.from(new Set(candidates)); const searchingOptions = word.startsWith('--'); if (searchingOptions) { word = word.slice(2); candidates = candidates.map(candidate => candidate.slice(2)); } let similar = []; let bestDistance = maxDistance; const minSimilarity = 0.4; candidates.forEach((candidate) => { if (candidate.length <= 1) return; // no one character guesses const distance = editDistance(word, candidate); const length = Math.max(word.length, candidate.length); const similarity = (length - distance) / length; if (similarity > minSimilarity) { if (distance < bestDistance) { // better edit distance, throw away previous worse matches bestDistance = distance; similar = [candidate]; } else if (distance === bestDistance) { similar.push(candidate); } } }); similar.sort((a, b) => a.localeCompare(b)); if (searchingOptions) { similar = similar.map(candidate => `--${candidate}`); } if (similar.length > 1) { return `\n(Did you mean one of ${similar.join(', ')}?)`; } if (similar.length === 1) { return `\n(Did you mean ${similar[0]}?)`; } return ''; } suggestSimilar$2.suggestSimilar = suggestSimilar$1; const EventEmitter = require$$0.EventEmitter; const childProcess = require$$1; const path = require$$2; const fs = require$$3; const process$1 = require$$4; const { Argument: Argument$1, humanReadableArgName } = argument; const { CommanderError: CommanderError$1 } = error; const { Help: Help$1 } = help; const { Option: Option$1, splitOptionFlags, DualOptions } = option; const { suggestSimilar } = suggestSimilar$2; let Command$1 = class Command extends EventEmitter { /** * Initialize a new `Command`. * * @param {string} [name] */ constructor(name) { super(); /** @type {Command[]} */ this.commands = []; /** @type {Option[]} */ this.options = []; this.parent = null; this._allowUnknownOption = false; this._allowExcessArguments = true; /** @type {Argument[]} */ this.registeredArguments = []; this._args = this.registeredArguments; // deprecated old name /** @type {string[]} */ this.args = []; // cli args with options removed this.rawArgs = []; this.processedArgs = []; // like .args but after custom processing and collecting variadic this._scriptPath = null; this._name = name || ''; this._optionValues = {}; this._optionValueSources = {}; // default, env, cli etc this._storeOptionsAsProperties = false; this._actionHandler = null; this._executableHandler = false; this._executableFile = null; // custom name for executable this._executableDir = null; // custom search directory for subcommands this._defaultCommandName = null; this._exitCallback = null; this._aliases = []; this._combineFlagAndOptionalValue = true; this._description = ''; this._summary = ''; this._argsDescription = undefined; // legacy this._enablePositionalOptions = false; this._passThroughOptions = false; this._lifeCycleHooks = {}; // a hash of arrays /** @type {boolean | string} */ this._showHelpAfterError = false; this._showSuggestionAfterError = true; // see .configureOutput() for docs this._outputConfiguration = { writeOut: (str) => process$1.stdout.write(str), writeErr: (str) => process$1.stderr.write(str), getOutHelpWidth: () => process$1.stdout.isTTY ? process$1.stdout.columns : undefined, getErrHelpWidth: () => process$1.stderr.isTTY ? process$1.stderr.columns : undefined, outputError: (str, write) => write(str) }; this._hidden = false; this._hasHelpOption = true; this._helpFlags = '-h, --help'; this._helpDescription = 'display help for command'; this._helpShortFlag = '-h'; this._helpLongFlag = '--help'; this._addImplicitHelpCommand = undefined; // Deliberately undefined, not decided whether true or false this._helpCommandName = 'help'; this._helpCommandnameAndArgs = 'help [command]'; this._helpCommandDescription = 'display help for command'; this._helpConfiguration = {}; } /** * Copy settings that are useful to have in common across root command and subcommands. * * (Used internally when adding a command using `.command()` so subcommands inherit parent settings.) * * @param {Command} sourceCommand * @return {Command} `this` command for chaining */ copyInheritedSettings(sourceCommand) { this._outputConfiguration = sourceCommand._outputConfiguration; this._hasHelpOption = sourceCommand._hasHelpOption; this._helpFlags = sourceCommand._helpFlags; this._helpDescription = sourceCommand._helpDescription; this._helpShortFlag = sourceCommand._helpShortFlag; this._helpLongFlag = sourceCommand._helpLongFlag; this._helpCommandName = sourceCommand._helpCommandName; this._helpCommandnameAndArgs = sourceCommand._helpCommandnameAndArgs; this._helpCommandDescription = sourceCommand._helpCommandDescription; this._helpConfiguration = sourceCommand._helpConfiguration; this._exitCallback = sourceCommand._exitCallback; this._storeOptionsAsProperties = sourceCommand._storeOptionsAsProperties; this._combineFlagAndOptionalValue = sourceCommand._combineFlagAndOptionalValue; this._allowExcessArguments = sourceCommand._allowExcessArguments; this._enablePositionalOptions = sourceCommand._enablePositionalOptions; this._showHelpAfterError = sourceCommand._showHelpAfterError; this._showSuggestionAfterError = sourceCommand._showSuggestionAfterError; return this; } /** * @returns {Command[]} * @api private */ _getCommandAndAncestors() { const result = []; for (let command = this; command; command = command.parent) { result.push(command); } return result; } /** * Define a command. * * There are two styles of command: pay attention to where to put the description. * * @example * // Command implemented using action handler (description is supplied separately to `.command`) * program * .command('clone <source> [destination]') * .description('clone a repository into a newly created directory') * .action((source, destination) => { * console.log('clone command called'); * }); * * // Command implemented using separate executable file (description is second parameter to `.command`) * program * .command('start <service>', 'start named service') * .command('stop [service]', 'stop named service, or all if no name supplied'); * * @param {string} nameAndArgs - command name and arguments, args are `<required>` or `[optional]` and last may also be `variadic...` * @param {Object|string} [actionOptsOrExecDesc] - configuration options (for action), or description (for executable) * @param {Object} [execOpts] - configuration options (for executable) * @return {Command} returns new command for action handler, or `this` for executable command */ command(nameAndArgs, actionOptsOrExecDesc, execOpts) { let desc = actionOptsOrExecDesc; let opts = execOpts; if (typeof desc === 'object' && desc !== null) { opts = desc; desc = null; } opts = opts || {}; const [, name, args] = nameAndArgs.match(/([^ ]+) *(.*)/); const cmd = this.createCommand(name); if (desc) { cmd.description(desc); cmd._executableHandler = true; } if (opts.isDefault) this._defaultCommandName = cmd._name; cmd._hidden = !!(opts.noHelp || opts.hidden); // noHelp is deprecated old name for hidden cmd._executableFile = opts.executableFile || null; // Custom name for executable file, set missing to null to match constructor if (args) cmd.arguments(args); this.commands.push(cmd); cmd.parent = this; cmd.copyInheritedSettings(this); if (desc) return this; return cmd; } /** * Factory routine to create a new unattached command. * * See .command() for creating an attached subcommand, which uses this routine to * create the command. You can override createCommand to customise subcommands. * * @param {string} [name] * @return {Command} new command */ createCommand(name) { return new Command(name); } /** * You can customise the help with a subclass of Help by overriding createHelp, * or by overriding Help properties using configureHelp(). * * @return {Help} */ createHelp() { return Object.assign(new Help$1(), this.configureHelp()); } /** * You can customise the help by overriding Help properties using configureHelp(), * or with a subclass of Help by overriding createHelp(). * * @param {Object} [configuration] - configuration options * @return {Command|Object} `this` command for chaining, or stored configuration */ configureHelp(configuration) { if (configuration === undefined) return this._helpConfiguration; this._helpConfiguration = configuration; return this; } /** * The default output goes to stdout and stderr. You can customise this for special * applications. You can also customise the display of errors by overriding outputError. * * The configuration properties are all functions: * * // functions to change where being written, stdout and stderr * writeOut(str) * writeErr(str) * // matching functions to specify width for wrapping help * getOutHelpWidth() * getErrHelpWidth() * // functions based on what is being written out * outputError(str, write) // used for displaying errors, and not used for displaying help * * @param {Object} [configuration] - configuration options * @return {Command|Object} `this` command for chaining, or stored configuration */ configureOutput(configuration) { if (configuration === undefined) return this._outputConfiguration; Object.assign(this._outputConfiguration, configuration); return this; } /** * Display the help or a custom message after an error occurs. * * @param {boolean|string} [displayHelp] * @return {Command} `this` command for chaining */ showHelpAfterError(displayHelp = true) { if (typeof displayHelp !== 'string') displayHelp = !!displayHelp; this._showHelpAfterError = displayHelp; return this; } /** * Display suggestion of similar commands for unknown commands, or options for unknown options. * * @param {boolean} [displaySuggestion] * @return {Command} `this` command for chaining */ showSuggestionAfterError(displaySuggestion = true) { this._showSuggestionAfterError = !!displaySuggestion; return this; } /** * Add a prepared subcommand. * * See .command() for creating an attached subcommand which inherits settings from its parent. * * @param {Command} cmd - new subcommand * @param {Object} [opts] - configuration options * @return {Command} `this` command for chaining */ addCommand(cmd, opts) { if (!cmd._name) { throw new Error(`Command passed to .addCommand() must have a name - specify the name in Command constructor or using .name()`); } opts = opts || {}; if (opts.isDefault) this._defaultCommandName = cmd._name; if (opts.noHelp || opts.hidden) cmd._hidden = true; // modifying passed command due to existing implementation this.commands.push(cmd); cmd.parent = this; return this; } /** * Factory routine to create a new unattached argument. * * See .argument() for creating an attached argument, which uses this routine to * create the argument. You can override createArgument to return a custom argument. * * @param {string} name * @param {string} [description] * @return {Argument} new argument */ createArgument(name, description) { return new Argument$1(name, description); } /** * Define argument syntax for command. * * The default is that the argument is required, and you can explicitly * indicate this with <> around the name. Put [] around the name for an optional argument. * * @example * program.argument('<input-file>'); * program.argument('[output-file]'); * * @param {string} name * @param {string} [description] * @param {Function|*} [fn] - custom argument processing function * @param {*} [defaultValue] * @return {Command} `this` command for chaining */ argument(name, description, fn, defaultValue) { const argument = this.createArgument(name, description); if (typeof fn === 'function') { argument.default(defaultValue).argParser(fn); } else { argument.default(fn); } this.addArgument(argument); return this; } /** * Define argument syntax for command, adding multiple at once (without descriptions). * * See also .argument(). * * @example * program.arguments('<cmd> [env]'); * * @param {string} names * @return {Command} `this` command for chaining */ arguments(names) { names.trim().split(/ +/).forEach((detail) => { this.argument(detail); }); return this; } /** * Define argument syntax for command, adding a prepared argument. * * @param {Argument} argument * @return {Command} `this` command for chaining */ addArgument(argument) { const previousArgument = this.registeredArguments.slice(-1)[0]; if (previousArgument && previousArgument.variadic) { throw new Error(`only the last argument can be variadic '${previousArgument.name()}'`); } if (argument.required && argument.defaultValue !== undefined && argument.parseArg === undefined) { throw new Error(`a default value for a required argument is never used: '${argument.name()}'`); } this.registeredArguments.push(argument); return this; } /** * Override default decision whether to add implicit help command. * * addHelpCommand() // force on * addHelpCommand(false); // force off * addHelpCommand('help [cmd]', 'display help for [cmd]'); // force on with custom details * * @return {Command} `this` command for chaining */ addHelpCommand(enableOrNameAndArgs, description) { if (enableOrNameAndArgs === false) { this._addImplicitHelpCommand = false; } else { this._addImplicitHelpCommand = true; if (typeof enableOrNameAndArgs === 'string') { this._helpCommandName = enableOrNameAndArgs.split(' ')[0]; this._helpCommandnameAndArgs = enableOrNameAndArgs; } this._helpCommandDescription = description || this._helpCommandDescription; } return this; } /** * @return {boolean} * @api private */ _hasImplicitHelpCommand() { if (this._addImplicitHelpCommand === undefined) { return this.commands.length && !this._actionHandler && !this._findCommand('help'); } return this._addImplicitHelpCommand; } /** * Add hook for life cycle event. * * @param {string} event * @param {Function} listener * @return {Command} `this` command for chaining */ hook(event, listener) { const allowedValues = ['preSubcommand', 'preAction', 'postAction']; if (!allowedValues.includes(event)) { throw new Error(`Unexpected value for event passed to hook : '${event}'. Expecting one of '${allowedValues.join("', '")}'`); } if (this._lifeCycleHooks[event]) { this._lifeCycleHooks[event].push(listener); } else { this._lifeCycleHooks[event] = [listener]; } return this; } /** * Register callback to use as replacement for calling process.exit. * * @param {Function} [fn] optional callback which will be passed a CommanderError, defaults to throwing * @return {Command} `this` command for chaining */ exitOverride(fn) { if (fn) { this._exitCallback = fn; } else { this._exitCallback = (err) => { if (err.code !== 'commander.executeSubCommandAsync') { throw err; } }; } return this; } /** * Call process.exit, and _exitCallback if defined. * * @param {number} exitCode exit code for using with process.exit * @param {string} code an id string representing the error * @param {string} message human-readable description of the error * @return never * @api private */ _exit(exitCode, code, message) { if (this._exitCallback) { this._exitCallback(new CommanderError$1(exitCode, code, message)); // Expecting this line is not reached. } process$1.exit(exitCode); } /** * Register callback `fn` for the command. * * @example * program * .command('serve') * .description('start service') * .action(function() { * // do work here * }); * * @param {Function} fn * @return {Command} `this` command for chaining */ action(fn) { const listener = (args) => { // The .action callback takes an extra parameter which is the command or options. const expectedArgsCount = this.registeredArguments.length; const actionArgs = args.slice(0, expectedArgsCount); if (this._storeOptionsAsProperties) { actionArgs[expectedArgsCount] = this; // backwards compatible "options" } else { actionArgs[expectedArgsCount] = this.opts(); } actionArgs.push(this); return fn.apply(this, actionArgs);