ripple 0.2.46 → 0.2.48

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