@shivaedev/quality 0.3.1 → 0.5.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 +46 -0
- package/README.md +187 -30
- package/biome/declarations.json +17 -0
- package/biome/plugins/ambient-runtime.grit +18 -0
- package/biome/plugins/effect-fn-spans.grit +7 -0
- package/biome/preset.json +602 -0
- package/dist/baseline/compare.d.ts +8 -9
- package/dist/baseline/compare.d.ts.map +1 -1
- package/dist/baseline/compare.js +4 -4
- package/dist/baseline/compare.js.map +1 -1
- package/dist/baseline/format.d.ts +4 -4
- package/dist/baseline/format.d.ts.map +1 -1
- package/dist/baseline/format.js +3 -2
- package/dist/baseline/format.js.map +1 -1
- package/dist/baseline/guard.d.ts +3 -3
- package/dist/baseline/guard.d.ts.map +1 -1
- package/dist/baseline/guard.js +6 -5
- package/dist/baseline/guard.js.map +1 -1
- package/dist/baseline/legacy.d.ts +3 -3
- package/dist/baseline/legacy.d.ts.map +1 -1
- package/dist/baseline/legacy.js.map +1 -1
- package/dist/baseline/rewrite.d.ts +1 -1
- package/dist/baseline/rewrite.d.ts.map +1 -1
- package/dist/baseline/rewrite.js.map +1 -1
- package/dist/baseline/update.d.ts +8 -9
- package/dist/baseline/update.d.ts.map +1 -1
- package/dist/baseline/update.js +10 -9
- package/dist/baseline/update.js.map +1 -1
- package/dist/biome/report.d.ts +33 -0
- package/dist/biome/report.d.ts.map +1 -0
- package/dist/biome/report.js +25 -0
- package/dist/biome/report.js.map +1 -0
- package/dist/biome/run.d.ts +7 -0
- package/dist/biome/run.d.ts.map +1 -0
- package/dist/biome/run.js +16 -0
- package/dist/biome/run.js.map +1 -0
- package/dist/cli/args.d.ts +6 -3
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +9 -4
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/baseline.d.ts +1 -1
- package/dist/cli/baseline.d.ts.map +1 -1
- package/dist/cli/baseline.js +3 -3
- package/dist/cli/baseline.js.map +1 -1
- package/dist/cli/check.js.map +1 -1
- package/dist/cli/fix.d.ts +4 -0
- package/dist/cli/fix.d.ts.map +1 -0
- package/dist/cli/fix.js +19 -0
- package/dist/cli/fix.js.map +1 -0
- package/dist/cli/lint.js +1 -1
- package/dist/cli/lint.js.map +1 -1
- package/dist/cli/run-main.d.ts.map +1 -1
- package/dist/cli/run-main.js +7 -3
- package/dist/cli/run-main.js.map +1 -1
- package/dist/cli.js +3 -0
- package/dist/cli.js.map +1 -1
- package/dist/config/decode.d.ts +4 -4
- package/dist/config/decode.js +12 -12
- package/dist/config/decode.js.map +1 -1
- package/dist/config/load.d.ts +3 -3
- package/dist/config/load.d.ts.map +1 -1
- package/dist/config/load.js +1 -1
- package/dist/config/load.js.map +1 -1
- package/dist/config/resolve.d.ts +3 -5
- package/dist/config/resolve.d.ts.map +1 -1
- package/dist/config/resolve.js +11 -5
- package/dist/config/resolve.js.map +1 -1
- package/dist/config.d.ts +9 -9
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/decoded.d.ts +1 -1
- package/dist/decoded.d.ts.map +1 -1
- package/dist/engine/baseline-file.d.ts +1 -1
- package/dist/engine/baseline-file.d.ts.map +1 -1
- package/dist/engine/evaluate.d.ts +8 -9
- package/dist/engine/evaluate.d.ts.map +1 -1
- package/dist/engine/evaluate.js +4 -4
- package/dist/engine/evaluate.js.map +1 -1
- package/dist/engine/run-rules.d.ts +4 -3
- package/dist/engine/run-rules.d.ts.map +1 -1
- package/dist/engine/run-rules.js +3 -3
- package/dist/engine/run-rules.js.map +1 -1
- package/dist/engine/session.d.ts +3 -3
- package/dist/engine/session.d.ts.map +1 -1
- package/dist/engine/violation.d.ts +11 -3
- package/dist/engine/violation.d.ts.map +1 -1
- package/dist/engine/violation.js +6 -2
- package/dist/engine/violation.js.map +1 -1
- package/dist/exceptions/registry.d.ts +7 -8
- package/dist/exceptions/registry.d.ts.map +1 -1
- package/dist/exceptions/registry.js +6 -6
- package/dist/exceptions/registry.js.map +1 -1
- package/dist/git/base.d.ts +1 -1
- package/dist/git/base.d.ts.map +1 -1
- package/dist/git/base.js.map +1 -1
- package/dist/git/command.d.ts +3 -3
- package/dist/git/command.d.ts.map +1 -1
- package/dist/git/command.js.map +1 -1
- package/dist/git/history.d.ts +1 -1
- package/dist/git/history.d.ts.map +1 -1
- package/dist/git/history.js +1 -1
- package/dist/git/history.js.map +1 -1
- package/dist/imports/ambient.d.ts +7 -0
- package/dist/imports/ambient.d.ts.map +1 -0
- package/dist/imports/ambient.js +44 -0
- package/dist/imports/ambient.js.map +1 -0
- package/dist/imports/components.d.ts +2 -0
- package/dist/imports/components.d.ts.map +1 -0
- package/dist/imports/components.js +63 -0
- package/dist/imports/components.js.map +1 -0
- package/dist/imports/extract.d.ts +10 -0
- package/dist/imports/extract.d.ts.map +1 -0
- package/dist/imports/extract.js +100 -0
- package/dist/imports/extract.js.map +1 -0
- package/dist/imports/fences/dsl.d.ts +18 -0
- package/dist/imports/fences/dsl.d.ts.map +1 -0
- package/dist/imports/fences/dsl.js +14 -0
- package/dist/imports/fences/dsl.js.map +1 -0
- package/dist/imports/fences/evaluate.d.ts +12 -0
- package/dist/imports/fences/evaluate.d.ts.map +1 -0
- package/dist/imports/fences/evaluate.js +55 -0
- package/dist/imports/fences/evaluate.js.map +1 -0
- package/dist/imports/fences/match.d.ts +17 -0
- package/dist/imports/fences/match.d.ts.map +1 -0
- package/dist/imports/fences/match.js +106 -0
- package/dist/imports/fences/match.js.map +1 -0
- package/dist/imports/fences/model.d.ts +60 -0
- package/dist/imports/fences/model.d.ts.map +1 -0
- package/dist/imports/fences/model.js +2 -0
- package/dist/imports/fences/model.js.map +1 -0
- package/dist/imports/fences/policy.d.ts +5 -0
- package/dist/imports/fences/policy.d.ts.map +1 -0
- package/dist/imports/fences/policy.js +59 -0
- package/dist/imports/fences/policy.js.map +1 -0
- package/dist/imports/fences/reach.d.ts +12 -0
- package/dist/imports/fences/reach.d.ts.map +1 -0
- package/dist/imports/fences/reach.js +39 -0
- package/dist/imports/fences/reach.js.map +1 -0
- package/dist/imports/fences/selectors.d.ts +11 -0
- package/dist/imports/fences/selectors.d.ts.map +1 -0
- package/dist/imports/fences/selectors.js +27 -0
- package/dist/imports/fences/selectors.js.map +1 -0
- package/dist/imports/fences/vocabulary.d.ts +9 -0
- package/dist/imports/fences/vocabulary.d.ts.map +1 -0
- package/dist/imports/fences/vocabulary.js +40 -0
- package/dist/imports/fences/vocabulary.js.map +1 -0
- package/dist/imports/generated.d.ts +6 -0
- package/dist/imports/generated.d.ts.map +1 -0
- package/dist/imports/generated.js +78 -0
- package/dist/imports/generated.js.map +1 -0
- package/dist/imports/glob.d.ts +2 -0
- package/dist/imports/glob.d.ts.map +1 -0
- package/dist/imports/glob.js +125 -0
- package/dist/imports/glob.js.map +1 -0
- package/dist/imports/graph.d.ts +23 -0
- package/dist/imports/graph.d.ts.map +1 -0
- package/dist/imports/graph.js +56 -0
- package/dist/imports/graph.js.map +1 -0
- package/dist/imports/paths.d.ts +2 -0
- package/dist/imports/paths.d.ts.map +1 -0
- package/dist/imports/paths.js +24 -0
- package/dist/imports/paths.js.map +1 -0
- package/dist/imports/pnpm-workspace.d.ts +2 -0
- package/dist/imports/pnpm-workspace.d.ts.map +1 -0
- package/dist/imports/pnpm-workspace.js +122 -0
- package/dist/imports/pnpm-workspace.js.map +1 -0
- package/dist/imports/projects.d.ts +10 -0
- package/dist/imports/projects.d.ts.map +1 -0
- package/dist/imports/projects.js +83 -0
- package/dist/imports/projects.js.map +1 -0
- package/dist/imports/resolve.d.ts +16 -0
- package/dist/imports/resolve.d.ts.map +1 -0
- package/dist/imports/resolve.js +88 -0
- package/dist/imports/resolve.js.map +1 -0
- package/dist/imports/workspace.d.ts +10 -0
- package/dist/imports/workspace.d.ts.map +1 -0
- package/dist/imports/workspace.js +55 -0
- package/dist/imports/workspace.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/inventory/collect.d.ts +6 -6
- package/dist/inventory/collect.d.ts.map +1 -1
- package/dist/inventory/collect.js.map +1 -1
- package/dist/manifests/sorted.d.ts +14 -0
- package/dist/manifests/sorted.d.ts.map +1 -0
- package/dist/manifests/sorted.js +39 -0
- package/dist/manifests/sorted.js.map +1 -0
- package/dist/report/guard.d.ts +1 -1
- package/dist/report/guard.d.ts.map +1 -1
- package/dist/report/guard.js +1 -1
- package/dist/report/guard.js.map +1 -1
- package/dist/report/render.d.ts +2 -2
- package/dist/report/render.d.ts.map +1 -1
- package/dist/report/render.js.map +1 -1
- package/dist/report/stale.d.ts +2 -2
- package/dist/report/stale.d.ts.map +1 -1
- package/dist/report/stale.js.map +1 -1
- package/dist/rule.d.ts +12 -11
- package/dist/rule.d.ts.map +1 -1
- package/dist/rule.js.map +1 -1
- package/dist/rules/biome.d.ts +4 -0
- package/dist/rules/biome.d.ts.map +1 -0
- package/dist/rules/biome.js +40 -0
- package/dist/rules/biome.js.map +1 -0
- package/dist/rules/built-in.d.ts +8 -10
- package/dist/rules/built-in.d.ts.map +1 -1
- package/dist/rules/built-in.js +12 -0
- package/dist/rules/built-in.js.map +1 -1
- package/dist/rules/comments/kinds.d.ts +1 -2
- package/dist/rules/comments/kinds.d.ts.map +1 -1
- package/dist/rules/comments/kinds.js +7 -16
- package/dist/rules/comments/kinds.js.map +1 -1
- package/dist/rules/comments/max-per-file.d.ts +1 -1
- package/dist/rules/comments/max-per-file.js +11 -11
- package/dist/rules/comments/max-per-file.js.map +1 -1
- package/dist/rules/comments/no-banner.js +2 -2
- package/dist/rules/comments/no-banner.js.map +1 -1
- package/dist/rules/comments/no-environment-pragma.d.ts +2 -0
- package/dist/rules/comments/no-environment-pragma.d.ts.map +1 -0
- package/dist/rules/comments/no-environment-pragma.js +8 -0
- package/dist/rules/comments/no-environment-pragma.js.map +1 -0
- package/dist/rules/comments/no-jsdoc.js +3 -3
- package/dist/rules/comments/no-jsdoc.js.map +1 -1
- package/dist/rules/comments/no-line-reference.js +2 -2
- package/dist/rules/comments/no-line-reference.js.map +1 -1
- package/dist/rules/comments/no-pr-reference.js +6 -6
- package/dist/rules/comments/no-pr-reference.js.map +1 -1
- package/dist/rules/comments/no-todo.js +2 -2
- package/dist/rules/comments/no-todo.js.map +1 -1
- package/dist/rules/comments/pattern-rule.d.ts +2 -2
- package/dist/rules/comments/pattern-rule.d.ts.map +1 -1
- package/dist/rules/comments/pattern-rule.js +2 -2
- package/dist/rules/comments/pattern-rule.js.map +1 -1
- package/dist/rules/comments/scan.d.ts +6 -6
- package/dist/rules/comments/scan.d.ts.map +1 -1
- package/dist/rules/comments/scan.js +2 -2
- package/dist/rules/comments/scan.js.map +1 -1
- package/dist/rules/imports/cycles.d.ts +2 -0
- package/dist/rules/imports/cycles.d.ts.map +1 -0
- package/dist/rules/imports/cycles.js +38 -0
- package/dist/rules/imports/cycles.js.map +1 -0
- package/dist/rules/imports/fences.d.ts +5 -0
- package/dist/rules/imports/fences.d.ts.map +1 -0
- package/dist/rules/imports/fences.js +24 -0
- package/dist/rules/imports/fences.js.map +1 -0
- package/dist/rules/imports/resolvable.d.ts +4 -0
- package/dist/rules/imports/resolvable.d.ts.map +1 -0
- package/dist/rules/imports/resolvable.js +22 -0
- package/dist/rules/imports/resolvable.js.map +1 -0
- package/dist/rules/manifests-sorted.d.ts +2 -0
- package/dist/rules/manifests-sorted.d.ts.map +1 -0
- package/dist/rules/manifests-sorted.js +22 -0
- package/dist/rules/manifests-sorted.js.map +1 -0
- package/dist/rules/max-lines.js +4 -4
- package/dist/rules/max-lines.js.map +1 -1
- package/dist/rules/suppressions/biome/configs.d.ts +21 -10
- package/dist/rules/suppressions/biome/configs.d.ts.map +1 -1
- package/dist/rules/suppressions/biome/configs.js +58 -22
- package/dist/rules/suppressions/biome/configs.js.map +1 -1
- package/dist/rules/suppressions/biome/declarations.d.ts +18 -0
- package/dist/rules/suppressions/biome/declarations.d.ts.map +1 -0
- package/dist/rules/suppressions/biome/declarations.js +20 -0
- package/dist/rules/suppressions/biome/declarations.js.map +1 -0
- package/dist/rules/suppressions/biome/json.d.ts +2 -2
- package/dist/rules/suppressions/biome/json.d.ts.map +1 -1
- package/dist/rules/suppressions/biome/json.js +1 -1
- package/dist/rules/suppressions/biome/json.js.map +1 -1
- package/dist/rules/suppressions/biome/resolve.d.ts +3 -0
- package/dist/rules/suppressions/biome/resolve.d.ts.map +1 -0
- package/dist/rules/suppressions/biome/resolve.js +70 -0
- package/dist/rules/suppressions/biome/resolve.js.map +1 -0
- package/dist/rules/suppressions/biome/weakenings.d.ts +5 -5
- package/dist/rules/suppressions/biome/weakenings.d.ts.map +1 -1
- package/dist/rules/suppressions/biome/weakenings.js +41 -33
- package/dist/rules/suppressions/biome/weakenings.js.map +1 -1
- package/dist/rules/suppressions/biome-overrides.d.ts +1 -1
- package/dist/rules/suppressions/biome-overrides.d.ts.map +1 -1
- package/dist/rules/suppressions/biome-overrides.js +35 -27
- package/dist/rules/suppressions/biome-overrides.js.map +1 -1
- package/dist/rules/suppressions/directives.d.ts +1 -1
- package/dist/rules/suppressions/directives.d.ts.map +1 -1
- package/dist/rules/suppressions/directives.js +11 -11
- package/dist/rules/suppressions/directives.js.map +1 -1
- package/dist/rules/suppressions/no-double-cast.js +2 -2
- package/dist/rules/suppressions/no-double-cast.js.map +1 -1
- package/dist/rules/suppressions/no-inline.d.ts +1 -7
- package/dist/rules/suppressions/no-inline.d.ts.map +1 -1
- package/dist/rules/suppressions/no-inline.js +7 -30
- package/dist/rules/suppressions/no-inline.js.map +1 -1
- package/dist/rules/suppressions/stylesheet-comments.d.ts +1 -1
- package/dist/rules/suppressions/stylesheet-comments.d.ts.map +1 -1
- package/dist/rules/suppressions/stylesheet-comments.js +3 -3
- package/dist/rules/suppressions/stylesheet-comments.js.map +1 -1
- package/dist/rules/syntax.js +4 -4
- package/dist/rules/syntax.js.map +1 -1
- package/dist/standard-schema.d.ts +1 -1
- package/dist/standard-schema.d.ts.map +1 -1
- package/dist/vitest.d.ts +3 -0
- package/dist/vitest.d.ts.map +1 -0
- package/dist/vitest.js +20 -0
- package/dist/vitest.js.map +1 -0
- package/package.json +48 -31
- package/src/baseline/compare.ts +10 -15
- package/src/baseline/format.ts +8 -6
- package/src/baseline/guard.ts +11 -10
- package/src/baseline/legacy.ts +3 -3
- package/src/baseline/rewrite.ts +1 -1
- package/src/baseline/update.ts +21 -21
- package/src/biome/report.ts +35 -0
- package/src/biome/run.ts +23 -0
- package/src/cli/args.ts +14 -8
- package/src/cli/baseline.ts +5 -6
- package/src/cli/check.ts +1 -1
- package/src/cli/fix.ts +19 -0
- package/src/cli/lint.ts +1 -1
- package/src/cli/run-main.ts +8 -3
- package/src/cli.ts +3 -0
- package/src/config/decode.ts +12 -12
- package/src/config/load.ts +4 -4
- package/src/config/resolve.ts +15 -11
- package/src/config.ts +9 -9
- package/src/decoded.ts +1 -1
- package/src/engine/baseline-file.ts +1 -1
- package/src/engine/evaluate.ts +14 -16
- package/src/engine/run-rules.ts +8 -7
- package/src/engine/session.ts +3 -3
- package/src/engine/violation.ts +18 -4
- package/src/exceptions/registry.ts +10 -16
- package/src/git/base.ts +2 -2
- package/src/git/command.ts +3 -3
- package/src/git/history.ts +2 -2
- package/src/imports/ambient.ts +54 -0
- package/src/imports/components.ts +80 -0
- package/src/imports/extract.ts +129 -0
- package/src/imports/fences/dsl.ts +33 -0
- package/src/imports/fences/evaluate.ts +75 -0
- package/src/imports/fences/match.ts +148 -0
- package/src/imports/fences/model.ts +42 -0
- package/src/imports/fences/policy.ts +70 -0
- package/src/imports/fences/reach.ts +62 -0
- package/src/imports/fences/selectors.ts +37 -0
- package/src/imports/fences/vocabulary.ts +51 -0
- package/src/imports/generated.ts +95 -0
- package/src/imports/glob.ts +142 -0
- package/src/imports/graph.ts +81 -0
- package/src/imports/paths.ts +28 -0
- package/src/imports/pnpm-workspace.ts +134 -0
- package/src/imports/projects.ts +106 -0
- package/src/imports/resolve.ts +115 -0
- package/src/imports/workspace.ts +76 -0
- package/src/index.ts +3 -0
- package/src/inventory/collect.ts +6 -6
- package/src/manifests/sorted.ts +46 -0
- package/src/report/guard.ts +2 -2
- package/src/report/render.ts +9 -9
- package/src/report/stale.ts +3 -3
- package/src/rule.ts +13 -12
- package/src/rules/biome.ts +47 -0
- package/src/rules/built-in.ts +12 -0
- package/src/rules/comments/kinds.ts +11 -21
- package/src/rules/comments/max-per-file.ts +13 -13
- package/src/rules/comments/no-banner.ts +2 -2
- package/src/rules/comments/no-environment-pragma.ts +9 -0
- package/src/rules/comments/no-jsdoc.ts +3 -3
- package/src/rules/comments/no-line-reference.ts +2 -2
- package/src/rules/comments/no-pr-reference.ts +6 -6
- package/src/rules/comments/no-todo.ts +2 -2
- package/src/rules/comments/pattern-rule.ts +6 -6
- package/src/rules/comments/scan.ts +13 -13
- package/src/rules/imports/cycles.ts +44 -0
- package/src/rules/imports/fences.ts +27 -0
- package/src/rules/imports/resolvable.ts +23 -0
- package/src/rules/manifests-sorted.ts +23 -0
- package/src/rules/max-lines.ts +6 -6
- package/src/rules/suppressions/biome/configs.ts +99 -30
- package/src/rules/suppressions/biome/declarations.ts +37 -0
- package/src/rules/suppressions/biome/json.ts +3 -3
- package/src/rules/suppressions/biome/resolve.ts +86 -0
- package/src/rules/suppressions/biome/weakenings.ts +64 -48
- package/src/rules/suppressions/biome-overrides.ts +45 -36
- package/src/rules/suppressions/directives.ts +12 -12
- package/src/rules/suppressions/no-double-cast.ts +4 -4
- package/src/rules/suppressions/no-inline.ts +13 -37
- package/src/rules/suppressions/stylesheet-comments.ts +4 -4
- package/src/rules/syntax.ts +4 -4
- package/src/standard-schema.ts +1 -1
- package/src/vitest.ts +22 -0
- package/tsconfig/base.json +35 -0
- package/tsconfig/package.json +10 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.0 - 2026-10-04
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Add the Biome preset `@shivaedev/quality/biome`. It sets the formatting (tabs, a line width of 150, double quotes), every rule Biome recommends at `error`, a list of stricter rules such as `noUnsafeTypeAssertion`, `useBlockStatements`, `noEqualsToNull`, `useConsistentArrayType` with `readonly T[]`, `noFloatingPromises` and `useExhaustiveSwitchCases`, sorted keys in JSON and object literals, and the GritQL plugins that ban ambient time, randomness, `console` and `process.env` and check `Effect.fn` span names. Its `declarations.json` declares the rules it turns off.
|
|
8
|
+
- Add the `biome` rule. It runs Biome, now a dependency pinned to 2.5.14, and reports each finding as `biome/<category>`, so Biome's findings take part in the baseline. `adopt: ["biome"]` adopts all of them. It also asks for a root Biome config that extends the preset. Like every rule it is an error by default, so a repository that upgrades extends the preset and adopts `biome` into its baseline.
|
|
9
|
+
- Add `quality fix`, which sorts every `package.json` and applies Biome's safe fixes, assist actions and formatting.
|
|
10
|
+
- Add the `manifests/sorted` rule. It reports every `package.json` in the repository whose keys are not in the order of sort-package-json, now a dependency pinned to 4.0.0, and every `package.json` that is not valid JSON.
|
|
11
|
+
- Add `@shivaedev/quality/vitest`. Its `testProjects()` sets up Vitest projects by file name: `unit` runs `*.test.ts` files in Node, `dom` runs `*.dom.test.ts` and `*.dom.test.tsx` files in happy-dom, and `slow` runs `*.slow.test.ts` files only when `--project slow` asks for it. Type tests (`*.typecheck.test.ts`) never run. `vitest` is an optional peer dependency.
|
|
12
|
+
- Add `comments/no-environment-pragma`, which reports a test environment set by a Vitest or Jest environment pragma. A DOM test is named `*.dom.test.ts` instead.
|
|
13
|
+
- Add `imports/cycles` and `imports/resolvable`, which read the import graph of the sources with the TypeScript compiler. `imports/cycles` reports each group of modules that import each other at run time, ignoring type-only imports, and takes no registry exceptions. `imports/resolvable` reports an import that resolves to nothing; its `generated` option names the folders where a relative import of a missing file is generated output. Like every rule they are errors by default, so a repository that upgrades baselines what they find. A run whose sources hold no module fails instead of passing on an empty graph.
|
|
14
|
+
- Add `imports/fences` and the `fence()` builder for declaring import fences in the config without patterns: `fence(name).because(reason).from(target)` with `mayNotImport`, `mayNotReach` or `mayImportOnly(...).of(...)`, demonstrated by an illegal and a legal example that the rule checks against the policy. Targets are `packages`, `folders`, `files`, `modules`, `scopes`, `anyOf`, `workspace` and `anything`, each with `.except`, and every name must exist.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- `suppressions/no-inline` allows `@ts-expect-error` in type tests by their file name, `*.typecheck.test.ts` or `typecheck.test.ts`, and takes no options. The `declared` option is gone, so a config that sets it fails to load until it is removed.
|
|
19
|
+
- The `allow` lists of `comments/no-jsdoc` and `comments/max-per-file` are empty by default, so a tool pragma counts as a comment until the config allows its tag.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- Write all of a long report before exiting when the output is a pipe, instead of cutting it off.
|
|
24
|
+
|
|
25
|
+
## 0.4.0 - 2026-10-03
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- `suppressions/biome-overrides` follows `extends` entries that name a package,
|
|
30
|
+
such as a shared Biome preset, resolved from the root's `node_modules` through
|
|
31
|
+
`exports` (the `biome` and `default` conditions and `*` patterns), `main` or a
|
|
32
|
+
file inside the package, as Biome resolves them. A non-relative entry that
|
|
33
|
+
names a file at the repository root is read as that file. The weakenings a
|
|
34
|
+
preset makes count as declared when a `declarations.json` beside the preset
|
|
35
|
+
declares them; any other preset weakening is reported at the `extends` line.
|
|
36
|
+
A repository declaration that repeats one the preset ships is reported, and so
|
|
37
|
+
is an `extends` entry that cannot be resolved or read.
|
|
38
|
+
- Add tsconfig presets for TypeScript 7. `@shivaedev/quality/tsconfig/base.json` is for type-checking with `noEmit`. `@shivaedev/quality/tsconfig/package.json` is the base plus declaration output, declaration and source maps, and relative `.ts` imports rewritten to `.js`, for building a package. The base sets `target` and `lib` to ESNext with bundler resolution; type-checks JavaScript with `allowJs` and `checkJs`; turns on `strict` (stating `noImplicitAny` and `strictBuiltinIteratorReturn` explicitly), `noUncheckedIndexedAccess`, `exactOptionalPropertyTypes`, `noImplicitOverride`, `noImplicitReturns`, `noFallthroughCasesInSwitch`, `noUnusedLocals`, `noUnusedParameters`, `verbatimModuleSyntax`, `isolatedModules`, `erasableSyntaxOnly`, `noUncheckedSideEffectImports` and `forceConsistentCasingInFileNames`; and rejects unreachable code and unused labels. Dot access to index signatures stays allowed.
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
|
|
42
|
+
- `suppressions/biome-overrides` merges a config with the configs it extends the
|
|
43
|
+
way Biome does: a later setting of a rule, a group, `recommended` or `enabled`
|
|
44
|
+
replaces an earlier one, so a weakening that the extending config sets back to
|
|
45
|
+
`error` is no longer reported, while `overrides` and every `includes` list are
|
|
46
|
+
appended. The `extends` of an extended config are no longer followed, since
|
|
47
|
+
Biome does not apply them.
|
|
48
|
+
|
|
3
49
|
## 0.3.1 - 2026-10-03
|
|
4
50
|
|
|
5
51
|
### Changed
|
package/README.md
CHANGED
|
@@ -61,31 +61,34 @@ The config is typed: an unknown rule id, a misspelled option or an option of the
|
|
|
61
61
|
|
|
62
62
|
### Comments
|
|
63
63
|
|
|
64
|
-
A comment says why, never what the code already says or what it used to be.
|
|
64
|
+
A comment says why, never what the code already says or what it used to be. Seven rules hold every comment to that:
|
|
65
65
|
|
|
66
66
|
| Rule | Reports | Options |
|
|
67
67
|
| --- | --- | --- |
|
|
68
|
-
| `comments/no-jsdoc` | Every `/** */` block, except a tool pragma | `allow` |
|
|
68
|
+
| `comments/no-jsdoc` | Every `/** */` block, except a tool pragma the config allows | `allow` |
|
|
69
69
|
| `comments/no-line-reference` | A line number: `file.ts:42`, `file.ts#L42`, `line 42` | none |
|
|
70
70
|
| `comments/no-pr-reference` | A pull request or issue: `#123`, `PR 123`, `MR 123`, `pull request 123`, `merge request 123`, `issue 123`, `ticket 123`, `/pull/123`, `/pulls/123`, `/issues/123`, `/merge_requests/123`, `GH-123` | none |
|
|
71
71
|
| `comments/no-banner` | A banner, divider or region: a line that starts or ends with three or more of `- = * # ~ _ + / \ ─ ━ ═ ┄ ┈`, `#region`, `#endregion` | none |
|
|
72
72
|
| `comments/no-todo` | `TODO`, `FIXME`, `XXX` and `@todo` | none |
|
|
73
|
+
| `comments/no-environment-pragma` | A test environment set by a `@vitest-environment` or `@jest-environment` pragma (and its `-options`); name the file `*.dom.test.ts` instead, see [Vitest projects](#vitest-projects) | none |
|
|
73
74
|
| `comments/max-per-file` | A file with more than `max` comments, 2 by default | `max`, `allow` |
|
|
74
75
|
|
|
75
76
|
The rules find comments with the TypeScript parser, so text inside strings, template literals, regular expressions and JSX never counts as a comment. They read the TypeScript and JavaScript modules among the sources and skip declaration files and the directives listed below. Each finding names the line its comment starts on.
|
|
76
77
|
|
|
78
|
+
The default is no comments at all: a comment states only what the code cannot show, such as a constraint or a reason, and never narrates the code. The limit of `comments/max-per-file` is a crude tripwire against runaway comments, not a budget to fill, so a file that reaches it means something went wrong.
|
|
79
|
+
|
|
77
80
|
`comments/max-per-file` counts comments this way:
|
|
78
81
|
|
|
79
82
|
- A block comment counts once, however many lines it spans.
|
|
80
83
|
- Line comments that each stand alone on adjacent lines form one run and count once. A blank line, code, a directive, or a comment after code on the same line starts a new one.
|
|
81
|
-
-
|
|
84
|
+
- Allowed tool pragmas and directives are not counted: compiler and linter directives (`@ts-…`, triple-slash directives such as `/// <reference …>`, `biome-ignore…`, `eslint-disable…`, `eslint-enable…`, `oxlint-…`, `stylelint-…`, `deno-lint-ignore…`, `tslint:disable…`, `prettier-ignore`, Flow's `$FlowFixMe`, `$FlowIgnore`, `$FlowExpectedError`, `$FlowIssue` and `@noflow`), bundler annotations (`#__PURE__`, `@__PURE__`, `#__NO_SIDE_EFFECTS__`, `@__NO_SIDE_EFFECTS__`) and coverage hints (`c8 ignore`, `v8 ignore`, `istanbul ignore`). The suppression rules below report the directives that silence a check.
|
|
82
85
|
|
|
83
86
|
A file over the limit counts one violation for each comment above it: 5 comments against a limit of 2 count 3. A finding names the first comment over the limit.
|
|
84
87
|
|
|
85
|
-
A tool pragma is a comment whose every line starts with an allowed tag, such as `/** @
|
|
88
|
+
A tool pragma is a comment whose every line starts with an allowed tag, such as `/** @jsxImportSource preact */`. `allow` lists the tags and is empty by default, so a pragma counts as a comment until the config allows its tag. Settings that a pragma would repeat in each file belong in the tool's config. `comments/no-jsdoc` and `comments/max-per-file` each take their own list, so give both the same one:
|
|
86
89
|
|
|
87
90
|
```ts
|
|
88
|
-
const pragmas = ["@
|
|
91
|
+
const pragmas = ["@jsxImportSource", "@license"];
|
|
89
92
|
|
|
90
93
|
export default defineConfig({
|
|
91
94
|
rules: {
|
|
@@ -103,7 +106,7 @@ A check that is silenced at one site hides the problem instead of fixing it. Thr
|
|
|
103
106
|
|
|
104
107
|
| Rule | Reports | Options |
|
|
105
108
|
| --- | --- | --- |
|
|
106
|
-
| `suppressions/no-inline` | Every comment directive that silences a linter, the compiler or a formatter, except
|
|
109
|
+
| `suppressions/no-inline` | Every comment directive that silences a linter, the compiler or a formatter, except `@ts-expect-error` in a type test | none |
|
|
107
110
|
| `suppressions/no-double-cast` | A cast through `unknown`, `any` or `never`: `x as unknown as T`, `x as any as T`, `x as never as T`, `<T><unknown>x` | none |
|
|
108
111
|
| `suppressions/biome-overrides` | A Biome setting that turns a check off or down, or keeps files out of it, without a declaration, and a declaration that matches no setting | `declared` |
|
|
109
112
|
|
|
@@ -117,35 +120,15 @@ A check that is silenced at one site hides the problem instead of fixing it. Thr
|
|
|
117
120
|
|
|
118
121
|
Coverage hints (`c8 ignore`, `v8 ignore`, `istanbul ignore`) are allowed: they leave code out of a coverage measure and silence no linter or compiler. The rule reads the TypeScript and JavaScript modules among the sources, declaration files included, and every `.css`, `.scss` and `.less` file among the checked files, whatever `extensions` says. Each finding names the line its comment starts on and has the directive as its subject.
|
|
119
122
|
|
|
120
|
-
####
|
|
121
|
-
|
|
122
|
-
A type test proves that an API rejects what its types forbid, and TypeScript asserts a compile error only through `@ts-expect-error`, which fails as soon as the error it expects goes away. So `@ts-expect-error`, and no other directive, may be declared for a set of files:
|
|
123
|
-
|
|
124
|
-
```ts
|
|
125
|
-
export default defineConfig({
|
|
126
|
-
rules: {
|
|
127
|
-
"suppressions/no-inline": {
|
|
128
|
-
options: {
|
|
129
|
-
declared: [
|
|
130
|
-
{
|
|
131
|
-
directive: "@ts-expect-error",
|
|
132
|
-
includes: ["*.typecheck.ts"],
|
|
133
|
-
reason: "Type tests assert the compile errors the public types must raise.",
|
|
134
|
-
},
|
|
135
|
-
],
|
|
136
|
-
},
|
|
137
|
-
},
|
|
138
|
-
},
|
|
139
|
-
});
|
|
140
|
-
```
|
|
123
|
+
#### Type tests
|
|
141
124
|
|
|
142
|
-
|
|
125
|
+
A type test proves that an API rejects what its types forbid, and TypeScript asserts a compile error only through `@ts-expect-error`, which fails as soon as the error it expects goes away. So `@ts-expect-error`, and no other directive, is allowed in a type test: a file named `*.typecheck.test.ts` or `typecheck.test.ts` (or `.tsx`). The compiler checks these files; [Vitest projects](#vitest-projects) never run them. `@ts-ignore`, `@ts-nocheck` and every linter and formatter directive stay reported in them. A test that must pass a rejected value at run time, to prove the runtime refuses it too, calls the API through `Reflect.apply` instead of a directive.
|
|
143
126
|
|
|
144
127
|
`suppressions/no-double-cast` finds casts with the TypeScript parser, through parentheses and in either assertion syntax. A single cast is left to the linter, and `as const` is not a cast.
|
|
145
128
|
|
|
146
129
|
#### Declared Biome overrides
|
|
147
130
|
|
|
148
|
-
A scope that truly cannot follow a lint rule keeps its exception in the Biome config, and the quality config declares it with a reason. `suppressions/biome-overrides` reads `biome.json` or `biome.jsonc` at the root, every nested `biome.json` and `biome.jsonc` among the checked files, and the
|
|
131
|
+
A scope that truly cannot follow a lint rule keeps its exception in the Biome config, and the quality config declares it with a reason. `suppressions/biome-overrides` reads `biome.json` or `biome.jsonc` at the root, every nested `biome.json` and `biome.jsonc` among the checked files, and the configs each of them `extends`, resolved the way Biome resolves them (see [Shared presets](#shared-presets)). These settings count as overrides, at the top level and in every `overrides` entry:
|
|
149
132
|
|
|
150
133
|
| Setting | Declared as |
|
|
151
134
|
| --- | --- |
|
|
@@ -188,6 +171,93 @@ export default defineConfig({
|
|
|
188
171
|
|
|
189
172
|
A declaration covers a setting when its `rule` and its `includes` match the setting's exactly, in any order. An `includes` list that keeps files out is declared as the whole list, so every pattern added to it needs the declaration to change too. A setting at the top level of the root config has the scope `["**"]`, and an `overrides` entry has its own `includes` (`["**"]` when it has none). Patterns in a nested config are relative to its folder, so they are declared with the folder in front: `"src/**"` in `packages/web/biome.json` is declared as `"packages/web/src/**"`, and its top level as `"packages/web/**"`. A setting without a declaration is reported at its line in the Biome config; a declaration that no setting matches is reported against the root config, so the list cannot outlive the overrides it explains.
|
|
190
173
|
|
|
174
|
+
#### Shared presets
|
|
175
|
+
|
|
176
|
+
A config `extends` its entries the way Biome does:
|
|
177
|
+
|
|
178
|
+
- An entry that starts with `./` or `../` is a file next to the config. Any other entry is first a file at the repository root, then a package in the root's `node_modules`, resolved through its `exports` (with the `biome` and `default` conditions, and `*` patterns), then its `main`, then a file inside it. A package may also name itself through the repository's own `package.json` `exports`. `"//"` in a nested config extends the root config, which is read on its own.
|
|
179
|
+
- Biome reads only the entries of the config it loads: the `extends` of an extended config are not followed, so they are not read here either.
|
|
180
|
+
- The extended configs apply from left to right and the config itself last. A later setting of a rule, a group, `recommended` or `enabled` replaces an earlier one, so a preset's weakening that the repository sets back to `error` is no override. A group set as a whole (`"suspicious": "error"`) replaces the settings of its rules; a rule set inside a group the preset turned off leaves the rest of the group off.
|
|
181
|
+
- Lists are appended, not replaced: `overrides`, `plugins` and every `includes` list, `files.includes` among them. A repository's `files.includes` adds to the preset's, so each list is declared by whoever wrote it.
|
|
182
|
+
- Patterns in an extended config resolve against the repository root, or against the folder of a nested config that extends it, as patterns in the config itself do.
|
|
183
|
+
|
|
184
|
+
A package that ships a preset declares the weakenings the preset makes in a `declarations.json` beside the preset file, as an array of the same `{ rule, includes, reason }` entries the `declared` option takes. Its `includes` are written relative to the repository that extends the preset. Those weakenings count as declared, so the repository adds nothing for them:
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
[
|
|
188
|
+
{
|
|
189
|
+
"rule": "lint/style/noDefaultExport",
|
|
190
|
+
"includes": ["**/*.config.ts"],
|
|
191
|
+
"reason": "Tools load their config files through the default export."
|
|
192
|
+
}
|
|
193
|
+
]
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
A weakening the preset makes without such a declaration is reported at the line of the repository's `extends` entry, and the repository's own declaration covers it. A weakening the repository adds beyond the preset needs its own declaration, and a repository declaration that repeats one the preset ships is reported until it is removed. A preset entry that cannot be resolved or read, or whose `declarations.json` is invalid, is reported at the `extends` entry as well.
|
|
197
|
+
|
|
198
|
+
### Imports
|
|
199
|
+
|
|
200
|
+
Three rules read the import graph of the TypeScript and JavaScript modules among the sources. They build it once per run with the TypeScript compiler, without a bundler or another dependency.
|
|
201
|
+
|
|
202
|
+
| Rule | Reports | Options |
|
|
203
|
+
| --- | --- | --- |
|
|
204
|
+
| `imports/cycles` | Modules that import each other at run time | none |
|
|
205
|
+
| `imports/resolvable` | An import that resolves to nothing | `generated` |
|
|
206
|
+
| `imports/fences` | An import that crosses a fence the config declares | `fences` |
|
|
207
|
+
|
|
208
|
+
Each import resolves the way the compiler resolves it, with the options of the nearest `tsconfig.json` and the projects it references, under bundler resolution: `paths`, `package.json` `imports` and `exports` and the `source` condition hold, so a workspace package resolves to its source. The graph reads static and dynamic imports, `export ... from`, `require()`, `require.resolve()`, `import.meta.resolve()` of a bare specifier (a relative one is URL arithmetic that never checks the path), `import()` types, `/// <reference types>`, `/// <reference path>` and JSDoc `@import` tags.
|
|
209
|
+
|
|
210
|
+
- An import of a stylesheet, an image or JSON resolves to the file, and only when the file exists, whatever declaration describes it. A Node builtin resolves.
|
|
211
|
+
- A runtime import must resolve to code: a declaration file (`.d.ts`, `.d.mts`, `.d.cts`, or an asset declaration such as `.d.css.ts`) satisfies only `import type` and `export type`, however the import reaches it, so a package that has only its `@types` package installed, or an `exports` entry that points at a declaration, is reported.
|
|
212
|
+
- A bare import resolves when a declaration file of the importer's tsconfig project declares the module in a script (`declare module "virtual:*"`). A relative import always needs a real file, `declare module "*"` and patterns with more than one `*` never count, and a `declare module` inside a module is an augmentation, which declares nothing new.
|
|
213
|
+
- A relative import of a missing file resolves only inside a folder that `generated` names, such as a client a generator writes before the tests run. Each folder must be ignored by git, or at least every file an import names in it must be, hold no file git tracks but a `.gitkeep` or `.keep` placeholder, lie outside `node_modules` and hold a file that an import names. The import stays an edge to its path, so fences apply to it before the file exists.
|
|
214
|
+
- A module that resolves into `node_modules` or outside the root is external, and its package is the one it resolves into, whatever alias the import uses. A `@types` package counts as the package it describes: `@types/hast` is `hast`, `@types/scope__name` is `@scope/name`.
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
"imports/resolvable": { options: { generated: ["packages/db/test/generated"] } },
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
`imports/cycles` reports each group of modules that import each other at run time once, at the alphabetically first of them, with one loop through the group. Its count is the number of modules in the group. `import type`, `export type`, type references and imports in declaration files are left out; `import { type X }` stays a runtime import. Dynamic `import()` and `require()` count; `require.resolve()` does not. It takes no registry exceptions. Each finding of `imports/resolvable` has the import as its subject.
|
|
221
|
+
|
|
222
|
+
The gate fails closed: when the sources hold no module at all, the imports rules stop the run instead of passing on an empty graph. Point `sources` at the code, or turn the rules off.
|
|
223
|
+
|
|
224
|
+
#### Fences
|
|
225
|
+
|
|
226
|
+
A fence is one prohibition, stated in the config without patterns:
|
|
227
|
+
|
|
228
|
+
```ts
|
|
229
|
+
import { defineConfig, external, fence, folders, packages } from "@shivaedev/quality";
|
|
230
|
+
|
|
231
|
+
const fences = [
|
|
232
|
+
fence("ui-never-imports-server")
|
|
233
|
+
.because("The UI ships to browsers.")
|
|
234
|
+
.from(folders("packages/ui/src"))
|
|
235
|
+
.mayNotImport(packages("server"))
|
|
236
|
+
.demonstratedBy({
|
|
237
|
+
illegal: ["packages/ui/src/index.ts", "packages/server/src/index.ts"],
|
|
238
|
+
legal: ["packages/ui/src/index.ts", external("effect")],
|
|
239
|
+
}),
|
|
240
|
+
];
|
|
241
|
+
|
|
242
|
+
export default defineConfig({ rules: { "imports/fences": { options: { fences } } } });
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
A fence has a name, a reason, the modules it holds (`from`) and one prohibition:
|
|
246
|
+
|
|
247
|
+
- `mayNotImport(target)`: no module it holds imports the target.
|
|
248
|
+
- `mayNotReach(target)`: nothing a module it holds imports, directly or through other modules of the repository, is the target. The finding names the path.
|
|
249
|
+
- `mayImportOnly(...subjects).of(unit)`: the modules it holds import only the named modules or folders directly in a package's `src` folder (or the package folder) or in a folder.
|
|
250
|
+
|
|
251
|
+
Targets are `packages(...)` (workspace packages by name, with or without their scope; a workspace package is a named `package.json` that `pnpm-workspace.yaml` or the root `package.json` `workspaces` includes, with `*`, `**`, `?`, `{a,b}` and `[...]` in its patterns, none of which match a folder whose name starts with `.` unless the pattern spells the dot, and a `packages` list or pattern the gate cannot read stops the run, and it holds every file below it that no deeper workspace package holds), `folders(...)`, `files(...)`, `modules(...)` (external packages by package name, and Node builtins), `scopes(...)` (every external package of a scope), `anyOf(...)`, `workspace` (every workspace package) and `anything`. Each takes `.except(...)`.
|
|
252
|
+
|
|
253
|
+
The config does not compile without `demonstratedBy`, and the rule checks the examples against the policy: each is a chain of imports from a file of the repository, which may end in `external(name)`. The illegal example must cross this fence and no other; the legal example must cross none. Every name a fence uses must exist: a package, a folder that holds checked files, a checked file, a subject of the unit. Two fences may not share a name, and each needs a reason. A policy that breaks any of this stops the run. Fences count type imports too.
|
|
254
|
+
|
|
255
|
+
A finding has its fence's name as its subject, so a registry entry with that subject excuses one file from one fence.
|
|
256
|
+
|
|
257
|
+
### Manifests
|
|
258
|
+
|
|
259
|
+
`manifests/sorted` keeps every `package.json` in the repository in the key order of [sort-package-json](https://github.com/keithamus/sort-package-json), a dependency of this package. It walks the whole repository, not only the sources, and skips files ignored by git and `node_modules`. A manifest that is not valid JSON is reported too. `quality fix` sorts the manifests, and the rule takes no registry exceptions.
|
|
260
|
+
|
|
191
261
|
### Local rules
|
|
192
262
|
|
|
193
263
|
```ts
|
|
@@ -288,6 +358,7 @@ An entry covers a rule's violations in one file, for good, and must say why. Wit
|
|
|
288
358
|
|
|
289
359
|
```text
|
|
290
360
|
quality lint [--config <file>] [--warnings summary|all]
|
|
361
|
+
quality fix [--config <file>]
|
|
291
362
|
quality baseline write [--config <file>] [--rule <id>]...
|
|
292
363
|
quality baseline prune [--config <file>] [--against <ref>]
|
|
293
364
|
quality baseline tighten [--config <file>] [--staged]
|
|
@@ -303,6 +374,92 @@ quality baseline migrate [--config <file>] [--from <file>]
|
|
|
303
374
|
| 1 | Failed: an uncovered error-level violation, a baselined file that got worse, a stale registry entry, a baseline entry for a rule that is off or unknown, or a baseline that grew against the merge base. |
|
|
304
375
|
| 2 | Could not run: no or invalid config, an invalid baseline or registry, a baseline left in the earlier format, a missing source, a rule that threw, git history the check cannot read, or a usage error. |
|
|
305
376
|
|
|
377
|
+
## Biome preset
|
|
378
|
+
|
|
379
|
+
`@shivaedev/quality/biome` is one Biome setup for every repository. Biome is a dependency of this package, pinned to an exact version, and `quality` runs it, so a repository needs no Biome install of its own. The root `biome.json` extends the preset and adds only what is the repository's own:
|
|
380
|
+
|
|
381
|
+
```json
|
|
382
|
+
{
|
|
383
|
+
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
|
|
384
|
+
"extends": ["@shivaedev/quality/biome"]
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
The preset sets:
|
|
389
|
+
|
|
390
|
+
- **Formatting:** tabs, a line width of 150, double quotes, semicolons, trailing commas and operators at the start of a wrapped line.
|
|
391
|
+
- **Lint:** every rule Biome recommends, at `error`, and a list of stricter rules on top, among them `noUnsafeTypeAssertion`, `useBlockStatements`, `noEqualsToNull`, `useNumericSeparators`, `useUnicodeRegex`, `useLiteralKeys`, `readonly T[]` arrays, `noFloatingPromises`, `noMisusedPromises`, `useExhaustiveSwitchCases`, `useExhaustiveDependencies`, `it` for every test, function declarations over function expressions, interfaces for object types, a cognitive complexity limit of 15, no nested ternaries, no barrel files and no `export *`.
|
|
392
|
+
- **Assist:** organized imports and sorted keys, attributes, enum members, interface members and properties. Keys are sorted in JSON and in object literals alike; `package.json` is left out, because `manifests/sorted` gives it the order npm users expect.
|
|
393
|
+
- **Plugins:** GritQL rules that ban ambient time, randomness, `console` and `process.env` for Effect's services, and that ask `Effect.fn` for a literal span name shaped `Owner.operation`. They load from `./node_modules/@shivaedev/quality/biome/plugins`, so the package must be installed at the repository root.
|
|
394
|
+
- Files ignored by git are skipped.
|
|
395
|
+
|
|
396
|
+
The preset turns off `noUnusedVariables` and `noUnusedFunctionParameters`, because the tsconfig presets report them through TypeScript, and allows default exports in `*.config.*` files, which tools load through the default export. It declares these weakenings in its `declarations.json`, so `suppressions/biome-overrides` takes them as declared. Every other weakening a repository adds is an override it declares with a reason.
|
|
397
|
+
|
|
398
|
+
The `biome` rule runs `biome check` with the repository's config and reports each finding as `biome/<category>`, such as `biome/lint/style/useBlockStatements`, `biome/assist/source/useSortedKeys`, `biome/format` or `biome/plugin`, so Biome's findings go through the baseline like any other rule's. `adopt: ["biome"]` and `quality baseline write --rule biome` take in every Biome category at once. A finding below `error`, such as a rule a repository declared at `warn`, is not reported. The rule also asks for a root `biome.json` or `biome.jsonc` that extends the preset, and it takes no registry exceptions. A Biome config that Biome cannot load stops the run with Biome's message.
|
|
399
|
+
|
|
400
|
+
`quality fix` sorts every `package.json`, then applies Biome's safe fixes, assist actions and formatting. An editor that runs Biome on save uses the same version when it resolves Biome from the root `node_modules`, so a repository that wants that installs `@biomejs/biome` at the version this package pins.
|
|
401
|
+
|
|
402
|
+
## Vitest projects
|
|
403
|
+
|
|
404
|
+
`@shivaedev/quality/vitest` sets up the tests of a package by file name, so no test file sets its environment with a pragma. It needs `vitest`, and `happy-dom` for DOM tests.
|
|
405
|
+
|
|
406
|
+
```ts
|
|
407
|
+
// vitest.config.ts
|
|
408
|
+
import { testProjects } from "@shivaedev/quality/vitest";
|
|
409
|
+
import { defineConfig } from "vitest/config";
|
|
410
|
+
|
|
411
|
+
export default defineConfig({ test: testProjects() });
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
| Project | Files | Environment | Runs |
|
|
415
|
+
| --- | --- | --- | --- |
|
|
416
|
+
| `unit` | `*.test.ts` and the other test files | Node | By default |
|
|
417
|
+
| `dom` | `*.dom.test.ts`, `*.dom.test.tsx` | happy-dom | By default |
|
|
418
|
+
| `slow` | `*.slow.test.ts`, `*.slow.test.tsx` | Node | Only with `vitest run --project slow` |
|
|
419
|
+
|
|
420
|
+
Type tests (`*.typecheck.test.ts` and `typecheck.test.ts`) are in no project: the compiler checks them. A test that is too slow for every run goes into the slow project instead of being skipped.
|
|
421
|
+
|
|
422
|
+
## tsconfig presets
|
|
423
|
+
|
|
424
|
+
Two presets hold one strict, current TypeScript setup for every repository. They target TypeScript 7.
|
|
425
|
+
|
|
426
|
+
| Preset | Use |
|
|
427
|
+
| --- | --- |
|
|
428
|
+
| `@shivaedev/quality/tsconfig/base.json` | Type-checking with `noEmit`: applications, tests and scripts. |
|
|
429
|
+
| `@shivaedev/quality/tsconfig/package.json` | Building a package: the base plus `declaration`, `declarationMap`, `sourceMap` and `rewriteRelativeImportExtensions`, so relative `.ts` imports are emitted as `.js`. |
|
|
430
|
+
|
|
431
|
+
```json
|
|
432
|
+
{
|
|
433
|
+
"extends": "@shivaedev/quality/tsconfig/base.json",
|
|
434
|
+
"compilerOptions": {
|
|
435
|
+
"lib": ["ESNext", "DOM"],
|
|
436
|
+
"types": ["node"]
|
|
437
|
+
},
|
|
438
|
+
"include": ["src", "test"]
|
|
439
|
+
}
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
The base sets:
|
|
443
|
+
|
|
444
|
+
- **Language:** `target` and `lib` ESNext, so the newest syntax is emitted as written and the newest built-ins are typed.
|
|
445
|
+
- **Modules:** `module` ESNext, `moduleResolution` bundler, `moduleDetection` force, `verbatimModuleSyntax`, `isolatedModules`, `allowImportingTsExtensions`, `resolveJsonModule`, `noUncheckedSideEffectImports`, `forceConsistentCasingInFileNames` and `libReplacement: false`.
|
|
446
|
+
- **JavaScript:** `allowJs` and `checkJs`, so JavaScript files are type-checked with the TypeScript ones.
|
|
447
|
+
- **Erasable syntax only:** `erasableSyntaxOnly` rejects syntax that type stripping cannot erase, such as parameter properties.
|
|
448
|
+
- **Checks:** `strict`, with `noImplicitAny`, `strictBuiltinIteratorReturn` and `useUnknownInCatchVariables` stated explicitly, `noUncheckedIndexedAccess`, `exactOptionalPropertyTypes`, `noImplicitOverride`, `noImplicitReturns`, `noFallthroughCasesInSwitch`, `noUnusedLocals`, `noUnusedParameters`, `allowUnreachableCode: false` and `allowUnusedLabels: false`.
|
|
449
|
+
- **Compilers:** `stableTypeOrdering`, so a TypeScript 6 build orders types the way TypeScript 7 does, and `skipLibCheck`.
|
|
450
|
+
|
|
451
|
+
`noPropertyAccessFromIndexSignature` stays off, so a map-like key is read with dot access, and `isolatedDeclarations` stays off, so exported values keep their inferred types. `types`, `jsx`, `paths`, `include` and the output directories belong to each project. A project that needs more built-ins, such as the DOM, sets `lib` itself and keeps `ESNext` in it.
|
|
452
|
+
|
|
453
|
+
A package that type-checks its tests with one config and builds `src` with another extends both, the package preset last:
|
|
454
|
+
|
|
455
|
+
```json
|
|
456
|
+
{
|
|
457
|
+
"extends": ["./tsconfig.json", "@shivaedev/quality/tsconfig/package.json"],
|
|
458
|
+
"compilerOptions": { "outDir": "dist", "rootDir": "src" },
|
|
459
|
+
"include": ["src"]
|
|
460
|
+
}
|
|
461
|
+
```
|
|
462
|
+
|
|
306
463
|
## Validation
|
|
307
464
|
|
|
308
|
-
`pnpm ready` checks formatting,
|
|
465
|
+
`pnpm ready` checks formatting, TypeScript 7, the rules, the import graph against seeded repositories and the fence policy against its examples, config, discovery, registry, baseline and report behavior, the command line against seeded repositories and git histories, the Biome preset against every rule Biome recommends and against its declarations, the `biome` and `manifests/sorted` rules and `quality fix` against seeded repositories, the Vitest projects against a seeded repository that Vitest runs, an installed tarball consumer that type-checks a config and runs the `quality` bin through a baseline that takes in the preset's lint and plugin findings, and installed consumers that extend each tsconfig preset, type-check a fixture with an expected error for each check the base turns on, and run the package preset's build output.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"includes": ["**"],
|
|
4
|
+
"reason": "TypeScript reports unused variables through noUnusedLocals in the shared tsconfig, so Biome would report each one twice.",
|
|
5
|
+
"rule": "lint/correctness/noUnusedVariables"
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"includes": ["**"],
|
|
9
|
+
"reason": "TypeScript reports unused parameters through noUnusedParameters in the shared tsconfig, so Biome would report each one twice.",
|
|
10
|
+
"rule": "lint/correctness/noUnusedFunctionParameters"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"includes": ["**/*.config.*"],
|
|
14
|
+
"reason": "Tools load their config files through the default export.",
|
|
15
|
+
"rule": "lint/style/noDefaultExport"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
engine biome(1.0)
|
|
2
|
+
language js(typescript, jsx)
|
|
3
|
+
|
|
4
|
+
or {
|
|
5
|
+
or {
|
|
6
|
+
`Date.now()` as $match,
|
|
7
|
+
`new Date()` as $match
|
|
8
|
+
} where { register_diagnostic(span=$match, message="Ambient time is banned. Read time through Effect's Clock service.") },
|
|
9
|
+
`Math.random()` as $match where { register_diagnostic(span=$match, message="Ambient randomness is banned. Use Effect's Random service.") },
|
|
10
|
+
or {
|
|
11
|
+
`console.$method($...)` as $match,
|
|
12
|
+
`console[$method]($...)` as $match
|
|
13
|
+
} where { register_diagnostic(span=$match, message="console.* is banned. Log through Effect's logger.") },
|
|
14
|
+
or {
|
|
15
|
+
JsStaticMemberExpression() as $match where { $match <: `process.env` },
|
|
16
|
+
JsComputedMemberExpression() as $match where { $match <: `process["env"]` }
|
|
17
|
+
} where { register_diagnostic(span=$match, message="process.env is banned. Read configuration through Effect's Config service.") }
|
|
18
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
engine biome(1.0)
|
|
2
|
+
language js(typescript, jsx)
|
|
3
|
+
|
|
4
|
+
`Effect.fn($name)` as $match where {
|
|
5
|
+
! $name <: r"^[\"'][A-Z][A-Za-z0-9]*\.[a-z][A-Za-z0-9]*[\"']$",
|
|
6
|
+
register_diagnostic(span=$match, message="Effect.fn takes a literal span name shaped PascalCaseOwner.lowerCamelCaseOperation, such as \"Transact.invalidateOnCommit\".")
|
|
7
|
+
}
|