kvalita 1.23.0 → 1.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,7 +30,25 @@ Create a `biome.json` file in your project root:
30
30
  }
31
31
  ```
32
32
 
33
- The config runs two GritQL plugins on Bun tests. One flags `await` before `expect(...).rejects`, since Bun tracks that assertion on its own. The other flags a callback written inline in `expect(() => fn()).toThrow()`, which reads better as `const throwing = () => fn()`. The plugins load from `node_modules/kvalita`, so the config expects kvalita installed at the project root.
33
+ The config also runs GritQL plugins. Most of them target Bun tests:
34
+
35
+ - `await` before `expect(...).rejects`, since Bun tracks that assertion on its own.
36
+ - A callback written inline in `expect(() => fn()).toThrow()`, which reads better as `const throwing = () => fn()`.
37
+ - `const expected = value`, where asserting against `value` says the input comes back unchanged.
38
+ - `const expected = true` or `false`, where the boolean reads better inline in `toBe()`.
39
+ - `toBe(undefined)` and `toEqual(undefined)`, where `toBeUndefined()` says it directly.
40
+ - A test title that doesn't start with "should".
41
+ - A table written inline in `it.each(...)`, which reads better as a typed const above the call.
42
+
43
+ The rest apply to all code:
44
+
45
+ - `as unknown as`, which skips the type check entirely.
46
+ - An object literal cast with `as Type`, where an annotation checks every field. `as const` is fine.
47
+ - `.catch()` chained on a promise, where try/catch around an `await` is the house style. A fire-and-forget call behind `void` keeps its `.catch()`.
48
+ - Two or more `===` comparisons against literals in one condition, which read better as a named array and `.includes()`.
49
+ - A regex constant whose name doesn't end with `Regex`.
50
+
51
+ The plugins load from `node_modules/kvalita`, so the config expects kvalita installed at the project root.
34
52
 
35
53
  ### [Commitlint](https://github.com/conventional-changelog/commitlint) Configuration
36
54
 
@@ -113,7 +131,34 @@ jobs:
113
131
  secrets: inherit
114
132
  ```
115
133
 
116
- `shared-release` takes `branch`, `build`, `config` and `bun-version`; `shared-test` takes `bun-version`. Every one has a default, so a repo on the common setup passes nothing.
134
+ `shared-release` takes `branch`, `build`, `config` and `bun-version`; `shared-test` takes `bun-version` and `ref`. Every one has a default, so a repo on the common setup passes nothing. Start every release from `main` and pick the branch with `branch`: `shared-release` checks it out and tells semantic-release to release it. Pass the same branch as `ref` when a release job waits on `shared-test`, or the tests check `main` instead of the branch being released.
135
+
136
+ `shared-package` checks the package the way a consumer gets it. It builds the package, installs it into an empty project with `npm install --install-links`, so only the published files land there, and loads every entry point in `exports`: through `import`, and through `require()` where a `require` condition exists. It does that on every even Node.js major from the floor in `engines.node`, or 18 when it isn't set, up to the latest release. On the latest one, it also turns off syntax detection and `require()` of ESM files, which older versions don't have. Optional peers get installed too, since an entry point built on one needs it.
137
+
138
+ A second job checks the same entry points the way TypeScript and bundler users reach them. It type-checks them from an ESM and a CommonJS project under each module resolution (`node10`, `node16`, `nodenext` and `bundler`) with `strict` and `skipLibCheck: false`, and fails only on errors in the package's own types. It also bundles them with Vite as a server build. With `browser: true`, it bundles them for the browser as well. Every setup only imports what the package claims: CommonJS files only use entry points with a `require` condition, and `node10` only checks the root entry, when the package has a top-level `types`.
139
+
140
+ It takes `ref`, `bun-version` and `browser`:
141
+
142
+ ```yaml
143
+ # .github/workflows/package.yml
144
+ name: Package
145
+ on:
146
+ push:
147
+ branches: [main, alpha, beta, rc]
148
+ pull_request:
149
+ workflow_call:
150
+ inputs:
151
+ ref:
152
+ type: string
153
+ default: ''
154
+ jobs:
155
+ package:
156
+ uses: macieklamberski/kvalita/.github/workflows/shared-package.yml@main
157
+ with:
158
+ ref: ${{ inputs.ref }}
159
+ ```
160
+
161
+ The `workflow_call` trigger lets the release workflow run it as a gate with `ref: ${{ inputs.branch }}`, next to `shared-test`.
117
162
 
118
163
  ### Ignore File Templates
119
164
 
@@ -0,0 +1,5 @@
1
+ language js
2
+
3
+ `$value as unknown as $type` where {
4
+ register_diagnostic(span=$value, message="Drop the `as unknown` step: try a single cast, a runtime check or a property of the base type first.")
5
+ }
@@ -0,0 +1,6 @@
1
+ language js
2
+
3
+ `const expected = $alias` where {
4
+ $alias <: `value`,
5
+ register_diagnostic(span=$alias, message="Assert against `value` itself: `const expected = value` names the same value twice.")
6
+ }
@@ -0,0 +1,9 @@
1
+ language js
2
+
3
+ `const expected = $boolean` where {
4
+ $boolean <: or {
5
+ `true`,
6
+ `false`
7
+ },
8
+ register_diagnostic(span=$boolean, message="Inline the boolean in the matcher: `expect(...).toBe(true)`.")
9
+ }
@@ -0,0 +1,11 @@
1
+ language js
2
+
3
+ `$test.each($rows)` where {
4
+ $test <: or {
5
+ `it`,
6
+ `test`,
7
+ `describe`
8
+ },
9
+ $rows <: array(),
10
+ register_diagnostic(span=$rows, message="Move the table into a typed const above the call.")
11
+ }
@@ -0,0 +1,13 @@
1
+ language js
2
+
3
+ `$left === $first || $left === $second` where {
4
+ $first <: or {
5
+ string(),
6
+ number()
7
+ },
8
+ $second <: or {
9
+ string(),
10
+ number()
11
+ },
12
+ register_diagnostic(span=$left, message="Put the literals in a named array and check membership with `.includes()`.")
13
+ }
@@ -0,0 +1,7 @@
1
+ language js
2
+
3
+ `$object as $type` where {
4
+ $object <: object(),
5
+ not $type <: r"^const$",
6
+ register_diagnostic(span=$object, message="Annotate instead of casting: `const value: Type = { ... }` checks every field.")
7
+ }
@@ -0,0 +1,6 @@
1
+ language js
2
+
3
+ `$promise.catch($handler)` where {
4
+ not $promise <: within `void $_` ,
5
+ register_diagnostic(span=$handler, message="Handle the rejection in try/catch around an await. A fire-and-forget call keeps `.catch()` behind `void`.")
6
+ }
@@ -3,7 +3,17 @@
3
3
  "root": false,
4
4
  "plugins": [
5
5
  "./node_modules/kvalita/configs/biome/no-await-expect-rejects.grit",
6
- "./node_modules/kvalita/configs/biome/use-throwing-const.grit"
6
+ "./node_modules/kvalita/configs/biome/no-double-cast.grit",
7
+ "./node_modules/kvalita/configs/biome/no-expected-alias.grit",
8
+ "./node_modules/kvalita/configs/biome/no-expected-boolean.grit",
9
+ "./node_modules/kvalita/configs/biome/no-inline-each-table.grit",
10
+ "./node_modules/kvalita/configs/biome/no-literal-equality-chain.grit",
11
+ "./node_modules/kvalita/configs/biome/no-object-cast.grit",
12
+ "./node_modules/kvalita/configs/biome/no-promise-catch.grit",
13
+ "./node_modules/kvalita/configs/biome/use-throwing-const.grit",
14
+ "./node_modules/kvalita/configs/biome/use-regex-suffix.grit",
15
+ "./node_modules/kvalita/configs/biome/use-should-title.grit",
16
+ "./node_modules/kvalita/configs/biome/use-to-be-undefined.grit"
7
17
  ],
8
18
  "vcs": {
9
19
  "enabled": true,
@@ -49,7 +59,9 @@
49
59
  "noMultiAssign": "error",
50
60
  "noMultilineString": "error",
51
61
  "noNestedTernary": "error",
62
+ "noNonNullAssertion": "error",
52
63
  "noParameterAssign": "error",
64
+ "noProcessEnv": "error",
53
65
  "noSubstr": "error",
54
66
  "noUnusedTemplateLiteral": "error",
55
67
  "noUselessElse": "error",
@@ -82,6 +94,7 @@
82
94
  "useShorthandAssign": "error",
83
95
  "useSingleVarDeclarator": "error",
84
96
  "useSpreadOverApply": "error",
97
+ "useTemplate": "error",
85
98
  "useThrowNewError": "error",
86
99
  "useThrowOnlyError": "error",
87
100
  "useTrimStartEnd": "error"
@@ -105,10 +118,13 @@
105
118
  "suspicious": {
106
119
  "noDuplicatedSpreadProps": "error",
107
120
  "noEmptySource": "error",
121
+ "noFocusedTests": "error",
108
122
  "noForIn": "error",
123
+ "noMisplacedAssertion": "error",
109
124
  "noParametersOnlyUsedInRecursion": "error",
110
125
  "noProto": "error",
111
126
  "noReturnAssign": "error",
127
+ "noSkippedTests": "error",
112
128
  "noVar": "error",
113
129
  "useArraySortCompare": "error",
114
130
  "useAwait": "error",
@@ -121,6 +137,21 @@
121
137
  },
122
138
  "security": {
123
139
  "noScriptUrl": "error"
140
+ },
141
+ "nursery": {
142
+ "noConditionalExpect": "error",
143
+ "noFloatingPromises": "error",
144
+ "noIdenticalTestTitle": "error",
145
+ "useConsistentFunctionStyle": {
146
+ "level": "error",
147
+ "options": {
148
+ "style": "expression"
149
+ }
150
+ },
151
+ "useConsistentTestIt": "error",
152
+ "useNullishCoalescing": "error",
153
+ "useTestHooksOnTop": "error",
154
+ "useValidTestTitle": "error"
124
155
  }
125
156
  }
126
157
  }
@@ -0,0 +1,11 @@
1
+ language js
2
+
3
+ `const $name = $value` where {
4
+ $value <: or {
5
+ r"^/.+/[dgimsuvy]*$",
6
+ `new RegExp($pattern)`,
7
+ `new RegExp($pattern, $flags)`
8
+ },
9
+ not $name <: r".*Regex$",
10
+ register_diagnostic(span=$name, message="End a regex constant's name with `Regex`.")
11
+ }
@@ -0,0 +1,17 @@
1
+ language js
2
+
3
+ `$test($title, $callback)` where {
4
+ $test <: or {
5
+ `it`,
6
+ `test`,
7
+ `it.todo`,
8
+ `it.skip`,
9
+ `it.only`,
10
+ `test.todo`,
11
+ `test.skip`,
12
+ `test.only`
13
+ },
14
+ $title <: r"^['\"`].*",
15
+ not $title <: r"^['\"`]should .*",
16
+ register_diagnostic(span=$title, message="Start the test title with \"should\".")
17
+ }
@@ -0,0 +1,10 @@
1
+ language js
2
+
3
+ `expect($value).$matcher(undefined)` where {
4
+ $matcher <: or {
5
+ `toBe`,
6
+ `toEqual`,
7
+ `toStrictEqual`
8
+ },
9
+ register_diagnostic(span=$matcher, message="Assert a missing value with `toBeUndefined()`.")
10
+ }
package/package.json CHANGED
@@ -42,5 +42,5 @@
42
42
  "semantic-release": "^25.0.9",
43
43
  "typescript": "^7.0.2"
44
44
  },
45
- "version": "1.23.0"
45
+ "version": "1.24.0"
46
46
  }