@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.
Files changed (289) hide show
  1. package/README.ko.md +2 -2
  2. package/README.md +3 -2
  3. package/abstractDoc.test.ts +6 -15
  4. package/abstractDoc.ts +6 -8
  5. package/agentsIndex.test.ts +4 -7
  6. package/agentsIndex.ts +6 -22
  7. package/akanApp/BackendImportGraph.test.ts +8 -22
  8. package/akanApp/BackendImportGraph.ts +1 -20
  9. package/akanApp/akanApp.host.test.ts +1 -3
  10. package/akanApp/akanApp.host.ts +82 -254
  11. package/akanApp/devHostPolicy.test.ts +10 -34
  12. package/akanApp/devHostPolicy.ts +12 -98
  13. package/akanConfig/akanConfig.test.ts +142 -106
  14. package/akanConfig/akanConfig.ts +147 -191
  15. package/akanConfig/types.ts +4 -3
  16. package/akanContext.ts +110 -160
  17. package/akanMcpContract.ts +28 -73
  18. package/appSelectionMemory.ts +1 -4
  19. package/applicationBuildReporter.ts +37 -5
  20. package/applicationBuildRunner.test.ts +5 -3
  21. package/applicationBuildRunner.ts +54 -61
  22. package/applicationTestPreload.ts +3 -12
  23. package/artifact/implicitRootLayout.test.ts +32 -30
  24. package/artifact/implicitRootLayout.ts +58 -106
  25. package/artifact/routeSeedIndex.test.ts +7 -6
  26. package/artifact/routeSeedIndex.ts +11 -44
  27. package/biome.base.json +32 -1
  28. package/biomeBase.ts +2 -5
  29. package/cloud/cloudApi.test.ts +202 -0
  30. package/cloud/cloudApi.ts +77 -39
  31. package/cloud/configLock.test.ts +64 -0
  32. package/cloud/configLock.ts +96 -0
  33. package/cloud/constants.ts +26 -5
  34. package/cloud/globalConfig.ts +70 -32
  35. package/codeAgent/agent/AkanCodeServices.ts +97 -0
  36. package/codeAgent/agent/AkanContextFiles.ts +94 -0
  37. package/codeAgent/agent/CodeAgent.ts +551 -0
  38. package/codeAgent/agent/CodeAgentAsks.ts +66 -0
  39. package/codeAgent/agent/CodeAgentEventMapper.ts +225 -0
  40. package/codeAgent/agent/CodeAgentGate.ts +93 -0
  41. package/codeAgent/agent/CodeAgentProxy.ts +82 -0
  42. package/codeAgent/agent/CodeAgentRpcHost.ts +103 -0
  43. package/codeAgent/agent/CodeAgentRpcListener.ts +109 -0
  44. package/codeAgent/agent/CodeAgentStreamPrinter.ts +86 -0
  45. package/codeAgent/agent/CodeAgentSuspended.ts +85 -0
  46. package/codeAgent/agent/CodeAgentUi.ts +47 -0
  47. package/codeAgent/agent/CodeMailbox.ts +148 -0
  48. package/codeAgent/agent/CodeSessionFork.ts +56 -0
  49. package/codeAgent/agent/CodeSessionIndex.ts +78 -0
  50. package/codeAgent/agent/SubagentPool.ts +214 -0
  51. package/codeAgent/agent/akanCodeModel.ts +47 -0
  52. package/codeAgent/agent/akanCodePaths.ts +31 -0
  53. package/codeAgent/agent/akanContextFiles.test.ts +114 -0
  54. package/codeAgent/agent/akanSystemPrompt.ts +46 -0
  55. package/codeAgent/agent/codeAgent.test.ts +178 -0
  56. package/codeAgent/agent/codeAgentContract.test.ts +523 -0
  57. package/codeAgent/agent/codeAgentProxy.test.ts +74 -0
  58. package/codeAgent/agent/codeAgentRpcListener.test.ts +113 -0
  59. package/codeAgent/agent/codeAgentSuspended.test.ts +47 -0
  60. package/codeAgent/agent/codeMailbox.test.ts +76 -0
  61. package/codeAgent/agent/codeSessionFork.test.ts +58 -0
  62. package/codeAgent/agent/codeSessionIndex.test.ts +77 -0
  63. package/codeAgent/agent/consoleToStderr.ts +8 -0
  64. package/codeAgent/agent/jsonLines.ts +29 -0
  65. package/codeAgent/agent/stringArg.ts +5 -0
  66. package/codeAgent/feedback/DevLogFeedback.ts +82 -0
  67. package/codeAgent/feedback/PreviewChrome.ts +82 -0
  68. package/codeAgent/feedback/PreviewFeedback.ts +46 -0
  69. package/codeAgent/feedback/PreviewView.ts +95 -0
  70. package/codeAgent/feedback/TurnFeedback.ts +98 -0
  71. package/codeAgent/feedback/VerifyFeedback.ts +40 -0
  72. package/codeAgent/feedback/previewChrome.test.ts +73 -0
  73. package/codeAgent/index.ts +34 -0
  74. package/codeAgent/skills/akan-code/SKILL.md +130 -0
  75. package/codeAgent/skills/akan-debug/SKILL.md +42 -0
  76. package/codeAgent/skills/akan-deps/SKILL.md +50 -0
  77. package/codeAgent/skills/akan-err/SKILL.md +39 -0
  78. package/codeAgent/skills/akan-module/SKILL.md +45 -0
  79. package/codeAgent/skills/akan-scaffold/SKILL.md +76 -0
  80. package/codeAgent/skills/akan-scalar/SKILL.md +52 -0
  81. package/codeAgent/skills/akan-service/SKILL.md +34 -0
  82. package/codeAgent/skills/akan-store/SKILL.md +67 -0
  83. package/codeAgent/skills/akan-ui/SKILL.md +63 -0
  84. package/codeAgent/skills/akan-validate/SKILL.md +44 -0
  85. package/codeAgent/tools/AkanCli.ts +50 -0
  86. package/codeAgent/tools/AkanCodePlugins.ts +136 -0
  87. package/codeAgent/tools/AkanEditScope.ts +48 -0
  88. package/codeAgent/tools/AkanEnvKeys.ts +50 -0
  89. package/codeAgent/tools/AkanToolPack.ts +92 -0
  90. package/codeAgent/tools/AkanVerifier.ts +48 -0
  91. package/codeAgent/tools/AskToolPack.ts +99 -0
  92. package/codeAgent/tools/MailToolPack.ts +76 -0
  93. package/codeAgent/tools/McpClient.ts +187 -0
  94. package/codeAgent/tools/McpOAuth.ts +233 -0
  95. package/codeAgent/tools/McpOAuthLoopback.ts +64 -0
  96. package/codeAgent/tools/McpServerConfig.ts +129 -0
  97. package/codeAgent/tools/McpSignIn.ts +158 -0
  98. package/codeAgent/tools/McpTokenStore.ts +81 -0
  99. package/codeAgent/tools/McpToolPack.ts +199 -0
  100. package/codeAgent/tools/SessionToolPack.ts +91 -0
  101. package/codeAgent/tools/WebToolPack.ts +147 -0
  102. package/codeAgent/tools/akanCodePlugins.test.ts +677 -0
  103. package/codeAgent/tools/askToolPack.test.ts +116 -0
  104. package/codeAgent/tools/mcpOAuth.test.ts +612 -0
  105. package/codeAgent/tools/mcpServerConfig.test.ts +212 -0
  106. package/codegenLock.test.ts +4 -13
  107. package/codegenLock.ts +3 -17
  108. package/commandDecorators/argMeta.ts +1 -5
  109. package/commandDecorators/camelToKebabCase.ts +1 -0
  110. package/commandDecorators/command.ts +46 -102
  111. package/commandDecorators/commandBuilder.ts +30 -61
  112. package/commandDecorators/commandDecorators.test.ts +2 -10
  113. package/commandDecorators/dependencyBuilder.ts +10 -1
  114. package/commandDecorators/helpFormatter.ts +68 -145
  115. package/commandDecorators/index.ts +0 -2
  116. package/commandDecorators/targetMeta.ts +9 -7
  117. package/createTunnel.ts +7 -14
  118. package/dependencyScanner.test.ts +4 -16
  119. package/dependencyScanner.ts +31 -80
  120. package/devkitUtils.test.ts +48 -20
  121. package/executors.linkFallback.test.ts +103 -0
  122. package/executors.test.ts +132 -106
  123. package/executors.ts +195 -496
  124. package/fileEditor.ts +9 -37
  125. package/{getDirname.test.ts → fileSys.test.ts} +5 -3
  126. package/fileSys.ts +6 -8
  127. package/formSetterScanner.ts +3 -16
  128. package/frontendBuild/allRoutesBuilder.ts +7 -21
  129. package/frontendBuild/autoImportSync.test.ts +27 -38
  130. package/frontendBuild/autoImportSync.ts +31 -65
  131. package/frontendBuild/buildRouteClient.test.ts +62 -37
  132. package/frontendBuild/bundleDefine.ts +12 -0
  133. package/frontendBuild/clientBuildTypes.ts +5 -36
  134. package/frontendBuild/clientEntriesBundler.ts +9 -40
  135. package/frontendBuild/clientEntryDiscovery.ts +6 -41
  136. package/frontendBuild/csrArtifactBuilder.ts +6 -25
  137. package/frontendBuild/cssCandidateCache.test.ts +0 -6
  138. package/frontendBuild/cssCandidateCache.ts +6 -41
  139. package/frontendBuild/cssCompiler.ts +29 -78
  140. package/frontendBuild/cssImportResolver.ts +6 -19
  141. package/frontendBuild/devChangePlanner.ts +7 -10
  142. package/frontendBuild/devGeneratedIndexSync.test.ts +12 -27
  143. package/frontendBuild/devGeneratedIndexSync.ts +1 -4
  144. package/frontendBuild/fontOptimizer.test.ts +6 -14
  145. package/frontendBuild/fontOptimizer.ts +15 -72
  146. package/frontendBuild/fontPruner.test.ts +5 -10
  147. package/frontendBuild/fontPruner.ts +5 -20
  148. package/frontendBuild/frontendBuild.test.ts +46 -69
  149. package/frontendBuild/hmrChangeClassifier.ts +2 -2
  150. package/frontendBuild/hmrWatcher.test.ts +8 -32
  151. package/frontendBuild/hmrWatcher.ts +14 -71
  152. package/frontendBuild/pagesBundleBuilder.ts +4 -29
  153. package/frontendBuild/precompressArtifacts.ts +5 -18
  154. package/frontendBuild/routeClientBuilder.ts +23 -46
  155. package/frontendBuild/routesManifestArtifactSerializer.ts +5 -36
  156. package/frontendBuild/sourceMtimeIndex.test.ts +41 -97
  157. package/frontendBuild/sourceMtimeIndex.ts +19 -130
  158. package/frontendBuild/ssrBaseArtifactBuilder.ts +2 -8
  159. package/frontendBuild/styleContract.ts +1 -8
  160. package/frontendBuild/themeValidator.ts +4 -26
  161. package/frontendBuild/watchRootResolver.test.ts +23 -42
  162. package/frontendBuild/watchRootResolver.ts +5 -27
  163. package/getRelatedCnsts.ts +12 -92
  164. package/incrementalBuilder/buildBatch.proc.ts +29 -58
  165. package/incrementalBuilder/buildBatchProtocol.ts +8 -36
  166. package/incrementalBuilder/buildBatchRunner.test.ts +34 -0
  167. package/incrementalBuilder/buildBatchRunner.ts +10 -31
  168. package/incrementalBuilder/builderChannel.test.ts +24 -49
  169. package/incrementalBuilder/builderChannel.ts +6 -37
  170. package/incrementalBuilder/builderRequestRouter.test.ts +0 -8
  171. package/incrementalBuilder/builderRequestRouter.ts +5 -24
  172. package/incrementalBuilder/devWatchBatch.test.ts +21 -24
  173. package/incrementalBuilder/incrementalBuilder.host.test.ts +18 -55
  174. package/incrementalBuilder/incrementalBuilder.host.ts +18 -76
  175. package/incrementalBuilder/incrementalBuilder.proc.test.ts +81 -0
  176. package/incrementalBuilder/incrementalBuilder.proc.ts +49 -161
  177. package/index.ts +5 -19
  178. package/integration/devResourceProbe.ts +50 -56
  179. package/integration/devStability.integration.test.ts +47 -194
  180. package/integration/devStabilityHarness.test.ts +13 -28
  181. package/integration/devStabilityHarness.ts +65 -330
  182. package/integration/ssrMemoryProbe.ts +41 -167
  183. package/libSource.test.ts +4 -15
  184. package/libSource.ts +19 -31
  185. package/lint/__fixtures__/no-document-cookie/bad.tsx +4 -0
  186. package/lint/__fixtures__/no-document-cookie/good.tsx +7 -0
  187. package/lint/__fixtures__/no-web-only-api-outside-webkit/bad.tsx +7 -0
  188. package/lint/__fixtures__/no-web-only-api-outside-webkit/good.tsx +11 -0
  189. package/lint/__fixtures__/no-web-storage/bad.tsx +6 -0
  190. package/lint/__fixtures__/no-web-storage/good.tsx +7 -0
  191. package/lint/gritRules.test.ts +8 -24
  192. package/lint/no-deep-internal-import.grit +2 -2
  193. package/lint/no-document-cookie.grit +29 -0
  194. package/lint/no-redeclare-predefined-endpoint.grit +2 -1
  195. package/lint/no-web-only-api-outside-webkit.grit +44 -0
  196. package/lint/no-web-storage.grit +30 -0
  197. package/linter.test.ts +1 -2
  198. package/linter.ts +26 -116
  199. package/mobile/index.ts +6 -0
  200. package/mobile/mobileTarget.ts +16 -13
  201. package/mobile/nativeApi.test.ts +30 -0
  202. package/mobile/nativeApi.ts +40 -0
  203. package/mobile/nativeApp.test.ts +85 -0
  204. package/mobile/nativeApp.ts +210 -0
  205. package/mobile/nativeConfig.test.ts +199 -0
  206. package/mobile/nativeConfig.ts +184 -0
  207. package/mobile/nativeWebDir.test.ts +80 -0
  208. package/mobile/nativeWebDir.ts +62 -0
  209. package/mobile/placeholderAppId.test.ts +13 -0
  210. package/mobile/placeholderAppId.ts +16 -0
  211. package/mobile/usageDescriptions.test.ts +27 -0
  212. package/mobile/usageDescriptions.ts +24 -0
  213. package/package.json +10 -10
  214. package/packageExports.test.ts +3 -18
  215. package/packageExportsMap.ts +3 -19
  216. package/piDependencyPins.test.ts +46 -0
  217. package/platformTest/BunTestReport.test.ts +60 -0
  218. package/platformTest/BunTestReport.ts +65 -0
  219. package/platformTest/IdleWatchedProcess.test.ts +56 -0
  220. package/platformTest/IdleWatchedProcess.ts +73 -0
  221. package/platformTest/LinuxDockerTarget.ts +105 -0
  222. package/platformTest/PlatformTestRun.test.ts +64 -0
  223. package/platformTest/PlatformTestRun.ts +141 -0
  224. package/platformTest/PlatformTestTarget.ts +142 -0
  225. package/platformTest/TestSnapshot.test.ts +72 -0
  226. package/platformTest/TestSnapshot.ts +103 -0
  227. package/platformTest/WindowsSshTarget.ts +151 -0
  228. package/prompter.ts +3 -13
  229. package/qualityScanner.test.ts +9 -26
  230. package/qualityScanner.ts +200 -286
  231. package/recipeScanner.test.ts +7 -12
  232. package/recipeScanner.ts +9 -38
  233. package/repoIdentity.ts +2 -9
  234. package/routeSourceValidator.test.ts +0 -1
  235. package/routeSourceValidator.ts +7 -42
  236. package/scanInfo.ts +91 -225
  237. package/selectModel.ts +1 -2
  238. package/semver.ts +3 -5
  239. package/slicePlanner.test.ts +6 -24
  240. package/slicePlanner.ts +11 -32
  241. package/spinner.test.ts +3 -8
  242. package/spinner.ts +4 -16
  243. package/sshTunnel.ts +1 -6
  244. package/ssrScanner.test.ts +1 -6
  245. package/ssrScanner.ts +103 -113
  246. package/stripAnsi.ts +3 -0
  247. package/subspace.test.ts +30 -25
  248. package/subspace.ts +81 -115
  249. package/subspaceConfig.ts +11 -9
  250. package/testHelpers.ts +194 -0
  251. package/transforms/asyncDefaultExportDetector.ts +3 -11
  252. package/transforms/barrelAnalyzer.ts +43 -135
  253. package/transforms/barrelImportsPlugin.ts +38 -175
  254. package/transforms/externalizeFrameworkPlugin.test.ts +39 -118
  255. package/transforms/externalizeFrameworkPlugin.ts +13 -87
  256. package/transforms/moduleSyntax.ts +32 -0
  257. package/transforms/rscUseClientTransform.ts +35 -20
  258. package/transforms/transforms.test.ts +153 -44
  259. package/transforms/tsconfigPackageResolver.test.ts +6 -24
  260. package/transforms/useClientBundlePlugin.ts +2 -22
  261. package/typeChecker.ts +7 -63
  262. package/types.ts +1 -0
  263. package/uploadRelease.ts +10 -25
  264. package/workflow/artifacts.ts +18 -0
  265. package/workflow/executor.test.ts +1 -12
  266. package/workflow/executor.ts +101 -110
  267. package/workflow/index.ts +0 -1
  268. package/workflow/moduleIndex.test.ts +4 -15
  269. package/workflow/moduleIndex.ts +9 -49
  270. package/workflow/plan.ts +18 -25
  271. package/workflow/render.ts +80 -96
  272. package/workflow/rolloutGate.test.ts +1 -1
  273. package/workflow/source.ts +52 -69
  274. package/workflow/sourceAst.ts +50 -0
  275. package/workspaceLayout.test.ts +13 -3
  276. package/workspaceLayout.ts +20 -23
  277. package/abstractCompactor.ts +0 -112
  278. package/aiEditor.test.ts +0 -60
  279. package/aiEditor.ts +0 -387
  280. package/applicationReleasePackager.ts +0 -209
  281. package/capacitorApp.test.ts +0 -372
  282. package/capacitorApp.ts +0 -1648
  283. package/commandDecorators/commandMeta.ts +0 -7
  284. package/commandDecorators/types.ts +0 -10
  285. package/getDirname.ts +0 -4
  286. package/guideline.ts +0 -19
  287. package/local/optimize-resource/ipcprobe/child.ts +0 -1
  288. package/local/optimize-resource/ipcprobe/parent.ts +0 -11
  289. package/workflow/primitive.ts +0 -59
@@ -1,44 +1,36 @@
1
1
  import path from "node:path";
2
2
  import { Logger } from "akanjs/common";
3
+ import { parseNamedList } from "./moduleSyntax";
3
4
 
4
5
  export interface BarrelExportTarget {
5
- /** Subpath specifier to emit in rewritten import, e.g. `akanjs/ui/Empty`. */
6
6
  subpath: string;
7
- /** Name as it is exported by the leaf module. */
8
7
  originalName: string;
9
8
  }
10
9
 
11
10
  export type BarrelExportMap = Map<string, BarrelExportTarget>;
12
11
 
13
12
  export interface PackageEntry {
14
- /** Package specifier (e.g. `akanjs/ui`). */
15
13
  pkgName: string;
16
14
  /** Absolute path of the barrel entry file. */
17
15
  entryFile: string;
18
- /** Absolute directory used as the base for subpath computation. Typically dirname(entryFile). */
16
+ /** Absolute base directory for subpath computation, typically `dirname(entryFile)`. */
19
17
  pkgDir: string;
20
- /** Preserve concrete file paths for package exports that do not support extensionless deep imports. */
18
+ /** Emit concrete file paths, for package exports that do not support extensionless deep imports. */
21
19
  preserveFilePath?: boolean;
22
20
  }
23
21
 
24
22
  export interface BarrelAnalyzerOptions {
25
23
  resolvePackage: (pkgName: string) => Promise<PackageEntry | null>;
26
- /** Resolve a relative specifier from `fromFile` to an absolute file path on disk. */
24
+ /** Resolves to an absolute file path on disk. */
27
25
  resolveRelative?: (fromFile: string, relSpec: string) => Promise<string | null>;
28
26
  }
29
27
 
30
- // Re-export statements with an explicit source: `export { A, B as C } from "./x"`,
31
- // `export * from "./x"`, `export * as ns from "./x"`. We rely on `Transpiler.scan`
32
- // for the authoritative export name set; this regex only needs to give us the
33
- // name↔source mapping that scan doesn't expose.
28
+ // `Transpiler.scan` gives the export names but not their sources; this regex supplies the name↔source mapping.
34
29
  const REEXPORT_RE =
35
30
  /(?:^|\n)\s*export\s+(?:type\s+)?(?:(\*)(?:\s+as\s+(\w+))?|\{\s*([^}]*?)\s*\})\s+from\s+(["'])([^"']+)\4;?/g;
36
31
 
37
- // Local named re-export without `from`: `export { A, B as C };`
38
- // `Transpiler.scan` currently omits these from its `exports` list so we keep a
39
- // small regex fallback. The lookahead sits immediately after `}` so that
40
- // greedy whitespace matching cannot backtrack past it — otherwise it would
41
- // also match `export type { X } from "./y"` statements.
32
+ // `Transpiler.scan` omits `export { A, B as C };`. The lookahead sits right after `}` so whitespace backtracking
33
+ // cannot also match `export type { X } from "./y"`.
42
34
  const LOCAL_NAMED_RE = /(?:^|\n)\s*export\s+\{\s*([^}]*?)\s*\}(?!\s*from)/g;
43
35
 
44
36
  const CANDIDATE_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
@@ -64,68 +56,53 @@ export class BarrelAnalyzer {
64
56
 
65
57
  async #analyzeSafe(pkgName: string): Promise<BarrelExportMap | null> {
66
58
  try {
67
- return await this.#analyzeUncached(pkgName);
59
+ const pkg = await this.#opts.resolvePackage(pkgName);
60
+ if (!pkg) return null;
61
+ const map: BarrelExportMap = new Map();
62
+ await this.#walk(pkg.entryFile, pkg, map, new Set());
63
+ return map;
68
64
  } catch (err) {
69
65
  this.#logger.error(`analyze failed for ${pkgName}: ${(err as Error).message}`);
70
66
  return null;
71
67
  }
72
68
  }
73
69
 
74
- async #analyzeUncached(pkgName: string): Promise<BarrelExportMap | null> {
75
- const pkg = await this.#opts.resolvePackage(pkgName);
76
- if (!pkg) return null;
77
- const map: BarrelExportMap = new Map();
78
- const visited = new Set<string>();
79
- await this.#walk(pkg.entryFile, pkg, map, visited);
80
- return map;
81
- }
82
-
83
70
  async #walk(absFile: string, pkg: PackageEntry, map: BarrelExportMap, visited: Set<string>): Promise<void> {
84
71
  if (visited.has(absFile)) return;
85
72
  visited.add(absFile);
86
- const source = await readIfExists(absFile);
87
- if (source === null) return;
73
+ const file = Bun.file(absFile);
74
+ if (!(await file.exists())) return;
75
+ const source = await file.text();
88
76
  const currentSubpath = this.#subpathFor(pkg, absFile);
89
77
  if (!currentSubpath) return;
90
78
 
91
- // Authoritative export names from Bun's transpiler. Filters type-only,
92
- // comments, strings, etc. automatically. `export *` re-exports are NOT
93
- // flooded here — they only show up as imports, so we recurse separately.
79
+ // scan leaves out `export *` names (they surface only as imports), so star re-exports are walked below.
94
80
  const authoritative = this.#scanExports(source, absFile);
95
- // `default` is intentionally skipped — barrels rarely proxy defaults and
96
- // rewriting `import X from "pkg"` requires different semantics.
81
+ // Barrels rarely proxy defaults, and rewriting `import X from "pkg"` needs different semantics.
97
82
  authoritative.delete("default");
98
83
 
99
- // Names attributed to a specific leaf via `export ... from "./path"`.
100
- // Everything left over in `authoritative` after this is treated as a local
101
- // declaration at this file's own subpath.
102
84
  const attributed = new Set<string>();
85
+ const attribute = (listBody: string, subpath: string) => {
86
+ for (const item of parseNamedList(listBody)) {
87
+ if (item.isType || item.imported === "default" || !authoritative.has(item.local)) continue;
88
+ attributed.add(item.local);
89
+ if (!map.has(item.local)) map.set(item.local, { subpath, originalName: item.imported });
90
+ }
91
+ };
103
92
 
104
- // Pass 1: `export { ... } from "./x"` and `export * [as ns] from "./x"`.
105
- REEXPORT_RE.lastIndex = 0;
106
- let m: RegExpExecArray | null = REEXPORT_RE.exec(source);
107
- while (m !== null) {
93
+ // A module's own exports shadow the names its stars bring (ES), so every one is mapped before a star is walked.
94
+ const starSpecs: string[] = [];
95
+ for (const m of source.matchAll(REEXPORT_RE)) {
108
96
  const star = m[1];
109
97
  const nsAs = m[2];
110
98
  const namedList = m[3];
111
99
  const spec = m[5] ?? "";
112
- m = REEXPORT_RE.exec(source);
113
100
  if (!isRelative(spec)) continue;
114
101
 
115
102
  if (star) {
116
- if (nsAs) {
117
- // `export * as ns from "./x"` exposes a namespace object that we
118
- // cannot flatten into direct subpath imports. Ensure it's not misread
119
- // as a local declaration.
120
- authoritative.delete(nsAs);
121
- continue;
122
- }
123
- const targetAbs = await this.#resolveRel(absFile, spec);
124
- if (!targetAbs) continue;
125
- // Recurse: target's exports will land in `map` with target's subpath.
126
- // These names don't appear in this file's `authoritative` set, so the
127
- // local-declaration pass below won't misattribute them.
128
- await this.#walk(targetAbs, pkg, map, visited);
103
+ // A namespace re-export cannot be flattened into subpath imports, nor is it a local declaration.
104
+ if (nsAs) authoritative.delete(nsAs);
105
+ else starSpecs.push(spec);
129
106
  continue;
130
107
  }
131
108
 
@@ -133,49 +110,28 @@ export class BarrelAnalyzer {
133
110
  const targetAbs = await this.#resolveRel(absFile, spec);
134
111
  if (!targetAbs) continue;
135
112
  const targetSubpath = this.#subpathFor(pkg, targetAbs);
136
- if (!targetSubpath) continue;
137
- for (const item of parseNamedList(namedList)) {
138
- if (item.isType) continue;
139
- if (item.imported === "default") continue;
140
- // Validate against scan — drops anything hidden behind `export type`
141
- // or otherwise not actually exported at runtime.
142
- if (!authoritative.has(item.local)) continue;
143
- attributed.add(item.local);
144
- if (!map.has(item.local)) {
145
- map.set(item.local, { subpath: targetSubpath, originalName: item.imported });
146
- }
147
- }
113
+ if (targetSubpath) attribute(namedList, targetSubpath);
148
114
  }
149
115
  }
150
116
 
151
- // Pass 2: `export { A, B as C };` (no `from`). scan omits these, so the
152
- // regex is authoritative for this narrow case.
153
117
  LOCAL_NAMED_RE.lastIndex = 0;
154
118
  let n: RegExpExecArray | null = LOCAL_NAMED_RE.exec(source);
155
119
  while (n !== null) {
156
120
  const body = n[1] ?? "";
157
121
  n = LOCAL_NAMED_RE.exec(source);
158
- for (const item of parseNamedList(body)) {
159
- if (item.isType) continue;
160
- if (item.imported === "default") continue;
161
- // Validate against scan — ensures we don't treat commented-out or
162
- // otherwise dead code as a real local re-export.
163
- if (!authoritative.has(item.local)) continue;
164
- attributed.add(item.local);
165
- if (!map.has(item.local)) {
166
- map.set(item.local, { subpath: currentSubpath, originalName: item.imported });
167
- }
168
- }
122
+ attribute(body, currentSubpath);
169
123
  }
170
124
 
171
- // Pass 3: remaining authoritative names are local declarations defined in
172
- // this file (const/function/class/...). scan already filtered out
173
- // type-only and namespace-only exports for us.
174
125
  for (const name of authoritative) {
175
126
  if (attributed.has(name)) continue;
176
127
  if (map.has(name)) continue;
177
128
  map.set(name, { subpath: currentSubpath, originalName: name });
178
129
  }
130
+
131
+ for (const spec of starSpecs) {
132
+ const targetAbs = await this.#resolveRel(absFile, spec);
133
+ if (targetAbs) await this.#walk(targetAbs, pkg, map, visited);
134
+ }
179
135
  }
180
136
 
181
137
  #scanExports(source: string, absFile: string): Set<string> {
@@ -193,12 +149,12 @@ export class BarrelAnalyzer {
193
149
  const rel = path.relative(pkg.pkgDir, absFile);
194
150
  if (!rel || rel.startsWith("..") || path.isAbsolute(rel)) return null;
195
151
  if (pkg.preserveFilePath) return `${pkg.pkgName}/${rel.split(path.sep).join("/")}`;
196
- const noExt = stripKnownExt(rel);
197
- // index files collapse to the directory — but the barrel entry is itself an index.
198
- // For nested `xxx/index.*`, callers are expected to import `@pkg/xxx` (not `xxx/index`).
199
- const tail = collapseIndex(noExt);
200
- if (tail === "") return pkg.pkgName;
201
- return `${pkg.pkgName}/${tail.split(path.sep).join("/")}`;
152
+ const ext = CANDIDATE_EXTS.find((candidate) => rel.endsWith(candidate));
153
+ const parts = (ext ? rel.slice(0, -ext.length) : rel).split(path.sep);
154
+ // `xxx/index` collapses to `xxx`: callers import `@pkg/xxx`, never `@pkg/xxx/index`.
155
+ if (parts[parts.length - 1] === "index") parts.pop();
156
+ const tail = parts.join("/");
157
+ return tail === "" ? pkg.pkgName : `${pkg.pkgName}/${tail}`;
202
158
  }
203
159
 
204
160
  async #resolveRel(fromFile: string, relSpec: string): Promise<string | null> {
@@ -207,45 +163,10 @@ export class BarrelAnalyzer {
207
163
  }
208
164
  }
209
165
 
210
- interface NamedItem {
211
- imported: string;
212
- local: string;
213
- isType: boolean;
214
- }
215
-
216
- const parseNamedList = (listBody: string): NamedItem[] => {
217
- const out: NamedItem[] = [];
218
- for (const raw of listBody.split(",")) {
219
- const s = raw.trim();
220
- if (!s) continue;
221
- let rest = s;
222
- let isType = false;
223
- if (rest.startsWith("type ")) {
224
- isType = true;
225
- rest = rest.slice(5).trim();
226
- }
227
- const asMatch = /^(\w+)\s+as\s+(\w+)$/.exec(rest);
228
- if (asMatch) {
229
- out.push({ imported: asMatch[1] ?? "", local: asMatch[2] ?? "", isType });
230
- continue;
231
- }
232
- if (/^\w+$/.test(rest)) {
233
- out.push({ imported: rest, local: rest, isType });
234
- }
235
- }
236
- return out;
237
- };
238
-
239
166
  const isRelative = (spec: string): boolean => {
240
167
  return spec.startsWith("./") || spec.startsWith("../") || spec === "." || spec === "..";
241
168
  };
242
169
 
243
- const readIfExists = async (absFile: string): Promise<string | null> => {
244
- const file = Bun.file(absFile);
245
- if (!(await file.exists())) return null;
246
- return file.text();
247
- };
248
-
249
170
  const defaultResolveRelative = async (fromFile: string, relSpec: string): Promise<string | null> => {
250
171
  const baseDir = path.dirname(fromFile);
251
172
  const joined = path.resolve(baseDir, relSpec);
@@ -263,16 +184,3 @@ const defaultResolveRelative = async (fromFile: string, relSpec: string): Promis
263
184
  }
264
185
  return null;
265
186
  };
266
-
267
- const stripKnownExt = (relPath: string): string => {
268
- for (const ext of CANDIDATE_EXTS) {
269
- if (relPath.endsWith(ext)) return relPath.slice(0, -ext.length);
270
- }
271
- return relPath;
272
- };
273
-
274
- const collapseIndex = (relPathNoExt: string): string => {
275
- const parts = relPathNoExt.split(path.sep);
276
- if (parts[parts.length - 1] === "index") parts.pop();
277
- return parts.join(path.sep);
278
- };