Skip to content

Document how to add eslint-plugin-react #388

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
JanMisker opened this issue May 15, 2024 · 14 comments
Open

Document how to add eslint-plugin-react #388

JanMisker opened this issue May 15, 2024 · 14 comments
Labels
documentation Improvements or additions to documentation help wanted Extra attention is needed

Comments

@JanMisker
Copy link

It would be awesome if someone can share how they added eslint-plugin-react together with this plugin.

@ota-meshi ota-meshi added documentation Improvements or additions to documentation help wanted Extra attention is needed labels May 24, 2024
@cyrus01337
Copy link

cyrus01337 commented Jul 3, 2024

I've setup a minimal project with what little knowledge I have of ESLint 9 here, drawing from the configuration provided from facebook/react#28313, specifically from this comment.

While the project provides linting for React components and <script> tags in Astro files, there's a perceived bug where no linting occurs for React components used within Astro files if a <script> tag is present. If anyone could assist with, that would be most helpful as I'm unsure if this is a configuration error on my part, or a potential bug in the plugin itself.

@smnbbrv
Copy link

smnbbrv commented Oct 17, 2024

This is actually how I managed to do it so far. Still testing, but seems to be quite working.

The only thing that doesn't yet was fixable with #132 however can't get it working with v9

import pluginJs from '@eslint/js';
// import tsParser from '@typescript-eslint/parser';
import eslintPluginAstro from 'eslint-plugin-astro';
import importPlugin from 'eslint-plugin-import';
import prettierRecommended from 'eslint-plugin-prettier/recommended';
import reactPlugin from 'eslint-plugin-react';
import hooksPlugin from 'eslint-plugin-react-hooks';
import tseslint from 'typescript-eslint';

export default [
  // global ignores
  { ignores: ['dist/*', 'node_modules/*'] },

  // standard js rules
  pluginJs.configs.recommended,

  // standard typescript rules
  ...tseslint.configs.recommended,

  // override typescript rules
  {
    rules: {
      '@typescript-eslint/explicit-function-return-type': 0,
      '@typescript-eslint/explicit-module-boundary-types': 0,
      '@typescript-eslint/no-empty-function': 0,
      '@typescript-eslint/no-unused-expressions': 0,
      '@typescript-eslint/no-empty-interface': 0,
      '@typescript-eslint/no-explicit-any': 0,
      '@typescript-eslint/no-unused-vars': [
        'error',
        {
          varsIgnorePattern: '^_',
          argsIgnorePattern: '^_',
          caughtErrorsIgnorePattern: '^_',
        },
      ],
    },
  },

  // astro rules
  ...eslintPluginAstro.configs.recommended,

  // react rules
  {
    files: ['**/*.{js,mjs,cjs,ts,jsx,tsx}'],
    settings: {
      react: {
        version: 'detect',
      },
    },
    languageOptions: {
      parserOptions: {
        ecmaFeatures: {
          jsx: true,
        },
      },
    },
    ...reactPlugin.configs.flat.recommended,
  },

  // react hooks
  {
    plugins: {
      'react-hooks': hooksPlugin,
    },
    rules: {
      ...hooksPlugin.configs.recommended.rules,
    },
  },

  // override react rules
  {
    rules: {
      'react/react-in-jsx-scope': 'off',
      'react/prop-types': 'off',
    },
  },

  // prettier rules
  prettierRecommended,

  // TODO doesnt work yet
  // define the configuration for `<script>` tag.
  // script in `<script>` is assigned a virtual file name with the `.js` extension.
  // {
  //   files: ['**/*.astro/*.js', '*.astro/*.js'],
  //   parser: tsParser,
  //   rules: {
  //     'prettier/prettier': 'off',
  //   },
  // },

  // import rules
  {
    ...importPlugin.flatConfigs.recommended,
    rules: {
      'import/order': [
        'error',
        {
          groups: ['builtin', 'external', 'parent', 'sibling', 'index'],
          alphabetize: {
            order: 'asc',
          },
        },
      ],
    },
  },
];

@drwpow-figma
Copy link

drwpow-figma commented Nov 8, 2024

I’m able to run this plugin in ESLint 9.x without eslint-plugin-react. But when using reactPlugin.configs.flat.recommended, I get this error:

Oops! Something went wrong! :(

ESLint: 9.14.0

Error: The prop must be a JSXAttribute collected by the AST parser.
Occurred while linting /src/components/hero.astro:20
Rule: "react/jsx-key"
    at propName (/node_modules/.pnpm/[email protected]/node_modules/jsx-ast-utils/lib/propName.js:14:11)
    at /node_modules/.pnpm/[email protected]/node_modules/jsx-ast-utils/lib/hasProp.js:38:67
    at Array.some (<anonymous>)
    at hasProp (/node_modules/.pnpm/[email protected]/node_modules/jsx-ast-utils/lib/hasProp.js:32:16)
    at checkIteratorElement (/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint-plugin-react/lib/rules/jsx-key.js:92:14)
    at checkArrowFunctionWithJSX (/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint-plugin-react/lib/rules/jsx-key.js:164:9)
    at CallExpression[callee.type="MemberExpression"][callee.property.name="map"],       CallExpression[callee.type="OptionalMemberExpression"][callee.property.name="map"],       OptionalCallExpression[callee.type="MemberExpression"][callee.property.name="map"],       OptionalCallExpression[callee.type="OptionalMemberExpression"][callee.property.name="map"] (/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint-plugin-react/lib/rules/jsx-key.js:281:9)
    at ruleErrorHandler (/node_modules/.pnpm/[email protected]/node_modules/eslint/lib/linter/linter.js:1084:48)
    at /node_modules/.pnpm/[email protected]/node_modules/eslint/lib/linter/safe-emitter.js:45:58
    at Array.forEach (<anonymous>)

@joska-p
Copy link

joska-p commented Jan 14, 2025

I am using eslint 9.17.0 and it is working perfectly with this config. Hope this will help.

import path from "node:path";
import { fileURLToPath } from "node:url";
import { includeIgnoreFile } from "@eslint/compat";
import eslint from "@eslint/js";
import eslintConfigPrettier from "eslint-config-prettier";
import eslintPluginAstro from "eslint-plugin-astro";
import importPlugin from "eslint-plugin-import";
import jsxA11y from "eslint-plugin-jsx-a11y";
import pluginReact from "eslint-plugin-react";
import eslintPluginReactHooks from "eslint-plugin-react-hooks";
import globals from "globals";
import typescriptESLint from "typescript-eslint";

// File path setup
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const gitignorePath = path.resolve(__dirname, ".gitignore");

const reactConfig = {
  files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"],
  ...pluginReact.configs.flat.recommended,
  languageOptions: {
    ...pluginReact.configs.flat.recommended.languageOptions,
    globals: {
      ...globals.serviceworker,
      ...globals.browser,
    },
  },
};

const reactHooksConfig = {
  plugins: {
    "react-hooks": eslintPluginReactHooks,
  },
  settings: { react: { version: "detect" } },
  rules: {
    ...eslintPluginReactHooks.configs.recommended.rules,
    "react/react-in-jsx-scope": "off",
    "react/prop-types": "off",
  },
};

const jsxa11yConfig = {
  files: ["**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}"],
  ...jsxA11y.flatConfigs.recommended,
  languageOptions: {
    ...jsxA11y.flatConfigs.recommended.languageOptions,
    globals: {
      ...globals.serviceworker,
      ...globals.browser,
    },
  },
};

const importConfig = {
  extends: [importPlugin.flatConfigs.recommended, importPlugin.flatConfigs.typescript],
  settings: {
    "import/resolver": {
      typescript: {
        alwaysTryTypes: true,
        project: "./tsconfig.json",
      },
    },
  },
  rules: {
    "import/order": [
      "error",
      {
        groups: ["builtin", "external", "parent", "sibling", "index"],
        alphabetize: {
          order: "asc",
        },
      },
    ],
    "import/no-named-as-default-member": "off",
  },
};

export default typescriptESLint.config(
  includeIgnoreFile(gitignorePath),
  eslint.configs.recommended,
  eslintConfigPrettier,
  typescriptESLint.configs.strict,
  typescriptESLint.configs.stylistic,
  ...eslintPluginAstro.configs.recommended,
  reactConfig,
  reactHooksConfig,
  jsxa11yConfig,
  importConfig
);

@dvelasquez
Copy link

@joska-p Neat configuration, thanks for sharing it.

Right now I'm getting an error with error Unable to resolve path to module 'astro:actions' import/no-unresolved.

Any ideas?

@joska-p
Copy link

joska-p commented Jan 31, 2025

It could help if you give more context. What line of code give that error for example.
I would be happy to help you if i can. Just know that i don't really know what i'm doing :)
you can use the the Config Inspector that is very usefull to check your eslint config. https://eslint.org/docs/latest/use/configure/debug#use-the-config-inspector

i have change my config to take advantages of tseslint.config function. you can have a look there https://tseslint.com/users/configs.

my config today is as follow:

import path from "node:path";
import { fileURLToPath } from "node:url";
import { includeIgnoreFile } from "@eslint/compat";
import eslint from "@eslint/js";
import eslintConfigPrettier from "eslint-config-prettier";
import eslintPluginAstro from "eslint-plugin-astro";
import importPlugin from "eslint-plugin-import";
import jsxA11y from "eslint-plugin-jsx-a11y";
import pluginReact from "eslint-plugin-react";
import reactCompiler from "eslint-plugin-react-compiler";
import eslintPluginReactHooks from "eslint-plugin-react-hooks";
import globals from "globals";
import tseslint from "typescript-eslint";

// File path setup
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const gitignorePath = path.resolve(__dirname, ".gitignore");

const baseConfig = tseslint.config({
  extends: [eslint.configs.recommended, tseslint.configs.strict, tseslint.configs.stylistic],
  rules: {
    "no-console": "warn",
    "no-unused-vars": "off", // handled by typescript
  },
});

const importConfig = tseslint.config({
  extends: [importPlugin.flatConfigs.recommended, importPlugin.flatConfigs.typescript],
  settings: {
    "import/resolver": {
      typescript: {
        alwaysTryTypes: true,
        project: "./tsconfig.json",
      },
    },
  },
  rules: {
    "import/order": [
      "error",
      {
        groups: ["builtin", "external", "parent", "sibling", "index"],
        alphabetize: {
          order: "asc",
        },
      },
    ],
    "import/no-named-as-default-member": "off",
  },
});

const jsxA11yConfig = tseslint.config({
  files: ["**/*.{js,jsx,ts,tsx}"],
  extends: [jsxA11y.flatConfigs.recommended],
  languageOptions: {
    ...jsxA11y.flatConfigs.recommended.languageOptions,
  },
  rules: {
    ...jsxA11y.flatConfigs.recommended.rules,
  },
});

const reactConfig = tseslint.config({
  files: ["**/*.{js,jsx,ts,tsx}"],
  extends: [pluginReact.configs.flat.recommended],
  languageOptions: {
    ...pluginReact.configs.flat.recommended.languageOptions,
    globals: {
      ...globals.serviceworker,
      ...globals.browser,
    },
  },
  plugins: {
    "react-hooks": eslintPluginReactHooks,
    "react-compiler": reactCompiler,
  },
  settings: { react: { version: "detect" } },
  rules: {
    ...eslintPluginReactHooks.configs.recommended.rules,
    "react/react-in-jsx-scope": "off",
    "react-compiler/react-compiler": "error",
  },
});

export default tseslint.config(
  includeIgnoreFile(gitignorePath),
  baseConfig,
  jsxA11yConfig,
  reactConfig,
  eslintPluginAstro.configs.recommended,
  importConfig,
  eslintConfigPrettier
);

@dvelasquez
Copy link

dvelasquez commented Feb 2, 2025

Hey @joska-p thanks again for your config, I really appreciate it and I like the approach.

Here is a repo with the minimal reproduction of this: https://github.com/dvelasquez/astro-eslint
The problem is specifically here: https://github.com/dvelasquez/astro-eslint/blob/c65f63d6aa2cd877be386b8bcf00acd8d45b8dd5/src/actions/index.ts#L4
For some reason is like the plugin eslint-plugin-import is not able to find the type declaration for these Astro runtime API modules.

@joska-p
Copy link

joska-p commented Feb 2, 2025

Hi. I think you just need to generate the .astro directory. You can do so with npm run build
But it may be that the import plugin resolver is not able to resolve the imports that are specific to Astro.
I don't see a workaround for that. Either disable the error manually in the file or remove the import plugin.
If you find a way, let me know.

@dvelasquez
Copy link

Yes, the .astro directory is generated when you run npm run dev but even then is not picked up by eslint.

Does the same happen to you? If that is the case I will disable that rule.

@joska-p
Copy link

joska-p commented Feb 2, 2025

Yes, I got the same error. But I think we can disable this rule in the config. TypeScript should handle that kind of error. I only use this plugin to sort the imports, not to check if they resolve. I really think TypeScript would show an error if it couldn't resolve the import.
Just add the rule "import/no-unresolved": "off", in your ESLint config.

@danis039
Copy link

How can I prevent eslint-plugin-react rules from being applied to Astro files? I have the following ESLint 9 flat config:

import pluginJs from '@eslint/js'
import tseslint from 'typescript-eslint'
import pluginReact from 'eslint-plugin-react'
import pluginHooks from 'eslint-plugin-react-hooks'
import eslintPluginAstro from 'eslint-plugin-astro'
import tailwind from 'eslint-plugin-tailwindcss'
import eslintConfigPrettier from 'eslint-config-prettier'

/** @type {import('eslint').Linter.Config[]} */
export default [
  pluginJs.configs.recommended,
  ...tseslint.configs.recommended,
  pluginReact.configs.flat.recommended,
  pluginReact.configs.flat['jsx-runtime'],
  ...eslintPluginAstro.configs.recommended,
  ...tailwind.configs['flat/recommended'],
  {
    settings: {
      react: {
        version: 'detect',
      },
    },
    plugins: {
      'react-hooks': pluginHooks,
    },
    rules: {
      ...pluginHooks.configs.recommended.rules,
    },
  },
  // How to avoid using the object below?
  // Shouldn't React rules be disabled for Astro files?
  {
    files: ['**/*.astro'],
    rules: {
      'react/no-unknown-property': 'off',
      'react/jsx-key': 'off',
    },
  },
  eslintConfigPrettier,
]

When not using eslint-plugin-astro the problem stops occurring as no React rules are applied to my Astro files.

@joska-p
Copy link

joska-p commented Feb 13, 2025

Hello. The issue is due to the line files: ['**/*.astro'],.
Please take a look at my configuration above.
I have separate configurations for different plugins.
I am using the tseslint.config() function to handle those.

@danis039
Copy link

danis039 commented Feb 14, 2025

Thanks so much, @joska-p! I've managed to fix my issue with your suggestions.

I'm including my configuration file here as well, in case it helps others:

import globals from 'globals'
import pluginJs from '@eslint/js'
import tseslint from 'typescript-eslint'
import pluginReact from 'eslint-plugin-react'
import pluginHooks from 'eslint-plugin-react-hooks'
import eslintPluginAstro from 'eslint-plugin-astro'
import tailwind from 'eslint-plugin-tailwindcss'
import jsxA11y from 'eslint-plugin-jsx-a11y'
import eslintConfigPrettier from 'eslint-config-prettier'

const baseConfig = tseslint.config({
  extends: [
    pluginJs.configs.recommended,
    tseslint.configs.recommended,
    tseslint.configs.stylistic,
  ],
  rules: {
    'no-console': ['warn', { allow: ['warn', 'error'] }],
  },
})

const reactConfig = tseslint.config({
  files: ['**/*.{js,jsx,ts,tsx}'],
  extends: [
    pluginReact.configs.flat.recommended,
    pluginReact.configs.flat['jsx-runtime'],
  ],
  languageOptions: {
    ...pluginReact.configs.flat.recommended.languageOptions,
    globals: {
      ...globals.browser,
    },
  },
  plugins: {
    'react-hooks': pluginHooks,
  },
  settings: { react: { version: 'detect' } },
  rules: {
    ...pluginHooks.configs.recommended.rules,
  },
})

export default tseslint.config(
  baseConfig,
  reactConfig,
  eslintPluginAstro.configs.recommended,
  jsxA11y.flatConfigs.recommended,
  tailwind.configs['flat/recommended'],
  eslintConfigPrettier,
)

@joska-p
Copy link

joska-p commented Feb 14, 2025

happy to help.
In case you didn't know, there is a helpful tool to check your configuration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

8 participants