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.
- package/.effect-lsp.json +60 -0
- package/.oxlintrc.json +33 -0
- package/package.json +51 -0
- package/scripts/add-rule.ts +87 -0
- package/scripts/codegen.ts +108 -0
- package/src/index.ts +9 -0
- package/src/plugin.ts +16 -0
- package/src/presets/core.ts +36 -0
- package/src/presets/effect-native.ts +41 -0
- package/src/presets/full.ts +84 -0
- package/src/presets/index.ts +3 -0
- package/src/rules/_effect-context.ts +28 -0
- package/src/rules/index.ts +83 -0
- package/src/rules/no-arrow-ladder.ts +71 -0
- package/src/rules/no-async-function.ts +48 -0
- package/src/rules/no-console-in-effect.ts +46 -0
- package/src/rules/no-date-in-effect.ts +61 -0
- package/src/rules/no-dynamic-import.ts +13 -0
- package/src/rules/no-effect-as.ts +14 -0
- package/src/rules/no-effect-async.ts +14 -0
- package/src/rules/no-effect-bind.ts +13 -0
- package/src/rules/no-effect-do.ts +14 -0
- package/src/rules/no-effect-fn-generator.ts +59 -0
- package/src/rules/no-effect-gen-adapter.ts +51 -0
- package/src/rules/no-effect-map-void.ts +81 -0
- package/src/rules/no-effect-never.ts +14 -0
- package/src/rules/no-effect-orElse-ladder.ts +80 -0
- package/src/rules/no-effect-succeed-string.ts +53 -0
- package/src/rules/no-effect-succeed-variable.ts +51 -0
- package/src/rules/no-effect-succeed-void.ts +64 -0
- package/src/rules/no-effect-sync-console.ts +92 -0
- package/src/rules/no-effect-sync-wrapper.ts +68 -0
- package/src/rules/no-effect-type-alias.ts +72 -0
- package/src/rules/no-effect-wrapper-alias.ts +98 -0
- package/src/rules/no-extends-native-error.ts +55 -0
- package/src/rules/no-fetch-in-effect.ts +47 -0
- package/src/rules/no-flatmap-ladder.ts +79 -0
- package/src/rules/no-fromnullable-coalesce.ts +63 -0
- package/src/rules/no-global-console.ts +17 -0
- package/src/rules/no-global-date.ts +20 -0
- package/src/rules/no-global-fetch.ts +14 -0
- package/src/rules/no-global-random.ts +13 -0
- package/src/rules/no-global-timers.ts +17 -0
- package/src/rules/no-if-statement.ts +16 -0
- package/src/rules/no-iife-wrapper.ts +52 -0
- package/src/rules/no-inline-runtime-provide.ts +42 -0
- package/src/rules/no-instanceof-schema.ts +43 -0
- package/src/rules/no-json-in-effect.ts +45 -0
- package/src/rules/no-json-parse.ts +18 -0
- package/src/rules/no-manual-effect-channels.ts +59 -0
- package/src/rules/no-match-effect-branch.ts +83 -0
- package/src/rules/no-match-void-branch.ts +73 -0
- package/src/rules/no-nested-effect-call.ts +59 -0
- package/src/rules/no-nested-effect-gen.ts +44 -0
- package/src/rules/no-nested-pipe.ts +76 -0
- package/src/rules/no-new-error.ts +17 -0
- package/src/rules/no-new-promise.ts +14 -0
- package/src/rules/no-node-builtin-import.ts +70 -0
- package/src/rules/no-option-as.ts +13 -0
- package/src/rules/no-option-boolean-normalization.ts +89 -0
- package/src/rules/no-platform-globals.ts +117 -0
- package/src/rules/no-process-env-in-effect.ts +39 -0
- package/src/rules/no-process-env.ts +13 -0
- package/src/rules/no-random-in-effect.ts +39 -0
- package/src/rules/no-return-in-arrow.ts +77 -0
- package/src/rules/no-return-null.ts +47 -0
- package/src/rules/no-run-in-effect.ts +18 -0
- package/src/rules/no-runtime-run-fork.ts +14 -0
- package/src/rules/no-string-sentinel-const.ts +50 -0
- package/src/rules/no-switch-statement.ts +13 -0
- package/src/rules/no-ternary.ts +32 -0
- package/src/rules/no-throw-in-effect-gen.ts +42 -0
- package/src/rules/no-throw-statement.ts +13 -0
- package/src/rules/no-timers-in-effect.ts +47 -0
- package/src/rules/no-try-catch-in-effect-gen.ts +42 -0
- package/src/rules/no-try-catch.ts +14 -0
- package/src/rules/no-unnecessary-arrow-block.ts +52 -0
- package/src/rules/no-unnecessary-effect-gen.ts +85 -0
- package/src/rules/no-unnecessary-pipe.ts +46 -0
- package/src/vendor/effect-oxlint/AST.ts +516 -0
- package/src/vendor/effect-oxlint/Comment.ts +86 -0
- package/src/vendor/effect-oxlint/Diagnostic.ts +199 -0
- package/src/vendor/effect-oxlint/Plugin.ts +71 -0
- package/src/vendor/effect-oxlint/Rule.ts +590 -0
- package/src/vendor/effect-oxlint/RuleContext.ts +144 -0
- package/src/vendor/effect-oxlint/Scope.ts +177 -0
- package/src/vendor/effect-oxlint/SourceCode.ts +377 -0
- package/src/vendor/effect-oxlint/Testing.ts +1227 -0
- package/src/vendor/effect-oxlint/Token.ts +131 -0
- package/src/vendor/effect-oxlint/Visitor.ts +287 -0
- package/src/vendor/effect-oxlint/index.ts +88 -0
- package/tests/ban-rules.test.ts +252 -0
- package/tests/context-rules.test.ts +281 -0
- package/tests/pattern-rules.test.ts +258 -0
- 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
|
+
});
|