Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
ESLint and Prettier setup

This setup achieves:

  • Imports will be resolved from /src folder.
  • Code formatter and linter that works with the import module resolver.
  • Setup react-hooks linting

NOT using TypeScript

1. Install dependencies

If you are using ReactJS

yarn add prettier eslint @babel/eslint-parser eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-module-resolver eslint-plugin-import eslint-import-resolver-babel-module babel-plugin-module-resolver husky lint-staged eslint-plugin-jest --dev

If you are not using ReactJS

yarn add prettier eslint @babel/eslint-parser eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-module-resolver eslint-plugin-import eslint-import-resolver-babel-module babel-plugin-module-resolver husky lint-staged eslint-plugin-jest --dev

2. Code formatting

On your .prettierrc.js file, copy-paste the following codes:

module.exports = {
  tabWidth: 2,
  printWidth: 69,
  useTabs: false,
  semi: true,
  singleQuote: true,
  trailingComma: "none",
  bracketSpacing: true,
  jsxBracketSameLine: false,
  jsxSingleQuote: false,
  quoteProps: "as-needed",
  arrowParens: "avoid",
  insertPragma: false,
  parser: "babel",
};

3. Code Linting

On your .eslintrc.js, copy-paste one of the following codes:

If you are using React

module.exports = {
  settings: {
    react: {
      version: "detect",
    },
    "import/ignore": ["react-native"],
    "import/resolver": {
      "babel-module": {
        extensions: [".android.js", ".ios.js", ".js"],
        alias,
      },
    },
  },
  env: {
    browser: true,
    node: true,
    es6: true,
    "jest/globals": true,
  },
  root: true,
  plugins: ["jest", "react", "module-resolver"],
  extends: [
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:jest/recommended",
    "plugin:react-hooks/recommended",
    "plugin:import/errors",
    "plugin:import/warnings",
  ],
  globals: {
    Atomics: "readonly",
  },
  parser: "@babel/eslint-parser",
  parserOptions: {
    sourceType: "module",
  },
  rules: {
    "brace-style": ["error", "1tbs", { allowSingleLine: false }],
    "no-multiple-empty-lines": ["error", { max: 1 }],
    "no-case-declarations": 0,
    "no-return-await": "error",
    "import/no-unresolved": 0,
    "import/order": [
      "error",
      {
        "newlines-between": "never",
        alphabetize: {
          order: "asc",
          caseInsensitive: true,
        },
      },
    ],
    "react/jsx-closing-tag-location": "error",
    "module-resolver/use-alias": 2,
    "react-hooks/exhaustive-deps": "error",
    "react-hooks/rules-of-hooks": "error",
    semi: ["error", "always"],
    quotes: ["error", "single", { avoidEscape: true }],
    curly: ["error", "multi-or-nest", "consistent"],
    "linebreak-style": ["error", "unix"],
    "no-duplicate-imports": [
      "error",
      {
        includeExports: true,
      },
    ],
    "react/prop-types": 0,
    "react/display-name": 0,
    "rest-spread-spacing": ["error", "never"],
    "no-inline-comments": [
      "error",
      {
        // https://github.com/prettier/prettier/issues/7884#issuecomment-760175877
        ignorePattern: "_prettier-hack",
      },
    ],
    "jsx-quotes": ["error", "prefer-double"],
    "prefer-spread": ["error"],
    "prefer-const": "error",
    "no-useless-call": ["error"],
    "no-trailing-spaces": ["error"],
    "space-before-blocks": ["error", "always"],
    "no-unused-vars": [
      "error",
      {
        varsIgnorePattern: unusedVarsIgnorePattern,
        argsIgnorePattern: unusedVarsIgnorePattern,
        caughtErrorsIgnorePattern: unusedVarsIgnorePattern,
      },
    ],
    "no-floating-decimal": ["error"],
    "comma-dangle": ["error", "never"],
    "array-bracket-spacing": ["error", "never"],
    "object-curly-spacing": ["error", "always"],
    "switch-colon-spacing": [
      "error",
      {
        after: true,
        before: false,
      },
    ],
    "space-unary-ops": [
      "error",
      {
        words: true,
        nonwords: false,
      },
    ],
    "space-before-function-paren": [
      "error",
      {
        anonymous: "always",
        named: "always",
        asyncArrow: "always",
      },
    ],
    "keyword-spacing": [
      "error",
      {
        before: true,
        after: true,
      },
    ],
    "space-in-parens": ["error", "never"],
    "block-spacing": "error",
    "key-spacing": [
      "error",
      {
        singleLine: {
          beforeColon: false,
          afterColon: true,
          mode: "strict",
        },
        multiLine: {
          beforeColon: false,
          afterColon: true,
          mode: "strict",
        },
      },
    ],
    "generator-star-spacing": [
      "error",
      {
        before: false,
        after: true,
      },
    ],
    eqeqeq: "error",
    "no-empty": "error",
    "no-debugger": process.env.NODE_ENV === "production" ? "error" : "warn",
    "no-console": process.env.NODE_ENV === "production" ? "error" : "warn",
    "no-alert": process.env.NODE_ENV === "production" ? "error" : "warn",
  },
};

If you are NOT using ReactJS

module.exports = {
  settings: {
    "import/resolver": {
      "babel-module": {
        extensions: [".js"],
        alias,
      },
    },
  },
  env: {
    browser: true,
    node: true,
    es6: true,
    "jest/globals": true,
  },
  root: true,
  plugins: ["jest", "module-resolver"],
  extends: [
    "eslint:recommended",
    "plugin:jest/recommended",
    "plugin:import/errors",
    "plugin:import/warnings",
  ],
  globals: {
    Atomics: "readonly",
  },
  parser: "@babel/eslint-parser",
  parserOptions: {
    sourceType: "module",
  },
  rules: {
    "brace-style": ["error", "1tbs", { allowSingleLine: false }],
    "no-multiple-empty-lines": ["error", { max: 1 }],
    "no-case-declarations": 0,
    "no-return-await": "error",
    "import/no-unresolved": 0,
    "import/order": [
      "error",
      {
        "newlines-between": "never",
        alphabetize: {
          order: "asc",
          caseInsensitive: true,
        },
      },
    ],
    semi: ["error", "always"],
    quotes: ["error", "single", { avoidEscape: true }],
    curly: ["error", "multi-or-nest", "consistent"],
    "linebreak-style": ["error", "unix"],
    "no-duplicate-imports": [
      "error",
      {
        includeExports: true,
      },
    ],
    "rest-spread-spacing": ["error", "never"],
    "no-inline-comments": [
      "error",
      {
        ignorePattern: "_prettier-hack",
      },
    ],
    "prefer-spread": ["error"],
    "prefer-const": "error",
    "no-useless-call": ["error"],
    "no-trailing-spaces": ["error"],
    "space-before-blocks": ["error", "always"],
    "no-unused-vars": [
      "error",
      {
        varsIgnorePattern: unusedVarsIgnorePattern,
        argsIgnorePattern: unusedVarsIgnorePattern,
        caughtErrorsIgnorePattern: unusedVarsIgnorePattern,
      },
    ],
    "no-floating-decimal": ["error"],
    "comma-dangle": ["error", "never"],
    "array-bracket-spacing": ["error", "never"],
    "object-curly-spacing": ["error", "always"],
    "switch-colon-spacing": [
      "error",
      {
        after: true,
        before: false,
      },
    ],
    "space-unary-ops": [
      "error",
      {
        words: true,
        nonwords: false,
      },
    ],
    "space-before-function-paren": [
      "error",
      {
        anonymous: "always",
        named: "always",
        asyncArrow: "always",
      },
    ],
    "keyword-spacing": [
      "error",
      {
        before: true,
        after: true,
      },
    ],
    "space-in-parens": ["error", "never"],
    "block-spacing": "error",
    "key-spacing": [
      "error",
      {
        singleLine: {
          beforeColon: false,
          afterColon: true,
          mode: "strict",
        },
        multiLine: {
          beforeColon: false,
          afterColon: true,
          mode: "strict",
        },
      },
    ],
    "generator-star-spacing": [
      "error",
      {
        before: false,
        after: true,
      },
    ],
    eqeqeq: "error",
    "no-empty": "error",
    "no-debugger": process.env.NODE_ENV === "production" ? "error" : "warn",
    "no-console": process.env.NODE_ENV === "production" ? "error" : "warn",
    "no-alert": process.env.NODE_ENV === "production" ? "error" : "warn",
  },
};

Then make sure to add the following codes at the very top:

const alias = require("./importAliases");

const unusedVarsIgnorePattern = "^_[0-9]+$";

4. Import aliases

Create a file on the root, same level as your .eslintrc, called importAliases.js, and copy-paste the following codes.

// you can setup whatever alias you need here
module.exports = {};

Using these aliases, you can do imports like this:

import MyComponent from "components/MyComponent";

instead of this:

import MyComponent from "../../components/MyComponent";

Feel free to add to these aliases as needed, you only need to update this file.

5. Setup husky and lint-staged

Add the following somewhere at the bottom of your package.js:

"lint-staged": {
  "*.js": [
    "prettier --write",
    "eslint --fix"
  ]
}

Then on your the scripts, add the following:

"eslint": "eslint \"*.js\" \"src/**/*.js\" --fix",
"prettier": "prettier \"*.js\" \"src/**/*.js\" --write",
"lint": "npm run prettier && npm run eslint"

Then run yarn husky install && yarn husky add .husky/pre-commit "yarn lint-staged"

6. jsconfig.json

create a file called jsconfig.json on your root directory, and copy-paste the following codes:

{
  "exclude": ["node_modules"],
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "*": ["src/*"]
    }
  }
}

7. babel.config.js

On your babel.config.js file, add the following plugin:

plugins: [
  // .. other plugins

  // start here
  [
    "module-resolver",
    {
      root: ["./src"],
      alias,
    },
  ],
  // end here
];

and also add the following require at the top:

const alias = require("./importAliases");

8. Final touch

Don't forget to create a src/ folder on your project root, and then move the App.js on that file. Then on the index.js on your root project directory, change this line:

import App from "./App";

To this:

import App from "App";

After this, you should be able to run your app successfully.

This setup achieves:

  • Imports will be resolved from /src folder.
  • Code formatter and linter that works with the import module resolver.
  • Setup react-hooks linting

Using TypeScript

1. Install dependencies

If you are using ReactJS

yarn add prettier eslint @typescript-eslint/typescript-estree @typescript-eslint/eslint-plugin eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-module-resolver eslint-plugin-import eslint-import-resolver-babel-module babel-plugin-module-resolver husky lint-staged eslint-plugin-jest @typescript-eslint/parser --dev

If you are not using ReactJS

yarn add prettier eslint @typescript-eslint/typescript-estree @typescript-eslint/eslint-plugin eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-module-resolver eslint-plugin-import  eslint-import-resolver-babel-module babel-plugin-module-resolver husky lint-staged eslint-plugin-jest @typescript-eslint/parser --dev

2. Code formatting

On your .prettierrc.js file, copy-paste the following codes:

module.exports = {
  tabWidth: 2,
  printWidth: 69,
  useTabs: false,
  semi: true,
  singleQuote: true,
  trailingComma: "none",
  bracketSpacing: true,
  jsxBracketSameLine: false,
  jsxSingleQuote: false,
  quoteProps: "as-needed",
  arrowParens: "avoid",
  insertPragma: false,
  parser: "babel",
};

3. Code Linting

On your .eslintrc.js, copy-paste one of the following codes:

If you are using React

module.exports = {
  settings: {
    react: {
      version: "detect",
    },
    "import/ignore": ["react-native"],
    "import/resolver": {
      "babel-module": {
        extensions: [".android.js", ".ios.js", ".js"],
        alias,
      },
    },
  },
  env: {
    browser: true,
    node: true,
    es6: true,
    "jest/globals": true,
  },
  root: true,
  plugins: ["@typescript-eslint", "jest", "react", "module-resolver"],
  extends: [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:react/recommended",
    "plugin:jest/recommended",
    "plugin:react-hooks/recommended",
    "plugin:import/errors",
    "plugin:import/warnings",
  ],
  globals: {
    Atomics: "readonly",
  },
  parser: "@typescript-eslint/parser",
  parserOptions: {
    sourceType: "module",
  },
  rules: {
    "brace-style": ["error", "1tbs", { allowSingleLine: false }],
    "no-multiple-empty-lines": ["error", { max: 1 }],
    "no-case-declarations": 0,
    "no-return-await": "error",
    "import/no-unresolved": 0,
    "import/order": [
      "error",
      {
        "newlines-between": "never",
        alphabetize: {
          order: "asc",
          caseInsensitive: true,
        },
      },
    ],
    "react/jsx-closing-tag-location": "error",
    "module-resolver/use-alias": 2,
    "react-hooks/exhaustive-deps": "error",
    "react-hooks/rules-of-hooks": "error",
    semi: ["error", "always"],
    quotes: ["error", "single", { avoidEscape: true }],
    curly: ["error", "multi-or-nest", "consistent"],
    "linebreak-style": ["error", "unix"],
    "no-duplicate-imports": [
      "error",
      {
        includeExports: true,
      },
    ],
    "react/prop-types": 0,
    "react/display-name": 0,
    "rest-spread-spacing": ["error", "never"],
    "no-inline-comments": [
      "error",
      {
        // https://github.com/prettier/prettier/issues/7884#issuecomment-760175877
        ignorePattern: "_prettier-hack",
      },
    ],
    "jsx-quotes": ["error", "prefer-double"],
    "prefer-spread": ["error"],
    "prefer-const": "error",
    "no-useless-call": ["error"],
    "no-trailing-spaces": ["error"],
    "space-before-blocks": ["error", "always"],
    "@typescript-eslint/no-unused-vars": [
      "error",
      {
        varsIgnorePattern: unusedVarsIgnorePattern,
        argsIgnorePattern: unusedVarsIgnorePattern,
        caughtErrorsIgnorePattern: unusedVarsIgnorePattern,
      },
    ],
    "no-floating-decimal": ["error"],
    "comma-dangle": ["error", "never"],
    "array-bracket-spacing": ["error", "never"],
    "object-curly-spacing": ["error", "always"],
    "switch-colon-spacing": [
      "error",
      {
        after: true,
        before: false,
      },
    ],
    "space-unary-ops": [
      "error",
      {
        words: true,
        nonwords: false,
      },
    ],
    "space-before-function-paren": [
      "error",
      {
        anonymous: "always",
        named: "always",
        asyncArrow: "always",
      },
    ],
    "keyword-spacing": [
      "error",
      {
        before: true,
        after: true,
      },
    ],
    "space-in-parens": ["error", "never"],
    "block-spacing": "error",
    "key-spacing": [
      "error",
      {
        singleLine: {
          beforeColon: false,
          afterColon: true,
          mode: "strict",
        },
        multiLine: {
          beforeColon: false,
          afterColon: true,
          mode: "strict",
        },
      },
    ],
    "generator-star-spacing": [
      "error",
      {
        before: false,
        after: true,
      },
    ],
    eqeqeq: "error",
    "no-empty": "error",
    "no-debugger": process.env.NODE_ENV === "production" ? "error" : "warn",
    "no-console": process.env.NODE_ENV === "production" ? "error" : "warn",
    "no-alert": process.env.NODE_ENV === "production" ? "error" : "warn",
  },
};

If you are NOT using ReactJS

module.exports = {
  settings: {
    "import/resolver": {
      "babel-module": {
        extensions: [".js"],
        alias,
      },
    },
  },
  env: {
    browser: true,
    node: true,
    es6: true,
    "jest/globals": true,
  },
  root: true,
  plugins: ["@typescript-eslint", "jest", "module-resolver"],
  extends: [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:jest/recommended",
    "plugin:import/errors",
    "plugin:import/warnings",
  ],
  globals: {
    Atomics: "readonly",
  },
  parser: "@typescript-eslint/parser",
  parserOptions: {
    sourceType: "module",
  },
  rules: {
    "brace-style": ["error", "1tbs", { allowSingleLine: false }],
    "no-multiple-empty-lines": ["error", { max: 1 }],
    "no-case-declarations": 0,
    "no-return-await": "error",
    "import/no-unresolved": 0,
    "import/order": [
      "error",
      {
        "newlines-between": "never",
        alphabetize: {
          order: "asc",
          caseInsensitive: true,
        },
      },
    ],
    semi: ["error", "always"],
    quotes: ["error", "single", { avoidEscape: true }],
    curly: ["error", "multi-or-nest", "consistent"],
    "linebreak-style": ["error", "unix"],
    "no-duplicate-imports": [
      "error",
      {
        includeExports: true,
      },
    ],
    "rest-spread-spacing": ["error", "never"],
    "no-inline-comments": [
      "error",
      {
        ignorePattern: "_prettier-hack",
      },
    ],
    "prefer-spread": ["error"],
    "prefer-const": "error",
    "no-useless-call": ["error"],
    "no-trailing-spaces": ["error"],
    "space-before-blocks": ["error", "always"],
    "@typescript-eslint/no-unused-vars": [
      "error",
      {
        varsIgnorePattern: unusedVarsIgnorePattern,
        argsIgnorePattern: unusedVarsIgnorePattern,
        caughtErrorsIgnorePattern: unusedVarsIgnorePattern,
      },
    ],
    "no-floating-decimal": ["error"],
    "comma-dangle": ["error", "never"],
    "array-bracket-spacing": ["error", "never"],
    "object-curly-spacing": ["error", "always"],
    "switch-colon-spacing": [
      "error",
      {
        after: true,
        before: false,
      },
    ],
    "space-unary-ops": [
      "error",
      {
        words: true,
        nonwords: false,
      },
    ],
    "space-before-function-paren": [
      "error",
      {
        anonymous: "always",
        named: "always",
        asyncArrow: "always",
      },
    ],
    "keyword-spacing": [
      "error",
      {
        before: true,
        after: true,
      },
    ],
    "space-in-parens": ["error", "never"],
    "block-spacing": "error",
    "key-spacing": [
      "error",
      {
        singleLine: {
          beforeColon: false,
          afterColon: true,
          mode: "strict",
        },
        multiLine: {
          beforeColon: false,
          afterColon: true,
          mode: "strict",
        },
      },
    ],
    "generator-star-spacing": [
      "error",
      {
        before: false,
        after: true,
      },
    ],
    eqeqeq: "error",
    "no-empty": "error",
    "no-debugger": process.env.NODE_ENV === "production" ? "error" : "warn",
    "no-console": process.env.NODE_ENV === "production" ? "error" : "warn",
    "no-alert": process.env.NODE_ENV === "production" ? "error" : "warn",
  },
};

Then make sure to add the following codes at the very top:

const alias = require("./importAliases");

const unusedVarsIgnorePattern = "^_[0-9]+$";

4. Import aliases

Create a file on the root, same level as your .eslintrc, called importAliases.js, and copy-paste the following codes.

// you can setup whatever alias you need here
module.exports = {};

Using these aliases, you can do imports like this:

import MyComponent from "components/MyComponent";

instead of this:

import MyComponent from "../../components/MyComponent";

Feel free to add to these aliases as needed, you only need to update this file.

5. Setup husky and lint-staged

Add the following somewhere at the bottom of your package.js:

"lint-staged": {
  "*.js": [
    "prettier --write",
    "eslint --fix"
  ]
}

Then on your the scripts, add the following:

"eslint": "eslint \"*.js\" \"src/**/*.js\" --fix",
"prettier": "prettier \"*.js\" \"src/**/*.js\" --write",
"lint": "npm run prettier && npm run eslint"

Then run yarn husky install && yarn husky add .husky/pre-commit "yarn lint-staged"

6. jsconfig.json

create a file called jsconfig.json on your root directory, and copy-paste the following codes:

{
  "exclude": ["node_modules"],
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "*": ["src/*"]
    }
  }
}

7. babel.config.js

On your babel.config.js file, add the following plugin:

plugins: [
  // .. other plugins

  // start here
  [
    "module-resolver",
    {
      root: ["./src"],
      alias,
    },
  ],
  // end here
];

and also add the following require at the top:

const alias = require("./importAliases");

8. Final touch

Don't forget to create a src/ folder on your project root, and then move the App.js on that file. Then on the index.js on your root project directory, change this line:

import App from "./App";

To this:

import App from "App";

After this, you should be able to run your app successfully.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment