8

I'm a beginner in React development.
I'm trying to solve all the errors/warnings of my project but I get different results between development environment and production environment. I didn't make any difference between them in the configuration.

Running npm run lint gives me this output: npm run lint

Running npm run build gives me this output: npm run build

Is it normal that I get different ESLint outputs?

Here's my package.json:

{
  "name": "immersion-dashboard",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@reduxjs/toolkit": "^1.5.1",
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.3.2",
    "@testing-library/user-event": "^7.1.2",
    "@types/jest": "^24.0.0",
    "@types/node": "^12.0.0",
    "@types/react": "^16.9.0",
    "@types/react-dom": "^16.9.0",
    "@types/react-redux": "^7.1.7",
    "bootstrap": "^5.1.3",
    "react": "^17.0.2",
    "react-bootstrap": "^2.0.2",
    "react-dom": "^17.0.2",
    "react-redux": "^7.2.0",
    "react-scripts": "4.0.3",
    "typescript": "~4.1.5"
  },
  "devDependencies": {
    "@typescript-eslint/eslint-plugin": "^5.4.0",
    "@typescript-eslint/parser": "^5.4.0",
    "cross-env": "^7.0.3",
    "eslint": "^7.32.0",
    "eslint-config-airbnb": "^19.0.0",
    "eslint-config-prettier": "^8.3.0",
    "eslint-import-resolver-typescript": "^2.5.0",
    "eslint-plugin-import": "^2.25.3",
    "eslint-plugin-jest": "^25.2.4",
    "eslint-plugin-jsx-a11y": "^6.5.1",
    "eslint-plugin-react": "^7.27.0",
    "eslint-plugin-react-hooks": "^4.3.0",
    "husky": "^7.0.4",
    "lint-staged": "^12.0.2",
    "prettier": "^2.4.1"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "test:staged": "cross-env CI=true react-scripts test --env=jsdom --passWithNoTests",
    "eject": "react-scripts eject",
    "typescript": "tsc --project tsconfig.json --noEmit",
    "prettier": "prettier . --write",
    "lint": "eslint . --ext .ts --ext .tsx --fix",
    "lint-staged": "lint-staged"
  },
  "lint-staged": {
    "*.{ts,tsx,json,css}": "prettier --write",
    "*.{ts,tsx}": [
      "eslint --fix",
      "npm run test:staged"
    ]
  }
}

and my .eslintrc.json:

{
  "env": {
    "browser": true,
    "es2021": true,
    "jest/globals": true
  },
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:@typescript-eslint/recommended",
    "airbnb",
    "prettier"
  ],
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true
    },
    "ecmaVersion": 12,
    "sourceType": "module"
  },
  "plugins": ["react", "@typescript-eslint", "jest"],
  "rules": {
    "import/extensions": [
      "error",
      "ignorePackages",
      {
        "ts": "never",
        "tsx": "never"
      }
    ],
    "react/jsx-filename-extension": [
      "warn",
      {
        "extensions": [".tsx"]
      }
    ],
    "no-use-before-define": "off",
    "@typescript-eslint/no-use-before-define": ["error"],
    "no-param-reassign": [
      "error",
      {
        "props": false
      }
    ],
    "no-console": "off",
    "@typescript-eslint/no-unused-vars": [
      "error",
      {
        "argsIgnorePattern": "^_",
        "varsIgnorePattern": "^_"
      }
    ]
  },
  "settings": {
    "import/resolver": {
      "typescript": {}
    }
  }
}
Tom Combet
  • 83
  • 1
  • 5

1 Answers1

17

I'm afraid you are running two instances of ESLint with different configs for each, let me explain why.

Create React App ESLint

CRA already sets up ESLint (among other things) for you:

Under the hood, we use webpack, Babel, ESLint, and other amazing projects to power your app.

In CRA, ESLint is installed and run internally when you run start or build commands, so you see the output in the console while you develop the app, or build the final bundle. You can also get the lint output in your editor. CRA already includes a default config for ESLint, including several plugins.

Your project ESLint

You have installed ESLint individually in your project, and set it up within your own .eslintrc.json. That's absolutely fine! This is the usual way to lint your projects. The way you run this ESLint is by the lint command you added to your scripts.


So when you run start or build, you are linting your project with CRA's ESLint instance and config, but when you run lint you are linting your project with your ESLint instance and config. Their configs don't match 100%, hence the different errors reported.

You can check you have two instances of ESLint installed by running npm ls eslint, you'll see something like this: npm dependencies tree displaying ESLint installed for both the project and CRA

There you can see a direct eslint dependency (the one you manually installed), and another eslint which belongs to react-scripts (the one installed as sub-dependency by CRA).

How can you solve this?

You have two options basically:

  1. Remove your ESLint and customize CRA ESLint. You could uninstall your eslint dependency from your project, remove your custom .eslintrc.json, and extend CRA ESLint config. That has to be done through the eslintConfig key in your package.json. You wouldn't need to put there everything you had in your .eslintrc.json since most of it is already covered by CRA config. The downside of this option is that 1) you can't lint your code on demand with the npm run lint since CRA doesn't allow you to do so, and 2) you are tied to the ESLint plugins version used by CRA (e.g. they are using eslint-plugin-testing-library v3, but the latest is v5 and you can't use it).
  2. Ignore ESLint from CRA (recommended). This is what I usually do. You can opt-out of the CRA built-in ESLint instance. To do this, you need to create a .env file in the root of your project, and then put DISABLE_ESLINT_PLUGIN=true inside. After that, CRA won't lint your code when running start or build, so it's up to you when to lint it with your lint command. Ideally, you'll run the lint command in your CI, and locally every time you commit files with lint-staged (this might not sound familiar to you, let me know if you need help to set up any of these), besides getting instant feedback of ESLint errors through your code editor (it should be really straightforward to set VSCode or WebStorm up to do so).

I hope this helps you, let me know if there is something else you want to discuss!

Mario Beltrán
  • 521
  • 3
  • 6
  • 1
    Thank you for the complete answer! I already set up lint-staged with husky for pre-commit so I'm ready to start developing now but again, thanks for helping me out! – Tom Combet Nov 27 '21 at 17:58
  • 1
    I’m glad it was helpful! – Mario Beltrán Nov 27 '21 at 18:14
  • For ejected CRA projects, can I just delete the `ESLintPlugin` in webpack.config.js? I assume that this will then require the project to manually run linting prior to `start`/`build` commands? – JoeTidee Mar 23 '23 at 16:55
  • @JoeTidee that's right, although it's the same as using the `DISABLE_ESLINT_PLUGIN` env var. – Mario Beltrán Mar 25 '23 at 11:20