ripple 0.2.76 → 0.2.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,29 +4,31 @@ import { print } from 'esrap';
4
4
  import tsx from 'esrap/languages/tsx';
5
5
  import * as b from '../../../../utils/builders.js';
6
6
  import {
7
- IS_CONTROLLED,
8
- IS_INDEXED,
9
- TEMPLATE_FRAGMENT,
10
- TEMPLATE_SVG_NAMESPACE,
11
- TEMPLATE_MATHML_NAMESPACE,
7
+ IS_CONTROLLED,
8
+ IS_INDEXED,
9
+ TEMPLATE_FRAGMENT,
10
+ TEMPLATE_SVG_NAMESPACE,
11
+ TEMPLATE_MATHML_NAMESPACE,
12
12
  } from '../../../../constants.js';
13
13
  import { sanitize_template_string } from '../../../../utils/sanitize_template_string.js';
14
14
  import {
15
- build_hoisted_params,
16
- is_inside_component,
17
- build_assignment,
18
- visit_assignment_expression,
19
- escape_html,
20
- is_boolean_attribute,
21
- is_dom_property,
22
- is_declared_function_within_component,
23
- is_inside_call_expression,
24
- is_value_static,
25
- is_void_element,
26
- is_component_level_function,
27
- is_element_dom_element,
28
- is_top_level_await,
29
- is_ripple_track_call,
15
+ build_hoisted_params,
16
+ is_inside_component,
17
+ build_assignment,
18
+ visit_assignment_expression,
19
+ escape_html,
20
+ is_boolean_attribute,
21
+ is_dom_property,
22
+ is_declared_function_within_component,
23
+ is_inside_call_expression,
24
+ is_value_static,
25
+ is_void_element,
26
+ is_component_level_function,
27
+ is_element_dom_element,
28
+ is_top_level_await,
29
+ is_ripple_track_call,
30
+ normalize_children,
31
+ build_getter,
30
32
  } from '../../../utils.js';
31
33
  import is_reference from 'is-reference';
32
34
  import { object } from '../../../../utils/ast.js';
@@ -34,1686 +36,1632 @@ import { render_stylesheets } from '../stylesheet.js';
34
36
  import { is_event_attribute, is_passive_event } from '../../../../utils/events.js';
35
37
 
36
38
  function add_ripple_internal_import(context) {
37
- if (!context.state.to_ts) {
38
- if (!context.state.imports.has(`import * as _$_ from 'ripple/internal/client'`)) {
39
- context.state.imports.add(`import * as _$_ from 'ripple/internal/client'`);
40
- }
41
- }
39
+ if (!context.state.to_ts) {
40
+ if (!context.state.imports.has(`import * as _$_ from 'ripple/internal/client'`)) {
41
+ context.state.imports.add(`import * as _$_ from 'ripple/internal/client'`);
42
+ }
43
+ }
42
44
  }
43
45
 
44
46
  function visit_function(node, context) {
45
- if (context.state.to_ts) {
46
- context.next(context.state);
47
- return;
48
- }
49
- const metadata = node.metadata;
50
- const state = context.state;
51
-
52
- delete node.returnType;
53
-
54
- for (const param of node.params) {
55
- delete param.typeAnnotation;
56
- }
57
-
58
- if (metadata?.hoisted === true) {
59
- const params = build_hoisted_params(node, context);
60
-
61
- return /** @type {FunctionExpression} */ ({
62
- ...node,
63
- params,
64
- body: context.visit(node.body, state),
65
- });
66
- }
67
-
68
- let body = context.visit(node.body, {
69
- ...state,
70
- // we are new context so tracking no longer applies
71
- metadata: { ...state.metadata, tracked: false },
72
- });
73
-
74
- if (metadata?.tracked === true) {
75
- const new_body = [];
76
-
77
- if (!is_inside_component(context, true) && is_component_level_function(context)) {
78
- add_ripple_internal_import(context);
79
- new_body.push(b.var('__block', b.call('_$_.scope')));
80
- }
81
- if (body.type === 'BlockStatement') {
82
- new_body.push(...body.body);
83
- }
84
-
85
- return /** @type {FunctionExpression} */ ({
86
- ...node,
87
- params: node.params.map((param) => context.visit(param, state)),
88
- body: body.type === 'BlockStatement' ? { ...body, body: new_body } : body,
89
- });
90
- }
91
-
92
- context.next(state);
93
- }
94
-
95
- function build_getter(node, context) {
96
- const state = context.state;
97
-
98
- for (let i = context.path.length - 1; i >= 0; i -= 1) {
99
- const binding = state.scope.get(node.name);
100
- const transform = binding?.transform;
101
-
102
- // don't transform the declaration itself
103
- if (node !== binding?.node) {
104
- const read_fn = transform?.read;
105
-
106
- if (read_fn) {
107
- add_ripple_internal_import(context);
108
-
109
- return read_fn(node, context.state?.metadata?.spread, context.visit);
110
- }
111
- }
112
- }
113
-
114
- return node;
47
+ if (context.state.to_ts) {
48
+ context.next(context.state);
49
+ return;
50
+ }
51
+ const metadata = node.metadata;
52
+ const state = context.state;
53
+
54
+ delete node.returnType;
55
+
56
+ for (const param of node.params) {
57
+ delete param.typeAnnotation;
58
+ }
59
+
60
+ if (metadata?.hoisted === true) {
61
+ const params = build_hoisted_params(node, context);
62
+
63
+ return /** @type {FunctionExpression} */ ({
64
+ ...node,
65
+ params,
66
+ body: context.visit(node.body, state),
67
+ });
68
+ }
69
+
70
+ let body = context.visit(node.body, {
71
+ ...state,
72
+ // we are new context so tracking no longer applies
73
+ metadata: { ...state.metadata, tracked: false },
74
+ });
75
+
76
+ if (metadata?.tracked === true) {
77
+ const new_body = [];
78
+
79
+ if (!is_inside_component(context, true) && is_component_level_function(context)) {
80
+ add_ripple_internal_import(context);
81
+ new_body.push(b.var('__block', b.call('_$_.scope')));
82
+ }
83
+ if (body.type === 'BlockStatement') {
84
+ new_body.push(...body.body);
85
+ }
86
+
87
+ return /** @type {FunctionExpression} */ ({
88
+ ...node,
89
+ params: node.params.map((param) => context.visit(param, state)),
90
+ body: body.type === 'BlockStatement' ? { ...body, body: new_body } : body,
91
+ });
92
+ }
93
+
94
+ context.next(state);
115
95
  }
116
96
 
117
97
  function determine_namespace_for_children(element_name, current_namespace) {
118
- if (element_name === 'foreignObject') {
119
- return 'html';
120
- }
98
+ if (element_name === 'foreignObject') {
99
+ return 'html';
100
+ }
121
101
 
122
- if (element_name === 'svg') {
123
- return 'svg';
124
- }
102
+ if (element_name === 'svg') {
103
+ return 'svg';
104
+ }
125
105
 
126
- if (element_name === 'math') {
127
- return 'mathml';
128
- }
106
+ if (element_name === 'math') {
107
+ return 'mathml';
108
+ }
129
109
 
130
- return current_namespace;
110
+ return current_namespace;
131
111
  }
132
112
 
133
113
  const visitors = {
134
- _: function set_scope(node, { next, state }) {
135
- const scope = state.scopes.get(node);
136
-
137
- if (scope && scope !== state.scope) {
138
- return next({ ...state, scope });
139
- } else {
140
- return next();
141
- }
142
- },
143
-
144
- Identifier(node, context) {
145
- const parent = /** @type {Node} */ (context.path.at(-1));
146
-
147
- if (is_reference(node, parent)) {
148
- if (context.state.to_ts) {
149
- if (node.tracked) {
150
- return b.member(node, b.literal('#v'), true);
151
- }
152
- } else {
153
- const binding = context.state.scope.get(node.name);
154
- if (
155
- (context.state.metadata?.tracking === false ||
156
- (parent.type !== 'AssignmentExpression' && parent.type !== 'UpdateExpression')) &&
157
- (node.tracked ||
158
- binding?.kind === 'prop' ||
159
- binding?.kind === 'index' ||
160
- binding?.kind === 'prop_fallback') &&
161
- binding?.node !== node
162
- ) {
163
- if (context.state.metadata?.tracking === false) {
164
- context.state.metadata.tracking = true;
165
- }
166
- if (node.tracked) {
167
- return b.call('_$_.get', build_getter(node, context));
168
- }
169
- }
170
-
171
- return build_getter(node, context);
172
- }
173
- }
174
- },
175
-
176
- ImportDeclaration(node, context) {
177
- if (!context.state.to_ts && node.importKind === 'type') {
178
- return b.empty;
179
- }
180
-
181
- return {
182
- ...node,
183
- specifiers: node.specifiers
184
- .filter((spec) => spec.importKind !== 'type')
185
- .map((spec) => context.visit(spec)),
186
- };
187
- },
188
-
189
- CallExpression(node, context) {
190
- const callee = node.callee;
191
- const parent = context.path.at(-1);
192
-
193
- if (context.state.metadata?.tracking === false) {
194
- context.state.metadata.tracking = true;
195
- }
196
-
197
- if (!context.state.to_ts && is_ripple_track_call(callee, context)) {
198
- if (node.arguments.length === 0) {
199
- node.arguments.push(b.void0, b.void0);
200
- } else if (node.arguments.length === 1) {
201
- node.arguments.push(b.void0);
202
- }
203
- return {
204
- ...node,
205
- arguments: [...node.arguments.map((arg) => context.visit(arg)), b.id('__block')],
206
- };
207
- }
208
-
209
- if (
210
- !is_inside_component(context, true) ||
211
- context.state.to_ts ||
212
- (parent?.type === 'MemberExpression' && parent.property === node) ||
213
- is_inside_call_expression(context) ||
214
- !context.path.some((node) => node.type === 'Component') ||
215
- is_declared_function_within_component(callee, context)
216
- ) {
217
- return context.next();
218
- }
219
-
220
- // Handle array methods that access the array
221
- if (callee.type === 'MemberExpression') {
222
- const property = callee.property;
223
-
224
- if (callee.computed) {
225
- return b.call(
226
- '_$_.with_scope',
227
- b.id('__block'),
228
- b.thunk(
229
- b.call(
230
- '_$_.call_property',
231
- context.visit(callee.object),
232
- context.visit(property),
233
- callee.optional ? b.true : undefined,
234
- node.optional ? b.true : undefined,
235
- ...node.arguments.map((arg) => context.visit(arg)),
236
- ),
237
- ),
238
- );
239
- }
240
- }
241
-
242
- return b.call(
243
- '_$_.with_scope',
244
- b.id('__block'),
245
- b.thunk(
246
- {
247
- ...node,
248
- callee: context.visit(callee),
249
- arguments: node.arguments.map((arg) => context.visit(arg)),
250
- },
251
- context.state.metadata?.await ?? false,
252
- ),
253
- );
254
- },
255
-
256
- TSTypeAliasDeclaration(_, context) {
257
- if (!context.state.to_ts) {
258
- return b.empty;
259
- }
260
- context.next();
261
- },
262
-
263
- TSInterfaceDeclaration(_, context) {
264
- if (!context.state.to_ts) {
265
- return b.empty;
266
- }
267
- context.next();
268
- },
269
-
270
- TSMappedType(_, context) {
271
- if (!context.state.to_ts) {
272
- return b.empty;
273
- }
274
- context.next();
275
- },
276
-
277
- NewExpression(node, context) {
278
- const callee = node.callee;
279
- const parent = context.path.at(-1);
280
-
281
- if (context.state.metadata?.tracking === false) {
282
- context.state.metadata.tracking = true;
283
- }
284
-
285
- if (
286
- context.state.to_ts ||
287
- !is_inside_component(context, true) ||
288
- is_inside_call_expression(context) ||
289
- is_value_static(node)
290
- ) {
291
- return context.next();
292
- }
293
-
294
- return b.call(
295
- '_$_.with_scope',
296
- b.id('__block'),
297
- b.thunk({
298
- ...node,
299
- callee: context.visit(callee),
300
- arguments: node.arguments.map((arg) => context.visit(arg)),
301
- }),
302
- );
303
- },
304
-
305
- MemberExpression(node, context) {
306
- const parent = context.path.at(-1);
307
-
308
- if (context.state.metadata?.tracking === false) {
309
- context.state.metadata.tracking = true;
310
- }
311
-
312
- if (node.property.type === 'Identifier' && node.property.tracked) {
313
- add_ripple_internal_import(context);
314
-
315
- return b.call(
316
- '_$_.get_property',
317
- context.visit(node.object),
318
- node.computed ? context.visit(node.property) : b.literal(node.property.name),
319
- node.optional ? b.true : undefined,
320
- );
321
- }
322
-
323
- if (node.object.type === 'MemberExpression' && node.object.optional) {
324
- const metadata = { tracking: false, await: false };
325
-
326
- const object = context.visit(node.object, { ...context.state, metadata });
327
-
328
- if (metadata.tracking) {
329
- if (context.state.metadata?.tracking === false) {
330
- context.state.metadata.tracking = true;
331
- }
332
-
333
- return {
334
- ...node,
335
- optional: true,
336
- object,
337
- property: context.visit(node.property),
338
- };
339
- }
340
- if (metadata.await) {
341
- if (context.state.metadata?.await === false) {
342
- context.state.metadata.await = true;
343
- }
344
- }
345
- } else {
346
- context.next();
347
- }
348
- },
349
-
350
- PropertyDefinition(node, context) {
351
- if (!context.state.to_ts) {
352
- delete node.typeAnnotation;
353
- }
354
- return context.next();
355
- },
356
-
357
- VariableDeclaration(node, context) {
358
- const declarations = [];
359
-
360
- for (const declarator of node.declarations) {
361
- const metadata = declarator.metadata;
362
-
363
- if (declarator.id.type === 'Identifier') {
364
- const binding = context.state.scope.get(declarator.id.name);
365
-
366
- if (!context.state.to_ts) {
367
- delete declarator.id.typeAnnotation;
368
- }
369
-
370
- if (binding !== null && binding.kind === 'tracked') {
371
- let expression;
372
-
373
- if (context.state.to_ts) {
374
- // TypeScript mode: lighter transformation
375
- if (metadata.tracking && !metadata.await) {
376
- expression = b.call(
377
- '_$_.derived',
378
- b.thunk(context.visit(declarator.init)),
379
- b.id('__block'),
380
- );
381
- } else {
382
- expression = b.call(
383
- '_$_.tracked',
384
- declarator.init === null ? undefined : context.visit(declarator.init),
385
- b.id('__block'),
386
- );
387
- }
388
- } else {
389
- debugger;
390
- // Runtime mode: full transformation
391
- if (metadata.tracking && metadata.await) {
392
- expression = b.call(
393
- b.await(
394
- b.call(
395
- '_$_.resume_context',
396
- b.call(
397
- '_$_.async_computed',
398
- b.thunk(context.visit(declarator.init), true),
399
- b.id('__block'),
400
- ),
401
- ),
402
- ),
403
- );
404
- } else if (metadata.tracking && !metadata.await) {
405
- expression = b.call(
406
- '_$_.derived',
407
- b.thunk(context.visit(declarator.init)),
408
- b.id('__block'),
409
- );
410
- } else {
411
- expression = b.call(
412
- '_$_.tracked',
413
- declarator.init === null ? undefined : context.visit(declarator.init),
414
- b.id('__block'),
415
- );
416
- }
417
- }
418
-
419
- declarations.push(b.declarator(declarator.id, expression));
420
- } else {
421
- declarations.push(context.visit(declarator));
422
- }
423
- } else {
424
- if (!context.state.to_ts) {
425
- delete declarator.id.typeAnnotation;
426
- }
427
-
428
- declarations.push(context.visit(declarator));
429
- }
430
- }
431
-
432
- return { ...node, declarations };
433
- },
434
-
435
- FunctionDeclaration(node, context) {
436
- return visit_function(node, context);
437
- },
438
- ArrowFunctionExpression(node, context) {
439
- return visit_function(node, context);
440
- },
441
- FunctionExpression(node, context) {
442
- return visit_function(node, context);
443
- },
444
-
445
- Element(node, context) {
446
- const { state, visit } = context;
447
-
448
- const is_dom_element = is_element_dom_element(node);
449
- const is_spreading = node.attributes.some((attr) => attr.type === 'SpreadAttribute');
450
- const spread_attributes = is_spreading ? [] : null;
451
-
452
- const child_namespace = is_dom_element
453
- ? determine_namespace_for_children(node.id.name, state.namespace)
454
- : state.namespace;
455
-
456
- const handle_static_attr = (name, value) => {
457
- const attr_value = b.literal(
458
- ` ${name}${
459
- is_boolean_attribute(name) && value === true
460
- ? ''
461
- : `="${value === true ? '' : escape_html(value, true)}"`
462
- }`,
463
- );
464
-
465
- if (is_spreading) {
466
- // For spread attributes, store just the actual value, not the full attribute string
467
- const actual_value =
468
- is_boolean_attribute(name) && value === true
469
- ? b.literal(true)
470
- : b.literal(value === true ? '' : value);
471
- spread_attributes.push(b.prop('init', b.literal(name), actual_value));
472
- } else {
473
- state.template.push(attr_value);
474
- }
475
- };
476
-
477
- if (is_dom_element) {
478
- let class_attribute = null;
479
- const local_updates = [];
480
- const is_void = is_void_element(node.id.name);
481
-
482
- state.template.push(`<${node.id.name}`);
483
-
484
- for (const attr of node.attributes) {
485
- if (attr.type === 'Attribute') {
486
- if (attr.name.type === 'Identifier') {
487
- const name = attr.name.name;
488
-
489
- if (attr.value === null) {
490
- handle_static_attr(name, true);
491
- continue;
492
- }
493
-
494
- if (attr.value.type === 'Literal' && name !== 'class') {
495
- handle_static_attr(name, attr.value.value);
496
- continue;
497
- }
498
-
499
- if (name === 'class' || name === '$class') {
500
- class_attribute = attr;
501
-
502
- continue;
503
- }
504
-
505
- if (name === 'value' || name === '$value') {
506
- const id = state.flush_node();
507
- const metadata = { tracking: false, await: false };
508
- const expression = visit(attr.value, { ...state, metadata });
509
-
510
- if (name === '$value' || metadata.tracking) {
511
- local_updates.push(b.stmt(b.call('_$_.set_value', id, expression)));
512
- } else {
513
- state.init.push(b.stmt(b.call('_$_.set_value', id, expression)));
514
- }
515
-
516
- continue;
517
- }
518
-
519
- if (name === 'checked' || name === '$checked') {
520
- const id = state.flush_node();
521
- const metadata = { tracking: false, await: false };
522
- const expression = visit(attr.value, { ...state, metadata });
523
-
524
- if (name === '$checked' || metadata.tracking) {
525
- local_updates.push(b.stmt(b.call('_$_.set_checked', id, expression)));
526
- } else {
527
- state.init.push(b.stmt(b.call('_$_.set_checked', id, expression)));
528
- }
529
- continue;
530
- }
531
-
532
- if (name === 'selected' || name === '$selected') {
533
- const id = state.flush_node();
534
- const metadata = { tracking: false, await: false };
535
- const expression = visit(attr.value, { ...state, metadata });
536
-
537
- if (name === '$selected' || metadata.tracking) {
538
- local_updates.push(b.stmt(b.call('_$_.set_selected', id, expression)));
539
- } else {
540
- state.init.push(b.stmt(b.call('_$_.set_selected', id, expression)));
541
- }
542
- continue;
543
- }
544
-
545
- if (is_event_attribute(name)) {
546
- let capture = name.endsWith('Capture');
547
- let event_name = capture
548
- ? name.slice(2, -7).toLowerCase()
549
- : name.slice(2).toLowerCase();
550
- let handler = visit(attr.value, state);
551
-
552
- if (attr.metadata?.delegated) {
553
- let delegated_assignment;
554
-
555
- if (!state.events.has(event_name)) {
556
- state.events.add(event_name);
557
- }
558
-
559
- // Hoist function if we can, otherwise we leave the function as is
560
- if (attr.metadata.delegated.hoisted) {
561
- if (attr.metadata.delegated.function === attr.value) {
562
- const func_name = state.scope.root.unique('on_' + event_name);
563
- state.hoisted.push(b.var(func_name, handler));
564
- handler = func_name;
565
- }
566
-
567
- const hoisted_params = /** @type {Expression[]} */ (
568
- attr.metadata.delegated.function.metadata.hoisted_params
569
- );
570
-
571
- const args = [handler, b.id('__block'), ...hoisted_params];
572
- delegated_assignment = b.array(args);
573
- } else if (
574
- handler.type === 'Identifier' &&
575
- is_declared_function_within_component(handler, context)
576
- ) {
577
- delegated_assignment = handler;
578
- } else {
579
- delegated_assignment = b.array([handler, b.id('__block')]);
580
- }
581
- const id = state.flush_node();
582
-
583
- state.init.push(
584
- b.stmt(b.assignment('=', b.member(id, '__' + event_name), delegated_assignment)),
585
- );
586
- } else {
587
- const passive = is_passive_event(event_name);
588
- const id = state.flush_node();
589
-
590
- state.init.push(
591
- b.stmt(
592
- b.call(
593
- '_$_.event',
594
- b.literal(event_name),
595
- id,
596
- handler,
597
- capture && b.true,
598
- passive === undefined ? undefined : b.literal(passive),
599
- ),
600
- ),
601
- );
602
- }
603
-
604
- continue;
605
- }
606
-
607
- const metadata = { tracking: false, await: false };
608
- const expression = visit(attr.value, { ...state, metadata });
609
- // All other attributes
610
- if (metadata.tracking) {
611
- const attribute = name;
612
- const id = state.flush_node();
613
-
614
- if (is_dom_property(attribute)) {
615
- local_updates.push(b.stmt(b.assignment('=', b.member(id, attribute), expression)));
616
- } else {
617
- local_updates.push(
618
- b.stmt(b.call('_$_.set_attribute', id, b.literal(attribute), expression)),
619
- );
620
- }
621
- } else {
622
- const id = state.flush_node();
623
-
624
- if (is_dom_property(name)) {
625
- state.init.push(b.stmt(b.assignment('=', b.member(id, name), expression)));
626
- } else {
627
- state.init.push(
628
- b.stmt(b.call('_$_.set_attribute', id, b.literal(name), expression)),
629
- );
630
- }
631
- }
632
- }
633
- } else if (attr.type === 'SpreadAttribute') {
634
- spread_attributes.push(b.spread(visit(attr.argument, state)));
635
- } else if (attr.type === 'RefAttribute') {
636
- const id = state.flush_node();
637
- state.init.push(b.stmt(b.call('_$_.ref', id, b.thunk(visit(attr.argument, state)))));
638
- }
639
- }
640
-
641
- if (class_attribute !== null) {
642
- if (class_attribute.value.type === 'Literal') {
643
- let value = class_attribute.value.value;
644
-
645
- if (node.metadata.scoped && state.component.css) {
646
- value = `${state.component.css.hash} ${value}`;
647
- }
648
-
649
- handle_static_attr(class_attribute.name.name, value);
650
- } else {
651
- const id = state.flush_node();
652
- const metadata = { tracking: false, await: false };
653
- let expression = visit(class_attribute.value, { ...state, metadata });
654
-
655
- if (node.metadata.scoped && state.component.css) {
656
- expression = b.binary('+', b.literal(state.component.css.hash + ' '), expression);
657
- }
658
- const is_html = context.state.metadata.namespace === 'html' && node.id.name !== 'svg';
659
-
660
- if (class_attribute.name.name === '$class' || metadata.tracking) {
661
- local_updates.push(
662
- b.stmt(b.call('_$_.set_class', id, expression, undefined, b.literal(is_html))),
663
- );
664
- } else {
665
- state.init.push(
666
- b.stmt(b.call('_$_.set_class', id, expression, undefined, b.literal(is_html))),
667
- );
668
- }
669
- }
670
- } else if (node.metadata.scoped && state.component.css) {
671
- const value = state.component.css.hash;
672
-
673
- handle_static_attr(is_spreading ? '#class' : 'class', value);
674
- }
675
-
676
- state.template.push('>');
677
-
678
- if (spread_attributes !== null && spread_attributes.length > 0) {
679
- const id = state.flush_node();
680
- state.init.push(
681
- b.stmt(b.call('_$_.render_spread', id, b.thunk(b.object(spread_attributes)))),
682
- );
683
- }
684
-
685
- const init = [];
686
- const update = [];
687
-
688
- if (!is_void) {
689
- transform_children(node.children, node, {
690
- visit,
691
- state: { ...state, init, update, namespace: child_namespace },
692
- root: false,
693
- });
694
- state.template.push(`</${node.id.name}>`);
695
- }
696
-
697
- update.push(...local_updates);
698
-
699
- if (init.length > 0) {
700
- state.init.push(b.block(init));
701
- }
702
-
703
- if (update.length > 0) {
704
- if (state.scope.parent.declarations.size > 0) {
705
- state.init.push(b.stmt(b.call('_$_.render', b.thunk(b.block(update), !!update.async))));
706
- } else {
707
- state.update.push(...update);
708
- }
709
- }
710
- } else {
711
- const id = state.flush_node();
712
-
713
- state.template.push('<!>');
714
-
715
- const is_spreading = node.attributes.some((attr) => attr.type === 'SpreadAttribute');
716
- const props = [];
717
- let children_prop = null;
718
-
719
- for (const attr of node.attributes) {
720
- if (attr.type === 'Attribute') {
721
- if (attr.name.type === 'Identifier') {
722
- const metadata = { tracking: false, await: false };
723
- let property = visit(attr.value, { ...state, metadata });
724
-
725
- if (metadata.tracking || attr.name.tracked) {
726
- if (attr.name.name === 'children') {
727
- children_prop = b.thunk(property);
728
- continue;
729
- }
730
-
731
- props.push(
732
- b.prop('get', attr.name, b.function(null, [], b.block([b.return(property)]))),
733
- );
734
- } else {
735
- props.push(b.prop('init', attr.name, property));
736
- }
737
- } else {
738
- props.push(b.prop('init', attr.name, visit(attr.value, state)));
739
- }
740
- } else if (attr.type === 'SpreadAttribute') {
741
- props.push(
742
- b.spread(
743
- visit(attr.argument, { ...state, metadata: { ...state.metadata, spread: true } }),
744
- ),
745
- );
746
- } else if (attr.type === 'RefAttribute') {
747
- props.push(b.prop('init', b.call('_$_.ref_prop'), visit(attr.argument, state), true));
748
- } else if (attr.type === 'AccessorAttribute') {
749
- let get_expr;
750
-
751
- if (
752
- attr.get.type === 'FunctionExpression' ||
753
- attr.get.type === 'ArrowFunctionExpression'
754
- ) {
755
- get_expr = context.state.scope.generate(attr.name.name + '_get');
756
-
757
- state.init.push(b.const(get_expr, visit(attr.get, state)));
758
- } else {
759
- get_expr = visit(attr.get, state);
760
- }
761
-
762
- props.push(
763
- b.prop('get', attr.name, b.function(null, [], b.block([b.return(b.call(get_expr))]))),
764
- );
765
-
766
- if (attr.set) {
767
- let set_expr;
768
-
769
- if (
770
- attr.set.type === 'FunctionExpression' ||
771
- attr.set.type === 'ArrowFunctionExpression'
772
- ) {
773
- set_expr = context.state.scope.generate(attr.name.name + '_set');
774
-
775
- state.init.push(b.const(set_expr, visit(attr.set, state)));
776
- } else {
777
- set_expr = visit(attr.set, state);
778
- }
779
-
780
- props.push(
781
- b.prop(
782
- 'set',
783
- attr.name,
784
- b.function(
785
- null,
786
- [b.id('__value')],
787
- b.block([b.return(b.call(set_expr, b.id('__value')))]),
788
- ),
789
- ),
790
- );
791
- }
792
- } else {
793
- throw new Error('TODO');
794
- }
795
- }
796
-
797
- const children_filtered = [];
798
-
799
- for (const child of node.children) {
800
- if (child.type === 'Component') {
801
- const id = child.id;
802
- props.push(b.prop('init', id, visit(child, { ...state, namespace: child_namespace })));
803
- } else {
804
- children_filtered.push(child);
805
- }
806
- }
807
-
808
- if (children_filtered.length > 0) {
809
- const component_scope = context.state.scopes.get(node);
810
- const children = visit(b.component(b.id('children'), [], children_filtered), {
811
- ...context.state,
812
- scope: component_scope,
813
- namespace: child_namespace,
814
- });
815
-
816
- if (children_prop) {
817
- children_prop.body = b.logical('??', children_prop.body, children);
818
- } else {
819
- props.push(b.prop('init', b.id('children'), children));
820
- }
821
- }
822
-
823
- if (is_spreading) {
824
- state.init.push(
825
- b.stmt(
826
- b.call(
827
- node.id,
828
- id,
829
- b.call('_$_.spread_props', b.thunk(b.object(props)), b.id('__block')),
830
- b.id('_$_.active_block'),
831
- ),
832
- ),
833
- );
834
- } else {
835
- state.init.push(
836
- b.stmt(b.call(visit(node.id, state), id, b.object(props), b.id('_$_.active_block'))),
837
- );
838
- }
839
- }
840
- },
841
-
842
- Fragment(node, context) {
843
- if (!context.state.to_ts) {
844
- add_ripple_internal_import(context);
845
- }
846
-
847
- const metadata = { await: false };
848
-
849
- const body_statements = transform_body(node.body, {
850
- ...context,
851
- state: { ...context.state, component: node, metadata },
852
- });
853
-
854
- return b.function(
855
- node.id,
856
- [b.id('__anchor'), ...node.params.map((param) => context.visit(param, context.state))],
857
- b.block(
858
- metadata.await
859
- ? [b.stmt(b.call('_$_.async', b.thunk(b.block(body_statements), true)))]
860
- : body_statements,
861
- ),
862
- );
863
- },
864
-
865
- Component(node, context) {
866
- let prop_statements;
867
-
868
- add_ripple_internal_import(context);
869
-
870
- const metadata = { await: false };
871
-
872
- if (context.state.to_ts) {
873
- const body_statements = [
874
- ...transform_body(node.body, {
875
- ...context,
876
- state: { ...context.state, component: node, metadata },
877
- }),
878
- ];
879
-
880
- return b.function(node.id, node.params, b.block(body_statements));
881
- }
882
-
883
- let props = b.id('__props');
884
-
885
- if (node.params.length > 0) {
886
- let props_param = node.params[0];
887
-
888
- if (props_param.type === 'Identifier') {
889
- delete props_param.typeAnnotation;
890
- props = props_param;
891
- } else if (props_param.type === 'ObjectPattern') {
892
- delete props_param.typeAnnotation;
893
- }
894
- }
895
-
896
- const body_statements = [
897
- b.stmt(b.call('_$_.push_component')),
898
- ...transform_body(node.body, {
899
- ...context,
900
- state: { ...context.state, component: node, metadata },
901
- }),
902
- b.stmt(b.call('_$_.pop_component')),
903
- ];
904
-
905
- if (node.css !== null && node.css) {
906
- context.state.stylesheets.push(node.css);
907
- }
908
-
909
- return b.function(
910
- node.id,
911
- node.params.length > 0
912
- ? [b.id('__anchor'), props, b.id('__block')]
913
- : [b.id('__anchor'), b.id('_'), b.id('__block')],
914
- b.block([
915
- ...(prop_statements ?? []),
916
- ...(metadata.await
917
- ? [b.stmt(b.call('_$_.async', b.thunk(b.block(body_statements), true)))]
918
- : body_statements),
919
- ]),
920
- );
921
- },
922
-
923
- AssignmentExpression(node, context) {
924
- if (context.state.to_ts) {
925
- return context.next();
926
- }
927
-
928
- const left = node.left;
929
-
930
- if (
931
- left.type === 'MemberExpression' &&
932
- left.property.type === 'Identifier' &&
933
- left.property.tracked
934
- ) {
935
- add_ripple_internal_import(context);
936
- const operator = node.operator;
937
- const right = node.right;
938
-
939
- if (operator !== '=') {
940
- context.state.metadata.tracking = true;
941
- }
942
-
943
- return b.call(
944
- '_$_.set_property',
945
- context.visit(left.object, { ...context.state, metadata: { tracking: false } }),
946
- left.computed ? context.visit(left.property) : b.literal(left.property.name),
947
- operator === '='
948
- ? context.visit(right)
949
- : b.binary(
950
- operator === '+=' ? '+' : operator === '-=' ? '-' : operator === '*=' ? '*' : '/',
951
- /** @type {Pattern} */ (context.visit(left)),
952
- /** @type {Expression} */ (context.visit(right)),
953
- ),
954
- b.id('__block'),
955
- );
956
- }
957
-
958
- if (left.type === 'Identifier' && left.tracked) {
959
- add_ripple_internal_import(context);
960
- const operator = node.operator;
961
- const right = node.right;
962
-
963
- return b.call(
964
- '_$_.set',
965
- context.visit(left, { ...context.state, metadata: { tracking: null } }),
966
- operator === '='
967
- ? context.visit(right)
968
- : b.binary(
969
- operator === '+=' ? '+' : operator === '-=' ? '-' : operator === '*=' ? '*' : '/',
970
- /** @type {Pattern} */ (
971
- context.visit(left, { ...context.state, metadata: { tracking: false } })
972
- ),
973
- /** @type {Expression} */ (context.visit(right)),
974
- ),
975
- b.id('__block'),
976
- );
977
- }
978
-
979
- return visit_assignment_expression(node, context, build_assignment) ?? context.next();
980
- },
981
-
982
- UpdateExpression(node, context) {
983
- if (context.state.to_ts) {
984
- context.next();
985
- return;
986
- }
987
- const argument = node.argument;
988
-
989
- if (
990
- argument.type === 'MemberExpression' &&
991
- argument.property.type === 'Identifier' &&
992
- argument.property.tracked
993
- ) {
994
- add_ripple_internal_import(context);
995
- context.state.metadata.tracking = true;
996
-
997
- return b.call(
998
- node.prefix ? '_$_.update_pre_property' : '_$_.update_property',
999
- context.visit(argument.object, { ...context.state, metadata: { tracking: false } }),
1000
- argument.computed ? context.visit(argument.property) : b.literal(argument.property.name),
1001
- b.id('__block'),
1002
- node.operator === '--' ? b.literal(-1) : undefined,
1003
- );
1004
- }
1005
-
1006
- if (argument.type === 'Identifier' && argument.tracked) {
1007
- return b.call(
1008
- node.prefix ? '_$_.update_pre' : '_$_.update',
1009
- context.visit(argument, { ...context.state, metadata: { tracking: null } }),
1010
- b.id('__block'),
1011
- node.operator === '--' ? b.literal(-1) : undefined,
1012
- );
1013
- }
1014
-
1015
- const left = object(argument);
1016
- const binding = context.state.scope.get(left.name);
1017
- const transformers = left && binding?.transform;
1018
-
1019
- if (left === argument) {
1020
- const update_fn = transformers?.update || transformers?.update_tracked;
1021
- if (update_fn) {
1022
- return update_fn(node);
1023
- }
1024
- }
1025
-
1026
- context.next();
1027
- },
1028
-
1029
- ForOfStatement(node, context) {
1030
- if (!is_inside_component(context)) {
1031
- context.next();
1032
- return;
1033
- }
1034
- const is_controlled = node.is_controlled;
1035
- const index = node.index;
1036
- let flags = is_controlled ? IS_CONTROLLED : 0;
1037
-
1038
- if (index !== null) {
1039
- flags |= IS_INDEXED;
1040
- }
1041
-
1042
- // do only if not controller
1043
- if (!is_controlled) {
1044
- context.state.template.push('<!>');
1045
- }
1046
-
1047
- const id = context.state.flush_node(is_controlled);
1048
- const pattern = node.left.declarations[0].id;
1049
- const body_scope = context.state.scopes.get(node.body);
1050
-
1051
- context.state.init.push(
1052
- b.stmt(
1053
- b.call(
1054
- '_$_.for',
1055
- id,
1056
- b.thunk(context.visit(node.right)),
1057
- b.arrow(
1058
- index ? [b.id('__anchor'), pattern, index] : [b.id('__anchor'), pattern],
1059
- b.block(
1060
- transform_body(node.body.body, {
1061
- ...context,
1062
- state: { ...context.state, scope: body_scope, namespace: context.state.namespace },
1063
- }),
1064
- ),
1065
- ),
1066
- b.literal(flags),
1067
- ),
1068
- ),
1069
- );
1070
- },
1071
-
1072
- IfStatement(node, context) {
1073
- if (!is_inside_component(context)) {
1074
- context.next();
1075
- return;
1076
- }
1077
- context.state.template.push('<!>');
1078
-
1079
- const id = context.state.flush_node();
1080
- const statements = [];
1081
-
1082
- const consequent_scope = context.state.scopes.get(node.consequent);
1083
- const consequent = b.block(
1084
- transform_body(node.consequent.body, {
1085
- ...context,
1086
- state: { ...context.state, scope: consequent_scope },
1087
- }),
1088
- );
1089
- const consequent_id = context.state.scope.generate('consequent');
1090
-
1091
- statements.push(b.var(b.id(consequent_id), b.arrow([b.id('__anchor')], consequent)));
1092
-
1093
- let alternate_id;
1094
-
1095
- if (node.alternate !== null) {
1096
- const alternate_scope = context.state.scopes.get(node.alternate) || context.state.scope;
1097
- let alternate_body = node.alternate.body;
1098
- if (node.alternate.type === 'IfStatement') {
1099
- alternate_body = [node.alternate];
1100
- }
1101
- const alternate = b.block(
1102
- transform_body(alternate_body, {
1103
- ...context,
1104
- state: { ...context.state, scope: alternate_scope },
1105
- }),
1106
- );
1107
- alternate_id = context.state.scope.generate('alternate');
1108
- statements.push(b.var(b.id(alternate_id), b.arrow([b.id('__anchor')], alternate)));
1109
- }
1110
-
1111
- statements.push(
1112
- b.stmt(
1113
- b.call(
1114
- '_$_.if',
1115
- id,
1116
- b.arrow(
1117
- [b.id('__render')],
1118
- b.block([
1119
- b.if(
1120
- context.visit(node.test),
1121
- b.stmt(b.call(b.id('__render'), b.id(consequent_id))),
1122
- alternate_id
1123
- ? b.stmt(
1124
- b.call(
1125
- b.id('__render'),
1126
- b.id(alternate_id),
1127
- node.alternate ? b.literal(false) : undefined,
1128
- ),
1129
- )
1130
- : undefined,
1131
- ),
1132
- ]),
1133
- ),
1134
- ),
1135
- ),
1136
- );
1137
-
1138
- context.state.init.push(b.block(statements));
1139
- },
1140
-
1141
- TSAsExpression(node, context) {
1142
- if (!context.state.to_ts) {
1143
- return context.visit(node.expression);
1144
- }
1145
- return context.next();
1146
- },
1147
-
1148
- TryStatement(node, context) {
1149
- if (!is_inside_component(context)) {
1150
- context.next();
1151
- return;
1152
- }
1153
- context.state.template.push('<!>');
1154
-
1155
- const id = context.state.flush_node();
1156
- const metadata = { await: false };
1157
- let body = transform_body(node.block.body, {
1158
- ...context,
1159
- state: { ...context.state, metadata },
1160
- });
1161
-
1162
- if (metadata.pending) {
1163
- body = [b.stmt(b.call('_$_.async', b.thunk(b.block(body), true)))];
1164
- }
1165
-
1166
- context.state.init.push(
1167
- b.stmt(
1168
- b.call(
1169
- '_$_.try',
1170
- id,
1171
- b.arrow([b.id('__anchor')], b.block(body)),
1172
- node.handler === null
1173
- ? b.literal(null)
1174
- : b.arrow(
1175
- [b.id('__anchor'), ...(node.handler.param ? [node.handler.param] : [])],
1176
- b.block(transform_body(node.handler.body.body, context)),
1177
- ),
1178
- node.pending === null
1179
- ? undefined
1180
- : b.arrow([b.id('__anchor')], b.block(transform_body(node.pending.body, context))),
1181
- ),
1182
- ),
1183
- );
1184
- },
1185
-
1186
- AwaitExpression(node, context) {
1187
- if (!is_top_level_await(context) || context.state.to_ts) {
1188
- return context.next();
1189
- }
1190
-
1191
- if (context.state.metadata?.await === false) {
1192
- context.state.metadata.await = true;
1193
- }
1194
-
1195
- return b.call(b.await(b.call('_$_.maybe_tracked', context.visit(node.argument))));
1196
- },
1197
-
1198
- BinaryExpression(node, context) {
1199
- return b.binary(node.operator, context.visit(node.left), context.visit(node.right));
1200
- },
1201
-
1202
- TemplateLiteral(node, context) {
1203
- const parent = context.path.at(-1);
1204
-
1205
- if (node.expressions.length === 0 && parent?.type !== 'TaggedTemplateExpression') {
1206
- return b.literal(node.quasis[0].value.cooked);
1207
- }
1208
-
1209
- const expressions = node.expressions.map((expr) => context.visit(expr));
1210
- return b.template(node.quasis, expressions);
1211
- },
1212
-
1213
- BlockStatement(node, context) {
1214
- const statements = [];
1215
-
1216
- for (const statement of node.body) {
1217
- statements.push(context.visit(statement));
1218
- }
1219
-
1220
- return b.block(statements);
1221
- },
1222
-
1223
- Program(node, context) {
1224
- const statements = [];
1225
-
1226
- for (const statement of node.body) {
1227
- statements.push(context.visit(statement));
1228
- }
1229
-
1230
- return { ...node, body: statements };
1231
- },
114
+ _: function set_scope(node, { next, state }) {
115
+ const scope = state.scopes.get(node);
116
+
117
+ if (scope && scope !== state.scope) {
118
+ return next({ ...state, scope });
119
+ } else {
120
+ return next();
121
+ }
122
+ },
123
+
124
+ Identifier(node, context) {
125
+ const parent = /** @type {Node} */ (context.path.at(-1));
126
+
127
+ if (is_reference(node, parent)) {
128
+ if (context.state.to_ts) {
129
+ if (node.tracked) {
130
+ return b.member(node, b.literal('#v'), true);
131
+ }
132
+ } else {
133
+ const binding = context.state.scope.get(node.name);
134
+ if (
135
+ (context.state.metadata?.tracking === false ||
136
+ (parent.type !== 'AssignmentExpression' && parent.type !== 'UpdateExpression')) &&
137
+ (node.tracked ||
138
+ binding?.kind === 'prop' ||
139
+ binding?.kind === 'index' ||
140
+ binding?.kind === 'prop_fallback') &&
141
+ binding?.node !== node
142
+ ) {
143
+ if (context.state.metadata?.tracking === false) {
144
+ context.state.metadata.tracking = true;
145
+ }
146
+ if (node.tracked) {
147
+ add_ripple_internal_import(context);
148
+ return b.call('_$_.get', build_getter(node, context));
149
+ }
150
+ }
151
+
152
+ add_ripple_internal_import(context);
153
+ return build_getter(node, context);
154
+ }
155
+ }
156
+ },
157
+
158
+ ImportDeclaration(node, context) {
159
+ if (!context.state.to_ts && node.importKind === 'type') {
160
+ return b.empty;
161
+ }
162
+
163
+ return {
164
+ ...node,
165
+ specifiers: node.specifiers
166
+ .filter((spec) => spec.importKind !== 'type')
167
+ .map((spec) => context.visit(spec)),
168
+ };
169
+ },
170
+
171
+ CallExpression(node, context) {
172
+ const callee = node.callee;
173
+ const parent = context.path.at(-1);
174
+
175
+ if (context.state.metadata?.tracking === false) {
176
+ context.state.metadata.tracking = true;
177
+ }
178
+
179
+ if (!context.state.to_ts && is_ripple_track_call(callee, context)) {
180
+ if (node.arguments.length === 0) {
181
+ node.arguments.push(b.void0, b.void0);
182
+ } else if (node.arguments.length === 1) {
183
+ node.arguments.push(b.void0);
184
+ }
185
+ return {
186
+ ...node,
187
+ arguments: [...node.arguments.map((arg) => context.visit(arg)), b.id('__block')],
188
+ };
189
+ }
190
+
191
+ if (
192
+ !is_inside_component(context, true) ||
193
+ context.state.to_ts ||
194
+ (parent?.type === 'MemberExpression' && parent.property === node) ||
195
+ is_inside_call_expression(context) ||
196
+ !context.path.some((node) => node.type === 'Component') ||
197
+ is_declared_function_within_component(callee, context)
198
+ ) {
199
+ return context.next();
200
+ }
201
+
202
+ // Handle array methods that access the array
203
+ if (callee.type === 'MemberExpression') {
204
+ const property = callee.property;
205
+
206
+ if (callee.computed) {
207
+ return b.call(
208
+ '_$_.with_scope',
209
+ b.id('__block'),
210
+ b.thunk(
211
+ b.call(
212
+ '_$_.call_property',
213
+ context.visit(callee.object),
214
+ context.visit(property),
215
+ callee.optional ? b.true : undefined,
216
+ node.optional ? b.true : undefined,
217
+ ...node.arguments.map((arg) => context.visit(arg)),
218
+ ),
219
+ ),
220
+ );
221
+ }
222
+ }
223
+
224
+ return b.call(
225
+ '_$_.with_scope',
226
+ b.id('__block'),
227
+ b.thunk(
228
+ {
229
+ ...node,
230
+ callee: context.visit(callee),
231
+ arguments: node.arguments.map((arg) => context.visit(arg)),
232
+ },
233
+ context.state.metadata?.await ?? false,
234
+ ),
235
+ );
236
+ },
237
+
238
+ TSTypeAliasDeclaration(_, context) {
239
+ if (!context.state.to_ts) {
240
+ return b.empty;
241
+ }
242
+ context.next();
243
+ },
244
+
245
+ TSInterfaceDeclaration(_, context) {
246
+ if (!context.state.to_ts) {
247
+ return b.empty;
248
+ }
249
+ context.next();
250
+ },
251
+
252
+ TSMappedType(_, context) {
253
+ if (!context.state.to_ts) {
254
+ return b.empty;
255
+ }
256
+ context.next();
257
+ },
258
+
259
+ NewExpression(node, context) {
260
+ const callee = node.callee;
261
+ const parent = context.path.at(-1);
262
+
263
+ if (context.state.metadata?.tracking === false) {
264
+ context.state.metadata.tracking = true;
265
+ }
266
+
267
+ if (
268
+ context.state.to_ts ||
269
+ !is_inside_component(context, true) ||
270
+ is_inside_call_expression(context) ||
271
+ is_value_static(node)
272
+ ) {
273
+ return context.next();
274
+ }
275
+
276
+ return b.call(
277
+ '_$_.with_scope',
278
+ b.id('__block'),
279
+ b.thunk({
280
+ ...node,
281
+ callee: context.visit(callee),
282
+ arguments: node.arguments.map((arg) => context.visit(arg)),
283
+ }),
284
+ );
285
+ },
286
+
287
+ MemberExpression(node, context) {
288
+ const parent = context.path.at(-1);
289
+
290
+ if (context.state.metadata?.tracking === false) {
291
+ context.state.metadata.tracking = true;
292
+ }
293
+
294
+ if (node.property.type === 'Identifier' && node.property.tracked) {
295
+ add_ripple_internal_import(context);
296
+
297
+ return b.call(
298
+ '_$_.get_property',
299
+ context.visit(node.object),
300
+ node.computed ? context.visit(node.property) : b.literal(node.property.name),
301
+ node.optional ? b.true : undefined,
302
+ );
303
+ }
304
+
305
+ if (node.object.type === 'MemberExpression' && node.object.optional) {
306
+ const metadata = { tracking: false, await: false };
307
+
308
+ const object = context.visit(node.object, { ...context.state, metadata });
309
+
310
+ if (metadata.tracking) {
311
+ if (context.state.metadata?.tracking === false) {
312
+ context.state.metadata.tracking = true;
313
+ }
314
+
315
+ return {
316
+ ...node,
317
+ optional: true,
318
+ object,
319
+ property: context.visit(node.property),
320
+ };
321
+ }
322
+ if (metadata.await) {
323
+ if (context.state.metadata?.await === false) {
324
+ context.state.metadata.await = true;
325
+ }
326
+ }
327
+ } else {
328
+ context.next();
329
+ }
330
+ },
331
+
332
+ PropertyDefinition(node, context) {
333
+ if (!context.state.to_ts) {
334
+ delete node.typeAnnotation;
335
+ }
336
+ return context.next();
337
+ },
338
+
339
+ VariableDeclaration(node, context) {
340
+ const declarations = [];
341
+
342
+ for (const declarator of node.declarations) {
343
+ const metadata = declarator.metadata;
344
+
345
+ if (declarator.id.type === 'Identifier') {
346
+ const binding = context.state.scope.get(declarator.id.name);
347
+
348
+ if (!context.state.to_ts) {
349
+ delete declarator.id.typeAnnotation;
350
+ }
351
+
352
+ if (binding !== null && binding.kind === 'tracked') {
353
+ let expression;
354
+
355
+ if (context.state.to_ts) {
356
+ // TypeScript mode: lighter transformation
357
+ if (metadata.tracking && !metadata.await) {
358
+ expression = b.call(
359
+ '_$_.derived',
360
+ b.thunk(context.visit(declarator.init)),
361
+ b.id('__block'),
362
+ );
363
+ } else {
364
+ expression = b.call(
365
+ '_$_.tracked',
366
+ declarator.init === null ? undefined : context.visit(declarator.init),
367
+ b.id('__block'),
368
+ );
369
+ }
370
+ } else {
371
+ debugger;
372
+ // Runtime mode: full transformation
373
+ if (metadata.tracking && metadata.await) {
374
+ expression = b.call(
375
+ b.await(
376
+ b.call(
377
+ '_$_.resume_context',
378
+ b.call(
379
+ '_$_.async_computed',
380
+ b.thunk(context.visit(declarator.init), true),
381
+ b.id('__block'),
382
+ ),
383
+ ),
384
+ ),
385
+ );
386
+ } else if (metadata.tracking && !metadata.await) {
387
+ expression = b.call(
388
+ '_$_.derived',
389
+ b.thunk(context.visit(declarator.init)),
390
+ b.id('__block'),
391
+ );
392
+ } else {
393
+ expression = b.call(
394
+ '_$_.tracked',
395
+ declarator.init === null ? undefined : context.visit(declarator.init),
396
+ b.id('__block'),
397
+ );
398
+ }
399
+ }
400
+
401
+ declarations.push(b.declarator(declarator.id, expression));
402
+ } else {
403
+ declarations.push(context.visit(declarator));
404
+ }
405
+ } else {
406
+ if (!context.state.to_ts) {
407
+ delete declarator.id.typeAnnotation;
408
+ }
409
+
410
+ declarations.push(context.visit(declarator));
411
+ }
412
+ }
413
+
414
+ return { ...node, declarations };
415
+ },
416
+
417
+ FunctionDeclaration(node, context) {
418
+ return visit_function(node, context);
419
+ },
420
+ ArrowFunctionExpression(node, context) {
421
+ return visit_function(node, context);
422
+ },
423
+ FunctionExpression(node, context) {
424
+ return visit_function(node, context);
425
+ },
426
+
427
+ Element(node, context) {
428
+ const { state, visit } = context;
429
+
430
+ const is_dom_element = is_element_dom_element(node);
431
+ const is_spreading = node.attributes.some((attr) => attr.type === 'SpreadAttribute');
432
+ const spread_attributes = is_spreading ? [] : null;
433
+
434
+ const child_namespace = is_dom_element
435
+ ? determine_namespace_for_children(node.id.name, state.namespace)
436
+ : state.namespace;
437
+
438
+ const handle_static_attr = (name, value) => {
439
+ const attr_value = b.literal(
440
+ ` ${name}${
441
+ is_boolean_attribute(name) && value === true
442
+ ? ''
443
+ : `="${value === true ? '' : escape_html(value, true)}"`
444
+ }`,
445
+ );
446
+
447
+ if (is_spreading) {
448
+ // For spread attributes, store just the actual value, not the full attribute string
449
+ const actual_value =
450
+ is_boolean_attribute(name) && value === true
451
+ ? b.literal(true)
452
+ : b.literal(value === true ? '' : value);
453
+ spread_attributes.push(b.prop('init', b.literal(name), actual_value));
454
+ } else {
455
+ state.template.push(attr_value);
456
+ }
457
+ };
458
+
459
+ if (is_dom_element) {
460
+ let class_attribute = null;
461
+ const local_updates = [];
462
+ const is_void = is_void_element(node.id.name);
463
+
464
+ state.template.push(`<${node.id.name}`);
465
+
466
+ for (const attr of node.attributes) {
467
+ if (attr.type === 'Attribute') {
468
+ if (attr.name.type === 'Identifier') {
469
+ const name = attr.name.name;
470
+
471
+ if (attr.value === null) {
472
+ handle_static_attr(name, true);
473
+ continue;
474
+ }
475
+
476
+ if (attr.value.type === 'Literal' && name !== 'class') {
477
+ handle_static_attr(name, attr.value.value);
478
+ continue;
479
+ }
480
+
481
+ if (name === 'class') {
482
+ class_attribute = attr;
483
+
484
+ continue;
485
+ }
486
+
487
+ if (name === 'value') {
488
+ const id = state.flush_node();
489
+ const metadata = { tracking: false, await: false };
490
+ const expression = visit(attr.value, { ...state, metadata });
491
+
492
+ if (metadata.tracking) {
493
+ local_updates.push(b.stmt(b.call('_$_.set_value', id, expression)));
494
+ } else {
495
+ state.init.push(b.stmt(b.call('_$_.set_value', id, expression)));
496
+ }
497
+
498
+ continue;
499
+ }
500
+
501
+ if (name === 'checked') {
502
+ const id = state.flush_node();
503
+ const metadata = { tracking: false, await: false };
504
+ const expression = visit(attr.value, { ...state, metadata });
505
+
506
+ if (name === '$checked' || metadata.tracking) {
507
+ local_updates.push(b.stmt(b.call('_$_.set_checked', id, expression)));
508
+ } else {
509
+ state.init.push(b.stmt(b.call('_$_.set_checked', id, expression)));
510
+ }
511
+ continue;
512
+ }
513
+
514
+ if (name === 'selected') {
515
+ const id = state.flush_node();
516
+ const metadata = { tracking: false, await: false };
517
+ const expression = visit(attr.value, { ...state, metadata });
518
+
519
+ if (metadata.tracking) {
520
+ local_updates.push(b.stmt(b.call('_$_.set_selected', id, expression)));
521
+ } else {
522
+ state.init.push(b.stmt(b.call('_$_.set_selected', id, expression)));
523
+ }
524
+ continue;
525
+ }
526
+
527
+ if (is_event_attribute(name)) {
528
+ let capture = name.endsWith('Capture');
529
+ let event_name = capture
530
+ ? name.slice(2, -7).toLowerCase()
531
+ : name.slice(2).toLowerCase();
532
+ let handler = visit(attr.value, state);
533
+
534
+ if (attr.metadata?.delegated) {
535
+ let delegated_assignment;
536
+
537
+ if (!state.events.has(event_name)) {
538
+ state.events.add(event_name);
539
+ }
540
+
541
+ // Hoist function if we can, otherwise we leave the function as is
542
+ if (attr.metadata.delegated.hoisted) {
543
+ if (attr.metadata.delegated.function === attr.value) {
544
+ const func_name = state.scope.root.unique('on_' + event_name);
545
+ state.hoisted.push(b.var(func_name, handler));
546
+ handler = func_name;
547
+ }
548
+
549
+ const hoisted_params = /** @type {Expression[]} */ (
550
+ attr.metadata.delegated.function.metadata.hoisted_params
551
+ );
552
+
553
+ const args = [handler, b.id('__block'), ...hoisted_params];
554
+ delegated_assignment = b.array(args);
555
+ } else if (
556
+ handler.type === 'Identifier' &&
557
+ is_declared_function_within_component(handler, context)
558
+ ) {
559
+ delegated_assignment = handler;
560
+ } else {
561
+ delegated_assignment = b.array([handler, b.id('__block')]);
562
+ }
563
+ const id = state.flush_node();
564
+
565
+ state.init.push(
566
+ b.stmt(b.assignment('=', b.member(id, '__' + event_name), delegated_assignment)),
567
+ );
568
+ } else {
569
+ const passive = is_passive_event(event_name);
570
+ const id = state.flush_node();
571
+
572
+ state.init.push(
573
+ b.stmt(
574
+ b.call(
575
+ '_$_.event',
576
+ b.literal(event_name),
577
+ id,
578
+ handler,
579
+ capture && b.true,
580
+ passive === undefined ? undefined : b.literal(passive),
581
+ ),
582
+ ),
583
+ );
584
+ }
585
+
586
+ continue;
587
+ }
588
+
589
+ const metadata = { tracking: false, await: false };
590
+ const expression = visit(attr.value, { ...state, metadata });
591
+ // All other attributes
592
+ if (metadata.tracking) {
593
+ const attribute = name;
594
+ const id = state.flush_node();
595
+
596
+ if (is_dom_property(attribute)) {
597
+ local_updates.push(b.stmt(b.assignment('=', b.member(id, attribute), expression)));
598
+ } else {
599
+ local_updates.push(
600
+ b.stmt(b.call('_$_.set_attribute', id, b.literal(attribute), expression)),
601
+ );
602
+ }
603
+ } else {
604
+ const id = state.flush_node();
605
+
606
+ if (is_dom_property(name)) {
607
+ state.init.push(b.stmt(b.assignment('=', b.member(id, name), expression)));
608
+ } else {
609
+ state.init.push(
610
+ b.stmt(b.call('_$_.set_attribute', id, b.literal(name), expression)),
611
+ );
612
+ }
613
+ }
614
+ }
615
+ } else if (attr.type === 'SpreadAttribute') {
616
+ spread_attributes.push(b.spread(visit(attr.argument, state)));
617
+ } else if (attr.type === 'RefAttribute') {
618
+ const id = state.flush_node();
619
+ state.init.push(b.stmt(b.call('_$_.ref', id, b.thunk(visit(attr.argument, state)))));
620
+ }
621
+ }
622
+
623
+ if (class_attribute !== null) {
624
+ if (class_attribute.value.type === 'Literal') {
625
+ let value = class_attribute.value.value;
626
+
627
+ if (node.metadata.scoped && state.component.css) {
628
+ value = `${state.component.css.hash} ${value}`;
629
+ }
630
+
631
+ handle_static_attr(class_attribute.name.name, value);
632
+ } else {
633
+ const id = state.flush_node();
634
+ const metadata = { tracking: false, await: false };
635
+ let expression = visit(class_attribute.value, { ...state, metadata });
636
+
637
+ if (node.metadata.scoped && state.component.css) {
638
+ expression = b.binary('+', expression, b.literal(' ' + state.component.css.hash));
639
+ }
640
+ const is_html = context.state.metadata.namespace === 'html' && node.id.name !== 'svg';
641
+
642
+ if (metadata.tracking) {
643
+ local_updates.push(
644
+ b.stmt(b.call('_$_.set_class', id, expression, undefined, b.literal(is_html))),
645
+ );
646
+ } else {
647
+ state.init.push(
648
+ b.stmt(b.call('_$_.set_class', id, expression, undefined, b.literal(is_html))),
649
+ );
650
+ }
651
+ }
652
+ } else if (node.metadata.scoped && state.component.css) {
653
+ const value = state.component.css.hash;
654
+
655
+ handle_static_attr(is_spreading ? '#class' : 'class', value);
656
+ }
657
+
658
+ state.template.push('>');
659
+
660
+ if (spread_attributes !== null && spread_attributes.length > 0) {
661
+ const id = state.flush_node();
662
+ state.init.push(
663
+ b.stmt(b.call('_$_.render_spread', id, b.thunk(b.object(spread_attributes)))),
664
+ );
665
+ }
666
+
667
+ const init = [];
668
+ const update = [];
669
+
670
+ if (!is_void) {
671
+ transform_children(node.children, {
672
+ visit,
673
+ state: { ...state, init, update, namespace: child_namespace },
674
+ root: false,
675
+ });
676
+ state.template.push(`</${node.id.name}>`);
677
+ }
678
+
679
+ update.push(...local_updates);
680
+
681
+ if (update.length > 0) {
682
+ if (state.scope.parent.declarations.size > 0) {
683
+ init.push(b.stmt(b.call('_$_.render', b.thunk(b.block(update), !!update.async))));
684
+ } else {
685
+ state.update.push(...update);
686
+ }
687
+ }
688
+
689
+ if (init.length > 0) {
690
+ state.init.push(b.block(init));
691
+ }
692
+ } else {
693
+ const id = state.flush_node();
694
+
695
+ state.template.push('<!>');
696
+
697
+ const is_spreading = node.attributes.some((attr) => attr.type === 'SpreadAttribute');
698
+ const props = [];
699
+ let children_prop = null;
700
+
701
+ for (const attr of node.attributes) {
702
+ if (attr.type === 'Attribute') {
703
+ if (attr.name.type === 'Identifier') {
704
+ const metadata = { tracking: false, await: false };
705
+ let property = visit(attr.value, { ...state, metadata });
706
+
707
+ if (metadata.tracking || attr.name.tracked) {
708
+ if (attr.name.name === 'children') {
709
+ children_prop = b.thunk(property);
710
+ continue;
711
+ }
712
+
713
+ props.push(
714
+ b.prop('get', attr.name, b.function(null, [], b.block([b.return(property)]))),
715
+ );
716
+ } else {
717
+ props.push(b.prop('init', attr.name, property));
718
+ }
719
+ } else {
720
+ props.push(b.prop('init', attr.name, visit(attr.value, state)));
721
+ }
722
+ } else if (attr.type === 'SpreadAttribute') {
723
+ props.push(
724
+ b.spread(
725
+ visit(attr.argument, { ...state, metadata: { ...state.metadata, spread: true } }),
726
+ ),
727
+ );
728
+ } else if (attr.type === 'RefAttribute') {
729
+ props.push(b.prop('init', b.call('_$_.ref_prop'), visit(attr.argument, state), true));
730
+ } else if (attr.type === 'AccessorAttribute') {
731
+ let get_expr;
732
+
733
+ if (
734
+ attr.get.type === 'FunctionExpression' ||
735
+ attr.get.type === 'ArrowFunctionExpression'
736
+ ) {
737
+ get_expr = context.state.scope.generate(attr.name.name + '_get');
738
+
739
+ state.init.push(b.const(get_expr, visit(attr.get, state)));
740
+ } else {
741
+ get_expr = visit(attr.get, state);
742
+ }
743
+
744
+ props.push(
745
+ b.prop('get', attr.name, b.function(null, [], b.block([b.return(b.call(get_expr))]))),
746
+ );
747
+
748
+ if (attr.set) {
749
+ let set_expr;
750
+
751
+ if (
752
+ attr.set.type === 'FunctionExpression' ||
753
+ attr.set.type === 'ArrowFunctionExpression'
754
+ ) {
755
+ set_expr = context.state.scope.generate(attr.name.name + '_set');
756
+
757
+ state.init.push(b.const(set_expr, visit(attr.set, state)));
758
+ } else {
759
+ set_expr = visit(attr.set, state);
760
+ }
761
+
762
+ props.push(
763
+ b.prop(
764
+ 'set',
765
+ attr.name,
766
+ b.function(
767
+ null,
768
+ [b.id('__value')],
769
+ b.block([b.return(b.call(set_expr, b.id('__value')))]),
770
+ ),
771
+ ),
772
+ );
773
+ }
774
+ } else {
775
+ throw new Error('TODO');
776
+ }
777
+ }
778
+
779
+ const children_filtered = [];
780
+
781
+ for (const child of node.children) {
782
+ if (child.type === 'Component') {
783
+ const id = child.id;
784
+ props.push(b.prop('init', id, visit(child, { ...state, namespace: child_namespace })));
785
+ } else {
786
+ children_filtered.push(child);
787
+ }
788
+ }
789
+
790
+ if (children_filtered.length > 0) {
791
+ const component_scope = context.state.scopes.get(node);
792
+ const children = visit(b.component(b.id('children'), [], children_filtered), {
793
+ ...context.state,
794
+ scope: component_scope,
795
+ namespace: child_namespace,
796
+ });
797
+
798
+ if (children_prop) {
799
+ children_prop.body = b.logical('??', children_prop.body, children);
800
+ } else {
801
+ props.push(b.prop('init', b.id('children'), children));
802
+ }
803
+ }
804
+
805
+ if (is_spreading) {
806
+ state.init.push(
807
+ b.stmt(
808
+ b.call(
809
+ node.id,
810
+ id,
811
+ b.call('_$_.spread_props', b.thunk(b.object(props)), b.id('__block')),
812
+ b.id('_$_.active_block'),
813
+ ),
814
+ ),
815
+ );
816
+ } else {
817
+ state.init.push(
818
+ b.stmt(b.call(visit(node.id, state), id, b.object(props), b.id('_$_.active_block'))),
819
+ );
820
+ }
821
+ }
822
+ },
823
+
824
+ Fragment(node, context) {
825
+ if (!context.state.to_ts) {
826
+ add_ripple_internal_import(context);
827
+ }
828
+
829
+ const metadata = { await: false };
830
+
831
+ const body_statements = transform_body(node.body, {
832
+ ...context,
833
+ state: { ...context.state, component: node, metadata },
834
+ });
835
+
836
+ return b.function(
837
+ node.id,
838
+ [b.id('__anchor'), ...node.params.map((param) => context.visit(param, context.state))],
839
+ b.block(
840
+ metadata.await
841
+ ? [b.stmt(b.call('_$_.async', b.thunk(b.block(body_statements), true)))]
842
+ : body_statements,
843
+ ),
844
+ );
845
+ },
846
+
847
+ Component(node, context) {
848
+ let prop_statements;
849
+
850
+ add_ripple_internal_import(context);
851
+
852
+ const metadata = { await: false };
853
+
854
+ if (context.state.to_ts) {
855
+ const body_statements = [
856
+ ...transform_body(node.body, {
857
+ ...context,
858
+ state: { ...context.state, component: node, metadata },
859
+ }),
860
+ ];
861
+
862
+ return b.function(node.id, node.params, b.block(body_statements));
863
+ }
864
+
865
+ let props = b.id('__props');
866
+
867
+ if (node.params.length > 0) {
868
+ let props_param = node.params[0];
869
+
870
+ if (props_param.type === 'Identifier') {
871
+ delete props_param.typeAnnotation;
872
+ props = props_param;
873
+ } else if (props_param.type === 'ObjectPattern') {
874
+ delete props_param.typeAnnotation;
875
+ }
876
+ }
877
+
878
+ const body_statements = [
879
+ b.stmt(b.call('_$_.push_component')),
880
+ ...transform_body(node.body, {
881
+ ...context,
882
+ state: { ...context.state, component: node, metadata },
883
+ }),
884
+ b.stmt(b.call('_$_.pop_component')),
885
+ ];
886
+
887
+ if (node.css !== null && node.css) {
888
+ context.state.stylesheets.push(node.css);
889
+ }
890
+
891
+ return b.function(
892
+ node.id,
893
+ node.params.length > 0
894
+ ? [b.id('__anchor'), props, b.id('__block')]
895
+ : [b.id('__anchor'), b.id('_'), b.id('__block')],
896
+ b.block([
897
+ ...(prop_statements ?? []),
898
+ ...(metadata.await
899
+ ? [b.stmt(b.call('_$_.async', b.thunk(b.block(body_statements), true)))]
900
+ : body_statements),
901
+ ]),
902
+ );
903
+ },
904
+
905
+ AssignmentExpression(node, context) {
906
+ if (context.state.to_ts) {
907
+ return context.next();
908
+ }
909
+
910
+ const left = node.left;
911
+
912
+ if (
913
+ left.type === 'MemberExpression' &&
914
+ left.property.type === 'Identifier' &&
915
+ left.property.tracked
916
+ ) {
917
+ add_ripple_internal_import(context);
918
+ const operator = node.operator;
919
+ const right = node.right;
920
+
921
+ if (operator !== '=') {
922
+ context.state.metadata.tracking = true;
923
+ }
924
+
925
+ return b.call(
926
+ '_$_.set_property',
927
+ context.visit(left.object, { ...context.state, metadata: { tracking: false } }),
928
+ left.computed ? context.visit(left.property) : b.literal(left.property.name),
929
+ operator === '='
930
+ ? context.visit(right)
931
+ : b.binary(
932
+ operator === '+=' ? '+' : operator === '-=' ? '-' : operator === '*=' ? '*' : '/',
933
+ /** @type {Pattern} */ (context.visit(left)),
934
+ /** @type {Expression} */ (context.visit(right)),
935
+ ),
936
+ b.id('__block'),
937
+ );
938
+ }
939
+
940
+ if (left.type === 'Identifier' && left.tracked) {
941
+ add_ripple_internal_import(context);
942
+ const operator = node.operator;
943
+ const right = node.right;
944
+
945
+ return b.call(
946
+ '_$_.set',
947
+ context.visit(left, { ...context.state, metadata: { tracking: null } }),
948
+ operator === '='
949
+ ? context.visit(right)
950
+ : b.binary(
951
+ operator === '+=' ? '+' : operator === '-=' ? '-' : operator === '*=' ? '*' : '/',
952
+ /** @type {Pattern} */ (
953
+ context.visit(left, { ...context.state, metadata: { tracking: false } })
954
+ ),
955
+ /** @type {Expression} */ (context.visit(right)),
956
+ ),
957
+ b.id('__block'),
958
+ );
959
+ }
960
+
961
+ return visit_assignment_expression(node, context, build_assignment) ?? context.next();
962
+ },
963
+
964
+ UpdateExpression(node, context) {
965
+ if (context.state.to_ts) {
966
+ context.next();
967
+ return;
968
+ }
969
+ const argument = node.argument;
970
+
971
+ if (
972
+ argument.type === 'MemberExpression' &&
973
+ argument.property.type === 'Identifier' &&
974
+ argument.property.tracked
975
+ ) {
976
+ add_ripple_internal_import(context);
977
+ context.state.metadata.tracking = true;
978
+
979
+ return b.call(
980
+ node.prefix ? '_$_.update_pre_property' : '_$_.update_property',
981
+ context.visit(argument.object, { ...context.state, metadata: { tracking: false } }),
982
+ argument.computed ? context.visit(argument.property) : b.literal(argument.property.name),
983
+ b.id('__block'),
984
+ node.operator === '--' ? b.literal(-1) : undefined,
985
+ );
986
+ }
987
+
988
+ if (argument.type === 'Identifier' && argument.tracked) {
989
+ return b.call(
990
+ node.prefix ? '_$_.update_pre' : '_$_.update',
991
+ context.visit(argument, { ...context.state, metadata: { tracking: null } }),
992
+ b.id('__block'),
993
+ node.operator === '--' ? b.literal(-1) : undefined,
994
+ );
995
+ }
996
+
997
+ const left = object(argument);
998
+ const binding = context.state.scope.get(left.name);
999
+ const transformers = left && binding?.transform;
1000
+
1001
+ if (left === argument) {
1002
+ const update_fn = transformers?.update || transformers?.update_tracked;
1003
+ if (update_fn) {
1004
+ return update_fn(node);
1005
+ }
1006
+ }
1007
+
1008
+ context.next();
1009
+ },
1010
+
1011
+ ForOfStatement(node, context) {
1012
+ if (!is_inside_component(context)) {
1013
+ context.next();
1014
+ return;
1015
+ }
1016
+ const is_controlled = node.is_controlled;
1017
+ const index = node.index;
1018
+ let flags = is_controlled ? IS_CONTROLLED : 0;
1019
+
1020
+ if (index !== null) {
1021
+ flags |= IS_INDEXED;
1022
+ }
1023
+
1024
+ // do only if not controller
1025
+ if (!is_controlled) {
1026
+ context.state.template.push('<!>');
1027
+ }
1028
+
1029
+ const id = context.state.flush_node(is_controlled);
1030
+ const pattern = node.left.declarations[0].id;
1031
+ const body_scope = context.state.scopes.get(node.body);
1032
+
1033
+ context.state.init.push(
1034
+ b.stmt(
1035
+ b.call(
1036
+ '_$_.for',
1037
+ id,
1038
+ b.thunk(context.visit(node.right)),
1039
+ b.arrow(
1040
+ index ? [b.id('__anchor'), pattern, index] : [b.id('__anchor'), pattern],
1041
+ b.block(
1042
+ transform_body(node.body.body, {
1043
+ ...context,
1044
+ state: { ...context.state, scope: body_scope, namespace: context.state.namespace },
1045
+ }),
1046
+ ),
1047
+ ),
1048
+ b.literal(flags),
1049
+ ),
1050
+ ),
1051
+ );
1052
+ },
1053
+
1054
+ IfStatement(node, context) {
1055
+ if (!is_inside_component(context)) {
1056
+ context.next();
1057
+ return;
1058
+ }
1059
+ context.state.template.push('<!>');
1060
+
1061
+ const id = context.state.flush_node();
1062
+ const statements = [];
1063
+
1064
+ const consequent_scope = context.state.scopes.get(node.consequent);
1065
+ const consequent = b.block(
1066
+ transform_body(node.consequent.body, {
1067
+ ...context,
1068
+ state: { ...context.state, scope: consequent_scope },
1069
+ }),
1070
+ );
1071
+ const consequent_id = context.state.scope.generate('consequent');
1072
+
1073
+ statements.push(b.var(b.id(consequent_id), b.arrow([b.id('__anchor')], consequent)));
1074
+
1075
+ let alternate_id;
1076
+
1077
+ if (node.alternate !== null) {
1078
+ const alternate_scope = context.state.scopes.get(node.alternate) || context.state.scope;
1079
+ let alternate_body = node.alternate.body;
1080
+ if (node.alternate.type === 'IfStatement') {
1081
+ alternate_body = [node.alternate];
1082
+ }
1083
+ const alternate = b.block(
1084
+ transform_body(alternate_body, {
1085
+ ...context,
1086
+ state: { ...context.state, scope: alternate_scope },
1087
+ }),
1088
+ );
1089
+ alternate_id = context.state.scope.generate('alternate');
1090
+ statements.push(b.var(b.id(alternate_id), b.arrow([b.id('__anchor')], alternate)));
1091
+ }
1092
+
1093
+ statements.push(
1094
+ b.stmt(
1095
+ b.call(
1096
+ '_$_.if',
1097
+ id,
1098
+ b.arrow(
1099
+ [b.id('__render')],
1100
+ b.block([
1101
+ b.if(
1102
+ context.visit(node.test),
1103
+ b.stmt(b.call(b.id('__render'), b.id(consequent_id))),
1104
+ alternate_id
1105
+ ? b.stmt(
1106
+ b.call(
1107
+ b.id('__render'),
1108
+ b.id(alternate_id),
1109
+ node.alternate ? b.literal(false) : undefined,
1110
+ ),
1111
+ )
1112
+ : undefined,
1113
+ ),
1114
+ ]),
1115
+ ),
1116
+ ),
1117
+ ),
1118
+ );
1119
+
1120
+ context.state.init.push(b.block(statements));
1121
+ },
1122
+
1123
+ TSAsExpression(node, context) {
1124
+ if (!context.state.to_ts) {
1125
+ return context.visit(node.expression);
1126
+ }
1127
+ return context.next();
1128
+ },
1129
+
1130
+ TryStatement(node, context) {
1131
+ if (!is_inside_component(context)) {
1132
+ context.next();
1133
+ return;
1134
+ }
1135
+ context.state.template.push('<!>');
1136
+
1137
+ const id = context.state.flush_node();
1138
+ const metadata = { await: false };
1139
+ let body = transform_body(node.block.body, {
1140
+ ...context,
1141
+ state: { ...context.state, metadata },
1142
+ });
1143
+
1144
+ if (metadata.pending) {
1145
+ body = [b.stmt(b.call('_$_.async', b.thunk(b.block(body), true)))];
1146
+ }
1147
+
1148
+ context.state.init.push(
1149
+ b.stmt(
1150
+ b.call(
1151
+ '_$_.try',
1152
+ id,
1153
+ b.arrow([b.id('__anchor')], b.block(body)),
1154
+ node.handler === null
1155
+ ? b.literal(null)
1156
+ : b.arrow(
1157
+ [b.id('__anchor'), ...(node.handler.param ? [node.handler.param] : [])],
1158
+ b.block(transform_body(node.handler.body.body, context)),
1159
+ ),
1160
+ node.pending === null
1161
+ ? undefined
1162
+ : b.arrow([b.id('__anchor')], b.block(transform_body(node.pending.body, context))),
1163
+ ),
1164
+ ),
1165
+ );
1166
+ },
1167
+
1168
+ AwaitExpression(node, context) {
1169
+ if (!is_top_level_await(context) || context.state.to_ts) {
1170
+ return context.next();
1171
+ }
1172
+
1173
+ if (context.state.metadata?.await === false) {
1174
+ context.state.metadata.await = true;
1175
+ }
1176
+
1177
+ return b.call(b.await(b.call('_$_.maybe_tracked', context.visit(node.argument))));
1178
+ },
1179
+
1180
+ BinaryExpression(node, context) {
1181
+ return b.binary(node.operator, context.visit(node.left), context.visit(node.right));
1182
+ },
1183
+
1184
+ TemplateLiteral(node, context) {
1185
+ const parent = context.path.at(-1);
1186
+
1187
+ if (node.expressions.length === 0 && parent?.type !== 'TaggedTemplateExpression') {
1188
+ return b.literal(node.quasis[0].value.cooked);
1189
+ }
1190
+
1191
+ const expressions = node.expressions.map((expr) => context.visit(expr));
1192
+ return b.template(node.quasis, expressions);
1193
+ },
1194
+
1195
+ BlockStatement(node, context) {
1196
+ const statements = [];
1197
+
1198
+ for (const statement of node.body) {
1199
+ statements.push(context.visit(statement));
1200
+ }
1201
+
1202
+ return b.block(statements);
1203
+ },
1204
+
1205
+ Program(node, context) {
1206
+ const statements = [];
1207
+
1208
+ for (const statement of node.body) {
1209
+ statements.push(context.visit(statement));
1210
+ }
1211
+
1212
+ return { ...node, body: statements };
1213
+ },
1232
1214
  };
1233
1215
 
1234
1216
  /**
1235
1217
  * @param {Array<string | Expression>} items
1236
1218
  */
1237
1219
  function join_template(items) {
1238
- let quasi = b.quasi('');
1239
- const template = b.template([quasi], []);
1240
-
1241
- /**
1242
- * @param {Expression} expression
1243
- */
1244
- function push(expression) {
1245
- if (expression.type === 'TemplateLiteral') {
1246
- for (let i = 0; i < expression.expressions.length; i += 1) {
1247
- const q = expression.quasis[i];
1248
- const e = expression.expressions[i];
1249
-
1250
- quasi.value.cooked += /** @type {string} */ (q.value.cooked);
1251
- push(e);
1252
- }
1253
-
1254
- const last = /** @type {TemplateElement} */ (expression.quasis.at(-1));
1255
- quasi.value.cooked += /** @type {string} */ (last.value.cooked);
1256
- } else if (expression.type === 'Literal') {
1257
- /** @type {string} */ (quasi.value.cooked) += expression.value;
1258
- } else {
1259
- template.expressions.push(expression);
1260
- template.quasis.push((quasi = b.quasi('')));
1261
- }
1262
- }
1263
-
1264
- for (const item of items) {
1265
- if (typeof item === 'string') {
1266
- quasi.value.cooked += item;
1267
- } else {
1268
- push(item);
1269
- }
1270
- }
1271
-
1272
- for (const quasi of template.quasis) {
1273
- quasi.value.raw = sanitize_template_string(/** @type {string} */ (quasi.value.cooked));
1274
- }
1275
-
1276
- quasi.tail = true;
1277
-
1278
- return template;
1279
- }
1280
-
1281
- function normalize_child(node, normalized) {
1282
- if (node.type === 'EmptyStatement') {
1283
- return;
1284
- } else if (node.type === 'Element' && node.id.type === 'Identifier' && node.id.name === 'style') {
1285
- return;
1286
- } else {
1287
- normalized.push(node);
1288
- }
1220
+ let quasi = b.quasi('');
1221
+ const template = b.template([quasi], []);
1222
+
1223
+ /**
1224
+ * @param {Expression} expression
1225
+ */
1226
+ function push(expression) {
1227
+ if (expression.type === 'TemplateLiteral') {
1228
+ for (let i = 0; i < expression.expressions.length; i += 1) {
1229
+ const q = expression.quasis[i];
1230
+ const e = expression.expressions[i];
1231
+
1232
+ quasi.value.cooked += /** @type {string} */ (q.value.cooked);
1233
+ push(e);
1234
+ }
1235
+
1236
+ const last = /** @type {TemplateElement} */ (expression.quasis.at(-1));
1237
+ quasi.value.cooked += /** @type {string} */ (last.value.cooked);
1238
+ } else if (expression.type === 'Literal') {
1239
+ /** @type {string} */ (quasi.value.cooked) += expression.value;
1240
+ } else {
1241
+ template.expressions.push(expression);
1242
+ template.quasis.push((quasi = b.quasi('')));
1243
+ }
1244
+ }
1245
+
1246
+ for (const item of items) {
1247
+ if (typeof item === 'string') {
1248
+ quasi.value.cooked += item;
1249
+ } else {
1250
+ push(item);
1251
+ }
1252
+ }
1253
+
1254
+ for (const quasi of template.quasis) {
1255
+ quasi.value.raw = sanitize_template_string(/** @type {string} */ (quasi.value.cooked));
1256
+ }
1257
+
1258
+ quasi.tail = true;
1259
+
1260
+ return template;
1289
1261
  }
1290
1262
 
1291
1263
  function transform_ts_child(node, context) {
1292
- const { state, visit } = context;
1293
-
1294
- if (node.type === 'Text') {
1295
- state.init.push(b.stmt(visit(node.expression, { ...state })));
1296
- } else if (node.type === 'Element') {
1297
- const type = node.id.name;
1298
- const children = [];
1299
- let has_children_props = false;
1300
-
1301
- // Filter out RefAttributes and handle them separately
1302
- const ref_attributes = [];
1303
- const attributes = node.attributes
1304
- .filter((attr) => {
1305
- if (attr.type === 'RefAttribute') {
1306
- ref_attributes.push(attr);
1307
- return false;
1308
- }
1309
- return true;
1310
- })
1311
- .map((attr) => {
1312
- if (attr.type === 'Attribute') {
1313
- const metadata = { await: false };
1314
- const name = visit(attr.name, { ...state, metadata });
1315
- const value = visit(attr.value, { ...state, metadata });
1316
- const jsx_name = b.jsx_id(name.name);
1317
- if (name.name === 'children') {
1318
- has_children_props = true;
1319
- }
1320
- jsx_name.loc = name.loc;
1321
-
1322
- return b.jsx_attribute(jsx_name, b.jsx_expression_container(value));
1323
- } else if (attr.type === 'SpreadAttribute') {
1324
- const metadata = { await: false };
1325
- const argument = visit(attr.argument, { ...state, metadata });
1326
- return b.jsx_spread_attribute(argument);
1327
- }
1328
- });
1329
-
1330
- // Add RefAttribute references separately for sourcemap purposes
1331
- for (const ref_attr of ref_attributes) {
1332
- const metadata = { await: false };
1333
- const argument = visit(ref_attr.argument, { ...state, metadata });
1334
- state.init.push(b.stmt(argument));
1335
- }
1336
-
1337
- if (!node.selfClosing && !has_children_props && node.children.length > 0) {
1338
- const is_dom_element = is_element_dom_element(node);
1339
-
1340
- const component_scope = context.state.scopes.get(node);
1341
- const thunk = b.thunk(
1342
- b.block(
1343
- transform_body(node.children, {
1344
- ...context,
1345
- state: { ...state, scope: component_scope },
1346
- }),
1347
- ),
1348
- );
1349
-
1350
- if (is_dom_element) {
1351
- children.push(b.jsx_expression_container(b.call(thunk)));
1352
- } else {
1353
- const children_name = context.state.scope.generate('component');
1354
- const children_id = b.id(children_name);
1355
- const jsx_id = b.jsx_id('children');
1356
- jsx_id.loc = node.id.loc;
1357
- state.init.push(b.const(children_id, thunk));
1358
- attributes.push(b.jsx_attribute(jsx_id, b.jsx_expression_container(children_id)));
1359
- }
1360
- }
1361
-
1362
- const opening_type = b.jsx_id(type);
1363
- opening_type.loc = node.id.loc;
1364
-
1365
- let closing_type = undefined;
1366
-
1367
- if (!node.selfClosing) {
1368
- closing_type = b.jsx_id(type);
1369
- closing_type.loc = {
1370
- start: {
1371
- line: node.loc.end.line,
1372
- column: node.loc.end.column - type.length - 1,
1373
- },
1374
- end: {
1375
- line: node.loc.end.line,
1376
- column: node.loc.end.column - 1,
1377
- },
1378
- };
1379
- }
1380
-
1381
- state.init.push(
1382
- b.stmt(b.jsx_element(opening_type, attributes, children, node.selfClosing, closing_type)),
1383
- );
1384
- } else if (node.type === 'IfStatement') {
1385
- const consequent_scope = context.state.scopes.get(node.consequent);
1386
- const consequent = b.block(
1387
- transform_body(node.consequent.body, {
1388
- ...context,
1389
- state: { ...context.state, scope: consequent_scope },
1390
- }),
1391
- );
1392
-
1393
- let alternate;
1394
-
1395
- if (node.alternate !== null) {
1396
- const alternate_scope = context.state.scopes.get(node.alternate) || context.state.scope;
1397
- let alternate_body = node.alternate.body;
1398
- if (node.alternate.type === 'IfStatement') {
1399
- alternate_body = [node.alternate];
1400
- }
1401
- alternate = b.block(
1402
- transform_body(alternate_body, {
1403
- ...context,
1404
- state: { ...context.state, scope: alternate_scope },
1405
- }),
1406
- );
1407
- }
1408
-
1409
- state.init.push(b.if(visit(node.test), consequent, alternate));
1410
- } else if (node.type === 'ForOfStatement') {
1411
- const body_scope = context.state.scopes.get(node.body);
1412
- const body = b.block(
1413
- transform_body(node.body.body, {
1414
- ...context,
1415
- state: { ...context.state, scope: body_scope },
1416
- }),
1417
- );
1418
-
1419
- state.init.push(b.for_of(visit(node.left), visit(node.right), body, node.await));
1420
- } else if (node.type === 'TryStatement') {
1421
- const try_scope = context.state.scopes.get(node.block);
1422
- const try_body = b.block(
1423
- transform_body(node.block.body, {
1424
- ...context,
1425
- state: { ...context.state, scope: try_scope },
1426
- }),
1427
- );
1428
-
1429
- let catch_handler = null;
1430
- if (node.handler) {
1431
- const catch_scope = context.state.scopes.get(node.handler.body);
1432
- const catch_body = b.block(
1433
- transform_body(node.handler.body.body, {
1434
- ...context,
1435
- state: { ...context.state, scope: catch_scope },
1436
- }),
1437
- );
1438
- catch_handler = b.catch_clause(node.handler.param || null, catch_body);
1439
- }
1440
-
1441
- let finally_block = null;
1442
- if (node.finalizer) {
1443
- const finally_scope = context.state.scopes.get(node.finalizer);
1444
- finally_block = b.block(
1445
- transform_body(node.finalizer.body, {
1446
- ...context,
1447
- state: { ...context.state, scope: finally_scope },
1448
- }),
1449
- );
1450
- }
1451
-
1452
- state.init.push(b.try(try_body, catch_handler, finally_block));
1453
- } else if (node.type === 'Component') {
1454
- const component = visit(node, context.state);
1455
-
1456
- state.init.push(component);
1457
- } else {
1458
- debugger;
1459
- throw new Error('TODO');
1460
- }
1264
+ const { state, visit } = context;
1265
+
1266
+ if (node.type === 'Text') {
1267
+ state.init.push(b.stmt(visit(node.expression, { ...state })));
1268
+ } else if (node.type === 'Element') {
1269
+ const type = node.id.name;
1270
+ const children = [];
1271
+ let has_children_props = false;
1272
+
1273
+ // Filter out RefAttributes and handle them separately
1274
+ const ref_attributes = [];
1275
+ const attributes = node.attributes
1276
+ .filter((attr) => {
1277
+ if (attr.type === 'RefAttribute') {
1278
+ ref_attributes.push(attr);
1279
+ return false;
1280
+ }
1281
+ return true;
1282
+ })
1283
+ .map((attr) => {
1284
+ if (attr.type === 'Attribute') {
1285
+ const metadata = { await: false };
1286
+ const name = visit(attr.name, { ...state, metadata });
1287
+ const value = visit(attr.value, { ...state, metadata });
1288
+ const jsx_name = b.jsx_id(name.name);
1289
+ if (name.name === 'children') {
1290
+ has_children_props = true;
1291
+ }
1292
+ jsx_name.loc = name.loc;
1293
+
1294
+ return b.jsx_attribute(jsx_name, b.jsx_expression_container(value));
1295
+ } else if (attr.type === 'SpreadAttribute') {
1296
+ const metadata = { await: false };
1297
+ const argument = visit(attr.argument, { ...state, metadata });
1298
+ return b.jsx_spread_attribute(argument);
1299
+ }
1300
+ });
1301
+
1302
+ // Add RefAttribute references separately for sourcemap purposes
1303
+ for (const ref_attr of ref_attributes) {
1304
+ const metadata = { await: false };
1305
+ const argument = visit(ref_attr.argument, { ...state, metadata });
1306
+ state.init.push(b.stmt(argument));
1307
+ }
1308
+
1309
+ if (!node.selfClosing && !has_children_props && node.children.length > 0) {
1310
+ const is_dom_element = is_element_dom_element(node);
1311
+
1312
+ const component_scope = context.state.scopes.get(node);
1313
+ const thunk = b.thunk(
1314
+ b.block(
1315
+ transform_body(node.children, {
1316
+ ...context,
1317
+ state: { ...state, scope: component_scope },
1318
+ }),
1319
+ ),
1320
+ );
1321
+
1322
+ if (is_dom_element) {
1323
+ children.push(b.jsx_expression_container(b.call(thunk)));
1324
+ } else {
1325
+ const children_name = context.state.scope.generate('component');
1326
+ const children_id = b.id(children_name);
1327
+ const jsx_id = b.jsx_id('children');
1328
+ jsx_id.loc = node.id.loc;
1329
+ state.init.push(b.const(children_id, thunk));
1330
+ attributes.push(b.jsx_attribute(jsx_id, b.jsx_expression_container(children_id)));
1331
+ }
1332
+ }
1333
+
1334
+ const opening_type = b.jsx_id(type);
1335
+ opening_type.loc = node.id.loc;
1336
+
1337
+ let closing_type = undefined;
1338
+
1339
+ if (!node.selfClosing) {
1340
+ closing_type = b.jsx_id(type);
1341
+ closing_type.loc = {
1342
+ start: {
1343
+ line: node.loc.end.line,
1344
+ column: node.loc.end.column - type.length - 1,
1345
+ },
1346
+ end: {
1347
+ line: node.loc.end.line,
1348
+ column: node.loc.end.column - 1,
1349
+ },
1350
+ };
1351
+ }
1352
+
1353
+ state.init.push(
1354
+ b.stmt(b.jsx_element(opening_type, attributes, children, node.selfClosing, closing_type)),
1355
+ );
1356
+ } else if (node.type === 'IfStatement') {
1357
+ const consequent_scope = context.state.scopes.get(node.consequent);
1358
+ const consequent = b.block(
1359
+ transform_body(node.consequent.body, {
1360
+ ...context,
1361
+ state: { ...context.state, scope: consequent_scope },
1362
+ }),
1363
+ );
1364
+
1365
+ let alternate;
1366
+
1367
+ if (node.alternate !== null) {
1368
+ const alternate_scope = context.state.scopes.get(node.alternate) || context.state.scope;
1369
+ let alternate_body = node.alternate.body;
1370
+ if (node.alternate.type === 'IfStatement') {
1371
+ alternate_body = [node.alternate];
1372
+ }
1373
+ alternate = b.block(
1374
+ transform_body(alternate_body, {
1375
+ ...context,
1376
+ state: { ...context.state, scope: alternate_scope },
1377
+ }),
1378
+ );
1379
+ }
1380
+
1381
+ state.init.push(b.if(visit(node.test), consequent, alternate));
1382
+ } else if (node.type === 'ForOfStatement') {
1383
+ const body_scope = context.state.scopes.get(node.body);
1384
+ const body = b.block(
1385
+ transform_body(node.body.body, {
1386
+ ...context,
1387
+ state: { ...context.state, scope: body_scope },
1388
+ }),
1389
+ );
1390
+
1391
+ state.init.push(b.for_of(visit(node.left), visit(node.right), body, node.await));
1392
+ } else if (node.type === 'TryStatement') {
1393
+ const try_scope = context.state.scopes.get(node.block);
1394
+ const try_body = b.block(
1395
+ transform_body(node.block.body, {
1396
+ ...context,
1397
+ state: { ...context.state, scope: try_scope },
1398
+ }),
1399
+ );
1400
+
1401
+ let catch_handler = null;
1402
+ if (node.handler) {
1403
+ const catch_scope = context.state.scopes.get(node.handler.body);
1404
+ const catch_body = b.block(
1405
+ transform_body(node.handler.body.body, {
1406
+ ...context,
1407
+ state: { ...context.state, scope: catch_scope },
1408
+ }),
1409
+ );
1410
+ catch_handler = b.catch_clause(node.handler.param || null, catch_body);
1411
+ }
1412
+
1413
+ let finally_block = null;
1414
+ if (node.finalizer) {
1415
+ const finally_scope = context.state.scopes.get(node.finalizer);
1416
+ finally_block = b.block(
1417
+ transform_body(node.finalizer.body, {
1418
+ ...context,
1419
+ state: { ...context.state, scope: finally_scope },
1420
+ }),
1421
+ );
1422
+ }
1423
+
1424
+ state.init.push(b.try(try_body, catch_handler, finally_block));
1425
+ } else if (node.type === 'Component') {
1426
+ const component = visit(node, context.state);
1427
+
1428
+ state.init.push(component);
1429
+ } else {
1430
+ debugger;
1431
+ throw new Error('TODO');
1432
+ }
1461
1433
  }
1462
1434
 
1463
- function transform_children(children, element, context) {
1464
- const { visit, state, root } = context;
1465
- const normalized = [];
1466
-
1467
- for (const node of children) {
1468
- normalize_child(node, normalized);
1469
- }
1470
-
1471
- const is_fragment =
1472
- normalized.some(
1473
- (node) =>
1474
- node.type === 'IfStatement' ||
1475
- node.type === 'TryStatement' ||
1476
- node.type === 'ForOfStatement' ||
1477
- (node.type === 'Element' &&
1478
- (node.id.type !== 'Identifier' || !is_element_dom_element(node))),
1479
- ) ||
1480
- normalized.filter(
1481
- (node) => node.type !== 'VariableDeclaration' && node.type !== 'EmptyStatement',
1482
- ).length > 1;
1483
- let initial = null;
1484
- let prev = null;
1485
- let template_id = null;
1486
-
1487
- const get_id = (node) => {
1488
- return b.id(
1489
- node.type == 'Element' && is_element_dom_element(node)
1490
- ? state.scope.generate(node.id.name)
1491
- : node.type == 'Text'
1492
- ? state.scope.generate('text')
1493
- : state.scope.generate('node'),
1494
- );
1495
- };
1496
-
1497
- const create_initial = (node) => {
1498
- const id = is_fragment ? b.id(state.scope.generate('fragment')) : get_id(node);
1499
- initial = id;
1500
- template_id = state.scope.generate('root');
1501
- state.setup.push(b.var(id, b.call(template_id)));
1502
- };
1503
-
1504
- for (let i = normalized.length - 1; i >= 0; i--) {
1505
- const child = normalized[i];
1506
- const prev_child = normalized[i - 1];
1507
-
1508
- if (child.type === 'Text' && prev_child?.type === 'Text') {
1509
- if (child.expression.type === 'Literal' && prev_child.expression.type === 'Literal') {
1510
- prev_child.expression = b.literal(
1511
- prev_child.expression.value + String(child.expression.value),
1512
- );
1513
- } else {
1514
- prev_child.expression = b.binary(
1515
- '+',
1516
- prev_child.expression,
1517
- b.call('String', child.expression),
1518
- );
1519
- }
1520
- normalized.splice(i, 1);
1521
- }
1522
- }
1523
-
1524
- for (const node of normalized) {
1525
- if (
1526
- node.type === 'VariableDeclaration' ||
1527
- node.type === 'ExpressionStatement' ||
1528
- node.type === 'ThrowStatement' ||
1529
- node.type === 'FunctionDeclaration' ||
1530
- node.type === 'DebuggerStatement' ||
1531
- node.type === 'ClassDeclaration' ||
1532
- node.type === 'TSTypeAliasDeclaration' ||
1533
- node.type === 'TSInterfaceDeclaration' ||
1534
- node.type === 'Component'
1535
- ) {
1536
- const metadata = { await: false };
1537
- state.init.push(visit(node, { ...state, metadata }));
1538
- if (metadata.await) {
1539
- state.init.push(b.if(b.call('_$_.aborted'), b.return(null)));
1540
- if (state.metadata?.await === false) {
1541
- state.metadata.await = true;
1542
- }
1543
- }
1544
- } else if (state.to_ts) {
1545
- transform_ts_child(node, { visit, state });
1546
- } else {
1547
- if (initial === null && root) {
1548
- create_initial(node);
1549
- }
1550
-
1551
- const current_prev = prev;
1552
- let cached;
1553
- const flush_node = (is_controlled) => {
1554
- if (cached && !is_controlled) {
1555
- return cached;
1556
- } else if (current_prev !== null) {
1557
- const id = get_id(node);
1558
- state.setup.push(b.var(id, b.call('_$_.sibling', current_prev())));
1559
- cached = id;
1560
- return id;
1561
- } else if (initial !== null) {
1562
- if (is_fragment) {
1563
- const id = get_id(node);
1564
- state.setup.push(b.var(id, b.call('_$_.child_frag', initial)));
1565
- cached = id;
1566
- return id;
1567
- }
1568
- return initial;
1569
- } else if (state.flush_node !== null) {
1570
- if (is_controlled) {
1571
- return state.flush_node();
1572
- }
1573
-
1574
- const id = get_id(node);
1575
- state.setup.push(b.var(id, b.call('_$_.child', state.flush_node())));
1576
- cached = id;
1577
- return id;
1578
- } else {
1579
- debugger;
1580
- }
1581
- };
1582
-
1583
- prev = flush_node;
1584
-
1585
- const is_controlled = normalized.length === 1 && element !== null;
1586
-
1587
- if (node.type === 'Element') {
1588
- visit(node, { ...state, flush_node, namespace: state.namespace });
1589
- } else if (node.type === 'Text') {
1590
- const metadata = { tracking: false, await: false };
1591
- const expression = visit(node.expression, { ...state, metadata });
1592
-
1593
- if (metadata.tracking) {
1594
- state.template.push(' ');
1595
- const id = flush_node();
1596
- state.update.push(b.stmt(b.call('_$_.set_text', id, expression)));
1597
- if (metadata.await) {
1598
- state.update.async = true;
1599
- }
1600
- } else if (normalized.length === 1) {
1601
- if (expression.type === 'Literal') {
1602
- state.template.push(escape_html(expression.value));
1603
- } else {
1604
- const id = flush_node();
1605
- state.template.push(' ');
1606
- // avoid set_text overhead for single text nodes
1607
- state.init.push(b.stmt(b.assignment('=', b.member(id, b.id('nodeValue')), expression)));
1608
- }
1609
- } else {
1610
- // Handle Text nodes in fragments
1611
- state.template.push(' ');
1612
- const id = flush_node();
1613
- state.update.push(b.stmt(b.call('_$_.set_text', id, expression)));
1614
- if (metadata.await) {
1615
- state.update.async = true;
1616
- }
1617
- }
1618
- } else if (node.type === 'ForOfStatement') {
1619
- node.is_controlled = is_controlled;
1620
- visit(node, { ...state, flush_node, namespace: state.namespace });
1621
- } else if (node.type === 'IfStatement') {
1622
- node.is_controlled = is_controlled;
1623
- visit(node, { ...state, flush_node, namespace: state.namespace });
1624
- } else if (node.type === 'TryStatement') {
1625
- node.is_controlled = is_controlled;
1626
- visit(node, { ...state, flush_node, namespace: state.namespace });
1627
- } else {
1628
- debugger;
1629
- }
1630
- }
1631
- }
1632
-
1633
- const template_namespace = state.namespace || 'html';
1634
-
1635
- if (root && initial !== null && template_id !== null) {
1636
- let flags = is_fragment ? TEMPLATE_FRAGMENT : 0;
1637
- if (template_namespace === 'svg') {
1638
- flags |= TEMPLATE_SVG_NAMESPACE;
1639
- } else if (template_namespace === 'mathml') {
1640
- flags |= TEMPLATE_MATHML_NAMESPACE;
1641
- }
1642
- state.final.push(b.stmt(b.call('_$_.append', b.id('__anchor'), initial)));
1643
- state.hoisted.push(
1644
- b.var(template_id, b.call('_$_.template', join_template(state.template), b.literal(flags))),
1645
- );
1646
- }
1435
+ function transform_children(children, context) {
1436
+ const { visit, state, root } = context;
1437
+ const normalized = normalize_children(children);
1438
+
1439
+ const is_fragment =
1440
+ normalized.some(
1441
+ (node) =>
1442
+ node.type === 'IfStatement' ||
1443
+ node.type === 'TryStatement' ||
1444
+ node.type === 'ForOfStatement' ||
1445
+ (node.type === 'Element' &&
1446
+ (node.id.type !== 'Identifier' || !is_element_dom_element(node))),
1447
+ ) ||
1448
+ normalized.filter(
1449
+ (node) => node.type !== 'VariableDeclaration' && node.type !== 'EmptyStatement',
1450
+ ).length > 1;
1451
+ let initial = null;
1452
+ let prev = null;
1453
+ let template_id = null;
1454
+
1455
+ const get_id = (node) => {
1456
+ return b.id(
1457
+ node.type == 'Element' && is_element_dom_element(node)
1458
+ ? state.scope.generate(node.id.name)
1459
+ : node.type == 'Text'
1460
+ ? state.scope.generate('text')
1461
+ : state.scope.generate('node'),
1462
+ );
1463
+ };
1464
+
1465
+ const create_initial = (node) => {
1466
+ const id = is_fragment ? b.id(state.scope.generate('fragment')) : get_id(node);
1467
+ initial = id;
1468
+ template_id = state.scope.generate('root');
1469
+ state.setup.push(b.var(id, b.call(template_id)));
1470
+ };
1471
+
1472
+ for (const node of normalized) {
1473
+ if (
1474
+ node.type === 'VariableDeclaration' ||
1475
+ node.type === 'ExpressionStatement' ||
1476
+ node.type === 'ThrowStatement' ||
1477
+ node.type === 'FunctionDeclaration' ||
1478
+ node.type === 'DebuggerStatement' ||
1479
+ node.type === 'ClassDeclaration' ||
1480
+ node.type === 'TSTypeAliasDeclaration' ||
1481
+ node.type === 'TSInterfaceDeclaration' ||
1482
+ node.type === 'Component'
1483
+ ) {
1484
+ const metadata = { await: false };
1485
+ state.init.push(visit(node, { ...state, metadata }));
1486
+ if (metadata.await) {
1487
+ state.init.push(b.if(b.call('_$_.aborted'), b.return(null)));
1488
+ if (state.metadata?.await === false) {
1489
+ state.metadata.await = true;
1490
+ }
1491
+ }
1492
+ } else if (state.to_ts) {
1493
+ transform_ts_child(node, { visit, state });
1494
+ } else {
1495
+ if (initial === null && root) {
1496
+ create_initial(node);
1497
+ }
1498
+
1499
+ const current_prev = prev;
1500
+ let cached;
1501
+ const flush_node = (is_controlled) => {
1502
+ if (cached && !is_controlled) {
1503
+ return cached;
1504
+ } else if (current_prev !== null) {
1505
+ const id = get_id(node);
1506
+ state.setup.push(b.var(id, b.call('_$_.sibling', current_prev())));
1507
+ cached = id;
1508
+ return id;
1509
+ } else if (initial !== null) {
1510
+ if (is_fragment) {
1511
+ const id = get_id(node);
1512
+ state.setup.push(b.var(id, b.call('_$_.child_frag', initial)));
1513
+ cached = id;
1514
+ return id;
1515
+ }
1516
+ return initial;
1517
+ } else if (state.flush_node !== null) {
1518
+ if (is_controlled) {
1519
+ return state.flush_node();
1520
+ }
1521
+
1522
+ const id = get_id(node);
1523
+ state.setup.push(b.var(id, b.call('_$_.child', state.flush_node())));
1524
+ cached = id;
1525
+ return id;
1526
+ } else {
1527
+ debugger;
1528
+ }
1529
+ };
1530
+
1531
+ prev = flush_node;
1532
+
1533
+ const is_controlled = normalized.length === 1 && !root;
1534
+
1535
+ if (node.type === 'Element') {
1536
+ visit(node, { ...state, flush_node, namespace: state.namespace });
1537
+ } else if (node.type === 'Text') {
1538
+ const metadata = { tracking: false, await: false };
1539
+ const expression = visit(node.expression, { ...state, metadata });
1540
+
1541
+ if (metadata.tracking) {
1542
+ state.template.push(' ');
1543
+ const id = flush_node();
1544
+ state.update.push(b.stmt(b.call('_$_.set_text', id, expression)));
1545
+ if (metadata.await) {
1546
+ state.update.async = true;
1547
+ }
1548
+ } else if (normalized.length === 1) {
1549
+ if (expression.type === 'Literal') {
1550
+ state.template.push(escape_html(expression.value));
1551
+ } else {
1552
+ const id = flush_node();
1553
+ state.template.push(' ');
1554
+ // avoid set_text overhead for single text nodes
1555
+ state.init.push(b.stmt(b.assignment('=', b.member(id, b.id('nodeValue')), expression)));
1556
+ }
1557
+ } else {
1558
+ // Handle Text nodes in fragments
1559
+ state.template.push(' ');
1560
+ const id = flush_node();
1561
+ state.update.push(b.stmt(b.call('_$_.set_text', id, expression)));
1562
+ if (metadata.await) {
1563
+ state.update.async = true;
1564
+ }
1565
+ }
1566
+ } else if (node.type === 'ForOfStatement') {
1567
+ node.is_controlled = is_controlled;
1568
+ visit(node, { ...state, flush_node, namespace: state.namespace });
1569
+ } else if (node.type === 'IfStatement') {
1570
+ node.is_controlled = is_controlled;
1571
+ visit(node, { ...state, flush_node, namespace: state.namespace });
1572
+ } else if (node.type === 'TryStatement') {
1573
+ node.is_controlled = is_controlled;
1574
+ visit(node, { ...state, flush_node, namespace: state.namespace });
1575
+ } else {
1576
+ debugger;
1577
+ }
1578
+ }
1579
+ }
1580
+
1581
+ const template_namespace = state.namespace || 'html';
1582
+
1583
+ if (root && initial !== null && template_id !== null) {
1584
+ let flags = is_fragment ? TEMPLATE_FRAGMENT : 0;
1585
+ if (template_namespace === 'svg') {
1586
+ flags |= TEMPLATE_SVG_NAMESPACE;
1587
+ } else if (template_namespace === 'mathml') {
1588
+ flags |= TEMPLATE_MATHML_NAMESPACE;
1589
+ }
1590
+ state.final.push(b.stmt(b.call('_$_.append', b.id('__anchor'), initial)));
1591
+ state.hoisted.push(
1592
+ b.var(template_id, b.call('_$_.template', join_template(state.template), b.literal(flags))),
1593
+ );
1594
+ }
1647
1595
  }
1648
1596
 
1649
1597
  function transform_body(body, { visit, state }) {
1650
- const body_state = {
1651
- ...state,
1652
- template: [],
1653
- setup: [],
1654
- init: [],
1655
- update: [],
1656
- final: [],
1657
- metadata: state.metadata,
1658
- namespace: state.namespace || 'html', // Preserve namespace context
1659
- };
1660
-
1661
- transform_children(body, null, { visit, state: body_state, root: true });
1662
-
1663
- if (body_state.update.length > 0) {
1664
- body_state.init.push(b.stmt(b.call('_$_.render', b.thunk(b.block(body_state.update)))));
1665
- }
1666
-
1667
- return [...body_state.setup, ...body_state.init, ...body_state.final];
1598
+ const body_state = {
1599
+ ...state,
1600
+ template: [],
1601
+ setup: [],
1602
+ init: [],
1603
+ update: [],
1604
+ final: [],
1605
+ metadata: state.metadata,
1606
+ namespace: state.namespace || 'html', // Preserve namespace context
1607
+ };
1608
+
1609
+ transform_children(body, { visit, state: body_state, root: true });
1610
+
1611
+ if (body_state.update.length > 0) {
1612
+ body_state.init.push(b.stmt(b.call('_$_.render', b.thunk(b.block(body_state.update)))));
1613
+ }
1614
+
1615
+ return [...body_state.setup, ...body_state.init, ...body_state.final];
1668
1616
  }
1669
1617
 
1670
1618
  export function transform_client(filename, source, analysis, to_ts) {
1671
- const state = {
1672
- imports: new Set(),
1673
- events: new Set(),
1674
- template: null,
1675
- hoisted: [],
1676
- setup: null,
1677
- init: null,
1678
- update: null,
1679
- final: null,
1680
- flush_node: null,
1681
- scope: analysis.scope,
1682
- scopes: analysis.scopes,
1683
- stylesheets: [],
1684
- to_ts,
1685
- };
1686
-
1687
- const program = /** @type {ESTree.Program} */ (
1688
- walk(analysis.ast, { ...state, namespace: 'html' }, visitors)
1689
- );
1690
-
1691
- for (const hoisted of state.hoisted) {
1692
- program.body.unshift(hoisted);
1693
- }
1694
-
1695
- for (const import_node of state.imports) {
1696
- program.body.unshift(b.stmt(b.id(import_node)));
1697
- }
1698
-
1699
- if (state.events.size > 0) {
1700
- program.body.push(
1701
- b.stmt(
1702
- b.call('_$_.delegate', b.array(Array.from(state.events).map((name) => b.literal(name)))),
1703
- ),
1704
- );
1705
- }
1706
-
1707
- const js = print(program, tsx(), {
1708
- sourceMapContent: source,
1709
- sourceMapSource: path.basename(filename),
1710
- });
1711
-
1712
- const css = render_stylesheets(state.stylesheets);
1713
-
1714
- return {
1715
- ast: program,
1716
- js,
1717
- css,
1718
- };
1619
+ const state = {
1620
+ imports: new Set(),
1621
+ events: new Set(),
1622
+ template: null,
1623
+ hoisted: [],
1624
+ setup: null,
1625
+ init: null,
1626
+ update: null,
1627
+ final: null,
1628
+ flush_node: null,
1629
+ scope: analysis.scope,
1630
+ scopes: analysis.scopes,
1631
+ stylesheets: [],
1632
+ to_ts,
1633
+ };
1634
+
1635
+ const program = /** @type {ESTree.Program} */ (
1636
+ walk(analysis.ast, { ...state, namespace: 'html' }, visitors)
1637
+ );
1638
+
1639
+ for (const hoisted of state.hoisted) {
1640
+ program.body.unshift(hoisted);
1641
+ }
1642
+
1643
+ for (const import_node of state.imports) {
1644
+ program.body.unshift(b.stmt(b.id(import_node)));
1645
+ }
1646
+
1647
+ if (state.events.size > 0) {
1648
+ program.body.push(
1649
+ b.stmt(
1650
+ b.call('_$_.delegate', b.array(Array.from(state.events).map((name) => b.literal(name)))),
1651
+ ),
1652
+ );
1653
+ }
1654
+
1655
+ const js = print(program, tsx(), {
1656
+ sourceMapContent: source,
1657
+ sourceMapSource: path.basename(filename),
1658
+ });
1659
+
1660
+ const css = render_stylesheets(state.stylesheets);
1661
+
1662
+ return {
1663
+ ast: program,
1664
+ js,
1665
+ css,
1666
+ };
1719
1667
  }