Socket
Socket
Sign inDemoInstall

tiny-parse-argv

Package Overview
Dependencies
Maintainers
1
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

tiny-parse-argv - npm Package Compare versions

Comparing version 1.0.2 to 2.0.0

dist/utils.d.ts

2

dist/index.d.ts
import type { Options, ParsedArgs } from './types';
declare const parseArgv: (argv?: string[], options?: Options) => ParsedArgs;
declare const parseArgv: (argv: string[], options?: Options) => ParsedArgs;
export default parseArgv;
/* IMPORT */
import minimist from 'minimist';
import process from 'node:process';
import { isBoolean, isOverridable, set, uniq, without, zip } from './utils.js';
/* HELPERS */
const getAliasesMap = (aliases = {}) => {
const map = {};
for (const key in aliases) {
const values = uniq([key, ...aliases[key]]);
for (const value of values) {
if (value in map)
continue;
map[value] = without(values, value);
}
}
return map;
};
const getAliasedSet = (aliases, values = []) => {
const valuesAliases = values.flatMap(value => aliases[value] || []);
const valuesAliased = new Set([...values, ...valuesAliases]);
return valuesAliased;
};
const setAliased = (target, key, value, aliases) => {
set(target, key, value);
aliases[key]?.forEach(alias => {
set(target, alias, value);
});
};
const parseDoubleHyphen = (argv) => {
const index = argv.indexOf('--');
if (index < 0)
return [argv, []];
const parse = argv.slice(0, index);
const preserve = argv.slice(index + 1);
return [parse, preserve];
};
const parseWithRegExp = (argv, re, callback) => {
return argv.flatMap(arg => {
const match = re.exec(arg);
if (!match)
return arg;
return callback(...match);
});
};
const parseCharSeparator = (argv) => {
const re = /^-([a-zA-Z0-9]{2,})([^]*)$/;
return parseWithRegExp(argv, re, (_, chars) => chars.split('').map(char => `-${char}`));
};
const parseEqualsSeparator = (argv) => {
const re = /^(--?[^=][^=]*?)=([^]*)$/;
return parseWithRegExp(argv, re, (_, key, value) => [key, value]);
};
const parseImplicitSeparator = (argv) => {
const re = /^(--?(?:no-)?\S*?[a-zA-Z]\S*?)((?:[0-9\/]|-(?=$))[^]*)$/;
return parseWithRegExp(argv, re, (_, key, value) => [key, value]);
};
const parseProto = (argv) => {
const re = /^--?(no-)?(__proto__|prototype|constructor)$/;
return argv.filter((arg, index) => !re.test(arg) && !re.test(argv[index - 1]));
};
const parseOption = (arg) => {
const optionRe = /^(--?)([^]+)$/;
const match = optionRe.exec(arg);
if (!match)
return;
return match[2];
};
const parseOptionNegation = (arg) => {
const negationRe = /^no-([^]+)$/;
const match = negationRe.exec(arg);
if (!match)
return [arg, true];
return [match[1], false];
};
const parseValue = (key, value, booleans, strings) => {
if (booleans.has(key)) {
if (value === 'true')
return true;
if (value === 'false')
return false;
}
if (!strings.has(key)) {
const numberRe = /^0[xX][0-9a-fA-F]+$|^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][-+]?\d+)?$/;
if (numberRe.test(value)) {
return Number(value);
}
}
return String(value);
};
/* MAIN */
const parseArgv = (argv = process.argv, options) => {
return minimist(argv, options);
const parseArgv = (argv, options = {}) => {
const aliases = getAliasesMap(options.alias);
const booleans = getAliasedSet(aliases, options.boolean);
const strings = getAliasedSet(aliases, options.string);
const defaults = options.default || {};
const [parse, preserve] = parseDoubleHyphen(argv);
const parsed = { _: [], '--': preserve };
const args = parseCharSeparator(parseImplicitSeparator(parseEqualsSeparator(parseProto(parse))));
let optionPrev = '';
for (let i = 0, l = args.length; i < l; i++) {
const arg = args[i];
const option = parseOption(arg);
if (option) { // Option
const [key, positive] = parseOptionNegation(option);
if (isOverridable(parsed[key])) { // Maybe we are setting this option multiple times
const value = (strings.has(key) ? '' : positive);
setAliased(parsed, key, value, aliases);
}
optionPrev = option;
}
else { // Value or Argument
const value = parseValue(optionPrev, arg, booleans, strings);
if (optionPrev && (!booleans.has(optionPrev) || isBoolean(value))) { // Value
setAliased(parsed, optionPrev, value, aliases);
}
else { // Argument
parsed._.push(String(value));
}
optionPrev = '';
}
}
return { ...zip(booleans, false), ...defaults, ...parsed };
};
/* EXPORT */
export default parseArgv;

@@ -1,49 +0,12 @@

declare type Options = {
/**
* A string or array of strings argument names to always treat as strings
*/
string?: string | string[];
/**
* A boolean, string or array of strings to always treat as booleans. If true will treat
* all double hyphenated arguments without equals signs as boolean (e.g. affects `--foo`, not `-f` or `--foo=bar`)
*/
boolean?: boolean | string | string[];
/**
* An object mapping string names to strings or arrays of string argument names to use as aliases
*/
alias?: {
[key: string]: string | string[];
};
/**
* An object mapping string argument names to default values
*/
default?: {
[key: string]: any;
};
/**
* When true, populate argv._ with everything after the first non-option
*/
stopEarly?: boolean;
/**
* A function which is invoked with a command line parameter not defined in the opts
* configuration object. If the function returns false, the unknown option is not added to argv
*/
unknown?: (arg: string) => boolean;
/**
* When true, populate argv._ with everything before the -- and argv['--'] with everything after the --.
* Note that with -- set, parsing for arguments still stops after the `--`.
*/
'--'?: boolean;
type Options = {
boolean?: string[];
string?: string[];
alias?: Record<string, string[]>;
default?: Partial<Record<string, any>>;
};
declare type ParsedArgs = {
type ParsedArgs = {
[arg: string]: any;
/**
* If opts['--'] is true, populated with everything after the --
*/
'--'?: string[];
/**
* Contains all the arguments that didn't have an option associated with them
*/
_: string[];
'_': string[];
'--': string[];
};
export type { Options, ParsedArgs };
{
"name": "tiny-parse-argv",
"repository": "github:fabiospampinato/tiny-parse-argv",
"description": "A tiny function for parsing process.argv.",
"version": "1.0.2",
"description": "A tiny function for parsing process.argv, a modern rewrite of a sensible subset of minimist.",
"version": "2.0.0",
"type": "module",

@@ -14,3 +14,5 @@ "main": "dist/index.js",

"compile:watch": "tsex compile --watch",
"prepublishOnly": "npm run clean && npm run compile"
"test": "tsex test",
"test:watch": "tsex test --watch",
"prepublishOnly": "npm run clean && npm run compile && npm run test"
},

@@ -22,11 +24,7 @@ "keywords": [

],
"dependencies": {
"minimist": "^1.2.7"
},
"devDependencies": {
"@types/minimist": "^1.2.2",
"@types/node": "^18.11.9",
"tsex": "^1.1.2",
"typescript": "^4.8.4"
"fava": "^0.0.8",
"tsex": "^1.1.5",
"typescript": "^4.9.5"
}
}

@@ -1,7 +0,37 @@

> **Note**: This is just a type-checked wrapper over [minimist](https://github.com/minimistjs/minimist). The underlying implementation may change in the future.
# Tiny Parse Argv
A tiny function for parsing `process.argv`.
A tiny function for parsing `process.argv`, a modern rewrite of a sensible subset of [`minimist`](https://github.com/minimistjs/minimist).
## Features
The following features are provided:
- Built-in TypeScript types, and pretty clean and understandable code.
- Single/multiple implicit/explicit shorthand flags: `-f`, `-f some`, `-f 123`, `-f123`, `-abc`, `-abc 123`, `-abc123`, `-f some -f other`.
- Single/multiple implicit/explicit longhand flags: `--foo`, `--foo some`, `--foo 123`, `--foo=123`, `--foo=some`, `--foo some --foo other`.
- Explicitly negated flags are `false` by default: `--no-foo`, `--no-bar`.
- Arguments: `./app.sh with some list of arguments`.
- Values that would be interpreted as numbers if they were JavaScript are coerced to numbers automatically.
- Flags that could lead to prototype pollution issues are safely ignored.
- `options.boolean`: the value for the listed flags will always be coerced to a boolean.
- `options.string`: the value for the listed flags will always be coerced to a string.
- `options.alias`: if any aliased flag is assigned then all the aliases for it will be assigned too, automatically.
- `options.default`: an object containing default values, which will be used if not overridded by the `argv` array.
- `--`: a special flag that stops parsing, everything after it will be copied, untouched, into the `--` property of the return object.
## Differences with `minimist`
The following differences exist compared to `minimist`:
- `option['--']` set to `false` is not supported, it's as if it's always set to `true`.
- `option.boolean` set to `true` is not supported, you should always explicitly list all your supported boolean flags instead.
- `option.boolean` set to a single string is not supported, always provide an array of flags instead.
- `option.string` set to a single string is not supported, always provide an array of flags instead.
- `option.alias` mapping to a single string is not supported, always provide an array of aliases instead.
- `option.unknown` is not supported, you should handle unknown flags at another level of abstraction.
- `option.stopEarly` is not supported, it's as if it's always set to `false`.
- Dotted flags are not supported, so their paths will not be expanded, you can use [`path-prop`](https://github.com/fabiospampinato/path-prop)'s `unflat` function for that.
Other than that it should work pretty much identically, since we are basically using the same tests.
## Install

@@ -18,3 +48,4 @@

parseArgv ( process.argv );
parseArgv ([ '-f', '--foo', 'some', 'argument', '--', '--app-flag' ]);
// => { f: true, foo: 'some', _: ['argument'], '--': ['--app-flag'] }
```

@@ -21,0 +52,0 @@

/* IMPORT */
import minimist from 'minimist';
import process from 'node:process';
import {isBoolean, isOverridable, set, uniq, without, zip} from './utils';
import type {Options, ParsedArgs} from './types';
/* HELPERS */
const getAliasesMap = ( aliases: Record<string, string[]> = {} ): Partial<Record<string, string[]>> => {
const map: Partial<Record<string, string[]>> = {};
for ( const key in aliases ) {
const values = uniq ([ key, ...aliases[key] ]);
for ( const value of values ) {
if ( value in map ) continue;
map[value] = without ( values, value );
}
}
return map;
};
const getAliasedSet = ( aliases: Partial<Record<string, string[]>>, values: string[] = [] ): Set<string> => {
const valuesAliases = values.flatMap ( value => aliases[value] || [] );
const valuesAliased = new Set ([ ...values, ...valuesAliases ]);
return valuesAliased;
};
const setAliased = ( target: any, key: string, value: any, aliases: Partial<Record<string, string[]>> ): void => {
set ( target, key, value );
aliases[key]?.forEach ( alias => {
set ( target, alias, value );
});
};
const parseDoubleHyphen = ( argv: string[] ): [parse: string[], preserve: string[]] => {
const index = argv.indexOf ( '--' );
if ( index < 0 ) return [argv, []];
const parse = argv.slice ( 0, index );
const preserve = argv.slice ( index + 1 );
return [parse, preserve];
};
const parseWithRegExp = ( argv: string[], re: RegExp, callback: ( ...args: string[] ) => string[] ): string[] => {
return argv.flatMap ( arg => {
const match = re.exec ( arg );
if ( !match ) return arg;
return callback ( ...match );
});
};
const parseCharSeparator = ( argv: string[] ): string[] => {
const re = /^-([a-zA-Z0-9]{2,})([^]*)$/;
return parseWithRegExp ( argv, re, ( _, chars ) => chars.split ( '' ).map ( char => `-${char}` ) );
};
const parseEqualsSeparator = ( argv: string[] ): string[] => {
const re = /^(--?[^=][^=]*?)=([^]*)$/;
return parseWithRegExp ( argv, re, ( _, key, value ) => [key, value] );
};
const parseImplicitSeparator = ( argv: string[] ): string[] => {
const re = /^(--?(?:no-)?\S*?[a-zA-Z]\S*?)((?:[0-9\/]|-(?=$))[^]*)$/;
return parseWithRegExp ( argv, re, ( _, key, value ) => [key, value] );
};
const parseProto = ( argv: string[] ): string[] => {
const re = /^--?(no-)?(__proto__|prototype|constructor)$/;
return argv.filter ( ( arg, index ) => !re.test ( arg ) && !re.test ( argv[index - 1] ) );
};
const parseOption = ( arg: string ): string | undefined => {
const optionRe = /^(--?)([^]+)$/;
const match = optionRe.exec ( arg );
if ( !match ) return;
return match[2];
};
const parseOptionNegation = ( arg: string ): [key: string, positive: boolean] => {
const negationRe = /^no-([^]+)$/;
const match = negationRe.exec ( arg );
if ( !match ) return [arg, true];
return [match[1], false];
};
const parseValue = ( key: string, value: string, booleans: Set<string>, strings: Set<string> ): string | number | boolean => {
if ( booleans.has ( key ) ) {
if ( value === 'true' ) return true;
if ( value === 'false' ) return false;
}
if ( !strings.has ( key ) ) {
const numberRe = /^0[xX][0-9a-fA-F]+$|^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][-+]?\d+)?$/;
if ( numberRe.test ( value ) ) {
return Number ( value );
}
}
return String ( value );
};
/* MAIN */
const parseArgv = ( argv: string[] = process.argv, options?: Options ): ParsedArgs => {
const parseArgv = ( argv: string[], options: Options = {} ): ParsedArgs => {
return minimist ( argv, options );
const aliases = getAliasesMap ( options.alias );
const booleans = getAliasedSet ( aliases, options.boolean );
const strings = getAliasedSet ( aliases, options.string );
const defaults = options.default || {};
const [parse, preserve] = parseDoubleHyphen ( argv );
const parsed: ParsedArgs = { _: [], '--': preserve };
const args = parseCharSeparator ( parseImplicitSeparator ( parseEqualsSeparator ( parseProto ( parse ) ) ) );
let optionPrev: string = '';
for ( let i = 0, l = args.length; i < l; i++ ) {
const arg = args[i];
const option = parseOption ( arg );
if ( option ) { // Option
const [key, positive] = parseOptionNegation ( option );
if ( isOverridable ( parsed[key] ) ) { // Maybe we are setting this option multiple times
const value = ( strings.has ( key ) ? '' : positive );
setAliased ( parsed, key, value, aliases );
}
optionPrev = option;
} else { // Value or Argument
const value = parseValue ( optionPrev, arg, booleans, strings );
if ( optionPrev && ( !booleans.has ( optionPrev ) || isBoolean ( value ) ) ) { // Value
setAliased ( parsed, optionPrev, value, aliases );
} else { // Argument
parsed._.push ( String ( value ) );
}
optionPrev = '';
}
}
return { ...zip ( booleans, false ), ...defaults, ...parsed };
};

@@ -15,0 +216,0 @@

/* MAIN */
//TODO: Type these more strictly, if it doesn't cause too many troubles with generics...
type Options = {
/**
* A string or array of strings argument names to always treat as strings
*/
string?: string | string[],
/**
* A boolean, string or array of strings to always treat as booleans. If true will treat
* all double hyphenated arguments without equals signs as boolean (e.g. affects `--foo`, not `-f` or `--foo=bar`)
*/
boolean?: boolean | string | string[],
/**
* An object mapping string names to strings or arrays of string argument names to use as aliases
*/
alias?: { [key: string]: string | string[] },
/**
* An object mapping string argument names to default values
*/
default?: { [key: string]: any },
/**
* When true, populate argv._ with everything after the first non-option
*/
stopEarly?: boolean,
/**
* A function which is invoked with a command line parameter not defined in the opts
* configuration object. If the function returns false, the unknown option is not added to argv
*/
unknown?: ( arg: string ) => boolean,
/**
* When true, populate argv._ with everything before the -- and argv['--'] with everything after the --.
* Note that with -- set, parsing for arguments still stops after the `--`.
*/
'--'?: boolean
boolean?: string[],
string?: string[],
alias?: Record<string, string[]>,
default?: Partial<Record<string, any>>
};

@@ -46,12 +15,4 @@

[arg: string]: any,
/**
* If opts['--'] is true, populated with everything after the --
*/
'--'?: string[],
/**
* Contains all the arguments that didn't have an option associated with them
*/
_: string[]
'_': string[],
'--': string[]
};

@@ -58,0 +19,0 @@

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