@isentinel/eslint-config 5.2.0 → 6.0.0-beta.10
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/README.md +159 -13
- package/dist/cli.d.mts +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +19241 -10582
- package/dist/index.mjs +14537 -3232
- package/dist/oxlint.d.mts +32104 -0
- package/dist/oxlint.mjs +4064 -0
- package/package.json +103 -74
package/dist/oxlint.mjs
ADDED
|
@@ -0,0 +1,4064 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { isPackageExists } from "local-pkg";
|
|
3
|
+
import { defineConfig } from "oxlint";
|
|
4
|
+
import { findUpSync } from "find-up-simple";
|
|
5
|
+
import fs, { readFileSync } from "node:fs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import process from "node:process";
|
|
8
|
+
import "prettier";
|
|
9
|
+
import { minVersion } from "semver";
|
|
10
|
+
import globals from "globals";
|
|
11
|
+
import { pathToFileURL } from "node:url";
|
|
12
|
+
//#region src/rules/oxlint-mapping.ts
|
|
13
|
+
/**
|
|
14
|
+
* Notable rule families that intentionally stay in ESLint in hybrid mode,
|
|
15
|
+
* with the reason why.
|
|
16
|
+
*/
|
|
17
|
+
const staysInEslint = {
|
|
18
|
+
"e18e/* (optionally type-aware)": "Four e18e rules (prefer-array-at, prefer-array-to-reversed, prefer-array-to-sorted, prefer-spread-syntax) run without type information but detect more with it, so they stay in ESLint (see optionallyTypeAwareRules); the rest run in oxlint as a jsPlugin",
|
|
19
|
+
"eslint-comments/*": "Lints eslint-disable directives, which only exist in ESLint-linted code; oxlint-comments covers oxlint directives",
|
|
20
|
+
"flawless/naming-convention": "Type-aware custom rule; oxlint jsPlugins have no type information. The syntax-only flawless rules (prefer-parameter-destructuring, the react jsx-shorthand-*, purity, no-unnecessary-use-*, prefer-destructuring-assignment) are mapped and run in oxlint. flawless/toml-* and flawless/yaml-* lint non-JS files and stay in ESLint",
|
|
21
|
+
"flawless/prefer-read-only-props": "Type-aware custom rule; oxlint jsPlugins have no type information",
|
|
22
|
+
"format-lua/*": "Oxlint cannot lint Lua files",
|
|
23
|
+
"jest/* (type-aware)": "Four type-aware jest rules stay in ESLint (see typeAwareJsPluginRules); the rest run in oxlint via eslint-plugin-jest as a jsPlugin (renamed jest-js, since oxlint reserves the native jest prefix), which honors settings.jest.globalPackage = @rbxts/jest-globals (the NATIVE oxlint jest plugin does not, https://github.com/oxc-project/oxc/issues/23290)",
|
|
24
|
+
"jsonc/yaml/toml/markdown/package-json/pnpm/sort-*": "Oxlint only lints JS/TS files; JSON, YAML, TOML and Markdown stay in ESLint",
|
|
25
|
+
"react/* (type-aware)": "The @eslint-react type-aware rules (no-implicit-children/key/ref, no-leaked-conditional-rendering, no-unused-props) need type information but do NOT declare requiresTypeChecking, so the metadata-based parity test cannot catch them; they are excluded manually and stay in ESLint",
|
|
26
|
+
"roblox/* (type-aware)": "Type-aware custom rules (lua-truthiness etc.); oxlint jsPlugins have no type information",
|
|
27
|
+
"sentinel/explicit-size-check": "Type-aware custom rule; oxlint jsPlugins have no type information",
|
|
28
|
+
"type-aware jsPlugin rules": "Rules whose meta.docs.requiresTypeChecking is true crash or silently no-op under oxlint's jsPlugin runtime (no type information): sonar/no-async-constructor, sonar/no-ignored-return, sonar/no-incompatible-assertion-types, sonar/no-redundant-optional, sonar/no-try-promise, sonar/prefer-immediate-return, unicorn/no-non-function-verb-prefix, arrow-style/no-export-default-arrow, eslint-plugin/no-property-in-node, jest/no-error-equal, jest/no-unnecessary-assertion, jest/unbound-method, jest/valid-expect-with-promise",
|
|
29
|
+
"unicorn/no-unsafe-string-replacement": "False positives under oxlint's jsPlugin scope analysis (template-literal replacements are not resolved)"
|
|
30
|
+
};
|
|
31
|
+
const oxlintRuleMapping = {
|
|
32
|
+
"accessor-pairs": "native",
|
|
33
|
+
"array-callback-return": "native",
|
|
34
|
+
"block-scoped-var": "native",
|
|
35
|
+
"constructor-super": "native",
|
|
36
|
+
"curly": "native",
|
|
37
|
+
"default-case-last": "native",
|
|
38
|
+
"eqeqeq": "native",
|
|
39
|
+
"for-direction": "native",
|
|
40
|
+
"func-style": "native",
|
|
41
|
+
"logical-assignment-operators": "native",
|
|
42
|
+
"max-classes-per-file": "native",
|
|
43
|
+
"max-depth": "native",
|
|
44
|
+
"max-lines": "native",
|
|
45
|
+
"max-lines-per-function": "native",
|
|
46
|
+
"new-cap": "native",
|
|
47
|
+
"no-alert": "native",
|
|
48
|
+
"no-array-constructor": "native",
|
|
49
|
+
"no-async-promise-executor": "native",
|
|
50
|
+
"no-caller": "native",
|
|
51
|
+
"no-case-declarations": "native",
|
|
52
|
+
"no-class-assign": "native",
|
|
53
|
+
"no-compare-neg-zero": "native",
|
|
54
|
+
"no-cond-assign": "native",
|
|
55
|
+
"no-console": "native",
|
|
56
|
+
"no-const-assign": "native",
|
|
57
|
+
"no-constant-condition": "native",
|
|
58
|
+
"no-control-regex": "native",
|
|
59
|
+
"no-debugger": "native",
|
|
60
|
+
"no-delete-var": "native",
|
|
61
|
+
"no-dupe-class-members": "native",
|
|
62
|
+
"no-dupe-keys": "native",
|
|
63
|
+
"no-duplicate-case": "native",
|
|
64
|
+
"no-duplicate-imports": "native",
|
|
65
|
+
"no-else-return": "native",
|
|
66
|
+
"no-empty": "native",
|
|
67
|
+
"no-empty-character-class": "native",
|
|
68
|
+
"no-empty-function": "native",
|
|
69
|
+
"no-empty-pattern": "native",
|
|
70
|
+
"no-empty-static-block": "native",
|
|
71
|
+
"no-eval": "native",
|
|
72
|
+
"no-ex-assign": "native",
|
|
73
|
+
"no-extend-native": "native",
|
|
74
|
+
"no-extra-bind": "native",
|
|
75
|
+
"no-extra-boolean-cast": "native",
|
|
76
|
+
"no-fallthrough": "native",
|
|
77
|
+
"no-func-assign": "native",
|
|
78
|
+
"no-global-assign": "native",
|
|
79
|
+
"no-implied-eval": "native",
|
|
80
|
+
"no-import-assign": "native",
|
|
81
|
+
"no-inline-comments": "native",
|
|
82
|
+
"no-invalid-regexp": "native",
|
|
83
|
+
"no-irregular-whitespace": "native",
|
|
84
|
+
"no-iterator": "native",
|
|
85
|
+
"no-labels": "native",
|
|
86
|
+
"no-lone-blocks": "native",
|
|
87
|
+
"no-lonely-if": "native",
|
|
88
|
+
"no-loss-of-precision": "native",
|
|
89
|
+
"no-misleading-character-class": "native",
|
|
90
|
+
"no-multi-str": "native",
|
|
91
|
+
"no-new": "native",
|
|
92
|
+
"no-new-func": "native",
|
|
93
|
+
"no-new-native-nonconstructor": "native",
|
|
94
|
+
"no-new-wrappers": "native",
|
|
95
|
+
"no-obj-calls": "native",
|
|
96
|
+
"no-proto": "native",
|
|
97
|
+
"no-prototype-builtins": "native",
|
|
98
|
+
"no-redeclare": "native",
|
|
99
|
+
"no-regex-spaces": "native",
|
|
100
|
+
"no-restricted-globals": "native",
|
|
101
|
+
"no-restricted-properties": "native",
|
|
102
|
+
"no-return-assign": "native",
|
|
103
|
+
"no-self-assign": "native",
|
|
104
|
+
"no-self-compare": "native",
|
|
105
|
+
"no-sequences": "native",
|
|
106
|
+
"no-shadow": "native",
|
|
107
|
+
"no-shadow-restricted-names": "native",
|
|
108
|
+
"no-sparse-arrays": "native",
|
|
109
|
+
"no-template-curly-in-string": "native",
|
|
110
|
+
"no-this-before-super": "native",
|
|
111
|
+
"no-throw-literal": "native",
|
|
112
|
+
"no-undef": "native",
|
|
113
|
+
"no-unexpected-multiline": "native",
|
|
114
|
+
"no-unmodified-loop-condition": "native",
|
|
115
|
+
"no-unneeded-ternary": "native",
|
|
116
|
+
"no-unreachable": "native",
|
|
117
|
+
"no-unreachable-loop": "native",
|
|
118
|
+
"no-unsafe-finally": "native",
|
|
119
|
+
"no-unsafe-negation": "native",
|
|
120
|
+
"no-unsafe-optional-chaining": "native",
|
|
121
|
+
"no-unused-expressions": "native",
|
|
122
|
+
"no-unused-private-class-members": "native",
|
|
123
|
+
"no-use-before-define": "native",
|
|
124
|
+
"no-useless-backreference": "native",
|
|
125
|
+
"no-useless-call": "native",
|
|
126
|
+
"no-useless-catch": "native",
|
|
127
|
+
"no-useless-computed-key": "native",
|
|
128
|
+
"no-useless-constructor": "native",
|
|
129
|
+
"no-useless-rename": "native",
|
|
130
|
+
"no-useless-return": "native",
|
|
131
|
+
"no-var": "native",
|
|
132
|
+
"no-with": "native",
|
|
133
|
+
"object-shorthand": "native",
|
|
134
|
+
"prefer-arrow-callback": "native",
|
|
135
|
+
"prefer-const": "native",
|
|
136
|
+
"prefer-exponentiation-operator": "native",
|
|
137
|
+
"prefer-promise-reject-errors": "native",
|
|
138
|
+
"prefer-regex-literals": "native",
|
|
139
|
+
"prefer-rest-params": "native",
|
|
140
|
+
"prefer-spread": "native",
|
|
141
|
+
"prefer-template": "native",
|
|
142
|
+
"symbol-description": "native",
|
|
143
|
+
"unicode-bom": "native",
|
|
144
|
+
"use-isnan": "native",
|
|
145
|
+
"valid-typeof": "native",
|
|
146
|
+
"vars-on-top": "native",
|
|
147
|
+
"yoda": "native",
|
|
148
|
+
"dot-notation": "js-plugin",
|
|
149
|
+
"id-length": "js-plugin",
|
|
150
|
+
"no-dupe-args": "js-plugin",
|
|
151
|
+
"no-octal": "js-plugin",
|
|
152
|
+
"no-octal-escape": "js-plugin",
|
|
153
|
+
"no-restricted-syntax": "js-plugin",
|
|
154
|
+
"no-undef-init": "js-plugin",
|
|
155
|
+
"one-var": "js-plugin",
|
|
156
|
+
"ts/default-param-last": "native",
|
|
157
|
+
"ts/no-empty-function": "native",
|
|
158
|
+
"ts/no-shadow": "native",
|
|
159
|
+
"ts/no-unused-expressions": "native",
|
|
160
|
+
"ts/no-unused-private-class-members": "native",
|
|
161
|
+
"ts/no-useless-constructor": "native",
|
|
162
|
+
"ts/prefer-destructuring": "native",
|
|
163
|
+
"ts/array-type": "native",
|
|
164
|
+
"ts/ban-ts-comment": "native",
|
|
165
|
+
"ts/consistent-generic-constructors": "native",
|
|
166
|
+
"ts/consistent-indexed-object-style": "native",
|
|
167
|
+
"ts/consistent-type-assertions": "native",
|
|
168
|
+
"ts/consistent-type-definitions": "native",
|
|
169
|
+
"ts/consistent-type-imports": "native",
|
|
170
|
+
"ts/explicit-function-return-type": "native",
|
|
171
|
+
"ts/explicit-member-accessibility": "native",
|
|
172
|
+
"ts/no-confusing-non-null-assertion": "native",
|
|
173
|
+
"ts/no-duplicate-enum-values": "native",
|
|
174
|
+
"ts/no-empty-object-type": "native",
|
|
175
|
+
"ts/no-extra-non-null-assertion": "native",
|
|
176
|
+
"ts/no-extraneous-class": "native",
|
|
177
|
+
"ts/no-import-type-side-effects": "native",
|
|
178
|
+
"ts/no-inferrable-types": "native",
|
|
179
|
+
"ts/no-misused-new": "native",
|
|
180
|
+
"ts/no-non-null-asserted-nullish-coalescing": "native",
|
|
181
|
+
"ts/no-non-null-asserted-optional-chain": "native",
|
|
182
|
+
"ts/no-non-null-assertion": "native",
|
|
183
|
+
"ts/no-require-imports": "native",
|
|
184
|
+
"ts/no-this-alias": "native",
|
|
185
|
+
"ts/no-unnecessary-parameter-property-assignment": "native",
|
|
186
|
+
"ts/no-unnecessary-type-constraint": "native",
|
|
187
|
+
"ts/no-unsafe-declaration-merging": "native",
|
|
188
|
+
"ts/no-unsafe-function-type": "native",
|
|
189
|
+
"ts/no-wrapper-object-types": "native",
|
|
190
|
+
"ts/prefer-as-const": "native",
|
|
191
|
+
"ts/prefer-for-of": "native",
|
|
192
|
+
"ts/prefer-function-type": "native",
|
|
193
|
+
"ts/prefer-literal-enum-member": "native",
|
|
194
|
+
"ts/prefer-namespace-keyword": "native",
|
|
195
|
+
"ts/await-thenable": "tsgolint",
|
|
196
|
+
"ts/dot-notation": "tsgolint",
|
|
197
|
+
"ts/no-confusing-void-expression": "tsgolint",
|
|
198
|
+
"ts/no-deprecated": "tsgolint",
|
|
199
|
+
"ts/no-duplicate-type-constituents": "tsgolint",
|
|
200
|
+
"ts/no-floating-promises": "tsgolint",
|
|
201
|
+
"ts/no-for-in-array": "tsgolint",
|
|
202
|
+
"ts/no-implied-eval": "tsgolint",
|
|
203
|
+
"ts/no-meaningless-void-operator": "tsgolint",
|
|
204
|
+
"ts/no-misused-promises": "tsgolint",
|
|
205
|
+
"ts/no-mixed-enums": "tsgolint",
|
|
206
|
+
"ts/no-redundant-type-constituents": "tsgolint",
|
|
207
|
+
"ts/no-unnecessary-boolean-literal-compare": "tsgolint",
|
|
208
|
+
"ts/no-unnecessary-condition": "tsgolint",
|
|
209
|
+
"ts/no-unnecessary-qualifier": "tsgolint",
|
|
210
|
+
"ts/no-unnecessary-template-expression": "tsgolint",
|
|
211
|
+
"ts/no-unnecessary-type-arguments": "tsgolint",
|
|
212
|
+
"ts/no-unnecessary-type-assertion": "tsgolint",
|
|
213
|
+
"ts/no-unnecessary-type-parameters": "tsgolint",
|
|
214
|
+
"ts/no-unsafe-argument": "tsgolint",
|
|
215
|
+
"ts/no-unsafe-assignment": "tsgolint",
|
|
216
|
+
"ts/no-unsafe-call": "tsgolint",
|
|
217
|
+
"ts/no-unsafe-enum-comparison": "tsgolint",
|
|
218
|
+
"ts/no-unsafe-member-access": "tsgolint",
|
|
219
|
+
"ts/no-unsafe-return": "tsgolint",
|
|
220
|
+
"ts/no-unsafe-unary-minus": "tsgolint",
|
|
221
|
+
"ts/no-useless-default-assignment": "tsgolint",
|
|
222
|
+
"ts/non-nullable-type-assertion-style": "tsgolint",
|
|
223
|
+
"ts/only-throw-error": "tsgolint",
|
|
224
|
+
"ts/prefer-find": "tsgolint",
|
|
225
|
+
"ts/prefer-includes": "tsgolint",
|
|
226
|
+
"ts/prefer-nullish-coalescing": "tsgolint",
|
|
227
|
+
"ts/prefer-optional-chain": "tsgolint",
|
|
228
|
+
"ts/prefer-promise-reject-errors": "tsgolint",
|
|
229
|
+
"ts/prefer-readonly": "tsgolint",
|
|
230
|
+
"ts/prefer-reduce-type-parameter": "tsgolint",
|
|
231
|
+
"ts/prefer-return-this-type": "tsgolint",
|
|
232
|
+
"ts/promise-function-async": "tsgolint",
|
|
233
|
+
"ts/restrict-plus-operands": "tsgolint",
|
|
234
|
+
"ts/return-await": "tsgolint",
|
|
235
|
+
"ts/strict-boolean-expressions": "tsgolint",
|
|
236
|
+
"ts/strict-void-return": "tsgolint",
|
|
237
|
+
"ts/switch-exhaustiveness-check": "tsgolint",
|
|
238
|
+
"ts/unbound-method": "tsgolint",
|
|
239
|
+
"ts/use-unknown-in-catch-callback-variable": "tsgolint",
|
|
240
|
+
"unicorn/catch-error-name": "native",
|
|
241
|
+
"unicorn/consistent-function-scoping": "native",
|
|
242
|
+
"unicorn/consistent-template-literal-escape": "native",
|
|
243
|
+
"unicorn/filename-case": "native",
|
|
244
|
+
"unicorn/no-array-fill-with-reference-type": "native",
|
|
245
|
+
"unicorn/no-await-expression-member": "native",
|
|
246
|
+
"unicorn/no-empty-file": "native",
|
|
247
|
+
"unicorn/no-for-each": "native",
|
|
248
|
+
"unicorn/no-immediate-mutation": "native",
|
|
249
|
+
"unicorn/no-lonely-if": "native",
|
|
250
|
+
"unicorn/no-negation-in-equality-check": "native",
|
|
251
|
+
"unicorn/no-object-as-default-parameter": "native",
|
|
252
|
+
"unicorn/no-single-promise-in-promise-methods": "native",
|
|
253
|
+
"unicorn/no-static-only-class": "native",
|
|
254
|
+
"unicorn/no-unreadable-array-destructuring": "native",
|
|
255
|
+
"unicorn/no-useless-collection-argument": "native",
|
|
256
|
+
"unicorn/no-useless-iterator-to-array": "native",
|
|
257
|
+
"unicorn/no-useless-promise-resolve-reject": "native",
|
|
258
|
+
"unicorn/no-useless-undefined": "native",
|
|
259
|
+
"unicorn/number-literal-case": "native",
|
|
260
|
+
"unicorn/prefer-array-flat-map": "native",
|
|
261
|
+
"unicorn/prefer-default-parameters": "native",
|
|
262
|
+
"unicorn/prefer-export-from": "native",
|
|
263
|
+
"unicorn/prefer-includes": "native",
|
|
264
|
+
"unicorn/prefer-logical-operator-over-ternary": "native",
|
|
265
|
+
"unicorn/prefer-optional-catch-binding": "native",
|
|
266
|
+
"unicorn/prefer-set-has": "native",
|
|
267
|
+
"unicorn/prefer-single-call": "native",
|
|
268
|
+
"unicorn/prefer-ternary": "native",
|
|
269
|
+
"unicorn/switch-case-braces": "native",
|
|
270
|
+
"unicorn/throw-new-error": "native",
|
|
271
|
+
"unicorn/consistent-compound-words": "js-plugin",
|
|
272
|
+
"unicorn/consistent-conditional-object-spread": "js-plugin",
|
|
273
|
+
"unicorn/consistent-destructuring": "js-plugin",
|
|
274
|
+
"unicorn/consistent-json-file-read": "js-plugin",
|
|
275
|
+
"unicorn/consistent-tuple-labels": "js-plugin",
|
|
276
|
+
"unicorn/isolated-functions": "js-plugin",
|
|
277
|
+
"unicorn/name-replacements": "js-plugin",
|
|
278
|
+
"unicorn/no-array-concat-in-loop": "js-plugin",
|
|
279
|
+
"unicorn/no-array-sort-for-min-max": "js-plugin",
|
|
280
|
+
"unicorn/no-async-promise-finally": "js-plugin",
|
|
281
|
+
"unicorn/no-boolean-sort-comparator": "js-plugin",
|
|
282
|
+
"unicorn/no-break-in-nested-loop": "js-plugin",
|
|
283
|
+
"unicorn/no-confusing-array-splice": "js-plugin",
|
|
284
|
+
"unicorn/no-constant-zero-expression": "js-plugin",
|
|
285
|
+
"unicorn/no-declarations-before-early-exit": "js-plugin",
|
|
286
|
+
"unicorn/no-double-comparison": "js-plugin",
|
|
287
|
+
"unicorn/no-duplicate-loops": "js-plugin",
|
|
288
|
+
"unicorn/no-duplicate-set-values": "js-plugin",
|
|
289
|
+
"unicorn/no-error-property-assignment": "js-plugin",
|
|
290
|
+
"unicorn/no-exports-in-scripts": "js-plugin",
|
|
291
|
+
"unicorn/no-for-loop": "js-plugin",
|
|
292
|
+
"unicorn/no-global-object-property-assignment": "js-plugin",
|
|
293
|
+
"unicorn/no-impossible-length-comparison": "js-plugin",
|
|
294
|
+
"unicorn/no-incorrect-template-string-interpolation": "js-plugin",
|
|
295
|
+
"unicorn/no-invalid-character-comparison": "js-plugin",
|
|
296
|
+
"unicorn/no-invalid-well-known-symbol-methods": "js-plugin",
|
|
297
|
+
"unicorn/no-keyword-prefix": "js-plugin",
|
|
298
|
+
"unicorn/no-loop-iterable-mutation": "js-plugin",
|
|
299
|
+
"unicorn/no-mismatched-map-key": "js-plugin",
|
|
300
|
+
"unicorn/no-misrefactored-assignment": "js-plugin",
|
|
301
|
+
"unicorn/no-negated-array-predicate": "js-plugin",
|
|
302
|
+
"unicorn/no-object-methods-with-collections": "js-plugin",
|
|
303
|
+
"unicorn/no-optional-chaining-on-undeclared-variable": "js-plugin",
|
|
304
|
+
"unicorn/no-redundant-comparison": "js-plugin",
|
|
305
|
+
"unicorn/no-return-array-push": "js-plugin",
|
|
306
|
+
"unicorn/no-subtraction-comparison": "js-plugin",
|
|
307
|
+
"unicorn/no-unnecessary-fetch-options": "js-plugin",
|
|
308
|
+
"unicorn/no-unnecessary-global-this": "js-plugin",
|
|
309
|
+
"unicorn/no-unnecessary-string-trim": "js-plugin",
|
|
310
|
+
"unicorn/no-unreadable-for-of-expression": "js-plugin",
|
|
311
|
+
"unicorn/no-unreadable-new-expression": "js-plugin",
|
|
312
|
+
"unicorn/no-unsafe-property-key": "js-plugin",
|
|
313
|
+
"unicorn/no-unused-properties": "js-plugin",
|
|
314
|
+
"unicorn/no-useless-coercion": "js-plugin",
|
|
315
|
+
"unicorn/no-useless-compound-assignment": "js-plugin",
|
|
316
|
+
"unicorn/no-useless-concat": "js-plugin",
|
|
317
|
+
"unicorn/no-useless-delete-check": "js-plugin",
|
|
318
|
+
"unicorn/no-useless-logical-operand": "js-plugin",
|
|
319
|
+
"unicorn/no-useless-re-export": "js-plugin",
|
|
320
|
+
"unicorn/no-useless-recursion": "js-plugin",
|
|
321
|
+
"unicorn/no-xor-as-exponentiation": "js-plugin",
|
|
322
|
+
"unicorn/operator-assignment": "js-plugin",
|
|
323
|
+
"unicorn/prefer-abort-signal-any": "js-plugin",
|
|
324
|
+
"unicorn/prefer-array-from-map": "js-plugin",
|
|
325
|
+
"unicorn/prefer-array-from-range": "js-plugin",
|
|
326
|
+
"unicorn/prefer-array-iterable-methods": "js-plugin",
|
|
327
|
+
"unicorn/prefer-array-slice": "js-plugin",
|
|
328
|
+
"unicorn/prefer-block-statement-over-iife": "js-plugin",
|
|
329
|
+
"unicorn/prefer-continue": "js-plugin",
|
|
330
|
+
"unicorn/prefer-direct-iteration": "js-plugin",
|
|
331
|
+
"unicorn/prefer-else-if": "js-plugin",
|
|
332
|
+
"unicorn/prefer-flat-math-min-max": "js-plugin",
|
|
333
|
+
"unicorn/prefer-global-number-constants": "js-plugin",
|
|
334
|
+
"unicorn/prefer-group-by": "js-plugin",
|
|
335
|
+
"unicorn/prefer-has-check": "js-plugin",
|
|
336
|
+
"unicorn/prefer-hoisting-branch-code": "js-plugin",
|
|
337
|
+
"unicorn/prefer-identifier-import-export-specifiers": "js-plugin",
|
|
338
|
+
"unicorn/prefer-iterator-helpers": "js-plugin",
|
|
339
|
+
"unicorn/prefer-math-constants": "js-plugin",
|
|
340
|
+
"unicorn/prefer-simple-condition-first": "js-plugin",
|
|
341
|
+
"unicorn/prefer-simplified-conditions": "js-plugin",
|
|
342
|
+
"unicorn/prefer-split-limit": "js-plugin",
|
|
343
|
+
"unicorn/prefer-string-match-all": "js-plugin",
|
|
344
|
+
"unicorn/prefer-string-pad-start-end": "js-plugin",
|
|
345
|
+
"unicorn/prefer-string-repeat": "js-plugin",
|
|
346
|
+
"unicorn/prefer-switch": "js-plugin",
|
|
347
|
+
"unicorn/prefer-then-catch": "js-plugin",
|
|
348
|
+
"unicorn/prefer-unary-minus": "js-plugin",
|
|
349
|
+
"unicorn/require-passive-events": "js-plugin",
|
|
350
|
+
"import/first": "native",
|
|
351
|
+
"import/newline-after-import": "native",
|
|
352
|
+
"import/no-mutable-exports": "native",
|
|
353
|
+
"import/no-named-default": "native",
|
|
354
|
+
"promise/always-return": "native",
|
|
355
|
+
"promise/catch-or-return": "native",
|
|
356
|
+
"promise/no-multiple-resolved": "native",
|
|
357
|
+
"promise/no-nesting": "native",
|
|
358
|
+
"promise/no-promise-in-callback": "native",
|
|
359
|
+
"promise/no-return-in-finally": "native",
|
|
360
|
+
"promise/no-return-wrap": "native",
|
|
361
|
+
"promise/param-names": "native",
|
|
362
|
+
"jsdoc/check-access": "native",
|
|
363
|
+
"jsdoc/check-property-names": "native",
|
|
364
|
+
"jsdoc/empty-tags": "native",
|
|
365
|
+
"jsdoc/implements-on-classes": "native",
|
|
366
|
+
"jsdoc/no-defaults": "native",
|
|
367
|
+
"jsdoc/require-param": "native",
|
|
368
|
+
"jsdoc/require-param-description": "native",
|
|
369
|
+
"jsdoc/require-param-name": "native",
|
|
370
|
+
"jsdoc/require-property": "native",
|
|
371
|
+
"jsdoc/require-property-description": "native",
|
|
372
|
+
"jsdoc/require-property-name": "native",
|
|
373
|
+
"jsdoc/require-returns": "native",
|
|
374
|
+
"jsdoc/require-returns-description": "native",
|
|
375
|
+
"jsdoc/check-alignment": "js-plugin",
|
|
376
|
+
"jsdoc/check-param-names": "js-plugin",
|
|
377
|
+
"jsdoc/check-types": "js-plugin",
|
|
378
|
+
"jsdoc/convert-to-jsdoc-comments": "js-plugin",
|
|
379
|
+
"jsdoc/informative-docs": "js-plugin",
|
|
380
|
+
"jsdoc/multiline-blocks": "js-plugin",
|
|
381
|
+
"jsdoc/no-blank-block-descriptions": "js-plugin",
|
|
382
|
+
"jsdoc/no-blank-blocks": "js-plugin",
|
|
383
|
+
"jsdoc/no-multi-asterisks": "js-plugin",
|
|
384
|
+
"jsdoc/no-types": "js-plugin",
|
|
385
|
+
"jsdoc/no-undefined-types": "js-plugin",
|
|
386
|
+
"jsdoc/require-asterisk-prefix": "js-plugin",
|
|
387
|
+
"jsdoc/require-description": "js-plugin",
|
|
388
|
+
"jsdoc/require-description-complete-sentence": "js-plugin",
|
|
389
|
+
"jsdoc/require-hyphen-before-param-description": "js-plugin",
|
|
390
|
+
"jsdoc/require-rejects": "js-plugin",
|
|
391
|
+
"jsdoc/require-returns-check": "js-plugin",
|
|
392
|
+
"jsdoc/require-template": "js-plugin",
|
|
393
|
+
"jsdoc/require-yields-check": "js-plugin",
|
|
394
|
+
"node/handle-callback-err": "native",
|
|
395
|
+
"node/no-exports-assign": "native",
|
|
396
|
+
"node/no-new-require": "native",
|
|
397
|
+
"node/no-path-concat": "native",
|
|
398
|
+
"node/no-deprecated-api": "js-plugin",
|
|
399
|
+
"node/prefer-global/buffer": "js-plugin",
|
|
400
|
+
"node/prefer-global/process": "js-plugin",
|
|
401
|
+
"node/prefer-node-protocol": "js-plugin",
|
|
402
|
+
"node/process-exit-as-throw": "js-plugin",
|
|
403
|
+
"sonar/bool-param-default": "js-plugin",
|
|
404
|
+
"sonar/cognitive-complexity": "js-plugin",
|
|
405
|
+
"sonar/constructor-for-side-effects": "js-plugin",
|
|
406
|
+
"sonar/destructuring-assignment-syntax": "js-plugin",
|
|
407
|
+
"sonar/file-name-differ-from-class": "js-plugin",
|
|
408
|
+
"sonar/fixme-tag": "js-plugin",
|
|
409
|
+
"sonar/max-switch-cases": "js-plugin",
|
|
410
|
+
"sonar/misplaced-loop-counter": "js-plugin",
|
|
411
|
+
"sonar/no-collapsible-if": "js-plugin",
|
|
412
|
+
"sonar/no-dead-store": "js-plugin",
|
|
413
|
+
"sonar/no-default-utility-imports": "js-plugin",
|
|
414
|
+
"sonar/no-duplicate-string": "js-plugin",
|
|
415
|
+
"sonar/no-duplicated-branches": "js-plugin",
|
|
416
|
+
"sonar/no-element-overwrite": "js-plugin",
|
|
417
|
+
"sonar/no-empty-collection": "js-plugin",
|
|
418
|
+
"sonar/no-floating-point-equality": "js-plugin",
|
|
419
|
+
"sonar/no-identical-conditions": "js-plugin",
|
|
420
|
+
"sonar/no-identical-expressions": "js-plugin",
|
|
421
|
+
"sonar/no-identical-functions": "js-plugin",
|
|
422
|
+
"sonar/no-inverted-boolean-check": "js-plugin",
|
|
423
|
+
"sonar/no-mixed-completion-style": "js-plugin",
|
|
424
|
+
"sonar/no-nested-conditional": "js-plugin",
|
|
425
|
+
"sonar/no-nested-incdec": "js-plugin",
|
|
426
|
+
"sonar/no-nested-switch": "js-plugin",
|
|
427
|
+
"sonar/no-nested-template-literals": "js-plugin",
|
|
428
|
+
"sonar/no-parameter-reassignment": "js-plugin",
|
|
429
|
+
"sonar/no-redundant-boolean": "js-plugin",
|
|
430
|
+
"sonar/no-redundant-jump": "js-plugin",
|
|
431
|
+
"sonar/no-trivial-assertions": "js-plugin",
|
|
432
|
+
"sonar/no-unthrown-error": "js-plugin",
|
|
433
|
+
"sonar/no-unused-collection": "js-plugin",
|
|
434
|
+
"sonar/no-use-of-empty-return-value": "js-plugin",
|
|
435
|
+
"sonar/no-useless-catch": "js-plugin",
|
|
436
|
+
"sonar/no-useless-increment": "js-plugin",
|
|
437
|
+
"sonar/non-existent-operator": "js-plugin",
|
|
438
|
+
"sonar/prefer-object-literal": "js-plugin",
|
|
439
|
+
"sonar/prefer-promise-shorthand": "js-plugin",
|
|
440
|
+
"sonar/prefer-single-boolean-return": "js-plugin",
|
|
441
|
+
"sonar/prefer-while": "js-plugin",
|
|
442
|
+
"sonar/public-static-readonly": "js-plugin",
|
|
443
|
+
"sonar/super-linear-regex": "js-plugin",
|
|
444
|
+
"sonar/synchronous-suite-callback": "js-plugin",
|
|
445
|
+
"sonar/todo-tag": "js-plugin",
|
|
446
|
+
"sonar/use-type-alias": "js-plugin",
|
|
447
|
+
"perfectionist/sort-array-includes": "js-plugin",
|
|
448
|
+
"perfectionist/sort-classes": "js-plugin",
|
|
449
|
+
"perfectionist/sort-decorators": "js-plugin",
|
|
450
|
+
"perfectionist/sort-enums": "js-plugin",
|
|
451
|
+
"perfectionist/sort-exports": "js-plugin",
|
|
452
|
+
"perfectionist/sort-heritage-clauses": "js-plugin",
|
|
453
|
+
"perfectionist/sort-interfaces": "js-plugin",
|
|
454
|
+
"perfectionist/sort-intersection-types": "js-plugin",
|
|
455
|
+
"perfectionist/sort-jsx-props": "js-plugin",
|
|
456
|
+
"perfectionist/sort-maps": "js-plugin",
|
|
457
|
+
"perfectionist/sort-modules": "js-plugin",
|
|
458
|
+
"perfectionist/sort-named-imports": "js-plugin",
|
|
459
|
+
"perfectionist/sort-object-types": "js-plugin",
|
|
460
|
+
"perfectionist/sort-objects": "js-plugin",
|
|
461
|
+
"perfectionist/sort-sets": "js-plugin",
|
|
462
|
+
"perfectionist/sort-switch-case": "js-plugin",
|
|
463
|
+
"perfectionist/sort-union-types": "js-plugin",
|
|
464
|
+
"perfectionist/sort-variable-declarations": "js-plugin",
|
|
465
|
+
"@cspell/spellchecker": "js-plugin",
|
|
466
|
+
"antfu/import-dedupe": "js-plugin",
|
|
467
|
+
"antfu/no-import-dist": "js-plugin",
|
|
468
|
+
"antfu/no-import-node-modules-by-path": "js-plugin",
|
|
469
|
+
"antfu/no-top-level-await": "js-plugin",
|
|
470
|
+
"antfu/top-level-function": "js-plugin",
|
|
471
|
+
"better-max-params/better-max-params": "js-plugin",
|
|
472
|
+
"de-morgan/no-negated-conjunction": "js-plugin",
|
|
473
|
+
"de-morgan/no-negated-disjunction": "js-plugin",
|
|
474
|
+
"unused-imports/no-unused-imports": "js-plugin",
|
|
475
|
+
"unused-imports/no-unused-vars": "js-plugin",
|
|
476
|
+
"e18e/ban-dependencies": "js-plugin",
|
|
477
|
+
"e18e/no-spread-in-reduce": "js-plugin",
|
|
478
|
+
"e18e/prefer-array-fill": "js-plugin",
|
|
479
|
+
"e18e/prefer-array-from-map": "js-plugin",
|
|
480
|
+
"e18e/prefer-array-some": "js-plugin",
|
|
481
|
+
"e18e/prefer-array-to-spliced": "js-plugin",
|
|
482
|
+
"e18e/prefer-date-now": "js-plugin",
|
|
483
|
+
"e18e/prefer-get-or-insert": "js-plugin",
|
|
484
|
+
"e18e/prefer-includes": "js-plugin",
|
|
485
|
+
"e18e/prefer-includes-over-regex-test": "js-plugin",
|
|
486
|
+
"e18e/prefer-nullish-coalescing": "js-plugin",
|
|
487
|
+
"e18e/prefer-object-has-own": "js-plugin",
|
|
488
|
+
"e18e/prefer-regex-test": "js-plugin",
|
|
489
|
+
"e18e/prefer-static-collator": "js-plugin",
|
|
490
|
+
"e18e/prefer-static-regex": "js-plugin",
|
|
491
|
+
"e18e/prefer-string-fromcharcode": "js-plugin",
|
|
492
|
+
"e18e/prefer-timer-args": "js-plugin",
|
|
493
|
+
"e18e/prefer-url-canparse": "js-plugin",
|
|
494
|
+
"arrow-style/arrow-return-style": "js-plugin",
|
|
495
|
+
"style/jsx-curly-brace-presence": "js-plugin",
|
|
496
|
+
"style/jsx-newline": "js-plugin",
|
|
497
|
+
"style/jsx-self-closing-comp": "js-plugin",
|
|
498
|
+
"style/lines-between-class-members": "js-plugin",
|
|
499
|
+
"style/multiline-comment-style": "js-plugin",
|
|
500
|
+
"style/object-property-newline": "js-plugin",
|
|
501
|
+
"style/padding-line-between-statements": "js-plugin",
|
|
502
|
+
"style/quotes": "js-plugin",
|
|
503
|
+
"style/spaced-comment": "js-plugin",
|
|
504
|
+
"comment-length/limit-multi-line-comments": "js-plugin",
|
|
505
|
+
"comment-length/limit-single-line-comments": "js-plugin",
|
|
506
|
+
"small-rules/no-array-constructor-elements": "js-plugin",
|
|
507
|
+
"small-rules/no-array-size-assignment": "js-plugin",
|
|
508
|
+
"small-rules/no-commented-code": "js-plugin",
|
|
509
|
+
"small-rules/prefer-class-properties": "js-plugin",
|
|
510
|
+
"small-rules/prefer-early-return": "js-plugin",
|
|
511
|
+
"small-rules/prefer-module-scope-constants": "js-plugin",
|
|
512
|
+
"small-rules/prefer-singular-enums": "js-plugin",
|
|
513
|
+
"small-rules/react-hooks-strict-return": "js-plugin",
|
|
514
|
+
"small-rules/strict-component-boundaries": "js-plugin",
|
|
515
|
+
"flawless/jsx-shorthand-boolean": "js-plugin",
|
|
516
|
+
"flawless/jsx-shorthand-fragment": "js-plugin",
|
|
517
|
+
"flawless/no-unnecessary-use-callback": "js-plugin",
|
|
518
|
+
"flawless/no-unnecessary-use-memo": "js-plugin",
|
|
519
|
+
"flawless/prefer-destructuring-assignment": "js-plugin",
|
|
520
|
+
"flawless/prefer-parameter-destructuring": "js-plugin",
|
|
521
|
+
"flawless/purity": "js-plugin",
|
|
522
|
+
"react/error-boundaries": "js-plugin",
|
|
523
|
+
"react/exhaustive-deps": "js-plugin",
|
|
524
|
+
"react/globals": "js-plugin",
|
|
525
|
+
"react/immutability": "js-plugin",
|
|
526
|
+
"react/no-access-state-in-setstate": "js-plugin",
|
|
527
|
+
"react/no-array-index-key": "js-plugin",
|
|
528
|
+
"react/no-children-count": "js-plugin",
|
|
529
|
+
"react/no-children-for-each": "js-plugin",
|
|
530
|
+
"react/no-children-map": "js-plugin",
|
|
531
|
+
"react/no-children-only": "js-plugin",
|
|
532
|
+
"react/no-children-to-array": "js-plugin",
|
|
533
|
+
"react/no-class-component": "js-plugin",
|
|
534
|
+
"react/no-clone-element": "js-plugin",
|
|
535
|
+
"react/no-component-will-mount": "js-plugin",
|
|
536
|
+
"react/no-component-will-receive-props": "js-plugin",
|
|
537
|
+
"react/no-component-will-update": "js-plugin",
|
|
538
|
+
"react/no-create-ref": "js-plugin",
|
|
539
|
+
"react/no-direct-mutation-state": "js-plugin",
|
|
540
|
+
"react/no-duplicate-key": "js-plugin",
|
|
541
|
+
"react/no-forward-ref": "js-plugin",
|
|
542
|
+
"react/no-missing-component-display-name": "js-plugin",
|
|
543
|
+
"react/no-missing-context-display-name": "js-plugin",
|
|
544
|
+
"react/no-missing-key": "js-plugin",
|
|
545
|
+
"react/no-misused-capture-owner-stack": "js-plugin",
|
|
546
|
+
"react/no-nested-component-definitions": "js-plugin",
|
|
547
|
+
"react/no-nested-lazy-component-declarations": "js-plugin",
|
|
548
|
+
"react/no-set-state-in-component-did-mount": "js-plugin",
|
|
549
|
+
"react/no-set-state-in-component-did-update": "js-plugin",
|
|
550
|
+
"react/no-set-state-in-component-will-update": "js-plugin",
|
|
551
|
+
"react/no-unnecessary-use-prefix": "js-plugin",
|
|
552
|
+
"react/no-unsafe-component-will-mount": "js-plugin",
|
|
553
|
+
"react/no-unsafe-component-will-receive-props": "js-plugin",
|
|
554
|
+
"react/no-unsafe-component-will-update": "js-plugin",
|
|
555
|
+
"react/no-unstable-context-value": "js-plugin",
|
|
556
|
+
"react/no-unstable-default-props": "js-plugin",
|
|
557
|
+
"react/no-unused-class-component-members": "js-plugin",
|
|
558
|
+
"react/no-unused-state": "js-plugin",
|
|
559
|
+
"react/no-use-context": "js-plugin",
|
|
560
|
+
"react/refs": "js-plugin",
|
|
561
|
+
"react/rules-of-hooks": "js-plugin",
|
|
562
|
+
"react/set-state-in-effect": "js-plugin",
|
|
563
|
+
"react/set-state-in-render": "js-plugin",
|
|
564
|
+
"react/static-components": "js-plugin",
|
|
565
|
+
"react/use-memo": "js-plugin",
|
|
566
|
+
"react/use-state": "js-plugin",
|
|
567
|
+
"react-jsx/no-children-prop": "js-plugin",
|
|
568
|
+
"react-jsx/no-children-prop-with-children": "js-plugin",
|
|
569
|
+
"react-jsx/no-comment-textnodes": "js-plugin",
|
|
570
|
+
"react-jsx/no-key-after-spread": "js-plugin",
|
|
571
|
+
"react-jsx/no-leaked-dollar": "js-plugin",
|
|
572
|
+
"react-jsx/no-leaked-semicolon": "js-plugin",
|
|
573
|
+
"react-jsx/no-useless-fragment": "js-plugin",
|
|
574
|
+
"react-naming-convention/context-name": "js-plugin",
|
|
575
|
+
"react-naming-convention/ref-name": "js-plugin",
|
|
576
|
+
"jest/consistent-test-it": "js-plugin",
|
|
577
|
+
"jest/expect-expect": "js-plugin",
|
|
578
|
+
"jest/max-expects": "js-plugin",
|
|
579
|
+
"jest/max-nested-describe": "js-plugin",
|
|
580
|
+
"jest/no-alias-methods": "js-plugin",
|
|
581
|
+
"jest/no-commented-out-tests": "js-plugin",
|
|
582
|
+
"jest/no-conditional-expect": "js-plugin",
|
|
583
|
+
"jest/no-conditional-in-test": "js-plugin",
|
|
584
|
+
"jest/no-disabled-tests": "js-plugin",
|
|
585
|
+
"jest/no-done-callback": "js-plugin",
|
|
586
|
+
"jest/no-duplicate-hooks": "js-plugin",
|
|
587
|
+
"jest/no-export": "js-plugin",
|
|
588
|
+
"jest/no-focused-tests": "js-plugin",
|
|
589
|
+
"jest/no-hooks": "js-plugin",
|
|
590
|
+
"jest/no-identical-title": "js-plugin",
|
|
591
|
+
"jest/no-standalone-expect": "js-plugin",
|
|
592
|
+
"jest/no-test-prefixes": "js-plugin",
|
|
593
|
+
"jest/no-test-return-statement": "js-plugin",
|
|
594
|
+
"jest/no-unneeded-async-expect-function": "js-plugin",
|
|
595
|
+
"jest/no-untyped-mock-factory": "js-plugin",
|
|
596
|
+
"jest/padding-around-all": "js-plugin",
|
|
597
|
+
"jest/prefer-called-with": "js-plugin",
|
|
598
|
+
"jest/prefer-comparison-matcher": "js-plugin",
|
|
599
|
+
"jest/prefer-each": "js-plugin",
|
|
600
|
+
"jest/prefer-ending-with-an-expect": "js-plugin",
|
|
601
|
+
"jest/prefer-equality-matcher": "js-plugin",
|
|
602
|
+
"jest/prefer-expect-assertions": "js-plugin",
|
|
603
|
+
"jest/prefer-hooks-in-order": "js-plugin",
|
|
604
|
+
"jest/prefer-lowercase-title": "js-plugin",
|
|
605
|
+
"jest/prefer-mock-promise-shorthand": "js-plugin",
|
|
606
|
+
"jest/prefer-mock-return-shorthand": "js-plugin",
|
|
607
|
+
"jest/prefer-spy-on": "js-plugin",
|
|
608
|
+
"jest/prefer-strict-equal": "js-plugin",
|
|
609
|
+
"jest/prefer-to-be": "js-plugin",
|
|
610
|
+
"jest/prefer-to-contain": "js-plugin",
|
|
611
|
+
"jest/prefer-to-have-been-called": "js-plugin",
|
|
612
|
+
"jest/prefer-to-have-been-called-times": "js-plugin",
|
|
613
|
+
"jest/prefer-to-have-length": "js-plugin",
|
|
614
|
+
"jest/prefer-todo": "js-plugin",
|
|
615
|
+
"jest/require-hook": "js-plugin",
|
|
616
|
+
"jest/require-to-throw-message": "js-plugin",
|
|
617
|
+
"jest/require-top-level-describe": "js-plugin",
|
|
618
|
+
"jest/valid-describe-callback": "js-plugin",
|
|
619
|
+
"jest/valid-expect": "js-plugin",
|
|
620
|
+
"jest/valid-expect-in-promise": "js-plugin",
|
|
621
|
+
"jest/valid-title": "js-plugin",
|
|
622
|
+
"jest-extended/prefer-to-be-array": "js-plugin",
|
|
623
|
+
"jest-extended/prefer-to-be-false": "js-plugin",
|
|
624
|
+
"jest-extended/prefer-to-be-object": "js-plugin",
|
|
625
|
+
"jest-extended/prefer-to-be-true": "js-plugin",
|
|
626
|
+
"jest-extended/prefer-to-have-been-called-once": "js-plugin",
|
|
627
|
+
// @vitest/eslint-plugin as a jsPlugin, renamed vitest-js.
|
|
628
|
+
"vitest/consistent-each-for": "native",
|
|
629
|
+
"vitest/consistent-test-filename": "native",
|
|
630
|
+
"vitest/consistent-test-it": "native",
|
|
631
|
+
"vitest/consistent-vitest-vi": "native",
|
|
632
|
+
"vitest/expect-expect": "native",
|
|
633
|
+
"vitest/hoisted-apis-on-top": "native",
|
|
634
|
+
"vitest/max-expects": "native",
|
|
635
|
+
"vitest/max-nested-describe": "native",
|
|
636
|
+
"vitest/no-alias-methods": "native",
|
|
637
|
+
"vitest/no-commented-out-tests": "native",
|
|
638
|
+
"vitest/no-conditional-expect": "native",
|
|
639
|
+
"vitest/no-conditional-in-test": "native",
|
|
640
|
+
"vitest/no-conditional-tests": "native",
|
|
641
|
+
"vitest/no-disabled-tests": "native",
|
|
642
|
+
"vitest/no-duplicate-hooks": "native",
|
|
643
|
+
"vitest/no-focused-tests": "native",
|
|
644
|
+
"vitest/no-hooks": "native",
|
|
645
|
+
"vitest/no-identical-title": "native",
|
|
646
|
+
"vitest/no-import-node-test": "native",
|
|
647
|
+
"vitest/no-interpolation-in-snapshots": "native",
|
|
648
|
+
"vitest/no-large-snapshots": "native",
|
|
649
|
+
"vitest/no-mocks-import": "native",
|
|
650
|
+
"vitest/no-standalone-expect": "native",
|
|
651
|
+
"vitest/no-test-prefixes": "native",
|
|
652
|
+
"vitest/no-test-return-statement": "native",
|
|
653
|
+
"vitest/no-unneeded-async-expect-function": "native",
|
|
654
|
+
"vitest/padding-around-all": "js-plugin",
|
|
655
|
+
"vitest/prefer-called-exactly-once-with": "native",
|
|
656
|
+
"vitest/prefer-called-once": "native",
|
|
657
|
+
"vitest/prefer-called-with": "native",
|
|
658
|
+
"vitest/prefer-comparison-matcher": "native",
|
|
659
|
+
"vitest/prefer-describe-function-title": "native",
|
|
660
|
+
"vitest/prefer-each": "native",
|
|
661
|
+
"vitest/prefer-equality-matcher": "native",
|
|
662
|
+
"vitest/prefer-expect-assertions": "native",
|
|
663
|
+
"vitest/prefer-expect-resolves": "native",
|
|
664
|
+
"vitest/prefer-expect-type-of": "native",
|
|
665
|
+
"vitest/prefer-hooks-in-order": "native",
|
|
666
|
+
"vitest/prefer-hooks-on-top": "native",
|
|
667
|
+
"vitest/prefer-import-in-mock": "native",
|
|
668
|
+
"vitest/prefer-importing-vitest-globals": "native",
|
|
669
|
+
"vitest/prefer-lowercase-title": "native",
|
|
670
|
+
"vitest/prefer-mock-promise-shorthand": "native",
|
|
671
|
+
"vitest/prefer-mock-return-shorthand": "native",
|
|
672
|
+
"vitest/prefer-snapshot-hint": "native",
|
|
673
|
+
"vitest/prefer-spy-on": "native",
|
|
674
|
+
"vitest/prefer-strict-boolean-matchers": "native",
|
|
675
|
+
"vitest/prefer-strict-equal": "native",
|
|
676
|
+
"vitest/prefer-to-be": "native",
|
|
677
|
+
"vitest/prefer-to-be-object": "native",
|
|
678
|
+
"vitest/prefer-to-contain": "native",
|
|
679
|
+
"vitest/prefer-to-have-been-called-times": "native",
|
|
680
|
+
"vitest/prefer-to-have-length": "native",
|
|
681
|
+
"vitest/prefer-todo": "native",
|
|
682
|
+
"vitest/prefer-vi-mocked": "js-plugin",
|
|
683
|
+
"vitest/require-awaited-expect-poll": "native",
|
|
684
|
+
"vitest/require-local-test-context-for-concurrent-snapshots": "native",
|
|
685
|
+
"vitest/require-mock-type-parameters": "native",
|
|
686
|
+
"vitest/require-to-throw-message": "native",
|
|
687
|
+
"vitest/require-top-level-describe": "native",
|
|
688
|
+
"vitest/valid-describe-callback": "native",
|
|
689
|
+
"vitest/valid-expect": "native",
|
|
690
|
+
"vitest/valid-expect-in-promise": "native",
|
|
691
|
+
"vitest/valid-title": "native",
|
|
692
|
+
"roblox/no-any": "js-plugin",
|
|
693
|
+
"roblox/no-enum-merging": "js-plugin",
|
|
694
|
+
"roblox/no-export-assignment-let": "js-plugin",
|
|
695
|
+
"roblox/no-for-in": "js-plugin",
|
|
696
|
+
"roblox/no-function-expression-name": "js-plugin",
|
|
697
|
+
"roblox/no-get-set": "js-plugin",
|
|
698
|
+
"roblox/no-implicit-self": "js-plugin",
|
|
699
|
+
"roblox/no-invalid-identifier": "js-plugin",
|
|
700
|
+
"roblox/no-namespace-merging": "js-plugin",
|
|
701
|
+
"roblox/no-null": "js-plugin",
|
|
702
|
+
"roblox/no-private-identifier": "js-plugin",
|
|
703
|
+
"roblox/no-unsupported-syntax": "js-plugin",
|
|
704
|
+
"roblox/no-user-defined-lua-tuple": "js-plugin",
|
|
705
|
+
"roblox/no-value-typeof": "js-plugin",
|
|
706
|
+
"roblox/prefer-get-players": "js-plugin",
|
|
707
|
+
"roblox/prefer-task-library": "js-plugin",
|
|
708
|
+
"sentinel/prefer-math-min-max": "js-plugin",
|
|
709
|
+
"eslint-plugin/consistent-output": "js-plugin",
|
|
710
|
+
"eslint-plugin/fixer-return": "js-plugin",
|
|
711
|
+
"eslint-plugin/no-deprecated-context-methods": "js-plugin",
|
|
712
|
+
"eslint-plugin/no-deprecated-report-api": "js-plugin",
|
|
713
|
+
"eslint-plugin/no-identical-tests": "js-plugin",
|
|
714
|
+
"eslint-plugin/no-matching-violation-suggest-message-ids": "js-plugin",
|
|
715
|
+
"eslint-plugin/no-meta-replaced-by": "js-plugin",
|
|
716
|
+
"eslint-plugin/no-meta-schema-default": "js-plugin",
|
|
717
|
+
"eslint-plugin/no-missing-message-ids": "js-plugin",
|
|
718
|
+
"eslint-plugin/no-missing-placeholders": "js-plugin",
|
|
719
|
+
"eslint-plugin/no-only-tests": "js-plugin",
|
|
720
|
+
"eslint-plugin/no-unused-message-ids": "js-plugin",
|
|
721
|
+
"eslint-plugin/no-unused-placeholders": "js-plugin",
|
|
722
|
+
"eslint-plugin/no-useless-token-range": "js-plugin",
|
|
723
|
+
"eslint-plugin/prefer-message-ids": "js-plugin",
|
|
724
|
+
"eslint-plugin/prefer-object-rule": "js-plugin",
|
|
725
|
+
"eslint-plugin/prefer-output-null": "js-plugin",
|
|
726
|
+
"eslint-plugin/prefer-placeholders": "js-plugin",
|
|
727
|
+
"eslint-plugin/prefer-replace-text": "js-plugin",
|
|
728
|
+
"eslint-plugin/report-message-format": "js-plugin",
|
|
729
|
+
"eslint-plugin/require-meta-default-options": "js-plugin",
|
|
730
|
+
"eslint-plugin/require-meta-docs-description": "js-plugin",
|
|
731
|
+
"eslint-plugin/require-meta-docs-recommended": "js-plugin",
|
|
732
|
+
"eslint-plugin/require-meta-fixable": "js-plugin",
|
|
733
|
+
"eslint-plugin/require-meta-has-suggestions": "js-plugin",
|
|
734
|
+
"eslint-plugin/require-meta-schema": "js-plugin",
|
|
735
|
+
"eslint-plugin/require-meta-schema-description": "js-plugin",
|
|
736
|
+
"eslint-plugin/require-meta-type": "js-plugin",
|
|
737
|
+
"eslint-plugin/require-test-case-name": "js-plugin",
|
|
738
|
+
"eslint-plugin/require-test-error-positions": "js-plugin",
|
|
739
|
+
"eslint-plugin/test-case-property-ordering": "js-plugin",
|
|
740
|
+
"eslint-plugin/test-case-shorthand-strings": "js-plugin",
|
|
741
|
+
"eslint-plugin/unique-test-case-names": "js-plugin",
|
|
742
|
+
"erasable-syntax-only/enums": "js-plugin",
|
|
743
|
+
"erasable-syntax-only/import-aliases": "js-plugin",
|
|
744
|
+
"erasable-syntax-only/namespaces": "js-plugin",
|
|
745
|
+
"erasable-syntax-only/parameter-properties": "js-plugin"
|
|
746
|
+
};
|
|
747
|
+
/**
|
|
748
|
+
* Type-aware ESLint rules (meta.docs.requiresTypeChecking) that our shared
|
|
749
|
+
* rule maps reference. Oxlint jsPlugins have no type information, so these
|
|
750
|
+
* rules crash (e.g. Eslint-plugin-jest throws) or silently no-op (e.g.
|
|
751
|
+
* Eslint-plugin-sonarjs skips) when run inside oxlint. The oxlint factory
|
|
752
|
+
* must never emit them; in hybrid mode they stay in ESLint.
|
|
753
|
+
*
|
|
754
|
+
* A test asserts (against the plugins' runtime metadata) that no rule
|
|
755
|
+
* emitted as a jsPlugin requires type checking.
|
|
756
|
+
*/
|
|
757
|
+
const typeAwareJsPluginRules = /* @__PURE__ */ new Set([
|
|
758
|
+
"arrow-style/no-export-default-arrow",
|
|
759
|
+
"eslint-plugin/no-property-in-node",
|
|
760
|
+
"jest/no-error-equal",
|
|
761
|
+
"jest/no-unnecessary-assertion",
|
|
762
|
+
"jest/unbound-method",
|
|
763
|
+
"jest/valid-expect-with-promise",
|
|
764
|
+
"react/no-implicit-children",
|
|
765
|
+
"react/no-implicit-key",
|
|
766
|
+
"react/no-implicit-ref",
|
|
767
|
+
"react/no-leaked-conditional-rendering",
|
|
768
|
+
"react/no-unused-props",
|
|
769
|
+
"sonar/no-async-constructor",
|
|
770
|
+
"sonar/no-ignored-return",
|
|
771
|
+
"sonar/no-incompatible-assertion-types",
|
|
772
|
+
"sonar/no-redundant-optional",
|
|
773
|
+
"sonar/no-try-promise",
|
|
774
|
+
"sonar/prefer-immediate-return",
|
|
775
|
+
"unicorn/no-non-function-verb-prefix"
|
|
776
|
+
]);
|
|
777
|
+
/**
|
|
778
|
+
* Rules that run without type information but produce better results with it:
|
|
779
|
+
* they widen their detection (or avoid false negatives) when the TypeScript
|
|
780
|
+
* parser services are available, and silently fall back to a syntax-only
|
|
781
|
+
* heuristic otherwise. Oxlint jsPlugins have no type information, so these
|
|
782
|
+
* rules would run in their degraded form there; they stay in ESLint, where the
|
|
783
|
+
* type-aware pass gives them a program.
|
|
784
|
+
*
|
|
785
|
+
* Unlike {@link typeAwareJsPluginRules} these do not declare
|
|
786
|
+
* `meta.docs.requiresTypeChecking`, so the metadata-based parity test cannot
|
|
787
|
+
* catch them; they are listed manually.
|
|
788
|
+
*/
|
|
789
|
+
const optionallyTypeAwareRules = /* @__PURE__ */ new Set([
|
|
790
|
+
"e18e/prefer-array-at",
|
|
791
|
+
"e18e/prefer-array-to-reversed",
|
|
792
|
+
"e18e/prefer-array-to-sorted",
|
|
793
|
+
"e18e/prefer-spread-syntax"
|
|
794
|
+
]);
|
|
795
|
+
/**
|
|
796
|
+
* ESLint rules covered on the oxlint side by a differently-named native oxc
|
|
797
|
+
* rule (rather than by translating the rule itself). They are treated as
|
|
798
|
+
* oxlint-covered, so hybrid mode drops them from ESLint and lets the oxc rule
|
|
799
|
+
* run instead; {@link translateRuleToOxlint} resolves them to the oxc rule so
|
|
800
|
+
* the coverage checks compare against the rule that actually runs. In
|
|
801
|
+
* ESLint-only mode oxlint does not run, so they stay enabled in ESLint.
|
|
802
|
+
*
|
|
803
|
+
* Their canonical ESLint versions must not also run inside oxlint as jsPlugins
|
|
804
|
+
* (they are in {@link excludedFromOxlint}), which would double-report.
|
|
805
|
+
*/
|
|
806
|
+
const oxcCoveredRules = {
|
|
807
|
+
"sonar/no-all-duplicated-branches": "oxc/branches-sharing-code",
|
|
808
|
+
"unicorn/no-accidental-bitwise-operator": "oxc/bad-bitwise-operator"
|
|
809
|
+
};
|
|
810
|
+
/**
|
|
811
|
+
* Rules that must not run inside oxlint at all (even in standalone mode),
|
|
812
|
+
* either because they misbehave under oxlint's jsPlugin runtime or because a
|
|
813
|
+
* native oxc rule already covers the oxlint side. See {@link staysInEslint}
|
|
814
|
+
* and {@link oxcCoveredRules} for the reasons.
|
|
815
|
+
*/
|
|
816
|
+
const excludedFromOxlint = /* @__PURE__ */ new Set([
|
|
817
|
+
...Object.keys(oxcCoveredRules),
|
|
818
|
+
"unicorn/no-unsafe-string-replacement",
|
|
819
|
+
...typeAwareJsPluginRules,
|
|
820
|
+
...optionallyTypeAwareRules
|
|
821
|
+
]);
|
|
822
|
+
/**
|
|
823
|
+
* TypeScript extension rules that map to oxlint's core (TypeScript-aware)
|
|
824
|
+
* implementations.
|
|
825
|
+
*/
|
|
826
|
+
const TS_EXTENSION_TO_CORE = /* @__PURE__ */ new Set([
|
|
827
|
+
"default-param-last",
|
|
828
|
+
"no-empty-function",
|
|
829
|
+
"no-shadow",
|
|
830
|
+
"no-unused-expressions",
|
|
831
|
+
"no-unused-private-class-members",
|
|
832
|
+
"no-useless-constructor",
|
|
833
|
+
"prefer-destructuring"
|
|
834
|
+
]);
|
|
835
|
+
/** Unicorn rules renamed in eslint-plugin-unicorn v70 but not in oxlint. */
|
|
836
|
+
const UNICORN_NATIVE_RENAMES = { "no-for-each": "no-array-for-each" };
|
|
837
|
+
/**
|
|
838
|
+
* Prefix translation for rules that run via jsPlugins. Native oxlint plugin
|
|
839
|
+
* prefixes are reserved, so those jsPlugins use `-js` aliases.
|
|
840
|
+
*/
|
|
841
|
+
const oxlintJsPluginPrefixRenames = {
|
|
842
|
+
"": "eslint-js",
|
|
843
|
+
"import": "import-js",
|
|
844
|
+
"jest": "jest-js",
|
|
845
|
+
"jsdoc": "jsdoc-js",
|
|
846
|
+
"node": "node-js",
|
|
847
|
+
"promise": "promise-js",
|
|
848
|
+
"react": "react-x",
|
|
849
|
+
"unicorn": "unicorn-js",
|
|
850
|
+
"vitest": "vitest-js"
|
|
851
|
+
};
|
|
852
|
+
/** JsPlugin package specifiers keyed by their oxlint-side prefix. */
|
|
853
|
+
const oxlintJsPlugins = {
|
|
854
|
+
"@cspell": "@cspell/eslint-plugin",
|
|
855
|
+
"antfu": "eslint-plugin-antfu",
|
|
856
|
+
"arrow-style": "eslint-plugin-arrow-return-style-x",
|
|
857
|
+
"better-max-params": "eslint-plugin-better-max-params",
|
|
858
|
+
"comment-length": "eslint-plugin-comment-length",
|
|
859
|
+
"de-morgan": "eslint-plugin-de-morgan",
|
|
860
|
+
"e18e": "@e18e/eslint-plugin",
|
|
861
|
+
"erasable-syntax-only": "eslint-plugin-erasable-syntax-only",
|
|
862
|
+
"eslint-js": "oxlint-plugin-eslint",
|
|
863
|
+
"eslint-plugin": "eslint-plugin-eslint-plugin",
|
|
864
|
+
"flawless": "eslint-plugin-flawless",
|
|
865
|
+
"import-js": "eslint-plugin-import-lite",
|
|
866
|
+
"jest-extended": "eslint-plugin-jest-extended",
|
|
867
|
+
"jest-js": "eslint-plugin-jest",
|
|
868
|
+
"jsdoc-js": "eslint-plugin-jsdoc",
|
|
869
|
+
"node-js": "eslint-plugin-n",
|
|
870
|
+
"oxfmt": "eslint-plugin-oxfmt",
|
|
871
|
+
"oxlint-comments": "oxlint-plugin-oxlint-comments",
|
|
872
|
+
"perfectionist": "eslint-plugin-perfectionist",
|
|
873
|
+
"promise-js": "eslint-plugin-promise",
|
|
874
|
+
"react-jsx": "eslint-plugin-react-jsx",
|
|
875
|
+
"react-naming-convention": "eslint-plugin-react-naming-convention",
|
|
876
|
+
"react-x": "eslint-plugin-react-x",
|
|
877
|
+
"roblox": "eslint-plugin-roblox-ts",
|
|
878
|
+
"sentinel": "eslint-plugin-sentinel",
|
|
879
|
+
"small-rules": "@pobammer-ts/small-rules",
|
|
880
|
+
"sonar": "eslint-plugin-sonarjs",
|
|
881
|
+
"style": "@stylistic/eslint-plugin",
|
|
882
|
+
"ts": "@typescript-eslint/eslint-plugin",
|
|
883
|
+
"unicorn-js": "eslint-plugin-unicorn",
|
|
884
|
+
"unused-imports": "eslint-plugin-unused-imports",
|
|
885
|
+
"vitest-js": "@vitest/eslint-plugin"
|
|
886
|
+
};
|
|
887
|
+
/**
|
|
888
|
+
* Whether the given ESLint rule (canonical renamed name) is covered by oxlint
|
|
889
|
+
* in hybrid mode.
|
|
890
|
+
*
|
|
891
|
+
* @param rule - The canonical ESLint rule name.
|
|
892
|
+
* @returns Whether oxlint covers the rule.
|
|
893
|
+
*/
|
|
894
|
+
function isOxlintCovered(rule) {
|
|
895
|
+
return rule in oxlintRuleMapping || rule in oxcCoveredRules;
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Whether the rule runs via oxlint-tsgolint (`oxlint --type-aware`); such rules
|
|
899
|
+
* are noise without type information, so they are gated on `typeAware`.
|
|
900
|
+
*
|
|
901
|
+
* @param rule - The canonical ESLint rule name.
|
|
902
|
+
* @returns Whether the rule is executed by oxlint-tsgolint.
|
|
903
|
+
*/
|
|
904
|
+
function isTsgolintRule(rule) {
|
|
905
|
+
return oxlintRuleMapping[rule] === "tsgolint";
|
|
906
|
+
}
|
|
907
|
+
/**
|
|
908
|
+
* Translate a canonical ESLint rule name into its oxlint configuration name.
|
|
909
|
+
*
|
|
910
|
+
* @param rule - The canonical ESLint rule name.
|
|
911
|
+
* @returns The rule name to use in an oxlint configuration.
|
|
912
|
+
*/
|
|
913
|
+
function translateRuleToOxlint(rule) {
|
|
914
|
+
const oxcCovered = oxcCoveredRules[rule];
|
|
915
|
+
if (oxcCovered !== void 0) return oxcCovered;
|
|
916
|
+
const target = oxlintRuleMapping[rule];
|
|
917
|
+
const { name, prefix } = splitRuleName(rule);
|
|
918
|
+
if (target === "native") {
|
|
919
|
+
if (prefix === "") return name;
|
|
920
|
+
if (prefix === "ts") return TS_EXTENSION_TO_CORE.has(name) ? name : `typescript/${name}`;
|
|
921
|
+
if (prefix === "unicorn") return `unicorn/${UNICORN_NATIVE_RENAMES[name] ?? name}`;
|
|
922
|
+
return rule;
|
|
923
|
+
}
|
|
924
|
+
if (target === "tsgolint") return `typescript/${name}`;
|
|
925
|
+
const renamed = oxlintJsPluginPrefixRenames[prefix];
|
|
926
|
+
return renamed === void 0 ? rule : `${renamed}/${name}`;
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* Whether a rule is a TypeScript extension rule that oxlint implements as its
|
|
930
|
+
* TypeScript-aware core rule, so `ts/<name>` and the bare core `<name>`
|
|
931
|
+
* collapse onto a single native entry. The extension entry must win.
|
|
932
|
+
*
|
|
933
|
+
* @param rule - The canonical ESLint rule name.
|
|
934
|
+
* @returns Whether the rule collapses onto a shared native core rule.
|
|
935
|
+
*/
|
|
936
|
+
function collapsesToTsCoreRule(rule) {
|
|
937
|
+
const { name, prefix } = splitRuleName(rule);
|
|
938
|
+
return prefix === "ts" && TS_EXTENSION_TO_CORE.has(name);
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Whether a rule is the bare core counterpart of a collapsing TypeScript
|
|
942
|
+
* extension rule; its severity must yield to the extension entry.
|
|
943
|
+
*
|
|
944
|
+
* @param rule - The canonical ESLint rule name.
|
|
945
|
+
* @returns Whether the rule is such a core counterpart.
|
|
946
|
+
*/
|
|
947
|
+
function isTsCoreCounterpartRule(rule) {
|
|
948
|
+
const { name, prefix } = splitRuleName(rule);
|
|
949
|
+
return prefix === "" && TS_EXTENSION_TO_CORE.has(name);
|
|
950
|
+
}
|
|
951
|
+
function splitRuleName(rule) {
|
|
952
|
+
const slashIndex = rule.indexOf("/");
|
|
953
|
+
if (slashIndex === -1) return {
|
|
954
|
+
name: rule,
|
|
955
|
+
prefix: ""
|
|
956
|
+
};
|
|
957
|
+
return {
|
|
958
|
+
name: rule.slice(slashIndex + 1),
|
|
959
|
+
prefix: rule.slice(0, slashIndex)
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
//#endregion
|
|
963
|
+
//#region src/globs.ts
|
|
964
|
+
const GLOB_ROOT = [
|
|
965
|
+
"*",
|
|
966
|
+
"packages/*/*",
|
|
967
|
+
"apps/*/*",
|
|
968
|
+
"libs/*/*",
|
|
969
|
+
"packages/*/*/*",
|
|
970
|
+
"apps/*/*/*",
|
|
971
|
+
"libs/*/*/*"
|
|
972
|
+
];
|
|
973
|
+
const GLOB_SRC_EXT = "{,c,m}[jt]s{,x}";
|
|
974
|
+
const GLOB_SRC = "**/*.{,c,m}[jt]s{,x}";
|
|
975
|
+
const GLOB_JS = "**/*.{,c,m}js";
|
|
976
|
+
const GLOB_JSX = "**/*.{,c,m}jsx";
|
|
977
|
+
const GLOB_TS = "**/*.{,c,m}ts";
|
|
978
|
+
const GLOB_TSX = "**/*.{,c,m}tsx";
|
|
979
|
+
const GLOB_DTS = "**/*.d.{,c,m}ts";
|
|
980
|
+
const GLOB_TESTS = [
|
|
981
|
+
`**/__tests__/**/*.${GLOB_SRC_EXT}`,
|
|
982
|
+
`**/*.spec.${GLOB_SRC_EXT}`,
|
|
983
|
+
`**/*.test.${GLOB_SRC_EXT}`,
|
|
984
|
+
`**/*.bench.${GLOB_SRC_EXT}`,
|
|
985
|
+
`**/*.benchmark.${GLOB_SRC_EXT}`
|
|
986
|
+
];
|
|
987
|
+
const GLOB_BUILD_TOOLS = [
|
|
988
|
+
"**/.husky/**/*",
|
|
989
|
+
`**/config/${GLOB_SRC}`,
|
|
990
|
+
`**/*config.${GLOB_SRC_EXT}`,
|
|
991
|
+
`**/*config.*.${GLOB_SRC_EXT}`,
|
|
992
|
+
`**/build/${GLOB_SRC}`,
|
|
993
|
+
`**/tools/${GLOB_SRC}`,
|
|
994
|
+
`**/setup/${GLOB_SRC}`,
|
|
995
|
+
`**/dev/${GLOB_SRC}`,
|
|
996
|
+
`**/tasks/${GLOB_SRC}`,
|
|
997
|
+
"**/.github/scripts/**/*"
|
|
998
|
+
];
|
|
999
|
+
const GLOB_EXCLUDE = [
|
|
1000
|
+
"**/.pnpm-store",
|
|
1001
|
+
"**/bun.lockb",
|
|
1002
|
+
"**/dist",
|
|
1003
|
+
"**/node_modules",
|
|
1004
|
+
"**/package-lock.json",
|
|
1005
|
+
"**/pnpm-lock.yaml",
|
|
1006
|
+
"**/yarn.lock",
|
|
1007
|
+
"**/.cache",
|
|
1008
|
+
"**/.changeset",
|
|
1009
|
+
"**/.agents",
|
|
1010
|
+
"**/.claude/**/*",
|
|
1011
|
+
"**/.gemini",
|
|
1012
|
+
"**/.kilocode",
|
|
1013
|
+
"**/.opencode",
|
|
1014
|
+
"**/.history",
|
|
1015
|
+
"**/.idea",
|
|
1016
|
+
"**/.next",
|
|
1017
|
+
"**/.nuxt",
|
|
1018
|
+
"**/.output",
|
|
1019
|
+
"**/.svelte-kit",
|
|
1020
|
+
"**/.temp",
|
|
1021
|
+
"**/.tmp",
|
|
1022
|
+
"**/.vercel",
|
|
1023
|
+
"**/.vite-inspect",
|
|
1024
|
+
"**/.vitepress/cache",
|
|
1025
|
+
"**/.worktree",
|
|
1026
|
+
"**/.yarn",
|
|
1027
|
+
"**/coverage",
|
|
1028
|
+
"**/out",
|
|
1029
|
+
"**/output",
|
|
1030
|
+
"**/temp",
|
|
1031
|
+
"**/tmp",
|
|
1032
|
+
"**/vite.config.*.timestamp-*",
|
|
1033
|
+
"**/*.min.*",
|
|
1034
|
+
"**/CHANGELOG*.md",
|
|
1035
|
+
"**/LICENSE*",
|
|
1036
|
+
"**/__snapshots__",
|
|
1037
|
+
"**/auto-import{,s}.d.ts",
|
|
1038
|
+
"**/components.d.ts",
|
|
1039
|
+
"**/roblox.yml"
|
|
1040
|
+
];
|
|
1041
|
+
//#endregion
|
|
1042
|
+
//#region src/rules/oxfmt.ts
|
|
1043
|
+
const UNSUPPORTED_PRETTIER_KEYS = /* @__PURE__ */ new Set([
|
|
1044
|
+
"experimentalOperatorPosition",
|
|
1045
|
+
"experimentalTernaries",
|
|
1046
|
+
"jsdocPreferCodeFences",
|
|
1047
|
+
"jsdocPrintWidth",
|
|
1048
|
+
"parser",
|
|
1049
|
+
"plugins",
|
|
1050
|
+
"tsdoc"
|
|
1051
|
+
]);
|
|
1052
|
+
const defaultSortImports = {
|
|
1053
|
+
customGroups: [{
|
|
1054
|
+
elementNamePattern: ["react"],
|
|
1055
|
+
groupName: "react"
|
|
1056
|
+
}, {
|
|
1057
|
+
elementNamePattern: ["@*/**"],
|
|
1058
|
+
groupName: "scoped"
|
|
1059
|
+
}],
|
|
1060
|
+
groups: [
|
|
1061
|
+
"react",
|
|
1062
|
+
"scoped",
|
|
1063
|
+
[
|
|
1064
|
+
"type-builtin",
|
|
1065
|
+
"type-external",
|
|
1066
|
+
"builtin",
|
|
1067
|
+
"external"
|
|
1068
|
+
],
|
|
1069
|
+
[
|
|
1070
|
+
"type-internal",
|
|
1071
|
+
"internal",
|
|
1072
|
+
"type-parent",
|
|
1073
|
+
"type-sibling",
|
|
1074
|
+
"type-index",
|
|
1075
|
+
"parent",
|
|
1076
|
+
"sibling",
|
|
1077
|
+
"index"
|
|
1078
|
+
],
|
|
1079
|
+
"unknown"
|
|
1080
|
+
],
|
|
1081
|
+
newlinesBetween: true
|
|
1082
|
+
};
|
|
1083
|
+
/**
|
|
1084
|
+
* Migrate Prettier options to oxfmt options, dropping unsupported keys.
|
|
1085
|
+
*
|
|
1086
|
+
* @param prettierOptions - The Prettier options to migrate.
|
|
1087
|
+
* @returns The migrated oxfmt options.
|
|
1088
|
+
*/
|
|
1089
|
+
function migratePrettierOptions(prettierOptions) {
|
|
1090
|
+
const oxfmtOptions = {};
|
|
1091
|
+
for (const [key, value] of Object.entries(prettierOptions)) {
|
|
1092
|
+
if (UNSUPPORTED_PRETTIER_KEYS.has(key)) continue;
|
|
1093
|
+
if (key === "endOfLine" && value === "auto") continue;
|
|
1094
|
+
oxfmtOptions[key] = value;
|
|
1095
|
+
}
|
|
1096
|
+
return oxfmtOptions;
|
|
1097
|
+
}
|
|
1098
|
+
/**
|
|
1099
|
+
* Build the effective oxfmt options shared between the ESLint and oxlint
|
|
1100
|
+
* factories.
|
|
1101
|
+
*
|
|
1102
|
+
* @param options - The option sources, in increasing precedence.
|
|
1103
|
+
* @returns The effective oxfmt options.
|
|
1104
|
+
*/
|
|
1105
|
+
function buildOxfmtOptions({ oxfmtConfigOptions = {}, oxfmtOptions, prettierOptions = {} }) {
|
|
1106
|
+
return {
|
|
1107
|
+
sortImports: defaultSortImports,
|
|
1108
|
+
sortPackageJson: false,
|
|
1109
|
+
...migratePrettierOptions(prettierOptions),
|
|
1110
|
+
...oxfmtConfigOptions,
|
|
1111
|
+
...oxfmtOptions
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
createRequire(import.meta.url);
|
|
1115
|
+
/**
|
|
1116
|
+
* Resolve options with default values. Handles the pattern where `true` means
|
|
1117
|
+
* "use defaults", `false` disables the feature, and objects are used as-is.
|
|
1118
|
+
*
|
|
1119
|
+
* @template T - The type of the defaults object.
|
|
1120
|
+
* @param value - The option value (true | false | undefined | object).
|
|
1121
|
+
* @param defaults - Default values to use when value is true or undefined.
|
|
1122
|
+
* @returns The resolved options.
|
|
1123
|
+
*/
|
|
1124
|
+
function resolveWithDefaults(value, defaults) {
|
|
1125
|
+
if (value === false) return false;
|
|
1126
|
+
if (value === true || value === void 0) return defaults;
|
|
1127
|
+
return value;
|
|
1128
|
+
}
|
|
1129
|
+
/**
|
|
1130
|
+
* Finds the Node major version the linted project targets.
|
|
1131
|
+
*
|
|
1132
|
+
* The shared `settings.n.version` / `settings.node.version` values win, so the
|
|
1133
|
+
* setting `eslint-plugin-n` already reads doubles as the override for every
|
|
1134
|
+
* version-gated rule in the preset. Failing that, this walks up from `cwd` for
|
|
1135
|
+
* the nearest `package.json` that declares a version — leaf manifests in a
|
|
1136
|
+
* workspace frequently omit the field and leave it to the root, so a manifest
|
|
1137
|
+
* without it is skipped rather than treated as an answer.
|
|
1138
|
+
*
|
|
1139
|
+
* A range that cannot be parsed resolves to `undefined`, which leaves
|
|
1140
|
+
* version-gated rules off rather than enabling them for a runtime that cannot
|
|
1141
|
+
* support them.
|
|
1142
|
+
*
|
|
1143
|
+
* @param settings - Shared ESLint/oxlint settings to read the override from.
|
|
1144
|
+
* @param cwd - Directory to start searching from.
|
|
1145
|
+
* @returns The targeted Node major, or `undefined` when nothing declares one.
|
|
1146
|
+
*/
|
|
1147
|
+
function resolveNodeMajor(settings, cwd = process.cwd()) {
|
|
1148
|
+
const configured = readSettingsNodeVersion(settings);
|
|
1149
|
+
if (configured !== void 0) return parseNodeMajor(configured);
|
|
1150
|
+
let searchFrom = cwd;
|
|
1151
|
+
while (true) {
|
|
1152
|
+
const manifestPath = findUpSync("package.json", { cwd: searchFrom });
|
|
1153
|
+
if (manifestPath === void 0) return;
|
|
1154
|
+
let manifest = {};
|
|
1155
|
+
try {
|
|
1156
|
+
manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
1157
|
+
} catch {}
|
|
1158
|
+
const range = readNodeRange(manifest);
|
|
1159
|
+
if (range !== void 0) return parseNodeMajor(range);
|
|
1160
|
+
const parent = path.dirname(path.dirname(manifestPath));
|
|
1161
|
+
if (parent === searchFrom) return;
|
|
1162
|
+
searchFrom = parent;
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
function resolveSubOptions(options, key) {
|
|
1166
|
+
const optionValue = options[key];
|
|
1167
|
+
const defaults = resolveWithDefaults(optionValue, {});
|
|
1168
|
+
return defaults === false ? {} : defaults;
|
|
1169
|
+
}
|
|
1170
|
+
function toSourceGlob(glob) {
|
|
1171
|
+
return glob.startsWith("!") ? `!${glob.slice(1)}.${GLOB_SRC_EXT}` : `${glob}.${GLOB_SRC_EXT}`;
|
|
1172
|
+
}
|
|
1173
|
+
function getOverrides(options, key) {
|
|
1174
|
+
const sub = resolveSubOptions(options, key);
|
|
1175
|
+
return {
|
|
1176
|
+
files: typeof sub === "object" && "files" in sub ? sub.files : void 0,
|
|
1177
|
+
filesTypeAware: typeof sub === "object" && "filesTypeAware" in sub ? sub.filesTypeAware : void 0,
|
|
1178
|
+
ignoresTypeAware: typeof sub === "object" && "ignoresTypeAware" in sub ? sub.ignoresTypeAware : void 0,
|
|
1179
|
+
overrides: { ...typeof sub === "object" && "overrides" in sub ? sub.overrides : {} },
|
|
1180
|
+
overridesTypeAware: { ...typeof sub === "object" && "overridesTypeAware" in sub ? sub.overridesTypeAware : {} }
|
|
1181
|
+
};
|
|
1182
|
+
}
|
|
1183
|
+
function isInGitHooksOrLintStaged() {
|
|
1184
|
+
return [
|
|
1185
|
+
process.env["GIT_HOOK"],
|
|
1186
|
+
process.env["GIT_PARAMS"],
|
|
1187
|
+
process.env["VSCODE_GIT_COMMAND"],
|
|
1188
|
+
process.env["npm_lifecycle_script"]?.startsWith("lint-staged")
|
|
1189
|
+
].some(Boolean);
|
|
1190
|
+
}
|
|
1191
|
+
function isInAgentSession() {
|
|
1192
|
+
if (isInGitHooksOrLintStaged()) return false;
|
|
1193
|
+
return [
|
|
1194
|
+
process.env["CLAUDECODE"],
|
|
1195
|
+
process.env["CLAUDE_CODE_ENTRYPOINT"],
|
|
1196
|
+
process.env["CODEX_THREAD_ID"],
|
|
1197
|
+
process.env["CURSOR_AGENT"],
|
|
1198
|
+
process.env["GEMINI_CLI"],
|
|
1199
|
+
process.env["OPENCODE"]
|
|
1200
|
+
].some(Boolean);
|
|
1201
|
+
}
|
|
1202
|
+
function isInEditorEnvironment() {
|
|
1203
|
+
const explicitValue = process.env["ESLINT_IN_EDITOR"];
|
|
1204
|
+
if (explicitValue !== void 0) return explicitValue === "true" || explicitValue === "1";
|
|
1205
|
+
if (process.env["CI"] ?? "") return false;
|
|
1206
|
+
if (isInGitHooksOrLintStaged()) return false;
|
|
1207
|
+
return [
|
|
1208
|
+
process.env["VSCODE_PID"],
|
|
1209
|
+
process.env["VSCODE_CWD"],
|
|
1210
|
+
process.env["JETBRAINS_IDE"],
|
|
1211
|
+
process.env["VIM"],
|
|
1212
|
+
process.env["NVIM"]
|
|
1213
|
+
].some(Boolean);
|
|
1214
|
+
}
|
|
1215
|
+
/**
|
|
1216
|
+
* Merge custom glob patterns.
|
|
1217
|
+
*
|
|
1218
|
+
* - Patterns starting with "!" are used to remove matching patterns
|
|
1219
|
+
* - Other patterns are added to the result.
|
|
1220
|
+
*
|
|
1221
|
+
* @example
|
|
1222
|
+
*
|
|
1223
|
+
* ```ts
|
|
1224
|
+
* const result = mergeGlobs(GLOB_ROOT, ["places/**", "!apps/**"]);
|
|
1225
|
+
* // Returns: ["*", "packages/**", "libs/**", "places/**"]
|
|
1226
|
+
* ```
|
|
1227
|
+
*
|
|
1228
|
+
* @param globs - The default root glob patterns.
|
|
1229
|
+
* @param additionalPatterns - Custom root patterns to merge (optional).
|
|
1230
|
+
* @returns The merged array of glob patterns.
|
|
1231
|
+
*/
|
|
1232
|
+
function mergeGlobs(globs, additionalPatterns) {
|
|
1233
|
+
if (!additionalPatterns || additionalPatterns.length === 0) return [...globs];
|
|
1234
|
+
let result = [...globs];
|
|
1235
|
+
for (const pattern of additionalPatterns) if (pattern.startsWith("!")) {
|
|
1236
|
+
const patternToRemove = pattern.slice(1);
|
|
1237
|
+
result = result.filter((item) => item !== patternToRemove);
|
|
1238
|
+
} else result.push(pattern);
|
|
1239
|
+
return result;
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Rename plugin prefixes in a rule object. Accepts a map of prefixes to rename.
|
|
1243
|
+
*
|
|
1244
|
+
* @example
|
|
1245
|
+
*
|
|
1246
|
+
* ```ts
|
|
1247
|
+
* import { renameRules } from "@antfu/eslint-config";
|
|
1248
|
+
*
|
|
1249
|
+
* export default [
|
|
1250
|
+
* {
|
|
1251
|
+
* rules: renameRules(
|
|
1252
|
+
* {
|
|
1253
|
+
* "@typescript-eslint/indent": "error",
|
|
1254
|
+
* },
|
|
1255
|
+
* { "@typescript-eslint": "ts" },
|
|
1256
|
+
* ),
|
|
1257
|
+
* },
|
|
1258
|
+
* ];
|
|
1259
|
+
* ```
|
|
1260
|
+
*
|
|
1261
|
+
* @param rules - The rules object to rename.
|
|
1262
|
+
* @param map - A map of prefixes to rename.
|
|
1263
|
+
* @returns The renamed rules object.
|
|
1264
|
+
*/
|
|
1265
|
+
function renameRules(rules, map) {
|
|
1266
|
+
return Object.fromEntries(Object.entries(rules).map(([key, value]) => {
|
|
1267
|
+
for (const [from, to] of Object.entries(map)) if (key.startsWith(`${from}/`)) return [to + key.slice(from.length), value];
|
|
1268
|
+
return [key, value];
|
|
1269
|
+
}));
|
|
1270
|
+
}
|
|
1271
|
+
const OXFMT_CONFIG_FILES = [".oxfmtrc.json", ".oxfmtrc.jsonc"];
|
|
1272
|
+
/**
|
|
1273
|
+
* Resolve oxfmt configuration from `.oxfmtrc.json` or `.oxfmtrc.jsonc`,
|
|
1274
|
+
* synchronously. Used by the (synchronous) oxlint factory.
|
|
1275
|
+
*
|
|
1276
|
+
* @returns The oxfmt configuration options, or an empty object if none found.
|
|
1277
|
+
*/
|
|
1278
|
+
function resolveOxfmtConfigOptionsSync() {
|
|
1279
|
+
for (const filename of OXFMT_CONFIG_FILES) {
|
|
1280
|
+
const configPath = path.resolve(process.cwd(), filename);
|
|
1281
|
+
try {
|
|
1282
|
+
const content = fs.readFileSync(configPath, "utf-8");
|
|
1283
|
+
const { $schema: _, ...config } = JSON.parse(content);
|
|
1284
|
+
return config;
|
|
1285
|
+
} catch {
|
|
1286
|
+
continue;
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
return {};
|
|
1290
|
+
}
|
|
1291
|
+
/**
|
|
1292
|
+
* Override the severity of all rules in a rules object, preserving rule
|
|
1293
|
+
* options. Rules set to `"off"` are not affected.
|
|
1294
|
+
*
|
|
1295
|
+
* @param rules - The rules object to override.
|
|
1296
|
+
* @param severity - The target severity level.
|
|
1297
|
+
* @param excludeRules - Rules to exclude from the severity override.
|
|
1298
|
+
* @returns A new rules object with overridden severities.
|
|
1299
|
+
*/
|
|
1300
|
+
function overrideRuleSeverity(rules, severity, excludeRules = /* @__PURE__ */ new Set()) {
|
|
1301
|
+
return Object.fromEntries(Object.entries(rules).map(([key, value]) => {
|
|
1302
|
+
if (value === "off" || value === 0 || excludeRules.has(key)) return [key, value];
|
|
1303
|
+
if (Array.isArray(value)) {
|
|
1304
|
+
const [currentSeverity, ...options] = value;
|
|
1305
|
+
if (currentSeverity === "off" || currentSeverity === 0) return [key, value];
|
|
1306
|
+
return [key, [severity, ...options]];
|
|
1307
|
+
}
|
|
1308
|
+
if (value === "error" || value === "warn" || value === 1 || value === 2) return [key, severity];
|
|
1309
|
+
return [key, value];
|
|
1310
|
+
}));
|
|
1311
|
+
}
|
|
1312
|
+
/**
|
|
1313
|
+
* Resolves the lowest Node major version a semver range can match.
|
|
1314
|
+
*
|
|
1315
|
+
* @param range - A semver range such as `>=24.12.0` or `^24 || ^26`.
|
|
1316
|
+
* @returns The lowest major version, or `undefined` for an invalid range.
|
|
1317
|
+
*/
|
|
1318
|
+
function parseNodeMajor(range) {
|
|
1319
|
+
try {
|
|
1320
|
+
return minVersion(range)?.major;
|
|
1321
|
+
} catch {
|
|
1322
|
+
return;
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
/**
|
|
1326
|
+
* Reads the Node version range from the shared settings, checking both keys
|
|
1327
|
+
* `eslint-plugin-n` accepts so a project configuring it for that plugin gets
|
|
1328
|
+
* the preset's version-gated rules lined up for free.
|
|
1329
|
+
*
|
|
1330
|
+
* @param settings - The shared settings object, if any.
|
|
1331
|
+
* @returns The configured range, or `undefined` when unset.
|
|
1332
|
+
*/
|
|
1333
|
+
function readSettingsNodeVersion(settings) {
|
|
1334
|
+
for (const key of ["n", "node"]) {
|
|
1335
|
+
const version = (settings?.[key])?.version;
|
|
1336
|
+
if (typeof version === "string") return version;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
/**
|
|
1340
|
+
* Reads the declared Node version range from a manifest, preferring
|
|
1341
|
+
* `engines.node` and falling back to the `devEngines.runtime` entry named
|
|
1342
|
+
* `node`, matching how `eslint-plugin-n` resolves the same question.
|
|
1343
|
+
*
|
|
1344
|
+
* @param manifest - The parsed manifest.
|
|
1345
|
+
* @returns The declared range, or `undefined` when the manifest declares none.
|
|
1346
|
+
*/
|
|
1347
|
+
function readNodeRange(manifest) {
|
|
1348
|
+
const enginesNode = manifest.engines?.node;
|
|
1349
|
+
if (enginesNode !== void 0) return enginesNode;
|
|
1350
|
+
const { runtime } = manifest.devEngines ?? {};
|
|
1351
|
+
return (Array.isArray(runtime) ? runtime : [runtime]).find((entry) => entry?.name === "node")?.version;
|
|
1352
|
+
}
|
|
1353
|
+
//#endregion
|
|
1354
|
+
//#region src/rules/comments.ts
|
|
1355
|
+
/**
|
|
1356
|
+
* Comment style rules shared between the ESLint and oxlint factories.
|
|
1357
|
+
*
|
|
1358
|
+
* The directive-comment rules themselves differ per linter
|
|
1359
|
+
* (`eslint-comments/*` vs `oxlint-comments/*`) and live in the respective
|
|
1360
|
+
* config modules.
|
|
1361
|
+
*
|
|
1362
|
+
* @param options - Shared rule options.
|
|
1363
|
+
* @returns The rule map.
|
|
1364
|
+
*/
|
|
1365
|
+
function commentsRules({ stylistic = true } = {}) {
|
|
1366
|
+
return { ...stylistic !== false ? {
|
|
1367
|
+
"no-inline-comments": "error",
|
|
1368
|
+
"style/multiline-comment-style": ["error", "separate-lines"]
|
|
1369
|
+
} : {} };
|
|
1370
|
+
}
|
|
1371
|
+
/**
|
|
1372
|
+
* Comment length rules shared between the ESLint and oxlint factories.
|
|
1373
|
+
*
|
|
1374
|
+
* @param options - Shared rule options.
|
|
1375
|
+
* @returns The rule map.
|
|
1376
|
+
*/
|
|
1377
|
+
function commentLengthRules({ maxLength, multiLineMaxLength, semanticComments, tabSize }) {
|
|
1378
|
+
return {
|
|
1379
|
+
"comment-length/limit-multi-line-comments": ["error", {
|
|
1380
|
+
maxLength: multiLineMaxLength,
|
|
1381
|
+
mode: "compact-on-overflow",
|
|
1382
|
+
...semanticComments ? { semanticComments } : {},
|
|
1383
|
+
tabSize
|
|
1384
|
+
}],
|
|
1385
|
+
"comment-length/limit-single-line-comments": ["error", {
|
|
1386
|
+
maxLength,
|
|
1387
|
+
...semanticComments ? { semanticComments } : {},
|
|
1388
|
+
tabSize
|
|
1389
|
+
}]
|
|
1390
|
+
};
|
|
1391
|
+
}
|
|
1392
|
+
//#endregion
|
|
1393
|
+
//#region src/oxlint/utils.ts
|
|
1394
|
+
const NATIVE_PLUGINS = /* @__PURE__ */ new Set([
|
|
1395
|
+
"eslint",
|
|
1396
|
+
"import",
|
|
1397
|
+
"jest",
|
|
1398
|
+
"jsdoc",
|
|
1399
|
+
"jsx-a11y",
|
|
1400
|
+
"nextjs",
|
|
1401
|
+
"node",
|
|
1402
|
+
"oxc",
|
|
1403
|
+
"promise",
|
|
1404
|
+
"react",
|
|
1405
|
+
"react-perf",
|
|
1406
|
+
"typescript",
|
|
1407
|
+
"unicorn",
|
|
1408
|
+
"vitest",
|
|
1409
|
+
"vue"
|
|
1410
|
+
]);
|
|
1411
|
+
/**
|
|
1412
|
+
* Split a canonical (ESLint-named) rule map into oxlint-native rules and
|
|
1413
|
+
* jsPlugin rules, translating rule names via the oxlint rule mapping.
|
|
1414
|
+
*
|
|
1415
|
+
* Rules that are not part of the hybrid mapping (for example jest or react
|
|
1416
|
+
* rules, which only run in standalone mode) are treated as jsPlugin rules.
|
|
1417
|
+
* Disabled unmapped rules are skipped so that no jsPlugin is loaded solely for
|
|
1418
|
+
* an "off" entry, unless `keepUnmappedOff` is set (user options.rules must not
|
|
1419
|
+
* silently discard an explicit disable).
|
|
1420
|
+
*
|
|
1421
|
+
* @param rules - The canonical rule map.
|
|
1422
|
+
* @param keepUnmappedOff - Emit disabled unmapped rules instead of skipping
|
|
1423
|
+
* them (without registering a jsPlugin for them).
|
|
1424
|
+
* @returns The split rules with the plugins each side requires.
|
|
1425
|
+
*/
|
|
1426
|
+
function splitOxlintRules(rules, keepUnmappedOff = false) {
|
|
1427
|
+
const nativeRules = {};
|
|
1428
|
+
const jsPluginRules = {};
|
|
1429
|
+
const nativePlugins = /* @__PURE__ */ new Set();
|
|
1430
|
+
const jsPluginPrefixes = /* @__PURE__ */ new Set();
|
|
1431
|
+
const tsCollapsed = /* @__PURE__ */ new Set();
|
|
1432
|
+
const entries = Object.entries(rules ?? {});
|
|
1433
|
+
for (const [rule, value] of entries) {
|
|
1434
|
+
if (value === void 0 || excludedFromOxlint.has(rule)) continue;
|
|
1435
|
+
const covered = isOxlintCovered(rule);
|
|
1436
|
+
const severity = Array.isArray(value) ? value[0] : value;
|
|
1437
|
+
const isOff = severity === "off" || severity === 0;
|
|
1438
|
+
const translated = translateRuleToOxlint(rule);
|
|
1439
|
+
const slashIndex = translated.indexOf("/");
|
|
1440
|
+
const prefix = slashIndex === -1 ? "eslint" : translated.slice(0, slashIndex);
|
|
1441
|
+
const isNativePrefix = NATIVE_PLUGINS.has(prefix);
|
|
1442
|
+
if (!covered && isOff && keepUnmappedOff) {
|
|
1443
|
+
if (isNativePrefix) {
|
|
1444
|
+
nativeRules[translated] = value;
|
|
1445
|
+
nativePlugins.add(prefix);
|
|
1446
|
+
} else {
|
|
1447
|
+
const specifier = oxlintJsPlugins[prefix];
|
|
1448
|
+
if (specifier !== void 0 && isPackageExists(specifier)) {
|
|
1449
|
+
jsPluginRules[translated] = value;
|
|
1450
|
+
jsPluginPrefixes.add(prefix);
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
continue;
|
|
1454
|
+
}
|
|
1455
|
+
if (!covered && isOff) continue;
|
|
1456
|
+
if ((covered ? mappedTarget(rule) : isNativePrefix ? "native" : "js-plugin") === "js-plugin") {
|
|
1457
|
+
jsPluginRules[translated] = value;
|
|
1458
|
+
jsPluginPrefixes.add(prefix);
|
|
1459
|
+
continue;
|
|
1460
|
+
}
|
|
1461
|
+
if (collapsesToTsCoreRule(rule)) {
|
|
1462
|
+
tsCollapsed.add(translated);
|
|
1463
|
+
nativeRules[translated] = value;
|
|
1464
|
+
} else if (!isTsCoreCounterpartRule(rule) || !tsCollapsed.has(translated)) nativeRules[translated] = value;
|
|
1465
|
+
if (isNativePrefix) nativePlugins.add(prefix);
|
|
1466
|
+
}
|
|
1467
|
+
const jsPlugins = [];
|
|
1468
|
+
for (const prefix of jsPluginPrefixes) {
|
|
1469
|
+
const specifier = oxlintJsPlugins[prefix];
|
|
1470
|
+
if (specifier === void 0) throw new Error(`[@isentinel/eslint-config] Unknown oxlint jsPlugin prefix: ${prefix}`);
|
|
1471
|
+
jsPlugins.push({
|
|
1472
|
+
name: prefix,
|
|
1473
|
+
specifier
|
|
1474
|
+
});
|
|
1475
|
+
}
|
|
1476
|
+
return {
|
|
1477
|
+
jsPluginRules,
|
|
1478
|
+
jsPlugins,
|
|
1479
|
+
nativePlugins: [...nativePlugins],
|
|
1480
|
+
nativeRules
|
|
1481
|
+
};
|
|
1482
|
+
}
|
|
1483
|
+
/**
|
|
1484
|
+
* Create oxlint config fragments from a canonical rule map, one fragment for
|
|
1485
|
+
* native rules and one for jsPlugin rules.
|
|
1486
|
+
*
|
|
1487
|
+
* @param options - The fragment options.
|
|
1488
|
+
* @returns The generated config fragments.
|
|
1489
|
+
*/
|
|
1490
|
+
function createOxlintConfigs({ name, excludeFiles, files, globals, keepUnmappedOff = false, rules, settings }) {
|
|
1491
|
+
const { jsPluginRules, jsPlugins, nativePlugins, nativeRules } = splitOxlintRules(rules, keepUnmappedOff);
|
|
1492
|
+
const fragments = [];
|
|
1493
|
+
if (Object.keys(nativeRules).length > 0) fragments.push({
|
|
1494
|
+
name,
|
|
1495
|
+
...excludeFiles ? { excludeFiles } : {},
|
|
1496
|
+
files,
|
|
1497
|
+
...globals ? { globals } : {},
|
|
1498
|
+
plugins: nativePlugins,
|
|
1499
|
+
rules: nativeRules,
|
|
1500
|
+
...settings ? { settings } : {}
|
|
1501
|
+
});
|
|
1502
|
+
if (Object.keys(jsPluginRules).length > 0) fragments.push({
|
|
1503
|
+
name: `${name}/js-plugin`,
|
|
1504
|
+
...excludeFiles ? { excludeFiles } : {},
|
|
1505
|
+
files,
|
|
1506
|
+
...globals && fragments.length === 0 ? { globals } : {},
|
|
1507
|
+
jsPlugins,
|
|
1508
|
+
rules: jsPluginRules,
|
|
1509
|
+
...settings && fragments.length === 0 ? { settings } : {}
|
|
1510
|
+
});
|
|
1511
|
+
return fragments;
|
|
1512
|
+
}
|
|
1513
|
+
function mappedTarget(rule) {
|
|
1514
|
+
return oxlintRuleMapping[rule] === "js-plugin" ? "js-plugin" : "native";
|
|
1515
|
+
}
|
|
1516
|
+
//#endregion
|
|
1517
|
+
//#region src/oxlint/configs/comments.ts
|
|
1518
|
+
function oxlintComments({ prettierOptions = {}, stylistic = true } = {}) {
|
|
1519
|
+
return [
|
|
1520
|
+
{
|
|
1521
|
+
name: "isentinel/comments/directives",
|
|
1522
|
+
files: [GLOB_SRC],
|
|
1523
|
+
jsPlugins: [{
|
|
1524
|
+
name: "oxlint-comments",
|
|
1525
|
+
specifier: "oxlint-plugin-oxlint-comments"
|
|
1526
|
+
}],
|
|
1527
|
+
rules: {
|
|
1528
|
+
"oxlint-comments/disable-enable-pair": ["error", { allowWholeFile: true }],
|
|
1529
|
+
"oxlint-comments/no-aggregating-enable": "error",
|
|
1530
|
+
"oxlint-comments/no-duplicate-disable": "error",
|
|
1531
|
+
"oxlint-comments/no-unlimited-disable": "error",
|
|
1532
|
+
"oxlint-comments/no-unused-enable": "error",
|
|
1533
|
+
"oxlint-comments/require-description": ["error", { ignore: ["oxlint-enable"] }]
|
|
1534
|
+
}
|
|
1535
|
+
},
|
|
1536
|
+
...createOxlintConfigs({
|
|
1537
|
+
name: "isentinel/comments",
|
|
1538
|
+
files: [GLOB_SRC],
|
|
1539
|
+
rules: commentsRules({ stylistic })
|
|
1540
|
+
}),
|
|
1541
|
+
...stylistic !== false ? createOxlintConfigs({
|
|
1542
|
+
name: "isentinel/comments/length",
|
|
1543
|
+
files: [GLOB_SRC],
|
|
1544
|
+
rules: commentLengthRules({
|
|
1545
|
+
maxLength: (Number(prettierOptions["jsdocPrintWidth"]) || 80) + 2,
|
|
1546
|
+
multiLineMaxLength: Number(prettierOptions["jsdocPrintWidth"]) || 80,
|
|
1547
|
+
semanticComments: ["oxlint-disable", "oxlint-enable"],
|
|
1548
|
+
tabSize: typeof prettierOptions["tabWidth"] === "number" ? prettierOptions["tabWidth"] : 4
|
|
1549
|
+
})
|
|
1550
|
+
}) : []
|
|
1551
|
+
];
|
|
1552
|
+
}
|
|
1553
|
+
//#endregion
|
|
1554
|
+
//#region src/oxlint/configs/disables.ts
|
|
1555
|
+
function oxlintDisables({ root }) {
|
|
1556
|
+
return [
|
|
1557
|
+
...createOxlintConfigs({
|
|
1558
|
+
name: "isentinel/disables/scripts",
|
|
1559
|
+
files: [`**/scripts/${GLOB_SRC}`],
|
|
1560
|
+
rules: {
|
|
1561
|
+
"antfu/no-top-level-await": "off",
|
|
1562
|
+
"no-console": "off",
|
|
1563
|
+
"ts/explicit-function-return-type": "off"
|
|
1564
|
+
}
|
|
1565
|
+
}),
|
|
1566
|
+
...createOxlintConfigs({
|
|
1567
|
+
name: "isentinel/disables/cli",
|
|
1568
|
+
files: [`**/cli/${GLOB_SRC}`, `**/cli.${GLOB_SRC_EXT}`],
|
|
1569
|
+
rules: {
|
|
1570
|
+
"antfu/no-top-level-await": "off",
|
|
1571
|
+
"no-console": "off"
|
|
1572
|
+
}
|
|
1573
|
+
}),
|
|
1574
|
+
...createOxlintConfigs({
|
|
1575
|
+
name: "isentinel/disables/build-tools",
|
|
1576
|
+
files: GLOB_BUILD_TOOLS,
|
|
1577
|
+
rules: {
|
|
1578
|
+
"antfu/no-top-level-await": "off",
|
|
1579
|
+
"no-console": "off",
|
|
1580
|
+
"ts/explicit-function-return-type": "off"
|
|
1581
|
+
}
|
|
1582
|
+
}),
|
|
1583
|
+
...createOxlintConfigs({
|
|
1584
|
+
name: "isentinel/disables/bin",
|
|
1585
|
+
files: ["**/bin/**/*", `**/bin.${GLOB_SRC_EXT}`],
|
|
1586
|
+
rules: {
|
|
1587
|
+
"antfu/no-import-dist": "off",
|
|
1588
|
+
"antfu/no-import-node-modules-by-path": "off",
|
|
1589
|
+
"antfu/no-top-level-await": "off"
|
|
1590
|
+
}
|
|
1591
|
+
}),
|
|
1592
|
+
...createOxlintConfigs({
|
|
1593
|
+
name: "isentinel/disables/dts",
|
|
1594
|
+
files: [GLOB_DTS],
|
|
1595
|
+
keepUnmappedOff: true,
|
|
1596
|
+
rules: {
|
|
1597
|
+
"max-lines": "off",
|
|
1598
|
+
"no-duplicate-imports": "off",
|
|
1599
|
+
"no-restricted-syntax": "off",
|
|
1600
|
+
"oxc/no-barrel-file": "off",
|
|
1601
|
+
"roblox/no-namespace-merging": "off",
|
|
1602
|
+
"small-rules/prefer-class-properties": "off",
|
|
1603
|
+
"sonar/no-duplicate-string": "off",
|
|
1604
|
+
"unused-imports/no-unused-vars": "off"
|
|
1605
|
+
}
|
|
1606
|
+
}),
|
|
1607
|
+
{
|
|
1608
|
+
name: "isentinel/disables/dts/directives",
|
|
1609
|
+
files: [GLOB_DTS],
|
|
1610
|
+
jsPlugins: [{
|
|
1611
|
+
name: "oxlint-comments",
|
|
1612
|
+
specifier: "oxlint-plugin-oxlint-comments"
|
|
1613
|
+
}],
|
|
1614
|
+
rules: { "oxlint-comments/no-unlimited-disable": "off" }
|
|
1615
|
+
},
|
|
1616
|
+
...createOxlintConfigs({
|
|
1617
|
+
name: "isentinel/disables/test",
|
|
1618
|
+
files: [...GLOB_TESTS],
|
|
1619
|
+
rules: {
|
|
1620
|
+
"antfu/no-top-level-await": "off",
|
|
1621
|
+
"max-lines": "off",
|
|
1622
|
+
"max-lines-per-function": "off",
|
|
1623
|
+
"no-empty-function": "off",
|
|
1624
|
+
"no-unused-expressions": "off",
|
|
1625
|
+
"sonar/no-duplicate-string": "off",
|
|
1626
|
+
"ts/explicit-function-return-type": "off",
|
|
1627
|
+
"ts/no-empty-function": "off",
|
|
1628
|
+
"ts/no-extraneous-class": "off",
|
|
1629
|
+
"ts/no-non-null-assertion": "off",
|
|
1630
|
+
"ts/unbound-method": "off",
|
|
1631
|
+
"unicorn/consistent-function-scoping": "off"
|
|
1632
|
+
}
|
|
1633
|
+
}),
|
|
1634
|
+
...createOxlintConfigs({
|
|
1635
|
+
name: "isentinel/disables/cjs",
|
|
1636
|
+
files: ["**/*.js", "**/*.cjs"],
|
|
1637
|
+
rules: { "ts/no-require-imports": "off" }
|
|
1638
|
+
}),
|
|
1639
|
+
...createOxlintConfigs({
|
|
1640
|
+
name: "isentinel/disables/root",
|
|
1641
|
+
files: [...root],
|
|
1642
|
+
rules: {
|
|
1643
|
+
"sonar/file-name-differ-from-class": "off",
|
|
1644
|
+
"unicorn/filename-case": "off"
|
|
1645
|
+
}
|
|
1646
|
+
}),
|
|
1647
|
+
...createOxlintConfigs({
|
|
1648
|
+
name: "isentinel/disables/jsx",
|
|
1649
|
+
files: [GLOB_JSX, GLOB_TSX],
|
|
1650
|
+
rules: { "max-lines-per-function": "off" }
|
|
1651
|
+
})
|
|
1652
|
+
];
|
|
1653
|
+
}
|
|
1654
|
+
//#endregion
|
|
1655
|
+
//#region src/rules/e18e.ts
|
|
1656
|
+
/** `Map.prototype.getOrInsert` landed in V8 14.6, shipped in Node 26. */
|
|
1657
|
+
const NODE_GET_OR_INSERT = 26;
|
|
1658
|
+
/**
|
|
1659
|
+
* The e18e source rules shared between the ESLint and oxlint factories. Mirrors
|
|
1660
|
+
* the `modernization` and `performanceImprovements` presets of
|
|
1661
|
+
* `@e18e/eslint-plugin`; all rules are syntax-only (no type information).
|
|
1662
|
+
*
|
|
1663
|
+
* The `moduleReplacements` preset is not included here: its only rule,
|
|
1664
|
+
* `ban-dependencies`, has JSON-only visitors and so is applied separately
|
|
1665
|
+
* against `package.json` by the ESLint factory.
|
|
1666
|
+
*
|
|
1667
|
+
* @param options - Which preset groups to enable.
|
|
1668
|
+
* @returns The rule map.
|
|
1669
|
+
*/
|
|
1670
|
+
function e18eRules({ modernization = true, nodeMajor, performanceImprovements = true } = {}) {
|
|
1671
|
+
return {
|
|
1672
|
+
...modernization ? {
|
|
1673
|
+
"e18e/prefer-array-at": "error",
|
|
1674
|
+
"e18e/prefer-array-fill": "error",
|
|
1675
|
+
"e18e/prefer-array-to-reversed": "error",
|
|
1676
|
+
"e18e/prefer-array-to-sorted": "error",
|
|
1677
|
+
"e18e/prefer-array-to-spliced": "error",
|
|
1678
|
+
"e18e/prefer-includes": "error",
|
|
1679
|
+
"e18e/prefer-nullish-coalescing": "error",
|
|
1680
|
+
"e18e/prefer-object-has-own": "error",
|
|
1681
|
+
"e18e/prefer-spread-syntax": "error",
|
|
1682
|
+
"e18e/prefer-url-canparse": "error",
|
|
1683
|
+
...nodeMajor !== void 0 && nodeMajor >= NODE_GET_OR_INSERT ? { "e18e/prefer-get-or-insert": "error" } : {}
|
|
1684
|
+
} : {},
|
|
1685
|
+
...performanceImprovements ? {
|
|
1686
|
+
"e18e/no-spread-in-reduce": "error",
|
|
1687
|
+
"e18e/prefer-array-from-map": "error",
|
|
1688
|
+
"e18e/prefer-array-some": "error",
|
|
1689
|
+
"e18e/prefer-date-now": "error",
|
|
1690
|
+
"e18e/prefer-includes-over-regex-test": "error",
|
|
1691
|
+
"e18e/prefer-regex-test": "error",
|
|
1692
|
+
"e18e/prefer-static-collator": "error",
|
|
1693
|
+
"e18e/prefer-static-regex": "error",
|
|
1694
|
+
"e18e/prefer-string-fromcharcode": "error",
|
|
1695
|
+
"e18e/prefer-timer-args": "error"
|
|
1696
|
+
} : {}
|
|
1697
|
+
};
|
|
1698
|
+
}
|
|
1699
|
+
//#endregion
|
|
1700
|
+
//#region src/oxlint/configs/e18e.ts
|
|
1701
|
+
function oxlintE18e({ excludeFiles, files = [GLOB_SRC], modernization = true, nodeMajor = resolveNodeMajor(), overrides = {}, performanceImprovements = true } = {}) {
|
|
1702
|
+
return [...createOxlintConfigs({
|
|
1703
|
+
name: "isentinel/e18e",
|
|
1704
|
+
...excludeFiles ? { excludeFiles } : {},
|
|
1705
|
+
files: files.flat(),
|
|
1706
|
+
rules: {
|
|
1707
|
+
...e18eRules({
|
|
1708
|
+
modernization,
|
|
1709
|
+
nodeMajor,
|
|
1710
|
+
performanceImprovements
|
|
1711
|
+
}),
|
|
1712
|
+
...overrides
|
|
1713
|
+
}
|
|
1714
|
+
}), ...createOxlintConfigs({
|
|
1715
|
+
name: "isentinel/e18e/disables/test",
|
|
1716
|
+
files: [...GLOB_TESTS],
|
|
1717
|
+
rules: { "e18e/prefer-static-regex": "off" }
|
|
1718
|
+
})];
|
|
1719
|
+
}
|
|
1720
|
+
//#endregion
|
|
1721
|
+
//#region src/rules/eslint-plugin.ts
|
|
1722
|
+
/**
|
|
1723
|
+
* ESLint-plugin-development rules shared between the ESLint and oxlint
|
|
1724
|
+
* factories.
|
|
1725
|
+
*
|
|
1726
|
+
* @returns The rule map.
|
|
1727
|
+
*/
|
|
1728
|
+
function eslintPluginRules() {
|
|
1729
|
+
return {
|
|
1730
|
+
"eslint-plugin/consistent-output": "error",
|
|
1731
|
+
"eslint-plugin/fixer-return": "error",
|
|
1732
|
+
"eslint-plugin/meta-property-ordering": "off",
|
|
1733
|
+
"eslint-plugin/no-deprecated-context-methods": "error",
|
|
1734
|
+
"eslint-plugin/no-deprecated-report-api": "error",
|
|
1735
|
+
"eslint-plugin/no-identical-tests": "error",
|
|
1736
|
+
"eslint-plugin/no-matching-violation-suggest-message-ids": "error",
|
|
1737
|
+
"eslint-plugin/no-meta-replaced-by": "error",
|
|
1738
|
+
"eslint-plugin/no-meta-schema-default": "error",
|
|
1739
|
+
"eslint-plugin/no-missing-message-ids": "error",
|
|
1740
|
+
"eslint-plugin/no-missing-placeholders": "error",
|
|
1741
|
+
"eslint-plugin/no-only-tests": "error",
|
|
1742
|
+
"eslint-plugin/no-property-in-node": "error",
|
|
1743
|
+
"eslint-plugin/no-unused-message-ids": "error",
|
|
1744
|
+
"eslint-plugin/no-unused-placeholders": "error",
|
|
1745
|
+
"eslint-plugin/no-useless-token-range": "error",
|
|
1746
|
+
"eslint-plugin/prefer-message-ids": "error",
|
|
1747
|
+
"eslint-plugin/prefer-object-rule": "error",
|
|
1748
|
+
"eslint-plugin/prefer-output-null": "error",
|
|
1749
|
+
"eslint-plugin/prefer-placeholders": "error",
|
|
1750
|
+
"eslint-plugin/prefer-replace-text": "error",
|
|
1751
|
+
"eslint-plugin/report-message-format": "error",
|
|
1752
|
+
"eslint-plugin/require-meta-default-options": "error",
|
|
1753
|
+
"eslint-plugin/require-meta-docs-description": ["error", { pattern: "^(Enforce|Require|Disallow).*[^.!]$" }],
|
|
1754
|
+
"eslint-plugin/require-meta-docs-recommended": "error",
|
|
1755
|
+
"eslint-plugin/require-meta-docs-url": "off",
|
|
1756
|
+
"eslint-plugin/require-meta-fixable": "error",
|
|
1757
|
+
"eslint-plugin/require-meta-has-suggestions": "error",
|
|
1758
|
+
"eslint-plugin/require-meta-schema": "error",
|
|
1759
|
+
"eslint-plugin/require-meta-schema-description": "error",
|
|
1760
|
+
"eslint-plugin/require-meta-type": "error",
|
|
1761
|
+
"eslint-plugin/require-test-case-name": "error",
|
|
1762
|
+
"eslint-plugin/require-test-error-positions": "error",
|
|
1763
|
+
"eslint-plugin/test-case-property-ordering": "error",
|
|
1764
|
+
"eslint-plugin/test-case-shorthand-strings": "error",
|
|
1765
|
+
"eslint-plugin/unique-test-case-names": "error"
|
|
1766
|
+
};
|
|
1767
|
+
}
|
|
1768
|
+
//#endregion
|
|
1769
|
+
//#region src/oxlint/configs/eslint-plugin.ts
|
|
1770
|
+
function oxlintEslintPlugin(options = {}) {
|
|
1771
|
+
const { overrides = {} } = options;
|
|
1772
|
+
return createOxlintConfigs({
|
|
1773
|
+
name: "isentinel/eslint-plugin",
|
|
1774
|
+
files: options.files?.flat() ?? ["**/*.{,c,m}[jt]s{,x}"],
|
|
1775
|
+
rules: {
|
|
1776
|
+
...eslintPluginRules(),
|
|
1777
|
+
...overrides
|
|
1778
|
+
}
|
|
1779
|
+
});
|
|
1780
|
+
}
|
|
1781
|
+
//#endregion
|
|
1782
|
+
//#region src/rules/flawless.ts
|
|
1783
|
+
/**
|
|
1784
|
+
* Base (non-React) flawless rules shared between the ESLint and oxlint
|
|
1785
|
+
* factories. The React flawless rules live in the react rule map; the
|
|
1786
|
+
* type-aware `flawless/naming-convention` and the non-JS `flawless/toml-*` and
|
|
1787
|
+
* `flawless/yaml-*` rules are configured by their own configs and stay in
|
|
1788
|
+
* ESLint.
|
|
1789
|
+
*
|
|
1790
|
+
* @param options - Shared stylistic options.
|
|
1791
|
+
* @returns The rule map.
|
|
1792
|
+
*/
|
|
1793
|
+
function flawlessRules({ stylistic = true } = {}) {
|
|
1794
|
+
return { ...stylistic !== false ? { "flawless/prefer-parameter-destructuring": "warn" } : {} };
|
|
1795
|
+
}
|
|
1796
|
+
//#endregion
|
|
1797
|
+
//#region src/oxlint/configs/flawless.ts
|
|
1798
|
+
function oxlintFlawless({ stylistic = true } = {}) {
|
|
1799
|
+
return createOxlintConfigs({
|
|
1800
|
+
name: "isentinel/flawless",
|
|
1801
|
+
files: [GLOB_TS],
|
|
1802
|
+
rules: flawlessRules({ stylistic })
|
|
1803
|
+
});
|
|
1804
|
+
}
|
|
1805
|
+
//#endregion
|
|
1806
|
+
//#region src/oxlint/configs/gitignore.ts
|
|
1807
|
+
const LINE_BREAK_PATTERN = /\r?\n/;
|
|
1808
|
+
/**
|
|
1809
|
+
* Reads `.gitignore` and `.git/info/exclude` and returns their patterns as an
|
|
1810
|
+
* array for oxlint's top-level `ignorePatterns`.
|
|
1811
|
+
*
|
|
1812
|
+
* Oxlint does not natively support `.git/info/exclude`, so we parse it
|
|
1813
|
+
* ourselves.
|
|
1814
|
+
*
|
|
1815
|
+
* @returns Parsed ignore patterns from gitignore files.
|
|
1816
|
+
*/
|
|
1817
|
+
function oxlintGitignore() {
|
|
1818
|
+
const cwd = process.cwd();
|
|
1819
|
+
const patterns = [];
|
|
1820
|
+
const gitignoreFile = findUpSync(".gitignore", { cwd });
|
|
1821
|
+
if (gitignoreFile !== void 0) patterns.push(...parseGitignoreFile(gitignoreFile));
|
|
1822
|
+
const gitDirectory = findUpSync(".git", {
|
|
1823
|
+
cwd,
|
|
1824
|
+
type: "directory"
|
|
1825
|
+
});
|
|
1826
|
+
if (gitDirectory !== void 0) {
|
|
1827
|
+
const excludeFile = path.join(gitDirectory, "info", "exclude");
|
|
1828
|
+
try {
|
|
1829
|
+
patterns.push(...parseGitignoreContent(readFileSync(excludeFile, "utf-8")));
|
|
1830
|
+
} catch {}
|
|
1831
|
+
}
|
|
1832
|
+
return patterns;
|
|
1833
|
+
}
|
|
1834
|
+
function parseGitignoreContent(content) {
|
|
1835
|
+
const patterns = [];
|
|
1836
|
+
for (const raw of content.split(LINE_BREAK_PATTERN)) {
|
|
1837
|
+
const line = raw.trim();
|
|
1838
|
+
if (line !== "" && !line.startsWith("#")) patterns.push(line);
|
|
1839
|
+
}
|
|
1840
|
+
return patterns;
|
|
1841
|
+
}
|
|
1842
|
+
function parseGitignoreFile(filePath) {
|
|
1843
|
+
return parseGitignoreContent(readFileSync(filePath, "utf-8"));
|
|
1844
|
+
}
|
|
1845
|
+
//#endregion
|
|
1846
|
+
//#region src/rules/imports.ts
|
|
1847
|
+
/**
|
|
1848
|
+
* Import rules shared between the ESLint and oxlint factories.
|
|
1849
|
+
*
|
|
1850
|
+
* @param options - Shared rule options.
|
|
1851
|
+
* @returns The rule map.
|
|
1852
|
+
*/
|
|
1853
|
+
function importsRules({ stylistic = true } = {}) {
|
|
1854
|
+
return {
|
|
1855
|
+
"antfu/import-dedupe": "error",
|
|
1856
|
+
"antfu/no-import-dist": "error",
|
|
1857
|
+
"antfu/no-import-node-modules-by-path": "error",
|
|
1858
|
+
"import/first": "error",
|
|
1859
|
+
"import/no-mutable-exports": "error",
|
|
1860
|
+
"import/no-named-default": "error",
|
|
1861
|
+
...stylistic !== false ? { "import/newline-after-import": ["error", {
|
|
1862
|
+
considerComments: true,
|
|
1863
|
+
count: 1
|
|
1864
|
+
}] } : {}
|
|
1865
|
+
};
|
|
1866
|
+
}
|
|
1867
|
+
/**
|
|
1868
|
+
* Game-only import restrictions shared between the ESLint and oxlint
|
|
1869
|
+
* factories.
|
|
1870
|
+
*
|
|
1871
|
+
* @returns The rule map.
|
|
1872
|
+
*/
|
|
1873
|
+
function importsGameRules() {
|
|
1874
|
+
return { "no-restricted-syntax": ["error", {
|
|
1875
|
+
message: "Prefer named exports",
|
|
1876
|
+
selector: "ExportDefaultDeclaration"
|
|
1877
|
+
}] };
|
|
1878
|
+
}
|
|
1879
|
+
//#endregion
|
|
1880
|
+
//#region src/oxlint/configs/imports.ts
|
|
1881
|
+
function oxlintImports({ stylistic = true, type = "game" } = {}) {
|
|
1882
|
+
return [...createOxlintConfigs({
|
|
1883
|
+
name: "isentinel/imports",
|
|
1884
|
+
files: [GLOB_SRC],
|
|
1885
|
+
rules: importsRules({ stylistic })
|
|
1886
|
+
}), ...type === "game" ? createOxlintConfigs({
|
|
1887
|
+
name: "isentinel/imports/game",
|
|
1888
|
+
files: [`src/${GLOB_SRC}`],
|
|
1889
|
+
rules: importsGameRules()
|
|
1890
|
+
}) : []];
|
|
1891
|
+
}
|
|
1892
|
+
//#endregion
|
|
1893
|
+
//#region src/rules/javascript.ts
|
|
1894
|
+
/**
|
|
1895
|
+
* Core JavaScript rules shared between the ESLint and oxlint factories.
|
|
1896
|
+
*
|
|
1897
|
+
* Rule names use the canonical (renamed) ESLint prefixes. The oxlint factory
|
|
1898
|
+
* translates names via the oxlint rule mapping.
|
|
1899
|
+
*
|
|
1900
|
+
* @param options - Shared rule options.
|
|
1901
|
+
* @returns The rule map.
|
|
1902
|
+
*/
|
|
1903
|
+
function javascriptRules({ isInEditor = false, roblox = true, stylistic = true } = {}) {
|
|
1904
|
+
return {
|
|
1905
|
+
"accessor-pairs": ["error", {
|
|
1906
|
+
enforceForClassMembers: true,
|
|
1907
|
+
setWithoutGet: true
|
|
1908
|
+
}],
|
|
1909
|
+
"antfu/no-top-level-await": "error",
|
|
1910
|
+
"array-callback-return": ["error", { allowImplicit: true }],
|
|
1911
|
+
"better-max-params/better-max-params": ["error", { func: 4 }],
|
|
1912
|
+
"block-scoped-var": "error",
|
|
1913
|
+
"constructor-super": "error",
|
|
1914
|
+
"de-morgan/no-negated-conjunction": "error",
|
|
1915
|
+
"de-morgan/no-negated-disjunction": "error",
|
|
1916
|
+
"default-case-last": "error",
|
|
1917
|
+
"dot-notation": ["error", { allowKeywords: true }],
|
|
1918
|
+
"eqeqeq": "error",
|
|
1919
|
+
"for-direction": "error",
|
|
1920
|
+
"logical-assignment-operators": "error",
|
|
1921
|
+
"max-classes-per-file": "error",
|
|
1922
|
+
"max-depth": "error",
|
|
1923
|
+
"new-cap": ["error", {
|
|
1924
|
+
capIsNew: false,
|
|
1925
|
+
newIsCap: true,
|
|
1926
|
+
newIsCapExceptionPattern: "^mock",
|
|
1927
|
+
properties: true
|
|
1928
|
+
}],
|
|
1929
|
+
"no-alert": "error",
|
|
1930
|
+
"no-array-constructor": "error",
|
|
1931
|
+
"no-async-promise-executor": "error",
|
|
1932
|
+
"no-caller": "error",
|
|
1933
|
+
"no-case-declarations": "error",
|
|
1934
|
+
"no-class-assign": "error",
|
|
1935
|
+
"no-compare-neg-zero": "error",
|
|
1936
|
+
"no-cond-assign": ["error", "always"],
|
|
1937
|
+
"no-console": ["error", { allow: ["warn", "error"] }],
|
|
1938
|
+
"no-const-assign": "error",
|
|
1939
|
+
"no-constant-condition": ["error", { checkLoops: false }],
|
|
1940
|
+
"no-control-regex": "error",
|
|
1941
|
+
"no-debugger": "error",
|
|
1942
|
+
"no-delete-var": "error",
|
|
1943
|
+
"no-dupe-args": "error",
|
|
1944
|
+
"no-dupe-class-members": "error",
|
|
1945
|
+
"no-dupe-keys": "error",
|
|
1946
|
+
"no-duplicate-case": "error",
|
|
1947
|
+
"no-duplicate-imports": ["error", { allowSeparateTypeImports: true }],
|
|
1948
|
+
"no-else-return": ["error", { allowElseIf: false }],
|
|
1949
|
+
"no-empty": ["error", { allowEmptyCatch: true }],
|
|
1950
|
+
"no-empty-character-class": "error",
|
|
1951
|
+
"no-empty-function": "error",
|
|
1952
|
+
"no-empty-pattern": "error",
|
|
1953
|
+
"no-empty-static-block": "error",
|
|
1954
|
+
"no-eval": "error",
|
|
1955
|
+
"no-ex-assign": "error",
|
|
1956
|
+
"no-extend-native": "error",
|
|
1957
|
+
"no-extra-bind": "error",
|
|
1958
|
+
"no-extra-boolean-cast": "error",
|
|
1959
|
+
"no-fallthrough": "error",
|
|
1960
|
+
"no-func-assign": "error",
|
|
1961
|
+
"no-global-assign": "error",
|
|
1962
|
+
"no-implied-eval": "error",
|
|
1963
|
+
"no-import-assign": "error",
|
|
1964
|
+
"no-invalid-regexp": "error",
|
|
1965
|
+
"no-irregular-whitespace": "error",
|
|
1966
|
+
"no-iterator": "error",
|
|
1967
|
+
"no-labels": ["error", {
|
|
1968
|
+
allowLoop: false,
|
|
1969
|
+
allowSwitch: false
|
|
1970
|
+
}],
|
|
1971
|
+
"no-lonely-if": "error",
|
|
1972
|
+
"no-loop-func": "error",
|
|
1973
|
+
"no-loss-of-precision": "error",
|
|
1974
|
+
"no-misleading-character-class": "error",
|
|
1975
|
+
"no-new": "error",
|
|
1976
|
+
"no-new-func": "error",
|
|
1977
|
+
"no-new-native-nonconstructor": "error",
|
|
1978
|
+
"no-new-wrappers": "error",
|
|
1979
|
+
"no-obj-calls": "error",
|
|
1980
|
+
"no-octal": "error",
|
|
1981
|
+
"no-octal-escape": "error",
|
|
1982
|
+
"no-proto": "error",
|
|
1983
|
+
"no-prototype-builtins": "error",
|
|
1984
|
+
"no-redeclare": ["error", { builtinGlobals: false }],
|
|
1985
|
+
"no-regex-spaces": "error",
|
|
1986
|
+
"no-restricted-globals": [
|
|
1987
|
+
"error",
|
|
1988
|
+
{
|
|
1989
|
+
name: "global",
|
|
1990
|
+
message: "Use `globalThis` instead."
|
|
1991
|
+
},
|
|
1992
|
+
{
|
|
1993
|
+
name: "self",
|
|
1994
|
+
message: "Use `globalThis` instead."
|
|
1995
|
+
}
|
|
1996
|
+
],
|
|
1997
|
+
"no-restricted-properties": [
|
|
1998
|
+
"error",
|
|
1999
|
+
{
|
|
2000
|
+
message: "Use `Object.getPrototypeOf` or `Object.setPrototypeOf` instead.",
|
|
2001
|
+
property: "__proto__"
|
|
2002
|
+
},
|
|
2003
|
+
{
|
|
2004
|
+
message: "Use `Object.defineProperty` instead.",
|
|
2005
|
+
property: "__defineGetter__"
|
|
2006
|
+
},
|
|
2007
|
+
{
|
|
2008
|
+
message: "Use `Object.defineProperty` instead.",
|
|
2009
|
+
property: "__defineSetter__"
|
|
2010
|
+
},
|
|
2011
|
+
{
|
|
2012
|
+
message: "Use `Object.getOwnPropertyDescriptor` instead.",
|
|
2013
|
+
property: "__lookupGetter__"
|
|
2014
|
+
},
|
|
2015
|
+
{
|
|
2016
|
+
message: "Use `Object.getOwnPropertyDescriptor` instead.",
|
|
2017
|
+
property: "__lookupSetter__"
|
|
2018
|
+
}
|
|
2019
|
+
],
|
|
2020
|
+
"no-restricted-syntax": [
|
|
2021
|
+
"error",
|
|
2022
|
+
"TSEnumDeclaration[const=true]",
|
|
2023
|
+
"TSExportAssignment"
|
|
2024
|
+
],
|
|
2025
|
+
"no-return-assign": ["error", "always"],
|
|
2026
|
+
"no-self-assign": ["error", { props: true }],
|
|
2027
|
+
"no-self-compare": "error",
|
|
2028
|
+
"no-sequences": "error",
|
|
2029
|
+
"no-shadow-restricted-names": "error",
|
|
2030
|
+
"no-sparse-arrays": "error",
|
|
2031
|
+
"no-template-curly-in-string": "error",
|
|
2032
|
+
"no-this-before-super": "error",
|
|
2033
|
+
"no-throw-literal": "error",
|
|
2034
|
+
"no-undef": "error",
|
|
2035
|
+
"no-undef-init": "error",
|
|
2036
|
+
"no-unexpected-multiline": "error",
|
|
2037
|
+
"no-unmodified-loop-condition": "error",
|
|
2038
|
+
"no-unneeded-ternary": ["error", { defaultAssignment: false }],
|
|
2039
|
+
"no-unreachable": "error",
|
|
2040
|
+
"no-unreachable-loop": "error",
|
|
2041
|
+
"no-unsafe-finally": "error",
|
|
2042
|
+
"no-unsafe-negation": "error",
|
|
2043
|
+
"no-unused-expressions": ["error", {
|
|
2044
|
+
allowShortCircuit: true,
|
|
2045
|
+
allowTaggedTemplates: true,
|
|
2046
|
+
allowTernary: true
|
|
2047
|
+
}],
|
|
2048
|
+
"no-unused-vars": "off",
|
|
2049
|
+
"no-use-before-define": ["error", {
|
|
2050
|
+
classes: false,
|
|
2051
|
+
functions: false,
|
|
2052
|
+
variables: true
|
|
2053
|
+
}],
|
|
2054
|
+
"no-useless-backreference": "error",
|
|
2055
|
+
"no-useless-call": "error",
|
|
2056
|
+
"no-useless-catch": "error",
|
|
2057
|
+
"no-useless-computed-key": "error",
|
|
2058
|
+
"no-useless-constructor": "error",
|
|
2059
|
+
"no-useless-rename": "error",
|
|
2060
|
+
"no-useless-return": "error",
|
|
2061
|
+
"no-var": "error",
|
|
2062
|
+
"no-with": "error",
|
|
2063
|
+
"prefer-arrow-callback": ["error", {
|
|
2064
|
+
allowNamedFunctions: false,
|
|
2065
|
+
allowUnboundThis: true
|
|
2066
|
+
}],
|
|
2067
|
+
"prefer-const": [isInEditor ? "warn" : "error", {
|
|
2068
|
+
destructuring: "all",
|
|
2069
|
+
ignoreReadBeforeAssign: true
|
|
2070
|
+
}],
|
|
2071
|
+
"prefer-exponentiation-operator": "error",
|
|
2072
|
+
"prefer-promise-reject-errors": "error",
|
|
2073
|
+
"prefer-regex-literals": roblox ? "off" : ["error", { disallowRedundantWrapping: true }],
|
|
2074
|
+
"prefer-rest-params": "error",
|
|
2075
|
+
"prefer-spread": "error",
|
|
2076
|
+
"prefer-template": "error",
|
|
2077
|
+
"symbol-description": "error",
|
|
2078
|
+
"unicode-bom": ["error", "never"],
|
|
2079
|
+
"unused-imports/no-unused-imports": isInEditor ? "warn" : "error",
|
|
2080
|
+
"unused-imports/no-unused-vars": ["error", {
|
|
2081
|
+
args: "all",
|
|
2082
|
+
argsIgnorePattern: "^_+",
|
|
2083
|
+
caughtErrors: "all",
|
|
2084
|
+
caughtErrorsIgnorePattern: "^_+",
|
|
2085
|
+
destructuredArrayIgnorePattern: "^_+",
|
|
2086
|
+
ignoreRestSiblings: true,
|
|
2087
|
+
reportUsedIgnorePattern: true,
|
|
2088
|
+
vars: "all",
|
|
2089
|
+
varsIgnorePattern: "^_+"
|
|
2090
|
+
}],
|
|
2091
|
+
"use-isnan": ["error", {
|
|
2092
|
+
enforceForIndexOf: true,
|
|
2093
|
+
enforceForSwitchCase: true
|
|
2094
|
+
}],
|
|
2095
|
+
"valid-typeof": ["error", { requireStringLiterals: true }],
|
|
2096
|
+
"vars-on-top": "error",
|
|
2097
|
+
...stylistic !== false ? {
|
|
2098
|
+
"id-length": ["error", {
|
|
2099
|
+
exceptions: [
|
|
2100
|
+
"_",
|
|
2101
|
+
"x",
|
|
2102
|
+
"y",
|
|
2103
|
+
"z",
|
|
2104
|
+
"a",
|
|
2105
|
+
"b",
|
|
2106
|
+
"e"
|
|
2107
|
+
],
|
|
2108
|
+
max: 30,
|
|
2109
|
+
min: 2,
|
|
2110
|
+
properties: "never"
|
|
2111
|
+
}],
|
|
2112
|
+
"max-lines": ["warn", {
|
|
2113
|
+
max: 300,
|
|
2114
|
+
skipBlankLines: true,
|
|
2115
|
+
skipComments: true
|
|
2116
|
+
}],
|
|
2117
|
+
"max-lines-per-function": ["warn", {
|
|
2118
|
+
max: 30,
|
|
2119
|
+
skipBlankLines: true,
|
|
2120
|
+
skipComments: true
|
|
2121
|
+
}],
|
|
2122
|
+
"no-lone-blocks": "error",
|
|
2123
|
+
"no-multi-str": "error",
|
|
2124
|
+
"object-shorthand": [
|
|
2125
|
+
"error",
|
|
2126
|
+
"always",
|
|
2127
|
+
{
|
|
2128
|
+
avoidQuotes: true,
|
|
2129
|
+
ignoreConstructors: false
|
|
2130
|
+
}
|
|
2131
|
+
],
|
|
2132
|
+
"one-var": ["error", { initialized: "never" }],
|
|
2133
|
+
"yoda": ["error", "never"],
|
|
2134
|
+
...!roblox ? { "func-style": ["error", "declaration"] } : {}
|
|
2135
|
+
} : {}
|
|
2136
|
+
};
|
|
2137
|
+
}
|
|
2138
|
+
//#endregion
|
|
2139
|
+
//#region src/oxlint/configs/javascript.ts
|
|
2140
|
+
function oxlintJavascript(options = {}) {
|
|
2141
|
+
const { excludeFiles, isInEditor = false, overrides = {}, roblox = true, stylistic = true } = options;
|
|
2142
|
+
const files = options.files?.flat() ?? ["**/*.{,c,m}[jt]s{,x}"];
|
|
2143
|
+
return createOxlintConfigs({
|
|
2144
|
+
name: "isentinel/javascript",
|
|
2145
|
+
...excludeFiles ? { excludeFiles } : {},
|
|
2146
|
+
files,
|
|
2147
|
+
globals: {
|
|
2148
|
+
...toGlobals(globals.browser),
|
|
2149
|
+
...toGlobals(globals.es2021),
|
|
2150
|
+
...toGlobals(globals.node),
|
|
2151
|
+
document: "readonly",
|
|
2152
|
+
navigator: "readonly",
|
|
2153
|
+
window: "readonly"
|
|
2154
|
+
},
|
|
2155
|
+
rules: {
|
|
2156
|
+
...javascriptRules({
|
|
2157
|
+
isInEditor,
|
|
2158
|
+
roblox,
|
|
2159
|
+
stylistic
|
|
2160
|
+
}),
|
|
2161
|
+
...overrides
|
|
2162
|
+
}
|
|
2163
|
+
});
|
|
2164
|
+
}
|
|
2165
|
+
function toGlobals(source, override) {
|
|
2166
|
+
const result = {};
|
|
2167
|
+
for (const [key, value] of Object.entries(source)) result[key] = override ?? (value ? "writable" : "readonly");
|
|
2168
|
+
return result;
|
|
2169
|
+
}
|
|
2170
|
+
//#endregion
|
|
2171
|
+
//#region src/rules/jsdoc.ts
|
|
2172
|
+
/**
|
|
2173
|
+
* JSDoc rules shared between the ESLint and oxlint factories.
|
|
2174
|
+
*
|
|
2175
|
+
* @param options - Shared rule options.
|
|
2176
|
+
* @returns The rule map.
|
|
2177
|
+
*/
|
|
2178
|
+
function jsdocRules({ full = false, stylistic = true, type = "game" } = {}) {
|
|
2179
|
+
return {
|
|
2180
|
+
"jsdoc/check-access": "warn",
|
|
2181
|
+
"jsdoc/check-param-names": ["warn", { checkDestructured: false }],
|
|
2182
|
+
"jsdoc/check-property-names": "warn",
|
|
2183
|
+
"jsdoc/check-types": "warn",
|
|
2184
|
+
"jsdoc/empty-tags": "warn",
|
|
2185
|
+
"jsdoc/implements-on-classes": "warn",
|
|
2186
|
+
"jsdoc/informative-docs": "warn",
|
|
2187
|
+
"jsdoc/no-defaults": "warn",
|
|
2188
|
+
"jsdoc/no-types": "warn",
|
|
2189
|
+
"jsdoc/no-undefined-types": "error",
|
|
2190
|
+
"jsdoc/require-description": ["warn", { exemptedBy: [
|
|
2191
|
+
"hidden",
|
|
2192
|
+
"ignore",
|
|
2193
|
+
"inheritdoc",
|
|
2194
|
+
"client",
|
|
2195
|
+
"server",
|
|
2196
|
+
"see",
|
|
2197
|
+
"metadata"
|
|
2198
|
+
] }],
|
|
2199
|
+
"jsdoc/require-description-complete-sentence": "warn",
|
|
2200
|
+
"jsdoc/require-param-description": "warn",
|
|
2201
|
+
"jsdoc/require-param-name": "warn",
|
|
2202
|
+
"jsdoc/require-property": "warn",
|
|
2203
|
+
"jsdoc/require-property-description": "warn",
|
|
2204
|
+
"jsdoc/require-property-name": "warn",
|
|
2205
|
+
"jsdoc/require-rejects": "error",
|
|
2206
|
+
"jsdoc/require-returns-check": "warn",
|
|
2207
|
+
"jsdoc/require-returns-description": "warn",
|
|
2208
|
+
"jsdoc/require-yields-check": "warn",
|
|
2209
|
+
"jsdoc/sort-tags": "off",
|
|
2210
|
+
...type === "package" || full ? {
|
|
2211
|
+
"jsdoc/require-param": ["warn", {
|
|
2212
|
+
checkDestructured: false,
|
|
2213
|
+
exemptedBy: ["ignore"]
|
|
2214
|
+
}],
|
|
2215
|
+
"jsdoc/require-returns": ["warn", { exemptedBy: ["hidden"] }],
|
|
2216
|
+
"jsdoc/require-template": "warn"
|
|
2217
|
+
} : {},
|
|
2218
|
+
...stylistic !== false ? {
|
|
2219
|
+
"jsdoc/check-alignment": "warn",
|
|
2220
|
+
"jsdoc/convert-to-jsdoc-comments": ["warn", { allowedPrefixes: [
|
|
2221
|
+
"@ts-",
|
|
2222
|
+
"istanbul ",
|
|
2223
|
+
"c8 ",
|
|
2224
|
+
"v8 ",
|
|
2225
|
+
"eslint",
|
|
2226
|
+
"jshint",
|
|
2227
|
+
"jslint",
|
|
2228
|
+
"globals",
|
|
2229
|
+
"exported",
|
|
2230
|
+
"jscs",
|
|
2231
|
+
"oxlint-",
|
|
2232
|
+
"prettier-"
|
|
2233
|
+
] }],
|
|
2234
|
+
"jsdoc/multiline-blocks": "warn",
|
|
2235
|
+
"jsdoc/no-blank-block-descriptions": "warn",
|
|
2236
|
+
"jsdoc/no-blank-blocks": "warn",
|
|
2237
|
+
"jsdoc/no-multi-asterisks": "warn",
|
|
2238
|
+
"jsdoc/require-asterisk-prefix": "warn",
|
|
2239
|
+
"jsdoc/require-hyphen-before-param-description": "warn"
|
|
2240
|
+
} : {}
|
|
2241
|
+
};
|
|
2242
|
+
}
|
|
2243
|
+
//#endregion
|
|
2244
|
+
//#region src/oxlint/configs/jsdoc.ts
|
|
2245
|
+
function oxlintJsdoc(options = {}) {
|
|
2246
|
+
return createOxlintConfigs({
|
|
2247
|
+
name: "isentinel/jsdoc",
|
|
2248
|
+
files: [GLOB_SRC],
|
|
2249
|
+
rules: jsdocRules(options)
|
|
2250
|
+
});
|
|
2251
|
+
}
|
|
2252
|
+
//#endregion
|
|
2253
|
+
//#region src/rules/node.ts
|
|
2254
|
+
/**
|
|
2255
|
+
* Node.js rules shared between the ESLint and oxlint factories.
|
|
2256
|
+
*
|
|
2257
|
+
* @returns The rule map.
|
|
2258
|
+
*/
|
|
2259
|
+
function nodeRules() {
|
|
2260
|
+
return {
|
|
2261
|
+
"node/handle-callback-err": ["error", "^(err|error)$"],
|
|
2262
|
+
"node/no-deprecated-api": "error",
|
|
2263
|
+
"node/no-exports-assign": "error",
|
|
2264
|
+
"node/no-new-require": "error",
|
|
2265
|
+
"node/no-path-concat": "error",
|
|
2266
|
+
"node/prefer-global/buffer": ["error", "never"],
|
|
2267
|
+
"node/prefer-global/process": ["error", "never"],
|
|
2268
|
+
"node/prefer-node-protocol": "error",
|
|
2269
|
+
"node/process-exit-as-throw": "error"
|
|
2270
|
+
};
|
|
2271
|
+
}
|
|
2272
|
+
//#endregion
|
|
2273
|
+
//#region src/oxlint/configs/node.ts
|
|
2274
|
+
function oxlintNode(options = {}) {
|
|
2275
|
+
const files = options.files?.flat() ?? ["**/*.{,c,m}[jt]s{,x}"];
|
|
2276
|
+
return createOxlintConfigs({
|
|
2277
|
+
name: "isentinel/node",
|
|
2278
|
+
...options.excludeFiles ? { excludeFiles: options.excludeFiles } : {},
|
|
2279
|
+
files,
|
|
2280
|
+
rules: nodeRules()
|
|
2281
|
+
});
|
|
2282
|
+
}
|
|
2283
|
+
//#endregion
|
|
2284
|
+
//#region src/oxlint/configs/oxc.ts
|
|
2285
|
+
/**
|
|
2286
|
+
* Oxlint's native `oxc/*` rules: correctness, performance and suspicious-pattern
|
|
2287
|
+
* checks with no ESLint equivalent, so they only run under oxlint.
|
|
2288
|
+
*
|
|
2289
|
+
* The rules are emitted as a raw fragment (`plugins: ["oxc"]` with literal
|
|
2290
|
+
* `oxc/*` names) rather than through the shared `createOxlintConfigs` helper,
|
|
2291
|
+
* since the canonical-name translation layer only knows rules that exist on the
|
|
2292
|
+
* ESLint side.
|
|
2293
|
+
*
|
|
2294
|
+
* @param options - Shared rule options.
|
|
2295
|
+
* @returns The oxc config fragment.
|
|
2296
|
+
*/
|
|
2297
|
+
function oxlintOxc(options = {}) {
|
|
2298
|
+
const { excludeFiles, roblox = true } = options;
|
|
2299
|
+
const files = options.files?.flat() ?? ["**/*.{,c,m}[jt]s{,x}"];
|
|
2300
|
+
return [{
|
|
2301
|
+
name: "isentinel/oxc",
|
|
2302
|
+
...excludeFiles ? { excludeFiles } : {},
|
|
2303
|
+
files,
|
|
2304
|
+
plugins: ["oxc"],
|
|
2305
|
+
rules: {
|
|
2306
|
+
"oxc/approx-constant": "error",
|
|
2307
|
+
"oxc/bad-array-method-on-arguments": "error",
|
|
2308
|
+
"oxc/bad-char-at-comparison": "error",
|
|
2309
|
+
"oxc/bad-comparison-sequence": "error",
|
|
2310
|
+
"oxc/bad-min-max-func": "error",
|
|
2311
|
+
"oxc/bad-object-literal-comparison": "error",
|
|
2312
|
+
"oxc/bad-replace-all-arg": "error",
|
|
2313
|
+
"oxc/branches-sharing-code": "error",
|
|
2314
|
+
"oxc/const-comparisons": "error",
|
|
2315
|
+
"oxc/double-comparisons": "error",
|
|
2316
|
+
"oxc/erasing-op": "error",
|
|
2317
|
+
"oxc/misrefactored-assign-op": "error",
|
|
2318
|
+
"oxc/missing-throw": "error",
|
|
2319
|
+
"oxc/no-accumulating-spread": "error",
|
|
2320
|
+
"oxc/no-barrel-file": "error",
|
|
2321
|
+
"oxc/no-map-spread": "error",
|
|
2322
|
+
"oxc/no-this-in-exported-function": "error",
|
|
2323
|
+
"oxc/number-arg-out-of-range": "error",
|
|
2324
|
+
"oxc/only-used-in-recursion": "error",
|
|
2325
|
+
"oxc/uninvoked-array-callback": "error",
|
|
2326
|
+
...!roblox ? {
|
|
2327
|
+
"oxc/bad-bitwise-operator": "error",
|
|
2328
|
+
"oxc/no-const-enum": "error"
|
|
2329
|
+
} : {}
|
|
2330
|
+
}
|
|
2331
|
+
}];
|
|
2332
|
+
}
|
|
2333
|
+
//#endregion
|
|
2334
|
+
//#region src/eslint/plugin-renaming.ts
|
|
2335
|
+
const defaultPluginRenaming = {
|
|
2336
|
+
"@eslint-react": "react",
|
|
2337
|
+
"@eslint-react/hooks-extra": "react-hooks-extra",
|
|
2338
|
+
"@eslint-react/naming-convention": "react-naming-convention",
|
|
2339
|
+
"@isentinel/eslint-plugin-comment-length": "comment-length",
|
|
2340
|
+
"@stylistic": "style",
|
|
2341
|
+
"@typescript-eslint": "ts",
|
|
2342
|
+
"arrow-return-style-x": "arrow-style",
|
|
2343
|
+
"n": "node",
|
|
2344
|
+
"yml": "yaml"
|
|
2345
|
+
};
|
|
2346
|
+
//#endregion
|
|
2347
|
+
//#region src/oxlint/configs/oxfmt.ts
|
|
2348
|
+
const require$1 = createRequire(import.meta.url);
|
|
2349
|
+
function oxlintOxfmt(options) {
|
|
2350
|
+
const { componentExts: componentExtensions = [], files: oxfmtFiles, oxfmtOptions = {} } = options ?? {};
|
|
2351
|
+
const configPrettier = require$1("eslint-config-prettier/flat");
|
|
2352
|
+
const rulesToIgnore = /* @__PURE__ */ new Set(["curly", "style/quotes"]);
|
|
2353
|
+
const canonicalDisables = {};
|
|
2354
|
+
const prettierRuleNames = Object.keys(renameRules(configPrettier.rules, defaultPluginRenaming));
|
|
2355
|
+
for (const key of prettierRuleNames) if (!rulesToIgnore.has(key)) canonicalDisables[key] = "off";
|
|
2356
|
+
const { jsPluginRules, jsPlugins, nativeRules } = splitOxlintRules(canonicalDisables);
|
|
2357
|
+
const stylisticPlugin = require$1("@stylistic/eslint-plugin");
|
|
2358
|
+
for (const [rule, value] of Object.entries(canonicalDisables)) {
|
|
2359
|
+
if (!rule.startsWith("style/") || jsPluginRules[rule] !== void 0) continue;
|
|
2360
|
+
if (stylisticPlugin.rules[rule.slice(6)] !== void 0) jsPluginRules[rule] = value;
|
|
2361
|
+
}
|
|
2362
|
+
if (!jsPlugins.some((plugin) => typeof plugin !== "string" && plugin.name === "style")) jsPlugins.push({
|
|
2363
|
+
name: "style",
|
|
2364
|
+
specifier: "@stylistic/eslint-plugin"
|
|
2365
|
+
});
|
|
2366
|
+
const oxfmtPlugin = {
|
|
2367
|
+
name: "oxfmt",
|
|
2368
|
+
specifier: "eslint-plugin-oxfmt"
|
|
2369
|
+
};
|
|
2370
|
+
const jsFiles = oxfmtFiles?.flat() ?? ["**/*.{,c,m}js", "**/*.{,c,m}jsx"];
|
|
2371
|
+
const tsFiles = oxfmtFiles?.flat() ?? [
|
|
2372
|
+
"**/*.{,c,m}ts",
|
|
2373
|
+
"**/*.{,c,m}tsx",
|
|
2374
|
+
...componentExtensions.map((extension) => `**/*.${extension}`)
|
|
2375
|
+
];
|
|
2376
|
+
function createFragments(name, files) {
|
|
2377
|
+
return [{
|
|
2378
|
+
name,
|
|
2379
|
+
files,
|
|
2380
|
+
rules: {
|
|
2381
|
+
...nativeRules,
|
|
2382
|
+
"arrow-body-style": "off",
|
|
2383
|
+
"prefer-arrow-callback": "off"
|
|
2384
|
+
}
|
|
2385
|
+
}, {
|
|
2386
|
+
name: `${name}/js-plugin`,
|
|
2387
|
+
files,
|
|
2388
|
+
jsPlugins: [...jsPlugins, oxfmtPlugin],
|
|
2389
|
+
rules: {
|
|
2390
|
+
...jsPluginRules,
|
|
2391
|
+
"oxfmt/oxfmt": ["error", oxfmtOptions]
|
|
2392
|
+
}
|
|
2393
|
+
}];
|
|
2394
|
+
}
|
|
2395
|
+
return [...createFragments("isentinel/oxfmt/javascript", jsFiles), ...createFragments("isentinel/oxfmt/typescript", tsFiles)];
|
|
2396
|
+
}
|
|
2397
|
+
//#endregion
|
|
2398
|
+
//#region src/rules/perfectionist.ts
|
|
2399
|
+
const constructorGroup = {
|
|
2400
|
+
elementNamePattern: "constructor",
|
|
2401
|
+
groupName: "custom-constructor"
|
|
2402
|
+
};
|
|
2403
|
+
/** Shared perfectionist plugin settings for both factories. */
|
|
2404
|
+
const perfectionistSettings = { perfectionist: {
|
|
2405
|
+
order: "asc",
|
|
2406
|
+
partitionByComment: "^Part:\\s*(.*)$",
|
|
2407
|
+
type: "natural"
|
|
2408
|
+
} };
|
|
2409
|
+
/**
|
|
2410
|
+
* Perfectionist rules shared between the ESLint and oxlint factories.
|
|
2411
|
+
*
|
|
2412
|
+
* @param config - Shared rule options.
|
|
2413
|
+
* @returns The rule map.
|
|
2414
|
+
*/
|
|
2415
|
+
function perfectionistRules(config) {
|
|
2416
|
+
const { customClassGroups = [], sortObjects, type = "game" } = config ?? {};
|
|
2417
|
+
const customGroups = [
|
|
2418
|
+
...Array.from(customClassGroups, (customGroup) => {
|
|
2419
|
+
return {
|
|
2420
|
+
elementNamePattern: customGroup,
|
|
2421
|
+
groupName: customGroup
|
|
2422
|
+
};
|
|
2423
|
+
}),
|
|
2424
|
+
constructorGroup,
|
|
2425
|
+
createUnsortedMethod("private"),
|
|
2426
|
+
createUnsortedMethod("protected"),
|
|
2427
|
+
createUnsortedMethod("public")
|
|
2428
|
+
];
|
|
2429
|
+
function createUnsortedMethod(methodType) {
|
|
2430
|
+
return {
|
|
2431
|
+
groupName: methodType,
|
|
2432
|
+
modifiers: [methodType],
|
|
2433
|
+
newlinesInside: 1,
|
|
2434
|
+
selector: "method",
|
|
2435
|
+
type: type === "game" ? "unsorted" : "natural"
|
|
2436
|
+
};
|
|
2437
|
+
}
|
|
2438
|
+
const sortedObjectConfig = sortObjects ?? {
|
|
2439
|
+
customGroups: [
|
|
2440
|
+
{
|
|
2441
|
+
elementNamePattern: "^id$",
|
|
2442
|
+
groupName: "id"
|
|
2443
|
+
},
|
|
2444
|
+
{
|
|
2445
|
+
elementNamePattern: "^key$",
|
|
2446
|
+
groupName: "key"
|
|
2447
|
+
},
|
|
2448
|
+
{
|
|
2449
|
+
elementNamePattern: "^name$",
|
|
2450
|
+
groupName: "name"
|
|
2451
|
+
}
|
|
2452
|
+
],
|
|
2453
|
+
groups: [
|
|
2454
|
+
"id",
|
|
2455
|
+
"key",
|
|
2456
|
+
"name",
|
|
2457
|
+
"unknown"
|
|
2458
|
+
]
|
|
2459
|
+
};
|
|
2460
|
+
return {
|
|
2461
|
+
"perfectionist/sort-array-includes": ["error"],
|
|
2462
|
+
"perfectionist/sort-classes": ["warn", {
|
|
2463
|
+
customGroups,
|
|
2464
|
+
fallbackSort: {
|
|
2465
|
+
order: "asc",
|
|
2466
|
+
type: "natural"
|
|
2467
|
+
},
|
|
2468
|
+
groups: [
|
|
2469
|
+
"private-static-readonly-property",
|
|
2470
|
+
"private-readonly-property",
|
|
2471
|
+
"private-static-property",
|
|
2472
|
+
"private-property",
|
|
2473
|
+
"protected-static-readonly-property",
|
|
2474
|
+
"protected-readonly-property",
|
|
2475
|
+
"protected-static-property",
|
|
2476
|
+
"protected-property",
|
|
2477
|
+
"public-static-readonly-property",
|
|
2478
|
+
"public-readonly-property",
|
|
2479
|
+
"public-static-property",
|
|
2480
|
+
"public-property",
|
|
2481
|
+
"custom-constructor",
|
|
2482
|
+
...customClassGroups.reduce((accumulator, item) => {
|
|
2483
|
+
accumulator.push(item);
|
|
2484
|
+
return accumulator;
|
|
2485
|
+
}, []),
|
|
2486
|
+
"public",
|
|
2487
|
+
"protected",
|
|
2488
|
+
"private",
|
|
2489
|
+
"unknown"
|
|
2490
|
+
],
|
|
2491
|
+
newlinesBetween: 1,
|
|
2492
|
+
useExperimentalDependencyDetection: true
|
|
2493
|
+
}],
|
|
2494
|
+
"perfectionist/sort-decorators": ["error"],
|
|
2495
|
+
"perfectionist/sort-enums": ["error", { sortByValue: "always" }],
|
|
2496
|
+
"perfectionist/sort-exports": ["error"],
|
|
2497
|
+
"perfectionist/sort-heritage-clauses": ["error", {
|
|
2498
|
+
customGroups: customClassGroups.map((item) => {
|
|
2499
|
+
return {
|
|
2500
|
+
elementNamePattern: `^${capitalizeFirstLetter(item)}$`,
|
|
2501
|
+
groupName: item
|
|
2502
|
+
};
|
|
2503
|
+
}),
|
|
2504
|
+
groups: [...customClassGroups, "unknown"]
|
|
2505
|
+
}],
|
|
2506
|
+
"perfectionist/sort-interfaces": ["error", { ...sortedObjectConfig }],
|
|
2507
|
+
"perfectionist/sort-intersection-types": ["error"],
|
|
2508
|
+
"perfectionist/sort-maps": ["error"],
|
|
2509
|
+
"perfectionist/sort-named-imports": ["error"],
|
|
2510
|
+
"perfectionist/sort-object-types": ["error"],
|
|
2511
|
+
"perfectionist/sort-objects": ["error", { ...sortedObjectConfig }],
|
|
2512
|
+
"perfectionist/sort-sets": ["error"],
|
|
2513
|
+
"perfectionist/sort-switch-case": ["error"],
|
|
2514
|
+
"perfectionist/sort-union-types": ["error"],
|
|
2515
|
+
"perfectionist/sort-variable-declarations": ["error"],
|
|
2516
|
+
...type === "package" ? { "perfectionist/sort-modules": ["error", { type: "usage" }] } : {}
|
|
2517
|
+
};
|
|
2518
|
+
}
|
|
2519
|
+
/**
|
|
2520
|
+
* JSX-specific perfectionist rules shared between the ESLint and oxlint
|
|
2521
|
+
* factories.
|
|
2522
|
+
*
|
|
2523
|
+
* @param config - Shared rule options.
|
|
2524
|
+
* @returns The rule map.
|
|
2525
|
+
*/
|
|
2526
|
+
function perfectionistJsxRules(config) {
|
|
2527
|
+
const { sortObjects } = config ?? {};
|
|
2528
|
+
const sortedObjectJsxConfig = sortObjects ?? {
|
|
2529
|
+
customGroups: [
|
|
2530
|
+
{
|
|
2531
|
+
elementNamePattern: "^id$",
|
|
2532
|
+
groupName: "id"
|
|
2533
|
+
},
|
|
2534
|
+
{
|
|
2535
|
+
elementNamePattern: "^key$",
|
|
2536
|
+
groupName: "key"
|
|
2537
|
+
},
|
|
2538
|
+
{
|
|
2539
|
+
elementNamePattern: "^name$",
|
|
2540
|
+
groupName: "name"
|
|
2541
|
+
},
|
|
2542
|
+
{
|
|
2543
|
+
elementNamePattern: ["\b(on[A-Z][a-zA-Z]*)\b"],
|
|
2544
|
+
groupName: "callbacks"
|
|
2545
|
+
},
|
|
2546
|
+
{
|
|
2547
|
+
elementNamePattern: ["^children$", "^ref$"],
|
|
2548
|
+
groupName: "react"
|
|
2549
|
+
}
|
|
2550
|
+
],
|
|
2551
|
+
groups: [
|
|
2552
|
+
"id",
|
|
2553
|
+
"key",
|
|
2554
|
+
"name",
|
|
2555
|
+
"unknown",
|
|
2556
|
+
"react",
|
|
2557
|
+
"callbacks"
|
|
2558
|
+
]
|
|
2559
|
+
};
|
|
2560
|
+
return {
|
|
2561
|
+
"perfectionist/sort-interfaces": ["error", { ...sortedObjectJsxConfig }],
|
|
2562
|
+
"perfectionist/sort-jsx-props": ["error", {
|
|
2563
|
+
customGroups: [{
|
|
2564
|
+
elementNamePattern: "^(?:key|ref)$",
|
|
2565
|
+
groupName: "reserved"
|
|
2566
|
+
}, {
|
|
2567
|
+
elementNamePattern: "^on.+",
|
|
2568
|
+
groupName: "callback"
|
|
2569
|
+
}],
|
|
2570
|
+
groups: [
|
|
2571
|
+
"reserved",
|
|
2572
|
+
"shorthand-prop",
|
|
2573
|
+
"unknown",
|
|
2574
|
+
"callback"
|
|
2575
|
+
]
|
|
2576
|
+
}],
|
|
2577
|
+
"perfectionist/sort-objects": ["error", { ...sortedObjectJsxConfig }]
|
|
2578
|
+
};
|
|
2579
|
+
}
|
|
2580
|
+
function capitalizeFirstLetter(value) {
|
|
2581
|
+
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
2582
|
+
}
|
|
2583
|
+
//#endregion
|
|
2584
|
+
//#region src/oxlint/configs/perfectionist.ts
|
|
2585
|
+
function oxlintPerfectionist(config) {
|
|
2586
|
+
return [...createOxlintConfigs({
|
|
2587
|
+
name: "isentinel/perfectionist",
|
|
2588
|
+
files: [GLOB_SRC],
|
|
2589
|
+
rules: perfectionistRules(config),
|
|
2590
|
+
settings: perfectionistSettings
|
|
2591
|
+
}), ...createOxlintConfigs({
|
|
2592
|
+
name: "isentinel/perfectionist/jsx",
|
|
2593
|
+
files: [GLOB_JSX, GLOB_TSX],
|
|
2594
|
+
rules: perfectionistJsxRules(config)
|
|
2595
|
+
})];
|
|
2596
|
+
}
|
|
2597
|
+
//#endregion
|
|
2598
|
+
//#region src/rules/promise.ts
|
|
2599
|
+
/**
|
|
2600
|
+
* Promise rules shared between the ESLint and oxlint factories.
|
|
2601
|
+
*
|
|
2602
|
+
* @returns The rule map.
|
|
2603
|
+
*/
|
|
2604
|
+
function promiseRules() {
|
|
2605
|
+
return {
|
|
2606
|
+
"promise/always-return": ["error", { ignoreLastCallback: true }],
|
|
2607
|
+
"promise/avoid-new": "off",
|
|
2608
|
+
"promise/catch-or-return": ["error", {
|
|
2609
|
+
allowFinally: true,
|
|
2610
|
+
allowThen: true
|
|
2611
|
+
}],
|
|
2612
|
+
"promise/no-callback-in-promise": "off",
|
|
2613
|
+
"promise/no-multiple-resolved": "error",
|
|
2614
|
+
"promise/no-native": "off",
|
|
2615
|
+
"promise/no-nesting": "warn",
|
|
2616
|
+
"promise/no-new-statics": "off",
|
|
2617
|
+
"promise/no-promise-in-callback": "warn",
|
|
2618
|
+
"promise/no-return-in-finally": "warn",
|
|
2619
|
+
"promise/no-return-wrap": "error",
|
|
2620
|
+
"promise/param-names": "warn",
|
|
2621
|
+
"promise/prefer-await-to-callbacks": "off",
|
|
2622
|
+
"promise/prefer-await-to-then": "off"
|
|
2623
|
+
};
|
|
2624
|
+
}
|
|
2625
|
+
//#endregion
|
|
2626
|
+
//#region src/oxlint/configs/promise.ts
|
|
2627
|
+
function oxlintPromise() {
|
|
2628
|
+
return createOxlintConfigs({
|
|
2629
|
+
name: "isentinel/promise",
|
|
2630
|
+
files: [GLOB_SRC],
|
|
2631
|
+
rules: promiseRules()
|
|
2632
|
+
});
|
|
2633
|
+
}
|
|
2634
|
+
//#endregion
|
|
2635
|
+
//#region src/rules/react.ts
|
|
2636
|
+
/**
|
|
2637
|
+
* React rules shared between the ESLint and oxlint factories.
|
|
2638
|
+
*
|
|
2639
|
+
* The type-aware react rules stay in the ESLint config (oxlint jsPlugins have
|
|
2640
|
+
* no type information).
|
|
2641
|
+
*
|
|
2642
|
+
* @param options - Shared rule options.
|
|
2643
|
+
* @returns The rule map.
|
|
2644
|
+
*/
|
|
2645
|
+
function reactRules({ filenameCase = "kebabCase", reactCompiler = true, stylistic = true } = {}) {
|
|
2646
|
+
return {
|
|
2647
|
+
"flawless/no-unnecessary-use-callback": "error",
|
|
2648
|
+
"flawless/no-unnecessary-use-memo": "error",
|
|
2649
|
+
"flawless/purity": "error",
|
|
2650
|
+
"small-rules/react-hooks-strict-return": "error",
|
|
2651
|
+
...reactCompiler ? {
|
|
2652
|
+
"react/globals": "error",
|
|
2653
|
+
"react/refs": "error"
|
|
2654
|
+
} : {},
|
|
2655
|
+
"react-jsx/no-children-prop": "warn",
|
|
2656
|
+
"react-jsx/no-children-prop-with-children": "error",
|
|
2657
|
+
"react-jsx/no-comment-textnodes": "off",
|
|
2658
|
+
"react-jsx/no-key-after-spread": "error",
|
|
2659
|
+
"react-jsx/no-leaked-dollar": "warn",
|
|
2660
|
+
"react-jsx/no-leaked-semicolon": "warn",
|
|
2661
|
+
"react/error-boundaries": "error",
|
|
2662
|
+
"react/exhaustive-deps": "error",
|
|
2663
|
+
"react/immutability": "error",
|
|
2664
|
+
"react/no-access-state-in-setstate": "error",
|
|
2665
|
+
"react/no-array-index-key": "warn",
|
|
2666
|
+
"react/no-children-count": "warn",
|
|
2667
|
+
"react/no-children-for-each": "warn",
|
|
2668
|
+
"react/no-children-map": "warn",
|
|
2669
|
+
"react/no-children-only": "warn",
|
|
2670
|
+
"react/no-children-to-array": "warn",
|
|
2671
|
+
"react/no-class-component": "error",
|
|
2672
|
+
"react/no-clone-element": "warn",
|
|
2673
|
+
"react/no-component-will-mount": "error",
|
|
2674
|
+
"react/no-component-will-receive-props": "error",
|
|
2675
|
+
"react/no-component-will-update": "error",
|
|
2676
|
+
"react/no-create-ref": "error",
|
|
2677
|
+
"react/no-direct-mutation-state": "error",
|
|
2678
|
+
"react/no-duplicate-key": "error",
|
|
2679
|
+
"react/no-forward-ref": "off",
|
|
2680
|
+
"react/no-missing-component-display-name": "error",
|
|
2681
|
+
"react/no-missing-context-display-name": "error",
|
|
2682
|
+
"react/no-missing-key": "error",
|
|
2683
|
+
"react/no-misused-capture-owner-stack": "off",
|
|
2684
|
+
"react/no-nested-component-definitions": "warn",
|
|
2685
|
+
"react/no-nested-lazy-component-declarations": "warn",
|
|
2686
|
+
"react/no-set-state-in-component-did-mount": "warn",
|
|
2687
|
+
"react/no-set-state-in-component-did-update": "warn",
|
|
2688
|
+
"react/no-set-state-in-component-will-update": "warn",
|
|
2689
|
+
"react/no-unnecessary-use-prefix": "error",
|
|
2690
|
+
"react/no-unsafe-component-will-mount": "error",
|
|
2691
|
+
"react/no-unsafe-component-will-receive-props": "error",
|
|
2692
|
+
"react/no-unsafe-component-will-update": "error",
|
|
2693
|
+
"react/no-unstable-context-value": "error",
|
|
2694
|
+
"react/no-unstable-default-props": ["error", { safeDefaultProps: [
|
|
2695
|
+
"Axes",
|
|
2696
|
+
"BrickColor",
|
|
2697
|
+
"CatalogSearchParams",
|
|
2698
|
+
"CFrame",
|
|
2699
|
+
"Color3",
|
|
2700
|
+
"ColorSequence",
|
|
2701
|
+
"CFrame",
|
|
2702
|
+
"Content",
|
|
2703
|
+
"DateTime",
|
|
2704
|
+
"DockWidgetPluginGuiInfo",
|
|
2705
|
+
"Enum",
|
|
2706
|
+
"Faces",
|
|
2707
|
+
"FloatCurveKey",
|
|
2708
|
+
"Font",
|
|
2709
|
+
"NumberRange",
|
|
2710
|
+
"NumberSequence",
|
|
2711
|
+
"NumberSequenceKeypoint",
|
|
2712
|
+
"OverlapParams",
|
|
2713
|
+
"Path2DControlPoint",
|
|
2714
|
+
"PathWaypoint",
|
|
2715
|
+
"PhysicalProperties",
|
|
2716
|
+
"Ray",
|
|
2717
|
+
"RaycastParams",
|
|
2718
|
+
"Rect",
|
|
2719
|
+
"Region3",
|
|
2720
|
+
"Region3int16",
|
|
2721
|
+
"RotationCurveKey",
|
|
2722
|
+
"SecurityCapabilities",
|
|
2723
|
+
"TweenInfo",
|
|
2724
|
+
"UDim",
|
|
2725
|
+
"UDim2",
|
|
2726
|
+
"ValueCurveKey",
|
|
2727
|
+
"Vector2",
|
|
2728
|
+
"Vector3",
|
|
2729
|
+
"Vector3int16"
|
|
2730
|
+
] }],
|
|
2731
|
+
"react/no-unused-class-component-members": "off",
|
|
2732
|
+
"react/no-unused-state": "error",
|
|
2733
|
+
"react/no-use-context": "off",
|
|
2734
|
+
"react/rules-of-hooks": "error",
|
|
2735
|
+
"react/set-state-in-effect": "error",
|
|
2736
|
+
"react/set-state-in-render": "error",
|
|
2737
|
+
"react/static-components": "error",
|
|
2738
|
+
"react/use-memo": "error",
|
|
2739
|
+
"react/use-state": ["error", {
|
|
2740
|
+
enforceAssignment: false,
|
|
2741
|
+
enforceSetterName: false
|
|
2742
|
+
}],
|
|
2743
|
+
...stylistic !== false ? {
|
|
2744
|
+
"flawless/jsx-shorthand-boolean": "warn",
|
|
2745
|
+
"flawless/jsx-shorthand-fragment": "warn",
|
|
2746
|
+
"flawless/prefer-destructuring-assignment": "error",
|
|
2747
|
+
"one-var": "off",
|
|
2748
|
+
"react-jsx/no-useless-fragment": "warn",
|
|
2749
|
+
"react-naming-convention/context-name": "error",
|
|
2750
|
+
"react-naming-convention/ref-name": "error",
|
|
2751
|
+
"react/use-state": "error",
|
|
2752
|
+
"style/jsx-curly-brace-presence": ["error", {
|
|
2753
|
+
children: "never",
|
|
2754
|
+
propElementValues: "always",
|
|
2755
|
+
props: "never"
|
|
2756
|
+
}],
|
|
2757
|
+
"style/jsx-newline": "error",
|
|
2758
|
+
"style/jsx-self-closing-comp": "error",
|
|
2759
|
+
"unicorn/filename-case": ["error", {
|
|
2760
|
+
case: filenameCase,
|
|
2761
|
+
ignore: ["^[A-Z0-9]+.md$"],
|
|
2762
|
+
multipleFileExtensions: true
|
|
2763
|
+
}]
|
|
2764
|
+
} : {}
|
|
2765
|
+
};
|
|
2766
|
+
}
|
|
2767
|
+
//#endregion
|
|
2768
|
+
//#region src/oxlint/configs/react.ts
|
|
2769
|
+
function oxlintReact(options = {}) {
|
|
2770
|
+
const { filenameCase = "kebabCase", importSource, overrides = {}, reactCompiler = true, stylistic = true } = options;
|
|
2771
|
+
return createOxlintConfigs({
|
|
2772
|
+
name: "isentinel/react",
|
|
2773
|
+
files: options.files?.flat() ?? ["**/*.{,c,m}jsx", "**/*.{,c,m}tsx"],
|
|
2774
|
+
rules: {
|
|
2775
|
+
...reactRules({
|
|
2776
|
+
filenameCase,
|
|
2777
|
+
reactCompiler,
|
|
2778
|
+
stylistic
|
|
2779
|
+
}),
|
|
2780
|
+
...overrides
|
|
2781
|
+
},
|
|
2782
|
+
settings: { "react-x": {
|
|
2783
|
+
importSource: importSource ?? "@rbxts",
|
|
2784
|
+
version: "17.0.2"
|
|
2785
|
+
} }
|
|
2786
|
+
});
|
|
2787
|
+
}
|
|
2788
|
+
//#endregion
|
|
2789
|
+
//#region src/rules/roblox.ts
|
|
2790
|
+
/**
|
|
2791
|
+
* Roblox rules shared between the ESLint and oxlint factories.
|
|
2792
|
+
*
|
|
2793
|
+
* These do not require type information; the type-aware roblox rules stay in
|
|
2794
|
+
* the ESLint config (oxlint jsPlugins have no type information).
|
|
2795
|
+
*
|
|
2796
|
+
* @param options - Shared rule options.
|
|
2797
|
+
* @returns The rule map.
|
|
2798
|
+
*/
|
|
2799
|
+
function robloxRules({ stylistic = true } = {}) {
|
|
2800
|
+
return {
|
|
2801
|
+
"roblox/no-any": "error",
|
|
2802
|
+
"roblox/no-enum-merging": "error",
|
|
2803
|
+
"roblox/no-export-assignment-let": "error",
|
|
2804
|
+
"roblox/no-for-in": "error",
|
|
2805
|
+
"roblox/no-function-expression-name": "error",
|
|
2806
|
+
"roblox/no-get-set": "error",
|
|
2807
|
+
"roblox/no-implicit-self": "error",
|
|
2808
|
+
"roblox/no-invalid-identifier": "error",
|
|
2809
|
+
"roblox/no-namespace-merging": "error",
|
|
2810
|
+
"roblox/no-null": "error",
|
|
2811
|
+
"roblox/no-private-identifier": "error",
|
|
2812
|
+
"roblox/no-unsupported-syntax": "error",
|
|
2813
|
+
"roblox/no-user-defined-lua-tuple": "error",
|
|
2814
|
+
"roblox/no-value-typeof": "error",
|
|
2815
|
+
"roblox/prefer-get-players": "error",
|
|
2816
|
+
"roblox/prefer-task-library": "error",
|
|
2817
|
+
"small-rules/no-array-size-assignment": "error",
|
|
2818
|
+
...stylistic !== false ? {
|
|
2819
|
+
"sentinel/prefer-math-min-max": "error",
|
|
2820
|
+
"small-rules/no-array-constructor-elements": "error"
|
|
2821
|
+
} : {}
|
|
2822
|
+
};
|
|
2823
|
+
}
|
|
2824
|
+
//#endregion
|
|
2825
|
+
//#region src/oxlint/configs/roblox.ts
|
|
2826
|
+
function oxlintRoblox(options = {}) {
|
|
2827
|
+
const { componentExts: componentExtensions = [], overrides = {}, stylistic = true } = options;
|
|
2828
|
+
return createOxlintConfigs({
|
|
2829
|
+
name: "isentinel/roblox",
|
|
2830
|
+
files: options.files?.flat() ?? [
|
|
2831
|
+
"**/*/*.{,c,m}ts",
|
|
2832
|
+
"**/*/*.{,c,m}tsx",
|
|
2833
|
+
...componentExtensions.map((extension) => `**/*/*.${extension}`)
|
|
2834
|
+
],
|
|
2835
|
+
rules: {
|
|
2836
|
+
...robloxRules({ stylistic }),
|
|
2837
|
+
...overrides
|
|
2838
|
+
}
|
|
2839
|
+
});
|
|
2840
|
+
}
|
|
2841
|
+
//#endregion
|
|
2842
|
+
//#region src/rules/small-rules.ts
|
|
2843
|
+
/**
|
|
2844
|
+
* Small-rules rules shared between the ESLint and oxlint factories.
|
|
2845
|
+
*
|
|
2846
|
+
* These do not require type information. The type-aware
|
|
2847
|
+
* `flawless/prefer-read-only-props` replaces the former read-only-props rule
|
|
2848
|
+
* and is configured by the flawless config.
|
|
2849
|
+
*
|
|
2850
|
+
* @param options - Shared rule options.
|
|
2851
|
+
* @returns The rule map.
|
|
2852
|
+
*/
|
|
2853
|
+
function smallRulesRules({ isInEditor = false, stylistic = true } = {}) {
|
|
2854
|
+
return {
|
|
2855
|
+
"small-rules/no-commented-code": isInEditor ? "off" : "error",
|
|
2856
|
+
"small-rules/prefer-class-properties": "error",
|
|
2857
|
+
"small-rules/prefer-early-return": ["error", { maximumStatements: 1 }],
|
|
2858
|
+
"small-rules/strict-component-boundaries": "error",
|
|
2859
|
+
...stylistic !== false ? {
|
|
2860
|
+
"small-rules/prefer-module-scope-constants": "error",
|
|
2861
|
+
"small-rules/prefer-singular-enums": "error"
|
|
2862
|
+
} : {}
|
|
2863
|
+
};
|
|
2864
|
+
}
|
|
2865
|
+
//#endregion
|
|
2866
|
+
//#region src/oxlint/configs/small-rules.ts
|
|
2867
|
+
function oxlintSmallRules(options = {}) {
|
|
2868
|
+
const { componentExts: componentExtensions = [], isInEditor = false, stylistic = true } = options;
|
|
2869
|
+
return createOxlintConfigs({
|
|
2870
|
+
name: "isentinel/small-rules",
|
|
2871
|
+
files: options.files?.flat() ?? [
|
|
2872
|
+
"**/*.{,c,m}ts",
|
|
2873
|
+
"**/*.{,c,m}tsx",
|
|
2874
|
+
...componentExtensions.map((extension) => `**/*.${extension}`)
|
|
2875
|
+
],
|
|
2876
|
+
rules: smallRulesRules({
|
|
2877
|
+
isInEditor,
|
|
2878
|
+
stylistic
|
|
2879
|
+
})
|
|
2880
|
+
});
|
|
2881
|
+
}
|
|
2882
|
+
//#endregion
|
|
2883
|
+
//#region src/rules/sonarjs.ts
|
|
2884
|
+
/**
|
|
2885
|
+
* SonarJS rules shared between the ESLint and oxlint factories.
|
|
2886
|
+
*
|
|
2887
|
+
* @param options - Shared rule options.
|
|
2888
|
+
* @returns The rule map.
|
|
2889
|
+
*/
|
|
2890
|
+
function sonarjsRules({ isInEditor, roblox = true }) {
|
|
2891
|
+
return {
|
|
2892
|
+
"sonar/bool-param-default": "error",
|
|
2893
|
+
"sonar/cognitive-complexity": "warn",
|
|
2894
|
+
"sonar/constructor-for-side-effects": "error",
|
|
2895
|
+
"sonar/destructuring-assignment-syntax": "error",
|
|
2896
|
+
"sonar/elseif-without-else": "off",
|
|
2897
|
+
"sonar/file-name-differ-from-class": "error",
|
|
2898
|
+
"sonar/fixme-tag": isInEditor ? "warn" : "off",
|
|
2899
|
+
"sonar/max-switch-cases": "error",
|
|
2900
|
+
"sonar/misplaced-loop-counter": "error",
|
|
2901
|
+
"sonar/no-all-duplicated-branches": "error",
|
|
2902
|
+
"sonar/no-async-constructor": "error",
|
|
2903
|
+
"sonar/no-collapsible-if": "error",
|
|
2904
|
+
"sonar/no-commented-code": "off",
|
|
2905
|
+
"sonar/no-dead-store": "error",
|
|
2906
|
+
"sonar/no-duplicate-string": ["error", { ignoreStrings: "Not implemented" }],
|
|
2907
|
+
"sonar/no-duplicated-branches": "error",
|
|
2908
|
+
"sonar/no-element-overwrite": "error",
|
|
2909
|
+
"sonar/no-empty-collection": "error",
|
|
2910
|
+
"sonar/no-floating-point-equality": "error",
|
|
2911
|
+
"sonar/no-gratuitous-expressions": "off",
|
|
2912
|
+
"sonar/no-identical-conditions": "error",
|
|
2913
|
+
"sonar/no-identical-expressions": "error",
|
|
2914
|
+
"sonar/no-identical-functions": "error",
|
|
2915
|
+
"sonar/no-ignored-return": "error",
|
|
2916
|
+
"sonar/no-inverted-boolean-check": "error",
|
|
2917
|
+
"sonar/no-nested-conditional": "error",
|
|
2918
|
+
"sonar/no-nested-incdec": "error",
|
|
2919
|
+
"sonar/no-nested-switch": "error",
|
|
2920
|
+
"sonar/no-nested-template-literals": "error",
|
|
2921
|
+
"sonar/no-parameter-reassignment": "error",
|
|
2922
|
+
"sonar/no-redundant-boolean": "error",
|
|
2923
|
+
"sonar/no-redundant-jump": "error",
|
|
2924
|
+
"sonar/no-redundant-optional": "error",
|
|
2925
|
+
"sonar/no-try-promise": "error",
|
|
2926
|
+
"sonar/no-unthrown-error": "error",
|
|
2927
|
+
"sonar/no-unused-collection": "error",
|
|
2928
|
+
"sonar/no-use-of-empty-return-value": "error",
|
|
2929
|
+
"sonar/no-useless-catch": "error",
|
|
2930
|
+
"sonar/no-useless-increment": "error",
|
|
2931
|
+
"sonar/non-existent-operator": "error",
|
|
2932
|
+
"sonar/prefer-immediate-return": "error",
|
|
2933
|
+
"sonar/prefer-object-literal": "error",
|
|
2934
|
+
"sonar/prefer-promise-shorthand": "error",
|
|
2935
|
+
"sonar/prefer-single-boolean-return": "error",
|
|
2936
|
+
"sonar/prefer-while": "error",
|
|
2937
|
+
"sonar/public-static-readonly": "error",
|
|
2938
|
+
"sonar/todo-tag": isInEditor ? "warn" : "off",
|
|
2939
|
+
"sonar/use-type-alias": "error",
|
|
2940
|
+
...!roblox ? {
|
|
2941
|
+
"sonar/no-default-utility-imports": "error",
|
|
2942
|
+
"sonar/super-linear-regex": "error"
|
|
2943
|
+
} : {}
|
|
2944
|
+
};
|
|
2945
|
+
}
|
|
2946
|
+
/**
|
|
2947
|
+
* SonarJS rules that only apply to test files.
|
|
2948
|
+
*
|
|
2949
|
+
* `no-mixed-completion-style` and `synchronous-suite-callback` are jest-only:
|
|
2950
|
+
* both exclude vitest by design, since vitest awaits the suite callback and
|
|
2951
|
+
* does not support the `done` callback, so neither reports in a vitest file.
|
|
2952
|
+
*
|
|
2953
|
+
* @param options - Which test framework the rules are being enabled for.
|
|
2954
|
+
* @returns The rule map.
|
|
2955
|
+
*/
|
|
2956
|
+
function sonarjsTestRules({ jest = false } = {}) {
|
|
2957
|
+
return {
|
|
2958
|
+
"sonar/no-incompatible-assertion-types": "error",
|
|
2959
|
+
"sonar/no-trivial-assertions": "error",
|
|
2960
|
+
...jest ? {
|
|
2961
|
+
"sonar/no-mixed-completion-style": "error",
|
|
2962
|
+
"sonar/synchronous-suite-callback": "error"
|
|
2963
|
+
} : {}
|
|
2964
|
+
};
|
|
2965
|
+
}
|
|
2966
|
+
//#endregion
|
|
2967
|
+
//#region src/oxlint/configs/sonarjs.ts
|
|
2968
|
+
function oxlintSonarjs(options) {
|
|
2969
|
+
return createOxlintConfigs({
|
|
2970
|
+
name: "isentinel/sonarjs",
|
|
2971
|
+
files: [GLOB_SRC],
|
|
2972
|
+
rules: sonarjsRules(options)
|
|
2973
|
+
});
|
|
2974
|
+
}
|
|
2975
|
+
//#endregion
|
|
2976
|
+
//#region src/rules/spelling.ts
|
|
2977
|
+
const require = createRequire(import.meta.url);
|
|
2978
|
+
/**
|
|
2979
|
+
* CSpell rules shared between the ESLint and oxlint factories.
|
|
2980
|
+
*
|
|
2981
|
+
* @param options - Shared rule options.
|
|
2982
|
+
* @returns The rule map.
|
|
2983
|
+
*/
|
|
2984
|
+
function spellingRules({ inEditor, isInEditor = false, language = "en-US" } = {}) {
|
|
2985
|
+
const urlRobloxDictionary = pathToFileURL(require.resolve("@isentinel/dict-roblox"));
|
|
2986
|
+
const urlRoblox = new URL("dict/roblox.txt", urlRobloxDictionary);
|
|
2987
|
+
const urlRbxtsDictionary = pathToFileURL(require.resolve("@isentinel/dict-rbxts"));
|
|
2988
|
+
const urlRbxts = new URL("dict/rbxts.txt", urlRbxtsDictionary);
|
|
2989
|
+
return { "@cspell/spellchecker": [(inEditor === false ? isInEditor : true) ? "warn" : "off", {
|
|
2990
|
+
autoFix: false,
|
|
2991
|
+
checkComments: true,
|
|
2992
|
+
cspell: {
|
|
2993
|
+
dictionaries: ["roblox", "rbxts"],
|
|
2994
|
+
dictionaryDefinitions: [{
|
|
2995
|
+
name: "roblox",
|
|
2996
|
+
path: urlRoblox.href
|
|
2997
|
+
}, {
|
|
2998
|
+
name: "rbxts",
|
|
2999
|
+
path: urlRbxts.href
|
|
3000
|
+
}],
|
|
3001
|
+
language,
|
|
3002
|
+
words: ["isentinel", "uninvoked"]
|
|
3003
|
+
},
|
|
3004
|
+
generateSuggestions: isInEditor,
|
|
3005
|
+
numSuggestions: isInEditor ? 8 : 0
|
|
3006
|
+
}] };
|
|
3007
|
+
}
|
|
3008
|
+
//#endregion
|
|
3009
|
+
//#region src/oxlint/configs/spelling.ts
|
|
3010
|
+
function oxlintSpelling(options = {}) {
|
|
3011
|
+
const { componentExts: componentExtensions = [] } = options;
|
|
3012
|
+
return createOxlintConfigs({
|
|
3013
|
+
name: "isentinel/spelling",
|
|
3014
|
+
files: options.files?.flat() ?? ["**/*.{,c,m}[jt]s{,x}", ...componentExtensions.map((extension) => `**/*.${extension}`)],
|
|
3015
|
+
rules: spellingRules(options)
|
|
3016
|
+
});
|
|
3017
|
+
}
|
|
3018
|
+
//#endregion
|
|
3019
|
+
//#region src/rules/stylistic.ts
|
|
3020
|
+
/**
|
|
3021
|
+
* Stylistic rules shared between the ESLint and oxlint factories.
|
|
3022
|
+
*
|
|
3023
|
+
* These complement the `@stylistic` customize preset (ESLint side) and are
|
|
3024
|
+
* the stylistic rules that survive the formatter disables.
|
|
3025
|
+
*
|
|
3026
|
+
* @param options - Resolved stylistic options.
|
|
3027
|
+
* @returns The rule map.
|
|
3028
|
+
*/
|
|
3029
|
+
function stylisticRules({ jsx = true, quotes = "double" } = {}) {
|
|
3030
|
+
return {
|
|
3031
|
+
"antfu/consistent-list-newline": "off",
|
|
3032
|
+
"antfu/if-newline": "off",
|
|
3033
|
+
"antfu/top-level-function": "error",
|
|
3034
|
+
"arrow-style/no-export-default-arrow": "error",
|
|
3035
|
+
"curly": ["error", "all"],
|
|
3036
|
+
...jsx ? { "style/jsx-curly-brace-presence": ["error", { propElementValues: "always" }] } : {},
|
|
3037
|
+
"style/lines-between-class-members": ["error", { enforce: [{
|
|
3038
|
+
blankLine: "always",
|
|
3039
|
+
next: "method",
|
|
3040
|
+
prev: "method"
|
|
3041
|
+
}] }],
|
|
3042
|
+
"style/object-property-newline": ["error", { allowAllPropertiesOnSameLine: true }],
|
|
3043
|
+
"style/padding-line-between-statements": [
|
|
3044
|
+
"error",
|
|
3045
|
+
{
|
|
3046
|
+
blankLine: "always",
|
|
3047
|
+
next: "*",
|
|
3048
|
+
prev: [
|
|
3049
|
+
"block",
|
|
3050
|
+
"block-like",
|
|
3051
|
+
"class",
|
|
3052
|
+
"export",
|
|
3053
|
+
"import"
|
|
3054
|
+
]
|
|
3055
|
+
},
|
|
3056
|
+
{
|
|
3057
|
+
blankLine: "never",
|
|
3058
|
+
next: "*",
|
|
3059
|
+
prev: ["case"]
|
|
3060
|
+
},
|
|
3061
|
+
{
|
|
3062
|
+
blankLine: "any",
|
|
3063
|
+
next: ["export", "import"],
|
|
3064
|
+
prev: ["export", "import"]
|
|
3065
|
+
},
|
|
3066
|
+
{
|
|
3067
|
+
blankLine: "any",
|
|
3068
|
+
next: "*",
|
|
3069
|
+
prev: ["do"]
|
|
3070
|
+
}
|
|
3071
|
+
],
|
|
3072
|
+
"style/quotes": [
|
|
3073
|
+
"error",
|
|
3074
|
+
quotes,
|
|
3075
|
+
{
|
|
3076
|
+
allowTemplateLiterals: "never",
|
|
3077
|
+
avoidEscape: true
|
|
3078
|
+
}
|
|
3079
|
+
],
|
|
3080
|
+
"style/spaced-comment": [
|
|
3081
|
+
"error",
|
|
3082
|
+
"always",
|
|
3083
|
+
{ markers: ["!native", "!optimize"] }
|
|
3084
|
+
]
|
|
3085
|
+
};
|
|
3086
|
+
}
|
|
3087
|
+
/**
|
|
3088
|
+
* Arrow return style rule shared between the ESLint and oxlint factories.
|
|
3089
|
+
*
|
|
3090
|
+
* @param options - Shared rule options.
|
|
3091
|
+
* @returns The rule map.
|
|
3092
|
+
*/
|
|
3093
|
+
function arrowStyleRules({ arrowLength, maxLength, printWidth, usePrettier } = {}) {
|
|
3094
|
+
return { "arrow-style/arrow-return-style": ["error", {
|
|
3095
|
+
jsxAlwaysUseExplicitReturn: true,
|
|
3096
|
+
maxLen: maxLength ?? arrowLength ?? printWidth ?? 100,
|
|
3097
|
+
maxObjectProperties: 2,
|
|
3098
|
+
namedExportsAlwaysUseExplicitReturn: true,
|
|
3099
|
+
objectReturnStyle: "complex-explicit",
|
|
3100
|
+
...usePrettier ? { usePrettier } : {}
|
|
3101
|
+
}] };
|
|
3102
|
+
}
|
|
3103
|
+
//#endregion
|
|
3104
|
+
//#region src/oxlint/configs/stylistic.ts
|
|
3105
|
+
function oxlintStylistic(options = {}, prettierOptions = {}) {
|
|
3106
|
+
const { arrowLength } = options;
|
|
3107
|
+
const arrowRules = arrowStyleRules({
|
|
3108
|
+
arrowLength,
|
|
3109
|
+
printWidth: typeof prettierOptions["printWidth"] === "number" ? prettierOptions["printWidth"] : void 0,
|
|
3110
|
+
usePrettier: prettierOptions
|
|
3111
|
+
});
|
|
3112
|
+
return [
|
|
3113
|
+
...createOxlintConfigs({
|
|
3114
|
+
name: "isentinel/stylistic",
|
|
3115
|
+
files: [GLOB_SRC],
|
|
3116
|
+
rules: stylisticRules(options)
|
|
3117
|
+
}),
|
|
3118
|
+
...createOxlintConfigs({
|
|
3119
|
+
name: "isentinel/stylistic/ts",
|
|
3120
|
+
files: [GLOB_TS, GLOB_TSX],
|
|
3121
|
+
rules: arrowRules
|
|
3122
|
+
}),
|
|
3123
|
+
...createOxlintConfigs({
|
|
3124
|
+
name: "isentinel/stylistic/js",
|
|
3125
|
+
files: [GLOB_JS, GLOB_JSX],
|
|
3126
|
+
rules: arrowRules
|
|
3127
|
+
})
|
|
3128
|
+
];
|
|
3129
|
+
}
|
|
3130
|
+
//#endregion
|
|
3131
|
+
//#region src/rules/test.ts
|
|
3132
|
+
/**
|
|
3133
|
+
* Jest-extended rules shared between the ESLint and oxlint factories.
|
|
3134
|
+
*
|
|
3135
|
+
* @returns The rule map.
|
|
3136
|
+
*/
|
|
3137
|
+
function jestExtendedRules() {
|
|
3138
|
+
return {
|
|
3139
|
+
"jest-extended/prefer-to-be-array": "error",
|
|
3140
|
+
"jest-extended/prefer-to-be-false": "error",
|
|
3141
|
+
"jest-extended/prefer-to-be-object": "error",
|
|
3142
|
+
"jest-extended/prefer-to-be-true": "error",
|
|
3143
|
+
"jest-extended/prefer-to-have-been-called-once": "error"
|
|
3144
|
+
};
|
|
3145
|
+
}
|
|
3146
|
+
/**
|
|
3147
|
+
* Jest rules shared between the ESLint and oxlint factories.
|
|
3148
|
+
*
|
|
3149
|
+
* Both factories run `eslint-plugin-jest` as a jsPlugin: the native oxlint jest
|
|
3150
|
+
* plugin does not support `settings.jest.globalPackage` (oxc-project/oxc#23290)
|
|
3151
|
+
* and we use `@rbxts/jest-globals`. The four type-aware jest rules stay in
|
|
3152
|
+
* ESLint (jsPlugins have no type information).
|
|
3153
|
+
*
|
|
3154
|
+
* @param options - Shared rule options.
|
|
3155
|
+
* @returns The rule map.
|
|
3156
|
+
*/
|
|
3157
|
+
function jestRules({ extended = false, isInEditor = false, roblox: isRoblox = true, stylistic = true } = {}) {
|
|
3158
|
+
return {
|
|
3159
|
+
"jest/consistent-test-it": "error",
|
|
3160
|
+
"jest/expect-expect": "warn",
|
|
3161
|
+
"jest/max-expects": "error",
|
|
3162
|
+
"jest/max-nested-describe": ["error", { max: 4 }],
|
|
3163
|
+
"jest/no-alias-methods": "error",
|
|
3164
|
+
"jest/no-commented-out-tests": "warn",
|
|
3165
|
+
"jest/no-conditional-expect": "error",
|
|
3166
|
+
"jest/no-conditional-in-test": ["error", { allowOptionalChaining: false }],
|
|
3167
|
+
"jest/no-disabled-tests": isInEditor ? "off" : "error",
|
|
3168
|
+
"jest/no-done-callback": "error",
|
|
3169
|
+
"jest/no-duplicate-hooks": "error",
|
|
3170
|
+
"jest/no-error-equal": isRoblox ? "off" : "error",
|
|
3171
|
+
"jest/no-export": "error",
|
|
3172
|
+
"jest/no-focused-tests": isInEditor ? "off" : "error",
|
|
3173
|
+
"jest/no-hooks": "error",
|
|
3174
|
+
"jest/no-identical-title": "error",
|
|
3175
|
+
"jest/no-standalone-expect": "error",
|
|
3176
|
+
"jest/no-test-prefixes": "error",
|
|
3177
|
+
"jest/no-test-return-statement": "error",
|
|
3178
|
+
"jest/no-unnecessary-assertion": "error",
|
|
3179
|
+
"jest/no-unneeded-async-expect-function": "error",
|
|
3180
|
+
"jest/no-untyped-mock-factory": "error",
|
|
3181
|
+
"jest/prefer-called-with": "warn",
|
|
3182
|
+
"jest/prefer-comparison-matcher": "warn",
|
|
3183
|
+
"jest/prefer-each": "warn",
|
|
3184
|
+
"jest/prefer-ending-with-an-expect": "warn",
|
|
3185
|
+
"jest/prefer-equality-matcher": "warn",
|
|
3186
|
+
"jest/prefer-expect-assertions": "error",
|
|
3187
|
+
"jest/prefer-hooks-in-order": "warn",
|
|
3188
|
+
"jest/prefer-lowercase-title": "warn",
|
|
3189
|
+
"jest/prefer-mock-promise-shorthand": "error",
|
|
3190
|
+
"jest/prefer-mock-return-shorthand": "error",
|
|
3191
|
+
"jest/prefer-spy-on": "warn",
|
|
3192
|
+
"jest/prefer-strict-equal": "error",
|
|
3193
|
+
"jest/prefer-to-be": "error",
|
|
3194
|
+
"jest/prefer-to-contain": "error",
|
|
3195
|
+
"jest/prefer-to-have-been-called": "error",
|
|
3196
|
+
"jest/prefer-to-have-been-called-times": "error",
|
|
3197
|
+
"jest/prefer-to-have-length": "error",
|
|
3198
|
+
"jest/prefer-todo": "warn",
|
|
3199
|
+
"jest/require-hook": "error",
|
|
3200
|
+
"jest/require-to-throw-message": "warn",
|
|
3201
|
+
"jest/require-top-level-describe": "error",
|
|
3202
|
+
"jest/unbound-method": "error",
|
|
3203
|
+
"jest/valid-describe-callback": "error",
|
|
3204
|
+
"jest/valid-expect": isRoblox ? "off" : "error",
|
|
3205
|
+
"jest/valid-expect-in-promise": "error",
|
|
3206
|
+
"jest/valid-expect-with-promise": "error",
|
|
3207
|
+
"jest/valid-title": ["error", {
|
|
3208
|
+
ignoreTypeOfDescribeName: true,
|
|
3209
|
+
mustMatch: { it: ["^should", "Test title must start with \"should\""] }
|
|
3210
|
+
}],
|
|
3211
|
+
...extended ? jestExtendedRules() : {},
|
|
3212
|
+
...stylistic !== false ? { "jest/padding-around-all": "warn" } : {}
|
|
3213
|
+
};
|
|
3214
|
+
}
|
|
3215
|
+
/**
|
|
3216
|
+
* Vitest rules shared between the ESLint and oxlint factories.
|
|
3217
|
+
*
|
|
3218
|
+
* The ESLint factory runs `@vitest/eslint-plugin` (an optional peer). The
|
|
3219
|
+
* oxlint factory runs the family as native rules, except `padding-around-all`
|
|
3220
|
+
* and `prefer-vi-mocked` (no native port) which run via the plugin as a
|
|
3221
|
+
* jsPlugin.
|
|
3222
|
+
*
|
|
3223
|
+
* @param options - Shared rule options.
|
|
3224
|
+
* @returns The rule map.
|
|
3225
|
+
*/
|
|
3226
|
+
function vitestRules({ extended = false, isInEditor = false, stylistic = true } = {}) {
|
|
3227
|
+
return {
|
|
3228
|
+
"vitest/consistent-each-for": ["error", {
|
|
3229
|
+
describe: "for",
|
|
3230
|
+
it: "for",
|
|
3231
|
+
suite: "for",
|
|
3232
|
+
test: "for"
|
|
3233
|
+
}],
|
|
3234
|
+
"vitest/consistent-test-filename": ["error", { pattern: ".*\\.spec\\.[tj]sx?$" }],
|
|
3235
|
+
"vitest/consistent-test-it": ["error", {
|
|
3236
|
+
fn: "it",
|
|
3237
|
+
withinDescribe: "it"
|
|
3238
|
+
}],
|
|
3239
|
+
"vitest/consistent-vitest-vi": "error",
|
|
3240
|
+
"vitest/expect-expect": "warn",
|
|
3241
|
+
"vitest/hoisted-apis-on-top": "error",
|
|
3242
|
+
"vitest/max-expects": "error",
|
|
3243
|
+
"vitest/max-nested-describe": ["error", { max: 4 }],
|
|
3244
|
+
"vitest/no-alias-methods": "error",
|
|
3245
|
+
"vitest/no-commented-out-tests": "warn",
|
|
3246
|
+
"vitest/no-conditional-expect": "error",
|
|
3247
|
+
"vitest/no-conditional-in-test": "error",
|
|
3248
|
+
"vitest/no-conditional-tests": "error",
|
|
3249
|
+
"vitest/no-disabled-tests": isInEditor ? "off" : "error",
|
|
3250
|
+
"vitest/no-duplicate-hooks": "error",
|
|
3251
|
+
"vitest/no-focused-tests": isInEditor ? "off" : "error",
|
|
3252
|
+
"vitest/no-hooks": "error",
|
|
3253
|
+
"vitest/no-identical-title": "error",
|
|
3254
|
+
"vitest/no-import-node-test": "error",
|
|
3255
|
+
"vitest/no-interpolation-in-snapshots": "error",
|
|
3256
|
+
"vitest/no-large-snapshots": "warn",
|
|
3257
|
+
"vitest/no-mocks-import": "error",
|
|
3258
|
+
"vitest/no-standalone-expect": "error",
|
|
3259
|
+
"vitest/no-test-prefixes": "error",
|
|
3260
|
+
"vitest/no-test-return-statement": "error",
|
|
3261
|
+
"vitest/no-unneeded-async-expect-function": "error",
|
|
3262
|
+
"vitest/prefer-called-exactly-once-with": "warn",
|
|
3263
|
+
"vitest/prefer-called-once": "error",
|
|
3264
|
+
"vitest/prefer-called-with": "error",
|
|
3265
|
+
"vitest/prefer-comparison-matcher": "warn",
|
|
3266
|
+
"vitest/prefer-describe-function-title": "warn",
|
|
3267
|
+
"vitest/prefer-each": "warn",
|
|
3268
|
+
"vitest/prefer-equality-matcher": "warn",
|
|
3269
|
+
"vitest/prefer-expect-assertions": "warn",
|
|
3270
|
+
"vitest/prefer-expect-resolves": "error",
|
|
3271
|
+
"vitest/prefer-expect-type-of": "error",
|
|
3272
|
+
"vitest/prefer-hooks-in-order": "error",
|
|
3273
|
+
"vitest/prefer-hooks-on-top": "error",
|
|
3274
|
+
"vitest/prefer-import-in-mock": "error",
|
|
3275
|
+
"vitest/prefer-importing-vitest-globals": "error",
|
|
3276
|
+
"vitest/prefer-lowercase-title": "error",
|
|
3277
|
+
"vitest/prefer-mock-promise-shorthand": "error",
|
|
3278
|
+
"vitest/prefer-mock-return-shorthand": "error",
|
|
3279
|
+
"vitest/prefer-snapshot-hint": "warn",
|
|
3280
|
+
"vitest/prefer-spy-on": "warn",
|
|
3281
|
+
"vitest/prefer-strict-boolean-matchers": "error",
|
|
3282
|
+
"vitest/prefer-strict-equal": "error",
|
|
3283
|
+
"vitest/prefer-to-be": "error",
|
|
3284
|
+
"vitest/prefer-to-be-object": "error",
|
|
3285
|
+
"vitest/prefer-to-contain": "error",
|
|
3286
|
+
"vitest/prefer-to-have-been-called-times": "error",
|
|
3287
|
+
"vitest/prefer-to-have-length": "error",
|
|
3288
|
+
"vitest/prefer-todo": "warn",
|
|
3289
|
+
"vitest/prefer-vi-mocked": "error",
|
|
3290
|
+
"vitest/require-awaited-expect-poll": "error",
|
|
3291
|
+
"vitest/require-local-test-context-for-concurrent-snapshots": "error",
|
|
3292
|
+
"vitest/require-mock-type-parameters": "error",
|
|
3293
|
+
"vitest/require-to-throw-message": "warn",
|
|
3294
|
+
"vitest/require-top-level-describe": "error",
|
|
3295
|
+
"vitest/valid-describe-callback": "error",
|
|
3296
|
+
"vitest/valid-expect": "error",
|
|
3297
|
+
"vitest/valid-expect-in-promise": "error",
|
|
3298
|
+
"vitest/valid-title": ["error", {
|
|
3299
|
+
ignoreTypeOfDescribeName: true,
|
|
3300
|
+
mustMatch: { it: ["^should", "Test title must start with \"should\""] }
|
|
3301
|
+
}],
|
|
3302
|
+
...extended ? jestExtendedRules() : {},
|
|
3303
|
+
...stylistic !== false ? { "vitest/padding-around-all": "warn" } : {}
|
|
3304
|
+
};
|
|
3305
|
+
}
|
|
3306
|
+
//#endregion
|
|
3307
|
+
//#region src/oxlint/configs/test.ts
|
|
3308
|
+
/**
|
|
3309
|
+
* Test rules for standalone oxlint.
|
|
3310
|
+
*
|
|
3311
|
+
* Jest runs through `eslint-plugin-jest` as a jsPlugin: the native oxlint
|
|
3312
|
+
* jest plugin does not support `settings.jest.globalPackage`
|
|
3313
|
+
* (https://github.com/oxc-project/oxc/issues/23290). Vitest runs as native
|
|
3314
|
+
* oxlint rules, except `padding-around-all` and `prefer-vi-mocked` (no
|
|
3315
|
+
* native port) which run via `@vitest/eslint-plugin` as a jsPlugin. The
|
|
3316
|
+
* four type-aware jest rules are filtered out because oxlint has no type
|
|
3317
|
+
* information for them.
|
|
3318
|
+
*
|
|
3319
|
+
* @param options - The test rule options.
|
|
3320
|
+
* @returns The generated config fragments.
|
|
3321
|
+
*/
|
|
3322
|
+
function oxlintTest({ files = GLOB_TESTS, isInEditor = false, jest = false, overrides = {}, roblox: isRoblox = true, stylistic = true, type = "game", vitest = false } = {}) {
|
|
3323
|
+
const vitestOptions = typeof vitest === "object" ? vitest : {};
|
|
3324
|
+
const vitestEnabled = vitest === true || typeof vitest === "object";
|
|
3325
|
+
const jestOptions = typeof jest === "object" ? jest : {};
|
|
3326
|
+
const jestEnabled = jest === true || typeof jest === "object";
|
|
3327
|
+
const enableJest = jestEnabled || !vitestEnabled && (type === "game" || isRoblox);
|
|
3328
|
+
const enableVitest = vitestEnabled || !jestEnabled && type === "package" && !isRoblox;
|
|
3329
|
+
const configs = [];
|
|
3330
|
+
if (enableJest) configs.push(...createOxlintConfigs({
|
|
3331
|
+
name: "isentinel/test/jest",
|
|
3332
|
+
files: jestOptions.files?.flat() ?? files.flat(),
|
|
3333
|
+
rules: {
|
|
3334
|
+
...jestRules({
|
|
3335
|
+
extended: jestOptions.extended === true,
|
|
3336
|
+
isInEditor,
|
|
3337
|
+
roblox: isRoblox,
|
|
3338
|
+
stylistic
|
|
3339
|
+
}),
|
|
3340
|
+
...sonarjsTestRules({ jest: true }),
|
|
3341
|
+
...overrides,
|
|
3342
|
+
...jestOptions.overrides
|
|
3343
|
+
},
|
|
3344
|
+
settings: { jest: {
|
|
3345
|
+
globalPackage: "@rbxts/jest-globals",
|
|
3346
|
+
version: 27
|
|
3347
|
+
} }
|
|
3348
|
+
}));
|
|
3349
|
+
if (enableVitest) configs.push(...createOxlintConfigs({
|
|
3350
|
+
name: "isentinel/test/vitest",
|
|
3351
|
+
files: vitestOptions.files?.flat() ?? files.flat(),
|
|
3352
|
+
rules: {
|
|
3353
|
+
...vitestRules({
|
|
3354
|
+
extended: vitestOptions.extended === true,
|
|
3355
|
+
isInEditor,
|
|
3356
|
+
stylistic
|
|
3357
|
+
}),
|
|
3358
|
+
...sonarjsTestRules(),
|
|
3359
|
+
...overrides,
|
|
3360
|
+
...vitestOptions.overrides
|
|
3361
|
+
},
|
|
3362
|
+
settings: { vitest: { typecheck: vitestOptions.typecheck ?? false } }
|
|
3363
|
+
}));
|
|
3364
|
+
return configs;
|
|
3365
|
+
}
|
|
3366
|
+
//#endregion
|
|
3367
|
+
//#region src/rules/typescript.ts
|
|
3368
|
+
/**
|
|
3369
|
+
* TypeScript rules shared between the ESLint and oxlint factories.
|
|
3370
|
+
*
|
|
3371
|
+
* These do not require type information. Rule names use the canonical
|
|
3372
|
+
* (renamed) ESLint prefixes; the oxlint factory translates them via the
|
|
3373
|
+
* oxlint rule mapping.
|
|
3374
|
+
*
|
|
3375
|
+
* @param options - Shared rule options.
|
|
3376
|
+
* @returns The rule map.
|
|
3377
|
+
*/
|
|
3378
|
+
function typescriptRules({ stylistic = true } = {}) {
|
|
3379
|
+
return {
|
|
3380
|
+
"no-dupe-class-members": "off",
|
|
3381
|
+
"no-empty-function": "off",
|
|
3382
|
+
"no-loss-of-precision": "off",
|
|
3383
|
+
"no-redeclare": "off",
|
|
3384
|
+
"no-restricted-syntax": ["error", "[declare=true]"],
|
|
3385
|
+
"no-shadow": "off",
|
|
3386
|
+
"no-throw-literal": "off",
|
|
3387
|
+
"no-unused-expressions": "off",
|
|
3388
|
+
"no-unused-private-class-members": "off",
|
|
3389
|
+
"no-use-before-define": "off",
|
|
3390
|
+
"no-useless-constructor": "off",
|
|
3391
|
+
"prefer-destructuring": "off",
|
|
3392
|
+
"ts/adjacent-overload-signatures": "off",
|
|
3393
|
+
"ts/ban-ts-comment": ["error", { "ts-ignore": "allow-with-description" }],
|
|
3394
|
+
"ts/default-param-last": "error",
|
|
3395
|
+
"ts/explicit-function-return-type": ["error", { allowExpressions: true }],
|
|
3396
|
+
"ts/explicit-member-accessibility": ["error", { overrides: { constructors: "no-public" } }],
|
|
3397
|
+
"ts/method-signature-style": "off",
|
|
3398
|
+
"ts/no-array-constructor": "off",
|
|
3399
|
+
"ts/no-confusing-non-null-assertion": "error",
|
|
3400
|
+
"ts/no-dupe-class-members": "off",
|
|
3401
|
+
"ts/no-dynamic-delete": "off",
|
|
3402
|
+
"ts/no-empty-function": "error",
|
|
3403
|
+
"ts/no-empty-object-type": ["error", { allowInterfaces: "always" }],
|
|
3404
|
+
"ts/no-explicit-any": "off",
|
|
3405
|
+
"ts/no-extraneous-class": "error",
|
|
3406
|
+
"ts/no-for-in-array": "off",
|
|
3407
|
+
"ts/no-import-type-side-effects": "error",
|
|
3408
|
+
"ts/no-inferrable-types": "error",
|
|
3409
|
+
"ts/no-invalid-void-type": "off",
|
|
3410
|
+
"ts/no-namespace": "off",
|
|
3411
|
+
"ts/no-non-null-assertion": "error",
|
|
3412
|
+
"ts/no-redeclare": "off",
|
|
3413
|
+
"ts/no-require-imports": "error",
|
|
3414
|
+
"ts/no-shadow": "error",
|
|
3415
|
+
"ts/no-unused-expressions": "error",
|
|
3416
|
+
"ts/no-unused-private-class-members": "error",
|
|
3417
|
+
"ts/no-unused-vars": "off",
|
|
3418
|
+
"ts/no-use-before-define": "off",
|
|
3419
|
+
"ts/no-useless-constructor": "error",
|
|
3420
|
+
"ts/no-wrapper-object-types": "error",
|
|
3421
|
+
"ts/prefer-for-of": "error",
|
|
3422
|
+
"ts/prefer-function-type": "error",
|
|
3423
|
+
"ts/prefer-literal-enum-member": ["error", { allowBitwiseExpressions: true }],
|
|
3424
|
+
"ts/triple-slash-reference": "off",
|
|
3425
|
+
"ts/unified-signatures": "off",
|
|
3426
|
+
...stylistic !== false ? {
|
|
3427
|
+
"ts/array-type": ["error", {
|
|
3428
|
+
default: "generic",
|
|
3429
|
+
readonly: "generic"
|
|
3430
|
+
}],
|
|
3431
|
+
"ts/consistent-generic-constructors": ["error", "constructor"],
|
|
3432
|
+
"ts/consistent-indexed-object-style": ["error", "record"],
|
|
3433
|
+
"ts/consistent-type-definitions": ["error", "interface"],
|
|
3434
|
+
"ts/consistent-type-imports": ["error", {
|
|
3435
|
+
disallowTypeAnnotations: false,
|
|
3436
|
+
prefer: "type-imports"
|
|
3437
|
+
}]
|
|
3438
|
+
} : {}
|
|
3439
|
+
};
|
|
3440
|
+
}
|
|
3441
|
+
/**
|
|
3442
|
+
* Type-aware TypeScript rules shared between the ESLint and oxlint factories.
|
|
3443
|
+
*
|
|
3444
|
+
* In hybrid mode these are executed by oxlint-tsgolint (`oxlint
|
|
3445
|
+
* --type-aware`).
|
|
3446
|
+
*
|
|
3447
|
+
* @returns The rule map.
|
|
3448
|
+
*/
|
|
3449
|
+
function typescriptTypeAwareRules() {
|
|
3450
|
+
return {
|
|
3451
|
+
"dot-notation": "off",
|
|
3452
|
+
"no-implied-eval": "off",
|
|
3453
|
+
"no-unsafe-optional-chaining": "error",
|
|
3454
|
+
"prefer-promise-reject-errors": "off",
|
|
3455
|
+
"ts/await-thenable": "error",
|
|
3456
|
+
"ts/consistent-type-assertions": ["error", {
|
|
3457
|
+
assertionStyle: "as",
|
|
3458
|
+
objectLiteralTypeAssertions: "allow"
|
|
3459
|
+
}],
|
|
3460
|
+
"ts/dot-notation": ["error", { allowKeywords: true }],
|
|
3461
|
+
"ts/no-confusing-void-expression": "error",
|
|
3462
|
+
"ts/no-deprecated": "error",
|
|
3463
|
+
"ts/no-duplicate-type-constituents": "error",
|
|
3464
|
+
"ts/no-empty-object-type": "error",
|
|
3465
|
+
"ts/no-floating-promises": ["error", { ignoreVoid: true }],
|
|
3466
|
+
"ts/no-for-in-array": "error",
|
|
3467
|
+
"ts/no-implied-eval": "error",
|
|
3468
|
+
"ts/no-meaningless-void-operator": "error",
|
|
3469
|
+
"ts/no-misused-promises": "error",
|
|
3470
|
+
"ts/no-mixed-enums": "error",
|
|
3471
|
+
"ts/no-redundant-type-constituents": "error",
|
|
3472
|
+
"ts/no-unnecessary-boolean-literal-compare": "error",
|
|
3473
|
+
"ts/no-unnecessary-condition": ["error", { allowConstantLoopConditions: true }],
|
|
3474
|
+
"ts/no-unnecessary-parameter-property-assignment": "error",
|
|
3475
|
+
"ts/no-unnecessary-qualifier": "error",
|
|
3476
|
+
"ts/no-unnecessary-template-expression": "error",
|
|
3477
|
+
"ts/no-unnecessary-type-arguments": "error",
|
|
3478
|
+
"ts/no-unnecessary-type-assertion": "error",
|
|
3479
|
+
"ts/no-unnecessary-type-constraint": "error",
|
|
3480
|
+
"ts/no-unnecessary-type-parameters": "error",
|
|
3481
|
+
"ts/no-unsafe-argument": "error",
|
|
3482
|
+
"ts/no-unsafe-assignment": "error",
|
|
3483
|
+
"ts/no-unsafe-call": "error",
|
|
3484
|
+
"ts/no-unsafe-enum-comparison": "error",
|
|
3485
|
+
"ts/no-unsafe-member-access": "error",
|
|
3486
|
+
"ts/no-unsafe-return": "error",
|
|
3487
|
+
"ts/no-unsafe-unary-minus": "error",
|
|
3488
|
+
"ts/no-useless-default-assignment": "error",
|
|
3489
|
+
"ts/non-nullable-type-assertion-style": "error",
|
|
3490
|
+
"ts/only-throw-error": ["error", { allow: [{
|
|
3491
|
+
name: "Error",
|
|
3492
|
+
from: "package",
|
|
3493
|
+
package: "@rbxts/luau-polyfill"
|
|
3494
|
+
}] }],
|
|
3495
|
+
"ts/prefer-destructuring": ["error", {
|
|
3496
|
+
array: false,
|
|
3497
|
+
object: true
|
|
3498
|
+
}],
|
|
3499
|
+
"ts/prefer-find": "error",
|
|
3500
|
+
"ts/prefer-includes": "error",
|
|
3501
|
+
"ts/prefer-nullish-coalescing": "error",
|
|
3502
|
+
"ts/prefer-optional-chain": "error",
|
|
3503
|
+
"ts/prefer-promise-reject-errors": "error",
|
|
3504
|
+
"ts/prefer-readonly": "error",
|
|
3505
|
+
"ts/prefer-reduce-type-parameter": "error",
|
|
3506
|
+
"ts/prefer-return-this-type": "error",
|
|
3507
|
+
"ts/promise-function-async": "error",
|
|
3508
|
+
"ts/restrict-plus-operands": "error",
|
|
3509
|
+
"ts/restrict-template-expressions": "off",
|
|
3510
|
+
"ts/return-await": "error",
|
|
3511
|
+
"ts/strict-boolean-expressions": "error",
|
|
3512
|
+
"ts/strict-void-return": "error",
|
|
3513
|
+
"ts/switch-exhaustiveness-check": "error",
|
|
3514
|
+
"ts/unbound-method": "error",
|
|
3515
|
+
"ts/use-unknown-in-catch-callback-variable": "error"
|
|
3516
|
+
};
|
|
3517
|
+
}
|
|
3518
|
+
/**
|
|
3519
|
+
* Static port of the `@typescript-eslint` `eslint-recommended` overrides:
|
|
3520
|
+
* core rules that are redundant (or wrong) on TypeScript files because the
|
|
3521
|
+
* compiler already checks them — most importantly `no-undef`, which would
|
|
3522
|
+
* otherwise flag every ambient global (Roblox/Lua globals, roblox-ts macros).
|
|
3523
|
+
*
|
|
3524
|
+
* The ESLint factory spreads the preset dynamically from the plugin; the
|
|
3525
|
+
* oxlint factory applies this static copy to its TypeScript files.
|
|
3526
|
+
*
|
|
3527
|
+
* @returns The rule map.
|
|
3528
|
+
*/
|
|
3529
|
+
function typescriptRecommendedOverrides() {
|
|
3530
|
+
return {
|
|
3531
|
+
"constructor-super": "off",
|
|
3532
|
+
"getter-return": "off",
|
|
3533
|
+
"no-array-constructor": "off",
|
|
3534
|
+
"no-class-assign": "off",
|
|
3535
|
+
"no-const-assign": "off",
|
|
3536
|
+
"no-dupe-args": "off",
|
|
3537
|
+
"no-dupe-class-members": "off",
|
|
3538
|
+
"no-dupe-keys": "off",
|
|
3539
|
+
"no-func-assign": "off",
|
|
3540
|
+
"no-import-assign": "off",
|
|
3541
|
+
"no-new-native-nonconstructor": "off",
|
|
3542
|
+
"no-obj-calls": "off",
|
|
3543
|
+
"no-redeclare": "off",
|
|
3544
|
+
"no-setter-return": "off",
|
|
3545
|
+
"no-this-before-super": "off",
|
|
3546
|
+
"no-undef": "off",
|
|
3547
|
+
"no-unreachable": "off",
|
|
3548
|
+
"no-unsafe-negation": "off",
|
|
3549
|
+
"no-var": "error",
|
|
3550
|
+
"no-with": "off",
|
|
3551
|
+
"prefer-const": "error",
|
|
3552
|
+
"prefer-rest-params": "error",
|
|
3553
|
+
"prefer-spread": "error"
|
|
3554
|
+
};
|
|
3555
|
+
}
|
|
3556
|
+
/**
|
|
3557
|
+
* Rules enabled by the `@typescript-eslint` `strict` preset that are not
|
|
3558
|
+
* already configured explicitly in {@link typescriptRules}.
|
|
3559
|
+
*
|
|
3560
|
+
* The ESLint factory spreads the preset dynamically from the plugin; the
|
|
3561
|
+
* oxlint factory enables these explicitly so hybrid mode keeps full coverage.
|
|
3562
|
+
*
|
|
3563
|
+
* @returns The rule map.
|
|
3564
|
+
*/
|
|
3565
|
+
function typescriptStrictPresetRules() {
|
|
3566
|
+
return {
|
|
3567
|
+
"ts/no-duplicate-enum-values": "error",
|
|
3568
|
+
"ts/no-extra-non-null-assertion": "error",
|
|
3569
|
+
"ts/no-misused-new": "error",
|
|
3570
|
+
"ts/no-non-null-asserted-nullish-coalescing": "error",
|
|
3571
|
+
"ts/no-non-null-asserted-optional-chain": "error",
|
|
3572
|
+
"ts/no-this-alias": "error",
|
|
3573
|
+
"ts/no-unsafe-declaration-merging": "error",
|
|
3574
|
+
"ts/no-unsafe-function-type": "error",
|
|
3575
|
+
"ts/prefer-as-const": "error",
|
|
3576
|
+
"ts/prefer-namespace-keyword": "error"
|
|
3577
|
+
};
|
|
3578
|
+
}
|
|
3579
|
+
/**
|
|
3580
|
+
* Erasable-syntax-only rules shared between the ESLint and oxlint factories.
|
|
3581
|
+
*
|
|
3582
|
+
* @returns The rule map.
|
|
3583
|
+
*/
|
|
3584
|
+
function erasableSyntaxOnlyRules() {
|
|
3585
|
+
return {
|
|
3586
|
+
"erasable-syntax-only/enums": "error",
|
|
3587
|
+
"erasable-syntax-only/import-aliases": "error",
|
|
3588
|
+
"erasable-syntax-only/namespaces": "error",
|
|
3589
|
+
"erasable-syntax-only/parameter-properties": "error"
|
|
3590
|
+
};
|
|
3591
|
+
}
|
|
3592
|
+
//#endregion
|
|
3593
|
+
//#region src/oxlint/configs/typescript.ts
|
|
3594
|
+
function oxlintTypescript(options = {}) {
|
|
3595
|
+
const { componentExts: componentExtensions = [], erasableOnly = false, overrides = {}, stylistic = true, typeAware = true } = options;
|
|
3596
|
+
const files = options.files?.flat() ?? [
|
|
3597
|
+
"**/*.{,c,m}ts",
|
|
3598
|
+
"**/*.{,c,m}tsx",
|
|
3599
|
+
...componentExtensions.map((extension) => `**/*.${extension}`)
|
|
3600
|
+
];
|
|
3601
|
+
const typeAwareRules = typescriptTypeAwareRules() ?? {};
|
|
3602
|
+
const gatedTypeAwareRules = typeAware ? typeAwareRules : Object.fromEntries(Object.entries(typeAwareRules).filter(([rule]) => !isTsgolintRule(rule)));
|
|
3603
|
+
return createOxlintConfigs({
|
|
3604
|
+
name: "isentinel/typescript",
|
|
3605
|
+
files,
|
|
3606
|
+
rules: {
|
|
3607
|
+
...typescriptRecommendedOverrides(),
|
|
3608
|
+
...typescriptStrictPresetRules(),
|
|
3609
|
+
...typescriptRules({ stylistic }),
|
|
3610
|
+
...gatedTypeAwareRules,
|
|
3611
|
+
...erasableOnly ? erasableSyntaxOnlyRules() : {},
|
|
3612
|
+
...overrides
|
|
3613
|
+
}
|
|
3614
|
+
});
|
|
3615
|
+
}
|
|
3616
|
+
//#endregion
|
|
3617
|
+
//#region src/rules/unicorn.ts
|
|
3618
|
+
const abbreviations = {
|
|
3619
|
+
args: false,
|
|
3620
|
+
ctx: false,
|
|
3621
|
+
dist: { distance: true },
|
|
3622
|
+
e: false,
|
|
3623
|
+
err: false,
|
|
3624
|
+
fn: {
|
|
3625
|
+
func: true,
|
|
3626
|
+
function: false
|
|
3627
|
+
},
|
|
3628
|
+
func: false,
|
|
3629
|
+
inst: { instance: true },
|
|
3630
|
+
jsdoc: false,
|
|
3631
|
+
nums: { numbers: true },
|
|
3632
|
+
pos: { position: true },
|
|
3633
|
+
props: false,
|
|
3634
|
+
ref: false,
|
|
3635
|
+
refs: false,
|
|
3636
|
+
str: false,
|
|
3637
|
+
util: false,
|
|
3638
|
+
utils: false
|
|
3639
|
+
};
|
|
3640
|
+
/**
|
|
3641
|
+
* Resolve the `unicorn/name-replacements` replacement map.
|
|
3642
|
+
*
|
|
3643
|
+
* @param options - Shared rule options.
|
|
3644
|
+
* @returns The merged replacements map.
|
|
3645
|
+
*/
|
|
3646
|
+
function unicornNameReplacements({ nameReplacements, roblox = true } = {}) {
|
|
3647
|
+
return {
|
|
3648
|
+
...abbreviations,
|
|
3649
|
+
...roblox ? { buf: false } : {},
|
|
3650
|
+
...nameReplacements
|
|
3651
|
+
};
|
|
3652
|
+
}
|
|
3653
|
+
/**
|
|
3654
|
+
* Unicorn rules shared between the ESLint and oxlint factories.
|
|
3655
|
+
*
|
|
3656
|
+
* @param options - Shared rule options.
|
|
3657
|
+
* @returns The rule map.
|
|
3658
|
+
*/
|
|
3659
|
+
function unicornRules(options = {}) {
|
|
3660
|
+
const { roblox = true, stylistic = true } = options;
|
|
3661
|
+
return {
|
|
3662
|
+
"unicorn/catch-error-name": ["error", { name: "err" }],
|
|
3663
|
+
"unicorn/consistent-conditional-object-spread": ["error", "ternary"],
|
|
3664
|
+
"unicorn/consistent-destructuring": "error",
|
|
3665
|
+
"unicorn/consistent-function-scoping": ["error", { checkArrowFunctions: false }],
|
|
3666
|
+
"unicorn/consistent-json-file-read": "error",
|
|
3667
|
+
"unicorn/consistent-template-literal-escape": "error",
|
|
3668
|
+
"unicorn/consistent-tuple-labels": "error",
|
|
3669
|
+
"unicorn/error-message": "off",
|
|
3670
|
+
"unicorn/filename-case": ["error", {
|
|
3671
|
+
case: "kebabCase",
|
|
3672
|
+
ignore: ["^[A-Z0-9]+.md$"],
|
|
3673
|
+
multipleFileExtensions: true
|
|
3674
|
+
}],
|
|
3675
|
+
"unicorn/isolated-functions": "error",
|
|
3676
|
+
"unicorn/name-replacements": ["error", {
|
|
3677
|
+
checkFilenames: true,
|
|
3678
|
+
replacements: unicornNameReplacements(options)
|
|
3679
|
+
}],
|
|
3680
|
+
"unicorn/no-array-sort-for-min-max": "error",
|
|
3681
|
+
"unicorn/no-async-promise-finally": "error",
|
|
3682
|
+
"unicorn/no-await-expression-member": "error",
|
|
3683
|
+
"unicorn/no-break-in-nested-loop": "error",
|
|
3684
|
+
"unicorn/no-constant-zero-expression": "error",
|
|
3685
|
+
"unicorn/no-declarations-before-early-exit": "error",
|
|
3686
|
+
"unicorn/no-double-comparison": "error",
|
|
3687
|
+
"unicorn/no-duplicate-loops": "error",
|
|
3688
|
+
"unicorn/no-duplicate-set-values": "error",
|
|
3689
|
+
"unicorn/no-empty-file": "error",
|
|
3690
|
+
"unicorn/no-exports-in-scripts": "error",
|
|
3691
|
+
"unicorn/no-for-each": "error",
|
|
3692
|
+
"unicorn/no-for-loop": "error",
|
|
3693
|
+
"unicorn/no-immediate-mutation": "error",
|
|
3694
|
+
"unicorn/no-incorrect-template-string-interpolation": "error",
|
|
3695
|
+
"unicorn/no-invalid-well-known-symbol-methods": "error",
|
|
3696
|
+
"unicorn/no-keyword-prefix": "error",
|
|
3697
|
+
"unicorn/no-lonely-if": "error",
|
|
3698
|
+
"unicorn/no-loop-iterable-mutation": "error",
|
|
3699
|
+
"unicorn/no-mismatched-map-key": "error",
|
|
3700
|
+
"unicorn/no-misrefactored-assignment": "error",
|
|
3701
|
+
"unicorn/no-negated-array-predicate": "error",
|
|
3702
|
+
"unicorn/no-negated-condition": "off",
|
|
3703
|
+
"unicorn/no-negation-in-equality-check": "error",
|
|
3704
|
+
"unicorn/no-non-function-verb-prefix": "error",
|
|
3705
|
+
"unicorn/no-object-as-default-parameter": "error",
|
|
3706
|
+
"unicorn/no-optional-chaining-on-undeclared-variable": "error",
|
|
3707
|
+
"unicorn/no-redundant-comparison": "error",
|
|
3708
|
+
"unicorn/no-return-array-push": "error",
|
|
3709
|
+
"unicorn/no-single-promise-in-promise-methods": "error",
|
|
3710
|
+
"unicorn/no-static-only-class": "error",
|
|
3711
|
+
"unicorn/no-subtraction-comparison": "error",
|
|
3712
|
+
"unicorn/no-unreadable-array-destructuring": "error",
|
|
3713
|
+
"unicorn/no-unreadable-for-of-expression": "error",
|
|
3714
|
+
"unicorn/no-unreadable-new-expression": "error",
|
|
3715
|
+
"unicorn/no-unsafe-property-key": "error",
|
|
3716
|
+
"unicorn/no-unused-properties": "error",
|
|
3717
|
+
"unicorn/no-useless-collection-argument": "error",
|
|
3718
|
+
"unicorn/no-useless-compound-assignment": "error",
|
|
3719
|
+
"unicorn/no-useless-concat": "error",
|
|
3720
|
+
"unicorn/no-useless-delete-check": "error",
|
|
3721
|
+
"unicorn/no-useless-logical-operand": "error",
|
|
3722
|
+
"unicorn/no-useless-promise-resolve-reject": "error",
|
|
3723
|
+
"unicorn/no-useless-re-export": "error",
|
|
3724
|
+
"unicorn/no-useless-recursion": "error",
|
|
3725
|
+
"unicorn/no-useless-spread": "off",
|
|
3726
|
+
"unicorn/no-useless-undefined": ["error", { checkArguments: false }],
|
|
3727
|
+
"unicorn/number-literal-case": "error",
|
|
3728
|
+
"unicorn/operator-assignment": "error",
|
|
3729
|
+
"unicorn/prefer-continue": "error",
|
|
3730
|
+
"unicorn/prefer-default-parameters": "error",
|
|
3731
|
+
"unicorn/prefer-direct-iteration": "error",
|
|
3732
|
+
"unicorn/prefer-else-if": "error",
|
|
3733
|
+
"unicorn/prefer-export-from": "error",
|
|
3734
|
+
"unicorn/prefer-has-check": "error",
|
|
3735
|
+
"unicorn/prefer-hoisting-branch-code": "error",
|
|
3736
|
+
"unicorn/prefer-identifier-import-export-specifiers": "error",
|
|
3737
|
+
"unicorn/prefer-includes": "error",
|
|
3738
|
+
"unicorn/prefer-logical-operator-over-ternary": "error",
|
|
3739
|
+
"unicorn/prefer-math-min-max": "off",
|
|
3740
|
+
"unicorn/prefer-optional-catch-binding": "error",
|
|
3741
|
+
"unicorn/prefer-set-has": "error",
|
|
3742
|
+
"unicorn/prefer-simple-condition-first": "error",
|
|
3743
|
+
"unicorn/prefer-simplified-conditions": "error",
|
|
3744
|
+
"unicorn/prefer-single-call": "error",
|
|
3745
|
+
"unicorn/prefer-switch": "error",
|
|
3746
|
+
"unicorn/prefer-ternary": ["error", "only-single-line"],
|
|
3747
|
+
"unicorn/prefer-then-catch": "error",
|
|
3748
|
+
"unicorn/prefer-unary-minus": "error",
|
|
3749
|
+
...stylistic !== false ? {
|
|
3750
|
+
"unicorn/consistent-compound-words": "error",
|
|
3751
|
+
"unicorn/prefer-block-statement-over-iife": "error",
|
|
3752
|
+
"unicorn/switch-case-braces": "error"
|
|
3753
|
+
} : {},
|
|
3754
|
+
...!roblox ? {
|
|
3755
|
+
"unicorn/no-accidental-bitwise-operator": "error",
|
|
3756
|
+
"unicorn/no-array-concat-in-loop": "error",
|
|
3757
|
+
"unicorn/no-array-fill-with-reference-type": "error",
|
|
3758
|
+
"unicorn/no-boolean-sort-comparator": "error",
|
|
3759
|
+
"unicorn/no-confusing-array-splice": "error",
|
|
3760
|
+
"unicorn/no-error-property-assignment": "error",
|
|
3761
|
+
"unicorn/no-global-object-property-assignment": "error",
|
|
3762
|
+
"unicorn/no-impossible-length-comparison": "error",
|
|
3763
|
+
"unicorn/no-invalid-character-comparison": "error",
|
|
3764
|
+
"unicorn/no-object-methods-with-collections": "error",
|
|
3765
|
+
"unicorn/no-unnecessary-fetch-options": "error",
|
|
3766
|
+
"unicorn/no-unnecessary-global-this": "error",
|
|
3767
|
+
"unicorn/no-unnecessary-string-trim": "error",
|
|
3768
|
+
"unicorn/no-unsafe-string-replacement": "error",
|
|
3769
|
+
"unicorn/no-useless-coercion": "error",
|
|
3770
|
+
"unicorn/no-useless-iterator-to-array": "error",
|
|
3771
|
+
"unicorn/no-xor-as-exponentiation": "error",
|
|
3772
|
+
"unicorn/prefer-abort-signal-any": "error",
|
|
3773
|
+
"unicorn/prefer-array-flat-map": "error",
|
|
3774
|
+
"unicorn/prefer-array-from-map": "error",
|
|
3775
|
+
"unicorn/prefer-array-from-range": "error",
|
|
3776
|
+
"unicorn/prefer-array-iterable-methods": "error",
|
|
3777
|
+
"unicorn/prefer-array-slice": "error",
|
|
3778
|
+
"unicorn/prefer-flat-math-min-max": "error",
|
|
3779
|
+
"unicorn/prefer-global-number-constants": "error",
|
|
3780
|
+
"unicorn/prefer-group-by": "error",
|
|
3781
|
+
"unicorn/prefer-iterator-helpers": "error",
|
|
3782
|
+
"unicorn/prefer-math-constants": "error",
|
|
3783
|
+
"unicorn/prefer-split-limit": "error",
|
|
3784
|
+
"unicorn/prefer-string-match-all": "error",
|
|
3785
|
+
"unicorn/prefer-string-pad-start-end": "error",
|
|
3786
|
+
"unicorn/prefer-string-repeat": "error",
|
|
3787
|
+
"unicorn/require-passive-events": "error",
|
|
3788
|
+
"unicorn/throw-new-error": "error"
|
|
3789
|
+
} : {}
|
|
3790
|
+
};
|
|
3791
|
+
}
|
|
3792
|
+
/**
|
|
3793
|
+
* Root-level unicorn rules shared between the ESLint and oxlint factories.
|
|
3794
|
+
*
|
|
3795
|
+
* @param options - Shared rule options.
|
|
3796
|
+
* @returns The rule map.
|
|
3797
|
+
*/
|
|
3798
|
+
function unicornRootRules(options = {}) {
|
|
3799
|
+
return { "unicorn/name-replacements": ["error", {
|
|
3800
|
+
checkFilenames: false,
|
|
3801
|
+
replacements: unicornNameReplacements(options)
|
|
3802
|
+
}] };
|
|
3803
|
+
}
|
|
3804
|
+
//#endregion
|
|
3805
|
+
//#region src/oxlint/configs/unicorn.ts
|
|
3806
|
+
function oxlintUnicorn({ excludeFiles, nameReplacements, roblox = true, root: customRootGlobs, stylistic = true } = {}) {
|
|
3807
|
+
const rootGlobs = mergeGlobs(GLOB_ROOT.map(toSourceGlob), customRootGlobs?.map(toSourceGlob));
|
|
3808
|
+
return [...createOxlintConfigs({
|
|
3809
|
+
name: "isentinel/unicorn",
|
|
3810
|
+
...excludeFiles ? { excludeFiles } : {},
|
|
3811
|
+
files: [GLOB_SRC],
|
|
3812
|
+
rules: unicornRules({
|
|
3813
|
+
nameReplacements,
|
|
3814
|
+
roblox,
|
|
3815
|
+
stylistic
|
|
3816
|
+
})
|
|
3817
|
+
}), ...createOxlintConfigs({
|
|
3818
|
+
name: "isentinel/unicorn/root",
|
|
3819
|
+
...excludeFiles ? { excludeFiles } : {},
|
|
3820
|
+
files: rootGlobs,
|
|
3821
|
+
rules: unicornRootRules({
|
|
3822
|
+
nameReplacements,
|
|
3823
|
+
roblox
|
|
3824
|
+
})
|
|
3825
|
+
})];
|
|
3826
|
+
}
|
|
3827
|
+
//#endregion
|
|
3828
|
+
//#region src/oxlint/factory.ts
|
|
3829
|
+
/**
|
|
3830
|
+
* The preset enables its rules explicitly, so every category is disabled to
|
|
3831
|
+
* stop oxlint's own defaults (notably `correctness: "warn"`) from firing on top
|
|
3832
|
+
* of the curated set.
|
|
3833
|
+
*/
|
|
3834
|
+
const DEFAULT_CATEGORIES = {
|
|
3835
|
+
correctness: "off",
|
|
3836
|
+
nursery: "off",
|
|
3837
|
+
pedantic: "off",
|
|
3838
|
+
perf: "off",
|
|
3839
|
+
restriction: "off",
|
|
3840
|
+
style: "off",
|
|
3841
|
+
suspicious: "off"
|
|
3842
|
+
};
|
|
3843
|
+
/**
|
|
3844
|
+
* Implementation signature; the public overload above carries the
|
|
3845
|
+
* redundant-override validation generics.
|
|
3846
|
+
*
|
|
3847
|
+
* @param factoryOptions - The options for generating the oxlint configuration.
|
|
3848
|
+
* @param userConfigs - Additional oxlint config fragments (merged as
|
|
3849
|
+
* overrides).
|
|
3850
|
+
* @returns The oxlint configuration.
|
|
3851
|
+
*/
|
|
3852
|
+
function isentinel(factoryOptions, ...userConfigs) {
|
|
3853
|
+
const options = factoryOptions ?? { name: "isentinel" };
|
|
3854
|
+
const { categories, componentExts: componentExtensions = [], e18e: enableE18e = true, env, eslintPlugin: enableEslintPlugin = false, formatters, gitignore: enableGitignore = true, globals, ignores, jsdoc: enableJsdoc = true, jsx: enableJsx = true, options: linterOptions, oxc: enableOxc = true, react: enableReact = false, root: customRootGlobs, rules = {}, spellCheck: enableSpellCheck, test: enableTest = false } = options;
|
|
3855
|
+
const rootGlobs = mergeGlobs(GLOB_ROOT, customRootGlobs);
|
|
3856
|
+
const enableRoblox = options.roblox !== false;
|
|
3857
|
+
const robloxScopedFiles = typeof options.roblox === "object" && options.roblox.files !== void 0 ? options.roblox.files.flat() : void 0;
|
|
3858
|
+
const hasComplement = !enableRoblox || robloxScopedFiles !== void 0;
|
|
3859
|
+
const needsComplementOverlay = enableRoblox && robloxScopedFiles !== void 0;
|
|
3860
|
+
const typeAware = linterOptions?.typeAware ?? isPackageExists("oxlint-tsgolint");
|
|
3861
|
+
const inAgentSession = options.isAgent ?? isInAgentSession();
|
|
3862
|
+
let { defaultSeverity, isInEditor } = options;
|
|
3863
|
+
if (defaultSeverity === void 0 && inAgentSession) defaultSeverity = "error";
|
|
3864
|
+
isInEditor ??= isInEditorEnvironment();
|
|
3865
|
+
const projectType = (() => {
|
|
3866
|
+
if (options.type === "app") return "game";
|
|
3867
|
+
if (options.type !== void 0) return options.type;
|
|
3868
|
+
return enableRoblox ? "game" : "package";
|
|
3869
|
+
})();
|
|
3870
|
+
const stylisticOptions = (() => {
|
|
3871
|
+
if (options.stylistic === false) return false;
|
|
3872
|
+
if (typeof options.stylistic === "object") return options.stylistic;
|
|
3873
|
+
return {};
|
|
3874
|
+
})();
|
|
3875
|
+
if (stylisticOptions !== false && !("jsx" in stylisticOptions)) stylisticOptions.jsx = enableJsx;
|
|
3876
|
+
const formatterOptions = typeof formatters === "object" ? formatters : {};
|
|
3877
|
+
const prettierSettings = {
|
|
3878
|
+
arrowParens: "always",
|
|
3879
|
+
printWidth: 100,
|
|
3880
|
+
quoteProps: "consistent",
|
|
3881
|
+
semi: true,
|
|
3882
|
+
singleQuote: false,
|
|
3883
|
+
tabWidth: 4,
|
|
3884
|
+
trailingComma: "all",
|
|
3885
|
+
useTabs: true,
|
|
3886
|
+
...formatterOptions.prettierOptions
|
|
3887
|
+
};
|
|
3888
|
+
const configs = [
|
|
3889
|
+
oxlintJavascript({
|
|
3890
|
+
...getOverrides(options, "javascript"),
|
|
3891
|
+
isInEditor,
|
|
3892
|
+
roblox: enableRoblox,
|
|
3893
|
+
stylistic: stylisticOptions
|
|
3894
|
+
}),
|
|
3895
|
+
oxlintImports({
|
|
3896
|
+
stylistic: stylisticOptions,
|
|
3897
|
+
type: projectType
|
|
3898
|
+
}),
|
|
3899
|
+
oxlintPromise(),
|
|
3900
|
+
oxlintSonarjs({
|
|
3901
|
+
isInEditor,
|
|
3902
|
+
roblox: enableRoblox
|
|
3903
|
+
}),
|
|
3904
|
+
oxlintTypescript({
|
|
3905
|
+
...resolveSubOptions(options, "typescript"),
|
|
3906
|
+
...getOverrides(options, "typescript"),
|
|
3907
|
+
componentExts: componentExtensions,
|
|
3908
|
+
stylistic: stylisticOptions,
|
|
3909
|
+
typeAware
|
|
3910
|
+
}),
|
|
3911
|
+
oxlintUnicorn({
|
|
3912
|
+
...resolveSubOptions(options, "unicorn"),
|
|
3913
|
+
roblox: enableRoblox,
|
|
3914
|
+
root: customRootGlobs,
|
|
3915
|
+
stylistic: stylisticOptions
|
|
3916
|
+
})
|
|
3917
|
+
];
|
|
3918
|
+
if (enableJsdoc !== false) configs.push(oxlintJsdoc({
|
|
3919
|
+
...resolveSubOptions(options, "jsdoc"),
|
|
3920
|
+
stylistic: stylisticOptions,
|
|
3921
|
+
type: projectType
|
|
3922
|
+
}));
|
|
3923
|
+
if (enableRoblox) configs.push(oxlintRoblox({
|
|
3924
|
+
...getOverrides(options, "roblox"),
|
|
3925
|
+
componentExts: componentExtensions,
|
|
3926
|
+
stylistic: stylisticOptions
|
|
3927
|
+
}));
|
|
3928
|
+
if (needsComplementOverlay) configs.push(oxlintJavascript({
|
|
3929
|
+
...getOverrides(options, "javascript"),
|
|
3930
|
+
excludeFiles: robloxScopedFiles,
|
|
3931
|
+
isInEditor,
|
|
3932
|
+
roblox: false,
|
|
3933
|
+
stylistic: stylisticOptions
|
|
3934
|
+
}), oxlintUnicorn({
|
|
3935
|
+
...resolveSubOptions(options, "unicorn"),
|
|
3936
|
+
excludeFiles: robloxScopedFiles,
|
|
3937
|
+
roblox: false,
|
|
3938
|
+
stylistic: stylisticOptions
|
|
3939
|
+
}));
|
|
3940
|
+
if (enableE18e !== false && hasComplement) configs.push(oxlintE18e({
|
|
3941
|
+
excludeFiles: robloxScopedFiles,
|
|
3942
|
+
isInEditor,
|
|
3943
|
+
nodeMajor: resolveNodeMajor(options.settings),
|
|
3944
|
+
type: projectType,
|
|
3945
|
+
...enableE18e === true ? {} : enableE18e
|
|
3946
|
+
}));
|
|
3947
|
+
if (hasComplement) configs.push(oxlintNode({ excludeFiles: robloxScopedFiles }));
|
|
3948
|
+
if (enableTest !== false) {
|
|
3949
|
+
const testOptions = typeof enableTest === "object" ? enableTest : {};
|
|
3950
|
+
configs.push(oxlintTest({
|
|
3951
|
+
...getOverrides(options, "test"),
|
|
3952
|
+
isInEditor,
|
|
3953
|
+
roblox: enableRoblox,
|
|
3954
|
+
stylistic: stylisticOptions,
|
|
3955
|
+
type: projectType,
|
|
3956
|
+
...testOptions
|
|
3957
|
+
}));
|
|
3958
|
+
}
|
|
3959
|
+
if (stylisticOptions !== false) configs.push(oxlintPerfectionist({
|
|
3960
|
+
...resolveSubOptions(options, "perfectionist"),
|
|
3961
|
+
type: projectType
|
|
3962
|
+
}), oxlintStylistic(stylisticOptions, prettierSettings));
|
|
3963
|
+
if (enableReact !== false) {
|
|
3964
|
+
const reactOptions = typeof enableReact === "object" ? enableReact : {};
|
|
3965
|
+
configs.push(oxlintReact({
|
|
3966
|
+
stylistic: stylisticOptions,
|
|
3967
|
+
...reactOptions
|
|
3968
|
+
}));
|
|
3969
|
+
}
|
|
3970
|
+
if (enableSpellCheck !== false) configs.push(oxlintSpelling({
|
|
3971
|
+
...resolveSubOptions(options, "spellCheck"),
|
|
3972
|
+
componentExts: componentExtensions,
|
|
3973
|
+
isInEditor
|
|
3974
|
+
}));
|
|
3975
|
+
if (enableEslintPlugin !== false) configs.push(oxlintEslintPlugin({ ...getOverrides(options, "eslintPlugin") }));
|
|
3976
|
+
if (enableOxc) {
|
|
3977
|
+
configs.push(oxlintOxc({ roblox: enableRoblox }));
|
|
3978
|
+
if (needsComplementOverlay) configs.push(oxlintOxc({
|
|
3979
|
+
excludeFiles: robloxScopedFiles,
|
|
3980
|
+
roblox: false
|
|
3981
|
+
}));
|
|
3982
|
+
}
|
|
3983
|
+
configs.push(oxlintSmallRules({
|
|
3984
|
+
componentExts: componentExtensions,
|
|
3985
|
+
isInEditor,
|
|
3986
|
+
stylistic: stylisticOptions
|
|
3987
|
+
}), oxlintFlawless({ stylistic: stylisticOptions }), oxlintComments({
|
|
3988
|
+
prettierOptions: prettierSettings,
|
|
3989
|
+
stylistic: stylisticOptions
|
|
3990
|
+
}), oxlintDisables({ root: rootGlobs }));
|
|
3991
|
+
if (stylisticOptions !== false && formatters !== false) {
|
|
3992
|
+
const oxfmtConfigOptions = resolveOxfmtConfigOptionsSync();
|
|
3993
|
+
configs.push(oxlintOxfmt({
|
|
3994
|
+
componentExts: componentExtensions,
|
|
3995
|
+
oxfmtOptions: buildOxfmtOptions({
|
|
3996
|
+
oxfmtConfigOptions,
|
|
3997
|
+
oxfmtOptions: formatterOptions.oxfmtOptions,
|
|
3998
|
+
prettierOptions: prettierSettings
|
|
3999
|
+
})
|
|
4000
|
+
}));
|
|
4001
|
+
}
|
|
4002
|
+
const jsPlugins = /* @__PURE__ */ new Map();
|
|
4003
|
+
const nativePlugins = /* @__PURE__ */ new Set();
|
|
4004
|
+
const overrides = [];
|
|
4005
|
+
const mergedSettings = {};
|
|
4006
|
+
const gitignorePatterns = enableGitignore !== false ? oxlintGitignore() : [];
|
|
4007
|
+
const ignorePatterns = typeof ignores === "function" ? ignores([...GLOB_EXCLUDE, ...gitignorePatterns]) : [
|
|
4008
|
+
...GLOB_EXCLUDE,
|
|
4009
|
+
...gitignorePatterns,
|
|
4010
|
+
...ignores ?? []
|
|
4011
|
+
];
|
|
4012
|
+
function mergeFragment(config) {
|
|
4013
|
+
const fragmentPlugins = config.plugins ?? [];
|
|
4014
|
+
for (const plugin of fragmentPlugins) nativePlugins.add(plugin);
|
|
4015
|
+
const fragmentJsPlugins = config.jsPlugins ?? [];
|
|
4016
|
+
for (const jsPlugin of fragmentJsPlugins) {
|
|
4017
|
+
const key = typeof jsPlugin === "string" ? jsPlugin : jsPlugin.name;
|
|
4018
|
+
jsPlugins.set(key, jsPlugin);
|
|
4019
|
+
}
|
|
4020
|
+
if (config.settings) Object.assign(mergedSettings, config.settings);
|
|
4021
|
+
const { name: _name, plugins: _plugins, settings: _settings, ...override } = config;
|
|
4022
|
+
overrides.push(override);
|
|
4023
|
+
}
|
|
4024
|
+
const fragments = configs.flat();
|
|
4025
|
+
for (const fragment of fragments) mergeFragment(fragment);
|
|
4026
|
+
if (Object.keys(rules).length > 0) {
|
|
4027
|
+
const optionsFragments = createOxlintConfigs({
|
|
4028
|
+
name: "isentinel/options-rules",
|
|
4029
|
+
files: [GLOB_SRC],
|
|
4030
|
+
keepUnmappedOff: true,
|
|
4031
|
+
rules
|
|
4032
|
+
});
|
|
4033
|
+
for (const fragment of optionsFragments) mergeFragment(fragment);
|
|
4034
|
+
}
|
|
4035
|
+
if (globals && Object.keys(globals).length > 0) mergeFragment({
|
|
4036
|
+
name: "isentinel/options-globals",
|
|
4037
|
+
files: [GLOB_SRC],
|
|
4038
|
+
globals
|
|
4039
|
+
});
|
|
4040
|
+
for (const userConfig of userConfigs) mergeFragment(userConfig);
|
|
4041
|
+
if (defaultSeverity) {
|
|
4042
|
+
const severityExcludeRules = /* @__PURE__ */ new Set(["sonar/todo-tag"]);
|
|
4043
|
+
for (const override of overrides) if (override.rules) override.rules = overrideRuleSeverity(override.rules, defaultSeverity, severityExcludeRules);
|
|
4044
|
+
}
|
|
4045
|
+
return defineConfig({
|
|
4046
|
+
categories: {
|
|
4047
|
+
...DEFAULT_CATEGORIES,
|
|
4048
|
+
...categories
|
|
4049
|
+
},
|
|
4050
|
+
...env ? { env } : {},
|
|
4051
|
+
...globals ? { globals } : {},
|
|
4052
|
+
ignorePatterns,
|
|
4053
|
+
jsPlugins: [...jsPlugins.values()],
|
|
4054
|
+
options: {
|
|
4055
|
+
typeAware,
|
|
4056
|
+
...linterOptions
|
|
4057
|
+
},
|
|
4058
|
+
overrides,
|
|
4059
|
+
plugins: [...nativePlugins],
|
|
4060
|
+
settings: mergedSettings
|
|
4061
|
+
});
|
|
4062
|
+
}
|
|
4063
|
+
//#endregion
|
|
4064
|
+
export { isentinel as default, isentinel, isOxlintCovered, oxlintJsPlugins, oxlintRuleMapping, staysInEslint, translateRuleToOxlint };
|