@avi2dg/checks 0.35.1 → 0.37.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 CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  Every release of `@avi2dg/checks`, newest first, written by the release from its conventional commits.
4
4
 
5
+ ## 0.37.0
6
+
7
+ Released 2026-10-09.
8
+
9
+ ### Features
10
+
11
+ - ship typed defineConfig builders for oxlint, knip and dependency-cruiser [#138](https://github.com/avi2d/checks/pull/138)
12
+
13
+ ## 0.36.0
14
+
15
+ Released 2026-10-08.
16
+
17
+ ### Features
18
+
19
+ - **testing:** add unitTestFiles so each mutant loads only the unit tests that cover it [#132](https://github.com/avi2d/checks/pull/132)
20
+
5
21
  ## 0.35.1
6
22
 
7
23
  Released 2026-10-07.
package/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # checks
2
2
 
3
3
  `@avi2dg/checks` is the kit of deterministic checks a TypeScript repository installs to hold its code, tests, commits, CI wiring and docs to one shared standard.
4
- It ships the lint gates `checks-lint` runs over each pull request, the test runners, and the configs a repository extends for oxlint, tsc, dependency-cruiser, commitlint, bun and Stryker.
4
+ It ships the lint gates `checks-lint` runs over each pull request, the test runners, and a `defineConfig` for oxlint, Knip and dependency-cruiser.
5
+ It also ships the configs a repository extends for tsc, commitlint, bun and Stryker.
5
6
  Each repository owns its workflows and native tool configs, as [Native settings](docs/configs/native-settings.md) maps.
6
7
 
7
8
  ## Before you begin
@@ -42,13 +43,12 @@ To consume the kit from a repository:
42
43
 
43
44
  <!-- end generated install -->
44
45
 
45
- 1. Extend the oxlint base in `.oxlintrc.json`, restating `plugins`:
46
+ 1. Write `oxlint.config.ts` with the kit's builder, saying whether the sources are Effect programs:
46
47
 
47
- ```json
48
- {
49
- "extends": ["./node_modules/@avi2dg/checks/oxlintrc.json"],
50
- "plugins": ["typescript", "oxc", "eslint", "import"]
51
- }
48
+ ```ts
49
+ import { defineConfig } from "@avi2dg/checks/oxlint";
50
+
51
+ export default defineConfig({ effect: true });
52
52
  ```
53
53
 
54
54
  1. Keep oxlint out of `node_modules/` through `.gitignore`:
@@ -71,19 +71,27 @@ To consume the kit from a repository:
71
71
  cp node_modules/@avi2dg/checks/bunfig.toml bunfig.toml
72
72
  ```
73
73
 
74
- 1. Name the repository's entry files in `knip.config.ts`, spreading the kit's Knip base:
74
+ 1. Name the files nothing imports in `knip.config.ts`:
75
+
76
+ ```ts
77
+ import { defineConfig } from "@avi2dg/checks/knip";
78
+
79
+ export default defineConfig({ entry: ["src/index.ts"] });
80
+ ```
81
+
82
+ 1. Write `dependency-cruiser.config.ts` only when the kit's import rules need an entry point or a boundary of the repository's own:
75
83
 
76
84
  ```ts
77
- import base from "@avi2dg/checks/knip-base.json";
85
+ import { defineConfig } from "@avi2dg/checks/dependency-cruiser";
78
86
 
79
- export default { ...base, entry: ["src/index.ts", "tests/**/*.test.ts"] };
87
+ export default defineConfig({ orphans: ["^src/bin[.]ts$"] });
80
88
  ```
81
89
 
82
90
  1. Add scripts to `package.json`, replacing the build entry with the repository's own build command:
83
91
 
84
92
  ```json
85
- "build": "bun build src/index.ts --outdir dist --target node",
86
- "lint": "oxlint --type-aware && checks-lint",
93
+ "build": "bun build src/index.ts --outdir dist --target node && checks-effect-scope",
94
+ "lint": "oxlint && checks-lint",
87
95
  "typecheck": "tsc --noEmit && effect-tsgo diagnostics --project tsconfig.json --format text --strict",
88
96
  "test": "checks-test"
89
97
  ```
@@ -111,6 +119,7 @@ To consume the kit from a repository:
111
119
 
112
120
  Add a pull request title lint step in another workflow using `./node_modules/.bin/commitlint`.
113
121
  A private repository sets `runs-on: ${{ vars.CI_RUNS_ON || 'ubuntu-latest' }}` on each job, as [checks-ci-wiring](docs/gates/checks-ci-wiring.md#runners) requires.
122
+ `checks-effect-scope` writes the Effect paths of `oxlint.config.ts` and the kit's severities into `tsconfig.json`, as [The Effect rules](docs/configs/effect-rules.md#language-service) says.
114
123
  `bun run lint` then ends with `checks-lint: <count> gate(s) pass`.
115
124
 
116
125
  ## What runs
@@ -129,6 +138,7 @@ The table groups the gates by vector, the part of a repository each one judges.
129
138
  | complexity | [`checks-exports`](docs/gates/checks-exports.md) | the range | a repository tracking `*.ts` or `*.tsx` |
130
139
  | quality | [`checks-lint-coverage`](docs/gates/checks-lint-coverage.md) | the working tree | a repository tracking `*.ts` or `*.tsx` or `*.astro` |
131
140
  | quality | [`checks-comment-gate`](docs/gates/checks-comment-gate.md) | the range | every repository |
141
+ | quality | [`checks-effect-scope`](docs/gates/checks-effect-scope.md) | the working tree | a repository tracking `oxlint.config.ts` |
132
142
  | quality | [`checks-frontend-syntax`](docs/gates/checks-frontend-syntax.md) | the working tree | a repository tracking `frontend-syntax.json` |
133
143
  | testing | [`checks-test-layout`](docs/gates/checks-test-layout.md) | the working tree | a repository tracking `*.ts` or `*.tsx` |
134
144
  | testing | [`checks-quarantine-clock`](docs/gates/checks-quarantine-clock.md) | the range | every repository |
@@ -136,6 +146,7 @@ The table groups the gates by vector, the part of a repository each one judges.
136
146
  | delivery | [`checks-commit-identity`](docs/gates/checks-commit-identity.md) | the range | every repository |
137
147
  | delivery | [`checks-ci-wiring`](docs/gates/checks-ci-wiring.md) | the working tree | every repository |
138
148
  | delivery | [`checks-secrets`](docs/gates/checks-secrets.md) | the range | every repository |
149
+ | dependencies | [`checks-imports`](docs/gates/checks-imports.md) | the working tree | a repository tracking `*.ts` or `*.tsx` |
139
150
  | dependencies | [`checks-advisories`](docs/gates/checks-advisories.md) | the range | a repository tracking `bun.lock` |
140
151
 
141
152
  <!-- end generated gates -->
@@ -145,7 +156,7 @@ These bins run on their own:
145
156
  - [`checks-test`](docs/gates/checks-test.md) runs the suite as `scripts.test` and refuses a skip without a reason at its test site.
146
157
  - [`checks-flake`](docs/gates/checks-flake.md) runs the suite on a schedule and records the seeds a flaky test fails with.
147
158
  - [`checks-mutation`](docs/gates/checks-mutation.md) runs Stryker for scoped checks and refuses a full run outside CI.
148
- - [`checks-mutation-compare`](docs/gates/checks-mutation-compare.md) holds every mutant in a pull request to no regression.
159
+ - [`checks-mutation-compare`](docs/gates/checks-mutation-compare.md) compares the mutants of two reports and lists each one that regresses.
149
160
  - [`checks-subsumed-tests`](docs/gates/checks-subsumed-tests.md) lists each test another test subsumes in a mutation run.
150
161
  - [`checks-changelog`](docs/gates/checks-changelog.md) writes the pending release into `CHANGELOG.md` from the conventional commits since the last release.
151
162
  - [`checks-release-notes`](docs/gates/checks-release-notes.md) writes one `CHANGELOG.md` section to a file for a GitHub release.
@@ -156,7 +167,7 @@ These bins run on their own:
156
167
  - [`checks-browser`](docs/gates/checks-browser.md) opens a product's built pages in Chrome and fails on the layout, keyboard, motion, accessibility, nesting and asset checks its `browser-checks.json` declares.
157
168
 
158
169
  `checks-lint` has [its own page](docs/gates/checks-lint.md), which says which range it resolves.
159
- The oxlint base, the dependency-cruiser base and the commitlint config run through their own tools, as the pages under Related topics say.
170
+ oxlint and commitlint run through their own tools, as the pages under Related topics say.
160
171
 
161
172
  ## Upgrade
162
173
 
@@ -164,7 +175,8 @@ To move a repository to a newer release of the kit:
164
175
 
165
176
  1. Run the install line again, which moves the kit to its newest release and the peers to the versions it pins.
166
177
  A repository that opted in to `checks-browser` or `checks-frontend-syntax` also reruns the `bun add` line on its page.
167
- 1. Review the Effect overrides in `.oxlintrc.json` and `tsconfig.json` when a release changes their presets.
178
+ 1. Run `bun run build`, whose `checks-effect-scope` rewrites the Effect override and the kit's severities in `tsconfig.json` when a release changes them.
179
+ A severity key the release drops stays in `tsconfig.json` until the repository removes it.
168
180
  1. Copy `node_modules/@avi2dg/checks/bunfig.toml` over `bunfig.toml` again, since `checks-test-layout` compares the copy with the installed preset.
169
181
  1. Run `bun run lint`, `bun run typecheck` and `bun run test`.
170
182
 
@@ -183,11 +195,11 @@ Every path is relative to the installed package, `node_modules/@avi2dg/checks/`.
183
195
  | `docs/` | a reference page per bin and per shared config, and why the kit is shaped this way |
184
196
  | `bunfig.toml` | the bunfig preset a repository copies |
185
197
  | `commitlint.config.js` | the shared commitlint config |
186
- | `dependency-cruiser.config.js` | the shared dependency-cruiser base |
187
- | `knip-base.json` | the Knip base a repository's configuration imports |
188
- | `src/` | every bin, which a package script calls by its `checks-` name, the modules the bins import, and the Effect rule blocks under `src/quality/presets/` |
189
- | `dist/` | the compiled oxlint plugins and the doc templates, one template per kind of doc file |
190
- | `oxlintrc.json` | the oxlint base config `.oxlintrc.json` extends |
198
+ | `dependency-cruiser.config.js` | the kit's dependency-cruiser rules as a base a `.dependency-cruiser.cjs` extends by path, which the build writes |
199
+ | `knip-base.json` | the Knip `include` setting, for a configuration that spreads it |
200
+ | `src/` | every bin, which a package script calls by its `checks-` name, the modules the bins import, and the Effect language service severities under `src/quality/presets/` |
201
+ | `dist/` | the compiled oxlint plugins, the config builders `@avi2dg/checks/oxlint`, `@avi2dg/checks/knip` and `@avi2dg/checks/dependency-cruiser` resolve to, and the doc templates, one template per kind of doc file |
202
+ | `oxlintrc.json` | the oxlint `base` as a config a `.oxlintrc.json` extends by path, which the build writes |
191
203
  | `stryker.preset.js` | the Stryker mutation-testing preset, which refuses a full run outside CI |
192
204
  | `tsconfig.effect.json` | the tsconfig fragment with the shared compiler options and the Effect language-service block |
193
205
  | `ts-reset.d.ts` | the two ts-reset rules `tsconfig.effect.json` lists in `files` |
@@ -1,83 +1,115 @@
1
1
  export default {
2
- forbidden: [
2
+ "forbidden": [
3
3
  {
4
- name: "no-circular",
5
- severity: "error",
6
- comment: "A circular relationship never has a single entry point, so invert one side.",
7
- from: {},
8
- to: { circular: true },
4
+ "name": "no-circular",
5
+ "severity": "error",
6
+ "comment": "A circular relationship never has a single entry point, so invert one side.",
7
+ "from": {},
8
+ "to": {
9
+ "circular": true
10
+ }
9
11
  },
10
12
  {
11
- name: "no-orphans",
12
- severity: "error",
13
- comment:
14
- "Nothing reaches this module. Use it, remove it, or exempt the entry point by name in your own config.",
15
- from: {
16
- orphan: true,
17
- pathNot: [
13
+ "name": "no-orphans",
14
+ "severity": "error",
15
+ "comment": "Nothing reaches this module. Use it, remove it, or exempt the entry point by name in your own config.",
16
+ "from": {
17
+ "orphan": true,
18
+ "pathNot": [
18
19
  "(^|/)[.][^/]+[.](?:js|cjs|mjs|ts|cts|mts|json)$",
19
20
  "[.]d[.]ts$",
20
21
  "(^|/)tsconfig[.]json$",
21
22
  "(^|/)(?:babel|webpack)[.]config[.](?:js|cjs|mjs|ts|cts|mts|json)$",
22
23
  "(^|/)[^/]*[.]config[.](?:js|cjs|mjs|ts|cts|mts)$",
23
- "[.](?:spec|test)[.](?:js|mjs|cjs|jsx|ts|mts|cts|tsx)$",
24
- ],
24
+ "[.](?:spec|test)[.](?:js|mjs|cjs|jsx|ts|mts|cts|tsx)$"
25
+ ]
25
26
  },
26
- to: {},
27
+ "to": {}
27
28
  },
28
29
  {
29
- name: "not-to-dev-dep",
30
- severity: "error",
31
- comment:
32
- "Shipped source cannot rely on a package that is absent in production. Move it to dependencies or peerDependencies, or keep the import in a test or config file.",
33
- from: {
34
- pathNot: [
30
+ "name": "not-to-dev-dep",
31
+ "severity": "error",
32
+ "comment": "Shipped source cannot rely on a package that is absent in production. Move it to dependencies or peerDependencies, or keep the import in a test or config file.",
33
+ "from": {
34
+ "pathNot": [
35
35
  "[.](?:spec|test)[.](?:js|mjs|cjs|jsx|ts|mts|cts|tsx)$",
36
- "(^|/)[^/]*[.]config[.](?:js|cjs|mjs|ts|cts|mts)$",
37
- ],
38
- },
39
- to: {
40
- dependencyTypes: ["npm-dev"],
41
- dependencyTypesNot: ["type-only", "npm-peer"],
36
+ "(^|/)[^/]*[.]config[.](?:js|cjs|mjs|ts|cts|mts)$"
37
+ ]
42
38
  },
39
+ "to": {
40
+ "dependencyTypes": [
41
+ "npm-dev"
42
+ ],
43
+ "dependencyTypesNot": [
44
+ "type-only",
45
+ "npm-peer"
46
+ ]
47
+ }
43
48
  },
44
49
  {
45
- name: "no-non-package-json",
46
- severity: "error",
47
- comment:
48
- "The import resolves to an installed package the nearest package.json does not declare, so it holds only while something else keeps it hoisted. Declare it in dependencies, devDependencies or peerDependencies.",
49
- from: {},
50
- // dependency-cruiser reads every package.json key holding "ependencies" as a list, so peerDependenciesMeta tags a declared peer npm-no-pkg too.
51
- to: { dependencyTypes: ["npm-no-pkg", "npm-unknown"], dependencyTypesNot: ["npm", "npm-dev", "npm-optional", "npm-peer"] },
50
+ "name": "no-non-package-json",
51
+ "severity": "error",
52
+ "comment": "The import resolves to an installed package the nearest package.json does not declare, so it holds only while something else keeps it hoisted. Declare it in dependencies, devDependencies or peerDependencies.",
53
+ "from": {},
54
+ "to": {
55
+ "dependencyTypes": [
56
+ "npm-no-pkg",
57
+ "npm-unknown"
58
+ ],
59
+ "dependencyTypesNot": [
60
+ "npm",
61
+ "npm-dev",
62
+ "npm-optional",
63
+ "npm-peer"
64
+ ]
65
+ }
52
66
  },
53
67
  {
54
- name: "not-to-unresolvable",
55
- severity: "error",
56
- comment: "Nothing installed answers to this specifier. Install the package or fix the path.",
57
- from: {},
58
- to: { couldNotResolve: true },
68
+ "name": "not-to-unresolvable",
69
+ "severity": "error",
70
+ "comment": "Nothing installed answers to this specifier. Install the package or fix the path.",
71
+ "from": {},
72
+ "to": {
73
+ "couldNotResolve": true
74
+ }
59
75
  },
60
76
  {
61
- name: "no-deep-imports",
62
- severity: "error",
63
- comment:
64
- "The specifier reaches past the package name into a subpath its exports map does not publish. Depend on a published entry instead.",
65
- from: {},
66
- to: {
67
- couldNotResolve: true,
68
- path: "^(@[^/]+/[^/]+|[^@./#][^/]*)/",
69
- },
70
- },
77
+ "name": "no-deep-imports",
78
+ "severity": "error",
79
+ "comment": "The specifier reaches past the package name into a subpath its exports map does not publish. Depend on a published entry instead.",
80
+ "from": {},
81
+ "to": {
82
+ "couldNotResolve": true,
83
+ "path": "^(@[^/]+/[^/]+|[^@./#][^/]*)/"
84
+ }
85
+ }
71
86
  ],
72
- options: {
73
- parser: "swc",
74
- builtInModules: { add: ["bun"] },
75
- doNotFollow: { path: ["node_modules"] },
76
- // The cruise follows the repos/ links without this.
77
- exclude: { path: "^repos/" },
78
- enhancedResolveOptions: {
79
- exportsFields: ["exports"],
80
- conditionNames: ["types", "import", "require", "node", "default"],
87
+ "options": {
88
+ "parser": "swc",
89
+ "builtInModules": {
90
+ "add": [
91
+ "bun"
92
+ ]
93
+ },
94
+ "doNotFollow": {
95
+ "path": [
96
+ "node_modules"
97
+ ]
98
+ },
99
+ "exclude": {
100
+ "path": "^repos/"
81
101
  },
82
- },
102
+ "enhancedResolveOptions": {
103
+ "exportsFields": [
104
+ "exports"
105
+ ],
106
+ "conditionNames": [
107
+ "types",
108
+ "import",
109
+ "require",
110
+ "node",
111
+ "default"
112
+ ]
113
+ }
114
+ }
83
115
  };
@@ -0,0 +1,15 @@
1
+ import type { IConfiguration, ICruiseOptions } from "dependency-cruiser";
2
+ type CruiseOptions = Omit<ICruiseOptions, "builtInModules"> & {
3
+ readonly builtInModules?: Partial<NonNullable<ICruiseOptions["builtInModules"]>>;
4
+ };
5
+ export type CruiseConfig = Omit<IConfiguration, "extends" | "options"> & {
6
+ readonly options?: CruiseOptions;
7
+ };
8
+ export type ChecksCruiseConfig = CruiseConfig & {
9
+ readonly devOnly?: readonly string[];
10
+ readonly orphans?: readonly string[];
11
+ };
12
+ export declare const DEV_ONLY: readonly ["^tests/"];
13
+ export declare const base: CruiseConfig;
14
+ export declare function defineConfig({ devOnly, orphans, forbidden, options, ...rest }?: ChecksCruiseConfig): CruiseConfig;
15
+ export {};
@@ -0,0 +1,126 @@
1
+ // src/quality/presets/dependency-cruiser.ts
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ var DEV_ONLY = ["^tests/"];
4
+ var TEST_FILES = "[.](?:spec|test)[.](?:js|mjs|cjs|jsx|ts|mts|cts|tsx)$";
5
+ var MANIFEST = "package.json";
6
+ var CONFIG_FILES = "(^|/)[^/]*[.]config[.](?:js|cjs|mjs|ts|cts|mts)$";
7
+ var BASE = {
8
+ forbidden: [
9
+ {
10
+ name: "no-circular",
11
+ severity: "error",
12
+ comment: "A circular relationship never has a single entry point, so invert one side.",
13
+ from: {},
14
+ to: { circular: true }
15
+ },
16
+ {
17
+ name: "no-orphans",
18
+ severity: "error",
19
+ comment: "Nothing reaches this module. Use it, remove it, or exempt the entry point by name in your own config.",
20
+ from: {
21
+ orphan: true,
22
+ pathNot: [
23
+ "(^|/)[.][^/]+[.](?:js|cjs|mjs|ts|cts|mts|json)$",
24
+ "[.]d[.]ts$",
25
+ "(^|/)tsconfig[.]json$",
26
+ "(^|/)(?:babel|webpack)[.]config[.](?:js|cjs|mjs|ts|cts|mts|json)$",
27
+ CONFIG_FILES,
28
+ TEST_FILES
29
+ ]
30
+ },
31
+ to: {}
32
+ },
33
+ {
34
+ name: "not-to-dev-dep",
35
+ severity: "error",
36
+ comment: "Shipped source cannot rely on a package that is absent in production. Move it to dependencies or peerDependencies, or keep the import in a test or config file.",
37
+ from: { pathNot: [TEST_FILES, CONFIG_FILES] },
38
+ to: { dependencyTypes: ["npm-dev"], dependencyTypesNot: ["type-only", "npm-peer"] }
39
+ },
40
+ {
41
+ name: "no-non-package-json",
42
+ severity: "error",
43
+ comment: "The import resolves to an installed package the nearest package.json does not declare, so it holds only while something else keeps it hoisted. Declare it in dependencies, devDependencies or peerDependencies.",
44
+ from: {},
45
+ to: { dependencyTypes: ["npm-no-pkg", "npm-unknown"], dependencyTypesNot: ["npm", "npm-dev", "npm-optional", "npm-peer"] }
46
+ },
47
+ {
48
+ name: "not-to-unresolvable",
49
+ severity: "error",
50
+ comment: "Nothing installed answers to this specifier. Install the package or fix the path.",
51
+ from: {},
52
+ to: { couldNotResolve: true }
53
+ },
54
+ {
55
+ name: "no-deep-imports",
56
+ severity: "error",
57
+ comment: "The specifier reaches past the package name into a subpath its exports map does not publish. Depend on a published entry instead.",
58
+ from: {},
59
+ to: { couldNotResolve: true, path: "^(@[^/]+/[^/]+|[^@./#][^/]*)/" }
60
+ }
61
+ ],
62
+ options: {
63
+ parser: "swc",
64
+ builtInModules: { add: ["bun"] },
65
+ doNotFollow: { path: ["node_modules"] },
66
+ exclude: { path: "^repos/" },
67
+ enhancedResolveOptions: {
68
+ exportsFields: ["exports"],
69
+ conditionNames: ["types", "import", "require", "node", "default"]
70
+ }
71
+ }
72
+ };
73
+ var base = BASE;
74
+ function asList(value) {
75
+ if (value === undefined)
76
+ return [];
77
+ return typeof value === "string" ? [value] : value;
78
+ }
79
+ function widened(rule, extra) {
80
+ if (extra.length === 0 || !("pathNot" in rule.from))
81
+ return rule;
82
+ return { ...rule, from: { ...rule.from, pathNot: [...asList(rule.from.pathNot), ...extra] } };
83
+ }
84
+ function kitRule(rule, devOnly, orphans) {
85
+ if (rule.name === "not-to-dev-dep")
86
+ return widened(rule, devOnly);
87
+ if (rule.name === "no-orphans")
88
+ return widened(rule, orphans);
89
+ return rule;
90
+ }
91
+ function excludeObject(exclude) {
92
+ if (exclude === undefined || typeof exclude === "string" || Array.isArray(exclude))
93
+ return { path: [...asList(exclude)] };
94
+ return exclude;
95
+ }
96
+ function joinedExclude(exclude) {
97
+ const own = excludeObject(exclude);
98
+ return { ...own, path: [...asList(BASE.options.exclude.path), ...asList(own.path)] };
99
+ }
100
+ function declaresAstro(manifest) {
101
+ if (typeof manifest !== "object" || manifest === null)
102
+ return false;
103
+ return ["dependencies", "devDependencies"].some((field) => {
104
+ const declared = Reflect.get(manifest, field);
105
+ return typeof declared === "object" && declared !== null && "astro" in declared;
106
+ });
107
+ }
108
+ function dependsOnAstro() {
109
+ return existsSync(MANIFEST) && declaresAstro(JSON.parse(readFileSync(MANIFEST, "utf8")));
110
+ }
111
+ function defineConfig({ devOnly = DEV_ONLY, orphans = [], forbidden = [], options, ...rest } = {}) {
112
+ const named = new Set(forbidden.map(({ name }) => name));
113
+ if (dependsOnAstro())
114
+ named.add("no-orphans");
115
+ const kitRules = BASE.forbidden.filter(({ name }) => !named.has(name)).map((rule) => kitRule(rule, devOnly, orphans));
116
+ return {
117
+ ...rest,
118
+ forbidden: [...kitRules, ...forbidden],
119
+ options: { ...BASE.options, ...options, exclude: joinedExclude(options?.exclude) }
120
+ };
121
+ }
122
+ export {
123
+ DEV_ONLY,
124
+ base,
125
+ defineConfig
126
+ };
@@ -0,0 +1,6 @@
1
+ import type { KnipConfiguration } from "knip";
2
+ export type ChecksKnipConfig = Omit<KnipConfiguration, "entry" | "include"> & {
3
+ readonly entry: readonly string[];
4
+ };
5
+ export declare const DEFAULT_ENTRY: readonly ["tests/**/*.test.ts", "dependency-cruiser.config.ts"];
6
+ export declare function defineConfig({ entry, ...rest }: ChecksKnipConfig): KnipConfiguration;
@@ -0,0 +1,15 @@
1
+ // src/quality/presets/knip.ts
2
+ var DEFAULT_ENTRY = ["tests/**/*.test.ts", "dependency-cruiser.config.ts"];
3
+ var REFUSED_ENTRY = "@avi2dg/checks/knip: set entry to the files nothing imports, or [] when package.json scripts and tests name them all";
4
+ function isGlobList(value) {
5
+ return Array.isArray(value) && value.every((glob) => typeof glob === "string" && glob !== "");
6
+ }
7
+ function defineConfig({ entry, ...rest }) {
8
+ if (!isGlobList(entry))
9
+ throw new TypeError(REFUSED_ENTRY);
10
+ return { include: ["files"], ...rest, entry: [...new Set([...entry, ...DEFAULT_ENTRY])] };
11
+ }
12
+ export {
13
+ DEFAULT_ENTRY,
14
+ defineConfig
15
+ };
@@ -0,0 +1,33 @@
1
+ import type { OxlintConfig, OxlintOverride } from "oxlint";
2
+ type Globs = readonly [string, ...string[]];
3
+ export type EffectScope = boolean | {
4
+ readonly files?: Globs;
5
+ readonly excludeFiles?: readonly string[];
6
+ };
7
+ export type ChecksConfig = Omit<OxlintConfig, "extends" | "plugins" | "jsPlugins" | "categories"> & {
8
+ readonly effect: EffectScope;
9
+ };
10
+ declare const SIZE_RULES: readonly ["max-lines", "max-lines-per-function", "max-statements", "readability/cognitive-complexity", "max-depth"];
11
+ export type SizeRule = (typeof SIZE_RULES)[number];
12
+ export type SizeLimits = Readonly<Record<SizeRule, number | "off">>;
13
+ export declare const SOURCE_LIMITS: {
14
+ readonly "max-lines": 400;
15
+ readonly "max-lines-per-function": 100;
16
+ readonly "max-statements": 30;
17
+ readonly "readability/cognitive-complexity": 15;
18
+ readonly "max-depth": 4;
19
+ };
20
+ export declare const TEST_LIMITS: {
21
+ readonly "max-lines": 600;
22
+ readonly "max-lines-per-function": "off";
23
+ readonly "max-statements": 50;
24
+ readonly "readability/cognitive-complexity": 15;
25
+ readonly "max-depth": 4;
26
+ };
27
+ export declare const SOURCES: readonly ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"];
28
+ export declare const TESTS: readonly ["tests/**", "**/*.test.ts", "**/*.test.tsx"];
29
+ export declare const base: OxlintConfig;
30
+ export declare function sizeBudget(files: readonly string[], limits: SizeLimits, excludeFiles?: readonly string[]): OxlintOverride;
31
+ export declare function effectRules(files: readonly string[], excludeFiles?: readonly string[]): OxlintOverride;
32
+ export declare function defineConfig({ effect, rules, overrides, options, ...rest }: ChecksConfig): OxlintConfig;
33
+ export {};