cleye

repository·master·Indexed 20 days ago

https://github.com/privatenumber/cleye

An intuitive command-line interface (CLI) development tool for Node.js. It provides a minimal API surface for parsing arguments and flags with strong typing, automatic help documentation generation, and support for sub-commands, custom flag types, and strict flag parsing.

Tokens
12.2K
Snippets
43
Records
53
Agent score
71%

What's inside cleye

  1. Generate help documentation with --help

    master

    Cleye automatically generates help documentation based on your cli() configuration. Users can view this documentation by passing the --help flag to your script.

    $ node greet.js --help
    
    greet.js
    
    Usage:
      greet.js [flags...] <first name> [last name]
    
    Flags:
      -h, --help                 Show help
          --time <string>        Time of day to greet (morning or evening) (default: "morning")
  2. Quick Patterns for cleye CLI development

    master

    Use these common patterns to structure your Node.js CLI scripts with cleye:

    • Basic CLI: Use cli({ name, parameters, flags }) for a simple script.
    • Named command: Use command({ name, parameters, flags }, callback) to define a specific subcommand.
    • Register commands: Pass an array of commands to the commands option in cli({ commands: [cmd1, cmd2] }).
    • Async callback: Pass an async function as the callback to cli to handle asynchronous logic.
    • Raw argv: Provide process.argv.slice(2) as the final argument to cli to use custom input instead of the default.
    // Basic CLI
    cli({ name, parameters, flags })
    
    // Named command
    command({ name, parameters, flags }, callback)
    
    // Register commands
    cli({ commands: [cmd1, cmd2] })
    
    // Async callback
    await cli({ ... }, async (argv) => { ... })
    
    // Raw argv
    cli({ ... }, callback, process.argv.slice(2))
  3. Use cleye to build a CLI

    master

    Cleye simplifies CLI development by handling argv parsing, providing strongly typed parameters and flags, and automatically generating --help documentation.

    To use it, call the cli() function with a configuration object defining the script name, positional parameters, and flags.

    import { cli } from 'cleye'
    
    // Parse argv
    const argv = cli({
        name: 'greet.js',
    
        // Define parameters using bracket notation:
        // <name> for required, [name] for optional
        parameters: [
            '<first name>', // First name is required
            '[last name]' // Last name is optional
        ],
    
        // Define flags/options
        flags: {
            // Parses `--time` as a string
            time: {
                type: String,
                description: 'Time of day to greet (morning or evening)',
                default: 'morning'
            }
        }
    })
    
    // Accessing parameters via argv._
    const name = [argv._.firstName, argv._.lastName].filter(Boolean).join(' ')
    
    // Accessing flags via argv.flags
    if (argv.flags.time === 'morning') {
        console.log(`Good morning ${name}!`)
    } else {
        console.log(`Good evening ${name}!`)
    }
  4. Enable Strict Flags

    master

    When strictFlags: true is set in the cli() or command() configuration, the parser will throw an error if an unknown flag is provided (e.g., --baz when only --foo is defined).

    cli({
        flags: { foo: Boolean },
        strictFlags: true
    })
    // --baz → Error: Unknown flag: --baz. (Did you mean --foo?)
  5. How the render method processes nodes

    master

    The render(nodes) method is the primary entry point for generating output. It recursively processes input which can be:

    1. A single string.
    2. An Array of nodes.
    3. A HelpDocumentNode object: { type: string, data: any }.

    When a node object is encountered, the method looks up a method on the Renderers instance matching the type and calls it with data. If the type does not match a valid method, it throws an Error: Invalid node type: ....

  6. Configure help and version flags

    master

    Cleye handles --help and -h by default. You can customize or disable them.

    • Disable Help: Set help: false in the config. You can still call .showHelp() manually.
    • Enable Version: Specify the version property. This also adds the version to the help documentation.
    • Customizing Version: To show the version in help but not handle the --version flag, use help: { version: '...' }.
    cli({
        version: '1.2.3'
    })
    
    // $ my-script --version
    // 1.2.3
    cli({
        version: '1.2.3'
    })
  7. Configure Help and Version

    master

    The help object allows you to customize the auto-generated help documentation. Setting a version in the cli() options automatically enables the --version flag.

    Help Options

    • description: A description of the tool.
    • usage: A usage string (e.g., my-script [flags] <file>).
    • examples: An array of usage examples.
    • version: A version string to show in the help output.
    • render: A function to customize how help nodes are rendered.

    Note: If you set help: false, auto-help handling is disabled, and you must call argv.showHelp() manually.

    cli({
        name: 'my-script',
        version: '1.2.3',
        help: {
            description: 'Does things',
            usage: 'my-script [flags] <file>',
            examples: ['my-script foo.txt', 'my-script --output=dist foo.txt'],
            version: '1.2.3'
        }
    })
  8. Define CLI parameters using angle and square brackets

    master

    When defining the parameters array in your CliOptions, you can specify how arguments are treated using specific bracket syntax:

    • Required parameters: Wrap the name in angle brackets <name>. If the parameter is missing from the command line, the CLI will throw an error and show help.
    • Optional parameters: Wrap the name in square brackets [name]. These do not trigger errors if omitted.
    • Spread parameters: Append ... to the name inside the brackets, e.g., [...items]. This captures all remaining arguments into an array. Spread parameters must always be the last parameter defined.

    Constraints:

    • Required parameters cannot follow optional parameters.
    • Parameter names cannot contain special characters like |, \, {, }, (, ), [, ], ^, $, +, *, ?, or ..
    // Example parameter definitions
    const options = {
      parameters: ['<id>', '[name]', '[...tags]']
    };
  9. Define subcommands with the `commands` option

    master

    You can organize your CLI into subcommands by providing an array of command objects to the commands property in your CliOptions. Each command can have its own unique name, alias, flags, parameters, and callback function.

    When a user runs my-cli <command>, the CLI identifies the command and executes its specific logic. The command inherits strictFlags and booleanFlagNegation from its parent if they are not explicitly set on the command itself.

    import { cli } from 'cleye';
    
    await cli({
      name: 'app',
      commands: [
        {
          name: 'init',
          alias: 'i',
          options: {
            force: { type: 'boolean', description: 'Force initialization' }
          },
          callback: (parsed) => {
            if (parsed.force) console.log('Forcing...');
          }
        }
      ]
    });
  10. Handle unknown flags with ignoreArgv

    master

    If you want to silently skip unknown flags instead of throwing an error, use the ignoreArgv option. The callback receives a type which can be 'known-flag', 'unknown-flag', or 'argument'. Returning true for 'unknown-flag' will cause the parser to ignore it.

    cli({
        ignoreArgv(type, flagOrArgv, value) {
            if (type === 'unknown-flag') { return true }
        }
    })
  11. Enable Boolean Flag Negation

    master

    By default, boolean flags are set to false using the = operator (e.g., --verbose=false).

    To support the --no-<flag> convention, set booleanFlagNegation: true in your cli() or command() configuration. This is additive: --verbose=false will still work. If both --verbose and --no-verbose are provided, the last one wins.

    cli({
        flags: { verbose: Boolean },
        booleanFlagNegation: true
    })
    // --no-verbose     → false
    // --verbose=false   → false