22/**
33 * Transform example
44 *
5- * Demonstrates how to use transforms to :
5+ * Demonstrates how to use transforms with both simple CLIs and commands :
66 *
7- * - Load and merge config from a JSON file
8- * - Add computed/derived values
9- * - Transform positionals
7+ * - Global transforms that apply to all commands
8+ * - Command-specific transforms
9+ * - Computed/derived values flowing through handlers
1010 *
11- * Usage: npx tsx examples/transforms.ts --verbose npx tsx
12- * examples/transforms.ts --config config.json npx tsx examples/transforms.ts
13- * file1.txt file2.txt
11+ * Usage: npx tsx examples/transforms.ts process file1.txt file2.txt --verbose
12+ * npx tsx examples/transforms.ts info --config config.json npx tsx
13+ * examples/transforms.ts --help
1414 */
1515import { existsSync , readFileSync } from 'node:fs' ;
1616
@@ -25,88 +25,137 @@ interface Config {
2525 verbose ?: boolean ;
2626}
2727
28- const main = async ( ) => {
29- /* eslint-disable perfectionist/sort-objects -- transforms must come before handler for type inference */
30- const result = await bargsAsync ( {
31- description : 'Demonstrates transforms feature' ,
32- name : 'transforms-demo' ,
33- options : {
34- config : bargsAsync . string ( {
35- aliases : [ 'c' ] ,
36- description : 'Path to JSON config file' ,
37- } ) ,
38- outputDir : bargsAsync . string ( {
39- aliases : [ 'o' ] ,
40- description : 'Output directory' ,
41- } ) ,
42- verbose : bargsAsync . boolean ( {
43- aliases : [ 'v' ] ,
44- default : false ,
45- description : 'Enable verbose output' ,
46- } ) ,
47- } ,
48- positionals : [
49- bargsAsync . variadic ( 'string' , {
50- description : 'Input files to process' ,
51- name : 'files' ,
52- } ) ,
53- ] ,
54- transforms : {
55- positionals : ( positionals ) => {
56- const [ files ] = positionals ;
57- const validFiles = files . filter ( ( f ) => {
28+ // Global options that will be available to all commands
29+ const globalOptions = {
30+ config : bargsAsync . string ( {
31+ aliases : [ 'c' ] ,
32+ description : 'Path to JSON config file' ,
33+ } ) ,
34+ outputDir : bargsAsync . string ( {
35+ aliases : [ 'o' ] ,
36+ description : 'Output directory' ,
37+ } ) ,
38+ verbose : bargsAsync . boolean ( {
39+ aliases : [ 'v' ] ,
40+ default : false ,
41+ description : 'Enable verbose output' ,
42+ } ) ,
43+ } as const ;
44+
45+ // Global transforms - these run before command transforms
46+ const globalTransforms = {
47+ values : ( values : {
48+ config : string | undefined ;
49+ outputDir : string | undefined ;
50+ verbose : boolean ;
51+ } ) => {
52+ let fileConfig : Config = { } ;
53+
54+ if ( values . config && existsSync ( values . config ) ) {
55+ const content = readFileSync ( values . config , 'utf8' ) ;
56+ fileConfig = JSON . parse ( content ) as Config ;
57+ }
58+
59+ return {
60+ ...fileConfig ,
61+ ...values ,
62+ configLoaded : ! ! values . config ,
63+ timestamp : new Date ( ) . toISOString ( ) ,
64+ } ;
65+ } ,
66+ } as const ;
67+
68+ // Define commands using the typed command builder with global transforms
69+ // The second type argument passes global transforms for proper type inference
70+ const processCommand = bargsAsync . command <
71+ typeof globalOptions ,
72+ typeof globalTransforms
73+ > ( ) ( {
74+ description : 'Process files' ,
75+ handler : ( { positionals, values } ) => {
76+ // Global transform properties are now available via TGlobalTransforms type arg
77+ const [ files ] = positionals ;
78+
79+ if ( values . verbose ) {
80+ console . log ( 'Processing configuration:' , {
81+ configLoaded : values . configLoaded ,
82+ outputDir : values . outputDir ,
83+ timestamp : values . timestamp ,
84+ verbose : values . verbose ,
85+ } ) ;
86+ }
87+
88+ console . log ( `Processing ${ files . length } file(s):` ) ;
89+ for ( const file of files ) {
90+ console . log ( ` - ${ file . toUpperCase ( ) } ` ) ; // Command transform uppercases
91+ }
92+
93+ if ( values . outputDir ) {
94+ console . log ( `Output will be written to: ${ values . outputDir } ` ) ;
95+ }
96+ } ,
97+ positionals : [
98+ bargsAsync . variadic ( 'string' , {
99+ description : 'Input files to process' ,
100+ name : 'files' ,
101+ } ) ,
102+ ] ,
103+ // Command-level transform - processes positionals
104+ transforms : {
105+ positionals : ( positionals ) => {
106+ const [ files ] = positionals ;
107+ // Filter non-existent files and uppercase the rest
108+ const validFiles = files
109+ . filter ( ( f ) => {
58110 if ( ! existsSync ( f ) ) {
59111 console . warn ( `Warning: File not found: ${ f } ` ) ;
60112 return false ;
61113 }
62114 return true ;
63- } ) ;
64- return [ validFiles ] as const ;
65- } ,
66- values : ( values ) => {
67- let fileConfig : Config = { } ;
115+ } )
116+ . map ( ( f ) => f . toUpperCase ( ) ) ;
117+ return [ validFiles ] as const ;
118+ } ,
119+ } ,
120+ } ) ;
68121
69- if ( values . config && existsSync ( values . config ) ) {
70- const content = readFileSync ( values . config , 'utf8' ) ;
71- fileConfig = JSON . parse ( content ) as Config ;
72- }
122+ const infoCommand = bargsAsync . command <
123+ typeof globalOptions ,
124+ typeof globalTransforms
125+ > ( ) ( {
126+ description : 'Show configuration info' ,
127+ handler : ( { values } ) => {
128+ // Global transform properties are available via TGlobalTransforms type arg
129+ console . log ( 'Current configuration:' ) ;
130+ console . log ( ` Config file: ${ values . config ?? '(none)' } ` ) ;
131+ console . log ( ` Output dir: ${ values . outputDir ?? '(default)' } ` ) ;
132+ console . log ( ` Verbose: ${ values . verbose } ` ) ;
133+ console . log ( ` Config loaded: ${ values . configLoaded } ` ) ;
134+ console . log ( ` Timestamp: ${ values . timestamp } ` ) ;
135+ } ,
136+ } ) ;
73137
74- return {
75- ...fileConfig ,
76- ...values ,
77- configLoaded : ! ! values . config ,
78- timestamp : new Date ( ) . toISOString ( ) ,
79- } ;
80- } ,
138+ const main = async ( ) => {
139+ await bargsAsync ( {
140+ commands : {
141+ info : infoCommand ,
142+ process : processCommand ,
81143 } ,
82- handler : ( { positionals, values } ) => {
83- const [ files ] = positionals ;
84-
144+ defaultHandler : ( { values } ) => {
145+ console . log ( 'No command specified. Use --help for usage.' ) ;
85146 if ( values . verbose ) {
86- console . log ( 'Configuration:' , {
87- configLoaded : values . configLoaded ,
88- outputDir : values . outputDir ,
89- timestamp : values . timestamp ,
90- verbose : values . verbose ,
91- } ) ;
92- }
93-
94- console . log ( `Processing ${ files . length } file(s):` ) ;
95- for ( const file of files ) {
96- console . log ( ` - ${ file } ` ) ;
97- }
98-
99- if ( values . outputDir ) {
100- console . log ( `Output will be written to: ${ values . outputDir } ` ) ;
147+ console . log ( '(verbose mode enabled)' ) ;
101148 }
149+ // Test: Can we access global transform-added properties?
150+ console . log ( `Config loaded: ${ values . configLoaded } ` ) ;
151+ console . log ( `Timestamp: ${ values . timestamp } ` ) ;
102152 } ,
153+ description : 'Demonstrates transforms with commands' ,
154+ name : 'transforms-demo' ,
155+ options : globalOptions ,
156+ transforms : globalTransforms ,
103157 version : '1.0.0' ,
104158 } ) ;
105- /* eslint-enable perfectionist/sort-objects */
106-
107- if ( result . values . verbose ) {
108- console . log ( '\nFinal result:' , result ) ;
109- }
110159} ;
111160
112161void main ( ) ;
0 commit comments