oxlint-plugin-effect 0.2.0 → 0.2.2

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.
Files changed (258) hide show
  1. package/dist/_virtual/_rolldown/runtime.js +13 -0
  2. package/dist/index.d.ts +4 -0
  3. package/dist/index.js +4 -0
  4. package/dist/plugin.d.ts +5 -0
  5. package/dist/plugin.js +18 -0
  6. package/dist/presets/core.d.ts +43 -0
  7. package/dist/presets/core.js +43 -0
  8. package/dist/presets/effect-native.d.ts +28 -0
  9. package/dist/presets/effect-native.js +28 -0
  10. package/dist/presets/full.d.ts +60 -0
  11. package/dist/presets/full.js +60 -0
  12. package/dist/presets/functional.d.ts +21 -0
  13. package/dist/presets/functional.js +21 -0
  14. package/dist/presets/index.d.ts +14 -0
  15. package/dist/presets/index.js +20 -0
  16. package/dist/presets/strict.d.ts +64 -0
  17. package/dist/presets/strict.js +64 -0
  18. package/dist/presets/v3-strict.d.ts +16 -0
  19. package/dist/presets/v3-strict.js +16 -0
  20. package/dist/presets/v3.d.ts +18 -0
  21. package/dist/presets/v3.js +18 -0
  22. package/dist/rules/_effect-context.d.ts +19 -0
  23. package/dist/rules/_effect-context.js +22 -0
  24. package/dist/rules/index.d.ts +66 -0
  25. package/dist/rules/index.js +124 -0
  26. package/dist/rules/no-arrow-ladder.d.ts +6 -0
  27. package/dist/rules/no-arrow-ladder.js +61 -0
  28. package/dist/rules/no-async-function.d.ts +6 -0
  29. package/dist/rules/no-async-function.js +46 -0
  30. package/dist/rules/no-catch-all-to-map-error.d.ts +6 -0
  31. package/dist/rules/no-catch-all-to-map-error.js +67 -0
  32. package/dist/rules/no-effect-as.d.ts +6 -0
  33. package/dist/rules/no-effect-as.js +16 -0
  34. package/dist/rules/no-effect-async.d.ts +6 -0
  35. package/dist/rules/no-effect-async.js +16 -0
  36. package/dist/rules/no-effect-bind.d.ts +6 -0
  37. package/dist/rules/no-effect-bind.js +16 -0
  38. package/dist/rules/no-effect-do.d.ts +6 -0
  39. package/dist/rules/no-effect-do.js +16 -0
  40. package/dist/rules/no-effect-fn-generator.d.ts +6 -0
  41. package/dist/rules/no-effect-fn-generator.js +30 -0
  42. package/dist/rules/no-effect-gen-adapter.d.ts +6 -0
  43. package/dist/rules/no-effect-gen-adapter.js +30 -0
  44. package/dist/rules/no-effect-map-flatten.d.ts +6 -0
  45. package/dist/rules/no-effect-map-flatten.js +53 -0
  46. package/dist/rules/no-effect-map-void.d.ts +6 -0
  47. package/dist/rules/no-effect-map-void.js +40 -0
  48. package/dist/rules/no-effect-never.d.ts +6 -0
  49. package/dist/rules/no-effect-never.js +16 -0
  50. package/dist/rules/no-effect-orElse-ladder.d.ts +6 -0
  51. package/dist/rules/no-effect-orElse-ladder.js +53 -0
  52. package/dist/rules/no-effect-succeed-string.d.ts +6 -0
  53. package/dist/rules/no-effect-succeed-string.js +30 -0
  54. package/dist/rules/no-effect-succeed-variable.d.ts +6 -0
  55. package/dist/rules/no-effect-succeed-variable.js +30 -0
  56. package/dist/rules/no-effect-succeed-void.d.ts +6 -0
  57. package/dist/rules/no-effect-succeed-void.js +33 -0
  58. package/dist/rules/no-effect-sync-console.d.ts +6 -0
  59. package/dist/rules/no-effect-sync-console.js +63 -0
  60. package/dist/rules/no-effect-sync-wrapper.d.ts +6 -0
  61. package/dist/rules/no-effect-sync-wrapper.js +37 -0
  62. package/dist/rules/no-effect-type-alias.d.ts +6 -0
  63. package/dist/rules/no-effect-type-alias.js +52 -0
  64. package/dist/rules/no-effect-wrapper-alias.d.ts +6 -0
  65. package/dist/rules/no-effect-wrapper-alias.js +69 -0
  66. package/dist/rules/no-extends-native-error.d.ts +6 -0
  67. package/dist/rules/no-extends-native-error.js +41 -0
  68. package/dist/rules/no-flatmap-ladder.d.ts +6 -0
  69. package/dist/rules/no-flatmap-ladder.js +48 -0
  70. package/dist/rules/no-fromnullable-coalesce.d.ts +6 -0
  71. package/dist/rules/no-fromnullable-coalesce.js +36 -0
  72. package/dist/rules/no-global-error-in-catch.d.ts +6 -0
  73. package/dist/rules/no-global-error-in-catch.js +75 -0
  74. package/dist/rules/no-global-error-in-failure.d.ts +6 -0
  75. package/dist/rules/no-global-error-in-failure.js +54 -0
  76. package/dist/rules/no-globals.d.ts +6 -0
  77. package/dist/rules/no-globals.js +164 -0
  78. package/dist/rules/no-if-statement.d.ts +6 -0
  79. package/dist/rules/no-if-statement.js +18 -0
  80. package/dist/rules/no-iife-wrapper.d.ts +6 -0
  81. package/dist/rules/no-iife-wrapper.js +37 -0
  82. package/dist/rules/no-inline-provide.d.ts +6 -0
  83. package/dist/rules/no-inline-provide.js +31 -0
  84. package/dist/rules/no-instanceof-schema.d.ts +6 -0
  85. package/dist/rules/no-instanceof-schema.js +35 -0
  86. package/dist/rules/no-manual-effect-channels.d.ts +6 -0
  87. package/dist/rules/no-manual-effect-channels.js +40 -0
  88. package/dist/rules/no-match-effect-branch.d.ts +6 -0
  89. package/dist/rules/no-match-effect-branch.js +64 -0
  90. package/dist/rules/no-match-void-branch.d.ts +6 -0
  91. package/dist/rules/no-match-void-branch.js +50 -0
  92. package/dist/rules/no-multiple-effect-provide.d.ts +6 -0
  93. package/dist/rules/no-multiple-effect-provide.js +45 -0
  94. package/dist/rules/no-nested-effect-call.d.ts +6 -0
  95. package/dist/rules/no-nested-effect-call.js +97 -0
  96. package/dist/rules/no-nested-effect-gen.d.ts +6 -0
  97. package/dist/rules/no-nested-effect-gen.js +46 -0
  98. package/dist/rules/no-nested-pipe.d.ts +6 -0
  99. package/dist/rules/no-nested-pipe.js +38 -0
  100. package/dist/rules/no-new-error.d.ts +6 -0
  101. package/dist/rules/no-new-error.js +39 -0
  102. package/dist/rules/no-new-promise.d.ts +6 -0
  103. package/dist/rules/no-new-promise.js +38 -0
  104. package/dist/rules/no-node-builtin-import.d.ts +6 -0
  105. package/dist/rules/no-node-builtin-import.js +61 -0
  106. package/dist/rules/no-option-as.d.ts +6 -0
  107. package/dist/rules/no-option-as.js +16 -0
  108. package/dist/rules/no-option-boolean-normalization.d.ts +6 -0
  109. package/dist/rules/no-option-boolean-normalization.js +50 -0
  110. package/dist/rules/no-positional-log-error.d.ts +6 -0
  111. package/dist/rules/no-positional-log-error.js +52 -0
  112. package/dist/rules/no-redundant-schema-tag-identifier.d.ts +6 -0
  113. package/dist/rules/no-redundant-schema-tag-identifier.js +46 -0
  114. package/dist/rules/no-return-in-arrow.d.ts +6 -0
  115. package/dist/rules/no-return-in-arrow.js +41 -0
  116. package/dist/rules/no-return-nullish.d.ts +6 -0
  117. package/dist/rules/no-return-nullish.js +47 -0
  118. package/dist/rules/no-run-in-effect-gen.d.ts +6 -0
  119. package/dist/rules/no-run-in-effect-gen.js +48 -0
  120. package/dist/rules/no-runtime-run-fork.d.ts +6 -0
  121. package/dist/rules/no-runtime-run-fork.js +19 -0
  122. package/dist/rules/no-schema-struct-with-tag.d.ts +6 -0
  123. package/dist/rules/no-schema-struct-with-tag.js +48 -0
  124. package/dist/rules/no-schema-union-of-literals.d.ts +6 -0
  125. package/dist/rules/no-schema-union-of-literals.js +35 -0
  126. package/dist/rules/no-string-sentinel-const.d.ts +6 -0
  127. package/dist/rules/no-string-sentinel-const.js +38 -0
  128. package/dist/rules/no-switch-statement.d.ts +6 -0
  129. package/dist/rules/no-switch-statement.js +16 -0
  130. package/dist/rules/no-ternary.d.ts +6 -0
  131. package/dist/rules/no-ternary.js +30 -0
  132. package/dist/rules/no-throw-statement.d.ts +6 -0
  133. package/dist/rules/no-throw-statement.js +34 -0
  134. package/dist/rules/no-try-catch.d.ts +6 -0
  135. package/dist/rules/no-try-catch.js +34 -0
  136. package/dist/rules/no-unnecessary-arrow-block.d.ts +6 -0
  137. package/dist/rules/no-unnecessary-arrow-block.js +34 -0
  138. package/dist/rules/no-unnecessary-effect-gen.d.ts +6 -0
  139. package/dist/rules/no-unnecessary-effect-gen.js +35 -0
  140. package/dist/rules/no-unnecessary-pipe-chain.d.ts +6 -0
  141. package/dist/rules/no-unnecessary-pipe-chain.js +36 -0
  142. package/dist/rules/no-unnecessary-pipe.d.ts +6 -0
  143. package/dist/rules/no-unnecessary-pipe.js +29 -0
  144. package/dist/vendor/effect-oxlint/AST.d.ts +199 -0
  145. package/dist/vendor/effect-oxlint/AST.js +214 -0
  146. package/dist/vendor/effect-oxlint/Comment.d.ts +55 -0
  147. package/dist/vendor/effect-oxlint/Comment.js +67 -0
  148. package/dist/vendor/effect-oxlint/Diagnostic.d.ts +86 -0
  149. package/dist/vendor/effect-oxlint/Diagnostic.js +115 -0
  150. package/dist/vendor/effect-oxlint/Plugin.d.ts +38 -0
  151. package/dist/vendor/effect-oxlint/Plugin.js +45 -0
  152. package/dist/vendor/effect-oxlint/Rule.d.ts +218 -0
  153. package/dist/vendor/effect-oxlint/Rule.js +309 -0
  154. package/dist/vendor/effect-oxlint/RuleContext.d.ts +77 -0
  155. package/dist/vendor/effect-oxlint/RuleContext.js +74 -0
  156. package/dist/vendor/effect-oxlint/Scope.d.ts +100 -0
  157. package/dist/vendor/effect-oxlint/Scope.js +110 -0
  158. package/dist/vendor/effect-oxlint/SourceCode.d.ts +180 -0
  159. package/dist/vendor/effect-oxlint/SourceCode.js +203 -0
  160. package/dist/vendor/effect-oxlint/Testing.d.ts +510 -0
  161. package/dist/vendor/effect-oxlint/Testing.js +963 -0
  162. package/dist/vendor/effect-oxlint/Token.d.ts +98 -0
  163. package/dist/vendor/effect-oxlint/Token.js +103 -0
  164. package/dist/vendor/effect-oxlint/Visitor.d.ts +166 -0
  165. package/dist/vendor/effect-oxlint/Visitor.js +138 -0
  166. package/dist/vendor/effect-oxlint/index.d.ts +13 -0
  167. package/dist/vendor/effect-oxlint/index.js +12 -0
  168. package/package.json +24 -5
  169. package/.effect-lsp.json +0 -60
  170. package/.oxlintrc.json +0 -33
  171. package/CHANGELOG.md +0 -55
  172. package/scripts/add-rule.ts +0 -87
  173. package/scripts/codegen.ts +0 -108
  174. package/src/index.ts +0 -9
  175. package/src/plugin.ts +0 -16
  176. package/src/presets/core.ts +0 -47
  177. package/src/presets/effect-native.ts +0 -34
  178. package/src/presets/full.ts +0 -70
  179. package/src/presets/functional.ts +0 -18
  180. package/src/presets/index.ts +0 -5
  181. package/src/presets/strict.ts +0 -77
  182. package/src/rules/_effect-context.ts +0 -28
  183. package/src/rules/index.ts +0 -72
  184. package/src/rules/no-arrow-ladder.ts +0 -70
  185. package/src/rules/no-async-function.ts +0 -47
  186. package/src/rules/no-catch-all-to-map-error.ts +0 -90
  187. package/src/rules/no-effect-as.ts +0 -14
  188. package/src/rules/no-effect-async.ts +0 -14
  189. package/src/rules/no-effect-bind.ts +0 -13
  190. package/src/rules/no-effect-do.ts +0 -14
  191. package/src/rules/no-effect-fn-generator.ts +0 -58
  192. package/src/rules/no-effect-gen-adapter.ts +0 -51
  193. package/src/rules/no-effect-map-flatten.ts +0 -82
  194. package/src/rules/no-effect-map-void.ts +0 -80
  195. package/src/rules/no-effect-never.ts +0 -14
  196. package/src/rules/no-effect-orElse-ladder.ts +0 -79
  197. package/src/rules/no-effect-succeed-string.ts +0 -52
  198. package/src/rules/no-effect-succeed-variable.ts +0 -50
  199. package/src/rules/no-effect-succeed-void.ts +0 -63
  200. package/src/rules/no-effect-sync-console.ts +0 -91
  201. package/src/rules/no-effect-sync-wrapper.ts +0 -67
  202. package/src/rules/no-effect-type-alias.ts +0 -71
  203. package/src/rules/no-effect-wrapper-alias.ts +0 -95
  204. package/src/rules/no-extends-native-error.ts +0 -54
  205. package/src/rules/no-flatmap-ladder.ts +0 -77
  206. package/src/rules/no-fromnullable-coalesce.ts +0 -62
  207. package/src/rules/no-global-error-in-catch.ts +0 -104
  208. package/src/rules/no-global-error-in-failure.ts +0 -83
  209. package/src/rules/no-globals.ts +0 -178
  210. package/src/rules/no-if-statement.ts +0 -16
  211. package/src/rules/no-iife-wrapper.ts +0 -51
  212. package/src/rules/no-inline-provide.ts +0 -41
  213. package/src/rules/no-instanceof-schema.ts +0 -42
  214. package/src/rules/no-manual-effect-channels.ts +0 -58
  215. package/src/rules/no-match-effect-branch.ts +0 -82
  216. package/src/rules/no-match-void-branch.ts +0 -72
  217. package/src/rules/no-multiple-effect-provide.ts +0 -75
  218. package/src/rules/no-nested-effect-call.ts +0 -57
  219. package/src/rules/no-nested-effect-gen.ts +0 -43
  220. package/src/rules/no-nested-pipe.ts +0 -73
  221. package/src/rules/no-new-error.ts +0 -53
  222. package/src/rules/no-new-promise.ts +0 -50
  223. package/src/rules/no-node-builtin-import.ts +0 -68
  224. package/src/rules/no-option-as.ts +0 -13
  225. package/src/rules/no-option-boolean-normalization.ts +0 -88
  226. package/src/rules/no-positional-log-error.ts +0 -76
  227. package/src/rules/no-redundant-schema-tag-identifier.ts +0 -62
  228. package/src/rules/no-return-in-arrow.ts +0 -76
  229. package/src/rules/no-return-nullish.ts +0 -80
  230. package/src/rules/no-run-in-effect-gen.ts +0 -66
  231. package/src/rules/no-runtime-run-fork.ts +0 -14
  232. package/src/rules/no-schema-struct-with-tag.ts +0 -64
  233. package/src/rules/no-schema-union-of-literals.ts +0 -48
  234. package/src/rules/no-string-sentinel-const.ts +0 -49
  235. package/src/rules/no-switch-statement.ts +0 -13
  236. package/src/rules/no-ternary.ts +0 -30
  237. package/src/rules/no-throw-statement.ts +0 -39
  238. package/src/rules/no-try-catch.ts +0 -39
  239. package/src/rules/no-unnecessary-arrow-block.ts +0 -51
  240. package/src/rules/no-unnecessary-effect-gen.ts +0 -83
  241. package/src/rules/no-unnecessary-pipe-chain.ts +0 -47
  242. package/src/rules/no-unnecessary-pipe.ts +0 -45
  243. package/src/vendor/effect-oxlint/AST.ts +0 -516
  244. package/src/vendor/effect-oxlint/Comment.ts +0 -86
  245. package/src/vendor/effect-oxlint/Diagnostic.ts +0 -199
  246. package/src/vendor/effect-oxlint/Plugin.ts +0 -71
  247. package/src/vendor/effect-oxlint/Rule.ts +0 -548
  248. package/src/vendor/effect-oxlint/RuleContext.ts +0 -144
  249. package/src/vendor/effect-oxlint/Scope.ts +0 -177
  250. package/src/vendor/effect-oxlint/SourceCode.ts +0 -377
  251. package/src/vendor/effect-oxlint/Testing.ts +0 -1227
  252. package/src/vendor/effect-oxlint/Token.ts +0 -131
  253. package/src/vendor/effect-oxlint/Visitor.ts +0 -287
  254. package/src/vendor/effect-oxlint/index.ts +0 -88
  255. package/tests/ban-rules.test.ts +0 -68
  256. package/tests/context-rules.test.ts +0 -263
  257. package/tests/pattern-rules.test.ts +0 -247
  258. package/tsconfig.json +0 -15
@@ -1,72 +0,0 @@
1
- /**
2
- * Ban `Match.when(true/false, () => Effect.void)` and `Match.orElse(() => Effect.void)`.
3
- *
4
- * Remove the no-op branch or restructure.
5
- *
6
- * Source: biome-effect-linting-rules/no-match-void-branch
7
- */
8
- import type { ESTree } from "@oxlint/plugins"
9
- import { AST, Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
10
- import * as Effect from "effect/Effect"
11
-
12
- const isEffectVoid = (node: unknown): boolean => {
13
- if (node == null || typeof node !== "object") return false
14
- const n = node as Record<string, unknown>
15
- if (n["type"] !== "MemberExpression") return false
16
- const obj = n["object"] as Record<string, unknown> | null
17
- const prop = n["property"] as Record<string, unknown> | null
18
- return (
19
- obj != null && obj["type"] === "Identifier" && obj["name"] === "Effect" &&
20
- prop != null && prop["type"] === "Identifier" && prop["name"] === "void"
21
- )
22
- }
23
-
24
- const isVoidCallback = (node: unknown): boolean => {
25
- if (node == null || typeof node !== "object") return false
26
- const n = node as Record<string, unknown>
27
- if (n["type"] !== "ArrowFunctionExpression" && n["type"] !== "FunctionExpression") return false
28
- if (!("body" in n)) return false
29
- return isEffectVoid(n["body"])
30
- }
31
-
32
- export const noMatchVoidBranch = Rule.define({
33
- name: "no-match-void-branch",
34
- meta: Rule.meta({
35
- type: "suggestion",
36
- description: "Avoid no-op Match branches returning Effect.void. Remove the branch or restructure.",
37
- }),
38
- create: function* () {
39
- const ctx = yield* RuleContext
40
- return {
41
- CallExpression: (node) => {
42
- const ce = node as ESTree.CallExpression
43
- const isMatchWhen = AST.isCallOf(ce, "Match", "when")
44
- const isMatchOrElse = AST.isCallOf(ce, "Match", "orElse")
45
-
46
- if (!isMatchWhen && !isMatchOrElse) return Effect.void
47
-
48
- if (!("arguments" in node) || !Array.isArray(node.arguments)) return Effect.void
49
-
50
- if (isMatchOrElse && node.arguments.length >= 1 && isVoidCallback(node.arguments[0])) {
51
- return ctx.report(
52
- Diagnostic.make({
53
- node,
54
- message: "Match.orElse returning Effect.void is a no-op. Remove the branch or restructure.",
55
- }),
56
- )
57
- }
58
-
59
- if (isMatchWhen && node.arguments.length >= 2 && isVoidCallback(node.arguments[1])) {
60
- return ctx.report(
61
- Diagnostic.make({
62
- node,
63
- message: "Match.when branch returning Effect.void is a no-op. Remove the branch or restructure.",
64
- }),
65
- )
66
- }
67
-
68
- return Effect.void
69
- },
70
- }
71
- },
72
- })
@@ -1,75 +0,0 @@
1
- /**
2
- * Ban consecutive `Effect.provide()` calls — merge into one.
3
- *
4
- * Detects: `effect.pipe(Effect.provide(A), Effect.provide(B))`
5
- * Suggests: `effect.pipe(Effect.provide(Layer.mergeAll(A, B)))`
6
- *
7
- * This is an AST heuristic — it catches the common patterns without
8
- * needing the type checker to confirm they're actually Layer values.
9
- *
10
- * Source: language-service/multipleEffectProvide
11
- */
12
- import type { ESTree } from "@oxlint/plugins"
13
- import { AST, Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
14
- import * as Effect from "effect/Effect"
15
- import * as Option from "effect/Option"
16
-
17
- const isEffectProvide = (node: ESTree.Node): boolean => {
18
- if (node.type !== "CallExpression") return false
19
- return Option.isSome(AST.matchCallOf(node as ESTree.CallExpression, "Effect", "provide"))
20
- }
21
-
22
- const countProvides = (args: ReadonlyArray<ESTree.Node>): number => {
23
- let count = 0
24
- for (const arg of args) {
25
- if (isEffectProvide(arg as ESTree.Node)) count++
26
- }
27
- return count
28
- }
29
-
30
- export const noMultipleEffectProvide = Rule.define({
31
- name: "no-multiple-effect-provide",
32
- meta: Rule.meta({
33
- type: "suggestion",
34
- description:
35
- "Avoid consecutive Effect.provide() calls. Merge into Effect.provide(Layer.mergeAll(...)).",
36
- }),
37
- create: function* () {
38
- const ctx = yield* RuleContext
39
- return {
40
- CallExpression: (node) => {
41
- const call = node as ESTree.CallExpression
42
- const args = call.arguments
43
- if (args.length < 3) return Effect.void
44
-
45
- // Check bare pipe() or .pipe() with multiple Effect.provide args
46
- const calleeName = Option.getOrUndefined(AST.calleeName(call))
47
- const callee = call.callee
48
-
49
- const isPipe =
50
- calleeName === "pipe" ||
51
- (callee.type === "MemberExpression" &&
52
- "property" in callee &&
53
- (callee as unknown as Record<string, unknown>)["computed"] !== true &&
54
- (() => {
55
- const prop = (callee as unknown as Record<string, unknown>)["property"]
56
- return prop != null && typeof prop === "object" && "name" in prop && prop.name === "pipe"
57
- })())
58
-
59
- if (!isPipe) return Effect.void
60
-
61
- if (countProvides(args as ReadonlyArray<ESTree.Node>) >= 2) {
62
- return ctx.report(
63
- Diagnostic.make({
64
- node,
65
- message:
66
- "Multiple Effect.provide() in pipe. Merge into Effect.provide(Layer.mergeAll(...)).",
67
- }),
68
- )
69
- }
70
-
71
- return Effect.void
72
- },
73
- }
74
- },
75
- })
@@ -1,57 +0,0 @@
1
- /**
2
- * Ban nested Effect.* calls — `Effect.outer(Effect.inner(...))`.
3
- *
4
- * Build values first, then one flat pipeline.
5
- *
6
- * Sources: biome-effect-linting-rules/no-nested-effect-call, no-effect-call-in-effect-arg, no-call-tower, no-effect-ladder
7
- */
8
- import type { ESTree } from "@oxlint/plugins"
9
- import { Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
10
- import * as Effect from "effect/Effect"
11
-
12
- const isEffectCall = (node: unknown): boolean => {
13
- if (node == null || typeof node !== "object") return false
14
- const n = node as Record<string, unknown>
15
- if (n["type"] !== "CallExpression") return false
16
- const callee = n["callee"]
17
- if (callee == null || typeof callee !== "object") return false
18
- const c = callee as Record<string, unknown>
19
- if (c["type"] !== "MemberExpression") return false
20
- const obj = c["object"]
21
- if (obj == null || typeof obj !== "object") return false
22
- const o = obj as Record<string, unknown>
23
- return o["type"] === "Identifier" && o["name"] === "Effect"
24
- }
25
-
26
- export const noNestedEffectCall = Rule.define({
27
- name: "no-nested-effect-call",
28
- meta: Rule.meta({
29
- type: "suggestion",
30
- description:
31
- "Avoid nesting Effect.* calls. Build values first, then use a flat pipeline.",
32
- }),
33
- create: function* () {
34
- const ctx = yield* RuleContext
35
- return {
36
- CallExpression: (node) => {
37
- if (!isEffectCall(node)) return Effect.void
38
-
39
- const ce = node as ESTree.CallExpression
40
- if (!("arguments" in ce) || !Array.isArray(ce.arguments)) return Effect.void
41
-
42
- for (const arg of ce.arguments) {
43
- if (isEffectCall(arg)) {
44
- return ctx.report(
45
- Diagnostic.make({
46
- node,
47
- message:
48
- "Nested Effect.* call detected. Build values first, then use a flat pipeline.",
49
- }),
50
- )
51
- }
52
- }
53
- return Effect.void
54
- },
55
- }
56
- },
57
- })
@@ -1,43 +0,0 @@
1
- /**
2
- * Ban nested `Effect.gen` calls.
3
- *
4
- * An `Effect.gen` inside another `Effect.gen` should be flattened.
5
- *
6
- * Sources: biome-effect-linting-rules/no-nested-effect-gen, language-service/nestedEffectGenYield
7
- */
8
- import type { ESTree } from "@oxlint/plugins"
9
- import { AST, Diagnostic, Rule, Visitor, RuleContext } from "../vendor/effect-oxlint/index.js"
10
- import * as Effect from "effect/Effect"
11
- import * as Ref from "effect/Ref"
12
-
13
- export const noNestedEffectGen = Rule.define({
14
- name: "no-nested-effect-gen",
15
- meta: Rule.meta({
16
- type: "suggestion",
17
- description:
18
- "Avoid nested Effect.gen. Flatten to a single Effect.gen per method.",
19
- }),
20
- create: function* () {
21
- const ctx = yield* RuleContext
22
- const depth = yield* Ref.make(0)
23
-
24
- return Visitor.merge(
25
- Visitor.tracked("CallExpression", (node) => AST.isCallOf(node, "Effect", "gen"), depth),
26
- {
27
- CallExpression: (node) =>
28
- Effect.flatMap(Ref.get(depth), (d) => {
29
- if (d > 1 && AST.isCallOf(node as ESTree.CallExpression, "Effect", "gen")) {
30
- return ctx.report(
31
- Diagnostic.make({
32
- node,
33
- message:
34
- "Nested Effect.gen detected. Flatten to a single Effect.gen.",
35
- }),
36
- )
37
- }
38
- return Effect.void
39
- }),
40
- },
41
- )
42
- },
43
- })
@@ -1,73 +0,0 @@
1
- /**
2
- * Ban nested `pipe()` calls — `pipe(pipe(...), ...)` or `.pipe(...).pipe(...)`.
3
- *
4
- * Flatten into a single pipeline.
5
- *
6
- * Sources: biome-effect-linting-rules/no-pipe-ladder, language-service/unnecessaryPipeChain
7
- */
8
- import type { ESTree } from "@oxlint/plugins"
9
- import { Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
10
- import * as Effect from "effect/Effect"
11
-
12
- const isPipeCall = (node: ESTree.Node): boolean => {
13
- if (node.type === "CallExpression" && "callee" in node) {
14
- const callee = (node as unknown as Record<string, unknown>)["callee"]
15
- if (callee != null && typeof callee === "object" && "type" in callee) {
16
- // pipe(...)
17
- if (callee.type === "Identifier" && "name" in callee && callee.name === "pipe") {
18
- return true
19
- }
20
- // x.pipe(...)
21
- if (
22
- callee.type === "MemberExpression" &&
23
- "property" in callee &&
24
- callee.property != null &&
25
- typeof callee.property === "object" &&
26
- "type" in callee.property &&
27
- callee.property.type === "Identifier" &&
28
- "name" in callee.property &&
29
- callee.property.name === "pipe"
30
- ) {
31
- return true
32
- }
33
- }
34
- }
35
- return false
36
- }
37
-
38
- export const noNestedPipe = Rule.define({
39
- name: "no-nested-pipe",
40
- meta: Rule.meta({
41
- type: "suggestion",
42
- description: "Avoid nested pipe() calls. Flatten into a single pipeline.",
43
- }),
44
- create: function* () {
45
- const ctx = yield* RuleContext
46
- return {
47
- CallExpression: (node) => {
48
- if (!isPipeCall(node)) return Effect.void
49
-
50
- // Check if any argument is itself a pipe call
51
- if ("arguments" in node && Array.isArray(node.arguments)) {
52
- for (const arg of node.arguments) {
53
- if (
54
- arg != null &&
55
- typeof arg === "object" &&
56
- "type" in arg &&
57
- isPipeCall(arg as unknown as ESTree.Node)
58
- ) {
59
- return ctx.report(
60
- Diagnostic.make({
61
- node: arg,
62
- message:
63
- "Nested pipe() detected. Flatten into a single pipeline.",
64
- }),
65
- )
66
- }
67
- }
68
- }
69
- return Effect.void
70
- },
71
- }
72
- },
73
- })
@@ -1,53 +0,0 @@
1
- /**
2
- * Ban `new Error(...)` and native error constructors. Context-aware messaging.
3
- *
4
- * Inside Effect.gen/fn: "Use yield* new MyError() (yieldable TaggedErrorClass)"
5
- * Outside: "Define class MyError extends Schema.TaggedErrorClass..."
6
- *
7
- * Source: language-service/globalErrorInEffectFailure, extendsNativeError
8
- */
9
- import type { ESTree } from "@oxlint/plugins"
10
- import { Diagnostic, Rule, Visitor, RuleContext } from "../vendor/effect-oxlint/index.js"
11
- import * as Effect from "effect/Effect"
12
- import * as Ref from "effect/Ref"
13
-
14
- import { makeEffectContextTracker } from "./_effect-context.js"
15
-
16
- const nativeErrors = new Set([
17
- "Error", "TypeError", "RangeError", "ReferenceError", "SyntaxError",
18
- "URIError", "EvalError",
19
- ])
20
-
21
- export const noNewError = Rule.define({
22
- name: "no-new-error",
23
- meta: Rule.meta({
24
- type: "suggestion",
25
- description: "Avoid native Error constructors. Use Schema.TaggedErrorClass.",
26
- }),
27
- create: function* () {
28
- const ctx = yield* RuleContext
29
- const [depth, tracker] = yield* makeEffectContextTracker
30
-
31
- return Visitor.merge(tracker, {
32
- NewExpression: (node) =>
33
- Effect.flatMap(Ref.get(depth), (d) => {
34
- const callee = (node as unknown as Record<string, unknown>)["callee"] as ESTree.Node
35
- if (
36
- callee.type === "Identifier" &&
37
- "name" in callee &&
38
- nativeErrors.has(callee.name as string)
39
- ) {
40
- return ctx.report(
41
- Diagnostic.make({
42
- node,
43
- message: d > 0
44
- ? "Avoid native Error inside Effect.gen/fn. Define: class MyError extends Schema.TaggedErrorClass<MyError>()(\"MyError\", { ... }) {} — then yield* new MyError()."
45
- : "Avoid native Error constructors. Define: class MyError extends Schema.TaggedErrorClass<MyError>()(\"MyError\", { message: Schema.String }) {}",
46
- }),
47
- )
48
- }
49
- return Effect.void
50
- }),
51
- })
52
- },
53
- })
@@ -1,50 +0,0 @@
1
- /**
2
- * Ban `new Promise()`. Context-aware messaging.
3
- *
4
- * Inside Effect.gen/fn: "Use Effect.tryPromise or Effect.async"
5
- * Outside: "Use Effect.tryPromise or wrap with Effect.fn"
6
- *
7
- * Source: language-service/newPromise
8
- */
9
- import { Diagnostic, Rule, Visitor, RuleContext } from "../vendor/effect-oxlint/index.js"
10
- import * as Effect from "effect/Effect"
11
- import * as Ref from "effect/Ref"
12
-
13
- import { makeEffectContextTracker } from "./_effect-context.js"
14
-
15
- export const noNewPromise = Rule.define({
16
- name: "no-new-promise",
17
- meta: Rule.meta({
18
- type: "suggestion",
19
- description: "Avoid new Promise(). Use Effect.async or Effect.tryPromise.",
20
- }),
21
- create: function* () {
22
- const ctx = yield* RuleContext
23
- const [depth, tracker] = yield* makeEffectContextTracker
24
-
25
- return Visitor.merge(tracker, {
26
- NewExpression: (node) =>
27
- Effect.flatMap(Ref.get(depth), (d) => {
28
- const callee = (node as unknown as Record<string, unknown>)["callee"]
29
- if (
30
- callee != null &&
31
- typeof callee === "object" &&
32
- "type" in callee &&
33
- callee.type === "Identifier" &&
34
- "name" in callee &&
35
- callee.name === "Promise"
36
- ) {
37
- return ctx.report(
38
- Diagnostic.make({
39
- node,
40
- message: d > 0
41
- ? "Avoid new Promise() inside Effect.gen/fn. Use Effect.async for callback APIs, Effect.tryPromise for existing promises, or Effect.promise for infallible promises."
42
- : "Avoid new Promise(). Use Effect.async for callback APIs, Effect.tryPromise for existing promises — wrap this function with Effect.fn.",
43
- }),
44
- )
45
- }
46
- return Effect.void
47
- }),
48
- })
49
- },
50
- })
@@ -1,68 +0,0 @@
1
- /**
2
- * Ban Node.js builtin imports.
3
- *
4
- * Use Effect platform services: FileSystem, Path, HttpClient, Command.
5
- *
6
- * Source: language-service/nodeBuiltinImport
7
- */
8
- import { AST, Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
9
- import * as Effect from "effect/Effect"
10
- import * as Option from "effect/Option"
11
-
12
- const hints: Record<string, string> = {
13
- fs: "Use FileSystem from 'effect'",
14
- path: "Use Path from 'effect'",
15
- http: "Use HttpClient from 'effect/unstable/http'",
16
- https: "Use HttpClient from 'effect/unstable/http'",
17
- child_process: "Use Command from 'effect'",
18
- crypto: "Use a service wrapper",
19
- os: "Use PlatformInfo from 'effect'",
20
- net: "Use HttpServer from 'effect/unstable/http'",
21
- dns: "Use a service wrapper",
22
- tls: "Use a service wrapper",
23
- dgram: "Use a service wrapper",
24
- stream: "Use Stream from 'effect'",
25
- zlib: "Use a service wrapper",
26
- readline: "Use Terminal from 'effect'",
27
- cluster: "Use a service wrapper",
28
- worker_threads: "Use a service wrapper",
29
- }
30
-
31
- const nodeBuiltins = new Set(Object.keys(hints))
32
-
33
- const resolveModule = (source: string): string | undefined => {
34
- const name = source.startsWith("node:") ? source.slice(5) : source
35
- const base = name.split("/")[0]
36
- return base !== undefined && nodeBuiltins.has(base) ? base : undefined
37
- }
38
-
39
- export const noNodeBuiltinImport = Rule.define({
40
- name: "no-node-builtin-import",
41
- meta: Rule.meta({
42
- type: "suggestion",
43
- description:
44
- "Avoid Node.js builtin imports. Use Effect platform services.",
45
- }),
46
- create: function* () {
47
- const ctx = yield* RuleContext
48
- return {
49
- ImportDeclaration: (node) => {
50
- const source = Option.getOrUndefined(
51
- AST.narrow(node, "ImportDeclaration").pipe(Option.map(AST.importSource)),
52
- )
53
- if (source === undefined) return Effect.void
54
-
55
- const mod = resolveModule(source)
56
- if (mod === undefined) return Effect.void
57
-
58
- const hint = hints[mod] ?? "Use an Effect service wrapper"
59
- return ctx.report(
60
- Diagnostic.make({
61
- node,
62
- message: `Avoid importing '${source}'. ${hint}.`,
63
- }),
64
- )
65
- },
66
- }
67
- },
68
- })
@@ -1,13 +0,0 @@
1
- /**
2
- * Ban `Option.as`.
3
- *
4
- * Use `Option.map` or `Option.match` with explicit value return.
5
- *
6
- * Source: biome-effect-linting-rules/no-option-as
7
- */
8
- import { Rule } from "../vendor/effect-oxlint/index.js"
9
-
10
- export const noOptionAs = Rule.banMember("Option", "as", {
11
- message: "Avoid Option.as. Use Option.map or Option.match instead.",
12
- meta: { type: "suggestion" },
13
- })
@@ -1,88 +0,0 @@
1
- /**
2
- * Ban `Option.match(x, { onSome: (v) => v === true, onNone: () => false })`.
3
- *
4
- * Normalize at the schema boundary instead.
5
- *
6
- * Source: biome-effect-linting-rules/no-option-boolean-normalization
7
- */
8
- import type { ESTree } from "@oxlint/plugins"
9
- import { AST, Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
10
- import * as Effect from "effect/Effect"
11
-
12
- export const noOptionBooleanNormalization = Rule.define({
13
- name: "no-option-boolean-normalization",
14
- meta: Rule.meta({
15
- type: "suggestion",
16
- description:
17
- "Avoid Option.match for boolean normalization. Normalize at schema boundary.",
18
- }),
19
- create: function* () {
20
- const ctx = yield* RuleContext
21
- return {
22
- CallExpression: (node) => {
23
- if (!AST.isCallOf(node as ESTree.CallExpression, "Option", "match")) return Effect.void
24
-
25
- if (!("arguments" in node) || !Array.isArray(node.arguments) || node.arguments.length < 2)
26
- return Effect.void
27
-
28
- const opts = node.arguments[1]
29
- if (opts == null || typeof opts !== "object" || !("type" in opts)) return Effect.void
30
- if (opts.type !== "ObjectExpression" || !("properties" in opts) || !Array.isArray(opts.properties))
31
- return Effect.void
32
-
33
- let hasTripleEqualTrue = false
34
- let hasReturnFalse = false
35
-
36
- for (const prop of opts.properties) {
37
- if (prop == null || typeof prop !== "object" || !("type" in prop) || prop.type !== "Property")
38
- continue
39
- const key = (prop as unknown as Record<string, unknown>)["key"] as unknown as Record<string, unknown> | null
40
- const value = (prop as unknown as Record<string, unknown>)["value"] as unknown as Record<string, unknown> | null
41
-
42
- if (key == null || value == null) continue
43
-
44
- // onSome: (v) => v === true
45
- if (
46
- key["type"] === "Identifier" && key["name"] === "onSome" &&
47
- value["type"] === "ArrowFunctionExpression" && "body" in value
48
- ) {
49
- const body = value["body"] as unknown as Record<string, unknown> | null
50
- if (
51
- body != null &&
52
- body["type"] === "BinaryExpression" &&
53
- body["operator"] === "===" &&
54
- "right" in body
55
- ) {
56
- const right = body["right"] as unknown as Record<string, unknown> | null
57
- if (right != null && right["type"] === "Literal" && right["value"] === true) {
58
- hasTripleEqualTrue = true
59
- }
60
- }
61
- }
62
-
63
- // onNone: () => false
64
- if (
65
- key["type"] === "Identifier" && key["name"] === "onNone" &&
66
- value["type"] === "ArrowFunctionExpression" && "body" in value
67
- ) {
68
- const body = value["body"] as unknown as Record<string, unknown> | null
69
- if (body != null && body["type"] === "Literal" && body["value"] === false) {
70
- hasReturnFalse = true
71
- }
72
- }
73
- }
74
-
75
- if (hasTripleEqualTrue && hasReturnFalse) {
76
- return ctx.report(
77
- Diagnostic.make({
78
- node,
79
- message:
80
- "Avoid Option.match for boolean normalization. Normalize at schema boundary instead.",
81
- }),
82
- )
83
- }
84
- return Effect.void
85
- },
86
- }
87
- },
88
- })
@@ -1,76 +0,0 @@
1
- /**
2
- * Ban positional error/cause arguments in Effect.log* calls.
3
- *
4
- * `Effect.logWarning("message", error)` loses error structure.
5
- * Use `Effect.logWarning("message").pipe(Effect.annotateLogs({ error }))` instead,
6
- * or pass cause via `Effect.logWarning("message", Cause.fail(error))`.
7
- *
8
- * Source: agent session analysis — 57 occurrences across 6 projects
9
- */
10
- import type { ESTree } from "@oxlint/plugins"
11
- import { AST, Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
12
- import * as Effect from "effect/Effect"
13
- import * as Option from "effect/Option"
14
-
15
- const logMethods = new Set([
16
- "log",
17
- "logDebug",
18
- "logInfo",
19
- "logWarning",
20
- "logError",
21
- "logFatal",
22
- "logTrace",
23
- ])
24
-
25
- export const noPositionalLogError = Rule.define({
26
- name: "no-positional-log-error",
27
- meta: Rule.meta({
28
- type: "suggestion",
29
- description:
30
- "Avoid passing errors as positional arguments to Effect.log*. Use Effect.annotateLogs instead.",
31
- }),
32
- create: function* () {
33
- const ctx = yield* RuleContext
34
- return {
35
- CallExpression: (node) => {
36
- const call = node as ESTree.CallExpression
37
- const names = Option.getOrUndefined(
38
- AST.memberNames(call.callee as ESTree.MemberExpression),
39
- )
40
- if (names === undefined) return Effect.void
41
- const [obj, prop] = names
42
- if (obj !== "Effect") return Effect.void
43
- if (!logMethods.has(prop)) return Effect.void
44
-
45
- // Effect.log* with more than 1 argument — the second arg is likely
46
- // an error/cause being passed positionally
47
- const args = call.arguments
48
- if (args.length < 2) return Effect.void
49
-
50
- // The first arg should be a string message. If second arg exists,
51
- // it's likely an error being passed positionally.
52
- const secondArg = args[1]
53
- if (secondArg === undefined) return Effect.void
54
-
55
- // Allow Cause.fail/Cause.die as second arg (that's the proper API)
56
- if (secondArg.type === "CallExpression") {
57
- const secondNames = Option.getOrUndefined(
58
- AST.memberNames((secondArg as ESTree.CallExpression).callee as ESTree.MemberExpression),
59
- )
60
- if (secondNames !== undefined) {
61
- const [sObj] = secondNames
62
- if (sObj === "Cause") return Effect.void
63
- }
64
- }
65
-
66
- return ctx.report(
67
- Diagnostic.make({
68
- node: secondArg,
69
- message:
70
- `Avoid passing errors as positional args to Effect.${prop}. Use .pipe(Effect.annotateLogs({ error: String(err) })) or pass a Cause.`,
71
- }),
72
- )
73
- },
74
- }
75
- },
76
- })