[EN] The next step to cleaner, more consistent Frontend code
ℹ️ 本記事の日本語版はこちらをご覧ください。
Introduction
In this second part, I want to guide you on how to get the most out of your ESLint/Prettier setup. Aside from just installing them, there are several additional adjustments you can make to maximise their effectiveness.
⚠️ If you stumbled on this page first and want to read part 1, please refer to it here.
Who is this for?
- Junior developers looking to level up
- Senior developers wanting to review their approach
- Anyone in between that might be looking for new ideas
What to focus on
Prettier
As mentioned in the first part, Prettier's opinionated stance means there isn't much to do beyond installation. However, here are some recommendations to get the most out of Prettier:
Set up your .prettierignore
By default, Prettier's ignore file will reference your .gitignore, but more often than not, there are additional files you don't want the formatter touching:
- Sensitive files relating to production builds
- CI/CD tool config files
- Documentation
- Additional directories hosted within the same codebase
- Cache files
Adjust formatting preferences
Prettier does have some options that change the way it formats. Here are some changes you can make for your .prettierrc
file that are not the default values:
-
"singleAttributePerLine": true
- Easier to read attributes on any given HTML, Vue, JSX component
-
"jsxSingleQuote": true
- For those that prefer single quotes in their JSX
-
"quoteProps": "consistent"
- If at least one property in an object needs quotes, then quote all properties
-
"singleQuote": true
- Similar rationale to
jsxSingleQuote
above
- Similar rationale to
-
"trailingComma": "es5"
- The default
"all"
option requires a minimum ES2017 and could conflict with older projects
- The default
Make sure to go through the full list of options in the Prettier documentation if you really want to customize the formatting rules. Explicitly declaring all options in your config file (even if it is a default value) helps to eliminate uncertainty in a shared repository, and makes for a reusable file in multiple projects.
Plugins
Prettier already supports a large number of programming languages, but with third-party plugins, you can extend that list even further.
Take for example the popular frontend framework Tailwind CSS and it's Prettier plugin:
npm install --save-dev --save-exact prettier-plugin-tailwindcss
By installing and adding it to your .prettierrc
, Prettier is able to reorder Tailwind CSS classnames based on the recommended class order.
{
"plugins": ["prettier-plugin-tailwindcss"]
}
ESLint
The following information will assume you answered the questions for the initialiser as below, but the concepts should be the same across the board.
✔ What do you want to lint? · javascript
✔ How would you like to use ESLint? · problems
✔ What type of modules does your project use? · esm
✔ Which framework does your project use? · react
✔ Does your project use TypeScript? · Yes
✔ Where does your code run? · browser
✔ Which language do you want your configuration file be written in? · js
The config that you've selected requires the following dependencies:
eslint, @eslint/js, globals, typescript-eslint, eslint-plugin-react
✔ Would you like to install them now? · Yes
✔ Which package manager do you want to use? · npm
The Configuration File
The initialiser will create a eslint.config.js
file for you in your root directory. You will refer to this whenever you want to adjust rules, or add new ones. Assuming you followed the initialiser the same as above, your eslint.config.js
file will be configured to check all the filetypes in the files
array. It will also be using ESLint's recommended set of rules for JavaScript environments, as well as recommendations from typescript-eslint
and eslint-plugin-react
.
If there are any rules you would like to tweak, you can add them to the rules
object.
The same rules defined later in the configuration will take priority over earlier ones. In this example, the tseslint
recommended "error" setting for no-unused-vars
will be turned down to "warn".
export default defineConfig([
// ...
tseslint.configs.recommended,
pluginReact.configs.flat.recommended,
{
rules: {
"@typescript-eslint/no-unused-vars": "warn",
},
},
]);
This is useful if you are adding rules to an existing project but don't want to hamper ongoing development. This way, you can see the problem code, but don't have to fix it right away. The recommended rules alone should be more than enough to have any project on the right path, but there are a few more that go that extra step.
Rule recommendations
Here is a short list of rules (and a brief rationale as to why for each) that you should consider including in your configuration:
-
"no-console": "error"
- Never push
console.log()
to production ever again
- Never push
-
"no-constant-condition": ["error", { "checkLoops": "allExceptWhileTrue" }]
- Similar to
no-console
, this prevents development-only code likeif (true)
from reaching production (except for the ever-so usefulwhile (true)
)
- Similar to
-
"no-duplicate-imports": "error"
- For more succinct import statements
-
"no-nested-ternary": "error"
- Avoids ternary expressions from getting too long and hard to read
-
"@typescript-eslint/no-explicit-any": "error"
- Prevent unsafe use of
any
assertions that disable type-checking
- Prevent unsafe use of
-
"@typescript-eslint/strict-boolean-expressions": "error"
- Prevents use of non-boolean values where booleans are expected (
if
,filter
returns,assert
, etc.)
- Prevents use of non-boolean values where booleans are expected (
-
"@typescript-eslint/switch-exhaustiveness-check": "error"
- Making sure you either use a default, or cover every possible case for
switch
statements (much like the Rust compiler andmatch
expressions)
- Making sure you either use a default, or cover every possible case for
-
"@typescript-eslint/consistent-type-imports": "error"
- Useful when you only need to import a type
💡 Always remember to refer to documentation to see the full list of rules and detailed explanations on how to use them. Much like the
no-constant-condition
above, rules tend to have several additional settings to adjust their specificity.
Plugins
If the plugins you start with are not enough, there are many third-party plugins to further extend your configuration.
-
eslint-plugin-unicorn
- A kitchen-sink of rules, enforcing modern best practices and suggesting concise ways to write code
-
eslint-plugin-total-functions
- A handful of rules promoting 'total functions' programming in Typescript
-
eslint-plugin-import
- For ES6+ import/export syntax
-
eslint-plugin-next
- Tailored ruleset for Next.js development
Plugins are added by installing first,
npm install --save-dev --save-exact eslint-plugin-unicorn
then importing and adding it to your eslint.config.js
file
import eslintPluginUnicorn from 'eslint-plugin-unicorn';
export default defineConfig([
// ...
eslintPluginUnicorn.configs.recommended,
]);
Custom rules
You are also able to write your own rules to fit your needs. ESLint has robust documentation on making custom rules, which I will not go over here. However, for some inspiration, I will share some of my use cases:
- Raising a warning when an error catch block is missing a logger function to report errors
- Restricting use of getter/setter methods for client-side data (sessionStorage, localStorage) to a dedicated file/module
- Warning the use of deprecated functions/components that are slowly being refactored out
- Enforcing useHook naming conventions for React Hooks
When working on your own projects, you will encounter issues unique to your codebase. You might notice yourself making the same mistake every time, but existing rules can't seem to point it out. In these cases, taking the time to write a custom rule to detect said mistake could be useful. You could even consider leveraging AI to create these rules for you and save on time.
In conclusion
Hopefully by the end of this, you have a better understanding about ESLint and Prettier, as well as some ideas for how to best make use of them. They are a powerful combination and are utilised in many projects, their customisability making them versatile and long-lasting. Here's to cleaner, more consistent coding!
Discussion