@akanjs/devkit 3.0.0-beta.9 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1819 -0
- 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 +34 -31
- package/artifact/implicitRootLayout.ts +60 -107
- package/artifact/routeSeedIndex.test.ts +5 -18
- package/artifact/routeSeedIndex.ts +11 -44
- package/biome.base.json +39 -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 +506 -0
- package/csrE2e/csrFastRefresh.e2e.test.ts +233 -0
- package/csrE2e/csrPageStack.e2e.test.ts +265 -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 +37 -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 +111 -202
- package/integration/devStabilityHarness.test.ts +13 -28
- package/integration/devStabilityHarness.ts +67 -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-static-in-object-light-model/bad.ts +43 -0
- package/lint/__fixtures__/no-static-in-object-light-model/fixture.json +1 -0
- package/lint/__fixtures__/no-static-in-object-light-model/good.ts +42 -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-static-in-object-light-model.grit +27 -0
- 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 +19 -26
- 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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readdir, rm } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import type { App } from "../commandDecorators";
|
|
4
4
|
import { AsyncDefaultExportDetector } from "../transforms/asyncDefaultExportDetector";
|
|
@@ -11,23 +11,17 @@ export interface PageEntry {
|
|
|
11
11
|
|
|
12
12
|
const LAYOUT_KEY_RE = /^\.\/(.+\/)?_layout\.(tsx|ts|jsx|js)$/;
|
|
13
13
|
|
|
14
|
-
async function appHasStModule(appCwdPath: string): Promise<boolean> {
|
|
15
|
-
return Bun.file(path.join(appCwdPath, "lib", "st.ts")).exists();
|
|
16
|
-
}
|
|
17
|
-
|
|
18
14
|
const IMPLICIT_LAYOUT_DIR = path.join(".akan", "generated", "root-layouts");
|
|
19
15
|
const IMPLICIT_DICT_DIR = path.join(".akan", "generated", "dict");
|
|
20
16
|
const IMPLICIT_OVERRIDES_DIR = path.join(".akan", "generated", "overrides");
|
|
21
17
|
const OVERRIDES_KEY_RE = /^\.\/(.+\/)?_overrides\.(tsx|ts|jsx|js)$/;
|
|
22
18
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
* resolve it — and the author never writes `"use client"`.
|
|
30
|
-
*/
|
|
19
|
+
const importSpecifier = (fromAbsPath: string, toAbsPath: string) => {
|
|
20
|
+
const rel = path.relative(path.dirname(fromAbsPath), toAbsPath).split(path.sep).join("/");
|
|
21
|
+
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// UiOverrideProvider is a client component, so `_overrides.tsx` mounts through a generated "use client" layout.
|
|
31
25
|
async function writeGeneratedOverridesLayoutFile(opts: {
|
|
32
26
|
appCwdPath: string;
|
|
33
27
|
key: string;
|
|
@@ -35,12 +29,10 @@ async function writeGeneratedOverridesLayoutFile(opts: {
|
|
|
35
29
|
}): Promise<string> {
|
|
36
30
|
const filename = `${opts.key.replace(/^\.\//, "").replace(/[^a-zA-Z0-9]+/g, "_")}.tsx`;
|
|
37
31
|
const absPath = path.join(path.resolve(opts.appCwdPath), IMPLICIT_OVERRIDES_DIR, filename);
|
|
38
|
-
const userRel = path.relative(path.dirname(absPath), opts.userAbsPath).split(path.sep).join("/");
|
|
39
|
-
const userSpecifier = userRel.startsWith(".") ? userRel : `./${userRel}`;
|
|
40
32
|
const source = `"use client";
|
|
41
33
|
import { UiOverrideProvider } from "akanjs/ui";
|
|
42
34
|
import { createElement, type ReactNode } from "react";
|
|
43
|
-
import value from ${JSON.stringify(
|
|
35
|
+
import value from ${JSON.stringify(importSpecifier(absPath, opts.userAbsPath))};
|
|
44
36
|
|
|
45
37
|
export default function AkanUiOverridesLayout({ children }: { children?: ReactNode }) {
|
|
46
38
|
return createElement(UiOverrideProvider, { value }, children);
|
|
@@ -50,6 +42,17 @@ export default function AkanUiOverridesLayout({ children }: { children?: ReactNo
|
|
|
50
42
|
return absPath;
|
|
51
43
|
}
|
|
52
44
|
|
|
45
|
+
async function pruneGeneratedFiles(dirAbsPath: string, keepAbsPaths: string[]): Promise<void> {
|
|
46
|
+
const keep = new Set(keepAbsPaths);
|
|
47
|
+
const names = await readdir(dirAbsPath).catch(() => [] as string[]);
|
|
48
|
+
await Promise.all(
|
|
49
|
+
names
|
|
50
|
+
.map((name) => path.join(dirAbsPath, name))
|
|
51
|
+
.filter((absPath) => !keep.has(absPath))
|
|
52
|
+
.map((absPath) => rm(absPath, { force: true })),
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
53
56
|
interface RootBoundary {
|
|
54
57
|
sourceKey: string | null;
|
|
55
58
|
sourceAbsPath: string | null;
|
|
@@ -73,10 +76,6 @@ function implicitRootLayoutAbsPath(appCwdPath: string, segments: string[]): stri
|
|
|
73
76
|
return path.join(path.resolve(appCwdPath), IMPLICIT_LAYOUT_DIR, filename);
|
|
74
77
|
}
|
|
75
78
|
|
|
76
|
-
function implicitDictionaryMacroAbsPath(appCwdPath: string): string {
|
|
77
|
-
return path.join(path.resolve(appCwdPath), IMPLICIT_DICT_DIR, "useDict.ts");
|
|
78
|
-
}
|
|
79
|
-
|
|
80
79
|
export function isRootBoundarySegments(segments: string[], basePaths: Iterable<string>): boolean {
|
|
81
80
|
const firstVisibleIndex = segments.findIndex((segment) => !/^\(.+\)$/.test(segment));
|
|
82
81
|
if (firstVisibleIndex === -1) return segments.length <= 1;
|
|
@@ -99,10 +98,7 @@ function findRootBoundaries(pageKeys: string[], appCwdPath: string, basePaths: I
|
|
|
99
98
|
segments,
|
|
100
99
|
});
|
|
101
100
|
}
|
|
102
|
-
|
|
103
|
-
if (!hasExplicitRootBoundary && boundaries.size === 0) {
|
|
104
|
-
boundaries.set("", { sourceKey: null, sourceAbsPath: null, segments: [] });
|
|
105
|
-
}
|
|
101
|
+
if (boundaries.size === 0) boundaries.set("", { sourceKey: null, sourceAbsPath: null, segments: [] });
|
|
106
102
|
return [...boundaries.values()].sort((a, b) => a.segments.join("/").localeCompare(b.segments.join("/")));
|
|
107
103
|
}
|
|
108
104
|
|
|
@@ -123,11 +119,6 @@ function findExplicitRootLayoutAbsPath(pageKeys: string[], appCwdPath: string):
|
|
|
123
119
|
return rootLayoutKey ? path.resolve(appCwdPath, "page", rootLayoutKey.replace(/^\.\//, "")) : null;
|
|
124
120
|
}
|
|
125
121
|
|
|
126
|
-
function routePrefixForSegments(segments: string[]): string | null {
|
|
127
|
-
const visible = segments.filter((segment) => !/^\(.+\)$/.test(segment));
|
|
128
|
-
return visible[0] ?? null;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
122
|
async function assertEnvClientConvention(appCwdPath: string, appName: string) {
|
|
132
123
|
const envPath = path.join(appCwdPath, "env", "env.client.ts");
|
|
133
124
|
if (!(await Bun.file(envPath).exists())) {
|
|
@@ -138,8 +129,7 @@ async function assertEnvClientConvention(appCwdPath: string, appName: string) {
|
|
|
138
129
|
}
|
|
139
130
|
|
|
140
131
|
async function writeGeneratedDictionaryMacroFile(appCwdPath: string, appName: string): Promise<string> {
|
|
141
|
-
const absPath =
|
|
142
|
-
await mkdir(path.dirname(absPath), { recursive: true });
|
|
132
|
+
const absPath = path.join(path.resolve(appCwdPath), IMPLICIT_DICT_DIR, "useDict.ts");
|
|
143
133
|
await Bun.write(
|
|
144
134
|
absPath,
|
|
145
135
|
`import { getAllDictionary } from "@apps/${appName}/lib/dict" with { type: "macro" };
|
|
@@ -163,35 +153,20 @@ async function writeGeneratedRootLayoutFile(opts: {
|
|
|
163
153
|
? await writeGeneratedDictionaryMacroFile(opts.appCwdPath, opts.appName)
|
|
164
154
|
: null;
|
|
165
155
|
const absPath = implicitRootLayoutAbsPath(opts.appCwdPath, opts.boundary.segments);
|
|
166
|
-
|
|
167
|
-
const
|
|
168
|
-
? path.relative(path.dirname(absPath), dictMacroAbsPath).split(path.sep).join("/")
|
|
169
|
-
: null;
|
|
170
|
-
const dictMacroSpecifier = dictMacroRel ? (dictMacroRel.startsWith(".") ? dictMacroRel : `./${dictMacroRel}`) : null;
|
|
171
|
-
const sourceRel = opts.boundary.sourceAbsPath
|
|
172
|
-
? path.relative(path.dirname(absPath), opts.boundary.sourceAbsPath).split(path.sep).join("/")
|
|
173
|
-
: null;
|
|
174
|
-
const sourceSpecifier = sourceRel ? (sourceRel.startsWith(".") ? sourceRel : `./${sourceRel}`) : null;
|
|
156
|
+
const dictMacroSpecifier = dictMacroAbsPath ? importSpecifier(absPath, dictMacroAbsPath) : null;
|
|
157
|
+
const sourceSpecifier = opts.boundary.sourceAbsPath ? importSpecifier(absPath, opts.boundary.sourceAbsPath) : null;
|
|
175
158
|
const inheritedSourceAbsPath =
|
|
176
159
|
opts.rootSourceAbsPath && opts.rootSourceAbsPath !== opts.boundary.sourceAbsPath ? opts.rootSourceAbsPath : null;
|
|
177
|
-
const
|
|
178
|
-
? path.relative(path.dirname(absPath), inheritedSourceAbsPath).split(path.sep).join("/")
|
|
179
|
-
: null;
|
|
180
|
-
const inheritedSourceSpecifier = inheritedSourceRel
|
|
181
|
-
? inheritedSourceRel.startsWith(".")
|
|
182
|
-
? inheritedSourceRel
|
|
183
|
-
: `./${inheritedSourceRel}`
|
|
184
|
-
: null;
|
|
160
|
+
const inheritedSourceSpecifier = inheritedSourceAbsPath ? importSpecifier(absPath, inheritedSourceAbsPath) : null;
|
|
185
161
|
const clientImport = opts.includeStInit
|
|
186
162
|
? `import { st } from "@apps/${opts.appName}/client";\nvoid st;\n`
|
|
187
163
|
: `import "@apps/${opts.appName}/client";\n`;
|
|
188
|
-
|
|
189
|
-
// the same afterwards, and this file never has to know which shape the app wrote.
|
|
164
|
+
//? resolveRouteModule reads a rootLayout() chain and the legacy named exports alike.
|
|
190
165
|
const inheritedLabel = inheritedSourceAbsPath ? path.relative(opts.appCwdPath, inheritedSourceAbsPath) : "";
|
|
191
166
|
const inheritedImport = inheritedSourceSpecifier
|
|
192
167
|
? `import * as inheritedModule from ${JSON.stringify(inheritedSourceSpecifier)};\nconst inheritedLayout = resolveRouteModule(inheritedModule as never, ${JSON.stringify(inheritedLabel)}).module as LayoutModule;\n`
|
|
193
168
|
: "const inheritedLayout: LayoutModule = {};\n";
|
|
194
|
-
const prefix =
|
|
169
|
+
const prefix = opts.boundary.segments.find((segment) => !/^\(.+\)$/.test(segment)) ?? null;
|
|
195
170
|
const userLabel = opts.boundary.sourceAbsPath ? path.relative(opts.appCwdPath, opts.boundary.sourceAbsPath) : "";
|
|
196
171
|
const userImport = sourceSpecifier
|
|
197
172
|
? `import * as userModule from ${JSON.stringify(sourceSpecifier)};\nconst userLayout = resolveRouteModule(userModule as never, ${JSON.stringify(userLabel)}).module as LayoutModule;\nconst UserLayout = userLayout.default as (props: LayoutProps) => ReactNode | Promise<ReactNode>;\n`
|
|
@@ -200,9 +175,8 @@ async function writeGeneratedRootLayoutFile(opts: {
|
|
|
200
175
|
? await AsyncDefaultExportDetector.detect(opts.boundary.sourceAbsPath)
|
|
201
176
|
: false;
|
|
202
177
|
const userLayoutElement = "<UserLayout params={params} searchParams={searchParams}>{children}</UserLayout>";
|
|
203
|
-
// React has no async client component, so
|
|
204
|
-
//
|
|
205
|
-
// the layout's own awaits instead of streaming it as its own Flight chunk.
|
|
178
|
+
// React has no async client component, so CSR awaits an async layout's node; RSC keeps the element, since
|
|
179
|
+
// awaiting there would hold the shell behind the layout's own awaits instead of streaming it.
|
|
206
180
|
const layoutSignature = isAsyncUserLayout
|
|
207
181
|
? "export default async function GeneratedLayout"
|
|
208
182
|
: "export default function GeneratedLayout";
|
|
@@ -215,6 +189,19 @@ async function writeGeneratedRootLayoutFile(opts: {
|
|
|
215
189
|
: "";
|
|
216
190
|
const layoutChild = isAsyncUserLayout ? "{layout}" : userLayoutElement;
|
|
217
191
|
const layoutReturn = isAsyncUserLayout ? "layout" : userLayoutElement;
|
|
192
|
+
const layoutExports = `export async function generateHead(props: PageProps) {
|
|
193
|
+
if (userLayout.generateHead) return userLayout.generateHead(props);
|
|
194
|
+
if (userLayout.head !== undefined) return userLayout.head;
|
|
195
|
+
if (inheritedLayout.generateHead) return inheritedLayout.generateHead(props);
|
|
196
|
+
return inheritedLayout.head;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export const NotFound = userLayout.NotFound ?? inheritedLayout.NotFound;
|
|
200
|
+
export const Error = userLayout.Error ?? inheritedLayout.Error;
|
|
201
|
+
export const pageConfig = userLayout.pageConfig ?? inheritedLayout.pageConfig;
|
|
202
|
+
|
|
203
|
+
${layoutSignature}({ children, params, searchParams }: LayoutProps) {
|
|
204
|
+
${layoutBinding} return `;
|
|
218
205
|
const source = opts.includeSystemProvider
|
|
219
206
|
? `import type { LayoutModule, LayoutProps, PageProps } from "akanjs/client";
|
|
220
207
|
import { loadFonts, resolveRouteModule } from "akanjs/client";
|
|
@@ -228,37 +215,18 @@ const defaultFonts = userFonts.filter((font) => font.default);
|
|
|
228
215
|
if (defaultFonts.length > 1) throw new Error("[route-convention] only one default font is allowed per root layout");
|
|
229
216
|
const defaultFont = defaultFonts[0];
|
|
230
217
|
const defaultFontClassName = defaultFont ? (defaultFont.className ?? \`font-\${defaultFont.name}\`) : undefined;
|
|
218
|
+
export const theme = userLayout.theme ?? inheritedLayout.theme;
|
|
231
219
|
|
|
232
|
-
|
|
233
|
-
if (userLayout.generateHead) return userLayout.generateHead(props);
|
|
234
|
-
if (userLayout.head !== undefined) return userLayout.head;
|
|
235
|
-
if (inheritedLayout.generateHead) return inheritedLayout.generateHead(props);
|
|
236
|
-
return inheritedLayout.head;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
export async function generateMetadata(props: PageProps) {
|
|
240
|
-
if (userLayout.generateMetadata) return userLayout.generateMetadata(props);
|
|
241
|
-
if (userLayout.metadata !== undefined) return userLayout.metadata;
|
|
242
|
-
if (inheritedLayout.generateMetadata) return inheritedLayout.generateMetadata(props);
|
|
243
|
-
return inheritedLayout.metadata;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
export const NotFound = userLayout.NotFound ?? inheritedLayout.NotFound;
|
|
247
|
-
export const Error = userLayout.Error ?? inheritedLayout.Error;
|
|
248
|
-
export const pageConfig = userLayout.pageConfig ?? inheritedLayout.pageConfig;
|
|
249
|
-
|
|
250
|
-
${layoutSignature}({ children, params, searchParams }: LayoutProps) {
|
|
251
|
-
${layoutBinding} return (
|
|
220
|
+
${layoutExports}(
|
|
252
221
|
<System.Provider
|
|
253
222
|
of={GeneratedLayout as never}
|
|
254
223
|
appName=${JSON.stringify(opts.appName)}
|
|
255
224
|
${prefix ? `prefix=${JSON.stringify(prefix)}\n ` : ""}params={params}
|
|
256
225
|
manifest={userLayout.manifest ?? inheritedLayout.manifest}
|
|
257
226
|
env={env}
|
|
258
|
-
theme={
|
|
227
|
+
theme={theme}
|
|
259
228
|
fonts={loadFonts(userFonts)}
|
|
260
229
|
className={defaultFontClassName}
|
|
261
|
-
gaTrackingId={userLayout.gaTrackingId ?? inheritedLayout.gaTrackingId}
|
|
262
230
|
layoutStyle={userLayout.layoutStyle ?? inheritedLayout.layoutStyle}
|
|
263
231
|
reconnect={userLayout.reconnect ?? inheritedLayout.reconnect ?? false}
|
|
264
232
|
wsConnect={userLayout.wsConnect ?? inheritedLayout.wsConnect ?? true}
|
|
@@ -273,36 +241,13 @@ ${layoutBinding} return (
|
|
|
273
241
|
import { resolveRouteModule } from "akanjs/client";
|
|
274
242
|
import type { ReactNode } from "react";
|
|
275
243
|
${inheritedImport}${userImport}
|
|
276
|
-
|
|
277
|
-
if (userLayout.generateHead) return userLayout.generateHead(props);
|
|
278
|
-
if (userLayout.head !== undefined) return userLayout.head;
|
|
279
|
-
if (inheritedLayout.generateHead) return inheritedLayout.generateHead(props);
|
|
280
|
-
return inheritedLayout.head;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
export async function generateMetadata(props: PageProps) {
|
|
284
|
-
if (userLayout.generateMetadata) return userLayout.generateMetadata(props);
|
|
285
|
-
if (userLayout.metadata !== undefined) return userLayout.metadata;
|
|
286
|
-
if (inheritedLayout.generateMetadata) return inheritedLayout.generateMetadata(props);
|
|
287
|
-
return inheritedLayout.metadata;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
export const NotFound = userLayout.NotFound ?? inheritedLayout.NotFound;
|
|
291
|
-
export const Error = userLayout.Error ?? inheritedLayout.Error;
|
|
292
|
-
export const pageConfig = userLayout.pageConfig ?? inheritedLayout.pageConfig;
|
|
293
|
-
|
|
294
|
-
${layoutSignature}({ children, params, searchParams }: LayoutProps) {
|
|
295
|
-
${layoutBinding} return ${layoutReturn};
|
|
244
|
+
${layoutExports}${layoutReturn};
|
|
296
245
|
}
|
|
297
246
|
`;
|
|
298
247
|
await Bun.write(absPath, source);
|
|
299
248
|
return absPath;
|
|
300
249
|
}
|
|
301
250
|
|
|
302
|
-
/**
|
|
303
|
-
* When no root `page/_layout.*` exists on disk, merge a generated implicit root layout
|
|
304
|
-
* (with generated client runtime registration and optional `void st` when `lib/st.ts` exists).
|
|
305
|
-
*/
|
|
306
251
|
export async function resolveSsrPageEntries(opts: {
|
|
307
252
|
appCwdPath: string;
|
|
308
253
|
appName: string;
|
|
@@ -310,7 +255,7 @@ export async function resolveSsrPageEntries(opts: {
|
|
|
310
255
|
basePaths?: Iterable<string>;
|
|
311
256
|
}): Promise<PageEntry[]> {
|
|
312
257
|
const absPageDir = path.resolve(opts.appCwdPath, "page");
|
|
313
|
-
const hasSt = await
|
|
258
|
+
const hasSt = await Bun.file(path.join(opts.appCwdPath, "lib", "st.ts")).exists();
|
|
314
259
|
const basePaths = opts.basePaths ?? [];
|
|
315
260
|
const rootSourceAbsPath = findExplicitRootLayoutAbsPath(opts.pageKeys, opts.appCwdPath);
|
|
316
261
|
const rootBoundaries = findRootBoundaries(opts.pageKeys, opts.appCwdPath, basePaths);
|
|
@@ -325,9 +270,7 @@ export async function resolveSsrPageEntries(opts: {
|
|
|
325
270
|
.filter((key) => !rootLayoutKeys.has(key))
|
|
326
271
|
.map(async (key) => {
|
|
327
272
|
const userAbsPath = path.resolve(absPageDir, key);
|
|
328
|
-
|
|
329
|
-
// so the author writes no directive; the raw manifest becomes a build seed so its slot components (and
|
|
330
|
-
// the manifest itself) enter the client graph / RSC client manifest.
|
|
273
|
+
//? The raw manifest is a seed, so it and its slot components enter the client graph and RSC client manifest.
|
|
331
274
|
if (OVERRIDES_KEY_RE.test(key)) {
|
|
332
275
|
const moduleAbsPath = await writeGeneratedOverridesLayoutFile({
|
|
333
276
|
appCwdPath: opts.appCwdPath,
|
|
@@ -353,9 +296,19 @@ export async function resolveSsrPageEntries(opts: {
|
|
|
353
296
|
seedAbsPaths: [...new Set([boundary.sourceAbsPath, rootSourceAbsPath].filter((absPath) => absPath !== null))],
|
|
354
297
|
})),
|
|
355
298
|
);
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
299
|
+
//? A moved `_overrides.tsx` or root layout leaves a wrapper importing a file that is gone.
|
|
300
|
+
const appCwdAbsPath = path.resolve(opts.appCwdPath);
|
|
301
|
+
await Promise.all([
|
|
302
|
+
pruneGeneratedFiles(
|
|
303
|
+
path.join(appCwdAbsPath, IMPLICIT_OVERRIDES_DIR),
|
|
304
|
+
base.filter(({ key }) => OVERRIDES_KEY_RE.test(key)).map(({ moduleAbsPath }) => moduleAbsPath),
|
|
305
|
+
),
|
|
306
|
+
pruneGeneratedFiles(
|
|
307
|
+
path.join(appCwdAbsPath, IMPLICIT_LAYOUT_DIR),
|
|
308
|
+
generated.map(({ moduleAbsPath }) => moduleAbsPath),
|
|
309
|
+
),
|
|
310
|
+
]);
|
|
311
|
+
return [...base, ...generated].sort((a, b) => a.key.localeCompare(b.key));
|
|
359
312
|
}
|
|
360
313
|
|
|
361
314
|
export async function resolveSsrPageEntriesForApp(app: App, pageKeys: string[]): Promise<PageEntry[]> {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import path from "node:path";
|
|
2
3
|
import { computeRouteSeedIndex, serializeRouteSeedIndexForArtifact } from "./routeSeedIndex";
|
|
3
4
|
|
|
4
5
|
describe("computeRouteSeedIndex", () => {
|
|
@@ -19,10 +20,10 @@ describe("computeRouteSeedIndex", () => {
|
|
|
19
20
|
]);
|
|
20
21
|
|
|
21
22
|
const leaf = index.entries.find((entry) => entry.routeId === "/:lang/foo");
|
|
22
|
-
expect(leaf?.seeds).toContain("/app/page/foo.tsx");
|
|
23
|
-
expect(leaf?.seeds).not.toContain("/app/page/foo/_layout.tsx");
|
|
24
|
-
expect(leaf?.seeds).toContain("/app/.akan/generated/root-layouts/__root_layout.tsx");
|
|
25
|
-
expect(leaf?.seeds).toContain("/app/page/_layout.tsx");
|
|
23
|
+
expect(leaf?.seeds).toContain(path.resolve("/app/page/foo.tsx"));
|
|
24
|
+
expect(leaf?.seeds).not.toContain(path.resolve("/app/page/foo/_layout.tsx"));
|
|
25
|
+
expect(leaf?.seeds).toContain(path.resolve("/app/.akan/generated/root-layouts/__root_layout.tsx"));
|
|
26
|
+
expect(leaf?.seeds).toContain(path.resolve("/app/page/_layout.tsx"));
|
|
26
27
|
|
|
27
28
|
expect(() =>
|
|
28
29
|
computeRouteSeedIndex([
|
|
@@ -32,20 +33,6 @@ describe("computeRouteSeedIndex", () => {
|
|
|
32
33
|
).toThrow("route conflict");
|
|
33
34
|
});
|
|
34
35
|
|
|
35
|
-
test("keeps special route seeds outside implicit locale", () => {
|
|
36
|
-
const index = computeRouteSeedIndex([
|
|
37
|
-
{
|
|
38
|
-
key: "./__root_layout.tsx",
|
|
39
|
-
moduleAbsPath: "/app/.akan/generated/root-layouts/__root_layout.tsx",
|
|
40
|
-
seedAbsPaths: ["/app/page/_layout.tsx"],
|
|
41
|
-
},
|
|
42
|
-
{ key: "./robots.txt.tsx", moduleAbsPath: "/app/page/robots.txt.tsx" },
|
|
43
|
-
]);
|
|
44
|
-
const robots = index.entries.find((entry) => entry.routeId === "/robots.txt");
|
|
45
|
-
expect(robots?.seeds).not.toContain("/app/.akan/generated/root-layouts/__root_layout.tsx");
|
|
46
|
-
expect(robots?.seeds).not.toContain("/app/page/_layout.tsx");
|
|
47
|
-
});
|
|
48
|
-
|
|
49
36
|
test("serializes seed paths relative to the artifact directory", () => {
|
|
50
37
|
const artifactDir = "/repo/dist/apps/akan/.akan/artifact";
|
|
51
38
|
const serialized = serializeRouteSeedIndexForArtifact(
|
|
@@ -1,29 +1,13 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { assertUniqueRoutePatterns, compareRouteSpecificity, parseRouteModuleKey } from "akanjs/common";
|
|
3
|
+
import {
|
|
4
|
+
ROUTE_SEED_INDEX_JSON,
|
|
5
|
+
type RouteSeedEntry,
|
|
6
|
+
type RouteSeedIndex,
|
|
7
|
+
} from "akanjs/server/artifact/routeSeedIndexStore";
|
|
3
8
|
import type { PageEntry } from "./implicitRootLayout";
|
|
4
9
|
|
|
5
|
-
|
|
6
|
-
* Build-time utilities for the route seed index. `computeRouteSeedIndex`
|
|
7
|
-
* takes resolved `PageEntry` rows (`key` plus absolute module path — files
|
|
8
|
-
* under `app/` or the generated implicit root layout) and produces — for every
|
|
9
|
-
* route — the list of source files that seed the `"use client"` graph walk.
|
|
10
|
-
*
|
|
11
|
-
* The result is serialized to `route-seed-index.json` by
|
|
12
|
-
* `saveRouteSeedIndex` so the runtime server can call
|
|
13
|
-
* `loadRouteSeedIndex` to restore it without re-importing `pages.ts`
|
|
14
|
-
* (which no longer exists) or reparsing loader sources.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
export interface RouteSeedEntry {
|
|
18
|
-
routeId: string;
|
|
19
|
-
pattern: string;
|
|
20
|
-
seeds: string[];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface RouteSeedIndex {
|
|
24
|
-
entries: RouteSeedEntry[];
|
|
25
|
-
globalLayoutFiles: string[];
|
|
26
|
-
}
|
|
10
|
+
export { ROUTE_SEED_INDEX_JSON, type RouteSeedEntry, type RouteSeedIndex };
|
|
27
11
|
|
|
28
12
|
export type SerializedRouteSeedEntry = Pick<RouteSeedEntry, "routeId"> &
|
|
29
13
|
Partial<Pick<RouteSeedEntry, "pattern" | "seeds">>;
|
|
@@ -33,10 +17,6 @@ export interface SerializedRouteSeedIndex {
|
|
|
33
17
|
globalLayoutFiles?: string[];
|
|
34
18
|
}
|
|
35
19
|
|
|
36
|
-
/**
|
|
37
|
-
* Compute the route seed index from `PageEntry`s (disk paths or generated
|
|
38
|
-
* implicit root layout absolute paths).
|
|
39
|
-
*/
|
|
40
20
|
export function computeRouteSeedIndex(pageEntries: PageEntry[]): RouteSeedIndex {
|
|
41
21
|
const layoutsByPrefix = new Map<string, string[]>();
|
|
42
22
|
const pagesBySegments: Array<{
|
|
@@ -51,11 +31,8 @@ export function computeRouteSeedIndex(pageEntries: PageEntry[]): RouteSeedIndex
|
|
|
51
31
|
const parsed = parseRouteModuleKey(key);
|
|
52
32
|
const files = [path.resolve(moduleAbsPath), ...(seedAbsPaths ?? []).map((seed) => path.resolve(seed))];
|
|
53
33
|
if (parsed.kind === "layout" || parsed.kind === "overrides") {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const prefix = parsed.routeSegments.join("/");
|
|
57
|
-
const prev = layoutsByPrefix.get(prefix) ?? [];
|
|
58
|
-
layoutsByPrefix.set(prefix, [...prev, ...files]);
|
|
34
|
+
//? Every route under an overrides prefix needs its "use client" wrapper in the client graph, as with a layout.
|
|
35
|
+
layoutsByPrefix.getOrInsert(parsed.routeSegments.join("/"), []).push(...files);
|
|
59
36
|
} else if (parsed.kind === "page") {
|
|
60
37
|
pagesBySegments.push({
|
|
61
38
|
key,
|
|
@@ -68,9 +45,7 @@ export function computeRouteSeedIndex(pageEntries: PageEntry[]): RouteSeedIndex
|
|
|
68
45
|
}
|
|
69
46
|
assertUniqueRoutePatterns(pagesBySegments);
|
|
70
47
|
|
|
71
|
-
const
|
|
72
|
-
const globalLayoutFiles = rootLayouts;
|
|
73
|
-
|
|
48
|
+
const globalLayoutFiles = layoutsByPrefix.get("") ?? [];
|
|
74
49
|
const seedEntries: RouteSeedEntry[] = [];
|
|
75
50
|
for (const { pattern, segments, files, includeOwnLayout } of pagesBySegments) {
|
|
76
51
|
const layouts: string[] = [];
|
|
@@ -84,26 +59,18 @@ export function computeRouteSeedIndex(pageEntries: PageEntry[]): RouteSeedIndex
|
|
|
84
59
|
const routeId = pattern || "/";
|
|
85
60
|
seedEntries.push({ routeId, pattern: routeId, seeds });
|
|
86
61
|
}
|
|
87
|
-
// Static segments must beat `:param` ones
|
|
88
|
-
// `/persona/new` resolves to `[personaId]=new`. Sort once at build time
|
|
89
|
-
// so the runtime matcher can stay first-match-wins.
|
|
62
|
+
// Static segments must beat `:param` ones (`/persona/new` is not `[personaId]=new`); the runtime is first-match-wins.
|
|
90
63
|
seedEntries.sort((a, b) => compareRouteSpecificity(a.pattern, b.pattern));
|
|
91
64
|
return { entries: seedEntries, globalLayoutFiles };
|
|
92
65
|
}
|
|
93
66
|
|
|
94
|
-
export const ROUTE_SEED_INDEX_JSON = "route-seed-index.json";
|
|
95
|
-
|
|
96
67
|
export function serializeRouteSeedIndexForArtifact(
|
|
97
68
|
index: RouteSeedIndex,
|
|
98
69
|
artifactDir: string,
|
|
99
70
|
options: { production?: boolean } = {},
|
|
100
71
|
): SerializedRouteSeedIndex {
|
|
101
72
|
const normalizedArtifactDir = path.resolve(artifactDir);
|
|
102
|
-
if (options.production) {
|
|
103
|
-
return {
|
|
104
|
-
entries: index.entries.map((entry) => ({ routeId: entry.routeId })),
|
|
105
|
-
};
|
|
106
|
-
}
|
|
73
|
+
if (options.production) return { entries: index.entries.map((entry) => ({ routeId: entry.routeId })) };
|
|
107
74
|
return {
|
|
108
75
|
entries: index.entries.map((entry) => ({
|
|
109
76
|
...entry,
|
package/biome.base.json
CHANGED
|
@@ -137,7 +137,8 @@
|
|
|
137
137
|
"!**/*.spec.tsx",
|
|
138
138
|
"!**/*.constant.ts",
|
|
139
139
|
"!**/common/**",
|
|
140
|
-
"!**/env/**"
|
|
140
|
+
"!**/env/**",
|
|
141
|
+
"!{apps,libs}/*/native/**"
|
|
141
142
|
],
|
|
142
143
|
"plugins": ["./node_modules/@akanjs/devkit/lint/no-throw-raw-error.grit"]
|
|
143
144
|
},
|
|
@@ -154,7 +155,12 @@
|
|
|
154
155
|
]
|
|
155
156
|
},
|
|
156
157
|
{
|
|
157
|
-
"includes": [
|
|
158
|
+
"includes": [
|
|
159
|
+
"{apps,libs}/**/page/**/*.ts",
|
|
160
|
+
"{apps,libs}/**/page/**/*.tsx",
|
|
161
|
+
"{apps,libs}/**/*.Unit.tsx",
|
|
162
|
+
"{apps,libs}/**/*.View.tsx"
|
|
163
|
+
],
|
|
158
164
|
"plugins": ["./node_modules/@akanjs/devkit/lint/non-scalar-props-restricted.grit"]
|
|
159
165
|
},
|
|
160
166
|
{
|
|
@@ -170,6 +176,10 @@
|
|
|
170
176
|
"includes": ["{apps,libs}/**/*.store.ts"],
|
|
171
177
|
"plugins": ["./node_modules/@akanjs/devkit/lint/no-return-in-store-action.grit"]
|
|
172
178
|
},
|
|
179
|
+
{
|
|
180
|
+
"includes": ["{apps,libs}/**/*.constant.ts"],
|
|
181
|
+
"plugins": ["./node_modules/@akanjs/devkit/lint/no-static-in-object-light-model.grit"]
|
|
182
|
+
},
|
|
173
183
|
{
|
|
174
184
|
"includes": ["{apps,libs}/**/*.ts", "{apps,libs}/**/*.tsx"],
|
|
175
185
|
"plugins": ["./node_modules/@akanjs/devkit/lint/no-deprecated-log-level.grit"]
|
|
@@ -201,6 +211,33 @@
|
|
|
201
211
|
],
|
|
202
212
|
"plugins": ["./node_modules/@akanjs/devkit/lint/no-init-fetch-in-client.grit"]
|
|
203
213
|
},
|
|
214
|
+
{
|
|
215
|
+
"includes": [
|
|
216
|
+
"{apps,libs}/**/*.ts",
|
|
217
|
+
"{apps,libs}/**/*.tsx",
|
|
218
|
+
"!**/*.test.ts",
|
|
219
|
+
"!**/*.test.tsx",
|
|
220
|
+
"!**/*.spec.ts",
|
|
221
|
+
"!**/*.spec.tsx"
|
|
222
|
+
],
|
|
223
|
+
"plugins": [
|
|
224
|
+
"./node_modules/@akanjs/devkit/lint/no-document-cookie.grit",
|
|
225
|
+
"./node_modules/@akanjs/devkit/lint/no-web-storage.grit"
|
|
226
|
+
]
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"includes": [
|
|
230
|
+
"{apps,libs}/**/*.ts",
|
|
231
|
+
"{apps,libs}/**/*.tsx",
|
|
232
|
+
"!{apps,libs}/**/webkit/**",
|
|
233
|
+
"!{apps,libs}/*/native/**",
|
|
234
|
+
"!**/*.test.ts",
|
|
235
|
+
"!**/*.test.tsx",
|
|
236
|
+
"!**/*.spec.ts",
|
|
237
|
+
"!**/*.spec.tsx"
|
|
238
|
+
],
|
|
239
|
+
"plugins": ["./node_modules/@akanjs/devkit/lint/no-web-only-api-outside-webkit.grit"]
|
|
240
|
+
},
|
|
204
241
|
{
|
|
205
242
|
"includes": [
|
|
206
243
|
"**/ui/**/*.ts",
|
package/biomeBase.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
/** `extends` target for a workspace `biome.json`; Biome resolves it through node_modules. */
|
|
2
2
|
export const biomeBaseConfig = "@akanjs/devkit/biome.base.json";
|
|
3
3
|
|
|
4
|
-
// Biome moves rules between groups across minors
|
|
5
|
-
// `
|
|
6
|
-
// Biome disagrees with the shipped base config therefore fails to load it at all, which is why the version is
|
|
7
|
-
// pinned here instead of resolved to latest at create time. Bump this and `biome.base.json` in one commit, and run
|
|
8
|
-
// `biome migrate --write` in the workspace root and in `pkgs/@akanjs/devkit` so both configs move together.
|
|
4
|
+
// Pinned: Biome moves rules between groups across minors and a stale position is a hard config error. Bump it with
|
|
5
|
+
// `biome.base.json` in one commit, running `biome migrate --write` in the workspace root and in this package.
|
|
9
6
|
export const biomeVersion = "2.5.12";
|