@@ -283,14 +283,21 @@ export function processExpressions(
283
283
function analyzeExpressions ( expressions : SimpleExpressionNode [ ] ) {
284
284
const seenVariable : Record < string , number > = Object . create ( null )
285
285
const variableToExpMap = new Map < string , Set < SimpleExpressionNode > > ( )
286
- const expToVariableMap = new Map < SimpleExpressionNode , string [ ] > ( )
286
+ const expToVariableMap = new Map <
287
+ SimpleExpressionNode ,
288
+ Array < {
289
+ name : string
290
+ loc ?: { start : number ; end : number }
291
+ } >
292
+ > ( )
287
293
const seenIdentifier = new Set < string > ( )
288
294
const updatedVariable = new Set < string > ( )
289
295
290
296
const registerVariable = (
291
297
name : string ,
292
298
exp : SimpleExpressionNode ,
293
299
isIdentifier : boolean ,
300
+ loc ?: { start : number ; end : number } ,
294
301
parentStack : Node [ ] = [ ] ,
295
302
) => {
296
303
if ( isIdentifier ) seenIdentifier . add ( name )
@@ -299,7 +306,11 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) {
299
306
name ,
300
307
( variableToExpMap . get ( name ) || new Set ( ) ) . add ( exp ) ,
301
308
)
302
- expToVariableMap . set ( exp , ( expToVariableMap . get ( exp ) || [ ] ) . concat ( name ) )
309
+
310
+ const variables = expToVariableMap . get ( exp ) || [ ]
311
+ variables . push ( { name, loc } )
312
+ expToVariableMap . set ( exp , variables )
313
+
303
314
if (
304
315
parentStack . some (
305
316
p => p . type === 'UpdateExpression' || p . type === 'AssignmentExpression' ,
@@ -317,12 +328,27 @@ function analyzeExpressions(expressions: SimpleExpressionNode[]) {
317
328
318
329
walkIdentifiers ( exp . ast , ( currentNode , parent , parentStack ) => {
319
330
if ( parent && isMemberExpression ( parent ) ) {
320
- const memberExp = extractMemberExpression ( parent , name => {
321
- registerVariable ( name , exp , true )
331
+ const memberExp = extractMemberExpression ( parent , id => {
332
+ registerVariable ( id . name , exp , true , {
333
+ start : id . start ! ,
334
+ end : id . end ! ,
335
+ } )
322
336
} )
323
- registerVariable ( memberExp , exp , false , parentStack )
337
+ registerVariable (
338
+ memberExp ,
339
+ exp ,
340
+ false ,
341
+ { start : parent . start ! , end : parent . end ! } ,
342
+ parentStack ,
343
+ )
324
344
} else if ( ! parentStack . some ( isMemberExpression ) ) {
325
- registerVariable ( currentNode . name , exp , true , parentStack )
345
+ registerVariable (
346
+ currentNode . name ,
347
+ exp ,
348
+ true ,
349
+ { start : currentNode . start ! , end : currentNode . end ! } ,
350
+ parentStack ,
351
+ )
326
352
}
327
353
} )
328
354
}
@@ -340,11 +366,22 @@ function processRepeatedVariables(
340
366
context : CodegenContext ,
341
367
seenVariable : Record < string , number > ,
342
368
variableToExpMap : Map < string , Set < SimpleExpressionNode > > ,
343
- expToVariableMap : Map < SimpleExpressionNode , string [ ] > ,
369
+ expToVariableMap : Map <
370
+ SimpleExpressionNode ,
371
+ Array < { name : string ; loc ?: { start : number ; end : number } } >
372
+ > ,
344
373
seenIdentifier : Set < string > ,
345
374
updatedVariable : Set < string > ,
346
375
) : DeclarationValue [ ] {
347
376
const declarations : DeclarationValue [ ] = [ ]
377
+ const expToReplacementMap = new Map <
378
+ SimpleExpressionNode ,
379
+ Array < {
380
+ name : string
381
+ locs : { start : number ; end : number } [ ]
382
+ } >
383
+ > ( )
384
+
348
385
for ( const [ name , exps ] of variableToExpMap ) {
349
386
if ( updatedVariable . has ( name ) ) continue
350
387
if ( seenVariable [ name ] > 1 && exps . size > 0 ) {
@@ -356,12 +393,20 @@ function processRepeatedVariables(
356
393
// e.g., foo[baz] -> foo_baz.
357
394
// for identifiers, we don't need to replace the content - they will be
358
395
// replaced during context.withId(..., ids)
359
- const replaceRE = new RegExp ( escapeRegExp ( name ) , 'g' )
360
396
exps . forEach ( node => {
361
- if ( node . ast ) {
362
- node . content = node . content . replace ( replaceRE , varName )
363
- // re-parse the expression
364
- node . ast = parseExp ( context , node . content )
397
+ if ( node . ast && varName !== name ) {
398
+ const replacements = expToReplacementMap . get ( node ) || [ ]
399
+ replacements . push ( {
400
+ name : varName ,
401
+ locs : expToVariableMap . get ( node ) ! . reduce (
402
+ ( locs , v ) => {
403
+ if ( v . name === name && v . loc ) locs . push ( v . loc )
404
+ return locs
405
+ } ,
406
+ [ ] as { start : number ; end : number } [ ] ,
407
+ ) ,
408
+ } )
409
+ expToReplacementMap . set ( node , replacements )
365
410
}
366
411
} )
367
412
@@ -384,15 +429,35 @@ function processRepeatedVariables(
384
429
}
385
430
}
386
431
432
+ for ( const [ exp , replacements ] of expToReplacementMap ) {
433
+ replacements
434
+ . flatMap ( ( { name, locs } ) =>
435
+ locs . map ( ( { start, end } ) => ( { start, end, name } ) ) ,
436
+ )
437
+ . sort ( ( a , b ) => b . end - a . end )
438
+ . forEach ( ( { start, end, name } ) => {
439
+ exp . content =
440
+ exp . content . slice ( 0 , start - 1 ) + name + exp . content . slice ( end - 1 )
441
+ } )
442
+
443
+ // re-parse the expression
444
+ exp . ast = parseExp ( context , exp . content )
445
+ }
446
+
387
447
return declarations
388
448
}
389
449
390
450
function shouldDeclareVariable (
391
451
name : string ,
392
- expToVariableMap : Map < SimpleExpressionNode , string [ ] > ,
452
+ expToVariableMap : Map <
453
+ SimpleExpressionNode ,
454
+ Array < { name : string ; loc ?: { start : number ; end : number } } >
455
+ > ,
393
456
exps : Set < SimpleExpressionNode > ,
394
457
) : boolean {
395
- const vars = Array . from ( exps , exp => expToVariableMap . get ( exp ) ! )
458
+ const vars = Array . from ( exps , exp =>
459
+ expToVariableMap . get ( exp ) ! . map ( v => v . name ) ,
460
+ )
396
461
// assume name equals to `foo`
397
462
// if each expression only references `foo`, declaration is needed
398
463
// to avoid reactivity tracking
@@ -439,12 +504,15 @@ function processRepeatedExpressions(
439
504
expressions : SimpleExpressionNode [ ] ,
440
505
varDeclarations : DeclarationValue [ ] ,
441
506
updatedVariable : Set < string > ,
442
- expToVariableMap : Map < SimpleExpressionNode , string [ ] > ,
507
+ expToVariableMap : Map <
508
+ SimpleExpressionNode ,
509
+ Array < { name : string ; loc ?: { start : number ; end : number } } >
510
+ > ,
443
511
) : DeclarationValue [ ] {
444
512
const declarations : DeclarationValue [ ] = [ ]
445
513
const seenExp = expressions . reduce (
446
514
( acc , exp ) => {
447
- const variables = expToVariableMap . get ( exp )
515
+ const variables = expToVariableMap . get ( exp ) ! . map ( v => v . name )
448
516
// only handle expressions that are not identifiers
449
517
if (
450
518
exp . ast &&
@@ -572,12 +640,12 @@ function genVarName(exp: string): string {
572
640
573
641
function extractMemberExpression (
574
642
exp : Node ,
575
- onIdentifier : ( name : string ) => void ,
643
+ onIdentifier : ( id : Identifier ) => void ,
576
644
) : string {
577
645
if ( ! exp ) return ''
578
646
switch ( exp . type ) {
579
647
case 'Identifier' : // foo[bar]
580
- onIdentifier ( exp . name )
648
+ onIdentifier ( exp )
581
649
return exp . name
582
650
case 'StringLiteral' : // foo['bar']
583
651
return exp . extra ? ( exp . extra . raw as string ) : exp . value
0 commit comments