Type-Safe CLI Framework
Clipanion is a TypeScript-first framework for building robust and type-safe command-line interfaces (CLIs). It leverages TypeScript's powerful type system to define command arguments and options, providing compile-time validation and autocompletion, significantly reducing common runtime errors associated with CLI parsing. The package is currently at version `4.0.0-rc.4`, indicating active development towards a stable major release with a strong emphasis on modern JavaScript and TypeScript practices. A key differentiator is its zero runtime dependencies (beyond its peer dependency `typanion` for runtime type validation), resulting in extremely small bundle sizes. It integrates deeply with `typanion` to derive runtime validators directly from static TypeScript types, providing a seamless development experience for complex CLI applications. This approach contrasts with other CLI libraries that often rely on separate schema definitions or less integrated type checking.
Common errors
-
TypeError: Command.String is not a function
cause This error typically occurs when trying to use `Command.String()` for option validation in Clipanion v3, or when `typanion` is not correctly integrated/installed in v4.fixFor Clipanion v4, ensure `typanion` is installed (`npm i typanion`) and that you're correctly using `Option.String('--name', { validator: Command.String() })`. For Clipanion v3, argument validation was handled differently and `Command.String()` was not part of the public API for options. -
Error: Cannot find module 'clipanion' or 'clipanion/package.json'
cause This usually indicates a module resolution issue, most commonly when a CommonJS project tries to import an ESM-only package, or when the package is not installed.fixFirst, ensure `clipanion` is installed (`npm i clipanion`). If it is, configure your `package.json` with `"type": "module"` for ESM, or ensure your build system transpiles ESM imports to CommonJS correctly if you must remain in a CJS environment. -
ReferenceError: require is not defined
cause This error occurs when you're using a `require()` statement in an ES Module context, or when running an ES Module file directly with Node.js without the `--experimental-json-modules` flag for JSON or other non-JS imports.fixReplace `require()` calls with `import` statements. Ensure your project's `package.json` has `"type": "module"` or that the file explicitly uses the `.mjs` extension for ES Module interpretation.
Warnings
- breaking Clipanion v4 introduces significant breaking changes from v3, primarily around option and argument definition. The `Command.String()`, `Command.Boolean()`, etc., now explicitly leverage `typanion` validators, which replaces the older, less type-safe methods of defining options. Direct access to `this.args` for positional arguments might also be altered in favor of explicit `Option.Rest()` or `Option.Array()` definitions.
- gotcha Clipanion v4 is designed with an ESM-first approach. Attempting to use `require()` for imports in a CommonJS context without proper transpilation or configuration will lead to module resolution errors. Projects should generally be configured for ESM (`"type": "module"` in `package.json`).
- gotcha The `typanion` peer dependency is crucial for Clipanion v4's core functionality, especially for runtime argument validation. Forgetting to install `typanion` or having a mismatch in versions can lead to runtime errors or unexpected parsing behavior.
- gotcha Clipanion provides `Builtins.HelpCommand` and `Builtins.VersionCommand` for standard CLI functionality. Developers new to Clipanion sometimes overlook registering these, leading to CLIs without a `--help` or `--version` option, which is a common user expectation.
Install
-
npm install clipanion -
yarn add clipanion -
pnpm add clipanion
Imports
- Cli
const { Cli } = require('clipanion')import { Cli } from 'clipanion' - Command
import Command from 'clipanion'
import { Command, Option } from 'clipanion' - Builtins
import { help, version } from 'clipanion'import { Builtins } from 'clipanion'
Quickstart
import { Cli, Command, Option, Builtins } from 'clipanion';
import process from 'node:process';
class HelloCommand extends Command {
static paths = [['hello'], ['hi']];
name = Option.String('--name', {
description: 'Name to greet',
required: false,
validator: Command.String().withDefault('World')
});
async execute() {
this.context.stdout.write(`Hello, ${this.name}!\n`);
}
}
async function main() {
const cli = new Cli({
binaryLabel: `My CLI App`,
binaryName: `my-cli`,
binaryVersion: `1.0.0`,
});
cli.register(HelloCommand);
cli.register(Builtins.VersionCommand);
cli.register(Builtins.HelpCommand);
await cli.run(process.argv.slice(2), {
cwd: process.cwd(),
stdout: process.stdout,
stdin: process.stdin,
stderr: process.stderr,
env: process.env,
});
}
main().catch(err => {
console.error('CLI Error:', err);
process.exit(1);
});