# How to Setup/Configure a Node.js project with Typescript, ESLint, Prettier and Husky ![1_D8Wwwce8wS3auLAiM3BQKA](https://user-images.githubusercontent.com/11991333/72227393-f67be200-3593-11ea-8510-7bec5d98afc6.jpeg) > Starting a personal node project could be easy; starting a team node project could be challenging. I Forked from https://gist.github.com/silver-xu/1dcceaa14c4f0253d9637d4811948437 and customize for myself. In my experience, common mistakes developer make when starting a projects are: * No Linting * Lack of compile-time type checking (not really mistake but less desirable in my preference) * Inconsistent code styling * Linter breaking the build In this article, I am going to not only to share how to address these above problems, but also some of the best practices. **The article assumes the reader knows the basics about nodes and typescripts** ## Typescript We can start off by a simple node project consisting only the package.json file (could be -D for DevDependencies it's depends on your neeeds) ```shell npm install typescript ``` After adding the dev dependency, create a `ts.config.json` file under the root of the node project ```json { "compileOnSave": true, "compilerOptions": { "target": "es2021", "module": "commonjs", "moduleResolution": "node", "outDir": "./build", "typeRoots": ["node_modules/@types"], "resolveJsonModule": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "noUnusedLocals": false, "noUnusedParameters": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "sourceMap": true, "skipLibCheck": true, }, "include": [ "src/**/*.ts", "test/*.*.ts"], "exclude": [ "node_modules", "src/logs", "src/tests" ] } ``` The above are the minimal settings of typescript compiler `tsc`. It tells the compiler to * use `es2021` syntax when generating the distributable * use the `commonjs` module format. * generate *.js to `/build` folder * generate source map * include all *.ts file inside `/src` folder * exclude all files inside `/node_modules` folder **Fancy configuration of `ts.config.json` is beyond the scope of this article** Adding the following line to package.json ```json { "scripts":{ "build": "tsc" } } ``` To build, run the following in shell ```shell npm run build ``` ## ESLint We have a handful choices of linting tools for node development, but the de-factor standard these days for typescript is `ESLint`. Partnering with prettier it really improves consistency and productivity of a development team. Again we are starting off by adding the dev dependencies ```shell npm install eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser -D ``` ESLint does not support typescript out of the box. Fortunately we are able to add the typescript support by these two packages `@typescript-eslint/eslint-plugin` `@typescript-eslint/parser` thanks to the ESLint team's modular design. The next thing is to create a `.eslintrc` file. It does not have a file extension but the default is `JSON` format: ```json { "parser": "@typescript-eslint/parser", "extends": ["plugin:@typescript-eslint/recommended"], "parserOptions": { "ecmaVersion": 2021, "sourceType": "module" }, // 0 = off, 1 = warn, 2 = error "rules": { "@typescript-eslint/explicit-member-accessibility": 0, "@typescript-eslint/explicit-function-return-type": 0, "@typescript-eslint/no-parameter-properties": 0, "@typescript-eslint/interface-name-prefix": 0, "@typescript-eslint/explicit-module-boundary-types": 0, "@typescript-eslint/no-explicit-any": 0, "@typescript-eslint/ban-types": 0, "@typescript-eslint/no-var-requires": 0, "@typescript-eslint/no-empty-function": 1 } } ``` It tells the ESLint linter to: * use Typescript parser * use `Recommended Typescript preset` for linting * use `ES2018` and `module` sourceType to match ts.config settings Add the following lines in `package.json`: ```json { "scripts": { "lint": "eslint src/**/*.ts", "format": "eslint src/**/*.ts --fix" } } ``` To lint, run the following in shell ```shell npm run lint ``` To format the code to comply with linting rules, run the following in shell ``` npm run format ``` ## Prettier Prettier drastically improves team consistency by automatically formatting the code. To enable prettier, we first install it by running: ```shell npm install prettier eslint-config-prettier eslint-plugin-prettier -D ``` Configure prettier by adding a `.prettierrc` to the root of the project with the following content ```json { "semi": true, "trailingComma": "all", "singleQuote": true, "printWidth": 120, "tabWidth": 2 } ``` The setting let Prettier to * Ensure semi colon at the end of each statement * Ensure trailing comma at the end of each statement * Convert all double quotes to single quotes where applicable * Break into new lines for all lines greater than 120 characters wide * Ensure tab width is 2 characters In Visual Studio Code, `Ctrl (CMD) + P` then select `Format Code`, or enable `Format on Save` in settings for best result. after that update your `.eslintrc` add prettier config add `prettier` and `plugin:prettier/recommended` on extends key, should be looks like this ```json { "parser": "@typescript-eslint/parser", "extends": ["prettier", "plugin:@typescript-eslint/recommended", "plugin:prettier/recommended"], "parserOptions": { "ecmaVersion": 2021, "sourceType": "module" }, // 0 = off, 1 = warn, 2 = error "rules": { "@typescript-eslint/explicit-member-accessibility": 0, "@typescript-eslint/explicit-function-return-type": 0, "@typescript-eslint/no-parameter-properties": 0, "@typescript-eslint/interface-name-prefix": 0, "@typescript-eslint/explicit-module-boundary-types": 0, "@typescript-eslint/no-explicit-any": 0, "@typescript-eslint/ban-types": 0, "@typescript-eslint/no-var-requires": 0, "@typescript-eslint/no-empty-function": 1 } } ``` ## Husky No matter how careful I am, I always endup with situations where I changed and committed the code to Github without linting, and that can lead to failure CI builds. (YOU SHOULD HAVE SOME ESLINT + PRETTIER READY BEFORE THIS). A good pratcice is to lint before commit. `Husky` is a very popular plugin to achieve so. Install lint-staged by ``` npm install lint-staged husky@4 -D ``` Install husky with npx like this ``` npx husky install ``` Now generate and config lint-staged ``` npx husky add .husky/pre-commit "npx --no-install lint-staged" ``` Make sure to commit the auto-generated husky folder to your Git repo. The above code will run lint-staged command against the staged files before committing. Make sure to run npx husky install if you clone your Husky configured git repo. So now let's add the lint-staged in our package.json file. Open your package.json file and add the code that I have specified at the same level as dependencies. You can configure some script for running unit or functional test if you want after commit or implement for push. ```json "lint-staged": { "*.{js,jsx,ts,tsx}": [ "npm run lint", "npm run unit-test" // example ] } ``` Next time you commit, husky would exit the `git commit` when the code does not pass linting. IMPORTANT If you have troubles remenbers install husky as Dev dependency with v4 ``` npm install -D husky@4 ``` and try it again and you should see the magic ## The End There are so many more things you could do to your project to ensure productivity, consistency and coding styles, but I think this is a good start. This article will be subject to improvements to the latest changes and practices. If you find this article useful please let me know.