@fabdeh/eslint-config 0.0.3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,1935 @@
1
+ import tseslint from 'typescript-eslint';
2
+ import { statSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import process from 'node:process';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { isPackageExists } from 'local-pkg';
7
+ import eslintComments from '@eslint-community/eslint-plugin-eslint-comments';
8
+ import * as importX from 'eslint-plugin-import-x';
9
+ import eslint from '@eslint/js';
10
+ import preferArrowFunctions from 'eslint-plugin-prefer-arrow-functions';
11
+ import unusedImports from 'eslint-plugin-unused-imports';
12
+ import globals from 'globals';
13
+ import jsdocPlugin from 'eslint-plugin-jsdoc';
14
+ import { mergeProcessors, processorPassThrough } from 'eslint-merge-processors';
15
+ import perfectionistPlugin from 'eslint-plugin-perfectionist';
16
+ import unicornPlugin from 'eslint-plugin-unicorn';
17
+
18
+ const GLOB_SRC_EXT = "?([cm])[jt]s?(x)";
19
+ const GLOB_SRC = `**/*.${GLOB_SRC_EXT}`;
20
+ const GLOB_JS = "**/*.?([cm])js?(x)";
21
+ const GLOB_TS_EXT = "?([cm])ts?(x)";
22
+ const GLOB_TS = `**/*.${GLOB_TS_EXT}`;
23
+ const GLOB_CSS = "**/*.css";
24
+ const GLOB_POSTCSS = "**/*.{p,post}css";
25
+ const GLOB_LESS = "**/*.less";
26
+ const GLOB_SCSS = "**/*.scss";
27
+ const GLOB_JSON = "**/*.json";
28
+ const GLOB_JSON5 = "**/*.json5";
29
+ const GLOB_JSONC = "**/*.jsonc";
30
+ const GLOB_MARKDOWN = "**/*.md";
31
+ const GLOB_MARKDOWN_IN_MARKDOWN = "**/*.md/*.md";
32
+ const GLOB_MARKDOWN_CODE = `${GLOB_MARKDOWN}/${GLOB_SRC}`;
33
+ const GLOB_YAML = "**/*.y?(a)ml";
34
+ const GLOB_TOML = "**/*.toml";
35
+ const GLOB_XML = "**/*.xml";
36
+ const GLOB_SVG = "**/*.svg";
37
+ const GLOB_GRAPHQL = "**/*.{g,graph}ql";
38
+ const GLOB_HTML = "**/*.htm?(l)";
39
+ const GLOB_TESTS = [`**/*.spec.${GLOB_SRC_EXT}`, `**/*.test.${GLOB_SRC_EXT}`, `**/test-setup.${GLOB_SRC_EXT}`];
40
+ const GLOB_EXCLUDE = [
41
+ "**/node_modules",
42
+ "**/dist",
43
+ "**/package-lock.json",
44
+ "**/yarn.lock",
45
+ "**/pnpm-lock.yaml",
46
+ "**/bun.lockb",
47
+ "**/output",
48
+ "**/coverage",
49
+ "**/temp",
50
+ "**/.temp",
51
+ "**/tmp",
52
+ "**/.tmp",
53
+ "**/.history",
54
+ "**/.vitepress/cache",
55
+ "**/.nuxt",
56
+ "**/.next",
57
+ "**/.svelte-kit",
58
+ "**/.vercel",
59
+ "**/.changeset",
60
+ "**/.idea",
61
+ "**/.cache",
62
+ "**/.output",
63
+ "**/.tsup",
64
+ "**/.vite-inspect",
65
+ "**/.yarn",
66
+ "**/vite.config.*.timestamp-*",
67
+ "**/CHANGELOG*.md",
68
+ "**/*.min.*",
69
+ "**/LICENSE*",
70
+ "**/__snapshots__",
71
+ "**/auto-import?(s).d.ts",
72
+ "**/components.d.ts",
73
+ "**/prettier-types.ts"
74
+ ];
75
+
76
+ const SCOPE_URL = fileURLToPath(new URL(".", import.meta.url));
77
+ const IS_CWD_IN_SCOPE = isPackageExists("@antfu/eslint-config");
78
+ function isInEditorEnv() {
79
+ if (process.env.CI) {
80
+ return false;
81
+ }
82
+ const envKeys = Object.keys(process.env);
83
+ if (envKeys.some((k) => /^GIT_/i.test(k))) {
84
+ return false;
85
+ }
86
+ const isVsCode = envKeys.some((k) => /^VSCODE_/i.test(k));
87
+ const isIntelliJ = envKeys.some((k) => /^INTELLIJ_/i.test(k));
88
+ const isJetbrains = envKeys.some((k) => /^JETBRAINS_/i.test(k));
89
+ const isVim = envKeys.some((k) => /^VIM$/i.test(k));
90
+ const isNeoVim = envKeys.some((k) => /^NVIM$/i.test(k));
91
+ return isVsCode || isIntelliJ || isJetbrains || isVim || isNeoVim;
92
+ }
93
+ function toArray(value) {
94
+ return Array.isArray(value) ? value : [value];
95
+ }
96
+ async function interopDefault(m) {
97
+ const resolved = await m;
98
+ return resolved.default ?? resolved;
99
+ }
100
+ function fileExists(path) {
101
+ try {
102
+ return statSync(path).isFile();
103
+ } catch {
104
+ return false;
105
+ }
106
+ }
107
+ function getWorkspaceRoot(dir, candidateRoot) {
108
+ if (process.env.NX_WORKSPACE_ROOT_PATH) {
109
+ return process.env.NX_WORKSPACE_ROOT_PATH;
110
+ }
111
+ if (dirname(dir) === dir) {
112
+ return candidateRoot;
113
+ }
114
+ const matches = [join(dir, "nx.json"), join(dir, "nx"), join(dir, "nx.bat")];
115
+ if (matches.some((x) => fileExists(x))) {
116
+ return dir;
117
+ } else if (fileExists(join(dir, "node_modules", "nx", "package.json"))) {
118
+ return getWorkspaceRoot(dirname(dir), dir);
119
+ } else {
120
+ return getWorkspaceRoot(dirname(dir), candidateRoot);
121
+ }
122
+ }
123
+ function isPackageInScope(name) {
124
+ return isPackageExists(name, { paths: [SCOPE_URL] });
125
+ }
126
+ async function ensurePackages(packages) {
127
+ if (process.env.CI || !process.stdout.isTTY || !IS_CWD_IN_SCOPE) {
128
+ return;
129
+ }
130
+ const nonExistingPackages = packages.filter((i) => i && !isPackageInScope(i));
131
+ if (nonExistingPackages.length === 0) {
132
+ return;
133
+ }
134
+ const p = await import('@clack/prompts');
135
+ const result = await p.confirm({
136
+ message: `${nonExistingPackages.length === 1 ? "Package is" : "Packages are"} required for this config: ${nonExistingPackages.join(", ")}. Do you want to install them?`
137
+ });
138
+ if (result) {
139
+ await import('@antfu/install-pkg').then((i) => i.installPackage(nonExistingPackages, { dev: true }));
140
+ }
141
+ }
142
+
143
+ async function angular(options = {}) {
144
+ const angularEslint = await interopDefault(import('angular-eslint'));
145
+ const { enableAccessibilityRules = true, tsOverrides = {}, htmlOverrides = {}, prefix = "app" } = options;
146
+ return tseslint.config(
147
+ {
148
+ name: "fabdeh/angular/rules",
149
+ plugins: {
150
+ "@angular-eslint": angularEslint.tsPlugin
151
+ },
152
+ processor: angularEslint.processInlineTemplates,
153
+ files: [GLOB_TS],
154
+ extends: [angularEslint.configs.tsRecommended],
155
+ rules: {
156
+ "max-classes-per-file": ["error", 1],
157
+ "max-lines": ["error", 400],
158
+ "@angular-eslint/component-max-inline-declarations": "error",
159
+ "@angular-eslint/component-selector": [
160
+ "error",
161
+ {
162
+ type: "element",
163
+ prefix,
164
+ style: "kebab-case"
165
+ }
166
+ ],
167
+ "@angular-eslint/contextual-decorator": "error",
168
+ "@angular-eslint/directive-selector": [
169
+ "error",
170
+ {
171
+ type: "attribute",
172
+ prefix,
173
+ style: "camelCase"
174
+ }
175
+ ],
176
+ "@angular-eslint/no-async-lifecycle-method": "error",
177
+ "@angular-eslint/no-attribute-decorator": "error",
178
+ "@angular-eslint/no-conflicting-lifecycle": "error",
179
+ "@angular-eslint/no-duplicates-in-metadata-arrays": "error",
180
+ "@angular-eslint/no-forward-ref": "error",
181
+ "@angular-eslint/no-lifecycle-call": "error",
182
+ "@angular-eslint/no-pipe-impure": "error",
183
+ "@angular-eslint/no-queries-metadata-property": "error",
184
+ "@angular-eslint/prefer-output-readonly": "error",
185
+ "@angular-eslint/prefer-signals": "error",
186
+ "@angular-eslint/relative-url-prefix": "error",
187
+ "@angular-eslint/sort-lifecycle-methods": "error",
188
+ "@angular-eslint/use-component-selector": "error",
189
+ "@angular-eslint/use-component-view-encapsulation": "error",
190
+ "@angular-eslint/use-lifecycle-interface": "error",
191
+ ...tsOverrides
192
+ }
193
+ },
194
+ {
195
+ name: "fabdeh/angular-template/rules",
196
+ plugins: {
197
+ "@angular-eslint": angularEslint.tsPlugin
198
+ },
199
+ files: [GLOB_HTML],
200
+ extends: [
201
+ ...angularEslint.configs.templateRecommended,
202
+ ...enableAccessibilityRules ? angularEslint.configs.templateAccessibility : []
203
+ ],
204
+ rules: {
205
+ "@angular-eslint/template/attributes-order": ["error", { alphabetical: true }],
206
+ "@angular-eslint/template/button-has-type": "error",
207
+ "@angular-eslint/template/conditional-complexity": "error",
208
+ "@angular-eslint/template/eqeqeq": ["error", { allowNullOrUndefined: true }],
209
+ "@angular-eslint/template/no-any": "error",
210
+ "@angular-eslint/template/no-duplicate-attributes": "error",
211
+ "@angular-eslint/template/no-interpolation-in-attributes": "error",
212
+ "@angular-eslint/template/no-positive-tabindex": "error",
213
+ "@angular-eslint/template/prefer-control-flow": "error",
214
+ "@angular-eslint/template/prefer-ngsrc": "error",
215
+ "@angular-eslint/template/prefer-self-closing-tags": "error",
216
+ ...htmlOverrides
217
+ }
218
+ }
219
+ );
220
+ }
221
+
222
+ function comments() {
223
+ return tseslint.config({
224
+ name: "fabdeh/comments/rules",
225
+ files: [GLOB_SRC],
226
+ plugins: {
227
+ "@eslint-community/eslint-comments": eslintComments
228
+ },
229
+ rules: {
230
+ "@eslint-community/eslint-comments/disable-enable-pair": ["error", { allowWholeFile: true }],
231
+ "@eslint-community/eslint-comments/no-aggregating-enable": "error",
232
+ "@eslint-community/eslint-comments/no-duplicate-disable": "error",
233
+ "@eslint-community/eslint-comments/no-unlimited-disable": "error",
234
+ "@eslint-community/eslint-comments/no-unused-disable": "error",
235
+ "@eslint-community/eslint-comments/no-unused-enable": "error"
236
+ }
237
+ });
238
+ }
239
+
240
+ function ignores(userIgnores = []) {
241
+ return tseslint.config({
242
+ name: "fabdeh/ignores",
243
+ ignores: [...GLOB_EXCLUDE, ...userIgnores]
244
+ });
245
+ }
246
+
247
+ function imports(options = {}) {
248
+ const { stylistic = true } = options;
249
+ return tseslint.config(
250
+ {
251
+ name: "fabdeh/imports/common/rules",
252
+ files: [GLOB_SRC],
253
+ plugins: {
254
+ "import-x": importX
255
+ },
256
+ rules: {
257
+ "import-x/consistent-type-specifier-style": ["error", "prefer-top-level"],
258
+ "import-x/default": "error",
259
+ "import-x/export": "error",
260
+ "import-x/first": "error",
261
+ "import-x/no-absolute-path": "error",
262
+ "import-x/no-duplicates": "error",
263
+ "import-x/no-empty-named-blocks": "error",
264
+ "import-x/no-extraneous-dependencies": "error",
265
+ "import-x/no-mutable-exports": "error",
266
+ "import-x/no-named-as-default": "warn",
267
+ "import-x/no-named-as-default-member": "warn",
268
+ "import-x/no-named-default": "error",
269
+ "import-x/no-self-import": "error",
270
+ "import-x/no-useless-path-segments": "error",
271
+ "import-x/no-webpack-loader-syntax": "error",
272
+ ...stylistic ? {
273
+ "import-x/newline-after-import": ["error", { considerComments: true, count: 1 }]
274
+ } : {}
275
+ }
276
+ },
277
+ {
278
+ name: "fabdeh/imports/js-only/rules",
279
+ files: [GLOB_JS],
280
+ rules: {
281
+ "import-x/named": "error",
282
+ "import-x/no-deprecated": "error"
283
+ }
284
+ },
285
+ {
286
+ name: "fabdeh/imports/ts-only/rules",
287
+ files: [GLOB_TS],
288
+ rules: {
289
+ "import-x/named": "off",
290
+ "import-x/no-deprecated": "off"
291
+ }
292
+ }
293
+ );
294
+ }
295
+
296
+ function javascript(options = {}) {
297
+ const { isInEditor = false, overrides = {}, allowAngularDecorator = false } = options;
298
+ return tseslint.config(
299
+ {
300
+ name: "fabdeh/javascript/setup",
301
+ languageOptions: {
302
+ ecmaVersion: 2022,
303
+ globals: {
304
+ ...globals.browser,
305
+ ...globals.es2021,
306
+ ...globals.node,
307
+ document: "readonly",
308
+ navigator: "readonly",
309
+ window: "readonly"
310
+ },
311
+ parser: tseslint.parser,
312
+ parserOptions: {
313
+ ecmaFeatures: {
314
+ jsx: true
315
+ },
316
+ ecmaVersion: 2022,
317
+ sourceType: "module"
318
+ },
319
+ sourceType: "module"
320
+ },
321
+ linterOptions: {
322
+ reportUnusedDisableDirectives: "error"
323
+ }
324
+ },
325
+ {
326
+ name: "fabdeh/javascript/rules",
327
+ files: [GLOB_SRC],
328
+ plugins: {
329
+ "@typescript-eslint": tseslint.plugin,
330
+ "prefer-arrow-functions": preferArrowFunctions,
331
+ "unused-imports": unusedImports
332
+ },
333
+ extends: [eslint.configs.recommended, ...tseslint.configs.recommended],
334
+ rules: {
335
+ "accessor-pairs": "error",
336
+ "array-callback-return": "error",
337
+ "block-scoped-var": "error",
338
+ "default-case-last": "error",
339
+ "dot-notation": "error",
340
+ eqeqeq: "error",
341
+ "id-denylist": [
342
+ "error",
343
+ "any",
344
+ "Number",
345
+ "number",
346
+ "String",
347
+ "string",
348
+ "Boolean",
349
+ "boolean",
350
+ "Undefined",
351
+ "undefined"
352
+ ],
353
+ "new-cap": allowAngularDecorator ? [
354
+ "error",
355
+ {
356
+ capIsNewExceptions: [
357
+ "Attribute",
358
+ "Component",
359
+ "ContentChild",
360
+ "ContentChildren",
361
+ "Directive",
362
+ "Host",
363
+ "HostBinding",
364
+ "HostListener",
365
+ "Inject",
366
+ "Injectable",
367
+ "Input",
368
+ "NgModule",
369
+ "Optional",
370
+ "Output",
371
+ "Pipe",
372
+ "Self",
373
+ "SkipSelf",
374
+ "ViewChild",
375
+ "ViewChildren"
376
+ ]
377
+ }
378
+ ] : "error",
379
+ "no-alert": "error",
380
+ "no-caller": "error",
381
+ "no-cond-assign": ["error", "always"],
382
+ "no-console": ["error", { allow: ["warn", "error"] }],
383
+ "no-empty": ["error", { allowEmptyCatch: true }],
384
+ "no-eval": "error",
385
+ "no-extend-native": "error",
386
+ "no-extra-bind": "error",
387
+ "no-implied-eval": "error",
388
+ "no-iterator": "error",
389
+ "no-labels": "error",
390
+ "no-lone-blocks": "error",
391
+ "no-lonely-if": "error",
392
+ "no-multi-str": "error",
393
+ "no-new": "error",
394
+ "no-new-func": "error",
395
+ "no-new-wrappers": "error",
396
+ "no-octal-escape": "error",
397
+ "no-plusplus": ["error", { allowForLoopAfterthoughts: true }],
398
+ "no-proto": "error",
399
+ "no-restricted-syntax": ["error", "TSEnumDeclaration[const=true]", "TSExportAssignment", "ForInStatement"],
400
+ "no-self-compare": "error",
401
+ "no-sequences": "error",
402
+ "no-template-curly-in-string": "error",
403
+ "no-throw-literal": "error",
404
+ "no-undef-init": "error",
405
+ "no-unmodified-loop-condition": "error",
406
+ "no-unneeded-ternary": ["error", { defaultAssignment: false }],
407
+ "no-unreachable-loop": "error",
408
+ "no-unused-expressions": [
409
+ "error",
410
+ {
411
+ allowShortCircuit: true,
412
+ allowTaggedTemplates: true,
413
+ allowTernary: true
414
+ }
415
+ ],
416
+ "no-use-before-define": ["error", { classes: false, functions: false }],
417
+ "no-useless-call": "error",
418
+ "no-useless-computed-key": "error",
419
+ "no-useless-constructor": "error",
420
+ "no-useless-rename": "error",
421
+ "no-useless-return": "error",
422
+ "object-shorthand": [
423
+ "error",
424
+ "always",
425
+ {
426
+ avoidQuotes: true,
427
+ ignoreConstructors: false
428
+ }
429
+ ],
430
+ "prefer-arrow-callback": "error",
431
+ "prefer-arrow-functions/prefer-arrow-functions": [
432
+ "error",
433
+ { singleReturnOnly: true, allowNamedFunctions: true }
434
+ ],
435
+ "prefer-exponentiation-operator": "error",
436
+ "prefer-object-spread": "error",
437
+ "prefer-promise-reject-errors": "error",
438
+ "prefer-regex-literals": ["error", { disallowRedundantWrapping: true }],
439
+ "prefer-template": "error",
440
+ "symbol-description": "error",
441
+ "unicode-bom": ["error", "never"],
442
+ "unused-imports/no-unused-imports": isInEditor ? "warn" : "error",
443
+ "unused-imports/no-unused-vars": [
444
+ "error",
445
+ {
446
+ args: "after-used",
447
+ argsIgnorePattern: "^_",
448
+ ignoreRestSiblings: true,
449
+ vars: "all",
450
+ varsIgnorePattern: "^_"
451
+ }
452
+ ],
453
+ "use-isnan": ["error", { enforceForIndexOf: true }],
454
+ "valid-typeof": ["error", { requireStringLiterals: true }],
455
+ "vars-on-top": "error",
456
+ yoda: ["error", "never", { exceptRange: true }],
457
+ "@typescript-eslint/no-unused-vars": "off",
458
+ // superseded by unused-imports/no-unused-vars
459
+ // TODO: check if required to apply more nx javascript rules here
460
+ ...overrides
461
+ }
462
+ }
463
+ );
464
+ }
465
+
466
+ async function jest(options = {}) {
467
+ const {
468
+ overrides = {},
469
+ useJestDom = isPackageExists("@testing-library/jest-dom"),
470
+ useTestingLibrary = isPackageExists("@testing-library/angular"),
471
+ stylistic = true
472
+ } = options;
473
+ const [jestPlugin, jestDomPlugin, testingLibraryPlugin] = await Promise.all([
474
+ interopDefault(import('eslint-plugin-jest')),
475
+ useJestDom ? interopDefault(import('eslint-plugin-jest-dom')) : Promise.resolve(undefined),
476
+ useTestingLibrary ? interopDefault(import('eslint-plugin-testing-library')) : Promise.resolve(undefined)
477
+ ]);
478
+ return tseslint.config({
479
+ name: "fabdeh/jest/rules",
480
+ plugins: {
481
+ jest: jestPlugin
482
+ },
483
+ languageOptions: {
484
+ globals: {
485
+ ...globals.node,
486
+ ...globals.jest
487
+ }
488
+ },
489
+ files: [...GLOB_TESTS, `** /jest.config.${GLOB_SRC_EXT}`],
490
+ extends: [
491
+ jestPlugin.configs["flat/recommended"],
492
+ ...stylistic ? [jestPlugin.configs["flat/style"]] : [],
493
+ ...jestDomPlugin ? [jestDomPlugin.configs["flat/recommended"]] : [],
494
+ ...testingLibraryPlugin ? [testingLibraryPlugin.configs["flat/angular"]] : []
495
+ ],
496
+ rules: {
497
+ "max-classes-per-file": "off",
498
+ "max-lines": "off",
499
+ "@typescript-eslint/consistent-type-assertions": "off",
500
+ "@typescript-eslint/no-empty-function": "off",
501
+ "@typescript-eslint/no-unsafe-assignment": "off",
502
+ "@typescript-eslint/no-unsafe-call": "off",
503
+ "@typescript-eslint/no-unsafe-member-access": "off",
504
+ "@typescript-eslint/unbound-method": "off",
505
+ "jest/consistent-test-it": ["error", { fn: "test" }],
506
+ "jest/prefer-spy-on": "error",
507
+ "jest/require-top-level-describe": "error",
508
+ "jest/unbound-method": "error",
509
+ "unicorn/no-null": "off",
510
+ ...overrides
511
+ }
512
+ });
513
+ }
514
+
515
+ function jsdoc(options = {}) {
516
+ const { stylistic = true } = options;
517
+ return tseslint.config(
518
+ {
519
+ name: "fabdeh/jsdoc/rules",
520
+ files: [GLOB_SRC],
521
+ plugins: { jsdoc: jsdocPlugin },
522
+ rules: {
523
+ "jsdoc/check-access": "warn",
524
+ "jsdoc/check-param-names": "warn",
525
+ "jsdoc/check-property-names": "warn",
526
+ "jsdoc/check-tag-names": "warn",
527
+ "jsdoc/check-types": "warn",
528
+ "jsdoc/check-values": "warn",
529
+ "jsdoc/empty-tags": "warn",
530
+ "jsdoc/implements-on-classes": "warn",
531
+ "jsdoc/no-defaults": "warn",
532
+ "jsdoc/no-multi-asterisks": "warn",
533
+ "jsdoc/no-undefined-types": "warn",
534
+ "jsdoc/require-jsdoc": "warn",
535
+ "jsdoc/require-param": "warn",
536
+ "jsdoc/require-param-description": "warn",
537
+ "jsdoc/require-param-name": "warn",
538
+ "jsdoc/require-param-type": "warn",
539
+ "jsdoc/require-property": "warn",
540
+ "jsdoc/require-property-description": "warn",
541
+ "jsdoc/require-property-name": "warn",
542
+ "jsdoc/require-property-type": "warn",
543
+ "jsdoc/require-returns": "warn",
544
+ "jsdoc/require-returns-check": "warn",
545
+ "jsdoc/require-returns-description": "warn",
546
+ "jsdoc/require-returns-type": "warn",
547
+ "jsdoc/require-yields": "warn",
548
+ "jsdoc/require-yields-check": "warn",
549
+ "jsdoc/tag-lines": ["warn", "never", { startLines: 1 }],
550
+ "jsdoc/valid-types": "warn",
551
+ ...stylistic ? {
552
+ "jsdoc/check-alignment": "warn",
553
+ "jsdoc/multiline-blocks": "warn"
554
+ } : {}
555
+ }
556
+ },
557
+ {
558
+ name: "fabdeh/jsdoc/ts-only/rules",
559
+ files: [GLOB_TS],
560
+ rules: {
561
+ "jsdoc/check-tag-names": ["warn", { typed: true }],
562
+ "jsdoc/no-types": "warn",
563
+ "jsdoc/no-undefined-types": "off",
564
+ "jsdoc/require-param-type": "off",
565
+ "jsdoc/require-property-type": "off",
566
+ "jsdoc/require-returns-type": "off"
567
+ }
568
+ }
569
+ );
570
+ }
571
+
572
+ async function jsonc(options = {}) {
573
+ const {
574
+ files = [GLOB_JSON, GLOB_JSON5, GLOB_JSONC],
575
+ overrides = {},
576
+ stylistic = true
577
+ } = options;
578
+ const { indent = 2 } = typeof stylistic === "object" ? stylistic : {};
579
+ const [jsoncPlugin, jsoncParser] = await Promise.all([
580
+ interopDefault(import('eslint-plugin-jsonc')),
581
+ interopDefault(import('jsonc-eslint-parser'))
582
+ ]);
583
+ return tseslint.config(
584
+ {
585
+ name: "fabdeh/jsonc/setup",
586
+ plugins: {
587
+ jsonc: jsoncPlugin
588
+ }
589
+ },
590
+ {
591
+ name: "fabdeh/jsonc/rules",
592
+ languageOptions: {
593
+ parser: jsoncParser
594
+ },
595
+ files,
596
+ rules: {
597
+ "jsonc/no-bigint-literals": "error",
598
+ "jsonc/no-binary-expression": "error",
599
+ "jsonc/no-binary-numeric-literals": "error",
600
+ "jsonc/no-dupe-keys": "error",
601
+ "jsonc/no-escape-sequence-in-identifier": "error",
602
+ "jsonc/no-floating-decimal": "error",
603
+ "jsonc/no-hexadecimal-numeric-literals": "error",
604
+ "jsonc/no-infinity": "error",
605
+ "jsonc/no-multi-str": "error",
606
+ "jsonc/no-nan": "error",
607
+ "jsonc/no-number-props": "error",
608
+ "jsonc/no-numeric-separators": "error",
609
+ "jsonc/no-octal": "error",
610
+ "jsonc/no-octal-escape": "error",
611
+ "jsonc/no-octal-numeric-literals": "error",
612
+ "jsonc/no-parenthesized": "error",
613
+ "jsonc/no-plus-sign": "error",
614
+ "jsonc/no-regexp-literals": "error",
615
+ "jsonc/no-sparse-arrays": "error",
616
+ "jsonc/no-template-literals": "error",
617
+ "jsonc/no-undefined-value": "error",
618
+ "jsonc/no-unicode-codepoint-escapes": "error",
619
+ "jsonc/no-useless-escape": "error",
620
+ "jsonc/space-unary-ops": "error",
621
+ "jsonc/valid-json-number": "error",
622
+ "jsonc/vue-custom-block/no-parsing-error": "error",
623
+ ...stylistic ? {
624
+ "jsonc/array-bracket-spacing": ["error", "never"],
625
+ "jsonc/comma-dangle": ["error", "never"],
626
+ "jsonc/comma-style": ["error", "last"],
627
+ "jsonc/indent": ["error", indent],
628
+ "jsonc/key-spacing": ["error", { afterColon: true, beforeColon: false }],
629
+ "jsonc/object-curly-newline": ["error", { consistent: true, multiline: true }],
630
+ "jsonc/object-curly-spacing": ["error", "always"],
631
+ "jsonc/object-property-newline": ["error", { allowMultiplePropertiesPerLine: true }],
632
+ "jsonc/quote-props": "error",
633
+ "jsonc/quotes": "error"
634
+ } : {},
635
+ ...overrides
636
+ }
637
+ }
638
+ );
639
+ }
640
+
641
+ async function markdown(options = {}) {
642
+ const {
643
+ files = [GLOB_MARKDOWN],
644
+ overrides = {}
645
+ } = options;
646
+ const parserPlain = {
647
+ meta: {
648
+ name: "parser-plain"
649
+ },
650
+ parseForESLint: (code) => ({
651
+ ast: {
652
+ body: [],
653
+ comments: [],
654
+ loc: { end: code.length, start: 0 },
655
+ range: [0, code.length],
656
+ tokens: [],
657
+ type: "Program"
658
+ },
659
+ // eslint-disable-next-line unicorn/no-null
660
+ scopeManager: null,
661
+ services: { isPlain: true },
662
+ visitorKeys: {
663
+ // eslint-disable-next-line @typescript-eslint/naming-convention
664
+ Program: []
665
+ }
666
+ })
667
+ };
668
+ const markdownPlugin = await interopDefault(import('@eslint/markdown'));
669
+ return tseslint.config(
670
+ {
671
+ name: "fabdeh/markdown/setup",
672
+ plugins: {
673
+ markdown: markdownPlugin
674
+ }
675
+ },
676
+ {
677
+ name: "fabdeh/markdown/processor",
678
+ files,
679
+ ignores: [GLOB_MARKDOWN_IN_MARKDOWN],
680
+ processor: mergeProcessors([
681
+ markdownPlugin.processors.markdown,
682
+ processorPassThrough
683
+ ])
684
+ },
685
+ {
686
+ name: "fabdeh/markdown/parser",
687
+ language: "markdown/gfm",
688
+ languageOptions: {
689
+ parser: parserPlain
690
+ },
691
+ files
692
+ },
693
+ {
694
+ name: "fabdeh/markdown/disables",
695
+ languageOptions: {
696
+ parserOptions: {
697
+ ecmaFeatures: {
698
+ impliedStrict: true
699
+ }
700
+ }
701
+ },
702
+ files: [GLOB_MARKDOWN_CODE],
703
+ extends: [tseslint.configs.disableTypeChecked],
704
+ rules: {
705
+ "import-x/newline-after-import": "off",
706
+ "no-alert": "off",
707
+ "no-console": "off",
708
+ "no-labels": "off",
709
+ "no-lone-blocks": "off",
710
+ "no-restricted-syntax": "off",
711
+ "no-undef": "off",
712
+ "no-unused-expressions": "off",
713
+ "no-unused-labels": "off",
714
+ "no-unused-vars": "off",
715
+ // 'node/prefer-global/process': 'off',
716
+ "@stylistic/comma-dangle": "off",
717
+ "@stylistic/eol-last": "off",
718
+ "@typescript-eslint/consistent-type-imports": "off",
719
+ "@typescript-eslint/explicit-function-return-type": "off",
720
+ "@typescript-eslint/no-namespace": "off",
721
+ "@typescript-eslint/no-redeclare": "off",
722
+ "@typescript-eslint/no-require-imports": "off",
723
+ "@typescript-eslint/no-unused-expressions": "off",
724
+ "@typescript-eslint/no-unused-vars": "off",
725
+ "@typescript-eslint/no-use-before-define": "off",
726
+ "unicode-bom": "off",
727
+ "unused-imports/no-unused-imports": "off",
728
+ "unused-imports/no-unused-vars": "off",
729
+ "unicorn/filename-case": "off",
730
+ ...overrides
731
+ }
732
+ }
733
+ );
734
+ }
735
+
736
+ const namingConvention = [
737
+ { selector: "default", format: ["camelCase"], leadingUnderscore: "forbid", trailingUnderscore: "forbid" },
738
+ { selector: ["variableLike", "memberLike"], format: ["camelCase"] },
739
+ // eslint-disable-next-line unicorn/no-null
740
+ { selector: "memberLike", modifiers: ["requiresQuotes"], format: null },
741
+ { selector: "typeLike", format: ["PascalCase"] },
742
+ { selector: "variable", modifiers: ["const", "global"], format: ["UPPER_CASE"] },
743
+ { selector: "enumMember", format: ["UPPER_CASE"] },
744
+ { selector: "parameter", modifiers: ["unused"], format: ["camelCase"], leadingUnderscore: "allow" },
745
+ { selector: "import", modifiers: ["default"], format: ["camelCase", "PascalCase"] },
746
+ { selector: "import", modifiers: ["namespace"], format: ["camelCase", "PascalCase"] }
747
+ ];
748
+
749
+ const DEFAULT_STORE_GLOB = [
750
+ `**/*.actions.${GLOB_TS_EXT}`,
751
+ `**/*.feature.${GLOB_TS_EXT}`,
752
+ `**/*.reducer.${GLOB_TS_EXT}`,
753
+ `**/*.state.${GLOB_TS_EXT}`
754
+ ];
755
+ const DEFAULT_EFFECTS_GLOB = [`**/*.effects.${GLOB_TS_EXT}`];
756
+ const DEFAULT_SIGNALS_GLOB = [`**/*.store.${GLOB_TS_EXT}`];
757
+ async function ngrx(options = {}) {
758
+ const ngrxPlugin = await interopDefault(import('@ngrx/eslint-plugin/v9'));
759
+ const {
760
+ store = isPackageExists("@ngrx/store"),
761
+ effects = isPackageExists("@ngrx/effects"),
762
+ signals = isPackageExists("@ngrx/signals")
763
+ } = options;
764
+ const configs = [];
765
+ if (store) {
766
+ const {
767
+ files = DEFAULT_STORE_GLOB,
768
+ enforceOperatorsRules = isPackageExists("@ngrx/operators"),
769
+ overrides = {}
770
+ } = typeof store === "object" ? store : {};
771
+ configs.push({
772
+ name: "fabdeh/ngrx-store/rules",
773
+ files,
774
+ extends: [...ngrxPlugin.configs.store, ...enforceOperatorsRules ? ngrxPlugin.configs.operators : []],
775
+ rules: {
776
+ "@typescript-eslint/naming-convention": [
777
+ "error",
778
+ ...namingConvention,
779
+ {
780
+ selector: ["objectLiteralProperty"],
781
+ // eslint-disable-next-line unicorn/no-null
782
+ format: null,
783
+ custom: {
784
+ regex: String.raw`^(?:(?:[a-z]+(?:[A-Z][a-z]*)*)|[A-Z][a-z]*(?:\s[A-Z][a-z]*)*)$`,
785
+ match: true
786
+ }
787
+ },
788
+ {
789
+ selector: "variable",
790
+ modifiers: ["const", "global", "exported"],
791
+ format: ["camelCase", "PascalCase"],
792
+ leadingUnderscore: "forbid",
793
+ trailingUnderscore: "forbid"
794
+ }
795
+ ],
796
+ ...overrides
797
+ }
798
+ });
799
+ }
800
+ if (effects) {
801
+ const {
802
+ files = DEFAULT_EFFECTS_GLOB,
803
+ enforceOperatorsRules = isPackageExists("@ngrx/operators"),
804
+ overrides = {}
805
+ } = typeof effects === "object" ? effects : {};
806
+ configs.push({
807
+ name: "fabdeh/ngrx-effects/rules",
808
+ files,
809
+ extends: [...ngrxPlugin.configs.effects, ...enforceOperatorsRules ? ngrxPlugin.configs.operators : []],
810
+ rules: {
811
+ "@typescript-eslint/naming-convention": [
812
+ "error",
813
+ ...namingConvention,
814
+ {
815
+ selector: "variable",
816
+ modifiers: ["const", "global", "exported"],
817
+ format: ["camelCase"],
818
+ leadingUnderscore: "forbid",
819
+ trailingUnderscore: "forbid",
820
+ suffix: ["$"]
821
+ }
822
+ ],
823
+ ...overrides
824
+ }
825
+ });
826
+ }
827
+ if (signals) {
828
+ const {
829
+ files = DEFAULT_SIGNALS_GLOB,
830
+ enforceOperatorsRules = isPackageExists("@ngrx/operators"),
831
+ overrides = {}
832
+ } = typeof signals === "object" ? signals : {};
833
+ const allowLeadingUnderscoreOnMemberLike = (conventions) => [
834
+ ...conventions.filter((c) => !Array.isArray(c.selector)),
835
+ { selector: "variableLike", format: ["camelCase"] },
836
+ { selector: "memberLike", format: ["camelCase"], leadingUnderscore: "allow" }
837
+ ];
838
+ configs.push({
839
+ name: "fabdeh/ngrx-signals/rules",
840
+ files,
841
+ extends: [...ngrxPlugin.configs.signals, ...enforceOperatorsRules ? ngrxPlugin.configs.operators : []],
842
+ rules: {
843
+ "@typescript-eslint/naming-convention": [
844
+ "error",
845
+ ...allowLeadingUnderscoreOnMemberLike(namingConvention),
846
+ {
847
+ selector: "variable",
848
+ modifiers: ["const", "global", "exported"],
849
+ format: ["PascalCase"],
850
+ leadingUnderscore: "forbid",
851
+ trailingUnderscore: "forbid"
852
+ }
853
+ ],
854
+ ...overrides
855
+ }
856
+ });
857
+ }
858
+ return tseslint.config(configs);
859
+ }
860
+
861
+ const SORT_IMPORT_GROUPS = [
862
+ "type",
863
+ { newlinesBetween: "never" },
864
+ "builtin-type",
865
+ { newlinesBetween: "never" },
866
+ "external-type",
867
+ { newlinesBetween: "never" },
868
+ "internal-type",
869
+ { newlinesBetween: "never" },
870
+ "parent-type",
871
+ { newlinesBetween: "never" },
872
+ "sibling-type",
873
+ { newlinesBetween: "never" },
874
+ "index-type",
875
+ { newlinesBetween: "always" },
876
+ "builtin",
877
+ { newlinesBetween: "always" },
878
+ "external",
879
+ { newlinesBetween: "always" },
880
+ "internal",
881
+ { newlinesBetween: "always" },
882
+ "parent",
883
+ { newlinesBetween: "never" },
884
+ "sibling",
885
+ { newlinesBetween: "never" },
886
+ "index",
887
+ { newlinesBetween: "always" },
888
+ "side-effect",
889
+ { newlinesBetween: "always" },
890
+ "object",
891
+ { newlinesBetween: "always" },
892
+ "unknown"
893
+ ];
894
+ const SORT_UNION_OR_INTERSECTION_GROUPS = [
895
+ "named",
896
+ "keyword",
897
+ "operator",
898
+ "literal",
899
+ "function",
900
+ "import",
901
+ "conditional",
902
+ "object",
903
+ "tuple",
904
+ "intersection",
905
+ "union",
906
+ "nullish",
907
+ "unknown"
908
+ ];
909
+
910
+ function perfectionist() {
911
+ return tseslint.config({
912
+ name: "fabdeh/perfectionist/rules",
913
+ plugins: {
914
+ perfectionist: perfectionistPlugin
915
+ },
916
+ rules: {
917
+ "perfectionist/sort-exports": ["error", { order: "asc", type: "natural" }],
918
+ "perfectionist/sort-imports": [
919
+ "error",
920
+ {
921
+ groups: SORT_IMPORT_GROUPS,
922
+ tsconfigRootDir: getWorkspaceRoot(process.cwd(), process.cwd()),
923
+ order: "asc",
924
+ type: "natural",
925
+ newlinesBetween: "never"
926
+ }
927
+ ],
928
+ "perfectionist/sort-intersection-types": [
929
+ "error",
930
+ {
931
+ groups: SORT_UNION_OR_INTERSECTION_GROUPS,
932
+ order: "asc",
933
+ type: "natural"
934
+ }
935
+ ],
936
+ "perfectionist/sort-named-exports": ["error", { order: "asc", type: "natural" }],
937
+ "perfectionist/sort-named-imports": ["error", { order: "asc", type: "natural" }],
938
+ "perfectionist/sort-switch-case": ["error", { order: "asc", type: "natural" }],
939
+ "perfectionist/sort-union-types": [
940
+ "error",
941
+ {
942
+ groups: SORT_UNION_OR_INTERSECTION_GROUPS,
943
+ order: "asc",
944
+ type: "natural"
945
+ }
946
+ ]
947
+ }
948
+ });
949
+ }
950
+
951
+ function sortPackageJson() {
952
+ return tseslint.config({
953
+ name: "fabdeh/sort/package-json",
954
+ files: ["**/package.json"],
955
+ rules: {
956
+ "jsonc/sort-array-values": [
957
+ "error",
958
+ {
959
+ order: { type: "asc" },
960
+ pathPattern: "^files$"
961
+ }
962
+ ],
963
+ "jsonc/sort-keys": [
964
+ "error",
965
+ {
966
+ order: [
967
+ "publisher",
968
+ "name",
969
+ "displayName",
970
+ "type",
971
+ "version",
972
+ "private",
973
+ "packageManager",
974
+ "description",
975
+ "author",
976
+ "contributors",
977
+ "license",
978
+ "funding",
979
+ "homepage",
980
+ "repository",
981
+ "bugs",
982
+ "keywords",
983
+ "categories",
984
+ "sideEffects",
985
+ "exports",
986
+ "main",
987
+ "module",
988
+ "unpkg",
989
+ "jsdelivr",
990
+ "types",
991
+ "typesVersions",
992
+ "bin",
993
+ "icon",
994
+ "files",
995
+ "engines",
996
+ "activationEvents",
997
+ "contributes",
998
+ "scripts",
999
+ "peerDependencies",
1000
+ "peerDependenciesMeta",
1001
+ "dependencies",
1002
+ "optionalDependencies",
1003
+ "devDependencies",
1004
+ "pnpm",
1005
+ "overrides",
1006
+ "resolutions",
1007
+ "husky",
1008
+ "simple-git-hooks",
1009
+ "lint-staged",
1010
+ "nano-staged",
1011
+ "eslintConfig",
1012
+ "config"
1013
+ ],
1014
+ pathPattern: "^$"
1015
+ },
1016
+ {
1017
+ order: { type: "asc" },
1018
+ pathPattern: "^(?:dev|peer|optional|bundled)?[Dd]ependencies(Meta)?$"
1019
+ },
1020
+ {
1021
+ order: { type: "asc" },
1022
+ pathPattern: "^(?:resolutions|overrides|pnpm.overrides)$"
1023
+ },
1024
+ {
1025
+ order: [
1026
+ "types",
1027
+ "import",
1028
+ "require",
1029
+ "default"
1030
+ ],
1031
+ pathPattern: "^exports.*$"
1032
+ },
1033
+ {
1034
+ order: [
1035
+ // client hooks only
1036
+ "pre-commit",
1037
+ "prepare-commit-msg",
1038
+ "commit-msg",
1039
+ "post-commit",
1040
+ "pre-rebase",
1041
+ "post-rewrite",
1042
+ "post-checkout",
1043
+ "post-merge",
1044
+ "pre-push",
1045
+ "pre-auto-gc"
1046
+ ],
1047
+ pathPattern: "^(?:gitHooks|husky|simple-git-hooks)$"
1048
+ }
1049
+ ]
1050
+ }
1051
+ });
1052
+ }
1053
+ function sortTsConfig() {
1054
+ return tseslint.config({
1055
+ name: "fabdeh/sort/tsconfig-json",
1056
+ files: ["**/tsconfig.json", "**/tsconfig.*.json"],
1057
+ rules: {
1058
+ "jsonc/sort-keys": [
1059
+ "error",
1060
+ {
1061
+ order: [
1062
+ "extends",
1063
+ "compilerOptions",
1064
+ "references",
1065
+ "files",
1066
+ "include",
1067
+ "exclude"
1068
+ ],
1069
+ pathPattern: "^$"
1070
+ },
1071
+ {
1072
+ order: [
1073
+ /* Projects */
1074
+ "incremental",
1075
+ "composite",
1076
+ "tsBuildInfoFile",
1077
+ "disableSourceOfProjectReferenceRedirect",
1078
+ "disableSolutionSearching",
1079
+ "disableReferencedProjectLoad",
1080
+ /* Language and Environment */
1081
+ "target",
1082
+ "jsx",
1083
+ "jsxFactory",
1084
+ "jsxFragmentFactory",
1085
+ "jsxImportSource",
1086
+ "lib",
1087
+ "moduleDetection",
1088
+ "noLib",
1089
+ "reactNamespace",
1090
+ "useDefineForClassFields",
1091
+ "emitDecoratorMetadata",
1092
+ "experimentalDecorators",
1093
+ /* Modules */
1094
+ "baseUrl",
1095
+ "rootDir",
1096
+ "rootDirs",
1097
+ "customConditions",
1098
+ "module",
1099
+ "moduleResolution",
1100
+ "moduleSuffixes",
1101
+ "noResolve",
1102
+ "paths",
1103
+ "resolveJsonModule",
1104
+ "resolvePackageJsonExports",
1105
+ "resolvePackageJsonImports",
1106
+ "typeRoots",
1107
+ "types",
1108
+ "allowArbitraryExtensions",
1109
+ "allowImportingTsExtensions",
1110
+ "allowUmdGlobalAccess",
1111
+ /* JavaScript Support */
1112
+ "allowJs",
1113
+ "checkJs",
1114
+ "maxNodeModuleJsDepth",
1115
+ /* Type Checking */
1116
+ "strict",
1117
+ "strictBindCallApply",
1118
+ "strictFunctionTypes",
1119
+ "strictNullChecks",
1120
+ "strictPropertyInitialization",
1121
+ "allowUnreachableCode",
1122
+ "allowUnusedLabels",
1123
+ "alwaysStrict",
1124
+ "exactOptionalPropertyTypes",
1125
+ "noFallthroughCasesInSwitch",
1126
+ "noImplicitAny",
1127
+ "noImplicitOverride",
1128
+ "noImplicitReturns",
1129
+ "noImplicitThis",
1130
+ "noPropertyAccessFromIndexSignature",
1131
+ "noUncheckedIndexedAccess",
1132
+ "noUnusedLocals",
1133
+ "noUnusedParameters",
1134
+ "useUnknownInCatchVariables",
1135
+ /* Emit */
1136
+ "declaration",
1137
+ "declarationDir",
1138
+ "declarationMap",
1139
+ "downlevelIteration",
1140
+ "emitBOM",
1141
+ "emitDeclarationOnly",
1142
+ "importHelpers",
1143
+ "importsNotUsedAsValues",
1144
+ "inlineSourceMap",
1145
+ "inlineSources",
1146
+ "mapRoot",
1147
+ "newLine",
1148
+ "noEmit",
1149
+ "noEmitHelpers",
1150
+ "noEmitOnError",
1151
+ "outDir",
1152
+ "outFile",
1153
+ "preserveConstEnums",
1154
+ "preserveValueImports",
1155
+ "removeComments",
1156
+ "sourceMap",
1157
+ "sourceRoot",
1158
+ "stripInternal",
1159
+ /* Interop Constraints */
1160
+ "allowSyntheticDefaultImports",
1161
+ "esModuleInterop",
1162
+ "forceConsistentCasingInFileNames",
1163
+ "isolatedDeclarations",
1164
+ "isolatedModules",
1165
+ "preserveSymlinks",
1166
+ "verbatimModuleSyntax",
1167
+ /* Completeness */
1168
+ "skipDefaultLibCheck",
1169
+ "skipLibCheck"
1170
+ ],
1171
+ pathPattern: "^compilerOptions$"
1172
+ }
1173
+ ]
1174
+ }
1175
+ });
1176
+ }
1177
+
1178
+ const STYLISTIC_CONFIG_DEFAULT = {
1179
+ semi: true,
1180
+ arrowParens: true,
1181
+ braceStyle: "1tbs",
1182
+ quoteProps: "as-needed"
1183
+ };
1184
+ async function stylistic(options = {}) {
1185
+ const stylisticOptions = { ...STYLISTIC_CONFIG_DEFAULT, ...options };
1186
+ const stylisticPlugin = await interopDefault(import('@stylistic/eslint-plugin'));
1187
+ const config = stylisticPlugin.configs.customize({ ...stylisticOptions, flat: true });
1188
+ return tseslint.config({
1189
+ name: "fabdeh/stylistic/rules",
1190
+ files: [GLOB_SRC],
1191
+ plugins: {
1192
+ "@stylistic": stylisticPlugin
1193
+ },
1194
+ rules: {
1195
+ ...config.rules,
1196
+ "@stylistic/comma-dangle": ["error", { arrays: "always-multiline", objects: "always-multiline" }],
1197
+ "@stylistic/no-extra-semi": "error",
1198
+ "@stylistic/operator-linebreak": ["error", "after", { overrides: { "?": "before", ":": "before" } }]
1199
+ }
1200
+ });
1201
+ }
1202
+
1203
+ async function tailwindcss(options = {}) {
1204
+ const tailwindcssPlugin = await interopDefault(import('eslint-plugin-tailwindcss'));
1205
+ const { overrides } = options;
1206
+ return tseslint.config({
1207
+ name: "fabdeh/tailwindcss/rules",
1208
+ plugins: { tailwindcss: tailwindcssPlugin },
1209
+ files: [GLOB_SRC, GLOB_HTML],
1210
+ rules: {
1211
+ "tailwindcss/classnames-order": "error",
1212
+ "tailwindcss/enforces-negative-arbitrary-values": "error",
1213
+ "tailwindcss/enforces-shorthand": "error",
1214
+ "tailwindcss/no-contradicting-classname": "error",
1215
+ "tailwindcss/no-unnecessary-arbitrary-value": "error",
1216
+ ...overrides
1217
+ }
1218
+ });
1219
+ }
1220
+
1221
+ async function toml(options = {}) {
1222
+ const {
1223
+ files = [GLOB_TOML],
1224
+ overrides = {},
1225
+ stylistic = true
1226
+ } = options;
1227
+ const { indent = 2 } = typeof stylistic === "object" ? stylistic : {};
1228
+ const [tomlPlugin, tomlParser] = await Promise.all([
1229
+ interopDefault(import('eslint-plugin-toml')),
1230
+ interopDefault(import('toml-eslint-parser'))
1231
+ ]);
1232
+ return tseslint.config(
1233
+ {
1234
+ name: "fabdeh/toml/setup",
1235
+ plugins: {
1236
+ toml: tomlPlugin
1237
+ }
1238
+ },
1239
+ {
1240
+ name: "fabdeh/toml/rules",
1241
+ languageOptions: {
1242
+ parser: tomlParser
1243
+ },
1244
+ files,
1245
+ rules: {
1246
+ "@stylistic/spaced-comment": "off",
1247
+ "toml/comma-style": "error",
1248
+ "toml/keys-order": "error",
1249
+ "toml/no-space-dots": "error",
1250
+ "toml/no-unreadable-number-separator": "error",
1251
+ "toml/precision-of-fractional-seconds": "error",
1252
+ "toml/precision-of-integer": "error",
1253
+ "toml/tables-order": "error",
1254
+ "toml/vue-custom-block/no-parsing-error": "error",
1255
+ ...stylistic ? {
1256
+ "toml/array-bracket-newline": "error",
1257
+ "toml/array-bracket-spacing": "error",
1258
+ "toml/array-element-newline": "error",
1259
+ "toml/indent": ["error", indent === "tab" ? 2 : indent],
1260
+ "toml/inline-table-curly-spacing": "error",
1261
+ "toml/key-spacing": "error",
1262
+ "toml/padding-line-between-pairs": "error",
1263
+ "toml/padding-line-between-tables": "error",
1264
+ "toml/quoted-keys": "error",
1265
+ "toml/spaced-comment": "error",
1266
+ "toml/table-bracket-spacing": "error"
1267
+ } : {},
1268
+ ...overrides
1269
+ }
1270
+ }
1271
+ );
1272
+ }
1273
+
1274
+ const memberOrdering = {
1275
+ default: [
1276
+ "signature",
1277
+ "#private-field",
1278
+ "private-field",
1279
+ "protected-field",
1280
+ "public-field",
1281
+ ["#private-accessor", "#private-get", "#private-set"],
1282
+ ["private-accessor", "private-get", "private-set"],
1283
+ ["protected-accessor", "protected-get", "protected-set"],
1284
+ ["public-accessor", "public-get", "public-set"],
1285
+ "static-initialization",
1286
+ "constructor",
1287
+ "call-signature",
1288
+ "public-method",
1289
+ "protected-method",
1290
+ "private-method",
1291
+ "#private-method"
1292
+ ]
1293
+ };
1294
+
1295
+ function typescript(options = {}) {
1296
+ const { stylistic = true, parserOptions, overrides } = options;
1297
+ return tseslint.config(
1298
+ {
1299
+ name: "fabdeh/typescript/setup",
1300
+ languageOptions: {
1301
+ parserOptions: parserOptions ?? {
1302
+ projectService: true,
1303
+ tsconfigRootDir: getWorkspaceRoot(process.cwd(), process.cwd())
1304
+ }
1305
+ }
1306
+ },
1307
+ {
1308
+ name: "fabdeh/typescript/rules",
1309
+ files: [GLOB_TS],
1310
+ plugins: {
1311
+ "@typescript-eslint": tseslint.plugin
1312
+ },
1313
+ extends: [
1314
+ eslint.configs.recommended,
1315
+ ...tseslint.configs.strictTypeChecked,
1316
+ ...stylistic ? tseslint.configs.stylisticTypeChecked : []
1317
+ ],
1318
+ rules: {
1319
+ "@typescript-eslint/array-type": ["error", { default: "array" }],
1320
+ "@typescript-eslint/consistent-indexed-object-style": "error",
1321
+ "@typescript-eslint/consistent-type-assertions": [
1322
+ "error",
1323
+ { assertionStyle: "as", objectLiteralTypeAssertions: "never" }
1324
+ ],
1325
+ "@typescript-eslint/consistent-type-definitions": ["error", "interface"],
1326
+ "@typescript-eslint/dot-notation": "error",
1327
+ "@typescript-eslint/explicit-function-return-type": [
1328
+ "error",
1329
+ {
1330
+ allowExpressions: true,
1331
+ allowTypedFunctionExpressions: true,
1332
+ allowHigherOrderFunctions: true,
1333
+ allowDirectConstAssertionInArrowFunctions: true,
1334
+ allowConciseArrowFunctionExpressionsStartingWithVoid: true
1335
+ }
1336
+ ],
1337
+ "@typescript-eslint/explicit-member-accessibility": ["error", { accessibility: "no-public" }],
1338
+ "@typescript-eslint/member-ordering": ["error", memberOrdering],
1339
+ "@typescript-eslint/method-signature-style": "error",
1340
+ "@typescript-eslint/naming-convention": ["error", ...namingConvention],
1341
+ "@typescript-eslint/no-base-to-string": "error",
1342
+ "@typescript-eslint/no-confusing-non-null-assertion": "error",
1343
+ "@typescript-eslint/no-confusing-void-expression": ["error", { ignoreArrowShorthand: true }],
1344
+ "@typescript-eslint/no-empty-interface": "off",
1345
+ // deprecated but included in nx
1346
+ "@typescript-eslint/no-extra-non-null-assertion": "error",
1347
+ "@typescript-eslint/no-extraneous-class": ["error", { allowStaticOnly: true, allowWithDecorator: true }],
1348
+ "@typescript-eslint/no-invalid-void-type": "error",
1349
+ "@typescript-eslint/no-non-null-asserted-nullish-coalescing": "error",
1350
+ "@typescript-eslint/no-require-imports": "error",
1351
+ "@typescript-eslint/only-throw-error": "error",
1352
+ "@typescript-eslint/no-unnecessary-condition": "error",
1353
+ "@typescript-eslint/no-unnecessary-type-arguments": "error",
1354
+ "@typescript-eslint/no-unsafe-unary-minus": "error",
1355
+ "@typescript-eslint/parameter-properties": ["error", { prefer: "parameter-property" }],
1356
+ "@typescript-eslint/prefer-enum-initializers": "error",
1357
+ "@typescript-eslint/prefer-for-of": "error",
1358
+ "@typescript-eslint/prefer-function-type": "error",
1359
+ "@typescript-eslint/prefer-includes": "error",
1360
+ "@typescript-eslint/prefer-literal-enum-member": "error",
1361
+ "@typescript-eslint/prefer-nullish-coalescing": "error",
1362
+ "@typescript-eslint/prefer-optional-chain": "error",
1363
+ "@typescript-eslint/prefer-readonly": "error",
1364
+ "@typescript-eslint/prefer-reduce-type-parameter": "error",
1365
+ "@typescript-eslint/prefer-return-this-type": "error",
1366
+ "@typescript-eslint/prefer-string-starts-ends-with": "error",
1367
+ "@typescript-eslint/require-array-sort-compare": "error",
1368
+ "@typescript-eslint/restrict-plus-operands": ["error", { skipCompoundAssignments: true }],
1369
+ "@typescript-eslint/restrict-template-expressions": "off",
1370
+ "@typescript-eslint/switch-exhaustiveness-check": "error",
1371
+ "@typescript-eslint/unbound-method": ["error", { ignoreStatic: true }],
1372
+ "@typescript-eslint/unified-signatures": "error",
1373
+ ...overrides
1374
+ }
1375
+ }
1376
+ );
1377
+ }
1378
+
1379
+ function unicorn(options = {}) {
1380
+ return tseslint.config({
1381
+ name: "fabdeh/unicorn/rules",
1382
+ plugins: { unicorn: unicornPlugin },
1383
+ rules: {
1384
+ ...options.allRecommended ? unicornPlugin.configs["flat/recommended"].rules : {
1385
+ "unicorn/catch-error-name": "error",
1386
+ "unicorn/consistent-empty-array-spread": "error",
1387
+ "unicorn/consistent-existence-index-check": "error",
1388
+ "unicorn/consistent-function-scoping": ["error", { checkArrowFunctions: false }],
1389
+ "unicorn/custom-error-definition": "error",
1390
+ "unicorn/error-message": "error",
1391
+ "unicorn/escape-case": "error",
1392
+ "unicorn/explicit-length-check": "error",
1393
+ "unicorn/filename-case": ["error", { case: "kebabCase" }],
1394
+ "unicorn/new-for-builtins": "error",
1395
+ "unicorn/no-abusive-eslint-disable": "error",
1396
+ "unicorn/no-anonymous-default-export": "error",
1397
+ "unicorn/no-array-for-each": "error",
1398
+ "unicorn/no-array-method-this-argument": "error",
1399
+ "unicorn/no-array-push-push": "error",
1400
+ "unicorn/no-array-reduce": "error",
1401
+ "unicorn/no-await-expression-member": "error",
1402
+ "unicorn/no-await-in-promise-methods": "error",
1403
+ "unicorn/no-document-cookie": "error",
1404
+ "unicorn/no-empty-file": "error",
1405
+ "unicorn/no-for-loop": "error",
1406
+ "unicorn/no-hex-escape": "error",
1407
+ "unicorn/no-instanceof-array": "error",
1408
+ "unicorn/no-invalid-fetch-options": "error",
1409
+ "unicorn/no-invalid-remove-event-listener": "error",
1410
+ "unicorn/no-length-as-slice-end": "error",
1411
+ "unicorn/no-lonely-if": "error",
1412
+ "unicorn/no-magic-array-flat-depth": "error",
1413
+ "unicorn/no-negated-condition": "error",
1414
+ "unicorn/no-negation-in-equality-check": "error",
1415
+ "unicorn/no-new-array": "error",
1416
+ "unicorn/no-new-buffer": "error",
1417
+ "unicorn/no-null": "error",
1418
+ "unicorn/no-process-exit": "error",
1419
+ "unicorn/no-single-promise-in-promise-methods": "error",
1420
+ "unicorn/no-thenable": "error",
1421
+ "unicorn/no-this-assignment": "error",
1422
+ "unicorn/no-typeof-undefined": "error",
1423
+ "unicorn/no-unnecessary-await": "error",
1424
+ "unicorn/no-unused-properties": "warn",
1425
+ "unicorn/no-useless-fallback-in-spread": "error",
1426
+ "unicorn/no-useless-length-check": "error",
1427
+ "unicorn/no-useless-promise-resolve-reject": "error",
1428
+ "unicorn/no-useless-spread": "error",
1429
+ "unicorn/no-useless-switch-case": "error",
1430
+ "unicorn/no-zero-fractions": "error",
1431
+ "unicorn/number-literal-case": "error",
1432
+ "unicorn/numeric-separators-style": "error",
1433
+ "unicorn/prefer-array-find": "error",
1434
+ "unicorn/prefer-array-flat": "error",
1435
+ "unicorn/prefer-array-flat-map": "error",
1436
+ "unicorn/prefer-array-index-of": "error",
1437
+ "unicorn/prefer-array-some": "error",
1438
+ "unicorn/prefer-at": "error",
1439
+ "unicorn/prefer-blob-reading-methods": "error",
1440
+ "unicorn/prefer-code-point": "error",
1441
+ "unicorn/prefer-date-now": "error",
1442
+ "unicorn/prefer-default-parameters": "error",
1443
+ "unicorn/prefer-export-from": "error",
1444
+ "unicorn/prefer-includes": "error",
1445
+ "unicorn/prefer-keyboard-event-key": "error",
1446
+ "unicorn/prefer-logical-operator-over-ternary": "error",
1447
+ "unicorn/prefer-math-min-max": "error",
1448
+ "unicorn/prefer-math-trunc": "error",
1449
+ "unicorn/prefer-modern-math-apis": "error",
1450
+ "unicorn/prefer-negative-index": "error",
1451
+ "unicorn/prefer-node-protocol": "error",
1452
+ "unicorn/prefer-number-properties": "error",
1453
+ "unicorn/prefer-object-from-entries": "error",
1454
+ "unicorn/prefer-optional-catch-binding": "error",
1455
+ "unicorn/prefer-query-selector": "error",
1456
+ "unicorn/prefer-regexp-test": "error",
1457
+ "unicorn/prefer-set-has": "error",
1458
+ "unicorn/prefer-set-size": "error",
1459
+ "unicorn/prefer-spread": "error",
1460
+ "unicorn/prefer-string-raw": "error",
1461
+ "unicorn/prefer-string-replace-all": "error",
1462
+ "unicorn/prefer-string-slice": "error",
1463
+ "unicorn/prefer-string-starts-ends-with": "error",
1464
+ "unicorn/prefer-string-trim-start-end": "error",
1465
+ "unicorn/prefer-structured-clone": "error",
1466
+ "unicorn/prefer-switch": "error",
1467
+ "unicorn/prefer-ternary": "error",
1468
+ "unicorn/prefer-type-error": "error",
1469
+ "unicorn/require-array-join-separator": "error",
1470
+ "unicorn/require-number-to-fixed-digits-argument": "error",
1471
+ "unicorn/require-post-message-target-origin": "error",
1472
+ "unicorn/throw-new-error": "error"
1473
+ }
1474
+ }
1475
+ });
1476
+ }
1477
+
1478
+ async function vitest(options = {}) {
1479
+ const {
1480
+ overrides = {},
1481
+ useJestDom = isPackageExists("@testing-library/jest-dom"),
1482
+ useTestingLibrary = isPackageExists("@testing-library/angular")
1483
+ } = options;
1484
+ const [vitestPlugin, jestDomPlugin, testingLibraryPlugin] = await Promise.all([
1485
+ interopDefault(import('@vitest/eslint-plugin')),
1486
+ useJestDom ? interopDefault(import('eslint-plugin-jest-dom')) : Promise.resolve(undefined),
1487
+ useTestingLibrary ? interopDefault(import('eslint-plugin-testing-library')) : Promise.resolve(undefined)
1488
+ ]);
1489
+ return tseslint.config({
1490
+ name: "fabdeh/vitest/rules",
1491
+ plugins: { vitest: vitestPlugin },
1492
+ languageOptions: {
1493
+ globals: {
1494
+ ...globals.node,
1495
+ ...globals.vitest,
1496
+ ...vitestPlugin.environments.env.globals
1497
+ }
1498
+ },
1499
+ settings: {
1500
+ vitest: {
1501
+ typecheck: true
1502
+ }
1503
+ },
1504
+ files: [...GLOB_TESTS],
1505
+ extends: [
1506
+ vitestPlugin.configs.recommended,
1507
+ ...jestDomPlugin ? [jestDomPlugin.configs["flat/recommended"]] : [],
1508
+ ...testingLibraryPlugin ? [testingLibraryPlugin.configs["flat/angular"]] : []
1509
+ ],
1510
+ rules: {
1511
+ "max-classes-per-file": "off",
1512
+ "max-lines": "off",
1513
+ "@typescript-eslint/consistent-type-assertions": "off",
1514
+ "@typescript-eslint/no-empty-function": "off",
1515
+ "@typescript-eslint/no-unsafe-assignment": "off",
1516
+ "@typescript-eslint/no-unsafe-call": "off",
1517
+ "@typescript-eslint/no-unsafe-member-access": "off",
1518
+ "@typescript-eslint/unbound-method": "off",
1519
+ "unicorn/no-null": "off",
1520
+ "vitest/consistent-test-it": ["error", { fn: "test" }],
1521
+ "vitest/no-standalone-expect": "error",
1522
+ "vitest/no-test-return-statement": "error",
1523
+ "vitest/prefer-hooks-in-order": "error",
1524
+ "vitest/prefer-hooks-on-top": "error",
1525
+ "vitest/prefer-lowercase-title": "error",
1526
+ "vitest/prefer-spy-on": "error",
1527
+ "vitest/prefer-to-be": "error",
1528
+ "vitest/prefer-to-be-falsy": "error",
1529
+ "vitest/prefer-to-be-object": "error",
1530
+ "vitest/prefer-to-be-truthy": "error",
1531
+ "vitest/prefer-to-contain": "error",
1532
+ "vitest/prefer-to-have-length": "error",
1533
+ "vitest/prefer-todo": "error",
1534
+ "vitest/prefer-vi-mocked": "error",
1535
+ "vitest/require-top-level-describe": "error",
1536
+ ...overrides
1537
+ }
1538
+ });
1539
+ }
1540
+
1541
+ async function yaml(options = {}) {
1542
+ const {
1543
+ files = [GLOB_YAML],
1544
+ overrides = {},
1545
+ stylistic = true
1546
+ } = options;
1547
+ const {
1548
+ indent = 2,
1549
+ quotes = "single"
1550
+ } = typeof stylistic === "object" ? stylistic : {};
1551
+ const [yamlPlugin, yamlParser] = await Promise.all([
1552
+ interopDefault(import('eslint-plugin-yml')),
1553
+ interopDefault(import('yaml-eslint-parser'))
1554
+ ]);
1555
+ return tseslint.config(
1556
+ {
1557
+ name: "fabdeh/yaml/setup",
1558
+ plugins: {
1559
+ yaml: yamlPlugin
1560
+ }
1561
+ },
1562
+ {
1563
+ name: "fabdeh/yaml/rules",
1564
+ languageOptions: {
1565
+ parser: yamlParser
1566
+ },
1567
+ files,
1568
+ rules: {
1569
+ "@stylistic/spaced-comment": "off",
1570
+ "yaml/block-mapping": "error",
1571
+ "yaml/block-sequence": "error",
1572
+ "yaml/no-empty-key": "error",
1573
+ "yaml/no-empty-sequence-entry": "error",
1574
+ "yaml/no-irregular-whitespace": "error",
1575
+ "yaml/plain-scalar": "error",
1576
+ "yaml/vue-custom-block/no-parsing-error": "error",
1577
+ ...stylistic ? {
1578
+ "yaml/block-mapping-question-indicator-newline": "error",
1579
+ "yaml/block-sequence-hyphen-indicator-newline": "error",
1580
+ "yaml/flow-mapping-curly-newline": "error",
1581
+ "yaml/flow-mapping-curly-spacing": "error",
1582
+ "yaml/flow-sequence-bracket-newline": "error",
1583
+ "yaml/flow-sequence-bracket-spacing": "error",
1584
+ "yaml/indent": ["error", indent === "tab" ? 2 : indent],
1585
+ "yaml/key-spacing": "error",
1586
+ "yaml/no-tab-indent": "error",
1587
+ "yaml/quotes": ["error", { avoidEscape: true, prefer: quotes === "backtick" ? "single" : quotes }],
1588
+ "yaml/spaced-comment": "error"
1589
+ } : {},
1590
+ ...overrides
1591
+ }
1592
+ }
1593
+ );
1594
+ }
1595
+
1596
+ function mergePrettierOptions(options, overrides = {}) {
1597
+ return {
1598
+ ...options,
1599
+ ...overrides,
1600
+ plugins: [
1601
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
1602
+ ...overrides.plugins ?? [],
1603
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
1604
+ ...options.plugins ?? []
1605
+ ]
1606
+ };
1607
+ }
1608
+ async function formatters(options = {}, stylistic = {}) {
1609
+ if (options === true) {
1610
+ const isPrettierPluginXmlInScope = isPackageInScope("@prettier/plugin-xml");
1611
+ options = {
1612
+ css: true,
1613
+ graphql: true,
1614
+ html: true,
1615
+ markdown: true,
1616
+ slidev: isPackageExists("@slidev/cli"),
1617
+ svg: isPrettierPluginXmlInScope,
1618
+ xml: isPrettierPluginXmlInScope
1619
+ };
1620
+ }
1621
+ await ensurePackages([
1622
+ "eslint-plugin-format",
1623
+ options.markdown && options.slidev ? "prettier-plugin-slidev" : undefined,
1624
+ options.xml || options.svg ? "@prettier/plugin-xml" : undefined
1625
+ ]);
1626
+ const {
1627
+ indent,
1628
+ quotes,
1629
+ semi
1630
+ } = {
1631
+ ...STYLISTIC_CONFIG_DEFAULT,
1632
+ ...stylistic
1633
+ };
1634
+ const prettierOptions = {
1635
+ endOfLine: "auto",
1636
+ printWidth: 120,
1637
+ semi,
1638
+ singleQuote: quotes === "single",
1639
+ tabWidth: typeof indent === "number" ? indent : 2,
1640
+ trailingComma: "all",
1641
+ useTabs: indent === "tab",
1642
+ ...options.options
1643
+ };
1644
+ const prettierXmlOptions = {
1645
+ xmlQuoteAttributes: "double",
1646
+ xmlSelfClosingSpace: true,
1647
+ xmlSortAttributesByKey: false,
1648
+ xmlWhitespaceSensitivity: "ignore"
1649
+ };
1650
+ const formatPlugin = await interopDefault(import('eslint-plugin-format'));
1651
+ const configs = [
1652
+ {
1653
+ name: "fabdeh/formatter/setup",
1654
+ plugins: {
1655
+ format: formatPlugin
1656
+ }
1657
+ }
1658
+ ];
1659
+ if (options.css) {
1660
+ configs.push(
1661
+ {
1662
+ name: "fabdeh/formatter/css",
1663
+ languageOptions: {
1664
+ parser: formatPlugin.parserPlain
1665
+ },
1666
+ files: [GLOB_CSS, GLOB_POSTCSS],
1667
+ rules: {
1668
+ "format/prettier": [
1669
+ "error",
1670
+ mergePrettierOptions(prettierOptions, { parser: "css" })
1671
+ ]
1672
+ }
1673
+ },
1674
+ {
1675
+ name: "fabdeh/formatter/scss",
1676
+ languageOptions: {
1677
+ parser: formatPlugin.parserPlain
1678
+ },
1679
+ files: [GLOB_SCSS],
1680
+ rules: {
1681
+ "format/prettier": [
1682
+ "error",
1683
+ mergePrettierOptions(prettierOptions, { parser: "scss" })
1684
+ ]
1685
+ }
1686
+ },
1687
+ {
1688
+ name: "fabdeh/formatter/less",
1689
+ languageOptions: {
1690
+ parser: formatPlugin.parserPlain
1691
+ },
1692
+ files: [GLOB_LESS],
1693
+ rules: {
1694
+ "format/prettier": [
1695
+ "error",
1696
+ mergePrettierOptions(prettierOptions, { parser: "less" })
1697
+ ]
1698
+ }
1699
+ }
1700
+ );
1701
+ }
1702
+ if (options.html) {
1703
+ configs.push({
1704
+ name: "fabdeh/formatter/html",
1705
+ languageOptions: {
1706
+ parser: formatPlugin.parserPlain
1707
+ },
1708
+ files: [GLOB_HTML],
1709
+ rules: {
1710
+ "format/prettier": [
1711
+ "error",
1712
+ mergePrettierOptions(prettierOptions, { parser: "html" })
1713
+ ]
1714
+ }
1715
+ });
1716
+ }
1717
+ if (options.xml) {
1718
+ configs.push({
1719
+ name: "fabdeh/formatter/xml",
1720
+ languageOptions: {
1721
+ parser: formatPlugin.parserPlain
1722
+ },
1723
+ files: [GLOB_XML],
1724
+ rules: {
1725
+ "format/prettier": [
1726
+ "error",
1727
+ mergePrettierOptions({ ...prettierXmlOptions, ...prettierOptions }, {
1728
+ parser: "xml",
1729
+ plugins: ["@prettier/plugin-xml"]
1730
+ })
1731
+ ]
1732
+ }
1733
+ });
1734
+ }
1735
+ if (options.svg) {
1736
+ configs.push({
1737
+ name: "fabdeh/formatter/svg",
1738
+ languageOptions: {
1739
+ parser: formatPlugin.parserPlain
1740
+ },
1741
+ files: [GLOB_SVG],
1742
+ rules: {
1743
+ "format/prettier": [
1744
+ "error",
1745
+ mergePrettierOptions({ ...prettierXmlOptions, ...prettierOptions }, {
1746
+ parser: "xml",
1747
+ plugins: ["@prettier/plugin-xml"]
1748
+ })
1749
+ ]
1750
+ }
1751
+ });
1752
+ }
1753
+ if (options.markdown) {
1754
+ const GLOB_SLIDEV = options.slidev ? options.slidev === true ? ["**/.slides.md"] : options.slidev.files : [];
1755
+ configs.push({
1756
+ name: "fabdeh/formatter/markdown",
1757
+ languageOptions: {
1758
+ parser: formatPlugin.parserPlain
1759
+ },
1760
+ files: [GLOB_MARKDOWN],
1761
+ ignores: GLOB_SLIDEV,
1762
+ rules: {
1763
+ "format/prettier": [
1764
+ "error",
1765
+ mergePrettierOptions(prettierOptions, {
1766
+ parser: "markdown",
1767
+ embeddedLanguageFormatting: "off"
1768
+ })
1769
+ ]
1770
+ }
1771
+ });
1772
+ if (options.slidev) {
1773
+ configs.push({
1774
+ name: "fabdeh/formatter/slidev",
1775
+ languageOptions: {
1776
+ parser: formatPlugin.parserPlain
1777
+ },
1778
+ files: GLOB_SLIDEV,
1779
+ rules: {
1780
+ "format/prettier": [
1781
+ "error",
1782
+ mergePrettierOptions(prettierOptions, {
1783
+ embeddedLanguageFormatting: "off",
1784
+ parser: "slidev",
1785
+ plugins: ["prettier-plugin-slidev"]
1786
+ })
1787
+ ]
1788
+ }
1789
+ });
1790
+ }
1791
+ }
1792
+ if (options.graphql) {
1793
+ configs.push({
1794
+ name: "fabdeh/formatter/graphql",
1795
+ languageOptions: {
1796
+ parser: formatPlugin.parserPlain
1797
+ },
1798
+ files: [GLOB_GRAPHQL],
1799
+ rules: {
1800
+ "format/prettier": [
1801
+ "error",
1802
+ mergePrettierOptions(prettierOptions, { parser: "graphql" })
1803
+ ]
1804
+ }
1805
+ });
1806
+ }
1807
+ return tseslint.config(configs);
1808
+ }
1809
+
1810
+ const FLAT_CONFIG_PROPS = [
1811
+ "name",
1812
+ "languageOptions",
1813
+ "linterOptions",
1814
+ "processor",
1815
+ "plugins",
1816
+ "rules",
1817
+ "settings",
1818
+ "extends"
1819
+ ];
1820
+ const NGRX_PACKAGES = ["@ngrx/store", "@ngrx/effects", "@ngrx/signals", "@ngrx/operators"];
1821
+ async function createConfig(options = {}, ...userConfigs) {
1822
+ const {
1823
+ angular: enableAngular = isPackageExists("@angular/core"),
1824
+ gitignore: enableGitignore = true,
1825
+ isInEditor = isInEditorEnv(),
1826
+ jest: enableJest = isPackageExists("jest"),
1827
+ ngrx: enableNgrx = NGRX_PACKAGES.some((p) => isPackageExists(p)),
1828
+ tailwindcss: enableTailwind = isPackageExists("tailwindcss"),
1829
+ unicorn: enableUnicorn = true,
1830
+ vitest: enableVitest = isPackageExists("vitest")
1831
+ } = options;
1832
+ if (isInEditor) {
1833
+ console.log(
1834
+ "[@fabdeh/eslint-config] Detected running in editor, some rules are configured as warn or completely turned off."
1835
+ );
1836
+ }
1837
+ if (enableJest && enableVitest) {
1838
+ throw new Error(
1839
+ '[@fabdeh/eslint-config] Detected that both "jest" and "vitest" are enabled which is not supported. Manually disable or uninstall one of them.'
1840
+ );
1841
+ }
1842
+ const stylisticOptions = options.stylistic === false ? false : typeof options.stylistic === "object" ? options.stylistic : {};
1843
+ const configs = [];
1844
+ if (enableGitignore) {
1845
+ if (typeof enableGitignore === "object") {
1846
+ configs.push(
1847
+ interopDefault(import('eslint-config-flat-gitignore')).then(
1848
+ (r) => tseslint.config(r({ name: "fabdeh/gitignore", ...enableGitignore }))
1849
+ )
1850
+ );
1851
+ } else {
1852
+ configs.push(
1853
+ interopDefault(import('eslint-config-flat-gitignore')).then(
1854
+ (r) => tseslint.config(r({ name: "fabdeh/gitignore", strict: true }))
1855
+ )
1856
+ );
1857
+ }
1858
+ }
1859
+ const allowAngularDecorator = options.angular !== false;
1860
+ configs.push(
1861
+ ignores(options.ignores),
1862
+ javascript({ isInEditor, overrides: options.javascript?.overrides, allowAngularDecorator }),
1863
+ typescript({
1864
+ stylistic: stylisticOptions,
1865
+ parserOptions: options.typescript?.parserOptions,
1866
+ overrides: options.typescript?.overrides
1867
+ }),
1868
+ comments(),
1869
+ imports({ stylistic: stylisticOptions }),
1870
+ jsdoc({ stylistic: stylisticOptions }),
1871
+ perfectionist()
1872
+ );
1873
+ if (enableUnicorn) {
1874
+ configs.push(unicorn(typeof enableUnicorn === "object" ? enableUnicorn : {}));
1875
+ }
1876
+ if (stylisticOptions) {
1877
+ configs.push(stylistic({ stylistic: stylisticOptions }));
1878
+ }
1879
+ if (enableAngular) {
1880
+ configs.push(angular(typeof enableAngular === "object" ? enableAngular : {}));
1881
+ }
1882
+ if (enableNgrx) {
1883
+ configs.push(ngrx(typeof enableNgrx === "object" ? enableNgrx : {}));
1884
+ }
1885
+ if (enableJest) {
1886
+ configs.push(
1887
+ jest(
1888
+ typeof enableJest === "object" ? { ...enableJest, stylistic: stylisticOptions } : { stylistic: stylisticOptions }
1889
+ )
1890
+ );
1891
+ }
1892
+ if (enableVitest) {
1893
+ configs.push(vitest(typeof enableVitest === "object" ? enableVitest : {}));
1894
+ }
1895
+ if (enableTailwind) {
1896
+ configs.push(tailwindcss(typeof enableTailwind === "object" ? enableTailwind : {}));
1897
+ }
1898
+ if (options.jsonc ?? true) {
1899
+ configs.push(
1900
+ jsonc({
1901
+ overrides: typeof options.jsonc === "object" ? options.jsonc.overrides : {},
1902
+ stylistic: stylisticOptions
1903
+ }),
1904
+ sortPackageJson(),
1905
+ sortTsConfig()
1906
+ );
1907
+ }
1908
+ if (options.yaml ?? true) {
1909
+ configs.push(yaml({
1910
+ overrides: typeof options.yaml === "object" ? options.yaml.overrides : {},
1911
+ stylistic: stylisticOptions
1912
+ }));
1913
+ }
1914
+ if (options.toml ?? true) {
1915
+ configs.push(toml({
1916
+ overrides: typeof options.toml === "object" ? options.toml.overrides : {},
1917
+ stylistic: stylisticOptions
1918
+ }));
1919
+ }
1920
+ if (options.formatters) {
1921
+ configs.push(formatters(options.formatters, typeof stylisticOptions === "boolean" ? {} : stylisticOptions));
1922
+ }
1923
+ if ("files" in options) {
1924
+ 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.');
1925
+ }
1926
+ const fusedConfig = Object.fromEntries(
1927
+ Object.entries(options).filter(([k]) => FLAT_CONFIG_PROPS.includes(k))
1928
+ );
1929
+ if (Object.keys(fusedConfig).length > 0) {
1930
+ configs.push([fusedConfig]);
1931
+ }
1932
+ return tseslint.config(...await Promise.all(configs), ...await Promise.all(userConfigs));
1933
+ }
1934
+
1935
+ export { GLOB_HTML, GLOB_JS, GLOB_SRC, GLOB_TESTS, GLOB_TS, STYLISTIC_CONFIG_DEFAULT, angular, comments, createConfig, ensurePackages, getWorkspaceRoot, ignores, imports, interopDefault, isInEditorEnv, isPackageInScope, javascript, jest, jsdoc, jsonc, markdown, ngrx, perfectionist, sortPackageJson, sortTsConfig, stylistic, tailwindcss, toArray, toml, typescript, unicorn, vitest, yaml };