@favorodera/eslint-config 0.0.1 → 0.0.2

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 CHANGED
@@ -1,50 +1,13 @@
1
- import { FlatConfigComposer } from "eslint-flat-config-utils";
1
+ import { FlatConfigComposer, renamePluginsInRules } from "eslint-flat-config-utils";
2
2
  import { defu } from "defu";
3
3
  import { mergeProcessors, processorPassThrough } from "eslint-merge-processors";
4
+ import vueBlocksProcessor from "eslint-processor-vue-blocks";
4
5
  import globals from "globals";
5
- //#region src/utils.ts
6
- /**
7
- * Return the default export from a module-like object.
8
- * If the module has no default export, return the original resolved value.
9
- *
10
- * @param module - module or promise resolving to a module
11
- * @returns resolved module default or original module
12
- */
13
- async function getModuleDefault(module) {
14
- const resolvedModule = await module;
15
- return resolvedModule.default || resolvedModule;
16
- }
17
- /**
18
- * Normalize boolean or options value into merged options or null.
19
- * Returns null when the input is false or undefined.
20
- *
21
- * @param value - boolean, options object, or undefined
22
- * @param defaults - defaults to merge with
23
- * @returns merged options or false
24
- */
25
- function resolveOptions(value, defaults) {
26
- if (!value) return false;
27
- return defu(value === true ? {} : value, defaults);
28
- }
29
- /**
30
- * Rename rules by replacing configured plugin prefixes.
31
- *
32
- * @param rules - rules object to rename
33
- * @param map - prefix map used for renaming
34
- * @returns renamed rules object
35
- */
36
- function renameRules(rules, map) {
37
- return Object.fromEntries(Object.entries(rules).map(([key, value]) => {
38
- for (const [from, to] of Object.entries(map)) if (key.startsWith(`${from}/`)) return [to + key.slice(from.length), value];
39
- return [key, value];
40
- }));
41
- }
42
- //#endregion
43
6
  //#region src/globs.ts
44
7
  /** Glob pattern for JavaScript linting */
45
- const jsGlob = "**/*.?([cm])js";
8
+ const jsGlob = "**/*.{js,cjs,mjs}";
46
9
  /** Glob pattern for TypeScript linting */
47
- const tsGlob = "**/*.?([cm])ts";
10
+ const tsGlob = "**/*.{ts,cts,mts}";
48
11
  /** Glob pattern for Vue linting */
49
12
  const vueGlob = "**/*.vue";
50
13
  /** Glob pattern for Markdown linting */
@@ -54,8 +17,8 @@ const mdGlob = "**/*.md";
54
17
  * Includes common build outputs, dependencies, caches, and temporary files
55
18
  */
56
19
  const ignoresGlob = [
57
- "**/node_modules",
58
- "**/dist",
20
+ "**/node_modules/**",
21
+ "**/dist/**",
59
22
  "**/package-lock.json",
60
23
  "**/yarn.lock",
61
24
  "**/pnpm-lock.yaml",
@@ -91,6 +54,35 @@ const ignoresGlob = [
91
54
  "**/.*/skills"
92
55
  ];
93
56
  //#endregion
57
+ //#region src/utils.ts
58
+ /**
59
+ * Resolves a module (or a promise of one) and returns its default export
60
+ * if present, otherwise returns the module itself.
61
+ *
62
+ * Handles both ESM modules (which wrap exports under `.default`) and
63
+ * CJS / plain-object modules uniformly.
64
+ *
65
+ * @param module - A module or a promise that resolves to one
66
+ * @returns The `.default` export if it exists, otherwise the module itself
67
+ */
68
+ async function importModule(module) {
69
+ const resolved = await module;
70
+ if (resolved !== null && typeof resolved === "object" && "default" in resolved) return resolved.default;
71
+ return resolved;
72
+ }
73
+ /**
74
+ * Normalize boolean or options value into merged options or null.
75
+ * Returns null when the input is false or undefined.
76
+ *
77
+ * @param value - boolean, options object, or undefined
78
+ * @param defaults - defaults to merge with
79
+ * @returns merged options or false
80
+ */
81
+ function resolveOptions(value, defaults) {
82
+ if (!value) return false;
83
+ return defu(value === true ? {} : value, defaults);
84
+ }
85
+ //#endregion
94
86
  //#region src/configs/vue.ts
95
87
  const sfcBlocksDefaults = { blocks: {
96
88
  styles: true,
@@ -101,22 +93,21 @@ const vueDefaults = {
101
93
  files: [vueGlob],
102
94
  sfcBlocks: sfcBlocksDefaults
103
95
  };
104
- /**
105
- * Vue SFC linting via `vue-eslint-parser`, `eslint-plugin-vue`, `typescript-eslint(parser)`.
106
- * @param options - Vue configuration options
107
- * @returns Promise resolving to Vue ESLint config items
108
- */
109
96
  async function vue(options) {
110
97
  const resolved = defu(options, vueDefaults);
111
98
  const sfcBlocks = resolveOptions(resolved.sfcBlocks, sfcBlocksDefaults);
112
- const [vuePlugin, vueParser, tsEsLint, vueBlocksProcessor] = await Promise.all([
113
- getModuleDefault(import("eslint-plugin-vue")),
114
- getModuleDefault(import("vue-eslint-parser")),
115
- getModuleDefault(import("typescript-eslint")),
116
- getModuleDefault(import("eslint-processor-vue-blocks"))
99
+ const [vuePlugin, vueParser, tsEsLint] = await Promise.all([
100
+ importModule(import("eslint-plugin-vue")),
101
+ importModule(import("vue-eslint-parser")),
102
+ importModule(import("typescript-eslint"))
117
103
  ]);
104
+ const inheritedRules = Object.assign({}, ...[
105
+ ...vuePlugin.configs["flat/essential"],
106
+ ...vuePlugin.configs["flat/strongly-recommended"],
107
+ ...vuePlugin.configs["flat/recommended"]
108
+ ].map((config) => config?.rules || {}));
118
109
  return [{
119
- name: "favorodera/vue/rules",
110
+ name: "favorodera/vue",
120
111
  plugins: { vue: vuePlugin },
121
112
  files: resolved.files,
122
113
  languageOptions: {
@@ -145,19 +136,7 @@ async function vue(options) {
145
136
  },
146
137
  processor: sfcBlocks === false ? vuePlugin.processors[".vue"] : mergeProcessors([vuePlugin.processors[".vue"], vueBlocksProcessor(sfcBlocks)]),
147
138
  rules: {
148
- ...vuePlugin.configs.base.rules,
149
- ...vuePlugin.configs["flat/essential"].map((config) => config.rules).reduce((accumulator, currentConfig) => ({
150
- ...accumulator,
151
- ...currentConfig
152
- }), {}),
153
- ...vuePlugin.configs["flat/strongly-recommended"].map((config) => config.rules).reduce((accumulator, currentConfig) => ({
154
- ...accumulator,
155
- ...currentConfig
156
- }), {}),
157
- ...vuePlugin.configs["flat/recommended"].map((config) => config.rules).reduce((accumulator, currentConfig) => ({
158
- ...accumulator,
159
- ...currentConfig
160
- }), {}),
139
+ ...inheritedRules,
161
140
  "vue/block-order": ["error", { order: [
162
141
  "script",
163
142
  "template",
@@ -179,43 +158,6 @@ async function vue(options) {
179
158
  }];
180
159
  }
181
160
  //#endregion
182
- //#region src/configs/typescript.ts
183
- const typescriptDefaults = { files: [tsGlob] };
184
- /**
185
- * Extract and merge rules from a compatible config array.
186
- * @param configs - Array of compatible ESLint configs
187
- * @returns Merged rules object from all configs
188
- */
189
- function extractRules(configs) {
190
- return Object.assign({}, ...configs.map((config) => config.rules || {}));
191
- }
192
- /**
193
- * Typescript linting via `typescript-eslint`.
194
- * @param options - TypeScript configuration options
195
- * @returns Promise resolving to TypeScript ESLint config items
196
- */
197
- async function typescript(options) {
198
- const resolved = defu(options, typescriptDefaults);
199
- const tsEsLint = await getModuleDefault(import("typescript-eslint"));
200
- return [{
201
- name: "favorodera/typescript/rules",
202
- files: resolved.files,
203
- plugins: { ts: tsEsLint.plugin },
204
- languageOptions: {
205
- parser: tsEsLint.parser,
206
- parserOptions: { sourceType: "module" }
207
- },
208
- rules: {
209
- ...renameRules(extractRules(tsEsLint.configs.strict), { "@typescript-eslint": "ts" }),
210
- ...renameRules(extractRules(tsEsLint.configs.stylistic), { "@typescript-eslint": "ts" }),
211
- "ts/consistent-type-imports": ["error", { prefer: "type-imports" }],
212
- "ts/no-empty-object-type": ["error", { allowInterfaces: "with-single-extends" }],
213
- "ts/array-type": ["error", { default: "array" }],
214
- ...resolved.overrides
215
- }
216
- }];
217
- }
218
- //#endregion
219
161
  //#region src/configs/ignores.ts
220
162
  const defaultPatterns = ignoresGlob;
221
163
  /**
@@ -230,141 +172,25 @@ function ignores(patterns = []) {
230
172
  }];
231
173
  }
232
174
  //#endregion
233
- //#region src/configs/stylistic.ts
234
- const stylisticDefaults = {
235
- indent: 2,
236
- experimental: false,
237
- quotes: "single",
238
- semi: false,
239
- jsx: false
240
- };
241
- /**
242
- * Code style via `@stylistic/eslint-plugin`.
243
- * @param options - Stylistic configuration options
244
- * @returns Promise resolving to stylistic ESLint config items
245
- */
246
- async function stylistic(options) {
247
- const resolved = defu(options, stylisticDefaults);
248
- const stylePlugin = await getModuleDefault(import("@stylistic/eslint-plugin"));
249
- const customizedStyleConfig = stylePlugin.configs.customize({
250
- pluginName: "style",
251
- ...resolved
252
- });
253
- return [{
254
- name: "favorodera/stylistic/rules",
255
- plugins: { style: stylePlugin },
256
- rules: {
257
- ...customizedStyleConfig.rules,
258
- "style/quotes": [
259
- "error",
260
- "single",
261
- { avoidEscape: true }
262
- ],
263
- "style/no-multiple-empty-lines": ["error", {
264
- max: 2,
265
- maxEOF: 2,
266
- maxBOF: 0
267
- }],
268
- "style/padded-blocks": "off",
269
- "style/no-trailing-spaces": ["error", { skipBlankLines: true }],
270
- "style/brace-style": "off",
271
- "style/generator-star-spacing": ["error", {
272
- after: true,
273
- before: false
274
- }],
275
- "style/yield-star-spacing": ["error", {
276
- after: true,
277
- before: false
278
- }],
279
- ...resolved.overrides
280
- }
281
- }];
282
- }
283
- //#endregion
284
- //#region src/configs/tailwind.ts
285
- const tailwindDefaults = {
286
- files: [
287
- jsGlob,
288
- tsGlob,
289
- vueGlob
290
- ],
291
- settings: { detectComponentClasses: true }
292
- };
293
- /**
294
- * Tailwind linting via `eslint-plugin-better-tailwindcss`.
295
- * @param options - Tailwind configuration options
296
- * @returns Promise resolving to Tailwind ESLint config items
297
- */
298
- async function tailwind(options) {
299
- const resolved = defu(options, tailwindDefaults);
300
- const tailwindPlugin = await getModuleDefault(import("eslint-plugin-better-tailwindcss"));
301
- return [{
302
- name: "favorodera/tailwind/rules",
303
- plugins: { tailwind: tailwindPlugin },
304
- files: resolved.files,
305
- settings: { tailwindcss: resolved.settings },
306
- rules: {
307
- ...renameRules(tailwindPlugin.configs["recommended-error"].rules, { "better-tailwindcss": "tailwind" }),
308
- ...renameRules(tailwindPlugin.configs["stylistic-error"].rules, { "better-tailwindcss": "tailwind" }),
309
- "tailwind/no-unregistered-classes": "off",
310
- "tailwind/enforce-consistent-line-wrapping": ["error", { group: "emptyLine" }],
311
- ...resolved.overrides
312
- }
313
- }];
314
- }
315
- //#endregion
316
- //#region src/configs/comments.ts
317
- const commentsDefaults = { files: [
318
- jsGlob,
319
- tsGlob,
320
- vueGlob
321
- ] };
322
- /**
323
- * Comments linting via `@eslint-community/eslint-plugin-eslint-comments`
324
- * @param options - Comments configuration options
325
- * @returns Promise resolving to comments ESLint config items
326
- */
327
- async function comments(options) {
328
- const resolved = defu(options, commentsDefaults);
329
- const commentsPlugin = await getModuleDefault(import("@eslint-community/eslint-plugin-eslint-comments"));
330
- return [{
331
- name: "favorodera/comments/rules",
332
- files: resolved.files,
333
- plugins: { comments: commentsPlugin },
334
- rules: {
335
- ...renameRules(commentsPlugin.configs.recommended?.rules || {}, { "@eslint-community/eslint-comments": "comments" }),
336
- "comments/no-aggregating-enable": "error",
337
- "comments/no-duplicate-disable": "error",
338
- "comments/no-unlimited-disable": "error",
339
- "comments/no-unused-enable": "error",
340
- ...resolved.overrides
341
- }
342
- }];
343
- }
344
- //#endregion
345
175
  //#region src/configs/imports.ts
346
176
  const importsDefaults = { files: [
347
177
  jsGlob,
348
178
  tsGlob,
349
179
  vueGlob
350
180
  ] };
351
- /**
352
- * Imports & unused imports linting via `eslint-plugin-import-lite` and `eslint-plugin-unused-imports`.
353
- * @param options - Imports & unused imports configuration options
354
- * @returns Promise resolving to imports & unused imports ESLint config items
355
- */
356
181
  async function imports(options) {
357
182
  const resolved = defu(options, importsDefaults);
358
- const [importPlugin, unusedImportsPlugin] = await Promise.all([getModuleDefault(import("eslint-plugin-import-lite")), getModuleDefault(import("eslint-plugin-unused-imports"))]);
183
+ const [importPlugin, unusedImportsPlugin] = await Promise.all([importModule(import("eslint-plugin-import-lite")), importModule(import("eslint-plugin-unused-imports"))]);
184
+ const inheritedRules = importPlugin.configs.recommended?.rules || {};
359
185
  return [{
360
- name: "favorodera/imports/rules",
186
+ name: "favorodera/imports",
361
187
  files: resolved.files,
362
188
  plugins: {
363
189
  "import": importPlugin,
364
190
  "unused-imports": unusedImportsPlugin
365
191
  },
366
192
  rules: {
367
- ...renameRules(importPlugin.configs.recommended?.rules || {}, { "import-lite": "import" }),
193
+ ...renamePluginsInRules(inheritedRules, { "import-lite": "import" }),
368
194
  "import/consistent-type-specifier-style": ["error", "top-level"],
369
195
  "import/first": "error",
370
196
  "import/no-duplicates": "error",
@@ -384,34 +210,6 @@ async function imports(options) {
384
210
  }];
385
211
  }
386
212
  //#endregion
387
- //#region src/configs/markdown.ts
388
- const markdownDefaults = {
389
- files: [mdGlob],
390
- gfm: true
391
- };
392
- /**
393
- * Markdown linting via `@eslint/markdown`.
394
- * @param options - Markdown configuration options
395
- * @returns Promise resolving to Markdown ESLint config items
396
- */
397
- async function markdown(options) {
398
- const resolved = defu(options, markdownDefaults);
399
- const markdownPlugin = await getModuleDefault(import("@eslint/markdown"));
400
- return [{
401
- name: "favorodera/markdown/rules",
402
- files: resolved.files,
403
- plugins: { md: markdownPlugin },
404
- processor: mergeProcessors([markdownPlugin.processors?.markdown, processorPassThrough]),
405
- language: resolved.gfm ? "md/gfm" : "md/commonmark",
406
- rules: {
407
- ...renameRules(markdownPlugin.configs.recommended[0]?.rules || {}, { markdown: "md" }),
408
- "md/fenced-code-language": "off",
409
- "md/no-missing-label-refs": "off",
410
- ...resolved.overrides
411
- }
412
- }];
413
- }
414
- //#endregion
415
213
  //#region src/configs/javascript.ts
416
214
  const javascriptDefaults = { files: [
417
215
  jsGlob,
@@ -425,9 +223,9 @@ const javascriptDefaults = { files: [
425
223
  */
426
224
  async function javascript(options) {
427
225
  const resolved = defu(options, javascriptDefaults);
428
- const jsPlugin = await getModuleDefault(import("@eslint/js"));
226
+ const inheritedRules = (await importModule(import("@eslint/js"))).configs.recommended.rules;
429
227
  return [{
430
- name: "favorodera/javascript/rules",
228
+ name: "favorodera/javascript",
431
229
  files: resolved.files,
432
230
  languageOptions: {
433
231
  ecmaVersion: "latest",
@@ -442,189 +240,156 @@ async function javascript(options) {
442
240
  }
443
241
  },
444
242
  linterOptions: { reportUnusedDisableDirectives: true },
445
- plugins: { js: jsPlugin },
446
243
  rules: {
447
- ...jsPlugin.configs.recommended.rules,
244
+ ...inheritedRules,
448
245
  "accessor-pairs": ["error", {
449
246
  enforceForClassMembers: true,
450
247
  setWithoutGet: true
451
248
  }],
452
249
  "array-callback-return": "error",
453
250
  "block-scoped-var": "error",
454
- "constructor-super": "error",
455
- "default-case-last": "error",
456
- "dot-notation": ["error", { allowKeywords: true }],
457
- "eqeqeq": ["error", "smart"],
458
- "new-cap": ["error", {
459
- capIsNew: false,
460
- newIsCap: true,
461
- properties: true
462
- }],
463
- "no-alert": "error",
464
- "no-array-constructor": "error",
465
- "no-async-promise-executor": "error",
466
- "no-caller": "error",
467
- "no-case-declarations": "error",
468
- "no-class-assign": "error",
469
- "no-compare-neg-zero": "error",
470
- "no-cond-assign": ["error", "always"],
471
- "no-console": ["error", { allow: ["warn", "error"] }],
472
- "no-const-assign": "error",
473
- "no-control-regex": "error",
474
- "no-debugger": "error",
475
- "no-delete-var": "error",
476
- "no-dupe-args": "error",
477
- "no-dupe-class-members": "error",
478
- "no-dupe-keys": "error",
479
- "no-duplicate-case": "error",
480
- "no-empty": ["error", { allowEmptyCatch: true }],
481
- "no-empty-character-class": "error",
482
- "no-empty-pattern": "error",
483
- "no-eval": "error",
484
- "no-ex-assign": "error",
485
- "no-extend-native": "error",
486
- "no-extra-bind": "error",
487
- "no-extra-boolean-cast": "error",
488
- "no-fallthrough": "error",
489
- "no-func-assign": "error",
490
- "no-global-assign": "error",
491
- "no-implied-eval": "error",
492
- "no-import-assign": "error",
493
- "no-invalid-regexp": "error",
494
- "no-irregular-whitespace": "error",
495
- "no-iterator": "error",
496
- "no-labels": ["error", {
497
- allowLoop: false,
498
- allowSwitch: false
499
- }],
500
- "no-lone-blocks": "error",
501
- "no-loss-of-precision": "error",
502
- "no-misleading-character-class": "error",
503
- "no-multi-str": "error",
504
- "no-new": "error",
505
- "no-new-func": "error",
506
- "no-new-native-nonconstructor": "error",
507
- "no-new-wrappers": "error",
508
- "no-obj-calls": "error",
509
- "no-octal": "error",
510
- "no-octal-escape": "error",
511
- "no-proto": "error",
512
- "no-prototype-builtins": "error",
513
- "no-redeclare": ["error", { builtinGlobals: false }],
514
- "no-regex-spaces": "error",
515
- "no-restricted-globals": [
516
- "error",
517
- {
518
- message: "Use `globalThis` instead.",
519
- name: "global"
520
- },
521
- {
522
- message: "Use `globalThis` instead.",
523
- name: "self"
524
- }
525
- ],
526
- "no-restricted-properties": [
527
- "error",
528
- {
529
- message: "Use `Object.getPrototypeOf` or `Object.setPrototypeOf` instead.",
530
- property: "__proto__"
531
- },
532
- {
533
- message: "Use `Object.defineProperty` instead.",
534
- property: "__defineGetter__"
535
- },
536
- {
537
- message: "Use `Object.defineProperty` instead.",
538
- property: "__defineSetter__"
539
- },
540
- {
541
- message: "Use `Object.getOwnPropertyDescriptor` instead.",
542
- property: "__lookupGetter__"
543
- },
544
- {
545
- message: "Use `Object.getOwnPropertyDescriptor` instead.",
546
- property: "__lookupSetter__"
547
- }
548
- ],
549
- "no-restricted-syntax": [
550
- "error",
551
- "TSEnumDeclaration[const=true]",
552
- "TSExportAssignment"
553
- ],
554
- "no-self-assign": ["error", { props: true }],
555
- "no-self-compare": "error",
556
- "no-sequences": "error",
557
- "no-shadow-restricted-names": "error",
558
- "no-sparse-arrays": "error",
559
- "no-template-curly-in-string": "error",
560
- "no-this-before-super": "error",
561
- "no-throw-literal": "error",
562
- "no-undef": "error",
563
- "no-undef-init": "error",
564
- "no-unexpected-multiline": "error",
565
- "no-unmodified-loop-condition": "error",
566
- "no-unneeded-ternary": ["error", { defaultAssignment: false }],
567
- "no-unreachable": "error",
568
- "no-unreachable-loop": "error",
569
- "no-unsafe-finally": "error",
570
- "no-unsafe-negation": "error",
571
- "no-unused-expressions": ["error", {
572
- allowShortCircuit: true,
573
- allowTaggedTemplates: true,
574
- allowTernary: true
575
- }],
576
- "no-unused-vars": ["error", {
577
- args: "none",
578
- caughtErrors: "none",
579
- ignoreRestSiblings: true,
580
- vars: "all"
581
- }],
582
- "no-use-before-define": ["error", {
583
- classes: false,
584
- functions: false,
585
- variables: true
586
- }],
587
- "no-useless-backreference": "error",
588
- "no-useless-call": "error",
589
- "no-useless-catch": "error",
590
- "no-useless-computed-key": "error",
591
- "no-useless-constructor": "error",
592
- "no-useless-rename": "error",
593
- "no-useless-return": "error",
594
- "no-var": "error",
595
- "no-with": "error",
596
- "object-shorthand": [
251
+ ...resolved.overrides
252
+ }
253
+ }];
254
+ }
255
+ //#endregion
256
+ //#region src/configs/markdown.ts
257
+ const markdownDefaults = {
258
+ files: [mdGlob],
259
+ gfm: true
260
+ };
261
+ async function markdown(options) {
262
+ const resolved = defu(options, markdownDefaults);
263
+ const markdownPlugin = await importModule(import("@eslint/markdown"));
264
+ const inheritedRules = Object.assign({}, ...[...markdownPlugin.configs.recommended].map((config) => config?.rules || {}));
265
+ return [{
266
+ name: "favorodera/markdown",
267
+ files: resolved.files,
268
+ plugins: { md: markdownPlugin },
269
+ processor: mergeProcessors([markdownPlugin.processors?.markdown, processorPassThrough]),
270
+ language: resolved.gfm ? "md/gfm" : "md/commonmark",
271
+ rules: {
272
+ ...renamePluginsInRules(inheritedRules, { markdown: "md" }),
273
+ "md/fenced-code-language": "off",
274
+ "md/no-missing-label-refs": "off",
275
+ ...resolved.overrides
276
+ }
277
+ }];
278
+ }
279
+ //#endregion
280
+ //#region src/configs/stylistic.ts
281
+ const stylisticDefaults = {
282
+ settings: {
283
+ indent: 2,
284
+ experimental: false,
285
+ quotes: "single",
286
+ semi: false,
287
+ jsx: false
288
+ },
289
+ files: [
290
+ jsGlob,
291
+ tsGlob,
292
+ vueGlob
293
+ ]
294
+ };
295
+ async function stylistic(options) {
296
+ const resolved = defu(options, stylisticDefaults);
297
+ const config = (await importModule(import("@stylistic/eslint-plugin"))).configs.customize({
298
+ pluginName: "style",
299
+ ...resolved.settings
300
+ });
301
+ const inheritedRules = config?.rules || {};
302
+ return [{
303
+ ...config,
304
+ name: "favorodera/stylistic",
305
+ files: resolved.files,
306
+ rules: {
307
+ ...inheritedRules,
308
+ "style/quotes": [
597
309
  "error",
598
- "always",
599
- {
600
- avoidQuotes: true,
601
- ignoreConstructors: false
602
- }
310
+ "single",
311
+ { avoidEscape: true }
603
312
  ],
604
- "one-var": ["error", { initialized: "never" }],
605
- "prefer-arrow-callback": ["error", {
606
- allowNamedFunctions: false,
607
- allowUnboundThis: true
313
+ "style/no-multiple-empty-lines": ["error", {
314
+ max: 2,
315
+ maxEOF: 2,
316
+ maxBOF: 0
608
317
  }],
609
- "prefer-const": ["error", {
610
- destructuring: "all",
611
- ignoreReadBeforeAssign: true
318
+ "style/padded-blocks": "off",
319
+ "style/no-trailing-spaces": ["error", { skipBlankLines: true }],
320
+ "style/brace-style": "off",
321
+ "style/generator-star-spacing": ["error", {
322
+ after: true,
323
+ before: false
612
324
  }],
613
- "prefer-exponentiation-operator": "error",
614
- "prefer-promise-reject-errors": "error",
615
- "prefer-regex-literals": ["error", { disallowRedundantWrapping: true }],
616
- "prefer-rest-params": "error",
617
- "prefer-spread": "error",
618
- "prefer-template": "error",
619
- "symbol-description": "error",
620
- "unicode-bom": ["error", "never"],
621
- "use-isnan": ["error", {
622
- enforceForIndexOf: true,
623
- enforceForSwitchCase: true
325
+ "style/yield-star-spacing": ["error", {
326
+ after: true,
327
+ before: false
624
328
  }],
625
- "valid-typeof": ["error", { requireStringLiterals: true }],
626
- "vars-on-top": "error",
627
- "yoda": ["error", "never"],
329
+ ...resolved.overrides
330
+ }
331
+ }];
332
+ }
333
+ //#endregion
334
+ //#region src/configs/tailwind.ts
335
+ const tailwindDefaults = {
336
+ files: [
337
+ jsGlob,
338
+ tsGlob,
339
+ vueGlob
340
+ ],
341
+ settings: { detectComponentClasses: true }
342
+ };
343
+ /**
344
+ * Tailwind linting via `eslint-plugin-better-tailwindcss`.
345
+ * @param options - Tailwind configuration options
346
+ * @returns Promise resolving to Tailwind ESLint config items
347
+ */
348
+ async function tailwind(options) {
349
+ const resolved = defu(options, tailwindDefaults);
350
+ const tailwindPlugin = await importModule(import("eslint-plugin-better-tailwindcss"));
351
+ const inheritedRules = {
352
+ ...tailwindPlugin.configs["recommended-error"].rules,
353
+ ...tailwindPlugin.configs["stylistic-error"].rules
354
+ };
355
+ return [{
356
+ name: "favorodera/tailwind",
357
+ files: resolved.files,
358
+ plugins: { tailwind: tailwindPlugin },
359
+ settings: { tailwindcss: resolved.settings },
360
+ rules: {
361
+ ...renamePluginsInRules(inheritedRules, { "better-tailwindcss": "tailwind" }),
362
+ "tailwind/no-unregistered-classes": "off",
363
+ "tailwind/enforce-consistent-line-wrapping": ["error", { group: "emptyLine" }],
364
+ ...resolved.overrides
365
+ }
366
+ }];
367
+ }
368
+ //#endregion
369
+ //#region src/configs/typescript.ts
370
+ const typescriptDefaults = { files: [tsGlob] };
371
+ /**
372
+ * Typescript linting via `typescript-eslint`.
373
+ * @param options - TypeScript configuration options
374
+ * @returns Array of TypeScript ESLint config items
375
+ */
376
+ async function typescript(options) {
377
+ const resolved = defu(options, typescriptDefaults);
378
+ const tsEsLint = await importModule(import("typescript-eslint"));
379
+ const inheritedRules = Object.assign({}, ...[...tsEsLint.configs.recommended, ...tsEsLint.configs.strict].map((config) => config?.rules || {}));
380
+ return [{
381
+ name: "favorodera/typescript",
382
+ plugins: { ts: tsEsLint.plugin },
383
+ languageOptions: {
384
+ parser: tsEsLint.parser,
385
+ parserOptions: { sourceType: "module" }
386
+ },
387
+ files: resolved.files,
388
+ rules: {
389
+ ...renamePluginsInRules(inheritedRules, { "@typescript-eslint": "ts" }),
390
+ "ts/consistent-type-imports": ["error", { prefer: "type-imports" }],
391
+ "ts/no-empty-object-type": ["error", { allowInterfaces: "with-single-extends" }],
392
+ "ts/array-type": ["error", { default: "array" }],
628
393
  ...resolved.overrides
629
394
  }
630
395
  }];
@@ -639,25 +404,28 @@ async function javascript(options) {
639
404
  function factory(options = {}) {
640
405
  const configs = [];
641
406
  configs.push(ignores(options.ignores));
642
- const vueOptions = resolveOptions(options.vue, {});
643
- const typescriptOptions = resolveOptions(options.typescript, {});
644
- const stylisticOptions = resolveOptions(options.stylistic, {});
645
- const tailwindOptions = resolveOptions(options.tailwind, {});
646
- const commentsOptions = resolveOptions(options.comments, {});
647
- const importsOptions = resolveOptions(options.imports, {});
648
- const markdownOptions = resolveOptions(options.markdown, {});
649
- const javascriptOptions = resolveOptions(options.javascript, {});
650
- if (vueOptions) configs.push(vue(vueOptions));
651
- if (typescriptOptions) configs.push(typescript(typescriptOptions));
652
- if (stylisticOptions) configs.push(stylistic(stylisticOptions));
653
- if (tailwindOptions) configs.push(tailwind(tailwindOptions));
654
- if (commentsOptions) configs.push(comments(commentsOptions));
655
- if (importsOptions) configs.push(imports(importsOptions));
656
- if (markdownOptions) configs.push(markdown(markdownOptions));
657
- if (javascriptOptions) configs.push(javascript(javascriptOptions));
407
+ const configFunctions = {
408
+ vue,
409
+ typescript,
410
+ stylistic,
411
+ tailwind,
412
+ imports,
413
+ markdown,
414
+ javascript
415
+ };
416
+ for (const [key, configFunction] of Object.entries(configFunctions)) {
417
+ const configOption = options[key];
418
+ const resolved = resolveOptions(configOption ?? true, {});
419
+ if (resolved) configs.push(configFunction(resolved));
420
+ }
658
421
  let composer = new FlatConfigComposer();
659
- composer = composer.append(...configs);
422
+ composer = composer.append(...configs).renamePlugins({
423
+ "better-tailwindcss": "tailwind",
424
+ "@typescript-eslint": "ts",
425
+ "markdown": "md",
426
+ "import-lite": "import"
427
+ });
660
428
  return composer;
661
429
  }
662
430
  //#endregion
663
- export { factory, getModuleDefault, renameRules };
431
+ export { factory, importModule };