@tsrx/core 0.1.19 → 0.1.20

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": "Core compiler infrastructure for TSRX syntax",
4
4
  "license": "MIT",
5
5
  "author": "Dominic Gannaway",
6
- "version": "0.1.19",
6
+ "version": "0.1.20",
7
7
  "type": "module",
8
8
  "repository": {
9
9
  "type": "git",
package/src/plugin.js CHANGED
@@ -341,10 +341,7 @@ export function TSRXPlugin(config) {
341
341
  */
342
342
  #isNativeTemplateNode(node) {
343
343
  return (
344
- node?.type === 'Element' ||
345
- node?.type === 'Tsx' ||
346
- node?.type === 'Tsrx' ||
347
- node?.type === 'TsxCompat'
344
+ node?.type === 'Element' || node?.type === 'TsrxFragment' || node?.type === 'TsxCompat'
348
345
  );
349
346
  }
350
347
 
@@ -368,7 +365,7 @@ export function TSRXPlugin(config) {
368
365
  );
369
366
  }
370
367
  this.#reportDynamicJsxElementsInTsx(/** @type {AST.Node[]} */ (child.children));
371
- } else if (child?.type === 'Tsx' || child?.type === 'TsxCompat') {
368
+ } else if (child?.type === 'TsxCompat') {
372
369
  this.#reportDynamicJsxElementsInTsx(/** @type {AST.Node[]} */ (child.children));
373
370
  }
374
371
  }
@@ -396,16 +393,11 @@ export function TSRXPlugin(config) {
396
393
  }
397
394
 
398
395
  /**
399
- * @param {AST.Tsx | AST.TsxCompat} island
396
+ * @param {AST.TsxCompat} island
400
397
  * @param {AST.Node[]} body
401
398
  */
402
399
  #parseTsxIslandBody(island, body) {
403
- const tagName =
404
- island.type === 'TsxCompat'
405
- ? `tsx:${island.kind}`
406
- : island.openingElement.name
407
- ? 'tsx'
408
- : '';
400
+ const tagName = `tsx:${island.kind}`;
409
401
 
410
402
  this.exprAllowed = true;
411
403
 
@@ -424,7 +416,7 @@ export function TSRXPlugin(config) {
424
416
  return;
425
417
  }
426
418
 
427
- if (this.#isAtTsxIslandClosing(island)) {
419
+ if (this.#isAtTsxIslandClosing()) {
428
420
  this.exprAllowed = false;
429
421
  return;
430
422
  }
@@ -459,7 +451,7 @@ export function TSRXPlugin(config) {
459
451
  );
460
452
  node.expression = expression;
461
453
  this.#popTokenContextsAfterTemplateExpressionElement(
462
- /** @type {AST.Tsx | AST.Tsrx | AST.TsxCompat} */ (/** @type {unknown} */ (expression)),
454
+ /** @type {AST.TsrxFragment | AST.TsxCompat} */ (/** @type {unknown} */ (expression)),
463
455
  );
464
456
  this.expect(tt.braceR);
465
457
  return this.finishNode(node, 'JSXExpressionContainer');
@@ -499,38 +491,13 @@ export function TSRXPlugin(config) {
499
491
  * @param {number} index
500
492
  */
501
493
  #isReservedTemplateTagNameStart(index) {
502
- const char_after_tsx = this.input.charCodeAt(index + 3);
503
- return (
504
- this.input.startsWith('tsx', index) &&
505
- (index + 3 >= this.input.length ||
506
- char_after_tsx === CharCode.greaterThan ||
507
- char_after_tsx === CharCode.slash ||
508
- char_after_tsx === CharCode.space ||
509
- char_after_tsx === CharCode.tab ||
510
- char_after_tsx === CharCode.lineFeed ||
511
- char_after_tsx === CharCode.carriageReturn ||
512
- char_after_tsx === CharCode.colon)
513
- );
494
+ return this.input.startsWith('tsx:', index);
514
495
  }
515
496
 
516
497
  /**
517
- * @param {AST.Tsx | AST.TsxCompat} island
518
498
  */
519
- #isAtTsxIslandClosing(island) {
520
- if (island.type === 'TsxCompat') {
521
- return this.input.slice(this.pos, this.pos + 5) === '/tsx:';
522
- }
523
-
524
- if (!island.openingElement.name) {
525
- return this.input.slice(this.pos, this.pos + 2) === '/>';
526
- }
527
-
528
- if (this.input.slice(this.pos, this.pos + 4) !== '/tsx') {
529
- return false;
530
- }
531
-
532
- const after = this.input.charCodeAt(this.pos + 4);
533
- return after === CharCode.greaterThan;
499
+ #isAtTsxIslandClosing() {
500
+ return this.input.slice(this.pos, this.pos + 5) === '/tsx:';
534
501
  }
535
502
 
536
503
  #parseTsxIslandText() {
@@ -567,7 +534,7 @@ export function TSRXPlugin(config) {
567
534
  let index = this.pos;
568
535
  let has_newline = false;
569
536
 
570
- // Text-only Tsx nodes can leave the tokenizer in JSX text mode.
537
+ // Text-only compat islands can leave the tokenizer in JSX text mode.
571
538
  // Only unwind it for ASI before a following TSRX `{expr}` child;
572
539
  // fragment props like `content={<></>}` still need the JSX context.
573
540
  while (index < this.input.length) {
@@ -664,7 +631,7 @@ export function TSRXPlugin(config) {
664
631
  }
665
632
 
666
633
  /**
667
- * @param {AST.Tsx | AST.Tsrx | AST.TsxCompat} node
634
+ * @param {AST.TsrxFragment | AST.TsxCompat} node
668
635
  * @returns {boolean}
669
636
  */
670
637
  #hasDirectStatementChild(node) {
@@ -674,7 +641,7 @@ export function TSRXPlugin(config) {
674
641
  }
675
642
 
676
643
  /**
677
- * @param {AST.Tsx | AST.Tsrx | AST.TsxCompat} node
644
+ * @param {AST.TsrxFragment | AST.TsxCompat} node
678
645
  */
679
646
  #popTokenContextsAfterTemplateExpressionElement(node) {
680
647
  const ctx = this.context;
@@ -769,15 +736,14 @@ export function TSRXPlugin(config) {
769
736
 
770
737
  #isDoubleQuotedTextChildStart() {
771
738
  const current_template_node = this.#path.findLast(
772
- (n) =>
773
- n.type === 'Element' || n.type === 'Tsx' || n.type === 'Tsrx' || n.type === 'TsxCompat',
739
+ (n) => n.type === 'Element' || n.type === 'TsrxFragment' || n.type === 'TsxCompat',
774
740
  );
775
- if (current_template_node?.type === 'TsxCompat' || current_template_node?.type === 'Tsx') {
741
+ if (current_template_node?.type === 'TsxCompat') {
776
742
  return false;
777
743
  }
778
744
 
779
745
  const parent = this.#path.at(-1);
780
- if (!parent || (parent.type !== 'Element' && parent.type !== 'Tsrx')) {
746
+ if (!parent || (parent.type !== 'Element' && parent.type !== 'TsrxFragment')) {
781
747
  return false;
782
748
  }
783
749
 
@@ -1193,7 +1159,7 @@ export function TSRXPlugin(config) {
1193
1159
  const parent = this.#path.at(-1);
1194
1160
  const inNativeTemplate =
1195
1161
  this.#functionBodyDepth === 0 &&
1196
- (parent?.type === 'Element' || parent?.type === 'Tsrx');
1162
+ (parent?.type === 'Element' || parent?.type === 'TsrxFragment');
1197
1163
  /** @type {number | null} */
1198
1164
  let prevNonWhitespaceChar = null;
1199
1165
 
@@ -1740,16 +1706,9 @@ export function TSRXPlugin(config) {
1740
1706
 
1741
1707
  if (this.eat(tt.braceL)) {
1742
1708
  const current_template_node = this.#path.findLast(
1743
- (n) =>
1744
- n.type === 'Element' ||
1745
- n.type === 'Tsx' ||
1746
- n.type === 'Tsrx' ||
1747
- n.type === 'TsxCompat',
1709
+ (n) => n.type === 'Element' || n.type === 'TsrxFragment' || n.type === 'TsxCompat',
1748
1710
  );
1749
- if (
1750
- current_template_node?.type === 'Tsx' ||
1751
- current_template_node?.type === 'TsxCompat'
1752
- ) {
1711
+ if (current_template_node?.type === 'TsxCompat') {
1753
1712
  if (this.type === tt.ellipsis) {
1754
1713
  this.expect(tt.ellipsis);
1755
1714
  /** @type {ESTreeJSX.JSXSpreadAttribute} */ (node).argument = this.parseMaybeAssign();
@@ -1981,10 +1940,9 @@ export function TSRXPlugin(config) {
1981
1940
  /** @type {Parse.Parser['jsx_readToken']} */
1982
1941
  jsx_readToken() {
1983
1942
  const current_template_node = this.#path.findLast(
1984
- (n) =>
1985
- n.type === 'Element' || n.type === 'Tsx' || n.type === 'Tsrx' || n.type === 'TsxCompat',
1943
+ (n) => n.type === 'Element' || n.type === 'TsrxFragment' || n.type === 'TsxCompat',
1986
1944
  );
1987
- if (current_template_node?.type === 'TsxCompat' || current_template_node?.type === 'Tsx') {
1945
+ if (current_template_node?.type === 'TsxCompat') {
1988
1946
  return super.jsx_readToken();
1989
1947
  }
1990
1948
  let out = '',
@@ -1993,11 +1951,7 @@ export function TSRXPlugin(config) {
1993
1951
  while (true) {
1994
1952
  if (this.pos >= this.input.length) {
1995
1953
  const inside_open_template = this.#path.findLast(
1996
- (n) =>
1997
- n.type === 'Element' ||
1998
- n.type === 'Tsrx' ||
1999
- n.type === 'TsxCompat' ||
2000
- n.type === 'Tsx',
1954
+ (n) => n.type === 'Element' || n.type === 'TsrxFragment' || n.type === 'TsxCompat',
2001
1955
  );
2002
1956
  if (!inside_open_template) {
2003
1957
  while (this.curContext() === tstc.tc_expr) {
@@ -2117,7 +2071,7 @@ export function TSRXPlugin(config) {
2117
2071
  ch === CharCode.closeBrace &&
2118
2072
  (this.#path.length === 0 ||
2119
2073
  this.#path.at(-1)?.type === 'Element' ||
2120
- this.#path.at(-1)?.type === 'Tsrx')
2074
+ this.#path.at(-1)?.type === 'TsrxFragment')
2121
2075
  ) {
2122
2076
  this.#resetTokenStartToCurrentPosition();
2123
2077
  return original.readToken.call(this, ch);
@@ -2155,19 +2109,17 @@ export function TSRXPlugin(config) {
2155
2109
 
2156
2110
  /**
2157
2111
  * Override jsx_parseElement to parse tags and bare fragments as native TSRX
2158
- * by default. Explicit <tsx> and <tsx:*> islands keep ordinary TSX parsing
2159
- * for their children.
2112
+ * by default. Explicit <tsx:*> islands keep ordinary TSX parsing for
2113
+ * their children.
2160
2114
  * @type {Parse.Parser['jsx_parseElement']}
2161
2115
  */
2162
2116
  jsx_parseElement() {
2163
2117
  // Current token is jsxTagStart, this.end is position after '<'
2164
2118
  const tag_name_start = this.end;
2165
2119
  const current_template_node = this.#path.findLast(
2166
- (n) =>
2167
- n.type === 'Element' || n.type === 'Tsx' || n.type === 'Tsrx' || n.type === 'TsxCompat',
2120
+ (n) => n.type === 'Element' || n.type === 'TsrxFragment' || n.type === 'TsxCompat',
2168
2121
  );
2169
- const inside_tsx_island =
2170
- current_template_node?.type === 'Tsx' || current_template_node?.type === 'TsxCompat';
2122
+ const inside_tsx_island = current_template_node?.type === 'TsxCompat';
2171
2123
  if (inside_tsx_island) {
2172
2124
  if (this.input.charCodeAt(tag_name_start) === CharCode.at) {
2173
2125
  this.#report_recoverable_error_range(
@@ -2186,7 +2138,7 @@ export function TSRXPlugin(config) {
2186
2138
  );
2187
2139
  if (!inside_tsx_island) {
2188
2140
  this.#popTokenContextsAfterTemplateExpressionElement(
2189
- /** @type {AST.Tsx | AST.Tsrx | AST.TsxCompat} */ (/** @type {unknown} */ (parsed)),
2141
+ /** @type {AST.TsrxFragment | AST.TsxCompat} */ (/** @type {unknown} */ (parsed)),
2190
2142
  );
2191
2143
  } else if (this.type === tt.braceR && this.curContext() === tstc.tc_expr) {
2192
2144
  if (this.#tsxIslandExpressionDepth === 0) {
@@ -2210,7 +2162,7 @@ export function TSRXPlugin(config) {
2210
2162
  const start = this.start - 1;
2211
2163
  const position = new acorn.Position(this.curLine, start - this.lineStart);
2212
2164
 
2213
- const element = /** @type {AST.Element | AST.Tsx | AST.Tsrx | AST.TsxCompat} */ (
2165
+ const element = /** @type {AST.Element | AST.TsrxFragment | AST.TsxCompat} */ (
2214
2166
  this.startNode()
2215
2167
  );
2216
2168
  element.start = start;
@@ -2229,19 +2181,10 @@ export function TSRXPlugin(config) {
2229
2181
 
2230
2182
  // Fragments (<>) produce JSXOpeningFragment with no `name` property
2231
2183
  const is_fragment = !open.name;
2232
- const is_tsx_compat = !is_fragment && open.name.type === 'JSXNamespacedName';
2233
- const is_tsx =
2234
- !is_fragment &&
2235
- !is_tsx_compat &&
2236
- open.name.type === 'JSXIdentifier' &&
2237
- open.name.name === 'tsx';
2238
- const is_dynamic_name =
2184
+ const is_tsx_compat =
2239
2185
  !is_fragment &&
2240
- ((open.name.type === 'JSXIdentifier' && open.name.tracked) ||
2241
- (open.name.type === 'JSXMemberExpression' &&
2242
- open.name.object.type === 'JSXIdentifier' &&
2243
- open.name.object.tracked));
2244
-
2186
+ open.name.type === 'JSXNamespacedName' &&
2187
+ open.name.namespace.name === 'tsx';
2245
2188
  if (is_tsx_compat) {
2246
2189
  const namespace_node = /** @type {ESTreeJSX.JSXNamespacedName} */ (open.name);
2247
2190
  /** @type {AST.TsxCompat} */ (element).type = 'TsxCompat';
@@ -2254,29 +2197,12 @@ export function TSRXPlugin(config) {
2254
2197
  `TSX compatibility elements cannot be self-closing. '<${tagName} />' must have a closing tag '</${tagName}>'.`,
2255
2198
  );
2256
2199
  }
2257
- } else if (is_tsx) {
2258
- /** @type {AST.Tsx} */ (element).type = 'Tsx';
2259
-
2260
- if (open.selfClosing) {
2261
- this.raise(
2262
- open.start,
2263
- `TSX elements cannot be self-closing. '<tsx />' must have a closing tag '</tsx>'.`,
2264
- );
2265
- }
2266
2200
  } else if (is_fragment) {
2267
- /** @type {AST.Tsrx} */ (element).type = 'Tsrx';
2201
+ /** @type {AST.TsrxFragment} */ (element).type = 'TsrxFragment';
2268
2202
  } else {
2269
2203
  element.type = 'Element';
2270
2204
  }
2271
2205
 
2272
- if (is_tsx && is_dynamic_name) {
2273
- this.#report_recoverable_error_range(
2274
- open.name.start ?? open.start,
2275
- open.name.end ?? open.end,
2276
- DYNAMIC_ELEMENT_IN_TSX_ERROR,
2277
- );
2278
- }
2279
-
2280
2206
  for (const attr of open.attributes) {
2281
2207
  if (attr.type === 'JSXAttribute') {
2282
2208
  /** @type {AST.Attribute} */ (/** @type {unknown} */ (attr)).type = 'Attribute';
@@ -2298,7 +2224,7 @@ export function TSRXPlugin(config) {
2298
2224
  }
2299
2225
  }
2300
2226
 
2301
- if (!is_tsx_compat && !is_tsx && !is_fragment) {
2227
+ if (!is_tsx_compat && !is_fragment) {
2302
2228
  /** @type {AST.Element} */ (element).id = /** @type {AST.Identifier} */ (
2303
2229
  convert_from_jsx(/** @type {ESTreeJSX.JSXIdentifier} */ (open.name))
2304
2230
  );
@@ -2496,35 +2422,8 @@ export function TSRXPlugin(config) {
2496
2422
  enterScope: true,
2497
2423
  resetFunctionBodyDepth: true,
2498
2424
  });
2499
- if (/** @type {AST.Tsx} */ (element).type === 'Tsx') {
2425
+ if (/** @type {AST.TsxCompat} */ (element).type === 'TsxCompat') {
2500
2426
  this.#reportDynamicJsxElementsInTsx(/** @type {AST.Element} */ (element).children);
2501
- }
2502
-
2503
- if (/** @type {AST.Tsx} */ (element).type === 'Tsx') {
2504
- this.#path.pop();
2505
-
2506
- if (!element.unclosed) {
2507
- const raise_error = () => {
2508
- this.raise(this.start, `Expected closing tag '</tsx>'`);
2509
- };
2510
-
2511
- this.next();
2512
- // we should expect to see </tsx>
2513
- if (this.value !== '/') {
2514
- raise_error();
2515
- }
2516
- this.next();
2517
- if (this.value !== 'tsx') {
2518
- raise_error();
2519
- }
2520
- this.next();
2521
- if (this.type !== tstt.jsxTagEnd) {
2522
- raise_error();
2523
- }
2524
- this.#popTsxTokenContextBeforeTemplateExpressionChild();
2525
- this.next();
2526
- }
2527
- } else if (/** @type {AST.TsxCompat} */ (element).type === 'TsxCompat') {
2528
2427
  this.#path.pop();
2529
2428
 
2530
2429
  if (!element.unclosed) {
@@ -2560,7 +2459,7 @@ export function TSRXPlugin(config) {
2560
2459
  this.next();
2561
2460
  }
2562
2461
  } else if (
2563
- /** @type {AST.Tsrx} */ (element).type === 'Tsrx' &&
2462
+ /** @type {AST.TsrxFragment} */ (element).type === 'TsrxFragment' &&
2564
2463
  this.#path[this.#path.length - 1] === element
2565
2464
  ) {
2566
2465
  const displayTag = element.openingElement.name ? 'tsrx' : '';
@@ -2603,7 +2502,7 @@ export function TSRXPlugin(config) {
2603
2502
  }
2604
2503
  }
2605
2504
 
2606
- if (element.closingElement && !is_tsx_compat && !is_tsx && element.closingElement.name) {
2505
+ if (element.closingElement && !is_tsx_compat && element.closingElement.name) {
2607
2506
  /** @type {unknown} */ (element.closingElement.name) = convert_from_jsx(
2608
2507
  element.closingElement.name,
2609
2508
  );
@@ -2620,15 +2519,12 @@ export function TSRXPlugin(config) {
2620
2519
  const inside_func =
2621
2520
  this.context.some((n) => n.token === 'function') || this.scopeStack.length > 1;
2622
2521
  const current_template_node = this.#path.findLast(
2623
- (n) =>
2624
- n.type === 'Element' || n.type === 'Tsx' || n.type === 'Tsrx' || n.type === 'TsxCompat',
2522
+ (n) => n.type === 'Element' || n.type === 'TsrxFragment' || n.type === 'TsxCompat',
2625
2523
  );
2626
2524
  const inside_tsx_island =
2627
- current_template_node?.type === 'Tsx' || current_template_node?.type === 'TsxCompat'
2628
- ? current_template_node
2629
- : null;
2525
+ current_template_node?.type === 'TsxCompat' ? current_template_node : null;
2630
2526
 
2631
- if (current_template_node?.type === 'Tsrx' && this.type === tstt.jsxText) {
2527
+ if (current_template_node?.type === 'TsrxFragment' && this.type === tstt.jsxText) {
2632
2528
  while (this.curContext() === tstc.tc_expr) {
2633
2529
  this.context.pop();
2634
2530
  }
@@ -2649,13 +2545,13 @@ export function TSRXPlugin(config) {
2649
2545
 
2650
2546
  if (inside_tsx_island) {
2651
2547
  this.#parseTsxIslandBody(
2652
- /** @type {AST.Tsx | AST.TsxCompat} */ (inside_tsx_island),
2548
+ /** @type {AST.TsxCompat} */ (inside_tsx_island),
2653
2549
  /** @type {AST.Node[]} */ (/** @type {unknown} */ (body)),
2654
2550
  );
2655
2551
  return;
2656
2552
  }
2657
2553
  if (
2658
- current_template_node?.type === 'Tsrx' &&
2554
+ current_template_node?.type === 'TsrxFragment' &&
2659
2555
  !current_template_node.openingElement.name &&
2660
2556
  ((this.type === tstt.jsxTagStart && this.input.slice(this.pos, this.pos + 2) === '/>') ||
2661
2557
  (this.input.charCodeAt(this.start) === CharCode.lessThan &&
@@ -2715,8 +2611,7 @@ export function TSRXPlugin(config) {
2715
2611
  if (
2716
2612
  !currentElement ||
2717
2613
  (currentElement.type !== 'Element' &&
2718
- currentElement.type !== 'Tsx' &&
2719
- currentElement.type !== 'Tsrx' &&
2614
+ currentElement.type !== 'TsrxFragment' &&
2720
2615
  currentElement.type !== 'TsxCompat')
2721
2616
  ) {
2722
2617
  this.raise(this.start, 'Unexpected closing tag');
@@ -2733,13 +2628,7 @@ export function TSRXPlugin(config) {
2733
2628
  closingElement.name?.type === 'JSXNamespacedName'
2734
2629
  ? closingElement.name.namespace.name + ':' + closingElement.name.name.name
2735
2630
  : this.getElementName(closingElement.name);
2736
- } else if (currentElement.type === 'Tsx') {
2737
- openingTagName = currentElement.openingElement.name ? 'tsx' : null;
2738
- closingTagName =
2739
- closingElement.name?.type === 'JSXNamespacedName'
2740
- ? closingElement.name.namespace.name + ':' + closingElement.name.name.name
2741
- : this.getElementName(closingElement.name);
2742
- } else if (currentElement.type === 'Tsrx') {
2631
+ } else if (currentElement.type === 'TsrxFragment') {
2743
2632
  openingTagName = '';
2744
2633
  closingTagName =
2745
2634
  closingElement.name?.type === 'JSXNamespacedName'
@@ -2769,8 +2658,7 @@ export function TSRXPlugin(config) {
2769
2658
  // Stop at non-template boundaries.
2770
2659
  if (
2771
2660
  elem.type !== 'Element' &&
2772
- elem.type !== 'Tsx' &&
2773
- elem.type !== 'Tsrx' &&
2661
+ elem.type !== 'TsrxFragment' &&
2774
2662
  elem.type !== 'TsxCompat'
2775
2663
  ) {
2776
2664
  break;
@@ -2779,15 +2667,11 @@ export function TSRXPlugin(config) {
2779
2667
  const elemName =
2780
2668
  elem.type === 'TsxCompat'
2781
2669
  ? 'tsx:' + elem.kind
2782
- : elem.type === 'Tsx'
2783
- ? elem.openingElement.name
2784
- ? 'tsx'
2785
- : null
2786
- : elem.type === 'Tsrx'
2787
- ? ''
2788
- : elem.id
2789
- ? this.getElementName(elem.id)
2790
- : null;
2670
+ : elem.type === 'TsrxFragment'
2671
+ ? ''
2672
+ : elem.id
2673
+ ? this.getElementName(elem.id)
2674
+ : null;
2791
2675
 
2792
2676
  // Found matching opening tag
2793
2677
  if (elemName === closingTagName) {
@@ -2808,16 +2692,16 @@ export function TSRXPlugin(config) {
2808
2692
  const elementToClose = this.#path[this.#path.length - 1];
2809
2693
  if (
2810
2694
  elementToClose &&
2811
- (elementToClose.type === 'Element' || elementToClose.type === 'Tsrx')
2695
+ (elementToClose.type === 'Element' || elementToClose.type === 'TsrxFragment')
2812
2696
  ) {
2813
2697
  const elementToCloseName =
2814
- elementToClose.type === 'Tsrx'
2698
+ elementToClose.type === 'TsrxFragment'
2815
2699
  ? ''
2816
2700
  : /** @type {AST.Element} */ (elementToClose).id
2817
2701
  ? this.getElementName(/** @type {AST.Element} */ (elementToClose).id)
2818
2702
  : null;
2819
2703
  if (elementToCloseName === closingTagName) {
2820
- /** @type {AST.Element | AST.Tsrx} */ (elementToClose).closingElement =
2704
+ /** @type {AST.Element | AST.TsrxFragment} */ (elementToClose).closingElement =
2821
2705
  closingElement;
2822
2706
  }
2823
2707
  }
@@ -2933,7 +2817,11 @@ export function TSRXPlugin(config) {
2933
2817
  if (!node) {
2934
2818
  this.unexpected();
2935
2819
  }
2936
- if (this.#functionBodyDepth > 0 && node.type === 'Tsrx' && this.curContext() === b_stat) {
2820
+ if (
2821
+ this.#functionBodyDepth > 0 &&
2822
+ node.type === 'TsrxFragment' &&
2823
+ this.curContext() === b_stat
2824
+ ) {
2937
2825
  this.context.pop();
2938
2826
  if (this.curContext() === tstc.tc_expr) {
2939
2827
  this.context.pop();
@@ -2949,7 +2837,7 @@ export function TSRXPlugin(config) {
2949
2837
  this.#functionBodyDepth === 0 &&
2950
2838
  this.type === tt.string &&
2951
2839
  this.input.charCodeAt(this.start) === CharCode.doubleQuote &&
2952
- (this.#path.at(-1)?.type === 'Element' || this.#path.at(-1)?.type === 'Tsrx')
2840
+ (this.#path.at(-1)?.type === 'Element' || this.#path.at(-1)?.type === 'TsrxFragment')
2953
2841
  ) {
2954
2842
  this.pos = this.start;
2955
2843
  this.#readDoubleQuotedTextChildToken();
@@ -3003,7 +2891,7 @@ export function TSRXPlugin(config) {
3003
2891
  // nested function callable, not in a template.
3004
2892
  if (
3005
2893
  this.#functionBodyDepth === 0 &&
3006
- (parent?.type === 'Element' || parent?.type === 'Tsrx')
2894
+ (parent?.type === 'Element' || parent?.type === 'TsrxFragment')
3007
2895
  ) {
3008
2896
  if (createNewLexicalScope === void 0) createNewLexicalScope = true;
3009
2897
  if (node === void 0) node = /** @type {AST.BlockStatement} */ (this.startNode());
package/src/scope.js CHANGED
@@ -114,7 +114,7 @@ export function create_scopes(ast, root, parent, error_options) {
114
114
  next({ scope });
115
115
  },
116
116
 
117
- Tsrx(node, { state, next }) {
117
+ TsrxFragment(node, { state, next }) {
118
118
  const scope = state.scope.child();
119
119
  scopes.set(node, scope);
120
120
 
@@ -192,13 +192,13 @@ export function create_scopes(ast, root, parent, error_options) {
192
192
  for (const declarator of node.declarations) {
193
193
  /** @type {Binding[]} */
194
194
  const bindings = [];
195
- const initial = /** @type {AST.Expression | AST.Tsx | AST.Tsrx | null} */ (declarator.init);
195
+ const initial = /** @type {AST.Expression | AST.TsrxFragment | null} */ (declarator.init);
196
196
 
197
197
  state.scope.declarators.set(declarator, bindings);
198
198
 
199
199
  for (const id of extract_identifiers(declarator.id)) {
200
200
  const binding = state.scope.declare(id, 'normal', node.kind, initial);
201
- if (initial?.type === 'Tsx' || initial?.type === 'Tsrx') {
201
+ if (initial?.type === 'TsrxFragment') {
202
202
  binding.metadata = {
203
203
  ...(binding.metadata ?? {}),
204
204
  is_template_value: true,
@@ -242,8 +242,7 @@ export function is_jsx_child(node) {
242
242
  t === 'JSXFragment' ||
243
243
  t === 'JSXExpressionContainer' ||
244
244
  t === 'JSXText' ||
245
- t === 'Tsx' ||
246
- t === 'Tsrx' ||
245
+ t === 'TsrxFragment' ||
247
246
  t === 'TsxCompat' ||
248
247
  t === 'Element' ||
249
248
  t === 'Text' ||
@@ -256,8 +255,8 @@ export function is_jsx_child(node) {
256
255
  }
257
256
 
258
257
  /**
259
- * The parser represents `<>{expr}</>` / `<tsx>{expr}</tsx>` as a Tsx node,
260
- * and expression-position lowering unwraps that to the inner expression.
258
+ * Expression-position lowering unwraps single-expression native fragments to
259
+ * the inner expression.
261
260
  * When such a node appears directly in a component or statement render body,
262
261
  * the unwrapped expression is still render output rather than an executable
263
262
  * statement.
@@ -5,9 +5,9 @@ import tsx from 'esrap/languages/tsx';
5
5
 
6
6
  /**
7
7
  * Zimmerframe provides `path` as the ancestor chain (in original pre-transform
8
- * types, since visitors run bottom-up). A Tsx node whose parent is a ripple
9
- * `Element` will render as a JSX child of that element; anywhere else it
10
- * renders as a standalone expression (e.g. a return value).
8
+ * types, since visitors run bottom-up). A native template node whose parent is
9
+ * a ripple `Element` will render as a JSX child of that element; anywhere else
10
+ * it renders as a standalone expression (e.g. a return value).
11
11
  *
12
12
  * @param {any[]} path
13
13
  * @returns {boolean}
@@ -35,7 +35,7 @@ export function set_node_path_metadata(node, path) {
35
35
  }
36
36
 
37
37
  /**
38
- * Flatten a `<tsx>` / fragment node's children into a single expression. In a
38
+ * Flatten a JSX-compatible island's children into a single expression. In a
39
39
  * JSX-child position, a JSXExpressionContainer `{expr}` is valid and must stay
40
40
  * wrapped. In an expression position (e.g. `return ...`), `{expr}` parses as
41
41
  * a block/object literal, so unwrap to `expr`.
@@ -62,7 +62,7 @@ const HOOK_OUTER_ASSIGNMENT_ERROR =
62
62
  const HOOK_CALLBACK_OUTER_MUTATION_ERROR =
63
63
  'Hook callbacks inside conditional or repeated TSRX scopes must not mutate bindings declared outside the generated hook component.';
64
64
  const TEMPLATE_FRAGMENT_ERROR =
65
- 'JSX fragment syntax is not needed in TSRX templates. TSRX renders in immediate mode, so everything is already a fragment. Use `<>...</>` only within <tsx>...</tsx>.';
65
+ 'JSX fragment syntax is not needed in TSRX templates. TSRX renders in immediate mode, so everything is already a fragment. Use `<>...</>` only in expression position.';
66
66
 
67
67
  /**
68
68
  * @param {AST.Node} node
@@ -213,15 +213,7 @@ export function createJsxTransform(platform) {
213
213
  return next();
214
214
  },
215
215
 
216
- Tsx(node, { next, path }) {
217
- const inner = /** @type {any} */ (next() ?? node);
218
- const in_jsx_child = in_jsx_child_context(path);
219
- return /** @type {any} */ (
220
- wrap_jsx_setup_declarations(tsx_node_to_jsx_expression(inner, in_jsx_child), in_jsx_child)
221
- );
222
- },
223
-
224
- Tsrx(node, { next, path, state, visit }) {
216
+ TsrxFragment(node, { next, path, state, visit }) {
225
217
  const parent = /** @type {AST.ArrowFunctionExpression} */ (path.at(-1));
226
218
  if (parent?.metadata?.native_tsrx && parent.body === node) {
227
219
  return /** @type {any} */ (visit(create_native_tsrx_render_block(node, state), state));
@@ -1016,7 +1008,7 @@ function transform_block_statement(node, { next, visit, state, path }) {
1016
1008
  * @returns {any}
1017
1009
  */
1018
1010
  function transform_return_statement(node, { next, visit, state, path }) {
1019
- if (get_active_native_tsrx_function(path) && node.argument?.type === 'Tsrx') {
1011
+ if (get_active_native_tsrx_function(path) && node.argument?.type === 'TsrxFragment') {
1020
1012
  return visit(create_native_tsrx_render_block(node.argument, state), state);
1021
1013
  }
1022
1014
 
@@ -1080,7 +1072,7 @@ function transform_native_tsrx_function(node, { next, state }) {
1080
1072
  if (
1081
1073
  inner !== node &&
1082
1074
  node.type === 'ArrowFunctionExpression' &&
1083
- node.body?.type === 'Tsrx' &&
1075
+ node.body?.type === 'TsrxFragment' &&
1084
1076
  inner.body?.type === 'BlockStatement'
1085
1077
  ) {
1086
1078
  inner.expression = false;
@@ -1163,7 +1155,7 @@ function find_native_await_in_list(statements) {
1163
1155
  function find_native_await_in_statement(statement) {
1164
1156
  if (!statement || typeof statement !== 'object') return null;
1165
1157
 
1166
- if (statement.type === 'ReturnStatement' && statement.argument?.type === 'Tsrx') {
1158
+ if (statement.type === 'ReturnStatement' && statement.argument?.type === 'TsrxFragment') {
1167
1159
  return find_first_top_level_await_in_tsrx_function_body(statement.argument.children || []);
1168
1160
  }
1169
1161
 
@@ -1258,7 +1250,7 @@ function transform_function_with_hook_helpers(node, { next, state }) {
1258
1250
  * @returns {string}
1259
1251
  */
1260
1252
  function get_function_helper_base_name(node) {
1261
- return get_function_like_name(node) || 'Tsrx';
1253
+ return get_function_like_name(node) || 'TsrxFragment';
1262
1254
  }
1263
1255
 
1264
1256
  /**
@@ -1337,7 +1329,7 @@ function collect_function_scope_bindings(node) {
1337
1329
  const bindings = collect_param_bindings(node.params || []);
1338
1330
  if (node.body?.type === 'BlockStatement') {
1339
1331
  for (const statement of node.body.body || []) {
1340
- if (statement.type === 'ReturnStatement' && statement.argument?.type === 'Tsrx') {
1332
+ if (statement.type === 'ReturnStatement' && statement.argument?.type === 'TsrxFragment') {
1341
1333
  for (const child of get_tsrx_render_children(statement.argument)) {
1342
1334
  collect_statement_bindings(child, bindings);
1343
1335
  }
@@ -1446,7 +1438,7 @@ function statement_contains_native_tsrx_return(statement) {
1446
1438
  */
1447
1439
  function node_contains_native_tsrx_template(node) {
1448
1440
  if (!node || typeof node !== 'object') return false;
1449
- if (node.type === 'Element' || node.type === 'Tsrx') return true;
1441
+ if (node.type === 'Element' || node.type === 'TsrxFragment') return true;
1450
1442
 
1451
1443
  if (is_function_or_class_boundary(node)) {
1452
1444
  return false;
@@ -1637,7 +1629,7 @@ function collect_style_elements(node, styles) {
1637
1629
  return;
1638
1630
  }
1639
1631
 
1640
- if (is_function_or_class_boundary(node) || node.type === 'Tsrx') {
1632
+ if (is_function_or_class_boundary(node) || node.type === 'TsrxFragment') {
1641
1633
  return;
1642
1634
  }
1643
1635
 
@@ -1756,8 +1748,7 @@ function is_style_expression_position(path) {
1756
1748
  const parent = path.at(-1);
1757
1749
  return !(
1758
1750
  parent?.type === 'Element' ||
1759
- parent?.type === 'Tsrx' ||
1760
- parent?.type === 'Tsx' ||
1751
+ parent?.type === 'TsrxFragment' ||
1761
1752
  parent?.type === 'TsxCompat' ||
1762
1753
  parent?.type === 'BlockStatement' ||
1763
1754
  parent?.type === 'Program' ||
@@ -1845,7 +1836,7 @@ function expand_native_tsrx_return_statement_list(statements, transform_context)
1845
1836
  function expand_native_tsrx_return_statement(statement, transform_context) {
1846
1837
  if (!statement || typeof statement !== 'object') return [statement];
1847
1838
 
1848
- if (statement.type === 'ReturnStatement' && statement.argument?.type === 'Tsrx') {
1839
+ if (statement.type === 'ReturnStatement' && statement.argument?.type === 'TsrxFragment') {
1849
1840
  return create_native_tsrx_render_statements(statement.argument, transform_context);
1850
1841
  }
1851
1842
 
@@ -2045,7 +2036,7 @@ function node_contains_hook_bearing_tsrx(node, transform_context) {
2045
2036
  return node.some((child) => node_contains_hook_bearing_tsrx(child, transform_context));
2046
2037
  }
2047
2038
 
2048
- if (node.type === 'Tsrx') {
2039
+ if (node.type === 'TsrxFragment') {
2049
2040
  return body_contains_top_level_hook_call(node.children || [], transform_context, true);
2050
2041
  }
2051
2042
 
@@ -2092,7 +2083,7 @@ function should_extract_hook_helpers(transform_context) {
2092
2083
  */
2093
2084
  function create_module_scoped_hook_component_id(helper_id, transform_context) {
2094
2085
  return create_generated_identifier(
2095
- `${transform_context.helper_state?.base_name || 'Tsrx'}__${helper_id.name}`,
2086
+ `${transform_context.helper_state?.base_name || 'TsrxFragment'}__${helper_id.name}`,
2096
2087
  );
2097
2088
  }
2098
2089
 
@@ -4014,11 +4005,7 @@ function get_body_source_node(body_nodes) {
4014
4005
  function to_jsx_child(node, transform_context) {
4015
4006
  if (!node) return node;
4016
4007
  switch (node.type) {
4017
- case 'Tsx':
4018
- // We're inside a JSX child position by construction, so keep a
4019
- // JSXExpressionContainer wrapper for bare `{expr}` children.
4020
- return tsx_node_to_jsx_expression(node, true);
4021
- case 'Tsrx':
4008
+ case 'TsrxFragment':
4022
4009
  return tsrx_node_to_jsx_expression(node, transform_context, true);
4023
4010
  case 'TsxCompat':
4024
4011
  return tsx_compat_node_to_jsx_expression(node, transform_context, true);
@@ -4052,8 +4039,8 @@ function to_jsx_child(node, transform_context) {
4052
4039
 
4053
4040
  /**
4054
4041
  * Lower a native TSRX fragment body to a JSX expression.
4055
- * Unlike `<tsx>`, children have already been parsed and transformed through
4056
- * the normal TSRX Element/Text/control-flow visitors.
4042
+ * Children have already been parsed and transformed through the normal TSRX
4043
+ * Element/Text/control-flow visitors.
4057
4044
  *
4058
4045
  * @param {any} node
4059
4046
  * @param {TransformContext} transform_context
@@ -5529,7 +5516,7 @@ function wrap_jsx_setup_declarations(expression, in_jsx_child) {
5529
5516
  * This validator runs over the raw, pre-lowering attribute list so each
5530
5517
  * shape is still distinguishable by `type`. Ripple `Element` attributes have type `Attribute` with an
5531
5518
  * `Identifier` name (the parser normalizes `JSXAttribute`/`JSXIdentifier`
5532
- * for non-Tsx elements); inside `<tsx:react>` compat blocks they retain
5519
+ * for native elements); inside `<tsx:react>` compat blocks they retain
5533
5520
  * the original `JSXAttribute`/`JSXIdentifier` shape, so we accept both.
5534
5521
  *
5535
5522
  * @param {any[]} raw_attrs
@@ -5942,7 +5929,7 @@ function tsx_compat_node_to_jsx_expression(node, transform_context, in_jsx_child
5942
5929
  if (!platform.jsx.acceptedTsxKinds.includes(node.kind)) {
5943
5930
  const accepted = platform.jsx.acceptedTsxKinds.map((k) => `<tsx:${k}>`).join(', ');
5944
5931
  error(
5945
- `${platform.name} TSRX does not support <tsx:${node.kind}> blocks. Use <tsx> or one of: ${accepted}.`,
5932
+ `${platform.name} TSRX does not support <tsx:${node.kind}> blocks. Use one of: ${accepted}.`,
5946
5933
  transform_context.filename,
5947
5934
  node,
5948
5935
  transform_context.errors,
package/types/index.d.ts CHANGED
@@ -236,8 +236,7 @@ declare module 'estree' {
236
236
 
237
237
  // Include TypeScript node types and TSRX-specific nodes in NodeMap
238
238
  interface NodeMap {
239
- Tsx: Tsx;
240
- Tsrx: Tsrx;
239
+ TsrxFragment: TsrxFragment;
241
240
  TsxCompat: TsxCompat;
242
241
  TSRXExpression: TSRXExpression;
243
242
  Element: Element;
@@ -249,7 +248,7 @@ declare module 'estree' {
249
248
  }
250
249
 
251
250
  interface ExpressionMap {
252
- Tsrx: Tsrx;
251
+ TsrxFragment: TsrxFragment;
253
252
  Text: TextNode;
254
253
  JSXEmptyExpression: ESTreeJSX.JSXEmptyExpression;
255
254
  ParenthesizedExpression: ParenthesizedExpression;
@@ -345,18 +344,8 @@ declare module 'estree' {
345
344
  trailingComments?: AST.Comment[] | undefined;
346
345
  }
347
346
 
348
- interface Tsx extends AST.BaseNode {
349
- type: 'Tsx';
350
- attributes: Array<any>;
351
- children: ESTreeJSX.JSXElement['children'];
352
- selfClosing?: boolean;
353
- unclosed?: boolean;
354
- openingElement: ESTreeJSX.JSXOpeningElement;
355
- closingElement: ESTreeJSX.JSXClosingElement;
356
- }
357
-
358
- interface Tsrx extends AST.BaseExpression {
359
- type: 'Tsrx';
347
+ interface TsrxFragment extends AST.BaseExpression {
348
+ type: 'TsrxFragment';
360
349
  attributes: Array<any>;
361
350
  children: AST.Node[];
362
351
  selfClosing?: boolean;
@@ -461,7 +450,7 @@ declare module 'estree' {
461
450
 
462
451
  export type TSRXStatement = AST.Statement | TSESTree.Statement;
463
452
 
464
- export type NodeWithChildren = AST.Element | AST.Tsx | AST.Tsrx | AST.TsxCompat;
453
+ export type NodeWithChildren = AST.Element | AST.TsrxFragment | AST.TsxCompat;
465
454
 
466
455
  export namespace CSS {
467
456
  export interface BaseNode extends AST.NodeWithMaybeComments {
@@ -1222,8 +1211,7 @@ export interface Binding {
1222
1211
  | AST.ClassDeclaration
1223
1212
  | AST.ImportDeclaration
1224
1213
  | AST.TSModuleDeclaration
1225
- | AST.Tsx
1226
- | AST.Tsrx;
1214
+ | AST.TsrxFragment;
1227
1215
  /** Whether this binding has been reassigned */
1228
1216
  reassigned: boolean;
1229
1217
  /** Whether this binding has been mutated (property access) */
@@ -1323,8 +1311,7 @@ export interface ScopeInterface {
1323
1311
  | AST.ClassDeclaration
1324
1312
  | AST.ImportDeclaration
1325
1313
  | AST.TSModuleDeclaration
1326
- | AST.Tsx
1327
- | AST.Tsrx,
1314
+ | AST.TsrxFragment,
1328
1315
  ): Binding;
1329
1316
  /** Get binding by name */
1330
1317
  get(name: string): Binding | null;
package/types/parse.d.ts CHANGED
@@ -1172,7 +1172,7 @@ export namespace Parse {
1172
1172
  */
1173
1173
  parseTopLevel(node: AST.Program): AST.Program;
1174
1174
 
1175
- parseElement(): AST.Element | AST.Tsx | AST.Tsrx | AST.TsxCompat;
1175
+ parseElement(): AST.Element | AST.TsrxFragment | AST.TsxCompat;
1176
1176
 
1177
1177
  parseDoubleQuotedTextChild(): AST.TextNode;
1178
1178