ripple 0.2.134 → 0.2.136

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.
@@ -317,21 +317,21 @@ const visitors = {
317
317
  if (!context.state.to_ts) {
318
318
  return b.empty;
319
319
  }
320
- context.next();
320
+ return context.next();
321
321
  },
322
322
 
323
323
  TSInterfaceDeclaration(_, context) {
324
324
  if (!context.state.to_ts) {
325
325
  return b.empty;
326
326
  }
327
- context.next();
327
+ return context.next();
328
328
  },
329
329
 
330
330
  TSMappedType(_, context) {
331
331
  if (!context.state.to_ts) {
332
332
  return b.empty;
333
333
  }
334
- context.next();
334
+ return context.next();
335
335
  },
336
336
 
337
337
  NewExpression(node, context) {
@@ -384,7 +384,7 @@ const visitors = {
384
384
 
385
385
  TrackedArrayExpression(node, context) {
386
386
  if (context.state.to_ts) {
387
- const arrayAlias = import_from_ripple_if_needed("TrackedArray", context);
387
+ const arrayAlias = import_from_ripple_if_needed('TrackedArray', context);
388
388
 
389
389
  return b.call(
390
390
  b.member(b.id(arrayAlias), b.id('from')),
@@ -401,12 +401,9 @@ const visitors = {
401
401
 
402
402
  TrackedObjectExpression(node, context) {
403
403
  if (context.state.to_ts) {
404
- const objectAlias = import_from_ripple_if_needed("TrackedObject", context);
404
+ const objectAlias = import_from_ripple_if_needed('TrackedObject', context);
405
405
 
406
- return b.new(
407
- b.id(objectAlias),
408
- b.object(node.properties.map((prop) => context.visit(prop))),
409
- );
406
+ return b.new(b.id(objectAlias), b.object(node.properties.map((prop) => context.visit(prop))));
410
407
  }
411
408
 
412
409
  return b.call(
@@ -466,14 +463,17 @@ const visitors = {
466
463
  }
467
464
 
468
465
  if (node.tracked || (node.property.type === 'Identifier' && node.property.tracked)) {
466
+ // In TypeScript mode, skip the transformation and let transform_ts_child handle it
469
467
  add_ripple_internal_import(context);
470
468
 
471
- return b.call(
472
- '_$_.get_property',
473
- context.visit(node.object),
474
- node.computed ? context.visit(node.property) : b.literal(node.property.name),
475
- node.optional ? b.true : undefined,
476
- );
469
+ if (!context.state.to_ts) {
470
+ return b.call(
471
+ '_$_.get_property',
472
+ context.visit(node.object),
473
+ node.computed ? context.visit(node.property) : b.literal(node.property.name),
474
+ node.optional ? b.true : undefined,
475
+ );
476
+ }
477
477
  }
478
478
 
479
479
  if (node.object.type === 'MemberExpression' && node.object.optional) {
@@ -499,7 +499,7 @@ const visitors = {
499
499
  }
500
500
  }
501
501
  } else {
502
- context.next();
502
+ return context.next();
503
503
  }
504
504
  },
505
505
 
@@ -549,18 +549,68 @@ const visitors = {
549
549
  },
550
550
 
551
551
  JSXText(node, context) {
552
+ if (context.state.to_ts) {
553
+ return context.next();
554
+ }
552
555
  return b.literal(node.value + '');
553
556
  },
554
557
 
555
558
  JSXIdentifier(node, context) {
559
+ if (context.state.to_ts) {
560
+ return context.next();
561
+ }
556
562
  return b.id(node.name);
557
563
  },
558
564
 
559
565
  JSXExpressionContainer(node, context) {
566
+ if (context.state.to_ts) {
567
+ return context.next();
568
+ }
560
569
  return context.visit(node.expression);
561
570
  },
562
571
 
572
+ JSXFragment(node, context) {
573
+ if (context.state.to_ts) {
574
+ return context.next();
575
+ }
576
+ const attributes = node.openingFragment.attributes;
577
+ const normalized_children = node.children.filter((child) => {
578
+ return child.type !== 'JSXText' || child.value.trim() !== '';
579
+ });
580
+
581
+ const props = b.object(
582
+ attributes.map((attr) => {
583
+ if (attr.type === 'JSXAttribute') {
584
+ return b.prop('init', context.visit(attr.name), context.visit(attr.value));
585
+ } else if (attr.type === 'JSXSpreadAttribute') {
586
+ return b.spread(context.visit(attr.argument));
587
+ }
588
+ }),
589
+ );
590
+
591
+ if (normalized_children.length > 0) {
592
+ props.properties.push(
593
+ b.prop(
594
+ 'init',
595
+ b.id('children'),
596
+ normalized_children.length === 1
597
+ ? context.visit(normalized_children[0])
598
+ : b.array(normalized_children.map((child) => context.visit(child))),
599
+ ),
600
+ );
601
+ }
602
+
603
+ return b.call(
604
+ normalized_children.length > 1 ? '__compat.jsxs' : '__compat.jsx',
605
+ b.id('__compat.Fragment'),
606
+ props,
607
+ );
608
+ },
609
+
563
610
  JSXElement(node, context) {
611
+ if (context.state.to_ts) {
612
+ return context.next();
613
+ }
564
614
  const name = node.openingElement.name;
565
615
  const attributes = node.openingElement.attributes;
566
616
  const normalized_children = node.children.filter((child) => {
@@ -577,7 +627,7 @@ const visitors = {
577
627
  }),
578
628
  );
579
629
 
580
- if (normalize_children.length > 0) {
630
+ if (normalized_children.length > 0) {
581
631
  props.properties.push(
582
632
  b.prop(
583
633
  'init',
@@ -590,7 +640,7 @@ const visitors = {
590
640
  }
591
641
 
592
642
  return b.call(
593
- '__compat.jsx',
643
+ normalized_children.length > 1 ? '__compat.jsxs' : '__compat.jsx',
594
644
  name.type === 'JSXIdentifier' && name.name[0].toLowerCase() === name.name[0]
595
645
  ? b.literal(name.name)
596
646
  : context.visit(name),
@@ -682,6 +732,29 @@ const visitors = {
682
732
  const local_updates = [];
683
733
  const is_void = is_void_element(node.id.name);
684
734
 
735
+ let scoping_hash = null;
736
+ if (node.metadata.scoped && state.component.css) {
737
+ scoping_hash = state.component.css.hash;
738
+ } else {
739
+ let inside_dynamic_children = false;
740
+ for (let i = context.path.length - 1; i >= 0; i--) {
741
+ const anc = context.path[i];
742
+ if (anc && anc.type === 'Component' && anc.metadata && anc.metadata.inherited_css) {
743
+ inside_dynamic_children = true;
744
+ break;
745
+ }
746
+ }
747
+ if (inside_dynamic_children) {
748
+ for (let i = context.path.length - 1; i >= 0; i--) {
749
+ const anc = context.path[i];
750
+ if (anc && anc.type === 'Component' && anc.css) {
751
+ scoping_hash = anc.css.hash;
752
+ break;
753
+ }
754
+ }
755
+ }
756
+ }
757
+
685
758
  state.template.push(`<${node.id.name}`);
686
759
 
687
760
  for (const attr of node.attributes) {
@@ -851,8 +924,8 @@ const visitors = {
851
924
  if (class_attribute.value.type === 'Literal') {
852
925
  let value = class_attribute.value.value;
853
926
 
854
- if (node.metadata.scoped && state.component.css) {
855
- value = `${state.component.css.hash} ${value}`;
927
+ if (scoping_hash) {
928
+ value = `${scoping_hash} ${value}`;
856
929
  }
857
930
 
858
931
  handle_static_attr(class_attribute.name.name, value);
@@ -861,10 +934,7 @@ const visitors = {
861
934
  const metadata = { tracking: false, await: false };
862
935
  let expression = visit(class_attribute.value, { ...state, metadata });
863
936
 
864
- const hash_arg =
865
- node.metadata.scoped && state.component.css
866
- ? b.literal(state.component.css.hash)
867
- : undefined;
937
+ const hash_arg = scoping_hash ? b.literal(scoping_hash) : undefined;
868
938
  const is_html = context.state.metadata.namespace === 'html' && node.id.name !== 'svg';
869
939
 
870
940
  if (metadata.tracking) {
@@ -877,10 +947,8 @@ const visitors = {
877
947
  );
878
948
  }
879
949
  }
880
- } else if (node.metadata.scoped && state.component.css) {
881
- const value = state.component.css.hash;
882
-
883
- handle_static_attr(is_spreading ? '#class' : 'class', value);
950
+ } else if (scoping_hash) {
951
+ handle_static_attr(is_spreading ? '#class' : 'class', scoping_hash);
884
952
  }
885
953
 
886
954
  if (style_attribute !== null) {
@@ -1019,7 +1087,14 @@ const visitors = {
1019
1087
 
1020
1088
  if (children_filtered.length > 0) {
1021
1089
  const component_scope = context.state.scopes.get(node);
1022
- const children = visit(b.component(b.id('children'), [], children_filtered), {
1090
+ const children_component = b.component(b.id('children'), [], children_filtered);
1091
+
1092
+ children_component.metadata = {
1093
+ ...(children_component.metadata || {}),
1094
+ inherited_css: true,
1095
+ };
1096
+
1097
+ const children = visit(children_component, {
1023
1098
  ...context.state,
1024
1099
  scope: component_scope,
1025
1100
  namespace: child_namespace,
@@ -1081,11 +1156,15 @@ const visitors = {
1081
1156
  }),
1082
1157
  ];
1083
1158
 
1084
- return b.function(
1159
+ const func = b.function(
1085
1160
  node.id,
1086
1161
  node.params.map((param) => context.visit(param, { ...context.state, metadata })),
1087
1162
  b.block(body_statements),
1088
1163
  );
1164
+ // Mark that this function was originally a component
1165
+ func.metadata = { ...func.metadata, was_component: true };
1166
+ func.loc = node.loc; // Copy source location for Volar mappings
1167
+ return func;
1089
1168
  }
1090
1169
 
1091
1170
  let props = b.id('__props');
@@ -1114,7 +1193,7 @@ const visitors = {
1114
1193
  context.state.stylesheets.push(node.css);
1115
1194
  }
1116
1195
 
1117
- return b.function(
1196
+ const func = b.function(
1118
1197
  node.id,
1119
1198
  node.params.length > 0
1120
1199
  ? [b.id('__anchor'), props, b.id('__block')]
@@ -1126,6 +1205,10 @@ const visitors = {
1126
1205
  : body_statements),
1127
1206
  ]),
1128
1207
  );
1208
+ // Mark that this function was originally a component
1209
+ func.metadata = { ...func.metadata, was_component: true };
1210
+ func.loc = node.loc; // Copy source location for Volar mappings
1211
+ return func;
1129
1212
  },
1130
1213
 
1131
1214
  AssignmentExpression(node, context) {
@@ -1143,7 +1226,7 @@ const visitors = {
1143
1226
  const operator = node.operator;
1144
1227
  const right = node.right;
1145
1228
 
1146
- if (operator !== '=') {
1229
+ if (operator !== '=' && context.state.metadata?.tracking === false) {
1147
1230
  context.state.metadata.tracking = true;
1148
1231
  }
1149
1232
 
@@ -1197,7 +1280,9 @@ const visitors = {
1197
1280
  (argument.tracked || (argument.property.type === 'Identifier' && argument.property.tracked))
1198
1281
  ) {
1199
1282
  add_ripple_internal_import(context);
1200
- context.state.metadata.tracking = true;
1283
+ if (context.state.metadata?.tracking === false) {
1284
+ context.state.metadata.tracking = true;
1285
+ }
1201
1286
 
1202
1287
  return b.call(
1203
1288
  node.prefix ? '_$_.update_pre_property' : '_$_.update_property',
@@ -1613,7 +1698,17 @@ function transform_ts_child(node, context) {
1613
1698
  state.init.push(b.stmt(visit(node.expression, { ...state })));
1614
1699
  } else if (node.type === 'Element') {
1615
1700
  // Use capitalized name for dynamic components/elements in TypeScript output
1616
- const type = node.metadata?.ts_name || node.id.name;
1701
+ // If node.id is not an Identifier (e.g., MemberExpression like props.children),
1702
+ // we need to visit it to get the proper expression
1703
+ let type_expression;
1704
+ let type_is_expression = false;
1705
+ if (node.id.type === 'MemberExpression') {
1706
+ // For MemberExpressions, we need to create a JSXExpression, not a JSXIdentifier
1707
+ type_expression = visit(node.id, state);
1708
+ type_is_expression = true;
1709
+ } else {
1710
+ type_expression = node.metadata?.ts_name || node.id.name;
1711
+ }
1617
1712
  const children = [];
1618
1713
  let has_children_props = false;
1619
1714
 
@@ -1651,9 +1746,7 @@ function transform_ts_child(node, context) {
1651
1746
  const createRefKeyAlias = import_from_ripple_if_needed('createRefKey', context);
1652
1747
  const metadata = { await: false };
1653
1748
  const argument = visit(attr.argument, { ...state, metadata });
1654
- const wrapper = b.object(
1655
- [b.prop('init', b.call(createRefKeyAlias), argument, true)]
1656
- );
1749
+ const wrapper = b.object([b.prop('init', b.call(createRefKeyAlias), argument, true)]);
1657
1750
  return b.jsx_spread_attribute(wrapper);
1658
1751
  }
1659
1752
  });
@@ -1678,33 +1771,40 @@ function transform_ts_child(node, context) {
1678
1771
  }
1679
1772
  }
1680
1773
 
1681
- const opening_type = b.jsx_id(type);
1682
- // Use node.id.loc if available, otherwise create a loc based on the element's position
1683
- opening_type.loc = node.id.loc || {
1684
- start: {
1685
- line: node.loc.start.line,
1686
- column: node.loc.start.column + 2, // After "<@"
1687
- },
1688
- end: {
1689
- line: node.loc.start.line,
1690
- column: node.loc.start.column + 2 + type.length,
1691
- },
1692
- };
1693
-
1694
- let closing_type = undefined;
1774
+ let opening_type, closing_type;
1695
1775
 
1696
- if (!node.selfClosing) {
1697
- closing_type = b.jsx_id(type);
1698
- closing_type.loc = {
1776
+ if (type_is_expression) {
1777
+ // For dynamic/expression-based components (e.g., props.children),
1778
+ // use JSX expression instead of identifier
1779
+ opening_type = type_expression;
1780
+ closing_type = node.selfClosing ? undefined : type_expression;
1781
+ } else {
1782
+ opening_type = b.jsx_id(type_expression);
1783
+ // Use node.id.loc if available, otherwise create a loc based on the element's position
1784
+ opening_type.loc = node.id.loc || {
1699
1785
  start: {
1700
- line: node.loc.end.line,
1701
- column: node.loc.end.column - type.length - 1,
1786
+ line: node.loc.start.line,
1787
+ column: node.loc.start.column + 2, // After "<@"
1702
1788
  },
1703
1789
  end: {
1704
- line: node.loc.end.line,
1705
- column: node.loc.end.column - 1,
1790
+ line: node.loc.start.line,
1791
+ column: node.loc.start.column + 2 + type_expression.length,
1706
1792
  },
1707
1793
  };
1794
+
1795
+ if (!node.selfClosing) {
1796
+ closing_type = b.jsx_id(type_expression);
1797
+ closing_type.loc = {
1798
+ start: {
1799
+ line: node.loc.end.line,
1800
+ column: node.loc.end.column - type_expression.length - 1,
1801
+ },
1802
+ end: {
1803
+ line: node.loc.end.line,
1804
+ column: node.loc.end.column - 1,
1805
+ },
1806
+ };
1807
+ }
1708
1808
  }
1709
1809
 
1710
1810
  const jsxElement = b.jsx_element(
@@ -1819,8 +1919,14 @@ function transform_ts_child(node, context) {
1819
1919
  state.init.push(component);
1820
1920
  } else if (node.type === 'BreakStatement') {
1821
1921
  state.init.push(b.break);
1822
- } else {
1922
+ } else if (node.type === 'TsxCompat') {
1923
+ const children = node.children
1924
+ .map((child) => visit(child, state))
1925
+ .filter((child) => child.type !== 'JSXText' || child.value.trim() !== '');
1926
+
1823
1927
  debugger;
1928
+ state.init.push(b.stmt(b.jsx_fragment(children)));
1929
+ } else {
1824
1930
  throw new Error('TODO');
1825
1931
  }
1826
1932
  }
@@ -2077,6 +2183,56 @@ function create_tsx_with_typescript_support() {
2077
2183
  context.visit(node.typeAnnotation);
2078
2184
  context.write(')');
2079
2185
  },
2186
+ // Custom handler for TSMappedType: { [K in keyof T]: T[K] }
2187
+ TSMappedType(node, context) {
2188
+ context.write('{ ');
2189
+ if (node.readonly) {
2190
+ if (node.readonly === '+' || node.readonly === true) {
2191
+ context.write('readonly ');
2192
+ } else if (node.readonly === '-') {
2193
+ context.write('-readonly ');
2194
+ }
2195
+ }
2196
+ context.write('[');
2197
+ // Visit the entire type parameter (TSTypeParameter node)
2198
+ if (node.typeParameter) {
2199
+ context.visit(node.typeParameter);
2200
+ }
2201
+ context.write(']');
2202
+ if (node.optional) {
2203
+ if (node.optional === '+' || node.optional === true) {
2204
+ context.write('?');
2205
+ } else if (node.optional === '-') {
2206
+ context.write('-?');
2207
+ }
2208
+ }
2209
+ context.write(': ');
2210
+ // Visit the value type - could be either typeAnnotation or nameType
2211
+ if (node.typeAnnotation) {
2212
+ context.visit(node.typeAnnotation);
2213
+ } else if (node.nameType) {
2214
+ context.visit(node.nameType);
2215
+ }
2216
+ context.write(' }');
2217
+ },
2218
+ // Custom handler for TSTypeParameter: K in T (for mapped types)
2219
+ // acord ts has a bug where `in` is printed as `extends`, so we override it here
2220
+ TSTypeParameter(node, context) {
2221
+ // For mapped types, the name is just a string, not an Identifier node
2222
+ if (typeof node.name === 'string') {
2223
+ context.write(node.name);
2224
+ } else if (node.name && node.name.name) {
2225
+ context.write(node.name.name);
2226
+ }
2227
+ if (node.constraint) {
2228
+ context.write(' in ');
2229
+ context.visit(node.constraint);
2230
+ }
2231
+ if (node.default) {
2232
+ context.write(' = ');
2233
+ context.visit(node.default);
2234
+ }
2235
+ },
2080
2236
  // Override the ArrowFunctionExpression handler to support TypeScript return types
2081
2237
  ArrowFunctionExpression(node, context) {
2082
2238
  if (node.async) context.write('async ');
@@ -2108,7 +2264,7 @@ function create_tsx_with_typescript_support() {
2108
2264
  } else {
2109
2265
  context.visit(node.body);
2110
2266
  }
2111
- }
2267
+ },
2112
2268
  };
2113
2269
  }
2114
2270
 
@@ -2124,7 +2280,12 @@ export function transform_client(filename, source, analysis, to_ts) {
2124
2280
  const ripple_user_imports = new Map(); // exported -> local
2125
2281
  if (analysis && analysis.ast && Array.isArray(analysis.ast.body)) {
2126
2282
  for (const stmt of analysis.ast.body) {
2127
- if (stmt && stmt.type === 'ImportDeclaration' && stmt.source && stmt.source.value === 'ripple') {
2283
+ if (
2284
+ stmt &&
2285
+ stmt.type === 'ImportDeclaration' &&
2286
+ stmt.source &&
2287
+ stmt.source.value === 'ripple'
2288
+ ) {
2128
2289
  for (const spec of stmt.specifiers || []) {
2129
2290
  if (spec.type === 'ImportSpecifier' && spec.imported && spec.local) {
2130
2291
  ripple_user_imports.set(spec.imported.name, spec.local.name);
@@ -1,4 +1,4 @@
1
- import { walk } from 'zimmerframe';
1
+ import { walk } from 'zimmerframe';
2
2
 
3
3
  export const mapping_data = {
4
4
  verification: true,
@@ -354,6 +354,13 @@ export function convert_source_map_to_mappings(ast, source, generated_code, sour
354
354
 
355
355
  return;
356
356
  } else if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
357
+ // Map function/component keywords
358
+ if (node.metadata?.was_component && node.loc) {
359
+ tokens.push({ source: 'component', generated: 'function' });
360
+ } else if (node.loc && node.type === 'FunctionDeclaration') {
361
+ tokens.push('function');
362
+ }
363
+
357
364
  // Visit in source order: id, params, body
358
365
  if (node.id) {
359
366
  visit(node.id);
@@ -361,6 +368,9 @@ export function convert_source_map_to_mappings(ast, source, generated_code, sour
361
368
  if (node.params) {
362
369
  for (const param of node.params) {
363
370
  visit(param);
371
+ if (param.typeAnnotation) {
372
+ visit(param.typeAnnotation);
373
+ }
364
374
  }
365
375
  }
366
376
  if (node.body) {
@@ -1260,11 +1270,11 @@ export function convert_source_map_to_mappings(ast, source, generated_code, sour
1260
1270
  sourceOffsets: [0],
1261
1271
  generatedOffsets: [0],
1262
1272
  lengths: [1],
1263
- data: {
1264
- ...mapping_data,
1265
- codeActions: true, // auto-import
1266
- rename: false, // avoid rename for a “dummy” mapping
1267
- }
1273
+ data: {
1274
+ ...mapping_data,
1275
+ codeActions: true, // auto-import
1276
+ rename: false, // avoid rename for a “dummy” mapping
1277
+ }
1268
1278
  });
1269
1279
  }
1270
1280
 
@@ -124,17 +124,32 @@ export function ref(element, get_fn) {
124
124
  }
125
125
 
126
126
  /**
127
- * @param {() => void} fn
127
+ * @param {() => (void | (() => void))} fn
128
128
  * @param {CompatOptions} [compat]
129
129
  * @returns {Block}
130
130
  */
131
131
  export function root(fn, compat) {
132
+ var target_fn = fn;
133
+
132
134
  if (compat != null) {
135
+ /** @type {Array<void | (() => void)>} */
136
+ var unmounts = [];
133
137
  for (var key in compat) {
134
138
  var api = compat[key];
135
- api.createRoot();
139
+ unmounts.push(api.createRoot());
136
140
  }
141
+ target_fn = () => {
142
+ var component_unmount = fn();
143
+
144
+ return () => {
145
+ component_unmount?.();
146
+ for (var unmount of unmounts) {
147
+ unmount?.();
148
+ }
149
+ };
150
+ };
137
151
  }
152
+
138
153
  return block(ROOT_BLOCK, fn, { compat });
139
154
  }
140
155
 
@@ -753,6 +753,23 @@ export function jsx_element(
753
753
  return element;
754
754
  }
755
755
 
756
+ /**
757
+ * @param {Array<ESTree.JSXText | ESTree.JSXExpressionContainer | ESTree.JSXSpreadChild | ESTree.JSXElement | ESTree.JSXFragment>} children
758
+ * @returns {ESTree.JSXFragment}
759
+ */
760
+ export function jsx_fragment(children = []) {
761
+ return {
762
+ type: 'JSXFragment',
763
+ openingFragment: {
764
+ type: 'JSXOpeningFragment',
765
+ },
766
+ closingFragment: {
767
+ type: 'JSXClosingFragment',
768
+ },
769
+ children,
770
+ };
771
+ }
772
+
756
773
  /**
757
774
  * @param {ESTree.Expression | ESTree.JSXEmptyExpression} expression
758
775
  * @returns {ESTree.JSXExpressionContainer}