export function parseArguments()

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;
}