@codefast/cli 0.11.0 → 0.13.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 +87 -0
- package/README.md +152 -56
- package/dist/arrange/command.js +24 -25
- package/dist/arrange/domain/ast/collectors-cn.d.ts +2 -2
- package/dist/arrange/domain/ast/collectors-cn.js +2 -2
- package/dist/arrange/domain/ast/collectors-jsx.d.ts +2 -2
- package/dist/arrange/domain/ast/collectors-jsx.js +1 -1
- package/dist/arrange/domain/ast/collectors-tv.d.ts +2 -2
- package/dist/arrange/domain/ast/collectors-tv.js +5 -5
- package/dist/arrange/domain/ast/helpers.d.ts +2 -2
- package/dist/arrange/domain/ast/helpers.js +3 -4
- package/dist/arrange/domain/ast/simplify-targets.d.ts +1 -1
- package/dist/arrange/domain/ast/simplify-targets.js +5 -5
- package/dist/arrange/domain/ast/targets.d.ts +2 -2
- package/dist/arrange/domain/ast/targets.js +9 -9
- package/dist/arrange/domain/ast/translator.d.ts +1 -1
- package/dist/arrange/domain/ast/translator.js +43 -29
- package/dist/arrange/domain/constants.d.ts +1 -1
- package/dist/arrange/domain/grouping-service.d.ts +2 -2
- package/dist/arrange/domain/grouping-service.js +3 -3
- package/dist/arrange/domain/grouping.js +3 -3
- package/dist/arrange/domain/imports.d.ts +1 -1
- package/dist/arrange/domain/imports.js +2 -2
- package/dist/arrange/domain/source-text-formatters.js +1 -1
- package/dist/arrange/domain/tailwind-token.js +1 -1
- package/dist/arrange/domain/token-classifier.d.ts +1 -1
- package/dist/arrange/domain/token-classifier.js +2 -2
- package/dist/arrange/domain/types.d.ts +3 -3
- package/dist/arrange/group/cli-result.d.ts +1 -1
- package/dist/arrange/group/output.d.ts +1 -1
- package/dist/arrange/group/output.js +1 -1
- package/dist/arrange/group/suggest.d.ts +2 -2
- package/dist/arrange/group/suggest.js +2 -2
- package/dist/arrange/inspect/cli-result.d.ts +1 -1
- package/dist/arrange/inspect/domain/analyze-service.d.ts +2 -2
- package/dist/arrange/inspect/domain/analyze-service.js +7 -7
- package/dist/arrange/inspect/output.d.ts +1 -1
- package/dist/arrange/inspect/output.js +2 -2
- package/dist/arrange/inspect/run.d.ts +4 -4
- package/dist/arrange/inspect/run.js +6 -6
- package/dist/arrange/prepare.d.ts +4 -4
- package/dist/arrange/prepare.js +5 -5
- package/dist/arrange/regroup/cli-result.d.ts +1 -1
- package/dist/arrange/regroup/cli-result.js +1 -1
- package/dist/arrange/regroup/output.d.ts +2 -2
- package/dist/arrange/regroup/output.js +2 -2
- package/dist/arrange/regroup/process-file.d.ts +2 -2
- package/dist/arrange/regroup/process-file.js +3 -3
- package/dist/arrange/regroup/run.d.ts +5 -5
- package/dist/arrange/regroup/run.js +4 -4
- package/dist/arrange/resolve-target.d.ts +1 -1
- package/dist/arrange/resolve-target.js +2 -2
- package/dist/arrange/scan-target.d.ts +1 -1
- package/dist/arrange/scan-target.js +3 -3
- package/dist/arrange/simplify/cli-result.d.ts +1 -1
- package/dist/arrange/simplify/fold-targets.d.ts +3 -3
- package/dist/arrange/simplify/fold-targets.js +3 -3
- package/dist/arrange/simplify/output.d.ts +1 -1
- package/dist/arrange/simplify/output.js +1 -1
- package/dist/arrange/simplify/process-file.d.ts +4 -4
- package/dist/arrange/simplify/process-file.js +5 -5
- package/dist/arrange/simplify/run.d.ts +4 -4
- package/dist/arrange/simplify/run.js +4 -4
- package/dist/arrange/simplify/variant-classname-probe.js +1 -1
- package/dist/arrange/source-parse.d.ts +1 -1
- package/dist/arrange/source-parse.js +3 -3
- package/dist/audit/assertions/cli-result.d.ts +13 -0
- package/dist/audit/assertions/cli-result.js +22 -0
- package/dist/audit/assertions/cli-schema.d.ts +18 -0
- package/dist/audit/assertions/cli-schema.js +12 -0
- package/dist/audit/assertions/domain/double-assertion.d.ts +20 -0
- package/dist/audit/assertions/domain/double-assertion.js +122 -0
- package/dist/audit/assertions/output.d.ts +7 -0
- package/dist/audit/assertions/output.js +21 -0
- package/dist/audit/assertions/prepare.d.ts +16 -0
- package/dist/audit/assertions/prepare.js +12 -0
- package/dist/audit/assertions/run.d.ts +14 -0
- package/dist/audit/assertions/run.js +40 -0
- package/dist/audit/command.js +89 -28
- package/dist/audit/comments/cli-result.d.ts +1 -1
- package/dist/audit/comments/cli-result.js +1 -1
- package/dist/audit/comments/output.d.ts +1 -1
- package/dist/audit/comments/output.js +1 -1
- package/dist/audit/comments/prepare.d.ts +4 -4
- package/dist/audit/comments/prepare.js +1 -1
- package/dist/audit/comments/run.d.ts +4 -4
- package/dist/audit/comments/run.js +9 -9
- package/dist/audit/constants/cli-result.d.ts +13 -0
- package/dist/audit/constants/cli-result.js +22 -0
- package/dist/audit/constants/cli-schema.d.ts +18 -0
- package/dist/audit/constants/cli-schema.js +12 -0
- package/dist/audit/constants/domain/constants.d.ts +8 -0
- package/dist/audit/constants/domain/constants.js +66 -0
- package/dist/audit/constants/output.d.ts +7 -0
- package/dist/audit/constants/output.js +21 -0
- package/dist/audit/constants/prepare.d.ts +16 -0
- package/dist/audit/constants/prepare.js +37 -0
- package/dist/audit/constants/run.d.ts +14 -0
- package/dist/audit/constants/run.js +64 -0
- package/dist/audit/display-names/cli-result.d.ts +1 -1
- package/dist/audit/display-names/cli-result.js +1 -1
- package/dist/audit/display-names/domain/display-names.d.ts +1 -1
- package/dist/audit/display-names/domain/display-names.js +1 -9
- package/dist/audit/display-names/output.d.ts +1 -1
- package/dist/audit/display-names/output.js +1 -1
- package/dist/audit/display-names/prepare.d.ts +4 -4
- package/dist/audit/display-names/prepare.js +1 -1
- package/dist/audit/display-names/run.d.ts +4 -4
- package/dist/audit/display-names/run.js +5 -5
- package/dist/audit/domain/types.d.ts +128 -0
- package/dist/audit/imports/cli-result.d.ts +1 -1
- package/dist/audit/imports/cli-result.js +1 -1
- package/dist/audit/imports/domain/import-policy.d.ts +1 -1
- package/dist/audit/imports/domain/import-policy.js +4 -18
- package/dist/audit/imports/output.d.ts +1 -1
- package/dist/audit/imports/output.js +1 -1
- package/dist/audit/imports/prepare.d.ts +4 -4
- package/dist/audit/imports/prepare.js +1 -1
- package/dist/audit/imports/run.d.ts +4 -4
- package/dist/audit/imports/run.js +6 -6
- package/dist/audit/links/cli-result.d.ts +1 -1
- package/dist/audit/links/cli-result.js +1 -1
- package/dist/audit/links/domain/markdown-links.d.ts +3 -3
- package/dist/audit/links/domain/markdown-links.js +22 -8
- package/dist/audit/links/output.d.ts +1 -1
- package/dist/audit/links/output.js +1 -1
- package/dist/audit/links/prepare.d.ts +4 -4
- package/dist/audit/links/prepare.js +1 -1
- package/dist/audit/links/run.d.ts +4 -4
- package/dist/audit/links/run.js +4 -4
- package/dist/audit/prepare.d.ts +4 -4
- package/dist/audit/prepare.js +4 -4
- package/dist/audit/publish/cli-result.d.ts +13 -0
- package/dist/audit/publish/cli-result.js +25 -0
- package/dist/audit/publish/cli-schema.d.ts +18 -0
- package/dist/audit/publish/cli-schema.js +12 -0
- package/dist/audit/publish/domain/legacy-subpath.d.ts +8 -0
- package/dist/audit/publish/domain/legacy-subpath.js +22 -0
- package/dist/audit/publish/domain/stylesheet-sources.d.ts +22 -0
- package/dist/audit/publish/domain/stylesheet-sources.js +64 -0
- package/dist/audit/publish/output.d.ts +7 -0
- package/dist/audit/publish/output.js +33 -0
- package/dist/audit/publish/prepare.d.ts +16 -0
- package/dist/audit/publish/prepare.js +12 -0
- package/dist/audit/publish/run.d.ts +15 -0
- package/dist/audit/publish/run.js +91 -0
- package/dist/audit/publish/shipped-files.d.ts +21 -0
- package/dist/audit/publish/shipped-files.js +36 -0
- package/dist/audit/rtl/cli-result.d.ts +1 -1
- package/dist/audit/rtl/cli-result.js +1 -1
- package/dist/audit/rtl/domain/audit-file.d.ts +1 -1
- package/dist/audit/rtl/domain/audit-file.js +2 -2
- package/dist/audit/rtl/domain/tokenize.d.ts +1 -1
- package/dist/audit/rtl/output.d.ts +1 -1
- package/dist/audit/rtl/output.js +1 -1
- package/dist/audit/rtl/prepare.d.ts +4 -4
- package/dist/audit/rtl/prepare.js +5 -5
- package/dist/audit/rtl/run.d.ts +4 -4
- package/dist/audit/rtl/run.js +4 -4
- package/dist/bin.js +1 -1
- package/dist/cli.js +5 -5
- package/dist/core/cli/command-pipeline.d.ts +4 -5
- package/dist/core/cli/command-pipeline.js +7 -7
- package/dist/core/cli/format-error.d.ts +1 -1
- package/dist/core/cli/resolve-root.d.ts +3 -3
- package/dist/core/cli/resolve-root.js +3 -3
- package/dist/core/cli/result-handle.d.ts +2 -2
- package/dist/core/cli/result-handle.js +4 -4
- package/dist/core/config/define-config.d.ts +1 -1
- package/dist/core/config/loader.d.ts +2 -2
- package/dist/core/config/loader.js +2 -2
- package/dist/core/config/schema.d.ts +5 -0
- package/dist/core/config/schema.js +2 -0
- package/dist/core/config/warnings.js +1 -1
- package/dist/core/config.d.ts +4 -4
- package/dist/core/config.js +4 -4
- package/dist/core/filesystem/filesystem.d.ts +3 -4
- package/dist/core/filesystem/node.d.ts +1 -1
- package/dist/core/filesystem/node.js +1 -7
- package/dist/core/oxc-node.d.ts +32 -0
- package/dist/core/oxc-node.js +25 -0
- package/dist/core/schema-parse.d.ts +2 -2
- package/dist/core/schema-parse.js +2 -2
- package/dist/core/source-position.d.ts +15 -0
- package/dist/core/source-position.js +26 -0
- package/dist/core/workspace/markdown-walk.d.ts +1 -1
- package/dist/core/workspace/markdown-walk.js +1 -1
- package/dist/core/workspace/package-version.d.ts +1 -1
- package/dist/core/workspace/package-version.js +2 -2
- package/dist/core/workspace/resolver.d.ts +1 -1
- package/dist/core/workspace/resolver.js +5 -5
- package/dist/core/workspace/source-walk.d.ts +1 -1
- package/dist/core/workspace/source-walk.js +1 -1
- package/dist/core/workspace/typescript-walk.d.ts +1 -1
- package/dist/core/workspace/typescript-walk.js +1 -1
- package/dist/core/workspace/walk-files.d.ts +1 -1
- package/dist/core/workspace/walk-files.js +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/mirror/cli-result.d.ts +1 -1
- package/dist/mirror/cli-result.js +1 -1
- package/dist/mirror/cli-schema.d.ts +1 -1
- package/dist/mirror/command.js +7 -7
- package/dist/mirror/dist-filesystem-node.d.ts +2 -2
- package/dist/mirror/dist-filesystem-node.js +5 -15
- package/dist/mirror/domain/exports.d.ts +2 -2
- package/dist/mirror/domain/exports.js +1 -1
- package/dist/mirror/domain/types.d.ts +2 -2
- package/dist/mirror/output.d.ts +1 -1
- package/dist/mirror/output.js +2 -2
- package/dist/mirror/package-path.d.ts +3 -3
- package/dist/mirror/package-path.js +3 -3
- package/dist/mirror/prepare.d.ts +5 -5
- package/dist/mirror/prepare.js +4 -4
- package/dist/mirror/run.d.ts +6 -6
- package/dist/mirror/run.js +7 -7
- package/dist/mirror/supplement-exports.d.ts +1 -1
- package/dist/mirror/supplement-exports.js +2 -2
- package/dist/mirror/sync-workspace-package.d.ts +3 -3
- package/dist/mirror/sync-workspace-package.js +11 -14
- package/dist/mirror/write-exports.d.ts +2 -2
- package/dist/mirror/write-exports.js +3 -3
- package/dist/pack-slim/cli-result.d.ts +1 -1
- package/dist/pack-slim/cli-result.js +1 -1
- package/dist/pack-slim/command.js +9 -9
- package/dist/pack-slim/domain/transform.d.ts +22 -1
- package/dist/pack-slim/domain/transform.js +65 -8
- package/dist/pack-slim/output.d.ts +1 -1
- package/dist/pack-slim/output.js +1 -1
- package/dist/pack-slim/prepare.d.ts +3 -3
- package/dist/pack-slim/prepare.js +2 -2
- package/dist/pack-slim/run.d.ts +5 -5
- package/dist/pack-slim/run.js +6 -6
- package/dist/pack-slim/working-tree.d.ts +2 -2
- package/dist/pack-slim/working-tree.js +2 -2
- package/dist/tag/cli-result.d.ts +1 -1
- package/dist/tag/cli-result.js +1 -1
- package/dist/tag/cli-schema.d.ts +1 -1
- package/dist/tag/command.js +7 -7
- package/dist/tag/domain/skip-filter.d.ts +1 -1
- package/dist/tag/domain/skip-filter.js +1 -1
- package/dist/tag/domain/types.d.ts +1 -1
- package/dist/tag/domain/version-summary.d.ts +5 -2
- package/dist/tag/output.d.ts +1 -1
- package/dist/tag/output.js +1 -1
- package/dist/tag/prepare.d.ts +4 -4
- package/dist/tag/prepare.js +4 -4
- package/dist/tag/run.d.ts +4 -4
- package/dist/tag/run.js +7 -7
- package/dist/tag/target/candidates.d.ts +2 -2
- package/dist/tag/target/candidates.js +2 -2
- package/dist/tag/target/resolve-path.d.ts +1 -1
- package/dist/tag/target/runner.d.ts +2 -2
- package/dist/tag/target/runner.js +3 -3
- package/dist/tag/writer/since-writer.d.ts +2 -2
- package/dist/tag/writer/since-writer.js +3 -5
- package/dist/tag/writer/version-resolver.d.ts +1 -1
- package/dist/tag/writer/version-resolver.js +1 -1
- package/package.json +7 -7
- package/dist/mirror/domain/dirent-guard.d.ts +0 -10
- package/dist/mirror/domain/dirent-guard.js +0 -15
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { AuditCommandPrelude } from "
|
|
2
|
-
import type { AppError } from "
|
|
3
|
-
import type { Filesystem } from "
|
|
4
|
-
import type { Result } from "
|
|
1
|
+
import type { AuditCommandPrelude } from "#audit/prepare";
|
|
2
|
+
import type { AppError } from "#core/errors";
|
|
3
|
+
import type { Filesystem } from "#core/filesystem/filesystem";
|
|
4
|
+
import type { Result } from "#core/result";
|
|
5
5
|
/**
|
|
6
6
|
* Loads config and resolves the scan target for `audit comments`.
|
|
7
7
|
*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { CommentAuditResult } from "
|
|
2
|
-
import { AppError } from "
|
|
3
|
-
import type { Filesystem } from "
|
|
4
|
-
import type { Result } from "
|
|
1
|
+
import type { CommentAuditResult } from "#audit/domain/types";
|
|
2
|
+
import { AppError } from "#core/errors";
|
|
3
|
+
import type { Filesystem } from "#core/filesystem/filesystem";
|
|
4
|
+
import type { Result } from "#core/result";
|
|
5
5
|
/**
|
|
6
6
|
* Reports every comment off the repo's conventions: divider form (which `fix` rewrites), banned
|
|
7
7
|
* content — document pointers, JSDoc types, tag misuse — `{@link}` targets nothing declares, and
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { scanCommentContent } from "
|
|
3
|
-
import { applyCommentDividerFixes, DIVIDER_COLUMN, scanCommentDividers
|
|
4
|
-
import { countHeadMentions, isPathLinkTarget, linkTargetHead, scanLinkReferences, } from "
|
|
5
|
-
import { scanImpossibleSinceTags } from "
|
|
6
|
-
import { scanTsdocSyntax } from "
|
|
7
|
-
import { AppError, messageFrom } from "
|
|
8
|
-
import { err, ok } from "
|
|
9
|
-
import { findNearestPackageVersion } from "
|
|
10
|
-
import { sourceCommentLanguage, walkSourceFiles } from "
|
|
2
|
+
import { scanCommentContent } from "#audit/comments/domain/comment-content";
|
|
3
|
+
import { applyCommentDividerFixes, DIVIDER_COLUMN, scanCommentDividers } from "#audit/comments/domain/comment-dividers";
|
|
4
|
+
import { countHeadMentions, isPathLinkTarget, linkTargetHead, scanLinkReferences, } from "#audit/comments/domain/link-references";
|
|
5
|
+
import { scanImpossibleSinceTags } from "#audit/comments/domain/since-versions";
|
|
6
|
+
import { scanTsdocSyntax } from "#audit/comments/domain/tsdoc-syntax";
|
|
7
|
+
import { AppError, messageFrom } from "#core/errors";
|
|
8
|
+
import { err, ok } from "#core/result";
|
|
9
|
+
import { findNearestPackageVersion } from "#core/workspace/package-version";
|
|
10
|
+
import { sourceCommentLanguage, walkSourceFiles } from "#core/workspace/source-walk";
|
|
11
11
|
const reasonByDefect = {
|
|
12
12
|
"bad-width": `rule does not end at column ${DIVIDER_COLUMN}`,
|
|
13
13
|
"dead-link": "{@link} target has no mention outside links — likely renamed",
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ConstantAuditResult } from "#audit/domain/types";
|
|
2
|
+
/**
|
|
3
|
+
* Exit `1` when any non-allowlisted numeric constant is unlabelled.
|
|
4
|
+
*
|
|
5
|
+
* @since 0.11.0
|
|
6
|
+
*/
|
|
7
|
+
export declare function exitCodeForConstantAuditResult(result: ConstantAuditResult): number;
|
|
8
|
+
/**
|
|
9
|
+
* Machine-readable numeric-constant summary for `--json`.
|
|
10
|
+
*
|
|
11
|
+
* @since 0.11.0
|
|
12
|
+
*/
|
|
13
|
+
export declare function formatConstantAuditJsonOutput(result: ConstantAuditResult, 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 numeric constant is unlabelled.
|
|
4
|
+
*
|
|
5
|
+
* @since 0.11.0
|
|
6
|
+
*/
|
|
7
|
+
export function exitCodeForConstantAuditResult(result) {
|
|
8
|
+
return result.violationCount > 0 ? CLI_EXIT_GENERAL_ERROR : CLI_EXIT_SUCCESS;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Machine-readable numeric-constant summary for `--json`.
|
|
12
|
+
*
|
|
13
|
+
* @since 0.11.0
|
|
14
|
+
*/
|
|
15
|
+
export function formatConstantAuditJsonOutput(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 numeric-constant audit run.
|
|
4
|
+
*
|
|
5
|
+
* @since 0.11.0
|
|
6
|
+
*/
|
|
7
|
+
export type ConstantAuditRunRequest = {
|
|
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 ConstantAuditRunRequest}.
|
|
15
|
+
*
|
|
16
|
+
* @since 0.11.0
|
|
17
|
+
*/
|
|
18
|
+
export declare const constantAuditRunRequestSchema: z.ZodType<ConstantAuditRunRequest>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Zod schema for {@link ConstantAuditRunRequest}.
|
|
4
|
+
*
|
|
5
|
+
* @since 0.11.0
|
|
6
|
+
*/
|
|
7
|
+
export const constantAuditRunRequestSchema = 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
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** The numeric-constant convention: a `const NAME = <number>` in a source tree names the kind of number it is. */
|
|
2
|
+
import type { ConstantViolation } from "#audit/domain/types";
|
|
3
|
+
/**
|
|
4
|
+
* Scans one TypeScript source for numeric constants whose comment names none of the three kinds.
|
|
5
|
+
*
|
|
6
|
+
* @since 0.11.0
|
|
7
|
+
*/
|
|
8
|
+
export declare function auditNumericConstants(sourceText: string): Array<ConstantViolation>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The three kinds a numeric constant may be, each named by one phrase in the comment above it.
|
|
3
|
+
*
|
|
4
|
+
* @remarks A width of the machine, a value the contract fixes, or a figure derived from the data a
|
|
5
|
+
* bind hands in. A count that merely looks reasonable is none of them, and the audit says so.
|
|
6
|
+
*/
|
|
7
|
+
const KIND_PHRASES = [
|
|
8
|
+
"a constant of the machine",
|
|
9
|
+
"a value the contract fixes",
|
|
10
|
+
"derived from bind-time data",
|
|
11
|
+
];
|
|
12
|
+
/** An upper-case `const` bound to a numeric literal, with an optional type annotation. */
|
|
13
|
+
const NUMERIC_CONST = /^\s*(?:export\s+)?const\s+([A-Z][A-Z0-9_]*)\s*(?::[^=]+)?=\s*(-?\d[\d_]*(?:\.\d+)?)(?![\w.])/;
|
|
14
|
+
/** Values that stand for absence or identity, never for a tuned size: any distinct value would do. */
|
|
15
|
+
const SENTINEL_VALUES = new Set(["0", "1", "-1"]);
|
|
16
|
+
/**
|
|
17
|
+
* Scans one TypeScript source for numeric constants whose comment names none of the three kinds.
|
|
18
|
+
*
|
|
19
|
+
* @since 0.11.0
|
|
20
|
+
*/
|
|
21
|
+
export function auditNumericConstants(sourceText) {
|
|
22
|
+
const lines = sourceText.split("\n");
|
|
23
|
+
const violations = [];
|
|
24
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
25
|
+
const match = NUMERIC_CONST.exec(lines[index]);
|
|
26
|
+
if (match === null || SENTINEL_VALUES.has(match[2])) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const comment = commentAbove(lines, index);
|
|
30
|
+
if (KIND_PHRASES.some((phrase) => comment.includes(phrase))) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
violations.push({
|
|
34
|
+
line: index + 1,
|
|
35
|
+
raw: `${match[1]} = ${match[2]}`,
|
|
36
|
+
reason: `no kind named above it — say which it is: ${KIND_PHRASES.join(" · ")}`,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return violations;
|
|
40
|
+
}
|
|
41
|
+
/** The doc block or run of `//` lines directly above a line, as one text; empty when there is none. */
|
|
42
|
+
function commentAbove(lines, index) {
|
|
43
|
+
let cursor = index - 1;
|
|
44
|
+
const gathered = [];
|
|
45
|
+
while (cursor >= 0) {
|
|
46
|
+
const line = lines[cursor].trim();
|
|
47
|
+
if (line.startsWith("//")) {
|
|
48
|
+
gathered.unshift(line);
|
|
49
|
+
cursor -= 1;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (line.endsWith("*/")) {
|
|
53
|
+
// Walk back to the opening of the block, collecting it whole.
|
|
54
|
+
while (cursor >= 0) {
|
|
55
|
+
gathered.unshift(lines[cursor].trim());
|
|
56
|
+
if (lines[cursor].trim().startsWith("/*")) {
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
cursor -= 1;
|
|
60
|
+
}
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
return gathered.join(" ");
|
|
66
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { logger } from "#core/logger";
|
|
2
|
+
/**
|
|
3
|
+
* Human-readable numeric-constant report.
|
|
4
|
+
*
|
|
5
|
+
* @since 0.11.0
|
|
6
|
+
*/
|
|
7
|
+
export function presentConstantAuditResult(result) {
|
|
8
|
+
for (const file of result.files) {
|
|
9
|
+
logger.out(`\n${file.relativePath}`);
|
|
10
|
+
for (const { line, raw, reason } of file.violations) {
|
|
11
|
+
logger.out(` ${line}: ${raw} → ${reason}`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const allowlistSuffix = result.allowlistedCount > 0 ? ` (${result.allowlistedCount} allowlisted)` : "";
|
|
15
|
+
if (result.violationCount > 0) {
|
|
16
|
+
logger.out(`\n✖ ${result.violationCount} numeric constant(s) with no kind named${allowlistSuffix}`);
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
logger.out(`✓ Every numeric constant names its kind across ${result.scannedFileCount} source file(s)${allowlistSuffix}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
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 constants`.
|
|
7
|
+
*
|
|
8
|
+
* @remarks The target is the library tree the convention holds over, from `audit.constants.target`
|
|
9
|
+
* unless the command names one.
|
|
10
|
+
*
|
|
11
|
+
* @since 0.11.0
|
|
12
|
+
*/
|
|
13
|
+
export declare function prepareConstantAudit(fs: Filesystem, args: {
|
|
14
|
+
readonly currentWorkingDirectory: string;
|
|
15
|
+
readonly rawTarget: string | undefined;
|
|
16
|
+
}): Promise<Result<AuditCommandPrelude, AppError>>;
|
|
@@ -0,0 +1,37 @@
|
|
|
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 constants`.
|
|
8
|
+
*
|
|
9
|
+
* @remarks The target is the library tree the convention holds over, from `audit.constants.target`
|
|
10
|
+
* unless the command names one.
|
|
11
|
+
*
|
|
12
|
+
* @since 0.11.0
|
|
13
|
+
*/
|
|
14
|
+
export async function prepareConstantAudit(fs, args) {
|
|
15
|
+
let rootDir;
|
|
16
|
+
try {
|
|
17
|
+
rootDir = fs.canonicalPathSync(resolveProjectRoot(args.currentWorkingDirectory, fs).rootDir);
|
|
18
|
+
}
|
|
19
|
+
catch (caughtError) {
|
|
20
|
+
return err(new AppError("INFRA_FAILURE", messageFrom(caughtError), caughtError));
|
|
21
|
+
}
|
|
22
|
+
const loadedOutcome = await loadCodefastConfig(rootDir, fs);
|
|
23
|
+
if (!loadedOutcome.ok) {
|
|
24
|
+
return loadedOutcome;
|
|
25
|
+
}
|
|
26
|
+
const { config } = loadedOutcome.value;
|
|
27
|
+
const constantsConfig = config.audit?.constants ?? {};
|
|
28
|
+
const resolvedTargetInput = args.rawTarget ?? constantsConfig.target;
|
|
29
|
+
if (resolvedTargetInput === undefined) {
|
|
30
|
+
return err(new AppError("VALIDATION_ERROR", "audit constants needs a target: pass one, or set audit.constants.target in codefast.config"));
|
|
31
|
+
}
|
|
32
|
+
const targetPath = resolveRepoRelativePath(args.rawTarget !== undefined ? args.currentWorkingDirectory : rootDir, resolvedTargetInput);
|
|
33
|
+
if (!fs.existsSync(targetPath)) {
|
|
34
|
+
return err(new AppError("NOT_FOUND", `Not found: ${targetPath}`));
|
|
35
|
+
}
|
|
36
|
+
return ok({ rootDir, targetPath: fs.canonicalPathSync(targetPath), allowlist: constantsConfig.allowlist ?? [] });
|
|
37
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ConstantAuditResult } from "#audit/domain/types";
|
|
2
|
+
import { AppError } from "#core/errors";
|
|
3
|
+
import type { Filesystem } from "#core/filesystem/filesystem";
|
|
4
|
+
import type { Result } from "#core/result";
|
|
5
|
+
/**
|
|
6
|
+
* Scans a target path's library sources for numeric constants that name none of the three kinds.
|
|
7
|
+
*
|
|
8
|
+
* @since 0.11.0
|
|
9
|
+
*/
|
|
10
|
+
export declare function runConstantAudit(fs: Filesystem, args: {
|
|
11
|
+
readonly rootDir: string;
|
|
12
|
+
readonly targetPath: string;
|
|
13
|
+
readonly allowlist: ReadonlyArray<string>;
|
|
14
|
+
}): Result<ConstantAuditResult, AppError>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { auditNumericConstants } from "#audit/constants/domain/constants";
|
|
3
|
+
import { AppError, messageFrom } from "#core/errors";
|
|
4
|
+
import { err, ok } from "#core/result";
|
|
5
|
+
import { walkTsxFiles } from "#core/workspace/typescript-walk";
|
|
6
|
+
/**
|
|
7
|
+
* Trees the convention does not reach: a test fixes a value to observe it, a benchmark sizes a
|
|
8
|
+
* workload, and an app or example is a consumer, not the library.
|
|
9
|
+
*/
|
|
10
|
+
const SKIPPED_SEGMENTS = new Set([
|
|
11
|
+
"tests",
|
|
12
|
+
"benchmarks",
|
|
13
|
+
"apps",
|
|
14
|
+
"examples",
|
|
15
|
+
"node_modules",
|
|
16
|
+
"dist",
|
|
17
|
+
]);
|
|
18
|
+
/**
|
|
19
|
+
* Scans a target path's library sources for numeric constants that name none of the three kinds.
|
|
20
|
+
*
|
|
21
|
+
* @since 0.11.0
|
|
22
|
+
*/
|
|
23
|
+
export function runConstantAudit(fs, args) {
|
|
24
|
+
try {
|
|
25
|
+
const allowlist = new Set(args.allowlist);
|
|
26
|
+
const { rootDir, targetPath } = args;
|
|
27
|
+
const filesToScan = collectScanPaths(fs, rootDir, targetPath);
|
|
28
|
+
const files = [];
|
|
29
|
+
let violationCount = 0;
|
|
30
|
+
let allowlistedCount = 0;
|
|
31
|
+
for (const absolutePath of filesToScan) {
|
|
32
|
+
const relativePath = toPosixPath(path.relative(rootDir, absolutePath));
|
|
33
|
+
const content = fs.readFileSync(absolutePath, "utf8");
|
|
34
|
+
const remaining = auditNumericConstants(content).filter(({ raw }) => {
|
|
35
|
+
const name = raw.split(" ")[0];
|
|
36
|
+
const isAllowed = allowlist.has(name) || allowlist.has(`${relativePath}:${name}`);
|
|
37
|
+
if (isAllowed) {
|
|
38
|
+
allowlistedCount++;
|
|
39
|
+
}
|
|
40
|
+
return !isAllowed;
|
|
41
|
+
});
|
|
42
|
+
if (remaining.length === 0) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
violationCount += remaining.length;
|
|
46
|
+
files.push({ relativePath, violations: remaining });
|
|
47
|
+
}
|
|
48
|
+
return ok({ files, violationCount, allowlistedCount, scannedFileCount: filesToScan.length });
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
return err(new AppError("INFRA_FAILURE", messageFrom(error), error));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/** The `.ts` files under a `src` directory of a library package, below the target. */
|
|
55
|
+
function collectScanPaths(fs, rootDir, targetPath) {
|
|
56
|
+
const candidates = fs.statSync(targetPath).isDirectory() ? walkTsxFiles(targetPath, fs) : [targetPath];
|
|
57
|
+
return candidates.filter((absolutePath) => {
|
|
58
|
+
const segments = toPosixPath(path.relative(rootDir, absolutePath)).split("/");
|
|
59
|
+
return segments.includes("src") && !segments.some((segment) => SKIPPED_SEGMENTS.has(segment));
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function toPosixPath(filePath) {
|
|
63
|
+
return filePath.split(path.sep).join("/");
|
|
64
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
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 "
|
|
2
|
+
import type { DisplayNameViolation } from "#audit/domain/types";
|
|
3
3
|
/**
|
|
4
4
|
* Scans one source or markdown text for `token()`, `tag()` and module display names that break the convention.
|
|
5
5
|
*
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { lineOfOffset } from "#core/source-position";
|
|
1
2
|
/** The owner: a kebab-case package, app or feature slug, or a scoped package name. */
|
|
2
3
|
const NAMESPACE = /^(?:@[a-z0-9-]+\/)?[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
3
4
|
/** A token or module stands for a type or a unit of composition. */
|
|
@@ -59,13 +60,4 @@ function reasonFor(kind, name) {
|
|
|
59
60
|
return PASCAL_CASE.test(local)
|
|
60
61
|
? null
|
|
61
62
|
: `${label} '${local}' in '${name}' is not PascalCase — a ${kind} stands for a ${kind === "token" ? "type" : "unit of composition"}`;
|
|
62
|
-
}
|
|
63
|
-
function lineOfOffset(sourceText, offset) {
|
|
64
|
-
let line = 1;
|
|
65
|
-
for (let index = 0; index < offset; index++) {
|
|
66
|
-
if (sourceText.charCodeAt(index) === 10) {
|
|
67
|
-
line++;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return line;
|
|
71
63
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { AuditCommandPrelude } from "
|
|
2
|
-
import type { AppError } from "
|
|
3
|
-
import type { Filesystem } from "
|
|
4
|
-
import type { Result } from "
|
|
1
|
+
import type { AuditCommandPrelude } from "#audit/prepare";
|
|
2
|
+
import type { AppError } from "#core/errors";
|
|
3
|
+
import type { Filesystem } from "#core/filesystem/filesystem";
|
|
4
|
+
import type { Result } from "#core/result";
|
|
5
5
|
/**
|
|
6
6
|
* Loads config and resolves the scan target for `audit display-names`.
|
|
7
7
|
*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { DisplayNameAuditResult } from "
|
|
2
|
-
import { AppError } from "
|
|
3
|
-
import type { Filesystem } from "
|
|
4
|
-
import type { Result } from "
|
|
1
|
+
import type { DisplayNameAuditResult } from "#audit/domain/types";
|
|
2
|
+
import { AppError } from "#core/errors";
|
|
3
|
+
import type { Filesystem } from "#core/filesystem/filesystem";
|
|
4
|
+
import type { Result } from "#core/result";
|
|
5
5
|
/**
|
|
6
6
|
* Scans a target path for `token()`, `tag()` and module display names that break the convention.
|
|
7
7
|
*
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { auditDisplayNames } from "
|
|
3
|
-
import { AppError, messageFrom } from "
|
|
4
|
-
import { err, ok } from "
|
|
5
|
-
import { walkMarkdownFiles } from "
|
|
6
|
-
import { walkTsxFiles } from "
|
|
2
|
+
import { auditDisplayNames } from "#audit/display-names/domain/display-names";
|
|
3
|
+
import { AppError, messageFrom } from "#core/errors";
|
|
4
|
+
import { err, ok } from "#core/result";
|
|
5
|
+
import { walkMarkdownFiles } from "#core/workspace/markdown-walk";
|
|
6
|
+
import { walkTsxFiles } from "#core/workspace/typescript-walk";
|
|
7
7
|
/**
|
|
8
8
|
* Trees the convention does not reach: a test or benchmark token is scoped by its file and never
|
|
9
9
|
* meets another author's, and a changelog quotes names as they were.
|
|
@@ -73,6 +73,37 @@ export type ImportsAuditResult = {
|
|
|
73
73
|
readonly allowlistedCount: number;
|
|
74
74
|
readonly scannedFileCount: number;
|
|
75
75
|
};
|
|
76
|
+
/**
|
|
77
|
+
* A double assertion through `unknown` or `any`, or a directive that keeps one without cause.
|
|
78
|
+
*
|
|
79
|
+
* @since 0.13.0
|
|
80
|
+
*/
|
|
81
|
+
export type AssertionViolation = {
|
|
82
|
+
readonly line: number;
|
|
83
|
+
/** The assertion or the directive as written, up to its first line break. */
|
|
84
|
+
readonly raw: string;
|
|
85
|
+
readonly reason: string;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* The type-assertion violations found in one file.
|
|
89
|
+
*
|
|
90
|
+
* @since 0.13.0
|
|
91
|
+
*/
|
|
92
|
+
export type AssertionFileViolations = {
|
|
93
|
+
readonly relativePath: string;
|
|
94
|
+
readonly violations: Array<AssertionViolation>;
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Outcome of one `audit assertions` run.
|
|
98
|
+
*
|
|
99
|
+
* @since 0.13.0
|
|
100
|
+
*/
|
|
101
|
+
export type AssertionAuditResult = {
|
|
102
|
+
readonly files: Array<AssertionFileViolations>;
|
|
103
|
+
readonly violationCount: number;
|
|
104
|
+
readonly allowlistedCount: number;
|
|
105
|
+
readonly scannedFileCount: number;
|
|
106
|
+
};
|
|
76
107
|
/**
|
|
77
108
|
* A `token()`, `tag()` or module display name that breaks the display-name convention.
|
|
78
109
|
*
|
|
@@ -104,6 +135,37 @@ export type DisplayNameAuditResult = {
|
|
|
104
135
|
readonly allowlistedCount: number;
|
|
105
136
|
readonly scannedFileCount: number;
|
|
106
137
|
};
|
|
138
|
+
/**
|
|
139
|
+
* A numeric constant whose comment names none of the three kinds.
|
|
140
|
+
*
|
|
141
|
+
* @since 0.11.0
|
|
142
|
+
*/
|
|
143
|
+
export type ConstantViolation = {
|
|
144
|
+
readonly line: number;
|
|
145
|
+
/** The declaration's name and value, as `NAME = 32`. */
|
|
146
|
+
readonly raw: string;
|
|
147
|
+
readonly reason: string;
|
|
148
|
+
};
|
|
149
|
+
/**
|
|
150
|
+
* The numeric-constant violations found in one file.
|
|
151
|
+
*
|
|
152
|
+
* @since 0.11.0
|
|
153
|
+
*/
|
|
154
|
+
export type ConstantFileViolations = {
|
|
155
|
+
readonly relativePath: string;
|
|
156
|
+
readonly violations: Array<ConstantViolation>;
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* Outcome of one `audit constants` run.
|
|
160
|
+
*
|
|
161
|
+
* @since 0.11.0
|
|
162
|
+
*/
|
|
163
|
+
export type ConstantAuditResult = {
|
|
164
|
+
readonly files: Array<ConstantFileViolations>;
|
|
165
|
+
readonly violationCount: number;
|
|
166
|
+
readonly allowlistedCount: number;
|
|
167
|
+
readonly scannedFileCount: number;
|
|
168
|
+
};
|
|
107
169
|
/**
|
|
108
170
|
* A broken link or anchor found by the link audit.
|
|
109
171
|
*
|
|
@@ -168,4 +230,70 @@ export type CommentAuditResult = {
|
|
|
168
230
|
readonly fixedCount: number;
|
|
169
231
|
readonly dividerCount: number;
|
|
170
232
|
readonly scannedFileCount: number;
|
|
233
|
+
};
|
|
234
|
+
/**
|
|
235
|
+
* A `#/`-prefixed internal import specifier — valid to the in-repo runners but rejected by Node's ESM
|
|
236
|
+
* resolver on the supported floor, so it breaks the published package.
|
|
237
|
+
*
|
|
238
|
+
* @since 0.12.0
|
|
239
|
+
*/
|
|
240
|
+
export type LegacySubpathImport = {
|
|
241
|
+
readonly line: number;
|
|
242
|
+
/** The specifier as written, quotes included. */
|
|
243
|
+
readonly raw: string;
|
|
244
|
+
};
|
|
245
|
+
/**
|
|
246
|
+
* The `#/` imports found in one source file.
|
|
247
|
+
*
|
|
248
|
+
* @since 0.12.0
|
|
249
|
+
*/
|
|
250
|
+
export type LegacySubpathFile = {
|
|
251
|
+
readonly relativePath: string;
|
|
252
|
+
readonly imports: Array<LegacySubpathImport>;
|
|
253
|
+
};
|
|
254
|
+
/**
|
|
255
|
+
* A published package whose slimmed manifest exports or imports a target it does not ship.
|
|
256
|
+
*
|
|
257
|
+
* @since 0.12.0
|
|
258
|
+
*/
|
|
259
|
+
export type UnshippedTargetViolation = {
|
|
260
|
+
readonly packageName: string;
|
|
261
|
+
readonly field: string;
|
|
262
|
+
readonly subpath: string;
|
|
263
|
+
readonly target: string;
|
|
264
|
+
};
|
|
265
|
+
/**
|
|
266
|
+
* A path a stylesheet registers with Tailwind's `@source`, as written.
|
|
267
|
+
*
|
|
268
|
+
* @since 0.13.0
|
|
269
|
+
*/
|
|
270
|
+
export type StylesheetSource = {
|
|
271
|
+
readonly line: number;
|
|
272
|
+
readonly pattern: string;
|
|
273
|
+
};
|
|
274
|
+
/**
|
|
275
|
+
* A shipped stylesheet whose `@source` paths reach no file its package's tarball ships.
|
|
276
|
+
*
|
|
277
|
+
* @since 0.13.0
|
|
278
|
+
*/
|
|
279
|
+
export type UnreachableStylesheetViolation = {
|
|
280
|
+
readonly packageName: string;
|
|
281
|
+
/** Repo-relative path of the stylesheet. */
|
|
282
|
+
readonly stylesheet: string;
|
|
283
|
+
readonly sources: Array<StylesheetSource>;
|
|
284
|
+
/** The `files` entries missing on disk, which is how an unbuilt `dist` shows up. */
|
|
285
|
+
readonly missingFilesEntries: Array<string>;
|
|
286
|
+
};
|
|
287
|
+
/**
|
|
288
|
+
* Outcome of one `audit publish` run.
|
|
289
|
+
*
|
|
290
|
+
* @since 0.12.0
|
|
291
|
+
*/
|
|
292
|
+
export type PublishAuditResult = {
|
|
293
|
+
readonly legacyImportFiles: Array<LegacySubpathFile>;
|
|
294
|
+
readonly unshipped: Array<UnshippedTargetViolation>;
|
|
295
|
+
readonly unreachableStylesheets: Array<UnreachableStylesheetViolation>;
|
|
296
|
+
readonly legacyImportCount: number;
|
|
297
|
+
readonly scannedFileCount: number;
|
|
298
|
+
readonly packageCount: number;
|
|
171
299
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ImportPolicyViolation } from "
|
|
1
|
+
import type { ImportPolicyViolation } from "#audit/domain/types";
|
|
2
2
|
/**
|
|
3
3
|
* One library's import policy: which forms of importing `module` are banned, optionally limited to
|
|
4
4
|
* files whose repo-relative path matches `scope`, plus an optional UMD-global name to flag when the
|