@barefootjs/jsx 0.10.1 → 0.12.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 +1881 -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 +502 -0
- package/dist/profiler.d.ts.map +1 -0
- 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 +466 -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 +1520 -0
- package/src/types.ts +8 -0
|
@@ -16,11 +16,15 @@ import type {
|
|
|
16
16
|
ItemLookup,
|
|
17
17
|
} from './types.ts'
|
|
18
18
|
|
|
19
|
-
export function buildDynamicLoopDelegationPlan(
|
|
19
|
+
export function buildDynamicLoopDelegationPlan(
|
|
20
|
+
elem: TopLevelLoop,
|
|
21
|
+
profileComponentName?: string,
|
|
22
|
+
): EventDelegationPlan {
|
|
20
23
|
return {
|
|
21
24
|
kind: 'event-delegation',
|
|
22
25
|
containerVar: `_${varSlotId(elem.slotId)}`,
|
|
23
26
|
events: elem.bindings.events,
|
|
27
|
+
profileComponentName,
|
|
24
28
|
itemLookup: buildKeyedOrIndexLookup({
|
|
25
29
|
// Chain `.filter()` / `.toSorted()` so the index-based lookup walks
|
|
26
30
|
// the same array shape mapArray reconciled into the DOM (#1434).
|
|
@@ -35,11 +39,19 @@ export function buildDynamicLoopDelegationPlan(elem: TopLevelLoop): EventDelegat
|
|
|
35
39
|
}
|
|
36
40
|
}
|
|
37
41
|
|
|
38
|
-
|
|
42
|
+
// Branch-scoped delegation (a dynamic loop nested inside a conditional branch):
|
|
43
|
+
// `profileComponentName` is threaded from `buildInsertPlan` → `buildBranchLoopPlan`
|
|
44
|
+
// so its delegated handlers get turn markers like every other path (#1786).
|
|
45
|
+
export function buildBranchLoopDelegationPlan(
|
|
46
|
+
loop: BranchLoop,
|
|
47
|
+
cv: string,
|
|
48
|
+
profileComponentName?: string,
|
|
49
|
+
): EventDelegationPlan {
|
|
39
50
|
return {
|
|
40
51
|
kind: 'event-delegation',
|
|
41
52
|
containerVar: `__loop_${cv}`,
|
|
42
53
|
events: loop.bindings.events,
|
|
54
|
+
profileComponentName,
|
|
43
55
|
itemLookup: buildKeyedOrIndexLookup({
|
|
44
56
|
// See note on `buildDynamicLoopDelegationPlan` above (#1434).
|
|
45
57
|
array: buildChainedArrayExpr(loop),
|
|
@@ -56,11 +68,15 @@ export function buildBranchLoopDelegationPlan(loop: BranchLoop, cv: string): Eve
|
|
|
56
68
|
* no `data-key` markers, so the lookup walks up to the container's direct
|
|
57
69
|
* child and uses `indexOf` (with optional sibling offset).
|
|
58
70
|
*/
|
|
59
|
-
export function buildStaticArrayDelegationPlan(
|
|
71
|
+
export function buildStaticArrayDelegationPlan(
|
|
72
|
+
elem: TopLevelLoop,
|
|
73
|
+
profileComponentName?: string,
|
|
74
|
+
): EventDelegationPlan {
|
|
60
75
|
return {
|
|
61
76
|
kind: 'event-delegation',
|
|
62
77
|
containerVar: `_${varSlotId(elem.slotId)}`,
|
|
63
78
|
events: elem.bindings.events,
|
|
79
|
+
profileComponentName,
|
|
64
80
|
itemLookup: {
|
|
65
81
|
kind: 'static-index',
|
|
66
82
|
// Static arrays render through the same `.filter()`/`.toSorted()`
|
|
@@ -131,6 +131,8 @@ export function buildInnerLoopsPlan(args: BuildInnerLoopsArgs): InnerLoopsPlan {
|
|
|
131
131
|
plan.push({
|
|
132
132
|
uidSuffix,
|
|
133
133
|
markerId: inner.markerId,
|
|
134
|
+
// The loop node shares its container element's slot (#1795 Phase 3).
|
|
135
|
+
slotId: inner.containerSlotId ?? '?',
|
|
134
136
|
containerExpr,
|
|
135
137
|
arrayExpr,
|
|
136
138
|
arraySrc: inner.array,
|
|
@@ -24,6 +24,8 @@ import type {
|
|
|
24
24
|
export interface BuildInsertOptions {
|
|
25
25
|
scope: ScopeRef
|
|
26
26
|
eventNameMode: 'dom' | 'raw'
|
|
27
|
+
/** Owning component name in profile mode (#1690, SR3) — else undefined. */
|
|
28
|
+
profileComponentName?: string
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
export function buildInsertPlan(
|
|
@@ -36,6 +38,7 @@ export function buildInsertPlan(
|
|
|
36
38
|
slotId: elem.slotId,
|
|
37
39
|
condition: elem.condition,
|
|
38
40
|
eventNameMode: options.eventNameMode,
|
|
41
|
+
profileComponentName: options.profileComponentName,
|
|
39
42
|
arms: [
|
|
40
43
|
buildArm(elem.whenTrueHtml, elem.slotId, elem.whenTrue, options),
|
|
41
44
|
buildArm(elem.whenFalseHtml, elem.slotId, elem.whenFalse, options),
|
|
@@ -56,11 +59,15 @@ function buildArm(
|
|
|
56
59
|
}
|
|
57
60
|
|
|
58
61
|
function buildArmBody(branch: BranchSummary, options: BuildInsertOptions): ArmBody {
|
|
62
|
+
const pc = options.profileComponentName
|
|
59
63
|
return {
|
|
60
64
|
events: branch.events.map(e => ({
|
|
61
65
|
slotId: e.slotId,
|
|
62
66
|
eventName: e.eventName,
|
|
63
67
|
handler: e.handler,
|
|
68
|
+
// Profile mode (#1690, SR3): turn id so the arm listener is wrapped with
|
|
69
|
+
// beginTurn/endTurn, matching the top-level/delegation paths.
|
|
70
|
+
turnId: pc ? `${pc}#handler:${e.slotId}:${e.eventName}` : undefined,
|
|
64
71
|
})),
|
|
65
72
|
refs: branch.refs.map(r => ({
|
|
66
73
|
slotId: r.slotId,
|
|
@@ -81,13 +88,13 @@ function buildArmBody(branch: BranchSummary, options: BuildInsertOptions): ArmBo
|
|
|
81
88
|
expression: t.expression,
|
|
82
89
|
})),
|
|
83
90
|
// Branch-scoped loops, fully Plan-built (Item 2 final migration).
|
|
84
|
-
loops: branch.loops.map(buildBranchLoopPlan),
|
|
91
|
+
loops: branch.loops.map(l => buildBranchLoopPlan(l, pc)),
|
|
85
92
|
// Nested conditionals are themselves InsertPlans — built recursively so
|
|
86
93
|
// the same stringifier handles arbitrary depth. Their scope is always
|
|
87
94
|
// `__branchScope` (the parent arm's bindEvents argument), regardless of
|
|
88
95
|
// the outer scope; only the eventNameMode is inherited.
|
|
89
96
|
conditionals: branch.conditionals.map(c =>
|
|
90
|
-
buildInsertPlan(c, { scope: { kind: 'branchScope' }, eventNameMode: options.eventNameMode }),
|
|
97
|
+
buildInsertPlan(c, { scope: { kind: 'branchScope' }, eventNameMode: options.eventNameMode, profileComponentName: pc }),
|
|
91
98
|
),
|
|
92
99
|
}
|
|
93
100
|
}
|
|
@@ -76,6 +76,8 @@ export interface BuildBranchEventBindingsArgs {
|
|
|
76
76
|
events: readonly ConditionalBranchEvent[] | undefined
|
|
77
77
|
/** Loop-param wrap closure. Identity (`x => x`) when no loop param applies. */
|
|
78
78
|
wrap: (expr: string) => string
|
|
79
|
+
/** Owning component name in profile mode (#1690, SR3) — else undefined. */
|
|
80
|
+
profileComponentName?: string
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
/**
|
|
@@ -87,7 +89,7 @@ export interface BuildBranchEventBindingsArgs {
|
|
|
87
89
|
export function buildBranchEventBindingsPlan(
|
|
88
90
|
args: BuildBranchEventBindingsArgs,
|
|
89
91
|
): BranchEventBindingsPlan {
|
|
90
|
-
const { events, wrap } = args
|
|
92
|
+
const { events, wrap, profileComponentName: pc } = args
|
|
91
93
|
if (!events || events.length === 0) return []
|
|
92
94
|
|
|
93
95
|
const eventsBySlot = new Map<string, BranchEventListener[]>()
|
|
@@ -100,6 +102,9 @@ export function buildBranchEventBindingsPlan(
|
|
|
100
102
|
bucket.push({
|
|
101
103
|
eventName: ev.eventName,
|
|
102
104
|
wrappedHandler: wrap(ev.handler),
|
|
105
|
+
// Profile mode (#1690, SR3): turn id so the arm listener is wrapped with
|
|
106
|
+
// beginTurn/endTurn, matching every other handler path.
|
|
107
|
+
turnId: pc ? `${pc}#handler:${ev.slotId}:${ev.eventName}` : undefined,
|
|
103
108
|
})
|
|
104
109
|
}
|
|
105
110
|
|
|
@@ -273,6 +278,9 @@ export function buildBranchInnerLoopsPlan(
|
|
|
273
278
|
plan.push({
|
|
274
279
|
uidSuffix: `br_${i}`,
|
|
275
280
|
markerId: inner.markerId,
|
|
281
|
+
// The loop node shares its container element's slot, so `containerSlotId`
|
|
282
|
+
// is the IR slot the analyzer's `loop` domBinding uses (#1795 Phase 3).
|
|
283
|
+
slotId: csl ?? '?',
|
|
276
284
|
containerExpr,
|
|
277
285
|
arrayExpr: wrapOuter(inner.array),
|
|
278
286
|
keyFn: loopKeyFn(inner),
|
|
@@ -50,6 +50,8 @@ import type {
|
|
|
50
50
|
export interface BuildLoopPlanOptions {
|
|
51
51
|
/** Local names whose CSR-time substitution forces the static-array self-heal path (#1247). */
|
|
52
52
|
unsafeLocalNames: Set<string>
|
|
53
|
+
/** Owning component name when compiling in profile mode (#1690) — else undefined. */
|
|
54
|
+
profileComponentName?: string
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
/**
|
|
@@ -64,24 +66,24 @@ export function buildLoopPlan(elem: TopLevelLoop, opts: BuildLoopPlanOptions): L
|
|
|
64
66
|
// array's per-item conditional still toggles reactively instead of freezing
|
|
65
67
|
// in the SSR-time `forEach` (which has no conditional handling at all).
|
|
66
68
|
if (elem.bodyIsItemConditional) {
|
|
67
|
-
return buildPlainLoopPlan(elem)
|
|
69
|
+
return buildPlainLoopPlan(elem, opts.profileComponentName)
|
|
68
70
|
}
|
|
69
71
|
if (elem.isStaticArray) {
|
|
70
|
-
return buildStaticLoopPlan(elem, opts.unsafeLocalNames)
|
|
72
|
+
return buildStaticLoopPlan(elem, opts.unsafeLocalNames, opts.profileComponentName)
|
|
71
73
|
}
|
|
72
74
|
const hasInnerStructure = (elem.nestedComponents?.length ?? 0) > 0
|
|
73
75
|
|| (elem.innerLoops?.length ?? 0) > 0
|
|
74
76
|
if (elem.useElementReconciliation && hasInnerStructure) {
|
|
75
|
-
return buildTopLevelCompositePlan(elem)
|
|
77
|
+
return buildTopLevelCompositePlan(elem, opts.profileComponentName)
|
|
76
78
|
}
|
|
77
79
|
if (elem.childComponent) {
|
|
78
|
-
return buildComponentLoopPlan(elem)
|
|
80
|
+
return buildComponentLoopPlan(elem, opts.profileComponentName)
|
|
79
81
|
}
|
|
80
|
-
return buildPlainLoopPlan(elem)
|
|
82
|
+
return buildPlainLoopPlan(elem, opts.profileComponentName)
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
/** @internal — prefer `buildLoopPlan`. Exported for the branch-loop wrapper only. */
|
|
84
|
-
export function buildPlainLoopPlan(elem: TopLevelLoop): PlainLoopPlan {
|
|
86
|
+
export function buildPlainLoopPlan(elem: TopLevelLoop, profileComponentName?: string): PlainLoopPlan {
|
|
85
87
|
const wrap = (expr: string) => wrapLoopParamAsAccessor(expr, elem.param, elem.paramBindings)
|
|
86
88
|
const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(elem.param, elem.paramBindings)
|
|
87
89
|
const hasReactive = elem.bindings.reactiveAttrs.length > 0
|
|
@@ -92,6 +94,7 @@ export function buildPlainLoopPlan(elem: TopLevelLoop): PlainLoopPlan {
|
|
|
92
94
|
kind: 'plain',
|
|
93
95
|
containerVar: `_${varSlotId(elem.slotId)}`,
|
|
94
96
|
markerId: elem.markerId,
|
|
97
|
+
profileLoopId: profileComponentName ? `${profileComponentName}#binding:${elem.slotId}` : undefined,
|
|
95
98
|
arrayExpr: buildChainedArrayExpr(elem),
|
|
96
99
|
keyFn: loopKeyFn(elem),
|
|
97
100
|
paramHead,
|
|
@@ -99,7 +102,7 @@ export function buildPlainLoopPlan(elem: TopLevelLoop): PlainLoopPlan {
|
|
|
99
102
|
indexParam: elem.index || '__idx',
|
|
100
103
|
mapPreambleWrapped: elem.mapPreamble ? wrap(elem.mapPreamble) : '',
|
|
101
104
|
template: elem.template,
|
|
102
|
-
reactiveEffects: hasReactive ? buildLoopReactiveEffectsPlan(elem) : null,
|
|
105
|
+
reactiveEffects: hasReactive ? buildLoopReactiveEffectsPlan(elem, profileComponentName) : null,
|
|
103
106
|
childRefs: buildChildRefBindings(elem.bindings.refs, elem.param, elem.paramBindings),
|
|
104
107
|
bodyIsMultiRoot: elem.bodyIsMultiRoot ?? false,
|
|
105
108
|
anchored: elem.bodyIsItemConditional ?? false,
|
|
@@ -114,7 +117,7 @@ export function buildPlainLoopPlan(elem: TopLevelLoop): PlainLoopPlan {
|
|
|
114
117
|
}
|
|
115
118
|
|
|
116
119
|
/** @internal — prefer `buildLoopPlan`. */
|
|
117
|
-
export function buildStaticLoopPlan(elem: TopLevelLoop, unsafeLocalNames: Set<string
|
|
120
|
+
export function buildStaticLoopPlan(elem: TopLevelLoop, unsafeLocalNames: Set<string>, profileComponentName?: string): StaticLoopPlan {
|
|
118
121
|
// Group reactive attrs by their child slot id, preserving the legacy
|
|
119
122
|
// declaration-order Map-iteration semantics.
|
|
120
123
|
const attrsBySlotMap = new Map<string, LoopChildReactiveAttr[]>()
|
|
@@ -139,6 +142,7 @@ export function buildStaticLoopPlan(elem: TopLevelLoop, unsafeLocalNames: Set<st
|
|
|
139
142
|
param: elem.param,
|
|
140
143
|
indexParam,
|
|
141
144
|
childIndexExpr,
|
|
145
|
+
profileComponentName,
|
|
142
146
|
attrsBySlot: [...attrsBySlotMap].map(([slotId, attrs]) => [slotId, attrs] as const),
|
|
143
147
|
texts: elem.bindings.reactiveTexts,
|
|
144
148
|
// Static path: forEach binds `param` as the raw value. Passing through
|
|
@@ -50,13 +50,15 @@ export interface BuildReactiveEffectsArgs {
|
|
|
50
50
|
conditionals: readonly LoopChildConditional[] | undefined
|
|
51
51
|
loopParam: string
|
|
52
52
|
loopParamBindings?: readonly LoopParamBinding[]
|
|
53
|
+
/** Owning component name in profile mode (#1690, SR3) — else undefined. */
|
|
54
|
+
profileComponentName?: string
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
/** Build a fully-resolved `ReactiveEffectsPlan` from the IR slice. */
|
|
56
58
|
export function buildReactiveEffectsPlan(
|
|
57
59
|
args: BuildReactiveEffectsArgs,
|
|
58
60
|
): ReactiveEffectsPlan {
|
|
59
|
-
const { attrs, texts, conditionals, loopParam, loopParamBindings } = args
|
|
61
|
+
const { attrs, texts, conditionals, loopParam, loopParamBindings, profileComponentName } = args
|
|
60
62
|
const wrap = (expr: string) => wrapLoopParamAsAccessor(expr, loopParam, loopParamBindings)
|
|
61
63
|
|
|
62
64
|
// 1. Group attrs by slot, preserving declaration order (Map insertion order
|
|
@@ -131,8 +133,8 @@ export function buildReactiveEffectsPlan(
|
|
|
131
133
|
wrappedCondition: wrap(cond.condition),
|
|
132
134
|
whenTrueTemplateHtml: addCondAttrToTemplate(wrap(cond.whenTrueHtml), cond.slotId),
|
|
133
135
|
whenFalseTemplateHtml: addCondAttrToTemplate(wrap(cond.whenFalseHtml), cond.slotId),
|
|
134
|
-
whenTrueArm: buildOuterArm(cond.whenTrue, trueTexts, wrap, loopParam, loopParamBindings),
|
|
135
|
-
whenFalseArm: buildOuterArm(cond.whenFalse, falseTexts, wrap, loopParam, loopParamBindings),
|
|
136
|
+
whenTrueArm: buildOuterArm(cond.whenTrue, trueTexts, wrap, loopParam, loopParamBindings, profileComponentName),
|
|
137
|
+
whenFalseArm: buildOuterArm(cond.whenFalse, falseTexts, wrap, loopParam, loopParamBindings, profileComponentName),
|
|
136
138
|
})
|
|
137
139
|
}
|
|
138
140
|
}
|
|
@@ -141,6 +143,7 @@ export function buildReactiveEffectsPlan(
|
|
|
141
143
|
attrSlots,
|
|
142
144
|
outerTexts,
|
|
143
145
|
conditionals: conditionalPlans,
|
|
146
|
+
profileComponentName,
|
|
144
147
|
}
|
|
145
148
|
}
|
|
146
149
|
|
|
@@ -150,11 +153,13 @@ function buildOuterArm(
|
|
|
150
153
|
wrap: (expr: string) => string,
|
|
151
154
|
loopParam: string,
|
|
152
155
|
loopParamBindings: readonly LoopParamBinding[] | undefined,
|
|
156
|
+
profileComponentName?: string,
|
|
153
157
|
): LoopChildArmPlan {
|
|
154
158
|
return {
|
|
155
159
|
events: buildBranchEventBindingsPlan({
|
|
156
160
|
events: branch.events,
|
|
157
161
|
wrap,
|
|
162
|
+
profileComponentName,
|
|
158
163
|
}),
|
|
159
164
|
childComponents: buildBranchChildComponentInitsPlan({
|
|
160
165
|
components: branch.childComponents,
|
|
@@ -182,12 +187,13 @@ function buildOuterArm(
|
|
|
182
187
|
* Convenience: build a ReactiveEffectsPlan directly from a `TopLevelLoop`,
|
|
183
188
|
* pulling the same IR slice the legacy emitter consumed.
|
|
184
189
|
*/
|
|
185
|
-
export function buildLoopReactiveEffectsPlan(elem: TopLevelLoop): ReactiveEffectsPlan {
|
|
190
|
+
export function buildLoopReactiveEffectsPlan(elem: TopLevelLoop, profileComponentName?: string): ReactiveEffectsPlan {
|
|
186
191
|
return buildReactiveEffectsPlan({
|
|
187
192
|
attrs: elem.bindings.reactiveAttrs,
|
|
188
193
|
texts: elem.bindings.reactiveTexts,
|
|
189
194
|
conditionals: elem.bindings.conditionals,
|
|
190
195
|
loopParam: elem.param,
|
|
191
196
|
loopParamBindings: elem.paramBindings,
|
|
197
|
+
profileComponentName,
|
|
192
198
|
})
|
|
193
199
|
}
|
|
@@ -26,6 +26,13 @@ export interface EventDelegationPlan {
|
|
|
26
26
|
containerVar: string
|
|
27
27
|
events: LoopChildEvent[]
|
|
28
28
|
itemLookup: ItemLookup
|
|
29
|
+
/**
|
|
30
|
+
* Profile mode (#1690, SR3): the owning component name. When set, the
|
|
31
|
+
* stringifier brackets each delegated handler call with `beginTurn`/`endTurn`
|
|
32
|
+
* (id `<Component>#handler:<childSlotId>:<eventName>`). Undefined when
|
|
33
|
+
* profiling is off, so the emitted dispatcher is unchanged (SR8).
|
|
34
|
+
*/
|
|
35
|
+
profileComponentName?: string
|
|
29
36
|
}
|
|
30
37
|
|
|
31
38
|
/**
|
|
@@ -83,6 +83,13 @@ export interface InnerLoopPlan {
|
|
|
83
83
|
uidSuffix: string
|
|
84
84
|
/** Loop marker id — passed to mapArray so sibling loops disambiguate (#1087). */
|
|
85
85
|
markerId: string
|
|
86
|
+
/**
|
|
87
|
+
* IR slot id for this inner loop (#1690, #1795 Phase 3). The loop node shares
|
|
88
|
+
* its container element's slot, so this is `containerSlotId`. Used to emit the
|
|
89
|
+
* `<Component>#binding:<slotId>` profile id on the inner `mapArray`; resolves
|
|
90
|
+
* via the `loop` `domBinding`. `'?'` when the IR carried no slot.
|
|
91
|
+
*/
|
|
92
|
+
slotId: string
|
|
86
93
|
/** Container resolution expression — already includes scope / selectors. */
|
|
87
94
|
containerExpr: string
|
|
88
95
|
/** Array expression as wrapped (reactive) or as written in source (static). */
|
|
@@ -29,6 +29,12 @@ export interface InsertPlan {
|
|
|
29
29
|
* keeps the original event name (used by `@client` conditionals).
|
|
30
30
|
*/
|
|
31
31
|
eventNameMode: 'dom' | 'raw'
|
|
32
|
+
/**
|
|
33
|
+
* Owning component name in profile mode (#1690, SR4). When set, the
|
|
34
|
+
* conditional's `insert()` effect and its branch binding effects carry a
|
|
35
|
+
* `<Component>#binding:<slotId>` id so the profiler attributes them.
|
|
36
|
+
*/
|
|
37
|
+
profileComponentName?: string
|
|
32
38
|
}
|
|
33
39
|
|
|
34
40
|
/** A single branch arm of an insert(). */
|
|
@@ -79,6 +85,8 @@ export interface ArmEventBind {
|
|
|
79
85
|
eventName: string
|
|
80
86
|
/** Handler source expression (already trimmed). The stringifier wraps it. */
|
|
81
87
|
handler: string
|
|
88
|
+
/** Profile-mode turn id (#1690, SR3); when set the listener is turn-wrapped. */
|
|
89
|
+
turnId?: string
|
|
82
90
|
}
|
|
83
91
|
|
|
84
92
|
export interface ArmRefBind {
|
|
@@ -20,6 +20,8 @@ export interface BranchEventListener {
|
|
|
20
20
|
eventName: string
|
|
21
21
|
/** Already wrapped via wrapLoopParamAsAccessor at build time. */
|
|
22
22
|
wrappedHandler: string
|
|
23
|
+
/** Profile-mode turn id (#1690, SR3); when set the listener is turn-wrapped. */
|
|
24
|
+
turnId?: string
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
/** Listeners grouped by slot (one qsa() lookup per slot). */
|
|
@@ -90,6 +92,12 @@ export interface BranchInnerLoop {
|
|
|
90
92
|
uidSuffix: string
|
|
91
93
|
/** Loop marker id — passed to mapArray so sibling loops disambiguate (#1087). */
|
|
92
94
|
markerId: string
|
|
95
|
+
/**
|
|
96
|
+
* IR slot id for the inner loop (#1690, #1795 Phase 3). Used to emit the
|
|
97
|
+
* `<Component>#binding:<slotId>` profile id on the inner `mapArray`; resolves
|
|
98
|
+
* via the `loop` `domBinding`. `'?'` when the IR carried no slot.
|
|
99
|
+
*/
|
|
100
|
+
slotId: string
|
|
93
101
|
/** Container resolution expression — already includes scope and selectors. */
|
|
94
102
|
containerExpr: string
|
|
95
103
|
/** Wrapped array expression (`wrapOuter(inner.array)`). */
|
|
@@ -50,6 +50,13 @@ interface LoopPlanCommon {
|
|
|
50
50
|
interface DynamicLoopCommon extends LoopPlanCommon {
|
|
51
51
|
/** Loop marker id — passed to mapArray so sibling loops disambiguate (#1087). */
|
|
52
52
|
markerId: string
|
|
53
|
+
/**
|
|
54
|
+
* Profile mode (#1690, SR4): the loop's binding id
|
|
55
|
+
* (`<Component>#binding:<slotId>`), passed to `mapArray` as the `bfId` so its
|
|
56
|
+
* internal reconcile effect — typically the costliest subscriber in a list —
|
|
57
|
+
* is attributed to source. Undefined when profiling is off.
|
|
58
|
+
*/
|
|
59
|
+
profileLoopId?: string
|
|
53
60
|
/** Key function source — `null` when the loop has no explicit key. */
|
|
54
61
|
keyFn: string
|
|
55
62
|
/** renderItem param identifier (after destructure unwrap rename). */
|
|
@@ -134,6 +141,11 @@ interface ComponentLoopVariant extends DynamicLoopCommon {
|
|
|
134
141
|
nestedComps: NestedComponentInit[]
|
|
135
142
|
/** Reactive-effects plan for `childConditionals` inside the loop body. */
|
|
136
143
|
childConditionalEffects: ReactiveEffectsPlan | null
|
|
144
|
+
/**
|
|
145
|
+
* Profile-mode loop id (#1690, #1795 Phase 3) for the component loop's
|
|
146
|
+
* `mapArray`. Undefined off → byte-identical (SR8).
|
|
147
|
+
*/
|
|
148
|
+
profileLoopId?: string
|
|
137
149
|
}
|
|
138
150
|
|
|
139
151
|
/**
|
|
@@ -177,6 +189,16 @@ interface CompositeLoopVariant extends DynamicLoopCommon {
|
|
|
177
189
|
* `qsa(__el, ...)` → `qsaItem(__el, ...)` for reactive lookups (#1212).
|
|
178
190
|
*/
|
|
179
191
|
bodyIsMultiRoot: boolean
|
|
192
|
+
/**
|
|
193
|
+
* Profile-mode loop id (#1690, #1795 Phase 3): `<Component>#binding:<slotId>`
|
|
194
|
+
* for the composite `mapArray`. Undefined off → byte-identical (SR8).
|
|
195
|
+
*/
|
|
196
|
+
profileLoopId?: string
|
|
197
|
+
/**
|
|
198
|
+
* Owning component name in profile mode — threaded to `stringifyInnerLoops`
|
|
199
|
+
* so per-item inner-loop / text / attribute effects carry binding ids.
|
|
200
|
+
*/
|
|
201
|
+
profileComponentName?: string
|
|
180
202
|
}
|
|
181
203
|
|
|
182
204
|
/**
|
|
@@ -201,6 +223,12 @@ interface StaticLoopVariant extends LoopPlanCommon {
|
|
|
201
223
|
* Null for the SSR-then-hydrate-only common case.
|
|
202
224
|
*/
|
|
203
225
|
csrMaterialize: StaticLoopMaterializePlan | null
|
|
226
|
+
/**
|
|
227
|
+
* Owning component name in profile mode (#1690, #1795 Phase 3). When set,
|
|
228
|
+
* each static-loop child attribute / text `createEffect` carries a
|
|
229
|
+
* `<Component>#binding:<slotId>` id. Undefined off → byte-identical (SR8).
|
|
230
|
+
*/
|
|
231
|
+
profileComponentName?: string
|
|
204
232
|
}
|
|
205
233
|
|
|
206
234
|
/**
|
|
@@ -62,4 +62,11 @@ export interface ReactiveEffectsPlan {
|
|
|
62
62
|
/** Text effects scoped to the outer renderItem (not inside any conditional). */
|
|
63
63
|
outerTexts: readonly ReactiveTextEffect[]
|
|
64
64
|
conditionals: readonly NestedConditionalPlan[]
|
|
65
|
+
/**
|
|
66
|
+
* Owning component name in profile mode (#1690, SR4, #1795 Phase 2). When
|
|
67
|
+
* set, each loop-child attribute / outer-text `createEffect` carries a
|
|
68
|
+
* `<Component>#binding:<slotId>` id so the profiler attributes its per-item
|
|
69
|
+
* re-runs to source. Undefined off → byte-identical (SR8).
|
|
70
|
+
*/
|
|
71
|
+
profileComponentName?: string
|
|
65
72
|
}
|
|
@@ -57,8 +57,10 @@ function emitPlain(lines: string[], plan: BranchPlainLoopPlan): void {
|
|
|
57
57
|
eventDelegation,
|
|
58
58
|
childRefs,
|
|
59
59
|
bodyIsMultiRoot,
|
|
60
|
+
profileLoopId,
|
|
60
61
|
} = plan
|
|
61
62
|
|
|
63
|
+
const loopBfId = profileLoopId ? `, ${JSON.stringify(profileLoopId)}` : ''
|
|
62
64
|
const unwrapInline = paramUnwrap ? `${paramUnwrap} ` : ''
|
|
63
65
|
|
|
64
66
|
// Wrap the mapArray() in a disposable effect so the inner createEffect
|
|
@@ -73,9 +75,9 @@ function emitPlain(lines: string[], plan: BranchPlainLoopPlan): void {
|
|
|
73
75
|
// Simple case: single-line renderItem (single root, no reactive effects).
|
|
74
76
|
const cloneExpr = emitTemplateCloneInline(template)
|
|
75
77
|
if (mapPreambleWrapped) {
|
|
76
|
-
lines.push(` if (${containerVar}) mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (${paramHead}, ${indexParam}, __existing) => { ${unwrapInline}if (__existing) return __existing; ${mapPreambleWrapped}; ${cloneExpr} }, '${markerId}')`)
|
|
78
|
+
lines.push(` if (${containerVar}) mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (${paramHead}, ${indexParam}, __existing) => { ${unwrapInline}if (__existing) return __existing; ${mapPreambleWrapped}; ${cloneExpr} }, '${markerId}'${loopBfId})`)
|
|
77
79
|
} else {
|
|
78
|
-
lines.push(` if (${containerVar}) mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (${paramHead}, ${indexParam}, __existing) => { ${unwrapInline}if (__existing) return __existing; ${cloneExpr} }, '${markerId}')`)
|
|
80
|
+
lines.push(` if (${containerVar}) mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (${paramHead}, ${indexParam}, __existing) => { ${unwrapInline}if (__existing) return __existing; ${cloneExpr} }, '${markerId}'${loopBfId})`)
|
|
79
81
|
}
|
|
80
82
|
} else {
|
|
81
83
|
// Multi-line renderItem (reactive effects and/or multi-root and/or refs).
|
|
@@ -97,7 +99,7 @@ function emitPlain(lines: string[], plan: BranchPlainLoopPlan): void {
|
|
|
97
99
|
}
|
|
98
100
|
emitLoopChildRefs(lines, childRefs, { indent: ' ', elVar: '__el', bodyIsMultiRoot })
|
|
99
101
|
lines.push(` return __el`)
|
|
100
|
-
lines.push(` }, '${markerId}')`)
|
|
102
|
+
lines.push(` }, '${markerId}'${loopBfId})`)
|
|
101
103
|
}
|
|
102
104
|
lines.push(` }))`)
|
|
103
105
|
|
|
@@ -48,8 +48,10 @@ export function stringifyComponentLoop(lines: string[], plan: ComponentLoopPlan)
|
|
|
48
48
|
keyExpr,
|
|
49
49
|
nestedComps,
|
|
50
50
|
childConditionalEffects,
|
|
51
|
+
profileLoopId,
|
|
51
52
|
} = plan
|
|
52
53
|
|
|
54
|
+
const loopBfId = profileLoopId ? `, ${JSON.stringify(profileLoopId)}` : ''
|
|
53
55
|
lines.push(` mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (${paramHead}, ${indexParam}, __existing) => {`)
|
|
54
56
|
if (paramUnwrap) lines.push(` ${paramUnwrap}`)
|
|
55
57
|
|
|
@@ -58,7 +60,7 @@ export function stringifyComponentLoop(lines: string[], plan: ComponentLoopPlan)
|
|
|
58
60
|
if (nestedComps.length === 0) {
|
|
59
61
|
lines.push(` if (__existing) { initChild('${scopedComp}', __existing, ${componentPropsExpr}); return __existing }`)
|
|
60
62
|
lines.push(` return createComponent('${scopedComp}', ${componentPropsExpr}, ${keyExpr})`)
|
|
61
|
-
lines.push(` }, '${markerId}')`)
|
|
63
|
+
lines.push(` }, '${markerId}'${loopBfId})`)
|
|
62
64
|
return
|
|
63
65
|
}
|
|
64
66
|
|
|
@@ -79,7 +81,7 @@ export function stringifyComponentLoop(lines: string[], plan: ComponentLoopPlan)
|
|
|
79
81
|
stringifyReactiveEffects(lines, childConditionalEffects, { indent: ' ', elVar: '__csrEl' })
|
|
80
82
|
}
|
|
81
83
|
lines.push(` return __csrEl`)
|
|
82
|
-
lines.push(` }, '${markerId}')`)
|
|
84
|
+
lines.push(` }, '${markerId}'${loopBfId})`)
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
function emitNestedInit(lines: string[], indent: string, parentVar: string, nc: NestedComponentInit): void {
|
|
@@ -59,6 +59,8 @@ export function stringifyCompositeLoop(lines: string[], plan: CompositeLoopPlan)
|
|
|
59
59
|
topIndent,
|
|
60
60
|
bodyIndent: rawBodyIndent,
|
|
61
61
|
bodyIsMultiRoot,
|
|
62
|
+
profileComponentName: pc,
|
|
63
|
+
profileLoopId,
|
|
62
64
|
} = plan
|
|
63
65
|
|
|
64
66
|
// When wrapping the mapArray in createDisposableEffect (branch case), the
|
|
@@ -120,7 +122,7 @@ export function stringifyCompositeLoop(lines: string[], plan: CompositeLoopPlan)
|
|
|
120
122
|
})
|
|
121
123
|
emitComponentAndEventSetup(lines, bodyIndent, '__el', compsArr, eventsArr, loopParam, loopParamBindings, bodyIsMultiRoot)
|
|
122
124
|
if (innerLoops.length > 0) {
|
|
123
|
-
stringifyInnerLoops(lines, innerLoops, bodyIndent)
|
|
125
|
+
stringifyInnerLoops(lines, innerLoops, bodyIndent, pc)
|
|
124
126
|
}
|
|
125
127
|
|
|
126
128
|
if (reactiveEffects) {
|
|
@@ -130,11 +132,12 @@ export function stringifyCompositeLoop(lines: string[], plan: CompositeLoopPlan)
|
|
|
130
132
|
emitLoopChildRefs(lines, childRefs, { indent: bodyIndent, elVar: '__el', bodyIsMultiRoot })
|
|
131
133
|
|
|
132
134
|
lines.push(`${bodyIndent}return __el`)
|
|
135
|
+
const loopBfId = profileLoopId ? `, ${JSON.stringify(profileLoopId)}` : ''
|
|
133
136
|
if (branchClearChildren) {
|
|
134
137
|
// Close inner mapArray + createDisposableEffect wrapper.
|
|
135
|
-
lines.push(`${mapArrayIndent}}, '${markerId}')`)
|
|
138
|
+
lines.push(`${mapArrayIndent}}, '${markerId}'${loopBfId})`)
|
|
136
139
|
lines.push(`${topIndent}}))`)
|
|
137
140
|
} else {
|
|
138
|
-
lines.push(`${topIndent}}, '${markerId}')`)
|
|
141
|
+
lines.push(`${topIndent}}, '${markerId}'${loopBfId})`)
|
|
139
142
|
}
|
|
140
143
|
}
|
|
@@ -48,8 +48,20 @@ const NON_BUBBLING_EVENTS = new Set([
|
|
|
48
48
|
'pointerenter', 'pointerleave',
|
|
49
49
|
])
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Profile mode (#1690, SR3): bracket a delegated handler call with turn
|
|
53
|
+
* markers so a profiling run attributes the reactive work it triggers to one
|
|
54
|
+
* turn. `call` is the inline invocation (e.g. `(handler)(__bfEvt)`); the
|
|
55
|
+
* wrapper keeps it a single statement so it drops into every lookup shape.
|
|
56
|
+
*/
|
|
57
|
+
function withTurn(call: string, componentName: string | undefined, childSlotId: string, eventName: string): string {
|
|
58
|
+
if (!componentName) return call
|
|
59
|
+
const id = JSON.stringify(`${componentName}#handler:${childSlotId}:${eventName}`)
|
|
60
|
+
return `beginTurn(${id}); try { ${call} } finally { endTurn() }`
|
|
61
|
+
}
|
|
62
|
+
|
|
51
63
|
export function stringifyEventDelegation(lines: string[], plan: EventDelegationPlan): void {
|
|
52
|
-
const { containerVar, events, itemLookup } = plan
|
|
64
|
+
const { containerVar, events, itemLookup, profileComponentName } = plan
|
|
53
65
|
const eventsByName = new Map<string, LoopChildEvent[]>()
|
|
54
66
|
for (const ev of events) {
|
|
55
67
|
if (!eventsByName.has(ev.eventName)) eventsByName.set(ev.eventName, [])
|
|
@@ -70,7 +82,7 @@ export function stringifyEventDelegation(lines: string[], plan: EventDelegationP
|
|
|
70
82
|
const childVar = varSlotId(ev.childSlotId)
|
|
71
83
|
lines.push(` const ${childVar}El = target.closest('[bf="${ev.childSlotId}"]')`)
|
|
72
84
|
lines.push(` if (${childVar}El) {`)
|
|
73
|
-
const handlerCall = `(${ev.handler.trim()})(__bfEvt)
|
|
85
|
+
const handlerCall = withTurn(`(${ev.handler.trim()})(__bfEvt)`, profileComponentName, ev.childSlotId, ev.eventName)
|
|
74
86
|
switch (itemLookup.kind) {
|
|
75
87
|
case 'keyed':
|
|
76
88
|
emitKeyedLookup(lines, ev, handlerCall, itemLookup)
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* by `@client` conditionals).
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import { toDomEventName, wrapHandlerInBlock } from '../../utils.ts'
|
|
16
|
+
import { toDomEventName, wrapHandlerInBlock, wrapHandlerForTurn } from '../../utils.ts'
|
|
17
17
|
|
|
18
18
|
export type EventNameMode = 'dom' | 'raw'
|
|
19
19
|
|
|
@@ -31,8 +31,11 @@ export function emitListenerLine(
|
|
|
31
31
|
eventName: string,
|
|
32
32
|
handler: string,
|
|
33
33
|
mode: EventNameMode = 'dom',
|
|
34
|
+
turnId?: string,
|
|
34
35
|
): void {
|
|
35
|
-
|
|
36
|
+
// Profile mode (#1690, SR3): when a turn id is supplied the handler is
|
|
37
|
+
// bracketed with beginTurn/endTurn instead of the plain block wrap.
|
|
38
|
+
const wrapped = turnId ? wrapHandlerForTurn(handler, turnId) : wrapHandlerInBlock(handler)
|
|
36
39
|
const name = mode === 'dom' ? toDomEventName(eventName) : eventName
|
|
37
40
|
lines.push(`${indent}if (${elementVar}) ${elementVar}.addEventListener('${name}', ${wrapped})`)
|
|
38
41
|
}
|