@codefast/cli 0.9.0 → 0.11.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 (279) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +345 -151
  3. package/dist/arrange/command.d.ts +7 -0
  4. package/dist/arrange/command.js +96 -132
  5. package/dist/arrange/domain/ast/ast-node.d.ts +394 -0
  6. package/dist/arrange/domain/ast/collectors-cn.d.ts +26 -0
  7. package/dist/arrange/domain/ast/collectors-jsx.d.ts +8 -0
  8. package/dist/arrange/domain/ast/collectors-tv.d.ts +34 -0
  9. package/dist/arrange/domain/ast/helpers.d.ts +36 -0
  10. package/dist/arrange/domain/ast/helpers.js +1 -0
  11. package/dist/arrange/domain/ast/simplify-targets.d.ts +22 -0
  12. package/dist/arrange/domain/ast/simplify-targets.js +18 -23
  13. package/dist/arrange/domain/ast/targets.d.ts +20 -0
  14. package/dist/arrange/domain/ast/translator.d.ts +32 -0
  15. package/dist/arrange/{typescript-ast-translator.js → domain/ast/translator.js} +19 -22
  16. package/dist/arrange/domain/constants.d.ts +111 -0
  17. package/dist/arrange/domain/grouping-service.d.ts +100 -0
  18. package/dist/arrange/domain/grouping.d.ts +21 -0
  19. package/dist/arrange/domain/imports.d.ts +14 -0
  20. package/dist/arrange/domain/source-text-formatters.d.ts +33 -0
  21. package/dist/arrange/domain/tailwind-token.d.ts +24 -0
  22. package/dist/arrange/domain/token-classifier.d.ts +47 -0
  23. package/dist/arrange/domain/types.d.ts +208 -0
  24. package/dist/arrange/group/cli-result.d.ts +7 -0
  25. package/dist/arrange/group/cli-result.js +12 -0
  26. package/dist/arrange/group/cli-schema.d.ts +17 -0
  27. package/dist/arrange/group/cli-schema.js +13 -0
  28. package/dist/arrange/group/output.d.ts +7 -0
  29. package/dist/arrange/group/output.js +10 -0
  30. package/dist/arrange/group/suggest.d.ts +8 -0
  31. package/dist/arrange/inspect/cli-result.d.ts +7 -0
  32. package/dist/arrange/inspect/cli-result.js +8 -0
  33. package/dist/arrange/inspect/cli-schema.d.ts +15 -0
  34. package/dist/arrange/inspect/cli-schema.js +9 -0
  35. package/dist/arrange/inspect/domain/analyze-service.d.ts +18 -0
  36. package/dist/arrange/inspect/output.d.ts +7 -0
  37. package/dist/arrange/inspect/output.js +42 -0
  38. package/dist/arrange/inspect/run.d.ts +10 -0
  39. package/dist/arrange/{analyze.js → inspect/run.js} +2 -2
  40. package/dist/arrange/prepare.d.ts +13 -0
  41. package/dist/arrange/{workspace.js → prepare.js} +5 -8
  42. package/dist/arrange/regroup/cli-result.d.ts +13 -0
  43. package/dist/arrange/regroup/cli-result.js +23 -0
  44. package/dist/arrange/regroup/cli-schema.d.ts +20 -0
  45. package/dist/arrange/regroup/cli-schema.js +14 -0
  46. package/dist/arrange/regroup/output.d.ts +14 -0
  47. package/dist/arrange/regroup/output.js +72 -0
  48. package/dist/arrange/regroup/process-file.d.ts +11 -0
  49. package/dist/arrange/regroup/run.d.ts +11 -0
  50. package/dist/arrange/{sync.js → regroup/run.js} +2 -2
  51. package/dist/arrange/resolve-target.d.ts +10 -0
  52. package/dist/arrange/resolve-target.js +3 -16
  53. package/dist/arrange/scan-target.d.ts +7 -0
  54. package/dist/arrange/simplify/cli-result.d.ts +7 -0
  55. package/dist/arrange/simplify/cli-result.js +8 -0
  56. package/dist/arrange/simplify/cli-schema.d.ts +17 -0
  57. package/dist/arrange/simplify/cli-schema.js +11 -0
  58. package/dist/arrange/simplify/fold-targets.d.ts +13 -0
  59. package/dist/arrange/simplify/fold-targets.js +139 -0
  60. package/dist/arrange/simplify/output.d.ts +7 -0
  61. package/dist/arrange/simplify/output.js +15 -0
  62. package/dist/arrange/simplify/process-file.d.ts +13 -0
  63. package/dist/arrange/simplify/process-file.js +49 -0
  64. package/dist/arrange/simplify/run.d.ts +14 -0
  65. package/dist/arrange/simplify/run.js +38 -0
  66. package/dist/arrange/simplify/variant-classname-probe.d.ts +35 -0
  67. package/dist/arrange/simplify/variant-classname-probe.js +95 -0
  68. package/dist/arrange/source-parse.d.ts +7 -0
  69. package/dist/arrange/source-parse.js +1 -1
  70. package/dist/audit/command.d.ts +8 -0
  71. package/dist/audit/command.js +134 -211
  72. package/dist/audit/comments/cli-result.d.ts +13 -0
  73. package/dist/audit/comments/cli-result.js +22 -0
  74. package/dist/audit/comments/cli-schema.d.ts +19 -0
  75. package/dist/audit/comments/cli-schema.js +13 -0
  76. package/dist/audit/comments/domain/comment-content.d.ts +26 -0
  77. package/dist/audit/comments/domain/comment-dividers.d.ts +62 -0
  78. package/dist/audit/comments/domain/link-references.d.ts +40 -0
  79. package/dist/audit/comments/domain/since-versions.d.ts +26 -0
  80. package/dist/audit/comments/domain/tsdoc-syntax.d.ts +20 -0
  81. package/dist/audit/comments/output.d.ts +7 -0
  82. package/dist/audit/comments/output.js +27 -0
  83. package/dist/audit/comments/prepare.d.ts +16 -0
  84. package/dist/audit/comments/prepare.js +12 -0
  85. package/dist/audit/comments/run.d.ts +17 -0
  86. package/dist/audit/{run-comments.js → comments/run.js} +5 -5
  87. package/dist/audit/display-names/cli-result.d.ts +13 -0
  88. package/dist/audit/display-names/cli-result.js +22 -0
  89. package/dist/audit/display-names/cli-schema.d.ts +18 -0
  90. package/dist/audit/display-names/cli-schema.js +12 -0
  91. package/dist/audit/display-names/domain/display-names.d.ts +11 -0
  92. package/dist/audit/display-names/output.d.ts +7 -0
  93. package/dist/audit/display-names/output.js +21 -0
  94. package/dist/audit/display-names/prepare.d.ts +16 -0
  95. package/dist/audit/display-names/prepare.js +12 -0
  96. package/dist/audit/display-names/run.d.ts +14 -0
  97. package/dist/audit/{run-display-names.js → display-names/run.js} +1 -1
  98. package/dist/audit/domain/types.d.ts +171 -0
  99. package/dist/audit/imports/cli-result.d.ts +13 -0
  100. package/dist/audit/imports/cli-result.js +22 -0
  101. package/dist/audit/imports/cli-schema.d.ts +18 -0
  102. package/dist/audit/imports/cli-schema.js +12 -0
  103. package/dist/audit/imports/domain/import-policy.d.ts +34 -0
  104. package/dist/audit/imports/domain/import-policy.js +140 -0
  105. package/dist/audit/imports/output.d.ts +7 -0
  106. package/dist/audit/imports/output.js +21 -0
  107. package/dist/audit/imports/prepare.d.ts +16 -0
  108. package/dist/audit/imports/prepare.js +12 -0
  109. package/dist/audit/imports/run.d.ts +14 -0
  110. package/dist/audit/{run-react.js → imports/run.js} +15 -5
  111. package/dist/audit/links/cli-result.d.ts +13 -0
  112. package/dist/audit/links/cli-result.js +22 -0
  113. package/dist/audit/links/cli-schema.d.ts +18 -0
  114. package/dist/audit/links/cli-schema.js +12 -0
  115. package/dist/audit/links/domain/markdown-links.d.ts +44 -0
  116. package/dist/audit/links/output.d.ts +7 -0
  117. package/dist/audit/links/output.js +21 -0
  118. package/dist/audit/links/prepare.d.ts +16 -0
  119. package/dist/audit/links/prepare.js +12 -0
  120. package/dist/audit/links/run.d.ts +14 -0
  121. package/dist/audit/{run-links.js → links/run.js} +1 -1
  122. package/dist/audit/prepare.d.ts +31 -0
  123. package/dist/audit/prepare.js +11 -134
  124. package/dist/audit/rtl/cli-result.d.ts +13 -0
  125. package/dist/audit/rtl/cli-result.js +22 -0
  126. package/dist/audit/rtl/cli-schema.d.ts +18 -0
  127. package/dist/audit/rtl/cli-schema.js +12 -0
  128. package/dist/audit/rtl/domain/audit-file.d.ts +7 -0
  129. package/dist/audit/{domain → rtl/domain}/audit-file.js +2 -2
  130. package/dist/audit/rtl/domain/mappings.d.ts +45 -0
  131. package/dist/audit/rtl/domain/tokenize.d.ts +14 -0
  132. package/dist/audit/rtl/output.d.ts +7 -0
  133. package/dist/audit/rtl/output.js +21 -0
  134. package/dist/audit/rtl/prepare.d.ts +13 -0
  135. package/dist/audit/rtl/prepare.js +41 -0
  136. package/dist/audit/rtl/run.d.ts +14 -0
  137. package/dist/audit/{run.js → rtl/run.js} +1 -1
  138. package/dist/bin.d.ts +2 -0
  139. package/dist/cli.d.ts +6 -0
  140. package/dist/core/cli/command-pipeline.d.ts +91 -0
  141. package/dist/core/cli/command-pipeline.js +83 -0
  142. package/dist/core/cli/format-error.d.ts +7 -0
  143. package/dist/core/cli/global-options.d.ts +15 -0
  144. package/dist/core/cli/global-options.js +1 -1
  145. package/dist/core/cli/positional.d.ts +6 -0
  146. package/dist/core/cli/resolve-root.d.ts +9 -0
  147. package/dist/core/cli/resolve-root.js +16 -0
  148. package/dist/core/cli/result-handle.d.ts +13 -0
  149. package/dist/core/cli/result-handle.js +0 -13
  150. package/dist/core/config/define-config.d.ts +7 -0
  151. package/dist/core/config/define-config.js +8 -0
  152. package/dist/core/config/loader.d.ts +18 -0
  153. package/dist/core/config/loader.js +2 -7
  154. package/dist/core/config/schema.d.ts +99 -0
  155. package/dist/core/config/schema.js +8 -86
  156. package/dist/core/config/warnings.d.ts +6 -0
  157. package/dist/core/config.d.ts +12 -0
  158. package/dist/core/errors.d.ts +25 -0
  159. package/dist/core/exit-codes.d.ts +18 -0
  160. package/dist/core/filesystem/filesystem.d.ts +44 -0
  161. package/dist/core/filesystem/node.d.ts +7 -0
  162. package/dist/core/filesystem/node.js +2 -1
  163. package/dist/core/glob.d.ts +19 -0
  164. package/dist/core/logger.d.ts +9 -0
  165. package/dist/core/result.d.ts +30 -0
  166. package/dist/core/schema-parse.d.ts +9 -0
  167. package/dist/core/source-text-edit.d.ts +47 -0
  168. package/dist/core/source-text-edit.js +22 -0
  169. package/dist/core/verbose-diagnostics.d.ts +6 -0
  170. package/dist/core/workspace/ancestor-directories.d.ts +12 -0
  171. package/dist/core/workspace/ancestor-directories.js +30 -0
  172. package/dist/core/workspace/markdown-walk.d.ts +7 -0
  173. package/dist/core/workspace/markdown-walk.js +2 -20
  174. package/dist/core/workspace/package-version.d.ts +9 -0
  175. package/dist/core/workspace/package-version.js +8 -12
  176. package/dist/core/workspace/resolver.d.ts +39 -0
  177. package/dist/core/workspace/resolver.js +58 -75
  178. package/dist/core/workspace/skip-directories.d.ts +6 -0
  179. package/dist/core/workspace/source-walk.d.ts +16 -0
  180. package/dist/core/workspace/source-walk.js +2 -19
  181. package/dist/core/workspace/typescript-walk.d.ts +7 -0
  182. package/dist/core/workspace/typescript-walk.js +2 -23
  183. package/dist/core/workspace/walk-files.d.ts +7 -0
  184. package/dist/core/workspace/walk-files.js +27 -0
  185. package/dist/core/workspace/well-known-files.d.ts +18 -0
  186. package/dist/core/workspace/well-known-files.js +18 -0
  187. package/dist/index.d.ts +6 -0
  188. package/dist/index.js +5 -0
  189. package/dist/mirror/cli-result.d.ts +13 -0
  190. package/dist/mirror/cli-schema.d.ts +8 -0
  191. package/dist/mirror/cli-schema.js +1 -1
  192. package/dist/mirror/command.d.ts +7 -0
  193. package/dist/mirror/command.js +31 -60
  194. package/dist/mirror/dist-filesystem-node.d.ts +8 -0
  195. package/dist/mirror/{dist-filesystem-impl.js → dist-filesystem-node.js} +1 -1
  196. package/dist/mirror/domain/constants.d.ts +18 -0
  197. package/dist/mirror/domain/constants.js +0 -12
  198. package/dist/mirror/domain/dirent-guard.d.ts +10 -0
  199. package/dist/mirror/domain/dist-filesystem.d.ts +9 -0
  200. package/dist/mirror/domain/errors.d.ts +24 -0
  201. package/dist/mirror/domain/exports.d.ts +36 -0
  202. package/dist/mirror/domain/package-display-name.d.ts +8 -0
  203. package/dist/mirror/domain/path-normalizer.d.ts +6 -0
  204. package/dist/mirror/domain/types.d.ts +188 -0
  205. package/dist/mirror/output.d.ts +21 -0
  206. package/dist/mirror/output.js +126 -1
  207. package/dist/mirror/package-path.d.ts +19 -0
  208. package/dist/mirror/prepare.d.ts +15 -0
  209. package/dist/mirror/prepare.js +6 -10
  210. package/dist/mirror/run.d.ts +12 -0
  211. package/dist/mirror/{sync.js → run.js} +5 -3
  212. package/dist/mirror/supplement-exports.d.ts +27 -0
  213. package/dist/mirror/supplement-exports.js +2 -2
  214. package/dist/mirror/sync-workspace-package.d.ts +9 -0
  215. package/dist/mirror/sync-workspace-package.js +4 -4
  216. package/dist/mirror/write-exports.d.ts +15 -0
  217. package/dist/pack-slim/cli-result.d.ts +13 -0
  218. package/dist/pack-slim/cli-schema.d.ts +17 -0
  219. package/dist/pack-slim/cli-schema.js +1 -1
  220. package/dist/pack-slim/command.d.ts +7 -0
  221. package/dist/pack-slim/command.js +31 -66
  222. package/dist/pack-slim/domain/transform.d.ts +69 -0
  223. package/dist/pack-slim/domain/types.d.ts +46 -0
  224. package/dist/pack-slim/output.d.ts +15 -0
  225. package/dist/pack-slim/prepare.d.ts +21 -0
  226. package/dist/pack-slim/prepare.js +14 -0
  227. package/dist/pack-slim/run.d.ts +23 -0
  228. package/dist/pack-slim/{sync.js → run.js} +4 -4
  229. package/dist/pack-slim/working-tree.d.ts +20 -0
  230. package/dist/tag/cli-result.d.ts +13 -0
  231. package/dist/tag/cli-result.js +14 -1
  232. package/dist/tag/cli-schema.d.ts +8 -0
  233. package/dist/tag/cli-schema.js +3 -4
  234. package/dist/tag/command.d.ts +7 -0
  235. package/dist/tag/command.js +33 -60
  236. package/dist/tag/domain/skip-filter.d.ts +13 -0
  237. package/dist/tag/domain/skip-filter.js +29 -0
  238. package/dist/tag/domain/types.d.ts +131 -0
  239. package/dist/tag/domain/version-summary.d.ts +13 -0
  240. package/dist/tag/domain/version-summary.js +24 -0
  241. package/dist/tag/output.d.ts +17 -0
  242. package/dist/tag/output.js +6 -9
  243. package/dist/tag/prepare.d.ts +13 -0
  244. package/dist/tag/prepare.js +4 -4
  245. package/dist/tag/run.d.ts +10 -0
  246. package/dist/tag/{sync.js → run.js} +20 -50
  247. package/dist/tag/target/candidates.d.ts +8 -0
  248. package/dist/tag/{target-candidates.js → target/candidates.js} +2 -2
  249. package/dist/tag/target/resolve-path.d.ts +10 -0
  250. package/dist/tag/target/runner.d.ts +8 -0
  251. package/dist/tag/{target-runner.js → target/runner.js} +2 -2
  252. package/dist/tag/writer/since-writer.d.ts +32 -0
  253. package/dist/tag/writer/version-resolver.d.ts +7 -0
  254. package/package.json +21 -2
  255. package/dist/arrange/cli-schema.js +0 -34
  256. package/dist/arrange/output.js +0 -127
  257. package/dist/arrange/simplify-process-file.js +0 -30
  258. package/dist/arrange/simplify-sync.js +0 -30
  259. package/dist/audit/cli-schema.js +0 -66
  260. package/dist/audit/domain/react-imports.js +0 -91
  261. package/dist/audit/output.js +0 -213
  262. package/dist/mirror/sync-reporter.js +0 -124
  263. package/dist/mirror/sync-types.js +0 -1
  264. /package/dist/arrange/{suggest.js → group/suggest.js} +0 -0
  265. /package/dist/arrange/{domain → inspect/domain}/analyze-service.js +0 -0
  266. /package/dist/arrange/{process-file.js → regroup/process-file.js} +0 -0
  267. /package/dist/audit/{domain → comments/domain}/comment-content.js +0 -0
  268. /package/dist/audit/{domain → comments/domain}/comment-dividers.js +0 -0
  269. /package/dist/audit/{domain → comments/domain}/link-references.js +0 -0
  270. /package/dist/audit/{domain → comments/domain}/since-versions.js +0 -0
  271. /package/dist/audit/{domain → comments/domain}/tsdoc-syntax.js +0 -0
  272. /package/dist/audit/{domain → display-names/domain}/display-names.js +0 -0
  273. /package/dist/audit/{domain → links/domain}/markdown-links.js +0 -0
  274. /package/dist/audit/{domain → rtl/domain}/mappings.js +0 -0
  275. /package/dist/audit/{domain → rtl/domain}/tokenize.js +0 -0
  276. /package/dist/core/filesystem/{port.js → filesystem.js} +0 -0
  277. /package/dist/tag/{resolve-target-path.js → target/resolve-path.js} +0 -0
  278. /package/dist/tag/{since-writer.js → writer/since-writer.js} +0 -0
  279. /package/dist/tag/{version-resolver.js → writer/version-resolver.js} +0 -0
@@ -1,35 +1,28 @@
1
- import { globSync } from "node:fs";
2
1
  import path from "node:path";
3
- import { fileURLToPath } from "node:url";
4
2
  import { parse as parseYaml } from "yaml";
5
3
  import { messageFrom } from "#/core/errors";
6
4
  import { createAnyGlobMatcher } from "#/core/glob";
7
5
  import { logger } from "#/core/logger";
6
+ import { findNearestAncestor } from "#/core/workspace/ancestor-directories";
7
+ import { packageJsonFileName, workspaceYamlFileName } from "#/core/workspace/well-known-files";
8
8
  /**
9
- * Resolves the monorepo root (directory containing `pnpm-workspace.yaml`).
9
+ * Resolves the project root: the `pnpm-workspace.yaml` directory, or the nearest `package.json` for a single package.
10
10
  *
11
- * @since 0.3.16-canary.0
11
+ * @since 0.10.0
12
12
  */
13
- export function findRepoRoot(fromDirectory, fs) {
14
- const candidates = [path.dirname(fileURLToPath(import.meta.url)), fromDirectory];
15
- for (const start of candidates) {
16
- let dir = path.resolve(start);
17
- for (;;) {
18
- if (fs.existsSync(path.join(dir, "pnpm-workspace.yaml"))) {
19
- return dir;
20
- }
21
- const parent = path.dirname(dir);
22
- if (parent === dir) {
23
- break;
24
- }
25
- dir = parent;
26
- }
13
+ export function resolveProjectRoot(fromDirectory, fs) {
14
+ // Resolution follows where the user is (cwd), not where the CLI is installed.
15
+ const workspaceRoot = findNearestAncestor(fromDirectory, (directoryPath) => fs.existsSync(path.join(directoryPath, workspaceYamlFileName)));
16
+ if (workspaceRoot !== undefined) {
17
+ return { rootDir: workspaceRoot, mode: "workspace" };
27
18
  }
28
- throw new Error(`Could not locate monorepo root (missing pnpm-workspace.yaml). Searched from: ${candidates.join(", ")}`);
19
+ const packageRoot = findNearestAncestor(fromDirectory, (directoryPath) => fs.existsSync(path.join(directoryPath, packageJsonFileName)));
20
+ if (packageRoot !== undefined) {
21
+ return { rootDir: packageRoot, mode: "single-package" };
22
+ }
23
+ throw new Error(`Could not locate a project root (no pnpm-workspace.yaml and no package.json found from: ${fromDirectory})`);
29
24
  }
30
- const workspaceYamlFileName = "pnpm-workspace.yaml";
31
25
  const defaultIncludePatterns = ["packages/*"];
32
- const packageJsonFileName = "package.json";
33
26
  function toPosix(filePath) {
34
27
  return filePath.split(path.sep).join("/");
35
28
  }
@@ -71,33 +64,28 @@ function splitPnpmWorkspacePackagesArray(raw) {
71
64
  return { include, exclude };
72
65
  }
73
66
  function parsePnpmWorkspaceDocument(doc) {
74
- const wf = workspaceYamlFileName;
75
- if (doc === null || doc === undefined) {
76
- throw new Error(`${wf} root document must be an object (got empty or null)`);
77
- }
78
- if (typeof doc !== "object" || Array.isArray(doc)) {
79
- throw new Error(`${wf} root must be a mapping, not an array or scalar`);
67
+ if (doc === null || typeof doc !== "object" || Array.isArray(doc)) {
68
+ throw new Error(`${workspaceYamlFileName} root must be a mapping, not an array or scalar`);
80
69
  }
81
70
  const parsedDoc = doc;
82
71
  if (!Object.prototype.hasOwnProperty.call(parsedDoc, "packages")) {
83
72
  return { include: [], exclude: [], hasPackagesKey: false, isEmptyPackagesArray: false };
84
73
  }
85
- const pkgs = parsedDoc.packages;
86
- if (!Array.isArray(pkgs)) {
87
- throw new Error(`${wf} field "packages" must be an array`);
74
+ const packagesField = parsedDoc.packages;
75
+ if (!Array.isArray(packagesField)) {
76
+ throw new Error(`${workspaceYamlFileName} field "packages" must be an array`);
88
77
  }
89
- if (pkgs.length === 0) {
78
+ if (packagesField.length === 0) {
90
79
  return { include: [], exclude: [], hasPackagesKey: true, isEmptyPackagesArray: true };
91
80
  }
92
81
  return {
93
- ...splitPnpmWorkspacePackagesArray(pkgs),
82
+ ...splitPnpmWorkspacePackagesArray(packagesField),
94
83
  hasPackagesKey: true,
95
84
  isEmptyPackagesArray: false,
96
85
  };
97
86
  }
98
87
  async function readWorkspaceYaml(rootDir, fs) {
99
- const wf = workspaceYamlFileName;
100
- const workspaceYamlPath = path.join(rootDir, wf);
88
+ const workspaceYamlPath = path.join(rootDir, workspaceYamlFileName);
101
89
  if (!fs.existsSync(workspaceYamlPath)) {
102
90
  return { exists: false };
103
91
  }
@@ -106,7 +94,7 @@ async function readWorkspaceYaml(rootDir, fs) {
106
94
  raw = await fs.readFile(workspaceYamlPath, "utf8");
107
95
  }
108
96
  catch (caughtReadError) {
109
- throw new Error(`Failed to read ${wf}: ${messageFrom(caughtReadError)}`, {
97
+ throw new Error(`Failed to read ${workspaceYamlFileName}: ${messageFrom(caughtReadError)}`, {
110
98
  cause: caughtReadError,
111
99
  });
112
100
  }
@@ -115,12 +103,12 @@ async function readWorkspaceYaml(rootDir, fs) {
115
103
  doc = parseYaml(raw);
116
104
  }
117
105
  catch (caughtYamlParseError) {
118
- throw new Error(`Failed to parse ${wf}: ${messageFrom(caughtYamlParseError)}`, {
106
+ throw new Error(`Failed to parse ${workspaceYamlFileName}: ${messageFrom(caughtYamlParseError)}`, {
119
107
  cause: caughtYamlParseError,
120
108
  });
121
109
  }
122
110
  if (doc === null || doc === undefined) {
123
- throw new Error(`${wf} must define a mapping at the document root`);
111
+ throw new Error(`${workspaceYamlFileName} must define a mapping at the document root`);
124
112
  }
125
113
  return { exists: true, doc };
126
114
  }
@@ -131,47 +119,42 @@ async function readWorkspaceYaml(rootDir, fs) {
131
119
  */
132
120
  export async function listWorkspacePackageDirectories(rootDirectoryPathAbsolute, fs, suppressGlobPermissionDiagnostics) {
133
121
  const workspaceYaml = await readWorkspaceYaml(rootDirectoryPathAbsolute, fs);
134
- const defInc = defaultIncludePatterns;
122
+ if (!workspaceYaml.exists) {
123
+ // No pnpm-workspace.yaml: treat the nearest package as the whole workspace.
124
+ const rootPackageJsonPath = path.join(rootDirectoryPathAbsolute, packageJsonFileName);
125
+ return {
126
+ packageDirectoryPathsAbsolute: fs.existsSync(rootPackageJsonPath) ? [rootDirectoryPathAbsolute] : [],
127
+ layoutSource: "single-package",
128
+ hasPnpmWorkspaceYamlFile: false,
129
+ };
130
+ }
135
131
  let include;
136
132
  let exclude;
137
133
  let layoutSource;
138
- const hasPnpmWorkspaceYamlFile = workspaceYaml.exists;
139
- if (!workspaceYaml.exists) {
140
- include = [...defInc];
134
+ const parsed = parsePnpmWorkspaceDocument(workspaceYaml.doc);
135
+ if (!parsed.hasPackagesKey) {
136
+ include = [...defaultIncludePatterns];
141
137
  exclude = [];
142
138
  layoutSource = "default-patterns";
143
139
  }
140
+ else if (parsed.isEmptyPackagesArray) {
141
+ return {
142
+ packageDirectoryPathsAbsolute: [],
143
+ layoutSource: "declared-empty",
144
+ hasPnpmWorkspaceYamlFile: true,
145
+ };
146
+ }
144
147
  else {
145
- const parsed = parsePnpmWorkspaceDocument(workspaceYaml.doc);
146
- if (!parsed.hasPackagesKey) {
147
- include = [...defInc];
148
- exclude = [];
149
- layoutSource = "default-patterns";
150
- }
151
- else if (parsed.isEmptyPackagesArray) {
152
- return {
153
- packageDirectoryPathsAbsolute: [],
154
- layoutSource: "declared-empty",
155
- hasPnpmWorkspaceYamlFile: true,
156
- };
157
- }
158
- else {
159
- include = parsed.include;
160
- exclude = parsed.exclude;
161
- layoutSource = "pnpm-workspace-yaml";
162
- }
148
+ include = parsed.include;
149
+ exclude = parsed.exclude;
150
+ layoutSource = "pnpm-workspace-yaml";
163
151
  }
164
152
  const foundRelativePosixPackageRoots = new Set();
165
- const globOpts = {
166
- cwd: rootDirectoryPathAbsolute,
167
- posix: true,
168
- ...(path.sep === "\\" ? { windowsPathsNoEscape: true } : {}),
169
- };
170
153
  for (const pattern of include) {
171
- const globPat = workspacePatternToPackageJsonGlob(pattern);
154
+ const packageJsonGlob = workspacePatternToPackageJsonGlob(pattern);
172
155
  let matches;
173
156
  try {
174
- matches = globSync(globPat, globOpts);
157
+ matches = fs.globSync(packageJsonGlob, { cwd: rootDirectoryPathAbsolute });
175
158
  }
176
159
  catch (caughtGlobError) {
177
160
  if (isGlobPermissionError(caughtGlobError)) {
@@ -184,26 +167,26 @@ export async function listWorkspacePackageDirectories(rootDirectoryPathAbsolute,
184
167
  cause: caughtGlobError,
185
168
  });
186
169
  }
170
+ const suffix = `/${packageJsonFileName}`;
187
171
  for (const matchedPath of matches) {
188
- const posix = toPosix(matchedPath);
189
- const suffix = `/${packageJsonFileName}`;
190
- if (!posix.endsWith(suffix)) {
172
+ const posixPath = toPosix(matchedPath);
173
+ if (!posixPath.endsWith(suffix)) {
191
174
  continue;
192
175
  }
193
- const rel = posix.slice(0, -suffix.length);
194
- if (!rel) {
176
+ const relativeRoot = posixPath.slice(0, -suffix.length);
177
+ if (!relativeRoot) {
195
178
  continue;
196
179
  }
197
- foundRelativePosixPackageRoots.add(rel);
180
+ foundRelativePosixPackageRoots.add(relativeRoot);
198
181
  }
199
182
  }
200
183
  const isExcluded = createAnyGlobMatcher(exclude, { dot: true });
201
- const filteredRelative = [...foundRelativePosixPackageRoots].filter((rel) => !isExcluded(rel));
184
+ const filteredRelative = [...foundRelativePosixPackageRoots].filter((relativeRoot) => !isExcluded(relativeRoot));
202
185
  filteredRelative.sort((a, b) => a.localeCompare(b));
203
- const packageDirectoryPathsAbsolute = filteredRelative.map((rel) => path.resolve(rootDirectoryPathAbsolute, rel.split("/").join(path.sep)));
186
+ const packageDirectoryPathsAbsolute = filteredRelative.map((relativeRoot) => path.resolve(rootDirectoryPathAbsolute, relativeRoot.split("/").join(path.sep)));
204
187
  return {
205
188
  packageDirectoryPathsAbsolute,
206
189
  layoutSource,
207
- hasPnpmWorkspaceYamlFile,
190
+ hasPnpmWorkspaceYamlFile: true,
208
191
  };
209
192
  }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Directory names every workspace walk skips: build output, vendored trees, and tool caches.
3
+ *
4
+ * @since 0.6.0
5
+ */
6
+ export declare const defaultSkipDirectoryNames: ReadonlySet<string>;
@@ -0,0 +1,16 @@
1
+ import type { Filesystem } from "#/core/filesystem/filesystem";
2
+ /**
3
+ * Every hand-written source file a comment convention applies to — `.ts`, `.tsx`, `.css`, and ignore files.
4
+ *
5
+ * @remarks Emitted declarations are excluded: they carry whatever the compiler copied over,
6
+ * and rewriting them would be undone by the next build.
7
+ *
8
+ * @since 0.6.0
9
+ */
10
+ export declare function walkSourceFiles(rootDirectoryPath: string, fs: Filesystem): Array<string>;
11
+ /**
12
+ * Which comment syntax a path is written in, or `null` when the convention does not cover it.
13
+ *
14
+ * @since 0.6.0
15
+ */
16
+ export declare function sourceCommentLanguage(filePath: string): "css" | "ignore" | "js" | null;
@@ -1,5 +1,5 @@
1
1
  import path from "node:path";
2
- import { defaultSkipDirectoryNames } from "#/core/workspace/skip-directories";
2
+ import { walkFiles } from "#/core/workspace/walk-files";
3
3
  // Ignore files carry the divider convention over `#` comments — the content rules stay code-only.
4
4
  const ignoreFileNames = new Set([
5
5
  ".gitignore",
@@ -17,9 +17,7 @@ const ignoreFileNames = new Set([
17
17
  * @since 0.6.0
18
18
  */
19
19
  export function walkSourceFiles(rootDirectoryPath, fs) {
20
- const result = [];
21
- visitSourcePaths(result, rootDirectoryPath, fs);
22
- return result;
20
+ return walkFiles(rootDirectoryPath, fs, (filePath) => sourceCommentLanguage(filePath) !== null);
23
21
  }
24
22
  /**
25
23
  * Which comment syntax a path is written in, or `null` when the convention does not cover it.
@@ -37,19 +35,4 @@ export function sourceCommentLanguage(filePath) {
37
35
  return "css";
38
36
  }
39
37
  return ignoreFileNames.has(path.basename(filePath)) ? "ignore" : null;
40
- }
41
- function visitSourcePaths(result, entryPath, fs) {
42
- const entryStats = fs.statSync(entryPath);
43
- if (entryStats.isDirectory()) {
44
- for (const childName of fs.readdirSync(entryPath)) {
45
- if (defaultSkipDirectoryNames.has(childName)) {
46
- continue;
47
- }
48
- visitSourcePaths(result, path.join(entryPath, childName), fs);
49
- }
50
- return;
51
- }
52
- if (sourceCommentLanguage(entryPath) !== null) {
53
- result.push(entryPath);
54
- }
55
38
  }
@@ -0,0 +1,7 @@
1
+ import type { Filesystem } from "#/core/filesystem/filesystem";
2
+ /**
3
+ * Recursively collects the `.ts`/`.tsx` file paths under a root, skipping `.d.ts` and skip-listed directories.
4
+ *
5
+ * @since 0.3.16-canary.0
6
+ */
7
+ export declare function walkTsxFiles(rootDirectoryPath: string, fs: Filesystem): Array<string>;
@@ -1,30 +1,9 @@
1
- import path from "node:path";
2
- import { defaultSkipDirectoryNames } from "#/core/workspace/skip-directories";
1
+ import { walkFiles } from "#/core/workspace/walk-files";
3
2
  /**
4
3
  * Recursively collects the `.ts`/`.tsx` file paths under a root, skipping `.d.ts` and skip-listed directories.
5
4
  *
6
5
  * @since 0.3.16-canary.0
7
6
  */
8
7
  export function walkTsxFiles(rootDirectoryPath, fs) {
9
- const result = [];
10
- visitTsxPaths(result, rootDirectoryPath, fs);
11
- return result;
12
- }
13
- function visitTsxPaths(result, entryPath, fs) {
14
- const entryStats = fs.statSync(entryPath);
15
- if (entryStats.isDirectory()) {
16
- for (const childName of fs.readdirSync(entryPath)) {
17
- if (defaultSkipDirectoryNames.has(childName)) {
18
- continue;
19
- }
20
- visitTsxPaths(result, path.join(entryPath, childName), fs);
21
- }
22
- return;
23
- }
24
- if (entryPath.endsWith(".d.ts")) {
25
- return;
26
- }
27
- if (entryPath.endsWith(".tsx") || entryPath.endsWith(".ts")) {
28
- result.push(entryPath);
29
- }
8
+ return walkFiles(rootDirectoryPath, fs, (filePath) => !filePath.endsWith(".d.ts") && (filePath.endsWith(".ts") || filePath.endsWith(".tsx")));
30
9
  }
@@ -0,0 +1,7 @@
1
+ import type { Filesystem } from "#/core/filesystem/filesystem";
2
+ /**
3
+ * Every file under a root the predicate accepts, skipping build output and vendored trees.
4
+ *
5
+ * @since 0.10.0
6
+ */
7
+ export declare function walkFiles(rootDirectoryPath: string, fs: Filesystem, shouldInclude: (filePath: string) => boolean): Array<string>;
@@ -0,0 +1,27 @@
1
+ import path from "node:path";
2
+ import { defaultSkipDirectoryNames } from "#/core/workspace/skip-directories";
3
+ /**
4
+ * Every file under a root the predicate accepts, skipping build output and vendored trees.
5
+ *
6
+ * @since 0.10.0
7
+ */
8
+ export function walkFiles(rootDirectoryPath, fs, shouldInclude) {
9
+ const result = [];
10
+ visit(result, rootDirectoryPath, fs, shouldInclude);
11
+ return result;
12
+ }
13
+ function visit(result, entryPath, fs, shouldInclude) {
14
+ const entryStats = fs.statSync(entryPath);
15
+ if (entryStats.isDirectory()) {
16
+ for (const childName of fs.readdirSync(entryPath)) {
17
+ if (defaultSkipDirectoryNames.has(childName)) {
18
+ continue;
19
+ }
20
+ visit(result, path.join(entryPath, childName), fs, shouldInclude);
21
+ }
22
+ return;
23
+ }
24
+ if (shouldInclude(entryPath)) {
25
+ result.push(entryPath);
26
+ }
27
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The manifest file name that marks a package directory.
3
+ *
4
+ * @since 0.10.0
5
+ */
6
+ export declare const packageJsonFileName = "package.json";
7
+ /**
8
+ * The pnpm workspace manifest file name that marks a workspace root.
9
+ *
10
+ * @since 0.10.0
11
+ */
12
+ export declare const workspaceYamlFileName = "pnpm-workspace.yaml";
13
+ /**
14
+ * The build output directory name packages emit to.
15
+ *
16
+ * @since 0.10.0
17
+ */
18
+ export declare const distDirName = "dist";
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The manifest file name that marks a package directory.
3
+ *
4
+ * @since 0.10.0
5
+ */
6
+ export const packageJsonFileName = "package.json";
7
+ /**
8
+ * The pnpm workspace manifest file name that marks a workspace root.
9
+ *
10
+ * @since 0.10.0
11
+ */
12
+ export const workspaceYamlFileName = "pnpm-workspace.yaml";
13
+ /**
14
+ * The build output directory name packages emit to.
15
+ *
16
+ * @since 0.10.0
17
+ */
18
+ export const distDirName = "dist";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * The `@codefast/cli` public API: run the CLI in-process, and author a typed `codefast.config.*`.
3
+ */
4
+ export { runCli } from "#/cli";
5
+ export { defineConfig } from "#/core/config/define-config";
6
+ export type { CodefastAfterWriteHook, CodefastArrangeConfig, CodefastConfig, CodefastTagConfig, MirrorConfig, } from "#/core/config/schema";
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * The `@codefast/cli` public API: run the CLI in-process, and author a typed `codefast.config.*`.
3
+ */
4
+ export { runCli } from "#/cli";
5
+ export { defineConfig } from "#/core/config/define-config";
@@ -0,0 +1,13 @@
1
+ import type { GlobalStats } from "#/mirror/domain/types";
2
+ /**
3
+ * Maps a mirror run's stats to the process exit code.
4
+ *
5
+ * @since 0.3.16-canary.0
6
+ */
7
+ export declare function exitCodeForMirrorSyncResult(stats: GlobalStats): number;
8
+ /**
9
+ * Serializes a mirror run's stats as the single-line JSON summary printed under `--json`.
10
+ *
11
+ * @since 0.3.16-canary.0
12
+ */
13
+ export declare function formatMirrorSyncJsonOutput(stats: GlobalStats, elapsedSeconds: number, write?: boolean): string;
@@ -0,0 +1,8 @@
1
+ import * as z from "zod";
2
+ import type { MirrorSyncRunRequest } from "#/mirror/domain/types";
3
+ /**
4
+ * Zod schema validating a `MirrorSyncRunRequest`.
5
+ *
6
+ * @since 0.3.16-canary.0
7
+ */
8
+ export declare const mirrorSyncRunRequestSchema: z.ZodType<MirrorSyncRunRequest>;
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import * as z from "zod";
2
2
  /**
3
3
  * Zod schema validating a `MirrorSyncRunRequest`.
4
4
  *
@@ -0,0 +1,7 @@
1
+ import { Command } from "commander";
2
+ /**
3
+ * Creates the `mirror` subcommand, which writes `package.json#exports` from `dist/`.
4
+ *
5
+ * @since 0.3.16-canary.0
6
+ */
7
+ export declare function createMirrorCommand(): Command;
@@ -1,74 +1,45 @@
1
- import process from "node:process";
2
1
  import { Command } from "commander";
3
- import { globalCliCommanderOptionsSchema } from "#/core/cli/global-options";
4
- import { readOptionalPositionalArg } from "#/core/cli/positional";
5
- import { consumeCliAppError } from "#/core/cli/result-handle";
2
+ import { applyCommandPipeline } from "#/core/cli/command-pipeline";
6
3
  import { nodeFilesystem } from "#/core/filesystem/node";
7
- import { logger } from "#/core/logger";
8
- import { parseWithSchema } from "#/core/schema-parse";
9
4
  import { exitCodeForMirrorSyncResult, formatMirrorSyncJsonOutput } from "#/mirror/cli-result";
10
5
  import { mirrorSyncRunRequestSchema } from "#/mirror/cli-schema";
11
6
  import { MirrorSyncProgressPresenter } from "#/mirror/output";
12
7
  import { prepareMirrorSync } from "#/mirror/prepare";
13
- import { runMirrorSync } from "#/mirror/sync";
8
+ import { runMirrorSync } from "#/mirror/run";
9
+ const mirrorPipeline = {
10
+ positional: { name: "[package]", help: "Optional package path relative to repo root (e.g. packages/ui)" },
11
+ schema: mirrorSyncRunRequestSchema,
12
+ configureArgv: (command) => {
13
+ command.option("--dry-run", "Report what would change without writing package.json", false);
14
+ command.option("-v, --verbose", "Print extra diagnostics", false);
15
+ },
16
+ prepare: (fs, input) => prepareMirrorSync(fs, {
17
+ currentWorkingDirectory: input.currentWorkingDirectory,
18
+ packageArg: input.rawArg,
19
+ globals: input.globals,
20
+ }),
21
+ buildRequest: ({ prelude, opts }) => ({
22
+ rootDir: prelude.rootDir,
23
+ config: prelude.config.mirror ?? {},
24
+ packageFilter: prelude.packageFilter,
25
+ write: !opts.dryRun,
26
+ }),
27
+ createPresenter: ({ opts, globals }) => {
28
+ const presenter = new MirrorSyncProgressPresenter();
29
+ presenter.configure({ noColor: globals.color === false, verbose: !!opts.verbose, dryRun: !!opts.dryRun });
30
+ return presenter;
31
+ },
32
+ run: (fs, request, presenter) => runMirrorSync(fs, { ...request, listener: presenter }),
33
+ formatJson: ({ result, opts, elapsedSeconds }) => formatMirrorSyncJsonOutput(result, elapsedSeconds, !opts.dryRun),
34
+ exitCode: exitCodeForMirrorSyncResult,
35
+ };
14
36
  /**
15
37
  * Creates the `mirror` subcommand, which writes `package.json#exports` from `dist/`.
16
38
  *
17
39
  * @since 0.3.16-canary.0
18
40
  */
19
41
  export function createMirrorCommand() {
20
- const cmd = new Command("mirror")
21
- .description("Write package.json exports from dist/ for workspace packages")
22
- .argument("[package]", "Optional package path relative to repo root (e.g. packages/ui)")
23
- .option("--dry-run", "Report what would change without writing package.json", false)
24
- .option("-v, --verbose", "Print extra diagnostics", false)
25
- .option("--json", "Print one JSON summary on stdout (suppresses human progress)", false)
26
- .action(async (packageArg, opts, command) => {
27
- const globalsOptionCarrier = command.optsWithGlobals?.() ??
28
- command.opts();
29
- const globalOptionsOutcome = parseWithSchema(globalCliCommanderOptionsSchema, globalsOptionCarrier);
30
- if (!consumeCliAppError(globalOptionsOutcome)) {
31
- return;
32
- }
33
- const prelude = await prepareMirrorSync(nodeFilesystem, {
34
- currentWorkingDirectory: process.cwd(),
35
- packageArg: readOptionalPositionalArg(packageArg),
36
- globals: globalOptionsOutcome.value,
37
- });
38
- if (!consumeCliAppError(prelude)) {
39
- return;
40
- }
41
- const write = !opts.dryRun;
42
- const { rootDir, config, packageFilter } = prelude.value;
43
- const parsed = parseWithSchema(mirrorSyncRunRequestSchema, {
44
- rootDir,
45
- config: config.mirror ?? {},
46
- packageFilter,
47
- write,
48
- });
49
- if (!consumeCliAppError(parsed)) {
50
- return;
51
- }
52
- const json = !!opts.json;
53
- const noColor = globalOptionsOutcome.value.color === false;
54
- const verbose = !!opts.verbose;
55
- const progressPresenter = new MirrorSyncProgressPresenter();
56
- const listener = json ? undefined : progressPresenter;
57
- if (!json) {
58
- progressPresenter.configure({ noColor, verbose, dryRun: !write });
59
- }
60
- const startTime = performance.now();
61
- const outcome = await runMirrorSync(nodeFilesystem, {
62
- ...parsed.value,
63
- listener,
64
- });
65
- if (!consumeCliAppError(outcome)) {
66
- return;
67
- }
68
- if (json) {
69
- logger.out(formatMirrorSyncJsonOutput(outcome.value, (performance.now() - startTime) / 1000, write));
70
- }
71
- process.exitCode = exitCodeForMirrorSyncResult(outcome.value);
72
- });
42
+ const cmd = new Command("mirror").description("Write package.json exports from dist/ for workspace packages");
43
+ applyCommandPipeline(cmd, nodeFilesystem, mirrorPipeline);
73
44
  return cmd;
74
45
  }
@@ -0,0 +1,8 @@
1
+ import type { Filesystem } from "#/core/filesystem/filesystem";
2
+ import type { DistFilesystem } from "#/mirror/domain/dist-filesystem";
3
+ /**
4
+ * Creates the `DistFilesystem` the mirror scan uses, backed by a `Filesystem`.
5
+ *
6
+ * @since 0.3.16-canary.0
7
+ */
8
+ export declare function createMirrorDistFilesystem(fs: Filesystem): DistFilesystem;
@@ -2,7 +2,7 @@ import path from "node:path";
2
2
  import { isDirentList } from "#/mirror/domain/dirent-guard";
3
3
  import { normalizePath } from "#/mirror/domain/path-normalizer";
4
4
  /**
5
- * Creates the `DistFilesystem` the mirror scan uses, backed by a `FilesystemPort`.
5
+ * Creates the `DistFilesystem` the mirror scan uses, backed by a `Filesystem`.
6
6
  *
7
7
  * @since 0.3.16-canary.0
8
8
  */
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The JavaScript extensions accepted as runtime entries in `dist/`.
3
+ *
4
+ * @since 0.3.16-canary.0
5
+ */
6
+ export declare const VALID_JS_EXTENSIONS: ReadonlySet<string>;
7
+ /**
8
+ * The declaration-file extensions accepted as type entries in `dist/`.
9
+ *
10
+ * @since 0.3.16-canary.0
11
+ */
12
+ export declare const VALID_DTS_EXTENSIONS: ReadonlySet<string>;
13
+ /**
14
+ * The `package.json` self-export specifier every generated map includes.
15
+ *
16
+ * @since 0.3.16-canary.0
17
+ */
18
+ export declare const PACKAGE_JSON_EXPORT = "./package.json";
@@ -1,15 +1,3 @@
1
- /**
2
- * The build output directory the mirror scan reads.
3
- *
4
- * @since 0.3.16-canary.0
5
- */
6
- export const DIST_DIR = "dist";
7
- /**
8
- * The manifest file name the mirror rewrites.
9
- *
10
- * @since 0.3.16-canary.0
11
- */
12
- export const PACKAGE_JSON = "package.json";
13
1
  /**
14
2
  * The JavaScript extensions accepted as runtime entries in `dist/`.
15
3
  *
@@ -0,0 +1,10 @@
1
+ import type { DirectoryEntry } from "#/core/filesystem/filesystem";
2
+ /**
3
+ * Narrow `fs.promises.readdir` overload result to `Dirent[]` when `withFileTypes: true`.
4
+ * Accepts both historical checks (`isFile` / `isDirectory`) in one guard.
5
+ * Returning `true` for an empty array is intentional: callers treat empty lists
6
+ * the same regardless of the element type, so `[]` is safely accepted as `Dirent[]`.
7
+ *
8
+ * @since 0.3.16-canary.0
9
+ */
10
+ export declare function isDirentList(x: Array<string> | Array<DirectoryEntry>): x is Array<DirectoryEntry>;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Filesystem surface used by mirror export generation (relative paths under `dist/`).
3
+ *
4
+ * @since 0.3.16-canary.0
5
+ */
6
+ export interface DistFilesystem {
7
+ listRelativeFilesRecursively(dirPath: string): Promise<Array<string>>;
8
+ isDirectoryCssOnly(distDir: string, dirPath: string): Promise<boolean>;
9
+ }