1
- /** @import { VariableDeclarator, Node, Identifier } from 'estree' */
1
+ /** @import { VariableDeclarator, Node, Identifier, AssignmentExpression, LabeledStatement } from 'estree' */
2
2
/** @import { Visitors } from 'zimmerframe' */
3
3
/** @import { ComponentAnalysis } from '../phases/types.js' */
4
4
/** @import { Scope, ScopeRoot } from '../phases/scope.js' */
@@ -88,7 +88,8 @@ export function migrate(source) {
88
88
} ,
89
89
legacy_imports : new Set ( ) ,
90
90
script_insertions : new Set ( ) ,
91
- derived_components : new Map ( )
91
+ derived_components : new Map ( ) ,
92
+ derived_labeled_statements : new Set ( )
92
93
} ;
93
94
94
95
if ( parsed . module ) {
@@ -102,6 +103,13 @@ export function migrate(source) {
102
103
walk ( parsed . instance . content , state , instance_script ) ;
103
104
}
104
105
106
+ for ( let labeled_to_remove of state . derived_labeled_statements ) {
107
+ state . str . remove (
108
+ /** @type {number } */ ( labeled_to_remove . start ) ,
109
+ /** @type {number } */ ( labeled_to_remove . end )
110
+ ) ;
111
+ }
112
+
105
113
state = { ...state , scope : analysis . template . scope } ;
106
114
walk ( parsed . fragment , state , template ) ;
107
115
@@ -289,7 +297,8 @@ export function migrate(source) {
289
297
* names: Record<string, string>;
290
298
* legacy_imports: Set<string>;
291
299
* script_insertions: Set<string>;
292
- * derived_components: Map<string, string>
300
+ * derived_components: Map<string, string>,
301
+ * derived_labeled_statements: Set<LabeledStatement>
293
302
* }} State
294
303
*/
295
304
@@ -337,7 +346,7 @@ const instance_script = {
337
346
state . str . remove ( /** @type {number } */ ( node . start ) , /** @type {number } */ ( node . end ) ) ;
338
347
}
339
348
} ,
340
- VariableDeclaration ( node , { state, path } ) {
349
+ VariableDeclaration ( node , { state, path, visit } ) {
341
350
if ( state . scope !== state . analysis . instance . scope ) {
342
351
return ;
343
352
}
@@ -457,10 +466,80 @@ const instance_script = {
457
466
state . str . prependLeft ( start , '$state(' ) ;
458
467
state . str . appendRight ( end , ')' ) ;
459
468
} else {
460
- state . str . prependLeft (
461
- /** @type {number } */ ( declarator . id . typeAnnotation ?. end ?? declarator . id . end ) ,
462
- ' = $state()'
469
+ /**
470
+ * @type {AssignmentExpression | undefined }
471
+ */
472
+ let assignment_in_labeled ;
473
+ /**
474
+ * @type {LabeledStatement | undefined }
475
+ */
476
+ let labeled_statement ;
477
+
478
+ const possible_derived = bindings . every ( ( binding ) =>
479
+ binding . references . every ( ( reference ) => {
480
+ const declaration_idx = reference . path . findIndex (
481
+ ( el ) => el . type === 'VariableDeclaration'
482
+ ) ;
483
+ const assignment_idx = reference . path . findIndex (
484
+ ( el ) => el . type === 'AssignmentExpression'
485
+ ) ;
486
+ const update_idx = reference . path . findIndex ( ( el ) => el . type === 'UpdateExpression' ) ;
487
+ const labeled_idx = reference . path . findIndex (
488
+ ( el ) => el . type === 'LabeledStatement' && el . label . name === '$'
489
+ ) ;
490
+
491
+ if ( assignment_idx !== - 1 && labeled_idx !== - 1 ) {
492
+ if ( assignment_in_labeled ) return false ;
493
+ assignment_in_labeled = /** @type {AssignmentExpression } */ (
494
+ reference . path [ assignment_idx ]
495
+ ) ;
496
+ labeled_statement = /** @type {LabeledStatement } */ ( reference . path [ labeled_idx ] ) ;
497
+ }
498
+
499
+ return (
500
+ update_idx === - 1 &&
501
+ ( declaration_idx !== - 1 ||
502
+ ( labeled_idx !== - 1 && assignment_idx !== - 1 ) ||
503
+ ( labeled_idx === - 1 && assignment_idx === - 1 ) )
504
+ ) ;
505
+ } )
463
506
) ;
507
+
508
+ const labeled_has_single_assignment =
509
+ labeled_statement ?. body . type === 'BlockStatement' &&
510
+ labeled_statement . body . body . length === 1 ;
511
+
512
+ if (
513
+ possible_derived &&
514
+ assignment_in_labeled &&
515
+ labeled_statement &&
516
+ labeled_has_single_assignment
517
+ ) {
518
+ state . str . appendRight (
519
+ /** @type {number } */ ( declarator . id . typeAnnotation ?. end ?? declarator . id . end ) ,
520
+ ' = $derived('
521
+ ) ;
522
+ visit ( assignment_in_labeled . right ) ;
523
+ state . str . appendRight (
524
+ /** @type {number } */ ( declarator . id . typeAnnotation ?. end ?? declarator . id . end ) ,
525
+ state . str
526
+ . snip (
527
+ /** @type {number } */ ( assignment_in_labeled . right . start ) ,
528
+ /** @type {number } */ ( assignment_in_labeled . right . end )
529
+ )
530
+ . toString ( )
531
+ ) ;
532
+ state . str . appendRight (
533
+ /** @type {number } */ ( declarator . id . typeAnnotation ?. end ?? declarator . id . end ) ,
534
+ ')'
535
+ ) ;
536
+ state . derived_labeled_statements . add ( labeled_statement ) ;
537
+ } else {
538
+ state . str . prependLeft (
539
+ /** @type {number } */ ( declarator . id . typeAnnotation ?. end ?? declarator . id . end ) ,
540
+ ' = $state()'
541
+ ) ;
542
+ }
464
543
}
465
544
}
466
545
@@ -491,6 +570,7 @@ const instance_script = {
491
570
if ( state . analysis . runes ) return ;
492
571
if ( path . length > 1 ) return ;
493
572
if ( node . label . name !== '$' ) return ;
573
+ if ( state . derived_labeled_statements . has ( node ) ) return ;
494
574
495
575
next ( ) ;
496
576
0 commit comments