@doubleloop-io/oxlint-plugin-effect 0.1.0-rc.0 → 0.1.0-rc.1
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 +42 -36
- package/RULES.md +87 -67
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -4
- package/dist/presets/all.json +3 -2
- package/dist/rules/all-errors-handled-at-boundary.d.ts +1 -0
- package/dist/rules/all-errors-handled-at-boundary.js +111 -0
- package/dist/rules/error-handling-in-effect-fn-handler.js +80 -2
- package/dist/rules/no-bare-error-in-effect.js +17 -2
- package/dist/rules/no-manual-discriminant-comparison.js +33 -10
- package/dist/rules/no-manual-discriminant-construction.js +47 -5
- package/dist/rules/no-provide-on-definition.js +30 -1
- package/dist/rules/prefer-effect-vitest.d.ts +1 -0
- package/dist/rules/prefer-effect-vitest.js +52 -0
- package/dist/rules/redacted-secret-names.js +2 -4
- package/dist/rules/run-only-in-runner.d.ts +1 -0
- package/dist/rules/{run-only-at-entry.js → run-only-in-runner.js} +10 -20
- package/dist/rules/single-style-per-function.js +89 -21
- package/dist/shared/effect-ast.d.ts +1 -0
- package/dist/shared/effect-ast.js +15 -4
- package/dist/shared/effect-runners.d.ts +2 -0
- package/dist/shared/effect-runners.js +14 -0
- package/dist/shared/entry-files.d.ts +1 -1
- package/dist/shared/test-files.d.ts +1 -1
- package/package.json +62 -56
- package/dist/rules/boundary-entrypoint-shape.d.ts +0 -1
- package/dist/rules/boundary-entrypoint-shape.js +0 -52
- package/dist/rules/run-only-at-entry.d.ts +0 -1
package/README.md
CHANGED
|
@@ -18,9 +18,16 @@ Requires Node `^22.20.0 || >=24.8.0` and oxlint `^1.82.0`.
|
|
|
18
18
|
|
|
19
19
|
## Configuration
|
|
20
20
|
|
|
21
|
-
Extend the `all` preset.
|
|
21
|
+
Extend the `all` preset. Prefer `oxlint.config.ts`, where `extends` takes config objects and the preset is imported by package name:
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
```ts
|
|
24
|
+
import dlEffectAll from "@doubleloop-io/oxlint-plugin-effect/presets/all"
|
|
25
|
+
import { defineConfig } from "oxlint"
|
|
26
|
+
|
|
27
|
+
export default defineConfig({ extends: [dlEffectAll] })
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
With `.oxlintrc.json`, `extends` takes file paths only, so the preset is reached through `node_modules`:
|
|
24
31
|
|
|
25
32
|
```json
|
|
26
33
|
{
|
|
@@ -28,48 +35,45 @@ With `.oxlintrc.json`, where `extends` takes file paths:
|
|
|
28
35
|
}
|
|
29
36
|
```
|
|
30
37
|
|
|
31
|
-
|
|
38
|
+
`all` enables every rule at `warn`, so adopting it never breaks a build: look at the warnings first, then raise to `error` the rules you want to enforce. A `recommended` preset with reasoned severities will come from measurements on real codebases.
|
|
32
39
|
|
|
33
|
-
|
|
34
|
-
import dlEffectAll from "@doubleloop-io/oxlint-plugin-effect/presets/all"
|
|
35
|
-
import { defineConfig } from "oxlint"
|
|
40
|
+
To pick rules without the preset, load the plugin by package name and list the rules:
|
|
36
41
|
|
|
37
|
-
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"jsPlugins": ["@doubleloop-io/oxlint-plugin-effect"],
|
|
45
|
+
"rules": {
|
|
46
|
+
"dl-effect/no-effect-promise": "error",
|
|
47
|
+
"dl-effect/try-object-form": "error"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
38
50
|
```
|
|
39
51
|
|
|
40
|
-
`all` enables every rule at `warn`, so adopting it never breaks a build: look at the warnings first, then raise to `error` the rules you want to enforce. A `recommended` preset with reasoned severities will come from measurements on real codebases.
|
|
41
|
-
|
|
42
52
|
### Configuration options
|
|
43
53
|
|
|
44
54
|
Two options tell the rules where your files are. Their defaults fit a Node CLI; on another layout (Next.js, a monorepo) set them before reading the warnings.
|
|
45
55
|
|
|
46
|
-
- `entryFiles`: the files allowed to run Effects and import adapters. Default `**/main.ts`, `**/cli.ts`, `**/bin/**`. Used by `run-only-
|
|
47
|
-
- `testFiles`: the files exempt from the rules that tests legitimately break. Default `**/*.test.*`, `**/*.spec.*`, `**/__tests__/**`, `**/test/**`, `**/tests/**`. Used by `adapter-import-boundary`, `no-effect-promise`, `provide-named-layer`, `no-provide-on-definition`, `error-handling-in-effect-fn-handler` and `anti-slop-no-service-constructor-imports`.
|
|
56
|
+
- `entryFiles`: the files allowed to run Effects and import adapters. Default `**/main.ts`, `**/cli.ts`, `**/bin/**`. Used by `run-only-in-runner` and `adapter-import-boundary`.
|
|
57
|
+
- `testFiles`: the test files, exempt from the rules that tests legitimately break and the only files `prefer-effect-vitest` checks. Default `**/*.test.*`, `**/*.spec.*`, `**/__tests__/**`, `**/test/**`, `**/tests/**`. Used by `run-only-in-runner`, `prefer-effect-vitest`, `adapter-import-boundary`, `no-effect-promise`, `provide-named-layer`, `no-provide-on-definition`, `error-handling-in-effect-fn-handler`, `all-errors-handled-at-boundary` and `anti-slop-no-service-constructor-imports`.
|
|
48
58
|
|
|
49
|
-
|
|
59
|
+
Set an option only where its default does not fit. It **replaces** the default instead of extending it, and every rule that uses it must get the same value. With `oxlint.config.ts`, declare it once and override the rules that use it after `extends`:
|
|
50
60
|
|
|
51
61
|
```ts
|
|
52
62
|
import dlEffectAll from "@doubleloop-io/oxlint-plugin-effect/presets/all"
|
|
53
63
|
import { defineConfig } from "oxlint"
|
|
54
64
|
|
|
55
65
|
const entryFiles = ["apps/web/src/app/**/route.ts", "apps/web/src/runtime.ts"]
|
|
56
|
-
const testFiles = ["**/*.test.ts", "e2e/**"]
|
|
57
66
|
|
|
58
67
|
export default defineConfig({
|
|
59
68
|
extends: [dlEffectAll],
|
|
60
69
|
rules: {
|
|
61
|
-
"dl-effect/run-only-
|
|
62
|
-
"dl-effect/adapter-import-boundary": ["warn", { entryFiles
|
|
63
|
-
"dl-effect/no-effect-promise": ["warn", { testFiles }],
|
|
64
|
-
"dl-effect/provide-named-layer": ["warn", { testFiles }],
|
|
65
|
-
"dl-effect/no-provide-on-definition": ["warn", { testFiles }],
|
|
66
|
-
"dl-effect/error-handling-in-effect-fn-handler": ["warn", { testFiles }],
|
|
67
|
-
"dl-effect/anti-slop-no-service-constructor-imports": ["warn", { testFiles }],
|
|
70
|
+
"dl-effect/run-only-in-runner": ["warn", { entryFiles }],
|
|
71
|
+
"dl-effect/adapter-import-boundary": ["warn", { entryFiles }],
|
|
68
72
|
},
|
|
69
73
|
})
|
|
70
74
|
```
|
|
71
75
|
|
|
72
|
-
With `.oxlintrc.json` the
|
|
76
|
+
The package exports the defaults as `defaultEntryFiles` and `defaultTestFiles`: spread them to extend a default (`testFiles: [...defaultTestFiles, "e2e/**"]`), or reuse them in your own `overrides`. With `.oxlintrc.json` the overrides go under `rules`, repeating each list in every rule.
|
|
73
77
|
|
|
74
78
|
The other options and every default are in [Default configuration](#default-configuration).
|
|
75
79
|
|
|
@@ -77,6 +81,14 @@ The other options and every default are in [Default configuration](#default-conf
|
|
|
77
81
|
|
|
78
82
|
[`@effect/tsgo`](https://github.com/Effect-TS/tsgo) is the official Effect language service on top of the TypeScript Go compiler; its `effecttsgo` oxlint presets add more than 100 type-aware rules. This plugin does not need it, and its rules target what those presets do not check, so the two complement each other. To use both, follow its [Oxlint Setup guide](https://github.com/Effect-TS/tsgo/blob/main/docs/README.md) and put its preset in `extends` **before** `all`:
|
|
79
83
|
|
|
84
|
+
```ts
|
|
85
|
+
import dlEffectAll from "@doubleloop-io/oxlint-plugin-effect/presets/all"
|
|
86
|
+
import { recommended as effectTsgoRecommended } from "@effect/tsgo/oxlint-presets"
|
|
87
|
+
import { defineConfig } from "oxlint"
|
|
88
|
+
|
|
89
|
+
export default defineConfig({ extends: [effectTsgoRecommended, dlEffectAll] })
|
|
90
|
+
```
|
|
91
|
+
|
|
80
92
|
```json
|
|
81
93
|
{
|
|
82
94
|
"extends": [
|
|
@@ -86,14 +98,6 @@ The other options and every default are in [Default configuration](#default-conf
|
|
|
86
98
|
}
|
|
87
99
|
```
|
|
88
100
|
|
|
89
|
-
```ts
|
|
90
|
-
import dlEffectAll from "@doubleloop-io/oxlint-plugin-effect/presets/all"
|
|
91
|
-
import { recommended as effectTsgoRecommended } from "@effect/tsgo/oxlint-presets"
|
|
92
|
-
import { defineConfig } from "oxlint"
|
|
93
|
-
|
|
94
|
-
export default defineConfig({ extends: [effectTsgoRecommended, dlEffectAll] })
|
|
95
|
-
```
|
|
96
|
-
|
|
97
101
|
## Rules
|
|
98
102
|
|
|
99
103
|
What each rule reports and why is in [`RULES.md`](RULES.md).
|
|
@@ -103,7 +107,7 @@ What each rule reports and why is in [`RULES.md`](RULES.md).
|
|
|
103
107
|
| Error channel | [`no-bare-error-in-effect`](RULES.md#no-bare-error-in-effect), [`try-object-form`](RULES.md#try-object-form), [`no-effect-promise`](RULES.md#no-effect-promise), [`no-parameterless-catch-all`](RULES.md#no-parameterless-catch-all), [`error-handling-in-effect-fn-handler`](RULES.md#error-handling-in-effect-fn-handler), [`anti-slop-no-manual-effect-error-tag`](RULES.md#anti-slop-no-manual-effect-error-tag) |
|
|
104
108
|
| Tagged values | [`no-manual-discriminant-comparison`](RULES.md#no-manual-discriminant-comparison), [`no-manual-discriminant-construction`](RULES.md#no-manual-discriminant-construction) |
|
|
105
109
|
| Services & Layers | [`service-port-shape-only`](RULES.md#service-port-shape-only), [`service-key-matches-name`](RULES.md#service-key-matches-name), [`service-built-with-of`](RULES.md#service-built-with-of), [`provide-named-layer`](RULES.md#provide-named-layer), [`no-provide-on-definition`](RULES.md#no-provide-on-definition), [`anti-slop-no-service-constructor-imports`](RULES.md#anti-slop-no-service-constructor-imports) |
|
|
106
|
-
| Runtime & entrypoint | [`run-only-
|
|
110
|
+
| Runtime & entrypoint | [`run-only-in-runner`](RULES.md#run-only-in-runner), [`all-errors-handled-at-boundary`](RULES.md#all-errors-handled-at-boundary) |
|
|
107
111
|
| Design & boundaries | [`adapter-import-boundary`](RULES.md#adapter-import-boundary), [`no-transport-types-in-generator`](RULES.md#no-transport-types-in-generator) |
|
|
108
112
|
| Effectful functions | [`prefer-effect-fn`](RULES.md#prefer-effect-fn), [`untraced-recursion`](RULES.md#untraced-recursion), [`single-style-per-function`](RULES.md#single-style-per-function) |
|
|
109
113
|
| Code shape | [`no-nested-match`](RULES.md#no-nested-match), [`no-yielded-match`](RULES.md#no-yielded-match), [`no-inline-yield-star`](RULES.md#no-inline-yield-star), [`no-effect-return-type`](RULES.md#no-effect-return-type), [`no-asymmetric-match-handlers`](RULES.md#no-asymmetric-match-handlers), [`anti-slop-prefer-effect-match`](RULES.md#anti-slop-prefer-effect-match) |
|
|
@@ -143,8 +147,7 @@ const discriminants = ["_tag"]
|
|
|
143
147
|
const secretFields = ["password", "secret", "apiKey", "accessToken", "refreshToken", "idToken"]
|
|
144
148
|
|
|
145
149
|
export default defineConfig({
|
|
146
|
-
|
|
147
|
-
jsPlugins: ["./node_modules/@doubleloop-io/oxlint-plugin-effect/dist/index.js"],
|
|
150
|
+
jsPlugins: ["@doubleloop-io/oxlint-plugin-effect"],
|
|
148
151
|
// Every rule at `warn`, to measure noise without blocking.
|
|
149
152
|
rules: {
|
|
150
153
|
// Error channel
|
|
@@ -153,6 +156,7 @@ export default defineConfig({
|
|
|
153
156
|
"dl-effect/no-effect-promise": ["warn", { testFiles }],
|
|
154
157
|
"dl-effect/no-parameterless-catch-all": "warn",
|
|
155
158
|
"dl-effect/error-handling-in-effect-fn-handler": ["warn", { testFiles }],
|
|
159
|
+
"dl-effect/all-errors-handled-at-boundary": ["warn", { testFiles }],
|
|
156
160
|
"dl-effect/anti-slop-no-manual-effect-error-tag": "warn",
|
|
157
161
|
// Tagged values
|
|
158
162
|
"dl-effect/no-manual-discriminant-comparison": ["warn", { discriminants }],
|
|
@@ -165,8 +169,9 @@ export default defineConfig({
|
|
|
165
169
|
"dl-effect/no-provide-on-definition": ["warn", { testFiles }],
|
|
166
170
|
"dl-effect/anti-slop-no-service-constructor-imports": ["warn", { testFiles }],
|
|
167
171
|
// Runtime & entrypoint
|
|
168
|
-
"dl-effect/run-only-
|
|
169
|
-
"dl-effect/
|
|
172
|
+
"dl-effect/run-only-in-runner": ["warn", { entryFiles, testFiles }],
|
|
173
|
+
"dl-effect/prefer-effect-vitest": ["warn", { testFiles }],
|
|
174
|
+
"dl-effect/all-errors-handled-at-boundary": "warn",
|
|
170
175
|
// Design & boundaries
|
|
171
176
|
"dl-effect/adapter-import-boundary": ["warn", { entryFiles, testFiles }],
|
|
172
177
|
"dl-effect/no-transport-types-in-generator": ["warn", { transportTypes }],
|
|
@@ -204,8 +209,9 @@ Supported oxlint is the `peerDependencies` range, `^1.82.0`; CI tests the tarbal
|
|
|
204
209
|
- `pnpm check`: format check, typecheck, lint and tests, writing nothing. The tests are the `RuleTester`s from `oxlint/plugins-dev` with `node --test`, plus an alignment check of `RULES.md` with one section per rule.
|
|
205
210
|
- `pnpm build`: compiles to `dist/` and generates `dist/presets/all.json` from `presets/all.ts` (the rule list comes from the plugin).
|
|
206
211
|
- `pnpm pack:check`: checks the built preset, then the tarball `npm pack` would publish: only the files in `files`, no internal names, `publint --strict`.
|
|
207
|
-
- `pnpm smoke-test`: installs the tarball in `smoke-fixture/` with pnpm and checks the diagnostics of oxlint with
|
|
212
|
+
- `pnpm smoke-test`: installs the tarball in `smoke-fixture/` with pnpm and checks the diagnostics of oxlint with each config form: `oxlint.config.ts` and `.oxlintrc.json` with the preset, `.oxlintrc.json` with the plugin by package name.
|
|
208
213
|
- `pnpm build-and-pack`: `check`, `build`, `pack:check` and `smoke-test`; run it after every change.
|
|
209
|
-
- `
|
|
214
|
+
- `pnpm build:w`: reruns `pnpm build`, preset included, on every change to `index.ts`, `rules/`, `presets/`, `shared/` or `tsconfig.build.json`; a failed build waits for the next change.
|
|
215
|
+
- `pnpm consumer:link <consumer>`: after `pnpm build`, replaces the pnpm symlinks of the plugin in every workspace package of `<consumer>` with links to the checkout, so the consumer's own lint runs the checkout's rules; no tracked file of the consumer changes. `pnpm consumer:unlink <consumer>` restores them with `pnpm install --frozen-lockfile`, as does any `pnpm install` in the consumer.
|
|
210
216
|
|
|
211
217
|
A new rule must be registered in `index.ts` and documented in `RULES.md` under a `` ### `rule-name` `` heading; `pnpm build` adds it to the preset.
|
package/RULES.md
CHANGED
|
@@ -1,47 +1,48 @@
|
|
|
1
1
|
# `@doubleloop-io/oxlint-plugin-effect` rules
|
|
2
2
|
|
|
3
|
-
The plugin's
|
|
3
|
+
The plugin's 31 rules. The `Pxx` identifiers come from the origin catalog; the patterns no rule checks are in [`skill/reference/effect-patterns.md`](skill/reference/effect-patterns.md).
|
|
4
4
|
|
|
5
5
|
## Rules index
|
|
6
6
|
|
|
7
|
-
| Rule | Pattern
|
|
8
|
-
| --------------------------------------------------------------------------------------- |
|
|
9
|
-
| [`service-port-shape-only`](#service-port-shape-only) | P01
|
|
10
|
-
| [`adapter-import-boundary`](#adapter-import-boundary) | P02
|
|
11
|
-
| [`run-only-
|
|
12
|
-
| [`no-bare-error-in-effect`](#no-bare-error-in-effect) | P05
|
|
13
|
-
| [`try-object-form`](#try-object-form) | P08
|
|
14
|
-
| [`no-effect-promise`](#no-effect-promise) | P08
|
|
15
|
-
| [`no-parameterless-catch-all`](#no-parameterless-catch-all) | P09
|
|
16
|
-
| [`redacted-secret-names`](#redacted-secret-names) | P13
|
|
17
|
-
| [`prefer-effect-fn`](#prefer-effect-fn) | P17
|
|
18
|
-
| [`untraced-recursion`](#untraced-recursion) | P17
|
|
19
|
-
| [`
|
|
20
|
-
| [`
|
|
21
|
-
| [`
|
|
22
|
-
| [`
|
|
23
|
-
| [`
|
|
24
|
-
| [`
|
|
25
|
-
| [`no-
|
|
26
|
-
| [`no-
|
|
27
|
-
| [`no-
|
|
28
|
-
| [`no-
|
|
29
|
-
| [`
|
|
30
|
-
| [`
|
|
31
|
-
| [`no-manual-discriminant-
|
|
32
|
-
| [`
|
|
33
|
-
| [`
|
|
34
|
-
| [`no-
|
|
35
|
-
| [`
|
|
36
|
-
| [`
|
|
37
|
-
| [`anti-slop-no-
|
|
38
|
-
| [`anti-slop-
|
|
7
|
+
| Rule | Pattern | Severity |
|
|
8
|
+
| --------------------------------------------------------------------------------------- | ------- | -------- |
|
|
9
|
+
| [`service-port-shape-only`](#service-port-shape-only) | P01 | `error` |
|
|
10
|
+
| [`adapter-import-boundary`](#adapter-import-boundary) | P02 | `error` |
|
|
11
|
+
| [`run-only-in-runner`](#run-only-in-runner) | P04 | `error` |
|
|
12
|
+
| [`no-bare-error-in-effect`](#no-bare-error-in-effect) | P05 | `error` |
|
|
13
|
+
| [`try-object-form`](#try-object-form) | P08 | `error` |
|
|
14
|
+
| [`no-effect-promise`](#no-effect-promise) | P08 | `warn` |
|
|
15
|
+
| [`no-parameterless-catch-all`](#no-parameterless-catch-all) | P09 | `error` |
|
|
16
|
+
| [`redacted-secret-names`](#redacted-secret-names) | P13 | `warn` |
|
|
17
|
+
| [`prefer-effect-fn`](#prefer-effect-fn) | P17 | `warn` |
|
|
18
|
+
| [`untraced-recursion`](#untraced-recursion) | P17 | `warn` |
|
|
19
|
+
| [`prefer-effect-vitest`](#prefer-effect-vitest) | P22 | — |
|
|
20
|
+
| [`single-style-per-function`](#single-style-per-function) | P24 | `error` |
|
|
21
|
+
| [`error-handling-in-effect-fn-handler`](#error-handling-in-effect-fn-handler) | P25 | `warn` |
|
|
22
|
+
| [`no-transport-types-in-generator`](#no-transport-types-in-generator) | P26 | `error` |
|
|
23
|
+
| [`all-errors-handled-at-boundary`](#all-errors-handled-at-boundary) | P27 | `warn` |
|
|
24
|
+
| [`provide-named-layer`](#provide-named-layer) | P28 | `error` |
|
|
25
|
+
| [`no-provide-on-definition`](#no-provide-on-definition) | P28 | `error` |
|
|
26
|
+
| [`no-nested-match`](#no-nested-match) | P30 | `warn` |
|
|
27
|
+
| [`no-inline-yield-star`](#no-inline-yield-star) | P32 | `warn` |
|
|
28
|
+
| [`no-effect-return-type`](#no-effect-return-type) | P33 | `warn` |
|
|
29
|
+
| [`no-asymmetric-match-handlers`](#no-asymmetric-match-handlers) | P34 | `warn` |
|
|
30
|
+
| [`exported-schema-identifier`](#exported-schema-identifier) | P35 | — |
|
|
31
|
+
| [`no-manual-discriminant-comparison`](#no-manual-discriminant-comparison) | P36 | — |
|
|
32
|
+
| [`no-manual-discriminant-construction`](#no-manual-discriminant-construction) | P37 | — |
|
|
33
|
+
| [`service-key-matches-name`](#service-key-matches-name) | P38 | — |
|
|
34
|
+
| [`no-inline-schema-codec`](#no-inline-schema-codec) | P39 | — |
|
|
35
|
+
| [`no-yielded-match`](#no-yielded-match) | P40 | — |
|
|
36
|
+
| [`service-built-with-of`](#service-built-with-of) | P41 | — |
|
|
37
|
+
| [`anti-slop-no-service-constructor-imports`](#anti-slop-no-service-constructor-imports) | — | — |
|
|
38
|
+
| [`anti-slop-no-manual-effect-error-tag`](#anti-slop-no-manual-effect-error-tag) | — | — |
|
|
39
|
+
| [`anti-slop-prefer-effect-match`](#anti-slop-prefer-effect-match) | — | — |
|
|
39
40
|
|
|
40
41
|
The `all` preset enables them all at `warn`. Severity is the one each rule was kept at zero noise with on an Effect 4 codebase: a data point about its stability, not a recommendation. `—` means not yet measured.
|
|
41
42
|
|
|
42
43
|
## Common limits
|
|
43
44
|
|
|
44
|
-
- **Syntax only**: no rule uses type information.
|
|
45
|
+
- **Syntax only**: no rule uses type information, and only [`prefer-effect-vitest`](#prefer-effect-vitest) looks past the file, to find whether `@effect/vitest` is installed.
|
|
45
46
|
- **Literal names**: `Effect.gen` is recognized, `import { Effect as E }` or `import * as L from "effect/Layer"` slip through. On code that uses aliases, silence is not compliance.
|
|
46
47
|
- **Effect 4**: the rules also recognize some v3 APIs (`catchAll`, `Effect.Service`, `Context.Tag`), but they are written and tested against v4.
|
|
47
48
|
|
|
@@ -49,8 +50,8 @@ The `all` preset enables them all at `warn`. Severity is the one each rule was k
|
|
|
49
50
|
|
|
50
51
|
When the reason for an exclusion is part of the rule, the rule itself recognizes the files, through an option that **replaces** the defaults (it does not extend them). Globs are matched against the path relative to oxlint's working directory, with `/` on every platform.
|
|
51
52
|
|
|
52
|
-
- **`entryFiles`**, used by [`run-only-
|
|
53
|
-
- **`testFiles`**, used by [`adapter-import-boundary`](#adapter-import-boundary), [`no-effect-promise`](#no-effect-promise), [`provide-named-layer`](#provide-named-layer), [`no-provide-on-definition`](#no-provide-on-definition), [`error-handling-in-effect-fn-handler`](#error-handling-in-effect-fn-handler) and [`anti-slop-no-service-constructor-imports`](#anti-slop-no-service-constructor-imports). Default `**/*.test.*`, `**/*.spec.*`, `**/__tests__/**`, `**/test/**`, `**/tests/**`. A helper directory such as `src/testing/` does not count as tests: it is support code.
|
|
53
|
+
- **`entryFiles`**, used by [`run-only-in-runner`](#run-only-in-runner) and [`adapter-import-boundary`](#adapter-import-boundary). Default `**/main.ts`, `**/cli.ts`, `**/bin/**`. `index.ts` is excluded on purpose: it is usually a barrel, and exempting it would let adapters reach the rest of the application through re-exports.
|
|
54
|
+
- **`testFiles`**, used by [`run-only-in-runner`](#run-only-in-runner), [`adapter-import-boundary`](#adapter-import-boundary), [`no-effect-promise`](#no-effect-promise), [`provide-named-layer`](#provide-named-layer), [`no-provide-on-definition`](#no-provide-on-definition), [`error-handling-in-effect-fn-handler`](#error-handling-in-effect-fn-handler), [`no-bare-error-in-effect`](#no-bare-error-in-effect), [`single-style-per-function`](#single-style-per-function), [`all-errors-handled-at-boundary`](#all-errors-handled-at-boundary) and [`anti-slop-no-service-constructor-imports`](#anti-slop-no-service-constructor-imports). Those rules skip test files, except `no-bare-error-in-effect`, which skips only primitive failures there, and `single-style-per-function`, which treats their helpers like tests; [`prefer-effect-vitest`](#prefer-effect-vitest) checks only them. Default `**/*.test.*`, `**/*.spec.*`, `**/__tests__/**`, `**/test/**`, `**/tests/**`. A helper directory such as `src/testing/` does not count as tests: it is support code.
|
|
54
55
|
|
|
55
56
|
The defaults come from a Node CLI. On another layout (Next.js, a monorepo) they must be reconfigured before reading the numbers, and every rule sharing an option must be reconfigured:
|
|
56
57
|
|
|
@@ -60,7 +61,7 @@ const entryFiles = ["apps/web/src/app/**/route.ts", "apps/web/src/runtime.ts"]
|
|
|
60
61
|
export default defineConfig({
|
|
61
62
|
extends: [dlEffectAll],
|
|
62
63
|
rules: {
|
|
63
|
-
"dl-effect/run-only-
|
|
64
|
+
"dl-effect/run-only-in-runner": ["warn", { entryFiles }],
|
|
64
65
|
"dl-effect/adapter-import-boundary": ["warn", { entryFiles }],
|
|
65
66
|
},
|
|
66
67
|
})
|
|
@@ -199,20 +200,16 @@ const issues = yield * IssueService
|
|
|
199
200
|
|
|
200
201
|
- **Pattern**: — · **Options**: `testFiles` ([File options](#file-options)) · **Fix**: none · **Severity**: —
|
|
201
202
|
|
|
202
|
-
### `run-only-
|
|
203
|
+
### `run-only-in-runner`
|
|
203
204
|
|
|
204
205
|
<sup>[↑ rules index](#rules-index)</sup>
|
|
205
206
|
|
|
206
|
-
**Intent**: Effects run only
|
|
207
|
+
**Intent**: Effects run only in the program's runner, the entry point.
|
|
207
208
|
|
|
208
209
|
```ts
|
|
209
210
|
// no
|
|
210
211
|
export const check = () => Effect.runPromise(pingDb(url))
|
|
211
212
|
|
|
212
|
-
test("…", async () => {
|
|
213
|
-
await Effect.runPromise(authorize({ role: "Manager" }))
|
|
214
|
-
})
|
|
215
|
-
|
|
216
213
|
// yes: in main.ts
|
|
217
214
|
NodeRuntime.runMain(
|
|
218
215
|
run(process.argv.slice(2)).pipe(Effect.provide(NodeServices.layer)) satisfies Effect.Effect<
|
|
@@ -220,18 +217,41 @@ NodeRuntime.runMain(
|
|
|
220
217
|
never
|
|
221
218
|
>,
|
|
222
219
|
)
|
|
223
|
-
|
|
224
|
-
// yes: in a test, fakes as Layers
|
|
225
|
-
it.effect("…", () => authorize({ role: "Manager" }).pipe(Effect.provide(sheetWith(row))))
|
|
226
220
|
```
|
|
227
221
|
|
|
228
222
|
**Trigger**: every reference, not just calls, to `Effect.run*`, `NodeRuntime.runMain`, `BunRuntime.runMain` and `ManagedRuntime.make` outside entry files.
|
|
229
223
|
|
|
230
|
-
**Exemptions and blind spots**: test files
|
|
224
|
+
**Exemptions and blind spots**: test files, checked by [`prefer-effect-vitest`](#prefer-effect-vitest) instead.
|
|
225
|
+
|
|
226
|
+
**Why**: logger, Layers and defect handling are configured in one place.
|
|
227
|
+
|
|
228
|
+
- **Pattern**: P04 · **Options**: `entryFiles`, `testFiles` ([File options](#file-options)) · **Fix**: none · **Severity**: `error`
|
|
229
|
+
|
|
230
|
+
### `prefer-effect-vitest`
|
|
231
|
+
|
|
232
|
+
<sup>[↑ rules index](#rules-index)</sup>
|
|
233
|
+
|
|
234
|
+
**Intent**: tests hand their Effects to `@effect/vitest` instead of running them.
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
// no
|
|
238
|
+
test("…", async () => {
|
|
239
|
+
await Effect.runPromise(authorize({ role: "Manager" }))
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
// yes: fakes as Layers
|
|
243
|
+
import { effect } from "@effect/vitest"
|
|
244
|
+
|
|
245
|
+
effect("…", () => authorize({ role: "Manager" }).pipe(Effect.provide(sheetWith(row))))
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**Trigger**: the same references as [`run-only-in-runner`](#run-only-in-runner), inside test files only.
|
|
249
|
+
|
|
250
|
+
**Exemptions and blind spots**: projects where `@effect/vitest` cannot be found in a `node_modules` from the test file up, since the remedy is out of reach there; scattered runs in their tests go unreported. Every file outside `testFiles`, test helpers such as `src/testing/` included.
|
|
231
251
|
|
|
232
|
-
**Why**:
|
|
252
|
+
**Why**: the test runtime brings `TestClock` and a scope per test, and fixtures provided as Layers replace services where the code reads them.
|
|
233
253
|
|
|
234
|
-
- **Pattern**:
|
|
254
|
+
- **Pattern**: P22 · **Options**: `testFiles` ([File options](#file-options)) · **Fix**: none · **Severity**: —
|
|
235
255
|
|
|
236
256
|
## Errors
|
|
237
257
|
|
|
@@ -259,11 +279,11 @@ Effect.fail(new SheetTabNotFoundError({ spreadsheetId, message: "tab not found"
|
|
|
259
279
|
|
|
260
280
|
**Trigger**: a `throw` directly inside a generator passed to `Effect.gen`/`Effect.fn`; `Effect.fail` with a literal or a template.
|
|
261
281
|
|
|
262
|
-
**Exemptions and blind spots**: a `throw` inside a nested callback. `Effect.fail(new Error(…))` and `class X extends Error`: see `global-error-in-effect-failure` and `extends-native-error` (effecttsgo).
|
|
282
|
+
**Exemptions and blind spots**: `Effect.fail` with a literal in test files, where a synthetic failure feeds code generic over the error; a `throw` in a test generator is still reported. A `throw` inside a nested callback. `Effect.fail(new Error(…))` and `class X extends Error`: see `global-error-in-effect-failure` and `extends-native-error` (effecttsgo).
|
|
263
283
|
|
|
264
284
|
**Why**: the typed error channel discriminates only on tags. A `throw` in the generator becomes a defect, outside the error channel.
|
|
265
285
|
|
|
266
|
-
- **Pattern**: P05 · **Options**:
|
|
286
|
+
- **Pattern**: P05 · **Options**: `testFiles` ([File options](#file-options)) · **Fix**: none · **Severity**: `error`
|
|
267
287
|
|
|
268
288
|
### `try-object-form`
|
|
269
289
|
|
|
@@ -386,7 +406,7 @@ Match.value(payment).pipe(
|
|
|
386
406
|
|
|
387
407
|
**Trigger**: an equality comparison between a discriminant (also `["type"]`) and a string literal, or a `switch` on a discriminant. The discriminants are the names in `discriminants`.
|
|
388
408
|
|
|
389
|
-
**Exemptions and blind spots**: comparisons against a variable rather than a literal. On `_tag`, the handlers covered by [`anti-slop-no-manual-effect-error-tag`](#anti-slop-no-manual-effect-error-tag): the two never fire on the same node. Guard clauses such as `if (x.type === "a") return …` fire too; on `_tag` use `Predicate.isTagged`. An exhaustive ts-pattern match is an equally valid remedy. A name added to `discriminants` also fires on objects outside closed unions (DOM events, external payloads): the list is a choice of the codebase.
|
|
409
|
+
**Exemptions and blind spots**: comparisons against a variable rather than a literal. On `_tag`, the handlers covered by [`anti-slop-no-manual-effect-error-tag`](#anti-slop-no-manual-effect-error-tag): the two never fire on the same node. Guard clauses such as `if (x.type === "a") return …` fire too; on `_tag` use `Predicate.isTagged`. A `_tag` compared with a variant of Effect's own types (`Success`/`Failure` of `Result` and `Exit`, `Left`/`Right`, `Some`/`None`) is reported with their refinement as the remedy, such as `Result.isFailure`: those types cannot gain a variant; a `switch` is reported as usual. An exhaustive ts-pattern match is an equally valid remedy. A name added to `discriminants` also fires on objects outside closed unions (DOM events, external payloads): the list is a choice of the codebase.
|
|
390
410
|
|
|
391
411
|
**Why**: `Match.tagsExhaustive`, `Match.discriminatorsExhaustive` and `$match` fail to compile when a variant is added; a comparison or a `switch` does not.
|
|
392
412
|
|
|
@@ -414,7 +434,7 @@ const event = PaymentStartedEvent.make({ id, payment })
|
|
|
414
434
|
|
|
415
435
|
**Trigger**: an object literal with a discriminant property (also `"type"` or `["type"]`) whose value is a string literal. The discriminants are the names in `discriminants`.
|
|
416
436
|
|
|
417
|
-
**Exemptions and blind spots**: values that are not literals (`"Ready" as const` included). Literals that are patterns or raw input, also nested: the arguments of `Match.when`, `Match.not`, of `.with(…)` on a ts-pattern `match(…)` chain,
|
|
437
|
+
**Exemptions and blind spots**: values that are not literals (`"Ready" as const` included). Literals that are patterns or raw input, also nested: the arguments of `Match.when`, `Match.not`, of `.with(…)` on a ts-pattern `match(…)` chain, the expected value of an `expect(…)` matcher or `expect.*` helper, the input of `Schema.decode*(X)(…)`/`Schema.encode*(X)(…)`, the argument of `JSON.stringify`, and an argument whose parameter is annotated `unknown` in a function declared in the same file (a fake API's raw body). Transport payloads written as literals fire, as does raw data passed to an imported function or through a variable, and a name added to `discriminants` fires on objects outside closed unions: the list is a choice of the codebase. See `schema-struct-with-tag` (effecttsgo) for `Schema.Struct` with a `_tag` field.
|
|
418
438
|
|
|
419
439
|
**Why**: a literal skips the constructor, so it carries none of the class's prototype, equality or validation, and drifts silently when the type changes.
|
|
420
440
|
|
|
@@ -440,7 +460,7 @@ const Session = Schema.Struct({ userId: UserId, accessToken: Schema.Redacted(Sch
|
|
|
440
460
|
|
|
441
461
|
**Trigger**:
|
|
442
462
|
|
|
443
|
-
- a literal name read with `Config.*` that ends with `KEY`, `SECRET`, `TOKEN`, `PASSWORD`, `CREDENTIAL`, `CREDENTIALS` or `_JSON` (service account keys often travel as inline JSON), when it does not go through `Config.
|
|
463
|
+
- a literal name read with `Config.*` that ends with `KEY`, `SECRET`, `TOKEN`, `PASSWORD`, `CREDENTIAL`, `CREDENTIALS` or `_JSON` (service account keys often travel as inline JSON), when it does not go through `Config.Redacted` or `Config.schema(Schema.Redacted…(…), name)`;
|
|
444
464
|
- a field of `Schema.Struct({…})` or `Schema.Class<X>("X")({…})` whose name is a `secretFields` entry or ends with it capitalized (`password` matches `dbPassword`), declared with a `Schema.*` constant such as `Schema.String`, also through `.pipe(…)`, `.check(…)`, `.annotate(…)` or `Schema.optional`/`optionalKey`/`NullOr`/`UndefinedOr`/`NullishOr`, with no `Schema.Redacted…` along the way.
|
|
445
465
|
|
|
446
466
|
**Exemptions and blind spots**: a naming heuristic: secrets with other names slip through, non-secrets with these names must be suppressed. Fields declared with a named schema (`accessToken: AccessToken`) are not inspected; other constructors (`Schema.TaggedClass`, `Schema.TaggedStruct`, …) are not seen. `process.env` read outside `Config`: see `process-env` and `process-env-in-effect` (effecttsgo).
|
|
@@ -583,13 +603,13 @@ const createEvent = Effect.fn("events.create")(function* (request: Request) {
|
|
|
583
603
|
})
|
|
584
604
|
```
|
|
585
605
|
|
|
586
|
-
**Trigger**: `async`, `await` and `.then(` in a function that builds Effects: an Effect generator, or a function using an `Effect.*` member.
|
|
606
|
+
**Trigger**: `async`, `await` and `.then(` in a function that builds Effects: an Effect generator, or a function using an `Effect.*` member. A `test`/`it` callback (`.only`, `.each(…)` and the like included) or a `beforeAll`/`afterAll`/`beforeEach`/`afterEach` hook, and in test files any other function except an Effect generator, is reported once, on the function, when more than one of its `await`s, or any `.then(`, involves Effect: has an `Effect.*` member in its expression, nested callbacks and `Effect.run*` included.
|
|
587
607
|
|
|
588
|
-
**Exemptions and blind spots**: `Effect.run*` alone does not make a function effectful: it is how an async boundary hands over to Effect, and where it may appear is decided by [`run-only-
|
|
608
|
+
**Exemptions and blind spots**: `Effect.run*` alone does not make a function effectful: it is how an async boundary hands over to Effect, and where it may appear is decided by [`run-only-in-runner`](#run-only-in-runner). An `async` function that renders JSX itself, not only in nested callbacks, is a Server Component: how it turns failures into UI is left to the application. A test, hook or test helper may await promise-based fixtures (a container, an identity provider), then one Effect program, and assert on its result. A step that reaches Effect only through a helper (`await run(seed)`) is not counted.
|
|
589
609
|
|
|
590
610
|
**Why**: errors travel on two channels, exceptions and Effect, and neither sees them all.
|
|
591
611
|
|
|
592
|
-
- **Pattern**: P24 · **Options**:
|
|
612
|
+
- **Pattern**: P24 · **Options**: `testFiles` ([File options](#file-options)) · **Fix**: none · **Severity**: `error`
|
|
593
613
|
|
|
594
614
|
### `error-handling-in-effect-fn-handler`
|
|
595
615
|
|
|
@@ -616,9 +636,9 @@ const createEvent = Effect.fn("events.create")(
|
|
|
616
636
|
)
|
|
617
637
|
```
|
|
618
638
|
|
|
619
|
-
**Trigger**: inside a generator passed to `Effect.gen`/`Effect.fn`,
|
|
639
|
+
**Trigger**: inside a generator passed to `Effect.gen`/`Effect.fn`, callbacks passed to `Effect.*` combinators included, calls to `Effect.catch*` (`catch`, `catchAll`, `catchTag`, `catchTags`, `catchIf`, `catchCause`, `catchDefect`, `catchReason`, …), `either`, `result`, `exit`, `match*` and `sandbox`.
|
|
620
640
|
|
|
621
|
-
**Exemptions and blind spots**: pipeables of a nested `Effect.fn`. Test files, where the failure is the outcome under observation (`yield* Effect.result(op)` then `expect`). A `.pipe(Effect.catch…)` appended to an `Effect.gen` escapes; [`prefer-effect-fn`](#prefer-effect-fn) closes that gap.
|
|
641
|
+
**Exemptions and blind spots**: pipeables of a nested `Effect.fn`. Handlers inside the callback of an iteration (`forEach`, `filter`, `reduce`, `partition`, `validate`, `findFirst`, …): they handle each element on its own, and in the pipeables the first failure would stop the rest. Functions the generator only defines, such as an `HttpApiBuilder` handler or a service method: they are programs of their own. Test files, where the failure is the outcome under observation (`yield* Effect.result(op)` then `expect`). A `catch`, `catchTag`, `catchTags`, `catchIf` or `catchFilter` whose handlers only die (`Effect.die` passed directly or as the sole return): it is a selective `orDie`, declaring the case untracked rather than handling it; a die behind a wrapper or a condition is still reported. A `.pipe(Effect.catch…)` appended to an `Effect.gen` escapes; [`prefer-effect-fn`](#prefer-effect-fn) closes that gap.
|
|
622
642
|
|
|
623
643
|
**Why**: translation becomes a single exhaustive point instead of catches scattered along the flow.
|
|
624
644
|
|
|
@@ -653,15 +673,15 @@ const createEvent = Effect.fn("events.create")(
|
|
|
653
673
|
)
|
|
654
674
|
```
|
|
655
675
|
|
|
656
|
-
**Trigger**: `new T(…)`, `T.method(…)` and `T(…)` inside an Effect generator,
|
|
676
|
+
**Trigger**: `new T(…)`, `T.method(…)` and `T(…)` inside an Effect generator, callbacks passed to `Effect.*` combinators included, when `T` is a transport type.
|
|
657
677
|
|
|
658
|
-
**Exemptions and blind spots**: pipeables of `Effect.fn`, where the conversion must live. `return yield* new Unauthenticated(…)` is a declared failure. An early `return` of a domain value that actually signals an error is indistinguishable from a success.
|
|
678
|
+
**Exemptions and blind spots**: pipeables of `Effect.fn`, where the conversion must live. Functions the generator only defines, such as an HTTP handler it registers. `return yield* new Unauthenticated(…)` is a declared failure. An early `return` of a domain value that actually signals an error is indistinguishable from a success.
|
|
659
679
|
|
|
660
680
|
**Why**: an early `return new Response(…)` on the happy path is an error branch disguised as success, and escapes exhaustive translation.
|
|
661
681
|
|
|
662
682
|
- **Pattern**: P26 · **Options**: `transportTypes`, replaces the default `["Response", "NextResponse"]`; add your own transport DTOs · **Fix**: none · **Severity**: `error`
|
|
663
683
|
|
|
664
|
-
### `
|
|
684
|
+
### `all-errors-handled-at-boundary`
|
|
665
685
|
|
|
666
686
|
<sup>[↑ rules index](#rules-index)</sup>
|
|
667
687
|
|
|
@@ -679,13 +699,13 @@ export const POST = (request: Request) =>
|
|
|
679
699
|
const runBoundary = (effect: Effect.Effect<Response, never, never>) => Effect.runPromise(effect)
|
|
680
700
|
```
|
|
681
701
|
|
|
682
|
-
**Trigger**: `Effect.runPromise`, `runSync`, `runFork
|
|
702
|
+
**Trigger**: `Effect.runPromise`, `runSync`, `runFork` and `runCallback` whose argument is not `… satisfies Effect.Effect<A, never>`, a parameter declared with that type, or a program ending in `Effect.result`/`Effect.exit` (`Effect.result(program)`, `program.pipe(…, Effect.result)`).
|
|
683
703
|
|
|
684
|
-
**Exemptions and blind spots**: the `*Exit` variants, which return failures as values. The rule checks the shape; `tsc` gives the guarantee.
|
|
704
|
+
**Exemptions and blind spots**: the `*Exit` variants, which return failures as values; `NodeRuntime.runMain`/`BunRuntime.runMain`, which log the Cause and exit with code 1, the right outcome for a process that cannot start; runners inside an `async` function that renders JSX itself, a Server Component, which hands failures to React rather than to an HTTP response; test files, where a rejected run fails the test with the error. The rule checks the shape; `tsc` gives the guarantee.
|
|
685
705
|
|
|
686
706
|
**Why**: `Effect.runPromise` turns any error left over into a rejected promise, a generic 500. With `never`, a new untranslated error does not compile, and `R = never` forces all Layers to be provided.
|
|
687
707
|
|
|
688
|
-
- **Pattern**: P27 · **Options**:
|
|
708
|
+
- **Pattern**: P27 · **Options**: `testFiles` ([File options](#file-options)) · **Fix**: none · **Severity**: `warn`
|
|
689
709
|
|
|
690
710
|
## Wiring and code shape
|
|
691
711
|
|
|
@@ -727,7 +747,7 @@ const printMissing = Effect.gen(function* () { … }).pipe(Effect.provide(missin
|
|
|
727
747
|
|
|
728
748
|
**Trigger**: `Effect.provide` applied to `Effect.gen(…)` or to `Effect.fn(…)(…)`, in all three forms: with `.pipe`, as an `Effect.fn` pipeable, data-first.
|
|
729
749
|
|
|
730
|
-
**Exemptions and blind spots**: test files. A context captured in an enclosing generator with `const ctx = yield* Effect.context<R>()` (also followed by `.pipe(…)`) and handed back to a method of the Layer being built: the caller chose the adapters when it built the Layer. A context obtained through a helper (`yield* contextWithoutSpan()`) is still reported. `provide` on a named program is allowed; `strict-effect-provide` (effecttsgo) reports it too.
|
|
750
|
+
**Exemptions and blind spots**: test files. A program passed directly to `Effect.run*`, also through `.pipe(…)` or data-first `Effect.*` combinators (`Effect.runPromise(Effect.gen(…).pipe(Effect.provide(layer), Effect.result))`): it is already being dispatched; through any other function it is still reported. A context captured in an enclosing generator with `const ctx = yield* Effect.context<R>()` (also followed by `.pipe(…)`) and handed back to a method of the Layer being built: the caller chose the adapters when it built the Layer. A context obtained through a helper (`yield* contextWithoutSpan()`) is still reported. `provide` on a named program is allowed; `strict-effect-provide` (effecttsgo) reports it too.
|
|
731
751
|
|
|
732
752
|
**Why**: a program that provides its own Layers can no longer be wired to different adapters by its caller.
|
|
733
753
|
|
|
@@ -852,7 +872,7 @@ export const findPrice = (id: PriceId): Effect.Effect<Price, PriceNotFound> =>
|
|
|
852
872
|
|
|
853
873
|
**Exemptions and blind spots**: module-level declarations with `export` (`export const`, `export function`, `export default`), unless `checkExported` is on; methods of an exported object, functions an exported function returns, and `export { f }` lists are still checked. Recursion, and returns of different shapes that need unifying (`Effect.succeed` and `Effect.fail`), recognized from the shape of the `return`s. An `import type { Effect }` used as `Effect<…>` slips through.
|
|
854
874
|
|
|
855
|
-
**Why**: the error and requirement channels collect what comes from everything the function calls; an error or a service added deep down would change every caller's annotation. An exported signature is the module's contract, like a named type: there the annotation makes such a change visible where it is published.
|
|
875
|
+
**Why**: the error and requirement channels collect what comes from everything the function calls; an error or a service added deep down would change every caller's annotation. An exported signature is the module's contract, like a named type: there the annotation makes such a change visible where it is published. To check a returned object against a named type, annotate the callback that builds it (`Effect.map(CurrentSession, (session): OrganizationContext => ({ … }))`) or use `satisfies`.
|
|
856
876
|
|
|
857
877
|
- **Pattern**: P33 · **Options**: `checkExported`, default `false`: also checks exported declarations · **Fix**: suggestion that removes the annotation · **Severity**: `warn`
|
|
858
878
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export { defaultEntryFiles } from "./shared/entry-files.ts";
|
|
2
|
+
export { defaultTestFiles } from "./shared/test-files.ts";
|
|
1
3
|
export declare const pluginName = "dl-effect";
|
|
2
4
|
export declare const pluginPath: string;
|
|
3
5
|
declare const dlEffectPlugin: import("@oxlint/plugins").Plugin;
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { fileURLToPath } from "node:url";
|
|
2
2
|
import { eslintCompatPlugin } from "@oxlint/plugins";
|
|
3
3
|
import { adapterImportBoundaryRule } from "./rules/adapter-import-boundary.js";
|
|
4
|
+
import { allErrorsHandledAtBoundaryRule } from "./rules/all-errors-handled-at-boundary.js";
|
|
4
5
|
import { antiSlopNoManualEffectErrorTagRule } from "./rules/anti-slop-no-manual-effect-error-tag.js";
|
|
5
6
|
import { antiSlopNoServiceConstructorImportsRule } from "./rules/anti-slop-no-service-constructor-imports.js";
|
|
6
7
|
import { antiSlopPreferEffectMatchRule } from "./rules/anti-slop-prefer-effect-match.js";
|
|
7
|
-
import { boundaryEntrypointShapeRule } from "./rules/boundary-entrypoint-shape.js";
|
|
8
8
|
import { errorHandlingInEffectFnHandlerRule } from "./rules/error-handling-in-effect-fn-handler.js";
|
|
9
9
|
import { exportedSchemaIdentifierRule } from "./rules/exported-schema-identifier.js";
|
|
10
10
|
import { noAsymmetricMatchHandlersRule } from "./rules/no-asymmetric-match-handlers.js";
|
|
@@ -21,15 +21,18 @@ import { noProvideOnDefinitionRule } from "./rules/no-provide-on-definition.js";
|
|
|
21
21
|
import { noTransportTypesInGeneratorRule } from "./rules/no-transport-types-in-generator.js";
|
|
22
22
|
import { noYieldedMatchRule } from "./rules/no-yielded-match.js";
|
|
23
23
|
import { preferEffectFnRule } from "./rules/prefer-effect-fn.js";
|
|
24
|
+
import { preferEffectVitestRule } from "./rules/prefer-effect-vitest.js";
|
|
24
25
|
import { provideNamedLayerRule } from "./rules/provide-named-layer.js";
|
|
25
26
|
import { redactedSecretNamesRule } from "./rules/redacted-secret-names.js";
|
|
26
|
-
import {
|
|
27
|
+
import { runOnlyInRunnerRule } from "./rules/run-only-in-runner.js";
|
|
27
28
|
import { serviceBuiltWithOfRule } from "./rules/service-built-with-of.js";
|
|
28
29
|
import { serviceKeyMatchesNameRule } from "./rules/service-key-matches-name.js";
|
|
29
30
|
import { servicePortShapeOnlyRule } from "./rules/service-port-shape-only.js";
|
|
30
31
|
import { singleStylePerFunctionRule } from "./rules/single-style-per-function.js";
|
|
31
32
|
import { tryObjectFormRule } from "./rules/try-object-form.js";
|
|
32
33
|
import { untracedRecursionRule } from "./rules/untraced-recursion.js";
|
|
34
|
+
export { defaultEntryFiles } from "./shared/entry-files.js";
|
|
35
|
+
export { defaultTestFiles } from "./shared/test-files.js";
|
|
33
36
|
export const pluginName = "dl-effect";
|
|
34
37
|
// Resolved at runtime so it points at this file both from the sources and from `dist/`.
|
|
35
38
|
export const pluginPath = fileURLToPath(import.meta.url);
|
|
@@ -40,7 +43,7 @@ const dlEffectPlugin = eslintCompatPlugin({
|
|
|
40
43
|
"anti-slop-no-service-constructor-imports": antiSlopNoServiceConstructorImportsRule,
|
|
41
44
|
"anti-slop-prefer-effect-match": antiSlopPreferEffectMatchRule,
|
|
42
45
|
"adapter-import-boundary": adapterImportBoundaryRule,
|
|
43
|
-
"
|
|
46
|
+
"all-errors-handled-at-boundary": allErrorsHandledAtBoundaryRule,
|
|
44
47
|
"error-handling-in-effect-fn-handler": errorHandlingInEffectFnHandlerRule,
|
|
45
48
|
"exported-schema-identifier": exportedSchemaIdentifierRule,
|
|
46
49
|
"no-asymmetric-match-handlers": noAsymmetricMatchHandlersRule,
|
|
@@ -57,9 +60,10 @@ const dlEffectPlugin = eslintCompatPlugin({
|
|
|
57
60
|
"no-transport-types-in-generator": noTransportTypesInGeneratorRule,
|
|
58
61
|
"no-yielded-match": noYieldedMatchRule,
|
|
59
62
|
"prefer-effect-fn": preferEffectFnRule,
|
|
63
|
+
"prefer-effect-vitest": preferEffectVitestRule,
|
|
60
64
|
"provide-named-layer": provideNamedLayerRule,
|
|
61
65
|
"redacted-secret-names": redactedSecretNamesRule,
|
|
62
|
-
"run-only-
|
|
66
|
+
"run-only-in-runner": runOnlyInRunnerRule,
|
|
63
67
|
"service-built-with-of": serviceBuiltWithOfRule,
|
|
64
68
|
"service-key-matches-name": serviceKeyMatchesNameRule,
|
|
65
69
|
"service-port-shape-only": servicePortShapeOnlyRule,
|
package/dist/presets/all.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"dl-effect/anti-slop-no-service-constructor-imports": "warn",
|
|
8
8
|
"dl-effect/anti-slop-prefer-effect-match": "warn",
|
|
9
9
|
"dl-effect/adapter-import-boundary": "warn",
|
|
10
|
-
"dl-effect/
|
|
10
|
+
"dl-effect/all-errors-handled-at-boundary": "warn",
|
|
11
11
|
"dl-effect/error-handling-in-effect-fn-handler": "warn",
|
|
12
12
|
"dl-effect/exported-schema-identifier": "warn",
|
|
13
13
|
"dl-effect/no-asymmetric-match-handlers": "warn",
|
|
@@ -24,9 +24,10 @@
|
|
|
24
24
|
"dl-effect/no-transport-types-in-generator": "warn",
|
|
25
25
|
"dl-effect/no-yielded-match": "warn",
|
|
26
26
|
"dl-effect/prefer-effect-fn": "warn",
|
|
27
|
+
"dl-effect/prefer-effect-vitest": "warn",
|
|
27
28
|
"dl-effect/provide-named-layer": "warn",
|
|
28
29
|
"dl-effect/redacted-secret-names": "warn",
|
|
29
|
-
"dl-effect/run-only-
|
|
30
|
+
"dl-effect/run-only-in-runner": "warn",
|
|
30
31
|
"dl-effect/service-built-with-of": "warn",
|
|
31
32
|
"dl-effect/service-key-matches-name": "warn",
|
|
32
33
|
"dl-effect/service-port-shape-only": "warn",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const allErrorsHandledAtBoundaryRule: import("@oxlint/plugins").Rule;
|