@tsrx/vue 0.0.22 → 0.1.0

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.22",
6
+ "version": "0.1.0",
7
7
  "type": "module",
8
8
  "publishConfig": {
9
9
  "access": "public"
@@ -22,16 +22,16 @@
22
22
  "types": "./types/error-boundary.d.ts",
23
23
  "default": "./src/error-boundary.js"
24
24
  },
25
- "./merge-refs": {
26
- "types": "./types/merge-refs.d.ts",
27
- "default": "./src/merge-refs.js"
25
+ "./ref": {
26
+ "types": "./types/ref.d.ts",
27
+ "default": "./src/ref.js"
28
28
  }
29
29
  },
30
30
  "dependencies": {
31
31
  "esrap": "^2.1.0",
32
32
  "is-reference": "^3.0.3",
33
33
  "zimmerframe": "^1.1.2",
34
- "@tsrx/core": "0.0.27"
34
+ "@tsrx/core": "0.1.0"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "vue": ">=3.5",
package/src/index.js CHANGED
@@ -4,6 +4,8 @@
4
4
  import { createVolarMappingsResult, dedupeMappings, parseModule } from '@tsrx/core';
5
5
  import { transform } from './transform.js';
6
6
 
7
+ export { isRefProp } from './ref.js';
8
+
7
9
  /**
8
10
  * Parse tsrx-vue source code to an ESTree AST.
9
11
  * @param {string} source
package/src/ref.js ADDED
@@ -0,0 +1 @@
1
+ export * from '@tsrx/core/runtime/ref';
package/src/transform.js CHANGED
@@ -6,11 +6,15 @@ import {
6
6
  builders,
7
7
  clone_expression_node,
8
8
  clone_identifier,
9
+ CREATE_REF_PROP_INTERNAL_NAME,
9
10
  create_generated_identifier,
10
11
  componentToFunctionDeclaration,
11
12
  createJsxTransform,
12
13
  error,
14
+ MERGE_REFS_INTERNAL_NAME,
15
+ NORMALIZE_SPREAD_PROPS_INTERNAL_NAME,
13
16
  setLocation,
17
+ toJsxAttribute,
14
18
  } from '@tsrx/core';
15
19
 
16
20
  /**
@@ -27,12 +31,14 @@ const vue_platform = {
27
31
  imports: {
28
32
  suspense: 'vue',
29
33
  errorBoundary: '@tsrx/vue/error-boundary',
30
- mergeRefs: '@tsrx/vue/merge-refs',
34
+ mergeRefs: '@tsrx/vue/ref',
35
+ refProp: '@tsrx/vue/ref',
31
36
  },
32
37
  jsx: {
33
38
  rewriteClassAttr: false,
34
39
  acceptedTsxKinds: ['vue'],
35
40
  multiRefStrategy: 'merge-refs',
41
+ hostSpreadRefStrategy: 'explicit-ref-attr',
36
42
  },
37
43
  validation: {
38
44
  requireUseServerForAwait: true,
@@ -58,6 +64,13 @@ const vue_platform = {
58
64
  preprocessElementAttributes(attrs, ctx, element) {
59
65
  return preprocess_ref_attributes(attrs, element, ctx);
60
66
  },
67
+ transformElementAttributes(attrs, ctx, element) {
68
+ const result = attrs.map((attr) => toJsxAttribute(attr, ctx));
69
+ if (!ctx.typeOnly || is_component_like_element(element)) {
70
+ return result;
71
+ }
72
+ return result.map(mark_type_only_host_ref_attribute);
73
+ },
61
74
  renderForOf: (node, loop_params, body_statements, ctx) =>
62
75
  render_for_of_as_vapor_for(node, loop_params, body_statements, ctx),
63
76
  createErrorBoundaryContent(try_content) {
@@ -93,6 +106,34 @@ const vue_platform = {
93
106
 
94
107
  export const transform = createJsxTransform(vue_platform);
95
108
 
109
+ /**
110
+ * Vue's `VNodeRef` type is wider than TSRX host refs because it also supports
111
+ * component instances and null teardown values. In editor-only TSX, keep the ref
112
+ * expression unchanged but stop TypeScript verification from reporting that
113
+ * Vue-specific assignability diagnostic on the generated `ref` prop token.
114
+ *
115
+ * @param {any} attr
116
+ * @returns {any}
117
+ */
118
+ function mark_type_only_host_ref_attribute(attr) {
119
+ if (
120
+ !attr ||
121
+ attr.type !== 'JSXAttribute' ||
122
+ attr.name?.type !== 'JSXIdentifier' ||
123
+ attr.name.name !== 'ref'
124
+ ) {
125
+ return attr;
126
+ }
127
+
128
+ return {
129
+ ...attr,
130
+ name: {
131
+ ...attr.name,
132
+ metadata: { ...(attr.name.metadata || {}), disable_verification: true },
133
+ },
134
+ };
135
+ }
136
+
96
137
  /**
97
138
  * @param {any} component
98
139
  * @param {any} transform_context
@@ -817,6 +858,7 @@ function preprocess_ref_attributes(attrs, element, transform_context) {
817
858
  if (!is_component_like_element(element)) {
818
859
  return attrs;
819
860
  }
861
+ const result = [];
820
862
  for (const attr of attrs) {
821
863
  if (attr?.type === 'RefAttribute') {
822
864
  error(
@@ -827,8 +869,72 @@ function preprocess_ref_attributes(attrs, element, transform_context) {
827
869
  transform_context?.comments,
828
870
  );
829
871
  }
872
+ if (!transform_context.typeOnly && is_vue_named_ref_attribute(attr)) {
873
+ result.push(create_vue_named_ref_spread(attr));
874
+ continue;
875
+ }
876
+ result.push(attr);
877
+ }
878
+ return result;
879
+ }
880
+
881
+ /**
882
+ * Vue's JSX transform treats prop names ending in `ref` as template-ref
883
+ * sugar on components. Keep named TSRX refs as ordinary runtime props by
884
+ * hiding the static prop name behind an object spread before Vue sees the JSX.
885
+ * Type-only virtual TSX skips that spread so Volar can offer completions on
886
+ * the real component prop name.
887
+ *
888
+ * @param {any} attr
889
+ * @returns {boolean}
890
+ */
891
+ function is_vue_named_ref_attribute(attr) {
892
+ const attr_name = get_vue_attribute_name(attr);
893
+ const value = get_vue_attribute_expression(attr);
894
+ return !!(
895
+ attr_name &&
896
+ attr_name !== 'ref' &&
897
+ (attr?.type === 'Attribute' || attr?.type === 'JSXAttribute') &&
898
+ (value?.type === 'RefExpression' ||
899
+ (value?.type === 'CallExpression' &&
900
+ value.callee?.type === 'Identifier' &&
901
+ value.callee.name === CREATE_REF_PROP_INTERNAL_NAME))
902
+ );
903
+ }
904
+
905
+ /**
906
+ * @param {any} attr
907
+ * @returns {any}
908
+ */
909
+ function create_vue_named_ref_spread(attr) {
910
+ const attr_name = get_vue_attribute_name(attr);
911
+ const value = get_vue_attribute_expression(attr);
912
+ if (attr_name === null) return attr;
913
+ const prop = builders.prop('init', builders.key(attr_name), value, false, false);
914
+ return builders.jsx_spread_attribute(builders.object([prop], attr), attr);
915
+ }
916
+
917
+ /**
918
+ * @param {any} attr
919
+ * @returns {string | null}
920
+ */
921
+ function get_vue_attribute_name(attr) {
922
+ if (attr?.type === 'Attribute') {
923
+ return typeof attr.name === 'string' ? attr.name : (attr.name?.name ?? null);
924
+ }
925
+ if (attr?.type === 'JSXAttribute') {
926
+ return attr.name?.type === 'JSXIdentifier' ? attr.name.name : null;
830
927
  }
831
- return attrs;
928
+ return null;
929
+ }
930
+
931
+ /**
932
+ * @param {any} attr
933
+ * @returns {any}
934
+ */
935
+ function get_vue_attribute_expression(attr) {
936
+ const value = attr?.value;
937
+ return value?.type === 'JSXExpressionContainer' ? value.expression : value;
832
938
  }
833
939
 
834
940
  /**
@@ -1008,7 +1114,20 @@ function inject_vue_imports(program, transform_context) {
1008
1114
  }
1009
1115
 
1010
1116
  if (transform_context.needs_merge_refs) {
1011
- ensure_named_import(program, '@tsrx/vue/merge-refs', 'mergeRefs', '__mergeRefs');
1117
+ ensure_named_import(program, '@tsrx/vue/ref', 'mergeRefs', MERGE_REFS_INTERNAL_NAME);
1118
+ }
1119
+
1120
+ if (transform_context.needs_ref_prop) {
1121
+ ensure_named_import(program, '@tsrx/vue/ref', 'create_ref_prop', CREATE_REF_PROP_INTERNAL_NAME);
1122
+ }
1123
+
1124
+ if (transform_context.needs_normalize_spread_props) {
1125
+ ensure_named_import(
1126
+ program,
1127
+ '@tsrx/vue/ref',
1128
+ 'normalize_spread_props',
1129
+ NORMALIZE_SPREAD_PROPS_INTERNAL_NAME,
1130
+ );
1012
1131
  }
1013
1132
  }
1014
1133
 
package/types/index.d.ts CHANGED
@@ -3,6 +3,8 @@ 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 { isRefProp } from './ref.js';
7
+
6
8
  export const compile: CompileFn;
7
9
 
8
10
  export const compile_to_volar_mappings: VolarCompileFn;
package/types/ref.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from '@tsrx/core/runtime/ref';
package/src/merge-refs.js DELETED
@@ -1 +0,0 @@
1
- export { mergeRefs } from '@tsrx/core/runtime/merge-refs';
@@ -1 +0,0 @@
1
- export { mergeRefs } from '@tsrx/core/runtime/merge-refs';