Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@pkgjs/parseargs

Package Overview
Dependencies
Maintainers
1
Versions
14
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@pkgjs/parseargs - npm Package Compare versions

Comparing version 0.3.0 to 0.4.0

.editorconfig

13

CHANGELOG.md
# Changelog
## [0.4.0](https://github.com/pkgjs/parseargs/compare/v0.3.0...v0.4.0) (2022-03-12)
### ⚠ BREAKING CHANGES
* parsing, revisit short option groups, add support for combined short and value (#75)
* restructure configuration to take options bag (#63)
### Code Refactoring
* parsing, revisit short option groups, add support for combined short and value ([#75](https://github.com/pkgjs/parseargs/issues/75)) ([a92600f](https://github.com/pkgjs/parseargs/commit/a92600fa6c214508ab1e016fa55879a314f541af))
* restructure configuration to take options bag ([#63](https://github.com/pkgjs/parseargs/issues/63)) ([b412095](https://github.com/pkgjs/parseargs/commit/b4120957d90e809ee8b607b06e747d3e6a6b213e))
## [0.3.0](https://github.com/pkgjs/parseargs/compare/v0.2.0...v0.3.0) (2022-02-06)

@@ -4,0 +17,0 @@

213

index.js

@@ -5,7 +5,8 @@ 'use strict';

ArrayPrototypeConcat,
ArrayPrototypeIncludes,
ArrayPrototypeForEach,
ArrayPrototypeShift,
ArrayPrototypeSlice,
ArrayPrototypeSplice,
ArrayPrototypePush,
ObjectHasOwn,
ObjectEntries,
StringPrototypeCharAt,

@@ -15,3 +16,2 @@ StringPrototypeIncludes,

StringPrototypeSlice,
StringPrototypeStartsWith,
} = require('./primordials');

@@ -21,5 +21,18 @@

validateArray,
validateObject
validateObject,
validateString,
validateUnion,
validateBoolean,
} = require('./validators');
const {
findLongOptionForShort,
isLoneLongOption,
isLoneShortOption,
isLongOptionAndValue,
isOptionValue,
isShortOptionAndValue,
isShortOptionGroup
} = require('./utils');
function getMainArgs() {

@@ -59,13 +72,12 @@ // This function is a placeholder for proposed process.mainArgs.

function storeOptionValue(parseOptions, option, value, result) {
const multiple = parseOptions.multiples &&
ArrayPrototypeIncludes(parseOptions.multiples, option);
function storeOptionValue(options, longOption, value, result) {
const optionConfig = options[longOption] || {};
// Flags
result.flags[option] = true;
result.flags[longOption] = true;
// Values
if (multiple) {
if (optionConfig.multiple) {
// Always store value in array, including for flags.
// result.values[option] starts out not present,
// result.values[longOption] starts out not present,
// first value is added as new array [newValue],

@@ -75,22 +87,39 @@ // subsequent values are pushed to existing array.

const newValue = usedAsFlag ? true : value;
if (result.values[option] !== undefined)
ArrayPrototypePush(result.values[option], newValue);
if (result.values[longOption] !== undefined)
ArrayPrototypePush(result.values[longOption], newValue);
else
result.values[option] = [newValue];
result.values[longOption] = [newValue];
} else {
result.values[option] = value;
result.values[longOption] = value;
}
}
const parseArgs = (
argv = getMainArgs(),
const parseArgs = ({
args = getMainArgs(),
options = {}
) => {
validateArray(argv, 'argv');
} = {}) => {
validateArray(args, 'args');
validateObject(options, 'options');
for (const key of ['withValue', 'multiples']) {
if (ObjectHasOwn(options, key)) {
validateArray(options[key], `options.${key}`);
ArrayPrototypeForEach(
ObjectEntries(options),
([longOption, optionConfig]) => {
validateObject(optionConfig, `options.${longOption}`);
if (ObjectHasOwn(optionConfig, 'type')) {
validateUnion(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 Error(`options.${longOption}.short must be a single character, got '${shortOption}'`);
}
}
if (ObjectHasOwn(optionConfig, 'multiple')) {
validateBoolean(optionConfig.multiple, `options.${longOption}.multiple`);
}
}
}
);

@@ -103,77 +132,85 @@ const result = {

let pos = 0;
while (pos < argv.length) {
let arg = argv[pos];
let remainingArgs = ArrayPrototypeSlice(args);
while (remainingArgs.length > 0) {
const arg = ArrayPrototypeShift(remainingArgs);
const nextArg = remainingArgs[0];
if (StringPrototypeStartsWith(arg, '-')) {
if (arg === '-') {
// '-' commonly used to represent stdin/stdout, treat as positional
result.positionals = ArrayPrototypeConcat(result.positionals, '-');
++pos;
continue;
} else if (arg === '--') {
// Everything after a bare '--' is considered a positional argument
// and is returned verbatim
result.positionals = ArrayPrototypeConcat(
result.positionals,
ArrayPrototypeSlice(argv, ++pos)
);
return result;
} else if (StringPrototypeCharAt(arg, 1) !== '-') {
// Look for shortcodes: -fXzy and expand them to -f -X -z -y:
if (arg.length > 2) {
for (let i = 2; i < arg.length; i++) {
const short = StringPrototypeCharAt(arg, i);
// Add 'i' to 'pos' such that short options are parsed in order
// of definition:
ArrayPrototypeSplice(argv, pos + (i - 1), 0, `-${short}`);
}
}
// Check if `arg` is an options terminator.
// Guideline 10 in https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html
if (arg === '--') {
// Everything after a bare '--' is considered a positional argument.
result.positionals = ArrayPrototypeConcat(
result.positionals,
remainingArgs
);
break; // Finished processing args, leave while loop.
}
arg = StringPrototypeCharAt(arg, 1); // short
if (options.short && options.short[arg])
arg = options.short[arg]; // now long!
// ToDo: later code tests for `=` in arg and wrong for shorts
} else {
arg = StringPrototypeSlice(arg, 2); // remove leading --
if (isLoneShortOption(arg)) {
// e.g. '-f'
const shortOption = StringPrototypeCharAt(arg, 1);
const longOption = findLongOptionForShort(shortOption, options);
let optionValue;
if (options[longOption]?.type === 'string' && isOptionValue(nextArg)) {
// e.g. '-f', 'bar'
optionValue = ArrayPrototypeShift(remainingArgs);
}
storeOptionValue(options, longOption, optionValue, result);
continue;
}
if (StringPrototypeIncludes(arg, '=')) {
// Store option=value same way independent of `withValue` as:
// - looks like a value, store as a value
// - match the intention of the user
// - preserve information for author to process further
const index = StringPrototypeIndexOf(arg, '=');
storeOptionValue(
options,
StringPrototypeSlice(arg, 0, index),
StringPrototypeSlice(arg, index + 1),
result);
} else if (pos + 1 < argv.length &&
!StringPrototypeStartsWith(argv[pos + 1], '-')
) {
// withValue option should also support setting values when '=
// isn't used ie. both --foo=b and --foo b should work
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 (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.
// ToDo: if strict then throw
// Expand -abfFILE to -a -b -fFILE
ArrayPrototypePush(expanded, `-${StringPrototypeSlice(arg, index)}`);
break; // finished short group
}
}
remainingArgs = ArrayPrototypeConcat(expanded, remainingArgs);
continue;
}
// If withValue option is specified, take next position arguement as
// value and then increment pos so that we don't re-evaluate that
// arg, else set value as undefined ie. --foo b --bar c, after setting
// b as the value for foo, evaluate --bar next and skip 'b'
const val = options.withValue &&
ArrayPrototypeIncludes(options.withValue, arg) ? argv[++pos] :
undefined;
storeOptionValue(options, arg, val, result);
} else {
// Cases when an arg is specified without a value, example
// '--foo --bar' <- 'foo' and 'bar' flags should be set to true and
// shave value as undefined
storeOptionValue(options, arg, undefined, result);
if (isShortOptionAndValue(arg, options)) {
// e.g. -fFILE
const shortOption = StringPrototypeCharAt(arg, 1);
const longOption = findLongOptionForShort(shortOption, options);
const optionValue = StringPrototypeSlice(arg, 2);
storeOptionValue(options, longOption, optionValue, result);
continue;
}
if (isLoneLongOption(arg)) {
// e.g. '--foo'
const longOption = StringPrototypeSlice(arg, 2);
let optionValue;
if (options[longOption]?.type === 'string' && isOptionValue(nextArg)) {
// e.g. '--foo', 'bar'
optionValue = ArrayPrototypeShift(remainingArgs);
}
storeOptionValue(options, longOption, optionValue, result);
continue;
}
} else {
// Arguements without a dash prefix are considered "positional"
ArrayPrototypePush(result.positionals, arg);
if (isLongOptionAndValue(arg)) {
// e.g. --foo=bar
const index = StringPrototypeIndexOf(arg, '=');
const longOption = StringPrototypeSlice(arg, 2, index);
const optionValue = StringPrototypeSlice(arg, index + 1);
storeOptionValue(options, longOption, optionValue, result);
continue;
}
pos++;
// Anything left is a positional
ArrayPrototypePush(result.positionals, arg);
}

@@ -180,0 +217,0 @@

{
"name": "@pkgjs/parseargs",
"version": "0.3.0",
"version": "0.4.0",
"description": "Polyfill of future proposal for `util.parseArgs()`",
"main": "index.js",
"exports": {
".": "./index.js",
"./package.json": "./package.json"
},
"scripts": {
"coverage": "c8 --check-coverage node test/index.js",
"test": "c8 node test/index.js",
"coverage": "c8 --check-coverage tape 'test/*.js'",
"test": "c8 tape 'test/*.js'",
"posttest": "eslint .",

@@ -10,0 +14,0 @@ "fix": "npm run posttest -- --fix"

@@ -12,5 +12,8 @@ <!-- omit in toc -->

### Scope
This package was implemented using [tape](https://www.npmjs.com/package/tape) as its test harness.
It is already possible to build great arg parsing modules on top of what Node.js provides; the prickly API is abstracted away by these modules. Thus, process.parseArgs() is not necessarily intended for library authors; it is intended for developers of simple CLI tools, ad-hoc scripts, deployed Node.js applications, and learning materials.
It is exceedingly difficult to provide an API which would both be friendly to these Node.js users while being extensible enough for libraries to build upon. We chose to prioritize these use cases because these are currently not well-served by Node.js' API.
### Links & Resources

@@ -29,3 +32,3 @@

- [Implementation:](#implementation)
- [💡 `util.parseArgs(argv)` Proposal](#-utilparseargsargv-proposal)
- [💡 `util.parseArgs([config])` Proposal](#-utilparseargsconfig-proposal)
- [📃 Examples](#-examples)

@@ -60,2 +63,4 @@ - [F.A.Qs](#faqs)

This package was implemented using [tape](https://www.npmjs.com/package/tape) as its test harness.
----

@@ -75,15 +80,12 @@

## 💡 `util.parseArgs([argv][, options])` Proposal
## 💡 `util.parseArgs([config])` Proposal
* `argv` {string[]} (Optional) Array of argument strings; defaults
to [`process.mainArgs`](process_argv)
* `options` {Object} (Optional) The `options` parameter is an
* `config` {Object} (Optional) The `config` parameter is an
object supporting the following properties:
* `withValue` {string[]} (Optional) An `Array` of argument
strings which expect a value to be defined in `argv` (see [Options][]
for details)
* `multiples` {string[]} (Optional) An `Array` of argument
strings which, when appearing multiple times in `argv`, will be concatenated
into an `Array`
* `short` {Object} (Optional) An `Object` of key, value pairs of strings which map a "short" alias to an argument; When appearing multiples times in `argv`; Respects `withValue` & `multiples`
* `args` {string[]} (Optional) Array of argument strings; defaults
to [`process.mainArgs`](process_argv)
* `options` {Object} (Optional) An object describing the known options to look for in `args`; `options` keys are the long names of the known options, and the values are objects with the following properties:
* `type` {'string'|'boolean'} (Optional) Type of known option; defaults to `'boolean'`;
* `multiple` {boolean} (Optional) If true, when appearing one or more times in `args`, results are collected in an `Array`
* `short` {string} (Optional) A single character alias for an option; When appearing one or more times in `args`; Respects the `multiple` configuration
* `strict` {Boolean} (Optional) A `Boolean` on wheather or not to throw an error when unknown args are encountered

@@ -106,5 +108,5 @@ * Returns: {Object} An object having properties:

const { parseArgs } = require('@pkgjs/parseargs');
const argv = ['-f', '--foo=a', '--bar', 'b'];
const args = ['-f', '--foo=a', '--bar', 'b'];
const options = {};
const { flags, values, positionals } = parseArgs(argv, options);
const { flags, values, positionals } = parseArgs({ args, options });
// flags = { f: true, bar: true }

@@ -118,7 +120,9 @@ // values = { foo: 'a' }

// withValue
const argv = ['-f', '--foo=a', '--bar', 'b'];
const args = ['-f', '--foo=a', '--bar', 'b'];
const options = {
withValue: ['bar']
foo: {
type: 'string',
},
};
const { flags, values, positionals } = parseArgs(argv, options);
const { flags, values, positionals } = parseArgs({ args, options });
// flags = { f: true }

@@ -131,9 +135,11 @@ // values = { foo: 'a', bar: 'b' }

const { parseArgs } = require('@pkgjs/parseargs');
// withValue & multiples
const argv = ['-f', '--foo=a', '--foo', 'b'];
// withValue & multiple
const args = ['-f', '--foo=a', '--foo', 'b'];
const options = {
withValue: ['foo'],
multiples: ['foo']
foo: {
type: 'string',
multiple: true,
},
};
const { flags, values, positionals } = parseArgs(argv, options);
const { flags, values, positionals } = parseArgs({ args, options });
// flags = { f: true }

@@ -147,7 +153,9 @@ // values = { foo: ['a', 'b'] }

// shorts
const argv = ['-f', 'b'];
const args = ['-f', 'b'];
const options = {
short: { f: 'foo' }
foo: {
short: 'f',
},
};
const { flags, values, positionals } = parseArgs(argv, options);
const { flags, values, positionals } = parseArgs({ args, options });
// flags = { foo: true }

@@ -161,3 +169,3 @@ // values = {}

- Is `cmd --foo=bar baz` the same as `cmd baz --foo=bar`?
- Yes, if `withValue: ['foo']`, otherwise no
- yes
- Does the parser execute a function?

@@ -164,0 +172,0 @@ - no

@@ -5,2 +5,4 @@ 'use strict';

ArrayIsArray,
ArrayPrototypeIncludes,
ArrayPrototypeJoin,
} = require('./primordials');

@@ -14,2 +16,20 @@

function validateString(value, name) {
if (typeof value !== 'string') {
throw new ERR_INVALID_ARG_TYPE(name, 'String', value);
}
}
function validateUnion(value, name, union) {
if (!ArrayPrototypeIncludes(union, value)) {
throw new ERR_INVALID_ARG_TYPE(name, `('${ArrayPrototypeJoin(union, '|')}')`, value);
}
}
function validateBoolean(value, name) {
if (typeof value !== 'boolean') {
throw new ERR_INVALID_ARG_TYPE(name, 'Boolean', value);
}
}
function validateArray(value, name) {

@@ -47,2 +67,5 @@ if (!ArrayIsArray(value)) {

validateObject,
validateString,
validateUnion,
validateBoolean,
};
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc