@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.
@@ -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 };