in packages/angular/cli/models/parser.ts [290:405]
export function parseArguments(
args: string[],
options: Option[] | null,
logger?: logging.Logger,
): Arguments {
if (options === null) {
options = [];
}
const leftovers: string[] = [];
const positionals: string[] = [];
const parsedOptions: Arguments = {};
const ignored: string[] = [];
const errors: string[] = [];
const warnings: string[] = [];
const state = { options, parsedOptions, positionals, leftovers, ignored, errors, warnings };
for (let argIndex = 0; argIndex < args.length; argIndex++) {
const arg = args[argIndex];
let consumedNextArg = false;
if (arg == '--') {
// If we find a --, we're done.
leftovers.push(...args.slice(argIndex + 1));
break;
}
if (arg.startsWith('--')) {
consumedNextArg = _assignOption(arg, args[argIndex + 1], state);
} else if (arg.startsWith('-')) {
// Argument is of form -abcdef. Starts at 1 because we skip the `-`.
for (let i = 1; i < arg.length; i++) {
const flag = arg[i];
// If the next character is an '=', treat it as a long flag.
if (arg[i + 1] == '=') {
const f = '-' + flag + arg.slice(i + 1);
consumedNextArg = _assignOption(f, args[argIndex + 1], state);
break;
}
// Treat the last flag as `--a` (as if full flag but just one letter). We do this in
// the loop because it saves us a check to see if the arg is just `-`.
if (i == arg.length - 1) {
const arg = '-' + flag;
consumedNextArg = _assignOption(arg, args[argIndex + 1], state);
} else {
const maybeOption = _getOptionFromName(flag, options);
if (maybeOption) {
const v = _coerce(undefined, maybeOption, parsedOptions[maybeOption.name]);
if (v !== undefined) {
parsedOptions[maybeOption.name] = v;
}
}
}
}
} else {
positionals.push(arg);
}
if (consumedNextArg) {
argIndex++;
}
}
// Deal with positionals.
// TODO(hansl): this is by far the most complex piece of code in this file. Try to refactor it
// simpler.
if (positionals.length > 0) {
let pos = 0;
for (let i = 0; i < positionals.length; ) {
let found = false;
let incrementPos = false;
let incrementI = true;
// We do this with a found flag because more than 1 option could have the same positional.
for (const option of options) {
// If any option has this positional and no value, AND fit the type, we need to remove it.
if (option.positional === pos) {
const coercedValue = _coerce(positionals[i], option, parsedOptions[option.name]);
if (parsedOptions[option.name] === undefined && coercedValue !== undefined) {
parsedOptions[option.name] = coercedValue;
found = true;
} else {
incrementI = false;
}
incrementPos = true;
}
}
if (found) {
positionals.splice(i--, 1);
}
if (incrementPos) {
pos++;
}
if (incrementI) {
i++;
}
}
}
if (positionals.length > 0 || leftovers.length > 0) {
parsedOptions['--'] = [...positionals, ...leftovers];
}
if (warnings.length > 0 && logger) {
warnings.forEach((message) => logger.warn(message));
}
if (errors.length > 0) {
throw new ParseArgumentException(errors, parsedOptions, ignored);
}
return parsedOptions;
}