@tsrx/core 0.1.19 → 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.
- package/package.json +3 -3
- package/src/analyze/prune.js +119 -61
- package/src/analyze/validation.js +122 -5
- package/src/index.js +11 -3
- package/src/parse/index.js +157 -99
- package/src/plugin.js +2417 -956
- package/src/runtime/iterable.js +15 -13
- package/src/scope.js +25 -10
- package/src/source-map-utils.js +1 -1
- package/src/transform/await.js +5 -1
- package/src/transform/jsx/ast-builders.js +46 -62
- package/src/transform/jsx/helpers.js +8 -5
- package/src/transform/jsx/index.js +756 -358
- package/src/transform/jsx-interleave.js +1 -2
- package/src/transform/scoping.js +26 -63
- package/src/transform/segments.js +53 -5
- package/src/transform/style-ref.js +3 -11
- package/src/utils/builders.js +2 -3
- package/types/index.d.ts +181 -125
- package/types/jsx-platform.d.ts +6 -11
- package/types/parse.d.ts +36 -10
- package/types/runtime/ref.d.ts +1 -0
package/src/runtime/iterable.js
CHANGED
|
@@ -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 (!
|
|
29
|
+
if (!empty) {
|
|
29
30
|
return [];
|
|
30
31
|
}
|
|
31
|
-
var
|
|
32
|
-
if (Array.isArray(
|
|
33
|
-
return
|
|
32
|
+
var empty_value = empty();
|
|
33
|
+
if (Array.isArray(empty_value)) {
|
|
34
|
+
return empty_value;
|
|
34
35
|
}
|
|
35
|
-
return [
|
|
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 (!
|
|
81
|
+
if (!empty) {
|
|
80
82
|
return [];
|
|
81
83
|
}
|
|
82
|
-
var
|
|
83
|
-
if (Array.isArray(
|
|
84
|
-
return
|
|
84
|
+
var empty_value = empty();
|
|
85
|
+
if (Array.isArray(empty_value)) {
|
|
86
|
+
return empty_value;
|
|
85
87
|
}
|
|
86
|
-
return [
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
115
|
+
next({ scope });
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
next();
|
|
115
120
|
},
|
|
116
121
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
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 |
|
|
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 (
|
|
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,
|
package/src/source-map-utils.js
CHANGED
package/src/transform/await.js
CHANGED
|
@@ -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 (
|
|
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,12 +280,10 @@ export function is_jsx_child(node) {
|
|
|
242
280
|
t === 'JSXFragment' ||
|
|
243
281
|
t === 'JSXExpressionContainer' ||
|
|
244
282
|
t === 'JSXText' ||
|
|
245
|
-
t === '
|
|
246
|
-
t === '
|
|
247
|
-
t === '
|
|
248
|
-
t === '
|
|
249
|
-
t === 'Text' ||
|
|
250
|
-
t === 'TSRXExpression' ||
|
|
283
|
+
t === 'JSXIfExpression' ||
|
|
284
|
+
t === 'JSXForExpression' ||
|
|
285
|
+
t === 'JSXSwitchExpression' ||
|
|
286
|
+
t === 'JSXTryExpression' ||
|
|
251
287
|
t === 'IfStatement' ||
|
|
252
288
|
t === 'ForOfStatement' ||
|
|
253
289
|
t === 'SwitchStatement' ||
|
|
@@ -256,8 +292,8 @@ export function is_jsx_child(node) {
|
|
|
256
292
|
}
|
|
257
293
|
|
|
258
294
|
/**
|
|
259
|
-
*
|
|
260
|
-
*
|
|
295
|
+
* Expression-position lowering unwraps single-expression native fragments to
|
|
296
|
+
* the inner expression.
|
|
261
297
|
* When such a node appears directly in a component or statement render body,
|
|
262
298
|
* the unwrapped expression is still render output rather than an executable
|
|
263
299
|
* statement.
|
|
@@ -317,10 +353,10 @@ export function is_dynamic_element_id(id) {
|
|
|
317
353
|
if (!id || typeof id !== 'object') {
|
|
318
354
|
return false;
|
|
319
355
|
}
|
|
320
|
-
if (id.type === 'Identifier') {
|
|
356
|
+
if (id.type === 'Identifier' || id.type === 'JSXIdentifier') {
|
|
321
357
|
return !!id.tracked;
|
|
322
358
|
}
|
|
323
|
-
if (id.type === 'MemberExpression') {
|
|
359
|
+
if (id.type === 'MemberExpression' || id.type === 'JSXMemberExpression') {
|
|
324
360
|
return is_dynamic_element_id(id.object);
|
|
325
361
|
}
|
|
326
362
|
return false;
|
|
@@ -369,58 +405,6 @@ export function flatten_switch_consequent(consequent) {
|
|
|
369
405
|
return result;
|
|
370
406
|
}
|
|
371
407
|
|
|
372
|
-
/**
|
|
373
|
-
* Compute fall-through expansions for each `case` in a `switch`. JavaScript
|
|
374
|
-
* `switch` semantics say that once a case body executes, execution continues
|
|
375
|
-
* into the bodies of subsequent cases until a `break` or terminal `return` is
|
|
376
|
-
* hit. We pre-compute, per case, the flat list of statements that should run
|
|
377
|
-
* when that case is the entry point — so downstream targets (which render each
|
|
378
|
-
* case independently rather than executing fall-through at runtime) still
|
|
379
|
-
* produce the right output.
|
|
380
|
-
*
|
|
381
|
-
* Walking right-to-left lets each case reuse the next case's already-expanded
|
|
382
|
-
* tail without recomputation. Downstream nodes are deep-cloned when absorbed
|
|
383
|
-
* so each case's expanded body owns its own AST subtree.
|
|
384
|
-
*
|
|
385
|
-
* @param {any[]} cases
|
|
386
|
-
* @returns {Array<{ test: any, body: any[], source: any }>}
|
|
387
|
-
*/
|
|
388
|
-
export function expand_switch_cases_for_fallthrough(cases) {
|
|
389
|
-
/** @type {Array<{ test: any, body: any[], source: any }>} */
|
|
390
|
-
const expanded = new Array(cases.length);
|
|
391
|
-
for (let i = cases.length - 1; i >= 0; i--) {
|
|
392
|
-
const consequent = flatten_switch_consequent(cases[i].consequent || []);
|
|
393
|
-
const body = [];
|
|
394
|
-
let has_terminal = false;
|
|
395
|
-
for (const child of consequent) {
|
|
396
|
-
if (child.type === 'BreakStatement') {
|
|
397
|
-
has_terminal = true;
|
|
398
|
-
break;
|
|
399
|
-
}
|
|
400
|
-
body.push(child);
|
|
401
|
-
if (child.type === 'ReturnStatement') {
|
|
402
|
-
has_terminal = true;
|
|
403
|
-
break;
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
// Strip locations from cloned downstream nodes. Only the original case
|
|
407
|
-
// (one entry up the chain) keeps `loc`/`start`/`end`; clones inlined
|
|
408
|
-
// into upstream cases would otherwise point editor IntelliSense at the
|
|
409
|
-
// same source range multiple times (one hover/go-to-definition per
|
|
410
|
-
// fall-through entry point), producing double/triple results in Volar.
|
|
411
|
-
const downstream =
|
|
412
|
-
!has_terminal && i + 1 < cases.length
|
|
413
|
-
? expanded[i + 1].body.map((n) => clone_expression_node(n, false))
|
|
414
|
-
: [];
|
|
415
|
-
expanded[i] = {
|
|
416
|
-
test: cases[i].test,
|
|
417
|
-
body: [...body, ...downstream],
|
|
418
|
-
source: cases[i],
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
return expanded;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
408
|
/**
|
|
425
409
|
* @param {AST.Expression | null | undefined} expression
|
|
426
410
|
* @returns {boolean}
|
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
import tsx from 'esrap/languages/tsx';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Zimmerframe provides `path` as the ancestor chain
|
|
8
|
-
*
|
|
9
|
-
* `Element` will render as a JSX child of that element; anywhere else it
|
|
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
|
|
10
9
|
* renders as a standalone expression (e.g. a return value).
|
|
11
10
|
*
|
|
12
11
|
* @param {any[]} path
|
|
@@ -14,7 +13,11 @@ import tsx from 'esrap/languages/tsx';
|
|
|
14
13
|
*/
|
|
15
14
|
export function in_jsx_child_context(path) {
|
|
16
15
|
const parent = path[path.length - 1];
|
|
17
|
-
return
|
|
16
|
+
return (
|
|
17
|
+
!!parent &&
|
|
18
|
+
(parent.type === 'JSXElement' || parent.type === 'JSXFragment') &&
|
|
19
|
+
parent.metadata?.native_tsrx
|
|
20
|
+
);
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
/**
|
|
@@ -35,7 +38,7 @@ export function set_node_path_metadata(node, path) {
|
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
/**
|
|
38
|
-
* Flatten a
|
|
41
|
+
* Flatten a JSX-compatible island's children into a single expression. In a
|
|
39
42
|
* JSX-child position, a JSXExpressionContainer `{expr}` is valid and must stay
|
|
40
43
|
* wrapped. In an expression position (e.g. `return ...`), `{expr}` parses as
|
|
41
44
|
* a block/object literal, so unwrap to `expr`.
|