npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

argmate

v0.8.2

Published

Fast CLI parameter parsing. Zero dependencies. Good DX. Pre-compile for turbo speed.

Downloads

25

Readme

mamate

ArgMate

CI-test NPM downloads npm version FOSSA Status OPEN open source software

Your go-to mate for CLI parameter parsing. Friendly, faster than a cut snake, and with added sprinkles of convenience to make your development experience a breeze. Only 6KB and zero dependencies - cheers!

While developing tools like AlaSQL and RexReplace, I've often been torn between two types of CLI parsers. On one hand, there are feature-rich options like yargs and 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 and 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.

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

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

yarn add argmate
# or
npm install argmate

Usage

argMate(arguments, [parameterDetails [, config ]]);

Examples

Getting started

ArgMate follows traditional CLI notations - similar to yargs and mri. Here are some simple examples:

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', '-i']);
// {_: ['bar'], foo: true, i: 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', '-i=123']);
// {_: [], foo: 'bar', 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', i: 42 });
// {_: [], foo: 'bar2', i: 42}

// Example running params from CLI `node index.js --foo=bar -X .md`
argv = argMate(process.argv.slice(2));
// { _: ['.md'], foo: "bar", X: true }

Default values and limiting input to known parameters

You can provide default values and enforce that no unknown parameters are allowed:

import argMate from 'argmate';

const args = process.argv.slice(2);

// Define parameter types and default values
const params = {
	foo: 10,	// --foo is expected to be an integer, default: 10
	bar: false  // --bar is expected to be a boolean, default: false
};

const config = {
	allowUnknown: false  // Only allow specified parameters (--foo and --bar)
};

const argv = argMate(args, params, config);

Same example but a bit shorter

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:

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  // Validate the input
	},
	help: {
		alias: ['h']
	}
};

const config = {
	allowUnknown: false,
	error: msg => {
		console.error('Error:', msg);
		process.exit(1);
	}
};

const argv = argMate(args, params, config);

// Display help and exit if the help flag is set
if (argv.help) {
	console.log(argInfo());
	process.exit(0);
}

// Use the parsed arguments
for (let i = argv.start; i < argv.start + argv.steps; i++) {
	console.log(i);
}

Enforcing parameter types and limiting allowed values

You can provide default values and enforce that no other parameters are allowed:

import argMate from 'argmate';

const args = process.argv.slice(2);

// Define parameter types and default values
const params = {
    foo: 10,    // --foo is expected to be an integer, default: 10
    bar: false  // --bar is expected to be a boolean, default: false
};

const config = {
    allowUnknown: false  // Only allow specified parameters (--foo and --bar)
};

const argv = argMate(args, params, config);

Same example but a bit shorter

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:

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  // Validate the input
    },
    help: {
        alias: ['h']
    }
};

const config = {
    allowUnknown: false,
    error: msg => {
        console.error('Error:', msg);
        process.exit(1);
    }
};

const argv = argMate(args, params, config);

// Display help and exit if the help flag is set
if (argv.help) {
    console.log(argInfo());
    process.exit(0);
}

// Use the parsed arguments
for (let i = argv.start; i < argv.start + argv.steps; i++) {
    console.log(i);
}

Configuration

Params

the second parameter for argMate is a a configuration object defining the parameters you expect and their propeties

const params = {
	// The object returned from argMate will only have propety names provided in this object (foo in this example) But see outputAlias config below 
	foo: {
		type: 'string', 				// boolean | string | number | float | int | hex | array | string[] | 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 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). Can also be a comma seperated string
		conflict: [], 					// Other keys to be treated as conflicting. Also accepts a single string. Can also be a comma seperated string. 
		valid: () => {}|[], 				// Function to check if the value is valid (will call config.error if not valid). Can also be an array of valid values (case sensitive). If you want case insensitive make a function with a regex valid:v=>/foo|bar/i.test(v) will accept both Foo and BAR.
		transform: 						// function that will transform the value. Example: trim values by using transform:v=>v.trim();
		describe: 'Description here', 	// A description of the parameter. Will be used for the help text (see below).
	},
};

Config

// THe default values of all possible porperties of the config object
const config = {
	error: msg => {throw msg},		// This function will be called when there is a problem with input data (foreample if you try to assign a value to a parameter you have defined as boolean). Defaults to throwing the error messages.
	panic: msg => {throw msg},		// This function will be called when there is a problem with the configuration of the parameters. YOu should only encounter these during development. Defaults to throwing the error messages. 
	allowUnknown: true, 	// Allows you to provie parameters not defined in the config objecet
	allowNegatingFlags: true, 				// Will let you prepend boolean parameters with "no-" provide the value as false. If so, --no-foo will result in {'_':[], 'foo': false}. 
	allowKeyNumValues: true		// Allows you to use ultra short notations like '-r255' to set -r = 255 
	allowAssign: true			// Allow the use of = after a parameter to indicate that the next value should to be assigned as a value. Works both for the value as part of the same parameter (-p=2) or the value in the next argument (-p= 2)
	allowBoolString: true		// Let you assign boolean parameters from strings like true|yes|on|false|no|off
	strict: false				// Will set all allow* propeties to false. Individual paramters can overwrite this by also being provided. 
	autoCamelKebabCase: true	// Let you treat input like 'foo-bar' as 'fooBar'
	outputAlias: false			// If set to true the returned data object will contain one property per parameter plus one for each alias. (Normally --foo with -f as alias will only come as {foo:...}. If this option is set to true it will output {foo:..., f: ...})
	outputInflate:false			// Will expand keys with dots in them into nested objects (--a.b=22 will result in {a:{b:22}})		
	intro: 'Intro Text', 	// Text that goes above the information about each parameter in the help text.
	outro: 'Outro Text', 	// Text that goes below the information about each parameter in the help text.
};

Help Text

You can call argInfo() after invoking argMate() to get a CLI-friendly description.

import argMate, {argInfo} from 'argmate';

const argv = argMate(
	process.argv.slice(2),
	{
		foo: {type: 'string'},
		foo2: {type: 'string'},
	},
	{
		intro: 'Introduction here', 	// Text to add above the information about each parameter in the help text.
		outro: 'See you later!', 		// Text to add below the information about each parameter in the help text.
	}
);

console.log(
	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 .
	})
);

Notes

  • Demonstrate how to use macros to pregenerate engineConfig to make things even faster. manual or via https://bun.sh/docs/bundler/macros - https://bun.sh/docs/bundler/macros#export-condition-macro

  • 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 dont specify, you get some help, but not consistency. If you specify you know exactly what you get.

  • Defaults to consider unknown params as flags. IF you want unknown things to be assigned you add a = behind the flag.

    • undefined parameters will default to being a boolean. If you want to assign values you need to A) define a type (or a default value) in the config obj, or B) add "=" to the parameter in the inputs
  • If you provide the same alias to two parameters, the alias will stay with the first parameter you define.

  • for defined params you need to provide int, number or float as type for it to be a number in the resulting data object expect( argMate(['--host', 'localhost', '--port', '555'], { host: '', port: 0, }) ).toEqual({ host: 'localhost', port: 555, _: [], });

  • but if you have not defined the param and provide is as assigned then numbers will be identified and provided as value

      	expect(argMate(['--host=', 'localhost', '--port=', '555'], {})).toEqual({
      		host: 'localhost',
      		port: 555,
      		_: [],
      	});
      	expect(argMate(['--host=localhost', '--port=55.5'], {})).toEqual({
      		host: 'localhost',
      		port: 55.5,
      		_: [],
      	});

lite quirks

  • Lite will not convert your 0x prepended hexvalues to int

ideas

  • ? Flag to outoconvert _ values to int when convertable? (like deno)
  • We do not support autoconverting magic strings like "true" and "false"
    • maybe we should have an option to convert magic strings...
  • ? input type json thet is parsed and added as data?
  • ? Commaseperated list to array?

Please note that argMate is an OPEN open source software project. This means that individuals making significant and valuable contributions are given commit access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project.

OPEN open source software

License

FOSSA Status