@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,41 +0,0 @@
|
|
|
1
|
-
import { resolve } from 'node:path'
|
|
2
|
-
import { NO_STORE } from '../../shared/CACHE_CONTROL_VALUES.ts'
|
|
3
|
-
import { isLayoutFile } from '../../shared/isLayoutFile.ts'
|
|
4
|
-
import { compileModule } from '../../ui/compile/compileModule.ts'
|
|
5
|
-
|
|
6
|
-
// Reused across requests — strips the embedded author TypeScript to browser JS.
|
|
7
|
-
const TRANSPILER = new Bun.Transpiler({ loader: 'ts' })
|
|
8
|
-
|
|
9
|
-
/*
|
|
10
|
-
Serves one edited `.abide`'s hot module (dev component HMR). Compiles it in hot
|
|
11
|
-
mode — runtime sourced from `window.__abide`, self-invoking `hotReplace` — and
|
|
12
|
-
transpiles the embedded author TypeScript to plain JS the browser can import
|
|
13
|
-
directly (the normal pipeline relies on the bundler's `ts` loader for this). The
|
|
14
|
-
browser imports this in place of a reload; on load it swaps the component's live
|
|
15
|
-
instances. The id is guarded as a project-relative `.abide` under cwd; a bad
|
|
16
|
-
module ID returns 400; a missing file 404s so the client falls back to a reload.
|
|
17
|
-
*/
|
|
18
|
-
export async function devHotModuleResponse(moduleId: string): Promise<Response> {
|
|
19
|
-
const root = process.cwd()
|
|
20
|
-
const path = resolve(root, moduleId)
|
|
21
|
-
if (!moduleId.endsWith('.abide') || !path.startsWith(`${root}/`)) {
|
|
22
|
-
return new Response('bad request', { status: 400 })
|
|
23
|
-
}
|
|
24
|
-
const source = await Bun.file(path)
|
|
25
|
-
.text()
|
|
26
|
-
.catch(() => undefined)
|
|
27
|
-
if (source === undefined) {
|
|
28
|
-
return new Response('not found', { status: 404 })
|
|
29
|
-
}
|
|
30
|
-
const { code } = compileModule(source, {
|
|
31
|
-
isLayout: isLayoutFile(moduleId),
|
|
32
|
-
moduleId,
|
|
33
|
-
hot: true,
|
|
34
|
-
})
|
|
35
|
-
return new Response(TRANSPILER.transformSync(code), {
|
|
36
|
-
headers: {
|
|
37
|
-
'Content-Type': 'application/javascript; charset=utf-8',
|
|
38
|
-
'Cache-Control': NO_STORE,
|
|
39
|
-
},
|
|
40
|
-
})
|
|
41
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Path prefix of the dev-only component hot-module endpoint (`/__abide/hot/<moduleId>`).
|
|
3
|
-
The browser imports `<prefix><moduleId>?v=<hash>` to fetch one edited component's
|
|
4
|
-
hot module instead of reloading. Shared so the server route (createServer) and the
|
|
5
|
-
live-reload client (DEV_RELOAD_CLIENT_SCRIPT) agree.
|
|
6
|
-
*/
|
|
7
|
-
export const DEV_HOT_PREFIX = '/__abide/hot/'
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
HTTP statuses that mean "the server didn't process this request" — the gateway /
|
|
3
|
-
availability family. A response with one of these is treated like a transport failure
|
|
4
|
-
by a durable RPC: the call still throws (the error framework is unchanged), and the
|
|
5
|
-
request is parked for replay on recovery. Everything else (4xx, 500, …) means the
|
|
6
|
-
server received and handled it — that flows to the error framework, never the outbox.
|
|
7
|
-
|
|
8
|
-
502 Bad Gateway · 503 Service Unavailable · 504 Gateway Timeout (abide's own client
|
|
9
|
-
timeout surfaces here too) · 520–527, 530 — Cloudflare/CDN origin-unreachable.
|
|
10
|
-
*/
|
|
11
|
-
export const UNREACHABLE_STATUSES: ReadonlySet<number> = new Set([
|
|
12
|
-
502, 503, 504, 520, 521, 522, 523, 524, 525, 526, 527, 530,
|
|
13
|
-
])
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { isReplayableMethod } from './isReplayableMethod.ts'
|
|
2
|
-
import type { CacheEntry } from './types/CacheEntry.ts'
|
|
3
|
-
|
|
4
|
-
/*
|
|
5
|
-
Whether a cache entry carries a wire request that can be replayed from a warm
|
|
6
|
-
snapshot — a replayable (GET-only) method, so the seed never re-fires a write or
|
|
7
|
-
loses a body. Producer entries (no request) and write methods fail it.
|
|
8
|
-
|
|
9
|
-
The request half of snapshot shippability, factored out so the two gates that
|
|
10
|
-
need it cannot drift: `snapshotShippable` composes it with `settled` for the
|
|
11
|
-
store-filter sites, and `snapshotEntryFromCache` calls it alone — its
|
|
12
|
-
streaming-drain caller hands it still-pending entries it then awaits, so it can't
|
|
13
|
-
require `settled` up front.
|
|
14
|
-
*/
|
|
15
|
-
export function hasReplayableRequest(entry: CacheEntry): boolean {
|
|
16
|
-
return entry.request !== undefined && isReplayableMethod(entry.request.method.toUpperCase())
|
|
17
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Set true only while the client adopts the server-rendered DOM (the hydration
|
|
3
|
-
render pass — see the router's hydrate branch and `hydrate`). Read by `peek`: the
|
|
4
|
-
server materializes no cache value (materializeRetained/cacheEntryFromSnapshot are
|
|
5
|
-
client-only), so server-side peek is uniformly undefined and the SSR render always
|
|
6
|
-
shows the fallback. A snapshot-seeded warm value surfacing DURING hydration would
|
|
7
|
-
diverge from that server text and corrupt the claimed text node, so peek withholds
|
|
8
|
-
it until the pass ends — `wakeHydrationPeeks` then re-runs the scope on the now-
|
|
9
|
-
congruent value. A plain boolean, save/restore-nested so a child hydrate can't clear
|
|
10
|
-
an outer pass early; false on the server (no hydration) and after boot.
|
|
11
|
-
*/
|
|
12
|
-
export const hydratingSlot: { active: boolean } = { active: false }
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Internal slot the browser outbox registry registers its prober into, so the
|
|
3
|
-
shared pending() probe can count parked durable-rpc writes without shared/
|
|
4
|
-
importing browser/. Mirrors tailProbeSlot. The prober reads the doc-backed
|
|
5
|
-
queue entries (reactive inside scope().computed() / scope().effect()) and reports whether any
|
|
6
|
-
undelivered entry matches the selector: a durable rpc selector (carries a
|
|
7
|
-
`url`) narrows to its own queue, the bare form spans every registered queue,
|
|
8
|
-
optional args narrow to one parked call by structural compare. Outbox state is
|
|
9
|
-
pending-only — a parked write has no value, so it never contributes to
|
|
10
|
-
refreshing(); probeRegistries reads this slot only on its `field === 'pending'`
|
|
11
|
-
branch, which is why the slot mirrors tailProbeSlot but reports a bare boolean
|
|
12
|
-
rather than a { pending, refreshing } pair. When no prober is registered (server
|
|
13
|
-
render, or no durable rpc was ever used) pending() sees no parked writes,
|
|
14
|
-
exactly as on the server where there are no client queues.
|
|
15
|
-
*/
|
|
16
|
-
export const outboxProbeSlot: {
|
|
17
|
-
probe: ((selector: unknown, args: unknown) => boolean) | undefined
|
|
18
|
-
} = {
|
|
19
|
-
probe: undefined,
|
|
20
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { OutboxEntry } from './OutboxEntry.ts'
|
|
2
|
-
|
|
3
|
-
/*
|
|
4
|
-
The `.outbox` face on a durable RPC's client proxy: callable to read the reactive list
|
|
5
|
-
of undelivered entries (`rpc.outbox()`), with an awaitable `retry()` that drains the whole
|
|
6
|
-
queue and resolves when the replay settles. Server-side there is no client queue, so the
|
|
7
|
-
face is absent.
|
|
8
|
-
*/
|
|
9
|
-
export type Outbox<Args> = (() => OutboxEntry<Args>[]) & { retry: () => Promise<void> }
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/* The lifecycle status of a parked mutation: waiting to (re)send, or currently sending.
|
|
2
|
-
A write lives in the outbox ONLY while it can't reach the server; the instant the server
|
|
3
|
-
responds at all — a 2xx OR a real 4xx/500 — the entry leaves the queue, so there is no
|
|
4
|
-
terminal `error` state. Only the unreachable family (transport failure / 502/503/504/52x)
|
|
5
|
-
keeps it `queued`. */
|
|
6
|
-
export type OutboxStatus = 'queued' | 'sending'
|
|
7
|
-
|
|
8
|
-
/* One durable, replayable mutation in an RPC's outbox — parked because the original
|
|
9
|
-
call couldn't reach the server (transport failure, or a 502/503/504/52x). `controller`
|
|
10
|
-
is the entry's own abort handle (cancel = `controller.abort()`); `request` is the
|
|
11
|
-
synthesized, persisted Request the drain re-sends; `args` is the typed input (for
|
|
12
|
-
rendering); `error` is why it's parked (the `kind: 'queued'` HttpError from the
|
|
13
|
-
unreachable attempt); `retry()` kicks a FIFO drain. `settled` is the eventual outcome
|
|
14
|
-
of THIS write as if the original call had reached the server — it resolves with the
|
|
15
|
-
decoded result on delivery and rejects with the real HttpError on a server refusal (or
|
|
16
|
-
an AbortError on cancel). It may stay pending indefinitely if the write is never
|
|
17
|
-
replayed; reading it is what arms it (a never-read `settled` never rejects). */
|
|
18
|
-
export type OutboxEntry<Args> = {
|
|
19
|
-
id: string
|
|
20
|
-
controller: AbortController
|
|
21
|
-
request: Request
|
|
22
|
-
args: Args
|
|
23
|
-
status: OutboxStatus
|
|
24
|
-
error?: unknown
|
|
25
|
-
retry: () => Promise<void>
|
|
26
|
-
settled: Promise<unknown>
|
|
27
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Second-argument options for the smart bare rpc call (`getFoo(args, opts)`). The
|
|
3
|
-
call is cached/coalesced/reactive with SWR always on for replayable reads, so
|
|
4
|
-
these govern retention and the refetch clock — NOT transport. Per-call transport
|
|
5
|
-
options (signal/keepalive/priority/cache/headers) live on `.raw(args, init)`.
|
|
6
|
-
|
|
7
|
-
Fetch reads:
|
|
8
|
-
- `ttl` — retention/staleness in ms. Default: 0 on the server (coalesce-only —
|
|
9
|
-
the request is the atomic unit, nothing is retained past it), Infinity on the
|
|
10
|
-
client (retain until invalidate/refresh — the tab is the atomic unit). On the
|
|
11
|
-
client, N ms marks a staleness deadline: the retained value goes stale after N
|
|
12
|
-
ms and the next access triggers a background revalidation (stale stays visible,
|
|
13
|
-
`refreshing()` true); the display value is never dropped. On the server, N ms
|
|
14
|
-
is a plain expiry and only takes effect in the shared store (pair with `shared`).
|
|
15
|
-
- `tags` — free-form invalidation-group labels (see the selector grammar).
|
|
16
|
-
- `throttle` / `debounce` — rate-limit the background refetch clock (set one, not
|
|
17
|
-
both). Leading-edge-then-coalesce, or fire-after-quiet respectively.
|
|
18
|
-
- `shared` — opts the entry into the process-level store instead of the default
|
|
19
|
-
request-scoped one (server) — a store that outlives every request. It selects
|
|
20
|
-
the store only; it does NOT retain (pair it with `ttl` to memoise across
|
|
21
|
-
requests). The shared store is keyed by method+url+args, never by user, so do
|
|
22
|
-
not put per-user data in it — it would be served to other users. Omit `shared`
|
|
23
|
-
for per-request data. Write only `shared: true`; there is no `false` form. On
|
|
24
|
-
the client there is a single tab store, so the flag is a no-op there.
|
|
25
|
-
|
|
26
|
-
Streaming reads (jsonl/sse):
|
|
27
|
-
- `n` — how many retained frames to replay before going live.
|
|
28
|
-
*/
|
|
29
|
-
export type SmartReadOptions = {
|
|
30
|
-
ttl?: number
|
|
31
|
-
tags?: string[]
|
|
32
|
-
throttle?: number
|
|
33
|
-
debounce?: number
|
|
34
|
-
shared?: boolean
|
|
35
|
-
n?: number
|
|
36
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { cacheStores } from './cacheStores.ts'
|
|
2
|
-
|
|
3
|
-
/*
|
|
4
|
-
Fired once when the outermost hydration pass ends (see the router's hydrate branch
|
|
5
|
-
and `hydrate`). During the pass `peek` withheld every snapshot-seeded warm value
|
|
6
|
-
for SSR congruence (see hydratingSlot); now that the pass is over those values are
|
|
7
|
-
congruent to show, so re-run the scopes that read them. A peek scope taps the exact
|
|
8
|
-
call key's lifecycle channel (peek's trackLifecycle), so marking every live entry's
|
|
9
|
-
key wakes them — cheap, one-time, and harmless for non-peek lifecycle readers (they
|
|
10
|
-
recompute the same value). Marks by key, not the bare store-wide channel, because a
|
|
11
|
-
peek(fn, args) scope taps the per-key prefix channel, which a keyless mark misses.
|
|
12
|
-
*/
|
|
13
|
-
export function wakeHydrationPeeks(): void {
|
|
14
|
-
const stores = cacheStores()
|
|
15
|
-
for (const store of stores) {
|
|
16
|
-
for (const key of store.entries.keys()) {
|
|
17
|
-
store.markLifecycle(key)
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
/* The `abide-` tag prefix on framework-owned custom elements (the router's
|
|
2
|
-
`abide-outlet`, the streaming `abide-resolve`/`abide-cache` fragments). `scopeLabel`
|
|
3
|
-
strips it to derive the bare name from a framework-owned host tag (e.g. `abide-resolve` → `resolve`). Child components no longer mount into
|
|
4
|
-
an `abide-<name>` wrapper — they build as marker ranges (see `mountRange`). */
|
|
5
|
-
export const COMPONENT_WRAPPER_PREFIX = 'abide-'
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/* The author-facing reactive primitive names (`state`, `state.linked`, `state.computed`,
|
|
2
|
-
and the destructuring `const {…} = props()`). The compiler no longer reads this —
|
|
3
|
-
recognition is import-resolved (see `resolveReactiveExport.ts`). It remains the canonical
|
|
4
|
-
inventory the docs surface-weighting tool enumerates (`scripts/surfaceWeight.ts`), so the
|
|
5
|
-
grammar's "primitives" bucket stays in sync with the language without hardcoding a list. */
|
|
6
|
-
export const REACTIVE_CALLEES: ReadonlySet<string> = new Set([
|
|
7
|
-
'state',
|
|
8
|
-
'linked',
|
|
9
|
-
'computed',
|
|
10
|
-
'props',
|
|
11
|
-
])
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import ts from 'typescript'
|
|
2
|
-
import { UI_RUNTIME_IMPORTS } from './UI_RUNTIME_IMPORTS.ts'
|
|
3
|
-
|
|
4
|
-
/*
|
|
5
|
-
Independent backstop on the per-component dead-import filter (`compileModule`). The filter
|
|
6
|
-
decides which runtime helpers to import by reading the generated output's identifiers; if it
|
|
7
|
-
ever undercounts — as a raw token scan once did, mis-reading the tail of a module after a
|
|
8
|
-
`${…}` template substitution — a helper gets CALLED but never imported, and the bundle throws
|
|
9
|
-
`ReferenceError` the instant `build()` runs. The router escalates that into a reload loop, so
|
|
10
|
-
the failure is both opaque and unrecoverable.
|
|
11
|
-
|
|
12
|
-
This re-derives the same question a DIFFERENT way (so it can't share the filter's blind spot):
|
|
13
|
-
walk the final module's AST, find every call whose callee is a bare runtime-helper identifier,
|
|
14
|
-
and require that name to be imported or locally bound. A helper name inside a string / template /
|
|
15
|
-
comment is not a `CallExpression` callee, so a docs component quoting framework code
|
|
16
|
-
(`mount(...)` in a snippet) never false-positives. Compile-time only; never on the hot path.
|
|
17
|
-
*/
|
|
18
|
-
/* `module` is either the source string (standalone use — parsed here) or the already-parsed
|
|
19
|
-
tree of the bodies WITHOUT the prepended import block (compile pipeline — shares the one
|
|
20
|
-
parse the dead-import filter made). In the latter case the import block's bindings aren't in
|
|
21
|
-
the tree, so `importedHelpers` supplies the helper names that block will bind. */
|
|
22
|
-
export function assertRuntimeHelpersBound(
|
|
23
|
-
module: string | ts.SourceFile,
|
|
24
|
-
importedHelpers: Set<string>,
|
|
25
|
-
context: string,
|
|
26
|
-
): void {
|
|
27
|
-
/* The EMITTED local names (the `$$` alias when set) — codegen calls those, and the
|
|
28
|
-
aliased import binds them, so the bound/called check must use the same form. */
|
|
29
|
-
const helperNames = new Set(UI_RUNTIME_IMPORTS.map((entry) => entry.alias ?? entry.name))
|
|
30
|
-
const source =
|
|
31
|
-
typeof module === 'string'
|
|
32
|
-
? ts.createSourceFile(
|
|
33
|
-
'module.ts',
|
|
34
|
-
module,
|
|
35
|
-
ts.ScriptTarget.Latest,
|
|
36
|
-
/* setParentNodes */ true,
|
|
37
|
-
)
|
|
38
|
-
: module
|
|
39
|
-
/* Names a bare call can resolve to: the import block's helper bindings (supplied when the
|
|
40
|
-
tree omits them) plus every import binding and declared name in the tree. Collected
|
|
41
|
-
generously (any identifier in a binding position) — over-approximating the bound set only
|
|
42
|
-
risks missing a defect, never raising a false alarm on valid output. */
|
|
43
|
-
const bound = new Set<string>(importedHelpers)
|
|
44
|
-
const calledHelpers: { name: string; position: number }[] = []
|
|
45
|
-
const visit = (node: ts.Node): void => {
|
|
46
|
-
if (
|
|
47
|
-
(ts.isVariableDeclaration(node) ||
|
|
48
|
-
ts.isFunctionDeclaration(node) ||
|
|
49
|
-
ts.isParameter(node) ||
|
|
50
|
-
ts.isBindingElement(node) ||
|
|
51
|
-
ts.isImportSpecifier(node) ||
|
|
52
|
-
ts.isImportClause(node) ||
|
|
53
|
-
ts.isNamespaceImport(node)) &&
|
|
54
|
-
node.name !== undefined &&
|
|
55
|
-
ts.isIdentifier(node.name)
|
|
56
|
-
) {
|
|
57
|
-
bound.add(node.name.text)
|
|
58
|
-
}
|
|
59
|
-
if (
|
|
60
|
-
ts.isCallExpression(node) &&
|
|
61
|
-
ts.isIdentifier(node.expression) &&
|
|
62
|
-
helperNames.has(node.expression.text)
|
|
63
|
-
) {
|
|
64
|
-
calledHelpers.push({
|
|
65
|
-
name: node.expression.text,
|
|
66
|
-
position: node.expression.getStart(source),
|
|
67
|
-
})
|
|
68
|
-
}
|
|
69
|
-
node.forEachChild(visit)
|
|
70
|
-
}
|
|
71
|
-
visit(source)
|
|
72
|
-
|
|
73
|
-
const unbound = calledHelpers.find((call) => !bound.has(call.name))
|
|
74
|
-
if (unbound !== undefined) {
|
|
75
|
-
const { line, character } = source.getLineAndCharacterOfPosition(unbound.position)
|
|
76
|
-
throw new Error(
|
|
77
|
-
`[abide] ${context} calls runtime helper \`${unbound.name}\` at line ${line + 1}:${character + 1} but never imports it — the dead-import filter dropped it. Please report this with the component source.\nOutput:\n${source.text}`,
|
|
78
|
-
)
|
|
79
|
-
}
|
|
80
|
-
}
|
|
@@ -1,313 +0,0 @@
|
|
|
1
|
-
import type { SemanticToken } from './types/SemanticToken.ts'
|
|
2
|
-
|
|
3
|
-
/*
|
|
4
|
-
Lexical highlighting for the HTML-shaped markup of a `.abide` component — element
|
|
5
|
-
and component tag names, attribute names and string values, comments, and the
|
|
6
|
-
`<`/`>`/`=` punctuation. A pure scan of raw source (independent of a successful
|
|
7
|
-
parse, so it survives mid-edit), it exists so the LSP owns the *structure* too,
|
|
8
|
-
not just `{…}` interiors: editors reuse tree-sitter-html as the grammar of record,
|
|
9
|
-
which has no production for abide's `attr={expr}` values — a multiline
|
|
10
|
-
template-literal attribute (`code={`…`}`) drops it into error recovery that never
|
|
11
|
-
re-syncs, miscoloring every element below. Emitting these tokens overrides that
|
|
12
|
-
broken parse wherever real structure sits.
|
|
13
|
-
|
|
14
|
-
`{…}` expression regions (attribute values, interpolations, `{#…}` block heads) and
|
|
15
|
-
the raw bodies of `<script>`/`<style>` are skipped here — those are the shadow
|
|
16
|
-
type-checker's and the structural block tokenizer's jobs. Lowercase tags get the
|
|
17
|
-
`tag` type, uppercase (components) get `type` (a constructor), mirroring the
|
|
18
|
-
tree-sitter highlight split.
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
/* Raw-text elements whose body is JS/CSS, not markup, so the scan jumps past it. */
|
|
22
|
-
const RAW_ELEMENTS = new Set(['script', 'style'])
|
|
23
|
-
|
|
24
|
-
const operator = (start: number, length: number): SemanticToken => ({
|
|
25
|
-
start,
|
|
26
|
-
length,
|
|
27
|
-
type: 'operator',
|
|
28
|
-
modifiers: [],
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
const isNameStart = (char: string): boolean => /[A-Za-z]/.test(char)
|
|
32
|
-
|
|
33
|
-
/* Characters that end a tag or attribute name. */
|
|
34
|
-
const isNameBreak = (char: string): boolean => /[\s=/>{}"'<]/.test(char)
|
|
35
|
-
|
|
36
|
-
export function markupTokens(source: string): SemanticToken[] {
|
|
37
|
-
const tokens: SemanticToken[] = []
|
|
38
|
-
let cursor = 0
|
|
39
|
-
while (cursor < source.length) {
|
|
40
|
-
const char = source.charAt(cursor)
|
|
41
|
-
/* An expression region — interiors are the shadow classifier's job. */
|
|
42
|
-
if (char === '{') {
|
|
43
|
-
cursor = skipExpression(source, cursor)
|
|
44
|
-
continue
|
|
45
|
-
}
|
|
46
|
-
if (char === '<') {
|
|
47
|
-
if (source.startsWith('<!--', cursor)) {
|
|
48
|
-
const end = source.indexOf('-->', cursor + 4)
|
|
49
|
-
const close = end === -1 ? source.length : end + 3
|
|
50
|
-
tokens.push({
|
|
51
|
-
start: cursor,
|
|
52
|
-
length: close - cursor,
|
|
53
|
-
type: 'comment',
|
|
54
|
-
modifiers: [],
|
|
55
|
-
})
|
|
56
|
-
cursor = close
|
|
57
|
-
continue
|
|
58
|
-
}
|
|
59
|
-
const isClose = source.charAt(cursor + 1) === '/'
|
|
60
|
-
const nameStart = cursor + (isClose ? 2 : 1)
|
|
61
|
-
if (isNameStart(source.charAt(nameStart))) {
|
|
62
|
-
tokens.push(operator(cursor, isClose ? 2 : 1))
|
|
63
|
-
const name = readName(source, nameStart)
|
|
64
|
-
const type = /[A-Z]/.test(name.charAt(0)) ? 'type' : 'tag'
|
|
65
|
-
tokens.push({ start: nameStart, length: name.length, type, modifiers: [] })
|
|
66
|
-
const afterName = nameStart + name.length
|
|
67
|
-
if (isClose) {
|
|
68
|
-
cursor = scanToClose(source, afterName, tokens)
|
|
69
|
-
continue
|
|
70
|
-
}
|
|
71
|
-
const { end, selfClosed } = scanAttributes(source, afterName, tokens)
|
|
72
|
-
cursor =
|
|
73
|
-
RAW_ELEMENTS.has(name) && !selfClosed ? skipRawBody(source, end, name) : end
|
|
74
|
-
continue
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
cursor += 1
|
|
78
|
-
}
|
|
79
|
-
return tokens
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/* Reads a tag/element name from `start` (letters, digits, `-`, `_`). */
|
|
83
|
-
function readName(source: string, start: number): string {
|
|
84
|
-
let cursor = start
|
|
85
|
-
while (cursor < source.length && /[A-Za-z0-9_-]/.test(source.charAt(cursor))) {
|
|
86
|
-
cursor += 1
|
|
87
|
-
}
|
|
88
|
-
return source.slice(start, cursor)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/* From after a close tag's name, emits the `>` operator and returns past it. */
|
|
92
|
-
function scanToClose(source: string, from: number, tokens: SemanticToken[]): number {
|
|
93
|
-
let cursor = from
|
|
94
|
-
while (cursor < source.length && source.charAt(cursor) !== '>') {
|
|
95
|
-
cursor += 1
|
|
96
|
-
}
|
|
97
|
-
if (cursor < source.length) {
|
|
98
|
-
tokens.push(operator(cursor, 1))
|
|
99
|
-
return cursor + 1
|
|
100
|
-
}
|
|
101
|
-
return cursor
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/* Scans an open tag's attributes from after its name to the closing `>`/`/>`,
|
|
105
|
-
emitting an `attribute` token per name, `operator` for `=`, and string tokens
|
|
106
|
-
for quoted values; `{…}` values and spreads are left to the shadow. */
|
|
107
|
-
function scanAttributes(
|
|
108
|
-
source: string,
|
|
109
|
-
from: number,
|
|
110
|
-
tokens: SemanticToken[],
|
|
111
|
-
): { end: number; selfClosed: boolean } {
|
|
112
|
-
let cursor = from
|
|
113
|
-
while (cursor < source.length) {
|
|
114
|
-
const char = source.charAt(cursor)
|
|
115
|
-
if (/\s/.test(char)) {
|
|
116
|
-
cursor += 1
|
|
117
|
-
continue
|
|
118
|
-
}
|
|
119
|
-
if (char === '>') {
|
|
120
|
-
tokens.push(operator(cursor, 1))
|
|
121
|
-
return { end: cursor + 1, selfClosed: false }
|
|
122
|
-
}
|
|
123
|
-
if (char === '/' && source.charAt(cursor + 1) === '>') {
|
|
124
|
-
tokens.push(operator(cursor, 2))
|
|
125
|
-
return { end: cursor + 2, selfClosed: true }
|
|
126
|
-
}
|
|
127
|
-
/* `{…code}` spread / expression attribute — shadow's job. */
|
|
128
|
-
if (char === '{') {
|
|
129
|
-
cursor = skipExpression(source, cursor)
|
|
130
|
-
continue
|
|
131
|
-
}
|
|
132
|
-
const nameStart = cursor
|
|
133
|
-
while (cursor < source.length && !isNameBreak(source.charAt(cursor))) {
|
|
134
|
-
cursor += 1
|
|
135
|
-
}
|
|
136
|
-
if (cursor === nameStart) {
|
|
137
|
-
/* No progress on a stray delimiter — advance to avoid a stall. */
|
|
138
|
-
cursor += 1
|
|
139
|
-
continue
|
|
140
|
-
}
|
|
141
|
-
tokens.push({
|
|
142
|
-
start: nameStart,
|
|
143
|
-
length: cursor - nameStart,
|
|
144
|
-
type: 'attribute',
|
|
145
|
-
modifiers: [],
|
|
146
|
-
})
|
|
147
|
-
let eq = cursor
|
|
148
|
-
while (eq < source.length && /\s/.test(source.charAt(eq))) {
|
|
149
|
-
eq += 1
|
|
150
|
-
}
|
|
151
|
-
if (source.charAt(eq) === '=') {
|
|
152
|
-
tokens.push(operator(eq, 1))
|
|
153
|
-
eq += 1
|
|
154
|
-
while (eq < source.length && /\s/.test(source.charAt(eq))) {
|
|
155
|
-
eq += 1
|
|
156
|
-
}
|
|
157
|
-
cursor = scanAttributeValue(source, eq, tokens)
|
|
158
|
-
} else {
|
|
159
|
-
cursor = eq
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
return { end: cursor, selfClosed: false }
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/* Scans an attribute value: a `{…}` expression (skipped), a quoted string (split
|
|
166
|
-
around any `{…}` interpolations), or a bare unquoted run. Returns past it. */
|
|
167
|
-
function scanAttributeValue(source: string, from: number, tokens: SemanticToken[]): number {
|
|
168
|
-
const char = source.charAt(from)
|
|
169
|
-
if (char === '{') {
|
|
170
|
-
return skipExpression(source, from)
|
|
171
|
-
}
|
|
172
|
-
if (char === '"' || char === "'") {
|
|
173
|
-
return scanQuotedValue(source, from, tokens)
|
|
174
|
-
}
|
|
175
|
-
let cursor = from
|
|
176
|
-
/* Unquoted values run to whitespace or `>`; a `/` is part of the value (a URL)
|
|
177
|
-
UNLESS it's the self-close `/>`, so stop before that `/` and leave it for the
|
|
178
|
-
attribute scanner to read as the self-close marker. */
|
|
179
|
-
while (cursor < source.length) {
|
|
180
|
-
const valueChar = source.charAt(cursor)
|
|
181
|
-
if (/[\s>]/.test(valueChar) || (valueChar === '/' && source.charAt(cursor + 1) === '>')) {
|
|
182
|
-
break
|
|
183
|
-
}
|
|
184
|
-
cursor += 1
|
|
185
|
-
}
|
|
186
|
-
if (cursor > from) {
|
|
187
|
-
tokens.push({ start: from, length: cursor - from, type: 'string', modifiers: [] })
|
|
188
|
-
}
|
|
189
|
-
return cursor
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/* Emits string tokens for the literal runs of a quoted value (delimiters
|
|
193
|
-
included), skipping `{…}` interpolations so the shadow colors their interiors. */
|
|
194
|
-
function scanQuotedValue(source: string, start: number, tokens: SemanticToken[]): number {
|
|
195
|
-
const quote = source.charAt(start)
|
|
196
|
-
let segmentStart = start
|
|
197
|
-
let cursor = start + 1
|
|
198
|
-
const flush = (end: number): void => {
|
|
199
|
-
if (end > segmentStart) {
|
|
200
|
-
tokens.push({
|
|
201
|
-
start: segmentStart,
|
|
202
|
-
length: end - segmentStart,
|
|
203
|
-
type: 'string',
|
|
204
|
-
modifiers: [],
|
|
205
|
-
})
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
while (cursor < source.length) {
|
|
209
|
-
const char = source.charAt(cursor)
|
|
210
|
-
if (char === quote) {
|
|
211
|
-
flush(cursor + 1)
|
|
212
|
-
return cursor + 1
|
|
213
|
-
}
|
|
214
|
-
if (char === '{') {
|
|
215
|
-
flush(cursor)
|
|
216
|
-
cursor = skipExpression(source, cursor)
|
|
217
|
-
segmentStart = cursor
|
|
218
|
-
continue
|
|
219
|
-
}
|
|
220
|
-
cursor += 1
|
|
221
|
-
}
|
|
222
|
-
flush(cursor)
|
|
223
|
-
return cursor
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/* From the close `>` of a raw-text element's open tag, returns the index of its
|
|
227
|
-
`</tag` so the close tag is scanned as markup but the body is left untouched. */
|
|
228
|
-
function skipRawBody(source: string, from: number, tag: string): number {
|
|
229
|
-
const close = source.indexOf(`</${tag}`, from)
|
|
230
|
-
return close === -1 ? source.length : close
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/* From the opening `{` at `start`, returns the index past the matching `}`,
|
|
234
|
-
tracking nested braces and skipping strings, template literals (with `${…}`),
|
|
235
|
-
and JS comments so their contents never sway the depth. A regex literal holding
|
|
236
|
-
an unbalanced brace is the one unhandled case — rare, accepted for a highlighter. */
|
|
237
|
-
function skipExpression(source: string, start: number): number {
|
|
238
|
-
let depth = 0
|
|
239
|
-
let cursor = start
|
|
240
|
-
while (cursor < source.length) {
|
|
241
|
-
const char = source.charAt(cursor)
|
|
242
|
-
if (char === '"' || char === "'") {
|
|
243
|
-
cursor = skipString(source, cursor)
|
|
244
|
-
continue
|
|
245
|
-
}
|
|
246
|
-
if (char === '`') {
|
|
247
|
-
cursor = skipTemplate(source, cursor)
|
|
248
|
-
continue
|
|
249
|
-
}
|
|
250
|
-
if (char === '/' && source.charAt(cursor + 1) === '/') {
|
|
251
|
-
const newline = source.indexOf('\n', cursor)
|
|
252
|
-
cursor = newline === -1 ? source.length : newline
|
|
253
|
-
continue
|
|
254
|
-
}
|
|
255
|
-
if (char === '/' && source.charAt(cursor + 1) === '*') {
|
|
256
|
-
const end = source.indexOf('*/', cursor + 2)
|
|
257
|
-
cursor = end === -1 ? source.length : end + 2
|
|
258
|
-
continue
|
|
259
|
-
}
|
|
260
|
-
if (char === '{') {
|
|
261
|
-
depth += 1
|
|
262
|
-
} else if (char === '}') {
|
|
263
|
-
depth -= 1
|
|
264
|
-
if (depth === 0) {
|
|
265
|
-
return cursor + 1
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
cursor += 1
|
|
269
|
-
}
|
|
270
|
-
return cursor
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/* From the opening quote at `start`, returns the index past the matching quote;
|
|
274
|
-
a backslash escapes the next character. */
|
|
275
|
-
function skipString(source: string, start: number): number {
|
|
276
|
-
const quote = source.charAt(start)
|
|
277
|
-
let cursor = start + 1
|
|
278
|
-
while (cursor < source.length) {
|
|
279
|
-
const char = source.charAt(cursor)
|
|
280
|
-
if (char === '\\') {
|
|
281
|
-
cursor += 2
|
|
282
|
-
continue
|
|
283
|
-
}
|
|
284
|
-
if (char === quote) {
|
|
285
|
-
return cursor + 1
|
|
286
|
-
}
|
|
287
|
-
cursor += 1
|
|
288
|
-
}
|
|
289
|
-
return cursor
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/* From the opening backtick at `start`, returns the index past the matching
|
|
293
|
-
backtick. `${…}` re-enters expression scanning so its inner braces balance;
|
|
294
|
-
braces in the literal text are ignored. */
|
|
295
|
-
function skipTemplate(source: string, start: number): number {
|
|
296
|
-
let cursor = start + 1
|
|
297
|
-
while (cursor < source.length) {
|
|
298
|
-
const char = source.charAt(cursor)
|
|
299
|
-
if (char === '\\') {
|
|
300
|
-
cursor += 2
|
|
301
|
-
continue
|
|
302
|
-
}
|
|
303
|
-
if (char === '`') {
|
|
304
|
-
return cursor + 1
|
|
305
|
-
}
|
|
306
|
-
if (char === '$' && source.charAt(cursor + 1) === '{') {
|
|
307
|
-
cursor = skipExpression(source, cursor + 1)
|
|
308
|
-
continue
|
|
309
|
-
}
|
|
310
|
-
cursor += 1
|
|
311
|
-
}
|
|
312
|
-
return cursor
|
|
313
|
-
}
|