npm.io
1.4.1 • Published 13h ago

cli-nano

Licence
MIT
Version
1.4.1
Deps
0
Size
67 kB
Vulns
0
Weekly
0
Stars
1

License: MIT TypeScript Vitest codecov npm npm npm bundle size Node

cli-nano

Small library to create command-line tool (aka CLI) which is quite similar to Yargs, it is as configurable as Yargs but is a fraction of its size. The library is also inspired by NodeJS parseArgs() but is a lot more configurable in order to get what we would expect from a more complete CLI builder tool.

Features

  • Parses arguments
  • Supports defining Positional arguments (input args)
    • Supports Variadic arguments (1 or more positional args)
    • Interleaved positionals — positionals may appear anywhere in the arg list (enabled by default). Set command.allowInterleaved = false to disable.
  • Automatically converts flags to camelCase to match config options
    • accepts both --camelCase and --kebab-case
  • Negates flags when using the --no- prefix
  • Clustered short flags (e.g. -ad behaves like -a -d)
  • Outputs version, when defined, by using --version
  • Outputs description and supplied help text by using --help
  • Supports defining required options
  • Supports default values
  • Supports group for grouping command options in help
  • No dependencies and very lightweight!
Recent additions
  • Interleaved positionals: positional arguments may appear anywhere in the argument list, interleaved with option flags — e.g. cmd --flag pos1 --other pos2. This behavior is enabled by default. Set command.allowInterleaved = false to opt out.
  • Grouped short flags: clustered short aliases (e.g. -ad behaves like -a -d); the last short in the cluster may consume the next token as its value. Kebab/camel duplication (default): parsed options are available under both camelCase and kebab-case keys (e.g. both dryRun and dry-run). Equivalent in yargs: camel-case-expansion (enabled by default).
  • -- passthrough: tokens after -- are exposed on result['--'] and are not parsed as options.
  • Raw argv exposure: the original argv slice is available on result.__rawArgs for diagnostics or passthrough adapters.
  • Bare long-form behavior: a bare long option with no following value is treated as an empty string (e.g. --opt''); a bare long array option becomes [''].
  • Boolean negation parity: negated booleans expose both noFoo and no-foo keys (e.g. --no-dry-run sets dryRun=false and also exposes noDryRun=true and no-dry-run=true).
npm install cli-nano
Usage
#!/usr/bin/env node

import { type Config, parseArgs } from 'cli-nano';

const config: Config = {
  command: {
    name: 'serve',
    describe: 'Start a server with the given options',
    examples: [
      { cmd: '$0 ./www/index.html 8080 --open', describe: 'Start web server on port 8080 and open browser' },
      {
        cmd: '$0 ./index.html 8081 --no-open --verbose',
        describe: 'Start web server on port 8081 without opening browser and print more debugging logging to the console',
      },
    ],
    positionals: [
      {
        name: 'input',
        describe: 'serving files or directory',
        type: 'string',
        variadic: true, // 1 or more
        required: true,
      },
      {
        name: 'port',
        type: 'number',
        describe: 'port to bind on',
        required: false,
        default: 5000, // optional default value
      },
    ],
  },
  options: {
    dryRun: {
      alias: 'd',
      type: 'boolean',
      describe: 'Show what would be executed but without starting the server',
      default: false, // optional default value
    },
    display: {
      group: 'Advanced Options',
      alias: 'D',
      required: true,
      type: 'boolean',
      describe: 'a required display option',
    },
    exclude: {
      alias: 'e',
      type: 'array',
      describe: 'pattern or glob to exclude (may be passed multiple times)',
    },
    verbose: {
      alias: 'V',
      type: 'boolean',
      describe: 'print more information to console',
    },
    open: {
      alias: 'o',
      type: 'boolean',
      describe: 'open browser when starting server',
      default: true,
    },
    cache: {
      type: 'number',
      describe: 'Set cache time (in seconds) for cache-control max-age header',
      default: 3600,
    },
    address: {
      type: 'string',
      describe: 'Address to use',
      required: true,
    },
    rainbow: {
      group: 'Advanced Options',
      type: 'boolean',
      alias: 'r',
      describe: 'Enable rainbow mode',
      default: true,
    },
  },
  version: '0.1.6',
  helpFlagCasing: 'camel',  // show help flag option in which casing (camel/kebab) (defaults to 'kebab')
  helpDescMinLength: 40,    // min description length shown in help (defaults to 50)
  helpDescMaxLength: 120,   // max description length shown in help (defaults to 100), will show ellipsis (...) when greater
  helpUsageSeparator: ':',  // defaults to "→"
};

const args = parseArgs(config);
console.log(args);

// do something with parsed arguments, for example
const { input, port, open } = args;
startServer({ input, port, open });
Usage with Type Inference

For full TypeScript auto-inference and intelliSense of parsed arguments, define your config as a const and cast it as const:

const config = {
  ... // your config object as shown above
} as const;

const args = parseArgs<typeof config>(config);

// TypeScript will infer the correct types:
args.input;   // [string, ...string[]] (required, variadic)
args.port;    // number  (optional, has default)
args.verbose; // boolean (optional)
args.display; // boolean (required)

Tip:
Using as const preserves literal types and tuple information, so TypeScript can infer required/optional fields and argument types automatically.
If you use const config: Config = { ... }, you get type checking but not full intelliSense for parsed arguments.

For required+variadic positionals, the type is [string, ...string[]] (at least one value required). For optional variadic, it's string[]. And Finally for non-variadic, it's string.

Example CLI Calls
# Show help guide (creates it by reading CLI config)
serve --help

# Show version (when defined)
serve --version

# Uses default port 5000
serve dist/index.html

# With required and optional positional args
serve index1.html index2.html 8080 -D value

# With boolean and array options entered as camelCase (kebab-case works too)
serve index.html 7000 --dryRun --exclude pattern1 --exclude pattern2 -D value

# With negated boolean entered as kebab-case
serve index.html 7000 --no-dryRun -D value

# With short aliases (case sensitive)
serve index.html 7000 -d -e pattern1 -e pattern2 -D value

# With number option
serve index.html 7000 --up 2 -D value

# Clustered short flags / last-short consumption
serve index.html 7000 -ad
serve index.html 7000 -ab value

# Equals form and bare-long
serve index.html 7000 --opt=value
serve index.html 7000 --opt=
serve index.html 7000 --bar

# Negation parity and passthrough
serve index.html 7000 --no-dry-run
serve index.html 7000 -- file.txt --not-a-flag

# Interleaved positionals (enabled by default)
serve --dryRun index.html 7000 -D value
serve index.html --exclude dist 7000 -D value
Notes
  • Default values: Use the default property in an option or positional argument to specify a value when the user does not provide one.

    • Example for option: { type: 'boolean', default: false }
    • Example for positional: { name: 'port', type: 'number', default: 5000 }
  • Variadic positionals: Use variadic: true for arguments that accept multiple values.

  • Required options: Add required: true to enforce presence of an option.

  • Negated booleans: Use --no-flag to set a boolean option to false.

  • Array options: Repeat the flag to collect multiple values (e.g., --exclude a --exclude b).

  • Aliases: Use alias for short flags (e.g., -d for --dryRun).

  • Groups: Use group for grouping some commands in help (e.g., { group: 'Extra Commands' }).

  • Equals form: --opt=value and --opt= are supported; an empty value after = yields an empty string.

  • Passthrough: Tokens after -- are not parsed and are available on result['--'].

  • Raw argv: The original argv slice is available on result.__rawArgs for diagnostics or passthrough adapters.

  • Negation duplicates: Using --no-foo sets foo=false and also exposes noFoo and no-foo keys.

  • Alias: alias must be a single string (multi-char aliases are supported). Array-style aliases are not supported and will be rejected.

  • Interleaved positionals: Positional arguments may appear anywhere alongside option flags by default. Set command.allowInterleaved = false to require positionals-first parsing.

    • Yargs equivalent: to stop at the first non-option use parserConfiguration({ 'halt-at-non-option': true }).

See examples/ for more usage patterns.

Clarifications
  • Subcommand parsing (modes): cli-nano can be used to parse either a top-level command or a subcommand. When parsing a subcommand, attach the subcommand's options under command.options in that subcommand's Config so flags are recognized for that subcommand invocation. Example shape:
const config = {
  command: {
    name: 'exec',
    positionals: [ /* ... */ ],
    options: { // subcommand-specific options live here
      parallel: { type: 'boolean', describe: 'run in parallel' },
    },
  },
  options: { /* top-level options */ },
};
  • Subcommand options tip: If you only add flags to the top-level options object but intend them for a subcommand, parsing will not recognize them for that subcommand. Put subcommand flags on config.command.options.

  • Interleaving vs yargs: Interleaved positionals are enabled by default in cli-nano (positionals may appear anywhere). To opt out and require positionals-first (yargs' typical halt-at-non-option behavior), set:

command.allowInterleaved = false;

This makes cli-nano behave like yargs configured with parserConfiguration({ 'halt-at-non-option': true }).

Help Example

You can see below an example of a CLI help (which is the result of calling --help with the config shown above).

Please note the following expectations:

  • <option> → required
  • [option] → optional
Usage:
  serve <input..> [port] [options] → Start a server with the given options

Examples:
  serve ./www/index.html 8080 --open → Start web server on port 8080 and open browser
  serve ./index.html 8081 --no-open --verbose → Start web server on port 8081 without opening browser and print more debugging logging to the console

Arguments:
  input           serving files or directory                                      <string..>
  port            port to bind on                                                 [number]

Options:
  -d, --dry-run   Show what would be executed but without starting the server     [boolean]
  -e, --exclude   pattern or glob to exclude (may be passed multiple times)       [array]
  -V, --verbose   print more information to console                               [boolean]
  -o, --open      open browser when starting server                               [boolean]
      --cache     Set cache time (in seconds) for cache-control max-age header    [number]
      --address   Address to use                                                  <string>
  -h, --help      Show help                                                       [boolean]
  -v, --version   Show version number                                             [boolean]

Advanced Options:
  -D, --display   a required display option                                       <boolean>
  -r, --rainbow   Enable rainbow mode                                             [boolean]

Used by

cli-nano is currently being used by the following projects, which is actually why I created this CLI tool, that I currently maintain as well (feel free to edit this list):