@onrails/biome-plugin 0.1.2 → 0.1.4
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 +15 -1
- package/package.json +5 -4
- package/rules/fluent-stays-local.grit +27 -0
- package/rules/no-deprecated-synonyms.grit +33 -20
package/README.md
CHANGED
|
@@ -16,7 +16,9 @@ Reference the rule files in `biome.json` plugins:
|
|
|
16
16
|
{
|
|
17
17
|
"plugins": [
|
|
18
18
|
"./node_modules/@onrails/biome-plugin/rules/no-promise-result.grit",
|
|
19
|
-
"./node_modules/@onrails/biome-plugin/rules/no-unsafe-unwrap.grit"
|
|
19
|
+
"./node_modules/@onrails/biome-plugin/rules/no-unsafe-unwrap.grit",
|
|
20
|
+
"./node_modules/@onrails/biome-plugin/rules/no-deprecated-synonyms.grit",
|
|
21
|
+
"./node_modules/@onrails/biome-plugin/rules/fluent-stays-local.grit"
|
|
20
22
|
]
|
|
21
23
|
}
|
|
22
24
|
```
|
|
@@ -27,6 +29,18 @@ Reference the rule files in `biome.json` plugins:
|
|
|
27
29
|
|------|------------------|
|
|
28
30
|
| `no-promise-result` | `Promise<Result<T, E>>` in any type position — return `ResultAsync<T, E>` and use `fromAsync` / `tryAsync` at the boundary (error) |
|
|
29
31
|
| `no-unsafe-unwrap` | `._unsafeUnwrap()` / `._unsafeUnwrapErr()` calls — use `match` / `resolve` / `yieldResult` instead (error) |
|
|
32
|
+
| `no-deprecated-synonyms` | Deprecated synonyms (`chain`, `fold`, `getOrElse`, `of`, …) — use the canonical names (error, partly fixable) |
|
|
33
|
+
| `fluent-stays-local` | Bare `FluentResult<…>` / `FluentMaybe<…>` type references — broader net than the ESLint rule, see `ENGINE_DIVERGENCES` (error) |
|
|
34
|
+
|
|
35
|
+
## Fixes
|
|
36
|
+
|
|
37
|
+
`no-deprecated-synonyms` rewrites. `.chain()` → `.flatMap()` is a **safe** fix, applied by `biome check --write`. The free-function renames (`matchResult`/`matchMaybe` → `match`, `getOrElse` → `unwrapOr`, `collect` → `combine`, `sequenceTupleAsync` → `ResultAsync.combineTuple`) are **unsafe** — the canonical name still has to be imported, which GritQL cannot do — so they need `biome check --write --unsafe` plus an import fix, or `@onrails/codemod`, which rewrites the imports too. `fold`, `of`, and `ResultAsync.isOk()` / `.isErr()` have no mechanical rewrite and stay diagnostic-only.
|
|
38
|
+
|
|
39
|
+
## Scoping
|
|
40
|
+
|
|
41
|
+
`no-deprecated-synonyms` only fires in files that mention an `@onrails/*` module source, so RxJS `of` or fp-ts `fold` in unrelated files are left alone. Files that reach onrails through a local alias or re-export are not matched — use the ESLint plugin, which matches by name everywhere. Declared as `no-deprecated-synonyms` / `import-scoped` in `ENGINE_DIVERGENCES`.
|
|
42
|
+
|
|
43
|
+
Requires Biome >= 2.5.12 for plugin rewrites and `fix_kind`.
|
|
30
44
|
|
|
31
45
|
## Tests
|
|
32
46
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onrails/biome-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Biome GritQL plugin: boundary diagnostics for @onrails/result (Promise<Result<…>>, _unsafeUnwrap*).",
|
|
6
6
|
"license": "MIT",
|
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
"exports": {
|
|
40
40
|
"./rules/no-promise-result.grit": "./rules/no-promise-result.grit",
|
|
41
41
|
"./rules/no-unsafe-unwrap.grit": "./rules/no-unsafe-unwrap.grit",
|
|
42
|
-
"./rules/no-deprecated-synonyms.grit": "./rules/no-deprecated-synonyms.grit"
|
|
42
|
+
"./rules/no-deprecated-synonyms.grit": "./rules/no-deprecated-synonyms.grit",
|
|
43
|
+
"./rules/fluent-stays-local.grit": "./rules/fluent-stays-local.grit"
|
|
43
44
|
},
|
|
44
45
|
"scripts": {
|
|
45
46
|
"typecheck": "echo 'noop (grit files only)'",
|
|
@@ -47,9 +48,9 @@
|
|
|
47
48
|
"check": "bun test"
|
|
48
49
|
},
|
|
49
50
|
"peerDependencies": {
|
|
50
|
-
"@biomejs/biome": ">=2.
|
|
51
|
+
"@biomejs/biome": ">=2.5.12"
|
|
51
52
|
},
|
|
52
53
|
"devDependencies": {
|
|
53
|
-
"@biomejs/biome": "^2.
|
|
54
|
+
"@biomejs/biome": "^2.5.12"
|
|
54
55
|
}
|
|
55
56
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// @onrails/biome-plugin: fluent-stays-local
|
|
2
|
+
//
|
|
3
|
+
// Flag `FluentResult<T, E>` / `FluentMaybe<T>` named as a type — the fluent
|
|
4
|
+
// wrapper (`@onrails/result/fluent`, `@onrails/maybe/fluent`) must open and
|
|
5
|
+
// close within a function body, never escape as a return type, an exported
|
|
6
|
+
// binding, or a stored field.
|
|
7
|
+
//
|
|
8
|
+
// Limitation: GritQL matches the bare type reference wherever it appears —
|
|
9
|
+
// a broader net than the ESLint rule, which only flags the four scoped
|
|
10
|
+
// positions (return type, exported binding, stored field, serialize-sink
|
|
11
|
+
// argument) and inspects call-argument shape for the sink check. This rule
|
|
12
|
+
// cannot express that argument heuristic at all; declared in
|
|
13
|
+
// ENGINE_DIVERGENCES (`fluent-stays-local` / `no-position-scoping`).
|
|
14
|
+
|
|
15
|
+
engine biome(1.0)
|
|
16
|
+
language js
|
|
17
|
+
|
|
18
|
+
or {
|
|
19
|
+
`FluentResult<$ok, $err>` as $t,
|
|
20
|
+
`FluentMaybe<$value>` as $t
|
|
21
|
+
} where {
|
|
22
|
+
register_diagnostic(
|
|
23
|
+
span = $t,
|
|
24
|
+
message = "FluentResult/FluentMaybe must stay local — it was flagged as a return type, an exported binding, a stored field, or an argument to a serialize/postMessage/cache call. Call toResult()/toMaybe()/toString() before it leaves the function.",
|
|
25
|
+
severity = "error"
|
|
26
|
+
)
|
|
27
|
+
}
|
|
@@ -9,32 +9,45 @@
|
|
|
9
9
|
// - collect() -> combine()
|
|
10
10
|
// - ResultAsync.isOk() / isErr() -> await and narrow with isOk() / isErr()
|
|
11
11
|
//
|
|
12
|
-
//
|
|
13
|
-
// same-named calls from other libraries (RxJS `of`, fp-ts `fold
|
|
14
|
-
//
|
|
15
|
-
//
|
|
12
|
+
// Scoping: only files that mention an `@onrails/*` module source are matched,
|
|
13
|
+
// so same-named calls from other libraries (RxJS `of`, fp-ts `fold`) are left
|
|
14
|
+
// alone. The ESLint rule is name-based everywhere; declared in
|
|
15
|
+
// ENGINE_DIVERGENCES (`no-deprecated-synonyms` / `import-scoped`).
|
|
16
|
+
//
|
|
17
|
+
// Fixes: `.chain()` -> `.flatMap()` is a safe rewrite (method rename, no
|
|
18
|
+
// import churn). The free-function renames are unsafe fixes — they need the
|
|
19
|
+
// canonical name imported, which GritQL cannot add; run `--write --unsafe`
|
|
20
|
+
// and fix the import, or use `@onrails/codemod`, which rewrites both.
|
|
16
21
|
|
|
17
22
|
engine biome(1.0)
|
|
18
|
-
language js
|
|
23
|
+
language js(typescript)
|
|
19
24
|
|
|
20
25
|
or {
|
|
21
|
-
`$
|
|
22
|
-
`$
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
`
|
|
28
|
-
`
|
|
29
|
-
`
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
} where {
|
|
26
|
+
`$x.chain($args)` => `$x.flatMap($args)`,
|
|
27
|
+
`$x?.chain($args)` => `$x?.flatMap($args)`,
|
|
28
|
+
`matchResult($args)` => `match($args)`,
|
|
29
|
+
`matchMaybe($args)` => `match($args)`,
|
|
30
|
+
`getOrElse($args)` => `unwrapOr($args)`,
|
|
31
|
+
`collect($args)` => `combine($args)`,
|
|
32
|
+
`sequenceTupleAsync($args)` => `ResultAsync.combineTuple($args)`,
|
|
33
|
+
`fold($args)`,
|
|
34
|
+
`of($args)`,
|
|
35
|
+
`$x.isOk()`,
|
|
36
|
+
`$x?.isOk()`,
|
|
37
|
+
`$x.isErr()`,
|
|
38
|
+
`$x?.isErr()`
|
|
39
|
+
} as $call where {
|
|
40
|
+
$call <: within JsModule(items = $items),
|
|
41
|
+
$items <: contains JsModuleSource() as $source,
|
|
42
|
+
$source <: r".*@onrails/.*",
|
|
43
|
+
or {
|
|
44
|
+
{ $call <: or { `$_.chain($_)`, `$_?.chain($_)` }, $kind = "safe" },
|
|
45
|
+
{ $kind = "unsafe" }
|
|
46
|
+
},
|
|
35
47
|
register_diagnostic(
|
|
36
48
|
span = $call,
|
|
37
49
|
message = "Avoid deprecated onrails synonyms/methods. Use the canonical equivalents instead.",
|
|
38
|
-
severity = "error"
|
|
50
|
+
severity = "error",
|
|
51
|
+
fix_kind = $kind
|
|
39
52
|
)
|
|
40
53
|
}
|