@barefootjs/jsx 0.10.0 → 0.11.0
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/dist/compiler.d.ts.map +1 -1
- package/dist/debug-profile.d.ts +115 -0
- package/dist/debug-profile.d.ts.map +1 -0
- package/dist/debug.d.ts +4 -3
- package/dist/debug.d.ts.map +1 -1
- package/dist/expression-parser.d.ts +31 -0
- package/dist/expression-parser.d.ts.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1905 -207
- package/dist/ir-to-client-js/control-flow/plan/branch-loop.d.ts +6 -0
- package/dist/ir-to-client-js/control-flow/plan/branch-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-branch-loop.d.ts +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-branch-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-component-loop.d.ts +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-component-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-composite-loop.d.ts +2 -2
- package/dist/ir-to-client-js/control-flow/plan/build-composite-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-event-delegation.d.ts +3 -3
- package/dist/ir-to-client-js/control-flow/plan/build-event-delegation.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-inner-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-insert.d.ts +2 -0
- package/dist/ir-to-client-js/control-flow/plan/build-insert.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-loop-child-arm.d.ts +2 -0
- package/dist/ir-to-client-js/control-flow/plan/build-loop-child-arm.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-loop.d.ts +4 -2
- package/dist/ir-to-client-js/control-flow/plan/build-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-reactive-effects.d.ts +3 -1
- package/dist/ir-to-client-js/control-flow/plan/build-reactive-effects.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/event-delegation.d.ts +7 -0
- package/dist/ir-to-client-js/control-flow/plan/event-delegation.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/inner-loop.d.ts +7 -0
- package/dist/ir-to-client-js/control-flow/plan/inner-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/insert.d.ts +8 -0
- package/dist/ir-to-client-js/control-flow/plan/insert.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/loop-child-arm.d.ts +8 -0
- package/dist/ir-to-client-js/control-flow/plan/loop-child-arm.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/loop.d.ts +28 -0
- package/dist/ir-to-client-js/control-flow/plan/loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/reactive-effects.d.ts +7 -0
- package/dist/ir-to-client-js/control-flow/plan/reactive-effects.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/component-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/composite-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/event-delegation.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/event-listener.d.ts +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/event-listener.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/inner-loop.d.ts +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/inner-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/insert.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/loop-child-arm.d.ts +2 -2
- package/dist/ir-to-client-js/control-flow/stringify/loop-child-arm.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/reactive-effects.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow.d.ts.map +1 -1
- package/dist/ir-to-client-js/emit-reactive.d.ts.map +1 -1
- package/dist/ir-to-client-js/imports.d.ts +2 -2
- package/dist/ir-to-client-js/imports.d.ts.map +1 -1
- package/dist/ir-to-client-js/index.d.ts +2 -2
- package/dist/ir-to-client-js/index.d.ts.map +1 -1
- package/dist/ir-to-client-js/phases/effects-and-on-mounts.d.ts.map +1 -1
- package/dist/ir-to-client-js/phases/event-handlers.d.ts.map +1 -1
- package/dist/ir-to-client-js/plan/build-declaration-emit.d.ts.map +1 -1
- package/dist/ir-to-client-js/plan/declaration-emit.d.ts +6 -0
- package/dist/ir-to-client-js/plan/declaration-emit.d.ts.map +1 -1
- package/dist/ir-to-client-js/types.d.ts +5 -0
- package/dist/ir-to-client-js/types.d.ts.map +1 -1
- package/dist/ir-to-client-js/utils.d.ts +29 -0
- package/dist/ir-to-client-js/utils.d.ts.map +1 -1
- package/dist/loop-destructure.d.ts +26 -0
- package/dist/loop-destructure.d.ts.map +1 -0
- package/dist/profiler.d.ts +492 -0
- package/dist/profiler.d.ts.map +1 -0
- package/dist/ssr-defaults.d.ts.map +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/debug-profile.test.ts +405 -0
- package/src/__tests__/expression-parser.test.ts +44 -1
- package/src/__tests__/profile-bfid-emission.test.ts +63 -0
- package/src/__tests__/profile-binding-ids.test.ts +123 -0
- package/src/__tests__/profile-cond-binding-ids.test.ts +80 -0
- package/src/__tests__/profile-loop-binding-ids.test.ts +106 -0
- package/src/__tests__/profile-nested-binding-ids.test.ts +153 -0
- package/src/__tests__/profile-turn-markers-branch.test.ts +83 -0
- package/src/__tests__/profile-turn-markers-delegation.test.ts +63 -0
- package/src/__tests__/profile-turn-markers.test.ts +54 -0
- package/src/__tests__/profiler-batch-advisor.test.ts +198 -0
- package/src/__tests__/profiler-coverage-conformance.test.ts +360 -0
- package/src/__tests__/profiler-e2e.test.ts +104 -0
- package/src/__tests__/profiler-hot-subscribers.test.ts +263 -0
- package/src/__tests__/profiler-wasted-re-runs.test.ts +147 -0
- package/src/__tests__/profiler.test.ts +408 -0
- package/src/__tests__/ssr-defaults.test.ts +24 -0
- package/src/compiler.ts +3 -0
- package/src/debug-profile.ts +543 -0
- package/src/debug.ts +192 -28
- package/src/expression-parser.ts +53 -0
- package/src/index.ts +72 -1
- package/src/ir-to-client-js/control-flow/plan/branch-loop.ts +6 -0
- package/src/ir-to-client-js/control-flow/plan/build-branch-loop.ts +5 -3
- package/src/ir-to-client-js/control-flow/plan/build-component-loop.ts +3 -1
- package/src/ir-to-client-js/control-flow/plan/build-composite-loop.ts +8 -2
- package/src/ir-to-client-js/control-flow/plan/build-event-delegation.ts +19 -3
- package/src/ir-to-client-js/control-flow/plan/build-inner-loop.ts +2 -0
- package/src/ir-to-client-js/control-flow/plan/build-insert.ts +9 -2
- package/src/ir-to-client-js/control-flow/plan/build-loop-child-arm.ts +9 -1
- package/src/ir-to-client-js/control-flow/plan/build-loop.ts +12 -8
- package/src/ir-to-client-js/control-flow/plan/build-reactive-effects.ts +10 -4
- package/src/ir-to-client-js/control-flow/plan/event-delegation.ts +7 -0
- package/src/ir-to-client-js/control-flow/plan/inner-loop.ts +7 -0
- package/src/ir-to-client-js/control-flow/plan/insert.ts +8 -0
- package/src/ir-to-client-js/control-flow/plan/loop-child-arm.ts +8 -0
- package/src/ir-to-client-js/control-flow/plan/loop.ts +28 -0
- package/src/ir-to-client-js/control-flow/plan/reactive-effects.ts +7 -0
- package/src/ir-to-client-js/control-flow/stringify/branch-loop.ts +5 -3
- package/src/ir-to-client-js/control-flow/stringify/component-loop.ts +4 -2
- package/src/ir-to-client-js/control-flow/stringify/composite-loop.ts +6 -3
- package/src/ir-to-client-js/control-flow/stringify/event-delegation.ts +14 -2
- package/src/ir-to-client-js/control-flow/stringify/event-listener.ts +5 -2
- package/src/ir-to-client-js/control-flow/stringify/inner-loop.ts +13 -11
- package/src/ir-to-client-js/control-flow/stringify/insert.ts +19 -7
- package/src/ir-to-client-js/control-flow/stringify/loop-child-arm.ts +18 -13
- package/src/ir-to-client-js/control-flow/stringify/loop.ts +9 -7
- package/src/ir-to-client-js/control-flow/stringify/reactive-effects.ts +18 -14
- package/src/ir-to-client-js/control-flow.ts +12 -6
- package/src/ir-to-client-js/emit-reactive.ts +18 -5
- package/src/ir-to-client-js/imports.ts +2 -0
- package/src/ir-to-client-js/index.ts +6 -1
- package/src/ir-to-client-js/phases/effects-and-on-mounts.ts +10 -4
- package/src/ir-to-client-js/phases/event-handlers.ts +6 -2
- package/src/ir-to-client-js/plan/build-declaration-emit.ts +7 -1
- package/src/ir-to-client-js/plan/declaration-emit.ts +6 -0
- package/src/ir-to-client-js/stringify/declaration-emit.ts +12 -6
- package/src/ir-to-client-js/types.ts +5 -0
- package/src/ir-to-client-js/utils.ts +37 -0
- package/src/jsx-to-ir.ts +2 -2
- package/src/loop-destructure.ts +170 -0
- package/src/profiler.ts +1488 -0
- package/src/ssr-defaults.ts +65 -0
- package/src/types.ts +8 -0
package/dist/index.js
CHANGED
|
@@ -232,6 +232,11 @@ function keyAttrName(loopDepth) {
|
|
|
232
232
|
function varSlotId(slotId) {
|
|
233
233
|
return slotId.startsWith("^") ? slotId.slice(1) : slotId;
|
|
234
234
|
}
|
|
235
|
+
function profileBindingId(componentName, slotId) {
|
|
236
|
+
if (!componentName || slotId === "?")
|
|
237
|
+
return "";
|
|
238
|
+
return `, ${JSON.stringify(`${componentName}#binding:${slotId}`)}`;
|
|
239
|
+
}
|
|
235
240
|
function templatePartsToJsExpr(parts, opts) {
|
|
236
241
|
let result = "`";
|
|
237
242
|
for (const part of parts) {
|
|
@@ -312,6 +317,10 @@ function wrapHandlerInBlock(handler) {
|
|
|
312
317
|
}
|
|
313
318
|
return trimmed;
|
|
314
319
|
}
|
|
320
|
+
function wrapHandlerForTurn(handler, handlerId, loc) {
|
|
321
|
+
const idArg = loc ? `${JSON.stringify(handlerId)}, ${JSON.stringify(loc)}` : JSON.stringify(handlerId);
|
|
322
|
+
return `(...__bfa) => { beginTurn(${idArg}); try { return (${handler.trim()})(...__bfa) } finally { endTurn() } }`;
|
|
323
|
+
}
|
|
315
324
|
function emitRefCall(callback, elementVar) {
|
|
316
325
|
const trimmed = callback.trim();
|
|
317
326
|
const isBareIdent = /^[a-zA-Z_$][\w$]*$/.test(trimmed);
|
|
@@ -5373,6 +5382,40 @@ function extractArrowBodyExpression(source) {
|
|
|
5373
5382
|
return null;
|
|
5374
5383
|
return expr.body.getText(sf).trim();
|
|
5375
5384
|
}
|
|
5385
|
+
function cssKebabCase(name) {
|
|
5386
|
+
return name.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase()).replace(/^ms-/, "-ms-");
|
|
5387
|
+
}
|
|
5388
|
+
function parseStyleObjectEntries(source) {
|
|
5389
|
+
const sf = ts8.createSourceFile("__style__.ts", `(${source})`, ts8.ScriptTarget.Latest, true);
|
|
5390
|
+
const stmt = sf.statements[0];
|
|
5391
|
+
if (!stmt || !ts8.isExpressionStatement(stmt) || sf.statements.length !== 1)
|
|
5392
|
+
return null;
|
|
5393
|
+
let expr = stmt.expression;
|
|
5394
|
+
while (ts8.isParenthesizedExpression(expr))
|
|
5395
|
+
expr = expr.expression;
|
|
5396
|
+
if (!ts8.isObjectLiteralExpression(expr))
|
|
5397
|
+
return null;
|
|
5398
|
+
const entries = [];
|
|
5399
|
+
for (const prop of expr.properties) {
|
|
5400
|
+
if (!ts8.isPropertyAssignment(prop))
|
|
5401
|
+
return null;
|
|
5402
|
+
let key;
|
|
5403
|
+
if (ts8.isIdentifier(prop.name))
|
|
5404
|
+
key = prop.name.text;
|
|
5405
|
+
else if (ts8.isStringLiteral(prop.name))
|
|
5406
|
+
key = prop.name.text;
|
|
5407
|
+
else
|
|
5408
|
+
return null;
|
|
5409
|
+
const cssKey = cssKebabCase(key);
|
|
5410
|
+
const init = prop.initializer;
|
|
5411
|
+
if (ts8.isStringLiteral(init) || ts8.isNoSubstitutionTemplateLiteral(init)) {
|
|
5412
|
+
entries.push({ cssKey, kind: "literal", value: init.text });
|
|
5413
|
+
} else {
|
|
5414
|
+
entries.push({ cssKey, kind: "expr", expr: init.getText(sf).trim() });
|
|
5415
|
+
}
|
|
5416
|
+
}
|
|
5417
|
+
return entries.length > 0 ? entries : null;
|
|
5418
|
+
}
|
|
5376
5419
|
function parseExpression(expr) {
|
|
5377
5420
|
const trimmed = expr.trim();
|
|
5378
5421
|
if (!trimmed) {
|
|
@@ -9221,7 +9264,7 @@ function tryStaticStyleObjectToCss(expr) {
|
|
|
9221
9264
|
return null;
|
|
9222
9265
|
if (!ts11.isStringLiteral(prop.initializer))
|
|
9223
9266
|
return null;
|
|
9224
|
-
const key = prop.name.text
|
|
9267
|
+
const key = cssKebabCase(prop.name.text);
|
|
9225
9268
|
parts.push(`${key}:${prop.initializer.text}`);
|
|
9226
9269
|
}
|
|
9227
9270
|
return parts.join(";");
|
|
@@ -11331,7 +11374,9 @@ var RUNTIME_IMPORT_CANDIDATES = [
|
|
|
11331
11374
|
"upsertChildItem",
|
|
11332
11375
|
"__slot",
|
|
11333
11376
|
"__bfSlot",
|
|
11334
|
-
"__bfText"
|
|
11377
|
+
"__bfText",
|
|
11378
|
+
"beginTurn",
|
|
11379
|
+
"endTurn"
|
|
11335
11380
|
];
|
|
11336
11381
|
var RUNTIME_MODULE = "@barefootjs/client/runtime";
|
|
11337
11382
|
var IMPORT_PLACEHOLDER = "/* __BAREFOOTJS_DOM_IMPORTS__ */";
|
|
@@ -12520,7 +12565,8 @@ function buildDeclarationEmitPlan(decl, ctx, lookups) {
|
|
|
12520
12565
|
return {
|
|
12521
12566
|
kind: "memo",
|
|
12522
12567
|
name: decl.info.name,
|
|
12523
|
-
computationExpr: decl.info.computation
|
|
12568
|
+
computationExpr: decl.info.computation,
|
|
12569
|
+
bfId: ctx.profile ? `${ctx.componentName}#memo:${decl.info.name}` : undefined
|
|
12524
12570
|
};
|
|
12525
12571
|
case "function": {
|
|
12526
12572
|
const fn = decl.info;
|
|
@@ -12538,13 +12584,18 @@ function buildDeclarationEmitPlan(decl, ctx, lookups) {
|
|
|
12538
12584
|
}
|
|
12539
12585
|
}
|
|
12540
12586
|
function buildSignalPlan(signal, ctx, lookups) {
|
|
12587
|
+
const controlledEffect = buildControlledSignalEffect(signal, lookups);
|
|
12588
|
+
if (controlledEffect && ctx.profile) {
|
|
12589
|
+
controlledEffect.bfId = `${ctx.componentName}#effect:controlled:${signal.setter}`;
|
|
12590
|
+
}
|
|
12541
12591
|
return {
|
|
12542
12592
|
kind: "signal",
|
|
12543
12593
|
getter: signal.getter,
|
|
12544
12594
|
setter: signal.setter,
|
|
12545
12595
|
initialValueExpr: resolveSignalInitialValue(signal, ctx, lookups),
|
|
12546
|
-
controlledEffect
|
|
12547
|
-
branchCondition: signal.branchCondition
|
|
12596
|
+
controlledEffect,
|
|
12597
|
+
branchCondition: signal.branchCondition,
|
|
12598
|
+
bfId: ctx.profile ? `${ctx.componentName}#signal:${signal.getter}` : undefined
|
|
12548
12599
|
};
|
|
12549
12600
|
}
|
|
12550
12601
|
function resolveSignalInitialValue(signal, ctx, lookups) {
|
|
@@ -12608,25 +12659,29 @@ function emitConstant(lines, plan) {
|
|
|
12608
12659
|
lines.push(` ${plan.keyword} ${plan.name}`);
|
|
12609
12660
|
}
|
|
12610
12661
|
}
|
|
12662
|
+
function bfIdArg(bfId) {
|
|
12663
|
+
return bfId ? `, ${JSON.stringify(bfId)}` : "";
|
|
12664
|
+
}
|
|
12611
12665
|
function emitSignal(lines, plan) {
|
|
12666
|
+
const id = bfIdArg(plan.bfId);
|
|
12612
12667
|
if (plan.branchCondition) {
|
|
12613
12668
|
if (plan.setter) {
|
|
12614
12669
|
lines.push(` let ${plan.getter}, ${plan.setter}`);
|
|
12615
12670
|
lines.push(` if (${plan.branchCondition}) {`);
|
|
12616
|
-
lines.push(` ;[${plan.getter}, ${plan.setter}] = createSignal(${plan.initialValueExpr})`);
|
|
12671
|
+
lines.push(` ;[${plan.getter}, ${plan.setter}] = createSignal(${plan.initialValueExpr}${id})`);
|
|
12617
12672
|
lines.push(` }`);
|
|
12618
12673
|
} else {
|
|
12619
12674
|
lines.push(` let ${plan.getter}`);
|
|
12620
12675
|
lines.push(` if (${plan.branchCondition}) {`);
|
|
12621
|
-
lines.push(` ;[${plan.getter}] = createSignal(${plan.initialValueExpr})`);
|
|
12676
|
+
lines.push(` ;[${plan.getter}] = createSignal(${plan.initialValueExpr}${id})`);
|
|
12622
12677
|
lines.push(` }`);
|
|
12623
12678
|
}
|
|
12624
12679
|
return;
|
|
12625
12680
|
}
|
|
12626
12681
|
if (plan.setter) {
|
|
12627
|
-
lines.push(` const [${plan.getter}, ${plan.setter}] = createSignal(${plan.initialValueExpr})`);
|
|
12682
|
+
lines.push(` const [${plan.getter}, ${plan.setter}] = createSignal(${plan.initialValueExpr}${id})`);
|
|
12628
12683
|
} else {
|
|
12629
|
-
lines.push(` const [${plan.getter}] = createSignal(${plan.initialValueExpr})`);
|
|
12684
|
+
lines.push(` const [${plan.getter}] = createSignal(${plan.initialValueExpr}${id})`);
|
|
12630
12685
|
}
|
|
12631
12686
|
if (plan.controlledEffect) {
|
|
12632
12687
|
emitControlledEffect(lines, plan.controlledEffect);
|
|
@@ -12636,10 +12691,10 @@ function emitControlledEffect(lines, plan) {
|
|
|
12636
12691
|
lines.push(` createEffect(() => {`);
|
|
12637
12692
|
lines.push(` const __val = ${plan.accessorExpr}`);
|
|
12638
12693
|
lines.push(` if (__val !== undefined) ${plan.setter}(__val)`);
|
|
12639
|
-
lines.push(` })`);
|
|
12694
|
+
lines.push(` }${bfIdArg(plan.bfId)})`);
|
|
12640
12695
|
}
|
|
12641
12696
|
function emitMemo(lines, plan) {
|
|
12642
|
-
lines.push(` const ${plan.name} = createMemo(${plan.computationExpr})`);
|
|
12697
|
+
lines.push(` const ${plan.name} = createMemo(${plan.computationExpr}${bfIdArg(plan.bfId)})`);
|
|
12643
12698
|
}
|
|
12644
12699
|
function emitFunction(lines, plan) {
|
|
12645
12700
|
const asyncKw = plan.isAsync ? "async " : "";
|
|
@@ -12851,13 +12906,14 @@ function collectConditionalSlotIds(ctx) {
|
|
|
12851
12906
|
|
|
12852
12907
|
// src/ir-to-client-js/phases/effects-and-on-mounts.ts
|
|
12853
12908
|
function emitEffectsAndOnMounts(lines, ctx) {
|
|
12854
|
-
|
|
12909
|
+
ctx.effects.forEach((effect, i) => {
|
|
12910
|
+
const idArg = ctx.profile ? `, ${JSON.stringify(`${ctx.componentName}#effect:${effect.captureName ?? effect.loc?.start.line ?? i}`)}` : "";
|
|
12855
12911
|
if (effect.captureName) {
|
|
12856
|
-
lines.push(` const ${effect.captureName} = createEffect(${effect.body})`);
|
|
12912
|
+
lines.push(` const ${effect.captureName} = createEffect(${effect.body}${idArg})`);
|
|
12857
12913
|
} else {
|
|
12858
|
-
lines.push(` createEffect(${effect.body})`);
|
|
12914
|
+
lines.push(` createEffect(${effect.body}${idArg})`);
|
|
12859
12915
|
}
|
|
12860
|
-
}
|
|
12916
|
+
});
|
|
12861
12917
|
for (const onMount of ctx.onMounts) {
|
|
12862
12918
|
lines.push(` onMount(${onMount.body})`);
|
|
12863
12919
|
}
|
|
@@ -12870,7 +12926,7 @@ function emitEventHandlers(lines, ctx, conditionalSlotIds) {
|
|
|
12870
12926
|
continue;
|
|
12871
12927
|
for (const event of elem.events) {
|
|
12872
12928
|
const eventName = toDomEventName(event.name);
|
|
12873
|
-
const wrappedHandler = wrapHandlerInBlock(event.handler);
|
|
12929
|
+
const wrappedHandler = ctx.profile ? wrapHandlerForTurn(event.handler, `${ctx.componentName}#handler:${elem.slotId}:${event.name}`) : wrapHandlerInBlock(event.handler);
|
|
12874
12930
|
if (elem.slotId === "__scope") {
|
|
12875
12931
|
lines.push(` if (__scope) __scope.addEventListener('${eventName}', ${wrappedHandler})`);
|
|
12876
12932
|
} else {
|
|
@@ -13002,8 +13058,8 @@ function emitRestAttrApplications(lines, ctx) {
|
|
|
13002
13058
|
}
|
|
13003
13059
|
|
|
13004
13060
|
// src/ir-to-client-js/control-flow/stringify/event-listener.ts
|
|
13005
|
-
function emitListenerLine(lines, indent, elementVar, eventName, handler, mode = "dom") {
|
|
13006
|
-
const wrapped = wrapHandlerInBlock(handler);
|
|
13061
|
+
function emitListenerLine(lines, indent, elementVar, eventName, handler, mode = "dom", turnId) {
|
|
13062
|
+
const wrapped = turnId ? wrapHandlerForTurn(handler, turnId) : wrapHandlerInBlock(handler);
|
|
13007
13063
|
const name = mode === "dom" ? toDomEventName(eventName) : eventName;
|
|
13008
13064
|
lines.push(`${indent}if (${elementVar}) ${elementVar}.addEventListener('${name}', ${wrapped})`);
|
|
13009
13065
|
}
|
|
@@ -13432,7 +13488,7 @@ function wrapAttrValueExpression(value, wrap) {
|
|
|
13432
13488
|
}
|
|
13433
13489
|
}
|
|
13434
13490
|
function buildBranchEventBindingsPlan(args) {
|
|
13435
|
-
const { events, wrap } = args;
|
|
13491
|
+
const { events, wrap, profileComponentName: pc } = args;
|
|
13436
13492
|
if (!events || events.length === 0)
|
|
13437
13493
|
return [];
|
|
13438
13494
|
const eventsBySlot = new Map;
|
|
@@ -13444,7 +13500,8 @@ function buildBranchEventBindingsPlan(args) {
|
|
|
13444
13500
|
}
|
|
13445
13501
|
bucket.push({
|
|
13446
13502
|
eventName: ev.eventName,
|
|
13447
|
-
wrappedHandler: wrap(ev.handler)
|
|
13503
|
+
wrappedHandler: wrap(ev.handler),
|
|
13504
|
+
turnId: pc ? `${pc}#handler:${ev.slotId}:${ev.eventName}` : undefined
|
|
13448
13505
|
});
|
|
13449
13506
|
}
|
|
13450
13507
|
const slots = [];
|
|
@@ -13548,6 +13605,7 @@ function buildBranchInnerLoopsPlan(args) {
|
|
|
13548
13605
|
plan.push({
|
|
13549
13606
|
uidSuffix: `br_${i}`,
|
|
13550
13607
|
markerId: inner.markerId,
|
|
13608
|
+
slotId: csl ?? "?",
|
|
13551
13609
|
containerExpr,
|
|
13552
13610
|
arrayExpr: wrapOuter(inner.array),
|
|
13553
13611
|
keyFn: loopKeyFn(inner),
|
|
@@ -13633,7 +13691,7 @@ function buildLoopChildArmPlan(args) {
|
|
|
13633
13691
|
|
|
13634
13692
|
// src/ir-to-client-js/control-flow/plan/build-reactive-effects.ts
|
|
13635
13693
|
function buildReactiveEffectsPlan(args) {
|
|
13636
|
-
const { attrs, texts, conditionals, loopParam, loopParamBindings } = args;
|
|
13694
|
+
const { attrs, texts, conditionals, loopParam, loopParamBindings, profileComponentName } = args;
|
|
13637
13695
|
const wrap = (expr) => wrapLoopParamAsAccessor(expr, loopParam, loopParamBindings);
|
|
13638
13696
|
const attrsBySlot = new Map;
|
|
13639
13697
|
for (const attr of attrs) {
|
|
@@ -13696,22 +13754,24 @@ function buildReactiveEffectsPlan(args) {
|
|
|
13696
13754
|
wrappedCondition: wrap(cond.condition),
|
|
13697
13755
|
whenTrueTemplateHtml: addCondAttrToTemplate(wrap(cond.whenTrueHtml), cond.slotId),
|
|
13698
13756
|
whenFalseTemplateHtml: addCondAttrToTemplate(wrap(cond.whenFalseHtml), cond.slotId),
|
|
13699
|
-
whenTrueArm: buildOuterArm(cond.whenTrue, trueTexts, wrap, loopParam, loopParamBindings),
|
|
13700
|
-
whenFalseArm: buildOuterArm(cond.whenFalse, falseTexts, wrap, loopParam, loopParamBindings)
|
|
13757
|
+
whenTrueArm: buildOuterArm(cond.whenTrue, trueTexts, wrap, loopParam, loopParamBindings, profileComponentName),
|
|
13758
|
+
whenFalseArm: buildOuterArm(cond.whenFalse, falseTexts, wrap, loopParam, loopParamBindings, profileComponentName)
|
|
13701
13759
|
});
|
|
13702
13760
|
}
|
|
13703
13761
|
}
|
|
13704
13762
|
return {
|
|
13705
13763
|
attrSlots,
|
|
13706
13764
|
outerTexts,
|
|
13707
|
-
conditionals: conditionalPlans
|
|
13765
|
+
conditionals: conditionalPlans,
|
|
13766
|
+
profileComponentName
|
|
13708
13767
|
};
|
|
13709
13768
|
}
|
|
13710
|
-
function buildOuterArm(branch, texts, wrap, loopParam, loopParamBindings) {
|
|
13769
|
+
function buildOuterArm(branch, texts, wrap, loopParam, loopParamBindings, profileComponentName) {
|
|
13711
13770
|
return {
|
|
13712
13771
|
events: buildBranchEventBindingsPlan({
|
|
13713
13772
|
events: branch.events,
|
|
13714
|
-
wrap
|
|
13773
|
+
wrap,
|
|
13774
|
+
profileComponentName
|
|
13715
13775
|
}),
|
|
13716
13776
|
childComponents: buildBranchChildComponentInitsPlan({
|
|
13717
13777
|
components: branch.childComponents,
|
|
@@ -13734,13 +13794,14 @@ function buildOuterArm(branch, texts, wrap, loopParam, loopParamBindings) {
|
|
|
13734
13794
|
texts
|
|
13735
13795
|
};
|
|
13736
13796
|
}
|
|
13737
|
-
function buildLoopReactiveEffectsPlan(elem) {
|
|
13797
|
+
function buildLoopReactiveEffectsPlan(elem, profileComponentName) {
|
|
13738
13798
|
return buildReactiveEffectsPlan({
|
|
13739
13799
|
attrs: elem.bindings.reactiveAttrs,
|
|
13740
13800
|
texts: elem.bindings.reactiveTexts,
|
|
13741
13801
|
conditionals: elem.bindings.conditionals,
|
|
13742
13802
|
loopParam: elem.param,
|
|
13743
|
-
loopParamBindings: elem.paramBindings
|
|
13803
|
+
loopParamBindings: elem.paramBindings,
|
|
13804
|
+
profileComponentName
|
|
13744
13805
|
});
|
|
13745
13806
|
}
|
|
13746
13807
|
|
|
@@ -13802,6 +13863,7 @@ function buildInnerLoopsPlan(args) {
|
|
|
13802
13863
|
plan.push({
|
|
13803
13864
|
uidSuffix,
|
|
13804
13865
|
markerId: inner.markerId,
|
|
13866
|
+
slotId: inner.containerSlotId ?? "?",
|
|
13805
13867
|
containerExpr,
|
|
13806
13868
|
arrayExpr,
|
|
13807
13869
|
arraySrc: inner.array,
|
|
@@ -13896,7 +13958,7 @@ function buildStaticEmit(inner, level) {
|
|
|
13896
13958
|
}
|
|
13897
13959
|
|
|
13898
13960
|
// src/ir-to-client-js/control-flow/plan/build-composite-loop.ts
|
|
13899
|
-
function buildTopLevelCompositePlan(elem) {
|
|
13961
|
+
function buildTopLevelCompositePlan(elem, profileComponentName) {
|
|
13900
13962
|
const nestedComps = elem.nestedComponents;
|
|
13901
13963
|
const depthLevels = buildDepthLevels(elem.innerLoops ?? [], nestedComps, elem.bindings.events);
|
|
13902
13964
|
const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(elem.param, elem.paramBindings);
|
|
@@ -13929,15 +13991,18 @@ function buildTopLevelCompositePlan(elem) {
|
|
|
13929
13991
|
texts: elem.bindings.reactiveTexts,
|
|
13930
13992
|
conditionals: elem.bindings.conditionals,
|
|
13931
13993
|
loopParam: elem.param,
|
|
13932
|
-
loopParamBindings: elem.paramBindings
|
|
13994
|
+
loopParamBindings: elem.paramBindings,
|
|
13995
|
+
profileComponentName
|
|
13933
13996
|
}) : null,
|
|
13934
13997
|
branchClearChildren: false,
|
|
13935
13998
|
topIndent: " ",
|
|
13936
13999
|
bodyIndent: " ",
|
|
13937
|
-
bodyIsMultiRoot: elem.bodyIsMultiRoot ?? false
|
|
14000
|
+
bodyIsMultiRoot: elem.bodyIsMultiRoot ?? false,
|
|
14001
|
+
profileLoopId: profileComponentName ? `${profileComponentName}#binding:${elem.slotId}` : undefined,
|
|
14002
|
+
profileComponentName
|
|
13938
14003
|
};
|
|
13939
14004
|
}
|
|
13940
|
-
function buildBranchCompositePlan(loop, cv) {
|
|
14005
|
+
function buildBranchCompositePlan(loop, cv, profileComponentName) {
|
|
13941
14006
|
const nestedComps = loop.nestedComponents;
|
|
13942
14007
|
const innerLoops = loop.innerLoops ?? [];
|
|
13943
14008
|
const childEvents = loop.bindings.events;
|
|
@@ -13972,12 +14037,15 @@ function buildBranchCompositePlan(loop, cv) {
|
|
|
13972
14037
|
texts: loop.bindings.reactiveTexts,
|
|
13973
14038
|
conditionals: loop.bindings.conditionals,
|
|
13974
14039
|
loopParam: loop.param,
|
|
13975
|
-
loopParamBindings: loop.paramBindings
|
|
14040
|
+
loopParamBindings: loop.paramBindings,
|
|
14041
|
+
profileComponentName
|
|
13976
14042
|
}) : null,
|
|
13977
14043
|
branchClearChildren: true,
|
|
13978
14044
|
topIndent: " ",
|
|
13979
14045
|
bodyIndent: " ",
|
|
13980
|
-
bodyIsMultiRoot: loop.bodyIsMultiRoot ?? false
|
|
14046
|
+
bodyIsMultiRoot: loop.bodyIsMultiRoot ?? false,
|
|
14047
|
+
profileLoopId: profileComponentName ? `${profileComponentName}#binding:${loop.containerSlotId}` : undefined,
|
|
14048
|
+
profileComponentName
|
|
13981
14049
|
};
|
|
13982
14050
|
}
|
|
13983
14051
|
function filterCondCompsOut(outerComps, conditionals) {
|
|
@@ -14002,11 +14070,12 @@ function hasReactiveBranch(loop) {
|
|
|
14002
14070
|
}
|
|
14003
14071
|
|
|
14004
14072
|
// src/ir-to-client-js/control-flow/plan/build-event-delegation.ts
|
|
14005
|
-
function buildDynamicLoopDelegationPlan(elem) {
|
|
14073
|
+
function buildDynamicLoopDelegationPlan(elem, profileComponentName) {
|
|
14006
14074
|
return {
|
|
14007
14075
|
kind: "event-delegation",
|
|
14008
14076
|
containerVar: `_${varSlotId(elem.slotId)}`,
|
|
14009
14077
|
events: elem.bindings.events,
|
|
14078
|
+
profileComponentName,
|
|
14010
14079
|
itemLookup: buildKeyedOrIndexLookup({
|
|
14011
14080
|
array: buildChainedArrayExpr(elem),
|
|
14012
14081
|
param: elem.param,
|
|
@@ -14016,11 +14085,12 @@ function buildDynamicLoopDelegationPlan(elem) {
|
|
|
14016
14085
|
})
|
|
14017
14086
|
};
|
|
14018
14087
|
}
|
|
14019
|
-
function buildBranchLoopDelegationPlan(loop, cv) {
|
|
14088
|
+
function buildBranchLoopDelegationPlan(loop, cv, profileComponentName) {
|
|
14020
14089
|
return {
|
|
14021
14090
|
kind: "event-delegation",
|
|
14022
14091
|
containerVar: `__loop_${cv}`,
|
|
14023
14092
|
events: loop.bindings.events,
|
|
14093
|
+
profileComponentName,
|
|
14024
14094
|
itemLookup: buildKeyedOrIndexLookup({
|
|
14025
14095
|
array: buildChainedArrayExpr(loop),
|
|
14026
14096
|
param: loop.param,
|
|
@@ -14030,11 +14100,12 @@ function buildBranchLoopDelegationPlan(loop, cv) {
|
|
|
14030
14100
|
})
|
|
14031
14101
|
};
|
|
14032
14102
|
}
|
|
14033
|
-
function buildStaticArrayDelegationPlan(elem) {
|
|
14103
|
+
function buildStaticArrayDelegationPlan(elem, profileComponentName) {
|
|
14034
14104
|
return {
|
|
14035
14105
|
kind: "event-delegation",
|
|
14036
14106
|
containerVar: `_${varSlotId(elem.slotId)}`,
|
|
14037
14107
|
events: elem.bindings.events,
|
|
14108
|
+
profileComponentName,
|
|
14038
14109
|
itemLookup: {
|
|
14039
14110
|
kind: "static-index",
|
|
14040
14111
|
arrayExpr: buildChainedArrayExpr(elem),
|
|
@@ -14068,14 +14139,14 @@ function buildKeyedOrIndexLookup(args) {
|
|
|
14068
14139
|
}
|
|
14069
14140
|
|
|
14070
14141
|
// src/ir-to-client-js/control-flow/plan/build-branch-loop.ts
|
|
14071
|
-
function buildBranchLoopPlan(loop) {
|
|
14142
|
+
function buildBranchLoopPlan(loop, profileComponentName) {
|
|
14072
14143
|
const containerSlotId = loop.containerSlotId;
|
|
14073
14144
|
const cv = varSlotId(containerSlotId);
|
|
14074
14145
|
const containerVar = `__loop_${cv}`;
|
|
14075
14146
|
if (loop.useElementReconciliation && (loop.nestedComponents?.length || loop.innerLoops?.length)) {
|
|
14076
14147
|
const composite = {
|
|
14077
14148
|
kind: "composite",
|
|
14078
|
-
composite: buildBranchCompositePlan(loop, cv),
|
|
14149
|
+
composite: buildBranchCompositePlan(loop, cv, profileComponentName),
|
|
14079
14150
|
containerSlotId,
|
|
14080
14151
|
containerVar
|
|
14081
14152
|
};
|
|
@@ -14100,11 +14171,13 @@ function buildBranchLoopPlan(loop) {
|
|
|
14100
14171
|
texts: loop.bindings.reactiveTexts,
|
|
14101
14172
|
conditionals: loop.bindings.conditionals,
|
|
14102
14173
|
loopParam: loop.param,
|
|
14103
|
-
loopParamBindings: loop.paramBindings
|
|
14174
|
+
loopParamBindings: loop.paramBindings,
|
|
14175
|
+
profileComponentName
|
|
14104
14176
|
}) : null,
|
|
14105
|
-
eventDelegation: buildBranchLoopDelegationPlan(loop, cv),
|
|
14177
|
+
eventDelegation: buildBranchLoopDelegationPlan(loop, cv, profileComponentName),
|
|
14106
14178
|
childRefs: buildChildRefBindings(loop.bindings.refs, loop.param, loop.paramBindings),
|
|
14107
|
-
bodyIsMultiRoot: loop.bodyIsMultiRoot ?? false
|
|
14179
|
+
bodyIsMultiRoot: loop.bodyIsMultiRoot ?? false,
|
|
14180
|
+
profileLoopId: profileComponentName ? `${profileComponentName}#binding:${containerSlotId}` : undefined
|
|
14108
14181
|
};
|
|
14109
14182
|
return plan;
|
|
14110
14183
|
}
|
|
@@ -14117,6 +14190,7 @@ function buildInsertPlan(elem, options) {
|
|
|
14117
14190
|
slotId: elem.slotId,
|
|
14118
14191
|
condition: elem.condition,
|
|
14119
14192
|
eventNameMode: options.eventNameMode,
|
|
14193
|
+
profileComponentName: options.profileComponentName,
|
|
14120
14194
|
arms: [
|
|
14121
14195
|
buildArm(elem.whenTrueHtml, elem.slotId, elem.whenTrue, options),
|
|
14122
14196
|
buildArm(elem.whenFalseHtml, elem.slotId, elem.whenFalse, options)
|
|
@@ -14130,11 +14204,13 @@ function buildArm(html, slotId, branch, options) {
|
|
|
14130
14204
|
};
|
|
14131
14205
|
}
|
|
14132
14206
|
function buildArmBody(branch, options) {
|
|
14207
|
+
const pc = options.profileComponentName;
|
|
14133
14208
|
return {
|
|
14134
14209
|
events: branch.events.map((e) => ({
|
|
14135
14210
|
slotId: e.slotId,
|
|
14136
14211
|
eventName: e.eventName,
|
|
14137
|
-
handler: e.handler
|
|
14212
|
+
handler: e.handler,
|
|
14213
|
+
turnId: pc ? `${pc}#handler:${e.slotId}:${e.eventName}` : undefined
|
|
14138
14214
|
})),
|
|
14139
14215
|
refs: branch.refs.map((r) => ({
|
|
14140
14216
|
slotId: r.slotId,
|
|
@@ -14150,12 +14226,17 @@ function buildArmBody(branch, options) {
|
|
|
14150
14226
|
slotId: t.slotId,
|
|
14151
14227
|
expression: t.expression
|
|
14152
14228
|
})),
|
|
14153
|
-
loops: branch.loops.map(buildBranchLoopPlan),
|
|
14154
|
-
conditionals: branch.conditionals.map((c) => buildInsertPlan(c, { scope: { kind: "branchScope" }, eventNameMode: options.eventNameMode }))
|
|
14229
|
+
loops: branch.loops.map((l) => buildBranchLoopPlan(l, pc)),
|
|
14230
|
+
conditionals: branch.conditionals.map((c) => buildInsertPlan(c, { scope: { kind: "branchScope" }, eventNameMode: options.eventNameMode, profileComponentName: pc }))
|
|
14155
14231
|
};
|
|
14156
14232
|
}
|
|
14157
14233
|
|
|
14158
14234
|
// src/ir-to-client-js/emit-reactive.ts
|
|
14235
|
+
function bindingIdArg(ctx, slotId) {
|
|
14236
|
+
if (!ctx.profile || !slotId)
|
|
14237
|
+
return "";
|
|
14238
|
+
return `, ${JSON.stringify(`${ctx.componentName}#binding:${slotId}`)}`;
|
|
14239
|
+
}
|
|
14159
14240
|
function emitAttrUpdate(target, attrName, expression, meta) {
|
|
14160
14241
|
const htmlName = toHtmlAttrName(attrName);
|
|
14161
14242
|
if (attrName === "dangerouslySetInnerHTML" || htmlName === "dangerouslySetInnerHTML") {
|
|
@@ -14227,6 +14308,7 @@ function emitDynamicTextUpdates(lines, ctx) {
|
|
|
14227
14308
|
const v = varSlotId(elem.slotId);
|
|
14228
14309
|
lines.push(` let __anchor_${v} = _${v}`);
|
|
14229
14310
|
}
|
|
14311
|
+
const __textSlot = (normalElems[0] ?? conditionalElems[0])?.slotId;
|
|
14230
14312
|
lines.push(` createEffect(() => {`);
|
|
14231
14313
|
if (normalElems.length > 0) {
|
|
14232
14314
|
lines.push(` const __val = ${expr}`);
|
|
@@ -14248,7 +14330,7 @@ function emitDynamicTextUpdates(lines, ctx) {
|
|
|
14248
14330
|
lines.push(` __bfText(__el_${v}, __val)`);
|
|
14249
14331
|
}
|
|
14250
14332
|
}
|
|
14251
|
-
lines.push(` })`);
|
|
14333
|
+
lines.push(` }${bindingIdArg(ctx, __textSlot)})`);
|
|
14252
14334
|
lines.push("");
|
|
14253
14335
|
}
|
|
14254
14336
|
}
|
|
@@ -14258,7 +14340,7 @@ function emitClientOnlyExpressions(lines, ctx) {
|
|
|
14258
14340
|
lines.push(` // @client: ${elem.slotId}`);
|
|
14259
14341
|
lines.push(` createEffect(() => {`);
|
|
14260
14342
|
lines.push(` updateClientMarker(__scope, '${elem.slotId}', ${elem.expression})`);
|
|
14261
|
-
lines.push(` })`);
|
|
14343
|
+
lines.push(` }${bindingIdArg(ctx, elem.slotId)})`);
|
|
14262
14344
|
lines.push("");
|
|
14263
14345
|
}
|
|
14264
14346
|
}
|
|
@@ -14282,7 +14364,7 @@ function emitReactiveAttributeUpdates(lines, ctx) {
|
|
|
14282
14364
|
}
|
|
14283
14365
|
}
|
|
14284
14366
|
lines.push(` }`);
|
|
14285
|
-
lines.push(` })`);
|
|
14367
|
+
lines.push(` }${bindingIdArg(ctx, slotId)})`);
|
|
14286
14368
|
lines.push("");
|
|
14287
14369
|
}
|
|
14288
14370
|
}
|
|
@@ -14328,7 +14410,7 @@ function emitReactivePropBindings(lines, ctx) {
|
|
|
14328
14410
|
}
|
|
14329
14411
|
lines.push(` }`);
|
|
14330
14412
|
}
|
|
14331
|
-
lines.push(` })`);
|
|
14413
|
+
lines.push(` }${bindingIdArg(ctx, ctx.reactiveProps[0]?.slotId)})`);
|
|
14332
14414
|
}
|
|
14333
14415
|
}
|
|
14334
14416
|
function emitReactiveChildProps(lines, ctx) {
|
|
@@ -14358,7 +14440,7 @@ function emitReactiveChildProps(lines, ctx) {
|
|
|
14358
14440
|
}
|
|
14359
14441
|
lines.push(` }`);
|
|
14360
14442
|
}
|
|
14361
|
-
lines.push(` })`);
|
|
14443
|
+
lines.push(` }${bindingIdArg(ctx, ctx.reactiveChildProps[0]?.slotId ?? undefined)})`);
|
|
14362
14444
|
}
|
|
14363
14445
|
}
|
|
14364
14446
|
|
|
@@ -14504,7 +14586,7 @@ function stringifyBranchEventBindings(lines, plan, indent) {
|
|
|
14504
14586
|
const v = varSlotId(slot.slotId);
|
|
14505
14587
|
lines.push(`${indent}{ const _${v} = qsa(__branchScope, '[bf="${slot.slotId}"]')`);
|
|
14506
14588
|
for (const ev of slot.listeners) {
|
|
14507
|
-
emitListenerLine(lines, `${indent} `, `_${v}`, ev.eventName, ev.wrappedHandler);
|
|
14589
|
+
emitListenerLine(lines, `${indent} `, `_${v}`, ev.eventName, ev.wrappedHandler, "dom", ev.turnId);
|
|
14508
14590
|
}
|
|
14509
14591
|
lines.push(`${indent}}`);
|
|
14510
14592
|
}
|
|
@@ -14514,7 +14596,7 @@ function stringifyBranchChildComponentInits(lines, plan, indent) {
|
|
|
14514
14596
|
lines.push(`${indent}{ let __c = qsa(__branchScope, ${init.selector}); if (!__c) { const __ph = __branchScope.querySelector('[${DATA_BF_PH}="${init.placeholderId}"]'); if (__ph) { __c = createComponent('${nameForRegistryRef(init.name)}', ${init.propsExpr}); __ph.replaceWith(__c) } } if (__c) initChild('${nameForRegistryRef(init.name)}', __c, ${init.propsExpr}) }`);
|
|
14515
14597
|
}
|
|
14516
14598
|
}
|
|
14517
|
-
function stringifyBranchInnerLoops(lines, plan, indent) {
|
|
14599
|
+
function stringifyBranchInnerLoops(lines, plan, indent, pc) {
|
|
14518
14600
|
for (const inner of plan) {
|
|
14519
14601
|
const uid = inner.uidSuffix;
|
|
14520
14602
|
lines.push(`${indent}{ const __bic${uid} = ${inner.containerExpr}`);
|
|
@@ -14535,48 +14617,49 @@ function stringifyBranchInnerLoops(lines, plan, indent) {
|
|
|
14535
14617
|
emitComponentAndEventSetup(lines, `${indent} `, `__bel${uid}`, [...inner.legacyComponents], [...inner.legacyEvents], inner.outerLoopParam, inner.outerLoopParamBindings);
|
|
14536
14618
|
}
|
|
14537
14619
|
for (const text of inner.reactiveTexts) {
|
|
14620
|
+
const bf = profileBindingId(pc, text.slotId);
|
|
14538
14621
|
if (text.insideConditional) {
|
|
14539
|
-
lines.push(`${indent} createEffect(() => { const [__rt] = $t(__bel${uid}, '${text.slotId}'); if (__rt) __rt.textContent = String(${text.wrappedExpression}) })`);
|
|
14622
|
+
lines.push(`${indent} createEffect(() => { const [__rt] = $t(__bel${uid}, '${text.slotId}'); if (__rt) __rt.textContent = String(${text.wrappedExpression}) }${bf})`);
|
|
14540
14623
|
} else {
|
|
14541
14624
|
lines.push(`${indent} { const [__rt] = $t(__bel${uid}, '${text.slotId}')`);
|
|
14542
|
-
lines.push(`${indent} if (__rt) createEffect(() => { __rt.textContent = String(${text.wrappedExpression}) }) }`);
|
|
14625
|
+
lines.push(`${indent} if (__rt) createEffect(() => { __rt.textContent = String(${text.wrappedExpression}) }${bf}) }`);
|
|
14543
14626
|
}
|
|
14544
14627
|
}
|
|
14545
14628
|
if (inner.nestedConditionals.length > 0) {
|
|
14546
|
-
stringifyLoopChildConditionals(lines, inner.nestedConditionals, `${indent}
|
|
14629
|
+
stringifyLoopChildConditionals(lines, inner.nestedConditionals, `${indent} `, pc);
|
|
14547
14630
|
}
|
|
14548
14631
|
lines.push(`${indent} return __bel${uid}`);
|
|
14549
|
-
lines.push(`${indent}}, '${inner.markerId}') }`);
|
|
14632
|
+
lines.push(`${indent}}, '${inner.markerId}'${profileBindingId(pc, inner.slotId)}) }`);
|
|
14550
14633
|
}
|
|
14551
14634
|
}
|
|
14552
|
-
function stringifyLoopChildConditionals(lines, conditionals, indent) {
|
|
14635
|
+
function stringifyLoopChildConditionals(lines, conditionals, indent, pc) {
|
|
14553
14636
|
for (const cond of conditionals) {
|
|
14554
|
-
stringifyLoopChildConditional(lines, cond, indent);
|
|
14637
|
+
stringifyLoopChildConditional(lines, cond, indent, pc);
|
|
14555
14638
|
}
|
|
14556
14639
|
}
|
|
14557
|
-
function stringifyLoopChildConditional(lines, cond, indent) {
|
|
14640
|
+
function stringifyLoopChildConditional(lines, cond, indent, pc) {
|
|
14558
14641
|
const armIndent = `${indent} `;
|
|
14559
14642
|
lines.push(`${indent}insert(${cond.scopeVar}, '${cond.slotId}', () => ${cond.wrappedCondition}, {`);
|
|
14560
14643
|
lines.push(`${indent} template: () => { const __slots = []; return { html: \`${cond.whenTrueTemplateHtml}\`, slots: __slots } },`);
|
|
14561
14644
|
lines.push(`${indent} bindEvents: (__branchScope, { isFirstRun: __bfFirstRun = false } = {}) => {`);
|
|
14562
|
-
stringifyLoopChildArm(lines, cond.whenTrueArm, armIndent);
|
|
14645
|
+
stringifyLoopChildArm(lines, cond.whenTrueArm, armIndent, pc);
|
|
14563
14646
|
lines.push(`${indent} }`);
|
|
14564
14647
|
lines.push(`${indent}}, {`);
|
|
14565
14648
|
lines.push(`${indent} template: () => { const __slots = []; return { html: \`${cond.whenFalseTemplateHtml}\`, slots: __slots } },`);
|
|
14566
14649
|
lines.push(`${indent} bindEvents: (__branchScope, { isFirstRun: __bfFirstRun = false } = {}) => {`);
|
|
14567
|
-
stringifyLoopChildArm(lines, cond.whenFalseArm, armIndent);
|
|
14650
|
+
stringifyLoopChildArm(lines, cond.whenFalseArm, armIndent, pc);
|
|
14568
14651
|
lines.push(`${indent} }`);
|
|
14569
|
-
lines.push(`${indent}})`);
|
|
14652
|
+
lines.push(`${indent}}${profileBindingId(pc, cond.slotId)})`);
|
|
14570
14653
|
}
|
|
14571
|
-
function stringifyLoopChildArm(lines, arm, armIndent) {
|
|
14654
|
+
function stringifyLoopChildArm(lines, arm, armIndent, pc) {
|
|
14572
14655
|
stringifyBranchEventBindings(lines, arm.events, armIndent);
|
|
14573
14656
|
stringifyBranchChildComponentInits(lines, arm.childComponents, armIndent);
|
|
14574
|
-
stringifyBranchInnerLoops(lines, arm.innerLoops, armIndent);
|
|
14575
|
-
stringifyLoopChildConditionals(lines, arm.nestedConditionals, armIndent);
|
|
14657
|
+
stringifyBranchInnerLoops(lines, arm.innerLoops, armIndent, pc);
|
|
14658
|
+
stringifyLoopChildConditionals(lines, arm.nestedConditionals, armIndent, pc);
|
|
14576
14659
|
for (const text of arm.texts) {
|
|
14577
14660
|
const varName = `__rt_${varSlotId(text.slotId)}`;
|
|
14578
14661
|
lines.push(`${armIndent}{ const [${varName}] = $t(__branchScope, '${text.slotId}')`);
|
|
14579
|
-
lines.push(`${armIndent}if (${varName}) createEffect(() => { ${varName}.textContent = String(${text.wrappedExpression}) }) }`);
|
|
14662
|
+
lines.push(`${armIndent}if (${varName}) createEffect(() => { ${varName}.textContent = String(${text.wrappedExpression}) }${profileBindingId(pc, text.slotId)}) }`);
|
|
14580
14663
|
}
|
|
14581
14664
|
}
|
|
14582
14665
|
|
|
@@ -14584,6 +14667,8 @@ function stringifyLoopChildArm(lines, arm, armIndent) {
|
|
|
14584
14667
|
function stringifyReactiveEffects(lines, plan, opts) {
|
|
14585
14668
|
const { indent, elVar, bodyIsMultiRoot } = opts;
|
|
14586
14669
|
const lookup = bodyIsMultiRoot ? "qsaItem" : "qsa";
|
|
14670
|
+
const pc = plan.profileComponentName;
|
|
14671
|
+
const bindingBfId = (slotId) => profileBindingId(pc, slotId);
|
|
14587
14672
|
for (const slot of plan.attrSlots) {
|
|
14588
14673
|
const varName = `__ra_${varSlotId(slot.slotId)}`;
|
|
14589
14674
|
lines.push(`${indent}{ const ${varName} = ${lookup}(${elVar}, '[bf="${slot.slotId}"]')`);
|
|
@@ -14593,49 +14678,49 @@ function stringifyReactiveEffects(lines, plan, opts) {
|
|
|
14593
14678
|
for (const stmt of emitAttrUpdate(varName, attr.attrName, attr.wrappedExpression, attr.meta)) {
|
|
14594
14679
|
lines.push(`${indent} ${stmt}`);
|
|
14595
14680
|
}
|
|
14596
|
-
lines.push(`${indent} })`);
|
|
14681
|
+
lines.push(`${indent} }${bindingBfId(slot.slotId)})`);
|
|
14597
14682
|
}
|
|
14598
14683
|
lines.push(`${indent}} }`);
|
|
14599
14684
|
}
|
|
14600
14685
|
for (const text of plan.outerTexts) {
|
|
14601
|
-
emitOuterText(lines, indent, elVar, text);
|
|
14686
|
+
emitOuterText(lines, indent, elVar, text, bindingBfId(text.slotId));
|
|
14602
14687
|
}
|
|
14603
14688
|
for (const cond of plan.conditionals) {
|
|
14604
|
-
emitOuterConditional(lines, indent, elVar, cond);
|
|
14689
|
+
emitOuterConditional(lines, indent, elVar, cond, pc);
|
|
14605
14690
|
}
|
|
14606
14691
|
}
|
|
14607
|
-
function emitOuterText(lines, indent, elVar, text) {
|
|
14692
|
+
function emitOuterText(lines, indent, elVar, text, bfId = "") {
|
|
14608
14693
|
const varName = `__rt_${varSlotId(text.slotId)}`;
|
|
14609
14694
|
lines.push(`${indent}{ const [${varName}] = $t(${elVar}, '${text.slotId}')`);
|
|
14610
|
-
lines.push(`${indent}if (${varName}) createEffect(() => { ${varName}.textContent = String(${text.wrappedExpression}) }) }`);
|
|
14695
|
+
lines.push(`${indent}if (${varName}) createEffect(() => { ${varName}.textContent = String(${text.wrappedExpression}) }${bfId}) }`);
|
|
14611
14696
|
}
|
|
14612
|
-
function emitOuterConditional(lines, indent, elVar, cond) {
|
|
14697
|
+
function emitOuterConditional(lines, indent, elVar, cond, pc) {
|
|
14613
14698
|
const armIndent = `${indent} `;
|
|
14614
14699
|
lines.push(`${indent}insert(${elVar}, '${cond.slotId}', () => ${cond.wrappedCondition}, {`);
|
|
14615
14700
|
lines.push(`${indent} template: () => { const __slots = []; return { html: \`${cond.whenTrueTemplateHtml}\`, slots: __slots } },`);
|
|
14616
14701
|
lines.push(`${indent} bindEvents: (__branchScope, { isFirstRun: __bfFirstRun = false } = {}) => {`);
|
|
14617
|
-
emitArmBody(lines, cond.whenTrueArm, armIndent);
|
|
14702
|
+
emitArmBody(lines, cond.whenTrueArm, armIndent, pc);
|
|
14618
14703
|
lines.push(`${indent} }`);
|
|
14619
14704
|
lines.push(`${indent}}, {`);
|
|
14620
14705
|
lines.push(`${indent} template: () => { const __slots = []; return { html: \`${cond.whenFalseTemplateHtml}\`, slots: __slots } },`);
|
|
14621
14706
|
lines.push(`${indent} bindEvents: (__branchScope, { isFirstRun: __bfFirstRun = false } = {}) => {`);
|
|
14622
|
-
emitArmBody(lines, cond.whenFalseArm, armIndent);
|
|
14707
|
+
emitArmBody(lines, cond.whenFalseArm, armIndent, pc);
|
|
14623
14708
|
lines.push(`${indent} }`);
|
|
14624
|
-
lines.push(`${indent}})`);
|
|
14709
|
+
lines.push(`${indent}}${profileBindingId(pc, cond.slotId)})`);
|
|
14625
14710
|
}
|
|
14626
|
-
function emitArmBody(lines, arm, armIndent) {
|
|
14711
|
+
function emitArmBody(lines, arm, armIndent, pc) {
|
|
14627
14712
|
stringifyBranchEventBindings(lines, arm.events, armIndent);
|
|
14628
14713
|
stringifyBranchChildComponentInits(lines, arm.childComponents, armIndent);
|
|
14629
|
-
stringifyBranchInnerLoops(lines, arm.innerLoops, armIndent);
|
|
14630
|
-
stringifyLoopChildConditionals(lines, arm.nestedConditionals, armIndent);
|
|
14714
|
+
stringifyBranchInnerLoops(lines, arm.innerLoops, armIndent, pc);
|
|
14715
|
+
stringifyLoopChildConditionals(lines, arm.nestedConditionals, armIndent, pc);
|
|
14631
14716
|
for (const text of arm.texts) {
|
|
14632
|
-
emitArmText(lines, armIndent, text);
|
|
14717
|
+
emitArmText(lines, armIndent, text, pc);
|
|
14633
14718
|
}
|
|
14634
14719
|
}
|
|
14635
|
-
function emitArmText(lines, indent, text) {
|
|
14720
|
+
function emitArmText(lines, indent, text, pc) {
|
|
14636
14721
|
const varName = `__rt_${varSlotId(text.slotId)}`;
|
|
14637
14722
|
lines.push(`${indent}{ const [${varName}] = $t(__branchScope, '${text.slotId}')`);
|
|
14638
|
-
lines.push(`${indent}if (${varName}) createEffect(() => { ${varName}.textContent = String(${text.wrappedExpression}) }) }`);
|
|
14723
|
+
lines.push(`${indent}if (${varName}) createEffect(() => { ${varName}.textContent = String(${text.wrappedExpression}) }${profileBindingId(pc, text.slotId)}) }`);
|
|
14639
14724
|
}
|
|
14640
14725
|
|
|
14641
14726
|
// src/ir-to-client-js/control-flow/stringify/component-loop.ts
|
|
@@ -14652,8 +14737,10 @@ function stringifyComponentLoop(lines, plan) {
|
|
|
14652
14737
|
componentPropsExpr,
|
|
14653
14738
|
keyExpr,
|
|
14654
14739
|
nestedComps,
|
|
14655
|
-
childConditionalEffects
|
|
14740
|
+
childConditionalEffects,
|
|
14741
|
+
profileLoopId
|
|
14656
14742
|
} = plan;
|
|
14743
|
+
const loopBfId = profileLoopId ? `, ${JSON.stringify(profileLoopId)}` : "";
|
|
14657
14744
|
lines.push(` mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (${paramHead}, ${indexParam}, __existing) => {`);
|
|
14658
14745
|
if (paramUnwrap)
|
|
14659
14746
|
lines.push(` ${paramUnwrap}`);
|
|
@@ -14661,7 +14748,7 @@ function stringifyComponentLoop(lines, plan) {
|
|
|
14661
14748
|
if (nestedComps.length === 0) {
|
|
14662
14749
|
lines.push(` if (__existing) { initChild('${scopedComp}', __existing, ${componentPropsExpr}); return __existing }`);
|
|
14663
14750
|
lines.push(` return createComponent('${scopedComp}', ${componentPropsExpr}, ${keyExpr})`);
|
|
14664
|
-
lines.push(` }, '${markerId}')`);
|
|
14751
|
+
lines.push(` }, '${markerId}'${loopBfId})`);
|
|
14665
14752
|
return;
|
|
14666
14753
|
}
|
|
14667
14754
|
lines.push(` if (__existing) {`);
|
|
@@ -14680,7 +14767,7 @@ function stringifyComponentLoop(lines, plan) {
|
|
|
14680
14767
|
stringifyReactiveEffects(lines, childConditionalEffects, { indent: " ", elVar: "__csrEl" });
|
|
14681
14768
|
}
|
|
14682
14769
|
lines.push(` return __csrEl`);
|
|
14683
|
-
lines.push(` }, '${markerId}')`);
|
|
14770
|
+
lines.push(` }, '${markerId}'${loopBfId})`);
|
|
14684
14771
|
}
|
|
14685
14772
|
function emitNestedInit(lines, indent, parentVar, nc) {
|
|
14686
14773
|
const scopedNc = nameForRegistryRef(nc.componentName);
|
|
@@ -14749,11 +14836,12 @@ function stringifyPlainLoop(lines, plan, topIndent = " ") {
|
|
|
14749
14836
|
stringifyAnchoredLoop(lines, plan, topIndent, anchorKeyExpr);
|
|
14750
14837
|
return;
|
|
14751
14838
|
}
|
|
14839
|
+
const loopBfId = plan.profileLoopId ? `, ${JSON.stringify(plan.profileLoopId)}` : "";
|
|
14752
14840
|
if (reactiveEffects === null && !bodyIsMultiRoot && childRefs.length === 0) {
|
|
14753
14841
|
const unwrapInline = paramUnwrap ? `${paramUnwrap} ` : "";
|
|
14754
14842
|
const preamble = mapPreambleWrapped ? `${mapPreambleWrapped}; ` : "";
|
|
14755
14843
|
const cloneExpr = emitTemplateCloneInline(template);
|
|
14756
|
-
lines.push(`${topIndent}mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (${paramHead}, ${indexParam}, __existing) => { ${unwrapInline}${preamble}if (__existing) return __existing; ${cloneExpr} }, '${markerId}')`);
|
|
14844
|
+
lines.push(`${topIndent}mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (${paramHead}, ${indexParam}, __existing) => { ${unwrapInline}${preamble}if (__existing) return __existing; ${cloneExpr} }, '${markerId}'${loopBfId})`);
|
|
14757
14845
|
return;
|
|
14758
14846
|
}
|
|
14759
14847
|
lines.push(`${topIndent}mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (${paramHead}, ${indexParam}, __existing) => {`);
|
|
@@ -14773,7 +14861,7 @@ function stringifyPlainLoop(lines, plan, topIndent = " ") {
|
|
|
14773
14861
|
}
|
|
14774
14862
|
emitLoopChildRefs(lines, childRefs, { indent: bodyIndent, elVar: "__el", bodyIsMultiRoot });
|
|
14775
14863
|
lines.push(`${bodyIndent}return __el`);
|
|
14776
|
-
lines.push(`${topIndent}}, '${markerId}')`);
|
|
14864
|
+
lines.push(`${topIndent}}, '${markerId}'${loopBfId})`);
|
|
14777
14865
|
}
|
|
14778
14866
|
function stringifyAnchoredLoop(lines, plan, topIndent, anchorKeyExpr) {
|
|
14779
14867
|
const {
|
|
@@ -14808,10 +14896,11 @@ function stringifyAnchoredLoop(lines, plan, topIndent, anchorKeyExpr) {
|
|
|
14808
14896
|
stringifyReactiveEffects(lines, reactiveEffects, { indent: bodyIndent, elVar: "__anchor", bodyIsMultiRoot: false });
|
|
14809
14897
|
}
|
|
14810
14898
|
lines.push(`${bodyIndent}return __frag ?? __anchor`);
|
|
14811
|
-
|
|
14899
|
+
const loopBfId = plan.profileLoopId ? `, ${JSON.stringify(plan.profileLoopId)}` : "";
|
|
14900
|
+
lines.push(`${topIndent}}, '${markerId}'${loopBfId})`);
|
|
14812
14901
|
}
|
|
14813
14902
|
function stringifyStaticLoop(lines, plan) {
|
|
14814
|
-
const { containerVar, arrayExpr, param, indexParam, childIndexExpr, attrsBySlot, texts, childRefs, csrMaterialize } = plan;
|
|
14903
|
+
const { containerVar, arrayExpr, param, indexParam, childIndexExpr, attrsBySlot, texts, childRefs, csrMaterialize, profileComponentName: pc } = plan;
|
|
14815
14904
|
const hasAttrs = attrsBySlot.length > 0;
|
|
14816
14905
|
const hasTexts = texts.length > 0;
|
|
14817
14906
|
const hasRefs = childRefs.length > 0;
|
|
@@ -14873,14 +14962,14 @@ function stringifyStaticLoop(lines, plan) {
|
|
|
14873
14962
|
for (const stmt of emitAttrUpdate(varName, attr.attrName, attr.expression, attr)) {
|
|
14874
14963
|
lines.push(` ${stmt}`);
|
|
14875
14964
|
}
|
|
14876
|
-
lines.push(` })`);
|
|
14965
|
+
lines.push(` }${profileBindingId(pc, slotId)})`);
|
|
14877
14966
|
}
|
|
14878
14967
|
lines.push(` }`);
|
|
14879
14968
|
}
|
|
14880
14969
|
for (const text of texts) {
|
|
14881
14970
|
const vn = `__rt_${varSlotId(text.slotId)}`;
|
|
14882
14971
|
lines.push(` { const [${vn}] = $t(__iterEl, '${text.slotId}')`);
|
|
14883
|
-
lines.push(` if (${vn}) createEffect(() => { ${vn}.textContent = String(${text.expression}) }) }`);
|
|
14972
|
+
lines.push(` if (${vn}) createEffect(() => { ${vn}.textContent = String(${text.expression}) }${profileBindingId(pc, text.slotId)}) }`);
|
|
14884
14973
|
}
|
|
14885
14974
|
emitLoopChildRefs(lines, childRefs, { indent: " ", elVar: "__iterEl", bodyIsMultiRoot: false });
|
|
14886
14975
|
lines.push(` }`);
|
|
@@ -14890,16 +14979,16 @@ function stringifyStaticLoop(lines, plan) {
|
|
|
14890
14979
|
}
|
|
14891
14980
|
|
|
14892
14981
|
// src/ir-to-client-js/control-flow/stringify/inner-loop.ts
|
|
14893
|
-
function stringifyInnerLoops(lines, plan, indent) {
|
|
14982
|
+
function stringifyInnerLoops(lines, plan, indent, pc) {
|
|
14894
14983
|
for (const inner of plan) {
|
|
14895
14984
|
if (inner.emit.mode === "reactive") {
|
|
14896
|
-
emitReactive(lines, inner, indent);
|
|
14985
|
+
emitReactive(lines, inner, indent, pc);
|
|
14897
14986
|
} else {
|
|
14898
|
-
emitStatic(lines, inner, indent);
|
|
14987
|
+
emitStatic(lines, inner, indent, pc);
|
|
14899
14988
|
}
|
|
14900
14989
|
}
|
|
14901
14990
|
}
|
|
14902
|
-
function emitReactive(lines, inner, indent) {
|
|
14991
|
+
function emitReactive(lines, inner, indent, pc) {
|
|
14903
14992
|
const uid = inner.uidSuffix;
|
|
14904
14993
|
const emit = inner.emit;
|
|
14905
14994
|
if (emit.mode !== "reactive")
|
|
@@ -14930,14 +15019,15 @@ function emitReactive(lines, inner, indent) {
|
|
|
14930
15019
|
emitComponentAndEventSetup(lines, `${indent} `, `__innerEl${uid}`, [...emit.components], [...emit.events], inner.outerLoopParam, inner.outerLoopParamBindings);
|
|
14931
15020
|
}
|
|
14932
15021
|
if (inner.childLevels.length > 0) {
|
|
14933
|
-
stringifyInnerLoops(lines, inner.childLevels, `${indent}
|
|
15022
|
+
stringifyInnerLoops(lines, inner.childLevels, `${indent} `, pc);
|
|
14934
15023
|
}
|
|
14935
15024
|
for (const text of emit.reactiveTexts) {
|
|
15025
|
+
const bf = profileBindingId(pc, text.slotId);
|
|
14936
15026
|
if (text.insideConditional) {
|
|
14937
|
-
lines.push(`${indent} createEffect(() => { const [__rt] = $t(__innerEl${uid}, '${text.slotId}'); if (__rt) __rt.textContent = String(${text.wrappedExpression}) })`);
|
|
15027
|
+
lines.push(`${indent} createEffect(() => { const [__rt] = $t(__innerEl${uid}, '${text.slotId}'); if (__rt) __rt.textContent = String(${text.wrappedExpression}) }${bf})`);
|
|
14938
15028
|
} else {
|
|
14939
15029
|
lines.push(`${indent} { const [__rt] = $t(__innerEl${uid}, '${text.slotId}')`);
|
|
14940
|
-
lines.push(`${indent} if (__rt) createEffect(() => { __rt.textContent = String(${text.wrappedExpression}) }) }`);
|
|
15030
|
+
lines.push(`${indent} if (__rt) createEffect(() => { __rt.textContent = String(${text.wrappedExpression}) }${bf}) }`);
|
|
14941
15031
|
}
|
|
14942
15032
|
}
|
|
14943
15033
|
for (const attr of emit.reactiveAttrs) {
|
|
@@ -14947,7 +15037,7 @@ function emitReactive(lines, inner, indent) {
|
|
|
14947
15037
|
for (const stmt of emitAttrUpdate(targetVar, attr.attrName, attr.wrappedExpression, attr.meta)) {
|
|
14948
15038
|
lines.push(`${indent} ${stmt}`);
|
|
14949
15039
|
}
|
|
14950
|
-
lines.push(`${indent} }) }`);
|
|
15040
|
+
lines.push(`${indent} }${profileBindingId(pc, attr.slotId)}) }`);
|
|
14951
15041
|
}
|
|
14952
15042
|
emitLoopChildRefs(lines, emit.childRefs, {
|
|
14953
15043
|
indent: `${indent} `,
|
|
@@ -14955,9 +15045,9 @@ function emitReactive(lines, inner, indent) {
|
|
|
14955
15045
|
bodyIsMultiRoot: emit.bodyIsMultiRoot
|
|
14956
15046
|
});
|
|
14957
15047
|
lines.push(`${indent} return __innerEl${uid}`);
|
|
14958
|
-
lines.push(`${indent}}, '${inner.markerId}') }`);
|
|
15048
|
+
lines.push(`${indent}}, '${inner.markerId}'${profileBindingId(pc, inner.slotId)}) }`);
|
|
14959
15049
|
}
|
|
14960
|
-
function emitStatic(lines, inner, indent) {
|
|
15050
|
+
function emitStatic(lines, inner, indent, pc) {
|
|
14961
15051
|
const uid = inner.uidSuffix;
|
|
14962
15052
|
const emit = inner.emit;
|
|
14963
15053
|
if (emit.mode !== "static")
|
|
@@ -14975,7 +15065,7 @@ function emitStatic(lines, inner, indent) {
|
|
|
14975
15065
|
}
|
|
14976
15066
|
emitComponentAndEventSetup(lines, `${indent} `, `__innerEl${uid}`, [...emit.components], [...emit.events], inner.outerLoopParam, inner.outerLoopParamBindings);
|
|
14977
15067
|
if (inner.childLevels.length > 0) {
|
|
14978
|
-
stringifyInnerLoops(lines, inner.childLevels, `${indent}
|
|
15068
|
+
stringifyInnerLoops(lines, inner.childLevels, `${indent} `, pc);
|
|
14979
15069
|
}
|
|
14980
15070
|
emitLoopChildRefs(lines, emit.childRefs, {
|
|
14981
15071
|
indent: `${indent} `,
|
|
@@ -15007,7 +15097,9 @@ function stringifyCompositeLoop(lines, plan) {
|
|
|
15007
15097
|
branchClearChildren,
|
|
15008
15098
|
topIndent,
|
|
15009
15099
|
bodyIndent: rawBodyIndent,
|
|
15010
|
-
bodyIsMultiRoot
|
|
15100
|
+
bodyIsMultiRoot,
|
|
15101
|
+
profileComponentName: pc,
|
|
15102
|
+
profileLoopId
|
|
15011
15103
|
} = plan;
|
|
15012
15104
|
const bodyIndent = branchClearChildren ? rawBodyIndent + " " : rawBodyIndent;
|
|
15013
15105
|
const mapArrayIndent = branchClearChildren ? topIndent + " " : topIndent;
|
|
@@ -15033,18 +15125,19 @@ function stringifyCompositeLoop(lines, plan) {
|
|
|
15033
15125
|
});
|
|
15034
15126
|
emitComponentAndEventSetup(lines, bodyIndent, "__el", compsArr, eventsArr, loopParam, loopParamBindings, bodyIsMultiRoot);
|
|
15035
15127
|
if (innerLoops.length > 0) {
|
|
15036
|
-
stringifyInnerLoops(lines, innerLoops, bodyIndent);
|
|
15128
|
+
stringifyInnerLoops(lines, innerLoops, bodyIndent, pc);
|
|
15037
15129
|
}
|
|
15038
15130
|
if (reactiveEffects) {
|
|
15039
15131
|
stringifyReactiveEffects(lines, reactiveEffects, { indent: bodyIndent, elVar: "__el", bodyIsMultiRoot });
|
|
15040
15132
|
}
|
|
15041
15133
|
emitLoopChildRefs(lines, childRefs, { indent: bodyIndent, elVar: "__el", bodyIsMultiRoot });
|
|
15042
15134
|
lines.push(`${bodyIndent}return __el`);
|
|
15135
|
+
const loopBfId = profileLoopId ? `, ${JSON.stringify(profileLoopId)}` : "";
|
|
15043
15136
|
if (branchClearChildren) {
|
|
15044
|
-
lines.push(`${mapArrayIndent}}, '${markerId}')`);
|
|
15137
|
+
lines.push(`${mapArrayIndent}}, '${markerId}'${loopBfId})`);
|
|
15045
15138
|
lines.push(`${topIndent}}))`);
|
|
15046
15139
|
} else {
|
|
15047
|
-
lines.push(`${topIndent}}, '${markerId}')`);
|
|
15140
|
+
lines.push(`${topIndent}}, '${markerId}'${loopBfId})`);
|
|
15048
15141
|
}
|
|
15049
15142
|
}
|
|
15050
15143
|
|
|
@@ -15059,8 +15152,14 @@ var NON_BUBBLING_EVENTS = new Set([
|
|
|
15059
15152
|
"pointerenter",
|
|
15060
15153
|
"pointerleave"
|
|
15061
15154
|
]);
|
|
15155
|
+
function withTurn(call, componentName, childSlotId, eventName) {
|
|
15156
|
+
if (!componentName)
|
|
15157
|
+
return call;
|
|
15158
|
+
const id = JSON.stringify(`${componentName}#handler:${childSlotId}:${eventName}`);
|
|
15159
|
+
return `beginTurn(${id}); try { ${call} } finally { endTurn() }`;
|
|
15160
|
+
}
|
|
15062
15161
|
function stringifyEventDelegation(lines, plan) {
|
|
15063
|
-
const { containerVar, events, itemLookup } = plan;
|
|
15162
|
+
const { containerVar, events, itemLookup, profileComponentName } = plan;
|
|
15064
15163
|
const eventsByName = new Map;
|
|
15065
15164
|
for (const ev of events) {
|
|
15066
15165
|
if (!eventsByName.has(ev.eventName))
|
|
@@ -15080,7 +15179,7 @@ function stringifyEventDelegation(lines, plan) {
|
|
|
15080
15179
|
const childVar = varSlotId(ev.childSlotId);
|
|
15081
15180
|
lines.push(` const ${childVar}El = target.closest('[bf="${ev.childSlotId}"]')`);
|
|
15082
15181
|
lines.push(` if (${childVar}El) {`);
|
|
15083
|
-
const handlerCall = `(${ev.handler.trim()})(__bfEvt)
|
|
15182
|
+
const handlerCall = withTurn(`(${ev.handler.trim()})(__bfEvt)`, profileComponentName, ev.childSlotId, ev.eventName);
|
|
15084
15183
|
switch (itemLookup.kind) {
|
|
15085
15184
|
case "keyed":
|
|
15086
15185
|
emitKeyedLookup(lines, ev, handlerCall, itemLookup);
|
|
@@ -15215,16 +15314,18 @@ function emitPlain(lines, plan) {
|
|
|
15215
15314
|
reactiveEffects,
|
|
15216
15315
|
eventDelegation,
|
|
15217
15316
|
childRefs,
|
|
15218
|
-
bodyIsMultiRoot
|
|
15317
|
+
bodyIsMultiRoot,
|
|
15318
|
+
profileLoopId
|
|
15219
15319
|
} = plan;
|
|
15320
|
+
const loopBfId = profileLoopId ? `, ${JSON.stringify(profileLoopId)}` : "";
|
|
15220
15321
|
const unwrapInline = paramUnwrap ? `${paramUnwrap} ` : "";
|
|
15221
15322
|
lines.push(` __disposers.push(createDisposableEffect(() => {`);
|
|
15222
15323
|
if (reactiveEffects === null && !bodyIsMultiRoot && childRefs.length === 0) {
|
|
15223
15324
|
const cloneExpr = emitTemplateCloneInline(template);
|
|
15224
15325
|
if (mapPreambleWrapped) {
|
|
15225
|
-
lines.push(` if (${containerVar}) mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (${paramHead}, ${indexParam}, __existing) => { ${unwrapInline}if (__existing) return __existing; ${mapPreambleWrapped}; ${cloneExpr} }, '${markerId}')`);
|
|
15326
|
+
lines.push(` if (${containerVar}) mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (${paramHead}, ${indexParam}, __existing) => { ${unwrapInline}if (__existing) return __existing; ${mapPreambleWrapped}; ${cloneExpr} }, '${markerId}'${loopBfId})`);
|
|
15226
15327
|
} else {
|
|
15227
|
-
lines.push(` if (${containerVar}) mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (${paramHead}, ${indexParam}, __existing) => { ${unwrapInline}if (__existing) return __existing; ${cloneExpr} }, '${markerId}')`);
|
|
15328
|
+
lines.push(` if (${containerVar}) mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (${paramHead}, ${indexParam}, __existing) => { ${unwrapInline}if (__existing) return __existing; ${cloneExpr} }, '${markerId}'${loopBfId})`);
|
|
15228
15329
|
}
|
|
15229
15330
|
} else {
|
|
15230
15331
|
lines.push(` if (${containerVar}) mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (${paramHead}, ${indexParam}, __existing) => {`);
|
|
@@ -15245,7 +15346,7 @@ function emitPlain(lines, plan) {
|
|
|
15245
15346
|
}
|
|
15246
15347
|
emitLoopChildRefs(lines, childRefs, { indent: " ", elVar: "__el", bodyIsMultiRoot });
|
|
15247
15348
|
lines.push(` return __el`);
|
|
15248
|
-
lines.push(` }, '${markerId}')`);
|
|
15349
|
+
lines.push(` }, '${markerId}'${loopBfId})`);
|
|
15249
15350
|
}
|
|
15250
15351
|
lines.push(` }))`);
|
|
15251
15352
|
stringifyEventDelegation(lines, eventDelegation);
|
|
@@ -15256,19 +15357,21 @@ function stringifyInsert(lines, plan, opts) {
|
|
|
15256
15357
|
const { leadingIndent, bodyIndent } = opts;
|
|
15257
15358
|
const scopeVar = scopeRefToVar(plan.scope);
|
|
15258
15359
|
const armIndent = leadingIndent + " ";
|
|
15360
|
+
const condBfId = plan.profileComponentName ? `, ${JSON.stringify(`${plan.profileComponentName}#binding:${plan.slotId}`)}` : "";
|
|
15259
15361
|
lines.push(`${leadingIndent}insert(${scopeVar}, '${plan.slotId}', () => ${plan.condition}, {`);
|
|
15260
|
-
emitArm(lines, plan.arms[0], plan.eventNameMode, armIndent, bodyIndent);
|
|
15362
|
+
emitArm(lines, plan.arms[0], plan.eventNameMode, armIndent, bodyIndent, plan.profileComponentName);
|
|
15261
15363
|
lines.push(`${leadingIndent}}, {`);
|
|
15262
|
-
emitArm(lines, plan.arms[1], plan.eventNameMode, armIndent, bodyIndent);
|
|
15263
|
-
lines.push(`${leadingIndent}})`);
|
|
15364
|
+
emitArm(lines, plan.arms[1], plan.eventNameMode, armIndent, bodyIndent, plan.profileComponentName);
|
|
15365
|
+
lines.push(`${leadingIndent}}${condBfId})`);
|
|
15264
15366
|
}
|
|
15265
|
-
function emitArm(lines, arm, mode, armIndent, bodyIndent) {
|
|
15367
|
+
function emitArm(lines, arm, mode, armIndent, bodyIndent, profileComponentName) {
|
|
15266
15368
|
lines.push(`${armIndent}template: () => { const __slots = []; return { html: \`${arm.templateHtml}\`, slots: __slots } },`);
|
|
15267
15369
|
lines.push(`${armIndent}bindEvents: (__branchScope, { isFirstRun: __bfFirstRun = false } = {}) => {`);
|
|
15268
|
-
emitArmBody2(lines, arm.body, mode, bodyIndent);
|
|
15370
|
+
emitArmBody2(lines, arm.body, mode, bodyIndent, profileComponentName);
|
|
15269
15371
|
lines.push(`${armIndent}}`);
|
|
15270
15372
|
}
|
|
15271
|
-
function emitArmBody2(lines, body, mode, indent) {
|
|
15373
|
+
function emitArmBody2(lines, body, mode, indent, profileComponentName) {
|
|
15374
|
+
const bindingBfId = (slotId) => profileComponentName ? `, ${JSON.stringify(`${profileComponentName}#binding:${slotId}`)}` : "";
|
|
15272
15375
|
const allSlotIds = new Set;
|
|
15273
15376
|
for (const ev of body.events)
|
|
15274
15377
|
allSlotIds.add(ev.slotId);
|
|
@@ -15289,7 +15392,7 @@ function emitArmBody2(lines, body, mode, indent) {
|
|
|
15289
15392
|
for (const [slotId, slotEvents] of eventsBySlot) {
|
|
15290
15393
|
const v = varSlotId(slotId);
|
|
15291
15394
|
for (const ev of slotEvents) {
|
|
15292
|
-
emitListenerLine(lines, indent, `_${v}`, ev.eventName, ev.handler, mode);
|
|
15395
|
+
emitListenerLine(lines, indent, `_${v}`, ev.eventName, ev.handler, mode, ev.turnId);
|
|
15293
15396
|
}
|
|
15294
15397
|
}
|
|
15295
15398
|
for (const ref of body.refs) {
|
|
@@ -15323,7 +15426,7 @@ function emitArmBody2(lines, body, mode, indent) {
|
|
|
15323
15426
|
for (const stmt of emitAttrUpdate(elVar, attr.attrName, attr.expression, attr)) {
|
|
15324
15427
|
lines.push(`${indent} ${stmt}`);
|
|
15325
15428
|
}
|
|
15326
|
-
lines.push(`${indent} }))`);
|
|
15429
|
+
lines.push(`${indent} }${bindingBfId(slotId)}))`);
|
|
15327
15430
|
}
|
|
15328
15431
|
lines.push(`${indent}} }`);
|
|
15329
15432
|
}
|
|
@@ -15333,7 +15436,7 @@ function emitArmBody2(lines, body, mode, indent) {
|
|
|
15333
15436
|
lines.push(`${indent}__disposers.push(createDisposableEffect(() => {`);
|
|
15334
15437
|
lines.push(`${indent} const __val = ${te.expression}`);
|
|
15335
15438
|
lines.push(`${indent} __anchor_${v} = __bfText(__anchor_${v}, __val)`);
|
|
15336
|
-
lines.push(`${indent}}))`);
|
|
15439
|
+
lines.push(`${indent}}${bindingBfId(te.slotId)}))`);
|
|
15337
15440
|
}
|
|
15338
15441
|
if (body.loops.length > 0) {
|
|
15339
15442
|
stringifyBranchLoops(lines, body.loops);
|
|
@@ -15360,7 +15463,7 @@ function scopeRefToVar(ref) {
|
|
|
15360
15463
|
}
|
|
15361
15464
|
|
|
15362
15465
|
// src/ir-to-client-js/control-flow/plan/build-component-loop.ts
|
|
15363
|
-
function buildComponentLoopPlan(elem) {
|
|
15466
|
+
function buildComponentLoopPlan(elem, profileComponentName) {
|
|
15364
15467
|
const { name } = elem.childComponent;
|
|
15365
15468
|
const propsExpr = buildComponentPropsExpr2(elem.childComponent, elem.param);
|
|
15366
15469
|
const keyExpr = wrapLoopParamAsAccessor(elem.key || "__idx", elem.param, elem.paramBindings);
|
|
@@ -15393,12 +15496,14 @@ function buildComponentLoopPlan(elem) {
|
|
|
15393
15496
|
keyExpr,
|
|
15394
15497
|
nestedComps,
|
|
15395
15498
|
childRefs: buildChildRefBindings(elem.bindings.refs, elem.param, elem.paramBindings),
|
|
15499
|
+
profileLoopId: profileComponentName ? `${profileComponentName}#binding:${elem.slotId}` : undefined,
|
|
15396
15500
|
childConditionalEffects: hasChildConds ? buildReactiveEffectsPlan({
|
|
15397
15501
|
attrs: [],
|
|
15398
15502
|
texts: [],
|
|
15399
15503
|
conditionals: elem.bindings.conditionals,
|
|
15400
15504
|
loopParam: elem.param,
|
|
15401
|
-
loopParamBindings: elem.paramBindings
|
|
15505
|
+
loopParamBindings: elem.paramBindings,
|
|
15506
|
+
profileComponentName
|
|
15402
15507
|
}) : null
|
|
15403
15508
|
};
|
|
15404
15509
|
}
|
|
@@ -15406,21 +15511,21 @@ function buildComponentLoopPlan(elem) {
|
|
|
15406
15511
|
// src/ir-to-client-js/control-flow/plan/build-loop.ts
|
|
15407
15512
|
function buildLoopPlan(elem, opts) {
|
|
15408
15513
|
if (elem.bodyIsItemConditional) {
|
|
15409
|
-
return buildPlainLoopPlan(elem);
|
|
15514
|
+
return buildPlainLoopPlan(elem, opts.profileComponentName);
|
|
15410
15515
|
}
|
|
15411
15516
|
if (elem.isStaticArray) {
|
|
15412
|
-
return buildStaticLoopPlan(elem, opts.unsafeLocalNames);
|
|
15517
|
+
return buildStaticLoopPlan(elem, opts.unsafeLocalNames, opts.profileComponentName);
|
|
15413
15518
|
}
|
|
15414
15519
|
const hasInnerStructure = (elem.nestedComponents?.length ?? 0) > 0 || (elem.innerLoops?.length ?? 0) > 0;
|
|
15415
15520
|
if (elem.useElementReconciliation && hasInnerStructure) {
|
|
15416
|
-
return buildTopLevelCompositePlan(elem);
|
|
15521
|
+
return buildTopLevelCompositePlan(elem, opts.profileComponentName);
|
|
15417
15522
|
}
|
|
15418
15523
|
if (elem.childComponent) {
|
|
15419
|
-
return buildComponentLoopPlan(elem);
|
|
15524
|
+
return buildComponentLoopPlan(elem, opts.profileComponentName);
|
|
15420
15525
|
}
|
|
15421
|
-
return buildPlainLoopPlan(elem);
|
|
15526
|
+
return buildPlainLoopPlan(elem, opts.profileComponentName);
|
|
15422
15527
|
}
|
|
15423
|
-
function buildPlainLoopPlan(elem) {
|
|
15528
|
+
function buildPlainLoopPlan(elem, profileComponentName) {
|
|
15424
15529
|
const wrap = (expr) => wrapLoopParamAsAccessor(expr, elem.param, elem.paramBindings);
|
|
15425
15530
|
const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(elem.param, elem.paramBindings);
|
|
15426
15531
|
const hasReactive2 = elem.bindings.reactiveAttrs.length > 0 || elem.bindings.reactiveTexts.length > 0 || elem.bindings.conditionals.length > 0;
|
|
@@ -15428,6 +15533,7 @@ function buildPlainLoopPlan(elem) {
|
|
|
15428
15533
|
kind: "plain",
|
|
15429
15534
|
containerVar: `_${varSlotId(elem.slotId)}`,
|
|
15430
15535
|
markerId: elem.markerId,
|
|
15536
|
+
profileLoopId: profileComponentName ? `${profileComponentName}#binding:${elem.slotId}` : undefined,
|
|
15431
15537
|
arrayExpr: buildChainedArrayExpr(elem),
|
|
15432
15538
|
keyFn: loopKeyFn(elem),
|
|
15433
15539
|
paramHead,
|
|
@@ -15435,14 +15541,14 @@ function buildPlainLoopPlan(elem) {
|
|
|
15435
15541
|
indexParam: elem.index || "__idx",
|
|
15436
15542
|
mapPreambleWrapped: elem.mapPreamble ? wrap(elem.mapPreamble) : "",
|
|
15437
15543
|
template: elem.template,
|
|
15438
|
-
reactiveEffects: hasReactive2 ? buildLoopReactiveEffectsPlan(elem) : null,
|
|
15544
|
+
reactiveEffects: hasReactive2 ? buildLoopReactiveEffectsPlan(elem, profileComponentName) : null,
|
|
15439
15545
|
childRefs: buildChildRefBindings(elem.bindings.refs, elem.param, elem.paramBindings),
|
|
15440
15546
|
bodyIsMultiRoot: elem.bodyIsMultiRoot ?? false,
|
|
15441
15547
|
anchored: elem.bodyIsItemConditional ?? false,
|
|
15442
15548
|
anchorKeyExpr: elem.key ? wrap(elem.key) : elem.index || "__idx"
|
|
15443
15549
|
};
|
|
15444
15550
|
}
|
|
15445
|
-
function buildStaticLoopPlan(elem, unsafeLocalNames) {
|
|
15551
|
+
function buildStaticLoopPlan(elem, unsafeLocalNames, profileComponentName) {
|
|
15446
15552
|
const attrsBySlotMap = new Map;
|
|
15447
15553
|
if (!elem.childComponent) {
|
|
15448
15554
|
for (const attr of elem.bindings.reactiveAttrs) {
|
|
@@ -15463,6 +15569,7 @@ function buildStaticLoopPlan(elem, unsafeLocalNames) {
|
|
|
15463
15569
|
param: elem.param,
|
|
15464
15570
|
indexParam,
|
|
15465
15571
|
childIndexExpr,
|
|
15572
|
+
profileComponentName,
|
|
15466
15573
|
attrsBySlot: [...attrsBySlotMap].map(([slotId, attrs]) => [slotId, attrs]),
|
|
15467
15574
|
texts: elem.bindings.reactiveTexts,
|
|
15468
15575
|
childRefs: buildStaticChildRefBindings(elem.bindings.refs),
|
|
@@ -15485,15 +15592,17 @@ function buildStaticLoopMaterialize(elem, unsafeLocalNames) {
|
|
|
15485
15592
|
|
|
15486
15593
|
// src/ir-to-client-js/control-flow.ts
|
|
15487
15594
|
function emitConditionalUpdates(lines, ctx) {
|
|
15595
|
+
const profileComponentName = ctx.profile ? ctx.componentName : undefined;
|
|
15488
15596
|
for (const elem of ctx.conditionalElements) {
|
|
15489
|
-
const plan = buildInsertPlan(elem, { scope: { kind: "top" }, eventNameMode: "dom" });
|
|
15597
|
+
const plan = buildInsertPlan(elem, { scope: { kind: "top" }, eventNameMode: "dom", profileComponentName });
|
|
15490
15598
|
stringifyInsert(lines, plan, { leadingIndent: " ", bodyIndent: " " });
|
|
15491
15599
|
lines.push("");
|
|
15492
15600
|
}
|
|
15493
15601
|
}
|
|
15494
15602
|
function emitClientOnlyConditionals(lines, ctx) {
|
|
15603
|
+
const profileComponentName = ctx.profile ? ctx.componentName : undefined;
|
|
15495
15604
|
for (const elem of ctx.clientOnlyConditionals) {
|
|
15496
|
-
const plan = buildInsertPlan(elem, { scope: { kind: "top" }, eventNameMode: "raw" });
|
|
15605
|
+
const plan = buildInsertPlan(elem, { scope: { kind: "top" }, eventNameMode: "raw", profileComponentName });
|
|
15497
15606
|
lines.push(` // @client conditional: ${elem.slotId}`);
|
|
15498
15607
|
stringifyInsert(lines, plan, { leadingIndent: " ", bodyIndent: " " });
|
|
15499
15608
|
lines.push("");
|
|
@@ -15501,20 +15610,23 @@ function emitClientOnlyConditionals(lines, ctx) {
|
|
|
15501
15610
|
}
|
|
15502
15611
|
function emitLoopUpdates(lines, ctx, unsafeLocalNames) {
|
|
15503
15612
|
for (const elem of ctx.loopElements) {
|
|
15504
|
-
const plan = buildLoopPlan(elem, {
|
|
15613
|
+
const plan = buildLoopPlan(elem, {
|
|
15614
|
+
unsafeLocalNames,
|
|
15615
|
+
profileComponentName: ctx.profile ? ctx.componentName : undefined
|
|
15616
|
+
});
|
|
15505
15617
|
stringifyLoop(lines, plan);
|
|
15506
|
-
emitLoopEventDelegation(lines, elem, plan.kind);
|
|
15618
|
+
emitLoopEventDelegation(lines, elem, plan.kind, ctx.profile ? ctx.componentName : undefined);
|
|
15507
15619
|
}
|
|
15508
15620
|
}
|
|
15509
|
-
function emitLoopEventDelegation(lines, elem, kind) {
|
|
15621
|
+
function emitLoopEventDelegation(lines, elem, kind, profileComponentName) {
|
|
15510
15622
|
if (kind === "static") {
|
|
15511
15623
|
if (!elem.childComponent && elem.bindings.events.length > 0) {
|
|
15512
|
-
stringifyEventDelegation(lines, buildStaticArrayDelegationPlan(elem));
|
|
15624
|
+
stringifyEventDelegation(lines, buildStaticArrayDelegationPlan(elem, profileComponentName));
|
|
15513
15625
|
}
|
|
15514
15626
|
return;
|
|
15515
15627
|
}
|
|
15516
15628
|
if (kind === "plain" && !elem.useElementReconciliation && elem.bindings.events.length > 0) {
|
|
15517
|
-
stringifyEventDelegation(lines, buildDynamicLoopDelegationPlan(elem));
|
|
15629
|
+
stringifyEventDelegation(lines, buildDynamicLoopDelegationPlan(elem, profileComponentName));
|
|
15518
15630
|
}
|
|
15519
15631
|
}
|
|
15520
15632
|
|
|
@@ -15973,11 +16085,11 @@ function mapEventHandlers(lines, node, gen) {
|
|
|
15973
16085
|
}
|
|
15974
16086
|
|
|
15975
16087
|
// src/ir-to-client-js/index.ts
|
|
15976
|
-
function generateClientJs(ir, siblingComponents, localImportPrefixes, scope, adapterCapabilities) {
|
|
15977
|
-
return generateClientJsWithSourceMap(ir, siblingComponents, localImportPrefixes, undefined, scope, adapterCapabilities).code;
|
|
16088
|
+
function generateClientJs(ir, siblingComponents, localImportPrefixes, scope, adapterCapabilities, profile) {
|
|
16089
|
+
return generateClientJsWithSourceMap(ir, siblingComponents, localImportPrefixes, undefined, scope, adapterCapabilities, profile).code;
|
|
15978
16090
|
}
|
|
15979
|
-
function generateClientJsWithSourceMap(ir, siblingComponents, localImportPrefixes, options, scope, adapterCapabilities) {
|
|
15980
|
-
const ctx = createContext(ir, scope, adapterCapabilities);
|
|
16091
|
+
function generateClientJsWithSourceMap(ir, siblingComponents, localImportPrefixes, options, scope, adapterCapabilities, profile) {
|
|
16092
|
+
const ctx = createContext(ir, scope, adapterCapabilities, profile);
|
|
15981
16093
|
const siblingOffsets = computeLoopSiblingOffsets(ir.root);
|
|
15982
16094
|
collectElements(ir.root, ctx, siblingOffsets);
|
|
15983
16095
|
if (!needsClientJs(ctx)) {
|
|
@@ -16022,11 +16134,12 @@ function analyzeClientNeeds(ir) {
|
|
|
16022
16134
|
}
|
|
16023
16135
|
return { needsInit: true, usedProps: [...neededProps] };
|
|
16024
16136
|
}
|
|
16025
|
-
function createContext(ir, scope, adapterCapabilities) {
|
|
16137
|
+
function createContext(ir, scope, adapterCapabilities, profile) {
|
|
16026
16138
|
return {
|
|
16027
16139
|
componentName: ir.metadata.componentName,
|
|
16028
16140
|
fileScope: scope?.fileScope ?? "",
|
|
16029
16141
|
nonExportedSiblings: scope?.nonExportedSiblings ?? new Set,
|
|
16142
|
+
profile: profile ?? false,
|
|
16030
16143
|
signals: ir.metadata.signals,
|
|
16031
16144
|
memos: ir.metadata.memos,
|
|
16032
16145
|
effects: ir.metadata.effects,
|
|
@@ -16775,8 +16888,41 @@ function extractSsrDefaults(metadata) {
|
|
|
16775
16888
|
out[memo.name] = { value: resultToJsonable(value) };
|
|
16776
16889
|
bindings[memo.name] = value;
|
|
16777
16890
|
}
|
|
16891
|
+
if (metadata.propsObjectName !== null) {
|
|
16892
|
+
const referenced = new Set;
|
|
16893
|
+
for (const sig of metadata.signals) {
|
|
16894
|
+
if (!sig.getter || sig.isModule)
|
|
16895
|
+
continue;
|
|
16896
|
+
collectPropRefs(sig.initialValue, metadata.propsObjectName, referenced);
|
|
16897
|
+
}
|
|
16898
|
+
for (const memo of metadata.memos) {
|
|
16899
|
+
if (memo.isModule)
|
|
16900
|
+
continue;
|
|
16901
|
+
collectPropRefs(memo.computation, metadata.propsObjectName, referenced);
|
|
16902
|
+
}
|
|
16903
|
+
for (const name of referenced) {
|
|
16904
|
+
if (name in out)
|
|
16905
|
+
continue;
|
|
16906
|
+
out[name] = { propName: name, value: null };
|
|
16907
|
+
}
|
|
16908
|
+
}
|
|
16778
16909
|
return Object.keys(out).length === 0 ? undefined : out;
|
|
16779
16910
|
}
|
|
16911
|
+
function collectPropRefs(expr, propsObjectName, out) {
|
|
16912
|
+
if (!expr || !expr.trim())
|
|
16913
|
+
return;
|
|
16914
|
+
const node = parseExpression2(expr);
|
|
16915
|
+
if (!node)
|
|
16916
|
+
return;
|
|
16917
|
+
const visit3 = (n) => {
|
|
16918
|
+
if (ts16.isPropertyAccessExpression(n) && ts16.isIdentifier(n.expression) && n.expression.text === propsObjectName && ts16.isIdentifier(n.name)) {
|
|
16919
|
+
out.add(n.name.text);
|
|
16920
|
+
return;
|
|
16921
|
+
}
|
|
16922
|
+
ts16.forEachChild(n, visit3);
|
|
16923
|
+
};
|
|
16924
|
+
visit3(node);
|
|
16925
|
+
}
|
|
16780
16926
|
function resultToJsonable(v) {
|
|
16781
16927
|
if (v === UNRESOLVED)
|
|
16782
16928
|
return null;
|
|
@@ -17135,7 +17281,7 @@ function compileMultipleComponents(source, filePath, componentNames, options) {
|
|
|
17135
17281
|
types,
|
|
17136
17282
|
moduleExports: moduleExports || "",
|
|
17137
17283
|
component,
|
|
17138
|
-
clientJs: generateClientJs(componentIR, componentNames, options.localImportPrefixes, undefined, multiAdapterCaps) || undefined,
|
|
17284
|
+
clientJs: generateClientJs(componentIR, componentNames, options.localImportPrefixes, undefined, multiAdapterCaps, options.profile) || undefined,
|
|
17139
17285
|
adapterTypes: adapterOutput.types || undefined
|
|
17140
17286
|
});
|
|
17141
17287
|
errors.push(...componentIR.errors);
|
|
@@ -17488,7 +17634,7 @@ function compileJSX(source, filePath, options) {
|
|
|
17488
17634
|
const result = generateClientJsWithSourceMap(componentIR, undefined, options.localImportPrefixes, {
|
|
17489
17635
|
sourceMaps: true,
|
|
17490
17636
|
generatedFileName: clientJsPath.split("/").pop()
|
|
17491
|
-
}, undefined, adapterCaps);
|
|
17637
|
+
}, undefined, adapterCaps, options.profile);
|
|
17492
17638
|
errors.push(...componentIR.errors);
|
|
17493
17639
|
if (result.code) {
|
|
17494
17640
|
files.push({ path: clientJsPath, content: result.code, type: "clientJs" });
|
|
@@ -17497,7 +17643,7 @@ function compileJSX(source, filePath, options) {
|
|
|
17497
17643
|
}
|
|
17498
17644
|
}
|
|
17499
17645
|
} else {
|
|
17500
|
-
const clientJs = generateClientJs(componentIR, undefined, options.localImportPrefixes, undefined, adapterCaps);
|
|
17646
|
+
const clientJs = generateClientJs(componentIR, undefined, options.localImportPrefixes, undefined, adapterCaps, options.profile);
|
|
17501
17647
|
errors.push(...componentIR.errors);
|
|
17502
17648
|
if (clientJs) {
|
|
17503
17649
|
files.push({ path: clientJsPath, content: clientJs, type: "clientJs" });
|
|
@@ -17679,6 +17825,7 @@ class JsxAdapter extends BaseAdapter {
|
|
|
17679
17825
|
return `${BF_COND}="${condId}"`;
|
|
17680
17826
|
}
|
|
17681
17827
|
}
|
|
17828
|
+
|
|
17682
17829
|
// src/adapters/template-imports.ts
|
|
17683
17830
|
var CLIENT_PACKAGE_SOURCES = new Set([
|
|
17684
17831
|
"@barefootjs/client",
|
|
@@ -17726,6 +17873,265 @@ function rewriteImportsForTemplate(imports, shimSource, rewriteRelative) {
|
|
|
17726
17873
|
function specKey(s) {
|
|
17727
17874
|
return `${s.isDefault ? "d" : ""}${s.isNamespace ? "n" : ""}:${s.name}:${s.alias ?? ""}`;
|
|
17728
17875
|
}
|
|
17876
|
+
|
|
17877
|
+
// src/adapters/test-adapter.ts
|
|
17878
|
+
class TestAdapter extends JsxAdapter {
|
|
17879
|
+
name = "test";
|
|
17880
|
+
extension = ".test.tsx";
|
|
17881
|
+
jsxConfig = { preserveTypes: false };
|
|
17882
|
+
generate(ir) {
|
|
17883
|
+
this.componentName = ir.metadata.componentName;
|
|
17884
|
+
const imports = this.generateImports(ir);
|
|
17885
|
+
const types = this.generateTypes(ir);
|
|
17886
|
+
const component = this.generateComponent(ir);
|
|
17887
|
+
const defaultExport = ir.metadata.hasDefaultExport ? `
|
|
17888
|
+
export default ${this.componentName}` : "";
|
|
17889
|
+
const sections = {
|
|
17890
|
+
imports,
|
|
17891
|
+
types: types || "",
|
|
17892
|
+
component,
|
|
17893
|
+
defaultExport
|
|
17894
|
+
};
|
|
17895
|
+
const template = [imports, types, component].filter(Boolean).join(`
|
|
17896
|
+
|
|
17897
|
+
`) + defaultExport;
|
|
17898
|
+
return {
|
|
17899
|
+
template,
|
|
17900
|
+
sections,
|
|
17901
|
+
types: types || undefined,
|
|
17902
|
+
extension: this.extension
|
|
17903
|
+
};
|
|
17904
|
+
}
|
|
17905
|
+
generateImports(ir) {
|
|
17906
|
+
const lines = [];
|
|
17907
|
+
const templateImports = rewriteImportsForTemplate(ir.metadata.templateImports, undefined);
|
|
17908
|
+
for (const imp of templateImports) {
|
|
17909
|
+
if (imp.specifiers.length === 0) {
|
|
17910
|
+
if (!imp.isTypeOnly) {
|
|
17911
|
+
lines.push(`import '${imp.source}'`);
|
|
17912
|
+
}
|
|
17913
|
+
continue;
|
|
17914
|
+
}
|
|
17915
|
+
if (imp.isTypeOnly) {
|
|
17916
|
+
lines.push(`import type ${this.formatImportSpecifiers(imp.specifiers)} from '${imp.source}'`);
|
|
17917
|
+
} else {
|
|
17918
|
+
lines.push(`import ${this.formatImportSpecifiers(imp.specifiers)} from '${imp.source}'`);
|
|
17919
|
+
}
|
|
17920
|
+
}
|
|
17921
|
+
return lines.join(`
|
|
17922
|
+
`);
|
|
17923
|
+
}
|
|
17924
|
+
generateTypes(ir) {
|
|
17925
|
+
const lines = [];
|
|
17926
|
+
for (const typeDef of ir.metadata.typeDefinitions) {
|
|
17927
|
+
lines.push(typeDef.definition);
|
|
17928
|
+
}
|
|
17929
|
+
const propsTypeName = ir.metadata.propsType?.raw;
|
|
17930
|
+
if (propsTypeName && !ir.metadata.propsObjectName) {
|
|
17931
|
+
lines.push("");
|
|
17932
|
+
lines.push(`type ${this.componentName}PropsWithHydration = ${propsTypeName} & {`);
|
|
17933
|
+
lines.push(" __instanceId?: string");
|
|
17934
|
+
lines.push(" __bfScope?: string");
|
|
17935
|
+
lines.push("}");
|
|
17936
|
+
}
|
|
17937
|
+
return lines.length > 0 ? lines.join(`
|
|
17938
|
+
`) : null;
|
|
17939
|
+
}
|
|
17940
|
+
generateComponent(ir) {
|
|
17941
|
+
const name = ir.metadata.componentName;
|
|
17942
|
+
const propsTypeName = ir.metadata.propsType?.raw;
|
|
17943
|
+
const hasClientInteractivity = ir.metadata.signals.length > 0 || ir.metadata.memos.length > 0;
|
|
17944
|
+
const typeAnnotation = propsTypeName ? `: ${name}PropsWithHydration` : ": { __instanceId?: string; __bfScope?: string }";
|
|
17945
|
+
const jsxBody = this.renderNode(ir.root);
|
|
17946
|
+
const signalInits = this.generateSignalInitializers(ir, jsxBody);
|
|
17947
|
+
const scopeIdLine = hasClientInteractivity ? `(/_s\\d/.test(__bfScope || '') ? __bfScope : null) || __instanceId` : `__bfScope || __instanceId`;
|
|
17948
|
+
const bodyRefText = [jsxBody, signalInits, scopeIdLine].join(`
|
|
17949
|
+
`);
|
|
17950
|
+
const bfScopeAlias = /\b__bfScope\b/.test(bodyRefText) ? "__bfScope" : "__bfScope: _bfScope";
|
|
17951
|
+
const propsParams = ir.metadata.propsParams.map((p) => p.defaultValue ? `${p.name} = ${p.defaultValue}` : p.name).join(", ");
|
|
17952
|
+
const restPropsName = ir.metadata.restPropsName;
|
|
17953
|
+
const hydrationProps = `__instanceId, ${bfScopeAlias}`;
|
|
17954
|
+
const parts = [];
|
|
17955
|
+
if (propsParams) {
|
|
17956
|
+
parts.push(propsParams);
|
|
17957
|
+
}
|
|
17958
|
+
parts.push(hydrationProps);
|
|
17959
|
+
if (restPropsName) {
|
|
17960
|
+
parts.push(`...${restPropsName}`);
|
|
17961
|
+
}
|
|
17962
|
+
const fullPropsDestructure = `{ ${parts.join(", ")} }`;
|
|
17963
|
+
const hasRequiredProps = ir.metadata.propsParams.some((p) => !p.optional && p.defaultValue === undefined && !p.isRest);
|
|
17964
|
+
const propsTypeExpr = typeAnnotation.replace(/^:\s*/, "");
|
|
17965
|
+
const noArgDefault = hasRequiredProps ? "" : ` = {} as ${propsTypeExpr}`;
|
|
17966
|
+
const lines = [];
|
|
17967
|
+
const exportPrefix = ir.metadata.isExported === false ? "" : "export ";
|
|
17968
|
+
lines.push(`${exportPrefix}function ${name}(${fullPropsDestructure}${typeAnnotation}${noArgDefault}) {`);
|
|
17969
|
+
if (hasClientInteractivity) {
|
|
17970
|
+
lines.push(` const __scopeId = (/_s\\d/.test(__bfScope || '') ? __bfScope : null) || __instanceId || \`${name}_\${Math.random().toString(36).slice(2, 8)}\``);
|
|
17971
|
+
} else {
|
|
17972
|
+
lines.push(` const __scopeId = __bfScope || __instanceId || \`${name}_\${Math.random().toString(36).slice(2, 8)}\``);
|
|
17973
|
+
}
|
|
17974
|
+
if (signalInits) {
|
|
17975
|
+
lines.push(signalInits);
|
|
17976
|
+
}
|
|
17977
|
+
lines.push("");
|
|
17978
|
+
lines.push(` return (`);
|
|
17979
|
+
lines.push(` ${jsxBody}`);
|
|
17980
|
+
lines.push(` )`);
|
|
17981
|
+
lines.push(`}`);
|
|
17982
|
+
return lines.join(`
|
|
17983
|
+
`);
|
|
17984
|
+
}
|
|
17985
|
+
renderNode(node) {
|
|
17986
|
+
switch (node.type) {
|
|
17987
|
+
case "element":
|
|
17988
|
+
return this.renderElement(node);
|
|
17989
|
+
case "text":
|
|
17990
|
+
return node.value;
|
|
17991
|
+
case "expression":
|
|
17992
|
+
return this.renderExpression(node);
|
|
17993
|
+
case "conditional":
|
|
17994
|
+
return this.renderConditional(node);
|
|
17995
|
+
case "loop":
|
|
17996
|
+
return this.renderLoop(node);
|
|
17997
|
+
case "component":
|
|
17998
|
+
return this.renderComponent(node);
|
|
17999
|
+
case "fragment":
|
|
18000
|
+
return this.renderFragment(node);
|
|
18001
|
+
case "slot":
|
|
18002
|
+
return "{children}";
|
|
18003
|
+
default:
|
|
18004
|
+
return "";
|
|
18005
|
+
}
|
|
18006
|
+
}
|
|
18007
|
+
renderElement(element) {
|
|
18008
|
+
const tag = element.tag;
|
|
18009
|
+
const attrs = this.renderAttributes(element);
|
|
18010
|
+
const children = this.renderChildren(element.children);
|
|
18011
|
+
let hydrationAttrs = "";
|
|
18012
|
+
if (element.needsScope) {
|
|
18013
|
+
hydrationAttrs += " bf-s={__scopeId}";
|
|
18014
|
+
}
|
|
18015
|
+
if (element.slotId) {
|
|
18016
|
+
hydrationAttrs += ` bf="${element.slotId}"`;
|
|
18017
|
+
}
|
|
18018
|
+
if (children) {
|
|
18019
|
+
return `<${tag}${attrs}${hydrationAttrs}>${children}</${tag}>`;
|
|
18020
|
+
} else {
|
|
18021
|
+
return `<${tag}${attrs}${hydrationAttrs} />`;
|
|
18022
|
+
}
|
|
18023
|
+
}
|
|
18024
|
+
renderExpression(expr) {
|
|
18025
|
+
if (expr.expr === "null" || expr.expr === "undefined") {
|
|
18026
|
+
return "null";
|
|
18027
|
+
}
|
|
18028
|
+
if (expr.reactive && expr.slotId) {
|
|
18029
|
+
return `{bfText("${expr.slotId}")}{${expr.expr}}{bfTextEnd()}`;
|
|
18030
|
+
}
|
|
18031
|
+
return `{${expr.expr}}`;
|
|
18032
|
+
}
|
|
18033
|
+
renderConditional(cond) {
|
|
18034
|
+
const whenTrue = this.renderNodeRaw(cond.whenTrue);
|
|
18035
|
+
let whenFalse = this.renderNodeRaw(cond.whenFalse);
|
|
18036
|
+
if (!whenFalse || whenFalse === "" || whenFalse === "null") {
|
|
18037
|
+
whenFalse = "null";
|
|
18038
|
+
}
|
|
18039
|
+
return `{${cond.condition} ? ${whenTrue} : ${whenFalse}}`;
|
|
18040
|
+
}
|
|
18041
|
+
renderLoop(loop) {
|
|
18042
|
+
const indexParam = loop.index ? `, ${loop.index}` : "";
|
|
18043
|
+
const children = this.renderChildren(loop.children);
|
|
18044
|
+
const safeChildren = children.startsWith("{") ? `<>${children}</>` : children;
|
|
18045
|
+
return `{${loop.array}.map((${loop.param}${indexParam}) => ${safeChildren})}`;
|
|
18046
|
+
}
|
|
18047
|
+
renderComponent(comp) {
|
|
18048
|
+
const props = this.renderComponentProps(comp);
|
|
18049
|
+
const children = this.renderChildren(comp.children);
|
|
18050
|
+
const scopeAttr = " __bfScope={__scopeId}";
|
|
18051
|
+
if (children) {
|
|
18052
|
+
return `<${comp.name}${props}${scopeAttr}>${children}</${comp.name}>`;
|
|
18053
|
+
} else {
|
|
18054
|
+
return `<${comp.name}${props}${scopeAttr} />`;
|
|
18055
|
+
}
|
|
18056
|
+
}
|
|
18057
|
+
renderFragment(fragment) {
|
|
18058
|
+
const children = this.renderChildren(fragment.children);
|
|
18059
|
+
return `<>${children}</>`;
|
|
18060
|
+
}
|
|
18061
|
+
renderAttributes(element) {
|
|
18062
|
+
const parts = [];
|
|
18063
|
+
for (const attr of element.attrs) {
|
|
18064
|
+
const attrName = attr.name === "class" ? "className" : attr.name;
|
|
18065
|
+
switch (attr.value.kind) {
|
|
18066
|
+
case "spread":
|
|
18067
|
+
parts.push(`{...${attr.value.expr}}`);
|
|
18068
|
+
break;
|
|
18069
|
+
case "boolean-attr":
|
|
18070
|
+
parts.push(attrName);
|
|
18071
|
+
break;
|
|
18072
|
+
case "expression":
|
|
18073
|
+
parts.push(`${attrName}={${attr.value.expr}}`);
|
|
18074
|
+
break;
|
|
18075
|
+
case "template":
|
|
18076
|
+
parts.push(`${attrName}={${this.flattenTemplate(attr.value)}}`);
|
|
18077
|
+
break;
|
|
18078
|
+
case "literal":
|
|
18079
|
+
parts.push(`${attrName}="${attr.value.value}"`);
|
|
18080
|
+
break;
|
|
18081
|
+
case "boolean-shorthand":
|
|
18082
|
+
case "jsx-children":
|
|
18083
|
+
break;
|
|
18084
|
+
}
|
|
18085
|
+
}
|
|
18086
|
+
for (const event of element.events) {
|
|
18087
|
+
const handlerName = event.originalAttr ?? `on${event.name.charAt(0).toUpperCase()}${event.name.slice(1)}`;
|
|
18088
|
+
parts.push(`${handlerName}={() => {}}`);
|
|
18089
|
+
}
|
|
18090
|
+
return parts.length > 0 ? " " + parts.join(" ") : "";
|
|
18091
|
+
}
|
|
18092
|
+
flattenTemplate(value) {
|
|
18093
|
+
const v = value;
|
|
18094
|
+
return "`" + v.parts.map((p) => {
|
|
18095
|
+
if (p.type === "string")
|
|
18096
|
+
return p.value;
|
|
18097
|
+
if (p.type === "ternary")
|
|
18098
|
+
return `\${${p.condition} ? '${p.whenTrue}' : '${p.whenFalse}'}`;
|
|
18099
|
+
return `\${(${JSON.stringify(p.cases)})[${p.key}]}`;
|
|
18100
|
+
}).join("") + "`";
|
|
18101
|
+
}
|
|
18102
|
+
renderComponentProps(comp) {
|
|
18103
|
+
const parts = [];
|
|
18104
|
+
for (const prop of comp.props) {
|
|
18105
|
+
switch (prop.value.kind) {
|
|
18106
|
+
case "jsx-children": {
|
|
18107
|
+
const rendered = prop.value.children.map((c) => this.renderNode(c)).join("");
|
|
18108
|
+
parts.push(`${prop.name}={<>${rendered}</>}`);
|
|
18109
|
+
break;
|
|
18110
|
+
}
|
|
18111
|
+
case "spread":
|
|
18112
|
+
parts.push(`{...${prop.value.expr}}`);
|
|
18113
|
+
break;
|
|
18114
|
+
case "expression":
|
|
18115
|
+
parts.push(`${prop.name}={${prop.value.expr}}`);
|
|
18116
|
+
break;
|
|
18117
|
+
case "template":
|
|
18118
|
+
parts.push(`${prop.name}={${this.flattenTemplate(prop.value)}}`);
|
|
18119
|
+
break;
|
|
18120
|
+
case "boolean-shorthand":
|
|
18121
|
+
parts.push(prop.name);
|
|
18122
|
+
break;
|
|
18123
|
+
case "literal":
|
|
18124
|
+
parts.push(`${prop.name}="${prop.value.value}"`);
|
|
18125
|
+
break;
|
|
18126
|
+
case "boolean-attr":
|
|
18127
|
+
parts.push(prop.name);
|
|
18128
|
+
break;
|
|
18129
|
+
}
|
|
18130
|
+
}
|
|
18131
|
+
return parts.length > 0 ? " " + parts.join(" ") : "";
|
|
18132
|
+
}
|
|
18133
|
+
}
|
|
18134
|
+
var testAdapter = new TestAdapter;
|
|
17729
18135
|
// src/adapters/parsed-expr-emitter.ts
|
|
17730
18136
|
function emitParsedExpr(expr, emitter) {
|
|
17731
18137
|
const emit = (child) => emitParsedExpr(child, emitter);
|
|
@@ -17930,7 +18336,139 @@ function parseAndMerge(content, importsBySource, otherImports, codeSections) {
|
|
|
17930
18336
|
codeSections.push(code);
|
|
17931
18337
|
}
|
|
17932
18338
|
}
|
|
18339
|
+
// src/loop-destructure.ts
|
|
18340
|
+
var SIMPLE_FIELD = /^\.[A-Za-z_$][\w$]*$/;
|
|
18341
|
+
function isLowerableObjectRestDestructure(loop) {
|
|
18342
|
+
const bindings = loop.paramBindings;
|
|
18343
|
+
if (!bindings || bindings.length === 0)
|
|
18344
|
+
return false;
|
|
18345
|
+
if (loop.filterPredicate)
|
|
18346
|
+
return false;
|
|
18347
|
+
for (const name of [...bindings.map((b) => b.name), loop.index]) {
|
|
18348
|
+
if (name && name.startsWith("__bf_"))
|
|
18349
|
+
return false;
|
|
18350
|
+
}
|
|
18351
|
+
for (const b of bindings) {
|
|
18352
|
+
if (b.rest) {
|
|
18353
|
+
if (b.rest.kind !== "object")
|
|
18354
|
+
return false;
|
|
18355
|
+
} else if (!SIMPLE_FIELD.test(b.path)) {
|
|
18356
|
+
return false;
|
|
18357
|
+
}
|
|
18358
|
+
}
|
|
18359
|
+
const restNames = bindings.filter((b) => b.rest).map((b) => b.name);
|
|
18360
|
+
if (restNames.length === 0)
|
|
18361
|
+
return true;
|
|
18362
|
+
return !restNamesMisused(loop, restNames);
|
|
18363
|
+
}
|
|
18364
|
+
function restNamesMisused(loop, names) {
|
|
18365
|
+
const valueUse = names.map((n) => new RegExp(`(?<![\\w.$])${escapeRe(n)}(?!\\s*\\??\\.)(?![\\w$])`));
|
|
18366
|
+
let misused = false;
|
|
18367
|
+
const check = (s) => {
|
|
18368
|
+
if (!s || misused)
|
|
18369
|
+
return;
|
|
18370
|
+
for (const re of valueUse) {
|
|
18371
|
+
if (re.test(s)) {
|
|
18372
|
+
misused = true;
|
|
18373
|
+
return;
|
|
18374
|
+
}
|
|
18375
|
+
}
|
|
18376
|
+
};
|
|
18377
|
+
const attr = (v) => {
|
|
18378
|
+
if (v.kind === "expression" || v.kind === "spread") {
|
|
18379
|
+
check(v.expr);
|
|
18380
|
+
check(v.templateExpr);
|
|
18381
|
+
} else if (v.kind === "template") {
|
|
18382
|
+
v.parts.forEach(part);
|
|
18383
|
+
}
|
|
18384
|
+
};
|
|
18385
|
+
const part = (p) => {
|
|
18386
|
+
if (p.type === "ternary") {
|
|
18387
|
+
check(p.condition);
|
|
18388
|
+
check(p.templateCondition);
|
|
18389
|
+
check(p.whenTrue);
|
|
18390
|
+
check(p.whenFalse);
|
|
18391
|
+
} else if (p.type === "lookup") {
|
|
18392
|
+
check(p.key);
|
|
18393
|
+
check(p.templateKey);
|
|
18394
|
+
}
|
|
18395
|
+
};
|
|
18396
|
+
const visitLoop = (l) => {
|
|
18397
|
+
if (misused)
|
|
18398
|
+
return;
|
|
18399
|
+
check(l.array);
|
|
18400
|
+
check(l.templateArray);
|
|
18401
|
+
check(l.key);
|
|
18402
|
+
check(l.mapPreamble);
|
|
18403
|
+
check(l.templateMapPreamble);
|
|
18404
|
+
if (l.flatMapCallback) {
|
|
18405
|
+
check(l.flatMapCallback.body);
|
|
18406
|
+
check(l.flatMapCallback.templateBody);
|
|
18407
|
+
l.flatMapCallback.fragments.forEach((f) => visit3(f.ir));
|
|
18408
|
+
}
|
|
18409
|
+
l.children.forEach(visit3);
|
|
18410
|
+
};
|
|
18411
|
+
const visit3 = (node) => {
|
|
18412
|
+
if (misused)
|
|
18413
|
+
return;
|
|
18414
|
+
switch (node.type) {
|
|
18415
|
+
case "expression":
|
|
18416
|
+
check(node.expr);
|
|
18417
|
+
check(node.templateExpr);
|
|
18418
|
+
break;
|
|
18419
|
+
case "conditional":
|
|
18420
|
+
check(node.condition);
|
|
18421
|
+
check(node.templateCondition);
|
|
18422
|
+
visit3(node.whenTrue);
|
|
18423
|
+
visit3(node.whenFalse);
|
|
18424
|
+
break;
|
|
18425
|
+
case "if-statement":
|
|
18426
|
+
check(node.condition);
|
|
18427
|
+
check(node.templateCondition);
|
|
18428
|
+
for (const sv of node.scopeVariables) {
|
|
18429
|
+
check(sv.initializer);
|
|
18430
|
+
check(sv.templateInitializer);
|
|
18431
|
+
}
|
|
18432
|
+
visit3(node.consequent);
|
|
18433
|
+
if (node.alternate)
|
|
18434
|
+
visit3(node.alternate);
|
|
18435
|
+
break;
|
|
18436
|
+
case "element":
|
|
18437
|
+
node.attrs.forEach((a) => attr(a.value));
|
|
18438
|
+
node.events.forEach((e) => check(e.handler));
|
|
18439
|
+
node.children.forEach(visit3);
|
|
18440
|
+
break;
|
|
18441
|
+
case "component":
|
|
18442
|
+
node.props.forEach((p) => attr(p.value));
|
|
18443
|
+
node.children.forEach(visit3);
|
|
18444
|
+
break;
|
|
18445
|
+
case "provider":
|
|
18446
|
+
attr(node.valueProp.value);
|
|
18447
|
+
node.children.forEach(visit3);
|
|
18448
|
+
break;
|
|
18449
|
+
case "fragment":
|
|
18450
|
+
node.children.forEach(visit3);
|
|
18451
|
+
break;
|
|
18452
|
+
case "async":
|
|
18453
|
+
visit3(node.fallback);
|
|
18454
|
+
node.children.forEach(visit3);
|
|
18455
|
+
break;
|
|
18456
|
+
case "loop":
|
|
18457
|
+
visitLoop(node);
|
|
18458
|
+
break;
|
|
18459
|
+
case "text":
|
|
18460
|
+
case "slot":
|
|
18461
|
+
break;
|
|
18462
|
+
}
|
|
18463
|
+
};
|
|
18464
|
+
visitLoop(loop);
|
|
18465
|
+
return misused;
|
|
18466
|
+
}
|
|
18467
|
+
function escapeRe(s) {
|
|
18468
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
18469
|
+
}
|
|
17933
18470
|
// src/debug.ts
|
|
18471
|
+
import ts19 from "typescript";
|
|
17934
18472
|
function buildComponentGraph(source, filePath, componentName) {
|
|
17935
18473
|
const ctx = analyzeComponent(source, filePath, componentName);
|
|
17936
18474
|
if (!ctx.jsxReturn) {
|
|
@@ -17961,15 +18499,55 @@ function buildComponentGraph(source, filePath, componentName) {
|
|
|
17961
18499
|
root: ir,
|
|
17962
18500
|
errors: []
|
|
17963
18501
|
};
|
|
17964
|
-
|
|
18502
|
+
const graph = buildGraphFromIR(componentIR);
|
|
18503
|
+
return graph.sourceFile ? graph : { ...graph, sourceFile: filePath };
|
|
17965
18504
|
}
|
|
17966
18505
|
function buildGraphFromIR(ir) {
|
|
17967
18506
|
const meta = ir.metadata;
|
|
17968
18507
|
const signalGetters = new Set(meta.signals.map((s) => s.getter));
|
|
17969
18508
|
const memoNames = new Set(meta.memos.map((m) => m.name));
|
|
17970
18509
|
const signalSetters = new Map(meta.signals.filter((s) => s.setter).map((s) => [s.setter, s.getter]));
|
|
18510
|
+
const destructuredPropNames = new Set(meta.propsParams.map((p) => p.name).filter((n) => n !== "children"));
|
|
18511
|
+
const propsObjectName = meta.propsObjectName;
|
|
18512
|
+
const constByName = new Map(meta.localConstants.map((c) => [c.name, c]));
|
|
18513
|
+
const propDerivedConsts = new Set;
|
|
18514
|
+
{
|
|
18515
|
+
const onStack = new Set;
|
|
18516
|
+
const derivesFromProp = (name) => {
|
|
18517
|
+
if (propDerivedConsts.has(name))
|
|
18518
|
+
return true;
|
|
18519
|
+
const c = constByName.get(name);
|
|
18520
|
+
if (!c?.freeIdentifiers || onStack.has(name))
|
|
18521
|
+
return false;
|
|
18522
|
+
onStack.add(name);
|
|
18523
|
+
let derived = false;
|
|
18524
|
+
for (const free of c.freeIdentifiers) {
|
|
18525
|
+
if (destructuredPropNames.has(free) || free === propsObjectName || derivesFromProp(free)) {
|
|
18526
|
+
derived = true;
|
|
18527
|
+
break;
|
|
18528
|
+
}
|
|
18529
|
+
}
|
|
18530
|
+
onStack.delete(name);
|
|
18531
|
+
if (derived)
|
|
18532
|
+
propDerivedConsts.add(name);
|
|
18533
|
+
return derived;
|
|
18534
|
+
};
|
|
18535
|
+
for (const c of meta.localConstants)
|
|
18536
|
+
derivesFromProp(c.name);
|
|
18537
|
+
}
|
|
18538
|
+
const exprReadsProp = (expr, freeIds) => {
|
|
18539
|
+
for (const name of destructuredPropNames) {
|
|
18540
|
+
if (freeIds ? freeIds.has(name) : tokenContainsIdent(expr, name))
|
|
18541
|
+
return true;
|
|
18542
|
+
}
|
|
18543
|
+
for (const name of propDerivedConsts) {
|
|
18544
|
+
if (freeIds ? freeIds.has(name) : tokenContainsIdent(expr, name))
|
|
18545
|
+
return true;
|
|
18546
|
+
}
|
|
18547
|
+
return propsObjectName ? exprReadsPropMember(expr, propsObjectName) : false;
|
|
18548
|
+
};
|
|
17971
18549
|
const domBindings = [];
|
|
17972
|
-
collectDomBindings(ir.root, domBindings, signalGetters, memoNames);
|
|
18550
|
+
collectDomBindings(ir.root, domBindings, signalGetters, memoNames, undefined, new Set, exprReadsProp);
|
|
17973
18551
|
const signalConsumers = new Map;
|
|
17974
18552
|
for (const s of meta.signals)
|
|
17975
18553
|
signalConsumers.set(s.getter, []);
|
|
@@ -18040,8 +18618,8 @@ function buildGraphFromIR(ir) {
|
|
|
18040
18618
|
domBindings
|
|
18041
18619
|
};
|
|
18042
18620
|
}
|
|
18043
|
-
function buildComponentAnalysis(source, filePath, componentName) {
|
|
18044
|
-
const ctx = analyzeComponent(source, filePath, componentName);
|
|
18621
|
+
function buildComponentAnalysis(source, filePath, componentName, program) {
|
|
18622
|
+
const ctx = analyzeComponent(source, filePath, componentName, program);
|
|
18045
18623
|
const emptyIR = {
|
|
18046
18624
|
version: "0.1",
|
|
18047
18625
|
metadata: buildMetadata(ctx),
|
|
@@ -18058,8 +18636,8 @@ function buildComponentAnalysis(source, filePath, componentName) {
|
|
|
18058
18636
|
const ir = { version: "0.1", metadata: buildMetadata(ctx), root, errors: [] };
|
|
18059
18637
|
return { graph: buildGraphFromIR(ir), ir };
|
|
18060
18638
|
}
|
|
18061
|
-
function buildEventSummary(source, filePath, componentName) {
|
|
18062
|
-
const { graph, ir } = buildComponentAnalysis(source, filePath, componentName);
|
|
18639
|
+
function buildEventSummary(source, filePath, componentName, program) {
|
|
18640
|
+
const { graph, ir } = buildComponentAnalysis(source, filePath, componentName, program);
|
|
18063
18641
|
const setterToSignal = new Map;
|
|
18064
18642
|
for (const s of ir.metadata.signals) {
|
|
18065
18643
|
if (s.setter)
|
|
@@ -18892,8 +19470,8 @@ function formatFallbackExplanations(componentName, fallbacks) {
|
|
|
18892
19470
|
return lines.join(`
|
|
18893
19471
|
`);
|
|
18894
19472
|
}
|
|
18895
|
-
function buildComponentSummary(source, filePath, componentName) {
|
|
18896
|
-
const { graph, ir } = buildComponentAnalysis(source, filePath, componentName);
|
|
19473
|
+
function buildComponentSummary(source, filePath, componentName, program) {
|
|
19474
|
+
const { graph, ir } = buildComponentAnalysis(source, filePath, componentName, program);
|
|
18897
19475
|
const meta = ir.metadata;
|
|
18898
19476
|
const clientNeeds = analyzeClientNeeds(ir);
|
|
18899
19477
|
const hasReactiveState = meta.signals.length > 0 || meta.memos.length > 0 || meta.effects.length > 0;
|
|
@@ -18995,18 +19573,23 @@ function inferWrapReasonForAttrLike(hasStringReactive, hasPropsRef, flags) {
|
|
|
18995
19573
|
const decision = decideWrapFromAstFlags(flags);
|
|
18996
19574
|
return decision.wrap ? decision.reason : undefined;
|
|
18997
19575
|
}
|
|
18998
|
-
function collectDomBindings(node, bindings, signalGetters, memoNames, parentTag) {
|
|
19576
|
+
function collectDomBindings(node, bindings, signalGetters, memoNames, parentTag, loopParams = new Set, readsProp = () => false) {
|
|
19577
|
+
const exprReadsLoopParam = (n) => loopParams.size > 0 && (n.origin?.freeRefs?.some((r) => loopParams.has(r.name)) ?? false);
|
|
19578
|
+
const attrReadsLoopParam = (free) => loopParams.size > 0 && free !== undefined && [...loopParams].some((p) => free.has(p));
|
|
18999
19579
|
switch (node.type) {
|
|
19000
19580
|
case "element": {
|
|
19001
19581
|
for (const attr of node.attrs) {
|
|
19002
19582
|
if (attr.value.kind !== "expression" && attr.value.kind !== "template" && attr.value.kind !== "spread")
|
|
19003
19583
|
continue;
|
|
19584
|
+
if (attr.name === "key" && loopParams.size > 0)
|
|
19585
|
+
continue;
|
|
19004
19586
|
const expr = attrValueToString2(attr.value);
|
|
19005
19587
|
if (!expr)
|
|
19006
19588
|
continue;
|
|
19007
19589
|
const deps = extractReactiveDeps(expr, signalGetters, memoNames);
|
|
19008
|
-
const isReactive = deps.length > 0;
|
|
19009
|
-
const
|
|
19590
|
+
const isReactive = deps.length > 0 || attrReadsLoopParam(attr.freeIdentifiers);
|
|
19591
|
+
const hasPropsRef = readsProp(expr, attr.freeIdentifiers);
|
|
19592
|
+
const wrapReason = inferWrapReasonForAttrLike(isReactive, hasPropsRef, attr);
|
|
19010
19593
|
if (wrapReason) {
|
|
19011
19594
|
bindings.push({
|
|
19012
19595
|
kind: "dom",
|
|
@@ -19014,7 +19597,7 @@ function collectDomBindings(node, bindings, signalGetters, memoNames, parentTag)
|
|
|
19014
19597
|
slotId: node.slotId ?? "?",
|
|
19015
19598
|
deps,
|
|
19016
19599
|
type: "attribute",
|
|
19017
|
-
classification: isReactive ? "reactive" : "fallback",
|
|
19600
|
+
classification: isReactive || hasPropsRef ? "reactive" : "fallback",
|
|
19018
19601
|
expression: expr,
|
|
19019
19602
|
wrapReason,
|
|
19020
19603
|
loc: attr.loc,
|
|
@@ -19035,13 +19618,14 @@ function collectDomBindings(node, bindings, signalGetters, memoNames, parentTag)
|
|
|
19035
19618
|
});
|
|
19036
19619
|
}
|
|
19037
19620
|
for (const child of node.children) {
|
|
19038
|
-
collectDomBindings(child, bindings, signalGetters, memoNames, node.tag);
|
|
19621
|
+
collectDomBindings(child, bindings, signalGetters, memoNames, node.tag, loopParams, readsProp);
|
|
19039
19622
|
}
|
|
19040
19623
|
break;
|
|
19041
19624
|
}
|
|
19042
19625
|
case "expression": {
|
|
19043
19626
|
const decision = decideWrapFromAstFlags(node);
|
|
19044
|
-
|
|
19627
|
+
const loopReactive = exprReadsLoopParam(node);
|
|
19628
|
+
if ((decision.wrap || loopReactive) && node.slotId) {
|
|
19045
19629
|
const deps = extractReactiveDeps(node.expr, signalGetters, memoNames);
|
|
19046
19630
|
const preview = parentTag ? `<${parentTag}>{${truncateExpr(node.expr)}}</${parentTag}>` : `{${truncateExpr(node.expr)}}`;
|
|
19047
19631
|
bindings.push({
|
|
@@ -19050,9 +19634,9 @@ function collectDomBindings(node, bindings, signalGetters, memoNames, parentTag)
|
|
|
19050
19634
|
slotId: node.slotId,
|
|
19051
19635
|
deps,
|
|
19052
19636
|
type: "text",
|
|
19053
|
-
classification: decision.reason === "proven-reactive" ? "reactive" : "fallback",
|
|
19637
|
+
classification: decision.wrap && decision.reason === "proven-reactive" || loopReactive ? "reactive" : "fallback",
|
|
19054
19638
|
expression: node.expr,
|
|
19055
|
-
wrapReason: decision.reason,
|
|
19639
|
+
wrapReason: decision.wrap ? decision.reason : "string-reactive",
|
|
19056
19640
|
loc: node.loc,
|
|
19057
19641
|
jsxPreview: preview
|
|
19058
19642
|
});
|
|
@@ -19061,7 +19645,8 @@ function collectDomBindings(node, bindings, signalGetters, memoNames, parentTag)
|
|
|
19061
19645
|
}
|
|
19062
19646
|
case "conditional": {
|
|
19063
19647
|
const decision = decideWrapFromAstFlags(node);
|
|
19064
|
-
|
|
19648
|
+
const loopReactive = loopParams.size > 0 && (node.origin?.freeRefs?.some((r) => loopParams.has(r.name)) ?? false);
|
|
19649
|
+
if ((decision.wrap || loopReactive) && node.slotId) {
|
|
19065
19650
|
const deps = extractReactiveDeps(node.condition, signalGetters, memoNames);
|
|
19066
19651
|
bindings.push({
|
|
19067
19652
|
kind: "dom",
|
|
@@ -19069,24 +19654,25 @@ function collectDomBindings(node, bindings, signalGetters, memoNames, parentTag)
|
|
|
19069
19654
|
slotId: node.slotId,
|
|
19070
19655
|
deps,
|
|
19071
19656
|
type: "conditional",
|
|
19072
|
-
classification: decision.reason === "proven-reactive" ? "reactive" : "fallback",
|
|
19657
|
+
classification: decision.wrap && decision.reason === "proven-reactive" || loopReactive ? "reactive" : "fallback",
|
|
19073
19658
|
expression: node.condition,
|
|
19074
|
-
wrapReason: decision.reason,
|
|
19659
|
+
wrapReason: decision.wrap ? decision.reason : "string-reactive",
|
|
19075
19660
|
loc: node.loc,
|
|
19076
19661
|
jsxPreview: `{${truncateExpr(node.condition)} ? ... : ...}`
|
|
19077
19662
|
});
|
|
19078
19663
|
}
|
|
19079
|
-
collectDomBindings(node.whenTrue, bindings, signalGetters, memoNames, parentTag);
|
|
19080
|
-
collectDomBindings(node.whenFalse, bindings, signalGetters, memoNames, parentTag);
|
|
19664
|
+
collectDomBindings(node.whenTrue, bindings, signalGetters, memoNames, parentTag, loopParams, readsProp);
|
|
19665
|
+
collectDomBindings(node.whenFalse, bindings, signalGetters, memoNames, parentTag, loopParams, readsProp);
|
|
19081
19666
|
break;
|
|
19082
19667
|
}
|
|
19083
19668
|
case "loop": {
|
|
19084
19669
|
if (node.slotId) {
|
|
19085
19670
|
const deps = extractReactiveDeps(node.array, signalGetters, memoNames);
|
|
19086
|
-
const
|
|
19671
|
+
const loopReactive = loopParams.size > 0 && node.arrayFreeIdentifiers !== undefined && [...loopParams].some((p) => node.arrayFreeIdentifiers.has(p));
|
|
19672
|
+
const isReactive = deps.length > 0 || node.callsReactiveGetters === true || loopReactive;
|
|
19087
19673
|
const isFallback = !isReactive && node.hasFunctionCalls === true;
|
|
19088
19674
|
if (isReactive || isFallback) {
|
|
19089
|
-
const wrapReason = deps.length > 0 ? "string-reactive" : node.callsReactiveGetters ? "proven-reactive" : "fallback-function-calls";
|
|
19675
|
+
const wrapReason = deps.length > 0 || loopReactive ? "string-reactive" : node.callsReactiveGetters ? "proven-reactive" : "fallback-function-calls";
|
|
19090
19676
|
bindings.push({
|
|
19091
19677
|
kind: "dom",
|
|
19092
19678
|
label: `loop "${node.slotId}"`,
|
|
@@ -19101,8 +19687,13 @@ function collectDomBindings(node, bindings, signalGetters, memoNames, parentTag)
|
|
|
19101
19687
|
});
|
|
19102
19688
|
}
|
|
19103
19689
|
}
|
|
19690
|
+
const childLoopParams = new Set(loopParams);
|
|
19691
|
+
for (const p of extractLoopParamNames(node.param, node))
|
|
19692
|
+
childLoopParams.add(p);
|
|
19693
|
+
if (node.index)
|
|
19694
|
+
childLoopParams.add(node.index);
|
|
19104
19695
|
for (const child of node.children) {
|
|
19105
|
-
collectDomBindings(child, bindings, signalGetters, memoNames, parentTag);
|
|
19696
|
+
collectDomBindings(child, bindings, signalGetters, memoNames, parentTag, childLoopParams, readsProp);
|
|
19106
19697
|
}
|
|
19107
19698
|
break;
|
|
19108
19699
|
}
|
|
@@ -19116,7 +19707,7 @@ function collectDomBindings(node, bindings, signalGetters, memoNames, parentTag)
|
|
|
19116
19707
|
if (!propValue)
|
|
19117
19708
|
continue;
|
|
19118
19709
|
const deps = extractReactiveDeps(propValue, signalGetters, memoNames);
|
|
19119
|
-
const hasPropsRef = propValue.
|
|
19710
|
+
const hasPropsRef = readsProp(propValue, prop.freeIdentifiers);
|
|
19120
19711
|
const isReactive = deps.length > 0 || hasPropsRef;
|
|
19121
19712
|
const wrapReason = inferWrapReasonForAttrLike(deps.length > 0, hasPropsRef, prop);
|
|
19122
19713
|
if (wrapReason) {
|
|
@@ -19135,21 +19726,21 @@ function collectDomBindings(node, bindings, signalGetters, memoNames, parentTag)
|
|
|
19135
19726
|
}
|
|
19136
19727
|
}
|
|
19137
19728
|
for (const child of node.children) {
|
|
19138
|
-
collectDomBindings(child, bindings, signalGetters, memoNames, parentTag);
|
|
19729
|
+
collectDomBindings(child, bindings, signalGetters, memoNames, parentTag, loopParams, readsProp);
|
|
19139
19730
|
}
|
|
19140
19731
|
break;
|
|
19141
19732
|
}
|
|
19142
19733
|
case "fragment":
|
|
19143
19734
|
case "provider": {
|
|
19144
19735
|
for (const child of node.children) {
|
|
19145
|
-
collectDomBindings(child, bindings, signalGetters, memoNames, parentTag);
|
|
19736
|
+
collectDomBindings(child, bindings, signalGetters, memoNames, parentTag, loopParams, readsProp);
|
|
19146
19737
|
}
|
|
19147
19738
|
break;
|
|
19148
19739
|
}
|
|
19149
19740
|
case "if-statement": {
|
|
19150
|
-
collectDomBindings(node.consequent, bindings, signalGetters, memoNames, parentTag);
|
|
19741
|
+
collectDomBindings(node.consequent, bindings, signalGetters, memoNames, parentTag, loopParams, readsProp);
|
|
19151
19742
|
if (node.alternate) {
|
|
19152
|
-
collectDomBindings(node.alternate, bindings, signalGetters, memoNames, parentTag);
|
|
19743
|
+
collectDomBindings(node.alternate, bindings, signalGetters, memoNames, parentTag, loopParams, readsProp);
|
|
19153
19744
|
}
|
|
19154
19745
|
break;
|
|
19155
19746
|
}
|
|
@@ -19159,6 +19750,26 @@ function truncateExpr(expr, max = 40) {
|
|
|
19159
19750
|
const s = expr.replace(/\s+/g, " ").trim();
|
|
19160
19751
|
return s.length > max ? s.slice(0, max - 1) + "…" : s;
|
|
19161
19752
|
}
|
|
19753
|
+
function exprReadsPropMember(expr, propsObjectName) {
|
|
19754
|
+
let sf;
|
|
19755
|
+
try {
|
|
19756
|
+
sf = ts19.createSourceFile("__attr.tsx", `(${expr})`, ts19.ScriptTarget.Latest, true, ts19.ScriptKind.TSX);
|
|
19757
|
+
} catch {
|
|
19758
|
+
return false;
|
|
19759
|
+
}
|
|
19760
|
+
let found = false;
|
|
19761
|
+
const visit3 = (n) => {
|
|
19762
|
+
if (found)
|
|
19763
|
+
return;
|
|
19764
|
+
if (ts19.isPropertyAccessExpression(n) && ts19.isIdentifier(n.expression) && n.expression.text === propsObjectName && n.name.text !== "children") {
|
|
19765
|
+
found = true;
|
|
19766
|
+
return;
|
|
19767
|
+
}
|
|
19768
|
+
ts19.forEachChild(n, visit3);
|
|
19769
|
+
};
|
|
19770
|
+
visit3(sf);
|
|
19771
|
+
return found;
|
|
19772
|
+
}
|
|
19162
19773
|
function attrValueToString2(value) {
|
|
19163
19774
|
switch (value.kind) {
|
|
19164
19775
|
case "literal":
|
|
@@ -19223,8 +19834,1068 @@ function findSourceFile2(meta) {
|
|
|
19223
19834
|
}
|
|
19224
19835
|
return null;
|
|
19225
19836
|
}
|
|
19837
|
+
// src/profiler.ts
|
|
19838
|
+
import ts20 from "typescript";
|
|
19839
|
+
var DEFAULT_FANOUT_THRESHOLD = 8;
|
|
19840
|
+
function buildStaticBudget(source, filePath, componentName, options = {}) {
|
|
19841
|
+
const threshold = options.fanOutThreshold ?? DEFAULT_FANOUT_THRESHOLD;
|
|
19842
|
+
const program = createProgramForFile(source, filePath)?.program;
|
|
19843
|
+
const { graph } = buildComponentAnalysis(source, filePath, componentName, program);
|
|
19844
|
+
const summary = buildComponentSummary(source, filePath, componentName, program);
|
|
19845
|
+
const subscriptions = graph.signals.reduce((n, s) => n + s.consumers.filter((c) => !isEventHandlerConsumer(c)).length, 0);
|
|
19846
|
+
const fanOut = graph.signals.map((s) => {
|
|
19847
|
+
const subscribers = transitiveSubscriberCount(graph, s.name);
|
|
19848
|
+
return { signal: s.name, subscribers, hot: subscribers >= threshold, loc: s.loc };
|
|
19849
|
+
}).sort((a, b) => b.subscribers - a.subscribers);
|
|
19850
|
+
const { depth, chain } = longestMemoChain(graph);
|
|
19851
|
+
const hasReactiveState = graph.signals.length > 0 || graph.memos.length > 0;
|
|
19852
|
+
const observedInComponent = graph.signals.some((s) => transitiveSubscriberCount(graph, s.name) > 0) || graph.memos.some((m) => transitiveSubscriberCount(graph, m.name) > 0);
|
|
19853
|
+
const crossComponentOnly = hasReactiveState && !observedInComponent;
|
|
19854
|
+
return {
|
|
19855
|
+
componentName: summary.componentName,
|
|
19856
|
+
sourceFile: summary.sourceFile,
|
|
19857
|
+
kind: "static-budget",
|
|
19858
|
+
signals: graph.signals.length,
|
|
19859
|
+
memos: graph.memos.length,
|
|
19860
|
+
effects: graph.effects.length,
|
|
19861
|
+
loops: summary.loops,
|
|
19862
|
+
subscriptions,
|
|
19863
|
+
memoChainDepth: depth,
|
|
19864
|
+
memoChainLongest: chain,
|
|
19865
|
+
fanOut,
|
|
19866
|
+
crossComponentOnly
|
|
19867
|
+
};
|
|
19868
|
+
}
|
|
19869
|
+
function isEventHandlerEntry(kind, name) {
|
|
19870
|
+
return kind === "dom" && /^\w+ handler "/.test(name);
|
|
19871
|
+
}
|
|
19872
|
+
function isEventHandlerConsumer(consumer) {
|
|
19873
|
+
const i = consumer.indexOf(":");
|
|
19874
|
+
return i > 0 && isEventHandlerEntry(consumer.slice(0, i), consumer.slice(i + 1));
|
|
19875
|
+
}
|
|
19876
|
+
function transitiveSubscriberCount(graph, name) {
|
|
19877
|
+
const path = traceUpdatePath(graph, name);
|
|
19878
|
+
if (!path)
|
|
19879
|
+
return 0;
|
|
19880
|
+
const seen = new Set;
|
|
19881
|
+
const walk = (entries) => {
|
|
19882
|
+
for (const e of entries) {
|
|
19883
|
+
if (!isEventHandlerEntry(e.kind, e.name))
|
|
19884
|
+
seen.add(`${e.kind}:${e.name}`);
|
|
19885
|
+
walk(e.children);
|
|
19886
|
+
}
|
|
19887
|
+
};
|
|
19888
|
+
walk(path.dependents);
|
|
19889
|
+
return seen.size;
|
|
19890
|
+
}
|
|
19891
|
+
function longestMemoChain(graph) {
|
|
19892
|
+
const memoChainFrom = (entry) => {
|
|
19893
|
+
if (entry.kind !== "memo")
|
|
19894
|
+
return [];
|
|
19895
|
+
let best2 = [];
|
|
19896
|
+
for (const child of entry.children) {
|
|
19897
|
+
const c = memoChainFrom(child);
|
|
19898
|
+
if (c.length > best2.length)
|
|
19899
|
+
best2 = c;
|
|
19900
|
+
}
|
|
19901
|
+
return [entry.name, ...best2];
|
|
19902
|
+
};
|
|
19903
|
+
let best = [];
|
|
19904
|
+
for (const memo of graph.memos) {
|
|
19905
|
+
const path = traceUpdatePath(graph, memo.name);
|
|
19906
|
+
let downstream = [];
|
|
19907
|
+
for (const dep of path?.dependents ?? []) {
|
|
19908
|
+
const c = memoChainFrom(dep);
|
|
19909
|
+
if (c.length > downstream.length)
|
|
19910
|
+
downstream = c;
|
|
19911
|
+
}
|
|
19912
|
+
const chain = [memo.name, ...downstream];
|
|
19913
|
+
if (chain.length > best.length)
|
|
19914
|
+
best = chain;
|
|
19915
|
+
}
|
|
19916
|
+
return { depth: best.length, chain: best };
|
|
19917
|
+
}
|
|
19918
|
+
function formatStaticBudget(b) {
|
|
19919
|
+
const lines = [];
|
|
19920
|
+
lines.push(`${b.componentName} — static reactivity budget`);
|
|
19921
|
+
lines.push(` signals: ${b.signals} memos: ${b.memos} effects: ${b.effects} loops: ${b.loops}`);
|
|
19922
|
+
lines.push(` subscriptions: ${b.subscriptions}`);
|
|
19923
|
+
if (b.memoChainDepth > 0) {
|
|
19924
|
+
lines.push(` memo-chain depth: ${b.memoChainDepth} (${b.memoChainLongest.join(" → ")})`);
|
|
19925
|
+
}
|
|
19926
|
+
const shown = b.fanOut.filter((f) => f.subscribers > 0).slice(0, 5);
|
|
19927
|
+
if (shown.length > 0) {
|
|
19928
|
+
lines.push(" fan-out (top):");
|
|
19929
|
+
for (const f of shown) {
|
|
19930
|
+
lines.push(` ${f.signal.padEnd(12)} → ${f.subscribers} subscribers${f.hot ? " ⚠ high" : ""}`);
|
|
19931
|
+
}
|
|
19932
|
+
}
|
|
19933
|
+
if (b.crossComponentOnly) {
|
|
19934
|
+
lines.push(` ⓘ compound: ${b.signals} signal(s) / ${b.memos} memo(s) but 0 in-component subscriptions —`);
|
|
19935
|
+
lines.push(" consumers are likely in composed child components (or it is read only from handlers);");
|
|
19936
|
+
lines.push(" run with --scenario to measure across the composition boundary.");
|
|
19937
|
+
}
|
|
19938
|
+
lines.push(" note: run with --scenario to measure actual cost; static budget is predictive only.");
|
|
19939
|
+
return lines.join(`
|
|
19940
|
+
`);
|
|
19941
|
+
}
|
|
19942
|
+
function diffStaticBudget(base, head) {
|
|
19943
|
+
const baseFan = new Map(base.fanOut.map((f) => [f.signal, f.subscribers]));
|
|
19944
|
+
const headFan = new Map(head.fanOut.map((f) => [f.signal, f.subscribers]));
|
|
19945
|
+
const signals = new Set([...baseFan.keys(), ...headFan.keys()]);
|
|
19946
|
+
const fanOut = [];
|
|
19947
|
+
for (const sig of signals) {
|
|
19948
|
+
const before = baseFan.get(sig) ?? 0;
|
|
19949
|
+
const after = headFan.get(sig) ?? 0;
|
|
19950
|
+
if (before !== after)
|
|
19951
|
+
fanOut.push({ signal: sig, before, after });
|
|
19952
|
+
}
|
|
19953
|
+
fanOut.sort((a, b) => b.after - b.before - (a.after - a.before));
|
|
19954
|
+
const d = {
|
|
19955
|
+
kind: "diff",
|
|
19956
|
+
componentName: head.componentName,
|
|
19957
|
+
signals: head.signals - base.signals,
|
|
19958
|
+
memos: head.memos - base.memos,
|
|
19959
|
+
effects: head.effects - base.effects,
|
|
19960
|
+
loops: head.loops - base.loops,
|
|
19961
|
+
subscriptions: head.subscriptions - base.subscriptions,
|
|
19962
|
+
memoChainDepth: head.memoChainDepth - base.memoChainDepth,
|
|
19963
|
+
fanOut
|
|
19964
|
+
};
|
|
19965
|
+
const regressed = d.signals > 0 || d.memos > 0 || d.effects > 0 || d.subscriptions > 0 || d.memoChainDepth > 0 || fanOut.some((f) => f.after > f.before);
|
|
19966
|
+
return { ...d, regressed };
|
|
19967
|
+
}
|
|
19968
|
+
function formatBudgetDiff(d) {
|
|
19969
|
+
const lines = [];
|
|
19970
|
+
lines.push(`${d.componentName} — reactivity diff`);
|
|
19971
|
+
const metric = (label, v) => {
|
|
19972
|
+
if (v !== 0)
|
|
19973
|
+
lines.push(` ${v > 0 ? "+" : ""}${v} ${label}`);
|
|
19974
|
+
};
|
|
19975
|
+
metric("signals", d.signals);
|
|
19976
|
+
metric("memos", d.memos);
|
|
19977
|
+
metric("effects", d.effects);
|
|
19978
|
+
metric("loops", d.loops);
|
|
19979
|
+
metric("subscriptions", d.subscriptions);
|
|
19980
|
+
if (d.memoChainDepth !== 0) {
|
|
19981
|
+
lines.push(` memo chain ${d.memoChainDepth > 0 ? "deepened" : "shortened"} by ${Math.abs(d.memoChainDepth)}`);
|
|
19982
|
+
}
|
|
19983
|
+
for (const f of d.fanOut) {
|
|
19984
|
+
lines.push(` signal \`${f.signal}\` fan-out ${f.before}→${f.after}`);
|
|
19985
|
+
}
|
|
19986
|
+
if (lines.length === 1)
|
|
19987
|
+
lines.push(" no structural reactivity change");
|
|
19988
|
+
else
|
|
19989
|
+
lines.push(d.regressed ? " ⚠ reactivity regressed" : " ✓ no regression");
|
|
19990
|
+
return lines.join(`
|
|
19991
|
+
`);
|
|
19992
|
+
}
|
|
19993
|
+
function parseProfilerId(id) {
|
|
19994
|
+
const hash = id.indexOf("#");
|
|
19995
|
+
if (hash < 0)
|
|
19996
|
+
return null;
|
|
19997
|
+
const colon = id.indexOf(":", hash);
|
|
19998
|
+
if (colon < 0)
|
|
19999
|
+
return null;
|
|
20000
|
+
const component = id.slice(0, hash);
|
|
20001
|
+
const kind = id.slice(hash + 1, colon);
|
|
20002
|
+
const rest = id.slice(colon + 1);
|
|
20003
|
+
if (!component || !kind || !rest)
|
|
20004
|
+
return null;
|
|
20005
|
+
return { component, kind, rest };
|
|
20006
|
+
}
|
|
20007
|
+
function buildIdIndex(graph) {
|
|
20008
|
+
const comp = graph.componentName;
|
|
20009
|
+
const index = new Map;
|
|
20010
|
+
for (const s of graph.signals) {
|
|
20011
|
+
index.set(`${comp}#signal:${s.name}`, { kind: "signal", name: s.name, loc: s.loc });
|
|
20012
|
+
if (s.setter) {
|
|
20013
|
+
index.set(`${comp}#effect:controlled:${s.setter}`, {
|
|
20014
|
+
kind: "effect",
|
|
20015
|
+
name: `controlled:${s.setter}`,
|
|
20016
|
+
loc: s.loc
|
|
20017
|
+
});
|
|
20018
|
+
}
|
|
20019
|
+
}
|
|
20020
|
+
for (const m of graph.memos) {
|
|
20021
|
+
index.set(`${comp}#memo:${m.name}`, { kind: "memo", name: m.name, loc: m.loc });
|
|
20022
|
+
}
|
|
20023
|
+
for (const e of graph.effects) {
|
|
20024
|
+
const node = { kind: "effect", name: e.label, loc: e.loc };
|
|
20025
|
+
index.set(`${comp}#effect:${e.loc.line}`, node);
|
|
20026
|
+
index.set(`${comp}#effect:${e.label}`, node);
|
|
20027
|
+
}
|
|
20028
|
+
for (const b of graph.domBindings) {
|
|
20029
|
+
if (!b.loc)
|
|
20030
|
+
continue;
|
|
20031
|
+
const loc = { file: b.loc.file, line: b.loc.start.line };
|
|
20032
|
+
if (b.type === "event") {
|
|
20033
|
+
const eventName = b.label.match(/^(\w+)\s+handler/)?.[1];
|
|
20034
|
+
if (eventName) {
|
|
20035
|
+
index.set(`${comp}#handler:${b.slotId}:${eventName}`, {
|
|
20036
|
+
kind: "handler",
|
|
20037
|
+
name: `${eventName}@${b.slotId}`,
|
|
20038
|
+
loc
|
|
20039
|
+
});
|
|
20040
|
+
}
|
|
20041
|
+
continue;
|
|
20042
|
+
}
|
|
20043
|
+
index.set(`${comp}#binding:${b.slotId}`, { kind: "effect", name: `${b.slotId} (${b.type})`, loc });
|
|
20044
|
+
}
|
|
20045
|
+
return index;
|
|
20046
|
+
}
|
|
20047
|
+
function isRuntimeBookkeepingId(id) {
|
|
20048
|
+
return /^[serm]\d+$/.test(id);
|
|
20049
|
+
}
|
|
20050
|
+
function isFallbackEffectId(id) {
|
|
20051
|
+
return /^e\d+$/.test(id);
|
|
20052
|
+
}
|
|
20053
|
+
function joinProfilerEvents(events, index) {
|
|
20054
|
+
const joined = [];
|
|
20055
|
+
const gaps = new Map;
|
|
20056
|
+
const resolve2 = (id) => {
|
|
20057
|
+
if (id === undefined)
|
|
20058
|
+
return;
|
|
20059
|
+
const node = index.get(id);
|
|
20060
|
+
if (!node)
|
|
20061
|
+
gaps.set(id, (gaps.get(id) ?? 0) + 1);
|
|
20062
|
+
return node;
|
|
20063
|
+
};
|
|
20064
|
+
for (const event of events) {
|
|
20065
|
+
joined.push({
|
|
20066
|
+
event,
|
|
20067
|
+
subscriber: resolve2(event.subscriber),
|
|
20068
|
+
signal: resolve2(event.signal)
|
|
20069
|
+
});
|
|
20070
|
+
}
|
|
20071
|
+
const unattributed = [];
|
|
20072
|
+
const diagnostics = [];
|
|
20073
|
+
for (const [id, count] of gaps.entries()) {
|
|
20074
|
+
(isRuntimeBookkeepingId(id) ? diagnostics : unattributed).push({ id, count });
|
|
20075
|
+
}
|
|
20076
|
+
const byCount = (a, b) => b.count - a.count;
|
|
20077
|
+
unattributed.sort(byCount);
|
|
20078
|
+
diagnostics.sort(byCount);
|
|
20079
|
+
return { joined, unattributed, diagnostics };
|
|
20080
|
+
}
|
|
20081
|
+
function findUninstrumentedEffects(source, filePath, instrumentedLines) {
|
|
20082
|
+
const sf = ts20.createSourceFile(filePath, source, ts20.ScriptTarget.Latest, true, ts20.ScriptKind.TSX);
|
|
20083
|
+
const out = [];
|
|
20084
|
+
const visit3 = (node) => {
|
|
20085
|
+
if (ts20.isCallExpression(node) && ts20.isIdentifier(node.expression) && node.expression.text === "createEffect") {
|
|
20086
|
+
const line = sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1;
|
|
20087
|
+
if (!instrumentedLines.has(line))
|
|
20088
|
+
out.push({ file: filePath, line });
|
|
20089
|
+
}
|
|
20090
|
+
ts20.forEachChild(node, visit3);
|
|
20091
|
+
};
|
|
20092
|
+
visit3(sf);
|
|
20093
|
+
out.sort((a, b) => a.line - b.line);
|
|
20094
|
+
return out;
|
|
20095
|
+
}
|
|
20096
|
+
var DEFAULT_HOT_RUNS_PER_TURN = 2;
|
|
20097
|
+
function analyzeHotSubscribers(events, index, options = {}) {
|
|
20098
|
+
const threshold = options.hotRunsPerTurn ?? DEFAULT_HOT_RUNS_PER_TURN;
|
|
20099
|
+
const byId = new Map;
|
|
20100
|
+
const acc = (id) => {
|
|
20101
|
+
let a = byId.get(id);
|
|
20102
|
+
if (!a) {
|
|
20103
|
+
a = { runs: 0, mountRuns: 0, totalMs: 0, turns: new Set };
|
|
20104
|
+
byId.set(id, a);
|
|
20105
|
+
}
|
|
20106
|
+
return a;
|
|
20107
|
+
};
|
|
20108
|
+
for (const e of events) {
|
|
20109
|
+
if (e.subscriber === undefined)
|
|
20110
|
+
continue;
|
|
20111
|
+
if (e.type === "effectEnter") {
|
|
20112
|
+
const a = acc(e.subscriber);
|
|
20113
|
+
a.runs++;
|
|
20114
|
+
if (e.turn === null)
|
|
20115
|
+
a.mountRuns++;
|
|
20116
|
+
else
|
|
20117
|
+
a.turns.add(String(e.turnSeq ?? e.turn));
|
|
20118
|
+
} else if (e.type === "effectExit" && e.dur !== undefined) {
|
|
20119
|
+
acc(e.subscriber).totalMs += e.dur;
|
|
20120
|
+
}
|
|
20121
|
+
}
|
|
20122
|
+
const { joined, unattributed } = joinProfilerEvents(events, index);
|
|
20123
|
+
const nodeFor = new Map;
|
|
20124
|
+
for (const j of joined) {
|
|
20125
|
+
if (j.event.subscriber !== undefined && j.subscriber)
|
|
20126
|
+
nodeFor.set(j.event.subscriber, j.subscriber);
|
|
20127
|
+
}
|
|
20128
|
+
let subscribers = [...byId.entries()].map(([subscriber, a]) => {
|
|
20129
|
+
const node = nodeFor.get(subscriber);
|
|
20130
|
+
const turns = a.turns.size;
|
|
20131
|
+
const interactionRuns = a.runs - a.mountRuns;
|
|
20132
|
+
const runsPerTurn = turns > 0 ? interactionRuns / turns : 0;
|
|
20133
|
+
const uninstrumented = !node && isFallbackEffectId(subscriber);
|
|
20134
|
+
return {
|
|
20135
|
+
subscriber,
|
|
20136
|
+
loc: node?.loc,
|
|
20137
|
+
name: node?.name,
|
|
20138
|
+
kind: node?.kind,
|
|
20139
|
+
runs: a.runs,
|
|
20140
|
+
mountRuns: a.mountRuns,
|
|
20141
|
+
totalMs: a.totalMs,
|
|
20142
|
+
turns,
|
|
20143
|
+
runsPerTurn,
|
|
20144
|
+
hot: runsPerTurn >= threshold,
|
|
20145
|
+
...uninstrumented ? {
|
|
20146
|
+
resolution: "uninstrumented",
|
|
20147
|
+
resolutionNote: "createEffect in non-JSX function scope (not attributed by compiler)",
|
|
20148
|
+
candidates: [...options.uninstrumentedCandidates ?? []]
|
|
20149
|
+
} : {}
|
|
20150
|
+
};
|
|
20151
|
+
});
|
|
20152
|
+
const roundMs = (m) => Math.round(m * 10) / 10;
|
|
20153
|
+
subscribers.sort((x, y) => roundMs(y.totalMs) - roundMs(x.totalMs) || y.runs - x.runs || (x.subscriber < y.subscriber ? -1 : x.subscriber > y.subscriber ? 1 : 0));
|
|
20154
|
+
if (options.minMs !== undefined)
|
|
20155
|
+
subscribers = subscribers.filter((s) => roundMs(s.totalMs) >= options.minMs);
|
|
20156
|
+
if (options.topN !== undefined)
|
|
20157
|
+
subscribers = subscribers.slice(0, options.topN);
|
|
20158
|
+
const subscriberIds = new Set(byId.keys());
|
|
20159
|
+
const gaps = unattributed.filter((u) => subscriberIds.has(u.id));
|
|
20160
|
+
return { kind: "hot-subscribers", subscribers, unattributed: gaps };
|
|
20161
|
+
}
|
|
20162
|
+
var BAR_EIGHTHS = ["", "▏", "▎", "▍", "▌", "▋", "▊", "▉"];
|
|
20163
|
+
function fitLabel(s, width) {
|
|
20164
|
+
return s.length > width ? `${s.slice(0, width - 1)}…` : s.padEnd(width);
|
|
20165
|
+
}
|
|
20166
|
+
function bar(value, max, width) {
|
|
20167
|
+
if (max <= 0 || value <= 0)
|
|
20168
|
+
return "".padEnd(width);
|
|
20169
|
+
const units = Math.min(value / max, 1) * width;
|
|
20170
|
+
let full = Math.floor(units);
|
|
20171
|
+
let rem = Math.round((units - full) * 8);
|
|
20172
|
+
if (rem === 8) {
|
|
20173
|
+
full++;
|
|
20174
|
+
rem = 0;
|
|
20175
|
+
}
|
|
20176
|
+
return ("█".repeat(full) + (rem > 0 ? BAR_EIGHTHS[rem] : "")).padEnd(width);
|
|
20177
|
+
}
|
|
20178
|
+
function formatEffectCandidates(candidates) {
|
|
20179
|
+
const byFile = new Map;
|
|
20180
|
+
for (const c of candidates) {
|
|
20181
|
+
const arr = byFile.get(c.file) ?? [];
|
|
20182
|
+
arr.push(c.line);
|
|
20183
|
+
byFile.set(c.file, arr);
|
|
20184
|
+
}
|
|
20185
|
+
const groups = [];
|
|
20186
|
+
for (const [file, ls] of byFile) {
|
|
20187
|
+
const sorted = [...new Set(ls)].sort((a, b) => a - b);
|
|
20188
|
+
groups.push(`${file}:${sorted[0]}${sorted.slice(1).map((l) => `, :${l}`).join("")}`);
|
|
20189
|
+
}
|
|
20190
|
+
return groups.join("; ");
|
|
20191
|
+
}
|
|
20192
|
+
function formatHotSubscribers(r, limit = 12) {
|
|
20193
|
+
const lines = [];
|
|
20194
|
+
lines.push("hot subscribers — most run / most time");
|
|
20195
|
+
if (r.subscribers.length === 0) {
|
|
20196
|
+
lines.push(" (no effect/memo runs recorded)");
|
|
20197
|
+
}
|
|
20198
|
+
const shown = r.subscribers.slice(0, limit);
|
|
20199
|
+
const maxMs = shown.reduce((m, s) => Math.max(m, s.totalMs), 0);
|
|
20200
|
+
for (const s of shown) {
|
|
20201
|
+
const where = s.loc ? `${s.loc.file}:${s.loc.line}` : s.resolution === "uninstrumented" ? "uninstrumented — createEffect in non-JSX scope" : "unresolved";
|
|
20202
|
+
const base = s.name ?? s.subscriber;
|
|
20203
|
+
const label = s.kind && !base.endsWith(")") ? `${base} (${s.kind})` : base;
|
|
20204
|
+
const note = s.hot ? ` ⚠ ${s.runsPerTurn.toFixed(1)}/turn` : "";
|
|
20205
|
+
lines.push(` ${fitLabel(label, 24)} ${bar(s.totalMs, maxMs, 14)} ${s.totalMs.toFixed(1)}ms ${String(s.runs).padStart(3)}× (${where})${note}`);
|
|
20206
|
+
if (s.candidates && s.candidates.length > 0) {
|
|
20207
|
+
lines.push(`${" ".repeat(26)}candidates: ${formatEffectCandidates(s.candidates)}`);
|
|
20208
|
+
}
|
|
20209
|
+
}
|
|
20210
|
+
if (r.subscribers.length > shown.length) {
|
|
20211
|
+
lines.push(` … and ${r.subscribers.length - shown.length} more`);
|
|
20212
|
+
}
|
|
20213
|
+
if (r.unattributed.length > 0) {
|
|
20214
|
+
lines.push(` ⚠ coverage: ${r.unattributed.length} unresolved subscriber id(s)`);
|
|
20215
|
+
}
|
|
20216
|
+
return lines.join(`
|
|
20217
|
+
`);
|
|
20218
|
+
}
|
|
20219
|
+
var DEFAULT_WASTED_RATIO = 0.5;
|
|
20220
|
+
function analyzeWastedReReruns(events, index, options = {}) {
|
|
20221
|
+
const threshold = options.wastedRatio ?? DEFAULT_WASTED_RATIO;
|
|
20222
|
+
const byId = new Map;
|
|
20223
|
+
for (const e of events) {
|
|
20224
|
+
if (e.type !== "effectOutput" || e.subscriber === undefined || e.changed === undefined)
|
|
20225
|
+
continue;
|
|
20226
|
+
let a = byId.get(e.subscriber);
|
|
20227
|
+
if (!a) {
|
|
20228
|
+
a = { total: 0, wasted: 0 };
|
|
20229
|
+
byId.set(e.subscriber, a);
|
|
20230
|
+
}
|
|
20231
|
+
a.total++;
|
|
20232
|
+
if (!e.changed)
|
|
20233
|
+
a.wasted++;
|
|
20234
|
+
}
|
|
20235
|
+
const { unattributed } = joinProfilerEvents(events, index);
|
|
20236
|
+
const nodeFor = new Map;
|
|
20237
|
+
for (const [id] of byId) {
|
|
20238
|
+
const node = index.get(id);
|
|
20239
|
+
if (node)
|
|
20240
|
+
nodeFor.set(id, node);
|
|
20241
|
+
}
|
|
20242
|
+
let subscribers = [...byId.entries()].map(([subscriber, a]) => {
|
|
20243
|
+
const node = nodeFor.get(subscriber);
|
|
20244
|
+
const wastedRatio = a.total > 0 ? a.wasted / a.total : 0;
|
|
20245
|
+
return {
|
|
20246
|
+
subscriber,
|
|
20247
|
+
loc: node?.loc,
|
|
20248
|
+
name: node?.name,
|
|
20249
|
+
kind: node?.kind,
|
|
20250
|
+
totalRuns: a.total,
|
|
20251
|
+
wastedRuns: a.wasted,
|
|
20252
|
+
wastedRatio,
|
|
20253
|
+
wasted: wastedRatio >= threshold
|
|
20254
|
+
};
|
|
20255
|
+
}).filter((s) => s.wastedRuns > 0);
|
|
20256
|
+
subscribers.sort((x, y) => y.wastedRuns - x.wastedRuns || y.wastedRatio - x.wastedRatio || (x.subscriber < y.subscriber ? -1 : x.subscriber > y.subscriber ? 1 : 0));
|
|
20257
|
+
if (options.topN !== undefined)
|
|
20258
|
+
subscribers = subscribers.slice(0, options.topN);
|
|
20259
|
+
const subscriberIds = new Set(byId.keys());
|
|
20260
|
+
const gaps = unattributed.filter((u) => subscriberIds.has(u.id));
|
|
20261
|
+
return { kind: "wasted-re-runs", subscribers, unattributed: gaps };
|
|
20262
|
+
}
|
|
20263
|
+
function wastedOutputNoun(kind) {
|
|
20264
|
+
return kind === "memo" ? "identical value" : "identical DOM";
|
|
20265
|
+
}
|
|
20266
|
+
function formatWastedReReruns(r, limit = 12) {
|
|
20267
|
+
const lines = [];
|
|
20268
|
+
lines.push("wasted re-runs — re-ran but produced identical output");
|
|
20269
|
+
if (r.subscribers.length === 0) {
|
|
20270
|
+
lines.push(" (no wasted re-runs recorded)");
|
|
20271
|
+
}
|
|
20272
|
+
const shown = r.subscribers.slice(0, limit);
|
|
20273
|
+
const maxWasted = shown.reduce((m, s) => Math.max(m, s.wastedRuns), 0);
|
|
20274
|
+
for (const s of shown) {
|
|
20275
|
+
const where = s.loc ? `${s.loc.file}:${s.loc.line}` : "(unresolved)";
|
|
20276
|
+
const base = s.name ?? s.subscriber;
|
|
20277
|
+
const label = s.kind && !base.endsWith(")") ? `${base} (${s.kind})` : base;
|
|
20278
|
+
const pct = Math.round(s.wastedRatio * 100);
|
|
20279
|
+
const note = s.wasted ? " ⚠ split so it doesn’t re-run on unrelated changes" : "";
|
|
20280
|
+
lines.push(` ${fitLabel(label, 24)} ${bar(s.wastedRuns, maxWasted, 14)} wasted: ${s.wastedRuns}/${s.totalRuns} produced ${wastedOutputNoun(s.kind)} (${pct}%) (${where})${note}`);
|
|
20281
|
+
}
|
|
20282
|
+
if (r.subscribers.length > shown.length) {
|
|
20283
|
+
lines.push(` … and ${r.subscribers.length - shown.length} more`);
|
|
20284
|
+
}
|
|
20285
|
+
if (r.unattributed.length > 0) {
|
|
20286
|
+
lines.push(` ⚠ coverage: ${r.unattributed.length} unresolved subscriber id(s)`);
|
|
20287
|
+
}
|
|
20288
|
+
return lines.join(`
|
|
20289
|
+
`);
|
|
20290
|
+
}
|
|
20291
|
+
function analyzeBatchAdvisor(events, index) {
|
|
20292
|
+
const byInvocation = new Map;
|
|
20293
|
+
const get = (e) => {
|
|
20294
|
+
if (e.turn === null)
|
|
20295
|
+
return;
|
|
20296
|
+
const key = String(e.turnSeq ?? e.turn);
|
|
20297
|
+
let acc = byInvocation.get(key);
|
|
20298
|
+
if (!acc) {
|
|
20299
|
+
acc = { handlerId: e.turn, totalRuns: 0, writes: 0, depth: 0, subscribers: new Set };
|
|
20300
|
+
byInvocation.set(key, acc);
|
|
20301
|
+
}
|
|
20302
|
+
return acc;
|
|
20303
|
+
};
|
|
20304
|
+
for (const e of events) {
|
|
20305
|
+
if (e.type === "signalSet") {
|
|
20306
|
+
const acc = get(e);
|
|
20307
|
+
if (acc && acc.depth === 0)
|
|
20308
|
+
acc.writes++;
|
|
20309
|
+
} else if (e.type === "effectEnter") {
|
|
20310
|
+
const acc = get(e);
|
|
20311
|
+
if (!acc)
|
|
20312
|
+
continue;
|
|
20313
|
+
acc.totalRuns++;
|
|
20314
|
+
acc.depth++;
|
|
20315
|
+
if (e.subscriber !== undefined)
|
|
20316
|
+
acc.subscribers.add(e.subscriber);
|
|
20317
|
+
} else if (e.type === "effectExit") {
|
|
20318
|
+
const acc = get(e);
|
|
20319
|
+
if (acc && acc.depth > 0)
|
|
20320
|
+
acc.depth--;
|
|
20321
|
+
}
|
|
20322
|
+
}
|
|
20323
|
+
const byHandler = new Map;
|
|
20324
|
+
for (const acc of byInvocation.values()) {
|
|
20325
|
+
if (acc.writes < 2)
|
|
20326
|
+
continue;
|
|
20327
|
+
const distinctSubscribers = acc.subscribers.size;
|
|
20328
|
+
const savings = acc.totalRuns - distinctSubscribers;
|
|
20329
|
+
if (savings <= 0)
|
|
20330
|
+
continue;
|
|
20331
|
+
const prev = byHandler.get(acc.handlerId);
|
|
20332
|
+
if (prev && prev.savings >= savings)
|
|
20333
|
+
continue;
|
|
20334
|
+
const node = index?.get(acc.handlerId);
|
|
20335
|
+
byHandler.set(acc.handlerId, {
|
|
20336
|
+
turn: acc.handlerId,
|
|
20337
|
+
loc: node?.loc,
|
|
20338
|
+
handler: node?.name,
|
|
20339
|
+
totalRuns: acc.totalRuns,
|
|
20340
|
+
distinctSubscribers,
|
|
20341
|
+
writes: acc.writes,
|
|
20342
|
+
savings,
|
|
20343
|
+
safety: "unverified"
|
|
20344
|
+
});
|
|
20345
|
+
}
|
|
20346
|
+
const candidates = [...byHandler.values()];
|
|
20347
|
+
candidates.sort((a, b) => b.savings - a.savings || b.totalRuns - a.totalRuns);
|
|
20348
|
+
return { kind: "batch-advisor", candidates };
|
|
20349
|
+
}
|
|
20350
|
+
function formatBatchAdvisor(r) {
|
|
20351
|
+
const lines = [];
|
|
20352
|
+
lines.push("batch advisor — unbatched multi-write turns");
|
|
20353
|
+
if (r.candidates.length === 0) {
|
|
20354
|
+
lines.push(" (no turn would benefit from batching)");
|
|
20355
|
+
}
|
|
20356
|
+
const maxSavings = r.candidates.reduce((m, c) => Math.max(m, c.savings), 0);
|
|
20357
|
+
for (const c of r.candidates) {
|
|
20358
|
+
const safe = c.safety === "safe" ? ", safe" : c.safety === "unsafe" ? ", UNSAFE" : ", safety unverified";
|
|
20359
|
+
const where = c.loc ? ` (${c.loc.file}:${c.loc.line})` : "";
|
|
20360
|
+
const label = fitLabel(c.handler ?? c.turn, 24);
|
|
20361
|
+
lines.push(` ${label} ${bar(c.savings, maxSavings, 14)} batch candidate ${c.totalRuns}→${c.distinctSubscribers} (saves ${c.savings}${safe})${where}`);
|
|
20362
|
+
}
|
|
20363
|
+
return lines.join(`
|
|
20364
|
+
`);
|
|
20365
|
+
}
|
|
20366
|
+
function downstreamMemos(graph, written) {
|
|
20367
|
+
const byName = new Map(graph.memos.map((m) => [m.name, m]));
|
|
20368
|
+
const result = new Set;
|
|
20369
|
+
const dependsOnWritten = (memoName, seen) => {
|
|
20370
|
+
if (seen.has(memoName))
|
|
20371
|
+
return false;
|
|
20372
|
+
seen.add(memoName);
|
|
20373
|
+
const m = byName.get(memoName);
|
|
20374
|
+
if (!m)
|
|
20375
|
+
return false;
|
|
20376
|
+
for (const dep of m.deps) {
|
|
20377
|
+
if (written.has(dep))
|
|
20378
|
+
return true;
|
|
20379
|
+
if (byName.has(dep) && dependsOnWritten(dep, seen))
|
|
20380
|
+
return true;
|
|
20381
|
+
}
|
|
20382
|
+
return false;
|
|
20383
|
+
};
|
|
20384
|
+
for (const m of graph.memos)
|
|
20385
|
+
if (dependsOnWritten(m.name, new Set))
|
|
20386
|
+
result.add(m.name);
|
|
20387
|
+
return result;
|
|
20388
|
+
}
|
|
20389
|
+
function assessBatchSafety(args) {
|
|
20390
|
+
if (args.hasIndirectSetters || args.setterNames.length === 0)
|
|
20391
|
+
return "unverified";
|
|
20392
|
+
const D = downstreamMemos(args.graph, new Set(args.writtenSignals));
|
|
20393
|
+
const setters = new Set(args.setterNames);
|
|
20394
|
+
const memoNames = new Set(args.graph.memos.map((m) => m.name));
|
|
20395
|
+
const signalGetters = new Set(args.graph.signals.map((s) => s.name));
|
|
20396
|
+
let sf;
|
|
20397
|
+
try {
|
|
20398
|
+
sf = ts20.createSourceFile("__h.ts", `const __h = ${args.handler}`, ts20.ScriptTarget.Latest, true);
|
|
20399
|
+
} catch {
|
|
20400
|
+
return "unverified";
|
|
20401
|
+
}
|
|
20402
|
+
const calls = [];
|
|
20403
|
+
const visit3 = (node) => {
|
|
20404
|
+
if (ts20.isCallExpression(node) && ts20.isIdentifier(node.expression)) {
|
|
20405
|
+
const name = node.expression.text;
|
|
20406
|
+
if (setters.has(name))
|
|
20407
|
+
calls.push({ pos: node.getStart(sf), kind: "write" });
|
|
20408
|
+
else if (D.has(name) && node.arguments.length === 0)
|
|
20409
|
+
calls.push({ pos: node.getStart(sf), kind: "memoRead" });
|
|
20410
|
+
else if (!signalGetters.has(name) && !memoNames.has(name))
|
|
20411
|
+
calls.push({ pos: node.getStart(sf), kind: "risky" });
|
|
20412
|
+
}
|
|
20413
|
+
ts20.forEachChild(node, visit3);
|
|
20414
|
+
};
|
|
20415
|
+
visit3(sf);
|
|
20416
|
+
calls.sort((a, b) => a.pos - b.pos);
|
|
20417
|
+
let seenWrite = false;
|
|
20418
|
+
let risky = false;
|
|
20419
|
+
for (const c of calls) {
|
|
20420
|
+
if (c.kind === "write")
|
|
20421
|
+
seenWrite = true;
|
|
20422
|
+
else if (seenWrite && c.kind === "memoRead")
|
|
20423
|
+
return "unsafe";
|
|
20424
|
+
else if (seenWrite && c.kind === "risky")
|
|
20425
|
+
risky = true;
|
|
20426
|
+
}
|
|
20427
|
+
if (!seenWrite)
|
|
20428
|
+
return "unverified";
|
|
20429
|
+
return risky ? "unverified" : "safe";
|
|
20430
|
+
}
|
|
20431
|
+
function turnToEventBinding(graph, events) {
|
|
20432
|
+
const byLine = new Map;
|
|
20433
|
+
for (const e of events)
|
|
20434
|
+
if (e.loc)
|
|
20435
|
+
byLine.set(e.loc.start.line, e);
|
|
20436
|
+
const out = new Map;
|
|
20437
|
+
for (const b of graph.domBindings) {
|
|
20438
|
+
if (b.type !== "event" || !b.loc)
|
|
20439
|
+
continue;
|
|
20440
|
+
const eventName = b.label.match(/^(\w+)\s+handler/)?.[1];
|
|
20441
|
+
const binding = byLine.get(b.loc.start.line);
|
|
20442
|
+
if (eventName && binding)
|
|
20443
|
+
out.set(`${graph.componentName}#handler:${b.slotId}:${eventName}`, binding);
|
|
20444
|
+
}
|
|
20445
|
+
return out;
|
|
20446
|
+
}
|
|
20447
|
+
function buildProfileReport(input) {
|
|
20448
|
+
const { source, filePath, componentName, scenario, events } = input;
|
|
20449
|
+
const primary = buildComponentAnalysis(source, filePath, componentName).graph;
|
|
20450
|
+
const allSources = [{ source, filePath }, ...input.extraSources ?? []];
|
|
20451
|
+
const index = new Map;
|
|
20452
|
+
const turnBindings = new Map;
|
|
20453
|
+
let handlersTotal = 0;
|
|
20454
|
+
const instrumentedEffectLines = new Map;
|
|
20455
|
+
for (const s of allSources) {
|
|
20456
|
+
const program = createProgramForFile(s.source, s.filePath)?.program;
|
|
20457
|
+
let componentNames;
|
|
20458
|
+
try {
|
|
20459
|
+
componentNames = listComponentFunctions(s.source, s.filePath);
|
|
20460
|
+
} catch {
|
|
20461
|
+
componentNames = [];
|
|
20462
|
+
}
|
|
20463
|
+
if (componentNames.length === 0)
|
|
20464
|
+
componentNames = [undefined];
|
|
20465
|
+
for (const name of componentNames) {
|
|
20466
|
+
let graph;
|
|
20467
|
+
try {
|
|
20468
|
+
graph = buildComponentAnalysis(s.source, s.filePath, name, program).graph;
|
|
20469
|
+
} catch {
|
|
20470
|
+
continue;
|
|
20471
|
+
}
|
|
20472
|
+
for (const [k, v] of buildIdIndex(graph))
|
|
20473
|
+
index.set(k, v);
|
|
20474
|
+
for (const e of graph.effects) {
|
|
20475
|
+
const set = instrumentedEffectLines.get(e.loc.file) ?? new Set;
|
|
20476
|
+
set.add(e.loc.line);
|
|
20477
|
+
instrumentedEffectLines.set(e.loc.file, set);
|
|
20478
|
+
}
|
|
20479
|
+
try {
|
|
20480
|
+
const summary = buildEventSummary(s.source, s.filePath, name, program);
|
|
20481
|
+
handlersTotal += summary.events.length;
|
|
20482
|
+
for (const [turn, binding] of turnToEventBinding(graph, summary.events)) {
|
|
20483
|
+
turnBindings.set(turn, { binding, graph });
|
|
20484
|
+
}
|
|
20485
|
+
} catch {}
|
|
20486
|
+
}
|
|
20487
|
+
}
|
|
20488
|
+
const hasFallbackEffectId = events.some((e) => e.subscriber !== undefined && isFallbackEffectId(e.subscriber));
|
|
20489
|
+
const uninstrumentedCandidates = [];
|
|
20490
|
+
if (hasFallbackEffectId) {
|
|
20491
|
+
for (const s of allSources) {
|
|
20492
|
+
uninstrumentedCandidates.push(...findUninstrumentedEffects(s.source, s.filePath, instrumentedEffectLines.get(s.filePath) ?? new Set));
|
|
20493
|
+
}
|
|
20494
|
+
}
|
|
20495
|
+
const hotSubscribers = analyzeHotSubscribers(events, index, {
|
|
20496
|
+
topN: input.topN,
|
|
20497
|
+
minMs: input.minMs,
|
|
20498
|
+
uninstrumentedCandidates
|
|
20499
|
+
});
|
|
20500
|
+
const wastedReReruns = analyzeWastedReReruns(events, index, { wastedRatio: input.wastedRatio });
|
|
20501
|
+
const batchAdvisor = analyzeBatchAdvisor(events, index);
|
|
20502
|
+
const { unattributed, diagnostics } = joinProfilerEvents(events, index);
|
|
20503
|
+
for (const c of batchAdvisor.candidates) {
|
|
20504
|
+
const hit = turnBindings.get(c.turn);
|
|
20505
|
+
if (!hit)
|
|
20506
|
+
continue;
|
|
20507
|
+
c.safety = assessBatchSafety({
|
|
20508
|
+
handler: hit.binding.handler,
|
|
20509
|
+
setterNames: hit.binding.setterCalls.map((s) => s.setter),
|
|
20510
|
+
hasIndirectSetters: hit.binding.setterCalls.some((s) => s.via && s.via.length > 0),
|
|
20511
|
+
writtenSignals: hit.binding.setterCalls.map((s) => s.signal).filter((s) => s !== null),
|
|
20512
|
+
graph: hit.graph
|
|
20513
|
+
});
|
|
20514
|
+
}
|
|
20515
|
+
const turnSeqs = new Set;
|
|
20516
|
+
const handlerIds = new Set;
|
|
20517
|
+
for (const e of events) {
|
|
20518
|
+
if (e.turn !== null) {
|
|
20519
|
+
turnSeqs.add(String(e.turnSeq ?? e.turn));
|
|
20520
|
+
handlerIds.add(e.turn);
|
|
20521
|
+
}
|
|
20522
|
+
}
|
|
20523
|
+
return {
|
|
20524
|
+
kind: "profile",
|
|
20525
|
+
componentName: primary.componentName,
|
|
20526
|
+
sourceFile: primary.sourceFile,
|
|
20527
|
+
scenario,
|
|
20528
|
+
events: events.length,
|
|
20529
|
+
turns: turnSeqs.size,
|
|
20530
|
+
hotSubscribers,
|
|
20531
|
+
wastedReReruns,
|
|
20532
|
+
batchAdvisor,
|
|
20533
|
+
coverage: {
|
|
20534
|
+
handlersFired: handlerIds.size,
|
|
20535
|
+
handlersTotal,
|
|
20536
|
+
unattributed,
|
|
20537
|
+
diagnostics: { count: diagnostics.length, sample: diagnostics.slice(0, 3).map((d) => d.id) }
|
|
20538
|
+
}
|
|
20539
|
+
};
|
|
20540
|
+
}
|
|
20541
|
+
function formatProfileReport(r) {
|
|
20542
|
+
const lines = [];
|
|
20543
|
+
lines.push(`${r.componentName} — profile (scenario: ${r.scenario})`);
|
|
20544
|
+
lines.push(` ${r.events} events across ${r.turns} turn(s)`);
|
|
20545
|
+
if (r.turns === 0) {
|
|
20546
|
+
lines.push(r.coverage.handlersTotal === 0 ? " note: no event handlers — run `bf debug profile <component>` for the static budget." : " note: no interactions measured (handlers are likely in composed children) — try `--scenario <story.tsx>`.");
|
|
20547
|
+
}
|
|
20548
|
+
lines.push("");
|
|
20549
|
+
lines.push(formatHotSubscribers(r.hotSubscribers));
|
|
20550
|
+
lines.push("");
|
|
20551
|
+
lines.push(formatWastedReReruns(r.wastedReReruns));
|
|
20552
|
+
lines.push("");
|
|
20553
|
+
lines.push(formatBatchAdvisor(r.batchAdvisor));
|
|
20554
|
+
lines.push("");
|
|
20555
|
+
const c = r.coverage;
|
|
20556
|
+
lines.push(`coverage: ${c.handlersFired}/${c.handlersTotal} handlers exercised`);
|
|
20557
|
+
if (c.unattributed.length > 0) {
|
|
20558
|
+
lines.push(` ⚠ ${c.unattributed.length} unattributed id(s): ${c.unattributed.slice(0, 3).map((u) => u.id).join(", ")}`);
|
|
20559
|
+
}
|
|
20560
|
+
if (c.diagnostics.count > 0) {
|
|
20561
|
+
lines.push(` · ${c.diagnostics.count} anonymous runtime id(s) (non-actionable bookkeeping)`);
|
|
20562
|
+
}
|
|
20563
|
+
return lines.join(`
|
|
20564
|
+
`);
|
|
20565
|
+
}
|
|
20566
|
+
// src/debug-profile.ts
|
|
20567
|
+
var THRESHOLDS = {
|
|
20568
|
+
highFanOut: 3,
|
|
20569
|
+
deepMemoChain: 3,
|
|
20570
|
+
fallbackHeavyMin: 3,
|
|
20571
|
+
fallbackHeavyRatio: 0.5,
|
|
20572
|
+
batchMinSignals: 2
|
|
20573
|
+
};
|
|
20574
|
+
function buildReactiveProfile(source, filePath, componentName) {
|
|
20575
|
+
const graph = buildComponentGraph(source, filePath, componentName);
|
|
20576
|
+
const eventSummary = buildEventSummary(source, filePath, componentName);
|
|
20577
|
+
const hydrated = graph.signals.length > 0 || graph.memos.length > 0 || graph.effects.length > 0;
|
|
20578
|
+
return buildProfileFromGraph(graph, eventSummary, hydrated);
|
|
20579
|
+
}
|
|
20580
|
+
function buildProfileFromGraph(graph, eventSummary, hydrated) {
|
|
20581
|
+
const metrics = computeMetrics(graph, eventSummary, hydrated);
|
|
20582
|
+
const findings = computeFindings(metrics, graph, eventSummary);
|
|
20583
|
+
return { metrics, findings };
|
|
20584
|
+
}
|
|
20585
|
+
function computeMetrics(graph, eventSummary, hydrated) {
|
|
20586
|
+
let maxFanOut = 0;
|
|
20587
|
+
let hotSignal = null;
|
|
20588
|
+
for (const signal of graph.signals) {
|
|
20589
|
+
if (signal.consumers.length > maxFanOut) {
|
|
20590
|
+
maxFanOut = signal.consumers.length;
|
|
20591
|
+
hotSignal = signal.name;
|
|
20592
|
+
}
|
|
20593
|
+
}
|
|
20594
|
+
const maxMemoChainDepth = computeMaxMemoChainDepth(graph);
|
|
20595
|
+
const dynamicBindings = graph.domBindings.length;
|
|
20596
|
+
const fallbacks = graph.domBindings.filter((d) => d.classification === "fallback").length;
|
|
20597
|
+
const eventHandlers = graph.domBindings.filter((d) => d.type === "event").length;
|
|
20598
|
+
const conditionals = graph.domBindings.filter((d) => d.type === "conditional").length;
|
|
20599
|
+
const loops = graph.domBindings.filter((d) => d.type === "loop").length;
|
|
20600
|
+
const totalSubscriptions = graph.memos.reduce((s, m) => s + m.deps.length, 0) + graph.effects.reduce((s, e) => s + e.deps.length, 0) + graph.domBindings.reduce((s, b) => s + b.deps.length, 0);
|
|
20601
|
+
const batchDedupeKeys = new Set;
|
|
20602
|
+
for (const event of eventSummary.events) {
|
|
20603
|
+
const distinct = new Set;
|
|
20604
|
+
for (const sc of event.setterCalls) {
|
|
20605
|
+
if (sc.signal)
|
|
20606
|
+
distinct.add(sc.signal);
|
|
20607
|
+
}
|
|
20608
|
+
if (distinct.size >= THRESHOLDS.batchMinSignals) {
|
|
20609
|
+
const key = `${event.eventName}|${event.loc.file ?? ""}|${event.loc.start.line}|${[...distinct].sort().join(",")}`;
|
|
20610
|
+
batchDedupeKeys.add(key);
|
|
20611
|
+
}
|
|
20612
|
+
}
|
|
20613
|
+
const batchCandidateCount = batchDedupeKeys.size;
|
|
20614
|
+
return {
|
|
20615
|
+
componentName: graph.componentName,
|
|
20616
|
+
sourceFile: graph.sourceFile,
|
|
20617
|
+
hydrated,
|
|
20618
|
+
signals: graph.signals.length,
|
|
20619
|
+
memos: graph.memos.length,
|
|
20620
|
+
effects: graph.effects.length,
|
|
20621
|
+
loops,
|
|
20622
|
+
eventHandlers,
|
|
20623
|
+
dynamicBindings,
|
|
20624
|
+
fallbacks,
|
|
20625
|
+
conditionals,
|
|
20626
|
+
maxSignalFanOut: maxFanOut,
|
|
20627
|
+
hotSignal,
|
|
20628
|
+
maxMemoChainDepth,
|
|
20629
|
+
totalSubscriptions,
|
|
20630
|
+
batchCandidateCount
|
|
20631
|
+
};
|
|
20632
|
+
}
|
|
20633
|
+
function computeMaxMemoChainDepth(graph) {
|
|
20634
|
+
if (graph.memos.length === 0)
|
|
20635
|
+
return 0;
|
|
20636
|
+
const memoSet = new Set(graph.memos.map((m) => m.name));
|
|
20637
|
+
const memoDeps = new Map;
|
|
20638
|
+
for (const memo of graph.memos) {
|
|
20639
|
+
memoDeps.set(memo.name, memo.deps.filter((d) => memoSet.has(d)));
|
|
20640
|
+
}
|
|
20641
|
+
const cache = new Map;
|
|
20642
|
+
function depth(name, visited) {
|
|
20643
|
+
if (cache.has(name))
|
|
20644
|
+
return cache.get(name);
|
|
20645
|
+
if (visited.has(name))
|
|
20646
|
+
return 0;
|
|
20647
|
+
const children = memoDeps.get(name) ?? [];
|
|
20648
|
+
if (children.length === 0) {
|
|
20649
|
+
cache.set(name, 1);
|
|
20650
|
+
return 1;
|
|
20651
|
+
}
|
|
20652
|
+
visited.add(name);
|
|
20653
|
+
const d = 1 + Math.max(...children.map((c) => depth(c, new Set(visited))));
|
|
20654
|
+
cache.set(name, d);
|
|
20655
|
+
return d;
|
|
20656
|
+
}
|
|
20657
|
+
let max = 0;
|
|
20658
|
+
for (const memo of graph.memos) {
|
|
20659
|
+
const d = depth(memo.name, new Set);
|
|
20660
|
+
if (d > max)
|
|
20661
|
+
max = d;
|
|
20662
|
+
}
|
|
20663
|
+
return max;
|
|
20664
|
+
}
|
|
20665
|
+
function computeFindings(metrics, graph, eventSummary) {
|
|
20666
|
+
const findings = [];
|
|
20667
|
+
for (const signal of graph.signals) {
|
|
20668
|
+
if (signal.consumers.length > THRESHOLDS.highFanOut) {
|
|
20669
|
+
findings.push({
|
|
20670
|
+
kind: "high-fan-out",
|
|
20671
|
+
severity: "warning",
|
|
20672
|
+
signal: signal.name,
|
|
20673
|
+
message: `${signal.name} has ${signal.consumers.length} consumers (fan-out > ${THRESHOLDS.highFanOut})`,
|
|
20674
|
+
suggestion: `Split ${signal.name} into finer-grained signals, or add a createMemo to shield downstream consumers from unrelated updates`,
|
|
20675
|
+
loc: { file: signal.loc.file, line: signal.loc.line }
|
|
20676
|
+
});
|
|
20677
|
+
}
|
|
20678
|
+
}
|
|
20679
|
+
if (metrics.maxMemoChainDepth > THRESHOLDS.deepMemoChain) {
|
|
20680
|
+
findings.push({
|
|
20681
|
+
kind: "deep-memo-chain",
|
|
20682
|
+
severity: "warning",
|
|
20683
|
+
depth: metrics.maxMemoChainDepth,
|
|
20684
|
+
message: `Memo chain depth ${metrics.maxMemoChainDepth} (threshold: ${THRESHOLDS.deepMemoChain}) — a single signal update cascades through ${metrics.maxMemoChainDepth} memo levels`,
|
|
20685
|
+
suggestion: "Flatten intermediate memos that do not cache expensive computations; deep chains increase propagation latency"
|
|
20686
|
+
});
|
|
20687
|
+
}
|
|
20688
|
+
const batchSeen = new Set;
|
|
20689
|
+
for (const event of eventSummary.events) {
|
|
20690
|
+
const distinct = new Set;
|
|
20691
|
+
const setterNames = [];
|
|
20692
|
+
for (const sc of event.setterCalls) {
|
|
20693
|
+
if (sc.signal) {
|
|
20694
|
+
distinct.add(sc.signal);
|
|
20695
|
+
setterNames.push(sc.setter);
|
|
20696
|
+
}
|
|
20697
|
+
}
|
|
20698
|
+
if (distinct.size >= THRESHOLDS.batchMinSignals) {
|
|
20699
|
+
const dedupeKey = `${event.eventName}|${event.loc.file ?? ""}|${event.loc.start.line}|${[...distinct].sort().join(",")}`;
|
|
20700
|
+
if (batchSeen.has(dedupeKey))
|
|
20701
|
+
continue;
|
|
20702
|
+
batchSeen.add(dedupeKey);
|
|
20703
|
+
findings.push({
|
|
20704
|
+
kind: "batch-candidate",
|
|
20705
|
+
severity: "info",
|
|
20706
|
+
signals: [...distinct],
|
|
20707
|
+
message: `${event.eventName} on <${event.elementContext}> sets ${distinct.size} signals (${[...distinct].join(", ")}) — triggers ${distinct.size} separate update cycles (static; verify setters are not in separate if/else branches)`,
|
|
20708
|
+
suggestion: `If all listed setters fire unconditionally in the same handler path, wrap in batch(() => { ${setterNames.join("; ")}; }) to collapse ${distinct.size} cycles into 1`,
|
|
20709
|
+
loc: event.loc.file ? { file: event.loc.file, line: event.loc.start.line } : undefined
|
|
20710
|
+
});
|
|
20711
|
+
}
|
|
20712
|
+
}
|
|
20713
|
+
if (metrics.dynamicBindings > 0 && metrics.fallbacks >= THRESHOLDS.fallbackHeavyMin && metrics.fallbacks / metrics.dynamicBindings > THRESHOLDS.fallbackHeavyRatio) {
|
|
20714
|
+
findings.push({
|
|
20715
|
+
kind: "fallback-heavy",
|
|
20716
|
+
severity: "info",
|
|
20717
|
+
message: `${metrics.fallbacks}/${metrics.dynamicBindings} bindings (${Math.round(metrics.fallbacks / metrics.dynamicBindings * 100)}%) are fallback-wrapped — reactivity not statically provable`,
|
|
20718
|
+
suggestion: "Run `bf debug fallbacks` to see each expression and fix them so the compiler can prove reactivity without the fallback wrapper"
|
|
20719
|
+
});
|
|
20720
|
+
}
|
|
20721
|
+
return findings;
|
|
20722
|
+
}
|
|
20723
|
+
function diffProfiles(before, after) {
|
|
20724
|
+
const worseWhenHigher = new Set([
|
|
20725
|
+
"fallbacks",
|
|
20726
|
+
"maxSignalFanOut",
|
|
20727
|
+
"maxMemoChainDepth",
|
|
20728
|
+
"totalSubscriptions",
|
|
20729
|
+
"batchCandidateCount"
|
|
20730
|
+
]);
|
|
20731
|
+
const numericKeys = [
|
|
20732
|
+
"signals",
|
|
20733
|
+
"memos",
|
|
20734
|
+
"effects",
|
|
20735
|
+
"loops",
|
|
20736
|
+
"eventHandlers",
|
|
20737
|
+
"dynamicBindings",
|
|
20738
|
+
"fallbacks",
|
|
20739
|
+
"conditionals",
|
|
20740
|
+
"maxSignalFanOut",
|
|
20741
|
+
"maxMemoChainDepth",
|
|
20742
|
+
"totalSubscriptions",
|
|
20743
|
+
"batchCandidateCount"
|
|
20744
|
+
];
|
|
20745
|
+
const regressions = [];
|
|
20746
|
+
const improvements = [];
|
|
20747
|
+
const neutral = [];
|
|
20748
|
+
for (const key of numericKeys) {
|
|
20749
|
+
const b = before[key];
|
|
20750
|
+
const a = after[key];
|
|
20751
|
+
if (a === b)
|
|
20752
|
+
continue;
|
|
20753
|
+
const entry = { metric: key, before: b, after: a, delta: a - b };
|
|
20754
|
+
if (worseWhenHigher.has(key)) {
|
|
20755
|
+
if (a > b)
|
|
20756
|
+
regressions.push(entry);
|
|
20757
|
+
else
|
|
20758
|
+
improvements.push(entry);
|
|
20759
|
+
} else {
|
|
20760
|
+
neutral.push(entry);
|
|
20761
|
+
}
|
|
20762
|
+
}
|
|
20763
|
+
return { componentName: after.componentName, before, after, regressions, improvements, neutral };
|
|
20764
|
+
}
|
|
20765
|
+
function formatSingleProfile(profile) {
|
|
20766
|
+
const m = profile.metrics;
|
|
20767
|
+
const lines = [];
|
|
20768
|
+
lines.push(`${m.componentName} — reactive profile (static)`);
|
|
20769
|
+
if (m.sourceFile)
|
|
20770
|
+
lines.push(` source: ${m.sourceFile}`);
|
|
20771
|
+
lines.push(` hydrated: ${m.hydrated ? "yes" : "no"}`);
|
|
20772
|
+
lines.push("");
|
|
20773
|
+
lines.push(" Counts:");
|
|
20774
|
+
lines.push(` signals: ${m.signals}`);
|
|
20775
|
+
lines.push(` memos: ${m.memos}`);
|
|
20776
|
+
if (m.effects > 0)
|
|
20777
|
+
lines.push(` effects: ${m.effects}`);
|
|
20778
|
+
lines.push(` dynamic bindings: ${m.dynamicBindings}`);
|
|
20779
|
+
if (m.fallbacks > 0)
|
|
20780
|
+
lines.push(` fallbacks: ${m.fallbacks}`);
|
|
20781
|
+
if (m.loops > 0)
|
|
20782
|
+
lines.push(` loops: ${m.loops}`);
|
|
20783
|
+
if (m.conditionals > 0)
|
|
20784
|
+
lines.push(` conditionals: ${m.conditionals}`);
|
|
20785
|
+
if (m.eventHandlers > 0)
|
|
20786
|
+
lines.push(` event handlers: ${m.eventHandlers}`);
|
|
20787
|
+
lines.push("");
|
|
20788
|
+
lines.push(" Reactive budget (SR5):");
|
|
20789
|
+
const fanOutSuffix = m.hotSignal ? ` (${m.hotSignal})` : "";
|
|
20790
|
+
lines.push(` max signal fan-out: ${m.maxSignalFanOut}${fanOutSuffix}`);
|
|
20791
|
+
lines.push(` max memo chain depth: ${m.maxMemoChainDepth}`);
|
|
20792
|
+
lines.push(` total subscriptions: ${m.totalSubscriptions}`);
|
|
20793
|
+
if (m.batchCandidateCount > 0) {
|
|
20794
|
+
lines.push(` batch candidates: ${m.batchCandidateCount} handler(s) set ≥2 signals`);
|
|
20795
|
+
}
|
|
20796
|
+
if (profile.findings.length > 0) {
|
|
20797
|
+
lines.push("");
|
|
20798
|
+
lines.push(" Findings:");
|
|
20799
|
+
for (const f of profile.findings) {
|
|
20800
|
+
const icon = f.severity === "warning" ? "⚠" : "→";
|
|
20801
|
+
lines.push(` ${icon} [${f.kind}] ${f.message}`);
|
|
20802
|
+
lines.push(` fix: ${f.suggestion}`);
|
|
20803
|
+
if (f.loc) {
|
|
20804
|
+
const file = f.loc.file.split("/").pop() ?? f.loc.file;
|
|
20805
|
+
lines.push(` at ${file}:${f.loc.line}`);
|
|
20806
|
+
}
|
|
20807
|
+
}
|
|
20808
|
+
} else {
|
|
20809
|
+
lines.push("");
|
|
20810
|
+
lines.push(" No findings — component is within all thresholds.");
|
|
20811
|
+
}
|
|
20812
|
+
return lines.join(`
|
|
20813
|
+
`);
|
|
20814
|
+
}
|
|
20815
|
+
function formatProfileTable(profiles) {
|
|
20816
|
+
if (profiles.length === 0)
|
|
20817
|
+
return "No components found.";
|
|
20818
|
+
const sorted = [...profiles].sort((a, b) => b.metrics.totalSubscriptions - a.metrics.totalSubscriptions);
|
|
20819
|
+
const lines = [];
|
|
20820
|
+
lines.push("Component sig memo bind fall fanOut chain subs batch findings");
|
|
20821
|
+
lines.push("─".repeat(90));
|
|
20822
|
+
for (const p of sorted) {
|
|
20823
|
+
const m = p.metrics;
|
|
20824
|
+
const name = m.componentName.padEnd(23).slice(0, 23);
|
|
20825
|
+
const findingStr = p.findings.length > 0 ? p.findings.map((f) => f.kind.replace(/-/g, "_")).join(",") : "—";
|
|
20826
|
+
const row = [
|
|
20827
|
+
name,
|
|
20828
|
+
String(m.signals).padStart(3),
|
|
20829
|
+
String(m.memos).padStart(5),
|
|
20830
|
+
String(m.dynamicBindings).padStart(5),
|
|
20831
|
+
String(m.fallbacks).padStart(5),
|
|
20832
|
+
String(m.maxSignalFanOut).padStart(7),
|
|
20833
|
+
String(m.maxMemoChainDepth).padStart(6),
|
|
20834
|
+
String(m.totalSubscriptions).padStart(5),
|
|
20835
|
+
String(m.batchCandidateCount).padStart(6),
|
|
20836
|
+
` ${findingStr}`
|
|
20837
|
+
].join(" ");
|
|
20838
|
+
lines.push(row);
|
|
20839
|
+
}
|
|
20840
|
+
const allFindings = sorted.flatMap((p) => p.findings.map((f) => ({ component: p.metrics.componentName, finding: f })));
|
|
20841
|
+
if (allFindings.length > 0) {
|
|
20842
|
+
lines.push("");
|
|
20843
|
+
lines.push("Findings:");
|
|
20844
|
+
for (const { component, finding } of allFindings) {
|
|
20845
|
+
const icon = finding.severity === "warning" ? "⚠" : "→";
|
|
20846
|
+
lines.push(` ${icon} ${component}: ${finding.message}`);
|
|
20847
|
+
lines.push(` fix: ${finding.suggestion}`);
|
|
20848
|
+
if (finding.loc) {
|
|
20849
|
+
const file = finding.loc.file.split("/").pop() ?? finding.loc.file;
|
|
20850
|
+
lines.push(` at ${file}:${finding.loc.line}`);
|
|
20851
|
+
}
|
|
20852
|
+
}
|
|
20853
|
+
} else {
|
|
20854
|
+
lines.push("");
|
|
20855
|
+
lines.push("No findings across all components.");
|
|
20856
|
+
}
|
|
20857
|
+
return lines.join(`
|
|
20858
|
+
`);
|
|
20859
|
+
}
|
|
20860
|
+
function formatProfileDiff(diff) {
|
|
20861
|
+
const lines = [];
|
|
20862
|
+
lines.push(`${diff.componentName} — reactive profile diff (before → after)`);
|
|
20863
|
+
lines.push("");
|
|
20864
|
+
if (diff.regressions.length === 0 && diff.improvements.length === 0 && diff.neutral.length === 0) {
|
|
20865
|
+
lines.push(" No changes in reactive metrics.");
|
|
20866
|
+
return lines.join(`
|
|
20867
|
+
`);
|
|
20868
|
+
}
|
|
20869
|
+
if (diff.regressions.length > 0) {
|
|
20870
|
+
lines.push(" Regressions (reactive cost increased):");
|
|
20871
|
+
for (const e of diff.regressions) {
|
|
20872
|
+
lines.push(` ${e.metric}: ${e.before} → ${e.after} (+${e.delta})`);
|
|
20873
|
+
}
|
|
20874
|
+
}
|
|
20875
|
+
if (diff.improvements.length > 0) {
|
|
20876
|
+
lines.push(" Improvements (reactive cost decreased):");
|
|
20877
|
+
for (const e of diff.improvements) {
|
|
20878
|
+
lines.push(` ${e.metric}: ${e.before} → ${e.after} (${e.delta})`);
|
|
20879
|
+
}
|
|
20880
|
+
}
|
|
20881
|
+
if (diff.neutral.length > 0) {
|
|
20882
|
+
lines.push(" Structural changes (count changes, no clear direction):");
|
|
20883
|
+
for (const e of diff.neutral) {
|
|
20884
|
+
const sign = (e.delta ?? 0) > 0 ? "+" : "";
|
|
20885
|
+
lines.push(` ${e.metric}: ${e.before} → ${e.after} (${sign}${e.delta})`);
|
|
20886
|
+
}
|
|
20887
|
+
}
|
|
20888
|
+
return lines.join(`
|
|
20889
|
+
`);
|
|
20890
|
+
}
|
|
20891
|
+
function profileToJSON(profile) {
|
|
20892
|
+
return {
|
|
20893
|
+
metrics: profile.metrics,
|
|
20894
|
+
findings: profile.findings
|
|
20895
|
+
};
|
|
20896
|
+
}
|
|
19226
20897
|
// src/augment-inherited-props.ts
|
|
19227
|
-
import
|
|
20898
|
+
import ts21 from "typescript";
|
|
19228
20899
|
function collectContextConsumers(metadata) {
|
|
19229
20900
|
const constants = metadata.localConstants ?? [];
|
|
19230
20901
|
const contextDefaults = new Map;
|
|
@@ -19252,39 +20923,39 @@ function collectContextConsumers(metadata) {
|
|
|
19252
20923
|
}
|
|
19253
20924
|
function parseUseContextArg(source) {
|
|
19254
20925
|
const expr = parseSingleExpression(source);
|
|
19255
|
-
if (!expr || !
|
|
20926
|
+
if (!expr || !ts21.isCallExpression(expr))
|
|
19256
20927
|
return null;
|
|
19257
|
-
if (!
|
|
20928
|
+
if (!ts21.isIdentifier(expr.expression) || expr.expression.text !== "useContext")
|
|
19258
20929
|
return null;
|
|
19259
20930
|
if (expr.arguments.length !== 1)
|
|
19260
20931
|
return null;
|
|
19261
20932
|
const arg = expr.arguments[0];
|
|
19262
|
-
return
|
|
20933
|
+
return ts21.isIdentifier(arg) ? arg.text : null;
|
|
19263
20934
|
}
|
|
19264
20935
|
function parseCreateContextDefault(source) {
|
|
19265
20936
|
const expr = parseSingleExpression(source);
|
|
19266
|
-
if (!expr || !
|
|
20937
|
+
if (!expr || !ts21.isCallExpression(expr))
|
|
19267
20938
|
return null;
|
|
19268
20939
|
if (expr.arguments.length === 0)
|
|
19269
20940
|
return null;
|
|
19270
20941
|
const arg = expr.arguments[0];
|
|
19271
|
-
if (
|
|
20942
|
+
if (ts21.isStringLiteral(arg) || ts21.isNoSubstitutionTemplateLiteral(arg))
|
|
19272
20943
|
return arg.text;
|
|
19273
|
-
if (
|
|
20944
|
+
if (ts21.isNumericLiteral(arg))
|
|
19274
20945
|
return Number(arg.text);
|
|
19275
|
-
if (arg.kind ===
|
|
20946
|
+
if (arg.kind === ts21.SyntaxKind.TrueKeyword)
|
|
19276
20947
|
return true;
|
|
19277
|
-
if (arg.kind ===
|
|
20948
|
+
if (arg.kind === ts21.SyntaxKind.FalseKeyword)
|
|
19278
20949
|
return false;
|
|
19279
20950
|
return null;
|
|
19280
20951
|
}
|
|
19281
20952
|
function parseSingleExpression(source) {
|
|
19282
|
-
const sf =
|
|
20953
|
+
const sf = ts21.createSourceFile("__ctx.ts", `(${source})`, ts21.ScriptTarget.Latest, false);
|
|
19283
20954
|
const stmt = sf.statements[0];
|
|
19284
|
-
if (!stmt || !
|
|
20955
|
+
if (!stmt || !ts21.isExpressionStatement(stmt))
|
|
19285
20956
|
return null;
|
|
19286
20957
|
let e = stmt.expression;
|
|
19287
|
-
while (
|
|
20958
|
+
while (ts21.isParenthesizedExpression(e))
|
|
19288
20959
|
e = e.expression;
|
|
19289
20960
|
return e;
|
|
19290
20961
|
}
|
|
@@ -19359,28 +21030,28 @@ function augmentInheritedPropAccesses(ir) {
|
|
|
19359
21030
|
}
|
|
19360
21031
|
}
|
|
19361
21032
|
function evalStringArrayJoin(source) {
|
|
19362
|
-
const sf =
|
|
21033
|
+
const sf = ts21.createSourceFile("__join.ts", `const __x = (${source});`, ts21.ScriptTarget.Latest, false);
|
|
19363
21034
|
const stmt = sf.statements[0];
|
|
19364
|
-
if (!stmt || !
|
|
21035
|
+
if (!stmt || !ts21.isVariableStatement(stmt))
|
|
19365
21036
|
return null;
|
|
19366
21037
|
let node = stmt.declarationList.declarations[0]?.initializer;
|
|
19367
|
-
while (node &&
|
|
21038
|
+
while (node && ts21.isParenthesizedExpression(node))
|
|
19368
21039
|
node = node.expression;
|
|
19369
|
-
if (!node || !
|
|
21040
|
+
if (!node || !ts21.isCallExpression(node))
|
|
19370
21041
|
return null;
|
|
19371
21042
|
const callee = node.expression;
|
|
19372
|
-
if (!
|
|
21043
|
+
if (!ts21.isPropertyAccessExpression(callee))
|
|
19373
21044
|
return null;
|
|
19374
21045
|
if (callee.name.text !== "join")
|
|
19375
21046
|
return null;
|
|
19376
21047
|
let recv = callee.expression;
|
|
19377
|
-
while (
|
|
21048
|
+
while (ts21.isParenthesizedExpression(recv))
|
|
19378
21049
|
recv = recv.expression;
|
|
19379
|
-
if (!
|
|
21050
|
+
if (!ts21.isArrayLiteralExpression(recv))
|
|
19380
21051
|
return null;
|
|
19381
21052
|
const parts = [];
|
|
19382
21053
|
for (const el of recv.elements) {
|
|
19383
|
-
if (
|
|
21054
|
+
if (ts21.isStringLiteral(el) || ts21.isNoSubstitutionTemplateLiteral(el)) {
|
|
19384
21055
|
parts.push(el.text);
|
|
19385
21056
|
} else {
|
|
19386
21057
|
return null;
|
|
@@ -19389,7 +21060,7 @@ function evalStringArrayJoin(source) {
|
|
|
19389
21060
|
let sep2 = ",";
|
|
19390
21061
|
if (node.arguments.length >= 1) {
|
|
19391
21062
|
const arg = node.arguments[0];
|
|
19392
|
-
if (
|
|
21063
|
+
if (ts21.isStringLiteral(arg) || ts21.isNoSubstitutionTemplateLiteral(arg))
|
|
19393
21064
|
sep2 = arg.text;
|
|
19394
21065
|
else
|
|
19395
21066
|
return null;
|
|
@@ -19397,11 +21068,11 @@ function evalStringArrayJoin(source) {
|
|
|
19397
21068
|
return parts.join(sep2);
|
|
19398
21069
|
}
|
|
19399
21070
|
function parseRecordIndexAccess(val, localConstants, propsParams, resolveKey) {
|
|
19400
|
-
if (!
|
|
21071
|
+
if (!ts21.isElementAccessExpression(val))
|
|
19401
21072
|
return null;
|
|
19402
21073
|
const obj = val.expression;
|
|
19403
21074
|
const arg = val.argumentExpression;
|
|
19404
|
-
if (!
|
|
21075
|
+
if (!ts21.isIdentifier(obj) || !ts21.isIdentifier(arg))
|
|
19405
21076
|
return null;
|
|
19406
21077
|
let indexPropName;
|
|
19407
21078
|
let defaultKey;
|
|
@@ -19417,35 +21088,35 @@ function parseRecordIndexAccess(val, localConstants, propsParams, resolveKey) {
|
|
|
19417
21088
|
const constInfo = localConstants.find((c) => c.name === obj.text && c.isModule);
|
|
19418
21089
|
if (constInfo?.value === undefined)
|
|
19419
21090
|
return null;
|
|
19420
|
-
const sf =
|
|
21091
|
+
const sf = ts21.createSourceFile("__rec.ts", `(${constInfo.value})`, ts21.ScriptTarget.Latest, true);
|
|
19421
21092
|
if (sf.statements.length !== 1)
|
|
19422
21093
|
return null;
|
|
19423
21094
|
const stmt = sf.statements[0];
|
|
19424
|
-
if (!
|
|
21095
|
+
if (!ts21.isExpressionStatement(stmt))
|
|
19425
21096
|
return null;
|
|
19426
21097
|
let parsed = stmt.expression;
|
|
19427
|
-
while (
|
|
21098
|
+
while (ts21.isParenthesizedExpression(parsed))
|
|
19428
21099
|
parsed = parsed.expression;
|
|
19429
|
-
if (!
|
|
21100
|
+
if (!ts21.isObjectLiteralExpression(parsed))
|
|
19430
21101
|
return null;
|
|
19431
21102
|
const entries = [];
|
|
19432
21103
|
for (const prop of parsed.properties) {
|
|
19433
|
-
if (!
|
|
21104
|
+
if (!ts21.isPropertyAssignment(prop))
|
|
19434
21105
|
return null;
|
|
19435
21106
|
let key;
|
|
19436
|
-
if (
|
|
21107
|
+
if (ts21.isIdentifier(prop.name)) {
|
|
19437
21108
|
key = prop.name.text;
|
|
19438
|
-
} else if (
|
|
21109
|
+
} else if (ts21.isStringLiteral(prop.name) || ts21.isNoSubstitutionTemplateLiteral(prop.name)) {
|
|
19439
21110
|
key = prop.name.text;
|
|
19440
21111
|
} else {
|
|
19441
21112
|
return null;
|
|
19442
21113
|
}
|
|
19443
21114
|
let v = prop.initializer;
|
|
19444
|
-
while (
|
|
21115
|
+
while (ts21.isParenthesizedExpression(v))
|
|
19445
21116
|
v = v.expression;
|
|
19446
|
-
if (
|
|
21117
|
+
if (ts21.isNumericLiteral(v)) {
|
|
19447
21118
|
entries.push({ key, value: { kind: "number", text: v.text } });
|
|
19448
|
-
} else if (
|
|
21119
|
+
} else if (ts21.isStringLiteral(v) || ts21.isNoSubstitutionTemplateLiteral(v)) {
|
|
19449
21120
|
entries.push({ key, value: { kind: "string", text: v.text } });
|
|
19450
21121
|
} else {
|
|
19451
21122
|
return null;
|
|
@@ -19455,12 +21126,16 @@ function parseRecordIndexAccess(val, localConstants, propsParams, resolveKey) {
|
|
|
19455
21126
|
}
|
|
19456
21127
|
export {
|
|
19457
21128
|
traceUpdatePath,
|
|
21129
|
+
testAdapter,
|
|
19458
21130
|
stringifyParsedExpr,
|
|
19459
21131
|
rewriteImportsForTemplate,
|
|
19460
21132
|
resolveSetters,
|
|
19461
21133
|
resetCompilerCounters,
|
|
19462
21134
|
renderImportMapHtml,
|
|
21135
|
+
profileToJSON,
|
|
21136
|
+
parseStyleObjectEntries,
|
|
19463
21137
|
parseRecordIndexAccess,
|
|
21138
|
+
parseProfilerId,
|
|
19464
21139
|
parseExpression,
|
|
19465
21140
|
parseBlockBody,
|
|
19466
21141
|
needsTypeBasedDetection,
|
|
@@ -19468,7 +21143,9 @@ export {
|
|
|
19468
21143
|
listComponentFunctions as listExportedComponents,
|
|
19469
21144
|
listComponentFunctions,
|
|
19470
21145
|
jsxToIR,
|
|
21146
|
+
joinProfilerEvents,
|
|
19471
21147
|
isSupported,
|
|
21148
|
+
isLowerableObjectRestDestructure,
|
|
19472
21149
|
isBooleanAttr,
|
|
19473
21150
|
identifierPath,
|
|
19474
21151
|
graphToJSON,
|
|
@@ -19479,15 +21156,25 @@ export {
|
|
|
19479
21156
|
generateClientJsWithSourceMap,
|
|
19480
21157
|
generateClientJs,
|
|
19481
21158
|
formatWhyUpdate,
|
|
21159
|
+
formatWastedReReruns,
|
|
19482
21160
|
formatUpdatePath,
|
|
21161
|
+
formatStaticBudget,
|
|
21162
|
+
formatSingleProfile,
|
|
19483
21163
|
formatSignalTrace,
|
|
21164
|
+
formatProfileTable,
|
|
21165
|
+
formatProfileReport,
|
|
21166
|
+
formatProfileDiff,
|
|
19484
21167
|
formatParamWithType,
|
|
19485
21168
|
formatLoopSummary,
|
|
21169
|
+
formatHotSubscribers,
|
|
19486
21170
|
formatFallbackExplanations,
|
|
19487
21171
|
formatEventSummary,
|
|
19488
21172
|
formatError,
|
|
19489
21173
|
formatComponentSummary,
|
|
19490
21174
|
formatComponentGraph,
|
|
21175
|
+
formatBudgetDiff,
|
|
21176
|
+
formatBatchAdvisor,
|
|
21177
|
+
findUninstrumentedEffects,
|
|
19491
21178
|
findReachableNames,
|
|
19492
21179
|
extractSsrDefaults,
|
|
19493
21180
|
extractFunctionParams,
|
|
@@ -19499,6 +21186,8 @@ export {
|
|
|
19499
21186
|
emitIRNode,
|
|
19500
21187
|
emitAttrValue,
|
|
19501
21188
|
disableCompilerInstrumentation,
|
|
21189
|
+
diffStaticBudget,
|
|
21190
|
+
diffProfiles,
|
|
19502
21191
|
describeFallback,
|
|
19503
21192
|
createProgramForFile,
|
|
19504
21193
|
createProgramForCorpus,
|
|
@@ -19508,11 +21197,16 @@ export {
|
|
|
19508
21197
|
combineParentChildClientJs,
|
|
19509
21198
|
collectContextConsumers,
|
|
19510
21199
|
buildWhyUpdate,
|
|
21200
|
+
buildStaticBudget,
|
|
19511
21201
|
buildSourceMapFromIR,
|
|
21202
|
+
buildReactiveProfile,
|
|
21203
|
+
buildProfileReport,
|
|
21204
|
+
buildProfileFromGraph,
|
|
19512
21205
|
buildMetadata,
|
|
19513
21206
|
buildLoopSummary,
|
|
19514
21207
|
buildLoopChainExpr,
|
|
19515
21208
|
buildLocalFunctionSetterMap,
|
|
21209
|
+
buildIdIndex,
|
|
19516
21210
|
buildGraphFromIR,
|
|
19517
21211
|
buildEventSummary,
|
|
19518
21212
|
buildComponentSummary,
|
|
@@ -19520,8 +21214,12 @@ export {
|
|
|
19520
21214
|
buildComponentAnalysis,
|
|
19521
21215
|
augmentInheritedPropAccesses,
|
|
19522
21216
|
applyCssLayerPrefix,
|
|
21217
|
+
analyzeWastedReReruns,
|
|
21218
|
+
analyzeHotSubscribers,
|
|
19523
21219
|
analyzeComponent,
|
|
19524
21220
|
analyzeClientNeeds,
|
|
21221
|
+
analyzeBatchAdvisor,
|
|
21222
|
+
TestAdapter,
|
|
19525
21223
|
SourceMapGenerator,
|
|
19526
21224
|
REACTIVE_PRIMITIVES,
|
|
19527
21225
|
JsxAdapter,
|