@black-flag/core
Version:
A declarative framework for building fluent, deeply hierarchical command line interfaces with yargs
665 lines (664 loc) • 36.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.discoverCommands = discoverCommands;
require("core-js/modules/es.array.push.js");
require("core-js/modules/es.iterator.constructor.js");
require("core-js/modules/es.iterator.filter.js");
require("core-js/modules/es.iterator.for-each.js");
require("core-js/modules/es.iterator.map.js");
require("core-js/modules/esnext.json.parse.js");
var _nodeAssert = _interopRequireDefault(require("node:assert"));
var _promises = _interopRequireDefault(require("node:fs/promises"));
var _nodePath = require("node:path");
var _nodeUrl = require("node:url");
var _types = require("node:util/types");
var _fs = require("@-xun/fs");
var _constant = require("./constant.js");
var _error = require("./error.js");
var _util = require("./util.js");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const hasSpacesRegExp = /\s+/;
const isValidYargsCommandDslRegExp = /^\$0( ((<[^>]*>)|(\[[^\]]*\])))*$/;
const alphaSort = new Intl.Collator(undefined, {
numeric: true
});
async function discoverCommands(basePath_, context) {
let alreadyLoadedRootCommand = false;
if (!basePath_ || typeof basePath_ !== 'string') {
throw new _error.AssertionFailedError(_error.BfErrorMessage.BadConfigurationPath(basePath_));
}
const basePath = (0, _fs.toAbsolutePath)(basePath_.startsWith('file://') ? (0, _nodeUrl.fileURLToPath)(basePath_) : basePath_);
const discoverDebug = context.debug.extend('discover');
const discoveredDebug = discoverDebug.extend('discovered');
discoverDebug('ensuring base path directory exists and is readable: "%O"', basePath);
try {
await _promises.default.access(basePath, _promises.default.constants.R_OK);
if (!(await _promises.default.stat(basePath)).isDirectory()) {
throw new Error(_error.BfErrorMessage.PathIsNotDirectory());
}
} catch (error) {
discoverDebug.error('failed due to invalid base path "%O": %O', basePath, error);
throw new _error.AssertionFailedError(_error.BfErrorMessage.BadConfigurationPath(basePath));
}
discoverDebug('searching upwards for nearest package.json file starting at %O', basePath);
const package_ = {
path: await (await import('package-up')).packageUp({
cwd: basePath
}),
name: undefined,
version: undefined
};
if (package_.path) {
discoverDebug('loading package.json file from %O', package_.path);
try {
const {
name,
version
} = JSON.parse(await _promises.default.readFile(package_.path, 'utf8'));
package_.name = name;
package_.version = version;
} catch (error) {
discoverDebug.warn('load failed: attempt to import %O failed: %O', package_.path, error);
}
} else {
discoverDebug.warn('search failed: no package.json file found');
}
discoverDebug('relevant cli package.json data discovered: %O', package_);
if (context.state.globalVersionOption) {
if (!context.state.globalVersionOption.text) {
context.state.globalVersionOption.text = package_.version || '';
}
if (!context.state.globalVersionOption.text) {
context.state.globalVersionOption = undefined;
discoverDebug.warn('disabled built-in version option (globalVersionOption=undefined) since no version info available');
}
}
discoverDebug('beginning configuration module auto-discovery at %O', basePath);
await discover(basePath);
discoverDebug('configuration module auto-discovery completed');
if (context.commands.size) {
discoveredDebug.message('%O commands loaded: %O', context.commands.size, context.commands.keys());
discoveredDebug.message('%O', context.commands);
} else {
throw new _error.AssertionFailedError(_error.BfErrorMessage.NoConfigurationLoaded(basePath));
}
return;
async function discover(configPath, lineage = [], grandparentPrograms = undefined, grandparentMeta = undefined) {
const isRootCommand = !alreadyLoadedRootCommand;
const parentType = isRootCommand ? 'pure parent' : 'parent-child';
const depth = lineage.length;
discoverDebug('initial parent lineage: %O', lineage);
discoverDebug('is root (aka "pure parent") command: %O', isRootCommand);
(0, _nodeAssert.default)(grandparentPrograms === undefined || !isRootCommand, _error.BfErrorMessage.GuruMeditation());
const {
configuration: parentConfig,
metadata: parentMeta
} = await loadConfiguration(['js', 'mjs', 'cjs', 'ts', 'mts', 'cts'].map(extension => (0, _fs.toPath)(configPath, `index.${extension}`)));
if (!parentConfig) {
discoverDebug.warn(`skipped ${parentType} configuration (depth: %O) due to missing or unloadable index file in directory %O`, depth, configPath);
return;
}
ensureConfigurationDoesNotConflictWithReservedNames(parentConfig, lineage.join(' '));
lineage = [...lineage, parentConfig.name];
const parentConfigFullName = lineage.join(' ');
discoverDebug('updated parent lineage: %O', lineage);
discoverDebug('command full name: %O', parentConfigFullName);
const parentPrograms = await makeCommandPrograms(parentConfig, parentMeta, parentConfigFullName, parentType);
context.commands.set(parentConfigFullName, {
programs: parentPrograms,
metadata: parentMeta
});
discoverDebug(`added ${parentType} command mapping to ExecutionContext::commands`);
linkChildRouterToParentRouter(parentPrograms.router, parentConfig, parentConfigFullName, grandparentPrograms?.router);
addChildCommandToParent(parentConfig, parentConfigFullName, grandparentPrograms);
if (grandparentMeta) {
grandparentMeta.hasChildren = true;
}
discoveredDebug(`discovered ${parentType} configuration (depth: %O) for command %O`, depth, parentConfigFullName);
const configDir = await _promises.default.opendir(configPath);
for await (const entry of configDir) {
const isPotentialChildConfigOfCurrentParent = /.*(?<!index)\.(?:js|mjs|cjs|ts|mts|cts)$/.test(entry.name) && !entry.name.endsWith('.d.ts');
const entryFullPath = (0, _fs.toPath)((0, _fs.toAbsolutePath)(entry.parentPath), entry.name);
discoverDebug('saw potential child configuration file: %O', entryFullPath);
if (entry.isDirectory()) {
discoverDebug('file is actually a directory, recursing');
await discover(entryFullPath, lineage, parentPrograms, parentMeta);
} else if (isPotentialChildConfigOfCurrentParent) {
discoverDebug('attempting to load file');
const {
configuration: childConfig,
metadata: childMeta
} = await loadConfiguration(entryFullPath);
if (!childConfig) {
discoverDebug.error(`failed to load pure child configuration (depth: %O) due to missing or invalid file %O`, depth, entryFullPath);
throw new _error.AssertionFailedError(_error.BfErrorMessage.ConfigLoadFailure(entryFullPath));
}
const childConfigFullName = `${parentConfigFullName} ${childConfig.name}`;
discoverDebug('pure child full name (lineage): %O', childConfigFullName);
ensureConfigurationDoesNotConflictWithReservedNames(childConfig, parentConfigFullName);
const childPrograms = await makeCommandPrograms(childConfig, childMeta, childConfigFullName, 'pure child');
context.commands.set(childConfigFullName, {
programs: childPrograms,
metadata: childMeta
});
discoverDebug(`added pure child command mapping to ExecutionContext::commands`);
linkChildRouterToParentRouter(childPrograms.router, childConfig, childConfigFullName, parentPrograms.router);
addChildCommandToParent(childConfig, childConfigFullName, parentPrograms);
parentMeta.hasChildren = true;
discoveredDebug(`discovered pure child configuration (depth: %O) for command %O`, depth + 1, childConfigFullName);
} else {
discoverDebug('file was ignored (only non-index sibling JS files are considered at this stage)');
}
}
}
async function loadConfiguration(configPath) {
const isRootProgram = !alreadyLoadedRootCommand;
const loadDebug = discoverDebug.extend('load-configuration');
const maybeConfigPaths = [configPath].flat().map(p => p.trim()).filter(Boolean);
loadDebug('loading new configuration from the first readable path: %O', maybeConfigPaths);
while (maybeConfigPaths.length) {
try {
const maybeConfigPath = maybeConfigPaths.shift();
const maybeConfigFileURL = (0, _nodeUrl.pathToFileURL)(maybeConfigPath).toString();
loadDebug('attempting to load configuration file url: %O (real path: %O)', maybeConfigFileURL, maybeConfigPath);
const maybeImportedConfig = await (async () => {
try {
return await import(maybeConfigFileURL);
} catch (error) {
if (isUnknownFileExtensionSystemError(error) || isModuleNotFoundSystemError(error) && (error.moduleName?.endsWith(maybeConfigPath) || error.url === maybeConfigFileURL.toString())) {
loadDebug.warn('a recoverable failure occurred while attempting to load configuration: %O', error);
} else {
throw error;
}
}
})();
loadDebug('maybeImportedConfig: %O', maybeImportedConfig);
if (maybeImportedConfig) {
const meta = {
filename: (0, _nodePath.basename)(maybeConfigPath),
filepath: maybeConfigFileURL,
hasChildren: false,
parentDirName: (0, _nodePath.basename)((0, _fs.toDirname)(maybeConfigPath)),
get filenameWithoutExtension() {
return _nodeAssert.default.fail(_error.BfErrorMessage.GuruMeditation());
},
set filenameWithoutExtension(v) {
delete meta.filenameWithoutExtension;
meta.filenameWithoutExtension = v;
},
get fullUsageText() {
return _nodeAssert.default.fail(_error.BfErrorMessage.GuruMeditation());
},
set fullUsageText(v) {
delete meta.fullUsageText;
meta.fullUsageText = v;
},
get isImplemented() {
return _nodeAssert.default.fail(_error.BfErrorMessage.GuruMeditation());
},
set isImplemented(v) {
delete meta.isImplemented;
meta.isImplemented = v;
},
get reservedCommandNames() {
return _nodeAssert.default.fail(_error.BfErrorMessage.GuruMeditation());
},
set reservedCommandNames(v) {
delete meta.reservedCommandNames;
meta.reservedCommandNames = v;
},
get type() {
return _nodeAssert.default.fail(_error.BfErrorMessage.GuruMeditation());
},
set type(v) {
delete meta.type;
meta.type = v;
}
};
meta.filenameWithoutExtension = meta.filename.split('.').slice(0, -1).join('.');
const isParentProgram = meta.filenameWithoutExtension === 'index';
meta.type = isRootProgram ? 'pure parent' : isParentProgram ? 'parent-child' : 'pure child';
loadDebug('configuration file metadata (w/o reservedCommandNames): %O', meta);
let importedConfig = maybeImportedConfig;
if (importedConfig.default) {
importedConfig = importedConfig.default;
}
if (importedConfig.default) {
importedConfig = importedConfig.default;
}
let rawConfig;
if (typeof importedConfig === 'function') {
loadDebug('configuration returned a function');
rawConfig = await importedConfig(context);
} else {
loadDebug('configuration returned an object (or something coerced into {})');
rawConfig = typeof importedConfig === 'object' ? importedConfig : {};
}
rawConfig = {
...rawConfig
};
const command = (rawConfig.command ?? '$0').trim();
meta.isImplemented = rawConfig.handler !== undefined;
const handlerDebug = loadDebug.extend('handler');
const finalConfig = {
aliases: rawConfig.aliases?.map(str => replaceSpaces(str).trim()) || [],
builder: rawConfig.builder || {},
command,
deprecated: rawConfig.deprecated ?? false,
description: rawConfig.description ?? '',
handler(...args) {
handlerDebug('executing real handler function for %O', finalConfig.name);
return (rawConfig.handler || defaultCommandHandler)(...args);
},
name: replaceSpaces(rawConfig.name || (isRootProgram && package_.name ? package_.name : isParentProgram ? meta.parentDirName : meta.filenameWithoutExtension)).trim(),
usage: (0, _util.capitalize)(rawConfig.usage || _constant.defaultUsageText).trim()
};
finalConfig.description = typeof finalConfig.description === 'string' ? (0, _util.capitalize)(finalConfig.description).trim() : finalConfig.description;
loadDebug.message('successfully loaded configuration object: %O', finalConfig);
loadDebug('validating loaded configuration object for correctness');
for (const name of [finalConfig.name, ...finalConfig.aliases]) {
if (hasSpacesRegExp.test(name)) {
throw new _error.AssertionFailedError(_error.BfErrorMessage.InvalidCharacters(name, 'space(s)'));
}
if (name.includes('$0')) {
throw new _error.AssertionFailedError(_error.BfErrorMessage.InvalidCharacters(name, '$0'));
}
if (name.includes('$1')) {
throw new _error.AssertionFailedError(_error.BfErrorMessage.InvalidCharacters(name, '$1'));
}
if (name.includes('|') || name.includes('<') || name.includes('>') || name.includes('[') || name.includes(']') || name.includes('{') || name.includes('}')) {
throw new _error.AssertionFailedError(_error.BfErrorMessage.InvalidCharacters(name, '|, <, >, [, ], {, or }'));
}
}
if (finalConfig.command !== '$0' && (!finalConfig.command.startsWith('$0') || !finalConfig.command.startsWith('$0 '))) {
throw new _error.AssertionFailedError(_error.BfErrorMessage.InvalidCommandExportBadStart(finalConfig.name));
}
if (!isValidYargsCommandDslRegExp.test(finalConfig.command)) {
throw new _error.AssertionFailedError(_error.BfErrorMessage.InvalidCommandExportBadPositionals(finalConfig.name));
}
loadDebug('configuration is valid!');
alreadyLoadedRootCommand = true;
meta.reservedCommandNames = [finalConfig.name, ...finalConfig.aliases];
loadDebug('metadata reserved command names: %O', meta.reservedCommandNames);
return {
configuration: finalConfig,
metadata: meta
};
}
} catch (error) {
loadDebug.error('an irrecoverable failure occurred while loading configuration: %O', error);
throw error;
}
}
return {
configuration: undefined,
metadata: undefined
};
}
function ensureConfigurationDoesNotConflictWithReservedNames(config, parentFullName) {
let checkCount = 0;
[config.name, ...config.aliases].forEach((reservedName, index, reservedNames) => {
[...reservedNames.slice(0, index), ...reservedNames.slice(index + 1)].forEach((reservedName_, index_) => {
checkCount++;
if (reservedName === reservedName_) {
throw new _error.AssertionFailedError(_error.BfErrorMessage.DuplicateCommandName(parentFullName, reservedName, index === 0 ? 'name' : 'alias', reservedName_, index !== 0 && index_ === 0 ? 'name' : 'alias'));
}
});
});
(0, _nodeAssert.default)(typeof parentFullName === 'string', _error.BfErrorMessage.GuruMeditation());
context.commands.forEach((command, commandName) => {
const splitCommandName = commandName.split(' ');
const seenParentFullName = splitCommandName.slice(0, -1).join(' ');
const seenName = splitCommandName.at(-1);
if (seenParentFullName === parentFullName) {
command.metadata.reservedCommandNames.forEach((reservedName, index) => {
(0, _nodeAssert.default)(index !== 0 || seenName === reservedName, _error.BfErrorMessage.GuruMeditation());
checkCount++;
if (reservedName === config.name) {
throw new _error.AssertionFailedError(_error.BfErrorMessage.DuplicateCommandName(parentFullName, config.name, 'name', reservedName, index === 0 ? 'name' : 'alias'));
}
config.aliases.forEach(aliasName => {
checkCount++;
if (reservedName === aliasName) {
throw new _error.AssertionFailedError(_error.BfErrorMessage.DuplicateCommandName(parentFullName, aliasName, 'alias', reservedName, index === 0 ? 'name' : 'alias'));
}
});
});
}
});
discoverDebug('no reserved name conflicts detected (%O checks performed)', checkCount);
}
async function makeCommandPrograms(config, meta, fullName, type) {
const makerDebug = discoverDebug.extend('make:programs');
if (type === 'pure parent' && config.aliases.length) {
makerDebug.warn('root command aliases will be ignored during routing and will not appear in help text: %O', config.aliases);
}
const programs = {
effector: await makePartiallyConfiguredProgram('effector'),
helper: await makePartiallyConfiguredProgram('helper'),
router: await makePartiallyConfiguredProgram('router')
};
if (type === 'pure parent' && context.state.globalVersionOption?.name.length) {
const {
name: versionOptionName,
description: versionOptionDescription
} = context.state.globalVersionOption;
(0, _nodeAssert.default)(versionOptionName.length, _error.BfErrorMessage.GuruMeditation());
[programs.helper, programs.effector].forEach(program => program.option(versionOptionName, {
boolean: true,
description: versionOptionDescription
}));
}
programs.effector.strict(true);
meta.fullUsageText = config.usage.replaceAll('$000', config.command).replaceAll('$1', config.description || '').trim();
programs.helper.scriptName(fullName);
programs.effector.scriptName(fullName);
programs.helper.wrap(context.state.initialTerminalWidth);
programs.effector.wrap(context.state.initialTerminalWidth);
programs.helper.fail(customFailHandler('helper'));
programs.effector.fail(customFailHandler('effector'));
const routerDebug = discoverDebug.extend('router@');
programs.router.command(['$0'], '[routed-1]', {}, async function () {
routerDebug('control reserved; calling HelperProgram::parseAsync');
await programs.helper.parseAsync(context.state.rawArgv, (0, _util.wrapExecutionContext)(context));
}, [], false);
programs.helper.command_deferred([makeRequiredPositionalsOptional(config.command), ...config.aliases], false, makeVanillaYargsBuilder(programs.helper, config, 'first-pass'), async parsedArgv => {
const helperDebug = discoverDebug.extend('helper');
helperDebug('entered wrapper handler function for %O', config.name);
(0, _nodeAssert.default)(context.state.firstPassArgv === undefined, _error.BfErrorMessage.GuruMeditation());
context.state.firstPassArgv = parsedArgv;
if (context.state.isHandlingHelpOption) {
helperDebug('sending help text to stdout (triggered by the %O option)', context.state.globalHelpOption?.name || '???');
setContextualUsageTextFor(meta.fullUsageText, 'full');
programs.effector.showHelp('log');
context.state.didOutputHelpOrVersionText = true;
helperDebug('gracefully exited wrapper handler function for %O', config.name);
throw new _error.GracefulEarlyExitError();
} else if (type === 'pure parent' && context.state.isHandlingVersionOption) {
helperDebug('sending version text to stdout (triggered by the %O option)', context.state.globalVersionOption?.name || '???');
console.log(context.state.globalVersionOption?.text || '???');
context.state.didOutputHelpOrVersionText = true;
helperDebug('gracefully exited wrapper handler function for %O', config.name);
throw new _error.GracefulEarlyExitError();
} else {
if (!meta.isImplemented && meta.hasChildren) {
helperDebug.message('short-circuited effector: command is both unimplemented and has children');
throw new _error.CommandNotImplementedError();
}
const localArgv = await programs.effector.parseAsync(context.state.rawArgv, (0, _util.wrapExecutionContext)(context));
(0, _nodeAssert.default)(context.state.deepestParseResult === undefined, _error.BfErrorMessage.GuruMeditation());
context.state.deepestParseResult = localArgv;
helperDebug('context.state.deepestParseResult set to EffectorProgram::parseAsync result: %O', localArgv);
helperDebug('exited wrapper handler function for %O', config.name);
}
}, [], config.deprecated);
programs.effector.command_deferred([config.command, ...config.aliases], false, makeVanillaYargsBuilder(programs.effector, config, 'second-pass'), config.handler, [], config.deprecated);
makerDebug('created and fully configured the effector, helper, and router programs for %O command %O', type, fullName);
return programs;
function customFailHandler(descriptor) {
return function (message, error) {
const descriptorDebug = discoverDebug.extend(`${descriptor}@`);
descriptorDebug.message('entered failure handler for command %O', fullName);
const {
showHelpOnFail
} = context.state;
descriptorDebug('message: %O', message);
descriptorDebug('error.message: %O', error?.message);
descriptorDebug('error is native error: %O', (0, _types.isNativeError)(error));
const isErrorCliError = (0, _error.isCliError)(error);
descriptorDebug('error is CliError: %O', isErrorCliError);
const isErrorVanillaYargsError = (0, _error.isYargsError)(error) || !error;
descriptorDebug('error is vanilla yargs error: %O', isErrorVanillaYargsError);
const isErrorOtherError = !isErrorCliError && !isErrorVanillaYargsError;
descriptorDebug('error is other error: %O', isErrorOtherError);
descriptorDebug('is allowed to show help on fail: %O (%O)', !!showHelpOnFail, showHelpOnFail);
descriptorDebug('already output help or version text: %O', context.state.didOutputHelpOrVersionText);
(0, _nodeAssert.default)(!meta.hasChildren || type !== 'pure child', _error.BfErrorMessage.GuruMeditation());
const isParousParentHelperHandlingCommandNotImplementedError = meta.hasChildren && descriptor === 'helper' && (0, _error.isCommandNotImplementedError)(error);
descriptorDebug('is parous parent helper handling CommandNotImplementedError: %O', isParousParentHelperHandlingCommandNotImplementedError);
const showForYargsErrors = showHelpOnFail !== false && (typeof showHelpOnFail !== 'object' || (showHelpOnFail.showFor?.yargs ?? true));
const showForCliErrors = showHelpOnFail !== false && (typeof showHelpOnFail !== 'object' || (showHelpOnFail.showFor?.cli ?? false));
const showForOtherErrors = showHelpOnFail !== false && (typeof showHelpOnFail !== 'object' || (showHelpOnFail.showFor?.other ?? false));
descriptorDebug('show help text for yargs errors: %O', showForYargsErrors);
descriptorDebug('show help text for CliError errors: %O', showForCliErrors);
descriptorDebug('show help text for other errors: %O', showForOtherErrors);
const shouldShowHelp = isParousParentHelperHandlingCommandNotImplementedError || isErrorVanillaYargsError && showForYargsErrors || isErrorCliError && (error.showHelp === 'default' && showForCliErrors || error.showHelp !== 'default' && error.showHelp) || isErrorOtherError && showForOtherErrors;
descriptorDebug('will show help text: %O', shouldShowHelp);
if (shouldShowHelp && !context.state.didOutputHelpOrVersionText) {
const outputStyle = isErrorCliError && error.showHelp !== 'default' ? error.showHelp !== 'full' ? 'short' : 'full' : typeof showHelpOnFail === 'string' ? showHelpOnFail : (typeof showHelpOnFail === 'object' ? showHelpOnFail.outputStyle : undefined) || 'short';
descriptorDebug(`sending %O help text to stderr (triggered by %O)`, outputStyle, isErrorOtherError ? 'external' : isErrorVanillaYargsError ? 'yargs' : 'black flag');
setContextualUsageTextFor(meta.fullUsageText, outputStyle);
try {
programs.effector.showHelp('error');
context.state.didOutputHelpOrVersionText = true;
} catch (error) {
descriptorDebug.warn('effector.showHelp threw the following error (which was ignored) while attempting to generate help text for a different error: %O', error);
descriptorDebug.message('will attempt to generate help text using the helper instead');
try {
programs.helper.showHelp('error');
context.state.didOutputHelpOrVersionText = true;
} catch (error) {
descriptorDebug.warn('helper.showHelp somehow threw the following error (which was ignored) while attempting to generate help text for a different error: %O', error);
descriptorDebug.message('giving up attempt to generate help text');
}
}
if (isParousParentHelperHandlingCommandNotImplementedError) {
descriptorDebug('exited failure handler: set finalError to CliError(InvalidSubCommandInvocation)');
context.state.finalError = new _error.CliError(_error.BfErrorMessage.InvalidSubCommandInvocation());
}
}
if (context.state.finalError === undefined) {
if (isErrorCliError) {
descriptorDebug('exited failure handler: set finalError to error as-is');
context.state.finalError = error;
} else {
descriptorDebug('exited failure handler: set finalError to error wrapped with CliError');
context.state.finalError = new _error.CliError(error || message || _error.BfErrorMessage.GuruMeditation());
}
} else {
descriptorDebug('exited failure handler: finalError unchanged');
}
throw context.state.finalError;
};
}
function setContextualUsageTextFor(fullUsageText, fullOrShort) {
const updatedUsage = fullOrShort === 'short' ? fullUsageText.split('\n')[0] : fullUsageText;
programs.helper.usage(null);
programs.effector.usage(null);
programs.helper.usage(updatedUsage);
programs.effector.usage(updatedUsage);
}
}
async function makePartiallyConfiguredProgram(descriptor) {
const makerDebug = discoverDebug.extend('make:program:partial');
const deferredCommandArgs = [];
makerDebug('creating new proto-%O program (awaiting full configuration)', descriptor);
const {
default: makeVanillaYargs
} = await import('yargs/yargs');
const vanillaYargs = makeVanillaYargs();
vanillaYargs.help(false);
vanillaYargs.version(false);
if (descriptor === 'router') {
vanillaYargs.scriptName('[router]').usage('[router]').strict(false).exitProcess(false).fail(false);
} else if (context.state.globalHelpOption?.name.length) {
const {
name: helpOptionName,
description: helpOptionDescription
} = context.state.globalHelpOption;
(0, _nodeAssert.default)(helpOptionName.length, _error.BfErrorMessage.GuruMeditation());
vanillaYargs.option(helpOptionName, {
boolean: true,
description: helpOptionDescription
});
}
if (descriptor === 'helper') {
vanillaYargs.strict(false);
}
vanillaYargs.exitProcess(false);
vanillaYargs.showHelpOnFail(false);
return new Proxy(vanillaYargs, {
get(target, property, proxy) {
const isOwnProperty = Object.hasOwn(vanillaYargs, property) || Object.hasOwn(Object.getPrototypeOf(vanillaYargs), property);
if (['help', 'version'].includes(property)) {
return function () {
throw new _error.AssertionFailedError(_error.BfErrorMessage.InvocationNotAllowed(property));
};
}
if (property === 'parseSync') {
return function () {
throw new _error.AssertionFailedError(_error.BfErrorMessage.UseParseAsyncInstead());
};
}
if (property === 'argv') {
makerDebug.warn(`discarded attempted access to disabled "${property}" magic property on %O program`, descriptor);
return void 'disabled by Black Flag (use parseAsync instead)';
}
if (descriptor === 'helper' && typeof property === 'string') {
if (property.startsWith('strict') || property.startsWith('demand')) {
return function () {
makerDebug(`discarded attempted access to disabled method "${property}" on %O program`, descriptor);
return proxy;
};
}
if (property === 'option' || property === 'options') {
return function (...args) {
if (!args.length) {
target.options(...args);
return proxy;
}
const options = args.length === 1 ? args[0] : {
[args[0]]: args[1] || {}
};
const optionsShallowClone = Object.fromEntries(Object.entries(options).map(([option, optionConfiguration]) => {
if ('demandOption' in optionConfiguration) {
makerDebug(`discarded attempted mutation of disabled yargs option configuration key "demandOption" (for the %O option) on %O program`, option, descriptor);
}
const {
demandOption: _,
...rest
} = optionConfiguration;
return [option, rest];
}));
target.options(optionsShallowClone);
return proxy;
};
}
}
if (descriptor === 'router') {
if (isOwnProperty && !['parseAsync', 'command'].includes(property)) {
return typeof target[property] === 'function' ? function () {
throw new _error.AssertionFailedError(_error.BfErrorMessage.InvocationNotAllowed(String(property)));
} : void 'disabled by Black Flag (do not access routers directly)';
}
} else {
if (property === 'command_deferred') {
return function (...args) {
makerDebug('::command call was deferred for %O program', descriptor);
deferredCommandArgs.push(args);
return proxy;
};
}
if (property === 'command_finalize_deferred') {
return function () {
makerDebug('began alpha-sorting deferred command calls for %O program', descriptor);
deferredCommandArgs.sort(([firstCommands], [secondCommands]) => {
const firstCommand = [firstCommands].flat()[0];
const secondCommand = [secondCommands].flat()[0];
return firstCommand?.startsWith('$0') ? -1 : secondCommand?.startsWith('$0') ? 1 : alphaSort.compare(String(firstCommand), String(secondCommand));
});
makerDebug('calling ::command with %O deferred argument tuples for %O program', deferredCommandArgs.length, descriptor);
for (const args of deferredCommandArgs) {
target.command(...args);
}
return proxy;
};
}
if (property === 'showHelpOnFail') {
return function (enabled) {
context.state.showHelpOnFail = enabled;
return proxy;
};
}
}
const value = target[property];
if (typeof value === 'function') {
return function (...args) {
const returnValue = value.apply(target, args);
return (0, _types.isPromise)(returnValue) ? returnValue.then(realReturnValue => maybeReturnProxy(realReturnValue)) : maybeReturnProxy(returnValue);
};
}
return value;
function maybeReturnProxy(returnValue) {
return returnValue === target ? proxy : returnValue;
}
}
});
}
function linkChildRouterToParentRouter(childRouter, childConfig, childFullName, parentRouter) {
parentRouter?.command([childConfig.name, ...childConfig.aliases], '[routed-2]', {}, async function () {
const routerDebug = discoverDebug.extend('router');
const givenName = context.state.rawArgv.shift();
if (routerDebug.enabled) {
const splitName = childFullName.split(' ');
routerDebug.message('entered router handler function bridging %O ==> %O', splitName.slice(0, -1).join(' '), splitName.at(-1));
routerDebug('shifted given name off rawArgv: %O', givenName);
routerDebug('new context.state.rawArgv: %O', context.state.rawArgv);
routerDebug("relinquishing control to child command's router program");
}
context.state.isHandlingVersionOption = false;
await childRouter.parseAsync(context.state.rawArgv, (0, _util.wrapExecutionContext)(context));
}, [], childConfig.deprecated);
if (parentRouter) {
discoverDebug("linked child command %O router program to its parent command's router program", childFullName);
}
}
function addChildCommandToParent(childConfig, childFullName, parents) {
if (parents) {
[parents.helper, parents.effector].forEach((parent, index) => {
parent.command_deferred([childConfig.name, ...childConfig.aliases], childConfig.description, makeVanillaYargsBuilder(parent, childConfig, 'second-pass'), async _parsedArgv => {
throw new _error.AssertionFailedError(_error.BfErrorMessage.GuruMeditation());
}, [], childConfig.deprecated);
discoverDebug(`added an entry for child command %O to its parent command's ${index === 0 ? 'helper' : 'effector'} program`, childFullName);
});
}
}
function makeVanillaYargsBuilder(program, config, pass) {
return (vanillaYargs, helpOrVersionSet) => {
const yargsDebug = discoverDebug.extend(pass === 'first-pass' ? 'helper' : 'effector');
yargsDebug(`entered wrapper builder function (${pass}) for %O`, config.name);
(0, _nodeAssert.default)(pass === 'first-pass' ? context.state.firstPassArgv === undefined : context.state.firstPassArgv !== undefined, _error.BfErrorMessage.BuilderCalledOnInvalidPass(pass));
try {
const blackFlagBuilderResult = typeof config.builder === 'function' ? config.builder(program, context.state.isHandlingHelpOption || context.state.isHandlingVersionOption || helpOrVersionSet, context.state.firstPassArgv) : config.builder;
(0, _nodeAssert.default)(!(0, _types.isPromise)(blackFlagBuilderResult), _error.BfErrorMessage.BuilderCannotBeAsync(config.name));
if (blackFlagBuilderResult && blackFlagBuilderResult !== program) {
program.options(blackFlagBuilderResult);
}
yargsDebug(`exited wrapper builder function (${pass}) for %O (no errors)`, config.name);
} catch (error) {
yargsDebug.warn(`exited wrapper builder function (${pass}) for %O (with error)`, config.name);
throw error;
} finally {
context.state.firstPassArgv = undefined;
}
return vanillaYargs;
};
}
}
function defaultCommandHandler() {
throw new _error.CommandNotImplementedError();
}
function replaceSpaces(str) {
return str.replaceAll(/\s/g, '-');
}
function isModuleNotFoundSystemError(error) {
return (0, _types.isNativeError)(error) && 'code' in error && (error.code === 'ERR_MODULE_NOT_FOUND' && 'url' in error || error.code === 'MODULE_NOT_FOUND' && 'moduleName' in error);
}
function isUnknownFileExtensionSystemError(error) {
return (0, _types.isNativeError)(error) && 'code' in error && error.code === 'ERR_UNKNOWN_FILE_EXTENSION';
}
function makeRequiredPositionalsOptional(dsl) {
return dsl.replaceAll(/<([^>]*)>/g, '[$1]');
}