@abide/abide 0.49.0 → 0.50.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +564 -437
- package/CHANGELOG.md +183 -0
- package/README.md +165 -202
- 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
|
@@ -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
|
-
}
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import type { SemanticToken } from './types/SemanticToken.ts'
|
|
2
|
-
|
|
3
|
-
/*
|
|
4
|
-
Lexical highlighting for `{#…}`/`{:…}`/`{/…}` control-flow framing — the part the
|
|
5
|
-
HTML grammar sees only as text and the shadow program lowers away. A pure scan of
|
|
6
|
-
raw source (independent of a successful parse, so it survives mid-edit), it emits
|
|
7
|
-
an `operator` token for the `{`+sigil opener and a `keyword` token for the block
|
|
8
|
-
word, plus the `of`/`by` connectors inside a `{#for …}` head. Expression interiors
|
|
9
|
-
and bare `{expr}` interpolations are NOT touched here — those are the shadow's job.
|
|
10
|
-
A keyword allowlist after the sigil prevents matching arbitrary `{:foo}` text.
|
|
11
|
-
Longest phrases first so `for await` beats `for` and `else if` beats `else`.
|
|
12
|
-
*/
|
|
13
|
-
const BLOCK_KEYWORDS = [
|
|
14
|
-
'for await',
|
|
15
|
-
'else if',
|
|
16
|
-
'if',
|
|
17
|
-
'for',
|
|
18
|
-
'await',
|
|
19
|
-
'switch',
|
|
20
|
-
'case',
|
|
21
|
-
'default',
|
|
22
|
-
'try',
|
|
23
|
-
'catch',
|
|
24
|
-
'finally',
|
|
25
|
-
'then',
|
|
26
|
-
'else',
|
|
27
|
-
]
|
|
28
|
-
|
|
29
|
-
const BLOCK_HEAD = new RegExp(`\\{([#:/])\\s*(${BLOCK_KEYWORDS.join('|')})\\b`, 'g')
|
|
30
|
-
|
|
31
|
-
export function structuralBlockTokens(source: string): SemanticToken[] {
|
|
32
|
-
const tokens: SemanticToken[] = []
|
|
33
|
-
for (const match of source.matchAll(BLOCK_HEAD)) {
|
|
34
|
-
const braceStart = match.index
|
|
35
|
-
const sigil = match[1]
|
|
36
|
-
const keyword = match[2]
|
|
37
|
-
if (keyword === undefined) {
|
|
38
|
-
continue
|
|
39
|
-
}
|
|
40
|
-
const keywordStart = braceStart + match[0].length - keyword.length
|
|
41
|
-
tokens.push({ start: braceStart, length: 2, type: 'operator', modifiers: [] })
|
|
42
|
-
tokens.push({ start: keywordStart, length: keyword.length, type: 'keyword', modifiers: [] })
|
|
43
|
-
/* A `{#for …}` head carries abide-only connectors `of`/`by` that the shadow
|
|
44
|
-
lowers away (`of`) or never emits (`by`), so color them here. */
|
|
45
|
-
if (sigil === '#' && (keyword === 'for' || keyword === 'for await')) {
|
|
46
|
-
tokens.push(...forHeadConnectors(source, keywordStart + keyword.length))
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return tokens
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/*
|
|
53
|
-
Scans a `{#for …}` head from `from` to its closing `}`, emitting `keyword` tokens
|
|
54
|
-
for the `of`/`by` connectors at brace depth 0 — skipping any `of`/`by` nested in a
|
|
55
|
-
destructure, call, or string so an identifier or object key is never miscolored.
|
|
56
|
-
*/
|
|
57
|
-
function forHeadConnectors(source: string, from: number): SemanticToken[] {
|
|
58
|
-
const tokens: SemanticToken[] = []
|
|
59
|
-
let depth = 0
|
|
60
|
-
let cursor = from
|
|
61
|
-
while (cursor < source.length) {
|
|
62
|
-
const char = source.charAt(cursor)
|
|
63
|
-
if (char === '"' || char === "'" || char === '`') {
|
|
64
|
-
cursor += 1
|
|
65
|
-
while (cursor < source.length && source.charAt(cursor) !== char) {
|
|
66
|
-
if (source.charAt(cursor) === '\\') {
|
|
67
|
-
cursor += 1
|
|
68
|
-
}
|
|
69
|
-
cursor += 1
|
|
70
|
-
}
|
|
71
|
-
} else if (char === '{' || char === '(' || char === '[') {
|
|
72
|
-
depth += 1
|
|
73
|
-
} else if (char === ')' || char === ']') {
|
|
74
|
-
depth -= 1
|
|
75
|
-
} else if (char === '}') {
|
|
76
|
-
if (depth === 0) {
|
|
77
|
-
break
|
|
78
|
-
}
|
|
79
|
-
depth -= 1
|
|
80
|
-
} else if (depth === 0 && (char === 'o' || char === 'b')) {
|
|
81
|
-
const word = source.startsWith('of', cursor)
|
|
82
|
-
? 'of'
|
|
83
|
-
: source.startsWith('by', cursor)
|
|
84
|
-
? 'by'
|
|
85
|
-
: undefined
|
|
86
|
-
const isWordBoundary = (offset: number): boolean => /\s/.test(source.charAt(offset))
|
|
87
|
-
if (
|
|
88
|
-
word !== undefined &&
|
|
89
|
-
isWordBoundary(cursor - 1) &&
|
|
90
|
-
isWordBoundary(cursor + word.length)
|
|
91
|
-
) {
|
|
92
|
-
tokens.push({ start: cursor, length: word.length, type: 'keyword', modifiers: [] })
|
|
93
|
-
cursor += word.length
|
|
94
|
-
continue
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
cursor += 1
|
|
98
|
-
}
|
|
99
|
-
return tokens
|
|
100
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { seedResolved } from '../seedResolved.ts'
|
|
2
|
-
import { isComment } from './isComment.ts'
|
|
3
|
-
|
|
4
|
-
/*
|
|
5
|
-
Bundle-side consumer of an SSR stream chunk, the counterpart of the doc stream's inline
|
|
6
|
-
vanilla scripts (`SSR_SWAP_SCRIPT`'s `__abideSwap` and `CACHE_RESOLVE_SCRIPT`'s `__abideResolve`) for a stream the
|
|
7
|
-
running bundle consumes itself — streaming SPA navigation, socket-delivered SSR. It routes
|
|
8
|
-
the two chunk kinds the stream interleaves:
|
|
9
|
-
|
|
10
|
-
- `<abide-cache>{StreamedResolution}</abide-cache>` → seed the streamed cache partition.
|
|
11
|
-
A script set via innerHTML never runs, so the cache channel rides a data frame here
|
|
12
|
-
(not the doc stream's `<script>__abideResolve(…)</script>`); seeding it keeps the
|
|
13
|
-
pending {#await} read warm instead of cold-missing to the network.
|
|
14
|
-
- `<abide-resolve data-id="ID"><script type="application/json">…</script>…</abide-resolve>`
|
|
15
|
-
→ register the value in the resume manifest (for hydration), find the matching
|
|
16
|
-
`<!--abide:await:ID-->…<!--/abide:await:ID-->` boundary in `root`, remove the pending
|
|
17
|
-
nodes between the markers, and insert the resolved content in their place.
|
|
18
|
-
|
|
19
|
-
The pending shell painted instantly; this swaps in each value as it arrives — completing
|
|
20
|
-
the out-of-order streaming loop on the client.
|
|
21
|
-
*/
|
|
22
|
-
// @documentation plumbing
|
|
23
|
-
export function applyResolved(root: Element, frame: string): void {
|
|
24
|
-
const holder = document.createElement('div')
|
|
25
|
-
holder.innerHTML = frame
|
|
26
|
-
const resolved = holder.firstChild as Element | null
|
|
27
|
-
if (resolved === null || resolved.getAttribute === undefined) {
|
|
28
|
-
return
|
|
29
|
-
}
|
|
30
|
-
/* A cache-seed frame warms the streamed cache partition — paired with the DOM swap so a
|
|
31
|
-
bundle-consumed stream can't adopt a resolved branch while dropping its cache key. */
|
|
32
|
-
if (resolved.nodeName === 'ABIDE-CACHE') {
|
|
33
|
-
try {
|
|
34
|
-
seedResolved({ kind: 'cache', resolution: JSON.parse(resolved.textContent ?? 'null') })
|
|
35
|
-
} catch {
|
|
36
|
-
/* malformed payload — leave unseeded; the read falls back to a live fetch */
|
|
37
|
-
}
|
|
38
|
-
return
|
|
39
|
-
}
|
|
40
|
-
const id = resolved.getAttribute('data-id')
|
|
41
|
-
if (id === null) {
|
|
42
|
-
return
|
|
43
|
-
}
|
|
44
|
-
/* The resolved value rides in a leading <script type=application/json> as ref-json
|
|
45
|
-
text; store it raw and remove the node so only the resolved markup moves into the
|
|
46
|
-
boundary. Decoding is deferred to the read in `awaitBlock` (which has the codec);
|
|
47
|
-
recording it lets a later hydrate adopt this branch (no re-fetch). */
|
|
48
|
-
const payload = resolved.firstChild as Element | null
|
|
49
|
-
if (payload !== null && payload.nodeName === 'SCRIPT') {
|
|
50
|
-
seedResolved({ kind: 'resume', id: Number(id), resume: payload.textContent ?? '' })
|
|
51
|
-
payload.remove()
|
|
52
|
-
}
|
|
53
|
-
const open = `abide:await:${id}`
|
|
54
|
-
const close = `/abide:await:${id}`
|
|
55
|
-
const boundary = findBoundary(root, open)
|
|
56
|
-
if (boundary === undefined) {
|
|
57
|
-
return
|
|
58
|
-
}
|
|
59
|
-
const { parent, start } = boundary
|
|
60
|
-
/* Remove the pending nodes between the markers (exclusive of the markers). */
|
|
61
|
-
let node = start.nextSibling
|
|
62
|
-
while (node !== null && !isComment(node, close)) {
|
|
63
|
-
const next = node.nextSibling
|
|
64
|
-
parent.removeChild(node)
|
|
65
|
-
node = next
|
|
66
|
-
}
|
|
67
|
-
/* Insert resolved content after the open marker. */
|
|
68
|
-
let anchor = start.nextSibling
|
|
69
|
-
for (const child of [...resolved.childNodes]) {
|
|
70
|
-
parent.insertBefore(child, anchor)
|
|
71
|
-
anchor = child.nextSibling
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/* Depth-first search for the parent + open-marker comment of a boundary. */
|
|
76
|
-
function findBoundary(node: Node, open: string): { parent: Node; start: Node } | undefined {
|
|
77
|
-
for (const child of [...node.childNodes]) {
|
|
78
|
-
if (isComment(child, open)) {
|
|
79
|
-
return { parent: node, start: child }
|
|
80
|
-
}
|
|
81
|
-
const nested = findBoundary(child, open)
|
|
82
|
-
if (nested !== undefined) {
|
|
83
|
-
return nested
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return undefined
|
|
87
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import type { Doc } from '../runtime/types/Doc.ts'
|
|
2
|
-
import { readCall } from './readCall.ts'
|
|
3
|
-
|
|
4
|
-
/*
|
|
5
|
-
An in-place-mutating array method lowered on a reactive-document array. The
|
|
6
|
-
doc-access lowering rewrites e.g. `model.items.splice(i, 1)` to
|
|
7
|
-
`$$mutateDocArray(model, "items", "splice", [i, 1])` rather than the bare
|
|
8
|
-
`$$readCall(model.read("items"), …)` it emits for non-mutating calls. The reason:
|
|
9
|
-
`read` returns the live tree array by reference, so a bare `.splice()`/`.sort()`/…
|
|
10
|
-
would mutate the document in place while never emitting a patch — no reader would
|
|
11
|
-
re-render and undo/persistence/multiplayer sync (all keyed off `apply`) would
|
|
12
|
-
never see the change (`.push` is the one already-handled exception, lowered to
|
|
13
|
-
`add` patches). Cloning the array, applying the mutation to the copy, and writing
|
|
14
|
-
it back through `replace` emits a real patch: readers wake and the change is
|
|
15
|
-
journalled. Element identity is preserved for untouched entries (a shallow
|
|
16
|
-
`slice`), so a keyed `{#for}` reconciles instead of rebuilding. The native
|
|
17
|
-
method's return value (spliced-out elements, the new length, …) is returned
|
|
18
|
-
unchanged.
|
|
19
|
-
*/
|
|
20
|
-
// @documentation plumbing
|
|
21
|
-
export function mutateDocArray(doc: Doc, path: string, member: string, args: unknown[]): unknown {
|
|
22
|
-
const current = doc.read<unknown>(path)
|
|
23
|
-
/* Not an array — nothing patchable to clone. Fall back to the guarded in-place
|
|
24
|
-
call so the author still gets readCall's authored-scope error message. */
|
|
25
|
-
if (!Array.isArray(current)) {
|
|
26
|
-
return readCall(current, path, member, args)
|
|
27
|
-
}
|
|
28
|
-
const copy = current.slice()
|
|
29
|
-
const method = (copy as unknown as Record<string, unknown>)[member]
|
|
30
|
-
if (typeof method !== 'function') {
|
|
31
|
-
throw new TypeError(
|
|
32
|
-
`abide: cannot call .${member}() — "${path}".${member} is not a function (got ${typeof method})`,
|
|
33
|
-
)
|
|
34
|
-
}
|
|
35
|
-
const returned = (method as (...callArgs: unknown[]) => unknown).apply(copy, args)
|
|
36
|
-
doc.replace(path, copy)
|
|
37
|
-
return returned
|
|
38
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
The human-readable name for the scope the direct-mount entry (`mount`/`hydrate`)
|
|
3
|
-
establishes, derived from its host element's tag. The router instead fills layers into
|
|
4
|
-
marker boundaries with no host element, so it passes each layer's route key as an
|
|
5
|
-
explicit label (`fillBoundary`); a nested child likewise passes its component name (see
|
|
6
|
-
`mountRange`). The `abide-` prefix strip turns a framework host tag like `abide-resolve`
|
|
7
|
-
into `resolve`; any other host yields its lowercased tag. Feeds the inspector's
|
|
8
|
-
Reactive tab a readable scope name; computed and stored in all builds (only the
|
|
9
|
-
inspector surface is dev-only). Returns undefined when there's no element to name from.
|
|
10
|
-
*/
|
|
11
|
-
import { COMPONENT_WRAPPER_PREFIX } from '../COMPONENT_WRAPPER_PREFIX.ts'
|
|
12
|
-
|
|
13
|
-
export function scopeLabel(host: Element): string | undefined {
|
|
14
|
-
const tag = host.tagName?.toLowerCase()
|
|
15
|
-
if (tag === undefined) {
|
|
16
|
-
return undefined
|
|
17
|
-
}
|
|
18
|
-
return tag.startsWith(COMPONENT_WRAPPER_PREFIX)
|
|
19
|
-
? tag.slice(COMPONENT_WRAPPER_PREFIX.length)
|
|
20
|
-
: tag
|
|
21
|
-
}
|
package/src/lib/ui/dom/text.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { effect } from '../effect.ts'
|
|
2
|
-
|
|
3
|
-
/*
|
|
4
|
-
A text node whose content tracks `read()`. The effect captures whatever reactive
|
|
5
|
-
cells `read` touches, so the node — and only this node — re-renders when they
|
|
6
|
-
change: one text node, one effect — fine-grained granularity.
|
|
7
|
-
|
|
8
|
-
Plain-text only — `read()` is coerced with String(). For a `{expr}` interpolation
|
|
9
|
-
that may yield an `html\`…\`` or snippet value the compiler emits `appendText`,
|
|
10
|
-
which branches on the branded value and inserts raw markup; `text` cannot host
|
|
11
|
-
multiple nodes and would stringify such a value to `[object Object]`.
|
|
12
|
-
*/
|
|
13
|
-
// @documentation plumbing
|
|
14
|
-
export function text(read: () => unknown): Text {
|
|
15
|
-
const node = document.createTextNode('')
|
|
16
|
-
effect(() => {
|
|
17
|
-
node.data = String(read())
|
|
18
|
-
})
|
|
19
|
-
return node
|
|
20
|
-
}
|
package/src/lib/ui/history.ts
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { PATCH_BUS } from './runtime/PATCH_BUS.ts'
|
|
2
|
-
import type { Doc } from './runtime/types/Doc.ts'
|
|
3
|
-
import type { Patch } from './runtime/types/Patch.ts'
|
|
4
|
-
import type { History } from './types/History.ts'
|
|
5
|
-
|
|
6
|
-
/*
|
|
7
|
-
Undo/redo for a document. Subscribes the `PATCH_BUS`, filters to this `doc`, and
|
|
8
|
-
journals each applied patch's `inverse` into a bounded stack — O(size of the
|
|
9
|
-
change) per step, never a tree snapshot. `undo`/`redo` replay inverses through
|
|
10
|
-
`doc.apply`; the patches that replay itself emits are routed into a `sink` so a
|
|
11
|
-
replay never opens a fresh entry nor clears the opposite stack (it instead fills
|
|
12
|
-
it, keeping the move reversible). A genuine new edit clears the redo stack.
|
|
13
|
-
`transaction(fn)` groups every patch `fn` emits into one entry. `limit` caps
|
|
14
|
-
depth — the oldest entry drops. Inert (but harmless) on the server.
|
|
15
|
-
*/
|
|
16
|
-
// @documentation plumbing
|
|
17
|
-
export function history(doc: Doc, { limit = 100 }: { limit?: number } = {}): History {
|
|
18
|
-
const undoStack: Patch[][] = []
|
|
19
|
-
const redoStack: Patch[][] = []
|
|
20
|
-
/* When set, captured inverses accumulate here instead of opening a new undo entry
|
|
21
|
-
— held by an in-progress transaction and by an undo/redo replay. */
|
|
22
|
-
let sink: Patch[] | undefined
|
|
23
|
-
|
|
24
|
-
/* Push one finished entry, evicting the oldest past `limit`. */
|
|
25
|
-
const push = (stack: Patch[][], entry: Patch[]): void => {
|
|
26
|
-
stack.push(entry)
|
|
27
|
-
if (stack.length > limit) {
|
|
28
|
-
stack.shift()
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const unsubscribe = PATCH_BUS.subscribe((event) => {
|
|
33
|
-
if (event.doc !== doc || event.inverse === undefined) {
|
|
34
|
-
return
|
|
35
|
-
}
|
|
36
|
-
if (sink !== undefined) {
|
|
37
|
-
sink.push(event.inverse)
|
|
38
|
-
return
|
|
39
|
-
}
|
|
40
|
-
push(undoStack, [event.inverse])
|
|
41
|
-
redoStack.length = 0
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
/* Apply an entry's inverses in reverse application order (LIFO), capturing the
|
|
45
|
-
inverses they emit — the original forward patches — onto `target`, so the move
|
|
46
|
-
is itself reversible and lands on the opposite stack. */
|
|
47
|
-
const replay = (entry: Patch[], target: Patch[][]): void => {
|
|
48
|
-
const captured: Patch[] = []
|
|
49
|
-
sink = captured
|
|
50
|
-
try {
|
|
51
|
-
for (let index = entry.length - 1; index >= 0; index -= 1) {
|
|
52
|
-
doc.apply(entry[index] as Patch)
|
|
53
|
-
}
|
|
54
|
-
} finally {
|
|
55
|
-
sink = undefined
|
|
56
|
-
/* Push captured inverses even if `apply` threw mid-loop: the already-popped
|
|
57
|
-
entry is gone, so the forward patches for what DID apply must land on the
|
|
58
|
-
opposite stack or a partial replay becomes irreversible. */
|
|
59
|
-
if (captured.length > 0) {
|
|
60
|
-
push(target, captured)
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return {
|
|
66
|
-
undo: () => {
|
|
67
|
-
const entry = undoStack.pop()
|
|
68
|
-
if (entry !== undefined) {
|
|
69
|
-
replay(entry, redoStack)
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
redo: () => {
|
|
73
|
-
const entry = redoStack.pop()
|
|
74
|
-
if (entry !== undefined) {
|
|
75
|
-
replay(entry, undoStack)
|
|
76
|
-
}
|
|
77
|
-
},
|
|
78
|
-
canUndo: () => undoStack.length > 0,
|
|
79
|
-
canRedo: () => redoStack.length > 0,
|
|
80
|
-
transaction: (run: () => void) => {
|
|
81
|
-
/* A nested transaction folds into the one already collecting. */
|
|
82
|
-
if (sink !== undefined) {
|
|
83
|
-
run()
|
|
84
|
-
return
|
|
85
|
-
}
|
|
86
|
-
const entry: Patch[] = []
|
|
87
|
-
sink = entry
|
|
88
|
-
try {
|
|
89
|
-
run()
|
|
90
|
-
} finally {
|
|
91
|
-
sink = undefined
|
|
92
|
-
/* Commit whatever was applied even if `run` threw partway: those patches are
|
|
93
|
-
already live in the doc, so their inverses must reach the undo stack or the
|
|
94
|
-
committed changes are left with no way to undo them. The throw still
|
|
95
|
-
propagates after this. */
|
|
96
|
-
if (entry.length > 0) {
|
|
97
|
-
push(undoStack, entry)
|
|
98
|
-
redoStack.length = 0
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
|
-
dispose: () => {
|
|
103
|
-
unsubscribe()
|
|
104
|
-
undoStack.length = 0
|
|
105
|
-
redoStack.length = 0
|
|
106
|
-
},
|
|
107
|
-
}
|
|
108
|
-
}
|