Comparing version 0.4.1-beta to 0.6.0
export {default} from './src/argMate.ts'; | ||
export {helpText} from './src/argMate.ts'; | ||
export {argInfo} from './src/argMate.ts'; |
{ | ||
"name": "argmate", | ||
"version": "0.4.1-beta", | ||
"author": "Mathias Rengel Wulff", | ||
"description": "Lightning-fast CLI parameter parsing, seasoned with convenient features for better DX", | ||
"version": "0.6.0", | ||
"author": "Mathias Wulff", | ||
"description": "CLI parameter parsing. Zero dependencies. Fast. Convenient features for better DX", | ||
"license": "CC-BY-SA-4.0", | ||
@@ -17,6 +17,7 @@ "repository": { | ||
"arg", | ||
"args", | ||
"argv", | ||
"parse", | ||
"flags", | ||
"options", | ||
"argv", | ||
"mri", | ||
@@ -30,5 +31,4 @@ "yargs", | ||
"scripts": { | ||
"build": " bun build --target bun src/argMate.ts --outfile dist/argMate.mjs ", | ||
"postbuild": "bun build --target bun src/argMate.ts --outfile dist/argMate.min.mjs --minify", | ||
"prebuild": "rm -fr dist/", | ||
"build": "bun build --target bun src/argMate.ts src/argMateLite.ts src/argEngine.ts src/argEngineLite.ts --outdir dist/ --minify ", | ||
"test": "yarn test-only && yarn build && yarn test-bundle", | ||
@@ -49,6 +49,6 @@ "test-only": "bun test --bail", | ||
"devDependencies": { | ||
"bun-types": "^1.0.3", | ||
"bun-types": "1.1.21", | ||
"columnify": "^1.6.0", | ||
"husky": "^8.0.3", | ||
"prettier": "^3.0.3", | ||
"prettier": "3.3.3", | ||
"rexreplace": "^7.1.3" | ||
@@ -55,0 +55,0 @@ }, |
196
README.md
@@ -14,21 +14,17 @@ | ||
> _Your go-to companion for lightning-fast CLI parameter parsing, enhanced with convenient features to make your development experience much smoother._ | ||
> _Your go-to companion for lightning-fast CLI parameter parsing, seasoned with convenient features to make your development experience much more smooth._ | ||
While developing tools like [AlaSQL](https://www.npmjs.com/package/alasql) and [RexReplace](https://www.npmjs.com/package/rexreplace), I've often been torn between two types of CLI parsers. On one hand, there are feature-rich options like [yargs](https://www.npmjs.com/package/yargs) and [commander](https://www.npmjs.com/package/commander). Despite their heavy startup time, these parsers provide useful features like easy defaults, smooth validation, and well-structured CLI help text output. On the other hand, simpler alternatives like [nopt](https://www.npmjs.com/package/nopt) and [mri](https://www.npmjs.com/package/mri) excel in performance but lack in development experience. After uncovering yet another performance hit from using a heavyweight parser, I decided to solve this issue once and for all. | ||
While developing things like [AlaSQL](https://www.npmjs.com/package/alasql) and [RexReplace](https://www.npmjs.com/package/rexreplace) I've always been caught between two types of CLI parsers. On one hand, there are feature-rich options like [yargs](https://www.npmjs.com/package/yargs) and [commander](https://www.npmjs.com/package/commander), which, despite their heavy startup time, provide useful help like easy defaults, smooth validation, and well-structured CLI help text output. On the other hand, more simple alternatives like [nopt](https://www.npmjs.com/package/nopt) and [mri](https://www.npmjs.com/package/mri) excel in performance but lack in development experience. After yet again uncovering a performance hit from using a heavyweight parser, I decided to solve this issue once and for all. | ||
``` | ||
Benchmark: | ||
argMate 9,089,813 ops/sec ±2.15% (98 runs sampled) 1x | ||
nopt 2,070,397 ops/sec ±1.21% (94 runs sampled) 4x | ||
mri 1,832,768 ops/sec ±0.13% (99 runs sampled) 5x | ||
minimist 706,265 ops/sec ±1.05% (94 runs sampled) 13x | ||
yargs-parser 67,417 ops/sec ±0.39% (97 runs sampled) 135x | ||
argMate 9,089,813 ops/sec ±2.15% (98 runs sampled) 1x | ||
nopt 2,070,397 ops/sec ±1.21% (94 runs sampled) 4x | ||
mri 1,832,768 ops/sec ±0.13% (99 runs sampled) 5x | ||
minimist 706,265 ops/sec ±1.05% (94 runs sampled) 13x | ||
yargs-parser 67,417 ops/sec ±0.39% (97 runs sampled) 135x | ||
``` | ||
Meet ArgMate: a CLI parameter parser that's not just fast—it's 4-5 times faster than other parsers focused on speed, while being feature-rich. _But how?!?_ A computer processes instructions at a set pace. To get results faster the only option is to ask the computer to do less work. By minimising how many times variables are touched and keeping those operations close together, the implementation enables efficient caching of data, resulting in fewer CPU cycles to get the result. | ||
**Meet ArgMate**, a CLI parameter parser that's not just fast - it's 4-5 times faster than other parsers focused on speed, while still being feature-rich. _But how?!?_ A computer processes instructions at a set pace. To get results faster, the only option is to do fewer things. By minimising how many times variables are touched and keeping those operations close together, the implementation enables efficient caching of data, resulting in fewer CPU cycles to get stuff done. | ||
## Installation | ||
@@ -45,3 +41,3 @@ | ||
```js | ||
argMate(arguments, [parameters [, config ]]); | ||
argMate(arguments, [parameterDetails [, config ]]); | ||
``` | ||
@@ -51,77 +47,123 @@ | ||
### Getting Started | ||
### Getting started | ||
ArgMate follows traditional CLI notations similar to yargs and mri. Here are some simple examples: | ||
```js | ||
import argMate from 'argmate'; | ||
import ArgMate from 'ArgMate'; | ||
let argv; | ||
// By default, parameters are treated as boolean flags | ||
// Non-parameters are stored in the `_` property of the output | ||
argv = ArgMate(['--foo', 'bar']); | ||
// {_: ['bar'], foo: true} | ||
// Use the `=` notation for assignment, with or without seperation to the value | ||
// Type is inferred from the value (string or number) | ||
argv = ArgMate(['--foo=', 'bar']); | ||
// {_: [], foo: 'bar'} | ||
argv = ArgMate(['-i=123']); | ||
// {_: [], i: 123} | ||
// Setting a default value makes the parser treat it as a parameter that must be assigned | ||
// The type is guessed based on the default value | ||
argv = ArgMate(['--foo', 'bar2'], { foo: 'bar' }); | ||
// {_: [], foo: 'bar2'} | ||
// Specify the type explicitly to avoid the guessing game and improving performance | ||
argv = ArgMate(['--foo', 'bar'], { foo: { type: 'string' } }); | ||
// {_: [], foo: 'bar'} | ||
// Example of parsing actual command-line arguments | ||
// Running `node index.js --foo=bar -X .md` | ||
// Assuming the following code is in index.js: | ||
argv = ArgMate(process.argv.slice(2)); | ||
// { _: ['.md'], foo: "bar", X: true } | ||
``` | ||
### Enforcing parameter types and limiting allowed values | ||
You can provide default values and enforce that no other parameters are allowed: | ||
```js | ||
import ArgMate from 'ArgMate'; | ||
const args = process.argv.slice(2); | ||
// Define parameter types and default values | ||
const params = { | ||
loops: 10, // --loops must be an integer and will default to 10 if not set. | ||
help: false | ||
foo: 10, // --foo is expected to be an integer, default: 10 | ||
bar: false // --bar is expected to be a boolean, default: false | ||
}; | ||
const config = { | ||
Defaults to true. | ||
allowUnknown: false, // Only allow parameters we have specified (--loops and --help). | ||
error: msg => { // If there is an error (like providing parameters not allowed), this function will be invoked. | ||
console.error('There was a problem:', msg); | ||
process.exit(1); | ||
}, | ||
allowUnknown: false // Only allow specified parameters (--foo and --bar) | ||
}; | ||
const argv = argMate(args, params, config); // params and config are not mandatory | ||
const argv = ArgMate(args, params, config); | ||
``` | ||
### A More Complete Example | ||
Same example but a bit shorter | ||
```js | ||
import argMate, {helpText} from 'argmate'; | ||
import ArgMate from 'ArgMate'; | ||
const argv = ArgMate(process.argv.slice(2), | ||
{ | ||
foo: 10, | ||
bar: false | ||
}, { | ||
allowUnknown: false | ||
}); | ||
``` | ||
### Real world example | ||
Here's a more comprehensive example demonstrating additional features: | ||
```javascript | ||
import ArgMate, { argInfo } from 'ArgMate'; | ||
const args = process.argv.slice(2); | ||
const params = { | ||
start: { | ||
default: 0, | ||
alias: ['s'], | ||
}, | ||
steps: { | ||
type: 'number', | ||
mandatory: true, | ||
alias: ['l', 'loops'], | ||
valid: v => v > 0, // Call config.error if value is not valid | ||
}, | ||
help: { | ||
alias: ['h'], | ||
}, | ||
start: { | ||
default: 0, | ||
alias: ['s'] | ||
}, | ||
steps: { | ||
type: 'number', | ||
mandatory: true, | ||
alias: ['l', 'loops'], | ||
valid: v => v > 0 // Validate the input | ||
}, | ||
help: { | ||
alias: ['h'] | ||
} | ||
}; | ||
const argv = argMate(args, params); | ||
const config = { | ||
allowUnknown: false, | ||
error: msg => { | ||
console.error('Error:', msg); | ||
process.exit(1); | ||
} | ||
}; | ||
// If the help flag is set, display the help text and exit. | ||
const argv = ArgMate(args, params, config); | ||
// Display help and exit if the help flag is set | ||
if (argv.help) { | ||
console.log(helpText()); | ||
process.exit(); | ||
console.log(argInfo()); | ||
process.exit(0); | ||
} | ||
// Run a loop based on parsed arguments. | ||
// Use the parsed arguments | ||
for (let i = argv.start; i < argv.start + argv.steps; i++) { | ||
console.log(i); | ||
console.log(i); | ||
} | ||
``` | ||
### Default Behavior | ||
```js | ||
import argMate from 'argmate'; | ||
let argv; | ||
// By default, parameters are treated as boolean. | ||
argv = argMate(['--foo', 'bar']); | ||
// Output: {_: ['bar'], foo: true} | ||
// If the type is explicitly set, it will be parsed accordingly. | ||
argv = argMate(['--foo', 'bar'], {foo: {type: 'string'}}); | ||
// Output: {_: [], foo: 'bar'} | ||
``` | ||
## Configuration | ||
@@ -133,12 +175,12 @@ | ||
const params = { | ||
// The object returned from argMate will only have propety names provided in this object | ||
// The object returned from argMate will only have propety names provided in this object (foo in this example) | ||
foo: { | ||
type: 'string', // boolean | string | number/float | int | hex | array/string[] | number[]/float[] | int[] | hex[] | ||
default: 'val', // The default value for the parameter. If the type is not specified, the type will be determined from this field. | ||
type: 'string', // boolean | string/number | float | int | hex | array7string[] | number[]/float[] | int[] | hex[]. Optional. Defaults to boolean. | ||
default: 'val', // The default value for the parameter. If the type is not specified, the type will be determined from this field. Optional. | ||
mandatory: true, // Calls config.error if the value is not provided. No effect if used in combination with "default". | ||
alias: [], // Other values to be treated as this parameter. Also accepts a single string. | ||
// If you camelCase the keyword, it will treat kebab-case of the word as an alias | ||
alias: [], // Other values to be treated as this parameter. Also accepts a string with a single value. | ||
// If you camelCase the property name, it will treat kebab-case of the word as an alias (so fooBar will automaticly have foo-bar as alias) | ||
conflict: [], // Other keys to be treated as conflicting. Also accepts a single string. | ||
valid: () => {}, // Function to check if the value is valid (will call config.error if not valid) | ||
describe: 'Description here', // A description of the parameter. Will be used for the helpText (see below). | ||
describe: 'Description here', // A description of the parameter. Will be used for the help text (see below). | ||
}, | ||
@@ -161,8 +203,8 @@ }; | ||
## Help Text | ||
### Help Text | ||
You can call `helpText()` after invoking `argMate()` to get a CLI-friendly description of the options. | ||
You can call `argInfo()` after invoking `argMate()` to get a CLI-friendly description. | ||
```js | ||
import argMate, {helpText} from 'argmate'; | ||
import argMate, {argInfo} from 'argmate'; | ||
@@ -182,7 +224,7 @@ const argv = argMate( | ||
console.log( | ||
helpText({ | ||
width: 100, // Max character limit in the width of the output. | ||
format: 'cli', // cli | markdown | ||
voidIntro: false, // Avoid including the intro. | ||
voidOutro: false, // Avoid including the outro. | ||
argInfo({ | ||
width: 100, // Max character limit of the width of the output. | ||
format: 'cli', // cli | markdown. Default CLI. | ||
voidIntro: false, // Avoid including the intro. Default false. | ||
voidOutro: false, // Avoid including the outro. Default false . | ||
}) | ||
@@ -192,2 +234,6 @@ ); | ||
## Notes | ||
- If you provide array kind of types (like string[]) you can trust the value is alwas an array. If no values provided the array is emptly. | ||
- If you provide the same alias to two parameters, the alias will stay with the first parameter you define. | ||
--- | ||
@@ -203,1 +249,3 @@ | ||
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fmathiasrw%2Fargmate.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fmathiasrw%2Fargmate?ref=badge_large) | ||
#!/usr/bin/env node | ||
// @ts-ignore | ||
//export {ArgMateParams, ArgMateConfig, ArgMateHelpTextConfig} from './types.d.ts'; | ||
import {ArgMateParams, ArgMateConfig} from './types.js'; | ||
// @ts-ignore | ||
import {ArgMateParams, ArgMateConfig} from './types.d.ts'; | ||
export {compileConfig, precompileConfig} from './compileConfig.js'; | ||
import {engine} from './engine'; | ||
// @ts-ignore | ||
export {argInfo} from './argService.js'; | ||
import formatParamInfo from './helpText'; | ||
// @ts-ignore | ||
import {argService} from './argService.js'; | ||
let params_; | ||
// @ts-ignore | ||
import argEngine from './argEngine.js'; | ||
let conf_; | ||
export default function argMate( | ||
args: string[], | ||
params: ArgMateParams = {}, | ||
conf: ArgMateConfig = {} | ||
) { | ||
params_ = JSON.stringify(params); | ||
conf_ = conf; | ||
return engine(args, params, conf); | ||
export default function argMate(args: string[], params?: ArgMateParams, conf?: ArgMateConfig) { | ||
return argService(argEngine, args, params, conf); | ||
} | ||
export function helpText(settings: any = {}) { | ||
return formatParamInfo(JSON.parse(params_), conf_, { | ||
...{ | ||
width: 100, | ||
format: 'cli', | ||
voidIntro: false, | ||
voidOutro: false, | ||
}, | ||
...settings, | ||
}); | ||
} |
@@ -18,3 +18,3 @@ export interface ArgMateParams { | ||
| 'hex[]'; | ||
default?: any; | ||
default?: string | number | string[] | number[]; | ||
mandatory?: boolean; | ||
@@ -41,2 +41,3 @@ alias?: string | string[]; | ||
allowKeyNumValues?: boolean; | ||
allowAssign?: boolean; | ||
intro?: IntroOutroType; | ||
@@ -46,7 +47,43 @@ outro?: IntroOutroType; | ||
export interface ArgMateHelpTextConfig { | ||
export interface ArgMateConfigMandatory extends ArgMateConfig { | ||
error: (msg: string) => void; | ||
panic: (msg: string) => void; | ||
} | ||
export interface ArgMateArgInfoConfig { | ||
width?: number; | ||
format?: 'cli' | 'markdown' | 'json'; | ||
voidIntro?: boolean; | ||
voidOutro?: boolean; | ||
preIntro?: IntroOutroType; | ||
showIntro?: boolean; | ||
showOutro?: boolean; | ||
postOutro?: IntroOutroType; | ||
} | ||
export default function argMate( | ||
args: string[], | ||
params?: ArgMateParams, | ||
conf?: ArgMateConfig | ||
): {[key: string]: any}; | ||
type ArgProcessObj = void | { | ||
output: {[key: string]: any}; | ||
mandatory: string[]; | ||
validate: string[]; | ||
complexDefault: {[key: string]: string[] | number[]}; | ||
conf: ArgMateConfigMandatory; | ||
params: ArgMateParams; | ||
}; | ||
export function compileConfig( | ||
params: ArgMateParams, | ||
conf: ArgMateConfigMandatory, | ||
precompile: boolean | ||
): ArgProcessObj | string; | ||
export function argEngine(params: ArgProcessObj): {[key: string]: any}; | ||
export function argInfo( | ||
settings: ArgMateArgInfoConfig, | ||
conf?: ArgMateConfig, | ||
params?: ArgMateParams | ||
): string; |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
76499
19
1161
244
2