DEV Community

Cover image for The best JavaScript project setup to work efficiently and keep your code base clean
Andreas Riedmüller
Andreas Riedmüller

Posted on • Updated on

The best JavaScript project setup to work efficiently and keep your code base clean

To be honest, "The best…" for anything is hard to argue and in most cases not true. Nevertheless, I chose this title because I'm documenting what I currently consider to be "the best" setup for my JavaScript projects.

This is not my first shot, but the result of my experiences over the last few years. I would also like to take this opportunity to thank my colleague Jay Watts for the many suggestions that have led to the current state of this setup.

The setup is quite simple and most of the time default configs are used.

🤓 Whilst there is a link to a GitHub template (React/Typescript) at the end of this article, following the steps below doesn't take long and I think it's worth doing it manually, especially the first time. If you feel like this is already to much effort, you probably don't need any of this and can just scaffold a default Vite project. You can always add these features later if you miss them.


Here is my take on "the best" setup in a few bullet points:

  • GitHub
  • Vite
  • Prettier
  • Import sorting (eslint plugin)
  • lint-staged
  • husky (pre-commit/pre-push hooks)

1. Create a new Vite project

Vite made my life so much easier by providing a simple way to setup a new bundler project with very good default configurations. It is just so powerful that you can easily bootstrap a setup that works out of the box in seconds. For many just creating a new Vite project might be good enough already.

To create a new Vite project all you need to do is run this command and follow the instructions:

npm create vite@latest

? Project name: › my-project
? Select a framework: › React
? Select a variant: › TypeScript
Enter fullscreen mode Exit fullscreen mode
cd my-project
Enter fullscreen mode Exit fullscreen mode
npm i
Enter fullscreen mode Exit fullscreen mode

2. Init Git and create a .nvmrc

This are the two things I do every time I setup a new node project.

Early first commit

git init
git add --all
git commit -m "blank vite react/typescript project"
Enter fullscreen mode Exit fullscreen mode

Create a .nvmrc

This file contains the version of node that is recommended for the project. If nvm is installed on your system, you can run nvm use in the project folder and nvm will automatically switch to the specified version.

node --version > .nvmrc
git add .nvmrc
git commit -m "create .nvmrc file"
Enter fullscreen mode Exit fullscreen mode

💡Pro Tip You can set up a script that will automatically call nvm use whenever you enter a directory that contains an .nvmrc

3. Prettier

Since I started using Prettier, there's no going back for me. Every time I save, my code is automatically formatted according to a uniform standard. I decided to just follow their default rules and got used to it quickly. This is a big time saver.

You can read my in depth article on how to install and configure Prettier (including configuring your editor) or follow the official installation guide

Here are the steps in a nutshell:

Install Prettier

npm install --save-dev --save-exact prettier
node --eval "fs.writeFileSync('.prettierrc','{}\n')"
Enter fullscreen mode Exit fullscreen mode
git add --all
git commit -m "install prettier"
Enter fullscreen mode Exit fullscreen mode

Format all files

npx prettier . --write
Enter fullscreen mode Exit fullscreen mode
git add --all
git commit -m "format files with prettier"
Enter fullscreen mode Exit fullscreen mode

4. Import sorting

This is another feature I don't want to miss anymore. Especially when your editor automatically adds imports it is cumbersome to sort them manually.

I have already written a separate article on this topic, which also contains my custom sorting configuration: Automatic import sorting with ESLint.

Here are the basic steps to set up automatic import sorting for your project:

Install ESLint plugin

I currently use ESLint for sorting imports. My plugin of choice is eslint-plugin-simple-import-sort https://github.com/lydell/eslint-plugin-simple-import-sort

npm install --save-dev eslint-plugin-simple-import-sort
Enter fullscreen mode Exit fullscreen mode
git add --all
git commit -m "install eslint-plugin-simple-import-sort"
Enter fullscreen mode Exit fullscreen mode

Configuration

Enable the plugin and rule in your eslint config:

// .eslintrc.cjs
--  plugins: ["react-refresh"],
++  plugins: ["react-refresh", "eslint-plugin-simple-import-sort"],
Enter fullscreen mode Exit fullscreen mode
// .eslintrc.cjs
  rules: {
++    "simple-import-sort/imports": ["error"],
Enter fullscreen mode Exit fullscreen mode

If you have set up the plugin in this way, you will receive ESLint errors if the imports are not sorted according to the rules. You can now run fix for all files to automatically fix these issues and sort your imports.

npx eslint --fix .
Enter fullscreen mode Exit fullscreen mode

I highly recommended you setup your editor to "fix" automatically every time you save a file. You can find detailed instructions to set this up for VSCode in my automatic import sorting article.

git add --all
git commit -m "configure eslint-plugin-simple-import-sort"
Enter fullscreen mode Exit fullscreen mode

5. Import sorting

I've never been a fan of sorting CSS properties alphabetically. Why should text-related styles, for example, be scattered? At the same time I do like sorting and have had my own (admittedly inconsistent) way of organizing properties.

Recently I stumbled upon prettier-plugin-css-order which uses concentric-css (per default) to sort CSS properties. I just started using this, and I do have a very good feeling about it.

There is also the option to sort alphabetically (🙂‍↔️) and SMACSS (another opinionated sorting approach).

This is what concentric-css does:

Order properties applying outside the box model, moving inward to intrinsic changes.

  1. Positioning
  2. Visibility
  3. Box model
  4. Dimensions
  5. Text

Install prettier plugin

npm install --save-dev prettier-plugin-css-order 
Enter fullscreen mode Exit fullscreen mode

The docs suggest to also explicitly install postcss but I think this can/should be skipped because Vite does this already.

Configuration

.prettierrc
{
  "plugins": ["prettier-plugin-css-order"],
  "cssDeclarationSorterKeepOverrides": false
}
Enter fullscreen mode Exit fullscreen mode

The config cssDeclarationSorterKeepOverrides is true per default, but it is recommended to turn this off if you can.

Read more about what this does here: https://github.com/Siilwyn/css-declaration-sorter#keepoverrides

6. Pre-Commit and Pre-Push hooks

With Husky you can run checks on the code you are about to commit/push. I use this to enforce the formatting rules for all commits and check for Typescript issues before pushing. The fact that this rules trigger from time to time is a good indicator that they are indeed very helpful for me :-)

And lint-staged is yet another tool that can run linters on staged files only.

As per Prettier docs you can run this command to automatically setup lint-staged and husky with a pre-commit hook. It will also add the necessary configuration to your package.json

npx mrm@2 lint-staged
Enter fullscreen mode Exit fullscreen mode

Instead of the generated configuration I use this setup which does not allow you to commit anything with ESLint warnings and formats all files with prettier:

// package.json
…
  "lint-staged": {
    "*.{js?(x),ts?(x)}": [
      "eslint --max-warnings 0",
      "prettier --write --ignore-unknown"
    ],
    "!*.{js?(x),ts?(x)}": "prettier --write --ignore-unknown"
  }
…
Enter fullscreen mode Exit fullscreen mode
git add --all
git commit -m "install/configure husky and lint-staged"
Enter fullscreen mode Exit fullscreen mode

7. TypeScript checks with pre-push hook

Because for Typescript the context of "only staged files" is not enough, you need to run tsc on the whole project instead of using lint-staged. As this takes a little longer, I do not want to run this for every commit but only before pushing code.

Add a pre-push file in the .husky directory:

mkdir -p .husky && touch .husky/pre-push
Enter fullscreen mode Exit fullscreen mode

Copy this script to the created file:

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

printf "\n[PRE-PUSH HOOK] Running tsc checks. This might take a few moments.\n\n"

npx tsc --noEmit
Enter fullscreen mode Exit fullscreen mode
git add --all
git commit -m "add pre-push TypeScript check"
Enter fullscreen mode Exit fullscreen mode

Done!

That's it, you can now start doing changes and enjoy your setup. Have fun!

And please let me know if this article has helped you. I am always happy to receive feedback and hear about your experiences.


GitHub Template Repository: https://github.com/receter/my-vite-react-template

Top comments (2)

Collapse
 
dikamilo profile image
dikamilo

Hard pass on git hooks. They generate more problems that advantages especially when you have complex git flow, need to merge/rebase/hard push, have problem with conflicts etc.

I prefer lint and prettier on save (action in editor/IDE) + same on CI/CD.

Collapse
 
receter profile image
Andreas Riedmüller • Edited

Hi dikamilo,

Can you still remember what problems you had with git hooks?

Also I would be interested in how you use prettier/lint in CI/CD.

Thanks for your comment!

Some comments may only be visible to logged-in visitors. Sign in to view all comments.