@fabdeh/eslint-config 0.2.2 → 0.3.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
@@ -9,8 +9,8 @@
9
9
  - Designed to work with TypeScript, JSX, etc. Out-of-box.
10
10
  - Opinionated, but [very customizable](#customization)
11
11
  - [ESLint Flat config](https://eslint.org/docs/latest/use/configure/configuration-files-new), compose easily!
12
- - Automatic [Angular](#angular), [NGRX](#ngrx), [TailwindCSS](#tailwindcc), [Vitest](#vitest) support when the corresponding dependency is detected.
13
- <!--- - Optional [formatters](#formatters) support for formatting CSS, HTML, XML, etc. --->
12
+ - Automatic [Angular](#angular), [NGRX](#ngrx), [TypeScript](#typescript), [Vitest](#vitest) support when the corresponding dependency is detected.
13
+ - Optional [formatters](#formatters) support for formatting CSS, HTML, XML, etc.
14
14
  - **Style principle**: Minimal for reading, stable for diff, consistent
15
15
  - Sorted imports, dangling commas
16
16
  - Single quotes, no semi
@@ -49,8 +49,8 @@ For example:
49
49
  ```json
50
50
  {
51
51
  "scripts": {
52
- "lint": "eslint .",
53
- "lint:fix": "eslint . --fix"
52
+ "lint": "eslint . --fix",
53
+ "lint:ci": "eslint ."
54
54
  }
55
55
  }
56
56
  ```
@@ -78,11 +78,27 @@ Add the following settings to your `.vscode/settings.json`:
78
78
  "source.organizeImports": "never"
79
79
  },
80
80
 
81
- // Silent the stylistic rules in you IDE, but still auto fix them
81
+ // Silence the style rules in you IDE, but still fix them automatically
82
82
  "eslint.rules.customizations": [{ "rule": "@stylistic/*", "severity": "off", "fixable": true }],
83
83
 
84
84
  // Enable eslint for all supported languages
85
- "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact", "html"]
85
+ "eslint.validate": [
86
+ "css",
87
+ "html",
88
+ "javascript",
89
+ "javascriptreact",
90
+ "json",
91
+ "jsonc",
92
+ "json5",
93
+ "less",
94
+ "markdown",
95
+ "scss",
96
+ "typescript",
97
+ "typescriptreact",
98
+ "toml",
99
+ "yaml",
100
+ "xml"
101
+ ]
86
102
  }
87
103
  ```
88
104
 
@@ -202,6 +218,242 @@ Check out the [configs](https://github.com/FabienDehopre/eslint-config/blob/main
202
218
 
203
219
  > Thanks to [antfu/eslint-config](https://github.com/antfu/eslint-config) for the inspiration and reference.
204
220
 
221
+ ### Rules Overrides
222
+
223
+ All the rules are always bound to one or more file extensions (via minimatch pattern. i.e.: \*_/_.?([cm])[jt]s?(x) for all JS and TS file types including JSX syntax).
224
+ If you want to override the rules, you need to specify the file extension:
225
+
226
+ ```js
227
+ // eslint.config.js
228
+ import { createConfig } from '@fabdeh/eslint-config';
229
+
230
+ export default createConfig(
231
+ {
232
+ typescript: true,
233
+ vitest: true
234
+ },
235
+ {
236
+ // Remember to specify the file glob here, otherwise it might cause the vitest plugin to handle non-spec files
237
+ files: ['**/*.spec.?([cm])[jt]s', '**/*.test.?([cm])[jt]s'],
238
+ rules: {
239
+ 'vitest/consistent-test-it': ['error', { fn: 'it' }],
240
+ }
241
+ },
242
+ {
243
+ // Without `files`, they are general rules for all files
244
+ rules: {
245
+ '@stylistic/semi': ['error', 'never']
246
+ }
247
+ }
248
+ );
249
+ ```
250
+
251
+ We also provide the `overrides` option in each integration to make it easier:
252
+
253
+ ```js
254
+ // eslint.config.js
255
+ import { createConfig } from '@fabdeh/eslint-config';
256
+
257
+ export default createConfig({
258
+ typescript: {
259
+ overrides: {
260
+ '@typescript-eslint/consisten-type-definitions': ['error', 'interface'],
261
+ },
262
+ },
263
+ angular: {
264
+ tsOverrides: {
265
+ '@angular-eslint/prefer-signals': 'off',
266
+ },
267
+ htmlOverrides: {
268
+ '@angular-eslint/template/no-any': 'warn',
269
+ },
270
+ },
271
+ yaml: {
272
+ overrides: {
273
+ // ...
274
+ }
275
+ }
276
+ });
277
+ ```
278
+
279
+ ### Auto-detected integrations
280
+
281
+ The following integrations are automatically enabled if the corresponding package is installed in your project:
282
+
283
+ - [TypeScript](#typescript)
284
+ - [Angular](#angular)
285
+ - [NgRx](#ngrx)
286
+ - [Vitest](#vitest)
287
+
288
+ #### TypeScript
289
+
290
+ Most of TypeScript rules are enable automatically if `typescript` package is installed in you project. Some `@typescript-eslint` rules are also enabled by default for JavaScript files.
291
+ You can explicitly enable/disable TypeScript integration manually:
292
+
293
+ ```js
294
+ // eslint.config.js
295
+ import { createConfig } from '@fabdeh/eslint-config';
296
+
297
+ export default createConfig({
298
+ typescript: true,
299
+ });
300
+ ```
301
+
302
+ ##### Erasable Syntax Only
303
+
304
+ The TypeScript integration also allow you to turn on/off rules that will report on using syntax that will not be allowed by TypeScript's [--erasableSyntaxOnly option](https://devblogs.microsoft.com/typescript/announcing-typescript-5-8-beta/#the---erasablesyntaxonly-option):
305
+
306
+ > Recently, Node.js 23.6 unflagged [experimental support for running TypeScript files directly](https://nodejs.org/api/typescript.html#type-stripping); however, only certain constructs are supported under this mode.
307
+ >
308
+ > ...
309
+ >
310
+ > TypeScript 5.8 introduces the `--erasableSyntaxOnly` flag. When this flag is enabled, TypeScript will only allow you to use constructs that can be erased from a file, and will issue an error if it encounters any constructs that cannot be erased.
311
+
312
+ You can enable these rules as follow:
313
+
314
+ ```js
315
+ // eslint.config.js
316
+ import { createConfig } from '@fabdeh/eslint-config';
317
+
318
+ export default createConfig({
319
+ typescript: {
320
+ enableErasableSyntaxOnly: true,
321
+ },
322
+ });
323
+ ```
324
+
325
+ These rules are disabled by default and therefore the associated ESLint plugin is also not installed by default.
326
+ Running `pnpx eslint` should prompt you to install the required plugin; otherwise, you can install it manually:
327
+
328
+ ```bash
329
+ pnpm add -D eslint-plugin-erasable-syntax-only
330
+ ```
331
+
332
+ #### Angular
333
+
334
+ Angular support is detected automatically by checking if `@angular/core` is installed in your project. You can also explicitly enable/disable it:
335
+
336
+ ```js
337
+ // eslint.config.js
338
+ import { createConfig } from '@fabdeh/eslint-config';
339
+
340
+ export default createConfig({
341
+ angular: true,
342
+ });
343
+ ```
344
+
345
+ #### NgRx
346
+
347
+ NgRx support is also detected automatically if any of the following package is installed in your project.
348
+
349
+ - `@ngrx/store`
350
+ - `@ngrx/effects`
351
+ - `@ngrx/signals`
352
+ - `@ngrx/operators`
353
+
354
+ As the Angular integration is can be explicitly enabled/disabled:
355
+
356
+ ```js
357
+ // eslint.config.js
358
+ import { createConfig } from '@fabdeh/eslint-config';
359
+
360
+ export default createConfig({
361
+ ngrx: true,
362
+ });
363
+ ```
364
+
365
+ > Of course, NgRx depends on Angular so the Angular integration will be enabled as well.
366
+
367
+ #### Vitest
368
+
369
+ The vitest integration is detected automatically by checking if `vitest` is installed in your project. It can be enable/disable manually in the configuration:
370
+
371
+ ```js
372
+ // eslint.config.js
373
+ import { createConfig } from '@fabdeh/eslint-config';
374
+
375
+ export default createConfig({
376
+ vitest: true,
377
+ });
378
+ ```
379
+
380
+ ### Optional integrations
381
+
382
+ We provide some optional integrations for specific use cases, that we don't include their dependencies by default.
383
+
384
+ #### Formatters
385
+
386
+ Use external formatters to format files that ESLint cannot handle yet (`.css`, `.html`, etc). Powered by [`eslint-plugin-format`](https://github.com/antfu/eslint-plugin-format).
387
+
388
+ ```js
389
+ // eslint.config.js
390
+ import { createConfig } from '@fabdeh/eslint-config';
391
+
392
+ export default createConfig({
393
+ formatters: {
394
+ /**
395
+ * Format CSS, LESS, SCSS files
396
+ */
397
+ css: true,
398
+ /**
399
+ * Format HTML files
400
+ */
401
+ html: true,
402
+ /**
403
+ * Format Markdown files
404
+ */
405
+ markdown: true,
406
+ }
407
+ });
408
+ ```
409
+
410
+ Running `npx eslint` should prompt you to install the required dependencies; otherwise, you can install them manually:
411
+
412
+ ```bash
413
+ pnpm add -D eslint-plugin-format
414
+ ```
415
+
416
+ ### Lint Staged
417
+
418
+ If you want to apply lint and auto-fix before every commit, you can add the following to your `package.json`:
419
+
420
+ ```json
421
+ {
422
+ "simple-git-hooks": {
423
+ "pre-commit": "pnpm nano-staged"
424
+ },
425
+ "nano-staged": {
426
+ "*": "eslint --fix"
427
+ }
428
+ }
429
+ ```
430
+
431
+ and then
432
+
433
+ ```bash
434
+ pnpm add -D nano-staged simple-git-hooks
435
+
436
+ # then, to activate the hooks
437
+ pnpm simple-git-hooks
438
+ ```
439
+
440
+ ## Versioning Policy
441
+
442
+ This project follows [Semantic Versioning](https://semver.org/) for releases. However, since this is just a config and involves opinions and many moving parts, we don't treat rules changes as breaking changes.
443
+
444
+ ### Changes Considered as Breaking Changes
445
+
446
+ - Node.js version requirement changes
447
+ - Huge refactors that might break the config
448
+ - Plugins made major changes that might break the config
449
+ - Changes that might affect most of the codebases
450
+
451
+ ### Changes Considered as Non-breaking Changes
452
+
453
+ - Enable/disable rules and plugins (that might become stricter)
454
+ - Rules options changes
455
+ - Version bumps of dependencies
456
+
205
457
  ## License
206
458
 
207
459
  [MIT](./LICENSE) License &copy; 2025-PRESENT [Fabien Dehopré](https://github.com/FabienDehopre)
package/dist/index.d.mts CHANGED
@@ -170,9 +170,9 @@ interface UnicornOptions {
170
170
  allRecommended?: boolean;
171
171
  }
172
172
  /**
173
- * An alias for the `StylisticCustomizeOptions` type without the `flat`, `name`, and `pluginName` properties.
173
+ * An alias for the `StylisticCustomizeOptions` type without the `pluginName` property.
174
174
  */
175
- type StylisticConfig = Omit<StylisticCustomizeOptions, 'flat' | 'name' | 'pluginName'>;
175
+ type StylisticConfig = Omit<StylisticCustomizeOptions, 'pluginName'>;
176
176
  /**
177
177
  * Interface representing the stylistic options.
178
178
  */
@@ -191,6 +191,14 @@ interface TypeScriptParserOptions {
191
191
  */
192
192
  parserOptions?: TSESLint.FlatConfig.ParserOptions;
193
193
  }
194
+ interface TypeScriptErasableSyntaxOnlyOptions {
195
+ /**
196
+ * Indicates whether the erasable syntax only rules should be enabled or not.
197
+ *
198
+ * @default false
199
+ */
200
+ enableErasableSyntaxOnly?: boolean;
201
+ }
194
202
  /**
195
203
  * Options for configuring Angular-specific linting rules.
196
204
  */
@@ -283,6 +291,19 @@ interface FormattersOptions {
283
291
  files?: string[];
284
292
  };
285
293
  }
294
+ interface RegExpOptions {
295
+ /**
296
+ * Override rules level.
297
+ */
298
+ level?: 'error' | 'warn';
299
+ }
300
+ interface TailwindcssParserPerGlobOptions {
301
+ /**
302
+ * Provides a specific ESLint parser per glob pattern.
303
+ * This is only needed if you want to lint tailwindcss without using Angular and TypeScript.
304
+ */
305
+ parsers?: Record<string, TSESLint.FlatConfig.Parser>;
306
+ }
286
307
  /**
287
308
  * Options for creating an ESLint configuration.
288
309
  *
@@ -307,7 +328,7 @@ interface CreateConfigOptions {
307
328
  *
308
329
  * @default auto-detected
309
330
  */
310
- typescript?: boolean | (OverridesOptions & TypeScriptParserOptions);
331
+ typescript?: boolean | (OverridesOptions & TypeScriptErasableSyntaxOnlyOptions & TypeScriptParserOptions);
311
332
  /**
312
333
  * Enable stylistic rules.
313
334
  *
@@ -315,6 +336,13 @@ interface CreateConfigOptions {
315
336
  * @default true
316
337
  */
317
338
  stylistic?: boolean | (OverridesOptions & StylisticConfig);
339
+ /**
340
+ * Enable regexp rules.
341
+ *
342
+ * @see https://ota-meshi.github.io/eslint-plugin-regexp/
343
+ * @default true
344
+ */
345
+ regexp?: boolean | (OverridesOptions & RegExpOptions);
318
346
  /**
319
347
  * Options for eslint-plugin-unicorn.
320
348
  *
@@ -342,9 +370,9 @@ interface CreateConfigOptions {
342
370
  /**
343
371
  * Options for the TailwindCSS linting rules.
344
372
  *
345
- * @default auto-detect based on dependencies.
373
+ * @default false
346
374
  */
347
- tailwindcss?: OverridesOptions | boolean;
375
+ tailwindcss?: boolean | (FilesOptions & OverridesOptions) | (OverridesOptions & TailwindcssParserPerGlobOptions);
348
376
  /**
349
377
  * Enable JSONC support.
350
378
  *
@@ -529,6 +557,14 @@ declare function ngrx(options?: NgrxOptions): Promise<ConfigArray>;
529
557
  */
530
558
  declare function perfectionist(): Promise<ConfigArray>;
531
559
 
560
+ /**
561
+ * Configure the recommended regexp rules.
562
+ *
563
+ * @param options - The options
564
+ * @returns The ESLint configuration for regexp linting
565
+ */
566
+ declare function regexp(options?: OverridesOptions & RegExpOptions): ConfigArray;
567
+
532
568
  /**
533
569
  * Configures ESLint rules for sorting keys and array values in `package.json` files.
534
570
  *
@@ -577,7 +613,7 @@ declare function stylistic(options?: StylisticOptions): Promise<ConfigArray>;
577
613
  * },
578
614
  * });
579
615
  */
580
- declare function tailwindcss(options?: OverridesOptions): Promise<ConfigArray>;
616
+ declare function tailwindcss(options?: (FilesOptions & OverridesOptions) | (OverridesOptions & TailwindcssParserPerGlobOptions)): Promise<ConfigArray>;
581
617
 
582
618
  /**
583
619
  * Generates an ESLint configuration for TOML files.
@@ -600,7 +636,7 @@ declare function toml(options?: FilesOptions & OverridesOptions & StylisticOptio
600
636
  * @param options.overrides - Additional rule overrides.
601
637
  * @returns A ConfigArray containing the TypeScript ESLint configuration.
602
638
  */
603
- declare function typescript(options?: OverridesOptions & StylisticOptions & TypeScriptParserOptions): ConfigArray;
639
+ declare function typescript(options?: OverridesOptions & StylisticOptions & TypeScriptErasableSyntaxOnlyOptions & TypeScriptParserOptions): Promise<ConfigArray>;
604
640
 
605
641
  /**
606
642
  * Generates a configuration array for the Unicorn plugin with the specified options.
@@ -739,4 +775,4 @@ type ResolvedOptions<T> = T extends boolean ? never : NonNullable<T>;
739
775
  */
740
776
  declare function resolveSubOptions<K extends keyof CreateConfigOptions>(options: CreateConfigOptions, key: K): ResolvedOptions<CreateConfigOptions[K]>;
741
777
 
742
- 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 ResolvedOptions, STYLISTIC_CONFIG_DEFAULT, type StylisticConfig, type StylisticOptions, type TestingOptions, type TypeScriptParserOptions, type UnicornOptions, 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 };
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 };
package/dist/index.mjs CHANGED
@@ -14,6 +14,7 @@ import jsdocPlugin from 'eslint-plugin-jsdoc';
14
14
  import * as importX from 'eslint-plugin-import-x';
15
15
  import perfectionistPlugin from 'eslint-plugin-perfectionist';
16
16
  import unicornPlugin from 'eslint-plugin-unicorn';
17
+ import { configs } from 'eslint-plugin-regexp';
17
18
  import { mergeProcessors, processorPassThrough } from 'eslint-merge-processors';
18
19
 
19
20
  const GLOB_SRC_EXT = "?([cm])[jt]s?(x)";
@@ -37,7 +38,7 @@ const GLOB_XML = "**/*.xml";
37
38
  const GLOB_SVG = "**/*.svg";
38
39
  const GLOB_GRAPHQL = "**/*.{g,graph}ql";
39
40
  const GLOB_HTML = "**/*.htm?(l)";
40
- const GLOB_TESTS = [`**/*.spec.${GLOB_SRC_EXT}`, `**/*.test.${GLOB_SRC_EXT}`, `**/test-setup.${GLOB_SRC_EXT}`];
41
+ const GLOB_TESTS = [`**/*.spec.?([cm])[jt]s`, `**/*.test.?([cm])[jt]s`, `**/test-setup.?([cm])[jt]s`];
41
42
  const GLOB_EXCLUDE = [
42
43
  "**/node_modules",
43
44
  "**/dist",
@@ -934,6 +935,27 @@ async function perfectionist() {
934
935
  });
935
936
  }
936
937
 
938
+ function regexp(options = {}) {
939
+ const { level, overrides } = options;
940
+ const config = configs["flat/recommended"];
941
+ const rules = Object.fromEntries(
942
+ Object.entries(config.rules).map(([ruleName, ruleLevel]) => [ruleName, level === "warn" ? level : ruleLevel])
943
+ );
944
+ return tseslint.config(
945
+ {
946
+ name: "fabdeh/regexp/rules",
947
+ files: [GLOB_SRC],
948
+ plugins: {
949
+ ...config.plugins
950
+ },
951
+ rules: {
952
+ ...rules,
953
+ ...overrides
954
+ }
955
+ }
956
+ );
957
+ }
958
+
937
959
  function sortPackageJson() {
938
960
  return tseslint.config({
939
961
  name: "fabdeh/sort/package-json",
@@ -1168,9 +1190,12 @@ const STYLISTIC_CONFIG_DEFAULT = {
1168
1190
  quoteProps: "as-needed"
1169
1191
  };
1170
1192
  async function stylistic(options = {}) {
1171
- const stylisticOptions = { ...STYLISTIC_CONFIG_DEFAULT, ...options };
1193
+ const stylisticOptions = {
1194
+ ...STYLISTIC_CONFIG_DEFAULT,
1195
+ ...typeof options.stylistic === "boolean" ? {} : options.stylistic
1196
+ };
1172
1197
  const stylisticPlugin = await interopDefault(import('@stylistic/eslint-plugin'));
1173
- const config = stylisticPlugin.configs.customize({ ...stylisticOptions, flat: true });
1198
+ const config = stylisticPlugin.configs.customize(stylisticOptions);
1174
1199
  return tseslint.config({
1175
1200
  name: "fabdeh/stylistic/rules",
1176
1201
  files: [GLOB_SRC],
@@ -1186,22 +1211,45 @@ async function stylistic(options = {}) {
1186
1211
  });
1187
1212
  }
1188
1213
 
1214
+ function isTailwindcssParserPerGlobOptions(options) {
1215
+ return "parsers" in options && options.parsers !== void 0;
1216
+ }
1189
1217
  async function tailwindcss(options = {}) {
1218
+ await ensurePackages(["eslint-plugin-tailwindcss"]);
1190
1219
  const tailwindcssPlugin = await interopDefault(import('eslint-plugin-tailwindcss'));
1191
- const { overrides } = options;
1192
- return tseslint.config({
1193
- name: "fabdeh/tailwindcss/rules",
1194
- plugins: { tailwindcss: tailwindcssPlugin },
1195
- files: [GLOB_SRC, GLOB_HTML],
1196
- rules: {
1197
- "tailwindcss/classnames-order": "error",
1198
- "tailwindcss/enforces-negative-arbitrary-values": "error",
1199
- "tailwindcss/enforces-shorthand": "error",
1200
- "tailwindcss/no-contradicting-classname": "error",
1201
- "tailwindcss/no-unnecessary-arbitrary-value": "error",
1202
- ...overrides
1220
+ let files;
1221
+ let parserConfigs;
1222
+ const { overrides = {} } = options;
1223
+ if (isTailwindcssParserPerGlobOptions(options)) {
1224
+ const parsers = options.parsers ?? {};
1225
+ files = { files: [...new Set(Object.keys(parsers))] };
1226
+ parserConfigs = Object.entries(parsers).map(([glob, parser], index) => ({
1227
+ name: `fabdeh/tailwindcss/parser-${index + 1}`,
1228
+ file: [glob],
1229
+ languageOptions: {
1230
+ parser
1231
+ }
1232
+ }));
1233
+ } else {
1234
+ files = { files: options.files ?? [GLOB_SRC, GLOB_HTML] };
1235
+ parserConfigs = [];
1236
+ }
1237
+ return tseslint.config(
1238
+ ...parserConfigs,
1239
+ {
1240
+ name: "fabdeh/tailwindcss/rules",
1241
+ plugins: { tailwindcss: tailwindcssPlugin },
1242
+ ...files,
1243
+ rules: {
1244
+ "tailwindcss/classnames-order": "error",
1245
+ "tailwindcss/enforces-negative-arbitrary-values": "error",
1246
+ "tailwindcss/enforces-shorthand": "error",
1247
+ "tailwindcss/no-contradicting-classname": "error",
1248
+ "tailwindcss/no-unnecessary-arbitrary-value": "error",
1249
+ ...overrides
1250
+ }
1203
1251
  }
1204
- });
1252
+ );
1205
1253
  }
1206
1254
 
1207
1255
  async function toml(options = {}) {
@@ -1278,8 +1326,16 @@ const memberOrdering = {
1278
1326
  ]
1279
1327
  };
1280
1328
 
1281
- function typescript(options = {}) {
1282
- const { stylistic = true, parserOptions = {}, overrides = {} } = options;
1329
+ async function typescript(options = {}) {
1330
+ const { stylistic = true, parserOptions = {}, overrides = {}, enableErasableSyntaxOnly = false } = options;
1331
+ let erasableSyntaxOnlyPlugin;
1332
+ let erasableSyntaxOnlyRules;
1333
+ if (enableErasableSyntaxOnly) {
1334
+ await ensurePackages(["eslint-plugin-erasable-syntax-only"]);
1335
+ const erasableSyntaxOnly = await interopDefault(import('eslint-plugin-erasable-syntax-only'));
1336
+ erasableSyntaxOnlyPlugin = erasableSyntaxOnly;
1337
+ erasableSyntaxOnlyRules = erasableSyntaxOnly.configs.recommended.rules;
1338
+ }
1283
1339
  return tseslint.config(
1284
1340
  {
1285
1341
  name: "fabdeh/typescript/setup",
@@ -1304,7 +1360,8 @@ function typescript(options = {}) {
1304
1360
  ignores: [`${GLOB_MARKDOWN}/**`],
1305
1361
  plugins: {
1306
1362
  "@typescript-eslint": tseslint.plugin,
1307
- "unused-imports": unusedImports
1363
+ "unused-imports": unusedImports,
1364
+ ...erasableSyntaxOnlyPlugin ? { "erasable-syntax-only": erasableSyntaxOnlyPlugin } : {}
1308
1365
  },
1309
1366
  rules: {
1310
1367
  ...tseslint.configs.strictTypeChecked.find((c) => c.name === "typescript-eslint/eslint-recommended")?.rules,
@@ -1371,6 +1428,7 @@ function typescript(options = {}) {
1371
1428
  "@typescript-eslint/unbound-method": ["error", { ignoreStatic: true }],
1372
1429
  "@typescript-eslint/unified-signatures": "error",
1373
1430
  "@typescript-eslint/no-unused-vars": "off",
1431
+ ...erasableSyntaxOnlyRules,
1374
1432
  ...overrides
1375
1433
  }
1376
1434
  }
@@ -1610,7 +1668,7 @@ function mergePrettierOptions(options, overrides = {}) {
1610
1668
  /* eslint-enable @typescript-eslint/no-unsafe-assignment */
1611
1669
  };
1612
1670
  }
1613
- async function formatters(options = {}, stylistic = {}) {
1671
+ async function formatters(options = {}, stylistic = {}, hasAngularTemplateParser = false) {
1614
1672
  if (options === true) {
1615
1673
  const isPrettierPluginXmlInScope = isPackageInScope("@prettier/plugin-xml");
1616
1674
  options = {
@@ -1707,8 +1765,10 @@ async function formatters(options = {}, stylistic = {}) {
1707
1765
  if (options.html) {
1708
1766
  configs.push({
1709
1767
  name: "fabdeh/formatter/html",
1710
- languageOptions: {
1711
- parser: formatPlugin.parserPlain
1768
+ ...hasAngularTemplateParser ? {} : {
1769
+ languageOptions: {
1770
+ parser: formatPlugin.parserPlain
1771
+ }
1712
1772
  },
1713
1773
  files: [GLOB_HTML],
1714
1774
  rules: {
@@ -1828,7 +1888,8 @@ async function createConfig(options = {}, ...userConfigs) {
1828
1888
  angular: enableAngular = isPackageExists("@angular/core"),
1829
1889
  gitignore: enableGitignore = true,
1830
1890
  ngrx: enableNgrx = NGRX_PACKAGES.some((p) => isPackageExists(p)),
1831
- tailwindcss: enableTailwind = isPackageExists("tailwindcss"),
1891
+ regexp: enableRegexp = true,
1892
+ tailwindcss: enableTailwind = false,
1832
1893
  typescript: enableTypescript = isPackageExists("typescript"),
1833
1894
  unicorn: enableUnicorn = true,
1834
1895
  vitest: enableVitest = isPackageExists("vitest")
@@ -1873,6 +1934,10 @@ async function createConfig(options = {}, ...userConfigs) {
1873
1934
  if (stylisticOptions) {
1874
1935
  configs.push(stylistic({ stylistic: stylisticOptions }));
1875
1936
  }
1937
+ if (enableRegexp) {
1938
+ const regexpOptions = resolveSubOptions(options, "regexp");
1939
+ configs.push(regexp(regexpOptions));
1940
+ }
1876
1941
  if (enableAngular) {
1877
1942
  const angularOptions = resolveSubOptions(options, "angular");
1878
1943
  configs.push(angular(angularOptions));
@@ -1919,7 +1984,11 @@ async function createConfig(options = {}, ...userConfigs) {
1919
1984
  configs.push(markdown(markdownOptions));
1920
1985
  }
1921
1986
  if (options.formatters) {
1922
- configs.push(formatters(options.formatters, typeof stylisticOptions === "boolean" ? {} : stylisticOptions));
1987
+ configs.push(formatters(
1988
+ options.formatters,
1989
+ typeof stylisticOptions === "boolean" ? {} : stylisticOptions,
1990
+ Boolean(enableAngular)
1991
+ ));
1923
1992
  }
1924
1993
  if ("files" in options) {
1925
1994
  throw new Error('[@fabdeh/eslint-config] The first argument should not contain the "files" property as the options are supposed to be global. Place it in the second or later config instead.');
@@ -1933,4 +2002,4 @@ async function createConfig(options = {}, ...userConfigs) {
1933
2002
  return tseslint.config(...await Promise.all(configs), ...await Promise.all(userConfigs));
1934
2003
  }
1935
2004
 
1936
- 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 };
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 };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fabdeh/eslint-config",
3
3
  "type": "module",
4
- "version": "0.2.2",
4
+ "version": "0.3.0",
5
5
  "packageManager": "pnpm@10.4.1",
6
6
  "description": "My personal eslint config preset",
7
7
  "author": {
@@ -55,16 +55,24 @@
55
55
  "peerDependencies": {
56
56
  "@prettier/plugin-xml": "^3.4.1",
57
57
  "eslint": "^9.20.0",
58
+ "eslint-plugin-erasable-syntax-only": "^0.2.1",
58
59
  "eslint-plugin-format": "^1.0.1",
60
+ "eslint-plugin-tailwindcss": "^3.18.0",
59
61
  "prettier-plugin-slidev": "^1.0.5"
60
62
  },
61
63
  "peerDependenciesMeta": {
62
64
  "@prettier/plugin-xml": {
63
65
  "optional": true
64
66
  },
67
+ "eslint-plugin-erasable-syntax-only": {
68
+ "optional": true
69
+ },
65
70
  "eslint-plugin-format": {
66
71
  "optional": true
67
72
  },
73
+ "eslint-plugin-tailwindcss": {
74
+ "optional": true
75
+ },
68
76
  "prettier-plugin-slidev": {
69
77
  "optional": true
70
78
  }
@@ -73,10 +81,10 @@
73
81
  "@antfu/install-pkg": "^1.0.0",
74
82
  "@clack/prompts": "^0.10.0",
75
83
  "@eslint-community/eslint-plugin-eslint-comments": "^4.4.1",
76
- "@eslint/js": "^9.20.0",
84
+ "@eslint/js": "^9.21.0",
77
85
  "@eslint/markdown": "^6.2.2",
78
86
  "@ngrx/eslint-plugin": "^19.0.1",
79
- "@stylistic/eslint-plugin": "^3.1.0",
87
+ "@stylistic/eslint-plugin": "^4.0.1",
80
88
  "@typescript-eslint/eslint-plugin": "^8.24.1",
81
89
  "@typescript-eslint/parser": "^8.24.1",
82
90
  "@typescript-eslint/utils": "^8.24.1",
@@ -91,13 +99,13 @@
91
99
  "eslint-plugin-jsonc": "^2.19.1",
92
100
  "eslint-plugin-perfectionist": "^4.9.0",
93
101
  "eslint-plugin-prefer-arrow-functions": "^3.6.2",
94
- "eslint-plugin-tailwindcss": "^3.18.0",
102
+ "eslint-plugin-regexp": "^2.7.0",
95
103
  "eslint-plugin-testing-library": "^7.1.1",
96
104
  "eslint-plugin-toml": "^0.12.0",
97
105
  "eslint-plugin-unicorn": "^57.0.0",
98
106
  "eslint-plugin-unused-imports": "^4.1.4",
99
107
  "eslint-plugin-yml": "^1.17.0",
100
- "globals": "^15.15.0",
108
+ "globals": "^16.0.0",
101
109
  "jsonc-eslint-parser": "^2.4.0",
102
110
  "local-pkg": "^1.0.0",
103
111
  "toml-eslint-parser": "^0.10.0",
@@ -105,7 +113,7 @@
105
113
  "yaml-eslint-parser": "^1.2.3"
106
114
  },
107
115
  "devDependencies": {
108
- "@angular/core": "^19.1.6",
116
+ "@angular/core": "^19.1.7",
109
117
  "@commitlint/cli": "^19.7.1",
110
118
  "@commitlint/config-conventional": "^19.7.1",
111
119
  "@commitlint/cz-commitlint": "^19.6.1",
@@ -118,12 +126,14 @@
118
126
  "@testing-library/angular": "^17.3.6",
119
127
  "@testing-library/jest-dom": "^6.6.3",
120
128
  "@types/fs-extra": "^11.0.4",
121
- "@types/node": "^22.13.4",
129
+ "@types/node": "^22.13.5",
122
130
  "bumpp": "^10.0.3",
123
131
  "changelogithub": "^13.12.1",
124
132
  "commitizen": "^4.3.1",
125
- "eslint": "^9.20.1",
133
+ "eslint": "^9.21.0",
134
+ "eslint-plugin-erasable-syntax-only": "^0.2.1",
126
135
  "eslint-plugin-format": "^1.0.1",
136
+ "eslint-plugin-tailwindcss": "^3.18.0",
127
137
  "execa": "^9.5.2",
128
138
  "fast-glob": "^3.3.3",
129
139
  "fs-extra": "^11.3.0",
@@ -136,7 +146,7 @@
136
146
  "tailwindcss": "^3.4.17",
137
147
  "typescript": "^5.7.3",
138
148
  "unbuild": "^3.3.1",
139
- "vitest": "^3.0.5"
149
+ "vitest": "^3.0.6"
140
150
  },
141
151
  "pnpm": {
142
152
  "overrides": {