@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
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import ts from 'typescript'
|
|
2
|
+
|
|
3
|
+
/* Every identifier read as a value in `code` — excludes property member names (`.name`) and object
|
|
4
|
+
literal keys, which are not free variable references. Deliberately generous elsewhere: an inner
|
|
5
|
+
arrow's param is still collected, so an expression shadowing a binder name inside a callback is
|
|
6
|
+
judged conservatively (NOT prefix-evaluable) rather than risking a false hoist. Shared by the
|
|
7
|
+
await-flight and child-render-flight prefix-evaluability checks (ADR-0034 / ADR-0037). */
|
|
8
|
+
export function referencedIdentifiers(code: string): Set<string> {
|
|
9
|
+
const names = new Set<string>()
|
|
10
|
+
let source: ts.SourceFile
|
|
11
|
+
try {
|
|
12
|
+
source = ts.createSourceFile('expression.ts', code, ts.ScriptTarget.Latest, true)
|
|
13
|
+
} catch {
|
|
14
|
+
/* Unparseable expression → treat as non-hoistable by returning a sentinel the caller's
|
|
15
|
+
binder check can never clear; simplest is a name no scope defines. */
|
|
16
|
+
return new Set(['\0unparseable'])
|
|
17
|
+
}
|
|
18
|
+
const visit = (node: ts.Node): void => {
|
|
19
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
20
|
+
visit(node.expression)
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name)) {
|
|
24
|
+
visit(node.initializer)
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
if (ts.isIdentifier(node)) {
|
|
28
|
+
names.add(node.text)
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
ts.forEachChild(node, visit)
|
|
32
|
+
}
|
|
33
|
+
visit(source)
|
|
34
|
+
return names
|
|
35
|
+
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import ts from 'typescript'
|
|
2
|
-
import { TS_PRINTER } from './TS_PRINTER.ts'
|
|
3
2
|
|
|
4
3
|
/*
|
|
5
4
|
Rewrites references to a component's signal bindings into the document form the
|
|
6
5
|
rest of the pipeline understands: a `state` binding `count` becomes `$$model.count`
|
|
7
|
-
(data access `
|
|
6
|
+
(data access `docAccessTransformer` then lowers to a patch/read), and a `computed`
|
|
8
7
|
binding `total` becomes `total.value`. Only value-position identifiers are
|
|
9
8
|
touched — declaration names, parameter names, and property names are collected
|
|
10
9
|
into a skip set first, and object shorthand (`{ count }`) is expanded to
|
|
@@ -17,27 +16,10 @@ to that inner binding, not the component signal, so it is left untouched. Withou
|
|
|
17
16
|
this, a callback like `list.map(option => option.toUpperCase())` in a component
|
|
18
17
|
that also has an `option` prop (`const { option } = props()`) would have its loop
|
|
19
18
|
variable rewritten to `option()` and blow up at runtime (`option is not a function`).
|
|
20
|
-
*/
|
|
21
|
-
export function renameSignalRefs(
|
|
22
|
-
code: string,
|
|
23
|
-
stateNames: ReadonlySet<string>,
|
|
24
|
-
derivedNames: ReadonlySet<string>,
|
|
25
|
-
computedNames: ReadonlySet<string> = new Set(),
|
|
26
|
-
): string {
|
|
27
|
-
const source = ts.createSourceFile('component.ts', code, ts.ScriptTarget.Latest, true)
|
|
28
|
-
const result = ts.transform(source, [
|
|
29
|
-
signalRefsTransformer(stateNames, derivedNames, computedNames),
|
|
30
|
-
])
|
|
31
|
-
const output = TS_PRINTER.printFile(result.transformed[0] as ts.SourceFile)
|
|
32
|
-
result.dispose()
|
|
33
|
-
return output
|
|
34
|
-
}
|
|
35
19
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
print-then-reparse between passes. `renameSignalRefs` is the standalone string wrapper
|
|
40
|
-
kept for test and one-off callers.
|
|
20
|
+
Exposed as a `ts.TransformerFactory`, so the script pipeline can chain it with
|
|
21
|
+
`docAccessTransformer` over a SINGLE parsed tree (see `lowerScript`) instead of
|
|
22
|
+
print-then-reparse between passes.
|
|
41
23
|
*/
|
|
42
24
|
export function signalRefsTransformer(
|
|
43
25
|
stateNames: ReadonlySet<string>,
|
|
@@ -53,6 +35,10 @@ export function signalRefsTransformer(
|
|
|
53
35
|
bare identifier, not `.value`: SSR declares `const foo = <resolved>`, so a reference
|
|
54
36
|
reads the local directly. Seeded into the root shadow set so it is left untouched. */
|
|
55
37
|
blockLocalPlain: ReadonlySet<string> = new Set(),
|
|
38
|
+
/* Component signals read through `$$readCell(name)` — every `linked` and every async
|
|
39
|
+
`computed` (see `desugarSignals`). A nearer lexical binding of the same name shadows
|
|
40
|
+
them like any other signal. */
|
|
41
|
+
cellReadNames: ReadonlySet<string> = new Set(),
|
|
56
42
|
): ts.TransformerFactory<ts.SourceFile> {
|
|
57
43
|
/* The signal names that a nested binding can shadow — only these matter for
|
|
58
44
|
scope tracking, so we ignore every other local binding. */
|
|
@@ -60,6 +46,7 @@ export function signalRefsTransformer(
|
|
|
60
46
|
...stateNames,
|
|
61
47
|
...derivedNames,
|
|
62
48
|
...computedNames,
|
|
49
|
+
...cellReadNames,
|
|
63
50
|
...blockLocal,
|
|
64
51
|
/* `scope` is the author-facing reactive entry (`scope().state(...)`), lowered to the
|
|
65
52
|
reserved `$$scope` import so a user variable named `scope` can never collide — but
|
|
@@ -109,6 +96,7 @@ export function signalRefsTransformer(
|
|
|
109
96
|
derivedNames,
|
|
110
97
|
computedNames,
|
|
111
98
|
blockLocal,
|
|
99
|
+
cellReadNames,
|
|
112
100
|
)
|
|
113
101
|
if (replacement !== undefined) {
|
|
114
102
|
return ts.factory.createPropertyAssignment(node.name.text, replacement)
|
|
@@ -121,6 +109,7 @@ export function signalRefsTransformer(
|
|
|
121
109
|
derivedNames,
|
|
122
110
|
computedNames,
|
|
123
111
|
blockLocal,
|
|
112
|
+
cellReadNames,
|
|
124
113
|
)
|
|
125
114
|
if (replacement !== undefined) {
|
|
126
115
|
return replacement
|
|
@@ -338,6 +327,7 @@ function referenceFor(
|
|
|
338
327
|
derivedNames: ReadonlySet<string>,
|
|
339
328
|
computedNames: ReadonlySet<string>,
|
|
340
329
|
blockLocal: ReadonlySet<string> = new Set(),
|
|
330
|
+
cellReadNames: ReadonlySet<string> = new Set(),
|
|
341
331
|
): ts.Expression | undefined {
|
|
342
332
|
if (blockLocal.has(name)) {
|
|
343
333
|
return ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(name), 'value')
|
|
@@ -347,6 +337,15 @@ function referenceFor(
|
|
|
347
337
|
if (name === 'scope') {
|
|
348
338
|
return ts.factory.createIdentifier('$$scope')
|
|
349
339
|
}
|
|
340
|
+
/* A `linked` / async `computed` cell → `$$readCell(name)`: one read shape that peeks an
|
|
341
|
+
async cell and reads `.value` off a sync one. */
|
|
342
|
+
if (cellReadNames.has(name)) {
|
|
343
|
+
return ts.factory.createCallExpression(
|
|
344
|
+
ts.factory.createIdentifier('$$readCell'),
|
|
345
|
+
undefined,
|
|
346
|
+
[ts.factory.createIdentifier(name)],
|
|
347
|
+
)
|
|
348
|
+
}
|
|
350
349
|
if (stateNames.has(name)) {
|
|
351
350
|
return ts.factory.createPropertyAccessExpression(
|
|
352
351
|
ts.factory.createIdentifier('$$model'),
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { classifyInterpolationType } from './classifyInterpolationType.ts'
|
|
2
|
+
import { createShadowProgram, type ShadowProgram } from './createShadowProgram.ts'
|
|
3
|
+
import { nodeAtShadowOffset } from './nodeAtShadowOffset.ts'
|
|
4
|
+
import { shadowNaming } from './shadowNaming.ts'
|
|
5
|
+
import { sourceToShadowOffset } from './sourceToShadowOffset.ts'
|
|
6
|
+
import type { SeedTypeClassifier } from './types/SeedTypeClassifier.ts'
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
Builds the type-directed SEED classifier for one `.abide` file (ADR-0023) — the cell
|
|
10
|
+
transform's counterpart to `interpolationClassifierForRoot`, resolving a `computed`/`linked`
|
|
11
|
+
seed's checker type through the SAME warm shadow program (kept once per project root, ADR-0019
|
|
12
|
+
Stage B). Shares the `cache` the interpolation classifier populates, so no second program is
|
|
13
|
+
built: whichever classifier is requested first for a root warms it, the other reuses it.
|
|
14
|
+
|
|
15
|
+
A `computed(SEED)` declaration is emitted into the shadow's script region with `SEED` wrapped
|
|
16
|
+
in parens (verbatim if the callee is unrecognized, or projected as `(SEED)()` when recognized
|
|
17
|
+
as `state.computed` — either way the seed sub-expression maps back to source and resolves its
|
|
18
|
+
own type), so the identical `sourceToShadowOffset → nodeAtShadowOffset → classifyInterpolation
|
|
19
|
+
Type` pipeline the interpolation half uses resolves a seed with no node-finder variant.
|
|
20
|
+
|
|
21
|
+
FAIL-OPEN, but NOT to `'sync'`: if the program can't build, the file has no shadow, the seed's
|
|
22
|
+
location doesn't map, no node is found, or the checker throws, the classifier is absent /
|
|
23
|
+
returns `undefined` — the signal the caller reads as "degrade to `isBareCallComputed`", NOT as
|
|
24
|
+
"the seed is sync". Collapsing failure to `'sync'` (as the interpolation classifier does) would
|
|
25
|
+
mis-route a failed stream seed to the lazy `derive` slot instead of today's syntax heuristic.
|
|
26
|
+
*/
|
|
27
|
+
export function seedTypeClassifierForRoot(
|
|
28
|
+
cache: Map<string, ShadowProgram | undefined>,
|
|
29
|
+
root: string,
|
|
30
|
+
abidePath: string,
|
|
31
|
+
): SeedTypeClassifier | undefined {
|
|
32
|
+
if (!cache.has(root)) {
|
|
33
|
+
try {
|
|
34
|
+
cache.set(root, createShadowProgram(root))
|
|
35
|
+
} catch {
|
|
36
|
+
cache.set(root, undefined)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const shadowProgram = cache.get(root)
|
|
40
|
+
if (shadowProgram === undefined) {
|
|
41
|
+
return undefined
|
|
42
|
+
}
|
|
43
|
+
const { program, shadows } = shadowProgram
|
|
44
|
+
const shadowFile = program.getSourceFile(shadowNaming.suffixed(abidePath))
|
|
45
|
+
if (shadowFile === undefined) {
|
|
46
|
+
return undefined
|
|
47
|
+
}
|
|
48
|
+
const checker = program.getTypeChecker()
|
|
49
|
+
return (loc, code) => {
|
|
50
|
+
try {
|
|
51
|
+
const mappings = shadows.get(abidePath)?.mappings ?? []
|
|
52
|
+
const offset = sourceToShadowOffset(mappings, loc)
|
|
53
|
+
if (offset === undefined) {
|
|
54
|
+
return undefined
|
|
55
|
+
}
|
|
56
|
+
const node = nodeAtShadowOffset(shadowFile, offset, code.length)
|
|
57
|
+
if (node === undefined) {
|
|
58
|
+
return undefined
|
|
59
|
+
}
|
|
60
|
+
return classifyInterpolationType(checker.getTypeAtLocation(node), node, checker)
|
|
61
|
+
} catch {
|
|
62
|
+
return undefined
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type ts from 'typescript'
|
|
2
|
+
import { classifyInterpolationType } from './classifyInterpolationType.ts'
|
|
3
|
+
import { nodeAtShadowOffset } from './nodeAtShadowOffset.ts'
|
|
4
|
+
import { shadowNaming } from './shadowNaming.ts'
|
|
5
|
+
import { sourceToShadowOffset } from './sourceToShadowOffset.ts'
|
|
6
|
+
import type { CompiledShadow } from './types/CompiledShadow.ts'
|
|
7
|
+
import type { InterpolationClassifier } from './types/InterpolationClassifier.ts'
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
Builds the type-directed interpolation classifier for one `.abide` file against an already-built
|
|
11
|
+
shadow `program` (its `shadows` map carries each file's source→shadow `mappings`). Given an
|
|
12
|
+
interpolation's absolute source offset and text, it maps into shadow coordinates, finds the emitted
|
|
13
|
+
expression node, and reads its checker type — the shared core behind both `interpolationClassifier
|
|
14
|
+
ForRoot` (the runtime lowering, over `createShadowProgram`) and the type-check shadow's own peek-wrap
|
|
15
|
+
(over the check/LSP program). The classifier reads a VERBATIM shadow — one whose interpolations were
|
|
16
|
+
NOT peek-wrapped — so `getFoo()` still types as `Promise<…>` and classifies as `promise`.
|
|
17
|
+
|
|
18
|
+
FAIL-OPEN: no shadow file for the path ⇒ `undefined` (caller degrades to no wrap / plain binding);
|
|
19
|
+
the returned closure wraps its body so ANY throw (missing node, checker hiccup) returns `'sync'`.
|
|
20
|
+
*/
|
|
21
|
+
export function shadowInterpolationClassifier(
|
|
22
|
+
program: ts.Program,
|
|
23
|
+
shadows: Map<string, CompiledShadow>,
|
|
24
|
+
abidePath: string,
|
|
25
|
+
): InterpolationClassifier | undefined {
|
|
26
|
+
const shadowFile = program.getSourceFile(shadowNaming.suffixed(abidePath))
|
|
27
|
+
if (shadowFile === undefined) {
|
|
28
|
+
return undefined
|
|
29
|
+
}
|
|
30
|
+
const checker = program.getTypeChecker()
|
|
31
|
+
return (loc, code) => {
|
|
32
|
+
try {
|
|
33
|
+
const mappings = shadows.get(abidePath)?.mappings ?? []
|
|
34
|
+
const offset = sourceToShadowOffset(mappings, loc)
|
|
35
|
+
if (offset === undefined) {
|
|
36
|
+
return 'sync'
|
|
37
|
+
}
|
|
38
|
+
const node = nodeAtShadowOffset(shadowFile, offset, code.length)
|
|
39
|
+
if (node === undefined) {
|
|
40
|
+
return 'sync'
|
|
41
|
+
}
|
|
42
|
+
return classifyInterpolationType(checker.getTypeAtLocation(node), node, checker)
|
|
43
|
+
} catch {
|
|
44
|
+
return 'sync'
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type ts from 'typescript'
|
|
2
|
+
|
|
3
|
+
/* The compiler-option fields that change how a source file parses or binds — the only inputs
|
|
4
|
+
(besides the text) that make two parses of the same path differ, so the `cachedSourceFile`
|
|
5
|
+
key folds them in to keep two projects with different targets/libs from sharing a file. */
|
|
6
|
+
export function sourceFileOptionsSignature(options: ts.CompilerOptions): string {
|
|
7
|
+
return JSON.stringify([
|
|
8
|
+
options.target,
|
|
9
|
+
options.module,
|
|
10
|
+
options.moduleDetection,
|
|
11
|
+
options.jsx,
|
|
12
|
+
options.jsxImportSource,
|
|
13
|
+
options.useDefineForClassFields,
|
|
14
|
+
options.alwaysStrict,
|
|
15
|
+
options.strict,
|
|
16
|
+
options.verbatimModuleSyntax,
|
|
17
|
+
options.isolatedModules,
|
|
18
|
+
options.lib,
|
|
19
|
+
])
|
|
20
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { BLOCK_CONNECTORS, BLOCK_OPENERS } from './BLOCK_KEYWORDS.ts'
|
|
2
|
+
import type { SemanticToken } from './types/SemanticToken.ts'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
Semantic tokens for a `{#…}`/`{:…}`/`{/…}` control-block head, driven by the parse
|
|
6
|
+
walk (`readBlockToken`) rather than a raw source regex — so the SAME grammar that
|
|
7
|
+
builds the block tree colors it, and a `{#for}` sitting inside a `{expr}`/string/
|
|
8
|
+
backtick region (which the walk skips as opaque expression text) is never miscolored.
|
|
9
|
+
Emits an `operator` token for the `{`+sigil opener and a `keyword` token for the
|
|
10
|
+
block word, plus the `of`/`by` connectors inside a `{#for …}` head. A keyword
|
|
11
|
+
allowlist after the sigil keeps an arbitrary `{:foo}` uncolored (nothing emitted).
|
|
12
|
+
*/
|
|
13
|
+
/* The scan vocabulary, derived from the shared BLOCK_KEYWORDS source of truth (plus the
|
|
14
|
+
`for await` async-each phrase) so it can't drift from the parser. Sorted longest-first so
|
|
15
|
+
the match takes the whole phrase — `for await` beats `for`, `else if` beats `else` —
|
|
16
|
+
since a shorter prefix checked first would win otherwise. */
|
|
17
|
+
const BLOCK_KEYWORDS = [...BLOCK_OPENERS, ...BLOCK_CONNECTORS, 'for await'].sort(
|
|
18
|
+
(a, b) => b.length - a.length,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
const WORD_CHAR = /\w/
|
|
22
|
+
|
|
23
|
+
/*
|
|
24
|
+
Emits the head tokens for a block whose `{` sits at `braceStart`, whose sigil is
|
|
25
|
+
`#`/`:`/`/`, and whose first directive word begins at `keywordStart` (already past the
|
|
26
|
+
leading whitespace `{# for}` allows). Matches the longest allowlist keyword at
|
|
27
|
+
`keywordStart` requiring a trailing word boundary; if no keyword matches, emits NOTHING
|
|
28
|
+
(not even the opener) — mirroring the old regex, which only colored an allowlist match.
|
|
29
|
+
*/
|
|
30
|
+
export function structuralHeadTokens(
|
|
31
|
+
source: string,
|
|
32
|
+
braceStart: number,
|
|
33
|
+
sigil: string,
|
|
34
|
+
keywordStart: number,
|
|
35
|
+
): SemanticToken[] {
|
|
36
|
+
let matched: string | undefined
|
|
37
|
+
for (const keyword of BLOCK_KEYWORDS) {
|
|
38
|
+
if (
|
|
39
|
+
source.startsWith(keyword, keywordStart) &&
|
|
40
|
+
!WORD_CHAR.test(source.charAt(keywordStart + keyword.length))
|
|
41
|
+
) {
|
|
42
|
+
matched = keyword
|
|
43
|
+
break
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (matched === undefined) {
|
|
47
|
+
return []
|
|
48
|
+
}
|
|
49
|
+
const tokens: SemanticToken[] = [
|
|
50
|
+
{ start: braceStart, length: 2, type: 'operator', modifiers: [] },
|
|
51
|
+
{ start: keywordStart, length: matched.length, type: 'keyword', modifiers: [] },
|
|
52
|
+
]
|
|
53
|
+
/* A `{#for …}` head carries abide-only connectors `of`/`by` that the shadow lowers
|
|
54
|
+
away (`of`) or never emits (`by`), so color them here. */
|
|
55
|
+
if (sigil === '#' && (matched === 'for' || matched === 'for await')) {
|
|
56
|
+
forHeadConnectors(source, keywordStart + matched.length, tokens)
|
|
57
|
+
}
|
|
58
|
+
return tokens
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/*
|
|
62
|
+
Scans a `{#for …}` head from `from` to its closing `}`, pushing `keyword` tokens for the
|
|
63
|
+
`of`/`by` connectors at brace depth 0 — skipping any `of`/`by` nested in a destructure,
|
|
64
|
+
call, or string so an identifier or object key is never miscolored.
|
|
65
|
+
*/
|
|
66
|
+
function forHeadConnectors(source: string, from: number, tokens: SemanticToken[]): void {
|
|
67
|
+
let depth = 0
|
|
68
|
+
let cursor = from
|
|
69
|
+
while (cursor < source.length) {
|
|
70
|
+
const char = source.charAt(cursor)
|
|
71
|
+
if (char === '"' || char === "'" || char === '`') {
|
|
72
|
+
cursor += 1
|
|
73
|
+
while (cursor < source.length && source.charAt(cursor) !== char) {
|
|
74
|
+
if (source.charAt(cursor) === '\\') {
|
|
75
|
+
cursor += 1
|
|
76
|
+
}
|
|
77
|
+
cursor += 1
|
|
78
|
+
}
|
|
79
|
+
} else if (char === '{' || char === '(' || char === '[') {
|
|
80
|
+
depth += 1
|
|
81
|
+
} else if (char === ')' || char === ']') {
|
|
82
|
+
depth -= 1
|
|
83
|
+
} else if (char === '}') {
|
|
84
|
+
if (depth === 0) {
|
|
85
|
+
break
|
|
86
|
+
}
|
|
87
|
+
depth -= 1
|
|
88
|
+
} else if (depth === 0 && (char === 'o' || char === 'b')) {
|
|
89
|
+
const word = source.startsWith('of', cursor)
|
|
90
|
+
? 'of'
|
|
91
|
+
: source.startsWith('by', cursor)
|
|
92
|
+
? 'by'
|
|
93
|
+
: undefined
|
|
94
|
+
const isWordBoundary = (offset: number): boolean => /\s/.test(source.charAt(offset))
|
|
95
|
+
if (
|
|
96
|
+
word !== undefined &&
|
|
97
|
+
isWordBoundary(cursor - 1) &&
|
|
98
|
+
isWordBoundary(cursor + word.length)
|
|
99
|
+
) {
|
|
100
|
+
tokens.push({ start: cursor, length: word.length, type: 'keyword', modifiers: [] })
|
|
101
|
+
cursor += word.length
|
|
102
|
+
continue
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
cursor += 1
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { parseTemplateRecovering } from './parseTemplateRecovering.ts'
|
|
2
|
+
import type { SemanticToken } from './types/SemanticToken.ts'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
The LSP's markup + structural highlighting for a full `.abide` component, driven by
|
|
6
|
+
the ONE parse walk (`parseTemplateRecovering`) instead of a pair of hand-rolled
|
|
7
|
+
lexers — so the same grammar that builds the tree colors it, and no coloring can
|
|
8
|
+
drift from what the parser accepts. Runs over the FULL source at `baseOffset 0` with
|
|
9
|
+
token collection on: element/component tag names (`tag`/`type`), attribute names
|
|
10
|
+
(`attribute`), quoted/unquoted values (`string`), comments (`comment`), the
|
|
11
|
+
`<`/`>`/`=`/`/` punctuation (`operator`), and the `{#…}`/`{:…}`/`{/…}` block framing
|
|
12
|
+
(`operator` + `keyword`). The leading `<script>…</script>` reads through the same
|
|
13
|
+
`readElement` path as a nested script, so its open+close tags color with no separate
|
|
14
|
+
leading-region lexer. `{…}` expression interiors and raw `<script>`/`<style>` bodies
|
|
15
|
+
are skipped — those are the type-checking shadow's job. The parse's `nodes` and
|
|
16
|
+
`diagnostics` (including the spurious leading-script diagnostics a full-source parse
|
|
17
|
+
raises) are discarded; only `.tokens` is used.
|
|
18
|
+
*/
|
|
19
|
+
export function templateSemanticTokens(source: string): SemanticToken[] {
|
|
20
|
+
return parseTemplateRecovering(source, 0, true).tokens
|
|
21
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/* The regex that isolates a `.abide` file's leading `<script>` block, capturing its body (`[1]`) —
|
|
2
|
+
the single source `analyzeComponent`, `compileShadow`, and `templateStartOffset` all match on to
|
|
3
|
+
split script from template, so the three can never drift. Stateless (no `/g`), safe to share. */
|
|
4
|
+
export const LEADING_SCRIPT = /^\s*<script[^>]*>([\s\S]*?)<\/script>/
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
The source offset where the template markup begins — just past the closing `</script>` of a
|
|
8
|
+
leading `<script>` block, or `0` when the file is template-only. A component's template
|
|
9
|
+
interpolations all map to source offsets at or after this point, so it doubles as the boundary
|
|
10
|
+
that separates template expressions from `<script>` code.
|
|
11
|
+
*/
|
|
12
|
+
export function templateStartOffset(source: string): number {
|
|
13
|
+
const leadingScript = source.match(LEADING_SCRIPT)
|
|
14
|
+
return leadingScript ? (leadingScript.index ?? 0) + leadingScript[0].length : 0
|
|
15
|
+
}
|
|
@@ -16,8 +16,9 @@ export type TryPlan = {
|
|
|
16
16
|
finallyChildren: TemplateNode[]
|
|
17
17
|
/* No catch → a throw propagates to the enclosing boundary (re-throw / `undefined` thunk). */
|
|
18
18
|
hasCatch: boolean
|
|
19
|
-
/* The catch branch's binding (`catchAs`, `
|
|
20
|
-
|
|
19
|
+
/* The catch branch's binding (`catchAs`, `reactive`); empty when no catch branch. The
|
|
20
|
+
error is a `.value` cell so a catch→catch swap updates `err` in place. The guarded and
|
|
21
|
+
`finally` branches bind nothing. */
|
|
21
22
|
catchBindings: Binding[]
|
|
22
23
|
}
|
|
23
24
|
|
|
@@ -31,6 +32,6 @@ export function tryPlan(node: Extract<TemplateNode, { kind: 'try' }>): TryPlan {
|
|
|
31
32
|
catchAs,
|
|
32
33
|
finallyChildren: finallyBranch?.children ?? [],
|
|
33
34
|
hasCatch: catchBranch !== undefined,
|
|
34
|
-
catchBindings: catchBinding(catchAs, catchBranch !== undefined),
|
|
35
|
+
catchBindings: catchBinding(catchAs, catchBranch !== undefined, true),
|
|
35
36
|
}
|
|
36
37
|
}
|
|
@@ -24,6 +24,10 @@ export type AnalyzedComponent = {
|
|
|
24
24
|
stateNames: Set<string>
|
|
25
25
|
derivedNames: Set<string>
|
|
26
26
|
computedNames: Set<string>
|
|
27
|
+
/* Component signals read through `$$readCell(name)`: every `linked` and every async
|
|
28
|
+
`computed`. Threaded to both back-ends so a template reference (`{draft}`) lowers to
|
|
29
|
+
the unified cell read consistently on client and server. */
|
|
30
|
+
cellReadNames: Set<string>
|
|
27
31
|
nodes: TemplateNode[]
|
|
28
32
|
/* One entry per non-empty `<style>` in the template (in source order): the scope
|
|
29
33
|
attribute its covered elements carry (annotated onto `nodes`) and the scoped
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { LiftPosition } from '../liftAsyncSubExpressions.ts'
|
|
2
|
+
|
|
3
|
+
/* One async-liftable interpolation field of a template node — the shared reading both compile
|
|
4
|
+
front-ends drive their async lowering from (the block-Plan pattern of `ifPlan`/`awaitPlan`,
|
|
5
|
+
applied to interpolations instead of block structure). `code`/`loc` are the authored expression
|
|
6
|
+
and its absolute source offset; `position` is its value/content position (which feeds the shared
|
|
7
|
+
sub-expression walk's `each`-forbids-`AsyncIterable` rule). `subject` marks a control-flow test
|
|
8
|
+
(`{#if}` / `{:elseif}` / `{#switch}`) whose WHOLE expression lifting to one cell makes the block
|
|
9
|
+
hold while the cell is pending. `write` reseeds the field with the runtime's rewritten reference
|
|
10
|
+
and `setAsyncSubject` records the subject verdict — both are no-ops for a READER like the shadow,
|
|
11
|
+
which needs only `code`/`loc`/`position` to peek-wrap its projection. */
|
|
12
|
+
export type AsyncInterpolationField = {
|
|
13
|
+
code: string
|
|
14
|
+
loc: number
|
|
15
|
+
position: LiftPosition
|
|
16
|
+
subject: boolean
|
|
17
|
+
write: (code: string) => void
|
|
18
|
+
setAsyncSubject: (asyncSubject: boolean) => void
|
|
19
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { InterpolationKind } from './InterpolationKind.ts'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Classifies a text-position `{expr}` interpolation from its checker type, keyed by
|
|
5
|
+
the expression's source offset (`loc`) and verbatim `code`. Built by the bundler
|
|
6
|
+
plugin over a warm shadow program (see `abideUiPlugin`) and threaded, OPTIONAL,
|
|
7
|
+
through the compile front-end: when absent the pipeline behaves exactly as before
|
|
8
|
+
(every interpolation binds as a plain value). Always fail-open — a resolution
|
|
9
|
+
failure returns `'sync'`, never throws — so a type hiccup degrades to today's
|
|
10
|
+
behavior instead of breaking the build.
|
|
11
|
+
*/
|
|
12
|
+
export type InterpolationClassifier = (loc: number, code: string) => InterpolationKind
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/*
|
|
2
|
+
How a `{expr}` template interpolation resolves at runtime, from its checker type:
|
|
3
|
+
`promise` (thenable — awaited), `asyncIterable` (drained over time), or `sync`
|
|
4
|
+
(bound as a plain value). Stage A of type-directed interpolation lowering
|
|
5
|
+
classifies into this; later stages pick the binding back-end from it.
|
|
6
|
+
*/
|
|
7
|
+
export type InterpolationKind = 'promise' | 'asyncIterable' | 'sync'
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/*
|
|
2
|
+
A parser-local diagnostic — deliberately smaller than `AbideDiagnostic`, which
|
|
3
|
+
additionally needs the `file` + `category` the parser cannot know. `start` is the
|
|
4
|
+
absolute, `baseOffset`-relative offset (the same coordinate space every node `loc`
|
|
5
|
+
uses, so it round-trips through the shadow `mappings`); `length` is 0 for a point
|
|
6
|
+
diagnostic. Callers (`collectAbideDiagnostics`) adapt it to `AbideDiagnostic`.
|
|
7
|
+
*/
|
|
8
|
+
export type ParseDiagnostic = { message: string; start: number; length: number }
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { InterpolationKind } from './InterpolationKind.ts'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Classifies a `computed`/`linked` SEED expression from its checker type, keyed by the
|
|
5
|
+
seed's source offset (`loc`) and verbatim `code` — the cell-transform counterpart of
|
|
6
|
+
`InterpolationClassifier` (ADR-0023). Built by the bundler plugin over the SAME warm
|
|
7
|
+
shadow program and threaded, OPTIONAL, through the compile front-end to `desugarSignals`.
|
|
8
|
+
|
|
9
|
+
Unlike `InterpolationClassifier`, a resolution failure returns `undefined`, NOT `'sync'`:
|
|
10
|
+
for a seed, fail-open means degrade to today's `isBareCallComputed` syntax heuristic, so a
|
|
11
|
+
failure must be DISTINGUISHABLE from a genuine `sync` type (which routes to the lazy
|
|
12
|
+
`derive` slot). Absent classifier ⇒ every seed resolves `undefined` ⇒ exactly today's
|
|
13
|
+
behavior.
|
|
14
|
+
*/
|
|
15
|
+
export type SeedTypeClassifier = (loc: number, code: string) => InterpolationKind | undefined
|
|
@@ -46,7 +46,17 @@ export type TemplateNode =
|
|
|
46
46
|
keyLoc?: number
|
|
47
47
|
indexLoc?: number
|
|
48
48
|
}
|
|
49
|
-
| {
|
|
49
|
+
| {
|
|
50
|
+
kind: 'if'
|
|
51
|
+
condition: string
|
|
52
|
+
children: TemplateNode[]
|
|
53
|
+
loc?: number
|
|
54
|
+
/* `condition` is a bare async subject — the WHOLE test was one promise/stream that
|
|
55
|
+
`lowerAsyncInterpolations` lifted to a single peek-cell (`{#if getX()}`). The binary
|
|
56
|
+
back-ends then read its pending facet so a still-loading subject renders NO branch,
|
|
57
|
+
not the falsy `{:else}`. Unset (or on an `{:elseif}` chain) → the plain truthy test. */
|
|
58
|
+
asyncSubject?: boolean
|
|
59
|
+
}
|
|
50
60
|
| {
|
|
51
61
|
kind: 'await'
|
|
52
62
|
promise: string
|
|
@@ -77,11 +87,29 @@ export type TemplateNode =
|
|
|
77
87
|
loc?: number
|
|
78
88
|
/* Each authored attribute as a prop. A `spread` entry (`{...code}`) carries no
|
|
79
89
|
`name`; its keys merge in at runtime (`mergeProps`/`spreadProps`). `nameLoc` is
|
|
80
|
-
the prop name's source offset — the anchor for an excess-prop diagnostic.
|
|
81
|
-
|
|
90
|
+
the prop name's source offset — the anchor for an excess-prop diagnostic. `bind`
|
|
91
|
+
marks a `bind:<name>={target}` two-way prop: `code` is the writable target (an
|
|
92
|
+
lvalue or a `{ get, set }` accessor), passed with a write-back channel so the
|
|
93
|
+
child can push changes upstream (`bindProp`/`bindableProp`). */
|
|
94
|
+
props: {
|
|
95
|
+
name: string
|
|
96
|
+
code: string
|
|
97
|
+
loc?: number
|
|
98
|
+
nameLoc?: number
|
|
99
|
+
spread?: boolean
|
|
100
|
+
bind?: true
|
|
101
|
+
}[]
|
|
82
102
|
children: TemplateNode[]
|
|
83
103
|
}
|
|
84
|
-
| {
|
|
104
|
+
| {
|
|
105
|
+
kind: 'switch'
|
|
106
|
+
subject: string
|
|
107
|
+
children: TemplateNode[]
|
|
108
|
+
loc?: number
|
|
109
|
+
/* `subject` is a bare async subject (the whole expression lifted to one peek-cell) —
|
|
110
|
+
a still-loading subject selects no case (not even the default) until it settles. */
|
|
111
|
+
asyncSubject?: boolean
|
|
112
|
+
}
|
|
85
113
|
/* A branch of a `<template switch>` (`match` set) or `<template if>` chain. Inside an
|
|
86
114
|
`if`, `<template elseif={c}>` sets `condition` (match-less, truthy-tested), and
|
|
87
115
|
`<template else>` leaves both unset (the default). `loc` points at whichever
|
|
@@ -92,6 +120,10 @@ export type TemplateNode =
|
|
|
92
120
|
condition?: string
|
|
93
121
|
children: TemplateNode[]
|
|
94
122
|
loc?: number
|
|
123
|
+
/* An `{:elseif}` whose `condition` is a bare async subject (the whole test lifted to one
|
|
124
|
+
peek-cell) — the cond-chain holds at this branch while the cell is pending, resuming the
|
|
125
|
+
top-down walk (skip it / match it / fall to the next branch) once it settles. */
|
|
126
|
+
asyncSubject?: boolean
|
|
95
127
|
}
|
|
96
128
|
/* A `{#snippet row(item)}` snippet: a named, scope-capturing builder declared
|
|
97
129
|
once and called like a function (`{row(item)}`). `params` is the raw
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/*
|
|
2
|
+
A template interpolation sitting in a NON-content value position — where a
|
|
3
|
+
`Promise`/`AsyncIterable` can't be rendered over time and would silently stringify
|
|
4
|
+
to `[object Promise]` (Stage E of type-directed lowering, ADR-0019). `loc`/`code`
|
|
5
|
+
are the expression's source offset and verbatim text (the classifier's key), and
|
|
6
|
+
`position` names the position for the diagnostic. `for await` is the sanctioned
|
|
7
|
+
`{#for await}` iterable — the one value position where an `AsyncIterable` is allowed.
|
|
8
|
+
*/
|
|
9
|
+
export type ValuePositionInterpolation = {
|
|
10
|
+
loc: number
|
|
11
|
+
code: string
|
|
12
|
+
position: 'attribute' | 'if' | 'switch' | 'each' | 'for await'
|
|
13
|
+
}
|