@avi2dg/checks 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/README.md +334 -151
- package/dist/feature-rules.js +255 -0
- package/package.json +29 -5
- package/quality.schema.json +191 -1
- package/scripts/doc-outline.ts +215 -0
- package/scripts/doc-rules.ts +176 -0
- package/scripts/doc-templates.ts +203 -0
- package/scripts/docs.ts +90 -0
- package/scripts/feature-owners.ts +142 -0
- package/scripts/gates.ts +3 -0
- package/scripts/git.ts +70 -14
- package/scripts/quality-file.ts +135 -4
- package/scripts/quality.ts +10 -2
- package/scripts/size-budget.ts +158 -0
- package/templates/adr.md +29 -0
- package/templates/agents.md +17 -0
- package/templates/changelog.md +37 -0
- package/templates/claude.md +2 -0
- package/templates/explanation.md +15 -0
- package/templates/how-to.md +33 -0
- package/templates/readme.md +43 -0
- package/templates/reference.md +15 -0
- package/templates/tutorial.md +31 -0
package/README.md
CHANGED
|
@@ -8,15 +8,25 @@ run that records the seeds a failing test fails with, the oxlint base
|
|
|
8
8
|
config, the tsconfig fragment with the Effect language-service block,
|
|
9
9
|
the `quality.json` schema with the generator that turns its Effect paths
|
|
10
10
|
into oxlint and tsconfig fragments, the shared commitlint config, the
|
|
11
|
-
shared dependency-cruiser base
|
|
11
|
+
shared dependency-cruiser base with the feature-owner rules `quality.json`
|
|
12
|
+
compiles into it, the test-layout check with its bunfig preset, the commit-identity check, the
|
|
12
13
|
comment gate with its backtest, the oxlint suppressions ratchet, the
|
|
14
|
+
size budget, the feature-owner change signal and proof check, the
|
|
13
15
|
Stryker mutation-testing preset with its no-regression comparator, the
|
|
14
|
-
CI-wiring check,
|
|
15
|
-
|
|
16
|
+
CI-wiring check, a template for each kind of doc file with the gate that
|
|
17
|
+
holds each doc file to its template, and the Effect error-channel plugin
|
|
18
|
+
compiled to JavaScript.
|
|
16
19
|
|
|
17
20
|
Published as `@avi2dg/checks` on the public npm registry.
|
|
18
21
|
|
|
19
|
-
##
|
|
22
|
+
## Before you begin
|
|
23
|
+
|
|
24
|
+
- Bun, which runs every bin.
|
|
25
|
+
The kit is tested on the version its own `.bun-version` pins.
|
|
26
|
+
- A git repository, whose history the range gates read.
|
|
27
|
+
- The peer versions the install line below pins, which `peerDependencies` in `package.json` holds.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
20
30
|
|
|
21
31
|
From the consuming repo:
|
|
22
32
|
|
|
@@ -48,7 +58,7 @@ node_modules/
|
|
|
48
58
|
```
|
|
49
59
|
|
|
50
60
|
`quality.json` at the repository root declares what the repository
|
|
51
|
-
opts into, starting with the commands its CI runs
|
|
61
|
+
opts into, starting with the commands its CI runs. See "Declare policy in the quality file"
|
|
52
62
|
below:
|
|
53
63
|
|
|
54
64
|
```json
|
|
@@ -73,9 +83,9 @@ cp node_modules/@avi2dg/checks/bunfig.toml bunfig.toml
|
|
|
73
83
|
```
|
|
74
84
|
|
|
75
85
|
`checks-test` runs `bun test --randomize` and fails on a skip the
|
|
76
|
-
repository has not declared
|
|
86
|
+
repository has not declared. See "Run the suite" below.
|
|
77
87
|
|
|
78
|
-
`checks-lint` runs every kit gate a lint needs
|
|
88
|
+
`checks-lint` runs every kit gate a lint needs. See "Run every lint gate"
|
|
79
89
|
below. Three of them:
|
|
80
90
|
|
|
81
91
|
`lint-coverage.sh` fails when oxlint silently skips a tracked `.ts` or
|
|
@@ -87,7 +97,7 @@ or its config does not parse.
|
|
|
87
97
|
`test-layout.ts` decides the test layout described below.
|
|
88
98
|
|
|
89
99
|
`commit-identity.ts` refuses a commit with an author other than the
|
|
90
|
-
repository owner
|
|
100
|
+
repository owner. See "Check commit identities" below.
|
|
91
101
|
|
|
92
102
|
A repo that runs mutation testing installs `@stryker-mutator/core` and
|
|
93
103
|
`@hughescr/stryker-bun-runner`, then spreads the shipped preset in
|
|
@@ -104,7 +114,7 @@ export default {
|
|
|
104
114
|
The registry version is pinned by the consumer's lockfile; bump
|
|
105
115
|
`@avi2dg/checks` to adopt a new release.
|
|
106
116
|
|
|
107
|
-
##
|
|
117
|
+
## Declare policy in the quality file
|
|
108
118
|
|
|
109
119
|
`quality.json` at the repository root says what the repository has
|
|
110
120
|
opted into. The kit's bins find it at the git root and read it there:
|
|
@@ -122,26 +132,43 @@ opted into. The kit's bins find it at the git root and read it there:
|
|
|
122
132
|
"production": ["src/**/*.ts"],
|
|
123
133
|
"effect": { "paths": ["src/**/*.ts"], "exempt": ["src/host/*.ts"] }
|
|
124
134
|
},
|
|
125
|
-
"
|
|
135
|
+
"size": { "fileLines": 400, "functionLines": 100, "applies": "changed" },
|
|
136
|
+
"features": [
|
|
137
|
+
{
|
|
138
|
+
"name": "billing",
|
|
139
|
+
"root": "src/billing",
|
|
140
|
+
"entries": ["src/billing/index.ts"],
|
|
141
|
+
"allowFrom": ["src/main.ts"],
|
|
142
|
+
"proof": "tests/e2e/billing.test.ts"
|
|
143
|
+
}
|
|
144
|
+
],
|
|
145
|
+
"changeSignal": "advisory",
|
|
146
|
+
"agentRules": { "on": [], "off": [] },
|
|
147
|
+
"docs": { "pages": { "reference": ["docs/gates/*.md"], "explanation": ["docs/design.md"] } }
|
|
126
148
|
}
|
|
127
149
|
```
|
|
128
150
|
|
|
129
151
|
| Key | Read by | Holds |
|
|
130
152
|
| --- | --- | --- |
|
|
131
153
|
| `defaultBranch` | `checks-lint`, `checks-ci-wiring` | the branch pull requests merge into, `main` when absent |
|
|
132
|
-
| `gates.ci` | `checks-ci-wiring` | the commands CI runs on every pull request
|
|
154
|
+
| `gates.ci` | `checks-ci-wiring` | the commands CI runs on every pull request. See "Check the CI wiring" |
|
|
133
155
|
| `gates.scheduled` | `checks-ci-wiring` | the commands a schedule runs |
|
|
134
|
-
| `gates.lint` | `checks-lint`, `checks-ci-wiring` | the gates `checks-lint` runs when not all apply
|
|
135
|
-
| `commitIdentity.authors` | `checks-commit-identity` | the identities allowed to author and commit
|
|
136
|
-
| `sources.effect` | `checks-quality` | the paths held to the Effect rules, and the files under them that are not
|
|
137
|
-
| `sources.production` |
|
|
156
|
+
| `gates.lint` | `checks-lint`, `checks-ci-wiring` | the gates `checks-lint` runs when not all apply. See "Gate selection" |
|
|
157
|
+
| `commitIdentity.authors` | `checks-commit-identity` | the identities allowed to author and commit. See "Check commit identities" |
|
|
158
|
+
| `sources.effect` | `checks-quality` | the paths held to the Effect rules, and the files under them that are not. See "Enforce the Effect rules" |
|
|
159
|
+
| `sources.production` | `checks-size-budget`, `checks-quality` | the source the repository ships. See "Hold files to a size budget" |
|
|
160
|
+
| `size` | `checks-size-budget` | the line budget, and which production files it holds. See "Hold files to a size budget" |
|
|
161
|
+
| `features` | `featureRules`, `checks-feature-owners` | each feature's root, entries, exempt importers and proof. See "Declare feature owners" |
|
|
162
|
+
| `changeSignal` | `checks-feature-owners` | `advisory` to list the feature owners a change touches. See "Declare feature owners" |
|
|
138
163
|
| `agentRules.on`, `agentRules.off` | agent Rule selection, not the kit | catalogued Rules switched on or off for this repository |
|
|
164
|
+
| `docs.pages` | `checks-docs` | the Diátaxis mode of each page, by glob. See "Hold docs to their templates" |
|
|
139
165
|
|
|
140
166
|
Every key is optional. The bins decode the file with one Effect
|
|
141
167
|
`Schema`, and the package ships `quality.schema.json` emitted from that
|
|
142
168
|
schema, so the `$schema` line gives an editor the verdict the bins
|
|
143
|
-
reach, save
|
|
144
|
-
|
|
169
|
+
reach, save what no JSON Schema can express across two values, which
|
|
170
|
+
the bins refuse: a Rule switched both on and off, a feature entry
|
|
171
|
+
outside its root, and two features with one name or sharing a root. A key the schema does
|
|
145
172
|
not name is refused, not ignored, so a misspelt `sources` cannot switch
|
|
146
173
|
the Effect rules off unnoticed. A glob in `sources`
|
|
147
174
|
starts at the repository root, names a directory first, uses `*`
|
|
@@ -213,8 +240,9 @@ that changes a preset reaches the repository through its next
|
|
|
213
240
|
`sources.effect`;
|
|
214
241
|
- `.oxlintrc.json` or `tsconfig.json` does not list its fragment in
|
|
215
242
|
`extends`, so the tool never reads it;
|
|
216
|
-
- a `sources.effect.paths` glob
|
|
217
|
-
|
|
243
|
+
- a `sources.effect.paths` glob, or a `sources.production` glob while
|
|
244
|
+
`size` is declared, matches no tracked or untracked file, so it holds
|
|
245
|
+
nothing.
|
|
218
246
|
|
|
219
247
|
It exits 2 when `quality.json` does not decode. `generate` writes the
|
|
220
248
|
fragments, removes a left-over one, then runs the same check.
|
|
@@ -240,12 +268,12 @@ pin both:
|
|
|
240
268
|
because one that leaves any of the kit's out turns on the category
|
|
241
269
|
rules of the plugins it adds under every declared path.
|
|
242
270
|
|
|
243
|
-
##
|
|
271
|
+
## Run every lint gate
|
|
244
272
|
|
|
245
273
|
`checks-lint` runs each of the kit's lint gates in turn and names every
|
|
246
274
|
one that fails, rather than stopping at the first. A repository whose
|
|
247
275
|
tracked files give a gate nothing to check can leave it out through
|
|
248
|
-
`gates.lint
|
|
276
|
+
`gates.lint`. See "Gate selection" under "Check the CI wiring".
|
|
249
277
|
|
|
250
278
|
| Gate | Reads |
|
|
251
279
|
| --- | --- |
|
|
@@ -255,7 +283,10 @@ tracked files give a gate nothing to check can leave it out through
|
|
|
255
283
|
| `checks-comment-gate` | the range |
|
|
256
284
|
| `checks-suppressions-ratchet` | the range |
|
|
257
285
|
| `checks-ci-wiring` | the working tree |
|
|
286
|
+
| `checks-docs` | the range |
|
|
258
287
|
| `checks-quality` | the working tree |
|
|
288
|
+
| `checks-size-budget` | the range |
|
|
289
|
+
| `checks-feature-owners` | the range |
|
|
259
290
|
|
|
260
291
|
```sh
|
|
261
292
|
checks-lint
|
|
@@ -267,7 +298,7 @@ Locally, and on any event other than a pull request, the range ends at
|
|
|
267
298
|
`HEAD` and starts where `HEAD` branched from the origin default branch:
|
|
268
299
|
`origin/HEAD`, or when `origin/HEAD` is not set, as in an
|
|
269
300
|
`actions/checkout` clone, `origin/<defaultBranch>` from the
|
|
270
|
-
repository's `quality.json
|
|
301
|
+
repository's `quality.json`, as "Declare policy in the quality file" says, and `origin/main` when
|
|
271
302
|
that is not declared. In a GitHub Actions pull request, where
|
|
272
303
|
`GITHUB_EVENT_NAME` is `pull_request`, it ends
|
|
273
304
|
at the event's head sha and starts where that branched from
|
|
@@ -308,13 +339,13 @@ gate's own report, then its verdict:
|
|
|
308
339
|
```
|
|
309
340
|
checks-lint: range 2504acf098d120e73a8ece3c96f22b934f35c6a8..10ba7d8935b73ed72624120a1542e51bd21ca7c7 from HEAD against origin/main
|
|
310
341
|
...
|
|
311
|
-
checks-lint: 3 of
|
|
342
|
+
checks-lint: 3 of 9 gate(s) failed: checks-commit-identity, checks-comment-gate, checks-suppressions-ratchet
|
|
312
343
|
```
|
|
313
344
|
|
|
314
345
|
It exits 1 when any gate found a violation, and 2 when the range or the
|
|
315
346
|
selection does not resolve, or no failing gate could decide. ci-wiring
|
|
316
|
-
always runs, so a repository on `checks-lint` declares `gates.ci
|
|
317
|
-
|
|
347
|
+
always runs, so a repository on `checks-lint` declares `gates.ci`,
|
|
348
|
+
as "Check the CI wiring" says, and it holds the test layout unless its selection
|
|
318
349
|
leaves out `checks-test-layout`.
|
|
319
350
|
|
|
320
351
|
CI runs it through `lint`. The checkout fetches the whole history, which
|
|
@@ -335,7 +366,7 @@ jobs:
|
|
|
335
366
|
- run: bun run lint
|
|
336
367
|
```
|
|
337
368
|
|
|
338
|
-
## Effect rules
|
|
369
|
+
## Enforce the Effect rules
|
|
339
370
|
|
|
340
371
|
The base config loads the `effect-channel` plugin and turns on
|
|
341
372
|
`effect-channel/no-error-channel-escape`, which refuses `Effect.ignore`,
|
|
@@ -356,7 +387,7 @@ through `Effect.fail`, a throwing call wrapped in `Effect.try` or
|
|
|
356
387
|
|
|
357
388
|
A repository turns them on for the paths it writes in Effect by
|
|
358
389
|
declaring those paths in `quality.json` and extending the generated
|
|
359
|
-
fragments
|
|
390
|
+
fragments. See "Generated fragments" under "Declare policy in the quality file":
|
|
360
391
|
|
|
361
392
|
```json
|
|
362
393
|
"sources": {
|
|
@@ -392,7 +423,7 @@ the tsconfig fragment's override, whose severities are
|
|
|
392
423
|
effect-tsgo keeps the severities `tsconfig.effect.json` sets when a later
|
|
393
424
|
config in `extends` restates the plugin with only its overrides.
|
|
394
425
|
|
|
395
|
-
##
|
|
426
|
+
## Lay out the tests
|
|
396
427
|
|
|
397
428
|
`checks-test-layout` fails unless the repo holds this shape, and names the
|
|
398
429
|
file and the path to move it to when it does not:
|
|
@@ -415,7 +446,7 @@ file and the path to move it to when it does not:
|
|
|
415
446
|
with swc and reads import specifiers and identifier use, so a test that
|
|
416
447
|
only carries `"node:child_process"` as a string is not a violation.
|
|
417
448
|
- `scripts.test` is exactly `checks-test`, which runs `bun test --randomize`
|
|
418
|
-
|
|
449
|
+
as "Run the suite" says, and `scripts.lint` runs this check, itself or
|
|
419
450
|
through `checks-lint` called by its bare bin name.
|
|
420
451
|
- `bunfig.toml` carries every `[test]` key of the shipped preset with the
|
|
421
452
|
same value, and `[test].pathIgnorePatterns` is always
|
|
@@ -435,7 +466,7 @@ still run on demand:
|
|
|
435
466
|
bun test --path-ignore-patterns='' tests/quarantine
|
|
436
467
|
```
|
|
437
468
|
|
|
438
|
-
##
|
|
469
|
+
## Run the suite
|
|
439
470
|
|
|
440
471
|
`checks-test` runs the whole suite with `bun test --randomize`, passes
|
|
441
472
|
bun's output through, and then reads bun's JUnit report of the same run.
|
|
@@ -478,10 +509,10 @@ passed without writing its report. It takes no arguments: a `-t`
|
|
|
478
509
|
filter reports every test it leaves out as skipped and a path filter
|
|
479
510
|
drops files a declaration names, so a narrowed run is plain
|
|
480
511
|
`bun test --randomize` with the arguments. Files under
|
|
481
|
-
`tests/quarantine/` are never run and so never reported
|
|
512
|
+
`tests/quarantine/` are never run and so never reported. See "Test
|
|
482
513
|
layout".
|
|
483
514
|
|
|
484
|
-
##
|
|
515
|
+
## Find flaky tests
|
|
485
516
|
|
|
486
517
|
A green run proves nothing failed in that run, not that no test is
|
|
487
518
|
flaky. `checks-flake` runs the whole suite several times, each with its
|
|
@@ -536,9 +567,9 @@ jobs:
|
|
|
536
567
|
```
|
|
537
568
|
|
|
538
569
|
and declares the step in `gates.scheduled`, so `checks-ci-wiring`
|
|
539
|
-
fails once the schedule stops running it
|
|
570
|
+
fails once the schedule stops running it. See "Check the CI wiring".
|
|
540
571
|
|
|
541
|
-
##
|
|
572
|
+
## Enforce dependency rules
|
|
542
573
|
|
|
543
574
|
`.dependency-cruiser.cjs` extends the shared base, which carries
|
|
544
575
|
`no-circular`, `no-orphans`, `not-to-dev-dep` (shipped source importing
|
|
@@ -566,7 +597,9 @@ boundary you own. A rule that restates a base name overrides it field
|
|
|
566
597
|
by field, which is how an entry point stops being an orphan:
|
|
567
598
|
redeclare `no-orphans` with your entry added to its `pathNot`.
|
|
568
599
|
This repo's own `.dependency-cruiser.cjs` does that for the plugin
|
|
569
|
-
entry.
|
|
600
|
+
entry. A repository that declares feature owners spreads the rules
|
|
601
|
+
`quality.json` compiles to into the same `forbidden`. See "Feature
|
|
602
|
+
owners".
|
|
570
603
|
|
|
571
604
|
`package.json` gains the script:
|
|
572
605
|
|
|
@@ -586,7 +619,7 @@ jobs:
|
|
|
586
619
|
- run: bun run lint:deps
|
|
587
620
|
```
|
|
588
621
|
|
|
589
|
-
##
|
|
622
|
+
## Lint commit messages
|
|
590
623
|
|
|
591
624
|
Commits follow `@commitlint/config-conventional` plus the house
|
|
592
625
|
prefixes listed in `commitlint.config.js`, shared from
|
|
@@ -625,7 +658,7 @@ It never sees a commit's author or committer fields, nor the
|
|
|
625
658
|
squashes, so it cannot enforce who a commit belongs to. The
|
|
626
659
|
commit-identity check below is the enforcement.
|
|
627
660
|
|
|
628
|
-
##
|
|
661
|
+
## Check commit identities
|
|
629
662
|
|
|
630
663
|
`scripts/commit-identity.ts` walks every commit in a range and fails when
|
|
631
664
|
one carries an identity other than the repository owner's:
|
|
@@ -651,9 +684,10 @@ owners restates it in `quality.json`:
|
|
|
651
684
|
}
|
|
652
685
|
```
|
|
653
686
|
|
|
654
|
-
`checks-lint` runs it over each pull request's range
|
|
687
|
+
`checks-lint` runs it over each pull request's range.
|
|
688
|
+
See "Run every lint gate".
|
|
655
689
|
|
|
656
|
-
##
|
|
690
|
+
## Refuse banned comments
|
|
657
691
|
|
|
658
692
|
`scripts/comment-gate.ts` runs the comment check over a diff and fails
|
|
659
693
|
when an added line carries a banned comment:
|
|
@@ -682,9 +716,10 @@ and import it as `@avi2dg/checks/scripts/comment-matchers.ts`. The repo's
|
|
|
682
716
|
cruise fails when it gains an import. `scripts/comments.ts` wraps the
|
|
683
717
|
same matchers in Effect for the gate and the backtest.
|
|
684
718
|
|
|
685
|
-
`checks-lint` runs it over each pull request's range
|
|
719
|
+
`checks-lint` runs it over each pull request's range.
|
|
720
|
+
See "Run every lint gate".
|
|
686
721
|
|
|
687
|
-
##
|
|
722
|
+
## Ratchet the suppressions
|
|
688
723
|
|
|
689
724
|
`scripts/suppressions-ratchet.ts` holds oxlint's bulk-suppression
|
|
690
725
|
baseline, `oxlint-suppressions.json`, to counts that only fall. oxlint
|
|
@@ -715,9 +750,234 @@ suppressions-ratchet: 2 count(s) in oxlint-suppressions.json rose or appeared; f
|
|
|
715
750
|
src/dispatch.ts typescript/no-non-null-assertion rose from 12 to 13
|
|
716
751
|
```
|
|
717
752
|
|
|
718
|
-
`checks-lint` runs it over each pull request's range
|
|
753
|
+
`checks-lint` runs it over each pull request's range.
|
|
754
|
+
See "Run every lint gate".
|
|
755
|
+
|
|
756
|
+
## Hold files to a size budget
|
|
757
|
+
|
|
758
|
+
`checks-size-budget` holds production files to the line budget
|
|
759
|
+
`quality.json` declares, and lists every other file over it without
|
|
760
|
+
failing:
|
|
761
|
+
|
|
762
|
+
```json
|
|
763
|
+
"sources": { "production": ["src/**/*.ts"] },
|
|
764
|
+
"size": { "fileLines": 400, "functionLines": 100, "applies": "changed" }
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
```sh
|
|
768
|
+
checks-size-budget <base-ref> <head-ref>
|
|
769
|
+
checks-size-budget <ref>
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
It runs oxlint with a configuration of two rules and nothing else:
|
|
773
|
+
`max-lines` at `fileLines` and `max-lines-per-function` at
|
|
774
|
+
`functionLines`, both counting blank and comment lines. With `applies`
|
|
775
|
+
set to `changed` it holds the files under `sources.production` that the
|
|
776
|
+
range adds or changes, a rename that edits the file included. With
|
|
777
|
+
`all` it holds every file under `sources.production`. A file the range
|
|
778
|
+
deletes or only renames is not held. Every other tracked `.ts` or
|
|
779
|
+
`.tsx` file over the budget, tests and unchanged production files alike,
|
|
780
|
+
is listed as advisory and never fails the gate; `.d.ts` files are not
|
|
781
|
+
measured.
|
|
782
|
+
|
|
783
|
+
It reads each file from the head commit rather than the working tree,
|
|
784
|
+
so an uncommitted edit neither fails nor passes a range, and a pull
|
|
785
|
+
request's merge checkout measures what the pull request holds. With two
|
|
786
|
+
arguments the range starts where the head branched from the base, at
|
|
787
|
+
their merge-base. With one it is that commit against its parent, or
|
|
788
|
+
against the empty tree for a repository's first commit. oxlint must be
|
|
789
|
+
on `PATH`, as it is under a package script.
|
|
790
|
+
|
|
791
|
+
```
|
|
792
|
+
size-budget: 1 overrun(s) of 400 lines per file and 100 per function in the production files the range adds or changes:
|
|
793
|
+
src/billing/ledger.ts:12: The function `settle` has too many lines (131). Maximum allowed is 100.
|
|
794
|
+
size-budget: advisory, 1 overrun(s) where the budget does not hold yet:
|
|
795
|
+
tests/e2e/billing.test.ts: File has too many lines (512).
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
It exits 1 on an overrun in a file it holds, and 2 when `quality.json`
|
|
799
|
+
does not decode, a ref does not resolve or oxlint cannot run. A
|
|
800
|
+
repository that declares no `size` passes. `quality.json` refuses a
|
|
801
|
+
`size` without `sources.production`, which would hold nothing, and
|
|
802
|
+
with `size` declared `checks-quality` refuses a `sources.production`
|
|
803
|
+
glob that matches no file. Moving `applies` from `changed` to `all`
|
|
804
|
+
tightens the budget to every production file, once the advisory list
|
|
805
|
+
names none.
|
|
806
|
+
|
|
807
|
+
`checks-lint` runs it over each pull request's range.
|
|
808
|
+
See "Run every lint gate".
|
|
809
|
+
|
|
810
|
+
## Declare feature owners
|
|
811
|
+
|
|
812
|
+
A repository opts a feature in by declaring, in `quality.json`, the
|
|
813
|
+
directory it owns, the files code outside it imports it through, the
|
|
814
|
+
files that may reach past those, and the end-to-end test that proves it
|
|
815
|
+
runs:
|
|
816
|
+
|
|
817
|
+
```json
|
|
818
|
+
"features": [
|
|
819
|
+
{
|
|
820
|
+
"name": "billing",
|
|
821
|
+
"root": "src/billing",
|
|
822
|
+
"entries": ["src/billing/index.ts"],
|
|
823
|
+
"allowFrom": ["src/main.ts", "src/cli/*.ts"],
|
|
824
|
+
"proof": "tests/e2e/billing.test.ts"
|
|
825
|
+
}
|
|
826
|
+
],
|
|
827
|
+
"changeSignal": "advisory"
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
`root` is a directory and `entries` are files under it, both without
|
|
831
|
+
globs. `allowFrom` holds globs of the same shape as `sources`. `proof`
|
|
832
|
+
is a `.test.ts` or `.test.tsx` file under `tests/e2e/`. Nothing moves:
|
|
833
|
+
a root is wherever the feature already lives. `quality.json` refuses an
|
|
834
|
+
entry outside its root, a name used twice, and two features sharing a
|
|
835
|
+
root or one root inside another, so a file has at most one owner.
|
|
836
|
+
|
|
837
|
+
### Import boundary
|
|
719
838
|
|
|
720
|
-
|
|
839
|
+
`dist/feature-rules.js` compiles `features` into one dependency-cruiser
|
|
840
|
+
rule per feature, which `.dependency-cruiser.cjs` spreads beside its
|
|
841
|
+
own:
|
|
842
|
+
|
|
843
|
+
```js
|
|
844
|
+
const { featureRules } = require("@avi2dg/checks/dist/feature-rules.js");
|
|
845
|
+
|
|
846
|
+
module.exports = {
|
|
847
|
+
extends: "./node_modules/@avi2dg/checks/dependency-cruiser.config.js",
|
|
848
|
+
forbidden: [...featureRules(require("./quality.json"))],
|
|
849
|
+
};
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
A module outside a feature's root that imports a file inside it must
|
|
853
|
+
import one of the feature's `entries`. Modules under `tests/` and the
|
|
854
|
+
files `allowFrom` matches, such as a CLI or a harness, may import any
|
|
855
|
+
file in it:
|
|
856
|
+
|
|
857
|
+
```
|
|
858
|
+
error feature-billing-entries: src/report.ts → src/billing/charge.ts
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
`featureRules` decodes its argument with the schema the bins use and
|
|
862
|
+
throws the schema's refusal when it does not decode, which stops the
|
|
863
|
+
cruise. It is an ES module, as `effect` is, so a `.cjs` config loads it
|
|
864
|
+
through `require`, which needs node 20.19, 22.12 or later.
|
|
865
|
+
|
|
866
|
+
### Change signal and proof
|
|
867
|
+
|
|
868
|
+
`checks-feature-owners` reads the same declaration over a range:
|
|
869
|
+
|
|
870
|
+
```sh
|
|
871
|
+
checks-feature-owners <base-ref> <head-ref>
|
|
872
|
+
checks-feature-owners <ref>
|
|
873
|
+
```
|
|
874
|
+
|
|
875
|
+
It exits 1 when a feature's proof cannot prove it: the proof or an
|
|
876
|
+
entry is not in the head commit, the proof does not parse, or it
|
|
877
|
+
imports none of the feature's entries. An import counts when it is a
|
|
878
|
+
runtime `import`, `export ... from` or `export * from` of a relative
|
|
879
|
+
path that names an entry: by its own name, by the `.js`, `.jsx`, `.mjs`
|
|
880
|
+
or `.cjs` spelling of it, `.js` naming a `.tsx` entry as well as a
|
|
881
|
+
`.ts` one, or without an extension, the way a directory `index` is
|
|
882
|
+
imported. `import type` does not count, and neither does a
|
|
883
|
+
path alias. The proof runs in `bun run test` like any end-to-end test,
|
|
884
|
+
which is what shows it passes.
|
|
885
|
+
|
|
886
|
+
```
|
|
887
|
+
feature-owners: 1 problem(s) with the features' runnable proofs:
|
|
888
|
+
billing: proof tests/e2e/billing.test.ts imports none of its entries, src/billing/index.ts
|
|
889
|
+
```
|
|
890
|
+
|
|
891
|
+
With `changeSignal` set to `advisory` it also lists each owner the
|
|
892
|
+
range touches, with the paths it touched under the owner's root or at
|
|
893
|
+
its proof, and still exits 0. Whether a change that spans owners is one
|
|
894
|
+
coherent slice is for a reviewer to judge. A rename counts at both of
|
|
895
|
+
its paths:
|
|
896
|
+
|
|
897
|
+
```
|
|
898
|
+
feature-owners: advisory, the range touches 2 feature owner(s); a reviewer judges whether they make one slice:
|
|
899
|
+
billing: src/billing/charge.ts, src/billing/tax.ts
|
|
900
|
+
invoices: src/invoices/tax.ts, tests/e2e/invoices.test.ts
|
|
901
|
+
```
|
|
902
|
+
|
|
903
|
+
It exits 2 when `quality.json` does not decode, which is where a proof
|
|
904
|
+
outside `tests/e2e/` is refused, or a ref does not resolve. A
|
|
905
|
+
repository that declares no feature passes, and `quality.json` refuses
|
|
906
|
+
a `changeSignal` without features, which would map a change to no
|
|
907
|
+
owner.
|
|
908
|
+
|
|
909
|
+
`checks-lint` runs it over each pull request's range.
|
|
910
|
+
See "Run every lint gate".
|
|
911
|
+
|
|
912
|
+
## Hold docs to their templates
|
|
913
|
+
|
|
914
|
+
`checks-docs` holds each doc file a change touches to the template for its kind, and lists every other doc file that does not conform yet without failing.
|
|
915
|
+
The package ships one template per kind under `templates/`, and a repository starts a new doc file by copying one:
|
|
916
|
+
|
|
917
|
+
```sh
|
|
918
|
+
cp node_modules/@avi2dg/checks/templates/how-to.md docs/add-a-supplier.md
|
|
919
|
+
```
|
|
920
|
+
|
|
921
|
+
| File | Kind | Template |
|
|
922
|
+
| --- | --- | --- |
|
|
923
|
+
| `README.md` | readme | `templates/readme.md` |
|
|
924
|
+
| `CHANGELOG.md` | changelog | `templates/changelog.md` |
|
|
925
|
+
| `AGENTS.md` | agents | `templates/agents.md` |
|
|
926
|
+
| `CLAUDE.md` | claude | `templates/claude.md` |
|
|
927
|
+
| each file in `docs/adr/` but its generated index, `README.md` | adr | `templates/adr.md` |
|
|
928
|
+
| a page `docs.pages` declares | tutorial, how-to, reference or explanation | `templates/<mode>.md` |
|
|
929
|
+
|
|
930
|
+
The first four are the files at the repository root.
|
|
931
|
+
No other Markdown file is judged, save a page under `docs/`, which needs a mode.
|
|
932
|
+
Which Diátaxis mode a page is written in is a judgment, so `quality.json` declares it:
|
|
933
|
+
|
|
934
|
+
```json
|
|
935
|
+
"docs": {
|
|
936
|
+
"pages": {
|
|
937
|
+
"reference": ["docs/gates/*.md"],
|
|
938
|
+
"explanation": ["docs/design.md"]
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
A template decides a file's structure, and the template file itself is the reference for each kind:
|
|
944
|
+
|
|
945
|
+
- A file opens with one `# ` title on its first line and has text before its first section.
|
|
946
|
+
It skips no heading level, and no heading is Overview, Introduction or How it works.
|
|
947
|
+
- Its sections are the template's headings in the template's order.
|
|
948
|
+
A heading in angle brackets is one the writer names.
|
|
949
|
+
One marked verb first is left to review, since no program tells a verb from a noun there.
|
|
950
|
+
A heading the template does not have, in that place, is refused.
|
|
951
|
+
- A record in `docs/adr/` is named for its four-digit number, and its title opens with the same number.
|
|
952
|
+
A `Date: YYYY-MM-DD` line follows the title, the first word under Status is Proposed, Accepted, Rejected, Deprecated, Superseded or Retired, and no other record holds its number.
|
|
953
|
+
- A changelog lists its releases newest first, each opening with a `Released YYYY-MM-DD.` line.
|
|
954
|
+
- A how-to or tutorial page numbers its steps.
|
|
955
|
+
- `CLAUDE.md` is its template word for word.
|
|
956
|
+
|
|
957
|
+
```sh
|
|
958
|
+
checks-docs <base-ref> <head-ref>
|
|
959
|
+
checks-docs <ref>
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
It reads each file from the head commit.
|
|
963
|
+
With two arguments the range starts where the head branched from the base, at their merge-base.
|
|
964
|
+
With one it is that commit against its parent, or against the empty tree for a repository's first commit.
|
|
965
|
+
A file the range adds, changes or renames is held to its template, and a file it deletes is not.
|
|
966
|
+
|
|
967
|
+
```
|
|
968
|
+
docs: 2 violation(s) in the doc files the range touches:
|
|
969
|
+
README.md:1: lacks `## Where things are`
|
|
970
|
+
docs/parts.md: is a page under docs/ with no mode; declare it under docs.pages in quality.json as tutorial, how-to, reference, explanation
|
|
971
|
+
docs: advisory, 1 doc file(s) the range leaves alone do not hold to their templates yet:
|
|
972
|
+
docs/adr/0001-quality-gates.md: 5 violation(s)
|
|
973
|
+
```
|
|
974
|
+
|
|
975
|
+
It exits 1 on a violation in a file the range touches, and 2 when `quality.json` does not decode or a ref does not resolve.
|
|
976
|
+
|
|
977
|
+
`checks-lint` runs it over each pull request's range.
|
|
978
|
+
See "Run every lint gate".
|
|
979
|
+
|
|
980
|
+
## Check the CI wiring
|
|
721
981
|
|
|
722
982
|
`checks-ci-wiring` fails when a command the repository's CI must run no
|
|
723
983
|
longer runs on pull requests to the default branch. No local check sees
|
|
@@ -820,8 +1080,9 @@ ci-wiring: 1 of 1 scheduled command(s) do not run on a schedule:
|
|
|
820
1080
|
|
|
821
1081
|
### Gate selection
|
|
822
1082
|
|
|
823
|
-
A repository with no TypeScript source gives `checks-lint-coverage
|
|
824
|
-
`checks-test-layout`
|
|
1083
|
+
A repository with no TypeScript source gives `checks-lint-coverage`,
|
|
1084
|
+
`checks-test-layout`, `checks-size-budget` and `checks-feature-owners`
|
|
1085
|
+
nothing to check, and test-layout still refuses its
|
|
825
1086
|
missing `bun test` script and `bunfig.toml`. It declares the gates
|
|
826
1087
|
`checks-lint` runs as `gates.lint`:
|
|
827
1088
|
|
|
@@ -833,13 +1094,14 @@ missing `bun test` script and `bunfig.toml`. It declares the gates
|
|
|
833
1094
|
"checks-comment-gate",
|
|
834
1095
|
"checks-suppressions-ratchet",
|
|
835
1096
|
"checks-ci-wiring",
|
|
1097
|
+
"checks-docs",
|
|
836
1098
|
"checks-quality"
|
|
837
1099
|
]
|
|
838
1100
|
}
|
|
839
1101
|
```
|
|
840
1102
|
|
|
841
|
-
`checks-lint` runs exactly those, in the "
|
|
842
|
-
order, and all
|
|
1103
|
+
`checks-lint` runs exactly those, in the "Run every lint gate" table's
|
|
1104
|
+
order, and all ten when `gates.lint` is absent. A selection in
|
|
843
1105
|
`quality.json` always keeps `checks-quality`, since the file it sits in
|
|
844
1106
|
is what makes that gate apply. A step running
|
|
845
1107
|
`checks-lint` then counts only for a declared gate that `gates.lint`
|
|
@@ -855,7 +1117,10 @@ A selection may leave out only a gate that does not apply:
|
|
|
855
1117
|
| `checks-comment-gate` | always |
|
|
856
1118
|
| `checks-suppressions-ratchet` | always |
|
|
857
1119
|
| `checks-ci-wiring` | always |
|
|
1120
|
+
| `checks-docs` | always |
|
|
858
1121
|
| `checks-quality` | tracks a `quality.json` |
|
|
1122
|
+
| `checks-size-budget` | tracks a `.ts` or `.tsx` file |
|
|
1123
|
+
| `checks-feature-owners` | tracks a `.ts` or `.tsx` file |
|
|
859
1124
|
|
|
860
1125
|
Both bins exit 2 on a `gates.lint` that names an unknown gate or leaves
|
|
861
1126
|
out one that always applies. ci-wiring exits 1 when the
|
|
@@ -863,9 +1128,11 @@ selection leaves out a gate the repository's tracked files make
|
|
|
863
1128
|
applicable, and names the gate and the files:
|
|
864
1129
|
|
|
865
1130
|
```
|
|
866
|
-
ci-wiring: quality.json gates.lint leaves out
|
|
1131
|
+
ci-wiring: quality.json gates.lint leaves out 4 gate(s) this repository's contents make applicable:
|
|
867
1132
|
checks-lint-coverage: the repository tracks TypeScript source (src/widget.ts)
|
|
868
1133
|
checks-test-layout: the repository tracks TypeScript source (src/widget.ts)
|
|
1134
|
+
checks-size-budget: the repository tracks TypeScript source (src/widget.ts)
|
|
1135
|
+
checks-feature-owners: the repository tracks TypeScript source (src/widget.ts)
|
|
869
1136
|
```
|
|
870
1137
|
|
|
871
1138
|
It reads the files tracked at the checkout, so the pull request that
|
|
@@ -886,7 +1153,7 @@ does not evaluate:
|
|
|
886
1153
|
command itself does, the shell's options, and a step or job that
|
|
887
1154
|
fails or times out before the gate step.
|
|
888
1155
|
|
|
889
|
-
## Backtest
|
|
1156
|
+
## Backtest the comment check
|
|
890
1157
|
|
|
891
1158
|
`scripts/backtest.ts` reports what the comment check would have refused
|
|
892
1159
|
at each recent commit, so a repository can measure its own history:
|
|
@@ -902,7 +1169,7 @@ comment text as a share of added lines.
|
|
|
902
1169
|
`generated/`, `vendor/`, `repos/`, `node_modules/` and `dist/` are out
|
|
903
1170
|
of reach, so the figures are authored code.
|
|
904
1171
|
|
|
905
|
-
##
|
|
1172
|
+
## Compare mutation scores
|
|
906
1173
|
|
|
907
1174
|
`checks-mutation-compare` gates a pull request on no-regression rather
|
|
908
1175
|
than an absolute threshold: the head mutation score may not fall below
|
|
@@ -948,107 +1215,6 @@ jobs:
|
|
|
948
1215
|
The shared Stryker preset's `json` reporter writes
|
|
949
1216
|
`reports/mutation/mutation.json` in each worktree.
|
|
950
1217
|
|
|
951
|
-
## Why it is shaped this way
|
|
952
|
-
|
|
953
|
-
- Every config in an oxlint `extends` chain brings its own `plugins`,
|
|
954
|
-
and one that sets none brings oxlint's default plugins, whose
|
|
955
|
-
category rules the base's `categories` then turn on across the tree.
|
|
956
|
-
`rules`, `categories` and `jsPlugins` inherit as expected. That is why
|
|
957
|
-
the consumer snippet restates `plugins` and nothing else, and why the
|
|
958
|
-
generated fragment always sets them.
|
|
959
|
-
- `node_modules/` is excluded through the consumer's `.gitignore`, not
|
|
960
|
-
`ignorePatterns`: oxlint still walks the installed package when only
|
|
961
|
-
`ignorePatterns` names it.
|
|
962
|
-
- `files` in package.json is the published surface: `tests/`, `AGENTS.md`
|
|
963
|
-
and the `.ts` plugin source never reach an install. npm adds
|
|
964
|
-
`package.json`, `README` and `LICENSE` to the tarball whatever `files`
|
|
965
|
-
says. `bun pm pack` builds the same tarball the registry serves, which
|
|
966
|
-
is what the packed-tarball consumer e2e test installs.
|
|
967
|
-
- The plugin ships compiled as `dist/index.js`, built with
|
|
968
|
-
`bun build effect-channel/index.ts --outdir dist --target node --format esm`.
|
|
969
|
-
Node refuses to type-strip a `.ts` plugin under `node_modules`, so the
|
|
970
|
-
`.ts` source would fail to load from an installed package.
|
|
971
|
-
- `dist/` is committed. No `prepack` or `prepublishOnly` builds it, so a
|
|
972
|
-
publish ships whatever bundle the publishing worktree holds. Rebuild it
|
|
973
|
-
after pulling with `bun run build`; CI fails when the committed bundle
|
|
974
|
-
is stale. `bun run build` also emits `quality.schema.json`, which is
|
|
975
|
-
committed the same way, and a test fails when it differs from what
|
|
976
|
-
the schema emits.
|
|
977
|
-
- `quality.json` is JSON, not TOML or a TypeScript module: a bun bin, a
|
|
978
|
-
hook running without `node_modules`, a `.cjs` or `.mjs` config and
|
|
979
|
-
`jq` all parse it with nothing installed, and nobody runs a
|
|
980
|
-
repository's own code to learn its policy. It holds declarations
|
|
981
|
-
only. The kit's bins read it directly; oxlint and tsc read nothing but
|
|
982
|
-
their own JSON, so they extend generated fragments, which
|
|
983
|
-
`checks-quality --check` holds to the declarations.
|
|
984
|
-
- `quality.json` refuses a key its schema does not name, so a kit that
|
|
985
|
-
cannot enforce a newer key refuses it rather than let the repository
|
|
986
|
-
believe it enforced.
|
|
987
|
-
- The base parses with swc because typescript 7 (tsgo) has no compiler
|
|
988
|
-
API for dependency-cruiser to use. Without `@swc/core` installed the
|
|
989
|
-
cruise silently skips every `.ts` file, so this repo's test asserts its
|
|
990
|
-
own TypeScript is cruised.
|
|
991
|
-
- `bunfig.toml` has no `extends` and no include: bun ignores an unknown
|
|
992
|
-
top-level key in silence, so a preset cannot be inherited and the
|
|
993
|
-
consumer's copy is compared key by key against the installed one
|
|
994
|
-
instead. `[test] pathIgnorePatterns` is a real bunfig key, and an empty
|
|
995
|
-
`--path-ignore-patterns` flag overrides the file's own list.
|
|
996
|
-
- The Stryker preset is a JavaScript module, not JSON: Stryker 10 does
|
|
997
|
-
not resolve `extends` in a JSON config, but a `.mjs` config that
|
|
998
|
-
spreads an imported object consumes it. Keys the consumer sets after
|
|
999
|
-
the spread win.
|
|
1000
|
-
- The `.ts` bins are written in Effect, so `effect` is a peer dependency
|
|
1001
|
-
and `@effect/platform-bun`, which only the bins use, is a dependency.
|
|
1002
|
-
`@effect/platform-node-shared` is a direct dependency at the same exact
|
|
1003
|
-
version only to pin it: `@effect/platform-bun` asks for it with a `^`
|
|
1004
|
-
range, and a newer rc peers on a newer `effect` than consumers install,
|
|
1005
|
-
so all three move together.
|
|
1006
|
-
- Each runnable script ships a `checks-` bin entry, so consumer
|
|
1007
|
-
`package.json` scripts call the short name, which the package manager
|
|
1008
|
-
puts on `PATH` only there; a shell runs it through `bun run`, which
|
|
1009
|
-
never falls back to the registry the way `bunx` does. The `.ts` checks
|
|
1010
|
-
keep a `bun` shebang, which needs no build step and no `dist/`
|
|
1011
|
-
entry, unlike the oxlint plugin that node loads.
|
|
1012
|
-
- `checks-lint` runs each gate as its own bin in a child process rather
|
|
1013
|
-
than importing it, so a gate behaves the same called alone or through
|
|
1014
|
-
the entry point, and `lint-coverage.sh` stays a shell script. The
|
|
1015
|
-
gates run one at a time with their output passed straight through, so
|
|
1016
|
-
each report reads whole and in the table's order.
|
|
1017
|
-
- A gate selection is checked against the repository's contents rather
|
|
1018
|
-
than trusted, so it cannot skip a gate that applies. ci-wiring does
|
|
1019
|
-
that check, which is why a selection without it, or without another
|
|
1020
|
-
gate that applies everywhere, is refused as `checks-lint` reads it:
|
|
1021
|
-
nothing would check the selection otherwise.
|
|
1022
|
-
- `checks-test` runs bun itself rather than reading a report some other
|
|
1023
|
-
run left: a skip taken only on CI is visible only in CI's own run, and
|
|
1024
|
-
an earlier run's report may be stale or narrowed. It reads the JUnit
|
|
1025
|
-
report bun writes to a temporary directory, since bun has no other
|
|
1026
|
-
per-test output meant for a program.
|
|
1027
|
-
- `checks-ci-wiring` runs inside `lint`, not in a workflow of its own:
|
|
1028
|
-
deleting the step that runs a check is the violation it catches, so the
|
|
1029
|
-
local `lint` is where it has to fail.
|
|
1030
|
-
- Workflows are parsed with `Bun.YAML`, which the `bun` shebang already
|
|
1031
|
-
provides, so the check adds no dependency. It reads `on` as a string
|
|
1032
|
-
key, not as the YAML 1.1 boolean.
|
|
1033
|
-
- `bun` counts as a built-in module. Nothing installed resolves it except
|
|
1034
|
-
`@types/bun`, which would otherwise make every runtime `bun` import look
|
|
1035
|
-
like a dev-only dependency.
|
|
1036
|
-
- The pull request merge commit GitHub builds is authored by `GitHub
|
|
1037
|
-
<noreply@github.com>`, which commit-identity refuses as an author.
|
|
1038
|
-
`checks-lint` ends a pull request's range at the event's head sha, so
|
|
1039
|
-
the merge commit is never in it. A `lint` that calls
|
|
1040
|
-
`checks-commit-identity HEAD` itself checks out
|
|
1041
|
-
`github.event.pull_request.head.sha` instead of the default merge ref.
|
|
1042
|
-
- `no-deep-imports` judges the import specifier, never the resolved file.
|
|
1043
|
-
The base honours `exports` maps, so a subpath the map publishes resolves
|
|
1044
|
-
and passes, one it omits fails to resolve and is reported, and a package
|
|
1045
|
-
without an `exports` map publishes every file. A bare import always
|
|
1046
|
-
passes whatever file its entry lives in. Setting your own
|
|
1047
|
-
`options.enhancedResolveOptions` replaces the base's, so restate
|
|
1048
|
-
`exportsFields` and `conditionNames` if you do.
|
|
1049
|
-
- dependency-cruiser `extends` merges same-name `forbidden` rules with the
|
|
1050
|
-
child's fields winning. That is the entry-point and layer recipe above.
|
|
1051
|
-
|
|
1052
1218
|
## Develop
|
|
1053
1219
|
|
|
1054
1220
|
```sh
|
|
@@ -1082,3 +1248,20 @@ every later tag publishes through the workflow.
|
|
|
1082
1248
|
|
|
1083
1249
|
`publishConfig.access` in package.json is what makes the scoped package
|
|
1084
1250
|
public.
|
|
1251
|
+
|
|
1252
|
+
## Where things are
|
|
1253
|
+
|
|
1254
|
+
| Path | What it holds |
|
|
1255
|
+
| --- | --- |
|
|
1256
|
+
| `scripts/` | every bin, and the modules they share |
|
|
1257
|
+
| `effect-channel/` | the Effect error-channel oxlint plugin |
|
|
1258
|
+
| `dist/` | the committed bundles of the plugin and of `featureRules` |
|
|
1259
|
+
| `presets/` | the Effect presets `checks-quality` builds its fragments from |
|
|
1260
|
+
| `templates/` | one template per kind of doc file, which `bun run build` renders |
|
|
1261
|
+
| `tests/` | the suite, with the tests that spawn a process under `tests/e2e/` |
|
|
1262
|
+
| `docs/` | pages for whoever develops the kit |
|
|
1263
|
+
| the root configs | `oxlintrc.json`, `tsconfig.effect.json`, `bunfig.toml`, `commitlint.config.js`, `dependency-cruiser.config.js`, `stryker.preset.js` and `quality.schema.json`, which a consuming repository extends or copies |
|
|
1264
|
+
|
|
1265
|
+
## Related topics
|
|
1266
|
+
|
|
1267
|
+
- [Why it is shaped this way](docs/design.md)
|