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