@akanjs/devkit 3.0.0-beta.2 → 3.0.0-beta.21
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/README.ko.md +2 -2
- package/README.md +3 -2
- 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 +1 -3
- package/akanApp/akanApp.host.ts +82 -254
- package/akanApp/devHostPolicy.test.ts +10 -34
- package/akanApp/devHostPolicy.ts +12 -98
- package/akanConfig/akanConfig.test.ts +142 -106
- package/akanConfig/akanConfig.ts +147 -191
- package/akanConfig/types.ts +4 -3
- package/akanContext.ts +110 -160
- package/akanMcpContract.ts +28 -73
- package/appSelectionMemory.ts +1 -4
- package/applicationBuildReporter.ts +37 -5
- package/applicationBuildRunner.test.ts +5 -3
- package/applicationBuildRunner.ts +54 -61
- 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 +32 -1
- 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 +70 -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/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 +132 -106
- package/executors.ts +195 -496
- package/fileEditor.ts +9 -37
- package/{getDirname.test.ts → fileSys.test.ts} +5 -3
- package/fileSys.ts +6 -8
- 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 +62 -37
- package/frontendBuild/bundleDefine.ts +12 -0
- package/frontendBuild/clientBuildTypes.ts +5 -36
- package/frontendBuild/clientEntriesBundler.ts +9 -40
- package/frontendBuild/clientEntryDiscovery.ts +6 -41
- package/frontendBuild/csrArtifactBuilder.ts +6 -25
- package/frontendBuild/cssCandidateCache.test.ts +0 -6
- package/frontendBuild/cssCandidateCache.ts +6 -41
- package/frontendBuild/cssCompiler.ts +29 -78
- 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/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 +46 -69
- package/frontendBuild/hmrChangeClassifier.ts +2 -2
- package/frontendBuild/hmrWatcher.test.ts +8 -32
- package/frontendBuild/hmrWatcher.ts +14 -71
- package/frontendBuild/pagesBundleBuilder.ts +4 -29
- package/frontendBuild/precompressArtifacts.ts +5 -18
- package/frontendBuild/routeClientBuilder.ts +23 -46
- package/frontendBuild/routesManifestArtifactSerializer.ts +5 -36
- package/frontendBuild/sourceMtimeIndex.test.ts +41 -97
- package/frontendBuild/sourceMtimeIndex.ts +19 -130
- package/frontendBuild/ssrBaseArtifactBuilder.ts +2 -8
- 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 +29 -58
- package/incrementalBuilder/buildBatchProtocol.ts +8 -36
- package/incrementalBuilder/buildBatchRunner.test.ts +34 -0
- package/incrementalBuilder/buildBatchRunner.ts +10 -31
- 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/devWatchBatch.test.ts +21 -24
- package/incrementalBuilder/incrementalBuilder.host.test.ts +18 -55
- package/incrementalBuilder/incrementalBuilder.host.ts +18 -76
- package/incrementalBuilder/incrementalBuilder.proc.test.ts +81 -0
- package/incrementalBuilder/incrementalBuilder.proc.ts +49 -161
- package/index.ts +5 -19
- package/integration/devResourceProbe.ts +50 -56
- package/integration/devStability.integration.test.ts +47 -194
- 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/index.ts +6 -0
- package/mobile/mobileTarget.ts +16 -13
- package/mobile/nativeApi.test.ts +30 -0
- package/mobile/nativeApi.ts +40 -0
- package/mobile/nativeApp.test.ts +85 -0
- package/mobile/nativeApp.ts +210 -0
- package/mobile/nativeConfig.test.ts +199 -0
- package/mobile/nativeConfig.ts +184 -0
- package/mobile/nativeWebDir.test.ts +80 -0
- package/mobile/nativeWebDir.ts +62 -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 +10 -10
- 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 +30 -25
- package/subspace.ts +81 -115
- package/subspaceConfig.ts +11 -9
- 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 +35 -20
- package/transforms/transforms.test.ts +153 -44
- package/transforms/tsconfigPackageResolver.test.ts +6 -24
- package/transforms/useClientBundlePlugin.ts +2 -22
- package/typeChecker.ts +7 -63
- package/types.ts +1 -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 +13 -3
- package/workspaceLayout.ts +20 -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
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { realpathSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
1
3
|
import { Logger } from "akanjs/common";
|
|
2
4
|
|
|
3
5
|
export interface ApplicationBuildPhaseResult {
|
|
@@ -35,12 +37,12 @@ export class ApplicationBuildReporter {
|
|
|
35
37
|
Logger.rawLog(`Done in ${ApplicationBuildReporter.formatDuration(result.durationMs)}`);
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
static formatError(error: unknown): string {
|
|
40
|
+
static formatError(error: unknown, workspaceRoot = process.cwd()): string {
|
|
39
41
|
if (error instanceof AggregateError) {
|
|
40
42
|
const nestedMessages = error.errors
|
|
41
43
|
.map(
|
|
42
44
|
(nestedError, index) =>
|
|
43
|
-
ApplicationBuildReporter.formatError(nestedError).trim() || `Unknown error ${index + 1}`,
|
|
45
|
+
ApplicationBuildReporter.formatError(nestedError, workspaceRoot).trim() || `Unknown error ${index + 1}`,
|
|
44
46
|
)
|
|
45
47
|
.map((message) => message.replace(/^/gm, " "))
|
|
46
48
|
.join("\n");
|
|
@@ -48,13 +50,43 @@ export class ApplicationBuildReporter {
|
|
|
48
50
|
return nestedMessages ? `${error.message}\n${nestedMessages}` : error.message;
|
|
49
51
|
}
|
|
50
52
|
if (error instanceof Error) {
|
|
51
|
-
const causeMessage = error.cause
|
|
52
|
-
|
|
53
|
+
const causeMessage = error.cause
|
|
54
|
+
? `\nCaused by: ${ApplicationBuildReporter.formatError(error.cause, workspaceRoot)}`
|
|
55
|
+
: "";
|
|
56
|
+
return `${ApplicationBuildReporter.#withLocation(error.message, error, workspaceRoot)}${causeMessage}`;
|
|
53
57
|
}
|
|
54
|
-
if (typeof error === "object" && error !== null && "message" in error)
|
|
58
|
+
if (typeof error === "object" && error !== null && "message" in error)
|
|
59
|
+
return ApplicationBuildReporter.#withLocation(String(error.message), error, workspaceRoot);
|
|
55
60
|
return String(error);
|
|
56
61
|
}
|
|
57
62
|
|
|
63
|
+
static #withLocation(message: string, error: object, workspaceRoot: string): string {
|
|
64
|
+
const { position } = error as { position?: { file: string; line: number; column: number } | null };
|
|
65
|
+
if (!position?.file) return message;
|
|
66
|
+
const file = ApplicationBuildReporter.#relativeToWorkspace(position.file, workspaceRoot);
|
|
67
|
+
const location = position.line > 0 ? `${file}:${position.line}:${position.column}` : file;
|
|
68
|
+
return message.includes(location) ? message : message.replace(/^.*/, (headline) => `${headline} (${location})`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Bun names the file by its realpath, so a workspace root reached through a symlink is matched by its own too.
|
|
72
|
+
static #relativeToWorkspace(file: string, workspaceRoot: string): string {
|
|
73
|
+
if (!path.isAbsolute(file)) return file;
|
|
74
|
+
for (const root of [workspaceRoot, ApplicationBuildReporter.#realpathOf(workspaceRoot)]) {
|
|
75
|
+
const relative = path.relative(root, file);
|
|
76
|
+
if (!relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative))
|
|
77
|
+
return relative.replaceAll(path.sep, "/");
|
|
78
|
+
}
|
|
79
|
+
return file;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
static #realpathOf(dir: string): string {
|
|
83
|
+
try {
|
|
84
|
+
return realpathSync(dir);
|
|
85
|
+
} catch {
|
|
86
|
+
return dir;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
58
90
|
static formatDuration(ms: number): string {
|
|
59
91
|
if (ms < 1000) return `${ms}ms`;
|
|
60
92
|
if (ms < 60_000) return `${Math.round(ms / 100) / 10}s`;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
import { rm } from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
3
5
|
import { AKAN_BACKEND_MINIFY, AKAN_OPTIONAL_BACKEND_EXTERNALS } from "./applicationBuildRunner";
|
|
4
6
|
|
|
5
7
|
describe("ApplicationBuildRunner", () => {
|
|
@@ -16,8 +18,8 @@ describe("ApplicationBuildRunner", () => {
|
|
|
16
18
|
});
|
|
17
19
|
|
|
18
20
|
test("a bundler that mangles identifiers loses the class name a Logger reads", async () => {
|
|
19
|
-
const dir =
|
|
20
|
-
const entry =
|
|
21
|
+
const dir = path.join(os.tmpdir(), `akan-minify-${Bun.randomUUIDv7()}`);
|
|
22
|
+
const entry = path.join(dir, "entry.ts");
|
|
21
23
|
await Bun.write(
|
|
22
24
|
entry,
|
|
23
25
|
`const serve = () => class Service { readonly name = this.constructor.name; };
|
|
@@ -26,7 +28,7 @@ console.info(new SampleService().name);
|
|
|
26
28
|
`,
|
|
27
29
|
);
|
|
28
30
|
const build = async (minify: Bun.BuildConfig["minify"]) => {
|
|
29
|
-
const result = await Bun.build({ entrypoints: [entry], target: "bun", minify, outdir:
|
|
31
|
+
const result = await Bun.build({ entrypoints: [entry], target: "bun", minify, outdir: path.join(dir, "out") });
|
|
30
32
|
expect(result.success).toBe(true);
|
|
31
33
|
const proc = Bun.spawn([process.execPath, result.outputs[0].path], { stdout: "pipe" });
|
|
32
34
|
return (await new Response(proc.stdout).text()).trim();
|
|
@@ -60,9 +60,8 @@ const SSR_RENDER_EXTERNALS = [
|
|
|
60
60
|
"react-server-dom-webpack/client.browser",
|
|
61
61
|
] as const;
|
|
62
62
|
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
// `minify.keepNames` typechecks and does nothing as of Bun 1.4.2.
|
|
63
|
+
// Mangling renames classes, and `this.constructor.name` names loggers, exceptions and guards; `minify.keepNames`
|
|
64
|
+
// is a no-op as of Bun 1.4.2.
|
|
66
65
|
export const AKAN_BACKEND_MINIFY = { whitespace: true, syntax: true, identifiers: false } as const;
|
|
67
66
|
|
|
68
67
|
export const AKAN_OPTIONAL_BACKEND_EXTERNALS = [
|
|
@@ -77,6 +76,7 @@ export class ApplicationBuildRunner {
|
|
|
77
76
|
#app: App;
|
|
78
77
|
#fast: boolean;
|
|
79
78
|
#reporter?: BuildProgressReporter;
|
|
79
|
+
#spinner?: boolean;
|
|
80
80
|
#startedAt = Date.now();
|
|
81
81
|
#phases: BuildPhaseResult[] = [];
|
|
82
82
|
|
|
@@ -88,18 +88,15 @@ export class ApplicationBuildRunner {
|
|
|
88
88
|
|
|
89
89
|
async build({ spinner = false }: BuildOptions = {}): Promise<BuildResult> {
|
|
90
90
|
// serial build is needed because of Bun.build is unstable for parallel build
|
|
91
|
-
|
|
91
|
+
this.#spinner = spinner;
|
|
92
92
|
const { web, assets } = await this.#app.getConfig();
|
|
93
|
-
await this.#runPhase("prepare", "Preparing output directory", () => this.#app.prepareCommand("build")
|
|
94
|
-
|
|
95
|
-
});
|
|
96
|
-
if (!this.#fast) await this.#runPhase("typecheck", "Typechecking", () => this.typecheck(), undefined, phaseOptions);
|
|
93
|
+
await this.#runPhase("prepare", "Preparing output directory", () => this.#app.prepareCommand("build"));
|
|
94
|
+
if (!this.#fast) await this.#runPhase("typecheck", "Typechecking", () => this.typecheck());
|
|
97
95
|
await this.#runPhase(
|
|
98
96
|
"backend",
|
|
99
97
|
"Compiling backend",
|
|
100
98
|
() => this.#buildBackend(),
|
|
101
99
|
(result) => `${result.entrypoints} entrypoints, ${result.outputs} outputs`,
|
|
102
|
-
phaseOptions,
|
|
103
100
|
);
|
|
104
101
|
await this.#runPhase(
|
|
105
102
|
"ssr",
|
|
@@ -111,14 +108,12 @@ export class ApplicationBuildRunner {
|
|
|
111
108
|
: web.ssr
|
|
112
109
|
? "skipped"
|
|
113
110
|
: "disabled by akan.config.ts web.ssr",
|
|
114
|
-
phaseOptions,
|
|
115
111
|
);
|
|
116
112
|
await this.#runPhase(
|
|
117
113
|
"csr",
|
|
118
114
|
"Building CSR assets",
|
|
119
|
-
async () => (web.csr ? await this.#
|
|
115
|
+
async () => (web.csr ? await new CsrArtifactBuilder(this.#app, "build").build() : null),
|
|
120
116
|
(result) => result?.outputDir ?? (web.csr ? "skipped" : "disabled by akan.config.ts web.csr"),
|
|
121
|
-
phaseOptions,
|
|
122
117
|
);
|
|
123
118
|
await this.#runPhase(
|
|
124
119
|
"assets",
|
|
@@ -128,7 +123,6 @@ export class ApplicationBuildRunner {
|
|
|
128
123
|
result
|
|
129
124
|
? `${result.removed.length} font file(s) dropped, ${ApplicationBuildRunner.formatBytes(result.freedBytes)} freed; ${result.kept.length} kept`
|
|
130
125
|
: "disabled by akan.config.ts assets.pruneFonts",
|
|
131
|
-
phaseOptions,
|
|
132
126
|
);
|
|
133
127
|
await this.#runPhase(
|
|
134
128
|
"compress",
|
|
@@ -138,11 +132,8 @@ export class ApplicationBuildRunner {
|
|
|
138
132
|
result.files > 0
|
|
139
133
|
? `${result.files} files, ${ApplicationBuildRunner.formatBytes(result.inputBytes)} -> gzip ${ApplicationBuildRunner.formatBytes(result.outputBytes)} / br ${ApplicationBuildRunner.formatBytes(result.brotliBytes)}`
|
|
140
134
|
: "no files",
|
|
141
|
-
phaseOptions,
|
|
142
135
|
);
|
|
143
|
-
await this.#runPhase("metadata", "Writing production metadata", () => this.#buildAppMeta()
|
|
144
|
-
spinner,
|
|
145
|
-
});
|
|
136
|
+
await this.#runPhase("metadata", "Writing production metadata", () => this.#buildAppMeta());
|
|
146
137
|
return {
|
|
147
138
|
phases: this.#phases,
|
|
148
139
|
durationMs: Date.now() - this.#startedAt,
|
|
@@ -154,7 +145,7 @@ export class ApplicationBuildRunner {
|
|
|
154
145
|
async typecheck(options: TypecheckOptions = {}) {
|
|
155
146
|
const { clean = false, incremental = true } = options;
|
|
156
147
|
await this.#app.getPageKeys({ refresh: true });
|
|
157
|
-
const { typecheckDir, tsconfigPath } = await this.#writeTypecheckTsconfig(
|
|
148
|
+
const { typecheckDir, tsconfigPath } = await this.#writeTypecheckTsconfig(incremental);
|
|
158
149
|
if (clean) await rm(path.join(typecheckDir, "tsconfig.tsbuildinfo"), { force: true });
|
|
159
150
|
await this.#checkProjectInChildProcess(tsconfigPath);
|
|
160
151
|
}
|
|
@@ -164,19 +155,17 @@ export class ApplicationBuildRunner {
|
|
|
164
155
|
label: string,
|
|
165
156
|
task: () => Promise<T>,
|
|
166
157
|
summarize?: (result: T) => string | undefined,
|
|
167
|
-
options: BuildPhaseRunOptions = {},
|
|
168
158
|
) {
|
|
169
159
|
this.#reporter?.phaseStart?.({ id, label });
|
|
170
160
|
const phaseStartedAt = Date.now();
|
|
171
|
-
const spinner =
|
|
161
|
+
const spinner = this.#spinner
|
|
172
162
|
? new Spinner(label, { prefix: `${BUILD_PHASE_EMOJIS[id]} ${id}` }).start()
|
|
173
163
|
: undefined;
|
|
174
164
|
try {
|
|
175
165
|
const result = await task();
|
|
176
166
|
const phase = { id, label, durationMs: Date.now() - phaseStartedAt, summary: summarize?.(result) };
|
|
177
167
|
this.#phases.push(phase);
|
|
178
|
-
|
|
179
|
-
spinner?.succeed(`${label}${summary}`);
|
|
168
|
+
spinner?.succeed(`${label}${phase.summary ? `: ${phase.summary}` : ""}`);
|
|
180
169
|
this.#reporter?.phaseDone?.(phase);
|
|
181
170
|
return result;
|
|
182
171
|
} catch (error) {
|
|
@@ -191,66 +180,79 @@ export class ApplicationBuildRunner {
|
|
|
191
180
|
await Promise.all([
|
|
192
181
|
this.#app.dist.writeJson("package.json", akanConfig.getProductionPackageJson()),
|
|
193
182
|
this.#app.dist.writeFile(`${this.#app.dist.cwdPath}/Dockerfile`, akanConfig.dockerfile),
|
|
183
|
+
this.#app.dist.writeJson("akan.build.json", {
|
|
184
|
+
buildId: await this.#resolveBuildId(),
|
|
185
|
+
akanVersion: akanConfig.akanVersion,
|
|
186
|
+
builtAt: new Date().toISOString(),
|
|
187
|
+
}),
|
|
194
188
|
]);
|
|
195
189
|
}
|
|
196
190
|
|
|
191
|
+
//* Read back at runtime as `buildId` on the ops channel; a deployment's own AKAN_BUILD_ID still wins over it there.
|
|
192
|
+
async #resolveBuildId() {
|
|
193
|
+
const fromEnv = process.env.AKAN_BUILD_ID?.trim();
|
|
194
|
+
if (fromEnv) return fromEnv;
|
|
195
|
+
const root = this.#app.workspace.workspaceRoot;
|
|
196
|
+
const sha = Bun.spawnSync(["git", "rev-parse", "--short=12", "HEAD"], { cwd: root, stderr: "ignore" });
|
|
197
|
+
if (sha.exitCode !== 0) return null;
|
|
198
|
+
const dirty = Bun.spawnSync(["git", "status", "--porcelain", "--untracked-files=no"], {
|
|
199
|
+
cwd: root,
|
|
200
|
+
stderr: "ignore",
|
|
201
|
+
});
|
|
202
|
+
const suffix = dirty.exitCode === 0 && dirty.stdout.toString().trim() ? "-dirty" : "";
|
|
203
|
+
return `${sha.stdout.toString().trim()}${suffix}`;
|
|
204
|
+
}
|
|
205
|
+
|
|
197
206
|
async #buildBackend() {
|
|
198
|
-
const
|
|
199
|
-
const backendExternals = [
|
|
200
|
-
...new Set([...akanConfig.externalLibs, ...SSR_RENDER_EXTERNALS, ...AKAN_OPTIONAL_BACKEND_EXTERNALS]),
|
|
201
|
-
];
|
|
202
|
-
const { web } = akanConfig;
|
|
207
|
+
const { externalLibs, web } = await this.#app.getConfig();
|
|
203
208
|
const backendEntryPoints = [`${this.#app.cwdPath}/main.ts`, `${this.#app.cwdPath}/server.ts`];
|
|
204
209
|
for (const entrypoint of backendEntryPoints) {
|
|
205
210
|
if (!(await Bun.file(entrypoint).exists())) throw new Error(`Backend entrypoint not found: ${entrypoint}`);
|
|
206
211
|
}
|
|
207
|
-
const
|
|
208
|
-
entrypoints: backendEntryPoints,
|
|
212
|
+
const sharedConfig = {
|
|
209
213
|
outdir: this.#app.dist.cwdPath,
|
|
210
214
|
target: "bun",
|
|
211
215
|
minify: AKAN_BACKEND_MINIFY,
|
|
212
216
|
naming: { entry: "[name].[ext]", chunk: "chunk-[hash].[ext]" },
|
|
217
|
+
// `akan build` must embed production react-server-dom regardless of the shell's NODE_ENV.
|
|
213
218
|
define: { "process.env.NODE_ENV": JSON.stringify("production") },
|
|
214
|
-
|
|
215
|
-
}
|
|
219
|
+
} satisfies Omit<Bun.BuildConfig, "entrypoints">;
|
|
220
|
+
const backendConfig = { ...sharedConfig, plugins: [this.#createExternalSpecifiersPlugin(externalLibs)] };
|
|
221
|
+
//* Built apart so main.js keeps its own module copies; splitting moves lazy vendor `import()`s out of the boot parse.
|
|
222
|
+
const [mainResult, serverResult] = [
|
|
223
|
+
await this.#buildOrThrow("backend", { ...backendConfig, entrypoints: [backendEntryPoints[0]] }),
|
|
224
|
+
await this.#buildOrThrow("backend", { ...backendConfig, entrypoints: [backendEntryPoints[1]], splitting: true }),
|
|
225
|
+
];
|
|
216
226
|
// Nothing spawns the RSC worker without SSR, so an api-only image does not carry it.
|
|
217
227
|
const rscWorkerResult = web.ssr
|
|
218
228
|
? await this.#buildOrThrow("rsc-worker", {
|
|
229
|
+
...backendConfig,
|
|
219
230
|
entrypoints: [this.#resolveRscWorkerBuildEntry()],
|
|
220
|
-
outdir: this.#app.dist.cwdPath,
|
|
221
|
-
target: "bun",
|
|
222
|
-
minify: AKAN_BACKEND_MINIFY,
|
|
223
|
-
naming: { entry: "[name].[ext]", chunk: "chunk-[hash].[ext]" },
|
|
224
231
|
conditions: ["react-server"],
|
|
225
|
-
// `akan build` must embed production react-server-dom regardless of the shell's NODE_ENV.
|
|
226
|
-
define: { "process.env.NODE_ENV": JSON.stringify("production") },
|
|
227
|
-
plugins: backendExternals.length > 0 ? [this.#createExternalSpecifiersPlugin(backendExternals)] : [],
|
|
228
232
|
})
|
|
229
233
|
: null;
|
|
230
234
|
const consoleRuntimeResult = await this.#buildOrThrow("console-runtime", {
|
|
235
|
+
...sharedConfig,
|
|
231
236
|
entrypoints: [this.#resolveConsoleRuntimeBuildEntry()],
|
|
232
|
-
outdir: this.#app.dist.cwdPath,
|
|
233
|
-
target: "bun",
|
|
234
|
-
minify: AKAN_BACKEND_MINIFY,
|
|
235
237
|
naming: { entry: "console-runtime.[ext]", chunk: "chunk-[hash].[ext]" },
|
|
236
|
-
define: { "process.env.NODE_ENV": JSON.stringify("production") },
|
|
237
238
|
});
|
|
238
239
|
await this.#writeConsoleShim();
|
|
240
|
+
const results = [mainResult, serverResult, rscWorkerResult, consoleRuntimeResult];
|
|
239
241
|
return {
|
|
240
242
|
entrypoints: backendEntryPoints.length + (rscWorkerResult ? 2 : 1),
|
|
241
|
-
outputs:
|
|
242
|
-
backendResult.outputs.length + (rscWorkerResult?.outputs.length ?? 0) + consoleRuntimeResult.outputs.length + 1,
|
|
243
|
+
outputs: results.reduce((sum, result) => sum + (result?.outputs.length ?? 0), 1),
|
|
243
244
|
};
|
|
244
245
|
}
|
|
245
246
|
|
|
246
247
|
async #writeConsoleShim() {
|
|
247
248
|
await Bun.write(
|
|
248
249
|
path.join(this.#app.dist.cwdPath, "console.js"),
|
|
249
|
-
`
|
|
250
|
-
|
|
250
|
+
`process.env.AKAN_COMMAND_TYPE = "console";
|
|
251
|
+
const { cnst, db, dict, option, server, sig, srv } = await import("./server.js");
|
|
252
|
+
const { assertAkanConsoleAllowed, startAkanConsole } = await import("./console-runtime.js");
|
|
251
253
|
|
|
252
254
|
const run = async () => {
|
|
253
|
-
assertAkanConsoleAllowed(
|
|
255
|
+
assertAkanConsoleAllowed();
|
|
254
256
|
await server.start({ listen: false, web: false });
|
|
255
257
|
try {
|
|
256
258
|
await startAkanConsole(server, { globals: { cnst, db, dict, option, sig, srv } });
|
|
@@ -283,10 +285,6 @@ void run().catch((error) => {
|
|
|
283
285
|
}
|
|
284
286
|
}
|
|
285
287
|
|
|
286
|
-
async #buildCsr() {
|
|
287
|
-
return await new CsrArtifactBuilder(this.#app, "build").build();
|
|
288
|
-
}
|
|
289
|
-
|
|
290
288
|
async #buildSsr() {
|
|
291
289
|
const pageKeys = await this.#app.getPageKeys();
|
|
292
290
|
if (pageKeys.length === 0) {
|
|
@@ -298,7 +296,7 @@ void run().catch((error) => {
|
|
|
298
296
|
return { base, allRoutes };
|
|
299
297
|
}
|
|
300
298
|
|
|
301
|
-
async #writeTypecheckTsconfig(
|
|
299
|
+
async #writeTypecheckTsconfig(incremental: boolean) {
|
|
302
300
|
const typecheckDir = path.join(this.#app.cwdPath, ".akan", "typecheck");
|
|
303
301
|
await mkdir(typecheckDir, { recursive: true });
|
|
304
302
|
//* TypeScript's `include` globs do not cross a symlink, so synced lib pages need their real path.
|
|
@@ -367,11 +365,10 @@ void run().catch((error) => {
|
|
|
367
365
|
return result;
|
|
368
366
|
}
|
|
369
367
|
|
|
370
|
-
#createExternalSpecifiersPlugin(
|
|
371
|
-
const
|
|
372
|
-
const escaped =
|
|
368
|
+
#createExternalSpecifiersPlugin(externalLibs: readonly string[]): BunPlugin {
|
|
369
|
+
const specifiers = new Set([...externalLibs, ...SSR_RENDER_EXTERNALS, ...AKAN_OPTIONAL_BACKEND_EXTERNALS]);
|
|
370
|
+
const escaped = [...specifiers].map((specifier) => specifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
373
371
|
const filter = new RegExp(`^(${escaped.join("|")})(?:/.*)?$`);
|
|
374
|
-
|
|
375
372
|
return {
|
|
376
373
|
name: "akan-backend-externalize-specifiers",
|
|
377
374
|
setup(build) {
|
|
@@ -380,9 +377,5 @@ void run().catch((error) => {
|
|
|
380
377
|
};
|
|
381
378
|
}
|
|
382
379
|
|
|
383
|
-
static formatBytes
|
|
384
|
-
if (bytes < 1024) return `${bytes}B`;
|
|
385
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
386
|
-
return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
|
|
387
|
-
}
|
|
380
|
+
static formatBytes = FontPruner.formatBytes;
|
|
388
381
|
}
|
|
@@ -8,7 +8,7 @@ export interface SignalTestPreloadTarget {
|
|
|
8
8
|
|
|
9
9
|
export async function resolveSignalTestPreloadPath(target: SignalTestPreloadTarget) {
|
|
10
10
|
const candidates: string[] = [];
|
|
11
|
-
const
|
|
11
|
+
for (const basePath of [target.cwdPath, process.cwd(), path.dirname(Bun.main), import.meta.dir]) {
|
|
12
12
|
try {
|
|
13
13
|
candidates.push(
|
|
14
14
|
path.join(path.dirname(Bun.resolveSync("akanjs/package.json", basePath)), SIGNAL_TEST_PRELOAD_PATH),
|
|
@@ -16,13 +16,7 @@ export async function resolveSignalTestPreloadPath(target: SignalTestPreloadTarg
|
|
|
16
16
|
} catch {
|
|
17
17
|
// Source workspaces and published installs can resolve Akan packages from different roots.
|
|
18
18
|
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
addResolvedPackageCandidate(target.cwdPath);
|
|
22
|
-
addResolvedPackageCandidate(process.cwd());
|
|
23
|
-
addResolvedPackageCandidate(path.dirname(Bun.main));
|
|
24
|
-
addResolvedPackageCandidate(import.meta.dir);
|
|
25
|
-
|
|
19
|
+
}
|
|
26
20
|
candidates.push(
|
|
27
21
|
path.join(target.cwdPath, "../../node_modules/akanjs", SIGNAL_TEST_PRELOAD_PATH),
|
|
28
22
|
path.join(target.cwdPath, "../../pkgs/akanjs", SIGNAL_TEST_PRELOAD_PATH),
|
|
@@ -33,10 +27,7 @@ export async function resolveSignalTestPreloadPath(target: SignalTestPreloadTarg
|
|
|
33
27
|
);
|
|
34
28
|
|
|
35
29
|
const uniqueCandidates = [...new Set(candidates)];
|
|
36
|
-
for (const candidate of uniqueCandidates)
|
|
37
|
-
if (await Bun.file(candidate).exists()) return candidate;
|
|
38
|
-
}
|
|
39
|
-
|
|
30
|
+
for (const candidate of uniqueCandidates) if (await Bun.file(candidate).exists()) return candidate;
|
|
40
31
|
throw new Error(
|
|
41
32
|
`Failed to locate ${SIGNAL_TEST_PRELOAD_PATH} from ${target.cwdPath}.\nProbed paths:\n${uniqueCandidates
|
|
42
33
|
.map((candidate) => ` - ${candidate}`)
|
|
@@ -1,25 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
3
|
-
import os from "node:os";
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
4
2
|
import path from "node:path";
|
|
3
|
+
import { tempDirs, writeText as write } from "../testHelpers";
|
|
5
4
|
import { resolveSsrPageEntries } from "./implicitRootLayout";
|
|
6
5
|
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
const makeTempRoot = async () => {
|
|
10
|
-
const root = await mkdtemp(path.join(os.tmpdir(), "akan-implicit-root-layout-"));
|
|
11
|
-
tempRoots.push(root);
|
|
12
|
-
return root;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const write = async (filePath: string, content: string) => {
|
|
16
|
-
await mkdir(path.dirname(filePath), { recursive: true });
|
|
17
|
-
await writeFile(filePath, content);
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
afterEach(async () => {
|
|
21
|
-
await Promise.all(tempRoots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
|
|
22
|
-
});
|
|
6
|
+
const makeTempRoot = tempDirs("akan-implicit-root-layout-");
|
|
23
7
|
|
|
24
8
|
describe("resolveSsrPageEntries", () => {
|
|
25
9
|
test("inherits root layout static exports for grouped root boundaries", async () => {
|
|
@@ -31,11 +15,11 @@ describe("resolveSsrPageEntries", () => {
|
|
|
31
15
|
await write(path.join(appRoot, "env", "env.client.ts"), "export const env = {};\n");
|
|
32
16
|
await write(
|
|
33
17
|
rootLayoutPath,
|
|
34
|
-
'export const theme = "dark";\nexport const fonts = [{ name: "pretendard" }];\nexport const
|
|
18
|
+
'export const theme = "dark";\nexport const fonts = [{ name: "pretendard" }];\nexport const head = null;\n',
|
|
35
19
|
);
|
|
36
20
|
await write(
|
|
37
21
|
groupedLayoutPath,
|
|
38
|
-
|
|
22
|
+
"export function generateHead() { return null; }\nexport default function Layout({ children }) { return children; }\n",
|
|
39
23
|
);
|
|
40
24
|
|
|
41
25
|
const entries = await resolveSsrPageEntries({
|
|
@@ -54,15 +38,10 @@ describe("resolveSsrPageEntries", () => {
|
|
|
54
38
|
expect(generatedSource).toContain('import * as inheritedModule from "../../../page/_layout.tsx";');
|
|
55
39
|
expect(generatedSource).toContain("const inheritedLayout = resolveRouteModule(inheritedModule as never");
|
|
56
40
|
expect(generatedSource).not.toContain("<System.Provider");
|
|
57
|
-
expect(generatedSource).toContain("export async function
|
|
58
|
-
expect(generatedSource).toContain("if (userLayout.
|
|
59
|
-
expect(generatedSource).toContain("
|
|
60
|
-
expect(generatedSource).toContain(
|
|
61
|
-
"if (inheritedLayout.generateMetadata) return inheritedLayout.generateMetadata(props);",
|
|
62
|
-
);
|
|
63
|
-
expect(generatedSource).toContain("return inheritedLayout.metadata;");
|
|
64
|
-
expect(generatedSource).not.toContain("Object.keys(userLayout.metadata)");
|
|
65
|
-
expect(generatedSource).not.toContain("Object.keys(inheritedLayout.metadata)");
|
|
41
|
+
expect(generatedSource).toContain("export async function generateHead(props: PageProps)");
|
|
42
|
+
expect(generatedSource).toContain("if (userLayout.generateHead) return userLayout.generateHead(props);");
|
|
43
|
+
expect(generatedSource).toContain("return inheritedLayout.head;");
|
|
44
|
+
expect(generatedSource).not.toContain("generateMetadata");
|
|
66
45
|
expect(generatedSource).toContain("export const NotFound = userLayout.NotFound ?? inheritedLayout.NotFound;");
|
|
67
46
|
expect(generatedSource).toContain("export const Error = userLayout.Error ?? inheritedLayout.Error;");
|
|
68
47
|
expect(generatedSource).toContain("export const pageConfig = userLayout.pageConfig ?? inheritedLayout.pageConfig;");
|
|
@@ -171,4 +150,27 @@ describe("resolveSsrPageEntries", () => {
|
|
|
171
150
|
expect(generatedSource).toContain("? await UserLayout({ params, searchParams, children })");
|
|
172
151
|
expect(generatedSource).toContain(" return layout;\n}");
|
|
173
152
|
});
|
|
153
|
+
|
|
154
|
+
test("prunes generated wrappers whose route file moved away", async () => {
|
|
155
|
+
const appRoot = await makeTempRoot();
|
|
156
|
+
const pageRoot = path.join(appRoot, "page");
|
|
157
|
+
await write(path.join(appRoot, "env", "env.client.ts"), "export const env = {};\n");
|
|
158
|
+
await write(path.join(pageRoot, "a", "_overrides.tsx"), "export default {};\n");
|
|
159
|
+
await write(path.join(pageRoot, "b", "_overrides.tsx"), "export default {};\n");
|
|
160
|
+
await write(path.join(pageRoot, "(home)", "_layout.tsx"), "export default ({ children }) => children;\n");
|
|
161
|
+
const resolve = async (pageKeys: string[]) =>
|
|
162
|
+
await resolveSsrPageEntries({ appCwdPath: appRoot, appName: "demo", pageKeys });
|
|
163
|
+
const generatedPaths = async (pageKeys: string[]) =>
|
|
164
|
+
(await resolve(pageKeys))
|
|
165
|
+
.filter((entry) => entry.moduleAbsPath.includes(`${path.sep}.akan${path.sep}`))
|
|
166
|
+
.map((entry) => entry.moduleAbsPath);
|
|
167
|
+
|
|
168
|
+
const before = await generatedPaths(["./(home)/_layout.tsx", "./a/_overrides.tsx", "./a/x.tsx"]);
|
|
169
|
+
const after = await generatedPaths(["./b/_overrides.tsx", "./b/y.tsx"]);
|
|
170
|
+
|
|
171
|
+
expect(before).toHaveLength(2);
|
|
172
|
+
for (const stale of before) expect(await Bun.file(stale).exists()).toBe(false);
|
|
173
|
+
expect(after).toHaveLength(2);
|
|
174
|
+
for (const fresh of after) expect(await Bun.file(fresh).exists()).toBe(true);
|
|
175
|
+
});
|
|
174
176
|
});
|