@felixarntz/biome 0.1.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/LICENSE +21 -0
- package/README.md +79 -0
- package/package.json +48 -0
- package/rules/all.grit +29 -0
- package/rules/no-as-unknown-as.grit +5 -0
- package/rules/no-in-operator.grit +5 -0
- package/rules/prefer-object-parameter.grit +18 -0
- package/types/object-hasown/index.d.ts +18 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Felix Arntz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# @felixarntz/biome
|
|
2
|
+
|
|
3
|
+
Reusable [Biome](https://biomejs.dev) lint rules that make your agent produce better code in fewer review cycles, written as [GritQL plugins](https://biomejs.dev/linter/plugins/). Install the package once and reference the rules from any project's Biome config — either all of them with a single line, or individually as needed — without copying `.grit` files around.
|
|
4
|
+
|
|
5
|
+
## Rules
|
|
6
|
+
|
|
7
|
+
| Rule | What it flags |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| `no-as-unknown-as` | `value as unknown as T` double assertions, which bypass TypeScript's type checking entirely. |
|
|
10
|
+
| `no-in-operator` | The `in` operator, recommending `Object.hasOwn(obj, prop)` (which does not walk the prototype chain). |
|
|
11
|
+
| `prefer-object-parameter` | Functions, methods, and constructors with more than one positional parameter, recommending a single object argument with named parameters instead. A leading TypeScript `this` parameter is not counted. |
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
pnpm add -D @felixarntz/biome @biomejs/biome
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
Biome plugins are enabled through the [`plugins`](https://biomejs.dev/reference/configuration/#plugins) array in your `biome.json`. **Biome does not resolve plugin entries as package names** — it only accepts file paths — so the rules are referenced by their path inside `node_modules`.
|
|
22
|
+
|
|
23
|
+
### All rules at once
|
|
24
|
+
|
|
25
|
+
The package ships a generated `rules/all.grit` that bundles every rule. Enable the whole set with a single entry:
|
|
26
|
+
|
|
27
|
+
```jsonc
|
|
28
|
+
{
|
|
29
|
+
"$schema": "https://biomejs.dev/schemas/2.4.16/schema.json",
|
|
30
|
+
"plugins": ["./node_modules/@felixarntz/biome/rules/all.grit"]
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Individual rules
|
|
35
|
+
|
|
36
|
+
Some of the rules are more opinionated than others. So if you don't want to use all of them, you can pick only the rules you want:
|
|
37
|
+
|
|
38
|
+
```jsonc
|
|
39
|
+
{
|
|
40
|
+
"$schema": "https://biomejs.dev/schemas/2.4.16/schema.json",
|
|
41
|
+
"plugins": [
|
|
42
|
+
"./node_modules/@felixarntz/biome/rules/no-in-operator.grit",
|
|
43
|
+
"./node_modules/@felixarntz/biome/rules/no-as-unknown-as.grit",
|
|
44
|
+
"./node_modules/@felixarntz/biome/rules/prefer-object-parameter.grit"
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
> **Note on the path.** Plugin paths are resolved relative to the `biome.json` that declares them.
|
|
50
|
+
>
|
|
51
|
+
> The `./node_modules/...` form above assumes your config sits next to the `node_modules` that contains this package — the usual case for a single-package project, and for pnpm, whose symlink at `node_modules/@felixarntz/biome` resolves transparently. In a monorepo where the config and the installed package live in different directories, adjust the relative path accordingly (e.g. `../../node_modules/@felixarntz/biome/rules/all.grit`).
|
|
52
|
+
>
|
|
53
|
+
> This is a current Biome limitation: `extends` resolves npm packages, but `plugins` never does. Until Biome adds package resolution for plugins, the relative file path is the only way to reference them. Track progress in [biomejs/biome discussion #6265](https://github.com/biomejs/biome/discussions/6265).
|
|
54
|
+
|
|
55
|
+
## `Object.hasOwn` type augmentation
|
|
56
|
+
|
|
57
|
+
`no-in-operator` steers you toward `Object.hasOwn`, but the built-in TypeScript signature returns a plain `boolean` and therefore doesn't narrow the checked object. This package ships a type augmentation that restores narrowing parity with the `in` operator.
|
|
58
|
+
|
|
59
|
+
Enable it by importing it once from any `.ts`/`.d.ts` file that is part of your compilation:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import "@felixarntz/biome/object-hasown";
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Or reference it from `tsconfig.json` (requires `"moduleResolution": "bundler"` or `"node16"`):
|
|
66
|
+
|
|
67
|
+
```jsonc
|
|
68
|
+
{
|
|
69
|
+
"compilerOptions": {
|
|
70
|
+
"types": ["@felixarntz/biome/object-hasown"]
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
After that, `Object.hasOwn(obj, "key")` narrows `obj` the same way `"key" in obj` would.
|
|
76
|
+
|
|
77
|
+
## License
|
|
78
|
+
|
|
79
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@felixarntz/biome",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Reusable Biome (GritQL) lint rules that make your agent produce better code in fewer review cycles.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"biome",
|
|
8
|
+
"biomejs",
|
|
9
|
+
"gritql",
|
|
10
|
+
"lint",
|
|
11
|
+
"rules",
|
|
12
|
+
"linter",
|
|
13
|
+
"plugin"
|
|
14
|
+
],
|
|
15
|
+
"type": "module",
|
|
16
|
+
"files": [
|
|
17
|
+
"rules/",
|
|
18
|
+
"types/",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE"
|
|
21
|
+
],
|
|
22
|
+
"exports": {
|
|
23
|
+
"./object-hasown": {
|
|
24
|
+
"types": "./types/object-hasown/index.d.ts"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"homepage": "https://github.com/felixarntz/biome#readme",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/felixarntz/biome.git"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@biomejs/biome": "^2.4.15",
|
|
34
|
+
"husky": "^9.1.7",
|
|
35
|
+
"typescript": "^6.0.3",
|
|
36
|
+
"ultracite": "7.8.0"
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "node scripts/generate-all.mjs",
|
|
43
|
+
"test": "node scripts/generate-all.mjs && node test/run.mjs",
|
|
44
|
+
"typecheck": "tsc --noEmit",
|
|
45
|
+
"check": "ultracite check",
|
|
46
|
+
"fix": "ultracite fix"
|
|
47
|
+
}
|
|
48
|
+
}
|
package/rules/all.grit
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// AUTO-GENERATED by scripts/generate-all.mjs — do not edit.
|
|
2
|
+
// Combines every rule in rules/*.grit. Run `pnpm build` to regenerate.
|
|
3
|
+
|
|
4
|
+
language js
|
|
5
|
+
|
|
6
|
+
or {
|
|
7
|
+
`$value as unknown as $type` as $expr where {
|
|
8
|
+
register_diagnostic(span=$expr, message="Avoid `as unknown as $type`. Double assertions through `unknown` bypass TypeScript's type checking entirely. Fix the underlying type mismatch instead.")
|
|
9
|
+
},
|
|
10
|
+
`$prop in $obj` as $expr where {
|
|
11
|
+
register_diagnostic(span=$expr, message="Use `Object.hasOwn($obj, $prop)` instead of the `in` operator. `in` also walks the prototype chain, which is rarely the intended runtime check.")
|
|
12
|
+
},
|
|
13
|
+
or {
|
|
14
|
+
JsParameters(items = $params) where {
|
|
15
|
+
or {
|
|
16
|
+
$params <: [TsThisParameter(), $second, $third, ...],
|
|
17
|
+
and {
|
|
18
|
+
$params <: [$first, $second, ...],
|
|
19
|
+
$first <: not TsThisParameter()
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
register_diagnostic(span=$params, message="Use a single object argument with named parameters instead of multiple positional parameters. Object parameters keep call sites self-documenting and make argument order irrelevant.")
|
|
23
|
+
},
|
|
24
|
+
JsConstructorParameters(parameters = $params) where {
|
|
25
|
+
$params <: [$first, $second, ...],
|
|
26
|
+
register_diagnostic(span=$params, message="Use a single object argument with named parameters instead of multiple positional parameters. Object parameters keep call sites self-documenting and make argument order irrelevant.")
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
language js
|
|
2
|
+
|
|
3
|
+
or {
|
|
4
|
+
JsParameters(items = $params) where {
|
|
5
|
+
or {
|
|
6
|
+
$params <: [TsThisParameter(), $second, $third, ...],
|
|
7
|
+
and {
|
|
8
|
+
$params <: [$first, $second, ...],
|
|
9
|
+
$first <: not TsThisParameter()
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
register_diagnostic(span=$params, message="Use a single object argument with named parameters instead of multiple positional parameters. Object parameters keep call sites self-documenting and make argument order irrelevant.")
|
|
13
|
+
},
|
|
14
|
+
JsConstructorParameters(parameters = $params) where {
|
|
15
|
+
$params <: [$first, $second, ...],
|
|
16
|
+
register_diagnostic(span=$params, message="Use a single object argument with named parameters instead of multiple positional parameters. Object parameters keep call sites self-documenting and make argument order irrelevant.")
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Augment `Object.hasOwn` with a type predicate so it narrows the input the
|
|
3
|
+
* same way the `in` operator does. The lib signature returns plain `boolean`,
|
|
4
|
+
* which means TS cannot narrow after a `hasOwn` check — this restores parity.
|
|
5
|
+
*/
|
|
6
|
+
declare global {
|
|
7
|
+
interface ObjectConstructor {
|
|
8
|
+
hasOwn<O extends object, K extends PropertyKey>(
|
|
9
|
+
// biome-ignore lint/plugin/all: this mirrors the native `Object.hasOwn` signature, whose two positional parameters are fixed by the ECMAScript contract.
|
|
10
|
+
o: O,
|
|
11
|
+
v: K
|
|
12
|
+
): o is Extract<O, Record<K, unknown>> extends never
|
|
13
|
+
? O & Record<K, unknown>
|
|
14
|
+
: Extract<O, Record<K, unknown>>;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export {};
|