Setting up ESLint & Prettier With Webpack in VSCode 🧑‍💻
Tags: eslint, vscode, webpack, prettier
Published: 2022-10-23 01:39:02

In our third part of the series, we will be setting up the configuration for ESLint and Prettier within VSCode.

We will be setting up our ESLint and Prettier without going through either Webpack nor Babel. If you would still like to set up the project like it is in [GitHub](https://github.com/RinconCamilo/Setting-up-ESLint-and-Prettier) then follow the quick set-up guide. Visit the previous articles for a more in-depth explanation of what is happening behind the scenes [here](https://dev.to/rinconcamilo/setting-up-react-babel-webpack-typescript-without-create-react-app-a9l).

**<u>Versions Being Used:</u>**
- React 18
- Babel 7
- Webpack 5
- TypeScript 4
- ESlint 8
- Prettier 2
<br />

# Quick Set-Up
In order to quickly initialize the project, run the following commands:

a. Create package.json file
```powershell
npm init -y
```
b. Install React, Babel, Webpack, TypeScript, and other needed packages
```powershell
npm install --save-dev --save-exact react react-dom @babel/core @babel/preset-env @babel/preset-react babel-loader webpack webpack-cli webpack-dev-server html-webpack-plugin style-loader css-loader file-loader typescript @types/react @types/react-dom @babel/preset-typescript
```
c. Create the Babel configuration file `.babelrc` and paste the following code:
```json
{
  "presets": [
    "@babel/preset-env",
    [
      "@babel/preset-react",
      {
        "runtime": "automatic"
      }
    ],
    "@babel/preset-typescript"
  ]
}
```
d. Create the TypeScript configuration file `tsconfig.json` and paste the following code:
```json
{
  "compilerOptions": {
    // Emit Configuration
    "noEmit": true,

    // Type Checking Configuration
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "exactOptionalPropertyTypes": true,
    "noFallthroughCasesInSwitch": true,
    "noImplicitThis": true,
    "noPropertyAccessFromIndexSignature": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitOverride": true,
    "noImplicitAny": true,
    "noImplicitReturns": true,
    // Strict Rules - v4.8
    "alwaysStrict": true,
    "strictBindCallApply": true,
    "strictFunctionTypes": true,
    "strictNullChecks": true,
    "strictPropertyInitialization": true,
    "useUnknownInCatchVariables": true,
    
    // Modules Configuration
    "baseUrl": "./",
    "module": "ES2022",
    "moduleResolution": "node",
    
    // Language and Environment Configuration
    "target": "ES2022",
    "jsx": "react-jsx",

    // JavaScript Support Configuration
    "allowJs": true,
    "checkJs": true,

    // Interop Constraints Configuration
    "esModuleInterop": true,
    "isolatedModules": true
  },
  "include": ["src/**/**/*"],
  "exclude": ["node_modules"]
}
```
e. Create the `webpack.config.js` file and paste the following code:
```javascript
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");

module.exports = {
  entry: "./src/index.tsx",
  mode: "development",
  output: {
    filename: "bundle.[fullhash].js",
    path: path.resolve(__dirname, "dist"),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./src/index.html",
    }),
  ],
  resolve: {
    modules: [__dirname, "src", "node_modules"],
    extensions: ["*", ".js", ".jsx", ".tsx", ".ts"],
  },
  module: {
    rules: [
      {
        test: /\.(js|ts)x?$/,
        exclude: /node_modules/,
        use: ["babel-loader"]
      },
      {
        test: /\.css$/,
        exclude: /node_modules/,
        use: ["style-loader", "css-loader"]
      },
      {
        test: /\.(png|svg|jpg|gif)$/,
        exclude: /node_modules/,
        use: ["file-loader"]
      }, 
    ],
  },
};
```
f. Create a folder called "src" with the following files:
- `App.tsx`
```javascript
const App = () => (
  <div>
    <h1>Hello, World!</h1>
  </div>
);

export default App;
```
- `index.tsx`
```javascript
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

const rootElement = document.querySelector('#root');
if (!rootElement) throw new Error('Failed to find the root element');
const root = createRoot(rootElement);
root.render(
  <StrictMode>
    <App />
  </StrictMode>
);
```
- `index.html`
```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>React</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>
```
f. Creating the `tscheck`, `build`, and `start` scripts
In the `package.json` file, add these lines to your scripts section:
```powershell
"tscheck": "tsc",
"build": "webpack --config webpack.config.js --mode production",
"start": "webpack serve --open"
```
Now that we have all of the above set-up, the quick set-up should be able to run. If you have any questions or would like to learn more on what is happening above, visit my previous article [here](https://dev.to/rinconcamilo/setting-up-react-babel-webpack-typescript-without-create-react-app-a9l).
<br />

# Implementing ESLint and Prettier
### Background Information:
As a developer, it is always good practice to ask ourselves, why do I need to do this? What use cases would I need to implement this? Is the implementation worth the work compared to the alternative? These are the questions that we are going to be answering.
<br />

#### 1. ESLint vs. Prettier
First things first, "ESlint vs Prettier" should really always be said: ESLint and Prettier. This is because the jobs between these two tools are meant to accomplish different tasks. ESLint will lint our code while Prettier will simply format our code.

**<u>Linting:</u>** Is the way to enforce our code to adhere to certain coding practices. Some examples would include ensuring all console.logs() and un-used variables are deleted while more specific rules would be like not allowing the use of continue statements.

**<u>Formatting:</u>** We only care about how the code visually looks, does not involve catching errors or coding rules. An example would be ensuring that all lines of code are 100 in length and then auto-formatting the code to adhere to this rule.

Now that we have a clearer definition, ESLint will be used as a linter while Prettier will be taking care of our formatting.
<br />

#### 2. Why ESLint and Prettier?
Why would we ever need tools like ESLint and Prettier? As an individual developer, these tools would be in the "nice to haves" categories and are not "100%" necessary. This is because individual developers working on a project can adhere to their own coding rules and styles. 

Though what happens when we now have a team? Maybe multiple teams of developers all working together and each developer having their own coding rules and styling? This can quickly complicate our projects as each pull request (PRs) can have a lot more "un-needed" styling changes and code rules that will then be debated. This makes reviewing PRs a bit more overwhelming than needed and takes time away from really constructively critiquing the actual needed change. This is where ESLint and Prettier come into play.

ESLint will ensure each developer is adhering to certain coding requirements that a team agrees on. Prettier will then ensure that every file will have the same "look and feel" across the project. These two tools will make sure that the team(s) will have better unity and any PRs will have have minimal changes. All of this fulfills a need in having a consistent, front-end project.

If you have any additional benefits of using ESLint and Prettier leave a comment!💬
<br />

#### 3. Implementation Choice
Before starting our set-up, there are three different ways that I know on how we can utilize ESLint; all depending on if we want either Babel, Webpack, or NPM to be linting our code. In this article, we are going to have our ESLint not depend on any other tool other than having NPM run our linting.

My personal reason in organizing the ESLint like this is to have a separation of concerns. I believe having the ability to have our tools (and code) decoupled creates less dependencies and thus, creates less hassle for whenever things eventually break from either updates, configuration changes, or changes to the code itself.

Since we are going to implementing ESLint like with NPM, we will have to add another line in our scripting process.
<br />

### 1. Installing ESLint and Prettier
In order to install ESLint, we need to include the following in our dev dependencies:
```powershell
npm install --save-dev --save-exact eslint eslint-plugin-react eslint-plugin-react-hooks eslint-config-prettier prettier @typescript-eslint/parser @typescript-eslint/eslint-plugin
```
- [eslint](https://www.npmjs.com/package/eslint)
    - Is a tool to identify and report on code patterns.
- [eslint-plugin-react](https://www.npmjs.com/package/eslint-plugin-react)
    - React specific linting rules for ESLint .
- [eslint-plugin-react-hooks](https://www.npmjs.com/package/eslint-plugin-react-hooks)
    - Enforces the Rules of Hooks, is a part of the Hooks API for React.
- [eslint-config-prettier](https://www.npmjs.com/package/eslint-config-prettier)
    - Turns off ESLint rules that would conflict with Prettier's formatting.
- [prettier](https://www.npmjs.com/package/prettier)
    - An opinionated code formatter. Used to enforce a consistent style.
- [@typescript-eslint/parser](https://www.npmjs.com/package/@typescript-eslint/parser)
    - ESLint parser which leverages TypeScript ESTree to allow for ESLint to lint TypeScript source code. 
- [@typescript-eslint/eslint-plugin](https://www.npmjs.com/package/@typescript-eslint/eslint-plugin)
    - ESLint plugin which provides lint rules for TypeScript codebases.
<br />

#### 2. Configuring ESLint
In order to configure ESLint, we need to create the `.eslintrc.json` file in the root directory our current project.

**<u>Background:</u>** Within this file, we can implement different plugins and extensions for ESLint. Some of the basic fields that are in the configuration files are as follows:
- [env](https://eslint.org/docs/latest/user-guide/configuring/language-options#specifying-environments)
    - Environments your script is designed to run in.
- extends
    - Allows ESLint to use a pre-existing ESLint configuration and will make overrides.
- [parser](https://eslint.org/docs/latest/user-guide/configuring/plugins)
    - Converts code to an Abstract Syntax Tree (AST).
- [parserOptions](https://eslint.org/docs/latest/user-guide/configuring/language-options#specifying-parser-options)
    - Tells ESLint what version of JavaScript we are targeting.
- [plugins](https://eslint.org/docs/latest/user-guide/configuring/plugins)
    - Are additional rules that can be used in order configure your ESLint. 
- [settings](https://eslint.org/docs/latest/user-guide/configuring/configuration-files#adding-shared-settings)
    - Is shared settings that specify information that should be consistent across all of its rules.
- [rules](https://eslint.org/docs/latest/user-guide/configuring/rules)
    - Where we can configure the different rules our plugins and ESLint has to offer.

**<u>Setting up .eslintrc.json:</u>** Here is my `.eslintrc.json` that I use, if you have any suggestions please drop a comment!
```json
{
  "env": {
    "browser": true,
    "es2022": true,
    "node": true
  },
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:react/recommended",
    "plugin:react-hooks/recommended",
    "prettier"
  ],
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true
    },
    "ecmaVersion": "latest",
    "sourceType": "module"
  },
  "plugins": [
    "react",
    "@typescript-eslint"
  ],
  "settings": {
    "react": {
      "version": "999.999.999"
    }
  },
  "rules": {
    "react/jsx-uses-react": "off",
    "react/react-in-jsx-scope": "off"
  }
}
```
Here are some explanations over a few configurations:
- env
    - We are going to add support for the es6 global variables by setting es2022 to true.
- extends
    - Here we utilize the recommended ESLint and plugin rules. We need to make sure to place Prettier last in order to override any rules that would cause conflict between Prettier and the other packages.
- parser
    - We are selecting the `@typescript-eslint/parser` since our project has TypeScript.
- parserOptions
    - Since we are utilizing React, we are wanting to turn on jsx. We are also going to add support for the latest ecma syntax by setting ecmaVersion to latest.
- plugins
    - We are needing to modify some of the rules for these plugins which is why we included them here.
- settings
    - We are wanting to setting the react version here in order to avoid the react warning of not having a version specified. We can place an exact version of react but for now we can leave it as the value shown.
- rules
    - Since we are using the jsx transform, we can disable the specific rules listed.

Lastly, for setting up our `.eslintrc.json` we need to create a file similar to `.gitignore` called `.eslintignore`. This works very much the same with the main difference of telling ESLint which files we should not check. We will be adding several files for our current project:
```txt
node_modules
dist
package-lock.json
webpack.config.js
```
We do this in order to help speed up the process of linting our project. Add or take away depending on your needs!
<br />

####3. Configuring Prettier
Now we can create a .prettierrc.json. This is where we will put our code formatting rules that prettier will run and adjust to!

Here is my formatting rules, leave a comment as to what rules you guys like to use!
```json
{
  "semi": true,
  "tabWidth": 2,
  "printWidth": 100,
  "singleQuote": true,
  "trailingComma": "none",
  "jsxBracketSameLine": true
}
```
<br />

####4. Configuring VSCode
With the VSCode IDE, we have the following settings options available for the user to configure:
- User Settings
    - Settings that will apply globally to any instance of VSCode on your machine.
- Workspace Settings
    - Settings that are set inside your project that will be applied every time your specific project is open

We will be creating a workspace settings in order to be able to share, and have a coherent environment across teams when sharing our project. The following is what I have in my settings.json:
```json
{
  "editor.defaultFormatter": "rvest.vs-code-prettier-eslint",
  "editor.formatOnPaste": false, // required
  "editor.formatOnType": false, // required
  "editor.formatOnSave": true, // optional
  "editor.formatOnSaveMode": "file", // required to format on save
  "files.autoSave": "onFocusChange" // optional but recommended
}
```

With these settings, we will now be able to to use prettier everytime we save.
<br />

####5. Configuring package.json
Now in the package.json file, we need to add this line to your scripts section:
```json
"lint": "eslint .",
"lint:fix": "eslint . --fix",
```
This will lint all the code. And will also try to auto-fix any of the linting errors that are brought up.
<br />

View everything together in [Github](https://github.com/RinconCamilo/Setting-up-ESLint-and-Prettier).

If you found this helpful or just enjoyed reading the article, consider grabbing me a [cup of coffee](buymeacoffee.com/rinconcami9).