@@ -19,16 +19,83 @@ export type Ancestor = {
1919 message : string , //color output in the format: "common: source -> target"
2020 } ;
2121export type Result = {
22- flagMap : StringFlagMap , //map of flag values for each user supplied flag
22+ flagMap : StringFlagMap , //map of unescaped flag values for each user supplied flag
23+ flagMapRaw : StringFlagMap , //map of flag values for each user supplied flag
2324 flagOn : BooleanFlagMap , //map of the enabled status for all valid flags
2425 invalidFlag : string | null , //name of the first invalid flag
2526 invalidFlagMsg : string | null , //error message for the invalid flag
26- params : string [ ] , //array of parameter values supplied by the user
2727 paramCount : number , //number of parameters supplied by the user
28+ params : string [ ] , //array of parameter values supplied by the user
2829 } ;
30+ type Json = string | number | boolean | null | undefined | JsonObject | Json [ ] ;
31+ type JsonObject = { [ key : string ] : Json } ;
2932
3033const cliArgvUtil = {
3134
35+ assert ( ok : unknown , message : string | null ) {
36+ if ( ! ok )
37+ throw new Error ( `[replacer-util] ${ message } ` ) ;
38+ } ,
39+
40+ readPackageJson ( ) {
41+ // Returns package.json as an object literal.
42+ const pkgExists = fs . existsSync ( 'package.json' ) ;
43+ const pkg = pkgExists ? < JsonObject > JSON . parse ( fs . readFileSync ( 'package.json' , 'utf-8' ) ) : null ;
44+ const fixHiddenKeys = ( pkgObj : JsonObject ) => {
45+ const unhide = ( key : string ) => {
46+ const newKey = key . replace ( / [ @ . / ] / g, '-' ) ;
47+ if ( ! pkgObj [ newKey ] )
48+ pkgObj [ newKey ] = pkgObj [ key ] ! ;
49+ } ;
50+ Object . keys ( pkgObj ) . forEach ( unhide ) ;
51+ } ;
52+ if ( pkg ?. dependencies )
53+ fixHiddenKeys ( < JsonObject > pkg . dependencies ) ;
54+ if ( pkg ?. devDependencies )
55+ fixHiddenKeys ( < JsonObject > pkg . devDependencies ) ;
56+ return pkg ;
57+ } ,
58+
59+ unescape ( flags : StringFlagMap ) : StringFlagMap {
60+ // Returns a map of CLI flags with all the flag values unescaped and macros expanded.
61+ // Example:
62+ // '{{hash}}{{space}}Allow{{space}}bots{{bang}}' --> '# Allow bots!'
63+ const escapers : [ RegExp , string ] [ ] = [
64+ [ / { { apos} } / g, "'" ] ,
65+ [ / { { bang} } / g, '!' ] ,
66+ [ / { { close-c u r l y } } / g, '}' ] ,
67+ [ / { { equals} } / g, '=' ] ,
68+ [ / { { gt} } / g, '>' ] ,
69+ [ / { { hash} } / g, '#' ] ,
70+ [ / { { lt} } / g, '<' ] ,
71+ [ / { { open-c u r l y } } / g, '{' ] ,
72+ [ / { { pipe} } / g, '|' ] ,
73+ [ / { { quote} } / g, '"' ] ,
74+ [ / { { semi} } / g, ';' ] ,
75+ [ / { { space} } / g, ' ' ] ,
76+ ] ;
77+ const macroPattern = / ^ { { m a c r o : ( .* ) } } $ / ;
78+ const flagEntries = Object . entries ( flags ) ;
79+ const usesMacros = flagEntries . some ( entry => entry [ 1 ] ?. match ( macroPattern ) ) ;
80+ const pkg = usesMacros ? cliArgvUtil . readPackageJson ( ) ! : { } ;
81+ if ( ! pkg . cliConfig && pkg . replacerConfig ) //DEPRECATED
82+ pkg . cliConfig = pkg . replacerConfig ; //backwards compatibility workaround
83+ const macros = < JsonObject | undefined > ( < JsonObject | undefined > pkg . cliConfig ) ?. macros ;
84+ const unescapeOne = ( flagValue : string , escaper : typeof escapers [ number ] ) =>
85+ flagValue . replace ( escaper [ 0 ] , escaper [ 1 ] ) ;
86+ const expandMacro = ( flagValue : string ) => {
87+ // If param is a macro defined in package.json, return the macro's value.
88+ const macroName = < keyof JsonObject > flagValue . match ( macroPattern ) ?. [ 1 ] ;
89+ const macroValue = < string > macros ?. [ macroName ] ;
90+ const missing = macroName && ! macroValue ;
91+ cliArgvUtil . assert ( ! missing , `Macro "${ macroName } " used but not defined in package.json` ) ;
92+ return macroName ? macroValue : flagValue ;
93+ } ;
94+ const doReplacements = ( flagValue ?: string ) =>
95+ ! flagValue ? undefined : escapers . reduce ( unescapeOne , expandMacro ( flagValue ) ) ;
96+ return Object . fromEntries ( flagEntries . map ( pair => [ pair [ 0 ] , doReplacements ( pair [ 1 ] ) ] ) ) ;
97+ } ,
98+
3299 parse ( validFlags : string [ ] ) : Result {
33100 const toCamel = ( token : string ) => token . replace ( / - ./ g, char => char [ 1 ] ! . toUpperCase ( ) ) ; //example: 'no-map' --> 'noMap'
34101 const toEntry = ( pair : string [ ] ) => [ toCamel ( pair [ 0 ] ! ) , pair [ 1 ] ] ; //example: ['no-map'] --> ['noMap', undefined]
@@ -42,7 +109,8 @@ const cliArgvUtil = {
42109 const helpMsg = '\nValid flags are --' + validFlags . join ( ' --' ) ;
43110 const params = args . filter ( arg => ! / ^ - - / . test ( arg ) ) ;
44111 return {
45- flagMap : flagMap ,
112+ flagMap : cliArgvUtil . unescape ( flagMap ) ,
113+ flagMapRaw : flagMap ,
46114 flagOn : flagOn ,
47115 invalidFlag : invalidFlag ,
48116 invalidFlagMsg : invalidFlag ? 'Invalid flag: --' + invalidFlag + helpMsg : null ,
0 commit comments