@tsrx/core 0.0.6 → 0.0.7

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.6",
6
+ "version": "0.0.7",
7
7
  "type": "module",
8
8
  "repository": {
9
9
  "type": "git",
package/src/index.js CHANGED
@@ -164,6 +164,13 @@ export {
164
164
  is_capturable_jsx_child as isCapturableJsxChild,
165
165
  capture_jsx_child as captureJsxChild,
166
166
  } from './transform/jsx-interleave.js';
167
+ export {
168
+ is_static_literal as isStaticLiteral,
169
+ is_hoist_safe_expression as isHoistSafeExpression,
170
+ is_hoist_safe_jsx_child as isHoistSafeJsxChild,
171
+ is_hoist_safe_jsx_attribute as isHoistSafeJsxAttribute,
172
+ is_hoist_safe_jsx_node as isHoistSafeJsxNode,
173
+ } from './transform/jsx-hoist.js';
167
174
 
168
175
  // Analyze
169
176
  export { analyze_css as analyzeCss } from './analyze/css-analyze.js';
@@ -0,0 +1,119 @@
1
+ /** @import * as ESTreeJSX from 'estree-jsx' */
2
+
3
+ /**
4
+ * Predicates that decide whether a JSX subtree can be safely hoisted out of a
5
+ * component body into a module-level `const`. A subtree is hoist-safe only
6
+ * when evaluating it at module-load time produces the same value as
7
+ * evaluating it on every render — i.e. it contains no identifier references,
8
+ * no calls, no spreads, and no other render-time expressions.
9
+ */
10
+
11
+ /**
12
+ * @param {import('estree').Literal} node
13
+ * @returns {boolean}
14
+ */
15
+ export function is_static_literal(node) {
16
+ return (
17
+ node.value === null ||
18
+ typeof node.value === 'string' ||
19
+ typeof node.value === 'number' ||
20
+ typeof node.value === 'boolean' ||
21
+ typeof node.value === 'bigint'
22
+ );
23
+ }
24
+
25
+ /**
26
+ * @param {any} node
27
+ * @returns {boolean}
28
+ */
29
+ export function is_hoist_safe_expression(node) {
30
+ if (!node || typeof node !== 'object') return false;
31
+
32
+ switch (node.type) {
33
+ case 'Literal':
34
+ return is_static_literal(node);
35
+ case 'TemplateLiteral':
36
+ return node.expressions.length === 0;
37
+ case 'UnaryExpression':
38
+ return node.operator !== 'delete' && is_hoist_safe_expression(node.argument);
39
+ case 'BinaryExpression':
40
+ case 'LogicalExpression':
41
+ return is_hoist_safe_expression(node.left) && is_hoist_safe_expression(node.right);
42
+ case 'ConditionalExpression':
43
+ return (
44
+ is_hoist_safe_expression(node.test) &&
45
+ is_hoist_safe_expression(node.consequent) &&
46
+ is_hoist_safe_expression(node.alternate)
47
+ );
48
+ case 'SequenceExpression':
49
+ return node.expressions.every(is_hoist_safe_expression);
50
+ case 'ParenthesizedExpression':
51
+ return is_hoist_safe_expression(node.expression);
52
+ case 'JSXElement':
53
+ return is_hoist_safe_jsx_node(node);
54
+ case 'JSXFragment':
55
+ return node.children.every(is_hoist_safe_jsx_child);
56
+ default:
57
+ return false;
58
+ }
59
+ }
60
+
61
+ /**
62
+ * @param {any} node
63
+ * @returns {boolean}
64
+ */
65
+ export function is_hoist_safe_jsx_child(node) {
66
+ if (!node || typeof node !== 'object') return false;
67
+
68
+ switch (node.type) {
69
+ case 'JSXText':
70
+ return true;
71
+ case 'JSXElement':
72
+ return is_hoist_safe_jsx_node(node);
73
+ case 'JSXFragment':
74
+ return node.children.every(is_hoist_safe_jsx_child);
75
+ case 'JSXExpressionContainer':
76
+ return (
77
+ node.expression.type !== 'JSXEmptyExpression' && is_hoist_safe_expression(node.expression)
78
+ );
79
+ default:
80
+ return false;
81
+ }
82
+ }
83
+
84
+ /**
85
+ * @param {ESTreeJSX.JSXAttribute | ESTreeJSX.JSXSpreadAttribute} attribute
86
+ * @returns {boolean}
87
+ */
88
+ export function is_hoist_safe_jsx_attribute(attribute) {
89
+ if (attribute.type === 'JSXSpreadAttribute') return false;
90
+ if (attribute.value == null) return true;
91
+
92
+ if (attribute.value.type === 'Literal') {
93
+ return is_static_literal(attribute.value);
94
+ }
95
+
96
+ if (attribute.value.type === 'JSXExpressionContainer') {
97
+ return (
98
+ attribute.value.expression.type !== 'JSXEmptyExpression' &&
99
+ is_hoist_safe_expression(attribute.value.expression)
100
+ );
101
+ }
102
+
103
+ return false;
104
+ }
105
+
106
+ /**
107
+ * @param {ESTreeJSX.JSXElement | ESTreeJSX.JSXFragment} node
108
+ * @returns {boolean}
109
+ */
110
+ export function is_hoist_safe_jsx_node(node) {
111
+ if (node.type === 'JSXFragment') {
112
+ return node.children.every(is_hoist_safe_jsx_child);
113
+ }
114
+
115
+ return (
116
+ node.openingElement.attributes.every(is_hoist_safe_jsx_attribute) &&
117
+ node.children.every(is_hoist_safe_jsx_child)
118
+ );
119
+ }
package/types/index.d.ts CHANGED
@@ -98,6 +98,12 @@ declare module 'estree' {
98
98
  };
99
99
  }
100
100
 
101
+ interface SimpleCallExpression {
102
+ metadata: BaseNodeMetaData & {
103
+ hash?: string;
104
+ };
105
+ }
106
+
101
107
  type Accessibility = 'public' | 'protected' | 'private'; // missing in acorn-typescript types
102
108
  interface MethodDefinition {
103
109
  typeParameters?: TSTypeParameterDeclaration;
@@ -1335,6 +1341,11 @@ export interface AnalysisState extends BaseState {
1335
1341
  styleClasses?: StyleClasses;
1336
1342
  };
1337
1343
  mode: CompileOptions['mode'];
1344
+ // keep this as an object as we destructure
1345
+ module: {
1346
+ // Incremented counter for generating unique track/trackAsync hashes
1347
+ track_id: number;
1348
+ };
1338
1349
  }
1339
1350
 
1340
1351
  export interface TransformServerState extends BaseState {