@solidjs/babel-plugin 2.0.0-rc.5 → 2.0.0-rc.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +20 -0
- package/README.md +38 -7
- package/index.js +3523 -384
- package/package.json +10 -3
package/index.js
CHANGED
|
@@ -1520,7 +1520,11 @@ value,
|
|
|
1520
1520
|
const isLocked = isLockedDOMProperty(tagName, name);
|
|
1521
1521
|
|
|
1522
1522
|
if (isChildProp || namespace === "prop" || isLocked) {
|
|
1523
|
-
|
|
1523
|
+
// Locked/stateful DOM properties (value/checked/...) must go through the
|
|
1524
|
+
// runtime in hydratable builds so the hydration claim pass can adopt
|
|
1525
|
+
// pre-hydration user state instead of overwriting it (#3182). setProperty
|
|
1526
|
+
// carries the select-microtask and input/textarea nullish special cases.
|
|
1527
|
+
if (config.hydratable && (isLocked || namespace !== "prop")) {
|
|
1524
1528
|
return t$1.callExpression(registerImportMethod(path, "setProperty"), [
|
|
1525
1529
|
elem,
|
|
1526
1530
|
t$1.stringLiteral(name),
|
|
@@ -2330,7 +2334,10 @@ results)
|
|
|
2330
2334
|
}
|
|
2331
2335
|
|
|
2332
2336
|
// properties
|
|
2333
|
-
if (
|
|
2337
|
+
if (
|
|
2338
|
+
staticValue && (
|
|
2339
|
+
ChildProperties.has(key) || tagName === "select" && key === "value"))
|
|
2340
|
+
{
|
|
2334
2341
|
results.exprs.push(
|
|
2335
2342
|
t$1.expressionStatement(
|
|
2336
2343
|
setAttr$2(attribute, elem, key, staticValue, { tagName })
|
|
@@ -2814,170 +2821,6 @@ attributes,
|
|
|
2814
2821
|
|
|
2815
2822
|
}
|
|
2816
2823
|
|
|
2817
|
-
/**
|
|
2818
|
-
* Patch-mode eligibility + emission (DESIGN-PATCH-CHANNEL.md, PR-C).
|
|
2819
|
-
*
|
|
2820
|
-
* A template scope qualifies for patch mode when EVERY dynamic binding's
|
|
2821
|
-
* value is a pure expression over member reads of ONE stable subject
|
|
2822
|
-
* identifier (Tier 1: bare member chains; Tier 2: ternary/binary/logical/
|
|
2823
|
-
* template-literal/unary compositions of Tier-1 reads and literals).
|
|
2824
|
-
* Anything else — calls, assignments, functions, foreign identifiers —
|
|
2825
|
-
* disqualifies the SCOPE (per-binding islands remain future work; v1 is
|
|
2826
|
-
* all-or-nothing per template scope, matching the fixture shape).
|
|
2827
|
-
*
|
|
2828
|
-
* Emission (dual driver, shared body):
|
|
2829
|
-
*
|
|
2830
|
-
* _$patchDriver(subject, (_n$, _p$, _f$) => {
|
|
2831
|
-
* let _v$;
|
|
2832
|
-
* if (_f$ || (_v$ = <expr(_n$)>) !== <expr(_p$)>) <setAttr(..., _v$)>;
|
|
2833
|
-
* ...
|
|
2834
|
-
* });
|
|
2835
|
-
*
|
|
2836
|
-
* The runtime driver registers on the store's patch channel when the
|
|
2837
|
-
* subject is a patchable record, else falls back to a tracked force-mode
|
|
2838
|
-
* effect running the same body (reads through the subject track; force
|
|
2839
|
-
* short-circuits every compare, so `prev` is never dereferenced there).
|
|
2840
|
-
* Semantics are therefore IDENTICAL either way; patch mode only changes
|
|
2841
|
-
* who dispatches.
|
|
2842
|
-
*/
|
|
2843
|
-
|
|
2844
|
-
// Node types allowed inside an eligible binding expression (Tier 1+2).
|
|
2845
|
-
function isEligibleExpr(node, subject) {
|
|
2846
|
-
switch (node.type) {
|
|
2847
|
-
case "Identifier":
|
|
2848
|
-
return node.name === subject || node.name === "undefined";
|
|
2849
|
-
case "MemberExpression":{
|
|
2850
|
-
const m = node;
|
|
2851
|
-
if (m.computed) {
|
|
2852
|
-
if (!t__namespace.isStringLiteral(m.property) && !t__namespace.isNumericLiteral(m.property)) return false;
|
|
2853
|
-
} else if (!t__namespace.isIdentifier(m.property)) {
|
|
2854
|
-
return false;
|
|
2855
|
-
}
|
|
2856
|
-
return isEligibleExpr(m.object, subject);
|
|
2857
|
-
}
|
|
2858
|
-
case "StringLiteral":
|
|
2859
|
-
case "NumericLiteral":
|
|
2860
|
-
case "BooleanLiteral":
|
|
2861
|
-
case "NullLiteral":
|
|
2862
|
-
case "BigIntLiteral":
|
|
2863
|
-
return true;
|
|
2864
|
-
case "ConditionalExpression":{
|
|
2865
|
-
const c = node;
|
|
2866
|
-
return (
|
|
2867
|
-
isEligibleExpr(c.test, subject) &&
|
|
2868
|
-
isEligibleExpr(c.consequent, subject) &&
|
|
2869
|
-
isEligibleExpr(c.alternate, subject));
|
|
2870
|
-
|
|
2871
|
-
}
|
|
2872
|
-
case "BinaryExpression":{
|
|
2873
|
-
const b = node;
|
|
2874
|
-
if (b.operator === "in" || b.operator === "instanceof") return false;
|
|
2875
|
-
return isEligibleExpr(b.left, subject) && isEligibleExpr(b.right, subject);
|
|
2876
|
-
}
|
|
2877
|
-
case "LogicalExpression":{
|
|
2878
|
-
const l = node;
|
|
2879
|
-
return isEligibleExpr(l.left, subject) && isEligibleExpr(l.right, subject);
|
|
2880
|
-
}
|
|
2881
|
-
case "UnaryExpression":{
|
|
2882
|
-
const u = node;
|
|
2883
|
-
if (u.operator === "delete" || u.operator === "throw") return false;
|
|
2884
|
-
return isEligibleExpr(u.argument, subject);
|
|
2885
|
-
}
|
|
2886
|
-
case "TemplateLiteral":
|
|
2887
|
-
return node.expressions.every((e) =>
|
|
2888
|
-
t__namespace.isExpression(e) ? isEligibleExpr(e, subject) : false
|
|
2889
|
-
);
|
|
2890
|
-
case "ParenthesizedExpression":
|
|
2891
|
-
return isEligibleExpr(node.expression, subject);
|
|
2892
|
-
default:
|
|
2893
|
-
return false;
|
|
2894
|
-
}
|
|
2895
|
-
}
|
|
2896
|
-
|
|
2897
|
-
/** Find the single subject: the root identifier of the FIRST member chain
|
|
2898
|
-
* encountered. Every other read must root at the same name. */
|
|
2899
|
-
function findSubject(node) {
|
|
2900
|
-
switch (node.type) {
|
|
2901
|
-
case "MemberExpression":
|
|
2902
|
-
return findSubject(node.object);
|
|
2903
|
-
case "Identifier":
|
|
2904
|
-
return node.name === "undefined" ? null : node.name;
|
|
2905
|
-
case "ConditionalExpression":
|
|
2906
|
-
return (
|
|
2907
|
-
findSubject(node.test) ??
|
|
2908
|
-
findSubject(node.consequent) ??
|
|
2909
|
-
findSubject(node.alternate));
|
|
2910
|
-
|
|
2911
|
-
case "BinaryExpression":
|
|
2912
|
-
case "LogicalExpression":
|
|
2913
|
-
return (
|
|
2914
|
-
findSubject(node.left) ?? findSubject(node.right));
|
|
2915
|
-
|
|
2916
|
-
case "UnaryExpression":
|
|
2917
|
-
return findSubject(node.argument);
|
|
2918
|
-
case "TemplateLiteral":{
|
|
2919
|
-
for (const e of node.expressions) {
|
|
2920
|
-
const s = findSubject(e);
|
|
2921
|
-
if (s) return s;
|
|
2922
|
-
}
|
|
2923
|
-
return null;
|
|
2924
|
-
}
|
|
2925
|
-
case "ParenthesizedExpression":
|
|
2926
|
-
return findSubject(node.expression);
|
|
2927
|
-
default:
|
|
2928
|
-
return null;
|
|
2929
|
-
}
|
|
2930
|
-
}
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
/** Analyze a template scope's dynamic binding values. Returns the subject
|
|
2937
|
-
* when EVERY value is an eligible pure expression over it, else null. */
|
|
2938
|
-
function analyzePatchEligibility(values) {
|
|
2939
|
-
if (values.length === 0) return null;
|
|
2940
|
-
let subject = null;
|
|
2941
|
-
for (const v of values) {
|
|
2942
|
-
const s = findSubject(v);
|
|
2943
|
-
if (s === null) return null; // static-only binding: no dispatch source
|
|
2944
|
-
if (subject === null) subject = s;else
|
|
2945
|
-
if (subject !== s) return null; // multi-subject: Q3 territory, bail in v1
|
|
2946
|
-
}
|
|
2947
|
-
if (subject === null) return null;
|
|
2948
|
-
for (const v of values) {
|
|
2949
|
-
if (!isEligibleExpr(v, subject)) return null;
|
|
2950
|
-
}
|
|
2951
|
-
return { subject };
|
|
2952
|
-
}
|
|
2953
|
-
|
|
2954
|
-
/** Clone `expr` substituting the subject identifier with `replacement`.
|
|
2955
|
-
* Safe because eligibility rejected functions/shadowing constructs. */
|
|
2956
|
-
function substituteSubject(
|
|
2957
|
-
expr,
|
|
2958
|
-
subject,
|
|
2959
|
-
replacement)
|
|
2960
|
-
{
|
|
2961
|
-
const clone = t__namespace.cloneNode(expr, true);
|
|
2962
|
-
const rewrite = (node) => {
|
|
2963
|
-
if (t__namespace.isIdentifier(node) && node.name === subject) return t__namespace.cloneNode(replacement);
|
|
2964
|
-
for (const key of Object.keys(node)) {
|
|
2965
|
-
const value = node[key];
|
|
2966
|
-
if (Array.isArray(value)) {
|
|
2967
|
-
for (let i = 0; i < value.length; i++) {
|
|
2968
|
-
if (value[i] && typeof value[i].type === "string") value[i] = rewrite(value[i]);
|
|
2969
|
-
}
|
|
2970
|
-
} else if (value && typeof value.type === "string") {
|
|
2971
|
-
// Never rewrite non-computed member PROPERTY positions.
|
|
2972
|
-
if (t__namespace.isMemberExpression(node) && key === "property" && !node.computed) continue;
|
|
2973
|
-
node[key] = rewrite(value);
|
|
2974
|
-
}
|
|
2975
|
-
}
|
|
2976
|
-
return node;
|
|
2977
|
-
};
|
|
2978
|
-
return rewrite(clone);
|
|
2979
|
-
}
|
|
2980
|
-
|
|
2981
2824
|
function createTemplate$2(
|
|
2982
2825
|
path,
|
|
2983
2826
|
result,
|
|
@@ -2991,16 +2834,9 @@ wrap)
|
|
|
2991
2834
|
!(result.exprs.length || result.dynamics.length || result.postExprs?.length) &&
|
|
2992
2835
|
decl.declarations.length === 1)
|
|
2993
2836
|
{
|
|
2994
|
-
// Static single-root template: a candidate row function returning it is
|
|
2995
|
-
// trivially pure (no reactive work at all).
|
|
2996
|
-
if (config.patchDriver) recordPureRow(path, result, null);
|
|
2997
2837
|
return decl.declarations[0].init;
|
|
2998
2838
|
} else {
|
|
2999
|
-
const
|
|
3000
|
-
const dynamicsStmt = patched ? patched.stmt : wrapDynamics$1(path, result.dynamics);
|
|
3001
|
-
if (config.patchDriver && (result.dynamics.length === 0 || patched)) {
|
|
3002
|
-
recordPureRow(path, result, patched ? patched.subject : null);
|
|
3003
|
-
}
|
|
2839
|
+
const dynamicsStmt = wrapDynamics$1(path, result.dynamics);
|
|
3004
2840
|
const stmts = [
|
|
3005
2841
|
decl,
|
|
3006
2842
|
...result.exprs,
|
|
@@ -3116,179 +2952,6 @@ function registerTemplate(path, results) {
|
|
|
3116
2952
|
results.decl = t__namespace.variableDeclaration("var", results.declarations);
|
|
3117
2953
|
}
|
|
3118
2954
|
|
|
3119
|
-
/**
|
|
3120
|
-
* Row-proof analysis (DESIGN-PATCH-CHANNEL §3c). A function qualifies as a
|
|
3121
|
-
* PURE ROW — buildable with no per-row owner, so the patch-mode list driver
|
|
3122
|
-
* may engage — when ALL of:
|
|
3123
|
-
*
|
|
3124
|
-
* - it is a single-parameter function (plain Identifier param, not
|
|
3125
|
-
* destructured; not async/generator) whose body is exactly this compiled
|
|
3126
|
-
* template: an expression body, or a block whose ONLY statement returns
|
|
3127
|
-
* it (checked before the flat-statement emission inserts our own);
|
|
3128
|
-
* - the template has a single root element (`result.id` — fragments never
|
|
3129
|
-
* reach here) and emitted NO reactive or owned work: dynamics either
|
|
3130
|
-
* absent or all landed in ONE patchDriver body, and `result.exprs` holds
|
|
3131
|
-
* only inert DOM wiring (member-target assignments like `_el$.$$click =
|
|
3132
|
-
* fn` / `_el$.style.cssText = v`, and `_el$.addEventListener(...)`) —
|
|
3133
|
-
* any `insert`/`createComponent`/`memo`/`use`/ref/spread emission is a
|
|
3134
|
-
* call or conditional shape and fails the walk (default-deny);
|
|
3135
|
-
* - the patch subject IS the row parameter. A pure template patching an
|
|
3136
|
-
* OUTER subject would register once per created row on a long-lived
|
|
3137
|
-
* record with no per-row disposal — the foreign-subject leak the runtime
|
|
3138
|
-
* probe never caught.
|
|
3139
|
-
*
|
|
3140
|
-
* Handler/attribute VALUE expressions may be arbitrary user code: stamped
|
|
3141
|
-
* rows are only ever built for real mounts (never speculatively), so their
|
|
3142
|
-
* evaluation timing is identical to the classic path. Event handlers are
|
|
3143
|
-
* not reactive bindings — values evaluate exactly once at build — so the
|
|
3144
|
-
* only ownership divergence is a factory creating owned work in value
|
|
3145
|
-
* position (`onClick={makeHandler(row)}` calling onCleanup), which is not
|
|
3146
|
-
* a supported pattern (owned work belongs in effects/refs; ruled
|
|
3147
|
-
* non-responsibility). Denying evaluation-position calls would cost
|
|
3148
|
-
* legitimate currying to guard it; instead the runtime dev-asserts that a
|
|
3149
|
-
* stamped row's build attaches nothing to the list owner.
|
|
3150
|
-
*
|
|
3151
|
-
* Qualifying functions are recorded and wrapped with `rowProof` at program
|
|
3152
|
-
* exit (postprocess) — the stamp travels with the function object, so
|
|
3153
|
-
* extracted row functions prove at their DEFINITION site and work across
|
|
3154
|
-
* modules, which the runtime probe could never see.
|
|
3155
|
-
*/
|
|
3156
|
-
function recordPureRow(path, result, subject) {
|
|
3157
|
-
const parent = path.parentPath;
|
|
3158
|
-
if (!parent) return;
|
|
3159
|
-
let fn;
|
|
3160
|
-
if (
|
|
3161
|
-
(t__namespace.isArrowFunctionExpression(parent.node) || t__namespace.isFunctionExpression(parent.node)) &&
|
|
3162
|
-
parent.node.body === path.node)
|
|
3163
|
-
{
|
|
3164
|
-
fn = parent.node;
|
|
3165
|
-
} else if (
|
|
3166
|
-
t__namespace.isReturnStatement(parent.node) &&
|
|
3167
|
-
parent.node.argument === path.node &&
|
|
3168
|
-
parent.parentPath &&
|
|
3169
|
-
t__namespace.isBlockStatement(parent.parentPath.node) &&
|
|
3170
|
-
parent.parentPath.node.body.length === 1 &&
|
|
3171
|
-
parent.parentPath.parentPath && (
|
|
3172
|
-
t__namespace.isArrowFunctionExpression(parent.parentPath.parentPath.node) ||
|
|
3173
|
-
t__namespace.isFunctionExpression(parent.parentPath.parentPath.node)) &&
|
|
3174
|
-
parent.parentPath.parentPath.node.body === parent.parentPath.node)
|
|
3175
|
-
{
|
|
3176
|
-
fn = parent.parentPath.parentPath.node;
|
|
3177
|
-
}
|
|
3178
|
-
if (!fn || fn.async || fn.generator) return;
|
|
3179
|
-
if (fn.params.length !== 1 || !t__namespace.isIdentifier(fn.params[0])) return;
|
|
3180
|
-
const param = fn.params[0].name;
|
|
3181
|
-
if (subject !== null && subject !== param) return;
|
|
3182
|
-
|
|
3183
|
-
const isLocalMemberTarget = (n) => {
|
|
3184
|
-
if (!t__namespace.isMemberExpression(n) || n.computed) return false;
|
|
3185
|
-
let obj = n.object;
|
|
3186
|
-
while (t__namespace.isMemberExpression(obj) && !obj.computed) obj = obj.object;
|
|
3187
|
-
return t__namespace.isIdentifier(obj);
|
|
3188
|
-
};
|
|
3189
|
-
for (const stmt of result.exprs) {
|
|
3190
|
-
if (!t__namespace.isExpressionStatement(stmt)) return;
|
|
3191
|
-
const e = stmt.expression;
|
|
3192
|
-
if (t__namespace.isAssignmentExpression(e) && e.operator === "=" && isLocalMemberTarget(e.left)) continue;
|
|
3193
|
-
if (
|
|
3194
|
-
t__namespace.isCallExpression(e) &&
|
|
3195
|
-
t__namespace.isMemberExpression(e.callee) &&
|
|
3196
|
-
!e.callee.computed &&
|
|
3197
|
-
t__namespace.isIdentifier(e.callee.property) &&
|
|
3198
|
-
e.callee.property.name === "addEventListener")
|
|
3199
|
-
|
|
3200
|
-
continue;
|
|
3201
|
-
return;
|
|
3202
|
-
}
|
|
3203
|
-
if (result.postExprs?.length) {
|
|
3204
|
-
const data = path.scope.getProgramParent().data;
|
|
3205
|
-
const moduleName = getRendererConfig(path, "dom").moduleName;
|
|
3206
|
-
const rheUid = data.imports?.get(`${moduleName}:runHydrationEvents`);
|
|
3207
|
-
for (const stmt of result.postExprs) {
|
|
3208
|
-
if (
|
|
3209
|
-
!t__namespace.isExpressionStatement(stmt) ||
|
|
3210
|
-
!t__namespace.isCallExpression(stmt.expression) ||
|
|
3211
|
-
!t__namespace.isIdentifier(stmt.expression.callee) ||
|
|
3212
|
-
!rheUid ||
|
|
3213
|
-
stmt.expression.callee.name !== rheUid.name)
|
|
3214
|
-
|
|
3215
|
-
return;
|
|
3216
|
-
}
|
|
3217
|
-
}
|
|
3218
|
-
|
|
3219
|
-
const data = path.scope.getProgramParent().data;
|
|
3220
|
-
(data.pureRows || (data.pureRows = new Set())).add(fn);
|
|
3221
|
-
}
|
|
3222
|
-
|
|
3223
|
-
/** Patch-mode emission (shared/patch.ts): one compiled body doing inline
|
|
3224
|
-
* compares + setAttr writes, handed to the runtime driver which registers
|
|
3225
|
-
* on the store patch channel (patchable subject) or falls back to a
|
|
3226
|
-
* tracked force-mode effect running the SAME body. Returns undefined when
|
|
3227
|
-
* the scope is ineligible — caller falls through to the effect shapes.
|
|
3228
|
-
* The subject rides along for row-proof analysis (recordPureRow). */
|
|
3229
|
-
function wrapPatchMode(
|
|
3230
|
-
path,
|
|
3231
|
-
dynamics)
|
|
3232
|
-
{
|
|
3233
|
-
const config = getConfig(path);
|
|
3234
|
-
if (dynamics.length === 0) return;
|
|
3235
|
-
const eligibility = analyzePatchEligibility(dynamics.map((d) => d.value));
|
|
3236
|
-
if (!eligibility) return;
|
|
3237
|
-
const subject = eligibility.subject;
|
|
3238
|
-
// The subject must be a stable local binding (not reassigned): row params
|
|
3239
|
-
// and const destructures qualify; anything else falls back to effects.
|
|
3240
|
-
const binding = path.scope.getBinding(subject);
|
|
3241
|
-
if (!binding || !binding.constant) return;
|
|
3242
|
-
// Fixed `$`-suffixed locals, matching wrapDynamics' `_v$`/`_p$` convention
|
|
3243
|
-
// (and byte-parity with the Oxc compiler's emission): substitution only
|
|
3244
|
-
// rewrites subject references, so nothing else in an eligible expression
|
|
3245
|
-
// can collide with these.
|
|
3246
|
-
const nId = t__namespace.identifier("_n$");
|
|
3247
|
-
const pId = t__namespace.identifier("_p$");
|
|
3248
|
-
const fId = t__namespace.identifier("_f$");
|
|
3249
|
-
const stmts = [];
|
|
3250
|
-
let vIndex = 0;
|
|
3251
|
-
for (const d of dynamics) {
|
|
3252
|
-
let value = d.value;
|
|
3253
|
-
if (d.classProperty && !t__namespace.isBooleanLiteral(value) && !t__namespace.isUnaryExpression(value)) {
|
|
3254
|
-
value = t__namespace.unaryExpression("!", t__namespace.unaryExpression("!", value));
|
|
3255
|
-
}
|
|
3256
|
-
const vId = t__namespace.identifier(++vIndex === 1 ? "_v$" : `_v$${vIndex}`);
|
|
3257
|
-
stmts.push(
|
|
3258
|
-
t__namespace.variableDeclaration("const", [
|
|
3259
|
-
t__namespace.variableDeclarator(vId, substituteSubject(value, subject, nId))]
|
|
3260
|
-
)
|
|
3261
|
-
);
|
|
3262
|
-
stmts.push(
|
|
3263
|
-
t__namespace.ifStatement(
|
|
3264
|
-
t__namespace.logicalExpression(
|
|
3265
|
-
"||",
|
|
3266
|
-
t__namespace.cloneNode(fId),
|
|
3267
|
-
t__namespace.binaryExpression("!==", t__namespace.cloneNode(vId), substituteSubject(value, subject, pId))
|
|
3268
|
-
),
|
|
3269
|
-
t__namespace.expressionStatement(
|
|
3270
|
-
setAttr$2(path, d.elem, d.key, t__namespace.cloneNode(vId), {
|
|
3271
|
-
tagName: d.tagName,
|
|
3272
|
-
dynamic: true,
|
|
3273
|
-
styleProperty: d.styleProperty,
|
|
3274
|
-
classProperty: d.classProperty
|
|
3275
|
-
})
|
|
3276
|
-
)
|
|
3277
|
-
)
|
|
3278
|
-
);
|
|
3279
|
-
}
|
|
3280
|
-
const driverId = registerImportMethod(path, config.patchDriver, undefined);
|
|
3281
|
-
return {
|
|
3282
|
-
stmt: t__namespace.expressionStatement(
|
|
3283
|
-
t__namespace.callExpression(driverId, [
|
|
3284
|
-
t__namespace.identifier(subject),
|
|
3285
|
-
t__namespace.arrowFunctionExpression([nId, pId, fId], t__namespace.blockStatement(stmts))]
|
|
3286
|
-
)
|
|
3287
|
-
),
|
|
3288
|
-
subject
|
|
3289
|
-
};
|
|
3290
|
-
}
|
|
3291
|
-
|
|
3292
2955
|
function wrapDynamics$1(path, dynamics) {
|
|
3293
2956
|
if (!dynamics.length) return;
|
|
3294
2957
|
const config = getConfig(path);
|
|
@@ -4375,9 +4038,15 @@ path,
|
|
|
4375
4038
|
return memo;
|
|
4376
4039
|
}, []);
|
|
4377
4040
|
|
|
4041
|
+
// The DOM transform handles `ref` outside its spread prop sources.
|
|
4042
|
+
const propAttributes = attributes.filter((attribute) => {
|
|
4043
|
+
const node = attribute.node;
|
|
4044
|
+
return !(t.isJSXAttribute(node) && t.isJSXIdentifier(node.name) && node.name.name === "ref");
|
|
4045
|
+
});
|
|
4046
|
+
|
|
4378
4047
|
let props;
|
|
4379
|
-
if (
|
|
4380
|
-
props = [
|
|
4048
|
+
if (propAttributes.length === 1 && t.isJSXSpreadAttribute(propAttributes[0].node)) {
|
|
4049
|
+
props = [propAttributes[0].node.argument];
|
|
4381
4050
|
} else {
|
|
4382
4051
|
props = [];
|
|
4383
4052
|
let runningObject = [],
|
|
@@ -14276,6 +13945,29 @@ function serialize(node, options) {
|
|
|
14276
13945
|
}
|
|
14277
13946
|
return serializeChildNodes(node, opts);
|
|
14278
13947
|
}
|
|
13948
|
+
/**
|
|
13949
|
+
* Serializes an AST element node to an HTML string, including the element node.
|
|
13950
|
+
*
|
|
13951
|
+
* @example
|
|
13952
|
+
*
|
|
13953
|
+
* ```js
|
|
13954
|
+
* const parse5 = require('parse5');
|
|
13955
|
+
*
|
|
13956
|
+
* const document = parse5.parseFragment('<div>Hello, <b>world</b>!</div>');
|
|
13957
|
+
*
|
|
13958
|
+
* // Serializes the <div> element.
|
|
13959
|
+
* const str = parse5.serializeOuter(document.childNodes[0]);
|
|
13960
|
+
*
|
|
13961
|
+
* console.log(str); //> '<div>Hello, <b>world</b>!</div>'
|
|
13962
|
+
* ```
|
|
13963
|
+
*
|
|
13964
|
+
* @param node Node to serialize.
|
|
13965
|
+
* @param options Serialization options.
|
|
13966
|
+
*/
|
|
13967
|
+
function serializeOuter(node, options) {
|
|
13968
|
+
const opts = { ...defaultOpts, ...options };
|
|
13969
|
+
return serializeNode(node, opts);
|
|
13970
|
+
}
|
|
14279
13971
|
function serializeChildNodes(parentNode, options) {
|
|
14280
13972
|
let html = '';
|
|
14281
13973
|
// Get container of the child nodes
|
|
@@ -14409,6 +14101,29 @@ function innerHTML(htmlFragment) {
|
|
|
14409
14101
|
return serialize(parsedFragment);
|
|
14410
14102
|
}
|
|
14411
14103
|
|
|
14104
|
+
/**
|
|
14105
|
+
* A document shell (`<html>`/`<head>`/`<body>` root) can never round-trip a
|
|
14106
|
+
* body-context fragment parse — the parser strips those wrappers no matter
|
|
14107
|
+
* how well-formed the markup is — so it is parsed as a DOCUMENT instead and
|
|
14108
|
+
* the shell element serialized back out (#3259). The same "used in the right
|
|
14109
|
+
* place" assumption the synthetic `<table>` makes for table partials: a
|
|
14110
|
+
* shell template is hydrated against a real document, not client-created.
|
|
14111
|
+
* Restructuring inside the shell still mismatches — an implied `<head>`,
|
|
14112
|
+
* flow content hoisted out of `<head>`, a `<p>` split in `<body>` — so the
|
|
14113
|
+
* #3099 guarantee holds for markup a browser would actually rebuild.
|
|
14114
|
+
*/
|
|
14115
|
+
function documentShell(html, tag) {
|
|
14116
|
+
const document = parse(html);
|
|
14117
|
+
const htmlElement = document.childNodes.find(
|
|
14118
|
+
(node) => node.tagName === "html"
|
|
14119
|
+
);
|
|
14120
|
+
const shell =
|
|
14121
|
+
tag === "html" ?
|
|
14122
|
+
htmlElement :
|
|
14123
|
+
htmlElement.childNodes.find((node) => node.tagName === tag);
|
|
14124
|
+
return serializeOuter(shell);
|
|
14125
|
+
}
|
|
14126
|
+
|
|
14412
14127
|
/**
|
|
14413
14128
|
* Returns an object with information when the markup is invalid
|
|
14414
14129
|
*
|
|
@@ -14479,7 +14194,8 @@ function isInvalidMarkup(html) {
|
|
|
14479
14194
|
}
|
|
14480
14195
|
|
|
14481
14196
|
/** Parse HTML. `browser` is a string with the supposed resulting html of a real `innerHTML` call */
|
|
14482
|
-
const
|
|
14197
|
+
const shell = /^<(html|head|body)>/.exec(html);
|
|
14198
|
+
const browser = shell ? documentShell(html, shell[1]) : innerHTML(html);
|
|
14483
14199
|
|
|
14484
14200
|
if (html.toLowerCase() !== browser.toLowerCase()) {
|
|
14485
14201
|
return {
|
|
@@ -14540,35 +14256,11 @@ var postprocess = (path, state) => {
|
|
|
14540
14256
|
domTemplates.length > 0 && appendTemplates$1(path, domTemplates);
|
|
14541
14257
|
ssrTemplates.length > 0 && appendTemplates(path, ssrTemplates);
|
|
14542
14258
|
}
|
|
14543
|
-
|
|
14544
|
-
// Compile-time row proofs (DESIGN-PATCH-CHANNEL §3c): wrap each function
|
|
14545
|
-
// recorded by recordPureRow with the runtime's `rowProof` marker so the
|
|
14546
|
-
// patch-mode list driver can engage on proven-pure rows — admission is
|
|
14547
|
-
// decided here, statically; there is no runtime purity probe. The stamp
|
|
14548
|
-
// travels with the function object, so extracted row functions qualify at
|
|
14549
|
-
// their definition site.
|
|
14550
|
-
if (data.pureRows?.size) {
|
|
14551
|
-
const rowProofId = registerImportMethod(
|
|
14552
|
-
path,
|
|
14553
|
-
"rowProof",
|
|
14554
|
-
getRendererConfig(path, "dom").moduleName
|
|
14555
|
-
);
|
|
14556
|
-
const pureRows = data.pureRows;
|
|
14557
|
-
path.traverse({
|
|
14558
|
-
"ArrowFunctionExpression|FunctionExpression"(fnPath) {
|
|
14559
|
-
if (!pureRows.has(fnPath.node)) return;
|
|
14560
|
-
pureRows.delete(fnPath.node);
|
|
14561
|
-
fnPath.replaceWith(
|
|
14562
|
-
t__namespace.callExpression(t__namespace.cloneNode(rowProofId), [fnPath.node])
|
|
14563
|
-
);
|
|
14564
|
-
fnPath.skip();
|
|
14565
|
-
}
|
|
14566
|
-
});
|
|
14567
|
-
}
|
|
14568
14259
|
};
|
|
14569
14260
|
|
|
14570
14261
|
const config = {
|
|
14571
14262
|
moduleName: "@solidjs/web",
|
|
14263
|
+
syntax: "auto",
|
|
14572
14264
|
generate: "dom",
|
|
14573
14265
|
hydratable: false,
|
|
14574
14266
|
dev: false,
|
|
@@ -14595,7 +14287,6 @@ const config = {
|
|
|
14595
14287
|
contextToCustomElements: true,
|
|
14596
14288
|
staticMarker: "@static",
|
|
14597
14289
|
effectWrapper: "effect",
|
|
14598
|
-
patchDriver: false,
|
|
14599
14290
|
memoWrapper: "memo",
|
|
14600
14291
|
validate: true,
|
|
14601
14292
|
inlineStyles: true,
|
|
@@ -14604,10 +14295,13 @@ const config = {
|
|
|
14604
14295
|
|
|
14605
14296
|
var preprocess = (path, state) => {
|
|
14606
14297
|
const file = path.hub.file;
|
|
14298
|
+
const parsedMetadata = file.ast.tsrxStyle;
|
|
14299
|
+
if (parsedMetadata) {
|
|
14300
|
+
file.metadata.css = parsedMetadata.css;
|
|
14301
|
+
file.metadata.cssHash = parsedMetadata.cssHash;
|
|
14302
|
+
delete file.ast.tsrxStyle;
|
|
14303
|
+
}
|
|
14607
14304
|
const merged = file.metadata.config = Object.assign({}, config, state.opts);
|
|
14608
|
-
// Boolean opt-in parity with the native loader: `patchDriver: true` means
|
|
14609
|
-
// the default import name (downstream code uses the value AS the name).
|
|
14610
|
-
if (merged.patchDriver === true) merged.patchDriver = "patchDriver";
|
|
14611
14305
|
const lib = merged.requireImportSource;
|
|
14612
14306
|
if (lib) {
|
|
14613
14307
|
const comments = file.ast.comments ?? [];
|
|
@@ -14627,14 +14321,3459 @@ var preprocess = (path, state) => {
|
|
|
14627
14321
|
}
|
|
14628
14322
|
};
|
|
14629
14323
|
|
|
14630
|
-
|
|
14324
|
+
/**
|
|
14325
|
+
* TSRX → Solid JSX desugaring.
|
|
14326
|
+
*
|
|
14327
|
+
* Walks the ESTree AST produced by `@tsrx/core` (before Solid's local
|
|
14328
|
+
* lazy-destructuring transform runs) and lowers every TSRX construct to Solid 2.0 builtIn
|
|
14329
|
+
* component JSX, in place. The result is a plain ESTree TSX program that
|
|
14330
|
+
* `estree-to-babel` converts for the existing JSX pipeline. BuiltIns are
|
|
14331
|
+
* referenced as bare identifiers (`Show`, `For`, …) so the plugin's normal
|
|
14332
|
+
* `builtIns` auto-import machinery resolves them against `moduleName`.
|
|
14333
|
+
*
|
|
14334
|
+
* Lowering contract (mirrored byte-for-byte by the Oxc frontend):
|
|
14335
|
+
* - `@{ body; render }` — expression position: `render` alone when there is no
|
|
14336
|
+
* setup, otherwise `(() => { body; return render; })()`; function-body
|
|
14337
|
+
* position: inline statements ending in `return render`.
|
|
14338
|
+
* - `@if` — `Show` for a single branch, `Switch`/`Match` for chains; `@else`
|
|
14339
|
+
* becomes `fallback`.
|
|
14340
|
+
* - `@for (const x of expr; index i; key k)` — `For`; `key` present emits
|
|
14341
|
+
* `keyed={(x) => k}`, while an index without a key emits `keyed={false}`;
|
|
14342
|
+
* `@empty` becomes `fallback`. RC `For` hands the callback an item accessor
|
|
14343
|
+
* in custom-key and non-keyed modes. The index is an accessor only with a
|
|
14344
|
+
* custom key.
|
|
14345
|
+
* - `@switch` — `Switch` with one `Match when={disc === test}` per `@case`;
|
|
14346
|
+
* `@default` becomes `fallback`.
|
|
14347
|
+
* - `@try/@pending/@catch (e, reset)` — `<Errored fallback={(e, reset) =>
|
|
14348
|
+
* …}><Loading fallback={pending}>{content}</Loading></Errored>`; RC
|
|
14349
|
+
* `Errored` passes an `ErrorAccessor`, so reads of `e` rewrite to calls.
|
|
14350
|
+
* - `<{expr}>` — `<Dynamic component={expr} …>` (deliberate adaptation from
|
|
14351
|
+
* `@tsrx/solid`'s hoisted `dynamic()` factory; semantically equivalent).
|
|
14352
|
+
*
|
|
14353
|
+
* Scoped `<style>` elements are handled and stripped by the preceding style
|
|
14354
|
+
* pass, so they must never reach this transform.
|
|
14355
|
+
*/
|
|
14631
14356
|
|
|
14632
14357
|
|
|
14633
14358
|
|
|
14634
|
-
|
|
14635
|
-
|
|
14636
|
-
|
|
14637
|
-
|
|
14359
|
+
|
|
14360
|
+
|
|
14361
|
+
|
|
14362
|
+
|
|
14363
|
+
|
|
14364
|
+
|
|
14365
|
+
|
|
14366
|
+
|
|
14367
|
+
/** Keys that are never traversed: locations, tooling metadata, and TSRX
|
|
14368
|
+
* keyword-span pointers that carry no semantics of their own. */
|
|
14369
|
+
const SKIP_KEYS$2 = new Set([
|
|
14370
|
+
"type",
|
|
14371
|
+
"loc",
|
|
14372
|
+
"start",
|
|
14373
|
+
"end",
|
|
14374
|
+
"range",
|
|
14375
|
+
"metadata",
|
|
14376
|
+
"leadingComments",
|
|
14377
|
+
"trailingComments",
|
|
14378
|
+
"innerComments",
|
|
14379
|
+
"alternateKeyword",
|
|
14380
|
+
"emptyKeyword"]
|
|
14381
|
+
);
|
|
14382
|
+
|
|
14383
|
+
const RENDER_ENTRY_TYPES = new Set([
|
|
14384
|
+
"JSXElement",
|
|
14385
|
+
"JSXFragment",
|
|
14386
|
+
"JSXText",
|
|
14387
|
+
"JSXCodeBlock",
|
|
14388
|
+
"JSXIfExpression",
|
|
14389
|
+
"JSXForExpression",
|
|
14390
|
+
"JSXSwitchExpression",
|
|
14391
|
+
"JSXTryExpression",
|
|
14392
|
+
"JSXStyleElement"]
|
|
14393
|
+
);
|
|
14394
|
+
|
|
14395
|
+
const VALID_JSX_CHILD_TYPES = new Set([
|
|
14396
|
+
"JSXElement",
|
|
14397
|
+
"JSXFragment",
|
|
14398
|
+
"JSXText",
|
|
14399
|
+
"JSXExpressionContainer",
|
|
14400
|
+
"JSXSpreadChild"]
|
|
14401
|
+
);
|
|
14402
|
+
|
|
14403
|
+
const FUNCTION_TYPES$1 = new Set([
|
|
14404
|
+
"FunctionDeclaration",
|
|
14405
|
+
"FunctionExpression",
|
|
14406
|
+
"ArrowFunctionExpression"]
|
|
14407
|
+
);
|
|
14408
|
+
|
|
14409
|
+
function isNode$3(value) {
|
|
14410
|
+
return (
|
|
14411
|
+
typeof value === "object" &&
|
|
14412
|
+
value !== null &&
|
|
14413
|
+
typeof value.type === "string");
|
|
14414
|
+
|
|
14415
|
+
}
|
|
14416
|
+
|
|
14417
|
+
function fail$2(message, node) {
|
|
14418
|
+
const start = node?.loc?.start;
|
|
14419
|
+
throw new SyntaxError(start ? `${message} (${start.line}:${start.column})` : message);
|
|
14420
|
+
}
|
|
14421
|
+
|
|
14422
|
+
// ---------------------------------------------------------------------------
|
|
14423
|
+
// ESTree builders (converted to Babel shapes later by estree-to-babel)
|
|
14424
|
+
// ---------------------------------------------------------------------------
|
|
14425
|
+
|
|
14426
|
+
function withLoc$2(node, src) {
|
|
14427
|
+
if (src) {
|
|
14428
|
+
if (src.start !== undefined) node.start = src.start;
|
|
14429
|
+
if (src.end !== undefined) node.end = src.end;
|
|
14430
|
+
if (src.loc !== undefined) node.loc = src.loc;
|
|
14431
|
+
}
|
|
14432
|
+
return node;
|
|
14433
|
+
}
|
|
14434
|
+
|
|
14435
|
+
function ident$1(name, src) {
|
|
14436
|
+
return withLoc$2({ type: "Identifier", name }, src);
|
|
14437
|
+
}
|
|
14438
|
+
|
|
14439
|
+
function jsxIdent(name, src) {
|
|
14440
|
+
return withLoc$2({ type: "JSXIdentifier", name }, src);
|
|
14441
|
+
}
|
|
14442
|
+
|
|
14443
|
+
function jsxContainer(expression, src) {
|
|
14444
|
+
return withLoc$2({ type: "JSXExpressionContainer", expression }, src ?? expression);
|
|
14445
|
+
}
|
|
14446
|
+
|
|
14447
|
+
function jsxAttr(name, expression, src) {
|
|
14448
|
+
return withLoc$2(
|
|
14449
|
+
{
|
|
14450
|
+
type: "JSXAttribute",
|
|
14451
|
+
name: jsxIdent(name, src),
|
|
14452
|
+
value: jsxContainer(expression, src ?? expression)
|
|
14453
|
+
},
|
|
14454
|
+
src ?? expression
|
|
14455
|
+
);
|
|
14456
|
+
}
|
|
14457
|
+
|
|
14458
|
+
function jsxElement(name, attributes, children, src) {
|
|
14459
|
+
const selfClosing = children.length === 0;
|
|
14460
|
+
return withLoc$2(
|
|
14461
|
+
{
|
|
14462
|
+
type: "JSXElement",
|
|
14463
|
+
openingElement: withLoc$2(
|
|
14464
|
+
{ type: "JSXOpeningElement", name: jsxIdent(name, src), attributes, selfClosing },
|
|
14465
|
+
src
|
|
14466
|
+
),
|
|
14467
|
+
closingElement: selfClosing ?
|
|
14468
|
+
null :
|
|
14469
|
+
withLoc$2({ type: "JSXClosingElement", name: jsxIdent(name, src) }, src),
|
|
14470
|
+
children
|
|
14471
|
+
},
|
|
14472
|
+
src
|
|
14473
|
+
);
|
|
14474
|
+
}
|
|
14475
|
+
|
|
14476
|
+
function jsxFragment(children, src) {
|
|
14477
|
+
return withLoc$2(
|
|
14478
|
+
{
|
|
14479
|
+
type: "JSXFragment",
|
|
14480
|
+
openingFragment: withLoc$2({ type: "JSXOpeningFragment" }, src),
|
|
14481
|
+
closingFragment: withLoc$2({ type: "JSXClosingFragment" }, src),
|
|
14482
|
+
children
|
|
14483
|
+
},
|
|
14484
|
+
src
|
|
14485
|
+
);
|
|
14486
|
+
}
|
|
14487
|
+
|
|
14488
|
+
function arrow(params, body, src) {
|
|
14489
|
+
return withLoc$2(
|
|
14490
|
+
{
|
|
14491
|
+
type: "ArrowFunctionExpression",
|
|
14492
|
+
id: null,
|
|
14493
|
+
params,
|
|
14494
|
+
body,
|
|
14495
|
+
expression: body.type !== "BlockStatement",
|
|
14496
|
+
async: false,
|
|
14497
|
+
generator: false
|
|
14498
|
+
},
|
|
14499
|
+
src
|
|
14500
|
+
);
|
|
14501
|
+
}
|
|
14502
|
+
|
|
14503
|
+
function blockStatement(body, src) {
|
|
14504
|
+
return withLoc$2({ type: "BlockStatement", body }, src);
|
|
14505
|
+
}
|
|
14506
|
+
|
|
14507
|
+
function returnStatement(argument, src) {
|
|
14508
|
+
return withLoc$2({ type: "ReturnStatement", argument }, src);
|
|
14509
|
+
}
|
|
14510
|
+
|
|
14511
|
+
function iife(statements, src) {
|
|
14512
|
+
return withLoc$2(
|
|
14513
|
+
{
|
|
14514
|
+
type: "CallExpression",
|
|
14515
|
+
callee: arrow([], blockStatement(statements, src), src),
|
|
14516
|
+
arguments: [],
|
|
14517
|
+
optional: false
|
|
14518
|
+
},
|
|
14519
|
+
src
|
|
14520
|
+
);
|
|
14521
|
+
}
|
|
14522
|
+
|
|
14523
|
+
function callOn(callee, src) {
|
|
14524
|
+
return withLoc$2({ type: "CallExpression", callee, arguments: [], optional: false }, src);
|
|
14525
|
+
}
|
|
14526
|
+
|
|
14527
|
+
function strictEquals(left, right, src) {
|
|
14528
|
+
return withLoc$2({ type: "BinaryExpression", operator: "===", left, right }, src);
|
|
14529
|
+
}
|
|
14530
|
+
|
|
14531
|
+
function stringLiteral(value, src) {
|
|
14532
|
+
return withLoc$2({ type: "Literal", value, raw: JSON.stringify(value) }, src);
|
|
14533
|
+
}
|
|
14534
|
+
|
|
14535
|
+
function cloneNode$1(value) {
|
|
14536
|
+
if (Array.isArray(value)) return value.map(cloneNode$1);
|
|
14537
|
+
if (typeof value === "object" && value !== null) {
|
|
14538
|
+
const out = {};
|
|
14539
|
+
for (const key of Object.keys(value)) {
|
|
14540
|
+
out[key] = cloneNode$1(value[key]);
|
|
14541
|
+
}
|
|
14542
|
+
return out;
|
|
14543
|
+
}
|
|
14544
|
+
return value;
|
|
14545
|
+
}
|
|
14546
|
+
|
|
14547
|
+
function accessorLazyPattern(pattern) {
|
|
14548
|
+
const clone = cloneNode$1(pattern);
|
|
14549
|
+
clone.lazy = true;
|
|
14550
|
+
const metadata = clone.metadata ??= {};
|
|
14551
|
+
delete metadata.lazy_id;
|
|
14552
|
+
metadata.lazy_source_accessor = true;
|
|
14553
|
+
return clone;
|
|
14554
|
+
}
|
|
14555
|
+
|
|
14556
|
+
function eagerPattern(pattern) {
|
|
14557
|
+
const clone = cloneNode$1(pattern);
|
|
14558
|
+
clearLazy(clone);
|
|
14559
|
+
return clone;
|
|
14560
|
+
|
|
14561
|
+
function clearLazy(node) {
|
|
14562
|
+
if (node.type === "ObjectPattern" || node.type === "ArrayPattern") node.lazy = false;
|
|
14563
|
+
for (const key of Object.keys(node)) {
|
|
14564
|
+
if (SKIP_KEYS$2.has(key)) continue;
|
|
14565
|
+
const value = node[key];
|
|
14566
|
+
if (Array.isArray(value)) {
|
|
14567
|
+
for (const item of value) if (isNode$3(item)) clearLazy(item);
|
|
14568
|
+
} else if (isNode$3(value)) {
|
|
14569
|
+
clearLazy(value);
|
|
14570
|
+
}
|
|
14571
|
+
}
|
|
14572
|
+
}
|
|
14573
|
+
}
|
|
14574
|
+
|
|
14575
|
+
// ---------------------------------------------------------------------------
|
|
14576
|
+
// Main walk
|
|
14577
|
+
// ---------------------------------------------------------------------------
|
|
14578
|
+
|
|
14579
|
+
function desugarProgram(program) {
|
|
14580
|
+
genericVisit(program);
|
|
14581
|
+
return program;
|
|
14582
|
+
}
|
|
14583
|
+
|
|
14584
|
+
/** Desugars `node`, returning its replacement for expression-shaped slots. */
|
|
14585
|
+
function transform$1(node) {
|
|
14586
|
+
switch (node.type) {
|
|
14587
|
+
case "JSXCodeBlock":
|
|
14588
|
+
return codeBlockToExpression(node);
|
|
14589
|
+
case "JSXIfExpression":
|
|
14590
|
+
return ifToJsx(node);
|
|
14591
|
+
case "JSXForExpression":
|
|
14592
|
+
return forToJsx(node);
|
|
14593
|
+
case "JSXSwitchExpression":
|
|
14594
|
+
return switchToJsx(node);
|
|
14595
|
+
case "JSXTryExpression":
|
|
14596
|
+
return tryToJsx(node);
|
|
14597
|
+
case "JSXStyleElement":
|
|
14598
|
+
return fail$2("A TSRX <style> element survived the scoped style pass", node);
|
|
14599
|
+
case "JSXElement":
|
|
14600
|
+
return desugarElement(node);
|
|
14601
|
+
case "JSXFragment":
|
|
14602
|
+
node.children && (node.children = desugarChildren(node.children));
|
|
14603
|
+
return node;
|
|
14604
|
+
default:
|
|
14605
|
+
if (FUNCTION_TYPES$1.has(node.type) && isNode$3(node.body) && node.body.type === "JSXCodeBlock") {
|
|
14606
|
+
const body = node.body;
|
|
14607
|
+
genericVisit(node, "body");
|
|
14608
|
+
node.body = codeBlockToFunctionBody(body);
|
|
14609
|
+
if (node.type === "ArrowFunctionExpression") node.expression = false;
|
|
14610
|
+
return node;
|
|
14611
|
+
}
|
|
14612
|
+
genericVisit(node);
|
|
14613
|
+
return node;
|
|
14614
|
+
}
|
|
14615
|
+
}
|
|
14616
|
+
|
|
14617
|
+
function genericVisit(node, skipKey) {
|
|
14618
|
+
for (const key of Object.keys(node)) {
|
|
14619
|
+
if (SKIP_KEYS$2.has(key) || key === skipKey) continue;
|
|
14620
|
+
const value = node[key];
|
|
14621
|
+
if (Array.isArray(value)) {
|
|
14622
|
+
for (let i = 0; i < value.length; i++) {
|
|
14623
|
+
if (isNode$3(value[i])) value[i] = transform$1(value[i]);
|
|
14624
|
+
}
|
|
14625
|
+
} else if (isNode$3(value)) {
|
|
14626
|
+
node[key] = transform$1(value);
|
|
14627
|
+
}
|
|
14628
|
+
}
|
|
14629
|
+
}
|
|
14630
|
+
|
|
14631
|
+
// ---------------------------------------------------------------------------
|
|
14632
|
+
// Elements and children
|
|
14633
|
+
// ---------------------------------------------------------------------------
|
|
14634
|
+
|
|
14635
|
+
function desugarElement(el) {
|
|
14636
|
+
const opening = el.openingElement;
|
|
14637
|
+
const isDynamic = !!opening.isDynamic;
|
|
14638
|
+
genericVisit(opening);
|
|
14639
|
+
const children = desugarChildren(el.children ?? []);
|
|
14640
|
+
if (isDynamic) {
|
|
14641
|
+
const nameContainer = opening.name;
|
|
14642
|
+
const componentExpr = nameContainer.expression;
|
|
14643
|
+
const attributes = [
|
|
14644
|
+
jsxAttr("component", componentExpr, nameContainer),
|
|
14645
|
+
...(opening.attributes ?? [])];
|
|
14646
|
+
|
|
14647
|
+
return jsxElement("Dynamic", attributes, children, el);
|
|
14648
|
+
}
|
|
14649
|
+
el.children = children;
|
|
14650
|
+
return el;
|
|
14651
|
+
}
|
|
14652
|
+
|
|
14653
|
+
function desugarChildren(children) {
|
|
14654
|
+
return children.map((child) => {
|
|
14655
|
+
const result = transform$1(child);
|
|
14656
|
+
if (!VALID_JSX_CHILD_TYPES.has(result.type)) return jsxContainer(result, child);
|
|
14657
|
+
return result;
|
|
14658
|
+
});
|
|
14659
|
+
}
|
|
14660
|
+
|
|
14661
|
+
/** Embed an expression as a JSX child: JSX stays structural, everything else
|
|
14662
|
+
* goes through an expression container. */
|
|
14663
|
+
function asChild(expression) {
|
|
14664
|
+
if (expression.type === "JSXElement" || expression.type === "JSXFragment") return expression;
|
|
14665
|
+
return jsxContainer(expression);
|
|
14666
|
+
}
|
|
14667
|
+
|
|
14668
|
+
// ---------------------------------------------------------------------------
|
|
14669
|
+
// Template blocks (implicit statement containers)
|
|
14670
|
+
// ---------------------------------------------------------------------------
|
|
14671
|
+
|
|
14672
|
+
|
|
14673
|
+
|
|
14674
|
+
|
|
14675
|
+
|
|
14676
|
+
|
|
14677
|
+
const LOOP_TYPES = new Set([
|
|
14678
|
+
"ForStatement",
|
|
14679
|
+
"ForInStatement",
|
|
14680
|
+
"ForOfStatement",
|
|
14681
|
+
"WhileStatement",
|
|
14682
|
+
"DoWhileStatement"]
|
|
14683
|
+
);
|
|
14684
|
+
|
|
14685
|
+
/** TSRX constructs are validation boundaries: their blocks are checked when
|
|
14686
|
+
* they desugar, with their own construct label. `@{}` containers become
|
|
14687
|
+
* function bodies (IIFEs) where escaping statements are legal again. */
|
|
14688
|
+
const ESCAPE_BOUNDARY_TYPES = new Set([
|
|
14689
|
+
"JSXCodeBlock",
|
|
14690
|
+
"JSXIfExpression",
|
|
14691
|
+
"JSXForExpression",
|
|
14692
|
+
"JSXSwitchExpression",
|
|
14693
|
+
"JSXTryExpression"]
|
|
14694
|
+
);
|
|
14695
|
+
|
|
14696
|
+
function escapeMessage(kind, construct) {
|
|
14697
|
+
// Match the ecosystem (@tsrx/solid) diagnostics verbatim for @if and @for.
|
|
14698
|
+
if (construct === "@if" || construct === "@else") {
|
|
14699
|
+
if (kind === "return")
|
|
14700
|
+
return "Return statements are not allowed inside TSRX template @if blocks. Move the return before the template output or render conditionally instead.";
|
|
14701
|
+
if (kind === "break")
|
|
14702
|
+
return "Break statements are not allowed inside TSRX template @if blocks.";
|
|
14703
|
+
return "Continue statements are not allowed inside TSRX template @if blocks. Filter before rendering or use conditional output instead.";
|
|
14704
|
+
}
|
|
14705
|
+
if (construct === "@for" || construct === "@empty") {
|
|
14706
|
+
if (kind === "return")
|
|
14707
|
+
return "Return statements are not allowed inside TSRX template for...of loops. Filter the iterable before rendering or use an @empty fallback for empty lists.";
|
|
14708
|
+
if (kind === "break")
|
|
14709
|
+
return "Break statements are not allowed inside TSRX template for...of loops.";
|
|
14710
|
+
return "Continue statements are not allowed inside TSRX template for...of loops. Filter the iterable before rendering.";
|
|
14711
|
+
}
|
|
14712
|
+
const noun = kind === "return" ? "Return" : kind === "break" ? "Break" : "Continue";
|
|
14713
|
+
return `${noun} statements are not allowed inside TSRX template ${construct} blocks.`;
|
|
14714
|
+
}
|
|
14715
|
+
|
|
14716
|
+
/** Reject return/break/continue that would escape a template control-flow
|
|
14717
|
+
* block. Nested functions, loops, switches, and labels open their own escape
|
|
14718
|
+
* targets; nested TSRX constructs validate themselves. */
|
|
14719
|
+
function validateNoControlFlowEscape(entries, construct) {
|
|
14720
|
+
for (const entry of entries) check(entry, 0, 0, new Set());
|
|
14721
|
+
|
|
14722
|
+
function check(node, loops, breakables, labels) {
|
|
14723
|
+
if (FUNCTION_TYPES$1.has(node.type) || ESCAPE_BOUNDARY_TYPES.has(node.type)) return;
|
|
14724
|
+
switch (node.type) {
|
|
14725
|
+
case "ReturnStatement":
|
|
14726
|
+
fail$2(escapeMessage("return", construct), node);
|
|
14727
|
+
break;
|
|
14728
|
+
case "BreakStatement":{
|
|
14729
|
+
const label = isNode$3(node.label) ? node.label.name : null;
|
|
14730
|
+
if (label ? !labels.has(label) : breakables === 0) {
|
|
14731
|
+
fail$2(escapeMessage("break", construct), node);
|
|
14732
|
+
}
|
|
14733
|
+
return;
|
|
14734
|
+
}
|
|
14735
|
+
case "ContinueStatement":{
|
|
14736
|
+
const label = isNode$3(node.label) ? node.label.name : null;
|
|
14737
|
+
if (label ? !labels.has(label) : loops === 0) {
|
|
14738
|
+
fail$2(escapeMessage("continue", construct), node);
|
|
14739
|
+
}
|
|
14740
|
+
return;
|
|
14741
|
+
}
|
|
14742
|
+
case "SwitchStatement":{
|
|
14743
|
+
if (isNode$3(node.discriminant)) check(node.discriminant, loops, breakables, labels);
|
|
14744
|
+
for (const switchCase of node.cases) {
|
|
14745
|
+
check(switchCase, loops, breakables + 1, labels);
|
|
14746
|
+
}
|
|
14747
|
+
return;
|
|
14748
|
+
}
|
|
14749
|
+
case "LabeledStatement":{
|
|
14750
|
+
const withLabel = new Set(labels);
|
|
14751
|
+
withLabel.add(node.label.name);
|
|
14752
|
+
check(node.body, loops, breakables, withLabel);
|
|
14753
|
+
return;
|
|
14754
|
+
}
|
|
14755
|
+
}
|
|
14756
|
+
const nextLoops = LOOP_TYPES.has(node.type) ? loops + 1 : loops;
|
|
14757
|
+
const nextBreakables = LOOP_TYPES.has(node.type) ? breakables + 1 : breakables;
|
|
14758
|
+
for (const key of Object.keys(node)) {
|
|
14759
|
+
if (SKIP_KEYS$2.has(key)) continue;
|
|
14760
|
+
const value = node[key];
|
|
14761
|
+
if (Array.isArray(value)) {
|
|
14762
|
+
for (const item of value) if (isNode$3(item)) check(item, nextLoops, nextBreakables, labels);
|
|
14763
|
+
} else if (isNode$3(value)) {
|
|
14764
|
+
check(value, nextLoops, nextBreakables, labels);
|
|
14765
|
+
}
|
|
14766
|
+
}
|
|
14767
|
+
}
|
|
14768
|
+
}
|
|
14769
|
+
|
|
14770
|
+
function toBlockBody(node) {
|
|
14771
|
+
return node.type === "BlockStatement" ? node.body : [node];
|
|
14772
|
+
}
|
|
14773
|
+
|
|
14774
|
+
/** Split a template block into leading setup statements and trailing render
|
|
14775
|
+
* output nodes. Setup is desugared here; renders are desugared by callers
|
|
14776
|
+
* (their lowering depends on the position they land in). When `construct` is
|
|
14777
|
+
* given, escaping control flow (return/break/continue) is rejected first. */
|
|
14778
|
+
function blockToParts(entries, construct) {
|
|
14779
|
+
if (construct) validateNoControlFlowEscape(entries, construct);
|
|
14780
|
+
const setup = [];
|
|
14781
|
+
const renders = [];
|
|
14782
|
+
for (const entry of entries) {
|
|
14783
|
+
if (RENDER_ENTRY_TYPES.has(entry.type)) {
|
|
14784
|
+
if (entry.type === "JSXText" && /^\s*$/.test(String(entry.value))) continue;
|
|
14785
|
+
renders.push(entry);
|
|
14786
|
+
} else {
|
|
14787
|
+
if (renders.length > 0) {
|
|
14788
|
+
fail$2("Statements may not follow the rendered output inside a TSRX template block", entry);
|
|
14789
|
+
}
|
|
14790
|
+
setup.push(transform$1(entry));
|
|
14791
|
+
}
|
|
14792
|
+
}
|
|
14793
|
+
return { setup, renders };
|
|
14794
|
+
}
|
|
14795
|
+
|
|
14796
|
+
function renderNodeToExpression(node) {
|
|
14797
|
+
const result = transform$1(node);
|
|
14798
|
+
if (result.type === "JSXText") return stringLiteral(String(result.value), result);
|
|
14799
|
+
return result;
|
|
14800
|
+
}
|
|
14801
|
+
|
|
14802
|
+
/** Combine desugared render nodes into one expression (fragment for
|
|
14803
|
+
* siblings). Returns null when the block rendered nothing. */
|
|
14804
|
+
function rendersToExpression(renders, src) {
|
|
14805
|
+
if (renders.length === 0) return null;
|
|
14806
|
+
if (renders.length === 1) return renderNodeToExpression(renders[0]);
|
|
14807
|
+
return jsxFragment(
|
|
14808
|
+
renders.map((render) => {
|
|
14809
|
+
const result = transform$1(render);
|
|
14810
|
+
if (!VALID_JSX_CHILD_TYPES.has(result.type)) return jsxContainer(result, render);
|
|
14811
|
+
return result;
|
|
14812
|
+
}),
|
|
14813
|
+
src
|
|
14814
|
+
);
|
|
14815
|
+
}
|
|
14816
|
+
|
|
14817
|
+
/** Lower a whole template block to a single expression. Returns null only for
|
|
14818
|
+
* a fully empty block; a block with setup but no render is an error. */
|
|
14819
|
+
function blockToExpression(blockNode, context) {
|
|
14820
|
+
const parts = blockToParts(toBlockBody(blockNode), context);
|
|
14821
|
+
const renderExpr = rendersToExpression(parts.renders, blockNode);
|
|
14822
|
+
if (parts.setup.length === 0) return renderExpr;
|
|
14823
|
+
if (!renderExpr) {
|
|
14824
|
+
fail$2(`A TSRX ${context} block with setup statements must end with rendered output`, blockNode);
|
|
14825
|
+
}
|
|
14826
|
+
return iife([...parts.setup, returnStatement(renderExpr, blockNode)], blockNode);
|
|
14827
|
+
}
|
|
14828
|
+
|
|
14829
|
+
// ---------------------------------------------------------------------------
|
|
14830
|
+
// @{} statement containers
|
|
14831
|
+
// ---------------------------------------------------------------------------
|
|
14832
|
+
|
|
14833
|
+
function codeBlockToExpression(cb) {
|
|
14834
|
+
const setup = cb.body.map(transform$1);
|
|
14835
|
+
if (!isNode$3(cb.render)) {
|
|
14836
|
+
fail$2("A TSRX statement container is missing its rendered output node", cb);
|
|
14837
|
+
}
|
|
14838
|
+
const renderExpr = renderNodeToExpression(cb.render);
|
|
14839
|
+
if (setup.length === 0) return renderExpr;
|
|
14840
|
+
return iife([...setup, returnStatement(renderExpr, cb)], cb);
|
|
14841
|
+
}
|
|
14842
|
+
|
|
14843
|
+
function codeBlockToFunctionBody(cb) {
|
|
14844
|
+
const setup = cb.body.map(transform$1);
|
|
14845
|
+
if (!isNode$3(cb.render)) {
|
|
14846
|
+
fail$2("A TSRX statement container is missing its rendered output node", cb);
|
|
14847
|
+
}
|
|
14848
|
+
const renderExpr = renderNodeToExpression(cb.render);
|
|
14849
|
+
return blockStatement([...setup, returnStatement(renderExpr, cb)], cb);
|
|
14850
|
+
}
|
|
14851
|
+
|
|
14852
|
+
// ---------------------------------------------------------------------------
|
|
14853
|
+
// @if — Show / Switch+Match
|
|
14854
|
+
// ---------------------------------------------------------------------------
|
|
14855
|
+
|
|
14856
|
+
function ifToJsx(node) {
|
|
14857
|
+
const branches = [];
|
|
14858
|
+
let current = node;
|
|
14859
|
+
let elseBlock = null;
|
|
14860
|
+
for (;;) {
|
|
14861
|
+
branches.push({
|
|
14862
|
+
test: transform$1(current.test),
|
|
14863
|
+
block: current.consequent,
|
|
14864
|
+
src: current
|
|
14865
|
+
});
|
|
14866
|
+
const alternate = current.alternate;
|
|
14867
|
+
if (alternate && (alternate.type === "IfStatement" || alternate.type === "JSXIfExpression")) {
|
|
14868
|
+
current = alternate;
|
|
14869
|
+
} else {
|
|
14870
|
+
elseBlock = alternate;
|
|
14871
|
+
break;
|
|
14872
|
+
}
|
|
14873
|
+
}
|
|
14874
|
+
|
|
14875
|
+
const elseExpr = elseBlock ? blockToExpression(elseBlock, "@else") : null;
|
|
14876
|
+
|
|
14877
|
+
if (branches.length === 1) {
|
|
14878
|
+
const branch = branches[0];
|
|
14879
|
+
const branchExpr = blockToExpression(branch.block, "@if");
|
|
14880
|
+
const attributes = [jsxAttr("when", branch.test, branch.src)];
|
|
14881
|
+
if (elseExpr) attributes.push(jsxAttr("fallback", elseExpr, elseBlock));
|
|
14882
|
+
return jsxElement("Show", attributes, branchExpr ? [asChild(branchExpr)] : [], node);
|
|
14883
|
+
}
|
|
14884
|
+
|
|
14885
|
+
const matches = branches.map((branch) => {
|
|
14886
|
+
const branchExpr = blockToExpression(branch.block, "@if");
|
|
14887
|
+
return jsxElement(
|
|
14888
|
+
"Match",
|
|
14889
|
+
[jsxAttr("when", branch.test, branch.src)],
|
|
14890
|
+
branchExpr ? [asChild(branchExpr)] : [],
|
|
14891
|
+
branch.src
|
|
14892
|
+
);
|
|
14893
|
+
});
|
|
14894
|
+
const attributes = elseExpr ? [jsxAttr("fallback", elseExpr, elseBlock)] : [];
|
|
14895
|
+
return jsxElement("Switch", attributes, matches, node);
|
|
14896
|
+
}
|
|
14897
|
+
|
|
14898
|
+
// ---------------------------------------------------------------------------
|
|
14899
|
+
// @for — For
|
|
14900
|
+
// ---------------------------------------------------------------------------
|
|
14901
|
+
|
|
14902
|
+
function forToJsx(node) {
|
|
14903
|
+
if (node.statementType !== "ForOfStatement") {
|
|
14904
|
+
fail$2(
|
|
14905
|
+
"@for must iterate with for...of; for...in and classic for loops are not TSRX template constructs",
|
|
14906
|
+
node
|
|
14907
|
+
);
|
|
14908
|
+
}
|
|
14909
|
+
if (node.await) {
|
|
14910
|
+
fail$2("`for await` is not supported inside Solid TSRX templates", node);
|
|
14911
|
+
}
|
|
14912
|
+
|
|
14913
|
+
const left = node.left;
|
|
14914
|
+
const pattern =
|
|
14915
|
+
left.type === "VariableDeclaration" ? left.declarations[0].id : left;
|
|
14916
|
+
const each = transform$1(node.right);
|
|
14917
|
+
const index = isNode$3(node.index) ? node.index : null;
|
|
14918
|
+
const key = isNode$3(node.key) ? node.key : null;
|
|
14919
|
+
const usesIndexOnlyMode = index !== null && key === null;
|
|
14920
|
+
const itemIsAccessor = key !== null || usesIndexOnlyMode;
|
|
14921
|
+
|
|
14922
|
+
const parts = blockToParts(toBlockBody(node.body), "@for");
|
|
14923
|
+
const renderExpr = rendersToExpression(parts.renders, node.body);
|
|
14924
|
+
if (!renderExpr) fail$2("A TSRX @for body must end with rendered output", node);
|
|
14925
|
+
|
|
14926
|
+
const callbackBody =
|
|
14927
|
+
parts.setup.length > 0 ?
|
|
14928
|
+
blockStatement(
|
|
14929
|
+
[...parts.setup, returnStatement(renderExpr, node.body)],
|
|
14930
|
+
node.body
|
|
14931
|
+
) :
|
|
14932
|
+
renderExpr;
|
|
14933
|
+
|
|
14934
|
+
// RC `For` callback shape:
|
|
14935
|
+
// - default keyed mode: raw item, accessor index (there is no TSRX index)
|
|
14936
|
+
// - keyed={false}: accessor item, raw index
|
|
14937
|
+
// - custom key: accessor item, accessor index
|
|
14938
|
+
if (itemIsAccessor && pattern.type === "Identifier")
|
|
14939
|
+
rewriteReadsToCalls(callbackBody, pattern.name);
|
|
14940
|
+
if (key && index) rewriteReadsToCalls(callbackBody, index.name);
|
|
14941
|
+
|
|
14942
|
+
const callbackPattern =
|
|
14943
|
+
itemIsAccessor && pattern.type !== "Identifier" ? accessorLazyPattern(pattern) : pattern;
|
|
14944
|
+
const params = [callbackPattern];
|
|
14945
|
+
if (index) params.push(index);
|
|
14946
|
+
|
|
14947
|
+
const attributes = [jsxAttr("each", each, node.right)];
|
|
14948
|
+
if (key) {
|
|
14949
|
+
attributes.push(jsxAttr("keyed", arrow([eagerPattern(pattern)], transform$1(key), key), key));
|
|
14950
|
+
} else if (usesIndexOnlyMode) {
|
|
14951
|
+
attributes.push(
|
|
14952
|
+
jsxAttr("keyed", withLoc$2({ type: "Literal", value: false, raw: "false" }, index), index)
|
|
14953
|
+
);
|
|
14954
|
+
}
|
|
14955
|
+
if (isNode$3(node.empty)) {
|
|
14956
|
+
const emptyExpr = blockToExpression(node.empty, "@empty");
|
|
14957
|
+
if (emptyExpr) attributes.push(jsxAttr("fallback", emptyExpr, node.empty));
|
|
14958
|
+
}
|
|
14959
|
+
|
|
14960
|
+
return jsxElement(
|
|
14961
|
+
"For",
|
|
14962
|
+
attributes,
|
|
14963
|
+
[jsxContainer(arrow(params, callbackBody, node), node)],
|
|
14964
|
+
node
|
|
14965
|
+
);
|
|
14966
|
+
}
|
|
14967
|
+
|
|
14968
|
+
// ---------------------------------------------------------------------------
|
|
14969
|
+
// @switch — Switch / Match
|
|
14970
|
+
// ---------------------------------------------------------------------------
|
|
14971
|
+
|
|
14972
|
+
function switchToJsx(node) {
|
|
14973
|
+
const discriminant = transform$1(node.discriminant);
|
|
14974
|
+
const matches = [];
|
|
14975
|
+
let fallbackExpr = null;
|
|
14976
|
+
let fallbackSrc = null;
|
|
14977
|
+
|
|
14978
|
+
for (const switchCase of node.cases) {
|
|
14979
|
+
const consequent = switchCase.consequent;
|
|
14980
|
+
const entries =
|
|
14981
|
+
consequent.length === 1 && consequent[0].type === "BlockStatement" ?
|
|
14982
|
+
consequent[0].body :
|
|
14983
|
+
consequent;
|
|
14984
|
+
const parts = blockToParts(entries, switchCase.test == null ? "@default" : "@case");
|
|
14985
|
+
const renderExpr = rendersToExpression(parts.renders, switchCase);
|
|
14986
|
+
const caseExpr =
|
|
14987
|
+
parts.setup.length > 0 ?
|
|
14988
|
+
renderExpr ?
|
|
14989
|
+
iife([...parts.setup, returnStatement(renderExpr, switchCase)], switchCase) :
|
|
14990
|
+
fail$2(
|
|
14991
|
+
"A TSRX @case block with setup statements must end with rendered output",
|
|
14992
|
+
switchCase
|
|
14993
|
+
) :
|
|
14994
|
+
renderExpr;
|
|
14995
|
+
|
|
14996
|
+
if (switchCase.test == null) {
|
|
14997
|
+
fallbackExpr = caseExpr;
|
|
14998
|
+
fallbackSrc = switchCase;
|
|
14999
|
+
} else {
|
|
15000
|
+
const when = strictEquals(
|
|
15001
|
+
cloneNode$1(discriminant),
|
|
15002
|
+
transform$1(switchCase.test),
|
|
15003
|
+
switchCase
|
|
15004
|
+
);
|
|
15005
|
+
matches.push(
|
|
15006
|
+
jsxElement(
|
|
15007
|
+
"Match",
|
|
15008
|
+
[jsxAttr("when", when, switchCase)],
|
|
15009
|
+
caseExpr ? [asChild(caseExpr)] : [],
|
|
15010
|
+
switchCase
|
|
15011
|
+
)
|
|
15012
|
+
);
|
|
15013
|
+
}
|
|
15014
|
+
}
|
|
15015
|
+
|
|
15016
|
+
const attributes = fallbackExpr ? [jsxAttr("fallback", fallbackExpr, fallbackSrc)] : [];
|
|
15017
|
+
return jsxElement("Switch", attributes, matches, node);
|
|
15018
|
+
}
|
|
15019
|
+
|
|
15020
|
+
// ---------------------------------------------------------------------------
|
|
15021
|
+
// @try / @pending / @catch — Errored / Loading
|
|
15022
|
+
// ---------------------------------------------------------------------------
|
|
15023
|
+
|
|
15024
|
+
function tryToJsx(node) {
|
|
15025
|
+
if (isNode$3(node.finalizer)) {
|
|
15026
|
+
fail$2("@finally is not part of the TSRX template grammar", node.finalizer);
|
|
15027
|
+
}
|
|
15028
|
+
|
|
15029
|
+
const contentExpr = blockToExpression(node.block, "@try");
|
|
15030
|
+
if (!contentExpr) fail$2("A TSRX @try block must end with rendered output", node);
|
|
15031
|
+
|
|
15032
|
+
let result = contentExpr;
|
|
15033
|
+
|
|
15034
|
+
if (isNode$3(node.pending)) {
|
|
15035
|
+
const pendingExpr = blockToExpression(node.pending, "@pending");
|
|
15036
|
+
const attributes = pendingExpr ? [jsxAttr("fallback", pendingExpr, node.pending)] : [];
|
|
15037
|
+
result = jsxElement("Loading", attributes, [asChild(result)], node);
|
|
15038
|
+
}
|
|
15039
|
+
|
|
15040
|
+
if (isNode$3(node.handler)) {
|
|
15041
|
+
const handler = node.handler;
|
|
15042
|
+
const param = isNode$3(handler.param) ? handler.param : null;
|
|
15043
|
+
if (
|
|
15044
|
+
param &&
|
|
15045
|
+
param.type !== "Identifier" &&
|
|
15046
|
+
param.type !== "ObjectPattern" &&
|
|
15047
|
+
param.type !== "ArrayPattern")
|
|
15048
|
+
{
|
|
15049
|
+
fail$2(
|
|
15050
|
+
"The @catch error binding must be an identifier, object pattern, or array pattern",
|
|
15051
|
+
param
|
|
15052
|
+
);
|
|
15053
|
+
}
|
|
15054
|
+
const resetParam = isNode$3(handler.resetParam) ? handler.resetParam : null;
|
|
15055
|
+
const errorName = param ? param.name : "_e";
|
|
15056
|
+
|
|
15057
|
+
const handlerExpr = blockToExpression(handler.body, "@catch");
|
|
15058
|
+
if (!handlerExpr) fail$2("A TSRX @catch block must end with rendered output", handler);
|
|
15059
|
+
// RC `Errored` passes an `ErrorAccessor`: reads of the binding become calls.
|
|
15060
|
+
if (param) rewriteReadsToCalls(handlerExpr, errorName);
|
|
15061
|
+
|
|
15062
|
+
const params = [
|
|
15063
|
+
param && param.type !== "Identifier" ? accessorLazyPattern(param) : ident$1(errorName, param)];
|
|
15064
|
+
|
|
15065
|
+
if (resetParam)
|
|
15066
|
+
params.push(ident$1(resetParam.name, resetParam));
|
|
15067
|
+
|
|
15068
|
+
result = jsxElement(
|
|
15069
|
+
"Errored",
|
|
15070
|
+
[jsxAttr("fallback", arrow(params, handlerExpr, handler), handler)],
|
|
15071
|
+
[asChild(result)],
|
|
15072
|
+
node
|
|
15073
|
+
);
|
|
15074
|
+
}
|
|
15075
|
+
|
|
15076
|
+
return result;
|
|
15077
|
+
}
|
|
15078
|
+
|
|
15079
|
+
// ---------------------------------------------------------------------------
|
|
15080
|
+
// Lazy-engine adaptation: intrinsic element names
|
|
15081
|
+
// ---------------------------------------------------------------------------
|
|
15082
|
+
|
|
15083
|
+
/**
|
|
15084
|
+
* Older lazy-transform paths rewrote *any* JSX name matching a lazy binding —
|
|
15085
|
+
* including lowercase intrinsic tags (`<address>` with a lazy `address` in
|
|
15086
|
+
* scope became `<__lazy0.address>`), which hijacked the element into a
|
|
15087
|
+
* component. Per JSX semantics a single lowercase identifier tag is always
|
|
15088
|
+
* intrinsic, so such rewrites are unambiguously wrong. Keep this repair for
|
|
15089
|
+
* compatibility with trees produced by those paths; the local engine does not
|
|
15090
|
+
* produce the invalid rewrite.
|
|
15091
|
+
*/
|
|
15092
|
+
function restoreIntrinsicJsxNames(root) {
|
|
15093
|
+
visit(root);
|
|
15094
|
+
|
|
15095
|
+
function restore(owner) {
|
|
15096
|
+
const name = owner.name;
|
|
15097
|
+
if (!name || name.type !== "JSXMemberExpression") return;
|
|
15098
|
+
const object = name.object;
|
|
15099
|
+
const property = name.property;
|
|
15100
|
+
if (
|
|
15101
|
+
object.type === "JSXIdentifier" &&
|
|
15102
|
+
/^__lazy\d+$/.test(object.name) &&
|
|
15103
|
+
property.type === "JSXIdentifier" &&
|
|
15104
|
+
/^[a-z]/.test(property.name))
|
|
15105
|
+
{
|
|
15106
|
+
owner.name = jsxIdent(property.name, name);
|
|
15107
|
+
}
|
|
15108
|
+
}
|
|
15109
|
+
|
|
15110
|
+
function visit(node) {
|
|
15111
|
+
if (node.type === "JSXOpeningElement" || node.type === "JSXClosingElement") restore(node);
|
|
15112
|
+
for (const key of Object.keys(node)) {
|
|
15113
|
+
if (SKIP_KEYS$2.has(key)) continue;
|
|
15114
|
+
const value = node[key];
|
|
15115
|
+
if (Array.isArray(value)) {
|
|
15116
|
+
for (const item of value) if (isNode$3(item)) visit(item);
|
|
15117
|
+
} else if (isNode$3(value)) {
|
|
15118
|
+
visit(value);
|
|
15119
|
+
}
|
|
15120
|
+
}
|
|
15121
|
+
}
|
|
15122
|
+
}
|
|
15123
|
+
|
|
15124
|
+
// ---------------------------------------------------------------------------
|
|
15125
|
+
// Scope-aware read → call rewriting (accessor adaptations)
|
|
15126
|
+
// ---------------------------------------------------------------------------
|
|
15127
|
+
|
|
15128
|
+
/** Keys whose contents are TS type positions — never value references. */
|
|
15129
|
+
const TYPE_KEYS$1 = new Set([
|
|
15130
|
+
"typeAnnotation",
|
|
15131
|
+
"typeParameters",
|
|
15132
|
+
"typeArguments",
|
|
15133
|
+
"superTypeArguments",
|
|
15134
|
+
"returnType"]
|
|
15135
|
+
);
|
|
15136
|
+
|
|
15137
|
+
function patternNames$1(pattern, into) {
|
|
15138
|
+
switch (pattern.type) {
|
|
15139
|
+
case "Identifier":
|
|
15140
|
+
into.add(pattern.name);
|
|
15141
|
+
break;
|
|
15142
|
+
case "ObjectPattern":
|
|
15143
|
+
for (const prop of pattern.properties) {
|
|
15144
|
+
if (prop.type === "RestElement") patternNames$1(prop.argument, into);else
|
|
15145
|
+
patternNames$1(prop.value, into);
|
|
15146
|
+
}
|
|
15147
|
+
break;
|
|
15148
|
+
case "ArrayPattern":
|
|
15149
|
+
for (const el of pattern.elements) {
|
|
15150
|
+
if (el) patternNames$1(el, into);
|
|
15151
|
+
}
|
|
15152
|
+
break;
|
|
15153
|
+
case "AssignmentPattern":
|
|
15154
|
+
patternNames$1(pattern.left, into);
|
|
15155
|
+
break;
|
|
15156
|
+
case "RestElement":
|
|
15157
|
+
patternNames$1(pattern.argument, into);
|
|
15158
|
+
break;
|
|
15159
|
+
}
|
|
15160
|
+
}
|
|
15161
|
+
|
|
15162
|
+
function statementsShadow(statements, name) {
|
|
15163
|
+
const names = new Set();
|
|
15164
|
+
for (const stmt of statements) {
|
|
15165
|
+
if (stmt.type === "VariableDeclaration") {
|
|
15166
|
+
for (const decl of stmt.declarations) patternNames$1(decl.id, names);
|
|
15167
|
+
} else if (
|
|
15168
|
+
(stmt.type === "FunctionDeclaration" || stmt.type === "ClassDeclaration") &&
|
|
15169
|
+
isNode$3(stmt.id))
|
|
15170
|
+
{
|
|
15171
|
+
names.add(stmt.id.name);
|
|
15172
|
+
}
|
|
15173
|
+
}
|
|
15174
|
+
return names.has(name);
|
|
15175
|
+
}
|
|
15176
|
+
|
|
15177
|
+
/**
|
|
15178
|
+
* Rewrite value reads of `name` within `root` (a plain, already-desugared
|
|
15179
|
+
* subtree) to zero-argument calls, respecting shadowing by function params,
|
|
15180
|
+
* block declarations, and catch clauses. Write targets are left untouched.
|
|
15181
|
+
*/
|
|
15182
|
+
function rewriteReadsToCalls(root, name) {
|
|
15183
|
+
visit(root);
|
|
15184
|
+
|
|
15185
|
+
function shouldReplace(value) {
|
|
15186
|
+
return isNode$3(value) && value.type === "Identifier" && value.name === name;
|
|
15187
|
+
}
|
|
15188
|
+
|
|
15189
|
+
function replaceIn(container, key) {
|
|
15190
|
+
const value = container[key];
|
|
15191
|
+
container[key] = callOn(value, value);
|
|
15192
|
+
}
|
|
15193
|
+
|
|
15194
|
+
function visit(node) {
|
|
15195
|
+
if (FUNCTION_TYPES$1.has(node.type)) {
|
|
15196
|
+
const bound = new Set();
|
|
15197
|
+
for (const param of node.params) patternNames$1(param, bound);
|
|
15198
|
+
if (bound.has(name)) return;
|
|
15199
|
+
if (isNode$3(node.body)) visit(node.body);
|
|
15200
|
+
return;
|
|
15201
|
+
}
|
|
15202
|
+
if (node.type === "BlockStatement" || node.type === "Program") {
|
|
15203
|
+
if (statementsShadow(node.body, name)) return;
|
|
15204
|
+
}
|
|
15205
|
+
if (node.type === "CatchClause") {
|
|
15206
|
+
const bound = new Set();
|
|
15207
|
+
if (isNode$3(node.param)) patternNames$1(node.param, bound);
|
|
15208
|
+
if (bound.has(name)) return;
|
|
15209
|
+
}
|
|
15210
|
+
if (node.type === "MetaProperty") return;
|
|
15211
|
+
|
|
15212
|
+
for (const key of Object.keys(node)) {
|
|
15213
|
+
if (SKIP_KEYS$2.has(key) || TYPE_KEYS$1.has(key)) continue;
|
|
15214
|
+
// Labels, binding sites, and non-computed accesses are not value reads.
|
|
15215
|
+
if (key === "property" && node.type === "MemberExpression" && !node.computed) continue;
|
|
15216
|
+
if (key === "property" && node.type === "JSXMemberExpression") continue;
|
|
15217
|
+
if (key === "name" && (node.type === "JSXAttribute" || node.type === "JSXNamespacedName"))
|
|
15218
|
+
continue;
|
|
15219
|
+
if (key === "key" && node.type === "Property" && !node.computed) continue;
|
|
15220
|
+
if (key === "id" && (node.type === "VariableDeclarator" || FUNCTION_TYPES$1.has(node.type)))
|
|
15221
|
+
continue;
|
|
15222
|
+
if (key === "label") continue;
|
|
15223
|
+
if (key === "left" && node.type === "AssignmentExpression") {
|
|
15224
|
+
// Writes to the accessor binding are invalid anyway; skip identifier
|
|
15225
|
+
// targets but still walk member-expression targets.
|
|
15226
|
+
if (shouldReplace(node.left)) continue;
|
|
15227
|
+
}
|
|
15228
|
+
if (key === "argument" && node.type === "UpdateExpression" && shouldReplace(node.argument))
|
|
15229
|
+
continue;
|
|
15230
|
+
if (
|
|
15231
|
+
(key === "imported" || key === "local" || key === "exported") &&
|
|
15232
|
+
/Specifier$/.test(node.type))
|
|
15233
|
+
|
|
15234
|
+
continue;
|
|
15235
|
+
|
|
15236
|
+
const value = node[key];
|
|
15237
|
+
if (Array.isArray(value)) {
|
|
15238
|
+
for (let i = 0; i < value.length; i++) {
|
|
15239
|
+
const item = value[i];
|
|
15240
|
+
if (shouldReplace(item)) replaceIn(value, i);else
|
|
15241
|
+
if (isNode$3(item)) visit(item);
|
|
15242
|
+
}
|
|
15243
|
+
} else if (shouldReplace(value)) {
|
|
15244
|
+
// Object-pattern shorthand shares the key/value node: split them.
|
|
15245
|
+
if (node.type === "Property" && node.shorthand && key === "value") {
|
|
15246
|
+
node.shorthand = false;
|
|
15247
|
+
node.key = ident$1(name, value);
|
|
15248
|
+
}
|
|
15249
|
+
replaceIn(node, key);
|
|
15250
|
+
} else if (isNode$3(value)) {
|
|
15251
|
+
visit(value);
|
|
15252
|
+
}
|
|
15253
|
+
}
|
|
15254
|
+
}
|
|
15255
|
+
}
|
|
15256
|
+
|
|
15257
|
+
/**
|
|
15258
|
+
* ESTree (acorn / typescript-eslint shaped, as produced by `@tsrx/core`) to
|
|
15259
|
+
* Babel AST conversion.
|
|
15260
|
+
*
|
|
15261
|
+
* Purely mechanical: no TSRX node may reach this pass (the desugarer lowers
|
|
15262
|
+
* them all first), and unknown shapes fail closed rather than producing a
|
|
15263
|
+
* silently wrong tree. Location info (`start`/`end`/`loc`) is preserved for
|
|
15264
|
+
* sourcemaps. Comments are not attached in v1 (documented limitation).
|
|
15265
|
+
*/
|
|
15266
|
+
|
|
15267
|
+
|
|
15268
|
+
|
|
15269
|
+
|
|
15270
|
+
/** ESTree/TSRX bookkeeping keys with no Babel equivalent. */
|
|
15271
|
+
const STRIP_KEYS = new Set([
|
|
15272
|
+
"type",
|
|
15273
|
+
"start",
|
|
15274
|
+
"end",
|
|
15275
|
+
"loc",
|
|
15276
|
+
"range",
|
|
15277
|
+
"metadata",
|
|
15278
|
+
"leadingComments",
|
|
15279
|
+
"trailingComments",
|
|
15280
|
+
"innerComments",
|
|
15281
|
+
"alternateKeyword",
|
|
15282
|
+
"emptyKeyword",
|
|
15283
|
+
"isDynamic",
|
|
15284
|
+
"statementType",
|
|
15285
|
+
"resetParam",
|
|
15286
|
+
"lazy",
|
|
15287
|
+
"directive"]
|
|
15288
|
+
);
|
|
15289
|
+
|
|
15290
|
+
function isNode$2(value) {
|
|
15291
|
+
return (
|
|
15292
|
+
typeof value === "object" &&
|
|
15293
|
+
value !== null &&
|
|
15294
|
+
typeof value.type === "string");
|
|
15295
|
+
|
|
15296
|
+
}
|
|
15297
|
+
|
|
15298
|
+
function fail$1(message, node) {
|
|
15299
|
+
const start = node.loc?.start;
|
|
15300
|
+
throw new SyntaxError(start ? `${message} (${start.line}:${start.column})` : message);
|
|
15301
|
+
}
|
|
15302
|
+
|
|
15303
|
+
function copyLoc(from, to) {
|
|
15304
|
+
if (from.start !== undefined) to.start = from.start;
|
|
15305
|
+
if (from.end !== undefined) to.end = from.end;
|
|
15306
|
+
if (from.loc !== undefined) to.loc = from.loc;
|
|
15307
|
+
}
|
|
15308
|
+
|
|
15309
|
+
function convertValue(value) {
|
|
15310
|
+
if (Array.isArray(value)) return value.map(convertValue);
|
|
15311
|
+
if (isNode$2(value)) return convertNode(value);
|
|
15312
|
+
return value;
|
|
15313
|
+
}
|
|
15314
|
+
|
|
15315
|
+
/** Shallow-map every kept key, renaming typescript-eslint's `typeArguments`
|
|
15316
|
+
* to Babel 7's `typeParameters` along the way. */
|
|
15317
|
+
function convertGeneric(node, overrideType) {
|
|
15318
|
+
const out = { type: overrideType ?? node.type };
|
|
15319
|
+
copyLoc(node, out);
|
|
15320
|
+
for (const key of Object.keys(node)) {
|
|
15321
|
+
if (STRIP_KEYS.has(key)) continue;
|
|
15322
|
+
const outKey =
|
|
15323
|
+
key === "typeArguments" && node.typeParameters === undefined ? "typeParameters" : key;
|
|
15324
|
+
out[outKey] = convertValue(node[key]);
|
|
15325
|
+
}
|
|
15326
|
+
return out;
|
|
15327
|
+
}
|
|
15328
|
+
|
|
15329
|
+
function convertLiteral(node) {
|
|
15330
|
+
const out = {};
|
|
15331
|
+
copyLoc(node, out);
|
|
15332
|
+
const regex = node.regex;
|
|
15333
|
+
if (regex) {
|
|
15334
|
+
out.type = "RegExpLiteral";
|
|
15335
|
+
out.pattern = regex.pattern;
|
|
15336
|
+
out.flags = regex.flags;
|
|
15337
|
+
return out;
|
|
15338
|
+
}
|
|
15339
|
+
if (node.bigint !== undefined && node.bigint !== null) {
|
|
15340
|
+
out.type = "BigIntLiteral";
|
|
15341
|
+
out.value = node.bigint;
|
|
15342
|
+
return out;
|
|
15343
|
+
}
|
|
15344
|
+
const value = node.value;
|
|
15345
|
+
if (value === null) {
|
|
15346
|
+
out.type = "NullLiteral";
|
|
15347
|
+
return out;
|
|
15348
|
+
}
|
|
15349
|
+
switch (typeof value) {
|
|
15350
|
+
case "string":
|
|
15351
|
+
out.type = "StringLiteral";
|
|
15352
|
+
break;
|
|
15353
|
+
case "number":
|
|
15354
|
+
out.type = "NumericLiteral";
|
|
15355
|
+
break;
|
|
15356
|
+
case "boolean":
|
|
15357
|
+
out.type = "BooleanLiteral";
|
|
15358
|
+
break;
|
|
15359
|
+
default:
|
|
15360
|
+
return fail$1(`Unsupported literal value type: ${typeof value}`, node);
|
|
15361
|
+
}
|
|
15362
|
+
out.value = value;
|
|
15363
|
+
if (typeof node.raw === "string") out.extra = { raw: node.raw, rawValue: value };
|
|
15364
|
+
return out;
|
|
15365
|
+
}
|
|
15366
|
+
|
|
15367
|
+
function extractDirectives(body) {
|
|
15368
|
+
const directives = [];
|
|
15369
|
+
let i = 0;
|
|
15370
|
+
while (i < body.length) {
|
|
15371
|
+
const stmt = body[i];
|
|
15372
|
+
if (stmt.type !== "ExpressionStatement" || typeof stmt.directive !== "string") break;
|
|
15373
|
+
const expr = stmt.expression;
|
|
15374
|
+
const literal = {
|
|
15375
|
+
type: "DirectiveLiteral",
|
|
15376
|
+
value: expr.value
|
|
15377
|
+
};
|
|
15378
|
+
copyLoc(expr, literal);
|
|
15379
|
+
if (typeof expr.raw === "string") literal.extra = { raw: expr.raw, rawValue: expr.value };
|
|
15380
|
+
const directive = { type: "Directive", value: literal };
|
|
15381
|
+
copyLoc(stmt, directive);
|
|
15382
|
+
directives.push(directive);
|
|
15383
|
+
i++;
|
|
15384
|
+
}
|
|
15385
|
+
return { directives, rest: body.slice(i) };
|
|
15386
|
+
}
|
|
15387
|
+
|
|
15388
|
+
function convertFunctionParts(fn, out) {
|
|
15389
|
+
out.params = convertValue(fn.params);
|
|
15390
|
+
out.body = convertValue(fn.body);
|
|
15391
|
+
out.generator = !!fn.generator;
|
|
15392
|
+
out.async = !!fn.async;
|
|
15393
|
+
if (fn.returnType !== undefined) out.returnType = convertValue(fn.returnType);
|
|
15394
|
+
if (fn.typeParameters !== undefined) out.typeParameters = convertValue(fn.typeParameters);
|
|
15395
|
+
}
|
|
15396
|
+
|
|
15397
|
+
/** Convert an optional chain, dropping the ESTree `ChainExpression` wrapper
|
|
15398
|
+
* and switching member/call links to Babel's `Optional*` node types. */
|
|
15399
|
+
function convertChainLink(node) {
|
|
15400
|
+
switch (node.type) {
|
|
15401
|
+
case "MemberExpression":{
|
|
15402
|
+
const out = {
|
|
15403
|
+
type: "OptionalMemberExpression",
|
|
15404
|
+
object: convertChainLink(node.object),
|
|
15405
|
+
property: convertValue(node.property),
|
|
15406
|
+
computed: !!node.computed,
|
|
15407
|
+
optional: !!node.optional
|
|
15408
|
+
};
|
|
15409
|
+
copyLoc(node, out);
|
|
15410
|
+
return out;
|
|
15411
|
+
}
|
|
15412
|
+
case "CallExpression":{
|
|
15413
|
+
const out = {
|
|
15414
|
+
type: "OptionalCallExpression",
|
|
15415
|
+
callee: convertChainLink(node.callee),
|
|
15416
|
+
arguments: convertValue(node.arguments),
|
|
15417
|
+
optional: !!node.optional
|
|
15418
|
+
};
|
|
15419
|
+
copyLoc(node, out);
|
|
15420
|
+
if (node.typeParameters !== undefined || node.typeArguments !== undefined) {
|
|
15421
|
+
out.typeParameters = convertValue(node.typeParameters ?? node.typeArguments);
|
|
15422
|
+
}
|
|
15423
|
+
return out;
|
|
15424
|
+
}
|
|
15425
|
+
case "TSNonNullExpression":{
|
|
15426
|
+
const out = {
|
|
15427
|
+
type: "TSNonNullExpression",
|
|
15428
|
+
expression: convertChainLink(node.expression)
|
|
15429
|
+
};
|
|
15430
|
+
copyLoc(node, out);
|
|
15431
|
+
return out;
|
|
15432
|
+
}
|
|
15433
|
+
default:
|
|
15434
|
+
return convertNode(node);
|
|
15435
|
+
}
|
|
15436
|
+
}
|
|
15437
|
+
|
|
15438
|
+
function convertNode(node) {
|
|
15439
|
+
switch (node.type) {
|
|
15440
|
+
case "Program":{
|
|
15441
|
+
const { directives, rest } = extractDirectives(node.body);
|
|
15442
|
+
const out = {
|
|
15443
|
+
type: "Program",
|
|
15444
|
+
sourceType: node.sourceType ?? "module",
|
|
15445
|
+
interpreter: null,
|
|
15446
|
+
body: rest.map(convertNode),
|
|
15447
|
+
directives
|
|
15448
|
+
};
|
|
15449
|
+
copyLoc(node, out);
|
|
15450
|
+
return out;
|
|
15451
|
+
}
|
|
15452
|
+
|
|
15453
|
+
case "BlockStatement":{
|
|
15454
|
+
const { directives, rest } = extractDirectives(node.body);
|
|
15455
|
+
const out = {
|
|
15456
|
+
type: "BlockStatement",
|
|
15457
|
+
body: rest.map(convertNode),
|
|
15458
|
+
directives
|
|
15459
|
+
};
|
|
15460
|
+
copyLoc(node, out);
|
|
15461
|
+
return out;
|
|
15462
|
+
}
|
|
15463
|
+
|
|
15464
|
+
case "Literal":
|
|
15465
|
+
return convertLiteral(node);
|
|
15466
|
+
|
|
15467
|
+
case "Property":{
|
|
15468
|
+
const kind = node.kind;
|
|
15469
|
+
if (kind === "get" || kind === "set" || node.method) {
|
|
15470
|
+
const fn = node.value;
|
|
15471
|
+
const out = {
|
|
15472
|
+
type: "ObjectMethod",
|
|
15473
|
+
kind: node.method ? "method" : kind,
|
|
15474
|
+
key: convertValue(node.key),
|
|
15475
|
+
computed: !!node.computed
|
|
15476
|
+
};
|
|
15477
|
+
copyLoc(node, out);
|
|
15478
|
+
convertFunctionParts(fn, out);
|
|
15479
|
+
return out;
|
|
15480
|
+
}
|
|
15481
|
+
const out = {
|
|
15482
|
+
type: "ObjectProperty",
|
|
15483
|
+
key: convertValue(node.key),
|
|
15484
|
+
value: convertValue(node.value),
|
|
15485
|
+
computed: !!node.computed,
|
|
15486
|
+
shorthand: !!node.shorthand
|
|
15487
|
+
};
|
|
15488
|
+
copyLoc(node, out);
|
|
15489
|
+
return out;
|
|
15490
|
+
}
|
|
15491
|
+
|
|
15492
|
+
case "MethodDefinition":{
|
|
15493
|
+
const fn = node.value;
|
|
15494
|
+
const isPrivate = node.key.type === "PrivateIdentifier";
|
|
15495
|
+
const out = {
|
|
15496
|
+
type: isPrivate ? "ClassPrivateMethod" : "ClassMethod",
|
|
15497
|
+
kind: node.kind,
|
|
15498
|
+
key: convertValue(node.key),
|
|
15499
|
+
computed: !!node.computed,
|
|
15500
|
+
static: !!node.static
|
|
15501
|
+
};
|
|
15502
|
+
copyLoc(node, out);
|
|
15503
|
+
convertFunctionParts(fn, out);
|
|
15504
|
+
if (node.accessibility !== undefined) out.accessibility = node.accessibility;
|
|
15505
|
+
if (node.override !== undefined) out.override = node.override;
|
|
15506
|
+
if (node.optional !== undefined) out.optional = node.optional;
|
|
15507
|
+
return out;
|
|
15508
|
+
}
|
|
15509
|
+
|
|
15510
|
+
case "PropertyDefinition":{
|
|
15511
|
+
const isPrivate = node.key.type === "PrivateIdentifier";
|
|
15512
|
+
const out = {
|
|
15513
|
+
type: isPrivate ? "ClassPrivateProperty" : "ClassProperty",
|
|
15514
|
+
key: convertValue(node.key),
|
|
15515
|
+
value: node.value == null ? null : convertValue(node.value),
|
|
15516
|
+
computed: !!node.computed,
|
|
15517
|
+
static: !!node.static
|
|
15518
|
+
};
|
|
15519
|
+
copyLoc(node, out);
|
|
15520
|
+
for (const key of [
|
|
15521
|
+
"typeAnnotation",
|
|
15522
|
+
"declare",
|
|
15523
|
+
"readonly",
|
|
15524
|
+
"accessibility",
|
|
15525
|
+
"optional",
|
|
15526
|
+
"definite",
|
|
15527
|
+
"override"])
|
|
15528
|
+
{
|
|
15529
|
+
if (node[key] !== undefined) out[key] = convertValue(node[key]);
|
|
15530
|
+
}
|
|
15531
|
+
return out;
|
|
15532
|
+
}
|
|
15533
|
+
|
|
15534
|
+
case "PrivateIdentifier":{
|
|
15535
|
+
const id = { type: "Identifier", name: node.name };
|
|
15536
|
+
copyLoc(node, id);
|
|
15537
|
+
const out = { type: "PrivateName", id };
|
|
15538
|
+
copyLoc(node, out);
|
|
15539
|
+
return out;
|
|
15540
|
+
}
|
|
15541
|
+
|
|
15542
|
+
case "ChainExpression":
|
|
15543
|
+
return convertChainLink(node.expression);
|
|
15544
|
+
|
|
15545
|
+
case "ImportExpression":{
|
|
15546
|
+
const importCallee = { type: "Import" };
|
|
15547
|
+
copyLoc(node, importCallee);
|
|
15548
|
+
const args = [convertValue(node.source)];
|
|
15549
|
+
if (node.options != null) args.push(convertValue(node.options));
|
|
15550
|
+
const out = {
|
|
15551
|
+
type: "CallExpression",
|
|
15552
|
+
callee: importCallee,
|
|
15553
|
+
arguments: args
|
|
15554
|
+
};
|
|
15555
|
+
copyLoc(node, out);
|
|
15556
|
+
return out;
|
|
15557
|
+
}
|
|
15558
|
+
|
|
15559
|
+
case "JSXText":{
|
|
15560
|
+
const out = { type: "JSXText", value: node.value };
|
|
15561
|
+
copyLoc(node, out);
|
|
15562
|
+
if (typeof node.raw === "string") out.extra = { raw: node.raw, rawValue: node.value };
|
|
15563
|
+
return out;
|
|
15564
|
+
}
|
|
15565
|
+
|
|
15566
|
+
case "TSTypeParameter":{
|
|
15567
|
+
const out = convertGeneric(node);
|
|
15568
|
+
if (isNode$2(out.name)) out.name = out.name.name;
|
|
15569
|
+
return out;
|
|
15570
|
+
}
|
|
15571
|
+
|
|
15572
|
+
case "TSInterfaceHeritage":
|
|
15573
|
+
return convertGeneric(node, "TSExpressionWithTypeArguments");
|
|
15574
|
+
|
|
15575
|
+
case "TSAbstractMethodDefinition":
|
|
15576
|
+
case "TSAbstractPropertyDefinition":
|
|
15577
|
+
case "TSParameterProperty":
|
|
15578
|
+
return fail$1(`Unsupported TypeScript construct in TSRX source: ${node.type}`, node);
|
|
15579
|
+
|
|
15580
|
+
case "JSXCodeBlock":
|
|
15581
|
+
case "JSXIfExpression":
|
|
15582
|
+
case "JSXForExpression":
|
|
15583
|
+
case "JSXSwitchExpression":
|
|
15584
|
+
case "JSXTryExpression":
|
|
15585
|
+
case "JSXStyleElement":
|
|
15586
|
+
return fail$1(
|
|
15587
|
+
`TSRX node ${node.type} survived desugaring; this is a bug in the frontend`,
|
|
15588
|
+
node
|
|
15589
|
+
);
|
|
15590
|
+
|
|
15591
|
+
default:
|
|
15592
|
+
return convertGeneric(node);
|
|
15593
|
+
}
|
|
15594
|
+
}
|
|
15595
|
+
|
|
15596
|
+
function toBabelFile(program) {
|
|
15597
|
+
const file = {
|
|
15598
|
+
type: "File",
|
|
15599
|
+
program: convertNode(program),
|
|
15600
|
+
comments: [],
|
|
15601
|
+
tokens: []
|
|
15602
|
+
};
|
|
15603
|
+
copyLoc(program, file);
|
|
15604
|
+
return file;
|
|
15605
|
+
}
|
|
15606
|
+
|
|
15607
|
+
/**
|
|
15608
|
+
* Solid's TSRX lazy-destructuring transform.
|
|
15609
|
+
*
|
|
15610
|
+
* This is intentionally local to the Babel frontend. `@tsrx/core` still owns
|
|
15611
|
+
* parsing and semantic analysis, but its framework-agnostic lazy transform
|
|
15612
|
+
* cannot express Solid's reactive object-rest view or JavaScript-correct lazy
|
|
15613
|
+
* defaults and updates.
|
|
15614
|
+
*
|
|
15615
|
+
* The scope and pattern traversal is adapted from @tsrx/core 0.1.61's
|
|
15616
|
+
* MIT-licensed `src/transform/lazy.js`.
|
|
15617
|
+
*/
|
|
15618
|
+
|
|
15619
|
+
|
|
15620
|
+
|
|
15621
|
+
|
|
15622
|
+
|
|
15623
|
+
|
|
15624
|
+
|
|
15625
|
+
|
|
15626
|
+
|
|
15627
|
+
|
|
15628
|
+
|
|
15629
|
+
|
|
15630
|
+
|
|
15631
|
+
|
|
15632
|
+
|
|
15633
|
+
|
|
15634
|
+
|
|
15635
|
+
|
|
15636
|
+
|
|
15637
|
+
|
|
15638
|
+
|
|
15639
|
+
|
|
15640
|
+
|
|
15641
|
+
|
|
15642
|
+
|
|
15643
|
+
|
|
15644
|
+
|
|
15645
|
+
|
|
15646
|
+
|
|
15647
|
+
|
|
15648
|
+
|
|
15649
|
+
|
|
15650
|
+
const FUNCTION_TYPES = new Set([
|
|
15651
|
+
"FunctionDeclaration",
|
|
15652
|
+
"FunctionExpression",
|
|
15653
|
+
"ArrowFunctionExpression"]
|
|
15654
|
+
);
|
|
15655
|
+
|
|
15656
|
+
const TYPE_KEYS = new Set([
|
|
15657
|
+
"typeAnnotation",
|
|
15658
|
+
"typeParameters",
|
|
15659
|
+
"typeArguments",
|
|
15660
|
+
"superTypeArguments",
|
|
15661
|
+
"returnType",
|
|
15662
|
+
"implements"]
|
|
15663
|
+
);
|
|
15664
|
+
|
|
15665
|
+
const SKIP_KEYS$1 = new Set([
|
|
15666
|
+
"type",
|
|
15667
|
+
"loc",
|
|
15668
|
+
"start",
|
|
15669
|
+
"end",
|
|
15670
|
+
"range",
|
|
15671
|
+
"metadata",
|
|
15672
|
+
"leadingComments",
|
|
15673
|
+
"trailingComments",
|
|
15674
|
+
"innerComments"]
|
|
15675
|
+
);
|
|
15676
|
+
|
|
15677
|
+
function isNode$1(value) {
|
|
15678
|
+
return (
|
|
15679
|
+
typeof value === "object" &&
|
|
15680
|
+
value !== null &&
|
|
15681
|
+
typeof value.type === "string");
|
|
15682
|
+
|
|
15683
|
+
}
|
|
15684
|
+
|
|
15685
|
+
function metadata(node) {
|
|
15686
|
+
return node.metadata ??= {};
|
|
15687
|
+
}
|
|
15688
|
+
|
|
15689
|
+
function withLoc$1(node, source) {
|
|
15690
|
+
if (source) {
|
|
15691
|
+
if (source.start !== undefined) node.start = source.start;
|
|
15692
|
+
if (source.end !== undefined) node.end = source.end;
|
|
15693
|
+
if (source.loc !== undefined) node.loc = source.loc;
|
|
15694
|
+
}
|
|
15695
|
+
return node;
|
|
15696
|
+
}
|
|
15697
|
+
|
|
15698
|
+
function cloneNode(value) {
|
|
15699
|
+
if (Array.isArray(value)) return value.map(cloneNode);
|
|
15700
|
+
if (typeof value === "object" && value !== null) {
|
|
15701
|
+
const clone = {};
|
|
15702
|
+
for (const key of Object.keys(value)) {
|
|
15703
|
+
clone[key] = cloneNode(value[key]);
|
|
15704
|
+
}
|
|
15705
|
+
return clone;
|
|
15706
|
+
}
|
|
15707
|
+
return value;
|
|
15708
|
+
}
|
|
15709
|
+
|
|
15710
|
+
function ident(name, source) {
|
|
15711
|
+
return withLoc$1({ type: "Identifier", name }, source);
|
|
15712
|
+
}
|
|
15713
|
+
|
|
15714
|
+
function literal(value, source) {
|
|
15715
|
+
return withLoc$1({ type: "Literal", value, raw: JSON.stringify(value) }, source);
|
|
15716
|
+
}
|
|
15717
|
+
|
|
15718
|
+
function undefinedExpression(source) {
|
|
15719
|
+
return withLoc$1(
|
|
15720
|
+
{ type: "UnaryExpression", operator: "void", prefix: true, argument: literal(0, source) },
|
|
15721
|
+
source
|
|
15722
|
+
);
|
|
15723
|
+
}
|
|
15724
|
+
|
|
15725
|
+
function member(object, property, computed, source) {
|
|
15726
|
+
return withLoc$1({ type: "MemberExpression", object, property, computed, optional: false }, source);
|
|
15727
|
+
}
|
|
15728
|
+
|
|
15729
|
+
function call(callee, args, source) {
|
|
15730
|
+
return withLoc$1({ type: "CallExpression", callee, arguments: args, optional: false }, source);
|
|
15731
|
+
}
|
|
15732
|
+
|
|
15733
|
+
function assignment(operator, left, right, source) {
|
|
15734
|
+
return withLoc$1({ type: "AssignmentExpression", operator, left, right }, source);
|
|
15735
|
+
}
|
|
15736
|
+
|
|
15737
|
+
function sequence(expressions, source) {
|
|
15738
|
+
return withLoc$1({ type: "SequenceExpression", expressions }, source);
|
|
15739
|
+
}
|
|
15740
|
+
|
|
15741
|
+
function conditional(test, consequent, alternate, source) {
|
|
15742
|
+
return withLoc$1({ type: "ConditionalExpression", test, consequent, alternate }, source);
|
|
15743
|
+
}
|
|
15744
|
+
|
|
15745
|
+
function binary(operator, left, right, source) {
|
|
15746
|
+
return withLoc$1({ type: "BinaryExpression", operator, left, right }, source);
|
|
15747
|
+
}
|
|
15748
|
+
|
|
15749
|
+
function logical(operator, left, right, source) {
|
|
15750
|
+
return withLoc$1({ type: "LogicalExpression", operator, left, right }, source);
|
|
15751
|
+
}
|
|
15752
|
+
|
|
15753
|
+
function variableDeclaration(kind, names, init) {
|
|
15754
|
+
return {
|
|
15755
|
+
type: "VariableDeclaration",
|
|
15756
|
+
kind,
|
|
15757
|
+
declarations: names.map((name, index) => ({
|
|
15758
|
+
type: "VariableDeclarator",
|
|
15759
|
+
id: ident(name),
|
|
15760
|
+
init: index === 0 && init ? init : null
|
|
15761
|
+
}))
|
|
15762
|
+
};
|
|
15763
|
+
}
|
|
15764
|
+
|
|
15765
|
+
function fail(message, node) {
|
|
15766
|
+
const start = node?.loc?.start;
|
|
15767
|
+
throw new SyntaxError(start ? `${message} (${start.line}:${start.column})` : message);
|
|
15768
|
+
}
|
|
15769
|
+
|
|
15770
|
+
const AUTHORED_LAZY_UNSUPPORTED =
|
|
15771
|
+
"Solid's TSRX frontend does not support authored lazy destructuring; keep property and accessor reads explicit";
|
|
15772
|
+
|
|
15773
|
+
/**
|
|
15774
|
+
* Reject parser-authored `&{}` / `&[]` before TSRX controls synthesize the
|
|
15775
|
+
* deferred patterns Solid needs for accessor-backed `@for` and `@catch`
|
|
15776
|
+
* callbacks.
|
|
15777
|
+
*/
|
|
15778
|
+
function rejectAuthoredLazyDestructuring(root) {
|
|
15779
|
+
const visit = (value) => {
|
|
15780
|
+
if (Array.isArray(value)) {
|
|
15781
|
+
for (const child of value) visit(child);
|
|
15782
|
+
return;
|
|
15783
|
+
}
|
|
15784
|
+
if (!isNode$1(value)) return;
|
|
15785
|
+
if ((value.type === "ObjectPattern" || value.type === "ArrayPattern") && value.lazy === true) {
|
|
15786
|
+
fail(AUTHORED_LAZY_UNSUPPORTED, value);
|
|
15787
|
+
}
|
|
15788
|
+
for (const key of Object.keys(value)) {
|
|
15789
|
+
if (!SKIP_KEYS$1.has(key)) visit(value[key]);
|
|
15790
|
+
}
|
|
15791
|
+
};
|
|
15792
|
+
visit(root);
|
|
15793
|
+
}
|
|
15794
|
+
|
|
15795
|
+
class LazyState {
|
|
15796
|
+
names = new Set();
|
|
15797
|
+
next = new Map();
|
|
15798
|
+
programTemps = [];
|
|
15799
|
+
tempScopes = [this.programTemps];
|
|
15800
|
+
|
|
15801
|
+
|
|
15802
|
+
usesArrayRest = false;
|
|
15803
|
+
topLevelGlobalThisBinding = false;
|
|
15804
|
+
|
|
15805
|
+
constructor(root) {
|
|
15806
|
+
this.collectNames(root);
|
|
15807
|
+
this.topLevelGlobalThisBinding = programBindsName(root, "globalThis");
|
|
15808
|
+
}
|
|
15809
|
+
|
|
15810
|
+
collectNames(value) {
|
|
15811
|
+
if (Array.isArray(value)) {
|
|
15812
|
+
for (const child of value) this.collectNames(child);
|
|
15813
|
+
return;
|
|
15814
|
+
}
|
|
15815
|
+
if (!isNode$1(value)) return;
|
|
15816
|
+
if (value.type === "Identifier" || value.type === "JSXIdentifier") {
|
|
15817
|
+
this.names.add(value.name);
|
|
15818
|
+
}
|
|
15819
|
+
for (const key of Object.keys(value)) {
|
|
15820
|
+
if (!SKIP_KEYS$1.has(key)) this.collectNames(value[key]);
|
|
15821
|
+
}
|
|
15822
|
+
}
|
|
15823
|
+
|
|
15824
|
+
name(prefix) {
|
|
15825
|
+
let index = this.next.get(prefix) ?? 0;
|
|
15826
|
+
let name;
|
|
15827
|
+
do name = `${prefix}${index++}`; while (
|
|
15828
|
+
this.names.has(name));
|
|
15829
|
+
this.next.set(prefix, index);
|
|
15830
|
+
this.names.add(name);
|
|
15831
|
+
return name;
|
|
15832
|
+
}
|
|
15833
|
+
|
|
15834
|
+
temp() {
|
|
15835
|
+
const name = this.name("__lazyValue");
|
|
15836
|
+
this.tempScopes[this.tempScopes.length - 1].push(name);
|
|
15837
|
+
return name;
|
|
15838
|
+
}
|
|
15839
|
+
|
|
15840
|
+
pushFunctionTemps() {
|
|
15841
|
+
this.tempScopes.push([]);
|
|
15842
|
+
}
|
|
15843
|
+
|
|
15844
|
+
popFunctionTemps() {
|
|
15845
|
+
if (this.tempScopes.length === 1) {
|
|
15846
|
+
return fail("Internal TSRX lazy-transform temporary-scope imbalance");
|
|
15847
|
+
}
|
|
15848
|
+
return this.tempScopes.pop();
|
|
15849
|
+
}
|
|
15850
|
+
|
|
15851
|
+
defaultRead(
|
|
15852
|
+
raw,
|
|
15853
|
+
fallback,
|
|
15854
|
+
bindings,
|
|
15855
|
+
selfName,
|
|
15856
|
+
source)
|
|
15857
|
+
{
|
|
15858
|
+
const tempName = this.temp();
|
|
15859
|
+
const temp = () => ident(tempName, source);
|
|
15860
|
+
const fallbackBindings = new Map(bindings);
|
|
15861
|
+
fallbackBindings.delete(selfName);
|
|
15862
|
+
return sequence(
|
|
15863
|
+
[
|
|
15864
|
+
assignment("=", temp(), raw, source),
|
|
15865
|
+
conditional(
|
|
15866
|
+
binary("===", temp(), undefinedExpression(source), source),
|
|
15867
|
+
transform(cloneNode(fallback), fallbackBindings, this),
|
|
15868
|
+
temp(),
|
|
15869
|
+
source
|
|
15870
|
+
)],
|
|
15871
|
+
|
|
15872
|
+
source
|
|
15873
|
+
);
|
|
15874
|
+
}
|
|
15875
|
+
|
|
15876
|
+
omitIdentifier() {
|
|
15877
|
+
this.omitName ??= this.name("__lazyOmit");
|
|
15878
|
+
return ident(this.omitName);
|
|
15879
|
+
}
|
|
15880
|
+
|
|
15881
|
+
arrayIdentifier(node) {
|
|
15882
|
+
if (this.topLevelGlobalThisBinding) {
|
|
15883
|
+
fail(
|
|
15884
|
+
"TSRX lazy array rest cannot safely access the intrinsic Array because this module binds `globalThis`; rename that top-level binding",
|
|
15885
|
+
node
|
|
15886
|
+
);
|
|
15887
|
+
}
|
|
15888
|
+
this.usesArrayRest = true;
|
|
15889
|
+
this.arrayName ??= this.name("__lazyArray");
|
|
15890
|
+
return ident(this.arrayName, node);
|
|
15891
|
+
}
|
|
15892
|
+
|
|
15893
|
+
finalize(program) {
|
|
15894
|
+
if (program.type !== "Program") {
|
|
15895
|
+
if (this.omitName || this.usesArrayRest || this.programTemps.length) {
|
|
15896
|
+
return fail("The TSRX lazy transform requires a Program root", program);
|
|
15897
|
+
}
|
|
15898
|
+
return program;
|
|
15899
|
+
}
|
|
15900
|
+
|
|
15901
|
+
const generated = [];
|
|
15902
|
+
if (this.arrayName) {
|
|
15903
|
+
generated.push(
|
|
15904
|
+
variableDeclaration(
|
|
15905
|
+
"const",
|
|
15906
|
+
[this.arrayName],
|
|
15907
|
+
member(ident("globalThis"), ident("Array"), false)
|
|
15908
|
+
)
|
|
15909
|
+
);
|
|
15910
|
+
}
|
|
15911
|
+
if (this.programTemps.length) generated.push(variableDeclaration("let", this.programTemps));
|
|
15912
|
+
|
|
15913
|
+
const body = program.body;
|
|
15914
|
+
let directiveEnd = 0;
|
|
15915
|
+
while (
|
|
15916
|
+
directiveEnd < body.length &&
|
|
15917
|
+
body[directiveEnd].type === "ExpressionStatement" &&
|
|
15918
|
+
typeof body[directiveEnd].directive === "string")
|
|
15919
|
+
{
|
|
15920
|
+
directiveEnd++;
|
|
15921
|
+
}
|
|
15922
|
+
let importEnd = directiveEnd;
|
|
15923
|
+
while (importEnd < body.length && body[importEnd].type === "ImportDeclaration") importEnd++;
|
|
15924
|
+
|
|
15925
|
+
const additions = [];
|
|
15926
|
+
if (this.omitName) {
|
|
15927
|
+
additions.push({
|
|
15928
|
+
type: "ImportDeclaration",
|
|
15929
|
+
specifiers: [
|
|
15930
|
+
{
|
|
15931
|
+
type: "ImportSpecifier",
|
|
15932
|
+
imported: ident("omit"),
|
|
15933
|
+
local: ident(this.omitName)
|
|
15934
|
+
}],
|
|
15935
|
+
|
|
15936
|
+
source: literal("solid-js")
|
|
15937
|
+
});
|
|
15938
|
+
}
|
|
15939
|
+
additions.push(...generated);
|
|
15940
|
+
if (additions.length) {
|
|
15941
|
+
program.body = [...body.slice(0, importEnd), ...additions, ...body.slice(importEnd)];
|
|
15942
|
+
}
|
|
15943
|
+
return program;
|
|
15944
|
+
}
|
|
15945
|
+
}
|
|
15946
|
+
|
|
15947
|
+
function programBindsName(root, name) {
|
|
15948
|
+
if (root.type !== "Program") return false;
|
|
15949
|
+
for (const statement of root.body) {
|
|
15950
|
+
const declaration =
|
|
15951
|
+
(statement.type === "ExportNamedDeclaration" ||
|
|
15952
|
+
statement.type === "ExportDefaultDeclaration") &&
|
|
15953
|
+
isNode$1(statement.declaration) ?
|
|
15954
|
+
statement.declaration :
|
|
15955
|
+
statement;
|
|
15956
|
+
if (declaration.type === "VariableDeclaration") {
|
|
15957
|
+
for (const item of declaration.declarations) {
|
|
15958
|
+
const names = new Set();
|
|
15959
|
+
patternNames(item.id, names);
|
|
15960
|
+
if (names.has(name)) return true;
|
|
15961
|
+
}
|
|
15962
|
+
} else if (
|
|
15963
|
+
(declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") &&
|
|
15964
|
+
isNode$1(declaration.id) &&
|
|
15965
|
+
declaration.id.name === name)
|
|
15966
|
+
{
|
|
15967
|
+
return true;
|
|
15968
|
+
} else if (statement.type === "ImportDeclaration") {
|
|
15969
|
+
for (const specifier of statement.specifiers) {
|
|
15970
|
+
if (isNode$1(specifier.local) && specifier.local.name === name) return true;
|
|
15971
|
+
}
|
|
15972
|
+
}
|
|
15973
|
+
}
|
|
15974
|
+
return false;
|
|
15975
|
+
}
|
|
15976
|
+
|
|
15977
|
+
function assertWritable(binding, node) {
|
|
15978
|
+
if (binding.complexDefault) {
|
|
15979
|
+
fail(
|
|
15980
|
+
"A TSRX lazy binding nested beneath an ancestor default is read-only; assign to the source object explicitly",
|
|
15981
|
+
node
|
|
15982
|
+
);
|
|
15983
|
+
}
|
|
15984
|
+
}
|
|
15985
|
+
|
|
15986
|
+
function staticPropertyName(node) {
|
|
15987
|
+
if (node.type === "Identifier") return node.name;
|
|
15988
|
+
if (node.type === "Literal") return String(node.value);
|
|
15989
|
+
return null;
|
|
15990
|
+
}
|
|
15991
|
+
|
|
15992
|
+
function typePropertyKeys(typeAnnotation) {
|
|
15993
|
+
const keys = new Map();
|
|
15994
|
+
const type = typeAnnotation?.typeAnnotation;
|
|
15995
|
+
if (type?.type !== "TSTypeLiteral") return keys;
|
|
15996
|
+
for (const item of type.members ?? []) {
|
|
15997
|
+
if (item.type !== "TSPropertySignature" || !isNode$1(item.key)) continue;
|
|
15998
|
+
const name = staticPropertyName(item.key);
|
|
15999
|
+
if (name !== null && !keys.has(name)) keys.set(name, item.key);
|
|
16000
|
+
}
|
|
16001
|
+
return keys;
|
|
16002
|
+
}
|
|
16003
|
+
|
|
16004
|
+
function patternRange(pattern) {
|
|
16005
|
+
if (pattern.start === undefined || pattern.end === undefined || pattern.loc === undefined) {
|
|
16006
|
+
return null;
|
|
16007
|
+
}
|
|
16008
|
+
const annotation = pattern.typeAnnotation;
|
|
16009
|
+
return {
|
|
16010
|
+
type: "Identifier",
|
|
16011
|
+
start: pattern.start,
|
|
16012
|
+
end: annotation?.start ?? pattern.end,
|
|
16013
|
+
loc: {
|
|
16014
|
+
start: pattern.loc.start,
|
|
16015
|
+
end:
|
|
16016
|
+
annotation?.loc?.start ??
|
|
16017
|
+
pattern.loc.end
|
|
16018
|
+
}
|
|
16019
|
+
};
|
|
16020
|
+
}
|
|
16021
|
+
|
|
16022
|
+
function generatedPatternIdentifier(pattern, name, isTop) {
|
|
16023
|
+
const range = patternRange(pattern);
|
|
16024
|
+
const id = ident(name, range);
|
|
16025
|
+
if (range && range.start !== undefined && range.end !== undefined) {
|
|
16026
|
+
metadata(id).source_length = range.end - range.start;
|
|
16027
|
+
}
|
|
16028
|
+
if (isTop && isNode$1(pattern.typeAnnotation)) id.typeAnnotation = pattern.typeAnnotation;
|
|
16029
|
+
if (isTop) setParamTypeMappings(id, pattern);
|
|
16030
|
+
return id;
|
|
16031
|
+
}
|
|
16032
|
+
|
|
16033
|
+
function setParamTypeMappings(id, pattern) {
|
|
16034
|
+
if (pattern.type !== "ObjectPattern") return;
|
|
16035
|
+
const keys = typePropertyKeys(id.typeAnnotation);
|
|
16036
|
+
if (!keys.size) return;
|
|
16037
|
+
const mappings = [];
|
|
16038
|
+
for (const property of pattern.properties) {
|
|
16039
|
+
if (property.type === "RestElement" || property.computed) continue;
|
|
16040
|
+
const value = property.value;
|
|
16041
|
+
const actual = value.type === "AssignmentPattern" ? value.left : value;
|
|
16042
|
+
if (actual.type !== "Identifier" || actual.start === undefined) continue;
|
|
16043
|
+
const name = staticPropertyName(property.key);
|
|
16044
|
+
const generated = name === null ? undefined : keys.get(name);
|
|
16045
|
+
if (generated?.start !== undefined) {
|
|
16046
|
+
metadata(generated).disable_verification = true;
|
|
16047
|
+
mappings.push({ source: actual, generated });
|
|
16048
|
+
}
|
|
16049
|
+
}
|
|
16050
|
+
if (mappings.length) metadata(id).lazy_param_binding_mappings = mappings;
|
|
16051
|
+
}
|
|
16052
|
+
|
|
16053
|
+
function visitTopmostLazyPatterns(
|
|
16054
|
+
pattern,
|
|
16055
|
+
visit)
|
|
16056
|
+
{
|
|
16057
|
+
if (!pattern) return;
|
|
16058
|
+
if (pattern.type === "AssignmentPattern") {
|
|
16059
|
+
visitTopmostLazyPatterns(pattern.left, visit);
|
|
16060
|
+
return;
|
|
16061
|
+
}
|
|
16062
|
+
if (pattern.type === "RestElement") {
|
|
16063
|
+
visitTopmostLazyPatterns(pattern.argument, visit);
|
|
16064
|
+
return;
|
|
16065
|
+
}
|
|
16066
|
+
if (pattern.type !== "ObjectPattern" && pattern.type !== "ArrayPattern") return;
|
|
16067
|
+
if (pattern.lazy) {
|
|
16068
|
+
visit(pattern);
|
|
16069
|
+
return;
|
|
16070
|
+
}
|
|
16071
|
+
if (pattern.type === "ObjectPattern") {
|
|
16072
|
+
for (const property of pattern.properties) {
|
|
16073
|
+
visitTopmostLazyPatterns(
|
|
16074
|
+
property.type === "RestElement" ? property.argument : property.value,
|
|
16075
|
+
visit
|
|
16076
|
+
);
|
|
16077
|
+
}
|
|
16078
|
+
} else {
|
|
16079
|
+
for (const element of pattern.elements) {
|
|
16080
|
+
if (element) visitTopmostLazyPatterns(element, visit);
|
|
16081
|
+
}
|
|
16082
|
+
}
|
|
16083
|
+
}
|
|
16084
|
+
|
|
16085
|
+
function preallocate(root, state) {
|
|
16086
|
+
const assign = (pattern) => {
|
|
16087
|
+
visitTopmostLazyPatterns(pattern, (lazy) => {
|
|
16088
|
+
if (!metadata(lazy).lazy_id) metadata(lazy).lazy_id = state.name("__lazy");
|
|
16089
|
+
});
|
|
16090
|
+
};
|
|
16091
|
+
|
|
16092
|
+
const visit = (value) => {
|
|
16093
|
+
if (Array.isArray(value)) {
|
|
16094
|
+
let found = false;
|
|
16095
|
+
for (const child of value) if (visit(child)) found = true;
|
|
16096
|
+
return found;
|
|
16097
|
+
}
|
|
16098
|
+
if (!isNode$1(value)) return false;
|
|
16099
|
+
|
|
16100
|
+
const functionLike = FUNCTION_TYPES.has(value.type);
|
|
16101
|
+
if (functionLike) {
|
|
16102
|
+
for (const param of value.params) {
|
|
16103
|
+
assign(param.type === "AssignmentPattern" ? param.left : param);
|
|
16104
|
+
}
|
|
16105
|
+
}
|
|
16106
|
+
if (value.type === "VariableDeclarator") assign(value.id);
|
|
16107
|
+
if (value.type === "CatchClause" && isNode$1(value.param)) assign(value.param);
|
|
16108
|
+
if (
|
|
16109
|
+
value.type === "ExpressionStatement" &&
|
|
16110
|
+
isNode$1(value.expression) &&
|
|
16111
|
+
value.expression.type === "AssignmentExpression" &&
|
|
16112
|
+
value.expression.operator === "=")
|
|
16113
|
+
{
|
|
16114
|
+
assign(value.expression.left);
|
|
16115
|
+
}
|
|
16116
|
+
|
|
16117
|
+
let found =
|
|
16118
|
+
(value.type === "ObjectPattern" || value.type === "ArrayPattern") && value.lazy === true;
|
|
16119
|
+
for (const key of Object.keys(value)) {
|
|
16120
|
+
if (SKIP_KEYS$1.has(key)) continue;
|
|
16121
|
+
if (visit(value[key])) found = true;
|
|
16122
|
+
}
|
|
16123
|
+
if (functionLike && found) metadata(value).has_lazy_descendants = true;
|
|
16124
|
+
return found;
|
|
16125
|
+
};
|
|
16126
|
+
visit(root);
|
|
16127
|
+
}
|
|
16128
|
+
|
|
16129
|
+
function transformedEmbedded(
|
|
16130
|
+
node,
|
|
16131
|
+
bindings,
|
|
16132
|
+
state,
|
|
16133
|
+
selfName)
|
|
16134
|
+
{
|
|
16135
|
+
const effective = new Map(bindings);
|
|
16136
|
+
effective.delete(selfName);
|
|
16137
|
+
return transform(cloneNode(node), effective, state);
|
|
16138
|
+
}
|
|
16139
|
+
|
|
16140
|
+
function rootAccess(sourceName, accessor) {
|
|
16141
|
+
const source = (reference) => {
|
|
16142
|
+
const identifier = ident(sourceName, reference);
|
|
16143
|
+
return accessor ? call(identifier, [], reference) : identifier;
|
|
16144
|
+
};
|
|
16145
|
+
return {
|
|
16146
|
+
raw: (reference) => source(reference),
|
|
16147
|
+
value: (reference) => source(reference),
|
|
16148
|
+
plain: true,
|
|
16149
|
+
jsxCompatible: !accessor,
|
|
16150
|
+
complexDefault: false
|
|
16151
|
+
};
|
|
16152
|
+
}
|
|
16153
|
+
|
|
16154
|
+
function propertyAccess(parent, key, computed, state) {
|
|
16155
|
+
const property = (bindings, selfName, reference) =>
|
|
16156
|
+
computed ?
|
|
16157
|
+
transformedEmbedded(key, bindings, state, selfName) :
|
|
16158
|
+
ident(key.name, reference);
|
|
16159
|
+
return {
|
|
16160
|
+
raw: (reference, bindings, selfName) =>
|
|
16161
|
+
member(
|
|
16162
|
+
parent.raw(reference, bindings, selfName),
|
|
16163
|
+
property(bindings, selfName, reference),
|
|
16164
|
+
computed,
|
|
16165
|
+
reference
|
|
16166
|
+
),
|
|
16167
|
+
value: (reference, bindings, selfName) =>
|
|
16168
|
+
member(
|
|
16169
|
+
parent.value(reference, bindings, selfName),
|
|
16170
|
+
property(bindings, selfName, reference),
|
|
16171
|
+
computed,
|
|
16172
|
+
reference
|
|
16173
|
+
),
|
|
16174
|
+
plain: parent.plain,
|
|
16175
|
+
jsxCompatible: parent.jsxCompatible && !computed,
|
|
16176
|
+
complexDefault: parent.complexDefault || parent.directDefault !== undefined
|
|
16177
|
+
};
|
|
16178
|
+
}
|
|
16179
|
+
|
|
16180
|
+
function defaultAccess(parent, fallback, state) {
|
|
16181
|
+
return {
|
|
16182
|
+
raw: parent.raw,
|
|
16183
|
+
value: (reference, bindings, selfName) =>
|
|
16184
|
+
state.defaultRead(
|
|
16185
|
+
parent.value(reference, bindings, selfName),
|
|
16186
|
+
fallback,
|
|
16187
|
+
bindings,
|
|
16188
|
+
selfName,
|
|
16189
|
+
reference
|
|
16190
|
+
),
|
|
16191
|
+
plain: false,
|
|
16192
|
+
jsxCompatible: false,
|
|
16193
|
+
directDefault: fallback,
|
|
16194
|
+
complexDefault: parent.complexDefault || parent.directDefault !== undefined
|
|
16195
|
+
};
|
|
16196
|
+
}
|
|
16197
|
+
|
|
16198
|
+
function jsxNameFromMember(expression, source) {
|
|
16199
|
+
if (expression.type === "Identifier") {
|
|
16200
|
+
return withLoc$1({ type: "JSXIdentifier", name: expression.name }, source);
|
|
16201
|
+
}
|
|
16202
|
+
if (
|
|
16203
|
+
expression.type === "MemberExpression" &&
|
|
16204
|
+
!expression.computed &&
|
|
16205
|
+
isNode$1(expression.property) &&
|
|
16206
|
+
expression.property.type === "Identifier")
|
|
16207
|
+
{
|
|
16208
|
+
const object = jsxNameFromMember(expression.object, source);
|
|
16209
|
+
if (object) {
|
|
16210
|
+
return withLoc$1(
|
|
16211
|
+
{
|
|
16212
|
+
type: "JSXMemberExpression",
|
|
16213
|
+
object,
|
|
16214
|
+
property: withLoc$1(
|
|
16215
|
+
{ type: "JSXIdentifier", name: expression.property.name },
|
|
16216
|
+
expression.property
|
|
16217
|
+
)
|
|
16218
|
+
},
|
|
16219
|
+
source
|
|
16220
|
+
);
|
|
16221
|
+
}
|
|
16222
|
+
}
|
|
16223
|
+
return null;
|
|
16224
|
+
}
|
|
16225
|
+
|
|
16226
|
+
function registerBinding(
|
|
16227
|
+
name,
|
|
16228
|
+
sourceName,
|
|
16229
|
+
access,
|
|
16230
|
+
bindings)
|
|
16231
|
+
{
|
|
16232
|
+
bindings.set(name, {
|
|
16233
|
+
sourceName,
|
|
16234
|
+
read(reference, current) {
|
|
16235
|
+
return access.value(reference, current, name);
|
|
16236
|
+
},
|
|
16237
|
+
target(reference, current) {
|
|
16238
|
+
return access.raw(reference, current, name);
|
|
16239
|
+
},
|
|
16240
|
+
jsxName(reference, current) {
|
|
16241
|
+
return access.jsxCompatible ?
|
|
16242
|
+
jsxNameFromMember(access.raw(reference, current, name), reference) :
|
|
16243
|
+
null;
|
|
16244
|
+
},
|
|
16245
|
+
directDefault: access.directDefault,
|
|
16246
|
+
complexDefault: access.complexDefault
|
|
16247
|
+
});
|
|
16248
|
+
}
|
|
16249
|
+
|
|
16250
|
+
function collectBindingsAt(
|
|
16251
|
+
pattern,
|
|
16252
|
+
sourceName,
|
|
16253
|
+
access,
|
|
16254
|
+
bindings,
|
|
16255
|
+
state)
|
|
16256
|
+
{
|
|
16257
|
+
if (pattern.type === "AssignmentPattern") {
|
|
16258
|
+
collectBindingsAt(
|
|
16259
|
+
pattern.left,
|
|
16260
|
+
sourceName,
|
|
16261
|
+
defaultAccess(access, pattern.right, state),
|
|
16262
|
+
bindings,
|
|
16263
|
+
state
|
|
16264
|
+
);
|
|
16265
|
+
return;
|
|
16266
|
+
}
|
|
16267
|
+
if (pattern.type === "Identifier") {
|
|
16268
|
+
registerBinding(pattern.name, sourceName, access, bindings);
|
|
16269
|
+
return;
|
|
16270
|
+
}
|
|
16271
|
+
if (pattern.type === "ObjectPattern") {
|
|
16272
|
+
const properties = pattern.properties;
|
|
16273
|
+
const sourceKeys = properties.
|
|
16274
|
+
filter((property) => property.type !== "RestElement").
|
|
16275
|
+
map((property) => {
|
|
16276
|
+
const key = property.key;
|
|
16277
|
+
return property.computed ? key : literal(staticPropertyName(key) ?? "", key);
|
|
16278
|
+
});
|
|
16279
|
+
for (const property of properties) {
|
|
16280
|
+
if (property.type === "RestElement") {
|
|
16281
|
+
const argument = property.argument;
|
|
16282
|
+
if (argument.type !== "Identifier") {
|
|
16283
|
+
fail("TSRX lazy object rest currently requires an identifier binding", argument);
|
|
16284
|
+
}
|
|
16285
|
+
const name = argument.name;
|
|
16286
|
+
bindings.set(name, {
|
|
16287
|
+
sourceName,
|
|
16288
|
+
read(reference, current) {
|
|
16289
|
+
const effective = new Map(current);
|
|
16290
|
+
effective.delete(name);
|
|
16291
|
+
return call(
|
|
16292
|
+
state.omitIdentifier(),
|
|
16293
|
+
[
|
|
16294
|
+
access.value(reference, current, name),
|
|
16295
|
+
...sourceKeys.map((key) => transform(cloneNode(key), effective, state))],
|
|
16296
|
+
|
|
16297
|
+
reference
|
|
16298
|
+
);
|
|
16299
|
+
},
|
|
16300
|
+
target() {
|
|
16301
|
+
return null;
|
|
16302
|
+
},
|
|
16303
|
+
jsxName() {
|
|
16304
|
+
return null;
|
|
16305
|
+
},
|
|
16306
|
+
complexDefault: access.complexDefault
|
|
16307
|
+
});
|
|
16308
|
+
continue;
|
|
16309
|
+
}
|
|
16310
|
+
const key = property.key;
|
|
16311
|
+
const computed = !!property.computed || key.type !== "Identifier";
|
|
16312
|
+
collectBindingsAt(
|
|
16313
|
+
property.value,
|
|
16314
|
+
sourceName,
|
|
16315
|
+
propertyAccess(access, key, computed, state),
|
|
16316
|
+
bindings,
|
|
16317
|
+
state
|
|
16318
|
+
);
|
|
16319
|
+
}
|
|
16320
|
+
return;
|
|
16321
|
+
}
|
|
16322
|
+
if (pattern.type === "ArrayPattern") {
|
|
16323
|
+
const elements = pattern.elements;
|
|
16324
|
+
for (let index = 0; index < elements.length; index++) {
|
|
16325
|
+
const element = elements[index];
|
|
16326
|
+
if (!element) continue;
|
|
16327
|
+
if (element.type === "RestElement") {
|
|
16328
|
+
const argument = element.argument;
|
|
16329
|
+
if (argument.type !== "Identifier") {
|
|
16330
|
+
fail("TSRX lazy array rest currently requires an identifier binding", argument);
|
|
16331
|
+
}
|
|
16332
|
+
const name = argument.name;
|
|
16333
|
+
// Reserve and validate the intrinsic alias when the pattern is
|
|
16334
|
+
// registered, even if the rest binding is never read. The native
|
|
16335
|
+
// frontend plans helpers at binding time and must emit the same
|
|
16336
|
+
// `globalThis` shadowing diagnostic.
|
|
16337
|
+
state.arrayIdentifier(argument);
|
|
16338
|
+
bindings.set(name, {
|
|
16339
|
+
sourceName,
|
|
16340
|
+
read(reference, current) {
|
|
16341
|
+
const from = member(
|
|
16342
|
+
state.arrayIdentifier(reference),
|
|
16343
|
+
ident("from", reference),
|
|
16344
|
+
false,
|
|
16345
|
+
reference
|
|
16346
|
+
);
|
|
16347
|
+
const array = call(from, [access.value(reference, current, name)], reference);
|
|
16348
|
+
return call(
|
|
16349
|
+
member(array, ident("slice", reference), false, reference),
|
|
16350
|
+
[literal(index, reference)],
|
|
16351
|
+
reference
|
|
16352
|
+
);
|
|
16353
|
+
},
|
|
16354
|
+
target() {
|
|
16355
|
+
return null;
|
|
16356
|
+
},
|
|
16357
|
+
jsxName() {
|
|
16358
|
+
return null;
|
|
16359
|
+
},
|
|
16360
|
+
complexDefault: access.complexDefault
|
|
16361
|
+
});
|
|
16362
|
+
continue;
|
|
16363
|
+
}
|
|
16364
|
+
collectBindingsAt(
|
|
16365
|
+
element,
|
|
16366
|
+
sourceName,
|
|
16367
|
+
propertyAccess(access, literal(index, element), true, state),
|
|
16368
|
+
bindings,
|
|
16369
|
+
state
|
|
16370
|
+
);
|
|
16371
|
+
}
|
|
16372
|
+
}
|
|
16373
|
+
}
|
|
16374
|
+
|
|
16375
|
+
function collectBindings(
|
|
16376
|
+
pattern,
|
|
16377
|
+
sourceName,
|
|
16378
|
+
bindings,
|
|
16379
|
+
state)
|
|
16380
|
+
{
|
|
16381
|
+
collectBindingsAt(
|
|
16382
|
+
pattern,
|
|
16383
|
+
sourceName,
|
|
16384
|
+
rootAccess(sourceName, metadata(pattern).lazy_source_accessor === true),
|
|
16385
|
+
bindings,
|
|
16386
|
+
state
|
|
16387
|
+
);
|
|
16388
|
+
}
|
|
16389
|
+
|
|
16390
|
+
function collectBindingsFromStatements(
|
|
16391
|
+
statements,
|
|
16392
|
+
bindings,
|
|
16393
|
+
state)
|
|
16394
|
+
{
|
|
16395
|
+
for (const statement of statements) {
|
|
16396
|
+
const declaration =
|
|
16397
|
+
(statement.type === "ExportNamedDeclaration" ||
|
|
16398
|
+
statement.type === "ExportDefaultDeclaration") &&
|
|
16399
|
+
isNode$1(statement.declaration) ?
|
|
16400
|
+
statement.declaration :
|
|
16401
|
+
statement;
|
|
16402
|
+
if (declaration.type === "VariableDeclaration" && declaration.kind !== "var") {
|
|
16403
|
+
for (const item of declaration.declarations) {
|
|
16404
|
+
visitTopmostLazyPatterns(item.id, (lazy) => {
|
|
16405
|
+
const sourceName = metadata(lazy).lazy_id;
|
|
16406
|
+
if (sourceName) collectBindings(lazy, sourceName, bindings, state);
|
|
16407
|
+
});
|
|
16408
|
+
}
|
|
16409
|
+
} else if (
|
|
16410
|
+
statement.type === "ExpressionStatement" &&
|
|
16411
|
+
isNode$1(statement.expression) &&
|
|
16412
|
+
statement.expression.type === "AssignmentExpression" &&
|
|
16413
|
+
statement.expression.operator === "=")
|
|
16414
|
+
{
|
|
16415
|
+
visitTopmostLazyPatterns(statement.expression.left, (lazy) => {
|
|
16416
|
+
const sourceName = metadata(lazy).lazy_id;
|
|
16417
|
+
if (sourceName) collectBindings(lazy, sourceName, bindings, state);
|
|
16418
|
+
});
|
|
16419
|
+
}
|
|
16420
|
+
}
|
|
16421
|
+
}
|
|
16422
|
+
|
|
16423
|
+
|
|
16424
|
+
|
|
16425
|
+
|
|
16426
|
+
|
|
16427
|
+
|
|
16428
|
+
/**
|
|
16429
|
+
* Collect function/program-scoped `var` declarations before rewriting any
|
|
16430
|
+
* references in that scope. `var` crosses blocks, loops, switches, and catch
|
|
16431
|
+
* bodies, but never crosses a nested function, class, or class static block.
|
|
16432
|
+
*/
|
|
16433
|
+
function collectVarScopeBindings(statements, state) {
|
|
16434
|
+
const declared = new Set();
|
|
16435
|
+
const lazy = new Map();
|
|
16436
|
+
|
|
16437
|
+
const visit = (value) => {
|
|
16438
|
+
if (Array.isArray(value)) {
|
|
16439
|
+
for (const child of value) visit(child);
|
|
16440
|
+
return;
|
|
16441
|
+
}
|
|
16442
|
+
if (!isNode$1(value)) return;
|
|
16443
|
+
if (
|
|
16444
|
+
FUNCTION_TYPES.has(value.type) ||
|
|
16445
|
+
value.type === "ClassDeclaration" ||
|
|
16446
|
+
value.type === "ClassExpression" ||
|
|
16447
|
+
value.type === "StaticBlock" ||
|
|
16448
|
+
value.type === "ClassStaticBlock")
|
|
16449
|
+
{
|
|
16450
|
+
return;
|
|
16451
|
+
}
|
|
16452
|
+
if (value.type === "VariableDeclaration" && value.kind === "var") {
|
|
16453
|
+
for (const declaration of value.declarations) {
|
|
16454
|
+
patternNames(declaration.id, declared);
|
|
16455
|
+
visitTopmostLazyPatterns(declaration.id, (pattern) => {
|
|
16456
|
+
const sourceName = metadata(pattern).lazy_id;
|
|
16457
|
+
if (sourceName) collectBindings(pattern, sourceName, lazy, state);
|
|
16458
|
+
});
|
|
16459
|
+
}
|
|
16460
|
+
}
|
|
16461
|
+
for (const key of Object.keys(value)) {
|
|
16462
|
+
if (!SKIP_KEYS$1.has(key)) visit(value[key]);
|
|
16463
|
+
}
|
|
16464
|
+
};
|
|
16465
|
+
|
|
16466
|
+
visit(statements);
|
|
16467
|
+
return { declared, lazy };
|
|
16468
|
+
}
|
|
16469
|
+
|
|
16470
|
+
function replaceLazyInPattern(pattern, isTop = true) {
|
|
16471
|
+
if (pattern.type === "AssignmentPattern") {
|
|
16472
|
+
const left = replaceLazyInPattern(pattern.left, isTop);
|
|
16473
|
+
return left === pattern.left ? pattern : { ...pattern, left };
|
|
16474
|
+
}
|
|
16475
|
+
if (pattern.type === "RestElement") {
|
|
16476
|
+
const argument = replaceLazyInPattern(pattern.argument, false);
|
|
16477
|
+
return argument === pattern.argument ? pattern : { ...pattern, argument };
|
|
16478
|
+
}
|
|
16479
|
+
if (pattern.type !== "ObjectPattern" && pattern.type !== "ArrayPattern") return pattern;
|
|
16480
|
+
const sourceName = metadata(pattern).lazy_id;
|
|
16481
|
+
if (pattern.lazy && sourceName) return generatedPatternIdentifier(pattern, sourceName, isTop);
|
|
16482
|
+
if (pattern.type === "ObjectPattern") {
|
|
16483
|
+
let changed = false;
|
|
16484
|
+
const properties = pattern.properties.map((property) => {
|
|
16485
|
+
const key = property.type === "RestElement" ? "argument" : "value";
|
|
16486
|
+
const value = property[key];
|
|
16487
|
+
const replacement = replaceLazyInPattern(value, false);
|
|
16488
|
+
if (replacement === value) return property;
|
|
16489
|
+
changed = true;
|
|
16490
|
+
return { ...property, [key]: replacement };
|
|
16491
|
+
});
|
|
16492
|
+
return changed ? { ...pattern, properties } : pattern;
|
|
16493
|
+
}
|
|
16494
|
+
let changed = false;
|
|
16495
|
+
const elements = pattern.elements.map((element) => {
|
|
16496
|
+
if (!element) return element;
|
|
16497
|
+
const replacement = replaceLazyInPattern(element, false);
|
|
16498
|
+
if (replacement !== element) changed = true;
|
|
16499
|
+
return replacement;
|
|
16500
|
+
});
|
|
16501
|
+
return changed ? { ...pattern, elements } : pattern;
|
|
16502
|
+
}
|
|
16503
|
+
|
|
16504
|
+
function replaceLazyParams(params) {
|
|
16505
|
+
return params.map((param) => replaceLazyInPattern(param));
|
|
16506
|
+
}
|
|
16507
|
+
|
|
16508
|
+
function patternNames(pattern, names) {
|
|
16509
|
+
switch (pattern.type) {
|
|
16510
|
+
case "Identifier":
|
|
16511
|
+
names.add(pattern.name);
|
|
16512
|
+
return;
|
|
16513
|
+
case "AssignmentPattern":
|
|
16514
|
+
patternNames(pattern.left, names);
|
|
16515
|
+
return;
|
|
16516
|
+
case "RestElement":
|
|
16517
|
+
patternNames(pattern.argument, names);
|
|
16518
|
+
return;
|
|
16519
|
+
case "ObjectPattern":
|
|
16520
|
+
for (const property of pattern.properties) {
|
|
16521
|
+
patternNames(
|
|
16522
|
+
property.type === "RestElement" ? property.argument : property.value,
|
|
16523
|
+
names
|
|
16524
|
+
);
|
|
16525
|
+
}
|
|
16526
|
+
return;
|
|
16527
|
+
case "ArrayPattern":
|
|
16528
|
+
for (const element of pattern.elements) {
|
|
16529
|
+
if (element) patternNames(element, names);
|
|
16530
|
+
}
|
|
16531
|
+
}
|
|
16532
|
+
}
|
|
16533
|
+
|
|
16534
|
+
function collectShadowed(pattern, bindings, names) {
|
|
16535
|
+
const all = new Set();
|
|
16536
|
+
patternNames(pattern, all);
|
|
16537
|
+
for (const name of all) if (bindings.has(name)) names.add(name);
|
|
16538
|
+
}
|
|
16539
|
+
|
|
16540
|
+
function collectBlockShadowed(statements, bindings) {
|
|
16541
|
+
const names = new Set();
|
|
16542
|
+
for (const statement of statements) {
|
|
16543
|
+
const declaration =
|
|
16544
|
+
(statement.type === "ExportNamedDeclaration" ||
|
|
16545
|
+
statement.type === "ExportDefaultDeclaration") &&
|
|
16546
|
+
isNode$1(statement.declaration) ?
|
|
16547
|
+
statement.declaration :
|
|
16548
|
+
statement;
|
|
16549
|
+
if (declaration.type === "VariableDeclaration" && declaration.kind !== "var") {
|
|
16550
|
+
for (const item of declaration.declarations) {
|
|
16551
|
+
collectShadowed(item.id, bindings, names);
|
|
16552
|
+
}
|
|
16553
|
+
} else if (
|
|
16554
|
+
(declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") &&
|
|
16555
|
+
isNode$1(declaration.id) &&
|
|
16556
|
+
bindings.has(declaration.id.name))
|
|
16557
|
+
{
|
|
16558
|
+
names.add(declaration.id.name);
|
|
16559
|
+
} else if (statement.type === "ImportDeclaration") {
|
|
16560
|
+
for (const specifier of statement.specifiers) {
|
|
16561
|
+
if (isNode$1(specifier.local) && bindings.has(specifier.local.name)) {
|
|
16562
|
+
names.add(specifier.local.name);
|
|
16563
|
+
}
|
|
16564
|
+
}
|
|
16565
|
+
}
|
|
16566
|
+
}
|
|
16567
|
+
return names;
|
|
16568
|
+
}
|
|
16569
|
+
|
|
16570
|
+
function without(bindings, names) {
|
|
16571
|
+
const result = new Map(bindings);
|
|
16572
|
+
for (const name of names) result.delete(name);
|
|
16573
|
+
return result;
|
|
16574
|
+
}
|
|
16575
|
+
|
|
16576
|
+
function transformValue(value, bindings, state) {
|
|
16577
|
+
if (Array.isArray(value)) return value.map((item) => transformValue(item, bindings, state));
|
|
16578
|
+
return isNode$1(value) ? transform(value, bindings, state) : value;
|
|
16579
|
+
}
|
|
16580
|
+
|
|
16581
|
+
function targetParts(target, source) {
|
|
16582
|
+
if (target.type !== "MemberExpression") {
|
|
16583
|
+
return fail("A writable TSRX lazy binding did not resolve to a member expression", source);
|
|
16584
|
+
}
|
|
16585
|
+
const property = target.property;
|
|
16586
|
+
return {
|
|
16587
|
+
object: target.object,
|
|
16588
|
+
key: target.computed ? property : literal(property.name, property)
|
|
16589
|
+
};
|
|
16590
|
+
}
|
|
16591
|
+
|
|
16592
|
+
function lowerDefaultedAssignment(
|
|
16593
|
+
node,
|
|
16594
|
+
binding,
|
|
16595
|
+
bindings,
|
|
16596
|
+
state)
|
|
16597
|
+
{
|
|
16598
|
+
if (!binding.directDefault) return fail("Missing default for TSRX lazy assignment", node);
|
|
16599
|
+
const left = node.left;
|
|
16600
|
+
const target = binding.target(left, bindings);
|
|
16601
|
+
if (!target) return fail("A TSRX lazy rest binding is read-only", left);
|
|
16602
|
+
const { object, key } = targetParts(target, left);
|
|
16603
|
+
const objectName = state.temp();
|
|
16604
|
+
const keyName = state.temp();
|
|
16605
|
+
const valueName = state.temp();
|
|
16606
|
+
const objectId = () => ident(objectName, left);
|
|
16607
|
+
const keyId = () => ident(keyName, left);
|
|
16608
|
+
const valueId = () => ident(valueName, left);
|
|
16609
|
+
const destination = () => member(objectId(), keyId(), true, left);
|
|
16610
|
+
const effective = new Map(bindings);
|
|
16611
|
+
effective.delete(left.name);
|
|
16612
|
+
const fallback = transform(cloneNode(binding.directDefault), effective, state);
|
|
16613
|
+
const right = transform(node.right, bindings, state);
|
|
16614
|
+
const operator = node.operator;
|
|
16615
|
+
const expressions = [
|
|
16616
|
+
assignment("=", objectId(), object, left),
|
|
16617
|
+
assignment("=", keyId(), key, left),
|
|
16618
|
+
assignment("=", valueId(), destination(), left),
|
|
16619
|
+
assignment(
|
|
16620
|
+
"=",
|
|
16621
|
+
valueId(),
|
|
16622
|
+
conditional(
|
|
16623
|
+
binary("===", valueId(), undefinedExpression(left), left),
|
|
16624
|
+
fallback,
|
|
16625
|
+
valueId(),
|
|
16626
|
+
left
|
|
16627
|
+
),
|
|
16628
|
+
left
|
|
16629
|
+
)];
|
|
16630
|
+
|
|
16631
|
+
|
|
16632
|
+
if (operator === "&&=" || operator === "||=" || operator === "??=") {
|
|
16633
|
+
expressions.push(
|
|
16634
|
+
logical(operator.slice(0, -1), valueId(), assignment("=", destination(), right, node), node)
|
|
16635
|
+
);
|
|
16636
|
+
} else {
|
|
16637
|
+
expressions.push(
|
|
16638
|
+
assignment("=", destination(), binary(operator.slice(0, -1), valueId(), right, node), node)
|
|
16639
|
+
);
|
|
16640
|
+
}
|
|
16641
|
+
return sequence(expressions, node);
|
|
16642
|
+
}
|
|
16643
|
+
|
|
16644
|
+
function lowerDefaultedUpdate(
|
|
16645
|
+
node,
|
|
16646
|
+
binding,
|
|
16647
|
+
bindings,
|
|
16648
|
+
state)
|
|
16649
|
+
{
|
|
16650
|
+
if (!binding.directDefault) return fail("Missing default for TSRX lazy update", node);
|
|
16651
|
+
const argument = node.argument;
|
|
16652
|
+
const target = binding.target(argument, bindings);
|
|
16653
|
+
if (!target) return fail("A TSRX lazy rest binding is read-only", argument);
|
|
16654
|
+
const { object, key } = targetParts(target, argument);
|
|
16655
|
+
const objectName = state.temp();
|
|
16656
|
+
const keyName = state.temp();
|
|
16657
|
+
const valueName = state.temp();
|
|
16658
|
+
const objectId = () => ident(objectName, argument);
|
|
16659
|
+
const keyId = () => ident(keyName, argument);
|
|
16660
|
+
const valueId = () => ident(valueName, argument);
|
|
16661
|
+
const destination = () => member(objectId(), keyId(), true, argument);
|
|
16662
|
+
const effective = new Map(bindings);
|
|
16663
|
+
effective.delete(argument.name);
|
|
16664
|
+
const fallback = transform(cloneNode(binding.directDefault), effective, state);
|
|
16665
|
+
const expressions = [
|
|
16666
|
+
assignment("=", objectId(), object, argument),
|
|
16667
|
+
assignment("=", keyId(), key, argument),
|
|
16668
|
+
assignment("=", valueId(), destination(), argument),
|
|
16669
|
+
assignment(
|
|
16670
|
+
"=",
|
|
16671
|
+
valueId(),
|
|
16672
|
+
conditional(
|
|
16673
|
+
binary("===", valueId(), undefinedExpression(argument), argument),
|
|
16674
|
+
fallback,
|
|
16675
|
+
valueId(),
|
|
16676
|
+
argument
|
|
16677
|
+
),
|
|
16678
|
+
argument
|
|
16679
|
+
)];
|
|
16680
|
+
|
|
16681
|
+
|
|
16682
|
+
if (node.prefix) {
|
|
16683
|
+
expressions.push(
|
|
16684
|
+
assignment(
|
|
16685
|
+
"=",
|
|
16686
|
+
destination(),
|
|
16687
|
+
withLoc$1(
|
|
16688
|
+
{
|
|
16689
|
+
type: "UpdateExpression",
|
|
16690
|
+
operator: node.operator,
|
|
16691
|
+
prefix: true,
|
|
16692
|
+
argument: valueId()
|
|
16693
|
+
},
|
|
16694
|
+
node
|
|
16695
|
+
),
|
|
16696
|
+
node
|
|
16697
|
+
)
|
|
16698
|
+
);
|
|
16699
|
+
} else {
|
|
16700
|
+
const oldName = state.temp();
|
|
16701
|
+
const oldId = () => ident(oldName, argument);
|
|
16702
|
+
expressions.push(
|
|
16703
|
+
assignment(
|
|
16704
|
+
"=",
|
|
16705
|
+
oldId(),
|
|
16706
|
+
withLoc$1(
|
|
16707
|
+
{
|
|
16708
|
+
type: "UpdateExpression",
|
|
16709
|
+
operator: node.operator,
|
|
16710
|
+
prefix: false,
|
|
16711
|
+
argument: valueId()
|
|
16712
|
+
},
|
|
16713
|
+
node
|
|
16714
|
+
),
|
|
16715
|
+
node
|
|
16716
|
+
),
|
|
16717
|
+
assignment("=", destination(), valueId(), node),
|
|
16718
|
+
oldId()
|
|
16719
|
+
);
|
|
16720
|
+
}
|
|
16721
|
+
return sequence(expressions, node);
|
|
16722
|
+
}
|
|
16723
|
+
|
|
16724
|
+
function dynamicJsxElement(node, bindings, state) {
|
|
16725
|
+
const opening = node.openingElement;
|
|
16726
|
+
const name = opening.name;
|
|
16727
|
+
let base = name;
|
|
16728
|
+
const properties = [];
|
|
16729
|
+
while (base.type === "JSXMemberExpression") {
|
|
16730
|
+
properties.unshift(base.property);
|
|
16731
|
+
base = base.object;
|
|
16732
|
+
}
|
|
16733
|
+
if (base.type !== "JSXIdentifier" || !/^[A-Z]/.test(base.name)) return null;
|
|
16734
|
+
const binding = bindings.get(base.name);
|
|
16735
|
+
if (!binding || binding.jsxName(base, bindings)) return null;
|
|
16736
|
+
|
|
16737
|
+
let expression = binding.read(base, bindings);
|
|
16738
|
+
for (const property of properties) {
|
|
16739
|
+
expression = member(expression, ident(property.name, property), false, property);
|
|
16740
|
+
}
|
|
16741
|
+
const dynamicName = withLoc$1({ type: "JSXIdentifier", name: "Dynamic" }, name);
|
|
16742
|
+
const componentAttribute = {
|
|
16743
|
+
type: "JSXAttribute",
|
|
16744
|
+
name: withLoc$1({ type: "JSXIdentifier", name: "component" }, name),
|
|
16745
|
+
value: withLoc$1({ type: "JSXExpressionContainer", expression }, name)
|
|
16746
|
+
};
|
|
16747
|
+
const nextOpening = {
|
|
16748
|
+
...opening,
|
|
16749
|
+
name: dynamicName,
|
|
16750
|
+
attributes: [
|
|
16751
|
+
componentAttribute,
|
|
16752
|
+
...(opening.attributes ?? []).map((attribute) =>
|
|
16753
|
+
transform(attribute, bindings, state)
|
|
16754
|
+
)]
|
|
16755
|
+
|
|
16756
|
+
};
|
|
16757
|
+
const closing = node.closingElement;
|
|
16758
|
+
return {
|
|
16759
|
+
...node,
|
|
16760
|
+
openingElement: nextOpening,
|
|
16761
|
+
closingElement: closing ? { ...closing, name: cloneNode(dynamicName) } : null,
|
|
16762
|
+
children: (node.children ?? []).map((child) => transform(child, bindings, state))
|
|
16763
|
+
};
|
|
16764
|
+
}
|
|
16765
|
+
|
|
16766
|
+
function addFunctionTemps(body, names) {
|
|
16767
|
+
if (!names.length) return body;
|
|
16768
|
+
const declaration = variableDeclaration("let", names);
|
|
16769
|
+
if (body.type !== "BlockStatement") {
|
|
16770
|
+
return {
|
|
16771
|
+
type: "BlockStatement",
|
|
16772
|
+
body: [declaration, withLoc$1({ type: "ReturnStatement", argument: body }, body)]
|
|
16773
|
+
};
|
|
16774
|
+
}
|
|
16775
|
+
const statements = body.body;
|
|
16776
|
+
let directiveEnd = 0;
|
|
16777
|
+
while (
|
|
16778
|
+
directiveEnd < statements.length &&
|
|
16779
|
+
statements[directiveEnd].type === "ExpressionStatement" &&
|
|
16780
|
+
typeof statements[directiveEnd].directive === "string")
|
|
16781
|
+
{
|
|
16782
|
+
directiveEnd++;
|
|
16783
|
+
}
|
|
16784
|
+
return {
|
|
16785
|
+
...body,
|
|
16786
|
+
body: [...statements.slice(0, directiveEnd), declaration, ...statements.slice(directiveEnd)]
|
|
16787
|
+
};
|
|
16788
|
+
}
|
|
16789
|
+
|
|
16790
|
+
function transform(node, bindings, state) {
|
|
16791
|
+
if (FUNCTION_TYPES.has(node.type)) {
|
|
16792
|
+
const shadowed = new Set();
|
|
16793
|
+
if (isNode$1(node.id)) collectShadowed(node.id, bindings, shadowed);
|
|
16794
|
+
for (const param of node.params) collectShadowed(param, bindings, shadowed);
|
|
16795
|
+
const outer = shadowed.size ? without(bindings, shadowed) : bindings;
|
|
16796
|
+
const own = new Map();
|
|
16797
|
+
let lazyParam = false;
|
|
16798
|
+
for (const param of node.params) {
|
|
16799
|
+
visitTopmostLazyPatterns(param, (lazy) => {
|
|
16800
|
+
const sourceName = metadata(lazy).lazy_id;
|
|
16801
|
+
if (!sourceName) return;
|
|
16802
|
+
lazyParam = true;
|
|
16803
|
+
collectBindings(lazy, sourceName, own, state);
|
|
16804
|
+
});
|
|
16805
|
+
}
|
|
16806
|
+
const inner = own.size ? new Map([...outer, ...own]) : outer;
|
|
16807
|
+
let paramsChanged = false;
|
|
16808
|
+
const params = node.params.map((param) => {
|
|
16809
|
+
if (param.type !== "AssignmentPattern") return param;
|
|
16810
|
+
const right = transform(param.right, inner, state);
|
|
16811
|
+
if (right === param.right) return param;
|
|
16812
|
+
paramsChanged = true;
|
|
16813
|
+
return { ...param, right };
|
|
16814
|
+
});
|
|
16815
|
+
if (!inner.size && !paramsChanged && !lazyParam && !metadata(node).has_lazy_descendants) {
|
|
16816
|
+
return node;
|
|
16817
|
+
}
|
|
16818
|
+
state.pushFunctionTemps();
|
|
16819
|
+
const functionBody = node.body;
|
|
16820
|
+
let bodyBindings = inner;
|
|
16821
|
+
if (functionBody.type === "BlockStatement") {
|
|
16822
|
+
const varScope = collectVarScopeBindings(functionBody.body, state);
|
|
16823
|
+
const unshadowed = varScope.declared.size ?
|
|
16824
|
+
without(bodyBindings, varScope.declared) :
|
|
16825
|
+
bodyBindings;
|
|
16826
|
+
bodyBindings = new Map([...unshadowed, ...own, ...varScope.lazy]);
|
|
16827
|
+
}
|
|
16828
|
+
const transformedBody = transform(functionBody, bodyBindings, state);
|
|
16829
|
+
const bodyTemps = state.popFunctionTemps();
|
|
16830
|
+
const body = addFunctionTemps(transformedBody, bodyTemps);
|
|
16831
|
+
const finalParams = lazyParam ? replaceLazyParams(params) : params;
|
|
16832
|
+
return body !== node.body || finalParams !== node.params ?
|
|
16833
|
+
{
|
|
16834
|
+
...node,
|
|
16835
|
+
params: finalParams,
|
|
16836
|
+
body,
|
|
16837
|
+
...(node.type === "ArrowFunctionExpression" && body.type === "BlockStatement" ?
|
|
16838
|
+
{ expression: false } :
|
|
16839
|
+
{})
|
|
16840
|
+
} :
|
|
16841
|
+
node;
|
|
16842
|
+
}
|
|
16843
|
+
|
|
16844
|
+
if (node.type === "Program") {
|
|
16845
|
+
const body = node.body;
|
|
16846
|
+
const varScope = collectVarScopeBindings(body, state);
|
|
16847
|
+
const varOuter = varScope.declared.size ? without(bindings, varScope.declared) : bindings;
|
|
16848
|
+
const withVar = varScope.lazy.size ? new Map([...varOuter, ...varScope.lazy]) : varOuter;
|
|
16849
|
+
const shadowed = collectBlockShadowed(body, withVar);
|
|
16850
|
+
const outer = shadowed.size ? without(withVar, shadowed) : withVar;
|
|
16851
|
+
const own = new Map();
|
|
16852
|
+
collectBindingsFromStatements(body, own, state);
|
|
16853
|
+
const inner = own.size ? new Map([...outer, ...own]) : outer;
|
|
16854
|
+
let changed = false;
|
|
16855
|
+
const next = body.map((statement) => {
|
|
16856
|
+
const result = transform(statement, inner, state);
|
|
16857
|
+
if (result !== statement) changed = true;
|
|
16858
|
+
return result;
|
|
16859
|
+
});
|
|
16860
|
+
return changed ? { ...node, body: next } : node;
|
|
16861
|
+
}
|
|
16862
|
+
|
|
16863
|
+
if (node.type === "BlockStatement") {
|
|
16864
|
+
const body = node.body;
|
|
16865
|
+
const shadowed = collectBlockShadowed(body, bindings);
|
|
16866
|
+
const outer = shadowed.size ? without(bindings, shadowed) : bindings;
|
|
16867
|
+
const own = new Map();
|
|
16868
|
+
collectBindingsFromStatements(body, own, state);
|
|
16869
|
+
const inner = own.size ? new Map([...outer, ...own]) : outer;
|
|
16870
|
+
let changed = false;
|
|
16871
|
+
const next = body.map((statement) => {
|
|
16872
|
+
const result = transform(statement, inner, state);
|
|
16873
|
+
if (result !== statement) changed = true;
|
|
16874
|
+
return result;
|
|
16875
|
+
});
|
|
16876
|
+
return changed ? { ...node, body: next } : node;
|
|
16877
|
+
}
|
|
16878
|
+
|
|
16879
|
+
if (node.type === "StaticBlock" || node.type === "ClassStaticBlock") {
|
|
16880
|
+
const body = node.body;
|
|
16881
|
+
const varScope = collectVarScopeBindings(body, state);
|
|
16882
|
+
const varOuter = varScope.declared.size ? without(bindings, varScope.declared) : bindings;
|
|
16883
|
+
const withVar = varScope.lazy.size ? new Map([...varOuter, ...varScope.lazy]) : varOuter;
|
|
16884
|
+
const shadowed = collectBlockShadowed(body, withVar);
|
|
16885
|
+
const outer = shadowed.size ? without(withVar, shadowed) : withVar;
|
|
16886
|
+
const own = new Map();
|
|
16887
|
+
collectBindingsFromStatements(body, own, state);
|
|
16888
|
+
const inner = own.size ? new Map([...outer, ...own]) : outer;
|
|
16889
|
+
let changed = false;
|
|
16890
|
+
const next = body.map((statement) => {
|
|
16891
|
+
const result = transform(statement, inner, state);
|
|
16892
|
+
if (result !== statement) changed = true;
|
|
16893
|
+
return result;
|
|
16894
|
+
});
|
|
16895
|
+
return changed ? { ...node, body: next } : node;
|
|
16896
|
+
}
|
|
16897
|
+
|
|
16898
|
+
if (node.type === "CatchClause") {
|
|
16899
|
+
const own = new Map();
|
|
16900
|
+
if (isNode$1(node.param)) {
|
|
16901
|
+
visitTopmostLazyPatterns(node.param, (lazy) => {
|
|
16902
|
+
const sourceName = metadata(lazy).lazy_id;
|
|
16903
|
+
if (sourceName) collectBindings(lazy, sourceName, own, state);
|
|
16904
|
+
});
|
|
16905
|
+
}
|
|
16906
|
+
const shadowed = new Set();
|
|
16907
|
+
if (isNode$1(node.param)) collectShadowed(node.param, bindings, shadowed);
|
|
16908
|
+
const outer = shadowed.size ? without(bindings, shadowed) : bindings;
|
|
16909
|
+
const inner = own.size ? new Map([...outer, ...own]) : outer;
|
|
16910
|
+
const param = isNode$1(node.param) ? replaceLazyInPattern(node.param, true) : node.param;
|
|
16911
|
+
const body = transform(node.body, inner, state);
|
|
16912
|
+
return param === node.param && body === node.body ? node : { ...node, param, body };
|
|
16913
|
+
}
|
|
16914
|
+
|
|
16915
|
+
if (node.type === "ForStatement") {
|
|
16916
|
+
const shadowed = new Set();
|
|
16917
|
+
const own = new Map();
|
|
16918
|
+
const init = node.init;
|
|
16919
|
+
if (init?.type === "VariableDeclaration") {
|
|
16920
|
+
for (const declaration of init.declarations) {
|
|
16921
|
+
if (init.kind !== "var") {
|
|
16922
|
+
visitTopmostLazyPatterns(declaration.id, (lazy) => {
|
|
16923
|
+
const sourceName = metadata(lazy).lazy_id;
|
|
16924
|
+
if (sourceName) collectBindings(lazy, sourceName, own, state);
|
|
16925
|
+
});
|
|
16926
|
+
collectShadowed(declaration.id, bindings, shadowed);
|
|
16927
|
+
}
|
|
16928
|
+
}
|
|
16929
|
+
}
|
|
16930
|
+
const outer = shadowed.size ? without(bindings, shadowed) : bindings;
|
|
16931
|
+
const inner = own.size ? new Map([...outer, ...own]) : outer;
|
|
16932
|
+
return {
|
|
16933
|
+
...node,
|
|
16934
|
+
init: init ? transform(init, inner, state) : init,
|
|
16935
|
+
test: isNode$1(node.test) ? transform(node.test, inner, state) : node.test,
|
|
16936
|
+
update: isNode$1(node.update) ? transform(node.update, inner, state) : node.update,
|
|
16937
|
+
body: transform(node.body, inner, state)
|
|
16938
|
+
};
|
|
16939
|
+
}
|
|
16940
|
+
|
|
16941
|
+
if (node.type === "ForOfStatement" || node.type === "ForInStatement") {
|
|
16942
|
+
const shadowed = new Set();
|
|
16943
|
+
const own = new Map();
|
|
16944
|
+
const left = node.left;
|
|
16945
|
+
if (left.type === "VariableDeclaration") {
|
|
16946
|
+
for (const declaration of left.declarations) {
|
|
16947
|
+
if (left.kind !== "var") {
|
|
16948
|
+
visitTopmostLazyPatterns(declaration.id, (lazy) => {
|
|
16949
|
+
const sourceName = metadata(lazy).lazy_id;
|
|
16950
|
+
if (sourceName) collectBindings(lazy, sourceName, own, state);
|
|
16951
|
+
});
|
|
16952
|
+
collectShadowed(declaration.id, bindings, shadowed);
|
|
16953
|
+
}
|
|
16954
|
+
}
|
|
16955
|
+
}
|
|
16956
|
+
const outer = shadowed.size ? without(bindings, shadowed) : bindings;
|
|
16957
|
+
const inner = own.size ? new Map([...outer, ...own]) : outer;
|
|
16958
|
+
return {
|
|
16959
|
+
...node,
|
|
16960
|
+
left: transform(left, inner, state),
|
|
16961
|
+
right: transform(node.right, inner, state),
|
|
16962
|
+
body: transform(node.body, inner, state)
|
|
16963
|
+
};
|
|
16964
|
+
}
|
|
16965
|
+
|
|
16966
|
+
if (node.type === "SwitchStatement") {
|
|
16967
|
+
const cases = node.cases;
|
|
16968
|
+
const statements = cases.flatMap((item) => item.consequent);
|
|
16969
|
+
const shadowed = collectBlockShadowed(statements, bindings);
|
|
16970
|
+
const outer = shadowed.size ? without(bindings, shadowed) : bindings;
|
|
16971
|
+
const own = new Map();
|
|
16972
|
+
collectBindingsFromStatements(statements, own, state);
|
|
16973
|
+
const inner = own.size ? new Map([...outer, ...own]) : outer;
|
|
16974
|
+
return {
|
|
16975
|
+
...node,
|
|
16976
|
+
discriminant: transform(node.discriminant, outer, state),
|
|
16977
|
+
cases: cases.map((item) => ({
|
|
16978
|
+
...item,
|
|
16979
|
+
test: isNode$1(item.test) ? transform(item.test, inner, state) : item.test,
|
|
16980
|
+
consequent: item.consequent.map((statement) =>
|
|
16981
|
+
transform(statement, inner, state)
|
|
16982
|
+
)
|
|
16983
|
+
}))
|
|
16984
|
+
};
|
|
16985
|
+
}
|
|
16986
|
+
|
|
16987
|
+
if (node.type === "ClassDeclaration" || node.type === "ClassExpression") {
|
|
16988
|
+
const shadowed = new Set();
|
|
16989
|
+
if (isNode$1(node.id)) collectShadowed(node.id, bindings, shadowed);
|
|
16990
|
+
const inner = shadowed.size ? without(bindings, shadowed) : bindings;
|
|
16991
|
+
return {
|
|
16992
|
+
...node,
|
|
16993
|
+
superClass: isNode$1(node.superClass) ?
|
|
16994
|
+
transform(node.superClass, bindings, state) :
|
|
16995
|
+
node.superClass,
|
|
16996
|
+
body: transform(node.body, inner, state)
|
|
16997
|
+
};
|
|
16998
|
+
}
|
|
16999
|
+
|
|
17000
|
+
if (
|
|
17001
|
+
node.type === "ExportNamedDeclaration" &&
|
|
17002
|
+
isNode$1(node.declaration) &&
|
|
17003
|
+
node.declaration.type === "VariableDeclaration")
|
|
17004
|
+
{
|
|
17005
|
+
for (const declaration of node.declaration.declarations) {
|
|
17006
|
+
visitTopmostLazyPatterns(declaration.id, (pattern) => {
|
|
17007
|
+
fail(
|
|
17008
|
+
"TSRX lazy bindings cannot be exported; export the source object or an explicit accessor instead",
|
|
17009
|
+
pattern
|
|
17010
|
+
);
|
|
17011
|
+
});
|
|
17012
|
+
}
|
|
17013
|
+
}
|
|
17014
|
+
|
|
17015
|
+
if (
|
|
17016
|
+
node.type === "ExportSpecifier" &&
|
|
17017
|
+
isNode$1(node.local) &&
|
|
17018
|
+
node.local.type === "Identifier" &&
|
|
17019
|
+
bindings.has(node.local.name))
|
|
17020
|
+
{
|
|
17021
|
+
return fail(
|
|
17022
|
+
"TSRX lazy bindings cannot be exported; export the source object or an explicit accessor instead",
|
|
17023
|
+
node.local
|
|
17024
|
+
);
|
|
17025
|
+
}
|
|
17026
|
+
|
|
17027
|
+
if (node.type === "JSXElement") {
|
|
17028
|
+
const dynamic = dynamicJsxElement(node, bindings, state);
|
|
17029
|
+
if (dynamic) return dynamic;
|
|
17030
|
+
}
|
|
17031
|
+
|
|
17032
|
+
if (
|
|
17033
|
+
node.type === "ExpressionStatement" &&
|
|
17034
|
+
isNode$1(node.expression) &&
|
|
17035
|
+
node.expression.type === "AssignmentExpression" &&
|
|
17036
|
+
node.expression.operator === "=" &&
|
|
17037
|
+
isNode$1(node.expression.left) && (
|
|
17038
|
+
node.expression.left.type === "ObjectPattern" ||
|
|
17039
|
+
node.expression.left.type === "ArrayPattern") &&
|
|
17040
|
+
node.expression.left.lazy)
|
|
17041
|
+
{
|
|
17042
|
+
const pattern = node.expression.left;
|
|
17043
|
+
const sourceName = metadata(pattern).lazy_id;
|
|
17044
|
+
if (sourceName) {
|
|
17045
|
+
const id = generatedPatternIdentifier(pattern, sourceName, true);
|
|
17046
|
+
return {
|
|
17047
|
+
type: "VariableDeclaration",
|
|
17048
|
+
kind: "const",
|
|
17049
|
+
declarations: [
|
|
17050
|
+
{
|
|
17051
|
+
type: "VariableDeclarator",
|
|
17052
|
+
id,
|
|
17053
|
+
init: transform(node.expression.right, bindings, state)
|
|
17054
|
+
}]
|
|
17055
|
+
|
|
17056
|
+
};
|
|
17057
|
+
}
|
|
17058
|
+
}
|
|
17059
|
+
|
|
17060
|
+
if (
|
|
17061
|
+
node.type === "ExpressionStatement" &&
|
|
17062
|
+
isNode$1(node.expression) &&
|
|
17063
|
+
node.expression.type === "AssignmentExpression" &&
|
|
17064
|
+
node.expression.operator === "=" &&
|
|
17065
|
+
isNode$1(node.expression.left) && (
|
|
17066
|
+
node.expression.left.type === "ObjectPattern" ||
|
|
17067
|
+
node.expression.left.type === "ArrayPattern") &&
|
|
17068
|
+
!node.expression.left.lazy)
|
|
17069
|
+
{
|
|
17070
|
+
const left = replaceLazyInPattern(node.expression.left);
|
|
17071
|
+
if (left !== node.expression.left) {
|
|
17072
|
+
return fail(
|
|
17073
|
+
"A lazy pattern nested inside a standalone destructuring assignment is not supported because its generated source binding cannot be scoped correctly; use a lazy variable declaration",
|
|
17074
|
+
node.expression.left
|
|
17075
|
+
);
|
|
17076
|
+
}
|
|
17077
|
+
}
|
|
17078
|
+
|
|
17079
|
+
if (
|
|
17080
|
+
node.type === "AssignmentExpression" &&
|
|
17081
|
+
isNode$1(node.left) &&
|
|
17082
|
+
node.left.type === "Identifier")
|
|
17083
|
+
{
|
|
17084
|
+
const binding = bindings.get(node.left.name);
|
|
17085
|
+
if (binding) {
|
|
17086
|
+
assertWritable(binding, node.left);
|
|
17087
|
+
if (node.operator !== "=" && binding.directDefault) {
|
|
17088
|
+
return lowerDefaultedAssignment(node, binding, bindings, state);
|
|
17089
|
+
}
|
|
17090
|
+
const target = binding.target(node.left, bindings);
|
|
17091
|
+
if (!target) return fail("A TSRX lazy rest binding is read-only", node.left);
|
|
17092
|
+
return {
|
|
17093
|
+
...node,
|
|
17094
|
+
left: target,
|
|
17095
|
+
right: transform(node.right, bindings, state)
|
|
17096
|
+
};
|
|
17097
|
+
}
|
|
17098
|
+
}
|
|
17099
|
+
|
|
17100
|
+
if (
|
|
17101
|
+
node.type === "UpdateExpression" &&
|
|
17102
|
+
isNode$1(node.argument) &&
|
|
17103
|
+
node.argument.type === "Identifier")
|
|
17104
|
+
{
|
|
17105
|
+
const binding = bindings.get(node.argument.name);
|
|
17106
|
+
if (binding) {
|
|
17107
|
+
assertWritable(binding, node.argument);
|
|
17108
|
+
if (binding.directDefault) {
|
|
17109
|
+
return lowerDefaultedUpdate(node, binding, bindings, state);
|
|
17110
|
+
}
|
|
17111
|
+
const target = binding.target(node.argument, bindings);
|
|
17112
|
+
if (!target) return fail("A TSRX lazy rest binding is read-only", node.argument);
|
|
17113
|
+
return { ...node, argument: target };
|
|
17114
|
+
}
|
|
17115
|
+
}
|
|
17116
|
+
|
|
17117
|
+
if (node.type === "VariableDeclarator" && isNode$1(node.id) && metadata(node.id).lazy_id) {
|
|
17118
|
+
const sourceName = metadata(node.id).lazy_id;
|
|
17119
|
+
return {
|
|
17120
|
+
...node,
|
|
17121
|
+
id: generatedPatternIdentifier(node.id, sourceName, true),
|
|
17122
|
+
init: isNode$1(node.init) ? transform(node.init, bindings, state) : node.init
|
|
17123
|
+
};
|
|
17124
|
+
}
|
|
17125
|
+
|
|
17126
|
+
if (
|
|
17127
|
+
node.type === "VariableDeclarator" &&
|
|
17128
|
+
isNode$1(node.id) && (
|
|
17129
|
+
node.id.type === "ObjectPattern" || node.id.type === "ArrayPattern") &&
|
|
17130
|
+
!node.id.lazy)
|
|
17131
|
+
{
|
|
17132
|
+
const id = replaceLazyInPattern(node.id);
|
|
17133
|
+
if (id !== node.id) {
|
|
17134
|
+
return {
|
|
17135
|
+
...node,
|
|
17136
|
+
id,
|
|
17137
|
+
init: isNode$1(node.init) ? transform(node.init, bindings, state) : node.init
|
|
17138
|
+
};
|
|
17139
|
+
}
|
|
17140
|
+
}
|
|
17141
|
+
|
|
17142
|
+
if (
|
|
17143
|
+
node.type === "Property" &&
|
|
17144
|
+
node.shorthand &&
|
|
17145
|
+
isNode$1(node.value) &&
|
|
17146
|
+
node.value.type === "Identifier")
|
|
17147
|
+
{
|
|
17148
|
+
const binding = bindings.get(node.value.name);
|
|
17149
|
+
if (binding) return { ...node, shorthand: false, value: binding.read(node.value, bindings) };
|
|
17150
|
+
}
|
|
17151
|
+
|
|
17152
|
+
if (node.type === "Identifier") {
|
|
17153
|
+
const binding = bindings.get(node.name);
|
|
17154
|
+
return binding ? binding.read(node, bindings) : node;
|
|
17155
|
+
}
|
|
17156
|
+
|
|
17157
|
+
if (node.type === "JSXIdentifier") return node;
|
|
17158
|
+
if (node.type === "MetaProperty") return node;
|
|
17159
|
+
|
|
17160
|
+
let changed = false;
|
|
17161
|
+
const clone = { ...node };
|
|
17162
|
+
for (const key of Object.keys(node)) {
|
|
17163
|
+
if (SKIP_KEYS$1.has(key) || TYPE_KEYS.has(key)) continue;
|
|
17164
|
+
if (
|
|
17165
|
+
key === "key" &&
|
|
17166
|
+
!node.computed && (
|
|
17167
|
+
node.type === "Property" ||
|
|
17168
|
+
node.type === "MethodDefinition" ||
|
|
17169
|
+
node.type === "PropertyDefinition"))
|
|
17170
|
+
{
|
|
17171
|
+
continue;
|
|
17172
|
+
}
|
|
17173
|
+
if (key === "property" && node.type === "MemberExpression" && !node.computed) continue;
|
|
17174
|
+
if (key === "property" && node.type === "JSXMemberExpression") continue;
|
|
17175
|
+
if (key === "name" && node.type === "JSXAttribute") continue;
|
|
17176
|
+
if (key === "id" && node.type === "VariableDeclarator") continue;
|
|
17177
|
+
if (
|
|
17178
|
+
(key === "local" || key === "imported" || key === "exported") &&
|
|
17179
|
+
/Specifier$/.test(node.type))
|
|
17180
|
+
{
|
|
17181
|
+
continue;
|
|
17182
|
+
}
|
|
17183
|
+
if (key === "label") continue;
|
|
17184
|
+
|
|
17185
|
+
if (
|
|
17186
|
+
key === "name" && (
|
|
17187
|
+
node.type === "JSXOpeningElement" || node.type === "JSXClosingElement") &&
|
|
17188
|
+
isNode$1(node.name))
|
|
17189
|
+
{
|
|
17190
|
+
const jsxName = rewriteJsxName(node.name, bindings);
|
|
17191
|
+
if (jsxName) {
|
|
17192
|
+
clone[key] = jsxName;
|
|
17193
|
+
changed = true;
|
|
17194
|
+
continue;
|
|
17195
|
+
}
|
|
17196
|
+
}
|
|
17197
|
+
const value = transformValue(node[key], bindings, state);
|
|
17198
|
+
if (value !== node[key]) {
|
|
17199
|
+
clone[key] = value;
|
|
17200
|
+
changed = true;
|
|
17201
|
+
}
|
|
17202
|
+
}
|
|
17203
|
+
return changed ? clone : node;
|
|
17204
|
+
}
|
|
17205
|
+
|
|
17206
|
+
function rewriteJsxName(name, bindings) {
|
|
17207
|
+
if (name.type === "JSXIdentifier") {
|
|
17208
|
+
if (!/^[A-Z]/.test(name.name)) return null;
|
|
17209
|
+
return bindings.get(name.name)?.jsxName(name, bindings) ?? null;
|
|
17210
|
+
}
|
|
17211
|
+
if (name.type === "JSXMemberExpression") {
|
|
17212
|
+
const object = rewriteJsxName(name.object, bindings);
|
|
17213
|
+
return object ? { ...name, object } : null;
|
|
17214
|
+
}
|
|
17215
|
+
return null;
|
|
17216
|
+
}
|
|
17217
|
+
|
|
17218
|
+
/** Apply Solid's local lazy transform to a fully desugared TSRX program. */
|
|
17219
|
+
function applyLazyTransforms(root) {
|
|
17220
|
+
const state = new LazyState(root);
|
|
17221
|
+
preallocate(root, state);
|
|
17222
|
+
return state.finalize(transform(root, new Map(), state));
|
|
17223
|
+
}
|
|
17224
|
+
|
|
17225
|
+
const SKIP_KEYS = new Set([
|
|
17226
|
+
"type",
|
|
17227
|
+
"loc",
|
|
17228
|
+
"start",
|
|
17229
|
+
"end",
|
|
17230
|
+
"range",
|
|
17231
|
+
"metadata",
|
|
17232
|
+
"css",
|
|
17233
|
+
"leadingComments",
|
|
17234
|
+
"trailingComments",
|
|
17235
|
+
"innerComments"]
|
|
17236
|
+
);
|
|
17237
|
+
|
|
17238
|
+
const FUNCTION_BOUNDARIES = new Set([
|
|
17239
|
+
"FunctionDeclaration",
|
|
17240
|
+
"FunctionExpression",
|
|
17241
|
+
"ArrowFunctionExpression",
|
|
17242
|
+
"ClassDeclaration",
|
|
17243
|
+
"ClassExpression"]
|
|
17244
|
+
);
|
|
17245
|
+
|
|
17246
|
+
function isNode(value) {
|
|
17247
|
+
return (
|
|
17248
|
+
typeof value === "object" &&
|
|
17249
|
+
value !== null &&
|
|
17250
|
+
typeof value.type === "string");
|
|
17251
|
+
|
|
17252
|
+
}
|
|
17253
|
+
|
|
17254
|
+
function isStyleElement(node) {
|
|
17255
|
+
return node.type === "JSXStyleElement";
|
|
17256
|
+
}
|
|
17257
|
+
|
|
17258
|
+
function isNativeRenderRoot(node) {
|
|
17259
|
+
return (
|
|
17260
|
+
(node.type === "JSXElement" || node.type === "JSXFragment") &&
|
|
17261
|
+
!!node.metadata?.native_tsrx);
|
|
17262
|
+
|
|
17263
|
+
}
|
|
17264
|
+
|
|
17265
|
+
function isNativeTsrxNode(node) {
|
|
17266
|
+
return (
|
|
17267
|
+
node?.type === "JSXCodeBlock" ||
|
|
17268
|
+
!!node && (
|
|
17269
|
+
node.type === "JSXElement" ||
|
|
17270
|
+
node.type === "JSXFragment" ||
|
|
17271
|
+
node.type === "JSXStyleElement") &&
|
|
17272
|
+
!!node.metadata?.native_tsrx);
|
|
17273
|
+
|
|
17274
|
+
}
|
|
17275
|
+
|
|
17276
|
+
function isStyleExpressionPosition(path) {
|
|
17277
|
+
const parent = path[path.length - 1];
|
|
17278
|
+
return !(
|
|
17279
|
+
isNativeTsrxNode(parent) ||
|
|
17280
|
+
parent?.type === "BlockStatement" ||
|
|
17281
|
+
parent?.type === "Program" ||
|
|
17282
|
+
parent?.type === "SwitchCase");
|
|
17283
|
+
|
|
17284
|
+
}
|
|
17285
|
+
|
|
17286
|
+
function structuralChildren(node) {
|
|
17287
|
+
switch (node.type) {
|
|
17288
|
+
case "JSXElement":
|
|
17289
|
+
case "JSXFragment":
|
|
17290
|
+
return (node.children ?? []).filter(isNode);
|
|
17291
|
+
case "JSXCodeBlock":
|
|
17292
|
+
return [
|
|
17293
|
+
...(node.body ?? []).filter(isNode),
|
|
17294
|
+
...(isNode(node.render) ? [node.render] : [])];
|
|
17295
|
+
|
|
17296
|
+
case "BlockStatement":
|
|
17297
|
+
return (node.body ?? []).filter(isNode);
|
|
17298
|
+
case "JSXIfExpression":
|
|
17299
|
+
case "IfStatement":
|
|
17300
|
+
return [node.consequent, node.alternate].filter(isNode);
|
|
17301
|
+
case "JSXForExpression":
|
|
17302
|
+
return [node.body, node.empty].filter(isNode);
|
|
17303
|
+
case "JSXSwitchExpression":
|
|
17304
|
+
case "SwitchStatement":
|
|
17305
|
+
return (node.cases ?? []).flatMap((switchCase) =>
|
|
17306
|
+
(switchCase.consequent ?? []).filter(isNode)
|
|
17307
|
+
);
|
|
17308
|
+
case "JSXTryExpression":
|
|
17309
|
+
case "TryStatement":
|
|
17310
|
+
return [node.block, node.pending, node.handler, node.finalizer].filter(isNode);
|
|
17311
|
+
case "CatchClause":
|
|
17312
|
+
return isNode(node.body) ? [node.body] : [];
|
|
17313
|
+
default:
|
|
17314
|
+
return [];
|
|
17315
|
+
}
|
|
17316
|
+
}
|
|
17317
|
+
|
|
17318
|
+
function collectRuntimeStyleElements(value, styles) {
|
|
17319
|
+
if (Array.isArray(value)) {
|
|
17320
|
+
for (const child of value) collectRuntimeStyleElements(child, styles);
|
|
17321
|
+
return;
|
|
17322
|
+
}
|
|
17323
|
+
if (isStyleElement(value)) {
|
|
17324
|
+
styles.push(value);
|
|
17325
|
+
return;
|
|
17326
|
+
}
|
|
17327
|
+
if (FUNCTION_BOUNDARIES.has(value.type)) return;
|
|
17328
|
+
|
|
17329
|
+
if ((value.type === "JSXElement" || value.type === "JSXFragment") && isNativeRenderRoot(value)) {
|
|
17330
|
+
collectRuntimeStyleElements(value.children ?? [], styles);
|
|
17331
|
+
return;
|
|
17332
|
+
}
|
|
17333
|
+
if (value.type === "BlockStatement") {
|
|
17334
|
+
collectRuntimeStyleElements(value.body ?? [], styles);
|
|
17335
|
+
return;
|
|
17336
|
+
}
|
|
17337
|
+
if (value.type === "JSXIfExpression" || value.type === "IfStatement") {
|
|
17338
|
+
if (isNode(value.consequent)) collectRuntimeStyleElements(value.consequent, styles);
|
|
17339
|
+
if (isNode(value.alternate)) collectRuntimeStyleElements(value.alternate, styles);
|
|
17340
|
+
return;
|
|
17341
|
+
}
|
|
17342
|
+
// This is an intentional ownership boundary in @tsrx/core: a style directly
|
|
17343
|
+
// rendered by an @for body is visited later as an unowned statement style.
|
|
17344
|
+
if (value.type === "JSXForExpression") return;
|
|
17345
|
+
if (value.type === "JSXSwitchExpression" || value.type === "SwitchStatement") {
|
|
17346
|
+
for (const switchCase of value.cases ?? []) {
|
|
17347
|
+
collectRuntimeStyleElements(switchCase.consequent ?? [], styles);
|
|
17348
|
+
}
|
|
17349
|
+
return;
|
|
17350
|
+
}
|
|
17351
|
+
if (value.type === "JSXTryExpression" || value.type === "TryStatement") {
|
|
17352
|
+
if (isNode(value.block)) collectRuntimeStyleElements(value.block, styles);
|
|
17353
|
+
const handler = isNode(value.handler) ? value.handler : undefined;
|
|
17354
|
+
if (handler && isNode(handler.body)) collectRuntimeStyleElements(handler.body, styles);
|
|
17355
|
+
if (isNode(value.finalizer)) collectRuntimeStyleElements(value.finalizer, styles);
|
|
17356
|
+
// `pending` is deliberately absent: core does not assign direct @pending
|
|
17357
|
+
// statement styles to the surrounding fragment.
|
|
17358
|
+
}
|
|
17359
|
+
}
|
|
17360
|
+
|
|
17361
|
+
function collectPrunableElements(
|
|
17362
|
+
value,
|
|
17363
|
+
elements,
|
|
17364
|
+
path = [])
|
|
17365
|
+
{
|
|
17366
|
+
if (Array.isArray(value)) {
|
|
17367
|
+
for (const child of value) collectPrunableElements(child, elements, path);
|
|
17368
|
+
return;
|
|
17369
|
+
}
|
|
17370
|
+
if (FUNCTION_BOUNDARIES.has(value.type) || isStyleElement(value)) return;
|
|
17371
|
+
|
|
17372
|
+
if (value.type === "JSXElement" && isNativeRenderRoot(value)) {
|
|
17373
|
+
const metadata = value.metadata ??= {};
|
|
17374
|
+
metadata.path = path;
|
|
17375
|
+
elements.push(value);
|
|
17376
|
+
}
|
|
17377
|
+
|
|
17378
|
+
const childPath = [...path, value];
|
|
17379
|
+
for (const child of structuralChildren(value)) {
|
|
17380
|
+
collectPrunableElements(child, elements, childPath);
|
|
17381
|
+
}
|
|
17382
|
+
}
|
|
17383
|
+
|
|
17384
|
+
function annotateRuntimeValue(value, hash, core) {
|
|
17385
|
+
if (isStyleElement(value)) return null;
|
|
17386
|
+
if (FUNCTION_BOUNDARIES.has(value.type)) return value;
|
|
17387
|
+
if (value.type === "JSXElement") {
|
|
17388
|
+
return core.annotateWithHash(value, hash, "class", false);
|
|
17389
|
+
}
|
|
17390
|
+
if (value.type === "JSXFragment") {
|
|
17391
|
+
value.children = annotateRuntimeList(
|
|
17392
|
+
value.children ?? [],
|
|
17393
|
+
hash,
|
|
17394
|
+
core
|
|
17395
|
+
);
|
|
17396
|
+
return value;
|
|
17397
|
+
}
|
|
17398
|
+
|
|
17399
|
+
mutateStructuralChildren(value, (child) => annotateRuntimeValue(child, hash, core));
|
|
17400
|
+
return value;
|
|
17401
|
+
}
|
|
17402
|
+
|
|
17403
|
+
function annotateRuntimeList(values, hash, core) {
|
|
17404
|
+
return values.
|
|
17405
|
+
map((value) => annotateRuntimeValue(value, hash, core)).
|
|
17406
|
+
filter((value) => value !== null);
|
|
17407
|
+
}
|
|
17408
|
+
|
|
17409
|
+
function mutateStructuralChildren(node, transform) {
|
|
17410
|
+
const mapList = (values) =>
|
|
17411
|
+
(values ?? []).map(transform).filter((value) => value !== null);
|
|
17412
|
+
const mapSlot = (key) => {
|
|
17413
|
+
const value = node[key];
|
|
17414
|
+
if (isNode(value)) node[key] = transform(value);
|
|
17415
|
+
};
|
|
17416
|
+
|
|
17417
|
+
switch (node.type) {
|
|
17418
|
+
case "JSXElement":
|
|
17419
|
+
case "JSXFragment":
|
|
17420
|
+
node.children = mapList(node.children);
|
|
17421
|
+
break;
|
|
17422
|
+
case "JSXCodeBlock":
|
|
17423
|
+
node.body = mapList(node.body);
|
|
17424
|
+
mapSlot("render");
|
|
17425
|
+
break;
|
|
17426
|
+
case "BlockStatement":
|
|
17427
|
+
node.body = mapList(node.body);
|
|
17428
|
+
break;
|
|
17429
|
+
case "JSXIfExpression":
|
|
17430
|
+
case "IfStatement":
|
|
17431
|
+
mapSlot("consequent");
|
|
17432
|
+
mapSlot("alternate");
|
|
17433
|
+
break;
|
|
17434
|
+
case "JSXForExpression":
|
|
17435
|
+
mapSlot("body");
|
|
17436
|
+
mapSlot("empty");
|
|
17437
|
+
break;
|
|
17438
|
+
case "JSXSwitchExpression":
|
|
17439
|
+
case "SwitchStatement":
|
|
17440
|
+
for (const switchCase of node.cases ?? []) {
|
|
17441
|
+
switchCase.consequent = mapList(switchCase.consequent);
|
|
17442
|
+
}
|
|
17443
|
+
break;
|
|
17444
|
+
case "JSXTryExpression":
|
|
17445
|
+
case "TryStatement":
|
|
17446
|
+
mapSlot("block");
|
|
17447
|
+
mapSlot("pending");
|
|
17448
|
+
mapSlot("handler");
|
|
17449
|
+
mapSlot("finalizer");
|
|
17450
|
+
break;
|
|
17451
|
+
case "CatchClause":
|
|
17452
|
+
mapSlot("body");
|
|
17453
|
+
break;
|
|
17454
|
+
}
|
|
17455
|
+
}
|
|
17456
|
+
|
|
17457
|
+
function cleanRuntimeStyles(node) {
|
|
17458
|
+
if (FUNCTION_BOUNDARIES.has(node.type)) return;
|
|
17459
|
+
mutateStructuralChildren(node, (child) => {
|
|
17460
|
+
if (isStyleElement(child)) return null;
|
|
17461
|
+
cleanRuntimeStyles(child);
|
|
17462
|
+
return child;
|
|
17463
|
+
});
|
|
17464
|
+
}
|
|
17465
|
+
|
|
17466
|
+
function withLoc(node, source) {
|
|
17467
|
+
if (source.start !== undefined) node.start = source.start;
|
|
17468
|
+
if (source.end !== undefined) node.end = source.end;
|
|
17469
|
+
if (source.loc !== undefined) node.loc = source.loc;
|
|
17470
|
+
return node;
|
|
17471
|
+
}
|
|
17472
|
+
|
|
17473
|
+
function wrapWithSetup(expression, statements) {
|
|
17474
|
+
const body = withLoc(
|
|
17475
|
+
{
|
|
17476
|
+
type: "BlockStatement",
|
|
17477
|
+
body: [...statements, withLoc({ type: "ReturnStatement", argument: expression }, expression)]
|
|
17478
|
+
},
|
|
17479
|
+
expression
|
|
17480
|
+
);
|
|
17481
|
+
const fn = withLoc(
|
|
17482
|
+
{
|
|
17483
|
+
type: "ArrowFunctionExpression",
|
|
17484
|
+
id: null,
|
|
17485
|
+
params: [],
|
|
17486
|
+
body,
|
|
17487
|
+
expression: false,
|
|
17488
|
+
async: false,
|
|
17489
|
+
generator: false
|
|
17490
|
+
},
|
|
17491
|
+
expression
|
|
17492
|
+
);
|
|
17493
|
+
return withLoc(
|
|
17494
|
+
{ type: "CallExpression", callee: fn, arguments: [], optional: false },
|
|
17495
|
+
expression
|
|
17496
|
+
);
|
|
17497
|
+
}
|
|
17498
|
+
|
|
17499
|
+
function collectIdentifierNames(node, names) {
|
|
17500
|
+
if (node.type === "Identifier" && typeof node.name === "string") names.add(node.name);
|
|
17501
|
+
for (const key of Object.keys(node)) {
|
|
17502
|
+
if (SKIP_KEYS.has(key)) continue;
|
|
17503
|
+
const value = node[key];
|
|
17504
|
+
if (Array.isArray(value)) {
|
|
17505
|
+
for (const child of value) if (isNode(child)) collectIdentifierNames(child, names);
|
|
17506
|
+
} else if (isNode(value)) {
|
|
17507
|
+
collectIdentifierNames(value, names);
|
|
17508
|
+
}
|
|
17509
|
+
}
|
|
17510
|
+
}
|
|
17511
|
+
|
|
17512
|
+
function createTempIdentifierFactory(program) {
|
|
17513
|
+
const names = new Set();
|
|
17514
|
+
collectIdentifierNames(program, names);
|
|
17515
|
+
let index = 0;
|
|
17516
|
+
return () => {
|
|
17517
|
+
let name;
|
|
17518
|
+
do {
|
|
17519
|
+
name = `_tsrx_style_ref_${++index}`;
|
|
17520
|
+
} while (names.has(name));
|
|
17521
|
+
names.add(name);
|
|
17522
|
+
return { type: "Identifier", name };
|
|
17523
|
+
};
|
|
17524
|
+
}
|
|
17525
|
+
|
|
17526
|
+
function createEmptyStyleElement(node) {
|
|
17527
|
+
const metadata = node.metadata ?? {};
|
|
17528
|
+
const cloneElementPart = (part) => {
|
|
17529
|
+
if (!isNode(part)) return part;
|
|
17530
|
+
return {
|
|
17531
|
+
...part,
|
|
17532
|
+
metadata: {
|
|
17533
|
+
path: [...(metadata.path ?? [])]
|
|
17534
|
+
}
|
|
17535
|
+
};
|
|
17536
|
+
};
|
|
17537
|
+
return {
|
|
17538
|
+
...node,
|
|
17539
|
+
type: "JSXElement",
|
|
17540
|
+
openingElement: cloneElementPart(node.openingElement),
|
|
17541
|
+
closingElement: cloneElementPart(node.closingElement),
|
|
17542
|
+
children: []
|
|
17543
|
+
};
|
|
17544
|
+
}
|
|
17545
|
+
|
|
17546
|
+
function prepareRuntimeStyle(
|
|
17547
|
+
owner,
|
|
17548
|
+
style,
|
|
17549
|
+
core,
|
|
17550
|
+
createTempIdentifier)
|
|
17551
|
+
{
|
|
17552
|
+
const stylesheet = core.getStyleElementStylesheet(style);
|
|
17553
|
+
if (!stylesheet) {
|
|
17554
|
+
throw new SyntaxError("A TSRX <style> element is missing its parsed stylesheet");
|
|
17555
|
+
}
|
|
17556
|
+
|
|
17557
|
+
core.analyzeCss(stylesheet);
|
|
17558
|
+
const metadata = owner.metadata ??= {};
|
|
17559
|
+
const styleClasses = new Map();
|
|
17560
|
+
const topScopedClasses = new Map();
|
|
17561
|
+
|
|
17562
|
+
const elements = [];
|
|
17563
|
+
collectPrunableElements(owner.children ?? [], elements);
|
|
17564
|
+
for (const element of elements) {
|
|
17565
|
+
core.pruneCss(stylesheet, element, styleClasses, topScopedClasses, stylesheet.hash);
|
|
17566
|
+
}
|
|
17567
|
+
|
|
17568
|
+
const refs = core.collectStyleRefAttributes(owner);
|
|
17569
|
+
if (refs.length > 0) {
|
|
17570
|
+
for (const [name, classInfo] of topScopedClasses) {
|
|
17571
|
+
styleClasses.set(
|
|
17572
|
+
name,
|
|
17573
|
+
classInfo?.selector ?? classInfo
|
|
17574
|
+
);
|
|
17575
|
+
}
|
|
17576
|
+
for (const element of elements) {
|
|
17577
|
+
core.pruneCss(stylesheet, element, styleClasses, topScopedClasses, stylesheet.hash);
|
|
17578
|
+
}
|
|
17579
|
+
}
|
|
17580
|
+
if (topScopedClasses.size > 0) metadata.topScopedClasses = topScopedClasses;
|
|
17581
|
+
|
|
17582
|
+
const setup =
|
|
17583
|
+
refs.length === 0 ?
|
|
17584
|
+
[] :
|
|
17585
|
+
core.createStyleRefSetupStatements(refs, core.createStyleClassMap(owner, stylesheet), {
|
|
17586
|
+
allowMutableRefTarget: true,
|
|
17587
|
+
createTempIdentifier
|
|
17588
|
+
});
|
|
17589
|
+
|
|
17590
|
+
mutateStructuralChildren(owner, (child) => annotateRuntimeValue(child, stylesheet.hash, core));
|
|
17591
|
+
cleanRuntimeStyles(owner);
|
|
17592
|
+
return { owner, stylesheet, setup };
|
|
17593
|
+
}
|
|
17594
|
+
|
|
17595
|
+
function prepareStyleOwner(
|
|
17596
|
+
owner,
|
|
17597
|
+
core,
|
|
17598
|
+
createTempIdentifier)
|
|
17599
|
+
{
|
|
17600
|
+
const styles = [];
|
|
17601
|
+
collectRuntimeStyleElements(owner.children ?? [], styles);
|
|
17602
|
+
if (styles.length > 1) {
|
|
17603
|
+
throw new SyntaxError("TSRX fragments can only have one style tag");
|
|
17604
|
+
}
|
|
17605
|
+
return styles.length === 1 ?
|
|
17606
|
+
prepareRuntimeStyle(owner, styles[0], core, createTempIdentifier) :
|
|
17607
|
+
null;
|
|
17608
|
+
}
|
|
17609
|
+
|
|
17610
|
+
function processTsrxStyles(program, core) {
|
|
17611
|
+
const stylesheets = [];
|
|
17612
|
+
const createTempIdentifier = createTempIdentifierFactory(program);
|
|
17613
|
+
|
|
17614
|
+
const visit = (node, path) => {
|
|
17615
|
+
if (isStyleElement(node)) {
|
|
17616
|
+
if (!isStyleExpressionPosition(path)) return createEmptyStyleElement(node);
|
|
17617
|
+
const stylesheet = core.getStyleElementStylesheet(node);
|
|
17618
|
+
if (!stylesheet) {
|
|
17619
|
+
throw new SyntaxError("A TSRX <style> expression is missing its parsed stylesheet");
|
|
17620
|
+
}
|
|
17621
|
+
core.analyzeCss(stylesheet);
|
|
17622
|
+
stylesheets.push(core.prepareStylesheetForRender(stylesheet, true));
|
|
17623
|
+
return core.createStyleClassMapFromStylesheet(stylesheet);
|
|
17624
|
+
}
|
|
17625
|
+
|
|
17626
|
+
if (node.type === "JSXCodeBlock" && isNode(node.render) && isNativeRenderRoot(node.render)) {
|
|
17627
|
+
// Core gives a code block's native element/fragment render one style
|
|
17628
|
+
// scope. A native element elsewhere is deliberately not an owner.
|
|
17629
|
+
const prepared = prepareStyleOwner(node.render, core, createTempIdentifier);
|
|
17630
|
+
if (prepared) {
|
|
17631
|
+
stylesheets.push(prepared.stylesheet);
|
|
17632
|
+
node.render = prepared.owner;
|
|
17633
|
+
node.body = [...(node.body ?? []), ...prepared.setup];
|
|
17634
|
+
}
|
|
17635
|
+
} else if (node.type === "JSXFragment" && isNativeRenderRoot(node)) {
|
|
17636
|
+
// Native fragments are owners in every expression/child position.
|
|
17637
|
+
const prepared = prepareStyleOwner(node, core, createTempIdentifier);
|
|
17638
|
+
if (prepared) {
|
|
17639
|
+
stylesheets.push(prepared.stylesheet);
|
|
17640
|
+
node = prepared.setup.length ?
|
|
17641
|
+
wrapWithSetup(prepared.owner, prepared.setup) :
|
|
17642
|
+
prepared.owner;
|
|
17643
|
+
}
|
|
17644
|
+
}
|
|
17645
|
+
|
|
17646
|
+
for (const key of Object.keys(node)) {
|
|
17647
|
+
if (SKIP_KEYS.has(key)) continue;
|
|
17648
|
+
const value = node[key];
|
|
17649
|
+
if (Array.isArray(value)) {
|
|
17650
|
+
node[key] = value.map((child) => isNode(child) ? visit(child, [...path, node]) : child);
|
|
17651
|
+
} else if (isNode(value)) {
|
|
17652
|
+
node[key] = visit(value, [...path, node]);
|
|
17653
|
+
}
|
|
17654
|
+
}
|
|
17655
|
+
return node;
|
|
17656
|
+
};
|
|
17657
|
+
|
|
17658
|
+
visit(program, []);
|
|
17659
|
+
return core.renderCssResult(stylesheets);
|
|
17660
|
+
}
|
|
17661
|
+
|
|
17662
|
+
/**
|
|
17663
|
+
* TSRX syntax frontend entry point.
|
|
17664
|
+
*
|
|
17665
|
+
* Routes `.tsrx` sources (or any source when `syntax: "tsrx"`) through
|
|
17666
|
+
* `@tsrx/core`'s parser + semantic analysis, processes scoped styles with
|
|
17667
|
+
* core's CSS helpers, desugars every TSRX construct to Solid builtIn JSX,
|
|
17668
|
+
* applies Solid's local deferred-pattern transform for generated control-flow
|
|
17669
|
+
* callbacks, and converts the result to a Babel `File` for the unchanged JSX
|
|
17670
|
+
* pipeline. Authored lazy destructuring is rejected for the Solid target.
|
|
17671
|
+
*
|
|
17672
|
+
* `@tsrx/core` is an optional peer dependency loaded lazily on first TSRX
|
|
17673
|
+
* routing, so plain JSX users never pay for it.
|
|
17674
|
+
*/
|
|
17675
|
+
|
|
17676
|
+
|
|
17677
|
+
|
|
17678
|
+
// The plugin ships as CJS; `require` of the ESM-only `@tsrx/core` is
|
|
17679
|
+
// supported natively on Node >= 22.12. No @types/node in this package.
|
|
17680
|
+
|
|
17681
|
+
|
|
17682
|
+
|
|
17683
|
+
|
|
17684
|
+
|
|
17685
|
+
|
|
17686
|
+
|
|
17687
|
+
|
|
17688
|
+
|
|
17689
|
+
|
|
17690
|
+
|
|
17691
|
+
let core;
|
|
17692
|
+
|
|
17693
|
+
function loadTsrxCore() {
|
|
17694
|
+
if (core) return core;
|
|
17695
|
+
if (typeof require !== "function") {
|
|
17696
|
+
throw new Error(
|
|
17697
|
+
"@solidjs/babel-plugin: TSRX support requires a CommonJS environment with `require` available."
|
|
17698
|
+
);
|
|
17699
|
+
}
|
|
17700
|
+
try {
|
|
17701
|
+
core = require("@tsrx/core");
|
|
17702
|
+
} catch (error) {
|
|
17703
|
+
const err = error;
|
|
17704
|
+
if (err.code === "MODULE_NOT_FOUND") {
|
|
17705
|
+
throw new Error(
|
|
17706
|
+
"@solidjs/babel-plugin: compiling .tsrx sources requires the optional peer dependency `@tsrx/core`. Install it with your package manager (e.g. `pnpm add -D @tsrx/core`)."
|
|
17707
|
+
);
|
|
17708
|
+
}
|
|
17709
|
+
if (err.code === "ERR_REQUIRE_ESM" || err.code === "ERR_REQUIRE_ASYNC_MODULE") {
|
|
17710
|
+
throw new Error(
|
|
17711
|
+
"@solidjs/babel-plugin: loading `@tsrx/core` synchronously requires Node.js >= 22.12 (require(esm) support)."
|
|
17712
|
+
);
|
|
17713
|
+
}
|
|
17714
|
+
throw error;
|
|
17715
|
+
}
|
|
17716
|
+
return core;
|
|
17717
|
+
}
|
|
17718
|
+
|
|
17719
|
+
|
|
17720
|
+
|
|
17721
|
+
/** Whether the given file should be parsed as TSRX under the configured
|
|
17722
|
+
* `syntax` option. */
|
|
17723
|
+
function isTsrxSource(syntax, filename) {
|
|
17724
|
+
const mode = syntax ?? "auto";
|
|
17725
|
+
if (mode === "tsrx") return true;
|
|
17726
|
+
if (mode === "jsx") return false;
|
|
17727
|
+
return typeof filename === "string" && filename.endsWith(".tsrx");
|
|
17728
|
+
}
|
|
17729
|
+
|
|
17730
|
+
/** Parse TSRX source text into a Babel `File` with all TSRX constructs
|
|
17731
|
+
* lowered to Solid builtIn JSX. */
|
|
17732
|
+
function parseTsrx(code, filename) {
|
|
17733
|
+
const tsrx = loadTsrxCore();
|
|
17734
|
+
const program = tsrx.parseModule(code, filename ?? "module.tsrx");
|
|
17735
|
+
rejectAuthoredLazyDestructuring(program);
|
|
17736
|
+
// Throws structured diagnostics (invalid returns, misplaced directives, …).
|
|
17737
|
+
tsrx.analyzeTsrx(program, filename ?? null);
|
|
17738
|
+
const styleResult = processTsrxStyles(program, tsrx);
|
|
17739
|
+
|
|
17740
|
+
// Desugar before the lazy transform: the lazy engine collects block-level
|
|
17741
|
+
// `let &[…]`/`const &{…}` bindings in its BlockStatement/Program handlers,
|
|
17742
|
+
// which only fire once `@{}` containers have become real blocks. Lazy
|
|
17743
|
+
// patterns themselves pass through the desugarer untouched.
|
|
17744
|
+
desugarProgram(program);
|
|
17745
|
+
|
|
17746
|
+
const transformed = applyLazyTransforms(program);
|
|
17747
|
+
// Keep this compatibility repair in place for trees produced by older
|
|
17748
|
+
// desugaring paths. The local engine itself never rewrites intrinsic names.
|
|
17749
|
+
restoreIntrinsicJsxNames(transformed);
|
|
17750
|
+
|
|
17751
|
+
const file = toBabelFile(transformed);
|
|
17752
|
+
// Babel's default AST clone preserves enumerable string keys. Program.enter
|
|
17753
|
+
// lifts this temporary payload into transform metadata and removes it from
|
|
17754
|
+
// the AST, avoiding mutable plugin-factory state (and concurrent-run races).
|
|
17755
|
+
file.tsrxStyle = styleResult;
|
|
17756
|
+
return file;
|
|
17757
|
+
}
|
|
17758
|
+
|
|
17759
|
+
var index = (
|
|
17760
|
+
_api,
|
|
17761
|
+
options = {}) =>
|
|
17762
|
+
|
|
17763
|
+
|
|
17764
|
+
|
|
17765
|
+
|
|
17766
|
+
|
|
17767
|
+
{
|
|
17768
|
+
return {
|
|
17769
|
+
name: "@solidjs/babel-plugin",
|
|
17770
|
+
inherits: SyntaxJSX.default,
|
|
17771
|
+
parserOverride(code, parserOpts, parse) {
|
|
17772
|
+
if (isTsrxSource(options.syntax, parserOpts?.sourceFileName)) {
|
|
17773
|
+
return parseTsrx(code, parserOpts?.sourceFileName);
|
|
17774
|
+
}
|
|
17775
|
+
return parse(code, parserOpts);
|
|
17776
|
+
},
|
|
14638
17777
|
visitor: {
|
|
14639
17778
|
JSXElement: transformJSX,
|
|
14640
17779
|
JSXFragment: transformJSX,
|