@@ -27,17 +27,24 @@ export class InvalidPipeException extends BaseException {
27
27
}
28
28
29
29
30
- export const kPathTemplateComponentRE = / _ _ ( .+ ?) _ _ / g;
31
- export const kPathTemplatePipeRE = / @ ( [ ^ @ ] + ) / ;
32
-
33
-
34
30
export type PathTemplateValue = boolean | string | number | undefined ;
35
31
export type PathTemplatePipeFunction = ( x : string ) => PathTemplateValue ;
36
- export type PathTemplateOptions = {
37
- [ key : string ] : PathTemplateValue | PathTemplateOptions | PathTemplatePipeFunction ,
32
+ export type PathTemplateData = {
33
+ [ key : string ] : PathTemplateValue | PathTemplateData | PathTemplatePipeFunction ,
38
34
} ;
39
35
40
36
37
+ export interface PathTemplateOptions {
38
+ // Interpolation start and end strings.
39
+ interpolationStart : string ;
40
+ // Interpolation start and end strings.
41
+ interpolationEnd : string ;
42
+
43
+ // Separator for pipes. Do not specify to remove pipe support.
44
+ pipeSeparator ?: string ;
45
+ }
46
+
47
+
41
48
export function applyContentTemplate < T > ( options : T ) : FileOperator {
42
49
return ( entry : FileEntry ) => {
43
50
const { path, content} = entry ;
@@ -58,57 +65,91 @@ export function contentTemplate<T>(options: T): Rule {
58
65
}
59
66
60
67
61
- export function applyPathTemplate < T extends PathTemplateOptions > ( options : T ) : FileOperator {
68
+ export function applyPathTemplate < T extends PathTemplateData > (
69
+ data : T ,
70
+ options : PathTemplateOptions = {
71
+ interpolationStart : '__' ,
72
+ interpolationEnd : '__' ,
73
+ pipeSeparator : '@' ,
74
+ } ,
75
+ ) : FileOperator {
76
+ const is = options . interpolationStart ;
77
+ const ie = options . interpolationEnd ;
78
+ const isL = is . length ;
79
+ const ieL = ie . length ;
80
+
62
81
return ( entry : FileEntry ) => {
63
- let path = entry . path ;
82
+ let path = entry . path as string ;
64
83
const content = entry . content ;
65
84
const original = path ;
66
85
67
- // Path template.
68
- path = normalize ( path . replace ( kPathTemplateComponentRE , ( _ , match ) => {
69
- const [ name , ...pipes ] = match . split ( kPathTemplatePipeRE ) ;
70
- let value = options [ name ] ;
86
+ let start = path . indexOf ( is ) ;
87
+ // + 1 to have at least a length 1 name. `____` is not valid.
88
+ let end = path . indexOf ( ie , start + isL + 1 ) ;
71
89
72
- if ( typeof value == 'function' ) {
73
- value = value . call ( options , original ) ;
74
- }
90
+ while ( start != - 1 && end != - 1 ) {
91
+ const match = path . substring ( start + isL , end ) ;
92
+ let replacement = data [ match ] ;
75
93
76
- if ( value === undefined ) {
77
- throw new OptionIsNotDefinedException ( name ) ;
78
- }
94
+ if ( ! options . pipeSeparator ) {
95
+ if ( typeof replacement == 'function' ) {
96
+ replacement = replacement . call ( data , original ) ;
97
+ }
79
98
80
- return pipes . reduce ( ( acc : string , pipe : string ) => {
81
- if ( ! pipe ) {
82
- return acc ;
99
+ if ( replacement === undefined ) {
100
+ throw new OptionIsNotDefinedException ( match ) ;
83
101
}
84
- if ( ! ( pipe in options ) ) {
85
- throw new UnknownPipeException ( pipe ) ;
102
+ } else {
103
+ const [ name , ...pipes ] = match . split ( options . pipeSeparator ) ;
104
+ replacement = data [ name ] ;
105
+
106
+ if ( typeof replacement == 'function' ) {
107
+ replacement = replacement . call ( data , original ) ;
86
108
}
87
- if ( typeof options [ pipe ] != 'function' ) {
88
- throw new InvalidPipeException ( pipe ) ;
109
+
110
+ if ( replacement === undefined ) {
111
+ throw new OptionIsNotDefinedException ( name ) ;
89
112
}
90
113
91
- // Coerce to string.
92
- return '' + ( options [ pipe ] as PathTemplatePipeFunction ) ( acc ) ;
93
- } , '' + value ) ;
94
- } ) ) ;
114
+ replacement = pipes . reduce ( ( acc : string , pipe : string ) => {
115
+ if ( ! pipe ) {
116
+ return acc ;
117
+ }
118
+ if ( ! ( pipe in data ) ) {
119
+ throw new UnknownPipeException ( pipe ) ;
120
+ }
121
+ if ( typeof data [ pipe ] != 'function' ) {
122
+ throw new InvalidPipeException ( pipe ) ;
123
+ }
124
+
125
+ // Coerce to string.
126
+ return '' + ( data [ pipe ] as PathTemplatePipeFunction ) ( acc ) ;
127
+ } , '' + replacement ) ;
128
+ }
129
+
130
+ path = path . substring ( 0 , start ) + replacement + path . substring ( end + ieL ) ;
131
+
132
+ start = path . indexOf ( options . interpolationStart ) ;
133
+ // See above.
134
+ end = path . indexOf ( options . interpolationEnd , start + isL + 1 ) ;
135
+ }
95
136
96
- return { path, content } ;
137
+ return { path : normalize ( path ) , content } ;
97
138
} ;
98
139
}
99
140
100
141
101
- export function pathTemplate < T extends PathTemplateOptions > ( options : T ) : Rule {
142
+ export function pathTemplate < T extends PathTemplateData > ( options : T ) : Rule {
102
143
return forEach ( applyPathTemplate ( options ) ) ;
103
144
}
104
145
105
146
106
147
export function template < T > ( options : T ) : Rule {
107
148
return chain ( [
108
149
contentTemplate ( options ) ,
109
- // Force cast to PathTemplateOptions . We need the type for the actual pathTemplate() call,
150
+ // Force cast to PathTemplateData . We need the type for the actual pathTemplate() call,
110
151
// but in this case we cannot do anything as contentTemplate are more permissive.
111
152
// Since values are coerced to strings in PathTemplates it will be fine in the end.
112
- pathTemplate ( options as { } as PathTemplateOptions ) ,
153
+ pathTemplate ( options as { } as PathTemplateData ) ,
113
154
] ) ;
114
155
}
0 commit comments