oxlint-plugin-effect 0.0.0

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 (95) hide show
  1. package/.effect-lsp.json +60 -0
  2. package/.oxlintrc.json +33 -0
  3. package/package.json +51 -0
  4. package/scripts/add-rule.ts +87 -0
  5. package/scripts/codegen.ts +108 -0
  6. package/src/index.ts +9 -0
  7. package/src/plugin.ts +16 -0
  8. package/src/presets/core.ts +36 -0
  9. package/src/presets/effect-native.ts +41 -0
  10. package/src/presets/full.ts +84 -0
  11. package/src/presets/index.ts +3 -0
  12. package/src/rules/_effect-context.ts +28 -0
  13. package/src/rules/index.ts +83 -0
  14. package/src/rules/no-arrow-ladder.ts +71 -0
  15. package/src/rules/no-async-function.ts +48 -0
  16. package/src/rules/no-console-in-effect.ts +46 -0
  17. package/src/rules/no-date-in-effect.ts +61 -0
  18. package/src/rules/no-dynamic-import.ts +13 -0
  19. package/src/rules/no-effect-as.ts +14 -0
  20. package/src/rules/no-effect-async.ts +14 -0
  21. package/src/rules/no-effect-bind.ts +13 -0
  22. package/src/rules/no-effect-do.ts +14 -0
  23. package/src/rules/no-effect-fn-generator.ts +59 -0
  24. package/src/rules/no-effect-gen-adapter.ts +51 -0
  25. package/src/rules/no-effect-map-void.ts +81 -0
  26. package/src/rules/no-effect-never.ts +14 -0
  27. package/src/rules/no-effect-orElse-ladder.ts +80 -0
  28. package/src/rules/no-effect-succeed-string.ts +53 -0
  29. package/src/rules/no-effect-succeed-variable.ts +51 -0
  30. package/src/rules/no-effect-succeed-void.ts +64 -0
  31. package/src/rules/no-effect-sync-console.ts +92 -0
  32. package/src/rules/no-effect-sync-wrapper.ts +68 -0
  33. package/src/rules/no-effect-type-alias.ts +72 -0
  34. package/src/rules/no-effect-wrapper-alias.ts +98 -0
  35. package/src/rules/no-extends-native-error.ts +55 -0
  36. package/src/rules/no-fetch-in-effect.ts +47 -0
  37. package/src/rules/no-flatmap-ladder.ts +79 -0
  38. package/src/rules/no-fromnullable-coalesce.ts +63 -0
  39. package/src/rules/no-global-console.ts +17 -0
  40. package/src/rules/no-global-date.ts +20 -0
  41. package/src/rules/no-global-fetch.ts +14 -0
  42. package/src/rules/no-global-random.ts +13 -0
  43. package/src/rules/no-global-timers.ts +17 -0
  44. package/src/rules/no-if-statement.ts +16 -0
  45. package/src/rules/no-iife-wrapper.ts +52 -0
  46. package/src/rules/no-inline-runtime-provide.ts +42 -0
  47. package/src/rules/no-instanceof-schema.ts +43 -0
  48. package/src/rules/no-json-in-effect.ts +45 -0
  49. package/src/rules/no-json-parse.ts +18 -0
  50. package/src/rules/no-manual-effect-channels.ts +59 -0
  51. package/src/rules/no-match-effect-branch.ts +83 -0
  52. package/src/rules/no-match-void-branch.ts +73 -0
  53. package/src/rules/no-nested-effect-call.ts +59 -0
  54. package/src/rules/no-nested-effect-gen.ts +44 -0
  55. package/src/rules/no-nested-pipe.ts +76 -0
  56. package/src/rules/no-new-error.ts +17 -0
  57. package/src/rules/no-new-promise.ts +14 -0
  58. package/src/rules/no-node-builtin-import.ts +70 -0
  59. package/src/rules/no-option-as.ts +13 -0
  60. package/src/rules/no-option-boolean-normalization.ts +89 -0
  61. package/src/rules/no-platform-globals.ts +117 -0
  62. package/src/rules/no-process-env-in-effect.ts +39 -0
  63. package/src/rules/no-process-env.ts +13 -0
  64. package/src/rules/no-random-in-effect.ts +39 -0
  65. package/src/rules/no-return-in-arrow.ts +77 -0
  66. package/src/rules/no-return-null.ts +47 -0
  67. package/src/rules/no-run-in-effect.ts +18 -0
  68. package/src/rules/no-runtime-run-fork.ts +14 -0
  69. package/src/rules/no-string-sentinel-const.ts +50 -0
  70. package/src/rules/no-switch-statement.ts +13 -0
  71. package/src/rules/no-ternary.ts +32 -0
  72. package/src/rules/no-throw-in-effect-gen.ts +42 -0
  73. package/src/rules/no-throw-statement.ts +13 -0
  74. package/src/rules/no-timers-in-effect.ts +47 -0
  75. package/src/rules/no-try-catch-in-effect-gen.ts +42 -0
  76. package/src/rules/no-try-catch.ts +14 -0
  77. package/src/rules/no-unnecessary-arrow-block.ts +52 -0
  78. package/src/rules/no-unnecessary-effect-gen.ts +85 -0
  79. package/src/rules/no-unnecessary-pipe.ts +46 -0
  80. package/src/vendor/effect-oxlint/AST.ts +516 -0
  81. package/src/vendor/effect-oxlint/Comment.ts +86 -0
  82. package/src/vendor/effect-oxlint/Diagnostic.ts +199 -0
  83. package/src/vendor/effect-oxlint/Plugin.ts +71 -0
  84. package/src/vendor/effect-oxlint/Rule.ts +590 -0
  85. package/src/vendor/effect-oxlint/RuleContext.ts +144 -0
  86. package/src/vendor/effect-oxlint/Scope.ts +177 -0
  87. package/src/vendor/effect-oxlint/SourceCode.ts +377 -0
  88. package/src/vendor/effect-oxlint/Testing.ts +1227 -0
  89. package/src/vendor/effect-oxlint/Token.ts +131 -0
  90. package/src/vendor/effect-oxlint/Visitor.ts +287 -0
  91. package/src/vendor/effect-oxlint/index.ts +88 -0
  92. package/tests/ban-rules.test.ts +252 -0
  93. package/tests/context-rules.test.ts +281 -0
  94. package/tests/pattern-rules.test.ts +258 -0
  95. package/tsconfig.json +15 -0
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Structured diagnostic construction for Effect-first lint rules.
3
+ *
4
+ * Wraps the oxlint `Diagnostic` type with builder helpers.
5
+ *
6
+ * @since 0.1.0
7
+ */
8
+ import type {
9
+ Diagnostic as OxlintDiagnostic,
10
+ DiagnosticData,
11
+ Fix as OxlintFix,
12
+ FixFn,
13
+ Ranged,
14
+ Suggestion
15
+ } from '@oxlint/plugins';
16
+ import * as Arr from 'effect/Array';
17
+ import { dual } from 'effect/Function';
18
+ import * as Option from 'effect/Option';
19
+ import * as P from 'effect/Predicate';
20
+ import * as Result from 'effect/Result';
21
+
22
+ // ---------------------------------------------------------------------------
23
+ // Re-exports
24
+ // ---------------------------------------------------------------------------
25
+
26
+ /**
27
+ * The raw oxlint diagnostic type.
28
+ *
29
+ * @since 0.1.0
30
+ */
31
+ export type { OxlintDiagnostic as Diagnostic };
32
+
33
+ // ---------------------------------------------------------------------------
34
+ // Constructors
35
+ // ---------------------------------------------------------------------------
36
+
37
+ /**
38
+ * Create a diagnostic with a message and node location.
39
+ *
40
+ * @since 0.1.0
41
+ */
42
+ export const make = (opts: {
43
+ readonly node: Ranged;
44
+ readonly message: string;
45
+ readonly data?: DiagnosticData;
46
+ }): OxlintDiagnostic => ({
47
+ node: opts.node,
48
+ message: opts.message,
49
+ data: opts.data
50
+ });
51
+
52
+ /**
53
+ * Create a diagnostic using a `messageId` from `meta.messages`.
54
+ *
55
+ * @since 0.1.0
56
+ */
57
+ export const fromId = (opts: {
58
+ readonly node: Ranged;
59
+ readonly messageId: string;
60
+ readonly data?: DiagnosticData;
61
+ }): OxlintDiagnostic => ({
62
+ node: opts.node,
63
+ messageId: opts.messageId,
64
+ data: opts.data
65
+ });
66
+
67
+ // ---------------------------------------------------------------------------
68
+ // Combinators
69
+ // ---------------------------------------------------------------------------
70
+
71
+ /**
72
+ * Attach an autofix function to a diagnostic.
73
+ *
74
+ * @since 0.1.0
75
+ */
76
+ export const withFix: {
77
+ (fix: FixFn): (diagnostic: OxlintDiagnostic) => OxlintDiagnostic;
78
+ (diagnostic: OxlintDiagnostic, fix: FixFn): OxlintDiagnostic;
79
+ } = dual(
80
+ 2,
81
+ (diagnostic: OxlintDiagnostic, fix: FixFn): OxlintDiagnostic => ({
82
+ ...diagnostic,
83
+ fix
84
+ })
85
+ );
86
+
87
+ /**
88
+ * Attach suggestion fixes to a diagnostic.
89
+ *
90
+ * @since 0.1.0
91
+ */
92
+ export const withSuggestions: {
93
+ (
94
+ suggestions: ReadonlyArray<Suggestion>
95
+ ): (diagnostic: OxlintDiagnostic) => OxlintDiagnostic;
96
+ (
97
+ diagnostic: OxlintDiagnostic,
98
+ suggestions: ReadonlyArray<Suggestion>
99
+ ): OxlintDiagnostic;
100
+ } = dual(
101
+ 2,
102
+ (
103
+ diagnostic: OxlintDiagnostic,
104
+ suggestions: ReadonlyArray<Suggestion>
105
+ ): OxlintDiagnostic => ({
106
+ ...diagnostic,
107
+ suggest: Array.from(suggestions)
108
+ })
109
+ );
110
+
111
+ // ---------------------------------------------------------------------------
112
+ // Fix helpers
113
+ // ---------------------------------------------------------------------------
114
+
115
+ /**
116
+ * Composable fix operations.
117
+ *
118
+ * Each function returns a `FixFn` that can be passed to `withFix`
119
+ * or composed via `composeFixes`.
120
+ *
121
+ * @since 0.1.0
122
+ */
123
+
124
+ /**
125
+ * Replace the text of a node or token.
126
+ *
127
+ * @since 0.1.0
128
+ */
129
+ export const replaceText =
130
+ (nodeOrToken: Ranged, text: string): FixFn =>
131
+ (fixer) =>
132
+ fixer.replaceText(nodeOrToken, text);
133
+
134
+ /**
135
+ * Insert text before a node or token.
136
+ *
137
+ * @since 0.1.0
138
+ */
139
+ export const insertBefore =
140
+ (nodeOrToken: Ranged, text: string): FixFn =>
141
+ (fixer) =>
142
+ fixer.insertTextBefore(nodeOrToken, text);
143
+
144
+ /**
145
+ * Insert text after a node or token.
146
+ *
147
+ * @since 0.1.0
148
+ */
149
+ export const insertAfter =
150
+ (nodeOrToken: Ranged, text: string): FixFn =>
151
+ (fixer) =>
152
+ fixer.insertTextAfter(nodeOrToken, text);
153
+
154
+ /**
155
+ * Remove a node or token.
156
+ *
157
+ * @since 0.1.0
158
+ */
159
+ export const removeFix =
160
+ (nodeOrToken: Ranged): FixFn =>
161
+ (fixer) =>
162
+ fixer.remove(nodeOrToken);
163
+
164
+ /** @internal Type guard for an oxlint Fix value (has `range` and `text`). */
165
+ const isFix = (value: unknown): value is OxlintFix =>
166
+ P.isObject(value) && 'range' in value && 'text' in value;
167
+
168
+ /**
169
+ * Extract fixes from a single `FixFn` result.
170
+ *
171
+ * The parameter is typed as `unknown` because oxlint's `FixFn` returns
172
+ * a nullable union (`Fix | Array<Fix | null> | IterableIterator<…> | null`).
173
+ * We guard at the boundary instead of mirroring the nullable upstream type.
174
+ *
175
+ * @internal
176
+ */
177
+ const extractFixes = (result: unknown): ReadonlyArray<OxlintFix> => {
178
+ if (result === null || result === undefined) return [];
179
+ if (isFix(result)) return [result];
180
+ if (!P.isIterable(result)) return [];
181
+ return Arr.filterMap(Array.from(result), (item) =>
182
+ Option.fromNullishOr(item).pipe(
183
+ Option.filter(isFix),
184
+ Result.fromOption(() => undefined)
185
+ )
186
+ );
187
+ };
188
+
189
+ /**
190
+ * Compose multiple fix functions into one.
191
+ *
192
+ * All individual fixes are collected into a single array result.
193
+ *
194
+ * @since 0.1.0
195
+ */
196
+ export const composeFixes =
197
+ (...fixes: ReadonlyArray<FixFn>): FixFn =>
198
+ (fixer) =>
199
+ Arr.flatMap(fixes, (fn) => Array.from(extractFixes(fn(fixer))));
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Plugin definition and composition for Effect-first oxlint plugins.
3
+ *
4
+ * @since 0.1.0
5
+ */
6
+ import type { CreateRule, Plugin as OxlintPlugin, Rule } from '@oxlint/plugins';
7
+ import * as Arr from 'effect/Array';
8
+
9
+ // ---------------------------------------------------------------------------
10
+ // Re-exports
11
+ // ---------------------------------------------------------------------------
12
+
13
+ /**
14
+ * The raw oxlint plugin type.
15
+ *
16
+ * @since 0.1.0
17
+ */
18
+ export type { OxlintPlugin as Plugin };
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // Constructor
22
+ // ---------------------------------------------------------------------------
23
+
24
+ /**
25
+ * Define a typed oxlint plugin from a name and rule map.
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * import { Plugin, Rule } from 'effect-oxlint'
30
+ *
31
+ * export default Plugin.define({
32
+ * name: 'my-effect-rules',
33
+ * rules: {
34
+ * 'no-throw-in-gen': myThrowRule,
35
+ * 'prefer-effect-fn': myFnRule,
36
+ * }
37
+ * })
38
+ * ```
39
+ *
40
+ * @since 0.1.0
41
+ */
42
+ export const define = (config: {
43
+ readonly name: string;
44
+ readonly rules: Record<string, CreateRule>;
45
+ }): OxlintPlugin => ({
46
+ meta: { name: config.name },
47
+ rules: config.rules
48
+ });
49
+
50
+ /**
51
+ * Merge multiple plugins into one.
52
+ *
53
+ * If two plugins define a rule with the same name, the later one wins.
54
+ *
55
+ * @since 0.1.0
56
+ */
57
+ export const merge = (
58
+ ...plugins: ReadonlyArray<OxlintPlugin>
59
+ ): OxlintPlugin => ({
60
+ meta: {
61
+ name: Arr.join(
62
+ Arr.map(plugins, (p) => p.meta?.name ?? 'unknown'),
63
+ '+'
64
+ )
65
+ },
66
+ rules: Arr.reduce<OxlintPlugin, Record<string, Rule>>(
67
+ plugins,
68
+ {},
69
+ (acc, p) => ({ ...acc, ...p.rules })
70
+ )
71
+ });