Proper way of Installing ESlint with Prettier in a React Project - javascript

I've recently started using eslint and prettier in my projects, but I'm always not sure if I'm installing them correctly. I've read several articles online and it seems each one does it differently. I'm trying to use the Airbnb configuration. I currently do not get any errors in a basic React app, but I just want to be sure it's the correct configuration. What would be the best way to run eslint with prettier?
Here are my files:
.eslintrc.js
module.exports = {
env: {
browser: true,
es2021: true,
jest: true,
},
extends: [
'plugin:react/recommended',
'airbnb',
'plugin:prettier/recommended',
],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 12,
sourceType: 'module',
},
plugins: ['react', 'prettier'],
rules: {
'prettier/prettier': 'error',
'react/jsx-filename-extension': [1, { extensions: ['.js', '.jsx'] }],
},
};
.prettierrc.js
module.exports = {
trailingComma: "es5",
tabWidth: 2,
semi: true,
singleQuote: true,
};
in my package.json
"devDependencies": {
"eslint": "^7.25.0",
"eslint-config-airbnb": "^18.2.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-react": "^7.23.2",
"eslint-plugin-react-hooks": "^4.2.0",
"prettier": "^2.2.1"
}

There are a number of ways to set up prettier to work with eslint and it can be confusing.
To run prettier as an eslint rule, you would want to add a rule that allows eslint to run prettier. You do that with the eslint-plugin-prettier plugin.
"plugins": ["prettier"], // Defines a rule that allows eslint to run prettier.
"rules": {
"prettier/prettier": "error" // Turns on that rule.
}
You would also want to turn off eslint rules that may conflct with prettier. You do this with the eslint-config-prettier extension. Note this should come after any other extensions in order to override the rules as appropriate.
"extends": [
/*other extensions*/,
"prettier" // Turns off conflicting eslint rules.
]
As per their documentation, it sounds the recommended extension that comes with the prettier plugin actually takes care of everything for you, so you should be able to get away with just:
extends: [
// .. other extensions
'plugin:prettier/recommended',
]
https://github.com/prettier/eslint-plugin-prettier
Also, in case third-party plugins come with their own rules that may conflict with prettier, you used to have to add "prettier/that-plugin" - "prettier/react" in your case for instance - to the list of plugins in order to tell eslint to defer to prettier for these non-standard rules as relevant as well. But this no longer seems required.

I used this guide for setting up eslint with airbnb's style guide on a Yarn monorepo/workspace.

Related

How config Stylelint in vue3 app(vscode) to lint on save

I want to lint my scss files and scss scope in .vue components. my config in stylelint.config:
module.exports = {
extends: [
'stylelint-config-standard',
'stylelint-config-recess-order',
'stylelint-config-prettier',
'stylelint-config-css-modules',
],
plugins: [
'stylelint-scss',
],
rules: {},
}
and package.json:
{
"scripts": {
"lint:all:stylelint": "yarn lint:stylelint \"src/**/*.{vue,scss}\""
},
"devDependencies": {
"stylelint": "^14.1.0",
"stylelint-config-prettier": "^9.0.3",
"stylelint-config-recess-order": "^3.0.0",
"stylelint-config-standard": "^24.0.0",
"stylelint-scss": "^4.0.0",
}
}
but it doesn't work. any solution?
You need to specify a custom syntax using the customSyntax option when linting anything other than CSS with Stylelint.
However, rather than use the customSyntax option yourself, you can extend shared configs that do it for you:
module.exports = {
extends: [
'stylelint-config-standard-scss', // configure for SCSS
'stylelint-config-recommended-vue', // add overrides for .Vue files
'stylelint-config-recess-order', // use the recess order for properties
'stylelint-config-css-modules', // configure for CSS Modules methodology
'stylelint-config-prettier' // turn off any rules that conflict with Prettier
]
}
The stylelint-config-standard-scss and stylelint-config-recommended-vue shared configs will configure Stylelint for SCSS and Vue files, respectively.
You don't need to include the stylelint-scss plugin yourself either, as that also comes in stylelint-config-standard-scss.

Prettier and / or Eslint?

Most of the projects I work on I just fire up and, at most, disable a linting rule that bugs me. That is to say I don't know much about linting and linters except that eslint is everywhere.
A Vue project I'm working on now (that I did not initially build) has four linting modules and I now want to understand if all of them are necessary, if they are conflicting with each other or complimenting each other. I'm getting so many yellow warnings that don't get fixed with the --fix flag that I want to uninstall everything and install one linter to rule them all.
The project package.json has these:
{
"eslint": "^7.3.1",
"eslint-plugin-prettier": "^3.1.1",
"eslint-plugin-vue": "^6.2.2",
"lint-staged": "^10.2.7"
}
Thoughts?
My eslintrc.js
module.exports = {
root: true,
env: {
node: true,
},
extends: ["plugin:vue/essential", "#vue/prettier"],
rules: {
"no-console": process.env.NODE_ENV === "production" ? "off" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
"max-len": [0, 0, 0],
singleQuote: 0,
trailingComma: 0,
"no-unused-vars": 0,
"vue/no-unused-components": 0,
},
parserOptions: {
parser: "babel-eslint",
},
overrides: [
{
files: ["**/__tests__/*.{j,t}s?(x)", "**/tests/unit/**/*.spec.{j,t}s?(x)"],
env: {
jest: true,
},
},
],
};
Not sure if I can give a direct answer, but it's super common to combine eslint along with prettier via eslint-plugin-prettier. We use prettier purely for code formatting rules like:
single vs double quotes
max line length
semicolons or not
eslint is more commonly used to find errors in your code that otherwise wouldn't have been caught until runtime. Not every rule in eslint can be fixed via eslint --fix, but many can be. What does your .eslintrc look like?

JavaScript - babel-preset-env not transpiling arrow functions for IE11

I'm having a hard time trying to configure Babel to transpile code that IE11 can understand, specifically arrow functions. Running npx webpack --mode=development with my configuration doesn't convert the arrow functions in my code: in the eval() statement in the generated code, I can see that all the instances have gone unconverted.
Unlike in the console output quoted in this question, there's no mention in mine of "Using targets" or "Using presets". Whether that's something to do with using npx webpack rather than npm run build I don't know.
Here's the Babel part of my package.json:
{
// name, version etc. snipped
"devDependencies": {
"#babel/core": "^7.1.2",
"#babel/plugin-transform-async-to-generator": "^7.1.0",
"#babel/plugin-transform-es2015-arrow-functions": "^6.22.0",
"#babel/plugin-transform-es2015-modules-commonjs": "^6.26.2",
"#babel/preset-env": "^7.1.0",
"ajv": "^6.5.4",
"copy-webpack-plugin": "^4.5.2",
"eslint-plugin-jest": "^21.24.1",
"jest": "^23.6.0",
"jest-dom": "^2.0.4",
"webpack": "^4.20.2",
"webpack-cli": "^3.1.2"
},
"babel": {
"presets": [
[
"#babel/preset-env",
{
"targets": {
"ie": "11"
}
}
]
],
"env": {
"development": {
"plugins": [
"transform-es2015-arrow-functions",
"transform-es2015-modules-commonjs"
]
},
"test": {
"plugins": [
"transform-es2015-arrow-functions",
"transform-es2015-modules-commonjs"
]
}
}
}
}
My webpack.config.js looks like:
const CopyWebpackPlugin = require("copy-webpack-plugin");
const path = require("path");
module.exports = {
entry: "./src/thing.js",
optimization: {
minimize: false
},
output: {
filename: "thing.js",
path: path.resolve(__dirname, "dist")
},
plugins: [
new CopyWebpackPlugin([
// snipped
])
]
};
I arrived at this point from reading other questions here about Babel configurations, and the babel-preset-env docs and also the very skimpy babel-plugin-transform-es2015-arrow-functions docs. The answers to this very similar question (no accepted answer) don't mention that plugin at all, and one suggests using a polyfill, which seems to involve loading a library in your actual code rather than at this stage?
I'm very new to working with Webpack in general and don't understand what the difference is between "env" (which gets mentioned in a lot of questions) and "#babel/preset-env". Or really what the former implies in general; if you click on "env" in the docs navigation, it takes you to the page for #babel/preset-env.
What am I doing wrong?
If you are using Webpack 5, you need to specify the features that you want to transpile in the ouput.environment configuration, as explained here: https://webpack.js.org/configuration/output/#outputenvironment.
output: {
// ... other configs
environment: {
arrowFunction: false,
bigIntLiteral: false,
const: false,
destructuring: false,
dynamicImport: false,
forOf: false,
module: false,
},
}
EDIT 08/10/22
While doing some refactoring of my webpack configuration, I figured out that the arrows had stopped transpiling (or maybe I didn't test extensively when giving the original answer).
Two things weren't setup correctly: the target key was missing, and the babel configuration had a wrong value.
For supporting legacy browsers, the target key needs to be set like this:
target: ['web', 'es5']
While in the Babel configuration, I had added 'not dead' in the browserslist configuration under targets in the Babel loader, so since IE11 is now technically dead, this configuration removed the trasnpilation of the arrow function.
So if you still need to transpile the arrow function, this would be the relevant part in the Babel configuration.
module: {
rules: [
{
test: /\.m?js$/,
use: {
loader: 'babel-loader',
options: {
presets: [
[
'#babel/preset-env',
{
useBuiltIns: 'entry',
targets: {
// be sure not to add 'not dead' here
browsers: ['> 1%', 'last 2 versions', 'Firefox ESR']
},
corejs: {
version: 3,
proposals: false,
},
},
],
],
plugins: [['#babel/plugin-transform-runtime', { corejs: 3 }]],
},
},
},
],
},
In addition to loganfsmyth's answer that solved the problem, I want to note for any other beginners reading this that I made life easier for myself afterward by moving the Babel configuration out of package.json into a .babelrc.
I also discovered that the plugins I needed, such as the one I mentioned above, babel-plugin-transform-es2015-arrow-functions, have newer versions with a different naming scheme - for that example, #babel/plugin-transform-arrow-functions. The documentation pages for the old versions don't mention that.
The module part of my webpack.config.js now looks like:
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
}
]
}
My .babelrc looks like:
{
"presets": [
[
"#babel/preset-env",
{
"targets": {
"ie": "11"
},
"useBuiltIns": "entry"
}
]
],
"plugins": [
"#babel/transform-async-to-generator",
"#babel/transform-arrow-functions",
"#babel/transform-modules-commonjs"
],
"env": {
"development": {},
"test": {},
"production": {}
}
}
Update 2021: As of Webpack version 5, it outputs ES6 code by default. If you need that not to happen, you need to add a configuration setting. See Giorgio Tempesta's answer.
Babel itself is a transformation library, but on its own it will not integrate into any specific tooling. To use Babel with Webpack, you'll want to install the babel-loader package and configure it in your Webpack config using something along the lines of
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
}
}
]
}
I had the same problem. Turned out it wasn't all of my code that had arrow functions, but only one single library. I went inside my built bundle, and searched for code with arrow functions (=>). Then I searched and copied some unique-looking names around it and managed to find the source of it searching for it in all files within node_modules. In my case it turned out that the code with arrow functions came from fetch polyfill called unfetch. I'm not sure why it didn't get transpiled by the plugin, but I switched it to "whatwg-fetch" and it worked just fine - no arrow functions in bundle anymore. You could try the same technique to discover what causes it in your case.
2022 Update
If you have this problem now, it could be because browerslist treats IE 11 as dead since version 4.21.0. The solution to access IE 11 anyway is to add ie 11 either as the last entry in browserslist or at least after not dead.
Normally you don't see browserslist in the package.json, but if you use npm ls browserslist you can see which dependency uses browserslist and which version of it.
See also: https://github.com/browserslist/browserslist/issues/699

babel-preset-env not transpiling arrow functions using webpack

I'm using babel with webpack, I'm trying to make arrow functions work with Internet Explorer, but I can't get it working.
This is my package.json dev dependencies:
"devDependencies": {
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-preset-env": "^1.7.0",
"webpack": "^3.12.0",
"webpack-cli": "^3.1.0"
}
and this is my webpack.config.js:
module.exports = {
entry: ['./chat.js'],
devtool: 'source-map',
output: {
path: path.resolve(__dirname, "dist"),
filename: "chat.js"
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
}
]
}
};
I'm using the plugins with .babelrc:
{
"presets": ["env"],
"plugins": ["transform-class-properties"]
}
I don't know what I'm doing wrong or if I'm missing something but I get the following syntax error on Internet Explorer:
DF.fn = () => {
// Content
};
If you are using Babel 7, the behaviour of .babelrc has changed.
My advice is to drop .babelrc and put the config inside your webpack config.
Additionally you will need to either remove exclude: /node_modules/ from your config config, or add in some conditions for not excluding any libraries that use browser incompatible code (eg, arrow functions if you want to use IE).
Personally I removed the exclude altogether and noticed no degredation in speed or size.
For those using Webpack 5, you need to specify the features that you want to transpile in the ouput.environment configuration, as explained here: https://webpack.js.org/configuration/output/#outputenvironment.
I am using differential serving so I have set all the flags according to a modern variable (which is set to true only if building for recent browsers, which is the last 5 versions of Chrome and Firefox and the last 2 versions of Safari, Edge and iOS).
output: {
// ... other configs
environment: {
arrowFunction: modern,
bigIntLiteral: modern,
const: modern,
destructuring: modern,
dynamicImport: modern,
forOf: modern,
module: modern,
},
}
You have to specify the target browsers for your build. Based on that babel-preset-env decides which transforms need to be applied. Here's docs and example of config
{
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions", "safari >= 7"]
}
}]
]
}
And here are all possible ways to specify the browsers set.

How to use ESLint with Jest

I'm attempting to use the ESLint linter with the Jest testing framework.
Jest tests run with some globals like jest, which I'll need to tell the linter about; but the tricky thing is the directory structure, with Jest the tests are embedded with the source code in __tests__ folders, so the directory structure looks something like:
src
foo
foo.js
__tests__
fooTest.js
bar
bar.js
__tests__
barTest.js
Normally, I'd have all my tests under a single dir, and I could just add an .eslintrc file there to add the globals... but I certainly don't want to add a .eslintrc file to every single __test__ dir.
For now, I've just added the test globals to the global .eslintrc file, but since that means I could now reference jest in non-testing code, that doesn't seem like the "right" solution.
Is there a way to get eslint to apply rules based on some pattern based on the directory name, or something like that?
The docs show you are now able to add:
"env": {
"jest/globals": true
}
To your .eslintrc which will add all the jest related things to your environment, eliminating the linter errors/warnings.
You may need to include plugins: ["jest"] to your esconfig, and add the eslint-plugin-jest plugin if it still isn't working.
ESLint supports this as of version >= 4:
/*
.eslintrc.js
*/
const ERROR = 2;
const WARN = 1;
module.exports = {
extends: "eslint:recommended",
env: {
es6: true
},
overrides: [
{
files: [
"**/*.test.js"
],
env: {
jest: true // now **/*.test.js files' env has both es6 *and* jest
},
// Can't extend in overrides: https://github.com/eslint/eslint/issues/8813
// "extends": ["plugin:jest/recommended"]
plugins: ["jest"],
rules: {
"jest/no-disabled-tests": "warn",
"jest/no-focused-tests": "error",
"jest/no-identical-title": "error",
"jest/prefer-to-have-length": "warn",
"jest/valid-expect": "error"
}
}
],
};
Here is a workaround (from another answer on here, vote it up!) for the "extend in overrides" limitation of eslint config :
overrides: [
Object.assign(
{
files: [ '**/*.test.js' ],
env: { jest: true },
plugins: [ 'jest' ],
},
require('eslint-plugin-jest').configs.recommended
)
]
From https://github.com/eslint/eslint/issues/8813#issuecomment-320448724
You can also set the test env in your test file as follows:
/* eslint-env jest */
describe(() => {
/* ... */
})
To complete Zachary's answer, here is a workaround for the "extend in overrides" limitation of eslint config :
overrides: [
Object.assign(
{
files: [ '**/*.test.js' ],
env: { jest: true },
plugins: [ 'jest' ],
},
require('eslint-plugin-jest').configs.recommended
)
]
From https://github.com/eslint/eslint/issues/8813#issuecomment-320448724
As of 2021, I think the correct way or at least the one that works is to install #types/jest and eslint-plugin-jest:
npm i -D eslint-plugin-jest #types/jest
And adding the Jest plugin into .eslintrc.js with the overrides instruction mentioned by #Loren:
module.exports = {
...
plugins: ["jest"],
...
overrides: [
{
files: ["**/*.test.js"],
env: { "jest/globals": true },
plugins: ["jest"],
extends: ["plugin:jest/recommended"],
},
],
...
};
This way you get linting errors in your source files as well as in test files, but in test files you don't get linting errors for test and other Jest's functions, but you will get them in your source files as they will appear as undefined there.
I solved the problem REF
Run
# For Yarn
yarn add eslint-plugin-jest -D
# For NPM
npm i eslint-plugin-jest -D
And then add in your .eslintrc file
{
"extends": ["airbnb","plugin:jest/recommended"],
}
some of the answers assume you have eslint-plugin-jest installed, however without needing to do that, you can simply do this in your .eslintrc file, add:
"globals": {
"jest": true,
}
First install eslint-plugin-jest
Running:
yarn add eslint-plugin-jest or npm install eslint-plugin-jest
Then edit .eslintrc.json
{
"env":{
"jest": true
}
}
As of ESLint V 6 (released in late 2019), you can use extends in the glob based config as follows:
"overrides": [
{
"files": ["*.test.js"],
"env": {
"jest": true
},
"plugins": ["jest"],
"extends": ["plugin:jest/recommended"]
}
]
Add environment only for __tests__ folder
You could add a .eslintrc.yml file in your __tests__ folders, that extends you basic configuration:
extends: <relative_path to .eslintrc>
env:
jest: true
If you have only one __tests__folder, this solution is the best since it scope jest environment only where it is needed.
Dealing with many test folders
If you have more test folders (OPs case), I'd still suggest to add those files. And if you have tons of those folders can add them with a simple zsh script:
#!/usr/bin/env zsh
for folder in **/__tests__/ ;do
count=$(($(tr -cd '/' <<< $folder | wc -c)))
echo $folder : $count
cat <<EOF > $folder.eslintrc.yml
extends: $(printf '../%.0s' {1..$count}).eslintrc
env:
jest: true
EOF
done
This script will look for __tests__ folders and add a .eslintrc.yml file with to configuration shown above. This script has to be launched within the folder containing your parent .eslintrc.
Pattern based configs are scheduled for 2.0.0 release of ESLint. For now, however, you will have to create two separate tasks (as mentioned in the comments). One for tests and one for the rest of the code and run both of them, while providing different .eslintrc files.
P.S. There's a jest environment coming in the next release of ESLint, it will register all of the necessary globals.
I got it running after spending some time trying out different options. Hope this helps anyone else getting stuck.
.eslintrc.json (in root project folder):
{
"env": {
"browser": true,
"es2021": true,
"jest/globals": true
},
"extends": [
"standard",
"plugin:jest/all"
],
"parser": "#babel/eslint-parser",
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module"
},
"rules": {
"jest/no-hooks": [
"error",
{
"allow": [
"afterEach",
"beforeEach"
]
}
]
},
"plugins": [
"jest"
]
}
Empty .babelrc (in root project folder):
{}
.package.json (in root project folder):
{
"scripts": {
"test": "jest",
"lint": "npx eslint --format=table .",
"lintfix": "npx eslint --fix ."
},
"devDependencies": {
"#babel/core": "^7.15.0",
"#babel/eslint-parser": "^7.15.0",
"aws-sdk-mock": "^5.2.1",
"eslint": "^7.32.0",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.24.0",
"eslint-plugin-jest": "^24.4.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.1.0",
"jest": "^27.0.6"
}
}
VS Code settings.xml (editor configuration: enables auto fix on save + babel parser):
"eslint.alwaysShowStatus": true,
"eslint.format.enable": true,
"eslint.lintTask.enable": true,
"eslint.options": {
"parser": "#babel/eslint-parser"
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"eslint.validate": [
"javascript"
]
In your .eslintignore file add the following value:
**/__tests__/
This should ignore all instances of the __tests__ directory and their children.

Categories