@barefootjs/cli 0.26.4 → 0.27.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/docs/core/advanced/compiler-internals.md +8 -7
- package/dist/docs/core/advanced/performance.md +1 -1
- package/dist/docs/core/core-concepts/how-it-works.mdx +23 -7
- package/dist/docs/core/introduction.mdx +6 -4
- package/dist/docs/core/rendering/client-directive.md +24 -7
- package/dist/index.js +337 -102
- package/package.json +4 -4
|
@@ -195,7 +195,7 @@ createEffect(() => {
|
|
|
195
195
|
### 4. Code Generation Order
|
|
196
196
|
|
|
197
197
|
```javascript
|
|
198
|
-
import { $,
|
|
198
|
+
import { $, createEffect, createMemo, createSignal, escapeTextOrNode, hydrate, lazySlots, onMount } from '@barefootjs/client/runtime'
|
|
199
199
|
|
|
200
200
|
export function initCounter(__scope, _p = {}) {
|
|
201
201
|
if (!__scope) return
|
|
@@ -216,15 +216,16 @@ export function initCounter(__scope, _p = {}) {
|
|
|
216
216
|
const doubled = createMemo(() => count() * 2)
|
|
217
217
|
|
|
218
218
|
// 4. Element references (always destructured, always returns array)
|
|
219
|
-
// $()
|
|
220
|
-
// $t() — text nodes: find comment marker <!--bf:id-->
|
|
219
|
+
// $() — regular elements: querySelector('[bf="id"]') within scope
|
|
221
220
|
const [_s3] = $(__scope, 's3')
|
|
222
|
-
const [_s0, _s2] = $t(__scope, 's0', 's2')
|
|
223
221
|
|
|
224
|
-
// 5. Dynamic
|
|
222
|
+
// 5. Dynamic content updates — the compiler emits a claim plan (data)
|
|
223
|
+
// per slot; lazySlots() claims the <!--bf:id-->…<!--/--> marker
|
|
224
|
+
// range once on first write, then writes through the held reference
|
|
225
|
+
const __bfw_s0 = lazySlots(__scope, [{ id: 's0', kind: 'markup', path: [] }])
|
|
225
226
|
createEffect(() => {
|
|
226
227
|
const __val = count()
|
|
227
|
-
|
|
228
|
+
__bfw_s0('s0', escapeTextOrNode(__val))
|
|
228
229
|
})
|
|
229
230
|
|
|
230
231
|
// 6. Reactive attribute updates
|
|
@@ -266,7 +267,7 @@ hydrate('Counter', {
|
|
|
266
267
|
Only used imports are included:
|
|
267
268
|
|
|
268
269
|
```javascript
|
|
269
|
-
import { $,
|
|
270
|
+
import { $, createEffect, createMemo, createSignal, escapeTextOrNode, hydrate, lazySlots, onMount } from '@barefootjs/client/runtime'
|
|
270
271
|
```
|
|
271
272
|
|
|
272
273
|
### 6. Template Registration
|
|
@@ -51,7 +51,7 @@ The compiler detects static arrays and skips reconciliation:
|
|
|
51
51
|
const tabs = ['Home', 'About', 'Contact']
|
|
52
52
|
{tabs.map(tab => <Tab label={tab} />)}
|
|
53
53
|
|
|
54
|
-
// Dynamic —
|
|
54
|
+
// Dynamic — keyed reconciliation (mapArray) needed
|
|
55
55
|
const [items, setItems] = createSignal([...])
|
|
56
56
|
{items().map(item => <Item key={item.id} data={item} />)}
|
|
57
57
|
```
|
|
@@ -82,7 +82,7 @@ export function Counter({ __instanceId, ... }) {
|
|
|
82
82
|
Client JS (Phase 2b):
|
|
83
83
|
|
|
84
84
|
```js
|
|
85
|
-
import { $,
|
|
85
|
+
import { $, createEffect, createSignal, escapeText, escapeTextOrNode, hydrate, lazySlots } from '@barefootjs/client/runtime'
|
|
86
86
|
|
|
87
87
|
export function initCounter(__scope, _p = {}) {
|
|
88
88
|
if (!__scope) return
|
|
@@ -90,11 +90,13 @@ export function initCounter(__scope, _p = {}) {
|
|
|
90
90
|
const [count, setCount] = createSignal(0)
|
|
91
91
|
|
|
92
92
|
const [_s1] = $(__scope, 's1') // element lookup
|
|
93
|
-
const [_s0] = $t(__scope, 's0') // text node lookup
|
|
94
93
|
|
|
94
|
+
// Content slot: claimed lazily from the <!--bf:s0-->…<!--/--> marker
|
|
95
|
+
// pair on first write, then updated through the held reference
|
|
96
|
+
const __bfw_s0 = lazySlots(__scope, [{ id: 's0', kind: 'markup', path: [] }])
|
|
95
97
|
createEffect(() => {
|
|
96
98
|
const __val = count()
|
|
97
|
-
|
|
99
|
+
__bfw_s0('s0', escapeTextOrNode(__val))
|
|
98
100
|
})
|
|
99
101
|
|
|
100
102
|
if (_s1) _s1.addEventListener('click', () => { setCount(n => n + 1) })
|
|
@@ -102,7 +104,7 @@ export function initCounter(__scope, _p = {}) {
|
|
|
102
104
|
|
|
103
105
|
hydrate('Counter', {
|
|
104
106
|
init: initCounter,
|
|
105
|
-
template: (_p) => `<button bf="s1"> Count: <!--bf:s0-->${(0)}<!--/--></button>`
|
|
107
|
+
template: (_p) => `<button bf="s1"> Count: <!--bf:s0-->${escapeText((0))}<!--/--></button>`
|
|
106
108
|
})
|
|
107
109
|
```
|
|
108
110
|
|
|
@@ -135,9 +137,11 @@ Marker-driven hydration attaches behavior to server-rendered HTML.
|
|
|
135
137
|
4. Init function runs per scope — signals, effects, handlers
|
|
136
138
|
5. Runtime tracks scopes to prevent double initialization
|
|
137
139
|
|
|
138
|
-
### Scoped Queries
|
|
140
|
+
### Scoped Queries and Slot Claiming
|
|
139
141
|
|
|
140
|
-
`$()` and
|
|
142
|
+
Element lookups (`$()`) and content-slot claims (`claimSlots()` /
|
|
143
|
+
`lazySlots()`) both operate within a scope, excluding child component
|
|
144
|
+
scopes:
|
|
141
145
|
|
|
142
146
|
```html
|
|
143
147
|
<div bf-s="TodoApp_x1">
|
|
@@ -148,4 +152,16 @@ Marker-driven hydration attaches behavior to server-rendered HTML.
|
|
|
148
152
|
</div>
|
|
149
153
|
```
|
|
150
154
|
|
|
151
|
-
`$(__scope, 's0')` in TodoApp finds `<h1>`, not the `<span>` inside
|
|
155
|
+
`$(__scope, 's0')` in TodoApp finds `<h1>`, not the `<span>` inside
|
|
156
|
+
TodoItem. The `~` prefix marks a child scope excluded from parent
|
|
157
|
+
queries.
|
|
158
|
+
|
|
159
|
+
Dynamic *content* (reactive text and markup regions) is not looked up
|
|
160
|
+
per update. Instead the compiler emits a **claim plan** — a data
|
|
161
|
+
description of each slot (`{ id, kind, path }`) — and the runtime claims
|
|
162
|
+
the slot's DOM position **once**: `lazySlots()` scans for the slot's
|
|
163
|
+
`<!--bf:id-->…<!--/-->` marker pair on the first write (skipping child
|
|
164
|
+
scopes, same as `$()`), holds the reference, and every later write goes
|
|
165
|
+
through that held reference with no re-scanning. `claimSlots()` is the
|
|
166
|
+
eager variant used where content may be mutated before the first write
|
|
167
|
+
(streaming, portals).
|
|
@@ -63,7 +63,7 @@ export function Counter({ __instanceId, ... }) {
|
|
|
63
63
|
**Client script** — Wires up only the interactive parts:
|
|
64
64
|
|
|
65
65
|
```js
|
|
66
|
-
import { $,
|
|
66
|
+
import { $, createEffect, createSignal, escapeText, escapeTextOrNode, hydrate, lazySlots } from '@barefootjs/client/runtime'
|
|
67
67
|
|
|
68
68
|
export function initCounter(__scope, _p = {}) {
|
|
69
69
|
if (!__scope) return
|
|
@@ -71,11 +71,13 @@ export function initCounter(__scope, _p = {}) {
|
|
|
71
71
|
const [count, setCount] = createSignal(0)
|
|
72
72
|
|
|
73
73
|
const [_s1] = $(__scope, 's1') // find element with bf="s1"
|
|
74
|
-
const [_s0] = $t(__scope, 's0') // find text node at <!--bf:s0-->
|
|
75
74
|
|
|
75
|
+
// Claim the content slot between <!--bf:s0--> and <!--/--> on first
|
|
76
|
+
// write; later writes reuse the claimed reference (no re-scanning)
|
|
77
|
+
const __bfw_s0 = lazySlots(__scope, [{ id: 's0', kind: 'markup', path: [] }])
|
|
76
78
|
createEffect(() => {
|
|
77
79
|
const __val = count()
|
|
78
|
-
|
|
80
|
+
__bfw_s0('s0', escapeTextOrNode(__val))
|
|
79
81
|
})
|
|
80
82
|
|
|
81
83
|
if (_s1) _s1.addEventListener('click', () => { setCount(n => n + 1) })
|
|
@@ -83,6 +85,6 @@ export function initCounter(__scope, _p = {}) {
|
|
|
83
85
|
|
|
84
86
|
hydrate('Counter', {
|
|
85
87
|
init: initCounter,
|
|
86
|
-
template: (_p) => `<button bf="s1"> Count: <!--bf:s0-->${(0)}<!--/--></button>`
|
|
88
|
+
template: (_p) => `<button bf="s1"> Count: <!--bf:s0-->${escapeText((0))}<!--/--></button>`
|
|
87
89
|
})
|
|
88
90
|
```
|
|
@@ -32,23 +32,40 @@ See [JSX Compatibility — Limitations](./jsx-compatibility.md#limitations) for
|
|
|
32
32
|
|
|
33
33
|
## How It Works
|
|
34
34
|
|
|
35
|
-
The compiler skips template generation for the expression. The
|
|
35
|
+
The compiler skips template generation for the expression: the server can never evaluate it, so the SSR-rendered width is always zero. The client claims a slot for it and writes the real value once the browser evaluates the expression — the general case behind both compiled shapes below (`spec/slot-unification.md` §4).
|
|
36
36
|
|
|
37
|
-
**
|
|
37
|
+
**Claimed slot (the common case):** a marker pair is still emitted so the client has an anchor comment to claim against.
|
|
38
38
|
|
|
39
39
|
```html
|
|
40
|
-
<!--
|
|
40
|
+
<!-- server output -->
|
|
41
|
+
<!--bf:s0--><!--/--> items left
|
|
41
42
|
```
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
```js
|
|
45
|
+
// client JS
|
|
46
|
+
{ const __bfw_s0 = lazySlots(__scope, [{ id: 's0', kind: 'text', path: [] }])
|
|
47
|
+
createEffect(() => {
|
|
48
|
+
__bfw_s0('s0', todos().filter(t => !t.done).length)
|
|
49
|
+
}) }
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Markerless elision (Step B):** when the expression is the ONLY content of its own element — not adjacent to other text/expressions, and not inside a loop or conditional branch — the compiler proves a static child-index path to the slot's position and drops the marker pair entirely from both SSR and CSR output. `<strong>{/* @client */ todos().filter(t => !t.done).length}</strong>` from the [TodoApp example](https://github.com/piconic-ai/barefootjs/blob/main/integrations/shared/components/TodoApp.tsx) qualifies:
|
|
53
|
+
|
|
54
|
+
```html
|
|
55
|
+
<!-- server output -->
|
|
56
|
+
<strong bf="s1"></strong>
|
|
57
|
+
```
|
|
44
58
|
|
|
45
59
|
```js
|
|
46
|
-
//
|
|
60
|
+
// client JS
|
|
61
|
+
{ const __bfw_s0 = lazySlots(__scope, [{ id: 's0', kind: 'text', path: [0, 0], markerless: true }])
|
|
47
62
|
createEffect(() => {
|
|
48
|
-
|
|
49
|
-
})
|
|
63
|
+
__bfw_s0('s0', todos().filter(t => !t.done).length)
|
|
64
|
+
}) }
|
|
50
65
|
```
|
|
51
66
|
|
|
67
|
+
Either way, the claim happens lazily on the first write — nothing is touched until the effect actually runs — and every later write goes through the held reference, never re-scanning the DOM (`packages/client/src/runtime/claim-slots.ts`).
|
|
68
|
+
|
|
52
69
|
|
|
53
70
|
## Examples
|
|
54
71
|
|
package/dist/index.js
CHANGED
|
@@ -3926,6 +3926,10 @@ function irToHtmlTemplate(node, restSpreadNames, loopDepth = 0, loopParams, bran
|
|
|
3926
3926
|
return escapeHtml(node.value);
|
|
3927
3927
|
case "expression": {
|
|
3928
3928
|
if (node.expr === "null" || node.expr === "undefined") return "";
|
|
3929
|
+
if (node.markerless) {
|
|
3930
|
+
const bare = wrapInterpolation(wrapExpr(node.expr));
|
|
3931
|
+
return `\${${bare}}`;
|
|
3932
|
+
}
|
|
3929
3933
|
const inner = wrapInterpolation(wrapExpr(node.expr));
|
|
3930
3934
|
const valueExpr = node.joinArrayChild ? `Array.isArray(${inner}) ? ${inner}.join('') : (${inner} ?? '')` : inner;
|
|
3931
3935
|
if (node.slotId) {
|
|
@@ -4741,7 +4745,7 @@ function generateCsrTemplateWithOpts(node, opts) {
|
|
|
4741
4745
|
case "expression":
|
|
4742
4746
|
if (node.expr === "null" || node.expr === "undefined") return "";
|
|
4743
4747
|
if (node.clientOnly && node.slotId) {
|
|
4744
|
-
return `<!--bf
|
|
4748
|
+
return `<!--bf:${node.slotId}--><!--/-->`;
|
|
4745
4749
|
}
|
|
4746
4750
|
{
|
|
4747
4751
|
const transformed = transformExpr(node.expr, node.templateExpr);
|
|
@@ -14323,7 +14327,11 @@ function collectElements(node, ctx2, siblingOffsets, insideConditional = false)
|
|
|
14323
14327
|
},
|
|
14324
14328
|
expression: ({ node: ex, scope: inCond }) => {
|
|
14325
14329
|
if (ex.clientOnly && ex.slotId) {
|
|
14326
|
-
ctx2.clientOnlyElements.push({
|
|
14330
|
+
ctx2.clientOnlyElements.push({
|
|
14331
|
+
slotId: ex.slotId,
|
|
14332
|
+
expression: ex.expr,
|
|
14333
|
+
elidedPath: ex.markerless ? ex.elidedPath : void 0
|
|
14334
|
+
});
|
|
14327
14335
|
return;
|
|
14328
14336
|
}
|
|
14329
14337
|
if (!ex.slotId || inCond) return;
|
|
@@ -15327,13 +15335,11 @@ var init_imports = __esm({
|
|
|
15327
15335
|
"onMount",
|
|
15328
15336
|
"hydrate",
|
|
15329
15337
|
"insert",
|
|
15330
|
-
"reconcileElements",
|
|
15331
15338
|
"getLoopChildren",
|
|
15332
15339
|
"getLoopNodes",
|
|
15333
15340
|
"mapArray",
|
|
15334
15341
|
"mapArrayAnchored",
|
|
15335
15342
|
"patchLeaf",
|
|
15336
|
-
"patchSlotRange",
|
|
15337
15343
|
"createDisposableEffect",
|
|
15338
15344
|
"createComponent",
|
|
15339
15345
|
"renderChild",
|
|
@@ -15341,7 +15347,6 @@ var init_imports = __esm({
|
|
|
15341
15347
|
"registerTemplate",
|
|
15342
15348
|
"initChild",
|
|
15343
15349
|
"upsertChild",
|
|
15344
|
-
"updateClientMarker",
|
|
15345
15350
|
"createPortal",
|
|
15346
15351
|
"provideContext",
|
|
15347
15352
|
"createContext",
|
|
@@ -15353,6 +15358,7 @@ var init_imports = __esm({
|
|
|
15353
15358
|
"styleToCss",
|
|
15354
15359
|
"escapeAttr",
|
|
15355
15360
|
"escapeText",
|
|
15361
|
+
"escapeTextOrNode",
|
|
15356
15362
|
"qsa",
|
|
15357
15363
|
"qsaItem",
|
|
15358
15364
|
"qsaChildScope",
|
|
@@ -15361,7 +15367,11 @@ var init_imports = __esm({
|
|
|
15361
15367
|
"__slot",
|
|
15362
15368
|
"__bfSlot",
|
|
15363
15369
|
"__bfText",
|
|
15364
|
-
|
|
15370
|
+
// Claim-plan interpreter (slot unification A2/A3, spec/slot-unification.md)
|
|
15371
|
+
// — the "one claim mechanism" that replaced `patchSlotRange` and
|
|
15372
|
+
// `updateClientMarker` (both deleted) as the content-slot update door.
|
|
15373
|
+
"claimSlots",
|
|
15374
|
+
"lazySlots",
|
|
15365
15375
|
// Profile mode (#1690, SR3) — turn-boundary markers around event handlers.
|
|
15366
15376
|
"beginTurn",
|
|
15367
15377
|
"endTurn",
|
|
@@ -18617,6 +18627,25 @@ var init_build_insert = __esm({
|
|
|
18617
18627
|
}
|
|
18618
18628
|
});
|
|
18619
18629
|
|
|
18630
|
+
// ../jsx/src/ir-to-client-js/control-flow/stringify/claim-plan.ts
|
|
18631
|
+
function slotSpecLiteral(slot) {
|
|
18632
|
+
const pathSrc = slot.pathExpr ?? `[${slot.path.join(", ")}]`;
|
|
18633
|
+
const markerlessSrc = slot.markerless ? ", markerless: true" : "";
|
|
18634
|
+
return `{ id: '${slot.id}', kind: '${slot.kind}', path: ${pathSrc}${markerlessSrc} }`;
|
|
18635
|
+
}
|
|
18636
|
+
function claimPlanLiteral(slots) {
|
|
18637
|
+
return `[${slots.map(slotSpecLiteral).join(", ")}]`;
|
|
18638
|
+
}
|
|
18639
|
+
function claimWriterVarName(slots, sanitize) {
|
|
18640
|
+
const first = slots[0]?.id ?? "0";
|
|
18641
|
+
return `__bfw_${sanitize(first)}`;
|
|
18642
|
+
}
|
|
18643
|
+
var init_claim_plan = __esm({
|
|
18644
|
+
"../jsx/src/ir-to-client-js/control-flow/stringify/claim-plan.ts"() {
|
|
18645
|
+
"use strict";
|
|
18646
|
+
}
|
|
18647
|
+
});
|
|
18648
|
+
|
|
18620
18649
|
// ../jsx/src/ir-to-client-js/emit-reactive.ts
|
|
18621
18650
|
import ts14 from "typescript";
|
|
18622
18651
|
function bindingIdArg(ctx2, slotId) {
|
|
@@ -18795,17 +18824,18 @@ function emitDynamicTextUpdates(lines, ctx2) {
|
|
|
18795
18824
|
const conditionalElems = elems.filter((e) => e.insideConditional);
|
|
18796
18825
|
const normalElems = elems.filter((e) => !e.insideConditional);
|
|
18797
18826
|
if (normalElems.length > 0 || conditionalElems.length > 0) {
|
|
18798
|
-
for (const elem of normalElems) {
|
|
18799
|
-
const v = varSlotId(elem.slotId);
|
|
18800
|
-
lines.push(` let __anchor_${v} = _${v}`);
|
|
18801
|
-
}
|
|
18802
18827
|
const __textSlot = (normalElems[0] ?? conditionalElems[0])?.slotId;
|
|
18828
|
+
let writer = "";
|
|
18829
|
+
if (normalElems.length > 0) {
|
|
18830
|
+
const slots = normalElems.map((elem) => ({ id: elem.slotId, kind: "markup", path: [] }));
|
|
18831
|
+
writer = claimWriterVarName(slots, varSlotId);
|
|
18832
|
+
lines.push(` const ${writer} = lazySlots(__scope, ${claimPlanLiteral(slots)})`);
|
|
18833
|
+
}
|
|
18803
18834
|
lines.push(` createEffect(() => {`);
|
|
18804
18835
|
if (normalElems.length > 0) {
|
|
18805
18836
|
lines.push(` const __val = ${expr}`);
|
|
18806
18837
|
for (const elem of normalElems) {
|
|
18807
|
-
|
|
18808
|
-
lines.push(` __anchor_${v} = __bfText(__anchor_${v}, __val)`);
|
|
18838
|
+
lines.push(` ${writer}('${elem.slotId}', escapeTextOrNode(__val))`);
|
|
18809
18839
|
}
|
|
18810
18840
|
for (const elem of conditionalElems) {
|
|
18811
18841
|
const v = varSlotId(elem.slotId);
|
|
@@ -18828,10 +18858,13 @@ function emitDynamicTextUpdates(lines, ctx2) {
|
|
|
18828
18858
|
}
|
|
18829
18859
|
function emitClientOnlyExpressions(lines, ctx2) {
|
|
18830
18860
|
for (const elem of ctx2.clientOnlyElements) {
|
|
18861
|
+
const slots = elem.elidedPath ? [{ id: elem.slotId, kind: "text", path: elem.elidedPath, markerless: true }] : [{ id: elem.slotId, kind: "text", path: [] }];
|
|
18862
|
+
const writer = claimWriterVarName(slots, varSlotId);
|
|
18831
18863
|
lines.push(` // @client: ${elem.slotId}`);
|
|
18864
|
+
lines.push(` { const ${writer} = lazySlots(__scope, ${claimPlanLiteral(slots)})`);
|
|
18832
18865
|
lines.push(` createEffect(() => {`);
|
|
18833
|
-
lines.push(`
|
|
18834
|
-
lines.push(` }${bindingIdArg(ctx2, elem.slotId)})`);
|
|
18866
|
+
lines.push(` ${writer}('${elem.slotId}', ${elem.expression})`);
|
|
18867
|
+
lines.push(` }${bindingIdArg(ctx2, elem.slotId)}) }`);
|
|
18835
18868
|
lines.push("");
|
|
18836
18869
|
}
|
|
18837
18870
|
}
|
|
@@ -18939,6 +18972,7 @@ var init_emit_reactive = __esm({
|
|
|
18939
18972
|
"use strict";
|
|
18940
18973
|
init_html_constants();
|
|
18941
18974
|
init_utils();
|
|
18975
|
+
init_claim_plan();
|
|
18942
18976
|
init_html_template();
|
|
18943
18977
|
init_date_lowering();
|
|
18944
18978
|
init_to_locale_date_lowering();
|
|
@@ -19008,13 +19042,18 @@ function stringifyBranchInnerLoops(lines, plan, indent, pc) {
|
|
|
19008
19042
|
inner.outerLoopParamBindings
|
|
19009
19043
|
);
|
|
19010
19044
|
}
|
|
19011
|
-
|
|
19045
|
+
const conditionalTexts = inner.reactiveTexts.filter((t) => t.insideConditional);
|
|
19046
|
+
const plainTexts = inner.reactiveTexts.filter((t) => !t.insideConditional);
|
|
19047
|
+
for (const text of conditionalTexts) {
|
|
19012
19048
|
const bf = profileBindingId(pc, text.slotId);
|
|
19013
|
-
|
|
19014
|
-
|
|
19015
|
-
|
|
19016
|
-
|
|
19017
|
-
|
|
19049
|
+
lines.push(`${indent} createEffect(() => { claimSlots(__bel${uid}, [{ id: '${text.slotId}', kind: 'text', path: [] }]).write('${text.slotId}', String(${text.wrappedExpression})) }${bf})`);
|
|
19050
|
+
}
|
|
19051
|
+
if (plainTexts.length > 0) {
|
|
19052
|
+
const slots = plainTexts.map((t) => ({ id: t.slotId, kind: "text", path: [] }));
|
|
19053
|
+
const writer = claimWriterVarName(slots, varSlotId);
|
|
19054
|
+
lines.push(`${indent} const ${writer} = lazySlots(__bel${uid}, ${claimPlanLiteral(slots)})`);
|
|
19055
|
+
for (const text of plainTexts) {
|
|
19056
|
+
lines.push(`${indent} createEffect(() => { ${writer}('${text.slotId}', String(${text.wrappedExpression})) }${profileBindingId(pc, text.slotId)})`);
|
|
19018
19057
|
}
|
|
19019
19058
|
}
|
|
19020
19059
|
if (inner.nestedConditionals.length > 0) {
|
|
@@ -19056,10 +19095,13 @@ function stringifyLoopChildArm(lines, arm, armIndent, pc) {
|
|
|
19056
19095
|
stringifyLoopChildConditional(lines, cond, `${armIndent} `, pc);
|
|
19057
19096
|
lines.push(`${armIndent}}))`);
|
|
19058
19097
|
}
|
|
19059
|
-
|
|
19060
|
-
const
|
|
19061
|
-
|
|
19062
|
-
lines.push(`${armIndent}
|
|
19098
|
+
if (arm.texts.length > 0) {
|
|
19099
|
+
const slots = arm.texts.map((t) => ({ id: t.slotId, kind: "markup", path: [] }));
|
|
19100
|
+
const writer = claimWriterVarName(slots, varSlotId);
|
|
19101
|
+
lines.push(`${armIndent}const ${writer} = lazySlots(__branchScope, ${claimPlanLiteral(slots)})`);
|
|
19102
|
+
for (const text of arm.texts) {
|
|
19103
|
+
lines.push(`${armIndent}__disposers.push(createDisposableEffect(() => { ${writer}('${text.slotId}', escapeTextOrNode(${text.wrappedExpression})) }${profileBindingId(pc, text.slotId)}))`);
|
|
19104
|
+
}
|
|
19063
19105
|
}
|
|
19064
19106
|
lines.push(`${armIndent}return () => __disposers.forEach(d => d())`);
|
|
19065
19107
|
}
|
|
@@ -19072,19 +19114,45 @@ var init_loop_child_arm = __esm({
|
|
|
19072
19114
|
init_template_parse();
|
|
19073
19115
|
init_event_listener();
|
|
19074
19116
|
init_component_scope();
|
|
19117
|
+
init_claim_plan();
|
|
19075
19118
|
}
|
|
19076
19119
|
});
|
|
19077
19120
|
|
|
19078
19121
|
// ../jsx/src/ir-to-client-js/control-flow/stringify/reactive-effects.ts
|
|
19079
19122
|
function stringifyReactiveEffects(lines, plan, opts) {
|
|
19080
|
-
const { indent, elVar, bodyIsMultiRoot, elementIndexBySlot,
|
|
19123
|
+
const { indent, elVar, bodyIsMultiRoot, elementIndexBySlot, textClaimPathExprs, preambleRegions = [], mapPreambleWrapped } = opts;
|
|
19081
19124
|
const lookup = bodyIsMultiRoot ? "qsaItem" : "qsa";
|
|
19082
|
-
const pc = plan
|
|
19125
|
+
const pc = plan?.profileComponentName;
|
|
19083
19126
|
const bindingBfId = (slotId) => profileBindingId(pc, slotId);
|
|
19084
|
-
|
|
19127
|
+
const attrSlots = plan?.attrSlots ?? [];
|
|
19128
|
+
const outerTexts = plan?.outerTexts ?? [];
|
|
19129
|
+
const conditionals = plan?.conditionals ?? [];
|
|
19130
|
+
if (pc) {
|
|
19131
|
+
emitAttrSlotsGranular(lines, indent, elVar, lookup, attrSlots, elementIndexBySlot, bindingBfId);
|
|
19132
|
+
emitOuterTexts(lines, indent, elVar, outerTexts, bindingBfId, textClaimPathExprs);
|
|
19133
|
+
emitPreambleRegionsEffect(lines, indent, elVar, preambleRegions, mapPreambleWrapped);
|
|
19134
|
+
} else {
|
|
19135
|
+
emitConsolidatedRowEffect(
|
|
19136
|
+
lines,
|
|
19137
|
+
indent,
|
|
19138
|
+
elVar,
|
|
19139
|
+
lookup,
|
|
19140
|
+
attrSlots,
|
|
19141
|
+
outerTexts,
|
|
19142
|
+
elementIndexBySlot,
|
|
19143
|
+
textClaimPathExprs,
|
|
19144
|
+
preambleRegions,
|
|
19145
|
+
mapPreambleWrapped
|
|
19146
|
+
);
|
|
19147
|
+
}
|
|
19148
|
+
for (const cond of conditionals) {
|
|
19149
|
+
emitOuterConditional(lines, indent, elVar, cond, pc);
|
|
19150
|
+
}
|
|
19151
|
+
}
|
|
19152
|
+
function emitAttrSlotsGranular(lines, indent, elVar, lookup, attrSlots, elementIndexBySlot, bindingBfId) {
|
|
19153
|
+
for (const slot of attrSlots) {
|
|
19085
19154
|
const varName = `__ra_${varSlotId(slot.slotId)}`;
|
|
19086
|
-
const
|
|
19087
|
-
const lookupExpr = pIdx !== void 0 ? `__p ? __p[${pIdx}] : ${lookup}(${elVar}, '[bf="${slot.slotId}"]')` : `${lookup}(${elVar}, '[bf="${slot.slotId}"]')`;
|
|
19155
|
+
const lookupExpr = attrLookupExpr(slot.slotId, varName, elVar, lookup, elementIndexBySlot);
|
|
19088
19156
|
lines.push(`${indent}{ const ${varName} = ${lookupExpr}`);
|
|
19089
19157
|
lines.push(`${indent}if (${varName}) {`);
|
|
19090
19158
|
for (const attr of slot.attrs) {
|
|
@@ -19096,21 +19164,79 @@ function stringifyReactiveEffects(lines, plan, opts) {
|
|
|
19096
19164
|
}
|
|
19097
19165
|
lines.push(`${indent}} }`);
|
|
19098
19166
|
}
|
|
19099
|
-
|
|
19100
|
-
|
|
19167
|
+
}
|
|
19168
|
+
function emitPreambleRegionsEffect(lines, indent, elVar, preambleRegions, mapPreambleWrapped) {
|
|
19169
|
+
if (preambleRegions.length === 0) return;
|
|
19170
|
+
const slots = preambleRegions.map((r2) => ({ id: r2.slotId, kind: "markup", path: [] }));
|
|
19171
|
+
const writer = claimWriterVarName(slots, varSlotId);
|
|
19172
|
+
lines.push(`${indent}const ${writer} = lazySlots(${elVar}, ${claimPlanLiteral(slots)})`);
|
|
19173
|
+
lines.push(`${indent}createEffect(() => {`);
|
|
19174
|
+
if (mapPreambleWrapped) lines.push(`${indent} ${mapPreambleWrapped}`);
|
|
19175
|
+
for (const region of preambleRegions) {
|
|
19176
|
+
lines.push(`${indent} ${writer}('${region.slotId}', ${region.valueExpr})`);
|
|
19101
19177
|
}
|
|
19102
|
-
|
|
19103
|
-
|
|
19178
|
+
lines.push(`${indent}})`);
|
|
19179
|
+
}
|
|
19180
|
+
function attrLookupExpr(slotId, varName, elVar, lookup, elementIndexBySlot) {
|
|
19181
|
+
const pIdx = elementIndexBySlot?.get(slotId);
|
|
19182
|
+
return pIdx !== void 0 ? `__p ? __p[${pIdx}] : ${lookup}(${elVar}, '[bf="${slotId}"]')` : `${lookup}(${elVar}, '[bf="${slotId}"]')`;
|
|
19183
|
+
}
|
|
19184
|
+
function emitConsolidatedRowEffect(lines, indent, elVar, lookup, attrSlots, outerTexts, elementIndexBySlot, textClaimPathExprs, preambleRegions, mapPreambleWrapped) {
|
|
19185
|
+
if (attrSlots.length === 0 && outerTexts.length === 0 && preambleRegions.length === 0) return;
|
|
19186
|
+
for (const slot of attrSlots) {
|
|
19187
|
+
const varName = `__ra_${varSlotId(slot.slotId)}`;
|
|
19188
|
+
lines.push(`${indent}const ${varName} = ${attrLookupExpr(slot.slotId, varName, elVar, lookup, elementIndexBySlot)}`);
|
|
19189
|
+
}
|
|
19190
|
+
const claimSlots = [
|
|
19191
|
+
...outerTexts.map((t) => ({ id: t.slotId, kind: "text", path: [], pathExpr: textClaimPathExprs?.get(t.slotId) })),
|
|
19192
|
+
...preambleRegions.map((r2) => ({ id: r2.slotId, kind: "markup", path: [] }))
|
|
19193
|
+
];
|
|
19194
|
+
const writer = claimSlots.length > 0 ? claimWriterVarName(claimSlots, varSlotId) : null;
|
|
19195
|
+
if (writer) {
|
|
19196
|
+
lines.push(`${indent}const ${writer} = lazySlots(${elVar}, ${claimPlanLiteral(claimSlots)})`);
|
|
19197
|
+
}
|
|
19198
|
+
if (attrSlots.length === 0 && preambleRegions.length === 0 && outerTexts.length === 1) {
|
|
19199
|
+
const text = outerTexts[0];
|
|
19200
|
+
lines.push(`${indent}createEffect(() => { ${writer}('${text.slotId}', String(${text.wrappedExpression})) })`);
|
|
19201
|
+
return;
|
|
19202
|
+
}
|
|
19203
|
+
lines.push(`${indent}createEffect(() => {`);
|
|
19204
|
+
for (const slot of attrSlots) {
|
|
19205
|
+
const varName = `__ra_${varSlotId(slot.slotId)}`;
|
|
19206
|
+
lines.push(`${indent} if (${varName}) {`);
|
|
19207
|
+
for (const attr of slot.attrs) {
|
|
19208
|
+
lines.push(`${indent} {`);
|
|
19209
|
+
for (const stmt of emitAttrUpdate(varName, attr.attrName, attr.wrappedExpression, attr.meta)) {
|
|
19210
|
+
lines.push(`${indent} ${stmt}`);
|
|
19211
|
+
}
|
|
19212
|
+
lines.push(`${indent} }`);
|
|
19213
|
+
}
|
|
19214
|
+
lines.push(`${indent} }`);
|
|
19104
19215
|
}
|
|
19216
|
+
if (preambleRegions.length > 0 && mapPreambleWrapped) {
|
|
19217
|
+
lines.push(`${indent} ${mapPreambleWrapped}`);
|
|
19218
|
+
}
|
|
19219
|
+
for (const text of outerTexts) {
|
|
19220
|
+
lines.push(`${indent} ${writer}('${text.slotId}', String(${text.wrappedExpression}))`);
|
|
19221
|
+
}
|
|
19222
|
+
for (const region of preambleRegions) {
|
|
19223
|
+
lines.push(`${indent} ${writer}('${region.slotId}', ${region.valueExpr})`);
|
|
19224
|
+
}
|
|
19225
|
+
lines.push(`${indent}})`);
|
|
19105
19226
|
}
|
|
19106
|
-
function
|
|
19107
|
-
|
|
19108
|
-
|
|
19109
|
-
|
|
19110
|
-
|
|
19111
|
-
|
|
19227
|
+
function emitOuterTexts(lines, indent, elVar, texts, bindingBfId, textClaimPathExprs) {
|
|
19228
|
+
if (texts.length === 0) return;
|
|
19229
|
+
const slots = texts.map((t) => ({
|
|
19230
|
+
id: t.slotId,
|
|
19231
|
+
kind: "text",
|
|
19232
|
+
path: [],
|
|
19233
|
+
pathExpr: textClaimPathExprs?.get(t.slotId)
|
|
19234
|
+
}));
|
|
19235
|
+
const writer = claimWriterVarName(slots, varSlotId);
|
|
19236
|
+
lines.push(`${indent}const ${writer} = lazySlots(${elVar}, ${claimPlanLiteral(slots)})`);
|
|
19237
|
+
for (const text of texts) {
|
|
19238
|
+
lines.push(`${indent}createEffect(() => { ${writer}('${text.slotId}', String(${text.wrappedExpression})) }${bindingBfId(text.slotId)})`);
|
|
19112
19239
|
}
|
|
19113
|
-
lines.push(`${indent}if (${varName}) createEffect(() => { ${varName}.textContent = String(${text.wrappedExpression}) }${bfId}) }`);
|
|
19114
19240
|
}
|
|
19115
19241
|
function emitOuterConditional(lines, indent, elVar, cond, pc) {
|
|
19116
19242
|
const armIndent = `${indent} `;
|
|
@@ -19132,6 +19258,7 @@ var init_reactive_effects = __esm({
|
|
|
19132
19258
|
init_utils();
|
|
19133
19259
|
init_emit_reactive();
|
|
19134
19260
|
init_loop_child_arm();
|
|
19261
|
+
init_claim_plan();
|
|
19135
19262
|
}
|
|
19136
19263
|
});
|
|
19137
19264
|
|
|
@@ -19242,20 +19369,6 @@ function emitLoopChildRefs(lines, refs, opts) {
|
|
|
19242
19369
|
lines.push(`${indent}if (${varName}) ${emitRefCall(ref.callback, varName)} }`);
|
|
19243
19370
|
}
|
|
19244
19371
|
}
|
|
19245
|
-
function emitPreambleRegionEffects(lines, regions, mapPreambleWrapped, opts) {
|
|
19246
|
-
if (regions.length === 0) return;
|
|
19247
|
-
const { indent, elVar } = opts;
|
|
19248
|
-
for (const region of regions) {
|
|
19249
|
-
const v = varSlotId(region.slotId);
|
|
19250
|
-
lines.push(`${indent}{ let __last_${v}`);
|
|
19251
|
-
lines.push(`${indent}createEffect(() => {`);
|
|
19252
|
-
if (mapPreambleWrapped) lines.push(`${indent} ${mapPreambleWrapped}`);
|
|
19253
|
-
lines.push(`${indent} const __html_${v} = ${region.valueExpr}`);
|
|
19254
|
-
lines.push(`${indent} if (__last_${v} === undefined) { __last_${v} = __html_${v}; return }`);
|
|
19255
|
-
lines.push(`${indent} if (__html_${v} !== __last_${v}) { __last_${v} = __html_${v}; patchSlotRange(${elVar}, '${region.slotId}', __html_${v}) }`);
|
|
19256
|
-
lines.push(`${indent}}) }`);
|
|
19257
|
-
}
|
|
19258
|
-
}
|
|
19259
19372
|
function stringifyLoop(lines, plan) {
|
|
19260
19373
|
switch (plan.kind) {
|
|
19261
19374
|
case "static":
|
|
@@ -19354,26 +19467,33 @@ function stringifyPlainLoop(lines, plan, topIndent = " ") {
|
|
|
19354
19467
|
...reactiveEffects?.attrSlots.map((s) => s.slotId) ?? [],
|
|
19355
19468
|
...childRefs.map((r2) => r2.childSlotId)
|
|
19356
19469
|
];
|
|
19357
|
-
|
|
19358
|
-
|
|
19359
|
-
const built = buildSkeletonPathPlan(plan.skeletonPaths, "__el", { elementSlotIds, textSlotIds });
|
|
19470
|
+
if (elementSlotIds.length > 0) {
|
|
19471
|
+
const built = buildSkeletonPathPlan(plan.skeletonPaths, "__el", { elementSlotIds, textSlotIds: [] });
|
|
19360
19472
|
if (built.arrayElems.length > 0) {
|
|
19361
19473
|
pathPlan = built;
|
|
19362
19474
|
lines.push(`${bodyIndent}const __p = __existing ? null : [${built.arrayElems.join(", ")}]`);
|
|
19363
19475
|
}
|
|
19364
19476
|
}
|
|
19365
19477
|
}
|
|
19366
|
-
|
|
19478
|
+
const textClaimPathExprs = /* @__PURE__ */ new Map();
|
|
19479
|
+
if (hoistedTpl && plan.skeletonPaths) {
|
|
19480
|
+
for (const text of reactiveEffects?.outerTexts ?? []) {
|
|
19481
|
+
const path25 = plan.skeletonPaths.textMarkerPaths.get(text.slotId);
|
|
19482
|
+
if (path25) textClaimPathExprs.set(text.slotId, `__existing ? [] : [${path25.join(", ")}]`);
|
|
19483
|
+
}
|
|
19484
|
+
}
|
|
19485
|
+
if (reactiveEffects !== null || preambleRegions.length > 0) {
|
|
19367
19486
|
stringifyReactiveEffects(lines, reactiveEffects, {
|
|
19368
19487
|
indent: bodyIndent,
|
|
19369
19488
|
elVar: "__el",
|
|
19370
19489
|
bodyIsMultiRoot,
|
|
19371
19490
|
elementIndexBySlot: pathPlan?.elementIndexBySlot,
|
|
19372
|
-
|
|
19491
|
+
textClaimPathExprs,
|
|
19492
|
+
preambleRegions,
|
|
19493
|
+
mapPreambleWrapped
|
|
19373
19494
|
});
|
|
19374
19495
|
}
|
|
19375
19496
|
emitLoopChildRefs(lines, childRefs, { indent: bodyIndent, elVar: "__el", bodyIsMultiRoot, elementIndexBySlot: pathPlan?.elementIndexBySlot });
|
|
19376
|
-
emitPreambleRegionEffects(lines, preambleRegions, mapPreambleWrapped, { indent: bodyIndent, elVar: "__el" });
|
|
19377
19497
|
lines.push(`${bodyIndent}return __el`);
|
|
19378
19498
|
lines.push(`${topIndent}}, '${markerId}'${loopBfId})`);
|
|
19379
19499
|
}
|
|
@@ -19474,10 +19594,13 @@ function stringifyStaticLoop(lines, plan) {
|
|
|
19474
19594
|
}
|
|
19475
19595
|
lines.push(` }`);
|
|
19476
19596
|
}
|
|
19477
|
-
|
|
19478
|
-
const
|
|
19479
|
-
|
|
19480
|
-
lines.push(`
|
|
19597
|
+
if (texts.length > 0) {
|
|
19598
|
+
const slots = texts.map((t) => ({ id: t.slotId, kind: "text", path: [] }));
|
|
19599
|
+
const writer = claimWriterVarName(slots, varSlotId);
|
|
19600
|
+
lines.push(` const ${writer} = lazySlots(__iterEl, ${claimPlanLiteral(slots)})`);
|
|
19601
|
+
for (const text of texts) {
|
|
19602
|
+
lines.push(` createEffect(() => { ${writer}('${text.slotId}', String(${text.expression})) }${profileBindingId(pc, text.slotId)})`);
|
|
19603
|
+
}
|
|
19481
19604
|
}
|
|
19482
19605
|
emitLoopChildRefs(lines, childRefs, { indent: " ", elVar: "__iterEl", bodyIsMultiRoot: false });
|
|
19483
19606
|
lines.push(` }`);
|
|
@@ -19495,6 +19618,7 @@ var init_loop = __esm({
|
|
|
19495
19618
|
init_skeleton_paths();
|
|
19496
19619
|
init_component_loop();
|
|
19497
19620
|
init_composite_loop();
|
|
19621
|
+
init_claim_plan();
|
|
19498
19622
|
}
|
|
19499
19623
|
});
|
|
19500
19624
|
|
|
@@ -19550,13 +19674,18 @@ function emitReactive(lines, inner, indent, pc) {
|
|
|
19550
19674
|
if (inner.childLevels.length > 0) {
|
|
19551
19675
|
stringifyInnerLoops(lines, inner.childLevels, `${indent} `, pc);
|
|
19552
19676
|
}
|
|
19553
|
-
|
|
19677
|
+
const conditionalTexts = emit.reactiveTexts.filter((t) => t.insideConditional);
|
|
19678
|
+
const plainTexts = emit.reactiveTexts.filter((t) => !t.insideConditional);
|
|
19679
|
+
for (const text of conditionalTexts) {
|
|
19554
19680
|
const bf = profileBindingId(pc, text.slotId);
|
|
19555
|
-
|
|
19556
|
-
|
|
19557
|
-
|
|
19558
|
-
|
|
19559
|
-
|
|
19681
|
+
lines.push(`${indent} createEffect(() => { claimSlots(__innerEl${uid}, [{ id: '${text.slotId}', kind: 'text', path: [] }]).write('${text.slotId}', String(${text.wrappedExpression})) }${bf})`);
|
|
19682
|
+
}
|
|
19683
|
+
if (plainTexts.length > 0) {
|
|
19684
|
+
const slots = plainTexts.map((t) => ({ id: t.slotId, kind: "text", path: [] }));
|
|
19685
|
+
const writer = claimWriterVarName(slots, varSlotId);
|
|
19686
|
+
lines.push(`${indent} const ${writer} = lazySlots(__innerEl${uid}, ${claimPlanLiteral(slots)})`);
|
|
19687
|
+
for (const text of plainTexts) {
|
|
19688
|
+
lines.push(`${indent} createEffect(() => { ${writer}('${text.slotId}', String(${text.wrappedExpression})) }${profileBindingId(pc, text.slotId)})`);
|
|
19560
19689
|
}
|
|
19561
19690
|
}
|
|
19562
19691
|
for (const attr of emit.reactiveAttrs) {
|
|
@@ -19618,6 +19747,7 @@ var init_inner_loop = __esm({
|
|
|
19618
19747
|
init_emit_reactive();
|
|
19619
19748
|
init_template_parse();
|
|
19620
19749
|
init_loop();
|
|
19750
|
+
init_claim_plan();
|
|
19621
19751
|
}
|
|
19622
19752
|
});
|
|
19623
19753
|
|
|
@@ -19943,11 +20073,16 @@ function emitPlain(lines, plan) {
|
|
|
19943
20073
|
indent: " ",
|
|
19944
20074
|
singleRootLayout: "inline"
|
|
19945
20075
|
});
|
|
19946
|
-
if (reactiveEffects !== null) {
|
|
19947
|
-
stringifyReactiveEffects(lines, reactiveEffects, {
|
|
20076
|
+
if (reactiveEffects !== null || preambleRegions.length > 0) {
|
|
20077
|
+
stringifyReactiveEffects(lines, reactiveEffects, {
|
|
20078
|
+
indent: " ",
|
|
20079
|
+
elVar: "__el",
|
|
20080
|
+
bodyIsMultiRoot,
|
|
20081
|
+
preambleRegions,
|
|
20082
|
+
mapPreambleWrapped
|
|
20083
|
+
});
|
|
19948
20084
|
}
|
|
19949
20085
|
emitLoopChildRefs(lines, childRefs, { indent: " ", elVar: "__el", bodyIsMultiRoot });
|
|
19950
|
-
emitPreambleRegionEffects(lines, preambleRegions, mapPreambleWrapped, { indent: " ", elVar: "__el" });
|
|
19951
20086
|
lines.push(` return __el`);
|
|
19952
20087
|
lines.push(` }, '${markerId}'${loopBfId})`);
|
|
19953
20088
|
}
|
|
@@ -20038,13 +20173,13 @@ function emitArmBody(lines, body2, mode2, indent, profileComponentName) {
|
|
|
20038
20173
|
}
|
|
20039
20174
|
lines.push(`${indent}} }`);
|
|
20040
20175
|
}
|
|
20041
|
-
|
|
20042
|
-
const
|
|
20043
|
-
|
|
20044
|
-
lines.push(`${indent}
|
|
20045
|
-
|
|
20046
|
-
|
|
20047
|
-
|
|
20176
|
+
if (body2.textEffects.length > 0) {
|
|
20177
|
+
const slots = body2.textEffects.map((te) => ({ id: te.slotId, kind: "markup", path: [] }));
|
|
20178
|
+
const writer = claimWriterVarName(slots, varSlotId);
|
|
20179
|
+
lines.push(`${indent}const ${writer} = lazySlots(__branchScope, ${claimPlanLiteral(slots)})`);
|
|
20180
|
+
for (const te of body2.textEffects) {
|
|
20181
|
+
lines.push(`${indent}__disposers.push(createDisposableEffect(() => { ${writer}('${te.slotId}', escapeTextOrNode(${te.expression})) }${bindingBfId(te.slotId)}))`);
|
|
20182
|
+
}
|
|
20048
20183
|
}
|
|
20049
20184
|
if (body2.loops.length > 0) {
|
|
20050
20185
|
stringifyBranchLoops(lines, body2.loops);
|
|
@@ -20077,6 +20212,7 @@ var init_insert = __esm({
|
|
|
20077
20212
|
init_branch_loop();
|
|
20078
20213
|
init_event_listener();
|
|
20079
20214
|
init_component_scope();
|
|
20215
|
+
init_claim_plan();
|
|
20080
20216
|
}
|
|
20081
20217
|
});
|
|
20082
20218
|
|
|
@@ -20340,7 +20476,6 @@ var init_control_flow = __esm({
|
|
|
20340
20476
|
// ../jsx/src/ir-to-client-js/element-refs.ts
|
|
20341
20477
|
function generateElementRefs(ctx2) {
|
|
20342
20478
|
const regularSlots = /* @__PURE__ */ new Set();
|
|
20343
|
-
const textSlots = /* @__PURE__ */ new Set();
|
|
20344
20479
|
const componentSlots = /* @__PURE__ */ new Set();
|
|
20345
20480
|
const conditionalSlotIds = collectConditionalSlotIds(ctx2);
|
|
20346
20481
|
for (const elem of ctx2.interactiveElements) {
|
|
@@ -20348,11 +20483,6 @@ function generateElementRefs(ctx2) {
|
|
|
20348
20483
|
regularSlots.add(elem.slotId);
|
|
20349
20484
|
}
|
|
20350
20485
|
}
|
|
20351
|
-
for (const elem of ctx2.dynamicElements) {
|
|
20352
|
-
if (!elem.insideConditional) {
|
|
20353
|
-
textSlots.add(elem.slotId);
|
|
20354
|
-
}
|
|
20355
|
-
}
|
|
20356
20486
|
for (const elem of ctx2.conditionalElements) {
|
|
20357
20487
|
regularSlots.add(elem.slotId);
|
|
20358
20488
|
}
|
|
@@ -20381,10 +20511,9 @@ function generateElementRefs(ctx2) {
|
|
|
20381
20511
|
for (const slotId of componentSlots) {
|
|
20382
20512
|
regularSlots.delete(slotId);
|
|
20383
20513
|
}
|
|
20384
|
-
if (regularSlots.size === 0 &&
|
|
20514
|
+
if (regularSlots.size === 0 && componentSlots.size === 0) return "";
|
|
20385
20515
|
const refLines = [];
|
|
20386
20516
|
emitSlotRefs(refLines, [...regularSlots], "$");
|
|
20387
|
-
emitSlotRefs(refLines, [...textSlots], "$t");
|
|
20388
20517
|
emitSlotRefs(refLines, [...componentSlots], "$c");
|
|
20389
20518
|
return refLines.join("\n");
|
|
20390
20519
|
}
|
|
@@ -21034,6 +21163,107 @@ var init_ir_to_client_js = __esm({
|
|
|
21034
21163
|
}
|
|
21035
21164
|
});
|
|
21036
21165
|
|
|
21166
|
+
// ../jsx/src/ir-to-client-js/client-only-elision.ts
|
|
21167
|
+
function decideClientOnlyElision(root2) {
|
|
21168
|
+
walkNode(root2, [], /* @__PURE__ */ new Set(), { bailed: false });
|
|
21169
|
+
}
|
|
21170
|
+
function walkNode(node, path25, forceCloseAncestors, state2) {
|
|
21171
|
+
if (state2.bailed) return;
|
|
21172
|
+
if (node.type === "element") {
|
|
21173
|
+
if (!elementIsPathSafe(node.tag, flattenSkeletonChildren(node.children))) {
|
|
21174
|
+
state2.bailed = true;
|
|
21175
|
+
return;
|
|
21176
|
+
}
|
|
21177
|
+
const groupIdx = skeletonForceCloseGroup(node.tag);
|
|
21178
|
+
if (groupIdx >= 0 && forceCloseAncestors.has(groupIdx)) {
|
|
21179
|
+
state2.bailed = true;
|
|
21180
|
+
return;
|
|
21181
|
+
}
|
|
21182
|
+
const nextAncestors = groupIdx >= 0 ? /* @__PURE__ */ new Set([...forceCloseAncestors, groupIdx]) : forceCloseAncestors;
|
|
21183
|
+
walkChildren(flattenSkeletonChildren(node.children), path25, nextAncestors, state2);
|
|
21184
|
+
} else if (node.type === "fragment") {
|
|
21185
|
+
walkChildren(flattenSkeletonChildren(node.children), path25, forceCloseAncestors, state2);
|
|
21186
|
+
}
|
|
21187
|
+
}
|
|
21188
|
+
function walkChildren(children2, parentPath, forceCloseAncestors, state2) {
|
|
21189
|
+
let frozen = false;
|
|
21190
|
+
let idx = 0;
|
|
21191
|
+
let pendingText = false;
|
|
21192
|
+
for (let i = 0; i < children2.length; i++) {
|
|
21193
|
+
if (state2.bailed) return;
|
|
21194
|
+
const child = children2[i];
|
|
21195
|
+
switch (child.type) {
|
|
21196
|
+
case "text": {
|
|
21197
|
+
if (child.value === "") continue;
|
|
21198
|
+
if (!pendingText) idx += 1;
|
|
21199
|
+
pendingText = true;
|
|
21200
|
+
continue;
|
|
21201
|
+
}
|
|
21202
|
+
case "expression": {
|
|
21203
|
+
if (child.expr === "null" || child.expr === "undefined") continue;
|
|
21204
|
+
if (child.clientOnly && child.slotId) {
|
|
21205
|
+
const adjacent = isTextLike(children2[i - 1]) || isTextLike(children2[i + 1]);
|
|
21206
|
+
if (!frozen && !adjacent) {
|
|
21207
|
+
markElided(child, [...parentPath, idx]);
|
|
21208
|
+
frozen = true;
|
|
21209
|
+
} else {
|
|
21210
|
+
frozen = true;
|
|
21211
|
+
}
|
|
21212
|
+
idx += 1;
|
|
21213
|
+
pendingText = false;
|
|
21214
|
+
continue;
|
|
21215
|
+
}
|
|
21216
|
+
frozen = true;
|
|
21217
|
+
idx += 1;
|
|
21218
|
+
pendingText = false;
|
|
21219
|
+
continue;
|
|
21220
|
+
}
|
|
21221
|
+
case "element": {
|
|
21222
|
+
if (!elementIsPathSafe(child.tag, flattenSkeletonChildren(child.children))) {
|
|
21223
|
+
state2.bailed = true;
|
|
21224
|
+
return;
|
|
21225
|
+
}
|
|
21226
|
+
if (!frozen) {
|
|
21227
|
+
walkNode(child, [...parentPath, idx], forceCloseAncestors, state2);
|
|
21228
|
+
}
|
|
21229
|
+
idx += 1;
|
|
21230
|
+
pendingText = false;
|
|
21231
|
+
continue;
|
|
21232
|
+
}
|
|
21233
|
+
case "fragment":
|
|
21234
|
+
continue;
|
|
21235
|
+
// already flattened
|
|
21236
|
+
default:
|
|
21237
|
+
frozen = true;
|
|
21238
|
+
idx += 1;
|
|
21239
|
+
pendingText = false;
|
|
21240
|
+
continue;
|
|
21241
|
+
}
|
|
21242
|
+
}
|
|
21243
|
+
}
|
|
21244
|
+
function isTextLike(node) {
|
|
21245
|
+
if (!node) return false;
|
|
21246
|
+
if (node.type === "text") return node.value !== "";
|
|
21247
|
+
if (node.type === "expression") return node.expr !== "null" && node.expr !== "undefined";
|
|
21248
|
+
return false;
|
|
21249
|
+
}
|
|
21250
|
+
function markElided(expr, path25) {
|
|
21251
|
+
expr.markerless = true;
|
|
21252
|
+
expr.elidedPath = path25;
|
|
21253
|
+
}
|
|
21254
|
+
function elementIsPathSafe(tag, flatChildren) {
|
|
21255
|
+
if (SKELETON_PATH_HAZARD_TAGS.has(tag)) return false;
|
|
21256
|
+
if (VOID_ELEMENTS.has(tag) && flatChildren.length > 0) return false;
|
|
21257
|
+
if (tag === "tr" && hasForeignTableRowContent(flatChildren)) return false;
|
|
21258
|
+
return true;
|
|
21259
|
+
}
|
|
21260
|
+
var init_client_only_elision = __esm({
|
|
21261
|
+
"../jsx/src/ir-to-client-js/client-only-elision.ts"() {
|
|
21262
|
+
"use strict";
|
|
21263
|
+
init_html_template();
|
|
21264
|
+
}
|
|
21265
|
+
});
|
|
21266
|
+
|
|
21037
21267
|
// ../jsx/src/css-layer-prefixer.ts
|
|
21038
21268
|
function prefixClass(cls, layerName) {
|
|
21039
21269
|
if (!cls || cls.startsWith("layer-")) return cls;
|
|
@@ -22208,7 +22438,7 @@ function checkRichTypeMethodCalls(root2, metadata, errors) {
|
|
|
22208
22438
|
if (!metadata.propsType) return;
|
|
22209
22439
|
const matchers = prepareLoweringMatchers(metadata);
|
|
22210
22440
|
const seen = /* @__PURE__ */ new Set();
|
|
22211
|
-
|
|
22441
|
+
walkNode2(root2, metadata, EMPTY_BINDINGS2, matchers, errors, seen);
|
|
22212
22442
|
}
|
|
22213
22443
|
function isLoweringClaimed(matchers, callee, args2) {
|
|
22214
22444
|
return matchers.some((m) => m(callee, args2) !== null);
|
|
@@ -22336,7 +22566,7 @@ function walkAttrValue(value2, clientOnly, loc, meta, bindings, matchers, errors
|
|
|
22336
22566
|
walkTemplateParts(value2.parts, loc, meta, bindings, matchers, errors, seen);
|
|
22337
22567
|
}
|
|
22338
22568
|
}
|
|
22339
|
-
function
|
|
22569
|
+
function walkNode2(node, meta, bindings, matchers, errors, seen) {
|
|
22340
22570
|
if (node.type === "expression") {
|
|
22341
22571
|
if (!node.clientOnly && node.parsed) checkExpr(node.parsed, node.loc, meta, bindings, matchers, errors, seen);
|
|
22342
22572
|
} else if (node.type === "conditional") {
|
|
@@ -22356,11 +22586,11 @@ function walkNode(node, meta, bindings, matchers, errors, seen) {
|
|
|
22356
22586
|
case "component":
|
|
22357
22587
|
case "fragment":
|
|
22358
22588
|
case "provider":
|
|
22359
|
-
for (const child of node.children)
|
|
22589
|
+
for (const child of node.children) walkNode2(child, meta, bindings, matchers, errors, seen);
|
|
22360
22590
|
break;
|
|
22361
22591
|
case "async":
|
|
22362
|
-
|
|
22363
|
-
for (const child of node.children)
|
|
22592
|
+
walkNode2(node.fallback, meta, bindings, matchers, errors, seen);
|
|
22593
|
+
for (const child of node.children) walkNode2(child, meta, bindings, matchers, errors, seen);
|
|
22364
22594
|
break;
|
|
22365
22595
|
case "loop": {
|
|
22366
22596
|
if (node.clientOnly) break;
|
|
@@ -22369,29 +22599,29 @@ function walkNode(node, meta, bindings, matchers, errors, seen) {
|
|
|
22369
22599
|
const arrayType = node.arrayParsed ? resolveReceiverType(node.arrayParsed, meta, bindings) : null;
|
|
22370
22600
|
loopBindings.set(node.param, arrayType?.kind === "array" ? arrayType.elementType ?? null : null);
|
|
22371
22601
|
if (node.index) loopBindings.set(node.index, null);
|
|
22372
|
-
for (const child of node.children)
|
|
22602
|
+
for (const child of node.children) walkNode2(child, meta, loopBindings, matchers, errors, seen);
|
|
22373
22603
|
if (node.childComponent) {
|
|
22374
|
-
for (const child of node.childComponent.children)
|
|
22604
|
+
for (const child of node.childComponent.children) walkNode2(child, meta, loopBindings, matchers, errors, seen);
|
|
22375
22605
|
}
|
|
22376
22606
|
for (const nested of node.nestedComponents ?? []) {
|
|
22377
|
-
for (const child of nested.children)
|
|
22607
|
+
for (const child of nested.children) walkNode2(child, meta, loopBindings, matchers, errors, seen);
|
|
22378
22608
|
}
|
|
22379
22609
|
for (const seg of node.flatMapCallback?.segments ?? []) {
|
|
22380
|
-
if (seg.kind === "jsx")
|
|
22610
|
+
if (seg.kind === "jsx") walkNode2(seg.ir, meta, loopBindings, matchers, errors, seen);
|
|
22381
22611
|
}
|
|
22382
22612
|
for (const seg of node.preamble?.segments ?? []) {
|
|
22383
|
-
if (seg.kind === "jsx")
|
|
22613
|
+
if (seg.kind === "jsx") walkNode2(seg.ir, meta, loopBindings, matchers, errors, seen);
|
|
22384
22614
|
}
|
|
22385
22615
|
break;
|
|
22386
22616
|
}
|
|
22387
22617
|
case "conditional":
|
|
22388
22618
|
if (node.clientOnly) break;
|
|
22389
|
-
|
|
22390
|
-
|
|
22619
|
+
walkNode2(node.whenTrue, meta, bindings, matchers, errors, seen);
|
|
22620
|
+
walkNode2(node.whenFalse, meta, bindings, matchers, errors, seen);
|
|
22391
22621
|
break;
|
|
22392
22622
|
case "if-statement":
|
|
22393
|
-
|
|
22394
|
-
if (node.alternate)
|
|
22623
|
+
walkNode2(node.consequent, meta, bindings, matchers, errors, seen);
|
|
22624
|
+
if (node.alternate) walkNode2(node.alternate, meta, bindings, matchers, errors, seen);
|
|
22395
22625
|
break;
|
|
22396
22626
|
}
|
|
22397
22627
|
}
|
|
@@ -22464,6 +22694,7 @@ function compileMultipleComponents(source, filePath, componentNames, options2) {
|
|
|
22464
22694
|
};
|
|
22465
22695
|
componentIR.metadata.clientAnalysis = analyzeClientNeeds(componentIR);
|
|
22466
22696
|
checkRichTypeMethodCalls(componentIR.root, componentIR.metadata, errors);
|
|
22697
|
+
decideClientOnlyElision(componentIR.root);
|
|
22467
22698
|
if (options2.cssLayerPrefix) {
|
|
22468
22699
|
applyCssLayerPrefix(componentIR, options2.cssLayerPrefix);
|
|
22469
22700
|
}
|
|
@@ -22801,6 +23032,7 @@ function compileJSX(source, filePath, options2) {
|
|
|
22801
23032
|
};
|
|
22802
23033
|
componentIR.metadata.clientAnalysis = analyzeClientNeeds(componentIR);
|
|
22803
23034
|
checkRichTypeMethodCalls(componentIR.root, componentIR.metadata, errors);
|
|
23035
|
+
decideClientOnlyElision(componentIR.root);
|
|
22804
23036
|
if (ctx2.importedClientSignalNames.size > 0) {
|
|
22805
23037
|
const sources = /* @__PURE__ */ new Set();
|
|
22806
23038
|
for (const imp of ctx2.imports) {
|
|
@@ -22922,6 +23154,7 @@ var init_compiler = __esm({
|
|
|
22922
23154
|
init_jsx_to_ir();
|
|
22923
23155
|
init_builtins();
|
|
22924
23156
|
init_ir_to_client_js();
|
|
23157
|
+
init_client_only_elision();
|
|
22925
23158
|
init_emit_module_level();
|
|
22926
23159
|
init_imports();
|
|
22927
23160
|
init_component_scope();
|
|
@@ -26867,6 +27100,7 @@ __export(src_exports, {
|
|
|
26867
27100
|
createProgramForFile: () => createProgramForFile,
|
|
26868
27101
|
dangerousInnerHtmlDiagnostic: () => dangerousInnerHtmlDiagnostic,
|
|
26869
27102
|
dangerousInnerHtmlMetacharViolation: () => dangerousInnerHtmlMetacharViolation,
|
|
27103
|
+
decideClientOnlyElision: () => decideClientOnlyElision,
|
|
26870
27104
|
describeFallback: () => describeFallback,
|
|
26871
27105
|
diffProfiles: () => diffProfiles,
|
|
26872
27106
|
diffStaticBudget: () => diffStaticBudget,
|
|
@@ -26981,6 +27215,7 @@ var init_src2 = __esm({
|
|
|
26981
27215
|
init_analyzer();
|
|
26982
27216
|
init_shared_program();
|
|
26983
27217
|
init_jsx_to_ir();
|
|
27218
|
+
init_client_only_elision();
|
|
26984
27219
|
init_module_exports();
|
|
26985
27220
|
init_interface();
|
|
26986
27221
|
init_test_adapter();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "CLI for agent-driven UI component discovery and scaffolding",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -29,11 +29,11 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"esbuild": "^0.25.0",
|
|
31
31
|
"typescript": "^5.0.0",
|
|
32
|
-
"@barefootjs/client": "0.
|
|
33
|
-
"@barefootjs/shared": "0.
|
|
32
|
+
"@barefootjs/client": "0.27.0",
|
|
33
|
+
"@barefootjs/shared": "0.27.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@barefootjs/jsx": "0.
|
|
36
|
+
"@barefootjs/jsx": "0.27.0",
|
|
37
37
|
"@types/node": "^22.0.0",
|
|
38
38
|
"@happy-dom/global-registrator": "^20.0.11",
|
|
39
39
|
"happy-dom": "^20.0.11"
|