@tsrx/vue 0.1.16 → 0.1.18

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 (2) hide show
  1. package/package.json +2 -2
  2. package/src/transform.js +125 -129
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.1.16",
6
+ "version": "0.1.18",
7
7
  "type": "module",
8
8
  "publishConfig": {
9
9
  "access": "public"
@@ -35,7 +35,7 @@
35
35
  "esrap": "^2.2.8",
36
36
  "is-reference": "^3.0.3",
37
37
  "zimmerframe": "^1.1.2",
38
- "@tsrx/core": "0.1.16"
38
+ "@tsrx/core": "0.1.18"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "vue": ">=3.5",
package/src/transform.js CHANGED
@@ -8,16 +8,14 @@ import {
8
8
  clone_expression_node,
9
9
  clone_identifier,
10
10
  contains_component_jsx,
11
- CREATE_REF_PROP_INTERNAL_NAME,
12
11
  createHookSafeHelper,
13
12
  create_generated_identifier,
14
- componentToFunctionDeclaration,
15
13
  createJsxTransform,
16
14
  error,
17
15
  is_component_like_element,
18
16
  MERGE_REFS_INTERNAL_NAME,
17
+ NORMALIZE_SPREAD_PROPS_FOR_REF_ATTR_INTERNAL_NAME,
19
18
  NORMALIZE_SPREAD_PROPS_INTERNAL_NAME,
20
- rewriteHostHtmlChildren as rewrite_host_html_children,
21
19
  setLocation,
22
20
  toJsxAttribute,
23
21
  } from '@tsrx/core';
@@ -68,11 +66,14 @@ const vue_platform = {
68
66
  ctx.needs_define_vapor_component = true;
69
67
  return wrap_helper_component(helper_fn, helper_id, source_node);
70
68
  },
69
+ wrapNativeFunctionComponent(fn, ctx, path) {
70
+ return wrap_native_function_component(fn, ctx, path);
71
+ },
71
72
  canHoistStaticNode(node) {
72
73
  return !contains_component_jsx(node);
73
74
  },
74
- preprocessElementAttributes(attrs, ctx, element) {
75
- return preprocess_ref_attributes(attrs, element, ctx);
75
+ preprocessElementAttributes(attrs, ctx) {
76
+ return preprocess_ref_attributes(attrs, ctx);
76
77
  },
77
78
  transformElementAttributes(attrs, ctx, element) {
78
79
  const result = attrs.map((attr) => toJsxAttribute(attr, ctx));
@@ -130,13 +131,7 @@ const vue_platform = {
130
131
  return builders.arrow([], jsx_child_to_expression(try_content));
131
132
  },
132
133
  transformElementChildren(node, walked_children, raw_children, attributes, ctx) {
133
- return rewrite_host_text_or_html_children(
134
- node,
135
- walked_children,
136
- raw_children,
137
- attributes,
138
- ctx,
139
- );
134
+ return rewrite_host_text_children(node, walked_children, raw_children, attributes);
140
135
  },
141
136
  validateComponentAwait(await_expression, _component, ctx) {
142
137
  error(
@@ -147,10 +142,6 @@ const vue_platform = {
147
142
  ctx?.comments,
148
143
  );
149
144
  },
150
- componentToFunction(component, ctx, helper_state) {
151
- ctx.needs_define_vapor_component = true;
152
- return component_to_vapor_component_declaration(component, ctx, helper_state);
153
- },
154
145
  injectImports(program, ctx) {
155
146
  inject_vue_imports(program, ctx);
156
147
  },
@@ -388,43 +379,6 @@ function mark_type_only_host_ref_attribute(attr) {
388
379
  };
389
380
  }
390
381
 
391
- /**
392
- * @param {any} component
393
- * @param {any} transform_context
394
- * @param {any} helper_state
395
- * @returns {any}
396
- */
397
- function component_to_vapor_component_declaration(component, transform_context, helper_state) {
398
- const fn = componentToFunctionDeclaration(component, transform_context, helper_state);
399
- const generated_helpers = helper_state?.helpers || [];
400
- const generated_statics = helper_state?.statics || [];
401
- const call = create_define_vapor_component_call(
402
- function_declaration_to_expression(fn),
403
- generated_helpers,
404
- generated_statics,
405
- );
406
-
407
- if (component.default || !component.id) {
408
- return call;
409
- }
410
-
411
- const component_id = create_generated_identifier(component.id.name);
412
- const fn_id = fn.type === 'FunctionDeclaration' ? fn.id : null;
413
- component_id.metadata = {
414
- ...component_id.metadata,
415
- ...(fn_id?.metadata || {}),
416
- path: component_id.metadata?.path || [],
417
- };
418
- /** @type {any} */ (component_id.metadata).hover = create_component_hover_replacement(fn.params);
419
-
420
- const declaration = builders.declaration('const', [builders.declarator(component_id, call)]);
421
- Object.assign(/** @type {any} */ (declaration.metadata), {
422
- generated_helpers,
423
- generated_statics,
424
- });
425
- return declaration;
426
- }
427
-
428
382
  /**
429
383
  * @param {any} helper_fn
430
384
  * @param {any} helper_id
@@ -443,6 +397,91 @@ function wrap_helper_component(helper_fn, helper_id, source_node) {
443
397
  );
444
398
  }
445
399
 
400
+ /**
401
+ * @param {any} fn
402
+ * @param {any} ctx
403
+ * @param {any[]} path
404
+ * @returns {any | null}
405
+ */
406
+ function wrap_native_function_component(fn, ctx, path) {
407
+ const name = get_function_component_name(fn, path);
408
+ if (!name || !/^[A-Z]/.test(name)) {
409
+ return null;
410
+ }
411
+
412
+ ctx.needs_define_vapor_component = true;
413
+
414
+ const call = create_define_vapor_component_call(
415
+ function_declaration_to_expression(fn),
416
+ fn.metadata?.generated_helpers || [],
417
+ fn.metadata?.generated_statics || [],
418
+ );
419
+
420
+ if (fn.type !== 'FunctionDeclaration') {
421
+ return call;
422
+ }
423
+
424
+ const parent = path.at(-1);
425
+ if (parent?.type === 'ExportDefaultDeclaration' && parent.declaration === fn) {
426
+ return setLocation(call, fn, true);
427
+ }
428
+
429
+ if (!fn.id) {
430
+ return call;
431
+ }
432
+
433
+ return setLocation(
434
+ builders.declaration('const', [
435
+ builders.declarator(create_generated_identifier(fn.id.name), setLocation(call, fn, true)),
436
+ ]),
437
+ fn,
438
+ true,
439
+ );
440
+ }
441
+
442
+ /**
443
+ * @param {any} fn
444
+ * @param {any[]} path
445
+ * @returns {string | null}
446
+ */
447
+ function get_function_component_name(fn, path) {
448
+ if (fn.id?.type === 'Identifier') {
449
+ return fn.id.name;
450
+ }
451
+
452
+ const parent = path.at(-1);
453
+ if (parent?.type === 'VariableDeclarator' && parent.init === fn) {
454
+ return get_static_name(parent.id);
455
+ }
456
+
457
+ if (parent?.type === 'Property' && parent.value === fn) {
458
+ return get_static_name(parent.key);
459
+ }
460
+
461
+ if (parent?.type === 'AssignmentExpression' && parent.right === fn) {
462
+ return get_static_name(parent.left);
463
+ }
464
+
465
+ return null;
466
+ }
467
+
468
+ /**
469
+ * @param {any} node
470
+ * @returns {string | null}
471
+ */
472
+ function get_static_name(node) {
473
+ if (node?.type === 'Identifier') {
474
+ return node.name;
475
+ }
476
+ if (node?.type === 'Literal' && typeof node.value === 'string') {
477
+ return node.value;
478
+ }
479
+ if (node?.type === 'MemberExpression' && !node.computed) {
480
+ return get_static_name(node.property);
481
+ }
482
+ return null;
483
+ }
484
+
446
485
  /**
447
486
  * @param {any} fn_expression
448
487
  * @param {any[]} generated_helpers
@@ -1026,29 +1065,6 @@ function function_declaration_to_expression(fn) {
1026
1065
  };
1027
1066
  }
1028
1067
 
1029
- const VUE_COMPONENT_HOVER_LABEL_REGEX = /(function|\((property|method)\))/;
1030
-
1031
- /**
1032
- * @param {any[]} [params]
1033
- * @returns {(content: string) => string}
1034
- */
1035
- function create_component_hover_replacement(params) {
1036
- const lazy_param_regexes = (params || [])
1037
- .filter((param) => param.type === 'Identifier' && /^__lazy\d+$/.test(param.name))
1038
- .map((param) => new RegExp(`\\b${param.name}\\s*:\\s*`, 'g'));
1039
-
1040
- return (content) => {
1041
- let next = content.replace(VUE_COMPONENT_HOVER_LABEL_REGEX, (_, fn, kind) => {
1042
- if (fn === 'function') return 'component';
1043
- return `(component ${kind})`;
1044
- });
1045
- for (const regex of lazy_param_regexes) {
1046
- next = next.replace(regex, '&');
1047
- }
1048
- return next;
1049
- };
1050
- }
1051
-
1052
1068
  const VUE_SETUP_CALLS = new Set([
1053
1069
  'ref',
1054
1070
  'shallowRef',
@@ -1085,32 +1101,19 @@ function is_vue_setup_call(call_expression) {
1085
1101
  }
1086
1102
 
1087
1103
  /**
1088
- * Reject `{ref expr}` on composite (component-like) elements: Vue component
1089
- * refs resolve to the component instance, not the rendered DOM node, so
1090
- * Ripple-style component refs don't have a meaningful DOM target. Multi-ref
1091
- * merging itself is handled by the shared `merge_duplicate_refs` pass via
1092
- * the platform's `multiRefStrategy: 'merge-refs'` config.
1104
+ * Vue's JSX transform treats some prop names ending in `ref` as template-ref
1105
+ * sugar on components. Keep those as ordinary runtime props by hiding the
1106
+ * static prop name behind an object spread before Vue sees the JSX. Type-only
1107
+ * virtual TSX skips that spread so Volar can offer completions on the real
1108
+ * component prop name.
1093
1109
  *
1094
1110
  * @param {any[]} attrs
1095
- * @param {any} element
1096
1111
  * @param {any} transform_context
1097
1112
  * @returns {any[]}
1098
1113
  */
1099
- function preprocess_ref_attributes(attrs, element, transform_context) {
1100
- if (!is_component_like_element(element)) {
1101
- return attrs;
1102
- }
1114
+ function preprocess_ref_attributes(attrs, transform_context) {
1103
1115
  const result = [];
1104
1116
  for (const attr of attrs) {
1105
- if (attr?.type === 'RefAttribute') {
1106
- error(
1107
- '`{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.',
1108
- transform_context?.filename ?? null,
1109
- attr,
1110
- transform_context?.errors,
1111
- transform_context?.comments,
1112
- );
1113
- }
1114
1117
  if (!transform_context.typeOnly && is_vue_named_ref_attribute(attr)) {
1115
1118
  result.push(create_vue_named_ref_spread(attr));
1116
1119
  continue;
@@ -1121,12 +1124,6 @@ function preprocess_ref_attributes(attrs, element, transform_context) {
1121
1124
  }
1122
1125
 
1123
1126
  /**
1124
- * Vue's JSX transform treats prop names ending in `ref` as template-ref
1125
- * sugar on components. Keep named TSRX refs as ordinary runtime props by
1126
- * hiding the static prop name behind an object spread before Vue sees the JSX.
1127
- * Type-only virtual TSX skips that spread so Volar can offer completions on
1128
- * the real component prop name.
1129
- *
1130
1127
  * @param {any} attr
1131
1128
  * @returns {boolean}
1132
1129
  */
@@ -1137,13 +1134,19 @@ function is_vue_named_ref_attribute(attr) {
1137
1134
  attr_name &&
1138
1135
  attr_name !== 'ref' &&
1139
1136
  (attr?.type === 'Attribute' || attr?.type === 'JSXAttribute') &&
1140
- (value?.type === 'RefExpression' ||
1141
- (value?.type === 'CallExpression' &&
1142
- value.callee?.type === 'Identifier' &&
1143
- value.callee.name === CREATE_REF_PROP_INTERNAL_NAME))
1137
+ value &&
1138
+ is_vue_ref_prop_name(attr_name)
1144
1139
  );
1145
1140
  }
1146
1141
 
1142
+ /**
1143
+ * @param {string} name
1144
+ * @returns {boolean}
1145
+ */
1146
+ function is_vue_ref_prop_name(name) {
1147
+ return /(?:^|[-_])ref$/i.test(name) || /Ref$/.test(name);
1148
+ }
1149
+
1147
1150
  /**
1148
1151
  * @param {any} attr
1149
1152
  * @returns {any}
@@ -1152,7 +1155,13 @@ function create_vue_named_ref_spread(attr) {
1152
1155
  const attr_name = get_vue_attribute_name(attr);
1153
1156
  const value = get_vue_attribute_expression(attr);
1154
1157
  if (attr_name === null) return attr;
1155
- const prop = builders.prop('init', builders.key(attr_name), value, false, false);
1158
+ const prop = builders.prop(
1159
+ 'init',
1160
+ builders.key(attr_name),
1161
+ value ?? builders.literal(true),
1162
+ false,
1163
+ false,
1164
+ );
1156
1165
  return builders.jsx_spread_attribute(builders.object([prop], attr), attr);
1157
1166
  }
1158
1167
 
@@ -1184,30 +1193,12 @@ function get_vue_attribute_expression(attr) {
1184
1193
  * @param {any[]} walked_children
1185
1194
  * @param {any[]} raw_children
1186
1195
  * @param {any[]} attributes
1187
- * @param {any} [transform_context]
1188
1196
  * @returns {{ children: any[]; selfClosing?: boolean } | null}
1189
1197
  */
1190
- function rewrite_host_text_or_html_children(
1191
- node,
1192
- walked_children,
1193
- raw_children,
1194
- attributes,
1195
- transform_context,
1196
- ) {
1198
+ function rewrite_host_text_children(node, walked_children, raw_children, attributes) {
1197
1199
  const source_children = raw_children || walked_children;
1198
1200
  const is_composite = is_component_like_element(node);
1199
1201
 
1200
- const html_child_transform = rewrite_host_html_children(
1201
- node,
1202
- walked_children,
1203
- raw_children,
1204
- attributes,
1205
- transform_context,
1206
- );
1207
- if (html_child_transform) {
1208
- return html_child_transform;
1209
- }
1210
-
1211
1202
  if (!is_composite && source_children.length === 1 && source_children[0]?.type === 'Text') {
1212
1203
  return null;
1213
1204
  }
@@ -1250,10 +1241,6 @@ function inject_vue_imports(program, transform_context) {
1250
1241
  ensure_named_import(program, '@tsrx/vue/ref', 'mergeRefs', MERGE_REFS_INTERNAL_NAME);
1251
1242
  }
1252
1243
 
1253
- if (transform_context.needs_ref_prop) {
1254
- ensure_named_import(program, '@tsrx/vue/ref', 'create_ref_prop', CREATE_REF_PROP_INTERNAL_NAME);
1255
- }
1256
-
1257
1244
  if (transform_context.needs_normalize_spread_props) {
1258
1245
  ensure_named_import(
1259
1246
  program,
@@ -1262,6 +1249,15 @@ function inject_vue_imports(program, transform_context) {
1262
1249
  NORMALIZE_SPREAD_PROPS_INTERNAL_NAME,
1263
1250
  );
1264
1251
  }
1252
+
1253
+ if (transform_context.needs_normalize_spread_props_for_ref_attr) {
1254
+ ensure_named_import(
1255
+ program,
1256
+ '@tsrx/vue/ref',
1257
+ 'normalize_spread_props_for_ref_attr',
1258
+ NORMALIZE_SPREAD_PROPS_FOR_REF_ATTR_INTERNAL_NAME,
1259
+ );
1260
+ }
1265
1261
  }
1266
1262
 
1267
1263
  /**