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