mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
Implemented comprehensive CLI functionality: - Fast zero-allocation flag parser for common cases - Support for multiple flag types (boolean, string, number, array) - Interactive prompts with TTY detection and fallback - Performance-optimized with single-pass parsing - Full TypeScript API with type definitions 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
287 lines
7.0 KiB
Markdown
287 lines
7.0 KiB
Markdown
# Bun CLI Parser & Interactive System Design
|
|
|
|
## Three Implementation Approaches
|
|
|
|
### Approach 1: TypeScript-First with Zig Backend
|
|
**Pros**: Familiar API, easy to extend, good DX
|
|
**Cons**: JS overhead for parsing
|
|
|
|
```typescript
|
|
// User-facing API
|
|
const cli = Bun.CLI.create({
|
|
name: "myapp",
|
|
version: "1.0.0",
|
|
flags: {
|
|
verbose: { type: "boolean", short: "v", default: false },
|
|
port: { type: "number", short: "p", default: 3000 },
|
|
files: { type: "array", of: "string" },
|
|
config: { type: "string", env: "MY_APP_CONFIG" }
|
|
}
|
|
});
|
|
|
|
const args = cli.parse();
|
|
|
|
// Interactive mode
|
|
if (!args.config && cli.isTTY) {
|
|
args.config = await cli.prompt.select({
|
|
message: "Choose config",
|
|
choices: ["dev", "prod", "test"]
|
|
});
|
|
}
|
|
```
|
|
|
|
### Approach 2: Pure Zig with Code Generation
|
|
**Pros**: Zero overhead, compile-time validation, fastest possible
|
|
**Cons**: Less flexible, harder to maintain
|
|
|
|
```zig
|
|
// Generated from schema at build time
|
|
const MyAppCLI = generateCLI(.{
|
|
.name = "myapp",
|
|
.flags = .{
|
|
.verbose = .{ .type = .boolean, .short = 'v' },
|
|
.port = .{ .type = .number, .short = 'p', .default = 3000 },
|
|
.files = .{ .type = .array, .of = .string },
|
|
},
|
|
});
|
|
|
|
// Runtime usage
|
|
const args = try MyAppCLI.parse(std.os.argv);
|
|
```
|
|
|
|
### Approach 3: Hybrid with Smart Optimization (RECOMMENDED)
|
|
**Pros**: Best of both worlds, progressive enhancement
|
|
**Cons**: More complex implementation
|
|
|
|
```typescript
|
|
// Fast path for simple cases
|
|
const args = Bun.CLI.parseSimple(); // Zero alloc for basic flags
|
|
|
|
// Full featured for complex cases
|
|
const cli = new Bun.CLI({
|
|
// Schema definition
|
|
schema: {
|
|
commands: {
|
|
serve: {
|
|
flags: {
|
|
port: { type: "number", short: "p" },
|
|
host: { type: "string", short: "h" }
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
// Performance hints
|
|
hints: {
|
|
maxArgs: 10, // Pre-allocate buffers
|
|
commonFlags: ["v", "h", "help"], // Optimize these
|
|
lazyInteractive: true // Load interactive only when needed
|
|
}
|
|
});
|
|
```
|
|
|
|
## Core Implementation Plan
|
|
|
|
### Phase 1: Fast Flag Parser (Week 1)
|
|
|
|
```typescript
|
|
// Core parser with zero allocations for common cases
|
|
export namespace Bun.CLI {
|
|
export interface ParseOptions {
|
|
// Stop at first non-flag
|
|
stopEarly?: boolean;
|
|
// Allow unknown flags
|
|
allowUnknown?: boolean;
|
|
// Parse numbers/booleans
|
|
autoType?: boolean;
|
|
}
|
|
|
|
export function parse(
|
|
args?: string[],
|
|
options?: ParseOptions
|
|
): Record<string, any>;
|
|
|
|
// Fast path for simple cases
|
|
export function parseSimple(args?: string[]): {
|
|
_: string[];
|
|
[key: string]: any;
|
|
};
|
|
}
|
|
```
|
|
|
|
### Phase 2: Type-Safe Schema (Week 2)
|
|
|
|
```typescript
|
|
// Type-safe flag definitions
|
|
export interface FlagSchema {
|
|
type: "string" | "number" | "boolean" | "array" | "enum";
|
|
short?: string;
|
|
long?: string;
|
|
default?: any;
|
|
required?: boolean;
|
|
env?: string; // Environment variable fallback
|
|
validate?: (value: any) => boolean | string;
|
|
transform?: (value: any) => any;
|
|
}
|
|
|
|
// Advanced array handling
|
|
export interface ArrayFlagSchema extends FlagSchema {
|
|
type: "array";
|
|
of: "string" | "number";
|
|
separator?: string; // For comma-separated values
|
|
accumulate?: boolean; // Multiple --flag values
|
|
}
|
|
```
|
|
|
|
### Phase 3: Interactive System (Week 3)
|
|
|
|
```typescript
|
|
export namespace Bun.CLI.Interactive {
|
|
// TTY detection with fallback
|
|
export const isTTY: boolean;
|
|
|
|
// Core prompt types
|
|
export interface PromptOptions {
|
|
message: string;
|
|
default?: any;
|
|
validate?: (input: any) => boolean | string;
|
|
// Non-TTY fallback
|
|
fallback?: () => any;
|
|
}
|
|
|
|
export async function text(options: PromptOptions): Promise<string>;
|
|
export async function confirm(options: PromptOptions): Promise<boolean>;
|
|
export async function select<T>(options: SelectOptions<T>): Promise<T>;
|
|
export async function multiselect<T>(options: MultiSelectOptions<T>): Promise<T[]>;
|
|
|
|
// Advanced: Form with multiple fields
|
|
export async function form<T>(schema: FormSchema): Promise<T>;
|
|
}
|
|
```
|
|
|
|
### Phase 4: Performance Optimizations
|
|
|
|
```zig
|
|
// Zig backend for hot paths
|
|
pub const FastParser = struct {
|
|
allocator: std.mem.Allocator,
|
|
args_buffer: [256][]const u8, // Stack allocation for common case
|
|
|
|
pub fn parse(args: []const []const u8) ParseResult {
|
|
// Single pass parsing
|
|
// Zero-copy string slicing
|
|
// Compile-time type coercion
|
|
}
|
|
};
|
|
|
|
// Incremental renderer for interactive mode
|
|
pub const IncrementalRenderer = struct {
|
|
last_frame: []u8,
|
|
dirty_regions: std.ArrayList(Region),
|
|
|
|
pub fn render(content: []const u8) !void {
|
|
// Diff-based updates
|
|
// Batched escape sequences
|
|
// Adaptive frame rate
|
|
}
|
|
};
|
|
```
|
|
|
|
## Performance Benchmarks Target
|
|
|
|
```
|
|
Simple flag parsing (10 args):
|
|
- Target: < 100ns
|
|
- Baseline (minimist): ~1μs
|
|
|
|
Complex parsing (100 args, nested commands):
|
|
- Target: < 1μs
|
|
- Baseline (yargs): ~50μs
|
|
|
|
Interactive prompt render:
|
|
- Target: < 1ms per frame
|
|
- 60fps for smooth animations
|
|
|
|
Memory usage:
|
|
- Zero allocations for < 16 args
|
|
- Single arena for complex parsing
|
|
- Reusable buffers for interactive
|
|
```
|
|
|
|
## Edge Cases Handled
|
|
|
|
1. **No TTY**: Graceful fallback to simple prompts or defaults
|
|
2. **Piped input**: Detect and handle stdin/stdout pipes
|
|
3. **CI environment**: Auto-detect CI and disable interactive
|
|
4. **Windows Terminal**: Special handling for Windows console
|
|
5. **SSH sessions**: Detect and adapt rendering
|
|
6. **Screen readers**: Accessibility mode with plain text
|
|
7. **Partial args**: Handle incomplete flag values
|
|
8. **Unicode**: Full UTF-8 support in prompts
|
|
9. **Signals**: Proper cleanup on SIGINT/SIGTERM
|
|
10. **Large inputs**: Stream processing for huge arg lists
|
|
|
|
## API Examples
|
|
|
|
### Basic Usage
|
|
```typescript
|
|
// Simple parsing
|
|
const args = Bun.CLI.parse();
|
|
console.log(args.verbose, args.port);
|
|
|
|
// With schema
|
|
const cli = Bun.CLI.create({
|
|
flags: {
|
|
verbose: { type: "boolean", short: "v" },
|
|
port: { type: "number", default: 3000 }
|
|
}
|
|
});
|
|
const { verbose, port } = cli.parse();
|
|
```
|
|
|
|
### Interactive Usage
|
|
```typescript
|
|
// Auto-detect TTY and fallback
|
|
const name = await Bun.CLI.prompt.text({
|
|
message: "Your name?",
|
|
fallback: () => process.env.USER || "anonymous"
|
|
});
|
|
|
|
// Complex form
|
|
const config = await Bun.CLI.prompt.form({
|
|
fields: {
|
|
database: { type: "select", choices: ["postgres", "mysql", "sqlite"] },
|
|
port: { type: "number", default: 5432 },
|
|
ssl: { type: "confirm", default: true }
|
|
}
|
|
});
|
|
```
|
|
|
|
### Advanced Subcommands
|
|
```typescript
|
|
const cli = Bun.CLI.create({
|
|
commands: {
|
|
serve: {
|
|
handler: (args) => startServer(args),
|
|
flags: { port: { type: "number" } }
|
|
},
|
|
build: {
|
|
handler: (args) => runBuild(args),
|
|
flags: { watch: { type: "boolean" } }
|
|
}
|
|
}
|
|
});
|
|
|
|
await cli.run();
|
|
```
|
|
|
|
## Testing Strategy
|
|
|
|
1. **Unit tests**: Each parser component
|
|
2. **Integration tests**: Full CLI flows
|
|
3. **Performance tests**: Benchmark suite
|
|
4. **TTY simulation**: Mock terminal tests
|
|
5. **Cross-platform**: Windows, macOS, Linux
|
|
6. **Fuzzing**: Random input generation
|
|
7. **Memory tests**: Leak detection
|
|
8. **Stress tests**: Large arg counts |