@abide/abide 0.48.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 +85 -59
- package/CHANGELOG.md +216 -0
- package/README.md +21 -10
- package/package.json +15 -4
- package/src/abideLsp.ts +5 -6
- package/src/abideResolverPlugin.ts +197 -169
- package/src/build.ts +79 -56
- package/src/buildCli.ts +3 -5
- package/src/buildDisconnected.ts +2 -5
- package/src/bundleApp.ts +2 -3
- package/src/checkAbide.ts +12 -2
- package/src/compile.ts +2 -4
- 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/installDownloads.ts +13 -3
- package/src/lib/bundle/installMacMenu.ts +13 -3
- package/src/lib/bundle/spawnEmbeddedServer.ts +13 -4
- package/src/lib/bundle/waitForServer.ts +6 -1
- package/src/lib/cli/parseArgvForRpc.ts +36 -9
- package/src/lib/cli/tokenizeArgvFlags.ts +4 -2
- package/src/lib/mcp/createMcpResourceServer.ts +13 -2
- package/src/lib/mcp/mcpTools.ts +15 -6
- package/src/lib/mcp/toolResultFromResponse.ts +40 -12
- 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 +113 -14
- package/src/lib/server/rpc/resolveRpcJsonSchema.ts +28 -0
- package/src/lib/server/rpc/runWithRpcTimeout.ts +12 -1
- 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/finalizeResponse.ts +11 -1
- 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 +24 -16
- 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 +20 -0
- package/src/lib/shared/buildRpcRequest.ts +6 -2
- 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/canonicalJson.ts +24 -1
- package/src/lib/shared/changeAffectsClient.ts +12 -7
- package/src/lib/shared/createCacheStore.ts +31 -8
- package/src/lib/shared/createChannelLog.ts +24 -0
- 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/shared/exitOnBuildFailure.ts +4 -3
- 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/parseEnv.ts +17 -6
- 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/serializeEnv.ts +18 -6
- package/src/lib/shared/snapshotShippable.ts +8 -7
- package/src/lib/shared/snippet.ts +11 -6
- 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/url.ts +5 -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 -7
- 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/bindListenEvent.ts +5 -6
- 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 +52 -13
- package/src/lib/ui/compile/compileShadow.ts +265 -42
- package/src/lib/ui/compile/composeProps.ts +15 -4
- 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 +419 -37
- package/src/lib/ui/compile/expressionIsPrefixEvaluable.ts +19 -0
- package/src/lib/ui/compile/generateBuild.ts +74 -17
- package/src/lib/ui/compile/generateSSR.ts +286 -75
- 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/isWhitespaceText.ts +10 -2
- 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 +51 -10
- 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 -1090
- 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 +26 -40
- package/src/lib/ui/compile/resolveReactiveExport.ts +4 -7
- package/src/lib/ui/compile/scopeCss.ts +27 -4
- 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 +62 -75
- 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/discardBoundary.ts +24 -6
- package/src/lib/ui/dom/disposeRange.ts +2 -1
- package/src/lib/ui/dom/each.ts +41 -12
- package/src/lib/ui/dom/eachAsync.ts +39 -3
- package/src/lib/ui/dom/fillBefore.ts +6 -7
- 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/mergeProps.ts +1 -1
- 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/mountSlot.ts +1 -1
- 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/restProps.ts +2 -2
- package/src/lib/ui/dom/spreadProps.ts +3 -4
- package/src/lib/ui/dom/switchBlock.ts +30 -7
- package/src/lib/ui/dom/tryBlock.ts +233 -70
- 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 +9 -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/props.ts +17 -0
- package/src/lib/ui/remoteProxy.ts +84 -159
- package/src/lib/ui/renderChain.ts +53 -13
- package/src/lib/ui/renderToStream.ts +22 -18
- package/src/lib/ui/resumeSeedScript.ts +1 -1
- package/src/lib/ui/router.ts +86 -53
- package/src/lib/ui/runtime/AsyncCellError.ts +20 -0
- package/src/lib/ui/runtime/CELL_SEED.ts +14 -0
- package/src/lib/ui/runtime/CHILD_PRESENT.ts +2 -2
- 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 +17 -4
- 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 +31 -54
- 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 +33 -1
- 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/scope.ts +11 -0
- package/src/lib/ui/runtime/toTeardown.ts +10 -3
- 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/types/UiProps.ts +6 -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/runtime/withoutHydration.ts +22 -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/state.ts +9 -3
- 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/scopeLabel.ts +0 -21
- package/src/lib/ui/dom/text.ts +0 -20
- package/src/lib/ui/history.ts +0 -101
- package/src/lib/ui/installHotBridge.ts +0 -93
- 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 -28
- 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
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
/*
|
|
2
2
|
The abide-ui runtime names every compiled component module depends on, each
|
|
3
|
-
paired with its package subpath (after the package name). One source of truth
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
paired with its package subpath (after the package name). One source of truth for
|
|
4
|
+
the import block `compileModule` emits at the head of every component module, so the
|
|
5
|
+
emitted imports can't drift from the real package exports (a test checks each
|
|
6
|
+
resolves). Order is the emit order.
|
|
7
7
|
|
|
8
8
|
`alias` (when set) is the LOCAL name codegen emits — a `$$`-prefixed form reserved
|
|
9
9
|
for the compiler so a user variable of the helper's bare name can never collide
|
|
10
|
-
(`each as $$each`). It defaults to `name
|
|
11
|
-
|
|
12
|
-
flip to its `$$` alias, set `alias` here in lockstep.
|
|
10
|
+
(`each as $$each`). It defaults to `name`. As each helper's emit sites flip to its
|
|
11
|
+
`$$` alias, set `alias` here in lockstep.
|
|
13
12
|
*/
|
|
14
13
|
export const UI_RUNTIME_IMPORTS: { name: string; specifier: string; alias?: string }[] = [
|
|
15
14
|
{ name: 'snippet', specifier: 'shared/snippet', alias: '$$snippet' },
|
|
@@ -44,14 +43,43 @@ export const UI_RUNTIME_IMPORTS: { name: string; specifier: string; alias?: stri
|
|
|
44
43
|
{ name: 'mountSlot', specifier: 'ui/dom/mountSlot', alias: '$$mountSlot' },
|
|
45
44
|
{ name: 'outlet', specifier: 'ui/dom/outlet', alias: '$$outlet' },
|
|
46
45
|
{ name: 'mountChild', specifier: 'ui/dom/mountChild', alias: '$$mountChild' },
|
|
46
|
+
{
|
|
47
|
+
name: 'mountStreamedChild',
|
|
48
|
+
specifier: 'ui/dom/mountStreamedChild',
|
|
49
|
+
alias: '$$mountStreamedChild',
|
|
50
|
+
},
|
|
47
51
|
{ name: 'mergeProps', specifier: 'ui/dom/mergeProps', alias: '$$mergeProps' },
|
|
48
52
|
{ name: 'spreadProps', specifier: 'ui/dom/spreadProps', alias: '$$spreadProps' },
|
|
49
53
|
{ name: 'restProps', specifier: 'ui/dom/restProps', alias: '$$restProps' },
|
|
54
|
+
{ name: 'bindProp', specifier: 'ui/dom/bindProp', alias: '$$bindProp' },
|
|
55
|
+
{ name: 'bindableProp', specifier: 'ui/dom/bindableProp', alias: '$$bindableProp' },
|
|
50
56
|
{ name: 'spreadAttrs', specifier: 'ui/dom/spreadAttrs', alias: '$$spreadAttrs' },
|
|
51
57
|
{ name: 'readCall', specifier: 'ui/dom/readCall', alias: '$$readCall' },
|
|
58
|
+
{ name: 'readCell', specifier: 'ui/dom/readCell', alias: '$$readCell' },
|
|
59
|
+
{ name: 'cellPending', specifier: 'ui/dom/cellPending', alias: '$$cellPending' },
|
|
60
|
+
{ name: 'withPath', specifier: 'ui/runtime/withPath', alias: '$$withPath' },
|
|
61
|
+
{ name: 'settleAsyncCells', specifier: 'ui/settleAsyncCells', alias: '$$settleAsyncCells' },
|
|
62
|
+
{ name: 'flight', specifier: 'ui/flight', alias: '$$flight' },
|
|
63
|
+
{
|
|
64
|
+
name: 'isolateCellBarrier',
|
|
65
|
+
specifier: 'ui/isolateCellBarrier',
|
|
66
|
+
alias: '$$isolateCellBarrier',
|
|
67
|
+
},
|
|
68
|
+
{ name: 'renderPath', specifier: 'ui/runtime/renderPath', alias: '$$renderPath' },
|
|
69
|
+
{
|
|
70
|
+
name: 'finalizeStreamedChildren',
|
|
71
|
+
specifier: 'ui/finalizeStreamedChildren',
|
|
72
|
+
alias: '$$finalizeStreamedChildren',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'mutateDocContainer',
|
|
76
|
+
specifier: 'ui/dom/mutateDocContainer',
|
|
77
|
+
alias: '$$mutateDocContainer',
|
|
78
|
+
},
|
|
52
79
|
{ name: 'hydrate', specifier: 'ui/dom/hydrate', alias: '$$hydrate' },
|
|
53
80
|
{ name: 'escapeKey', specifier: 'ui/runtime/escapeKey', alias: '$$escapeKey' },
|
|
54
81
|
{ name: 'nextBlockId', specifier: 'ui/runtime/nextBlockId', alias: '$$nextBlockId' },
|
|
82
|
+
{ name: 'blockId', specifier: 'ui/runtime/blockId', alias: '$$blockId' },
|
|
55
83
|
{
|
|
56
84
|
name: 'enterRenderPass',
|
|
57
85
|
specifier: 'ui/runtime/enterRenderPass',
|
|
@@ -4,8 +4,11 @@ import { isLayoutFile } from '../../shared/isLayoutFile.ts'
|
|
|
4
4
|
import { messageFromError } from '../../shared/messageFromError.ts'
|
|
5
5
|
import { AbideCompileError } from './AbideCompileError.ts'
|
|
6
6
|
import { compileModule } from './compileModule.ts'
|
|
7
|
+
import type { ShadowProgram } from './createShadowProgram.ts'
|
|
8
|
+
import { interpolationClassifierForRoot } from './interpolationClassifierForRoot.ts'
|
|
7
9
|
import { nearestProjectRoot } from './nearestProjectRoot.ts'
|
|
8
10
|
import { offsetToLineColumn } from './offsetToLineColumn.ts'
|
|
11
|
+
import { seedTypeClassifierForRoot } from './seedTypeClassifierForRoot.ts'
|
|
9
12
|
|
|
10
13
|
/*
|
|
11
14
|
Bun plugin that loads `.abide` single-file components: compiles each to the ES
|
|
@@ -38,11 +41,25 @@ export const abideUiPlugin: BunPlugin = {
|
|
|
38
41
|
/* Scoped CSS keyed by its virtual specifier, filled as each `.abide` loads and
|
|
39
42
|
read back when Bun resolves the matching `abide-style:` import (browser only). */
|
|
40
43
|
const cssByVirtual = new Map<string, string>()
|
|
44
|
+
/* One WARM shadow program per project root (ADR-0019, Stage B): the checker +
|
|
45
|
+
per-file mappings type-directed lowering needs, built lazily on first `.abide`
|
|
46
|
+
in a root and reused for every component there — not rebuilt per module. */
|
|
47
|
+
const shadowByRoot = new Map<string, ShadowProgram | undefined>()
|
|
41
48
|
|
|
42
49
|
build.onLoad({ filter: /\.abide$/ }, async (args) => {
|
|
43
50
|
const source = await Bun.file(args.path).text()
|
|
44
|
-
const
|
|
51
|
+
const root = nearestProjectRoot(args.path, process.cwd())
|
|
52
|
+
const moduleId = relative(root, args.path)
|
|
45
53
|
const isLayout = isLayoutFile(args.path)
|
|
54
|
+
/* The classifier for this file over its root's warm shadow program. Fail-open:
|
|
55
|
+
a program that can't build (or a file with no shadow) yields `undefined`, so
|
|
56
|
+
the module compiles exactly as before. */
|
|
57
|
+
const classify = interpolationClassifierForRoot(shadowByRoot, root, args.path)
|
|
58
|
+
/* The seed classifier for this file over the SAME warm shadow program (ADR-0023):
|
|
59
|
+
reuses the `shadowByRoot` cache `classify` just warmed, so no second program is
|
|
60
|
+
built. Fail-open identically — a no-marker `computed(seed)` routes by type when
|
|
61
|
+
it resolves, else by the `isBareCallComputed` syntax heuristic. */
|
|
62
|
+
const seedClassify = seedTypeClassifierForRoot(shadowByRoot, root, args.path)
|
|
46
63
|
/* Bun frames a plugin throw at `<file>:0` regardless of the real spot, so
|
|
47
64
|
carry the component path + resolved line:col in the message — otherwise a
|
|
48
65
|
control-flow / compile error reads as `:0` and (in deep imports) can look
|
|
@@ -66,7 +83,7 @@ export const abideUiPlugin: BunPlugin = {
|
|
|
66
83
|
the styles come from the analysis `compileModule` already ran, so the loader no
|
|
67
84
|
longer re-analyzes the source just to recover them. */
|
|
68
85
|
const { code, styles } = compileAbide(() =>
|
|
69
|
-
compileModule(source, { isLayout, moduleId }),
|
|
86
|
+
compileModule(source, { isLayout, moduleId, classify, seedClassify }),
|
|
70
87
|
)
|
|
71
88
|
/* Browser build with `<style>`(s): concatenate every scoped block's CSS and
|
|
72
89
|
pull it into the bundle via one virtual import, keyed by `moduleId` so the
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
import { lowerAsyncInterpolations } from './lowerAsyncInterpolations.ts'
|
|
1
2
|
import { lowerScript } from './lowerScript.ts'
|
|
2
3
|
import { parseTemplate } from './parseTemplate.ts'
|
|
3
4
|
import { scopeCss } from './scopeCss.ts'
|
|
5
|
+
import { LEADING_SCRIPT } from './templateStartOffset.ts'
|
|
4
6
|
import type { AnalyzedComponent } from './types/AnalyzedComponent.ts'
|
|
7
|
+
import type { InterpolationClassifier } from './types/InterpolationClassifier.ts'
|
|
8
|
+
import type { SeedTypeClassifier } from './types/SeedTypeClassifier.ts'
|
|
5
9
|
import type { TemplateNode } from './types/TemplateNode.ts'
|
|
10
|
+
import { writtenTemplateNames } from './writtenTemplateNames.ts'
|
|
6
11
|
|
|
7
12
|
/*
|
|
8
13
|
The shared compile front-end: splits the leading `<script>` off the template,
|
|
@@ -20,21 +25,89 @@ component+position identity, not CSS text: an edit to a `<style>` keeps the same
|
|
|
20
25
|
`data-a-…`, so the live elements still match and the CSS can hot-swap in place.
|
|
21
26
|
Absent (direct compile calls / tests) it falls back to hashing the style body.
|
|
22
27
|
*/
|
|
23
|
-
export function analyzeComponent(
|
|
28
|
+
export function analyzeComponent(
|
|
29
|
+
source: string,
|
|
30
|
+
scopeSeed?: string,
|
|
31
|
+
classify?: InterpolationClassifier,
|
|
32
|
+
/* Type-directed SEED classifier (ADR-0023): resolves a no-marker `computed(seed)`'s
|
|
33
|
+
async-ness from the seed's checker type over the SAME warm shadow program `classify`
|
|
34
|
+
uses. Optional and fail-open — absent, every seed routes by the `isBareCallComputed`
|
|
35
|
+
syntax heuristic (today's behavior). */
|
|
36
|
+
seedClassify?: SeedTypeClassifier,
|
|
37
|
+
): AnalyzedComponent {
|
|
24
38
|
/* Only the LEADING `<script>` is the component script; scripts nested in the
|
|
25
39
|
template (scoped reactive blocks) survive into the parsed nodes. The `<style>`
|
|
26
40
|
is left in the template for the parser to extract structurally (below), so a
|
|
27
41
|
`<style>` quoted inside an expression is never mistaken for the component's. */
|
|
28
|
-
const scriptMatch = source.match(
|
|
29
|
-
const
|
|
30
|
-
const
|
|
42
|
+
const scriptMatch = source.match(LEADING_SCRIPT)
|
|
43
|
+
const rawScriptBody = scriptMatch?.[1] ?? ''
|
|
44
|
+
const scriptBody = rawScriptBody.trim()
|
|
45
|
+
/* Absolute `.abide` source offset where the TRIMMED `<script>` body begins — the base that
|
|
46
|
+
maps a desugared `computed(seed)` node (offset-relative to the trimmed body) back to an
|
|
47
|
+
original source location, so the type-directed seed classifier (ADR-0023) resolves it
|
|
48
|
+
against the shadow's source→shadow `mappings`, exactly as `templateBase` anchors each
|
|
49
|
+
interpolation's `loc`. `indexOf('>')` lands just past the opening `<script …>` (matching
|
|
50
|
+
`compileShadow`'s `scriptStart`); the trim delta re-adds the leading whitespace the parsed
|
|
51
|
+
script dropped. */
|
|
52
|
+
const scriptContentBase =
|
|
53
|
+
(scriptMatch ? source.indexOf('>', scriptMatch.index) + 1 : 0) +
|
|
54
|
+
(rawScriptBody.length - rawScriptBody.trimStart().length)
|
|
55
|
+
const afterScript = scriptMatch ? source.slice(scriptMatch[0].length) : source
|
|
56
|
+
const template = afterScript.trim()
|
|
57
|
+
/* Absolute source offset of the trimmed template's first char (script length + the
|
|
58
|
+
remainder's leading whitespace). Passed as the parse base so each interpolation's
|
|
59
|
+
`loc` is an offset into the ORIGINAL source — the coordinate the shadow's
|
|
60
|
+
source→shadow `mappings` use, so a type-directed classifier resolves against it.
|
|
61
|
+
The runtime back-ends ignore `loc`, so this only affects the classifier's lookup. */
|
|
62
|
+
const templateBase =
|
|
63
|
+
(scriptMatch ? scriptMatch[0].length : 0) +
|
|
64
|
+
(afterScript.length - afterScript.trimStart().length)
|
|
31
65
|
|
|
32
66
|
/* Parse the template first so the nested branch `<script>`s are in hand before the
|
|
33
67
|
leading script lowers: their raw code feeds the dead-import usage check, keeping a
|
|
34
68
|
reactive import (`state`) alive when only a nested branch uses it (its
|
|
35
69
|
`state.computed(...)` stays literal, unlike the desugared leading script). */
|
|
36
|
-
const
|
|
70
|
+
const parsed = parseTemplate(template, templateBase)
|
|
71
|
+
/* Type-directed async lowering (ADR-0032): lift every promise/`AsyncIterable`-typed
|
|
72
|
+
(sub)expression — in content AND value positions — to an injected peek-cell, rewriting it
|
|
73
|
+
in place to a bare `{__vN}` reference BEFORE the script lowers and scopes annotate, so it
|
|
74
|
+
reads `undefined` while pending and composes with `??`/`?.`. Runs unconditionally: with no
|
|
75
|
+
classifier only syntactic `await`s lift (fail-open), so a value-position `{await …}` still
|
|
76
|
+
works while a bare async position degrades to today's plain binding. */
|
|
77
|
+
const { nodes, cells } = lowerAsyncInterpolations(parsed.nodes, classify)
|
|
78
|
+
/* Each lifted async (sub)expression was rewritten to a bare `{__vN}` reference; APPEND a
|
|
79
|
+
matching `const __vN = computed(<seed>)` to the script so the seed's author-signal reads
|
|
80
|
+
lower through the normal pipeline and the declaration desugars to a `trackedComputed` cell
|
|
81
|
+
(read via `$$readCell(__vN)`). `computed` here is a bare, unimported callee — `desugarSignals`
|
|
82
|
+
recognizes it by the injected name (`injectedCellNames`), not by import resolution. A
|
|
83
|
+
BLOCKING cell (author `await`, `blockingCellNames`) joins the SSR barrier; a streaming one
|
|
84
|
+
ships pending. Appended (after the author's `state(...)` inits, not before) so a signal-arg
|
|
85
|
+
seed `{getStream(count)}` reads the INITIALIZED `count`; the cell only feeds the render,
|
|
86
|
+
which runs after the whole script, so end-of-script placement is safe. */
|
|
87
|
+
const injectedCellNames = new Set(cells.map((cell) => cell.name))
|
|
88
|
+
const blockingCellNames = new Set(
|
|
89
|
+
cells.filter((cell) => cell.blocking).map((cell) => cell.name),
|
|
90
|
+
)
|
|
91
|
+
/* A promise seed is wrapped as an async thunk `async () => await (<code>)` — inside an async
|
|
92
|
+
arrow `await` is a keyword and the parens are unambiguous (a bare-text `computed(await (X))`
|
|
93
|
+
reparsed at module scope reads `await(X)` as a call). A stream seed stays a bare
|
|
94
|
+
`computed(<code>)` (byte-identical to an explicit `state.computed(getStream())`). */
|
|
95
|
+
const injectedScript = cells.map((cell) =>
|
|
96
|
+
cell.kind === 'promise'
|
|
97
|
+
? `const ${cell.name} = computed(async () => await (${cell.code}));`
|
|
98
|
+
: `const ${cell.name} = computed(${cell.code});`,
|
|
99
|
+
)
|
|
100
|
+
const fullScriptBody =
|
|
101
|
+
injectedScript.length === 0
|
|
102
|
+
? scriptBody
|
|
103
|
+
: scriptBody.length > 0
|
|
104
|
+
? `${scriptBody}\n${injectedScript.join('\n')}`
|
|
105
|
+
: injectedScript.join('\n')
|
|
37
106
|
const nestedScriptCode = collectNestedScriptCode(nodes)
|
|
107
|
+
/* Names the markup writes or forwards as a `bind:` target — unioned (in `desugarSignals`)
|
|
108
|
+
with the script's own writes so a `props()` binding used two-way desugars to a writable
|
|
109
|
+
cell (`bindableProp`) rather than a read-only derive. */
|
|
110
|
+
const templateWrittenNames = writtenTemplateNames(nodes)
|
|
38
111
|
/* `lowerScript` parses the script ONCE and chains signal desugaring, reference
|
|
39
112
|
renaming, and doc-access lowering over that single tree, then hoists top-level
|
|
40
113
|
imports off the tree structurally — imports live at module scope, not inside the
|
|
@@ -46,8 +119,17 @@ export function analyzeComponent(source: string, scopeSeed?: string): AnalyzedCo
|
|
|
46
119
|
stateNames,
|
|
47
120
|
derivedNames,
|
|
48
121
|
computedNames,
|
|
122
|
+
cellReadNames,
|
|
49
123
|
droppedReactiveImports,
|
|
50
|
-
} = lowerScript(
|
|
124
|
+
} = lowerScript(
|
|
125
|
+
fullScriptBody,
|
|
126
|
+
nestedScriptCode,
|
|
127
|
+
injectedCellNames,
|
|
128
|
+
blockingCellNames,
|
|
129
|
+
templateWrittenNames,
|
|
130
|
+
seedClassify,
|
|
131
|
+
scriptContentBase,
|
|
132
|
+
)
|
|
51
133
|
/* `annotateScopes` mutates the tree — assigning each style its scope attribute and
|
|
52
134
|
stamping covered elements — and returns the scoped CSS per block for the bundler. */
|
|
53
135
|
const styles = annotateScopes(nodes, [], scopeSeed, { count: 0 })
|
|
@@ -59,6 +141,7 @@ export function analyzeComponent(source: string, scopeSeed?: string): AnalyzedCo
|
|
|
59
141
|
stateNames,
|
|
60
142
|
derivedNames,
|
|
61
143
|
computedNames,
|
|
144
|
+
cellReadNames,
|
|
62
145
|
nodes,
|
|
63
146
|
styles,
|
|
64
147
|
/* Hydration adopts every block in place — including `await`, which resumes
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import ts from 'typescript'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Collects every identifier NAME that is written somewhere in `source` — the target of
|
|
5
|
+
an assignment (`x = …`, `x += …`, and every compound form) or the operand of a `++`/`--`
|
|
6
|
+
update. A write to a member (`x.foo = …`, `x[i]++`) counts as a write to its root object
|
|
7
|
+
identifier (`x`), since mutating the object the prop holds still needs a writable cell.
|
|
8
|
+
Reads never count. Used to decide which props a component actually writes, so only those
|
|
9
|
+
are upgraded from a read-only derive to a writable cell (`bindableProp`).
|
|
10
|
+
|
|
11
|
+
Accumulates into `into` so several fragments (the script, each template expression) can
|
|
12
|
+
feed one set. Best-effort and syntactic — it does not resolve scope, so a shadowing local
|
|
13
|
+
of the same name is conservatively treated as a write to the prop; that only ever upgrades
|
|
14
|
+
a prop needlessly, never drops a real write.
|
|
15
|
+
*/
|
|
16
|
+
export function assignmentTargetNames(source: ts.SourceFile, into: Set<string>): void {
|
|
17
|
+
const rootIdentifier = (node: ts.Expression): string | undefined => {
|
|
18
|
+
let current: ts.Expression = node
|
|
19
|
+
/* Peel member/element access and parens down to the base object identifier — every
|
|
20
|
+
one of these node kinds exposes the inner expression as `.expression`. */
|
|
21
|
+
while (
|
|
22
|
+
ts.isPropertyAccessExpression(current) ||
|
|
23
|
+
ts.isElementAccessExpression(current) ||
|
|
24
|
+
ts.isParenthesizedExpression(current) ||
|
|
25
|
+
ts.isNonNullExpression(current)
|
|
26
|
+
) {
|
|
27
|
+
current = current.expression
|
|
28
|
+
}
|
|
29
|
+
return ts.isIdentifier(current) ? current.text : undefined
|
|
30
|
+
}
|
|
31
|
+
const visit = (node: ts.Node): void => {
|
|
32
|
+
if (
|
|
33
|
+
ts.isBinaryExpression(node) &&
|
|
34
|
+
node.operatorToken.kind >= ts.SyntaxKind.FirstAssignment &&
|
|
35
|
+
node.operatorToken.kind <= ts.SyntaxKind.LastAssignment
|
|
36
|
+
) {
|
|
37
|
+
const name = rootIdentifier(node.left)
|
|
38
|
+
if (name !== undefined) {
|
|
39
|
+
into.add(name)
|
|
40
|
+
}
|
|
41
|
+
} else if (
|
|
42
|
+
(ts.isPrefixUnaryExpression(node) || ts.isPostfixUnaryExpression(node)) &&
|
|
43
|
+
(node.operator === ts.SyntaxKind.PlusPlusToken ||
|
|
44
|
+
node.operator === ts.SyntaxKind.MinusMinusToken)
|
|
45
|
+
) {
|
|
46
|
+
const name = rootIdentifier(node.operand as ts.Expression)
|
|
47
|
+
if (name !== undefined) {
|
|
48
|
+
into.add(name)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
ts.forEachChild(node, visit)
|
|
52
|
+
}
|
|
53
|
+
visit(source)
|
|
54
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { attrLiftPosition } from './attrLiftPosition.ts'
|
|
2
|
+
import type { AsyncInterpolationField } from './types/AsyncInterpolationField.ts'
|
|
3
|
+
import type { TemplateNode } from './types/TemplateNode.ts'
|
|
4
|
+
|
|
5
|
+
/* No control-flow subject to record — the writeback for a content/attribute field. */
|
|
6
|
+
const NO_OP = (): void => {}
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
Enumerates the async-liftable interpolation fields of ONE template node — content text parts,
|
|
10
|
+
value-position attributes (`attrLiftPosition`), and the control-flow subjects — as the single shared
|
|
11
|
+
reading both compile front-ends lower from. It mirrors `ifPlan`/`awaitPlan` (a per-node structural
|
|
12
|
+
plan the two back-ends read), applied to interpolations: the runtime lifts each field to an injected
|
|
13
|
+
`computed` cell and `write`s the rewritten `__vN` reference back — marking `setAsyncSubject` when a
|
|
14
|
+
whole subject collapsed to one bare cell — while the shadow reads the same `code`/`loc`/`position` to
|
|
15
|
+
peek-wrap its value-typed projection. Neither can disagree on WHICH interpolations are async or their
|
|
16
|
+
position.
|
|
17
|
+
|
|
18
|
+
Fields are yielded in source order (text parts / attributes before the control-flow subject) so the
|
|
19
|
+
runtime's global `__vN` cell numbering stays deterministic. The node's children are NOT descended —
|
|
20
|
+
each front-end recurses on its own. A `{#for await}` source is the sanctioned `AsyncIterable`, drained
|
|
21
|
+
unchanged, so it is never lifted (excluded); `await` / `component` / `snippet` / `try` carry no lifted
|
|
22
|
+
field (an `await`'s promise and a component's props route through their own lowerings).
|
|
23
|
+
*/
|
|
24
|
+
export function asyncInterpolationFields(node: TemplateNode): AsyncInterpolationField[] {
|
|
25
|
+
const fields: AsyncInterpolationField[] = []
|
|
26
|
+
switch (node.kind) {
|
|
27
|
+
case 'text':
|
|
28
|
+
for (const part of node.parts) {
|
|
29
|
+
if (part.kind === 'expression' && part.loc !== undefined) {
|
|
30
|
+
fields.push({
|
|
31
|
+
code: part.code,
|
|
32
|
+
loc: part.loc,
|
|
33
|
+
position: 'content',
|
|
34
|
+
subject: false,
|
|
35
|
+
write: (code) => {
|
|
36
|
+
part.code = code
|
|
37
|
+
},
|
|
38
|
+
setAsyncSubject: NO_OP,
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return fields
|
|
43
|
+
case 'element':
|
|
44
|
+
for (const attr of node.attrs) {
|
|
45
|
+
if (attr.kind === 'interpolated') {
|
|
46
|
+
for (const part of attr.parts) {
|
|
47
|
+
if (part.kind === 'expression' && part.loc !== undefined) {
|
|
48
|
+
fields.push({
|
|
49
|
+
code: part.code,
|
|
50
|
+
loc: part.loc,
|
|
51
|
+
position: 'attribute',
|
|
52
|
+
subject: false,
|
|
53
|
+
write: (code) => {
|
|
54
|
+
part.code = code
|
|
55
|
+
},
|
|
56
|
+
setAsyncSubject: NO_OP,
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
continue
|
|
61
|
+
}
|
|
62
|
+
/* Only `expression` attributes lift (directives evaluate to a handler/lvalue, not a
|
|
63
|
+
rendered value) — `attrLiftPosition` is the shared gate. */
|
|
64
|
+
const position = attrLiftPosition(attr)
|
|
65
|
+
if (
|
|
66
|
+
position !== undefined &&
|
|
67
|
+
attr.kind === 'expression' &&
|
|
68
|
+
attr.loc !== undefined
|
|
69
|
+
) {
|
|
70
|
+
fields.push({
|
|
71
|
+
code: attr.code,
|
|
72
|
+
loc: attr.loc,
|
|
73
|
+
position,
|
|
74
|
+
subject: false,
|
|
75
|
+
write: (code) => {
|
|
76
|
+
attr.code = code
|
|
77
|
+
},
|
|
78
|
+
setAsyncSubject: NO_OP,
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return fields
|
|
83
|
+
case 'if':
|
|
84
|
+
if (node.loc !== undefined) {
|
|
85
|
+
fields.push({
|
|
86
|
+
code: node.condition,
|
|
87
|
+
loc: node.loc,
|
|
88
|
+
position: 'if',
|
|
89
|
+
subject: true,
|
|
90
|
+
write: (code) => {
|
|
91
|
+
node.condition = code
|
|
92
|
+
},
|
|
93
|
+
setAsyncSubject: (asyncSubject) => {
|
|
94
|
+
node.asyncSubject = asyncSubject
|
|
95
|
+
},
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
return fields
|
|
99
|
+
case 'switch':
|
|
100
|
+
if (node.loc !== undefined) {
|
|
101
|
+
fields.push({
|
|
102
|
+
code: node.subject,
|
|
103
|
+
loc: node.loc,
|
|
104
|
+
position: 'switch',
|
|
105
|
+
subject: true,
|
|
106
|
+
write: (code) => {
|
|
107
|
+
node.subject = code
|
|
108
|
+
},
|
|
109
|
+
setAsyncSubject: (asyncSubject) => {
|
|
110
|
+
node.asyncSubject = asyncSubject
|
|
111
|
+
},
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
return fields
|
|
115
|
+
case 'case':
|
|
116
|
+
/* An `{:elseif}` condition — a truthy-tested control-flow subject, lifted like an
|
|
117
|
+
`{#if}` head so a bare async `{:elseif}` holds the cond-chain while it loads. */
|
|
118
|
+
if (node.condition !== undefined && node.loc !== undefined) {
|
|
119
|
+
fields.push({
|
|
120
|
+
code: node.condition,
|
|
121
|
+
loc: node.loc,
|
|
122
|
+
position: 'if',
|
|
123
|
+
subject: true,
|
|
124
|
+
write: (code) => {
|
|
125
|
+
node.condition = code
|
|
126
|
+
},
|
|
127
|
+
setAsyncSubject: (asyncSubject) => {
|
|
128
|
+
node.asyncSubject = asyncSubject
|
|
129
|
+
},
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
return fields
|
|
133
|
+
case 'each':
|
|
134
|
+
/* A plain `{#for}` source lifts (a promise-of-iterable → empty while pending); a
|
|
135
|
+
`{#for await}` source is the sanctioned `AsyncIterable`, never lifted. */
|
|
136
|
+
if (node.loc !== undefined && !node.async) {
|
|
137
|
+
fields.push({
|
|
138
|
+
code: node.items,
|
|
139
|
+
loc: node.loc,
|
|
140
|
+
position: 'each',
|
|
141
|
+
subject: false,
|
|
142
|
+
write: (code) => {
|
|
143
|
+
node.items = code
|
|
144
|
+
},
|
|
145
|
+
setAsyncSubject: NO_OP,
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
return fields
|
|
149
|
+
default:
|
|
150
|
+
return fields
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { InterpolationKind } from './types/InterpolationKind.ts'
|
|
2
|
+
import type { ValuePositionInterpolation } from './types/ValuePositionInterpolation.ts'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
The narrowed value-position guard (ADR-0032): a promise/`AsyncIterable` (sub)expression now LIFTS
|
|
6
|
+
to a peek-cell in every position — an attribute, an `{#if}`/`{#switch}` subject, a plain `{#for}`
|
|
7
|
+
source — reading `undefined` while pending. The ONE remaining error is a raw `AsyncIterable`
|
|
8
|
+
driving a PLAIN `{#for}`: a frame is not a collection, so iterate its frames with `{#for await}`
|
|
9
|
+
(the `for await` position is exempt). Everything else (promises everywhere, streams in attribute/
|
|
10
|
+
`{#if}`/`{#switch}`/content) is allowed. Shared by the build front-end (`liftAsyncSubExpressions`
|
|
11
|
+
throws the same message) and `abide check` (`collectAbideDiagnostics`).
|
|
12
|
+
*/
|
|
13
|
+
export function asyncValuePositionError(
|
|
14
|
+
kind: InterpolationKind,
|
|
15
|
+
position: ValuePositionInterpolation['position'],
|
|
16
|
+
): string | undefined {
|
|
17
|
+
if (kind !== 'asyncIterable' || position !== 'each') {
|
|
18
|
+
return undefined
|
|
19
|
+
}
|
|
20
|
+
return "[abide] an `AsyncIterable` can't drive a plain `{#for}` — iterate its frames with `{#for await}`."
|
|
21
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { TemplateNode } from './types/TemplateNode.ts'
|
|
2
|
+
import type { ValuePositionInterpolation } from './types/ValuePositionInterpolation.ts'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
Collects every interpolation sitting in a NON-content value position across a parsed
|
|
6
|
+
template — the positions Stage E guards (ADR-0019): an attribute value, an `{#if}` /
|
|
7
|
+
`{#switch}` head, and a `{#each}`/`{#for}` iterable. Text-child positions are NOT
|
|
8
|
+
collected: promise/asyncIterable text interpolations lower to a streaming await /
|
|
9
|
+
stream cell (Stages C+D), so they render fine. A `{#for await}` iterable is included
|
|
10
|
+
but tagged `for await` so the caller allows an `AsyncIterable` there.
|
|
11
|
+
|
|
12
|
+
Shared by both guard sites: the build front-end (`lowerAsyncInterpolations`, which
|
|
13
|
+
classifies each and throws) and `abide check` (`collectAbideDiagnostics`, which
|
|
14
|
+
classifies each and pushes a diagnostic). Only positions the parser tracked an offset
|
|
15
|
+
for are collected — an untracked span (`loc === undefined`) has no classifier key.
|
|
16
|
+
*/
|
|
17
|
+
export function asyncValuePositionInterpolations(
|
|
18
|
+
nodes: TemplateNode[],
|
|
19
|
+
): ValuePositionInterpolation[] {
|
|
20
|
+
const found: ValuePositionInterpolation[] = []
|
|
21
|
+
collect(nodes, found)
|
|
22
|
+
return found
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* Recursive walk: pushes each node's value-position interpolations, then descends
|
|
26
|
+
into its children so nested elements and blocks are reached. */
|
|
27
|
+
function collect(nodes: TemplateNode[], found: ValuePositionInterpolation[]): void {
|
|
28
|
+
for (const node of nodes) {
|
|
29
|
+
if (node === undefined) {
|
|
30
|
+
continue
|
|
31
|
+
}
|
|
32
|
+
if (node.kind === 'element') {
|
|
33
|
+
for (const attr of node.attrs) {
|
|
34
|
+
/* `name={expr}` — a plain reactive value bind. */
|
|
35
|
+
if (attr.kind === 'expression' && attr.loc !== undefined) {
|
|
36
|
+
found.push({ loc: attr.loc, code: attr.code, position: 'attribute' })
|
|
37
|
+
continue
|
|
38
|
+
}
|
|
39
|
+
/* `name="… {expr} …"` — each interpolated part stringifies into the value. */
|
|
40
|
+
if (attr.kind === 'interpolated') {
|
|
41
|
+
for (const part of attr.parts) {
|
|
42
|
+
if (part.kind === 'expression' && part.loc !== undefined) {
|
|
43
|
+
found.push({ loc: part.loc, code: part.code, position: 'attribute' })
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
} else if (node.kind === 'if' && node.loc !== undefined) {
|
|
49
|
+
found.push({ loc: node.loc, code: node.condition, position: 'if' })
|
|
50
|
+
} else if (node.kind === 'switch' && node.loc !== undefined) {
|
|
51
|
+
found.push({ loc: node.loc, code: node.subject, position: 'switch' })
|
|
52
|
+
} else if (node.kind === 'each' && node.loc !== undefined) {
|
|
53
|
+
/* `{#for await}` is the sanctioned async iterable; a sync `{#each}` is not. */
|
|
54
|
+
found.push({
|
|
55
|
+
loc: node.loc,
|
|
56
|
+
code: node.items,
|
|
57
|
+
position: node.async ? 'for await' : 'each',
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
if ('children' in node) {
|
|
61
|
+
collect(node.children, found)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { LiftPosition } from './liftAsyncSubExpressions.ts'
|
|
2
|
+
import type { TemplateAttr } from './types/TemplateAttr.ts'
|
|
3
|
+
|
|
4
|
+
/* The value position an attribute's authored expression occupies for async lowering, or undefined
|
|
5
|
+
when the attribute carries no async-liftable expression. Only `name={code}` (`expression`) and
|
|
6
|
+
`name="… {code} …"` (`interpolated`) bind a rendered value a promise/stream sub-expression may sit
|
|
7
|
+
in and lift to a peek-cell — every element value position is `'attribute'`. The directive
|
|
8
|
+
attributes (`on*` event, `bind:`, `class:`, `style:`, `attach`, `{...}` spread) evaluate to a
|
|
9
|
+
handler / lvalue / attachment / spread object, not a rendered value, so their expressions are NOT
|
|
10
|
+
lifted; a `static` literal carries no expression at all. Both compile front-ends read this so the
|
|
11
|
+
runtime's injected cells and the shadow's peek-wrap cover the SAME attribute set — the two can
|
|
12
|
+
never silently disagree on which attribute expressions are async. */
|
|
13
|
+
export function attrLiftPosition(attr: TemplateAttr): LiftPosition | undefined {
|
|
14
|
+
if (attr.kind === 'expression' || attr.kind === 'interpolated') {
|
|
15
|
+
return 'attribute'
|
|
16
|
+
}
|
|
17
|
+
return undefined
|
|
18
|
+
}
|
|
@@ -2,18 +2,17 @@
|
|
|
2
2
|
The DOM event a generic two-way `bind:<property>` listens on to write the bound
|
|
3
3
|
path back. Most form fields report edits via `input`, but a few properties are
|
|
4
4
|
driven by their own event and never fire `input` — `<details open>` fires
|
|
5
|
-
`toggle
|
|
6
|
-
|
|
5
|
+
`toggle` and a checkbox/radio `checked` fires `change`. `<select value>` is NOT
|
|
6
|
+
handled here: generateBuild intercepts it before this function and routes it to
|
|
7
|
+
`bindSelectValue` (a MutationObserver-based helper that re-applies on late
|
|
8
|
+
options), so this function never sees that case.
|
|
7
9
|
*/
|
|
8
|
-
export function bindListenEvent(property: string
|
|
10
|
+
export function bindListenEvent(property: string): string {
|
|
9
11
|
if (property === 'open') {
|
|
10
12
|
return 'toggle'
|
|
11
13
|
}
|
|
12
14
|
if (property === 'checked') {
|
|
13
15
|
return 'change'
|
|
14
16
|
}
|
|
15
|
-
if (property === 'value' && tag === 'select') {
|
|
16
|
-
return 'change'
|
|
17
|
-
}
|
|
18
17
|
return 'input'
|
|
19
18
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import ts from 'typescript'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
One parsed `ts.SourceFile` per (fileName, exact text, options signature), shared across
|
|
5
|
+
every `ts.Program` this process builds. `ts.createProgram` re-parses all ~90 default
|
|
6
|
+
`lib.*.d.ts` files (~3MB) plus every resolved dependency on each call — negligible for the
|
|
7
|
+
one-shot `abide check` CLI, but the LSP rebuilds a program per edit and the test suite builds
|
|
8
|
+
dozens over identical inputs. A `SourceFile` is a pure function of its text and the
|
|
9
|
+
parse/bind-affecting compiler options, so reuse is safe — the same reuse TS performs for the
|
|
10
|
+
source files it carries forward from an incremental `oldProgram`. Keying on the EXACT text
|
|
11
|
+
means any content change re-parses (never a stale AST); the `signature` keeps two projects
|
|
12
|
+
with different targets/libs from sharing a file; one entry per fileName bounds the cache to a
|
|
13
|
+
single program's footprint.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
type CacheEntry = { text: string; signature: string; sourceFile: ts.SourceFile }
|
|
17
|
+
|
|
18
|
+
const CACHE = new Map<string, CacheEntry>()
|
|
19
|
+
|
|
20
|
+
export function cachedSourceFile(
|
|
21
|
+
fileName: string,
|
|
22
|
+
text: string,
|
|
23
|
+
languageVersionOrOptions: ts.ScriptTarget | ts.CreateSourceFileOptions,
|
|
24
|
+
signature: string,
|
|
25
|
+
scriptKind?: ts.ScriptKind,
|
|
26
|
+
): ts.SourceFile {
|
|
27
|
+
const existing = CACHE.get(fileName)
|
|
28
|
+
if (existing && existing.text === text && existing.signature === signature) {
|
|
29
|
+
return existing.sourceFile
|
|
30
|
+
}
|
|
31
|
+
const sourceFile = ts.createSourceFile(
|
|
32
|
+
fileName,
|
|
33
|
+
text,
|
|
34
|
+
languageVersionOrOptions,
|
|
35
|
+
true,
|
|
36
|
+
scriptKind,
|
|
37
|
+
)
|
|
38
|
+
CACHE.set(fileName, { text, signature, sourceFile })
|
|
39
|
+
return sourceFile
|
|
40
|
+
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { Binding } from './types/Binding.ts'
|
|
2
2
|
|
|
3
|
-
/* A block's `catch` branch binding — the error name
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
/* A block's `catch` branch binding — the error name, or none when the block has no catch
|
|
4
|
+
branch. Shared by the await/each/try plans so the one catch-binding shape stays
|
|
5
|
+
single-source. `reactive` binds the error as a `.value` cell the runtime can update in
|
|
6
|
+
place (the reactive `{#try}` — a catch→catch error swaps in place, no rebuild); the
|
|
7
|
+
await/each catches leave it `plain` (they pass the error by value). */
|
|
8
|
+
export function catchBinding(catchAs: string, hasCatch: boolean, reactive = false): Binding[] {
|
|
9
|
+
return hasCatch ? [{ name: catchAs, classification: reactive ? 'reactive' : 'plain' }] : []
|
|
8
10
|
}
|