-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtransforms.ts
More file actions
153 lines (135 loc) · 5.97 KB
/
transforms.ts
File metadata and controls
153 lines (135 loc) · 5.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#!/usr/bin/env npx tsx
/**
* Transform example
*
* Demonstrates how to use map() transforms:
*
* - Global transforms via map() applied to globals parser
* - Using camelCaseValues to convert kebab-case to camelCase
* - Command-specific transforms via map() in command parsers
* - Computed/derived values flowing through handlers
* - Full type inference with the (Parser, handler) API
*
* Usage: npx tsx examples/transforms.ts process file1.txt file2.txt --verbose
* npx tsx examples/transforms.ts info --config config.json npx tsx
* examples/transforms.ts --help
*/
import { existsSync, readFileSync } from 'node:fs';
import { bargs, camelCaseValues, map, opt, pos } from '../src/index.js';
// ═══════════════════════════════════════════════════════════════════════════════
// CONFIG TYPE
// ═══════════════════════════════════════════════════════════════════════════════
interface Config {
maxRetries?: number;
outputDir?: string;
verbose?: boolean;
}
// ═══════════════════════════════════════════════════════════════════════════════
// GLOBAL OPTIONS WITH TRANSFORM
// ═══════════════════════════════════════════════════════════════════════════════
// Global options using kebab-case (CLI-friendly)
const baseGlobals = opt.options({
config: opt.string(),
'output-dir': opt.string(), // CLI: --output-dir
verbose: opt.boolean({ default: false }),
});
// First, convert kebab-case to camelCase for ergonomic property access
const camelGlobals = map(baseGlobals, camelCaseValues);
// Then apply additional transforms for computed properties
const globals = map(camelGlobals, ({ positionals, values }) => {
let fileConfig: Config = {};
// Load config from JSON file if specified
if (values.config && existsSync(values.config)) {
const content = readFileSync(values.config, 'utf8');
fileConfig = JSON.parse(content) as Config;
}
// Return enriched values with file config merged in
// Note: values.outputDir is now camelCase thanks to camelCaseValues!
return {
positionals,
values: {
...fileConfig,
...values,
configLoaded: !!values.config,
timestamp: new Date().toISOString(),
},
};
});
// ═══════════════════════════════════════════════════════════════════════════════
// COMMAND PARSERS
// ═══════════════════════════════════════════════════════════════════════════════
// Process command: variadic positional with transform
const processBase = pos.positionals(pos.variadic('string', { name: 'files' }));
const processParser = map(processBase, ({ positionals, values }) => {
const [files] = positionals;
const validFiles = (files ?? [])
.filter((f) => {
if (!existsSync(f)) {
console.warn(`Warning: File not found: ${f}`);
return false;
}
return true;
})
.map((f) => f.toUpperCase());
return {
positionals: [validFiles] as const,
values,
};
});
// Info command: no command-specific options
const infoParser = opt.options({});
// ═══════════════════════════════════════════════════════════════════════════════
// CLI
// Using (Parser, handler, description) form for full type inference!
// ═══════════════════════════════════════════════════════════════════════════════
await bargs
.create('transforms-demo', {
description: 'Demonstrates transforms with commands',
version: '1.0.0',
})
.globals(globals)
// The handler receives merged global + command types
.command(
'process',
processParser,
({ positionals, values }) => {
const [files] = positionals;
// values has full type from globals transform:
// { config, outputDir, verbose, configLoaded, timestamp, maxRetries }
if (values.verbose) {
console.log('Processing configuration:', {
configLoaded: values.configLoaded,
outputDir: values.outputDir,
timestamp: values.timestamp,
verbose: values.verbose,
});
}
console.log(`Processing ${files.length} file(s):`);
for (const file of files) {
console.log(` - ${file}`);
}
if (values.outputDir) {
console.log(`Output will be written to: ${values.outputDir}`);
}
},
'Process files',
)
.command(
'info',
infoParser,
({ values }) => {
// values has full type from globals transform
if (values.verbose) {
console.log('Verbose mode enabled');
}
console.log('Current configuration:');
console.log(` Config file: ${values.config ?? '(none)'}`);
console.log(` Output dir: ${values.outputDir ?? '(default)'}`);
console.log(` Verbose: ${values.verbose}`);
console.log(` Config loaded: ${values.configLoaded}`);
console.log(` Timestamp: ${values.timestamp}`);
},
'Show configuration info',
)
.defaultCommand('info')
.parseAsync();