oxlint-plugin-effect 0.1.0 → 0.2.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.
Files changed (256) 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 +13 -0
  15. package/dist/presets/index.js +18 -0
  16. package/dist/presets/strict.d.ts +69 -0
  17. package/dist/presets/strict.js +69 -0
  18. package/dist/presets/v3.d.ts +15 -0
  19. package/dist/presets/v3.js +15 -0
  20. package/dist/rules/_effect-context.d.ts +19 -0
  21. package/dist/rules/_effect-context.js +22 -0
  22. package/dist/rules/index.d.ts +66 -0
  23. package/dist/rules/index.js +124 -0
  24. package/dist/rules/no-arrow-ladder.d.ts +6 -0
  25. package/dist/rules/no-arrow-ladder.js +61 -0
  26. package/dist/rules/no-async-function.d.ts +6 -0
  27. package/dist/rules/no-async-function.js +46 -0
  28. package/dist/rules/no-catch-all-to-map-error.d.ts +6 -0
  29. package/dist/rules/no-catch-all-to-map-error.js +67 -0
  30. package/dist/rules/no-effect-as.d.ts +6 -0
  31. package/dist/rules/no-effect-as.js +16 -0
  32. package/dist/rules/no-effect-async.d.ts +6 -0
  33. package/dist/rules/no-effect-async.js +16 -0
  34. package/dist/rules/no-effect-bind.d.ts +6 -0
  35. package/dist/rules/no-effect-bind.js +16 -0
  36. package/dist/rules/no-effect-do.d.ts +6 -0
  37. package/dist/rules/no-effect-do.js +16 -0
  38. package/dist/rules/no-effect-fn-generator.d.ts +6 -0
  39. package/dist/rules/no-effect-fn-generator.js +30 -0
  40. package/dist/rules/no-effect-gen-adapter.d.ts +6 -0
  41. package/dist/rules/no-effect-gen-adapter.js +30 -0
  42. package/dist/rules/no-effect-map-flatten.d.ts +6 -0
  43. package/dist/rules/no-effect-map-flatten.js +53 -0
  44. package/dist/rules/no-effect-map-void.d.ts +6 -0
  45. package/dist/rules/no-effect-map-void.js +40 -0
  46. package/dist/rules/no-effect-never.d.ts +6 -0
  47. package/dist/rules/no-effect-never.js +16 -0
  48. package/dist/rules/no-effect-orElse-ladder.d.ts +6 -0
  49. package/dist/rules/no-effect-orElse-ladder.js +53 -0
  50. package/dist/rules/no-effect-succeed-string.d.ts +6 -0
  51. package/dist/rules/no-effect-succeed-string.js +30 -0
  52. package/dist/rules/no-effect-succeed-variable.d.ts +6 -0
  53. package/dist/rules/no-effect-succeed-variable.js +30 -0
  54. package/dist/rules/no-effect-succeed-void.d.ts +6 -0
  55. package/dist/rules/no-effect-succeed-void.js +33 -0
  56. package/dist/rules/no-effect-sync-console.d.ts +6 -0
  57. package/dist/rules/no-effect-sync-console.js +63 -0
  58. package/dist/rules/no-effect-sync-wrapper.d.ts +6 -0
  59. package/dist/rules/no-effect-sync-wrapper.js +37 -0
  60. package/dist/rules/no-effect-type-alias.d.ts +6 -0
  61. package/dist/rules/no-effect-type-alias.js +52 -0
  62. package/dist/rules/no-effect-wrapper-alias.d.ts +6 -0
  63. package/dist/rules/no-effect-wrapper-alias.js +69 -0
  64. package/dist/rules/no-extends-native-error.d.ts +6 -0
  65. package/dist/rules/no-extends-native-error.js +41 -0
  66. package/dist/rules/no-flatmap-ladder.d.ts +6 -0
  67. package/dist/rules/no-flatmap-ladder.js +48 -0
  68. package/dist/rules/no-fromnullable-coalesce.d.ts +6 -0
  69. package/dist/rules/no-fromnullable-coalesce.js +36 -0
  70. package/dist/rules/no-global-error-in-catch.d.ts +6 -0
  71. package/dist/rules/no-global-error-in-catch.js +75 -0
  72. package/dist/rules/no-global-error-in-failure.d.ts +6 -0
  73. package/dist/rules/no-global-error-in-failure.js +54 -0
  74. package/dist/rules/no-globals.d.ts +6 -0
  75. package/dist/rules/no-globals.js +164 -0
  76. package/dist/rules/no-if-statement.d.ts +6 -0
  77. package/dist/rules/no-if-statement.js +18 -0
  78. package/dist/rules/no-iife-wrapper.d.ts +6 -0
  79. package/dist/rules/no-iife-wrapper.js +37 -0
  80. package/dist/rules/no-inline-provide.d.ts +6 -0
  81. package/dist/rules/no-inline-provide.js +31 -0
  82. package/dist/rules/no-instanceof-schema.d.ts +6 -0
  83. package/dist/rules/no-instanceof-schema.js +35 -0
  84. package/dist/rules/no-manual-effect-channels.d.ts +6 -0
  85. package/dist/rules/no-manual-effect-channels.js +40 -0
  86. package/dist/rules/no-match-effect-branch.d.ts +6 -0
  87. package/dist/rules/no-match-effect-branch.js +64 -0
  88. package/dist/rules/no-match-void-branch.d.ts +6 -0
  89. package/dist/rules/no-match-void-branch.js +50 -0
  90. package/dist/rules/no-multiple-effect-provide.d.ts +6 -0
  91. package/dist/rules/no-multiple-effect-provide.js +45 -0
  92. package/dist/rules/no-nested-effect-call.d.ts +6 -0
  93. package/dist/rules/no-nested-effect-call.js +97 -0
  94. package/dist/rules/no-nested-effect-gen.d.ts +6 -0
  95. package/dist/rules/no-nested-effect-gen.js +46 -0
  96. package/dist/rules/no-nested-pipe.d.ts +6 -0
  97. package/dist/rules/no-nested-pipe.js +38 -0
  98. package/dist/rules/no-new-error.d.ts +6 -0
  99. package/dist/rules/no-new-error.js +39 -0
  100. package/dist/rules/no-new-promise.d.ts +6 -0
  101. package/dist/rules/no-new-promise.js +38 -0
  102. package/dist/rules/no-node-builtin-import.d.ts +6 -0
  103. package/dist/rules/no-node-builtin-import.js +61 -0
  104. package/dist/rules/no-option-as.d.ts +6 -0
  105. package/dist/rules/no-option-as.js +16 -0
  106. package/dist/rules/no-option-boolean-normalization.d.ts +6 -0
  107. package/dist/rules/no-option-boolean-normalization.js +50 -0
  108. package/dist/rules/no-positional-log-error.d.ts +6 -0
  109. package/dist/rules/no-positional-log-error.js +52 -0
  110. package/dist/rules/no-redundant-schema-tag-identifier.d.ts +6 -0
  111. package/dist/rules/no-redundant-schema-tag-identifier.js +46 -0
  112. package/dist/rules/no-return-in-arrow.d.ts +6 -0
  113. package/dist/rules/no-return-in-arrow.js +41 -0
  114. package/dist/rules/no-return-nullish.d.ts +6 -0
  115. package/dist/rules/no-return-nullish.js +47 -0
  116. package/dist/rules/no-run-in-effect-gen.d.ts +6 -0
  117. package/dist/rules/no-run-in-effect-gen.js +48 -0
  118. package/dist/rules/no-runtime-run-fork.d.ts +6 -0
  119. package/dist/rules/no-runtime-run-fork.js +19 -0
  120. package/dist/rules/no-schema-struct-with-tag.d.ts +6 -0
  121. package/dist/rules/no-schema-struct-with-tag.js +48 -0
  122. package/dist/rules/no-schema-union-of-literals.d.ts +6 -0
  123. package/dist/rules/no-schema-union-of-literals.js +35 -0
  124. package/dist/rules/no-string-sentinel-const.d.ts +6 -0
  125. package/dist/rules/no-string-sentinel-const.js +38 -0
  126. package/dist/rules/no-switch-statement.d.ts +6 -0
  127. package/dist/rules/no-switch-statement.js +16 -0
  128. package/dist/rules/no-ternary.d.ts +6 -0
  129. package/dist/rules/no-ternary.js +30 -0
  130. package/dist/rules/no-throw-statement.d.ts +6 -0
  131. package/dist/rules/no-throw-statement.js +34 -0
  132. package/dist/rules/no-try-catch.d.ts +6 -0
  133. package/dist/rules/no-try-catch.js +34 -0
  134. package/dist/rules/no-unnecessary-arrow-block.d.ts +6 -0
  135. package/dist/rules/no-unnecessary-arrow-block.js +34 -0
  136. package/dist/rules/no-unnecessary-effect-gen.d.ts +6 -0
  137. package/dist/rules/no-unnecessary-effect-gen.js +35 -0
  138. package/dist/rules/no-unnecessary-pipe-chain.d.ts +6 -0
  139. package/dist/rules/no-unnecessary-pipe-chain.js +36 -0
  140. package/dist/rules/no-unnecessary-pipe.d.ts +6 -0
  141. package/dist/rules/no-unnecessary-pipe.js +29 -0
  142. package/dist/vendor/effect-oxlint/AST.d.ts +199 -0
  143. package/dist/vendor/effect-oxlint/AST.js +214 -0
  144. package/dist/vendor/effect-oxlint/Comment.d.ts +55 -0
  145. package/dist/vendor/effect-oxlint/Comment.js +67 -0
  146. package/dist/vendor/effect-oxlint/Diagnostic.d.ts +86 -0
  147. package/dist/vendor/effect-oxlint/Diagnostic.js +115 -0
  148. package/dist/vendor/effect-oxlint/Plugin.d.ts +38 -0
  149. package/dist/vendor/effect-oxlint/Plugin.js +45 -0
  150. package/dist/vendor/effect-oxlint/Rule.d.ts +218 -0
  151. package/dist/vendor/effect-oxlint/Rule.js +309 -0
  152. package/dist/vendor/effect-oxlint/RuleContext.d.ts +77 -0
  153. package/dist/vendor/effect-oxlint/RuleContext.js +74 -0
  154. package/dist/vendor/effect-oxlint/Scope.d.ts +100 -0
  155. package/dist/vendor/effect-oxlint/Scope.js +110 -0
  156. package/dist/vendor/effect-oxlint/SourceCode.d.ts +180 -0
  157. package/dist/vendor/effect-oxlint/SourceCode.js +203 -0
  158. package/dist/vendor/effect-oxlint/Testing.d.ts +510 -0
  159. package/dist/vendor/effect-oxlint/Testing.js +963 -0
  160. package/dist/vendor/effect-oxlint/Token.d.ts +98 -0
  161. package/dist/vendor/effect-oxlint/Token.js +103 -0
  162. package/dist/vendor/effect-oxlint/Visitor.d.ts +166 -0
  163. package/dist/vendor/effect-oxlint/Visitor.js +138 -0
  164. package/dist/vendor/effect-oxlint/index.d.ts +13 -0
  165. package/dist/vendor/effect-oxlint/index.js +12 -0
  166. package/package.json +24 -5
  167. package/.effect-lsp.json +0 -60
  168. package/.oxlintrc.json +0 -33
  169. package/CHANGELOG.md +0 -37
  170. package/scripts/add-rule.ts +0 -87
  171. package/scripts/codegen.ts +0 -108
  172. package/src/index.ts +0 -9
  173. package/src/plugin.ts +0 -16
  174. package/src/presets/core.ts +0 -47
  175. package/src/presets/effect-native.ts +0 -34
  176. package/src/presets/full.ts +0 -70
  177. package/src/presets/functional.ts +0 -18
  178. package/src/presets/index.ts +0 -5
  179. package/src/presets/strict.ts +0 -77
  180. package/src/rules/_effect-context.ts +0 -28
  181. package/src/rules/index.ts +0 -72
  182. package/src/rules/no-arrow-ladder.ts +0 -70
  183. package/src/rules/no-async-function.ts +0 -47
  184. package/src/rules/no-catch-all-to-map-error.ts +0 -89
  185. package/src/rules/no-effect-as.ts +0 -14
  186. package/src/rules/no-effect-async.ts +0 -14
  187. package/src/rules/no-effect-bind.ts +0 -13
  188. package/src/rules/no-effect-do.ts +0 -14
  189. package/src/rules/no-effect-fn-generator.ts +0 -58
  190. package/src/rules/no-effect-gen-adapter.ts +0 -50
  191. package/src/rules/no-effect-map-flatten.ts +0 -82
  192. package/src/rules/no-effect-map-void.ts +0 -80
  193. package/src/rules/no-effect-never.ts +0 -14
  194. package/src/rules/no-effect-orElse-ladder.ts +0 -79
  195. package/src/rules/no-effect-succeed-string.ts +0 -52
  196. package/src/rules/no-effect-succeed-variable.ts +0 -50
  197. package/src/rules/no-effect-succeed-void.ts +0 -63
  198. package/src/rules/no-effect-sync-console.ts +0 -91
  199. package/src/rules/no-effect-sync-wrapper.ts +0 -67
  200. package/src/rules/no-effect-type-alias.ts +0 -71
  201. package/src/rules/no-effect-wrapper-alias.ts +0 -95
  202. package/src/rules/no-extends-native-error.ts +0 -54
  203. package/src/rules/no-flatmap-ladder.ts +0 -77
  204. package/src/rules/no-fromnullable-coalesce.ts +0 -62
  205. package/src/rules/no-global-error-in-catch.ts +0 -104
  206. package/src/rules/no-global-error-in-failure.ts +0 -83
  207. package/src/rules/no-globals.ts +0 -178
  208. package/src/rules/no-if-statement.ts +0 -16
  209. package/src/rules/no-iife-wrapper.ts +0 -51
  210. package/src/rules/no-inline-provide.ts +0 -41
  211. package/src/rules/no-instanceof-schema.ts +0 -42
  212. package/src/rules/no-manual-effect-channels.ts +0 -58
  213. package/src/rules/no-match-effect-branch.ts +0 -82
  214. package/src/rules/no-match-void-branch.ts +0 -72
  215. package/src/rules/no-multiple-effect-provide.ts +0 -75
  216. package/src/rules/no-nested-effect-call.ts +0 -57
  217. package/src/rules/no-nested-effect-gen.ts +0 -43
  218. package/src/rules/no-nested-pipe.ts +0 -73
  219. package/src/rules/no-new-error.ts +0 -53
  220. package/src/rules/no-new-promise.ts +0 -50
  221. package/src/rules/no-node-builtin-import.ts +0 -68
  222. package/src/rules/no-option-as.ts +0 -13
  223. package/src/rules/no-option-boolean-normalization.ts +0 -88
  224. package/src/rules/no-positional-log-error.ts +0 -76
  225. package/src/rules/no-redundant-schema-tag-identifier.ts +0 -62
  226. package/src/rules/no-return-in-arrow.ts +0 -76
  227. package/src/rules/no-return-nullish.ts +0 -80
  228. package/src/rules/no-run-in-effect.ts +0 -18
  229. package/src/rules/no-runtime-run-fork.ts +0 -14
  230. package/src/rules/no-schema-struct-with-tag.ts +0 -64
  231. package/src/rules/no-schema-union-of-literals.ts +0 -48
  232. package/src/rules/no-string-sentinel-const.ts +0 -49
  233. package/src/rules/no-switch-statement.ts +0 -13
  234. package/src/rules/no-ternary.ts +0 -30
  235. package/src/rules/no-throw-statement.ts +0 -39
  236. package/src/rules/no-try-catch.ts +0 -39
  237. package/src/rules/no-unnecessary-arrow-block.ts +0 -51
  238. package/src/rules/no-unnecessary-effect-gen.ts +0 -83
  239. package/src/rules/no-unnecessary-pipe-chain.ts +0 -47
  240. package/src/rules/no-unnecessary-pipe.ts +0 -45
  241. package/src/vendor/effect-oxlint/AST.ts +0 -516
  242. package/src/vendor/effect-oxlint/Comment.ts +0 -86
  243. package/src/vendor/effect-oxlint/Diagnostic.ts +0 -199
  244. package/src/vendor/effect-oxlint/Plugin.ts +0 -71
  245. package/src/vendor/effect-oxlint/Rule.ts +0 -528
  246. package/src/vendor/effect-oxlint/RuleContext.ts +0 -144
  247. package/src/vendor/effect-oxlint/Scope.ts +0 -177
  248. package/src/vendor/effect-oxlint/SourceCode.ts +0 -377
  249. package/src/vendor/effect-oxlint/Testing.ts +0 -1227
  250. package/src/vendor/effect-oxlint/Token.ts +0 -131
  251. package/src/vendor/effect-oxlint/Visitor.ts +0 -287
  252. package/src/vendor/effect-oxlint/index.ts +0 -88
  253. package/tests/ban-rules.test.ts +0 -85
  254. package/tests/context-rules.test.ts +0 -198
  255. package/tests/pattern-rules.test.ts +0 -247
  256. package/tsconfig.json +0 -15
@@ -1,104 +0,0 @@
1
- /**
2
- * Ban `new Error()` inside Effect catch handlers.
3
- *
4
- * Catches: `.catchAll(e => new Error(...))`, `.catch(e => new Error(...))`
5
- * Use Schema.TaggedErrorClass or Data.TaggedError instead.
6
- *
7
- * Source: language-service/globalErrorInEffectCatch
8
- */
9
- import type { ESTree } from "@oxlint/plugins"
10
- import { AST, Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
11
- import * as Effect from "effect/Effect"
12
- import * as Option from "effect/Option"
13
-
14
- const nativeErrors = new Set([
15
- "Error",
16
- "TypeError",
17
- "RangeError",
18
- "ReferenceError",
19
- "SyntaxError",
20
- "URIError",
21
- "EvalError",
22
- ])
23
-
24
- const catchMethods = new Set([
25
- "catch",
26
- "catchAll",
27
- "catchAllCause",
28
- "catchIf",
29
- "catchCauseIf",
30
- "catchTag",
31
- "catchTags",
32
- "catchDefect",
33
- ])
34
-
35
- const isNativeErrorNew = (node: ESTree.Node): boolean => {
36
- if (node.type !== "NewExpression") return false
37
- const callee = (node as unknown as Record<string, unknown>)["callee"] as ESTree.Node
38
- return callee.type === "Identifier" && "name" in callee && nativeErrors.has(callee.name as string)
39
- }
40
-
41
- /**
42
- * Check if a function body returns `new Error(...)`.
43
- */
44
- const bodyReturnsNativeError = (fn: ESTree.Node): boolean => {
45
- const body = (fn as unknown as Record<string, unknown>)["body"] as ESTree.Node
46
- // Arrow expression body: (e) => new Error(...)
47
- if (body.type !== "BlockStatement") {
48
- return isNativeErrorNew(body)
49
- }
50
- // Block body: (e) => { return new Error(...) }
51
- const stmts = (body as unknown as Record<string, unknown>)["body"]
52
- if (!Array.isArray(stmts) || stmts.length !== 1) return false
53
- const stmt = stmts[0] as ESTree.Node
54
- if (stmt.type !== "ReturnStatement") return false
55
- const arg = (stmt as unknown as Record<string, unknown>)["argument"]
56
- return arg != null && typeof arg === "object" && isNativeErrorNew(arg as ESTree.Node)
57
- }
58
-
59
- export const noGlobalErrorInCatch = Rule.define({
60
- name: "no-global-error-in-catch",
61
- meta: Rule.meta({
62
- type: "suggestion",
63
- description:
64
- "Avoid native Error in Effect catch handlers. Use Schema.TaggedErrorClass or Data.TaggedError.",
65
- }),
66
- create: function* () {
67
- const ctx = yield* RuleContext
68
- return {
69
- CallExpression: (node) => {
70
- const call = node as ESTree.CallExpression
71
- const names = Option.getOrUndefined(
72
- AST.memberNames(call.callee as ESTree.MemberExpression),
73
- )
74
- if (names === undefined) return Effect.void
75
- const [obj, prop] = names
76
- if (obj !== "Effect") return Effect.void
77
- if (!catchMethods.has(prop)) return Effect.void
78
-
79
- // The callback is the last argument
80
- const args = call.arguments
81
- if (args.length === 0) return Effect.void
82
- const callback = args[args.length - 1]
83
- if (callback === undefined) return Effect.void
84
- if (
85
- callback.type !== "ArrowFunctionExpression" &&
86
- callback.type !== "FunctionExpression"
87
- ) {
88
- return Effect.void
89
- }
90
-
91
- if (bodyReturnsNativeError(callback as ESTree.Node)) {
92
- return ctx.report(
93
- Diagnostic.make({
94
- node: callback,
95
- message:
96
- "Avoid native Error in Effect catch handler. Use Schema.TaggedErrorClass or Data.TaggedError.",
97
- }),
98
- )
99
- }
100
- return Effect.void
101
- },
102
- }
103
- },
104
- })
@@ -1,83 +0,0 @@
1
- /**
2
- * Ban `new Error()` inside `Effect.fail()`.
3
- *
4
- * Use Schema.TaggedErrorClass or Data.TaggedError instead.
5
- *
6
- * Source: language-service/globalErrorInEffectFailure
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
- import * as Option from "effect/Option"
12
-
13
- const nativeErrors = new Set([
14
- "Error",
15
- "TypeError",
16
- "RangeError",
17
- "ReferenceError",
18
- "SyntaxError",
19
- "URIError",
20
- "EvalError",
21
- ])
22
-
23
- const isNativeErrorNew = (node: ESTree.Node): boolean => {
24
- if (node.type !== "NewExpression") return false
25
- const callee = (node as unknown as Record<string, unknown>)["callee"] as ESTree.Node
26
- return callee.type === "Identifier" && "name" in callee && nativeErrors.has(callee.name as string)
27
- }
28
-
29
- export const noGlobalErrorInFailure = Rule.define({
30
- name: "no-global-error-in-failure",
31
- meta: Rule.meta({
32
- type: "suggestion",
33
- description:
34
- "Avoid native Error in Effect.fail(). Use Schema.TaggedErrorClass or Data.TaggedError.",
35
- }),
36
- create: function* () {
37
- const ctx = yield* RuleContext
38
- return {
39
- CallExpression: (node) => {
40
- const call = node as ESTree.CallExpression
41
- if (
42
- Option.isNone(AST.matchCallOf(call, "Effect", "fail")) &&
43
- Option.isNone(AST.matchCallOf(call, "Effect", "failSync")) &&
44
- Option.isNone(AST.matchCallOf(call, "Effect", "failCause"))
45
- ) {
46
- return Effect.void
47
- }
48
-
49
- const args = call.arguments
50
- if (args.length === 0) return Effect.void
51
-
52
- // Check direct arg: Effect.fail(new Error(...))
53
- const arg = args[args.length - 1]
54
- if (arg === undefined) return Effect.void
55
- if (isNativeErrorNew(arg as ESTree.Node)) {
56
- return ctx.report(
57
- Diagnostic.make({
58
- node: arg,
59
- message:
60
- "Avoid native Error in Effect.fail(). Use Schema.TaggedErrorClass or Data.TaggedError.",
61
- }),
62
- )
63
- }
64
-
65
- // Check arrow body: Effect.failSync(() => new Error(...))
66
- if (arg.type === "ArrowFunctionExpression") {
67
- const body = (arg as unknown as Record<string, unknown>)["body"] as ESTree.Node
68
- if (body.type !== "BlockStatement" && isNativeErrorNew(body)) {
69
- return ctx.report(
70
- Diagnostic.make({
71
- node: arg,
72
- message:
73
- "Avoid native Error in Effect.failSync(). Use Schema.TaggedErrorClass or Data.TaggedError.",
74
- }),
75
- )
76
- }
77
- }
78
-
79
- return Effect.void
80
- },
81
- }
82
- },
83
- })
@@ -1,178 +0,0 @@
1
- /**
2
- * Ban global/platform APIs inside Effect.gen/fn context.
3
- *
4
- * Consolidates: console, fetch(), Date/new Date, Math.random/crypto,
5
- * setTimeout/setInterval, JSON.parse/stringify, process/Bun/Deno globals
6
- *
7
- * Each has a specific Effect alternative hint.
8
- *
9
- * Sources: language-service globalConsoleInEffect, globalFetchInEffect,
10
- * globalDateInEffect, globalRandomInEffect, globalTimersInEffect,
11
- * preferSchemaOverJson, processEnvInEffect, cryptoRandomUUIDInEffect
12
- */
13
- import type { ESTree } from "@oxlint/plugins"
14
- import { AST, Diagnostic, Rule, Visitor, RuleContext } from "../vendor/effect-oxlint/index.js"
15
- import * as Effect from "effect/Effect"
16
- import * as Option from "effect/Option"
17
- import * as Ref from "effect/Ref"
18
-
19
- import { makeEffectContextTracker } from "./_effect-context.js"
20
-
21
- // --- Member expression bans (obj.prop) ---
22
-
23
- type MemberBan = readonly [obj: string, props: string | ReadonlyArray<string>, message: string]
24
-
25
- const memberBans: ReadonlyArray<MemberBan> = [
26
- // console
27
- ["console", ["log", "warn", "error", "info", "debug", "trace"], "Avoid console.* inside Effect context. Use Effect.log / Effect.logWarning / Effect.logError."],
28
- // Date
29
- ["Date", "now", "Avoid Date.now() inside Effect context. Use Clock service from 'effect'."],
30
- // JSON
31
- ["JSON", ["parse", "stringify"], "Avoid JSON.parse/stringify inside Effect context. Use Schema.fromJsonString."],
32
- // Math.random
33
- ["Math", "random", "Avoid Math.random() inside Effect context. Use Random service from 'effect'."],
34
- // crypto
35
- ["crypto", ["randomUUID", "getRandomValues"], "Avoid crypto.randomUUID() inside Effect context. Use Random service from 'effect'."],
36
- ]
37
-
38
- // --- Platform globals (Bun.*, process.*, Deno.*) ---
39
-
40
- const platformObjects = new Set(["Bun", "process", "Deno"])
41
-
42
- const alternatives: Record<string, Record<string, string>> = {
43
- Bun: {
44
- file: "Use FileSystem from 'effect'",
45
- write: "Use FileSystem from 'effect'",
46
- read: "Use FileSystem from 'effect'",
47
- serve: "Use HttpServer from 'effect/unstable/http'",
48
- env: "Use Config from 'effect'",
49
- spawn: "Use Command from 'effect'",
50
- sleep: "Use Effect.sleep",
51
- stdin: "Use Terminal from 'effect'",
52
- stdout: "Use Terminal from 'effect'",
53
- stderr: "Use Terminal from 'effect'",
54
- cwd: "Use Path from 'effect'",
55
- which: "Use Command from 'effect'",
56
- resolveSync: "Use Path from 'effect'",
57
- pathToFileURL: "Use Path from 'effect'",
58
- randomUUID: "Use Random service from 'effect'",
59
- randomUUIDv7: "Use Random service from 'effect'",
60
- },
61
- process: {
62
- env: "Use Config from 'effect'",
63
- exit: "Use Effect.fail or Effect.die",
64
- cwd: "Use Path from 'effect'",
65
- argv: "Use Args/Command from 'effect/unstable/cli'",
66
- stdin: "Use Terminal from 'effect'",
67
- stdout: "Use Terminal from 'effect'",
68
- stderr: "Use Terminal from 'effect'",
69
- chdir: "Use Path from 'effect'",
70
- kill: "Use Command from 'effect'",
71
- on: "Use Effect.async or Stream.async",
72
- platform: "Use PlatformInfo from 'effect'",
73
- arch: "Use PlatformInfo from 'effect'",
74
- execPath: "Use Command from 'effect'",
75
- execArgv: "Use Args/Command from 'effect/unstable/cli'",
76
- },
77
- Deno: {
78
- readFile: "Use FileSystem from 'effect'",
79
- writeFile: "Use FileSystem from 'effect'",
80
- readTextFile: "Use FileSystem from 'effect'",
81
- writeTextFile: "Use FileSystem from 'effect'",
82
- open: "Use FileSystem from 'effect'",
83
- remove: "Use FileSystem from 'effect'",
84
- mkdir: "Use FileSystem from 'effect'",
85
- readDir: "Use FileSystem from 'effect'",
86
- stat: "Use FileSystem from 'effect'",
87
- cwd: "Use Path from 'effect'",
88
- env: "Use Config from 'effect'",
89
- exit: "Use Effect.fail or Effect.die",
90
- serve: "Use HttpServer from 'effect/unstable/http'",
91
- run: "Use Command from 'effect'",
92
- args: "Use Args/Command from 'effect/unstable/cli'",
93
- },
94
- }
95
-
96
- function getHint(obj: string, prop: string): string {
97
- const hint = alternatives[obj]?.[prop]
98
- if (hint) return `. ${hint}`
99
- return ". Wrap in a service for testability"
100
- }
101
-
102
- // --- Call expression bans (bare function calls) ---
103
-
104
- const callBans: Record<string, string> = {
105
- fetch: "Avoid fetch() inside Effect context. Use HttpClient from 'effect/unstable/http'.",
106
- setTimeout: "Avoid setTimeout inside Effect context. Use Effect.sleep or Schedule.",
107
- setInterval: "Avoid setInterval inside Effect context. Use Effect.sleep or Schedule.",
108
- }
109
-
110
- export const noGlobals = Rule.define({
111
- name: "no-globals",
112
- meta: Rule.meta({
113
- type: "suggestion",
114
- description:
115
- "Avoid global/platform APIs inside Effect.gen/fn. Use Effect platform services instead.",
116
- }),
117
- create: function* () {
118
- const ctx = yield* RuleContext
119
- const [depth, tracker] = yield* makeEffectContextTracker
120
-
121
- return Visitor.merge(tracker, {
122
- // Member expressions: console.log, Date.now, JSON.parse, Math.random, process.env, Bun.*, etc.
123
- MemberExpression: (node) =>
124
- Effect.flatMap(Ref.get(depth), (d) => {
125
- if (d <= 0) return Effect.void
126
- const memberNode = node as ESTree.MemberExpression
127
-
128
- // Check specific member bans
129
- for (const [obj, props, message] of memberBans) {
130
- if (Option.isSome(AST.matchMember(memberNode, obj, props))) {
131
- return ctx.report(Diagnostic.make({ node, message }))
132
- }
133
- }
134
-
135
- // Check platform globals (Bun.*, process.*, Deno.*)
136
- const names = Option.getOrUndefined(AST.memberNames(memberNode))
137
- if (names !== undefined) {
138
- const [obj, prop] = names
139
- if (platformObjects.has(obj)) {
140
- return ctx.report(
141
- Diagnostic.make({
142
- node,
143
- message: `Avoid ${obj}.${prop} inside Effect context${getHint(obj, prop)}.`,
144
- }),
145
- )
146
- }
147
- }
148
-
149
- return Effect.void
150
- }),
151
-
152
- // Call expressions: fetch(), setTimeout(), setInterval()
153
- CallExpression: (node) =>
154
- Effect.flatMap(Ref.get(depth), (d) => {
155
- if (d <= 0) return Effect.void
156
- const name = Option.getOrUndefined(AST.calleeName(node as ESTree.CallExpression))
157
- const message = name !== undefined ? callBans[name] : undefined
158
- if (message !== undefined) {
159
- return ctx.report(Diagnostic.make({ node, message }))
160
- }
161
- return Effect.void
162
- }),
163
-
164
- // New expressions: new Date()
165
- NewExpression: (node) =>
166
- Effect.flatMap(Ref.get(depth), (d) => {
167
- if (d <= 0) return Effect.void
168
- const callee = (node as unknown as Record<string, unknown>)["callee"] as ESTree.Node
169
- if (callee.type === "Identifier" && "name" in callee && callee.name === "Date") {
170
- return ctx.report(
171
- Diagnostic.make({ node, message: "Avoid new Date() inside Effect context. Use Clock service from 'effect'." }),
172
- )
173
- }
174
- return Effect.void
175
- }),
176
- })
177
- },
178
- })
@@ -1,16 +0,0 @@
1
- /**
2
- * Ban `if` statements in Effect codebases.
3
- *
4
- * Use `Option.match`, `Either.match`, `Match.value`, or data combinators.
5
- *
6
- * Source: biome-effect-linting-rules/no-if-statement
7
- *
8
- * Note: Very opinionated — recommended for strict functional presets only.
9
- */
10
- import { Rule } from "../vendor/effect-oxlint/index.js"
11
-
12
- export const noIfStatement = Rule.banStatement("IfStatement", {
13
- message:
14
- "Avoid if statements. Use Option.match, Either.match, Match.value, or Effect.if.",
15
- meta: { type: "suggestion" },
16
- })
@@ -1,51 +0,0 @@
1
- /**
2
- * Ban immediately-invoked function expressions (IIFEs).
3
- *
4
- * Use named const binding + flat pipeline.
5
- *
6
- * Source: biome-effect-linting-rules/no-iife-wrapper
7
- */
8
- import { Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
9
- import * as Effect from "effect/Effect"
10
-
11
- export const noIifeWrapper = Rule.define({
12
- name: "no-iife-wrapper",
13
- meta: Rule.meta({
14
- type: "suggestion",
15
- description: "Avoid IIFEs. Use named const bindings + flat pipeline.",
16
- }),
17
- create: function* () {
18
- const ctx = yield* RuleContext
19
- return {
20
- CallExpression: (node) => {
21
- if (!("callee" in node)) return Effect.void
22
- const callee = node.callee
23
- if (callee == null || typeof callee !== "object" || !("type" in callee)) return Effect.void
24
-
25
- const isWrappedFn =
26
- callee.type === "ArrowFunctionExpression" ||
27
- callee.type === "FunctionExpression"
28
-
29
- // Also catch (expr)(...) where expr is parenthesized fn
30
- const isParenWrapped =
31
- callee.type === "ParenthesizedExpression" &&
32
- "expression" in callee &&
33
- callee.expression != null &&
34
- typeof callee.expression === "object" &&
35
- "type" in callee.expression &&
36
- (callee.expression.type === "ArrowFunctionExpression" ||
37
- callee.expression.type === "FunctionExpression")
38
-
39
- if (isWrappedFn || isParenWrapped) {
40
- return ctx.report(
41
- Diagnostic.make({
42
- node,
43
- message: "Avoid IIFEs. Use named const binding + flat pipeline.",
44
- }),
45
- )
46
- }
47
- return Effect.void
48
- },
49
- }
50
- },
51
- })
@@ -1,41 +0,0 @@
1
- /**
2
- * Ban `Effect.provide(layer)` used inline inside Effect.gen/fn.
3
- *
4
- * Provide layers at the boundary, not deep in generators.
5
- *
6
- * Source: biome-effect-linting-rules/no-inline-runtime-provide
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
- import { makeEffectContextTracker } from "./_effect-context.js"
14
-
15
- export const noInlineProvide = Rule.define({
16
- name: "no-inline-provide",
17
- meta: Rule.meta({
18
- type: "suggestion",
19
- description:
20
- "Avoid Effect.provide inside Effect.gen/fn. Provide layers at the boundary.",
21
- }),
22
- create: function* () {
23
- const ctx = yield* RuleContext
24
- const [depth, tracker] = yield* makeEffectContextTracker
25
-
26
- return Visitor.merge(tracker, {
27
- CallExpression: (node) =>
28
- Effect.flatMap(Ref.get(depth), (d) => {
29
- if (d <= 0) return Effect.void
30
- if (!AST.isCallOf(node as ESTree.CallExpression, "Effect", "provide")) return Effect.void
31
-
32
- return ctx.report(
33
- Diagnostic.make({
34
- node,
35
- message: "Avoid Effect.provide inside Effect.gen/fn. Provide layers at the boundary.",
36
- }),
37
- )
38
- }),
39
- })
40
- },
41
- })
@@ -1,42 +0,0 @@
1
- /**
2
- * Ban `instanceof` checks against Schema-derived classes.
3
- *
4
- * Use `Schema.is(SchemaType)` instead.
5
- *
6
- * Source: language-service/instanceOfSchema
7
- *
8
- * Note: Without type information, we detect common Effect Schema class names
9
- * used in `instanceof` checks. This is a best-effort heuristic.
10
- */
11
- import { Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
12
- import * as Effect from "effect/Effect"
13
-
14
- export const noInstanceofSchema = Rule.define({
15
- name: "no-instanceof-schema",
16
- meta: Rule.meta({
17
- type: "suggestion",
18
- description:
19
- "Avoid instanceof with Schema types. Use Schema.is(SchemaType) instead.",
20
- }),
21
- create: function* () {
22
- const ctx = yield* RuleContext
23
- return {
24
- BinaryExpression: (node) => {
25
- if (
26
- "operator" in node &&
27
- node.operator === "instanceof"
28
- ) {
29
- // Report all instanceof — in Effect codebases, instanceof is generally wrong
30
- return ctx.report(
31
- Diagnostic.make({
32
- node,
33
- message:
34
- "Avoid instanceof. Add a static `is` field to your class: `static is = Schema.is(this)`, then use `MyClass.is(value)`. For tagged types, use `_tag` checks.",
35
- }),
36
- )
37
- }
38
- return Effect.void
39
- },
40
- }
41
- },
42
- })
@@ -1,58 +0,0 @@
1
- /**
2
- * Ban manual `Effect.Effect<A, E, R>` type annotations.
3
- *
4
- * Let return types infer from the Effect/Layer you return.
5
- *
6
- * Source: biome-effect-linting-rules/no-manual-effect-channels
7
- */
8
- import { Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
9
- import * as Effect from "effect/Effect"
10
-
11
- export const noManualEffectChannels = Rule.define({
12
- name: "no-manual-effect-channels",
13
- meta: Rule.meta({
14
- type: "suggestion",
15
- description:
16
- "Avoid manual Effect.Effect<A, E, R> annotations. Let types infer from the returned Effect.",
17
- }),
18
- create: function* () {
19
- const ctx = yield* RuleContext
20
- return {
21
- TSTypeReference: (node) => {
22
- if (!("typeName" in node) || node.typeName == null) return Effect.void
23
- const typeName = node.typeName as unknown as Record<string, unknown>
24
-
25
- // Effect.Effect<...> or Layer.Layer<...>
26
- if (typeName["type"] === "TSQualifiedName" && "left" in typeName && "right" in typeName) {
27
- const left = typeName["left"] as unknown as Record<string, unknown> | null
28
- const right = typeName["right"] as unknown as Record<string, unknown> | null
29
- if (
30
- left != null && left["type"] === "Identifier" &&
31
- (left["name"] === "Effect" || left["name"] === "Layer") &&
32
- right != null && right["type"] === "Identifier" &&
33
- (right["name"] === "Effect" || right["name"] === "Layer")
34
- ) {
35
- // Only flag if explicit type parameters are provided
36
- if (
37
- "typeParameters" in node &&
38
- node.typeParameters != null &&
39
- typeof node.typeParameters === "object" &&
40
- "params" in (node.typeParameters as unknown as Record<string, unknown>) &&
41
- Array.isArray((node.typeParameters as unknown as Record<string, unknown>)["params"]) &&
42
- ((node.typeParameters as unknown as Record<string, unknown>)["params"] as unknown[]).length > 0
43
- ) {
44
- return ctx.report(
45
- Diagnostic.make({
46
- node,
47
- message:
48
- "Avoid manual Effect/Layer channel annotations. Let types infer from the returned value.",
49
- }),
50
- )
51
- }
52
- }
53
- }
54
- return Effect.void
55
- },
56
- }
57
- },
58
- })
@@ -1,82 +0,0 @@
1
- /**
2
- * Ban Match.when/orElse branches that contain multi-step Effect sequencing.
3
- *
4
- * Select the value in Match, then run one Effect pipeline outside.
5
- *
6
- * Source: biome-effect-linting-rules/no-match-effect-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 sequencingMethods = new Set(["flatMap", "map", "andThen", "tap", "zipRight", "pipe"])
13
-
14
- const containsEffectSequencing = (node: unknown): boolean => {
15
- if (node == null || typeof node !== "object") return false
16
- const n = node as Record<string, unknown>
17
- if (n["type"] === "CallExpression" && "callee" in n) {
18
- const callee = n["callee"] as Record<string, unknown> | null
19
- if (callee != null && callee["type"] === "MemberExpression") {
20
- const obj = callee["object"] as Record<string, unknown> | null
21
- const prop = callee["property"] as Record<string, unknown> | null
22
- if (
23
- obj != null && obj["type"] === "Identifier" && obj["name"] === "Effect" &&
24
- prop != null && prop["type"] === "Identifier" && typeof prop["name"] === "string" &&
25
- sequencingMethods.has(prop["name"])
26
- ) {
27
- return true
28
- }
29
- }
30
- if ("arguments" in n && Array.isArray(n["arguments"])) {
31
- for (const arg of n["arguments"]) {
32
- if (containsEffectSequencing(arg)) return true
33
- }
34
- }
35
- }
36
- if ("body" in n) {
37
- if (Array.isArray(n["body"])) {
38
- for (const child of n["body"]) {
39
- if (containsEffectSequencing(child)) return true
40
- }
41
- } else if (containsEffectSequencing(n["body"])) return true
42
- }
43
- if ("argument" in n && containsEffectSequencing(n["argument"])) return true
44
- if ("expression" in n && containsEffectSequencing(n["expression"])) return true
45
- return false
46
- }
47
-
48
- export const noMatchEffectBranch = Rule.define({
49
- name: "no-match-effect-branch",
50
- meta: Rule.meta({
51
- type: "suggestion",
52
- description:
53
- "Avoid Effect sequencing inside Match branches. Select value in Match, run Effect outside.",
54
- }),
55
- create: function* () {
56
- const ctx = yield* RuleContext
57
- return {
58
- CallExpression: (node) => {
59
- const ce = node as ESTree.CallExpression
60
- const isMatchWhen = AST.isCallOf(ce, "Match", "when")
61
- const isMatchOrElse = AST.isCallOf(ce, "Match", "orElse")
62
-
63
- if (!isMatchWhen && !isMatchOrElse) return Effect.void
64
- if (!("arguments" in node) || !Array.isArray(node.arguments)) return Effect.void
65
-
66
- // Check the callback argument for Effect sequencing
67
- const callbackIdx = isMatchWhen ? 1 : 0
68
- const callback = node.arguments[callbackIdx]
69
- if (callback != null && containsEffectSequencing(callback)) {
70
- return ctx.report(
71
- Diagnostic.make({
72
- node,
73
- message:
74
- "Avoid Effect sequencing inside Match branches. Select value in Match, then run one Effect pipeline outside.",
75
- }),
76
- )
77
- }
78
- return Effect.void
79
- },
80
- }
81
- },
82
- })