44
55import { exitWithError } from '@utils/exit.js' ;
66import { printDeprecated } from '@utils/messages.js' ;
7- import { Command as CommanderCommand } from 'commander' ;
7+ import { Command as CommanderCommand , Option } from 'commander' ;
88
99import type { Argument , CommandSpec , Specs } from './types.js' ;
1010
@@ -164,6 +164,12 @@ export function commandHasAnyImplementation(
164164 pathParts : string [ ] ,
165165 hasImplementation : ImplementationChecker
166166) : boolean {
167+ // Removed commands are still registered so we can intercept and
168+ // redirect users to the replacement instead of "unknown command".
169+ if ( command . removed ) {
170+ return true ;
171+ }
172+
167173 if ( hasImplementation ( pathParts ) ) {
168174 return true ;
169175 }
@@ -181,22 +187,58 @@ export function commandHasAnyImplementation(
181187 return false ;
182188}
183189
190+ /**
191+ * Print a redirect message and exit. Used for hard-removed commands
192+ * and arguments. `subject` is the human-readable thing the user invoked
193+ * (e.g. `tigris buckets set-ttl` or `--region`).
194+ */
195+ function printRemovedAndExit (
196+ subject : string ,
197+ replacedBy : string | undefined
198+ ) : never {
199+ const hint = replacedBy
200+ ? ` Use ${ replacedBy } instead.`
201+ : ' See the changelog for migration guidance.' ;
202+ console . error ( `${ subject } was removed in this version.${ hint } ` ) ;
203+ process . exit ( 1 ) ;
204+ }
205+
206+ /**
207+ * Inspect parsed options for any argument the spec marks as removed.
208+ * If the user supplied one, print the redirect and exit.
209+ */
210+ function checkRemovedArguments (
211+ args : Argument [ ] | undefined ,
212+ options : Record < string , unknown >
213+ ) : void {
214+ if ( ! args ) return ;
215+ for ( const arg of args ) {
216+ if ( ! arg . removed ) continue ;
217+ const value = getOptionValue ( options , arg . name , args ) ;
218+ if ( value !== undefined ) {
219+ printRemovedAndExit ( `--${ arg . name } ` , arg . replaced_by ) ;
220+ }
221+ }
222+ }
223+
184224export function showCommandHelp (
185225 specs : Specs ,
186226 command : CommandSpec ,
187227 pathParts : string [ ] ,
188228 hasImplementation : ImplementationChecker
189229) {
190230 const fullPath = pathParts . join ( ' ' ) ;
191- console . log ( `\n${ specs . name } ${ fullPath } - ${ command . description } \n` ) ;
231+ console . log ( `\n${ specs . name } ${ fullPath } - ${ command . description ?? '' } \n` ) ;
192232
193233 if ( command . commands && command . commands . length > 0 ) {
194- const availableCmds = command . commands . filter ( ( cmd ) =>
195- commandHasAnyImplementation (
196- cmd ,
197- [ ...pathParts , cmd . name ] ,
198- hasImplementation
199- )
234+ const availableCmds = command . commands . filter (
235+ ( cmd ) =>
236+ ! cmd . removed &&
237+ commandHasAnyImplementation (
238+ cmd ,
239+ [ ...pathParts , cmd . name ] ,
240+ hasImplementation
241+ )
200242 ) ;
201243
202244 if ( availableCmds . length > 0 ) {
@@ -208,14 +250,17 @@ export function showCommandHelp(
208250 cmdPart += ` (${ aliases . join ( ', ' ) } )` ;
209251 }
210252 const paddedCmdPart = cmdPart . padEnd ( 24 ) ;
211- console . log ( `${ paddedCmdPart } ${ cmd . description } ` ) ;
253+ console . log ( `${ paddedCmdPart } ${ cmd . description ?? '' } ` ) ;
212254 } ) ;
213255 console . log ( ) ;
214256 }
215257 }
216258
217259 const globalArgs = specs . definitions ?. global_arguments ?? [ ] ;
218- const effectiveArgs = getEffectiveArguments ( globalArgs , command . arguments ) ;
260+ const effectiveArgs = getEffectiveArguments (
261+ globalArgs ,
262+ command . arguments
263+ ) . filter ( ( arg ) => ! arg . removed ) ;
219264 if ( effectiveArgs . length > 0 ) {
220265 console . log ( 'Arguments:' ) ;
221266 effectiveArgs . forEach ( ( arg ) => {
@@ -248,8 +293,10 @@ export function showMainHelp(
248293 console . log ( 'Usage: tigris [command] [options]\n' ) ;
249294 console . log ( 'Commands:' ) ;
250295
251- const availableCommands = specs . commands . filter ( ( cmd ) =>
252- commandHasAnyImplementation ( cmd , [ cmd . name ] , hasImplementation )
296+ const availableCommands = specs . commands . filter (
297+ ( cmd ) =>
298+ ! cmd . removed &&
299+ commandHasAnyImplementation ( cmd , [ cmd . name ] , hasImplementation )
253300 ) ;
254301
255302 availableCommands . forEach ( ( command : CommandSpec ) => {
@@ -261,7 +308,7 @@ export function showMainHelp(
261308 commandPart += ` (${ aliases . join ( ', ' ) } )` ;
262309 }
263310 const paddedCommandPart = commandPart . padEnd ( 24 ) ;
264- console . log ( `${ paddedCommandPart } ${ command . description } ` ) ;
311+ console . log ( `${ paddedCommandPart } ${ command . description ?? '' } ` ) ;
265312 } ) ;
266313 console . log (
267314 `\nUse "${ specs . name } <command> help" for more information about a command.`
@@ -319,7 +366,15 @@ export function addArgumentsToCommand(
319366 arg . required || arg [ 'required-when' ] ? ' <value>' : ' [value]' ;
320367 }
321368
322- cmd . option ( optionString , arg . description , arg . default ) ;
369+ if ( arg . removed ) {
370+ // Register but hide from --help so commander still parses the
371+ // value; the dispatch handler intercepts it post-parse.
372+ cmd . addOption (
373+ new Option ( optionString , arg . description ?? '' ) . hideHelp ( )
374+ ) ;
375+ } else {
376+ cmd . option ( optionString , arg . description ?? '' , arg . default ) ;
377+ }
323378 }
324379 } ) ;
325380}
@@ -493,13 +548,29 @@ export function registerCommands(
493548 continue ;
494549 }
495550
496- const cmd = parent . command ( spec . name ) . description ( spec . description ) ;
551+ const cmd = parent
552+ . command ( spec . name , spec . removed ? { hidden : true } : undefined )
553+ . description ( spec . description ?? '' ) ;
497554
498555 if ( spec . alias ) {
499556 const aliases = Array . isArray ( spec . alias ) ? spec . alias : [ spec . alias ] ;
500557 aliases . forEach ( ( alias ) => cmd . alias ( alias ) ) ;
501558 }
502559
560+ // Removed commands: register a redirect-and-exit action; skip
561+ // children, arguments, and help registration entirely.
562+ if ( spec . removed ) {
563+ cmd . allowUnknownOption ( true ) ;
564+ cmd . allowExcessArguments ( true ) ;
565+ cmd . action ( ( ) => {
566+ printRemovedAndExit (
567+ `${ specs . name } ${ currentPath . join ( ' ' ) } ` ,
568+ spec . replaced_by
569+ ) ;
570+ } ) ;
571+ continue ;
572+ }
573+
503574 if ( spec . commands && spec . commands . length > 0 ) {
504575 // Has children - recurse
505576 registerCommands ( config , cmd , spec . commands , currentPath ) ;
@@ -524,16 +595,21 @@ export function registerCommands(
524595 hasImplementation
525596 ) ;
526597
598+ const extracted = extractArgumentValues (
599+ allArguments ,
600+ positionalArgs ,
601+ options
602+ ) ;
603+
527604 if (
528605 allArguments . length > 0 &&
529- ! validateRequiredWhen (
530- allArguments ,
531- extractArgumentValues ( allArguments , positionalArgs , options )
532- )
606+ ! validateRequiredWhen ( allArguments , extracted )
533607 ) {
534608 return ;
535609 }
536610
611+ checkRemovedArguments ( allArguments , extracted ) ;
612+
537613 if ( defaultCmd . deprecated && defaultCmd . messages ?. onDeprecated ) {
538614 printDeprecated ( defaultCmd . messages . onDeprecated ) ;
539615 }
@@ -542,7 +618,7 @@ export function registerCommands(
542618 loadModule ,
543619 [ ...currentPath , defaultCmd . name ] ,
544620 positionalArgs ,
545- extractArgumentValues ( allArguments , positionalArgs , options )
621+ extracted
546622 ) ;
547623 } ) ;
548624 }
@@ -570,16 +646,21 @@ export function registerCommands(
570646 const options = args . pop ( ) ;
571647 const positionalArgs = args ;
572648
649+ const extracted = extractArgumentValues (
650+ spec . arguments || [ ] ,
651+ positionalArgs ,
652+ options
653+ ) ;
654+
573655 if (
574656 spec . arguments &&
575- ! validateRequiredWhen (
576- spec . arguments ,
577- extractArgumentValues ( spec . arguments , positionalArgs , options )
578- )
657+ ! validateRequiredWhen ( spec . arguments , extracted )
579658 ) {
580659 return ;
581660 }
582661
662+ checkRemovedArguments ( spec . arguments , extracted ) ;
663+
583664 if ( spec . deprecated && spec . messages ?. onDeprecated ) {
584665 printDeprecated ( spec . messages . onDeprecated ) ;
585666 }
@@ -588,7 +669,7 @@ export function registerCommands(
588669 loadModule ,
589670 currentPath ,
590671 positionalArgs ,
591- extractArgumentValues ( spec . arguments || [ ] , positionalArgs , options )
672+ extracted
592673 ) ;
593674 } ) ;
594675 }
0 commit comments