@felixarntz/biome 0.1.0 → 0.2.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 +18 -4
- package/package.json +1 -1
- package/rules/all.grit +73 -14
- package/rules/no-empty-object-accumulator.grit +5 -0
- package/rules/no-has-own-property.grit +5 -0
- package/rules/no-in-operator.grit +5 -2
- package/rules/no-object-assign-target.grit +6 -0
- package/rules/no-object-from-entries.grit +5 -0
- package/rules/no-prototype-mutation.grit +8 -0
- package/rules/no-prototype-property-access.grit +8 -0
- package/rules/prefer-object-parameter.grit +43 -12
package/README.md
CHANGED
|
@@ -2,13 +2,21 @@
|
|
|
2
2
|
|
|
3
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
4
|
|
|
5
|
+
Rules cover aspects like type-safety escape hatches, prototype-chain-safe property access and iteration, safer dynamic-key object construction, and self-documenting function APIs.
|
|
6
|
+
|
|
5
7
|
## Rules
|
|
6
8
|
|
|
7
9
|
| Rule | What it flags |
|
|
8
10
|
| --- | --- |
|
|
9
11
|
| `no-as-unknown-as` | `value as unknown as T` double assertions, which bypass TypeScript's type checking entirely. |
|
|
10
|
-
| `no-
|
|
11
|
-
| `
|
|
12
|
+
| `no-empty-object-accumulator` | `.reduce(..., {})` accumulator seeds, recommending `Object.create(null)` or `Map` for dynamic-key aggregation. |
|
|
13
|
+
| `no-has-own-property` | Direct `obj.hasOwnProperty(key)` calls, recommending `Object.hasOwn(obj, key)` because the method can be missing or shadowed. |
|
|
14
|
+
| `no-in-operator` | The `in` operator and `for...in`, recommending own-property checks and own-key iteration that do not walk the prototype chain. |
|
|
15
|
+
| `no-object-assign-target` | `Object.assign({}, ...)` plain-object merge targets, recommending `Object.assign(Object.create(null), ...)`. |
|
|
16
|
+
| `no-object-from-entries` | `Object.fromEntries(...)`, which creates a plain object with `Object.prototype` for dynamic keys. |
|
|
17
|
+
| `no-prototype-mutation` | `Object.setPrototypeOf(...)` and `Reflect.setPrototypeOf(...)`, which mutate prototype chains. |
|
|
18
|
+
| `no-prototype-property-access` | Direct `obj.__proto__` and `obj.constructor.prototype` access, which are common prototype-pollution primitives. |
|
|
19
|
+
| `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. Inline callbacks, whose signature is dictated by the calling API, are left alone. |
|
|
12
20
|
|
|
13
21
|
## Installation
|
|
14
22
|
|
|
@@ -39,8 +47,14 @@ Some of the rules are more opinionated than others. So if you don't want to use
|
|
|
39
47
|
{
|
|
40
48
|
"$schema": "https://biomejs.dev/schemas/2.4.16/schema.json",
|
|
41
49
|
"plugins": [
|
|
42
|
-
"./node_modules/@felixarntz/biome/rules/no-in-operator.grit",
|
|
43
50
|
"./node_modules/@felixarntz/biome/rules/no-as-unknown-as.grit",
|
|
51
|
+
"./node_modules/@felixarntz/biome/rules/no-empty-object-accumulator.grit",
|
|
52
|
+
"./node_modules/@felixarntz/biome/rules/no-has-own-property.grit",
|
|
53
|
+
"./node_modules/@felixarntz/biome/rules/no-in-operator.grit",
|
|
54
|
+
"./node_modules/@felixarntz/biome/rules/no-object-assign-target.grit",
|
|
55
|
+
"./node_modules/@felixarntz/biome/rules/no-object-from-entries.grit",
|
|
56
|
+
"./node_modules/@felixarntz/biome/rules/no-prototype-mutation.grit",
|
|
57
|
+
"./node_modules/@felixarntz/biome/rules/no-prototype-property-access.grit",
|
|
44
58
|
"./node_modules/@felixarntz/biome/rules/prefer-object-parameter.grit"
|
|
45
59
|
]
|
|
46
60
|
}
|
|
@@ -54,7 +68,7 @@ Some of the rules are more opinionated than others. So if you don't want to use
|
|
|
54
68
|
|
|
55
69
|
## `Object.hasOwn` type augmentation
|
|
56
70
|
|
|
57
|
-
`no-in-operator`
|
|
71
|
+
`no-in-operator` and `no-has-own-property` steer 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
72
|
|
|
59
73
|
Enable it by importing it once from any `.ts`/`.d.ts` file that is part of your compilation:
|
|
60
74
|
|
package/package.json
CHANGED
package/rules/all.grit
CHANGED
|
@@ -7,23 +7,82 @@ or {
|
|
|
7
7
|
`$value as unknown as $type` as $expr where {
|
|
8
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
9
|
},
|
|
10
|
-
`$
|
|
11
|
-
register_diagnostic(span=$
|
|
10
|
+
`$items.reduce($reducer, {})` as $call where {
|
|
11
|
+
register_diagnostic(span=$call, message="Use `Object.create(null)` or `new Map()` instead of `{}` as a reducer accumulator. Plain object accumulators have a prototype chain that can leak into dynamic-key aggregation.")
|
|
12
|
+
},
|
|
13
|
+
`$obj.hasOwnProperty($...)` as $call where {
|
|
14
|
+
register_diagnostic(span=$call, message="Use `Object.hasOwn($obj, key)` instead of calling `hasOwnProperty()` on the object. The method can be missing on null-prototype objects or shadowed by user-controlled properties.")
|
|
15
|
+
},
|
|
16
|
+
or {
|
|
17
|
+
`$prop in $obj` as $expr,
|
|
18
|
+
JsForInStatement() as $expr
|
|
19
|
+
} where {
|
|
20
|
+
register_diagnostic(span=$expr, message="Avoid prototype-chain property checks and iteration. Use `Object.hasOwn($obj, $prop)` instead of the `in` operator, and `Object.keys()` or `Object.entries()` with `for...of` instead of `for...in`.")
|
|
21
|
+
},
|
|
22
|
+
`Object.assign($target, $...)` as $call where {
|
|
23
|
+
$target <: `{}`,
|
|
24
|
+
register_diagnostic(span=$call, message="Use `Object.assign(Object.create(null), ...)` instead of `Object.assign({}, ...)`. A plain object merge target has a prototype chain that can preserve prototype-pollution risk.")
|
|
25
|
+
},
|
|
26
|
+
`Object.fromEntries($...)` as $call where {
|
|
27
|
+
register_diagnostic(span=$call, message="Avoid `Object.fromEntries()` for dynamic keys. It creates a plain object with `Object.prototype`; use `Map`, a null-prototype helper, or explicit assignment into `Object.create(null)` instead.")
|
|
12
28
|
},
|
|
13
29
|
or {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
30
|
+
`Object.setPrototypeOf($...)` as $call,
|
|
31
|
+
`Reflect.setPrototypeOf($...)` as $call
|
|
32
|
+
} where {
|
|
33
|
+
register_diagnostic(span=$call, message="Avoid mutating prototype chains with `setPrototypeOf()`. Prefer creating the intended shape up front with `Object.create(null)` or an explicit class/object model.")
|
|
34
|
+
},
|
|
35
|
+
or {
|
|
36
|
+
`$obj.__proto__` as $access,
|
|
37
|
+
`$obj.constructor.prototype` as $access
|
|
38
|
+
} where {
|
|
39
|
+
register_diagnostic(span=$access, message="Avoid direct prototype access through `__proto__` or `constructor.prototype`. These property paths are common prototype-pollution primitives; use explicit prototype APIs only in audited code.")
|
|
40
|
+
},
|
|
41
|
+
// Every arm below binds $params to a parameter items list, then the shared
|
|
42
|
+
// trailing where applies one multi-positional check and emits the diagnostic.
|
|
43
|
+
// The list is deliberately structural: it covers signatures whose author owns
|
|
44
|
+
// the parameter list -- named/declared functions, every method form (class,
|
|
45
|
+
// object, interface, abstract), constructors and construct signatures, function
|
|
46
|
+
// values assigned to a variable, and named function/constructor type aliases.
|
|
47
|
+
// It intentionally omits callback-shaped signatures whose parameter list is
|
|
48
|
+
// dictated by the API that calls them: arrow/function expressions passed inline
|
|
49
|
+
// (such as the reducer in arr.reduce(...)), object-property function values,
|
|
50
|
+
// class arrow fields, bare call signatures, and inline function types. Those
|
|
51
|
+
// cannot be rewritten to a single object parameter by the author, so flagging
|
|
52
|
+
// them only produces unfixable diagnostics.
|
|
53
|
+
//
|
|
54
|
+
// Grit parameterized pattern definitions do not compile in Biome, so the
|
|
55
|
+
// multi-positional check cannot be factored into a helper; the shared trailing
|
|
56
|
+
// where is how it stays written once.
|
|
57
|
+
or {
|
|
58
|
+
JsFunctionDeclaration(parameters = JsParameters(items = $params)),
|
|
59
|
+
TsDeclareFunctionDeclaration(parameters = JsParameters(items = $params)),
|
|
60
|
+
JsMethodClassMember(parameters = JsParameters(items = $params)),
|
|
61
|
+
TsMethodSignatureClassMember(parameters = JsParameters(items = $params)),
|
|
62
|
+
JsMethodObjectMember(parameters = JsParameters(items = $params)),
|
|
63
|
+
TsMethodSignatureTypeMember(parameters = JsParameters(items = $params)),
|
|
64
|
+
TsConstructSignatureTypeMember(parameters = JsParameters(items = $params)),
|
|
65
|
+
JsConstructorParameters(parameters = $params),
|
|
66
|
+
JsVariableDeclarator(initializer = JsInitializerClause(expression = $fn)) where {
|
|
67
|
+
$fn <: or {
|
|
68
|
+
JsArrowFunctionExpression(parameters = JsParameters(items = $params)),
|
|
69
|
+
JsFunctionExpression(parameters = JsParameters(items = $params))
|
|
70
|
+
}
|
|
23
71
|
},
|
|
24
|
-
|
|
25
|
-
$
|
|
26
|
-
|
|
72
|
+
TsTypeAliasDeclaration(ty = $aliased) where {
|
|
73
|
+
$aliased <: or {
|
|
74
|
+
TsFunctionType(parameters = JsParameters(items = $params)),
|
|
75
|
+
TsConstructorType(parameters = JsParameters(items = $params))
|
|
76
|
+
}
|
|
27
77
|
}
|
|
78
|
+
} where {
|
|
79
|
+
or {
|
|
80
|
+
$params <: [TsThisParameter(), $second, $third, ...],
|
|
81
|
+
and {
|
|
82
|
+
$params <: [$first, $second, ...],
|
|
83
|
+
$first <: not TsThisParameter()
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
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.")
|
|
28
87
|
}
|
|
29
88
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
language js
|
|
2
|
+
|
|
3
|
+
`$items.reduce($reducer, {})` as $call where {
|
|
4
|
+
register_diagnostic(span=$call, message="Use `Object.create(null)` or `new Map()` instead of `{}` as a reducer accumulator. Plain object accumulators have a prototype chain that can leak into dynamic-key aggregation.")
|
|
5
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
language js
|
|
2
|
+
|
|
3
|
+
`$obj.hasOwnProperty($...)` as $call where {
|
|
4
|
+
register_diagnostic(span=$call, message="Use `Object.hasOwn($obj, key)` instead of calling `hasOwnProperty()` on the object. The method can be missing on null-prototype objects or shadowed by user-controlled properties.")
|
|
5
|
+
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
language js
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
or {
|
|
4
|
+
`$prop in $obj` as $expr,
|
|
5
|
+
JsForInStatement() as $expr
|
|
6
|
+
} where {
|
|
7
|
+
register_diagnostic(span=$expr, message="Avoid prototype-chain property checks and iteration. Use `Object.hasOwn($obj, $prop)` instead of the `in` operator, and `Object.keys()` or `Object.entries()` with `for...of` instead of `for...in`.")
|
|
5
8
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
language js
|
|
2
|
+
|
|
3
|
+
`Object.assign($target, $...)` as $call where {
|
|
4
|
+
$target <: `{}`,
|
|
5
|
+
register_diagnostic(span=$call, message="Use `Object.assign(Object.create(null), ...)` instead of `Object.assign({}, ...)`. A plain object merge target has a prototype chain that can preserve prototype-pollution risk.")
|
|
6
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
language js
|
|
2
|
+
|
|
3
|
+
`Object.fromEntries($...)` as $call where {
|
|
4
|
+
register_diagnostic(span=$call, message="Avoid `Object.fromEntries()` for dynamic keys. It creates a plain object with `Object.prototype`; use `Map`, a null-prototype helper, or explicit assignment into `Object.create(null)` instead.")
|
|
5
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
language js
|
|
2
|
+
|
|
3
|
+
or {
|
|
4
|
+
`Object.setPrototypeOf($...)` as $call,
|
|
5
|
+
`Reflect.setPrototypeOf($...)` as $call
|
|
6
|
+
} where {
|
|
7
|
+
register_diagnostic(span=$call, message="Avoid mutating prototype chains with `setPrototypeOf()`. Prefer creating the intended shape up front with `Object.create(null)` or an explicit class/object model.")
|
|
8
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
language js
|
|
2
|
+
|
|
3
|
+
or {
|
|
4
|
+
`$obj.__proto__` as $access,
|
|
5
|
+
`$obj.constructor.prototype` as $access
|
|
6
|
+
} where {
|
|
7
|
+
register_diagnostic(span=$access, message="Avoid direct prototype access through `__proto__` or `constructor.prototype`. These property paths are common prototype-pollution primitives; use explicit prototype APIs only in audited code.")
|
|
8
|
+
}
|
|
@@ -1,18 +1,49 @@
|
|
|
1
1
|
language js
|
|
2
2
|
|
|
3
|
+
// Every arm below binds $params to a parameter items list, then the shared
|
|
4
|
+
// trailing where applies one multi-positional check and emits the diagnostic.
|
|
5
|
+
// The list is deliberately structural: it covers signatures whose author owns
|
|
6
|
+
// the parameter list -- named/declared functions, every method form (class,
|
|
7
|
+
// object, interface, abstract), constructors and construct signatures, function
|
|
8
|
+
// values assigned to a variable, and named function/constructor type aliases.
|
|
9
|
+
// It intentionally omits callback-shaped signatures whose parameter list is
|
|
10
|
+
// dictated by the API that calls them: arrow/function expressions passed inline
|
|
11
|
+
// (such as the reducer in arr.reduce(...)), object-property function values,
|
|
12
|
+
// class arrow fields, bare call signatures, and inline function types. Those
|
|
13
|
+
// cannot be rewritten to a single object parameter by the author, so flagging
|
|
14
|
+
// them only produces unfixable diagnostics.
|
|
15
|
+
//
|
|
16
|
+
// Grit parameterized pattern definitions do not compile in Biome, so the
|
|
17
|
+
// multi-positional check cannot be factored into a helper; the shared trailing
|
|
18
|
+
// where is how it stays written once.
|
|
3
19
|
or {
|
|
4
|
-
JsParameters(items = $params)
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
20
|
+
JsFunctionDeclaration(parameters = JsParameters(items = $params)),
|
|
21
|
+
TsDeclareFunctionDeclaration(parameters = JsParameters(items = $params)),
|
|
22
|
+
JsMethodClassMember(parameters = JsParameters(items = $params)),
|
|
23
|
+
TsMethodSignatureClassMember(parameters = JsParameters(items = $params)),
|
|
24
|
+
JsMethodObjectMember(parameters = JsParameters(items = $params)),
|
|
25
|
+
TsMethodSignatureTypeMember(parameters = JsParameters(items = $params)),
|
|
26
|
+
TsConstructSignatureTypeMember(parameters = JsParameters(items = $params)),
|
|
27
|
+
JsConstructorParameters(parameters = $params),
|
|
28
|
+
JsVariableDeclarator(initializer = JsInitializerClause(expression = $fn)) where {
|
|
29
|
+
$fn <: or {
|
|
30
|
+
JsArrowFunctionExpression(parameters = JsParameters(items = $params)),
|
|
31
|
+
JsFunctionExpression(parameters = JsParameters(items = $params))
|
|
32
|
+
}
|
|
13
33
|
},
|
|
14
|
-
|
|
15
|
-
$
|
|
16
|
-
|
|
34
|
+
TsTypeAliasDeclaration(ty = $aliased) where {
|
|
35
|
+
$aliased <: or {
|
|
36
|
+
TsFunctionType(parameters = JsParameters(items = $params)),
|
|
37
|
+
TsConstructorType(parameters = JsParameters(items = $params))
|
|
38
|
+
}
|
|
17
39
|
}
|
|
40
|
+
} where {
|
|
41
|
+
or {
|
|
42
|
+
$params <: [TsThisParameter(), $second, $third, ...],
|
|
43
|
+
and {
|
|
44
|
+
$params <: [$first, $second, ...],
|
|
45
|
+
$first <: not TsThisParameter()
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
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.")
|
|
18
49
|
}
|