@tsrx/vue 0.0.20 → 0.0.22

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.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Vue compiler built on @tsrx/core",
4
4
  "license": "MIT",
5
5
  "author": "Dominic Gannaway",
6
- "version": "0.0.20",
6
+ "version": "0.0.22",
7
7
  "type": "module",
8
8
  "publishConfig": {
9
9
  "access": "public"
@@ -29,12 +29,13 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "esrap": "^2.1.0",
32
+ "is-reference": "^3.0.3",
32
33
  "zimmerframe": "^1.1.2",
33
- "@tsrx/core": "0.0.25"
34
+ "@tsrx/core": "0.0.27"
34
35
  },
35
36
  "peerDependencies": {
36
37
  "vue": ">=3.5",
37
- "vue-jsx-vapor": ">=3.2.10"
38
+ "vue-jsx-vapor": ">=3.2.12"
38
39
  },
39
40
  "devDependencies": {
40
41
  "@types/estree": "^1.0.8",
package/src/index.js CHANGED
@@ -22,7 +22,7 @@ export function parse(source, filename, options) {
22
22
  * @param {string} source
23
23
  * @param {string} [filename]
24
24
  * @param {{ collect?: boolean, loose?: boolean }} [options]
25
- * @returns {{ code: string, map: any, css: { code: string, hash: string } | null, errors: CompileError[] }}
25
+ * @returns {{ code: string, map: any, css: string, cssHash: string | null, errors: CompileError[] }}
26
26
  */
27
27
  export function compile(source, filename, options) {
28
28
  const errors = /** @type {CompileError[]} */ ([]);
@@ -63,6 +63,7 @@ export function compile_to_volar_mappings(source, filename, options) {
63
63
  const transformed = transform(ast, source, filename, {
64
64
  collect: true,
65
65
  loose: !!options?.loose,
66
+ typeOnly: true,
66
67
  errors,
67
68
  comments,
68
69
  });
package/src/transform.js CHANGED
@@ -1,13 +1,15 @@
1
1
  /** @import { JsxPlatform } from '@tsrx/core/types' */
2
2
 
3
+ import { walk } from 'zimmerframe';
4
+ import is_reference from 'is-reference';
3
5
  import {
4
6
  builders,
5
7
  clone_expression_node,
6
8
  clone_identifier,
9
+ create_generated_identifier,
7
10
  componentToFunctionDeclaration,
8
11
  createJsxTransform,
9
12
  error,
10
- identifier_to_jsx_name,
11
13
  setLocation,
12
14
  } from '@tsrx/core';
13
15
 
@@ -41,6 +43,7 @@ const vue_platform = {
41
43
  hooks: {
42
44
  initialState: () => ({
43
45
  needs_define_vapor_component: false,
46
+ needs_vapor_for: false,
44
47
  }),
45
48
  isTopLevelSetupCall(call_expression) {
46
49
  return is_vue_setup_call(call_expression);
@@ -55,18 +58,10 @@ const vue_platform = {
55
58
  preprocessElementAttributes(attrs, ctx, element) {
56
59
  return preprocess_ref_attributes(attrs, element, ctx);
57
60
  },
58
- renderForOf: (node, loop_params, body_statements) =>
59
- render_for_of_as_vapor_template(node, loop_params, body_statements),
61
+ renderForOf: (node, loop_params, body_statements, ctx) =>
62
+ render_for_of_as_vapor_for(node, loop_params, body_statements, ctx),
60
63
  createErrorBoundaryContent(try_content) {
61
- return {
62
- type: 'ArrowFunctionExpression',
63
- params: [],
64
- body: try_content.expression,
65
- async: false,
66
- generator: false,
67
- expression: true,
68
- metadata: { path: [] },
69
- };
64
+ return builders.arrow([], try_content.expression);
70
65
  },
71
66
  transformElementChildren(node, walked_children, raw_children, attributes, ctx) {
72
67
  return rewrite_host_text_or_html_children(
@@ -128,26 +123,12 @@ function component_to_vapor_component_declaration(component, transform_context,
128
123
  };
129
124
  /** @type {any} */ (component_id.metadata).hover = create_component_hover_replacement(fn.params);
130
125
 
131
- return setLocation(
132
- /** @type {any} */ ({
133
- type: 'VariableDeclaration',
134
- kind: 'const',
135
- declarations: [
136
- {
137
- type: 'VariableDeclarator',
138
- id: component_id,
139
- init: call,
140
- metadata: { path: [] },
141
- },
142
- ],
143
- metadata: {
144
- path: [],
145
- generated_helpers,
146
- generated_statics,
147
- },
148
- }),
149
- component,
150
- );
126
+ const declaration = builders.declaration('const', [builders.declarator(component_id, call)]);
127
+ Object.assign(/** @type {any} */ (declaration.metadata), {
128
+ generated_helpers,
129
+ generated_statics,
130
+ });
131
+ return setLocation(/** @type {any} */ (declaration), component);
151
132
  }
152
133
 
153
134
  /**
@@ -158,24 +139,17 @@ function component_to_vapor_component_declaration(component, transform_context,
158
139
  */
159
140
  function wrap_helper_component(helper_fn, helper_id, source_node) {
160
141
  return setLocation(
161
- /** @type {any} */ ({
162
- type: 'VariableDeclaration',
163
- kind: 'const',
164
- declarations: [
165
- {
166
- type: 'VariableDeclarator',
167
- id: clone_identifier(helper_id),
168
- init: create_define_vapor_component_call(
169
- function_declaration_to_expression(helper_fn),
170
- [],
171
- [],
172
- source_node,
173
- ),
174
- metadata: { path: [] },
175
- },
176
- ],
177
- metadata: { path: [] },
178
- }),
142
+ builders.declaration('const', [
143
+ builders.declarator(
144
+ clone_identifier(helper_id),
145
+ create_define_vapor_component_call(
146
+ function_declaration_to_expression(helper_fn),
147
+ [],
148
+ [],
149
+ source_node,
150
+ ),
151
+ ),
152
+ ]),
179
153
  source_node,
180
154
  );
181
155
  }
@@ -193,33 +167,22 @@ function create_define_vapor_component_call(
193
167
  generated_statics,
194
168
  source_node,
195
169
  ) {
196
- return setLocation(
197
- /** @type {any} */ ({
198
- type: 'CallExpression',
199
- callee: {
200
- type: 'Identifier',
201
- name: 'defineVaporComponent',
202
- metadata: { path: [] },
203
- },
204
- arguments: [fn_expression],
205
- optional: false,
206
- metadata: {
207
- path: [],
208
- generated_helpers,
209
- generated_statics,
210
- },
211
- }),
212
- source_node,
213
- );
170
+ const call = builders.call('defineVaporComponent', fn_expression);
171
+ Object.assign(/** @type {any} */ (call.metadata), {
172
+ generated_helpers,
173
+ generated_statics,
174
+ });
175
+ return setLocation(call, source_node);
214
176
  }
215
177
 
216
178
  /**
217
179
  * @param {any} node
218
180
  * @param {any[]} loop_params
219
181
  * @param {any[]} body_statements
182
+ * @param {any} transform_context
220
183
  * @returns {any | null}
221
184
  */
222
- function render_for_of_as_vapor_template(node, loop_params, body_statements) {
185
+ function render_for_of_as_vapor_for(node, loop_params, body_statements, transform_context) {
223
186
  if (body_statements.length !== 1) {
224
187
  return null;
225
188
  }
@@ -238,49 +201,43 @@ function render_for_of_as_vapor_template(node, loop_params, body_statements) {
238
201
  ? clone_expression_node(node.key)
239
202
  : (find_jsx_key_expression(rendered) ??
240
203
  (node.index ? clone_expression_node(node.index) : null));
241
- strip_top_level_jsx_keys(rendered);
242
- const children = rendered.type === 'JSXFragment' ? rendered.children : [rendered];
204
+
205
+ const slot = key_expression
206
+ ? create_keyed_vapor_for_slot(loop_params, rendered)
207
+ : { params: loop_params, body: rendered, expression: true };
208
+ if (!slot) {
209
+ return null;
210
+ }
211
+
212
+ transform_context.needs_vapor_for = true;
213
+
214
+ if (key_expression) {
215
+ strip_top_level_jsx_keys(slot.body);
216
+ }
217
+
243
218
  const attributes = [
244
- {
245
- type: 'JSXAttribute',
246
- name: { type: 'JSXIdentifier', name: 'v-for', metadata: { path: [] } },
247
- value: to_jsx_expression_container({
248
- type: 'BinaryExpression',
249
- operator: 'in',
250
- left: create_v_for_left(loop_params),
251
- right: clone_expression_node(node.right),
252
- metadata: { path: [] },
253
- }),
254
- metadata: { path: [] },
255
- },
219
+ builders.jsx_attribute(
220
+ builders.jsx_id('in'),
221
+ to_jsx_expression_container(clone_expression_node(node.right)),
222
+ ),
256
223
  ];
257
224
 
258
225
  if (key_expression) {
259
- attributes.push({
260
- type: 'JSXAttribute',
261
- name: { type: 'JSXIdentifier', name: 'key', metadata: { path: [] } },
262
- value: to_jsx_expression_container(key_expression),
263
- metadata: { path: [] },
264
- });
226
+ attributes.push(
227
+ builders.jsx_attribute(
228
+ builders.jsx_id('getKey'),
229
+ to_jsx_expression_container(create_loop_callback(loop_params, key_expression, true)),
230
+ ),
231
+ );
265
232
  }
266
233
 
267
- return to_jsx_expression_container({
268
- type: 'JSXElement',
269
- openingElement: {
270
- type: 'JSXOpeningElement',
271
- name: { type: 'JSXIdentifier', name: 'template', metadata: { path: [] } },
272
- attributes,
273
- selfClosing: false,
274
- metadata: { path: [] },
275
- },
276
- closingElement: {
277
- type: 'JSXClosingElement',
278
- name: { type: 'JSXIdentifier', name: 'template', metadata: { path: [] } },
279
- metadata: { path: [] },
280
- },
281
- children,
282
- metadata: { path: [] },
283
- });
234
+ return to_jsx_expression_container(
235
+ builders.jsx_element_fresh(
236
+ builders.jsx_opening_element(builders.jsx_id('VaporFor'), attributes),
237
+ builders.jsx_closing_element(builders.jsx_id('VaporFor')),
238
+ [to_jsx_expression_container(create_loop_callback(slot.params, slot.body, slot.expression))],
239
+ ),
240
+ );
284
241
  }
285
242
 
286
243
  /**
@@ -290,47 +247,20 @@ function render_for_of_as_vapor_template(node, loop_params, body_statements) {
290
247
  * @returns {any}
291
248
  */
292
249
  function render_for_of_as_flat_map(node, loop_params, rendered) {
293
- return to_jsx_expression_container({
294
- type: 'CallExpression',
295
- callee: {
296
- type: 'MemberExpression',
297
- object: clone_expression_node(node.right),
298
- property: { type: 'Identifier', name: 'flatMap', metadata: { path: [] } },
299
- computed: false,
300
- optional: false,
301
- metadata: { path: [] },
302
- },
303
- arguments: [
304
- {
305
- type: 'ArrowFunctionExpression',
306
- params: loop_params,
307
- body: {
308
- type: 'BlockStatement',
309
- body: [
310
- {
311
- type: 'ReturnStatement',
312
- argument: to_array_render_expression(rendered),
313
- metadata: { path: [] },
314
- },
315
- ],
316
- metadata: { path: [] },
317
- },
318
- async: false,
319
- generator: false,
320
- expression: false,
321
- metadata: { path: [] },
322
- },
323
- ],
324
- async: false,
325
- optional: false,
326
- metadata: { path: [] },
327
- });
250
+ return to_jsx_expression_container(
251
+ builders.call(
252
+ builders.member(clone_expression_node(node.right), 'flatMap'),
253
+ builders.arrow(
254
+ loop_params,
255
+ builders.block([builders.return(to_array_render_expression(rendered))]),
256
+ ),
257
+ ),
258
+ );
328
259
  }
329
260
 
330
261
  /**
331
- * `<template v-for>` preserves one rendered slot per source item in the current
332
- * Vue runtime path. Loop bodies that can return `null` need the shared callback
333
- * lowering so `continue` truly skips the iteration.
262
+ * Loop bodies that can return `null` need the shared callback lowering so
263
+ * `continue` truly skips the iteration.
334
264
  *
335
265
  * @param {any} node
336
266
  * @returns {boolean}
@@ -386,22 +316,6 @@ function to_array_render_expression(node) {
386
316
  return builders.array([node]);
387
317
  }
388
318
 
389
- /**
390
- * @param {any[]} loop_params
391
- * @returns {any}
392
- */
393
- function create_v_for_left(loop_params) {
394
- if (loop_params.length === 1) {
395
- return clone_expression_node(loop_params[0]);
396
- }
397
-
398
- return {
399
- type: 'SequenceExpression',
400
- expressions: loop_params.map((param) => clone_expression_node(param)),
401
- metadata: { path: [] },
402
- };
403
- }
404
-
405
319
  /**
406
320
  * @param {any} node
407
321
  * @returns {any | null}
@@ -450,6 +364,360 @@ function strip_top_level_jsx_keys(node) {
450
364
  }
451
365
  }
452
366
 
367
+ /**
368
+ * @param {any[]} loop_params
369
+ * @param {any} body
370
+ * @param {boolean} expression
371
+ * @returns {any}
372
+ */
373
+ function create_loop_callback(loop_params, body, expression) {
374
+ const callback = builders.arrow(
375
+ loop_params.map((param) => clone_expression_node(param)),
376
+ body,
377
+ );
378
+ callback.expression = expression;
379
+ return callback;
380
+ }
381
+
382
+ /**
383
+ * @param {any[]} loop_params
384
+ * @param {any} rendered
385
+ * @returns {{ params: any[], body: any, expression: boolean } | null}
386
+ */
387
+ function create_keyed_vapor_for_slot(loop_params, rendered) {
388
+ if (loop_params[0]?.type === 'Identifier') {
389
+ return {
390
+ params: loop_params,
391
+ body: rewrite_vapor_for_keyed_slot_refs(rendered, loop_params),
392
+ expression: true,
393
+ };
394
+ }
395
+
396
+ const item_ref = create_generated_identifier('__vapor_item');
397
+ const item_ref_value = create_value_member_expression(item_ref);
398
+ const replacements = create_pattern_replacements(loop_params[0], item_ref_value);
399
+ if (!replacements) {
400
+ return null;
401
+ }
402
+
403
+ const params = [item_ref, ...loop_params.slice(1)];
404
+ const rewritten_rendered = rewrite_vapor_for_keyed_slot_refs(
405
+ rendered,
406
+ loop_params.slice(1),
407
+ replacements,
408
+ );
409
+
410
+ return {
411
+ params,
412
+ body: rewritten_rendered,
413
+ expression: true,
414
+ };
415
+ }
416
+
417
+ /**
418
+ * Vue's `VaporFor` passes plain item values to unkeyed slots, but keyed slots
419
+ * receive shallow refs so row instances can update in place. Match that runtime
420
+ * shape by reading loop params through `.value` inside the slot body.
421
+ *
422
+ * @param {any} node
423
+ * @param {any[]} loop_params
424
+ * @param {Map<string, any>} [replacements]
425
+ * @returns {any}
426
+ */
427
+ function rewrite_vapor_for_keyed_slot_refs(node, loop_params, replacements = new Map()) {
428
+ const loop_param_names = new Set();
429
+ for (const param of loop_params) {
430
+ collect_pattern_names(param, loop_param_names);
431
+ }
432
+
433
+ if (loop_param_names.size === 0 && replacements.size === 0) {
434
+ return node;
435
+ }
436
+
437
+ return walk(
438
+ node,
439
+ { loop_param_names, shadowed_names: new Set() },
440
+ {
441
+ Identifier(identifier, { path, state, next }) {
442
+ const parent = path.at(-1);
443
+ if (
444
+ (state.loop_param_names.has(identifier.name) || replacements.has(identifier.name)) &&
445
+ !state.shadowed_names.has(identifier.name) &&
446
+ parent &&
447
+ is_runtime_reference(identifier, parent)
448
+ ) {
449
+ const replacement = replacements.get(identifier.name);
450
+ if (replacement) {
451
+ return clone_expression_node(replacement);
452
+ }
453
+ return create_value_member_expression(identifier);
454
+ }
455
+
456
+ return next();
457
+ },
458
+ FunctionDeclaration: rewrite_function_shadowed_refs,
459
+ FunctionExpression: rewrite_function_shadowed_refs,
460
+ ArrowFunctionExpression: rewrite_function_shadowed_refs,
461
+ BlockStatement: rewrite_block_shadowed_refs,
462
+ },
463
+ );
464
+ }
465
+
466
+ /**
467
+ * @param {any} identifier
468
+ * @param {any} parent
469
+ * @returns {boolean}
470
+ */
471
+ function is_runtime_reference(identifier, parent) {
472
+ if (parent.type === 'JSXExpressionContainer') {
473
+ return parent.expression === identifier;
474
+ }
475
+ if (parent.type === 'JSXAttribute') {
476
+ return parent.value === identifier || parent.value?.expression === identifier;
477
+ }
478
+ return is_reference(identifier, parent);
479
+ }
480
+
481
+ /**
482
+ * @param {any} pattern
483
+ * @param {any} source
484
+ * @returns {Map<string, any> | null}
485
+ */
486
+ function create_pattern_replacements(pattern, source) {
487
+ const replacements = new Map();
488
+ return collect_pattern_replacements(pattern, source, replacements) ? replacements : null;
489
+ }
490
+
491
+ /**
492
+ * @param {any} pattern
493
+ * @param {any} source
494
+ * @param {Map<string, any>} replacements
495
+ * @returns {boolean}
496
+ */
497
+ function collect_pattern_replacements(pattern, source, replacements) {
498
+ if (!pattern) return true;
499
+
500
+ switch (pattern.type) {
501
+ case 'Identifier':
502
+ replacements.set(pattern.name, source);
503
+ return true;
504
+ case 'ObjectPattern':
505
+ for (const property of pattern.properties || []) {
506
+ if (property.type === 'RestElement' || property.computed) {
507
+ return false;
508
+ }
509
+ if (
510
+ property.type !== 'Property' ||
511
+ !collect_pattern_replacements(
512
+ property.value,
513
+ create_property_member_expression(source, property.key),
514
+ replacements,
515
+ )
516
+ ) {
517
+ return false;
518
+ }
519
+ }
520
+ return true;
521
+ case 'ArrayPattern':
522
+ for (let index = 0; index < (pattern.elements || []).length; index++) {
523
+ const element = pattern.elements[index];
524
+ if (
525
+ element &&
526
+ !collect_pattern_replacements(
527
+ element,
528
+ create_index_member_expression(source, index),
529
+ replacements,
530
+ )
531
+ ) {
532
+ return false;
533
+ }
534
+ }
535
+ return true;
536
+ default:
537
+ return false;
538
+ }
539
+ }
540
+
541
+ /**
542
+ * @param {any} node
543
+ * @param {{ state: { loop_param_names: Set<string>, shadowed_names: Set<string> }, next: (state?: any) => any }} context
544
+ * @returns {any}
545
+ */
546
+ function rewrite_function_shadowed_refs(node, { state, next }) {
547
+ const shadowed_names = new Set(state.shadowed_names);
548
+ if (node.id) {
549
+ collect_pattern_names(node.id, shadowed_names);
550
+ }
551
+ for (const param of node.params || []) {
552
+ collect_pattern_names(param, shadowed_names);
553
+ }
554
+ collect_function_var_names(node.body, shadowed_names);
555
+ return next({ ...state, shadowed_names });
556
+ }
557
+
558
+ /**
559
+ * @param {any} node
560
+ * @param {{ state: { loop_param_names: Set<string>, shadowed_names: Set<string> }, next: (state?: any) => any }} context
561
+ * @returns {any}
562
+ */
563
+ function rewrite_block_shadowed_refs(node, { state, next }) {
564
+ const shadowed_names = new Set(state.shadowed_names);
565
+ collect_block_lexical_names(node.body, shadowed_names);
566
+ return next({ ...state, shadowed_names });
567
+ }
568
+
569
+ /**
570
+ * @param {any[]} statements
571
+ * @param {Set<string>} names
572
+ * @returns {void}
573
+ */
574
+ function collect_block_lexical_names(statements, names) {
575
+ for (const statement of statements || []) {
576
+ if (statement.type === 'VariableDeclaration' && statement.kind !== 'var') {
577
+ for (const declaration of statement.declarations || []) {
578
+ collect_pattern_names(declaration.id, names);
579
+ }
580
+ continue;
581
+ }
582
+
583
+ if (
584
+ (statement.type === 'FunctionDeclaration' || statement.type === 'ClassDeclaration') &&
585
+ statement.id
586
+ ) {
587
+ collect_pattern_names(statement.id, names);
588
+ }
589
+ }
590
+ }
591
+
592
+ /**
593
+ * @param {any} node
594
+ * @param {Set<string>} names
595
+ * @returns {void}
596
+ */
597
+ function collect_function_var_names(node, names) {
598
+ if (!node || typeof node !== 'object') return;
599
+
600
+ if (Array.isArray(node)) {
601
+ for (const child of node) {
602
+ collect_function_var_names(child, names);
603
+ }
604
+ return;
605
+ }
606
+
607
+ if (
608
+ node.type === 'FunctionDeclaration' ||
609
+ node.type === 'FunctionExpression' ||
610
+ node.type === 'ArrowFunctionExpression' ||
611
+ node.type === 'ClassDeclaration' ||
612
+ node.type === 'ClassExpression'
613
+ ) {
614
+ return;
615
+ }
616
+
617
+ if (node.type === 'VariableDeclaration' && node.kind === 'var') {
618
+ for (const declaration of node.declarations || []) {
619
+ collect_pattern_names(declaration.id, names);
620
+ }
621
+ }
622
+
623
+ for (const key of Object.keys(node)) {
624
+ if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
625
+ continue;
626
+ }
627
+ collect_function_var_names(node[key], names);
628
+ }
629
+ }
630
+
631
+ /**
632
+ * @param {any} node
633
+ * @param {Set<string>} names
634
+ * @returns {void}
635
+ */
636
+ function collect_pattern_names(node, names) {
637
+ if (!node) return;
638
+
639
+ switch (node.type) {
640
+ case 'Identifier':
641
+ names.add(node.name);
642
+ break;
643
+ case 'RestElement':
644
+ collect_pattern_names(node.argument, names);
645
+ break;
646
+ case 'AssignmentPattern':
647
+ collect_pattern_names(node.left, names);
648
+ break;
649
+ case 'ArrayPattern':
650
+ for (const element of node.elements || []) {
651
+ collect_pattern_names(element, names);
652
+ }
653
+ break;
654
+ case 'ObjectPattern':
655
+ for (const property of node.properties || []) {
656
+ collect_pattern_names(property, names);
657
+ }
658
+ break;
659
+ case 'Property':
660
+ collect_pattern_names(node.value, names);
661
+ break;
662
+ }
663
+ }
664
+
665
+ /**
666
+ * @param {any} object
667
+ * @param {any} key
668
+ * @returns {any}
669
+ */
670
+ function create_property_member_expression(object, key) {
671
+ if (key?.type === 'Identifier') {
672
+ return create_member_expression(
673
+ clone_expression_node(object),
674
+ clone_identifier(key),
675
+ false,
676
+ key,
677
+ );
678
+ }
679
+
680
+ return create_member_expression(
681
+ clone_expression_node(object),
682
+ clone_expression_node(key),
683
+ true,
684
+ key,
685
+ );
686
+ }
687
+
688
+ /**
689
+ * @param {any} object
690
+ * @param {number} index
691
+ * @returns {any}
692
+ */
693
+ function create_index_member_expression(object, index) {
694
+ return create_member_expression(
695
+ clone_expression_node(object),
696
+ builders.literal(index),
697
+ true,
698
+ object,
699
+ );
700
+ }
701
+
702
+ /**
703
+ * @param {any} identifier
704
+ * @returns {any}
705
+ */
706
+ function create_value_member_expression(identifier) {
707
+ return create_member_expression(clone_identifier(identifier), 'value', false, identifier);
708
+ }
709
+
710
+ /**
711
+ * @param {any} object
712
+ * @param {any} property
713
+ * @param {boolean} computed
714
+ * @param {any} source_node
715
+ * @returns {any}
716
+ */
717
+ function create_member_expression(object, property, computed, source_node) {
718
+ return builders.member(object, property, computed, false, source_node);
719
+ }
720
+
453
721
  /**
454
722
  * @param {any} fn
455
723
  * @returns {any}
@@ -652,16 +920,7 @@ function has_dom_content_attribute(attributes, name) {
652
920
  * @returns {any}
653
921
  */
654
922
  function create_jsx_attribute(name, value, source_node) {
655
- return setLocation(
656
- /** @type {any} */ ({
657
- type: 'JSXAttribute',
658
- name: identifier_to_jsx_name(builders.id(name)),
659
- value,
660
- shorthand: false,
661
- metadata: { path: [] },
662
- }),
663
- source_node,
664
- );
923
+ return builders.jsx_attribute(builders.jsx_id(name), value, false, source_node);
665
924
  }
666
925
 
667
926
  /**
@@ -670,12 +929,7 @@ function create_jsx_attribute(name, value, source_node) {
670
929
  * @returns {any}
671
930
  */
672
931
  function to_jsx_expression_container(expression, source_node = expression) {
673
- void source_node;
674
- return {
675
- type: 'JSXExpressionContainer',
676
- expression,
677
- metadata: { path: [] },
678
- };
932
+ return builders.jsx_expression_container(expression, source_node);
679
933
  }
680
934
 
681
935
  /**
@@ -741,6 +995,10 @@ function inject_vue_imports(program, transform_context) {
741
995
  ensure_named_import(program, 'vue-jsx-vapor', 'defineVaporComponent');
742
996
  }
743
997
 
998
+ if (transform_context.needs_vapor_for) {
999
+ ensure_named_import(program, 'vue-jsx-vapor', 'VaporFor');
1000
+ }
1001
+
744
1002
  if (transform_context.needs_suspense) {
745
1003
  ensure_named_import(program, 'vue', 'Suspense');
746
1004
  }
@@ -782,7 +1040,7 @@ function ensure_named_import(program, source, name, local = name) {
782
1040
  return;
783
1041
  }
784
1042
 
785
- program.body.unshift(create_import_declaration(source, [create_import_specifier(name, local)]));
1043
+ program.body.unshift(builders.imports([[name, local, 'value']], source));
786
1044
  }
787
1045
 
788
1046
  /**
@@ -799,19 +1057,3 @@ function create_import_specifier(name, local = name) {
799
1057
  metadata: { path: [] },
800
1058
  };
801
1059
  }
802
-
803
- /**
804
- * @param {string} source
805
- * @param {any[]} specifiers
806
- * @returns {any}
807
- */
808
- function create_import_declaration(source, specifiers) {
809
- return {
810
- type: 'ImportDeclaration',
811
- attributes: [],
812
- specifiers,
813
- importKind: 'value',
814
- source: builders.literal(source),
815
- metadata: { path: [] },
816
- };
817
- }
package/types/index.d.ts CHANGED
@@ -1,21 +1,8 @@
1
1
  import type { Program } from 'estree';
2
- import type { CompileError, ParseOptions, VolarMappingsResult } from '@tsrx/core/types';
2
+ import type { CompileFn, ParseOptions, VolarCompileFn } from '@tsrx/core/types';
3
3
 
4
4
  export function parse(source: string, filename?: string, options?: ParseOptions): Program;
5
5
 
6
- export function compile(
7
- source: string,
8
- filename?: string,
9
- options?: { collect?: boolean; loose?: boolean },
10
- ): {
11
- code: string;
12
- map: unknown;
13
- css: { code: string; hash: string } | null;
14
- errors: CompileError[];
15
- };
6
+ export const compile: CompileFn;
16
7
 
17
- export function compile_to_volar_mappings(
18
- source: string,
19
- filename?: string,
20
- options?: ParseOptions,
21
- ): VolarMappingsResult;
8
+ export const compile_to_volar_mappings: VolarCompileFn;