@tsrx/core 0.1.10 → 0.1.12
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 +7 -7
- package/src/index.js +1 -0
- package/src/plugin.js +130 -15
- package/src/scope.js +8 -1
- package/src/source-map-utils.js +19 -0
- package/src/transform/jsx/helpers.js +2 -0
- package/src/transform/jsx/index.js +91 -43
- package/src/transform/segments.js +14 -1
- package/types/index.d.ts +10 -2
- package/types/jsx-platform.d.ts +39 -1
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.1.
|
|
6
|
+
"version": "0.1.12",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
@@ -80,12 +80,12 @@
|
|
|
80
80
|
"solid-js": "2.0.0-beta.7",
|
|
81
81
|
"typescript": "^5.9.3",
|
|
82
82
|
"vscode-languageserver-types": "^3.17.5",
|
|
83
|
-
"vue": "3.6.0-beta.
|
|
84
|
-
"vue-jsx-vapor": "^3.2.
|
|
85
|
-
"@tsrx/preact": "0.1.
|
|
86
|
-
"@tsrx/react": "0.2.
|
|
87
|
-
"@tsrx/solid": "0.1.
|
|
88
|
-
"@tsrx/vue": "0.1.
|
|
83
|
+
"vue": "3.6.0-beta.12",
|
|
84
|
+
"vue-jsx-vapor": "^3.2.14",
|
|
85
|
+
"@tsrx/preact": "0.1.12",
|
|
86
|
+
"@tsrx/react": "0.2.12",
|
|
87
|
+
"@tsrx/solid": "0.1.12",
|
|
88
|
+
"@tsrx/vue": "0.1.12"
|
|
89
89
|
},
|
|
90
90
|
"files": [
|
|
91
91
|
"src",
|
package/src/index.js
CHANGED
|
@@ -146,6 +146,7 @@ export {
|
|
|
146
146
|
clone_switch_helper_invocation as cloneSwitchHelperInvocation,
|
|
147
147
|
collect_param_bindings as collectParamBindings,
|
|
148
148
|
collect_statement_bindings as collectStatementBindings,
|
|
149
|
+
create_hook_safe_helper as createHookSafeHelper,
|
|
149
150
|
create_host_html_attribute as createHostHtmlAttribute,
|
|
150
151
|
create_host_html_conflict_error as createHostHtmlConflictError,
|
|
151
152
|
createJsxTransform,
|
package/src/plugin.js
CHANGED
|
@@ -275,6 +275,7 @@ export function TSRXPlugin(config) {
|
|
|
275
275
|
#componentDepth = 0;
|
|
276
276
|
#functionBodyDepth = 0;
|
|
277
277
|
#allowExpressionContainerTrailingSemicolon = false;
|
|
278
|
+
#tsxIslandExpressionDepth = 0;
|
|
278
279
|
|
|
279
280
|
/**
|
|
280
281
|
* @type {Parse.Parser['finishNode']}
|
|
@@ -478,7 +479,7 @@ export function TSRXPlugin(config) {
|
|
|
478
479
|
}
|
|
479
480
|
|
|
480
481
|
if (this.type === tt.braceL) {
|
|
481
|
-
body.push(this
|
|
482
|
+
body.push(this.#parseTsxIslandExpressionContainer());
|
|
482
483
|
} else if (this.type === tstt.jsxTagStart) {
|
|
483
484
|
body.push(super.jsx_parseElement());
|
|
484
485
|
} else {
|
|
@@ -492,6 +493,84 @@ export function TSRXPlugin(config) {
|
|
|
492
493
|
}
|
|
493
494
|
}
|
|
494
495
|
|
|
496
|
+
#parseTsxIslandExpressionContainer() {
|
|
497
|
+
this.#tsxIslandExpressionDepth++;
|
|
498
|
+
try {
|
|
499
|
+
if (!this.#isAtReservedTemplateExpressionContainer()) {
|
|
500
|
+
return this.jsx_parseExpressionContainer();
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const node = /** @type {ESTreeJSX.JSXExpressionContainer} */ (this.startNode());
|
|
504
|
+
this.next();
|
|
505
|
+
this.next();
|
|
506
|
+
const expression = /** @type {AST.Expression | ESTreeJSX.JSXEmptyExpression} */ (
|
|
507
|
+
/** @type {unknown} */ (this.parseElement())
|
|
508
|
+
);
|
|
509
|
+
node.expression = expression;
|
|
510
|
+
this.#popTokenContextsAfterTemplateExpressionElement(
|
|
511
|
+
/** @type {AST.Tsx | AST.Tsrx | AST.TsxCompat} */ (/** @type {unknown} */ (expression)),
|
|
512
|
+
);
|
|
513
|
+
this.expect(tt.braceR);
|
|
514
|
+
return this.finishNode(node, 'JSXExpressionContainer');
|
|
515
|
+
} finally {
|
|
516
|
+
this.#tsxIslandExpressionDepth--;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
#isAtReservedTemplateExpressionContainer() {
|
|
521
|
+
if (this.type !== tt.braceL) {
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
let index = this.start + 1;
|
|
526
|
+
while (index < this.input.length) {
|
|
527
|
+
const ch = this.input.charCodeAt(index);
|
|
528
|
+
if (
|
|
529
|
+
ch === CharCode.space ||
|
|
530
|
+
ch === CharCode.tab ||
|
|
531
|
+
ch === CharCode.lineFeed ||
|
|
532
|
+
ch === CharCode.carriageReturn
|
|
533
|
+
) {
|
|
534
|
+
index++;
|
|
535
|
+
} else {
|
|
536
|
+
break;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (this.input.charCodeAt(index) !== CharCode.lessThan) {
|
|
541
|
+
return false;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
return this.#isReservedTemplateTagNameStart(index + 1);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* @param {number} index
|
|
549
|
+
*/
|
|
550
|
+
#isReservedTemplateTagNameStart(index) {
|
|
551
|
+
const char_after_tsx = this.input.charCodeAt(index + 3);
|
|
552
|
+
const char_after_tsrx = this.input.charCodeAt(index + 4);
|
|
553
|
+
return (
|
|
554
|
+
(this.input.startsWith('tsx', index) &&
|
|
555
|
+
(index + 3 >= this.input.length ||
|
|
556
|
+
char_after_tsx === CharCode.greaterThan ||
|
|
557
|
+
char_after_tsx === CharCode.slash ||
|
|
558
|
+
char_after_tsx === CharCode.space ||
|
|
559
|
+
char_after_tsx === CharCode.tab ||
|
|
560
|
+
char_after_tsx === CharCode.lineFeed ||
|
|
561
|
+
char_after_tsx === CharCode.carriageReturn ||
|
|
562
|
+
char_after_tsx === CharCode.colon)) ||
|
|
563
|
+
(this.input.startsWith('tsrx', index) &&
|
|
564
|
+
(index + 4 >= this.input.length ||
|
|
565
|
+
char_after_tsrx === CharCode.greaterThan ||
|
|
566
|
+
char_after_tsrx === CharCode.slash ||
|
|
567
|
+
char_after_tsrx === CharCode.space ||
|
|
568
|
+
char_after_tsrx === CharCode.tab ||
|
|
569
|
+
char_after_tsrx === CharCode.lineFeed ||
|
|
570
|
+
char_after_tsrx === CharCode.carriageReturn))
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
|
|
495
574
|
/**
|
|
496
575
|
* @param {AST.Tsx | AST.TsxCompat} island
|
|
497
576
|
*/
|
|
@@ -2202,10 +2281,11 @@ export function TSRXPlugin(config) {
|
|
|
2202
2281
|
|
|
2203
2282
|
/** @type {Parse.Parser['jsx_readToken']} */
|
|
2204
2283
|
jsx_readToken() {
|
|
2205
|
-
const
|
|
2206
|
-
(n) =>
|
|
2284
|
+
const current_template_node = this.#path.findLast(
|
|
2285
|
+
(n) =>
|
|
2286
|
+
n.type === 'Element' || n.type === 'Tsx' || n.type === 'Tsrx' || n.type === 'TsxCompat',
|
|
2207
2287
|
);
|
|
2208
|
-
if (
|
|
2288
|
+
if (current_template_node?.type === 'TsxCompat' || current_template_node?.type === 'Tsx') {
|
|
2209
2289
|
return super.jsx_readToken();
|
|
2210
2290
|
}
|
|
2211
2291
|
let out = '',
|
|
@@ -2385,12 +2465,6 @@ export function TSRXPlugin(config) {
|
|
|
2385
2465
|
* @type {Parse.Parser['jsx_parseElement']}
|
|
2386
2466
|
*/
|
|
2387
2467
|
jsx_parseElement() {
|
|
2388
|
-
const inside_tsx = this.#path.findLast((n) => n.type === 'TsxCompat' || n.type === 'Tsx');
|
|
2389
|
-
if (inside_tsx) {
|
|
2390
|
-
// Inside tsx/tsx:*, let acorn-jsx handle it normally
|
|
2391
|
-
return super.jsx_parseElement();
|
|
2392
|
-
}
|
|
2393
|
-
|
|
2394
2468
|
// Check if the element being parsed IS a <tsx>, <tsrx>, or <tsx:*> tag
|
|
2395
2469
|
// Current token is jsxTagStart, this.end is position after '<'
|
|
2396
2470
|
const tag_name_start = this.end;
|
|
@@ -2417,6 +2491,20 @@ export function TSRXPlugin(config) {
|
|
|
2417
2491
|
char_after_tsrx === CharCode.lineFeed ||
|
|
2418
2492
|
char_after_tsrx === CharCode.carriageReturn);
|
|
2419
2493
|
|
|
2494
|
+
const current_template_node = this.#path.findLast(
|
|
2495
|
+
(n) =>
|
|
2496
|
+
n.type === 'Element' || n.type === 'Tsx' || n.type === 'Tsrx' || n.type === 'TsxCompat',
|
|
2497
|
+
);
|
|
2498
|
+
if (
|
|
2499
|
+
(current_template_node?.type === 'TsxCompat' || current_template_node?.type === 'Tsx') &&
|
|
2500
|
+
!is_tsrx_tag
|
|
2501
|
+
) {
|
|
2502
|
+
// Inside tsx/tsx:*, let acorn-jsx handle regular TSX tags normally.
|
|
2503
|
+
// Nested <tsrx> still needs Ripple's native template parser so it
|
|
2504
|
+
// can lower through the same path as <tsrx> in component bodies.
|
|
2505
|
+
return super.jsx_parseElement();
|
|
2506
|
+
}
|
|
2507
|
+
|
|
2420
2508
|
if (is_fragment_tag || is_tsx_tag || is_tsrx_tag) {
|
|
2421
2509
|
// Use Ripple's parseElement to create a Tsx/Tsrx/TsxCompat node.
|
|
2422
2510
|
// Bare fragments (<></>) are shorthand for <tsx>...</tsx>.
|
|
@@ -2424,9 +2512,21 @@ export function TSRXPlugin(config) {
|
|
|
2424
2512
|
const parsed = /** @type {import('estree-jsx').JSXElement} */ (
|
|
2425
2513
|
/** @type {unknown} */ (this.parseElement())
|
|
2426
2514
|
);
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2515
|
+
if (
|
|
2516
|
+
current_template_node?.type !== 'Tsx' &&
|
|
2517
|
+
current_template_node?.type !== 'TsxCompat'
|
|
2518
|
+
) {
|
|
2519
|
+
this.#popTokenContextsAfterTemplateExpressionElement(
|
|
2520
|
+
/** @type {AST.Tsx | AST.Tsrx | AST.TsxCompat} */ (/** @type {unknown} */ (parsed)),
|
|
2521
|
+
);
|
|
2522
|
+
} else if (this.type === tt.braceR && this.curContext() === tstc.tc_expr) {
|
|
2523
|
+
if (this.#tsxIslandExpressionDepth === 0) {
|
|
2524
|
+
// Acorn still owns the surrounding JSX expression container.
|
|
2525
|
+
// Keep a block-expression context for its closing `}` so the
|
|
2526
|
+
// parent TSX tag continues tokenizing as JSX afterward.
|
|
2527
|
+
this.context.push(b_expr);
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2430
2530
|
return parsed;
|
|
2431
2531
|
}
|
|
2432
2532
|
|
|
@@ -2887,9 +2987,24 @@ export function TSRXPlugin(config) {
|
|
|
2887
2987
|
parseTemplateBody(body) {
|
|
2888
2988
|
const inside_func =
|
|
2889
2989
|
this.context.some((n) => n.token === 'function') || this.scopeStack.length > 1;
|
|
2890
|
-
const
|
|
2891
|
-
(n) =>
|
|
2990
|
+
const current_template_node = this.#path.findLast(
|
|
2991
|
+
(n) =>
|
|
2992
|
+
n.type === 'Element' || n.type === 'Tsx' || n.type === 'Tsrx' || n.type === 'TsxCompat',
|
|
2892
2993
|
);
|
|
2994
|
+
const inside_tsx_island =
|
|
2995
|
+
current_template_node?.type === 'Tsx' || current_template_node?.type === 'TsxCompat'
|
|
2996
|
+
? current_template_node
|
|
2997
|
+
: null;
|
|
2998
|
+
|
|
2999
|
+
if (current_template_node?.type === 'Tsrx' && this.type === tstt.jsxText) {
|
|
3000
|
+
while (this.curContext() === tstc.tc_expr) {
|
|
3001
|
+
this.context.pop();
|
|
3002
|
+
}
|
|
3003
|
+
this.pos = this.start;
|
|
3004
|
+
this.next();
|
|
3005
|
+
this.parseTemplateBody(body);
|
|
3006
|
+
return;
|
|
3007
|
+
}
|
|
2893
3008
|
|
|
2894
3009
|
if (!inside_func) {
|
|
2895
3010
|
if (this.type.label === 'continue') {
|
package/src/scope.js
CHANGED
|
@@ -205,11 +205,18 @@ export function create_scopes(ast, root, parent, error_options) {
|
|
|
205
205
|
for (const declarator of node.declarations) {
|
|
206
206
|
/** @type {Binding[]} */
|
|
207
207
|
const bindings = [];
|
|
208
|
+
const initial = /** @type {AST.Expression | AST.Tsx | AST.Tsrx | null} */ (declarator.init);
|
|
208
209
|
|
|
209
210
|
state.scope.declarators.set(declarator, bindings);
|
|
210
211
|
|
|
211
212
|
for (const id of extract_identifiers(declarator.id)) {
|
|
212
|
-
const binding = state.scope.declare(id, 'normal', node.kind,
|
|
213
|
+
const binding = state.scope.declare(id, 'normal', node.kind, initial);
|
|
214
|
+
if (initial?.type === 'Tsx' || initial?.type === 'Tsrx') {
|
|
215
|
+
binding.metadata = {
|
|
216
|
+
...(binding.metadata ?? {}),
|
|
217
|
+
is_template_value: true,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
213
220
|
bindings.push(binding);
|
|
214
221
|
}
|
|
215
222
|
}
|
package/src/source-map-utils.js
CHANGED
|
@@ -294,6 +294,25 @@ export function build_line_offsets(text) {
|
|
|
294
294
|
return offsets;
|
|
295
295
|
}
|
|
296
296
|
|
|
297
|
+
/**
|
|
298
|
+
* ONLY USE THIS FOR TESTS
|
|
299
|
+
*
|
|
300
|
+
* @param {CodeMapping[]} mappings
|
|
301
|
+
* @param {number} source_offset
|
|
302
|
+
* @param {number} generated_offset
|
|
303
|
+
* @param {number} length
|
|
304
|
+
* @returns {CodeMapping | undefined}
|
|
305
|
+
*/
|
|
306
|
+
export function find_exact_mapping(mappings, source_offset, generated_offset, length) {
|
|
307
|
+
return mappings.find(
|
|
308
|
+
(mapping) =>
|
|
309
|
+
mapping.sourceOffsets[0] === source_offset &&
|
|
310
|
+
mapping.generatedOffsets[0] === generated_offset &&
|
|
311
|
+
mapping.lengths[0] === length &&
|
|
312
|
+
mapping.generatedLengths[0] === length,
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
|
|
297
316
|
/**
|
|
298
317
|
* DO NOT EXPORT THIS FUNCTION!
|
|
299
318
|
* THE FIX NEEDS TO HAPPEN IN THE TRANSFORMER, SEGMENTS OR PARSER
|
|
@@ -221,6 +221,8 @@ export function tsx_with_ts_locations() {
|
|
|
221
221
|
// JS nodes whose esrap printer emits no location marker, causing
|
|
222
222
|
// segments.js get_mapping_from_node() to throw when it asks for the
|
|
223
223
|
// generated position of the node's start (or end).
|
|
224
|
+
'ClassDeclaration',
|
|
225
|
+
'ClassExpression',
|
|
224
226
|
'IfStatement',
|
|
225
227
|
'NewExpression',
|
|
226
228
|
'MemberExpression',
|
|
@@ -1215,23 +1215,27 @@ function is_hook_callee(callee) {
|
|
|
1215
1215
|
|
|
1216
1216
|
/**
|
|
1217
1217
|
* @param {AST.Identifier[]} bindings
|
|
1218
|
+
* @param {Set<string>} [mapped_bindings]
|
|
1218
1219
|
* @returns {AST.ObjectPattern}
|
|
1219
1220
|
*/
|
|
1220
|
-
function create_helper_props_pattern(bindings) {
|
|
1221
|
+
function create_helper_props_pattern(bindings, mapped_bindings = new Set()) {
|
|
1221
1222
|
return /** @type {any} */ ({
|
|
1222
1223
|
type: 'ObjectPattern',
|
|
1223
|
-
properties: bindings.map((binding) =>
|
|
1224
|
+
properties: bindings.map((binding) =>
|
|
1225
|
+
create_helper_props_property(binding, mapped_bindings.has(binding.name)),
|
|
1226
|
+
),
|
|
1224
1227
|
metadata: { path: [] },
|
|
1225
1228
|
});
|
|
1226
1229
|
}
|
|
1227
1230
|
|
|
1228
1231
|
/**
|
|
1229
1232
|
* @param {AST.Identifier} binding
|
|
1233
|
+
* @param {boolean} [map_binding]
|
|
1230
1234
|
* @returns {AST.Property}
|
|
1231
1235
|
*/
|
|
1232
|
-
function create_helper_props_property(binding) {
|
|
1233
|
-
const key = create_generated_identifier(binding.name);
|
|
1234
|
-
const value = create_generated_identifier(binding.name);
|
|
1236
|
+
function create_helper_props_property(binding, map_binding = false) {
|
|
1237
|
+
const key = map_binding ? clone_identifier(binding) : create_generated_identifier(binding.name);
|
|
1238
|
+
const value = map_binding ? clone_identifier(binding) : create_generated_identifier(binding.name);
|
|
1235
1239
|
|
|
1236
1240
|
return b.prop('init', key, value, false, true);
|
|
1237
1241
|
}
|
|
@@ -3411,14 +3415,16 @@ function get_hook_callee_name(callee) {
|
|
|
3411
3415
|
* Used by the switch lift's chained-call build, which allocates ids in
|
|
3412
3416
|
* source order in a forward pass and then constructs helpers in reverse so
|
|
3413
3417
|
* each fall-through case can reference the next case's component element.
|
|
3418
|
+
* @param {{ transientBindings?: Set<string> }} [options]
|
|
3414
3419
|
* @returns {{ setup_statements: any[], component_element: ESTreeJSX.JSXElement }}
|
|
3415
3420
|
*/
|
|
3416
|
-
function create_hook_safe_helper(
|
|
3421
|
+
export function create_hook_safe_helper(
|
|
3417
3422
|
body_nodes,
|
|
3418
3423
|
key_expression,
|
|
3419
3424
|
source_node,
|
|
3420
3425
|
transform_context,
|
|
3421
3426
|
preallocated_helper_id,
|
|
3427
|
+
options = {},
|
|
3422
3428
|
) {
|
|
3423
3429
|
validate_hook_safe_body_does_not_assign_hook_results_to_outer_bindings(
|
|
3424
3430
|
body_nodes,
|
|
@@ -3436,9 +3442,14 @@ function create_hook_safe_helper(
|
|
|
3436
3442
|
body_nodes,
|
|
3437
3443
|
transform_context.available_bindings,
|
|
3438
3444
|
);
|
|
3445
|
+
const transient_bindings = options.transientBindings ?? new Set();
|
|
3439
3446
|
const aliases = use_module_scoped_component
|
|
3440
3447
|
? []
|
|
3441
|
-
: helper_bindings.map((binding) =>
|
|
3448
|
+
: helper_bindings.map((binding) =>
|
|
3449
|
+
transient_bindings.has(binding.name)
|
|
3450
|
+
? null
|
|
3451
|
+
: create_helper_type_alias_declaration(helper_id, binding),
|
|
3452
|
+
);
|
|
3442
3453
|
const props_type =
|
|
3443
3454
|
helper_bindings.length > 0 && !use_module_scoped_component
|
|
3444
3455
|
? create_helper_props_type_literal(helper_bindings, aliases)
|
|
@@ -3447,8 +3458,8 @@ function create_hook_safe_helper(
|
|
|
3447
3458
|
helper_bindings.length > 0
|
|
3448
3459
|
? [
|
|
3449
3460
|
props_type !== null
|
|
3450
|
-
? create_typed_helper_props_pattern(helper_bindings, props_type)
|
|
3451
|
-
: create_helper_props_pattern(helper_bindings),
|
|
3461
|
+
? create_typed_helper_props_pattern(helper_bindings, props_type, transient_bindings)
|
|
3462
|
+
: create_helper_props_pattern(helper_bindings, transient_bindings),
|
|
3452
3463
|
]
|
|
3453
3464
|
: [];
|
|
3454
3465
|
|
|
@@ -3490,7 +3501,7 @@ function create_hook_safe_helper(
|
|
|
3490
3501
|
if (!transform_context.helper_state) {
|
|
3491
3502
|
return {
|
|
3492
3503
|
setup_statements: [
|
|
3493
|
-
...aliases.
|
|
3504
|
+
...aliases.flatMap((alias) => (alias ? [alias.declaration] : [])),
|
|
3494
3505
|
create_helper_declaration(helper_id, helper_fn, source_node, transform_context),
|
|
3495
3506
|
],
|
|
3496
3507
|
component_element,
|
|
@@ -3514,7 +3525,7 @@ function create_hook_safe_helper(
|
|
|
3514
3525
|
|
|
3515
3526
|
return {
|
|
3516
3527
|
setup_statements: [
|
|
3517
|
-
...aliases.
|
|
3528
|
+
...aliases.flatMap((alias) => (alias ? [alias.declaration] : [])),
|
|
3518
3529
|
create_cached_helper_declaration(
|
|
3519
3530
|
helper_id,
|
|
3520
3531
|
cache_id,
|
|
@@ -3588,7 +3599,7 @@ function create_helper_type_alias_declaration(helper_id, binding) {
|
|
|
3588
3599
|
|
|
3589
3600
|
/**
|
|
3590
3601
|
* @param {AST.Identifier[]} bindings
|
|
3591
|
-
* @param {{ id: AST.Identifier }[]} aliases
|
|
3602
|
+
* @param {({ id: AST.Identifier } | null)[]} aliases
|
|
3592
3603
|
* @returns {any}
|
|
3593
3604
|
*/
|
|
3594
3605
|
function create_helper_props_type_literal(bindings, aliases) {
|
|
@@ -3596,7 +3607,13 @@ function create_helper_props_type_literal(bindings, aliases) {
|
|
|
3596
3607
|
bindings.map((binding, i) =>
|
|
3597
3608
|
b.ts_property_signature(
|
|
3598
3609
|
create_generated_identifier(binding.name),
|
|
3599
|
-
b.ts_type_annotation(
|
|
3610
|
+
b.ts_type_annotation(
|
|
3611
|
+
aliases[i]
|
|
3612
|
+
? b.ts_type_query(
|
|
3613
|
+
clone_identifier(/** @type {{ id: AST.Identifier }} */ (aliases[i]).id),
|
|
3614
|
+
)
|
|
3615
|
+
: b.ts_keyword_type('any'),
|
|
3616
|
+
),
|
|
3600
3617
|
),
|
|
3601
3618
|
),
|
|
3602
3619
|
);
|
|
@@ -3605,10 +3622,11 @@ function create_helper_props_type_literal(bindings, aliases) {
|
|
|
3605
3622
|
/**
|
|
3606
3623
|
* @param {AST.Identifier[]} bindings
|
|
3607
3624
|
* @param {any} props_type
|
|
3625
|
+
* @param {Set<string>} [mapped_bindings]
|
|
3608
3626
|
* @returns {AST.ObjectPattern}
|
|
3609
3627
|
*/
|
|
3610
|
-
function create_typed_helper_props_pattern(bindings, props_type) {
|
|
3611
|
-
const pattern = create_helper_props_pattern(bindings);
|
|
3628
|
+
function create_typed_helper_props_pattern(bindings, props_type, mapped_bindings = new Set()) {
|
|
3629
|
+
const pattern = create_helper_props_pattern(bindings, mapped_bindings);
|
|
3612
3630
|
/** @type {any} */ (pattern).typeAnnotation = b.ts_type_annotation(props_type);
|
|
3613
3631
|
return pattern;
|
|
3614
3632
|
}
|
|
@@ -4502,18 +4520,25 @@ function try_statement_to_jsx_child(node, transform_context) {
|
|
|
4502
4520
|
? to_jsx_expression_container(create_null_literal())
|
|
4503
4521
|
: statement_body_to_jsx_child(pending_body_nodes, transform_context);
|
|
4504
4522
|
|
|
4505
|
-
result =
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4523
|
+
result =
|
|
4524
|
+
transform_context.platform.hooks?.createPendingBoundary?.(
|
|
4525
|
+
result,
|
|
4526
|
+
fallback_content,
|
|
4527
|
+
transform_context,
|
|
4528
|
+
node,
|
|
4529
|
+
) ??
|
|
4530
|
+
create_jsx_element(
|
|
4531
|
+
'Suspense',
|
|
4532
|
+
[
|
|
4533
|
+
{
|
|
4534
|
+
type: 'JSXAttribute',
|
|
4535
|
+
name: { type: 'JSXIdentifier', name: 'fallback', metadata: { path: [] } },
|
|
4536
|
+
value: fallback_content,
|
|
4537
|
+
metadata: { path: [] },
|
|
4538
|
+
},
|
|
4539
|
+
],
|
|
4540
|
+
[result],
|
|
4541
|
+
);
|
|
4517
4542
|
}
|
|
4518
4543
|
|
|
4519
4544
|
// Wrap in <TsrxErrorBoundary> if catch block exists
|
|
@@ -4551,19 +4576,42 @@ function try_statement_to_jsx_child(node, transform_context) {
|
|
|
4551
4576
|
|
|
4552
4577
|
const fallback_fn = b.arrow(
|
|
4553
4578
|
catch_params,
|
|
4554
|
-
b.block(build_render_statements(catch_body_nodes, true, transform_context)),
|
|
4579
|
+
b.block(build_render_statements(catch_body_nodes, true, transform_context), handler.body),
|
|
4580
|
+
false,
|
|
4581
|
+
undefined,
|
|
4582
|
+
handler,
|
|
4555
4583
|
);
|
|
4556
4584
|
|
|
4585
|
+
const fallback_component =
|
|
4586
|
+
transform_context.platform.hooks?.createErrorFallbackComponent?.(
|
|
4587
|
+
catch_body_nodes,
|
|
4588
|
+
catch_params,
|
|
4589
|
+
transform_context,
|
|
4590
|
+
node,
|
|
4591
|
+
) ?? null;
|
|
4592
|
+
|
|
4557
4593
|
transform_context.available_bindings = saved_catch_bindings;
|
|
4558
4594
|
|
|
4559
4595
|
const boundary_content =
|
|
4560
4596
|
transform_context.platform.hooks?.createErrorBoundaryContent?.(
|
|
4597
|
+
result,
|
|
4598
|
+
transform_context,
|
|
4599
|
+
node,
|
|
4600
|
+
) ?? null;
|
|
4601
|
+
|
|
4602
|
+
const custom_boundary =
|
|
4603
|
+
transform_context.platform.hooks?.createErrorBoundary?.(
|
|
4604
|
+
result,
|
|
4561
4605
|
try_content,
|
|
4606
|
+
fallback_fn,
|
|
4562
4607
|
transform_context,
|
|
4563
4608
|
node,
|
|
4609
|
+
{ fallbackComponent: fallback_component },
|
|
4564
4610
|
) ?? null;
|
|
4565
4611
|
|
|
4566
|
-
if (
|
|
4612
|
+
if (custom_boundary) {
|
|
4613
|
+
result = custom_boundary;
|
|
4614
|
+
} else if (boundary_content && transform_context.inside_element_child) {
|
|
4567
4615
|
result = to_jsx_expression_container(
|
|
4568
4616
|
b.call(
|
|
4569
4617
|
'TsrxErrorBoundary',
|
|
@@ -4572,21 +4620,21 @@ function try_statement_to_jsx_child(node, transform_context) {
|
|
|
4572
4620
|
);
|
|
4573
4621
|
|
|
4574
4622
|
return result;
|
|
4623
|
+
} else {
|
|
4624
|
+
result = create_jsx_element(
|
|
4625
|
+
'TsrxErrorBoundary',
|
|
4626
|
+
[
|
|
4627
|
+
b.jsx_attribute(
|
|
4628
|
+
b.jsx_id('fallback'),
|
|
4629
|
+
to_jsx_expression_container(/** @type {any} */ (fallback_fn)),
|
|
4630
|
+
),
|
|
4631
|
+
...(boundary_content
|
|
4632
|
+
? [b.jsx_attribute(b.jsx_id('content'), to_jsx_expression_container(boundary_content))]
|
|
4633
|
+
: []),
|
|
4634
|
+
],
|
|
4635
|
+
boundary_content ? [] : [result],
|
|
4636
|
+
);
|
|
4575
4637
|
}
|
|
4576
|
-
|
|
4577
|
-
result = create_jsx_element(
|
|
4578
|
-
'TsrxErrorBoundary',
|
|
4579
|
-
[
|
|
4580
|
-
b.jsx_attribute(
|
|
4581
|
-
b.jsx_id('fallback'),
|
|
4582
|
-
to_jsx_expression_container(/** @type {any} */ (fallback_fn)),
|
|
4583
|
-
),
|
|
4584
|
-
...(boundary_content
|
|
4585
|
-
? [b.jsx_attribute(b.jsx_id('content'), to_jsx_expression_container(boundary_content))]
|
|
4586
|
-
: []),
|
|
4587
|
-
],
|
|
4588
|
-
boundary_content ? [] : [result],
|
|
4589
|
-
);
|
|
4590
4638
|
}
|
|
4591
4639
|
|
|
4592
4640
|
// result is a JSXElement, but we need to return a JSXExpressionContainer
|
|
@@ -345,7 +345,7 @@ function extract_classes(node, src_to_gen_map, gen_line_offsets, src_line_offset
|
|
|
345
345
|
* @param {PostProcessingChanges } post_processing_changes - Optional post-processing changes
|
|
346
346
|
* @param {number[]} line_offsets - Pre-computed line offsets array for generated code
|
|
347
347
|
* @param {CompileError[]} [errors]
|
|
348
|
-
* @returns {Omit<VolarMappingsResult, 'errors'>}
|
|
348
|
+
* @returns {Omit<VolarMappingsResult, 'errors' | 'sourceAst'>}
|
|
349
349
|
*/
|
|
350
350
|
export function convert_source_map_to_mappings(
|
|
351
351
|
ast,
|
|
@@ -1528,6 +1528,18 @@ export function convert_source_map_to_mappings(
|
|
|
1528
1528
|
}
|
|
1529
1529
|
return;
|
|
1530
1530
|
} else if (node.type === 'ClassDeclaration' || node.type === 'ClassExpression') {
|
|
1531
|
+
if (node.loc) {
|
|
1532
|
+
tokens.push({
|
|
1533
|
+
source: 'class',
|
|
1534
|
+
generated: 'class',
|
|
1535
|
+
loc: {
|
|
1536
|
+
start: { line: node.loc.start.line, column: node.loc.start.column },
|
|
1537
|
+
end: { line: node.loc.start.line, column: node.loc.start.column + 'class'.length },
|
|
1538
|
+
},
|
|
1539
|
+
metadata: {},
|
|
1540
|
+
});
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1531
1543
|
// Visit in source order: id, superClass, body
|
|
1532
1544
|
if (node.id) {
|
|
1533
1545
|
visit(node.id);
|
|
@@ -2402,6 +2414,7 @@ export function create_volar_mappings_result({
|
|
|
2402
2414
|
|
|
2403
2415
|
return {
|
|
2404
2416
|
...result,
|
|
2417
|
+
sourceAst: ast_from_source,
|
|
2405
2418
|
errors,
|
|
2406
2419
|
};
|
|
2407
2420
|
}
|
package/types/index.d.ts
CHANGED
|
@@ -1247,7 +1247,9 @@ export interface Binding {
|
|
|
1247
1247
|
| AST.FunctionDeclaration
|
|
1248
1248
|
| AST.ClassDeclaration
|
|
1249
1249
|
| AST.ImportDeclaration
|
|
1250
|
-
| AST.TSModuleDeclaration
|
|
1250
|
+
| AST.TSModuleDeclaration
|
|
1251
|
+
| AST.Tsx
|
|
1252
|
+
| AST.Tsrx;
|
|
1251
1253
|
/** Whether this binding has been reassigned */
|
|
1252
1254
|
reassigned: boolean;
|
|
1253
1255
|
/** Whether this binding has been mutated (property access) */
|
|
@@ -1261,6 +1263,7 @@ export interface Binding {
|
|
|
1261
1263
|
is_dynamic_component?: boolean;
|
|
1262
1264
|
pattern?: AST.Identifier;
|
|
1263
1265
|
is_ripple_object?: boolean;
|
|
1266
|
+
is_template_value?: boolean;
|
|
1264
1267
|
} | null;
|
|
1265
1268
|
/** Kind of binding */
|
|
1266
1269
|
kind: BindingKind;
|
|
@@ -1340,7 +1343,9 @@ export interface ScopeInterface {
|
|
|
1340
1343
|
| AST.FunctionDeclaration
|
|
1341
1344
|
| AST.ClassDeclaration
|
|
1342
1345
|
| AST.ImportDeclaration
|
|
1343
|
-
| AST.TSModuleDeclaration
|
|
1346
|
+
| AST.TSModuleDeclaration
|
|
1347
|
+
| AST.Tsx
|
|
1348
|
+
| AST.Tsrx,
|
|
1344
1349
|
): Binding;
|
|
1345
1350
|
/** Get binding by name */
|
|
1346
1351
|
get(name: string): Binding | null;
|
|
@@ -1416,6 +1421,7 @@ export interface TransformServerState extends BaseState {
|
|
|
1416
1421
|
template_child?: boolean;
|
|
1417
1422
|
skip_regular_blocks?: boolean;
|
|
1418
1423
|
in_regular_block?: boolean;
|
|
1424
|
+
jsx_to_tsrx_element?: boolean;
|
|
1419
1425
|
}
|
|
1420
1426
|
|
|
1421
1427
|
export type UpdateList = Array<
|
|
@@ -1450,6 +1456,7 @@ export interface TransformClientState extends BaseState {
|
|
|
1450
1456
|
skip_children_traversal: boolean;
|
|
1451
1457
|
return_flags?: Map<AST.ReturnStatement, { name: string; tracked: boolean }>;
|
|
1452
1458
|
is_tsrx_element?: boolean;
|
|
1459
|
+
jsx_to_tsrx_element?: boolean;
|
|
1453
1460
|
}
|
|
1454
1461
|
|
|
1455
1462
|
/** Override zimmerframe types and provide our own */
|
|
@@ -1585,6 +1592,7 @@ export interface VolarMappingsResult {
|
|
|
1585
1592
|
mappings: CodeMapping[];
|
|
1586
1593
|
cssMappings: CodeMapping[];
|
|
1587
1594
|
errors: CompileError[];
|
|
1595
|
+
sourceAst: AST.Program;
|
|
1588
1596
|
}
|
|
1589
1597
|
|
|
1590
1598
|
/**
|
package/types/jsx-platform.d.ts
CHANGED
|
@@ -200,11 +200,49 @@ export interface JsxPlatformHooks {
|
|
|
200
200
|
* the loop to the downstream Vapor JSX compiler as a typed `VaporFor` component.
|
|
201
201
|
*/
|
|
202
202
|
renderForOf?: (node: any, loopParams: any[], bodyStatements: any[], ctx: any) => any | null;
|
|
203
|
+
/**
|
|
204
|
+
* Optionally replace the default React-style pending lowering for
|
|
205
|
+
* `try { ... } pending { ... }`. The default emits
|
|
206
|
+
* `<Suspense fallback={fallbackContent}>tryContent</Suspense>`.
|
|
207
|
+
* Vue Vapor uses this to provide `default` and `fallback` slots via
|
|
208
|
+
* `v-slots`.
|
|
209
|
+
*/
|
|
210
|
+
createPendingBoundary?: (
|
|
211
|
+
tryContent: any,
|
|
212
|
+
fallbackContent: any,
|
|
213
|
+
ctx: any,
|
|
214
|
+
node: any,
|
|
215
|
+
) => any | null;
|
|
216
|
+
/**
|
|
217
|
+
* Optionally create a generated component for a catch fallback body while
|
|
218
|
+
* the catch parameters are still in scope. Platforms can use this to reuse
|
|
219
|
+
* one mapped catch-body component from multiple runtime catch sites.
|
|
220
|
+
*/
|
|
221
|
+
createErrorFallbackComponent?: (
|
|
222
|
+
catchBodyNodes: any[],
|
|
223
|
+
catchParams: any[],
|
|
224
|
+
ctx: any,
|
|
225
|
+
node: any,
|
|
226
|
+
) => any | null;
|
|
227
|
+
/**
|
|
228
|
+
* Optionally replace the default `try/catch` boundary wrapper. The hook
|
|
229
|
+
* receives the current render content, the original try-body content before
|
|
230
|
+
* any pending wrapper, and the generated catch fallback function.
|
|
231
|
+
*/
|
|
232
|
+
createErrorBoundary?: (
|
|
233
|
+
tryContent: any,
|
|
234
|
+
rawTryContent: any,
|
|
235
|
+
fallbackFn: any,
|
|
236
|
+
ctx: any,
|
|
237
|
+
node: any,
|
|
238
|
+
info?: { fallbackComponent?: any },
|
|
239
|
+
) => any | null;
|
|
203
240
|
/**
|
|
204
241
|
* Optionally move the primary `try { ... }` render content into an explicit
|
|
205
242
|
* error-boundary prop instead of rendering it as the boundary's JSX children.
|
|
206
243
|
* Vue Vapor uses this because boundary content must execute lazily from a
|
|
207
|
-
* zero-argument function.
|
|
244
|
+
* zero-argument function. If a `pending` block exists, `tryContent` is the
|
|
245
|
+
* already-created pending boundary so catch wrappers still enclose it.
|
|
208
246
|
*/
|
|
209
247
|
createErrorBoundaryContent?: (tryContent: any, ctx: any, node: any) => any | null;
|
|
210
248
|
/**
|