@favorodera/eslint-config 0.1.3 → 1.0.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 CHANGED
@@ -1,4 +1,4 @@
1
- import { FlatConfigComposer, renamePluginsInRules } from "eslint-flat-config-utils";
1
+ import { FlatConfigComposer } from "eslint-flat-config-utils";
2
2
  import { defu } from "defu";
3
3
  import globals from "globals";
4
4
  import { mergeProcessors, processorPassThrough } from "eslint-merge-processors";
@@ -126,57 +126,70 @@ function resolveOptions(value, defaults) {
126
126
  if (!value) return false;
127
127
  return defu(value === true ? {} : value, defaults);
128
128
  }
129
+ /**
130
+ * Creates a new object that omits the specified keys from the target object.
131
+ * @template TTarget The type of the target object.
132
+ * @template TTargetKeys The type of the keys to omit from the target object.
133
+ * @param target The target object from which to omit the specified keys.
134
+ * @param keys An array of keys to omit from the target object.
135
+ * @returns A new object that contains all properties of the target object except for the specified keys.
136
+ */
137
+ function omit(target, keys) {
138
+ const targetClone = { ...target };
139
+ for (const key of keys) Reflect.deleteProperty(targetClone, key);
140
+ return targetClone;
141
+ }
129
142
  //#endregion
130
143
  //#region src/configs/imports.ts
131
- const importsDefaults = { files: [
132
- jsGlob,
133
- tsGlob,
134
- vueGlob
135
- ] };
136
144
  /**
137
145
  * Constructs the flat config items for imports linting, providing plugin setup and
138
146
  * specific rules to enforce consistent import ordering and quality.
139
- * @param options Configuration options for imports linting.
140
147
  * @returns Promise resolving to imports ESLint config items.
141
148
  */
142
- async function imports(options) {
143
- const resolved = defu(options, importsDefaults);
149
+ async function imports() {
144
150
  const importPlugin = await importModule(import("eslint-plugin-import-lite"));
145
- const baseRules = importPlugin.configs.recommended?.rules || {};
151
+ const files = [
152
+ jsGlob,
153
+ tsGlob,
154
+ vueGlob
155
+ ];
156
+ const recommendedConfig = importPlugin.configs.recommended;
157
+ const { rules = {} } = recommendedConfig;
146
158
  return [{
147
- name: "favorodera/imports/setup",
148
- plugins: { import: importPlugin }
159
+ ...omit(recommendedConfig, [
160
+ "rules",
161
+ "files",
162
+ "name"
163
+ ]),
164
+ name: "favorodera/imports/setup"
149
165
  }, {
150
- files: resolved.files,
166
+ files,
151
167
  name: "favorodera/imports/rules",
152
168
  rules: {
153
- ...renamePluginsInRules(baseRules, { "import-lite": "import" }),
169
+ ...rules,
154
170
  "import/consistent-type-specifier-style": ["error", "prefer-top-level"],
155
171
  "import/first": "error",
156
172
  "import/newline-after-import": ["error", { count: 1 }],
157
173
  "import/no-duplicates": ["error", { "prefer-inline": true }],
158
174
  "import/no-mutable-exports": "error",
159
- "import/no-named-default": "error",
160
- ...resolved.overrides
175
+ "import/no-named-default": "error"
161
176
  }
162
177
  }];
163
178
  }
164
179
  //#endregion
165
180
  //#region src/configs/javascript.ts
166
- const javascriptDefaults = { files: [
167
- jsGlob,
168
- tsGlob,
169
- vueGlob
170
- ] };
171
181
  /**
172
182
  * Constructs the flat config items for core JavaScript linting, setting up the required
173
183
  * language options, globals, and recommended baseline rules.
174
- * @param options Javascript configuration options.
175
184
  * @returns Promise resolving to javascript ESLint config items.
176
185
  */
177
- async function javascript(options) {
178
- const resolved = defu(options, javascriptDefaults);
179
- const baseRules = (await importModule(import("@eslint/js"))).configs.recommended.rules;
186
+ async function javascript() {
187
+ const jsPlugin = await importModule(import("@eslint/js"));
188
+ const files = [
189
+ jsGlob,
190
+ tsGlob,
191
+ vueGlob
192
+ ];
180
193
  return [{
181
194
  languageOptions: {
182
195
  ecmaVersion: "latest",
@@ -193,15 +206,16 @@ async function javascript(options) {
193
206
  linterOptions: { reportUnusedDisableDirectives: true },
194
207
  name: "favorodera/javascript/setup"
195
208
  }, {
196
- files: resolved.files,
209
+ files,
197
210
  name: "favorodera/javascript/rules",
198
211
  rules: {
199
- ...baseRules,
200
- "array-callback-return": "error",
212
+ ...jsPlugin.configs.recommended.rules,
213
+ "array-callback-return": ["error", { allowImplicit: true }],
201
214
  "block-scoped-var": "error",
202
215
  "default-case-last": "error",
203
216
  "dot-notation": "error",
204
217
  "eqeqeq": "error",
218
+ "getter-return": ["error", { allowImplicit: true }],
205
219
  "new-cap": "error",
206
220
  "no-alert": "error",
207
221
  "no-array-constructor": "error",
@@ -284,75 +298,91 @@ async function javascript(options) {
284
298
  "unicode-bom": "error",
285
299
  "valid-typeof": ["error", { requireStringLiterals: true }],
286
300
  "vars-on-top": "error",
287
- "yoda": "error",
288
- ...resolved.overrides
301
+ "yoda": "error"
289
302
  }
290
303
  }];
291
304
  }
292
305
  //#endregion
293
306
  //#region src/configs/jsdoc.ts
294
- const jsdocDefaults = { files: [
295
- jsGlob,
296
- tsGlob,
297
- vueGlob
298
- ] };
299
307
  /**
300
308
  * Constructs the flat config items for JSDoc linting, ensuring comments follow
301
309
  * proper styling, parameter checks, and types alignment.
302
- * @param options JSDoc configuration options.
303
310
  * @returns Promise resolving to JSDoc ESLint config items.
304
311
  */
305
- async function jsdoc(options) {
306
- const resolved = defu(options, jsdocDefaults);
312
+ async function jsdoc() {
307
313
  const jsdocPlugin = await importModule(import("eslint-plugin-jsdoc"));
308
- const baseRules = {
309
- ...jsdocPlugin.configs["flat/recommended-typescript-error"]?.rules,
310
- ...jsdocPlugin.configs["flat/stylistic-typescript-error"]?.rules
314
+ const files = [
315
+ jsGlob,
316
+ tsGlob,
317
+ vueGlob
318
+ ];
319
+ const recommendedConfig = jsdocPlugin.configs["flat/recommended-typescript-error"];
320
+ const stylisticConfig = jsdocPlugin.configs["flat/stylistic-typescript-error"];
321
+ const { rules: recommendedRules = {} } = recommendedConfig;
322
+ const recommendedRest = omit(recommendedConfig, [
323
+ "rules",
324
+ "files",
325
+ "name"
326
+ ]);
327
+ const { rules: stylisticRules = {} } = stylisticConfig;
328
+ const stylisticRest = omit(stylisticConfig, [
329
+ "rules",
330
+ "files",
331
+ "name"
332
+ ]);
333
+ const rules = {
334
+ ...recommendedRules,
335
+ ...stylisticRules
311
336
  };
312
- return [{
313
- name: "favorodera/jsdoc/setup",
314
- plugins: { jsdoc: jsdocPlugin }
315
- }, {
316
- files: resolved.files,
317
- name: "favorodera/jsdoc/rules",
318
- rules: {
319
- ...baseRules,
320
- "jsdoc/check-indentation": "error",
321
- "jsdoc/check-template-names": "error",
322
- "jsdoc/imports-as-dependencies": "error",
323
- "jsdoc/lines-before-block": ["error", { ignoreSingleLines: false }],
324
- "jsdoc/multiline-blocks": "error",
325
- "jsdoc/no-bad-blocks": "error",
326
- "jsdoc/no-blank-block-descriptions": "error",
327
- "jsdoc/no-blank-blocks": "error",
328
- "jsdoc/require-throws-type": "off",
329
- "jsdoc/sort-tags": "error",
330
- ...resolved.overrides
337
+ return [
338
+ {
339
+ ...recommendedRest,
340
+ name: "favorodera/jsdoc/recommended/setup"
341
+ },
342
+ {
343
+ ...stylisticRest,
344
+ name: "favorodera/jsdoc/stylistic/setup"
345
+ },
346
+ {
347
+ files,
348
+ name: "favorodera/jsdoc/rules",
349
+ rules: {
350
+ ...rules,
351
+ "jsdoc/check-indentation": "error",
352
+ "jsdoc/check-template-names": "error",
353
+ "jsdoc/imports-as-dependencies": "error",
354
+ "jsdoc/lines-before-block": ["error", { ignoreSingleLines: false }],
355
+ "jsdoc/multiline-blocks": "error",
356
+ "jsdoc/no-bad-blocks": "error",
357
+ "jsdoc/no-blank-block-descriptions": "error",
358
+ "jsdoc/no-blank-blocks": "error",
359
+ "jsdoc/require-throws-type": "off",
360
+ "jsdoc/sort-tags": "error"
361
+ }
331
362
  }
332
- }];
363
+ ];
333
364
  }
334
365
  //#endregion
335
366
  //#region src/configs/jsonc.ts
336
- const jsoncDefaults = { files: [
337
- json5Glob,
338
- jsoncGlob,
339
- jsonGlob
340
- ] };
341
367
  /**
342
368
  * Constructs the flat config items for JSON, JSON5, and JSONC linting, setting up
343
369
  * the custom parser, rule validations, and sorting rules for package.json/tsconfig.json.
344
- * @param options JSONC configuration options.
345
370
  * @returns Promise resolving to JSONC ESLint config items.
346
371
  */
347
- async function jsonc(options) {
348
- const resolved = defu(options, jsoncDefaults);
372
+ async function jsonc() {
373
+ const jsoncPlugin = await importModule(import("eslint-plugin-jsonc"));
374
+ const files = [
375
+ json5Glob,
376
+ jsoncGlob,
377
+ jsonGlob
378
+ ];
349
379
  return [
350
380
  {
351
381
  name: "favorodera/jsonc/setup",
352
- plugins: { jsonc: await importModule(import("eslint-plugin-jsonc")) }
382
+ plugins: { jsonc: jsoncPlugin }
353
383
  },
354
384
  {
355
- files: resolved.files,
385
+ files,
356
386
  language: "jsonc/x",
357
387
  name: "favorodera/jsonc/rules",
358
388
  rules: {
@@ -400,8 +430,7 @@ async function jsonc(options) {
400
430
  "jsonc/quotes": "error",
401
431
  "jsonc/space-unary-ops": "error",
402
432
  "jsonc/valid-json-number": "error",
403
- "jsonc/vue-custom-block/no-parsing-error": "error",
404
- ...resolved.overrides
433
+ "jsonc/vue-custom-block/no-parsing-error": "error"
405
434
  }
406
435
  },
407
436
  {
@@ -622,7 +651,7 @@ async function jsonc(options) {
622
651
  ] }
623
652
  },
624
653
  {
625
- files: resolved.files,
654
+ files,
626
655
  language: "jsonc/x",
627
656
  name: "favorodera/jsonc/disables",
628
657
  rules: { "no-irregular-whitespace": "off" }
@@ -631,10 +660,7 @@ async function jsonc(options) {
631
660
  }
632
661
  //#endregion
633
662
  //#region src/configs/markdown.ts
634
- const markdownDefaults = {
635
- files: [mdGlob],
636
- gfm: true
637
- };
663
+ const markdownDefaults = { gfm: true };
638
664
  /**
639
665
  * Constructs the flat config items for Markdown linting, extracting and linting
640
666
  * embedded code blocks within the document according to specified rules.
@@ -644,32 +670,29 @@ const markdownDefaults = {
644
670
  async function markdown(options) {
645
671
  const resolved = defu(options, markdownDefaults);
646
672
  const markdownPlugin = await importModule(import("@eslint/markdown"));
647
- const baseRules = extractRules(markdownPlugin.configs.recommended);
673
+ const [recommendedConfig] = markdownPlugin.configs.recommended;
674
+ const { rules = {} } = recommendedConfig;
648
675
  return [
649
676
  {
650
- name: "favorodera/markdown/setup",
651
- plugins: { md: markdownPlugin }
677
+ ...omit(recommendedConfig, [
678
+ "rules",
679
+ "files",
680
+ "name",
681
+ "language"
682
+ ]),
683
+ name: "favorodera/markdown/setup"
652
684
  },
653
685
  {
654
- files: resolved.files,
686
+ files: [mdGlob],
655
687
  ignores: [mdInMdGlob],
656
- name: "favorodera/markdown/processor",
657
- processor: mergeProcessors([markdownPlugin.processors?.markdown, processorPassThrough])
658
- },
659
- {
660
- files: resolved.files,
661
688
  language: resolved.gfm ? "md/gfm" : "md/commonmark",
662
689
  languageOptions: { frontmatter: "yaml" },
663
- name: "favorodera/markdown/parser"
664
- },
665
- {
666
- files: resolved.files,
667
690
  name: "favorodera/markdown/rules",
691
+ processor: mergeProcessors([markdownPlugin.processors?.markdown, processorPassThrough]),
668
692
  rules: {
669
- ...renamePluginsInRules(baseRules, { markdown: "md" }),
693
+ ...rules,
670
694
  "md/fenced-code-language": "off",
671
- "md/no-missing-label-refs": "off",
672
- ...resolved.overrides
695
+ "md/no-missing-label-refs": "off"
673
696
  }
674
697
  },
675
698
  {
@@ -709,31 +732,34 @@ async function markdown(options) {
709
732
  }
710
733
  //#endregion
711
734
  //#region src/configs/node.ts
712
- const nodeDefaults = { files: [
713
- jsGlob,
714
- tsGlob,
715
- vueGlob
716
- ] };
717
735
  /**
718
736
  * Constructs the flat config items for Node.js linting, providing rules
719
737
  * to enforce best practices for Node.js environments.
720
- * @param options Node configuration options.
721
738
  * @returns Promise resolving to Node ESLint config items.
722
739
  */
723
- async function node(options) {
724
- const resolved = defu(options, nodeDefaults);
740
+ async function node() {
725
741
  const nodePlugin = await importModule(import("eslint-plugin-n"));
726
- const baseRules = nodePlugin.configs?.["flat/recommended-module"]?.rules || {};
742
+ const files = [
743
+ jsGlob,
744
+ tsGlob,
745
+ vueGlob
746
+ ];
747
+ const recommendedConfig = nodePlugin.configs["flat/recommended-module"];
748
+ const { rules = {} } = recommendedConfig;
727
749
  return [
728
750
  {
729
- name: "favorodera/node/setup",
730
- plugins: { node: nodePlugin }
751
+ ...omit(recommendedConfig, [
752
+ "rules",
753
+ "files",
754
+ "name"
755
+ ]),
756
+ name: "favorodera/node/setup"
731
757
  },
732
758
  {
733
- files: resolved.files,
759
+ files,
734
760
  name: "favorodera/node/rules",
735
761
  rules: {
736
- ...renamePluginsInRules(baseRules, { n: "node" }),
762
+ ...rules,
737
763
  "node/callback-return": "error",
738
764
  "node/handle-callback-err": ["error", "^(err|error)$"],
739
765
  "node/no-callback-literal": "error",
@@ -752,12 +778,11 @@ async function node(options) {
752
778
  "node/prefer-node-protocol": "error",
753
779
  "node/prefer-promises/dns": "error",
754
780
  "node/prefer-promises/fs": "error",
755
- "node/no-process-exit": "off",
756
- ...resolved.overrides
781
+ "node/no-process-exit": "off"
757
782
  }
758
783
  },
759
784
  {
760
- files: [tsGlob, vueGlob],
785
+ files,
761
786
  name: "favorodera/node/disables",
762
787
  rules: {
763
788
  "node/no-missing-import": "off",
@@ -769,22 +794,15 @@ async function node(options) {
769
794
  //#endregion
770
795
  //#region src/configs/perfectionist.ts
771
796
  const perfectionistDefaults = {
772
- files: [
773
- jsGlob,
774
- tsGlob,
775
- vueGlob
776
- ],
777
- settings: {
778
- ignoreCase: true,
779
- locales: "en-US",
780
- newlinesBetween: "ignore",
781
- newlinesInside: "ignore",
782
- order: "asc",
783
- partitionByComment: true,
784
- partitionByNewLine: true,
785
- specialCharacters: "keep",
786
- type: "natural"
787
- }
797
+ ignoreCase: true,
798
+ locales: "en-US",
799
+ newlinesBetween: "ignore",
800
+ newlinesInside: "ignore",
801
+ order: "asc",
802
+ partitionByComment: true,
803
+ partitionByNewLine: true,
804
+ specialCharacters: "keep",
805
+ type: "natural"
788
806
  };
789
807
  /**
790
808
  * Constructs the flat config items for Perfectionist linting, providing rules
@@ -795,19 +813,27 @@ const perfectionistDefaults = {
795
813
  async function perfectionist(options) {
796
814
  const resolved = defu(options, perfectionistDefaults);
797
815
  const perfectionistPlugin = await importModule(import("eslint-plugin-perfectionist"));
798
- const safeType = resolved.settings?.type ?? "natural";
799
- const baseRules = perfectionistPlugin.configs[`recommended-${safeType}`]?.rules || {};
816
+ const files = [
817
+ jsGlob,
818
+ tsGlob,
819
+ vueGlob
820
+ ];
821
+ const safeType = resolved.type ?? "natural";
822
+ const presetConfig = perfectionistPlugin.configs[`recommended-${safeType}`];
823
+ const { rules = {} } = presetConfig;
800
824
  return [{
825
+ ...omit(presetConfig, [
826
+ "rules",
827
+ "settings",
828
+ "files",
829
+ "name"
830
+ ]),
801
831
  name: "favorodera/perfectionist/setup",
802
- plugins: { perfectionist: perfectionistPlugin },
803
- settings: { perfectionist: resolved.settings }
832
+ settings: { perfectionist: resolved }
804
833
  }, {
805
- files: resolved.files,
834
+ files,
806
835
  name: "favorodera/perfectionist/rules",
807
- rules: {
808
- ...baseRules,
809
- ...resolved.overrides
810
- }
836
+ rules
811
837
  }];
812
838
  }
813
839
  //#endregion
@@ -848,6 +874,15 @@ async function pnpm() {
848
874
  name: "favorodera/pnpm/pnpm-workspace-yaml",
849
875
  rules: {
850
876
  "pnpm/yaml-enforce-settings": ["error", { settings: {
877
+ allowBuilds: {
878
+ "@parcel/watcher": true,
879
+ "@tailwindcss/oxide": true,
880
+ "better-sqlite3": true,
881
+ "esbuild": true,
882
+ "sharp": true,
883
+ "unrs-resolver": true,
884
+ "vue-demi": true
885
+ },
851
886
  shellEmulator: true,
852
887
  trustPolicy: "no-downgrade"
853
888
  } }],
@@ -861,19 +896,12 @@ async function pnpm() {
861
896
  //#endregion
862
897
  //#region src/configs/stylistic.ts
863
898
  const stylisticDefaults = {
864
- files: [
865
- jsGlob,
866
- tsGlob,
867
- vueGlob
868
- ],
869
- settings: {
870
- braceStyle: "1tbs",
871
- experimental: false,
872
- indent: 2,
873
- jsx: false,
874
- quotes: "single",
875
- semi: false
876
- }
899
+ braceStyle: "1tbs",
900
+ experimental: false,
901
+ indent: 2,
902
+ jsx: false,
903
+ quotes: "single",
904
+ semi: false
877
905
  };
878
906
  /**
879
907
  * Constructs the flat config items for Stylistic linting, applying customized
@@ -884,18 +912,28 @@ const stylisticDefaults = {
884
912
  async function stylistic(options) {
885
913
  const resolved = defu(options, stylisticDefaults);
886
914
  const stylePlugin = await importModule(import("@stylistic/eslint-plugin"));
887
- const baseRules = stylePlugin.configs.customize({
915
+ const files = [
916
+ jsGlob,
917
+ tsGlob,
918
+ vueGlob
919
+ ];
920
+ const recommendedConfig = stylePlugin.configs.customize({
888
921
  pluginName: "style",
889
- ...resolved.settings
890
- })?.rules || {};
922
+ ...resolved
923
+ });
924
+ const { rules = {} } = recommendedConfig;
891
925
  return [{
892
- name: "favorodera/stylistic/setup",
893
- plugins: { style: stylePlugin }
926
+ ...omit(recommendedConfig, [
927
+ "rules",
928
+ "files",
929
+ "name"
930
+ ]),
931
+ name: "favorodera/stylistic/setup"
894
932
  }, {
895
- files: resolved.files,
933
+ files,
896
934
  name: "favorodera/stylistic/rules",
897
935
  rules: {
898
- ...baseRules,
936
+ ...rules,
899
937
  "style/array-bracket-newline": "error",
900
938
  "style/array-element-newline": "error",
901
939
  "style/curly-newline": ["error", "always"],
@@ -913,21 +951,13 @@ async function stylistic(options) {
913
951
  "style/object-curly-newline": "error",
914
952
  "style/semi-style": "error",
915
953
  "style/switch-colon-spacing": "error",
916
- "style/wrap-regex": "error",
917
- ...resolved.overrides
954
+ "style/wrap-regex": "error"
918
955
  }
919
956
  }];
920
957
  }
921
958
  //#endregion
922
959
  //#region src/configs/tailwind.ts
923
- const tailwindDefaults = {
924
- files: [
925
- jsGlob,
926
- tsGlob,
927
- vueGlob
928
- ],
929
- settings: { detectComponentClasses: true }
930
- };
960
+ const tailwindDefaults = { detectComponentClasses: true };
931
961
  /**
932
962
  * Constructs the flat config items for Tailwind CSS linting, providing custom settings
933
963
  * to parse and lint utility classes, and enforcing consistent ordering.
@@ -937,59 +967,75 @@ const tailwindDefaults = {
937
967
  async function tailwind(options) {
938
968
  const resolved = defu(options, tailwindDefaults);
939
969
  const tailwindPlugin = await importModule(import("eslint-plugin-better-tailwindcss"));
940
- const baseRules = {
941
- ...tailwindPlugin.configs["recommended-error"].rules,
942
- ...tailwindPlugin.configs["stylistic-error"].rules
970
+ const files = [
971
+ jsGlob,
972
+ tsGlob,
973
+ vueGlob
974
+ ];
975
+ const recommendedConfig = tailwindPlugin.configs["recommended-error"];
976
+ const stylisticConfig = tailwindPlugin.configs["stylistic-error"];
977
+ const { rules: recommendedRules } = recommendedConfig;
978
+ const recommendedRest = omit(recommendedConfig, ["rules"]);
979
+ const { rules: stylisticRules } = stylisticConfig;
980
+ const stylisticRest = omit(stylisticConfig, ["rules"]);
981
+ const rules = {
982
+ ...recommendedRules,
983
+ ...stylisticRules
943
984
  };
944
- return [{
945
- name: "favorodera/tailwind/setup",
946
- plugins: { tailwind: tailwindPlugin },
947
- settings: { "better-tailwindcss": resolved.settings }
948
- }, {
949
- files: resolved.files,
950
- name: "favorodera/tailwind/rules",
951
- rules: {
952
- ...renamePluginsInRules(baseRules, { "better-tailwindcss": "tailwind" }),
953
- "tailwind/enforce-consistent-class-order": ["error", {
954
- componentClassOrder: "asc",
955
- componentClassPosition: "start",
956
- order: "strict",
957
- unknownClassOrder: "asc",
958
- unknownClassPosition: "start"
959
- }],
960
- "tailwind/enforce-consistent-line-wrapping": ["error", { group: "emptyLine" }],
961
- "tailwind/enforce-consistent-variant-order": "error",
962
- "tailwind/enforce-logical-properties": "error",
963
- "tailwind/enforce-consistent-important-position": "off",
964
- "tailwind/enforce-consistent-variable-syntax": "off",
965
- "tailwind/enforce-shorthand-classes": "off",
966
- ...resolved.overrides
985
+ return [
986
+ {
987
+ ...recommendedRest,
988
+ name: "favorodera/tailwind/recommended/setup"
989
+ },
990
+ {
991
+ ...stylisticRest,
992
+ name: "favorodera/tailwind/stylistic/setup"
993
+ },
994
+ {
995
+ files,
996
+ name: "favorodera/tailwind/rules",
997
+ rules: {
998
+ ...rules,
999
+ "tailwind/enforce-consistent-class-order": ["error", {
1000
+ componentClassOrder: "asc",
1001
+ componentClassPosition: "start",
1002
+ order: "strict",
1003
+ unknownClassOrder: "asc",
1004
+ unknownClassPosition: "start"
1005
+ }],
1006
+ "tailwind/enforce-consistent-line-wrapping": ["error", { group: "emptyLine" }],
1007
+ "tailwind/enforce-consistent-variant-order": "error",
1008
+ "tailwind/enforce-logical-properties": "error",
1009
+ "tailwind/enforce-consistent-important-position": "off",
1010
+ "tailwind/enforce-consistent-variable-syntax": "off",
1011
+ "tailwind/enforce-shorthand-classes": "off"
1012
+ },
1013
+ settings: { "better-tailwindcss": resolved }
967
1014
  }
968
- }];
1015
+ ];
969
1016
  }
970
1017
  //#endregion
971
1018
  //#region src/configs/test.ts
972
- const testDefaults = { files: [testGlob] };
973
1019
  /**
974
1020
  * Constructs the flat config items for test linting, extending
975
1021
  * the recommended Vitest rule sets.
976
- * @param options Test configuration options.
977
1022
  * @returns Promise resolving to test ESLint config items.
978
1023
  */
979
- async function test(options) {
980
- const resolved = defu(options, testDefaults);
1024
+ async function test() {
981
1025
  const testPlugin = await importModule(import("@vitest/eslint-plugin"));
982
- const baseRules = extractRules(testPlugin.configs.recommended);
1026
+ const files = [testGlob];
1027
+ const recommendedConfig = testPlugin.configs.recommended;
1028
+ const { rules } = recommendedConfig;
983
1029
  return [
984
1030
  {
985
- name: "favorodera/test/setup",
986
- plugins: { test: testPlugin }
1031
+ ...omit(recommendedConfig, ["rules", "name"]),
1032
+ name: "favorodera/test/setup"
987
1033
  },
988
1034
  {
989
- files: resolved.files,
1035
+ files,
990
1036
  name: "favorodera/test/rules",
991
1037
  rules: {
992
- ...renamePluginsInRules(baseRules, { vitest: "test" }),
1038
+ ...rules,
993
1039
  "test/consistent-each-for": ["error", {
994
1040
  describe: "for",
995
1041
  it: "for",
@@ -1042,12 +1088,11 @@ async function test(options) {
1042
1088
  "test/require-hook": "error",
1043
1089
  "test/require-to-throw-message": "error",
1044
1090
  "test/require-top-level-describe": "error",
1045
- "test/warn-todo": "warn",
1046
- ...resolved.overrides
1091
+ "test/warn-todo": "warn"
1047
1092
  }
1048
1093
  },
1049
1094
  {
1050
- files: resolved.files,
1095
+ files,
1051
1096
  name: "favorodera/test/disables",
1052
1097
  rules: { "no-unused-expressions": "off" }
1053
1098
  }
@@ -1055,29 +1100,30 @@ async function test(options) {
1055
1100
  }
1056
1101
  //#endregion
1057
1102
  //#region src/configs/typescript.ts
1058
- const typescriptDefaults = { files: [tsGlob] };
1059
1103
  /**
1060
1104
  * Constructs the flat config items for TypeScript linting, initializing the parser
1061
1105
  * and extending the recommended and strict type-aware rule sets.
1062
- * @param options TypeScript configuration options.
1063
1106
  * @returns Promise resolving to TypeScript ESLint config items.
1064
1107
  */
1065
- async function typescript(options) {
1066
- const resolved = defu(options, typescriptDefaults);
1108
+ async function typescript() {
1067
1109
  const tsEsLint = await importModule(import("typescript-eslint"));
1068
- const baseRules = extractRules(tsEsLint.configs.strict, tsEsLint.configs.stylistic);
1110
+ const files = [tsGlob];
1111
+ const [baseConfig, eslintRecommended, strictConfig] = tsEsLint.configs.strict;
1112
+ const stylisticConfig = tsEsLint.configs.stylistic[2];
1113
+ const baseRest = omit(baseConfig, ["rules", "name"]);
1114
+ const rules = {
1115
+ ...eslintRecommended?.rules,
1116
+ ...strictConfig?.rules,
1117
+ ...stylisticConfig?.rules
1118
+ };
1069
1119
  return [{
1070
- name: "favorodera/typescript/setup",
1071
- plugins: { ts: tsEsLint.plugin }
1120
+ ...baseRest,
1121
+ name: "favorodera/typescript/setup"
1072
1122
  }, {
1073
- files: resolved.files,
1074
- languageOptions: {
1075
- parser: tsEsLint.parser,
1076
- parserOptions: { sourceType: "module" }
1077
- },
1123
+ files,
1078
1124
  name: "favorodera/typescript/rules",
1079
1125
  rules: {
1080
- ...renamePluginsInRules(baseRules, { "@typescript-eslint": "ts" }),
1126
+ ...rules,
1081
1127
  "ts/array-type": ["error", {
1082
1128
  default: "generic",
1083
1129
  readonly: "generic"
@@ -1087,66 +1133,65 @@ async function typescript(options) {
1087
1133
  "ts/method-signature-style": "error",
1088
1134
  "ts/no-import-type-side-effects": "error",
1089
1135
  "ts/no-loop-func": "error",
1090
- "ts/no-redeclare": "error",
1091
- ...resolved.overrides
1136
+ "ts/no-redeclare": "error"
1092
1137
  }
1093
1138
  }];
1094
1139
  }
1095
1140
  //#endregion
1096
1141
  //#region src/configs/unicorn.ts
1097
- const unicornDefaults = { files: [
1098
- jsGlob,
1099
- tsGlob,
1100
- vueGlob
1101
- ] };
1102
1142
  /**
1103
1143
  * Constructs the flat config items for Unicorn linting, providing a collection of
1104
1144
  * awesome ESLint rules to improve code quality and enforce best practices.
1105
- * @param options Unicorn configuration options.
1106
1145
  * @returns Promise resolving to Unicorn ESLint config items.
1107
1146
  */
1108
- async function unicorn(options) {
1109
- const resolved = defu(options, unicornDefaults);
1147
+ async function unicorn() {
1110
1148
  const unicornPlugin = await importModule(import("eslint-plugin-unicorn"));
1111
- const baseRules = unicornPlugin.configs.recommended?.rules || {};
1149
+ const files = [
1150
+ jsGlob,
1151
+ tsGlob,
1152
+ vueGlob
1153
+ ];
1154
+ const recommendedConfig = unicornPlugin.configs.recommended;
1155
+ const { rules = {} } = recommendedConfig;
1112
1156
  return [{
1113
- name: "favorodera/unicorn/setup",
1114
- plugins: { unicorn: unicornPlugin }
1157
+ ...omit(recommendedConfig, [
1158
+ "rules",
1159
+ "files",
1160
+ "name"
1161
+ ]),
1162
+ name: "favorodera/unicorn/setup"
1115
1163
  }, {
1116
- files: resolved.files,
1117
- languageOptions: { globals: globals.builtin },
1164
+ files,
1118
1165
  name: "favorodera/unicorn/rules",
1119
1166
  rules: {
1120
- ...baseRules,
1167
+ ...rules,
1121
1168
  "unicorn/filename-case": "off",
1122
1169
  "unicorn/no-process-exit": "off",
1123
- "unicorn/prevent-abbreviations": "off",
1124
- ...resolved.overrides
1170
+ "unicorn/prevent-abbreviations": "off"
1125
1171
  }
1126
1172
  }];
1127
1173
  }
1128
1174
  //#endregion
1129
1175
  //#region src/configs/unused-imports.ts
1130
- const unusedImportsDefaults = { files: [
1131
- jsGlob,
1132
- tsGlob,
1133
- vueGlob
1134
- ] };
1135
1176
  /**
1136
1177
  * Constructs the flat config items for unused imports linting, providing plugin setup and
1137
1178
  * specific rules to detect and remove unused imports and variables.
1138
- * @param options Configuration options for unused imports linting.
1139
1179
  * @returns Promise resolving to unused imports ESLint config items.
1140
1180
  */
1141
- async function unusedImports(options) {
1142
- const resolved = defu(options, unusedImportsDefaults);
1181
+ async function unusedImports() {
1182
+ const unusedImportsPlugin = await importModule(import("eslint-plugin-unused-imports"));
1183
+ const files = [
1184
+ jsGlob,
1185
+ tsGlob,
1186
+ vueGlob
1187
+ ];
1143
1188
  return [
1144
1189
  {
1145
1190
  name: "favorodera/unused-imports/setup",
1146
- plugins: { "unused-imports": await importModule(import("eslint-plugin-unused-imports")) }
1191
+ plugins: { "unused-imports": unusedImportsPlugin }
1147
1192
  },
1148
1193
  {
1149
- files: resolved.files,
1194
+ files,
1150
1195
  name: "favorodera/unused-imports/rules",
1151
1196
  rules: {
1152
1197
  "unused-imports/no-unused-imports": "error",
@@ -1156,12 +1201,11 @@ async function unusedImports(options) {
1156
1201
  ignoreRestSiblings: true,
1157
1202
  vars: "all",
1158
1203
  varsIgnorePattern: "^_"
1159
- }],
1160
- ...resolved.overrides
1204
+ }]
1161
1205
  }
1162
1206
  },
1163
1207
  {
1164
- files: resolved.files,
1208
+ files,
1165
1209
  name: "favorodera/unused-imports/disables",
1166
1210
  rules: {
1167
1211
  "no-unused-vars": "off",
@@ -1177,167 +1221,196 @@ const sfcBlocksDefaults = { blocks: {
1177
1221
  styles: true,
1178
1222
  template: false
1179
1223
  } };
1180
- const vueDefaults = {
1181
- files: [vueGlob],
1182
- sfcBlocks: sfcBlocksDefaults
1183
- };
1224
+ const vueDefaults = { sfcBlocks: sfcBlocksDefaults };
1184
1225
  /**
1185
- * Constructs the flat config items for Vue linting, setting up the custom template parser,
1226
+ * Constructs the flat config items for Vue and accessibility linting, setting up the custom template parser,
1186
1227
  * Vue block processors, and rules to enforce recommended component syntax.
1187
1228
  * @param options Vue configuration options.
1188
1229
  * @returns Promise resolving to Vue ESLint config items.
1189
1230
  */
1190
1231
  async function vue(options) {
1191
- const resolved = defu(options, vueDefaults);
1192
- const sfcBlocks = resolveOptions(resolved.sfcBlocks, sfcBlocksDefaults);
1193
- const [vuePlugin, vueParser, tsEsLint] = await Promise.all([
1232
+ const sfcBlocks = resolveOptions(defu(options, vueDefaults).sfcBlocks, sfcBlocksDefaults);
1233
+ const [vuePlugin, vueParser, tsEsLint, a11yPlugin] = await Promise.all([
1194
1234
  importModule(import("eslint-plugin-vue")),
1195
1235
  importModule(import("vue-eslint-parser")),
1196
- importModule(import("typescript-eslint"))
1236
+ importModule(import("typescript-eslint")),
1237
+ importModule(import("eslint-plugin-vuejs-accessibility"))
1238
+ ]);
1239
+ const vueRecommendedConfig = vuePlugin.configs["flat/recommended-error"];
1240
+ const a11yRecommendedConfig = a11yPlugin.configs["flat/recommended"];
1241
+ const [vueBase] = vueRecommendedConfig;
1242
+ const vueBaseRest = omit(vueBase, [
1243
+ "rules",
1244
+ "files",
1245
+ "name"
1197
1246
  ]);
1198
- const baseRules = extractRules(vuePlugin.configs["flat/recommended-error"]);
1247
+ const vueRules = extractRules(vueRecommendedConfig);
1248
+ const [a11yBase] = a11yRecommendedConfig;
1249
+ const a11yBaseRest = omit(a11yBase, ["name"]);
1250
+ const a11yRules = extractRules(a11yRecommendedConfig);
1199
1251
  const processor = sfcBlocks === false ? vuePlugin.processors[".vue"] : mergeProcessors([vuePlugin.processors[".vue"], vueBlocksProcessor(sfcBlocks)]);
1200
- return [{
1201
- languageOptions: { globals: {
1202
- computed: "readonly",
1203
- defineEmits: "readonly",
1204
- defineExpose: "readonly",
1205
- defineProps: "readonly",
1206
- onMounted: "readonly",
1207
- onUnmounted: "readonly",
1208
- reactive: "readonly",
1209
- ref: "readonly",
1210
- shallowReactive: "readonly",
1211
- shallowRef: "readonly",
1212
- toRef: "readonly",
1213
- toRefs: "readonly",
1214
- watch: "readonly",
1215
- watchEffect: "readonly"
1216
- } },
1217
- name: "favorodera/vue/setup",
1218
- plugins: { vue: vuePlugin }
1219
- }, {
1220
- files: resolved.files,
1221
- languageOptions: {
1222
- parser: vueParser,
1223
- parserOptions: {
1224
- extraFileExtensions: [".vue"],
1225
- parser: tsEsLint.parser,
1226
- sourceType: "module"
1227
- }
1252
+ return [
1253
+ {
1254
+ ...vueBaseRest,
1255
+ languageOptions: { globals: {
1256
+ computed: "readonly",
1257
+ defineEmits: "readonly",
1258
+ defineExpose: "readonly",
1259
+ defineProps: "readonly",
1260
+ onMounted: "readonly",
1261
+ onUnmounted: "readonly",
1262
+ reactive: "readonly",
1263
+ ref: "readonly",
1264
+ shallowReactive: "readonly",
1265
+ shallowRef: "readonly",
1266
+ toRef: "readonly",
1267
+ toRefs: "readonly",
1268
+ watch: "readonly",
1269
+ watchEffect: "readonly"
1270
+ } },
1271
+ name: "favorodera/vue/setup"
1228
1272
  },
1229
- name: "favorodera/vue/rules",
1230
- processor,
1231
- rules: {
1232
- ...baseRules,
1233
- "vue/block-lang": ["error", { script: { lang: "ts" } }],
1234
- "vue/block-order": ["error", { order: [
1235
- "script",
1236
- "template",
1237
- "style"
1238
- ] }],
1239
- "vue/block-tag-newline": ["error", {
1240
- multiline: "always",
1241
- singleline: "always"
1242
- }],
1243
- "vue/comment-directive": ["error", { reportUnusedDisableDirectives: true }],
1244
- "vue/define-macros-order": ["error", {
1245
- defineExposeLast: true,
1246
- order: [
1247
- "defineOptions",
1248
- "definePage",
1249
- "defineSlots",
1250
- "defineEmits",
1251
- "defineProps",
1252
- "defineModel"
1253
- ]
1254
- }],
1255
- "vue/define-props-declaration": ["error", "type-based"],
1256
- "vue/define-props-destructuring": ["error", { destructure: "never" }],
1257
- "vue/multi-word-component-names": "off",
1258
- "vue/next-tick-style": ["error", "promise"],
1259
- "vue/no-import-compiler-macros": "error",
1260
- "vue/no-negated-v-if-condition": "error",
1261
- "vue/no-reserved-component-names": ["error", {
1262
- disallowVue3BuiltInComponents: true,
1263
- disallowVueBuiltInComponents: true,
1264
- htmlElementCaseSensitive: false
1265
- }],
1266
- "vue/no-root-v-if": "error",
1267
- "vue/no-template-target-blank": "error",
1268
- "vue/no-unused-emit-declarations": "error",
1269
- "vue/no-unused-properties": "error",
1270
- "vue/no-unused-refs": "error",
1271
- "vue/no-use-v-else-with-v-for": "error",
1272
- "vue/no-useless-mustaches": "error",
1273
- "vue/no-useless-v-bind": "error",
1274
- "vue/padding-line-between-blocks": "error",
1275
- "vue/padding-line-between-tags": ["error", [
1276
- {
1277
- blankLine: "always",
1278
- next: "*:multi-line",
1279
- prev: "*:single-line"
1280
- },
1281
- {
1282
- blankLine: "always",
1283
- next: "*:single-line",
1284
- prev: "*:multi-line"
1285
- },
1286
- {
1287
- blankLine: "always",
1288
- next: "*:multi-line",
1289
- prev: "*:multi-line"
1290
- },
1291
- {
1292
- blankLine: "never",
1293
- next: "*:single-line",
1294
- prev: "*:single-line"
1273
+ {
1274
+ ...a11yBaseRest,
1275
+ name: "favorodera/vue/a11y/setup"
1276
+ },
1277
+ {
1278
+ files: [vueGlob],
1279
+ languageOptions: {
1280
+ parser: vueParser,
1281
+ parserOptions: {
1282
+ extraFileExtensions: [".vue"],
1283
+ parser: tsEsLint.parser,
1284
+ sourceType: "module"
1295
1285
  }
1296
- ]],
1297
- "vue/prefer-prop-type-boolean-first": "error",
1298
- "vue/prefer-separate-static-class": "error",
1299
- "vue/prefer-single-event-payload": "error",
1300
- "vue/prefer-use-template-ref": "error",
1301
- "vue/slot-name-casing": ["error", "kebab-case"],
1302
- "vue/v-for-delimiter-style": ["error", "in"],
1303
- ...resolved.overrides
1286
+ },
1287
+ name: "favorodera/vue/rules",
1288
+ processor,
1289
+ rules: {
1290
+ ...vueRules,
1291
+ "vue/block-lang": ["error", { script: { lang: "ts" } }],
1292
+ "vue/block-order": ["error", { order: [
1293
+ "script",
1294
+ "template",
1295
+ "style"
1296
+ ] }],
1297
+ "vue/block-tag-newline": ["error", {
1298
+ multiline: "always",
1299
+ singleline: "always"
1300
+ }],
1301
+ "vue/comment-directive": ["error", { reportUnusedDisableDirectives: true }],
1302
+ "vue/define-macros-order": ["error", {
1303
+ defineExposeLast: true,
1304
+ order: [
1305
+ "defineOptions",
1306
+ "definePage",
1307
+ "defineSlots",
1308
+ "defineEmits",
1309
+ "defineProps",
1310
+ "defineModel"
1311
+ ]
1312
+ }],
1313
+ "vue/define-props-declaration": ["error", "type-based"],
1314
+ "vue/define-props-destructuring": ["error", { destructure: "never" }],
1315
+ "vue/multi-word-component-names": "off",
1316
+ "vue/next-tick-style": ["error", "promise"],
1317
+ "vue/no-import-compiler-macros": "error",
1318
+ "vue/no-negated-v-if-condition": "error",
1319
+ "vue/no-reserved-component-names": ["error", {
1320
+ disallowVue3BuiltInComponents: true,
1321
+ disallowVueBuiltInComponents: true,
1322
+ htmlElementCaseSensitive: false
1323
+ }],
1324
+ "vue/no-root-v-if": "error",
1325
+ "vue/no-template-target-blank": "error",
1326
+ "vue/no-unused-emit-declarations": "error",
1327
+ "vue/no-unused-properties": "error",
1328
+ "vue/no-unused-refs": "error",
1329
+ "vue/no-use-v-else-with-v-for": "error",
1330
+ "vue/no-useless-mustaches": "error",
1331
+ "vue/no-useless-v-bind": "error",
1332
+ "vue/padding-line-between-blocks": "error",
1333
+ "vue/padding-line-between-tags": ["error", [
1334
+ {
1335
+ blankLine: "always",
1336
+ next: "*:multi-line",
1337
+ prev: "*:single-line"
1338
+ },
1339
+ {
1340
+ blankLine: "always",
1341
+ next: "*:single-line",
1342
+ prev: "*:multi-line"
1343
+ },
1344
+ {
1345
+ blankLine: "always",
1346
+ next: "*:multi-line",
1347
+ prev: "*:multi-line"
1348
+ },
1349
+ {
1350
+ blankLine: "never",
1351
+ next: "*:single-line",
1352
+ prev: "*:single-line"
1353
+ }
1354
+ ]],
1355
+ "vue/prefer-prop-type-boolean-first": "error",
1356
+ "vue/prefer-separate-static-class": "error",
1357
+ "vue/prefer-single-event-payload": "error",
1358
+ "vue/prefer-use-template-ref": "error",
1359
+ "vue/slot-name-casing": ["error", "kebab-case"],
1360
+ "vue/v-for-delimiter-style": ["error", "in"]
1361
+ }
1362
+ },
1363
+ {
1364
+ files: [vueGlob],
1365
+ name: "favorodera/vue/a11y/rules",
1366
+ rules: {
1367
+ ...a11yRules,
1368
+ "vue-a11y/no-aria-hidden-on-focusable": "error",
1369
+ "vue-a11y/no-onchange": "error",
1370
+ "vue-a11y/no-role-presentation-on-focusable": "error"
1371
+ }
1304
1372
  }
1305
- }];
1373
+ ];
1306
1374
  }
1307
1375
  //#endregion
1308
1376
  //#region src/configs/yaml.ts
1309
- const yamlDefaults = { files: [yamlGlob] };
1310
1377
  /**
1311
1378
  * Constructs the flat config items for YAML linting, setting up
1312
1379
  * the custom parser and rule validations.
1313
- * @param options YAML configuration options.
1314
1380
  * @returns Promise resolving to YAML ESLint config items.
1315
1381
  */
1316
- async function yaml(options) {
1317
- const resolved = defu(options, yamlDefaults);
1382
+ async function yaml() {
1318
1383
  const [yamlPlugin, yamlParser] = await Promise.all([importModule(import("eslint-plugin-yml")), importModule(import("yaml-eslint-parser"))]);
1319
- const baseRules = extractRules(yamlPlugin.configs.standard);
1384
+ const standardConfig = yamlPlugin.configs.standard;
1385
+ const [pluginConfig] = standardConfig;
1386
+ const pluginRest = omit(pluginConfig, [
1387
+ "rules",
1388
+ "files",
1389
+ "name"
1390
+ ]);
1391
+ const rules = extractRules(standardConfig);
1320
1392
  return [
1321
1393
  {
1322
- name: "favorodera/yaml/setup",
1323
- plugins: { yaml: yamlPlugin }
1394
+ ...pluginRest,
1395
+ name: "favorodera/yaml/setup"
1324
1396
  },
1325
1397
  {
1326
- files: resolved.files,
1398
+ files: [yamlGlob],
1399
+ language: "yaml/yaml",
1327
1400
  languageOptions: { parser: yamlParser },
1328
1401
  name: "favorodera/yaml/rules",
1329
1402
  rules: {
1330
- ...renamePluginsInRules(baseRules, { yml: "yaml" }),
1403
+ ...rules,
1331
1404
  "yaml/quotes": ["error", {
1332
1405
  avoidEscape: true,
1333
1406
  prefer: "single"
1334
1407
  }],
1335
- "yaml/require-string-key": "error",
1336
- ...resolved.overrides
1408
+ "yaml/require-string-key": "error"
1337
1409
  }
1338
1410
  },
1339
1411
  {
1340
1412
  files: [pnpmWorkspaceGlob],
1413
+ language: "yaml/yaml",
1341
1414
  languageOptions: { parser: yamlParser },
1342
1415
  name: "favorodera/yaml/sort/pnpm-workspace-yaml",
1343
1416
  rules: { "yaml/sort-keys": [
@@ -1452,9 +1525,10 @@ function factory(options = {}) {
1452
1525
  "markdown": "md",
1453
1526
  "n": "node",
1454
1527
  "vitest": "test",
1528
+ "vuejs-accessibility": "vue-a11y",
1455
1529
  "yml": "yaml"
1456
1530
  });
1457
1531
  return composer;
1458
1532
  }
1459
1533
  //#endregion
1460
- export { codeInMdGlob, extractRules, factory, ignoresGlob, importModule, jsGlob, json5Glob, jsonGlob, jsoncGlob, mdGlob, mdInMdGlob, packageJsonGlob, pnpmWorkspaceGlob, scriptsGlob, testGlob, tsConfigGlob, tsGlob, vueGlob, yamlGlob };
1534
+ export { codeInMdGlob, extractRules, factory, ignoresGlob, importModule, jsGlob, json5Glob, jsonGlob, jsoncGlob, mdGlob, mdInMdGlob, omit, packageJsonGlob, pnpmWorkspaceGlob, scriptsGlob, testGlob, tsConfigGlob, tsGlob, vueGlob, yamlGlob };