@shivaedev/quality 0.24.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +71 -28
- package/dist/baseline/compare.js +1 -1
- package/dist/baseline/compare.js.map +1 -1
- package/dist/baseline/update.js +3 -3
- package/dist/baseline/update.js.map +1 -1
- package/dist/cli/args.d.ts +1 -3
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +5 -18
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/fix.js +1 -1
- package/dist/cli/fix.js.map +1 -1
- package/dist/cli/lint.d.ts +1 -2
- package/dist/cli/lint.d.ts.map +1 -1
- package/dist/cli/lint.js +1 -2
- package/dist/cli/lint.js.map +1 -1
- package/dist/cli/messages.d.ts +2 -2
- package/dist/cli/messages.d.ts.map +1 -1
- package/dist/cli/messages.js +7 -3
- package/dist/cli/messages.js.map +1 -1
- package/dist/cli/migrate.js +1 -1
- package/dist/cli/migrate.js.map +1 -1
- package/dist/cli/migrateExceptions.js +1 -1
- package/dist/cli/migrateExceptions.js.map +1 -1
- package/dist/cli/program.js +1 -1
- package/dist/cli/program.js.map +1 -1
- package/dist/cli/{run-main.d.ts → runMain.d.ts} +1 -1
- package/dist/cli/runMain.d.ts.map +1 -0
- package/dist/cli/{run-main.js → runMain.js} +1 -1
- package/dist/cli/runMain.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/config/decode.d.ts +0 -2
- package/dist/config/decode.d.ts.map +1 -1
- package/dist/config/decode.js +0 -8
- package/dist/config/decode.js.map +1 -1
- package/dist/config/resolve.d.ts +1 -3
- package/dist/config/resolve.d.ts.map +1 -1
- package/dist/config/resolve.js +1 -1
- package/dist/config/resolve.js.map +1 -1
- package/dist/config.d.ts +2 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/decoded.d.ts +1 -1
- package/dist/decoded.d.ts.map +1 -1
- package/dist/decoded.js +1 -1
- package/dist/decoded.js.map +1 -1
- package/dist/engine/{baseline-file.d.ts → baselineFile.d.ts} +1 -1
- package/dist/engine/baselineFile.d.ts.map +1 -0
- package/dist/engine/{baseline-file.js → baselineFile.js} +1 -1
- package/dist/engine/baselineFile.js.map +1 -0
- package/dist/engine/evaluate.d.ts +0 -1
- package/dist/engine/evaluate.d.ts.map +1 -1
- package/dist/engine/evaluate.js +1 -2
- package/dist/engine/evaluate.js.map +1 -1
- package/dist/engine/{run-rules.d.ts → runRules.d.ts} +1 -1
- package/dist/engine/runRules.d.ts.map +1 -0
- package/dist/engine/{run-rules.js → runRules.js} +2 -2
- package/dist/engine/runRules.js.map +1 -0
- package/dist/engine/session.d.ts +1 -1
- package/dist/engine/session.d.ts.map +1 -1
- package/dist/engine/session.js +2 -2
- package/dist/engine/session.js.map +1 -1
- package/dist/engine/violation.d.ts +1 -1
- package/dist/engine/violation.d.ts.map +1 -1
- package/dist/engine/violation.js +1 -1
- package/dist/engine/violation.js.map +1 -1
- package/dist/hooks/preCommit.js +1 -1
- package/dist/hooks/preCommit.js.map +1 -1
- package/dist/imports/generated.js +1 -1
- package/dist/imports/generated.js.map +1 -1
- package/dist/imports/{pnpm-workspace.d.ts → pnpmWorkspacePatterns.d.ts} +1 -1
- package/dist/imports/pnpmWorkspacePatterns.d.ts.map +1 -0
- package/dist/imports/{pnpm-workspace.js → pnpmWorkspacePatterns.js} +1 -1
- package/dist/imports/pnpmWorkspacePatterns.js.map +1 -0
- package/dist/imports/resolve.js +1 -1
- package/dist/imports/resolve.js.map +1 -1
- package/dist/imports/workspace.js +1 -1
- package/dist/imports/workspace.js.map +1 -1
- package/dist/inventory/collect.js +1 -1
- package/dist/inventory/collect.js.map +1 -1
- package/dist/inventory/{ignore-scope.d.ts → ignoreScope.d.ts} +1 -1
- package/dist/inventory/ignoreScope.d.ts.map +1 -0
- package/dist/inventory/{ignore-scope.js → ignoreScope.js} +1 -1
- package/dist/inventory/ignoreScope.js.map +1 -0
- package/dist/inventory/walk.d.ts +1 -1
- package/dist/inventory/walk.d.ts.map +1 -1
- package/dist/inventory/walk.js +1 -1
- package/dist/inventory/walk.js.map +1 -1
- package/dist/manifests/sorted.js +1 -1
- package/dist/manifests/sorted.js.map +1 -1
- package/dist/report/render.d.ts +0 -2
- package/dist/report/render.d.ts.map +1 -1
- package/dist/report/render.js +6 -16
- package/dist/report/render.js.map +1 -1
- package/dist/report/stale.js +1 -1
- package/dist/report/stale.js.map +1 -1
- package/dist/rule.d.ts +1 -1
- package/dist/rule.d.ts.map +1 -1
- package/dist/rule.js +1 -1
- package/dist/rule.js.map +1 -1
- package/dist/rules/{built-in.d.ts → builtIn.d.ts} +2 -2
- package/dist/rules/builtIn.d.ts.map +1 -0
- package/dist/rules/{built-in.js → builtIn.js} +15 -13
- package/dist/rules/builtIn.js.map +1 -0
- package/dist/rules/comments/{pattern-rule.d.ts → definePattern.d.ts} +1 -1
- package/dist/rules/comments/definePattern.d.ts.map +1 -0
- package/dist/rules/comments/{pattern-rule.js → definePattern.js} +1 -1
- package/dist/rules/comments/definePattern.js.map +1 -0
- package/dist/rules/comments/{max-per-file.d.ts → maxPerFile.d.ts} +1 -1
- package/dist/rules/comments/maxPerFile.d.ts.map +1 -0
- package/dist/rules/comments/{max-per-file.js → maxPerFile.js} +1 -1
- package/dist/rules/comments/maxPerFile.js.map +1 -0
- package/dist/rules/comments/{no-banner.d.ts → noBanner.d.ts} +1 -1
- package/dist/rules/comments/noBanner.d.ts.map +1 -0
- package/dist/rules/comments/{no-banner.js → noBanner.js} +2 -2
- package/dist/rules/comments/noBanner.js.map +1 -0
- package/dist/rules/comments/{no-environment-pragma.d.ts → noEnvironmentPragma.d.ts} +1 -1
- package/dist/rules/comments/noEnvironmentPragma.d.ts.map +1 -0
- package/dist/rules/comments/{no-environment-pragma.js → noEnvironmentPragma.js} +2 -2
- package/dist/rules/comments/noEnvironmentPragma.js.map +1 -0
- package/dist/rules/comments/{no-jsdoc.d.ts → noJsdoc.d.ts} +1 -1
- package/dist/rules/comments/noJsdoc.d.ts.map +1 -0
- package/dist/rules/comments/{no-jsdoc.js → noJsdoc.js} +1 -1
- package/dist/rules/comments/noJsdoc.js.map +1 -0
- package/dist/rules/comments/{no-line-reference.d.ts → noLineReference.d.ts} +1 -1
- package/dist/rules/comments/noLineReference.d.ts.map +1 -0
- package/dist/rules/comments/{no-line-reference.js → noLineReference.js} +2 -2
- package/dist/rules/comments/noLineReference.js.map +1 -0
- package/dist/rules/comments/{no-pr-reference.d.ts → noPrReference.d.ts} +1 -1
- package/dist/rules/comments/noPrReference.d.ts.map +1 -0
- package/dist/rules/comments/{no-pr-reference.js → noPrReference.js} +2 -2
- package/dist/rules/comments/noPrReference.js.map +1 -0
- package/dist/rules/comments/{no-todo.d.ts → noTodo.d.ts} +1 -1
- package/dist/rules/comments/noTodo.d.ts.map +1 -0
- package/dist/rules/comments/{no-todo.js → noTodo.js} +2 -2
- package/dist/rules/comments/noTodo.js.map +1 -0
- package/dist/rules/{manifests-sorted.d.ts → manifestsSorted.d.ts} +1 -1
- package/dist/rules/manifestsSorted.d.ts.map +1 -0
- package/dist/rules/{manifests-sorted.js → manifestsSorted.js} +1 -1
- package/dist/rules/manifestsSorted.js.map +1 -0
- package/dist/rules/manifestsSourceFirst.d.ts +2 -0
- package/dist/rules/manifestsSourceFirst.d.ts.map +1 -0
- package/dist/rules/manifestsSourceFirst.js +43 -0
- package/dist/rules/manifestsSourceFirst.js.map +1 -0
- package/dist/rules/{max-lines.d.ts → maxLines.d.ts} +1 -1
- package/dist/rules/maxLines.d.ts.map +1 -0
- package/dist/rules/{max-lines.js → maxLines.js} +1 -1
- package/dist/rules/maxLines.js.map +1 -0
- package/dist/rules/suppressions/{biome-overrides.d.ts → biomeOverrides.d.ts} +1 -1
- package/dist/rules/suppressions/biomeOverrides.d.ts.map +1 -0
- package/dist/rules/suppressions/{biome-overrides.js → biomeOverrides.js} +1 -1
- package/dist/rules/suppressions/biomeOverrides.js.map +1 -0
- package/dist/rules/suppressions/{no-double-cast.d.ts → noDoubleCast.d.ts} +1 -1
- package/dist/rules/suppressions/noDoubleCast.d.ts.map +1 -0
- package/dist/rules/suppressions/{no-double-cast.js → noDoubleCast.js} +1 -1
- package/dist/rules/suppressions/noDoubleCast.js.map +1 -0
- package/dist/rules/suppressions/noIgnoreDeprecations.js +1 -1
- package/dist/rules/suppressions/noIgnoreDeprecations.js.map +1 -1
- package/dist/rules/suppressions/{no-inline.d.ts → noInline.d.ts} +1 -1
- package/dist/rules/suppressions/noInline.d.ts.map +1 -0
- package/dist/rules/suppressions/{no-inline.js → noInline.js} +2 -2
- package/dist/rules/suppressions/noInline.js.map +1 -0
- package/dist/rules/suppressions/{stylesheet-comments.d.ts → stylesheetComments.d.ts} +1 -1
- package/dist/rules/suppressions/stylesheetComments.d.ts.map +1 -0
- package/dist/rules/suppressions/{stylesheet-comments.js → stylesheetComments.js} +1 -1
- package/dist/rules/suppressions/stylesheetComments.js.map +1 -0
- package/dist/{standard-schema.d.ts → standardSchema.d.ts} +1 -1
- package/dist/standardSchema.d.ts.map +1 -0
- package/dist/{standard-schema.js → standardSchema.js} +1 -1
- package/dist/standardSchema.js.map +1 -0
- package/dist/testProjects.d.ts +1 -1
- package/dist/testProjects.d.ts.map +1 -1
- package/dist/testProjects.js +4 -4
- package/dist/testProjects.js.map +1 -1
- package/docs/debt-and-hooks.md +9 -38
- package/docs/north-star.md +0 -3
- package/package.json +2 -3
- package/src/baseline/compare.ts +1 -1
- package/src/baseline/update.ts +3 -3
- package/src/cli/args.ts +6 -21
- package/src/cli/fix.ts +1 -1
- package/src/cli/lint.ts +1 -3
- package/src/cli/messages.ts +7 -4
- package/src/cli/migrate.ts +1 -1
- package/src/cli/migrateExceptions.ts +1 -1
- package/src/cli/program.ts +1 -1
- package/src/cli.ts +1 -1
- package/src/config/decode.ts +0 -11
- package/src/config/resolve.ts +2 -2
- package/src/config.ts +2 -2
- package/src/decoded.ts +1 -1
- package/src/engine/evaluate.ts +1 -3
- package/src/engine/{run-rules.ts → runRules.ts} +1 -1
- package/src/engine/session.ts +2 -2
- package/src/engine/violation.ts +2 -2
- package/src/hooks/preCommit.ts +1 -1
- package/src/imports/generated.ts +1 -1
- package/src/imports/resolve.ts +1 -1
- package/src/imports/workspace.ts +1 -1
- package/src/inventory/collect.ts +1 -1
- package/src/inventory/walk.ts +1 -1
- package/src/manifests/sorted.ts +1 -1
- package/src/report/render.ts +6 -26
- package/src/report/stale.ts +1 -1
- package/src/rule.ts +1 -1
- package/src/rules/{built-in.ts → builtIn.ts} +14 -12
- package/src/rules/comments/{no-banner.ts → noBanner.ts} +1 -1
- package/src/rules/comments/{no-environment-pragma.ts → noEnvironmentPragma.ts} +1 -1
- package/src/rules/comments/{no-line-reference.ts → noLineReference.ts} +1 -1
- package/src/rules/comments/{no-pr-reference.ts → noPrReference.ts} +1 -1
- package/src/rules/comments/{no-todo.ts → noTodo.ts} +1 -1
- package/src/rules/manifestsSourceFirst.ts +48 -0
- package/src/rules/suppressions/noIgnoreDeprecations.ts +1 -1
- package/src/rules/suppressions/{no-inline.ts → noInline.ts} +1 -1
- package/src/testProjects.ts +4 -4
- package/dist/cli/run-main.d.ts.map +0 -1
- package/dist/cli/run-main.js.map +0 -1
- package/dist/engine/baseline-file.d.ts.map +0 -1
- package/dist/engine/baseline-file.js.map +0 -1
- package/dist/engine/run-rules.d.ts.map +0 -1
- package/dist/engine/run-rules.js.map +0 -1
- package/dist/imports/pnpm-workspace.d.ts.map +0 -1
- package/dist/imports/pnpm-workspace.js.map +0 -1
- package/dist/inventory/ignore-scope.d.ts.map +0 -1
- package/dist/inventory/ignore-scope.js.map +0 -1
- package/dist/rules/built-in.d.ts.map +0 -1
- package/dist/rules/built-in.js.map +0 -1
- package/dist/rules/comments/max-per-file.d.ts.map +0 -1
- package/dist/rules/comments/max-per-file.js.map +0 -1
- package/dist/rules/comments/no-banner.d.ts.map +0 -1
- package/dist/rules/comments/no-banner.js.map +0 -1
- package/dist/rules/comments/no-environment-pragma.d.ts.map +0 -1
- package/dist/rules/comments/no-environment-pragma.js.map +0 -1
- package/dist/rules/comments/no-jsdoc.d.ts.map +0 -1
- package/dist/rules/comments/no-jsdoc.js.map +0 -1
- package/dist/rules/comments/no-line-reference.d.ts.map +0 -1
- package/dist/rules/comments/no-line-reference.js.map +0 -1
- package/dist/rules/comments/no-pr-reference.d.ts.map +0 -1
- package/dist/rules/comments/no-pr-reference.js.map +0 -1
- package/dist/rules/comments/no-todo.d.ts.map +0 -1
- package/dist/rules/comments/no-todo.js.map +0 -1
- package/dist/rules/comments/pattern-rule.d.ts.map +0 -1
- package/dist/rules/comments/pattern-rule.js.map +0 -1
- package/dist/rules/manifests-sorted.d.ts.map +0 -1
- package/dist/rules/manifests-sorted.js.map +0 -1
- package/dist/rules/max-lines.d.ts.map +0 -1
- package/dist/rules/max-lines.js.map +0 -1
- package/dist/rules/suppressions/biome-overrides.d.ts.map +0 -1
- package/dist/rules/suppressions/biome-overrides.js.map +0 -1
- package/dist/rules/suppressions/no-double-cast.d.ts.map +0 -1
- package/dist/rules/suppressions/no-double-cast.js.map +0 -1
- package/dist/rules/suppressions/no-inline.d.ts.map +0 -1
- package/dist/rules/suppressions/no-inline.js.map +0 -1
- package/dist/rules/suppressions/stylesheet-comments.d.ts.map +0 -1
- package/dist/rules/suppressions/stylesheet-comments.js.map +0 -1
- package/dist/standard-schema.d.ts.map +0 -1
- package/dist/standard-schema.js.map +0 -1
- package/docs/modules.md +0 -274
- package/docs/naming.md +0 -130
- package/docs/roadmap.md +0 -37
- package/docs/rules.md +0 -297
- package/docs/tooling.md +0 -105
- /package/src/cli/{run-main.ts → runMain.ts} +0 -0
- /package/src/engine/{baseline-file.ts → baselineFile.ts} +0 -0
- /package/src/imports/{pnpm-workspace.ts → pnpmWorkspacePatterns.ts} +0 -0
- /package/src/inventory/{ignore-scope.ts → ignoreScope.ts} +0 -0
- /package/src/rules/comments/{pattern-rule.ts → definePattern.ts} +0 -0
- /package/src/rules/comments/{max-per-file.ts → maxPerFile.ts} +0 -0
- /package/src/rules/comments/{no-jsdoc.ts → noJsdoc.ts} +0 -0
- /package/src/rules/{manifests-sorted.ts → manifestsSorted.ts} +0 -0
- /package/src/rules/{max-lines.ts → maxLines.ts} +0 -0
- /package/src/rules/suppressions/{biome-overrides.ts → biomeOverrides.ts} +0 -0
- /package/src/rules/suppressions/{no-double-cast.ts → noDoubleCast.ts} +0 -0
- /package/src/rules/suppressions/{stylesheet-comments.ts → stylesheetComments.ts} +0 -0
- /package/src/{standard-schema.ts → standardSchema.ts} +0 -0
package/docs/naming.md
DELETED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
# Naming and test layout
|
|
2
|
-
|
|
3
|
-
A name says what a thing is. The rules below give identifiers, files, folders and tests a common shape, so a reader can find the defining module and recognize test code. For the gate's workflow, see the [README](../README.md).
|
|
4
|
-
|
|
5
|
-
A name says what a thing is, and the same kind of thing is named the same way everywhere. Every naming rule is an error without an autofix: nothing renames code or adds a `_` prefix. The message says what is wrong and which shapes are valid, and the author picks the name. Unused variables and parameters stay TypeScript errors; remove them instead of prefixing them.
|
|
6
|
-
|
|
7
|
-
### Identifiers
|
|
8
|
-
|
|
9
|
-
| What | Valid shape | Checked by |
|
|
10
|
-
| --- | --- | --- |
|
|
11
|
-
| Variables, functions and parameters | camelCase: `itemCount`, `loadItem`. A component or a class is PascalCase. | `useNamingConvention` |
|
|
12
|
-
| Acronyms | Spelled as a word: `HttpClient`, `userId`, `parseUrl`. Never `HTTPClient`, `userID` or `parseURL`. | `useNamingConvention` with `strictCase` |
|
|
13
|
-
| Types, interfaces, classes and enums | PascalCase, without an `I` prefix: `Item`, not `IItem`. | `useNamingConvention` |
|
|
14
|
-
| Type parameters | `T`, or `T` followed by a PascalCase name: `TItem`, `TResult`. Effect's positional `A`, `E` and `R` are valid as they are. | `useNamingConvention` |
|
|
15
|
-
| Module-level constants | A string or number literal at module level is CONSTANT_CASE: `const MAX_ITEMS = 50`. A constant inside a function is camelCase. | `constant-names` plugin, `useNamingConvention` |
|
|
16
|
-
| Object keys and type properties | camelCase or PascalCase, after any leading `_` or `$` (`_tag`, `$transaction`), also in a `Record`. A quoted key is a [foreign name](./naming.md#foreign-names) and is not checked. | `key-names` plugin |
|
|
17
|
-
| Schemas | PascalCase and named like their type: `const Item = Schema.Struct(...)` with `type Item = typeof Item.Type`. | `schema-names` plugin |
|
|
18
|
-
| `Schema.Struct` fields | camelCase, also when quoted. Outside data keeps its keys only at the edge: `Schema.Struct({ createdAt: Schema.String }).pipe(Schema.encodeKeys({ createdAt: "created_at" }))`. | `schema-struct-keys` plugin, `key-names` plugin |
|
|
19
|
-
| `Effect.fn` spans | `"Owner.operation"`, where the owner is the service or module and the operation is the name the function is bound to: `const loadItem = Effect.fn("ItemStore.loadItem")`, and `loadItem: Effect.fn("ItemStore.loadItem")` in an object. | `effect-fn-spans` plugin |
|
|
20
|
-
| `Effect.gen` functions | A top-level function whose whole body is `Effect.gen` is an `Effect.fn`: `const loadItem = Effect.fn("ItemStore.loadItem")(function* (id: string) { … })`, or `Effect.fnUntraced` when it needs no span. | `effect-fn-functions` plugin |
|
|
21
|
-
| Effect test bodies | In test code, a test takes the generator itself: `it.effect("loads the item", function* () { … })`, never `() => Effect.gen(function* () { … })`. The test helper runs it with `Effect.gen`. | `effect-test-bodies` plugin |
|
|
22
|
-
| Service Layers | A static `layer` on the service, read as `ItemStore.layer`. No exported `ItemStoreLive` or `ItemStoreLayer` constant. A Layer that wires an application together stays unexported. | `service-layers` plugin |
|
|
23
|
-
| Private members | A `#field`. No `private`, `protected` or `public` modifier. | `useConsistentMemberAccessibility` |
|
|
24
|
-
| React | A function passed to an `onX` prop is `handleX`, an `onX` prop forwarded as it is, or a state setter passed as it is: `onOpenChange={setOpen}`. A context is `ThemeContext`, a ref `inputRef`, an id `fieldId`. A module that exports a component exports only components; a test may define the components it renders. | `handler-names` plugin, `useReactNamingConvention`, `useComponentExportOnlyModules` |
|
|
25
|
-
| Booleans | A question: `isOpen`, `hasSave`, `canRetry`, `shouldFlush`. A name the DOM or React gives, such as `open` or `disabled`, stays. This is a convention only; no rule checks it. | Review |
|
|
26
|
-
|
|
27
|
-
### Foreign names
|
|
28
|
-
|
|
29
|
-
Some names are not ours to choose: a query parameter an outside API reads, an environment variable, an operator a query builder takes. Write such a name as a quoted key, and the naming rules skip it:
|
|
30
|
-
|
|
31
|
-
```ts
|
|
32
|
-
const query = { "per_page": 50, "sort_by": "created" };
|
|
33
|
-
const env = { "DATABASE_URL": url };
|
|
34
|
-
const where = { "OR": [{ id }, { slug }] };
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
An unquoted key is our own name and is camelCase or PascalCase. The formatter keeps the quotes as written, so a quoted key stays quoted. A `Schema.Struct` field is the exception: the struct is our model of the data, so the `schema-struct-keys` plugin reports a snake_case field even when it is quoted, and points to `Schema.encodeKeys`, which maps camelCase fields to the outside names at the edge.
|
|
38
|
-
|
|
39
|
-
### Re-exports
|
|
40
|
-
|
|
41
|
-
No file re-exports, package entry files included: every module is imported from the file that defines it. `noBarrelFile` reports `export { … } from`, `noReExportAll` reports `export *`, the `type-re-exports` plugin reports `export type … from`, and `noExportedImports` reports an import that is exported again. Its message suggests `export … from`, which is a re-export as well: import the name where it is used instead.
|
|
42
|
-
|
|
43
|
-
The plugins report under the one Biome category `plugin`, so their findings share the baseline rule `biome/plugin`.
|
|
44
|
-
|
|
45
|
-
### Files
|
|
46
|
-
|
|
47
|
-
`files/named-after-export` names a code file after its main export, and the folders above it are the prefix. The export takes the words of the file name, in order, and may add words of its folders around them, in any order and in singular or plural. Folders count from the package root, without a leading `src`; the package name does not count. The file's first letter follows the export's case.
|
|
48
|
-
|
|
49
|
-
| File | Exports | Valid |
|
|
50
|
-
| --- | --- | --- |
|
|
51
|
-
| `routers/items/list.ts` | `listItems` or `itemList` | Yes |
|
|
52
|
-
| `routers/items/Create.ts` | `CreateItemRouter` | Yes |
|
|
53
|
-
| `item/Panel.tsx` or `ItemPanel.tsx` | `ItemPanel`, with `ItemPanelProps` beside it | Yes |
|
|
54
|
-
| `Panel/Panel.tsx` | `Panel`: the main file of a module folder repeats the folder | Yes |
|
|
55
|
-
| `limits.ts` | `MAX_ITEMS` and `MAX_DEPTH`: a topic file of several exports is camelCase | Yes |
|
|
56
|
-
| `ItemPanel.tsx` | `export default memo(ItemPanel)`: a default export counts under the name it resolves to | Yes |
|
|
57
|
-
| `setupTests.ts` | Nothing, so it is camelCase | Yes |
|
|
58
|
-
| `script/build-docs.ts` | Run by a `package.json` script or `bin`, or starts with `#!`, so it is kebab-case | Yes |
|
|
59
|
-
| `order.ts` | `type Order` | No: the file's case follows the export, so it is `Order.ts` |
|
|
60
|
-
| `maxItems.ts` | `MAX_ITEMS` alone | No: constants live in a topic file with related constants |
|
|
61
|
-
| `items/listItems.ts` | `listItems` | No: the folder is the prefix, so it is `items/list.ts` |
|
|
62
|
-
|
|
63
|
-
Test files are left to the test rules. The `toolOwned` option lists `.gitignore` patterns of files whose names a tool fixes, `*.config.*`, declaration files and `.railway/railway.ts` (also `.py` and `.go`) by default. The Railway paths apply at any project root; other files in `.railway/` still follow the naming rules. The shared Biome preset also permits default exports in these config paths.
|
|
64
|
-
|
|
65
|
-
### Folders
|
|
66
|
-
|
|
67
|
-
`files/folder-names` checks every folder above a checked file.
|
|
68
|
-
|
|
69
|
-
- A module folder takes the name of its main file, inside it or beside it: `Panel/` with `Panel/Panel.tsx`, or `Dialog/` beside `Dialog.tsx`. A PascalCase folder without that file is a finding.
|
|
70
|
-
- A package folder is its package name without the scope, so `@acme/ui-kit` lives in `ui-kit/`.
|
|
71
|
-
- A folder that only groups files is kebab-case: `test-support/`, `.github/`.
|
|
72
|
-
- A folder the `content` option lists is snake_case: `forest_path/`.
|
|
73
|
-
|
|
74
|
-
The `toolOwned` option lists folders a tool names, `migrations/` by default. Authored files under `generated/` follow the same naming checks as other source; ignored, untracked output stays outside discovery.
|
|
75
|
-
|
|
76
|
-
### Other files
|
|
77
|
-
|
|
78
|
-
`files/other-names` names the files that are not code.
|
|
79
|
-
|
|
80
|
-
- Markdown, JSON, GritQL, images, fonts and SVG are kebab-case, with optional dotted parts: `release-notes.md`, `icons.sprite.svg`. Conventional upper-case names stay: `README.md`, `CHANGELOG.md`, `LICENSE`, `AGENTS.md`, `CLAUDE.md`, `CONTRIBUTING.md`, `SECURITY.md` and `SKILL.md`.
|
|
81
|
-
- A stylesheet that one component imports is named after it: `ItemPanel.css` beside `ItemPanel.tsx`. A shared stylesheet is kebab-case: `form-controls.css`. An import is a static `import` in a code file, relative or through a `#` alias from the `imports` of its `package.json`, or an `@import` in a stylesheet; a stylesheet that another stylesheet imports is shared. A path in a string, such as a test that reads the file, is not an import.
|
|
82
|
-
- A Prisma schema is camelCase: `schema.prisma`.
|
|
83
|
-
- A file the `content` option lists is snake_case: `forest_path.json`. A double underscore separates the parts of an id: `forest_path__clearing.json`.
|
|
84
|
-
|
|
85
|
-
The `toolOwned` option lists files a tool names: dotfiles, `package.json`, `tsconfig*.json`, `biome.json`, `*.config.*` and `migrations/` by default.
|
|
86
|
-
|
|
87
|
-
### Case
|
|
88
|
-
|
|
89
|
-
`files/distinct-case` keeps every path apart on a case-insensitive filesystem, such as macOS or Windows. Two files or folders in one folder must differ by more than case, and so must the files a build makes of them: `ReadingHistory.tsx` and `readingHistory.ts` both build to `readingHistory.js`, so they are a finding. A folder pair is one finding, not one per file inside it.
|
|
90
|
-
|
|
91
|
-
### Tests
|
|
92
|
-
|
|
93
|
-
A test sits beside the code it covers, and its name says what it covers and where it runs.
|
|
94
|
-
|
|
95
|
-
| File | Covers | Valid |
|
|
96
|
-
| --- | --- | --- |
|
|
97
|
-
| `cart/cart.test.ts` | `cart/cart.ts` | Yes |
|
|
98
|
-
| `cart/cart.typecheck.test.ts` | The types of `cart/cart.ts`, checked by the compiler | Yes |
|
|
99
|
-
| `cart/Basket.dom.test.tsx` | `cart/Basket.tsx`, in a DOM | Yes |
|
|
100
|
-
| `cart/checkoutFlow.spec.ts` | A behaviour of the `cart/` folder as a whole, such as a flow across several files | Yes |
|
|
101
|
-
| `cart/checkoutFlow.dom.spec.tsx` | The same, in a DOM | Yes |
|
|
102
|
-
| `cart/checkoutFlow.typecheck.spec.ts` | The types of that behaviour, checked by the compiler | Yes |
|
|
103
|
-
| `cart/totals.test.ts` | No `cart/totals.ts` beside it | No: a `.test` follows a file, and a test of the folder is a `.spec` |
|
|
104
|
-
| `cart/cart.spec.ts` | | No: a `.spec` names a behaviour, so it may not share a stem with a file beside it |
|
|
105
|
-
| `cart/cart.hydration.test.ts` | | No: an aspect gets its own file in the module's folder, or a `.spec` |
|
|
106
|
-
| `cart/cart.postgres.test.ts` | | No: a database or a running app comes from the app's test fixture, not a file name |
|
|
107
|
-
| `test/cart.test.ts` | | No: a test folder mirrors the source tree; the test sits beside `cart.ts` |
|
|
108
|
-
|
|
109
|
-
`tests/follow` checks the name: `<file>[.<environment>].test.ts` beside its file, or `<behaviour>[.<environment>].spec.ts` in camelCase in the folder it covers. The environment is at most one of `dom`, `slow` and `typecheck`; [Vitest projects](./tooling.md#vitest-projects) run the runtime tests, and the compiler checks `.typecheck` files. For runtime tests, the rule requires `.dom` when it sees a static `@testing-library/*` import or dotted `document`/`window` member access. It does not track aliases, destructuring or computed access.
|
|
110
|
-
|
|
111
|
-
`tests/colocated` checks the place: a test in a `test`, `tests`, `__tests__` or `spec` folder is a finding.
|
|
112
|
-
|
|
113
|
-
Both rules take a `suites` option, `.gitignore` patterns of folders that hold tests with their own layout, such as tests across several packages or a Playwright suite, whose `.spec.ts` files mean something else. The rules skip those folders.
|
|
114
|
-
|
|
115
|
-
### Test code
|
|
116
|
-
|
|
117
|
-
Test code is a test file (`*.test.ts` or `*.spec.ts`, with any environment), or any file under a folder named `test-support/`. `test-support/` is the folder for fixtures, harnesses and generated clients that tests share. `imports/fences` never holds test code, and `structure/max-lines` gives it the test limit. `imports/no-test-code` prevents production modules from importing test code; put code they share in a production module.
|
|
118
|
-
|
|
119
|
-
Name shared helpers without a `.test` or `.spec` suffix. `tests/follow` and `tests/colocated` use those suffixes to identify test files; the folder name alone does not make them skip a test-named file. `tests/story-setup` skips every file under `test-support/`, including a test-named file there.
|
|
120
|
-
|
|
121
|
-
### Story tests
|
|
122
|
-
|
|
123
|
-
Keep recurring setup in a domain kit under `test-support/`; the [test-story README](https://github.com/ShivaeDev/platform/tree/main/packages/test-story#readme) explains traits, verbs and a kit's `it`. Quality checks the setup patterns below; it does not establish which engine a test runs.
|
|
124
|
-
|
|
125
|
-
`tests/story-setup` reports, in a test file:
|
|
126
|
-
|
|
127
|
-
- A top-level function whose name starts with the word `seed`, `make`, `build` or `setup`, declared as a function or a variable holding an arrow function or function expression. The prefix is a whole word, so `builder` and `settings` pass. Nested helpers and declarations without a body are outside this check.
|
|
128
|
-
- A call that writes fixture files through Node's `fs` or `fs/promises`: `writeFile`, `appendFile`, `mkdir`, `mkdtemp`, `copyFile`, `cp` and `symlink`, including their sync forms. Named, renamed, namespace, default and `promises` imports are checked.
|
|
129
|
-
|
|
130
|
-
Each finding names its line and uses the helper or called function as its subject. The message asks for setup as traits of the kit and links to the README's story-test guidance. The rule reports without rewriting a test. Record existing findings with `quality baseline write --rule tests/story-setup` when adopting it.
|
package/docs/roadmap.md
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# Roadmap
|
|
2
|
-
|
|
3
|
-
This page owns Quality's shared policy, tooling, CLI and hook implementation status and the policy questions below. The [framework roadmap](https://github.com/ShivaeDev/platform/blob/main/docs/framework/roadmap.md) owns adoption across packages and hosts, integration and deployment validation; it links here for quality implementation work.
|
|
4
|
-
|
|
5
|
-
## Built
|
|
6
|
-
|
|
7
|
-
- [x] Optional typed and runtime-validated config with config-free defaults; built-in and local rules enforced at `error`; local-rule and Standard Schema option APIs; one checked inventory and one report grouped by rule.
|
|
8
|
-
- [x] The public config rejects `warn`, `off`, raised thresholds, inventory exclusions and custom test-file patterns. Vitest projects reject suite exclusions. Reasoned Biome declarations cover individual rules on explicit files and named directory exclusions.
|
|
9
|
-
- [x] A JSON Lines baseline with counted debt, explicit named-rule recording, lowering and pruning, tracked-move handling and preservation of unrelated lines.
|
|
10
|
-
- [x] One reasoned config exception list for Quality and Biome, generated native Biome settings at lint and fix time, legacy migration, optional Quality subjects, stale entries and rules that refuse exceptions.
|
|
11
|
-
- [x] File-size and comment rules; inline-suppression, double-cast, deprecation-setting and declared-Biome-override checks.
|
|
12
|
-
- [x] A shared Biome preset and bridge whose error findings use the gate's baseline; enforcement of the installed Biome's recommended rules; bounded fix rounds and selected fixes left to the author.
|
|
13
|
-
- [x] Static import resolution, rejection of production imports of test code, runtime-cycle checks, same-folder relative imports and direct, transitive and vocabulary fences with validated examples.
|
|
14
|
-
- [x] Manifest sorting and identifier, file, folder, content, test-layout and story-setup conventions.
|
|
15
|
-
- [x] Shared type-check and package-build TypeScript presets; Vitest unit, DOM and opt-in slow projects, excluded type tests and inherited project tags.
|
|
16
|
-
- [x] Opt-in pre-commit hooks with per-worktree config and dependencies, conventional `typecheck` and `quality:commit` scripts, and explicit installation.
|
|
17
|
-
- [x] Seeded-repository and real-command tests for the gate, import policies, Biome, baseline and git hooks, plus packed consumer checks for the CLI and presets.
|
|
18
|
-
- [x] Separate shipped Biome declarations from consumer exceptions; reject whole-check, group and blanket-scope consumer declarations.
|
|
19
|
-
- [x] Shared nesting-depth and index-module checks; Platform's catalog, manifest and Codecov checks use the local-rule API and the same inventory, report and baseline.
|
|
20
|
-
|
|
21
|
-
## Next
|
|
22
|
-
|
|
23
|
-
- [ ] Remove obsolete CLI warning-report controls; config levels are already enforced at `error`. Keep old-debt adoption in the baseline.
|
|
24
|
-
- [ ] Reduce policy-tuning inputs to the ones checks need about a repository. Thresholds can only be lowered; local-rule APIs remain exposed. Review the remaining extension boundary.
|
|
25
|
-
- [ ] Split Grit implementation modules by the checks they own, without making consumers select competing presets; route Platform's remaining static checks through Quality.
|
|
26
|
-
- [ ] Define the Effect-boundary rule's source and test scope before porting the repository's `try`/`async` restrictions.
|
|
27
|
-
- [ ] Define the service-parameter policy before enforcing it against public helpers that accept services or `Context`.
|
|
28
|
-
|
|
29
|
-
## Open questions
|
|
30
|
-
|
|
31
|
-
The whole-policy direction is settled: Quality selects which checks run, and enabled checks should fail on uncovered findings; repositories adopt its defaults. The questions below concern how to implement that policy and where checks apply.
|
|
32
|
-
|
|
33
|
-
- **Extension policy.** Which repository-specific local-rule APIs and Biome declarations should remain public? Consumer rule switches, raised limits and exclusions are rejected immediately with a migration fragment.
|
|
34
|
-
- **Effect boundaries in tests.** Should tests follow the same Effect-only boundaries as shipped code, use a test-specific adapter scope, or remain exempt? The repository rule and test fixture patterns do not settle this policy.
|
|
35
|
-
- **Services as parameters.** Should a rule cover only service implementation methods, allow explicit integration helpers, or require public APIs that accept services or `Context` to change? The maintainer owns those API decisions before a reusable rule can enforce them.
|
|
36
|
-
- **Strict boolean expressions.** Should the preset enable Biome's nursery `useStrictBooleanExpressions`? It reports truthiness checks whose falsy cases differ, such as `if (value)` on `string | undefined`, where an empty string reads as missing. It has 63 findings across this repository, mostly in `work-fleet` and `work-board`, and some of them rely on `||` treating `""` as absent on purpose. The open choice is whether to adopt it with the existing findings in the baseline or fix them first, and whether to wait until Biome's reworked type inference behind the rule settles.
|
|
37
|
-
- **Module exports.** The `./*.ts` export pattern makes implementation modules accessible alongside config, rules, fences and Vitest helpers. Which of those modules should consumers treat as extension APIs? Documentation lists the current exports; changing their public boundary requires a maintainer decision.
|
package/docs/rules.md
DELETED
|
@@ -1,297 +0,0 @@
|
|
|
1
|
-
# Rule reference
|
|
2
|
-
|
|
3
|
-
Use the [README](../README.md) for the gate's mental model and workflow. Quality owns the shared policy; consuming repositories adopt its defaults. This reference gives each built-in rule's scope, findings and accepted options, including the exposed local-rule API. An option being accepted is not a recommendation to tune the shared standard. Supply repository facts, such as generated locations and import boundaries, when a check needs them. The [naming reference](./naming.md) covers names and test layout; [shared tooling](./tooling.md) covers the Biome, Vitest and TypeScript setup.
|
|
4
|
-
|
|
5
|
-
## File structure
|
|
6
|
-
|
|
7
|
-
### File size
|
|
8
|
-
|
|
9
|
-
`structure/max-lines` keeps each module to one job: a source file may have 150 lines and [test code](./naming.md#test-code) 300, counted the way an editor numbers them. Declaration files are exempt. Its options are `source` and `test`: positive integer limits no greater than 150 and 300 respectively. Test code is identified by its conventional name or `test-support` folder; custom test-file patterns are rejected. A file over its limit counts one violation for each line above it: 168 lines under a limit of 150 count 18.
|
|
10
|
-
|
|
11
|
-
### Nesting and module structure
|
|
12
|
-
|
|
13
|
-
`nesting/max-depth` reports each source line that starts with eight or more tabs. Extract a named function or component. Declaration files are exempt; indentation with spaces is outside this text check. It takes no options.
|
|
14
|
-
|
|
15
|
-
`structure/no-barrel` rejects every source module named `index.ts` or `index.tsx`, including modules that define their own exports. Name the module after its purpose. It takes no options. The shared Biome preset separately rejects re-exports regardless of the file name.
|
|
16
|
-
|
|
17
|
-
### Comments
|
|
18
|
-
|
|
19
|
-
A comment says why, never what the code already says or what it used to be. Seven rules hold every comment to that:
|
|
20
|
-
|
|
21
|
-
| Rule | Reports | Options |
|
|
22
|
-
| --- | --- | --- |
|
|
23
|
-
| `comments/no-jsdoc` | Every `/** */` block, except a tool pragma the config allows | `allow` |
|
|
24
|
-
| `comments/no-line-reference` | A line number: `file.ts:42`, `file.ts#L42`, `line 42` | none |
|
|
25
|
-
| `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 |
|
|
26
|
-
| `comments/no-banner` | A banner, divider or region: a line that starts or ends with three or more of `- = * # ~ _ + / \ ─ ━ ═ ┄ ┈`, `#region`, `#endregion` | none |
|
|
27
|
-
| `comments/no-todo` | `TODO`, `FIXME`, `XXX` and `@todo` | none |
|
|
28
|
-
| `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](./tooling.md#vitest-projects) | none |
|
|
29
|
-
| `comments/max-per-file` | A file with more than `max` comments, 2 by default | `max` (an integer from 0 to 2), `allow` |
|
|
30
|
-
|
|
31
|
-
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.
|
|
32
|
-
|
|
33
|
-
Write a comment only for what the code cannot show, such as a constraint or a reason. The default limit of two comments is a tripwire against runaway commentary, not a budget to fill.
|
|
34
|
-
|
|
35
|
-
`comments/max-per-file` counts comments this way:
|
|
36
|
-
|
|
37
|
-
- A block comment counts once, however many lines it spans.
|
|
38
|
-
- 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.
|
|
39
|
-
- 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.
|
|
40
|
-
|
|
41
|
-
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.
|
|
42
|
-
|
|
43
|
-
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:
|
|
44
|
-
|
|
45
|
-
```ts
|
|
46
|
-
const pragmas = ["@jsxImportSource", "@license"];
|
|
47
|
-
|
|
48
|
-
export default defineConfig({
|
|
49
|
-
rules: {
|
|
50
|
-
"comments/no-jsdoc": { options: { allow: pragmas } },
|
|
51
|
-
"comments/max-per-file": { options: { allow: pragmas } },
|
|
52
|
-
},
|
|
53
|
-
});
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
A repository with existing comments adopts the rules through the baseline, for example `quality baseline write --rule comments/no-jsdoc --rule comments/max-per-file`.
|
|
57
|
-
|
|
58
|
-
### Suppressions
|
|
59
|
-
|
|
60
|
-
A check that is silenced at one site hides the problem instead of fixing it. Five rules close the escape hatches, and none of them takes exceptions: an exception that names one fails the gate as stale. A repository with existing suppressions adopts the rules through the baseline.
|
|
61
|
-
|
|
62
|
-
| Rule | Reports | Options |
|
|
63
|
-
| --- | --- | --- |
|
|
64
|
-
| `suppressions/no-inline` | Every comment directive that silences a linter, the compiler or a formatter, except `@ts-expect-error` in a type test | none |
|
|
65
|
-
| `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 |
|
|
66
|
-
| `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` |
|
|
67
|
-
| `suppressions/biome-recommended` | A rule the installed Biome recommends that the [Biome preset](./tooling.md#biome-preset) neither sets to `error` nor declares | none |
|
|
68
|
-
| `suppressions/no-ignore-deprecations` | `compilerOptions.ignoreDeprecations` in a tsconfig, which silences TypeScript's errors for deprecated options | none |
|
|
69
|
-
|
|
70
|
-
`suppressions/no-inline` reports these directives, wherever a line of a comment starts with one:
|
|
71
|
-
|
|
72
|
-
- Biome: `biome-ignore`, `biome-ignore-all` and `biome-ignore-start`, for lint, assist and format alike. The `biome-ignore-end` that closes a range is not reported again.
|
|
73
|
-
- TypeScript: `@ts-ignore`, `@ts-expect-error` and `@ts-nocheck`. `@ts-check` turns checking on and is allowed.
|
|
74
|
-
- Other linters: `eslint-disable`, `eslint-disable-line`, `eslint-disable-next-line` and inline rule settings such as `/* eslint no-console: "off" */`; the same `-disable` forms of `oxlint` and `stylelint`; `deno-lint-ignore` and `deno-lint-ignore-file`; `tslint:disable…`.
|
|
75
|
-
- Formatters: `prettier-ignore`, since `prettier --check` fails on the code it skips.
|
|
76
|
-
- Flow: `$FlowFixMe`, `$FlowIgnore`, `$FlowExpectedError`, `$FlowIssue` and `@noflow`.
|
|
77
|
-
|
|
78
|
-
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.
|
|
79
|
-
|
|
80
|
-
`suppressions/no-ignore-deprecations` reads every JSON file in the repository whose `compilerOptions` sets `ignoreDeprecations`, whatever the file is named, so a shared config such as `tsconfig/base.json` counts as much as a `tsconfig.json`. Like `manifests/sorted`, it walks the whole repository, not only the sources, and skips untracked files ignored by git and `node_modules`. Each finding names the line of the setting. Replace the deprecated option it hides, then remove it.
|
|
81
|
-
|
|
82
|
-
#### Type tests
|
|
83
|
-
|
|
84
|
-
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`, `*.typecheck.spec.ts` or `typecheck.test.ts` (or `.tsx`). The compiler checks these files; [Vitest projects](./tooling.md#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.
|
|
85
|
-
|
|
86
|
-
`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.
|
|
87
|
-
|
|
88
|
-
#### Declared Biome overrides
|
|
89
|
-
|
|
90
|
-
Consumer exceptions belong in `quality.config.ts`'s `exceptions` list, as `{ files, rules, reason, subject? }`. A single entry covers Quality rules and individual `biome/lint/<group>/<rule>` or `biome/assist/<group>/<action>` rules on explicit file paths. Quality applies the native Biome settings at lint and fix time; do not mirror them in `biome.json`. See [exceptions and upgrades](./debt-and-hooks.md#exceptions).
|
|
91
|
-
|
|
92
|
-
Globs, whole checks and rule groups cannot be excepted. A `biome/files/includes` entry must retain `**` and add only negative patterns naming a concrete directory, such as `!**/test-support/generated`. These patterns may start with `**/` and use `*` for an intermediate directory, as in `!**/test-support/*/generated`. Every other segment names a directory without dots except an optional leading dot; the final segment must be a name. File patterns, nested `**` and wildcard suffixes are rejected. Shipped preset declarations describe Quality's internal policy and are not subject to the consumer scope restriction.
|
|
93
|
-
|
|
94
|
-
Quality ships reasons for its own internal Biome exceptions. Extend the shared configuration and use the baseline for debt. `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:
|
|
95
|
-
|
|
96
|
-
| Setting | Detected as |
|
|
97
|
-
| --- | --- |
|
|
98
|
-
| A lint rule at `off`, `warn` or `info`, as a string or a `level` | `lint/<group>/<rule>` |
|
|
99
|
-
| A lint group at `off`, `warn` or `info`, or with `recommended: false` or `preset: "none"` | `lint/<group>` |
|
|
100
|
-
| `linter.rules` with `recommended: false` or `preset: "none"` | `lint/recommended` |
|
|
101
|
-
| A domain at `none` | `lint/domains/<domain>` |
|
|
102
|
-
| `linter.enabled: false` | `lint` |
|
|
103
|
-
| An assist action at `off`, such as `useSortedKeys` or `organizeImports` | `assist/<group>/<action>` |
|
|
104
|
-
| `assist.actions` with `recommended: false` or `preset: "none"` | `assist/recommended` |
|
|
105
|
-
| `assist.enabled: false` | `assist` |
|
|
106
|
-
| `formatter.enabled: false` | `format` |
|
|
107
|
-
| `enabled: false` in a language's `linter`, `assist` or `formatter`, such as `css.linter` | `<language>/lint`, `<language>/assist`, `<language>/format` |
|
|
108
|
-
| An `includes` list in `files`, `linter`, `assist` or `formatter` that excludes a pattern with `!` or lacks `**` | `files/includes`, `lint/includes`, `assist/includes`, `format/includes` |
|
|
109
|
-
|
|
110
|
-
Biome runs assist actions, key sorting among them, as part of `biome check` and reports what they would change, so turning one off is an override. Formatter options such as `indentStyle` or `lineWidth` choose a style, not an exception, and are not overrides. Settings that raise a rule or enable it are not overrides either. Rule options are not read, so an option that loosens a rule, such as a higher complexity limit, is left to review.
|
|
111
|
-
|
|
112
|
-
A manual setting without a legacy declaration is reported at its line in the Biome config. The compatibility reader matches the old `{ rule, includes, reason }` declarations to settings with the same rule and exact scope, in any order, and reports stale or repeated declarations. Run `quality exceptions migrate` to replace both with the single config entry. A generated override never makes a checked-in manual override declared.
|
|
113
|
-
|
|
114
|
-
#### Shared presets
|
|
115
|
-
|
|
116
|
-
A config `extends` its entries the way Biome does:
|
|
117
|
-
|
|
118
|
-
- 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.
|
|
119
|
-
- 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.
|
|
120
|
-
- 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.
|
|
121
|
-
- 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.
|
|
122
|
-
- 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.
|
|
123
|
-
|
|
124
|
-
A package that ships a preset declares the weakenings the preset makes in a `declarations.json` beside the preset file, as an array of `{ rule, includes, reason }` entries. Its `includes` are written relative to the repository that extends the preset. Those weakenings count as declared, so the repository adds nothing for them:
|
|
125
|
-
|
|
126
|
-
```json
|
|
127
|
-
[
|
|
128
|
-
{
|
|
129
|
-
"rule": "lint/style/noDefaultExport",
|
|
130
|
-
"includes": ["**/*.config.ts"],
|
|
131
|
-
"reason": "Tools load their config files through the default export."
|
|
132
|
-
}
|
|
133
|
-
]
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
A weakening the preset makes without such a declaration is reported at the line of the repository's `extends` entry, and the repository must repair the preset or replace that setting with a config exception. 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.
|
|
137
|
-
|
|
138
|
-
#### Recommended Biome rules
|
|
139
|
-
|
|
140
|
-
`suppressions/biome-recommended` holds the [Biome preset](./tooling.md#biome-preset) to every rule Biome recommends, so a Biome upgrade that adds a recommended rule, or recommends one the preset leaves below `error`, fails the gate until the preset takes a position on it. It asks the installed Biome, the one `quality` runs, which rules its `recommended` preset enables, through `biome rage --linter`, so the list is never written down. When the root Biome config extends `@shivaedev/quality/biome`, it reads that preset and its `declarations.json`, resolved from the repository root as under [Shared presets](#shared-presets); a repository that does not extend the preset is left to the `biome` rule, which asks for it. A recommended rule is in order when the preset's `linter.rules` sets it to `error`, directly or through its group's severity, or when a declaration names it, whatever the declaration's `includes`. Every other recommended rule is reported at the preset, one finding per rule: set it to `error` and repair the code it reports.
|
|
141
|
-
|
|
142
|
-
### Imports
|
|
143
|
-
|
|
144
|
-
Six rules check the imports of the TypeScript and JavaScript modules among the sources. `imports/cycles`, `imports/resolvable`, `imports/manifest`, `imports/no-test-code` and `imports/fences` with declared fences build a graph resolved by the TypeScript compiler. `imports/aliased` checks relative path syntax.
|
|
145
|
-
|
|
146
|
-
| Rule | Reports | Options |
|
|
147
|
-
| --- | --- | --- |
|
|
148
|
-
| `imports/cycles` | Modules that import each other at run time | none |
|
|
149
|
-
| `imports/resolvable` | An import that resolves to nothing | `generated` |
|
|
150
|
-
| `imports/manifest` | A workspace import missing from the importing package’s shipped dependencies, or present only in `devDependencies` in shipped code | none |
|
|
151
|
-
| `imports/fences` | An import that crosses a fence the config declares | `fences` |
|
|
152
|
-
| `imports/aliased` | A relative import that leaves its own folder | none |
|
|
153
|
-
| `imports/no-test-code` | A production module importing a test file or anything under `test-support/` | none |
|
|
154
|
-
|
|
155
|
-
Each graph edge 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 literal specifiers in 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. Computed specifiers are outside this check.
|
|
156
|
-
|
|
157
|
-
- 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.
|
|
158
|
-
- 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.
|
|
159
|
-
- 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.
|
|
160
|
-
- A relative import or a `#` package import of a missing file resolves only inside a folder that `generated` names, such as a client a generator writes before the tests run. A `#` import lands where its `imports` entry in `package.json` points once the file exists. 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.
|
|
161
|
-
- 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`.
|
|
162
|
-
|
|
163
|
-
```ts
|
|
164
|
-
import { defineConfig } from "@shivaedev/quality/config.ts";
|
|
165
|
-
|
|
166
|
-
export default defineConfig({
|
|
167
|
-
rules: { "imports/resolvable": { options: { generated: ["packages/db/src/test-support/generated"] } } },
|
|
168
|
-
});
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
`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 exceptions. Each finding of `imports/resolvable` has the import as its subject.
|
|
172
|
-
|
|
173
|
-
When the sources hold no module at all, the graph-building rules stop the run instead of passing on an empty graph. Point `sources` at the code; do not disable the checks to conceal an empty inventory. `imports/aliased` does not require a graph, and `imports/fences` does nothing when no fences are declared.
|
|
174
|
-
|
|
175
|
-
#### Runtime folders
|
|
176
|
-
|
|
177
|
-
`imports/runtime` adopts a package automatically when it has `src/client/` or `src/server/`, including folders outside `sources`. In an adopted package, code in `src/client/` is browser code, code in `src/server/` is server code, and code anywhere else is shared except package-root tooling. A package with a nonempty `bin` entry and no runtime folders is server code throughout; runtime folders take precedence over `bin`. A Node library without a CLI uses `src/server/`. `engines.node` alone does not identify Node-only code. Other packages retain their existing source policies. Existing explicit fences keep running in either case.
|
|
178
|
-
|
|
179
|
-
Package-root tooling is server code in every package, including the workspace root: top-level `script/`, `scripts/` and `tooling/` folders and top-level JavaScript or TypeScript config files. A tooling folder or config file nested under `src/` follows the source runtime policy.
|
|
180
|
-
|
|
181
|
-
Client code may not import server code or Node built-ins. Shared code may import neither client nor server code and may neither import Node built-ins nor use browser globals. Client and server code may import shared code. The rule uses the resolved import graph, including type imports, re-exports, dynamic imports and literal `require()` calls; missing file targets still carry their runtime folder. Test files and `test-support/` are outside the shipped runtime policy.
|
|
182
|
-
|
|
183
|
-
Browser-global checks report direct, shorthand and literal `globalThis` property uses, with lexical bindings respected. Property names, comments, strings and type annotations do not count as global value uses. Browser-specific globals come from TypeScript's DOM declarations, excluding the standard web APIs that Node also supplies. Worker browser APIs such as `indexedDB` and `caches` remain browser globals. Portable web APIs such as `URL`, `fetch` and `AbortController` remain available to shared code. This is a static source check, not proof of a deployment's runtime behavior. Each finding has the imported specifier or browser global as its subject. Move runtime-dependent code to its runtime folder or pass a value into shared code.
|
|
184
|
-
|
|
185
|
-
#### Manifest dependencies
|
|
186
|
-
|
|
187
|
-
`imports/manifest` discovers named workspace members from `pnpm-workspace.yaml` or the root manifest's `workspaces`, with no package scope convention or fence configuration. It discovers manifests across the repository, independently of `sources`, skipping files ignored by git and `node_modules`. Selecting only a member's `src` folder still checks its imports of members outside that selection against its own manifest. Each import of another member must appear in the importing member's `dependencies`, `peerDependencies` or `optionalDependencies`. The workspace root uses its own manifest for code outside a member. Self-imports and packages outside the workspace do not need a workspace declaration under this rule.
|
|
188
|
-
|
|
189
|
-
Tests and files under `test-support/` may also use `devDependencies`. Tooling means files under a `script/`, `scripts/` or `tooling/` folder directly below the importing package's root, or a config file directly at that root, named `config.ts`, `vitest.config.ts` or another `*.config.*` with a JavaScript or TypeScript extension. A folder or config file nested under `src/` still counts as shipped code. Source modules explicitly blocked by a null package export may use development dependencies when no runtime module reaches them in the scanned import graph. A more specific public export, an export under another name or an executable entry point keeps its imports in the runtime boundary. The rule follows imports, re-exports and type references through blocked helpers; a blocked public subpath alone cannot hide a runtime dependency.
|
|
190
|
-
|
|
191
|
-
The rule checks type imports as well as value imports. Resolved aliases and relative paths use the target's workspace owner; unresolved bare package names and subpaths still require a declaration. A nested manifest that is not a workspace member does not replace its owner's dependency policy. Malformed dependency objects stop the run. Add the dependency to the correct manifest field or remove the import; the rule does not edit manifests.
|
|
192
|
-
|
|
193
|
-
#### Aliases
|
|
194
|
-
|
|
195
|
-
A relative import names only a file in its own folder, such as `./format.ts`: the folder is one module. Every other import goes through an alias, so moving a file never rewrites a chain of `../`:
|
|
196
|
-
|
|
197
|
-
- the `imports` of the importer's nearest `package.json`, such as `"#lib/*": "./src/lib/*"`, or `"#shared/*": "@demo/shared/*"`, which names another workspace package;
|
|
198
|
-
- the name of another workspace package, as far as its `exports` reach.
|
|
199
|
-
|
|
200
|
-
An import that already goes through a tsconfig `paths` alias is not relative, so the rule leaves it alone.
|
|
201
|
-
|
|
202
|
-
`imports/aliased` reports a relative import that goes up (`../format.ts`) or down into a subfolder (`./parts/deeper.ts`, or `./widgets` for `widgets/index.ts`). It reads literal specifiers in static imports, `export ... from`, side-effect imports, `import()`, `require()`, `import()` types, JSDoc `@import` tags and the paths of `vi.mock`, `vi.doMock`, `vi.unmock`, `vi.doUnmock`, `vi.importActual` and `vi.importMock`, so a mock keeps pointing at the module it replaces. An escaped path, such as `"\x2e\x2e/format.ts"`, counts as the path it spells. Each finding has the import as its subject. Its message names the fix for where the import lands:
|
|
203
|
-
|
|
204
|
-
- inside its own package: import through a `#` alias from the `imports` of the package's `package.json`, and declare one if none fits;
|
|
205
|
-
- inside another workspace package: import it by that package's name, through a path its `exports` lists;
|
|
206
|
-
- inside an installed package under `node_modules`: import that package by its name.
|
|
207
|
-
|
|
208
|
-
The rule only reports; nothing rewrites the import. Whoever writes the import picks the alias, because only they know which conditions their runtimes, bundler and tests use.
|
|
209
|
-
|
|
210
|
-
#### Fences
|
|
211
|
-
|
|
212
|
-
A fence is one prohibition. A package declares stronger promises than its manifest in `quality.fences.config.ts` beside `package.json`. Quality discovers this file at the root and in workspace members without a root-config list, independently of the source inventory:
|
|
213
|
-
|
|
214
|
-
```ts
|
|
215
|
-
import { external, folders, packages } from "@shivaedev/quality/imports/fences/selectors.ts";
|
|
216
|
-
import { fence } from "@shivaedev/quality/imports/fences/dsl.ts";
|
|
217
|
-
|
|
218
|
-
const fences = [
|
|
219
|
-
fence("ui-never-imports-server")
|
|
220
|
-
.because("The UI ships to browsers.")
|
|
221
|
-
.from(folders("packages/ui/src"))
|
|
222
|
-
.mayNotImport(packages("server"))
|
|
223
|
-
.demonstratedBy({
|
|
224
|
-
illegal: ["packages/ui/src/app.ts", "packages/server/src/db.ts"],
|
|
225
|
-
legal: ["packages/ui/src/app.ts", external("effect")],
|
|
226
|
-
}),
|
|
227
|
-
];
|
|
228
|
-
|
|
229
|
-
export default fences;
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
A fence has a name, a reason, the modules it holds (`from`) and one prohibition:
|
|
233
|
-
|
|
234
|
-
- `mayNotImport(target)`: no module it holds imports the target.
|
|
235
|
-
- `mayNotReach(target)`: nothing a module it holds imports, directly or through other modules of the repository, is the target. The finding names the path.
|
|
236
|
-
- `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.
|
|
237
|
-
|
|
238
|
-
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(...)`.
|
|
239
|
-
|
|
240
|
-
The DSL does not compile without `demonstratedBy`, and the rule checks the examples against the combined discovered and config-supplied 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.
|
|
241
|
-
|
|
242
|
-
Fences guard shipped code, and [test code](./naming.md#test-code) ships nowhere, so a fence never holds it: a test file or a file under `test-support/` may import across every fence. There is no option to check them.
|
|
243
|
-
|
|
244
|
-
A finding has its fence's name as its subject, so a config exception with that subject excuses one file from one fence.
|
|
245
|
-
|
|
246
|
-
`imports/no-test-code` checks every file edge from a production module to a test file or a file under `test-support/`, including type imports, re-exports and test assets. It rejects missing relative paths under `test-support/` too. Test importers may still import production code and other test code. Move shared code into a production module and import it from both production and tests. The rule takes no exceptions.
|
|
247
|
-
|
|
248
|
-
### Manifests
|
|
249
|
-
|
|
250
|
-
`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 untracked 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 exceptions.
|
|
251
|
-
|
|
252
|
-
## Local-rule API
|
|
253
|
-
|
|
254
|
-
The API allows a local rule to receive checked source text and return findings. This is an extension reference, not a step every repository must complete; reusable quality policy belongs in the shared package. This example is the same text-scanning rule exercised by the package's CLI tests:
|
|
255
|
-
|
|
256
|
-
```ts
|
|
257
|
-
import { defineRule } from "@shivaedev/quality/rule.ts";
|
|
258
|
-
|
|
259
|
-
export const noLog = defineRule({
|
|
260
|
-
check: ({ sources }) =>
|
|
261
|
-
sources
|
|
262
|
-
.filter((file) => file.text.includes("console.log"))
|
|
263
|
-
.map((file) => ({ file: file.path, message: "logs to the console." })),
|
|
264
|
-
description: "Log through the logger.",
|
|
265
|
-
id: "local/no-console-log",
|
|
266
|
-
});
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
It reports a file whose text contains `console.log`; it does not parse calls or distinguish a string from code. Use a syntax-aware check when the policy needs that distinction. Register it in the config:
|
|
270
|
-
|
|
271
|
-
```ts
|
|
272
|
-
import { defineConfig } from "@shivaedev/quality/config.ts";
|
|
273
|
-
import { noLog } from "./quality/noLog.ts";
|
|
274
|
-
|
|
275
|
-
export default defineConfig({ local: [noLog], sources: ["src"] });
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
`check` may return findings directly or asynchronously. For options, provide a [Standard Schema](https://standardschema.dev) validator; the check receives the decoded result. An absent options object is validated as `{}`, so defaults belong in the schema:
|
|
279
|
-
|
|
280
|
-
```ts
|
|
281
|
-
import { Effect, Schema } from "effect";
|
|
282
|
-
import { defineRule } from "@shivaedev/quality/rule.ts";
|
|
283
|
-
|
|
284
|
-
const Limit = Schema.Struct({
|
|
285
|
-
max: Schema.Int.pipe(Schema.withDecodingDefaultKey(Effect.succeed(2))),
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
export const limited = defineRule({
|
|
289
|
-
check: async ({ files, options }) =>
|
|
290
|
-
files.length > options.max ? [{ file: ".", message: `${files.length} files exceed ${options.max}.` }] : [],
|
|
291
|
-
description: "Keep the repository small.",
|
|
292
|
-
id: "local/max-files",
|
|
293
|
-
options: Schema.toStandardSchemaV1(Limit, { parseOptions: { onExcessProperty: "error" } }),
|
|
294
|
-
});
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
This is the option-bearing rule the package tests. It defaults to two files and validates a configured `max` before the asynchronous check runs. A rule without an options schema refuses options. Set `registrable: false` when a rule must never be excused by a config exception.
|
package/docs/tooling.md
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
# Shared tooling
|
|
2
|
-
|
|
3
|
-
Quality's shared policy includes Biome and TypeScript configuration and Vitest project helpers alongside the gate. These connect the native tools to one opinionated set of conventions; consumers do not need to choose a rule set for each tool. Use the [README](../README.md) to configure the gate; use this page to wire the tools that check, format, compile and run the repository.
|
|
4
|
-
|
|
5
|
-
## Biome preset
|
|
6
|
-
|
|
7
|
-
`@shivaedev/quality/biome` supplies the shared Biome configuration. Quality includes and runs Biome; the root `biome.json` extends the shared configuration:
|
|
8
|
-
|
|
9
|
-
```json
|
|
10
|
-
{
|
|
11
|
-
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
|
|
12
|
-
"extends": ["@shivaedev/quality/biome"]
|
|
13
|
-
}
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
The preset sets:
|
|
17
|
-
|
|
18
|
-
- **Formatting:** tabs, a line width of 150, double quotes, semicolons, trailing commas and operators at the start of a wrapped line. An object key keeps its quotes, because a quoted key marks a [foreign name](./naming.md#foreign-names).
|
|
19
|
-
- **Lint:** recommended rules are set to `error` or explained in the preset's declarations. Stricter settings include `noUnsafeTypeAssertion`, `useBlockStatements`, `useNumericSeparators`, `useUnicodeRegex`, `useLiteralKeys`, `noFloatingPromises`, `useExhaustiveDependencies`, `useConsistentTestIt`, `noNonScalableViewport`, function declaration style, interface style, a cognitive complexity limit of 15, `noNestedTernary` and the [naming rules](./naming.md).
|
|
20
|
-
- **Imports:** organized in five groups: Node and Bun builtins, packages, `@shivaedev/*` packages, aliases and relative paths. Biome counts as an alias a specifier that starts with `#`, `@/`, `~`, `$` or `%`; a tsconfig path such as `@app/*` sorts with the packages, and Biome's `noUndeclaredDependencies` takes it for one, so name aliases in a form Biome recognizes.
|
|
21
|
-
- **Assist:** organized imports and checks for sorted keys, attributes, enum members, interface members and properties. The key-sorting check applies to JSON and object literals, but `quality fix` skips its fix. `package.json` is left out of that check because `manifests/sorted` supplies its key order.
|
|
22
|
-
- **Plugins:** GritQL rules that ban ambient time, randomness, `console` and `process.env` for Effect's services, and the naming plugins under [Naming](./naming.md). They load from `./node_modules/@shivaedev/quality/biome/plugins`, so the package must be installed at the repository root.
|
|
23
|
-
- Files ignored by git are skipped.
|
|
24
|
-
|
|
25
|
-
The preset turns off `noUnusedVariables` and `noUnusedFunctionParameters`, because the tsconfig presets report them through TypeScript. It allows default exports in `*.config.*` files, which tools load through the default export, and turns off `useComponentExportOnlyModules` in `*.test.*` and `*.spec.*` files, where tests define their harness components.
|
|
26
|
-
|
|
27
|
-
The preset reports `useSortedClasses` at `error` with its fix enabled for class attributes and calls to `cn`, `clsx` and `cva`. Tailwind output does not depend on the order of classes in an attribute, so sorting is safe.
|
|
28
|
-
|
|
29
|
-
The preset sets `noDelete` to `off` because its fix changed behavior or did not finish on real code. `noUndeclaredClasses` is off because its class resolution does not cover aliases and Tailwind utilities; `noInlineStyles` is off because computed values need inline styles. Quality owns these choices and ships their reasons in `declarations.json`. They do not require consumer declarations. The config exception API is described under [declared Biome overrides](./rules.md#declared-biome-overrides); use the shared defaults for adoption.
|
|
30
|
-
|
|
31
|
-
The preset keeps the following rules at `error` with their fixes off so the author decides the repair: `noAccessKey`, `noAriaHiddenOnFocusable`, `noAutofocus`, `noInteractiveElementToNoninteractiveRole`, `noNoninteractiveElementToInteractiveRole`, `noNoninteractiveTabindex`, `noRedundantRoles`, `useValidAriaProps`, `useValidAriaRole`, `noImportantStyles`, `noConstAssign`, `noProcessGlobal`, `useJsonImportAttributes`, `noUnusedPrivateClassMembers`, `useExhaustiveDependencies`, `noFloatingPromises`, `noMisusedPromises`, `useExhaustiveSwitchCases`, `noEqualsToNull`, `useConsistentArrayType`, `useConsistentCurlyBraces`, `useConsistentTestIt`, `useRegexpTest`, `useUnicodeRegex`, `noNonNullAssertion`, `useAtIndex`, `noParametersOnlyUsedInRecursion`, `noSkippedTests` and `useNamingConvention`. The fix command also skips `noDuplicateObjectKeys` and `useSortedKeys`. Import `process` from `node:process` in Node code or use the application's Effect service. Write JSON imports with `with { type: "json" }` for Node 24; the fixer leaves both choices to the author.
|
|
32
|
-
|
|
33
|
-
- `useConsistentArrayType`: its fix rewrites locally defined `Array<T>` and `ReadonlyArray<T>` types as built-in arrays; check the type binding before writing shorthand.
|
|
34
|
-
- `useConsistentCurlyBraces`: its fix can replace escaped newlines in JSX strings with literal backslash text; preserve rendered text when removing braces.
|
|
35
|
-
- `noMisusedPromises`: awaiting a promise changes timing, conditional results and rejection handling; make the callback contract accept its promise when the caller awaits it, or handle completion and rejection explicitly.
|
|
36
|
-
- `useExhaustiveSwitchCases`: adding cases that fall through to default conflicts with `noUselessSwitchCase`; write every case with its intended result, preserving a default fallback where needed.
|
|
37
|
-
- `noEqualsToNull`: replacing loose null equality with strict equality loses the `undefined` case; write explicit checks for both `null` and `undefined`.
|
|
38
|
-
|
|
39
|
-
`noSkippedTests` has no automatic fix because enabling a skipped test can change which setup and bodies run; the author must repair the test or remove a claim it cannot prove.
|
|
40
|
-
|
|
41
|
-
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. `quality baseline write --rule biome` takes in every Biome category at once. A finding below `error` is not reported; a consumer exception for an individual rule therefore lives in the config exceptions list with explicit files and a reason. Quality applies its Biome settings during lint and fix. Whole-check, group and blanket-scope declarations are rejected; see [configuration limits](../README.md#configuration-limits). The rule also asks for a root `biome.json` or `biome.jsonc` that extends the preset, and it refuses legacy registry exceptions. A Biome config that Biome cannot load stops the run with Biome's message.
|
|
42
|
-
|
|
43
|
-
`quality fix` sorts the discovered manifests, then runs `biome check --write --unsafe`, with the two skipped fixes above. It then runs Biome's formatter, because a lint fix can leave code unformatted. One fix can make room for another, so it repeats both passes until a round rewrites nothing, at most five rounds. When files still change in the fifth round, it names the changed text files it can discover and exits 2; when those files are outside its inventory, the error gives a verbose Biome command to name them. An unsafe fix can change behavior, such as `==` becoming `===`, so review what it changed.
|
|
44
|
-
|
|
45
|
-
## Vitest projects
|
|
46
|
-
|
|
47
|
-
`@shivaedev/quality/testProjects.ts` 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. A package's `vitest.config.ts` reads:
|
|
48
|
-
|
|
49
|
-
```ts
|
|
50
|
-
import { testProjects } from "@shivaedev/quality/testProjects.ts";
|
|
51
|
-
import { defineConfig } from "vitest/config";
|
|
52
|
-
|
|
53
|
-
export default defineConfig({ test: testProjects() });
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
| Project | Files | Environment | Runs |
|
|
57
|
-
| --- | --- | --- | --- |
|
|
58
|
-
| `unit` | `*.test.ts`, `*.spec.ts` and the other test and spec files | Node | By default |
|
|
59
|
-
| `dom` | `*.dom.test.tsx`, `*.dom.spec.tsx` and the other `.dom` files | happy-dom | By default |
|
|
60
|
-
| `slow` | `*.slow.test.ts`, `*.slow.spec.ts` and the other `.slow` files | Node | Only with `vitest run --project slow` |
|
|
61
|
-
|
|
62
|
-
Type tests (`*.typecheck.test.ts`, `*.typecheck.spec.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.
|
|
63
|
-
|
|
64
|
-
`testProjects()` takes no options. Every conventionally named suite runs in its project regardless of its folder; exclusion options are rejected.
|
|
65
|
-
|
|
66
|
-
## tsconfig presets
|
|
67
|
-
|
|
68
|
-
Two presets supply a shared strict TypeScript setup for type-checking and package builds.
|
|
69
|
-
|
|
70
|
-
| Preset | Use |
|
|
71
|
-
| --- | --- |
|
|
72
|
-
| `@shivaedev/quality/tsconfig/base.json` | Type-checking with `noEmit`: applications, tests and scripts. |
|
|
73
|
-
| `@shivaedev/quality/tsconfig/package.json` | Building a package: the base plus `declaration`, `declarationMap`, `sourceMap` and `rewriteRelativeImportExtensions`, so relative `.ts` imports are emitted as `.js`. |
|
|
74
|
-
|
|
75
|
-
```json
|
|
76
|
-
{
|
|
77
|
-
"extends": "@shivaedev/quality/tsconfig/base.json",
|
|
78
|
-
"compilerOptions": {
|
|
79
|
-
"lib": ["ESNext", "DOM"],
|
|
80
|
-
"types": ["node"]
|
|
81
|
-
},
|
|
82
|
-
"include": ["src"]
|
|
83
|
-
}
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
The base sets:
|
|
87
|
-
|
|
88
|
-
- **Language:** `target` and `lib` are `ESNext`.
|
|
89
|
-
- **Modules:** `module` ESNext, `moduleResolution` bundler, `moduleDetection` force, `verbatimModuleSyntax`, `isolatedModules`, `allowImportingTsExtensions`, `resolveJsonModule`, `noUncheckedSideEffectImports`, `forceConsistentCasingInFileNames` and `libReplacement: false`.
|
|
90
|
-
- **JavaScript:** `allowJs` and `checkJs`, so JavaScript files are type-checked with the TypeScript ones.
|
|
91
|
-
- **Erasable syntax only:** `erasableSyntaxOnly` rejects syntax that type stripping cannot erase, such as parameter properties.
|
|
92
|
-
- **Checks:** `strict`, with `noImplicitAny`, `strictBuiltinIteratorReturn` and `useUnknownInCatchVariables` stated explicitly, `noUncheckedIndexedAccess`, `exactOptionalPropertyTypes`, `noImplicitOverride`, `noImplicitReturns`, `noFallthroughCasesInSwitch`, `noUnusedLocals`, `noUnusedParameters`, `allowUnreachableCode: false` and `allowUnusedLabels: false`.
|
|
93
|
-
- **Compiler settings:** `stableTypeOrdering` and `skipLibCheck`.
|
|
94
|
-
|
|
95
|
-
`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.
|
|
96
|
-
|
|
97
|
-
A package that type-checks its tests with one config and builds `src` with another extends both, the package preset last:
|
|
98
|
-
|
|
99
|
-
```json
|
|
100
|
-
{
|
|
101
|
-
"extends": ["./tsconfig.json", "@shivaedev/quality/tsconfig/package.json"],
|
|
102
|
-
"compilerOptions": { "outDir": "dist", "rootDir": "src" },
|
|
103
|
-
"include": ["src"]
|
|
104
|
-
}
|
|
105
|
-
```
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|