mirror of
https://github.com/denoland/deno.git
synced 2024-12-24 08:09:08 -05:00
fix(ext/node): add util.parseArgs (#21342)
This commit is contained in:
parent
75ec650f08
commit
e332fa4a83
10 changed files with 1595 additions and 1 deletions
|
@ -430,6 +430,7 @@
|
|||
"test-nodeeventtarget.js",
|
||||
"test-outgoing-message-destroy.js",
|
||||
"test-outgoing-message-pipe.js",
|
||||
"test-parse-args.mjs",
|
||||
"test-path-basename.js",
|
||||
"test-path-dirname.js",
|
||||
"test-path-extname.js",
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
import "./polyfill_globals.js";
|
||||
import { createRequire } from "node:module";
|
||||
import { toFileUrl } from "../../../test_util/std/path/mod.ts";
|
||||
const file = Deno.args[0];
|
||||
if (!file) {
|
||||
throw new Error("No file provided");
|
||||
}
|
||||
|
||||
if (file.endsWith(".mjs")) {
|
||||
await import(toFileUrl(file).href);
|
||||
} else {
|
||||
createRequire(import.meta.url)(file);
|
||||
}
|
||||
|
|
|
@ -83,6 +83,7 @@ async function runTest(t: Deno.TestContext, path: string): Promise<void> {
|
|||
"--quiet",
|
||||
"--unstable",
|
||||
//"--unsafely-ignore-certificate-errors",
|
||||
"--unstable-bare-node-builtins",
|
||||
"--v8-flags=" + v8Flags.join(),
|
||||
"runner.ts",
|
||||
testCase,
|
||||
|
|
1001
cli/tests/node_compat/test/parallel/test-parse-args.mjs
Normal file
1001
cli/tests/node_compat/test/parallel/test-parse-args.mjs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -463,6 +463,8 @@ deno_core::extension!(deno_node,
|
|||
"internal/util/comparisons.ts",
|
||||
"internal/util/debuglog.ts",
|
||||
"internal/util/inspect.mjs",
|
||||
"internal/util/parse_args/parse_args.js",
|
||||
"internal/util/parse_args/utils.js",
|
||||
"internal/util/types.ts",
|
||||
"internal/validators.mjs",
|
||||
"path/_constants.ts",
|
||||
|
|
|
@ -2472,6 +2472,36 @@ export class ERR_PACKAGE_PATH_NOT_EXPORTED extends NodeError {
|
|||
}
|
||||
}
|
||||
|
||||
export class ERR_PARSE_ARGS_INVALID_OPTION_VALUE extends NodeTypeError {
|
||||
constructor(x: string) {
|
||||
super("ERR_PARSE_ARGS_INVALID_OPTION_VALUE", x);
|
||||
}
|
||||
}
|
||||
|
||||
export class ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL extends NodeTypeError {
|
||||
constructor(x: string) {
|
||||
super(
|
||||
"ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL",
|
||||
`Unexpected argument '${x}'. This ` +
|
||||
`command does not take positional arguments`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class ERR_PARSE_ARGS_UNKNOWN_OPTION extends NodeTypeError {
|
||||
constructor(option, allowPositionals) {
|
||||
const suggestDashDash = allowPositionals
|
||||
? ". To specify a positional " +
|
||||
"argument starting with a '-', place it at the end of the command after " +
|
||||
`'--', as in '-- ${JSONStringify(option)}`
|
||||
: "";
|
||||
super(
|
||||
"ERR_PARSE_ARGS_UNKNOWN_OPTION",
|
||||
`Unknown option '${option}'${suggestDashDash}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class ERR_INTERNAL_ASSERTION extends NodeError {
|
||||
constructor(message?: string) {
|
||||
const suffix = "This is caused by either a bug in Node.js " +
|
||||
|
|
345
ext/node/polyfills/internal/util/parse_args/parse_args.js
Normal file
345
ext/node/polyfills/internal/util/parse_args/parse_args.js
Normal file
|
@ -0,0 +1,345 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
// Copyright Joyent and Node contributors. All rights reserved. MIT license.
|
||||
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const {
|
||||
ArrayPrototypeForEach,
|
||||
ArrayPrototypeIncludes,
|
||||
ArrayPrototypePushApply,
|
||||
ArrayPrototypeShift,
|
||||
ArrayPrototypeSlice,
|
||||
ArrayPrototypePush,
|
||||
ArrayPrototypeUnshiftApply,
|
||||
ObjectHasOwn,
|
||||
ObjectEntries,
|
||||
StringPrototypeCharAt,
|
||||
StringPrototypeIndexOf,
|
||||
StringPrototypeSlice,
|
||||
} = primordials;
|
||||
|
||||
import {
|
||||
validateArray,
|
||||
validateBoolean,
|
||||
validateObject,
|
||||
validateString,
|
||||
validateUnion,
|
||||
} from "ext:deno_node/internal/validators.mjs";
|
||||
|
||||
import {
|
||||
findLongOptionForShort,
|
||||
isLoneLongOption,
|
||||
isLoneShortOption,
|
||||
isLongOptionAndValue,
|
||||
isOptionLikeValue,
|
||||
isOptionValue,
|
||||
isShortOptionAndValue,
|
||||
isShortOptionGroup,
|
||||
objectGetOwn,
|
||||
optionsGetOwn,
|
||||
} from "ext:deno_node/internal/util/parse_args/utils.js";
|
||||
|
||||
import { codes } from "ext:deno_node/internal/error_codes.ts";
|
||||
const {
|
||||
ERR_INVALID_ARG_VALUE,
|
||||
ERR_PARSE_ARGS_INVALID_OPTION_VALUE,
|
||||
ERR_PARSE_ARGS_UNKNOWN_OPTION,
|
||||
ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL,
|
||||
} = codes;
|
||||
|
||||
function getMainArgs() {
|
||||
// Work out where to slice process.argv for user supplied arguments.
|
||||
|
||||
// Check node options for scenarios where user CLI args follow executable.
|
||||
const execArgv = process.execArgv;
|
||||
if (
|
||||
ArrayPrototypeIncludes(execArgv, "-e") ||
|
||||
ArrayPrototypeIncludes(execArgv, "--eval") ||
|
||||
ArrayPrototypeIncludes(execArgv, "-p") ||
|
||||
ArrayPrototypeIncludes(execArgv, "--print")
|
||||
) {
|
||||
return ArrayPrototypeSlice(process.argv, 1);
|
||||
}
|
||||
|
||||
// Normally first two arguments are executable and script, then CLI arguments
|
||||
return ArrayPrototypeSlice(process.argv, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* In strict mode, throw for possible usage errors like --foo --bar
|
||||
*
|
||||
* @param {string} longOption - long option name e.g. 'foo'
|
||||
* @param {string|undefined} optionValue - value from user args
|
||||
* @param {string} shortOrLong - option used, with dashes e.g. `-l` or `--long`
|
||||
* @param {boolean} strict - show errors, from parseArgs({ strict })
|
||||
*/
|
||||
function checkOptionLikeValue(longOption, optionValue, shortOrLong, strict) {
|
||||
if (strict && isOptionLikeValue(optionValue)) {
|
||||
// Only show short example if user used short option.
|
||||
const example = (shortOrLong.length === 2)
|
||||
? `'--${longOption}=-XYZ' or '${shortOrLong}-XYZ'`
|
||||
: `'--${longOption}=-XYZ'`;
|
||||
const errorMessage = `Option '${shortOrLong}' argument is ambiguous.
|
||||
Did you forget to specify the option argument for '${shortOrLong}'?
|
||||
To specify an option argument starting with a dash use ${example}.`;
|
||||
throw new ERR_PARSE_ARGS_INVALID_OPTION_VALUE(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In strict mode, throw for usage errors.
|
||||
*
|
||||
* @param {string} longOption - long option name e.g. 'foo'
|
||||
* @param {string|undefined} optionValue - value from user args
|
||||
* @param {object} options - option configs, from parseArgs({ options })
|
||||
* @param {string} shortOrLong - option used, with dashes e.g. `-l` or `--long`
|
||||
* @param {boolean} strict - show errors, from parseArgs({ strict })
|
||||
* @param {boolean} allowPositionals - from parseArgs({ allowPositionals })
|
||||
*/
|
||||
function checkOptionUsage(
|
||||
longOption,
|
||||
optionValue,
|
||||
options,
|
||||
shortOrLong,
|
||||
strict,
|
||||
allowPositionals,
|
||||
) {
|
||||
// Strict and options are used from local context.
|
||||
if (!strict) return;
|
||||
|
||||
if (!ObjectHasOwn(options, longOption)) {
|
||||
throw new ERR_PARSE_ARGS_UNKNOWN_OPTION(shortOrLong, allowPositionals);
|
||||
}
|
||||
|
||||
const short = optionsGetOwn(options, longOption, "short");
|
||||
const shortAndLong = short ? `-${short}, --${longOption}` : `--${longOption}`;
|
||||
const type = optionsGetOwn(options, longOption, "type");
|
||||
if (type === "string" && typeof optionValue !== "string") {
|
||||
throw new ERR_PARSE_ARGS_INVALID_OPTION_VALUE(
|
||||
`Option '${shortAndLong} <value>' argument missing`,
|
||||
);
|
||||
}
|
||||
// (Idiomatic test for undefined||null, expecting undefined.)
|
||||
if (type === "boolean" && optionValue != null) {
|
||||
throw new ERR_PARSE_ARGS_INVALID_OPTION_VALUE(
|
||||
`Option '${shortAndLong}' does not take an argument`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the option value in `values`.
|
||||
*
|
||||
* @param {string} longOption - long option name e.g. 'foo'
|
||||
* @param {string|undefined} optionValue - value from user args
|
||||
* @param {object} options - option configs, from parseArgs({ options })
|
||||
* @param {object} values - option values returned in `values` by parseArgs
|
||||
*/
|
||||
function storeOption(longOption, optionValue, options, values) {
|
||||
if (longOption === "__proto__") {
|
||||
return; // No. Just no.
|
||||
}
|
||||
|
||||
// We store based on the option value rather than option type,
|
||||
// preserving the users intent for author to deal with.
|
||||
const newValue = optionValue ?? true;
|
||||
if (optionsGetOwn(options, longOption, "multiple")) {
|
||||
// Always store value in array, including for boolean.
|
||||
// values[longOption] starts out not present,
|
||||
// first value is added as new array [newValue],
|
||||
// subsequent values are pushed to existing array.
|
||||
// (note: values has null prototype, so simpler usage)
|
||||
if (values[longOption]) {
|
||||
ArrayPrototypePush(values[longOption], newValue);
|
||||
} else {
|
||||
values[longOption] = [newValue];
|
||||
}
|
||||
} else {
|
||||
values[longOption] = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
export const parseArgs = (config = { __proto__: null }) => {
|
||||
const args = objectGetOwn(config, "args") ?? getMainArgs();
|
||||
const strict = objectGetOwn(config, "strict") ?? true;
|
||||
const allowPositionals = objectGetOwn(config, "allowPositionals") ?? !strict;
|
||||
const options = objectGetOwn(config, "options") ?? { __proto__: null };
|
||||
|
||||
// Validate input configuration.
|
||||
validateArray(args, "args");
|
||||
validateBoolean(strict, "strict");
|
||||
validateBoolean(allowPositionals, "allowPositionals");
|
||||
validateObject(options, "options");
|
||||
ArrayPrototypeForEach(
|
||||
ObjectEntries(options),
|
||||
({ 0: longOption, 1: optionConfig }) => {
|
||||
validateObject(optionConfig, `options.${longOption}`);
|
||||
|
||||
// type is required
|
||||
validateUnion(
|
||||
objectGetOwn(optionConfig, "type"),
|
||||
`options.${longOption}.type`,
|
||||
["string", "boolean"],
|
||||
);
|
||||
|
||||
if (ObjectHasOwn(optionConfig, "short")) {
|
||||
const shortOption = optionConfig.short;
|
||||
validateString(shortOption, `options.${longOption}.short`);
|
||||
if (shortOption.length !== 1) {
|
||||
throw new ERR_INVALID_ARG_VALUE(
|
||||
`options.${longOption}.short`,
|
||||
shortOption,
|
||||
"must be a single character",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (ObjectHasOwn(optionConfig, "multiple")) {
|
||||
validateBoolean(
|
||||
optionConfig.multiple,
|
||||
`options.${longOption}.multiple`,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const result = {
|
||||
values: { __proto__: null },
|
||||
positionals: [],
|
||||
};
|
||||
|
||||
const remainingArgs = ArrayPrototypeSlice(args);
|
||||
while (remainingArgs.length > 0) {
|
||||
const arg = ArrayPrototypeShift(remainingArgs);
|
||||
const nextArg = remainingArgs[0];
|
||||
|
||||
// Check if `arg` is an options terminator.
|
||||
// Guideline 10 in https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html
|
||||
if (arg === "--") {
|
||||
if (!allowPositionals && remainingArgs.length > 0) {
|
||||
throw new ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL(nextArg);
|
||||
}
|
||||
|
||||
// Everything after a bare '--' is considered a positional argument.
|
||||
ArrayPrototypePushApply(
|
||||
result.positionals,
|
||||
remainingArgs,
|
||||
);
|
||||
break; // Finished processing args, leave while loop.
|
||||
}
|
||||
|
||||
if (isLoneShortOption(arg)) {
|
||||
// e.g. '-f'
|
||||
const shortOption = StringPrototypeCharAt(arg, 1);
|
||||
const longOption = findLongOptionForShort(shortOption, options);
|
||||
let optionValue;
|
||||
if (
|
||||
optionsGetOwn(options, longOption, "type") === "string" &&
|
||||
isOptionValue(nextArg)
|
||||
) {
|
||||
// e.g. '-f', 'bar'
|
||||
optionValue = ArrayPrototypeShift(remainingArgs);
|
||||
checkOptionLikeValue(longOption, optionValue, arg, strict);
|
||||
}
|
||||
checkOptionUsage(
|
||||
longOption,
|
||||
optionValue,
|
||||
options,
|
||||
arg,
|
||||
strict,
|
||||
allowPositionals,
|
||||
);
|
||||
storeOption(longOption, optionValue, options, result.values);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isShortOptionGroup(arg, options)) {
|
||||
// Expand -fXzy to -f -X -z -y
|
||||
const expanded = [];
|
||||
for (let index = 1; index < arg.length; index++) {
|
||||
const shortOption = StringPrototypeCharAt(arg, index);
|
||||
const longOption = findLongOptionForShort(shortOption, options);
|
||||
if (
|
||||
optionsGetOwn(options, longOption, "type") !== "string" ||
|
||||
index === arg.length - 1
|
||||
) {
|
||||
// Boolean option, or last short in group. Well formed.
|
||||
ArrayPrototypePush(expanded, `-${shortOption}`);
|
||||
} else {
|
||||
// String option in middle. Yuck.
|
||||
// Expand -abfFILE to -a -b -fFILE
|
||||
ArrayPrototypePush(expanded, `-${StringPrototypeSlice(arg, index)}`);
|
||||
break; // finished short group
|
||||
}
|
||||
}
|
||||
ArrayPrototypeUnshiftApply(remainingArgs, expanded);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isShortOptionAndValue(arg, options)) {
|
||||
// e.g. -fFILE
|
||||
const shortOption = StringPrototypeCharAt(arg, 1);
|
||||
const longOption = findLongOptionForShort(shortOption, options);
|
||||
const optionValue = StringPrototypeSlice(arg, 2);
|
||||
checkOptionUsage(
|
||||
longOption,
|
||||
optionValue,
|
||||
options,
|
||||
`-${shortOption}`,
|
||||
strict,
|
||||
allowPositionals,
|
||||
);
|
||||
storeOption(longOption, optionValue, options, result.values);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isLoneLongOption(arg)) {
|
||||
// e.g. '--foo'
|
||||
const longOption = StringPrototypeSlice(arg, 2);
|
||||
let optionValue;
|
||||
if (
|
||||
optionsGetOwn(options, longOption, "type") === "string" &&
|
||||
isOptionValue(nextArg)
|
||||
) {
|
||||
// e.g. '--foo', 'bar'
|
||||
optionValue = ArrayPrototypeShift(remainingArgs);
|
||||
checkOptionLikeValue(longOption, optionValue, arg, strict);
|
||||
}
|
||||
checkOptionUsage(
|
||||
longOption,
|
||||
optionValue,
|
||||
options,
|
||||
arg,
|
||||
strict,
|
||||
allowPositionals,
|
||||
);
|
||||
storeOption(longOption, optionValue, options, result.values);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isLongOptionAndValue(arg)) {
|
||||
// e.g. --foo=bar
|
||||
const index = StringPrototypeIndexOf(arg, "=");
|
||||
const longOption = StringPrototypeSlice(arg, 2, index);
|
||||
const optionValue = StringPrototypeSlice(arg, index + 1);
|
||||
checkOptionUsage(
|
||||
longOption,
|
||||
optionValue,
|
||||
options,
|
||||
`--${longOption}`,
|
||||
strict,
|
||||
allowPositionals,
|
||||
);
|
||||
storeOption(longOption, optionValue, options, result.values);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Anything left is a positional
|
||||
if (!allowPositionals) {
|
||||
throw new ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL(arg);
|
||||
}
|
||||
|
||||
ArrayPrototypePush(result.positionals, arg);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
187
ext/node/polyfills/internal/util/parse_args/utils.js
Normal file
187
ext/node/polyfills/internal/util/parse_args/utils.js
Normal file
|
@ -0,0 +1,187 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
// Copyright Joyent and Node contributors. All rights reserved. MIT license.
|
||||
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const {
|
||||
ArrayPrototypeFind,
|
||||
ObjectEntries,
|
||||
ObjectHasOwn,
|
||||
StringPrototypeCharAt,
|
||||
StringPrototypeIncludes,
|
||||
StringPrototypeStartsWith,
|
||||
} = primordials;
|
||||
|
||||
import { validateObject } from "ext:deno_node/internal/validators.mjs";
|
||||
|
||||
// These are internal utilities to make the parsing logic easier to read, and
|
||||
// add lots of detail for the curious. They are in a separate file to allow
|
||||
// unit testing, although that is not essential (this could be rolled into
|
||||
// main file and just tested implicitly via API).
|
||||
//
|
||||
// These routines are for internal use, not for export to client.
|
||||
|
||||
/**
|
||||
* Return the named property, but only if it is an own property.
|
||||
*/
|
||||
function objectGetOwn(obj, prop) {
|
||||
if (ObjectHasOwn(obj, prop)) {
|
||||
return obj[prop];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the named options property, but only if it is an own property.
|
||||
*/
|
||||
function optionsGetOwn(options, longOption, prop) {
|
||||
if (ObjectHasOwn(options, longOption)) {
|
||||
return objectGetOwn(options[longOption], prop);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the argument may be used as an option value.
|
||||
* @example
|
||||
* isOptionValue('V') // returns true
|
||||
* isOptionValue('-v') // returns true (greedy)
|
||||
* isOptionValue('--foo') // returns true (greedy)
|
||||
* isOptionValue(undefined) // returns false
|
||||
*/
|
||||
function isOptionValue(value) {
|
||||
if (value == null) return false;
|
||||
|
||||
// Open Group Utility Conventions are that an option-argument
|
||||
// is the argument after the option, and may start with a dash.
|
||||
return true; // greedy!
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect whether there is possible confusion and user may have omitted
|
||||
* the option argument, like `--port --verbose` when `port` of type:string.
|
||||
* In strict mode we throw errors if value is option-like.
|
||||
*/
|
||||
function isOptionLikeValue(value) {
|
||||
if (value == null) return false;
|
||||
|
||||
return value.length > 1 && StringPrototypeCharAt(value, 0) === "-";
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if `arg` is just a short option.
|
||||
* @example '-f'
|
||||
*/
|
||||
function isLoneShortOption(arg) {
|
||||
return arg.length === 2 &&
|
||||
StringPrototypeCharAt(arg, 0) === "-" &&
|
||||
StringPrototypeCharAt(arg, 1) !== "-";
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if `arg` is a lone long option.
|
||||
* @example
|
||||
* isLoneLongOption('a') // returns false
|
||||
* isLoneLongOption('-a') // returns false
|
||||
* isLoneLongOption('--foo') // returns true
|
||||
* isLoneLongOption('--foo=bar') // returns false
|
||||
*/
|
||||
function isLoneLongOption(arg) {
|
||||
return arg.length > 2 &&
|
||||
StringPrototypeStartsWith(arg, "--") &&
|
||||
!StringPrototypeIncludes(arg, "=", 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if `arg` is a long option and value in the same argument.
|
||||
* @example
|
||||
* isLongOptionAndValue('--foo') // returns false
|
||||
* isLongOptionAndValue('--foo=bar') // returns true
|
||||
*/
|
||||
function isLongOptionAndValue(arg) {
|
||||
return arg.length > 2 &&
|
||||
StringPrototypeStartsWith(arg, "--") &&
|
||||
StringPrototypeIncludes(arg, "=", 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if `arg` is a short option group.
|
||||
*
|
||||
* See Guideline 5 of the [Open Group Utility Conventions](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html).
|
||||
* One or more options without option-arguments, followed by at most one
|
||||
* option that takes an option-argument, should be accepted when grouped
|
||||
* behind one '-' delimiter.
|
||||
* @example
|
||||
* isShortOptionGroup('-a', {}) // returns false
|
||||
* isShortOptionGroup('-ab', {}) // returns true
|
||||
* // -fb is an option and a value, not a short option group
|
||||
* isShortOptionGroup('-fb', {
|
||||
* options: { f: { type: 'string' } }
|
||||
* }) // returns false
|
||||
* isShortOptionGroup('-bf', {
|
||||
* options: { f: { type: 'string' } }
|
||||
* }) // returns true
|
||||
* // -bfb is an edge case, return true and caller sorts it out
|
||||
* isShortOptionGroup('-bfb', {
|
||||
* options: { f: { type: 'string' } }
|
||||
* }) // returns true
|
||||
*/
|
||||
function isShortOptionGroup(arg, options) {
|
||||
if (arg.length <= 2) return false;
|
||||
if (StringPrototypeCharAt(arg, 0) !== "-") return false;
|
||||
if (StringPrototypeCharAt(arg, 1) === "-") return false;
|
||||
|
||||
const firstShort = StringPrototypeCharAt(arg, 1);
|
||||
const longOption = findLongOptionForShort(firstShort, options);
|
||||
return optionsGetOwn(options, longOption, "type") !== "string";
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if arg is a short string option followed by its value.
|
||||
* @example
|
||||
* isShortOptionAndValue('-a', {}); // returns false
|
||||
* isShortOptionAndValue('-ab', {}); // returns false
|
||||
* isShortOptionAndValue('-fFILE', {
|
||||
* options: { foo: { short: 'f', type: 'string' }}
|
||||
* }) // returns true
|
||||
*/
|
||||
function isShortOptionAndValue(arg, options) {
|
||||
validateObject(options, "options");
|
||||
|
||||
if (arg.length <= 2) return false;
|
||||
if (StringPrototypeCharAt(arg, 0) !== "-") return false;
|
||||
if (StringPrototypeCharAt(arg, 1) === "-") return false;
|
||||
|
||||
const shortOption = StringPrototypeCharAt(arg, 1);
|
||||
const longOption = findLongOptionForShort(shortOption, options);
|
||||
return optionsGetOwn(options, longOption, "type") === "string";
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the long option associated with a short option. Looks for a configured
|
||||
* `short` and returns the short option itself if a long option is not found.
|
||||
* @example
|
||||
* findLongOptionForShort('a', {}) // returns 'a'
|
||||
* findLongOptionForShort('b', {
|
||||
* options: { bar: { short: 'b' } }
|
||||
* }) // returns 'bar'
|
||||
*/
|
||||
function findLongOptionForShort(shortOption, options) {
|
||||
validateObject(options, "options");
|
||||
const longOptionEntry = ArrayPrototypeFind(
|
||||
ObjectEntries(options),
|
||||
({ 1: optionConfig }) =>
|
||||
objectGetOwn(optionConfig, "short") === shortOption,
|
||||
);
|
||||
return longOptionEntry?.[0] ?? shortOption;
|
||||
}
|
||||
|
||||
export {
|
||||
findLongOptionForShort,
|
||||
isLoneLongOption,
|
||||
isLoneShortOption,
|
||||
isLongOptionAndValue,
|
||||
isOptionLikeValue,
|
||||
isOptionValue,
|
||||
isShortOptionAndValue,
|
||||
isShortOptionGroup,
|
||||
objectGetOwn,
|
||||
optionsGetOwn,
|
||||
};
|
|
@ -9,6 +9,12 @@ import { hideStackFrames } from "ext:deno_node/internal/hide_stack_frames.ts";
|
|||
import { isArrayBufferView } from "ext:deno_node/internal/util/types.ts";
|
||||
import { normalizeEncoding } from "ext:deno_node/internal/normalize_encoding.mjs";
|
||||
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const {
|
||||
ArrayPrototypeIncludes,
|
||||
ArrayPrototypeJoin,
|
||||
} = primordials;
|
||||
|
||||
/**
|
||||
* @param {number} value
|
||||
* @returns {boolean}
|
||||
|
@ -282,6 +288,16 @@ const validateArray = hideStackFrames(
|
|||
},
|
||||
);
|
||||
|
||||
function validateUnion(value, name, union) {
|
||||
if (!ArrayPrototypeIncludes(union, value)) {
|
||||
throw new ERR_INVALID_ARG_TYPE(
|
||||
name,
|
||||
`('${ArrayPrototypeJoin(union, "|")}')`,
|
||||
value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
isInt32,
|
||||
isUint32,
|
||||
|
@ -299,6 +315,7 @@ export default {
|
|||
validatePort,
|
||||
validateString,
|
||||
validateUint32,
|
||||
validateUnion,
|
||||
};
|
||||
export {
|
||||
isInt32,
|
||||
|
@ -317,4 +334,5 @@ export {
|
|||
validatePort,
|
||||
validateString,
|
||||
validateUint32,
|
||||
validateUnion,
|
||||
};
|
||||
|
|
|
@ -15,6 +15,7 @@ import { Buffer } from "node:buffer";
|
|||
import { isDeepStrictEqual } from "ext:deno_node/internal/util/comparisons.ts";
|
||||
import process from "node:process";
|
||||
import { validateString } from "ext:deno_node/internal/validators.mjs";
|
||||
import { parseArgs } from "ext:deno_node/internal/util/parse_args/parse_args.js";
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const {
|
||||
ArrayIsArray,
|
||||
|
@ -48,6 +49,7 @@ export {
|
|||
format,
|
||||
formatWithOptions,
|
||||
inspect,
|
||||
parseArgs,
|
||||
promisify,
|
||||
stripVTControlCharacters,
|
||||
types,
|
||||
|
@ -312,6 +314,7 @@ export default {
|
|||
getSystemErrorName,
|
||||
deprecate,
|
||||
callbackify,
|
||||
parseArgs,
|
||||
promisify,
|
||||
inherits,
|
||||
types,
|
||||
|
|
Loading…
Reference in a new issue