CoolFace
Apppublic

Umama-at-Bluchip/Quick-UI

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
index.js397 linesDownload Raw Back to parseargs
1'use strict';2 3const {4  ArrayPrototypeForEach,5  ArrayPrototypeIncludes,6  ArrayPrototypeMap,7  ArrayPrototypePush,8  ArrayPrototypePushApply,9  ArrayPrototypeShift,10  ArrayPrototypeSlice,11  ArrayPrototypeUnshiftApply,12  ObjectEntries,13  ObjectPrototypeHasOwnProperty: ObjectHasOwn,14  StringPrototypeCharAt,15  StringPrototypeIndexOf,16  StringPrototypeSlice,17  StringPrototypeStartsWith,18} = require('./internal/primordials');19 20const {21  validateArray,22  validateBoolean,23  validateBooleanArray,24  validateObject,25  validateString,26  validateStringArray,27  validateUnion,28} = require('./internal/validators');29 30const {31  kEmptyObject,32} = require('./internal/util');33 34const {35  findLongOptionForShort,36  isLoneLongOption,37  isLoneShortOption,38  isLongOptionAndValue,39  isOptionValue,40  isOptionLikeValue,41  isShortOptionAndValue,42  isShortOptionGroup,43  useDefaultValueOption,44  objectGetOwn,45  optionsGetOwn,46} = require('./utils');47 48const {49  codes: {50    ERR_INVALID_ARG_VALUE,51    ERR_PARSE_ARGS_INVALID_OPTION_VALUE,52    ERR_PARSE_ARGS_UNKNOWN_OPTION,53    ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL,54  },55} = require('./internal/errors');56 57function getMainArgs() {58  // Work out where to slice process.argv for user supplied arguments.59 60  // Check node options for scenarios where user CLI args follow executable.61  const execArgv = process.execArgv;62  if (ArrayPrototypeIncludes(execArgv, '-e') ||63      ArrayPrototypeIncludes(execArgv, '--eval') ||64      ArrayPrototypeIncludes(execArgv, '-p') ||65      ArrayPrototypeIncludes(execArgv, '--print')) {66    return ArrayPrototypeSlice(process.argv, 1);67  }68 69  // Normally first two arguments are executable and script, then CLI arguments70  return ArrayPrototypeSlice(process.argv, 2);71}72 73/**74 * In strict mode, throw for possible usage errors like --foo --bar75 *76 * @param {object} token - from tokens as available from parseArgs77 */78function checkOptionLikeValue(token) {79  if (!token.inlineValue && isOptionLikeValue(token.value)) {80    // Only show short example if user used short option.81    const example = StringPrototypeStartsWith(token.rawName, '--') ?82      `'${token.rawName}=-XYZ'` :83      `'--${token.name}=-XYZ' or '${token.rawName}-XYZ'`;84    const errorMessage = `Option '${token.rawName}' argument is ambiguous.85Did you forget to specify the option argument for '${token.rawName}'?86To specify an option argument starting with a dash use ${example}.`;87    throw new ERR_PARSE_ARGS_INVALID_OPTION_VALUE(errorMessage);88  }89}90 91/**92 * In strict mode, throw for usage errors.93 *94 * @param {object} config - from config passed to parseArgs95 * @param {object} token - from tokens as available from parseArgs96 */97function checkOptionUsage(config, token) {98  if (!ObjectHasOwn(config.options, token.name)) {99    throw new ERR_PARSE_ARGS_UNKNOWN_OPTION(100      token.rawName, config.allowPositionals);101  }102 103  const short = optionsGetOwn(config.options, token.name, 'short');104  const shortAndLong = `${short ? `-${short}, ` : ''}--${token.name}`;105  const type = optionsGetOwn(config.options, token.name, 'type');106  if (type === 'string' && typeof token.value !== 'string') {107    throw new ERR_PARSE_ARGS_INVALID_OPTION_VALUE(`Option '${shortAndLong} <value>' argument missing`);108  }109  // (Idiomatic test for undefined||null, expecting undefined.)110  if (type === 'boolean' && token.value != null) {111    throw new ERR_PARSE_ARGS_INVALID_OPTION_VALUE(`Option '${shortAndLong}' does not take an argument`);112  }113}114 115 116/**117 * Store the option value in `values`.118 *119 * @param {string} longOption - long option name e.g. 'foo'120 * @param {string|undefined} optionValue - value from user args121 * @param {object} options - option configs, from parseArgs({ options })122 * @param {object} values - option values returned in `values` by parseArgs123 */124function storeOption(longOption, optionValue, options, values) {125  if (longOption === '__proto__') {126    return; // No. Just no.127  }128 129  // We store based on the option value rather than option type,130  // preserving the users intent for author to deal with.131  const newValue = optionValue ?? true;132  if (optionsGetOwn(options, longOption, 'multiple')) {133    // Always store value in array, including for boolean.134    // values[longOption] starts out not present,135    // first value is added as new array [newValue],136    // subsequent values are pushed to existing array.137    // (note: values has null prototype, so simpler usage)138    if (values[longOption]) {139      ArrayPrototypePush(values[longOption], newValue);140    } else {141      values[longOption] = [newValue];142    }143  } else {144    values[longOption] = newValue;145  }146}147 148/**149 * Store the default option value in `values`.150 *151 * @param {string} longOption - long option name e.g. 'foo'152 * @param {string153 *         | boolean154 *         | string[]155 *         | boolean[]} optionValue - default value from option config156 * @param {object} values - option values returned in `values` by parseArgs157 */158function storeDefaultOption(longOption, optionValue, values) {159  if (longOption === '__proto__') {160    return; // No. Just no.161  }162 163  values[longOption] = optionValue;164}165 166/**167 * Process args and turn into identified tokens:168 * - option (along with value, if any)169 * - positional170 * - option-terminator171 *172 * @param {string[]} args - from parseArgs({ args }) or mainArgs173 * @param {object} options - option configs, from parseArgs({ options })174 */175function argsToTokens(args, options) {176  const tokens = [];177  let index = -1;178  let groupCount = 0;179 180  const remainingArgs = ArrayPrototypeSlice(args);181  while (remainingArgs.length > 0) {182    const arg = ArrayPrototypeShift(remainingArgs);183    const nextArg = remainingArgs[0];184    if (groupCount > 0)185      groupCount--;186    else187      index++;188 189    // Check if `arg` is an options terminator.190    // Guideline 10 in https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html191    if (arg === '--') {192      // Everything after a bare '--' is considered a positional argument.193      ArrayPrototypePush(tokens, { kind: 'option-terminator', index });194      ArrayPrototypePushApply(195        tokens, ArrayPrototypeMap(remainingArgs, (arg) => {196          return { kind: 'positional', index: ++index, value: arg };197        })198      );199      break; // Finished processing args, leave while loop.200    }201 202    if (isLoneShortOption(arg)) {203      // e.g. '-f'204      const shortOption = StringPrototypeCharAt(arg, 1);205      const longOption = findLongOptionForShort(shortOption, options);206      let value;207      let inlineValue;208      if (optionsGetOwn(options, longOption, 'type') === 'string' &&209          isOptionValue(nextArg)) {210        // e.g. '-f', 'bar'211        value = ArrayPrototypeShift(remainingArgs);212        inlineValue = false;213      }214      ArrayPrototypePush(215        tokens,216        { kind: 'option', name: longOption, rawName: arg,217          index, value, inlineValue });218      if (value != null) ++index;219      continue;220    }221 222    if (isShortOptionGroup(arg, options)) {223      // Expand -fXzy to -f -X -z -y224      const expanded = [];225      for (let index = 1; index < arg.length; index++) {226        const shortOption = StringPrototypeCharAt(arg, index);227        const longOption = findLongOptionForShort(shortOption, options);228        if (optionsGetOwn(options, longOption, 'type') !== 'string' ||229          index === arg.length - 1) {230          // Boolean option, or last short in group. Well formed.231          ArrayPrototypePush(expanded, `-${shortOption}`);232        } else {233          // String option in middle. Yuck.234          // Expand -abfFILE to -a -b -fFILE235          ArrayPrototypePush(expanded, `-${StringPrototypeSlice(arg, index)}`);236          break; // finished short group237        }238      }239      ArrayPrototypeUnshiftApply(remainingArgs, expanded);240      groupCount = expanded.length;241      continue;242    }243 244    if (isShortOptionAndValue(arg, options)) {245      // e.g. -fFILE246      const shortOption = StringPrototypeCharAt(arg, 1);247      const longOption = findLongOptionForShort(shortOption, options);248      const value = StringPrototypeSlice(arg, 2);249      ArrayPrototypePush(250        tokens,251        { kind: 'option', name: longOption, rawName: `-${shortOption}`,252          index, value, inlineValue: true });253      continue;254    }255 256    if (isLoneLongOption(arg)) {257      // e.g. '--foo'258      const longOption = StringPrototypeSlice(arg, 2);259      let value;260      let inlineValue;261      if (optionsGetOwn(options, longOption, 'type') === 'string' &&262          isOptionValue(nextArg)) {263        // e.g. '--foo', 'bar'264        value = ArrayPrototypeShift(remainingArgs);265        inlineValue = false;266      }267      ArrayPrototypePush(268        tokens,269        { kind: 'option', name: longOption, rawName: arg,270          index, value, inlineValue });271      if (value != null) ++index;272      continue;273    }274 275    if (isLongOptionAndValue(arg)) {276      // e.g. --foo=bar277      const equalIndex = StringPrototypeIndexOf(arg, '=');278      const longOption = StringPrototypeSlice(arg, 2, equalIndex);279      const value = StringPrototypeSlice(arg, equalIndex + 1);280      ArrayPrototypePush(281        tokens,282        { kind: 'option', name: longOption, rawName: `--${longOption}`,283          index, value, inlineValue: true });284      continue;285    }286 287    ArrayPrototypePush(tokens, { kind: 'positional', index, value: arg });288  }289 290  return tokens;291}292 293const parseArgs = (config = kEmptyObject) => {294  const args = objectGetOwn(config, 'args') ?? getMainArgs();295  const strict = objectGetOwn(config, 'strict') ?? true;296  const allowPositionals = objectGetOwn(config, 'allowPositionals') ?? !strict;297  const returnTokens = objectGetOwn(config, 'tokens') ?? false;298  const options = objectGetOwn(config, 'options') ?? { __proto__: null };299  // Bundle these up for passing to strict-mode checks.300  const parseConfig = { args, strict, options, allowPositionals };301 302  // Validate input configuration.303  validateArray(args, 'args');304  validateBoolean(strict, 'strict');305  validateBoolean(allowPositionals, 'allowPositionals');306  validateBoolean(returnTokens, 'tokens');307  validateObject(options, 'options');308  ArrayPrototypeForEach(309    ObjectEntries(options),310    ({ 0: longOption, 1: optionConfig }) => {311      validateObject(optionConfig, `options.${longOption}`);312 313      // type is required314      const optionType = objectGetOwn(optionConfig, 'type');315      validateUnion(optionType, `options.${longOption}.type`, ['string', 'boolean']);316 317      if (ObjectHasOwn(optionConfig, 'short')) {318        const shortOption = optionConfig.short;319        validateString(shortOption, `options.${longOption}.short`);320        if (shortOption.length !== 1) {321          throw new ERR_INVALID_ARG_VALUE(322            `options.${longOption}.short`,323            shortOption,324            'must be a single character'325          );326        }327      }328 329      const multipleOption = objectGetOwn(optionConfig, 'multiple');330      if (ObjectHasOwn(optionConfig, 'multiple')) {331        validateBoolean(multipleOption, `options.${longOption}.multiple`);332      }333 334      const defaultValue = objectGetOwn(optionConfig, 'default');335      if (defaultValue !== undefined) {336        let validator;337        switch (optionType) {338          case 'string':339            validator = multipleOption ? validateStringArray : validateString;340            break;341 342          case 'boolean':343            validator = multipleOption ? validateBooleanArray : validateBoolean;344            break;345        }346        validator(defaultValue, `options.${longOption}.default`);347      }348    }349  );350 351  // Phase 1: identify tokens352  const tokens = argsToTokens(args, options);353 354  // Phase 2: process tokens into parsed option values and positionals355  const result = {356    values: { __proto__: null },357    positionals: [],358  };359  if (returnTokens) {360    result.tokens = tokens;361  }362  ArrayPrototypeForEach(tokens, (token) => {363    if (token.kind === 'option') {364      if (strict) {365        checkOptionUsage(parseConfig, token);366        checkOptionLikeValue(token);367      }368      storeOption(token.name, token.value, options, result.values);369    } else if (token.kind === 'positional') {370      if (!allowPositionals) {371        throw new ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL(token.value);372      }373      ArrayPrototypePush(result.positionals, token.value);374    }375  });376 377  // Phase 3: fill in default values for missing args378  ArrayPrototypeForEach(ObjectEntries(options), ({ 0: longOption,379                                                   1: optionConfig }) => {380    const mustSetDefault = useDefaultValueOption(longOption,381                                                 optionConfig,382                                                 result.values);383    if (mustSetDefault) {384      storeDefaultOption(longOption,385                         objectGetOwn(optionConfig, 'default'),386                         result.values);387    }388  });389 390 391  return result;392};393 394module.exports = {395  parseArgs,396};397