@favorodera/eslint-config 0.1.4 → 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,24 +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
688
  language: resolved.gfm ? "md/gfm" : "md/commonmark",
657
689
  languageOptions: { frontmatter: "yaml" },
658
690
  name: "favorodera/markdown/rules",
659
691
  processor: mergeProcessors([markdownPlugin.processors?.markdown, processorPassThrough]),
660
692
  rules: {
661
- ...renamePluginsInRules(baseRules, { markdown: "md" }),
693
+ ...rules,
662
694
  "md/fenced-code-language": "off",
663
- "md/no-missing-label-refs": "off",
664
- ...resolved.overrides
695
+ "md/no-missing-label-refs": "off"
665
696
  }
666
697
  },
667
698
  {
@@ -701,31 +732,34 @@ async function markdown(options) {
701
732
  }
702
733
  //#endregion
703
734
  //#region src/configs/node.ts
704
- const nodeDefaults = { files: [
705
- jsGlob,
706
- tsGlob,
707
- vueGlob
708
- ] };
709
735
  /**
710
736
  * Constructs the flat config items for Node.js linting, providing rules
711
737
  * to enforce best practices for Node.js environments.
712
- * @param options Node configuration options.
713
738
  * @returns Promise resolving to Node ESLint config items.
714
739
  */
715
- async function node(options) {
716
- const resolved = defu(options, nodeDefaults);
740
+ async function node() {
717
741
  const nodePlugin = await importModule(import("eslint-plugin-n"));
718
- 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;
719
749
  return [
720
750
  {
721
- name: "favorodera/node/setup",
722
- plugins: { node: nodePlugin }
751
+ ...omit(recommendedConfig, [
752
+ "rules",
753
+ "files",
754
+ "name"
755
+ ]),
756
+ name: "favorodera/node/setup"
723
757
  },
724
758
  {
725
- files: resolved.files,
759
+ files,
726
760
  name: "favorodera/node/rules",
727
761
  rules: {
728
- ...renamePluginsInRules(baseRules, { n: "node" }),
762
+ ...rules,
729
763
  "node/callback-return": "error",
730
764
  "node/handle-callback-err": ["error", "^(err|error)$"],
731
765
  "node/no-callback-literal": "error",
@@ -744,12 +778,11 @@ async function node(options) {
744
778
  "node/prefer-node-protocol": "error",
745
779
  "node/prefer-promises/dns": "error",
746
780
  "node/prefer-promises/fs": "error",
747
- "node/no-process-exit": "off",
748
- ...resolved.overrides
781
+ "node/no-process-exit": "off"
749
782
  }
750
783
  },
751
784
  {
752
- files: [tsGlob, vueGlob],
785
+ files,
753
786
  name: "favorodera/node/disables",
754
787
  rules: {
755
788
  "node/no-missing-import": "off",
@@ -761,22 +794,15 @@ async function node(options) {
761
794
  //#endregion
762
795
  //#region src/configs/perfectionist.ts
763
796
  const perfectionistDefaults = {
764
- files: [
765
- jsGlob,
766
- tsGlob,
767
- vueGlob
768
- ],
769
- settings: {
770
- ignoreCase: true,
771
- locales: "en-US",
772
- newlinesBetween: "ignore",
773
- newlinesInside: "ignore",
774
- order: "asc",
775
- partitionByComment: true,
776
- partitionByNewLine: true,
777
- specialCharacters: "keep",
778
- type: "natural"
779
- }
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"
780
806
  };
781
807
  /**
782
808
  * Constructs the flat config items for Perfectionist linting, providing rules
@@ -787,19 +813,27 @@ const perfectionistDefaults = {
787
813
  async function perfectionist(options) {
788
814
  const resolved = defu(options, perfectionistDefaults);
789
815
  const perfectionistPlugin = await importModule(import("eslint-plugin-perfectionist"));
790
- const safeType = resolved.settings?.type ?? "natural";
791
- 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;
792
824
  return [{
825
+ ...omit(presetConfig, [
826
+ "rules",
827
+ "settings",
828
+ "files",
829
+ "name"
830
+ ]),
793
831
  name: "favorodera/perfectionist/setup",
794
- plugins: { perfectionist: perfectionistPlugin },
795
- settings: { perfectionist: resolved.settings }
832
+ settings: { perfectionist: resolved }
796
833
  }, {
797
- files: resolved.files,
834
+ files,
798
835
  name: "favorodera/perfectionist/rules",
799
- rules: {
800
- ...baseRules,
801
- ...resolved.overrides
802
- }
836
+ rules
803
837
  }];
804
838
  }
805
839
  //#endregion
@@ -840,6 +874,15 @@ async function pnpm() {
840
874
  name: "favorodera/pnpm/pnpm-workspace-yaml",
841
875
  rules: {
842
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
+ },
843
886
  shellEmulator: true,
844
887
  trustPolicy: "no-downgrade"
845
888
  } }],
@@ -853,19 +896,12 @@ async function pnpm() {
853
896
  //#endregion
854
897
  //#region src/configs/stylistic.ts
855
898
  const stylisticDefaults = {
856
- files: [
857
- jsGlob,
858
- tsGlob,
859
- vueGlob
860
- ],
861
- settings: {
862
- braceStyle: "1tbs",
863
- experimental: false,
864
- indent: 2,
865
- jsx: false,
866
- quotes: "single",
867
- semi: false
868
- }
899
+ braceStyle: "1tbs",
900
+ experimental: false,
901
+ indent: 2,
902
+ jsx: false,
903
+ quotes: "single",
904
+ semi: false
869
905
  };
870
906
  /**
871
907
  * Constructs the flat config items for Stylistic linting, applying customized
@@ -876,18 +912,28 @@ const stylisticDefaults = {
876
912
  async function stylistic(options) {
877
913
  const resolved = defu(options, stylisticDefaults);
878
914
  const stylePlugin = await importModule(import("@stylistic/eslint-plugin"));
879
- const baseRules = stylePlugin.configs.customize({
915
+ const files = [
916
+ jsGlob,
917
+ tsGlob,
918
+ vueGlob
919
+ ];
920
+ const recommendedConfig = stylePlugin.configs.customize({
880
921
  pluginName: "style",
881
- ...resolved.settings
882
- })?.rules || {};
922
+ ...resolved
923
+ });
924
+ const { rules = {} } = recommendedConfig;
883
925
  return [{
884
- name: "favorodera/stylistic/setup",
885
- plugins: { style: stylePlugin }
926
+ ...omit(recommendedConfig, [
927
+ "rules",
928
+ "files",
929
+ "name"
930
+ ]),
931
+ name: "favorodera/stylistic/setup"
886
932
  }, {
887
- files: resolved.files,
933
+ files,
888
934
  name: "favorodera/stylistic/rules",
889
935
  rules: {
890
- ...baseRules,
936
+ ...rules,
891
937
  "style/array-bracket-newline": "error",
892
938
  "style/array-element-newline": "error",
893
939
  "style/curly-newline": ["error", "always"],
@@ -905,21 +951,13 @@ async function stylistic(options) {
905
951
  "style/object-curly-newline": "error",
906
952
  "style/semi-style": "error",
907
953
  "style/switch-colon-spacing": "error",
908
- "style/wrap-regex": "error",
909
- ...resolved.overrides
954
+ "style/wrap-regex": "error"
910
955
  }
911
956
  }];
912
957
  }
913
958
  //#endregion
914
959
  //#region src/configs/tailwind.ts
915
- const tailwindDefaults = {
916
- files: [
917
- jsGlob,
918
- tsGlob,
919
- vueGlob
920
- ],
921
- settings: { detectComponentClasses: true }
922
- };
960
+ const tailwindDefaults = { detectComponentClasses: true };
923
961
  /**
924
962
  * Constructs the flat config items for Tailwind CSS linting, providing custom settings
925
963
  * to parse and lint utility classes, and enforcing consistent ordering.
@@ -929,59 +967,75 @@ const tailwindDefaults = {
929
967
  async function tailwind(options) {
930
968
  const resolved = defu(options, tailwindDefaults);
931
969
  const tailwindPlugin = await importModule(import("eslint-plugin-better-tailwindcss"));
932
- const baseRules = {
933
- ...tailwindPlugin.configs["recommended-error"].rules,
934
- ...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
935
984
  };
936
- return [{
937
- name: "favorodera/tailwind/setup",
938
- plugins: { tailwind: tailwindPlugin },
939
- settings: { "better-tailwindcss": resolved.settings }
940
- }, {
941
- files: resolved.files,
942
- name: "favorodera/tailwind/rules",
943
- rules: {
944
- ...renamePluginsInRules(baseRules, { "better-tailwindcss": "tailwind" }),
945
- "tailwind/enforce-consistent-class-order": ["error", {
946
- componentClassOrder: "asc",
947
- componentClassPosition: "start",
948
- order: "strict",
949
- unknownClassOrder: "asc",
950
- unknownClassPosition: "start"
951
- }],
952
- "tailwind/enforce-consistent-line-wrapping": ["error", { group: "emptyLine" }],
953
- "tailwind/enforce-consistent-variant-order": "error",
954
- "tailwind/enforce-logical-properties": "error",
955
- "tailwind/enforce-consistent-important-position": "off",
956
- "tailwind/enforce-consistent-variable-syntax": "off",
957
- "tailwind/enforce-shorthand-classes": "off",
958
- ...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 }
959
1014
  }
960
- }];
1015
+ ];
961
1016
  }
962
1017
  //#endregion
963
1018
  //#region src/configs/test.ts
964
- const testDefaults = { files: [testGlob] };
965
1019
  /**
966
1020
  * Constructs the flat config items for test linting, extending
967
1021
  * the recommended Vitest rule sets.
968
- * @param options Test configuration options.
969
1022
  * @returns Promise resolving to test ESLint config items.
970
1023
  */
971
- async function test(options) {
972
- const resolved = defu(options, testDefaults);
1024
+ async function test() {
973
1025
  const testPlugin = await importModule(import("@vitest/eslint-plugin"));
974
- const baseRules = extractRules(testPlugin.configs.recommended);
1026
+ const files = [testGlob];
1027
+ const recommendedConfig = testPlugin.configs.recommended;
1028
+ const { rules } = recommendedConfig;
975
1029
  return [
976
1030
  {
977
- name: "favorodera/test/setup",
978
- plugins: { test: testPlugin }
1031
+ ...omit(recommendedConfig, ["rules", "name"]),
1032
+ name: "favorodera/test/setup"
979
1033
  },
980
1034
  {
981
- files: resolved.files,
1035
+ files,
982
1036
  name: "favorodera/test/rules",
983
1037
  rules: {
984
- ...renamePluginsInRules(baseRules, { vitest: "test" }),
1038
+ ...rules,
985
1039
  "test/consistent-each-for": ["error", {
986
1040
  describe: "for",
987
1041
  it: "for",
@@ -1034,12 +1088,11 @@ async function test(options) {
1034
1088
  "test/require-hook": "error",
1035
1089
  "test/require-to-throw-message": "error",
1036
1090
  "test/require-top-level-describe": "error",
1037
- "test/warn-todo": "warn",
1038
- ...resolved.overrides
1091
+ "test/warn-todo": "warn"
1039
1092
  }
1040
1093
  },
1041
1094
  {
1042
- files: resolved.files,
1095
+ files,
1043
1096
  name: "favorodera/test/disables",
1044
1097
  rules: { "no-unused-expressions": "off" }
1045
1098
  }
@@ -1047,29 +1100,30 @@ async function test(options) {
1047
1100
  }
1048
1101
  //#endregion
1049
1102
  //#region src/configs/typescript.ts
1050
- const typescriptDefaults = { files: [tsGlob] };
1051
1103
  /**
1052
1104
  * Constructs the flat config items for TypeScript linting, initializing the parser
1053
1105
  * and extending the recommended and strict type-aware rule sets.
1054
- * @param options TypeScript configuration options.
1055
1106
  * @returns Promise resolving to TypeScript ESLint config items.
1056
1107
  */
1057
- async function typescript(options) {
1058
- const resolved = defu(options, typescriptDefaults);
1108
+ async function typescript() {
1059
1109
  const tsEsLint = await importModule(import("typescript-eslint"));
1060
- 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
+ };
1061
1119
  return [{
1062
- name: "favorodera/typescript/setup",
1063
- plugins: { ts: tsEsLint.plugin }
1120
+ ...baseRest,
1121
+ name: "favorodera/typescript/setup"
1064
1122
  }, {
1065
- files: resolved.files,
1066
- languageOptions: {
1067
- parser: tsEsLint.parser,
1068
- parserOptions: { sourceType: "module" }
1069
- },
1123
+ files,
1070
1124
  name: "favorodera/typescript/rules",
1071
1125
  rules: {
1072
- ...renamePluginsInRules(baseRules, { "@typescript-eslint": "ts" }),
1126
+ ...rules,
1073
1127
  "ts/array-type": ["error", {
1074
1128
  default: "generic",
1075
1129
  readonly: "generic"
@@ -1079,66 +1133,65 @@ async function typescript(options) {
1079
1133
  "ts/method-signature-style": "error",
1080
1134
  "ts/no-import-type-side-effects": "error",
1081
1135
  "ts/no-loop-func": "error",
1082
- "ts/no-redeclare": "error",
1083
- ...resolved.overrides
1136
+ "ts/no-redeclare": "error"
1084
1137
  }
1085
1138
  }];
1086
1139
  }
1087
1140
  //#endregion
1088
1141
  //#region src/configs/unicorn.ts
1089
- const unicornDefaults = { files: [
1090
- jsGlob,
1091
- tsGlob,
1092
- vueGlob
1093
- ] };
1094
1142
  /**
1095
1143
  * Constructs the flat config items for Unicorn linting, providing a collection of
1096
1144
  * awesome ESLint rules to improve code quality and enforce best practices.
1097
- * @param options Unicorn configuration options.
1098
1145
  * @returns Promise resolving to Unicorn ESLint config items.
1099
1146
  */
1100
- async function unicorn(options) {
1101
- const resolved = defu(options, unicornDefaults);
1147
+ async function unicorn() {
1102
1148
  const unicornPlugin = await importModule(import("eslint-plugin-unicorn"));
1103
- 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;
1104
1156
  return [{
1105
- name: "favorodera/unicorn/setup",
1106
- plugins: { unicorn: unicornPlugin }
1157
+ ...omit(recommendedConfig, [
1158
+ "rules",
1159
+ "files",
1160
+ "name"
1161
+ ]),
1162
+ name: "favorodera/unicorn/setup"
1107
1163
  }, {
1108
- files: resolved.files,
1109
- languageOptions: { globals: globals.builtin },
1164
+ files,
1110
1165
  name: "favorodera/unicorn/rules",
1111
1166
  rules: {
1112
- ...baseRules,
1167
+ ...rules,
1113
1168
  "unicorn/filename-case": "off",
1114
1169
  "unicorn/no-process-exit": "off",
1115
- "unicorn/prevent-abbreviations": "off",
1116
- ...resolved.overrides
1170
+ "unicorn/prevent-abbreviations": "off"
1117
1171
  }
1118
1172
  }];
1119
1173
  }
1120
1174
  //#endregion
1121
1175
  //#region src/configs/unused-imports.ts
1122
- const unusedImportsDefaults = { files: [
1123
- jsGlob,
1124
- tsGlob,
1125
- vueGlob
1126
- ] };
1127
1176
  /**
1128
1177
  * Constructs the flat config items for unused imports linting, providing plugin setup and
1129
1178
  * specific rules to detect and remove unused imports and variables.
1130
- * @param options Configuration options for unused imports linting.
1131
1179
  * @returns Promise resolving to unused imports ESLint config items.
1132
1180
  */
1133
- async function unusedImports(options) {
1134
- 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
+ ];
1135
1188
  return [
1136
1189
  {
1137
1190
  name: "favorodera/unused-imports/setup",
1138
- plugins: { "unused-imports": await importModule(import("eslint-plugin-unused-imports")) }
1191
+ plugins: { "unused-imports": unusedImportsPlugin }
1139
1192
  },
1140
1193
  {
1141
- files: resolved.files,
1194
+ files,
1142
1195
  name: "favorodera/unused-imports/rules",
1143
1196
  rules: {
1144
1197
  "unused-imports/no-unused-imports": "error",
@@ -1148,12 +1201,11 @@ async function unusedImports(options) {
1148
1201
  ignoreRestSiblings: true,
1149
1202
  vars: "all",
1150
1203
  varsIgnorePattern: "^_"
1151
- }],
1152
- ...resolved.overrides
1204
+ }]
1153
1205
  }
1154
1206
  },
1155
1207
  {
1156
- files: resolved.files,
1208
+ files,
1157
1209
  name: "favorodera/unused-imports/disables",
1158
1210
  rules: {
1159
1211
  "no-unused-vars": "off",
@@ -1169,167 +1221,196 @@ const sfcBlocksDefaults = { blocks: {
1169
1221
  styles: true,
1170
1222
  template: false
1171
1223
  } };
1172
- const vueDefaults = {
1173
- files: [vueGlob],
1174
- sfcBlocks: sfcBlocksDefaults
1175
- };
1224
+ const vueDefaults = { sfcBlocks: sfcBlocksDefaults };
1176
1225
  /**
1177
- * 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,
1178
1227
  * Vue block processors, and rules to enforce recommended component syntax.
1179
1228
  * @param options Vue configuration options.
1180
1229
  * @returns Promise resolving to Vue ESLint config items.
1181
1230
  */
1182
1231
  async function vue(options) {
1183
- const resolved = defu(options, vueDefaults);
1184
- const sfcBlocks = resolveOptions(resolved.sfcBlocks, sfcBlocksDefaults);
1185
- 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([
1186
1234
  importModule(import("eslint-plugin-vue")),
1187
1235
  importModule(import("vue-eslint-parser")),
1188
- importModule(import("typescript-eslint"))
1236
+ importModule(import("typescript-eslint")),
1237
+ importModule(import("eslint-plugin-vuejs-accessibility"))
1189
1238
  ]);
1190
- const baseRules = extractRules(vuePlugin.configs["flat/recommended-error"]);
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"
1246
+ ]);
1247
+ const vueRules = extractRules(vueRecommendedConfig);
1248
+ const [a11yBase] = a11yRecommendedConfig;
1249
+ const a11yBaseRest = omit(a11yBase, ["name"]);
1250
+ const a11yRules = extractRules(a11yRecommendedConfig);
1191
1251
  const processor = sfcBlocks === false ? vuePlugin.processors[".vue"] : mergeProcessors([vuePlugin.processors[".vue"], vueBlocksProcessor(sfcBlocks)]);
1192
- return [{
1193
- languageOptions: { globals: {
1194
- computed: "readonly",
1195
- defineEmits: "readonly",
1196
- defineExpose: "readonly",
1197
- defineProps: "readonly",
1198
- onMounted: "readonly",
1199
- onUnmounted: "readonly",
1200
- reactive: "readonly",
1201
- ref: "readonly",
1202
- shallowReactive: "readonly",
1203
- shallowRef: "readonly",
1204
- toRef: "readonly",
1205
- toRefs: "readonly",
1206
- watch: "readonly",
1207
- watchEffect: "readonly"
1208
- } },
1209
- name: "favorodera/vue/setup",
1210
- plugins: { vue: vuePlugin }
1211
- }, {
1212
- files: resolved.files,
1213
- languageOptions: {
1214
- parser: vueParser,
1215
- parserOptions: {
1216
- extraFileExtensions: [".vue"],
1217
- parser: tsEsLint.parser,
1218
- sourceType: "module"
1219
- }
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"
1220
1272
  },
1221
- name: "favorodera/vue/rules",
1222
- processor,
1223
- rules: {
1224
- ...baseRules,
1225
- "vue/block-lang": ["error", { script: { lang: "ts" } }],
1226
- "vue/block-order": ["error", { order: [
1227
- "script",
1228
- "template",
1229
- "style"
1230
- ] }],
1231
- "vue/block-tag-newline": ["error", {
1232
- multiline: "always",
1233
- singleline: "always"
1234
- }],
1235
- "vue/comment-directive": ["error", { reportUnusedDisableDirectives: true }],
1236
- "vue/define-macros-order": ["error", {
1237
- defineExposeLast: true,
1238
- order: [
1239
- "defineOptions",
1240
- "definePage",
1241
- "defineSlots",
1242
- "defineEmits",
1243
- "defineProps",
1244
- "defineModel"
1245
- ]
1246
- }],
1247
- "vue/define-props-declaration": ["error", "type-based"],
1248
- "vue/define-props-destructuring": ["error", { destructure: "never" }],
1249
- "vue/multi-word-component-names": "off",
1250
- "vue/next-tick-style": ["error", "promise"],
1251
- "vue/no-import-compiler-macros": "error",
1252
- "vue/no-negated-v-if-condition": "error",
1253
- "vue/no-reserved-component-names": ["error", {
1254
- disallowVue3BuiltInComponents: true,
1255
- disallowVueBuiltInComponents: true,
1256
- htmlElementCaseSensitive: false
1257
- }],
1258
- "vue/no-root-v-if": "error",
1259
- "vue/no-template-target-blank": "error",
1260
- "vue/no-unused-emit-declarations": "error",
1261
- "vue/no-unused-properties": "error",
1262
- "vue/no-unused-refs": "error",
1263
- "vue/no-use-v-else-with-v-for": "error",
1264
- "vue/no-useless-mustaches": "error",
1265
- "vue/no-useless-v-bind": "error",
1266
- "vue/padding-line-between-blocks": "error",
1267
- "vue/padding-line-between-tags": ["error", [
1268
- {
1269
- blankLine: "always",
1270
- next: "*:multi-line",
1271
- prev: "*:single-line"
1272
- },
1273
- {
1274
- blankLine: "always",
1275
- next: "*:single-line",
1276
- prev: "*:multi-line"
1277
- },
1278
- {
1279
- blankLine: "always",
1280
- next: "*:multi-line",
1281
- prev: "*:multi-line"
1282
- },
1283
- {
1284
- blankLine: "never",
1285
- next: "*:single-line",
1286
- 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"
1287
1285
  }
1288
- ]],
1289
- "vue/prefer-prop-type-boolean-first": "error",
1290
- "vue/prefer-separate-static-class": "error",
1291
- "vue/prefer-single-event-payload": "error",
1292
- "vue/prefer-use-template-ref": "error",
1293
- "vue/slot-name-casing": ["error", "kebab-case"],
1294
- "vue/v-for-delimiter-style": ["error", "in"],
1295
- ...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
+ }
1296
1372
  }
1297
- }];
1373
+ ];
1298
1374
  }
1299
1375
  //#endregion
1300
1376
  //#region src/configs/yaml.ts
1301
- const yamlDefaults = { files: [yamlGlob] };
1302
1377
  /**
1303
1378
  * Constructs the flat config items for YAML linting, setting up
1304
1379
  * the custom parser and rule validations.
1305
- * @param options YAML configuration options.
1306
1380
  * @returns Promise resolving to YAML ESLint config items.
1307
1381
  */
1308
- async function yaml(options) {
1309
- const resolved = defu(options, yamlDefaults);
1382
+ async function yaml() {
1310
1383
  const [yamlPlugin, yamlParser] = await Promise.all([importModule(import("eslint-plugin-yml")), importModule(import("yaml-eslint-parser"))]);
1311
- 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);
1312
1392
  return [
1313
1393
  {
1314
- name: "favorodera/yaml/setup",
1315
- plugins: { yaml: yamlPlugin }
1394
+ ...pluginRest,
1395
+ name: "favorodera/yaml/setup"
1316
1396
  },
1317
1397
  {
1318
- files: resolved.files,
1398
+ files: [yamlGlob],
1399
+ language: "yaml/yaml",
1319
1400
  languageOptions: { parser: yamlParser },
1320
1401
  name: "favorodera/yaml/rules",
1321
1402
  rules: {
1322
- ...renamePluginsInRules(baseRules, { yml: "yaml" }),
1403
+ ...rules,
1323
1404
  "yaml/quotes": ["error", {
1324
1405
  avoidEscape: true,
1325
1406
  prefer: "single"
1326
1407
  }],
1327
- "yaml/require-string-key": "error",
1328
- ...resolved.overrides
1408
+ "yaml/require-string-key": "error"
1329
1409
  }
1330
1410
  },
1331
1411
  {
1332
1412
  files: [pnpmWorkspaceGlob],
1413
+ language: "yaml/yaml",
1333
1414
  languageOptions: { parser: yamlParser },
1334
1415
  name: "favorodera/yaml/sort/pnpm-workspace-yaml",
1335
1416
  rules: { "yaml/sort-keys": [
@@ -1444,9 +1525,10 @@ function factory(options = {}) {
1444
1525
  "markdown": "md",
1445
1526
  "n": "node",
1446
1527
  "vitest": "test",
1528
+ "vuejs-accessibility": "vue-a11y",
1447
1529
  "yml": "yaml"
1448
1530
  });
1449
1531
  return composer;
1450
1532
  }
1451
1533
  //#endregion
1452
- 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 };