@shayanthenerd/eslint-config 0.15.0 → 0.16.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/README.md CHANGED
@@ -4,7 +4,7 @@ A modern, flexible ESLint configuration for enforcing best practices and maintai
4
4
 
5
5
  - **Performant**: Powered by [OXLint (OXC Linter)](https://oxc.rs/docs/guide/usage/linter) for rapid linting
6
6
  - **Flat Config**: Type-safe [ESLint Flat Config](https://eslint.org/docs/latest/use/configure/configuration-files) with `extends` and `overrides` support
7
- - **Comprehensive**: Dependency detection with support for TypeScript, Vue & Nuxt, Tailwind, Storybook, Vitest & Playwright, and more
7
+ - **Comprehensive**: Dependency detection with support for TypeScript, Astro, Vue & Nuxt, Tailwind, Storybook, Vitest & Playwright, and more
8
8
  - **Automatic Formatting**: Fine-grained control over formatting with [ESLint Stylistic](https://eslint.style), eliminating the need for Prettier
9
9
  - **Smart Defaults**: Respects your _.gitignore_ file and provides reasonable, opinionated, yet [highly customizable](#customization) defaults
10
10
  - **Developer-friendly**: Easy to use and well-documented with JSDoc
@@ -32,16 +32,13 @@ A modern, flexible ESLint configuration for enforcing best practices and maintai
32
32
  1. Install the package:
33
33
  ```shell
34
34
  npm i -D @shayanthenerd/eslint-config
35
+ # or
36
+ bun i -d @shayanthenerd/eslint-config
37
+ # or
38
+ pnpm i -D @shayanthenerd/eslint-config oxlint
35
39
  ```
36
40
 
37
- OXLint and all necessary ESLint plugins and parsers will be installed automatically.
38
- > [!important]
39
- > If you're using PNPM without `shamefullyHoist: true`, make sure to install `eslint` and `oxlint` separately with `pnpm i -D eslint oxlint`, or hoist them in _pnpm-workspace.yaml_:
40
- > ```yaml
41
- > publicHoistPattern:
42
- > - eslint
43
- > - oxlint
44
- > ```
41
+ This will install OXLint along with all the necessary ESLint plugins and parsers. However, if you're using PNPM, you must install `oxlint` separately.
45
42
 
46
43
  2. Create an ESLint config file (_eslint.config.js_) at the root of your project:
47
44
  ```js
@@ -80,14 +77,20 @@ export default eslintConfigNuxt(eslintConfig);
80
77
 
81
78
  /* Customize based on your development environment. */
82
79
  "env": {
83
- "builtin": true,
84
- "es2026": true,
80
+ "worker": false,
85
81
  "commonjs": false,
82
+ "bun": false,
83
+ "deno": false,
86
84
  "node": true,
85
+ "nodeBuiltin": true,
87
86
  "browser": true,
88
- "worker": true,
89
87
  "serviceworker": false,
90
- "webextensions": false
88
+ "sharedWorker": false,
89
+ "webextension": false,
90
+ "audioWorklet": false,
91
+ "vitest": true,
92
+ "vue": true,
93
+ "astro": true
91
94
  },
92
95
 
93
96
  "categories": {
@@ -142,7 +145,7 @@ Unlike other plugins, the configuration for Tailwind isn't automatically enabled
142
145
  Stylistic, Perfectionist, ImportX, and core (JavaScript) rules are enabled by default.
143
146
 
144
147
  ## Formatting
145
- This config uses [ESLint Stylistic](https://eslint.style) to format JavaScript and TypeScript files (`?([mc])[jt]s?(x)`) as well as the `<script>` blocks in Vue components. HTML and the `<template>` blocks in Vue components are formatted with [html-eslint](https://html-eslint.org) and [eslint-plugin-vue](https://eslint.vuejs.org), respectively. For other files such as CSS, JSON, and Markdown, you'll need Prettier. To make this easier, a customizable [shared Prettier configuration](https://prettier.io/docs/sharing-configurations) is provided. Here's how to set it up:
148
+ This config uses [ESLint Stylistic](https://eslint.style) to format JavaScript and TypeScript files (`?([mc])[jt]s?(x)`) as well as Astro (similar to JSX/TSX) and the `<script>` blocks in Vue components. HTML and the `<template>` blocks in Vue components are formatted with [html-eslint](https://html-eslint.org) and [eslint-plugin-vue](https://eslint.vuejs.org), respectively. For other files such as CSS, JSON, and Markdown, you'll need Prettier. To make this easier, a customizable [shared Prettier configuration](https://prettier.io/docs/sharing-configurations) is provided. Here's how to set it up:
146
149
 
147
150
  1. Install Prettier:
148
151
  ```shell
@@ -172,17 +175,15 @@ export default {
172
175
  } satisfies Config;
173
176
  ```
174
177
 
175
- 3. To prevent conflicts with ESLint, Prettier should be configured to only format files other than JavaScript, TypeScript, HTML, and Vue. Hence, add the following script to your _package.json_ file:
178
+ 3. To prevent conflicts with ESLint, Prettier should be configured to only format files other than JavaScript, TypeScript, Astro, HTML, and Vue. Hence, add the following script to your _package.json_ file:
176
179
  ```json
177
180
  {
178
181
  "scripts": {
179
- "format": "prettier --write . '!**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx,html,vue}' --cache"
182
+ "format": "prettier --write . '!**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx,html,vue,astro}' --cache"
180
183
  }
181
184
  }
182
185
  ```
183
186
 
184
- For IDE setup guidance, see [VS Code Integration](#vs-code-integration).
185
-
186
187
  ## VS Code Integration
187
188
  Install VS Code extensions for [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint), [OXLint](https://marketplace.visualstudio.com/items?itemName=oxc.oxc-vscode), and [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode). Then, add the following in the _.vscode/settings.json_ file of your project:
188
189
  ```jsonc
@@ -212,7 +213,7 @@ Install VS Code extensions for [ESLint](https://marketplace.visualstudio.com/ite
212
213
 
213
214
  /* Format and lint JavaScript, TypeScript, HTML, and Vue files with ESLint, while everything else is formatted with Prettier. */
214
215
  "editor.defaultFormatter": "esbenp.prettier-vscode",
215
- "[javascript][typescript][javascriptreact][typescriptreact][html][vue]": {
216
+ "[javascript][typescript][javascriptreact][typescriptreact][html][vue][astro]": {
216
217
  "editor.defaultFormatter": "dbaeumer.vscode-eslint"
217
218
  },
218
219
  "eslint.validate": [
@@ -223,7 +224,8 @@ Install VS Code extensions for [ESLint](https://marketplace.visualstudio.com/ite
223
224
  "html",
224
225
  "css",
225
226
  "tailwindcss",
226
- "vue"
227
+ "vue",
228
+ "astro"
227
229
  ],
228
230
 
229
231
  /* Adjust these based on the features you're using to silently auto-fix the stylistic rules in your IDE. */
@@ -351,12 +353,20 @@ export default defineConfig(
351
353
  basePath?: string,
352
354
  ignores?: string[],
353
355
  globals?: {
354
- node?: boolean,
356
+ worker?: boolean,
355
357
  commonjs?: boolean,
358
+ bun?: boolean,
359
+ deno?: boolean,
360
+ node?: boolean,
361
+ nodeBuiltin?: boolean,
356
362
  browser?: boolean,
357
- worker?: boolean,
358
363
  serviceworker?: boolean,
364
+ sharedWorker?: boolean,
359
365
  webextension?: boolean,
366
+ audioWorklet?: boolean,
367
+ vitest?: boolean,
368
+ vue?: boolean,
369
+ astro?: boolean,
360
370
  custom?: {
361
371
  [key: string]: boolean | 'off' | 'readonly' | 'readable' | 'writable' | 'writeable',
362
372
  },
@@ -0,0 +1,34 @@
1
+ import { globs } from "../helpers/globs.mjs";
2
+ import { isEnabled } from "../utils/isEnabled.mjs";
3
+ import { defaultOptions } from "../helpers/options/defaultOptions.mjs";
4
+ import { getAstroRules } from "../rules/astro.mjs";
5
+ import { mergeConfigs } from "eslint-flat-config-utils";
6
+ import { parser } from "typescript-eslint";
7
+ import eslintPluginAstro from "eslint-plugin-astro";
8
+ import * as eslintParserAstro from "astro-eslint-parser";
9
+
10
+ //#region src/configs/astro.ts
11
+ function getAstroConfig(options) {
12
+ const { astro } = options.configs;
13
+ const { overrides } = isEnabled(astro) ? astro : defaultOptions.configs.astro;
14
+ return mergeConfigs({
15
+ name: "shayanthenerd/astro",
16
+ files: [globs.astro],
17
+ plugins: { astro: eslintPluginAstro },
18
+ languageOptions: {
19
+ globals: {
20
+ Astro: "readonly",
21
+ Fragment: "readonly"
22
+ },
23
+ parser: eslintParserAstro,
24
+ parserOptions: {
25
+ parser,
26
+ extraFileExtensions: [".astro"]
27
+ }
28
+ },
29
+ rules: getAstroRules()
30
+ }, overrides);
31
+ }
32
+
33
+ //#endregion
34
+ export { getAstroConfig };
@@ -7,10 +7,14 @@ import globals from "globals";
7
7
 
8
8
  //#region src/configs/base.ts
9
9
  function getBaseConfig(options) {
10
- const { env, configs: { vue, base: { overrides } }, global: { globals: { bun, node, deno, vitest, worker, browser, commonjs, nodeBuiltin, audioWorklet, webextension, sharedWorker, serviceworker, vue: vueGlobals, custom: userGlobals } } } = options;
10
+ const { env, configs: { vue, astro, base: { overrides } }, global: { globals: { bun, node, deno, vitest, worker, browser, commonjs, nodeBuiltin, audioWorklet, webextension, sharedWorker, serviceworker, vue: vueGlobals, astro: astroGlobals, custom: userGlobals } } } = options;
11
11
  return mergeConfigs({
12
12
  name: "shayanthenerd/base",
13
- files: isEnabled(vue) ? [globs.src, globs.vue] : [globs.src],
13
+ files: [
14
+ globs.src,
15
+ isEnabled(vue) ? globs.vue : "",
16
+ isEnabled(astro) ? globs.astro : ""
17
+ ].filter(Boolean),
14
18
  languageOptions: {
15
19
  parser,
16
20
  parserOptions: {
@@ -36,6 +40,7 @@ function getBaseConfig(options) {
36
40
  ...audioWorklet && env === "browser" && globals.audioWorklet,
37
41
  ...vitest && globals.vitest,
38
42
  ...vueGlobals && isEnabled(vue) && globals.vue,
43
+ ...astroGlobals && isEnabled(astro) && globals.astro,
39
44
  ...userGlobals
40
45
  }
41
46
  },
@@ -14,7 +14,6 @@ function getHTMLConfig(options) {
14
14
  name: "shayanthenerd/html",
15
15
  files: [globs.html],
16
16
  plugins: { "@html-eslint": eslintPluginHTML },
17
- language: "html/html",
18
17
  languageOptions: { parser: eslintParserHTML },
19
18
  rules: getHTMLRules(options)
20
19
  }, overrides);
@@ -8,11 +8,15 @@ import eslintPluginUnusedImports from "eslint-plugin-unused-imports";
8
8
 
9
9
  //#region src/configs/importX.ts
10
10
  function getImportXConfig(options) {
11
- const { vue, importX, typescript } = options.configs;
11
+ const { vue, astro, importX, typescript } = options.configs;
12
12
  const { overrides, removeUnusedImports } = isEnabled(importX) ? importX : defaultOptions.configs.importX;
13
13
  return mergeConfigs({
14
14
  name: "shayanthenerd/imports",
15
- files: isEnabled(vue) ? [globs.src, globs.vue] : [globs.src],
15
+ files: [
16
+ globs.src,
17
+ isEnabled(vue) ? globs.vue : "",
18
+ isEnabled(astro) ? globs.astro : ""
19
+ ].filter(Boolean),
16
20
  plugins: {
17
21
  "import-x": eslintPluginImportX,
18
22
  ...removeUnusedImports && { "unused-imports": eslintPluginUnusedImports }
@@ -5,9 +5,9 @@ import { getBaseConfig } from "./base.mjs";
5
5
  import { getVitestConfig } from "./vitest.mjs";
6
6
  import { getPlaywrightConfig } from "./playwright.mjs";
7
7
  import { getTypeScriptConfig } from "./typescript.mjs";
8
+ import eslintPluginVue from "eslint-plugin-vue";
8
9
  import typescriptESLint from "typescript-eslint";
9
10
  import eslintPluginVitest from "@vitest/eslint-plugin";
10
- import eslintPluginImportX from "eslint-plugin-import-x";
11
11
  import eslintPluginPlaywright from "eslint-plugin-playwright";
12
12
 
13
13
  //#region src/configs/oxlintOverrides.ts
@@ -15,7 +15,7 @@ import eslintPluginPlaywright from "eslint-plugin-playwright";
15
15
  * Prevent OXLint from overriding rules that are customizable via the configuration options.
16
16
  */
17
17
  function getOXLintOverridesConfig(options) {
18
- const { vue, importX, typescript, test: { vitest, playwright } } = options.configs;
18
+ const { vue, astro, typescript, test: { vitest, playwright } } = options.configs;
19
19
  const vueRules = getVueConfig(options).rules;
20
20
  const vitestRules = getVitestConfig(options).rules;
21
21
  const javascriptRules = getBaseConfig(options).rules;
@@ -26,11 +26,12 @@ function getOXLintOverridesConfig(options) {
26
26
  files: [
27
27
  globs.src,
28
28
  isEnabled(vue) ? globs.vue : "",
29
+ isEnabled(astro) ? globs.astro : "",
29
30
  isEnabled(vitest) || isEnabled(playwright) ? globs.test : ""
30
31
  ].filter(Boolean),
31
32
  plugins: {
33
+ ...isEnabled(vue) && { vue: eslintPluginVue },
32
34
  ...isEnabled(vitest) && { vitest: eslintPluginVitest },
33
- ...isEnabled(importX) && { "import-x": eslintPluginImportX },
34
35
  ...isEnabled(playwright) && { playwright: eslintPluginPlaywright },
35
36
  ...isEnabled(typescript) && { "@typescript-eslint": typescriptESLint.plugin }
36
37
  },
@@ -7,11 +7,15 @@ import eslintPluginPerfectionist from "eslint-plugin-perfectionist";
7
7
 
8
8
  //#region src/configs/perfectionist.ts
9
9
  function getPerfectionistConfig(options) {
10
- const { vue, perfectionist } = options.configs;
10
+ const { vue, astro, perfectionist } = options.configs;
11
11
  const { sortType, overrides } = isEnabled(perfectionist) ? perfectionist : defaultOptions.configs.perfectionist;
12
12
  return mergeConfigs({
13
13
  name: "shayanthenerd/perfectionist",
14
- files: isEnabled(vue) ? [globs.src, globs.vue] : [globs.src],
14
+ files: [
15
+ globs.src,
16
+ isEnabled(vue) ? globs.vue : "",
17
+ isEnabled(astro) ? globs.astro : ""
18
+ ].filter(Boolean),
15
19
  plugins: { perfectionist: eslintPluginPerfectionist },
16
20
  settings: { perfectionist: {
17
21
  type: sortType,
@@ -7,11 +7,15 @@ import eslintPluginStylistic from "@stylistic/eslint-plugin";
7
7
 
8
8
  //#region src/configs/stylistic.ts
9
9
  function getStylisticConfig(options) {
10
- const { vue, stylistic } = options.configs;
10
+ const { vue, astro, stylistic } = options.configs;
11
11
  const { overrides } = isEnabled(stylistic) ? stylistic : defaultOptions.configs.stylistic;
12
12
  return mergeConfigs({
13
13
  name: "shayanthenerd/stylistic",
14
- files: isEnabled(vue) ? [globs.src, globs.vue] : [globs.src],
14
+ files: [
15
+ globs.src,
16
+ isEnabled(vue) ? globs.vue : "",
17
+ isEnabled(astro) ? globs.astro : ""
18
+ ].filter(Boolean),
15
19
  plugins: { "@stylistic": eslintPluginStylistic },
16
20
  rules: getStylisticRules(options)
17
21
  }, overrides);
@@ -4,37 +4,37 @@ import { defaultOptions } from "../helpers/options/defaultOptions.mjs";
4
4
  import { getTailwindRules } from "../rules/tailwind.mjs";
5
5
  import path from "node:path";
6
6
  import { mergeConfigs } from "eslint-flat-config-utils";
7
- import eslintPluginVue from "eslint-plugin-vue";
8
- import eslintPluginHTML from "@html-eslint/eslint-plugin";
9
7
  import eslintPluginTailwind from "eslint-plugin-better-tailwindcss";
10
8
 
11
9
  //#region src/configs/tailwind.ts
12
- const eslintParserHTML = (eslintPluginHTML.configs?.["flat/recommended"])?.languageOptions?.parser;
13
- const eslintParserVue = eslintPluginVue.configs["flat/recommended"].find((config) => {
14
- return config.name === "vue/base/setup-for-vue";
15
- })?.languageOptions?.parser;
16
10
  const vueAttributes = [["^v-bind:ui$", [{ match: "objectValues" }]], ["^(?:v-bind:)?(class|activeClass|inactiveClass)$", [
17
11
  { match: "strings" },
18
12
  { match: "objectKeys" },
19
13
  { match: "objectValues" }
20
14
  ]]];
15
+ const astroAttributes = [["^class:list$", [
16
+ { match: "strings" },
17
+ { match: "objectKeys" },
18
+ { match: "objectValues" }
19
+ ]]];
21
20
  function getTailwindConfig(options) {
22
- const { tsConfig, configs: { vue, html, tailwind } } = options;
21
+ const { tsConfig, configs: { vue, html, astro, tailwind } } = options;
23
22
  const { config, entryPoint, overrides } = isEnabled(tailwind) ? tailwind : defaultOptions.configs.tailwind;
23
+ const attributes = isEnabled(vue) || isEnabled(astro) ? [...isEnabled(vue) ? vueAttributes : [], ...isEnabled(astro) ? astroAttributes : []].filter(Boolean) : void 0;
24
24
  return mergeConfigs({
25
25
  name: "shayanthenerd/tailwind",
26
26
  files: [
27
27
  globs.src,
28
- vue ? globs.vue : "",
29
- html ? globs.html : ""
28
+ isEnabled(vue) ? globs.vue : "",
29
+ isEnabled(html) ? globs.html : "",
30
+ isEnabled(astro) ? globs.astro : ""
30
31
  ].filter(Boolean),
31
32
  plugins: { "better-tailwindcss": eslintPluginTailwind },
32
- languageOptions: { parserOptions: { parser: isEnabled(html) ? eslintParserHTML : eslintParserVue } },
33
33
  settings: { "better-tailwindcss": {
34
34
  entryPoint: entryPoint || void 0,
35
35
  tailwindConfig: config || void 0,
36
- attributes: isEnabled(vue) ? vueAttributes : void 0,
37
- tsconfig: tsConfig ? path.resolve(tsConfig.rootDir, tsConfig.filename) : void 0
36
+ tsconfig: tsConfig ? path.resolve(tsConfig.rootDir, tsConfig.filename) : void 0,
37
+ attributes
38
38
  } },
39
39
  rules: getTailwindRules(options)
40
40
  }, overrides);
@@ -8,12 +8,16 @@ import { parser, plugin } from "typescript-eslint";
8
8
 
9
9
  //#region src/configs/typescript.ts
10
10
  function getTypeScriptConfig(options) {
11
- const { tsConfig, configs: { vue, typescript } } = options;
11
+ const { tsConfig, configs: { vue, astro, typescript } } = options;
12
12
  const { allowedDefaultProjects } = isEnabled(typescript) ? typescript : defaultOptions.configs.typescript;
13
13
  const { overrides } = isEnabled(typescript) ? typescript : defaultOptions.configs.typescript;
14
14
  return mergeConfigs({
15
15
  name: "shayanthenerd/typescript",
16
- files: isEnabled(vue) ? [globs.ts, globs.vue] : [globs.ts],
16
+ files: [
17
+ globs.src,
18
+ isEnabled(vue) ? globs.vue : "",
19
+ isEnabled(astro) ? globs.astro : ""
20
+ ].filter(Boolean),
17
21
  plugins: { "@typescript-eslint": plugin },
18
22
  languageOptions: {
19
23
  parser,
@@ -7,11 +7,15 @@ import eslintPluginZodX from "eslint-plugin-zod-x";
7
7
 
8
8
  //#region src/configs/zod.ts
9
9
  function getZodConfig(options) {
10
- const { zod, vue } = options.configs;
10
+ const { zod, vue, astro } = options.configs;
11
11
  const { overrides } = isEnabled(zod) ? zod : defaultOptions.configs.zod;
12
12
  return mergeConfigs({
13
13
  name: "shayanthenerd/zod",
14
- files: isEnabled(vue) ? [globs.src, globs.vue] : [globs.src],
14
+ files: [
15
+ globs.src,
16
+ isEnabled(vue) ? globs.vue : "",
17
+ isEnabled(astro) ? globs.astro : ""
18
+ ].filter(Boolean),
15
19
  plugins: { "zod-x": eslintPluginZodX },
16
20
  rules: getZodRules()
17
21
  }, overrides);
@@ -19,6 +19,7 @@ const globs = {
19
19
  vue: `**/*.${vueExtensions}`,
20
20
  vueServerComponents: `**/*.server.${vueExtensions}`,
21
21
  vueAppErrorLayoutsPages: `**/{{app,error},{layouts,pages}/**/*}.${vueExtensions}`,
22
+ astro: "**/*.astro",
22
23
  storybook: `**/*.(story|stories).${srcExtensions}`,
23
24
  test: `**/{__tests__/*,*.{test,spec,cy,bench?(mark)}.${srcExtensions}`
24
25
  };
@@ -36,6 +36,7 @@ const defaultIgnorePatterns = [
36
36
  "**/.yarn",
37
37
  "**/.nuxt",
38
38
  "**/.next",
39
+ "**/.astro",
39
40
  "**/.vitest",
40
41
  "**/.vercel",
41
42
  "**/.svelte-kit",
@@ -47,6 +48,7 @@ const defaultIgnorePatterns = [
47
48
  "**/.idea",
48
49
  "**/.fleet",
49
50
  "**/.history",
51
+ "**/.DS_Store",
50
52
  "**/LICENSE*",
51
53
  "**/CHANGELOG*.md",
52
54
  "**/CODEOWNERS.md",
@@ -27,6 +27,7 @@ const defaultOptions = {
27
27
  audioWorklet: true,
28
28
  vitest: false,
29
29
  vue: true,
30
+ astro: true,
30
31
  custom: {}
31
32
  },
32
33
  settings: {},
@@ -160,6 +161,7 @@ const defaultOptions = {
160
161
  icon: { component: "Icon" },
161
162
  ui: { prefix: "U" }
162
163
  },
164
+ astro: { overrides: {} },
163
165
  test: {
164
166
  storybook: { overrides: {} },
165
167
  vitest: { overrides: {} },
@@ -12,6 +12,7 @@ function enableDetectedConfigs(options) {
12
12
  options.configs.perfectionist ??= true;
13
13
  options.configs.vue ??= isPackageDetected("vue", options);
14
14
  options.configs.nuxt ??= isPackageDetected("nuxt", options);
15
+ options.configs.astro ??= isPackageDetected("astro", options);
15
16
  options.configs.typescript ??= isPackageDetected("typescript", options);
16
17
  options.configs.zod ??= isPackageDetected("zod", options);
17
18
  options.configs.test.vitest ??= isPackageDetected("vitest", options);
package/dist/index.mjs CHANGED
@@ -5,6 +5,7 @@ import { getVueConfig } from "./configs/vue.mjs";
5
5
  import { getZodConfig } from "./configs/zod.mjs";
6
6
  import { getBaseConfig } from "./configs/base.mjs";
7
7
  import { getHTMLConfig } from "./configs/html.mjs";
8
+ import { getAstroConfig } from "./configs/astro.mjs";
8
9
  import { getVitestConfig } from "./configs/vitest.mjs";
9
10
  import { getCypressConfig } from "./configs/cypress.mjs";
10
11
  import { getImportXConfig } from "./configs/importX.mjs";
@@ -35,7 +36,7 @@ import path from "node:path";
35
36
  */
36
37
  function defineConfig(options = {}, ...configs) {
37
38
  const mergedOptions = mergeWithDefaults(options);
38
- const { gitignore, global: { rules, ignores, settings, linterOptions }, configs: { css, zod, vue, html, nuxt, oxlint, importX, tailwind, stylistic, typescript, perfectionist, base: { preferNamedExports }, test: { vitest, cypress, storybook, playwright } } } = mergedOptions;
39
+ const { gitignore, global: { rules, ignores, settings, linterOptions }, configs: { css, zod, vue, html, nuxt, astro, oxlint, importX, tailwind, stylistic, typescript, perfectionist, base: { preferNamedExports }, test: { vitest, cypress, storybook, playwright } } } = mergedOptions;
39
40
  const ignorePatterns = getIgnorePatterns({
40
41
  gitignore,
41
42
  patterns: ignores
@@ -63,6 +64,7 @@ function defineConfig(options = {}, ...configs) {
63
64
  isEnabled(vue) && getVueConfig(mergedOptions),
64
65
  isEnabled(vue) && getVueComponentNamesConfig(),
65
66
  isEnabled(vue) && isEnabled(nuxt) && getVueServerComponentsConfig(),
67
+ isEnabled(astro) && getAstroConfig(mergedOptions),
66
68
  isEnabled(storybook) && getStorybookConfig(mergedOptions),
67
69
  isEnabled(vitest) && getVitestConfig(mergedOptions),
68
70
  isEnabled(cypress) && getCypressConfig(mergedOptions),
@@ -1,27 +1,6 @@
1
1
  {
2
2
  "$schema": "../node_modules/oxlint/configuration_schema.json",
3
3
 
4
- "env": {
5
- "builtin": true,
6
- "es2026": true,
7
- "commonjs": false,
8
- "node": true,
9
- "browser": true,
10
- "worker": true,
11
- "serviceworker": false,
12
- "webextensions": false
13
- },
14
-
15
- "categories": {
16
- "correctness": "error",
17
- "suspicious": "error",
18
- "restriction": "error",
19
- "pedantic": "error",
20
- "perf": "warn",
21
- "style": "warn",
22
- "nursery": "error"
23
- },
24
-
25
4
  "plugins": [
26
5
  "oxc",
27
6
  "vue",
@@ -90,6 +69,7 @@
90
69
  "import/no-named-as-default-member": "off",
91
70
  "import/no-named-export": "off",
92
71
  "import/no-namespace": "off",
72
+ "import/no-unassigned-import": "off",
93
73
  "import/prefer-default-export": "off",
94
74
  "import/unambiguous": "off",
95
75
 
@@ -138,74 +118,5 @@
138
118
  "eslint/no-undef": "off"
139
119
  }
140
120
  }
141
- ],
142
-
143
- "ignorePatterns": [
144
- /* Dependencies */
145
- "**/*.min.*",
146
- "**/jspm_packages",
147
- "**/pnpm-lock.yaml",
148
- "**/bower_components",
149
- "**/package-lock.json",
150
-
151
- /* Auto-generated type definitions */
152
- "**/typegen.d.ts",
153
- "**/components.d.ts",
154
- "**/auto-import?(s).d.ts",
155
-
156
- /* Build outputs */
157
- "**/out",
158
- "**/dist",
159
- "**/build",
160
- "**/.data",
161
- "**/output",
162
- "**/.output",
163
- "**/.serverless",
164
- "**/public/build",
165
- "**/public/static",
166
- "**/.eslint-config-inspector",
167
-
168
- /* Cache */
169
- "**/tmp",
170
- "**/.tmp",
171
- "**/.npm",
172
- "**/temp",
173
- "**/.temp",
174
- "**/cache",
175
- "**/.cache",
176
- "**/deno_dir",
177
- "**/.parcel-cache",
178
- "**/*.lerna_backup",
179
- "**/.postcss-cache",
180
- "**/.vitepress/cache",
181
- "**/vite.config.*.timestamp-*",
182
-
183
- /* Frameworks and tools */
184
- "**/.nx",
185
- "**/.vite",
186
- "**/.yarn",
187
- "**/.nuxt",
188
- "**/.next",
189
- "**/.vitest",
190
- "**/.vercel",
191
- "**/.svelte-kit",
192
- "**/.vite-inspect",
193
-
194
- /* Tests */
195
- "**/coverage",
196
- "**/_fixtures",
197
- "**/.nyc_output",
198
- "**/__snapshots__",
199
-
200
- /* Development environment */
201
- "**/.idea",
202
- "**/.fleet",
203
- "**/.history",
204
-
205
- // Documentation
206
- "**/LICENSE*",
207
- "**/CHANGELOG*.md",
208
- "**/CODEOWNERS.md",
209
- "**/CODE_OF_CONDUCT.md"
210
121
  ]
211
122
  }
@@ -0,0 +1,61 @@
1
+ //#region src/rules/astro.ts
2
+ function getAstroRules() {
3
+ return {
4
+ "import-x/exports-last": "off",
5
+ "@stylistic/jsx-one-expression-per-line": "off",
6
+ "astro/missing-client-only-directive-value": "error",
7
+ "astro/no-conflict-set-directives": "error",
8
+ "astro/no-deprecated-astro-canonicalurl": "error",
9
+ "astro/no-deprecated-astro-fetchcontent": "error",
10
+ "astro/no-deprecated-astro-resolve": "error",
11
+ "astro/no-deprecated-getentrybyslug": "error",
12
+ "astro/no-exports-from-components": "error",
13
+ "astro/no-unused-define-vars-in-style": "warn",
14
+ "astro/valid-compile": "error",
15
+ "astro/no-set-html-directive": "warn",
16
+ "astro/no-unsafe-inline-scripts": "warn",
17
+ "astro/no-set-text-directive": "warn",
18
+ "astro/no-unused-css-selector": "warn",
19
+ "astro/prefer-class-list-directive": "warn",
20
+ "astro/prefer-split-class-list": "warn",
21
+ "astro/jsx-a11y/alt-text": "error",
22
+ "astro/jsx-a11y/anchor-ambiguous-text": "warn",
23
+ "astro/jsx-a11y/anchor-has-content": "warn",
24
+ "astro/jsx-a11y/anchor-is-valid": "error",
25
+ "astro/jsx-a11y/aria-activedescendant-has-tabindex": "warn",
26
+ "astro/jsx-a11y/aria-props": "error",
27
+ "astro/jsx-a11y/aria-proptypes": "error",
28
+ "astro/jsx-a11y/aria-role": "warn",
29
+ "astro/jsx-a11y/aria-unsupported-elements": "error",
30
+ "astro/jsx-a11y/autocomplete-valid": "error",
31
+ "astro/jsx-a11y/click-events-have-key-events": "warn",
32
+ "astro/jsx-a11y/control-has-associated-label": "warn",
33
+ "astro/jsx-a11y/heading-has-content": "warn",
34
+ "astro/jsx-a11y/html-has-lang": "error",
35
+ "astro/jsx-a11y/iframe-has-title": "warn",
36
+ "astro/jsx-a11y/img-redundant-alt": "warn",
37
+ "astro/jsx-a11y/interactive-supports-focus": "warn",
38
+ "astro/jsx-a11y/label-has-associated-control": "warn",
39
+ "astro/jsx-a11y/lang": "error",
40
+ "astro/jsx-a11y/media-has-caption": "warn",
41
+ "astro/jsx-a11y/mouse-events-have-key-events": "warn",
42
+ "astro/jsx-a11y/no-access-key": "warn",
43
+ "astro/jsx-a11y/no-aria-hidden-on-focusable": "error",
44
+ "astro/jsx-a11y/no-autofocus": "warn",
45
+ "astro/jsx-a11y/no-distracting-elements": "warn",
46
+ "astro/jsx-a11y/no-interactive-element-to-noninteractive-role": "warn",
47
+ "astro/jsx-a11y/no-noninteractive-element-interactions": "warn",
48
+ "astro/jsx-a11y/no-noninteractive-element-to-interactive-role": "warn",
49
+ "astro/jsx-a11y/no-noninteractive-tabindex": "warn",
50
+ "astro/jsx-a11y/no-redundant-roles": "warn",
51
+ "astro/jsx-a11y/no-static-element-interactions": "warn",
52
+ "astro/jsx-a11y/prefer-tag-over-role": "warn",
53
+ "astro/jsx-a11y/role-has-required-aria-props": "warn",
54
+ "astro/jsx-a11y/role-supports-aria-props": "warn",
55
+ "astro/jsx-a11y/scope": "error",
56
+ "astro/jsx-a11y/tabindex-no-positive": "warn"
57
+ };
58
+ }
59
+
60
+ //#endregion
61
+ export { getAstroRules };
@@ -7,36 +7,6 @@ declare module 'eslint' {
7
7
  interface RulesRecord extends ESLintSchema {}
8
8
  }
9
9
  }
10
-
11
- // @ts-ignore - In case the package is not installed
12
- declare module 'eslint-flat-config-utils' {
13
- interface DefaultConfigNamesMap {
14
- 'shayanthenerd/ignores'?: true;
15
- 'shayanthenerd/global'?: true;
16
- 'shayanthenerd/base'?: true;
17
- 'shayanthenerd/typescript'?: true;
18
- 'shayanthenerd/html'?: true;
19
- 'shayanthenerd/css'?: true;
20
- 'shayanthenerd/imports'?: true;
21
- 'shayanthenerd/restricted-exports'?: true;
22
- 'shayanthenerd/stylistic'?: true;
23
- 'shayanthenerd/perfectionist'?: true;
24
- 'shayanthenerd/zod'?: true;
25
- 'shayanthenerd/tailwind'?: true;
26
- 'shayanthenerd/vue > setup'?: true;
27
- 'shayanthenerd/vue'?: true;
28
- 'shayanthenerd/vue/multi-word-component-names'?: true;
29
- 'shayanthenerd/vue/server-components'?: true;
30
- 'shayanthenerd/storybook'?: true;
31
- 'shayanthenerd/vitest'?: true;
32
- 'shayanthenerd/cypress'?: true;
33
- 'shayanthenerd/playwright'?: true;
34
- 'oxlint/oxlint-config-ignore-patterns'?: true;
35
- 'oxlint/from-oxlint-config'?: true;
36
- 'oxlint/from-oxlint-config-override-0'?: true;
37
- 'shayanthenerd/oxlint/overrides'?: true;
38
- }
39
- }
40
10
  interface ESLintSchema {
41
11
  /**
42
12
  * Enforce newline between attributes
@@ -1525,6 +1495,276 @@ interface ESLintSchema {
1525
1495
  * @deprecated
1526
1496
  */
1527
1497
  'arrow-spacing'?: Linter.RuleEntry<ArrowSpacing>;
1498
+ /**
1499
+ * apply `jsx-a11y/alt-text` rule to Astro components
1500
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/alt-text/
1501
+ */
1502
+ 'astro/jsx-a11y/alt-text'?: Linter.RuleEntry<AstroJsxA11YAltText>;
1503
+ /**
1504
+ * apply `jsx-a11y/anchor-ambiguous-text` rule to Astro components
1505
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/anchor-ambiguous-text/
1506
+ */
1507
+ 'astro/jsx-a11y/anchor-ambiguous-text'?: Linter.RuleEntry<AstroJsxA11YAnchorAmbiguousText>;
1508
+ /**
1509
+ * apply `jsx-a11y/anchor-has-content` rule to Astro components
1510
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/anchor-has-content/
1511
+ */
1512
+ 'astro/jsx-a11y/anchor-has-content'?: Linter.RuleEntry<AstroJsxA11YAnchorHasContent>;
1513
+ /**
1514
+ * apply `jsx-a11y/anchor-is-valid` rule to Astro components
1515
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/anchor-is-valid/
1516
+ */
1517
+ 'astro/jsx-a11y/anchor-is-valid'?: Linter.RuleEntry<AstroJsxA11YAnchorIsValid>;
1518
+ /**
1519
+ * apply `jsx-a11y/aria-activedescendant-has-tabindex` rule to Astro components
1520
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/aria-activedescendant-has-tabindex/
1521
+ */
1522
+ 'astro/jsx-a11y/aria-activedescendant-has-tabindex'?: Linter.RuleEntry<AstroJsxA11YAriaActivedescendantHasTabindex>;
1523
+ /**
1524
+ * apply `jsx-a11y/aria-props` rule to Astro components
1525
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/aria-props/
1526
+ */
1527
+ 'astro/jsx-a11y/aria-props'?: Linter.RuleEntry<AstroJsxA11YAriaProps>;
1528
+ /**
1529
+ * apply `jsx-a11y/aria-proptypes` rule to Astro components
1530
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/aria-proptypes/
1531
+ */
1532
+ 'astro/jsx-a11y/aria-proptypes'?: Linter.RuleEntry<AstroJsxA11YAriaProptypes>;
1533
+ /**
1534
+ * apply `jsx-a11y/aria-role` rule to Astro components
1535
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/aria-role/
1536
+ */
1537
+ 'astro/jsx-a11y/aria-role'?: Linter.RuleEntry<AstroJsxA11YAriaRole>;
1538
+ /**
1539
+ * apply `jsx-a11y/aria-unsupported-elements` rule to Astro components
1540
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/aria-unsupported-elements/
1541
+ */
1542
+ 'astro/jsx-a11y/aria-unsupported-elements'?: Linter.RuleEntry<AstroJsxA11YAriaUnsupportedElements>;
1543
+ /**
1544
+ * apply `jsx-a11y/autocomplete-valid` rule to Astro components
1545
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/autocomplete-valid/
1546
+ */
1547
+ 'astro/jsx-a11y/autocomplete-valid'?: Linter.RuleEntry<AstroJsxA11YAutocompleteValid>;
1548
+ /**
1549
+ * apply `jsx-a11y/click-events-have-key-events` rule to Astro components
1550
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/click-events-have-key-events/
1551
+ */
1552
+ 'astro/jsx-a11y/click-events-have-key-events'?: Linter.RuleEntry<AstroJsxA11YClickEventsHaveKeyEvents>;
1553
+ /**
1554
+ * apply `jsx-a11y/control-has-associated-label` rule to Astro components
1555
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/control-has-associated-label/
1556
+ */
1557
+ 'astro/jsx-a11y/control-has-associated-label'?: Linter.RuleEntry<AstroJsxA11YControlHasAssociatedLabel>;
1558
+ /**
1559
+ * apply `jsx-a11y/heading-has-content` rule to Astro components
1560
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/heading-has-content/
1561
+ */
1562
+ 'astro/jsx-a11y/heading-has-content'?: Linter.RuleEntry<AstroJsxA11YHeadingHasContent>;
1563
+ /**
1564
+ * apply `jsx-a11y/html-has-lang` rule to Astro components
1565
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/html-has-lang/
1566
+ */
1567
+ 'astro/jsx-a11y/html-has-lang'?: Linter.RuleEntry<AstroJsxA11YHtmlHasLang>;
1568
+ /**
1569
+ * apply `jsx-a11y/iframe-has-title` rule to Astro components
1570
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/iframe-has-title/
1571
+ */
1572
+ 'astro/jsx-a11y/iframe-has-title'?: Linter.RuleEntry<AstroJsxA11YIframeHasTitle>;
1573
+ /**
1574
+ * apply `jsx-a11y/img-redundant-alt` rule to Astro components
1575
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/img-redundant-alt/
1576
+ */
1577
+ 'astro/jsx-a11y/img-redundant-alt'?: Linter.RuleEntry<AstroJsxA11YImgRedundantAlt>;
1578
+ /**
1579
+ * apply `jsx-a11y/interactive-supports-focus` rule to Astro components
1580
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/interactive-supports-focus/
1581
+ */
1582
+ 'astro/jsx-a11y/interactive-supports-focus'?: Linter.RuleEntry<AstroJsxA11YInteractiveSupportsFocus>;
1583
+ /**
1584
+ * apply `jsx-a11y/label-has-associated-control` rule to Astro components
1585
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/label-has-associated-control/
1586
+ */
1587
+ 'astro/jsx-a11y/label-has-associated-control'?: Linter.RuleEntry<AstroJsxA11YLabelHasAssociatedControl>;
1588
+ /**
1589
+ * apply `jsx-a11y/lang` rule to Astro components
1590
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/lang/
1591
+ */
1592
+ 'astro/jsx-a11y/lang'?: Linter.RuleEntry<AstroJsxA11YLang>;
1593
+ /**
1594
+ * apply `jsx-a11y/media-has-caption` rule to Astro components
1595
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/media-has-caption/
1596
+ */
1597
+ 'astro/jsx-a11y/media-has-caption'?: Linter.RuleEntry<AstroJsxA11YMediaHasCaption>;
1598
+ /**
1599
+ * apply `jsx-a11y/mouse-events-have-key-events` rule to Astro components
1600
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/mouse-events-have-key-events/
1601
+ */
1602
+ 'astro/jsx-a11y/mouse-events-have-key-events'?: Linter.RuleEntry<AstroJsxA11YMouseEventsHaveKeyEvents>;
1603
+ /**
1604
+ * apply `jsx-a11y/no-access-key` rule to Astro components
1605
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/no-access-key/
1606
+ */
1607
+ 'astro/jsx-a11y/no-access-key'?: Linter.RuleEntry<AstroJsxA11YNoAccessKey>;
1608
+ /**
1609
+ * apply `jsx-a11y/no-aria-hidden-on-focusable` rule to Astro components
1610
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/no-aria-hidden-on-focusable/
1611
+ */
1612
+ 'astro/jsx-a11y/no-aria-hidden-on-focusable'?: Linter.RuleEntry<AstroJsxA11YNoAriaHiddenOnFocusable>;
1613
+ /**
1614
+ * apply `jsx-a11y/no-autofocus` rule to Astro components
1615
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/no-autofocus/
1616
+ */
1617
+ 'astro/jsx-a11y/no-autofocus'?: Linter.RuleEntry<AstroJsxA11YNoAutofocus>;
1618
+ /**
1619
+ * apply `jsx-a11y/no-distracting-elements` rule to Astro components
1620
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/no-distracting-elements/
1621
+ */
1622
+ 'astro/jsx-a11y/no-distracting-elements'?: Linter.RuleEntry<AstroJsxA11YNoDistractingElements>;
1623
+ /**
1624
+ * apply `jsx-a11y/no-interactive-element-to-noninteractive-role` rule to Astro components
1625
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/no-interactive-element-to-noninteractive-role/
1626
+ */
1627
+ 'astro/jsx-a11y/no-interactive-element-to-noninteractive-role'?: Linter.RuleEntry<AstroJsxA11YNoInteractiveElementToNoninteractiveRole>;
1628
+ /**
1629
+ * apply `jsx-a11y/no-noninteractive-element-interactions` rule to Astro components
1630
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/no-noninteractive-element-interactions/
1631
+ */
1632
+ 'astro/jsx-a11y/no-noninteractive-element-interactions'?: Linter.RuleEntry<AstroJsxA11YNoNoninteractiveElementInteractions>;
1633
+ /**
1634
+ * apply `jsx-a11y/no-noninteractive-element-to-interactive-role` rule to Astro components
1635
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/no-noninteractive-element-to-interactive-role/
1636
+ */
1637
+ 'astro/jsx-a11y/no-noninteractive-element-to-interactive-role'?: Linter.RuleEntry<AstroJsxA11YNoNoninteractiveElementToInteractiveRole>;
1638
+ /**
1639
+ * apply `jsx-a11y/no-noninteractive-tabindex` rule to Astro components
1640
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/no-noninteractive-tabindex/
1641
+ */
1642
+ 'astro/jsx-a11y/no-noninteractive-tabindex'?: Linter.RuleEntry<AstroJsxA11YNoNoninteractiveTabindex>;
1643
+ /**
1644
+ * apply `jsx-a11y/no-redundant-roles` rule to Astro components
1645
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/no-redundant-roles/
1646
+ */
1647
+ 'astro/jsx-a11y/no-redundant-roles'?: Linter.RuleEntry<AstroJsxA11YNoRedundantRoles>;
1648
+ /**
1649
+ * apply `jsx-a11y/no-static-element-interactions` rule to Astro components
1650
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/no-static-element-interactions/
1651
+ */
1652
+ 'astro/jsx-a11y/no-static-element-interactions'?: Linter.RuleEntry<AstroJsxA11YNoStaticElementInteractions>;
1653
+ /**
1654
+ * apply `jsx-a11y/prefer-tag-over-role` rule to Astro components
1655
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/prefer-tag-over-role/
1656
+ */
1657
+ 'astro/jsx-a11y/prefer-tag-over-role'?: Linter.RuleEntry<AstroJsxA11YPreferTagOverRole>;
1658
+ /**
1659
+ * apply `jsx-a11y/role-has-required-aria-props` rule to Astro components
1660
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/role-has-required-aria-props/
1661
+ */
1662
+ 'astro/jsx-a11y/role-has-required-aria-props'?: Linter.RuleEntry<AstroJsxA11YRoleHasRequiredAriaProps>;
1663
+ /**
1664
+ * apply `jsx-a11y/role-supports-aria-props` rule to Astro components
1665
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/role-supports-aria-props/
1666
+ */
1667
+ 'astro/jsx-a11y/role-supports-aria-props'?: Linter.RuleEntry<AstroJsxA11YRoleSupportsAriaProps>;
1668
+ /**
1669
+ * apply `jsx-a11y/scope` rule to Astro components
1670
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/scope/
1671
+ */
1672
+ 'astro/jsx-a11y/scope'?: Linter.RuleEntry<AstroJsxA11YScope>;
1673
+ /**
1674
+ * apply `jsx-a11y/tabindex-no-positive` rule to Astro components
1675
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/jsx-a11y/tabindex-no-positive/
1676
+ */
1677
+ 'astro/jsx-a11y/tabindex-no-positive'?: Linter.RuleEntry<AstroJsxA11YTabindexNoPositive>;
1678
+ /**
1679
+ * the client:only directive is missing the correct component's framework value
1680
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/missing-client-only-directive-value/
1681
+ */
1682
+ 'astro/missing-client-only-directive-value'?: Linter.RuleEntry<[]>;
1683
+ /**
1684
+ * disallow conflicting set directives and child contents
1685
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/no-conflict-set-directives/
1686
+ */
1687
+ 'astro/no-conflict-set-directives'?: Linter.RuleEntry<[]>;
1688
+ /**
1689
+ * disallow using deprecated `Astro.canonicalURL`
1690
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/no-deprecated-astro-canonicalurl/
1691
+ */
1692
+ 'astro/no-deprecated-astro-canonicalurl'?: Linter.RuleEntry<[]>;
1693
+ /**
1694
+ * disallow using deprecated `Astro.fetchContent()`
1695
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/no-deprecated-astro-fetchcontent/
1696
+ */
1697
+ 'astro/no-deprecated-astro-fetchcontent'?: Linter.RuleEntry<[]>;
1698
+ /**
1699
+ * disallow using deprecated `Astro.resolve()`
1700
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/no-deprecated-astro-resolve/
1701
+ */
1702
+ 'astro/no-deprecated-astro-resolve'?: Linter.RuleEntry<[]>;
1703
+ /**
1704
+ * disallow using deprecated `getEntryBySlug()`
1705
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/no-deprecated-getentrybyslug/
1706
+ */
1707
+ 'astro/no-deprecated-getentrybyslug'?: Linter.RuleEntry<[]>;
1708
+ /**
1709
+ * disallow value export
1710
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/no-exports-from-components/
1711
+ */
1712
+ 'astro/no-exports-from-components'?: Linter.RuleEntry<[]>;
1713
+ /**
1714
+ * disallow use of `set:html` to prevent XSS attack
1715
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/no-set-html-directive/
1716
+ */
1717
+ 'astro/no-set-html-directive'?: Linter.RuleEntry<[]>;
1718
+ /**
1719
+ * disallow use of `set:text`
1720
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/no-set-text-directive/
1721
+ */
1722
+ 'astro/no-set-text-directive'?: Linter.RuleEntry<[]>;
1723
+ /**
1724
+ * disallow inline `<script>` without `src` to encourage CSP-safe patterns
1725
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/no-unsafe-inline-scripts/
1726
+ */
1727
+ 'astro/no-unsafe-inline-scripts'?: Linter.RuleEntry<AstroNoUnsafeInlineScripts>;
1728
+ /**
1729
+ * disallow selectors defined in `style` tag that don't use in HTML
1730
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/no-unused-css-selector/
1731
+ */
1732
+ 'astro/no-unused-css-selector'?: Linter.RuleEntry<[]>;
1733
+ /**
1734
+ * disallow unused `define:vars={...}` in `style` tag
1735
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/no-unused-define-vars-in-style/
1736
+ */
1737
+ 'astro/no-unused-define-vars-in-style'?: Linter.RuleEntry<[]>;
1738
+ /**
1739
+ * require `class:list` directives instead of `class` with expressions
1740
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/prefer-class-list-directive/
1741
+ */
1742
+ 'astro/prefer-class-list-directive'?: Linter.RuleEntry<[]>;
1743
+ /**
1744
+ * require use object instead of ternary expression in `class:list`
1745
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/prefer-object-class-list/
1746
+ */
1747
+ 'astro/prefer-object-class-list'?: Linter.RuleEntry<[]>;
1748
+ /**
1749
+ * require use split array elements in `class:list`
1750
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/prefer-split-class-list/
1751
+ */
1752
+ 'astro/prefer-split-class-list'?: Linter.RuleEntry<AstroPreferSplitClassList>;
1753
+ /**
1754
+ * Require or disallow semicolons instead of ASI
1755
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/semi/
1756
+ */
1757
+ 'astro/semi'?: Linter.RuleEntry<AstroSemi>;
1758
+ /**
1759
+ * enforce sorting of attributes
1760
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/sort-attributes/
1761
+ */
1762
+ 'astro/sort-attributes'?: Linter.RuleEntry<AstroSortAttributes>;
1763
+ /**
1764
+ * disallow warnings when compiling.
1765
+ * @see https://ota-meshi.github.io/eslint-plugin-astro/rules/valid-compile/
1766
+ */
1767
+ 'astro/valid-compile'?: Linter.RuleEntry<[]>;
1528
1768
  /**
1529
1769
  * Enforce a consistent order for tailwind classes.
1530
1770
  * @see https://github.com/schoero/eslint-plugin-better-tailwindcss/blob/main/docs/rules/enforce-consistent-class-order.md
@@ -8088,6 +8328,212 @@ type ArrowSpacing = [] | [{
8088
8328
  before?: boolean;
8089
8329
  after?: boolean;
8090
8330
  }];
8331
+ // ----- astro/jsx-a11y/alt-text -----
8332
+ type AstroJsxA11YAltText = [] | [{
8333
+ elements?: string[];
8334
+ img?: string[];
8335
+ object?: string[];
8336
+ area?: string[];
8337
+ "input[type=\"image\"]"?: string[];
8338
+ [k: string]: unknown | undefined;
8339
+ }];
8340
+ // ----- astro/jsx-a11y/anchor-ambiguous-text -----
8341
+ type AstroJsxA11YAnchorAmbiguousText = [] | [{
8342
+ words?: string[];
8343
+ [k: string]: unknown | undefined;
8344
+ }];
8345
+ // ----- astro/jsx-a11y/anchor-has-content -----
8346
+ type AstroJsxA11YAnchorHasContent = [] | [{
8347
+ components?: string[];
8348
+ [k: string]: unknown | undefined;
8349
+ }];
8350
+ // ----- astro/jsx-a11y/anchor-is-valid -----
8351
+ type AstroJsxA11YAnchorIsValid = [] | [{
8352
+ components?: string[];
8353
+ specialLink?: string[];
8354
+ aspects?: [("noHref" | "invalidHref" | "preferButton"), ...(("noHref" | "invalidHref" | "preferButton"))[]];
8355
+ [k: string]: unknown | undefined;
8356
+ }];
8357
+ // ----- astro/jsx-a11y/aria-activedescendant-has-tabindex -----
8358
+ type AstroJsxA11YAriaActivedescendantHasTabindex = [] | [{
8359
+ [k: string]: unknown | undefined;
8360
+ }];
8361
+ // ----- astro/jsx-a11y/aria-props -----
8362
+ type AstroJsxA11YAriaProps = [] | [{
8363
+ [k: string]: unknown | undefined;
8364
+ }];
8365
+ // ----- astro/jsx-a11y/aria-proptypes -----
8366
+ type AstroJsxA11YAriaProptypes = [] | [{
8367
+ [k: string]: unknown | undefined;
8368
+ }];
8369
+ // ----- astro/jsx-a11y/aria-role -----
8370
+ type AstroJsxA11YAriaRole = [] | [{
8371
+ allowedInvalidRoles?: string[];
8372
+ ignoreNonDOM?: boolean;
8373
+ [k: string]: unknown | undefined;
8374
+ }];
8375
+ // ----- astro/jsx-a11y/aria-unsupported-elements -----
8376
+ type AstroJsxA11YAriaUnsupportedElements = [] | [{
8377
+ [k: string]: unknown | undefined;
8378
+ }];
8379
+ // ----- astro/jsx-a11y/autocomplete-valid -----
8380
+ type AstroJsxA11YAutocompleteValid = [] | [{
8381
+ inputComponents?: string[];
8382
+ [k: string]: unknown | undefined;
8383
+ }];
8384
+ // ----- astro/jsx-a11y/click-events-have-key-events -----
8385
+ type AstroJsxA11YClickEventsHaveKeyEvents = [] | [{
8386
+ [k: string]: unknown | undefined;
8387
+ }];
8388
+ // ----- astro/jsx-a11y/control-has-associated-label -----
8389
+ type AstroJsxA11YControlHasAssociatedLabel = [] | [{
8390
+ labelAttributes?: string[];
8391
+ controlComponents?: string[];
8392
+ ignoreElements?: string[];
8393
+ ignoreRoles?: string[];
8394
+ depth?: number;
8395
+ [k: string]: unknown | undefined;
8396
+ }];
8397
+ // ----- astro/jsx-a11y/heading-has-content -----
8398
+ type AstroJsxA11YHeadingHasContent = [] | [{
8399
+ components?: string[];
8400
+ [k: string]: unknown | undefined;
8401
+ }];
8402
+ // ----- astro/jsx-a11y/html-has-lang -----
8403
+ type AstroJsxA11YHtmlHasLang = [] | [{
8404
+ [k: string]: unknown | undefined;
8405
+ }];
8406
+ // ----- astro/jsx-a11y/iframe-has-title -----
8407
+ type AstroJsxA11YIframeHasTitle = [] | [{
8408
+ [k: string]: unknown | undefined;
8409
+ }];
8410
+ // ----- astro/jsx-a11y/img-redundant-alt -----
8411
+ type AstroJsxA11YImgRedundantAlt = [] | [{
8412
+ components?: string[];
8413
+ words?: string[];
8414
+ [k: string]: unknown | undefined;
8415
+ }];
8416
+ // ----- astro/jsx-a11y/interactive-supports-focus -----
8417
+ type AstroJsxA11YInteractiveSupportsFocus = [] | [{
8418
+ tabbable?: ("button" | "checkbox" | "columnheader" | "combobox" | "grid" | "gridcell" | "link" | "listbox" | "menu" | "menubar" | "menuitem" | "menuitemcheckbox" | "menuitemradio" | "option" | "progressbar" | "radio" | "radiogroup" | "row" | "rowheader" | "scrollbar" | "searchbox" | "slider" | "spinbutton" | "switch" | "tab" | "tablist" | "textbox" | "tree" | "treegrid" | "treeitem" | "doc-backlink" | "doc-biblioref" | "doc-glossref" | "doc-noteref")[];
8419
+ [k: string]: unknown | undefined;
8420
+ }];
8421
+ // ----- astro/jsx-a11y/label-has-associated-control -----
8422
+ type AstroJsxA11YLabelHasAssociatedControl = [] | [{
8423
+ labelComponents?: string[];
8424
+ labelAttributes?: string[];
8425
+ controlComponents?: string[];
8426
+ assert?: ("htmlFor" | "nesting" | "both" | "either");
8427
+ depth?: number;
8428
+ [k: string]: unknown | undefined;
8429
+ }];
8430
+ // ----- astro/jsx-a11y/lang -----
8431
+ type AstroJsxA11YLang = [] | [{
8432
+ [k: string]: unknown | undefined;
8433
+ }];
8434
+ // ----- astro/jsx-a11y/media-has-caption -----
8435
+ type AstroJsxA11YMediaHasCaption = [] | [{
8436
+ audio?: string[];
8437
+ video?: string[];
8438
+ track?: string[];
8439
+ [k: string]: unknown | undefined;
8440
+ }];
8441
+ // ----- astro/jsx-a11y/mouse-events-have-key-events -----
8442
+ type AstroJsxA11YMouseEventsHaveKeyEvents = [] | [{
8443
+ hoverInHandlers?: string[];
8444
+ hoverOutHandlers?: string[];
8445
+ [k: string]: unknown | undefined;
8446
+ }];
8447
+ // ----- astro/jsx-a11y/no-access-key -----
8448
+ type AstroJsxA11YNoAccessKey = [] | [{
8449
+ [k: string]: unknown | undefined;
8450
+ }];
8451
+ // ----- astro/jsx-a11y/no-aria-hidden-on-focusable -----
8452
+ type AstroJsxA11YNoAriaHiddenOnFocusable = [] | [{
8453
+ [k: string]: unknown | undefined;
8454
+ }];
8455
+ // ----- astro/jsx-a11y/no-autofocus -----
8456
+ type AstroJsxA11YNoAutofocus = [] | [{
8457
+ ignoreNonDOM?: boolean;
8458
+ [k: string]: unknown | undefined;
8459
+ }];
8460
+ // ----- astro/jsx-a11y/no-distracting-elements -----
8461
+ type AstroJsxA11YNoDistractingElements = [] | [{
8462
+ elements?: ("marquee" | "blink")[];
8463
+ [k: string]: unknown | undefined;
8464
+ }];
8465
+ // ----- astro/jsx-a11y/no-interactive-element-to-noninteractive-role -----
8466
+ type AstroJsxA11YNoInteractiveElementToNoninteractiveRole = [] | [{
8467
+ [k: string]: string[] | undefined;
8468
+ }];
8469
+ // ----- astro/jsx-a11y/no-noninteractive-element-interactions -----
8470
+ type AstroJsxA11YNoNoninteractiveElementInteractions = [] | [{
8471
+ handlers?: string[];
8472
+ [k: string]: unknown | undefined;
8473
+ }];
8474
+ // ----- astro/jsx-a11y/no-noninteractive-element-to-interactive-role -----
8475
+ type AstroJsxA11YNoNoninteractiveElementToInteractiveRole = [] | [{
8476
+ [k: string]: string[] | undefined;
8477
+ }];
8478
+ // ----- astro/jsx-a11y/no-noninteractive-tabindex -----
8479
+ type AstroJsxA11YNoNoninteractiveTabindex = [] | [{
8480
+ roles?: string[];
8481
+ tags?: string[];
8482
+ [k: string]: unknown | undefined;
8483
+ }];
8484
+ // ----- astro/jsx-a11y/no-redundant-roles -----
8485
+ type AstroJsxA11YNoRedundantRoles = [] | [{
8486
+ [k: string]: string[] | undefined;
8487
+ }];
8488
+ // ----- astro/jsx-a11y/no-static-element-interactions -----
8489
+ type AstroJsxA11YNoStaticElementInteractions = [] | [{
8490
+ handlers?: string[];
8491
+ [k: string]: unknown | undefined;
8492
+ }];
8493
+ // ----- astro/jsx-a11y/prefer-tag-over-role -----
8494
+ type AstroJsxA11YPreferTagOverRole = [] | [{
8495
+ [k: string]: unknown | undefined;
8496
+ }];
8497
+ // ----- astro/jsx-a11y/role-has-required-aria-props -----
8498
+ type AstroJsxA11YRoleHasRequiredAriaProps = [] | [{
8499
+ [k: string]: unknown | undefined;
8500
+ }];
8501
+ // ----- astro/jsx-a11y/role-supports-aria-props -----
8502
+ type AstroJsxA11YRoleSupportsAriaProps = [] | [{
8503
+ [k: string]: unknown | undefined;
8504
+ }];
8505
+ // ----- astro/jsx-a11y/scope -----
8506
+ type AstroJsxA11YScope = [] | [{
8507
+ [k: string]: unknown | undefined;
8508
+ }];
8509
+ // ----- astro/jsx-a11y/tabindex-no-positive -----
8510
+ type AstroJsxA11YTabindexNoPositive = [] | [{
8511
+ [k: string]: unknown | undefined;
8512
+ }];
8513
+ // ----- astro/no-unsafe-inline-scripts -----
8514
+ type AstroNoUnsafeInlineScripts = [] | [{
8515
+ allowDefineVars?: boolean;
8516
+ allowModuleScripts?: boolean;
8517
+ allowNonExecutingTypes?: string[];
8518
+ allowNonce?: boolean;
8519
+ }];
8520
+ // ----- astro/prefer-split-class-list -----
8521
+ type AstroPreferSplitClassList = [] | [{
8522
+ splitLiteral?: boolean;
8523
+ }];
8524
+ // ----- astro/semi -----
8525
+ type AstroSemi = ([] | ["never"] | ["never", {
8526
+ beforeStatementContinuationChars?: ("always" | "any" | "never");
8527
+ }] | [] | ["always"] | ["always", {
8528
+ omitLastInOneLineBlock?: boolean;
8529
+ omitLastInOneLineClassBody?: boolean;
8530
+ }]);
8531
+ // ----- astro/sort-attributes -----
8532
+ type AstroSortAttributes = [] | [{
8533
+ type?: ("alphabetical" | "line-length");
8534
+ ignoreCase?: boolean;
8535
+ order?: ("asc" | "desc");
8536
+ }];
8091
8537
  // ----- better-tailwindcss/enforce-consistent-class-order -----
8092
8538
  type BetterTailwindcssEnforceConsistentClassOrder = [] | [{
8093
8539
  callees?: ([] | [string] | [string, string] | [] | [string] | [string, ({
@@ -196,10 +196,11 @@ interface Options {
196
196
  * audioWorklet: false, // `true` if `env` is set to `browser`
197
197
  * vitest: false,
198
198
  * vue: false, // `true` if `configs.vue` is enabled
199
+ * astro: false, // `true` if `configs.astro` is enabled
199
200
  * custom: {},
200
201
  * }
201
202
  *
202
- * @see [Language Options: Specifying Globals: Using Configuration Files](https://eslint.org/docs/latest/use/configure/language-options#using-configuration-files)
203
+ * @see [Language Options: Specifying Globals](https://eslint.org/docs/latest/use/configure/language-options#using-configuration-files)
203
204
  */
204
205
  globals?: {
205
206
  worker?: boolean;
@@ -215,6 +216,7 @@ interface Options {
215
216
  audioWorklet?: boolean;
216
217
  vitest?: boolean;
217
218
  vue?: boolean;
219
+ astro?: boolean;
218
220
  custom?: Linter.LanguageOptions['globals'];
219
221
  };
220
222
 
@@ -374,6 +376,13 @@ interface Options {
374
376
  */
375
377
  nuxt?: boolean | NuxtOptions;
376
378
 
379
+ /**
380
+ * Use [eslint-plugin-astro](https://ota-meshi.github.io/eslint-plugin-astro) to enforce Astro best practices and accessibility guidelines.
381
+ *
382
+ * @default false // `true` if "astro" is detected in the dependencies when `autoDetectDeps` is enabled
383
+ */
384
+ astro?: boolean | ConfigWithOverrides;
385
+
377
386
  /**
378
387
  * Configuration options for the testing tools.
379
388
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shayanthenerd/eslint-config",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "license": "MIT",
5
5
  "description": "A modern, flexible ESLint configuration for enforcing best practices and maintaining a consistent coding style",
6
6
  "keywords": [
@@ -90,13 +90,16 @@
90
90
  "@html-eslint/eslint-plugin": "0.52.1",
91
91
  "@stylistic/eslint-plugin": "5.6.1",
92
92
  "@vitest/eslint-plugin": "1.6.5",
93
+ "astro-eslint-parser": "1.2.2",
93
94
  "defu": "6.1.4",
94
95
  "eslint": "9.39.2",
95
96
  "eslint-flat-config-utils": "2.1.4",
96
97
  "eslint-import-resolver-typescript": "4.4.4",
98
+ "eslint-plugin-astro": "1.5.0",
97
99
  "eslint-plugin-better-tailwindcss": "3.8.0",
98
100
  "eslint-plugin-cypress": "5.2.1",
99
101
  "eslint-plugin-import-x": "4.16.1",
102
+ "eslint-plugin-jsx-a11y": "6.10.2",
100
103
  "eslint-plugin-oxlint": "1.38.0",
101
104
  "eslint-plugin-perfectionist": "5.3.0",
102
105
  "eslint-plugin-playwright": "2.4.0",