@richardscull/eslint-config 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1120 @@
1
+ import commandConfig from 'eslint-plugin-command/config';
2
+ import pluginAntfu from 'eslint-plugin-antfu';
3
+ import * as pluginImport from 'eslint-plugin-import-x';
4
+ import simpleImportSort from 'eslint-plugin-simple-import-sort';
5
+ import js from '@eslint/js';
6
+ import globals from 'globals';
7
+ import pluginJsonc from 'eslint-plugin-jsonc';
8
+ import packageJson from 'eslint-plugin-package-json';
9
+ import * as parserJsonc from 'jsonc-eslint-parser';
10
+ import defu$1, { createDefu } from 'defu';
11
+ import * as regexpPlugin from 'eslint-plugin-regexp';
12
+ import pluginStylistic from '@stylistic/eslint-plugin';
13
+ import pluginHyoban from 'eslint-plugin-hyoban';
14
+ import typescriptEslint from 'typescript-eslint';
15
+ import pluginUnicorn from 'eslint-plugin-unicorn';
16
+ import pluginUnusedImports from 'eslint-plugin-unused-imports';
17
+ import { isPackageExists, getPackageInfo } from 'local-pkg';
18
+ import { readPackageUp } from 'read-package-up';
19
+
20
+ function importConfig() {
21
+ return {
22
+ name: "extend/import",
23
+ plugins: {
24
+ "simple-import-sort": simpleImportSort,
25
+ "import-x": pluginImport,
26
+ "antfu": pluginAntfu
27
+ },
28
+ rules: {
29
+ "sort-imports": "off",
30
+ "import-x/order": "off",
31
+ "simple-import-sort/imports": "error",
32
+ "simple-import-sort/exports": "error",
33
+ "import-x/first": "error",
34
+ "import-x/newline-after-import": "error",
35
+ "no-duplicate-imports": "off",
36
+ "import-x/no-duplicates": "error",
37
+ "antfu/import-dedupe": "error",
38
+ "import-x/consistent-type-specifier-style": [
39
+ "error",
40
+ "prefer-top-level"
41
+ ],
42
+ "antfu/no-import-dist": "error",
43
+ "antfu/no-import-node-modules-by-path": "error"
44
+ }
45
+ };
46
+ }
47
+
48
+ const GLOB_TS_SRC = [
49
+ "**/*.cts",
50
+ "**/*.mts",
51
+ "**/*.ts",
52
+ "**/*.tsx"
53
+ ];
54
+ const GLOB_JS_SRC = [
55
+ "**/*.cjs",
56
+ "**/*.mjs",
57
+ "**/*.js",
58
+ "**/*.jsx"
59
+ ];
60
+ const GLOB_JSX_SRC = [
61
+ "**/*.jsx",
62
+ "**/*.tsx"
63
+ ];
64
+ const GLOB_SRC = [
65
+ ...GLOB_TS_SRC,
66
+ ...GLOB_JS_SRC
67
+ ];
68
+ const GLOB_JSON = "**/*.json";
69
+ const GLOB_SHOULD_BE_JSONC = [
70
+ "**/tsconfig.json",
71
+ "**/tsconfig.*.json",
72
+ "**/.vscode/*.json"
73
+ ];
74
+ const GLOB_JSON5 = "**/*.json5";
75
+ const GLOB_JSONC = "**/*.jsonc";
76
+ const GLOB_JSON_SRC = [
77
+ GLOB_JSON,
78
+ GLOB_JSON5,
79
+ GLOB_JSONC
80
+ ];
81
+ const GLOB_EXCLUDE = [
82
+ "**/node_modules",
83
+ "**/dist",
84
+ "**/package-lock.json",
85
+ "**/yarn.lock",
86
+ "**/pnpm-lock.yaml",
87
+ "**/bun.lockb",
88
+ "**/output",
89
+ "**/coverage",
90
+ "**/temp",
91
+ "**/.temp",
92
+ "**/tmp",
93
+ "**/.tmp",
94
+ "**/.history",
95
+ "**/.vitepress/cache",
96
+ "**/.nuxt",
97
+ "**/.next",
98
+ "**/.vercel",
99
+ "**/.changeset",
100
+ "**/.idea",
101
+ "**/.cache",
102
+ "**/.output",
103
+ "**/.vite-inspect",
104
+ "**/.yarn",
105
+ "**/CHANGELOG*.md",
106
+ "**/*.min.*",
107
+ "**/LICENSE*",
108
+ "**/__snapshots__",
109
+ "**/auto-import?(s).d.ts",
110
+ "**/components.d.ts",
111
+ "**/eslint-typegen.d.ts"
112
+ ];
113
+ const DEFAULT_IGNORE_FILES = [
114
+ ".gitignore",
115
+ ".eslintignore"
116
+ ];
117
+
118
+ function javaScriptConfigs({ strict, restrictedSyntax, linterOptions, settings }) {
119
+ return [
120
+ /// keep-sorted
121
+ {
122
+ files: GLOB_SRC,
123
+ languageOptions: {
124
+ ecmaVersion: 2022,
125
+ globals: {
126
+ ...globals.browser,
127
+ ...globals.es2022,
128
+ ...globals.node
129
+ },
130
+ parserOptions: {
131
+ ecmaFeatures: {
132
+ jsx: true
133
+ },
134
+ ecmaVersion: 2022,
135
+ sourceType: "module"
136
+ },
137
+ sourceType: "module"
138
+ },
139
+ linterOptions,
140
+ name: strict ? "@eslint/js/all" : "@eslint/js/recommended",
141
+ rules: strict ? js.configs.all.rules : js.configs.recommended.rules,
142
+ settings
143
+ },
144
+ [
145
+ // we can enabled in stylistic config if needed
146
+ strict ? {
147
+ /// keep-sorted
148
+ rules: {
149
+ "arrow-body-style": "off",
150
+ "curly": "off",
151
+ "func-style": "off",
152
+ "no-empty-function": "off",
153
+ "object-shorthand": "off",
154
+ "prefer-destructuring": "off",
155
+ "prefer-template": "off"
156
+ }
157
+ } : {},
158
+ // too opinionated, let this be decided by the user
159
+ strict ? {
160
+ /// keep-sorted
161
+ rules: {
162
+ "camelcase": "off",
163
+ "capitalized-comments": "off",
164
+ "complexity": "off",
165
+ "consistent-return": "off",
166
+ "id-length": "off",
167
+ "max-lines-per-function": "off",
168
+ "max-lines": "off",
169
+ "max-statements": "off",
170
+ "no-implicit-coercion": "off",
171
+ "no-inline-comments": "off",
172
+ "no-magic-numbers": "off",
173
+ "no-warning-comments": "off"
174
+ }
175
+ } : {},
176
+ // may not very useful
177
+ strict ? {
178
+ /// keep-sorted
179
+ rules: {
180
+ "init-declarations": "off",
181
+ "no-continue": "off",
182
+ "no-shadow": "off",
183
+ "no-ternary": "off",
184
+ "no-undefined": "off",
185
+ "no-underscore-dangle": "off",
186
+ "no-useless-assignment": "off",
187
+ "one-var": "off",
188
+ "prefer-named-capture-group": "off",
189
+ "require-unicode-regexp": "off",
190
+ "sort-keys": "off",
191
+ "sort-vars": "off"
192
+ }
193
+ } : {},
194
+ // change for more precise
195
+ strict ? {
196
+ /// keep-sorted
197
+ rules: {
198
+ "no-void": [
199
+ "error",
200
+ {
201
+ allowAsStatement: true
202
+ }
203
+ ]
204
+ }
205
+ } : {},
206
+ // all rules here are nice to have, let's enable them
207
+ {
208
+ name: "@eslint/js/custom",
209
+ /// keep-sorted
210
+ rules: {
211
+ // https://twitter.com/karlhorky/status/1773632485055680875
212
+ "array-callback-return": "error",
213
+ "eqeqeq": [
214
+ "error",
215
+ "smart"
216
+ ],
217
+ "new-cap": [
218
+ "error",
219
+ {
220
+ capIsNew: false,
221
+ newIsCap: true,
222
+ properties: true
223
+ }
224
+ ],
225
+ "no-console": [
226
+ "error",
227
+ {
228
+ allow: [
229
+ "info",
230
+ "warn",
231
+ "error"
232
+ ]
233
+ }
234
+ ],
235
+ // https://twitter.com/ryanflorence/status/1786394911895683512
236
+ "no-param-reassign": "warn",
237
+ "no-restricted-syntax": [
238
+ "error",
239
+ ...restrictedSyntax
240
+ ],
241
+ "no-template-curly-in-string": "error",
242
+ "no-use-before-define": [
243
+ "error",
244
+ {
245
+ classes: false,
246
+ functions: false,
247
+ variables: true
248
+ }
249
+ ],
250
+ "prefer-arrow-callback": [
251
+ "error",
252
+ {
253
+ allowNamedFunctions: true,
254
+ allowUnboundThis: true
255
+ }
256
+ ]
257
+ }
258
+ }
259
+ ]
260
+ ];
261
+ }
262
+
263
+ function formattingConfigs$1({ formatting }) {
264
+ if (!formatting) return [];
265
+ const jsonFormateRules = {
266
+ "jsonc/array-bracket-spacing": [
267
+ "error",
268
+ "never"
269
+ ],
270
+ "jsonc/comma-dangle": [
271
+ "error",
272
+ "never"
273
+ ],
274
+ "jsonc/comma-style": [
275
+ "error",
276
+ "last"
277
+ ],
278
+ "jsonc/indent": [
279
+ "error",
280
+ formatting.indent ?? 2
281
+ ],
282
+ "jsonc/key-spacing": [
283
+ "error",
284
+ {
285
+ afterColon: true,
286
+ beforeColon: false
287
+ }
288
+ ],
289
+ "jsonc/object-curly-spacing": [
290
+ "error",
291
+ "always"
292
+ ],
293
+ "jsonc/quote-props": "error",
294
+ "jsonc/quotes": "error",
295
+ "hyoban/jsonc-inline-spacing": "error"
296
+ };
297
+ return [
298
+ {
299
+ name: "json/json",
300
+ files: [
301
+ GLOB_JSON
302
+ ],
303
+ ignores: GLOB_SHOULD_BE_JSONC,
304
+ rules: {
305
+ ...pluginJsonc.configs["recommended-with-json"].rules,
306
+ ...jsonFormateRules
307
+ }
308
+ },
309
+ {
310
+ name: "json/jsonc",
311
+ files: [
312
+ GLOB_JSONC,
313
+ ...GLOB_SHOULD_BE_JSONC
314
+ ],
315
+ rules: {
316
+ ...pluginJsonc.configs["recommended-with-jsonc"].rules,
317
+ ...jsonFormateRules
318
+ }
319
+ },
320
+ {
321
+ name: "json/json5",
322
+ files: [
323
+ GLOB_JSON5
324
+ ],
325
+ rules: {
326
+ ...pluginJsonc.configs["recommended-with-json5"].rules,
327
+ ...jsonFormateRules
328
+ }
329
+ }
330
+ ];
331
+ }
332
+ function jsonConfigs(options) {
333
+ return [
334
+ {
335
+ name: "json/setup",
336
+ files: [
337
+ GLOB_JSON,
338
+ GLOB_JSON5,
339
+ GLOB_JSONC
340
+ ],
341
+ /// keep-sorted
342
+ plugins: {
343
+ "jsonc": pluginJsonc,
344
+ "package-json": packageJson
345
+ },
346
+ languageOptions: {
347
+ parser: parserJsonc
348
+ }
349
+ },
350
+ ...formattingConfigs$1(options),
351
+ packageJson.configs.recommended,
352
+ {
353
+ name: "package-json/custom",
354
+ files: [
355
+ GLOB_JSON,
356
+ GLOB_JSON5,
357
+ GLOB_JSONC
358
+ ],
359
+ rules: {
360
+ "package-json/order-properties": [
361
+ "error",
362
+ {
363
+ order: [
364
+ "publisher",
365
+ "name",
366
+ "displayName",
367
+ "type",
368
+ "version",
369
+ "private",
370
+ "packageManager",
371
+ "description",
372
+ "author",
373
+ "contributors",
374
+ "license",
375
+ "funding",
376
+ "homepage",
377
+ "repository",
378
+ "bugs",
379
+ "keywords",
380
+ "categories",
381
+ "sideEffects",
382
+ "exports",
383
+ "main",
384
+ "module",
385
+ "unpkg",
386
+ "jsdelivr",
387
+ "types",
388
+ "typesVersions",
389
+ "bin",
390
+ "icon",
391
+ "files",
392
+ "engines",
393
+ "activationEvents",
394
+ "contributes",
395
+ "scripts",
396
+ "release-it",
397
+ "peerDependencies",
398
+ "peerDependenciesMeta",
399
+ "dependencies",
400
+ "optionalDependencies",
401
+ "devDependencies",
402
+ "pnpm",
403
+ "overrides",
404
+ "resolutions",
405
+ "husky",
406
+ "simple-git-hooks",
407
+ "lint-staged",
408
+ "eslintConfig"
409
+ ]
410
+ }
411
+ ]
412
+ },
413
+ settings: {
414
+ packageJson: {
415
+ enforceForPrivate: false
416
+ }
417
+ }
418
+ }
419
+ ];
420
+ }
421
+
422
+ async function interopDefault(m) {
423
+ const resolved = await m;
424
+ return resolved.default || resolved;
425
+ }
426
+ const severities = new Set([
427
+ 0,
428
+ 1,
429
+ 2,
430
+ "off",
431
+ "warn",
432
+ "error"
433
+ ]);
434
+ const defu = createDefu((obj, key, value)=>{
435
+ if (Array.isArray(value) && severities.has(value[0]) && Array.isArray(obj[key]) && severities.has(obj[key][0])) {
436
+ // @ts-expect-error It's fine here
437
+ obj[key] = [
438
+ ...value
439
+ ];
440
+ return true;
441
+ }
442
+ // use set to merge arrays
443
+ if (Array.isArray(value) && Array.isArray(obj[key])) {
444
+ // @ts-expect-error It's fine here
445
+ obj[key] = [
446
+ ...new Set([
447
+ ...obj[key],
448
+ ...value
449
+ ].sort())
450
+ ];
451
+ return true;
452
+ }
453
+ });
454
+ async function config(options, ...configs) {
455
+ const { ignoreFiles, ignores } = options;
456
+ const gitignore = await interopDefault(import('eslint-config-flat-gitignore'));
457
+ const globalIgnores = defu({
458
+ ignores
459
+ }, gitignore({
460
+ files: ignoreFiles,
461
+ strict: false
462
+ }));
463
+ return [
464
+ globalIgnores,
465
+ ...(await Promise.all(configs.map(async (c)=>{
466
+ if (typeof c === "function") {
467
+ const resolved = await c();
468
+ if (!resolved) return;
469
+ return mergeConfigs(resolved);
470
+ }
471
+ if (!c) return;
472
+ return mergeConfigs(c);
473
+ }))).filter((i)=>!!i)
474
+ ];
475
+ }
476
+ async function mergeConfigs(_c) {
477
+ const c = await _c;
478
+ if (!c) return;
479
+ if (Array.isArray(c)) return withFiles(defu({}, ...c.reverse()));
480
+ return withFiles(c);
481
+ }
482
+ function withFiles(config) {
483
+ return defu(config, "files" in config ? {} : {
484
+ files: GLOB_SRC
485
+ });
486
+ }
487
+
488
+ function excludePlugins(object) {
489
+ const { plugins, ...rest } = object;
490
+ return rest;
491
+ }
492
+ function reactConfigs({ react, reactCompiler, strict, typeChecked, filesDisableTypeChecking }) {
493
+ if (!react) return [];
494
+ return [
495
+ async ()=>{
496
+ const reactRefresh = await interopDefault(import('eslint-plugin-react-refresh'));
497
+ const reactGoogleTranslate = await interopDefault(import('eslint-plugin-react-google-translate'));
498
+ const eslintReact = await interopDefault(import('@eslint-react/eslint-plugin'));
499
+ const config = strict ? eslintReact.configs.strict : eslintReact.configs.recommended;
500
+ const reactHooks = await interopDefault(import('eslint-plugin-react-hooks'));
501
+ return {
502
+ name: `react/setup/${strict ? "strict" : "recommended"}`,
503
+ plugins: {
504
+ "react-refresh": reactRefresh,
505
+ "react-google-translate": reactGoogleTranslate,
506
+ "react-hooks": reactHooks,
507
+ // @ts-expect-error type error
508
+ ...config.plugins
509
+ }
510
+ };
511
+ },
512
+ async ()=>{
513
+ const eslintReact = await interopDefault(import('@eslint-react/eslint-plugin'));
514
+ const config = strict ? eslintReact.configs.strict : eslintReact.configs.recommended;
515
+ return {
516
+ ...excludePlugins(config),
517
+ name: `react/${strict ? "strict" : "recommended"}`,
518
+ files: GLOB_TS_SRC
519
+ };
520
+ },
521
+ ()=>{
522
+ if (strict) {
523
+ return {
524
+ name: "react/all/custom",
525
+ files: GLOB_TS_SRC,
526
+ rules: {
527
+ "@eslint-react/naming-convention/filename": "off",
528
+ "@eslint-react/naming-convention/use-state": "off",
529
+ "@eslint-react/avoid-shorthand-boolean": "off",
530
+ "@eslint-react/avoid-shorthand-fragment": "off",
531
+ "@eslint-react/prefer-namespace-import": "warn"
532
+ }
533
+ };
534
+ }
535
+ return {
536
+ name: "react/recommended/custom",
537
+ files: GLOB_TS_SRC,
538
+ rules: {
539
+ "@eslint-react/no-unstable-context-value": "warn",
540
+ "@eslint-react/dom/no-dangerously-set-innerhtml": "off",
541
+ "@eslint-react/naming-convention/use-state": "off",
542
+ // https://github.com/jsx-eslint/eslint-plugin-react/issues/2628#issuecomment-984160944
543
+ // {
544
+ // selector: 'ImportDeclaration[source.value=\'react\'][specifiers.0.type=\'ImportDefaultSpecifier\']',
545
+ // message: 'Default React import not allowed, use import * as React from \'react\'',
546
+ // },
547
+ "@eslint-react/prefer-namespace-import": "warn"
548
+ }
549
+ };
550
+ },
551
+ ()=>{
552
+ if (!typeChecked) return;
553
+ return {
554
+ name: "react/type-checked",
555
+ files: GLOB_TS_SRC,
556
+ ignores: filesDisableTypeChecking.length > 0 ? filesDisableTypeChecking : undefined,
557
+ rules: {
558
+ "@eslint-react/no-leaked-conditional-rendering": "error"
559
+ }
560
+ };
561
+ },
562
+ ()=>{
563
+ return {
564
+ name: "react/official",
565
+ files: GLOB_TS_SRC,
566
+ rules: {
567
+ // Core hooks rules
568
+ "react-hooks/rules-of-hooks": "error",
569
+ "react-hooks/exhaustive-deps": "warn",
570
+ ...reactCompiler ? {
571
+ // React Compiler rules
572
+ "react-hooks/config": "error",
573
+ "react-hooks/error-boundaries": "error",
574
+ "react-hooks/component-hook-factories": "error",
575
+ "react-hooks/gating": "error",
576
+ "react-hooks/globals": "error",
577
+ "react-hooks/immutability": "error",
578
+ "react-hooks/preserve-manual-memoization": "error",
579
+ "react-hooks/purity": "error",
580
+ "react-hooks/refs": "error",
581
+ "react-hooks/set-state-in-effect": "error",
582
+ "react-hooks/set-state-in-render": "error",
583
+ "react-hooks/static-components": "error",
584
+ "react-hooks/unsupported-syntax": "warn",
585
+ "react-hooks/use-memo": "error",
586
+ "react-hooks/incompatible-library": "warn"
587
+ } : {}
588
+ }
589
+ };
590
+ },
591
+ {
592
+ name: "react/refresh",
593
+ files: GLOB_JSX_SRC,
594
+ rules: {
595
+ "react-refresh/only-export-components": [
596
+ "warn",
597
+ {
598
+ allowConstantExport: react === "vite",
599
+ allowExportNames: react === "next" ? [
600
+ "config",
601
+ "generateStaticParams",
602
+ "metadata",
603
+ "generateMetadata",
604
+ "viewport",
605
+ "generateViewport",
606
+ "dynamic"
607
+ ] : react === "remix" ? [
608
+ "meta",
609
+ "links",
610
+ "headers",
611
+ "loader",
612
+ "action"
613
+ ] : react === "expo" ? [
614
+ "unstable_settings"
615
+ ] : undefined
616
+ }
617
+ ]
618
+ }
619
+ },
620
+ {
621
+ name: "react/google-translate",
622
+ files: GLOB_JSX_SRC,
623
+ rules: {
624
+ "react-google-translate/no-conditional-text-nodes-with-siblings": "warn",
625
+ "react-google-translate/no-return-text-nodes": "warn"
626
+ }
627
+ }
628
+ ];
629
+ }
630
+
631
+ function regexConfig() {
632
+ return [
633
+ regexpPlugin.configs["flat/recommended"],
634
+ {
635
+ name: "regexp/recommended"
636
+ }
637
+ ];
638
+ }
639
+
640
+ function formattingConfigs({ formatting, lessOpinionated }) {
641
+ if (!formatting) {
642
+ return [];
643
+ }
644
+ const jsxIgnoreNodes = [
645
+ "TemplateLiteral *",
646
+ "TSUnionType",
647
+ "TSIntersectionType",
648
+ "TSTypeParameterInstantiation",
649
+ "FunctionExpression > .params[decorators.length > 0]",
650
+ "FunctionExpression > .params > :matches(Decorator, :not(:first-child))"
651
+ ];
652
+ const nonJsxIgnoreNodes = [
653
+ "JSXOpeningElement",
654
+ "JSXClosingElement"
655
+ ];
656
+ const basicIndentRuleOptions = {
657
+ ArrayExpression: 1,
658
+ CallExpression: {
659
+ arguments: 1
660
+ },
661
+ flatTernaryExpressions: false,
662
+ FunctionDeclaration: {
663
+ body: 1,
664
+ parameters: 1
665
+ },
666
+ FunctionExpression: {
667
+ body: 1,
668
+ parameters: 1
669
+ },
670
+ ignoreComments: false,
671
+ ImportDeclaration: 1,
672
+ MemberExpression: 1,
673
+ ObjectExpression: 1,
674
+ offsetTernaryExpressions: true,
675
+ outerIIFEBody: 1,
676
+ SwitchCase: 1,
677
+ VariableDeclarator: 1
678
+ };
679
+ return [
680
+ {
681
+ name: "@stylistic/shared",
682
+ files: undefined,
683
+ ...pluginStylistic.configs.customize(formatting)
684
+ },
685
+ {
686
+ name: "@stylistic/customize",
687
+ files: undefined,
688
+ plugins: {
689
+ "antfu": pluginAntfu,
690
+ "hyoban": pluginHyoban,
691
+ "@stylistic": pluginStylistic
692
+ },
693
+ rules: {
694
+ "@stylistic/quotes": [
695
+ "error",
696
+ formatting.quotes,
697
+ {
698
+ allowTemplateLiterals: "always",
699
+ avoidEscape: true
700
+ }
701
+ ],
702
+ "@stylistic/max-statements-per-line": "off",
703
+ "@stylistic/multiline-ternary": [
704
+ "error",
705
+ "always-multiline",
706
+ {
707
+ ignoreJSX: true
708
+ }
709
+ ],
710
+ "@stylistic/operator-linebreak": [
711
+ "error",
712
+ formatting.lineBreak ?? "before"
713
+ ],
714
+ "@stylistic/jsx-one-expression-per-line": [
715
+ "error",
716
+ {
717
+ allow: "single-line"
718
+ }
719
+ ],
720
+ "antfu/consistent-chaining": "error",
721
+ "antfu/consistent-list-newline": "error",
722
+ "hyoban/jsx-attribute-spacing": "error",
723
+ "@stylistic/no-tabs": "off",
724
+ "@stylistic/jsx-indent-props": "off",
725
+ "@stylistic/indent": [
726
+ "error",
727
+ formatting.indent,
728
+ {
729
+ ...basicIndentRuleOptions,
730
+ ignoredNodes: [
731
+ ...jsxIgnoreNodes,
732
+ ...nonJsxIgnoreNodes
733
+ ]
734
+ }
735
+ ],
736
+ ...lessOpinionated ? {
737
+ curly: [
738
+ "error",
739
+ "multi-line",
740
+ "consistent"
741
+ ]
742
+ } : {
743
+ "antfu/curly": "error",
744
+ "antfu/if-newline": "error"
745
+ }
746
+ }
747
+ },
748
+ {
749
+ files: GLOB_JSX_SRC,
750
+ rules: {
751
+ "@stylistic/indent": [
752
+ "error",
753
+ formatting.indent,
754
+ {
755
+ ...basicIndentRuleOptions,
756
+ ignoredNodes: jsxIgnoreNodes
757
+ }
758
+ ]
759
+ }
760
+ }
761
+ ];
762
+ }
763
+ function stylisticConfigs(options) {
764
+ const { typeChecked, lessOpinionated } = options;
765
+ return [
766
+ ...formattingConfigs(options),
767
+ typeChecked === true ? typescriptEslint.configs.stylisticTypeChecked : typescriptEslint.configs.stylistic,
768
+ {
769
+ name: "typescript-eslint/stylistic/custom",
770
+ files: GLOB_TS_SRC,
771
+ rules: {
772
+ "@typescript-eslint/no-empty-function": "off",
773
+ "@typescript-eslint/consistent-type-definitions": "off",
774
+ ...lessOpinionated ? {
775
+ "@typescript-eslint/array-type": "off"
776
+ } : {
777
+ "@typescript-eslint/array-type": [
778
+ "error",
779
+ {
780
+ default: "array-simple"
781
+ }
782
+ ]
783
+ }
784
+ }
785
+ },
786
+ typeChecked && {
787
+ name: "typescript-eslint/stylistic-type-checked/custom",
788
+ files: GLOB_TS_SRC,
789
+ rules: {
790
+ "@typescript-eslint/prefer-nullish-coalescing": "off"
791
+ }
792
+ },
793
+ {
794
+ name: "@stylistic/customize",
795
+ files: undefined,
796
+ plugins: {
797
+ "antfu": pluginAntfu,
798
+ "hyoban": pluginHyoban,
799
+ "@stylistic": pluginStylistic
800
+ },
801
+ rules: {
802
+ "object-shorthand": "error",
803
+ "prefer-destructuring": [
804
+ "error",
805
+ {
806
+ VariableDeclarator: {
807
+ array: false,
808
+ object: true
809
+ },
810
+ AssignmentExpression: {
811
+ array: false,
812
+ object: false
813
+ }
814
+ },
815
+ {
816
+ enforceForRenamedProperties: false
817
+ }
818
+ ],
819
+ "prefer-template": "error",
820
+ "@stylistic/jsx-self-closing-comp": [
821
+ "error",
822
+ {
823
+ component: true,
824
+ html: true
825
+ }
826
+ ],
827
+ ...lessOpinionated ? {} : {
828
+ "antfu/top-level-function": "error",
829
+ "hyoban/prefer-early-return": "error"
830
+ }
831
+ }
832
+ }
833
+ ];
834
+ }
835
+
836
+ function tailwindCSSConfig({ tailwindCSS }) {
837
+ if (!tailwindCSS) return;
838
+ process.env.TAILWIND_MODE = "build";
839
+ return async ()=>{
840
+ const tailwind = await interopDefault(import('eslint-plugin-tailwindcss'));
841
+ return [
842
+ ...tailwind.configs["flat/recommended"],
843
+ {
844
+ name: "tailwindcss/recommended",
845
+ files: GLOB_TS_SRC
846
+ },
847
+ typeof tailwindCSS === "object" && !tailwindCSS.order && {
848
+ rules: {
849
+ "tailwindcss/classnames-order": "off"
850
+ }
851
+ }
852
+ ];
853
+ };
854
+ }
855
+
856
+ function typeScriptConfigs({ strict, typeChecked, project, projectService, tsconfigRootDir, filesDisableTypeChecking, preferESM }) {
857
+ const typescriptPreset = strict ? typeChecked === true ? typescriptEslint.configs.strictTypeChecked : typescriptEslint.configs.strict : typeChecked === true ? typescriptEslint.configs.recommendedTypeChecked : typescriptEslint.configs.recommended;
858
+ return [
859
+ typescriptEslint.configs.base,
860
+ [
861
+ ...typescriptPreset,
862
+ typeChecked && {
863
+ languageOptions: {
864
+ parserOptions: {
865
+ project,
866
+ projectService,
867
+ tsconfigRootDir
868
+ }
869
+ }
870
+ }
871
+ ],
872
+ [
873
+ {
874
+ name: "typescript-eslint/custom",
875
+ files: GLOB_TS_SRC,
876
+ rules: {
877
+ "no-use-before-define": "off",
878
+ "@typescript-eslint/consistent-type-imports": [
879
+ "error",
880
+ {
881
+ disallowTypeAnnotations: false
882
+ }
883
+ ],
884
+ "@typescript-eslint/no-import-type-side-effects": "error",
885
+ // https://www.totaltypescript.com/method-shorthand-syntax-considered-harmful
886
+ "@typescript-eslint/method-signature-style": [
887
+ "error",
888
+ "property"
889
+ ],
890
+ "@typescript-eslint/no-unused-expressions": [
891
+ "error",
892
+ {
893
+ allowShortCircuit: true,
894
+ allowTernary: true
895
+ }
896
+ ],
897
+ "@typescript-eslint/no-dynamic-delete": "off",
898
+ "@typescript-eslint/no-empty-object-type": [
899
+ "error",
900
+ {
901
+ allowInterfaces: "with-single-extends",
902
+ allowObjectTypes: "never",
903
+ allowWithName: "Props$"
904
+ }
905
+ ]
906
+ }
907
+ },
908
+ strict ? {
909
+ rules: {
910
+ "@typescript-eslint/no-non-null-assertion": "off"
911
+ }
912
+ } : {
913
+ rules: {
914
+ "@typescript-eslint/ban-ts-comment": "off",
915
+ "@typescript-eslint/no-explicit-any": "off",
916
+ "@typescript-eslint/no-non-null-asserted-optional-chain": "warn"
917
+ }
918
+ },
919
+ !preferESM && {
920
+ rules: {
921
+ "@typescript-eslint/no-require-imports": "off"
922
+ }
923
+ },
924
+ typeChecked && (typeChecked === "essential" ? {
925
+ rules: {
926
+ // https://youtu.be/OVNQWzdhCQA?si=PvPOOgtGW5H4uRB7
927
+ "@typescript-eslint/await-thenable": "error",
928
+ "@typescript-eslint/no-floating-promises": "error",
929
+ "@typescript-eslint/no-misused-promises": [
930
+ "error",
931
+ {
932
+ checksVoidReturn: {
933
+ arguments: false,
934
+ attributes: false
935
+ }
936
+ }
937
+ ]
938
+ }
939
+ } : {
940
+ rules: {
941
+ "@typescript-eslint/consistent-type-exports": "error",
942
+ "@typescript-eslint/no-misused-promises": [
943
+ "error",
944
+ {
945
+ checksVoidReturn: {
946
+ arguments: false,
947
+ attributes: false
948
+ }
949
+ }
950
+ ],
951
+ "@typescript-eslint/restrict-template-expressions": [
952
+ "error",
953
+ {}
954
+ ],
955
+ "@typescript-eslint/no-deprecated": "warn"
956
+ }
957
+ })
958
+ ],
959
+ ()=>{
960
+ if (filesDisableTypeChecking.length === 0) return;
961
+ return {
962
+ files: filesDisableTypeChecking,
963
+ ...typescriptEslint.configs.disableTypeChecked
964
+ };
965
+ }
966
+ ];
967
+ }
968
+
969
+ function unicornConfigs({ fileCase, preferESM }) {
970
+ return [
971
+ pluginUnicorn.configs.unopinionated,
972
+ {
973
+ name: "unicorn/custom",
974
+ rules: {
975
+ "unicorn/filename-case": fileCase ? [
976
+ "error",
977
+ {
978
+ case: fileCase
979
+ }
980
+ ] : "off",
981
+ "unicorn/prefer-module": preferESM ? "error" : "off",
982
+ // https://github.com/sindresorhus/eslint-plugin-unicorn/issues/1109#issuecomment-782689255
983
+ "unicorn/consistent-destructuring": "off",
984
+ // https://github.com/sindresorhus/eslint-plugin-unicorn/issues/2341
985
+ "unicorn/escape-case": "off",
986
+ "unicorn/no-hex-escape": "off",
987
+ // Buggy
988
+ "unicorn/custom-error-definition": "off",
989
+ "unicorn/consistent-function-scoping": "off",
990
+ // Annoying
991
+ "unicorn/no-keyword-prefix": "off",
992
+ "unicorn/prefer-global-this": "off",
993
+ "unicorn/catch-error-name": "off",
994
+ "unicorn/import-style": "off",
995
+ "unicorn/no-array-for-each": "off",
996
+ "unicorn/no-array-reduce": "off",
997
+ "unicorn/no-await-expression-member": "off",
998
+ "unicorn/no-negated-condition": "off",
999
+ "unicorn/no-nested-ternary": "off",
1000
+ // https://github.com/sindresorhus/meta/discussions/7
1001
+ "unicorn/no-null": "off",
1002
+ "unicorn/no-unreadable-array-destructuring": "off",
1003
+ "unicorn/numeric-separators-style": "off",
1004
+ "unicorn/prefer-spread": "off",
1005
+ "unicorn/prefer-string-raw": "off",
1006
+ // https://github.com/orgs/web-infra-dev/discussions/10
1007
+ "unicorn/prefer-top-level-await": "off",
1008
+ "unicorn/prevent-abbreviations": "off",
1009
+ // this is common in config files
1010
+ "unicorn/no-anonymous-default-export": "off",
1011
+ "unicorn/no-array-sort": "off",
1012
+ "unicorn/no-array-reverse": "off",
1013
+ "unicorn/text-encoding-identifier-case": "off",
1014
+ "unicorn/prefer-ternary": "off",
1015
+ "unicorn/prefer-add-event-listener": "off",
1016
+ "unicorn/number-literal-case": "off",
1017
+ "unicorn/prefer-math-trunc": "off"
1018
+ }
1019
+ }
1020
+ ];
1021
+ }
1022
+
1023
+ function unocssConfig({ unocss }) {
1024
+ if (!unocss) return;
1025
+ return async ()=>{
1026
+ const unocss = await interopDefault(import('@unocss/eslint-config/flat'));
1027
+ return unocss;
1028
+ };
1029
+ }
1030
+
1031
+ function unusedConfig() {
1032
+ return {
1033
+ name: "extend/unused",
1034
+ plugins: {
1035
+ "unused-imports": pluginUnusedImports
1036
+ },
1037
+ rules: {
1038
+ "no-unused-vars": "off",
1039
+ "@typescript-eslint/no-unused-vars": "off",
1040
+ "unused-imports/no-unused-imports": "error",
1041
+ "unused-imports/no-unused-vars": [
1042
+ "error",
1043
+ {
1044
+ args: "after-used",
1045
+ argsIgnorePattern: "^_",
1046
+ ignoreRestSiblings: true,
1047
+ vars: "all",
1048
+ varsIgnorePattern: "^_"
1049
+ }
1050
+ ]
1051
+ }
1052
+ };
1053
+ }
1054
+
1055
+ async function mergeDefaultOptions(options) {
1056
+ const packageJson = await readPackageUp();
1057
+ const hasReact = isPackageExists("react");
1058
+ const hasVite = isPackageExists("vite");
1059
+ const hasRemix = isPackageExists("remix");
1060
+ const hasNext = isPackageExists("next");
1061
+ const hasExpo = isPackageExists("expo");
1062
+ const hasUnocss = isPackageExists("unocss");
1063
+ const tailwindPackageInfo = await getPackageInfo("tailwindcss");
1064
+ const hasTailwindCSS = !!tailwindPackageInfo?.version;
1065
+ /// keep-sorted
1066
+ const defaultOptions = {
1067
+ fileCase: false,
1068
+ filesDisableTypeChecking: [],
1069
+ formatting: {
1070
+ indent: 2,
1071
+ quotes: "double",
1072
+ semi: true
1073
+ },
1074
+ ignoreFiles: DEFAULT_IGNORE_FILES,
1075
+ ignores: GLOB_EXCLUDE,
1076
+ lessOpinionated: false,
1077
+ linterOptions: {
1078
+ reportUnusedDisableDirectives: true
1079
+ },
1080
+ preferESM: packageJson?.packageJson.type === "module",
1081
+ project: !!options?.typeChecked,
1082
+ projectService: false,
1083
+ react: hasNext ? "next" : hasRemix ? "remix" : hasExpo ? "expo" : hasVite && hasReact ? "vite" : hasReact,
1084
+ reactCompiler: false,
1085
+ restrictedSyntax: [
1086
+ "DebuggerStatement",
1087
+ "LabeledStatement",
1088
+ "WithStatement",
1089
+ // https://www.typescriptlang.org/docs/handbook/enums.html#const-enums
1090
+ // https://youtu.be/XTXPKbPcvl4?si=J_2E9dM25sAEXM2x
1091
+ "TSEnumDeclaration[const=true]",
1092
+ "TSExportAssignment"
1093
+ ],
1094
+ settings: {
1095
+ ...hasTailwindCSS ? {
1096
+ tailwindcss: {
1097
+ callees: [
1098
+ "classnames",
1099
+ "clsx",
1100
+ "ctl",
1101
+ "cn"
1102
+ ]
1103
+ }
1104
+ } : {}
1105
+ },
1106
+ strict: false,
1107
+ tailwindCSS: hasTailwindCSS,
1108
+ tsconfigRootDir: process.cwd(),
1109
+ typeChecked: false,
1110
+ unocss: hasUnocss
1111
+ };
1112
+ return defu$1(options, defaultOptions);
1113
+ }
1114
+
1115
+ async function defineConfig(options, ...args) {
1116
+ const finalOptions = await mergeDefaultOptions(options);
1117
+ return config(finalOptions, ...javaScriptConfigs(finalOptions), ...unicornConfigs(finalOptions), ...typeScriptConfigs(finalOptions), importConfig(), unusedConfig(), regexConfig(), commandConfig(), ...stylisticConfigs(finalOptions), ...jsonConfigs(finalOptions), ...reactConfigs(finalOptions), tailwindCSSConfig(finalOptions), unocssConfig(finalOptions), ...args);
1118
+ }
1119
+
1120
+ export { DEFAULT_IGNORE_FILES, GLOB_EXCLUDE, GLOB_JSON, GLOB_JSON5, GLOB_JSONC, GLOB_JSON_SRC, GLOB_JSX_SRC, GLOB_JS_SRC, GLOB_SHOULD_BE_JSONC, GLOB_SRC, GLOB_TS_SRC, defineConfig };