@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
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* <indent>}) }
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
|
-
import { keyAttrName } from '../../utils.ts'
|
|
33
|
+
import { keyAttrName, profileBindingId } from '../../utils.ts'
|
|
34
34
|
import { emitComponentAndEventSetup } from '../shared.ts'
|
|
35
35
|
import { emitAttrUpdate } from '../../emit-reactive.ts'
|
|
36
36
|
import { emitMultiRootTemplateCloneLines } from './template-parse.ts'
|
|
@@ -44,17 +44,18 @@ export function stringifyInnerLoops(
|
|
|
44
44
|
lines: string[],
|
|
45
45
|
plan: InnerLoopsPlan,
|
|
46
46
|
indent: string,
|
|
47
|
+
pc?: string,
|
|
47
48
|
): void {
|
|
48
49
|
for (const inner of plan) {
|
|
49
50
|
if (inner.emit.mode === 'reactive') {
|
|
50
|
-
emitReactive(lines, inner, indent)
|
|
51
|
+
emitReactive(lines, inner, indent, pc)
|
|
51
52
|
} else {
|
|
52
|
-
emitStatic(lines, inner, indent)
|
|
53
|
+
emitStatic(lines, inner, indent, pc)
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
function emitReactive(lines: string[], inner: InnerLoopPlan, indent: string): void {
|
|
58
|
+
function emitReactive(lines: string[], inner: InnerLoopPlan, indent: string, pc: string | undefined): void {
|
|
58
59
|
const uid = inner.uidSuffix
|
|
59
60
|
const emit = inner.emit
|
|
60
61
|
if (emit.mode !== 'reactive') return // narrow
|
|
@@ -98,16 +99,17 @@ function emitReactive(lines: string[], inner: InnerLoopPlan, indent: string): vo
|
|
|
98
99
|
)
|
|
99
100
|
}
|
|
100
101
|
if (inner.childLevels.length > 0) {
|
|
101
|
-
stringifyInnerLoops(lines, inner.childLevels, `${indent}
|
|
102
|
+
stringifyInnerLoops(lines, inner.childLevels, `${indent} `, pc)
|
|
102
103
|
}
|
|
103
104
|
for (const text of emit.reactiveTexts) {
|
|
105
|
+
const bf = profileBindingId(pc, text.slotId)
|
|
104
106
|
if (text.insideConditional) {
|
|
105
107
|
// Re-query $t inside the effect: insert() may swap the text node so a
|
|
106
108
|
// captured reference would silently stop updating.
|
|
107
|
-
lines.push(`${indent} createEffect(() => { const [__rt] = $t(__innerEl${uid}, '${text.slotId}'); if (__rt) __rt.textContent = String(${text.wrappedExpression}) })`)
|
|
109
|
+
lines.push(`${indent} createEffect(() => { const [__rt] = $t(__innerEl${uid}, '${text.slotId}'); if (__rt) __rt.textContent = String(${text.wrappedExpression}) }${bf})`)
|
|
108
110
|
} else {
|
|
109
111
|
lines.push(`${indent} { const [__rt] = $t(__innerEl${uid}, '${text.slotId}')`)
|
|
110
|
-
lines.push(`${indent} if (__rt) createEffect(() => { __rt.textContent = String(${text.wrappedExpression}) }) }`)
|
|
112
|
+
lines.push(`${indent} if (__rt) createEffect(() => { __rt.textContent = String(${text.wrappedExpression}) }${bf}) }`)
|
|
111
113
|
}
|
|
112
114
|
}
|
|
113
115
|
for (const attr of emit.reactiveAttrs) {
|
|
@@ -117,7 +119,7 @@ function emitReactive(lines: string[], inner: InnerLoopPlan, indent: string): vo
|
|
|
117
119
|
for (const stmt of emitAttrUpdate(targetVar, attr.attrName, attr.wrappedExpression, attr.meta)) {
|
|
118
120
|
lines.push(`${indent} ${stmt}`)
|
|
119
121
|
}
|
|
120
|
-
lines.push(`${indent} }) }`)
|
|
122
|
+
lines.push(`${indent} }${profileBindingId(pc, attr.slotId)}) }`)
|
|
121
123
|
}
|
|
122
124
|
// Imperative ref callbacks fire on every renderItem invocation, which
|
|
123
125
|
// means every mount: SSR hydration, initial CSR creation, and same-key
|
|
@@ -128,10 +130,10 @@ function emitReactive(lines: string[], inner: InnerLoopPlan, indent: string): vo
|
|
|
128
130
|
bodyIsMultiRoot: emit.bodyIsMultiRoot,
|
|
129
131
|
})
|
|
130
132
|
lines.push(`${indent} return __innerEl${uid}`)
|
|
131
|
-
lines.push(`${indent}}, '${inner.markerId}') }`)
|
|
133
|
+
lines.push(`${indent}}, '${inner.markerId}'${profileBindingId(pc, inner.slotId)}) }`)
|
|
132
134
|
}
|
|
133
135
|
|
|
134
|
-
function emitStatic(lines: string[], inner: InnerLoopPlan, indent: string): void {
|
|
136
|
+
function emitStatic(lines: string[], inner: InnerLoopPlan, indent: string, pc: string | undefined): void {
|
|
135
137
|
const uid = inner.uidSuffix
|
|
136
138
|
const emit = inner.emit
|
|
137
139
|
if (emit.mode !== 'static') return
|
|
@@ -161,7 +163,7 @@ function emitStatic(lines: string[], inner: InnerLoopPlan, indent: string): void
|
|
|
161
163
|
inner.outerLoopParamBindings,
|
|
162
164
|
)
|
|
163
165
|
if (inner.childLevels.length > 0) {
|
|
164
|
-
stringifyInnerLoops(lines, inner.childLevels, `${indent}
|
|
166
|
+
stringifyInnerLoops(lines, inner.childLevels, `${indent} `, pc)
|
|
165
167
|
}
|
|
166
168
|
// Imperative ref callbacks for static inner loops — fire once per
|
|
167
169
|
// forEach iteration (#1244). Static arrays don't reactively re-iterate,
|
|
@@ -51,11 +51,17 @@ export function stringifyInsert(
|
|
|
51
51
|
const scopeVar = scopeRefToVar(plan.scope)
|
|
52
52
|
const armIndent = leadingIndent + ' '
|
|
53
53
|
|
|
54
|
+
// Profile mode (#1690, SR4): give the conditional's `insert()` effect an id
|
|
55
|
+
// so the profiler attributes its re-runs to source (resolved from the
|
|
56
|
+
// `conditional` domBinding). Empty when off → byte-identical (SR8).
|
|
57
|
+
const condBfId = plan.profileComponentName
|
|
58
|
+
? `, ${JSON.stringify(`${plan.profileComponentName}#binding:${plan.slotId}`)}`
|
|
59
|
+
: ''
|
|
54
60
|
lines.push(`${leadingIndent}insert(${scopeVar}, '${plan.slotId}', () => ${plan.condition}, {`)
|
|
55
|
-
emitArm(lines, plan.arms[0], plan.eventNameMode, armIndent, bodyIndent)
|
|
61
|
+
emitArm(lines, plan.arms[0], plan.eventNameMode, armIndent, bodyIndent, plan.profileComponentName)
|
|
56
62
|
lines.push(`${leadingIndent}}, {`)
|
|
57
|
-
emitArm(lines, plan.arms[1], plan.eventNameMode, armIndent, bodyIndent)
|
|
58
|
-
lines.push(`${leadingIndent}})`)
|
|
63
|
+
emitArm(lines, plan.arms[1], plan.eventNameMode, armIndent, bodyIndent, plan.profileComponentName)
|
|
64
|
+
lines.push(`${leadingIndent}}${condBfId})`)
|
|
59
65
|
}
|
|
60
66
|
|
|
61
67
|
function emitArm(
|
|
@@ -64,6 +70,7 @@ function emitArm(
|
|
|
64
70
|
mode: 'dom' | 'raw',
|
|
65
71
|
armIndent: string,
|
|
66
72
|
bodyIndent: string,
|
|
73
|
+
profileComponentName?: string,
|
|
67
74
|
): void {
|
|
68
75
|
// `__slots` accumulates live `Node` returns captured by `__bfSlot` so
|
|
69
76
|
// the `insert()` runtime can splice them into the parsed fragment
|
|
@@ -72,7 +79,7 @@ function emitArm(
|
|
|
72
79
|
// wrappers around Child-position interpolations under this var name.
|
|
73
80
|
lines.push(`${armIndent}template: () => { const __slots = []; return { html: \`${arm.templateHtml}\`, slots: __slots } },`)
|
|
74
81
|
lines.push(`${armIndent}bindEvents: (__branchScope, { isFirstRun: __bfFirstRun = false } = {}) => {`)
|
|
75
|
-
emitArmBody(lines, arm.body, mode, bodyIndent)
|
|
82
|
+
emitArmBody(lines, arm.body, mode, bodyIndent, profileComponentName)
|
|
76
83
|
lines.push(`${armIndent}}`)
|
|
77
84
|
}
|
|
78
85
|
|
|
@@ -81,7 +88,12 @@ function emitArmBody(
|
|
|
81
88
|
body: ArmBody,
|
|
82
89
|
mode: 'dom' | 'raw',
|
|
83
90
|
indent: string,
|
|
91
|
+
profileComponentName?: string,
|
|
84
92
|
): void {
|
|
93
|
+
// Profile mode (#1690, SR4): a branch binding effect's id, resolved from its
|
|
94
|
+
// text/attribute `domBinding`. Empty when off → byte-identical (SR8).
|
|
95
|
+
const bindingBfId = (slotId: string): string =>
|
|
96
|
+
profileComponentName ? `, ${JSON.stringify(`${profileComponentName}#binding:${slotId}`)}` : ''
|
|
85
97
|
// 1. Combine event-bearing slots and ref slots into a single `$()` query.
|
|
86
98
|
// Order: events-first, then refs (matches legacy emitter).
|
|
87
99
|
const allSlotIds = new Set<string>()
|
|
@@ -105,7 +117,7 @@ function emitArmBody(
|
|
|
105
117
|
for (const [slotId, slotEvents] of eventsBySlot) {
|
|
106
118
|
const v = varSlotId(slotId)
|
|
107
119
|
for (const ev of slotEvents) {
|
|
108
|
-
emitListenerLine(lines, indent, `_${v}`, ev.eventName, ev.handler, mode)
|
|
120
|
+
emitListenerLine(lines, indent, `_${v}`, ev.eventName, ev.handler, mode, ev.turnId)
|
|
109
121
|
}
|
|
110
122
|
}
|
|
111
123
|
|
|
@@ -156,7 +168,7 @@ function emitArmBody(
|
|
|
156
168
|
for (const stmt of emitAttrUpdate(elVar, attr.attrName, attr.expression, attr)) {
|
|
157
169
|
lines.push(`${indent} ${stmt}`)
|
|
158
170
|
}
|
|
159
|
-
lines.push(`${indent} }))`)
|
|
171
|
+
lines.push(`${indent} }${bindingBfId(slotId)}))`)
|
|
160
172
|
}
|
|
161
173
|
lines.push(`${indent}} }`)
|
|
162
174
|
}
|
|
@@ -172,7 +184,7 @@ function emitArmBody(
|
|
|
172
184
|
lines.push(`${indent}__disposers.push(createDisposableEffect(() => {`)
|
|
173
185
|
lines.push(`${indent} const __val = ${te.expression}`)
|
|
174
186
|
lines.push(`${indent} __anchor_${v} = __bfText(__anchor_${v}, __val)`)
|
|
175
|
-
lines.push(`${indent}}))`)
|
|
187
|
+
lines.push(`${indent}}${bindingBfId(te.slotId)}))`)
|
|
176
188
|
}
|
|
177
189
|
|
|
178
190
|
// Branch loops, now fully Plan-built. The stringifier writes its own
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* every nesting depth.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { varSlotId, DATA_BF_PH, keyAttrName } from '../../utils.ts'
|
|
14
|
+
import { varSlotId, DATA_BF_PH, keyAttrName, profileBindingId } from '../../utils.ts'
|
|
15
15
|
import { emitComponentAndEventSetup } from '../shared.ts'
|
|
16
16
|
import { templateRootIsSvg } from './template-parse.ts'
|
|
17
17
|
import { emitListenerLine } from './event-listener.ts'
|
|
@@ -44,7 +44,7 @@ export function stringifyBranchEventBindings(
|
|
|
44
44
|
// nearest bf-s and miss descendants in that case.
|
|
45
45
|
lines.push(`${indent}{ const _${v} = qsa(__branchScope, '[bf="${slot.slotId}"]')`)
|
|
46
46
|
for (const ev of slot.listeners) {
|
|
47
|
-
emitListenerLine(lines, `${indent} `, `_${v}`, ev.eventName, ev.wrappedHandler)
|
|
47
|
+
emitListenerLine(lines, `${indent} `, `_${v}`, ev.eventName, ev.wrappedHandler, 'dom', ev.turnId)
|
|
48
48
|
}
|
|
49
49
|
lines.push(`${indent}}`)
|
|
50
50
|
}
|
|
@@ -80,6 +80,7 @@ export function stringifyBranchInnerLoops(
|
|
|
80
80
|
lines: string[],
|
|
81
81
|
plan: BranchInnerLoopsPlan,
|
|
82
82
|
indent: string,
|
|
83
|
+
pc?: string,
|
|
83
84
|
): void {
|
|
84
85
|
for (const inner of plan) {
|
|
85
86
|
const uid = inner.uidSuffix
|
|
@@ -110,20 +111,21 @@ export function stringifyBranchInnerLoops(
|
|
|
110
111
|
)
|
|
111
112
|
}
|
|
112
113
|
for (const text of inner.reactiveTexts) {
|
|
114
|
+
const bf = profileBindingId(pc, text.slotId)
|
|
113
115
|
if (text.insideConditional) {
|
|
114
116
|
// Re-query $t inside the effect: insert() may swap the text node so a
|
|
115
117
|
// captured reference would silently stop updating.
|
|
116
|
-
lines.push(`${indent} createEffect(() => { const [__rt] = $t(__bel${uid}, '${text.slotId}'); if (__rt) __rt.textContent = String(${text.wrappedExpression}) })`)
|
|
118
|
+
lines.push(`${indent} createEffect(() => { const [__rt] = $t(__bel${uid}, '${text.slotId}'); if (__rt) __rt.textContent = String(${text.wrappedExpression}) }${bf})`)
|
|
117
119
|
} else {
|
|
118
120
|
lines.push(`${indent} { const [__rt] = $t(__bel${uid}, '${text.slotId}')`)
|
|
119
|
-
lines.push(`${indent} if (__rt) createEffect(() => { __rt.textContent = String(${text.wrappedExpression}) }) }`)
|
|
121
|
+
lines.push(`${indent} if (__rt) createEffect(() => { __rt.textContent = String(${text.wrappedExpression}) }${bf}) }`)
|
|
120
122
|
}
|
|
121
123
|
}
|
|
122
124
|
if (inner.nestedConditionals.length > 0) {
|
|
123
|
-
stringifyLoopChildConditionals(lines, inner.nestedConditionals, `${indent}
|
|
125
|
+
stringifyLoopChildConditionals(lines, inner.nestedConditionals, `${indent} `, pc)
|
|
124
126
|
}
|
|
125
127
|
lines.push(`${indent} return __bel${uid}`)
|
|
126
|
-
lines.push(`${indent}}, '${inner.markerId}') }`)
|
|
128
|
+
lines.push(`${indent}}, '${inner.markerId}'${profileBindingId(pc, inner.slotId)}) }`)
|
|
127
129
|
}
|
|
128
130
|
}
|
|
129
131
|
|
|
@@ -138,9 +140,10 @@ export function stringifyLoopChildConditionals(
|
|
|
138
140
|
lines: string[],
|
|
139
141
|
conditionals: readonly LoopChildConditionalPlan[],
|
|
140
142
|
indent: string,
|
|
143
|
+
pc?: string,
|
|
141
144
|
): void {
|
|
142
145
|
for (const cond of conditionals) {
|
|
143
|
-
stringifyLoopChildConditional(lines, cond, indent)
|
|
146
|
+
stringifyLoopChildConditional(lines, cond, indent, pc)
|
|
144
147
|
}
|
|
145
148
|
}
|
|
146
149
|
|
|
@@ -148,6 +151,7 @@ function stringifyLoopChildConditional(
|
|
|
148
151
|
lines: string[],
|
|
149
152
|
cond: LoopChildConditionalPlan,
|
|
150
153
|
indent: string,
|
|
154
|
+
pc: string | undefined,
|
|
151
155
|
): void {
|
|
152
156
|
const armIndent = `${indent} `
|
|
153
157
|
// Body-form arrows wire `__bfSlot` captures into the runtime so live
|
|
@@ -157,28 +161,29 @@ function stringifyLoopChildConditional(
|
|
|
157
161
|
lines.push(`${indent}insert(${cond.scopeVar}, '${cond.slotId}', () => ${cond.wrappedCondition}, {`)
|
|
158
162
|
lines.push(`${indent} template: () => { const __slots = []; return { html: \`${cond.whenTrueTemplateHtml}\`, slots: __slots } },`)
|
|
159
163
|
lines.push(`${indent} bindEvents: (__branchScope, { isFirstRun: __bfFirstRun = false } = {}) => {`)
|
|
160
|
-
stringifyLoopChildArm(lines, cond.whenTrueArm, armIndent)
|
|
164
|
+
stringifyLoopChildArm(lines, cond.whenTrueArm, armIndent, pc)
|
|
161
165
|
lines.push(`${indent} }`)
|
|
162
166
|
lines.push(`${indent}}, {`)
|
|
163
167
|
lines.push(`${indent} template: () => { const __slots = []; return { html: \`${cond.whenFalseTemplateHtml}\`, slots: __slots } },`)
|
|
164
168
|
lines.push(`${indent} bindEvents: (__branchScope, { isFirstRun: __bfFirstRun = false } = {}) => {`)
|
|
165
|
-
stringifyLoopChildArm(lines, cond.whenFalseArm, armIndent)
|
|
169
|
+
stringifyLoopChildArm(lines, cond.whenFalseArm, armIndent, pc)
|
|
166
170
|
lines.push(`${indent} }`)
|
|
167
|
-
lines.push(`${indent}})`)
|
|
171
|
+
lines.push(`${indent}}${profileBindingId(pc, cond.slotId)})`)
|
|
168
172
|
}
|
|
169
173
|
|
|
170
174
|
function stringifyLoopChildArm(
|
|
171
175
|
lines: string[],
|
|
172
176
|
arm: LoopChildArmPlan,
|
|
173
177
|
armIndent: string,
|
|
178
|
+
pc: string | undefined,
|
|
174
179
|
): void {
|
|
175
180
|
stringifyBranchEventBindings(lines, arm.events, armIndent)
|
|
176
181
|
stringifyBranchChildComponentInits(lines, arm.childComponents, armIndent)
|
|
177
|
-
stringifyBranchInnerLoops(lines, arm.innerLoops, armIndent)
|
|
178
|
-
stringifyLoopChildConditionals(lines, arm.nestedConditionals, armIndent)
|
|
182
|
+
stringifyBranchInnerLoops(lines, arm.innerLoops, armIndent, pc)
|
|
183
|
+
stringifyLoopChildConditionals(lines, arm.nestedConditionals, armIndent, pc)
|
|
179
184
|
for (const text of arm.texts) {
|
|
180
185
|
const varName = `__rt_${varSlotId(text.slotId)}`
|
|
181
186
|
lines.push(`${armIndent}{ const [${varName}] = $t(__branchScope, '${text.slotId}')`)
|
|
182
|
-
lines.push(`${armIndent}if (${varName}) createEffect(() => { ${varName}.textContent = String(${text.wrappedExpression}) }) }`)
|
|
187
|
+
lines.push(`${armIndent}if (${varName}) createEffect(() => { ${varName}.textContent = String(${text.wrappedExpression}) }${profileBindingId(pc, text.slotId)}) }`)
|
|
183
188
|
}
|
|
184
189
|
}
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* passed in via `topIndent`.
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
-
import { emitRefCall, varSlotId } from '../../utils.ts'
|
|
27
|
+
import { emitRefCall, varSlotId, profileBindingId } from '../../utils.ts'
|
|
28
28
|
import { emitAttrUpdate } from '../../emit-reactive.ts'
|
|
29
29
|
import { stringifyReactiveEffects } from './reactive-effects.ts'
|
|
30
30
|
import { emitTemplateCloneInline, emitLoopItemElementSetup } from './template-parse.ts'
|
|
@@ -136,13 +136,14 @@ export function stringifyPlainLoop(
|
|
|
136
136
|
// `childRefs` need `__el` as a handle to invoke the user's callback inside
|
|
137
137
|
// the factory, so non-empty refs force the multi-line layout the same way
|
|
138
138
|
// reactive effects do (#1244).
|
|
139
|
+
const loopBfId = plan.profileLoopId ? `, ${JSON.stringify(plan.profileLoopId)}` : ''
|
|
139
140
|
if (reactiveEffects === null && !bodyIsMultiRoot && childRefs.length === 0) {
|
|
140
141
|
// Single-line renderItem (no reactive effects, single root, no refs).
|
|
141
142
|
const unwrapInline = paramUnwrap ? `${paramUnwrap} ` : ''
|
|
142
143
|
const preamble = mapPreambleWrapped ? `${mapPreambleWrapped}; ` : ''
|
|
143
144
|
const cloneExpr = emitTemplateCloneInline(template)
|
|
144
145
|
lines.push(
|
|
145
|
-
`${topIndent}mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (${paramHead}, ${indexParam}, __existing) => { ${unwrapInline}${preamble}if (__existing) return __existing; ${cloneExpr} }, '${markerId}')`,
|
|
146
|
+
`${topIndent}mapArray(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (${paramHead}, ${indexParam}, __existing) => { ${unwrapInline}${preamble}if (__existing) return __existing; ${cloneExpr} }, '${markerId}'${loopBfId})`,
|
|
146
147
|
)
|
|
147
148
|
return
|
|
148
149
|
}
|
|
@@ -163,7 +164,7 @@ export function stringifyPlainLoop(
|
|
|
163
164
|
}
|
|
164
165
|
emitLoopChildRefs(lines, childRefs, { indent: bodyIndent, elVar: '__el', bodyIsMultiRoot })
|
|
165
166
|
lines.push(`${bodyIndent}return __el`)
|
|
166
|
-
lines.push(`${topIndent}}, '${markerId}')`)
|
|
167
|
+
lines.push(`${topIndent}}, '${markerId}'${loopBfId})`)
|
|
167
168
|
}
|
|
168
169
|
|
|
169
170
|
/**
|
|
@@ -212,11 +213,12 @@ function stringifyAnchoredLoop(
|
|
|
212
213
|
stringifyReactiveEffects(lines, reactiveEffects, { indent: bodyIndent, elVar: '__anchor', bodyIsMultiRoot: false })
|
|
213
214
|
}
|
|
214
215
|
lines.push(`${bodyIndent}return __frag ?? __anchor`)
|
|
215
|
-
|
|
216
|
+
const loopBfId = plan.profileLoopId ? `, ${JSON.stringify(plan.profileLoopId)}` : ''
|
|
217
|
+
lines.push(`${topIndent}}, '${markerId}'${loopBfId})`)
|
|
216
218
|
}
|
|
217
219
|
|
|
218
220
|
export function stringifyStaticLoop(lines: string[], plan: StaticLoopPlan): void {
|
|
219
|
-
const { containerVar, arrayExpr, param, indexParam, childIndexExpr, attrsBySlot, texts, childRefs, csrMaterialize } = plan
|
|
221
|
+
const { containerVar, arrayExpr, param, indexParam, childIndexExpr, attrsBySlot, texts, childRefs, csrMaterialize, profileComponentName: pc } = plan
|
|
220
222
|
const hasAttrs = attrsBySlot.length > 0
|
|
221
223
|
const hasTexts = texts.length > 0
|
|
222
224
|
const hasRefs = childRefs.length > 0
|
|
@@ -298,14 +300,14 @@ export function stringifyStaticLoop(lines: string[], plan: StaticLoopPlan): void
|
|
|
298
300
|
for (const stmt of emitAttrUpdate(varName, attr.attrName, attr.expression, attr)) {
|
|
299
301
|
lines.push(` ${stmt}`)
|
|
300
302
|
}
|
|
301
|
-
lines.push(` })`)
|
|
303
|
+
lines.push(` }${profileBindingId(pc, slotId)})`)
|
|
302
304
|
}
|
|
303
305
|
lines.push(` }`)
|
|
304
306
|
}
|
|
305
307
|
for (const text of texts) {
|
|
306
308
|
const vn = `__rt_${varSlotId(text.slotId)}`
|
|
307
309
|
lines.push(` { const [${vn}] = $t(__iterEl, '${text.slotId}')`)
|
|
308
|
-
lines.push(` if (${vn}) createEffect(() => { ${vn}.textContent = String(${text.expression}) }) }`)
|
|
310
|
+
lines.push(` if (${vn}) createEffect(() => { ${vn}.textContent = String(${text.expression}) }${profileBindingId(pc, text.slotId)}) }`)
|
|
309
311
|
}
|
|
310
312
|
// Ref callbacks fire on every forEach iteration — initial mount and any
|
|
311
313
|
// future array-change-driven re-iteration (#1244). For static arrays the
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* `loop-child-arm.ts` — no legacy passthrough remains.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { varSlotId } from '../../utils.ts'
|
|
11
|
+
import { varSlotId, profileBindingId } from '../../utils.ts'
|
|
12
12
|
import { emitAttrUpdate } from '../../emit-reactive.ts'
|
|
13
13
|
import {
|
|
14
14
|
stringifyBranchChildComponentInits,
|
|
@@ -49,6 +49,8 @@ export function stringifyReactiveEffects(
|
|
|
49
49
|
): void {
|
|
50
50
|
const { indent, elVar, bodyIsMultiRoot } = opts
|
|
51
51
|
const lookup = bodyIsMultiRoot ? 'qsaItem' : 'qsa'
|
|
52
|
+
const pc = plan.profileComponentName
|
|
53
|
+
const bindingBfId = (slotId: string): string => profileBindingId(pc, slotId)
|
|
52
54
|
|
|
53
55
|
// 1. Reactive attribute effects (one qsa per slot, then per-attr createEffect).
|
|
54
56
|
for (const slot of plan.attrSlots) {
|
|
@@ -60,20 +62,20 @@ export function stringifyReactiveEffects(
|
|
|
60
62
|
for (const stmt of emitAttrUpdate(varName, attr.attrName, attr.wrappedExpression, attr.meta)) {
|
|
61
63
|
lines.push(`${indent} ${stmt}`)
|
|
62
64
|
}
|
|
63
|
-
lines.push(`${indent} })`)
|
|
65
|
+
lines.push(`${indent} }${bindingBfId(slot.slotId)})`)
|
|
64
66
|
}
|
|
65
67
|
lines.push(`${indent}} }`)
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
// 2. Outer text effects (slots NOT inside any conditional branch).
|
|
69
71
|
for (const text of plan.outerTexts) {
|
|
70
|
-
emitOuterText(lines, indent, elVar, text)
|
|
72
|
+
emitOuterText(lines, indent, elVar, text, bindingBfId(text.slotId))
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
// 3. Reactive conditionals — each emits an insert(...) over `elVar` whose
|
|
74
76
|
// arm bodies dispatch through the per-arm stringifiers.
|
|
75
77
|
for (const cond of plan.conditionals) {
|
|
76
|
-
emitOuterConditional(lines, indent, elVar, cond)
|
|
78
|
+
emitOuterConditional(lines, indent, elVar, cond, pc)
|
|
77
79
|
}
|
|
78
80
|
}
|
|
79
81
|
|
|
@@ -82,10 +84,11 @@ function emitOuterText(
|
|
|
82
84
|
indent: string,
|
|
83
85
|
elVar: string,
|
|
84
86
|
text: ReactiveTextEffect,
|
|
87
|
+
bfId: string = '',
|
|
85
88
|
): void {
|
|
86
89
|
const varName = `__rt_${varSlotId(text.slotId)}`
|
|
87
90
|
lines.push(`${indent}{ const [${varName}] = $t(${elVar}, '${text.slotId}')`)
|
|
88
|
-
lines.push(`${indent}if (${varName}) createEffect(() => { ${varName}.textContent = String(${text.wrappedExpression}) }) }`)
|
|
91
|
+
lines.push(`${indent}if (${varName}) createEffect(() => { ${varName}.textContent = String(${text.wrappedExpression}) }${bfId}) }`)
|
|
89
92
|
}
|
|
90
93
|
|
|
91
94
|
function emitOuterConditional(
|
|
@@ -93,6 +96,7 @@ function emitOuterConditional(
|
|
|
93
96
|
indent: string,
|
|
94
97
|
elVar: string,
|
|
95
98
|
cond: NestedConditionalPlan,
|
|
99
|
+
pc: string | undefined,
|
|
96
100
|
): void {
|
|
97
101
|
const armIndent = `${indent} `
|
|
98
102
|
|
|
@@ -101,28 +105,28 @@ function emitOuterConditional(
|
|
|
101
105
|
lines.push(`${indent}insert(${elVar}, '${cond.slotId}', () => ${cond.wrappedCondition}, {`)
|
|
102
106
|
lines.push(`${indent} template: () => { const __slots = []; return { html: \`${cond.whenTrueTemplateHtml}\`, slots: __slots } },`)
|
|
103
107
|
lines.push(`${indent} bindEvents: (__branchScope, { isFirstRun: __bfFirstRun = false } = {}) => {`)
|
|
104
|
-
emitArmBody(lines, cond.whenTrueArm, armIndent)
|
|
108
|
+
emitArmBody(lines, cond.whenTrueArm, armIndent, pc)
|
|
105
109
|
lines.push(`${indent} }`)
|
|
106
110
|
lines.push(`${indent}}, {`)
|
|
107
111
|
lines.push(`${indent} template: () => { const __slots = []; return { html: \`${cond.whenFalseTemplateHtml}\`, slots: __slots } },`)
|
|
108
112
|
lines.push(`${indent} bindEvents: (__branchScope, { isFirstRun: __bfFirstRun = false } = {}) => {`)
|
|
109
|
-
emitArmBody(lines, cond.whenFalseArm, armIndent)
|
|
113
|
+
emitArmBody(lines, cond.whenFalseArm, armIndent, pc)
|
|
110
114
|
lines.push(`${indent} }`)
|
|
111
|
-
lines.push(`${indent}})`)
|
|
115
|
+
lines.push(`${indent}}${profileBindingId(pc, cond.slotId)})`)
|
|
112
116
|
}
|
|
113
117
|
|
|
114
|
-
function emitArmBody(lines: string[], arm: LoopChildArmPlan, armIndent: string): void {
|
|
118
|
+
function emitArmBody(lines: string[], arm: LoopChildArmPlan, armIndent: string, pc: string | undefined): void {
|
|
115
119
|
stringifyBranchEventBindings(lines, arm.events, armIndent)
|
|
116
120
|
stringifyBranchChildComponentInits(lines, arm.childComponents, armIndent)
|
|
117
|
-
stringifyBranchInnerLoops(lines, arm.innerLoops, armIndent)
|
|
118
|
-
stringifyLoopChildConditionals(lines, arm.nestedConditionals, armIndent)
|
|
121
|
+
stringifyBranchInnerLoops(lines, arm.innerLoops, armIndent, pc)
|
|
122
|
+
stringifyLoopChildConditionals(lines, arm.nestedConditionals, armIndent, pc)
|
|
119
123
|
for (const text of arm.texts) {
|
|
120
|
-
emitArmText(lines, armIndent, text)
|
|
124
|
+
emitArmText(lines, armIndent, text, pc)
|
|
121
125
|
}
|
|
122
126
|
}
|
|
123
127
|
|
|
124
|
-
function emitArmText(lines: string[], indent: string, text: LoopChildArmText): void {
|
|
128
|
+
function emitArmText(lines: string[], indent: string, text: LoopChildArmText, pc: string | undefined): void {
|
|
125
129
|
const varName = `__rt_${varSlotId(text.slotId)}`
|
|
126
130
|
lines.push(`${indent}{ const [${varName}] = $t(__branchScope, '${text.slotId}')`)
|
|
127
|
-
lines.push(`${indent}if (${varName}) createEffect(() => { ${varName}.textContent = String(${text.wrappedExpression}) }) }`)
|
|
131
|
+
lines.push(`${indent}if (${varName}) createEffect(() => { ${varName}.textContent = String(${text.wrappedExpression}) }${profileBindingId(pc, text.slotId)}) }`)
|
|
128
132
|
}
|
|
@@ -33,8 +33,9 @@ import { stringifyEventDelegation } from './control-flow/stringify/event-delegat
|
|
|
33
33
|
|
|
34
34
|
/** Emit insert() calls for server-rendered reactive conditionals with branch configs. */
|
|
35
35
|
export function emitConditionalUpdates(lines: string[], ctx: ClientJsContext): void {
|
|
36
|
+
const profileComponentName = ctx.profile ? ctx.componentName : undefined
|
|
36
37
|
for (const elem of ctx.conditionalElements) {
|
|
37
|
-
const plan = buildInsertPlan(elem, { scope: { kind: 'top' }, eventNameMode: 'dom' })
|
|
38
|
+
const plan = buildInsertPlan(elem, { scope: { kind: 'top' }, eventNameMode: 'dom', profileComponentName })
|
|
38
39
|
stringifyInsert(lines, plan, { leadingIndent: ' ', bodyIndent: ' ' })
|
|
39
40
|
lines.push('')
|
|
40
41
|
}
|
|
@@ -42,8 +43,9 @@ export function emitConditionalUpdates(lines: string[], ctx: ClientJsContext): v
|
|
|
42
43
|
|
|
43
44
|
/** Emit insert() calls for client-only conditionals (not server-rendered). */
|
|
44
45
|
export function emitClientOnlyConditionals(lines: string[], ctx: ClientJsContext): void {
|
|
46
|
+
const profileComponentName = ctx.profile ? ctx.componentName : undefined
|
|
45
47
|
for (const elem of ctx.clientOnlyConditionals) {
|
|
46
|
-
const plan = buildInsertPlan(elem, { scope: { kind: 'top' }, eventNameMode: 'raw' })
|
|
48
|
+
const plan = buildInsertPlan(elem, { scope: { kind: 'top' }, eventNameMode: 'raw', profileComponentName })
|
|
47
49
|
lines.push(` // @client conditional: ${elem.slotId}`)
|
|
48
50
|
stringifyInsert(lines, plan, { leadingIndent: ' ', bodyIndent: ' ' })
|
|
49
51
|
lines.push('')
|
|
@@ -64,9 +66,12 @@ export function emitClientOnlyConditionals(lines: string[], ctx: ClientJsContext
|
|
|
64
66
|
*/
|
|
65
67
|
export function emitLoopUpdates(lines: string[], ctx: ClientJsContext, unsafeLocalNames: Set<string>): void {
|
|
66
68
|
for (const elem of ctx.loopElements) {
|
|
67
|
-
const plan = buildLoopPlan(elem, {
|
|
69
|
+
const plan = buildLoopPlan(elem, {
|
|
70
|
+
unsafeLocalNames,
|
|
71
|
+
profileComponentName: ctx.profile ? ctx.componentName : undefined,
|
|
72
|
+
})
|
|
68
73
|
stringifyLoop(lines, plan)
|
|
69
|
-
emitLoopEventDelegation(lines, elem, plan.kind)
|
|
74
|
+
emitLoopEventDelegation(lines, elem, plan.kind, ctx.profile ? ctx.componentName : undefined)
|
|
70
75
|
}
|
|
71
76
|
}
|
|
72
77
|
|
|
@@ -74,13 +79,14 @@ function emitLoopEventDelegation(
|
|
|
74
79
|
lines: string[],
|
|
75
80
|
elem: TopLevelLoop,
|
|
76
81
|
kind: 'plain' | 'component' | 'composite' | 'static',
|
|
82
|
+
profileComponentName?: string,
|
|
77
83
|
): void {
|
|
78
84
|
if (kind === 'static') {
|
|
79
85
|
// Event delegation for plain elements in static arrays (#537). Static
|
|
80
86
|
// arrays have no data-key/bf-i markers, so walk up from target to the
|
|
81
87
|
// container's direct child and use indexOf for index lookup.
|
|
82
88
|
if (!elem.childComponent && elem.bindings.events.length > 0) {
|
|
83
|
-
stringifyEventDelegation(lines, buildStaticArrayDelegationPlan(elem))
|
|
89
|
+
stringifyEventDelegation(lines, buildStaticArrayDelegationPlan(elem, profileComponentName))
|
|
84
90
|
}
|
|
85
91
|
return
|
|
86
92
|
}
|
|
@@ -99,6 +105,6 @@ function emitLoopEventDelegation(
|
|
|
99
105
|
&& !elem.useElementReconciliation
|
|
100
106
|
&& elem.bindings.events.length > 0
|
|
101
107
|
) {
|
|
102
|
-
stringifyEventDelegation(lines, buildDynamicLoopDelegationPlan(elem))
|
|
108
|
+
stringifyEventDelegation(lines, buildDynamicLoopDelegationPlan(elem, profileComponentName))
|
|
103
109
|
}
|
|
104
110
|
}
|
|
@@ -10,6 +10,18 @@ import type { ClientJsContext } from './types.ts'
|
|
|
10
10
|
import { toHtmlAttrName, varSlotId, PROPS_PARAM } from './utils.ts'
|
|
11
11
|
import { createTemplateAwareStringProtector } from './html-template.ts'
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Profile mode (#1690, SR3/SR4): the id appended to a DOM-binding effect so the
|
|
15
|
+
* profiler attributes its re-runs to a source location. Keyed by `slotId` (the
|
|
16
|
+
* `bf="sN"` marker) — `buildIdIndex` resolves `<Component>#binding:<slotId>`
|
|
17
|
+
* from `graph.domBindings`, which carry the same slot + loc. Empty when
|
|
18
|
+
* profiling is off, so the emitted effect is byte-for-byte unchanged (SR8).
|
|
19
|
+
*/
|
|
20
|
+
function bindingIdArg(ctx: ClientJsContext, slotId: string | undefined): string {
|
|
21
|
+
if (!ctx.profile || !slotId) return ''
|
|
22
|
+
return `, ${JSON.stringify(`${ctx.componentName}#binding:${slotId}`)}`
|
|
23
|
+
}
|
|
24
|
+
|
|
13
25
|
/**
|
|
14
26
|
* Generate JS statements to update a DOM attribute reactively.
|
|
15
27
|
* Centralizes the attribute-type dispatch (value, class, boolean, presence, generic)
|
|
@@ -110,6 +122,7 @@ export function emitDynamicTextUpdates(lines: string[], ctx: ClientJsContext): v
|
|
|
110
122
|
const v = varSlotId(elem.slotId)
|
|
111
123
|
lines.push(` let __anchor_${v} = _${v}`)
|
|
112
124
|
}
|
|
125
|
+
const __textSlot = (normalElems[0] ?? conditionalElems[0])?.slotId
|
|
113
126
|
lines.push(` createEffect(() => {`)
|
|
114
127
|
if (normalElems.length > 0) {
|
|
115
128
|
// Expression is always evaluated for non-conditional elements
|
|
@@ -139,7 +152,7 @@ export function emitDynamicTextUpdates(lines: string[], ctx: ClientJsContext): v
|
|
|
139
152
|
lines.push(` __bfText(__el_${v}, __val)`)
|
|
140
153
|
}
|
|
141
154
|
}
|
|
142
|
-
lines.push(` })`)
|
|
155
|
+
lines.push(` }${bindingIdArg(ctx, __textSlot)})`)
|
|
143
156
|
lines.push('')
|
|
144
157
|
}
|
|
145
158
|
}
|
|
@@ -151,7 +164,7 @@ export function emitClientOnlyExpressions(lines: string[], ctx: ClientJsContext)
|
|
|
151
164
|
lines.push(` // @client: ${elem.slotId}`)
|
|
152
165
|
lines.push(` createEffect(() => {`)
|
|
153
166
|
lines.push(` updateClientMarker(__scope, '${elem.slotId}', ${elem.expression})`)
|
|
154
|
-
lines.push(` })`)
|
|
167
|
+
lines.push(` }${bindingIdArg(ctx, elem.slotId)})`)
|
|
155
168
|
lines.push('')
|
|
156
169
|
}
|
|
157
170
|
}
|
|
@@ -178,7 +191,7 @@ export function emitReactiveAttributeUpdates(lines: string[], ctx: ClientJsConte
|
|
|
178
191
|
}
|
|
179
192
|
}
|
|
180
193
|
lines.push(` }`)
|
|
181
|
-
lines.push(` })`)
|
|
194
|
+
lines.push(` }${bindingIdArg(ctx, slotId)})`)
|
|
182
195
|
lines.push('')
|
|
183
196
|
}
|
|
184
197
|
}
|
|
@@ -236,7 +249,7 @@ export function emitReactivePropBindings(lines: string[], ctx: ClientJsContext):
|
|
|
236
249
|
lines.push(` }`)
|
|
237
250
|
}
|
|
238
251
|
|
|
239
|
-
lines.push(` })`)
|
|
252
|
+
lines.push(` }${bindingIdArg(ctx, ctx.reactiveProps[0]?.slotId)})`)
|
|
240
253
|
}
|
|
241
254
|
}
|
|
242
255
|
|
|
@@ -271,6 +284,6 @@ export function emitReactiveChildProps(lines: string[], ctx: ClientJsContext): v
|
|
|
271
284
|
lines.push(` }`)
|
|
272
285
|
}
|
|
273
286
|
|
|
274
|
-
lines.push(` })`)
|
|
287
|
+
lines.push(` }${bindingIdArg(ctx, ctx.reactiveChildProps[0]?.slotId ?? undefined)})`)
|
|
275
288
|
}
|
|
276
289
|
}
|
|
@@ -13,6 +13,8 @@ export const RUNTIME_IMPORT_CANDIDATES = [
|
|
|
13
13
|
'provideContext', 'createContext', 'useContext',
|
|
14
14
|
'forwardProps', 'applyRestAttrs', 'splitProps', 'spreadAttrs', 'styleToCss', 'escapeAttr', 'escapeText',
|
|
15
15
|
'qsa', 'qsaItem', 'qsaChildScope', 'qsaChildScopes', 'upsertChildItem', '__slot', '__bfSlot', '__bfText',
|
|
16
|
+
// Profile mode (#1690, SR3) — turn-boundary markers around event handlers.
|
|
17
|
+
'beginTurn', 'endTurn',
|
|
16
18
|
] as const
|
|
17
19
|
|
|
18
20
|
/** @deprecated Use RUNTIME_IMPORT_CANDIDATES */
|
|
@@ -58,6 +58,7 @@ export function generateClientJs(
|
|
|
58
58
|
localImportPrefixes?: string[],
|
|
59
59
|
scope?: ScopeInfo,
|
|
60
60
|
adapterCapabilities?: AdapterCapabilities,
|
|
61
|
+
profile?: boolean,
|
|
61
62
|
): string {
|
|
62
63
|
return generateClientJsWithSourceMap(
|
|
63
64
|
ir,
|
|
@@ -66,6 +67,7 @@ export function generateClientJs(
|
|
|
66
67
|
undefined,
|
|
67
68
|
scope,
|
|
68
69
|
adapterCapabilities,
|
|
70
|
+
profile,
|
|
69
71
|
).code
|
|
70
72
|
}
|
|
71
73
|
|
|
@@ -80,8 +82,9 @@ export function generateClientJsWithSourceMap(
|
|
|
80
82
|
options?: { sourceMaps?: boolean; generatedFileName?: string },
|
|
81
83
|
scope?: ScopeInfo,
|
|
82
84
|
adapterCapabilities?: AdapterCapabilities,
|
|
85
|
+
profile?: boolean,
|
|
83
86
|
): ClientJsResult {
|
|
84
|
-
const ctx = createContext(ir, scope, adapterCapabilities)
|
|
87
|
+
const ctx = createContext(ir, scope, adapterCapabilities, profile)
|
|
85
88
|
const siblingOffsets = computeLoopSiblingOffsets(ir.root)
|
|
86
89
|
collectElements(ir.root, ctx, siblingOffsets)
|
|
87
90
|
|
|
@@ -154,11 +157,13 @@ function createContext(
|
|
|
154
157
|
ir: ComponentIR,
|
|
155
158
|
scope?: ScopeInfo,
|
|
156
159
|
adapterCapabilities?: AdapterCapabilities,
|
|
160
|
+
profile?: boolean,
|
|
157
161
|
): ClientJsContext {
|
|
158
162
|
return {
|
|
159
163
|
componentName: ir.metadata.componentName,
|
|
160
164
|
fileScope: scope?.fileScope ?? '',
|
|
161
165
|
nonExportedSiblings: scope?.nonExportedSiblings ?? new Set(),
|
|
166
|
+
profile: profile ?? false,
|
|
162
167
|
signals: ir.metadata.signals,
|
|
163
168
|
memos: ir.metadata.memos,
|
|
164
169
|
effects: ir.metadata.effects,
|
|
@@ -10,13 +10,19 @@
|
|
|
10
10
|
import type { ClientJsContext } from '../types.ts'
|
|
11
11
|
|
|
12
12
|
export function emitEffectsAndOnMounts(lines: string[], ctx: ClientJsContext): void {
|
|
13
|
-
|
|
13
|
+
ctx.effects.forEach((effect, i) => {
|
|
14
|
+
// Profile mode (#1690): IR-aligned id so runtime effect-run events join to
|
|
15
|
+
// this effect node. Keyed by captureName when present, else the source line
|
|
16
|
+
// (stable across compiles), falling back to source order.
|
|
17
|
+
const idArg = ctx.profile
|
|
18
|
+
? `, ${JSON.stringify(`${ctx.componentName}#effect:${effect.captureName ?? effect.loc?.start.line ?? i}`)}`
|
|
19
|
+
: ''
|
|
14
20
|
if (effect.captureName) {
|
|
15
|
-
lines.push(` const ${effect.captureName} = createEffect(${effect.body})`)
|
|
21
|
+
lines.push(` const ${effect.captureName} = createEffect(${effect.body}${idArg})`)
|
|
16
22
|
} else {
|
|
17
|
-
lines.push(` createEffect(${effect.body})`)
|
|
23
|
+
lines.push(` createEffect(${effect.body}${idArg})`)
|
|
18
24
|
}
|
|
19
|
-
}
|
|
25
|
+
})
|
|
20
26
|
for (const onMount of ctx.onMounts) {
|
|
21
27
|
lines.push(` onMount(${onMount.body})`)
|
|
22
28
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import type { ClientJsContext } from '../types.ts'
|
|
13
|
-
import { toDomEventName, varSlotId, wrapHandlerInBlock } from '../utils.ts'
|
|
13
|
+
import { toDomEventName, varSlotId, wrapHandlerInBlock, wrapHandlerForTurn } from '../utils.ts'
|
|
14
14
|
|
|
15
15
|
export function emitEventHandlers(
|
|
16
16
|
lines: string[],
|
|
@@ -21,7 +21,11 @@ export function emitEventHandlers(
|
|
|
21
21
|
if (conditionalSlotIds.has(elem.slotId)) continue
|
|
22
22
|
for (const event of elem.events) {
|
|
23
23
|
const eventName = toDomEventName(event.name)
|
|
24
|
-
|
|
24
|
+
// Profile mode (#1690, SR3): bracket the handler with turn markers so a
|
|
25
|
+
// profiling run attributes the reactive work it triggers to this turn.
|
|
26
|
+
const wrappedHandler = ctx.profile
|
|
27
|
+
? wrapHandlerForTurn(event.handler, `${ctx.componentName}#handler:${elem.slotId}:${event.name}`)
|
|
28
|
+
: wrapHandlerInBlock(event.handler)
|
|
25
29
|
if (elem.slotId === '__scope') {
|
|
26
30
|
lines.push(` if (__scope) __scope.addEventListener('${eventName}', ${wrappedHandler})`)
|
|
27
31
|
} else {
|