I hope you guys are aware of Eslint and its advantages, so let's start setting up Eslint, Prettier, with Husky on WordPress or any other project.
A one-liner about each of them.
ESLint: - ESLint is a linter for the JavaScript programming language, written in Node.js. That helps you find and fix problems in your JavaScript code.
Prettier: - Prettier is an opinionated code formatter that formats the code with the help of rules we set.
Husky: - Husky is an NPM package that lets you run a set of commands or script before any git action. For eg pre-push
, pre-commit
, pre-rebase
.
1. Adding NPM to the project.
For running ESLint, we need to have npm installed in our project. If you already have a package.json
file, You can skip this step and go directly to adding ESLint to the project; if not, then follow these steps to create a package.json file.
Navigate to the root folder of your project. For WordPress, it can be your theme folder or your assets folder within the theme.
Run this command.
npm init
Please add details about your projects, and after that, a package.json
and package-lock.json
file will be created in your root folder.
2. Setting up ESLint
Run this command to add ESlint to your project.
npm install eslint --save-dev
Reference: https://eslint.org/docs/user-guide/getting-started
The above command will install ESLint within your project. Now we will be setting up a few ESLint rules and config for our project; we will use eslint-config-airbnb rules
.
3. Configure ESLint with Airbnb package
Run this command.
npx eslint --init
Add the details regarding your project. And for the following questions, please select the below answers.
“What format do you want your config file to be in?” - please select JSON from the chosen options.
“Which style guide do you want to follow?“ - please select Airbnb from the chosen options.
Run this command.
npm i eslint-config-airbnb
Reference: https://www.npmjs.com/package/eslint-config-airbnb
4. Creating ESLint config file
Create a file named .eslintrc.json
to add all your ESLint rules.
ESLint config file that disables all rules individually.
This is an Eslint config (eslintrc.json
) file that has all the rules turned off so that you can change your code on a rule by rule basis rather than changing all the code to fit all the rules?
Copied the rules from here and turned them off - https://eslint.org/docs/rules/
filename - .eslintrc.json
{
"env": {
"browser": true
},
"extends": ["airbnb-base", "plugin:prettier/recommended"],
"plugins": ["prettier"],
"rules": {
// disable es6 rules -- start
"arrow-body-style": "off",
"arrow-parens": "off",
"arrow-spacing": "off",
"constructor-super": "off",
"generator-star-spacing": "off",
"no-class-assign": "off",
"no-confusing-arrow": "off",
"no-const-assign": "off",
"no-dupe-class-members": "off",
"no-duplicate-imports": "off",
"no-new-symbol": "off",
"no-restricted-imports": "off",
"no-this-before-super": "off",
"no-useless-computed-key": "off",
"no-useless-constructor": "off",
"no-useless-rename": "off",
"no-var": "off",
"object-shorthand": "off",
"prefer-arrow-callback": "off",
"prefer-const": "off",
"prefer-destructuring": "off",
"prefer-numeric-literals": "off",
"prefer-reflect": "off",
"prefer-rest-params": "off",
"prefer-spread": "off",
"prefer-template": "off",
"require-yield": "off",
"rest-spread-spacing": "off",
"sort-imports": "off",
"symbol-description": "off",
"template-curly-spacing": "off",
"yield-star-spacing": "off",
// disable es6 rules -- end
// Possible Errors -- start
"for-direction": "off",
"getter-return": "off",
"no-async-promise-executor": "off",
"no-await-in-loop": "off",
"no-compare-neg-zero": "off",
"no-cond-assign": "off",
"no-console": "off",
"no-constant-condition": "off",
"no-control-regex": "off",
"no-debugger": "off",
"no-dupe-args": "off",
"no-dupe-else-if": "off",
"no-dupe-keys": "off",
"no-duplicate-case": "off",
"no-empty": "off",
"no-empty-character-class": "off",
"no-ex-assign": "off",
"no-extra-boolean-cast": "off",
"no-extra-parens": "off",
"no-extra-semi": "off",
"no-func-assign": "off",
"no-import-assign": "off",
"no-inner-declarations": "off",
"no-invalid-regexp": "off",
"no-irregular-whitespace": "off",
"no-loss-of-precision": "off",
"no-misleading-character-class": "off",
"no-obj-calls": "off",
"no-promise-executor-return": "off",
"no-prototype-builtins": "off",
"no-regex-spaces": "off",
"no-setter-return": "off",
"no-sparse-arrays": "off",
"no-template-curly-in-string": "off",
"no-unexpected-multiline": "off",
"no-unreachable": "off",
"no-unreachable-loop": "off",
"no-unsafe-finally": "off",
"no-unsafe-negation": "off",
"no-useless-backreference": "off",
"require-atomic-updates": "off",
"use-isnan": "off",
"valid-typeof": "off",
// Possible Errors -- end
// Best Practices -- start
"accessor-pairs": "off",
"array-callback-return": "off",
"block-scoped-var": "off",
"class-methods-use-this": "off",
"complexity": "off",
"consistent-return": "off",
"curly": "off",
"default-case": "off",
"default-case-last": "off",
"default-param-last": "off",
"dot-location": "off",
"dot-notation": "off",
"eqeqeq": "off",
"grouped-accessor-pairs": "off",
"guard-for-in": "off",
"max-classes-per-file": "off",
"no-alert": "off",
"no-caller": "off",
"no-case-declarations": "off",
"no-constructor-return": "off",
"no-div-regex": "off",
"no-else-return": "off",
"no-empty-function": "off",
"no-empty-pattern": "off",
"no-eq-null": "off",
"no-eval": "off",
"no-extend-native": "off",
"no-extra-bind": "off",
"no-extra-label": "off",
"no-fallthrough": "off",
"no-floating-decimal": "off",
"no-global-assign": "off",
"no-implicit-coercion": "off",
"no-implicit-globals": "off",
"no-implied-eval": "off",
"no-invalid-this": "off",
"no-iterator": "off",
"no-labels": "off",
"no-lone-blocks": "off",
"no-loop-func": "off",
"no-magic-numbers": "off",
"no-multi-spaces": "off",
"no-multi-str": "off",
"no-new": "off",
"no-new-func": "off",
"no-new-wrappers": "off",
"no-octal": "off",
"no-octal-escape": "off",
"no-param-reassign": "off",
"no-proto": "off",
"no-redeclare": "off",
"no-restricted-properties": "off",
"no-return-assign": "off",
"no-return-await": "off",
"no-script-url": "off",
"no-self-assign": "off",
"no-self-compare": "off",
"no-sequences": "off",
"no-throw-literal": "off",
"no-unmodified-loop-condition": "off",
"no-unused-expressions": "off",
"no-unused-labels": "off",
"no-useless-call": "off",
"no-useless-catch": "off",
"no-useless-concat": "off",
"no-useless-escape": "off",
"no-useless-return": "off",
"no-void": "off",
"no-warning-comments": "off",
"no-with": "off",
"prefer-named-capture-group": "off",
"prefer-promise-reject-errors": "off",
"prefer-regex-literals": "off",
"radix": "off",
"require-await": "off",
"require-unicode-regexp": "off",
"vars-on-top": "off",
"wrap-iife": "off",
"yoda": "off",
// Best Practices -- end
// Strict Mode -- start
"strict": "off",
// Strict Mode -- end
// Variables -- start
"init-declarations": "off",
"no-delete-var": "off",
"no-label-var": "off",
"no-restricted-globals": "off",
"no-shadow": "off",
"no-shadow-restricted-names": "off",
"no-undef": "off",
"no-undef-init": "off",
"no-undefined": "off",
"no-unused-vars": "off",
"no-use-before-define": "off",
// Variables -- end
// Stylistic Issues -- start
"array-bracket-newline": "off",
"array-bracket-spacing": "off",
"array-element-newline": "off",
"block-spacing": "off",
"brace-style": "off",
"camelcase": "off",
"capitalized-comments": "off",
"comma-dangle": "off",
"comma-spacing": "off",
"comma-style": "off",
"computed-property-spacing": "off",
"consistent-this": "off",
"eol-last": "off",
"func-call-spacing": "off",
"func-name-matching": "off",
"func-names": "off",
"func-style": "off",
"function-call-argument-newline": "off",
"function-paren-newline": "off",
"id-denylist": "off",
"id-length": "off",
"id-match": "off",
"implicit-arrow-linebreak": "off",
"indent": "off",
"jsx-quotes": "off",
"key-spacing": "off",
"keyword-spacing": "off",
"line-comment-position": "off",
"linebreak-style": "off",
"lines-around-comment": "off",
"lines-between-class-members": "off",
"max-depth": "off",
"max-len": "off",
"max-lines": "off",
"max-lines-per-function": "off",
"max-nested-callbacks": "off",
"max-params": "off",
"max-statements": "off",
"max-statements-per-line": "off",
"multiline-comment-style": "off",
"multiline-ternary": "off",
"new-cap": "off",
"new-parens": "off",
"newline-per-chained-call": "off",
"no-array-constructor": "off",
"no-bitwise": "off",
"no-continue": "off",
"no-inline-comments": "off",
"no-lonely-if": "off",
"no-mixed-operators": "off",
"no-mixed-spaces-and-tabs": "off",
"no-multi-assign": "off",
"no-multiple-empty-lines": "off",
"no-negated-condition": "off",
"no-nested-ternary": "off",
"no-new-object": "off",
"no-plusplus": "off",
"no-restricted-syntax": "off",
"no-tabs": "off",
"no-ternary": "off",
"no-trailing-spaces": "off",
"no-underscore-dangle": "off",
"no-unneeded-ternary": "off",
"no-whitespace-before-property": "off",
"nonblock-statement-body-position": "off",
"object-curly-newline": "off",
"object-curly-spacing": "off",
"object-property-newline": "off",
"one-var": "off",
"one-var-declaration-per-line": "off",
"operator-assignment": "off",
"operator-linebreak": "off",
"padded-blocks": "off",
"padding-line-between-statements": "off",
"prefer-exponentiation-operator": "off",
"prefer-object-spread": "off",
"quote-props": "off",
"quotes": "off",
"semi": "off",
"semi-spacing": "off",
"semi-style": "off",
"sort-keys": "off",
"sort-vars": "off",
"space-before-blocks": "off",
"space-before-function-paren": "off",
"space-in-parens": "off",
"space-infix-ops": "off",
"space-unary-ops": "off",
"spaced-comment": "off",
"switch-colon-spacing": "off",
"template-tag-spacing": "off",
"unicode-bom": "off",
"wrap-regex": "off"
// Stylistic Issues -- end
}
}
5. Creating ESLint ignore file
Reference - https://eslint.org/docs/user-guide/configuring#ignorepatterns-in-config-files
Create a file named .eslintignore
This file is used to add rules to ignore specific files or folders.
For now, I have just ignored the node_modules folder
; you can customize and add new rules based on your requirement.
filename - .eslintignore
node_modules/**
6. Adding Prettier along with ESLint
Adding prettier with ESLint - https://prettier.io/docs/en/install.html
Run these commands to install prettier
npm install --save-dev --save-exact prettier
npm install --save-dev eslint-config-prettier eslint-plugin-prettier
The former turns off all ESLint rules that could conflict with Prettier, the latter integrates the Prettier rules into ESLint rules.
filename - .prettierrc
{
"semi": true,
"trailingComma": "es5",
"printWidth": 120,
"singleQuote": true,
"arrowParens": "always",
"proseWrap": "preserve"
}
7. Adding Prettier ignore file
.prettierignore
file contains a list of files that needs to be ignored from the Prettier check.
filename - .prettierignore
node_modules/**
# Ignore all JS files:
**/*.js
For now, I have ignored all the js files and the node_modules
folder.
Hurrah! We have now set up the ESLint + Prettier for our project.
Command to check ESLint errors in files.
For a single js file.
npx eslint js/amplitude.js --format table
For multiple js files at once
npx eslint **/*.js --format table
8. Configure ESlint and Prettier plugins in VSCode.
We have ESLint and Prettier config files in our project, so the VSCode editor plugins will only use our local config.
8. Pre-Commit hooks check using Husky
Setup Husky to use it with a pre-commit hook and check for any linting errors.
Steps to install Husky
npm install husky --save-dev
npm install --save-dev lint-staged
Add Husky pre-commit config in your package.json
file
Add husky config in your package.json
file
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"**/*.js": [
"eslint --fix",
"git add"
]
},
Whenever you commit any change and contain a js file, Husky will check if those files meet all the linting requirements or not; if not, it won't let you commit unless you fix those issues.
This is how a successful Husky check looks like.
9. Final package.json File
This is how my package.json
file looks like after performing all the above steps.
Filename - package.json
{
"name": "twentyseventeen",
"version": "1.0.0",
"description": "=== Twenty Seventeen === Contributors: wordpressdotorg Tested up to: 5.5 Version: 2.4 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Tags: one-column, two-columns, right-sidebar, flexible-header, accessibility-ready, custom-colors, custom-header, custom-menu, custom-logo, editor-style, featured-images, footer-widgets, post-formats, rtl-language-support, sticky-post, theme-options, threaded-comments, translation-ready",
"main": "index.js",
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"**/*.js": [
"eslint --fix",
"git add"
]
},
"dependencies": {
"eslint-config-airbnb": "^18.2.0"
},
"devDependencies": {
"eslint": "^7.11.0",
"eslint-config-airbnb-base": "^14.2.0",
"eslint-config-prettier": "^6.12.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-prettier": "^3.1.4",
"husky": "^4.3.0",
"lint-staged": "^10.4.0",
"prettier": "2.1.2"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
Top comments (2)
The steps described under 8. and 9. didn't work for me.
In the end I used the main guides of lint-stage and husky:
npmjs.com/package/lint-staged/v/7.3.0
github.com/typicode/husky
Husky seemed to be redesigned at some point. This is explained in a blog post:
blog.typicode.com/husky-git-hooks-...
Can you clarify this? If you extend from air bnb then disable everything what is the point of extending from air bnb?