js-style-kit 0.2.12 → 0.4.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.d.ts CHANGED
@@ -12,13 +12,13 @@ declare const configNames: {
12
12
  readonly base: "base";
13
13
  readonly disableTypeChecked: "typescript-eslint/disable-type-checked";
14
14
  readonly ignores: "ignores";
15
+ readonly import: "import";
15
16
  readonly jsdoc: "jsdoc";
16
17
  readonly markdown: "markdown";
17
18
  readonly nextjs: "nextjs";
18
19
  readonly perfectionist: "perfectionist";
19
20
  readonly preferArrowFunction: "prefer-arrow-function";
20
21
  readonly react: "react";
21
- readonly reactCompiler: "react-compiler";
22
22
  readonly reactRefresh: "react-refresh";
23
23
  readonly storybook: "storybook:stories";
24
24
  readonly storybookConfig: "storybook:config";
@@ -48,15 +48,16 @@ interface TestingConfig {
48
48
  interface EslintConfigOptions {
49
49
  functionStyle?: "off" | FunctionStyle;
50
50
  ignores?: string[];
51
+ importPlugin?: boolean;
51
52
  jsdoc?: false | {
52
53
  requireJsdoc?: boolean;
53
54
  };
54
55
  react?: boolean | {
55
56
  framework?: "next" | "none" | "vite";
56
- next?: boolean | undefined;
57
- reactCompiler?: boolean | undefined;
58
- reactRefresh?: boolean | undefined;
57
+ reactCompiler?: boolean;
58
+ reactRefresh?: boolean;
59
59
  };
60
+ rules?: Record<string, EslintRuleConfig>;
60
61
  sorting?: boolean;
61
62
  storybook?: boolean;
62
63
  testing?: false | TestingConfig;
@@ -70,10 +71,9 @@ interface EslintConfigOptions {
70
71
  * @param options - The optional configuration object.
71
72
  * @param options.functionStyle - The function style to enforce. Defaults to "arrow".
72
73
  * @param options.ignores - Additional paths to ignore. Already excludes `node_modules` and `dist`.
74
+ * @param options.importPlugin - Whether to include the import plugin. Defaults to true.
73
75
  * @param options.jsdoc - Whether to include JSDoc rules. Set to false to disable, or provide an object to configure.
74
- * @param options.react - Whether to include React rules. When true, reactCompiler is enabled by default.
75
- * Can be configured with an object to control next.js support and reactCompiler.
76
- * Also controls reactRefresh, which is enabled by default when react is true.
76
+ * @param options.react - Whether to include React, React hooks, and React compiler rules.
77
77
  * Can specify framework as "next", "none", or "vite" to control related configs:
78
78
  * - "next": Includes Next.js config, excludes React Refresh.
79
79
  * - "vite" or "none": Includes React Refresh, excludes Next.js.
@@ -89,10 +89,11 @@ interface EslintConfigOptions {
89
89
  * @param options.typescript - Whether to include TypeScript rules. Can be a boolean or a string with path to tsconfig.
90
90
  * @param options.turbo - Whether to include Turborepo rules. Defaults to false.
91
91
  * @param options.unicorn - Whether to include Unicorn rules. Defaults to true.
92
+ * @param options.rules - This is for rules that you need to alter or turn off.
92
93
  * @param additionalConfigs - Additional ESLint config objects to be merged into the final configuration.
93
94
  * @returns An array of ESLint configuration objects.
94
95
  */
95
- declare const eslintConfig: ({ functionStyle, ignores, jsdoc, react, sorting, storybook, testing, turbo, typescript, unicorn, }?: EslintConfigOptions, ...additionalConfigs: Linter.Config[]) => Linter.Config[];
96
+ declare const eslintConfig: ({ functionStyle, ignores, importPlugin, jsdoc, react, rules, sorting, storybook, testing, turbo, typescript, unicorn, }?: EslintConfigOptions, ...additionalConfigs: Linter.Config[]) => Linter.Config[];
96
97
 
97
98
  interface PrettierConfigOptions extends Config {
98
99
  cssOrderPlugin?: boolean;
package/dist/index.js CHANGED
@@ -11,13 +11,13 @@ var configNames = {
11
11
  base: "base",
12
12
  disableTypeChecked: "typescript-eslint/disable-type-checked",
13
13
  ignores: "ignores",
14
+ import: "import",
14
15
  jsdoc: "jsdoc",
15
16
  markdown: "markdown",
16
17
  nextjs: "nextjs",
17
18
  perfectionist: "perfectionist",
18
19
  preferArrowFunction: "prefer-arrow-function",
19
20
  react: "react",
20
- reactCompiler: "react-compiler",
21
21
  reactRefresh: "react-refresh",
22
22
  storybook: "storybook:stories",
23
23
  storybookConfig: "storybook:config",
@@ -27,9 +27,29 @@ var configNames = {
27
27
  typescriptTesting: "tseslint-testing",
28
28
  unicorn: "unicorn"
29
29
  };
30
+ var pluginPrefixMap = /* @__PURE__ */ new Map([
31
+ ["@typescript-eslint", configNames.typescript],
32
+ ["import", configNames.import],
33
+ ["import-x", configNames.import],
34
+ ["jest", configNames.testing],
35
+ ["jsdoc", configNames.jsdoc],
36
+ ["nextjs", configNames.nextjs],
37
+ ["perfectionist", configNames.perfectionist],
38
+ ["react", configNames.react],
39
+ ["react-hooks", configNames.react],
40
+ ["react-refresh", configNames.reactRefresh],
41
+ ["storybook", configNames.storybook],
42
+ ["turbo", configNames.turbo],
43
+ ["unicorn", configNames.unicorn],
44
+ ["vitest", configNames.testing]
45
+ ]);
30
46
 
31
47
  // src/eslint/base/rules.ts
32
- var baseEslintRules = (functionStyle) => ({
48
+ var baseEslintRules = (functionStyle, typescript) => ({
49
+ ...!typescript ? {
50
+ "no-unused-expressions": "warn",
51
+ "no-unused-vars": "warn"
52
+ } : {},
33
53
  /**
34
54
  * Require return statements in array methods callbacks.
35
55
  *
@@ -84,10 +104,8 @@ var baseEslintRules = (functionStyle) => ({
84
104
  * 🚫 Not fixable - https://eslint.org/docs/rules/func-names
85
105
  */
86
106
  "func-names": ["warn", "as-needed"],
87
- "func-style": (
88
- // if arrow function, we use the prefer-arrow-functions plugin
89
- functionStyle === "off" || functionStyle === "arrow" ? "off" : ["warn", functionStyle, { allowArrowFunctions: true }]
90
- ),
107
+ // if arrow function, we use the prefer-arrow-functions plugin
108
+ ...functionStyle === "off" ? { "func-style": "off" } : functionStyle === "arrow" ? { "func-style": "off" } : { "func-style": ["warn", functionStyle, { allowArrowFunctions: true }] },
91
109
  /**
92
110
  * Require grouped accessor pairs in object literals and classes.
93
111
  *
@@ -451,13 +469,17 @@ var baseEslintRules = (functionStyle) => ({
451
469
  });
452
470
 
453
471
  // src/eslint/base/config.ts
454
- var baseEslintConfig = (functionStyle) => ({
472
+ var baseEslintConfig = (functionStyle, typescript, customRules) => ({
455
473
  languageOptions: {
456
- ecmaVersion: 2022
474
+ ecmaVersion: "latest",
475
+ sourceType: "module"
457
476
  },
458
477
  linterOptions: { reportUnusedDisableDirectives: true },
459
478
  name: configNames.base,
460
- rules: baseEslintRules(functionStyle)
479
+ rules: {
480
+ ...baseEslintRules(functionStyle, typescript),
481
+ ...customRules ?? {}
482
+ }
461
483
  });
462
484
 
463
485
  // src/eslint/ignores.ts
@@ -467,9 +489,7 @@ var ignoresConfig = ({
467
489
  userIgnores = []
468
490
  } = {}) => ({
469
491
  ignores: [
470
- "**/node_modules/",
471
492
  "**/dist/",
472
- ".git/",
473
493
  ...next ? [".next"] : [],
474
494
  ...storybook ? ["!.storybook"] : [],
475
495
  ...userIgnores
@@ -477,6 +497,110 @@ var ignoresConfig = ({
477
497
  name: configNames.ignores
478
498
  });
479
499
 
500
+ // src/eslint/import/config.ts
501
+ import tsParser from "@typescript-eslint/parser";
502
+ import { createTypeScriptImportResolver } from "eslint-import-resolver-typescript";
503
+ import importXPlugin from "eslint-plugin-import-x";
504
+
505
+ // src/eslint/import/rules.ts
506
+ var importRules = (typescript) => ({
507
+ // these rules are better handled by typescript
508
+ ...!typescript ? {
509
+ "import-x/default": "warn",
510
+ "import-x/export": "warn",
511
+ "import-x/named": "warn",
512
+ "import-x/namespace": "warn",
513
+ "import-x/no-unresolved": "warn"
514
+ } : {},
515
+ /**
516
+ * Disallow non-import statements appearing before import statements.
517
+ *
518
+ * 🚫 Not fixable - https://github.com/import-js/eslint-plugin-import-x/blob/main/docs/rules/first.md
519
+ */
520
+ "import-x/first": "warn",
521
+ /**
522
+ * Require a newline after the last import-x/require.
523
+ *
524
+ * 🔧 Fixable - https://github.com/import-js/eslint-plugin-import-x/blob/main/docs/rules/newline-after-import.md
525
+ */
526
+ "import-x/newline-after-import": "warn",
527
+ /**
528
+ * Disallow import of modules using absolute paths.
529
+ *
530
+ * 🚫 Not fixable - https://github.com/import-js/eslint-plugin-import-x/blob/main/docs/rules/no-absolute-path.md
531
+ */
532
+ "import-x/no-absolute-path": "warn",
533
+ /**
534
+ * Disallow cyclical dependencies between modules.
535
+ *
536
+ * 🚫 Not fixable - https://github.com/import-js/eslint-plugin-import-x/blob/main/docs/rules/no-cycle.md
537
+ */
538
+ "import-x/no-cycle": "warn",
539
+ "import-x/no-duplicates": "warn",
540
+ /**
541
+ * Disallow the use of extraneous packages.
542
+ *
543
+ * 🚫 Not fixable - https://github.com/import-js/eslint-plugin-import-x/blob/main/docs/rules/no-extraneous-dependencies.md
544
+ */
545
+ "import-x/no-extraneous-dependencies": ["warn", { includeTypes: true }],
546
+ /**
547
+ * Disallow mutable exports.
548
+ *
549
+ * 🚫 Not fixable - https://github.com/import-js/eslint-plugin-import-x/blob/main/docs/rules/no-mutable-exports.md
550
+ */
551
+ "import-x/no-mutable-exports": "warn",
552
+ // red flags (thus, warnings)
553
+ "import-x/no-named-as-default": "warn",
554
+ "import-x/no-named-as-default-member": "warn",
555
+ /**
556
+ * Disallow importing packages through relative paths.
557
+ *
558
+ * 🚫 Not fixable - https://github.com/import-js/eslint-plugin-import-x/blob/main/docs/rules/no-relative-packages.md
559
+ */
560
+ "import-x/no-relative-packages": "warn",
561
+ /**
562
+ * Disallow a module from importing itself.
563
+ *
564
+ * 🚫 Not fixable - https://github.com/import-js/eslint-plugin-import-x/blob/main/docs/rules/no-self-import.md
565
+ */
566
+ "import-x/no-self-import": "warn",
567
+ /**
568
+ * Ensures that there are no useless path segments.
569
+ *
570
+ * 🚫 Not fixable - https://github.com/import-js/eslint-plugin-import-x/blob/main/docs/rules/no-useless-path-segments.md
571
+ */
572
+ "import-x/no-useless-path-segments": ["warn"]
573
+ });
574
+
575
+ // src/eslint/import/config.ts
576
+ var importConfig = (typescript, customRules) => ({
577
+ languageOptions: {
578
+ ecmaVersion: "latest",
579
+ parser: tsParser,
580
+ sourceType: "module"
581
+ },
582
+ name: configNames.import,
583
+ plugins: {
584
+ "import-x": importXPlugin
585
+ },
586
+ rules: {
587
+ ...importRules(typescript),
588
+ ...customRules ?? {}
589
+ },
590
+ settings: {
591
+ "import-x/resolver": {
592
+ node: true,
593
+ typescript
594
+ },
595
+ "import-x/resolver-next": [
596
+ createTypeScriptImportResolver({
597
+ alwaysTryTypes: true,
598
+ bun: true
599
+ })
600
+ ]
601
+ }
602
+ });
603
+
480
604
  // src/eslint/jsdoc/config.ts
481
605
  import jsdoc from "eslint-plugin-jsdoc";
482
606
 
@@ -551,14 +675,17 @@ var jsdocRules = (requireJsdoc = false, typescript = true) => ({
551
675
  });
552
676
 
553
677
  // src/eslint/jsdoc/config.ts
554
- var jsdocConfig = (requireJsdoc = false, typescript = true) => ({
678
+ var jsdocConfig = (requireJsdoc = false, customRules) => ({
555
679
  files: ["**/*.{js,jsx,ts,tsx,cjs,mjs}"],
556
680
  ignores: ["**/*.{test,spec}.{js,jsx,ts,tsx,cjs,mjs}"],
557
681
  name: configNames.jsdoc,
558
682
  plugins: {
559
683
  jsdoc
560
684
  },
561
- rules: jsdocRules(requireJsdoc, typescript)
685
+ rules: {
686
+ ...jsdocRules(requireJsdoc),
687
+ ...customRules ?? {}
688
+ }
562
689
  });
563
690
 
564
691
  // src/eslint/nextjs/config.ts
@@ -590,12 +717,15 @@ var nextjsRules = {
590
717
  };
591
718
 
592
719
  // src/eslint/nextjs/config.ts
593
- var nextjsConfig = () => ({
720
+ var nextjsConfig = (customRules) => ({
594
721
  name: configNames.nextjs,
595
722
  plugins: {
596
723
  nextjs
597
724
  },
598
- rules: nextjsRules
725
+ rules: {
726
+ ...nextjsRules,
727
+ ...customRules ?? {}
728
+ }
599
729
  });
600
730
 
601
731
  // src/eslint/perfectionist/config.ts
@@ -630,17 +760,20 @@ var perfectionistRules = {
630
760
  };
631
761
 
632
762
  // src/eslint/perfectionist/config.ts
633
- var perfectionistConfig = {
763
+ var perfectionistConfig = (customRules) => ({
634
764
  name: configNames.perfectionist,
635
765
  plugins: {
636
766
  perfectionist
637
767
  },
638
- rules: perfectionistRules
639
- };
768
+ rules: {
769
+ ...perfectionistRules,
770
+ ...customRules ?? {}
771
+ }
772
+ });
640
773
 
641
774
  // src/eslint/prefer-arrow-function/config.ts
642
775
  import preferArrowFunctions from "eslint-plugin-prefer-arrow-functions";
643
- var preferArrowFunctionConfig = () => ({
776
+ var preferArrowFunctionConfig = (customRules) => ({
644
777
  name: configNames.preferArrowFunction,
645
778
  plugins: {
646
779
  "prefer-arrow-functions": preferArrowFunctions
@@ -652,20 +785,46 @@ var preferArrowFunctionConfig = () => ({
652
785
  returnStyle: "unchanged",
653
786
  singleReturnOnly: false
654
787
  }
655
- ]
788
+ ],
789
+ ...customRules ?? {}
656
790
  }
657
791
  });
658
792
 
659
- // src/eslint/react-compiler/config.ts
660
- import reactCompiler from "eslint-plugin-react-compiler";
661
- var reactCompilerEslintConfig = {
662
- name: configNames.reactCompiler,
663
- plugins: {
664
- "react-compiler": reactCompiler
665
- },
666
- rules: {
667
- "react-compiler/react-compiler": "warn"
793
+ // src/eslint/process-custom-rules.ts
794
+ var processCustomRules = (customRules) => {
795
+ const categorizedRules = Object.values(configNames).reduce(
796
+ (acc, configName) => {
797
+ acc[configName] = {};
798
+ return acc;
799
+ },
800
+ {}
801
+ );
802
+ for (const [ruleKey, ruleValue] of Object.entries(customRules)) {
803
+ if (!ruleKey.includes("/") && !ruleKey.startsWith("@")) {
804
+ categorizedRules[configNames.base][ruleKey] = ruleValue;
805
+ continue;
806
+ }
807
+ let prefix = null;
808
+ if (ruleKey.startsWith("@")) {
809
+ const firstSlashIndex = ruleKey.indexOf("/");
810
+ if (firstSlashIndex !== -1) {
811
+ prefix = ruleKey.substring(0, firstSlashIndex);
812
+ }
813
+ } else {
814
+ const firstSlashIndex = ruleKey.indexOf("/");
815
+ if (firstSlashIndex !== -1) {
816
+ prefix = ruleKey.substring(0, firstSlashIndex);
817
+ }
818
+ }
819
+ const configName = prefix ? pluginPrefixMap.get(prefix) ?? configNames.base : configNames.base;
820
+ categorizedRules[configName][ruleKey] = ruleValue;
668
821
  }
822
+ return Object.entries(categorizedRules).reduce((acc, [configName, rules2]) => {
823
+ if (Object.keys(rules2).length > 0) {
824
+ acc[configName] = rules2;
825
+ }
826
+ return acc;
827
+ }, {});
669
828
  };
670
829
 
671
830
  // src/eslint/react-refresh/config.ts
@@ -688,13 +847,13 @@ var reactRefreshRules = {
688
847
  };
689
848
 
690
849
  // src/eslint/react-refresh/config.ts
691
- var reactRefreshEslintConfig = () => {
850
+ var reactRefreshEslintConfig = (customRules) => {
692
851
  return {
693
852
  name: configNames.reactRefresh,
694
853
  plugins: {
695
854
  "react-refresh": reactRefresh
696
855
  },
697
- rules: reactRefreshRules
856
+ rules: customRules ?? reactRefreshRules
698
857
  };
699
858
  };
700
859
 
@@ -716,6 +875,7 @@ var reactRules = (functionStyle, typescript) => {
716
875
  */
717
876
  ...typescript ? {} : { "react/prop-types": "warn" },
718
877
  "react-hooks/exhaustive-deps": "warn",
878
+ "react-hooks/react-compiler": "warn",
719
879
  "react-hooks/rules-of-hooks": "warn",
720
880
  /**
721
881
  * Require an explicit type when using button elements.
@@ -833,7 +993,7 @@ var reactRules = (functionStyle, typescript) => {
833
993
  };
834
994
 
835
995
  // src/eslint/react/config.ts
836
- var reactEslintConfig = (functionStyle, typescript) => {
996
+ var reactEslintConfig = (functionStyle, typescript, customRules) => {
837
997
  return {
838
998
  languageOptions: {
839
999
  globals: {
@@ -850,7 +1010,10 @@ var reactEslintConfig = (functionStyle, typescript) => {
850
1010
  react,
851
1011
  "react-hooks": pluginReactHooks
852
1012
  },
853
- rules: reactRules(functionStyle, typescript),
1013
+ rules: {
1014
+ ...reactRules(functionStyle, typescript),
1015
+ ...customRules ?? {}
1016
+ },
854
1017
  settings: {
855
1018
  react: {
856
1019
  version: "detect"
@@ -861,7 +1024,7 @@ var reactEslintConfig = (functionStyle, typescript) => {
861
1024
 
862
1025
  // src/eslint/storybook/config.ts
863
1026
  import storybookPlugin from "eslint-plugin-storybook";
864
- var storybookConfig = [
1027
+ var storybookConfig = (customRules) => [
865
1028
  {
866
1029
  files: [
867
1030
  "**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)",
@@ -872,6 +1035,7 @@ var storybookConfig = [
872
1035
  storybook: storybookPlugin
873
1036
  },
874
1037
  rules: {
1038
+ // Default Storybook rules
875
1039
  "import/no-anonymous-default-export": "off",
876
1040
  "react-hooks/rules-of-hooks": "off",
877
1041
  "storybook/await-interactions": "warn",
@@ -884,7 +1048,9 @@ var storybookConfig = [
884
1048
  "storybook/prefer-pascal-case": "warn",
885
1049
  "storybook/story-exports": "warn",
886
1050
  "storybook/use-storybook-expect": "warn",
887
- "storybook/use-storybook-testing-library": "warn"
1051
+ "storybook/use-storybook-testing-library": "warn",
1052
+ // Merge custom rules
1053
+ ...customRules ?? {}
888
1054
  }
889
1055
  },
890
1056
  {
@@ -1003,7 +1169,7 @@ var testingConfig = ({
1003
1169
  formattingRules: true,
1004
1170
  framework: "vitest",
1005
1171
  itOrTest: "test"
1006
- }) => ({
1172
+ }, customRules) => ({
1007
1173
  files: files ?? ["**/*.{test,spec}.{ts,tsx,js,jsx}"],
1008
1174
  languageOptions: {
1009
1175
  globals: framework === "vitest" ? { ...vitest.environments.env.globals } : jest.environments.globals.globals
@@ -1032,7 +1198,8 @@ var testingConfig = ({
1032
1198
  "jest/padding-around-describe-blocks": "warn",
1033
1199
  "jest/padding-around-expect-groups": "warn",
1034
1200
  "jest/padding-around-test-blocks": "warn"
1035
- } : {}
1201
+ } : {},
1202
+ ...customRules ?? {}
1036
1203
  },
1037
1204
  ...framework !== "jest" && framework !== "vitest" ? {
1038
1205
  settings: {
@@ -1045,12 +1212,12 @@ var testingConfig = ({
1045
1212
 
1046
1213
  // src/eslint/turbo/config.ts
1047
1214
  import turbo from "eslint-plugin-turbo";
1048
- var turboConfig = () => ({
1215
+ var turboConfig = (customRules) => ({
1049
1216
  name: configNames.turbo,
1050
1217
  plugins: {
1051
1218
  turbo
1052
1219
  },
1053
- rules: {
1220
+ rules: customRules ?? {
1054
1221
  "turbo/no-undeclared-env-vars": "warn"
1055
1222
  }
1056
1223
  });
@@ -1150,7 +1317,12 @@ var tseslintRules = {
1150
1317
  "@typescript-eslint/prefer-includes": "warn",
1151
1318
  "@typescript-eslint/prefer-literal-enum-member": "warn",
1152
1319
  "@typescript-eslint/prefer-namespace-keyword": "warn",
1153
- "@typescript-eslint/prefer-nullish-coalescing": "warn",
1320
+ "@typescript-eslint/prefer-nullish-coalescing": [
1321
+ "warn",
1322
+ {
1323
+ ignorePrimitives: { string: true }
1324
+ }
1325
+ ],
1154
1326
  "@typescript-eslint/prefer-optional-chain": "warn",
1155
1327
  "@typescript-eslint/prefer-promise-reject-errors": "warn",
1156
1328
  "@typescript-eslint/prefer-reduce-type-parameter": "warn",
@@ -1191,7 +1363,7 @@ var tseslintRules = {
1191
1363
  };
1192
1364
 
1193
1365
  // src/eslint/typescript/config.ts
1194
- var tseslintConfig = (tsconfigPath) => {
1366
+ var tseslintConfig = (tsconfigPath, customRules) => {
1195
1367
  const userCwd = process.cwd();
1196
1368
  return tseslint.config(
1197
1369
  {
@@ -1206,7 +1378,10 @@ var tseslintConfig = (tsconfigPath) => {
1206
1378
  plugins: {
1207
1379
  "@typescript-eslint": tseslint.plugin
1208
1380
  },
1209
- rules: tseslintRules
1381
+ rules: {
1382
+ ...tseslintRules,
1383
+ ...customRules ?? {}
1384
+ }
1210
1385
  },
1211
1386
  {
1212
1387
  // disable type-aware linting on JS files
@@ -1296,108 +1471,132 @@ var rules = {
1296
1471
  };
1297
1472
 
1298
1473
  // src/eslint/unicorn/config.ts
1299
- var unicornConfig = {
1474
+ var unicornConfig = (customRules) => ({
1300
1475
  name: configNames.unicorn,
1301
1476
  plugins: {
1302
1477
  unicorn
1303
1478
  },
1304
- rules
1305
- };
1479
+ rules: {
1480
+ ...rules,
1481
+ ...customRules ?? {}
1482
+ }
1483
+ });
1306
1484
 
1307
1485
  // src/eslint/index.ts
1486
+ var defaultTestingConfig = {
1487
+ filenamePattern: "test",
1488
+ files: ["**/*.{test,spec}.{ts,tsx,js,jsx}"],
1489
+ formattingRules: true,
1490
+ framework: "vitest",
1491
+ itOrTest: "it"
1492
+ };
1308
1493
  var eslintConfig = ({
1309
1494
  functionStyle = "arrow",
1310
1495
  ignores = [],
1496
+ importPlugin = true,
1311
1497
  jsdoc: jsdoc2 = { requireJsdoc: false },
1312
1498
  react: react2 = false,
1499
+ rules: rules2,
1313
1500
  sorting = true,
1314
1501
  storybook = false,
1315
- testing,
1316
- /**
1317
- * Some preceding documentation...
1318
- *
1319
- * @param options.turbo - Whether to include Turborepo rules. Defaults to false.
1320
- *
1321
- * Some following documentation...
1322
- */
1502
+ testing = defaultTestingConfig,
1323
1503
  turbo: turbo2 = false,
1324
1504
  typescript = true,
1325
1505
  unicorn: unicorn2 = true
1326
1506
  } = {}, ...additionalConfigs) => {
1507
+ const categorizedRules = rules2 === void 0 ? {} : processCustomRules(rules2);
1508
+ const usingNextjs = isObject(react2) && react2.framework === "next";
1327
1509
  const configs = [
1328
1510
  ignoresConfig({
1329
- next: isObject(react2) && (react2.framework === "next" || react2.next === true),
1511
+ next: usingNextjs,
1330
1512
  storybook,
1331
1513
  userIgnores: ignores
1332
1514
  }),
1333
- baseEslintConfig(functionStyle)
1515
+ baseEslintConfig(
1516
+ functionStyle,
1517
+ Boolean(typescript),
1518
+ categorizedRules[configNames.base]
1519
+ )
1334
1520
  ];
1335
1521
  if (jsdoc2 !== false) {
1336
- configs.push(jsdocConfig(jsdoc2.requireJsdoc ?? false));
1522
+ configs.push(
1523
+ jsdocConfig(
1524
+ jsdoc2.requireJsdoc ?? false,
1525
+ categorizedRules[configNames.jsdoc]
1526
+ )
1527
+ );
1337
1528
  }
1338
1529
  if (typescript) {
1339
1530
  configs.push(
1340
1531
  ...tseslintConfig(
1341
- isString(typescript) ? typescript : void 0
1532
+ isString(typescript) ? typescript : void 0,
1533
+ categorizedRules[configNames.typescript]
1342
1534
  )
1343
1535
  );
1344
1536
  }
1537
+ if (importPlugin) {
1538
+ configs.push(
1539
+ importConfig(Boolean(typescript), categorizedRules[configNames.import])
1540
+ );
1541
+ }
1345
1542
  if (react2) {
1346
- configs.push(reactEslintConfig(functionStyle, Boolean(typescript)));
1347
- const shouldUseReactCompiler = react2 === true || isObject(react2) && react2.reactCompiler !== false;
1348
- if (shouldUseReactCompiler) {
1349
- configs.push(reactCompilerEslintConfig);
1350
- }
1351
- const isNextFramework = isObject(react2) && (react2.framework === "next" || react2.next === true && react2.framework === void 0);
1352
- if (isNextFramework) {
1353
- configs.push(nextjsConfig());
1354
- }
1355
1543
  const shouldUseReactRefresh = (
1356
1544
  // Explicit setting takes precedence
1357
1545
  isObject(react2) && react2.reactRefresh === true || // Framework-based default (vite/none use reactRefresh by default)
1358
1546
  isObject(react2) && (react2.framework === "vite" || react2.framework === "none") && react2.reactRefresh !== false
1359
1547
  );
1360
1548
  if (shouldUseReactRefresh) {
1361
- configs.push(reactRefreshEslintConfig());
1549
+ configs.push(
1550
+ reactRefreshEslintConfig(categorizedRules[configNames.reactRefresh])
1551
+ );
1552
+ }
1553
+ configs.push(
1554
+ reactEslintConfig(
1555
+ functionStyle,
1556
+ Boolean(typescript),
1557
+ categorizedRules[configNames.react]
1558
+ )
1559
+ );
1560
+ if (usingNextjs) {
1561
+ configs.push(nextjsConfig(categorizedRules[configNames.nextjs]));
1362
1562
  }
1363
1563
  }
1364
1564
  if (testing !== false) {
1365
- const defaultTestingConfig = {
1366
- filenamePattern: "test",
1367
- files: ["**/*.{test,spec}.{ts,tsx,js,jsx}"],
1368
- formattingRules: true,
1369
- framework: "vitest",
1370
- itOrTest: "it"
1371
- };
1372
- const mergedTestingConfig = {
1373
- ...defaultTestingConfig,
1374
- ...isObject(testing) ? testing : {}
1375
- };
1565
+ const mergedTestingConfig = isObject(testing) ? { ...defaultTestingConfig, ...testing } : defaultTestingConfig;
1376
1566
  const { filenamePattern, files, formattingRules, framework, itOrTest } = mergedTestingConfig;
1377
1567
  configs.push(
1378
- testingConfig({
1379
- filenamePattern,
1380
- files,
1381
- formattingRules,
1382
- framework,
1383
- itOrTest
1384
- })
1568
+ testingConfig(
1569
+ {
1570
+ filenamePattern,
1571
+ files,
1572
+ formattingRules,
1573
+ framework,
1574
+ itOrTest
1575
+ },
1576
+ categorizedRules[configNames.testing]
1577
+ )
1385
1578
  );
1386
1579
  }
1387
1580
  if (sorting) {
1388
- configs.push(perfectionistConfig);
1581
+ configs.push(
1582
+ perfectionistConfig(categorizedRules[configNames.perfectionist])
1583
+ );
1389
1584
  }
1390
1585
  if (unicorn2) {
1391
- configs.push(unicornConfig);
1586
+ configs.push(unicornConfig(categorizedRules[configNames.unicorn]));
1392
1587
  }
1393
1588
  if (functionStyle === "arrow") {
1394
- configs.push(preferArrowFunctionConfig());
1589
+ configs.push(
1590
+ preferArrowFunctionConfig(
1591
+ categorizedRules[configNames.preferArrowFunction]
1592
+ )
1593
+ );
1395
1594
  }
1396
1595
  if (storybook) {
1397
- configs.push(...storybookConfig);
1596
+ configs.push(...storybookConfig(categorizedRules[configNames.storybook]));
1398
1597
  }
1399
1598
  if (turbo2) {
1400
- configs.push(turboConfig());
1599
+ configs.push(turboConfig(categorizedRules[configNames.turbo]));
1401
1600
  }
1402
1601
  if (additionalConfigs.length > 0) {
1403
1602
  configs.push(...additionalConfigs);