@@ -186,25 +186,12 @@ const getTypeLabel = (def: OptionDef): string => {
186186} ;
187187
188188/**
189- * Format a single option for help output.
190- *
191- * For boolean options with `default: true`, shows `--no-<name>` instead of
192- * `--<name>` since that's how users would turn it off.
193- *
194- * Displays aliases in order: short alias first (-v), then multi-char aliases
195- * sorted by length (--verb), then the canonical name (--verbose).
189+ * Get the flag text for an option (used for width calculation and display).
196190 *
197191 * @function
198192 */
199- const formatOptionHelp = (
200- name : string ,
201- def : OptionDef ,
202- styler : Styler ,
203- ) : string => {
204- const parts : string [ ] = [ ] ;
205-
193+ const getOptionFlagText = ( name : string , def : OptionDef ) : string => {
206194 // For boolean options with default: true, show --no-<name>
207- // since that's how users would turn it off
208195 const displayName =
209196 def . type === 'boolean' && def . default === true ? `no-${ name } ` : name ;
210197
@@ -215,7 +202,6 @@ const formatOptionHelp = (
215202 . sort ( ( a , b ) => a . length - b . length ) ;
216203
217204 // Build flag string: -v, --verb, --verbose
218- // Don't show short alias for negated booleans
219205 const flagParts : string [ ] = [ ] ;
220206 if ( shortAlias && displayName === name ) {
221207 flagParts . push ( `-${ shortAlias } ` ) ;
@@ -226,14 +212,51 @@ const formatOptionHelp = (
226212 flagParts . push ( `--${ displayName } ` ) ;
227213
228214 // If no short alias and no long aliases, add padding
229- const flagText =
230- flagParts . length === 1 && ! shortAlias
231- ? ` ${ flagParts [ 0 ] } `
232- : flagParts . join ( ', ' ) ;
215+ return flagParts . length === 1 && ! shortAlias
216+ ? ` ${ flagParts [ 0 ] } `
217+ : flagParts . join ( ', ' ) ;
218+ } ;
219+
220+ /**
221+ * Calculate the max flag width for a set of options.
222+ *
223+ * @function
224+ */
225+ const calculateMaxFlagWidth = (
226+ options : Array < { def : OptionDef ; name : string } > ,
227+ ) : number => {
228+ let maxWidth = 0 ;
229+ for ( const { def, name } of options ) {
230+ const flagText = getOptionFlagText ( name , def ) ;
231+ maxWidth = Math . max ( maxWidth , flagText . length ) ;
232+ }
233+ return maxWidth ;
234+ } ;
235+
236+ /**
237+ * Format a single option for help output.
238+ *
239+ * For boolean options with `default: true`, shows `--no-<name>` instead of
240+ * `--<name>` since that's how users would turn it off.
241+ *
242+ * Displays aliases in order: short alias first (-v), then multi-char aliases
243+ * sorted by length (--verb), then the canonical name (--verbose).
244+ *
245+ * @function
246+ */
247+ const formatOptionHelp = (
248+ name : string ,
249+ def : OptionDef ,
250+ styler : Styler ,
251+ maxFlagWidth ?: number ,
252+ ) : string => {
253+ const parts : string [ ] = [ ] ;
254+
255+ const flagText = getOptionFlagText ( name , def ) ;
233256 parts . push ( ` ${ styler . flag ( flagText ) } ` ) ;
234257
235- // Pad to align descriptions (increase base padding for longer alias chains)
236- const basePadding = Math . max ( 24 , flagText . length + 4 ) ;
258+ // Pad to align descriptions using provided maxFlagWidth or calculate dynamically
259+ const basePadding = Math . max ( 24 , ( maxFlagWidth ?? flagText . length ) + 4 ) ;
237260 const padding = Math . max ( 0 , basePadding - flagText . length - 2 ) ;
238261 parts . push ( ' ' . repeat ( padding ) ) ;
239262
@@ -354,11 +377,15 @@ export const generateHelp = (
354377 }
355378 }
356379
380+ // Calculate max flag width across all visible options for alignment
381+ const allOptions = [ ...ungrouped , ...Array . from ( groups . values ( ) ) . flat ( ) ] ;
382+ const maxFlagWidth = calculateMaxFlagWidth ( allOptions ) ;
383+
357384 // Print grouped options
358385 for ( const [ groupName , options ] of Array . from ( groups . entries ( ) ) ) {
359386 lines . push ( styler . sectionHeader ( groupName . toUpperCase ( ) ) ) ;
360387 for ( const opt of options ) {
361- lines . push ( formatOptionHelp ( opt . name , opt . def , styler ) ) ;
388+ lines . push ( formatOptionHelp ( opt . name , opt . def , styler , maxFlagWidth ) ) ;
362389 }
363390 lines . push ( '' ) ;
364391 }
@@ -368,7 +395,7 @@ export const generateHelp = (
368395 const label = hasCommands ( config ) ? 'GLOBAL OPTIONS' : 'OPTIONS' ;
369396 lines . push ( styler . sectionHeader ( label ) ) ;
370397 for ( const opt of ungrouped ) {
371- lines . push ( formatOptionHelp ( opt . name , opt . def , styler ) ) ;
398+ lines . push ( formatOptionHelp ( opt . name , opt . def , styler , maxFlagWidth ) ) ;
372399 }
373400 lines . push ( '' ) ;
374401 }
@@ -451,14 +478,32 @@ export const generateCommandHelp = (
451478 lines . push ( styler . usage ( ` ${ usageParts } ` ) ) ;
452479 lines . push ( '' ) ;
453480
481+ // Collect all visible options for alignment calculation
482+ const allOptions : Array < { def : OptionDef ; name : string } > = [ ] ;
483+ if ( command . options ) {
484+ for ( const [ name , def ] of Object . entries ( command . options ) ) {
485+ if ( ! def . hidden ) {
486+ allOptions . push ( { def, name } ) ;
487+ }
488+ }
489+ }
490+ if ( config . options ) {
491+ for ( const [ name , def ] of Object . entries ( config . options ) ) {
492+ if ( ! def . hidden ) {
493+ allOptions . push ( { def, name } ) ;
494+ }
495+ }
496+ }
497+ const maxFlagWidth = calculateMaxFlagWidth ( allOptions ) ;
498+
454499 // Command options
455500 if ( command . options && Object . keys ( command . options ) . length > 0 ) {
456501 lines . push ( styler . sectionHeader ( 'OPTIONS' ) ) ;
457502 for ( const [ name , def ] of Object . entries ( command . options ) ) {
458503 if ( def . hidden ) {
459504 continue ;
460505 }
461- lines . push ( formatOptionHelp ( name , def , styler ) ) ;
506+ lines . push ( formatOptionHelp ( name , def , styler , maxFlagWidth ) ) ;
462507 }
463508 lines . push ( '' ) ;
464509 }
@@ -470,7 +515,7 @@ export const generateCommandHelp = (
470515 if ( def . hidden ) {
471516 continue ;
472517 }
473- lines . push ( formatOptionHelp ( name , def , styler ) ) ;
518+ lines . push ( formatOptionHelp ( name , def , styler , maxFlagWidth ) ) ;
474519 }
475520 lines . push ( '' ) ;
476521 }
0 commit comments