@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
|
@@ -1,15 +1,20 @@
|
|
|
1
|
+
import type { Binding } from './types/Binding.ts'
|
|
1
2
|
import type { TemplateNode } from './types/TemplateNode.ts'
|
|
2
3
|
|
|
3
4
|
type Case = Extract<TemplateNode, { kind: 'case' }>
|
|
4
5
|
|
|
5
6
|
/* The structural shape of a `switch`, resolved once so the build and SSR back-ends share one
|
|
6
7
|
reading of its cases and only own emission. Both previously recomputed the `case` filter;
|
|
7
|
-
SSR additionally located the match-less default.
|
|
8
|
+
SSR additionally located the match-less default. A `switch` introduces no names, so
|
|
9
|
+
`bindings` is empty — present so every block plan answers the single binding source
|
|
10
|
+
uniformly. */
|
|
8
11
|
export type SwitchPlan = {
|
|
9
12
|
/* The `case` branches in source order (`match` set; the default leaves it unset). */
|
|
10
13
|
cases: Case[]
|
|
11
14
|
/* The match-less default branch, if present. */
|
|
12
15
|
fallback: Case | undefined
|
|
16
|
+
/* A `switch` binds no names. */
|
|
17
|
+
bindings: Binding[]
|
|
13
18
|
}
|
|
14
19
|
|
|
15
20
|
/* Resolves a `switch` node's cases into the shared structural plan. */
|
|
@@ -18,5 +23,6 @@ export function switchPlan(node: Extract<TemplateNode, { kind: 'switch' }>): Swi
|
|
|
18
23
|
return {
|
|
19
24
|
cases,
|
|
20
25
|
fallback: cases.find((branch) => branch.match === undefined),
|
|
26
|
+
bindings: [],
|
|
21
27
|
}
|
|
22
28
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { catchBinding } from './catchBinding.ts'
|
|
1
2
|
import { resolveBranches } from './resolveBranches.ts'
|
|
3
|
+
import type { Binding } from './types/Binding.ts'
|
|
2
4
|
import type { TemplateNode } from './types/TemplateNode.ts'
|
|
3
5
|
|
|
4
6
|
/* The structural shape of a `try` error boundary, resolved once so the build and SSR
|
|
@@ -14,16 +16,21 @@ export type TryPlan = {
|
|
|
14
16
|
finallyChildren: TemplateNode[]
|
|
15
17
|
/* No catch → a throw propagates to the enclosing boundary (re-throw / `undefined` thunk). */
|
|
16
18
|
hasCatch: boolean
|
|
19
|
+
/* The catch branch's binding (`catchAs`, `plain`); empty when no catch branch. The
|
|
20
|
+
guarded and `finally` branches bind nothing. */
|
|
21
|
+
catchBindings: Binding[]
|
|
17
22
|
}
|
|
18
23
|
|
|
19
24
|
/* Resolves a `try` node's branches into the shared structural plan. */
|
|
20
25
|
export function tryPlan(node: Extract<TemplateNode, { kind: 'try' }>): TryPlan {
|
|
21
26
|
const [catchBranch, finallyBranch] = resolveBranches(node, 'catch', 'finally')
|
|
27
|
+
const catchAs = catchBranch?.as ?? '_error'
|
|
22
28
|
return {
|
|
23
29
|
guarded: node.children.filter((child) => child.kind !== 'branch'),
|
|
24
30
|
catchChildren: catchBranch?.children ?? [],
|
|
25
|
-
catchAs
|
|
31
|
+
catchAs,
|
|
26
32
|
finallyChildren: finallyBranch?.children ?? [],
|
|
27
33
|
hasCatch: catchBranch !== undefined,
|
|
34
|
+
catchBindings: catchBinding(catchAs, catchBranch !== undefined),
|
|
28
35
|
}
|
|
29
36
|
}
|
|
@@ -25,7 +25,7 @@ export type AnalyzedComponent = {
|
|
|
25
25
|
CSS to bundle. A top-level `<style>` covers the whole component; a nested one
|
|
26
26
|
covers only its sibling subtree. Empty for a component with no style. */
|
|
27
27
|
styles: { attribute: string; css: string }[]
|
|
28
|
-
/*
|
|
29
|
-
|
|
28
|
+
/* Always `true` — hydration adopts every block in place, including `await`
|
|
29
|
+
(streamed value seeded through the resume manifest). */
|
|
30
30
|
hydratable: boolean
|
|
31
31
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/*
|
|
2
|
+
A name a block introduces into a body's scope, carried on its Plan and classified
|
|
3
|
+
ONCE as the single source of truth both back-ends read. `name` is the author param
|
|
4
|
+
as written (a plain identifier or a destructuring pattern — `item`, `i`, `_error`,
|
|
5
|
+
`{ a, b }`); its leaf names are derived where it is registered (`withBindings`),
|
|
6
|
+
never re-derived in a back-end. `classification` decides how each back-end RENDERS
|
|
7
|
+
the binding — the only per-back-end choice left:
|
|
8
|
+
|
|
9
|
+
- `reactive` — an `await` `then` value, an `each` item / index. The client binds a
|
|
10
|
+
`.value` cell the runtime can update in place (read as a deref via `reactiveBinding`);
|
|
11
|
+
SSR has no cells, so it renders it as a plain shadow.
|
|
12
|
+
- `plain` — a `catch` error, `snippet` args. A real JS local on both back-ends, read
|
|
13
|
+
as the bare identifier.
|
|
14
|
+
|
|
15
|
+
A binding that mis-lowers to the enclosing component signal is the `block-binding-shadow`
|
|
16
|
+
bug this model designs out: there is one name set and one classification per block, and
|
|
17
|
+
one registration loop (`withBindings`).
|
|
18
|
+
*/
|
|
19
|
+
export type Binding = {
|
|
20
|
+
/* The author param as written — a plain identifier or a destructuring pattern. */
|
|
21
|
+
name: string
|
|
22
|
+
/* How each back-end renders it: a reactive `.value` cell (client) vs a plain local. */
|
|
23
|
+
classification: 'reactive' | 'plain'
|
|
24
|
+
}
|
|
@@ -12,4 +12,16 @@ an offset in no mapping is synthesised scaffolding and its diagnostic is dropped
|
|
|
12
12
|
*/
|
|
13
13
|
export type ShadowMapping = { shadowStart: number; sourceStart: number; length: number }
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
/*
|
|
16
|
+
A diagnostic the shadow compiler raises itself (not from `tsc`), already located
|
|
17
|
+
in `.abide` source coordinates — `start`/`length` are an offset range in the
|
|
18
|
+
original file, so it needs no segment remap. Used for author rules the type
|
|
19
|
+
system can't express, e.g. importing a compiler-internal runtime helper.
|
|
20
|
+
*/
|
|
21
|
+
export type ShadowDiagnostic = { start: number; length: number; message: string }
|
|
22
|
+
|
|
23
|
+
export type CompiledShadow = {
|
|
24
|
+
code: string
|
|
25
|
+
mappings: ShadowMapping[]
|
|
26
|
+
diagnostics?: ShadowDiagnostic[]
|
|
27
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ClassStyleMergePlan } from '../classStyleMergePlan.ts'
|
|
2
|
+
import type { TemplateAttr } from './TemplateAttr.ts'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
The per-element shared compile decision both back-ends render from — the element-level
|
|
6
|
+
sibling of `skeletonContext`'s tree-level positional model, layered over it (ADR-0013).
|
|
7
|
+
It centralizes the DECISIONS the two code-gen back-ends (`generateBuild`, `generateSSR`)
|
|
8
|
+
previously made twice in hand-mirrored per-attribute dispatch: each attribute's kind, the
|
|
9
|
+
class/style/directive merge, which attrs are folded into that merge (so both skip them),
|
|
10
|
+
and whether the tag is void. Each back-end still RENDERS per kind — build wires live
|
|
11
|
+
effects/thunks, SSR escapes strings — but consults ONE classification, so the markup the
|
|
12
|
+
client clones and the server emits can never drift on the attribute set or merge logic.
|
|
13
|
+
|
|
14
|
+
The positional holes/anchors are NOT recomputed here — they stay in `skeletonContext` and
|
|
15
|
+
each back-end reads `elIndex`/`anIndex`/`markText` from it. `elementPlan` is the decision
|
|
16
|
+
on TOP of that positional pass, not a second positional model.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/* One classified attribute on an element. `attr` is the original `TemplateAttr`; `merged*`
|
|
20
|
+
flags it as folded into the class/style merge for that back-end (so the back-end skips it
|
|
21
|
+
in its per-attr dispatch). The merge TRIGGERS differ — build merges only when the base is
|
|
22
|
+
interpolated (a static base stays in the cloned skeleton with surgical toggles), SSR merges
|
|
23
|
+
whenever a directive exists (it must emit one attribute string) — so each side reads its own
|
|
24
|
+
flag. */
|
|
25
|
+
export type ElementPlanAttr = {
|
|
26
|
+
attr: TemplateAttr
|
|
27
|
+
/* Folded into the class/style merge on the build back-end (skip in build's dispatch). */
|
|
28
|
+
mergedBuild: boolean
|
|
29
|
+
/* Folded into the class/style merge on the SSR back-end (skip in SSR's dispatch). */
|
|
30
|
+
mergedSSR: boolean
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type ElementPlan = {
|
|
34
|
+
/* Every attribute classified in author order, each tagged with its per-back-end merge
|
|
35
|
+
status. Both back-ends iterate this one list and render per `attr.kind`. */
|
|
36
|
+
attrs: ElementPlanAttr[]
|
|
37
|
+
/* The class/style/directive merge decision (`classStyleMergePlan`) folded in as this
|
|
38
|
+
plan's class/style branch — both back-ends render the merged value from its parts. */
|
|
39
|
+
merge: ClassStyleMergePlan
|
|
40
|
+
/* A void tag (`<img>`, `<input>`) has no closing tag and no children — both back-ends
|
|
41
|
+
emit the open tag only. From the shared `VOID_TAGS` set. */
|
|
42
|
+
isVoid: boolean
|
|
43
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Which deref a branch-local shadow name lowers to. A block value param either binds
|
|
3
|
+
as a reactive `.value` cell (`derived` — read as `name.value`, the client back-end's
|
|
4
|
+
keyed-each item / `await then` value) or as a plain JS local holding the resolved
|
|
5
|
+
value (`plain` — read as the bare identifier, the SSR back-end's inline `await` value
|
|
6
|
+
and both back-ends' snippet/catch params). A name MUST be registered under the kind
|
|
7
|
+
its emitted code reads it as, or it mis-lowers to the component signal it shadows.
|
|
8
|
+
*/
|
|
9
|
+
export type ShadowKind = 'derived' | 'plain'
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ShadowKind } from './ShadowKind.ts'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
The typed branch-local shadow stack both lowering back-ends thread through. Replaces
|
|
5
|
+
the two loose `Set`s reached via closure with one value owning both kinds, and the
|
|
6
|
+
manual push-then-pop with a single structured `withShadow` that pushes on entry and
|
|
7
|
+
pops in a `finally` — so a branch's shadows cannot outlive the branch even if the body
|
|
8
|
+
throws (the SSR `await then` TDZ path threw mid-body and leaked under the old hand pop).
|
|
9
|
+
|
|
10
|
+
`names` snapshots a kind for the expression transformer: a binding pushed mid-compile
|
|
11
|
+
must be honoured AND shadow a same-named component signal.
|
|
12
|
+
*/
|
|
13
|
+
export type ShadowScope = {
|
|
14
|
+
/* Push the names not already present (under `kind`) for the duration of `body`, then
|
|
15
|
+
pop exactly what was added — in a `finally`, so a throw cannot leak the shadow. */
|
|
16
|
+
withShadow: <T>(names: Iterable<string>, kind: ShadowKind, body: () => T) => T
|
|
17
|
+
/* A snapshot of the names currently shadowing under `kind`, for the transformer. */
|
|
18
|
+
names: (kind: ShadowKind) => ReadonlySet<string>
|
|
19
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { TextPart } from './TextPart.ts'
|
|
2
|
+
|
|
1
3
|
/*
|
|
2
4
|
An element attribute. `static` is a literal — `bare` marks a valueless attribute
|
|
3
5
|
(`disabled`, not `disabled=""`), letting a component coerce it to `true` while a
|
|
@@ -15,6 +17,11 @@ optional, set only when the parser tracks positions for the type-checking shadow
|
|
|
15
17
|
export type TemplateAttr =
|
|
16
18
|
| { kind: 'static'; name: string; value: string; bare?: true; nameLoc?: number }
|
|
17
19
|
| { kind: 'expression'; name: string; code: string; loc?: number; nameLoc?: number }
|
|
20
|
+
/* `name="literal {expr}"` — a quoted value mixing literal text with `{expr}`
|
|
21
|
+
interpolations (the text-node `parts` model on an attribute). Bound reactively
|
|
22
|
+
as the template-literal concatenation of its parts; always yields a string, so
|
|
23
|
+
the attribute is always present. */
|
|
24
|
+
| { kind: 'interpolated'; name: string; parts: TextPart[]; nameLoc?: number }
|
|
18
25
|
| { kind: 'event'; event: string; code: string; loc?: number }
|
|
19
26
|
| { kind: 'bind'; property: string; code: string; loc?: number }
|
|
20
27
|
/* `class:<name>={code}` — toggles the class on/off by `code`'s truthiness, written
|
|
@@ -3,8 +3,8 @@ import type { TextPart } from './TextPart.ts'
|
|
|
3
3
|
|
|
4
4
|
/*
|
|
5
5
|
A parsed template node. `text` carries interpolation parts; `element` carries
|
|
6
|
-
attributes and children; `each` is the
|
|
7
|
-
|
|
6
|
+
attributes and children; `each` is the `{#for items as item}` control flow over a
|
|
7
|
+
list; `if`/`await`/`switch`/`try` are its control-flow siblings.
|
|
8
8
|
|
|
9
9
|
`loc` (where present) is the absolute offset of the node's primary expression in
|
|
10
10
|
the original `.abide` source — additive, set only when the parser tracks
|
|
@@ -12,7 +12,7 @@ positions for the type-checking shadow, ignored by the runtime back-ends.
|
|
|
12
12
|
*/
|
|
13
13
|
export type TemplateNode =
|
|
14
14
|
| { kind: 'text'; parts: TextPart[] }
|
|
15
|
-
| { kind: 'script'; code: string }
|
|
15
|
+
| { kind: 'script'; code: string; loc?: number }
|
|
16
16
|
/* A `<style>` declared in the template. `css` is its raw body, read structurally
|
|
17
17
|
so a `<style>` inside an expression isn't mistaken for one. The node stays in
|
|
18
18
|
place: it scopes its sibling subtree, so the front-end derives each scope
|
|
@@ -50,9 +50,9 @@ export type TemplateNode =
|
|
|
50
50
|
| {
|
|
51
51
|
kind: 'await'
|
|
52
52
|
promise: string
|
|
53
|
-
/* `then`
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
/* `then` in the await head (`{#await p then v}`) makes the block BLOCKING:
|
|
54
|
+
no pending branch, children are the resolved content bound to `as`, SSR
|
|
55
|
+
settles before the first flush. Absent → streaming. */
|
|
56
56
|
blocking: boolean
|
|
57
57
|
as: string | undefined
|
|
58
58
|
children: TemplateNode[]
|
|
@@ -93,9 +93,9 @@ export type TemplateNode =
|
|
|
93
93
|
children: TemplateNode[]
|
|
94
94
|
loc?: number
|
|
95
95
|
}
|
|
96
|
-
/* A
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
/* A `{#snippet row(item)}` snippet: a named, scope-capturing builder declared
|
|
97
|
+
once and called like a function (`{row(item)}`). `params` is the raw
|
|
98
|
+
parameter list from the parens (absent when there are no parameters). */
|
|
99
99
|
| {
|
|
100
100
|
kind: 'snippet'
|
|
101
101
|
name: string
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { destructureBindingNames } from './destructureBindingNames.ts'
|
|
2
|
+
import type { Binding } from './types/Binding.ts'
|
|
3
|
+
import type { ShadowKind } from './types/ShadowKind.ts'
|
|
4
|
+
import type { ShadowScope } from './types/ShadowScope.ts'
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
The ONE shared registration path for a block's bindings, built on `withShadow`. Iterates
|
|
8
|
+
a plan's `Binding[]` once, derives each binding's leaf names (`destructureBindingNames` —
|
|
9
|
+
the single name derivation), and registers them under the `ShadowKind` the injected
|
|
10
|
+
`kindMapping` returns, for the duration of `body`. The back-ends differ ONLY in the
|
|
11
|
+
mapping they inject: the client maps `reactive → derived` (a `.value` cell, its wiring
|
|
12
|
+
arranged separately via `reactiveBinding`) and `plain → plain`; SSR has no cells, so it
|
|
13
|
+
maps every binding → `plain`. A name a block introduces therefore flows to exactly one
|
|
14
|
+
shadow kind, decided by the plan's classification and one mapping — `block-binding-shadow`
|
|
15
|
+
(a name registered on one back-end but not the other, or under the wrong kind) is designed
|
|
16
|
+
out, not test-caught.
|
|
17
|
+
*/
|
|
18
|
+
export function withBindings<T>(
|
|
19
|
+
withShadow: ShadowScope['withShadow'],
|
|
20
|
+
bindings: Binding[],
|
|
21
|
+
kindMapping: (binding: Binding) => ShadowKind,
|
|
22
|
+
body: () => T,
|
|
23
|
+
): T {
|
|
24
|
+
/* Group each binding's leaf names by the kind the mapping assigns, so one `withShadow`
|
|
25
|
+
per kind registers them all (nested, so both kinds shadow simultaneously). */
|
|
26
|
+
const byKind = new Map<ShadowKind, string[]>()
|
|
27
|
+
for (const binding of bindings) {
|
|
28
|
+
const kind = kindMapping(binding)
|
|
29
|
+
const names = byKind.get(kind) ?? []
|
|
30
|
+
names.push(...destructureBindingNames(binding.name))
|
|
31
|
+
byKind.set(kind, names)
|
|
32
|
+
}
|
|
33
|
+
/* Fold the per-kind `withShadow` calls around `body` — each pushes its names on entry
|
|
34
|
+
and pops them in a `finally`, so no branch's shadows outlive the branch. */
|
|
35
|
+
const entries = [...byKind]
|
|
36
|
+
const run = (index: number): T => {
|
|
37
|
+
const entry = entries[index]
|
|
38
|
+
if (entry === undefined) {
|
|
39
|
+
return body()
|
|
40
|
+
}
|
|
41
|
+
const [kind, names] = entry
|
|
42
|
+
return withShadow(names, kind, () => run(index + 1))
|
|
43
|
+
}
|
|
44
|
+
return run(0)
|
|
45
|
+
}
|
|
@@ -13,7 +13,7 @@ import type { PersistHandle } from './types/PersistHandle.ts'
|
|
|
13
13
|
import type { Scope } from './types/Scope.ts'
|
|
14
14
|
|
|
15
15
|
/* A process-stable counter id. The serialization-stable LEXICAL id (route +
|
|
16
|
-
component + tree position) that `
|
|
16
|
+
component + tree position) that `persist`/`broadcast` want across reloads/peers
|
|
17
17
|
is stamped by the compiler later; until then a scope's id is unique within a
|
|
18
18
|
run — enough for in-session undo and the bus, and as an explicit key's fallback. */
|
|
19
19
|
let nextId = 0
|
|
@@ -23,7 +23,7 @@ Builds a lexical scope. Its data is a document — created eagerly from `initial
|
|
|
23
23
|
or (when `awaiting`) ADOPTED from the first `doc()` a component body creates under
|
|
24
24
|
it, so a scope can wrap the component's own model without changing the data
|
|
25
25
|
lowering. Data methods mirror `Doc` and delegate to that document; capabilities
|
|
26
|
-
are lazy (`
|
|
26
|
+
are lazy (`record`/`persist` attach `history`/`persist` to it on first call);
|
|
27
27
|
`child` nests; `dispose` tears the subtree down children-first.
|
|
28
28
|
*/
|
|
29
29
|
export function createScope(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
The MathML foreign-content namespace — the `<math>` counterpart to `SVG_NAMESPACE`.
|
|
3
|
-
The
|
|
4
|
-
|
|
3
|
+
The runtime reads it to namespace MathML children mounted into a `<math>` parent
|
|
4
|
+
dynamically, where no parser-built ancestor namespaces them for free.
|
|
5
5
|
*/
|
|
6
6
|
export const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML'
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/*
|
|
2
2
|
The SVG foreign-content namespace. An element created in it (or cloned from a
|
|
3
3
|
parser-built `<svg>` subtree) renders as SVG; the same tag in the HTML namespace
|
|
4
|
-
renders as nothing. The
|
|
5
|
-
|
|
4
|
+
renders as nothing. The runtime reads it (via `effectiveChildNamespace`) to namespace
|
|
5
|
+
foreign children mounted into a foreign parent dynamically — at a skeleton anchor or in a
|
|
6
|
+
control-flow fragment — where no parser-built `<svg>` ancestor namespaces them for free.
|
|
6
7
|
*/
|
|
7
8
|
export const SVG_NAMESPACE = 'http://www.w3.org/2000/svg'
|
|
@@ -16,7 +16,7 @@ node: created and appended (create), or the server-rendered text node claimed
|
|
|
16
16
|
claimed node is split at the current value's length — deterministic, because
|
|
17
17
|
`read()` returns the same value the server rendered.
|
|
18
18
|
|
|
19
|
-
A value branded by `html\`…\`` (see abide/
|
|
19
|
+
A value branded by `html\`…\`` (see abide/ui/html) inserts raw markup instead:
|
|
20
20
|
its parsed nodes go between an anchor (create), or the server-rendered nodes
|
|
21
21
|
between `<!--abide:html-->`/`<!--/abide:html-->` markers are adopted (hydrate), and
|
|
22
22
|
a change re-parses and swaps. A binding is text or raw for its lifetime (decided by
|
|
@@ -3,7 +3,7 @@ import { isComment } from './isComment.ts'
|
|
|
3
3
|
|
|
4
4
|
/*
|
|
5
5
|
Bundle-side consumer of an SSR stream chunk, the counterpart of the doc stream's inline
|
|
6
|
-
vanilla scripts (SSR_SWAP_SCRIPT's `__abideSwap`
|
|
6
|
+
vanilla scripts (`SSR_SWAP_SCRIPT`'s `__abideSwap` and `CACHE_RESOLVE_SCRIPT`'s `__abideResolve`) for a stream the
|
|
7
7
|
running bundle consumes itself — streaming SPA navigation, socket-delivered SSR. It routes
|
|
8
8
|
the two chunk kinds the stream interleaves:
|
|
9
9
|
|
|
@@ -6,9 +6,10 @@ import { enterNamespace } from './enterNamespace.ts'
|
|
|
6
6
|
Builds a `[ … ]`-bounded content range into a DETACHED fragment under a fresh reactive
|
|
7
7
|
scope, then returns the markers, the fragment, and the scope disposer — leaving
|
|
8
8
|
INSERTION to the caller. The create-path range builder for the keyed list runtimes
|
|
9
|
-
(`each` / `eachAsync`): `each` holds the fragment in
|
|
10
|
-
(its reconcile reorders rows), `eachAsync` inserts it
|
|
11
|
-
(arrival order).
|
|
9
|
+
(`each` / `eachAsync`) and `awaitBlock` (branch swaps): `each` holds the fragment in
|
|
10
|
+
`pending` for deferred placement (its reconcile reorders rows), `eachAsync` inserts it
|
|
11
|
+
immediately at the stream anchor (arrival order). All need the same bracketed-fragment
|
|
12
|
+
build, so it lives here once.
|
|
12
13
|
|
|
13
14
|
Unlike `openMarker`, this never touches a live parent or the hydrate claim cursor — the
|
|
14
15
|
markers are created fresh and parked in a fragment — so it is a pure CREATE primitive;
|
|
@@ -19,8 +19,9 @@ the same top-level node count as the server markup.
|
|
|
19
19
|
*/
|
|
20
20
|
// @documentation plumbing
|
|
21
21
|
export function cloneStatic(parent: Node, html: string): void {
|
|
22
|
-
/* When `parent` is a foreign element built
|
|
23
|
-
|
|
22
|
+
/* When `parent` is a foreign element built dynamically (not part of this static
|
|
23
|
+
run's own parser-built subtree), the run's markup has no foreign ancestor of
|
|
24
|
+
its own, so a bare `<path>` would parse into the HTML
|
|
24
25
|
namespace. Parse it inside a matching `<svg>`/`<math>` wrapper and clone the
|
|
25
26
|
wrapper's children — the parser namespaces them correctly. */
|
|
26
27
|
const wrapper = foreignWrapperTag(parent)
|
package/src/lib/ui/dom/each.ts
CHANGED
|
@@ -42,6 +42,10 @@ export function each<T>(
|
|
|
42
42
|
before: Node | null = null,
|
|
43
43
|
): void {
|
|
44
44
|
const rows = new Map<string, EachRow>()
|
|
45
|
+
/* Monotonic reconcile-pass id. Each reconcile stamps every surviving row with the
|
|
46
|
+
current pass, so prune is a `row.gen !== pass` int compare instead of building a
|
|
47
|
+
`Set` of present keys every reconcile. */
|
|
48
|
+
let generation = 0
|
|
45
49
|
/* Each row's scope, registered with the owner so every live row disposes on owner
|
|
46
50
|
teardown (the effect's own disposer only unsubscribes it from `items()`). */
|
|
47
51
|
const group = scopeGroup()
|
|
@@ -136,11 +140,26 @@ export function each<T>(
|
|
|
136
140
|
try {
|
|
137
141
|
const list = Array.isArray(source) ? source : [...source]
|
|
138
142
|
const keys = list.map(keyOf)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
143
|
+
generation += 1
|
|
144
|
+
const pass = generation
|
|
145
|
+
/* Resolve each desired key to its surviving row once, here, stamping it with
|
|
146
|
+
this pass — so prune below is an int compare (no `Set`) and the placement
|
|
147
|
+
walk reuses the resolved row instead of a second `rows.get`. Halving the map
|
|
148
|
+
lookups per reconcile is a real win once the harness's O(n) `nextSibling` no
|
|
149
|
+
longer swamps it. `undefined` marks a key with no surviving row — a build. */
|
|
150
|
+
const resolved: (EachRow | undefined)[] = new Array(keys.length)
|
|
151
|
+
for (let index = 0; index < keys.length; index += 1) {
|
|
152
|
+
const row = rows.get(keys[index] as string)
|
|
153
|
+
if (row !== undefined) {
|
|
154
|
+
row.gen = pass
|
|
155
|
+
}
|
|
156
|
+
resolved[index] = row
|
|
157
|
+
}
|
|
158
|
+
/* Prune departed rows first (those the resolve loop didn't reach this pass) so
|
|
159
|
+
their ranges don't sit between survivors and throw off the in-place sibling
|
|
160
|
+
checks below. */
|
|
142
161
|
for (const [key, row] of rows) {
|
|
143
|
-
if (
|
|
162
|
+
if (row.gen !== pass) {
|
|
144
163
|
row.dispose()
|
|
145
164
|
removeRange(row.start, row.end)
|
|
146
165
|
rows.delete(key)
|
|
@@ -151,11 +170,11 @@ export function each<T>(
|
|
|
151
170
|
freshly built) rows move. */
|
|
152
171
|
let cursor: Node = anchor
|
|
153
172
|
for (let index = list.length - 1; index >= 0; index -= 1) {
|
|
154
|
-
|
|
155
|
-
let row = rows.get(key)
|
|
173
|
+
let row = resolved[index]
|
|
156
174
|
if (row === undefined) {
|
|
157
175
|
row = buildRow(list[index] as T, index)
|
|
158
|
-
|
|
176
|
+
row.gen = pass
|
|
177
|
+
rows.set(keys[index] as string, row)
|
|
159
178
|
} else {
|
|
160
179
|
/* Surviving key: push the (possibly new) item and position into the row's
|
|
161
180
|
cells. Both write through `Object.is`, so an unchanged item/position is a
|
|
@@ -6,8 +6,8 @@ import { SVG_NAMESPACE } from './SVG_NAMESPACE.ts'
|
|
|
6
6
|
The wrapper tag a static run must be parsed inside so its children land in `parent`'s
|
|
7
7
|
foreign namespace — `svg`/`math`, or undefined for HTML. A bare `<path>` fragment
|
|
8
8
|
parses into the HTML namespace; wrapping it in `<svg>` lets the parser namespace it.
|
|
9
|
-
`cloneStatic` uses this for a static run coalesced under a foreign parent
|
|
10
|
-
|
|
9
|
+
`cloneStatic` uses this for a static run coalesced under a foreign parent built
|
|
10
|
+
dynamically, or under a control-flow block's fragment inside foreign content
|
|
11
11
|
(where `parent`'s effective namespace comes from the ambient context).
|
|
12
12
|
*/
|
|
13
13
|
export function foreignWrapperTag(parent: Node): string | undefined {
|
|
@@ -5,6 +5,13 @@ import { registerHotInstance } from '../runtime/registerHotInstance.ts'
|
|
|
5
5
|
import type { UiComponent } from '../runtime/types/UiComponent.ts'
|
|
6
6
|
import { mountRange } from './mountRange.ts'
|
|
7
7
|
|
|
8
|
+
/* Build-time flag the production client defines false (see build.ts `define`) so the hot
|
|
9
|
+
path below — and its captureModelDoc/registerHotInstance imports — dead-code-eliminate
|
|
10
|
+
(`!false` folds to a constant the minifier can prove) instead of shipping behind a runtime
|
|
11
|
+
flag it can't. Dev defines it true; the test preload sets it on globalThis so the bare
|
|
12
|
+
reference resolves to true there, leaving `hotReloadEnabled` alone in charge as before. */
|
|
13
|
+
declare const __ABIDE_DEV__: boolean
|
|
14
|
+
|
|
8
15
|
/*
|
|
9
16
|
Mounts a child component as a marker-bounded range at `before` in `parent` — no
|
|
10
17
|
wrapper element, so the child's root is a true direct child of the parent (see
|
|
@@ -24,7 +31,7 @@ export function mountChild(
|
|
|
24
31
|
label?: string,
|
|
25
32
|
): void {
|
|
26
33
|
const moduleId = factory.__abideId
|
|
27
|
-
if (!hotReloadEnabled.current || moduleId === undefined) {
|
|
34
|
+
if (!__ABIDE_DEV__ || !hotReloadEnabled.current || moduleId === undefined) {
|
|
28
35
|
mountRange(parent, factory.build, props, before, label)
|
|
29
36
|
return
|
|
30
37
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { effect } from '../effect.ts'
|
|
2
|
+
import { RANGE_CLOSE, RANGE_OPEN } from '../runtime/RANGE_MARKER.ts'
|
|
3
|
+
import { RENDER } from '../runtime/RENDER.ts'
|
|
4
|
+
import { scope } from '../runtime/scope.ts'
|
|
5
|
+
import { scopeGroup } from '../runtime/scopeGroup.ts'
|
|
6
|
+
import { fillBefore } from './fillBefore.ts'
|
|
7
|
+
import { openMarker } from './openMarker.ts'
|
|
8
|
+
import { replaceRange } from './replaceRange.ts'
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
The shared lifecycle of every single-slot control-flow block (`when`, `switchBlock`):
|
|
12
|
+
a marker-bounded RANGE that holds at most one piece of content at a time, swapped as a
|
|
13
|
+
unit when a `key` changes. The block supplies a `key()` thunk (the identity of the
|
|
14
|
+
chosen branch — `'then'|'else'` for `when`, the case index for `switchBlock`) and a
|
|
15
|
+
`contentFor(key)` resolver returning that branch's builder, or `undefined` for an
|
|
16
|
+
empty branch. This module owns the marker setup, the hydrate-vs-create fork, and the
|
|
17
|
+
swap effect with its reentrancy/teardown dance — the structure both blocks copy-pasted.
|
|
18
|
+
|
|
19
|
+
The chosen branch builds through `scope` (directly on hydrate, via `fillBefore` on a
|
|
20
|
+
swap), which builds untracked — so a raw reactive read in the branch content doesn't
|
|
21
|
+
subscribe the swap effect; only `key()` (which reads the block's source) drives the
|
|
22
|
+
toggle. The branch's own interpolations still track, each through its own effect.
|
|
23
|
+
|
|
24
|
+
On hydrate it adopts the server-rendered range: claim the start marker, run the chosen
|
|
25
|
+
branch in place (its content claims the existing nodes), then claim the end marker. The
|
|
26
|
+
effect's first run sees the same key and is a no-op; later changes clear the range and
|
|
27
|
+
build fresh into a fragment.
|
|
28
|
+
|
|
29
|
+
`before` (a static node located by the skeleton) places the range among siblings on
|
|
30
|
+
create, so the block sits before a static suffix rather than at the parent's end.
|
|
31
|
+
Hydrate ignores it — the claim cursor (positioned past the prefix) drives placement.
|
|
32
|
+
*/
|
|
33
|
+
export function mountSwappableRange<Key>(
|
|
34
|
+
parent: Node,
|
|
35
|
+
key: () => Key,
|
|
36
|
+
contentFor: (key: Key) => ((parent: Node) => void) | undefined,
|
|
37
|
+
before: Node | null = null,
|
|
38
|
+
): void {
|
|
39
|
+
const hydration = RENDER.hydration
|
|
40
|
+
/* The live branch's scope, registered with the owner so it disposes on owner
|
|
41
|
+
teardown — not only on a branch swap via replaceRange. */
|
|
42
|
+
const group = scopeGroup()
|
|
43
|
+
let dispose: (() => void) | undefined
|
|
44
|
+
let activeKey: Key
|
|
45
|
+
let end: Comment
|
|
46
|
+
|
|
47
|
+
const start = openMarker(parent, RANGE_OPEN, before)
|
|
48
|
+
if (hydration !== undefined) {
|
|
49
|
+
activeKey = key()
|
|
50
|
+
const chosen = contentFor(activeKey)
|
|
51
|
+
if (chosen !== undefined) {
|
|
52
|
+
dispose = group.track(scope(() => chosen(parent))) // content claims the SSR nodes in place
|
|
53
|
+
}
|
|
54
|
+
end = openMarker(parent, RANGE_CLOSE)
|
|
55
|
+
} else {
|
|
56
|
+
end = openMarker(parent, RANGE_CLOSE, before)
|
|
57
|
+
activeKey = key()
|
|
58
|
+
const chosen = contentFor(activeKey)
|
|
59
|
+
if (chosen !== undefined) {
|
|
60
|
+
dispose = group.track(fillBefore(end, chosen))
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
effect(() => {
|
|
65
|
+
const next = key()
|
|
66
|
+
if (next === activeKey) {
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
activeKey = next
|
|
70
|
+
const chosen = contentFor(next)
|
|
71
|
+
/* Null `dispose` before `replaceRange` builds the new branch: a reentrant swap
|
|
72
|
+
during that build (an effect in the new content writing the source) would
|
|
73
|
+
otherwise re-enter with the already-disposed disposer and clear it twice. */
|
|
74
|
+
const prior = dispose
|
|
75
|
+
dispose = undefined
|
|
76
|
+
const built = replaceRange(start, end, prior, chosen)
|
|
77
|
+
dispose = built !== undefined ? group.track(built) : undefined
|
|
78
|
+
})
|
|
79
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/*
|
|
2
2
|
Guarded method call on a reactive-document read. The doc-access lowering rewrites
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
a non-optional method call on a reactive-document read — e.g. `model.draft.trim()` —
|
|
4
|
+
directly to `$$readCall(model.read("draft"), "draft", "trim", [...])` rather than
|
|
5
|
+
the naive `model.read("draft").trim()`, which would throw the engine's opaque
|
|
6
|
+
`undefined is not an object (evaluating '…read("draft").trim()')`, naming only the
|
|
7
|
+
desugared call. This wraps the non-optional call so the throw names the authored
|
|
8
|
+
scope path and member instead —
|
|
7
9
|
the source-mapped stack frame still resolves to the `.abide` line, so message and
|
|
8
10
|
location together read in authored terms. Both opaque engine errors are covered: a
|
|
9
11
|
nullish receiver (`undefined is not an object`) AND a present receiver whose member
|
|
@@ -4,9 +4,9 @@ establishes, derived from its host element's tag. The router instead fills layer
|
|
|
4
4
|
marker boundaries with no host element, so it passes each layer's route key as an
|
|
5
5
|
explicit label (`fillBoundary`); a nested child likewise passes its component name (see
|
|
6
6
|
`mountRange`). The `abide-` prefix strip turns a framework host tag like `abide-resolve`
|
|
7
|
-
into `resolve`; any other host yields its lowercased tag.
|
|
8
|
-
|
|
9
|
-
to name from.
|
|
7
|
+
into `resolve`; any other host yields its lowercased tag. Feeds the inspector's
|
|
8
|
+
Reactive tab a readable scope name; computed and stored in all builds (only the
|
|
9
|
+
inspector surface is dev-only). Returns undefined when there's no element to name from.
|
|
10
10
|
*/
|
|
11
11
|
import { COMPONENT_WRAPPER_PREFIX } from '../COMPONENT_WRAPPER_PREFIX.ts'
|
|
12
12
|
|
|
@@ -129,10 +129,19 @@ export function skeleton(parent: Node, html: string): SkeletonHoles {
|
|
|
129
129
|
hydration.next.set(parent, node)
|
|
130
130
|
} else {
|
|
131
131
|
const children = source.childNodes
|
|
132
|
+
/* Stage clones in a fragment ONLY for a live (connected) parent, where one
|
|
133
|
+
append reflows once instead of per clone. A detached parent triggers no
|
|
134
|
+
reflow on append, so the fragment is pure overhead there — skip it and
|
|
135
|
+
append direct. `topLevel` collects each clone either way for anchor/
|
|
136
|
+
element-hole resolution. */
|
|
137
|
+
const target: Node = parent.isConnected ? document.createDocumentFragment() : parent
|
|
132
138
|
for (let index = 0; index < children.length; index += 1) {
|
|
133
139
|
const clone = (children[index] as Node).cloneNode(true)
|
|
134
140
|
topLevel.push(clone)
|
|
135
|
-
|
|
141
|
+
target.appendChild(clone)
|
|
142
|
+
}
|
|
143
|
+
if (target !== parent) {
|
|
144
|
+
parent.appendChild(target)
|
|
136
145
|
}
|
|
137
146
|
}
|
|
138
147
|
/* Anchor holes via the ONE shared ordering rule (`walkAnchorOrder`) — the same traversal
|