@shayanthenerd/eslint-config 0.26.0 → 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)
@@ -27,7 +27,7 @@ ESLint configuration for enforcing best practices and maintaining a consistent c
27
27
  Legend:
28
28
  - ✅ Enabled by default
29
29
  - ❌ Disabled by default
30
- - 🔎 [Automatically detected](#automatic-dependency-detection) (`autoDetectDeps: true`, `env: 'browser'` for Baseline)
30
+ - 🔎 [Automatically detected](#automatic-dependency-detection)
31
31
 
32
32
  | Category | Activation |
33
33
  | :---------------------------------------------------------------------------------------------------- | :--------: |
@@ -175,6 +175,8 @@ PNPM's strict dependency resolution avoids this issue.
175
175
  > [!NOTE]
176
176
  > For example, `eslint-plugin-storybook` depends on `storybook`, which is hoisted by NPM and Bun. As a result, the integration for `storybook` will be enabled automatically, even if you haven't explicitly installed it.
177
177
 
178
+ `configs.useBaseline` is automatically enabled when `autoDetectDeps: true` and `env: 'browser'` (both are the defaults).
179
+
178
180
  To opt out of this behavior, either [globally disable automatic dependency detection](#customization) or manually disable the unwanted integrations that were enabled automatically.
179
181
 
180
182
  ## Framework and Tool Integrations
@@ -228,7 +230,15 @@ By default, the plugin uses GitHub Flavored Markdown (GFM). You can [switch to C
228
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).
229
231
 
230
232
  ## IDE Support
231
- 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_:
232
242
  ```jsonc title=".vscode/settings.json"
233
243
  {
234
244
  /* Enforce Unix-like line endings (LF). */
@@ -284,9 +294,6 @@ Install the VS Code extensions for [ESLint][extension-eslint] and [Prettier][ext
284
294
  }
285
295
  ```
286
296
 
287
- > [!TIP]
288
- > 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.
289
-
290
297
  ## Formatting
291
298
  This configuration uses [ESLint Stylistic][plugin-stylistic] to format:
292
299
  - JavaScript and TypeScript (_js_, _cjs_, _mjs_, _jsx_, _ts_, _cts_, _mts_, _tsx_),
@@ -11,7 +11,7 @@ function getHtmlConfig(options) {
11
11
  const { overrides } = isEnabled(html) ? html : defaultOptions.configs.html;
12
12
  return mergeConfigs({
13
13
  name: "shayanthenerd/html",
14
- files: [globs.src, globs.html],
14
+ files: [globs.html],
15
15
  plugins: {
16
16
  "@html-eslint": eslintPluginHtml,
17
17
  ...isEnabled(unicorn) && { unicorn: eslintPluginUnicorn }
@@ -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
@@ -8,6 +8,7 @@ const defaultIgnorePatterns = [
8
8
  "**/typegen.d.ts",
9
9
  "**/next-env.d.ts",
10
10
  "**/components.d.ts",
11
+ "**/routeTree.gen.ts",
11
12
  "**/auto-import?(s).d.ts",
12
13
  "**/out",
13
14
  "**/dist",
@@ -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", "✔")} Active ESLint integrations:`, 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 };