typescript-language-server
Version:
Language Server Protocol (LSP) implementation for TypeScript using tsserver
1,395 lines (1,335 loc) • 867 kB
JavaScript
#!/usr/bin/env node
import fs$2, { promises, statSync, existsSync, readFileSync } from 'node:fs';
import { createRequire } from 'node:module';
import require$$0, { promisify } from 'util';
import require$$1 from 'path';
import require$$2 from 'os';
import crypto from 'crypto';
import require$$4 from 'net';
import require$$0$1 from 'url';
import require$$0$2 from 'fs';
import require$$3 from 'child_process';
import * as path from 'node:path';
import path__default, { extname, resolve } from 'node:path';
import require$$0$3 from 'constants';
import require$$0$4 from 'stream';
import require$$5 from 'assert';
import os from 'node:os';
import 'node:fs/promises';
import stream from 'node:stream';
import { promisify as promisify$1 } from 'node:util';
import ChildProcess from 'node:child_process';
import require$$1$1 from 'fs/promises';
import process$1 from 'node:process';
import { fileURLToPath } from 'node:url';
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function getDefaultExportFromCjs(x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
var commander$1 = {};
var argument = {};
var error = {};
var hasRequiredError;
function requireError() {
if (hasRequiredError) return error;
hasRequiredError = 1;
class CommanderError extends Error {
constructor(exitCode, code, message) {
super(message);
Error.captureStackTrace(this, this.constructor);
this.name = this.constructor.name;
this.code = code;
this.exitCode = exitCode;
this.nestedError = undefined;
}
}
class InvalidArgumentError extends CommanderError {
constructor(message) {
super(1, 'commander.invalidArgument', message);
Error.captureStackTrace(this, this.constructor);
this.name = this.constructor.name;
}
}
error.CommanderError = CommanderError;
error.InvalidArgumentError = InvalidArgumentError;
return error;
}
var hasRequiredArgument;
function requireArgument() {
if (hasRequiredArgument) return argument;
hasRequiredArgument = 1;
const {InvalidArgumentError: InvalidArgumentError} = requireError();
class Argument {
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 '<':
this.required = true;
this._name = name.slice(1, -1);
break;
case '[':
this.required = false;
this._name = name.slice(1, -1);
break;
default:
this.required = true;
this._name = name;
break;
}
if (this._name.endsWith('...')) {
this.variadic = true;
this._name = this._name.slice(0, -3);
}
}
name() {
return this._name;
}
_collectValue(value, previous) {
if (previous === this.defaultValue || !Array.isArray(previous)) {
return [ value ];
}
previous.push(value);
return previous;
}
default(value, description) {
this.defaultValue = value;
this.defaultValueDescription = description;
return this;
}
argParser(fn) {
this.parseArg = fn;
return this;
}
choices(values) {
this.argChoices = values.slice();
this.parseArg = (arg, previous) => {
if (!this.argChoices.includes(arg)) {
throw new InvalidArgumentError(`Allowed choices are ${this.argChoices.join(', ')}.`);
}
if (this.variadic) {
return this._collectValue(arg, previous);
}
return arg;
};
return this;
}
argRequired() {
this.required = true;
return this;
}
argOptional() {
this.required = false;
return this;
}
}
function humanReadableArgName(arg) {
const nameOutput = arg.name() + (arg.variadic === true ? '...' : '');
return arg.required ? '<' + nameOutput + '>' : '[' + nameOutput + ']';
}
argument.Argument = Argument;
argument.humanReadableArgName = humanReadableArgName;
return argument;
}
var command = {};
const require$5 = createRequire(import.meta.url);
function __require$5() {
return require$5('node:events');
}
const require$4 = createRequire(import.meta.url);
function __require$4() {
return require$4('node:child_process');
}
const require$3 = createRequire(import.meta.url);
function __require$3() {
return require$3('node:path');
}
const require$2 = createRequire(import.meta.url);
function __require$2() {
return require$2('node:fs');
}
const require$1 = createRequire(import.meta.url);
function __require$1() {
return require$1('node:process');
}
var help = {};
var hasRequiredHelp;
function requireHelp() {
if (hasRequiredHelp) return help;
hasRequiredHelp = 1;
const {humanReadableArgName: humanReadableArgName} = requireArgument();
class Help {
constructor() {
this.helpWidth = undefined;
this.minWidthToWrap = 40;
this.sortSubcommands = false;
this.sortOptions = false;
this.showGlobalOptions = false;
}
prepareContext(contextOptions) {
this.helpWidth = this.helpWidth ?? contextOptions.helpWidth ?? 80;
}
visibleCommands(cmd) {
const visibleCommands = cmd.commands.filter(cmd => !cmd._hidden);
const helpCommand = cmd._getHelpCommand();
if (helpCommand && !helpCommand._hidden) {
visibleCommands.push(helpCommand);
}
if (this.sortSubcommands) {
visibleCommands.sort((a, b) => a.name().localeCompare(b.name()));
}
return visibleCommands;
}
compareOptions(a, b) {
const getSortKey = option => option.short ? option.short.replace(/^-/, '') : option.long.replace(/^--/, '');
return getSortKey(a).localeCompare(getSortKey(b));
}
visibleOptions(cmd) {
const visibleOptions = cmd.options.filter(option => !option.hidden);
const helpOption = cmd._getHelpOption();
if (helpOption && !helpOption.hidden) {
const removeShort = helpOption.short && cmd._findOption(helpOption.short);
const removeLong = helpOption.long && cmd._findOption(helpOption.long);
if (!removeShort && !removeLong) {
visibleOptions.push(helpOption);
} else if (helpOption.long && !removeLong) {
visibleOptions.push(cmd.createOption(helpOption.long, helpOption.description));
} else if (helpOption.short && !removeShort) {
visibleOptions.push(cmd.createOption(helpOption.short, helpOption.description));
}
}
if (this.sortOptions) {
visibleOptions.sort(this.compareOptions);
}
return visibleOptions;
}
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;
}
visibleArguments(cmd) {
if (cmd._argsDescription) {
cmd.registeredArguments.forEach(argument => {
argument.description = argument.description || cmd._argsDescription[argument.name()] || '';
});
}
if (cmd.registeredArguments.find(argument => argument.description)) {
return cmd.registeredArguments;
}
return [];
}
subcommandTerm(cmd) {
const args = cmd.registeredArguments.map(arg => humanReadableArgName(arg)).join(' ');
return cmd._name + (cmd._aliases[0] ? '|' + cmd._aliases[0] : '') + (cmd.options.length ? ' [options]' : '') + (args ? ' ' + args : '');
}
optionTerm(option) {
return option.flags;
}
argumentTerm(argument) {
return argument.name();
}
longestSubcommandTermLength(cmd, helper) {
return helper.visibleCommands(cmd).reduce((max, command) => Math.max(max, this.displayWidth(helper.styleSubcommandTerm(helper.subcommandTerm(command)))), 0);
}
longestOptionTermLength(cmd, helper) {
return helper.visibleOptions(cmd).reduce((max, option) => Math.max(max, this.displayWidth(helper.styleOptionTerm(helper.optionTerm(option)))), 0);
}
longestGlobalOptionTermLength(cmd, helper) {
return helper.visibleGlobalOptions(cmd).reduce((max, option) => Math.max(max, this.displayWidth(helper.styleOptionTerm(helper.optionTerm(option)))), 0);
}
longestArgumentTermLength(cmd, helper) {
return helper.visibleArguments(cmd).reduce((max, argument) => Math.max(max, this.displayWidth(helper.styleArgumentTerm(helper.argumentTerm(argument)))), 0);
}
commandUsage(cmd) {
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();
}
commandDescription(cmd) {
return cmd.description();
}
subcommandDescription(cmd) {
return cmd.summary() || cmd.description();
}
optionDescription(option) {
const extraInfo = [];
if (option.argChoices) {
extraInfo.push(`choices: ${option.argChoices.map(choice => JSON.stringify(choice)).join(', ')}`);
}
if (option.defaultValue !== undefined) {
const showDefault = option.required || option.optional || option.isBoolean() && typeof option.defaultValue === 'boolean';
if (showDefault) {
extraInfo.push(`default: ${option.defaultValueDescription || JSON.stringify(option.defaultValue)}`);
}
}
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) {
const extraDescription = `(${extraInfo.join(', ')})`;
if (option.description) {
return `${option.description} ${extraDescription}`;
}
return extraDescription;
}
return option.description;
}
argumentDescription(argument) {
const extraInfo = [];
if (argument.argChoices) {
extraInfo.push(`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 extraDescription = `(${extraInfo.join(', ')})`;
if (argument.description) {
return `${argument.description} ${extraDescription}`;
}
return extraDescription;
}
return argument.description;
}
formatItemList(heading, items, helper) {
if (items.length === 0) return [];
return [ helper.styleTitle(heading), ...items, '' ];
}
groupItems(unsortedItems, visibleItems, getGroup) {
const result = new Map;
unsortedItems.forEach(item => {
const group = getGroup(item);
if (!result.has(group)) result.set(group, []);
});
visibleItems.forEach(item => {
const group = getGroup(item);
if (!result.has(group)) {
result.set(group, []);
}
result.get(group).push(item);
});
return result;
}
formatHelp(cmd, helper) {
const termWidth = helper.padWidth(cmd, helper);
const helpWidth = helper.helpWidth ?? 80;
function callFormatItem(term, description) {
return helper.formatItem(term, termWidth, description, helper);
}
let output = [ `${helper.styleTitle('Usage:')} ${helper.styleUsage(helper.commandUsage(cmd))}`, '' ];
const commandDescription = helper.commandDescription(cmd);
if (commandDescription.length > 0) {
output = output.concat([ helper.boxWrap(helper.styleCommandDescription(commandDescription), helpWidth), '' ]);
}
const argumentList = helper.visibleArguments(cmd).map(argument => callFormatItem(helper.styleArgumentTerm(helper.argumentTerm(argument)), helper.styleArgumentDescription(helper.argumentDescription(argument))));
output = output.concat(this.formatItemList('Arguments:', argumentList, helper));
const optionGroups = this.groupItems(cmd.options, helper.visibleOptions(cmd), option => option.helpGroupHeading ?? 'Options:');
optionGroups.forEach((options, group) => {
const optionList = options.map(option => callFormatItem(helper.styleOptionTerm(helper.optionTerm(option)), helper.styleOptionDescription(helper.optionDescription(option))));
output = output.concat(this.formatItemList(group, optionList, helper));
});
if (helper.showGlobalOptions) {
const globalOptionList = helper.visibleGlobalOptions(cmd).map(option => callFormatItem(helper.styleOptionTerm(helper.optionTerm(option)), helper.styleOptionDescription(helper.optionDescription(option))));
output = output.concat(this.formatItemList('Global Options:', globalOptionList, helper));
}
const commandGroups = this.groupItems(cmd.commands, helper.visibleCommands(cmd), sub => sub.helpGroup() || 'Commands:');
commandGroups.forEach((commands, group) => {
const commandList = commands.map(sub => callFormatItem(helper.styleSubcommandTerm(helper.subcommandTerm(sub)), helper.styleSubcommandDescription(helper.subcommandDescription(sub))));
output = output.concat(this.formatItemList(group, commandList, helper));
});
return output.join('\n');
}
displayWidth(str) {
return stripColor(str).length;
}
styleTitle(str) {
return str;
}
styleUsage(str) {
return str.split(' ').map(word => {
if (word === '[options]') return this.styleOptionText(word);
if (word === '[command]') return this.styleSubcommandText(word);
if (word[0] === '[' || word[0] === '<') return this.styleArgumentText(word);
return this.styleCommandText(word);
}).join(' ');
}
styleCommandDescription(str) {
return this.styleDescriptionText(str);
}
styleOptionDescription(str) {
return this.styleDescriptionText(str);
}
styleSubcommandDescription(str) {
return this.styleDescriptionText(str);
}
styleArgumentDescription(str) {
return this.styleDescriptionText(str);
}
styleDescriptionText(str) {
return str;
}
styleOptionTerm(str) {
return this.styleOptionText(str);
}
styleSubcommandTerm(str) {
return str.split(' ').map(word => {
if (word === '[options]') return this.styleOptionText(word);
if (word[0] === '[' || word[0] === '<') return this.styleArgumentText(word);
return this.styleSubcommandText(word);
}).join(' ');
}
styleArgumentTerm(str) {
return this.styleArgumentText(str);
}
styleOptionText(str) {
return str;
}
styleArgumentText(str) {
return str;
}
styleSubcommandText(str) {
return str;
}
styleCommandText(str) {
return str;
}
padWidth(cmd, helper) {
return Math.max(helper.longestOptionTermLength(cmd, helper), helper.longestGlobalOptionTermLength(cmd, helper), helper.longestSubcommandTermLength(cmd, helper), helper.longestArgumentTermLength(cmd, helper));
}
preformatted(str) {
return /\n[^\S\r\n]/.test(str);
}
formatItem(term, termWidth, description, helper) {
const itemIndent = 2;
const itemIndentStr = ' '.repeat(itemIndent);
if (!description) return itemIndentStr + term;
const paddedTerm = term.padEnd(termWidth + term.length - helper.displayWidth(term));
const spacerWidth = 2;
const helpWidth = this.helpWidth ?? 80;
const remainingWidth = helpWidth - termWidth - spacerWidth - itemIndent;
let formattedDescription;
if (remainingWidth < this.minWidthToWrap || helper.preformatted(description)) {
formattedDescription = description;
} else {
const wrappedDescription = helper.boxWrap(description, remainingWidth);
formattedDescription = wrappedDescription.replace(/\n/g, '\n' + ' '.repeat(termWidth + spacerWidth));
}
return itemIndentStr + paddedTerm + ' '.repeat(spacerWidth) + formattedDescription.replace(/\n/g, `\n${itemIndentStr}`);
}
boxWrap(str, width) {
if (width < this.minWidthToWrap) return str;
const rawLines = str.split(/\r\n|\n/);
const chunkPattern = /[\s]*[^\s]+/g;
const wrappedLines = [];
rawLines.forEach(line => {
const chunks = line.match(chunkPattern);
if (chunks === null) {
wrappedLines.push('');
return;
}
let sumChunks = [ chunks.shift() ];
let sumWidth = this.displayWidth(sumChunks[0]);
chunks.forEach(chunk => {
const visibleWidth = this.displayWidth(chunk);
if (sumWidth + visibleWidth <= width) {
sumChunks.push(chunk);
sumWidth += visibleWidth;
return;
}
wrappedLines.push(sumChunks.join(''));
const nextChunk = chunk.trimStart();
sumChunks = [ nextChunk ];
sumWidth = this.displayWidth(nextChunk);
});
wrappedLines.push(sumChunks.join(''));
});
return wrappedLines.join('\n');
}
}
function stripColor(str) {
const sgrPattern = /\x1b\[\d*(;\d*)*m/g;
return str.replace(sgrPattern, '');
}
help.Help = Help;
help.stripColor = stripColor;
return help;
}
var option = {};
var hasRequiredOption;
function requireOption() {
if (hasRequiredOption) return option;
hasRequiredOption = 1;
const {InvalidArgumentError: InvalidArgumentError} = requireError();
class Option {
constructor(flags, description) {
this.flags = flags;
this.description = description || '';
this.required = flags.includes('<');
this.optional = flags.includes('[');
this.variadic = /\w\.\.\.[>\]]$/.test(flags);
this.mandatory = false;
const optionFlags = splitOptionFlags(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;
this.helpGroupHeading = undefined;
}
default(value, description) {
this.defaultValue = value;
this.defaultValueDescription = description;
return this;
}
preset(arg) {
this.presetArg = arg;
return this;
}
conflicts(names) {
this.conflictsWith = this.conflictsWith.concat(names);
return this;
}
implies(impliedOptionValues) {
let newImplied = impliedOptionValues;
if (typeof impliedOptionValues === 'string') {
newImplied = {
[impliedOptionValues]: true
};
}
this.implied = Object.assign(this.implied || {}, newImplied);
return this;
}
env(name) {
this.envVar = name;
return this;
}
argParser(fn) {
this.parseArg = fn;
return this;
}
makeOptionMandatory(mandatory = true) {
this.mandatory = !!mandatory;
return this;
}
hideHelp(hide = true) {
this.hidden = !!hide;
return this;
}
_collectValue(value, previous) {
if (previous === this.defaultValue || !Array.isArray(previous)) {
return [ value ];
}
previous.push(value);
return previous;
}
choices(values) {
this.argChoices = values.slice();
this.parseArg = (arg, previous) => {
if (!this.argChoices.includes(arg)) {
throw new InvalidArgumentError(`Allowed choices are ${this.argChoices.join(', ')}.`);
}
if (this.variadic) {
return this._collectValue(arg, previous);
}
return arg;
};
return this;
}
name() {
if (this.long) {
return this.long.replace(/^--/, '');
}
return this.short.replace(/^-/, '');
}
attributeName() {
if (this.negate) {
return camelcase(this.name().replace(/^no-/, ''));
}
return camelcase(this.name());
}
helpGroup(heading) {
this.helpGroupHeading = heading;
return this;
}
is(arg) {
return this.short === arg || this.long === arg;
}
isBoolean() {
return !this.required && !this.optional && !this.negate;
}
}
class DualOptions {
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);
}
});
}
valueFromOption(value, option) {
const optionKey = option.attributeName();
if (!this.dualOptions.has(optionKey)) return true;
const preset = this.negativeOptions.get(optionKey).presetArg;
const negativeValue = preset !== undefined ? preset : false;
return option.negate === (negativeValue === value);
}
}
function camelcase(str) {
return str.split('-').reduce((str, word) => str + word[0].toUpperCase() + word.slice(1));
}
function splitOptionFlags(flags) {
let shortFlag;
let longFlag;
const shortFlagExp = /^-[^-]$/;
const longFlagExp = /^--[^-]/;
const flagParts = flags.split(/[ |,]+/).concat('guard');
if (shortFlagExp.test(flagParts[0])) shortFlag = flagParts.shift();
if (longFlagExp.test(flagParts[0])) longFlag = flagParts.shift();
if (!shortFlag && shortFlagExp.test(flagParts[0])) shortFlag = flagParts.shift();
if (!shortFlag && longFlagExp.test(flagParts[0])) {
shortFlag = longFlag;
longFlag = flagParts.shift();
}
if (flagParts[0].startsWith('-')) {
const unsupportedFlag = flagParts[0];
const baseError = `option creation failed due to '${unsupportedFlag}' in option flags '${flags}'`;
if (/^-[^-][^-]/.test(unsupportedFlag)) throw new Error(`${baseError}\n- a short flag is a single dash and a single character\n - either use a single dash and a single character (for a short flag)\n - or use a double dash for a long option (and can have two, like '--ws, --workspace')`);
if (shortFlagExp.test(unsupportedFlag)) throw new Error(`${baseError}\n- too many short flags`);
if (longFlagExp.test(unsupportedFlag)) throw new Error(`${baseError}\n- too many long flags`);
throw new Error(`${baseError}\n- unrecognised flag format`);
}
if (shortFlag === undefined && longFlag === undefined) throw new Error(`option creation failed due to no flags found in '${flags}'.`);
return {
shortFlag: shortFlag,
longFlag: longFlag
};
}
option.Option = Option;
option.DualOptions = DualOptions;
return option;
}
var suggestSimilar = {};
var hasRequiredSuggestSimilar;
function requireSuggestSimilar() {
if (hasRequiredSuggestSimilar) return suggestSimilar;
hasRequiredSuggestSimilar = 1;
const maxDistance = 3;
function editDistance(a, b) {
if (Math.abs(a.length - b.length) > maxDistance) return Math.max(a.length, b.length);
const d = [];
for (let i = 0; i <= a.length; i++) {
d[i] = [ i ];
}
for (let j = 0; j <= b.length; j++) {
d[0][j] = j;
}
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, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);
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];
}
function suggestSimilar$1(word, candidates) {
if (!candidates || candidates.length === 0) return '';
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 = .4;
candidates.forEach(candidate => {
if (candidate.length <= 1) return;
const distance = editDistance(word, candidate);
const length = Math.max(word.length, candidate.length);
const similarity = (length - distance) / length;
if (similarity > minSimilarity) {
if (distance < bestDistance) {
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.suggestSimilar = suggestSimilar$1;
return suggestSimilar;
}
var hasRequiredCommand;
function requireCommand() {
if (hasRequiredCommand) return command;
hasRequiredCommand = 1;
const EventEmitter = __require$5().EventEmitter;
const childProcess = __require$4();
const path = __require$3();
const fs = __require$2();
const process = __require$1();
const {Argument: Argument, humanReadableArgName: humanReadableArgName} = requireArgument();
const {CommanderError: CommanderError} = requireError();
const {Help: Help, stripColor: stripColor} = requireHelp();
const {Option: Option, DualOptions: DualOptions} = requireOption();
const {suggestSimilar: suggestSimilar} = requireSuggestSimilar();
class Command extends EventEmitter {
constructor(name) {
super();
this.commands = [];
this.options = [];
this.parent = null;
this._allowUnknownOption = false;
this._allowExcessArguments = false;
this.registeredArguments = [];
this._args = this.registeredArguments;
this.args = [];
this.rawArgs = [];
this.processedArgs = [];
this._scriptPath = null;
this._name = name || '';
this._optionValues = {};
this._optionValueSources = {};
this._storeOptionsAsProperties = false;
this._actionHandler = null;
this._executableHandler = false;
this._executableFile = null;
this._executableDir = null;
this._defaultCommandName = null;
this._exitCallback = null;
this._aliases = [];
this._combineFlagAndOptionalValue = true;
this._description = '';
this._summary = '';
this._argsDescription = undefined;
this._enablePositionalOptions = false;
this._passThroughOptions = false;
this._lifeCycleHooks = {};
this._showHelpAfterError = false;
this._showSuggestionAfterError = true;
this._savedState = null;
this._outputConfiguration = {
writeOut: str => process.stdout.write(str),
writeErr: str => process.stderr.write(str),
outputError: (str, write) => write(str),
getOutHelpWidth: () => process.stdout.isTTY ? process.stdout.columns : undefined,
getErrHelpWidth: () => process.stderr.isTTY ? process.stderr.columns : undefined,
getOutHasColors: () => useColor() ?? (process.stdout.isTTY && process.stdout.hasColors?.()),
getErrHasColors: () => useColor() ?? (process.stderr.isTTY && process.stderr.hasColors?.()),
stripColor: str => stripColor(str)
};
this._hidden = false;
this._helpOption = undefined;
this._addImplicitHelpCommand = undefined;
this._helpCommand = undefined;
this._helpConfiguration = {};
this._helpGroupHeading = undefined;
this._defaultCommandGroup = undefined;
this._defaultOptionGroup = undefined;
}
copyInheritedSettings(sourceCommand) {
this._outputConfiguration = sourceCommand._outputConfiguration;
this._helpOption = sourceCommand._helpOption;
this._helpCommand = sourceCommand._helpCommand;
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;
}
_getCommandAndAncestors() {
const result = [];
for (let command = this; command; command = command.parent) {
result.push(command);
}
return result;
}
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);
cmd._executableFile = opts.executableFile || null;
if (args) cmd.arguments(args);
this._registerCommand(cmd);
cmd.parent = this;
cmd.copyInheritedSettings(this);
if (desc) return this;
return cmd;
}
createCommand(name) {
return new Command(name);
}
createHelp() {
return Object.assign(new Help, this.configureHelp());
}
configureHelp(configuration) {
if (configuration === undefined) return this._helpConfiguration;
this._helpConfiguration = configuration;
return this;
}
configureOutput(configuration) {
if (configuration === undefined) return this._outputConfiguration;
this._outputConfiguration = {
...this._outputConfiguration,
...configuration
};
return this;
}
showHelpAfterError(displayHelp = true) {
if (typeof displayHelp !== 'string') displayHelp = !!displayHelp;
this._showHelpAfterError = displayHelp;
return this;
}
showSuggestionAfterError(displaySuggestion = true) {
this._showSuggestionAfterError = !!displaySuggestion;
return this;
}
addCommand(cmd, opts) {
if (!cmd._name) {
throw new Error(`Command passed to .addCommand() must have a name\n- 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;
this._registerCommand(cmd);
cmd.parent = this;
cmd._checkForBrokenPassThrough();
return this;
}
createArgument(name, description) {
return new Argument(name, description);
}
argument(name, description, parseArg, defaultValue) {
const argument = this.createArgument(name, description);
if (typeof parseArg === 'function') {
argument.default(defaultValue).argParser(parseArg);
} else {
argument.default(parseArg);
}
this.addArgument(argument);
return this;
}
arguments(names) {
names.trim().split(/ +/).forEach(detail => {
this.argument(detail);
});
return this;
}
addArgument(argument) {
const previousArgument = this.registeredArguments.slice(-1)[0];
if (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;
}
helpCommand(enableOrNameAndArgs, description) {
if (typeof enableOrNameAndArgs === 'boolean') {
this._addImplicitHelpCommand = enableOrNameAndArgs;
if (enableOrNameAndArgs && this._defaultCommandGroup) {
this._initCommandGroup(this._getHelpCommand());
}
return this;
}
const nameAndArgs = enableOrNameAndArgs ?? 'help [command]';
const [, helpName, helpArgs] = nameAndArgs.match(/([^ ]+) *(.*)/);
const helpDescription = description ?? 'display help for command';
const helpCommand = this.createCommand(helpName);
helpCommand.helpOption(false);
if (helpArgs) helpCommand.arguments(helpArgs);
if (helpDescription) helpCommand.description(helpDescription);
this._addImplicitHelpCommand = true;
this._helpCommand = helpCommand;
if (enableOrNameAndArgs || description) this._initCommandGroup(helpCommand);
return this;
}
addHelpCommand(helpCommand, deprecatedDescription) {
if (typeof helpCommand !== 'object') {
this.helpCommand(helpCommand, deprecatedDescription);
return this;
}
this._addImplicitHelpCommand = true;
this._helpCommand = helpCommand;
this._initCommandGroup(helpCommand);
return this;
}
_getHelpCommand() {
const hasImplicitHelpCommand = this._addImplicitHelpCommand ?? (this.commands.length && !this._actionHandler && !this._findCommand('help'));
if (hasImplicitHelpCommand) {
if (this._helpCommand === undefined) {
this.helpCommand(undefined, undefined);
}
return this._helpCommand;
}
return null;
}
hook(event, listener) {
const allowedValues = [ 'preSubcommand', 'preAction', 'postAction' ];
if (!allowedValues.includes(event)) {
throw new Error(`Unexpected value for event passed to hook : '${event}'.\nExpecting one of '${allowedValues.join('\', \'')}'`);
}
if (this._lifeCycleHooks[event]) {
this._lifeCycleHooks[event].push(listener);
} else {
this._lifeCycleHooks[event] = [ listener ];
}
return this;
}
exitOverride(fn) {
if (fn) {
this._exitCallback = fn;
} else {
this._exitCallback = err => {
if (err.code !== 'commander.executeSubCommandAsync') {
throw err;
}
};
}
return this;
}
_exit(exitCode, code, message) {
if (this._exitCallback) {
this._exitCallback(new CommanderError(exitCode, code, message));
}
process.exit(exitCode);
}
action(fn) {
const listener = args => {
const expectedArgsCount = this.registeredArguments.length;
const actionArgs = args.slice(0, expectedArgsCount);
if (this._storeOptionsAsProperties) {
actionArgs[expectedArgsCount] = this;
} else {
actionArgs[expectedArgsCount] = this.opts();
}
actionArgs.push(this);
return fn.apply(this, actionArgs);
};
this._actionHandler = listener;
return this;
}
createOption(flags, description) {
return new Option(flags, description);
}
_callParseArg(target, value, previous, invalidArgumentMessage) {
try {
return target.parseArg(value, previous);
} catch (err) {
if (err.code === 'commander.invalidArgument') {
const message = `${invalidArgumentMessage} ${err.message}`;
this.error(message, {
exitCode: err.exitCode,
code: err.code
});
}
throw err;
}
}
_registerOption(option) {
const matchingOption = option.short && this._findOption(option.short) || option.long && this._findOption(option.long);
if (matchingOption) {
const matchingFlag = option.long && this._findOption(option.long) ? option.long : option.short;
throw new Error(`Cannot add option '${option.flags}'${this._name && ` to command '${this._name}'`} due to conflicting flag '${matchingFlag}'\n- already used by option '${matchingOption.flags}'`);
}
this._initOptionGroup(option);
this.options.push(option);
}
_registerCommand(command) {
const knownBy = cmd => [ cmd.name() ].concat(cmd.aliases());
const alreadyUsed = knownBy(command).find(name => this._findCommand(name));
if (alreadyUsed) {
const existingCmd = knownBy(this._findCommand(alreadyUsed)).join('|');
const newCmd = knownBy(command).join('|');
throw new Error(`cannot add command '${newCmd}' as already have command '${existingCmd}'`);
}
this._initCommandGroup(command);
this.commands.push(command);
}
addOption(option) {
this._registerOption(option);
const oname = option.name();
const name = option.attributeName();
if (option.negate) {
const positiveLongFlag = option.long.replace(/^--no-/, '--');
if (!this._findOption(positiveLongFlag)) {
this.setOptionValueWithSource(name, option.defaultValue === undefined ? true : option.defaultValue, 'default');
}
} else if (option.defaultValue !== undefined) {
this.setOptionValueWithSource(name, option.defaultValue, 'default');
}
const handleOptionValue = (val, invalidValueMessage, valueSource) => {
if (val == null && option.presetArg !== undefined) {
val = option.presetArg;
}
const oldValue = this.getOptionValue(name);
if (val !== null && option.parseArg) {
val = this._callParseArg(option, val, oldValue, invalidValueMessage);
} else if (val !== null && option.variadic) {
val = option._collectValue(val, oldValue);
}
if (val == null) {
if (option.negate) {
val = false;
} else if (option.isBoolean() || option.optional) {
val = true;
} else {
val = '';
}
}
this.setOptionValueWithSource(name, val, valueSource);
};
this.on('option:' + oname, val => {
const invalidValueMessage = `error: option '${option.flags}' argument '${val}' is invalid.`;
handleOptionValue(val, invalidValueMessage, 'cli');
});
if (option.envVar) {
this.on('optionEnv:' + oname, val => {
const invalidValueMessage = `error: option '${option.flags}' value '${val}' from env '${option.envVar}' is invalid.`;
handleOptionValue(val, invalidValueMessage, 'env');
});
}
return this;
}
_optionEx(config, flags, description, fn, defaultValue) {
if (typeof flags === 'object' && flags instanceof Option) {
throw new Error('To add an Option object use addOption() instead of option() or requiredOption()');
}
const option = this.createOption(flags, description);
option.makeOptionMandatory(!!config.mandatory);
if (typeof fn === 'function') {
option.default(defaultValue).argParser(fn);
} else if (fn instanceof RegExp) {
const regex = fn;
fn = (val, def) => {
const m = regex.exec(val);
return m ? m[0] : def;
};
option.default(defaultValue).argParser(fn);
} else {
option.default(fn);
}
return this.addOption(option);
}
option(flags, description, parseArg, defaultValue) {
return this._optionEx({}, flags, description, parseArg, defaultValue);
}
requiredOption(flags, description, parseArg, defaultValue) {
return this._optionEx({
mandatory: true
}, flags, description, parseArg, defaultValue);
}
combineFlagAndOptionalValue(combine = true) {
this._combineFlagAndOptionalValue = !!combine;
return this;
}
allowUnknownOption(allowUnknown = true) {
this._allowUnknownOption = !!allowUnknown;
return this;
}
allowExcessArguments(allowExcess = true) {
this._allowExcessArguments = !!allowExcess;
return this;
}
enablePositionalOptions(positional = true) {
this._enablePositionalOptions = !!positional;
return this;
}
passThroughOptions(passThrough = true) {
this._passThroughOptions = !!passThrough;
this._checkForBrokenPassThrough();
return this;
}
_checkForBrokenPassThrough() {
if (this.parent && this._passThroughOptions && !this.parent._enablePositionalOptions) {
throw new Error(`passThroughOptions cannot be used for '${this._name}' without turning on enablePositionalOptions for parent command(s)`);
}
}
storeOptionsAsProperties(storeAsProperties = true) {
if (this.options.length) {
throw new Error('call .storeOptionsAsProperties() before adding options');
}
if (Object.keys(this._optionValues).length) {
throw new Error('call .storeOptionsAsProperties() before setting option values');
}
this._storeOptionsAsProperties = !!storeAsProperties;
return this;
}
getOptionValue(key) {
if (this._storeOptionsAsProperties) {
return this[key];
}
return this._optionValues[key];
}
setOptionValue(key, value) {
return this.setOptionValueWithSource(key, value, undefined);
}
setOptionValueWithSource(key, value, source) {
if (this._storeOptionsAsProperties) {
this[key] = value;
} else {
this._optionValues[key] = value;
}
this._optionValueSources[key] = source;
return this;
}
getOptionValueSource(key) {
return this._optionValueSources[key];
}
getOptionValueSourceWithGlobals(key) {
let source;
this._getCommandAndAncestors().forEach(cmd => {
if (cmd.getOptionValueSource(key) !== undefined) {
source = cmd.getOptionValueSource(key);
}
});
return source;
}
_prepareUserArgs(argv, parseOptions) {
if (argv !== undefined && !Array.isArray(argv)) {
throw new Error('first parameter to parse must be array or undefined');
}
parseOptions = parseOptions || {};
if (argv === undefined && parseOptions.from === undefined) {
if (process.versions?.electron) {
parseOptions.from = 'electron';
}
const execArgv = process.execArgv ?? [];
if (execArgv.includes('-e') || execArgv.includes('--eval') || execArgv.includes('-p') || execArgv.includes('--print')) {
parseOptions.from = 'eval';
}
}
if (argv === undefined) {
argv = process.argv;
}
this.rawArgs = argv.slice();
let userArgs;
switch (parseOptions.from) {
case undefined:
case 'node':
this._scriptPath = argv[1];
userArgs = argv.slice(2);
break;
case 'electron':
if (process.defaultApp) {
this._scriptPath = argv[1];
userArgs = argv.slice(2);
} else {
userArgs = argv.slice(1);
}
break;
case 'user':
userArgs = argv.slice(0);
break;
case 'eval':
userArgs = argv.slice(1);
break;
default:
throw new Error(`unexpected parse option { from: '${parseOptions.from}' }`);
}
if (!this._name && this._scriptPath) this.nameFromFilename(this._scriptPath);
this._name = this._name || 'program';
return userArgs;
}
parse(argv, parseOptions) {
this._prepareForParse();
const userArgs = this._prepareUserArgs(argv, parseOptions);
this._parseCommand([], userArgs);
return this;
}
async parseAsync(argv, parseOptions) {
this._prepareForParse();
const userArgs = this._prepareUserArgs(argv, parseOptions);
await this._parseCommand([], userArgs);
return this;
}
_prepareForParse() {
if (this._savedState === null) {
this.saveStateBeforeParse();
} else {
this.restoreStateBeforeParse();
}
}
saveStateBeforeParse() {
this._savedState = {
_name: this._name,
_optionValues: {
...this._optionValues
},
_optionValueSources: {
...this._optionValueSources
}
};
}
restoreStateBeforeParse() {
if (this._storeOptionsAsProperties) throw new Error(`Can not call parse again when storeOptionsAsProperties is true.\n- either make a new Command for each call to parse, or stop storing options as properties`);
this._name = this._savedState._name;
this._scriptPath = null;
this.rawArgs = [];
this._optionValues = {
...this._savedState._optionValues
};
this._optionValueSources = {
...this._savedState._optionValueSources
};
this.args = [];
this.processedArgs = [];
}
_checkForMissingExecutable(executableFile, executableDir, subcommandName) {
if (fs.existsSync(executableFile)) return;
const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : 'no directory for search for local subcommand, use .executableDir() to supply a custom directory';
const executableMissing = `'${executableFile}' does not exist\n - if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead\n - if the default executable name is not suitable, use the executableFile option to supply a custom name or path\n - ${executableDirMessage}`;
throw new Error(executableMissing);
}
_executeSubCommand(subcommand, args) {
args = args.slice();
let launchWithNode = false;
const sourceExt = [ '.js', '.ts', '.tsx', '.mjs', '.cjs' ];
function findFile(baseDir, baseName) {
const localBin = path.resolve(baseDir, baseName);
if (fs.existsSync(localBin)) return localBin;
if (sourceExt.includes(path.extname(baseName))) return undefined;
const foundExt = sourceExt.find(ext => fs.existsSync(`${localBin}${ext}`));
if (foundExt) return `${localBin}${foundExt}`;
return undefined;
}
this._checkForMissingMandatoryOptions();
this._checkForConflictingOptions();
let executableFile = subcommand._executableFile || `${this._name}-${subcommand._name}`;
let executableDir = this._executableDir || '';
if (this._scriptPath) {
let resolvedScriptPath;
try {
resolvedScriptPath = fs.realpathSync(this._scriptPath);
} catch {
resolvedScriptPath = this._scriptPath;
}
executableDir = path.resolve(path.dirname(resolvedScriptPath), executableDir);
}
if (executableDir) {
let localFile = findFile(executableDir, executableFile);
if (!localFile && !subcommand._executableFile && this._scriptPath) {
const legacyName = path.basename(this._scriptPath, path.extname(this._scriptPath));
if (legacyName !== this._name) {
localFile = findFile(executableDir, `${legacyName}-${subcommand._name}`);
}
}
executableFile = localFile || executableFile;
}
launchWithNode = sourceExt.includes(path.extname(executableFile));
let proc;
if (process.platform !== 'win32') {
if (launchWithNode) {
args.unshift(executableFile);
args = incrementNodeInspectorPort(process.execArgv).concat(args);
proc = childProcess.spawn(process.argv[0], args, {
stdio: 'inherit'
});
} else {
proc = childProcess.spawn(executableFile, args, {
stdio: 'inherit'
});
}
} else {
this._checkForMissingExecutable(executableFile, executableDir, subcommand._name);
args.unshift(executableFile);
args = incrementNodeInspectorPort(process.execArgv).concat(args);
proc = childProcess.spawn(process.execPath, args, {
stdio: 'inherit'
});
}
if (!proc.killed) {
const signals = [ 'SIGUSR1', 'SIGUSR2', 'SIGTERM', 'SIGINT', 'SIGHUP' ];
signals.forEach(signal => {
process.on(signal, () => {
if (proc.killed === false && proc.exitCode === null) {
proc.kill(signal);
}
});
});
}
const exitCallback = this._exitCallback;
proc.on('close', code => {
code = code ?? 1;
if (!exitCallback) {
process.exit(code);
} else {