@tsrx/solid 0.1.3 → 0.1.6
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/index.js +1 -0
- package/src/transform.js +212 -307
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.1.
|
|
6
|
+
"version": "0.1.6",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"publishConfig": {
|
|
9
9
|
"access": "public"
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"esrap": "^2.
|
|
27
|
+
"esrap": "^2.2.7",
|
|
28
28
|
"zimmerframe": "^1.1.2",
|
|
29
|
-
"@tsrx/core": "0.1.
|
|
29
|
+
"@tsrx/core": "0.1.6"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"solid-js": ">=1.8 || >=2.0.0-beta"
|
package/src/index.js
CHANGED
package/src/transform.js
CHANGED
|
@@ -27,10 +27,14 @@ import {
|
|
|
27
27
|
clone_expression_node,
|
|
28
28
|
clone_identifier,
|
|
29
29
|
clone_jsx_name,
|
|
30
|
+
cloneSwitchHelperInvocation as clone_switch_helper_invocation,
|
|
31
|
+
collectParamBindings as collect_param_bindings,
|
|
32
|
+
collectStatementBindings as collect_statement_bindings,
|
|
33
|
+
contains_component_jsx,
|
|
30
34
|
create_generated_identifier,
|
|
31
35
|
create_null_literal,
|
|
32
|
-
flatten_switch_consequent,
|
|
33
36
|
get_for_of_iteration_params,
|
|
37
|
+
planSwitchLift as plan_switch_lift,
|
|
34
38
|
identifier_to_jsx_name,
|
|
35
39
|
is_bare_render_expression,
|
|
36
40
|
is_dynamic_element_id,
|
|
@@ -104,6 +108,12 @@ const solid_platform = {
|
|
|
104
108
|
scanUseServerDirectiveForAwaitWithCustomValidator: false,
|
|
105
109
|
},
|
|
106
110
|
hooks: {
|
|
111
|
+
// Hoist to module scope in the client transform —
|
|
112
|
+
// same trade-off as React and Vue, where one definition per helper
|
|
113
|
+
// keeps bundles small and source mappings 1:1. The
|
|
114
|
+
// `compile_to_volar_mappings` entry point opts back out so Volar's
|
|
115
|
+
// type-only output keeps helpers inline against the component body.
|
|
116
|
+
moduleScopedHookComponents: true,
|
|
107
117
|
initialState: () => ({
|
|
108
118
|
needs_show: false,
|
|
109
119
|
needs_for: false,
|
|
@@ -113,6 +123,17 @@ const solid_platform = {
|
|
|
113
123
|
needs_loading: false,
|
|
114
124
|
needs_normalize_spread_props: false,
|
|
115
125
|
}),
|
|
126
|
+
canHoistStaticNode(node) {
|
|
127
|
+
// Solid's reactive runtime doesn't reuse JSX-element identity the
|
|
128
|
+
// way React does, so hoisting `<Component />` references to module
|
|
129
|
+
// level pays no runtime cost — it just creates an extra `const`
|
|
130
|
+
// that aliases a helper invocation (e.g. `App__static1 =
|
|
131
|
+
// <App__StatementBodyHook2 />`). Truly-static DOM trees like
|
|
132
|
+
// `<span>'Hello'</span>` still benefit from being hoisted out of
|
|
133
|
+
// the per-render closure, so we only veto hoisting when the
|
|
134
|
+
// subtree contains a *component* JSX element. Same logic Vue uses.
|
|
135
|
+
return !contains_component_jsx(node);
|
|
136
|
+
},
|
|
116
137
|
validateComponentAwait: (await_expression, _component, ctx, _requires, source) => {
|
|
117
138
|
const await_start = get_await_keyword_start(await_expression, source);
|
|
118
139
|
const adjusted_node = /** @type {any} */ ({
|
|
@@ -134,8 +155,8 @@ const solid_platform = {
|
|
|
134
155
|
switchStatement: switch_statement_to_jsx_child,
|
|
135
156
|
tryStatement: try_statement_to_jsx_child,
|
|
136
157
|
},
|
|
137
|
-
componentToFunction: (component, ctx) =>
|
|
138
|
-
component_to_function_declaration(component, /** @type {any} */ (ctx)),
|
|
158
|
+
componentToFunction: (component, ctx, helper_state) =>
|
|
159
|
+
component_to_function_declaration(component, /** @type {any} */ (ctx), helper_state),
|
|
139
160
|
injectImports: (program, ctx) => inject_solid_imports(program, /** @type {any} */ (ctx)),
|
|
140
161
|
// `transformElementAttributes` is intentionally omitted: the
|
|
141
162
|
// `transformElement` hook below short-circuits core's element walker
|
|
@@ -180,12 +201,42 @@ function get_await_keyword_start(await_node, source) {
|
|
|
180
201
|
/**
|
|
181
202
|
* @param {any} component
|
|
182
203
|
* @param {TransformContext} transform_context
|
|
204
|
+
* @param {any} [walk_helper_state]
|
|
205
|
+
* The helper_state owned by the walker for this component. Solid's local
|
|
206
|
+
* body lowering happens *after* the walker has restored `helper_state` to
|
|
207
|
+
* the outer scope's value, so we re-install the walker's helper_state here
|
|
208
|
+
* for the duration of the body lowering. That way `to_jsx_child` calls into
|
|
209
|
+
* `switch_statement_to_jsx_child` (and any other lift path that goes
|
|
210
|
+
* through `create_hook_safe_helper`) see the same module-scoped helper
|
|
211
|
+
* bucket the React / Vue paths see, and `moduleScopedHookComponents: true`
|
|
212
|
+
* actually hoists Solid's `<StatementBodyHook/>` helpers out of the
|
|
213
|
+
* component body.
|
|
183
214
|
* @returns {AST.FunctionDeclaration | AST.FunctionExpression | AST.ArrowFunctionExpression}
|
|
184
215
|
*/
|
|
185
|
-
function component_to_function_declaration(component, transform_context) {
|
|
216
|
+
function component_to_function_declaration(component, transform_context, walk_helper_state) {
|
|
186
217
|
const params = component.params || [];
|
|
187
218
|
const body = /** @type {any[]} */ (component.body || []);
|
|
188
219
|
|
|
220
|
+
const saved_helper_state = transform_context.helper_state;
|
|
221
|
+
if (walk_helper_state) {
|
|
222
|
+
transform_context.helper_state = walk_helper_state;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Re-install the component's body bindings before lowering the body. The
|
|
226
|
+
// walker sets `state.available_bindings` to the component-scope bindings
|
|
227
|
+
// during `next()` and restores them before calling the platform's
|
|
228
|
+
// `componentToFunction` hook — but Solid's body lowering happens *here*,
|
|
229
|
+
// after that restore, so `to_jsx_child` (and any `create_hook_safe_helper`
|
|
230
|
+
// it triggers via `switch_statement_to_jsx_child` / the if / try / for-of
|
|
231
|
+
// lifts) wouldn't otherwise see component-scope locals like `const obj =
|
|
232
|
+
// {...}` and would emit helpers that close over undefined references.
|
|
233
|
+
const saved_bindings = transform_context.available_bindings;
|
|
234
|
+
const body_bindings = collect_param_bindings(params);
|
|
235
|
+
for (const node of body) {
|
|
236
|
+
collect_statement_bindings(node, body_bindings);
|
|
237
|
+
}
|
|
238
|
+
transform_context.available_bindings = body_bindings;
|
|
239
|
+
|
|
189
240
|
// In type-only mode the lazy rewrite is skipped so destructuring patterns
|
|
190
241
|
// survive into the virtual TSX and TypeScript can flow real types.
|
|
191
242
|
const lazy_bindings = transform_context.typeOnly
|
|
@@ -314,27 +365,12 @@ function component_to_function_declaration(component, transform_context) {
|
|
|
314
365
|
}
|
|
315
366
|
|
|
316
367
|
if (render_nodes.length > 0) {
|
|
317
|
-
statements.push(
|
|
318
|
-
/** @type {any} */ ({
|
|
319
|
-
type: 'ReturnStatement',
|
|
320
|
-
argument: build_return_expression(render_nodes) || {
|
|
321
|
-
type: 'Literal',
|
|
322
|
-
value: null,
|
|
323
|
-
raw: 'null',
|
|
324
|
-
metadata: { path: [] },
|
|
325
|
-
},
|
|
326
|
-
metadata: { path: [] },
|
|
327
|
-
}),
|
|
328
|
-
);
|
|
368
|
+
statements.push(b.return(build_return_expression(render_nodes) || b.literal(null)));
|
|
329
369
|
}
|
|
330
370
|
|
|
331
371
|
const final_params = lazy_bindings.size > 0 ? replace_lazy_params(params) : params;
|
|
332
372
|
|
|
333
|
-
const body_block =
|
|
334
|
-
type: 'BlockStatement',
|
|
335
|
-
body: statements,
|
|
336
|
-
metadata: { path: [] },
|
|
337
|
-
});
|
|
373
|
+
const body_block = b.block(statements);
|
|
338
374
|
const final_body =
|
|
339
375
|
lazy_bindings.size > 0 ? apply_lazy_transforms(body_block, lazy_bindings) : body_block;
|
|
340
376
|
|
|
@@ -342,48 +378,19 @@ function component_to_function_declaration(component, transform_context) {
|
|
|
342
378
|
let fn;
|
|
343
379
|
|
|
344
380
|
if (component.id) {
|
|
345
|
-
fn =
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
generator: false,
|
|
353
|
-
metadata: {
|
|
354
|
-
path: [],
|
|
355
|
-
is_component: true,
|
|
356
|
-
},
|
|
357
|
-
});
|
|
381
|
+
fn = b.function_declaration(
|
|
382
|
+
component.id,
|
|
383
|
+
final_params,
|
|
384
|
+
final_body,
|
|
385
|
+
false,
|
|
386
|
+
component.typeParameters,
|
|
387
|
+
);
|
|
358
388
|
} else if (component.metadata?.arrow) {
|
|
359
|
-
fn =
|
|
360
|
-
type: 'ArrowFunctionExpression',
|
|
361
|
-
typeParameters: component.typeParameters,
|
|
362
|
-
params: final_params,
|
|
363
|
-
body: final_body,
|
|
364
|
-
async: false,
|
|
365
|
-
generator: false,
|
|
366
|
-
expression: false,
|
|
367
|
-
metadata: {
|
|
368
|
-
path: [],
|
|
369
|
-
is_component: true,
|
|
370
|
-
},
|
|
371
|
-
});
|
|
389
|
+
fn = b.arrow(final_params, final_body, false, component.typeParameters);
|
|
372
390
|
} else {
|
|
373
|
-
fn =
|
|
374
|
-
type: 'FunctionExpression',
|
|
375
|
-
id: null,
|
|
376
|
-
typeParameters: component.typeParameters,
|
|
377
|
-
params: final_params,
|
|
378
|
-
body: final_body,
|
|
379
|
-
async: false,
|
|
380
|
-
generator: false,
|
|
381
|
-
metadata: {
|
|
382
|
-
path: [],
|
|
383
|
-
is_component: true,
|
|
384
|
-
},
|
|
385
|
-
});
|
|
391
|
+
fn = b.function(null, final_params, final_body, false, component.typeParameters);
|
|
386
392
|
}
|
|
393
|
+
fn.metadata.is_component = true;
|
|
387
394
|
|
|
388
395
|
if (fn.type === 'FunctionDeclaration' && fn.id) {
|
|
389
396
|
fn.id.metadata = /** @type {AST.Identifier['metadata']} */ ({
|
|
@@ -393,6 +400,20 @@ function component_to_function_declaration(component, transform_context) {
|
|
|
393
400
|
}
|
|
394
401
|
|
|
395
402
|
setLocation(fn, /** @type {any} */ (component), true);
|
|
403
|
+
|
|
404
|
+
// Restore the outer helper_state and bindings, then surface this
|
|
405
|
+
// component's generated helpers / statics so the downstream Program-level
|
|
406
|
+
// lift can hoist them (`moduleScopedHookComponents: true` registers
|
|
407
|
+
// helpers into `helper_state.helpers`; the React/Vue post-pass reads them
|
|
408
|
+
// off `fn.metadata.generated_helpers`).
|
|
409
|
+
transform_context.helper_state = saved_helper_state;
|
|
410
|
+
transform_context.available_bindings = saved_bindings;
|
|
411
|
+
if (walk_helper_state) {
|
|
412
|
+
const fn_metadata = /** @type {any} */ (fn.metadata);
|
|
413
|
+
fn_metadata.generated_helpers = walk_helper_state.helpers;
|
|
414
|
+
fn_metadata.generated_statics = walk_helper_state.statics;
|
|
415
|
+
}
|
|
416
|
+
|
|
396
417
|
return fn;
|
|
397
418
|
}
|
|
398
419
|
|
|
@@ -528,14 +549,12 @@ function body_to_jsx_child(body_nodes, transform_context) {
|
|
|
528
549
|
let capture_index = 0;
|
|
529
550
|
for (const child of body_nodes) {
|
|
530
551
|
if (is_bare_return_statement(child)) {
|
|
531
|
-
statements.push(
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
loc: child.loc,
|
|
538
|
-
});
|
|
552
|
+
statements.push(
|
|
553
|
+
set_loc(
|
|
554
|
+
b.return(children.length > 0 ? build_return_expression(children) : create_null_literal()),
|
|
555
|
+
child,
|
|
556
|
+
),
|
|
557
|
+
);
|
|
539
558
|
children.length = 0;
|
|
540
559
|
has_terminal_return = true;
|
|
541
560
|
continue;
|
|
@@ -580,27 +599,13 @@ function body_to_jsx_child(body_nodes, transform_context) {
|
|
|
580
599
|
const block_body = [...statements];
|
|
581
600
|
if (children.length > 0 || !has_terminal_return) {
|
|
582
601
|
block_body.push(
|
|
583
|
-
|
|
584
|
-
type: 'ReturnStatement',
|
|
585
|
-
argument: children.length > 0 ? build_return_expression(children) : create_null_literal(),
|
|
586
|
-
metadata: { path: [] },
|
|
587
|
-
}),
|
|
602
|
+
b.return(children.length > 0 ? build_return_expression(children) : create_null_literal()),
|
|
588
603
|
);
|
|
589
604
|
}
|
|
590
605
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
body: {
|
|
595
|
-
type: 'BlockStatement',
|
|
596
|
-
body: block_body,
|
|
597
|
-
metadata: { path: [] },
|
|
598
|
-
},
|
|
599
|
-
async: false,
|
|
600
|
-
generator: false,
|
|
601
|
-
expression: false,
|
|
602
|
-
metadata: { path: [], is_branch_arrow: true },
|
|
603
|
-
});
|
|
606
|
+
const arrow = b.arrow([], b.block(block_body));
|
|
607
|
+
/** @type {any} */ (arrow.metadata).is_branch_arrow = true;
|
|
608
|
+
return arrow;
|
|
604
609
|
}
|
|
605
610
|
|
|
606
611
|
/**
|
|
@@ -686,14 +691,7 @@ function loop_body_to_callback_statements(body_nodes, transform_context) {
|
|
|
686
691
|
const create_return_statement = (source_node, render_nodes) => {
|
|
687
692
|
const cloned = render_nodes.map((node) => clone_expression_node(node));
|
|
688
693
|
const argument = cloned.length > 0 ? build_return_expression(cloned) : create_null_literal();
|
|
689
|
-
return
|
|
690
|
-
type: 'ReturnStatement',
|
|
691
|
-
argument,
|
|
692
|
-
metadata: { path: [] },
|
|
693
|
-
start: source_node?.start,
|
|
694
|
-
end: source_node?.end,
|
|
695
|
-
loc: source_node?.loc,
|
|
696
|
-
};
|
|
694
|
+
return set_loc(b.return(argument), source_node);
|
|
697
695
|
};
|
|
698
696
|
|
|
699
697
|
/** @param {any} source_node */
|
|
@@ -719,20 +717,7 @@ function loop_body_to_callback_statements(body_nodes, transform_context) {
|
|
|
719
717
|
transform_context,
|
|
720
718
|
);
|
|
721
719
|
prepend_render_nodes_to_return_statements(branch_statements, children);
|
|
722
|
-
statements.push(
|
|
723
|
-
type: 'IfStatement',
|
|
724
|
-
test: child.test,
|
|
725
|
-
consequent: {
|
|
726
|
-
type: 'BlockStatement',
|
|
727
|
-
body: branch_statements,
|
|
728
|
-
metadata: { path: [] },
|
|
729
|
-
},
|
|
730
|
-
alternate: null,
|
|
731
|
-
metadata: { path: [] },
|
|
732
|
-
start: child.start,
|
|
733
|
-
end: child.end,
|
|
734
|
-
loc: child.loc,
|
|
735
|
-
});
|
|
720
|
+
statements.push(set_loc(b.if(child.test, b.block(branch_statements), null), child));
|
|
736
721
|
continue;
|
|
737
722
|
}
|
|
738
723
|
|
|
@@ -953,13 +938,7 @@ function negate_expression(expr) {
|
|
|
953
938
|
return clone_expression_node(expr.argument);
|
|
954
939
|
}
|
|
955
940
|
|
|
956
|
-
return
|
|
957
|
-
type: 'UnaryExpression',
|
|
958
|
-
operator: '!',
|
|
959
|
-
prefix: true,
|
|
960
|
-
argument: clone_expression_node(expr),
|
|
961
|
-
metadata: { path: [] },
|
|
962
|
-
};
|
|
941
|
+
return b.unary('!', clone_expression_node(expr));
|
|
963
942
|
}
|
|
964
943
|
|
|
965
944
|
/**
|
|
@@ -971,13 +950,7 @@ function negate_expression(expr) {
|
|
|
971
950
|
*/
|
|
972
951
|
function iife_if_arrow(node) {
|
|
973
952
|
if (!is_branch_arrow(node)) return node;
|
|
974
|
-
return
|
|
975
|
-
type: 'CallExpression',
|
|
976
|
-
callee: node,
|
|
977
|
-
arguments: [],
|
|
978
|
-
optional: false,
|
|
979
|
-
metadata: { path: [] },
|
|
980
|
-
};
|
|
953
|
+
return b.call(node);
|
|
981
954
|
}
|
|
982
955
|
|
|
983
956
|
/**
|
|
@@ -1137,54 +1110,26 @@ function for_of_statement_to_jsx_child(node, transform_context) {
|
|
|
1137
1110
|
)
|
|
1138
1111
|
);
|
|
1139
1112
|
|
|
1140
|
-
let arrow
|
|
1141
|
-
type: 'ArrowFunctionExpression',
|
|
1142
|
-
params: loop_params,
|
|
1143
|
-
body: null,
|
|
1144
|
-
async: false,
|
|
1145
|
-
generator: false,
|
|
1146
|
-
expression: true,
|
|
1147
|
-
metadata: { path: [] },
|
|
1148
|
-
});
|
|
1113
|
+
let arrow;
|
|
1149
1114
|
|
|
1150
1115
|
if (body_has_loop_skip(loop_body)) {
|
|
1151
|
-
arrow
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
};
|
|
1156
|
-
arrow.expression = false;
|
|
1116
|
+
arrow = b.arrow(
|
|
1117
|
+
loop_params,
|
|
1118
|
+
b.block(loop_body_to_callback_statements(loop_body, transform_context)),
|
|
1119
|
+
);
|
|
1157
1120
|
} else {
|
|
1121
|
+
// Placeholder body — merge_branch_body_into_arrow replaces it below.
|
|
1122
|
+
arrow = b.arrow(loop_params, b.literal(null));
|
|
1158
1123
|
arrow = merge_branch_body_into_arrow(arrow, body_to_jsx_child(loop_body, transform_context));
|
|
1159
1124
|
}
|
|
1160
1125
|
|
|
1161
|
-
const attributes = [
|
|
1162
|
-
{
|
|
1163
|
-
type: 'JSXAttribute',
|
|
1164
|
-
name: { type: 'JSXIdentifier', name: 'each', metadata: { path: [] } },
|
|
1165
|
-
value: to_jsx_expression_container(node.right),
|
|
1166
|
-
metadata: { path: [] },
|
|
1167
|
-
},
|
|
1168
|
-
];
|
|
1126
|
+
const attributes = [b.jsx_attribute(b.jsx_id('each'), to_jsx_expression_container(node.right))];
|
|
1169
1127
|
|
|
1170
1128
|
if (node.key) {
|
|
1171
1129
|
const item_param = clone_expression_node(loop_params[0]);
|
|
1172
|
-
const keyed_arrow =
|
|
1173
|
-
type: 'ArrowFunctionExpression',
|
|
1174
|
-
params: [item_param],
|
|
1175
|
-
body: node.key,
|
|
1176
|
-
async: false,
|
|
1177
|
-
generator: false,
|
|
1178
|
-
expression: true,
|
|
1179
|
-
metadata: { path: [] },
|
|
1180
|
-
});
|
|
1130
|
+
const keyed_arrow = b.arrow([item_param], node.key);
|
|
1181
1131
|
attributes.push(
|
|
1182
|
-
|
|
1183
|
-
type: 'JSXAttribute',
|
|
1184
|
-
name: { type: 'JSXIdentifier', name: 'keyed', metadata: { path: [] } },
|
|
1185
|
-
value: to_jsx_expression_container(keyed_arrow, node.key),
|
|
1186
|
-
metadata: { path: [] },
|
|
1187
|
-
}),
|
|
1132
|
+
b.jsx_attribute(b.jsx_id('keyed'), to_jsx_expression_container(keyed_arrow, node.key)),
|
|
1188
1133
|
);
|
|
1189
1134
|
}
|
|
1190
1135
|
|
|
@@ -1197,6 +1142,20 @@ function for_of_statement_to_jsx_child(node, transform_context) {
|
|
|
1197
1142
|
* statement with a discriminant `d` and cases `[c1, c2, default]` becomes:
|
|
1198
1143
|
* <Switch fallback={...default}><Match when={d === c1}>...</Match>...</Switch>
|
|
1199
1144
|
*
|
|
1145
|
+
* Fall-through across cases reuses the shared `plan_switch_lift` pipeline
|
|
1146
|
+
* from `@tsrx/core`: each duplicated case body is hoisted into a
|
|
1147
|
+
* `StatementBodyHook` helper component that chains into the next helper, and
|
|
1148
|
+
* each `<Match>` body just renders the appropriate helper element. The
|
|
1149
|
+
* client transform hoists those helpers to module scope (Solid's platform
|
|
1150
|
+
* sets `moduleScopedHookComponents: true`); `compile_to_volar_mappings` opts
|
|
1151
|
+
* back out and emits the helpers locally inside the component body so Volar
|
|
1152
|
+
* still sees closure-captured bindings against the component scope.
|
|
1153
|
+
*
|
|
1154
|
+
* When any case is lifted in `typeOnly` mode the helper declarations have to
|
|
1155
|
+
* live somewhere local-scoped — we wrap the whole `<Switch>` in an IIFE that
|
|
1156
|
+
* declares them in order and returns the element. The client transform's
|
|
1157
|
+
* module-scoped helpers leave that IIFE empty, so we skip the wrapper.
|
|
1158
|
+
*
|
|
1200
1159
|
* @param {any} node
|
|
1201
1160
|
* @param {TransformContext} transform_context
|
|
1202
1161
|
* @returns {any}
|
|
@@ -1205,20 +1164,51 @@ function switch_statement_to_jsx_child(node, transform_context) {
|
|
|
1205
1164
|
transform_context.needs_switch = true;
|
|
1206
1165
|
transform_context.needs_match = true;
|
|
1207
1166
|
|
|
1167
|
+
const { case_info, case_helpers, find_next_helper_after, setup_statements } = plan_switch_lift(
|
|
1168
|
+
node,
|
|
1169
|
+
transform_context,
|
|
1170
|
+
);
|
|
1171
|
+
|
|
1208
1172
|
/** @type {any} */
|
|
1209
1173
|
let fallback = null;
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
const
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1174
|
+
/** @type {Array<{ test: any, body_jsx: any }>} */
|
|
1175
|
+
const match_entries = [];
|
|
1176
|
+
|
|
1177
|
+
for (let i = 0; i < node.cases.length; i++) {
|
|
1178
|
+
const original_case = node.cases[i];
|
|
1179
|
+
const info = case_info[i];
|
|
1180
|
+
const helper = case_helpers[i];
|
|
1181
|
+
|
|
1182
|
+
/** @type {any} */
|
|
1183
|
+
let body_jsx;
|
|
1184
|
+
if (helper) {
|
|
1185
|
+
// Lifted case: render the helper element directly. Use the
|
|
1186
|
+
// original `component_element` (not a clone) for this — its
|
|
1187
|
+
// definition's `loc` is what the case position should map to.
|
|
1188
|
+
body_jsx = helper.component_element;
|
|
1189
|
+
} else if (info.own_body.length === 0) {
|
|
1190
|
+
// Empty case in the source. If a downstream chain exists (alias
|
|
1191
|
+
// pattern like `case 1: case 2: body; break;`), the `<Match>` for
|
|
1192
|
+
// the empty label still has to render that downstream body —
|
|
1193
|
+
// Solid's `<Match>` arms are exclusive, so JS fall-through can't
|
|
1194
|
+
// rescue us here.
|
|
1195
|
+
const next_helper = find_next_helper_after(i);
|
|
1196
|
+
body_jsx = next_helper ? clone_switch_helper_invocation(next_helper) : create_null_literal();
|
|
1197
|
+
} else {
|
|
1198
|
+
// Inline case body: process JSX/non-JSX statements just like Solid
|
|
1199
|
+
// does for any other branch body, then append the chain helper if
|
|
1200
|
+
// this case falls through with no terminator.
|
|
1201
|
+
const inline_body = [...info.own_body];
|
|
1202
|
+
if (!info.has_terminator) {
|
|
1203
|
+
const next_helper = find_next_helper_after(i);
|
|
1204
|
+
if (next_helper) {
|
|
1205
|
+
inline_body.push(clone_switch_helper_invocation(next_helper));
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
body_jsx = body_to_jsx_child(inline_body, transform_context);
|
|
1218
1209
|
}
|
|
1219
1210
|
|
|
1220
|
-
|
|
1221
|
-
if (switch_case.test === null) {
|
|
1211
|
+
if (original_case.test === null) {
|
|
1222
1212
|
fallback = body_jsx;
|
|
1223
1213
|
continue;
|
|
1224
1214
|
}
|
|
@@ -1226,31 +1216,29 @@ function switch_statement_to_jsx_child(node, transform_context) {
|
|
|
1226
1216
|
// Clone the discriminant per-case: every generated `<Match when={d === caseN}>`
|
|
1227
1217
|
// would otherwise share the same AST node reference, so a downstream pass
|
|
1228
1218
|
// (lazy transforms, printer metadata, source-map annotation) mutating it on
|
|
1229
|
-
// one case would corrupt the others.
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
left: clone_expression_node(node.discriminant),
|
|
1234
|
-
right: switch_case.test,
|
|
1235
|
-
metadata: { path: [] },
|
|
1236
|
-
});
|
|
1219
|
+
// one case would corrupt the others. The right operand (`caseN`) is the
|
|
1220
|
+
// original source `test` node — unique per case, so we keep its real loc
|
|
1221
|
+
// for editor IntelliSense and don't clone it.
|
|
1222
|
+
const test = b.binary('===', clone_expression_node(node.discriminant), original_case.test);
|
|
1237
1223
|
|
|
1238
|
-
|
|
1239
|
-
create_jsx_element(
|
|
1240
|
-
'Match',
|
|
1241
|
-
[
|
|
1242
|
-
{
|
|
1243
|
-
type: 'JSXAttribute',
|
|
1244
|
-
name: { type: 'JSXIdentifier', name: 'when', metadata: { path: [] } },
|
|
1245
|
-
value: to_jsx_expression_container(test),
|
|
1246
|
-
metadata: { path: [] },
|
|
1247
|
-
},
|
|
1248
|
-
],
|
|
1249
|
-
[jsx_child_wrap(to_function_child(body_jsx))],
|
|
1250
|
-
),
|
|
1251
|
-
);
|
|
1224
|
+
match_entries.push({ test, body_jsx });
|
|
1252
1225
|
}
|
|
1253
1226
|
|
|
1227
|
+
const match_children = match_entries.map(({ test, body_jsx }) =>
|
|
1228
|
+
create_jsx_element(
|
|
1229
|
+
'Match',
|
|
1230
|
+
[
|
|
1231
|
+
{
|
|
1232
|
+
type: 'JSXAttribute',
|
|
1233
|
+
name: { type: 'JSXIdentifier', name: 'when', metadata: { path: [] } },
|
|
1234
|
+
value: to_jsx_expression_container(test),
|
|
1235
|
+
metadata: { path: [] },
|
|
1236
|
+
},
|
|
1237
|
+
],
|
|
1238
|
+
[jsx_child_wrap(to_function_child(body_jsx))],
|
|
1239
|
+
),
|
|
1240
|
+
);
|
|
1241
|
+
|
|
1254
1242
|
const attributes =
|
|
1255
1243
|
fallback !== null
|
|
1256
1244
|
? [
|
|
@@ -1263,7 +1251,17 @@ function switch_statement_to_jsx_child(node, transform_context) {
|
|
|
1263
1251
|
]
|
|
1264
1252
|
: [];
|
|
1265
1253
|
|
|
1266
|
-
|
|
1254
|
+
const switch_element = create_jsx_element('Switch', attributes, match_children);
|
|
1255
|
+
|
|
1256
|
+
if (setup_statements.length === 0) {
|
|
1257
|
+
return switch_element;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
// Local-scoped helpers (typeOnly mode): wrap the <Switch> in an IIFE that
|
|
1261
|
+
// declares the helpers in source order and returns the element.
|
|
1262
|
+
return to_jsx_expression_container(
|
|
1263
|
+
b.call(b.arrow([], b.block([...setup_statements, b.return(switch_element)]))),
|
|
1264
|
+
);
|
|
1267
1265
|
}
|
|
1268
1266
|
|
|
1269
1267
|
/**
|
|
@@ -1336,28 +1334,13 @@ function try_statement_to_jsx_child(node, transform_context) {
|
|
|
1336
1334
|
const catch_jsx = body_to_jsx_child(catch_body_nodes, transform_context);
|
|
1337
1335
|
|
|
1338
1336
|
const fallback_fn = merge_branch_body_into_arrow(
|
|
1339
|
-
|
|
1340
|
-
type: 'ArrowFunctionExpression',
|
|
1341
|
-
params: catch_params,
|
|
1342
|
-
body: null,
|
|
1343
|
-
async: false,
|
|
1344
|
-
generator: false,
|
|
1345
|
-
expression: true,
|
|
1346
|
-
metadata: { path: [] },
|
|
1347
|
-
}),
|
|
1337
|
+
b.arrow(catch_params, b.literal(null)),
|
|
1348
1338
|
catch_jsx,
|
|
1349
1339
|
);
|
|
1350
1340
|
|
|
1351
1341
|
result = create_jsx_element(
|
|
1352
1342
|
'Errored',
|
|
1353
|
-
[
|
|
1354
|
-
{
|
|
1355
|
-
type: 'JSXAttribute',
|
|
1356
|
-
name: { type: 'JSXIdentifier', name: 'fallback', metadata: { path: [] } },
|
|
1357
|
-
value: to_jsx_expression_container(fallback_fn),
|
|
1358
|
-
metadata: { path: [] },
|
|
1359
|
-
},
|
|
1360
|
-
],
|
|
1343
|
+
[b.jsx_attribute(b.jsx_id('fallback'), to_jsx_expression_container(fallback_fn))],
|
|
1361
1344
|
[result],
|
|
1362
1345
|
);
|
|
1363
1346
|
}
|
|
@@ -1424,47 +1407,16 @@ function inject_solid_imports(program, transform_context) {
|
|
|
1424
1407
|
const specifiers = [];
|
|
1425
1408
|
|
|
1426
1409
|
if (transform_context.needs_ref_prop) {
|
|
1427
|
-
specifiers.push(
|
|
1428
|
-
type: 'ImportSpecifier',
|
|
1429
|
-
imported: { type: 'Identifier', name: 'create_ref_prop', metadata: { path: [] } },
|
|
1430
|
-
local: {
|
|
1431
|
-
type: 'Identifier',
|
|
1432
|
-
name: CREATE_REF_PROP_INTERNAL_NAME,
|
|
1433
|
-
metadata: { path: [] },
|
|
1434
|
-
},
|
|
1435
|
-
metadata: { path: [] },
|
|
1436
|
-
});
|
|
1410
|
+
specifiers.push(b.import_specifier('create_ref_prop', CREATE_REF_PROP_INTERNAL_NAME));
|
|
1437
1411
|
}
|
|
1438
1412
|
|
|
1439
1413
|
if (transform_context.needs_normalize_spread_props) {
|
|
1440
|
-
specifiers.push(
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
type: 'Identifier',
|
|
1444
|
-
name: 'normalize_spread_props',
|
|
1445
|
-
metadata: { path: [] },
|
|
1446
|
-
},
|
|
1447
|
-
local: {
|
|
1448
|
-
type: 'Identifier',
|
|
1449
|
-
name: NORMALIZE_SPREAD_PROPS_INTERNAL_NAME,
|
|
1450
|
-
metadata: { path: [] },
|
|
1451
|
-
},
|
|
1452
|
-
metadata: { path: [] },
|
|
1453
|
-
});
|
|
1414
|
+
specifiers.push(
|
|
1415
|
+
b.import_specifier('normalize_spread_props', NORMALIZE_SPREAD_PROPS_INTERNAL_NAME),
|
|
1416
|
+
);
|
|
1454
1417
|
}
|
|
1455
1418
|
|
|
1456
|
-
program.body.unshift(
|
|
1457
|
-
/** @type {any} */ ({
|
|
1458
|
-
type: 'ImportDeclaration',
|
|
1459
|
-
specifiers,
|
|
1460
|
-
source: {
|
|
1461
|
-
type: 'Literal',
|
|
1462
|
-
value: '@tsrx/solid/ref',
|
|
1463
|
-
raw: "'@tsrx/solid/ref'",
|
|
1464
|
-
},
|
|
1465
|
-
metadata: { path: [] },
|
|
1466
|
-
}),
|
|
1467
|
-
);
|
|
1419
|
+
program.body.unshift(b.import_declaration(specifiers, '@tsrx/solid/ref'));
|
|
1468
1420
|
}
|
|
1469
1421
|
|
|
1470
1422
|
const needed = [];
|
|
@@ -1477,20 +1429,11 @@ function inject_solid_imports(program, transform_context) {
|
|
|
1477
1429
|
|
|
1478
1430
|
if (needed.length === 0) return;
|
|
1479
1431
|
|
|
1480
|
-
const specifiers = needed.map((name) => ({
|
|
1481
|
-
type: 'ImportSpecifier',
|
|
1482
|
-
imported: { type: 'Identifier', name, metadata: { path: [] } },
|
|
1483
|
-
local: { type: 'Identifier', name, metadata: { path: [] } },
|
|
1484
|
-
metadata: { path: [] },
|
|
1485
|
-
}));
|
|
1486
|
-
|
|
1487
1432
|
program.body.unshift(
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
metadata: { path: [] },
|
|
1493
|
-
}),
|
|
1433
|
+
b.imports(
|
|
1434
|
+
needed.map((name) => [name, name]),
|
|
1435
|
+
'solid-js',
|
|
1436
|
+
),
|
|
1494
1437
|
);
|
|
1495
1438
|
}
|
|
1496
1439
|
|
|
@@ -1876,57 +1819,19 @@ function is_solid_jsx_ref_attribute(attr) {
|
|
|
1876
1819
|
*/
|
|
1877
1820
|
function dynamic_element_to_jsx_child(node, transform_context) {
|
|
1878
1821
|
const dynamic_id = set_loc(create_generated_identifier('DynamicElement'), node.id);
|
|
1879
|
-
const alias_declaration = set_loc(
|
|
1880
|
-
/** @type {any} */ ({
|
|
1881
|
-
type: 'VariableDeclaration',
|
|
1882
|
-
kind: 'const',
|
|
1883
|
-
declarations: [
|
|
1884
|
-
{
|
|
1885
|
-
type: 'VariableDeclarator',
|
|
1886
|
-
id: dynamic_id,
|
|
1887
|
-
init: clone_expression_node(node.id),
|
|
1888
|
-
metadata: { path: [] },
|
|
1889
|
-
},
|
|
1890
|
-
],
|
|
1891
|
-
metadata: { path: [] },
|
|
1892
|
-
}),
|
|
1893
|
-
node,
|
|
1894
|
-
);
|
|
1822
|
+
const alias_declaration = set_loc(b.const(dynamic_id, clone_expression_node(node.id)), node);
|
|
1895
1823
|
const jsx_element = create_dynamic_jsx_element(dynamic_id, node, transform_context);
|
|
1896
1824
|
|
|
1897
1825
|
return to_jsx_expression_container(
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
{
|
|
1908
|
-
type: 'ReturnStatement',
|
|
1909
|
-
argument: {
|
|
1910
|
-
type: 'ConditionalExpression',
|
|
1911
|
-
test: clone_identifier(dynamic_id),
|
|
1912
|
-
consequent: jsx_element,
|
|
1913
|
-
alternate: create_null_literal(),
|
|
1914
|
-
metadata: { path: [] },
|
|
1915
|
-
},
|
|
1916
|
-
metadata: { path: [] },
|
|
1917
|
-
},
|
|
1918
|
-
],
|
|
1919
|
-
metadata: { path: [] },
|
|
1920
|
-
}),
|
|
1921
|
-
async: false,
|
|
1922
|
-
generator: false,
|
|
1923
|
-
expression: false,
|
|
1924
|
-
metadata: { path: [] },
|
|
1925
|
-
},
|
|
1926
|
-
arguments: [],
|
|
1927
|
-
optional: false,
|
|
1928
|
-
metadata: { path: [] },
|
|
1929
|
-
}),
|
|
1826
|
+
b.call(
|
|
1827
|
+
b.arrow(
|
|
1828
|
+
[],
|
|
1829
|
+
b.block([
|
|
1830
|
+
alias_declaration,
|
|
1831
|
+
b.return(b.conditional(clone_identifier(dynamic_id), jsx_element, create_null_literal())),
|
|
1832
|
+
]),
|
|
1833
|
+
),
|
|
1834
|
+
),
|
|
1930
1835
|
node,
|
|
1931
1836
|
);
|
|
1932
1837
|
}
|