@shayanthenerd/eslint-config 0.26.1 → 0.26.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/README.md CHANGED
@@ -6,7 +6,7 @@ ESLint configuration for enforcing best practices and maintaining a consistent c
6
6
  - **Smart**: Context-aware linting with [automatic dependency detection](#automatic-dependency-detection) and _.gitignore_ recognition.
7
7
  - **Comprehensive**: [Supports useful plugins](#plugin-support) for TypeScript, React & Next, Vue & Nuxt, Astro, Tailwind, and more.
8
8
  - **Type-safe**: [Fully-typed and well-documented API](#api-reference) with `overrides` support for built-in configuration objects.
9
- - **Modern**: Requires ESLint ^10.4.0 and Node.js ^20.19.0 (ESM-only)
9
+ - **Modern**: Requires ESLint ^10.6.0 and Node.js ^20.19.0 (ESM-only)
10
10
 
11
11
  ## Table of Contents
12
12
  - [Plugin Support](#plugin-support)
@@ -230,7 +230,15 @@ By default, the plugin uses GitHub Flavored Markdown (GFM). You can [switch to C
230
230
  Some rules depend on the specified Node.js version. Visit the documentation for [version-resolution options and project-specific configurations](https://github.com/eslint-community/eslint-plugin-n#configured-nodejs-version-range).
231
231
 
232
232
  ## IDE Support
233
- Install the VS Code extensions for [ESLint][extension-eslint] and [Prettier][extension-prettier]. Then add the following in _.vscode/settings.json_:
233
+ Install the VS Code extensions for [ESLint][extension-eslint] and [Prettier][extension-prettier].
234
+ > [!TIP]
235
+ > In case you're using PNPM without `shamefullyHoist: true`, add the following to your _pnpm-workspace.yaml_ and run `pnpm install --yes` so ESLint's VS Code extension works as expected:
236
+ > ```yaml title="pnpm-workspace.yaml"
237
+ > publicHoistPattern:
238
+ > - '*eslint*'
239
+ > ```
240
+
241
+ You can also add the following to your _.vscode/settings.json_:
234
242
  ```jsonc title=".vscode/settings.json"
235
243
  {
236
244
  /* Enforce Unix-like line endings (LF). */
@@ -286,9 +294,6 @@ Install the VS Code extensions for [ESLint][extension-eslint] and [Prettier][ext
286
294
  }
287
295
  ```
288
296
 
289
- > [!TIP]
290
- > These settings match the default behavior of this configuration. If you've customized any formatting or stylistic rules, update the corresponding editor settings to keep them consistent.
291
-
292
297
  ## Formatting
293
298
  This configuration uses [ESLint Stylistic][plugin-stylistic] to format:
294
299
  - JavaScript and TypeScript (_js_, _cjs_, _mjs_, _jsx_, _ts_, _cts_, _mts_, _tsx_),
@@ -16,11 +16,9 @@ function getReactConfig(options) {
16
16
  files: [globs.src],
17
17
  plugins: {
18
18
  "@eslint-react": eslintPluginReact,
19
+ "@html-eslint/react": eslintPluginHtmlReact,
19
20
  ...isEnabled(unicorn) && { unicorn: eslintPluginUnicorn },
20
- ...isEnabled(accessibility) && {
21
- "jsx-a11y": eslintPluginJsxA11y,
22
- "@html-eslint/react": eslintPluginHtmlReact
23
- }
21
+ ...isEnabled(accessibility) && { "jsx-a11y": eslintPluginJsxA11y }
24
22
  },
25
23
  rules: getReactRules(options)
26
24
  }, overrides);
@@ -2,7 +2,6 @@ import { globs } from "../helpers/globs.mjs";
2
2
  import { isEnabled } from "../utils/isEnabled.mjs";
3
3
  import { defaultOptions } from "../helpers/options/defaultOptions.mjs";
4
4
  import { getVueRules } from "../rules/vue.mjs";
5
- import { getVueAccessibilityRules } from "../rules/vueAccessibility.mjs";
6
5
  import { mergeConfigs } from "eslint-flat-config-utils";
7
6
  import eslintPluginVue from "eslint-plugin-vue";
8
7
  import { parser } from "typescript-eslint";
@@ -30,10 +29,7 @@ function getVueConfig(options) {
30
29
  vueFeatures: { filter: false }
31
30
  }
32
31
  },
33
- rules: {
34
- ...getVueRules(options),
35
- ...isVueAccessibilityEnabled && getVueAccessibilityRules(options)
36
- }
32
+ rules: getVueRules(options)
37
33
  }, overrides);
38
34
  }
39
35
  //#endregion
@@ -4,10 +4,11 @@ import { styleText } from "node:util";
4
4
  import { isPackageExists } from "local-pkg";
5
5
  //#region src/helpers/isPackageDetected.ts
6
6
  const detectedPackages = [];
7
- function logDetectedPackages() {
8
- if (detectedPackages.length > 0) {
9
- detectedPackages.sort();
10
- console.info(`${styleText("green", "✔")} ESLint integrations enabled via dependency detection:`, detectedPackages.map((packageName) => styleText("blue", packageName)).join(", "));
7
+ function logDetectedPackages(disabledConfigs) {
8
+ const nonDisabledDetectedPackages = detectedPackages.filter((packageName) => !disabledConfigs[packageName]);
9
+ if (nonDisabledDetectedPackages.length > 0) {
10
+ nonDisabledDetectedPackages.sort();
11
+ console.info(`${styleText("green", "✔")} ESLint integrations enabled via dependency detection:`, nonDisabledDetectedPackages.map((packageName) => styleText("blue", packageName)).join(", "));
11
12
  }
12
13
  }
13
14
  function isPackageDetected(packageName, options) {
@@ -1,7 +1,24 @@
1
+ import { isEnabled } from "../../utils/isEnabled.mjs";
1
2
  import { isPackageDetected, logDetectedPackages } from "../isPackageDetected.mjs";
2
3
  //#region src/helpers/options/enableDetectedConfigs.ts
3
4
  function enableDetectedConfigs(options) {
4
- const autoDetectedDeps = {
5
+ const explicitlyDisabledConfigs = {
6
+ "typescript": options.configs?.typescript === false,
7
+ "zod": options.configs?.zod === false,
8
+ "astro": options.configs?.astro === false,
9
+ "react": options.configs?.react === false,
10
+ "next": options.configs?.next === false,
11
+ "vue": options.configs?.vue === false,
12
+ "nuxt": options.configs?.nuxt === false,
13
+ "@nuxt/ui": isEnabled(options.configs?.nuxt) && options.configs?.nuxt?.ui === false,
14
+ "@nuxt/icon": isEnabled(options.configs?.nuxt) && options.configs?.nuxt?.icon === false,
15
+ "@nuxt/image": isEnabled(options.configs?.nuxt) && options.configs?.nuxt?.image === false,
16
+ "vitest": options.configs?.test?.vitest === false,
17
+ "cypress": options.configs?.test?.cypress === false,
18
+ "storybook": options.configs?.test?.storybook === false,
19
+ "@playwright/test": options.configs?.test?.playwright === false
20
+ };
21
+ const autoDetectedPackages = {
5
22
  typescript: isPackageDetected("typescript", options),
6
23
  zod: isPackageDetected("zod", options),
7
24
  astro: isPackageDetected("astro", options),
@@ -30,17 +47,17 @@ function enableDetectedConfigs(options) {
30
47
  options.configs.stylistic ??= true;
31
48
  options.configs.perfectionist ??= true;
32
49
  options.configs.useBaseline ??= options.env === "browser";
33
- options.configs.typescript ??= autoDetectedDeps.typescript;
34
- options.configs.zod ??= autoDetectedDeps.zod;
35
- options.configs.astro ??= autoDetectedDeps.astro;
36
- options.configs.vue ??= autoDetectedDeps.vue;
37
- options.configs.nuxt ??= autoDetectedDeps.nuxt;
38
- options.configs.react ??= autoDetectedDeps.react;
39
- options.configs.next ??= autoDetectedDeps.next;
40
- options.configs.test.vitest ??= autoDetectedDeps.vitest;
41
- options.configs.test.cypress ??= autoDetectedDeps.cypress;
42
- options.configs.test.storybook ??= autoDetectedDeps.storybook;
43
- options.configs.test.playwright ??= autoDetectedDeps.playwright;
50
+ options.configs.typescript ??= autoDetectedPackages.typescript;
51
+ options.configs.zod ??= autoDetectedPackages.zod;
52
+ options.configs.astro ??= autoDetectedPackages.astro;
53
+ options.configs.vue ??= autoDetectedPackages.vue;
54
+ options.configs.nuxt ??= autoDetectedPackages.nuxt;
55
+ options.configs.react ??= autoDetectedPackages.react;
56
+ options.configs.next ??= autoDetectedPackages.next;
57
+ options.configs.test.vitest ??= autoDetectedPackages.vitest;
58
+ options.configs.test.cypress ??= autoDetectedPackages.cypress;
59
+ options.configs.test.storybook ??= autoDetectedPackages.storybook;
60
+ options.configs.test.playwright ??= autoDetectedPackages.playwright;
44
61
  options.tsConfig ??= options.configs.typescript ? {
45
62
  rootDir: ".",
46
63
  filename: "tsconfig.json"
@@ -51,11 +68,11 @@ function enableDetectedConfigs(options) {
51
68
  }
52
69
  if (options.configs.nuxt) {
53
70
  if (options.configs.nuxt === true) options.configs.nuxt = {};
54
- options.configs.nuxt.ui ??= autoDetectedDeps.nuxtUI;
55
- options.configs.nuxt.icon ??= autoDetectedDeps.nuxtIcon;
56
- options.configs.nuxt.image ??= autoDetectedDeps.nuxtImage;
71
+ options.configs.nuxt.ui ??= autoDetectedPackages.nuxtUI;
72
+ options.configs.nuxt.icon ??= autoDetectedPackages.nuxtIcon;
73
+ options.configs.nuxt.image ??= autoDetectedPackages.nuxtImage;
57
74
  }
58
- if (options.autoDetectDeps === "verbose") logDetectedPackages();
75
+ if (options.autoDetectDeps === "verbose") logDetectedPackages(explicitlyDisabledConfigs);
59
76
  return options;
60
77
  }
61
78
  //#endregion
@@ -12,6 +12,7 @@ function getHtmlRules(options) {
12
12
  "@html-eslint/no-duplicate-id": "error",
13
13
  "@html-eslint/no-duplicate-in-head": "error",
14
14
  "@html-eslint/no-ineffective-attrs": "warn",
15
+ "@html-eslint/no-inline-styles": ["warn", { allowExpressions: true }],
15
16
  "@html-eslint/no-invalid-attr-value": "error",
16
17
  "@html-eslint/no-invalid-entity": "error",
17
18
  "@html-eslint/no-nested-interactive": "error",
@@ -16,7 +16,7 @@ function getJavaScriptRules(options) {
16
16
  "no-compare-neg-zero": "error",
17
17
  "no-cond-assign": "error",
18
18
  "no-const-assign": isTypeScriptEnabled ? "off" : "error",
19
- "no-constant-binary-expression": "error",
19
+ "no-constant-binary-expression": ["error", { checkRelationalComparisons: true }],
20
20
  "no-constant-condition": "error",
21
21
  "no-constructor-return": "error",
22
22
  "no-control-regex": "error",
@@ -15,7 +15,7 @@ function getNodeRules(options) {
15
15
  "n/no-unpublished-import": "error",
16
16
  "n/no-unsupported-features/es-builtins": "error",
17
17
  "n/no-unsupported-features/es-syntax": "error",
18
- "n/no-unsupported-features/node-builtins": "error",
18
+ "n/no-unsupported-features/node-builtins": ["error", { allowExperimental: true }],
19
19
  "n/prefer-global/buffer": "warn",
20
20
  "n/prefer-global/console": "warn",
21
21
  "n/prefer-global/crypto": "warn",
@@ -64,9 +64,28 @@ const packageJsonRules = {
64
64
  ] }],
65
65
  "package-json/scripts-name-casing": "warn",
66
66
  "package-json/sort-collections": ["warn", [
67
+ {
68
+ key: "author",
69
+ order: [
70
+ "name",
71
+ "email",
72
+ "url"
73
+ ]
74
+ },
75
+ {
76
+ key: "bugs",
77
+ order: ["email", "url"]
78
+ },
67
79
  "config",
68
80
  "dependencies",
81
+ "devEngines",
69
82
  "devDependencies",
83
+ "directories",
84
+ {
85
+ key: "dist",
86
+ order: ["shasum", "tarball"]
87
+ },
88
+ "engines",
70
89
  "exports",
71
90
  "husky",
72
91
  "husky.hooks",
@@ -76,6 +95,15 @@ const packageJsonRules = {
76
95
  "overrides",
77
96
  "peerDependencies",
78
97
  "peerDependenciesMeta",
98
+ {
99
+ key: "repository",
100
+ order: [
101
+ "type",
102
+ "url",
103
+ "directory"
104
+ ]
105
+ },
106
+ "release",
79
107
  "simple-git-hooks"
80
108
  ]],
81
109
  "package-json/specify-peers-locally": "warn",
@@ -16,7 +16,48 @@ const commonCallees = [
16
16
  function getReactRules(options) {
17
17
  const { react, unicorn, tailwind, useBaseline } = options.configs;
18
18
  const { imageComponents: userImageComponents, anchorComponents: userAnchorComponents, headingComponents: userHeadingComponents } = isEnabled(react) && isEnabled(react.accessibility) ? react.accessibility : defaultOptions.configs.react.accessibility;
19
- const reactRules = {
19
+ const jsxA11yRules = {
20
+ "jsx-a11y/alt-text": ["error", { img: userImageComponents }],
21
+ "jsx-a11y/anchor-ambiguous-text": "warn",
22
+ "jsx-a11y/anchor-has-content": ["error", { components: userAnchorComponents }],
23
+ "jsx-a11y/anchor-is-valid": ["error", {
24
+ specialLink: ["to"],
25
+ components: userAnchorComponents
26
+ }],
27
+ "jsx-a11y/aria-activedescendant-has-tabindex": "error",
28
+ "jsx-a11y/aria-props": "error",
29
+ "jsx-a11y/aria-proptypes": "error",
30
+ "jsx-a11y/aria-role": "error",
31
+ "jsx-a11y/aria-unsupported-elements": "error",
32
+ "jsx-a11y/autocomplete-valid": "error",
33
+ "jsx-a11y/click-events-have-key-events": "error",
34
+ "jsx-a11y/control-has-associated-label": "error",
35
+ "jsx-a11y/heading-has-content": ["error", { components: userHeadingComponents }],
36
+ "jsx-a11y/html-has-lang": "error",
37
+ "jsx-a11y/iframe-has-title": "error",
38
+ "jsx-a11y/img-redundant-alt": ["error", { components: userImageComponents }],
39
+ "jsx-a11y/interactive-supports-focus": "error",
40
+ "jsx-a11y/label-has-associated-control": "error",
41
+ "jsx-a11y/lang": "error",
42
+ "jsx-a11y/media-has-caption": "error",
43
+ "jsx-a11y/mouse-events-have-key-events": "error",
44
+ "jsx-a11y/no-access-key": "warn",
45
+ "jsx-a11y/no-aria-hidden-on-focusable": "error",
46
+ "jsx-a11y/no-autofocus": "warn",
47
+ "jsx-a11y/no-distracting-elements": "warn",
48
+ "jsx-a11y/no-interactive-element-to-noninteractive-role": "error",
49
+ "jsx-a11y/no-noninteractive-element-interactions": "error",
50
+ "jsx-a11y/no-noninteractive-element-to-interactive-role": "error",
51
+ "jsx-a11y/no-noninteractive-tabindex": "error",
52
+ "jsx-a11y/no-redundant-roles": "warn",
53
+ "jsx-a11y/no-static-element-interactions": "warn",
54
+ "jsx-a11y/prefer-tag-over-role": "warn",
55
+ "jsx-a11y/role-has-required-aria-props": "warn",
56
+ "jsx-a11y/role-supports-aria-props": "warn",
57
+ "jsx-a11y/scope": "error",
58
+ "jsx-a11y/tabindex-no-positive": "error"
59
+ };
60
+ const reactAndHtmlReactRules = {
20
61
  /*** @eslint/react ***/
21
62
  "@eslint-react/error-boundaries": "error",
22
63
  "@eslint-react/exhaustive-deps": "error",
@@ -103,46 +144,6 @@ function getReactRules(options) {
103
144
  "@eslint-react/naming-convention-context-name": "warn",
104
145
  "@eslint-react/naming-convention-id-name": "warn",
105
146
  "@eslint-react/naming-convention-ref-name": "warn",
106
- /*** jsx-a11y ***/
107
- "jsx-a11y/alt-text": ["error", { img: userImageComponents }],
108
- "jsx-a11y/anchor-ambiguous-text": "warn",
109
- "jsx-a11y/anchor-has-content": ["error", { components: userAnchorComponents }],
110
- "jsx-a11y/anchor-is-valid": ["error", {
111
- specialLink: ["to"],
112
- components: userAnchorComponents
113
- }],
114
- "jsx-a11y/aria-activedescendant-has-tabindex": "error",
115
- "jsx-a11y/aria-props": "error",
116
- "jsx-a11y/aria-proptypes": "error",
117
- "jsx-a11y/aria-role": "error",
118
- "jsx-a11y/aria-unsupported-elements": "error",
119
- "jsx-a11y/autocomplete-valid": "error",
120
- "jsx-a11y/click-events-have-key-events": "error",
121
- "jsx-a11y/control-has-associated-label": "error",
122
- "jsx-a11y/heading-has-content": ["error", { components: userHeadingComponents }],
123
- "jsx-a11y/html-has-lang": "error",
124
- "jsx-a11y/iframe-has-title": "error",
125
- "jsx-a11y/img-redundant-alt": ["error", { components: userImageComponents }],
126
- "jsx-a11y/interactive-supports-focus": "error",
127
- "jsx-a11y/label-has-associated-control": "error",
128
- "jsx-a11y/lang": "error",
129
- "jsx-a11y/media-has-caption": "error",
130
- "jsx-a11y/mouse-events-have-key-events": "error",
131
- "jsx-a11y/no-access-key": "warn",
132
- "jsx-a11y/no-aria-hidden-on-focusable": "error",
133
- "jsx-a11y/no-autofocus": "warn",
134
- "jsx-a11y/no-distracting-elements": "warn",
135
- "jsx-a11y/no-interactive-element-to-noninteractive-role": "error",
136
- "jsx-a11y/no-noninteractive-element-interactions": "error",
137
- "jsx-a11y/no-noninteractive-element-to-interactive-role": "error",
138
- "jsx-a11y/no-noninteractive-tabindex": "error",
139
- "jsx-a11y/no-redundant-roles": "warn",
140
- "jsx-a11y/no-static-element-interactions": "warn",
141
- "jsx-a11y/prefer-tag-over-role": "warn",
142
- "jsx-a11y/role-has-required-aria-props": "warn",
143
- "jsx-a11y/role-supports-aria-props": "warn",
144
- "jsx-a11y/scope": "error",
145
- "jsx-a11y/tabindex-no-positive": "error",
146
147
  /*** @html-eslint/react ***/
147
148
  "@html-eslint/react/no-invalid-attr-value": "error",
148
149
  "@html-eslint/react/use-baseline": isEnabled(useBaseline) ? ["warn", { available: useBaseline.baseline }] : "off",
@@ -152,9 +153,13 @@ function getReactRules(options) {
152
153
  "@html-eslint/react/classname-spacing": ["warn", { callees: commonCallees }],
153
154
  "@html-eslint/react/no-duplicate-classname": ["warn", { callees: commonCallees }]
154
155
  };
155
- if (isEnabled(tailwind)) reactRules["better-tailwindcss/no-duplicate-classes"] = "off";
156
- if (isEnabled(unicorn)) reactRules["unicorn/no-invalid-file-input-accept"] = "error";
157
- return reactRules;
156
+ if (isEnabled(tailwind)) reactAndHtmlReactRules["better-tailwindcss/no-duplicate-classes"] = "off";
157
+ if (isEnabled(unicorn)) reactAndHtmlReactRules["unicorn/no-invalid-file-input-accept"] = "error";
158
+ const isReactAccessibilityEnabled = isEnabled(react) && isEnabled(react.accessibility);
159
+ return {
160
+ ...reactAndHtmlReactRules,
161
+ ...isReactAccessibilityEnabled && jsxA11yRules
162
+ };
158
163
  }
159
164
  //#endregion
160
165
  export { getReactRules };
@@ -14,6 +14,7 @@ const unicornRules = {
14
14
  "unicorn/consistent-json-file-read": "error",
15
15
  "unicorn/consistent-optional-chaining": "warn",
16
16
  "unicorn/consistent-template-literal-escape": "warn",
17
+ "unicorn/consistent-tuple-labels": "warn",
17
18
  "unicorn/custom-error-definition": "error",
18
19
  "unicorn/dom-node-dataset": "warn",
19
20
  "unicorn/empty-brace-spaces": "warn",
@@ -59,7 +60,9 @@ const unicornRules = {
59
60
  "unicorn/no-invalid-character-comparison": "error",
60
61
  "unicorn/no-invalid-fetch-options": "error",
61
62
  "unicorn/no-invalid-remove-event-listener": "error",
63
+ "unicorn/no-invalid-well-known-symbol-methods": "error",
62
64
  "unicorn/no-late-current-target-access": "warn",
65
+ "unicorn/no-late-event-control": "error",
63
66
  "unicorn/no-lonely-if": "warn",
64
67
  "unicorn/no-loop-iterable-mutation": "warn",
65
68
  "unicorn/no-magic-array-flat-depth": "warn",
@@ -75,6 +78,7 @@ const unicornRules = {
75
78
  "unicorn/no-object-methods-with-collections": "warn",
76
79
  "unicorn/no-optional-chaining-on-undeclared-variable": "error",
77
80
  "unicorn/no-redundant-comparison": "warn",
81
+ "unicorn/no-return-array-push": "warn",
78
82
  "unicorn/no-selector-as-dom-name": "error",
79
83
  "unicorn/no-single-promise-in-promise-methods": "warn",
80
84
  "unicorn/no-static-only-class": "warn",
@@ -122,8 +126,10 @@ const unicornRules = {
122
126
  "unicorn/no-zero-fractions": "warn",
123
127
  "unicorn/number-literal-case": "warn",
124
128
  "unicorn/numeric-separators-style": "warn",
129
+ "unicorn/prefer-abort-signal-timeout": "warn",
125
130
  "unicorn/prefer-add-event-listener": "warn",
126
131
  "unicorn/prefer-add-event-listener-options": "warn",
132
+ "unicorn/prefer-aggregate-error": "warn",
127
133
  "unicorn/prefer-array-flat": "warn",
128
134
  "unicorn/prefer-array-flat-map": "warn",
129
135
  "unicorn/prefer-array-from-async": "warn",
@@ -147,7 +153,9 @@ const unicornRules = {
147
153
  "unicorn/prefer-dom-node-append": "warn",
148
154
  "unicorn/prefer-dom-node-html-methods": "warn",
149
155
  "unicorn/prefer-dom-node-remove": "warn",
156
+ "unicorn/prefer-dom-node-replace-children": "warn",
150
157
  "unicorn/prefer-dom-node-text-content": "warn",
158
+ "unicorn/prefer-error-is-error": "error",
151
159
  "unicorn/prefer-event-target": "warn",
152
160
  "unicorn/prefer-export-from": ["warn", { checkUsedVariables: false }],
153
161
  "unicorn/prefer-flat-math-min-max": "warn",
@@ -184,9 +192,11 @@ const unicornRules = {
184
192
  "unicorn/prefer-object-destructuring-defaults": "warn",
185
193
  "unicorn/prefer-object-from-entries": "warn",
186
194
  "unicorn/prefer-object-iterable-methods": "warn",
195
+ "unicorn/prefer-observer-apis": "warn",
187
196
  "unicorn/prefer-optional-catch-binding": "warn",
188
197
  "unicorn/prefer-path2d": "warn",
189
198
  "unicorn/prefer-private-class-fields": "warn",
199
+ "unicorn/prefer-promise-try": "warn",
190
200
  "unicorn/prefer-promise-with-resolvers": "warn",
191
201
  "unicorn/prefer-queue-microtask": "warn",
192
202
  "unicorn/prefer-reflect-apply": "warn",
@@ -194,6 +204,7 @@ const unicornRules = {
194
204
  "unicorn/prefer-regexp-test": "warn",
195
205
  "unicorn/prefer-response-static-json": "warn",
196
206
  "unicorn/prefer-scoped-selector": "warn",
207
+ "unicorn/prefer-set-methods": "warn",
197
208
  "unicorn/prefer-set-size": "warn",
198
209
  "unicorn/prefer-simple-condition-first": "warn",
199
210
  "unicorn/prefer-simple-sort-comparator": "warn",
@@ -212,12 +223,14 @@ const unicornRules = {
212
223
  "unicorn/prefer-string-trim-start-end": "warn",
213
224
  "unicorn/prefer-structured-clone": "warn",
214
225
  "unicorn/prefer-temporal": "warn",
226
+ "unicorn/prefer-toggle-attribute": "warn",
215
227
  "unicorn/prefer-top-level-await": "warn",
216
228
  "unicorn/prefer-type-error": "warn",
217
229
  "unicorn/prefer-uint8array-base64": "warn",
218
230
  "unicorn/prefer-unary-minus": "warn",
219
231
  "unicorn/prefer-url-can-parse": "warn",
220
232
  "unicorn/prefer-url-href": "warn",
233
+ "unicorn/prefer-url-search-parameters": "warn",
221
234
  "unicorn/prefer-while-loop-condition": "warn",
222
235
  "unicorn/relative-url-style": "warn",
223
236
  "unicorn/require-array-join-separator": "warn",
@@ -8,6 +8,7 @@ function getVueRules(options) {
8
8
  const { typescript, stylistic, vue, nuxt } = options.configs;
9
9
  const { indent, trailingComma, maxLineLength, maxAttributesPerLine, maxConsecutiveEmptyLines, selfCloseVoidHtmlElements } = isEnabled(stylistic) ? stylistic : defaultOptions.configs.stylistic;
10
10
  const { blockLang, blocksOrder, macrosOrder, attributesOrder, destructureProps, vForDelimiterStyle, attributeHyphenation, allowedStyleAttributes, preferVBindTrueShorthand, componentNameCaseInTemplate, preferVBindSameNameShorthand, restrictedElements: userRestrictedElements, ignoredUndefinedComponents: userIgnoredUndefinedComponents, restrictedStaticAttributes: userRestrictedStaticAttributes } = isEnabled(vue) ? vue : defaultOptions.configs.vue;
11
+ const { imageComponents: userImageComponents, anchorComponents: userAnchorComponents, accessibleChildComponents: userAccessibleChildComponents } = isEnabled(vue) && isEnabled(vue.accessibility) ? vue.accessibility : defaultOptions.configs.vue.accessibility;
11
12
  const isNuxtEnabled = isEnabled(nuxt);
12
13
  const isNuxtImageEnabled = isNuxtEnabled ? nuxt.image : void 0;
13
14
  const isNuxtUIEnabled = isNuxtEnabled ? nuxt.ui : void 0;
@@ -18,8 +19,43 @@ function getVueRules(options) {
18
19
  const restrictedVueInputs = isNuxtUIEnabled ? getRestrictedVueInputs(nuxtUIPrefix) : [];
19
20
  const isScriptLangTS = blockLang.script === "ts";
20
21
  const isStyleLangImplicit = blockLang.style === "implicit";
21
- return {
22
- "@stylistic/max-len": "off",
22
+ const vueAccessibilityRules = {
23
+ "vuejs-accessibility/alt-text": ["error", { img: userImageComponents }],
24
+ "vuejs-accessibility/anchor-has-content": ["error", {
25
+ components: userAnchorComponents,
26
+ accessibleChildren: userAccessibleChildComponents
27
+ }],
28
+ "vuejs-accessibility/aria-props": "error",
29
+ "vuejs-accessibility/aria-role": "error",
30
+ "vuejs-accessibility/aria-unsupported-elements": "error",
31
+ "vuejs-accessibility/form-control-has-label": ["error", { labelComponents: isNuxtUIEnabled ? [`${nuxtUIPrefix}FormField`] : void 0 }],
32
+ "vuejs-accessibility/heading-has-content": "error",
33
+ "vuejs-accessibility/iframe-has-title": "error",
34
+ "vuejs-accessibility/interactive-supports-focus": "error",
35
+ "vuejs-accessibility/label-has-for": ["error", {
36
+ allowChildren: true,
37
+ required: { some: ["nesting", "id"] },
38
+ controlComponents: [
39
+ "input",
40
+ "output",
41
+ "meter",
42
+ "select",
43
+ "textarea",
44
+ "progress"
45
+ ]
46
+ }],
47
+ "vuejs-accessibility/media-has-caption": "error",
48
+ "vuejs-accessibility/no-access-key": "warn",
49
+ "vuejs-accessibility/no-aria-hidden-on-focusable": "error",
50
+ "vuejs-accessibility/no-autofocus": "warn",
51
+ "vuejs-accessibility/no-distracting-elements": "warn",
52
+ "vuejs-accessibility/no-redundant-roles": "warn",
53
+ "vuejs-accessibility/no-role-presentation-on-focusable": "error",
54
+ "vuejs-accessibility/no-static-element-interactions": "error",
55
+ "vuejs-accessibility/role-has-required-aria-props": "error",
56
+ "vuejs-accessibility/tabindex-no-positive": "error"
57
+ };
58
+ const vueAndNuxtRules = {
23
59
  "vue/jsx-uses-vars": "error",
24
60
  "vue/comment-directive": ["error", { reportUnusedDisableDirectives: true }],
25
61
  "vue/multi-word-component-names": "error",
@@ -330,7 +366,7 @@ function getVueRules(options) {
330
366
  isNuxtUIEnabled && `^${nuxtUIPrefix}`,
331
367
  ...userIgnoredUndefinedComponents
332
368
  ].filter(isTruthy) }],
333
- "vue/no-undef-directives": nuxt ? "off" : "error",
369
+ "vue/no-undef-directives": isNuxtEnabled ? "off" : "error",
334
370
  "vue/no-unused-emit-declarations": "error",
335
371
  "vue/no-unused-properties": ["error", {
336
372
  groups: [
@@ -367,6 +403,12 @@ function getVueRules(options) {
367
403
  "vue/slot-name-casing": "warn",
368
404
  "vue/v-for-delimiter-style": ["warn", vForDelimiterStyle]
369
405
  };
406
+ if (isEnabled(stylistic)) vueAndNuxtRules["@stylistic/max-len"] = "off";
407
+ const isVueAccessibilityEnabled = isEnabled(vue) && isEnabled(vue.accessibility);
408
+ return {
409
+ ...vueAndNuxtRules,
410
+ ...isVueAccessibilityEnabled && vueAccessibilityRules
411
+ };
370
412
  }
371
413
  //#endregion
372
414
  export { getVueRules };