@@ -26,14 +26,16 @@ async function run(rootDir, options = {}) {
2626 const step = num => chalk . dim ( `[${ num } /${ NUM_STEPS } ]` ) ;
2727
2828 let config = options . config || readConfig ( rootDir ) ;
29+ let analyzeConcatExpression = options . analyzeConcatExpression || config . analyzeConcatExpression ;
30+ let analyzeOptions = { analyzeConcatExpression } ;
2931
3032 log ( `${ step ( 1 ) } 🔍 Finding JS and HBS files...` ) ;
3133 let appFiles = await findAppFiles ( rootDir ) ;
3234 let inRepoFiles = await findInRepoFiles ( rootDir ) ;
3335 let files = [ ...appFiles , ...inRepoFiles ] ;
3436
3537 log ( `${ step ( 2 ) } 🔍 Searching for translations keys in JS and HBS files...` ) ;
36- let usedTranslationKeys = await analyzeFiles ( rootDir , files ) ;
38+ let usedTranslationKeys = await analyzeFiles ( rootDir , files , analyzeOptions ) ;
3739
3840 log ( `${ step ( 3 ) } ⚙️ Checking for unused translations...` ) ;
3941
@@ -161,11 +163,11 @@ function joinPaths(inputPathOrPaths, outputPaths) {
161163 }
162164}
163165
164- async function analyzeFiles ( cwd , files ) {
166+ async function analyzeFiles ( cwd , files , options ) {
165167 let allTranslationKeys = new Map ( ) ;
166168
167169 for ( let file of files ) {
168- let translationKeys = await analyzeFile ( cwd , file ) ;
170+ let translationKeys = await analyzeFile ( cwd , file , options ) ;
169171
170172 for ( let key of translationKeys ) {
171173 if ( allTranslationKeys . has ( key ) ) {
@@ -179,17 +181,17 @@ async function analyzeFiles(cwd, files) {
179181 return allTranslationKeys ;
180182}
181183
182- async function analyzeFile ( cwd , file ) {
184+ async function analyzeFile ( cwd , file , options ) {
183185 let content = fs . readFileSync ( `${ cwd } /${ file } ` , 'utf8' ) ;
184186 let extension = path . extname ( file ) . toLowerCase ( ) ;
185187
186188 if ( extension === '.js' ) {
187- return analyzeJsFile ( content ) ;
189+ return analyzeJsFile ( content , options ) ;
188190 } else if ( extension === '.hbs' ) {
189- return analyzeHbsFile ( content ) ;
191+ return analyzeHbsFile ( content , options ) ;
190192 } else if ( extension === '.emblem' ) {
191193 let hbs = Emblem . compile ( content , { quiet : true } ) ;
192- return analyzeHbsFile ( hbs ) ;
194+ return analyzeHbsFile ( hbs , options ) ;
193195 } else {
194196 throw new Error ( `Unknown extension: ${ extension } (${ file } )` ) ;
195197 }
@@ -232,50 +234,77 @@ async function analyzeJsFile(content) {
232234 return translationKeys ;
233235}
234236
235- async function analyzeHbsFile ( content ) {
237+ async function analyzeHbsFile ( content , { analyzeConcatExpression = false } ) {
236238 let translationKeys = new Set ( ) ;
237239
238240 // parse the HBS file
239241 let ast = Glimmer . preprocess ( content ) ;
240242
243+ function findKeysInIfExpression ( node ) {
244+ let keysInFirstParam = findKeysInNode ( node . params [ 1 ] ) ;
245+ let keysInSecondParam = node . params . length > 2 ? findKeysInNode ( node . params [ 2 ] ) : [ '' ] ;
246+
247+ return [ ...keysInFirstParam , ...keysInSecondParam ] ;
248+ }
249+
250+ function findKeysInConcatExpression ( node ) {
251+ let potentialKeys = [ '' ] ;
252+
253+ for ( let param of node . params ) {
254+ let keysInParam = findKeysInNode ( param ) ;
255+
256+ if ( keysInParam . length === 0 ) return [ ] ;
257+
258+ potentialKeys = potentialKeys . reduce ( ( newPotentialKeys , potentialKey ) => {
259+ for ( let key of keysInParam ) {
260+ newPotentialKeys . push ( potentialKey + key ) ;
261+ }
262+
263+ return newPotentialKeys ;
264+ } , [ ] ) ;
265+ }
266+
267+ return potentialKeys ;
268+ }
269+
270+ function findKeysInNode ( node ) {
271+ if ( ! node ) return [ ] ;
272+
273+ if ( node . type === 'StringLiteral' ) {
274+ return [ node . value ] ;
275+ } else if ( node . type === 'SubExpression' && node . path . original === 'if' ) {
276+ return findKeysInIfExpression ( node ) ;
277+ } else if (
278+ analyzeConcatExpression &&
279+ node . type === 'SubExpression' &&
280+ node . path . original === 'concat'
281+ ) {
282+ return findKeysInConcatExpression ( node ) ;
283+ }
284+
285+ return [ ] ;
286+ }
287+
288+ function processNode ( node ) {
289+ if ( node . path . type !== 'PathExpression' ) return ;
290+ if ( node . path . original !== 't' ) return ;
291+ if ( node . params . length === 0 ) return ;
292+
293+ for ( let key of findKeysInNode ( node . params [ 0 ] ) ) {
294+ translationKeys . add ( key ) ;
295+ }
296+ }
297+
241298 // find translation keys in the syntax tree
242299 Glimmer . traverse ( ast , {
243300 // handle {{t "foo"}} case
244301 MustacheStatement ( node ) {
245- if ( node . path . type !== 'PathExpression' ) return ;
246- if ( node . path . original !== 't' ) return ;
247- if ( node . params . length === 0 ) return ;
248-
249- let firstParam = node . params [ 0 ] ;
250- if ( firstParam . type === 'StringLiteral' ) {
251- translationKeys . add ( firstParam . value ) ;
252- } else if ( firstParam . type === 'SubExpression' && firstParam . path . original === 'if' ) {
253- if ( firstParam . params [ 1 ] . type === 'StringLiteral' ) {
254- translationKeys . add ( firstParam . params [ 1 ] . value ) ;
255- }
256- if ( firstParam . params [ 2 ] . type === 'StringLiteral' ) {
257- translationKeys . add ( firstParam . params [ 2 ] . value ) ;
258- }
259- }
302+ processNode ( node ) ;
260303 } ,
261304
262305 // handle {{some-component foo=(t "bar")}} case
263306 SubExpression ( node ) {
264- if ( node . path . type !== 'PathExpression' ) return ;
265- if ( node . path . original !== 't' ) return ;
266- if ( node . params . length === 0 ) return ;
267-
268- let firstParam = node . params [ 0 ] ;
269- if ( firstParam . type === 'StringLiteral' ) {
270- translationKeys . add ( firstParam . value ) ;
271- } else if ( firstParam . type === 'SubExpression' && firstParam . path . original === 'if' ) {
272- if ( firstParam . params [ 1 ] . type === 'StringLiteral' ) {
273- translationKeys . add ( firstParam . params [ 1 ] . value ) ;
274- }
275- if ( firstParam . params [ 2 ] . type === 'StringLiteral' ) {
276- translationKeys . add ( firstParam . params [ 2 ] . value ) ;
277- }
278- }
307+ processNode ( node ) ;
279308 } ,
280309 } ) ;
281310
0 commit comments