@codefast/cli 0.10.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 (220) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +16 -2
  3. package/dist/arrange/command.js +96 -132
  4. package/dist/arrange/domain/ast/simplify-targets.js +18 -23
  5. package/dist/arrange/{typescript-ast-translator.d.ts → domain/ast/translator.d.ts} +5 -4
  6. package/dist/arrange/{typescript-ast-translator.js → domain/ast/translator.js} +19 -22
  7. package/dist/arrange/group/cli-result.d.ts +7 -0
  8. package/dist/arrange/group/cli-result.js +12 -0
  9. package/dist/arrange/group/cli-schema.d.ts +17 -0
  10. package/dist/arrange/group/cli-schema.js +13 -0
  11. package/dist/arrange/group/output.d.ts +7 -0
  12. package/dist/arrange/group/output.js +10 -0
  13. package/dist/arrange/{suggest.d.ts → group/suggest.d.ts} +1 -1
  14. package/dist/arrange/inspect/cli-result.d.ts +7 -0
  15. package/dist/arrange/inspect/cli-result.js +8 -0
  16. package/dist/arrange/inspect/cli-schema.d.ts +15 -0
  17. package/dist/arrange/inspect/cli-schema.js +9 -0
  18. package/dist/arrange/inspect/output.d.ts +7 -0
  19. package/dist/arrange/inspect/output.js +42 -0
  20. package/dist/arrange/{analyze.d.ts → inspect/run.d.ts} +2 -2
  21. package/dist/arrange/{analyze.js → inspect/run.js} +2 -2
  22. package/dist/arrange/{workspace.d.ts → prepare.d.ts} +2 -2
  23. package/dist/arrange/{workspace.js → prepare.js} +5 -8
  24. package/dist/arrange/regroup/cli-result.d.ts +13 -0
  25. package/dist/arrange/regroup/cli-result.js +23 -0
  26. package/dist/arrange/regroup/cli-schema.d.ts +20 -0
  27. package/dist/arrange/regroup/cli-schema.js +14 -0
  28. package/dist/arrange/regroup/output.d.ts +14 -0
  29. package/dist/arrange/regroup/output.js +72 -0
  30. package/dist/arrange/{process-file.d.ts → regroup/process-file.d.ts} +2 -2
  31. package/dist/arrange/{sync.d.ts → regroup/run.d.ts} +3 -3
  32. package/dist/arrange/{sync.js → regroup/run.js} +2 -2
  33. package/dist/arrange/resolve-target.d.ts +2 -2
  34. package/dist/arrange/scan-target.d.ts +2 -2
  35. package/dist/arrange/simplify/cli-result.d.ts +7 -0
  36. package/dist/arrange/simplify/cli-result.js +8 -0
  37. package/dist/arrange/simplify/cli-schema.d.ts +17 -0
  38. package/dist/arrange/simplify/cli-schema.js +11 -0
  39. package/dist/arrange/simplify/fold-targets.d.ts +13 -0
  40. package/dist/arrange/simplify/fold-targets.js +139 -0
  41. package/dist/arrange/simplify/output.d.ts +7 -0
  42. package/dist/arrange/simplify/output.js +15 -0
  43. package/dist/arrange/{simplify-process-file.d.ts → simplify/process-file.d.ts} +4 -2
  44. package/dist/arrange/simplify/process-file.js +49 -0
  45. package/dist/arrange/{simplify-sync.d.ts → simplify/run.d.ts} +3 -2
  46. package/dist/arrange/simplify/run.js +38 -0
  47. package/dist/arrange/simplify/variant-classname-probe.d.ts +35 -0
  48. package/dist/arrange/simplify/variant-classname-probe.js +95 -0
  49. package/dist/arrange/source-parse.js +1 -1
  50. package/dist/audit/command.js +134 -211
  51. package/dist/audit/comments/cli-result.d.ts +13 -0
  52. package/dist/audit/comments/cli-result.js +22 -0
  53. package/dist/audit/comments/cli-schema.d.ts +19 -0
  54. package/dist/audit/comments/cli-schema.js +13 -0
  55. package/dist/audit/comments/output.d.ts +7 -0
  56. package/dist/audit/comments/output.js +27 -0
  57. package/dist/audit/comments/prepare.d.ts +16 -0
  58. package/dist/audit/comments/prepare.js +12 -0
  59. package/dist/audit/{run-comments.d.ts → comments/run.d.ts} +2 -2
  60. package/dist/audit/{run-comments.js → comments/run.js} +5 -5
  61. package/dist/audit/display-names/cli-result.d.ts +13 -0
  62. package/dist/audit/display-names/cli-result.js +22 -0
  63. package/dist/audit/display-names/cli-schema.d.ts +18 -0
  64. package/dist/audit/display-names/cli-schema.js +12 -0
  65. package/dist/audit/display-names/output.d.ts +7 -0
  66. package/dist/audit/display-names/output.js +21 -0
  67. package/dist/audit/display-names/prepare.d.ts +16 -0
  68. package/dist/audit/display-names/prepare.js +12 -0
  69. package/dist/audit/{run-display-names.d.ts → display-names/run.d.ts} +2 -2
  70. package/dist/audit/{run-display-names.js → display-names/run.js} +1 -1
  71. package/dist/audit/imports/cli-result.d.ts +13 -0
  72. package/dist/audit/imports/cli-result.js +22 -0
  73. package/dist/audit/imports/cli-schema.d.ts +18 -0
  74. package/dist/audit/imports/cli-schema.js +12 -0
  75. package/dist/audit/{domain → imports/domain}/import-policy.d.ts +2 -2
  76. package/dist/audit/{domain → imports/domain}/import-policy.js +4 -11
  77. package/dist/audit/imports/output.d.ts +7 -0
  78. package/dist/audit/imports/output.js +21 -0
  79. package/dist/audit/imports/prepare.d.ts +16 -0
  80. package/dist/audit/imports/prepare.js +12 -0
  81. package/dist/audit/{run-imports.d.ts → imports/run.d.ts} +2 -2
  82. package/dist/audit/{run-imports.js → imports/run.js} +2 -2
  83. package/dist/audit/links/cli-result.d.ts +13 -0
  84. package/dist/audit/links/cli-result.js +22 -0
  85. package/dist/audit/links/cli-schema.d.ts +18 -0
  86. package/dist/audit/links/cli-schema.js +12 -0
  87. package/dist/audit/links/output.d.ts +7 -0
  88. package/dist/audit/links/output.js +21 -0
  89. package/dist/audit/links/prepare.d.ts +16 -0
  90. package/dist/audit/links/prepare.js +12 -0
  91. package/dist/audit/{run-links.d.ts → links/run.d.ts} +2 -2
  92. package/dist/audit/{run-links.js → links/run.js} +1 -1
  93. package/dist/audit/prepare.d.ts +11 -50
  94. package/dist/audit/prepare.js +9 -132
  95. package/dist/audit/rtl/cli-result.d.ts +13 -0
  96. package/dist/audit/rtl/cli-result.js +22 -0
  97. package/dist/audit/rtl/cli-schema.d.ts +18 -0
  98. package/dist/audit/rtl/cli-schema.js +12 -0
  99. package/dist/audit/{domain → rtl/domain}/audit-file.js +2 -2
  100. package/dist/audit/rtl/output.d.ts +7 -0
  101. package/dist/audit/rtl/output.js +21 -0
  102. package/dist/audit/rtl/prepare.d.ts +13 -0
  103. package/dist/audit/rtl/prepare.js +41 -0
  104. package/dist/audit/{run.d.ts → rtl/run.d.ts} +2 -2
  105. package/dist/audit/{run.js → rtl/run.js} +1 -1
  106. package/dist/core/cli/command-pipeline.d.ts +91 -0
  107. package/dist/core/cli/command-pipeline.js +83 -0
  108. package/dist/core/cli/global-options.d.ts +1 -1
  109. package/dist/core/cli/global-options.js +1 -1
  110. package/dist/core/cli/resolve-root.d.ts +9 -0
  111. package/dist/core/cli/resolve-root.js +16 -0
  112. package/dist/core/cli/result-handle.d.ts +1 -7
  113. package/dist/core/cli/result-handle.js +0 -13
  114. package/dist/core/config/loader.d.ts +2 -2
  115. package/dist/core/config/schema.d.ts +1 -1
  116. package/dist/core/config/schema.js +1 -1
  117. package/dist/core/config.d.ts +2 -2
  118. package/dist/core/filesystem/{port.d.ts → filesystem.d.ts} +1 -1
  119. package/dist/core/filesystem/node.d.ts +3 -3
  120. package/dist/core/filesystem/node.js +1 -1
  121. package/dist/core/source-text-edit.d.ts +14 -0
  122. package/dist/core/source-text-edit.js +22 -0
  123. package/dist/core/workspace/markdown-walk.d.ts +2 -2
  124. package/dist/core/workspace/package-version.d.ts +2 -2
  125. package/dist/core/workspace/resolver.d.ts +3 -3
  126. package/dist/core/workspace/source-walk.d.ts +2 -2
  127. package/dist/core/workspace/typescript-walk.d.ts +2 -2
  128. package/dist/core/workspace/walk-files.d.ts +2 -2
  129. package/dist/mirror/cli-schema.d.ts +2 -2
  130. package/dist/mirror/cli-schema.js +1 -1
  131. package/dist/mirror/command.js +31 -60
  132. package/dist/mirror/{dist-filesystem-impl.d.ts → dist-filesystem-node.d.ts} +3 -3
  133. package/dist/mirror/{dist-filesystem-impl.js → dist-filesystem-node.js} +1 -1
  134. package/dist/mirror/domain/dirent-guard.d.ts +1 -1
  135. package/dist/mirror/domain/types.d.ts +57 -0
  136. package/dist/mirror/output.d.ts +1 -3
  137. package/dist/mirror/output.js +126 -1
  138. package/dist/mirror/package-path.d.ts +3 -3
  139. package/dist/mirror/prepare.d.ts +3 -3
  140. package/dist/mirror/prepare.js +6 -10
  141. package/dist/mirror/run.d.ts +12 -0
  142. package/dist/mirror/supplement-exports.d.ts +3 -3
  143. package/dist/mirror/sync-workspace-package.d.ts +2 -2
  144. package/dist/mirror/sync-workspace-package.js +1 -1
  145. package/dist/mirror/write-exports.d.ts +2 -2
  146. package/dist/pack-slim/cli-schema.d.ts +1 -1
  147. package/dist/pack-slim/cli-schema.js +1 -1
  148. package/dist/pack-slim/command.js +31 -66
  149. package/dist/pack-slim/prepare.d.ts +21 -0
  150. package/dist/pack-slim/prepare.js +14 -0
  151. package/dist/pack-slim/{sync.d.ts → run.d.ts} +2 -2
  152. package/dist/tag/cli-result.d.ts +8 -2
  153. package/dist/tag/cli-result.js +14 -1
  154. package/dist/tag/cli-schema.d.ts +4 -4
  155. package/dist/tag/cli-schema.js +3 -4
  156. package/dist/tag/command.js +33 -60
  157. package/dist/tag/domain/skip-filter.d.ts +13 -0
  158. package/dist/tag/domain/skip-filter.js +29 -0
  159. package/dist/tag/domain/types.d.ts +21 -1
  160. package/dist/tag/domain/version-summary.d.ts +13 -0
  161. package/dist/tag/domain/version-summary.js +24 -0
  162. package/dist/tag/output.d.ts +4 -4
  163. package/dist/tag/output.js +6 -9
  164. package/dist/tag/prepare.d.ts +2 -2
  165. package/dist/tag/prepare.js +2 -2
  166. package/dist/tag/run.d.ts +10 -0
  167. package/dist/tag/{sync.js → run.js} +20 -50
  168. package/dist/tag/{target-candidates.d.ts → target/candidates.d.ts} +2 -2
  169. package/dist/tag/{target-candidates.js → target/candidates.js} +1 -1
  170. package/dist/tag/{resolve-target-path.d.ts → target/resolve-path.d.ts} +2 -2
  171. package/dist/tag/{target-runner.d.ts → target/runner.d.ts} +2 -2
  172. package/dist/tag/{target-runner.js → target/runner.js} +2 -2
  173. package/dist/tag/{since-writer.d.ts → writer/since-writer.d.ts} +2 -2
  174. package/dist/tag/{version-resolver.d.ts → writer/version-resolver.d.ts} +2 -2
  175. package/package.json +10 -2
  176. package/dist/arrange/cli-schema.d.ts +0 -50
  177. package/dist/arrange/cli-schema.js +0 -34
  178. package/dist/arrange/output.d.ts +0 -26
  179. package/dist/arrange/output.js +0 -127
  180. package/dist/arrange/simplify-process-file.js +0 -30
  181. package/dist/arrange/simplify-sync.js +0 -30
  182. package/dist/audit/cli-schema.d.ts +0 -93
  183. package/dist/audit/cli-schema.js +0 -66
  184. package/dist/audit/output.d.ts +0 -91
  185. package/dist/audit/output.js +0 -213
  186. package/dist/mirror/sync-reporter.d.ts +0 -60
  187. package/dist/mirror/sync-reporter.js +0 -128
  188. package/dist/mirror/sync-types.d.ts +0 -43
  189. package/dist/mirror/sync-types.js +0 -1
  190. package/dist/mirror/sync.d.ts +0 -12
  191. package/dist/tag/sync.d.ts +0 -42
  192. /package/dist/arrange/{suggest.js → group/suggest.js} +0 -0
  193. /package/dist/arrange/{domain → inspect/domain}/analyze-service.d.ts +0 -0
  194. /package/dist/arrange/{domain → inspect/domain}/analyze-service.js +0 -0
  195. /package/dist/arrange/{process-file.js → regroup/process-file.js} +0 -0
  196. /package/dist/audit/{domain → comments/domain}/comment-content.d.ts +0 -0
  197. /package/dist/audit/{domain → comments/domain}/comment-content.js +0 -0
  198. /package/dist/audit/{domain → comments/domain}/comment-dividers.d.ts +0 -0
  199. /package/dist/audit/{domain → comments/domain}/comment-dividers.js +0 -0
  200. /package/dist/audit/{domain → comments/domain}/link-references.d.ts +0 -0
  201. /package/dist/audit/{domain → comments/domain}/link-references.js +0 -0
  202. /package/dist/audit/{domain → comments/domain}/since-versions.d.ts +0 -0
  203. /package/dist/audit/{domain → comments/domain}/since-versions.js +0 -0
  204. /package/dist/audit/{domain → comments/domain}/tsdoc-syntax.d.ts +0 -0
  205. /package/dist/audit/{domain → comments/domain}/tsdoc-syntax.js +0 -0
  206. /package/dist/audit/{domain → display-names/domain}/display-names.d.ts +0 -0
  207. /package/dist/audit/{domain → display-names/domain}/display-names.js +0 -0
  208. /package/dist/audit/{domain → links/domain}/markdown-links.d.ts +0 -0
  209. /package/dist/audit/{domain → links/domain}/markdown-links.js +0 -0
  210. /package/dist/audit/{domain → rtl/domain}/audit-file.d.ts +0 -0
  211. /package/dist/audit/{domain → rtl/domain}/mappings.d.ts +0 -0
  212. /package/dist/audit/{domain → rtl/domain}/mappings.js +0 -0
  213. /package/dist/audit/{domain → rtl/domain}/tokenize.d.ts +0 -0
  214. /package/dist/audit/{domain → rtl/domain}/tokenize.js +0 -0
  215. /package/dist/core/filesystem/{port.js → filesystem.js} +0 -0
  216. /package/dist/mirror/{sync.js → run.js} +0 -0
  217. /package/dist/pack-slim/{sync.js → run.js} +0 -0
  218. /package/dist/tag/{resolve-target-path.js → target/resolve-path.js} +0 -0
  219. /package/dist/tag/{since-writer.js → writer/since-writer.js} +0 -0
  220. /package/dist/tag/{version-resolver.js → writer/version-resolver.js} +0 -0
@@ -1,13 +1,14 @@
1
1
  import type { ArrangeRunResult } from "#/arrange/domain/types";
2
2
  import type { AppError } from "#/core/errors";
3
- import type { FilesystemPort } from "#/core/filesystem/port";
3
+ import type { Filesystem } from "#/core/filesystem/filesystem";
4
4
  import type { Result } from "#/core/result";
5
5
  /**
6
6
  * Runs the simplify pass over every target file and returns the aggregated result.
7
7
  *
8
8
  * @since 0.3.16-canary.0
9
9
  */
10
- export declare function runArrangeSimplify(fs: FilesystemPort, args: {
10
+ export declare function runArrangeSimplify(fs: Filesystem, args: {
11
11
  targetPath: string;
12
12
  write: boolean;
13
+ foldVariantClassName?: boolean | undefined;
13
14
  }): Promise<Result<ArrangeRunResult, AppError>>;
@@ -0,0 +1,38 @@
1
+ import { scanArrangeTargets } from "#/arrange/scan-target";
2
+ import { processArrangeSimplifyFile } from "#/arrange/simplify/process-file";
3
+ import { createVariantClassNameProbe } from "#/arrange/simplify/variant-classname-probe";
4
+ import { ok } from "#/core/result";
5
+ /**
6
+ * Runs the simplify pass over every target file and returns the aggregated result.
7
+ *
8
+ * @since 0.3.16-canary.0
9
+ */
10
+ export async function runArrangeSimplify(fs, args) {
11
+ const filePaths = scanArrangeTargets(fs, args.targetPath);
12
+ const modifiedFiles = [];
13
+ let totalFound = 0;
14
+ let totalChanged = 0;
15
+ // The type probe spawns a TypeScript server, so it is created once and only when the fold is requested.
16
+ const probe = args.foldVariantClassName ? await createVariantClassNameProbe() : null;
17
+ try {
18
+ for (const filePath of filePaths) {
19
+ const result = processArrangeSimplifyFile(fs, { filePath, write: args.write, probe });
20
+ totalFound += result.totalFound;
21
+ totalChanged += result.changed;
22
+ if (result.changed > 0) {
23
+ modifiedFiles.push(result.filePath);
24
+ }
25
+ }
26
+ }
27
+ finally {
28
+ probe?.dispose();
29
+ }
30
+ return ok({
31
+ filePaths,
32
+ modifiedFiles,
33
+ totalFound,
34
+ totalChanged,
35
+ hookError: null,
36
+ previewPlans: [],
37
+ });
38
+ }
@@ -0,0 +1,35 @@
1
+ /** Type probe that asks whether a variant function's options accept a folded `className`. */
2
+ /**
3
+ * What a variant function's `className`/`class` option accepts, resolved from its type.
4
+ *
5
+ * @since 0.11.0
6
+ */
7
+ export interface VariantClassNameAcceptance {
8
+ readonly acceptsString: boolean;
9
+ readonly acceptsArray: boolean;
10
+ }
11
+ /**
12
+ * A per-file view that resolves a callee's `className` acceptance from a source offset.
13
+ *
14
+ * @since 0.11.0
15
+ */
16
+ export interface FileClassNameProbe {
17
+ classNameAcceptance(calleeOffset: number): VariantClassNameAcceptance | null;
18
+ }
19
+ /**
20
+ * A whole-run type probe backing the opt-in `className` fold, bound to one type server.
21
+ *
22
+ * @since 0.11.0
23
+ */
24
+ export interface VariantClassNameProbe {
25
+ forFile(filePath: string): FileClassNameProbe | null;
26
+ dispose(): void;
27
+ }
28
+ /**
29
+ * Creates a type probe over the native TypeScript type server for the opt-in `className` fold.
30
+ *
31
+ * @throws AppError when the `typescript` package providing the type server cannot be loaded.
32
+ *
33
+ * @since 0.11.0
34
+ */
35
+ export declare function createVariantClassNameProbe(): Promise<VariantClassNameProbe>;
@@ -0,0 +1,95 @@
1
+ /** Type probe that asks whether a variant function's options accept a folded `className`. */
2
+ import { AppError, messageFrom } from "#/core/errors";
3
+ // ── Type inspection ──────────────────────────────────────────────────────────────────────────────────────────────────
4
+ /** The type of the callee's first-parameter `className` (preferred) or `class` option, if any. */
5
+ function classNameOptionType(checker, optionsType) {
6
+ for (const name of ["className", "class"]) {
7
+ const symbol = checker.getPropertyOfType(optionsType, name);
8
+ if (symbol) {
9
+ const propertyType = checker.getTypeOfSymbol(symbol);
10
+ if (propertyType) {
11
+ return propertyType;
12
+ }
13
+ }
14
+ }
15
+ return undefined;
16
+ }
17
+ /** True when any constituent of the option type is array-like — the option accepts a class array. */
18
+ function optionAcceptsArray(checker, optionType) {
19
+ const constituents = optionType.isUnionType() ? optionType.getTypes() : [optionType];
20
+ return constituents.some((constituent) => checker.isArrayType(constituent) || checker.isTupleType(constituent) || checker.isArrayLikeType(constituent));
21
+ }
22
+ /** Resolves how the function called at `offset` accepts a `className`, or `null` when it is not a variant function. */
23
+ function classNameAcceptanceAt(checker, callKind, filePath, offset) {
24
+ const calleeType = checker.getTypeAtPosition(filePath, offset);
25
+ if (!calleeType) {
26
+ return null;
27
+ }
28
+ const signature = checker.getSignaturesOfType(calleeType, callKind)[0];
29
+ if (!signature) {
30
+ return null;
31
+ }
32
+ const parameterType = checker.getParameterType(signature, 0);
33
+ if (!parameterType) {
34
+ return null;
35
+ }
36
+ const nonNullableType = checker.getNonNullableType(parameterType);
37
+ if (!nonNullableType) {
38
+ return null;
39
+ }
40
+ const optionsType = checker.getApparentType(nonNullableType);
41
+ if (!optionsType) {
42
+ return null;
43
+ }
44
+ const optionType = classNameOptionType(checker, optionsType);
45
+ if (!optionType) {
46
+ return null;
47
+ }
48
+ return {
49
+ acceptsString: checker.isTypeAssignableTo(checker.getStringType(), optionType),
50
+ acceptsArray: optionAcceptsArray(checker, optionType),
51
+ };
52
+ }
53
+ // ── Probe lifecycle ──────────────────────────────────────────────────────────────────────────────────────────────────
54
+ /** Loads the native TypeScript type-server API, or fails with a coded error naming the missing package. */
55
+ async function loadTypeServer() {
56
+ try {
57
+ return await import("typescript/unstable/sync");
58
+ }
59
+ catch (cause) {
60
+ throw new AppError("INFRA_FAILURE", `--fold-variant-classname needs the "typescript" package (v7) resolvable from the target: ${messageFrom(cause)}`, cause);
61
+ }
62
+ }
63
+ /**
64
+ * Creates a type probe over the native TypeScript type server for the opt-in `className` fold.
65
+ *
66
+ * @throws AppError when the `typescript` package providing the type server cannot be loaded.
67
+ *
68
+ * @since 0.11.0
69
+ */
70
+ export async function createVariantClassNameProbe() {
71
+ const sync = await loadTypeServer();
72
+ const api = new sync.API();
73
+ const callKind = sync.SignatureKind.Call;
74
+ return {
75
+ forFile(filePath) {
76
+ try {
77
+ const project = api.updateSnapshot({ openFiles: [filePath] }).getDefaultProjectForFile(filePath);
78
+ if (!project) {
79
+ return null;
80
+ }
81
+ const { checker } = project;
82
+ return {
83
+ classNameAcceptance: (offset) => classNameAcceptanceAt(checker, callKind, filePath, offset),
84
+ };
85
+ }
86
+ catch {
87
+ // A file outside any tsconfig has no project to type against — skip its fold, keep the base pass.
88
+ return null;
89
+ }
90
+ },
91
+ dispose() {
92
+ api.close();
93
+ },
94
+ };
95
+ }
@@ -1,4 +1,4 @@
1
- import { TypeScriptAstTranslator } from "#/arrange/typescript-ast-translator";
1
+ import { TypeScriptAstTranslator } from "#/arrange/domain/ast/translator";
2
2
  import { messageFrom } from "#/core/errors";
3
3
  import { logger } from "#/core/logger";
4
4
  const translator = new TypeScriptAstTranslator();
@@ -1,18 +1,134 @@
1
- import process from "node:process";
2
1
  import { Command } from "commander";
3
- import { commentAuditRunRequestSchema, linkAuditRunRequestSchema, importsAuditRunRequestSchema, rtlAuditRunRequestSchema, displayNameAuditRunRequestSchema, } from "#/audit/cli-schema";
4
- import { exitCodeForCommentAuditResult, exitCodeForLinkAuditResult, exitCodeForImportsAuditResult, exitCodeForRtlAuditResult, exitCodeForDisplayNameAuditResult, formatCommentAuditJsonOutput, formatLinkAuditJsonOutput, formatImportsAuditJsonOutput, formatRtlAuditJsonOutput, formatDisplayNameAuditJsonOutput, presentCommentAuditResult, presentLinkAuditResult, presentImportsAuditResult, presentRtlAuditResult, presentDisplayNameAuditResult, } from "#/audit/output";
5
- import { prepareCommentAudit, prepareLinkAudit, prepareImportsAudit, prepareRtlAudit, prepareDisplayNameAudit, } from "#/audit/prepare";
6
- import { runRtlAudit } from "#/audit/run";
7
- import { runCommentAudit } from "#/audit/run-comments";
8
- import { runDisplayNameAudit } from "#/audit/run-display-names";
9
- import { runImportsAudit } from "#/audit/run-imports";
10
- import { runLinkAudit } from "#/audit/run-links";
11
- import { readOptionalPositionalArg } from "#/core/cli/positional";
12
- import { consumeCliAppError } from "#/core/cli/result-handle";
2
+ import { exitCodeForCommentAuditResult, formatCommentAuditJsonOutput } from "#/audit/comments/cli-result";
3
+ import { commentAuditRunRequestSchema } from "#/audit/comments/cli-schema";
4
+ import { presentCommentAuditResult } from "#/audit/comments/output";
5
+ import { prepareCommentAudit } from "#/audit/comments/prepare";
6
+ import { runCommentAudit } from "#/audit/comments/run";
7
+ import { exitCodeForDisplayNameAuditResult, formatDisplayNameAuditJsonOutput } from "#/audit/display-names/cli-result";
8
+ import { displayNameAuditRunRequestSchema } from "#/audit/display-names/cli-schema";
9
+ import { presentDisplayNameAuditResult } from "#/audit/display-names/output";
10
+ import { prepareDisplayNameAudit } from "#/audit/display-names/prepare";
11
+ import { runDisplayNameAudit } from "#/audit/display-names/run";
12
+ import { exitCodeForImportsAuditResult, formatImportsAuditJsonOutput } from "#/audit/imports/cli-result";
13
+ import { importsAuditRunRequestSchema } from "#/audit/imports/cli-schema";
14
+ import { presentImportsAuditResult } from "#/audit/imports/output";
15
+ import { prepareImportsAudit } from "#/audit/imports/prepare";
16
+ import { runImportsAudit } from "#/audit/imports/run";
17
+ import { exitCodeForLinkAuditResult, formatLinkAuditJsonOutput } from "#/audit/links/cli-result";
18
+ import { linkAuditRunRequestSchema } from "#/audit/links/cli-schema";
19
+ import { presentLinkAuditResult } from "#/audit/links/output";
20
+ import { prepareLinkAudit } from "#/audit/links/prepare";
21
+ import { runLinkAudit } from "#/audit/links/run";
22
+ import { exitCodeForRtlAuditResult, formatRtlAuditJsonOutput } from "#/audit/rtl/cli-result";
23
+ import { rtlAuditRunRequestSchema } from "#/audit/rtl/cli-schema";
24
+ import { presentRtlAuditResult } from "#/audit/rtl/output";
25
+ import { prepareRtlAudit } from "#/audit/rtl/prepare";
26
+ import { runRtlAudit } from "#/audit/rtl/run";
27
+ import { registerPipelineSubcommand } from "#/core/cli/command-pipeline";
13
28
  import { nodeFilesystem } from "#/core/filesystem/node";
14
- import { logger } from "#/core/logger";
15
- import { parseWithSchema } from "#/core/schema-parse";
29
+ function baseAuditRequest(prelude, opts) {
30
+ return {
31
+ rootDir: prelude.rootDir,
32
+ targetPath: prelude.targetPath,
33
+ allowlist: prelude.allowlist,
34
+ json: !!opts.json,
35
+ };
36
+ }
37
+ const rtlCheck = {
38
+ name: "rtl",
39
+ description: "Report physical-direction Tailwind classes that should be logical or rtl:-paired",
40
+ targetHelp: "Directory or file to scan (default: audit.rtl.target from config)",
41
+ schema: rtlAuditRunRequestSchema,
42
+ prepare: prepareRtlAudit,
43
+ buildRequest: baseAuditRequest,
44
+ run: (fs, request) => runRtlAudit(fs, { rootDir: request.rootDir, targetPath: request.targetPath, allowlist: request.allowlist ?? [] }),
45
+ present: presentRtlAuditResult,
46
+ formatJson: formatRtlAuditJsonOutput,
47
+ exitCode: exitCodeForRtlAuditResult,
48
+ };
49
+ const linksCheck = {
50
+ name: "links",
51
+ description: "Report markdown links pointing at a missing path or an anchor the target does not offer",
52
+ targetHelp: "Directory or file to scan (default: the repo root)",
53
+ schema: linkAuditRunRequestSchema,
54
+ prepare: prepareLinkAudit,
55
+ buildRequest: baseAuditRequest,
56
+ run: (fs, request) => runLinkAudit(fs, { rootDir: request.rootDir, targetPath: request.targetPath, allowlist: request.allowlist ?? [] }),
57
+ present: presentLinkAuditResult,
58
+ formatJson: formatLinkAuditJsonOutput,
59
+ exitCode: exitCodeForLinkAuditResult,
60
+ };
61
+ const importsCheck = {
62
+ name: "imports",
63
+ description: "Report banned import forms (React by-name, Zod namespace in front-end packages, …)",
64
+ targetHelp: "Directory or file to scan (default: the repo root)",
65
+ schema: importsAuditRunRequestSchema,
66
+ prepare: prepareImportsAudit,
67
+ buildRequest: baseAuditRequest,
68
+ run: (fs, request) => runImportsAudit(fs, {
69
+ rootDir: request.rootDir,
70
+ targetPath: request.targetPath,
71
+ allowlist: request.allowlist ?? [],
72
+ }),
73
+ present: presentImportsAuditResult,
74
+ formatJson: formatImportsAuditJsonOutput,
75
+ exitCode: exitCodeForImportsAuditResult,
76
+ };
77
+ const displayNamesCheck = {
78
+ name: "display-names",
79
+ description: "Report token(), tag() and module display names that break the <namespace>:<Name> convention",
80
+ targetHelp: "Directory or file to scan (default: the repo root)",
81
+ schema: displayNameAuditRunRequestSchema,
82
+ prepare: prepareDisplayNameAudit,
83
+ buildRequest: baseAuditRequest,
84
+ run: (fs, request) => runDisplayNameAudit(fs, {
85
+ rootDir: request.rootDir,
86
+ targetPath: request.targetPath,
87
+ allowlist: request.allowlist ?? [],
88
+ }),
89
+ present: presentDisplayNameAuditResult,
90
+ formatJson: formatDisplayNameAuditJsonOutput,
91
+ exitCode: exitCodeForDisplayNameAuditResult,
92
+ };
93
+ const commentsCheck = {
94
+ name: "comments",
95
+ description: "Report section dividers that are not in the repo's one allowed form",
96
+ targetHelp: "Directory or file to scan (default: the repo root)",
97
+ schema: commentAuditRunRequestSchema,
98
+ prepare: prepareCommentAudit,
99
+ buildRequest: (prelude, opts) => ({ ...baseAuditRequest(prelude, opts), fix: !!opts.fix }),
100
+ run: (fs, request) => runCommentAudit(fs, {
101
+ rootDir: request.rootDir,
102
+ targetPath: request.targetPath,
103
+ allowlist: request.allowlist ?? [],
104
+ fix: request.fix,
105
+ }),
106
+ present: presentCommentAuditResult,
107
+ formatJson: formatCommentAuditJsonOutput,
108
+ exitCode: exitCodeForCommentAuditResult,
109
+ extraOptions: (command) => {
110
+ command.option("--fix", "Rewrite every mechanically fixable divider in place", false);
111
+ },
112
+ };
113
+ /**
114
+ * Adapts an `AuditCheck` descriptor onto the shared command pipeline.
115
+ */
116
+ function auditCheckToPipeline(check) {
117
+ return {
118
+ name: check.name,
119
+ description: check.description,
120
+ positional: { name: "[target]", help: check.targetHelp },
121
+ jsonHelp: "Print one JSON summary on stdout",
122
+ schema: check.schema,
123
+ configureArgv: check.extraOptions,
124
+ prepare: (fs, input) => check.prepare(fs, { currentWorkingDirectory: input.currentWorkingDirectory, rawTarget: input.rawArg }),
125
+ buildRequest: ({ prelude, opts }) => check.buildRequest(prelude, opts),
126
+ run: async (fs, request) => check.run(fs, request),
127
+ presentHuman: ({ result }) => check.present(result),
128
+ formatJson: ({ result, prelude }) => check.formatJson(result, prelude.rootDir),
129
+ exitCode: check.exitCode,
130
+ };
131
+ }
16
132
  /**
17
133
  * Top-level `audit` command — the source scans. Every one of them reports by default; only
18
134
  * `comments --fix` writes, and only where the rewrite discards nothing a person wrote.
@@ -21,203 +137,10 @@ import { parseWithSchema } from "#/core/schema-parse";
21
137
  */
22
138
  export function createAuditCommand() {
23
139
  const cmd = new Command("audit").description("Source audits").enablePositionalOptions();
24
- cmd
25
- .command("rtl")
26
- .description("Report physical-direction Tailwind classes that should be logical or rtl:-paired")
27
- .argument("[target]", "Directory or file to scan (default: audit.rtl.target from config)")
28
- .option("--json", "Print one JSON summary on stdout", false)
29
- .action(async (target, opts) => {
30
- const prelude = await prepareRtlAudit(nodeFilesystem, {
31
- currentWorkingDirectory: process.cwd(),
32
- rawTarget: readOptionalPositionalArg(target),
33
- });
34
- if (!consumeCliAppError(prelude)) {
35
- return;
36
- }
37
- const { rootDir, targetPath, allowlist } = prelude.value;
38
- const parsed = parseWithSchema(rtlAuditRunRequestSchema, {
39
- rootDir,
40
- targetPath,
41
- allowlist,
42
- json: !!opts.json,
43
- });
44
- if (!consumeCliAppError(parsed)) {
45
- return;
46
- }
47
- const outcome = runRtlAudit(nodeFilesystem, {
48
- rootDir: parsed.value.rootDir,
49
- targetPath: parsed.value.targetPath,
50
- allowlist: parsed.value.allowlist ?? [],
51
- });
52
- if (!consumeCliAppError(outcome)) {
53
- return;
54
- }
55
- if (parsed.value.json) {
56
- logger.out(formatRtlAuditJsonOutput(outcome.value, rootDir));
57
- }
58
- else {
59
- presentRtlAuditResult(outcome.value);
60
- }
61
- process.exitCode = exitCodeForRtlAuditResult(outcome.value);
62
- });
63
- cmd
64
- .command("links")
65
- .description("Report markdown links pointing at a missing path or an anchor the target does not offer")
66
- .argument("[target]", "Directory or file to scan (default: the repo root)")
67
- .option("--json", "Print one JSON summary on stdout", false)
68
- .action(async (target, opts) => {
69
- const prelude = await prepareLinkAudit(nodeFilesystem, {
70
- currentWorkingDirectory: process.cwd(),
71
- rawTarget: readOptionalPositionalArg(target),
72
- });
73
- if (!consumeCliAppError(prelude)) {
74
- return;
75
- }
76
- const { rootDir, targetPath, allowlist } = prelude.value;
77
- const parsed = parseWithSchema(linkAuditRunRequestSchema, {
78
- rootDir,
79
- targetPath,
80
- allowlist,
81
- json: !!opts.json,
82
- });
83
- if (!consumeCliAppError(parsed)) {
84
- return;
85
- }
86
- const outcome = runLinkAudit(nodeFilesystem, {
87
- rootDir: parsed.value.rootDir,
88
- targetPath: parsed.value.targetPath,
89
- allowlist: parsed.value.allowlist ?? [],
90
- });
91
- if (!consumeCliAppError(outcome)) {
92
- return;
93
- }
94
- if (parsed.value.json) {
95
- logger.out(formatLinkAuditJsonOutput(outcome.value, rootDir));
96
- }
97
- else {
98
- presentLinkAuditResult(outcome.value);
99
- }
100
- process.exitCode = exitCodeForLinkAuditResult(outcome.value);
101
- });
102
- cmd
103
- .command("imports")
104
- .description("Report banned import forms (React by-name, Zod namespace in front-end packages, …)")
105
- .argument("[target]", "Directory or file to scan (default: the repo root)")
106
- .option("--json", "Print one JSON summary on stdout", false)
107
- .action(async (target, opts) => {
108
- const prelude = await prepareImportsAudit(nodeFilesystem, {
109
- currentWorkingDirectory: process.cwd(),
110
- rawTarget: readOptionalPositionalArg(target),
111
- });
112
- if (!consumeCliAppError(prelude)) {
113
- return;
114
- }
115
- const { rootDir, targetPath, allowlist } = prelude.value;
116
- const parsed = parseWithSchema(importsAuditRunRequestSchema, {
117
- rootDir,
118
- targetPath,
119
- allowlist,
120
- json: !!opts.json,
121
- });
122
- if (!consumeCliAppError(parsed)) {
123
- return;
124
- }
125
- const outcome = runImportsAudit(nodeFilesystem, {
126
- rootDir: parsed.value.rootDir,
127
- targetPath: parsed.value.targetPath,
128
- allowlist: parsed.value.allowlist ?? [],
129
- });
130
- if (!consumeCliAppError(outcome)) {
131
- return;
132
- }
133
- if (parsed.value.json) {
134
- logger.out(formatImportsAuditJsonOutput(outcome.value, rootDir));
135
- }
136
- else {
137
- presentImportsAuditResult(outcome.value);
138
- }
139
- process.exitCode = exitCodeForImportsAuditResult(outcome.value);
140
- });
141
- cmd
142
- .command("display-names")
143
- .description("Report token(), tag() and module display names that break the <namespace>:<Name> convention")
144
- .argument("[target]", "Directory or file to scan (default: the repo root)")
145
- .option("--json", "Print one JSON summary on stdout", false)
146
- .action(async (target, opts) => {
147
- const prelude = await prepareDisplayNameAudit(nodeFilesystem, {
148
- currentWorkingDirectory: process.cwd(),
149
- rawTarget: readOptionalPositionalArg(target),
150
- });
151
- if (!consumeCliAppError(prelude)) {
152
- return;
153
- }
154
- const { rootDir, targetPath, allowlist } = prelude.value;
155
- const parsed = parseWithSchema(displayNameAuditRunRequestSchema, {
156
- rootDir,
157
- targetPath,
158
- allowlist,
159
- json: !!opts.json,
160
- });
161
- if (!consumeCliAppError(parsed)) {
162
- return;
163
- }
164
- const outcome = runDisplayNameAudit(nodeFilesystem, {
165
- rootDir: parsed.value.rootDir,
166
- targetPath: parsed.value.targetPath,
167
- allowlist: parsed.value.allowlist ?? [],
168
- });
169
- if (!consumeCliAppError(outcome)) {
170
- return;
171
- }
172
- if (parsed.value.json) {
173
- logger.out(formatDisplayNameAuditJsonOutput(outcome.value, rootDir));
174
- }
175
- else {
176
- presentDisplayNameAuditResult(outcome.value);
177
- }
178
- process.exitCode = exitCodeForDisplayNameAuditResult(outcome.value);
179
- });
180
- cmd
181
- .command("comments")
182
- .description("Report section dividers that are not in the repo's one allowed form")
183
- .argument("[target]", "Directory or file to scan (default: the repo root)")
184
- .option("--fix", "Rewrite every mechanically fixable divider in place", false)
185
- .option("--json", "Print one JSON summary on stdout", false)
186
- .action(async (target, opts) => {
187
- const prelude = await prepareCommentAudit(nodeFilesystem, {
188
- currentWorkingDirectory: process.cwd(),
189
- rawTarget: readOptionalPositionalArg(target),
190
- });
191
- if (!consumeCliAppError(prelude)) {
192
- return;
193
- }
194
- const { rootDir, targetPath, allowlist } = prelude.value;
195
- const parsed = parseWithSchema(commentAuditRunRequestSchema, {
196
- rootDir,
197
- targetPath,
198
- allowlist,
199
- fix: !!opts.fix,
200
- json: !!opts.json,
201
- });
202
- if (!consumeCliAppError(parsed)) {
203
- return;
204
- }
205
- const outcome = runCommentAudit(nodeFilesystem, {
206
- rootDir: parsed.value.rootDir,
207
- targetPath: parsed.value.targetPath,
208
- allowlist: parsed.value.allowlist ?? [],
209
- fix: parsed.value.fix,
210
- });
211
- if (!consumeCliAppError(outcome)) {
212
- return;
213
- }
214
- if (parsed.value.json) {
215
- logger.out(formatCommentAuditJsonOutput(outcome.value, rootDir));
216
- }
217
- else {
218
- presentCommentAuditResult(outcome.value);
219
- }
220
- process.exitCode = exitCodeForCommentAuditResult(outcome.value);
221
- });
140
+ registerPipelineSubcommand(cmd, nodeFilesystem, auditCheckToPipeline(rtlCheck));
141
+ registerPipelineSubcommand(cmd, nodeFilesystem, auditCheckToPipeline(linksCheck));
142
+ registerPipelineSubcommand(cmd, nodeFilesystem, auditCheckToPipeline(importsCheck));
143
+ registerPipelineSubcommand(cmd, nodeFilesystem, auditCheckToPipeline(displayNamesCheck));
144
+ registerPipelineSubcommand(cmd, nodeFilesystem, auditCheckToPipeline(commentsCheck));
222
145
  return cmd;
223
146
  }
@@ -0,0 +1,13 @@
1
+ import type { CommentAuditResult } from "#/audit/domain/types";
2
+ /**
3
+ * Exit `1` when any non-allowlisted divider still breaks the convention.
4
+ *
5
+ * @since 0.6.0
6
+ */
7
+ export declare function exitCodeForCommentAuditResult(result: CommentAuditResult): number;
8
+ /**
9
+ * Machine-readable comment-divider summary for `--json`.
10
+ *
11
+ * @since 0.6.0
12
+ */
13
+ export declare function formatCommentAuditJsonOutput(result: CommentAuditResult, rootDir: string): string;
@@ -0,0 +1,22 @@
1
+ import { CLI_EXIT_GENERAL_ERROR, CLI_EXIT_SUCCESS } from "#/core/exit-codes";
2
+ /**
3
+ * Exit `1` when any non-allowlisted divider still breaks the convention.
4
+ *
5
+ * @since 0.6.0
6
+ */
7
+ export function exitCodeForCommentAuditResult(result) {
8
+ return result.breakageCount > 0 ? CLI_EXIT_GENERAL_ERROR : CLI_EXIT_SUCCESS;
9
+ }
10
+ /**
11
+ * Machine-readable comment-divider summary for `--json`.
12
+ *
13
+ * @since 0.6.0
14
+ */
15
+ export function formatCommentAuditJsonOutput(result, rootDir) {
16
+ return JSON.stringify({
17
+ schemaVersion: 1,
18
+ ok: result.breakageCount === 0,
19
+ cwd: rootDir,
20
+ result,
21
+ });
22
+ }
@@ -0,0 +1,19 @@
1
+ import * as z from "zod";
2
+ /**
3
+ * Resolved request for a single comment-divider audit run.
4
+ *
5
+ * @since 0.6.0
6
+ */
7
+ export type CommentAuditRunRequest = {
8
+ readonly rootDir: string;
9
+ readonly targetPath: string;
10
+ readonly allowlist?: ReadonlyArray<string> | undefined;
11
+ readonly fix: boolean;
12
+ readonly json: boolean;
13
+ };
14
+ /**
15
+ * Zod schema for {@link CommentAuditRunRequest}.
16
+ *
17
+ * @since 0.6.0
18
+ */
19
+ export declare const commentAuditRunRequestSchema: z.ZodType<CommentAuditRunRequest>;
@@ -0,0 +1,13 @@
1
+ import * as z from "zod";
2
+ /**
3
+ * Zod schema for {@link CommentAuditRunRequest}.
4
+ *
5
+ * @since 0.6.0
6
+ */
7
+ export const commentAuditRunRequestSchema = z.object({
8
+ rootDir: z.string().min(1),
9
+ targetPath: z.string().min(1),
10
+ allowlist: z.array(z.string()).optional(),
11
+ fix: z.boolean(),
12
+ json: z.boolean(),
13
+ });
@@ -0,0 +1,7 @@
1
+ import type { CommentAuditResult } from "#/audit/domain/types";
2
+ /**
3
+ * Human-readable comment-divider report.
4
+ *
5
+ * @since 0.6.0
6
+ */
7
+ export declare function presentCommentAuditResult(result: CommentAuditResult): void;
@@ -0,0 +1,27 @@
1
+ import { logger } from "#/core/logger";
2
+ /**
3
+ * Human-readable comment-divider report.
4
+ *
5
+ * @since 0.6.0
6
+ */
7
+ export function presentCommentAuditResult(result) {
8
+ for (const file of result.files) {
9
+ logger.out(`\n${file.relativePath}`);
10
+ for (const { line, raw, reason } of file.breakages) {
11
+ logger.out(` ${line}: ${truncate(raw)} → ${reason}`);
12
+ }
13
+ }
14
+ const allowlistSuffix = result.allowlistedCount > 0 ? ` (${result.allowlistedCount} allowlisted)` : "";
15
+ if (result.fixedCount > 0) {
16
+ logger.out(`\n✎ Rewrote ${result.fixedCount} divider(s)`);
17
+ }
18
+ if (result.breakageCount > 0) {
19
+ logger.out(`\n✖ ${result.breakageCount} comment issue(s)${allowlistSuffix} — --fix rewrites divider forms`);
20
+ }
21
+ else {
22
+ logger.out(`✓ ${result.dividerCount} divider(s) across ${result.scannedFileCount} file(s), no banned comment content${allowlistSuffix}`);
23
+ }
24
+ }
25
+ function truncate(raw) {
26
+ return raw.length <= 60 ? raw : `${raw.slice(0, 57)}…`;
27
+ }