rushstack
rushstack copied to clipboard
[ts-command-line] Investigate replacing argparse with built-in util.parseArgs
trafficstars
Summary
Node's built-in util.parseArgs functionality appears to be sufficiently feature-complete that we should be able to remove our dependency on argparse in favor of it:
https://nodejs.org/docs/latest-v22.x/api/util.html#utilparseargsconfig
Details
The integer and choice parameter types would need to be implemented by layering additional parameter validation on top of underlying string parameter types, but that's pretty much how it works anyway.
The concept of actions is not built in, but a sample of how that can be accomplished:
const { parseArgs } = require('node:util');
const rushOptions = {
debug: {
type: 'boolean',
default: false
},
quiet: {
type: 'boolean',
default: false
}
};
const phasedOptions = {
to: {
type: 'string',
multiple: true,
short: 't'
},
'to-except': {
type: 'string',
multiple: true,
short: 'T'
},
from: {
type: 'string',
multiple: true,
short: 'f'
},
'from-except': {
type: 'string',
multiple: true,
short: 'F'
},
only: {
type: 'string',
multiple: true,
short: 'o'
},
'impacted-by': {
type: 'string',
multiple: true,
short: 'i'
},
'impacted-by-except': {
type: 'string',
multiple: true,
short: 'I'
},
verbose: {
type: 'boolean',
default: false,
short: 'v'
}
}
const buildOptions = {
...phasedOptions,
production: {
type: 'boolean',
default: false
}
}
const testOptions = {
...phasedOptions,
'disable-code-coverage': {
type: 'boolean',
default: false
},
'update-snapshots': {
type: 'boolean',
default: false
}
}
const actions = {
build: buildOptions,
test: testOptions
}
function parseStrict(args, options) {
const { tokens } = parseArgs({ args, options, tokens: true, strict: false, allowPositionals: true });
const firstPositional = tokens.findIndex(token => token.kind === 'positional');
const { values } = parseArgs({ args: args.slice(0, firstPositional), options, strict: true, allowPositionals: false });
return {
options: values,
action: tokens[firstPositional]?.value,
remainder: args.slice(firstPositional + 1)
};
}
function parseRushArgs(args) {
const {
options,
action,
remainder
} = parseStrict(args, rushOptions);
const actionOptions = actions[action];
if (!actionOptions) {
throw new Error(`Unknown action: ${action}`);
}
const {
values
} = parseArgs({ args: remainder, options: actionOptions, strict: true, allowPositionals: false });
return {
rushOptions: options,
action,
actionOptions: values
};
}
console.log(parseRushArgs(process.argv.slice(2)));
node .\parser.js --debug build --only sp-loader -T spfx-heft-plugins --verbose --
{
rushOptions: [Object: null prototype] { debug: true, quiet: false },
action: 'build',
actionOptions: [Object: null prototype] {
only: [ 'sp-loader' ],
'to-except': [ 'spfx-heft-plugins' ],
verbose: true,
production: false
}
}