@rhapsodic/eslint-config 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,890 @@
1
+ import pluginImportX from "eslint-plugin-import-x";
2
+ import pluginUnicorn from "eslint-plugin-unicorn";
3
+ import globals from "globals";
4
+ import process from "node:process";
5
+ import { fileURLToPath } from "node:url";
6
+ import { isPackageExists } from "local-pkg";
7
+ import { mergeProcessors } from "eslint-merge-processors";
8
+ import { FlatConfigComposer } from "eslint-flat-config-utils";
9
+
10
+ //#region src/configs/disables.ts
11
+ async function disables() {
12
+ return [{
13
+ files: ["**/*.js", "**/*.cjs"],
14
+ name: "rhapsodic/disables/cjs",
15
+ rules: {
16
+ "ts/explicit-function-return-type": "off",
17
+ "ts/explicit-module-boundary-types": "off",
18
+ "ts/no-var-requires": "off",
19
+ "unicorn/prefer-module": "off"
20
+ }
21
+ }];
22
+ }
23
+
24
+ //#endregion
25
+ //#region src/globs.ts
26
+ const GLOB_SRC_EXT = "?([cm])[jt]s?(x)";
27
+ const GLOB_SRC = "**/*.?([cm])[jt]s?(x)";
28
+ const GLOB_JS = "**/*.?([cm])js";
29
+ const GLOB_JSX = "**/*.?([cm])jsx";
30
+ const GLOB_TS = "**/*.?([cm])ts";
31
+ const GLOB_TSX = "**/*.?([cm])tsx";
32
+ const GLOB_VUE = "**/*.vue";
33
+ const GLOB_TESTS = [
34
+ `**/__tests__/**/*.${GLOB_SRC_EXT}`,
35
+ `**/*.spec.${GLOB_SRC_EXT}`,
36
+ `**/*.test.${GLOB_SRC_EXT}`,
37
+ `**/*.bench.${GLOB_SRC_EXT}`,
38
+ `**/*.benchmark.${GLOB_SRC_EXT}`
39
+ ];
40
+ const GLOB_ALL_SRC = [GLOB_SRC, GLOB_VUE];
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
+ "**/.vite-inspect",
65
+ "**/.yarn",
66
+ "**/vite.config.*.timestamp-*",
67
+ "**/CHANGELOG*.md",
68
+ "**/*.min.*",
69
+ "**/LICENSE*",
70
+ "**/__snapshots__",
71
+ "**/auto-import?(s).d.ts",
72
+ "**/components.d.ts"
73
+ ];
74
+
75
+ //#endregion
76
+ //#region src/configs/ignores.ts
77
+ async function ignores(userIgnores = []) {
78
+ let ignoresArray = [...GLOB_EXCLUDE];
79
+ if (typeof userIgnores === "function") ignoresArray = userIgnores(ignoresArray);
80
+ else ignoresArray = [...ignoresArray, ...userIgnores];
81
+ return [{
82
+ ignores: ignoresArray,
83
+ name: "rhapsodic/ignores"
84
+ }];
85
+ }
86
+
87
+ //#endregion
88
+ //#region src/configs/imports.ts
89
+ async function imports(options = {}) {
90
+ const { overrides = {}, stylistic: stylistic$1 = true } = options;
91
+ return [{
92
+ name: "rhapsodic/imports/rules",
93
+ plugins: { import: pluginImportX },
94
+ rules: {
95
+ "import/default": "error",
96
+ "import/namespace": "error",
97
+ "import/export": "error",
98
+ "import/no-named-as-default": "warn",
99
+ "import/no-named-as-default-member": "warn",
100
+ "import/no-duplicates": "warn",
101
+ ...stylistic$1 ? { "import/newline-after-import": ["error", { count: 1 }] } : {},
102
+ ...overrides
103
+ }
104
+ }];
105
+ }
106
+
107
+ //#endregion
108
+ //#region src/configs/javascript.ts
109
+ async function javascript(options = {}) {
110
+ const { overrides = {} } = options;
111
+ return [{
112
+ languageOptions: {
113
+ ecmaVersion: "latest",
114
+ globals: {
115
+ ...globals.browser,
116
+ ...globals.es2021,
117
+ ...globals.node,
118
+ document: "readonly",
119
+ navigator: "readonly",
120
+ window: "readonly"
121
+ },
122
+ parserOptions: {
123
+ ecmaVersion: "latest",
124
+ sourceType: "module"
125
+ },
126
+ sourceType: "module"
127
+ },
128
+ linterOptions: { reportUnusedDisableDirectives: true },
129
+ name: "rhapsodic/javascript/setup"
130
+ }, {
131
+ name: "rhapsodic/javascript/rules",
132
+ rules: {
133
+ "constructor-super": "error",
134
+ "for-direction": "error",
135
+ "getter-return": ["error", { allowImplicit: true }],
136
+ "no-async-promise-executor": "error",
137
+ "no-await-in-loop": "off",
138
+ "no-case-declarations": "error",
139
+ "no-class-assign": "error",
140
+ "no-compare-neg-zero": "error",
141
+ "no-cond-assign": "error",
142
+ "no-console": "off",
143
+ "no-const-assign": "error",
144
+ "no-constant-binary-expression": "error",
145
+ "no-constant-condition": "error",
146
+ "no-continue": "off",
147
+ "no-control-regex": "error",
148
+ "no-debugger": "error",
149
+ "no-delete-var": "error",
150
+ "no-dupe-args": "error",
151
+ "no-dupe-class-members": "error",
152
+ "no-dupe-else-if": "error",
153
+ "no-dupe-keys": "error",
154
+ "no-duplicate-case": "error",
155
+ "no-empty": "error",
156
+ "no-empty-character-class": "error",
157
+ "no-empty-pattern": "error",
158
+ "no-empty-static-block": "error",
159
+ "no-ex-assign": "error",
160
+ "no-extra-boolean-cast": "error",
161
+ "no-fallthrough": "error",
162
+ "no-func-assign": "error",
163
+ "no-global-assign": "error",
164
+ "no-import-assign": "error",
165
+ "no-implicit-coercion": ["error", { allow: ["!!", "+"] }],
166
+ "no-invalid-regexp": "error",
167
+ "no-irregular-whitespace": "error",
168
+ "no-loss-of-precision": "error",
169
+ "no-misleading-character-class": "error",
170
+ "no-new-native-nonconstructor": "error",
171
+ "no-new-symbol": "error",
172
+ "no-nonoctal-decimal-escape": "error",
173
+ "no-obj-calls": "error",
174
+ "no-octal": "error",
175
+ "no-plusplus": "off",
176
+ "no-prototype-builtins": "error",
177
+ "no-redeclare": "error",
178
+ "no-regex-spaces": "error",
179
+ "no-self-assign": "error",
180
+ "no-setter-return": "error",
181
+ "no-shadow": "off",
182
+ "no-shadow-restricted-names": "error",
183
+ "no-sparse-arrays": "error",
184
+ "no-this-before-super": "error",
185
+ "no-unassigned-vars": "error",
186
+ "no-undef": "error",
187
+ "no-underscore-dangle": "off",
188
+ "no-unexpected-multiline": "error",
189
+ "no-unreachable": "error",
190
+ "no-unsafe-finally": "error",
191
+ "no-unsafe-negation": "error",
192
+ "no-unsafe-optional-chaining": "error",
193
+ "no-unused-labels": "error",
194
+ "no-unused-private-class-members": "error",
195
+ "no-unused-vars": "error",
196
+ "no-use-before-define": "off",
197
+ "no-useless-assignment": "off",
198
+ "no-useless-backreference": "error",
199
+ "no-useless-catch": "error",
200
+ "no-useless-escape": "error",
201
+ "no-useless-return": "off",
202
+ "no-var": "error",
203
+ "no-with": "error",
204
+ "prefer-const": "error",
205
+ "prefer-template": "error",
206
+ "preserve-caught-error": "error",
207
+ "require-yield": "error",
208
+ "use-isnan": "error",
209
+ "valid-typeof": ["error", { requireStringLiterals: true }],
210
+ ...overrides
211
+ }
212
+ }];
213
+ }
214
+
215
+ //#endregion
216
+ //#region src/utils.ts
217
+ const scopeUrl = fileURLToPath(new URL(".", import.meta.url));
218
+ const isCwdInScope = isPackageExists("@rhapsodic/eslint-config");
219
+ const parserPlain = {
220
+ meta: { name: "parser-plain" },
221
+ parseForESLint: (code) => ({
222
+ ast: {
223
+ body: [],
224
+ comments: [],
225
+ loc: {
226
+ end: code.length,
227
+ start: 0
228
+ },
229
+ range: [0, code.length],
230
+ tokens: [],
231
+ type: "Program"
232
+ },
233
+ scopeManager: null,
234
+ services: { isPlain: true },
235
+ visitorKeys: { Program: [] }
236
+ })
237
+ };
238
+ /**
239
+ * Combine array and non-array configs into a single array.
240
+ */
241
+ async function combine(...configs) {
242
+ return (await Promise.all(configs)).flat();
243
+ }
244
+ /**
245
+ * Rename plugin prefixes in a rule object.
246
+ * Accepts a map of prefixes to rename.
247
+ *
248
+ * @example
249
+ * ```ts
250
+ * import { renameRules } from '@rhapsodic/eslint-config'
251
+ *
252
+ * export default [{
253
+ * rules: renameRules(
254
+ * {
255
+ * '@typescript-eslint/indent': 'error'
256
+ * },
257
+ * { '@typescript-eslint': 'ts' }
258
+ * )
259
+ * }]
260
+ * ```
261
+ */
262
+ function renameRules(rules, map) {
263
+ return Object.fromEntries(Object.entries(rules).map(([key, value]) => {
264
+ for (const [from, to] of Object.entries(map)) if (key.startsWith(`${from}/`)) return [to + key.slice(from.length), value];
265
+ return [key, value];
266
+ }));
267
+ }
268
+ async function interopDefault(m) {
269
+ const resolved = await m;
270
+ return resolved.default ?? resolved;
271
+ }
272
+ function isPackageInScope(name) {
273
+ return isPackageExists(name, { paths: [scopeUrl] });
274
+ }
275
+ async function ensurePackages(packages) {
276
+ if (process.env.CI || process.stdout.isTTY === false || isCwdInScope === false) return;
277
+ const nonExistingPackages = packages.filter((i) => i && !isPackageInScope(i));
278
+ if (nonExistingPackages.length === 0) return;
279
+ if (await (await import("@clack/prompts")).confirm({ message: `${nonExistingPackages.length === 1 ? "Package is" : "Packages are"} required for this config: ${nonExistingPackages.join(", ")}. Do you want to install them?` })) await import("@antfu/install-pkg").then((i) => i.installPackage(nonExistingPackages, { dev: true }));
280
+ }
281
+
282
+ //#endregion
283
+ //#region src/configs/stylistic.ts
284
+ const StylisticConfigDefaults = {
285
+ arrowParens: true,
286
+ blockSpacing: true,
287
+ braceStyle: "1tbs",
288
+ commaDangle: "always-multiline",
289
+ indent: 2,
290
+ quoteProps: "as-needed",
291
+ quotes: "single",
292
+ semi: true
293
+ };
294
+ async function stylistic(options = {}) {
295
+ const { arrowParens, blockSpacing, braceStyle, commaDangle, indent, quoteProps, quotes, overrides, semi } = {
296
+ ...StylisticConfigDefaults,
297
+ ...options
298
+ };
299
+ const pluginStylistic = await interopDefault(import("@stylistic/eslint-plugin"));
300
+ const config = pluginStylistic.configs.customize({
301
+ arrowParens,
302
+ blockSpacing,
303
+ braceStyle,
304
+ commaDangle,
305
+ indent,
306
+ jsx: false,
307
+ quoteProps,
308
+ pluginName: "style",
309
+ quotes,
310
+ semi
311
+ });
312
+ return [{
313
+ name: "rhapsodic/stylistic/rules",
314
+ plugins: { style: pluginStylistic },
315
+ rules: {
316
+ ...config.rules,
317
+ "style/array-bracket-newline": ["error", "consistent"],
318
+ "style/array-element-newline": ["error", "consistent"],
319
+ "style/function-call-argument-newline": ["error", "consistent"],
320
+ "style/function-call-spacing": ["error", "never"],
321
+ "style/function-paren-newline": ["error", "multiline"],
322
+ "style/lines-between-class-members": [
323
+ "error",
324
+ "always",
325
+ {
326
+ exceptAfterSingleLine: true,
327
+ exceptAfterOverload: true
328
+ }
329
+ ],
330
+ "style/multiline-comment-style": ["error", "separate-lines"],
331
+ "style/max-len": ["error", {
332
+ code: 120,
333
+ ignoreStrings: true,
334
+ ignoreComments: true,
335
+ ignoreTemplateLiterals: true,
336
+ ignoreRegExpLiterals: true
337
+ }],
338
+ "style/newline-per-chained-call": ["error", { ignoreChainWithDepth: 2 }],
339
+ "style/no-confusing-arrow": ["error", {
340
+ allowParens: true,
341
+ onlyOneSimpleParam: false
342
+ }],
343
+ "style/no-extra-semi": "error",
344
+ "style/nonblock-statement-body-position": ["error", "beside"],
345
+ "style/object-curly-newline": ["error", {
346
+ ObjectExpression: {
347
+ multiline: true,
348
+ minProperties: 4,
349
+ consistent: true
350
+ },
351
+ ObjectPattern: {
352
+ multiline: true,
353
+ consistent: true
354
+ },
355
+ ImportDeclaration: {
356
+ multiline: true,
357
+ minProperties: 4,
358
+ consistent: true
359
+ },
360
+ ExportDeclaration: {
361
+ multiline: true,
362
+ consistent: true
363
+ }
364
+ }],
365
+ "style/object-property-newline": ["error", { allowAllPropertiesOnSameLine: true }],
366
+ "style/one-var-declaration-per-line": ["error", "always"],
367
+ "style/semi-style": ["error", "last"],
368
+ "style/switch-colon-spacing": ["error", {
369
+ after: true,
370
+ before: false
371
+ }],
372
+ ...overrides
373
+ }
374
+ }];
375
+ }
376
+
377
+ //#endregion
378
+ //#region src/configs/typescript.ts
379
+ async function typescript(options = {}) {
380
+ const { componentExts = [], overrides = {}, parserOptions = {} } = options;
381
+ const files = options.files ?? [
382
+ GLOB_TS,
383
+ GLOB_TSX,
384
+ ...componentExts.map((ext) => `**/*.${ext}`)
385
+ ];
386
+ const tsconfigPath = options?.tsconfigPath ?? void 0;
387
+ const [pluginTs, parserTs] = await Promise.all([interopDefault(import("@typescript-eslint/eslint-plugin")), interopDefault(import("@typescript-eslint/parser"))]);
388
+ function makeParser(filesArray, ignores$1) {
389
+ return {
390
+ files: filesArray,
391
+ ...ignores$1 ? { ignores: ignores$1 } : {},
392
+ languageOptions: {
393
+ parser: parserTs,
394
+ parserOptions: {
395
+ extraFileExtensions: componentExts.map((ext) => `.${ext}`),
396
+ sourceType: "module",
397
+ projectService: {
398
+ allowDefaultProject: ["./*.js"],
399
+ defaultProject: tsconfigPath
400
+ },
401
+ tsconfigRootDir: process.cwd(),
402
+ ...parserOptions
403
+ }
404
+ },
405
+ name: "rhapsodic/typescript/parser"
406
+ };
407
+ }
408
+ return [
409
+ {
410
+ name: "rhapsodic/typescript/setup",
411
+ plugins: { ts: pluginTs }
412
+ },
413
+ makeParser(files),
414
+ {
415
+ files,
416
+ name: "rhapsodic/typescript/rules",
417
+ rules: {
418
+ ...renameRules(pluginTs.configs.recommended.rules, { "@typescript-eslint": "ts" }),
419
+ ...renameRules(pluginTs.configs["stylistic-type-checked"].rules, { "@typescript-eslint": "ts" }),
420
+ "ts/no-inferrable-types": "off",
421
+ "ts/no-shadow": ["error"],
422
+ "ts/no-unused-vars": ["error", {
423
+ args: "all",
424
+ argsIgnorePattern: "^_",
425
+ caughtErrors: "all",
426
+ caughtErrorsIgnorePattern: "^_",
427
+ destructuredArrayIgnorePattern: "^_",
428
+ varsIgnorePattern: "^_",
429
+ ignoreRestSiblings: true
430
+ }],
431
+ "ts/prefer-function-type": "off",
432
+ "ts/consistent-type-imports": ["error", {
433
+ disallowTypeAnnotations: false,
434
+ fixStyle: "separate-type-imports",
435
+ prefer: "type-imports"
436
+ }],
437
+ "ts/naming-convention": ["error", {
438
+ selector: "typeLike",
439
+ format: ["PascalCase"]
440
+ }],
441
+ ...overrides
442
+ }
443
+ }
444
+ ];
445
+ }
446
+
447
+ //#endregion
448
+ //#region src/configs/unicorn.ts
449
+ async function unicorn(options = {}) {
450
+ const { overrides = {} } = options;
451
+ return [{
452
+ name: "rhapsodic/unicorn/rules",
453
+ plugins: { unicorn: pluginUnicorn },
454
+ rules: {
455
+ ...pluginUnicorn.configs.recommended.rules,
456
+ "unicorn/consistent-function-scoping": "off",
457
+ "unicorn/explicit-length-check": "off",
458
+ "unicorn/filename-case": "off",
459
+ "unicorn/no-array-for-each": "off",
460
+ "unicorn/no-array-reduce": "off",
461
+ "unicorn/no-for-loop": "off",
462
+ "unicorn/no-null": "off",
463
+ "unicorn/no-static-only-class": "off",
464
+ "unicorn/prefer-switch": "off",
465
+ "unicorn/prefer-ternary": "off",
466
+ "unicorn/prevent-abbreviations": "off",
467
+ ...overrides
468
+ }
469
+ }];
470
+ }
471
+
472
+ //#endregion
473
+ //#region src/configs/vue.ts
474
+ async function vue(options = {}) {
475
+ const { a11y = false, files = [GLOB_VUE], overrides = {}, stylistic: stylistic$1 = true } = options;
476
+ const sfcBlocks = options.sfcBlocks === true ? {} : options.sfcBlocks ?? {};
477
+ const { indent = 2 } = typeof stylistic$1 === "boolean" ? {} : stylistic$1;
478
+ if (a11y) await ensurePackages(["eslint-plugin-vuejs-accessibility"]);
479
+ const [pluginVue, parserVue, processorVueBlocks, pluginVueA11y] = await Promise.all([
480
+ interopDefault(import("eslint-plugin-vue")),
481
+ interopDefault(import("vue-eslint-parser")),
482
+ interopDefault(import("eslint-processor-vue-blocks")),
483
+ ...a11y ? [interopDefault(import("eslint-plugin-vuejs-accessibility"))] : []
484
+ ]);
485
+ return [{
486
+ languageOptions: { globals: {
487
+ ...globals["shared-node-browser"],
488
+ computed: "readonly",
489
+ defineEmits: "readonly",
490
+ defineExpose: "readonly",
491
+ defineProps: "readonly",
492
+ onMounted: "readonly",
493
+ onUnmounted: "readonly",
494
+ reactive: "readonly",
495
+ ref: "readonly",
496
+ shallowReactive: "readonly",
497
+ shallowRef: "readonly",
498
+ toRef: "readonly",
499
+ toRefs: "readonly",
500
+ watch: "readonly",
501
+ watchEffect: "readonly"
502
+ } },
503
+ name: "rhapsodic/vue/setup",
504
+ plugins: {
505
+ vue: pluginVue,
506
+ ...a11y ? { "vue-a11y": pluginVueA11y } : {}
507
+ }
508
+ }, {
509
+ files,
510
+ languageOptions: {
511
+ parser: parserVue,
512
+ parserOptions: {
513
+ ecmaFeatures: { jsx: true },
514
+ extraFileExtensions: [".vue"],
515
+ parser: options.typescript ? await interopDefault(import("@typescript-eslint/parser")) : null,
516
+ sourceType: "module"
517
+ }
518
+ },
519
+ name: "rhapsodic/vue/rules",
520
+ processor: sfcBlocks === false ? pluginVue.processors[".vue"] : mergeProcessors([pluginVue.processors[".vue"], processorVueBlocks({
521
+ ...sfcBlocks,
522
+ blocks: {
523
+ styles: true,
524
+ ...sfcBlocks.blocks
525
+ }
526
+ })]),
527
+ rules: {
528
+ ...pluginVue.configs.base.rules,
529
+ ...pluginVue.configs["flat/essential"].map((c) => c.rules).reduce((acc, c) => ({
530
+ ...acc,
531
+ ...c
532
+ }), {}),
533
+ "vue/block-lang": ["error", { script: {
534
+ lang: "ts",
535
+ allowNoLang: false
536
+ } }],
537
+ "vue/camelcase": ["error", {
538
+ properties: "never",
539
+ ignoreDestructuring: false
540
+ }],
541
+ "vue/component-definition-name-casing": ["error", "PascalCase"],
542
+ "vue/eqeqeq": [
543
+ "error",
544
+ "always",
545
+ { null: "ignore" }
546
+ ],
547
+ "vue/html-button-has-type": ["error", {
548
+ button: true,
549
+ submit: true,
550
+ reset: false
551
+ }],
552
+ "vue/html-self-closing": ["error", { html: { void: "any" } }],
553
+ "vue/max-len": ["error", {
554
+ code: 120,
555
+ template: 120,
556
+ comments: 120,
557
+ ignoreUrls: true,
558
+ ignoreStrings: true,
559
+ ignoreHTMLAttributeValues: true,
560
+ ignoreHTMLTextContents: true
561
+ }],
562
+ "vue/multi-word-component-names": "off",
563
+ "vue/no-child-content": "error",
564
+ "vue/no-constant-condition": "warn",
565
+ "vue/no-duplicate-attributes": ["error", {
566
+ allowCoexistClass: true,
567
+ allowCoexistStyle: true
568
+ }],
569
+ "vue/no-empty-pattern": "error",
570
+ "vue/no-implicit-coercion": ["error", { allow: ["!!", "+"] }],
571
+ "vue/no-irregular-whitespace": "error",
572
+ "vue/no-loss-of-precision": "error",
573
+ "vue/no-potential-component-option-typo": "error",
574
+ "vue/no-sparse-arrays": "error",
575
+ "vue/no-template-target-blank": ["error", {
576
+ allowReferrer: true,
577
+ enforceDynamicLinks: "always"
578
+ }],
579
+ "vue/no-useless-concat": "error",
580
+ "vue/no-v-html": "off",
581
+ "vue/object-shorthand": [
582
+ "error",
583
+ "always",
584
+ {
585
+ ignoreConstructors: false,
586
+ avoidQuotes: true
587
+ }
588
+ ],
589
+ "vue/prefer-template": "error",
590
+ "vue/require-default-prop": "off",
591
+ ...stylistic$1 ? {
592
+ "@stylistic/max-len": "off",
593
+ "vue/array-bracket-newline": ["error", "consistent"],
594
+ "vue/array-bracket-spacing": ["error", "never"],
595
+ "vue/arrow-spacing": ["error", {
596
+ after: true,
597
+ before: true
598
+ }],
599
+ "vue/block-order": ["error", { order: [
600
+ "template",
601
+ "script",
602
+ "style"
603
+ ] }],
604
+ "vue/block-spacing": ["error", "always"],
605
+ "vue/brace-style": [
606
+ "error",
607
+ "1tbs",
608
+ { allowSingleLine: true }
609
+ ],
610
+ "vue/comma-dangle": ["error", {
611
+ arrays: "always-multiline",
612
+ objects: "always-multiline",
613
+ imports: "always-multiline",
614
+ exports: "always-multiline",
615
+ functions: "always-multiline"
616
+ }],
617
+ "vue/comma-spacing": ["error", {
618
+ before: false,
619
+ after: true
620
+ }],
621
+ "vue/comma-style": [
622
+ "error",
623
+ "last",
624
+ { exceptions: {
625
+ ArrayExpression: false,
626
+ ArrayPattern: false,
627
+ ArrowFunctionExpression: false,
628
+ CallExpression: false,
629
+ FunctionDeclaration: false,
630
+ FunctionExpression: false,
631
+ ImportDeclaration: false,
632
+ ObjectExpression: false,
633
+ ObjectPattern: false,
634
+ VariableDeclaration: false,
635
+ NewExpression: false
636
+ } }
637
+ ],
638
+ "vue/dot-location": ["error", "property"],
639
+ "vue/dot-notation": ["error", { allowKeywords: true }],
640
+ "vue/first-attribute-linebreak": "off",
641
+ "vue/func-call-spacing": ["error", "never"],
642
+ "vue/html-closing-bracket-spacing": ["error", {
643
+ startTag: "never",
644
+ endTag: "never",
645
+ selfClosingTag: "always"
646
+ }],
647
+ "vue/html-closing-bracket-newline": ["error", {
648
+ singleline: "never",
649
+ multiline: "always",
650
+ selfClosingTag: {
651
+ singleline: "never",
652
+ multiline: "always"
653
+ }
654
+ }],
655
+ "vue/html-indent": ["error", indent],
656
+ "vue/key-spacing": ["error", {
657
+ beforeColon: false,
658
+ afterColon: true
659
+ }],
660
+ "vue/keyword-spacing": ["error", {
661
+ before: true,
662
+ after: true,
663
+ overrides: {
664
+ return: { after: true },
665
+ throw: { after: true },
666
+ case: { after: true }
667
+ }
668
+ }],
669
+ "vue/max-attributes-per-line": ["error", {
670
+ singleline: { max: 3 },
671
+ multiline: { max: 1 }
672
+ }],
673
+ "vue/multiline-html-element-content-newline": ["error", {
674
+ ignoreWhenEmpty: true,
675
+ ignores: [
676
+ "nuxt-link",
677
+ "nuxt-link-locale",
678
+ "pre",
679
+ "textarea",
680
+ "a",
681
+ "abbr",
682
+ "audio",
683
+ "b",
684
+ "bdi",
685
+ "bdo",
686
+ "canvas",
687
+ "cite",
688
+ "code",
689
+ "data",
690
+ "del",
691
+ "dfn",
692
+ "em",
693
+ "i",
694
+ "iframe",
695
+ "ins",
696
+ "kbd",
697
+ "label",
698
+ "map",
699
+ "mark",
700
+ "noscript",
701
+ "object",
702
+ "output",
703
+ "picture",
704
+ "q",
705
+ "ruby",
706
+ "s",
707
+ "samp",
708
+ "small",
709
+ "span",
710
+ "strong",
711
+ "sub",
712
+ "sup",
713
+ "svg",
714
+ "time",
715
+ "u",
716
+ "var",
717
+ "video"
718
+ ],
719
+ allowEmptyLines: false
720
+ }],
721
+ "vue/no-extra-parens": [
722
+ "off",
723
+ "all",
724
+ {
725
+ conditionalAssign: true,
726
+ nestedBinaryExpressions: false,
727
+ returnAssign: false,
728
+ ignoreJSX: "all",
729
+ enforceForArrowConditionals: false
730
+ }
731
+ ],
732
+ "vue/no-multi-spaces": "error",
733
+ "vue/no-spaces-around-equal-signs-in-attribute": "error",
734
+ "vue/object-curly-newline": ["error", {
735
+ ObjectExpression: {
736
+ multiline: true,
737
+ consistent: true
738
+ },
739
+ ObjectPattern: {
740
+ multiline: true,
741
+ consistent: true
742
+ },
743
+ ImportDeclaration: {
744
+ multiline: true,
745
+ minProperties: 4,
746
+ consistent: true
747
+ },
748
+ ExportDeclaration: {
749
+ multiline: true,
750
+ consistent: true
751
+ }
752
+ }],
753
+ "vue/object-curly-spacing": ["error", "always"],
754
+ "vue/object-property-newline": ["error", { allowAllPropertiesOnSameLine: true }],
755
+ "vue/operator-linebreak": [
756
+ "error",
757
+ "before",
758
+ { overrides: { "=": "none" } }
759
+ ],
760
+ "vue/quote-props": [
761
+ "error",
762
+ "as-needed",
763
+ {
764
+ keywords: false,
765
+ unnecessary: true,
766
+ numbers: false
767
+ }
768
+ ],
769
+ "vue/singleline-html-element-content-newline": "off",
770
+ "vue/space-in-parens": ["error", "never"],
771
+ "vue/space-infix-ops": "error",
772
+ "vue/space-unary-ops": ["error", {
773
+ words: true,
774
+ nonwords: false,
775
+ overrides: {}
776
+ }],
777
+ "vue/template-curly-spacing": "error"
778
+ } : {},
779
+ ...a11y ? {
780
+ "vue-a11y/alt-text": ["error", {
781
+ elements: [
782
+ "img",
783
+ "object",
784
+ "area",
785
+ "input[type=\"image\"]"
786
+ ],
787
+ img: [],
788
+ object: [],
789
+ area: [],
790
+ "input[type=\"image\"]": []
791
+ }],
792
+ "vue-a11y/anchor-has-content": ["error", {
793
+ components: [],
794
+ accessibleChildren: [],
795
+ accessibleDirectives: []
796
+ }],
797
+ "vue-a11y/aria-props": "error",
798
+ "vue-a11y/aria-role": ["error", { ignoreNonDOM: false }],
799
+ "vue-a11y/aria-unsupported-elements": "error",
800
+ "vue-a11y/click-events-have-key-events": "error",
801
+ "vue-a11y/form-control-has-label": "off",
802
+ "vue-a11y/heading-has-content": "error",
803
+ "vue-a11y/iframe-has-title": "error",
804
+ "vue-a11y/interactive-supports-focus": "error",
805
+ "vue-a11y/label-has-for": "off",
806
+ "vue-a11y/media-has-caption": "error",
807
+ "vue-a11y/mouse-events-have-key-events": "error",
808
+ "vue-a11y/no-access-key": "error",
809
+ "vue-a11y/no-autofocus": ["error", { ignoreNonDOM: true }],
810
+ "vue-a11y/no-distracting-elements": ["error", { elements: ["marquee", "blink"] }],
811
+ "vue-a11y/no-redundant-roles": "error",
812
+ "vue-a11y/no-static-element-interactions": "error",
813
+ "vue-a11y/role-has-required-aria-props": "error",
814
+ "vue-a11y/tabindex-no-positive": "error"
815
+ } : {},
816
+ ...overrides
817
+ }
818
+ }];
819
+ }
820
+
821
+ //#endregion
822
+ //#region src/factory.ts
823
+ const flatConfigProps = [
824
+ "name",
825
+ "languageOptions",
826
+ "linterOptions",
827
+ "processor",
828
+ "plugins",
829
+ "rules",
830
+ "settings"
831
+ ];
832
+ const VuePackages = ["vue", "nuxt"];
833
+ /**
834
+ * Construct an array of ESLint flat config items.
835
+ *
836
+ * @param {OptionsConfig & TypedFlatConfigItem} options
837
+ * The options for generating the ESLint configurations.
838
+ * @param {Awaitable<TypedFlatConfigItem | TypedFlatConfigItem[]>[]} userConfigs
839
+ * The user configurations to be merged with the generated configurations.
840
+ * @returns {Promise<TypedFlatConfigItem[]>}
841
+ * The merged ESLint configurations.
842
+ */
843
+ function rhapsodic(options = {}, ...userConfigs) {
844
+ const { componentExts = [], ignores: userIgnores = [], imports: enableImports = true, typescript: enableTypeScript = isPackageExists("typescript"), unicorn: enableUnicorn = true, vue: enableVue = VuePackages.some((i) => isPackageExists(i)) } = options;
845
+ const stylisticOptions = options.stylistic === false ? false : typeof options.stylistic === "object" ? options.stylistic : {};
846
+ const configs = [];
847
+ const typescriptOptions = resolveSubOptions(options, "typescript");
848
+ configs.push(ignores(userIgnores), javascript({ overrides: getOverrides(options, "javascript") }));
849
+ if (enableImports) configs.push(imports(enableImports === true ? {} : { ...enableImports }));
850
+ if (enableUnicorn) configs.push(unicorn(enableUnicorn === true ? {} : enableUnicorn));
851
+ if (enableVue) componentExts.push("vue");
852
+ if (enableTypeScript) configs.push(typescript({
853
+ ...typescriptOptions,
854
+ componentExts,
855
+ overrides: getOverrides(options, "typescript")
856
+ }));
857
+ if (stylisticOptions) configs.push(stylistic({
858
+ ...stylisticOptions,
859
+ overrides: getOverrides(options, "stylistic")
860
+ }));
861
+ if (enableVue) configs.push(vue({
862
+ ...resolveSubOptions(options, "vue"),
863
+ overrides: getOverrides(options, "vue"),
864
+ stylistic: stylisticOptions,
865
+ typescript: !!enableTypeScript
866
+ }));
867
+ configs.push(disables());
868
+ if ("files" in options) throw new Error("[@rhapsodic/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.");
869
+ const fusedConfig = flatConfigProps.reduce((acc, key) => {
870
+ if (key in options) acc[key] = options[key];
871
+ return acc;
872
+ }, {});
873
+ if (Object.keys(fusedConfig).length) configs.push([fusedConfig]);
874
+ let composer = new FlatConfigComposer();
875
+ composer = composer.append(...configs, ...userConfigs);
876
+ return composer;
877
+ }
878
+ function resolveSubOptions(options, key) {
879
+ return typeof options[key] === "boolean" ? {} : options[key] || {};
880
+ }
881
+ function getOverrides(options, key) {
882
+ const sub = resolveSubOptions(options, key);
883
+ return {
884
+ ...options.overrides?.[key],
885
+ ..."overrides" in sub ? sub.overrides : {}
886
+ };
887
+ }
888
+
889
+ //#endregion
890
+ export { GLOB_ALL_SRC, GLOB_EXCLUDE, GLOB_JS, GLOB_JSX, GLOB_SRC, GLOB_SRC_EXT, GLOB_TESTS, GLOB_TS, GLOB_TSX, GLOB_VUE, StylisticConfigDefaults, combine, rhapsodic as default, rhapsodic, disables, ensurePackages, getOverrides, ignores, imports, interopDefault, isPackageInScope, javascript, parserPlain, renameRules, resolveSubOptions, stylistic, typescript, unicorn, vue };