@abide/abide 0.28.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/CHANGELOG.md +607 -0
- package/LICENSE +21 -0
- package/README.md +154 -0
- package/bin/abide.ts +212 -0
- package/package.json +155 -0
- package/src/abideLsp.ts +211 -0
- package/src/abideModules.d.ts +8 -0
- package/src/abideResolverPlugin.ts +923 -0
- package/src/appEntry.ts +151 -0
- package/src/assets/app.html +12 -0
- package/src/build.ts +143 -0
- package/src/buildCli.ts +127 -0
- package/src/buildDisconnected.ts +118 -0
- package/src/bundleApp.ts +147 -0
- package/src/bundleDisconnectedEntry.ts +14 -0
- package/src/checkAbide.ts +77 -0
- package/src/cliEntry.ts +25 -0
- package/src/clientBuildPlugins.ts +33 -0
- package/src/clientEntry.ts +17 -0
- package/src/compile.ts +63 -0
- package/src/controlServerWorker.ts +426 -0
- package/src/devEntry.ts +250 -0
- package/src/discoveryEntry.ts +81 -0
- package/src/lib/bundle/BundleMenu.ts +12 -0
- package/src/lib/bundle/BundleMenuItem.ts +25 -0
- package/src/lib/bundle/BundleWindow.ts +37 -0
- package/src/lib/bundle/WEBVIEW_BUILD_REVISION.ts +9 -0
- package/src/lib/bundle/WEBVIEW_VERSION.ts +7 -0
- package/src/lib/bundle/bindConnectedFlag.ts +29 -0
- package/src/lib/bundle/bindRequestNavigate.ts +34 -0
- package/src/lib/bundle/buildWebviewLib.ts +111 -0
- package/src/lib/bundle/bundled.ts +35 -0
- package/src/lib/bundle/disconnected.abide +236 -0
- package/src/lib/bundle/disconnected.css +9 -0
- package/src/lib/bundle/ensureWebviewLib.ts +20 -0
- package/src/lib/bundle/exitWithParent.ts +28 -0
- package/src/lib/bundle/infoPlist.ts +46 -0
- package/src/lib/bundle/installDownloads.ts +24 -0
- package/src/lib/bundle/installMacMenu.ts +39 -0
- package/src/lib/bundle/listenLocalControlServer.ts +19 -0
- package/src/lib/bundle/native/abideMenu.mm +422 -0
- package/src/lib/bundle/native/webview.h +4557 -0
- package/src/lib/bundle/onMenu.ts +42 -0
- package/src/lib/bundle/openWebview.ts +104 -0
- package/src/lib/bundle/pngToIcns.ts +47 -0
- package/src/lib/bundle/probeAbideServer.ts +57 -0
- package/src/lib/bundle/resolveServerBinary.ts +12 -0
- package/src/lib/bundle/resolveWebviewLib.ts +53 -0
- package/src/lib/bundle/serverBinaryFilename.ts +8 -0
- package/src/lib/bundle/signMacApp.ts +37 -0
- package/src/lib/bundle/spawnEmbeddedServer.ts +64 -0
- package/src/lib/bundle/stableLocalPort.ts +19 -0
- package/src/lib/bundle/waitForServer.ts +23 -0
- package/src/lib/bundle/webviewCachePath.ts +23 -0
- package/src/lib/bundle/webviewLibName.ts +11 -0
- package/src/lib/cli/connectToServer.ts +23 -0
- package/src/lib/cli/createClient.ts +108 -0
- package/src/lib/cli/dispatchCommand.ts +71 -0
- package/src/lib/cli/loadEnvFromBinaryDir.ts +15 -0
- package/src/lib/cli/parseArgvForRpc.ts +100 -0
- package/src/lib/cli/printHelp.ts +119 -0
- package/src/lib/cli/printSessionHelp.ts +27 -0
- package/src/lib/cli/printSessionStatus.ts +21 -0
- package/src/lib/cli/printTrimmed.ts +8 -0
- package/src/lib/cli/printValue.ts +10 -0
- package/src/lib/cli/resolveCliTarget.ts +48 -0
- package/src/lib/cli/runCli.ts +176 -0
- package/src/lib/cli/runSession.ts +108 -0
- package/src/lib/cli/startLocalInstance.ts +14 -0
- package/src/lib/cli/tokenizeLine.ts +51 -0
- package/src/lib/cli/types/CliManifest.ts +9 -0
- package/src/lib/cli/types/CliManifestEntry.ts +17 -0
- package/src/lib/cli/types/CliTarget.ts +13 -0
- package/src/lib/mcp/annotationsForMethod.ts +29 -0
- package/src/lib/mcp/createMcpResourceServer.ts +102 -0
- package/src/lib/mcp/createMcpServer.ts +48 -0
- package/src/lib/mcp/dispatchMcpRequest.ts +138 -0
- package/src/lib/mcp/mcpResourceServerSlot.ts +18 -0
- package/src/lib/mcp/mcpSurface.ts +295 -0
- package/src/lib/mcp/toolResultFromResponse.ts +66 -0
- package/src/lib/mcp/types/JsonRpcRequest.ts +12 -0
- package/src/lib/mcp/types/JsonRpcResponse.ts +20 -0
- package/src/lib/mcp/types/McpResourceContents.ts +10 -0
- package/src/lib/mcp/types/McpResourceDescriptor.ts +6 -0
- package/src/lib/mcp/types/McpResourceServer.ts +12 -0
- package/src/lib/mcp/types/McpServer.ts +9 -0
- package/src/lib/mcp/types/McpServerOptions.ts +16 -0
- package/src/lib/server/AppModule.ts +47 -0
- package/src/lib/server/DELETE.ts +10 -0
- package/src/lib/server/GET.ts +10 -0
- package/src/lib/server/HEAD.ts +10 -0
- package/src/lib/server/PATCH.ts +10 -0
- package/src/lib/server/POST.ts +10 -0
- package/src/lib/server/PUT.ts +10 -0
- package/src/lib/server/agent.ts +86 -0
- package/src/lib/server/appDataDir.ts +16 -0
- package/src/lib/server/cli/buildEnvContent.ts +19 -0
- package/src/lib/server/cli/createTarGz.ts +77 -0
- package/src/lib/server/cli/handleCliDownload.ts +150 -0
- package/src/lib/server/cli/handleCliInstall.ts +37 -0
- package/src/lib/server/cli/installScript.ts +31 -0
- package/src/lib/server/cli/maxSourceMtime.ts +26 -0
- package/src/lib/server/cookies.ts +30 -0
- package/src/lib/server/env.ts +51 -0
- package/src/lib/server/error.ts +73 -0
- package/src/lib/server/json.ts +42 -0
- package/src/lib/server/jsonl.ts +47 -0
- package/src/lib/server/prompts/definePrompt.ts +21 -0
- package/src/lib/server/prompts/promptRegistry.ts +9 -0
- package/src/lib/server/prompts/registerPrompt.ts +6 -0
- package/src/lib/server/prompts/renderPromptTemplate.ts +17 -0
- package/src/lib/server/prompts/types/Prompt.ts +13 -0
- package/src/lib/server/prompts/types/PromptOptions.ts +12 -0
- package/src/lib/server/prompts/types/PromptRegistryEntry.ts +13 -0
- package/src/lib/server/prompts/types/PromptRoutes.ts +10 -0
- package/src/lib/server/reachable.ts +45 -0
- package/src/lib/server/redirect.ts +43 -0
- package/src/lib/server/request.ts +19 -0
- package/src/lib/server/rpc/defineVerb.ts +210 -0
- package/src/lib/server/rpc/dispatchVerbInProcess.ts +46 -0
- package/src/lib/server/rpc/findVerbByCommandName.ts +18 -0
- package/src/lib/server/rpc/parseArgs.ts +127 -0
- package/src/lib/server/rpc/readBodyWithinLimit.ts +44 -0
- package/src/lib/server/rpc/registerVerb.ts +6 -0
- package/src/lib/server/rpc/runWithVerbTimeout.ts +49 -0
- package/src/lib/server/rpc/types/RemoteHandler.ts +27 -0
- package/src/lib/server/rpc/types/RemoteRoutes.ts +13 -0
- package/src/lib/server/rpc/types/TypedResponse.ts +18 -0
- package/src/lib/server/rpc/types/VerbHelper.ts +87 -0
- package/src/lib/server/rpc/types/VerbRegistryEntry.ts +35 -0
- package/src/lib/server/rpc/unprocessed.ts +14 -0
- package/src/lib/server/rpc/verbRegistry.ts +11 -0
- package/src/lib/server/runtime/DEFAULT_PORT.ts +6 -0
- package/src/lib/server/runtime/DEV_READY_MESSAGE.ts +6 -0
- package/src/lib/server/runtime/DEV_REBUILD_MESSAGE.ts +4 -0
- package/src/lib/server/runtime/DEV_RELOAD_CLIENT_SCRIPT.ts +107 -0
- package/src/lib/server/runtime/SSR_SWAP_SCRIPT.ts +16 -0
- package/src/lib/server/runtime/acceptsGzip.ts +24 -0
- package/src/lib/server/runtime/buildCacheSnapshot.ts +61 -0
- package/src/lib/server/runtime/buildHealthPayload.ts +34 -0
- package/src/lib/server/runtime/buildInspectorSurface.ts +37 -0
- package/src/lib/server/runtime/buildOpenApiSpec.ts +106 -0
- package/src/lib/server/runtime/cacheControlForAsset.ts +22 -0
- package/src/lib/server/runtime/containsTraversal.ts +37 -0
- package/src/lib/server/runtime/createAppAssetServer.ts +76 -0
- package/src/lib/server/runtime/createAssetHeaderCache.ts +31 -0
- package/src/lib/server/runtime/createPublicAssetServer.ts +67 -0
- package/src/lib/server/runtime/createReachable.ts +109 -0
- package/src/lib/server/runtime/createRouteDispatcher.ts +127 -0
- package/src/lib/server/runtime/createServer.ts +674 -0
- package/src/lib/server/runtime/createUiPageRenderer.ts +181 -0
- package/src/lib/server/runtime/crossOriginForbidden.ts +17 -0
- package/src/lib/server/runtime/crossOriginGate.ts +29 -0
- package/src/lib/server/runtime/devClientFingerprint.ts +117 -0
- package/src/lib/server/runtime/devHotModuleResponse.ts +40 -0
- package/src/lib/server/runtime/devReloadResponse.ts +41 -0
- package/src/lib/server/runtime/disableIdleTimeoutForStream.ts +27 -0
- package/src/lib/server/runtime/envSchemaStore.ts +15 -0
- package/src/lib/server/runtime/findOpenPort.ts +21 -0
- package/src/lib/server/runtime/getActiveServer.ts +6 -0
- package/src/lib/server/runtime/globToPathSet.ts +29 -0
- package/src/lib/server/runtime/gzipResponse.ts +46 -0
- package/src/lib/server/runtime/inProcessServer.ts +20 -0
- package/src/lib/server/runtime/internalErrorResponse.ts +25 -0
- package/src/lib/server/runtime/isCrossOriginRequest.ts +23 -0
- package/src/lib/server/runtime/listenOnOpenPort.ts +36 -0
- package/src/lib/server/runtime/logExposedSurfaces.ts +156 -0
- package/src/lib/server/runtime/maybeMountInspector.ts +97 -0
- package/src/lib/server/runtime/mimeForExtension.ts +14 -0
- package/src/lib/server/runtime/pageUrlFromStore.ts +15 -0
- package/src/lib/server/runtime/parseIdleTimeout.ts +10 -0
- package/src/lib/server/runtime/parsePort.ts +11 -0
- package/src/lib/server/runtime/registryManifests.ts +66 -0
- package/src/lib/server/runtime/requestContext.ts +5 -0
- package/src/lib/server/runtime/resolvePageSnapshot.ts +25 -0
- package/src/lib/server/runtime/respondWithEmbeddedAsset.ts +18 -0
- package/src/lib/server/runtime/runWithRequestScope.ts +150 -0
- package/src/lib/server/runtime/safeJsonForScript.ts +17 -0
- package/src/lib/server/runtime/serializeCacheSnapshot.ts +45 -0
- package/src/lib/server/runtime/serverSlot.ts +13 -0
- package/src/lib/server/runtime/setActiveServer.ts +6 -0
- package/src/lib/server/runtime/snapshotEntryFromCache.ts +83 -0
- package/src/lib/server/runtime/streamCacheResolutions.ts +37 -0
- package/src/lib/server/runtime/streamFromIterator.ts +86 -0
- package/src/lib/server/runtime/types/Assets.ts +6 -0
- package/src/lib/server/runtime/types/DevReloadStamp.ts +18 -0
- package/src/lib/server/runtime/types/InspectorCacheEntry.ts +24 -0
- package/src/lib/server/runtime/types/InspectorCacheSnapshot.ts +11 -0
- package/src/lib/server/runtime/types/InspectorContext.ts +30 -0
- package/src/lib/server/runtime/types/InspectorSocket.ts +17 -0
- package/src/lib/server/runtime/types/InspectorSurface.ts +13 -0
- package/src/lib/server/runtime/types/InspectorVerb.ts +27 -0
- package/src/lib/server/runtime/types/RequestStore.ts +55 -0
- package/src/lib/server/runtime/warnUnguardedMcp.ts +32 -0
- package/src/lib/server/runtime/withResponseDefaults.ts +24 -0
- package/src/lib/server/server.ts +33 -0
- package/src/lib/server/socket.ts +32 -0
- package/src/lib/server/sockets/createSocketDispatcher.ts +337 -0
- package/src/lib/server/sockets/defineSocket.ts +179 -0
- package/src/lib/server/sockets/lookupSocket.ts +6 -0
- package/src/lib/server/sockets/registerSocket.ts +6 -0
- package/src/lib/server/sockets/socketOperations.ts +36 -0
- package/src/lib/server/sockets/socketRegistry.ts +9 -0
- package/src/lib/server/sockets/types/Socket.ts +23 -0
- package/src/lib/server/sockets/types/SocketClientFrame.ts +19 -0
- package/src/lib/server/sockets/types/SocketOperation.ts +22 -0
- package/src/lib/server/sockets/types/SocketOptions.ts +26 -0
- package/src/lib/server/sockets/types/SocketRegistryEntry.ts +19 -0
- package/src/lib/server/sockets/types/SocketRoutes.ts +10 -0
- package/src/lib/server/sockets/types/SocketServerFrame.ts +24 -0
- package/src/lib/server/sse.ts +54 -0
- package/src/lib/shared/ABIDE_PACKAGE_NAME.ts +7 -0
- package/src/lib/shared/ABIDE_VERSION.ts +9 -0
- package/src/lib/shared/CACHE_CONTROL_VALUES.ts +16 -0
- package/src/lib/shared/CACHE_WRAPPED.ts +8 -0
- package/src/lib/shared/CLI_PATH.ts +7 -0
- package/src/lib/shared/DEV_HOT_PREFIX.ts +7 -0
- package/src/lib/shared/DEV_RELOAD_PATH.ts +6 -0
- package/src/lib/shared/HEALTH_PATH.ts +7 -0
- package/src/lib/shared/HttpError.ts +20 -0
- package/src/lib/shared/IDENTITY_PATH.ts +6 -0
- package/src/lib/shared/INSPECTOR_PATH.ts +7 -0
- package/src/lib/shared/NAV_HEADER.ts +8 -0
- package/src/lib/shared/OFFLINE_HEADER.ts +8 -0
- package/src/lib/shared/REMOTE_FUNCTION.ts +8 -0
- package/src/lib/shared/REPLAYABLE_METHODS.ts +12 -0
- package/src/lib/shared/SOCKETS_PATH.ts +7 -0
- package/src/lib/shared/STREAMING_CONTENT_TYPES.ts +11 -0
- package/src/lib/shared/SocketDisconnectedError.ts +13 -0
- package/src/lib/shared/TEXT_PLAIN.ts +7 -0
- package/src/lib/shared/abideImportName.ts +44 -0
- package/src/lib/shared/abideLog.ts +38 -0
- package/src/lib/shared/activeCacheStore.ts +20 -0
- package/src/lib/shared/activePage.ts +25 -0
- package/src/lib/shared/appDataDir.ts +34 -0
- package/src/lib/shared/appNameSlot.ts +10 -0
- package/src/lib/shared/basePath.ts +10 -0
- package/src/lib/shared/basePathFromAppUrl.ts +20 -0
- package/src/lib/shared/baseSlot.ts +14 -0
- package/src/lib/shared/binaryDirEnvPath.ts +12 -0
- package/src/lib/shared/browserClientFlags.ts +10 -0
- package/src/lib/shared/buildRpcProxy.ts +39 -0
- package/src/lib/shared/buildRpcRequest.ts +70 -0
- package/src/lib/shared/buildSocketOverChannel.ts +58 -0
- package/src/lib/shared/bundleLayout.ts +36 -0
- package/src/lib/shared/cache.ts +951 -0
- package/src/lib/shared/cacheEntryFromSnapshot.ts +59 -0
- package/src/lib/shared/cacheStoreSlot.ts +16 -0
- package/src/lib/shared/cacheStores.ts +10 -0
- package/src/lib/shared/canonicalJson.ts +63 -0
- package/src/lib/shared/carriesBodyArgs.ts +13 -0
- package/src/lib/shared/clearLastConnection.ts +7 -0
- package/src/lib/shared/commandNameForUrl.ts +17 -0
- package/src/lib/shared/createCacheStore.ts +104 -0
- package/src/lib/shared/createChannelLog.ts +122 -0
- package/src/lib/shared/createLifecycleChannel.ts +56 -0
- package/src/lib/shared/createLivenessWatch.ts +118 -0
- package/src/lib/shared/createPushIterator.ts +127 -0
- package/src/lib/shared/createRemoteFunction.ts +122 -0
- package/src/lib/shared/createSubscriber.ts +55 -0
- package/src/lib/shared/createTraceContext.ts +21 -0
- package/src/lib/shared/dataDirEnvPath.ts +12 -0
- package/src/lib/shared/decodeResponse.ts +47 -0
- package/src/lib/shared/detectTarget.ts +27 -0
- package/src/lib/shared/detectVerbMethod.ts +17 -0
- package/src/lib/shared/emitLogRecord.ts +190 -0
- package/src/lib/shared/exeSuffix.ts +9 -0
- package/src/lib/shared/exitOnBuildFailure.ts +17 -0
- package/src/lib/shared/extraForwardHeaders.ts +16 -0
- package/src/lib/shared/fileStem.ts +9 -0
- package/src/lib/shared/findExportCallSite.ts +476 -0
- package/src/lib/shared/formatTraceparent.ts +6 -0
- package/src/lib/shared/forwardHeaders.ts +44 -0
- package/src/lib/shared/getRemoteMeta.ts +5 -0
- package/src/lib/shared/globalCacheStore.ts +15 -0
- package/src/lib/shared/globalCacheStoreSlot.ts +14 -0
- package/src/lib/shared/health.ts +179 -0
- package/src/lib/shared/healthReadSlot.ts +11 -0
- package/src/lib/shared/healthSeedSlot.ts +12 -0
- package/src/lib/shared/html.ts +38 -0
- package/src/lib/shared/importNamesToStrip.ts +13 -0
- package/src/lib/shared/invalidateEvent.ts +11 -0
- package/src/lib/shared/invalidateTripwire.ts +40 -0
- package/src/lib/shared/isAbideHealthPayload.ts +11 -0
- package/src/lib/shared/isCompileTarget.ts +15 -0
- package/src/lib/shared/isDebugEnabled.ts +26 -0
- package/src/lib/shared/isDebugNegated.ts +19 -0
- package/src/lib/shared/isModuleNotFound.ts +16 -0
- package/src/lib/shared/isReadOnlyMethod.ts +14 -0
- package/src/lib/shared/isReplayableMethod.ts +7 -0
- package/src/lib/shared/isStreamingResponse.ts +11 -0
- package/src/lib/shared/isSubscribable.ts +15 -0
- package/src/lib/shared/jsonSchemaForPromptArguments.ts +29 -0
- package/src/lib/shared/jsonSchemaForSchema.ts +39 -0
- package/src/lib/shared/jsonlErrorFrame.ts +24 -0
- package/src/lib/shared/keyForRemoteCall.ts +29 -0
- package/src/lib/shared/keyMatchesPrefix.ts +9 -0
- package/src/lib/shared/lastConnectionPath.ts +7 -0
- package/src/lib/shared/layoutChainForRoute.ts +22 -0
- package/src/lib/shared/loadEnvFile.ts +17 -0
- package/src/lib/shared/loadEnvFromDataDir.ts +14 -0
- package/src/lib/shared/log.ts +24 -0
- package/src/lib/shared/logClosingRecord.ts +28 -0
- package/src/lib/shared/logTapSlot.ts +13 -0
- package/src/lib/shared/manifestModule.ts +39 -0
- package/src/lib/shared/matchesDebugPattern.ts +16 -0
- package/src/lib/shared/memoizeByKey.ts +32 -0
- package/src/lib/shared/normalizeTarget.ts +10 -0
- package/src/lib/shared/online.ts +51 -0
- package/src/lib/shared/page.ts +30 -0
- package/src/lib/shared/pageSlot.ts +17 -0
- package/src/lib/shared/pageUrlForFile.ts +14 -0
- package/src/lib/shared/parseBoundedEnvInt.ts +20 -0
- package/src/lib/shared/parseDebugPatterns.ts +21 -0
- package/src/lib/shared/parseEnv.ts +30 -0
- package/src/lib/shared/parsePromptMarkdown.ts +35 -0
- package/src/lib/shared/parseRouteSegments.ts +22 -0
- package/src/lib/shared/parseTraceparent.ts +26 -0
- package/src/lib/shared/pending.ts +30 -0
- package/src/lib/shared/prepareRpcModule.ts +59 -0
- package/src/lib/shared/prepareSocketModule.ts +49 -0
- package/src/lib/shared/probeRegistries.ts +68 -0
- package/src/lib/shared/producerKey.ts +32 -0
- package/src/lib/shared/programNameForPackage.ts +14 -0
- package/src/lib/shared/promptNameForFile.ts +10 -0
- package/src/lib/shared/queryStringFromArgs.ts +27 -0
- package/src/lib/shared/randomHexId.ts +14 -0
- package/src/lib/shared/readEnvFile.ts +15 -0
- package/src/lib/shared/readLastConnection.ts +18 -0
- package/src/lib/shared/readPackageJson.ts +9 -0
- package/src/lib/shared/recordRemoteMeta.ts +5 -0
- package/src/lib/shared/refreshing.ts +31 -0
- package/src/lib/shared/remoteMetaStore.ts +16 -0
- package/src/lib/shared/requestScopeSlot.ts +15 -0
- package/src/lib/shared/resolveClientFlags.ts +20 -0
- package/src/lib/shared/responseErrorText.ts +9 -0
- package/src/lib/shared/rpcTimeoutSlot.ts +9 -0
- package/src/lib/shared/rpcUrlForFile.ts +19 -0
- package/src/lib/shared/runningAsStandaloneBinary.ts +13 -0
- package/src/lib/shared/selectorMatcher.ts +68 -0
- package/src/lib/shared/selectorPrefix.ts +39 -0
- package/src/lib/shared/serializeEnv.ts +18 -0
- package/src/lib/shared/setAppName.ts +5 -0
- package/src/lib/shared/setBaseResolver.ts +6 -0
- package/src/lib/shared/setCacheStoreResolver.ts +6 -0
- package/src/lib/shared/setGlobalCacheStoreResolver.ts +6 -0
- package/src/lib/shared/setPageResolver.ts +7 -0
- package/src/lib/shared/setRequestScopeResolver.ts +6 -0
- package/src/lib/shared/snippet.ts +25 -0
- package/src/lib/shared/socketNameForFile.ts +11 -0
- package/src/lib/shared/socketTapSlot.ts +12 -0
- package/src/lib/shared/sseErrorFrame.ts +29 -0
- package/src/lib/shared/streamResponse.ts +169 -0
- package/src/lib/shared/stripImport.ts +27 -0
- package/src/lib/shared/subscribableFromResponse.ts +51 -0
- package/src/lib/shared/tailProbeSlot.ts +16 -0
- package/src/lib/shared/toBunRoutePattern.ts +28 -0
- package/src/lib/shared/toScopeSet.ts +4 -0
- package/src/lib/shared/trace.ts +16 -0
- package/src/lib/shared/types/CacheEntry.ts +84 -0
- package/src/lib/shared/types/CacheInvalidation.ts +9 -0
- package/src/lib/shared/types/CacheOnContext.ts +25 -0
- package/src/lib/shared/types/CacheOptions.ts +39 -0
- package/src/lib/shared/types/CacheSelector.ts +17 -0
- package/src/lib/shared/types/CacheSnapshot.ts +16 -0
- package/src/lib/shared/types/CacheSnapshotEntry.ts +17 -0
- package/src/lib/shared/types/CacheStats.ts +13 -0
- package/src/lib/shared/types/CacheStore.ts +39 -0
- package/src/lib/shared/types/ChannelLog.ts +13 -0
- package/src/lib/shared/types/ClientFlags.ts +11 -0
- package/src/lib/shared/types/CompileTarget.ts +6 -0
- package/src/lib/shared/types/FrameworkLog.ts +13 -0
- package/src/lib/shared/types/HttpVerb.ts +1 -0
- package/src/lib/shared/types/LastConnection.ts +9 -0
- package/src/lib/shared/types/Log.ts +13 -0
- package/src/lib/shared/types/LogRecord.ts +42 -0
- package/src/lib/shared/types/LogVoice.ts +7 -0
- package/src/lib/shared/types/PageSnapshot.ts +14 -0
- package/src/lib/shared/types/PromptArgument.ts +12 -0
- package/src/lib/shared/types/RawRemoteFunction.ts +14 -0
- package/src/lib/shared/types/RemoteCallable.ts +12 -0
- package/src/lib/shared/types/RemoteFunction.ts +47 -0
- package/src/lib/shared/types/ReplayableMethod.ts +7 -0
- package/src/lib/shared/types/RequestScopeInfo.ts +16 -0
- package/src/lib/shared/types/RpcInvoker.ts +6 -0
- package/src/lib/shared/types/SocketChannel.ts +17 -0
- package/src/lib/shared/types/SocketSubCallbacks.ts +13 -0
- package/src/lib/shared/types/StandardSchemaV1.ts +56 -0
- package/src/lib/shared/types/StreamedResolution.ts +10 -0
- package/src/lib/shared/types/Subscribable.ts +26 -0
- package/src/lib/shared/types/TailHooks.ts +12 -0
- package/src/lib/shared/types/TailOptions.ts +10 -0
- package/src/lib/shared/types/TraceContext.ts +17 -0
- package/src/lib/shared/url.ts +118 -0
- package/src/lib/shared/withBase.ts +11 -0
- package/src/lib/shared/withBaseUrl.ts +17 -0
- package/src/lib/shared/withJsonSchema.ts +21 -0
- package/src/lib/shared/writeDts.ts +12 -0
- package/src/lib/shared/writeHealthDts.ts +36 -0
- package/src/lib/shared/writeLastConnection.ts +13 -0
- package/src/lib/shared/writePublicAssetsDts.ts +31 -0
- package/src/lib/shared/writeRoutesDts.ts +73 -0
- package/src/lib/shared/writeRpcDts.ts +49 -0
- package/src/lib/shared/writeTestRpcDts.ts +45 -0
- package/src/lib/shared/writeTestSocketsDts.ts +34 -0
- package/src/lib/test/assertAgentFrameConformance.ts +73 -0
- package/src/lib/test/createScriptedSurface.ts +45 -0
- package/src/lib/test/createTestApp.ts +203 -0
- package/src/lib/test/createTestSocketChannel.ts +142 -0
- package/src/lib/ui/README.md +86 -0
- package/src/lib/ui/compile/SSR_ESCAPE.ts +25 -0
- package/src/lib/ui/compile/UI_RUNTIME_IMPORTS.ts +36 -0
- package/src/lib/ui/compile/VOID_TAGS.ts +21 -0
- package/src/lib/ui/compile/abideUiPlugin.ts +65 -0
- package/src/lib/ui/compile/analyzeComponent.ts +117 -0
- package/src/lib/ui/compile/assetModulesFile.ts +32 -0
- package/src/lib/ui/compile/branchElements.ts +50 -0
- package/src/lib/ui/compile/collectAbideDiagnostics.ts +59 -0
- package/src/lib/ui/compile/compileComponent.ts +20 -0
- package/src/lib/ui/compile/compileModule.ts +116 -0
- package/src/lib/ui/compile/compileSSR.ts +36 -0
- package/src/lib/ui/compile/compileShadow.ts +352 -0
- package/src/lib/ui/compile/createShadowLanguageService.ts +197 -0
- package/src/lib/ui/compile/createShadowProgram.ts +96 -0
- package/src/lib/ui/compile/decodeHtmlEntities.ts +49 -0
- package/src/lib/ui/compile/desugarSignals.ts +133 -0
- package/src/lib/ui/compile/escapeHtml.ts +15 -0
- package/src/lib/ui/compile/generateBuild.ts +638 -0
- package/src/lib/ui/compile/generateSSR.ts +380 -0
- package/src/lib/ui/compile/groupBindParts.ts +28 -0
- package/src/lib/ui/compile/hoistCells.ts +120 -0
- package/src/lib/ui/compile/loadShadowTsConfig.ts +31 -0
- package/src/lib/ui/compile/lowerDocAccess.ts +202 -0
- package/src/lib/ui/compile/nearestProjectRoot.ts +16 -0
- package/src/lib/ui/compile/parseTemplate.ts +396 -0
- package/src/lib/ui/compile/partitionSlots.ts +36 -0
- package/src/lib/ui/compile/prepareNestedScript.ts +42 -0
- package/src/lib/ui/compile/remapShadowDiagnostic.ts +30 -0
- package/src/lib/ui/compile/renameSignalRefs.ts +85 -0
- package/src/lib/ui/compile/resolveAbideImports.ts +29 -0
- package/src/lib/ui/compile/scopeCss.ts +115 -0
- package/src/lib/ui/compile/shadowNaming.ts +11 -0
- package/src/lib/ui/compile/sourceToShadowOffset.ts +24 -0
- package/src/lib/ui/compile/staticAttrValue.ts +13 -0
- package/src/lib/ui/compile/stripEffects.ts +32 -0
- package/src/lib/ui/compile/types/AbideDiagnostic.ts +14 -0
- package/src/lib/ui/compile/types/AnalyzedComponent.ts +25 -0
- package/src/lib/ui/compile/types/CompiledShadow.ts +15 -0
- package/src/lib/ui/compile/types/TemplateAttr.ts +16 -0
- package/src/lib/ui/compile/types/TemplateNode.ts +78 -0
- package/src/lib/ui/compile/types/TextPart.ts +8 -0
- package/src/lib/ui/derived.ts +28 -0
- package/src/lib/ui/doc.ts +15 -0
- package/src/lib/ui/dom/appendSnippet.ts +34 -0
- package/src/lib/ui/dom/appendStatic.ts +27 -0
- package/src/lib/ui/dom/appendText.ts +114 -0
- package/src/lib/ui/dom/applyResolved.ts +72 -0
- package/src/lib/ui/dom/attach.ts +20 -0
- package/src/lib/ui/dom/attr.ts +19 -0
- package/src/lib/ui/dom/awaitBlock.ts +224 -0
- package/src/lib/ui/dom/cloneStatic.ts +52 -0
- package/src/lib/ui/dom/each.ts +115 -0
- package/src/lib/ui/dom/eachAsync.ts +153 -0
- package/src/lib/ui/dom/hydrate.ts +35 -0
- package/src/lib/ui/dom/mount.ts +29 -0
- package/src/lib/ui/dom/mountChild.ts +33 -0
- package/src/lib/ui/dom/on.ts +15 -0
- package/src/lib/ui/dom/openChild.ts +22 -0
- package/src/lib/ui/dom/openRoot.ts +20 -0
- package/src/lib/ui/dom/switchBlock.ts +75 -0
- package/src/lib/ui/dom/text.ts +20 -0
- package/src/lib/ui/dom/tryBlock.ts +112 -0
- package/src/lib/ui/dom/types/EachRow.ts +3 -0
- package/src/lib/ui/dom/types/SwitchCase.ts +6 -0
- package/src/lib/ui/dom/when.ts +73 -0
- package/src/lib/ui/effect.ts +16 -0
- package/src/lib/ui/installHotBridge.ts +73 -0
- package/src/lib/ui/matchRoute.ts +89 -0
- package/src/lib/ui/navigate.ts +17 -0
- package/src/lib/ui/probeNavigation.ts +33 -0
- package/src/lib/ui/remoteProxy.ts +97 -0
- package/src/lib/ui/renderChain.ts +50 -0
- package/src/lib/ui/renderToStream.ts +104 -0
- package/src/lib/ui/router.ts +286 -0
- package/src/lib/ui/runtime/OUTLET_TAG.ts +8 -0
- package/src/lib/ui/runtime/OWNER.ts +8 -0
- package/src/lib/ui/runtime/REACTIVE_CONTEXT.ts +14 -0
- package/src/lib/ui/runtime/RENDER.ts +23 -0
- package/src/lib/ui/runtime/RESUME.ts +16 -0
- package/src/lib/ui/runtime/applyPatchToTree.ts +41 -0
- package/src/lib/ui/runtime/claimChild.ts +10 -0
- package/src/lib/ui/runtime/clientPage.ts +16 -0
- package/src/lib/ui/runtime/createComputedNode.ts +16 -0
- package/src/lib/ui/runtime/createDoc.ts +177 -0
- package/src/lib/ui/runtime/createEffectNode.ts +58 -0
- package/src/lib/ui/runtime/createSignalNode.ts +16 -0
- package/src/lib/ui/runtime/detachLink.ts +21 -0
- package/src/lib/ui/runtime/endTracking.ts +24 -0
- package/src/lib/ui/runtime/enterRenderPass.ts +12 -0
- package/src/lib/ui/runtime/exitRenderPass.ts +7 -0
- package/src/lib/ui/runtime/firstOutlet.ts +22 -0
- package/src/lib/ui/runtime/flushEffects.ts +17 -0
- package/src/lib/ui/runtime/hotInstances.ts +10 -0
- package/src/lib/ui/runtime/hotReloadEnabled.ts +8 -0
- package/src/lib/ui/runtime/hotReplace.ts +25 -0
- package/src/lib/ui/runtime/nextBlockId.ts +11 -0
- package/src/lib/ui/runtime/pathExists.ts +23 -0
- package/src/lib/ui/runtime/readNode.ts +17 -0
- package/src/lib/ui/runtime/registerHotInstance.ts +23 -0
- package/src/lib/ui/runtime/runNode.ts +28 -0
- package/src/lib/ui/runtime/runtimePath.ts +9 -0
- package/src/lib/ui/runtime/scope.ts +24 -0
- package/src/lib/ui/runtime/toTeardown.ts +26 -0
- package/src/lib/ui/runtime/track.ts +58 -0
- package/src/lib/ui/runtime/trigger.ts +44 -0
- package/src/lib/ui/runtime/types/Cell.ts +5 -0
- package/src/lib/ui/runtime/types/Derived.ts +3 -0
- package/src/lib/ui/runtime/types/Doc.ts +19 -0
- package/src/lib/ui/runtime/types/EffectResult.ts +10 -0
- package/src/lib/ui/runtime/types/HotInstance.ts +14 -0
- package/src/lib/ui/runtime/types/NavVerdict.ts +9 -0
- package/src/lib/ui/runtime/types/Patch.ts +11 -0
- package/src/lib/ui/runtime/types/ReactiveLink.ts +21 -0
- package/src/lib/ui/runtime/types/ReactiveNode.ts +25 -0
- package/src/lib/ui/runtime/types/Route.ts +8 -0
- package/src/lib/ui/runtime/types/RouteLoader.ts +7 -0
- package/src/lib/ui/runtime/types/SsrRender.ts +22 -0
- package/src/lib/ui/runtime/types/State.ts +3 -0
- package/src/lib/ui/runtime/types/Teardown.ts +5 -0
- package/src/lib/ui/runtime/types/UiComponent.ts +16 -0
- package/src/lib/ui/runtime/types/UiProps.ts +15 -0
- package/src/lib/ui/runtime/unlinkDeps.ts +20 -0
- package/src/lib/ui/runtime/untrack.ts +20 -0
- package/src/lib/ui/runtime/valueAtPath.ts +18 -0
- package/src/lib/ui/runtime/writeNode.ts +16 -0
- package/src/lib/ui/socketChannel.ts +227 -0
- package/src/lib/ui/socketProxy.ts +25 -0
- package/src/lib/ui/startClient.ts +58 -0
- package/src/lib/ui/state.ts +25 -0
- package/src/lib/ui/tail.ts +324 -0
- package/src/lib/ui/types/Layouts.ts +9 -0
- package/src/lib/ui/types/Pages.ts +8 -0
- package/src/preload.ts +19 -0
- package/src/scaffold.ts +153 -0
- package/src/serverBuildPlugins.ts +19 -0
- package/src/serverEntry.ts +95 -0
- package/template/bunfig.toml +4 -0
- package/template/package.json +18 -0
- package/template/src/app.ts +28 -0
- package/template/src/bundle/icon.png +0 -0
- package/template/src/cli/banner.txt +3 -0
- package/template/src/cli/footer.txt +1 -0
- package/template/src/server/config.ts +17 -0
- package/template/src/server/rpc/getHello.ts +36 -0
- package/template/src/ui/Layout.abide +19 -0
- package/template/src/ui/app.css +21 -0
- package/template/src/ui/app.html +24 -0
- package/template/src/ui/pages/about/page.abide +9 -0
- package/template/src/ui/pages/page.abide +22 -0
- package/template/test/app.test.ts +30 -0
- package/template/tsconfig.json +18 -0
- package/tsconfig.app.json +17 -0
|
@@ -0,0 +1,638 @@
|
|
|
1
|
+
import { OUTLET_TAG } from '../runtime/OUTLET_TAG.ts'
|
|
2
|
+
import { branchElements } from './branchElements.ts'
|
|
3
|
+
import { escapeHtml } from './escapeHtml.ts'
|
|
4
|
+
import { groupBindParts } from './groupBindParts.ts'
|
|
5
|
+
import { lowerDocAccess } from './lowerDocAccess.ts'
|
|
6
|
+
import { partitionSlots } from './partitionSlots.ts'
|
|
7
|
+
import { nestedBindingNames } from './prepareNestedScript.ts'
|
|
8
|
+
import { renameSignalRefs } from './renameSignalRefs.ts'
|
|
9
|
+
import { staticAttrValue } from './staticAttrValue.ts'
|
|
10
|
+
import type { TemplateNode } from './types/TemplateNode.ts'
|
|
11
|
+
import { VOID_TAGS } from './VOID_TAGS.ts'
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
Generates the build statements for a parsed template: element creation, static
|
|
15
|
+
attributes, reactive `attr`/`text` bindings, `on` listeners, keyed `each`, and
|
|
16
|
+
conditional `when`. Every embedded expression is first rewritten from the signal
|
|
17
|
+
surface (`count` → `model.count`) and then lowered to the doc patch/read API
|
|
18
|
+
(cell-hoisting runs over the whole result afterwards). The output operates on
|
|
19
|
+
`hostVar` and expects the dom bindings, `doc`, `effect`, and the component's
|
|
20
|
+
`model` in scope — the body the component compiler wraps and hoists cells into.
|
|
21
|
+
*/
|
|
22
|
+
export function generateBuild(
|
|
23
|
+
nodes: TemplateNode[],
|
|
24
|
+
hostVar: string,
|
|
25
|
+
stateNames: ReadonlySet<string>,
|
|
26
|
+
derivedNames: ReadonlySet<string>,
|
|
27
|
+
isLayout = false,
|
|
28
|
+
): string {
|
|
29
|
+
let counter = 0
|
|
30
|
+
const nextVar = (prefix: string): string => `${prefix}${counter++}`
|
|
31
|
+
|
|
32
|
+
/* Branch-scoped signal bindings (from nested `<script>`s) — they deref to
|
|
33
|
+
`.value` like a `derived`. Pushed while a branch's script + markup compile,
|
|
34
|
+
popped after, so they shadow only within that subtree. */
|
|
35
|
+
const localDerived = new Set<string>()
|
|
36
|
+
const derefScope = (): ReadonlySet<string> =>
|
|
37
|
+
localDerived.size === 0 ? derivedNames : new Set([...derivedNames, ...localDerived])
|
|
38
|
+
|
|
39
|
+
/* Rewrites signal refs, then lowers a single expression (no trailing `;`). */
|
|
40
|
+
function lowerExpression(code: string): string {
|
|
41
|
+
const renamed = renameSignalRefs(code, stateNames, derefScope())
|
|
42
|
+
return lowerDocAccess(renamed, 'model').trim().replace(/;$/, '')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* As above but keeps the trailing `;` for a handler body. */
|
|
46
|
+
function lowerStatement(code: string): string {
|
|
47
|
+
const renamed = renameSignalRefs(code, stateNames, derefScope())
|
|
48
|
+
return lowerDocAccess(renamed, 'model').trim()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* Builds an element and its children; returns the build code and its var.
|
|
52
|
+
`varExpr` is how the element is obtained — `openChild(parent, tag)` for a
|
|
53
|
+
child (create-or-claim), or `document.createElement(tag)` for a returned
|
|
54
|
+
root (rows/branches, which are create-only). */
|
|
55
|
+
function generateElement(
|
|
56
|
+
node: Extract<TemplateNode, { kind: 'element' }>,
|
|
57
|
+
varExpr: string,
|
|
58
|
+
): { code: string; varName: string } {
|
|
59
|
+
const varName = nextVar('el')
|
|
60
|
+
let code = `const ${varName} = ${varExpr};\n`
|
|
61
|
+
/* Stamp the scope attribute of every `<style>` active at this element (its own
|
|
62
|
+
sibling list plus every ancestor's), so the bundled CSS matches it. */
|
|
63
|
+
for (const scope of node.scopes ?? []) {
|
|
64
|
+
code += `${varName}.setAttribute(${JSON.stringify(scope)}, "");\n`
|
|
65
|
+
}
|
|
66
|
+
for (const attr of node.attrs) {
|
|
67
|
+
if (attr.kind === 'static') {
|
|
68
|
+
code += `${varName}.setAttribute(${JSON.stringify(attr.name)}, ${JSON.stringify(attr.value)});\n`
|
|
69
|
+
} else if (attr.kind === 'expression') {
|
|
70
|
+
code += `attr(${varName}, ${JSON.stringify(attr.name)}, () => (${lowerExpression(attr.code)}));\n`
|
|
71
|
+
} else if (attr.kind === 'event') {
|
|
72
|
+
code += `on(${varName}, ${JSON.stringify(attr.event)}, (${lowerExpression(attr.code)}));\n`
|
|
73
|
+
} else if (attr.kind === 'attach') {
|
|
74
|
+
code += `attach(${varName}, (${lowerExpression(attr.code)}));\n`
|
|
75
|
+
} else if (attr.kind === 'bind' && attr.property === 'group') {
|
|
76
|
+
/* Grouped two-way: radio binds the path to the single checked
|
|
77
|
+
`value`; checkbox treats the path as an array, adding/removing
|
|
78
|
+
`value` on toggle. Membership reads the array via the lowered
|
|
79
|
+
path and calls native `.includes`/`.indexOf` (the doc API has no
|
|
80
|
+
array search); mutations go through `push`/`delete`, which lower
|
|
81
|
+
to `add`/`remove` patches that the doc reindexes. */
|
|
82
|
+
const { valueCode, isRadio } = groupBindParts(node)
|
|
83
|
+
const value = lowerExpression(valueCode)
|
|
84
|
+
if (isRadio) {
|
|
85
|
+
code += `effect(() => { ${varName}.checked = (${lowerExpression(attr.code)}) === (${value}); });\n`
|
|
86
|
+
code += `on(${varName}, "change", () => { if (${varName}.checked) { ${lowerStatement(`${attr.code} = ${valueCode}`)} } });\n`
|
|
87
|
+
} else {
|
|
88
|
+
code += `effect(() => { ${varName}.checked = (${lowerExpression(attr.code)}).includes(${value}); });\n`
|
|
89
|
+
code += `on(${varName}, "change", () => { const $groupValue = ${value}; if (${varName}.checked) { if (!(${lowerExpression(attr.code)}).includes($groupValue)) { ${lowerStatement(`${attr.code}.push($groupValue)`)} } } else { const $groupIndex = (${lowerExpression(attr.code)}).indexOf($groupValue); if ($groupIndex !== -1) { ${lowerStatement(`delete ${attr.code}[$groupIndex]`)} } } });\n`
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
/* Two-way: drive the property from the path, and write the path
|
|
93
|
+
back on input. The path is an lvalue, so the write is lowered
|
|
94
|
+
as an assignment statement. */
|
|
95
|
+
code += `effect(() => { ${varName}.${attr.property} = ${lowerExpression(attr.code)}; });\n`
|
|
96
|
+
code += `on(${varName}, "input", () => { ${lowerStatement(`${attr.code} = ${varName}.${attr.property}`)} });\n`
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/* A `<script>` among the children scopes its bindings to this element's
|
|
100
|
+
subtree (its later siblings auto-deref them); pop after. */
|
|
101
|
+
const added = scopeNestedScripts(node.children)
|
|
102
|
+
code += generateChildren(node.children, varName)
|
|
103
|
+
for (const name of added) {
|
|
104
|
+
localDerived.delete(name)
|
|
105
|
+
}
|
|
106
|
+
return { code, varName }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* Adds the binding names of any `<script>` children to the deref scope, returning
|
|
110
|
+
the names it added (for the caller to pop). */
|
|
111
|
+
function scopeNestedScripts(children: TemplateNode[]): string[] {
|
|
112
|
+
const added: string[] = []
|
|
113
|
+
for (const child of children) {
|
|
114
|
+
if (child.kind === 'script') {
|
|
115
|
+
for (const name of nestedBindingNames(child.code)) {
|
|
116
|
+
if (!localDerived.has(name)) {
|
|
117
|
+
localDerived.add(name)
|
|
118
|
+
added.push(name)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return added
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* Emits code appending `node` to `parentVar`. */
|
|
127
|
+
function generateChild(node: TemplateNode, parentVar: string): string {
|
|
128
|
+
if (node.kind === 'script') {
|
|
129
|
+
return `${lowerStatement(node.code)}\n`
|
|
130
|
+
}
|
|
131
|
+
/* A `<style>` emits no DOM — its CSS is bundled and its scope attribute is
|
|
132
|
+
already stamped onto the elements it covers (see `generateElement`). */
|
|
133
|
+
if (node.kind === 'style') {
|
|
134
|
+
return ''
|
|
135
|
+
}
|
|
136
|
+
if (node.kind === 'text') {
|
|
137
|
+
/* The non-whitespace parts share one merged SSR text node, so on hydrate
|
|
138
|
+
each must split off its own portion. Every consumer but the last passes
|
|
139
|
+
`splitAlways` so it leaves a node behind even on an exact-length consume
|
|
140
|
+
(e.g. an interpolation that renders empty) — the last keeps the cheaper
|
|
141
|
+
split-only-when-shorter path. */
|
|
142
|
+
const consumers = node.parts.filter(
|
|
143
|
+
(part) => part.kind !== 'static' || part.value.trim() !== '',
|
|
144
|
+
)
|
|
145
|
+
return consumers
|
|
146
|
+
.map((part, index) => {
|
|
147
|
+
const splitAlways = index < consumers.length - 1 ? ', true' : ''
|
|
148
|
+
return part.kind === 'static'
|
|
149
|
+
? `appendStatic(${parentVar}, ${JSON.stringify(part.value)}${splitAlways});\n`
|
|
150
|
+
: `appendText(${parentVar}, () => (${lowerExpression(part.code)})${splitAlways});\n`
|
|
151
|
+
})
|
|
152
|
+
.join('')
|
|
153
|
+
}
|
|
154
|
+
if (node.kind === 'element' && node.tag === 'slot') {
|
|
155
|
+
/* In a layout, the unnamed `<slot/>` is the router's page outlet: a bare
|
|
156
|
+
structural element the router mounts the next chain layer into. Created
|
|
157
|
+
empty (no scope attr, no children) so it matches the SSR placeholder. */
|
|
158
|
+
if (isLayout && staticAttrValue(node, 'name') === undefined) {
|
|
159
|
+
return `openChild(${parentVar}, ${JSON.stringify(OUTLET_TAG)});\n`
|
|
160
|
+
}
|
|
161
|
+
return generateSlot(node, parentVar)
|
|
162
|
+
}
|
|
163
|
+
if (node.kind === 'element') {
|
|
164
|
+
/* openChild appends (create) or claims (hydrate) — no separate append. */
|
|
165
|
+
return generateElement(node, `openChild(${parentVar}, ${JSON.stringify(node.tag)})`)
|
|
166
|
+
.code
|
|
167
|
+
}
|
|
168
|
+
if (node.kind === 'if') {
|
|
169
|
+
return generateIf(node, parentVar)
|
|
170
|
+
}
|
|
171
|
+
if (node.kind === 'await') {
|
|
172
|
+
return generateAwait(node, parentVar)
|
|
173
|
+
}
|
|
174
|
+
if (node.kind === 'try') {
|
|
175
|
+
return generateTry(node, parentVar)
|
|
176
|
+
}
|
|
177
|
+
if (node.kind === 'branch') {
|
|
178
|
+
return '' // branches are consumed by their await block, never standalone
|
|
179
|
+
}
|
|
180
|
+
if (node.kind === 'component') {
|
|
181
|
+
return generateComponent(node, parentVar)
|
|
182
|
+
}
|
|
183
|
+
if (node.kind === 'switch') {
|
|
184
|
+
return generateSwitch(node, parentVar)
|
|
185
|
+
}
|
|
186
|
+
if (node.kind === 'case') {
|
|
187
|
+
return '' // cases are consumed by their switch/if, never standalone
|
|
188
|
+
}
|
|
189
|
+
if (node.kind === 'snippet') {
|
|
190
|
+
return generateSnippet(node)
|
|
191
|
+
}
|
|
192
|
+
return generateEach(node, parentVar)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/* Builds a sibling list, coalescing maximal runs of fully-static element subtrees
|
|
196
|
+
into one `cloneStatic` clone (a single cloneNode in place of the N create/append
|
|
197
|
+
calls the imperative path would emit). Whitespace-only text is transparent — it
|
|
198
|
+
neither breaks a run nor adds markup, matching both back-ends dropping it. Every
|
|
199
|
+
other child flushes the pending run and builds imperatively, preserving order. */
|
|
200
|
+
function generateChildren(children: TemplateNode[], parentVar: string): string {
|
|
201
|
+
let code = ''
|
|
202
|
+
let runHtml = ''
|
|
203
|
+
const flush = (): void => {
|
|
204
|
+
if (runHtml !== '') {
|
|
205
|
+
code += `cloneStatic(${parentVar}, ${JSON.stringify(runHtml)});\n`
|
|
206
|
+
runHtml = ''
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
for (const child of children) {
|
|
210
|
+
if (isStaticCloneableElement(child)) {
|
|
211
|
+
runHtml += staticHtml(child)
|
|
212
|
+
} else if (!isWhitespaceText(child)) {
|
|
213
|
+
flush()
|
|
214
|
+
code += generateChild(child, parentVar)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
flush()
|
|
218
|
+
return code
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* A snippet declaration: a hoisted function returning a `snippet`-branded builder
|
|
222
|
+
that appends its body into the host it is mounted on. The function closes over
|
|
223
|
+
the component scope (its `model`/cells); `args` are plain parameters bound by
|
|
224
|
+
the call. Appends nothing at the declaration site — `{name(args)}` mounts it. */
|
|
225
|
+
function generateSnippet(node: Extract<TemplateNode, { kind: 'snippet' }>): string {
|
|
226
|
+
const body = node.children.map((child) => generateChild(child, '$host')).join('')
|
|
227
|
+
return `function ${node.name}(${node.params ?? ''}) {\nreturn snippet(($host) => {\n${body}});\n}\n`
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/* A switch: each `case` is `{ match: () => value, render }`, the default is
|
|
231
|
+
`{ match: undefined, render }`. */
|
|
232
|
+
function generateSwitch(
|
|
233
|
+
node: Extract<TemplateNode, { kind: 'switch' }>,
|
|
234
|
+
parentVar: string,
|
|
235
|
+
): string {
|
|
236
|
+
const cases = node.children
|
|
237
|
+
.filter(
|
|
238
|
+
(child): child is Extract<TemplateNode, { kind: 'case' }> => child.kind === 'case',
|
|
239
|
+
)
|
|
240
|
+
.map((branch) => {
|
|
241
|
+
const param = nextVar('p')
|
|
242
|
+
const roots = elementRoots(branch.children, '<template case>', param)
|
|
243
|
+
const match =
|
|
244
|
+
branch.match === undefined
|
|
245
|
+
? 'undefined'
|
|
246
|
+
: `() => (${lowerExpression(branch.match)})`
|
|
247
|
+
return `{ match: ${match}, render: (${param}) => {\n${roots.code}return ${roots.expr};\n} }`
|
|
248
|
+
})
|
|
249
|
+
.join(', ')
|
|
250
|
+
return `switchBlock(${parentVar}, () => (${lowerExpression(node.subject)}), [${cases}]);\n`
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/* A `<slot>` outlet: render the parent-provided content for this slot (default
|
|
254
|
+
via `$children`, named via `$slots[name]`), falling back to the slot's own
|
|
255
|
+
children when the parent supplied none. */
|
|
256
|
+
function generateSlot(
|
|
257
|
+
node: Extract<TemplateNode, { kind: 'element' }>,
|
|
258
|
+
parentVar: string,
|
|
259
|
+
): string {
|
|
260
|
+
const name = staticAttrValue(node, 'name')
|
|
261
|
+
const guard =
|
|
262
|
+
name === undefined
|
|
263
|
+
? '$props && $props.$children'
|
|
264
|
+
: `$props && $props.$slots && $props.$slots[${JSON.stringify(name)}]`
|
|
265
|
+
const invoke =
|
|
266
|
+
name === undefined
|
|
267
|
+
? `$props.$children(${parentVar})`
|
|
268
|
+
: `$props.$slots[${JSON.stringify(name)}](${parentVar})`
|
|
269
|
+
const fallback = node.children.map((child) => generateChild(child, parentVar)).join('')
|
|
270
|
+
if (fallback.trim() === '') {
|
|
271
|
+
return `if (${guard}) { ${invoke}; }\n`
|
|
272
|
+
}
|
|
273
|
+
return `if (${guard}) { ${invoke}; } else {\n${fallback}}\n`
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/* Mounts a child component into a wrapper element, passing each prop as a
|
|
277
|
+
reactive thunk so the child re-reads when the parent expression changes. */
|
|
278
|
+
function generateComponent(
|
|
279
|
+
node: Extract<TemplateNode, { kind: 'component' }>,
|
|
280
|
+
parentVar: string,
|
|
281
|
+
): string {
|
|
282
|
+
const wrapper = nextVar('cmp')
|
|
283
|
+
const parts = node.props.map(
|
|
284
|
+
(prop) => `${JSON.stringify(prop.name)}: () => (${lowerExpression(prop.code)})`,
|
|
285
|
+
)
|
|
286
|
+
/* Slot content compiles to builders the child mounts into the host it passes
|
|
287
|
+
from each <slot> position: the default markup as `$children`, and each
|
|
288
|
+
`slot="name"` group as `$slots[name]`. */
|
|
289
|
+
const groups = partitionSlots(node.children)
|
|
290
|
+
const slotCode = groups.default.map((child) => generateChild(child, '$slot')).join('')
|
|
291
|
+
if (slotCode.trim() !== '') {
|
|
292
|
+
parts.push(`"$children": ($slot) => {\n${slotCode}}`)
|
|
293
|
+
}
|
|
294
|
+
if (groups.named.length > 0) {
|
|
295
|
+
const entries = groups.named
|
|
296
|
+
.map((group) => {
|
|
297
|
+
const code = group.nodes.map((child) => generateChild(child, '$slot')).join('')
|
|
298
|
+
return `${JSON.stringify(group.name)}: ($slot) => {\n${code}}`
|
|
299
|
+
})
|
|
300
|
+
.join(', ')
|
|
301
|
+
parts.push(`"$slots": { ${entries} }`)
|
|
302
|
+
}
|
|
303
|
+
/* openChild appends (create) or claims the SSR wrapper (hydrate); since
|
|
304
|
+
hydration is still active, the child's own build then adopts its server
|
|
305
|
+
markup inside the wrapper. */
|
|
306
|
+
return (
|
|
307
|
+
`const ${wrapper} = openChild(${parentVar}, ${JSON.stringify(node.name.toLowerCase())});\n` +
|
|
308
|
+
`mountChild(${wrapper}, ${node.name}, { ${parts.join(', ')} });\n`
|
|
309
|
+
)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/* An await block: pending → resolved(value) / error branches. Each branch is a
|
|
313
|
+
single-element root; a render thunk returns its node. */
|
|
314
|
+
function generateAwait(
|
|
315
|
+
node: Extract<TemplateNode, { kind: 'await' }>,
|
|
316
|
+
parentVar: string,
|
|
317
|
+
): string {
|
|
318
|
+
const isBranch = (which: 'then' | 'catch' | 'finally') => (child: TemplateNode) =>
|
|
319
|
+
child.kind === 'branch' && child.branch === which
|
|
320
|
+
const catchBranch = node.children.find(isBranch('catch'))
|
|
321
|
+
const finallyChildren = branchChildren(node.children.find(isBranch('finally')))
|
|
322
|
+
/* Blocking: no pending, the children are the resolved branch bound to `node.as`.
|
|
323
|
+
Streaming: pending is the non-branch children, resolved is the `then` child. */
|
|
324
|
+
const pending = node.blocking
|
|
325
|
+
? []
|
|
326
|
+
: node.children.filter((child) => child.kind !== 'branch')
|
|
327
|
+
const thenThunk = node.blocking
|
|
328
|
+
? renderRangeThunk(
|
|
329
|
+
node.children.filter((child) => child.kind !== 'branch'),
|
|
330
|
+
node.as ?? '_value',
|
|
331
|
+
'<template await then>',
|
|
332
|
+
finallyChildren,
|
|
333
|
+
)
|
|
334
|
+
: renderSettledThunk(
|
|
335
|
+
node.children.find(isBranch('then')),
|
|
336
|
+
'_value',
|
|
337
|
+
'<template then>',
|
|
338
|
+
finallyChildren,
|
|
339
|
+
)
|
|
340
|
+
/* Neither catch nor finally → pass `undefined` so awaitBlock re-throws the
|
|
341
|
+
rejection (surfacing it) instead of rendering an empty branch. A finally-only
|
|
342
|
+
block keeps a catch thunk that renders just finally. */
|
|
343
|
+
const catchThunk =
|
|
344
|
+
catchBranch === undefined && finallyChildren.length === 0
|
|
345
|
+
? 'undefined'
|
|
346
|
+
: renderSettledThunk(catchBranch, '_error', '<template catch>', finallyChildren)
|
|
347
|
+
return (
|
|
348
|
+
`awaitBlock(${parentVar}, nextBlockId(), () => (${lowerExpression(node.promise)}), ` +
|
|
349
|
+
`${renderThunk(pending, undefined, '<template await> pending')}, ` +
|
|
350
|
+
`${thenThunk}, ` +
|
|
351
|
+
`${catchThunk});\n`
|
|
352
|
+
)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/* Children of a branch node (then/catch), or [] when the branch is absent. */
|
|
356
|
+
function branchChildren(branch: TemplateNode | undefined): TemplateNode[] {
|
|
357
|
+
return branch !== undefined && branch.kind === 'branch' ? branch.children : []
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/* The value/error variable name a branch binds, if any. */
|
|
361
|
+
function branchVar(branch: TemplateNode | undefined): string | undefined {
|
|
362
|
+
return branch !== undefined && branch.kind === 'branch' ? branch.as : undefined
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/* Builds the element roots of a branch into `parentVar` (each via openRoot, so
|
|
366
|
+
detached on create / claimed on hydrate), returning the code plus an array
|
|
367
|
+
expression of the root nodes the block tracks as a range. */
|
|
368
|
+
function elementRoots(
|
|
369
|
+
children: TemplateNode[],
|
|
370
|
+
context: string,
|
|
371
|
+
parentVar: string,
|
|
372
|
+
allowEmpty = false,
|
|
373
|
+
): { code: string; expr: string } {
|
|
374
|
+
/* Nested `<script>`s: add their bindings to the deref scope (so the script
|
|
375
|
+
body + this branch's markup auto-deref them), emit the lowered script
|
|
376
|
+
bodies first, then build the element roots — all within the scope, which
|
|
377
|
+
we pop afterward. The scripts run when the branch mounts, owned by its
|
|
378
|
+
scope. */
|
|
379
|
+
const added: string[] = []
|
|
380
|
+
for (const child of children) {
|
|
381
|
+
if (child.kind === 'script') {
|
|
382
|
+
for (const name of nestedBindingNames(child.code)) {
|
|
383
|
+
if (!localDerived.has(name)) {
|
|
384
|
+
localDerived.add(name)
|
|
385
|
+
added.push(name)
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
const scriptCode = children
|
|
391
|
+
.filter(
|
|
392
|
+
(child): child is Extract<TemplateNode, { kind: 'script' }> =>
|
|
393
|
+
child.kind === 'script',
|
|
394
|
+
)
|
|
395
|
+
.map((child) => `${lowerStatement(child.code)}\n`)
|
|
396
|
+
.join('')
|
|
397
|
+
const built = branchElements(children, context, allowEmpty).map((element) =>
|
|
398
|
+
generateElement(element, `openRoot(${parentVar}, ${JSON.stringify(element.tag)})`),
|
|
399
|
+
)
|
|
400
|
+
for (const name of added) {
|
|
401
|
+
localDerived.delete(name)
|
|
402
|
+
}
|
|
403
|
+
return {
|
|
404
|
+
code: scriptCode + built.map((part) => part.code).join(''),
|
|
405
|
+
expr: `[${built.map((part) => part.varName).join(', ')}]`,
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/* A `(parent[, value]) => Node[]` thunk over a branch's element roots, or
|
|
410
|
+
`undefined` when empty (a `<template await>` with no pending branch).
|
|
411
|
+
`paramName`/`fallback` name the resolved/error value the branch binds. */
|
|
412
|
+
function renderThunk(
|
|
413
|
+
children: TemplateNode[],
|
|
414
|
+
paramName: string | undefined,
|
|
415
|
+
context: string,
|
|
416
|
+
fallback?: string,
|
|
417
|
+
): string {
|
|
418
|
+
const hasElement = children.some((child) => child.kind === 'element')
|
|
419
|
+
const parentParam = nextVar('p')
|
|
420
|
+
if (!hasElement) {
|
|
421
|
+
const value = fallback === undefined ? '' : `, ${paramName ?? fallback}`
|
|
422
|
+
return fallback === undefined ? 'undefined' : `(${parentParam}${value}) => []`
|
|
423
|
+
}
|
|
424
|
+
const roots = elementRoots(children, context, parentParam)
|
|
425
|
+
const value = fallback === undefined ? '' : `, ${paramName ?? fallback}`
|
|
426
|
+
return `(${parentParam}${value}) => {\n${roots.code}return ${roots.expr};\n}`
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/* A thunk over a node range: `children`'s roots concatenated with the `finally`
|
|
430
|
+
roots, both possibly empty. `param` names a bound value (the resolved/error
|
|
431
|
+
value, or the caught error); undefined for a value-less branch (try/pending). */
|
|
432
|
+
function renderRangeThunk(
|
|
433
|
+
children: TemplateNode[],
|
|
434
|
+
param: string | undefined,
|
|
435
|
+
context: string,
|
|
436
|
+
finallyChildren: TemplateNode[],
|
|
437
|
+
): string {
|
|
438
|
+
const parentParam = nextVar('p')
|
|
439
|
+
const head = param === undefined ? `(${parentParam})` : `(${parentParam}, ${param})`
|
|
440
|
+
const roots = elementRoots(children, context, parentParam, true)
|
|
441
|
+
const finallyRoots = elementRoots(finallyChildren, '<template finally>', parentParam, true)
|
|
442
|
+
return `${head} => {\n${roots.code}${finallyRoots.code}return [...${roots.expr}, ...${finallyRoots.expr}];\n}`
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/* A settled (then/catch) thunk: the outcome branch's roots ++ `finally`. */
|
|
446
|
+
function renderSettledThunk(
|
|
447
|
+
branch: TemplateNode | undefined,
|
|
448
|
+
fallback: string,
|
|
449
|
+
context: string,
|
|
450
|
+
finallyChildren: TemplateNode[],
|
|
451
|
+
): string {
|
|
452
|
+
return renderRangeThunk(
|
|
453
|
+
branchChildren(branch),
|
|
454
|
+
branchVar(branch) ?? fallback,
|
|
455
|
+
context,
|
|
456
|
+
finallyChildren,
|
|
457
|
+
)
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/* The branch child of a control block matching `which` (then/catch/finally). */
|
|
461
|
+
function findBranch(
|
|
462
|
+
children: TemplateNode[],
|
|
463
|
+
which: 'then' | 'catch' | 'finally',
|
|
464
|
+
): Extract<TemplateNode, { kind: 'branch' }> | undefined {
|
|
465
|
+
return children.find(
|
|
466
|
+
(child): child is Extract<TemplateNode, { kind: 'branch' }> =>
|
|
467
|
+
child.kind === 'branch' && child.branch === which,
|
|
468
|
+
)
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/* A sync error boundary: build the guarded subtree (++ finally); a throw while
|
|
472
|
+
building swaps to the catch branch (++ finally). No catch → `undefined`, which
|
|
473
|
+
makes the runtime re-throw to the nearest enclosing boundary. */
|
|
474
|
+
function generateTry(node: Extract<TemplateNode, { kind: 'try' }>, parentVar: string): string {
|
|
475
|
+
const catchBranch = findBranch(node.children, 'catch')
|
|
476
|
+
const finallyChildren = branchChildren(findBranch(node.children, 'finally'))
|
|
477
|
+
const guarded = node.children.filter((child) => child.kind !== 'branch')
|
|
478
|
+
const tryThunk = renderRangeThunk(guarded, undefined, '<template try>', finallyChildren)
|
|
479
|
+
const catchThunk =
|
|
480
|
+
catchBranch === undefined
|
|
481
|
+
? 'undefined'
|
|
482
|
+
: renderRangeThunk(
|
|
483
|
+
branchChildren(catchBranch),
|
|
484
|
+
branchVar(catchBranch) ?? '_error',
|
|
485
|
+
'<template catch>',
|
|
486
|
+
finallyChildren,
|
|
487
|
+
)
|
|
488
|
+
return `tryBlock(${parentVar}, nextBlockId(), ${tryThunk}, ${catchThunk});\n`
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/* A conditional with an optional nested `<template else>` (a `case` child).
|
|
492
|
+
Both branches are single-element roots. */
|
|
493
|
+
function generateIf(node: Extract<TemplateNode, { kind: 'if' }>, parentVar: string): string {
|
|
494
|
+
const elseBranch = node.children.find(
|
|
495
|
+
(child): child is Extract<TemplateNode, { kind: 'case' }> => child.kind === 'case',
|
|
496
|
+
)
|
|
497
|
+
const thenChildren = node.children.filter((child) => child.kind !== 'case')
|
|
498
|
+
const thenParam = nextVar('p')
|
|
499
|
+
const thenRoots = elementRoots(thenChildren, '<template if>', thenParam)
|
|
500
|
+
const thenThunk = `(${thenParam}) => {\n${thenRoots.code}return ${thenRoots.expr};\n}`
|
|
501
|
+
if (elseBranch === undefined) {
|
|
502
|
+
return `when(${parentVar}, () => (${lowerExpression(node.condition)}), ${thenThunk});\n`
|
|
503
|
+
}
|
|
504
|
+
const elseParam = nextVar('p')
|
|
505
|
+
const elseRoots = elementRoots(elseBranch.children, '<template else>', elseParam)
|
|
506
|
+
const elseThunk = `(${elseParam}) => {\n${elseRoots.code}return ${elseRoots.expr};\n}`
|
|
507
|
+
return `when(${parentVar}, () => (${lowerExpression(node.condition)}), ${thenThunk}, ${elseThunk});\n`
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/* A keyed each. The row must have a single element root (it returns one node). */
|
|
511
|
+
function generateEach(
|
|
512
|
+
node: Extract<TemplateNode, { kind: 'each' }>,
|
|
513
|
+
parentVar: string,
|
|
514
|
+
): string {
|
|
515
|
+
const rowParam = nextVar('p')
|
|
516
|
+
/* A `<script>` in the row body declares per-row local signals (seeded from
|
|
517
|
+
the row item), scoped to this row's render thunk. */
|
|
518
|
+
const added = scopeNestedScripts(node.children)
|
|
519
|
+
const scriptCode = node.children
|
|
520
|
+
.filter(
|
|
521
|
+
(child): child is Extract<TemplateNode, { kind: 'script' }> =>
|
|
522
|
+
child.kind === 'script',
|
|
523
|
+
)
|
|
524
|
+
.map((child) => `${lowerStatement(child.code)}\n`)
|
|
525
|
+
.join('')
|
|
526
|
+
const row = singleElementRoot(
|
|
527
|
+
node.children,
|
|
528
|
+
'<template each> must contain a single element row',
|
|
529
|
+
rowParam,
|
|
530
|
+
)
|
|
531
|
+
for (const name of added) {
|
|
532
|
+
localDerived.delete(name)
|
|
533
|
+
}
|
|
534
|
+
const keyExpression = node.key === undefined ? node.as : lowerExpression(node.key)
|
|
535
|
+
/* `await` → the AsyncIterable runtime, drained row-by-row on the client, with an
|
|
536
|
+
optional `<template catch>` branch rendered (after the streamed rows) when the
|
|
537
|
+
iterator rejects. Absent → `undefined`, so the rejection surfaces instead. */
|
|
538
|
+
const fn = node.async ? 'eachAsync' : 'each'
|
|
539
|
+
const catchBranch = node.children.find(
|
|
540
|
+
(child) => child.kind === 'branch' && child.branch === 'catch',
|
|
541
|
+
)
|
|
542
|
+
const catchArg = node.async
|
|
543
|
+
? `, ${catchBranch === undefined ? 'undefined' : renderSettledThunk(catchBranch, '_error', '<template catch>', [])}`
|
|
544
|
+
: ''
|
|
545
|
+
return (
|
|
546
|
+
`${fn}(${parentVar}, () => (${lowerExpression(node.items)}), ` +
|
|
547
|
+
`(${node.as}) => (${keyExpression}), (${rowParam}, ${node.as}) => {\n${scriptCode}${row.code}return ${row.varName};\n}${catchArg});\n`
|
|
548
|
+
)
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/* Builds the lone element child of a control-flow block (each/if return one
|
|
552
|
+
node), erroring if the block isn't a single element. The root is opened
|
|
553
|
+
with `openRoot(parentVar, tag)` so it's detached on create and claimed on
|
|
554
|
+
hydrate — `parentVar` is the render thunk's parent parameter. */
|
|
555
|
+
function singleElementRoot(
|
|
556
|
+
children: TemplateNode[],
|
|
557
|
+
message: string,
|
|
558
|
+
parentVar: string,
|
|
559
|
+
): { code: string; varName: string } {
|
|
560
|
+
const root = children.find((child) => child.kind === 'element')
|
|
561
|
+
if (root === undefined || root.kind !== 'element') {
|
|
562
|
+
throw new Error(`[abide] ${message}`)
|
|
563
|
+
}
|
|
564
|
+
return generateElement(root, `openRoot(${parentVar}, ${JSON.stringify(root.tag)})`)
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
return generateChildren(nodes, hostVar)
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/* A text node that is purely whitespace (no interpolation, only blank static
|
|
571
|
+
parts). Both back-ends drop it, so it neither contributes markup nor breaks a
|
|
572
|
+
static clone run — it stays transparent so `<a/>\n<b/>` still coalesces. */
|
|
573
|
+
function isWhitespaceText(node: TemplateNode): boolean {
|
|
574
|
+
return (
|
|
575
|
+
node.kind === 'text' &&
|
|
576
|
+
node.parts.every((part) => part.kind === 'static' && part.value.trim() === '')
|
|
577
|
+
)
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/*
|
|
581
|
+
Whether an element subtree is fully static — no reactive/event/bind attributes,
|
|
582
|
+
no nested `<script>`, and every descendant likewise static (static text, static
|
|
583
|
+
child elements, or scope-only `<style>`). Such a subtree builds to fixed DOM with
|
|
584
|
+
no per-instance wiring, so it can be cloned from a template instead of built call
|
|
585
|
+
by call. Only ELEMENTS qualify as run members: a static element never merges with
|
|
586
|
+
an adjacent dynamic text node, whereas a bare static text sibling shares one
|
|
587
|
+
merged SSR text node with its neighbour (the `splitAlways` hazard) — those stay
|
|
588
|
+
imperative. Static text and elements nested INSIDE a qualifying element are fine,
|
|
589
|
+
enclosed by its tags.
|
|
590
|
+
*/
|
|
591
|
+
function isStaticCloneableElement(node: TemplateNode): boolean {
|
|
592
|
+
if (node.kind !== 'element' || node.tag === 'slot') {
|
|
593
|
+
return false
|
|
594
|
+
}
|
|
595
|
+
if (node.attrs.some((attr) => attr.kind !== 'static')) {
|
|
596
|
+
return false
|
|
597
|
+
}
|
|
598
|
+
return node.children.every(
|
|
599
|
+
(child) =>
|
|
600
|
+
child.kind === 'style' ||
|
|
601
|
+
(child.kind === 'text' && child.parts.every((part) => part.kind === 'static')) ||
|
|
602
|
+
isStaticCloneableElement(child),
|
|
603
|
+
)
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/*
|
|
607
|
+
Renders a fully-static node to its constant HTML, byte-identical to the SSR
|
|
608
|
+
back-end's output for the same node (same scope-attr order, same escaping, same
|
|
609
|
+
void-tag handling, same whitespace-only-text dropping) — so the client clone
|
|
610
|
+
template and the server markup parse to the same DOM. Only handles the shapes
|
|
611
|
+
`isStaticCloneableElement` admits.
|
|
612
|
+
*/
|
|
613
|
+
function staticHtml(node: TemplateNode): string {
|
|
614
|
+
if (node.kind === 'text') {
|
|
615
|
+
return node.parts
|
|
616
|
+
.map((part) =>
|
|
617
|
+
part.kind === 'static' && part.value.trim() !== '' ? escapeHtml(part.value) : '',
|
|
618
|
+
)
|
|
619
|
+
.join('')
|
|
620
|
+
}
|
|
621
|
+
if (node.kind !== 'element') {
|
|
622
|
+
return '' // <style> and any non-element emit no markup
|
|
623
|
+
}
|
|
624
|
+
let html = `<${node.tag}`
|
|
625
|
+
for (const scope of node.scopes ?? []) {
|
|
626
|
+
html += ` ${scope}=""`
|
|
627
|
+
}
|
|
628
|
+
for (const attr of node.attrs) {
|
|
629
|
+
if (attr.kind === 'static') {
|
|
630
|
+
html += ` ${attr.name}="${escapeHtml(attr.value)}"`
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
html += '>'
|
|
634
|
+
if (VOID_TAGS.has(node.tag)) {
|
|
635
|
+
return html
|
|
636
|
+
}
|
|
637
|
+
return `${html}${node.children.map(staticHtml).join('')}</${node.tag}>`
|
|
638
|
+
}
|