@fabdeh/eslint-config 0.1.3 → 0.2.1

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.mjs CHANGED
@@ -1,20 +1,20 @@
1
+ import { isPackageExists } from 'local-pkg';
1
2
  import tseslint from 'typescript-eslint';
2
3
  import { statSync } from 'node:fs';
3
4
  import { readFile } from 'node:fs/promises';
4
5
  import { resolve, dirname, join } from 'node:path';
5
6
  import process from 'node:process';
6
7
  import { fileURLToPath } from 'node:url';
7
- import { isPackageExists } from 'local-pkg';
8
- import eslintComments from '@eslint-community/eslint-plugin-eslint-comments';
9
- import * as importX from 'eslint-plugin-import-x';
10
8
  import eslint from '@eslint/js';
11
9
  import preferArrowFunctions from 'eslint-plugin-prefer-arrow-functions';
12
10
  import unusedImports from 'eslint-plugin-unused-imports';
13
11
  import globals from 'globals';
12
+ import eslintComments from '@eslint-community/eslint-plugin-eslint-comments';
14
13
  import jsdocPlugin from 'eslint-plugin-jsdoc';
15
- import { mergeProcessors, processorPassThrough } from 'eslint-merge-processors';
14
+ import * as importX from 'eslint-plugin-import-x';
16
15
  import perfectionistPlugin from 'eslint-plugin-perfectionist';
17
16
  import unicornPlugin from 'eslint-plugin-unicorn';
17
+ import { mergeProcessors, processorPassThrough } from 'eslint-merge-processors';
18
18
 
19
19
  const GLOB_SRC_EXT = "?([cm])[jt]s?(x)";
20
20
  const GLOB_SRC = `**/*.${GLOB_SRC_EXT}`;
@@ -76,24 +76,6 @@ const GLOB_EXCLUDE = [
76
76
 
77
77
  const SCOPE_URL = fileURLToPath(new URL(".", import.meta.url));
78
78
  const IS_CWD_IN_SCOPE = isPackageExists("@fabdeh/eslint-config");
79
- function isInEditorEnv() {
80
- if (process.env.CI) {
81
- return false;
82
- }
83
- const envKeys = Object.keys(process.env);
84
- if (envKeys.some((k) => /^GIT_/i.test(k))) {
85
- return false;
86
- }
87
- const isVsCode = envKeys.some((k) => /^VSCODE_/i.test(k));
88
- const isIntelliJ = envKeys.some((k) => /^INTELLIJ_/i.test(k));
89
- const isJetbrains = envKeys.some((k) => /^JETBRAINS_/i.test(k));
90
- const isVim = envKeys.some((k) => /^VIM$/i.test(k));
91
- const isNeoVim = envKeys.some((k) => /^NVIM$/i.test(k));
92
- return isVsCode || isIntelliJ || isJetbrains || isVim || isNeoVim;
93
- }
94
- function toArray(value) {
95
- return Array.isArray(value) ? value : [value];
96
- }
97
79
  async function interopDefault(m) {
98
80
  const resolved = await m;
99
81
  return resolved.default ?? resolved;
@@ -153,6 +135,12 @@ async function findNearestPackageJsonName(directoryPath = resolve()) {
153
135
  return findNearestPackageJsonName(parentDir);
154
136
  }
155
137
  }
138
+ function resolveSubOptions(options, key) {
139
+ if (typeof options[key] === "boolean") {
140
+ return {};
141
+ }
142
+ return options[key] ?? {};
143
+ }
156
144
 
157
145
  async function angular(options = {}) {
158
146
  const angularEslint = await interopDefault(import('angular-eslint'));
@@ -165,10 +153,36 @@ async function angular(options = {}) {
165
153
  },
166
154
  processor: angularEslint.processInlineTemplates,
167
155
  files: [GLOB_TS],
168
- extends: [angularEslint.configs.tsRecommended],
169
156
  rules: {
170
157
  "max-classes-per-file": ["error", 1],
171
158
  "max-lines": ["error", 400],
159
+ "new-cap": [
160
+ "error",
161
+ {
162
+ capIsNewExceptions: [
163
+ "Attribute",
164
+ "Component",
165
+ "ContentChild",
166
+ "ContentChildren",
167
+ "Directive",
168
+ "Host",
169
+ "HostBinding",
170
+ "HostListener",
171
+ "Inject",
172
+ "Injectable",
173
+ "Input",
174
+ "NgModule",
175
+ "Optional",
176
+ "Output",
177
+ "Pipe",
178
+ "Self",
179
+ "SkipSelf",
180
+ "ViewChild",
181
+ "ViewChildren"
182
+ ]
183
+ }
184
+ ],
185
+ ...angularEslint.configs.tsRecommended.find((c) => c.name === "angular-eslint/ts-recommended")?.rules,
172
186
  "@angular-eslint/component-max-inline-declarations": "error",
173
187
  "@angular-eslint/component-selector": [
174
188
  "error",
@@ -208,14 +222,14 @@ async function angular(options = {}) {
208
222
  {
209
223
  name: "fabdeh/angular-template/rules",
210
224
  plugins: {
211
- "@angular-eslint": angularEslint.tsPlugin
225
+ "@angular-eslint/template": angularEslint.templatePlugin
226
+ },
227
+ languageOptions: {
228
+ parser: angularEslint.templateParser
212
229
  },
213
230
  files: [GLOB_HTML],
214
- extends: [
215
- ...angularEslint.configs.templateRecommended,
216
- ...enableAccessibilityRules ? angularEslint.configs.templateAccessibility : []
217
- ],
218
231
  rules: {
232
+ ...angularEslint.configs.templateRecommended.find((c) => c.name === "angular-eslint/template-recommended")?.rules,
219
233
  "@angular-eslint/template/attributes-order": ["error", { alphabetical: true }],
220
234
  "@angular-eslint/template/button-has-type": "error",
221
235
  "@angular-eslint/template/conditional-complexity": "error",
@@ -227,6 +241,8 @@ async function angular(options = {}) {
227
241
  "@angular-eslint/template/prefer-control-flow": "error",
228
242
  "@angular-eslint/template/prefer-ngsrc": "error",
229
243
  "@angular-eslint/template/prefer-self-closing-tags": "error",
244
+ "@angular-eslint/template/prefer-static-string-properties": "error",
245
+ ...enableAccessibilityRules ? angularEslint.configs.templateAccessibility.find((c) => c.name === "angular-eslint/template-accessibility")?.rules : {},
230
246
  ...htmlOverrides
231
247
  }
232
248
  }
@@ -236,7 +252,6 @@ async function angular(options = {}) {
236
252
  function comments() {
237
253
  return tseslint.config({
238
254
  name: "fabdeh/comments/rules",
239
- files: [GLOB_SRC],
240
255
  plugins: {
241
256
  "@eslint-community/eslint-comments": eslintComments
242
257
  },
@@ -262,8 +277,7 @@ function imports(options = {}) {
262
277
  const { stylistic = true } = options;
263
278
  return tseslint.config(
264
279
  {
265
- name: "fabdeh/imports/common/rules",
266
- files: [GLOB_SRC],
280
+ name: "fabdeh/imports/rules",
267
281
  plugins: {
268
282
  "import-x": importX
269
283
  },
@@ -272,7 +286,9 @@ function imports(options = {}) {
272
286
  "import-x/default": "error",
273
287
  "import-x/export": "error",
274
288
  "import-x/first": "error",
289
+ "import-x/named": "error",
275
290
  "import-x/no-absolute-path": "error",
291
+ "import-x/no-deprecated": "error",
276
292
  "import-x/no-duplicates": "error",
277
293
  "import-x/no-empty-named-blocks": "error",
278
294
  "import-x/no-extraneous-dependencies": "error",
@@ -289,15 +305,7 @@ function imports(options = {}) {
289
305
  }
290
306
  },
291
307
  {
292
- name: "fabdeh/imports/js-only/rules",
293
- files: [GLOB_JS],
294
- rules: {
295
- "import-x/named": "error",
296
- "import-x/no-deprecated": "error"
297
- }
298
- },
299
- {
300
- name: "fabdeh/imports/ts-only/rules",
308
+ name: "fabdeh/imports/ts-disables",
301
309
  files: [GLOB_TS],
302
310
  rules: {
303
311
  "import-x/named": "off",
@@ -308,7 +316,7 @@ function imports(options = {}) {
308
316
  }
309
317
 
310
318
  function javascript(options = {}) {
311
- const { isInEditor = false, overrides = {}, allowAngularDecorator = false } = options;
319
+ const { overrides = {} } = options;
312
320
  return tseslint.config(
313
321
  {
314
322
  name: "fabdeh/javascript/setup",
@@ -322,7 +330,6 @@ function javascript(options = {}) {
322
330
  navigator: "readonly",
323
331
  window: "readonly"
324
332
  },
325
- parser: tseslint.parser,
326
333
  parserOptions: {
327
334
  ecmaFeatures: {
328
335
  jsx: true
@@ -338,14 +345,13 @@ function javascript(options = {}) {
338
345
  },
339
346
  {
340
347
  name: "fabdeh/javascript/rules",
341
- files: [GLOB_SRC],
342
348
  plugins: {
343
349
  "@typescript-eslint": tseslint.plugin,
344
350
  "prefer-arrow-functions": preferArrowFunctions,
345
351
  "unused-imports": unusedImports
346
352
  },
347
- extends: [eslint.configs.recommended, ...tseslint.configs.recommended],
348
353
  rules: {
354
+ ...eslint.configs.recommended.rules,
349
355
  "accessor-pairs": "error",
350
356
  "array-callback-return": "error",
351
357
  "block-scoped-var": "error",
@@ -364,32 +370,7 @@ function javascript(options = {}) {
364
370
  "Undefined",
365
371
  "undefined"
366
372
  ],
367
- "new-cap": allowAngularDecorator ? [
368
- "error",
369
- {
370
- capIsNewExceptions: [
371
- "Attribute",
372
- "Component",
373
- "ContentChild",
374
- "ContentChildren",
375
- "Directive",
376
- "Host",
377
- "HostBinding",
378
- "HostListener",
379
- "Inject",
380
- "Injectable",
381
- "Input",
382
- "NgModule",
383
- "Optional",
384
- "Output",
385
- "Pipe",
386
- "Self",
387
- "SkipSelf",
388
- "ViewChild",
389
- "ViewChildren"
390
- ]
391
- }
392
- ] : "error",
373
+ "new-cap": "error",
393
374
  "no-alert": "error",
394
375
  "no-caller": "error",
395
376
  "no-cond-assign": ["error", "always"],
@@ -433,6 +414,7 @@ function javascript(options = {}) {
433
414
  "no-useless-constructor": "error",
434
415
  "no-useless-rename": "error",
435
416
  "no-useless-return": "error",
417
+ "no-var": "error",
436
418
  "object-shorthand": [
437
419
  "error",
438
420
  "always",
@@ -446,14 +428,18 @@ function javascript(options = {}) {
446
428
  "error",
447
429
  { singleReturnOnly: true, allowNamedFunctions: true }
448
430
  ],
431
+ "prefer-const": "error",
449
432
  "prefer-exponentiation-operator": "error",
450
433
  "prefer-object-spread": "error",
451
434
  "prefer-promise-reject-errors": "error",
452
435
  "prefer-regex-literals": ["error", { disallowRedundantWrapping: true }],
436
+ "prefer-rest-params": "error",
437
+ "prefer-spread": "error",
453
438
  "prefer-template": "error",
454
439
  "symbol-description": "error",
455
440
  "unicode-bom": ["error", "never"],
456
- "unused-imports/no-unused-imports": isInEditor ? "warn" : "error",
441
+ "unused-imports/no-unused-imports": "error",
442
+ "no-unused-vars": "off",
457
443
  "unused-imports/no-unused-vars": [
458
444
  "error",
459
445
  {
@@ -468,70 +454,23 @@ function javascript(options = {}) {
468
454
  "valid-typeof": ["error", { requireStringLiterals: true }],
469
455
  "vars-on-top": "error",
470
456
  yoda: ["error", "never", { exceptRange: true }],
471
- "@typescript-eslint/no-unused-vars": "off",
472
- // superseded by unused-imports/no-unused-vars
473
- // TODO: check if required to apply more nx javascript rules here
457
+ "@typescript-eslint/ban-ts-comment": "error",
458
+ "@typescript-eslint/no-extra-non-null-assertion": "error",
459
+ "@typescript-eslint/no-misused-new": "error",
460
+ "@typescript-eslint/no-non-null-asserted-optional-chain": "error",
461
+ "@typescript-eslint/no-require-imports": ["error", { allowAsImport: true }],
462
+ "@typescript-eslint/no-this-alias": "error",
474
463
  ...overrides
475
464
  }
476
465
  }
477
466
  );
478
467
  }
479
468
 
480
- async function jest(options = {}) {
481
- const {
482
- overrides = {},
483
- useJestDom = isPackageExists("@testing-library/jest-dom"),
484
- useTestingLibrary = isPackageExists("@testing-library/angular"),
485
- stylistic = true
486
- } = options;
487
- const [jestPlugin, jestDomPlugin, testingLibraryPlugin] = await Promise.all([
488
- interopDefault(import('eslint-plugin-jest')),
489
- useJestDom ? interopDefault(import('eslint-plugin-jest-dom')) : Promise.resolve(undefined),
490
- useTestingLibrary ? interopDefault(import('eslint-plugin-testing-library')) : Promise.resolve(undefined)
491
- ]);
492
- return tseslint.config({
493
- name: "fabdeh/jest/rules",
494
- plugins: {
495
- jest: jestPlugin
496
- },
497
- languageOptions: {
498
- globals: {
499
- ...globals.node,
500
- ...globals.jest
501
- }
502
- },
503
- files: [...GLOB_TESTS, `** /jest.config.${GLOB_SRC_EXT}`],
504
- extends: [
505
- jestPlugin.configs["flat/recommended"],
506
- ...stylistic ? [jestPlugin.configs["flat/style"]] : [],
507
- ...jestDomPlugin ? [jestDomPlugin.configs["flat/recommended"]] : [],
508
- ...testingLibraryPlugin ? [testingLibraryPlugin.configs["flat/angular"]] : []
509
- ],
510
- rules: {
511
- "max-classes-per-file": "off",
512
- "max-lines": "off",
513
- "@typescript-eslint/consistent-type-assertions": "off",
514
- "@typescript-eslint/no-empty-function": "off",
515
- "@typescript-eslint/no-unsafe-assignment": "off",
516
- "@typescript-eslint/no-unsafe-call": "off",
517
- "@typescript-eslint/no-unsafe-member-access": "off",
518
- "@typescript-eslint/unbound-method": "off",
519
- "jest/consistent-test-it": ["error", { fn: "test" }],
520
- "jest/prefer-spy-on": "error",
521
- "jest/require-top-level-describe": "error",
522
- "jest/unbound-method": "error",
523
- "unicorn/no-null": "off",
524
- ...overrides
525
- }
526
- });
527
- }
528
-
529
469
  function jsdoc(options = {}) {
530
470
  const { stylistic = true } = options;
531
471
  return tseslint.config(
532
472
  {
533
473
  name: "fabdeh/jsdoc/rules",
534
- files: [GLOB_SRC],
535
474
  plugins: { jsdoc: jsdocPlugin },
536
475
  rules: {
537
476
  "jsdoc/check-access": "warn",
@@ -658,6 +597,28 @@ async function markdown(options = {}) {
658
597
  overrides = {}
659
598
  } = options;
660
599
  const markdownPlugin = await interopDefault(import('@eslint/markdown'));
600
+ const parserPlain = {
601
+ meta: {
602
+ name: "parser-plain"
603
+ },
604
+ parseForESLint: (code) => ({
605
+ ast: {
606
+ body: [],
607
+ comments: [],
608
+ loc: { end: code.length, start: 0 },
609
+ range: [0, code.length],
610
+ tokens: [],
611
+ type: "Program"
612
+ },
613
+ // eslint-disable-next-line unicorn/no-null
614
+ scopeManager: null,
615
+ services: { isPlain: true },
616
+ visitorKeys: {
617
+ // eslint-disable-next-line @typescript-eslint/naming-convention
618
+ Program: []
619
+ }
620
+ })
621
+ };
661
622
  return tseslint.config(
662
623
  {
663
624
  name: "fabdeh/markdown/setup",
@@ -674,6 +635,13 @@ async function markdown(options = {}) {
674
635
  processorPassThrough
675
636
  ])
676
637
  },
638
+ {
639
+ name: "fabdeh/markdown/parser",
640
+ files,
641
+ languageOptions: {
642
+ parser: parserPlain
643
+ }
644
+ },
677
645
  {
678
646
  name: "fabdeh/markdown/disables",
679
647
  languageOptions: {
@@ -684,8 +652,8 @@ async function markdown(options = {}) {
684
652
  }
685
653
  },
686
654
  files: [GLOB_MARKDOWN_CODE],
687
- extends: [tseslint.configs.disableTypeChecked],
688
655
  rules: {
656
+ ...tseslint.configs.disableTypeChecked.rules,
689
657
  "import-x/newline-after-import": "off",
690
658
  "no-alert": "off",
691
659
  "no-console": "off",
@@ -710,9 +678,16 @@ async function markdown(options = {}) {
710
678
  "unicode-bom": "off",
711
679
  "unused-imports/no-unused-imports": "off",
712
680
  "unused-imports/no-unused-vars": "off",
713
- "unicorn/filename-case": "off",
714
681
  ...overrides
715
682
  }
683
+ },
684
+ {
685
+ name: "fabdeh/markdown/rules",
686
+ files,
687
+ rules: {
688
+ ...markdownPlugin.configs.recommended.at(0)?.rules,
689
+ "markdown/no-duplicate-headings": "error"
690
+ }
716
691
  }
717
692
  );
718
693
  }
@@ -746,17 +721,22 @@ async function ngrx(options = {}) {
746
721
  signals = isPackageExists("@ngrx/signals")
747
722
  } = options;
748
723
  const configs = [];
724
+ let addOperatorsRules = false;
725
+ const ngrxOperatorsFiles = [];
749
726
  if (store) {
750
727
  const {
751
728
  files = DEFAULT_STORE_GLOB,
752
729
  enforceOperatorsRules = isPackageExists("@ngrx/operators"),
753
730
  overrides = {}
754
731
  } = typeof store === "object" ? store : {};
732
+ addOperatorsRules ||= enforceOperatorsRules;
733
+ ngrxOperatorsFiles.push(files);
755
734
  configs.push({
756
735
  name: "fabdeh/ngrx-store/rules",
757
736
  files,
758
- extends: [...ngrxPlugin.configs.store, ...enforceOperatorsRules ? ngrxPlugin.configs.operators : []],
737
+ plugins: { "@ngrx": ngrxPlugin },
759
738
  rules: {
739
+ ...ngrxPlugin.configs.store.find((c) => c.name === "ngrx/store")?.rules,
760
740
  "@typescript-eslint/naming-convention": [
761
741
  "error",
762
742
  ...namingConvention,
@@ -787,11 +767,14 @@ async function ngrx(options = {}) {
787
767
  enforceOperatorsRules = isPackageExists("@ngrx/operators"),
788
768
  overrides = {}
789
769
  } = typeof effects === "object" ? effects : {};
770
+ addOperatorsRules ||= enforceOperatorsRules;
771
+ ngrxOperatorsFiles.push(files);
790
772
  configs.push({
791
773
  name: "fabdeh/ngrx-effects/rules",
792
774
  files,
793
- extends: [...ngrxPlugin.configs.effects, ...enforceOperatorsRules ? ngrxPlugin.configs.operators : []],
775
+ plugins: { "@ngrx": ngrxPlugin },
794
776
  rules: {
777
+ ...ngrxPlugin.configs.effects.find((c) => c.name === "ngrx/effects")?.rules,
795
778
  "@typescript-eslint/naming-convention": [
796
779
  "error",
797
780
  ...namingConvention,
@@ -819,11 +802,14 @@ async function ngrx(options = {}) {
819
802
  { selector: "variableLike", format: ["camelCase"] },
820
803
  { selector: "memberLike", format: ["camelCase"], leadingUnderscore: "allow" }
821
804
  ];
805
+ addOperatorsRules ||= enforceOperatorsRules;
806
+ ngrxOperatorsFiles.push(files);
822
807
  configs.push({
823
808
  name: "fabdeh/ngrx-signals/rules",
824
809
  files,
825
- extends: [...ngrxPlugin.configs.signals, ...enforceOperatorsRules ? ngrxPlugin.configs.operators : []],
810
+ plugins: { "@ngrx": ngrxPlugin },
826
811
  rules: {
812
+ ...ngrxPlugin.configs.signals.find((c) => c.name === "ngrx/signals")?.rules,
827
813
  "@typescript-eslint/naming-convention": [
828
814
  "error",
829
815
  ...allowLeadingUnderscoreOnMemberLike(namingConvention),
@@ -839,6 +825,16 @@ async function ngrx(options = {}) {
839
825
  }
840
826
  });
841
827
  }
828
+ if (addOperatorsRules) {
829
+ configs.push({
830
+ name: "fabdeh/ngrx-operators/rules",
831
+ files: [...ngrxOperatorsFiles],
832
+ plugins: { "@ngrx": ngrxPlugin },
833
+ rules: {
834
+ ...ngrxPlugin.configs.operators.find((c) => c.name === "ngrx/operators")?.rules
835
+ }
836
+ });
837
+ }
842
838
  return tseslint.config(configs);
843
839
  }
844
840
 
@@ -892,7 +888,7 @@ const SORT_UNION_OR_INTERSECTION_GROUPS = [
892
888
  ];
893
889
 
894
890
  async function perfectionist() {
895
- const tsconfigRootDir = await findNearestPackageJsonName() === "@fabdeh/eslint-config" ? undefined : getWorkspaceRoot(process.cwd(), process.cwd());
891
+ const tsconfigRootDir = await findNearestPackageJsonName() === "@fabdeh/eslint-config" ? void 0 : getWorkspaceRoot(process.cwd(), process.cwd());
896
892
  return tseslint.config({
897
893
  name: "fabdeh/perfectionist/rules",
898
894
  plugins: {
@@ -1172,7 +1168,6 @@ async function stylistic(options = {}) {
1172
1168
  const config = stylisticPlugin.configs.customize({ ...stylisticOptions, flat: true });
1173
1169
  return tseslint.config({
1174
1170
  name: "fabdeh/stylistic/rules",
1175
- files: [GLOB_SRC],
1176
1171
  plugins: {
1177
1172
  "@stylistic": stylisticPlugin
1178
1173
  },
@@ -1278,29 +1273,37 @@ const memberOrdering = {
1278
1273
  };
1279
1274
 
1280
1275
  function typescript(options = {}) {
1281
- const { stylistic = true, parserOptions, overrides } = options;
1276
+ const { stylistic = true, parserOptions = {}, overrides = {} } = options;
1282
1277
  return tseslint.config(
1283
1278
  {
1284
1279
  name: "fabdeh/typescript/setup",
1280
+ files: [GLOB_TS],
1281
+ ignores: [`${GLOB_MARKDOWN}/**`],
1285
1282
  languageOptions: {
1286
- parserOptions: parserOptions ?? {
1287
- projectService: true,
1288
- tsconfigRootDir: getWorkspaceRoot(process.cwd(), process.cwd())
1283
+ parser: tseslint.parser,
1284
+ parserOptions: {
1285
+ sourceType: "module",
1286
+ projectService: {
1287
+ allowDefaultProject: ["*.js"],
1288
+ defaultProject: "tsconfig.json"
1289
+ },
1290
+ tsconfigRootDir: getWorkspaceRoot(process.cwd(), process.cwd()),
1291
+ ...parserOptions
1289
1292
  }
1290
1293
  }
1291
1294
  },
1292
1295
  {
1293
1296
  name: "fabdeh/typescript/rules",
1294
1297
  files: [GLOB_TS],
1298
+ ignores: [`${GLOB_MARKDOWN}/**`],
1295
1299
  plugins: {
1296
- "@typescript-eslint": tseslint.plugin
1300
+ "@typescript-eslint": tseslint.plugin,
1301
+ "unused-imports": unusedImports
1297
1302
  },
1298
- extends: [
1299
- eslint.configs.recommended,
1300
- ...tseslint.configs.strictTypeChecked,
1301
- ...stylistic ? tseslint.configs.stylisticTypeChecked : []
1302
- ],
1303
1303
  rules: {
1304
+ ...tseslint.configs.strictTypeChecked.find((c) => c.name === "typescript-eslint/eslint-recommended")?.rules,
1305
+ ...tseslint.configs.strictTypeChecked.find((c) => c.name === "typescript-eslint/strict-type-checked")?.rules,
1306
+ ...stylistic ? tseslint.configs.stylisticTypeChecked.find((c) => c.name === "typescript-eslint/stylistic-type-checked")?.rules : {},
1304
1307
  "@typescript-eslint/array-type": ["error", { default: "array" }],
1305
1308
  "@typescript-eslint/consistent-indexed-object-style": "error",
1306
1309
  "@typescript-eslint/consistent-type-assertions": [
@@ -1308,7 +1311,14 @@ function typescript(options = {}) {
1308
1311
  { assertionStyle: "as", objectLiteralTypeAssertions: "never" }
1309
1312
  ],
1310
1313
  "@typescript-eslint/consistent-type-definitions": ["error", "interface"],
1314
+ "@typescript-eslint/consistent-type-exports": "error",
1315
+ "@typescript-eslint/consistent-type-imports": ["error", {
1316
+ disallowTypeAnnotations: false,
1317
+ fixStyle: "separate-type-imports",
1318
+ prefer: "type-imports"
1319
+ }],
1311
1320
  "@typescript-eslint/dot-notation": "error",
1321
+ "@typescript-eslint/explicit-module-boundary-types": "error",
1312
1322
  "@typescript-eslint/explicit-function-return-type": [
1313
1323
  "error",
1314
1324
  {
@@ -1332,12 +1342,11 @@ function typescript(options = {}) {
1332
1342
  "@typescript-eslint/no-extraneous-class": ["error", { allowStaticOnly: true, allowWithDecorator: true }],
1333
1343
  "@typescript-eslint/no-invalid-void-type": "error",
1334
1344
  "@typescript-eslint/no-non-null-asserted-nullish-coalescing": "error",
1335
- "@typescript-eslint/no-require-imports": "error",
1336
1345
  "@typescript-eslint/only-throw-error": "error",
1337
1346
  "@typescript-eslint/no-unnecessary-condition": "error",
1338
1347
  "@typescript-eslint/no-unnecessary-type-arguments": "error",
1339
1348
  "@typescript-eslint/no-unsafe-unary-minus": "error",
1340
- "@typescript-eslint/parameter-properties": ["error", { prefer: "parameter-property" }],
1349
+ "@typescript-eslint/parameter-properties": "error",
1341
1350
  "@typescript-eslint/prefer-enum-initializers": "error",
1342
1351
  "@typescript-eslint/prefer-for-of": "error",
1343
1352
  "@typescript-eslint/prefer-function-type": "error",
@@ -1355,6 +1364,7 @@ function typescript(options = {}) {
1355
1364
  "@typescript-eslint/switch-exhaustiveness-check": "error",
1356
1365
  "@typescript-eslint/unbound-method": ["error", { ignoreStatic: true }],
1357
1366
  "@typescript-eslint/unified-signatures": "error",
1367
+ "@typescript-eslint/no-unused-vars": "off",
1358
1368
  ...overrides
1359
1369
  }
1360
1370
  }
@@ -1365,10 +1375,10 @@ function unicorn(options = {}) {
1365
1375
  return tseslint.config({
1366
1376
  name: "fabdeh/unicorn/rules",
1367
1377
  plugins: { unicorn: unicornPlugin },
1368
- files: [GLOB_SRC],
1369
1378
  rules: {
1370
- ...options.allRecommended ? unicornPlugin.configs["flat/recommended"].rules : {
1379
+ ...options.allRecommended ? unicornPlugin.configs.recommended.rules : {
1371
1380
  "unicorn/catch-error-name": "error",
1381
+ "unicorn/consistent-date-clone": "error",
1372
1382
  "unicorn/consistent-empty-array-spread": "error",
1373
1383
  "unicorn/consistent-existence-index-check": "error",
1374
1384
  "unicorn/consistent-function-scoping": ["error", { checkArrowFunctions: false }],
@@ -1376,7 +1386,7 @@ function unicorn(options = {}) {
1376
1386
  "unicorn/error-message": "error",
1377
1387
  "unicorn/escape-case": "error",
1378
1388
  "unicorn/explicit-length-check": "error",
1379
- "unicorn/filename-case": ["error", { case: "kebabCase" }],
1389
+ "unicorn/filename-case": ["error", { case: "kebabCase", ignore: [/^[A-Z0-9_-]+\.md$/] }],
1380
1390
  "unicorn/new-for-builtins": "error",
1381
1391
  "unicorn/no-abusive-eslint-disable": "error",
1382
1392
  "unicorn/no-anonymous-default-export": "error",
@@ -1390,7 +1400,7 @@ function unicorn(options = {}) {
1390
1400
  "unicorn/no-empty-file": "error",
1391
1401
  "unicorn/no-for-loop": "error",
1392
1402
  "unicorn/no-hex-escape": "error",
1393
- "unicorn/no-instanceof-array": "error",
1403
+ "unicorn/no-instanceof-builtins": "error",
1394
1404
  "unicorn/no-invalid-fetch-options": "error",
1395
1405
  "unicorn/no-invalid-remove-event-listener": "error",
1396
1406
  "unicorn/no-length-as-slice-end": "error",
@@ -1469,12 +1479,16 @@ async function vitest(options = {}) {
1469
1479
  } = options;
1470
1480
  const [vitestPlugin, jestDomPlugin, testingLibraryPlugin] = await Promise.all([
1471
1481
  interopDefault(import('@vitest/eslint-plugin')),
1472
- useJestDom ? interopDefault(import('eslint-plugin-jest-dom')) : Promise.resolve(undefined),
1473
- useTestingLibrary ? interopDefault(import('eslint-plugin-testing-library')) : Promise.resolve(undefined)
1482
+ useJestDom ? interopDefault(import('eslint-plugin-jest-dom')) : Promise.resolve(void 0),
1483
+ useTestingLibrary ? interopDefault(import('eslint-plugin-testing-library')) : Promise.resolve(void 0)
1474
1484
  ]);
1475
1485
  return tseslint.config({
1476
1486
  name: "fabdeh/vitest/rules",
1477
- plugins: { vitest: vitestPlugin },
1487
+ plugins: {
1488
+ vitest: vitestPlugin,
1489
+ ...jestDomPlugin ? { "jest-dom": jestDomPlugin } : {},
1490
+ ...testingLibraryPlugin ? { "testing-library": testingLibraryPlugin } : {}
1491
+ },
1478
1492
  languageOptions: {
1479
1493
  globals: {
1480
1494
  ...globals.node,
@@ -1488,12 +1502,10 @@ async function vitest(options = {}) {
1488
1502
  }
1489
1503
  },
1490
1504
  files: [...GLOB_TESTS],
1491
- extends: [
1492
- vitestPlugin.configs.recommended,
1493
- ...jestDomPlugin ? [jestDomPlugin.configs["flat/recommended"]] : [],
1494
- ...testingLibraryPlugin ? [testingLibraryPlugin.configs["flat/angular"]] : []
1495
- ],
1496
1505
  rules: {
1506
+ ...vitestPlugin.configs.recommended.rules,
1507
+ ...jestDomPlugin?.configs["flat/recommended"].rules,
1508
+ ...testingLibraryPlugin?.configs["flat/angular"].rules,
1497
1509
  "max-classes-per-file": "off",
1498
1510
  "max-lines": "off",
1499
1511
  "@typescript-eslint/consistent-type-assertions": "off",
@@ -1583,12 +1595,12 @@ function mergePrettierOptions(options, overrides = {}) {
1583
1595
  return {
1584
1596
  ...options,
1585
1597
  ...overrides,
1598
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
1586
1599
  plugins: [
1587
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
1588
1600
  ...overrides.plugins ?? [],
1589
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
1590
1601
  ...options.plugins ?? []
1591
1602
  ]
1603
+ /* eslint-enable @typescript-eslint/no-unsafe-assignment */
1592
1604
  };
1593
1605
  }
1594
1606
  async function formatters(options = {}, stylistic = {}) {
@@ -1606,8 +1618,8 @@ async function formatters(options = {}, stylistic = {}) {
1606
1618
  }
1607
1619
  await ensurePackages([
1608
1620
  "eslint-plugin-format",
1609
- options.markdown && options.slidev ? "prettier-plugin-slidev" : undefined,
1610
- options.xml || options.svg ? "@prettier/plugin-xml" : undefined
1621
+ options.markdown && options.slidev ? "prettier-plugin-slidev" : void 0,
1622
+ options.xml || options.svg ? "@prettier/plugin-xml" : void 0
1611
1623
  ]);
1612
1624
  const {
1613
1625
  indent,
@@ -1808,23 +1820,12 @@ async function createConfig(options = {}, ...userConfigs) {
1808
1820
  const {
1809
1821
  angular: enableAngular = isPackageExists("@angular/core"),
1810
1822
  gitignore: enableGitignore = true,
1811
- isInEditor = isInEditorEnv(),
1812
- jest: enableJest = isPackageExists("jest"),
1813
1823
  ngrx: enableNgrx = NGRX_PACKAGES.some((p) => isPackageExists(p)),
1814
1824
  tailwindcss: enableTailwind = isPackageExists("tailwindcss"),
1825
+ typescript: enableTypescript = isPackageExists("typescript"),
1815
1826
  unicorn: enableUnicorn = true,
1816
1827
  vitest: enableVitest = isPackageExists("vitest")
1817
1828
  } = options;
1818
- if (isInEditor) {
1819
- console.log(
1820
- "[@fabdeh/eslint-config] Detected running in editor, some rules are configured as warn or completely turned off."
1821
- );
1822
- }
1823
- if (enableJest && enableVitest) {
1824
- throw new Error(
1825
- '[@fabdeh/eslint-config] Detected that both "jest" and "vitest" are enabled which is not supported. Manually disable or uninstall one of them.'
1826
- );
1827
- }
1828
1829
  const stylisticOptions = options.stylistic === false ? false : typeof options.stylistic === "object" ? options.stylistic : {};
1829
1830
  const configs = [];
1830
1831
  if (enableGitignore) {
@@ -1842,49 +1843,50 @@ async function createConfig(options = {}, ...userConfigs) {
1842
1843
  );
1843
1844
  }
1844
1845
  }
1845
- const allowAngularDecorator = options.angular !== false;
1846
1846
  configs.push(
1847
1847
  ignores(options.ignores),
1848
- javascript({ isInEditor, overrides: options.javascript?.overrides, allowAngularDecorator }),
1849
- typescript({
1850
- stylistic: stylisticOptions,
1851
- parserOptions: options.typescript?.parserOptions,
1852
- overrides: options.typescript?.overrides
1853
- }),
1848
+ javascript({ overrides: options.javascript?.overrides }),
1854
1849
  comments(),
1855
- imports({ stylistic: stylisticOptions }),
1850
+ // TODO: add node rules ???
1856
1851
  jsdoc({ stylistic: stylisticOptions }),
1852
+ imports({ stylistic: stylisticOptions }),
1857
1853
  perfectionist()
1858
1854
  );
1859
1855
  if (enableUnicorn) {
1860
- configs.push(unicorn(typeof enableUnicorn === "object" ? enableUnicorn : {}));
1856
+ const unicornOptions = resolveSubOptions(options, "unicorn");
1857
+ configs.push(unicorn(unicornOptions));
1858
+ }
1859
+ if (enableTypescript) {
1860
+ const typescriptOptions = resolveSubOptions(options, "typescript");
1861
+ configs.push(typescript({
1862
+ ...typescriptOptions,
1863
+ stylistic: stylisticOptions
1864
+ }));
1861
1865
  }
1862
1866
  if (stylisticOptions) {
1863
1867
  configs.push(stylistic({ stylistic: stylisticOptions }));
1864
1868
  }
1865
1869
  if (enableAngular) {
1866
- configs.push(angular(typeof enableAngular === "object" ? enableAngular : {}));
1870
+ const angularOptions = resolveSubOptions(options, "angular");
1871
+ configs.push(angular(angularOptions));
1867
1872
  }
1868
1873
  if (enableNgrx) {
1869
- configs.push(ngrx(typeof enableNgrx === "object" ? enableNgrx : {}));
1870
- }
1871
- if (enableJest) {
1872
- configs.push(
1873
- jest(
1874
- typeof enableJest === "object" ? { ...enableJest, stylistic: stylisticOptions } : { stylistic: stylisticOptions }
1875
- )
1876
- );
1874
+ const ngrxOptions = resolveSubOptions(options, "ngrx");
1875
+ configs.push(ngrx(ngrxOptions));
1877
1876
  }
1878
1877
  if (enableVitest) {
1879
- configs.push(vitest(typeof enableVitest === "object" ? enableVitest : {}));
1878
+ const vitestOptions = resolveSubOptions(options, "vitest");
1879
+ configs.push(vitest(vitestOptions));
1880
1880
  }
1881
1881
  if (enableTailwind) {
1882
- configs.push(tailwindcss(typeof enableTailwind === "object" ? enableTailwind : {}));
1882
+ const tailwindcssOptions = resolveSubOptions(options, "tailwindcss");
1883
+ configs.push(tailwindcss(tailwindcssOptions));
1883
1884
  }
1884
1885
  if (options.jsonc ?? true) {
1886
+ const jsoncOptions = resolveSubOptions(options, "jsonc");
1885
1887
  configs.push(
1886
1888
  jsonc({
1887
- overrides: typeof options.jsonc === "object" ? options.jsonc.overrides : {},
1889
+ ...jsoncOptions,
1888
1890
  stylistic: stylisticOptions
1889
1891
  }),
1890
1892
  sortPackageJson(),
@@ -1892,21 +1894,22 @@ async function createConfig(options = {}, ...userConfigs) {
1892
1894
  );
1893
1895
  }
1894
1896
  if (options.yaml ?? true) {
1897
+ const yamlOptions = resolveSubOptions(options, "yaml");
1895
1898
  configs.push(yaml({
1896
- overrides: typeof options.yaml === "object" ? options.yaml.overrides : {},
1899
+ ...yamlOptions,
1897
1900
  stylistic: stylisticOptions
1898
1901
  }));
1899
1902
  }
1900
1903
  if (options.toml ?? true) {
1904
+ const tomlOptions = resolveSubOptions(options, "toml");
1901
1905
  configs.push(toml({
1902
- overrides: typeof options.toml === "object" ? options.toml.overrides : {},
1906
+ ...tomlOptions,
1903
1907
  stylistic: stylisticOptions
1904
1908
  }));
1905
1909
  }
1906
1910
  if (options.markdown ?? true) {
1907
- configs.push(markdown({
1908
- overrides: typeof options.markdown === "object" ? options.markdown.overrides : {}
1909
- }));
1911
+ const markdownOptions = resolveSubOptions(options, "markdown");
1912
+ configs.push(markdown(markdownOptions));
1910
1913
  }
1911
1914
  if (options.formatters) {
1912
1915
  configs.push(formatters(options.formatters, typeof stylisticOptions === "boolean" ? {} : stylisticOptions));
@@ -1923,4 +1926,4 @@ async function createConfig(options = {}, ...userConfigs) {
1923
1926
  return tseslint.config(...await Promise.all(configs), ...await Promise.all(userConfigs));
1924
1927
  }
1925
1928
 
1926
- export { GLOB_HTML, GLOB_JS, GLOB_SRC, GLOB_TESTS, GLOB_TS, STYLISTIC_CONFIG_DEFAULT, angular, comments, createConfig, ensurePackages, findNearestPackageJsonName, getWorkspaceRoot, ignores, imports, interopDefault, isInEditorEnv, isPackageInScope, javascript, jest, jsdoc, jsonc, markdown, ngrx, perfectionist, sortPackageJson, sortTsConfig, stylistic, tailwindcss, toArray, toml, typescript, unicorn, vitest, yaml };
1929
+ export { GLOB_HTML, GLOB_JS, GLOB_SRC, GLOB_TESTS, GLOB_TS, STYLISTIC_CONFIG_DEFAULT, angular, comments, createConfig, ensurePackages, findNearestPackageJsonName, getWorkspaceRoot, ignores, imports, interopDefault, isPackageInScope, javascript, jsdoc, jsonc, markdown, ngrx, perfectionist, resolveSubOptions, sortPackageJson, sortTsConfig, stylistic, tailwindcss, toml, typescript, unicorn, vitest, yaml };