@fabdeh/eslint-config 0.3.0 → 0.5.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
@@ -16,7 +16,7 @@
16
16
  - Single quotes, no semi
17
17
  - Using [ESLint Stylistic](https://github.com/eslint-stylistic/eslint-stylistic)
18
18
  - Respects `.gitignore` by default
19
- - Requires ESLint v9.5.0+
19
+ - Requires ESLint v9.21.0+
20
20
 
21
21
  > [!WARNING]
22
22
  > Please keep in mind that this is **_a personal config_** with a lot opinions. Changes might not always be pleased by everyone and every use cases.
@@ -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
@@ -224,6 +224,22 @@ interface AngularOptions {
224
224
  * HTML-specific linting rule overrides.
225
225
  */
226
226
  htmlOverrides?: TSESLint.FlatConfig.Config['rules'];
227
+ /**
228
+ * A class name pattern that allows service using `@Injectable` decorator to not use `providedIn` option.
229
+ */
230
+ ignoreClassNamePatternForInjectableProvidedIn?: string;
231
+ /**
232
+ * Defines whether the `styles`, `styleUrl`, `styleUrls` must be used.
233
+ *
234
+ * @default 'string'
235
+ */
236
+ componentStylesMode?: 'array' | 'string';
237
+ /**
238
+ * Indicates whether all component should use `OnPush` change strategy or not.
239
+ *
240
+ * @default true
241
+ */
242
+ preferOnPushOnly?: boolean;
227
243
  }
228
244
  /**
229
245
  * Interface representing configuration options for enforcing NgRx operators rules.
@@ -336,6 +352,12 @@ interface CreateConfigOptions {
336
352
  * @default true
337
353
  */
338
354
  stylistic?: boolean | (OverridesOptions & StylisticConfig);
355
+ /**
356
+ * Enable or disable JSDoc rules.
357
+ *
358
+ * @default true
359
+ */
360
+ jsdoc?: boolean;
339
361
  /**
340
362
  * Enable regexp rules.
341
363
  *
@@ -689,7 +711,7 @@ declare function yaml(options?: FilesOptions & OverridesOptions & StylisticOptio
689
711
  * const config = await createConfig({ vitest: true, typescript: { parserOptions: { project: './tsconfig.json' } } });
690
712
  * ```
691
713
  */
692
- declare function createConfig(options?: ConfigWithExtends & CreateConfigOptions, ...userConfigs: Awaitable<ConfigWithExtends | ConfigWithExtends[]>[]): Promise<ConfigArray>;
714
+ declare function defineConfig(options?: ConfigWithExtends & CreateConfigOptions, ...userConfigs: Awaitable<ConfigWithExtends | ConfigWithExtends[]>[]): Promise<ConfigArray>;
693
715
 
694
716
  /**
695
717
  * A glob pattern that matches JavaScript and TypeScript source files (including JSX/TSX).
@@ -738,6 +760,13 @@ declare function interopDefault<T>(m: Awaitable<T>): Promise<T extends {
738
760
  * @returns The root directory of the workspace.
739
761
  */
740
762
  declare function getWorkspaceRoot(dir: string, candidateRoot: string): string;
763
+ /**
764
+ * Returns the most probable filename for the TsConfig.
765
+ *
766
+ * @param dir - The directory into which the tsconfig.json is located.
767
+ * @returns The tsconfig.*.json file
768
+ */
769
+ declare function getTsConfigFileName(dir: string): string | undefined;
741
770
  /**
742
771
  * Checks if a package is within the specified scope.
743
772
  *
@@ -775,4 +804,5 @@ type ResolvedOptions<T> = T extends boolean ? never : NonNullable<T>;
775
804
  */
776
805
  declare function resolveSubOptions<K extends keyof CreateConfigOptions>(options: CreateConfigOptions, key: K): ResolvedOptions<CreateConfigOptions[K]>;
777
806
 
778
- export { type AngularOptions, type Awaitable, type CreateConfigOptions, type FilesOptions, type FormattersOptions, GLOB_HTML, GLOB_JS, GLOB_SRC, GLOB_TESTS, GLOB_TS, type NgrxOperators, type NgrxOptions, type OverridesOptions, type RegExpOptions, type ResolvedOptions, STYLISTIC_CONFIG_DEFAULT, type StylisticConfig, type StylisticOptions, type TailwindcssParserPerGlobOptions, type TestingOptions, type TypeScriptErasableSyntaxOnlyOptions, type TypeScriptParserOptions, type UnicornOptions, angular, comments, createConfig, ensurePackages, findNearestPackageJsonName, getWorkspaceRoot, ignores, imports, interopDefault, isPackageInScope, javascript, jsdoc, jsonc, markdown, ngrx, perfectionist, regexp, resolveSubOptions, sortPackageJson, sortTsConfig, stylistic, tailwindcss, toml, typescript, unicorn, vitest, yaml };
807
+ 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 };
808
+ export type { AngularOptions, Awaitable, CreateConfigOptions, FilesOptions, FormattersOptions, NgrxOperators, NgrxOptions, OverridesOptions, RegExpOptions, ResolvedOptions, StylisticConfig, StylisticOptions, TailwindcssParserPerGlobOptions, TestingOptions, TypeScriptErasableSyntaxOnlyOptions, TypeScriptParserOptions, UnicornOptions };
package/dist/index.mjs CHANGED
@@ -4,15 +4,14 @@ 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';
11
10
  import globals from 'globals';
12
11
  import eslintComments from '@eslint-community/eslint-plugin-eslint-comments';
13
- import jsdocPlugin from 'eslint-plugin-jsdoc';
14
12
  import * as importX from 'eslint-plugin-import-x';
15
13
  import perfectionistPlugin from 'eslint-plugin-perfectionist';
14
+ import jsdocPlugin from 'eslint-plugin-jsdoc';
16
15
  import unicornPlugin from 'eslint-plugin-unicorn';
17
16
  import { configs } from 'eslint-plugin-regexp';
18
17
  import { mergeProcessors, processorPassThrough } from 'eslint-merge-processors';
@@ -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
  }
@@ -145,7 +147,7 @@ function resolveSubOptions(options, key) {
145
147
 
146
148
  async function angular(options = {}) {
147
149
  const angularEslint = await interopDefault(import('angular-eslint'));
148
- const { enableAccessibilityRules = true, tsOverrides = {}, htmlOverrides = {}, prefix = "app" } = options;
150
+ const { enableAccessibilityRules = true, tsOverrides = {}, htmlOverrides = {}, prefix = "app", ignoreClassNamePatternForInjectableProvidedIn: ignoreClassNamePattern, componentStylesMode = "string", preferOnPushOnly = true } = options;
149
151
  return tseslint.config(
150
152
  {
151
153
  name: "fabdeh/angular/rules",
@@ -193,6 +195,7 @@ async function angular(options = {}) {
193
195
  style: "kebab-case"
194
196
  }
195
197
  ],
198
+ "@angular-eslint/consistent-component-styles": ["error", componentStylesMode],
196
199
  "@angular-eslint/contextual-decorator": "error",
197
200
  "@angular-eslint/directive-selector": [
198
201
  "error",
@@ -210,12 +213,18 @@ async function angular(options = {}) {
210
213
  "@angular-eslint/no-lifecycle-call": "error",
211
214
  "@angular-eslint/no-pipe-impure": "error",
212
215
  "@angular-eslint/no-queries-metadata-property": "error",
216
+ "@angular-eslint/no-uncalled-signals": "error",
217
+ "@angular-eslint/prefer-output-emitter-ref": "error",
213
218
  "@angular-eslint/prefer-output-readonly": "error",
214
219
  "@angular-eslint/prefer-signals": "error",
220
+ ...preferOnPushOnly ? { "@angular-eslint/prefer-on-push-component-change-detection": "error" } : {},
215
221
  "@angular-eslint/relative-url-prefix": "error",
222
+ "@angular-eslint/require-lifecycle-on-prototype": "error",
223
+ "@angular-eslint/sort-keys-in-type-decorator": "error",
216
224
  "@angular-eslint/sort-lifecycle-methods": "error",
217
225
  "@angular-eslint/use-component-selector": "error",
218
226
  "@angular-eslint/use-component-view-encapsulation": "error",
227
+ "@angular-eslint/use-injectable-provided-in": ignoreClassNamePattern ? ["error", { ignoreClassNamePattern }] : "error",
219
228
  "@angular-eslint/use-lifecycle-interface": "error",
220
229
  ...tsOverrides
221
230
  }
@@ -470,6 +479,54 @@ function javascript(options = {}) {
470
479
  );
471
480
  }
472
481
 
482
+ function getJsDocRules(level, stylistic, mode) {
483
+ return {
484
+ ...mode === "both" || mode === "jsOnly" ? {
485
+ "jsdoc/check-access": level,
486
+ "jsdoc/check-param-names": level,
487
+ "jsdoc/check-property-names": level,
488
+ "jsdoc/check-tag-names": level,
489
+ "jsdoc/check-types": level,
490
+ "jsdoc/check-values": level,
491
+ "jsdoc/empty-tags": level,
492
+ "jsdoc/implements-on-classes": level,
493
+ "jsdoc/no-defaults": level,
494
+ "jsdoc/no-undefined-types": level,
495
+ "jsdoc/require-jsdoc": level,
496
+ "jsdoc/require-param": level,
497
+ "jsdoc/require-param-description": level,
498
+ "jsdoc/require-param-name": level,
499
+ "jsdoc/require-param-type": level,
500
+ "jsdoc/require-property": level,
501
+ "jsdoc/require-property-description": level,
502
+ "jsdoc/require-property-name": level,
503
+ "jsdoc/require-property-type": level,
504
+ "jsdoc/require-returns": level,
505
+ "jsdoc/require-returns-check": level,
506
+ "jsdoc/require-returns-description": level,
507
+ "jsdoc/require-returns-type": level,
508
+ "jsdoc/require-yields": level,
509
+ "jsdoc/require-yields-check": level,
510
+ "jsdoc/valid-types": level,
511
+ ...stylistic ? {
512
+ "jsdoc/check-alignment": level,
513
+ "jsdoc/multiline-blocks": level,
514
+ "jsdoc/no-multi-asterisks": level,
515
+ "jsdoc/require-asterisk-prefix": level,
516
+ "jsdoc/require-hyphen-before-param-description": level,
517
+ "jsdoc/tag-lines": [level, "never", { startLines: 1 }]
518
+ } : {}
519
+ } : {},
520
+ ...mode === "both" || mode === "tsOnly" ? {
521
+ "jsdoc/check-tag-names": [level, { typed: true }],
522
+ "jsdoc/no-types": level,
523
+ "jsdoc/no-undefined-types": "off",
524
+ "jsdoc/require-param-type": "off",
525
+ "jsdoc/require-property-type": "off",
526
+ "jsdoc/require-returns-type": "off"
527
+ } : {}
528
+ };
529
+ }
473
530
  function jsdoc(options = {}) {
474
531
  const { stylistic = true } = options;
475
532
  return tseslint.config(
@@ -477,52 +534,12 @@ function jsdoc(options = {}) {
477
534
  name: "fabdeh/jsdoc/rules",
478
535
  files: [GLOB_SRC],
479
536
  plugins: { jsdoc: jsdocPlugin },
480
- rules: {
481
- "jsdoc/check-access": "warn",
482
- "jsdoc/check-param-names": "warn",
483
- "jsdoc/check-property-names": "warn",
484
- "jsdoc/check-tag-names": "warn",
485
- "jsdoc/check-types": "warn",
486
- "jsdoc/check-values": "warn",
487
- "jsdoc/empty-tags": "warn",
488
- "jsdoc/implements-on-classes": "warn",
489
- "jsdoc/no-defaults": "warn",
490
- "jsdoc/no-multi-asterisks": "warn",
491
- "jsdoc/no-undefined-types": "warn",
492
- "jsdoc/require-jsdoc": "warn",
493
- "jsdoc/require-param": "warn",
494
- "jsdoc/require-param-description": "warn",
495
- "jsdoc/require-param-name": "warn",
496
- "jsdoc/require-param-type": "warn",
497
- "jsdoc/require-property": "warn",
498
- "jsdoc/require-property-description": "warn",
499
- "jsdoc/require-property-name": "warn",
500
- "jsdoc/require-property-type": "warn",
501
- "jsdoc/require-returns": "warn",
502
- "jsdoc/require-returns-check": "warn",
503
- "jsdoc/require-returns-description": "warn",
504
- "jsdoc/require-returns-type": "warn",
505
- "jsdoc/require-yields": "warn",
506
- "jsdoc/require-yields-check": "warn",
507
- "jsdoc/tag-lines": ["warn", "never", { startLines: 1 }],
508
- "jsdoc/valid-types": "warn",
509
- ...stylistic ? {
510
- "jsdoc/check-alignment": "warn",
511
- "jsdoc/multiline-blocks": "warn"
512
- } : {}
513
- }
537
+ rules: getJsDocRules("warn", !!stylistic, "jsOnly")
514
538
  },
515
539
  {
516
540
  name: "fabdeh/jsdoc/ts-only/rules",
517
541
  files: [GLOB_TS],
518
- rules: {
519
- "jsdoc/check-tag-names": ["warn", { typed: true }],
520
- "jsdoc/no-types": "warn",
521
- "jsdoc/no-undefined-types": "off",
522
- "jsdoc/require-param-type": "off",
523
- "jsdoc/require-property-type": "off",
524
- "jsdoc/require-returns-type": "off"
525
- }
542
+ rules: getJsDocRules("warn", !!stylistic, "tsOnly")
526
543
  }
527
544
  );
528
545
  }
@@ -893,7 +910,7 @@ const SORT_UNION_OR_INTERSECTION_GROUPS = [
893
910
  ];
894
911
 
895
912
  async function perfectionist() {
896
- const tsconfigRootDir = await findNearestPackageJsonName() === "@fabdeh/eslint-config" ? void 0 : getWorkspaceRoot(process.cwd(), process.cwd());
913
+ const rootDir = await findNearestPackageJsonName() === "@fabdeh/eslint-config" ? void 0 : getWorkspaceRoot(process.cwd(), process.cwd());
897
914
  return tseslint.config({
898
915
  name: "fabdeh/perfectionist/rules",
899
916
  files: [GLOB_SRC],
@@ -906,7 +923,10 @@ async function perfectionist() {
906
923
  "error",
907
924
  {
908
925
  groups: SORT_IMPORT_GROUPS,
909
- tsconfigRootDir,
926
+ tsconfig: rootDir ? {
927
+ rootDir,
928
+ filename: getTsConfigFileName(rootDir)
929
+ } : void 0,
910
930
  order: "asc",
911
931
  type: "natural",
912
932
  newlinesBetween: "never"
@@ -1336,6 +1356,8 @@ async function typescript(options = {}) {
1336
1356
  erasableSyntaxOnlyPlugin = erasableSyntaxOnly;
1337
1357
  erasableSyntaxOnlyRules = erasableSyntaxOnly.configs.recommended.rules;
1338
1358
  }
1359
+ const tsconfigRootDir = getWorkspaceRoot(process.cwd(), process.cwd());
1360
+ const defaultProject = getTsConfigFileName(tsconfigRootDir);
1339
1361
  return tseslint.config(
1340
1362
  {
1341
1363
  name: "fabdeh/typescript/setup",
@@ -1347,9 +1369,9 @@ async function typescript(options = {}) {
1347
1369
  sourceType: "module",
1348
1370
  projectService: {
1349
1371
  allowDefaultProject: ["*.js"],
1350
- defaultProject: "tsconfig.json"
1372
+ defaultProject
1351
1373
  },
1352
- tsconfigRootDir: getWorkspaceRoot(process.cwd(), process.cwd()),
1374
+ tsconfigRootDir,
1353
1375
  ...parserOptions
1354
1376
  }
1355
1377
  }
@@ -1457,7 +1479,6 @@ function unicorn(options = {}) {
1457
1479
  "unicorn/no-anonymous-default-export": "error",
1458
1480
  "unicorn/no-array-for-each": "error",
1459
1481
  "unicorn/no-array-method-this-argument": "error",
1460
- "unicorn/no-array-push-push": "error",
1461
1482
  "unicorn/no-array-reduce": "error",
1462
1483
  "unicorn/no-await-expression-member": "error",
1463
1484
  "unicorn/no-await-in-promise-methods": "error",
@@ -1468,7 +1489,6 @@ function unicorn(options = {}) {
1468
1489
  "unicorn/no-instanceof-builtins": "error",
1469
1490
  "unicorn/no-invalid-fetch-options": "error",
1470
1491
  "unicorn/no-invalid-remove-event-listener": "error",
1471
- "unicorn/no-length-as-slice-end": "error",
1472
1492
  "unicorn/no-lonely-if": "error",
1473
1493
  "unicorn/no-magic-array-flat-depth": "error",
1474
1494
  "unicorn/no-negated-condition": "error",
@@ -1482,6 +1502,9 @@ function unicorn(options = {}) {
1482
1502
  "unicorn/no-this-assignment": "error",
1483
1503
  "unicorn/no-typeof-undefined": "error",
1484
1504
  "unicorn/no-unnecessary-await": "error",
1505
+ "unicorn/no-unnecessary-array-flat-depth": "error",
1506
+ "unicorn/no-unnecessary-array-splice-count": "error",
1507
+ "unicorn/no-unnecessary-slice-end": "error",
1485
1508
  "unicorn/no-unused-properties": "warn",
1486
1509
  "unicorn/no-useless-fallback-in-spread": "error",
1487
1510
  "unicorn/no-useless-length-check": "error",
@@ -1502,6 +1525,7 @@ function unicorn(options = {}) {
1502
1525
  "unicorn/prefer-date-now": "error",
1503
1526
  "unicorn/prefer-default-parameters": "error",
1504
1527
  "unicorn/prefer-export-from": "error",
1528
+ "unicorn/prefer-import-meta-properties": "error",
1505
1529
  "unicorn/prefer-includes": "error",
1506
1530
  "unicorn/prefer-keyboard-event-key": "error",
1507
1531
  "unicorn/prefer-logical-operator-over-ternary": "error",
@@ -1517,6 +1541,7 @@ function unicorn(options = {}) {
1517
1541
  "unicorn/prefer-regexp-test": "error",
1518
1542
  "unicorn/prefer-set-has": "error",
1519
1543
  "unicorn/prefer-set-size": "error",
1544
+ "unicorn/prefer-single-call": "error",
1520
1545
  "unicorn/prefer-spread": "error",
1521
1546
  "unicorn/prefer-string-raw": "error",
1522
1547
  "unicorn/prefer-string-replace-all": "error",
@@ -1596,6 +1621,7 @@ async function vitest(options = {}) {
1596
1621
  "vitest/prefer-todo": "error",
1597
1622
  "vitest/prefer-vi-mocked": "error",
1598
1623
  "vitest/require-top-level-describe": "error",
1624
+ ...getJsDocRules("off", true, "both"),
1599
1625
  ...overrides
1600
1626
  }
1601
1627
  });
@@ -1883,10 +1909,11 @@ const FLAT_CONFIG_PROPS = [
1883
1909
  "extends"
1884
1910
  ];
1885
1911
  const NGRX_PACKAGES = ["@ngrx/store", "@ngrx/effects", "@ngrx/signals", "@ngrx/operators"];
1886
- async function createConfig(options = {}, ...userConfigs) {
1912
+ async function defineConfig(options = {}, ...userConfigs) {
1887
1913
  const {
1888
1914
  angular: enableAngular = isPackageExists("@angular/core"),
1889
1915
  gitignore: enableGitignore = true,
1916
+ jsdoc: enableJsdoc = true,
1890
1917
  ngrx: enableNgrx = NGRX_PACKAGES.some((p) => isPackageExists(p)),
1891
1918
  regexp: enableRegexp = true,
1892
1919
  tailwindcss: enableTailwind = false,
@@ -1894,6 +1921,9 @@ async function createConfig(options = {}, ...userConfigs) {
1894
1921
  unicorn: enableUnicorn = true,
1895
1922
  vitest: enableVitest = isPackageExists("vitest")
1896
1923
  } = options;
1924
+ if (enableNgrx && !enableAngular) {
1925
+ throw new Error("NgRx rules can only be enabled if Angular rules are also enabled.");
1926
+ }
1897
1927
  const stylisticOptions = options.stylistic === false ? false : typeof options.stylistic === "object" ? options.stylistic : {};
1898
1928
  const configs = [];
1899
1929
  if (enableGitignore) {
@@ -1915,11 +1945,12 @@ async function createConfig(options = {}, ...userConfigs) {
1915
1945
  ignores(options.ignores),
1916
1946
  javascript({ overrides: options.javascript?.overrides }),
1917
1947
  comments(),
1918
- // TODO: add node rules ???
1919
- jsdoc({ stylistic: stylisticOptions }),
1920
1948
  imports({ stylistic: stylisticOptions }),
1921
1949
  perfectionist()
1922
1950
  );
1951
+ if (enableJsdoc) {
1952
+ configs.push(jsdoc({ stylistic: stylisticOptions }));
1953
+ }
1923
1954
  if (enableUnicorn) {
1924
1955
  const unicornOptions = resolveSubOptions(options, "unicorn");
1925
1956
  configs.push(unicorn(unicornOptions));
@@ -2002,4 +2033,4 @@ async function createConfig(options = {}, ...userConfigs) {
2002
2033
  return tseslint.config(...await Promise.all(configs), ...await Promise.all(userConfigs));
2003
2034
  }
2004
2035
 
2005
- 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, regexp, resolveSubOptions, sortPackageJson, sortTsConfig, stylistic, tailwindcss, toml, typescript, unicorn, vitest, yaml };
2036
+ 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.3.0",
5
- "packageManager": "pnpm@10.4.1",
4
+ "version": "0.5.0",
5
+ "packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39",
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",
@@ -54,8 +54,8 @@
54
54
  },
55
55
  "peerDependencies": {
56
56
  "@prettier/plugin-xml": "^3.4.1",
57
- "eslint": "^9.20.0",
58
- "eslint-plugin-erasable-syntax-only": "^0.2.1",
57
+ "eslint": "^9.21.0",
58
+ "eslint-plugin-erasable-syntax-only": "^0.3.0",
59
59
  "eslint-plugin-format": "^1.0.1",
60
60
  "eslint-plugin-tailwindcss": "^3.18.0",
61
61
  "prettier-plugin-slidev": "^1.0.5"
@@ -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.21.0",
85
- "@eslint/markdown": "^6.2.2",
86
- "@ngrx/eslint-plugin": "^19.0.1",
87
- "@stylistic/eslint-plugin": "^4.0.1",
88
- "@typescript-eslint/eslint-plugin": "^8.24.1",
89
- "@typescript-eslint/parser": "^8.24.1",
90
- "@typescript-eslint/utils": "^8.24.1",
91
- "@vitest/eslint-plugin": "^1.1.31",
92
- "angular-eslint": "^19.1.0",
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.33.1",
89
+ "@typescript-eslint/parser": "^8.33.1",
90
+ "@typescript-eslint/utils": "^8.33.1",
91
+ "@vitest/eslint-plugin": "^1.2.1",
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.6.1",
96
+ "eslint-plugin-import-x": "^4.15.1",
97
97
  "eslint-plugin-jest-dom": "^5.5.0",
98
- "eslint-plugin-jsdoc": "^50.6.3",
99
- "eslint-plugin-jsonc": "^2.19.1",
100
- "eslint-plugin-perfectionist": "^4.9.0",
98
+ "eslint-plugin-jsdoc": "^50.7.1",
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.8.0",
103
+ "eslint-plugin-testing-library": "^7.4.0",
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
- "local-pkg": "^1.0.0",
110
+ "local-pkg": "^1.1.1",
111
111
  "toml-eslint-parser": "^0.10.0",
112
- "typescript-eslint": "^8.24.1",
113
- "yaml-eslint-parser": "^1.2.3"
112
+ "typescript-eslint": "^8.33.1",
113
+ "yaml-eslint-parser": "^1.3.0"
114
114
  },
115
115
  "devDependencies": {
116
- "@angular/core": "^19.1.7",
117
- "@commitlint/cli": "^19.7.1",
118
- "@commitlint/config-conventional": "^19.7.1",
119
- "@commitlint/cz-commitlint": "^19.6.1",
116
+ "@angular/core": "^20.0.2",
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.5",
130
- "bumpp": "^10.0.3",
131
- "changelogithub": "^13.12.1",
129
+ "@types/node": "^22.15.30",
130
+ "bumpp": "^10.1.1",
131
+ "changelogithub": "^13.15.0",
132
132
  "commitizen": "^4.3.1",
133
- "eslint": "^9.21.0",
134
- "eslint-plugin-erasable-syntax-only": "^0.2.1",
133
+ "eslint": "^9.28.0",
134
+ "eslint-plugin-erasable-syntax-only": "^0.3.1",
135
135
  "eslint-plugin-format": "^1.0.1",
136
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",
145
+ "simple-git-hooks": "^2.13.0",
146
146
  "tailwindcss": "^3.4.17",
147
- "typescript": "^5.7.3",
148
- "unbuild": "^3.3.1",
149
- "vitest": "^3.0.6"
147
+ "typescript": "^5.8.3",
148
+ "unbuild": "^3.5.0",
149
+ "vitest": "^3.2.2"
150
150
  },
151
151
  "pnpm": {
152
152
  "overrides": {