@tsrx/solid 0.0.27 → 0.0.28

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": "Solid compiler built on @tsrx/core",
4
4
  "license": "MIT",
5
5
  "author": "Dominic Gannaway",
6
- "version": "0.0.27",
6
+ "version": "0.0.28",
7
7
  "type": "module",
8
8
  "publishConfig": {
9
9
  "access": "public"
@@ -17,12 +17,16 @@
17
17
  ".": {
18
18
  "types": "./types/index.d.ts",
19
19
  "default": "./src/index.js"
20
+ },
21
+ "./ref": {
22
+ "types": "./types/ref.d.ts",
23
+ "default": "./src/ref.js"
20
24
  }
21
25
  },
22
26
  "dependencies": {
23
27
  "esrap": "^2.1.0",
24
28
  "zimmerframe": "^1.1.2",
25
- "@tsrx/core": "0.0.27"
29
+ "@tsrx/core": "0.0.28"
26
30
  },
27
31
  "peerDependencies": {
28
32
  "solid-js": ">=1.8 || >=2.0.0-beta"
package/src/index.js CHANGED
@@ -4,6 +4,8 @@
4
4
  import { createVolarMappingsResult, dedupeMappings, parseModule } from '@tsrx/core';
5
5
  import { transform } from './transform.js';
6
6
 
7
+ export { isRefProp } from './ref.js';
8
+
7
9
  /**
8
10
  * Parse tsrx-solid source code to an ESTree AST.
9
11
  * @param {string} source
package/src/ref.js ADDED
@@ -0,0 +1 @@
1
+ export * from '@tsrx/core/runtime/ref';
package/src/transform.js CHANGED
@@ -9,13 +9,18 @@ import {
9
9
  toJsxAttribute,
10
10
  validateAtMostOneRefAttribute,
11
11
  setLocation,
12
+ addJsxSetupDeclaration as add_jsx_setup_declaration,
12
13
  applyLazyTransforms as apply_lazy_transforms,
13
14
  collectLazyBindingsFromComponent as collect_lazy_bindings_from_component,
15
+ extractJsxSetupDeclarations as extract_jsx_setup_declarations,
14
16
  replaceLazyParams as replace_lazy_params,
15
17
  rewriteLoopContinuesToBareReturns as rewrite_loop_continues_to_bare_returns,
18
+ isRefPropExpression as is_ref_prop_expression,
16
19
  isInterleavedBody as is_interleaved_body_core,
17
20
  isCapturableJsxChild as is_capturable_jsx_child,
18
21
  captureJsxChild,
22
+ CREATE_REF_PROP_INTERNAL_NAME,
23
+ NORMALIZE_SPREAD_PROPS_INTERNAL_NAME,
19
24
  tsxNodeToJsxExpression as tsx_node_to_jsx_expression,
20
25
  // Shared AST builders (truly platform-agnostic utilities).
21
26
  clone_expression_node,
@@ -49,6 +54,7 @@ import { builders as b } from '@tsrx/core';
49
54
  * needs_match: boolean,
50
55
  * needs_errored: boolean,
51
56
  * needs_loading: boolean,
57
+ * needs_normalize_spread_props: boolean,
52
58
  * }} TransformContext
53
59
  */
54
60
 
@@ -79,6 +85,7 @@ const solid_platform = {
79
85
  // import injection goes through `hooks.injectImports`.
80
86
  suspense: 'solid-js',
81
87
  errorBoundary: 'solid-js',
88
+ refProp: '@tsrx/solid/ref',
82
89
  },
83
90
  jsx: {
84
91
  rewriteClassAttr: false,
@@ -103,6 +110,7 @@ const solid_platform = {
103
110
  needs_match: false,
104
111
  needs_errored: false,
105
112
  needs_loading: false,
113
+ needs_normalize_spread_props: false,
106
114
  }),
107
115
  validateComponentAwait: (await_expression, _component, ctx, _requires, source) => {
108
116
  const await_start = get_await_keyword_start(await_expression, source);
@@ -244,6 +252,7 @@ function component_to_function_declaration(component, transform_context) {
244
252
  }
245
253
  if (early_interleaved) {
246
254
  const jsx = to_jsx_child(child, transform_context);
255
+ outer.push(...extract_jsx_setup_declarations(jsx));
247
256
  if (is_capturable_jsx_child(jsx)) {
248
257
  const { declaration, reference } = captureJsxChild(jsx, early_capture_index++);
249
258
  outer.push(declaration);
@@ -288,6 +297,7 @@ function component_to_function_declaration(component, transform_context) {
288
297
  for (const child of effective_body) {
289
298
  if (is_jsx_child(child)) {
290
299
  const jsx = to_jsx_child(child, transform_context);
300
+ statements.push(...extract_jsx_setup_declarations(jsx));
291
301
  if (interleaved && is_capturable_jsx_child(jsx)) {
292
302
  const { declaration, reference } = captureJsxChild(jsx, capture_index++);
293
303
  statements.push(declaration);
@@ -482,6 +492,7 @@ function body_to_jsx_child(body_nodes, transform_context) {
482
492
 
483
493
  if (is_jsx_child(child)) {
484
494
  const jsx = to_jsx_child(child, transform_context);
495
+ statements.push(...extract_jsx_setup_declarations(jsx));
485
496
  if (interleaved && is_capturable_jsx_child(jsx)) {
486
497
  const { declaration, reference } = captureJsxChild(jsx, capture_index++);
487
498
  statements.push(declaration);
@@ -669,7 +680,9 @@ function loop_body_to_callback_statements(body_nodes, transform_context) {
669
680
  }
670
681
 
671
682
  if (is_jsx_child(child)) {
672
- children.push(to_jsx_child(child, transform_context));
683
+ const jsx = to_jsx_child(child, transform_context);
684
+ statements.push(...extract_jsx_setup_declarations(jsx));
685
+ children.push(jsx);
673
686
  } else if (is_bare_render_expression(child)) {
674
687
  children.push(to_jsx_expression_container(child, child));
675
688
  } else {
@@ -1350,6 +1363,53 @@ const TEMPLATE_FRAGMENT_ERROR =
1350
1363
  * @param {TransformContext} transform_context
1351
1364
  */
1352
1365
  function inject_solid_imports(program, transform_context) {
1366
+ if (transform_context.needs_ref_prop || transform_context.needs_normalize_spread_props) {
1367
+ const specifiers = [];
1368
+
1369
+ if (transform_context.needs_ref_prop) {
1370
+ specifiers.push({
1371
+ type: 'ImportSpecifier',
1372
+ imported: { type: 'Identifier', name: 'create_ref_prop', metadata: { path: [] } },
1373
+ local: {
1374
+ type: 'Identifier',
1375
+ name: CREATE_REF_PROP_INTERNAL_NAME,
1376
+ metadata: { path: [] },
1377
+ },
1378
+ metadata: { path: [] },
1379
+ });
1380
+ }
1381
+
1382
+ if (transform_context.needs_normalize_spread_props) {
1383
+ specifiers.push({
1384
+ type: 'ImportSpecifier',
1385
+ imported: {
1386
+ type: 'Identifier',
1387
+ name: 'normalize_spread_props',
1388
+ metadata: { path: [] },
1389
+ },
1390
+ local: {
1391
+ type: 'Identifier',
1392
+ name: NORMALIZE_SPREAD_PROPS_INTERNAL_NAME,
1393
+ metadata: { path: [] },
1394
+ },
1395
+ metadata: { path: [] },
1396
+ });
1397
+ }
1398
+
1399
+ program.body.unshift(
1400
+ /** @type {any} */ ({
1401
+ type: 'ImportDeclaration',
1402
+ specifiers,
1403
+ source: {
1404
+ type: 'Literal',
1405
+ value: '@tsrx/solid/ref',
1406
+ raw: "'@tsrx/solid/ref'",
1407
+ },
1408
+ metadata: { path: [] },
1409
+ }),
1410
+ );
1411
+ }
1412
+
1353
1413
  const needed = [];
1354
1414
  if (transform_context.needs_show) needed.push('Show');
1355
1415
  if (transform_context.needs_for) needed.push('For');
@@ -1610,16 +1670,146 @@ function has_text_content_attribute(attributes) {
1610
1670
  * @returns {any[]}
1611
1671
  */
1612
1672
  function transform_element_attributes(raw_attrs, is_composite, transform_context) {
1613
- void is_composite;
1614
1673
  validateAtMostOneRefAttribute(raw_attrs, /** @type {any} */ (transform_context));
1615
1674
  /** @type {any[]} */
1616
1675
  const result = [];
1617
1676
 
1618
- for (const attr of raw_attrs) {
1677
+ for (const attr of normalize_solid_named_ref_attributes(
1678
+ raw_attrs,
1679
+ !is_composite,
1680
+ transform_context,
1681
+ )) {
1619
1682
  if (!attr) continue;
1620
1683
  result.push(toJsxAttribute(attr, /** @type {any} */ (transform_context)));
1621
1684
  }
1622
- return mergeDuplicateRefs(result, /** @type {any} */ (transform_context));
1685
+ return mergeDuplicateRefs(
1686
+ normalize_solid_host_ref_spreads(result, !is_composite, transform_context),
1687
+ /** @type {any} */ (transform_context),
1688
+ );
1689
+ }
1690
+
1691
+ /**
1692
+ * @param {any[]} attrs
1693
+ * @param {boolean} is_host
1694
+ * @param {TransformContext} transform_context
1695
+ * @returns {any[]}
1696
+ */
1697
+ function normalize_solid_named_ref_attributes(attrs, is_host, transform_context) {
1698
+ if (!is_host) return attrs;
1699
+
1700
+ return attrs.map((attr) => {
1701
+ if (
1702
+ !attr ||
1703
+ attr.type !== 'Attribute' ||
1704
+ attr.name?.type !== 'Identifier' ||
1705
+ attr.name.name === 'ref' ||
1706
+ !(
1707
+ attr.value?.type === 'RefExpression' ||
1708
+ is_ref_prop_expression(attr.value) ||
1709
+ (attr.value?.type === 'JSXExpressionContainer' &&
1710
+ is_ref_prop_expression(attr.value.expression))
1711
+ )
1712
+ ) {
1713
+ return attr;
1714
+ }
1715
+
1716
+ if (transform_context.typeOnly) {
1717
+ return {
1718
+ ...attr,
1719
+ name: {
1720
+ ...attr.name,
1721
+ metadata: { ...(attr.name.metadata || {}), disable_verification: true },
1722
+ },
1723
+ };
1724
+ }
1725
+
1726
+ return {
1727
+ ...attr,
1728
+ name: { ...attr.name, name: 'ref' },
1729
+ };
1730
+ });
1731
+ }
1732
+
1733
+ /**
1734
+ * @param {any[]} attrs
1735
+ * @param {boolean} is_host
1736
+ * @param {TransformContext} transform_context
1737
+ * @returns {any[]}
1738
+ */
1739
+ function normalize_solid_host_ref_spreads(attrs, is_host, transform_context) {
1740
+ if (!is_host) return attrs;
1741
+
1742
+ const ref_exprs = attrs
1743
+ .filter((attr) => is_solid_jsx_ref_attribute(attr))
1744
+ .map((attr) => attr.value.expression);
1745
+ const needs_synthetic_spread_ref = ref_exprs.length > 0;
1746
+
1747
+ return attrs.flatMap((attr) => {
1748
+ if (!attr || attr.type !== 'JSXSpreadAttribute') {
1749
+ return [attr];
1750
+ }
1751
+
1752
+ transform_context.needs_normalize_spread_props = true;
1753
+ const normalized = b.call(NORMALIZE_SPREAD_PROPS_INTERNAL_NAME, attr.argument);
1754
+
1755
+ if (needs_synthetic_spread_ref) {
1756
+ const normalized_id = create_generated_identifier(
1757
+ create_solid_spread_props_name(transform_context),
1758
+ );
1759
+ const spread = {
1760
+ ...attr,
1761
+ argument: clone_identifier(normalized_id),
1762
+ };
1763
+ const ref_attr = b.jsx_attribute(
1764
+ b.jsx_id('ref'),
1765
+ b.jsx_expression_container(b.member(clone_identifier(normalized_id), 'ref'), attr),
1766
+ false,
1767
+ attr,
1768
+ );
1769
+ ref_attr.metadata = { ...(ref_attr.metadata || {}) };
1770
+ /** @type {any} */ (ref_attr.metadata).from_ref_keyword = true;
1771
+ add_jsx_setup_declaration(spread, b.let(clone_identifier(normalized_id), normalized));
1772
+
1773
+ return [spread, ref_attr];
1774
+ }
1775
+
1776
+ return [
1777
+ {
1778
+ ...attr,
1779
+ argument: normalized,
1780
+ },
1781
+ ];
1782
+ });
1783
+ }
1784
+
1785
+ /**
1786
+ * @param {TransformContext} transform_context
1787
+ * @returns {string}
1788
+ */
1789
+ function create_solid_spread_props_name(transform_context) {
1790
+ if (transform_context.helper_state) {
1791
+ transform_context.helper_state.next_id += 1;
1792
+ return `${transform_context.helper_state.base_name}__spread_props${transform_context.helper_state.next_id}`;
1793
+ }
1794
+
1795
+ transform_context.local_statement_component_index += 1;
1796
+ return `_tsrx_spread_props_${transform_context.local_statement_component_index}`;
1797
+ }
1798
+
1799
+ /**
1800
+ * @param {any} attr
1801
+ * @returns {boolean}
1802
+ */
1803
+ function is_solid_jsx_ref_attribute(attr) {
1804
+ return !!(
1805
+ attr &&
1806
+ attr.type === 'JSXAttribute' &&
1807
+ attr.name?.type === 'JSXIdentifier' &&
1808
+ attr.name.name === 'ref' &&
1809
+ attr.value?.type === 'JSXExpressionContainer' &&
1810
+ attr.value.expression &&
1811
+ attr.value.expression.type !== 'JSXEmptyExpression'
1812
+ );
1623
1813
  }
1624
1814
 
1625
1815
  /**
package/types/index.d.ts CHANGED
@@ -3,6 +3,8 @@ import type { CompileFn, ParseOptions, VolarCompileFn } from '@tsrx/core/types';
3
3
 
4
4
  export function parse(source: string, filename?: string, options?: ParseOptions): Program;
5
5
 
6
+ export { isRefProp } from './ref.js';
7
+
6
8
  export const compile: CompileFn;
7
9
 
8
10
  export const compile_to_volar_mappings: VolarCompileFn;
package/types/ref.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from '@tsrx/core/runtime/ref';