@tsrx/core 0.1.20 → 0.1.22

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.
@@ -4,11 +4,12 @@
4
4
  * @param {Iterable<T> | Iterator<T>} iterable
5
5
  * @param {(item: T, index: number, is_last: boolean) => U} fn
6
6
  * @param {() => U | U[]} [tail]
7
+ * @param {() => U | U[]} [empty]
7
8
  * @returns {U[]}
8
9
  */
9
- export function map_iterable(iterable, fn, tail) {
10
+ export function map_iterable(iterable, fn, tail, empty) {
10
11
  if (Array.isArray(iterable)) {
11
- return map_array(iterable, fn, tail);
12
+ return map_array(iterable, fn, tail, empty);
12
13
  }
13
14
 
14
15
  /** @type {Iterator<T>} */
@@ -25,14 +26,14 @@ export function map_iterable(iterable, fn, tail) {
25
26
 
26
27
  var current = iterator.next();
27
28
  if (current.done) {
28
- if (!tail) {
29
+ if (!empty) {
29
30
  return [];
30
31
  }
31
- var tail_value = tail();
32
- if (Array.isArray(tail_value)) {
33
- return tail_value;
32
+ var empty_value = empty();
33
+ if (Array.isArray(empty_value)) {
34
+ return empty_value;
34
35
  }
35
- return [tail_value];
36
+ return [empty_value];
36
37
  }
37
38
 
38
39
  var index = 0;
@@ -71,19 +72,20 @@ export function map_iterable(iterable, fn, tail) {
71
72
  * @param {Array<T>} array
72
73
  * @param {(item: T, index: number, is_last: boolean) => U} fn
73
74
  * @param {() => U | U[]} [tail]
75
+ * @param {() => U | U[]} [empty]
74
76
  * @returns {U[]}
75
77
  */
76
- function map_array(array, fn, tail) {
78
+ function map_array(array, fn, tail, empty) {
77
79
  var length = array.length;
78
80
  if (length === 0) {
79
- if (!tail) {
81
+ if (!empty) {
80
82
  return [];
81
83
  }
82
- var tail_value = tail();
83
- if (Array.isArray(tail_value)) {
84
- return tail_value;
84
+ var empty_value = empty();
85
+ if (Array.isArray(empty_value)) {
86
+ return empty_value;
85
87
  }
86
- return [tail_value];
88
+ return [empty_value];
87
89
  }
88
90
  var result = [];
89
91
  for (var i = 0; i < length; i++) {
package/src/scope.js CHANGED
@@ -107,18 +107,28 @@ export function create_scopes(ast, root, parent, error_options) {
107
107
  }
108
108
  },
109
109
 
110
- Element(node, { state, next }) {
111
- const scope = state.scope.child();
112
- scopes.set(node, scope);
110
+ JSXElement(node, { state, next }) {
111
+ if (node.metadata?.native_tsrx) {
112
+ const scope = state.scope.child();
113
+ scopes.set(node, scope);
113
114
 
114
- next({ scope });
115
+ next({ scope });
116
+ return;
117
+ }
118
+
119
+ next();
115
120
  },
116
121
 
117
- TsrxFragment(node, { state, next }) {
118
- const scope = state.scope.child();
119
- scopes.set(node, scope);
122
+ JSXFragment(node, { state, next }) {
123
+ if (node.metadata?.native_tsrx) {
124
+ const scope = state.scope.child();
125
+ scopes.set(node, scope);
120
126
 
121
- next({ scope });
127
+ next({ scope });
128
+ return;
129
+ }
130
+
131
+ next();
122
132
  },
123
133
 
124
134
  TSModuleDeclaration(node, { state, next }) {
@@ -169,6 +179,8 @@ export function create_scopes(ast, root, parent, error_options) {
169
179
  ForInStatement: create_block_scope,
170
180
  ForOfStatement: create_block_scope,
171
181
  SwitchStatement: create_block_scope,
182
+ JSXForExpression: create_block_scope,
183
+ JSXSwitchExpression: create_block_scope,
172
184
  BlockStatement(node, context) {
173
185
  const parent = context.path.at(-1);
174
186
  if (
@@ -192,13 +204,16 @@ export function create_scopes(ast, root, parent, error_options) {
192
204
  for (const declarator of node.declarations) {
193
205
  /** @type {Binding[]} */
194
206
  const bindings = [];
195
- const initial = /** @type {AST.Expression | AST.TsrxFragment | null} */ (declarator.init);
207
+ const initial = /** @type {AST.Expression | null} */ (declarator.init);
196
208
 
197
209
  state.scope.declarators.set(declarator, bindings);
198
210
 
199
211
  for (const id of extract_identifiers(declarator.id)) {
200
212
  const binding = state.scope.declare(id, 'normal', node.kind, initial);
201
- if (initial?.type === 'TsrxFragment') {
213
+ if (
214
+ (initial?.type === 'JSXElement' || initial?.type === 'JSXFragment') &&
215
+ initial.metadata?.native_tsrx
216
+ ) {
202
217
  binding.metadata = {
203
218
  ...(binding.metadata ?? {}),
204
219
  is_template_value: true,
@@ -14,7 +14,7 @@
14
14
  * end_column: number,
15
15
  * code: string,
16
16
  * metadata: {
17
- * css?: AST.Element['metadata']['css']
17
+ * css?: AST.Node['metadata']['css']
18
18
  * },
19
19
  * }} CodePosition
20
20
  * @typedef {{
@@ -42,7 +42,11 @@ export function find_first_top_level_await(node, inside_nested_function) {
42
42
  return null;
43
43
  }
44
44
 
45
- if (node.type === 'AwaitExpression' || (node.type === 'ForOfStatement' && node.await === true)) {
45
+ if (
46
+ node.type === 'AwaitExpression' ||
47
+ (node.type === 'ForOfStatement' && node.await === true) ||
48
+ (node.type === 'JSXForExpression' && node.await === true)
49
+ ) {
46
50
  return node;
47
51
  }
48
52
 
@@ -89,6 +89,44 @@ export function clone_jsx_name(name, source_node = name) {
89
89
  return name;
90
90
  }
91
91
 
92
+ /**
93
+ * Convert a JSX tag name back into a JavaScript expression. Dynamic element
94
+ * tags are parsed as JSX-shaped names, but the runtime alias needs ordinary JS.
95
+ *
96
+ * @param {any} name
97
+ * @returns {any}
98
+ */
99
+ export function jsx_name_to_expression(name) {
100
+ if (!name) return name;
101
+ if (name.type === 'JSXIdentifier') {
102
+ return set_loc(
103
+ /** @type {any} */ ({
104
+ type: 'Identifier',
105
+ name: name.name,
106
+ metadata: name.metadata || { path: [] },
107
+ }),
108
+ name,
109
+ );
110
+ }
111
+ if (name.type === 'JSXMemberExpression') {
112
+ return set_loc(
113
+ /** @type {any} */ ({
114
+ type: 'MemberExpression',
115
+ object: jsx_name_to_expression(name.object),
116
+ property: jsx_name_to_expression(name.property),
117
+ computed: false,
118
+ optional: false,
119
+ metadata: name.metadata || { path: [] },
120
+ }),
121
+ name,
122
+ );
123
+ }
124
+ if (name.type === 'Identifier' || name.type === 'MemberExpression') {
125
+ return clone_expression_node(name);
126
+ }
127
+ return name;
128
+ }
129
+
92
130
  /**
93
131
  * @returns {AST.Literal}
94
132
  */
@@ -242,11 +280,10 @@ export function is_jsx_child(node) {
242
280
  t === 'JSXFragment' ||
243
281
  t === 'JSXExpressionContainer' ||
244
282
  t === 'JSXText' ||
245
- t === 'TsrxFragment' ||
246
- t === 'TsxCompat' ||
247
- t === 'Element' ||
248
- t === 'Text' ||
249
- t === 'TSRXExpression' ||
283
+ t === 'JSXIfExpression' ||
284
+ t === 'JSXForExpression' ||
285
+ t === 'JSXSwitchExpression' ||
286
+ t === 'JSXTryExpression' ||
250
287
  t === 'IfStatement' ||
251
288
  t === 'ForOfStatement' ||
252
289
  t === 'SwitchStatement' ||
@@ -316,10 +353,10 @@ export function is_dynamic_element_id(id) {
316
353
  if (!id || typeof id !== 'object') {
317
354
  return false;
318
355
  }
319
- if (id.type === 'Identifier') {
356
+ if (id.type === 'Identifier' || id.type === 'JSXIdentifier') {
320
357
  return !!id.tracked;
321
358
  }
322
- if (id.type === 'MemberExpression') {
359
+ if (id.type === 'MemberExpression' || id.type === 'JSXMemberExpression') {
323
360
  return is_dynamic_element_id(id.object);
324
361
  }
325
362
  return false;
@@ -368,58 +405,6 @@ export function flatten_switch_consequent(consequent) {
368
405
  return result;
369
406
  }
370
407
 
371
- /**
372
- * Compute fall-through expansions for each `case` in a `switch`. JavaScript
373
- * `switch` semantics say that once a case body executes, execution continues
374
- * into the bodies of subsequent cases until a `break` or terminal `return` is
375
- * hit. We pre-compute, per case, the flat list of statements that should run
376
- * when that case is the entry point — so downstream targets (which render each
377
- * case independently rather than executing fall-through at runtime) still
378
- * produce the right output.
379
- *
380
- * Walking right-to-left lets each case reuse the next case's already-expanded
381
- * tail without recomputation. Downstream nodes are deep-cloned when absorbed
382
- * so each case's expanded body owns its own AST subtree.
383
- *
384
- * @param {any[]} cases
385
- * @returns {Array<{ test: any, body: any[], source: any }>}
386
- */
387
- export function expand_switch_cases_for_fallthrough(cases) {
388
- /** @type {Array<{ test: any, body: any[], source: any }>} */
389
- const expanded = new Array(cases.length);
390
- for (let i = cases.length - 1; i >= 0; i--) {
391
- const consequent = flatten_switch_consequent(cases[i].consequent || []);
392
- const body = [];
393
- let has_terminal = false;
394
- for (const child of consequent) {
395
- if (child.type === 'BreakStatement') {
396
- has_terminal = true;
397
- break;
398
- }
399
- body.push(child);
400
- if (child.type === 'ReturnStatement') {
401
- has_terminal = true;
402
- break;
403
- }
404
- }
405
- // Strip locations from cloned downstream nodes. Only the original case
406
- // (one entry up the chain) keeps `loc`/`start`/`end`; clones inlined
407
- // into upstream cases would otherwise point editor IntelliSense at the
408
- // same source range multiple times (one hover/go-to-definition per
409
- // fall-through entry point), producing double/triple results in Volar.
410
- const downstream =
411
- !has_terminal && i + 1 < cases.length
412
- ? expanded[i + 1].body.map((n) => clone_expression_node(n, false))
413
- : [];
414
- expanded[i] = {
415
- test: cases[i].test,
416
- body: [...body, ...downstream],
417
- source: cases[i],
418
- };
419
- }
420
- return expanded;
421
- }
422
-
423
408
  /**
424
409
  * @param {AST.Expression | null | undefined} expression
425
410
  * @returns {boolean}
@@ -4,17 +4,20 @@
4
4
  import tsx from 'esrap/languages/tsx';
5
5
 
6
6
  /**
7
- * Zimmerframe provides `path` as the ancestor chain (in original pre-transform
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).
7
+ * Zimmerframe provides `path` as the ancestor chain. A native template node whose
8
+ * parent is another native template node renders as a JSX child; anywhere else it
9
+ * renders as a standalone expression (e.g. a return value).
11
10
  *
12
11
  * @param {any[]} path
13
12
  * @returns {boolean}
14
13
  */
15
14
  export function in_jsx_child_context(path) {
16
15
  const parent = path[path.length - 1];
17
- return !!parent && parent.type === 'Element';
16
+ return (
17
+ !!parent &&
18
+ (parent.type === 'JSXElement' || parent.type === 'JSXFragment') &&
19
+ parent.metadata?.native_tsrx
20
+ );
18
21
  }
19
22
 
20
23
  /**