@akanjs/devkit 3.0.0-beta.9 → 3.0.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/DEV_RUNTIME_KNOBS.md +44 -26
- package/README.ko.md +21 -7
- package/README.md +17 -8
- package/abstractDoc.test.ts +6 -15
- package/abstractDoc.ts +6 -8
- package/agentsIndex.test.ts +4 -7
- package/agentsIndex.ts +6 -22
- package/akanApp/BackendImportGraph.test.ts +8 -22
- package/akanApp/BackendImportGraph.ts +1 -20
- package/akanApp/akanApp.host.test.ts +56 -3
- package/akanApp/akanApp.host.ts +218 -278
- package/akanApp/devBootLatch.test.ts +39 -0
- package/akanApp/devBootLatch.ts +39 -0
- package/akanApp/devHostPolicy.test.ts +81 -35
- package/akanApp/devHostPolicy.ts +41 -102
- package/akanConfig/akanBin.test.ts +62 -0
- package/akanConfig/akanBin.ts +78 -0
- package/akanConfig/akanConfig.test.ts +439 -141
- package/akanConfig/akanConfig.ts +359 -275
- package/akanConfig/index.ts +1 -0
- package/akanConfig/types.ts +22 -8
- package/akanContext.ts +110 -160
- package/akanMcpContract.ts +28 -73
- package/appSelectionMemory.ts +1 -4
- package/applicationBuildReporter.ts +37 -5
- package/applicationBuildRunner.test.ts +41 -5
- package/applicationBuildRunner.ts +71 -63
- package/applicationTestPreload.ts +3 -12
- package/artifact/implicitRootLayout.test.ts +32 -30
- package/artifact/implicitRootLayout.ts +58 -106
- package/artifact/routeSeedIndex.test.ts +7 -6
- package/artifact/routeSeedIndex.ts +11 -44
- package/biome.base.json +35 -2
- package/biomeBase.ts +2 -5
- package/cloud/cloudApi.test.ts +202 -0
- package/cloud/cloudApi.ts +77 -39
- package/cloud/configLock.test.ts +64 -0
- package/cloud/configLock.ts +96 -0
- package/cloud/constants.ts +26 -5
- package/cloud/globalConfig.ts +71 -32
- package/codeAgent/agent/AkanCodeServices.ts +97 -0
- package/codeAgent/agent/AkanContextFiles.ts +94 -0
- package/codeAgent/agent/CodeAgent.ts +551 -0
- package/codeAgent/agent/CodeAgentAsks.ts +66 -0
- package/codeAgent/agent/CodeAgentEventMapper.ts +225 -0
- package/codeAgent/agent/CodeAgentGate.ts +93 -0
- package/codeAgent/agent/CodeAgentProxy.ts +82 -0
- package/codeAgent/agent/CodeAgentRpcHost.ts +103 -0
- package/codeAgent/agent/CodeAgentRpcListener.ts +109 -0
- package/codeAgent/agent/CodeAgentStreamPrinter.ts +86 -0
- package/codeAgent/agent/CodeAgentSuspended.ts +85 -0
- package/codeAgent/agent/CodeAgentUi.ts +47 -0
- package/codeAgent/agent/CodeMailbox.ts +148 -0
- package/codeAgent/agent/CodeSessionFork.ts +56 -0
- package/codeAgent/agent/CodeSessionIndex.ts +78 -0
- package/codeAgent/agent/SubagentPool.ts +214 -0
- package/codeAgent/agent/akanCodeModel.ts +47 -0
- package/codeAgent/agent/akanCodePaths.ts +31 -0
- package/codeAgent/agent/akanContextFiles.test.ts +114 -0
- package/codeAgent/agent/akanSystemPrompt.ts +46 -0
- package/codeAgent/agent/codeAgent.test.ts +178 -0
- package/codeAgent/agent/codeAgentContract.test.ts +523 -0
- package/codeAgent/agent/codeAgentProxy.test.ts +74 -0
- package/codeAgent/agent/codeAgentRpcListener.test.ts +113 -0
- package/codeAgent/agent/codeAgentSuspended.test.ts +47 -0
- package/codeAgent/agent/codeMailbox.test.ts +76 -0
- package/codeAgent/agent/codeSessionFork.test.ts +58 -0
- package/codeAgent/agent/codeSessionIndex.test.ts +77 -0
- package/codeAgent/agent/consoleToStderr.ts +8 -0
- package/codeAgent/agent/jsonLines.ts +29 -0
- package/codeAgent/agent/stringArg.ts +5 -0
- package/codeAgent/feedback/DevLogFeedback.ts +82 -0
- package/codeAgent/feedback/PreviewChrome.ts +82 -0
- package/codeAgent/feedback/PreviewFeedback.ts +46 -0
- package/codeAgent/feedback/PreviewView.ts +95 -0
- package/codeAgent/feedback/TurnFeedback.ts +98 -0
- package/codeAgent/feedback/VerifyFeedback.ts +40 -0
- package/codeAgent/feedback/previewChrome.test.ts +73 -0
- package/codeAgent/index.ts +34 -0
- package/codeAgent/skills/akan-code/SKILL.md +130 -0
- package/codeAgent/skills/akan-debug/SKILL.md +42 -0
- package/codeAgent/skills/akan-deps/SKILL.md +50 -0
- package/codeAgent/skills/akan-err/SKILL.md +39 -0
- package/codeAgent/skills/akan-module/SKILL.md +45 -0
- package/codeAgent/skills/akan-scaffold/SKILL.md +76 -0
- package/codeAgent/skills/akan-scalar/SKILL.md +52 -0
- package/codeAgent/skills/akan-service/SKILL.md +34 -0
- package/codeAgent/skills/akan-store/SKILL.md +67 -0
- package/codeAgent/skills/akan-ui/SKILL.md +63 -0
- package/codeAgent/skills/akan-validate/SKILL.md +44 -0
- package/codeAgent/tools/AkanCli.ts +50 -0
- package/codeAgent/tools/AkanCodePlugins.ts +136 -0
- package/codeAgent/tools/AkanEditScope.ts +48 -0
- package/codeAgent/tools/AkanEnvKeys.ts +50 -0
- package/codeAgent/tools/AkanToolPack.ts +92 -0
- package/codeAgent/tools/AkanVerifier.ts +48 -0
- package/codeAgent/tools/AskToolPack.ts +99 -0
- package/codeAgent/tools/MailToolPack.ts +76 -0
- package/codeAgent/tools/McpClient.ts +187 -0
- package/codeAgent/tools/McpOAuth.ts +233 -0
- package/codeAgent/tools/McpOAuthLoopback.ts +64 -0
- package/codeAgent/tools/McpServerConfig.ts +129 -0
- package/codeAgent/tools/McpSignIn.ts +158 -0
- package/codeAgent/tools/McpTokenStore.ts +81 -0
- package/codeAgent/tools/McpToolPack.ts +199 -0
- package/codeAgent/tools/SessionToolPack.ts +91 -0
- package/codeAgent/tools/WebToolPack.ts +147 -0
- package/codeAgent/tools/akanCodePlugins.test.ts +677 -0
- package/codeAgent/tools/askToolPack.test.ts +116 -0
- package/codeAgent/tools/mcpOAuth.test.ts +612 -0
- package/codeAgent/tools/mcpServerConfig.test.ts +212 -0
- package/codegenLock.test.ts +4 -13
- package/codegenLock.ts +3 -17
- package/commandDecorators/argMeta.ts +1 -5
- package/commandDecorators/camelToKebabCase.ts +1 -0
- package/commandDecorators/command.ts +46 -102
- package/commandDecorators/commandBuilder.ts +30 -61
- package/commandDecorators/commandDecorators.test.ts +2 -10
- package/commandDecorators/dependencyBuilder.ts +10 -1
- package/commandDecorators/helpFormatter.ts +68 -145
- package/commandDecorators/index.ts +0 -2
- package/commandDecorators/targetMeta.ts +9 -7
- package/createTunnel.ts +7 -14
- package/csrE2e/csrE2eHarness.fixture.ts +505 -0
- package/csrE2e/csrFastRefresh.e2e.test.ts +233 -0
- package/csrE2e/csrPageStack.e2e.test.ts +252 -0
- package/csrE2e/csrPatchFirst.e2e.test.ts +87 -0
- package/csrE2e/csrRootLayout.e2e.test.ts +134 -0
- package/csrE2e/devHmrLatency.e2e.test.ts +391 -0
- package/csrE2e/registryVendor.e2e.test.ts +221 -0
- package/csrE2e/ssrRegistry.e2e.test.ts +644 -0
- package/dependencyScanner.test.ts +4 -16
- package/dependencyScanner.ts +31 -80
- package/devkitUtils.test.ts +48 -20
- package/executors.linkFallback.test.ts +103 -0
- package/executors.test.ts +176 -106
- package/executors.ts +212 -498
- package/fileEditor.ts +9 -37
- package/{getDirname.test.ts → fileSys.test.ts} +5 -3
- package/fileSys.ts +22 -9
- package/formSetterScanner.ts +3 -16
- package/frontendBuild/allRoutesBuilder.ts +7 -21
- package/frontendBuild/autoImportSync.test.ts +27 -38
- package/frontendBuild/autoImportSync.ts +31 -65
- package/frontendBuild/buildRouteClient.test.ts +123 -37
- package/frontendBuild/bundleDefine.test.ts +12 -0
- package/frontendBuild/bundleDefine.ts +16 -0
- package/frontendBuild/clientBuildTypes.ts +5 -36
- package/frontendBuild/clientEntriesBundler.ts +9 -40
- package/frontendBuild/clientEntryDiscovery.test.ts +112 -0
- package/frontendBuild/clientEntryDiscovery.ts +122 -110
- package/frontendBuild/csrArtifactBuilder.ts +9 -31
- package/frontendBuild/csrDevArtifactWriter.test.ts +151 -0
- package/frontendBuild/csrDevArtifactWriter.ts +307 -0
- package/frontendBuild/csrDevBundler.test.ts +66 -0
- package/frontendBuild/csrDevBundler.ts +95 -0
- package/frontendBuild/csrDevFactoryCheck.test.ts +33 -0
- package/frontendBuild/csrDevFactoryCheck.ts +39 -0
- package/frontendBuild/csrDevModuleCompiler.test.ts +293 -0
- package/frontendBuild/csrDevModuleCompiler.ts +422 -0
- package/frontendBuild/csrDevPatcher.test.ts +572 -0
- package/frontendBuild/csrDevPatcher.ts +548 -0
- package/frontendBuild/csrDevPaths.ts +145 -0
- package/frontendBuild/csrDevResolver.test.ts +239 -0
- package/frontendBuild/csrDevResolver.ts +363 -0
- package/frontendBuild/csrDevTypes.ts +61 -0
- package/frontendBuild/csrEntryFiles.ts +24 -0
- package/frontendBuild/cssCandidateCache.test.ts +0 -6
- package/frontendBuild/cssCandidateCache.ts +6 -41
- package/frontendBuild/cssCompiler.ts +31 -82
- package/frontendBuild/cssImportResolver.ts +6 -19
- package/frontendBuild/devChangePlanner.ts +7 -10
- package/frontendBuild/devGeneratedIndexSync.test.ts +12 -27
- package/frontendBuild/devGeneratedIndexSync.ts +1 -4
- package/frontendBuild/devRegistryBundler.ts +192 -0
- package/frontendBuild/fontOptimizer.test.ts +6 -14
- package/frontendBuild/fontOptimizer.ts +15 -72
- package/frontendBuild/fontPruner.test.ts +5 -10
- package/frontendBuild/fontPruner.ts +5 -20
- package/frontendBuild/frontendBuild.test.ts +57 -70
- package/frontendBuild/hmrChangeClassifier.ts +2 -2
- package/frontendBuild/hmrWatcher.test.ts +59 -32
- package/frontendBuild/hmrWatcher.ts +31 -74
- package/frontendBuild/index.ts +4 -0
- package/frontendBuild/pagesBundleBuilder.ts +28 -30
- package/frontendBuild/pagesEntrySourceGenerator.ts +56 -6
- package/frontendBuild/precompressArtifacts.ts +5 -18
- package/frontendBuild/registryMetadataFingerprint.test.ts +63 -0
- package/frontendBuild/registryMetadataFingerprint.ts +28 -0
- package/frontendBuild/routeClientBuilder.ts +100 -62
- package/frontendBuild/routesManifestArtifactSerializer.ts +5 -36
- package/frontendBuild/serverGraphFile.test.ts +77 -0
- package/frontendBuild/serverGraphFile.ts +93 -0
- package/frontendBuild/sourceMtimeIndex.test.ts +41 -97
- package/frontendBuild/sourceMtimeIndex.ts +19 -130
- package/frontendBuild/ssrBaseArtifactBuilder.ts +19 -13
- package/frontendBuild/ssrDevBundler.ts +85 -0
- package/frontendBuild/styleContract.ts +1 -8
- package/frontendBuild/themeValidator.ts +4 -26
- package/frontendBuild/watchRootResolver.test.ts +23 -42
- package/frontendBuild/watchRootResolver.ts +5 -27
- package/getRelatedCnsts.ts +12 -92
- package/incrementalBuilder/buildBatch.proc.ts +147 -63
- package/incrementalBuilder/buildBatchProtocol.ts +13 -38
- package/incrementalBuilder/buildBatchRunner.test.ts +65 -0
- package/incrementalBuilder/buildBatchRunner.ts +26 -38
- package/incrementalBuilder/builderChannel.test.ts +24 -49
- package/incrementalBuilder/builderChannel.ts +6 -37
- package/incrementalBuilder/builderRequestRouter.test.ts +0 -8
- package/incrementalBuilder/builderRequestRouter.ts +5 -24
- package/incrementalBuilder/builderWorkQueue.test.ts +94 -0
- package/incrementalBuilder/builderWorkQueue.ts +126 -0
- package/incrementalBuilder/devWatchBatch.test.ts +21 -24
- package/incrementalBuilder/incrementalBuilder.host.test.ts +53 -56
- package/incrementalBuilder/incrementalBuilder.host.ts +68 -81
- package/incrementalBuilder/incrementalBuilder.proc.test.ts +270 -0
- package/incrementalBuilder/incrementalBuilder.proc.ts +596 -208
- package/index.ts +5 -19
- package/integration/devResourceProbe.ts +50 -56
- package/integration/devStability.integration.test.ts +105 -199
- package/integration/devStabilityHarness.test.ts +13 -28
- package/integration/devStabilityHarness.ts +65 -330
- package/integration/ssrMemoryProbe.ts +41 -167
- package/libSource.test.ts +4 -15
- package/libSource.ts +19 -31
- package/lint/__fixtures__/no-document-cookie/bad.tsx +4 -0
- package/lint/__fixtures__/no-document-cookie/good.tsx +7 -0
- package/lint/__fixtures__/no-web-only-api-outside-webkit/bad.tsx +7 -0
- package/lint/__fixtures__/no-web-only-api-outside-webkit/good.tsx +11 -0
- package/lint/__fixtures__/no-web-storage/bad.tsx +6 -0
- package/lint/__fixtures__/no-web-storage/good.tsx +7 -0
- package/lint/gritRules.test.ts +8 -24
- package/lint/no-deep-internal-import.grit +2 -2
- package/lint/no-document-cookie.grit +29 -0
- package/lint/no-redeclare-predefined-endpoint.grit +2 -1
- package/lint/no-web-only-api-outside-webkit.grit +44 -0
- package/lint/no-web-storage.grit +30 -0
- package/linter.test.ts +1 -2
- package/linter.ts +26 -116
- package/mobile/desktopBin.test.ts +182 -0
- package/mobile/desktopBin.ts +144 -0
- package/mobile/desktopServerStage.test.ts +252 -0
- package/mobile/desktopServerStage.ts +178 -0
- package/mobile/index.ts +9 -0
- package/mobile/mobileTarget.test.ts +57 -8
- package/mobile/mobileTarget.ts +45 -26
- package/mobile/nativeApi.test.ts +30 -0
- package/mobile/nativeApi.ts +40 -0
- package/mobile/nativeApp.test.ts +184 -0
- package/mobile/nativeApp.ts +454 -0
- package/mobile/nativeAppLine.test.ts +69 -0
- package/mobile/nativeAppLine.ts +71 -0
- package/mobile/nativeConfig.test.ts +438 -0
- package/mobile/nativeConfig.ts +294 -0
- package/mobile/nativePluginFolders.test.ts +81 -0
- package/mobile/nativePluginFolders.ts +60 -0
- package/mobile/nativeWebDir.test.ts +80 -0
- package/mobile/nativeWebDir.ts +66 -0
- package/mobile/placeholderAppId.test.ts +13 -0
- package/mobile/placeholderAppId.ts +16 -0
- package/mobile/usageDescriptions.test.ts +27 -0
- package/mobile/usageDescriptions.ts +24 -0
- package/package.json +11 -11
- package/packageExports.test.ts +3 -18
- package/packageExportsMap.ts +3 -19
- package/piDependencyPins.test.ts +46 -0
- package/platformTest/BunTestReport.test.ts +60 -0
- package/platformTest/BunTestReport.ts +65 -0
- package/platformTest/IdleWatchedProcess.test.ts +56 -0
- package/platformTest/IdleWatchedProcess.ts +73 -0
- package/platformTest/LinuxDockerTarget.ts +105 -0
- package/platformTest/PlatformTestRun.test.ts +64 -0
- package/platformTest/PlatformTestRun.ts +141 -0
- package/platformTest/PlatformTestTarget.ts +142 -0
- package/platformTest/TestSnapshot.test.ts +72 -0
- package/platformTest/TestSnapshot.ts +103 -0
- package/platformTest/WindowsSshTarget.ts +151 -0
- package/prompter.ts +3 -13
- package/qualityScanner.test.ts +9 -26
- package/qualityScanner.ts +200 -286
- package/recipeScanner.test.ts +7 -12
- package/recipeScanner.ts +9 -38
- package/repoIdentity.ts +2 -9
- package/routeSourceValidator.test.ts +0 -1
- package/routeSourceValidator.ts +7 -42
- package/scanInfo.ts +91 -225
- package/selectModel.ts +1 -2
- package/semver.ts +3 -5
- package/slicePlanner.test.ts +6 -24
- package/slicePlanner.ts +11 -32
- package/spinner.test.ts +3 -8
- package/spinner.ts +4 -16
- package/sshTunnel.ts +1 -6
- package/ssrScanner.test.ts +1 -6
- package/ssrScanner.ts +103 -113
- package/stripAnsi.ts +3 -0
- package/subspace.test.ts +9 -25
- package/subspace.ts +48 -115
- package/subspaceConfig.ts +3 -11
- package/testHelpers.ts +194 -0
- package/transforms/asyncDefaultExportDetector.ts +3 -11
- package/transforms/barrelAnalyzer.ts +43 -135
- package/transforms/barrelImportsPlugin.ts +38 -175
- package/transforms/externalizeFrameworkPlugin.test.ts +39 -118
- package/transforms/externalizeFrameworkPlugin.ts +13 -87
- package/transforms/moduleSyntax.ts +32 -0
- package/transforms/rscUseClientTransform.ts +42 -20
- package/transforms/serverEnvPlugin.test.ts +103 -0
- package/transforms/serverEnvPlugin.ts +41 -0
- package/transforms/transforms.test.ts +166 -44
- package/transforms/tsconfigPackageResolver.test.ts +6 -24
- package/transforms/useClientBundlePlugin.ts +6 -23
- package/typeChecker.ts +7 -63
- package/types.ts +2 -0
- package/uploadRelease.ts +10 -25
- package/workflow/artifacts.ts +18 -0
- package/workflow/executor.test.ts +1 -12
- package/workflow/executor.ts +101 -110
- package/workflow/index.ts +0 -1
- package/workflow/moduleIndex.test.ts +4 -15
- package/workflow/moduleIndex.ts +9 -49
- package/workflow/plan.ts +18 -25
- package/workflow/render.ts +80 -96
- package/workflow/rolloutGate.test.ts +1 -1
- package/workflow/source.ts +52 -69
- package/workflow/sourceAst.ts +50 -0
- package/workspaceLayout.test.ts +18 -3
- package/workspaceLayout.ts +22 -23
- package/abstractCompactor.ts +0 -112
- package/aiEditor.test.ts +0 -60
- package/aiEditor.ts +0 -387
- package/applicationReleasePackager.ts +0 -209
- package/capacitorApp.test.ts +0 -372
- package/capacitorApp.ts +0 -1648
- package/commandDecorators/commandMeta.ts +0 -7
- package/commandDecorators/types.ts +0 -10
- package/getDirname.ts +0 -4
- package/guideline.ts +0 -19
- package/local/optimize-resource/ipcprobe/child.ts +0 -1
- package/local/optimize-resource/ipcprobe/parent.ts +0 -11
- package/workflow/primitive.ts +0 -59
|
@@ -2,64 +2,19 @@ import path from "node:path";
|
|
|
2
2
|
import type { BunPlugin } from "bun";
|
|
3
3
|
import type { App } from "../commandDecorators";
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
* Keep framework/runtime singletons external while bundling authored
|
|
7
|
-
* workspace sources and ordinary npm dependencies into the pages bundle.
|
|
8
|
-
*
|
|
9
|
-
* Why a plugin instead of `Bun.build({ external })` or
|
|
10
|
-
* `Bun.build({ packages: "external" })`:
|
|
11
|
-
* - `packages: "external"` externalizes every bare specifier, including
|
|
12
|
-
* `@apps/*` / `@libs/*` — workspace packages that must stay bundled
|
|
13
|
-
* so the `"use client"` plugin can rewrite their exports — and ordinary
|
|
14
|
-
* npm dependencies like `dayjs` / `tailwind-merge`, which the production runtime
|
|
15
|
-
* package.json does not install for the SSR pages artifact.
|
|
16
|
-
* - Top-level `external: [...]` applies to macro-time module resolution
|
|
17
|
-
* too; any `with { type: "macro" }` import chain that transitively
|
|
18
|
-
* resolves `react` during build then fails with `ENOENT "react"`.
|
|
19
|
-
*
|
|
20
|
-
* An `onResolve` plugin only participates in the output-graph walk, so
|
|
21
|
-
* macros evaluate against the real modules in node_modules while the
|
|
22
|
-
* emitted bundle keeps bare specifiers only for runtime singletons that
|
|
23
|
-
* are installed in the generated production package.json.
|
|
24
|
-
*
|
|
25
|
-
* Externalization rules (applied in order):
|
|
26
|
-
* 1. Relative specifiers (`./`, `../`) are NEVER externalized — they
|
|
27
|
-
* always resolve to the current package's source tree and must be
|
|
28
|
-
* inlined so transitive `"use client"` stubs work.
|
|
29
|
-
* 2. Specifiers listed in `include` (workspace aliases like
|
|
30
|
-
* `@apps/*`, `@libs/*`) are NEVER externalized.
|
|
31
|
-
* 3. React / RSC runtime packages and build-time tooling packages are
|
|
32
|
-
* externalized so the runtime supplies a single shared instance.
|
|
33
|
-
* 4. Other bare npm packages are bundled so the Docker runtime does not
|
|
34
|
-
* need to install every transitive page dependency separately.
|
|
35
|
-
*/
|
|
36
5
|
export interface ExternalizeFrameworkOptions {
|
|
37
6
|
app: App;
|
|
38
|
-
/**
|
|
39
|
-
* Prefixes that should be BUNDLED (kept internal). Defaults to
|
|
40
|
-
* `@apps/` and `@libs/` which are the workspace aliases used by the
|
|
41
|
-
* framework's app template.
|
|
42
|
-
*/
|
|
7
|
+
/** Prefixes to bundle rather than externalize; defaults to `akanjs/`, `@apps/`, `@libs/`. */
|
|
43
8
|
include?: string[];
|
|
44
|
-
/**
|
|
45
|
-
* Extra bare specifiers to force-externalize beyond the allowlist
|
|
46
|
-
* defaults. Not commonly needed since the default rule already
|
|
47
|
-
* externalizes every non-workspace specifier.
|
|
48
|
-
*/
|
|
9
|
+
/** Bare specifiers to force-externalize, overriding `include`. */
|
|
49
10
|
extra?: string[];
|
|
50
11
|
}
|
|
51
12
|
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
// the RSC worker would resolve them at runtime — skipping the
|
|
55
|
-
// transforms entirely, which re-introduces the react-dom/client
|
|
56
|
-
// side-effects we wanted to strip from the server graph.
|
|
13
|
+
// Bundled so they pass the barrel and `"use client"` transforms; resolved at runtime they would drag
|
|
14
|
+
// react-dom/client side effects into the server graph.
|
|
57
15
|
const DEFAULT_INCLUDE = ["akanjs/", "@apps/", "@libs/"];
|
|
58
16
|
|
|
59
|
-
//
|
|
60
|
-
// otherwise bundle them. These are runtime hosts or shared singleton
|
|
61
|
-
// framework packages; ordinary npm dependencies intentionally fall through
|
|
62
|
-
// to Bun's resolver and get bundled.
|
|
17
|
+
// Runtime hosts and singleton framework packages stay external; ordinary npm dependencies are bundled.
|
|
63
18
|
const DEFAULT_EXCLUDE_EXACT = new Set<string>(["akanjs/webkit", "@akanjs/cli", "@akanjs/devkit"]);
|
|
64
19
|
const DEFAULT_EXCLUDE_PREFIX = ["@akanjs/cli/", "@akanjs/devkit/"];
|
|
65
20
|
const OPTIONAL_BACKEND_EXTERNAL_EXACT = new Set<string>([
|
|
@@ -81,19 +36,17 @@ const RUNTIME_EXTERNAL_EXACT = new Set<string>([
|
|
|
81
36
|
]);
|
|
82
37
|
const RUNTIME_EXTERNAL_PREFIX = ["react-dom/", "react-server-dom-webpack/"];
|
|
83
38
|
|
|
84
|
-
//
|
|
85
|
-
// extensionless path. Ordered to prefer TS over JS, since workspace
|
|
86
|
-
// sources are authored in TypeScript.
|
|
39
|
+
// TS before JS: workspace sources are authored in TypeScript.
|
|
87
40
|
const CANDIDATE_EXTS = [".tsx", ".ts", ".jsx", ".js", ".mjs", ".cjs"];
|
|
88
41
|
|
|
42
|
+
// Rejected `Bun.build` options: `external` also applies at macro time (ENOENT "react"), and `packages: "external"`
|
|
43
|
+
// externalizes workspace and npm packages the runtime does not install. onResolve shapes only the output graph.
|
|
89
44
|
export async function createExternalizeFrameworkPlugin(options: ExternalizeFrameworkOptions): Promise<BunPlugin> {
|
|
90
45
|
const tsconfig = await options.app.getTsConfig();
|
|
91
46
|
const includePrefixes = options.include ?? DEFAULT_INCLUDE;
|
|
92
47
|
const extraExact = new Set(options.extra ?? []);
|
|
93
48
|
const workspaceRoot = options.app.workspace.workspaceRoot;
|
|
94
49
|
const tsconfigPaths = tsconfig.compilerOptions.paths ?? {};
|
|
95
|
-
// Pre-compute the tsconfig root-only entries (`akanjs/client` → …)
|
|
96
|
-
// so we can resolve their subpaths by concatenating the remainder.
|
|
97
50
|
const rootEntries = Object.entries(tsconfigPaths)
|
|
98
51
|
.filter(([k]) => !k.endsWith("/*"))
|
|
99
52
|
.map(([k, v]) => ({ pkg: k, entryFile: v[0] ?? null }))
|
|
@@ -105,7 +58,6 @@ export async function createExternalizeFrameworkPlugin(options: ExternalizeFrame
|
|
|
105
58
|
|
|
106
59
|
async function resolveWorkspaceSubpath(spec: string): Promise<string | null> {
|
|
107
60
|
if (!workspaceRoot) return null;
|
|
108
|
-
// Wildcard `@apps/*` / `@libs/*` → direct filesystem path.
|
|
109
61
|
for (const { prefix, replacements } of wildcardEntries) {
|
|
110
62
|
if (!spec.startsWith(prefix)) continue;
|
|
111
63
|
const suffix = spec.slice(prefix.length);
|
|
@@ -117,12 +69,8 @@ export async function createExternalizeFrameworkPlugin(options: ExternalizeFrame
|
|
|
117
69
|
if (hit) return hit;
|
|
118
70
|
}
|
|
119
71
|
}
|
|
120
|
-
// Root-only tsconfig entries — resolve the subpath against the
|
|
121
|
-
// package directory. E.g. `akanjs/client` → `pkgs/akanjs/client/index.ts`
|
|
122
|
-
// so `akanjs/client/cookie` → `pkgs/akanjs/client/cookie`.
|
|
123
72
|
for (const { pkg, entryFile } of rootEntries) {
|
|
124
|
-
if (
|
|
125
|
-
if (spec === pkg) continue; // the package root itself — let Bun's tsconfig resolver handle it
|
|
73
|
+
if (!spec.startsWith(`${pkg}/`)) continue;
|
|
126
74
|
const suffix = spec.slice(pkg.length + 1);
|
|
127
75
|
const pkgDir = path.dirname(path.resolve(workspaceRoot, entryFile));
|
|
128
76
|
const candidate = path.join(pkgDir, suffix);
|
|
@@ -137,13 +85,8 @@ export async function createExternalizeFrameworkPlugin(options: ExternalizeFrame
|
|
|
137
85
|
setup(build) {
|
|
138
86
|
build.onResolve({ filter: /.*/ }, async (args) => {
|
|
139
87
|
const spec = args.path;
|
|
140
|
-
// Relative imports always stay inlined — they anchor to the
|
|
141
|
-
// current package's source tree and cannot leak cross-package.
|
|
142
|
-
// `.` and `..` (without trailing slash) are valid relative
|
|
143
|
-
// specifiers too (equivalent to `./index` / `../index`).
|
|
144
88
|
if (spec === "." || spec === ".." || spec.startsWith("./") || spec.startsWith("../") || spec.startsWith("/"))
|
|
145
89
|
return undefined;
|
|
146
|
-
// Force-external first (overrides the include allowlist).
|
|
147
90
|
if (extraExact.has(spec) || DEFAULT_EXCLUDE_EXACT.has(spec)) return { path: spec, external: true };
|
|
148
91
|
for (const prefix of DEFAULT_EXCLUDE_PREFIX) {
|
|
149
92
|
if (spec.startsWith(prefix)) return { path: spec, external: true };
|
|
@@ -153,38 +96,21 @@ export async function createExternalizeFrameworkPlugin(options: ExternalizeFrame
|
|
|
153
96
|
for (const prefix of RUNTIME_EXTERNAL_PREFIX) {
|
|
154
97
|
if (spec.startsWith(prefix)) return { path: spec, external: true };
|
|
155
98
|
}
|
|
156
|
-
//
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
// tsconfig-paths entry exists for `akanjs/client`) and get
|
|
161
|
-
// silently left as externals.
|
|
162
|
-
for (const prefix of includePrefixes) {
|
|
163
|
-
if (!spec.startsWith(prefix)) continue;
|
|
164
|
-
const resolved = await resolveWorkspaceSubpath(spec);
|
|
165
|
-
if (resolved) return { path: resolved };
|
|
166
|
-
// Fall through to let Bun attempt its own resolution for the
|
|
167
|
-
// package root (`akanjs/client`) — tsconfig `paths` handles
|
|
168
|
-
// that case directly.
|
|
169
|
-
return undefined;
|
|
170
|
-
}
|
|
171
|
-
// Everything else (ordinary npm dependencies like dayjs / tailwind-merge /
|
|
172
|
-
// immer) is bundled into the pages artifact.
|
|
173
|
-
return undefined;
|
|
99
|
+
// Bun resolves only root tsconfig `paths`, so a rewritten `akanjs/client/cookie` would silently stay external.
|
|
100
|
+
if (!includePrefixes.some((prefix) => spec.startsWith(prefix))) return undefined;
|
|
101
|
+
const resolved = await resolveWorkspaceSubpath(spec);
|
|
102
|
+
return resolved ? { path: resolved } : undefined;
|
|
174
103
|
});
|
|
175
104
|
},
|
|
176
105
|
};
|
|
177
106
|
}
|
|
178
107
|
|
|
179
108
|
async function firstExisting(basePath: string): Promise<string | null> {
|
|
180
|
-
// Direct file with its own extension.
|
|
181
109
|
if (await Bun.file(basePath).exists()) return basePath;
|
|
182
|
-
// Try each candidate extension.
|
|
183
110
|
for (const ext of CANDIDATE_EXTS) {
|
|
184
111
|
const candidate = `${basePath}${ext}`;
|
|
185
112
|
if (await Bun.file(candidate).exists()) return candidate;
|
|
186
113
|
}
|
|
187
|
-
// Directory with `index.<ext>` inside.
|
|
188
114
|
for (const ext of CANDIDATE_EXTS) {
|
|
189
115
|
const candidate = path.join(basePath, `index${ext}`);
|
|
190
116
|
if (await Bun.file(candidate).exists()) return candidate;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface NamedItem {
|
|
2
|
+
imported: string;
|
|
3
|
+
local: string;
|
|
4
|
+
isType: boolean;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const loaderFor = (absPath: string): "ts" | "tsx" | "js" | "jsx" => {
|
|
8
|
+
if (absPath.endsWith(".tsx")) return "tsx";
|
|
9
|
+
if (absPath.endsWith(".jsx")) return "jsx";
|
|
10
|
+
if (absPath.endsWith(".ts")) return "ts";
|
|
11
|
+
return "js";
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const parseNamedList = (listBody: string): NamedItem[] => {
|
|
15
|
+
const out: NamedItem[] = [];
|
|
16
|
+
for (const raw of listBody.split(",")) {
|
|
17
|
+
let rest = raw.trim();
|
|
18
|
+
if (!rest) continue;
|
|
19
|
+
let isType = false;
|
|
20
|
+
if (rest.startsWith("type ")) {
|
|
21
|
+
isType = true;
|
|
22
|
+
rest = rest.slice(5).trim();
|
|
23
|
+
}
|
|
24
|
+
const asMatch = /^(\w+)\s+as\s+(\w+)$/.exec(rest);
|
|
25
|
+
if (asMatch) {
|
|
26
|
+
out.push({ imported: asMatch[1] ?? "", local: asMatch[2] ?? "", isType });
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (/^\w+$/.test(rest)) out.push({ imported: rest, local: rest, isType });
|
|
30
|
+
}
|
|
31
|
+
return out;
|
|
32
|
+
};
|
|
@@ -1,35 +1,64 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import ts from "typescript";
|
|
3
|
+
import { loaderFor } from "./moduleSyntax";
|
|
2
4
|
|
|
3
|
-
// Pure "use client" transform: when `source` starts with the `"use client"`
|
|
4
|
-
// directive, replace its exports with `registerClientReference` stubs so the
|
|
5
|
-
// RSC renderer can serialize them as client component references instead of
|
|
6
|
-
// trying to run them on the server.
|
|
7
|
-
|
|
8
|
-
// Matches `"use client"` or `'use client'` at the start of a file,
|
|
9
|
-
// optionally after leading whitespace and JS comments.
|
|
10
5
|
const USE_CLIENT_RE = /^\s*(?:\/\*[\s\S]*?\*\/\s*|\/\/[^\n]*\n\s*)*["']use client["']/;
|
|
11
6
|
const IMPLICIT_ROOT_LAYOUT_RE =
|
|
12
7
|
/[/\\]\.akan[/\\]generated[/\\](?:implicit-root-layout|root-layouts[/\\].*__root_layout)\.(tsx|ts|jsx|js)$/;
|
|
8
|
+
const STAR_REEXPORT_HINT_RE = /\bexport\s*\*\s*from\b/;
|
|
13
9
|
|
|
14
10
|
export interface UseClientTransformArgs {
|
|
15
11
|
path: string;
|
|
16
12
|
workspaceRoot?: string;
|
|
13
|
+
/** Told each `"use client"` module's export names, the only part of it the server graph holds. */
|
|
14
|
+
onClientModule?: (path: string, exports: string[]) => void;
|
|
17
15
|
}
|
|
18
16
|
|
|
17
|
+
export const hasUseClientDirective = (source: string): boolean => USE_CLIENT_RE.test(source);
|
|
18
|
+
|
|
19
19
|
export function toClientReferencePath(absPath: string, workspaceRoot: string): string {
|
|
20
20
|
return path.relative(path.resolve(workspaceRoot), path.resolve(absPath)).split(path.sep).join("/");
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
const starReexportSpecifiers = (source: string, filePath: string) =>
|
|
24
|
+
ts
|
|
25
|
+
.createSourceFile(filePath, source, ts.ScriptTarget.Latest)
|
|
26
|
+
.statements.filter(
|
|
27
|
+
(statement): statement is ts.ExportDeclaration & { moduleSpecifier: ts.StringLiteral } =>
|
|
28
|
+
ts.isExportDeclaration(statement) &&
|
|
29
|
+
!statement.exportClause &&
|
|
30
|
+
!statement.isTypeOnly &&
|
|
31
|
+
!!statement.moduleSpecifier &&
|
|
32
|
+
ts.isStringLiteral(statement.moduleSpecifier),
|
|
33
|
+
)
|
|
34
|
+
.map((statement) => statement.moduleSpecifier.text);
|
|
35
|
+
|
|
36
|
+
// `Bun.Transpiler.scan` reports no name through `export *`, so the server could register none of a star's names as
|
|
37
|
+
// client references; they would be undefined in a server component. Refused as Next.js refuses it.
|
|
38
|
+
export const scanUseClientExports = (source: string, filePath: string, workspaceRoot?: string): string[] => {
|
|
39
|
+
const stars =
|
|
40
|
+
USE_CLIENT_RE.test(source) && STAR_REEXPORT_HINT_RE.test(source) ? starReexportSpecifiers(source, filePath) : [];
|
|
41
|
+
if (stars.length) {
|
|
42
|
+
const file = workspaceRoot ? toClientReferencePath(filePath, workspaceRoot) : filePath;
|
|
43
|
+
const listed = stars.slice(0, 3).map((specifier) => `\`export * from ${JSON.stringify(specifier)}\``);
|
|
44
|
+
const written = stars.length > 3 ? `${listed.join(", ")} (and ${stars.length - 3} more)` : listed.join(", ");
|
|
45
|
+
throw new Error(
|
|
46
|
+
`${file} is a "use client" module, so it cannot ${written}: the server sees only the names a client module ` +
|
|
47
|
+
`declares, and a star re-export declares none. Re-export them by name — \`export { … } from ${JSON.stringify(stars[0])}\`.`,
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
return new Bun.Transpiler({ loader: loaderFor(filePath) }).scan(source).exports;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/** `null` unless `source` is a `"use client"` module; the stub source is valid under loader `"ts"`. */
|
|
27
54
|
export function transformUseClient(source: string, args: UseClientTransformArgs): string | null {
|
|
28
55
|
if (!USE_CLIENT_RE.test(source)) return null;
|
|
29
56
|
if (IMPLICIT_ROOT_LAYOUT_RE.test(args.path)) return null;
|
|
30
|
-
const
|
|
31
|
-
|
|
57
|
+
const exports = scanUseClientExports(source, args.path, args.workspaceRoot);
|
|
58
|
+
//? Not reported without exports: the module then stays in the server bundle as it is, so a save of it changes what
|
|
59
|
+
//? the server runs, which a client module's edit that kept its export names does not.
|
|
32
60
|
if (exports.length === 0) return null;
|
|
61
|
+
args.onClientModule?.(args.path, exports);
|
|
33
62
|
|
|
34
63
|
const referencePath = args.workspaceRoot ? toClientReferencePath(args.path, args.workspaceRoot) : args.path;
|
|
35
64
|
const filePathLit = JSON.stringify(referencePath);
|
|
@@ -50,10 +79,3 @@ export function transformUseClient(source: string, args: UseClientTransformArgs)
|
|
|
50
79
|
|
|
51
80
|
return lines.join("\n");
|
|
52
81
|
}
|
|
53
|
-
|
|
54
|
-
function loaderFor(absPath: string): "ts" | "tsx" | "js" | "jsx" {
|
|
55
|
-
if (absPath.endsWith(".tsx")) return "tsx";
|
|
56
|
-
if (absPath.endsWith(".jsx")) return "jsx";
|
|
57
|
-
if (absPath.endsWith(".ts")) return "ts";
|
|
58
|
-
return "js";
|
|
59
|
-
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { tempDirs, writeText as write } from "../testHelpers";
|
|
4
|
+
import { createServerEnvPlugin } from "./serverEnvPlugin";
|
|
5
|
+
|
|
6
|
+
const makeTempRoot = tempDirs("akan-devkit-server-env-");
|
|
7
|
+
const environments = ["local", "testing", "debug", "develop", "main", "qa"];
|
|
8
|
+
|
|
9
|
+
const writeApp = async (root: string) => {
|
|
10
|
+
const envDir = path.join(root, "apps/demo/env");
|
|
11
|
+
await write(
|
|
12
|
+
path.join(envDir, "env.server.ts"),
|
|
13
|
+
[
|
|
14
|
+
...environments.map((name) => `import { env as ${name} } from "./env.server.${name}";`),
|
|
15
|
+
`const envConfigs = { ${environments.join(", ")} };`,
|
|
16
|
+
'const currentEnv = process.env.AKAN_PUBLIC_ENV ?? "local";',
|
|
17
|
+
"export const env = envConfigs[currentEnv as keyof typeof envConfigs];",
|
|
18
|
+
"",
|
|
19
|
+
].join("\n"),
|
|
20
|
+
);
|
|
21
|
+
await write(path.join(envDir, "env.server.type.ts"), 'export const libEnv = { shared: "shared-value" };\n');
|
|
22
|
+
for (const name of environments)
|
|
23
|
+
await write(
|
|
24
|
+
path.join(envDir, `env.server.${name}.ts`),
|
|
25
|
+
[
|
|
26
|
+
'import { libEnv } from "./env.server.type";',
|
|
27
|
+
`export const env = { ...libEnv, secret: "${name}-secret" };`,
|
|
28
|
+
`export default "${name}-default";`,
|
|
29
|
+
"",
|
|
30
|
+
].join("\n"),
|
|
31
|
+
);
|
|
32
|
+
const entry = path.join(root, "apps/demo/server.ts");
|
|
33
|
+
await write(entry, 'import { env } from "./env/env.server";\nconsole.info(JSON.stringify({ ...env }));\n');
|
|
34
|
+
return { envDir, entry };
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const build = async (root: string, environment: string) => {
|
|
38
|
+
const { envDir, entry } = await writeApp(root);
|
|
39
|
+
const result = await Bun.build({
|
|
40
|
+
entrypoints: [entry],
|
|
41
|
+
outdir: path.join(root, "out"),
|
|
42
|
+
target: "bun",
|
|
43
|
+
plugins: [createServerEnvPlugin({ envDir, environment, environments })],
|
|
44
|
+
});
|
|
45
|
+
expect(result.success).toBe(true);
|
|
46
|
+
const output = result.outputs.find((artifact) => artifact.kind === "entry-point");
|
|
47
|
+
if (!output) throw new Error("no entry output");
|
|
48
|
+
return { file: output.path, text: await output.text() };
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const run = (file: string, environment: string) =>
|
|
52
|
+
Bun.spawnSync([process.execPath, file], { env: { ...process.env, AKAN_PUBLIC_ENV: environment } });
|
|
53
|
+
|
|
54
|
+
describe("createServerEnvPlugin", () => {
|
|
55
|
+
test("bundles only the environment the image runs", async () => {
|
|
56
|
+
const { text } = await build(await makeTempRoot(), "main");
|
|
57
|
+
expect(text).toContain("main-secret");
|
|
58
|
+
expect(text).toContain("shared-value");
|
|
59
|
+
for (const name of environments.filter((name) => name !== "main")) {
|
|
60
|
+
expect(text).not.toContain(`${name}-secret`);
|
|
61
|
+
expect(text).not.toContain(`${name}-default`);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("the built environment boots with its own values", async () => {
|
|
66
|
+
const { file } = await build(await makeTempRoot(), "main");
|
|
67
|
+
const proc = run(file, "main");
|
|
68
|
+
expect(proc.exitCode).toBe(0);
|
|
69
|
+
expect(JSON.parse(proc.stdout.toString())).toEqual({ shared: "shared-value", secret: "main-secret" });
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("another environment refuses to boot and names the build to make", async () => {
|
|
73
|
+
const { file } = await build(await makeTempRoot(), "main");
|
|
74
|
+
const proc = run(file, "develop");
|
|
75
|
+
expect(proc.exitCode).not.toBe(0);
|
|
76
|
+
expect(proc.stderr.toString()).toContain(
|
|
77
|
+
"env/env.server.develop.ts is not in this build: `akan build` bundles the server env of AKAN_PUBLIC_ENV=main alone",
|
|
78
|
+
);
|
|
79
|
+
expect(proc.stderr.toString()).toContain(
|
|
80
|
+
"To run develop, build for it: AKAN_PUBLIC_ENV=develop in the workspace's .env, which outranks the shell, or --env develop on a desktop or mobile build.",
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("a custom branch is its own environment", async () => {
|
|
85
|
+
const { text } = await build(await makeTempRoot(), "qa");
|
|
86
|
+
expect(text).toContain("qa-secret");
|
|
87
|
+
expect(text).not.toContain("main-secret");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("leaves env files outside the app's env directory alone", async () => {
|
|
91
|
+
const root = await makeTempRoot();
|
|
92
|
+
const other = path.join(root, "libs/shared/env/env.server.testing.ts");
|
|
93
|
+
await write(other, 'export const env = { secret: "lib-testing-secret" };\n');
|
|
94
|
+
const { envDir, entry } = await writeApp(root);
|
|
95
|
+
await write(entry, `import { env } from ${JSON.stringify(other)};\nconsole.info(env.secret);\n`);
|
|
96
|
+
const result = await Bun.build({
|
|
97
|
+
entrypoints: [entry],
|
|
98
|
+
target: "bun",
|
|
99
|
+
plugins: [createServerEnvPlugin({ envDir, environment: "main", environments })],
|
|
100
|
+
});
|
|
101
|
+
expect(await result.outputs[0]?.text()).toContain("lib-testing-secret");
|
|
102
|
+
});
|
|
103
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import type { BunPlugin } from "bun";
|
|
4
|
+
|
|
5
|
+
export interface ServerEnvPluginOptions {
|
|
6
|
+
envDir: string;
|
|
7
|
+
/** The environment the image runs: its Dockerfile fixes `AKAN_PUBLIC_ENV` to it. */
|
|
8
|
+
environment: string;
|
|
9
|
+
environments: Iterable<string>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const escapeRegExp = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
13
|
+
|
|
14
|
+
//* `env.server.ts` imports every environment's file, so an image would hand out the secrets of each environment it
|
|
15
|
+
//* never runs. Every file but the image's own is swapped for exports that refuse to be read.
|
|
16
|
+
export const createServerEnvPlugin = ({ envDir, environment, environments }: ServerEnvPluginOptions): BunPlugin => {
|
|
17
|
+
const others = [...new Set(environments)].filter((name) => name !== environment);
|
|
18
|
+
//? Bun hands onLoad the real path, which differs from the given one under a symlink (macOS's /var -> /private/var).
|
|
19
|
+
const dirs = new Set([path.resolve(envDir), existsSync(envDir) ? realpathSync.native(envDir) : envDir]);
|
|
20
|
+
const names = others.map(escapeRegExp).join("|");
|
|
21
|
+
const filter = new RegExp(`^(?:${[...dirs].map(escapeRegExp).join("|")})[\\\\/]env\\.server\\.(?:${names})\\.ts$`);
|
|
22
|
+
return {
|
|
23
|
+
name: "akan-server-env",
|
|
24
|
+
setup(build) {
|
|
25
|
+
if (!others.length) return;
|
|
26
|
+
build.onLoad({ filter }, async (args) => {
|
|
27
|
+
const name = path.basename(args.path, ".ts").slice("env.server.".length);
|
|
28
|
+
const { exports } = new Bun.Transpiler({ loader: "ts" }).scan(await Bun.file(args.path).text());
|
|
29
|
+
const message = `env/env.server.${name}.ts is not in this build: \`akan build\` bundles the server env of AKAN_PUBLIC_ENV=${environment} alone, the environment its image runs. To run ${name}, build for it: AKAN_PUBLIC_ENV=${name} in the workspace's .env, which outranks the shell, or --env ${name} on a desktop or mobile build.`;
|
|
30
|
+
return {
|
|
31
|
+
loader: "js",
|
|
32
|
+
contents: [
|
|
33
|
+
`const refuse = () => { throw new Error(${JSON.stringify(message)}); };`,
|
|
34
|
+
"const unbuilt = new Proxy({}, { get: refuse, has: refuse, ownKeys: refuse, getOwnPropertyDescriptor: refuse });",
|
|
35
|
+
`export { ${exports.map((exported) => `unbuilt as ${JSON.stringify(exported)}`).join(", ")} };`,
|
|
36
|
+
].join("\n"),
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
};
|