@akanjs/cli 3.0.0-alpha.11 → 3.0.0-alpha.111

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 (140) hide show
  1. package/.build-stamp +1 -1
  2. package/README.ko.md +1 -1
  3. package/README.md +1 -1
  4. package/{abstractCompactor-e5yq60qz.js → abstractCompactor-mjh5ppsq.js} +3 -5
  5. package/{agent.command-a3n15c6t.js → agent.command-d2gfxfck.js} +11 -13
  6. package/{application.command-rdm3et0g.js → application.command-pj2m7584.js} +65 -38
  7. package/{applicationBuildRunner-6m8n71w4.js → applicationBuildRunner-kxwbssgn.js} +27 -23
  8. package/{applicationReleasePackager-gg8ccbeh.js → applicationReleasePackager-4yasxwv9.js} +8 -9
  9. package/buildBatch.proc.js +12 -14
  10. package/capacitorApp-36rbr858.js +12 -0
  11. package/{cloud.command-56aeekqn.js → cloud.command-gxqp8xdv.js} +15 -18
  12. package/commandManifest.json +1 -1
  13. package/{context.command-ysxawe7h.js → context.command-ez8a0zyz.js} +19 -23
  14. package/dependencyScanner-shs82ynp.js +8 -0
  15. package/devTui-40ggmd99.js +553 -0
  16. package/devTuiView-c08ccbat.js +9 -0
  17. package/getDirname-dr3jz1xg.js +7 -0
  18. package/{guideline.command-yqf9a372.js → guideline.command-xmqwx8zm.js} +24 -22
  19. package/guidelines/agentRule/agentRule.generate.json +31 -0
  20. package/guidelines/agentRule/agentRule.instruction.md +531 -0
  21. package/guidelines/conventions/conventions.instruction.md +493 -369
  22. package/guidelines/framework/framework.generate.json +3 -2
  23. package/guidelines/framework/framework.instruction.md +0 -9
  24. package/guidelines/mcpRule/mcpRule.generate.json +35 -0
  25. package/guidelines/mcpRule/mcpRule.instruction.md +460 -0
  26. package/guidelines/modelStore/modelStore.instruction.md +12 -0
  27. package/guidelines/modelTemplate/modelTemplate.instruction.md +5 -0
  28. package/guidelines/queryRule/queryRule.generate.json +31 -0
  29. package/guidelines/queryRule/queryRule.instruction.md +178 -0
  30. package/guidelines/runtimeRule/runtimeRule.generate.json +25 -0
  31. package/guidelines/runtimeRule/runtimeRule.instruction.md +220 -0
  32. package/guidelines/ssrRule/ssrRule.instruction.md +51 -5
  33. package/guidelines/transportRule/transportRule.generate.json +31 -0
  34. package/guidelines/transportRule/transportRule.instruction.md +60 -0
  35. package/guidelines/workspaceOnboarding/workspaceOnboarding.instruction.md +54 -764
  36. package/guidelines/workspaceRecipes/workspaceRecipes.generate.json +33 -0
  37. package/guidelines/workspaceRecipes/workspaceRecipes.instruction.md +467 -0
  38. package/incrementalBuilder.proc.js +26 -25
  39. package/{index-8pd02prk.js → index-259dsnz3.js} +58 -25
  40. package/{index-yzsxc30b.js → index-2tv3fdx4.js} +975 -234
  41. package/index-3d9y066c.js +76 -0
  42. package/{index-76rn3g2c.js → index-47pj3aee.js} +4 -4
  43. package/{index-m90fn8xe.js → index-4edzf1gr.js} +32 -32
  44. package/{index-3789fdpg.js → index-600zhnve.js} +94 -61
  45. package/{index-1577bej2.js → index-6wwzs1bc.js} +2 -2
  46. package/{index-cv9cxs5k.js → index-7737gxcp.js} +18 -18
  47. package/{index-a5cs7d8r.js → index-8few9nyh.js} +13 -13
  48. package/{index-mewacxye.js → index-8pm30m73.js} +57 -57
  49. package/{index-wnp7hwq7.js → index-92zztqy1.js} +7 -7
  50. package/{index-fgc8r6dj.js → index-a5vmm6x4.js} +3 -1
  51. package/{index-cav1azma.js → index-a64esmtb.js} +1106 -1369
  52. package/{index-nwqqssnb.js → index-aj7tyh5w.js} +26 -23
  53. package/{index-xys926f2.js → index-by5gpp8x.js} +4 -4
  54. package/{index-yzmqvg23.js → index-fz5q3gd7.js} +8 -8
  55. package/{index-kcsa1zff.js → index-gyd220p2.js} +4 -4
  56. package/{index-j37qq1f2.js → index-h7yw2z9n.js} +7 -7
  57. package/{index-bjpxzr6s.js → index-hwkygn9s.js} +51 -3
  58. package/index-j9yhg404.js +159 -0
  59. package/{index-46tjzh6s.js → index-kcxmv48c.js} +29 -29
  60. package/{index-0cj2zxbm.js → index-kn1h7p1x.js} +12 -11
  61. package/{index-e4b2ae6b.js → index-ktb5njgy.js} +41 -34
  62. package/{index-etwh7sd3.js → index-mak3cd28.js} +100 -32
  63. package/{index-xjd3rqzj.js → index-n68jx79w.js} +6 -15
  64. package/{index-z1s5f4e0.js → index-ne2qy0nk.js} +36 -37
  65. package/{index-65aeh789.js → index-pnqbkf5p.js} +1 -1
  66. package/{index-67546d0j.js → index-q0y2jq9j.js} +14 -2
  67. package/{index-4mgkgba3.js → index-rwg2aa4f.js} +14 -14
  68. package/{index-bzznxgcx.js → index-v0qh106m.js} +1002 -128
  69. package/{index-j9cxndc0.js → index-w8kmx1b6.js} +20 -11
  70. package/{index-h5whaj26.js → index-ws5e0j51.js} +16 -12
  71. package/{index-9va8zayb.js → index-wt7qrtd4.js} +27 -27
  72. package/index.js +22 -25
  73. package/{library.command-x6t29a30.js → library.command-nbsky672.js} +11 -10
  74. package/{localRegistry.command-649g1n7b.js → localRegistry.command-ts6t5e5a.js} +20 -23
  75. package/{module.command-8w8ys5t3.js → module.command-29vqzrh8.js} +13 -16
  76. package/{package.command-zgy7f3fz.js → package.command-axv0abmn.js} +8 -10
  77. package/package.json +5 -7
  78. package/{page.command-yjmnkyk2.js → page.command-vs9s3zrh.js} +7 -9
  79. package/{primitive.command-gfqaagsg.js → primitive.command-cbxmqbtc.js} +13 -16
  80. package/{quality.command-ec8r1dn3.js → quality.command-gmbs7nws.js} +184 -334
  81. package/recipeScanner-fpagtgc1.js +11 -0
  82. package/{repair.command-p9ey6mkm.js → repair.command-bqsjptsj.js} +12 -14
  83. package/{routeSourceValidator-e4jmbfcx.js → routeSourceValidator-apyqw7wk.js} +90 -19
  84. package/{scalar.command-9dhmqhtm.js → scalar.command-6k1vhe9s.js} +11 -14
  85. package/subspace.command-35a3t6vv.js +755 -0
  86. package/templates/app/env/env.client.debug.ts.template +1 -5
  87. package/templates/app/env/env.client.develop.ts.template +1 -5
  88. package/templates/app/env/env.client.local.ts.template +1 -5
  89. package/templates/app/env/env.client.main.ts.template +1 -5
  90. package/templates/app/env/env.client.testing.ts.template +1 -5
  91. package/templates/app/env/env.client.type.ts +2 -6
  92. package/templates/app/env/env.server.debug.ts.template +1 -5
  93. package/templates/app/env/env.server.develop.ts.template +1 -5
  94. package/templates/app/env/env.server.local.ts.template +1 -5
  95. package/templates/app/env/env.server.main.ts.template +1 -5
  96. package/templates/app/main.ts +3 -1
  97. package/templates/app/page/_index.tsx +3 -3
  98. package/templates/app/page/_layout.tsx +31 -32
  99. package/templates/appSample/lib/task/task.signal.ts +11 -6
  100. package/templates/appSample/page/task/[taskId]/_index.tsx +22 -27
  101. package/templates/appSample/page/task/[taskId]/edit.tsx +25 -30
  102. package/templates/appSample/page/task/_index.tsx +3 -7
  103. package/templates/appSample/page/task/_layout.tsx +4 -15
  104. package/templates/appSample/page/task/new.tsx +4 -10
  105. package/templates/appSample/srvkit/AuthGuard.ts +2 -2
  106. package/templates/crudPages/[__model__Id]/edit/page.tsx +27 -29
  107. package/templates/crudPages/[__model__Id]/page.tsx +24 -32
  108. package/templates/crudPages/new/page.tsx +26 -25
  109. package/templates/crudPages/page.tsx +27 -26
  110. package/templates/crudSinglePage/page.tsx +26 -21
  111. package/templates/env/_env.server.type.ts +7 -5
  112. package/templates/lib/db.ts +1 -1
  113. package/templates/lib/dict.ts +1 -1
  114. package/templates/lib/sig.ts +5 -1
  115. package/templates/lib/srv.ts +6 -2
  116. package/templates/module/__model__.dictionary.ts +4 -1
  117. package/templates/module/__model__.signal.ts +2 -3
  118. package/templates/module/__model__.store.ts +1 -2
  119. package/templates/workspaceRoot/.gitignore.template +0 -1
  120. package/templates/workspaceRoot/.husky/pre-commit.template +16 -0
  121. package/templates/workspaceRoot/.vscode/settings.json.template +1 -1
  122. package/templates/workspaceRoot/AGENTS.md.template +4 -0
  123. package/templates/workspaceRoot/CLAUDE.md.template +19 -0
  124. package/templates/workspaceRoot/biome.json.template +1 -1
  125. package/templates/workspaceRoot/docs/AI-DEVELOPMENT.md.template +10 -14
  126. package/templates/workspaceRoot/docs/GENERATED.md.template +12 -10
  127. package/templates/workspaceRoot/package.json.template +2 -4
  128. package/typeChecker-3xgrzc2v.js +7 -0
  129. package/typecheck.proc.js +4 -5
  130. package/{workflow.command-ycwc6zr6.js → workflow.command-nwpcdc8p.js} +22 -25
  131. package/{workspace.command-xhyhajxa.js → workspace.command-aczjxzbf.js} +64 -66
  132. package/capacitorApp-mr81qdxq.js +0 -61
  133. package/dependencyScanner-f6grrep3.js +0 -9
  134. package/getDirname-m9s0cmhq.js +0 -8
  135. package/index-mxvakhsm.js +0 -66
  136. package/index-r24hmh0q.js +0 -4
  137. package/recipeScanner-tghkzrrt.js +0 -12
  138. package/typeChecker-kravn7ns.js +0 -8
  139. /package/{index-0wae5ebk.js → index-meh558jy.js} +0 -0
  140. /package/{index-ss469dec.js → index-nz8prx9g.js} +0 -0
@@ -1,20 +1,20 @@
1
1
  // @bun
2
2
  import {
3
- AiSession,
4
- CloudApi,
3
+ getDefaultHostConfig,
5
4
  GlobalConfig,
6
- getDefaultHostConfig
7
- } from "./index-0cj2zxbm.js";
5
+ CloudApi,
6
+ AiSession
7
+ } from "./index-kn1h7p1x.js";
8
8
  import {
9
- openBrowser
10
- } from "./index-bzznxgcx.js";
9
+ WorkspaceExecutor,
10
+ AppExecutor
11
+ } from "./index-2tv3fdx4.js";
11
12
  import {
12
13
  runner
13
- } from "./index-etwh7sd3.js";
14
+ } from "./index-mak3cd28.js";
14
15
  import {
15
- AppExecutor,
16
- WorkspaceExecutor
17
- } from "./index-yzsxc30b.js";
16
+ openBrowser
17
+ } from "./index-v0qh106m.js";
18
18
 
19
19
  // pkgs/@akanjs/cli/npmRegistry.ts
20
20
  var defaultNpmRegistry = "https://registry.npmjs.org";
@@ -98,8 +98,8 @@ class CloudRunner extends runner("cloud") {
98
98
  const selectedName = await select({
99
99
  message: "Select the remote env server",
100
100
  choices: [
101
- ...serverEntries.map(([name, config2]) => ({
102
- name: `${name} (${config2.username ? `${config2.username}@` : ""}${config2.host}${config2.port ? `:${config2.port}` : ""})`,
101
+ ...serverEntries.map(([name, config]) => ({
102
+ name: `${name} (${config.username ? `${config.username}@` : ""}${config.host}${config.port ? `:${config.port}` : ""})`,
103
103
  value: name
104
104
  })),
105
105
  { name: "Add new remote server", value: addRemoteEnvServerValue },
@@ -203,12 +203,12 @@ ${chalk.green("\u27A4")} Authentication Required`));
203
203
  const MAX_RETRY = 300;
204
204
  for (let i = 0;i < MAX_RETRY; i++) {
205
205
  const accessToken = await cloudApi.getRemoteAuthToken(remoteId);
206
- const self2 = await cloudApi.getRemoteSelf();
207
- if (accessToken && self2) {
208
- await GlobalConfig.setHostConfig({ host: config.host, auth: { accessToken, self: self2 } });
206
+ const self = await cloudApi.getRemoteSelf();
207
+ if (accessToken && self) {
208
+ await GlobalConfig.setHostConfig({ host: config.host, auth: { accessToken, self } });
209
209
  Logger.rawLog(chalk.green(`\r\u2713 Authentication successful!`));
210
210
  Logger.rawLog(chalk.green.bold(`
211
- \u2728 Welcome aboard, ${self2.nickname ?? "anonymous"}!`));
211
+ \u2728 Welcome aboard, ${self.nickname ?? "anonymous"}!`));
212
212
  Logger.rawLog(chalk.dim(`You're now ready to use Akan CLI!
213
213
  `));
214
214
  return true;
@@ -253,12 +253,12 @@ ${chalk.green("\u27A4")} Authentication Required`));
253
253
  const isOfficialRelease = !devPatchVersion;
254
254
  const targetVersionPrefix = isOfficialRelease ? `${majorVersion}.${minorVersion}` : `${majorVersion}.${minorVersion}.${patchVersion}`;
255
255
  const tag = distTag ?? (isOfficialRelease ? "latest" : patchVersion.split("-").at(1) ?? "dev");
256
- const getNextVersion = async (prefix, tag2) => {
256
+ const getNextVersion = async (prefix, tag) => {
257
257
  try {
258
- const latestPublishedVersion2 = await getLatestPackageVersion("akanjs", tag2, registry);
259
- const latestPatch = latestPublishedVersion2.startsWith(prefix) ? parseInt(latestPublishedVersion2.split(".").at(-1) ?? "-1") : -1;
260
- const nextVersion2 = `${prefix}.${latestPatch + 1}`;
261
- return { nextVersion: nextVersion2, latestPublishedVersion: latestPublishedVersion2 };
258
+ const latestPublishedVersion = await getLatestPackageVersion("akanjs", tag, registry);
259
+ const latestPatch = latestPublishedVersion.startsWith(prefix) ? parseInt(latestPublishedVersion.split(".").at(-1) ?? "-1") : -1;
260
+ const nextVersion = `${prefix}.${latestPatch + 1}`;
261
+ return { nextVersion, latestPublishedVersion };
262
262
  } catch {
263
263
  return { nextVersion: `${prefix}.0`, latestPublishedVersion: null };
264
264
  }
@@ -283,7 +283,10 @@ ${chalk.green("\u27A4")} Authentication Required`));
283
283
  return;
284
284
  }
285
285
  }
286
- await Promise.all(akanPkgs.map(async (library) => {
286
+ Logger.info("Logging in to npm...");
287
+ await workspace.spawn("npm", ["login"], { stdio: "inherit" });
288
+ Logger.info("Logged in to npm");
289
+ for (const library of akanPkgs) {
287
290
  Logger.info(`Publishing ${library}@${nextVersion} to ${registry ?? "npm"}...`);
288
291
  await workspace.spawn("npm", ["publish", "--tag", tag, ...this.#getRegistryArgs(registry), ...this.#getLocalRegistryAuthArgs(registry)], {
289
292
  cwd: path.join(workspace.workspaceRoot, "dist/pkgs", library),
@@ -291,7 +294,7 @@ ${chalk.green("\u27A4")} Authentication Required`));
291
294
  stdio: "inherit"
292
295
  });
293
296
  Logger.info(`${library}@${nextVersion} is published to ${registry ?? "npm"}`);
294
- }));
297
+ }
295
298
  Logger.info(`All libraries are published to ${registry ?? "npm"}`);
296
299
  }
297
300
  async update(workspace, tag = "latest", { registryUrl } = {}) {
@@ -2,7 +2,7 @@
2
2
  // pkgs/@akanjs/devkit/recipeScanner.ts
3
3
  import { readdir } from "fs/promises";
4
4
  import ts from "typescript";
5
- var collectRecipeSources = async (uiDirPath, importFrom, basename = "Recipe") => {
5
+ var collectRecipeSources2 = async (uiDirPath, importFrom, basename = "Recipe") => {
6
6
  const read = async (filePath) => {
7
7
  const content = await Bun.file(filePath).text().catch(() => "");
8
8
  return content ? { path: filePath, content, importFrom } : null;
@@ -12,7 +12,7 @@ var collectRecipeSources = async (uiDirPath, importFrom, basename = "Recipe") =>
12
12
  const fromDir = await Promise.all(dirEntries.filter((entry) => entry.endsWith(".ts") && entry !== "index.ts" && !/\.(test|spec)\.ts$/.test(entry)).sort().map((entry) => read(`${uiDirPath}/${basename}/${entry}`)));
13
13
  return [flat, ...fromDir].filter((source) => !!source);
14
14
  };
15
- var scanRecipes = (sources) => {
15
+ var scanRecipes2 = (sources) => {
16
16
  const recipes = [];
17
17
  for (const source of sources) {
18
18
  const sourceFile = ts.createSourceFile(source.path, source.content, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
@@ -87,7 +87,7 @@ var isNamed = (name) => ts.isIdentifier(name) || ts.isStringLiteral(name);
87
87
  var propName = (name) => name.text;
88
88
  var DUPLICATE_TOKEN_RATIO = 0.7;
89
89
  var MIN_FINGERPRINT_TOKENS = 3;
90
- var findInlineRecipeDuplicates = (recipes, files) => {
90
+ var findInlineRecipeDuplicates2 = (recipes, files) => {
91
91
  const fingerprints = recipes.map((recipe) => ({ recipe: recipe.name, tokens: (recipe.base ?? "").split(/\s+/).filter(Boolean) })).filter((fingerprint) => fingerprint.tokens.length >= MIN_FINGERPRINT_TOKENS).map((fingerprint) => ({ ...fingerprint, needed: Math.ceil(fingerprint.tokens.length * DUPLICATE_TOKEN_RATIO) }));
92
92
  if (fingerprints.length === 0)
93
93
  return [];
@@ -133,4 +133,4 @@ var getLeadingDoc = (fullText, node) => {
133
133
  return line || undefined;
134
134
  };
135
135
 
136
- export { collectRecipeSources, scanRecipes, findInlineRecipeDuplicates };
136
+ export { collectRecipeSources2, scanRecipes2, findInlineRecipeDuplicates2 };
@@ -1,17 +1,17 @@
1
1
  // @bun
2
2
  import {
3
- AkanContextAnalyzer
4
- } from "./index-e4b2ae6b.js";
5
- import {
6
- createRepairReport,
3
+ writeWorkflowRunArtifact,
7
4
  generatedFilePathsForTarget,
8
- renderRepairReport,
9
5
  writeGeneratedSyncState,
10
- writeWorkflowRunArtifact
11
- } from "./index-kcsa1zff.js";
6
+ createRepairReport,
7
+ renderRepairReport
8
+ } from "./index-gyd220p2.js";
9
+ import {
10
+ AkanContextAnalyzer
11
+ } from "./index-ktb5njgy.js";
12
12
  import {
13
13
  runner
14
- } from "./index-etwh7sd3.js";
14
+ } from "./index-mak3cd28.js";
15
15
 
16
16
  // pkgs/@akanjs/cli/repair/repair.runner.ts
17
17
  var commandForShell = (command) => command.startsWith("akan ") ? `bun run ${command}` : command;
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  AppExecutor,
4
4
  LibExecutor
5
- } from "./index-yzsxc30b.js";
5
+ } from "./index-2tv3fdx4.js";
6
6
 
7
7
  // pkgs/@akanjs/devkit/workflow/utils.ts
8
8
  var jsonText = (value, { trailingNewline = true } = {}) => `${JSON.stringify(value, null, 2)}${trailingNewline ? `
@@ -914,9 +914,9 @@ ${options.closingIndent}}`);
914
914
  const beforeField = locator.fields[insertIndex];
915
915
  if (beforeField) {
916
916
  const leadingComments = ts.getLeadingCommentRanges(content, beforeField.fullStart) ?? [];
917
- const insertAt2 = lineStartAt(content, leadingComments[0]?.pos ?? beforeField.start);
918
- const indent2 = lineIndentAt(content, beforeField.start) || options.fieldIndent;
919
- return spliceText(content, insertAt2, insertAt2, `${indent2}${formattedLine}
917
+ const insertAt = lineStartAt(content, leadingComments[0]?.pos ?? beforeField.start);
918
+ const indent = lineIndentAt(content, beforeField.start) || options.fieldIndent;
919
+ return spliceText(content, insertAt, insertAt, `${indent}${formattedLine}
920
920
  `);
921
921
  }
922
922
  const afterField = locator.fields[locator.fields.length - 1];
@@ -1,14 +1,14 @@
1
1
  // @bun
2
2
  import {
3
- getDirname
4
- } from "./index-1577bej2.js";
3
+ getDirname2
4
+ } from "./index-6wwzs1bc.js";
5
5
 
6
6
  // pkgs/@akanjs/devkit/prompter.ts
7
7
  import fsPromise from "fs/promises";
8
8
  import { input, select } from "@inquirer/prompts";
9
9
  class Prompter {
10
10
  static async#getGuidelineRoot() {
11
- const dirname = getDirname(import.meta.url);
11
+ const dirname = getDirname2(import.meta.url);
12
12
  const candidates = [`${dirname}/guidelines`, `${dirname}/../cli/guidelines`];
13
13
  for (const candidate of candidates) {
14
14
  try {
@@ -19,7 +19,6 @@ class Prompter {
19
19
  return candidates[0];
20
20
  }
21
21
  static async selectGuideline() {
22
- const guidelineRoot = await Prompter.#getGuidelineRoot();
23
22
  const guideNames = await Prompter.listGuidelines();
24
23
  return await select({
25
24
  message: "Select a guideline",
@@ -37,10 +36,11 @@ class Prompter {
37
36
  return JSON.parse(guideJson);
38
37
  }
39
38
  static async getInstruction(guideName) {
39
+ const guideNames = await Prompter.listGuidelines();
40
+ if (!guideNames.includes(guideName))
41
+ throw new Error(`Unknown guideline "${guideName}". Available: ${guideNames.join(", ")}`);
40
42
  const guidelineRoot = await Prompter.#getGuidelineRoot();
41
- const filePath = `${guidelineRoot}/${guideName}/${guideName}.instruction.md`;
42
- const content = await fsPromise.readFile(filePath, "utf-8");
43
- return content;
43
+ return await fsPromise.readFile(`${guidelineRoot}/${guideName}/${guideName}.instruction.md`, "utf-8");
44
44
  }
45
45
  static async getUpdateRequest(guideName) {
46
46
  return await input({
@@ -422,7 +422,7 @@ class HmrWatcher {
422
422
  this.#unreportedChanges += unreported;
423
423
  if (!this.#reportedCompensating) {
424
424
  this.#reportedCompensating = true;
425
- this.#logger.info(`[hmr] recovered ${unreported} change(s) that fs.watch did not report; Bun coalesces concurrent saves and drops all but one, so changes are resolved by mtime`);
425
+ this.#logger.verbose(`[hmr] recovered ${unreported} change(s) that fs.watch did not report; Bun coalesces concurrent saves and drops all but one, so changes are resolved by mtime`);
426
426
  }
427
427
  this.#logger.verbose(`[hmr] mtime scan found ${unreported} change(s) fs.watch never reported (${this.#unreportedChanges} total)`);
428
428
  }
@@ -463,6 +463,7 @@ class HmrWatcher {
463
463
 
464
464
  // pkgs/@akanjs/devkit/frontendBuild/watchRootResolver.ts
465
465
  import fs2 from "fs";
466
+ import { readFile } from "fs/promises";
466
467
  import path4 from "path";
467
468
 
468
469
  class WatchRootResolver {
@@ -472,6 +473,10 @@ class WatchRootResolver {
472
473
  }
473
474
  async resolve() {
474
475
  const tsconfig = await this.#app.getTsConfig();
476
+ const appDir = path4.resolve(this.#app.cwdPath);
477
+ const appsContainer = path4.dirname(appDir);
478
+ const libsContainer = path4.resolve(this.#app.workspace.workspaceRoot, "libs");
479
+ const libRoots = await this.#resolveLibRoots(libsContainer);
475
480
  const set = new Set;
476
481
  set.add(path4.resolve(`${this.#app.cwdPath}/page`));
477
482
  for (const targets of Object.values(tsconfig.compilerOptions.paths ?? {})) {
@@ -482,12 +487,55 @@ class WatchRootResolver {
482
487
  continue;
483
488
  const cleaned = target.replace(/\/?\*+.*$/, "").replace(/\/[^/]+\.[^/]+$/, "");
484
489
  const resolved = path4.resolve(this.#app.workspace.workspaceRoot, cleaned);
485
- if (fs2.existsSync(resolved))
486
- set.add(resolved);
490
+ if (resolved === libsContainer && libRoots) {
491
+ for (const root of libRoots)
492
+ set.add(root);
493
+ continue;
494
+ }
495
+ const root = resolved === appsContainer ? appDir : resolved;
496
+ if (fs2.existsSync(root))
497
+ set.add(root);
487
498
  }
488
499
  }
489
500
  return [...set];
490
501
  }
502
+ async#resolveLibRoots(libsContainer) {
503
+ const libDeps = await this.#resolveLibDeps(libsContainer);
504
+ if (!libDeps)
505
+ return null;
506
+ return libDeps.map((name) => path4.join(libsContainer, name)).filter((dir) => fs2.existsSync(dir));
507
+ }
508
+ async#resolveLibDeps(libsContainer) {
509
+ const scanInfo = this.#app.getScanInfo({ allowEmpty: true });
510
+ if (scanInfo?.type === "app")
511
+ return scanInfo.libDeps;
512
+ const direct = await WatchRootResolver.#readManifestLibDeps(path4.join(this.#app.cwdPath, "akan.app.json"));
513
+ if (!direct)
514
+ return null;
515
+ const closure = new Set;
516
+ const queue = [...direct];
517
+ while (queue.length > 0) {
518
+ const name = queue.shift();
519
+ if (!name || closure.has(name))
520
+ continue;
521
+ closure.add(name);
522
+ const nested = await WatchRootResolver.#readManifestLibDeps(path4.join(libsContainer, name, "akan.lib.json"));
523
+ if (!nested)
524
+ return null;
525
+ queue.push(...nested);
526
+ }
527
+ return [...closure];
528
+ }
529
+ static async#readManifestLibDeps(manifestPath) {
530
+ try {
531
+ const parsed = JSON.parse(await readFile(manifestPath, "utf8"));
532
+ if (!Array.isArray(parsed.libDeps))
533
+ return null;
534
+ return parsed.libDeps.filter((name) => typeof name === "string" && name.length > 0);
535
+ } catch {
536
+ return null;
537
+ }
538
+ }
491
539
  }
492
540
 
493
541
  export { HmrWatcher, WatchRootResolver };
@@ -0,0 +1,159 @@
1
+ // @bun
2
+ import {
3
+ AppInfo
4
+ } from "./index-2tv3fdx4.js";
5
+
6
+ // pkgs/@akanjs/devkit/slicePlanner.ts
7
+ class SlicePlanner {
8
+ static memberDirs = ["apps", "libs", "pkgs"];
9
+ static toolchainDependencies = [
10
+ "@akanjs/cli",
11
+ "@akanjs/devkit",
12
+ "@biomejs/biome",
13
+ "@types/bun",
14
+ "akanjs",
15
+ "tailwindcss",
16
+ "typescript"
17
+ ];
18
+ static akanPackages = ["akanjs", "@akanjs/devkit", "@akanjs/cli"];
19
+ static async#frameworkPeers(workspace) {
20
+ const peers = new Set;
21
+ let found = false;
22
+ for (const akanPackage of SlicePlanner.akanPackages) {
23
+ for (const prefix of ["node_modules", "pkgs"]) {
24
+ const manifestPath = `${prefix}/${akanPackage}/package.json`;
25
+ if (!await workspace.exists(manifestPath))
26
+ continue;
27
+ const manifest = await workspace.readJson(manifestPath);
28
+ for (const peer of Object.keys(manifest.peerDependencies ?? {}))
29
+ peers.add(peer);
30
+ found = true;
31
+ break;
32
+ }
33
+ }
34
+ return found ? peers : null;
35
+ }
36
+ static typesPackageOf(dependency) {
37
+ return dependency.startsWith("@") ? `@types/${dependency.slice(1).replace("/", "__")}` : `@types/${dependency}`;
38
+ }
39
+ static async pruneDependencies(workspace, rootPackageJson, required) {
40
+ const peers = await SlicePlanner.#frameworkPeers(workspace);
41
+ const { workspaces: _workspaces, ...rest } = rootPackageJson;
42
+ if (!peers)
43
+ return {
44
+ packageJson: rest,
45
+ pruned: [],
46
+ warnings: [`dependencies carried whole \u2014 none of ${SlicePlanner.akanPackages.join(", ")} is installed`]
47
+ };
48
+ const keep = new Set([...SlicePlanner.toolchainDependencies, ...peers, ...required]);
49
+ for (const dependency of [...keep])
50
+ keep.add(SlicePlanner.typesPackageOf(dependency));
51
+ const pick = (entries) => Object.fromEntries(Object.entries(entries ?? {}).filter(([name]) => keep.has(name)));
52
+ const patched = rootPackageJson.patchedDependencies ?? {};
53
+ const packageJson = {
54
+ ...rest,
55
+ dependencies: pick(rootPackageJson.dependencies),
56
+ devDependencies: pick(rootPackageJson.devDependencies),
57
+ ...Object.keys(patched).length ? {
58
+ patchedDependencies: Object.fromEntries(Object.entries(patched).filter(([spec]) => keep.has(spec.slice(0, spec.lastIndexOf("@")))))
59
+ } : {}
60
+ };
61
+ const pruned = Object.keys({ ...rootPackageJson.dependencies, ...rootPackageJson.devDependencies }).filter((name) => !keep.has(name)).sort();
62
+ return { packageJson, pruned, warnings: [] };
63
+ }
64
+ #app;
65
+ constructor(app) {
66
+ this.#app = app;
67
+ }
68
+ async#trackedFiles(paths) {
69
+ return await this.#app.workspace.listGitFiles(paths);
70
+ }
71
+ async#untrackedFiles(paths) {
72
+ const [tracked, all] = await Promise.all([
73
+ this.#app.workspace.listGitFiles(paths),
74
+ this.#app.workspace.listGitFiles(paths, { untracked: true })
75
+ ]);
76
+ const trackedSet = new Set(tracked);
77
+ return all.filter((file) => !trackedSet.has(file));
78
+ }
79
+ #isMemberFile(file) {
80
+ return SlicePlanner.memberDirs.includes(file.split("/")[0] ?? "");
81
+ }
82
+ static #requiredDependencies(appInfo) {
83
+ const scanResults = [
84
+ appInfo.getScanResult(),
85
+ ...[...appInfo.getLibInfos().values()].map((lib) => lib.getScanResult())
86
+ ];
87
+ return new Set([
88
+ ...scanResults.flatMap((scanResult) => [
89
+ ...scanResult.dependencies,
90
+ ...scanResult.devDependencies,
91
+ ...scanResult.pkgDeps
92
+ ])
93
+ ]);
94
+ }
95
+ async#patchWarnings(packageJson) {
96
+ const patchedDependencies = packageJson.patchedDependencies ?? {};
97
+ const missing = (await Promise.all(Object.entries(patchedDependencies).map(async ([spec, patchPath]) => await this.#app.workspace.exists(patchPath) ? null : `${spec} -> ${patchPath}`))).filter((entry) => !!entry);
98
+ return missing.length ? [`patchedDependencies references a missing patch file: ${missing.join(", ")}`] : [];
99
+ }
100
+ async plan() {
101
+ const appInfo = await AppInfo.fromExecutor(this.#app);
102
+ const libs = appInfo.getLibs();
103
+ const slicePaths = [`apps/${this.#app.name}`, ...libs.map((lib) => `libs/${lib}`)];
104
+ const [appFiles, allTracked, untracked, rootPackageJson] = await Promise.all([
105
+ this.#trackedFiles([`apps/${this.#app.name}`]),
106
+ this.#trackedFiles(["."]),
107
+ this.#untrackedFiles(slicePaths),
108
+ this.#app.workspace.getPackageJson()
109
+ ]);
110
+ const libFiles = Object.fromEntries(await Promise.all(libs.map(async (lib) => [lib, await this.#trackedFiles([`libs/${lib}`])])));
111
+ const rootFiles = allTracked.filter((file) => !this.#isMemberFile(file));
112
+ const required = SlicePlanner.#requiredDependencies(appInfo);
113
+ const {
114
+ packageJson,
115
+ pruned,
116
+ warnings: manifestWarnings
117
+ } = await SlicePlanner.pruneDependencies(this.#app.workspace, rootPackageJson, required);
118
+ const warnings = [
119
+ ...untracked.length ? [`${untracked.length} untracked file(s) under the slice paths: ${untracked.join(", ")}`] : [],
120
+ ...rootPackageJson.workspaces ? ["root `workspaces` dropped \u2014 a slice consumes akanjs from the registry"] : [],
121
+ ...manifestWarnings,
122
+ ...await this.#patchWarnings(rootPackageJson)
123
+ ];
124
+ return {
125
+ app: this.#app.name,
126
+ libs,
127
+ appFiles,
128
+ libFiles,
129
+ rootFiles,
130
+ packageJson,
131
+ requiredDependencies: [...required].sort(),
132
+ unusedDependencies: pruned,
133
+ warnings
134
+ };
135
+ }
136
+ }
137
+ var rootEntriesOf = (rootFiles) => [...new Set(rootFiles.map((file) => file.split("/")[0] ?? file))].sort();
138
+ function formatSlicePlan(plan) {
139
+ const libCounts = Object.entries(plan.libFiles).map(([lib, files]) => ` - libs/${lib}: ${files.length} files`);
140
+ const sections = [
141
+ "Akan Slice Plan",
142
+ `app: ${plan.app} (apps/${plan.app}: ${plan.appFiles.length} files)`,
143
+ `libs: ${plan.libs.length ? plan.libs.join(", ") : "(none)"}`,
144
+ ...libCounts,
145
+ `workspace shell: ${plan.rootFiles.length} files across ${rootEntriesOf(plan.rootFiles).join(", ")}`,
146
+ "",
147
+ `Root dependencies this slice does not use (${plan.unusedDependencies.length}) \u2014 pruned:`,
148
+ "",
149
+ ...plan.unusedDependencies.length ? plan.unusedDependencies.map((dep) => ` - ${dep}`) : [" (none)"],
150
+ "",
151
+ `Warnings (${plan.warnings.length}):`,
152
+ "",
153
+ ...plan.warnings.length ? plan.warnings.map((warning) => ` - ${warning}`) : [" (none)"]
154
+ ];
155
+ return sections.join(`
156
+ `);
157
+ }
158
+
159
+ export { SlicePlanner, formatSlicePlan };
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  FileSys
4
- } from "./index-67546d0j.js";
4
+ } from "./index-q0y2jq9j.js";
5
5
 
6
6
  // pkgs/@akanjs/devkit/dependencyScanner.ts
7
7
  import { builtinModules } from "module";
@@ -9,39 +9,41 @@ import * as path from "path";
9
9
  import ignore from "ignore";
10
10
  import ts from "typescript";
11
11
  var testFileRegex = /\.(?:test|spec)\.[cm]?[tj]sx?$/;
12
+ var skipDirs = ["node_modules", "dist", "build", ".git", ".next", "public", "ios", "android", "__fixtures__"];
13
+ var isSkippedPath = (filePath) => skipDirs.some((dir) => filePath.includes(`/${dir}/`) || filePath.startsWith(`${dir}/`));
12
14
  var builtinModuleSet = new Set([...builtinModules, ...builtinModules.map((mod) => `node:${mod}`)]);
13
15
  var stripShebang = (source) => source.replace(/^#!.*(?:\r?\n|$)/, "");
14
16
 
15
- class TypeScriptDependencyScanner {
17
+ class TypeScriptDependencyScanner2 {
16
18
  #fileDependencies = new Map;
17
19
  #fileRuntimeDependencies = new Map;
18
20
  #fileTypeDependencies = new Map;
19
21
  #visitedFiles = new Set;
20
22
  #tsTranspiler = new Bun.Transpiler({ loader: "ts" });
21
23
  #tsxTranspiler = new Bun.Transpiler({ loader: "tsx" });
22
- directory;
23
- rootPackageJson;
24
- ig;
25
- workspaceRoot;
24
+ #directory;
25
+ #rootPackageJson;
26
+ #ig;
27
+ #workspaceRoot;
26
28
  constructor(directory, {
27
29
  workspaceRoot,
28
30
  rootPackageJson,
29
31
  gitignorePatterns = []
30
32
  }) {
31
- this.directory = directory;
32
- this.rootPackageJson = rootPackageJson;
33
- this.ig = ignore().add(gitignorePatterns);
34
- this.workspaceRoot = workspaceRoot;
33
+ this.#directory = directory;
34
+ this.#rootPackageJson = rootPackageJson;
35
+ this.#ig = ignore().add(gitignorePatterns);
36
+ this.#workspaceRoot = workspaceRoot;
35
37
  }
36
38
  async getMonorepoDependencies(projectName, { pkgs = [], libs = [] } = {}) {
37
- const npmSet = new Set(Object.keys({ ...this.rootPackageJson.dependencies, ...this.rootPackageJson.devDependencies }));
39
+ const npmSet = new Set(Object.keys({ ...this.#rootPackageJson.dependencies, ...this.#rootPackageJson.devDependencies }));
38
40
  const pkgPathSet = new Set(pkgs);
39
41
  const libPathSet = new Set(libs.map((lib) => `@libs/${lib}`));
40
42
  await this.getDependencies();
41
43
  const [npmDepSet, pkgPathDepSet, libPathDepSet] = this.#getImportSetsFromDependencies([npmSet, pkgPathSet, libPathSet], this.#fileRuntimeDependencies);
42
44
  const [npmDevDepSet] = this.#getImportSetsFromDependencies([npmSet], this.#fileTypeDependencies);
43
45
  const pkgDeps = [...pkgPathDepSet];
44
- const libDeps = [...libPathDepSet].map((path2) => path2.replace("@libs/", "")).filter((libName) => libName !== projectName);
46
+ const libDeps = [...libPathDepSet].map((path) => path.replace("@libs/", "")).filter((libName) => libName !== projectName);
45
47
  return {
46
48
  pkgDeps,
47
49
  libDeps,
@@ -52,11 +54,11 @@ class TypeScriptDependencyScanner {
52
54
  async getPackageBuildDependencies(projectName) {
53
55
  const runtimeDeps = new Set;
54
56
  const devDeps = new Set;
55
- const sourceFiles = await this.#findTypeScriptFiles(this.directory, {
57
+ const sourceFiles = await this.#findTypeScriptFiles(this.#directory, {
56
58
  excludeBuildFiles: true,
57
59
  excludeTestFiles: true
58
60
  });
59
- const cssFiles = await this.#findCssFiles(this.directory);
61
+ const cssFiles = await this.#findCssFiles(this.#directory);
60
62
  for (const filePath of sourceFiles) {
61
63
  const fileContent = await FileSys.readText(filePath);
62
64
  const { imports, typeImports } = this.#extractImports(fileContent, filePath);
@@ -67,7 +69,7 @@ class TypeScriptDependencyScanner {
67
69
  const fileContent = await FileSys.readText(filePath);
68
70
  this.#addNormalizedImports(runtimeDeps, this.#extractCssPluginImports(fileContent), projectName);
69
71
  }
70
- const buildFilePath = path.join(this.directory, "build.ts");
72
+ const buildFilePath = path.join(this.#directory, "build.ts");
71
73
  if (await FileSys.fileExists(buildFilePath)) {
72
74
  const fileContent = await FileSys.readText(buildFilePath);
73
75
  const { imports, typeImports } = this.#extractImports(fileContent, buildFilePath);
@@ -75,7 +77,7 @@ class TypeScriptDependencyScanner {
75
77
  }
76
78
  for (const dep of runtimeDeps)
77
79
  devDeps.delete(dep);
78
- const rootDeps = { ...this.rootPackageJson.dependencies, ...this.rootPackageJson.devDependencies };
80
+ const rootDeps = { ...this.#rootPackageJson.dependencies, ...this.#rootPackageJson.devDependencies };
79
81
  const missingDeps = [];
80
82
  for (const dep of [...runtimeDeps, ...devDeps]) {
81
83
  if (rootDeps[dep] || await this.#hasWorkspacePackage(dep))
@@ -89,7 +91,7 @@ class TypeScriptDependencyScanner {
89
91
  };
90
92
  }
91
93
  async#hasWorkspacePackage(dep) {
92
- const packageJsonPath = path.join(this.workspaceRoot, "pkgs", dep, "package.json");
94
+ const packageJsonPath = path.join(this.#workspaceRoot, "pkgs", dep, "package.json");
93
95
  if (!await Bun.file(packageJsonPath).exists())
94
96
  return false;
95
97
  try {
@@ -132,9 +134,9 @@ class TypeScriptDependencyScanner {
132
134
  this.#fileRuntimeDependencies.clear();
133
135
  this.#fileTypeDependencies.clear();
134
136
  this.#visitedFiles.clear();
135
- const files = await this.#findTypeScriptFiles(this.directory);
137
+ const files = await this.#findTypeScriptFiles(this.#directory);
136
138
  for (const file of files)
137
- await this.#analyzeFile(file, this.directory);
139
+ await this.#analyzeFile(file, this.#directory);
138
140
  return this.#fileDependencies;
139
141
  }
140
142
  async#findTypeScriptFiles(directory, {
@@ -142,18 +144,17 @@ class TypeScriptDependencyScanner {
142
144
  excludeTestFiles = false
143
145
  } = {}) {
144
146
  const files = [];
145
- const skipDirs = ["node_modules", "dist", "build", ".git", ".next", "public", "ios", "android"];
146
147
  const glob = new Bun.Glob("**/*.{ts,tsx}");
147
148
  for await (const filePath of glob.scan({ cwd: directory, onlyFiles: true })) {
148
- if (skipDirs.some((dir) => filePath.includes(`/${dir}/`) || filePath.startsWith(`${dir}/`)))
149
+ if (isSkippedPath(filePath))
149
150
  continue;
150
151
  if (excludeBuildFiles && filePath === "build.ts")
151
152
  continue;
152
153
  if (excludeTestFiles && testFileRegex.test(filePath))
153
154
  continue;
154
155
  const fullPath = path.join(directory, filePath);
155
- const relativePath = path.relative(this.workspaceRoot, fullPath);
156
- if (this.ig.ignores(relativePath))
156
+ const relativePath = path.relative(this.#workspaceRoot, fullPath);
157
+ if (this.#ig.ignores(relativePath))
157
158
  continue;
158
159
  files.push(fullPath);
159
160
  }
@@ -161,14 +162,13 @@ class TypeScriptDependencyScanner {
161
162
  }
162
163
  async#findCssFiles(directory) {
163
164
  const files = [];
164
- const skipDirs = ["node_modules", "dist", "build", ".git", ".next", "public", "ios", "android"];
165
165
  const glob = new Bun.Glob("**/*.css");
166
166
  for await (const filePath of glob.scan({ cwd: directory, onlyFiles: true })) {
167
- if (skipDirs.some((dir) => filePath.includes(`/${dir}/`) || filePath.startsWith(`${dir}/`)))
167
+ if (isSkippedPath(filePath))
168
168
  continue;
169
169
  const fullPath = path.join(directory, filePath);
170
- const relativePath = path.relative(this.workspaceRoot, fullPath);
171
- if (this.ig.ignores(relativePath))
170
+ const relativePath = path.relative(this.#workspaceRoot, fullPath);
171
+ if (this.#ig.ignores(relativePath))
172
172
  continue;
173
173
  files.push(fullPath);
174
174
  }
@@ -289,7 +289,7 @@ class TypeScriptDependencyScanner {
289
289
  exec.workspace.getPackageJson(),
290
290
  exec.workspace.getGitignorePatterns()
291
291
  ]);
292
- return new TypeScriptDependencyScanner(exec.cwdPath, {
292
+ return new TypeScriptDependencyScanner2(exec.cwdPath, {
293
293
  workspaceRoot: exec.workspace.cwdPath,
294
294
  tsconfig,
295
295
  rootPackageJson,
@@ -298,4 +298,4 @@ class TypeScriptDependencyScanner {
298
298
  }
299
299
  }
300
300
 
301
- export { TypeScriptDependencyScanner };
301
+ export { TypeScriptDependencyScanner2 };