@tsrx/vue 0.0.12 → 0.0.14

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.12",
6
+ "version": "0.0.14",
7
7
  "type": "module",
8
8
  "publishConfig": {
9
9
  "access": "public"
@@ -19,13 +19,18 @@
19
19
  "default": "./src/index.js"
20
20
  },
21
21
  "./error-boundary": {
22
+ "types": "./types/error-boundary.d.ts",
22
23
  "default": "./src/error-boundary.js"
24
+ },
25
+ "./merge-refs": {
26
+ "types": "./types/merge-refs.d.ts",
27
+ "default": "./src/merge-refs.js"
23
28
  }
24
29
  },
25
30
  "dependencies": {
26
31
  "esrap": "^2.1.0",
27
32
  "zimmerframe": "^1.1.2",
28
- "@tsrx/core": "0.0.17"
33
+ "@tsrx/core": "0.0.19"
29
34
  },
30
35
  "peerDependencies": {
31
36
  "vue": ">=3.5",
package/src/index.js CHANGED
@@ -26,9 +26,19 @@ export function parse(source, filename, options) {
26
26
  */
27
27
  export function compile(source, filename, options) {
28
28
  const errors = /** @type {CompileError[]} */ ([]);
29
+ const comments = /** @type {AST.CommentWithLocation[]} */ ([]);
29
30
  const collect = !!options?.loose;
30
- const ast = parseModule(source, filename, collect ? { loose: true, errors } : undefined);
31
- const { ast: _ast, ...result } = transform(ast, source, filename);
31
+ const ast = parseModule(
32
+ source,
33
+ filename,
34
+ collect ? { loose: true, errors, comments } : undefined,
35
+ );
36
+ const { ast: _ast, ...result } = transform(
37
+ ast,
38
+ source,
39
+ filename,
40
+ collect ? { loose: true, errors, comments } : undefined,
41
+ );
32
42
  return { ...result, errors };
33
43
  }
34
44
 
@@ -42,8 +52,13 @@ export function compile(source, filename, options) {
42
52
  */
43
53
  export function compile_to_volar_mappings(source, filename, options) {
44
54
  const errors = /** @type {import('@tsrx/core/types').CompileError[]} */ ([]);
45
- const ast = parseModule(source, filename, { ...options, errors });
46
- const transformed = transform(ast, source, filename);
55
+ const comments = /** @type {AST.CommentWithLocation[]} */ ([]);
56
+ const ast = parseModule(source, filename, { ...options, errors, comments });
57
+ const transformed = transform(ast, source, filename, {
58
+ loose: true,
59
+ errors,
60
+ comments,
61
+ });
47
62
  const result = createVolarMappingsResult({
48
63
  ast: transformed.ast,
49
64
  ast_from_source: ast,
@@ -0,0 +1 @@
1
+ export { mergeRefs } from '@tsrx/core/runtime/merge-refs';
package/src/transform.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  clone_identifier,
7
7
  componentToFunctionDeclaration,
8
8
  createJsxTransform,
9
- create_compile_error,
9
+ error,
10
10
  identifier_to_jsx_name,
11
11
  setLocation,
12
12
  } from '@tsrx/core';
@@ -25,10 +25,12 @@ const vue_platform = {
25
25
  imports: {
26
26
  suspense: 'vue',
27
27
  errorBoundary: '@tsrx/vue/error-boundary',
28
+ mergeRefs: '@tsrx/vue/merge-refs',
28
29
  },
29
30
  jsx: {
30
31
  rewriteClassAttr: false,
31
32
  acceptedTsxKinds: ['vue'],
33
+ multiRefStrategy: 'merge-refs',
32
34
  },
33
35
  validation: {
34
36
  requireUseServerForAwait: true,
@@ -66,13 +68,22 @@ const vue_platform = {
66
68
  metadata: { path: [] },
67
69
  };
68
70
  },
69
- transformElementChildren(node, walked_children, raw_children, attributes) {
70
- return rewrite_host_text_or_html_children(node, walked_children, raw_children, attributes);
71
+ transformElementChildren(node, walked_children, raw_children, attributes, ctx) {
72
+ return rewrite_host_text_or_html_children(
73
+ node,
74
+ walked_children,
75
+ raw_children,
76
+ attributes,
77
+ ctx,
78
+ );
71
79
  },
72
- validateComponentAwait(await_expression) {
73
- throw create_compile_error(
74
- await_expression,
80
+ validateComponentAwait(await_expression, _component, ctx) {
81
+ error(
75
82
  '`await` is not yet supported in Vue TSRX components.',
83
+ ctx?.filename ?? null,
84
+ await_expression,
85
+ ctx?.errors,
86
+ ctx?.comments,
76
87
  );
77
88
  },
78
89
  componentToFunction(component, ctx, helper_state) {
@@ -404,77 +415,33 @@ function is_vue_setup_call(call_expression) {
404
415
  }
405
416
 
406
417
  /**
418
+ * Reject `{ref expr}` on composite (component-like) elements: Vue component
419
+ * refs resolve to the component instance, not the rendered DOM node, so
420
+ * Ripple-style component refs don't have a meaningful DOM target. Multi-ref
421
+ * merging itself is handled by the shared `merge_duplicate_refs` pass via
422
+ * the platform's `multiRefStrategy: 'merge-refs'` config.
423
+ *
407
424
  * @param {any[]} attrs
408
425
  * @param {any} element
409
426
  * @param {any} transform_context
410
427
  * @returns {any[]}
411
428
  */
412
429
  function preprocess_ref_attributes(attrs, element, transform_context) {
413
- /** @type {any[]} */
414
- const result = [];
415
- /** @type {any[]} */
416
- const ref_attrs = [];
417
-
430
+ if (!is_component_like_element(element)) {
431
+ return attrs;
432
+ }
418
433
  for (const attr of attrs) {
419
- if (!attr) continue;
420
- if (attr.type === 'RefAttribute') {
421
- ref_attrs.push(attr);
422
- continue;
434
+ if (attr?.type === 'RefAttribute') {
435
+ error(
436
+ '`{ref ...}` on the Vue target is only supported on host elements. Vue component refs resolve to component instances rather than the rendered DOM node, so Ripple-style component refs are not supported here.',
437
+ transform_context?.filename ?? null,
438
+ attr,
439
+ transform_context?.errors,
440
+ transform_context?.comments,
441
+ );
423
442
  }
424
- result.push(attr);
425
- }
426
-
427
- if (ref_attrs.length > 0 && is_component_like_element(element)) {
428
- throw create_compile_error(
429
- ref_attrs[0],
430
- '`{ref ...}` on the Vue target is only supported on host elements. Vue component refs resolve to component instances rather than the rendered DOM node, so Ripple-style component refs are not supported here.',
431
- );
432
443
  }
433
-
434
- if (ref_attrs.length === 1) {
435
- result.push(ref_attrs[0]);
436
- } else if (ref_attrs.length > 1) {
437
- result.push({
438
- type: 'RefAttribute',
439
- argument: create_combined_ref_callback(ref_attrs),
440
- loc: ref_attrs[0].loc,
441
- metadata: { path: [] },
442
- });
443
- }
444
-
445
- return result;
446
- }
447
-
448
- /**
449
- * @param {any[]} ref_attrs
450
- * @returns {any}
451
- */
452
- function create_combined_ref_callback(ref_attrs) {
453
- const node_id = builders.id('node');
454
-
455
- return {
456
- type: 'ArrowFunctionExpression',
457
- params: [node_id],
458
- body: {
459
- type: 'BlockStatement',
460
- body: ref_attrs.map((attr) => ({
461
- type: 'ExpressionStatement',
462
- expression: {
463
- type: 'CallExpression',
464
- callee: attr.argument,
465
- arguments: [clone_identifier(node_id)],
466
- optional: false,
467
- metadata: { path: [] },
468
- },
469
- metadata: { path: [] },
470
- })),
471
- metadata: { path: [] },
472
- },
473
- expression: false,
474
- async: false,
475
- generator: false,
476
- metadata: { path: [] },
477
- };
444
+ return attrs;
478
445
  }
479
446
 
480
447
  /**
@@ -482,9 +449,16 @@ function create_combined_ref_callback(ref_attrs) {
482
449
  * @param {any[]} walked_children
483
450
  * @param {any[]} raw_children
484
451
  * @param {any[]} attributes
452
+ * @param {any} [transform_context]
485
453
  * @returns {{ children: any[]; selfClosing?: boolean } | null}
486
454
  */
487
- function rewrite_host_text_or_html_children(node, walked_children, raw_children, attributes) {
455
+ function rewrite_host_text_or_html_children(
456
+ node,
457
+ walked_children,
458
+ raw_children,
459
+ attributes,
460
+ transform_context,
461
+ ) {
488
462
  const source_children = raw_children || walked_children;
489
463
  const is_composite = is_component_like_element(node);
490
464
  const html_children = source_children.filter((child) => child?.type === 'Html');
@@ -496,9 +470,12 @@ function rewrite_host_text_or_html_children(node, walked_children, raw_children,
496
470
  has_dom_content_attribute(attributes, 'innerHTML') ||
497
471
  has_dom_content_attribute(attributes, 'textContent')
498
472
  ) {
499
- throw create_compile_error(
500
- html_children[0],
473
+ error(
501
474
  '`{html ...}` on the Vue target is only supported as the sole child of a host element. Use `innerHTML={...}` as an element attribute when you need the explicit prop form.',
475
+ transform_context?.filename ?? null,
476
+ html_children[0],
477
+ transform_context?.errors,
478
+ transform_context?.comments,
502
479
  );
503
480
  }
504
481
 
@@ -652,15 +629,20 @@ function inject_vue_imports(program, transform_context) {
652
629
  if (transform_context.needs_error_boundary) {
653
630
  ensure_named_import(program, '@tsrx/vue/error-boundary', 'TsrxErrorBoundary');
654
631
  }
632
+
633
+ if (transform_context.needs_merge_refs) {
634
+ ensure_named_import(program, '@tsrx/vue/merge-refs', 'mergeRefs', '__mergeRefs');
635
+ }
655
636
  }
656
637
 
657
638
  /**
658
639
  * @param {import('estree').Program} program
659
640
  * @param {string} source
660
641
  * @param {string} name
642
+ * @param {string} [local]
661
643
  * @returns {void}
662
644
  */
663
- function ensure_named_import(program, source, name) {
645
+ function ensure_named_import(program, source, name, local = name) {
664
646
  for (const statement of program.body) {
665
647
  if (statement.type !== 'ImportDeclaration' || statement.source?.value !== source) {
666
648
  continue;
@@ -670,28 +652,30 @@ function ensure_named_import(program, source, name) {
670
652
  (/** @type {any} */ specifier) =>
671
653
  specifier.type === 'ImportSpecifier' &&
672
654
  specifier.imported?.type === 'Identifier' &&
673
- specifier.imported.name === name,
655
+ specifier.imported.name === name &&
656
+ specifier.local?.name === local,
674
657
  );
675
658
 
676
659
  if (!has_specifier) {
677
- statement.specifiers.push(create_import_specifier(name));
660
+ statement.specifiers.push(create_import_specifier(name, local));
678
661
  }
679
662
 
680
663
  return;
681
664
  }
682
665
 
683
- program.body.unshift(create_import_declaration(source, [create_import_specifier(name)]));
666
+ program.body.unshift(create_import_declaration(source, [create_import_specifier(name, local)]));
684
667
  }
685
668
 
686
669
  /**
687
670
  * @param {string} name
671
+ * @param {string} [local]
688
672
  * @returns {any}
689
673
  */
690
- function create_import_specifier(name) {
674
+ function create_import_specifier(name, local = name) {
691
675
  return {
692
676
  type: 'ImportSpecifier',
693
677
  imported: builders.id(name),
694
- local: builders.id(name),
678
+ local: builders.id(local),
695
679
  importKind: 'value',
696
680
  metadata: { path: [] },
697
681
  };
@@ -0,0 +1,11 @@
1
+ export interface TsrxErrorBoundaryProps {
2
+ content: () => any;
3
+ fallback: (error: unknown, reset: () => void) => any;
4
+ }
5
+
6
+ export interface TsrxErrorBoundaryComponent {
7
+ (props: TsrxErrorBoundaryProps): any;
8
+ __setup(): void;
9
+ }
10
+
11
+ export const TsrxErrorBoundary: TsrxErrorBoundaryComponent;
@@ -0,0 +1 @@
1
+ export { mergeRefs } from '@tsrx/core/runtime/merge-refs';