@tsrx/react 0.1.0 → 0.1.2

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 +16 -133
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "React compiler built on @tsrx/core",
4
4
  "license": "MIT",
5
5
  "author": "Dominic Gannaway",
6
- "version": "0.1.0",
6
+ "version": "0.1.2",
7
7
  "type": "module",
8
8
  "publishConfig": {
9
9
  "access": "public"
@@ -25,7 +25,7 @@
25
25
  "dependencies": {
26
26
  "esrap": "^2.1.0",
27
27
  "zimmerframe": "^1.1.2",
28
- "@tsrx/core": "0.0.6"
28
+ "@tsrx/core": "0.0.8"
29
29
  },
30
30
  "peerDependencies": {
31
31
  "react": ">=18"
package/src/transform.js CHANGED
@@ -17,6 +17,7 @@ import {
17
17
  isInterleavedBody as is_interleaved_body_core,
18
18
  isCapturableJsxChild as is_capturable_jsx_child,
19
19
  captureJsxChild,
20
+ isHoistSafeJsxNode as is_hoist_safe_jsx_node,
20
21
  } from '@tsrx/core';
21
22
 
22
23
  /**
@@ -894,116 +895,6 @@ function references_scope_bindings(node, scope_bindings) {
894
895
  return false;
895
896
  }
896
897
 
897
- /**
898
- * @param {AST.Literal} node
899
- * @returns {boolean}
900
- */
901
- function is_static_literal(node) {
902
- return (
903
- node.value === null ||
904
- typeof node.value === 'string' ||
905
- typeof node.value === 'number' ||
906
- typeof node.value === 'boolean' ||
907
- typeof node.value === 'bigint'
908
- );
909
- }
910
-
911
- /**
912
- * @param {any} node
913
- * @returns {boolean}
914
- */
915
- function is_hoist_safe_expression(node) {
916
- if (!node || typeof node !== 'object') return false;
917
-
918
- switch (node.type) {
919
- case 'Literal':
920
- return is_static_literal(node);
921
- case 'TemplateLiteral':
922
- return node.expressions.length === 0;
923
- case 'UnaryExpression':
924
- return node.operator !== 'delete' && is_hoist_safe_expression(node.argument);
925
- case 'BinaryExpression':
926
- case 'LogicalExpression':
927
- return is_hoist_safe_expression(node.left) && is_hoist_safe_expression(node.right);
928
- case 'ConditionalExpression':
929
- return (
930
- is_hoist_safe_expression(node.test) &&
931
- is_hoist_safe_expression(node.consequent) &&
932
- is_hoist_safe_expression(node.alternate)
933
- );
934
- case 'SequenceExpression':
935
- return node.expressions.every(is_hoist_safe_expression);
936
- case 'ParenthesizedExpression':
937
- return is_hoist_safe_expression(node.expression);
938
- case 'JSXElement':
939
- return is_hoist_safe_jsx_node(node);
940
- case 'JSXFragment':
941
- return node.children.every(is_hoist_safe_jsx_child);
942
- default:
943
- return false;
944
- }
945
- }
946
-
947
- /**
948
- * @param {any} node
949
- * @returns {boolean}
950
- */
951
- function is_hoist_safe_jsx_child(node) {
952
- if (!node || typeof node !== 'object') return false;
953
-
954
- switch (node.type) {
955
- case 'JSXText':
956
- return true;
957
- case 'JSXElement':
958
- return is_hoist_safe_jsx_node(node);
959
- case 'JSXFragment':
960
- return node.children.every(is_hoist_safe_jsx_child);
961
- case 'JSXExpressionContainer':
962
- return (
963
- node.expression.type !== 'JSXEmptyExpression' && is_hoist_safe_expression(node.expression)
964
- );
965
- default:
966
- return false;
967
- }
968
- }
969
-
970
- /**
971
- * @param {ESTreeJSX.JSXAttribute | ESTreeJSX.JSXSpreadAttribute} attribute
972
- * @returns {boolean}
973
- */
974
- function is_hoist_safe_jsx_attribute(attribute) {
975
- if (attribute.type === 'JSXSpreadAttribute') return false;
976
- if (attribute.value == null) return true;
977
-
978
- if (attribute.value.type === 'Literal') {
979
- return is_static_literal(attribute.value);
980
- }
981
-
982
- if (attribute.value.type === 'JSXExpressionContainer') {
983
- return (
984
- attribute.value.expression.type !== 'JSXEmptyExpression' &&
985
- is_hoist_safe_expression(attribute.value.expression)
986
- );
987
- }
988
-
989
- return false;
990
- }
991
-
992
- /**
993
- * @param {ESTreeJSX.JSXElement | ESTreeJSX.JSXFragment} node
994
- * @returns {boolean}
995
- */
996
- function is_hoist_safe_jsx_node(node) {
997
- if (node.type === 'JSXFragment') {
998
- return node.children.every(is_hoist_safe_jsx_child);
999
- }
1000
-
1001
- return (
1002
- node.openingElement.attributes.every(is_hoist_safe_jsx_attribute) &&
1003
- node.children.every(is_hoist_safe_jsx_child)
1004
- );
1005
- }
1006
-
1007
898
  /**
1008
899
  * Hoist static JSX elements from render_nodes to module level.
1009
900
  * A JSX element is static if it doesn't reference any component-scope bindings.
@@ -1200,6 +1091,9 @@ function is_jsx_child(node) {
1200
1091
  );
1201
1092
  }
1202
1093
 
1094
+ const TEMPLATE_FRAGMENT_ERROR =
1095
+ '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>.';
1096
+
1203
1097
  /**
1204
1098
  * @param {any} node
1205
1099
  * @param {TransformContext} transform_context
@@ -1212,23 +1106,13 @@ function to_jsx_element(node, transform_context) {
1212
1106
  '`{html ...}` is not supported on the React target. Use `dangerouslySetInnerHTML={{ __html: ... }}` as an element attribute instead.',
1213
1107
  );
1214
1108
  }
1109
+ if (!node.id) {
1110
+ throw create_compile_error(node, TEMPLATE_FRAGMENT_ERROR);
1111
+ }
1215
1112
  if (is_dynamic_element_id(node.id)) {
1216
1113
  return dynamic_element_to_jsx_child(node, transform_context);
1217
1114
  }
1218
1115
 
1219
- if (!node.id) {
1220
- const children = create_element_children(node.children || [], transform_context);
1221
- return set_loc(
1222
- /** @type {any} */ ({
1223
- type: 'JSXFragment',
1224
- openingFragment: { type: 'JSXOpeningFragment' },
1225
- closingFragment: { type: 'JSXClosingFragment' },
1226
- children,
1227
- }),
1228
- node,
1229
- );
1230
- }
1231
-
1232
1116
  const name = identifier_to_jsx_name(node.id);
1233
1117
  const attributes = (node.attributes || []).map(to_jsx_attribute);
1234
1118
  const selfClosing = !!node.selfClosing;
@@ -1712,24 +1596,23 @@ function for_of_statement_to_jsx_child(node, transform_context) {
1712
1596
  );
1713
1597
  }
1714
1598
 
1715
- if (node.key) {
1716
- throw create_compile_error(
1717
- node.key,
1718
- 'React TSRX does not support `key` in `for` control flow. Put the key on the rendered element instead, for example `<div key={i}>...</div>`.',
1719
- );
1720
- }
1721
-
1722
1599
  const loop_params = get_for_of_iteration_params(node.left, node.index);
1723
1600
  const loop_body = node.body.type === 'BlockStatement' ? node.body.body : [node.body];
1724
1601
  const has_hooks = body_contains_top_level_hook_call(loop_body);
1725
- const explicit_key_expression = has_hooks ? find_key_expression_in_body(loop_body) : undefined;
1602
+ const body_key_expression = find_key_expression_in_body(loop_body);
1603
+ const explicit_key_expression =
1604
+ body_key_expression ?? (node.key ? clone_expression_node(node.key) : undefined);
1726
1605
  const key_expression =
1727
1606
  has_hooks && explicit_key_expression == null && node.index
1728
1607
  ? clone_expression_node(node.index)
1729
1608
  : explicit_key_expression;
1730
1609
  const implicit_non_hook_key_expression =
1731
- !has_hooks && node.index && find_key_expression_in_body(loop_body) == null
1732
- ? clone_expression_node(node.index)
1610
+ !has_hooks && body_key_expression == null
1611
+ ? node.key
1612
+ ? clone_expression_node(node.key)
1613
+ : node.index
1614
+ ? clone_expression_node(node.index)
1615
+ : undefined
1733
1616
  : undefined;
1734
1617
 
1735
1618
  // Add loop params to available bindings so hoisted helpers receive them as props