@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,36 @@
|
|
|
1
|
+
import { analyzeComponent } from './analyzeComponent.ts'
|
|
2
|
+
import { generateSSR } from './generateSSR.ts'
|
|
3
|
+
import { SSR_ESCAPE } from './SSR_ESCAPE.ts'
|
|
4
|
+
import { stripEffects } from './stripEffects.ts'
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
Compiles a component into the body of a server render function. Runs the shared
|
|
8
|
+
front-end, then the SSR back-end, and returns `{ html, state, awaits }`:
|
|
9
|
+
|
|
10
|
+
- `html` — server-rendered markup (await blocks render their pending shell);
|
|
11
|
+
- `state` — the document snapshot the client adopts on resume;
|
|
12
|
+
- `awaits` — pending await blocks (id + promise + resolved/error renderers) that
|
|
13
|
+
`renderToStream` flushes out of order; empty for a fully synchronous component.
|
|
14
|
+
|
|
15
|
+
Effects are stripped — they are client lifecycle and emit no HTML, so the server
|
|
16
|
+
render is a snapshot of the markup before any effect runs.
|
|
17
|
+
|
|
18
|
+
Runs with `doc`/`state`/`derived`/`effect`/`nextBlockId`/`enterRenderPass`/
|
|
19
|
+
`exitRenderPass` in scope and defines `model`. The body is bracketed by a render
|
|
20
|
+
pass so the outermost render resets the block-id counter and an inlined child
|
|
21
|
+
render continues it — keeping await/try ids unique and aligned with the client.
|
|
22
|
+
*/
|
|
23
|
+
export function compileSSR(source: string, isLayout = false, scopeSeed?: string): string {
|
|
24
|
+
const { script, stateNames, derivedNames, nodes } = analyzeComponent(source, scopeSeed)
|
|
25
|
+
const ssr = generateSSR(nodes, stateNames, derivedNames, isLayout)
|
|
26
|
+
/* No `<style>` in the markup — the scoped CSS is bundled into the entry stylesheet
|
|
27
|
+
the shell links (see `abideUiPlugin`), so SSR output is styled by that sheet. The
|
|
28
|
+
elements still carry their `data-a-…` scopes via `generateSSR`. */
|
|
29
|
+
/* `typeof model` guards a component with no reactive state (a pure-async or
|
|
30
|
+
static component declares no `model`); its snapshot is then empty. */
|
|
31
|
+
return (
|
|
32
|
+
`enterRenderPass();\ntry {\n${stripEffects(script)}\n${SSR_ESCAPE}\nconst $out = [];\nconst $awaits = [];\n${ssr}` +
|
|
33
|
+
`return { html: $out.join(''), state: (typeof model !== 'undefined' ? model.snapshot() : {}), awaits: $awaits };\n` +
|
|
34
|
+
`} finally { exitRenderPass(); }`
|
|
35
|
+
)
|
|
36
|
+
}
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import ts from 'typescript'
|
|
2
|
+
import { ABIDE_PACKAGE_NAME } from '../../shared/ABIDE_PACKAGE_NAME.ts'
|
|
3
|
+
import { parseTemplate } from './parseTemplate.ts'
|
|
4
|
+
import type { CompiledShadow, ShadowMapping } from './types/CompiledShadow.ts'
|
|
5
|
+
import type { TemplateNode } from './types/TemplateNode.ts'
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
Framework callables the `.abide` loader injects into a component's scope, imported
|
|
9
|
+
into the shadow with their real types so author calls (`effect`, `html`, …)
|
|
10
|
+
type-check. `state`/`derived` are imported too as a fallback for stray uses; their
|
|
11
|
+
declarations are rewritten to value types so the import is normally unused (fine —
|
|
12
|
+
the shadow program disables noUnusedLocals). `prop` has no runtime export, so it
|
|
13
|
+
never appears here — every `prop()` declaration is rewritten away. `$props` is the
|
|
14
|
+
legacy untyped prop bag (pre-`prop()` sugar) made available raw.
|
|
15
|
+
*/
|
|
16
|
+
const SHADOW_PREAMBLE = `import { state } from '${ABIDE_PACKAGE_NAME}/ui/state'
|
|
17
|
+
import { derived } from '${ABIDE_PACKAGE_NAME}/ui/derived'
|
|
18
|
+
import { effect } from '${ABIDE_PACKAGE_NAME}/ui/effect'
|
|
19
|
+
import { doc } from '${ABIDE_PACKAGE_NAME}/ui/doc'
|
|
20
|
+
import { html } from '${ABIDE_PACKAGE_NAME}/shared/html'
|
|
21
|
+
import { snippet } from '${ABIDE_PACKAGE_NAME}/shared/snippet'
|
|
22
|
+
declare const $props: Record<string, (() => unknown) | undefined>
|
|
23
|
+
void [state, derived, effect, doc, html, snippet]
|
|
24
|
+
`
|
|
25
|
+
|
|
26
|
+
/*
|
|
27
|
+
Compiles a `.abide` component into its type-checking shadow — a synthetic TS
|
|
28
|
+
module that reconstructs the author scope with value types and references every
|
|
29
|
+
template expression in a checkable position (see ADR-0010). The shadow is never
|
|
30
|
+
executed; it exists only so `tsc`/the language service can type-check template
|
|
31
|
+
expressions and child-component props, with diagnostics mapped back through the
|
|
32
|
+
returned segments.
|
|
33
|
+
|
|
34
|
+
The script's signal surface is rewritten to value types:
|
|
35
|
+
let count = state(0) → let count = (0)
|
|
36
|
+
const total = derived(() => …) → const total = (() => …)()
|
|
37
|
+
let title = prop<string>('t') → Props field + `let title = props['t']`
|
|
38
|
+
Everything else (functions, plain consts, imports) is emitted verbatim, so
|
|
39
|
+
expressions inside it (e.g. a derived's compute body) are checked and mapped too.
|
|
40
|
+
*/
|
|
41
|
+
export function compileShadow(source: string): CompiledShadow {
|
|
42
|
+
const builder = createBuilder()
|
|
43
|
+
const leadingScript = source.match(/^\s*<script[^>]*>([\s\S]*?)<\/script>/)
|
|
44
|
+
const scriptBody = leadingScript?.[1] ?? ''
|
|
45
|
+
/* Body starts just past the opening `<script …>`; template just past `</script>`. */
|
|
46
|
+
const scriptStart = leadingScript ? source.indexOf('>', leadingScript.index) + 1 : 0
|
|
47
|
+
const templateStart = leadingScript ? (leadingScript.index ?? 0) + leadingScript[0].length : 0
|
|
48
|
+
|
|
49
|
+
const { imports, scope, props } = analyzeScript(scriptBody, scriptStart)
|
|
50
|
+
builder.raw(SHADOW_PREAMBLE)
|
|
51
|
+
for (const line of imports) {
|
|
52
|
+
builder.flush(line)
|
|
53
|
+
}
|
|
54
|
+
builder.raw(`interface __Props {\n${props.join('\n')}\n}\n`)
|
|
55
|
+
/* async so `await` blocks are legal; never executed, so the return is void. */
|
|
56
|
+
builder.raw('export default async function (props: __Props): Promise<void> {\n')
|
|
57
|
+
/* Reference props so an all-optional bag with no reads doesn't read as unused. */
|
|
58
|
+
builder.raw('void props;\n')
|
|
59
|
+
for (const line of scope) {
|
|
60
|
+
builder.flush(line)
|
|
61
|
+
}
|
|
62
|
+
for (const node of parseTemplate(source.slice(templateStart), templateStart).nodes) {
|
|
63
|
+
emitNode(node, builder)
|
|
64
|
+
}
|
|
65
|
+
builder.raw('}\n')
|
|
66
|
+
return builder.result()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* The shadow text builder: `raw` appends synthesised scaffolding (no mapping),
|
|
70
|
+
`expr` appends an inline parenthesised source span `(code)` and records its
|
|
71
|
+
segment, `stmt` wraps one as a standalone statement, `flush` appends a
|
|
72
|
+
pre-assembled scope line carrying its own embedded segments. */
|
|
73
|
+
type Builder = {
|
|
74
|
+
raw: (text: string) => void
|
|
75
|
+
expr: (code: string, sourceLoc: number | undefined) => void
|
|
76
|
+
stmt: (code: string, sourceLoc: number | undefined) => void
|
|
77
|
+
flush: (line: ScopeLine) => void
|
|
78
|
+
result: () => CompiledShadow
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* A reconstructed scope statement plus the segments embedded inside it (the parts
|
|
82
|
+
emitted verbatim from the original script), offset-relative to the line start. */
|
|
83
|
+
type ScopeLine = { text: string; segments: ShadowMapping[] }
|
|
84
|
+
|
|
85
|
+
function createBuilder(): Builder {
|
|
86
|
+
let code = ''
|
|
87
|
+
const mappings: ShadowMapping[] = []
|
|
88
|
+
const builder: Builder = {
|
|
89
|
+
raw(text) {
|
|
90
|
+
code += text
|
|
91
|
+
},
|
|
92
|
+
expr(exprCode, sourceLoc) {
|
|
93
|
+
code += '('
|
|
94
|
+
if (sourceLoc !== undefined) {
|
|
95
|
+
mappings.push({
|
|
96
|
+
shadowStart: code.length,
|
|
97
|
+
sourceStart: sourceLoc,
|
|
98
|
+
length: exprCode.length,
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
code += `${exprCode})`
|
|
102
|
+
},
|
|
103
|
+
stmt(exprCode, sourceLoc) {
|
|
104
|
+
code += ';'
|
|
105
|
+
builder.expr(exprCode, sourceLoc)
|
|
106
|
+
code += ';\n'
|
|
107
|
+
},
|
|
108
|
+
flush(line) {
|
|
109
|
+
const base = code.length
|
|
110
|
+
for (const segment of line.segments) {
|
|
111
|
+
mappings.push({ ...segment, shadowStart: base + segment.shadowStart })
|
|
112
|
+
}
|
|
113
|
+
code += `${line.text}\n`
|
|
114
|
+
},
|
|
115
|
+
result: () => ({ code, mappings }),
|
|
116
|
+
}
|
|
117
|
+
return builder
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
type ScriptAnalysis = { imports: ScopeLine[]; scope: ScopeLine[]; props: string[] }
|
|
121
|
+
|
|
122
|
+
/* Walks the leading `<script>` and produces the shadow's module imports, the
|
|
123
|
+
value-typed scope lines, and the Props interface fields. `scriptStart` is the
|
|
124
|
+
body's absolute offset in the source, so verbatim spans map back exactly. */
|
|
125
|
+
function analyzeScript(scriptBody: string, scriptStart: number): ScriptAnalysis {
|
|
126
|
+
const imports: ScopeLine[] = []
|
|
127
|
+
const scope: ScopeLine[] = []
|
|
128
|
+
const props: string[] = []
|
|
129
|
+
if (scriptBody.trim() === '') {
|
|
130
|
+
return { imports, scope, props }
|
|
131
|
+
}
|
|
132
|
+
const file = ts.createSourceFile('script.ts', scriptBody, ts.ScriptTarget.Latest, true)
|
|
133
|
+
/* A verbatim span: original text + the segment mapping it back, relative to the
|
|
134
|
+
line start (the caller rebases shadowStart onto the running shadow length). */
|
|
135
|
+
const span = (node: ts.Node, prefixLength: number): ScopeLine['segments'][number] => ({
|
|
136
|
+
shadowStart: prefixLength,
|
|
137
|
+
sourceStart: scriptStart + node.getStart(file),
|
|
138
|
+
length: node.getEnd() - node.getStart(file),
|
|
139
|
+
})
|
|
140
|
+
const verbatim = (node: ts.Node): string => scriptBody.slice(node.getStart(file), node.getEnd())
|
|
141
|
+
|
|
142
|
+
for (const statement of file.statements) {
|
|
143
|
+
if (ts.isImportDeclaration(statement)) {
|
|
144
|
+
/* Emit verbatim with a span so hover/go-to resolve on the imported names. */
|
|
145
|
+
imports.push({ text: verbatim(statement), segments: [span(statement, 0)] })
|
|
146
|
+
continue
|
|
147
|
+
}
|
|
148
|
+
const reactive = reactiveDeclarations(statement)
|
|
149
|
+
if (reactive === undefined) {
|
|
150
|
+
/* Plain statement (function, const, expression) — emit verbatim, mapped. */
|
|
151
|
+
scope.push({ text: verbatim(statement), segments: [span(statement, 0)] })
|
|
152
|
+
continue
|
|
153
|
+
}
|
|
154
|
+
for (const declaration of reactive) {
|
|
155
|
+
scope.push(scopeLineFor(declaration, props, verbatim, span))
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return { imports, scope, props }
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/* The `state`/`derived`/`prop` declarations in a variable statement, or undefined
|
|
162
|
+
if it isn't one declaring them (so the caller emits it verbatim). A statement
|
|
163
|
+
mixing reactive and plain declarations is rare; treated as all-verbatim. */
|
|
164
|
+
function reactiveDeclarations(statement: ts.Statement): ts.VariableDeclaration[] | undefined {
|
|
165
|
+
if (!ts.isVariableStatement(statement)) {
|
|
166
|
+
return undefined
|
|
167
|
+
}
|
|
168
|
+
const declarations = statement.declarationList.declarations
|
|
169
|
+
const reactive = declarations.filter((declaration) => signalCallee(declaration) !== undefined)
|
|
170
|
+
return reactive.length === declarations.length && reactive.length > 0 ? reactive : undefined
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* The callee name of a `NAME = state(...)` / `derived(...)` / `prop(...)` decl. */
|
|
174
|
+
function signalCallee(declaration: ts.VariableDeclaration): string | undefined {
|
|
175
|
+
const initializer = declaration.initializer
|
|
176
|
+
if (
|
|
177
|
+
initializer !== undefined &&
|
|
178
|
+
ts.isCallExpression(initializer) &&
|
|
179
|
+
ts.isIdentifier(initializer.expression) &&
|
|
180
|
+
['state', 'derived', 'prop'].includes(initializer.expression.text)
|
|
181
|
+
) {
|
|
182
|
+
return initializer.expression.text
|
|
183
|
+
}
|
|
184
|
+
return undefined
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* Builds one scope line for a reactive declaration, projecting it to its value
|
|
188
|
+
type. `prop` also contributes a Props field (pushed into `props`). */
|
|
189
|
+
function scopeLineFor(
|
|
190
|
+
declaration: ts.VariableDeclaration,
|
|
191
|
+
props: string[],
|
|
192
|
+
verbatim: (node: ts.Node) => string,
|
|
193
|
+
span: (node: ts.Node, prefixLength: number) => ShadowMapping,
|
|
194
|
+
): ScopeLine {
|
|
195
|
+
const name = ts.isIdentifier(declaration.name) ? declaration.name.text : '_'
|
|
196
|
+
const call = declaration.initializer as ts.CallExpression
|
|
197
|
+
const callee = (call.expression as ts.Identifier).text
|
|
198
|
+
if (callee === 'state') {
|
|
199
|
+
/* state<T>(initial): T is the value type — carry it onto the `let` so an
|
|
200
|
+
explicit annotation isn't lost to `any`/`any[]` inference of the initial. */
|
|
201
|
+
const typeNode = call.typeArguments?.[0]
|
|
202
|
+
const annotation = typeNode === undefined ? '' : `: ${verbatim(typeNode)}`
|
|
203
|
+
const init = call.arguments[0]
|
|
204
|
+
if (init === undefined) {
|
|
205
|
+
return { text: `let ${name}${annotation};`, segments: [] }
|
|
206
|
+
}
|
|
207
|
+
const prefix = `let ${name}${annotation} = (`
|
|
208
|
+
return { text: `${prefix}${verbatim(init)});`, segments: [span(init, prefix.length)] }
|
|
209
|
+
}
|
|
210
|
+
if (callee === 'derived') {
|
|
211
|
+
/* derived<T>(compute): T is the value type — annotate so an explicit
|
|
212
|
+
argument isn't lost to inference of the compute's return. */
|
|
213
|
+
const typeNode = call.typeArguments?.[0]
|
|
214
|
+
const annotation = typeNode === undefined ? '' : `: ${verbatim(typeNode)}`
|
|
215
|
+
const fn = call.arguments[0]
|
|
216
|
+
if (fn === undefined) {
|
|
217
|
+
return { text: `const ${name} = undefined;`, segments: [] }
|
|
218
|
+
}
|
|
219
|
+
const prefix = `const ${name}${annotation} = (`
|
|
220
|
+
return { text: `${prefix}${verbatim(fn)})();`, segments: [span(fn, prefix.length)] }
|
|
221
|
+
}
|
|
222
|
+
/* prop<T>('key'): Props field `key[?]: T`, scope binding read from props. */
|
|
223
|
+
const key = call.arguments[0]
|
|
224
|
+
const keyText = key !== undefined && ts.isStringLiteralLike(key) ? key.text : name
|
|
225
|
+
const typeNode = call.typeArguments?.[0]
|
|
226
|
+
const typeText = typeNode === undefined ? 'unknown' : verbatim(typeNode)
|
|
227
|
+
const optional = typeNode === undefined || /\bundefined\b/.test(typeText)
|
|
228
|
+
props.push(` ${JSON.stringify(keyText)}${optional ? '?' : ''}: ${typeText}`)
|
|
229
|
+
return { text: `let ${name} = props[${JSON.stringify(keyText)}];`, segments: [] }
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* Emits a template node's expressions into the shadow's `if (false) {…}` render
|
|
233
|
+
body. Control flow introduces its binding so children type-check against it;
|
|
234
|
+
every expression is referenced in a statement so a type error surfaces and maps. */
|
|
235
|
+
function emitNode(node: TemplateNode, builder: Builder): void {
|
|
236
|
+
switch (node.kind) {
|
|
237
|
+
case 'text':
|
|
238
|
+
for (const part of node.parts) {
|
|
239
|
+
if (part.kind === 'expression') {
|
|
240
|
+
builder.stmt(part.code, part.loc)
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return
|
|
244
|
+
case 'element':
|
|
245
|
+
for (const attr of node.attrs) {
|
|
246
|
+
if (attr.kind !== 'static') {
|
|
247
|
+
builder.stmt(attr.code, attr.loc)
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
node.children.forEach((child) => {
|
|
251
|
+
emitNode(child, builder)
|
|
252
|
+
})
|
|
253
|
+
return
|
|
254
|
+
case 'component': {
|
|
255
|
+
/* Check each prop against the child's declared type. The imported tag
|
|
256
|
+
resolves (via the shadow host) to the child's `(props: Props) => …`
|
|
257
|
+
default, so `Parameters<typeof Child>[0]["name"]` is that prop's type;
|
|
258
|
+
assigning the mapped value to it lands a mismatch diagnostic on the
|
|
259
|
+
offending expression (an annotated target reports the error on the RHS,
|
|
260
|
+
unlike an object literal which reports it on the key). */
|
|
261
|
+
for (const prop of node.props) {
|
|
262
|
+
builder.raw(
|
|
263
|
+
`((__prop: Parameters<typeof ${node.name}>[0][${JSON.stringify(prop.name)}]) => {})(`,
|
|
264
|
+
)
|
|
265
|
+
builder.expr(prop.code, prop.loc)
|
|
266
|
+
builder.raw(');\n')
|
|
267
|
+
}
|
|
268
|
+
node.children.forEach((child) => {
|
|
269
|
+
emitNode(child, builder)
|
|
270
|
+
})
|
|
271
|
+
return
|
|
272
|
+
}
|
|
273
|
+
case 'if':
|
|
274
|
+
builder.raw('if ')
|
|
275
|
+
builder.expr(node.condition, node.loc)
|
|
276
|
+
builder.raw(' {\n')
|
|
277
|
+
node.children.forEach((child) => {
|
|
278
|
+
emitNode(child, builder)
|
|
279
|
+
})
|
|
280
|
+
builder.raw('}\n')
|
|
281
|
+
return
|
|
282
|
+
case 'each':
|
|
283
|
+
builder.raw(`for (const ${node.as} of `)
|
|
284
|
+
builder.expr(node.items, node.loc)
|
|
285
|
+
builder.raw(') {\n')
|
|
286
|
+
if (node.key !== undefined) {
|
|
287
|
+
builder.raw(`void (${node.key});\n`)
|
|
288
|
+
}
|
|
289
|
+
node.children.forEach((child) => {
|
|
290
|
+
emitNode(child, builder)
|
|
291
|
+
})
|
|
292
|
+
builder.raw('}\n')
|
|
293
|
+
return
|
|
294
|
+
case 'await':
|
|
295
|
+
builder.raw('{\n')
|
|
296
|
+
builder.raw(node.as !== undefined ? `const ${node.as} = await ` : 'await ')
|
|
297
|
+
builder.expr(node.promise, node.loc)
|
|
298
|
+
builder.raw(';\n')
|
|
299
|
+
node.children.forEach((child) => {
|
|
300
|
+
emitNode(child, builder)
|
|
301
|
+
})
|
|
302
|
+
builder.raw('}\n')
|
|
303
|
+
return
|
|
304
|
+
case 'switch':
|
|
305
|
+
builder.stmt(node.subject, node.loc)
|
|
306
|
+
node.children.forEach((child) => {
|
|
307
|
+
emitNode(child, builder)
|
|
308
|
+
})
|
|
309
|
+
return
|
|
310
|
+
case 'case':
|
|
311
|
+
if (node.match !== undefined) {
|
|
312
|
+
builder.stmt(node.match, node.loc)
|
|
313
|
+
}
|
|
314
|
+
node.children.forEach((child) => {
|
|
315
|
+
emitNode(child, builder)
|
|
316
|
+
})
|
|
317
|
+
return
|
|
318
|
+
case 'branch':
|
|
319
|
+
/* then/catch bind the resolved value / error as `any` so children check. */
|
|
320
|
+
builder.raw('{\n')
|
|
321
|
+
if (node.as !== undefined) {
|
|
322
|
+
builder.raw(`const ${node.as} = undefined as any;\n`)
|
|
323
|
+
}
|
|
324
|
+
node.children.forEach((child) => {
|
|
325
|
+
emitNode(child, builder)
|
|
326
|
+
})
|
|
327
|
+
builder.raw('}\n')
|
|
328
|
+
return
|
|
329
|
+
case 'try':
|
|
330
|
+
builder.raw('{\n')
|
|
331
|
+
node.children.forEach((child) => {
|
|
332
|
+
emitNode(child, builder)
|
|
333
|
+
})
|
|
334
|
+
builder.raw('}\n')
|
|
335
|
+
return
|
|
336
|
+
case 'snippet':
|
|
337
|
+
builder.raw(`const ${node.name} = (${node.params ?? ''}) => {\n`)
|
|
338
|
+
node.children.forEach((child) => {
|
|
339
|
+
emitNode(child, builder)
|
|
340
|
+
})
|
|
341
|
+
builder.raw('};\n')
|
|
342
|
+
return
|
|
343
|
+
case 'script':
|
|
344
|
+
/* A scoped reactive `<script>` block — its body is author TS; emit it so
|
|
345
|
+
its references check. Not yet position-mapped (rare). */
|
|
346
|
+
builder.raw(`{\n${node.code}\n}\n`)
|
|
347
|
+
return
|
|
348
|
+
case 'style':
|
|
349
|
+
/* CSS, not TypeScript — nothing for the shadow to type-check. */
|
|
350
|
+
return
|
|
351
|
+
}
|
|
352
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { resolve } from 'node:path'
|
|
2
|
+
import ts from 'typescript'
|
|
3
|
+
import { assetModulesFile } from './assetModulesFile.ts'
|
|
4
|
+
import { compileShadow } from './compileShadow.ts'
|
|
5
|
+
import { loadShadowTsConfig } from './loadShadowTsConfig.ts'
|
|
6
|
+
import { remapShadowDiagnostic } from './remapShadowDiagnostic.ts'
|
|
7
|
+
import { resolveAbideImports } from './resolveAbideImports.ts'
|
|
8
|
+
import { shadowNaming } from './shadowNaming.ts'
|
|
9
|
+
import { sourceToShadowOffset } from './sourceToShadowOffset.ts'
|
|
10
|
+
import type { AbideDiagnostic } from './types/AbideDiagnostic.ts'
|
|
11
|
+
import type { CompiledShadow } from './types/CompiledShadow.ts'
|
|
12
|
+
|
|
13
|
+
const { suffixed, isShadow, sourceOf } = shadowNaming
|
|
14
|
+
|
|
15
|
+
/* Hover quick-info for a source position: TypeScript's signature line and doc
|
|
16
|
+
comment, with the covered span mapped back onto the `.abide` source. */
|
|
17
|
+
export type ShadowQuickInfo = { text: string; documentation: string; start: number; length: number }
|
|
18
|
+
|
|
19
|
+
export type ShadowLanguageService = {
|
|
20
|
+
/* Record/replace an open document's in-memory text (overrides disk). */
|
|
21
|
+
update: (abidePath: string, text: string) => void
|
|
22
|
+
/* Forget an open document; subsequent reads fall back to disk. */
|
|
23
|
+
close: (abidePath: string) => void
|
|
24
|
+
/* Current diagnostics for one component, mapped onto its source. */
|
|
25
|
+
diagnostics: (abidePath: string) => AbideDiagnostic[]
|
|
26
|
+
/* Hover info at a source offset, or undefined if the offset isn't a checked
|
|
27
|
+
expression (markup, whitespace) or TypeScript has nothing to report. */
|
|
28
|
+
quickInfo: (abidePath: string, sourceOffset: number) => ShadowQuickInfo | undefined
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/*
|
|
32
|
+
An incremental `ts.LanguageService` over the shadow world, for the LSP. Open
|
|
33
|
+
documents are held as in-memory overlays that override disk, so diagnostics
|
|
34
|
+
reflect unsaved edits; every other file (real `.ts`, unopened `.abide`) reads from
|
|
35
|
+
disk. Shares the shadow compiler, module resolver, and tsconfig with the one-shot
|
|
36
|
+
check Program — the editor and CLI report identically.
|
|
37
|
+
*/
|
|
38
|
+
export function createShadowLanguageService(cwd: string): ShadowLanguageService {
|
|
39
|
+
const { options, fileNames } = loadShadowTsConfig(cwd)
|
|
40
|
+
const overlays = new Map<string, string>()
|
|
41
|
+
const versions = new Map<string, number>()
|
|
42
|
+
const shadows = new Map<string, CompiledShadow>()
|
|
43
|
+
const parseErrors = new Map<string, string>()
|
|
44
|
+
|
|
45
|
+
/* Ambient declarations for bundler-handled asset imports (`*.css`, …). */
|
|
46
|
+
const assets = assetModulesFile(cwd)
|
|
47
|
+
|
|
48
|
+
const exists = (abidePath: string): boolean =>
|
|
49
|
+
overlays.has(abidePath) || ts.sys.fileExists(abidePath)
|
|
50
|
+
|
|
51
|
+
/* Compiles (and caches) a component's shadow from its overlay or disk text; a
|
|
52
|
+
template parse error yields a minimal valid module + a recorded message. */
|
|
53
|
+
const shadowText = (abidePath: string): string => {
|
|
54
|
+
const source = overlays.get(abidePath) ?? ts.sys.readFile(abidePath) ?? ''
|
|
55
|
+
try {
|
|
56
|
+
const compiled = compileShadow(source)
|
|
57
|
+
shadows.set(abidePath, compiled)
|
|
58
|
+
parseErrors.delete(abidePath)
|
|
59
|
+
return compiled.code
|
|
60
|
+
} catch (error) {
|
|
61
|
+
shadows.set(abidePath, { code: '', mappings: [] })
|
|
62
|
+
parseErrors.set(abidePath, error instanceof Error ? error.message : String(error))
|
|
63
|
+
return 'export default function (): void {}\n'
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* All component shadows: those on disk plus any opened (possibly unsaved) ones. */
|
|
68
|
+
const shadowNames = (): string[] => {
|
|
69
|
+
const disk = [...new Bun.Glob('**/*.abide').scanSync({ cwd, onlyFiles: true })]
|
|
70
|
+
.filter((relative) => !relative.includes('node_modules'))
|
|
71
|
+
.map((relative) => resolve(cwd, relative))
|
|
72
|
+
return [...new Set([...disk, ...overlays.keys()])].map(suffixed)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const moduleResolutionHost: ts.ModuleResolutionHost = {
|
|
76
|
+
fileExists: (fileName) =>
|
|
77
|
+
fileName === assets.path ||
|
|
78
|
+
(isShadow(fileName) ? exists(sourceOf(fileName)) : ts.sys.fileExists(fileName)),
|
|
79
|
+
readFile: (fileName) =>
|
|
80
|
+
fileName === assets.path
|
|
81
|
+
? assets.content
|
|
82
|
+
: isShadow(fileName)
|
|
83
|
+
? shadowText(sourceOf(fileName))
|
|
84
|
+
: ts.sys.readFile(fileName),
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const host: ts.LanguageServiceHost = {
|
|
88
|
+
getScriptFileNames: () => [assets.path, ...fileNames, ...shadowNames()],
|
|
89
|
+
getScriptVersion: (fileName) => String(versions.get(fileName) ?? 0),
|
|
90
|
+
getScriptSnapshot: (fileName) => {
|
|
91
|
+
if (fileName === assets.path) {
|
|
92
|
+
return ts.ScriptSnapshot.fromString(assets.content)
|
|
93
|
+
}
|
|
94
|
+
if (isShadow(fileName)) {
|
|
95
|
+
return exists(sourceOf(fileName))
|
|
96
|
+
? ts.ScriptSnapshot.fromString(shadowText(sourceOf(fileName)))
|
|
97
|
+
: undefined
|
|
98
|
+
}
|
|
99
|
+
const text = ts.sys.readFile(fileName)
|
|
100
|
+
return text === undefined ? undefined : ts.ScriptSnapshot.fromString(text)
|
|
101
|
+
},
|
|
102
|
+
getCurrentDirectory: () => cwd,
|
|
103
|
+
getCompilationSettings: () => options,
|
|
104
|
+
getDefaultLibFileName: (compilerOptions) => ts.getDefaultLibFilePath(compilerOptions),
|
|
105
|
+
fileExists: moduleResolutionHost.fileExists,
|
|
106
|
+
readFile: moduleResolutionHost.readFile,
|
|
107
|
+
readDirectory: ts.sys.readDirectory,
|
|
108
|
+
directoryExists: ts.sys.directoryExists,
|
|
109
|
+
getDirectories: ts.sys.getDirectories,
|
|
110
|
+
resolveModuleNames: resolveAbideImports(options, moduleResolutionHost),
|
|
111
|
+
}
|
|
112
|
+
const service = ts.createLanguageService(host, ts.createDocumentRegistry())
|
|
113
|
+
|
|
114
|
+
const bump = (abidePath: string): void => {
|
|
115
|
+
const fileName = suffixed(abidePath)
|
|
116
|
+
versions.set(fileName, (versions.get(fileName) ?? 0) + 1)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
update(abidePath, text) {
|
|
121
|
+
overlays.set(abidePath, text)
|
|
122
|
+
bump(abidePath)
|
|
123
|
+
},
|
|
124
|
+
close(abidePath) {
|
|
125
|
+
overlays.delete(abidePath)
|
|
126
|
+
bump(abidePath)
|
|
127
|
+
},
|
|
128
|
+
diagnostics(abidePath) {
|
|
129
|
+
const fileName = suffixed(abidePath)
|
|
130
|
+
/* Fetching diagnostics drives getScriptSnapshot → shadowText, so the
|
|
131
|
+
shadows/parseErrors caches are current before we read them. */
|
|
132
|
+
const raw = [
|
|
133
|
+
...service.getSyntacticDiagnostics(fileName),
|
|
134
|
+
...service.getSemanticDiagnostics(fileName),
|
|
135
|
+
]
|
|
136
|
+
const parseError = parseErrors.get(abidePath)
|
|
137
|
+
if (parseError !== undefined) {
|
|
138
|
+
return [
|
|
139
|
+
{
|
|
140
|
+
file: abidePath,
|
|
141
|
+
start: 0,
|
|
142
|
+
length: 0,
|
|
143
|
+
message: parseError,
|
|
144
|
+
category: ts.DiagnosticCategory.Error,
|
|
145
|
+
},
|
|
146
|
+
]
|
|
147
|
+
}
|
|
148
|
+
const mappings = shadows.get(abidePath)?.mappings ?? []
|
|
149
|
+
return raw.flatMap((diagnostic) => {
|
|
150
|
+
if (diagnostic.start === undefined) {
|
|
151
|
+
return []
|
|
152
|
+
}
|
|
153
|
+
const located = remapShadowDiagnostic(
|
|
154
|
+
mappings,
|
|
155
|
+
diagnostic.start,
|
|
156
|
+
diagnostic.length ?? 0,
|
|
157
|
+
)
|
|
158
|
+
if (located === undefined) {
|
|
159
|
+
return []
|
|
160
|
+
}
|
|
161
|
+
return [
|
|
162
|
+
{
|
|
163
|
+
file: abidePath,
|
|
164
|
+
start: located.start,
|
|
165
|
+
length: located.length,
|
|
166
|
+
message: ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'),
|
|
167
|
+
category: diagnostic.category,
|
|
168
|
+
},
|
|
169
|
+
]
|
|
170
|
+
})
|
|
171
|
+
},
|
|
172
|
+
quickInfo(abidePath, sourceOffset) {
|
|
173
|
+
const fileName = suffixed(abidePath)
|
|
174
|
+
/* Compile the shadow first so its mappings are current before we
|
|
175
|
+
translate the source offset into shadow coordinates. */
|
|
176
|
+
shadowText(abidePath)
|
|
177
|
+
const mappings = shadows.get(abidePath)?.mappings ?? []
|
|
178
|
+
const shadowOffset = sourceToShadowOffset(mappings, sourceOffset)
|
|
179
|
+
if (shadowOffset === undefined) {
|
|
180
|
+
return undefined
|
|
181
|
+
}
|
|
182
|
+
const info = service.getQuickInfoAtPosition(fileName, shadowOffset)
|
|
183
|
+
if (info === undefined) {
|
|
184
|
+
return undefined
|
|
185
|
+
}
|
|
186
|
+
/* The reported span is in shadow coordinates; map it back so the editor
|
|
187
|
+
highlights the matching source. Falls back to the hovered offset. */
|
|
188
|
+
const span = remapShadowDiagnostic(mappings, info.textSpan.start, info.textSpan.length)
|
|
189
|
+
return {
|
|
190
|
+
text: ts.displayPartsToString(info.displayParts),
|
|
191
|
+
documentation: ts.displayPartsToString(info.documentation),
|
|
192
|
+
start: span?.start ?? sourceOffset,
|
|
193
|
+
length: span?.length ?? 1,
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { resolve } from 'node:path'
|
|
2
|
+
import ts from 'typescript'
|
|
3
|
+
import { assetModulesFile } from './assetModulesFile.ts'
|
|
4
|
+
import { compileShadow } from './compileShadow.ts'
|
|
5
|
+
import { loadShadowTsConfig } from './loadShadowTsConfig.ts'
|
|
6
|
+
import { resolveAbideImports } from './resolveAbideImports.ts'
|
|
7
|
+
import { shadowNaming } from './shadowNaming.ts'
|
|
8
|
+
import type { CompiledShadow } from './types/CompiledShadow.ts'
|
|
9
|
+
|
|
10
|
+
const { suffixed, isShadow, sourceOf } = shadowNaming
|
|
11
|
+
|
|
12
|
+
export type ShadowProgram = {
|
|
13
|
+
program: ts.Program
|
|
14
|
+
/* Compiled shadow (code + mappings) per `.abide` path, populated as the host
|
|
15
|
+
materialises each source file; the diagnostic remapper reads the mappings. */
|
|
16
|
+
shadows: Map<string, CompiledShadow>
|
|
17
|
+
/* Files whose template failed to parse — surfaced as a diagnostic at offset 0. */
|
|
18
|
+
parseErrors: Map<string, string>
|
|
19
|
+
abidePaths: string[]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/*
|
|
23
|
+
Builds a `ts.Program` over the project's real `.ts` files plus a virtual shadow
|
|
24
|
+
`.ts` for every `.abide` component, behind a CompilerHost that serves shadow text
|
|
25
|
+
for any `*.abide.ts` name and resolves `.abide` imports to their shadows. Reuses
|
|
26
|
+
the project's tsconfig (lib/paths/baseUrl) so the shadows type-check against the
|
|
27
|
+
same world the app does; `noUnusedLocals`/`noUnusedParameters` are forced off
|
|
28
|
+
because the shadow legitimately declares scope bindings a template may not read.
|
|
29
|
+
*/
|
|
30
|
+
export function createShadowProgram(cwd: string, abidePaths?: string[]): ShadowProgram {
|
|
31
|
+
const { options, fileNames } = loadShadowTsConfig(cwd)
|
|
32
|
+
const shadows = new Map<string, CompiledShadow>()
|
|
33
|
+
const parseErrors = new Map<string, string>()
|
|
34
|
+
/* The components to root the program at — caller-supplied (one project's files)
|
|
35
|
+
or, by default, every `.abide` under `cwd`. Imported components resolve on
|
|
36
|
+
demand through the host, so an explicit subset still type-checks fully. */
|
|
37
|
+
const rootAbidePaths =
|
|
38
|
+
abidePaths ??
|
|
39
|
+
[...new Bun.Glob('**/*.abide').scanSync({ cwd, onlyFiles: true })]
|
|
40
|
+
.filter((relative) => !relative.includes('node_modules'))
|
|
41
|
+
.map((relative) => resolve(cwd, relative))
|
|
42
|
+
|
|
43
|
+
/* Compiles (and caches) a `.abide` file's shadow; a template parse error yields
|
|
44
|
+
a minimal valid module and a recorded message so the program still builds. */
|
|
45
|
+
const shadowText = (abidePath: string): string => {
|
|
46
|
+
const source = ts.sys.readFile(abidePath) ?? ''
|
|
47
|
+
try {
|
|
48
|
+
const compiled = compileShadow(source)
|
|
49
|
+
shadows.set(abidePath, compiled)
|
|
50
|
+
parseErrors.delete(abidePath)
|
|
51
|
+
return compiled.code
|
|
52
|
+
} catch (error) {
|
|
53
|
+
shadows.set(abidePath, { code: '', mappings: [] })
|
|
54
|
+
parseErrors.set(abidePath, error instanceof Error ? error.message : String(error))
|
|
55
|
+
return 'export default function (): void {}\n'
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* Ambient declarations for bundler-handled asset imports (`*.css`, …). */
|
|
60
|
+
const assets = assetModulesFile(cwd)
|
|
61
|
+
|
|
62
|
+
const host = ts.createCompilerHost(options, true)
|
|
63
|
+
const originalGetSourceFile = host.getSourceFile.bind(host)
|
|
64
|
+
host.getSourceFile = (fileName, languageVersionOrOptions, onError, shouldCreate) => {
|
|
65
|
+
if (fileName === assets.path) {
|
|
66
|
+
return ts.createSourceFile(fileName, assets.content, languageVersionOrOptions, true)
|
|
67
|
+
}
|
|
68
|
+
if (isShadow(fileName)) {
|
|
69
|
+
return ts.createSourceFile(
|
|
70
|
+
fileName,
|
|
71
|
+
shadowText(sourceOf(fileName)),
|
|
72
|
+
languageVersionOrOptions,
|
|
73
|
+
true,
|
|
74
|
+
ts.ScriptKind.TS,
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
return originalGetSourceFile(fileName, languageVersionOrOptions, onError, shouldCreate)
|
|
78
|
+
}
|
|
79
|
+
host.fileExists = (fileName) =>
|
|
80
|
+
fileName === assets.path ||
|
|
81
|
+
(isShadow(fileName) ? ts.sys.fileExists(sourceOf(fileName)) : ts.sys.fileExists(fileName))
|
|
82
|
+
host.readFile = (fileName) => {
|
|
83
|
+
if (fileName === assets.path) {
|
|
84
|
+
return assets.content
|
|
85
|
+
}
|
|
86
|
+
return isShadow(fileName) ? shadowText(sourceOf(fileName)) : ts.sys.readFile(fileName)
|
|
87
|
+
}
|
|
88
|
+
host.resolveModuleNames = resolveAbideImports(options, host)
|
|
89
|
+
|
|
90
|
+
const program = ts.createProgram({
|
|
91
|
+
rootNames: [assets.path, ...fileNames, ...rootAbidePaths.map((path) => suffixed(path))],
|
|
92
|
+
options,
|
|
93
|
+
host,
|
|
94
|
+
})
|
|
95
|
+
return { program, shadows, parseErrors, abidePaths: rootAbidePaths }
|
|
96
|
+
}
|