@abide/abide 0.49.0 → 0.50.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/AGENTS.md +70 -52
- package/CHANGELOG.md +177 -0
- package/README.md +12 -7
- package/package.json +14 -5
- package/src/abideLsp.ts +5 -6
- package/src/abideResolverPlugin.ts +197 -169
- package/src/build.ts +79 -56
- package/src/buildDisconnected.ts +0 -2
- package/src/checkAbide.ts +12 -2
- package/src/devEntry.ts +113 -28
- package/src/discoveryEntry.ts +3 -2
- package/src/lib/bundle/disconnected.abide +69 -73
- package/src/lib/bundle/infoPlist.ts +13 -7
- package/src/lib/bundle/spawnEmbeddedServer.ts +13 -4
- package/src/lib/bundle/waitForServer.ts +6 -1
- package/src/lib/mcp/mcpTools.ts +15 -6
- package/src/lib/server/error.ts +6 -6
- package/src/lib/server/json.ts +17 -3
- package/src/lib/server/rpc/defineRpc.ts +58 -24
- package/src/lib/server/rpc/parseArgs.ts +98 -13
- package/src/lib/server/rpc/resolveRpcJsonSchema.ts +28 -0
- package/src/lib/server/rpc/types/RpcHelper.ts +75 -105
- package/src/lib/server/rpc/types/RpcRegistryEntry.ts +24 -9
- package/src/lib/server/rpc/validationError.ts +1 -1
- package/src/lib/server/runtime/DEV_RELOAD_CLIENT_SCRIPT.ts +0 -15
- package/src/lib/server/runtime/buildCacheSnapshot.ts +10 -9
- package/src/lib/server/runtime/buildInspectorSurface.ts +6 -4
- package/src/lib/server/runtime/buildOpenApiSpec.ts +55 -11
- package/src/lib/server/runtime/buildPreloadManifest.ts +12 -10
- package/src/lib/server/runtime/createAppAssetServer.ts +18 -15
- package/src/lib/server/runtime/createAppRouteResolver.ts +145 -0
- package/src/lib/server/runtime/createPlumbingRouter.ts +212 -0
- package/src/lib/server/runtime/createRouteDispatcher.ts +4 -10
- package/src/lib/server/runtime/createServer.ts +120 -324
- package/src/lib/server/runtime/createUiPageRenderer.ts +137 -26
- package/src/lib/server/runtime/devClientFingerprint.ts +19 -34
- package/src/lib/server/runtime/installAmbientScopeStore.ts +30 -9
- package/src/lib/server/runtime/logExposedSurfaces.ts +8 -7
- package/src/lib/server/runtime/pathStore.ts +15 -0
- package/src/lib/server/runtime/registryManifests.ts +1 -1
- package/src/lib/server/runtime/renderCellBarrierStore.ts +15 -0
- package/src/lib/server/runtime/runWithRequestScope.ts +3 -0
- package/src/lib/server/runtime/serializeCacheSnapshot.ts +6 -4
- package/src/lib/server/runtime/snapshotEntryFromCache.ts +14 -14
- package/src/lib/server/runtime/streamCacheResolutions.ts +14 -7
- package/src/lib/server/runtime/streamFromIterator.ts +30 -2
- package/src/lib/server/runtime/textResponse.ts +23 -0
- package/src/lib/server/runtime/types/DevReloadStamp.ts +6 -10
- package/src/lib/server/runtime/types/InspectorCacheEntry.ts +1 -1
- package/src/lib/server/runtime/types/RequestStore.ts +27 -0
- package/src/lib/server/runtime/warnUnguardedMcp.ts +15 -5
- package/src/lib/server/sockets/createSocketDispatcher.ts +9 -11
- package/src/lib/server/sockets/defineSocket.ts +5 -4
- package/src/lib/server/sse.ts +5 -1
- package/src/lib/shared/ASYNC_CELL.ts +5 -0
- package/src/lib/shared/DEV_REBUILD_PATH.ts +6 -0
- package/src/lib/shared/HTTP_METHODS.ts +6 -0
- package/src/lib/shared/HttpError.ts +1 -5
- package/src/lib/shared/MCP_PATH.ts +6 -0
- package/src/lib/shared/PROXIED_SERVER_SUBDIRS.ts +15 -0
- package/src/lib/shared/REF_JSON_TAGS.ts +13 -1
- package/src/lib/shared/RPC_SHIM_GLOBALS.ts +9 -0
- package/src/lib/shared/activeCacheStore.ts +1 -0
- package/src/lib/shared/activePage.ts +1 -0
- package/src/lib/shared/buildArtifact.ts +4 -1
- package/src/lib/shared/buildSocketOverChannel.ts +4 -3
- package/src/lib/shared/bundleGraphFromMetafile.ts +68 -0
- package/src/lib/shared/cache.ts +156 -131
- package/src/lib/shared/changeAffectsClient.ts +12 -7
- package/src/lib/shared/createCacheStore.ts +31 -8
- package/src/lib/shared/createLifecycleChannel.ts +7 -1
- package/src/lib/shared/createReachable.ts +47 -78
- package/src/lib/shared/createRemoteFunction.ts +52 -30
- package/src/lib/shared/createRpcServerProgram.ts +820 -0
- package/src/lib/shared/decodeRefJson.ts +4 -4
- package/src/lib/shared/decodeResponse.ts +2 -2
- package/src/lib/shared/decodeWireBody.ts +35 -0
- package/src/lib/shared/detectRpcMethod.ts +8 -1
- package/src/lib/shared/done.ts +8 -2
- package/src/lib/shared/encodeRefJson.ts +4 -4
- package/src/lib/shared/encodeWireBody.ts +18 -0
- package/src/lib/{server/rpc → shared}/fieldErrorsFromIssues.ts +5 -1
- package/src/lib/shared/generateDeclarations.ts +72 -0
- package/src/lib/shared/hasSeedableRequest.ts +25 -0
- package/src/lib/shared/hydrationWindow.ts +53 -0
- package/src/lib/shared/isAsyncCell.ts +11 -0
- package/src/lib/shared/isAsyncIterable.ts +8 -0
- package/src/lib/shared/isSubscribable.ts +3 -3
- package/src/lib/shared/isThenable.ts +9 -0
- package/src/lib/shared/jsonSchemaForType.ts +258 -0
- package/src/lib/shared/lenientDecode.ts +15 -0
- package/src/lib/shared/loadProjectTsConfig.ts +28 -0
- package/src/lib/shared/markFrameworkSourcesIgnored.ts +1 -1
- package/src/lib/shared/matchRoute.ts +4 -14
- package/src/lib/shared/peek.ts +14 -0
- package/src/lib/shared/pending.ts +9 -6
- package/src/lib/shared/pendingAsyncCellsSlot.ts +13 -0
- package/src/lib/shared/prepareRpcModule.ts +91 -93
- package/src/lib/shared/prepareSocketModule.ts +3 -2
- package/src/lib/shared/probeRegistries.ts +5 -18
- package/src/lib/shared/reachable.ts +7 -10
- package/src/lib/shared/refresh.ts +12 -2
- package/src/lib/shared/refreshing.ts +8 -2
- package/src/lib/shared/resolvedCellsSlot.ts +13 -0
- package/src/lib/shared/reviveWireField.ts +49 -0
- package/src/lib/shared/reviveWireOutput.ts +36 -0
- package/src/lib/shared/rpcServerForRoot.ts +25 -0
- package/src/lib/shared/scanPages.ts +25 -0
- package/src/lib/shared/snapshotShippable.ts +8 -7
- package/src/lib/shared/streamedCellsSlot.ts +14 -0
- package/src/lib/shared/subscribableFromResponse.ts +4 -4
- package/src/lib/shared/subscribableProbes.ts +1 -1
- package/src/lib/shared/tailProbeSlot.ts +1 -1
- package/src/lib/shared/types/AsyncComputed.ts +20 -0
- package/src/lib/shared/types/AsyncState.ts +13 -0
- package/src/lib/shared/types/CacheEntry.ts +7 -7
- package/src/lib/shared/types/CacheOptions.ts +23 -37
- package/src/lib/shared/types/CachePolicy.ts +25 -0
- package/src/lib/shared/types/CacheSnapshotEntry.ts +6 -5
- package/src/lib/shared/types/ErrorJsonSchemas.ts +8 -0
- package/src/lib/shared/types/HttpMethod.ts +3 -1
- package/src/lib/shared/types/InputCoercion.ts +17 -0
- package/src/lib/shared/types/{Subscribable.ts → NamedAsyncIterable.ts} +1 -1
- package/src/lib/shared/types/OutputWirePlan.ts +17 -0
- package/src/lib/shared/types/PagesScan.ts +7 -0
- package/src/lib/shared/types/PendingAsyncCells.ts +10 -0
- package/src/lib/shared/types/RawRemoteFunction.ts +6 -0
- package/src/lib/shared/types/RemoteCallable.ts +8 -7
- package/src/lib/shared/types/RemoteFunction.ts +16 -15
- package/src/lib/shared/types/ResolvedCells.ts +11 -0
- package/src/lib/shared/types/ReturnBody.ts +15 -0
- package/src/lib/shared/types/RpcBuildStamps.ts +23 -0
- package/src/lib/shared/types/RpcErrorGuard.ts +3 -8
- package/src/lib/shared/types/Socket.ts +4 -4
- package/src/lib/shared/types/SsrPayload.ts +5 -1
- package/src/lib/shared/types/StreamPolicy.ts +11 -0
- package/src/lib/shared/types/StreamedCells.ts +19 -0
- package/src/lib/shared/types/StreamedResolution.ts +24 -6
- package/src/lib/shared/types/TailHooks.ts +1 -1
- package/src/lib/shared/types/WireKind.ts +11 -0
- package/src/lib/shared/validationHttpError.ts +33 -0
- package/src/lib/shared/warmSeedKey.ts +16 -0
- package/src/lib/shared/wireJsonReplacer.ts +30 -0
- package/src/lib/shared/writeRpcDts.ts +11 -1
- package/src/lib/shared/writeTestSocketsDts.ts +1 -1
- package/src/lib/test/createTestApp.ts +19 -1
- package/src/lib/ui/README.md +1 -1
- package/src/lib/ui/activePendingCells.ts +14 -0
- package/src/lib/ui/compile/BLOCK_KEYWORDS.ts +21 -0
- package/src/lib/ui/compile/SSR_ESCAPE.ts +4 -1
- package/src/lib/ui/compile/UI_RUNTIME_IMPORTS.ts +35 -8
- package/src/lib/ui/compile/abideUiPlugin.ts +19 -2
- package/src/lib/ui/compile/analyzeComponent.ts +89 -6
- package/src/lib/ui/compile/assignmentTargetNames.ts +54 -0
- package/src/lib/ui/compile/asyncInterpolationFields.ts +152 -0
- package/src/lib/ui/compile/asyncValuePositionError.ts +21 -0
- package/src/lib/ui/compile/asyncValuePositionInterpolations.ts +64 -0
- package/src/lib/ui/compile/attrLiftPosition.ts +18 -0
- package/src/lib/ui/compile/cachedSourceFile.ts +40 -0
- package/src/lib/ui/compile/catchBinding.ts +7 -5
- package/src/lib/ui/compile/classifyInterpolationType.ts +46 -0
- package/src/lib/ui/compile/collectAbideDiagnostics.ts +96 -0
- package/src/lib/ui/compile/compileComponent.ts +19 -3
- package/src/lib/ui/compile/compileModule.ts +27 -58
- package/src/lib/ui/compile/compileSSR.ts +51 -12
- package/src/lib/ui/compile/compileShadow.ts +145 -18
- package/src/lib/ui/compile/composeProps.ts +12 -2
- package/src/lib/ui/compile/createShadowLanguageService.ts +116 -68
- package/src/lib/ui/compile/createShadowProgram.ts +35 -6
- package/src/lib/ui/compile/declaredNames.ts +36 -0
- package/src/lib/ui/compile/desugarSignals.ts +418 -36
- package/src/lib/ui/compile/expressionIsPrefixEvaluable.ts +19 -0
- package/src/lib/ui/compile/generateBuild.ts +52 -9
- package/src/lib/ui/compile/generateSSR.ts +242 -43
- package/src/lib/ui/compile/hoistableAwaits.ts +193 -0
- package/src/lib/ui/compile/hoistableChildRenders.ts +112 -0
- package/src/lib/ui/compile/interpolatedTemplateLiteral.ts +3 -1
- package/src/lib/ui/compile/interpolationClassifierForRoot.ts +37 -0
- package/src/lib/ui/compile/isSpuriousAsyncReadDiagnostic.ts +174 -0
- package/src/lib/ui/compile/liftAsyncSubExpressions.ts +166 -0
- package/src/lib/ui/compile/lowerAsyncInterpolations.ts +92 -0
- package/src/lib/ui/compile/lowerContext.ts +10 -0
- package/src/lib/ui/compile/lowerDocAccess.ts +26 -23
- package/src/lib/ui/compile/lowerScript.ts +40 -4
- package/src/lib/ui/compile/nodeAtShadowOffset.ts +28 -0
- package/src/lib/ui/compile/parseTemplate.ts +18 -1092
- package/src/lib/ui/compile/parseTemplateRecovering.ts +1385 -0
- package/src/lib/ui/compile/referencedIdentifiers.ts +35 -0
- package/src/lib/ui/compile/renameSignalRefs.ts +21 -22
- package/src/lib/ui/compile/seedTypeClassifierForRoot.ts +65 -0
- package/src/lib/ui/compile/shadowInterpolationClassifier.ts +47 -0
- package/src/lib/ui/compile/sourceFileOptionsSignature.ts +20 -0
- package/src/lib/ui/compile/structuralHeadTokens.ts +107 -0
- package/src/lib/ui/compile/templateSemanticTokens.ts +21 -0
- package/src/lib/ui/compile/templateStartOffset.ts +15 -0
- package/src/lib/ui/compile/tryPlan.ts +4 -3
- package/src/lib/ui/compile/types/AnalyzedComponent.ts +4 -0
- package/src/lib/ui/compile/types/AsyncInterpolationField.ts +19 -0
- package/src/lib/ui/compile/types/InterpolationClassifier.ts +12 -0
- package/src/lib/ui/compile/types/InterpolationKind.ts +7 -0
- package/src/lib/ui/compile/types/ParseDiagnostic.ts +8 -0
- package/src/lib/ui/compile/types/SeedTypeClassifier.ts +15 -0
- package/src/lib/ui/compile/types/TemplateNode.ts +36 -4
- package/src/lib/ui/compile/types/ValuePositionInterpolation.ts +13 -0
- package/src/lib/ui/compile/writtenTemplateNames.ts +84 -0
- package/src/lib/ui/computed.ts +28 -1
- package/src/lib/ui/createScope.ts +35 -68
- package/src/lib/ui/dom/anchoredBranch.ts +142 -0
- package/src/lib/ui/dom/appendText.ts +8 -3
- package/src/lib/ui/dom/awaitBlock.ts +43 -94
- package/src/lib/ui/dom/bindProp.ts +22 -0
- package/src/lib/ui/dom/bindableProp.ts +47 -0
- package/src/lib/ui/dom/cellPending.ts +24 -0
- package/src/lib/ui/dom/disposeRange.ts +2 -1
- package/src/lib/ui/dom/each.ts +26 -4
- package/src/lib/ui/dom/eachAsync.ts +39 -3
- package/src/lib/ui/dom/fillBoundary.ts +2 -3
- package/src/lib/ui/dom/fillRange.ts +4 -4
- package/src/lib/ui/dom/hydrate.ts +6 -12
- package/src/lib/ui/dom/isComment.ts +1 -1
- package/src/lib/ui/dom/matchingRangeClose.ts +29 -0
- package/src/lib/ui/dom/mount.ts +1 -2
- package/src/lib/ui/dom/mountChild.ts +9 -40
- package/src/lib/ui/dom/mountRange.ts +2 -3
- package/src/lib/ui/dom/mountStreamedChild.ts +84 -0
- package/src/lib/ui/dom/mountSwappableRange.ts +46 -4
- package/src/lib/ui/dom/mutateDocContainer.ts +65 -0
- package/src/lib/ui/dom/on.ts +2 -2
- package/src/lib/ui/dom/readCell.ts +40 -0
- package/src/lib/ui/dom/switchBlock.ts +30 -7
- package/src/lib/ui/dom/tryBlock.ts +230 -66
- package/src/lib/ui/dom/types/SwitchCase.ts +5 -1
- package/src/lib/ui/dom/when.ts +12 -2
- package/src/lib/ui/dom/withScope.ts +2 -2
- package/src/lib/ui/finalizeStreamedChildren.ts +89 -0
- package/src/lib/ui/flight.ts +56 -0
- package/src/lib/ui/html.ts +12 -1
- package/src/lib/ui/isolateCellBarrier.ts +17 -0
- package/src/lib/ui/linked.ts +48 -2
- package/src/lib/ui/remoteProxy.ts +84 -159
- package/src/lib/ui/renderChain.ts +49 -13
- package/src/lib/ui/renderToStream.ts +22 -18
- package/src/lib/ui/resumeSeedScript.ts +1 -1
- package/src/lib/ui/router.ts +63 -36
- package/src/lib/ui/runtime/AsyncCellError.ts +20 -0
- package/src/lib/ui/runtime/CELL_SEED.ts +14 -0
- package/src/lib/ui/runtime/CURRENT_BOUNDARY.ts +11 -0
- package/src/lib/ui/runtime/CURRENT_PATH.ts +29 -0
- package/src/lib/ui/runtime/RENDER.ts +10 -7
- package/src/lib/ui/runtime/RESUME.ts +4 -4
- package/src/lib/ui/runtime/STREAMED_CELLS.ts +57 -0
- package/src/lib/ui/runtime/ambientPathBacking.ts +32 -0
- package/src/lib/ui/runtime/applyPatchToTree.ts +1 -1
- package/src/lib/ui/runtime/blockId.ts +31 -0
- package/src/lib/ui/runtime/boundaryFor.ts +11 -0
- package/src/lib/ui/runtime/cellBarrierBacking.ts +30 -0
- package/src/lib/ui/runtime/createAsyncCell.ts +300 -0
- package/src/lib/ui/runtime/createDoc.ts +3 -42
- package/src/lib/ui/runtime/createEffectNode.ts +9 -0
- package/src/lib/ui/runtime/enterRenderPass.ts +5 -4
- package/src/lib/ui/runtime/flushEffects.ts +16 -6
- package/src/lib/ui/runtime/isAsyncFunction.ts +14 -0
- package/src/lib/ui/runtime/nextBlockId.ts +8 -7
- package/src/lib/ui/runtime/renderPath.ts +16 -0
- package/src/lib/ui/runtime/types/Boundary.ts +9 -0
- package/src/lib/ui/runtime/types/RenderContext.ts +9 -7
- package/src/lib/ui/runtime/types/SsrRender.ts +9 -2
- package/src/lib/ui/runtime/types/UiComponent.ts +1 -5
- package/src/lib/ui/runtime/withOptionalPath.ts +8 -0
- package/src/lib/ui/runtime/withPath.ts +16 -0
- package/src/lib/ui/runtime/withPathFrom.ts +20 -0
- package/src/lib/ui/seedStreamedResolution.ts +31 -6
- package/src/lib/ui/settleAsyncCells.ts +24 -0
- package/src/lib/ui/socketProxy.ts +1 -1
- package/src/lib/ui/startClient.ts +16 -31
- package/src/lib/ui/trackedComputed.ts +68 -0
- package/src/lib/ui/types/Scope.ts +8 -24
- package/src/lib/ui/watch.ts +3 -3
- package/src/serverEntry.ts +14 -0
- package/template/src/server/rpc/getHello.ts +15 -13
- package/template/test/app.test.ts +1 -1
- package/src/lib/server/runtime/devHotModuleResponse.ts +0 -41
- package/src/lib/shared/DEV_HOT_PREFIX.ts +0 -7
- package/src/lib/shared/UNREACHABLE_STATUSES.ts +0 -13
- package/src/lib/shared/hasReplayableRequest.ts +0 -17
- package/src/lib/shared/hydratingSlot.ts +0 -12
- package/src/lib/shared/outboxProbeSlot.ts +0 -20
- package/src/lib/shared/types/Outbox.ts +0 -9
- package/src/lib/shared/types/OutboxEntry.ts +0 -27
- package/src/lib/shared/types/SmartReadOptions.ts +0 -36
- package/src/lib/shared/wakeHydrationPeeks.ts +0 -20
- package/src/lib/ui/COMPONENT_WRAPPER_PREFIX.ts +0 -5
- package/src/lib/ui/compile/REACTIVE_CALLEES.ts +0 -11
- package/src/lib/ui/compile/assertRuntimeHelpersBound.ts +0 -80
- package/src/lib/ui/compile/markupTokens.ts +0 -313
- package/src/lib/ui/compile/structuralBlockTokens.ts +0 -100
- package/src/lib/ui/dom/applyResolved.ts +0 -87
- package/src/lib/ui/dom/mutateDocArray.ts +0 -38
- package/src/lib/ui/dom/scopeLabel.ts +0 -21
- package/src/lib/ui/dom/text.ts +0 -20
- package/src/lib/ui/history.ts +0 -108
- package/src/lib/ui/installHotBridge.ts +0 -95
- package/src/lib/ui/installInspectorBridge.ts +0 -140
- package/src/lib/ui/outbox.ts +0 -35
- package/src/lib/ui/persist.ts +0 -115
- package/src/lib/ui/rpcOutbox/createOutboxQueue.ts +0 -281
- package/src/lib/ui/rpcOutbox/outboxRegistry.ts +0 -69
- package/src/lib/ui/runtime/PATCH_BUS.ts +0 -28
- package/src/lib/ui/runtime/captureModelDoc.ts +0 -43
- package/src/lib/ui/runtime/hotInstances.ts +0 -10
- package/src/lib/ui/runtime/hotReloadEnabled.ts +0 -8
- package/src/lib/ui/runtime/hotReplace.ts +0 -38
- package/src/lib/ui/runtime/liveScopes.ts +0 -15
- package/src/lib/ui/runtime/localStoragePersistence.ts +0 -47
- package/src/lib/ui/runtime/registerHotInstance.ts +0 -23
- package/src/lib/ui/runtime/seedModelDoc.ts +0 -26
- package/src/lib/ui/runtime/types/HotInstance.ts +0 -22
- package/src/lib/ui/runtime/types/PatchEvent.ts +0 -16
- package/src/lib/ui/seedResolved.ts +0 -28
- package/src/lib/ui/sync.ts +0 -48
- package/src/lib/ui/types/History.ts +0 -14
- package/src/lib/ui/types/PersistHandle.ts +0 -11
- package/src/lib/ui/types/PersistenceStore.ts +0 -12
- package/src/lib/ui/types/ResolvedFrame.ts +0 -15
- package/src/lib/ui/types/SyncTransport.ts +0 -13
package/src/lib/ui/dom/mount.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { scope } from '../runtime/scope.ts'
|
|
2
|
-
import { scopeLabel } from './scopeLabel.ts'
|
|
3
2
|
import { withScope } from './withScope.ts'
|
|
4
3
|
|
|
5
4
|
/*
|
|
@@ -23,7 +22,7 @@ export function mount(
|
|
|
23
22
|
/* Establish this component's lexical scope (nested, `awaiting` so it adopts the model
|
|
24
23
|
doc the build's first `doc()` creates) and render pass, run the build under it, and
|
|
25
24
|
restore the previous scope — the shared mount core (see `withScope`). */
|
|
26
|
-
const { lexical } = withScope(
|
|
25
|
+
const { lexical } = withScope(() => scope(() => build(host, props)))
|
|
27
26
|
return () => {
|
|
28
27
|
lexical.dispose()
|
|
29
28
|
host.textContent = ''
|
|
@@ -1,25 +1,12 @@
|
|
|
1
|
-
import { captureModelDoc } from '../runtime/captureModelDoc.ts'
|
|
2
|
-
import { hotReloadEnabled } from '../runtime/hotReloadEnabled.ts'
|
|
3
1
|
import { OWNER } from '../runtime/OWNER.ts'
|
|
4
|
-
import { registerHotInstance } from '../runtime/registerHotInstance.ts'
|
|
5
2
|
import type { UiComponent } from '../runtime/types/UiComponent.ts'
|
|
3
|
+
import { withOptionalPath } from '../runtime/withOptionalPath.ts'
|
|
6
4
|
import { mountRange } from './mountRange.ts'
|
|
7
5
|
|
|
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
|
-
|
|
15
6
|
/*
|
|
16
7
|
Mounts a child component as a marker-bounded range at `before` in `parent` — no
|
|
17
8
|
wrapper element, so the child's root is a true direct child of the parent (see
|
|
18
|
-
`mountRange`).
|
|
19
|
-
range mount with the component's own `build`. Hot path (hotReloadEnabled and the
|
|
20
|
-
factory carries a module id): keep the mount handle (its range markers + disposer)
|
|
21
|
-
and record the instance so an edit can re-fill the same range in place (see
|
|
22
|
-
`hotReplace`), and file a cleanup with the mounting owner so the record and its scope
|
|
9
|
+
`mountRange`). Files the range's dispose with the mounting owner so its scope and DOM
|
|
23
10
|
leave together when the parent (or branch/row) tears down.
|
|
24
11
|
*/
|
|
25
12
|
// @documentation plumbing
|
|
@@ -28,30 +15,12 @@ export function mountChild(
|
|
|
28
15
|
factory: UiComponent,
|
|
29
16
|
props: Parameters<UiComponent>[1],
|
|
30
17
|
before: Node | null = null,
|
|
31
|
-
|
|
18
|
+
/* The compiler's source-order ordinal for this `<Child/>` mount site — pushed onto the render
|
|
19
|
+
path so the child's scope (and its cells) get a serialization-stable id under this parent
|
|
20
|
+
(two same-type siblings differ by ordinal; the same site across `{#each}` rows differs by the
|
|
21
|
+
row key the each block already pushed). Absent (a non-compiled caller) → no path segment. */
|
|
22
|
+
ordinal?: number,
|
|
32
23
|
): void {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
mountRange(parent, factory.build, props, before, label)
|
|
36
|
-
return
|
|
37
|
-
}
|
|
38
|
-
/* Capture the component's model alongside its mount handle, so a later swap can
|
|
39
|
-
carry its state across (see `hotReplace`). */
|
|
40
|
-
const { value: handle, model } = captureModelDoc(() =>
|
|
41
|
-
mountRange(parent, factory.build, props, before, label),
|
|
42
|
-
)
|
|
43
|
-
const instance = {
|
|
44
|
-
factory,
|
|
45
|
-
props,
|
|
46
|
-
label,
|
|
47
|
-
start: handle.start,
|
|
48
|
-
end: handle.end,
|
|
49
|
-
dispose: handle.dispose,
|
|
50
|
-
model,
|
|
51
|
-
}
|
|
52
|
-
const remove = registerHotInstance(moduleId, instance)
|
|
53
|
-
OWNER.current?.push(() => {
|
|
54
|
-
instance.dispose()
|
|
55
|
-
remove()
|
|
56
|
-
})
|
|
24
|
+
const handle = withOptionalPath(ordinal, () => mountRange(parent, factory.build, props, before))
|
|
25
|
+
OWNER.current?.push(handle.dispose)
|
|
57
26
|
}
|
|
@@ -29,18 +29,17 @@ export function mountRange(
|
|
|
29
29
|
build: (host: Node, props?: UiProps) => void,
|
|
30
30
|
props: UiProps | undefined,
|
|
31
31
|
before: Node | null = null,
|
|
32
|
-
label: string | undefined = undefined,
|
|
33
32
|
): { start: Comment; end: Comment; dispose: () => void } {
|
|
34
33
|
const hydration = RENDER.hydration
|
|
35
34
|
const start = openMarker(parent, RANGE_OPEN, before)
|
|
36
35
|
if (hydration === undefined) {
|
|
37
36
|
const end = openMarker(parent, RANGE_CLOSE, before)
|
|
38
|
-
return fillRange(start, end, build, props
|
|
37
|
+
return fillRange(start, end, build, props)
|
|
39
38
|
}
|
|
40
39
|
/* Hydrate: adopt the server range in place. Establish the child's lexical scope
|
|
41
40
|
and render pass (same as `fillRange`), build claiming the existing nodes, then
|
|
42
41
|
claim the end marker the build's content stops before. */
|
|
43
|
-
const scoped = withScope(
|
|
42
|
+
const scoped = withScope(() => scope(() => build(parent, props)))
|
|
44
43
|
const end = openMarker(parent, RANGE_CLOSE)
|
|
45
44
|
return { start, end, dispose: disposeRange(scoped, start, end) }
|
|
46
45
|
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { CURRENT_PATH } from '../runtime/CURRENT_PATH.ts'
|
|
2
|
+
import { claimChild } from '../runtime/claimChild.ts'
|
|
3
|
+
import { OWNER } from '../runtime/OWNER.ts'
|
|
4
|
+
import { RANGE_OPEN } from '../runtime/RANGE_MARKER.ts'
|
|
5
|
+
import { RENDER } from '../runtime/RENDER.ts'
|
|
6
|
+
import type { UiComponent } from '../runtime/types/UiComponent.ts'
|
|
7
|
+
import { withOptionalPath } from '../runtime/withOptionalPath.ts'
|
|
8
|
+
import { withoutHydration } from '../runtime/withoutHydration.ts'
|
|
9
|
+
import { discardBoundary } from './discardBoundary.ts'
|
|
10
|
+
import { mountRange } from './mountRange.ts'
|
|
11
|
+
import { openMarker } from './openMarker.ts'
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
The client mount for a HOISTABLE child (ADR-0039) — a DUAL-MODE adopter. The server decides per
|
|
15
|
+
render whether a hoistable child inlined (fast/settled) or streamed (still-pending), and the client
|
|
16
|
+
build can't know which, so this probes the hydration cursor:
|
|
17
|
+
|
|
18
|
+
- `<!--[-->` (RANGE_OPEN) next → the server INLINED the child; adopt its range exactly as
|
|
19
|
+
`mountChild` does (byte-identical to the pre-ADR-0039 path).
|
|
20
|
+
- `<!--abide:await:CHILDPATH-->` next → the server STREAMED it; `__abideSwap` has already spliced
|
|
21
|
+
the child's `<!--[-->…<!--]-->` between the boundary markers before the bundle ran, so claim the
|
|
22
|
+
boundary open, adopt the inner range in place, claim the boundary close.
|
|
23
|
+
- no hydration (a client-side navigation) → a plain create-mode mount, like `mountChild`.
|
|
24
|
+
|
|
25
|
+
CHILDPATH is the child's render-path — `withPath(ordinal, () => CURRENT_PATH.current)` — computed the
|
|
26
|
+
same way the server's `renderPath(ordinal)` wrote the boundary id, so the two agree with no counter.
|
|
27
|
+
A streamed child re-mounts (no RESUME value of its own — the boundary is html-only); nested `{#await}`
|
|
28
|
+
blocks and async cells inside it adopt through their own existing channels.
|
|
29
|
+
*/
|
|
30
|
+
// @documentation plumbing
|
|
31
|
+
export function mountStreamedChild(
|
|
32
|
+
parent: Node,
|
|
33
|
+
factory: UiComponent,
|
|
34
|
+
props: Parameters<UiComponent>[1],
|
|
35
|
+
before: Node | null = null,
|
|
36
|
+
ordinal?: number,
|
|
37
|
+
): void {
|
|
38
|
+
const run = <T>(build: () => T): T => withOptionalPath(ordinal, build)
|
|
39
|
+
const mount = (): { dispose: () => void } =>
|
|
40
|
+
run(() => mountRange(parent, factory.build, props, before))
|
|
41
|
+
|
|
42
|
+
const hydration = RENDER.hydration
|
|
43
|
+
if (hydration === undefined) {
|
|
44
|
+
/* Call mount() first, THEN register its dispose — `OWNER.current?.push(mount())` would
|
|
45
|
+
short-circuit the whole expression (never mounting) when there is no owner. */
|
|
46
|
+
const handle = mount()
|
|
47
|
+
OWNER.current?.push(handle.dispose)
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* Probe the cursor without consuming it. Streamed iff the next node is this child's boundary. */
|
|
52
|
+
const childPath = run(() => CURRENT_PATH.current)
|
|
53
|
+
const cursor = claimChild(hydration, parent)
|
|
54
|
+
const streamed =
|
|
55
|
+
cursor !== null &&
|
|
56
|
+
cursor.nodeType === 8 &&
|
|
57
|
+
(cursor as Comment).data === `abide:await:${childPath}`
|
|
58
|
+
|
|
59
|
+
if (!streamed) {
|
|
60
|
+
/* INLINE — the server inlined a settled child; adopt its `[ … ]` range in place. */
|
|
61
|
+
const handle = mount()
|
|
62
|
+
OWNER.current?.push(handle.dispose)
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* STREAMED — claim the boundary open (advances the cursor to the swapped-in `[`). */
|
|
67
|
+
const open = openMarker(parent, `abide:await:${childPath}`)
|
|
68
|
+
const inner = claimChild(hydration, parent)
|
|
69
|
+
const warm = inner !== null && inner.nodeType === 8 && (inner as Comment).data === RANGE_OPEN
|
|
70
|
+
if (warm) {
|
|
71
|
+
const handle = mount()
|
|
72
|
+
openMarker(parent, `/abide:await:${childPath}`)
|
|
73
|
+
OWNER.current?.push(handle.dispose)
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
/* COLD (defensive — renderToStream drains every await before closing, so a warm boundary is the
|
|
77
|
+
deterministic path). The fragment never arrived: discard the empty boundary and create-mount
|
|
78
|
+
the child fresh at that position; its streamed cells resolve post-mount via receiveStreamedCell. */
|
|
79
|
+
const after = discardBoundary(parent, open, `/abide:await:${childPath}`, hydration)
|
|
80
|
+
const handle = withoutHydration(() =>
|
|
81
|
+
run(() => mountRange(parent, factory.build, props, after)),
|
|
82
|
+
)
|
|
83
|
+
OWNER.current?.push(handle.dispose)
|
|
84
|
+
}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { effect } from '../effect.ts'
|
|
2
|
+
import { CURRENT_PATH } from '../runtime/CURRENT_PATH.ts'
|
|
2
3
|
import { RANGE_CLOSE, RANGE_OPEN } from '../runtime/RANGE_MARKER.ts'
|
|
3
4
|
import { RENDER } from '../runtime/RENDER.ts'
|
|
4
5
|
import { scope } from '../runtime/scope.ts'
|
|
5
6
|
import { scopeGroup } from '../runtime/scopeGroup.ts'
|
|
7
|
+
import { withPathFrom } from '../runtime/withPathFrom.ts'
|
|
8
|
+
import { clearBetween } from './clearBetween.ts'
|
|
6
9
|
import { fillBefore } from './fillBefore.ts'
|
|
10
|
+
import { matchingRangeClose } from './matchingRangeClose.ts'
|
|
7
11
|
import { openMarker } from './openMarker.ts'
|
|
8
12
|
import { replaceRange } from './replaceRange.ts'
|
|
9
13
|
|
|
@@ -35,6 +39,15 @@ export function mountSwappableRange<Key>(
|
|
|
35
39
|
key: () => Key,
|
|
36
40
|
contentFor: (key: Key) => ((parent: Node) => void) | undefined,
|
|
37
41
|
before: Node | null = null,
|
|
42
|
+
/* The chosen branch MAY differ between SSR and the client's first render — set only for an
|
|
43
|
+
ASYNC subject (a bare async `{#if}`/`{#switch}`, or an async cond-chain branch), whose cell
|
|
44
|
+
can be settled on the server (a barrier-baked value) yet pending on the client, so each side
|
|
45
|
+
selects a different branch. Adopting SSR nodes in place then orphans them (they are neither
|
|
46
|
+
claimed nor cleared) and the next swap duplicates. When set, hydration DISCARDS the SSR range
|
|
47
|
+
content and builds the client's branch fresh instead — correct regardless of what the server
|
|
48
|
+
rendered. Left false for a sync range, where SSR and client always agree and in-place
|
|
49
|
+
adoption (node reuse, preserved focus/scroll) is safe. */
|
|
50
|
+
mayDiverge = false,
|
|
38
51
|
): void {
|
|
39
52
|
const hydration = RENDER.hydration
|
|
40
53
|
/* The live branch's scope, registered with the owner so it disposes on owner
|
|
@@ -44,10 +57,39 @@ export function mountSwappableRange<Key>(
|
|
|
44
57
|
let activeKey: Key
|
|
45
58
|
let end: Comment
|
|
46
59
|
|
|
60
|
+
/* Capture the render path at construction (this block's ancestry), and wrap each chosen
|
|
61
|
+
branch's builder so its content builds under `basePath/branchKey` — the key is the swap
|
|
62
|
+
discriminator (`'then'`/`'else'` or a case index), so a component/cell in one branch gets a
|
|
63
|
+
distinct, stable id from the other, re-established on every reactive swap (when the ambient
|
|
64
|
+
path is gone). An empty branch (`undefined`) needs no wrap. */
|
|
65
|
+
const basePath = CURRENT_PATH.current
|
|
66
|
+
const chosenFor = (branchKey: Key): ((parent: Node) => void) | undefined => {
|
|
67
|
+
const content = contentFor(branchKey)
|
|
68
|
+
if (content === undefined) {
|
|
69
|
+
return undefined
|
|
70
|
+
}
|
|
71
|
+
return (host) => withPathFrom(basePath, String(branchKey), () => content(host))
|
|
72
|
+
}
|
|
73
|
+
|
|
47
74
|
const start = openMarker(parent, RANGE_OPEN, before)
|
|
48
|
-
if (hydration !== undefined) {
|
|
75
|
+
if (hydration !== undefined && mayDiverge) {
|
|
76
|
+
/* Async range: don't trust the SSR content to match the client's branch. Discard the
|
|
77
|
+
server's rendered range (balanced `[`…`]`), park the cursor on the real close so it
|
|
78
|
+
claims correctly, then build the client's chosen branch FRESH (`fillBefore` clears the
|
|
79
|
+
claim cursor for the build). Usually the client is pending here → nothing built → an
|
|
80
|
+
empty range the later reactive swap fills, matching a clean create. */
|
|
81
|
+
activeKey = key()
|
|
82
|
+
const chosen = chosenFor(activeKey)
|
|
83
|
+
const matchedEnd = matchingRangeClose(start)
|
|
84
|
+
clearBetween(start, matchedEnd)
|
|
85
|
+
hydration.next.set(parent, matchedEnd)
|
|
86
|
+
end = openMarker(parent, RANGE_CLOSE)
|
|
87
|
+
if (chosen !== undefined) {
|
|
88
|
+
dispose = group.track(fillBefore(end, chosen))
|
|
89
|
+
}
|
|
90
|
+
} else if (hydration !== undefined) {
|
|
49
91
|
activeKey = key()
|
|
50
|
-
const chosen =
|
|
92
|
+
const chosen = chosenFor(activeKey)
|
|
51
93
|
if (chosen !== undefined) {
|
|
52
94
|
dispose = group.track(scope(() => chosen(parent))) // content claims the SSR nodes in place
|
|
53
95
|
}
|
|
@@ -55,7 +97,7 @@ export function mountSwappableRange<Key>(
|
|
|
55
97
|
} else {
|
|
56
98
|
end = openMarker(parent, RANGE_CLOSE, before)
|
|
57
99
|
activeKey = key()
|
|
58
|
-
const chosen =
|
|
100
|
+
const chosen = chosenFor(activeKey)
|
|
59
101
|
if (chosen !== undefined) {
|
|
60
102
|
dispose = group.track(fillBefore(end, chosen))
|
|
61
103
|
}
|
|
@@ -67,7 +109,7 @@ export function mountSwappableRange<Key>(
|
|
|
67
109
|
return
|
|
68
110
|
}
|
|
69
111
|
activeKey = next
|
|
70
|
-
const chosen =
|
|
112
|
+
const chosen = chosenFor(next)
|
|
71
113
|
/* Null `dispose` before `replaceRange` builds the new branch: a reentrant swap
|
|
72
114
|
during that build (an effect in the new content writing the source) would
|
|
73
115
|
otherwise re-enter with the already-disposed disposer and clear it twice. */
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { Doc } from '../runtime/types/Doc.ts'
|
|
2
|
+
import { readCall } from './readCall.ts'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
An in-place-mutating container method lowered on a reactive-document value. The
|
|
6
|
+
doc-access lowering rewrites e.g. `model.items.splice(i, 1)` to
|
|
7
|
+
`$$mutateDocContainer(model, "items", "splice", [i, 1])` rather than the bare
|
|
8
|
+
`$$readCall(model.read("items"), …)` it emits for non-mutating calls. The reason:
|
|
9
|
+
`read` returns the live tree value by reference, so a bare `.splice()`/`.sort()`/
|
|
10
|
+
`.add()`/`.set()`/… would mutate the document in place while never emitting a
|
|
11
|
+
patch — so no reader would re-render (`.push` is the one already-handled exception,
|
|
12
|
+
lowered to `add` patches). Cloning the container, applying the mutation to the copy,
|
|
13
|
+
and writing it back through `replace` emits a real patch: readers wake.
|
|
14
|
+
|
|
15
|
+
Covers the three mutable containers the doc codec serializes (encodeRefJson):
|
|
16
|
+
Array (cloned by `slice`), Map (`new Map`) and Set (`new Set`) — the mutating
|
|
17
|
+
method names are disjoint across the three (`pop`/`splice`/… vs `add`/`set`/…), so
|
|
18
|
+
the caller routes them all here and the actual kind is decided at runtime by the
|
|
19
|
+
value's type, no compile-time check needed. Element identity is preserved for
|
|
20
|
+
untouched entries (a shallow copy), so a keyed `{#for}` reconciles instead of
|
|
21
|
+
rebuilding. The native method's return value (spliced-out elements, the new
|
|
22
|
+
length, the deleted flag, …) is returned unchanged.
|
|
23
|
+
*/
|
|
24
|
+
// @documentation plumbing
|
|
25
|
+
export function mutateDocContainer(
|
|
26
|
+
doc: Doc,
|
|
27
|
+
path: string,
|
|
28
|
+
member: string,
|
|
29
|
+
args: unknown[],
|
|
30
|
+
): unknown {
|
|
31
|
+
const current = doc.read<unknown>(path)
|
|
32
|
+
const copy = cloneContainer(current)
|
|
33
|
+
/* Not a container we clone (array/Map/Set) — fall back to the guarded in-place call so
|
|
34
|
+
the author still gets readCall's authored-scope error message (a plain doc object has
|
|
35
|
+
no methods, so this is the same throw the bare call would raise). */
|
|
36
|
+
if (copy === undefined) {
|
|
37
|
+
return readCall(current, path, member, args)
|
|
38
|
+
}
|
|
39
|
+
const method = (copy as unknown as Record<string, unknown>)[member]
|
|
40
|
+
if (typeof method !== 'function') {
|
|
41
|
+
throw new TypeError(
|
|
42
|
+
`abide: cannot call .${member}() — "${path}".${member} is not a function (got ${typeof method})`,
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
const returned = (method as (...callArgs: unknown[]) => unknown).apply(copy, args)
|
|
46
|
+
doc.replace(path, copy)
|
|
47
|
+
return returned
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* A shallow clone of a mutable doc container, or undefined when the value is not one — the
|
|
51
|
+
shallow copy preserves element/entry identity so keyed reconciliation survives the patch. */
|
|
52
|
+
function cloneContainer(
|
|
53
|
+
current: unknown,
|
|
54
|
+
): unknown[] | Map<unknown, unknown> | Set<unknown> | undefined {
|
|
55
|
+
if (Array.isArray(current)) {
|
|
56
|
+
return current.slice()
|
|
57
|
+
}
|
|
58
|
+
if (current instanceof Map) {
|
|
59
|
+
return new Map(current)
|
|
60
|
+
}
|
|
61
|
+
if (current instanceof Set) {
|
|
62
|
+
return new Set(current)
|
|
63
|
+
}
|
|
64
|
+
return undefined
|
|
65
|
+
}
|
package/src/lib/ui/dom/on.ts
CHANGED
|
@@ -8,8 +8,8 @@ Attaches an event listener and registers its removal with the current ownership
|
|
|
8
8
|
scope, so a component's listeners detach when it disposes. This is the runtime
|
|
9
9
|
target for an `onclick={…}` binding; the handler body is where the compiler's
|
|
10
10
|
lowered patches (`model.replace(...)`) run. The handler is pinned to the scope it
|
|
11
|
-
was attached under, so an ambient `scope()` inside it
|
|
12
|
-
|
|
11
|
+
was attached under, so an ambient `scope()` inside it resolves the component, not
|
|
12
|
+
whatever is current when the event fires.
|
|
13
13
|
*/
|
|
14
14
|
// @documentation plumbing
|
|
15
15
|
export function on(element: Element, type: string, handler: EventListener): void {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { isAsyncCell } from '../../shared/isAsyncCell.ts'
|
|
2
|
+
import type { AsyncComputed } from '../../shared/types/AsyncComputed.ts'
|
|
3
|
+
import { AsyncCellError } from '../runtime/AsyncCellError.ts'
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
Unified read for a `computed`/`linked` reference the compiler lowers to `$$readCell(NAME)`:
|
|
7
|
+
an async cell (`AsyncComputed`/`AsyncState`) yields its latest retained value, a `derive`
|
|
8
|
+
reader (a function) is called, and a sync `Computed`/`State` yields its `.value`. One read
|
|
9
|
+
shape lets a `linked`/`computed` binding auto-track whichever source it resolved to — a
|
|
10
|
+
settling promise, a stream, or a plain value — with no read-site branching in codegen.
|
|
11
|
+
|
|
12
|
+
For an async cell this is a **throwing peek** (ADR-0019 D3.2), the value-aware rule that
|
|
13
|
+
routes a "nothing to render" error to the nearest `{#try}`:
|
|
14
|
+
- error AND no retained value → throw `AsyncCellError` (carries the cell for keep-the-watch);
|
|
15
|
+
- error WITH a retained value → the value (a failed background refresh stays visible — SWR);
|
|
16
|
+
- pending (no value) → `undefined`;
|
|
17
|
+
- resolved → the value.
|
|
18
|
+
The throw is a codegen behaviour only — `cell.peek()`/`cell.error()` in JS never throw; the
|
|
19
|
+
author handles an error locally via the probes, or lets it reach a `{#try}`.
|
|
20
|
+
*/
|
|
21
|
+
// @documentation plumbing
|
|
22
|
+
export function readCell(cell: unknown): unknown {
|
|
23
|
+
if (isAsyncCell(cell)) {
|
|
24
|
+
const asyncCell = cell as AsyncComputed<unknown>
|
|
25
|
+
const value = asyncCell.peek()
|
|
26
|
+
/* A retained value wins over an error (SWR) and over pending — return it. Only when
|
|
27
|
+
nothing is retained does an error become "nothing to render" and throw. */
|
|
28
|
+
if (value === undefined) {
|
|
29
|
+
const error = asyncCell.error()
|
|
30
|
+
if (error !== undefined) {
|
|
31
|
+
throw new AsyncCellError(asyncCell, error)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return value
|
|
35
|
+
}
|
|
36
|
+
if (typeof cell === 'function') {
|
|
37
|
+
return (cell as () => unknown)()
|
|
38
|
+
}
|
|
39
|
+
return (cell as { value: unknown }).value
|
|
40
|
+
}
|
|
@@ -18,22 +18,45 @@ export function switchBlock(
|
|
|
18
18
|
subject: () => unknown,
|
|
19
19
|
cases: SwitchCase[],
|
|
20
20
|
before: Node | null = null,
|
|
21
|
+
isPending?: () => boolean,
|
|
21
22
|
): void {
|
|
22
|
-
/*
|
|
23
|
-
|
|
23
|
+
/* Walk the cases in source order (the same order a `{#if}`/`{:elseif}` chain reads): the
|
|
24
|
+
first whose own async condition is still `pending()` HOLDS the chain — returns -1, so no
|
|
25
|
+
later branch renders on a not-yet-known earlier condition; the first whose `match() ===`
|
|
26
|
+
the subject wins; a match-less `default` is the fallback once every case settled without a
|
|
27
|
+
match. A plain `{#switch}` (no per-case `pending`) reduces to first-match-else-default. */
|
|
24
28
|
const select = (value: unknown): number => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
let fallback = -1
|
|
30
|
+
for (let index = 0; index < cases.length; index++) {
|
|
31
|
+
const entry = cases[index]
|
|
32
|
+
if (entry === undefined) {
|
|
33
|
+
continue
|
|
34
|
+
}
|
|
35
|
+
if (entry.pending?.()) {
|
|
36
|
+
return -1
|
|
37
|
+
}
|
|
38
|
+
if (entry.match === undefined) {
|
|
39
|
+
fallback = index
|
|
40
|
+
} else if (entry.match() === value) {
|
|
41
|
+
return index
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return fallback
|
|
29
45
|
}
|
|
30
46
|
mountSwappableRange(
|
|
31
47
|
parent,
|
|
32
|
-
|
|
48
|
+
/* A pending bare async subject (compiler-supplied `isPending`) selects no case — not
|
|
49
|
+
even the default — so the block renders nothing until the cell settles, rather than
|
|
50
|
+
conflating "still loading" with a subject that matched the default. */
|
|
51
|
+
() => (isPending?.() === true ? -1 : select(subject())),
|
|
33
52
|
(index) => {
|
|
34
53
|
const chosen = index === -1 ? undefined : cases[index]
|
|
35
54
|
return chosen && ((p) => chosen.render(p))
|
|
36
55
|
},
|
|
37
56
|
before,
|
|
57
|
+
/* Any async subject/branch (a bare async subject, or an async `{:else if}` in a cond-chain)
|
|
58
|
+
can select a different branch on the server than the pending client, so hydration must
|
|
59
|
+
discard the SSR range rather than adopt it in place. */
|
|
60
|
+
isPending !== undefined || cases.some((entry) => entry.pending !== undefined),
|
|
38
61
|
)
|
|
39
62
|
}
|