A comprehensive CLI framework for creating command-line interfaces in Node.js and TypeScript
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Generate CLI components including commands and hooks using oclif's template system and code generators.
Adds a new command to an existing oclif CLI or plugin using predefined templates.
oclif generate command [NAME]Arguments:
NAME (string, required) - Name of the command to createFlags:
--commands-dir (string) - Directory to create the command file in--force (boolean) - Overwrite existing command file if it existsUsage Examples:
# Generate a simple command
oclif generate command hello
# Generate nested command
oclif generate command users:list
# Generate with custom directory
oclif generate command deploy --commands-dir=./src/commands
# Force overwrite existing command
oclif generate command existing-cmd --forceGenerated Command Structure:
import { Args, Command, Flags } from '@oclif/core'
export default class Hello extends Command {
static description = 'describe the command here'
static examples = [
'<%= config.bin %> <%= command.id %>',
]
static flags = {
// flag with a value (-n, --name=VALUE)
name: Flags.string({char: 'n', description: 'name to print'}),
// flag with no value (-f, --force)
force: Flags.boolean({char: 'f'}),
}
static args = {
file: Args.string({description: 'file to read'}),
}
async run(): Promise<void> {
const {args, flags} = await this.parse(Hello)
const name = flags.name ?? 'world'
this.log(`hello ${name} from ./src/commands/hello/world.ts`)
if (args.file && flags.force) {
this.log(`you input --force and --file: ${args.file}`)
}
}
}Adds a new hook to an existing oclif CLI or plugin for extending functionality at specific lifecycle points.
oclif generate hook [NAME]Arguments:
NAME (string, required) - Name of the hook to create (snake_case)Flags:
--event (string) - Event to run hook on (default: "init")--force (boolean) - Overwrite existing hook file if it existsUsage Examples:
# Generate a basic init hook
oclif generate hook analytics
# Generate hook for specific event
oclif generate hook my_prerun_hook --event=prerun
# Force overwrite existing hook
oclif generate hook existing_hook --forceGenerated Hook Structure:
import { Hook } from '@oclif/core'
const hook: Hook<'init'> = async function (opts) {
// Hook implementation
process.stdout.write(`example hook running ${opts.id}\n`)
}
export default hookoclif provides base generator classes for creating custom generators and extending the template system.
Abstract base class for creating custom file generators using EJS templates.
/**
* Base class for commands that generate files from templates
*/
abstract class GeneratorCommand<T extends typeof Command> extends Command {
/** Get flag value or prompt user interactively */
protected getFlagOrPrompt<K extends keyof FlagsOfPrompts<any>>(
options: GetFlagOrPromptOptions<K>
): Promise<any>;
/** Render EJS template to destination file */
protected template(
source: string,
destination: string,
data?: Record<string, any>
): Promise<void>;
}
interface GetFlagOrPromptOptions<K> {
/** Flag name to check */
flag: K;
/** Prompt configuration */
prompt: FlaggablePrompt;
/** Default value if no flag or prompt response */
default?: any;
}
interface FlaggablePrompt {
/** Prompt message */
message: string;
/** Prompt type (input, select, confirm) */
type: 'input' | 'select' | 'confirm';
/** Choices for select prompts */
choices?: Array<string | { name: string; value: any }>;
/** Initial/default value */
initial?: any;
}Helper functions for generator implementations and custom generators.
/**
* Execute shell command with promise wrapper
* @param command - Shell command to execute
* @param opts - Execution options
* @returns Promise resolving to command output
*/
function exec(command: string, opts?: ExecOptions): Promise<string>;
/**
* Read and parse package.json file
* @param location - Path to package.json
* @returns Parsed package.json object
*/
function readPJSON(location: string): Promise<any>;
/**
* Generate oclif flags from prompt definitions
* @param flaggablePrompts - Array of prompt configurations
* @returns oclif flag definitions object
*/
function makeFlags<T extends Record<string, FlaggablePrompt>>(
flaggablePrompts: T
): FlagsOfPrompts<T>;
interface ExecOptions {
cwd?: string;
env?: Record<string, string>;
shell?: boolean | string;
}
type FlagsOfPrompts<T extends Record<string, FlaggablePrompt>> = {
[K in keyof T]: T[K]['type'] extends 'confirm'
? BooleanFlag<boolean>
: StringFlag<string | undefined>;
};oclif uses EJS templates for code generation with built-in variables and helpers.
interface TemplateContext {
/** Package configuration */
config: {
name: string;
version: string;
bin: string;
};
/** Command information */
command: {
id: string;
name: string;
className: string;
};
/** User input */
answers: Record<string, any>;
/** Utility functions */
_: {
camelCase: (str: string) => string;
kebabCase: (str: string) => string;
pascalCase: (str: string) => string;
snakeCase: (str: string) => string;
};
}Templates are located in the oclif package and can be customized:
templates/command/templates/hook/templates/cli/Custom Template Usage:
// In a custom generator command
await this.template(
path.join(__dirname, '../templates/my-template.ejs'),
path.join(this.config.root, 'generated-file.ts'),
{
className: 'MyClass',
description: 'Custom generated class'
}
);Install with Tessl CLI
npx tessl i tessl/npm-oclif