@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,70 +1,31 @@
1
+ import type { CodefastConfig } from "#/core/config/schema";
1
2
  import { AppError } from "#/core/errors";
2
- import type { FilesystemPort } from "#/core/filesystem/port";
3
+ import type { Filesystem } from "#/core/filesystem/filesystem";
3
4
  import type { Result } from "#/core/result";
4
5
  /**
5
- * Shared prelude for `audit rtl`: repo root and the canonicalized scan target with its allowlist.
6
+ * Shared prelude for an audit: repo root and the canonicalized scan target with its allowlist.
6
7
  *
7
8
  * @since 0.5.0-canary.6
8
9
  */
9
- export type RtlAuditCommandPrelude = {
10
+ export type AuditCommandPrelude = {
10
11
  readonly rootDir: string;
11
12
  readonly targetPath: string;
12
13
  readonly allowlist: ReadonlyArray<string>;
13
14
  };
14
15
  /**
15
- * Loads config and resolves the scan target for `audit rtl`.
16
+ * Resolves a path that may be absolute or relative to `rootDir`.
16
17
  *
17
18
  * @since 0.5.0-canary.6
18
19
  */
19
- export declare function prepareRtlAudit(fs: FilesystemPort, args: {
20
- readonly currentWorkingDirectory: string;
21
- readonly rawTarget: string | undefined;
22
- }): Promise<Result<RtlAuditCommandPrelude, AppError>>;
23
- /**
24
- * Loads config and resolves the scan target for `audit links`.
25
- *
26
- * @remarks Defaults to the repo root rather than a configured path: a link audit that only covers one
27
- * package cannot see the cross-package references that are the ones most likely to rot.
28
- *
29
- * @since 0.5.0
30
- */
31
- export declare function prepareLinkAudit(fs: FilesystemPort, args: {
32
- readonly currentWorkingDirectory: string;
33
- readonly rawTarget: string | undefined;
34
- }): Promise<Result<RtlAuditCommandPrelude, AppError>>;
35
- /**
36
- * Loads config and resolves the scan target for `audit imports`.
37
- *
38
- * @remarks Defaults to the repo root: the import policy is repo-wide, and generated or vendored
39
- * trees are already excluded by the shared walk.
40
- *
41
- * @since 0.10.0
42
- */
43
- export declare function prepareImportsAudit(fs: FilesystemPort, args: {
44
- readonly currentWorkingDirectory: string;
45
- readonly rawTarget: string | undefined;
46
- }): Promise<Result<RtlAuditCommandPrelude, AppError>>;
47
- /**
48
- * Loads config and resolves the scan target for `audit comments`.
49
- *
50
- * @remarks Defaults to the repo root: a divider convention that only holds inside one package
51
- * is not a convention.
52
- *
53
- * @since 0.6.0
54
- */
55
- export declare function prepareCommentAudit(fs: FilesystemPort, args: {
56
- readonly currentWorkingDirectory: string;
57
- readonly rawTarget: string | undefined;
58
- }): Promise<Result<RtlAuditCommandPrelude, AppError>>;
20
+ export declare function resolveRepoRelativePath(rootDir: string, maybeRelative: string): string;
59
21
  /**
60
- * Loads config and resolves the scan target for `audit display-names`.
22
+ * Loads config and resolves the repo root as the scan target, taking the allowlist the caller selects.
61
23
  *
62
- * @remarks Defaults to the repo root: a display name collides across packages, so the convention
63
- * has to hold across them.
24
+ * @remarks Every repo-wide audit shares this prelude; only the config key its allowlist comes from differs.
64
25
  *
65
- * @since 0.9.0
26
+ * @since 0.11.0
66
27
  */
67
- export declare function prepareDisplayNameAudit(fs: FilesystemPort, args: {
28
+ export declare function prepareRepoRootAudit(fs: Filesystem, args: {
68
29
  readonly currentWorkingDirectory: string;
69
30
  readonly rawTarget: string | undefined;
70
- }): Promise<Result<RtlAuditCommandPrelude, AppError>>;
31
+ }, selectAllowlist: (config: CodefastConfig) => ReadonlyArray<string>): Promise<Result<AuditCommandPrelude, AppError>>;
@@ -1,146 +1,24 @@
1
- import { resolveRepoRelativePath } from "#/audit/cli-schema";
1
+ import path from "node:path";
2
2
  import { loadCodefastConfig } from "#/core/config";
3
3
  import { AppError, messageFrom } from "#/core/errors";
4
4
  import { err, ok } from "#/core/result";
5
5
  import { resolveProjectRoot } from "#/core/workspace/resolver";
6
6
  /**
7
- * Loads config and resolves the scan target for `audit rtl`.
7
+ * Resolves a path that may be absolute or relative to `rootDir`.
8
8
  *
9
9
  * @since 0.5.0-canary.6
10
10
  */
11
- export async function prepareRtlAudit(fs, args) {
12
- let rootDir;
13
- try {
14
- // Realpath so allowlist keys (`path.relative(rootDir, file)`) stay stable when cwd is a symlink.
15
- rootDir = fs.canonicalPathSync(resolveProjectRoot(args.currentWorkingDirectory, fs).rootDir);
16
- }
17
- catch (caughtError) {
18
- return err(new AppError("INFRA_FAILURE", messageFrom(caughtError), caughtError));
19
- }
20
- const loadedOutcome = await loadCodefastConfig(rootDir, fs);
21
- if (!loadedOutcome.ok) {
22
- return loadedOutcome;
23
- }
24
- const { config } = loadedOutcome.value;
25
- const rtlConfig = config.audit?.rtl ?? {};
26
- const targetFromCli = args.rawTarget;
27
- const targetFromConfig = rtlConfig.target;
28
- const resolvedTargetInput = targetFromCli ?? targetFromConfig;
29
- if (resolvedTargetInput === undefined) {
30
- return err(new AppError("VALIDATION_ERROR", "Missing scan target: pass a path argument or set audit.rtl.target in codefast.config"));
31
- }
32
- const targetPath = resolveRepoRelativePath(targetFromCli !== undefined ? args.currentWorkingDirectory : rootDir, resolvedTargetInput);
33
- if (!fs.existsSync(targetPath)) {
34
- return err(new AppError("NOT_FOUND", `Not found: ${targetPath}`));
35
- }
36
- return ok({
37
- rootDir,
38
- targetPath: fs.canonicalPathSync(targetPath),
39
- allowlist: rtlConfig.allowlist ?? [],
40
- });
41
- }
42
- /**
43
- * Loads config and resolves the scan target for `audit links`.
44
- *
45
- * @remarks Defaults to the repo root rather than a configured path: a link audit that only covers one
46
- * package cannot see the cross-package references that are the ones most likely to rot.
47
- *
48
- * @since 0.5.0
49
- */
50
- export async function prepareLinkAudit(fs, args) {
51
- let rootDir;
52
- try {
53
- rootDir = fs.canonicalPathSync(resolveProjectRoot(args.currentWorkingDirectory, fs).rootDir);
54
- }
55
- catch (caughtError) {
56
- return err(new AppError("INFRA_FAILURE", messageFrom(caughtError), caughtError));
57
- }
58
- const loadedOutcome = await loadCodefastConfig(rootDir, fs);
59
- if (!loadedOutcome.ok) {
60
- return loadedOutcome;
61
- }
62
- const linksConfig = loadedOutcome.value.config.audit?.links ?? {};
63
- const targetPath = args.rawTarget === undefined ? rootDir : resolveRepoRelativePath(args.currentWorkingDirectory, args.rawTarget);
64
- if (!fs.existsSync(targetPath)) {
65
- return err(new AppError("NOT_FOUND", `Not found: ${targetPath}`));
66
- }
67
- return ok({
68
- rootDir,
69
- targetPath: fs.canonicalPathSync(targetPath),
70
- allowlist: linksConfig.allowlist ?? [],
71
- });
72
- }
73
- /**
74
- * Loads config and resolves the scan target for `audit imports`.
75
- *
76
- * @remarks Defaults to the repo root: the import policy is repo-wide, and generated or vendored
77
- * trees are already excluded by the shared walk.
78
- *
79
- * @since 0.10.0
80
- */
81
- export async function prepareImportsAudit(fs, args) {
82
- let rootDir;
83
- try {
84
- rootDir = fs.canonicalPathSync(resolveProjectRoot(args.currentWorkingDirectory, fs).rootDir);
85
- }
86
- catch (caughtError) {
87
- return err(new AppError("INFRA_FAILURE", messageFrom(caughtError), caughtError));
88
- }
89
- const loadedOutcome = await loadCodefastConfig(rootDir, fs);
90
- if (!loadedOutcome.ok) {
91
- return loadedOutcome;
92
- }
93
- const importsConfig = loadedOutcome.value.config.audit?.imports ?? {};
94
- const targetPath = args.rawTarget === undefined ? rootDir : resolveRepoRelativePath(args.currentWorkingDirectory, args.rawTarget);
95
- if (!fs.existsSync(targetPath)) {
96
- return err(new AppError("NOT_FOUND", `Not found: ${targetPath}`));
97
- }
98
- return ok({
99
- rootDir,
100
- targetPath: fs.canonicalPathSync(targetPath),
101
- allowlist: importsConfig.allowlist ?? [],
102
- });
103
- }
104
- /**
105
- * Loads config and resolves the scan target for `audit comments`.
106
- *
107
- * @remarks Defaults to the repo root: a divider convention that only holds inside one package
108
- * is not a convention.
109
- *
110
- * @since 0.6.0
111
- */
112
- export async function prepareCommentAudit(fs, args) {
113
- let rootDir;
114
- try {
115
- rootDir = fs.canonicalPathSync(resolveProjectRoot(args.currentWorkingDirectory, fs).rootDir);
116
- }
117
- catch (caughtError) {
118
- return err(new AppError("INFRA_FAILURE", messageFrom(caughtError), caughtError));
119
- }
120
- const loadedOutcome = await loadCodefastConfig(rootDir, fs);
121
- if (!loadedOutcome.ok) {
122
- return loadedOutcome;
123
- }
124
- const commentsConfig = loadedOutcome.value.config.audit?.comments ?? {};
125
- const targetPath = args.rawTarget === undefined ? rootDir : resolveRepoRelativePath(args.currentWorkingDirectory, args.rawTarget);
126
- if (!fs.existsSync(targetPath)) {
127
- return err(new AppError("NOT_FOUND", `Not found: ${targetPath}`));
128
- }
129
- return ok({
130
- rootDir,
131
- targetPath: fs.canonicalPathSync(targetPath),
132
- allowlist: commentsConfig.allowlist ?? [],
133
- });
11
+ export function resolveRepoRelativePath(rootDir, maybeRelative) {
12
+ return path.isAbsolute(maybeRelative) ? path.resolve(maybeRelative) : path.resolve(rootDir, maybeRelative);
134
13
  }
135
14
  /**
136
- * Loads config and resolves the scan target for `audit display-names`.
15
+ * Loads config and resolves the repo root as the scan target, taking the allowlist the caller selects.
137
16
  *
138
- * @remarks Defaults to the repo root: a display name collides across packages, so the convention
139
- * has to hold across them.
17
+ * @remarks Every repo-wide audit shares this prelude; only the config key its allowlist comes from differs.
140
18
  *
141
- * @since 0.9.0
19
+ * @since 0.11.0
142
20
  */
143
- export async function prepareDisplayNameAudit(fs, args) {
21
+ export async function prepareRepoRootAudit(fs, args, selectAllowlist) {
144
22
  let rootDir;
145
23
  try {
146
24
  rootDir = fs.canonicalPathSync(resolveProjectRoot(args.currentWorkingDirectory, fs).rootDir);
@@ -152,7 +30,6 @@ export async function prepareDisplayNameAudit(fs, args) {
152
30
  if (!loadedOutcome.ok) {
153
31
  return loadedOutcome;
154
32
  }
155
- const displayNamesConfig = loadedOutcome.value.config.audit?.displayNames ?? {};
156
33
  const targetPath = args.rawTarget === undefined ? rootDir : resolveRepoRelativePath(args.currentWorkingDirectory, args.rawTarget);
157
34
  if (!fs.existsSync(targetPath)) {
158
35
  return err(new AppError("NOT_FOUND", `Not found: ${targetPath}`));
@@ -160,6 +37,6 @@ export async function prepareDisplayNameAudit(fs, args) {
160
37
  return ok({
161
38
  rootDir,
162
39
  targetPath: fs.canonicalPathSync(targetPath),
163
- allowlist: displayNamesConfig.allowlist ?? [],
40
+ allowlist: selectAllowlist(loadedOutcome.value.config),
164
41
  });
165
42
  }
@@ -0,0 +1,13 @@
1
+ import type { RtlAuditResult } from "#/audit/domain/types";
2
+ /**
3
+ * Exit `1` when any non-allowlisted violation remains.
4
+ *
5
+ * @since 0.5.0-canary.6
6
+ */
7
+ export declare function exitCodeForRtlAuditResult(result: RtlAuditResult): number;
8
+ /**
9
+ * Machine-readable RTL audit summary for `--json`.
10
+ *
11
+ * @since 0.5.0-canary.6
12
+ */
13
+ export declare function formatRtlAuditJsonOutput(result: RtlAuditResult, 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 violation remains.
4
+ *
5
+ * @since 0.5.0-canary.6
6
+ */
7
+ export function exitCodeForRtlAuditResult(result) {
8
+ return result.violationCount > 0 ? CLI_EXIT_GENERAL_ERROR : CLI_EXIT_SUCCESS;
9
+ }
10
+ /**
11
+ * Machine-readable RTL audit summary for `--json`.
12
+ *
13
+ * @since 0.5.0-canary.6
14
+ */
15
+ export function formatRtlAuditJsonOutput(result, rootDir) {
16
+ return JSON.stringify({
17
+ schemaVersion: 1,
18
+ ok: result.violationCount === 0,
19
+ cwd: rootDir,
20
+ result,
21
+ });
22
+ }
@@ -0,0 +1,18 @@
1
+ import * as z from "zod";
2
+ /**
3
+ * Resolved request for a single RTL audit run.
4
+ *
5
+ * @since 0.5.0-canary.6
6
+ */
7
+ export type RtlAuditRunRequest = {
8
+ readonly rootDir: string;
9
+ readonly targetPath: string;
10
+ readonly allowlist?: ReadonlyArray<string> | undefined;
11
+ readonly json: boolean;
12
+ };
13
+ /**
14
+ * Zod schema for {@link RtlAuditRunRequest}.
15
+ *
16
+ * @since 0.5.0-canary.6
17
+ */
18
+ export declare const rtlAuditRunRequestSchema: z.ZodType<RtlAuditRunRequest>;
@@ -0,0 +1,12 @@
1
+ import * as z from "zod";
2
+ /**
3
+ * Zod schema for {@link RtlAuditRunRequest}.
4
+ *
5
+ * @since 0.5.0-canary.6
6
+ */
7
+ export const rtlAuditRunRequestSchema = z.object({
8
+ rootDir: z.string().min(1),
9
+ targetPath: z.string().min(1),
10
+ allowlist: z.array(z.string()).optional(),
11
+ json: z.boolean(),
12
+ });
@@ -1,5 +1,5 @@
1
- import { DIRECTION_RESOLVED_VARIANT, PHYSICAL_SIDE_VARIANT, RTL_MAPPINGS, RTL_REVERSE_MAPPINGS, RTL_SWAP_MAPPINGS, RTL_TRANSLATE_X_MAPPINGS, SLIDE_PREFIXES, } from "#/audit/domain/mappings";
2
- import { collectTokens } from "#/audit/domain/tokenize";
1
+ import { DIRECTION_RESOLVED_VARIANT, PHYSICAL_SIDE_VARIANT, RTL_MAPPINGS, RTL_REVERSE_MAPPINGS, RTL_SWAP_MAPPINGS, RTL_TRANSLATE_X_MAPPINGS, SLIDE_PREFIXES, } from "#/audit/rtl/domain/mappings";
2
+ import { collectTokens } from "#/audit/rtl/domain/tokenize";
3
3
  /**
4
4
  * A token satisfies an "rtl companion" requirement when the same file has a
5
5
  * token whose variants include `rtl` and whose value matches. Variant order
@@ -0,0 +1,7 @@
1
+ import type { RtlAuditResult } from "#/audit/domain/types";
2
+ /**
3
+ * Human-readable RTL audit report (matches the former packages/ui script shape).
4
+ *
5
+ * @since 0.5.0-canary.6
6
+ */
7
+ export declare function presentRtlAuditResult(result: RtlAuditResult): void;
@@ -0,0 +1,21 @@
1
+ import { logger } from "#/core/logger";
2
+ /**
3
+ * Human-readable RTL audit report (matches the former packages/ui script shape).
4
+ *
5
+ * @since 0.5.0-canary.6
6
+ */
7
+ export function presentRtlAuditResult(result) {
8
+ for (const file of result.files) {
9
+ logger.out(`\n${file.relativePath}`);
10
+ for (const { line, raw, suggestion } of file.violations) {
11
+ logger.out(` ${line}: ${raw} → ${suggestion}`);
12
+ }
13
+ }
14
+ const allowlistSuffix = result.allowlistedCount > 0 ? ` (${result.allowlistedCount} allowlisted)` : "";
15
+ if (result.violationCount > 0) {
16
+ logger.out(`\n✖ ${result.violationCount} RTL violation(s)${allowlistSuffix}`);
17
+ }
18
+ else {
19
+ logger.out(`✓ No physical-direction classes outside the allowlist${allowlistSuffix}`);
20
+ }
21
+ }
@@ -0,0 +1,13 @@
1
+ import type { AuditCommandPrelude } from "#/audit/prepare";
2
+ import { AppError } from "#/core/errors";
3
+ import type { Filesystem } from "#/core/filesystem/filesystem";
4
+ import type { Result } from "#/core/result";
5
+ /**
6
+ * Loads config and resolves the scan target for `audit rtl`.
7
+ *
8
+ * @since 0.5.0-canary.6
9
+ */
10
+ export declare function prepareRtlAudit(fs: Filesystem, args: {
11
+ readonly currentWorkingDirectory: string;
12
+ readonly rawTarget: string | undefined;
13
+ }): Promise<Result<AuditCommandPrelude, AppError>>;
@@ -0,0 +1,41 @@
1
+ import { resolveRepoRelativePath } from "#/audit/prepare";
2
+ import { loadCodefastConfig } from "#/core/config";
3
+ import { AppError, messageFrom } from "#/core/errors";
4
+ import { err, ok } from "#/core/result";
5
+ import { resolveProjectRoot } from "#/core/workspace/resolver";
6
+ /**
7
+ * Loads config and resolves the scan target for `audit rtl`.
8
+ *
9
+ * @since 0.5.0-canary.6
10
+ */
11
+ export async function prepareRtlAudit(fs, args) {
12
+ let rootDir;
13
+ try {
14
+ // Realpath so allowlist keys (`path.relative(rootDir, file)`) stay stable when cwd is a symlink.
15
+ rootDir = fs.canonicalPathSync(resolveProjectRoot(args.currentWorkingDirectory, fs).rootDir);
16
+ }
17
+ catch (caughtError) {
18
+ return err(new AppError("INFRA_FAILURE", messageFrom(caughtError), caughtError));
19
+ }
20
+ const loadedOutcome = await loadCodefastConfig(rootDir, fs);
21
+ if (!loadedOutcome.ok) {
22
+ return loadedOutcome;
23
+ }
24
+ const { config } = loadedOutcome.value;
25
+ const rtlConfig = config.audit?.rtl ?? {};
26
+ const targetFromCli = args.rawTarget;
27
+ const targetFromConfig = rtlConfig.target;
28
+ const resolvedTargetInput = targetFromCli ?? targetFromConfig;
29
+ if (resolvedTargetInput === undefined) {
30
+ return err(new AppError("VALIDATION_ERROR", "Missing scan target: pass a path argument or set audit.rtl.target in codefast.config"));
31
+ }
32
+ const targetPath = resolveRepoRelativePath(targetFromCli !== undefined ? args.currentWorkingDirectory : rootDir, resolvedTargetInput);
33
+ if (!fs.existsSync(targetPath)) {
34
+ return err(new AppError("NOT_FOUND", `Not found: ${targetPath}`));
35
+ }
36
+ return ok({
37
+ rootDir,
38
+ targetPath: fs.canonicalPathSync(targetPath),
39
+ allowlist: rtlConfig.allowlist ?? [],
40
+ });
41
+ }
@@ -1,13 +1,13 @@
1
1
  import type { RtlAuditResult } from "#/audit/domain/types";
2
2
  import { 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
  * Scans a target path for physical-direction Tailwind class violations.
7
7
  *
8
8
  * @since 0.5.0-canary.6
9
9
  */
10
- export declare function runRtlAudit(fs: FilesystemPort, args: {
10
+ export declare function runRtlAudit(fs: Filesystem, args: {
11
11
  readonly rootDir: string;
12
12
  readonly targetPath: string;
13
13
  readonly allowlist: ReadonlyArray<string>;
@@ -1,5 +1,5 @@
1
1
  import path from "node:path";
2
- import { auditFileContent } from "#/audit/domain/audit-file";
2
+ import { auditFileContent } from "#/audit/rtl/domain/audit-file";
3
3
  import { AppError, messageFrom } from "#/core/errors";
4
4
  import { err, ok } from "#/core/result";
5
5
  import { walkTsxFiles } from "#/core/workspace/typescript-walk";
@@ -0,0 +1,91 @@
1
+ import type { Command } from "commander";
2
+ import type { ZodType } from "zod";
3
+ import type { GlobalCliOptions } from "#/core/cli/global-options";
4
+ import type { AppError } from "#/core/errors";
5
+ import type { Filesystem } from "#/core/filesystem/filesystem";
6
+ import type { Result } from "#/core/result";
7
+ /**
8
+ * The options every command shares: the `--json` machine-output flag. Per-command option shapes extend this.
9
+ *
10
+ * @since 0.11.0
11
+ */
12
+ export interface BaseCommandOptions {
13
+ readonly json?: boolean | undefined;
14
+ }
15
+ /**
16
+ * A pipeline's `prepare` slot: resolves the argv context (cwd, positional, global options) into the command's prelude.
17
+ *
18
+ * @since 0.11.0
19
+ */
20
+ export type CommandPrepare<Prelude> = (fs: Filesystem, input: {
21
+ readonly currentWorkingDirectory: string;
22
+ readonly rawArg: string | undefined;
23
+ readonly globals: GlobalCliOptions;
24
+ }) => Promise<Result<Prelude, AppError>>;
25
+ /** The prelude and options a request is built from, before the run. */
26
+ interface BuildInput<Prelude, Opts> {
27
+ readonly prelude: Prelude;
28
+ readonly opts: Opts;
29
+ readonly globals: GlobalCliOptions;
30
+ }
31
+ /** The run result and its surrounding context, for the report phase. */
32
+ interface ReportInput<Prelude, RunResult, Presenter, Opts> {
33
+ readonly result: RunResult;
34
+ readonly prelude: Prelude;
35
+ readonly opts: Opts;
36
+ readonly presenter: Presenter | undefined;
37
+ readonly elapsedSeconds: number;
38
+ }
39
+ /**
40
+ * The invariant shape every workspace command and subcommand shares: resolve a prelude, build and
41
+ * validate a request, run it, then report to one of two audiences and record a single exit code.
42
+ *
43
+ * @remarks The optional slots carry the per-command variations — a working-tree guard and a streaming
44
+ * presenter — so the control flow itself is written once.
45
+ *
46
+ * @since 0.11.0
47
+ */
48
+ export interface CommandPipeline<Prelude, Request, RunResult, Presenter = never, Opts extends BaseCommandOptions = BaseCommandOptions> {
49
+ readonly name?: string | undefined;
50
+ readonly description?: string | undefined;
51
+ readonly positional?: {
52
+ readonly name: string;
53
+ readonly help: string;
54
+ } | undefined;
55
+ readonly jsonHelp?: string | undefined;
56
+ readonly configureArgv?: ((command: Command) => void) | undefined;
57
+ readonly prepare: CommandPrepare<Prelude>;
58
+ readonly guard?: ((input: {
59
+ readonly prelude: Prelude;
60
+ readonly opts: Opts;
61
+ }) => Promise<Result<void, AppError>>) | undefined;
62
+ readonly schema: ZodType<Request>;
63
+ readonly buildRequest: (input: BuildInput<Prelude, Opts>) => unknown;
64
+ readonly createPresenter?: ((input: BuildInput<Prelude, Opts>) => Presenter) | undefined;
65
+ readonly run: (fs: Filesystem, request: Request, presenter: Presenter | undefined) => Promise<Result<RunResult, AppError>>;
66
+ readonly presentHuman?: ((input: ReportInput<Prelude, RunResult, Presenter, Opts>) => void) | undefined;
67
+ readonly formatJson: (input: ReportInput<Prelude, RunResult, Presenter, Opts>) => string;
68
+ readonly exitCode: (result: RunResult) => number;
69
+ }
70
+ /**
71
+ * A pipeline that names itself, so it can register as a subcommand under a parent command.
72
+ *
73
+ * @since 0.11.0
74
+ */
75
+ export type NamedCommandPipeline<Prelude, Request, RunResult, Presenter = never, Opts extends BaseCommandOptions = BaseCommandOptions> = CommandPipeline<Prelude, Request, RunResult, Presenter, Opts> & {
76
+ readonly name: string;
77
+ readonly description: string;
78
+ };
79
+ /**
80
+ * Wires a pipeline onto a named Command: the shared positional, `--json`, any extra options, and the action.
81
+ *
82
+ * @since 0.11.0
83
+ */
84
+ export declare function applyCommandPipeline<Prelude, Request, RunResult, Presenter, Opts extends BaseCommandOptions>(command: Command, fs: Filesystem, pipeline: CommandPipeline<Prelude, Request, RunResult, Presenter, Opts>): Command;
85
+ /**
86
+ * Registers a pipeline as a subcommand under `parent`, taking its name and description from the pipeline.
87
+ *
88
+ * @since 0.11.0
89
+ */
90
+ export declare function registerPipelineSubcommand<Prelude, Request, RunResult, Presenter, Opts extends BaseCommandOptions>(parent: Command, fs: Filesystem, pipeline: NamedCommandPipeline<Prelude, Request, RunResult, Presenter, Opts>): void;
91
+ export {};
@@ -0,0 +1,83 @@
1
+ import process from "node:process";
2
+ import { globalCliCommanderOptionsSchema } from "#/core/cli/global-options";
3
+ import { readOptionalPositionalArg } from "#/core/cli/positional";
4
+ import { consumeCliAppError } from "#/core/cli/result-handle";
5
+ import { logger } from "#/core/logger";
6
+ import { parseWithSchema } from "#/core/schema-parse";
7
+ const DEFAULT_JSON_HELP = "Print one JSON summary on stdout (suppresses human progress)";
8
+ function readGlobalOptions(command) {
9
+ return (command.optsWithGlobals?.() ?? command.opts());
10
+ }
11
+ function makeAction(fs, pipeline) {
12
+ return async (rawArg, opts, command) => {
13
+ const globalsOutcome = parseWithSchema(globalCliCommanderOptionsSchema, readGlobalOptions(command));
14
+ if (!consumeCliAppError(globalsOutcome)) {
15
+ return;
16
+ }
17
+ const globals = globalsOutcome.value;
18
+ const prelude = await pipeline.prepare(fs, {
19
+ currentWorkingDirectory: process.cwd(),
20
+ rawArg: readOptionalPositionalArg(rawArg),
21
+ globals,
22
+ });
23
+ if (!consumeCliAppError(prelude)) {
24
+ return;
25
+ }
26
+ if (pipeline.guard) {
27
+ const guarded = await pipeline.guard({ prelude: prelude.value, opts });
28
+ if (!consumeCliAppError(guarded)) {
29
+ return;
30
+ }
31
+ }
32
+ const parsed = parseWithSchema(pipeline.schema, pipeline.buildRequest({ prelude: prelude.value, opts, globals }));
33
+ if (!consumeCliAppError(parsed)) {
34
+ return;
35
+ }
36
+ const json = !!opts.json;
37
+ const presenter = !json && pipeline.createPresenter
38
+ ? pipeline.createPresenter({ prelude: prelude.value, opts, globals })
39
+ : undefined;
40
+ const startedAt = performance.now();
41
+ const outcome = await pipeline.run(fs, parsed.value, presenter);
42
+ if (!consumeCliAppError(outcome)) {
43
+ return;
44
+ }
45
+ const elapsedSeconds = (performance.now() - startedAt) / 1000;
46
+ const report = {
47
+ result: outcome.value,
48
+ prelude: prelude.value,
49
+ opts,
50
+ presenter,
51
+ elapsedSeconds,
52
+ };
53
+ if (json) {
54
+ logger.out(pipeline.formatJson(report));
55
+ }
56
+ else {
57
+ pipeline.presentHuman?.(report);
58
+ }
59
+ process.exitCode = pipeline.exitCode(outcome.value);
60
+ };
61
+ }
62
+ /**
63
+ * Wires a pipeline onto a named Command: the shared positional, `--json`, any extra options, and the action.
64
+ *
65
+ * @since 0.11.0
66
+ */
67
+ export function applyCommandPipeline(command, fs, pipeline) {
68
+ if (pipeline.positional) {
69
+ command.argument(pipeline.positional.name, pipeline.positional.help);
70
+ }
71
+ command.option("--json", pipeline.jsonHelp ?? DEFAULT_JSON_HELP, false);
72
+ pipeline.configureArgv?.(command);
73
+ command.action(makeAction(fs, pipeline));
74
+ return command;
75
+ }
76
+ /**
77
+ * Registers a pipeline as a subcommand under `parent`, taking its name and description from the pipeline.
78
+ *
79
+ * @since 0.11.0
80
+ */
81
+ export function registerPipelineSubcommand(parent, fs, pipeline) {
82
+ applyCommandPipeline(parent.command(pipeline.name).description(pipeline.description), fs, pipeline);
83
+ }
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import * as z from "zod";
2
2
  /**
3
3
  * The validated global CLI options shared by every subcommand.
4
4
  *
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import * as z from "zod";
2
2
  /**
3
3
  * Commander global opts shape validated before mirror prelude (`color` from `--no-color`).
4
4
  *
@@ -0,0 +1,9 @@
1
+ import { AppError } from "#/core/errors";
2
+ import type { Filesystem } from "#/core/filesystem/filesystem";
3
+ import type { Result } from "#/core/result";
4
+ /**
5
+ * Resolves the project root as a `Result`, mapping a resolution failure to an `INFRA_FAILURE` error.
6
+ *
7
+ * @since 0.11.0
8
+ */
9
+ export declare function resolveProjectRootResult(fs: Filesystem, currentWorkingDirectory: string): Result<string, AppError>;
@@ -0,0 +1,16 @@
1
+ import { AppError, messageFrom } from "#/core/errors";
2
+ import { err, ok } from "#/core/result";
3
+ import { resolveProjectRoot } from "#/core/workspace/resolver";
4
+ /**
5
+ * Resolves the project root as a `Result`, mapping a resolution failure to an `INFRA_FAILURE` error.
6
+ *
7
+ * @since 0.11.0
8
+ */
9
+ export function resolveProjectRootResult(fs, currentWorkingDirectory) {
10
+ try {
11
+ return ok(resolveProjectRoot(currentWorkingDirectory, fs).rootDir);
12
+ }
13
+ catch (caughtError) {
14
+ return err(new AppError("INFRA_FAILURE", messageFrom(caughtError), caughtError));
15
+ }
16
+ }