@avi2dg/checks 0.24.2 → 0.26.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 +22 -0
- package/README.md +29 -22
- package/dist/effect-channel/index.js +4 -4
- package/dist/readability/index.js +5 -5
- package/docs/configs/commit-messages.md +1 -1
- package/docs/configs/effect-rules.md +2 -2
- package/docs/configs/native-settings.md +4 -2
- package/docs/design.md +10 -4
- package/docs/gates/checks-comment-gate.md +3 -4
- package/docs/gates/checks-docs.md +16 -14
- package/docs/gates/checks-exports.md +104 -0
- package/docs/gates/checks-flake.md +1 -1
- package/docs/gates/checks-lint.md +1 -1
- package/docs/gates/checks-quarantine-clock.md +1 -1
- package/docs/gates/checks-test-layout.md +14 -9
- package/docs/gates/checks-test.md +1 -1
- package/docs/gates/checks-unused.md +65 -0
- package/knip-base.json +4 -0
- package/package.json +75 -108
- package/src/complexity/exports.ts +196 -0
- package/src/complexity/knip.ts +70 -0
- package/{scripts → src/complexity}/repetition.ts +2 -2
- package/{scripts → src/complexity}/suppressions-ratchet.ts +2 -2
- package/src/complexity/unused.ts +48 -0
- package/src/core/gates.ts +46 -0
- package/{scripts → src/core}/lint.ts +2 -2
- package/{scripts → src/core}/swc.ts +1 -1
- package/{scripts → src/delivery}/changelog-write.ts +2 -2
- package/{scripts → src/delivery}/changelog.ts +3 -3
- package/{scripts → src/delivery}/ci-wiring.ts +3 -3
- package/{scripts → src/delivery}/commit-identity.ts +2 -2
- package/{scripts → src/delivery}/release-notes.ts +1 -1
- package/{scripts → src/delivery}/release-report.ts +2 -2
- package/{scripts → src/dependencies}/vendor-args.ts +1 -1
- package/{scripts → src/dependencies}/vendor.ts +2 -2
- package/{scripts → src/docs}/doc-references.ts +1 -1
- package/{scripts → src/docs}/doc-snapshot.ts +2 -2
- package/{scripts → src/docs}/doc-templates.ts +3 -3
- package/{scripts → src/docs}/docs.ts +2 -2
- package/{scripts → src/quality}/comment-gate.ts +2 -2
- package/{scripts → src/quality}/comment-matchers.ts +1 -1
- package/{scripts → src/quality}/comments.ts +2 -2
- package/{scripts → src/testing}/flake.ts +1 -1
- package/{scripts → src/testing}/mutation-compare.ts +1 -1
- package/{scripts → src/testing}/quarantine-clock.ts +2 -2
- package/{scripts → src/testing}/subsumed-tests.ts +2 -2
- package/{scripts → src/testing}/test-layout.ts +47 -14
- package/{scripts → src/testing}/test-report.ts +1 -1
- package/{scripts → src/testing}/test-skips.ts +1 -1
- package/{scripts → src/testing}/test.ts +2 -2
- package/CONTRIBUTING.md +0 -94
- package/docs/gates/checks-backtest.md +0 -54
- package/scripts/backtest.ts +0 -122
- package/scripts/gates.ts +0 -37
- package/scripts/range-gate.ts +0 -8
- /package/{templates → dist/templates}/adr.md +0 -0
- /package/{templates → dist/templates}/agents.md +0 -0
- /package/{templates → dist/templates}/changelog.md +0 -0
- /package/{templates → dist/templates}/claude.md +0 -0
- /package/{templates → dist/templates}/explanation.md +0 -0
- /package/{templates → dist/templates}/how-to.md +0 -0
- /package/{templates → dist/templates}/readme.md +0 -0
- /package/{templates → dist/templates}/reference.md +0 -0
- /package/{templates → dist/templates}/tutorial.md +0 -0
- /package/{scripts → src/core}/git.ts +0 -0
- /package/{scripts → src/core}/main.ts +0 -0
- /package/{scripts → src/delivery}/shell-command.ts +0 -0
- /package/{scripts → src/docs}/doc-outline.ts +0 -0
- /package/{scripts → src/docs}/doc-rules.ts +0 -0
- /package/{scripts → src/docs}/prose-matchers.ts +0 -0
- /package/{scripts → src/quality}/lint-coverage.sh +0 -0
- /package/{presets → src/quality/presets}/effect.language-service.json +0 -0
- /package/{presets → src/quality/presets}/effect.oxlint.json +0 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export type Program = {
|
|
2
|
+
readonly bin: string;
|
|
3
|
+
readonly script: string;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
type TrackedContent = {
|
|
7
|
+
readonly pathspecs: readonly string[];
|
|
8
|
+
readonly content: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const EVERY_REPOSITORY = "every repository";
|
|
12
|
+
|
|
13
|
+
export const VECTORS = ["complexity", "quality", "testing", "docs", "delivery", "dependencies"] as const;
|
|
14
|
+
|
|
15
|
+
type Vector = (typeof VECTORS)[number];
|
|
16
|
+
|
|
17
|
+
export type KitGate = {
|
|
18
|
+
readonly bin: string;
|
|
19
|
+
readonly vector: Vector;
|
|
20
|
+
readonly file: string;
|
|
21
|
+
readonly reads: "tree" | "range";
|
|
22
|
+
readonly args?: readonly string[];
|
|
23
|
+
readonly appliesTo: typeof EVERY_REPOSITORY | TrackedContent;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const ENTRY_POINT: Program = { bin: "checks-lint", script: "core/lint.ts" };
|
|
27
|
+
|
|
28
|
+
export const TEST_ENTRY_POINT: Program = { bin: "checks-test", script: "testing/test.ts" };
|
|
29
|
+
|
|
30
|
+
export const DEFAULT_BRANCH = "main";
|
|
31
|
+
|
|
32
|
+
const TYPESCRIPT_SOURCE: TrackedContent = { pathspecs: ["*.ts", "*.tsx"], content: "TypeScript source" };
|
|
33
|
+
|
|
34
|
+
export const KIT_GATES = [
|
|
35
|
+
{ bin: "checks-lint-coverage", vector: "quality", file: "lint-coverage.sh", reads: "tree", appliesTo: TYPESCRIPT_SOURCE },
|
|
36
|
+
{ bin: "checks-test-layout", vector: "testing", file: "test-layout.ts", reads: "tree", appliesTo: TYPESCRIPT_SOURCE },
|
|
37
|
+
{ bin: "checks-commit-identity", vector: "delivery", file: "commit-identity.ts", reads: "range", appliesTo: EVERY_REPOSITORY },
|
|
38
|
+
{ bin: "checks-comment-gate", vector: "quality", file: "comment-gate.ts", reads: "range", appliesTo: EVERY_REPOSITORY },
|
|
39
|
+
{ bin: "checks-suppressions-ratchet", vector: "complexity", file: "suppressions-ratchet.ts", reads: "range", appliesTo: EVERY_REPOSITORY },
|
|
40
|
+
{ bin: "checks-ci-wiring", vector: "delivery", file: "ci-wiring.ts", reads: "tree", appliesTo: EVERY_REPOSITORY },
|
|
41
|
+
{ bin: "checks-docs", vector: "docs", file: "docs.ts", reads: "range", appliesTo: EVERY_REPOSITORY },
|
|
42
|
+
{ bin: "checks-repetition", vector: "complexity", file: "repetition.ts", reads: "range", appliesTo: TYPESCRIPT_SOURCE },
|
|
43
|
+
{ bin: "checks-unused", vector: "complexity", file: "unused.ts", reads: "tree", appliesTo: TYPESCRIPT_SOURCE },
|
|
44
|
+
{ bin: "checks-exports", vector: "complexity", file: "exports.ts", reads: "range", appliesTo: TYPESCRIPT_SOURCE },
|
|
45
|
+
{ bin: "checks-quarantine-clock", vector: "testing", file: "quarantine-clock.ts", reads: "range", appliesTo: EVERY_REPOSITORY },
|
|
46
|
+
] as const satisfies readonly KitGate[];
|
|
@@ -151,8 +151,8 @@ function outcomeOf(exitCode: number): Outcome {
|
|
|
151
151
|
|
|
152
152
|
const runGate = Effect.fn("runGate")(function* (gate: KitGate, range: Range) {
|
|
153
153
|
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
154
|
-
const script = (yield* Path.Path).join(import.meta.dir, gate.
|
|
155
|
-
const program = gate.
|
|
154
|
+
const script = (yield* Path.Path).join(import.meta.dir, "..", gate.vector, gate.file);
|
|
155
|
+
const program = gate.file.endsWith(".sh") ? "sh" : process.execPath;
|
|
156
156
|
const args = gate.reads === "range" ? [script, ...range.refs] : [script, ...(gate.args ?? [])];
|
|
157
157
|
const exitCode = yield* spawner
|
|
158
158
|
.exitCode(ChildProcess.make(program, args, { stdin: "ignore", stdout: "inherit", stderr: "inherit" }))
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { parse } from "@swc/core";
|
|
2
2
|
import { Effect, Schema } from "effect";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
class TypeScriptParseError extends Schema.TaggedError<TypeScriptParseError>()("TypeScriptParseError", {
|
|
5
5
|
message: Schema.String,
|
|
6
6
|
}) {}
|
|
7
7
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { Console, DateTime, Effect, FileSystem, Path, Schema } from "effect";
|
|
3
3
|
import { cuts, hasEntries, namesPullRequest, releaseDates, renderChangelog, type Bump, type Cut, type Release } from "./changelog.ts";
|
|
4
|
-
import { git } from "
|
|
5
|
-
import { runMain } from "
|
|
4
|
+
import { git } from "../core/git.ts";
|
|
5
|
+
import { runMain } from "../core/main.ts";
|
|
6
6
|
|
|
7
7
|
const NAME = "checks-changelog";
|
|
8
8
|
const CHANGELOG = "CHANGELOG.md";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { firstText, parseOutline, VERSION } from "
|
|
2
|
-
import { RELEASED } from "
|
|
3
|
-
import { CHANGE_GROUPS, type ChangeGroup } from "
|
|
1
|
+
import { firstText, parseOutline, VERSION } from "../docs/doc-outline.ts";
|
|
2
|
+
import { RELEASED } from "../docs/doc-rules.ts";
|
|
3
|
+
import { CHANGE_GROUPS, type ChangeGroup } from "../docs/doc-templates.ts";
|
|
4
4
|
|
|
5
5
|
export type Bump = {
|
|
6
6
|
readonly sha: string;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { Console, Effect, FileSystem, Path, Schema } from "effect";
|
|
3
|
-
import { defaultBranch, git } from "
|
|
4
|
-
import { runMain } from "
|
|
5
|
-
import { ENTRY_POINT, KIT_GATES, type KitGate } from "
|
|
3
|
+
import { defaultBranch, git } from "../core/git.ts";
|
|
4
|
+
import { runMain } from "../core/main.ts";
|
|
5
|
+
import { ENTRY_POINT, KIT_GATES, type KitGate } from "../core/gates.ts";
|
|
6
6
|
import { invokes, mentions, plainCommand, type Command } from "./shell-command.ts";
|
|
7
7
|
|
|
8
8
|
export type { Command };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { Console, Effect, FileSystem, Path, Schema } from "effect";
|
|
3
|
-
import { git, refArgs } from "
|
|
4
|
-
import { runMain } from "
|
|
3
|
+
import { git, refArgs } from "../core/git.ts";
|
|
4
|
+
import { runMain } from "../core/main.ts";
|
|
5
5
|
|
|
6
6
|
type Identity = { readonly name: string; readonly email: string };
|
|
7
7
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { Effect, FileSystem, Schema } from "effect";
|
|
3
|
-
import { runMain, Usage } from "
|
|
3
|
+
import { runMain, Usage } from "../core/main.ts";
|
|
4
4
|
|
|
5
5
|
export class ReleaseSectionUnavailable extends Schema.TaggedError<ReleaseSectionUnavailable>()("ReleaseSectionUnavailable", {
|
|
6
6
|
message: Schema.String,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { Console, Effect, Schema } from "effect";
|
|
3
3
|
import { groupOf } from "./changelog.ts";
|
|
4
|
-
import { git } from "
|
|
5
|
-
import { runMain, Usage } from "
|
|
4
|
+
import { git } from "../core/git.ts";
|
|
5
|
+
import { runMain, Usage } from "../core/main.ts";
|
|
6
6
|
|
|
7
7
|
export class ReleaseReportUnreadable extends Schema.TaggedError<ReleaseReportUnreadable>()("ReleaseReportUnreadable", {
|
|
8
8
|
message: Schema.String,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { Console, Effect, FileSystem, Option, Path, Schema } from "effect";
|
|
3
|
-
import { git } from "
|
|
4
|
-
import { runMain } from "
|
|
3
|
+
import { git } from "../core/git.ts";
|
|
4
|
+
import { runMain } from "../core/main.ts";
|
|
5
5
|
import { librariesFrom, NAME, OPENER, type Library } from "./vendor-args.ts";
|
|
6
6
|
|
|
7
7
|
const CACHE_HOME = ".cache/avi2dg-checks";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Effect, FileSystem, Path, Schema } from "effect";
|
|
2
2
|
import { anchoredTargets, anchorsOf, PACKAGE_MANIFEST, packagesOf, snapshotOf, type Unresolved } from "./doc-references.ts";
|
|
3
|
-
import { git, pathsAt } from "
|
|
3
|
+
import { git, pathsAt } from "../core/git.ts";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
class ManifestUnreadable extends Schema.TaggedError<ManifestUnreadable>()("ManifestUnreadable", {
|
|
6
6
|
message: Schema.String,
|
|
7
7
|
}) {}
|
|
8
8
|
|
|
@@ -9,21 +9,21 @@ export type Title =
|
|
|
9
9
|
| { readonly type: "fixed"; readonly text: string }
|
|
10
10
|
| { readonly type: "open"; readonly placeholder: string; readonly rule: HeadingRule; readonly prefix?: string };
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
type Outlined = {
|
|
13
13
|
readonly shape: "outline";
|
|
14
14
|
readonly title: Title;
|
|
15
15
|
readonly lead: readonly string[];
|
|
16
16
|
readonly sections: readonly Slot[];
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
type Exact = {
|
|
20
20
|
readonly shape: "exact";
|
|
21
21
|
readonly text: string;
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
export type Template = Outlined | Exact;
|
|
25
25
|
|
|
26
|
-
export const TEMPLATE_DIRECTORY = "templates";
|
|
26
|
+
export const TEMPLATE_DIRECTORY = "dist/templates";
|
|
27
27
|
|
|
28
28
|
export const ADR_STATUSES = ["Proposed", "Accepted", "Rejected", "Deprecated", "Superseded", "Retired"] as const;
|
|
29
29
|
|
|
@@ -3,8 +3,8 @@ import { Console, Effect } from "effect";
|
|
|
3
3
|
import { rootsOf, unresolvedIn, type Judging, type Unresolved } from "./doc-references.ts";
|
|
4
4
|
import { ADR_DIRECTORY, judge, placementOf, placementProblem, speaksToConsumers, type Placement } from "./doc-rules.ts";
|
|
5
5
|
import { readTexts, snapshotAt, stillMissing } from "./doc-snapshot.ts";
|
|
6
|
-
import { changedLines, changedPaths, git, pathsAt, rangeEnds, refArgs } from "
|
|
7
|
-
import { runMain } from "
|
|
6
|
+
import { changedLines, changedPaths, git, pathsAt, rangeEnds, refArgs } from "../core/git.ts";
|
|
7
|
+
import { runMain } from "../core/main.ts";
|
|
8
8
|
import { isLivingDoc, proseFindings, readerOf } from "./prose-matchers.ts";
|
|
9
9
|
|
|
10
10
|
type Finding = {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { Console, Effect, Option } from "effect";
|
|
3
3
|
import { refused, syntaxOf } from "./comments.ts";
|
|
4
|
-
import { changedLines, git, parentOrEmptyTree, refArgs } from "
|
|
5
|
-
import { runMain } from "
|
|
4
|
+
import { changedLines, git, parentOrEmptyTree, refArgs } from "../core/git.ts";
|
|
5
|
+
import { runMain } from "../core/main.ts";
|
|
6
6
|
|
|
7
7
|
export type GateResult = {
|
|
8
8
|
readonly files: number;
|
|
@@ -88,7 +88,7 @@ export function syntaxOf(path: string): Syntax | undefined {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
export function unreadable(path: string): string {
|
|
91
|
-
return `${path} is code the comment checks cannot read: add a comment syntax for .${extensionOf(path)} to
|
|
91
|
+
return `${path} is code the comment checks cannot read: add a comment syntax for .${extensionOf(path)} to src/quality/comment-matchers.ts`;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
const WORD = /[\w$]/;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Effect, Schema } from "effect";
|
|
2
2
|
import { commentsIn, refusalsIn, syntaxOf, unreadable, type Syntax } from "./comment-matchers.ts";
|
|
3
3
|
|
|
4
|
-
export {
|
|
4
|
+
export { syntaxOf } from "./comment-matchers.ts";
|
|
5
5
|
|
|
6
6
|
export class UnreadableCode extends Schema.TaggedError<UnreadableCode>()("UnreadableCode", {
|
|
7
7
|
message: Schema.String,
|
|
8
8
|
}) {}
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
function syntaxFor(path: string): Effect.Effect<Syntax, UnreadableCode> {
|
|
11
11
|
const syntax = syntaxOf(path);
|
|
12
12
|
return syntax ? Effect.succeed(syntax) : Effect.fail(new UnreadableCode({ message: unreadable(path) }));
|
|
13
13
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { Config, Console, Effect, FileSystem, Option, Path, Random, Schema } from "effect";
|
|
3
3
|
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
|
|
4
|
-
import { runMain, Usage } from "
|
|
4
|
+
import { runMain, Usage } from "../core/main.ts";
|
|
5
5
|
import { NAME_SEPARATOR, parseReport, ReportError, reporterArgs, type TestResult } from "./test-report.ts";
|
|
6
6
|
|
|
7
7
|
export type Run = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { Console, Effect, FileSystem, Schema } from "effect";
|
|
3
|
-
import { runMain, Usage } from "
|
|
3
|
+
import { runMain, Usage } from "../core/main.ts";
|
|
4
4
|
|
|
5
5
|
export type Location = {
|
|
6
6
|
readonly start: { readonly line: number; readonly column: number };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { Console, Effect, Schema } from "effect";
|
|
3
|
-
import { commitOf, git, isShallowBoundary, refArgs } from "
|
|
4
|
-
import { runMain } from "
|
|
3
|
+
import { commitOf, git, isShallowBoundary, refArgs } from "../core/git.ts";
|
|
4
|
+
import { runMain } from "../core/main.ts";
|
|
5
5
|
import { TEST_FILE } from "./test-layout.ts";
|
|
6
6
|
|
|
7
7
|
export const QUARANTINE = "tests/quarantine/";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { Console, Effect, FileSystem } from "effect";
|
|
3
|
-
import { runMain, Usage } from "
|
|
3
|
+
import { runMain, Usage } from "../core/main.ts";
|
|
4
4
|
import { type KillRun, parseKillRun, ReportError } from "./mutation-compare.ts";
|
|
5
5
|
|
|
6
6
|
export type KillSets = ReadonlyMap<string, ReadonlySet<string>>;
|
|
@@ -179,7 +179,7 @@ export const bailWarning = (source: string, run: KillRun): Effect.Effect<string
|
|
|
179
179
|
}),
|
|
180
180
|
);
|
|
181
181
|
if (run.bail === "unrecorded") return Effect.succeed(`${source} records no config, so nothing shows whether bail was off: build it with \`bunx stryker run --disableBail\``);
|
|
182
|
-
return Effect.
|
|
182
|
+
return Effect.undefined;
|
|
183
183
|
};
|
|
184
184
|
|
|
185
185
|
export const parseArgs = Effect.fnUntraced(function* (argv: readonly string[]): Effect.fn.Return<Options, Usage> {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { Console, Effect, FileSystem, Path, Schema } from "effect";
|
|
3
|
-
import { git } from "
|
|
4
|
-
import { runMain } from "
|
|
5
|
-
import { ENTRY_POINT, TEST_ENTRY_POINT } from "
|
|
6
|
-
import { identifierName, isRecord, lineOf, parseTypeScript, spanStart, stringValue } from "
|
|
3
|
+
import { git } from "../core/git.ts";
|
|
4
|
+
import { runMain } from "../core/main.ts";
|
|
5
|
+
import { ENTRY_POINT, TEST_ENTRY_POINT } from "../core/gates.ts";
|
|
6
|
+
import { identifierName, isRecord, lineOf, parseTypeScript, spanStart, stringValue } from "../core/swc.ts";
|
|
7
7
|
|
|
8
8
|
export type Violation = {
|
|
9
9
|
readonly file: string;
|
|
@@ -12,12 +12,20 @@ export type Violation = {
|
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
const TESTS = "tests/";
|
|
15
|
+
const UNIT = "tests/unit/";
|
|
15
16
|
const E2E = "tests/e2e/";
|
|
17
|
+
const QUARANTINED = "tests/quarantine/";
|
|
16
18
|
const HELPER_DIRS = ["tests/lib/", "tests/fixtures/"] as const;
|
|
17
19
|
const DATA_DIR = "tests/fixtures/";
|
|
20
|
+
const TEST_TIERS = ["live", "pixel"] as const;
|
|
21
|
+
const SPAWNING_LEVELS = ["e2e", ...TEST_TIERS] as const;
|
|
22
|
+
const LEVELS = ["unit", ...SPAWNING_LEVELS] as const;
|
|
23
|
+
const LEVEL_DIR = new RegExp(`^tests/(?:quarantine/)?(?:${LEVELS.join("|")})/`);
|
|
24
|
+
const QUARANTINE_BELOW_LEVEL = new RegExp(`^tests/(${LEVELS.join("|")})/((?:[^/]+/)*?)quarantine/(.+)$`);
|
|
25
|
+
const LEVEL_NAMES = new Intl.ListFormat("en", { type: "disjunction" }).format(LEVELS);
|
|
18
26
|
|
|
19
27
|
export const TEST_FILE = /(?:[.](?:test|spec)|_test)[.](tsx?)$/;
|
|
20
|
-
const TARGET_FILE =
|
|
28
|
+
const TARGET_FILE = new RegExp(`${LEVEL_DIR.source}(?:[^/]+/)*[^/]+[.]test[.]tsx?$`);
|
|
21
29
|
const TYPESCRIPT = /[.]tsx?$/;
|
|
22
30
|
|
|
23
31
|
const BANNED_MODULES: readonly string[] = [
|
|
@@ -44,16 +52,15 @@ const QUARANTINE = "**/tests/quarantine/**";
|
|
|
44
52
|
const LIVE_TESTS = "**/tests/live/**";
|
|
45
53
|
const PIXEL_TESTS = "**/tests/pixel/**";
|
|
46
54
|
const VENDORED = "repos/**";
|
|
47
|
-
const
|
|
48
|
-
const OUT_OF_PROCESS: readonly string[] = [E2E, ...TEST_TIERS.map((tier) => `${TESTS}${tier}/`)];
|
|
55
|
+
const OUT_OF_PROCESS: readonly string[] = SPAWNING_LEVELS.flatMap((level) => [`${TESTS}${level}/`, `${QUARANTINED}${level}/`]);
|
|
49
56
|
const ACCEPTED_IGNORES: readonly (readonly string[])[] = [
|
|
50
57
|
[QUARANTINE, LIVE_TESTS, PIXEL_TESTS, VENDORED],
|
|
51
58
|
[QUARANTINE, LIVE_TESTS, PIXEL_TESTS],
|
|
52
59
|
];
|
|
53
|
-
export const LAYOUT_CHECK_MARK = "
|
|
60
|
+
export const LAYOUT_CHECK_MARK = "src/testing/test-layout.ts";
|
|
54
61
|
export const LAYOUT_CHECK_BIN = "checks-test-layout";
|
|
55
|
-
const OWN_ENTRY_POINT = `
|
|
56
|
-
const TEST_SCRIPTS: readonly string[] = [TEST_ENTRY_POINT.bin, `bun
|
|
62
|
+
const OWN_ENTRY_POINT = `src/${ENTRY_POINT.script}`;
|
|
63
|
+
const TEST_SCRIPTS: readonly string[] = [TEST_ENTRY_POINT.bin, `bun src/${TEST_ENTRY_POINT.script}`];
|
|
57
64
|
|
|
58
65
|
export class LayoutError extends Schema.TaggedError<LayoutError>()("LayoutError", {
|
|
59
66
|
message: Schema.String,
|
|
@@ -75,7 +82,24 @@ function targetFor(file: string): string {
|
|
|
75
82
|
.replace(/(?:^|\/)__tests__\//, "/")
|
|
76
83
|
.replace(/^\/+/, "");
|
|
77
84
|
const renamed = withoutLeadingDirs.replace(TEST_FILE, ".test.$1");
|
|
78
|
-
|
|
85
|
+
if (LEVEL_DIR.test(renamed)) return renamed;
|
|
86
|
+
if (renamed.startsWith(QUARANTINED)) return `${QUARANTINED}unit/${renamed.slice(QUARANTINED.length)}`;
|
|
87
|
+
return `${UNIT}${renamed.startsWith(TESTS) ? renamed.slice(TESTS.length) : renamed}`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function quarantineTargetFor(file: string): string | undefined {
|
|
91
|
+
const below = QUARANTINE_BELOW_LEVEL.exec(file);
|
|
92
|
+
if (below !== null) return `${QUARANTINED}${below[1]}/${below[2]}${below[3]}`.replace(TEST_FILE, ".test.$1");
|
|
93
|
+
if (file.startsWith(QUARANTINED) && !LEVEL_DIR.test(file)) return targetFor(file);
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function outOfProcessTargetFor(file: string): string {
|
|
98
|
+
if (file.startsWith(QUARANTINED)) {
|
|
99
|
+
const rest = file.slice(QUARANTINED.length);
|
|
100
|
+
return `${QUARANTINED}e2e/${rest.startsWith("unit/") ? rest.slice("unit/".length) : rest}`;
|
|
101
|
+
}
|
|
102
|
+
return `${E2E}${file.slice(file.startsWith(UNIT) ? UNIT.length : TESTS.length)}`;
|
|
79
103
|
}
|
|
80
104
|
|
|
81
105
|
export function placementViolations(files: readonly string[]): readonly Violation[] {
|
|
@@ -91,11 +115,20 @@ export function placementViolations(files: readonly string[]): readonly Violatio
|
|
|
91
115
|
});
|
|
92
116
|
continue;
|
|
93
117
|
}
|
|
118
|
+
const quarantineTarget = quarantineTargetFor(file);
|
|
119
|
+
if (quarantineTarget !== undefined) {
|
|
120
|
+
violations.push({
|
|
121
|
+
file,
|
|
122
|
+
line: undefined,
|
|
123
|
+
message: `a quarantined test keeps its level at tests/quarantine/<level>/, which the default run skips; move it to ${quarantineTarget}`,
|
|
124
|
+
});
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
94
127
|
if (!TARGET_FILE.test(file)) {
|
|
95
128
|
violations.push({
|
|
96
129
|
file,
|
|
97
130
|
line: undefined,
|
|
98
|
-
message: `a test file must live at tests
|
|
131
|
+
message: `a test file must live at tests/<level>/**/*.test.ts, or tests/quarantine/<level>/**/*.test.ts while quarantined, with ${LEVEL_NAMES} as the level; move it to ${target}`,
|
|
99
132
|
});
|
|
100
133
|
}
|
|
101
134
|
}
|
|
@@ -185,7 +218,7 @@ export const isolationViolations = Effect.fn("isolationViolations")(function* (f
|
|
|
185
218
|
violations.push({
|
|
186
219
|
file,
|
|
187
220
|
line: lineOf(source, spanStart(node)),
|
|
188
|
-
message: `a test outside ${E2E} must stay in-process, and this ${use}; move it to ${
|
|
221
|
+
message: `a test outside ${E2E} must stay in-process, and this ${use}; move it to ${outOfProcessTargetFor(file)}`,
|
|
189
222
|
});
|
|
190
223
|
}
|
|
191
224
|
}
|
|
@@ -333,7 +366,7 @@ export function report({ files, violations }: Result): string {
|
|
|
333
366
|
const layout = Effect.gen(function* () {
|
|
334
367
|
const path = yield* Path.Path;
|
|
335
368
|
const root = process.argv[2] ?? process.cwd();
|
|
336
|
-
const result = yield* run(root, path.join(import.meta.dir, "..", "bunfig.toml"));
|
|
369
|
+
const result = yield* run(root, path.join(import.meta.dir, "..", "..", "bunfig.toml"));
|
|
337
370
|
yield* Console.log(report(result));
|
|
338
371
|
return result.violations.length === 0;
|
|
339
372
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Effect, FileSystem, Path, Schema } from "effect";
|
|
2
|
-
import { identifierName, isRecord, lineOf, parseTypeScript, spanStart, stringValue } from "
|
|
2
|
+
import { identifierName, isRecord, lineOf, parseTypeScript, spanStart, stringValue } from "../core/swc.ts";
|
|
3
3
|
|
|
4
4
|
export type Environment = "ci" | "local";
|
|
5
5
|
export type TestTier = "live" | "pixel";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { Config, Console, Effect, FileSystem, Path, Schema } from "effect";
|
|
3
3
|
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
|
|
4
|
-
import { TEST_ENTRY_POINT } from "
|
|
5
|
-
import { runMain, Usage } from "
|
|
4
|
+
import { TEST_ENTRY_POINT } from "../core/gates.ts";
|
|
5
|
+
import { runMain, Usage } from "../core/main.ts";
|
|
6
6
|
import { readSkipDeclarations, type Environment, type SkipDeclaration, type TestTier } from "./test-skips.ts";
|
|
7
7
|
import { NAME_SEPARATOR, parseReport, ReportError, reporterArgs, type TestResult } from "./test-report.ts";
|
|
8
8
|
|
package/CONTRIBUTING.md
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
# Contribute to checks
|
|
2
|
-
|
|
3
|
-
Whoever changes the kit follows these steps in a clone of this repository, before opening a pull request and when cutting a release.
|
|
4
|
-
|
|
5
|
-
## Before you begin
|
|
6
|
-
|
|
7
|
-
- Bun at the version `.bun-version` pins, since CI builds `dist/` with it and another version emits different bytes.
|
|
8
|
-
|
|
9
|
-
## Check a change
|
|
10
|
-
|
|
11
|
-
To check a change the way CI does:
|
|
12
|
-
|
|
13
|
-
1. Run `bun install`.
|
|
14
|
-
1. Run `bun run build`, which rewrites the files it generates, as Regenerate what is committed lists.
|
|
15
|
-
1. Run `bun run lint`, which runs oxlint, the kit's own gates through `scripts/lint.ts`, and the dependency cruise.
|
|
16
|
-
1. Run `bun run typecheck`.
|
|
17
|
-
1. Run `bun run test`, which runs the suite through `scripts/test.ts`.
|
|
18
|
-
|
|
19
|
-
Declare each skip with `skipReason(reason, name)` beside the native Bun test call, as [checks-test](docs/gates/checks-test.md) says.
|
|
20
|
-
|
|
21
|
-
CI runs the commands in `.github/workflows/ci.yml` and lints the pull request title in `.github/workflows/commitlint.yml`.
|
|
22
|
-
|
|
23
|
-
## Regenerate what is committed
|
|
24
|
-
|
|
25
|
-
Each generated file is committed, and lint, the suite or CI's diff after the build fails on one left stale.
|
|
26
|
-
|
|
27
|
-
To regenerate after an edit:
|
|
28
|
-
|
|
29
|
-
1. After editing `effect-channel/`, `readability/` or the templates in `scripts/doc-templates.ts`, run `bun run build`.
|
|
30
|
-
It rewrites `dist/` and `templates/`.
|
|
31
|
-
1. After editing anything a generated block names as its source in its opening marker, run `bun run build`, which rewrites every generated block.
|
|
32
|
-
1. Commit what the command rewrote in the same commit as the edit.
|
|
33
|
-
|
|
34
|
-
## Release a version
|
|
35
|
-
|
|
36
|
-
A release is a tag on `main`, which the `release` workflow publishes through npm trusted publishing, so no token is stored anywhere and GitHub mints the publish credential for each run.
|
|
37
|
-
|
|
38
|
-
To release a version:
|
|
39
|
-
|
|
40
|
-
1. In a pull request that holds only the release, bump `version` in `package.json` and run `bun run build`.
|
|
41
|
-
The build writes the version's section into `CHANGELOG.md` from the conventional commits since the last release, so the changelog is never edited by hand.
|
|
42
|
-
1. Commit both as `chore: release <version>` and title the pull request the same.
|
|
43
|
-
The squash merge lands the title as the commit's subject, and a `feat` or `fix` title would add an entry the committed changelog lacks.
|
|
44
|
-
1. Merge `main` into the release branch and run `bun run build` again right before it merges, committing what it rewrote, since the build lists a commit merged in after the bump under the release.
|
|
45
|
-
Never rebase the release branch.
|
|
46
|
-
1. Once it merges, tag that commit on `main` with the version and push the tag, since the version bump commit closes the release:
|
|
47
|
-
|
|
48
|
-
```sh
|
|
49
|
-
tag="v$(bun -p 'require("./package.json").version')"
|
|
50
|
-
git tag "$tag" && git push origin "$tag"
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
1. Watch the `release` workflow.
|
|
54
|
-
It refuses a tag off `main` or one that disagrees with `package.json`.
|
|
55
|
-
It reruns the build, the check that the build changed no committed file, lint, typecheck and the suite before it publishes to npm.
|
|
56
|
-
After npm publish succeeds, the workflow creates or updates the GitHub release with the matching `CHANGELOG.md` section.
|
|
57
|
-
|
|
58
|
-
`publishConfig.access` in `package.json` is what makes the scoped package public.
|
|
59
|
-
npm attaches a trusted publisher only to a package that already exists, so a package's first version goes out by hand.
|
|
60
|
-
That is `npm publish` from the tagged commit as `avi2dg`, then adding the trusted publisher in the package's npm settings, with the repository `avi2d/checks` and the workflow `release.yml`.
|
|
61
|
-
That first tag's `release` run fails on the already-published version, and every later tag publishes through the workflow.
|
|
62
|
-
|
|
63
|
-
## Find where a change goes
|
|
64
|
-
|
|
65
|
-
To place a change:
|
|
66
|
-
|
|
67
|
-
1. Find the path it belongs under:
|
|
68
|
-
|
|
69
|
-
| Path | What it holds |
|
|
70
|
-
| --- | --- |
|
|
71
|
-
| `scripts/` | every bin, and the modules they share |
|
|
72
|
-
| `effect-channel/` | the oxlint plugin with the Effect error-channel rules |
|
|
73
|
-
| `readability/` | the oxlint plugin with the readability rules |
|
|
74
|
-
| `dist/` | the committed oxlint plugin bundles |
|
|
75
|
-
| `presets/` | the Effect rule blocks consumers copy into native configs |
|
|
76
|
-
| `templates/` | one template per kind of doc file, which `bun run build` renders |
|
|
77
|
-
| `CHANGELOG.md` | every release, which `bun run build` writes from the conventional commits |
|
|
78
|
-
| `tests/` | the suite, with the tests that spawn a process under `tests/e2e/` |
|
|
79
|
-
| `docs/gates/` | one reference page per bin |
|
|
80
|
-
| `docs/configs/` | one reference page per shipped config a bin does not own |
|
|
81
|
-
| `docs/design.md` | why the kit is shaped the way it is |
|
|
82
|
-
| the root configs | `oxlintrc.json`, `tsconfig.effect.json`, `bunfig.toml`, `commitlint.config.js`, `dependency-cruiser.config.js`, `stryker.preset.js`, which a consuming repository extends or copies |
|
|
83
|
-
|
|
84
|
-
1. Change the page under `docs/` that describes the behaviour in the same commit as the behaviour.
|
|
85
|
-
A new bin gets its page under `docs/gates/`, and the suite fails until it has one.
|
|
86
|
-
|
|
87
|
-
This repository holds itself to the kit, with two exceptions of its own.
|
|
88
|
-
Its `.dependency-cruiser.cjs` redeclares `no-orphans` with each plugin entry added to its `pathNot`.
|
|
89
|
-
Its `.oxlintrc.json` lifts `effect-channel/no-throw` from `scripts/comment-matchers.ts`, whose synchronous `refused()` a host loads without `node_modules`.
|
|
90
|
-
|
|
91
|
-
## Related topics
|
|
92
|
-
|
|
93
|
-
- [checks](README.md)
|
|
94
|
-
- [Why it is shaped this way](docs/design.md)
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
kind: reference
|
|
3
|
-
audience: consumers
|
|
4
|
-
---
|
|
5
|
-
# checks-backtest
|
|
6
|
-
|
|
7
|
-
`checks-backtest` is the report of what the comment check would have refused at each recent commit, and a reader looks it up to measure a repository's own history before adopting the check.
|
|
8
|
-
|
|
9
|
-
## What it checks
|
|
10
|
-
|
|
11
|
-
It checks nothing and fails nothing.
|
|
12
|
-
It walks recent first-parent commits, prints a row per commit that touches code and then the totals, attributes only the refusals each commit introduced, and counts new comment text as a share of added lines.
|
|
13
|
-
A row holds the commit, its added lines, its added comment lines, its refusals and its subject.
|
|
14
|
-
Each commit that introduced a refusal is then listed with its refusals.
|
|
15
|
-
|
|
16
|
-
## What it reads
|
|
17
|
-
|
|
18
|
-
It reads each commit and its first parent, and the files each commit changes that have a comment syntax in `scripts/comment-matchers.ts`.
|
|
19
|
-
`generated/`, `vendor/`, `repos/`, `node_modules/` and `dist/` are out of reach, so the figures are authored code.
|
|
20
|
-
|
|
21
|
-
## Arguments
|
|
22
|
-
|
|
23
|
-
```sh
|
|
24
|
-
checks-backtest [commit-count]
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
`commit-count` is the number of first-parent commits it walks, 60 when absent.
|
|
28
|
-
|
|
29
|
-
## Exit codes
|
|
30
|
-
|
|
31
|
-
| Code | When |
|
|
32
|
-
| --- | --- |
|
|
33
|
-
| 0 | the report printed |
|
|
34
|
-
| 2 | it is given more than one argument, or git cannot read the history, as with a count git refuses |
|
|
35
|
-
|
|
36
|
-
## Sample output
|
|
37
|
-
|
|
38
|
-
```
|
|
39
|
-
fe12189 9 0 0 feat(scripts): hold CONTRIBUTING.md to the how-to template
|
|
40
|
-
086bdd0 1205 1 0 feat(scripts): add checks-docs gate holding doc files to s
|
|
41
|
-
dc23b30 21 1 0 fix: release 0.12.0 and read local exports in checks-featu
|
|
42
|
-
bcb4e3b 1252 4 0 feat(scripts): add size budget, feature-owner rules and ch
|
|
43
|
-
4 commits touching code, 2487 added lines
|
|
44
|
-
comment lines added: 6 (0.2% of added lines)
|
|
45
|
-
refusals introduced: 0
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
## Opting out
|
|
49
|
-
|
|
50
|
-
Nothing runs it but a person who wants the figures.
|
|
51
|
-
|
|
52
|
-
## Related topics
|
|
53
|
-
|
|
54
|
-
- [checks-comment-gate](checks-comment-gate.md)
|