@tsrx/core 0.0.7 → 0.0.8

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.0.7",
6
+ "version": "0.0.8",
7
7
  "type": "module",
8
8
  "repository": {
9
9
  "type": "git",
package/src/plugin.js CHANGED
@@ -1431,7 +1431,8 @@ export function TSRXPlugin(config) {
1431
1431
  /**
1432
1432
  * Override jsx_parseElement to intercept expression-level JSX.
1433
1433
  * This is called by acorn-jsx's parseExprAtom when it encounters <
1434
- * in expression position. Only <tsx> and <tsx:*> are allowed.
1434
+ * in expression position. Bare fragments are treated as shorthand
1435
+ * for <tsx>...</tsx>; other tags must still use <tsx> or <tsx:*>.
1435
1436
  * @type {Parse.Parser['jsx_parseElement']}
1436
1437
  */
1437
1438
  jsx_parseElement() {
@@ -1444,6 +1445,7 @@ export function TSRXPlugin(config) {
1444
1445
  // Check if the element being parsed IS a <tsx> or <tsx:*> tag
1445
1446
  // Current token is jsxTagStart, this.end is position after '<'
1446
1447
  const tag_name_start = this.end;
1448
+ const is_fragment_tag = this.input.charCodeAt(tag_name_start) === 62;
1447
1449
  const char_after_tsx = this.input.charCodeAt(tag_name_start + 3);
1448
1450
  const is_tsx_tag =
1449
1451
  this.input.startsWith('tsx', tag_name_start) &&
@@ -1456,8 +1458,9 @@ export function TSRXPlugin(config) {
1456
1458
  char_after_tsx === 13 || // carriage return
1457
1459
  char_after_tsx === 58); // : (tsx:react)
1458
1460
 
1459
- if (is_tsx_tag) {
1460
- // Use Ripple's parseElement to create a Tsx/TsxCompat node
1461
+ if (is_fragment_tag || is_tsx_tag) {
1462
+ // Use Ripple's parseElement to create a Tsx/TsxCompat node.
1463
+ // Bare fragments (<></>) are shorthand for <tsx>...</tsx>.
1461
1464
  this.next();
1462
1465
  return /** @type {import('estree-jsx').JSXElement} */ (
1463
1466
  /** @type {unknown} */ (this.parseElement())
@@ -1524,6 +1527,8 @@ export function TSRXPlugin(config) {
1524
1527
  `TSX elements cannot be self-closing. '<tsx />' must have a closing tag '</tsx>'.`,
1525
1528
  );
1526
1529
  }
1530
+ } else if (is_fragment) {
1531
+ /** @type {AST.Tsx} */ (element).type = 'Tsx';
1527
1532
  } else {
1528
1533
  element.type = 'Element';
1529
1534
  }
@@ -1575,6 +1580,26 @@ export function TSRXPlugin(config) {
1575
1580
  this.enterScope(0);
1576
1581
  this.parseTemplateBody(/** @type {AST.Element} */ (element).children);
1577
1582
  this.exitScope();
1583
+
1584
+ if (element.type === 'Tsx') {
1585
+ this.#path.pop();
1586
+
1587
+ if (!element.unclosed) {
1588
+ const raise_error = () => {
1589
+ this.raise(this.start, `Expected closing tag '</>'`);
1590
+ };
1591
+
1592
+ this.next();
1593
+ if (this.value !== '/') {
1594
+ raise_error();
1595
+ }
1596
+ this.next();
1597
+ if (this.type !== tstt.jsxTagEnd) {
1598
+ raise_error();
1599
+ }
1600
+ this.next();
1601
+ }
1602
+ }
1578
1603
  } else {
1579
1604
  if (/** @type {ESTreeJSX.JSXIdentifier} */ (open.name).name === 'script') {
1580
1605
  let content = '';
@@ -1875,7 +1900,11 @@ export function TSRXPlugin(config) {
1875
1900
  return;
1876
1901
  }
1877
1902
 
1878
- if (this.input.slice(this.pos, this.pos + 4) === '/tsx') {
1903
+ if (!inside_tsx.openingElement.name) {
1904
+ if (this.input.slice(this.pos, this.pos + 2) === '/>') {
1905
+ return;
1906
+ }
1907
+ } else if (this.input.slice(this.pos, this.pos + 4) === '/tsx') {
1879
1908
  const after = this.input.charCodeAt(this.pos + 4);
1880
1909
  // Make sure it's </tsx> and not </tsx:...>
1881
1910
  if (after === 62 /* > */) {
@@ -2046,7 +2075,7 @@ export function TSRXPlugin(config) {
2046
2075
  ? closingElement.name.namespace.name + ':' + closingElement.name.name.name
2047
2076
  : this.getElementName(closingElement.name);
2048
2077
  } else if (currentElement.type === 'Tsx') {
2049
- openingTagName = 'tsx';
2078
+ openingTagName = currentElement.openingElement.name ? 'tsx' : null;
2050
2079
  closingTagName =
2051
2080
  closingElement.name?.type === 'JSXNamespacedName'
2052
2081
  ? closingElement.name.namespace.name + ':' + closingElement.name.name.name
@@ -2081,7 +2110,9 @@ export function TSRXPlugin(config) {
2081
2110
  elem.type === 'TsxCompat'
2082
2111
  ? 'tsx:' + elem.kind
2083
2112
  : elem.type === 'Tsx'
2084
- ? 'tsx'
2113
+ ? elem.openingElement.name
2114
+ ? 'tsx'
2115
+ : null
2085
2116
  : elem.id
2086
2117
  ? this.getElementName(elem.id)
2087
2118
  : null;
@@ -1618,9 +1618,18 @@ export function convert_source_map_to_mappings(
1618
1618
  node.type === 'TSTypeParameterDeclaration'
1619
1619
  ) {
1620
1620
  if (node.loc) {
1621
- mappings.push(
1622
- get_mapping_from_node(node, src_to_gen_map, gen_line_offsets, mapping_data_verify_only),
1621
+ // Generic spans can be emitted by downstream transforms with sparse source-map
1622
+ // coverage around the angle-bracket delimiters. Skip missing whole-node mappings
1623
+ // instead of crashing Volar, and rely on child type-node mappings instead.
1624
+ const mapping = maybe_get_mapping_from_node(
1625
+ node,
1626
+ src_to_gen_map,
1627
+ gen_line_offsets,
1628
+ mapping_data_verify_only,
1623
1629
  );
1630
+ if (!(mapping instanceof Error)) {
1631
+ mappings.push(mapping);
1632
+ }
1624
1633
  }
1625
1634
  // Generic type parameters - visit to collect type variable names
1626
1635
  if (node.params) {
package/types/parse.d.ts CHANGED
@@ -1652,7 +1652,6 @@ export namespace Parse {
1652
1652
  startLoc?: AST.Position,
1653
1653
  ): ESTreeJSX.JSXOpeningElement;
1654
1654
  // it could also be ESTreeJSX.JSXOpeningFragment
1655
- // but not in our case since we don't use fragments
1656
1655
 
1657
1656
  /**
1658
1657
  * Parse JSX closing element at position