axom
v0.1.1
Published
Command-line flag parsing library for TypeScript/Node.js with a clean, intuitive API
Maintainers
Readme
axom
A comprehensive command-line flag parsing library for TypeScript/Node.js with a clean, intuitive API.
Features
- 🚀 Easy to use: Simple, intuitive API
- 🎯 Type-safe: Full TypeScript support with type inference
- 📦 Core types: Support for bool, number, string, and float
- 🔧 Custom flags: Implement custom flag types using the
Valueinterface - 🎨 Flexible parsing: Support for
-flag,--flag,-flag=value, and-flag valuesyntaxes - 🏗️ FlagSet support: Create multiple independent flag sets for subcommands
- ⚡ JavaScript-native: Designed specifically for JavaScript/TypeScript environments
Installation
npm install
npm run buildTesting
The library includes comprehensive tests covering all functionality:
# Run tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm run test:coverageTest Coverage: >80% (25 tests, all passing)
Quick Start
import * as axom from './axom';
// Define flags
const name = axom.string("name", "World", "Your name");
const age = axom.number("age", 0, "Your age");
const verbose = axom.bool("verbose", false, "Enable verbose mode");
// Parse command line
axom.parse();
// Use the values
console.log(`Hello, ${name.value}!`);
console.log(`Age: ${age.value}`);
if (verbose.value) {
console.log("Verbose mode enabled");
}Run your program:
ts-node your-program.ts -name John -age 30 -verboseUsage
Basic Flag Types
Boolean Flags
const verbose = axom.bool("verbose", false, "Enable verbose mode");
axom.parse();
// Usage: -verbose or -verbose=true or -verbose=false
console.log(verbose.value); // true/falseNumber Flags
const count = axom.number("count", 10, "Number of items");
const port = axom.number("port", 8080, "Server port");
axom.parse();
// Usage: -count 42 or -count=0x2A or -count=020 (octal)
console.log(count.value); // 42
console.log(port.value); // 8080String Flags
const name = axom.string("name", "default", "User name");
axom.parse();
// Usage: -name "John Doe" or -name=value
console.log(name.value);Float Flags
const rate = axom.float("rate", 1.5, "Rate multiplier");
axom.parse();
// Usage: -rate 2.5 or -rate=3.14
console.log(rate.value);Multiple Flags
Define multiple flags for your application:
// Define all your flags
const port = axom.number("port", 8080, "Server port");
const host = axom.string("host", "localhost", "Server host");
const debug = axom.bool("debug", false, "Debug mode");
const timeout = axom.number("timeout", 5000, "Timeout in ms");
// Parse command line
axom.parse();
// Access the values
console.log(`Server running on ${host.value}:${port.value}`);
console.log(`Timeout: ${timeout.value}ms`);
if (debug.value) {
console.log("Debug mode enabled");
}Custom Flag Types
Implement the Value interface to create custom flag types:
class EmailValue implements axom.Value {
constructor(private email: { value: string }) {}
set(s: string): Error | null {
if (!s.includes('@')) {
return new Error("invalid email format");
}
this.email.value = s;
return null;
}
string(): string {
return this.email.value;
}
}
const email = { value: "[email protected]" };
axom.varFlag(new EmailValue(email), "email", "[email protected]", "Email address");
axom.parse();
console.log(email.value);Function Flags
Execute custom logic when a flag is encountered:
const tags: string[] = [];
axom.func("tag", "Add a tag (can be specified multiple times)", (value: string) => {
tags.push(value);
return null;
});
axom.parse();
// Usage: -tag one -tag two -tag three
console.log(tags); // ['one', 'two', 'three']FlagSet for Subcommands
Create independent flag sets for implementing subcommands:
import * as axom from './axom';
const serverCmd = axom.newFlagSet("server", axom.ErrorHandling.ContinueOnError);
const serverPort = serverCmd.number("port", 3000, "Server port");
const serverHost = serverCmd.string("host", "0.0.0.0", "Server host");
const clientCmd = axom.newFlagSet("client", axom.ErrorHandling.ContinueOnError);
const clientURL = clientCmd.string("url", "http://localhost:3000", "Server URL");
const clientRetries = clientCmd.number("retries", 3, "Number of retries");
// Parse main command line
axom.parse();
const subcommand = axom.arg(0);
if (subcommand === "server") {
serverCmd.parse(axom.args().slice(1));
console.log(`Starting server on ${serverHost.value}:${serverPort.value}`);
} else if (subcommand === "client") {
clientCmd.parse(axom.args().slice(1));
console.log(`Connecting to ${clientURL.value} (${clientRetries.value} retries)`);
}Usage:
node app.js server -port 8080 -host localhost
node app.js client -url http://api.example.com -retries 5Error Handling
Control how parsing errors are handled:
const fs = axom.newFlagSet("myapp", axom.ErrorHandling.ContinueOnError);
// ErrorHandling.ContinueOnError - return error
// ErrorHandling.ExitOnError - call process.exit(2)
// ErrorHandling.PanicOnError - throw error
const err = fs.parse(process.argv.slice(2));
if (err) {
console.error("Error:", err.message);
// Handle error
}Accessing Arguments
Get non-flag arguments after parsing:
axom.parse();
console.log(axom.nArg()); // Number of arguments
console.log(axom.args()); // All arguments as array
console.log(axom.arg(0)); // First argument
console.log(axom.arg(1)); // Second argumentVisiting Flags
Iterate over defined or set flags:
// Visit all defined flags
axom.visitAll((f) => {
console.log(`-${f.name}: ${f.usage} (default: ${f.defValue})`);
});
// Visit only flags that were set
axom.visit((f) => {
console.log(`-${f.name} = ${f.value.string()}`);
});Custom Usage Function
Customize the help message:
axom.usage = () => {
console.log("Custom usage message");
console.log("\nOptions:");
axom.printDefaults();
};
axom.parse();
// Run with: -h or -help to see the custom messageCommand Line Syntax
The library supports several command-line syntaxes:
# Boolean flags
-verbose
-verbose=true
-verbose=false
# Number/String/Float flags
-count value
-count=value
--count value
--count=value
# Mixing formats
-v -name=John -count 30 file1 file2
# Terminator (stop parsing flags)
-v -name=John -- -not-a-flag file2Examples
The library includes 10 comprehensive examples demonstrating all features:
- Basic Usage - Simple flag definition and parsing
- All Types - Bool, number, string, float types
- Custom Flags - Custom validation with Value interface
- Function Flags - Func and BoolFunc for repeated flags
- Subcommands - FlagSet for git/docker style CLIs
- Visiting Flags - Inspecting and iterating over flags
- Set Flags - Programmatic flag setting
- Help & Usage - Custom help messages
- Error Handling - Different error handling modes
- Advanced - Complete production-ready example
See the examples/ directory and examples/README.md for detailed documentation and usage.
# Run any example
ts-node examples/01-basic-usage.ts -name John -age 30 -verbose
ts-node examples/04-func-flags.ts -v -v -v -add-tag one -add-tag two
ts-node examples/05-flagset-subcommands.ts server -port 8080 -debug
# Run the main example
npm run example -- -name John -age 30 -verboseAPI Reference
Top-Level Functions
bool(name, value, usage)- Define a boolean flagnumber(name, value, usage)- Define a number flag (integers and floats)float(name, value, usage)- Define a float flag (alias for number)string(name, value, usage)- Define a string flagvarFlag(value, name, usage)- Define a custom flagfunc(name, usage, fn)- Define a function flagboolFunc(name, usage, fn)- Define a boolean function flagparse()- Parse command line argumentsargs()- Get remaining non-flag argumentsarg(i)- Get i-th non-flag argumentnArg()- Get number of non-flag argumentsnFlag()- Get number of flags that were setvisit(fn)- Visit flags that were setvisitAll(fn)- Visit all defined flags
FlagSet Class
new FlagSet(name, errorHandling)- Create a new flag setparse(arguments)- Parse argumentsbool/number/string/float- Same as top-level functions- All other methods mirror the top-level API
License
BSD-3-Clause
Design Philosophy
axom is designed with TypeScript/JavaScript best practices in mind:
- Type Safety: Full TypeScript support with type inference
- Reference Objects: Returns reference objects with a
valueproperty:{ value: T } - Type Simplification:
- Uses JavaScript's unified
numbertype for all numeric values - No need for separate signed/unsigned types
floatis an alias fornumberfor clarity
- Uses JavaScript's unified
- Flexible Parsing: Supports multiple command-line syntaxes
- Error Handling: Uses JavaScript
Errorobjects with clear error messages - Custom Types: Easy to implement custom flag types using the
Valueinterface
Contributing
Contributions are welcome! Please feel free to submit issues or pull requests.
