mirror of
https://github.com/denoland/deno.git
synced 2024-12-25 00:29:09 -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-nodeeventtarget.js",
|
||||||
"test-outgoing-message-destroy.js",
|
"test-outgoing-message-destroy.js",
|
||||||
"test-outgoing-message-pipe.js",
|
"test-outgoing-message-pipe.js",
|
||||||
|
"test-parse-args.mjs",
|
||||||
"test-path-basename.js",
|
"test-path-basename.js",
|
||||||
"test-path-dirname.js",
|
"test-path-dirname.js",
|
||||||
"test-path-extname.js",
|
"test-path-extname.js",
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
import "./polyfill_globals.js";
|
import "./polyfill_globals.js";
|
||||||
import { createRequire } from "node:module";
|
import { createRequire } from "node:module";
|
||||||
|
import { toFileUrl } from "../../../test_util/std/path/mod.ts";
|
||||||
const file = Deno.args[0];
|
const file = Deno.args[0];
|
||||||
if (!file) {
|
if (!file) {
|
||||||
throw new Error("No file provided");
|
throw new Error("No file provided");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (file.endsWith(".mjs")) {
|
||||||
|
await import(toFileUrl(file).href);
|
||||||
|
} else {
|
||||||
createRequire(import.meta.url)(file);
|
createRequire(import.meta.url)(file);
|
||||||
|
}
|
||||||
|
|
|
@ -83,6 +83,7 @@ async function runTest(t: Deno.TestContext, path: string): Promise<void> {
|
||||||
"--quiet",
|
"--quiet",
|
||||||
"--unstable",
|
"--unstable",
|
||||||
//"--unsafely-ignore-certificate-errors",
|
//"--unsafely-ignore-certificate-errors",
|
||||||
|
"--unstable-bare-node-builtins",
|
||||||
"--v8-flags=" + v8Flags.join(),
|
"--v8-flags=" + v8Flags.join(),
|
||||||
"runner.ts",
|
"runner.ts",
|
||||||
testCase,
|
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/comparisons.ts",
|
||||||
"internal/util/debuglog.ts",
|
"internal/util/debuglog.ts",
|
||||||
"internal/util/inspect.mjs",
|
"internal/util/inspect.mjs",
|
||||||
|
"internal/util/parse_args/parse_args.js",
|
||||||
|
"internal/util/parse_args/utils.js",
|
||||||
"internal/util/types.ts",
|
"internal/util/types.ts",
|
||||||
"internal/validators.mjs",
|
"internal/validators.mjs",
|
||||||
"path/_constants.ts",
|
"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 {
|
export class ERR_INTERNAL_ASSERTION extends NodeError {
|
||||||
constructor(message?: string) {
|
constructor(message?: string) {
|
||||||
const suffix = "This is caused by either a bug in Node.js " +
|
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 { isArrayBufferView } from "ext:deno_node/internal/util/types.ts";
|
||||||
import { normalizeEncoding } from "ext:deno_node/internal/normalize_encoding.mjs";
|
import { normalizeEncoding } from "ext:deno_node/internal/normalize_encoding.mjs";
|
||||||
|
|
||||||
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
|
const {
|
||||||
|
ArrayPrototypeIncludes,
|
||||||
|
ArrayPrototypeJoin,
|
||||||
|
} = primordials;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} value
|
* @param {number} value
|
||||||
* @returns {boolean}
|
* @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 {
|
export default {
|
||||||
isInt32,
|
isInt32,
|
||||||
isUint32,
|
isUint32,
|
||||||
|
@ -299,6 +315,7 @@ export default {
|
||||||
validatePort,
|
validatePort,
|
||||||
validateString,
|
validateString,
|
||||||
validateUint32,
|
validateUint32,
|
||||||
|
validateUnion,
|
||||||
};
|
};
|
||||||
export {
|
export {
|
||||||
isInt32,
|
isInt32,
|
||||||
|
@ -317,4 +334,5 @@ export {
|
||||||
validatePort,
|
validatePort,
|
||||||
validateString,
|
validateString,
|
||||||
validateUint32,
|
validateUint32,
|
||||||
|
validateUnion,
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { Buffer } from "node:buffer";
|
||||||
import { isDeepStrictEqual } from "ext:deno_node/internal/util/comparisons.ts";
|
import { isDeepStrictEqual } from "ext:deno_node/internal/util/comparisons.ts";
|
||||||
import process from "node:process";
|
import process from "node:process";
|
||||||
import { validateString } from "ext:deno_node/internal/validators.mjs";
|
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 primordials = globalThis.__bootstrap.primordials;
|
||||||
const {
|
const {
|
||||||
ArrayIsArray,
|
ArrayIsArray,
|
||||||
|
@ -48,6 +49,7 @@ export {
|
||||||
format,
|
format,
|
||||||
formatWithOptions,
|
formatWithOptions,
|
||||||
inspect,
|
inspect,
|
||||||
|
parseArgs,
|
||||||
promisify,
|
promisify,
|
||||||
stripVTControlCharacters,
|
stripVTControlCharacters,
|
||||||
types,
|
types,
|
||||||
|
@ -312,6 +314,7 @@ export default {
|
||||||
getSystemErrorName,
|
getSystemErrorName,
|
||||||
deprecate,
|
deprecate,
|
||||||
callbackify,
|
callbackify,
|
||||||
|
parseArgs,
|
||||||
promisify,
|
promisify,
|
||||||
inherits,
|
inherits,
|
||||||
types,
|
types,
|
||||||
|
|
Loading…
Reference in a new issue