Skip to content

Instantly share code, notes, and snippets.

@gustavoalbuquerquebr
Last active June 11, 2020 15:14
Show Gist options
  • Save gustavoalbuquerquebr/e7ebb8902712a1fd8eb0f7c467fdbec1 to your computer and use it in GitHub Desktop.
Save gustavoalbuquerquebr/e7ebb8902712a1fd8eb0f7c467fdbec1 to your computer and use it in GitHub Desktop.
#eslint #prettier

EsLint

What is

Eslint checks syntax, finds problems and enforces code style in JavaScript files.

Installation

  • eslint is a NPM module
  • sudo npm install -D eslint
    • sudo is only needed while installing globally

Install locally or globally

  • install packages as locally (instead of globally) is a better option to make sure your npm settings (package.json) is a sharable configuration and when initiated (npm init) it'll download are the necessary packages

Defaults

  • "extends": "eslint:recommended" = default when you choose to 'Answer questions about your style'
  • You can view (part of it, I don't know the rest; doesn't contain 'no-console' rule, for example) here

Basic usage

  • eslint --init = creates '.eslintrc.js' with the config
  • eslint path = to check js files in the path directory and in its subdirectories
  • eslint --fix path = check and fix some of the warnings and errors; e.g. indentation, quotes and semicolon

Inheritance

  • ESLint will automatically look for config files ('.eslintrc') in the directory of the file to be linted,
  • and in successive parent directories all the way up to the root directory of the filesystem
  • this behavior is useful when you want different configurations for different parts of a project or when you want others to be able to use ESLint directly without needing to remember to pass in the configuration file
  • add "root": true to the '.eslintrc' file to disable this behavior

Display rule

  • elsint --print-config script.js
  • output all of the rules that will be used for a given file
  • no linting is performed and only config-related options are valid.

Edit rules

  • at '.eslintrc.js', edit the 'rules' object
  • "no-console": [ "warn" } = simple rule example
  • "rule-name": [ "off, 0 | warn, 1 | error, 2" ] = basic format
    • off or 0 = turn the rule off
    • warn or 1 = turn the rule on as a warning, exit code will still be success
    • error or 2 = turn the rule on as an error, exit code will be 1 (error)
    • "quotes": ["error", "double"], "no-console": { "allow": ["warn", "error"] } = rules with extra options

Ignore line, block and file/directory

  • ignore one line: console.log("!!!"); // eslint-disable-line
  • ignore multiple lines:
/* eslint-disable */
console.log("!!!");
/* eslint-enable */
  • ignore file/directory:
    • "ignorePatterns": [ ".eslintrc.js", "vendor/*" ], = add to '.eslintrc.js'
    • or create a file '.eslintignore' and add paths to ignore
.eslintrc.js
vendor/*

Watch

  • npm install -D eslint-watch
  • npx esw -w path

Lint automatically before commit

  • npm install -D pre-commit
    • NOTE: you must've already git init, otherwise this install won't find the '.git' directory to add the hook
  • will try to run your npm test command in the root of the git repository by default unless it's the default value that is set by 'package.json'
  • "precommit": ["foo", "bar", "test"], = run multiple scripts (npm run foo, ...)

Create custom rules

How eslint parses the code

  • to be able to read the code, eslint parse it using espree/acorn which creates an abstract syntax tree (ast)
  • https://astexplorer.net/ to view what your code's ast looks like
  • when creating a custom rule, you must know what type of structure the rule must apply (VariableDeclarator, FunctionDeclaration, CallExpression...) and how to filter it by one of its properties
// error if a 'x' variable is declared
return {
  VariableDeclarator: function (node) {
    if (node.id.name === "x") {
      context.report(node, "Not allowed!");
    }
  },
};

// error if a call to the 'test' function is made
return {
  CallExpression: function (node) {
    if (node.callee.name === "test") {
      context.report(node, "Not allowed!");
    }
  },
};

// error if a 'test' function is declared
return {
  FunctionDeclaration: function (node) {
    if (node.id.name === "test") {
      context.report(node, "Not allowed!");
    }
  },
};

2 methods to create custom rules

  • plugin: = it's good for reusability
  • cli option = you must include the 'rulesdir' option everytime you call eslint

METHOD: 1 - make a plugin

  • in the root of the main project, create the plugin directory, e.g. 'custom-rules'
  • in this directory, create 'index.js' and add rules like this:
module.exports.rules = {
  "var-length": (context) => ({
    VariableDeclarator: (node) => {
      if (node.id.name.length < 2) {
        context.report(
          node,
          "Variable names should be longer than 1 character"
        );
      }
    },
  }),
  "no-test-function": (context) => ({
    FunctionDeclaration: function (node) {
      if (node.id.name === "test") {
        context.report(node, "Not allowed!");
      }
    },
  }),
};
  • npm init, NOTE: the name of this package must start with 'eslint-plugin', e.g. 'eslint-plugin-custom-rules'
  • sudo npm link
  • back in the root, npm link eslint-plugin-custom-rules
  • in the 'eslintrc.js', add the new plugin "plugins": [ "custom-rules" ],; NOTE: the leading 'eslint-plugin-' isn't required and can be omitted
  • also, add to the rules array "my-rules/var-length": [ "error" ], "my-rules/no-test": [ "error" ],
export recommended rules
  • instead of add the custom rules to the rules array one by one
  • in the plugin's index export:
module.exports.configs = {
  recommended: {
    rules: {
      "my-rules/var-length": "error",
      "my-rules/no-test-function": "error",
    },
  },
};
  • in '.eslintrcjs', add "extends": ["plugin:my-rules/recommended"],

METHOD: 2 - '--rulesdir' in the cli

  • create a directory (with any name) to contain your custom rules, e.g 'eslint-custom-rules'
  • the code below (saved at 'eslint-custom-rules/no-test-function.js') creates a rule that will throw error if the function 'test' is called
module.exports = {
  create: function (context) {
    return {
      CallExpression: function (node) {
        if (node.callee.name === "test") {
          context.report(node, "Call 'test' function is not allowed!");
        }
      },
    };
  },
};
  • add it to '.eslintrc.js' in the rules array "no-test-function": [ "error" ]
  • eslint --rulesdir eslint-custom-rules/ .

passing options

  • "custom-rule": ["error", "opt1", "opt2"],, at '.eslintrc.js' = any other parameter passed to the rule besides the first one, will be considered a option
  • you can access they in the 'context.options' in your custom rule file

enable fix

  • will rename any variable named "colour" to "color"
module.exports = {
  create: function (context) {
    return {
      VariableDeclarator: function (node) {
        if (node.id.name === "colour") {
          context.report({
            node: node,
            message: "Use american english spelling!",
            fix: function (fixer) {
              return fixer.replaceText(node.id, "color");
            },
          });
        }
      },
    };
  },
};

Add a style guide

  • you can choose to use a popular config during the init
  • or, after init, do
    • npx install-peerdeps --dev eslint-config-airbnb = install airbnb config and all of its peer dependencies
    • "extends": "airbnb" = in '.eslintrc.js'

Prettier

What is

Opinionated (few defaults can be changed) code formatter with support for JS, JSX, TypeScript, JSON, CSS, SCSS, HTML, Markdown...

Prettier vs linters

  • Linters have two categories of rules:
    • formatting rules = e.g. max-len, tabs-or-spaces, comma-style...
    • code-quality rules = e.g. no-unused-vars, no-implicity-globals...
  • Prettier will take care of formatting rules - the first category, but not the second
    • and will format code much better than a linter

Install

  • npm i prettier --save-dev --save-exact or npm i -DE prettier
    • NOTE: "We recommend pinning an exact version of prettier in your package.json as we introduce stylistic changes in patch releases".

Basic Usage

  • npx prettier --write <filename...>

Watch

  • install the package onchange npm i onchange --save-dev and
  • execute it: npx onchange <file path> -- npx prettier --write {{changed}}

Add to npm script

  • run once: prettier --write "."
  • watch: onchange "." -- prettier --write {{changed}}

Options

// basic JSON '.prettierrc.json' file
{
  "singleQuote": true
}

Ignore

  • ignore files
    • add them in the '.prettierignore' file
    • cli --ignore-path option
  • to ignore blocks of code:
// TOPIC: next line will exclude the next node in the abstract syntax tree from formatting
// prettier-ignore
let z = {
  a   : 1,
  bbbb: 2,
  cc  : 3,
};
<div>
  {/* prettier-ignore */}
  <span     ugly  format=''   />
</div>
/* prettier-ignore  */
h1 { color: red; }

EsLint & Prettier Integration

  • integrate a liting tool to prettier is done in two steps
    • disable any existing formatiing rule in you linter that may coflict with Prettier
    • add an extension to your linting tool to format your file with Prettier - so that you only need a single command for format a file, or run your linter then Prettier as separate steps

Steps

  • npm i -DE prettier
  • npm i -D eslint
  • npx eslint --init, choose the 'airbnb' style guide
  • npm i -D eslint-config-prettier eslint-plugin-prettier
    • first is a config that disables rules that conflict with prettier
    • second runs prettier as an eslint rule
  • "extends": ["airbnb", "plugin:prettier/recommended"] in '.eslintrc.js'
    • NOTE: make sure to put 'prettier' last in the extends array, so it gets the chance to override other configs
  • done! now run with:
    • npx eslint "." = view errors that doesn't pass either eslint or prettier rules
      • --fix option = fix all (fixable) of them

enforce single-quotes

  • this is necessary, because the 'eslint-config-prettier' will disable any airbnb rules (or any other ruleset being applied in eslint) that conflict with prettier
  • to make prettier use single-quotes (just like airbnb), add "singleQuote": true to '.prettierrc.json'
    • NOTE: "singleQuote": true this isn't a hard rule in prettier, prettier will use the type of quotes which requires less escapes and, with that option set to true, will prefer single-quotes in case of a tie.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment