@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.
- package/CHANGELOG.md +28 -0
- package/README.md +329 -149
- package/dist/arrange/analyze.d.ts +10 -0
- package/dist/arrange/cli-schema.d.ts +50 -0
- package/dist/arrange/command.d.ts +7 -0
- package/dist/arrange/domain/analyze-service.d.ts +18 -0
- package/dist/arrange/domain/ast/ast-node.d.ts +394 -0
- package/dist/arrange/domain/ast/collectors-cn.d.ts +26 -0
- package/dist/arrange/domain/ast/collectors-jsx.d.ts +8 -0
- package/dist/arrange/domain/ast/collectors-tv.d.ts +34 -0
- package/dist/arrange/domain/ast/helpers.d.ts +36 -0
- package/dist/arrange/domain/ast/helpers.js +1 -0
- package/dist/arrange/domain/ast/simplify-targets.d.ts +22 -0
- package/dist/arrange/domain/ast/targets.d.ts +20 -0
- package/dist/arrange/domain/constants.d.ts +111 -0
- package/dist/arrange/domain/grouping-service.d.ts +100 -0
- package/dist/arrange/domain/grouping.d.ts +21 -0
- package/dist/arrange/domain/imports.d.ts +14 -0
- package/dist/arrange/domain/source-text-formatters.d.ts +33 -0
- package/dist/arrange/domain/tailwind-token.d.ts +24 -0
- package/dist/arrange/domain/token-classifier.d.ts +47 -0
- package/dist/arrange/domain/types.d.ts +208 -0
- package/dist/arrange/output.d.ts +26 -0
- package/dist/arrange/process-file.d.ts +11 -0
- package/dist/arrange/resolve-target.d.ts +10 -0
- package/dist/arrange/resolve-target.js +3 -16
- package/dist/arrange/scan-target.d.ts +7 -0
- package/dist/arrange/simplify-process-file.d.ts +11 -0
- package/dist/arrange/simplify-sync.d.ts +13 -0
- package/dist/arrange/source-parse.d.ts +7 -0
- package/dist/arrange/suggest.d.ts +8 -0
- package/dist/arrange/sync.d.ts +11 -0
- package/dist/arrange/typescript-ast-translator.d.ts +31 -0
- package/dist/arrange/workspace.d.ts +13 -0
- package/dist/arrange/workspace.js +2 -2
- package/dist/audit/cli-schema.d.ts +93 -0
- package/dist/audit/cli-schema.js +3 -3
- package/dist/audit/command.d.ts +8 -0
- package/dist/audit/command.js +12 -12
- package/dist/audit/domain/audit-file.d.ts +7 -0
- package/dist/audit/domain/comment-content.d.ts +26 -0
- package/dist/audit/domain/comment-dividers.d.ts +62 -0
- package/dist/audit/domain/display-names.d.ts +11 -0
- package/dist/audit/domain/import-policy.d.ts +34 -0
- package/dist/audit/domain/import-policy.js +147 -0
- package/dist/audit/domain/link-references.d.ts +40 -0
- package/dist/audit/domain/mappings.d.ts +45 -0
- package/dist/audit/domain/markdown-links.d.ts +44 -0
- package/dist/audit/domain/since-versions.d.ts +26 -0
- package/dist/audit/domain/tokenize.d.ts +14 -0
- package/dist/audit/domain/tsdoc-syntax.d.ts +20 -0
- package/dist/audit/domain/types.d.ts +171 -0
- package/dist/audit/output.d.ts +91 -0
- package/dist/audit/output.js +11 -11
- package/dist/audit/prepare.d.ts +70 -0
- package/dist/audit/prepare.js +11 -11
- package/dist/audit/run-comments.d.ts +17 -0
- package/dist/audit/run-display-names.d.ts +14 -0
- package/dist/audit/run-imports.d.ts +14 -0
- package/dist/audit/{run-react.js → run-imports.js} +15 -5
- package/dist/audit/run-links.d.ts +14 -0
- package/dist/audit/run.d.ts +14 -0
- package/dist/bin.d.ts +2 -0
- package/dist/cli.d.ts +6 -0
- package/dist/core/cli/format-error.d.ts +7 -0
- package/dist/core/cli/global-options.d.ts +15 -0
- package/dist/core/cli/positional.d.ts +6 -0
- package/dist/core/cli/result-handle.d.ts +19 -0
- package/dist/core/config/define-config.d.ts +7 -0
- package/dist/core/config/define-config.js +8 -0
- package/dist/core/config/loader.d.ts +18 -0
- package/dist/core/config/loader.js +2 -7
- package/dist/core/config/schema.d.ts +99 -0
- package/dist/core/config/schema.js +7 -85
- package/dist/core/config/warnings.d.ts +6 -0
- package/dist/core/config.d.ts +12 -0
- package/dist/core/errors.d.ts +25 -0
- package/dist/core/exit-codes.d.ts +18 -0
- package/dist/core/filesystem/node.d.ts +7 -0
- package/dist/core/filesystem/node.js +1 -0
- package/dist/core/filesystem/port.d.ts +44 -0
- package/dist/core/glob.d.ts +19 -0
- package/dist/core/logger.d.ts +9 -0
- package/dist/core/result.d.ts +30 -0
- package/dist/core/schema-parse.d.ts +9 -0
- package/dist/core/source-text-edit.d.ts +33 -0
- package/dist/core/verbose-diagnostics.d.ts +6 -0
- package/dist/core/workspace/ancestor-directories.d.ts +12 -0
- package/dist/core/workspace/ancestor-directories.js +30 -0
- package/dist/core/workspace/markdown-walk.d.ts +7 -0
- package/dist/core/workspace/markdown-walk.js +2 -20
- package/dist/core/workspace/package-version.d.ts +9 -0
- package/dist/core/workspace/package-version.js +8 -12
- package/dist/core/workspace/resolver.d.ts +39 -0
- package/dist/core/workspace/resolver.js +58 -75
- package/dist/core/workspace/skip-directories.d.ts +6 -0
- package/dist/core/workspace/source-walk.d.ts +16 -0
- package/dist/core/workspace/source-walk.js +2 -19
- package/dist/core/workspace/typescript-walk.d.ts +7 -0
- package/dist/core/workspace/typescript-walk.js +2 -23
- package/dist/core/workspace/walk-files.d.ts +7 -0
- package/dist/core/workspace/walk-files.js +27 -0
- package/dist/core/workspace/well-known-files.d.ts +18 -0
- package/dist/core/workspace/well-known-files.js +18 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +5 -0
- package/dist/mirror/cli-result.d.ts +13 -0
- package/dist/mirror/cli-schema.d.ts +8 -0
- package/dist/mirror/command.d.ts +7 -0
- package/dist/mirror/dist-filesystem-impl.d.ts +8 -0
- package/dist/mirror/domain/constants.d.ts +18 -0
- package/dist/mirror/domain/constants.js +0 -12
- package/dist/mirror/domain/dirent-guard.d.ts +10 -0
- package/dist/mirror/domain/dist-filesystem.d.ts +9 -0
- package/dist/mirror/domain/errors.d.ts +24 -0
- package/dist/mirror/domain/exports.d.ts +36 -0
- package/dist/mirror/domain/package-display-name.d.ts +8 -0
- package/dist/mirror/domain/path-normalizer.d.ts +6 -0
- package/dist/mirror/domain/types.d.ts +131 -0
- package/dist/mirror/output.d.ts +23 -0
- package/dist/mirror/package-path.d.ts +19 -0
- package/dist/mirror/prepare.d.ts +15 -0
- package/dist/mirror/prepare.js +2 -2
- package/dist/mirror/supplement-exports.d.ts +27 -0
- package/dist/mirror/supplement-exports.js +2 -2
- package/dist/mirror/sync-reporter.d.ts +60 -0
- package/dist/mirror/sync-reporter.js +4 -0
- package/dist/mirror/sync-types.d.ts +43 -0
- package/dist/mirror/sync-workspace-package.d.ts +9 -0
- package/dist/mirror/sync-workspace-package.js +3 -3
- package/dist/mirror/sync.d.ts +12 -0
- package/dist/mirror/sync.js +5 -3
- package/dist/mirror/write-exports.d.ts +15 -0
- package/dist/pack-slim/cli-result.d.ts +13 -0
- package/dist/pack-slim/cli-schema.d.ts +17 -0
- package/dist/pack-slim/command.d.ts +7 -0
- package/dist/pack-slim/command.js +2 -2
- package/dist/pack-slim/domain/transform.d.ts +69 -0
- package/dist/pack-slim/domain/types.d.ts +46 -0
- package/dist/pack-slim/output.d.ts +15 -0
- package/dist/pack-slim/sync.d.ts +23 -0
- package/dist/pack-slim/sync.js +4 -4
- package/dist/pack-slim/working-tree.d.ts +20 -0
- package/dist/tag/cli-result.d.ts +7 -0
- package/dist/tag/cli-schema.d.ts +8 -0
- package/dist/tag/command.d.ts +7 -0
- package/dist/tag/domain/types.d.ts +111 -0
- package/dist/tag/output.d.ts +17 -0
- package/dist/tag/prepare.d.ts +13 -0
- package/dist/tag/prepare.js +2 -2
- package/dist/tag/resolve-target-path.d.ts +10 -0
- package/dist/tag/since-writer.d.ts +32 -0
- package/dist/tag/sync.d.ts +42 -0
- package/dist/tag/target-candidates.d.ts +8 -0
- package/dist/tag/target-candidates.js +1 -1
- package/dist/tag/target-runner.d.ts +8 -0
- package/dist/tag/version-resolver.d.ts +7 -0
- package/package.json +12 -1
- 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 {
|
|
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 =
|
|
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;
|
package/dist/audit/cli-schema.js
CHANGED
|
@@ -35,11 +35,11 @@ export const commentAuditRunRequestSchema = z.object({
|
|
|
35
35
|
json: z.boolean(),
|
|
36
36
|
});
|
|
37
37
|
/**
|
|
38
|
-
* Zod schema for {@link
|
|
38
|
+
* Zod schema for {@link ImportsAuditRunRequest}.
|
|
39
39
|
*
|
|
40
|
-
* @since 0.
|
|
40
|
+
* @since 0.10.0
|
|
41
41
|
*/
|
|
42
|
-
export const
|
|
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;
|
package/dist/audit/command.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import process from "node:process";
|
|
2
2
|
import { Command } from "commander";
|
|
3
|
-
import { commentAuditRunRequestSchema, linkAuditRunRequestSchema,
|
|
4
|
-
import { exitCodeForCommentAuditResult, exitCodeForLinkAuditResult,
|
|
5
|
-
import { prepareCommentAudit, prepareLinkAudit,
|
|
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("
|
|
104
|
-
.description("Report React
|
|
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
|
|
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(
|
|
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 =
|
|
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(
|
|
134
|
+
logger.out(formatImportsAuditJsonOutput(outcome.value, rootDir));
|
|
135
135
|
}
|
|
136
136
|
else {
|
|
137
|
-
|
|
137
|
+
presentImportsAuditResult(outcome.value);
|
|
138
138
|
}
|
|
139
|
-
process.exitCode =
|
|
139
|
+
process.exitCode = exitCodeForImportsAuditResult(outcome.value);
|
|
140
140
|
});
|
|
141
141
|
cmd
|
|
142
142
|
.command("display-names")
|
|
@@ -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"];
|