@codefast/cli 0.9.0 → 0.10.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 (159) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +329 -149
  3. package/dist/arrange/analyze.d.ts +10 -0
  4. package/dist/arrange/cli-schema.d.ts +50 -0
  5. package/dist/arrange/command.d.ts +7 -0
  6. package/dist/arrange/domain/analyze-service.d.ts +18 -0
  7. package/dist/arrange/domain/ast/ast-node.d.ts +394 -0
  8. package/dist/arrange/domain/ast/collectors-cn.d.ts +26 -0
  9. package/dist/arrange/domain/ast/collectors-jsx.d.ts +8 -0
  10. package/dist/arrange/domain/ast/collectors-tv.d.ts +34 -0
  11. package/dist/arrange/domain/ast/helpers.d.ts +36 -0
  12. package/dist/arrange/domain/ast/helpers.js +1 -0
  13. package/dist/arrange/domain/ast/simplify-targets.d.ts +22 -0
  14. package/dist/arrange/domain/ast/targets.d.ts +20 -0
  15. package/dist/arrange/domain/constants.d.ts +111 -0
  16. package/dist/arrange/domain/grouping-service.d.ts +100 -0
  17. package/dist/arrange/domain/grouping.d.ts +21 -0
  18. package/dist/arrange/domain/imports.d.ts +14 -0
  19. package/dist/arrange/domain/source-text-formatters.d.ts +33 -0
  20. package/dist/arrange/domain/tailwind-token.d.ts +24 -0
  21. package/dist/arrange/domain/token-classifier.d.ts +47 -0
  22. package/dist/arrange/domain/types.d.ts +208 -0
  23. package/dist/arrange/output.d.ts +26 -0
  24. package/dist/arrange/process-file.d.ts +11 -0
  25. package/dist/arrange/resolve-target.d.ts +10 -0
  26. package/dist/arrange/resolve-target.js +3 -16
  27. package/dist/arrange/scan-target.d.ts +7 -0
  28. package/dist/arrange/simplify-process-file.d.ts +11 -0
  29. package/dist/arrange/simplify-sync.d.ts +13 -0
  30. package/dist/arrange/source-parse.d.ts +7 -0
  31. package/dist/arrange/suggest.d.ts +8 -0
  32. package/dist/arrange/sync.d.ts +11 -0
  33. package/dist/arrange/typescript-ast-translator.d.ts +31 -0
  34. package/dist/arrange/workspace.d.ts +13 -0
  35. package/dist/arrange/workspace.js +2 -2
  36. package/dist/audit/cli-schema.d.ts +93 -0
  37. package/dist/audit/cli-schema.js +3 -3
  38. package/dist/audit/command.d.ts +8 -0
  39. package/dist/audit/command.js +12 -12
  40. package/dist/audit/domain/audit-file.d.ts +7 -0
  41. package/dist/audit/domain/comment-content.d.ts +26 -0
  42. package/dist/audit/domain/comment-dividers.d.ts +62 -0
  43. package/dist/audit/domain/display-names.d.ts +11 -0
  44. package/dist/audit/domain/import-policy.d.ts +34 -0
  45. package/dist/audit/domain/import-policy.js +147 -0
  46. package/dist/audit/domain/link-references.d.ts +40 -0
  47. package/dist/audit/domain/mappings.d.ts +45 -0
  48. package/dist/audit/domain/markdown-links.d.ts +44 -0
  49. package/dist/audit/domain/since-versions.d.ts +26 -0
  50. package/dist/audit/domain/tokenize.d.ts +14 -0
  51. package/dist/audit/domain/tsdoc-syntax.d.ts +20 -0
  52. package/dist/audit/domain/types.d.ts +171 -0
  53. package/dist/audit/output.d.ts +91 -0
  54. package/dist/audit/output.js +11 -11
  55. package/dist/audit/prepare.d.ts +70 -0
  56. package/dist/audit/prepare.js +11 -11
  57. package/dist/audit/run-comments.d.ts +17 -0
  58. package/dist/audit/run-display-names.d.ts +14 -0
  59. package/dist/audit/run-imports.d.ts +14 -0
  60. package/dist/audit/{run-react.js → run-imports.js} +15 -5
  61. package/dist/audit/run-links.d.ts +14 -0
  62. package/dist/audit/run.d.ts +14 -0
  63. package/dist/bin.d.ts +2 -0
  64. package/dist/cli.d.ts +6 -0
  65. package/dist/core/cli/format-error.d.ts +7 -0
  66. package/dist/core/cli/global-options.d.ts +15 -0
  67. package/dist/core/cli/positional.d.ts +6 -0
  68. package/dist/core/cli/result-handle.d.ts +19 -0
  69. package/dist/core/config/define-config.d.ts +7 -0
  70. package/dist/core/config/define-config.js +8 -0
  71. package/dist/core/config/loader.d.ts +18 -0
  72. package/dist/core/config/loader.js +2 -7
  73. package/dist/core/config/schema.d.ts +99 -0
  74. package/dist/core/config/schema.js +7 -85
  75. package/dist/core/config/warnings.d.ts +6 -0
  76. package/dist/core/config.d.ts +12 -0
  77. package/dist/core/errors.d.ts +25 -0
  78. package/dist/core/exit-codes.d.ts +18 -0
  79. package/dist/core/filesystem/node.d.ts +7 -0
  80. package/dist/core/filesystem/node.js +1 -0
  81. package/dist/core/filesystem/port.d.ts +44 -0
  82. package/dist/core/glob.d.ts +19 -0
  83. package/dist/core/logger.d.ts +9 -0
  84. package/dist/core/result.d.ts +30 -0
  85. package/dist/core/schema-parse.d.ts +9 -0
  86. package/dist/core/source-text-edit.d.ts +33 -0
  87. package/dist/core/verbose-diagnostics.d.ts +6 -0
  88. package/dist/core/workspace/ancestor-directories.d.ts +12 -0
  89. package/dist/core/workspace/ancestor-directories.js +30 -0
  90. package/dist/core/workspace/markdown-walk.d.ts +7 -0
  91. package/dist/core/workspace/markdown-walk.js +2 -20
  92. package/dist/core/workspace/package-version.d.ts +9 -0
  93. package/dist/core/workspace/package-version.js +8 -12
  94. package/dist/core/workspace/resolver.d.ts +39 -0
  95. package/dist/core/workspace/resolver.js +58 -75
  96. package/dist/core/workspace/skip-directories.d.ts +6 -0
  97. package/dist/core/workspace/source-walk.d.ts +16 -0
  98. package/dist/core/workspace/source-walk.js +2 -19
  99. package/dist/core/workspace/typescript-walk.d.ts +7 -0
  100. package/dist/core/workspace/typescript-walk.js +2 -23
  101. package/dist/core/workspace/walk-files.d.ts +7 -0
  102. package/dist/core/workspace/walk-files.js +27 -0
  103. package/dist/core/workspace/well-known-files.d.ts +18 -0
  104. package/dist/core/workspace/well-known-files.js +18 -0
  105. package/dist/index.d.ts +6 -0
  106. package/dist/index.js +5 -0
  107. package/dist/mirror/cli-result.d.ts +13 -0
  108. package/dist/mirror/cli-schema.d.ts +8 -0
  109. package/dist/mirror/command.d.ts +7 -0
  110. package/dist/mirror/dist-filesystem-impl.d.ts +8 -0
  111. package/dist/mirror/domain/constants.d.ts +18 -0
  112. package/dist/mirror/domain/constants.js +0 -12
  113. package/dist/mirror/domain/dirent-guard.d.ts +10 -0
  114. package/dist/mirror/domain/dist-filesystem.d.ts +9 -0
  115. package/dist/mirror/domain/errors.d.ts +24 -0
  116. package/dist/mirror/domain/exports.d.ts +36 -0
  117. package/dist/mirror/domain/package-display-name.d.ts +8 -0
  118. package/dist/mirror/domain/path-normalizer.d.ts +6 -0
  119. package/dist/mirror/domain/types.d.ts +131 -0
  120. package/dist/mirror/output.d.ts +23 -0
  121. package/dist/mirror/package-path.d.ts +19 -0
  122. package/dist/mirror/prepare.d.ts +15 -0
  123. package/dist/mirror/prepare.js +2 -2
  124. package/dist/mirror/supplement-exports.d.ts +27 -0
  125. package/dist/mirror/supplement-exports.js +2 -2
  126. package/dist/mirror/sync-reporter.d.ts +60 -0
  127. package/dist/mirror/sync-reporter.js +4 -0
  128. package/dist/mirror/sync-types.d.ts +43 -0
  129. package/dist/mirror/sync-workspace-package.d.ts +9 -0
  130. package/dist/mirror/sync-workspace-package.js +3 -3
  131. package/dist/mirror/sync.d.ts +12 -0
  132. package/dist/mirror/sync.js +5 -3
  133. package/dist/mirror/write-exports.d.ts +15 -0
  134. package/dist/pack-slim/cli-result.d.ts +13 -0
  135. package/dist/pack-slim/cli-schema.d.ts +17 -0
  136. package/dist/pack-slim/command.d.ts +7 -0
  137. package/dist/pack-slim/command.js +2 -2
  138. package/dist/pack-slim/domain/transform.d.ts +69 -0
  139. package/dist/pack-slim/domain/types.d.ts +46 -0
  140. package/dist/pack-slim/output.d.ts +15 -0
  141. package/dist/pack-slim/sync.d.ts +23 -0
  142. package/dist/pack-slim/sync.js +4 -4
  143. package/dist/pack-slim/working-tree.d.ts +20 -0
  144. package/dist/tag/cli-result.d.ts +7 -0
  145. package/dist/tag/cli-schema.d.ts +8 -0
  146. package/dist/tag/command.d.ts +7 -0
  147. package/dist/tag/domain/types.d.ts +111 -0
  148. package/dist/tag/output.d.ts +17 -0
  149. package/dist/tag/prepare.d.ts +13 -0
  150. package/dist/tag/prepare.js +2 -2
  151. package/dist/tag/resolve-target-path.d.ts +10 -0
  152. package/dist/tag/since-writer.d.ts +32 -0
  153. package/dist/tag/sync.d.ts +42 -0
  154. package/dist/tag/target-candidates.d.ts +8 -0
  155. package/dist/tag/target-candidates.js +1 -1
  156. package/dist/tag/target-runner.d.ts +8 -0
  157. package/dist/tag/version-resolver.d.ts +7 -0
  158. package/package.json +12 -1
  159. package/dist/audit/domain/react-imports.js +0 -91
@@ -0,0 +1,7 @@
1
+ import type { DomainSourceFile } from "#/arrange/domain/ast/ast-node";
2
+ /**
3
+ * Parses source text into a domain source file via the shared `oxc-parser` translator.
4
+ *
5
+ * @since 0.3.16-canary.0
6
+ */
7
+ export declare function parseDomainSourceFile(filePath: string, sourceText: string): DomainSourceFile;
@@ -0,0 +1,8 @@
1
+ import type { ArrangeSuggestGroupsRequest } from "#/arrange/cli-schema";
2
+ import type { ArrangeSuggestGroupsOutput } from "#/arrange/domain/types";
3
+ /**
4
+ * Formats the suggested grouping for an inline class string as `arrange group` output lines.
5
+ *
6
+ * @since 0.3.16-canary.0
7
+ */
8
+ export declare function suggestCnGroupsFromCli(request: ArrangeSuggestGroupsRequest): ArrangeSuggestGroupsOutput;
@@ -0,0 +1,11 @@
1
+ import type { ArrangeSyncRunRequest } from "#/arrange/cli-schema";
2
+ import type { ArrangeRunResult } from "#/arrange/domain/types";
3
+ import type { AppError } from "#/core/errors";
4
+ import type { FilesystemPort } from "#/core/filesystem/port";
5
+ import type { Result } from "#/core/result";
6
+ /**
7
+ * Runs the grouping pipeline over every target file and returns the aggregated result.
8
+ *
9
+ * @since 0.3.16-canary.0
10
+ */
11
+ export declare function runArrangeSync(fs: FilesystemPort, request: ArrangeSyncRunRequest): Promise<Result<ArrangeRunResult, AppError>>;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Anti-corruption layer: maps the oxc-parser ESTree AST to the arrange domain AST.
3
+ * This is the only place that parses TypeScript source for arrange AST translation.
4
+ */
5
+ import type { DomainSourceFile } from "#/arrange/domain/ast/ast-node";
6
+ /**
7
+ * The translator mapping the `oxc-parser` ESTree AST to the arrange domain AST.
8
+ *
9
+ * @since 0.3.16-canary.0
10
+ */
11
+ export declare class TypeScriptAstTranslator {
12
+ translateSourceFile(filePath: string, sourceText: string): DomainSourceFile;
13
+ /**
14
+ * Own child nodes in source-declaration order, mirroring `ts.forEachChild`:
15
+ * recurses into nested node objects and arrays of nodes, skipping primitives
16
+ * and plain records (e.g. a template element's `value`) that carry no `type`.
17
+ */
18
+ private childNodesOf;
19
+ private translateUnknown;
20
+ private stringLiteralText;
21
+ private noSubstitutionTemplateText;
22
+ private mapBinaryOperator;
23
+ private translateImportDeclaration;
24
+ private buildImportClause;
25
+ private buildNamespaceImport;
26
+ private buildNamedImports;
27
+ private buildImportSpecifier;
28
+ private translateIdentifier;
29
+ private translateNode;
30
+ private parseDomainSourceFile;
31
+ }
@@ -0,0 +1,13 @@
1
+ import type { ArrangeTargetWorkspaceAndConfig } from "#/arrange/domain/types";
2
+ import { AppError } from "#/core/errors";
3
+ import type { FilesystemPort } from "#/core/filesystem/port";
4
+ import type { Result } from "#/core/result";
5
+ /**
6
+ * Resolves the arrange target, repo root, and loaded config an arrange run needs.
7
+ *
8
+ * @since 0.3.16-canary.0
9
+ */
10
+ export declare function prepareArrangeWorkspace(fs: FilesystemPort, args: {
11
+ readonly currentWorkingDirectory: string;
12
+ readonly rawTarget: string | undefined;
13
+ }): Promise<Result<ArrangeTargetWorkspaceAndConfig, AppError>>;
@@ -3,7 +3,7 @@ import { loadCodefastConfig } from "#/core/config";
3
3
  import { AppError } from "#/core/errors";
4
4
  import { messageFrom } from "#/core/errors";
5
5
  import { err, ok } from "#/core/result";
6
- import { findRepoRoot } from "#/core/workspace/resolver";
6
+ import { resolveProjectRoot } from "#/core/workspace/resolver";
7
7
  /**
8
8
  * Resolves the arrange target, repo root, and loaded config an arrange run needs.
9
9
  *
@@ -19,7 +19,7 @@ export async function prepareArrangeWorkspace(fs, args) {
19
19
  }
20
20
  let rootDir;
21
21
  try {
22
- rootDir = findRepoRoot(args.currentWorkingDirectory, fs);
22
+ rootDir = resolveProjectRoot(args.currentWorkingDirectory, fs).rootDir;
23
23
  }
24
24
  catch (caughtError) {
25
25
  return err(new AppError("INFRA_FAILURE", messageFrom(caughtError), caughtError));
@@ -0,0 +1,93 @@
1
+ import { 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>;
19
+ /**
20
+ * Resolved request for a single link audit run.
21
+ *
22
+ * @since 0.5.0
23
+ */
24
+ export type LinkAuditRunRequest = {
25
+ readonly rootDir: string;
26
+ readonly targetPath: string;
27
+ readonly allowlist?: ReadonlyArray<string> | undefined;
28
+ readonly json: boolean;
29
+ };
30
+ /**
31
+ * Zod schema for {@link LinkAuditRunRequest}.
32
+ *
33
+ * @since 0.5.0
34
+ */
35
+ export declare const linkAuditRunRequestSchema: z.ZodType<LinkAuditRunRequest>;
36
+ /**
37
+ * Resolved request for a single comment-divider audit run.
38
+ *
39
+ * @since 0.6.0
40
+ */
41
+ export type CommentAuditRunRequest = {
42
+ readonly rootDir: string;
43
+ readonly targetPath: string;
44
+ readonly allowlist?: ReadonlyArray<string> | undefined;
45
+ readonly fix: boolean;
46
+ readonly json: boolean;
47
+ };
48
+ /**
49
+ * Zod schema for {@link CommentAuditRunRequest}.
50
+ *
51
+ * @since 0.6.0
52
+ */
53
+ export declare const commentAuditRunRequestSchema: z.ZodType<CommentAuditRunRequest>;
54
+ /**
55
+ * Resolved request for a single import-policy audit run.
56
+ *
57
+ * @since 0.10.0
58
+ */
59
+ export type ImportsAuditRunRequest = {
60
+ readonly rootDir: string;
61
+ readonly targetPath: string;
62
+ readonly allowlist?: ReadonlyArray<string> | undefined;
63
+ readonly json: boolean;
64
+ };
65
+ /**
66
+ * Zod schema for {@link ImportsAuditRunRequest}.
67
+ *
68
+ * @since 0.10.0
69
+ */
70
+ export declare const importsAuditRunRequestSchema: z.ZodType<ImportsAuditRunRequest>;
71
+ /**
72
+ * Resolved request for a single display-name audit run.
73
+ *
74
+ * @since 0.9.0
75
+ */
76
+ export type DisplayNameAuditRunRequest = {
77
+ readonly rootDir: string;
78
+ readonly targetPath: string;
79
+ readonly allowlist?: ReadonlyArray<string> | undefined;
80
+ readonly json: boolean;
81
+ };
82
+ /**
83
+ * Zod schema for {@link DisplayNameAuditRunRequest}.
84
+ *
85
+ * @since 0.9.0
86
+ */
87
+ export declare const displayNameAuditRunRequestSchema: z.ZodType<DisplayNameAuditRunRequest>;
88
+ /**
89
+ * Resolves a path that may be absolute or relative to `rootDir`.
90
+ *
91
+ * @since 0.5.0-canary.6
92
+ */
93
+ export declare function resolveRepoRelativePath(rootDir: string, maybeRelative: string): string;
@@ -35,11 +35,11 @@ export const commentAuditRunRequestSchema = z.object({
35
35
  json: z.boolean(),
36
36
  });
37
37
  /**
38
- * Zod schema for {@link ReactAuditRunRequest}.
38
+ * Zod schema for {@link ImportsAuditRunRequest}.
39
39
  *
40
- * @since 0.8.0
40
+ * @since 0.10.0
41
41
  */
42
- export const reactAuditRunRequestSchema = z.object({
42
+ export const importsAuditRunRequestSchema = z.object({
43
43
  rootDir: z.string().min(1),
44
44
  targetPath: z.string().min(1),
45
45
  allowlist: z.array(z.string()).optional(),
@@ -0,0 +1,8 @@
1
+ import { Command } from "commander";
2
+ /**
3
+ * Top-level `audit` command — the source scans. Every one of them reports by default; only
4
+ * `comments --fix` writes, and only where the rewrite discards nothing a person wrote.
5
+ *
6
+ * @since 0.5.0-canary.6
7
+ */
8
+ export declare function createAuditCommand(): Command;
@@ -1,13 +1,13 @@
1
1
  import process from "node:process";
2
2
  import { Command } from "commander";
3
- import { commentAuditRunRequestSchema, linkAuditRunRequestSchema, reactAuditRunRequestSchema, rtlAuditRunRequestSchema, displayNameAuditRunRequestSchema, } from "#/audit/cli-schema";
4
- import { exitCodeForCommentAuditResult, exitCodeForLinkAuditResult, exitCodeForReactAuditResult, exitCodeForRtlAuditResult, exitCodeForDisplayNameAuditResult, formatCommentAuditJsonOutput, formatLinkAuditJsonOutput, formatReactAuditJsonOutput, formatRtlAuditJsonOutput, formatDisplayNameAuditJsonOutput, presentCommentAuditResult, presentLinkAuditResult, presentReactAuditResult, presentRtlAuditResult, presentDisplayNameAuditResult, } from "#/audit/output";
5
- import { prepareCommentAudit, prepareLinkAudit, prepareReactAudit, prepareRtlAudit, prepareDisplayNameAudit, } from "#/audit/prepare";
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
6
  import { runRtlAudit } from "#/audit/run";
7
7
  import { runCommentAudit } from "#/audit/run-comments";
8
8
  import { runDisplayNameAudit } from "#/audit/run-display-names";
9
+ import { runImportsAudit } from "#/audit/run-imports";
9
10
  import { runLinkAudit } from "#/audit/run-links";
10
- import { runReactAudit } from "#/audit/run-react";
11
11
  import { readOptionalPositionalArg } from "#/core/cli/positional";
12
12
  import { consumeCliAppError } from "#/core/cli/result-handle";
13
13
  import { nodeFilesystem } from "#/core/filesystem/node";
@@ -100,12 +100,12 @@ export function createAuditCommand() {
100
100
  process.exitCode = exitCodeForLinkAuditResult(outcome.value);
101
101
  });
102
102
  cmd
103
- .command("react")
104
- .description("Report React namespace/default imports and implicit React.* UMD-global type references")
103
+ .command("imports")
104
+ .description("Report banned import forms (React by-name, Zod namespace in front-end packages, …)")
105
105
  .argument("[target]", "Directory or file to scan (default: the repo root)")
106
106
  .option("--json", "Print one JSON summary on stdout", false)
107
107
  .action(async (target, opts) => {
108
- const prelude = await prepareReactAudit(nodeFilesystem, {
108
+ const prelude = await prepareImportsAudit(nodeFilesystem, {
109
109
  currentWorkingDirectory: process.cwd(),
110
110
  rawTarget: readOptionalPositionalArg(target),
111
111
  });
@@ -113,7 +113,7 @@ export function createAuditCommand() {
113
113
  return;
114
114
  }
115
115
  const { rootDir, targetPath, allowlist } = prelude.value;
116
- const parsed = parseWithSchema(reactAuditRunRequestSchema, {
116
+ const parsed = parseWithSchema(importsAuditRunRequestSchema, {
117
117
  rootDir,
118
118
  targetPath,
119
119
  allowlist,
@@ -122,7 +122,7 @@ export function createAuditCommand() {
122
122
  if (!consumeCliAppError(parsed)) {
123
123
  return;
124
124
  }
125
- const outcome = runReactAudit(nodeFilesystem, {
125
+ const outcome = runImportsAudit(nodeFilesystem, {
126
126
  rootDir: parsed.value.rootDir,
127
127
  targetPath: parsed.value.targetPath,
128
128
  allowlist: parsed.value.allowlist ?? [],
@@ -131,12 +131,12 @@ export function createAuditCommand() {
131
131
  return;
132
132
  }
133
133
  if (parsed.value.json) {
134
- logger.out(formatReactAuditJsonOutput(outcome.value, rootDir));
134
+ logger.out(formatImportsAuditJsonOutput(outcome.value, rootDir));
135
135
  }
136
136
  else {
137
- presentReactAuditResult(outcome.value);
137
+ presentImportsAuditResult(outcome.value);
138
138
  }
139
- process.exitCode = exitCodeForReactAuditResult(outcome.value);
139
+ process.exitCode = exitCodeForImportsAuditResult(outcome.value);
140
140
  });
141
141
  cmd
142
142
  .command("display-names")
@@ -0,0 +1,7 @@
1
+ import type { RtlViolation } from "#/audit/domain/types";
2
+ /**
3
+ * Detects physical-direction Tailwind classes that should be logical or rtl:-paired.
4
+ *
5
+ * @since 0.5.0-canary.6
6
+ */
7
+ export declare function auditFileContent(content: string): Array<RtlViolation>;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Recognises comment content the repo bans outright: repo-document pointers and JSDoc type syntax.
3
+ */
4
+ /**
5
+ * Why a comment's content fails the convention. None is mechanical — the writer restates the
6
+ * invariant, lets the type carry the type, or moves the tag.
7
+ *
8
+ * @since 0.6.0
9
+ */
10
+ export type CommentContentDefectKind = "detached-doc" | "doc-pointer" | "jsdoc-type" | "param-coverage" | "param-hyphen" | "since-order" | "stacked-doc";
11
+ /**
12
+ * One banned fragment found inside a comment.
13
+ *
14
+ * @since 0.6.0
15
+ */
16
+ export interface CommentContentFinding {
17
+ readonly line: number;
18
+ readonly raw: string;
19
+ readonly defect: CommentContentDefectKind;
20
+ }
21
+ /**
22
+ * Scans a source file's comments for banned content, in source order.
23
+ *
24
+ * @since 0.6.0
25
+ */
26
+ export declare function scanCommentContent(content: string, language: "css" | "js"): Array<CommentContentFinding>;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Recognises section dividers in source text and renders them in the one form the repo allows.
3
+ */
4
+ /**
5
+ * The column every divider's rule ends at — `oxfmt`'s `printWidth`, indentation included.
6
+ *
7
+ * @since 0.6.0
8
+ */
9
+ export declare const DIVIDER_COLUMN = 120;
10
+ /**
11
+ * Which comment syntax a divider is written in.
12
+ *
13
+ * @since 0.6.0
14
+ */
15
+ export type DividerLanguage = "css" | "ignore" | "js";
16
+ /**
17
+ * Why a divider fails the convention. Both kinds are mechanical, so every report is one `--fix` away.
18
+ *
19
+ * @since 0.6.0
20
+ */
21
+ export type DividerDefectKind = "bad-width" | "legacy-form";
22
+ /**
23
+ * One divider found in a file, with the verdict on its form.
24
+ *
25
+ * @since 0.6.0
26
+ */
27
+ export interface DividerRegion {
28
+ readonly startLine: number;
29
+ readonly endLine: number;
30
+ readonly indent: string;
31
+ readonly title: string;
32
+ readonly raw: string;
33
+ readonly defect: DividerDefectKind | null;
34
+ }
35
+ /**
36
+ * Renders a divider in the canonical form.
37
+ *
38
+ * @param indent - leading whitespace copied from the site, counted toward the column
39
+ * @param title - the section name, already trimmed
40
+ * @param language - which comment syntax to draw the divider in
41
+ *
42
+ * @since 0.6.0
43
+ */
44
+ export declare function renderDivider(indent: string, title: string, language: DividerLanguage): string;
45
+ /**
46
+ * Every section divider in a file, canonical ones included, in source order.
47
+ *
48
+ * @remarks A rule-framed comment that carries prose is a doc block, not a divider, and never
49
+ * appears here — the two are different things and only one of them has a fixed form.
50
+ *
51
+ * @since 0.6.0
52
+ */
53
+ export declare function scanCommentDividers(content: string, language: DividerLanguage): Array<DividerRegion>;
54
+ /**
55
+ * Rewrites every off-convention divider into the canonical form.
56
+ *
57
+ * @since 0.6.0
58
+ */
59
+ export declare function applyCommentDividerFixes(content: string, language: DividerLanguage): {
60
+ readonly content: string;
61
+ readonly fixedCount: number;
62
+ };
@@ -0,0 +1,11 @@
1
+ /** The display-name convention: a name is spelled like the TS symbol it stands for, under its owner's namespace. */
2
+ import type { DisplayNameViolation } from "#/audit/domain/types";
3
+ /**
4
+ * Scans one source or markdown text for `token()`, `tag()` and module display names that break the convention.
5
+ *
6
+ * @remarks Runs on markdown as well as TypeScript because a doc sample is what a reader copies:
7
+ * a convention the docs break is not one the docs teach.
8
+ *
9
+ * @since 0.9.0
10
+ */
11
+ export declare function auditDisplayNames(sourceText: string): Array<DisplayNameViolation>;
@@ -0,0 +1,34 @@
1
+ import type { ImportPolicyViolation } from "#/audit/domain/types";
2
+ /**
3
+ * One library's import policy: which forms of importing `module` are banned, optionally limited to
4
+ * files whose repo-relative path matches `scope`, plus an optional UMD-global name to flag when the
5
+ * file references `<name>.*` without importing it.
6
+ *
7
+ * @since 0.10.0
8
+ */
9
+ export interface ImportPolicyRule {
10
+ readonly module: string;
11
+ /** Banned forms: `namespace` (`import * as x`), `default` (`import x`), or `named:<name>`. */
12
+ readonly ban: ReadonlyArray<"namespace" | "default" | `named:${string}`>;
13
+ readonly scope?: ReadonlyArray<string> | undefined;
14
+ readonly umdGlobal?: string | undefined;
15
+ readonly message: string;
16
+ }
17
+ /**
18
+ * The import policies enforced across the monorepo: React members by name (never a namespace,
19
+ * default, or implicit `React.*` UMD global), and Zod as a namespace in front-end packages so
20
+ * bundlers can tree-shake it (a named `import { z }` pins Zod's full locale set into the bundle).
21
+ *
22
+ * @since 0.10.0
23
+ */
24
+ export declare const defaultImportPolicyRules: ReadonlyArray<ImportPolicyRule>;
25
+ /**
26
+ * Scans one TypeScript source against the given import-policy rules and returns the violations.
27
+ *
28
+ * @remarks Each rule matches import declarations from its `module` and flags the banned forms;
29
+ * a rule with `umdGlobal` additionally flags implicit `<name>.*` type references when nothing in
30
+ * the file imports that name (the case tsc accepts silently through a UMD `export as namespace`).
31
+ *
32
+ * @since 0.10.0
33
+ */
34
+ export declare function auditImportPolicySource(filePath: string, sourceText: string, rules: ReadonlyArray<ImportPolicyRule>): Array<ImportPolicyViolation>;
@@ -0,0 +1,147 @@
1
+ import { parseSync } from "oxc-parser";
2
+ /**
3
+ * The import policies enforced across the monorepo: React members by name (never a namespace,
4
+ * default, or implicit `React.*` UMD global), and Zod as a namespace in front-end packages so
5
+ * bundlers can tree-shake it (a named `import { z }` pins Zod's full locale set into the bundle).
6
+ *
7
+ * @since 0.10.0
8
+ */
9
+ export const defaultImportPolicyRules = [
10
+ {
11
+ module: "react",
12
+ ban: ["namespace", "default"],
13
+ umdGlobal: "React",
14
+ message: 'import React members by name from "react"',
15
+ },
16
+ {
17
+ module: "zod",
18
+ ban: ["named:z"],
19
+ scope: [
20
+ "packages/theme/**",
21
+ "packages/ui/**",
22
+ "packages/tailwind-variants/**",
23
+ "apps/web/**",
24
+ "examples/*/**",
25
+ "internal/benchmark-viewer/**",
26
+ ],
27
+ message: 'import Zod as a namespace so bundlers can tree-shake it: import * as z from "zod"',
28
+ },
29
+ ];
30
+ function isOxcNode(value) {
31
+ return typeof value === "object" && value !== null && typeof value.type === "string";
32
+ }
33
+ function isIdentifierNamed(node, name) {
34
+ return isOxcNode(node) && node.type === "Identifier" && node.name === name;
35
+ }
36
+ function importedName(specifier) {
37
+ const imported = specifier.imported;
38
+ return isOxcNode(imported) && typeof imported.name === "string" ? imported.name : undefined;
39
+ }
40
+ /**
41
+ * Scans one TypeScript source against the given import-policy rules and returns the violations.
42
+ *
43
+ * @remarks Each rule matches import declarations from its `module` and flags the banned forms;
44
+ * a rule with `umdGlobal` additionally flags implicit `<name>.*` type references when nothing in
45
+ * the file imports that name (the case tsc accepts silently through a UMD `export as namespace`).
46
+ *
47
+ * @since 0.10.0
48
+ */
49
+ export function auditImportPolicySource(filePath, sourceText, rules) {
50
+ const { program } = parseSync(filePath, sourceText);
51
+ const statements = program.body;
52
+ const violations = [];
53
+ const boundUmdNames = new Set();
54
+ for (const rule of rules) {
55
+ const bannedNamed = new Set();
56
+ let banNamespace = false;
57
+ let banDefault = false;
58
+ for (const form of rule.ban) {
59
+ if (form === "namespace") {
60
+ banNamespace = true;
61
+ }
62
+ else if (form === "default") {
63
+ banDefault = true;
64
+ }
65
+ else {
66
+ bannedNamed.add(form.slice("named:".length));
67
+ }
68
+ }
69
+ for (const statement of statements) {
70
+ if (statement.type !== "ImportDeclaration") {
71
+ continue;
72
+ }
73
+ const source = statement.source;
74
+ if (!isOxcNode(source) || source.value !== rule.module) {
75
+ continue;
76
+ }
77
+ const specifiers = Array.isArray(statement.specifiers) ? statement.specifiers.filter(isOxcNode) : [];
78
+ const umdGlobal = rule.umdGlobal;
79
+ if (umdGlobal !== undefined && specifiers.some((specifier) => isIdentifierNamed(specifier.local, umdGlobal))) {
80
+ boundUmdNames.add(umdGlobal);
81
+ }
82
+ for (const specifier of specifiers) {
83
+ if (banNamespace && specifier.type === "ImportNamespaceSpecifier") {
84
+ violations.push(violationAt(sourceText, statement, `namespace import of "${rule.module}" — ${rule.message}`));
85
+ }
86
+ else if (banDefault && specifier.type === "ImportDefaultSpecifier") {
87
+ violations.push(violationAt(sourceText, statement, `default import of "${rule.module}" — ${rule.message}`));
88
+ }
89
+ else if (specifier.type === "ImportSpecifier") {
90
+ const name = importedName(specifier);
91
+ if (name !== undefined && bannedNamed.has(name)) {
92
+ violations.push(violationAt(sourceText, statement, `named import { ${name} } from "${rule.module}" — ${rule.message}`));
93
+ }
94
+ }
95
+ }
96
+ }
97
+ }
98
+ for (const rule of rules) {
99
+ if (rule.umdGlobal !== undefined && !boundUmdNames.has(rule.umdGlobal)) {
100
+ collectUmdGlobalReferences(program, sourceText, rule, violations);
101
+ }
102
+ }
103
+ violations.sort((a, b) => a.line - b.line);
104
+ return violations;
105
+ }
106
+ function violationAt(sourceText, statement, reason) {
107
+ return {
108
+ line: lineOfOffset(sourceText, statement.start),
109
+ raw: firstLineOf(sourceText.slice(statement.start, statement.end)),
110
+ reason,
111
+ };
112
+ }
113
+ function collectUmdGlobalReferences(node, sourceText, rule, violations) {
114
+ if (node.type === "TSQualifiedName" && isIdentifierNamed(node.left, rule.umdGlobal ?? "")) {
115
+ violations.push({
116
+ line: lineOfOffset(sourceText, node.start),
117
+ raw: sourceText.slice(node.start, node.end),
118
+ reason: `implicit ${rule.umdGlobal}.* UMD global — ${rule.message}`,
119
+ });
120
+ return;
121
+ }
122
+ for (const value of Object.values(node)) {
123
+ if (Array.isArray(value)) {
124
+ for (const item of value) {
125
+ if (isOxcNode(item)) {
126
+ collectUmdGlobalReferences(item, sourceText, rule, violations);
127
+ }
128
+ }
129
+ }
130
+ else if (isOxcNode(value)) {
131
+ collectUmdGlobalReferences(value, sourceText, rule, violations);
132
+ }
133
+ }
134
+ }
135
+ function lineOfOffset(sourceText, offset) {
136
+ let line = 1;
137
+ for (let index = 0; index < offset; index++) {
138
+ if (sourceText.charCodeAt(index) === 10) {
139
+ line++;
140
+ }
141
+ }
142
+ return line;
143
+ }
144
+ function firstLineOf(text) {
145
+ const newlineIndex = text.indexOf("\n");
146
+ return newlineIndex === -1 ? text : text.slice(0, newlineIndex);
147
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Resolves `{@link}` references against the scanned tree, so a rename that orphans one is caught.
3
+ */
4
+ /**
5
+ * One `{@link}` occurrence found inside a comment.
6
+ *
7
+ * @since 0.6.0
8
+ */
9
+ export interface LinkReference {
10
+ readonly line: number;
11
+ /** The target as written, e.g. `writeJsonlRun`, `Foo.bar`, `../fixtures/adapter.ts`. */
12
+ readonly target: string;
13
+ }
14
+ /**
15
+ * Every `{@link}` target in a file's comments, in source order.
16
+ *
17
+ * @since 0.6.0
18
+ */
19
+ export declare function scanLinkReferences(content: string): Array<LinkReference>;
20
+ /**
21
+ * The identifier a declaration-style target must resolve through — `Foo.bar` resolves via `Foo`.
22
+ *
23
+ * @since 0.6.0
24
+ */
25
+ export declare function linkTargetHead(target: string): string;
26
+ /**
27
+ * Whether a target names a file or URL rather than a declaration.
28
+ *
29
+ * @since 0.6.0
30
+ */
31
+ export declare function isPathLinkTarget(target: string): boolean;
32
+ /**
33
+ * Counts word-boundary occurrences of each head across a body of source text.
34
+ *
35
+ * @remarks A `{@link X}` occurrence itself mentions `X` once, so a target is orphaned when its
36
+ * total mentions do not exceed its link occurrences — a rename removes every real mention.
37
+ *
38
+ * @since 0.6.0
39
+ */
40
+ export declare function countHeadMentions(contents: Iterable<string>, heads: ReadonlySet<string>): Map<string, number>;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Physical → logical replacements. Order matters: negative before positive,
3
+ * specific corners before general edges, with-value before bare.
4
+ *
5
+ * @since 0.5.0-canary.6
6
+ */
7
+ export declare const RTL_MAPPINGS: ReadonlyArray<readonly [string, string]>;
8
+ /**
9
+ * translate-x has no logical equivalent — it needs an rtl:-negated twin.
10
+ *
11
+ * @since 0.5.0-canary.6
12
+ */
13
+ export declare const RTL_TRANSLATE_X_MAPPINGS: ReadonlyArray<readonly [string, string]>;
14
+ /**
15
+ * Classes that need an rtl:*-reverse companion.
16
+ *
17
+ * @since 0.5.0-canary.6
18
+ */
19
+ export declare const RTL_REVERSE_MAPPINGS: ReadonlyArray<readonly [string, string]>;
20
+ /**
21
+ * Classes that need an rtl: companion with the swapped value.
22
+ *
23
+ * @since 0.5.0-canary.6
24
+ */
25
+ export declare const RTL_SWAP_MAPPINGS: ReadonlyArray<readonly [string, string]>;
26
+ /**
27
+ * Anything anchored to a physical side variant stays physical: Radix resolves
28
+ * `side` per direction, and a border/position/slide tied to that side must follow it.
29
+ *
30
+ * @since 0.5.0-canary.6
31
+ */
32
+ export declare const PHYSICAL_SIDE_VARIANT: RegExp;
33
+ /**
34
+ * Slide animations under direction-resolved contexts are correct as-is:
35
+ * Radix flips `side`/`motion` values itself under DirectionProvider.
36
+ *
37
+ * @since 0.5.0-canary.6
38
+ */
39
+ export declare const DIRECTION_RESOLVED_VARIANT: RegExp;
40
+ /**
41
+ * The physical slide-animation class prefixes the RTL audit inspects.
42
+ *
43
+ * @since 0.5.0-canary.6
44
+ */
45
+ export declare const SLIDE_PREFIXES: readonly ["slide-in-from-left", "slide-in-from-right", "slide-out-to-left", "slide-out-to-right"];