@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.
Files changed (341) hide show
  1. package/DEV_RUNTIME_KNOBS.md +44 -26
  2. package/README.ko.md +21 -7
  3. package/README.md +17 -8
  4. package/abstractDoc.test.ts +6 -15
  5. package/abstractDoc.ts +6 -8
  6. package/agentsIndex.test.ts +4 -7
  7. package/agentsIndex.ts +6 -22
  8. package/akanApp/BackendImportGraph.test.ts +8 -22
  9. package/akanApp/BackendImportGraph.ts +1 -20
  10. package/akanApp/akanApp.host.test.ts +56 -3
  11. package/akanApp/akanApp.host.ts +218 -278
  12. package/akanApp/devBootLatch.test.ts +39 -0
  13. package/akanApp/devBootLatch.ts +39 -0
  14. package/akanApp/devHostPolicy.test.ts +81 -35
  15. package/akanApp/devHostPolicy.ts +41 -102
  16. package/akanConfig/akanBin.test.ts +62 -0
  17. package/akanConfig/akanBin.ts +78 -0
  18. package/akanConfig/akanConfig.test.ts +439 -141
  19. package/akanConfig/akanConfig.ts +359 -275
  20. package/akanConfig/index.ts +1 -0
  21. package/akanConfig/types.ts +22 -8
  22. package/akanContext.ts +110 -160
  23. package/akanMcpContract.ts +28 -73
  24. package/appSelectionMemory.ts +1 -4
  25. package/applicationBuildReporter.ts +37 -5
  26. package/applicationBuildRunner.test.ts +41 -5
  27. package/applicationBuildRunner.ts +71 -63
  28. package/applicationTestPreload.ts +3 -12
  29. package/artifact/implicitRootLayout.test.ts +32 -30
  30. package/artifact/implicitRootLayout.ts +58 -106
  31. package/artifact/routeSeedIndex.test.ts +7 -6
  32. package/artifact/routeSeedIndex.ts +11 -44
  33. package/biome.base.json +35 -2
  34. package/biomeBase.ts +2 -5
  35. package/cloud/cloudApi.test.ts +202 -0
  36. package/cloud/cloudApi.ts +77 -39
  37. package/cloud/configLock.test.ts +64 -0
  38. package/cloud/configLock.ts +96 -0
  39. package/cloud/constants.ts +26 -5
  40. package/cloud/globalConfig.ts +71 -32
  41. package/codeAgent/agent/AkanCodeServices.ts +97 -0
  42. package/codeAgent/agent/AkanContextFiles.ts +94 -0
  43. package/codeAgent/agent/CodeAgent.ts +551 -0
  44. package/codeAgent/agent/CodeAgentAsks.ts +66 -0
  45. package/codeAgent/agent/CodeAgentEventMapper.ts +225 -0
  46. package/codeAgent/agent/CodeAgentGate.ts +93 -0
  47. package/codeAgent/agent/CodeAgentProxy.ts +82 -0
  48. package/codeAgent/agent/CodeAgentRpcHost.ts +103 -0
  49. package/codeAgent/agent/CodeAgentRpcListener.ts +109 -0
  50. package/codeAgent/agent/CodeAgentStreamPrinter.ts +86 -0
  51. package/codeAgent/agent/CodeAgentSuspended.ts +85 -0
  52. package/codeAgent/agent/CodeAgentUi.ts +47 -0
  53. package/codeAgent/agent/CodeMailbox.ts +148 -0
  54. package/codeAgent/agent/CodeSessionFork.ts +56 -0
  55. package/codeAgent/agent/CodeSessionIndex.ts +78 -0
  56. package/codeAgent/agent/SubagentPool.ts +214 -0
  57. package/codeAgent/agent/akanCodeModel.ts +47 -0
  58. package/codeAgent/agent/akanCodePaths.ts +31 -0
  59. package/codeAgent/agent/akanContextFiles.test.ts +114 -0
  60. package/codeAgent/agent/akanSystemPrompt.ts +46 -0
  61. package/codeAgent/agent/codeAgent.test.ts +178 -0
  62. package/codeAgent/agent/codeAgentContract.test.ts +523 -0
  63. package/codeAgent/agent/codeAgentProxy.test.ts +74 -0
  64. package/codeAgent/agent/codeAgentRpcListener.test.ts +113 -0
  65. package/codeAgent/agent/codeAgentSuspended.test.ts +47 -0
  66. package/codeAgent/agent/codeMailbox.test.ts +76 -0
  67. package/codeAgent/agent/codeSessionFork.test.ts +58 -0
  68. package/codeAgent/agent/codeSessionIndex.test.ts +77 -0
  69. package/codeAgent/agent/consoleToStderr.ts +8 -0
  70. package/codeAgent/agent/jsonLines.ts +29 -0
  71. package/codeAgent/agent/stringArg.ts +5 -0
  72. package/codeAgent/feedback/DevLogFeedback.ts +82 -0
  73. package/codeAgent/feedback/PreviewChrome.ts +82 -0
  74. package/codeAgent/feedback/PreviewFeedback.ts +46 -0
  75. package/codeAgent/feedback/PreviewView.ts +95 -0
  76. package/codeAgent/feedback/TurnFeedback.ts +98 -0
  77. package/codeAgent/feedback/VerifyFeedback.ts +40 -0
  78. package/codeAgent/feedback/previewChrome.test.ts +73 -0
  79. package/codeAgent/index.ts +34 -0
  80. package/codeAgent/skills/akan-code/SKILL.md +130 -0
  81. package/codeAgent/skills/akan-debug/SKILL.md +42 -0
  82. package/codeAgent/skills/akan-deps/SKILL.md +50 -0
  83. package/codeAgent/skills/akan-err/SKILL.md +39 -0
  84. package/codeAgent/skills/akan-module/SKILL.md +45 -0
  85. package/codeAgent/skills/akan-scaffold/SKILL.md +76 -0
  86. package/codeAgent/skills/akan-scalar/SKILL.md +52 -0
  87. package/codeAgent/skills/akan-service/SKILL.md +34 -0
  88. package/codeAgent/skills/akan-store/SKILL.md +67 -0
  89. package/codeAgent/skills/akan-ui/SKILL.md +63 -0
  90. package/codeAgent/skills/akan-validate/SKILL.md +44 -0
  91. package/codeAgent/tools/AkanCli.ts +50 -0
  92. package/codeAgent/tools/AkanCodePlugins.ts +136 -0
  93. package/codeAgent/tools/AkanEditScope.ts +48 -0
  94. package/codeAgent/tools/AkanEnvKeys.ts +50 -0
  95. package/codeAgent/tools/AkanToolPack.ts +92 -0
  96. package/codeAgent/tools/AkanVerifier.ts +48 -0
  97. package/codeAgent/tools/AskToolPack.ts +99 -0
  98. package/codeAgent/tools/MailToolPack.ts +76 -0
  99. package/codeAgent/tools/McpClient.ts +187 -0
  100. package/codeAgent/tools/McpOAuth.ts +233 -0
  101. package/codeAgent/tools/McpOAuthLoopback.ts +64 -0
  102. package/codeAgent/tools/McpServerConfig.ts +129 -0
  103. package/codeAgent/tools/McpSignIn.ts +158 -0
  104. package/codeAgent/tools/McpTokenStore.ts +81 -0
  105. package/codeAgent/tools/McpToolPack.ts +199 -0
  106. package/codeAgent/tools/SessionToolPack.ts +91 -0
  107. package/codeAgent/tools/WebToolPack.ts +147 -0
  108. package/codeAgent/tools/akanCodePlugins.test.ts +677 -0
  109. package/codeAgent/tools/askToolPack.test.ts +116 -0
  110. package/codeAgent/tools/mcpOAuth.test.ts +612 -0
  111. package/codeAgent/tools/mcpServerConfig.test.ts +212 -0
  112. package/codegenLock.test.ts +4 -13
  113. package/codegenLock.ts +3 -17
  114. package/commandDecorators/argMeta.ts +1 -5
  115. package/commandDecorators/camelToKebabCase.ts +1 -0
  116. package/commandDecorators/command.ts +46 -102
  117. package/commandDecorators/commandBuilder.ts +30 -61
  118. package/commandDecorators/commandDecorators.test.ts +2 -10
  119. package/commandDecorators/dependencyBuilder.ts +10 -1
  120. package/commandDecorators/helpFormatter.ts +68 -145
  121. package/commandDecorators/index.ts +0 -2
  122. package/commandDecorators/targetMeta.ts +9 -7
  123. package/createTunnel.ts +7 -14
  124. package/csrE2e/csrE2eHarness.fixture.ts +505 -0
  125. package/csrE2e/csrFastRefresh.e2e.test.ts +233 -0
  126. package/csrE2e/csrPageStack.e2e.test.ts +252 -0
  127. package/csrE2e/csrPatchFirst.e2e.test.ts +87 -0
  128. package/csrE2e/csrRootLayout.e2e.test.ts +134 -0
  129. package/csrE2e/devHmrLatency.e2e.test.ts +391 -0
  130. package/csrE2e/registryVendor.e2e.test.ts +221 -0
  131. package/csrE2e/ssrRegistry.e2e.test.ts +644 -0
  132. package/dependencyScanner.test.ts +4 -16
  133. package/dependencyScanner.ts +31 -80
  134. package/devkitUtils.test.ts +48 -20
  135. package/executors.linkFallback.test.ts +103 -0
  136. package/executors.test.ts +176 -106
  137. package/executors.ts +212 -498
  138. package/fileEditor.ts +9 -37
  139. package/{getDirname.test.ts → fileSys.test.ts} +5 -3
  140. package/fileSys.ts +22 -9
  141. package/formSetterScanner.ts +3 -16
  142. package/frontendBuild/allRoutesBuilder.ts +7 -21
  143. package/frontendBuild/autoImportSync.test.ts +27 -38
  144. package/frontendBuild/autoImportSync.ts +31 -65
  145. package/frontendBuild/buildRouteClient.test.ts +123 -37
  146. package/frontendBuild/bundleDefine.test.ts +12 -0
  147. package/frontendBuild/bundleDefine.ts +16 -0
  148. package/frontendBuild/clientBuildTypes.ts +5 -36
  149. package/frontendBuild/clientEntriesBundler.ts +9 -40
  150. package/frontendBuild/clientEntryDiscovery.test.ts +112 -0
  151. package/frontendBuild/clientEntryDiscovery.ts +122 -110
  152. package/frontendBuild/csrArtifactBuilder.ts +9 -31
  153. package/frontendBuild/csrDevArtifactWriter.test.ts +151 -0
  154. package/frontendBuild/csrDevArtifactWriter.ts +307 -0
  155. package/frontendBuild/csrDevBundler.test.ts +66 -0
  156. package/frontendBuild/csrDevBundler.ts +95 -0
  157. package/frontendBuild/csrDevFactoryCheck.test.ts +33 -0
  158. package/frontendBuild/csrDevFactoryCheck.ts +39 -0
  159. package/frontendBuild/csrDevModuleCompiler.test.ts +293 -0
  160. package/frontendBuild/csrDevModuleCompiler.ts +422 -0
  161. package/frontendBuild/csrDevPatcher.test.ts +572 -0
  162. package/frontendBuild/csrDevPatcher.ts +548 -0
  163. package/frontendBuild/csrDevPaths.ts +145 -0
  164. package/frontendBuild/csrDevResolver.test.ts +239 -0
  165. package/frontendBuild/csrDevResolver.ts +363 -0
  166. package/frontendBuild/csrDevTypes.ts +61 -0
  167. package/frontendBuild/csrEntryFiles.ts +24 -0
  168. package/frontendBuild/cssCandidateCache.test.ts +0 -6
  169. package/frontendBuild/cssCandidateCache.ts +6 -41
  170. package/frontendBuild/cssCompiler.ts +31 -82
  171. package/frontendBuild/cssImportResolver.ts +6 -19
  172. package/frontendBuild/devChangePlanner.ts +7 -10
  173. package/frontendBuild/devGeneratedIndexSync.test.ts +12 -27
  174. package/frontendBuild/devGeneratedIndexSync.ts +1 -4
  175. package/frontendBuild/devRegistryBundler.ts +192 -0
  176. package/frontendBuild/fontOptimizer.test.ts +6 -14
  177. package/frontendBuild/fontOptimizer.ts +15 -72
  178. package/frontendBuild/fontPruner.test.ts +5 -10
  179. package/frontendBuild/fontPruner.ts +5 -20
  180. package/frontendBuild/frontendBuild.test.ts +57 -70
  181. package/frontendBuild/hmrChangeClassifier.ts +2 -2
  182. package/frontendBuild/hmrWatcher.test.ts +59 -32
  183. package/frontendBuild/hmrWatcher.ts +31 -74
  184. package/frontendBuild/index.ts +4 -0
  185. package/frontendBuild/pagesBundleBuilder.ts +28 -30
  186. package/frontendBuild/pagesEntrySourceGenerator.ts +56 -6
  187. package/frontendBuild/precompressArtifacts.ts +5 -18
  188. package/frontendBuild/registryMetadataFingerprint.test.ts +63 -0
  189. package/frontendBuild/registryMetadataFingerprint.ts +28 -0
  190. package/frontendBuild/routeClientBuilder.ts +100 -62
  191. package/frontendBuild/routesManifestArtifactSerializer.ts +5 -36
  192. package/frontendBuild/serverGraphFile.test.ts +77 -0
  193. package/frontendBuild/serverGraphFile.ts +93 -0
  194. package/frontendBuild/sourceMtimeIndex.test.ts +41 -97
  195. package/frontendBuild/sourceMtimeIndex.ts +19 -130
  196. package/frontendBuild/ssrBaseArtifactBuilder.ts +19 -13
  197. package/frontendBuild/ssrDevBundler.ts +85 -0
  198. package/frontendBuild/styleContract.ts +1 -8
  199. package/frontendBuild/themeValidator.ts +4 -26
  200. package/frontendBuild/watchRootResolver.test.ts +23 -42
  201. package/frontendBuild/watchRootResolver.ts +5 -27
  202. package/getRelatedCnsts.ts +12 -92
  203. package/incrementalBuilder/buildBatch.proc.ts +147 -63
  204. package/incrementalBuilder/buildBatchProtocol.ts +13 -38
  205. package/incrementalBuilder/buildBatchRunner.test.ts +65 -0
  206. package/incrementalBuilder/buildBatchRunner.ts +26 -38
  207. package/incrementalBuilder/builderChannel.test.ts +24 -49
  208. package/incrementalBuilder/builderChannel.ts +6 -37
  209. package/incrementalBuilder/builderRequestRouter.test.ts +0 -8
  210. package/incrementalBuilder/builderRequestRouter.ts +5 -24
  211. package/incrementalBuilder/builderWorkQueue.test.ts +94 -0
  212. package/incrementalBuilder/builderWorkQueue.ts +126 -0
  213. package/incrementalBuilder/devWatchBatch.test.ts +21 -24
  214. package/incrementalBuilder/incrementalBuilder.host.test.ts +53 -56
  215. package/incrementalBuilder/incrementalBuilder.host.ts +68 -81
  216. package/incrementalBuilder/incrementalBuilder.proc.test.ts +270 -0
  217. package/incrementalBuilder/incrementalBuilder.proc.ts +596 -208
  218. package/index.ts +5 -19
  219. package/integration/devResourceProbe.ts +50 -56
  220. package/integration/devStability.integration.test.ts +105 -199
  221. package/integration/devStabilityHarness.test.ts +13 -28
  222. package/integration/devStabilityHarness.ts +65 -330
  223. package/integration/ssrMemoryProbe.ts +41 -167
  224. package/libSource.test.ts +4 -15
  225. package/libSource.ts +19 -31
  226. package/lint/__fixtures__/no-document-cookie/bad.tsx +4 -0
  227. package/lint/__fixtures__/no-document-cookie/good.tsx +7 -0
  228. package/lint/__fixtures__/no-web-only-api-outside-webkit/bad.tsx +7 -0
  229. package/lint/__fixtures__/no-web-only-api-outside-webkit/good.tsx +11 -0
  230. package/lint/__fixtures__/no-web-storage/bad.tsx +6 -0
  231. package/lint/__fixtures__/no-web-storage/good.tsx +7 -0
  232. package/lint/gritRules.test.ts +8 -24
  233. package/lint/no-deep-internal-import.grit +2 -2
  234. package/lint/no-document-cookie.grit +29 -0
  235. package/lint/no-redeclare-predefined-endpoint.grit +2 -1
  236. package/lint/no-web-only-api-outside-webkit.grit +44 -0
  237. package/lint/no-web-storage.grit +30 -0
  238. package/linter.test.ts +1 -2
  239. package/linter.ts +26 -116
  240. package/mobile/desktopBin.test.ts +182 -0
  241. package/mobile/desktopBin.ts +144 -0
  242. package/mobile/desktopServerStage.test.ts +252 -0
  243. package/mobile/desktopServerStage.ts +178 -0
  244. package/mobile/index.ts +9 -0
  245. package/mobile/mobileTarget.test.ts +57 -8
  246. package/mobile/mobileTarget.ts +45 -26
  247. package/mobile/nativeApi.test.ts +30 -0
  248. package/mobile/nativeApi.ts +40 -0
  249. package/mobile/nativeApp.test.ts +184 -0
  250. package/mobile/nativeApp.ts +454 -0
  251. package/mobile/nativeAppLine.test.ts +69 -0
  252. package/mobile/nativeAppLine.ts +71 -0
  253. package/mobile/nativeConfig.test.ts +438 -0
  254. package/mobile/nativeConfig.ts +294 -0
  255. package/mobile/nativePluginFolders.test.ts +81 -0
  256. package/mobile/nativePluginFolders.ts +60 -0
  257. package/mobile/nativeWebDir.test.ts +80 -0
  258. package/mobile/nativeWebDir.ts +66 -0
  259. package/mobile/placeholderAppId.test.ts +13 -0
  260. package/mobile/placeholderAppId.ts +16 -0
  261. package/mobile/usageDescriptions.test.ts +27 -0
  262. package/mobile/usageDescriptions.ts +24 -0
  263. package/package.json +11 -11
  264. package/packageExports.test.ts +3 -18
  265. package/packageExportsMap.ts +3 -19
  266. package/piDependencyPins.test.ts +46 -0
  267. package/platformTest/BunTestReport.test.ts +60 -0
  268. package/platformTest/BunTestReport.ts +65 -0
  269. package/platformTest/IdleWatchedProcess.test.ts +56 -0
  270. package/platformTest/IdleWatchedProcess.ts +73 -0
  271. package/platformTest/LinuxDockerTarget.ts +105 -0
  272. package/platformTest/PlatformTestRun.test.ts +64 -0
  273. package/platformTest/PlatformTestRun.ts +141 -0
  274. package/platformTest/PlatformTestTarget.ts +142 -0
  275. package/platformTest/TestSnapshot.test.ts +72 -0
  276. package/platformTest/TestSnapshot.ts +103 -0
  277. package/platformTest/WindowsSshTarget.ts +151 -0
  278. package/prompter.ts +3 -13
  279. package/qualityScanner.test.ts +9 -26
  280. package/qualityScanner.ts +200 -286
  281. package/recipeScanner.test.ts +7 -12
  282. package/recipeScanner.ts +9 -38
  283. package/repoIdentity.ts +2 -9
  284. package/routeSourceValidator.test.ts +0 -1
  285. package/routeSourceValidator.ts +7 -42
  286. package/scanInfo.ts +91 -225
  287. package/selectModel.ts +1 -2
  288. package/semver.ts +3 -5
  289. package/slicePlanner.test.ts +6 -24
  290. package/slicePlanner.ts +11 -32
  291. package/spinner.test.ts +3 -8
  292. package/spinner.ts +4 -16
  293. package/sshTunnel.ts +1 -6
  294. package/ssrScanner.test.ts +1 -6
  295. package/ssrScanner.ts +103 -113
  296. package/stripAnsi.ts +3 -0
  297. package/subspace.test.ts +9 -25
  298. package/subspace.ts +48 -115
  299. package/subspaceConfig.ts +3 -11
  300. package/testHelpers.ts +194 -0
  301. package/transforms/asyncDefaultExportDetector.ts +3 -11
  302. package/transforms/barrelAnalyzer.ts +43 -135
  303. package/transforms/barrelImportsPlugin.ts +38 -175
  304. package/transforms/externalizeFrameworkPlugin.test.ts +39 -118
  305. package/transforms/externalizeFrameworkPlugin.ts +13 -87
  306. package/transforms/moduleSyntax.ts +32 -0
  307. package/transforms/rscUseClientTransform.ts +42 -20
  308. package/transforms/serverEnvPlugin.test.ts +103 -0
  309. package/transforms/serverEnvPlugin.ts +41 -0
  310. package/transforms/transforms.test.ts +166 -44
  311. package/transforms/tsconfigPackageResolver.test.ts +6 -24
  312. package/transforms/useClientBundlePlugin.ts +6 -23
  313. package/typeChecker.ts +7 -63
  314. package/types.ts +2 -0
  315. package/uploadRelease.ts +10 -25
  316. package/workflow/artifacts.ts +18 -0
  317. package/workflow/executor.test.ts +1 -12
  318. package/workflow/executor.ts +101 -110
  319. package/workflow/index.ts +0 -1
  320. package/workflow/moduleIndex.test.ts +4 -15
  321. package/workflow/moduleIndex.ts +9 -49
  322. package/workflow/plan.ts +18 -25
  323. package/workflow/render.ts +80 -96
  324. package/workflow/rolloutGate.test.ts +1 -1
  325. package/workflow/source.ts +52 -69
  326. package/workflow/sourceAst.ts +50 -0
  327. package/workspaceLayout.test.ts +18 -3
  328. package/workspaceLayout.ts +22 -23
  329. package/abstractCompactor.ts +0 -112
  330. package/aiEditor.test.ts +0 -60
  331. package/aiEditor.ts +0 -387
  332. package/applicationReleasePackager.ts +0 -209
  333. package/capacitorApp.test.ts +0 -372
  334. package/capacitorApp.ts +0 -1648
  335. package/commandDecorators/commandMeta.ts +0 -7
  336. package/commandDecorators/types.ts +0 -10
  337. package/getDirname.ts +0 -4
  338. package/guideline.ts +0 -19
  339. package/local/optimize-resource/ipcprobe/child.ts +0 -1
  340. package/local/optimize-resource/ipcprobe/parent.ts +0 -11
  341. package/workflow/primitive.ts +0 -59
@@ -1,4 +1,4 @@
1
- import { mkdir } from "node:fs/promises";
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
- * A `_overrides.tsx` manifest is a plain, server-safe module (`export default override({ Modal: BrandModal })`)
25
- * with no `"use client"` directive. The `UiOverrideProvider` that consumes it is a client component, so the
26
- * build emits a `"use client"` wrapper layout that reads the manifest's default map and mounts the provider
27
- * around the subtree. As a normal `"use client"` module the wrapper participates in client-entry discovery and
28
- * the RSC client manifest, so the server (as a client reference) and the client (as the real component) both
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(userSpecifier)};
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
- const hasExplicitRootBoundary = [...boundaries.values()].some((boundary) => boundary.segments.length === 0);
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 = implicitDictionaryMacroAbsPath(appCwdPath);
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
- await mkdir(path.dirname(absPath), { recursive: true });
167
- const dictMacroRel = dictMacroAbsPath
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 inheritedSourceRel = inheritedSourceAbsPath
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
- // Both user modules go through `resolveRouteModule`: a `rootLayout()` chain and the legacy named exports read
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 = routePrefixForSegments(opts.boundary.segments);
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 the CSR bundle calls an async layout and awaits its node the way
204
- // `RenderLayer` does for pages. The RSC render keeps the element: awaiting there would hold the shell behind
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";
@@ -229,26 +216,7 @@ if (defaultFonts.length > 1) throw new Error("[route-convention] only one defaul
229
216
  const defaultFont = defaultFonts[0];
230
217
  const defaultFontClassName = defaultFont ? (defaultFont.className ?? \`font-\${defaultFont.name}\`) : undefined;
231
218
 
232
- export async function generateHead(props: PageProps) {
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 (
219
+ ${layoutExports}(
252
220
  <System.Provider
253
221
  of={GeneratedLayout as never}
254
222
  appName=${JSON.stringify(opts.appName)}
@@ -258,7 +226,6 @@ ${layoutBinding} return (
258
226
  theme={userLayout.theme ?? inheritedLayout.theme}
259
227
  fonts={loadFonts(userFonts)}
260
228
  className={defaultFontClassName}
261
- gaTrackingId={userLayout.gaTrackingId ?? inheritedLayout.gaTrackingId}
262
229
  layoutStyle={userLayout.layoutStyle ?? inheritedLayout.layoutStyle}
263
230
  reconnect={userLayout.reconnect ?? inheritedLayout.reconnect ?? false}
264
231
  wsConnect={userLayout.wsConnect ?? inheritedLayout.wsConnect ?? true}
@@ -273,36 +240,13 @@ ${layoutBinding} return (
273
240
  import { resolveRouteModule } from "akanjs/client";
274
241
  import type { ReactNode } from "react";
275
242
  ${inheritedImport}${userImport}
276
- export async function generateHead(props: PageProps) {
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};
243
+ ${layoutExports}${layoutReturn};
296
244
  }
297
245
  `;
298
246
  await Bun.write(absPath, source);
299
247
  return absPath;
300
248
  }
301
249
 
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
250
  export async function resolveSsrPageEntries(opts: {
307
251
  appCwdPath: string;
308
252
  appName: string;
@@ -310,7 +254,7 @@ export async function resolveSsrPageEntries(opts: {
310
254
  basePaths?: Iterable<string>;
311
255
  }): Promise<PageEntry[]> {
312
256
  const absPageDir = path.resolve(opts.appCwdPath, "page");
313
- const hasSt = await appHasStModule(opts.appCwdPath);
257
+ const hasSt = await Bun.file(path.join(opts.appCwdPath, "lib", "st.ts")).exists();
314
258
  const basePaths = opts.basePaths ?? [];
315
259
  const rootSourceAbsPath = findExplicitRootLayoutAbsPath(opts.pageKeys, opts.appCwdPath);
316
260
  const rootBoundaries = findRootBoundaries(opts.pageKeys, opts.appCwdPath, basePaths);
@@ -325,9 +269,7 @@ export async function resolveSsrPageEntries(opts: {
325
269
  .filter((key) => !rootLayoutKeys.has(key))
326
270
  .map(async (key) => {
327
271
  const userAbsPath = path.resolve(absPageDir, key);
328
- // `_overrides.tsx` is served through a generated `"use client"` wrapper layout that mounts the provider,
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.
272
+ //? The raw manifest is a seed, so it and its slot components enter the client graph and RSC client manifest.
331
273
  if (OVERRIDES_KEY_RE.test(key)) {
332
274
  const moduleAbsPath = await writeGeneratedOverridesLayoutFile({
333
275
  appCwdPath: opts.appCwdPath,
@@ -353,9 +295,19 @@ export async function resolveSsrPageEntries(opts: {
353
295
  seedAbsPaths: [...new Set([boundary.sourceAbsPath, rootSourceAbsPath].filter((absPath) => absPath !== null))],
354
296
  })),
355
297
  );
356
- const entries = [...base, ...generated];
357
- entries.sort((a, b) => a.key.localeCompare(b.key));
358
- return entries;
298
+ //? A moved `_overrides.tsx` or root layout leaves a wrapper importing a file that is gone.
299
+ const appCwdAbsPath = path.resolve(opts.appCwdPath);
300
+ await Promise.all([
301
+ pruneGeneratedFiles(
302
+ path.join(appCwdAbsPath, IMPLICIT_OVERRIDES_DIR),
303
+ base.filter(({ key }) => OVERRIDES_KEY_RE.test(key)).map(({ moduleAbsPath }) => moduleAbsPath),
304
+ ),
305
+ pruneGeneratedFiles(
306
+ path.join(appCwdAbsPath, IMPLICIT_LAYOUT_DIR),
307
+ generated.map(({ moduleAbsPath }) => moduleAbsPath),
308
+ ),
309
+ ]);
310
+ return [...base, ...generated].sort((a, b) => a.key.localeCompare(b.key));
359
311
  }
360
312
 
361
313
  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([
@@ -42,8 +43,8 @@ describe("computeRouteSeedIndex", () => {
42
43
  { key: "./robots.txt.tsx", moduleAbsPath: "/app/page/robots.txt.tsx" },
43
44
  ]);
44
45
  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");
46
+ expect(robots?.seeds).not.toContain(path.resolve("/app/.akan/generated/root-layouts/__root_layout.tsx"));
47
+ expect(robots?.seeds).not.toContain(path.resolve("/app/page/_layout.tsx"));
47
48
  });
48
49
 
49
50
  test("serializes seed paths relative to the artifact directory", () => {
@@ -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
- // Overrides seed the client graph like layouts: every route under the prefix must pull the generated
55
- // `"use client"` override wrapper (and its slot components) into the client bundle / RSC client manifest.
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 rootLayouts = layoutsByPrefix.get("") ?? [];
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 at match time, otherwise
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": ["{apps,libs}/**/page/**/*.ts", "{apps,libs}/**/page/**/*.tsx"],
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
  {
@@ -201,6 +207,33 @@
201
207
  ],
202
208
  "plugins": ["./node_modules/@akanjs/devkit/lint/no-init-fetch-in-client.grit"]
203
209
  },
210
+ {
211
+ "includes": [
212
+ "{apps,libs}/**/*.ts",
213
+ "{apps,libs}/**/*.tsx",
214
+ "!**/*.test.ts",
215
+ "!**/*.test.tsx",
216
+ "!**/*.spec.ts",
217
+ "!**/*.spec.tsx"
218
+ ],
219
+ "plugins": [
220
+ "./node_modules/@akanjs/devkit/lint/no-document-cookie.grit",
221
+ "./node_modules/@akanjs/devkit/lint/no-web-storage.grit"
222
+ ]
223
+ },
224
+ {
225
+ "includes": [
226
+ "{apps,libs}/**/*.ts",
227
+ "{apps,libs}/**/*.tsx",
228
+ "!{apps,libs}/**/webkit/**",
229
+ "!{apps,libs}/*/native/**",
230
+ "!**/*.test.ts",
231
+ "!**/*.test.tsx",
232
+ "!**/*.spec.ts",
233
+ "!**/*.spec.tsx"
234
+ ],
235
+ "plugins": ["./node_modules/@akanjs/devkit/lint/no-web-only-api-outside-webkit.grit"]
236
+ },
204
237
  {
205
238
  "includes": [
206
239
  "**/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 — `noUnnecessaryConditions` sat in `nursery` at 2.4 and moved to
5
- // `suspicious` at 2.5 — and the stale position is a hard "unknown key" error, not a warning. A workspace whose
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";