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 🙏

© 2026 – Pkg Stats / Ryan Hefner

pargs

v1.7.0

Published

A useful wrapper for `util.parseArgs`

Readme

pargs Version Badge

github actions coverage License Downloads

npm badge

A wrapper for node’s built-in util.parseArgs with helpful features added.

Usage

#!/usr/bin/env node

import pargs from 'pargs';

const {
    help,
    positionals,
    values,
    errors, // a mutable string array; push to it and pargs will include your error messages.
    name, // if subcommands are used
    tokens,
} = await pargs(import.meta.filename, options);

// do extra validation here

await help(); // to handle `--help` and print the help text if needed, or to print errors and exit

Help

Help text is automatically read from a help.txt file adjacent to import.meta.filename, when one is present.

When no help.txt file exists, help text is generated automatically from the provided config - the usage line, options (with their types, choices, defaults, and short flags), subcommands, and positional argument requirements. A help.txt file, when present, always takes precedence over the generated text.

The program name in the generated usage line comes from the nearest package.json (the bin entry that points at the entrypoint, falling back to the unscoped package name, then to the file’s basename).

The following config fields exist solely to enrich generated help; they are ignored by util.parseArgs:

  • options[name].description: a string describing the option.
  • options[name].placeholder: the value placeholder shown for a non-boolean option (eg, placeholder: 'MM/DD/YYYY' renders --before <MM/DD/YYYY> instead of <string>).
  • options[name].group: a heading to group the option under; ungrouped options appear first under Options, and --help is always listed there.
  • options[name].defaultDescription: a string shown as the default in place of the actual default value - useful to mask a secret, or to show an env-derived default symbolically (eg, $HOME/.cache rather than the resolved path).
  • positionals: an array of { name, description?, rest? }, used to name positionals in the usage line (<name> when required per minPositionals, else [name]; rest: true makes it variadic) and to render an Arguments: section.
  • description (on a config or subcommand): either a string (used as the summary), or an object { summary?, examples?, sections? }, where examples is an array of strings or { command, description? }, and sections is an array of { title, body } for free-form blocks (eg, Behavior, Exit codes).

Option defaults are shown as (default: …), except that a boolean option’s default: false is omitted (it is the implicit default; default: true is still shown). Array defaults render as [a, b] / [].

await an invocation of the help function returned from the pargs call to handle --help and print the help text if needed, or to print errors and exit.

It returns which path it handled - 'version', 'help', 'errors', or false when there was nothing to handle. Passing { exit: false } makes it return instead of calling process.exit(), which is what a function that parses args but is not itself the process entrypoint needs, since process.exit can not be caught. process.exitCode is still set, so if (await help({ exit: false })) { return; } is a working drop-in.

Version

--version is provided automatically: the same await help() call handles it, printing the version field from the nearest package.json, prefixed with v, and exiting.

The root version config controls it. It is deprecated, and marked @deprecated in the types: it exists so a CLI moving onto pargs can keep the exact --version output it already ships, and new code should take the default.

  • true, undefined, or absent: the package.json lookup described above.
  • a string: printed verbatim, with no v prefix and no package.json lookup - useful to match an existing CLI's output, or to print more than the bare number.
  • false: no built-in --version at all. It is then an unknown option, and the generated help omits its row.

Subcommands inherit the root version: a CLI that has no built-in --version, or that prints its own string, means that at every level. A subcommand may declare its own version to override what it inherited.

Unlike help, version is not reserved - if you define your own version option, yours is used instead and the built-in one is not added. Note that this means you must print it yourself; await help() will not, and a --version it does not own falls through to your program's normal path - including when a defaultCommand is what parsed the flag. Declaring an options.version alongside a root version of false or a string throws, since the two disagree about who owns --version; a root version of true is the default, so it is accepted and your option wins. That applies to one level: a subcommand declaring its own version option alongside a version it merely inherited yields to the option instead of throwing, since the two were not written in one place.

Options

See the node.js parseArgs documentation for some context.

In TypeScript, options is a union discriminated on type, so a table hoisted into its own variable needs as const (or 'string' as const per entry) - otherwise type widens to string and matches no branch. Written inline in the call, it is inferred correctly as-is.

  • strict: can not be set to false - strictness all the way.
  • allowNegative: can not be set to false.
  • usageOnError: where the usage text goes when help() reports an error the user did not ask for. 'stdout' is the default and current behavior; 'stderr' keeps it off stdout, which matters for a CLI whose own output is piped or redirected; false prints no usage at all, matching what most parsers do on a bad flag. Error messages always go to stderr regardless. A --help the user explicitly asked for is program output, so it always prints, and always to stdout, even alongside an error and even under false. Inherited by subcommands.
  • shorts: deprecated, and marked @deprecated in the types - it exists so a CLI moving onto pargs can keep the -h/-V short flags it already ships, and new code should use the long forms, which are always available. Opts in to short flags for the reserved --help/--version. true requests the conventional -h and -V, and silently yields either letter to an option that already claims it (so -v can stay yours while -V is the built-in version). An object - { help?, version? } - requests specific letters, and throws if one collides, since you asked for it by name. Omitted, or false, registers nothing, which is the default. shorts.version is not allowed when there is no built-in --version to attach it to. Subcommands inherit the root shorts and may declare their own to override it; an inherited letter that collides with a subcommand's own option is skipped rather than throwing, since the collision was not written in one place.
  • negation: 'exclusive' (the default) reports an error when both --x and --no-x appear in the same invocation; 'last-wins' suppresses that error and leaves the parsed value alone, so the last occurrence wins for a scalar boolean, and every occurrence is collected for a multiple one. May be set at the root, or per-option (options[name].negation) to override the root; it does not inherit into subcommands. The reserved --help and --version are unaffected - --no-help is always an unknown option.
  • args: when omitted, pargs uses process.argv, with the node binary and the entrypoint filtered out. When provided, it is the argument list, used verbatim - nothing is filtered out of it, each element is coerced with String, and a non-array throws. An explicit args also governs subcommand and defaultCommand routing, and suppresses the process.argv mutation that subcommand routing otherwise performs. A subcommand's own args, if it declares one, is overridden by the parent's routing.
  • options.type: in addition to 'boolean' and 'string':
    • 'enum': when provided, a choices string array is also required. The value is validated only when one is present - an option that was not passed and has no default is not an error. With multiple, each element is validated individually.
    • 'number': validates the value is a finite number and coerces it from a string.
    • 'integer': validates the value is a finite integer and coerces it from a string.
  • options[name].variadic: deprecated, and marked @deprecated in the types - it exists so a CLI moving onto pargs can keep an argv shape util.parseArgs deliberately does not support, and new code should take multiple with a repeated option (--plugins a --plugins b), which is unambiguous about where the list ends. One occurrence collects every following value-like argument, so --plugins a b c yields all three rather than dropping b and c into positionals. The run stops at the first argument that looks like an option (unless the option is also greedy, which widens what counts as a value for the whole run), at --, or at the end of the list; an attached value (--plugins=a) does not start a run. A lone - is a value, not an option, so it is collected like any other - --plugins a - b yields all three. Implies multiple: true, so repeated occurrences accumulate and the value is an array; an explicit multiple: false throws, and a scalar default is wrapped in an array for you. The one exception is optionalValue: true, where an occurrence with no values at all yields the scalar true rather than an empty array, exactly as it does without variadic. Not allowed on a boolean option.
  • options[name].optionalValue: the option's value is optional, so it may be passed bare. true makes a bare occurrence yield the boolean true, widening the value type to string | true; a string makes it yield that string, which is injected as an ordinary value and so leaves the type alone. An explicit value is still taken in every spelling - -p x, -px, --package=x, and at the end of a short cluster (-up x) - and an option-looking next token is not consumed, so -p --stdout is a bare -p followed by --stdout. With multiple, a bare occurrence under true contributes nothing to the list, unless every occurrence was bare, in which case the value is the scalar true; under the string form it contributes that string like any other value. Under true, a bare occurrence is exempt from enum choices and from number/integer coercion, since true is not one of those; under the string form the injected value is validated like any other, so an optionalValue outside an option's choices is an error naming that option. An optionalValue of '' is a value like any other, not "no optional value". A declared default is left alone - an unpassed option keeps its default rather than collapsing to true. Not allowed on a boolean option, which already has this shape.
  • options[name].greedy: deprecated, and marked @deprecated in the types - it exists so a CLI moving onto pargs can keep accepting --opt --value, which util.parseArgs rejects as ambiguous while naming the unambiguous spelling in the error (--opt=--value); that is what new code should use. The option takes the next argument as its value even when that argument looks like an option, so --append-git-log --first-parent works without the = form. -- is never consumed - --x -- keeps its argument is ambiguous error, and --x=-- is the way to pass a literal --. Note that a greedy option swallows whatever follows it, including --help and --version; that is inherent to the arity, and matches how other parsers treat it. Not allowed on a boolean option. When any option in a config asks for an arity util.parseArgs can not express, the argument list is rewritten before parsing, so tokens describes the rewritten list: a fused value is reported as one token with inlineValue: true. Configs that do not opt in get back the identical array, and therefore identical tokens. A bare optionalValue occurrence is reported the way parseArgs reports any valueless option, with value and inlineValue undefined. One constraint comes with the rewriting: at a level whose own options opt in, no argument may contain a NUL byte, and one that does throws. The check is per parsed level, not per config, so a subcommand that opts into nothing does not impose it on its own arguments. process.argv can never contain a NUL byte, so this only ever concerns a programmatic args.
  • partialValues: when true, a fatal parse error (an unknown option, a missing option argument) returns whatever else parsed cleanly instead of an empty values. Only declared options survive, and only when the loosely-parsed value still matches the declared type; enum choices and number/integer coercion are applied as usual, and anything that fails is dropped. errors still holds only the single fatal error, positionals come from the loose reparse and so do not re-apply the configured positional policy, and an option with a default may be missing - so values is a partial of its usual type. Defaults to false, and does not inherit into subcommands.
  • allowPositionals: in addition to a boolean, or an integer representing the maximum number of allowed positional arguments.
  • minPositionals: an integer representing the minimum required number of positional arguments.
  • subcommands: if provided, must be an object. Keys are the subcommand names (eg, in npm ls, ls is the subcommand), and values are the configuration options for each subcommand - as if they were a top-level invocation. The subcommand's generated usage line names the command it documents (eg, Usage: cli build [options]). Root options alongside subcommands are parsed git-style: only when they lead the argument list, before the subcommand name (eg, cli --dry-run build). The first argument that is not one of them selects the subcommand, and everything from there on is the subcommand's to parse - so a root option given after the subcommand name is an unknown option for that subcommand. When at least one root option is passed and no subcommand follows (eg, cli --setup), that is the whole invocation: there is no error, and command is undefined; a bare invocation with no arguments at all is still an unknown command error. A root option takes its values exactly as it would with no subcommands, except that a variadic run of values ends at a subcommand name (so cli --files a b build runs build). The first value is taken as it would be anywhere, so cli --files build collects build; a later value spelled like a subcommand can still be given inline, as --files=build. An error in a root option (eg, an invalid enum choice) is reported by the returned help(), along with any of the subcommand's, even when a subcommand runs. A root-level --help or --version is answered at the root, even when a subcommand follows it (see defaultCommand for how one is answered when there is a default command); when no subcommand follows it, command is undefined with no error, even though the types describe it as present, so call help() before reading command.
  • defaultCommand: only allowed alongside subcommands; must be the name of one of them. When the first argument (after any leading root options) is not a recognized subcommand (including when it is a flag, or absent entirely), the rest of the argument list is parsed against this command instead of erroring with unknown command. A root option that the default command also accepts - by name, or by a short flag it claims, including the ones its own --help/--version resolve to - belongs to the default command, so a root that mirrors its default command's options (eg, for the help text) keeps routing them there; when the root also has options of its own, the generated help lists the shared ones (other than any with a group of their own) under a <command> options heading, with a usage line of their own, and shows the short flags the default command actually uses. A leading --help/--version does not end the root options, but it is passed along to the default command - even when a subcommand name follows it - which answers it with the root help, as before; a --version the default command does not have (eg, version: false) is passed along too, and is an unknown option there, as before. This enables a bare default form (eg, vers <input>) to coexist with named subcommands. --help/--version still apply at the level invoked: a root-level --help (no recognized subcommand) shows the root help (the command list), not the default command's help.

Install

``

npm install --save pargs

License

MIT

Thanks

Thanks to @ibakaidov for donating the pargs package name!