@@ -365,9 +365,13 @@ class Jack {
365365 * an explicit CLI setting.
366366 */
367367 parse ( args = process . argv ) {
368- if ( args === process . argv ) {
369- args = args . slice ( process . _eval !== undefined ? 1 : 2 ) ;
370- }
368+ this . loadEnvDefaults ( ) ;
369+ const p = this . parseRaw ( args ) ;
370+ this . applyDefaults ( p ) ;
371+ this . writeEnv ( p ) ;
372+ return p ;
373+ }
374+ loadEnvDefaults ( ) {
371375 if ( this . #envPrefix) {
372376 for ( const [ field , my ] of Object . entries ( this . #configSet) ) {
373377 const ek = toEnvKey ( this . #envPrefix, field ) ;
@@ -377,6 +381,25 @@ class Jack {
377381 }
378382 }
379383 }
384+ }
385+ applyDefaults ( p ) {
386+ for ( const [ field , c ] of Object . entries ( this . #configSet) ) {
387+ if ( c . default !== undefined && ! ( field in p . values ) ) {
388+ //@ts -ignore
389+ p . values [ field ] = c . default ;
390+ }
391+ }
392+ }
393+ /**
394+ * Only parse the command line arguments passed in.
395+ * Does not strip off the `node script.js` bits, so it must be just the
396+ * arguments you wish to have parsed.
397+ * Does not read from or write to the environment, or set defaults.
398+ */
399+ parseRaw ( args ) {
400+ if ( args === process . argv ) {
401+ args = args . slice ( process . _eval !== undefined ? 1 : 2 ) ;
402+ }
380403 const options = toParseArgsOptionsConfig ( this . #configSet) ;
381404 const result = ( 0 , parse_args_js_1 . parseArgs ) ( {
382405 args,
@@ -393,9 +416,10 @@ class Jack {
393416 for ( const token of result . tokens ) {
394417 if ( token . kind === 'positional' ) {
395418 p . positionals . push ( token . value ) ;
396- if ( this . #options. stopAtPositional ) {
419+ if ( this . #options. stopAtPositional ||
420+ this . #options. stopAtPositionalTest ?. ( token . value ) ) {
397421 p . positionals . push ( ...args . slice ( token . index + 1 ) ) ;
398- return p ;
422+ break ;
399423 }
400424 }
401425 else if ( token . kind === 'option' ) {
@@ -469,12 +493,6 @@ class Jack {
469493 }
470494 }
471495 }
472- for ( const [ field , c ] of Object . entries ( this . #configSet) ) {
473- if ( c . default !== undefined && ! ( field in p . values ) ) {
474- //@ts -ignore
475- p . values [ field ] = c . default ;
476- }
477- }
478496 for ( const [ field , value ] of Object . entries ( p . values ) ) {
479497 const valid = this . #configSet[ field ] ?. validate ;
480498 const validOptions = this . #configSet[ field ] ?. validOptions ;
@@ -489,7 +507,6 @@ class Jack {
489507 throw new Error ( `Invalid value provided for --${ field } : ${ JSON . stringify ( value ) } ` , { cause } ) ;
490508 }
491509 }
492- this . #writeEnv( p ) ;
493510 return p ;
494511 }
495512 /**
@@ -557,7 +574,7 @@ class Jack {
557574 }
558575 }
559576 }
560- # writeEnv( p ) {
577+ writeEnv ( p ) {
561578 if ( ! this . #env || ! this . #envPrefix)
562579 return ;
563580 for ( const [ field , value ] of Object . entries ( p . values ) ) {
@@ -912,6 +929,7 @@ class Jack {
912929 ...( def . validate ? { validate : def . validate } : { } ) ,
913930 ...( def . validOptions ? { validOptions : def . validOptions } : { } ) ,
914931 ...( def . default !== undefined ? { default : def . default } : { } ) ,
932+ ...( def . hint ? { hint : def . hint } : { } ) ,
915933 } ,
916934 ] ) ) ;
917935 }
@@ -925,22 +943,52 @@ class Jack {
925943exports . Jack = Jack ;
926944// Unwrap and un-indent, so we can wrap description
927945// strings however makes them look nice in the code.
928- const normalize = ( s , pre = false ) => pre ?
929- // prepend a ZWSP to each line so cliui doesn't strip it.
930- s
931- . split ( '\n' )
932- . map ( l => `\u200b${ l } ` )
933- . join ( '\n' )
934- : s
935- // remove single line breaks, except for lists
936- . replace ( / ( [ ^ \n ] ) \n [ \t ] * ( [ ^ \n ] ) / g, ( _ , $1 , $2 ) => ! / ^ [ - * ] / . test ( $2 ) ? `${ $1 } ${ $2 } ` : `${ $1 } \n${ $2 } ` )
937- // normalize mid-line whitespace
938- . replace ( / ( [ ^ \n ] ) [ \t ] + ( [ ^ \n ] ) / g, '$1 $2' )
939- // two line breaks are enough
940- . replace ( / \n { 3 , } / g, '\n\n' )
941- // remove any spaces at the start of a line
942- . replace ( / \n [ \t ] + / g, '\n' )
943- . trim ( ) ;
946+ const normalize = ( s , pre = false ) => {
947+ if ( pre )
948+ // prepend a ZWSP to each line so cliui doesn't strip it.
949+ return s
950+ . split ( '\n' )
951+ . map ( l => `\u200b${ l } ` )
952+ . join ( '\n' ) ;
953+ return s
954+ . split ( / ^ \s * ` ` ` \s * $ / gm)
955+ . map ( ( s , i ) => {
956+ if ( i % 2 === 1 ) {
957+ if ( ! s . trim ( ) ) {
958+ return `\`\`\`\n\`\`\`\n` ;
959+ }
960+ // outdent the ``` blocks, but preserve whitespace otherwise.
961+ const split = s . split ( '\n' ) ;
962+ // throw out the \n at the start and end
963+ split . pop ( ) ;
964+ split . shift ( ) ;
965+ const si = split . reduce ( ( shortest , l ) => {
966+ /* c8 ignore next */
967+ const ind = l . match ( / ^ \s * / ) ?. [ 0 ] ?? '' ;
968+ if ( ind . length )
969+ return Math . min ( ind . length , shortest ) ;
970+ else
971+ return shortest ;
972+ } , Infinity ) ;
973+ /* c8 ignore next */
974+ const i = isFinite ( si ) ? si : 0 ;
975+ return ( '\n```\n' +
976+ split . map ( s => `\u200b${ s . substring ( i ) } ` ) . join ( '\n' ) +
977+ '\n```\n' ) ;
978+ }
979+ return ( s
980+ // remove single line breaks, except for lists
981+ . replace ( / ( [ ^ \n ] ) \n [ \t ] * ( [ ^ \n ] ) / g, ( _ , $1 , $2 ) => ! / ^ [ - * ] / . test ( $2 ) ? `${ $1 } ${ $2 } ` : `${ $1 } \n${ $2 } ` )
982+ // normalize mid-line whitespace
983+ . replace ( / ( [ ^ \n ] ) [ \t ] + ( [ ^ \n ] ) / g, '$1 $2' )
984+ // two line breaks are enough
985+ . replace ( / \n { 3 , } / g, '\n\n' )
986+ // remove any spaces at the start of a line
987+ . replace ( / \n [ \t ] + / g, '\n' )
988+ . trim ( ) ) ;
989+ } )
990+ . join ( '\n' ) ;
991+ } ;
944992// normalize for markdown printing, remove leading spaces on lines
945993const normalizeMarkdown = ( s , pre = false ) => {
946994 const n = normalize ( s , pre ) . replace ( / \\ / g, '\\\\' ) ;
0 commit comments