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,131 +0,0 @@
1
- /**
2
- * Token type predicates and helpers for Effect-first lint rules.
3
- *
4
- * Provides convenience checks over oxlint's `Token` type (a discriminated
5
- * union of `BooleanToken`, `IdentifierToken`, `KeywordToken`, etc.).
6
- *
7
- * @since 0.2.0
8
- */
9
- import type { Token } from '@oxlint/plugins';
10
- import { dual } from 'effect/Function';
11
-
12
- // ---------------------------------------------------------------------------
13
- // Type predicates
14
- // ---------------------------------------------------------------------------
15
-
16
- /**
17
- * Check whether a token is a keyword with the given value.
18
- *
19
- * @example
20
- * ```ts
21
- * Token.isKeyword(token, 'const') // true if keyword "const"
22
- * Token.isKeyword(token, 'return') // true if keyword "return"
23
- * ```
24
- *
25
- * @since 0.2.0
26
- */
27
- export const isKeyword: {
28
- (keyword: string): (token: Token) => boolean;
29
- (token: Token, keyword: string): boolean;
30
- } = dual(
31
- 2,
32
- (token: Token, keyword: string): boolean =>
33
- token.type === 'Keyword' && token.value === keyword
34
- );
35
-
36
- /**
37
- * Check whether a token is a punctuator with the given value.
38
- *
39
- * @example
40
- * ```ts
41
- * Token.isPunctuator(token, '{') // true if punctuator "{"
42
- * Token.isPunctuator(token, ';') // true if punctuator ";"
43
- * ```
44
- *
45
- * @since 0.2.0
46
- */
47
- export const isPunctuator: {
48
- (value: string): (token: Token) => boolean;
49
- (token: Token, value: string): boolean;
50
- } = dual(
51
- 2,
52
- (token: Token, value: string): boolean =>
53
- token.type === 'Punctuator' && token.value === value
54
- );
55
-
56
- /**
57
- * Check whether a token is an identifier.
58
- *
59
- * @since 0.2.0
60
- */
61
- export const isIdentifier = (token: Token): boolean =>
62
- token.type === 'Identifier';
63
-
64
- /**
65
- * Check whether a token is a string literal.
66
- *
67
- * @since 0.2.0
68
- */
69
- export const isString = (token: Token): boolean => token.type === 'String';
70
-
71
- /**
72
- * Check whether a token is a numeric literal.
73
- *
74
- * @since 0.2.0
75
- */
76
- export const isNumeric = (token: Token): boolean => token.type === 'Numeric';
77
-
78
- /**
79
- * Check whether a token is a boolean literal.
80
- *
81
- * @since 0.2.0
82
- */
83
- export const isBoolean = (token: Token): boolean => token.type === 'Boolean';
84
-
85
- /**
86
- * Check whether a token is a null literal.
87
- *
88
- * @since 0.2.0
89
- */
90
- export const isNull = (token: Token): boolean => token.type === 'Null';
91
-
92
- /**
93
- * Check whether a token is a template literal part.
94
- *
95
- * @since 0.2.0
96
- */
97
- export const isTemplate = (token: Token): boolean => token.type === 'Template';
98
-
99
- /**
100
- * Check whether a token is a regular expression literal.
101
- *
102
- * @since 0.2.0
103
- */
104
- export const isRegularExpression = (token: Token): boolean =>
105
- token.type === 'RegularExpression';
106
-
107
- /**
108
- * Check whether a token is a private identifier (e.g. `#foo`).
109
- *
110
- * @since 0.2.0
111
- */
112
- export const isPrivateIdentifier = (token: Token): boolean =>
113
- token.type === 'PrivateIdentifier';
114
-
115
- // ---------------------------------------------------------------------------
116
- // Accessors
117
- // ---------------------------------------------------------------------------
118
-
119
- /**
120
- * Get the string value of a token.
121
- *
122
- * @since 0.2.0
123
- */
124
- export const value = (token: Token): string => token.value;
125
-
126
- /**
127
- * Get the type discriminant of a token.
128
- *
129
- * @since 0.2.0
130
- */
131
- export const type = (token: Token): string => token.type;
@@ -1,287 +0,0 @@
1
- /**
2
- * Composable visitor construction for Effect-first oxlint rules.
3
- *
4
- * Visitors are maps from AST node type names to Effect-returning handlers.
5
- * This module provides combinators to build, merge, and enhance visitors.
6
- *
7
- * @since 0.1.0
8
- */
9
- import type { ESTree, Visitor as OxlintVisitor } from '@oxlint/plugins';
10
- import * as Arr from 'effect/Array';
11
- import * as Effect from 'effect/Effect';
12
- import { dual, pipe } from 'effect/Function';
13
- import * as Option from 'effect/Option';
14
- import * as R from 'effect/Record';
15
- import * as Ref from 'effect/Ref';
16
-
17
- import { RuleContext } from './RuleContext.ts';
18
-
19
- // ---------------------------------------------------------------------------
20
- // Types
21
- // ---------------------------------------------------------------------------
22
-
23
- /**
24
- * An effectful visitor handler.
25
- *
26
- * Receives an AST node and returns an `Effect<void>` that may read/write
27
- * `Ref` state and report diagnostics via `RuleContext`.
28
- *
29
- * @since 0.1.0
30
- */
31
- export type EffectHandler<N = ESTree.Node> = {
32
- bivarianceHack(node: N): Effect.Effect<void, never, RuleContext>;
33
- }['bivarianceHack'];
34
-
35
- /**
36
- * A map from AST node type names (and `"NodeType:exit"` variants) to
37
- * effectful handlers.
38
- *
39
- * This is the internal representation — handlers accept `ESTree.Node`.
40
- *
41
- * @since 0.1.0
42
- */
43
- export type EffectVisitor = {
44
- readonly [key: string]: EffectHandler;
45
- };
46
-
47
- /** @internal Extract the node parameter type from an oxlint visitor handler. */
48
- type ExtractNode<H> = H extends
49
- | ((node: infer N) => unknown)
50
- | { bivarianceHack(node: infer N): void }
51
- ? N
52
- : ESTree.Node;
53
-
54
- /**
55
- * Resolve a visitor key to its narrowed node type.
56
- *
57
- * For known keys (e.g. `'CallExpression'`), returns the specific
58
- * ESTree node type. For unknown keys, falls back to `ESTree.Node`.
59
- *
60
- * @since 0.2.0
61
- */
62
- export type VisitorNodeType<K extends string> = K extends keyof OxlintVisitor
63
- ? ExtractNode<Exclude<OxlintVisitor[K], undefined>>
64
- : ESTree.Node;
65
-
66
- /**
67
- * Typed visitor map where known oxlint visitor keys provide the
68
- * correctly narrowed node type to handlers (e.g. `MemberExpression`
69
- * handlers receive `ESTree.MemberExpression`).
70
- *
71
- * Return this from `Rule.define`'s `create` generator. Callers get
72
- * typed nodes in their handlers without manual narrowing.
73
- *
74
- * @since 0.2.0
75
- */
76
- export type TypedEffectVisitor = {
77
- readonly [K in keyof OxlintVisitor]?: EffectHandler<
78
- ExtractNode<Exclude<OxlintVisitor[K], undefined>>
79
- >;
80
- };
81
-
82
- // ---------------------------------------------------------------------------
83
- // Constructors
84
- // ---------------------------------------------------------------------------
85
-
86
- /**
87
- * Create a single-entry visitor that handles one node type.
88
- *
89
- * The handler is a generator function that can `yield*` Effects.
90
- *
91
- * @example
92
- * ```ts
93
- * Visitor.on('ThrowStatement', function*(node) {
94
- * const depth = yield* Ref.get(myDepthRef)
95
- * if (depth > 0) {
96
- * yield* RuleContext.report({ node, message: '...' })
97
- * }
98
- * })
99
- * ```
100
- *
101
- * @since 0.1.0
102
- */
103
- export const on = <K extends string>(
104
- nodeType: K,
105
- handler: EffectHandler<VisitorNodeType<K>>
106
- ): EffectVisitor => ({ [nodeType]: handler as EffectHandler });
107
-
108
- /**
109
- * Create a single-entry visitor for the exit phase of a node type.
110
- *
111
- * @since 0.1.0
112
- */
113
- export const onExit = <K extends string>(
114
- nodeType: K,
115
- handler: EffectHandler<VisitorNodeType<K>>
116
- ): EffectVisitor => ({ [`${nodeType}:exit`]: handler as EffectHandler });
117
-
118
- // ---------------------------------------------------------------------------
119
- // Combinators
120
- // ---------------------------------------------------------------------------
121
-
122
- /** @internal Empty visitor seed for reduce operations. */
123
- const emptyVisitor: EffectVisitor = {};
124
-
125
- /** @internal Combine two handlers for the same node type into one sequential handler. */
126
- const sequenceHandlers =
127
- (left: EffectHandler, right: EffectHandler): EffectHandler =>
128
- (node) =>
129
- Effect.andThen(left(node), right(node));
130
-
131
- /**
132
- * Merge multiple visitors into one.
133
- *
134
- * When two visitors handle the same node type, both handlers run
135
- * sequentially (left to right).
136
- *
137
- * @since 0.1.0
138
- */
139
- export const merge = (
140
- ...visitors: ReadonlyArray<EffectVisitor>
141
- ): EffectVisitor =>
142
- Arr.reduce(visitors, emptyVisitor, (acc, visitor) =>
143
- R.union(acc, visitor, (left, right) => sequenceHandlers(left, right))
144
- );
145
-
146
- /**
147
- * Create an enter/exit visitor pair that increments a `Ref<number>` on
148
- * enter and decrements on exit, but only when the predicate matches.
149
- *
150
- * This replaces the common `let depth = 0` mutable counter pattern.
151
- *
152
- * The predicate receives the narrowed node type for the given key
153
- * (e.g. `ESTree.CallExpression` for `'CallExpression'`).
154
- *
155
- * @example
156
- * ```ts
157
- * const effectGenDepth = yield* Ref.make(0)
158
- *
159
- * Visitor.tracked('CallExpression',
160
- * (node) => AST.isCallOf(node, 'Effect', 'gen'),
161
- * effectGenDepth
162
- * )
163
- * ```
164
- *
165
- * @since 0.1.0
166
- */
167
- export const tracked = <K extends string>(
168
- nodeType: K,
169
- predicate: (node: VisitorNodeType<K>) => boolean,
170
- ref: Ref.Ref<number>
171
- ): EffectVisitor => ({
172
- [nodeType]: (node: ESTree.Node) =>
173
- predicate(node as VisitorNodeType<K>)
174
- ? Ref.update(ref, (n) => n + 1)
175
- : Effect.void,
176
- [`${nodeType}:exit`]: (node: ESTree.Node) =>
177
- predicate(node as VisitorNodeType<K>)
178
- ? Ref.update(ref, (n) => n - 1)
179
- : Effect.void
180
- });
181
-
182
- /**
183
- * Conditionally apply a visitor based on a predicate evaluated once
184
- * at create time.
185
- *
186
- * Useful for restricting a visitor to specific files (e.g. skip test files).
187
- *
188
- * @example
189
- * ```ts
190
- * Visitor.filter(
191
- * (filename) => !filename.endsWith('.test.ts'),
192
- * mainVisitor
193
- * )
194
- * ```
195
- *
196
- * @since 0.2.0
197
- */
198
- export const filter: {
199
- (
200
- predicate: (filename: string) => boolean
201
- ): (
202
- visitor: EffectVisitor
203
- ) => Effect.Effect<EffectVisitor, never, RuleContext>;
204
- (
205
- predicate: (filename: string) => boolean,
206
- visitor: EffectVisitor
207
- ): Effect.Effect<EffectVisitor, never, RuleContext>;
208
- } = dual(
209
- 2,
210
- (
211
- predicate: (filename: string) => boolean,
212
- visitor: EffectVisitor
213
- ): Effect.Effect<EffectVisitor, never, RuleContext> =>
214
- Effect.service(RuleContext).pipe(
215
- Effect.map((ctx) => (predicate(ctx.filename) ? visitor : {}))
216
- )
217
- );
218
-
219
- /**
220
- * Accumulate values during traversal and analyze them at `Program:exit`.
221
- *
222
- * The `extract` function is called for each node of `nodeType`. If it
223
- * returns `Option.some(value)`, that value is accumulated. At
224
- * `Program:exit`, the `analyze` generator receives all collected items.
225
- *
226
- * @example
227
- * ```ts
228
- * Visitor.accumulate(
229
- * 'ExportNamedDeclaration',
230
- * (node) => AST.narrow(node, 'ExportNamedDeclaration').pipe(
231
- * Option.map(n => n.declaration)
232
- * ),
233
- * function*(accumulated) {
234
- * // Analyze all collected declarations at end of file
235
- * }
236
- * )
237
- * ```
238
- *
239
- * @since 0.2.0
240
- */
241
- export const accumulate = <A>(
242
- nodeType: string,
243
- extract: (node: ESTree.Node) => Option.Option<A>,
244
- analyze: (
245
- items: ReadonlyArray<A>
246
- ) => Effect.Effect<void, never, RuleContext>
247
- ): Effect.Effect<EffectVisitor, never, RuleContext> =>
248
- Effect.gen(function* () {
249
- const ref = yield* Ref.make<ReadonlyArray<A>>([]);
250
- return merge(
251
- on(nodeType, (node) =>
252
- pipe(
253
- extract(node),
254
- Option.match({
255
- onNone: () => Effect.void,
256
- onSome: (value) =>
257
- Ref.update(ref, (items) => [...items, value])
258
- })
259
- )
260
- ),
261
- on('Program:exit', () =>
262
- Effect.flatMap(Ref.get(ref), (items) => analyze(items))
263
- )
264
- );
265
- });
266
-
267
- // ---------------------------------------------------------------------------
268
- // Conversion (internal — used by Rule.define at the runtime boundary)
269
- // ---------------------------------------------------------------------------
270
-
271
- /**
272
- * Convert an `EffectVisitor` to a plain oxlint `Visitor` by wrapping
273
- * each handler with the provided runner function.
274
- *
275
- * This is the runtime boundary where Effects are executed. Called once
276
- * per file inside `Rule.define`'s `create`.
277
- *
278
- * @internal
279
- */
280
- export const toOxlintVisitor = (
281
- effectVisitor: EffectVisitor,
282
- runHandler: (effect: Effect.Effect<void, never, RuleContext>) => void
283
- ): OxlintVisitor =>
284
- R.map(
285
- effectVisitor,
286
- (handler) => (node: ESTree.Node) => runHandler(handler(node))
287
- );
@@ -1,88 +0,0 @@
1
- /**
2
- * Effect-first library for writing oxlint custom lint rules.
3
- *
4
- * @since 0.1.0
5
- * @packageDocumentation
6
- */
7
-
8
- // ---------------------------------------------------------------------------
9
- // Core modules
10
- // ---------------------------------------------------------------------------
11
-
12
- /** AST pattern matching helpers returning `Option` for safe composition. */
13
- export * as AST from './AST.ts';
14
-
15
- /** Comment type predicates and helpers. */
16
- export * as Comment from './Comment.ts';
17
-
18
- /** Structured diagnostic construction. */
19
- export * as Diagnostic from './Diagnostic.ts';
20
-
21
- /** Plugin definition and composition. */
22
- export * as Plugin from './Plugin.ts';
23
-
24
- /** Core rule builder — `Rule.define` is the primary entry point. */
25
- export * as Rule from './Rule.ts';
26
-
27
- /** Effect service wrapping the oxlint rule context. */
28
- export { RuleContext } from './RuleContext.ts';
29
- export {
30
- ast,
31
- cwd,
32
- filename,
33
- id,
34
- report,
35
- sourceCode,
36
- text
37
- } from './RuleContext.ts';
38
-
39
- /** Scope analysis helpers with Option. */
40
- export * as Scope from './Scope.ts';
41
-
42
- /** Effect-wrapped SourceCode queries with Option. */
43
- export * as SourceCode from './SourceCode.ts';
44
-
45
- /** Testing infrastructure — builders, runners, assertion helpers. */
46
- export * as Testing from './Testing.ts';
47
-
48
- /** Token type predicates and helpers. */
49
- export * as Token from './Token.ts';
50
-
51
- /** Composable visitor construction. */
52
- export * as Visitor from './Visitor.ts';
53
-
54
- // ---------------------------------------------------------------------------
55
- // Type re-exports from @oxlint/plugins
56
- // ---------------------------------------------------------------------------
57
-
58
- export type {
59
- Comment as OxlintComment,
60
- Context,
61
- CreateOnceRule,
62
- CreateRule,
63
- Definition,
64
- DefinitionType,
65
- ESTree,
66
- Fix,
67
- Fixer,
68
- FixFn,
69
- LineColumn,
70
- Location,
71
- Plugin as OxlintPlugin,
72
- Range,
73
- Ranged,
74
- Reference,
75
- Rule as OxlintRule,
76
- RuleDocs,
77
- RuleMeta,
78
- Scope as OxlintScope,
79
- ScopeManager,
80
- ScopeType,
81
- Settings,
82
- SourceCode as OxlintSourceCode,
83
- Span,
84
- Suggestion,
85
- Token as OxlintToken,
86
- Variable,
87
- Visitor as OxlintVisitor
88
- } from '@oxlint/plugins';
@@ -1,85 +0,0 @@
1
- import { describe, test, expect } from "bun:test"
2
- import { Testing } from "../src/vendor/effect-oxlint/index.js"
3
- import {
4
- noEffectDo,
5
- noEffectNever,
6
- noEffectAs,
7
- noEffectAsync,
8
- noEffectBind,
9
- noOptionAs,
10
- noRuntimeRunFork,
11
- noRunInEffect,
12
- } from "../src/rules/index.js"
13
-
14
- // --- API ban rules ---
15
-
16
- describe("noEffectDo", () => {
17
- test("reports Effect.Do", () => {
18
- const result = Testing.runRule(noEffectDo, "MemberExpression", Testing.memberExpr("Effect", "Do"))
19
- Testing.expectDiagnostics(result, [{ message: "Avoid Effect.Do builder notation. Use flat pipe-based flow or Effect.gen." }])
20
- })
21
-
22
- test("ignores Effect.map", () => {
23
- const result = Testing.runRule(noEffectDo, "MemberExpression", Testing.memberExpr("Effect", "map"))
24
- Testing.expectNoDiagnostics(result)
25
- })
26
- })
27
-
28
- describe("noEffectNever", () => {
29
- test("reports Effect.never", () => {
30
- const result = Testing.runRule(noEffectNever, "MemberExpression", Testing.memberExpr("Effect", "never"))
31
- Testing.expectDiagnostics(result, [{ message: "Avoid Effect.never. Use Stream or explicit acquire/release lifecycles." }])
32
- })
33
- })
34
-
35
- describe("noEffectAs", () => {
36
- test("reports Effect.as", () => {
37
- const result = Testing.runRule(noEffectAs, "MemberExpression", Testing.memberExpr("Effect", "as"))
38
- Testing.expectDiagnostics(result, [{ message: "Avoid Effect.as. Use Effect.map for value mapping or Effect.asVoid." }])
39
- })
40
- })
41
-
42
- describe("noEffectAsync", () => {
43
- test("reports Effect.async", () => {
44
- const result = Testing.runRule(noEffectAsync, "MemberExpression", Testing.memberExpr("Effect", "async"))
45
- expect(result.length).toBe(1)
46
- })
47
- })
48
-
49
- describe("noEffectBind", () => {
50
- test("reports Effect.bind", () => {
51
- const result = Testing.runRule(noEffectBind, "MemberExpression", Testing.memberExpr("Effect", "bind"))
52
- expect(result.length).toBe(1)
53
- })
54
- })
55
-
56
- describe("noOptionAs", () => {
57
- test("reports Option.as", () => {
58
- const result = Testing.runRule(noOptionAs, "MemberExpression", Testing.memberExpr("Option", "as"))
59
- expect(result.length).toBe(1)
60
- })
61
- })
62
-
63
- describe("noRuntimeRunFork", () => {
64
- test("reports Runtime.runFork", () => {
65
- const result = Testing.runRule(noRuntimeRunFork, "MemberExpression", Testing.memberExpr("Runtime", "runFork"))
66
- expect(result.length).toBe(1)
67
- })
68
- })
69
-
70
- describe("noRunInEffect", () => {
71
- test("reports Effect.runSync", () => {
72
- const result = Testing.runRule(noRunInEffect, "MemberExpression", Testing.memberExpr("Effect", "runSync"))
73
- expect(result.length).toBe(1)
74
- })
75
-
76
- test("reports Effect.runPromise", () => {
77
- const result = Testing.runRule(noRunInEffect, "MemberExpression", Testing.memberExpr("Effect", "runPromise"))
78
- expect(result.length).toBe(1)
79
- })
80
-
81
- test("ignores Effect.gen", () => {
82
- const result = Testing.runRule(noRunInEffect, "MemberExpression", Testing.memberExpr("Effect", "gen"))
83
- Testing.expectNoDiagnostics(result)
84
- })
85
- })