@abide/abide 0.43.1 → 0.44.1
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/AGENTS.md +293 -219
- package/CHANGELOG.md +63 -0
- package/README.md +191 -67
- package/package.json +2 -2
- package/src/abideLsp.ts +8 -4
- package/src/abideResolverPlugin.ts +17 -2
- package/src/build.ts +9 -0
- package/src/buildDisconnected.ts +2 -0
- package/src/devEntry.ts +44 -9
- package/src/lib/bundle/probeAbideServer.ts +1 -1
- package/src/lib/cli/parseArgvForRpc.ts +25 -26
- package/src/lib/cli/runCli.ts +9 -24
- package/src/lib/cli/tokenizeArgvFlags.ts +84 -0
- package/src/lib/cli/types/CliManifest.ts +6 -4
- package/src/lib/mcp/annotationsForMethod.ts +1 -1
- package/src/lib/mcp/toolResultFromResponse.ts +1 -1
- package/src/lib/mcp/types/JsonRpcRequest.ts +3 -3
- package/src/lib/server/AppModule.ts +1 -1
- package/src/lib/server/cli/handleCliDownload.ts +7 -5
- package/src/lib/server/cli/installScript.ts +3 -3
- package/src/lib/server/prompts/promptRegistry.ts +1 -1
- package/src/lib/server/prompts/types/Prompt.ts +2 -1
- package/src/lib/server/rpc/defineRpc.ts +9 -1
- package/src/lib/server/rpc/dispatchRpcInProcess.ts +2 -2
- package/src/lib/server/rpc/types/RpcHelper.ts +1 -1
- package/src/lib/server/runtime/createPublicAssetServer.ts +2 -2
- package/src/lib/server/runtime/createRouteDispatcher.ts +1 -1
- package/src/lib/server/runtime/createServer.ts +50 -12
- package/src/lib/server/runtime/createUiPageRenderer.ts +7 -11
- package/src/lib/server/runtime/crossOriginForbidden.ts +2 -2
- package/src/lib/server/runtime/crossOriginGate.ts +1 -1
- package/src/lib/server/runtime/devHotModuleResponse.ts +3 -3
- package/src/lib/server/runtime/disableIdleTimeoutForStream.ts +1 -1
- package/src/lib/server/runtime/gzipResponse.ts +14 -16
- package/src/lib/server/runtime/logExposedSurfaces.ts +3 -4
- package/src/lib/server/runtime/runWithRequestScope.ts +4 -1
- package/src/lib/server/runtime/serializeCacheSnapshot.ts +3 -8
- package/src/lib/server/runtime/snapshotEntryFromCache.ts +10 -7
- package/src/lib/server/runtime/streamFromIterator.ts +2 -2
- package/src/lib/server/runtime/types/InspectorRpc.ts +2 -2
- package/src/lib/server/runtime/types/RequestStore.ts +16 -0
- package/src/lib/server/runtime/withResponseDefaults.ts +8 -3
- package/src/lib/server/socket.ts +1 -1
- package/src/lib/server/sockets/createSocketDispatcher.ts +2 -2
- package/src/lib/server/sockets/defineSocket.ts +5 -1
- package/src/lib/server/sockets/socketRegistry.ts +2 -2
- package/src/lib/server/sockets/types/SocketClientFrame.ts +3 -3
- package/src/lib/shared/CACHE_WRAPPED.ts +3 -2
- package/src/lib/shared/REMOTE_FUNCTION.ts +1 -1
- package/src/lib/shared/RPC_ARGS_TYPE.ts +7 -0
- package/src/lib/shared/STREAMING_CONTENT_TYPES.ts +3 -2
- package/src/lib/shared/activeCacheStore.ts +5 -14
- package/src/lib/shared/activePage.ts +5 -19
- package/src/lib/shared/augmentModule.ts +18 -0
- package/src/lib/shared/basePath.ts +6 -5
- package/src/lib/shared/baseResolver.ts +10 -0
- package/src/lib/shared/baseSlot.ts +6 -12
- package/src/lib/shared/bodyValueForKind.ts +35 -0
- package/src/lib/shared/cache.ts +22 -2
- package/src/lib/shared/cacheEntryFromSnapshot.ts +23 -18
- package/src/lib/shared/cacheStoreResolver.ts +12 -0
- package/src/lib/shared/cacheStoreSlot.ts +5 -13
- package/src/lib/shared/changeAffectsClient.ts +35 -0
- package/src/lib/shared/createPushIterator.ts +1 -1
- package/src/lib/shared/createResolverSlot.ts +37 -0
- package/src/lib/shared/debugGate.ts +90 -0
- package/src/lib/shared/decodeResponse.ts +11 -8
- package/src/lib/shared/emitLogRecord.ts +18 -4
- package/src/lib/shared/globalCacheStoreResolver.ts +12 -0
- package/src/lib/shared/globalCacheStoreSlot.ts +6 -11
- package/src/lib/shared/hasReplayableRequest.ts +17 -0
- package/src/lib/shared/html.ts +7 -26
- package/src/lib/shared/isDebugEnabled.ts +3 -10
- package/src/lib/shared/isDebugNegated.ts +4 -6
- package/src/lib/shared/manifestModule.ts +2 -2
- package/src/lib/shared/online.ts +1 -1
- package/src/lib/shared/outboxProbeSlot.ts +1 -1
- package/src/lib/shared/pageResolver.ts +16 -0
- package/src/lib/shared/pageSlot.ts +5 -14
- package/src/lib/shared/pageUrlForFile.ts +1 -1
- package/src/lib/shared/requestScopeResolver.ts +12 -0
- package/src/lib/shared/requestScopeSlot.ts +7 -12
- package/src/lib/shared/responseBodyKind.ts +35 -0
- package/src/lib/shared/routeParamsShape.ts +20 -0
- package/src/lib/shared/setBaseResolver.ts +2 -4
- package/src/lib/shared/setCacheStoreResolver.ts +3 -5
- package/src/lib/shared/setGlobalCacheStoreResolver.ts +3 -5
- package/src/lib/shared/setPageResolver.ts +2 -5
- package/src/lib/shared/setRequestScopeResolver.ts +3 -5
- package/src/lib/shared/snapshotShippable.ts +22 -0
- package/src/lib/shared/types/CacheEntry.ts +1 -1
- package/src/lib/shared/types/CacheInvalidation.ts +3 -3
- package/src/lib/shared/types/ClientFlags.ts +5 -3
- package/src/lib/shared/types/DebugGate.ts +10 -0
- package/src/lib/shared/types/LastConnection.ts +1 -1
- package/src/lib/shared/types/ResolverSlot.ts +10 -0
- package/src/lib/shared/url.ts +4 -1
- package/src/lib/shared/withBase.ts +1 -2
- package/src/lib/shared/writeDts.ts +10 -3
- package/src/lib/shared/writePublicAssetsDts.ts +5 -9
- package/src/lib/shared/writeRoutesDts.ts +14 -46
- package/src/lib/shared/writeRpcDts.ts +13 -12
- package/src/lib/shared/writeTestRpcDts.ts +11 -11
- package/src/lib/shared/writeTestSocketsDts.ts +9 -9
- package/src/lib/test/createTestSocketChannel.ts +3 -3
- package/src/lib/ui/COMPONENT_WRAPPER_PREFIX.ts +1 -1
- package/src/lib/ui/compile/ABIDE_SEMANTIC_TOKENS_LEGEND.ts +40 -6
- package/src/lib/ui/compile/REACTIVE_CALLEES.ts +1 -1
- package/src/lib/ui/compile/UI_RUNTIME_IMPORTS.ts +49 -37
- package/src/lib/ui/compile/abideUiPlugin.ts +10 -8
- package/src/lib/ui/compile/assertRuntimeHelpersBound.ts +27 -13
- package/src/lib/ui/compile/awaitPlan.ts +14 -2
- package/src/lib/ui/compile/catchBinding.ts +8 -0
- package/src/lib/ui/compile/classStyleMergePlan.ts +108 -0
- package/src/lib/ui/compile/collectAbideDiagnostics.ts +11 -0
- package/src/lib/ui/compile/compileComponent.ts +4 -4
- package/src/lib/ui/compile/compileModule.ts +56 -21
- package/src/lib/ui/compile/compileSSR.ts +12 -10
- package/src/lib/ui/compile/compileShadow.ts +163 -12
- package/src/lib/ui/compile/composeProps.ts +3 -3
- package/src/lib/ui/compile/createShadowLanguageService.ts +58 -4
- package/src/lib/ui/compile/createShadowProgram.ts +2 -1
- package/src/lib/ui/compile/createShadowScope.ts +42 -0
- package/src/lib/ui/compile/decodeHtmlEntities.ts +4 -0
- package/src/lib/ui/compile/desugarSignals.ts +12 -7
- package/src/lib/ui/compile/eachPlan.ts +61 -0
- package/src/lib/ui/compile/elementPlan.ts +55 -0
- package/src/lib/ui/compile/encodeSemanticTokens.ts +24 -1
- package/src/lib/ui/compile/generateBuild.ts +156 -127
- package/src/lib/ui/compile/generateSSR.ts +112 -73
- package/src/lib/ui/compile/ifPlan.ts +7 -1
- package/src/lib/ui/compile/interpolatedTemplateLiteral.ts +25 -0
- package/src/lib/ui/compile/isPlainIdentifier.ts +7 -0
- package/src/lib/ui/compile/isTextLeaf.ts +8 -7
- package/src/lib/ui/compile/lowerContext.ts +36 -46
- package/src/lib/ui/compile/lowerDocAccess.ts +2 -2
- package/src/lib/ui/compile/lowerScript.ts +1 -1
- package/src/lib/ui/compile/markupTokens.ts +313 -0
- package/src/lib/ui/compile/pagePropsType.ts +23 -0
- package/src/lib/ui/compile/parseTemplate.ts +135 -62
- package/src/lib/ui/compile/reactiveBinding.ts +47 -0
- package/src/lib/ui/compile/renameSignalRefs.ts +44 -10
- package/src/lib/ui/compile/skeletonable.ts +5 -5
- package/src/lib/ui/compile/snippetPlan.ts +28 -0
- package/src/lib/ui/compile/spreadExcludedNames.ts +4 -4
- package/src/lib/ui/compile/structuralBlockTokens.ts +3 -4
- package/src/lib/ui/compile/switchPlan.ts +7 -1
- package/src/lib/ui/compile/tryPlan.ts +8 -1
- package/src/lib/ui/compile/types/AnalyzedComponent.ts +2 -2
- package/src/lib/ui/compile/types/Binding.ts +24 -0
- package/src/lib/ui/compile/types/CompiledShadow.ts +13 -1
- package/src/lib/ui/compile/types/ElementPlan.ts +43 -0
- package/src/lib/ui/compile/types/ShadowKind.ts +9 -0
- package/src/lib/ui/compile/types/ShadowScope.ts +19 -0
- package/src/lib/ui/compile/types/TemplateAttr.ts +7 -0
- package/src/lib/ui/compile/types/TemplateNode.ts +9 -9
- package/src/lib/ui/compile/withBindings.ts +45 -0
- package/src/lib/ui/createScope.ts +2 -2
- package/src/lib/ui/dom/MATHML_NAMESPACE.ts +2 -2
- package/src/lib/ui/dom/SVG_NAMESPACE.ts +3 -2
- package/src/lib/ui/dom/appendText.ts +1 -1
- package/src/lib/ui/dom/applyResolved.ts +1 -1
- package/src/lib/ui/dom/buildDetachedRange.ts +4 -3
- package/src/lib/ui/dom/cloneStatic.ts +3 -2
- package/src/lib/ui/dom/each.ts +26 -7
- package/src/lib/ui/dom/foreignWrapperTag.ts +2 -2
- package/src/lib/ui/dom/mountChild.ts +8 -1
- package/src/lib/ui/dom/mountSwappableRange.ts +79 -0
- package/src/lib/ui/dom/readCall.ts +6 -4
- package/src/lib/ui/dom/scopeLabel.ts +3 -3
- package/src/lib/ui/dom/skeleton.ts +10 -1
- package/src/lib/ui/dom/switchBlock.ts +17 -63
- package/src/lib/ui/dom/types/EachRow.ts +4 -0
- package/src/lib/ui/dom/types/SkeletonHoles.ts +4 -2
- package/src/lib/ui/dom/when.ts +11 -64
- package/src/lib/ui/history.ts +1 -2
- package/src/lib/ui/html.ts +28 -0
- package/src/lib/ui/installHotBridge.ts +0 -2
- package/src/lib/ui/renderChain.ts +16 -3
- package/src/lib/ui/router.ts +24 -3
- package/src/lib/ui/rpcOutbox/createOutboxQueue.ts +1 -1
- package/src/lib/ui/runtime/CHILD_PRESENT.ts +8 -0
- package/src/lib/ui/runtime/OUTLET_MARKER.ts +2 -2
- package/src/lib/ui/runtime/RANGE_MARKER.ts +1 -1
- package/src/lib/ui/runtime/REACTIVE_CONTEXT.ts +9 -3
- package/src/lib/ui/runtime/captureModelDoc.ts +1 -1
- package/src/lib/ui/runtime/createDoc.ts +107 -13
- package/src/lib/ui/runtime/createEffectNode.ts +5 -5
- package/src/lib/ui/runtime/flushEffects.ts +22 -9
- package/src/lib/ui/runtime/historyEntries.ts +1 -1
- package/src/lib/ui/runtime/track.ts +4 -2
- package/src/lib/ui/runtime/trigger.ts +34 -20
- package/src/lib/ui/runtime/types/Route.ts +2 -2
- package/src/lib/ui/runtime/types/UiComponent.ts +3 -2
- package/src/lib/ui/runtime/types/UiProps.ts +9 -12
- package/src/lib/ui/runtime/walkPath.ts +15 -3
- package/src/lib/ui/startClient.ts +41 -14
- package/src/lib/ui/state.ts +3 -3
- package/src/lib/ui/types/Scope.ts +3 -3
- package/src/serverBuildPlugins.ts +11 -5
- package/src/zodCjsPlugin.ts +51 -0
- package/template/.zed/settings.json +7 -1
- package/template/package.json +3 -0
- package/template/src/ui/pages/layout.abide +3 -5
- package/src/lib/shared/matchesDebugPattern.ts +0 -16
- package/src/lib/shared/parseDebugPatterns.ts +0 -21
|
@@ -6,23 +6,30 @@ import { asOutlet } from './asOutlet.ts'
|
|
|
6
6
|
import { awaitPlan } from './awaitPlan.ts'
|
|
7
7
|
import { bindListenEvent } from './bindListenEvent.ts'
|
|
8
8
|
import { composeProps } from './composeProps.ts'
|
|
9
|
-
import {
|
|
9
|
+
import { eachPlan } from './eachPlan.ts'
|
|
10
|
+
import { elementPlan } from './elementPlan.ts'
|
|
10
11
|
import { groupBindParts } from './groupBindParts.ts'
|
|
11
12
|
import { ifPlan } from './ifPlan.ts'
|
|
13
|
+
import { interpolatedTemplateLiteral } from './interpolatedTemplateLiteral.ts'
|
|
12
14
|
import { isControlFlow } from './isControlFlow.ts'
|
|
15
|
+
import { isPlainIdentifier } from './isPlainIdentifier.ts'
|
|
13
16
|
import { isWhitespaceText } from './isWhitespaceText.ts'
|
|
14
17
|
import { lowerContext } from './lowerContext.ts'
|
|
15
18
|
import { makeVarNamer } from './makeVarNamer.ts'
|
|
16
|
-
import {
|
|
19
|
+
import { reactiveBinding } from './reactiveBinding.ts'
|
|
17
20
|
import { scopeAttr } from './scopeAttr.ts'
|
|
18
21
|
import { skeletonContext } from './skeletonContext.ts'
|
|
22
|
+
import { snippetPlan } from './snippetPlan.ts'
|
|
19
23
|
import { spreadExcludedNames } from './spreadExcludedNames.ts'
|
|
20
24
|
import { staticAttr } from './staticAttr.ts'
|
|
21
25
|
import { staticTextPart } from './staticTextPart.ts'
|
|
22
26
|
import { switchPlan } from './switchPlan.ts'
|
|
23
27
|
import { tryPlan } from './tryPlan.ts'
|
|
28
|
+
import type { Binding } from './types/Binding.ts'
|
|
29
|
+
import type { ShadowKind } from './types/ShadowKind.ts'
|
|
24
30
|
import type { TemplateNode } from './types/TemplateNode.ts'
|
|
25
31
|
import { VOID_TAGS } from './VOID_TAGS.ts'
|
|
32
|
+
import { withBindings } from './withBindings.ts'
|
|
26
33
|
|
|
27
34
|
/* The skeleton positioning anchor a control-flow block / component / slot / outlet
|
|
28
35
|
stamps into its skeleton markup, sourced from the same `ANCHOR` wire-alphabet constant
|
|
@@ -36,8 +43,9 @@ attributes, reactive `attr`/`text` bindings, `on` listeners, keyed `each`, and
|
|
|
36
43
|
conditional `when`. Every embedded expression is first rewritten from the signal
|
|
37
44
|
surface (`count` → `model.count`) and then lowered to the doc patch/read API
|
|
38
45
|
(cell-hoisting runs over the whole result afterwards). The output operates on
|
|
39
|
-
`hostVar` and expects the dom bindings
|
|
40
|
-
|
|
46
|
+
`hostVar` and expects the `$$`-aliased dom bindings (`$$effect`, `$$each`, …)
|
|
47
|
+
and the component's `$$model` (emitted by `desugarSignals`) in scope — the body
|
|
48
|
+
the component compiler wraps and hoists cells into.
|
|
41
49
|
*/
|
|
42
50
|
|
|
43
51
|
/* A JS-identifier-safe frame name from an authored construct label (an attribute name
|
|
@@ -97,49 +105,17 @@ export function generateBuild(
|
|
|
97
105
|
expression: lowerExpression,
|
|
98
106
|
statement: lowerStatement,
|
|
99
107
|
withNestedScripts,
|
|
100
|
-
|
|
108
|
+
withShadow,
|
|
101
109
|
bindRead,
|
|
102
110
|
bindWrite,
|
|
103
111
|
} = lowerContext(stateNames, derivedNames, computedNames)
|
|
104
112
|
|
|
105
|
-
/*
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
/* How a reactive value param binds: `param` is the thunk's value parameter (the cell),
|
|
112
|
-
`prefix` declares any per-leaf readers, `localNames` enter the deref scope for the body.
|
|
113
|
-
A plain identifier reads the cell directly (`item` → `item.value`); a destructure
|
|
114
|
-
re-applies over the cell per read so each leaf stays reactive (JS handles
|
|
115
|
-
defaults/rest/rename/nesting). The caller lowers the body with `localNames` derived. */
|
|
116
|
-
function reactiveBinding(authorParam: string): {
|
|
117
|
-
param: string
|
|
118
|
-
prefix: string
|
|
119
|
-
localNames: string[]
|
|
120
|
-
} {
|
|
121
|
-
if (isPlainIdentifier(authorParam)) {
|
|
122
|
-
return { param: authorParam, prefix: '', localNames: [authorParam] }
|
|
123
|
-
}
|
|
124
|
-
const cellParam = nextVar('aw')
|
|
125
|
-
const deriveVar = nextVar('ad')
|
|
126
|
-
const leaves = destructureBindingNames(authorParam)
|
|
127
|
-
/* Lower the destructure declaration so a default/computed-key initializer that
|
|
128
|
-
references a component signal (`{ label = fallback }`, `{ [key]: v }`) is rewritten
|
|
129
|
-
to its `model`/cell form — the bound leaf names are name-slots and stay untouched.
|
|
130
|
-
A pattern with no such initializer lowers to itself, so the common case is unchanged
|
|
131
|
-
and SSR (which lowers the same declaration) stays congruent. */
|
|
132
|
-
const declaration = lowerStatement(`const ${authorParam} = ${cellParam}.value`)
|
|
133
|
-
const prefix =
|
|
134
|
-
`const ${deriveVar} = { get value() { ${declaration} return { ${leaves.join(', ')} }; } };\n` +
|
|
135
|
-
leaves
|
|
136
|
-
.map(
|
|
137
|
-
(leaf) =>
|
|
138
|
-
`const ${leaf} = { get value() { return ${deriveVar}.value.${leaf}; } };\n`,
|
|
139
|
-
)
|
|
140
|
-
.join('')
|
|
141
|
-
return { param: cellParam, prefix, localNames: leaves }
|
|
142
|
-
}
|
|
113
|
+
/* Maps a plan `Binding`'s classification to the client `ShadowKind`: a `reactive` value
|
|
114
|
+
derefs as a `.value` cell (`derived`), a `plain` value as the bare local (`plain`). The
|
|
115
|
+
injected mapping `withBindings` registers every binding through — the one place a name's
|
|
116
|
+
kind is chosen on this back-end. */
|
|
117
|
+
const buildBindingKind = (binding: Binding): ShadowKind =>
|
|
118
|
+
binding.classification === 'reactive' ? 'derived' : 'plain'
|
|
143
119
|
|
|
144
120
|
/* Emits the wiring for one non-static attribute against an already-obtained skeleton
|
|
145
121
|
element var — reactive `attr`, `on` listener, `attach`, or a two-way `bind`. */
|
|
@@ -147,27 +123,42 @@ export function generateBuild(
|
|
|
147
123
|
node: Extract<TemplateNode, { kind: 'element' }>,
|
|
148
124
|
attr: Extract<
|
|
149
125
|
(typeof node.attrs)[number],
|
|
150
|
-
{
|
|
126
|
+
{
|
|
127
|
+
kind:
|
|
128
|
+
| 'expression'
|
|
129
|
+
| 'interpolated'
|
|
130
|
+
| 'event'
|
|
131
|
+
| 'attach'
|
|
132
|
+
| 'bind'
|
|
133
|
+
| 'class'
|
|
134
|
+
| 'style'
|
|
135
|
+
}
|
|
151
136
|
>,
|
|
152
137
|
varName: string,
|
|
153
138
|
): string {
|
|
154
139
|
if (attr.kind === 'expression') {
|
|
155
|
-
return
|
|
140
|
+
return `$$attr(${varName}, ${JSON.stringify(attr.name)}, ${namedThunk(`attr_${attr.name}`, `return (${lowerExpression(attr.code)})`)});\n`
|
|
141
|
+
}
|
|
142
|
+
/* `name="literal {expr}"` — the template-literal concatenation bound as a reactive
|
|
143
|
+
string attribute (always present). A class/style with directives is merged
|
|
144
|
+
upstream of this dispatch, so it never reaches here. */
|
|
145
|
+
if (attr.kind === 'interpolated') {
|
|
146
|
+
return `$$attr(${varName}, ${JSON.stringify(attr.name)}, ${namedThunk(`attr_${attr.name}`, `return (${lowerExpression(interpolatedTemplateLiteral(attr.parts))})`)});\n`
|
|
156
147
|
}
|
|
157
148
|
if (attr.kind === 'event') {
|
|
158
|
-
return
|
|
149
|
+
return `$$on(${varName}, ${JSON.stringify(attr.event)}, (${lowerExpression(attr.code)}));\n`
|
|
159
150
|
}
|
|
160
151
|
if (attr.kind === 'attach') {
|
|
161
|
-
return
|
|
152
|
+
return `$$attach(${varName}, (${lowerExpression(attr.code)}));\n`
|
|
162
153
|
}
|
|
163
154
|
/* `class:<name>` — toggle the class by truthiness; surgical, no element re-render.
|
|
164
155
|
Layers on top of any static `class="…"` in the skeleton (classList is additive). */
|
|
165
156
|
if (attr.kind === 'class') {
|
|
166
|
-
return
|
|
157
|
+
return `$$effect(${namedThunk(`class_${attr.name}`, `${varName}.classList.toggle(${JSON.stringify(attr.name)}, !!(${lowerExpression(attr.code)}));`)});\n`
|
|
167
158
|
}
|
|
168
159
|
/* `style:<property>` — write one inline style / custom property reactively. */
|
|
169
160
|
if (attr.kind === 'style') {
|
|
170
|
-
return
|
|
161
|
+
return `$$effect(${namedThunk(`style_${attr.property}`, `${varName}.style.setProperty(${JSON.stringify(attr.property)}, String(${lowerExpression(attr.code)}));`)});\n`
|
|
171
162
|
}
|
|
172
163
|
if (attr.property === 'group') {
|
|
173
164
|
/* Grouped two-way: radio binds the path to the single checked `value`;
|
|
@@ -180,13 +171,13 @@ export function generateBuild(
|
|
|
180
171
|
const value = lowerExpression(valueCode)
|
|
181
172
|
if (isRadio) {
|
|
182
173
|
return (
|
|
183
|
-
|
|
184
|
-
|
|
174
|
+
`$$effect(${namedThunk('bind_group', `${varName}.checked = (${lowerExpression(attr.code)}) === (${value});`)});\n` +
|
|
175
|
+
`$$on(${varName}, "change", () => { if (${varName}.checked) { ${lowerStatement(`${attr.code} = ${valueCode}`)} } });\n`
|
|
185
176
|
)
|
|
186
177
|
}
|
|
187
178
|
return (
|
|
188
|
-
|
|
189
|
-
|
|
179
|
+
`$$effect(${namedThunk('bind_group', `${varName}.checked = (${lowerExpression(attr.code)}).includes(${value});`)});\n` +
|
|
180
|
+
`$$on(${varName}, "change", () => { const $groupValue = ${value}; if (${varName}.checked) { if (!(${lowerExpression(attr.code)}).includes($groupValue)) { ${lowerStatement(`${attr.code}.push($groupValue)`)} } } else { const $groupIndex = (${lowerExpression(attr.code)}).indexOf($groupValue); if ($groupIndex !== -1) { ${lowerStatement(`delete ${attr.code}[$groupIndex]`)} } } });\n`
|
|
190
181
|
)
|
|
191
182
|
}
|
|
192
183
|
/* Two-way: drive the property from the bind target, and write it back on the
|
|
@@ -196,8 +187,8 @@ export function generateBuild(
|
|
|
196
187
|
`.get()` and writes via `.set(v)` — see `bindRead`/`bindWrite`. */
|
|
197
188
|
const event = bindListenEvent(attr.property, node.tag)
|
|
198
189
|
return (
|
|
199
|
-
|
|
200
|
-
|
|
190
|
+
`$$effect(${namedThunk(`bind_${attr.property}`, `${varName}.${attr.property} = ${bindRead(attr.code)};`)});\n` +
|
|
191
|
+
`$$on(${varName}, ${JSON.stringify(event)}, () => { ${bindWrite(attr.code, `${varName}.${attr.property}`)} });\n`
|
|
201
192
|
)
|
|
202
193
|
}
|
|
203
194
|
|
|
@@ -231,7 +222,7 @@ export function generateBuild(
|
|
|
231
222
|
return staticTextPart(part.value)
|
|
232
223
|
}
|
|
233
224
|
binds.push(
|
|
234
|
-
|
|
225
|
+
`$$appendTextAt(${skVar}.an[${holeIndex(anIndex, part)}], ${namedThunk('text', `return (${lowerExpression(part.code)})`)});\n`,
|
|
235
226
|
)
|
|
236
227
|
return ANCHOR_COMMENT
|
|
237
228
|
})
|
|
@@ -245,7 +236,9 @@ export function generateBuild(
|
|
|
245
236
|
(`anchor.parentNode`). A component takes an anchor like a block — no wrapper
|
|
246
237
|
element — so its root lays out as a true direct child of `anchor.parentNode`. */
|
|
247
238
|
const anchorVar = anchorVarAt(node, skVar, binds)
|
|
248
|
-
binds.push(
|
|
239
|
+
binds.push(
|
|
240
|
+
generateChild(node, `${anchorVar}.parentNode`, `$$anchorCursor(${anchorVar})`),
|
|
241
|
+
)
|
|
249
242
|
return ANCHOR_COMMENT
|
|
250
243
|
}
|
|
251
244
|
if (node.kind === 'script') {
|
|
@@ -270,7 +263,7 @@ export function generateBuild(
|
|
|
270
263
|
`outlet` boundary the router fills with the next chain layer (`fillBoundary`).
|
|
271
264
|
No wrapper element — the filled child lays out as a direct child of the parent. */
|
|
272
265
|
const anchorVar = anchorVarAt(node, skVar, binds)
|
|
273
|
-
binds.push(
|
|
266
|
+
binds.push(`$$outlet(${anchorVar}.parentNode, $$anchorCursor(${anchorVar}));\n`)
|
|
274
267
|
return ANCHOR_COMMENT
|
|
275
268
|
}
|
|
276
269
|
if (node.tag === 'slot') {
|
|
@@ -279,7 +272,7 @@ export function generateBuild(
|
|
|
279
272
|
const anchorVar = anchorVarAt(node, skVar, binds)
|
|
280
273
|
const hostVar = nextVar('host')
|
|
281
274
|
binds.push(
|
|
282
|
-
|
|
275
|
+
`$$mountSlot(${anchorVar}.parentNode, (${hostVar}) => {\n${generateSlot(node, hostVar)}}, $$anchorCursor(${anchorVar}));\n`,
|
|
283
276
|
)
|
|
284
277
|
return ANCHOR_COMMENT
|
|
285
278
|
}
|
|
@@ -294,6 +287,12 @@ export function generateBuild(
|
|
|
294
287
|
SSR back-end also reads, rather than re-deriving leaf-ness via `isTextLeaf` here. */
|
|
295
288
|
const textLeafBind =
|
|
296
289
|
reactiveTextChild !== undefined && markText.get(reactiveTextChild) === false
|
|
290
|
+
/* The shared per-element emission DECISION (one site both back-ends consult): each
|
|
291
|
+
attribute classified + tagged with its merge status, the class/style/directive merge
|
|
292
|
+
folded in, and void-tag status. The composed `classParts`/`styleParts` and the
|
|
293
|
+
merge triggers come from the same plan SSR renders, so neither the attribute set nor
|
|
294
|
+
the merged value can drift. Build still RENDERS each kind below as live wiring. */
|
|
295
|
+
const plan = elementPlan(node, lowerExpression)
|
|
297
296
|
let openTag = `<${node.tag}`
|
|
298
297
|
let elVar = ''
|
|
299
298
|
if (hasReactiveAttr || textLeafBind) {
|
|
@@ -303,13 +302,33 @@ export function generateBuild(
|
|
|
303
302
|
elVar = nextVar('el')
|
|
304
303
|
binds.push(`const ${elVar} = ${skVar}.el[${holeIndex(elIndex, node)}];\n`)
|
|
305
304
|
openTag += ` ${HOLE_ATTRIBUTE}`
|
|
306
|
-
|
|
305
|
+
/* An interpolated (reactive) class/style base can't layer additive directive toggles
|
|
306
|
+
on top — re-setting the base would wipe them — so base + its directives collapse
|
|
307
|
+
into ONE effect computing the whole value (`mergeClassBuild`/`mergeStyleBuild`,
|
|
308
|
+
the build trigger); their attrs are then skipped (`mergedBuild`) in the dispatch
|
|
309
|
+
below. A STATIC base keeps the surgical-toggle model (the base sits in the cloned
|
|
310
|
+
skeleton). */
|
|
311
|
+
if (plan.merge.mergeClassBuild) {
|
|
312
|
+
binds.push(
|
|
313
|
+
`$$effect(${namedThunk('class_merge', `${elVar}.setAttribute("class", [${plan.merge.classParts.join(', ')}].filter(Boolean).join(' '));`)});\n`,
|
|
314
|
+
)
|
|
315
|
+
}
|
|
316
|
+
if (plan.merge.mergeStyleBuild) {
|
|
317
|
+
binds.push(
|
|
318
|
+
`$$effect(${namedThunk('style_merge', `${elVar}.setAttribute("style", [${plan.merge.styleParts.join(', ')}].filter(Boolean).join(';'));`)});\n`,
|
|
319
|
+
)
|
|
320
|
+
}
|
|
321
|
+
for (const { attr, mergedBuild } of plan.attrs) {
|
|
322
|
+
/* Skip the attrs already folded into a merged class/style effect. */
|
|
323
|
+
if (mergedBuild) {
|
|
324
|
+
continue
|
|
325
|
+
}
|
|
307
326
|
if (attr.kind === 'spread') {
|
|
308
327
|
/* `{...expr}` onto the element: each key binds as a reactive attribute
|
|
309
328
|
(or an `on<event>` function as a listener) via `spreadAttrs`, skipping
|
|
310
329
|
any key explicitly named on the element (the explicit attr wins). */
|
|
311
330
|
binds.push(
|
|
312
|
-
|
|
331
|
+
`$$spreadAttrs(${elVar}, ${namedThunk('spread', `return (${lowerExpression(attr.code)})`)}, ${JSON.stringify(spreadExcludedNames(node.attrs))});\n`,
|
|
313
332
|
)
|
|
314
333
|
} else if (attr.kind !== 'static') {
|
|
315
334
|
binds.push(dynamicAttr(node, attr, elVar))
|
|
@@ -319,13 +338,16 @@ export function generateBuild(
|
|
|
319
338
|
for (const scope of node.scopes ?? []) {
|
|
320
339
|
openTag += scopeAttr(scope)
|
|
321
340
|
}
|
|
322
|
-
|
|
323
|
-
|
|
341
|
+
/* Static attrs sit in the cloned skeleton markup — emitted here regardless of whether
|
|
342
|
+
the element is a hole. A static class/style folded into a merged attribute (build
|
|
343
|
+
trigger) is skipped, since the merge effect re-sets the whole value. */
|
|
344
|
+
for (const { attr, mergedBuild } of plan.attrs) {
|
|
345
|
+
if (attr.kind === 'static' && !mergedBuild) {
|
|
324
346
|
openTag += staticAttr(attr.name, attr.value)
|
|
325
347
|
}
|
|
326
348
|
}
|
|
327
349
|
openTag += '>'
|
|
328
|
-
if (
|
|
350
|
+
if (plan.isVoid) {
|
|
329
351
|
return openTag
|
|
330
352
|
}
|
|
331
353
|
if (textLeafBind) {
|
|
@@ -352,7 +374,7 @@ export function generateBuild(
|
|
|
352
374
|
const skVar = nextVar('sk')
|
|
353
375
|
const binds: string[] = []
|
|
354
376
|
const html = skeletonMarkup(node, skVar, binds)
|
|
355
|
-
return `const ${skVar} = skeleton(${parentVar}, ${JSON.stringify(html)});\n${binds.join('')}`
|
|
377
|
+
return `const ${skVar} = $$skeleton(${parentVar}, ${JSON.stringify(html)});\n${binds.join('')}`
|
|
356
378
|
}
|
|
357
379
|
|
|
358
380
|
/* Emits code appending `node` to `parentVar`. */
|
|
@@ -378,8 +400,8 @@ export function generateBuild(
|
|
|
378
400
|
.map((part, index) => {
|
|
379
401
|
const splitAlways = index < consumers.length - 1 ? ', true' : ''
|
|
380
402
|
return part.kind === 'static'
|
|
381
|
-
?
|
|
382
|
-
:
|
|
403
|
+
? `$$appendStatic(${parentVar}, ${JSON.stringify(part.value)}${splitAlways});\n`
|
|
404
|
+
: `$$appendText(${parentVar}, ${namedThunk('text', `return (${lowerExpression(part.code)})`)}${splitAlways});\n`
|
|
383
405
|
})
|
|
384
406
|
.join('')
|
|
385
407
|
}
|
|
@@ -387,7 +409,7 @@ export function generateBuild(
|
|
|
387
409
|
/* A standalone layout outlet (a top-level/element-nested `<slot/>` rewritten by
|
|
388
410
|
`asOutlet`, reached outside any skeleton): an empty `outlet` boundary at
|
|
389
411
|
`before`, no anchor — the router fills it with the next chain layer. */
|
|
390
|
-
return
|
|
412
|
+
return `$$outlet(${parentVar}, ${before});\n`
|
|
391
413
|
}
|
|
392
414
|
if (node.kind === 'element' && node.tag === 'slot') {
|
|
393
415
|
/* In a layout, `<slot/>` is the router's page outlet (`outlet` boundary the
|
|
@@ -395,7 +417,7 @@ export function generateBuild(
|
|
|
395
417
|
are rewritten to `OUTLET_TAG` up front by `asOutlet` and handled above; this
|
|
396
418
|
covers a layout slot reached inside a control-flow branch. */
|
|
397
419
|
if (isLayout) {
|
|
398
|
-
return
|
|
420
|
+
return `$$outlet(${parentVar}, ${before});\n`
|
|
399
421
|
}
|
|
400
422
|
return generateSlot(node, parentVar)
|
|
401
423
|
}
|
|
@@ -452,7 +474,7 @@ export function generateBuild(
|
|
|
452
474
|
let runHtml = ''
|
|
453
475
|
const flush = (): void => {
|
|
454
476
|
if (runHtml !== '') {
|
|
455
|
-
code +=
|
|
477
|
+
code += `$$cloneStatic(${parentVar}, ${JSON.stringify(runHtml)});\n`
|
|
456
478
|
runHtml = ''
|
|
457
479
|
}
|
|
458
480
|
}
|
|
@@ -473,8 +495,14 @@ export function generateBuild(
|
|
|
473
495
|
the component scope (its `model`/cells); `args` are plain parameters bound by
|
|
474
496
|
the call. Appends nothing at the declaration site — `{name(args)}` mounts it. */
|
|
475
497
|
function generateSnippet(node: Extract<TemplateNode, { kind: 'snippet' }>): string {
|
|
476
|
-
const
|
|
477
|
-
|
|
498
|
+
const plan = snippetPlan(node)
|
|
499
|
+
/* `args` are plain call parameters, not component cells — `withBindings` registers the
|
|
500
|
+
plan's `plain` bindings so the body reads the bare local, shadowing a same-named
|
|
501
|
+
component signal rather than reading it. */
|
|
502
|
+
const body = withBindings(withShadow, plan.bindings, buildBindingKind, () =>
|
|
503
|
+
plan.children.map((child) => generateChild(child, '$host')).join(''),
|
|
504
|
+
)
|
|
505
|
+
return `function ${plan.name}(${plan.params ?? ''}) {\nreturn $$snippet(($host) => {\n${body}});\n}\n`
|
|
478
506
|
}
|
|
479
507
|
|
|
480
508
|
/* A switch: each `case` is `{ match: () => value, render }`, the default is
|
|
@@ -493,21 +521,17 @@ export function generateBuild(
|
|
|
493
521
|
return `{ match: ${match}, render: ${branchThunk(branch.children)} }`
|
|
494
522
|
})
|
|
495
523
|
.join(', ')
|
|
496
|
-
return
|
|
524
|
+
return `$$switchBlock(${parentVar}, () => (${lowerExpression(node.subject)}), [${cases}], ${before});\n`
|
|
497
525
|
}
|
|
498
526
|
|
|
499
|
-
/* A
|
|
500
|
-
|
|
527
|
+
/* A `{children()}` slot fill point: render the parent-provided content (`$children`).
|
|
528
|
+
A fallback is now an authored `{#if children}…{:else}…{/if}`, so the slot node never
|
|
529
|
+
carries children — the invoke is unconditional but guarded against a propless mount. */
|
|
501
530
|
function generateSlot(
|
|
502
|
-
|
|
531
|
+
_node: Extract<TemplateNode, { kind: 'element' }>,
|
|
503
532
|
parentVar: string,
|
|
504
533
|
): string {
|
|
505
|
-
|
|
506
|
-
const invoke = `$props.$children(${parentVar})`
|
|
507
|
-
if (fallback.trim() === '') {
|
|
508
|
-
return `if ($props && $props.$children) { ${invoke}; }\n`
|
|
509
|
-
}
|
|
510
|
-
return `if ($props && $props.$children) { ${invoke}; } else {\n${fallback}}\n`
|
|
534
|
+
return `if ($props && $props.$children) { $props.$children(${parentVar}); }\n`
|
|
511
535
|
}
|
|
512
536
|
|
|
513
537
|
/* The child's slot content as a host-taking builder (`$children`), or undefined when
|
|
@@ -534,11 +558,11 @@ export function generateBuild(
|
|
|
534
558
|
parentVar: string,
|
|
535
559
|
before: string,
|
|
536
560
|
): string {
|
|
537
|
-
return
|
|
561
|
+
return `$$mountChild(${parentVar}, ${node.name}, ${propsArg(node)}, ${before}, ${JSON.stringify(node.name)});\n`
|
|
538
562
|
}
|
|
539
563
|
|
|
540
564
|
/* An await block: pending → resolved(value) / error branches. Each branch is a
|
|
541
|
-
|
|
565
|
+
void render thunk `(parent[, value]) => void` that builds its content into `parent`. */
|
|
542
566
|
function generateAwait(
|
|
543
567
|
node: Extract<TemplateNode, { kind: 'await' }>,
|
|
544
568
|
parentVar: string,
|
|
@@ -551,21 +575,20 @@ export function generateBuild(
|
|
|
551
575
|
resolved content/binding feeds one thunk regardless of mode. */
|
|
552
576
|
const thenThunk = branchThunk(
|
|
553
577
|
plan.resolvedChildren,
|
|
554
|
-
plan.
|
|
578
|
+
plan.resolvedBindings,
|
|
555
579
|
plan.finallyChildren,
|
|
556
|
-
true,
|
|
557
580
|
)
|
|
558
581
|
/* Neither catch nor finally → pass `undefined` so awaitBlock re-throws the
|
|
559
582
|
rejection (surfacing it) instead of rendering an empty branch. A finally-only
|
|
560
583
|
block keeps a catch thunk that renders just finally. */
|
|
561
584
|
const catchThunk = plan.surfaceRejection
|
|
562
585
|
? 'undefined'
|
|
563
|
-
: branchThunk(plan.catchChildren, plan.
|
|
586
|
+
: branchThunk(plan.catchChildren, plan.catchBindings, plan.finallyChildren)
|
|
564
587
|
const pendingThunk = hasRenderableContent(plan.pending)
|
|
565
588
|
? branchThunk(plan.pending)
|
|
566
589
|
: 'undefined'
|
|
567
590
|
return (
|
|
568
|
-
|
|
591
|
+
`$$awaitBlock(${parentVar}, $$nextBlockId(), () => (${lowerExpression(node.promise)}), ` +
|
|
569
592
|
`${pendingThunk}, ` +
|
|
570
593
|
`${thenThunk}, ` +
|
|
571
594
|
`${catchThunk}, ${before});\n`
|
|
@@ -576,28 +599,33 @@ export function generateBuild(
|
|
|
576
599
|
builds its children — and an optional trailing `finally` branch — into
|
|
577
600
|
`parent`. The full-range model tracks the built content between markers, so a
|
|
578
601
|
branch holds ANY content (components, text, nested control-flow, snippets) and
|
|
579
|
-
is generated exactly like a normal child list. `
|
|
580
|
-
|
|
581
|
-
|
|
602
|
+
is generated exactly like a normal child list. `bindings` are the value param(s)
|
|
603
|
+
the plan declared (an `await` `then` value → `reactive`; a `catch` error → `plain`)
|
|
604
|
+
— at most one. Names flow to the deref scope ONLY through `withBindings` over the
|
|
605
|
+
plan's bindings; the cell wiring of a `reactive` binding is arranged by
|
|
606
|
+
`reactiveBinding`. Nested `<script>`s are emitted in document order by
|
|
607
|
+
`generateChildren`; `withNestedScripts` puts their bindings in deref scope. */
|
|
582
608
|
function branchThunk(
|
|
583
609
|
children: TemplateNode[],
|
|
584
|
-
|
|
610
|
+
bindings: Binding[] = [],
|
|
585
611
|
finallyChildren: TemplateNode[] = [],
|
|
586
|
-
reactiveValue = false,
|
|
587
612
|
): string {
|
|
588
613
|
const parentParam = nextVar('p')
|
|
589
|
-
/* A reactive
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
const
|
|
594
|
-
const
|
|
595
|
-
|
|
614
|
+
/* A `reactive` binding arrives as a `.value` cell the runtime can set in place (the
|
|
615
|
+
branch re-runs in place on a re-settle); `reactiveBinding` renders its cell param +
|
|
616
|
+
per-leaf reader prefix. A `plain` binding is the author param verbatim — a real arrow
|
|
617
|
+
parameter `withBindings` registers under `plain`, read as the bare local. */
|
|
618
|
+
const reactive = bindings.find((binding) => binding.classification === 'reactive')
|
|
619
|
+
const wiring =
|
|
620
|
+
reactive === undefined ? undefined : reactiveBinding(reactive, nextVar, lowerStatement)
|
|
621
|
+
const plainBinding = bindings.find((binding) => binding.classification === 'plain')
|
|
622
|
+
const param = wiring?.param ?? plainBinding?.name
|
|
623
|
+
const prefix = wiring?.prefix ?? ''
|
|
596
624
|
const head = param === undefined ? `(${parentParam})` : `(${parentParam}, ${param})`
|
|
597
625
|
const body = withNestedScripts(children, () =>
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
626
|
+
withBindings(withShadow, bindings, buildBindingKind, () =>
|
|
627
|
+
generateChildren(children, parentParam),
|
|
628
|
+
),
|
|
601
629
|
)
|
|
602
630
|
const finallyBody =
|
|
603
631
|
finallyChildren.length > 0
|
|
@@ -630,11 +658,11 @@ export function generateBuild(
|
|
|
630
658
|
before: string,
|
|
631
659
|
): string {
|
|
632
660
|
const plan = tryPlan(node)
|
|
633
|
-
const tryThunk = branchThunk(plan.guarded,
|
|
661
|
+
const tryThunk = branchThunk(plan.guarded, [], plan.finallyChildren)
|
|
634
662
|
const catchThunk = plan.hasCatch
|
|
635
|
-
? branchThunk(plan.catchChildren, plan.
|
|
663
|
+
? branchThunk(plan.catchChildren, plan.catchBindings, plan.finallyChildren)
|
|
636
664
|
: 'undefined'
|
|
637
|
-
return
|
|
665
|
+
return `$$tryBlock(${parentVar}, $$nextBlockId(), ${tryThunk}, ${catchThunk}, ${before});\n`
|
|
638
666
|
}
|
|
639
667
|
|
|
640
668
|
/* A conditional with an optional nested `<template else>` (a `case` child). Each
|
|
@@ -652,7 +680,7 @@ export function generateBuild(
|
|
|
652
680
|
const thenThunk = branchThunk(plan.thenChildren)
|
|
653
681
|
const elseThunk =
|
|
654
682
|
plan.elseBranch === undefined ? 'undefined' : branchThunk(plan.elseBranch.children)
|
|
655
|
-
return
|
|
683
|
+
return `$$when(${parentVar}, () => (${lowerExpression(node.condition)}), ${thenThunk}, ${elseThunk}, ${before});\n`
|
|
656
684
|
}
|
|
657
685
|
/* if/elseif/else is a cond-chain — reuse `switchBlock` over a constant `true`
|
|
658
686
|
subject with `Boolean`-coerced match thunks, so the first truthy branch wins
|
|
@@ -665,7 +693,7 @@ export function generateBuild(
|
|
|
665
693
|
: `{ match: undefined, render: ${branchThunk(branch.children)} }`,
|
|
666
694
|
),
|
|
667
695
|
]
|
|
668
|
-
return
|
|
696
|
+
return `$$switchBlock(${parentVar}, () => true, [${entries.join(', ')}], ${before});\n`
|
|
669
697
|
}
|
|
670
698
|
|
|
671
699
|
/* A keyed each. Each row is a content RANGE (any content, tracked between the
|
|
@@ -675,6 +703,7 @@ export function generateBuild(
|
|
|
675
703
|
parentVar: string,
|
|
676
704
|
before: string,
|
|
677
705
|
): string {
|
|
706
|
+
const plan = eachPlan(node)
|
|
678
707
|
const rowParam = nextVar('p')
|
|
679
708
|
/* The item is a reactive `.value` cell so a re-key with a changed value updates the row
|
|
680
709
|
in place (no rebuild). `keyOf` receives the RAW item; the key expression is lowered
|
|
@@ -683,34 +712,34 @@ export function generateBuild(
|
|
|
683
712
|
plain `as` returns its name; a destructuring `as` binds a fresh param and returns
|
|
684
713
|
THAT, not the pattern re-wrapped (`[i,crumb]` → `[i,crumb]` would allocate a fresh
|
|
685
714
|
array per reconcile, so keys never match and every row rebuilds). An explicit `key`
|
|
686
|
-
destructures the item via `
|
|
687
|
-
const rawItemParam = isPlainIdentifier(
|
|
688
|
-
const keyParam =
|
|
689
|
-
const keyExpression =
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
const
|
|
695
|
-
const
|
|
696
|
-
node.index === undefined ? binding.localNames : [...binding.localNames, node.index]
|
|
715
|
+
destructures the item via `plan.as` to read its leaves. */
|
|
716
|
+
const rawItemParam = isPlainIdentifier(plan.as) ? plan.as : nextVar('k')
|
|
717
|
+
const keyParam = plan.key === undefined ? rawItemParam : plan.as
|
|
718
|
+
const keyExpression = plan.key === undefined ? rawItemParam : lowerExpression(plan.key)
|
|
719
|
+
/* The item is the row's `reactive` binding (its `.value` cell + per-leaf reader prefix
|
|
720
|
+
rendered by `reactiveBinding`); `index="i"` is a second reactive cell param, a plain
|
|
721
|
+
identifier read as `i.value` (no prefix). The names of BOTH enter the deref scope via
|
|
722
|
+
`withBindings` over `plan.bindings`, not a hand-built list. */
|
|
723
|
+
const itemWiring = reactiveBinding(plan.bindings[0] as Binding, nextVar, lowerStatement)
|
|
724
|
+
const indexParam = plan.index === undefined ? '' : `, ${plan.index}`
|
|
697
725
|
/* The row body builds its children (a `<script>` declares per-row local signals,
|
|
698
726
|
emitted in document order) into the row parent. A `<template catch>` child is
|
|
699
727
|
consumed by the async-each, not the row — `generateChildren` skips it. */
|
|
700
|
-
const rowBody = withNestedScripts(
|
|
701
|
-
|
|
728
|
+
const rowBody = withNestedScripts(plan.children, () =>
|
|
729
|
+
withBindings(withShadow, plan.bindings, buildBindingKind, () =>
|
|
730
|
+
generateChildren(plan.children, rowParam),
|
|
731
|
+
),
|
|
702
732
|
)
|
|
703
733
|
/* `await` → the AsyncIterable runtime, drained row-by-row on the client, with an
|
|
704
734
|
optional `<template catch>` branch rendered (after the streamed rows) when the
|
|
705
735
|
iterator rejects. Absent → `undefined`, so the rejection surfaces instead. */
|
|
706
|
-
const fn =
|
|
707
|
-
const
|
|
708
|
-
|
|
709
|
-
? `, ${catchBranch === undefined ? 'undefined' : branchThunk(catchBranch.children, catchBranch.as ?? '_error')}`
|
|
736
|
+
const fn = plan.async ? '$$eachAsync' : '$$each'
|
|
737
|
+
const catchArg = plan.async
|
|
738
|
+
? `, ${plan.hasCatch ? branchThunk(plan.catchChildren, plan.catchBindings) : 'undefined'}`
|
|
710
739
|
: ''
|
|
711
740
|
return (
|
|
712
|
-
`${fn}(${parentVar}, () => (${lowerExpression(
|
|
713
|
-
`(${keyParam}) => (${keyExpression}), (${rowParam}, ${
|
|
741
|
+
`${fn}(${parentVar}, () => (${lowerExpression(plan.items)}), ` +
|
|
742
|
+
`(${keyParam}) => (${keyExpression}), (${rowParam}, ${itemWiring.param}${indexParam}) => {\n${itemWiring.prefix}${rowBody}}${catchArg}, ${before});\n`
|
|
714
743
|
)
|
|
715
744
|
}
|
|
716
745
|
|