@fabdeh/eslint-config 0.4.0 → 0.6.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/README.md CHANGED
@@ -37,9 +37,9 @@ And create an `eslint.config.mjs` in you project root:
37
37
 
38
38
  ```js
39
39
  // eslint.config.mjs
40
- import { createConfig } from '@fabdeh/eslint-config';
40
+ import { defineConfig } from '@fabdeh/eslint-config';
41
41
 
42
- export default createConfig();
42
+ export default defineConfig();
43
43
  ```
44
44
 
45
45
  ### Add script for package.json
@@ -108,22 +108,22 @@ Add the following settings to your `.vscode/settings.json`:
108
108
 
109
109
  Since the beginning, we used [ESLint Flat config](https://eslint.org/docs/latest/use/configure/configuration-files-new). It provides much better organization and composition.
110
110
 
111
- Normally you only need to import the `createConfig` function:
111
+ Normally you only need to import the `defineConfig` function:
112
112
 
113
113
  ```js
114
114
  // eslint.config.js
115
- import { createConfig } from '@fabdeh/eslint-config';
115
+ import { defineConfig } from '@fabdeh/eslint-config';
116
116
 
117
- export default createConfig();
117
+ export default defineConfig();
118
118
  ```
119
119
 
120
120
  And that's it! Or you can configure each integration individually, for example:
121
121
 
122
122
  ```js
123
123
  // eslint.config.js
124
- import { createConfig } from '@fabdeh/eslint-config';
124
+ import { defineConfig } from '@fabdeh/eslint-config';
125
125
 
126
- export default createConfig({
126
+ export default defineConfig({
127
127
  // Enable stylistic formatting rules
128
128
  // stylistic: true,
129
129
 
@@ -148,13 +148,13 @@ export default createConfig({
148
148
  });
149
149
  ```
150
150
 
151
- The `createConfig` factory function also accepts any number of arbitrary custom config overrides:
151
+ The `defineConfig` factory function also accepts any number of arbitrary custom config overrides:
152
152
 
153
153
  ```js
154
154
  // eslint.config.js
155
- import { createConfig } from '@fabdeh/eslint-config';
155
+ import { defineConfig } from '@fabdeh/eslint-config';
156
156
 
157
- export default createConfig(
157
+ export default defineConfig(
158
158
  {
159
159
  // Configure the fabdeh's config
160
160
  },
@@ -225,9 +225,9 @@ If you want to override the rules, you need to specify the file extension:
225
225
 
226
226
  ```js
227
227
  // eslint.config.js
228
- import { createConfig } from '@fabdeh/eslint-config';
228
+ import { defineConfig } from '@fabdeh/eslint-config';
229
229
 
230
- export default createConfig(
230
+ export default defineConfig(
231
231
  {
232
232
  typescript: true,
233
233
  vitest: true
@@ -252,9 +252,9 @@ We also provide the `overrides` option in each integration to make it easier:
252
252
 
253
253
  ```js
254
254
  // eslint.config.js
255
- import { createConfig } from '@fabdeh/eslint-config';
255
+ import { defineConfig } from '@fabdeh/eslint-config';
256
256
 
257
- export default createConfig({
257
+ export default defineConfig({
258
258
  typescript: {
259
259
  overrides: {
260
260
  '@typescript-eslint/consisten-type-definitions': ['error', 'interface'],
@@ -292,9 +292,9 @@ You can explicitly enable/disable TypeScript integration manually:
292
292
 
293
293
  ```js
294
294
  // eslint.config.js
295
- import { createConfig } from '@fabdeh/eslint-config';
295
+ import { defineConfig } from '@fabdeh/eslint-config';
296
296
 
297
- export default createConfig({
297
+ export default defineConfig({
298
298
  typescript: true,
299
299
  });
300
300
  ```
@@ -313,9 +313,9 @@ You can enable these rules as follow:
313
313
 
314
314
  ```js
315
315
  // eslint.config.js
316
- import { createConfig } from '@fabdeh/eslint-config';
316
+ import { defineConfig } from '@fabdeh/eslint-config';
317
317
 
318
- export default createConfig({
318
+ export default defineConfig({
319
319
  typescript: {
320
320
  enableErasableSyntaxOnly: true,
321
321
  },
@@ -335,9 +335,9 @@ Angular support is detected automatically by checking if `@angular/core` is inst
335
335
 
336
336
  ```js
337
337
  // eslint.config.js
338
- import { createConfig } from '@fabdeh/eslint-config';
338
+ import { defineConfig } from '@fabdeh/eslint-config';
339
339
 
340
- export default createConfig({
340
+ export default defineConfig({
341
341
  angular: true,
342
342
  });
343
343
  ```
@@ -355,9 +355,9 @@ As the Angular integration is can be explicitly enabled/disabled:
355
355
 
356
356
  ```js
357
357
  // eslint.config.js
358
- import { createConfig } from '@fabdeh/eslint-config';
358
+ import { defineConfig } from '@fabdeh/eslint-config';
359
359
 
360
- export default createConfig({
360
+ export default defineConfig({
361
361
  ngrx: true,
362
362
  });
363
363
  ```
@@ -370,9 +370,9 @@ The vitest integration is detected automatically by checking if `vitest` is inst
370
370
 
371
371
  ```js
372
372
  // eslint.config.js
373
- import { createConfig } from '@fabdeh/eslint-config';
373
+ import { defineConfig } from '@fabdeh/eslint-config';
374
374
 
375
- export default createConfig({
375
+ export default defineConfig({
376
376
  vitest: true,
377
377
  });
378
378
  ```
@@ -387,9 +387,9 @@ Use external formatters to format files that ESLint cannot handle yet (`.css`, `
387
387
 
388
388
  ```js
389
389
  // eslint.config.js
390
- import { createConfig } from '@fabdeh/eslint-config';
390
+ import { defineConfig } from '@fabdeh/eslint-config';
391
391
 
392
- export default createConfig({
392
+ export default defineConfig({
393
393
  formatters: {
394
394
  /**
395
395
  * Format CSS, LESS, SCSS files
package/dist/index.d.mts CHANGED
@@ -1,7 +1,8 @@
1
- import type { ConfigArray, ConfigWithExtends } from 'typescript-eslint';
2
- import type { StylisticCustomizeOptions } from '@stylistic/eslint-plugin';
3
- import type { TSESLint } from '@typescript-eslint/utils';
4
- import type { FlatGitignoreOptions } from 'eslint-config-flat-gitignore';
1
+ import { ConfigArray, ConfigWithExtends } from 'typescript-eslint';
2
+ import { StylisticCustomizeOptions } from '@stylistic/eslint-plugin';
3
+ import { TSESLint } from '@typescript-eslint/utils';
4
+ import { FlatGitignoreOptions } from 'eslint-config-flat-gitignore';
5
+ import { Attributes, Callees, Variables, Tags } from 'eslint-plugin-better-tailwindcss/api/types';
5
6
 
6
7
  /**
7
8
  * Vendor types from Prettier so we don't rely on the dependency.
@@ -136,11 +137,6 @@ interface VendoredPrettierOptionsRequired {
136
137
  xmlWhitespaceSensitivity: 'ignore' | 'preserve' | 'strict';
137
138
  }
138
139
 
139
-
140
-
141
-
142
-
143
-
144
140
  /**
145
141
  * A type that can be awaited. Promise<T> or T.
146
142
  */
@@ -229,6 +225,22 @@ interface AngularOptions {
229
225
  * HTML-specific linting rule overrides.
230
226
  */
231
227
  htmlOverrides?: TSESLint.FlatConfig.Config['rules'];
228
+ /**
229
+ * A class name pattern that allows service using `@Injectable` decorator to not use `providedIn` option.
230
+ */
231
+ ignoreClassNamePatternForInjectableProvidedIn?: string;
232
+ /**
233
+ * Defines whether the `styles`, `styleUrl`, `styleUrls` must be used.
234
+ *
235
+ * @default 'string'
236
+ */
237
+ componentStylesMode?: 'array' | 'string';
238
+ /**
239
+ * Indicates whether all component should use `OnPush` change strategy or not.
240
+ *
241
+ * @default true
242
+ */
243
+ preferOnPushOnly?: boolean;
232
244
  }
233
245
  /**
234
246
  * Interface representing configuration options for enforcing NgRx operators rules.
@@ -302,12 +314,55 @@ interface RegExpOptions {
302
314
  */
303
315
  level?: 'error' | 'warn';
304
316
  }
305
- interface TailwindcssParserPerGlobOptions {
317
+ /**
318
+ * Interface for better-tailwindcss specific options.
319
+ */
320
+ interface TailwindCssOptions {
306
321
  /**
307
322
  * Provides a specific ESLint parser per glob pattern.
308
- * This is only needed if you want to lint tailwindcss without using Angular and TypeScript.
309
323
  */
310
324
  parsers?: Record<string, TSESLint.FlatConfig.Parser>;
325
+ /**
326
+ * Use all better-tailwindcss rules or only recommended ones.
327
+ *
328
+ * @default false
329
+ */
330
+ enableAllRules?: boolean;
331
+ /**
332
+ * The path to the entry file of the CSS-based tailwind config (eg: `src/global.css`). If not specified, the plugin will fall back to the default configuration.
333
+ * The tailwind config is used to determine the sorting order.
334
+ */
335
+ entryPoint?: string;
336
+ /**
337
+ * The path to the `tailwind.config.js` file. If not specified, the plugin will try to find it automatically or falls back to the default configuration.
338
+ * The tailwind config is used to determine the sorting order.
339
+ */
340
+ tailwindConfig?: string;
341
+ /**
342
+ * The name of the attribute that contains the tailwind classes.
343
+ *
344
+ * @default Name for `"class"` and strings Matcher for `"class", "className"`.
345
+ */
346
+ attributes?: Attributes;
347
+ /**
348
+ * List of function names which arguments should also get linted.
349
+ *
350
+ * @default Matchers for `"cc", "clb", "clsx", "cn", "cnb", "ctl", "cva", "cx", "dcnb", "objstr", "tv", "twJoin", "twMerge"`.
351
+ */
352
+ callees?: Callees;
353
+ /**
354
+ * List of variable names whose initializer should also get linted.
355
+ *
356
+ * @default strings Matcher for `"className", "classNames", "classes", "style", "styles"`.
357
+ */
358
+ variables?: Variables;
359
+ /**
360
+ * List of template literal tag names whose content should get linted.
361
+ * When using the `tags` option, it is recommended to use the strings Matcher for your tag names. This will ensure that nested expressions get linted correctly.
362
+ *
363
+ * @default None
364
+ */
365
+ tags?: Tags;
311
366
  }
312
367
  /**
313
368
  * Options for creating an ESLint configuration.
@@ -383,7 +438,7 @@ interface CreateConfigOptions {
383
438
  *
384
439
  * @default false
385
440
  */
386
- tailwindcss?: boolean | (FilesOptions & OverridesOptions) | (OverridesOptions & TailwindcssParserPerGlobOptions);
441
+ tailwindcss?: boolean | (FilesOptions & OverridesOptions & TailwindCssOptions);
387
442
  /**
388
443
  * Enable JSONC support.
389
444
  *
@@ -423,9 +478,6 @@ interface CreateConfigOptions {
423
478
  formatters?: FormattersOptions | boolean;
424
479
  }
425
480
 
426
-
427
-
428
-
429
481
  /**
430
482
  * Generates an ESLint configuration for Angular projects.
431
483
  *
@@ -438,8 +490,6 @@ interface CreateConfigOptions {
438
490
  */
439
491
  declare function angular(options?: AngularOptions): Promise<ConfigArray>;
440
492
 
441
-
442
-
443
493
  /**
444
494
  * Generates a configuration array for ESLint comments rules.
445
495
  *
@@ -451,8 +501,6 @@ declare function angular(options?: AngularOptions): Promise<ConfigArray>;
451
501
  */
452
502
  declare function comments(): ConfigArray;
453
503
 
454
-
455
-
456
504
  /**
457
505
  * Generates a configuration array for ESLint with default and user-defined ignore patterns.
458
506
  *
@@ -461,9 +509,6 @@ declare function comments(): ConfigArray;
461
509
  */
462
510
  declare function ignores(userIgnores?: string[]): ConfigArray;
463
511
 
464
-
465
-
466
-
467
512
  /**
468
513
  * Generates an ESLint configuration array for import rules.
469
514
  *
@@ -473,9 +518,6 @@ declare function ignores(userIgnores?: string[]): ConfigArray;
473
518
  */
474
519
  declare function imports(options?: StylisticOptions): ConfigArray;
475
520
 
476
-
477
-
478
-
479
521
  /**
480
522
  * Generates a configuration array for JavaScript projects with ESLint.
481
523
  *
@@ -494,9 +536,6 @@ declare function imports(options?: StylisticOptions): ConfigArray;
494
536
  */
495
537
  declare function javascript(options?: OverridesOptions): ConfigArray;
496
538
 
497
-
498
-
499
-
500
539
  /**
501
540
  * Generates a configuration array for JSDoc rules.
502
541
  *
@@ -506,13 +545,6 @@ declare function javascript(options?: OverridesOptions): ConfigArray;
506
545
  */
507
546
  declare function jsdoc(options?: StylisticOptions): ConfigArray;
508
547
 
509
-
510
-
511
-
512
-
513
-
514
-
515
-
516
548
  /**
517
549
  * Generates an ESLint configuration for JSONC files.
518
550
  *
@@ -533,11 +565,6 @@ declare function jsdoc(options?: StylisticOptions): ConfigArray;
533
565
  */
534
566
  declare function jsonc(options?: FilesOptions & OverridesOptions & StylisticOptions): Promise<ConfigArray>;
535
567
 
536
-
537
-
538
-
539
-
540
-
541
568
  /**
542
569
  * Configures ESLint for Markdown files.
543
570
  *
@@ -560,9 +587,6 @@ declare function jsonc(options?: FilesOptions & OverridesOptions & StylisticOpti
560
587
  */
561
588
  declare function markdown(options?: FilesOptions & OverridesOptions): Promise<ConfigArray>;
562
589
 
563
-
564
-
565
-
566
590
  /**
567
591
  * Generates an ESLint configuration array for NgRx based on the provided options.
568
592
  *
@@ -586,8 +610,6 @@ declare function markdown(options?: FilesOptions & OverridesOptions): Promise<Co
586
610
  */
587
611
  declare function ngrx(options?: NgrxOptions): Promise<ConfigArray>;
588
612
 
589
-
590
-
591
613
  /**
592
614
  * Generates a configuration array for the "perfectionist" ESLint plugin.
593
615
  *
@@ -601,11 +623,6 @@ declare function ngrx(options?: NgrxOptions): Promise<ConfigArray>;
601
623
  */
602
624
  declare function perfectionist(): Promise<ConfigArray>;
603
625
 
604
-
605
-
606
-
607
-
608
-
609
626
  /**
610
627
  * Configure the recommended regexp rules.
611
628
  *
@@ -614,8 +631,6 @@ declare function perfectionist(): Promise<ConfigArray>;
614
631
  */
615
632
  declare function regexp(options?: OverridesOptions & RegExpOptions): ConfigArray;
616
633
 
617
-
618
-
619
634
  /**
620
635
  * Configures ESLint rules for sorting keys and array values in `package.json` files.
621
636
  *
@@ -641,11 +656,6 @@ declare function sortPackageJson(): ConfigArray;
641
656
  */
642
657
  declare function sortTsConfig(): ConfigArray;
643
658
 
644
-
645
-
646
-
647
-
648
-
649
659
  declare const STYLISTIC_CONFIG_DEFAULT: StylisticConfig;
650
660
  /**
651
661
  * Generates a stylistic ESLint configuration.
@@ -657,33 +667,20 @@ declare const STYLISTIC_CONFIG_DEFAULT: StylisticConfig;
657
667
  */
658
668
  declare function stylistic(options?: StylisticOptions): Promise<ConfigArray>;
659
669
 
660
-
661
-
662
-
663
-
664
-
665
-
666
-
667
670
  /**
668
- * Generates an ESLint configuration array for Tailwind CSS.
671
+ * Generates an ESLint configuration array for Tailwind CSS using better-tailwindcss plugin.
669
672
  *
670
673
  * @param [options] - Optional overrides for the configuration.
671
674
  * @returns A promise that resolves to the ESLint configuration array.
672
675
  * @example
673
676
  * const config = await tailwindcss({
677
+ * enableAllRules: true,
674
678
  * overrides: {
675
- * 'tailwindcss/classnames-order': 'warn',
679
+ * 'better-tailwindcss/recommended': 'warn',
676
680
  * },
677
681
  * });
678
682
  */
679
- declare function tailwindcss(options?: (FilesOptions & OverridesOptions) | (OverridesOptions & TailwindcssParserPerGlobOptions)): Promise<ConfigArray>;
680
-
681
-
682
-
683
-
684
-
685
-
686
-
683
+ declare function tailwindcss(options?: (FilesOptions & OverridesOptions & TailwindCssOptions)): Promise<ConfigArray>;
687
684
 
688
685
  /**
689
686
  * Generates an ESLint configuration for TOML files.
@@ -697,15 +694,6 @@ declare function tailwindcss(options?: (FilesOptions & OverridesOptions) | (Over
697
694
  */
698
695
  declare function toml(options?: FilesOptions & OverridesOptions & StylisticOptions): Promise<ConfigArray>;
699
696
 
700
-
701
-
702
-
703
-
704
-
705
-
706
-
707
-
708
-
709
697
  /**
710
698
  * Generates a TypeScript ESLint configuration.
711
699
  *
@@ -717,9 +705,6 @@ declare function toml(options?: FilesOptions & OverridesOptions & StylisticOptio
717
705
  */
718
706
  declare function typescript(options?: OverridesOptions & StylisticOptions & TypeScriptErasableSyntaxOnlyOptions & TypeScriptParserOptions): Promise<ConfigArray>;
719
707
 
720
-
721
-
722
-
723
708
  /**
724
709
  * Generates a configuration array for the Unicorn plugin with the specified options.
725
710
  *
@@ -728,11 +713,6 @@ declare function typescript(options?: OverridesOptions & StylisticOptions & Type
728
713
  */
729
714
  declare function unicorn(options?: UnicornOptions): ConfigArray;
730
715
 
731
-
732
-
733
-
734
-
735
-
736
716
  /**
737
717
  * Configures and returns an ESLint configuration array for Vitest.
738
718
  *
@@ -752,13 +732,6 @@ declare function unicorn(options?: UnicornOptions): ConfigArray;
752
732
  */
753
733
  declare function vitest(options?: OverridesOptions & TestingOptions): Promise<ConfigArray>;
754
734
 
755
-
756
-
757
-
758
-
759
-
760
-
761
-
762
735
  /**
763
736
  * Generates an ESLint configuration for YAML files.
764
737
  *
@@ -772,13 +745,6 @@ declare function vitest(options?: OverridesOptions & TestingOptions): Promise<Co
772
745
  */
773
746
  declare function yaml(options?: FilesOptions & OverridesOptions & StylisticOptions): Promise<ConfigArray>;
774
747
 
775
-
776
-
777
-
778
-
779
-
780
-
781
-
782
748
  /**
783
749
  * Creates an ESLint configuration array based on the provided options and user configurations.
784
750
  *
@@ -813,10 +779,6 @@ declare const GLOB_HTML = "**/*.htm?(l)";
813
779
  */
814
780
  declare const GLOB_TESTS: string[];
815
781
 
816
-
817
-
818
-
819
-
820
782
  /**
821
783
  * A utility function to handle the default export of a module.
822
784
  *
@@ -843,6 +805,13 @@ declare function interopDefault<T>(m: Awaitable<T>): Promise<T extends {
843
805
  * @returns The root directory of the workspace.
844
806
  */
845
807
  declare function getWorkspaceRoot(dir: string, candidateRoot: string): string;
808
+ /**
809
+ * Returns the most probable filename for the TsConfig.
810
+ *
811
+ * @param dir - The directory into which the tsconfig.json is located.
812
+ * @returns The tsconfig.*.json file
813
+ */
814
+ declare function getTsConfigFileName(dir: string): string | undefined;
846
815
  /**
847
816
  * Checks if a package is within the specified scope.
848
817
  *
@@ -880,5 +849,5 @@ type ResolvedOptions<T> = T extends boolean ? never : NonNullable<T>;
880
849
  */
881
850
  declare function resolveSubOptions<K extends keyof CreateConfigOptions>(options: CreateConfigOptions, key: K): ResolvedOptions<CreateConfigOptions[K]>;
882
851
 
883
- export { GLOB_HTML, GLOB_JS, GLOB_SRC, GLOB_TESTS, GLOB_TS, STYLISTIC_CONFIG_DEFAULT, angular, comments, defineConfig, ensurePackages, findNearestPackageJsonName, getWorkspaceRoot, ignores, imports, interopDefault, isPackageInScope, javascript, jsdoc, jsonc, markdown, ngrx, perfectionist, regexp, resolveSubOptions, sortPackageJson, sortTsConfig, stylistic, tailwindcss, toml, typescript, unicorn, vitest, yaml };
884
- export type { AngularOptions, Awaitable, CreateConfigOptions, FilesOptions, FormattersOptions, NgrxOperators, NgrxOptions, OverridesOptions, RegExpOptions, ResolvedOptions, StylisticConfig, StylisticOptions, TailwindcssParserPerGlobOptions, TestingOptions, TypeScriptErasableSyntaxOnlyOptions, TypeScriptParserOptions, UnicornOptions };
852
+ export { GLOB_HTML, GLOB_JS, GLOB_SRC, GLOB_TESTS, GLOB_TS, STYLISTIC_CONFIG_DEFAULT, angular, comments, defineConfig, ensurePackages, findNearestPackageJsonName, getTsConfigFileName, getWorkspaceRoot, ignores, imports, interopDefault, isPackageInScope, javascript, jsdoc, jsonc, markdown, ngrx, perfectionist, regexp, resolveSubOptions, sortPackageJson, sortTsConfig, stylistic, tailwindcss, toml, typescript, unicorn, vitest, yaml };
853
+ export type { AngularOptions, Awaitable, CreateConfigOptions, FilesOptions, FormattersOptions, NgrxOperators, NgrxOptions, OverridesOptions, RegExpOptions, ResolvedOptions, StylisticConfig, StylisticOptions, TailwindCssOptions, TestingOptions, TypeScriptErasableSyntaxOnlyOptions, TypeScriptParserOptions, UnicornOptions };
package/dist/index.mjs CHANGED
@@ -4,7 +4,6 @@ import { statSync } from 'node:fs';
4
4
  import { readFile } from 'node:fs/promises';
5
5
  import { resolve, dirname, join } from 'node:path';
6
6
  import process from 'node:process';
7
- import { fileURLToPath } from 'node:url';
8
7
  import eslint from '@eslint/js';
9
8
  import preferArrowFunctions from 'eslint-plugin-prefer-arrow-functions';
10
9
  import unusedImports from 'eslint-plugin-unused-imports';
@@ -75,7 +74,7 @@ const GLOB_EXCLUDE = [
75
74
  "**/prettier-types.ts"
76
75
  ];
77
76
 
78
- const SCOPE_URL = fileURLToPath(new URL(".", import.meta.url));
77
+ const SCOPE_URL = import.meta.dirname;
79
78
  const IS_CWD_IN_SCOPE = isPackageExists("@fabdeh/eslint-config");
80
79
  async function interopDefault(m) {
81
80
  const resolved = await m;
@@ -89,8 +88,8 @@ function fileExists(path) {
89
88
  }
90
89
  }
91
90
  function getWorkspaceRoot(dir, candidateRoot) {
92
- if (process.env.NX_WORKSPACE_ROOT_PATH) {
93
- return process.env.NX_WORKSPACE_ROOT_PATH;
91
+ if (process.env.NX_WORKSPACE_ROOT) {
92
+ return process.env.NX_WORKSPACE_ROOT;
94
93
  }
95
94
  if (dirname(dir) === dir) {
96
95
  return candidateRoot;
@@ -104,6 +103,9 @@ function getWorkspaceRoot(dir, candidateRoot) {
104
103
  return getWorkspaceRoot(dirname(dir), candidateRoot);
105
104
  }
106
105
  }
106
+ function getTsConfigFileName(dir) {
107
+ return ["tsconfig.base.json", "tsconfig.json"].map((filename) => ({ filename, path: join(dir, filename) })).filter(({ path }) => fileExists(path)).map(({ filename }) => filename).at(0);
108
+ }
107
109
  function isPackageInScope(name) {
108
110
  return isPackageExists(name, { paths: [SCOPE_URL] });
109
111
  }
@@ -137,15 +139,16 @@ async function findNearestPackageJsonName(directoryPath = resolve()) {
137
139
  }
138
140
  }
139
141
  function resolveSubOptions(options, key) {
140
- if (typeof options[key] === "boolean") {
141
- return {};
142
+ const option = options[key];
143
+ if (typeof option === "boolean") {
144
+ throw new TypeError(`Option ${String(key)} should not be boolean.`);
142
145
  }
143
- return options[key] ?? {};
146
+ return option ?? {};
144
147
  }
145
148
 
146
149
  async function angular(options = {}) {
147
150
  const angularEslint = await interopDefault(import('angular-eslint'));
148
- const { enableAccessibilityRules = true, tsOverrides = {}, htmlOverrides = {}, prefix = "app" } = options;
151
+ const { enableAccessibilityRules = true, tsOverrides = {}, htmlOverrides = {}, prefix = "app", ignoreClassNamePatternForInjectableProvidedIn: ignoreClassNamePattern, componentStylesMode = "string", preferOnPushOnly = true } = options;
149
152
  return tseslint.config(
150
153
  {
151
154
  name: "fabdeh/angular/rules",
@@ -193,6 +196,7 @@ async function angular(options = {}) {
193
196
  style: "kebab-case"
194
197
  }
195
198
  ],
199
+ "@angular-eslint/consistent-component-styles": ["error", componentStylesMode],
196
200
  "@angular-eslint/contextual-decorator": "error",
197
201
  "@angular-eslint/directive-selector": [
198
202
  "error",
@@ -210,12 +214,18 @@ async function angular(options = {}) {
210
214
  "@angular-eslint/no-lifecycle-call": "error",
211
215
  "@angular-eslint/no-pipe-impure": "error",
212
216
  "@angular-eslint/no-queries-metadata-property": "error",
217
+ "@angular-eslint/no-uncalled-signals": "error",
218
+ "@angular-eslint/prefer-output-emitter-ref": "error",
213
219
  "@angular-eslint/prefer-output-readonly": "error",
214
220
  "@angular-eslint/prefer-signals": "error",
221
+ ...preferOnPushOnly ? { "@angular-eslint/prefer-on-push-component-change-detection": "error" } : {},
215
222
  "@angular-eslint/relative-url-prefix": "error",
223
+ "@angular-eslint/require-lifecycle-on-prototype": "error",
224
+ "@angular-eslint/sort-keys-in-type-decorator": "error",
216
225
  "@angular-eslint/sort-lifecycle-methods": "error",
217
226
  "@angular-eslint/use-component-selector": "error",
218
227
  "@angular-eslint/use-component-view-encapsulation": "error",
228
+ "@angular-eslint/use-injectable-provided-in": ignoreClassNamePattern ? ["error", { ignoreClassNamePattern }] : "error",
219
229
  "@angular-eslint/use-lifecycle-interface": "error",
220
230
  ...tsOverrides
221
231
  }
@@ -901,7 +911,7 @@ const SORT_UNION_OR_INTERSECTION_GROUPS = [
901
911
  ];
902
912
 
903
913
  async function perfectionist() {
904
- const tsconfigRootDir = await findNearestPackageJsonName() === "@fabdeh/eslint-config" ? void 0 : getWorkspaceRoot(process.cwd(), process.cwd());
914
+ const rootDir = await findNearestPackageJsonName() === "@fabdeh/eslint-config" ? void 0 : getWorkspaceRoot(process.cwd(), process.cwd());
905
915
  return tseslint.config({
906
916
  name: "fabdeh/perfectionist/rules",
907
917
  files: [GLOB_SRC],
@@ -914,7 +924,10 @@ async function perfectionist() {
914
924
  "error",
915
925
  {
916
926
  groups: SORT_IMPORT_GROUPS,
917
- tsconfigRootDir,
927
+ tsconfig: rootDir ? {
928
+ rootDir,
929
+ filename: getTsConfigFileName(rootDir)
930
+ } : void 0,
918
931
  order: "asc",
919
932
  type: "natural",
920
933
  newlinesBetween: "never"
@@ -1219,42 +1232,43 @@ async function stylistic(options = {}) {
1219
1232
  });
1220
1233
  }
1221
1234
 
1222
- function isTailwindcssParserPerGlobOptions(options) {
1223
- return "parsers" in options && options.parsers !== void 0;
1224
- }
1225
1235
  async function tailwindcss(options = {}) {
1226
- await ensurePackages(["eslint-plugin-tailwindcss"]);
1227
- const tailwindcssPlugin = await interopDefault(import('eslint-plugin-tailwindcss'));
1236
+ await ensurePackages(["eslint-plugin-better-tailwindcss"]);
1237
+ const betterTailwindcssPlugin = await interopDefault(import('eslint-plugin-better-tailwindcss'));
1238
+ const { files: filesGlob, overrides, enableAllRules, parsers, ...settings } = options;
1228
1239
  let files;
1229
1240
  let parserConfigs;
1230
- const { overrides = {} } = options;
1231
- if (isTailwindcssParserPerGlobOptions(options)) {
1232
- const parsers = options.parsers ?? {};
1241
+ if (parsers) {
1233
1242
  files = { files: [...new Set(Object.keys(parsers))] };
1234
1243
  parserConfigs = Object.entries(parsers).map(([glob, parser], index) => ({
1235
1244
  name: `fabdeh/tailwindcss/parser-${index + 1}`,
1236
- file: [glob],
1245
+ files: [glob],
1237
1246
  languageOptions: {
1238
1247
  parser
1239
1248
  }
1240
1249
  }));
1241
1250
  } else {
1242
- files = { files: options.files ?? [GLOB_SRC, GLOB_HTML] };
1251
+ files = { files: filesGlob ?? [GLOB_SRC, GLOB_HTML] };
1243
1252
  parserConfigs = [];
1244
1253
  }
1245
1254
  return tseslint.config(
1246
1255
  ...parserConfigs,
1247
1256
  {
1248
- name: "fabdeh/tailwindcss/rules",
1249
- plugins: { tailwindcss: tailwindcssPlugin },
1257
+ name: "@fabdeh/tailwindcss/rules",
1250
1258
  ...files,
1259
+ plugins: {
1260
+ "better-tailwindcss": betterTailwindcssPlugin
1261
+ },
1262
+ settings: {
1263
+ "better-tailwindcss": settings
1264
+ },
1251
1265
  rules: {
1252
- "tailwindcss/classnames-order": "error",
1253
- "tailwindcss/enforces-negative-arbitrary-values": "error",
1254
- "tailwindcss/enforces-shorthand": "error",
1255
- "tailwindcss/no-contradicting-classname": "error",
1256
- "tailwindcss/no-unnecessary-arbitrary-value": "error",
1257
- ...overrides
1266
+ ...betterTailwindcssPlugin.configs.recommended.rules,
1267
+ ...enableAllRules ? {
1268
+ "better-tailwindcss/enforce-consistent-variable-syntax": "warn",
1269
+ "better-tailwindcss/no-conflicting-classes": "error",
1270
+ "better-tailwindcss/no-restricted-classes": "error"
1271
+ } : {}
1258
1272
  }
1259
1273
  }
1260
1274
  );
@@ -1344,6 +1358,8 @@ async function typescript(options = {}) {
1344
1358
  erasableSyntaxOnlyPlugin = erasableSyntaxOnly;
1345
1359
  erasableSyntaxOnlyRules = erasableSyntaxOnly.configs.recommended.rules;
1346
1360
  }
1361
+ const tsconfigRootDir = getWorkspaceRoot(process.cwd(), process.cwd());
1362
+ const defaultProject = getTsConfigFileName(tsconfigRootDir);
1347
1363
  return tseslint.config(
1348
1364
  {
1349
1365
  name: "fabdeh/typescript/setup",
@@ -1355,9 +1371,9 @@ async function typescript(options = {}) {
1355
1371
  sourceType: "module",
1356
1372
  projectService: {
1357
1373
  allowDefaultProject: ["*.js"],
1358
- defaultProject: "tsconfig.json"
1374
+ defaultProject
1359
1375
  },
1360
- tsconfigRootDir: getWorkspaceRoot(process.cwd(), process.cwd()),
1376
+ tsconfigRootDir,
1361
1377
  ...parserOptions
1362
1378
  }
1363
1379
  }
@@ -1465,7 +1481,6 @@ function unicorn(options = {}) {
1465
1481
  "unicorn/no-anonymous-default-export": "error",
1466
1482
  "unicorn/no-array-for-each": "error",
1467
1483
  "unicorn/no-array-method-this-argument": "error",
1468
- "unicorn/no-array-push-push": "error",
1469
1484
  "unicorn/no-array-reduce": "error",
1470
1485
  "unicorn/no-await-expression-member": "error",
1471
1486
  "unicorn/no-await-in-promise-methods": "error",
@@ -1476,7 +1491,6 @@ function unicorn(options = {}) {
1476
1491
  "unicorn/no-instanceof-builtins": "error",
1477
1492
  "unicorn/no-invalid-fetch-options": "error",
1478
1493
  "unicorn/no-invalid-remove-event-listener": "error",
1479
- "unicorn/no-length-as-slice-end": "error",
1480
1494
  "unicorn/no-lonely-if": "error",
1481
1495
  "unicorn/no-magic-array-flat-depth": "error",
1482
1496
  "unicorn/no-negated-condition": "error",
@@ -1490,6 +1504,9 @@ function unicorn(options = {}) {
1490
1504
  "unicorn/no-this-assignment": "error",
1491
1505
  "unicorn/no-typeof-undefined": "error",
1492
1506
  "unicorn/no-unnecessary-await": "error",
1507
+ "unicorn/no-unnecessary-array-flat-depth": "error",
1508
+ "unicorn/no-unnecessary-array-splice-count": "error",
1509
+ "unicorn/no-unnecessary-slice-end": "error",
1493
1510
  "unicorn/no-unused-properties": "warn",
1494
1511
  "unicorn/no-useless-fallback-in-spread": "error",
1495
1512
  "unicorn/no-useless-length-check": "error",
@@ -1510,6 +1527,7 @@ function unicorn(options = {}) {
1510
1527
  "unicorn/prefer-date-now": "error",
1511
1528
  "unicorn/prefer-default-parameters": "error",
1512
1529
  "unicorn/prefer-export-from": "error",
1530
+ "unicorn/prefer-import-meta-properties": "error",
1513
1531
  "unicorn/prefer-includes": "error",
1514
1532
  "unicorn/prefer-keyboard-event-key": "error",
1515
1533
  "unicorn/prefer-logical-operator-over-ternary": "error",
@@ -1525,6 +1543,7 @@ function unicorn(options = {}) {
1525
1543
  "unicorn/prefer-regexp-test": "error",
1526
1544
  "unicorn/prefer-set-has": "error",
1527
1545
  "unicorn/prefer-set-size": "error",
1546
+ "unicorn/prefer-single-call": "error",
1528
1547
  "unicorn/prefer-spread": "error",
1529
1548
  "unicorn/prefer-string-raw": "error",
1530
1549
  "unicorn/prefer-string-replace-all": "error",
@@ -2016,4 +2035,4 @@ async function defineConfig(options = {}, ...userConfigs) {
2016
2035
  return tseslint.config(...await Promise.all(configs), ...await Promise.all(userConfigs));
2017
2036
  }
2018
2037
 
2019
- export { GLOB_HTML, GLOB_JS, GLOB_SRC, GLOB_TESTS, GLOB_TS, STYLISTIC_CONFIG_DEFAULT, angular, comments, defineConfig, ensurePackages, findNearestPackageJsonName, getWorkspaceRoot, ignores, imports, interopDefault, isPackageInScope, javascript, jsdoc, jsonc, markdown, ngrx, perfectionist, regexp, resolveSubOptions, sortPackageJson, sortTsConfig, stylistic, tailwindcss, toml, typescript, unicorn, vitest, yaml };
2038
+ export { GLOB_HTML, GLOB_JS, GLOB_SRC, GLOB_TESTS, GLOB_TS, STYLISTIC_CONFIG_DEFAULT, angular, comments, defineConfig, ensurePackages, findNearestPackageJsonName, getTsConfigFileName, getWorkspaceRoot, ignores, imports, interopDefault, isPackageInScope, javascript, jsdoc, jsonc, markdown, ngrx, perfectionist, regexp, resolveSubOptions, sortPackageJson, sortTsConfig, stylistic, tailwindcss, toml, typescript, unicorn, vitest, yaml };
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@fabdeh/eslint-config",
3
3
  "type": "module",
4
- "version": "0.4.0",
5
- "packageManager": "pnpm@10.6.5+sha512.cdf928fca20832cd59ec53826492b7dc25dc524d4370b6b4adbf65803d32efaa6c1c88147c0ae4e8d579a6c9eec715757b50d4fa35eea179d868eada4ed043af",
4
+ "version": "0.6.0",
5
+ "packageManager": "pnpm@10.12.1+sha512.f0dda8580f0ee9481c5c79a1d927b9164f2c478e90992ad268bbb2465a736984391d6333d2c327913578b2804af33474ca554ba29c04a8b13060a717675ae3ac",
6
6
  "description": "My personal eslint config preset",
7
7
  "author": {
8
8
  "name": "Fabien Dehopré",
@@ -37,7 +37,7 @@
37
37
  "dist"
38
38
  ],
39
39
  "engines": {
40
- "node": ">=18.18"
40
+ "node": "^20.11.1 || ^22.11.0"
41
41
  },
42
42
  "scripts": {
43
43
  "build": "unbuild",
@@ -55,22 +55,22 @@
55
55
  "peerDependencies": {
56
56
  "@prettier/plugin-xml": "^3.4.1",
57
57
  "eslint": "^9.21.0",
58
+ "eslint-plugin-better-tailwindcss": "^3.4.1",
58
59
  "eslint-plugin-erasable-syntax-only": "^0.3.0",
59
60
  "eslint-plugin-format": "^1.0.1",
60
- "eslint-plugin-tailwindcss": "^3.18.0",
61
61
  "prettier-plugin-slidev": "^1.0.5"
62
62
  },
63
63
  "peerDependenciesMeta": {
64
64
  "@prettier/plugin-xml": {
65
65
  "optional": true
66
66
  },
67
- "eslint-plugin-erasable-syntax-only": {
67
+ "eslint-plugin-better-tailwindcss": {
68
68
  "optional": true
69
69
  },
70
- "eslint-plugin-format": {
70
+ "eslint-plugin-erasable-syntax-only": {
71
71
  "optional": true
72
72
  },
73
- "eslint-plugin-tailwindcss": {
73
+ "eslint-plugin-format": {
74
74
  "optional": true
75
75
  },
76
76
  "prettier-plugin-slidev": {
@@ -78,63 +78,63 @@
78
78
  }
79
79
  },
80
80
  "dependencies": {
81
- "@antfu/install-pkg": "^1.0.0",
82
- "@clack/prompts": "^0.10.0",
83
- "@eslint-community/eslint-plugin-eslint-comments": "^4.4.1",
84
- "@eslint/js": "^9.22.0",
85
- "@eslint/markdown": "^6.3.0",
86
- "@ngrx/eslint-plugin": "^19.0.1",
87
- "@stylistic/eslint-plugin": "^4.2.0",
88
- "@typescript-eslint/eslint-plugin": "^8.26.1",
89
- "@typescript-eslint/parser": "^8.26.1",
90
- "@typescript-eslint/utils": "^8.26.1",
91
- "@vitest/eslint-plugin": "^1.1.38",
92
- "angular-eslint": "^19.2.1",
81
+ "@antfu/install-pkg": "^1.1.0",
82
+ "@clack/prompts": "^0.10.1",
83
+ "@eslint-community/eslint-plugin-eslint-comments": "^4.5.0",
84
+ "@eslint/js": "^9.28.0",
85
+ "@eslint/markdown": "^6.5.0",
86
+ "@ngrx/eslint-plugin": "^19.2.1",
87
+ "@stylistic/eslint-plugin": "^4.4.1",
88
+ "@typescript-eslint/eslint-plugin": "^8.34.0",
89
+ "@typescript-eslint/parser": "^8.34.0",
90
+ "@typescript-eslint/utils": "^8.34.0",
91
+ "@vitest/eslint-plugin": "^1.2.2",
92
+ "angular-eslint": "^20.0.0",
93
93
  "eslint-config-flat-gitignore": "^2.1.0",
94
- "eslint-flat-config-utils": "^2.0.1",
94
+ "eslint-flat-config-utils": "^2.1.0",
95
95
  "eslint-merge-processors": "^2.0.0",
96
- "eslint-plugin-import-x": "^4.9.0",
96
+ "eslint-plugin-import-x": "^4.15.1",
97
97
  "eslint-plugin-jest-dom": "^5.5.0",
98
- "eslint-plugin-jsdoc": "^50.6.8",
99
- "eslint-plugin-jsonc": "^2.19.1",
100
- "eslint-plugin-perfectionist": "^4.10.1",
98
+ "eslint-plugin-jsdoc": "^50.8.0",
99
+ "eslint-plugin-jsonc": "^2.20.1",
100
+ "eslint-plugin-perfectionist": "^4.14.0",
101
101
  "eslint-plugin-prefer-arrow-functions": "^3.6.2",
102
- "eslint-plugin-regexp": "^2.7.0",
103
- "eslint-plugin-testing-library": "^7.1.1",
102
+ "eslint-plugin-regexp": "^2.9.0",
103
+ "eslint-plugin-testing-library": "^7.5.2",
104
104
  "eslint-plugin-toml": "^0.12.0",
105
- "eslint-plugin-unicorn": "^57.0.0",
105
+ "eslint-plugin-unicorn": "^59.0.1",
106
106
  "eslint-plugin-unused-imports": "^4.1.4",
107
- "eslint-plugin-yml": "^1.17.0",
108
- "globals": "^16.0.0",
107
+ "eslint-plugin-yml": "^1.18.0",
108
+ "globals": "^16.2.0",
109
109
  "jsonc-eslint-parser": "^2.4.0",
110
110
  "local-pkg": "^1.1.1",
111
111
  "toml-eslint-parser": "^0.10.0",
112
- "typescript-eslint": "^8.26.1",
112
+ "typescript-eslint": "^8.34.0",
113
113
  "yaml-eslint-parser": "^1.3.0"
114
114
  },
115
115
  "devDependencies": {
116
- "@angular/core": "^19.2.2",
117
- "@commitlint/cli": "^19.8.0",
118
- "@commitlint/config-conventional": "^19.8.0",
119
- "@commitlint/cz-commitlint": "^19.8.0",
116
+ "@angular/core": "^20.0.3",
117
+ "@commitlint/cli": "^19.8.1",
118
+ "@commitlint/config-conventional": "^19.8.1",
119
+ "@commitlint/cz-commitlint": "^19.8.1",
120
120
  "@fabdeh/eslint-config": "workspace:*",
121
- "@ngrx/effects": "^19.0.1",
122
- "@ngrx/operators": "^19.0.1",
123
- "@ngrx/signals": "^19.0.1",
124
- "@ngrx/store": "^19.0.1",
121
+ "@ngrx/effects": "^19.2.1",
122
+ "@ngrx/operators": "^19.2.1",
123
+ "@ngrx/signals": "^19.2.1",
124
+ "@ngrx/store": "^19.2.1",
125
125
  "@prettier/plugin-xml": "^3.4.1",
126
- "@testing-library/angular": "^17.3.6",
126
+ "@testing-library/angular": "^17.4.0",
127
127
  "@testing-library/jest-dom": "^6.6.3",
128
128
  "@types/fs-extra": "^11.0.4",
129
- "@types/node": "^22.13.10",
130
- "bumpp": "^10.1.0",
131
- "changelogithub": "^13.13.0",
129
+ "@types/node": "^22.15.31",
130
+ "bumpp": "^10.1.1",
131
+ "changelogithub": "^13.15.0",
132
132
  "commitizen": "^4.3.1",
133
- "eslint": "^9.22.0",
134
- "eslint-plugin-erasable-syntax-only": "^0.3.0",
133
+ "eslint": "^9.28.0",
134
+ "eslint-plugin-better-tailwindcss": "^3.4.0",
135
+ "eslint-plugin-erasable-syntax-only": "^0.3.1",
135
136
  "eslint-plugin-format": "^1.0.1",
136
- "eslint-plugin-tailwindcss": "^3.18.0",
137
- "execa": "^9.5.2",
137
+ "execa": "^9.6.0",
138
138
  "fast-glob": "^3.3.3",
139
139
  "fs-extra": "^11.3.0",
140
140
  "inquirer": "^9.3.7",
@@ -142,11 +142,11 @@
142
142
  "jiti": "^2.4.2",
143
143
  "nano-staged": "^0.8.0",
144
144
  "prettier-plugin-slidev": "^1.0.5",
145
- "simple-git-hooks": "^2.11.1",
146
- "tailwindcss": "^3.4.17",
147
- "typescript": "^5.8.2",
145
+ "simple-git-hooks": "^2.13.0",
146
+ "tailwindcss": "^4.1.10",
147
+ "typescript": "^5.8.3",
148
148
  "unbuild": "^3.5.0",
149
- "vitest": "^3.0.9"
149
+ "vitest": "^3.2.3"
150
150
  },
151
151
  "pnpm": {
152
152
  "overrides": {