Shell CLI Argument Parser and Router
Shell.js is a declarative command-line argument parser and stringifier for Node.js, currently at version 0.12.0, with its latest update published approximately a year ago. It's designed to simplify the creation of complex CLI applications by providing a structured way to define options, commands, and their associated routing logic. Key features include automatic argument discovery, support for multi-level commands (e.g., `git pull origin main`), built-in type conversion for arguments (string, boolean, integer, array), and auto-generated help messages. It differentiates itself through a declarative API that integrates parsing, stringifying, and routing into a single configuration object, making CLI development more intuitive than some imperative alternatives. The package is actively maintained and currently requires Node.js version 20 or later.
Common errors
-
TypeError: (0 , shell_1.shell) is not a function
cause Incorrectly importing the `shell` module as a default import when it's a named export, common in TypeScript or Babel setups where transpilation might obscure the direct named import error.fixChange your import statement to `import { shell } from 'shell';` to correctly access the named export. -
ReferenceError: require is not defined in ES module scope
cause Attempting to use `require()` in a JavaScript file that is treated as an ES module (e.g., due to `"type": "module"` in `package.json` or `.mjs` extension) without proper CommonJS fallback or ESM compatibility.fixIf working in an ES module environment, use `import { shell } from 'shell';`. If you must use CommonJS, ensure your file is `.cjs` or your `package.json` does not specify `"type": "module"` for that context. -
Error: Command 'unknown-command' not found.
cause Attempting to execute a command that has not been defined in the `commands` object of the `shell()` configuration.fixEnsure that the command you are trying to run is explicitly defined within the `commands` object when you initialize your `shell` instance, or check for typos in the command name.
Warnings
- breaking Version 0.12.0 (latest) officially requires Node.js v20 or later. Older Node.js versions, particularly those indicated in outdated `package.json` snippets (like `node >= 0.10.x`), are not supported and will likely lead to runtime errors or unexpected behavior due to modern JavaScript features and API usage.
- gotcha The `shell` module exports a named function, not a default export. Attempting to `import shell from 'shell'` (default import) or `const shell = require('shell')` (CommonJS default require) will result in `undefined` or a `TypeError` when trying to invoke it as a function.
- gotcha The `shell.js` API relies heavily on a declarative configuration object passed to the main `shell()` function. Attempting to directly call methods like `parse` or `stringify` without first defining your CLI structure via the configuration object and `route()` call is not the intended modern usage and may lead to errors or incomplete functionality.
Install
-
npm install shell -
yarn add shell -
pnpm add shell
Imports
- shell
import shell from 'shell';
import { shell } from 'shell'; - CLI Application Initialization
const cli = require('shell'); cli.parse(process.argv.slice(2)); // Old imperative API or partial usageconst cli = shell({ name: 'my-app', description: 'My CLI application', options: { version: { shortcut: 'v', description: 'Show version' } }, commands: { start: { description: 'Start the application' } } }); cli.route(); - CommonJS Require
const shell = require('shell');const { shell } = require('shell');
Quickstart
import { shell } from 'shell';
const myCli = shell({
name: 'complex-app',
description: 'A robust command-line application demonstrating parsing and routing.',
options: {
verbose: { shortcut: 'v', description: 'Enable verbose logging', type: 'boolean' },
config: { shortcut: 'c', description: 'Path to configuration file', type: 'string', default: './config.json' }
},
commands: {
start: {
description: 'Start a service or process.',
options: {
port: { shortcut: 'p', description: 'Port to listen on', type: 'integer', default: 3000 }
},
main: async ({ options }) => {
console.log(`Starting service on port ${options.port} with config: ${options.config} (verbose: ${options.verbose})`);
// Simulate async operation
await new Promise(resolve => setTimeout(resolve, 1000));
console.log('Service started successfully.');
}
},
stop: {
description: 'Stop a running service.',
main: () => {
console.log('Stopping service...');
console.log('Service stopped.');
}
}
}
});
myCli.route().catch(err => {
console.error('CLI Error:', err.message);
process.exit(1);
});
// To run this: save as `cli.js`, add `"type": "module"` to package.json.
// Then run: `node cli.js start -p 8080 --verbose` or `node cli.js stop -c my-dev-config.json`