💡 Also see my page: "General DevOps and Dev Tools"
Enforcing coding standards
The usual way to enforce coding standards, such as how to use JSDoc commenting, is through a linting extension, such as eslint
for JS, or tslint
for TypeScript. These extensions are configurable, so you can tell it what to look for and how strict it should be.
Helpful links:
However, note that if these are installed as "extensions" they only enforce rules for yourself and devs that have it installed. If you are trying to enforce standards across many devs, across a repo, you might want to make the linter a dependency and then have it run as a build step and/or git hook.
TSLint vs ESLint
Based on what Palantir has publicly said, everyone should try to use ESLint over TSLint moving forward.
ESlint has also made it clear that this will be the way forward for TypeScript.
XO Notes:
XO - General Use
In general, XO is mostly a set of thought-out (opinionated) default settings for ESLint, so you don't have to mess with a bunch of config files.
This also means that adjusting XO really usually means just adjusting ESLint rules, so make sure to check out my eslint notes, as well as the official docs and rules.
If you want just the ESLint config files that match XO, you can find them here
XO - Config file
Easiest way is to add to your package.json
, under xo: {}
block. Or, run npm init xo
, which does the same thing for you.
You also have the option of:
.xo-config
(JSON).xo-config.json
(JSON).xo-config.js
(JS Module)xo.config.js
(JS Module)
For details, see the config section of the readme.
Warning: The main XO VSCode extension does not contribute JSON Schema intellisense for any configs.
XO - Ignoring Lines or Files
You can ignore a specific line by using the ESLint disable comment trick (since XO uses ESLint), but you must target a specific rule to disable. For example, to disable XO(no-unused-vars)
for one line:
// eslint-disable-next-line no-unused-vars
const neverUsed = 123;
For ignoring entire files, you have a few options:
- Add to
.gitignore
and/or.eslintignore
--ignore
CLI option- Add glob patterns to
ignores
config option:ignores: []
TSLint notes:
TSLint - Overriding rules:
See: rule-flags docs.
- Temporarily disable on next line:
/* tslint:disable-next-line */
- Or
// @ts-ignore
- Disable rule
/* tslint:disable:{rule} */
ESLint notes:
ESLint - Install
# NPM
npm install eslint --save-dev
# Yarn
yarn add eslint --dev
ESLint - Config file
See Configuring ESLint for details, and this section for precedence of config files.
Summary of options:
.eslintrc.*
.eslintrc.json
(comments allowed).eslintrc
(same as above).eslintrc.js
(export config object).eslintrc.yaml
|.eslintrc.yml
package.json
->eslintConfig: {}
You can use
npx eslint --init
to auto-create a.eslintrc
file.
ESLint - Config File - Intellisense and Autocomplete
If you are using the .eslintrc.js
option, you need to annotate the type of the config object for intellisense to kick in:
/** @type {import('eslint').Linter.Config} */
module.exports = {
env: {
browser: true,
}
//,
// ...
}
⚠ Warning: Due to incomplete types exported by ESLint core, this actually has worse type checking than using the JSON based options (since that pulls from published schema instead of types). Even using
@types/eslint
doesn't look much better 🤷♀️
For any .json
config files, if you aren't getting intellisense (due to non-standard filename), you can specify the $schema
manually:
{
"$schema": "https://json.schemastore.org/eslintrc",
"env": {
"browser": true
}
}
ESLint - Peer dependencies
A lot of eslint config packages require peer-dependencies that are not automatically installed when you do NPM install. For example, the eslint-config-airbnb
needs a lot of peer dependencies to be manually installed, or else it will not work.
See my notes on NPM / node for details on auto-installing peer-dependencies.
ESLint - TypeScript
Docs: TypeScript ESLint Docs
- REMEMBER: install
@types/node
for node support - Make sure typescript is installed locally (
yarn add typescript --dev
) - Add the parser (
@typescript-eslint/parser
) and the plugin (@typescript-eslint/eslint-plugin
) dependencies- Update the ESlint config to use them:
{ "parser": "@typescript-eslint/parser", "plugins": ["@typescript-eslint"] }
- Update the ESlint config to use them:
- You might need to explicitly add TypeScript as a language for ESlint to validate:
- In
.vscode/settings.json
->"eslint.validate": ["javascript", "javascriptreact", "typescript"]
- In
- If you want ESlint to recognize and read your TSConfig file, you might need to tweak some settings in your
eslintrc
config:parserOptions: {"project": "./tsconfig.json"}
- Warning: After enabling this, you might also have to deal with a "the file does not match your project config" error on certain files
- If you use import or exports, turn on module support:
parserOptions: {"sourceType": "module"}
- If you get the
error 'YourType' is not defined no-undef
for a variable that using a type defined via an ambient declaration file (e.g.my-types.d.ts
), check to make sure that you have correctly configured ESLint to read yourtsconfig
file & project info- See notes above, and Docs - "Linting with Type Information"
If you get "Parsing error: ImportDeclaration should appear when the mode is ES6 and in the module context" error, first try making sure you have "sourceType" set to "module" under
parserOptions
(see this for details). If this doesn't fix it, it could be because you are importing a type out of a type definition file (e.g.@types/...
), which the current parser + eslint can have a hard time handling. There are several issues around this, here and here.
Some extra guides:
- https://mhartington.io/post/typescript-eslint-setup/
- https://javascriptplayground.com/typescript-eslint/
- https://43081j.com/2019/02/using-eslint-with-typescript
ESLint - Misc troubleshooting
Parsing error: The keyword '___' is reserved
(where keyword is a native JS thing likeconst
orimport
)- Make sure you have
ecmaVersion
set. For example:// .eslintrc.json { "extends": ["prettier"], "parserOptions": { "ecmaVersion": 2018 } }
- Make sure you have
Parsing error: 'import' and 'export' may appear only with 'sourceType: module'
- Setting
parserOptions: { "sourceType": "module" }
is often enough to fix it (with the right ecmaVersion set) - Can also be fixed by using
babel-eslint
- Setting
Parsing error: Unexpected token <
- Are you using React? If so, you need to take a few steps:
- Make sure
eslint-plugin-react
is installed (yarn add --dev eslint-plugin-react
) - Add plugin to eslint config:
"plugins": ["react"]
- Add JSX support to eslint config:
"parserOptions" -> "ecmaFeatures" -> "jsx": true
- Make sure
- Are you using React? If so, you need to take a few steps:
- ESLint is not kicking-in on VSCode
- Very helpful: check what is showing up in the Output panel of VSCode, after selecting "ESLint" from the dropdown.
- Make sure that the filetype you are trying to have ESLint kick in for is part of the default set - e.g., if you are trying to lint React (
.jsx
,.tsx
) or Svelte (.svelte
) files, you would need to add those filetypes to your VSCode settings.- E.g.:
.vscode/settings.json
->"eslint.validate": ["javascript", "javascriptreact", "typescriptreact", "svelte"]
- E.g.:
ESLint - Issues with plugins
This can be tricky to troubleshoot. If you are on version 6, you could always try pinning ES to version 5 - see this thead and this one.
Another thing to try is to make sure you are not mixing dependency locations - e.g. ESLint and all its plugins and dependencies need to be installed locally (through package.json) or globally (npm install -g
), but not both. And with v6+, it looks like you should only install locally.
Another common issue is completely unrelated, but often throws errors about plugins - issues with a multi-root workspace - see below:
ESLint - Multi-root workspace / monorepos
I recently had a heck of a time figuring out why ESLint was throwing all kinds of errors that didn't make sense - primarily the:
"Failed to load plugin 'vue' declared in 'frontend.eslintrc.js': Cannot find module 'eslint-plugin-vue'
That error actually had nothing to do with the plugin itself, but everything to do with the workspace file structure. I had what one might call a multi-root or monorepo structure, where major distinct projects (with separate node_modules and package.json) are in the same workspace. My structure looked like:
.
(project root)./frontend
./backend
It turns out for this kind of setup, running eslint against a file will work fine from the command line, but running from VSCode / extension will fail, because it doesn't understand the separation.
The solution is simple: the eslint.workingDirectories
setting.
In {project-root}/.vscode/settings.json
, I should have had:
{
"eslint.workingDirectories": [
"./frontend",
"./backend"
]
}
You can omit a directory if it is not configured for eslint. Or, if eslint is having trouble with imports, use changeProcessCWD
(last resort):
{
"eslint.workingDirectories": [
{
"directory": "./frontend",
"changeProcessCWD": true
}
]
}
ESLint - Overriding rules
There are multiple ways to modify eslint rules.
Here is the full list of rules.
ESLint - Modifying the actual config files
See this section about the difference between all the different config file types.
If you you just to turn a rule off, just set the key/pair value to "off".
{
"rules": {
"prefer-arrow-callbacks": "off"
}
}
ESLint - Non-global Overrides
If you just need to suppress the errors in a single spot or file, you can use inline comments.
For example, to disable / ignore for an entire file, you can put this at the top of the file:
/* eslint-disable */
Or, for a specific line, // eslint-disable-line
or // eslint-disable-next-line
.
You can also target specific rules:
// Example is function with too many params - violates `max-params` rule
// eslint-disable-next-line max-params
function tooManyParams(alpha, beta, charlie, delta, echo, foxtrot, golf) {
//
}
If you want to disable multiple rules that apply to a single line, comma separate them.
// eslint-disable-next-line max-params, no-underscore-dangle
function tooManyParams(alpha, beta, charlie, delta, echo, foxtrot, golf_) {
//
}
If you want to combine with a ts-ignore
exception, or just disable for a lot of problematic code, the best way to do this is with multi-line comments that disable and re-enable rules for entire blocks of code. But make sure you use /*
as the starting delimiter, and not /**
:
/* eslint-disable */
// @ts-ignore
// [...] problematic code
/* eslint-enable */
// You can also combine this with rules:
/* eslint-disable {RULE_A}, {RULE_B} */
// [...] problematic code
/* eslint-enable */
ESLint - AirBnB Config Setup
- Install config and peer dependencies
- React:
npx install-peerdeps --dev eslint-config-airbnb
- If above doesn't work, see alternatives
- Non-React:
npx install-peerdeps --dev eslint-config-airbnb-base
- If above doesn't work, see alternatives
-
For above commands, use
--yarn
flag if using yarn.
- React:
- Add the airbnb config to the
extends
option in your eslint config file- If you are using prettier, make sure those rules come last:
- Example:
"extends": ["airbnb-base", "plugin:prettier/recommended"]
- Example:
- If you are using prettier, make sure those rules come last:
ESLint + Prettier - Sample Workflow
- Install ESlint, Prettier, Config, and Plugin
yarn add --dev eslint prettier eslint-config-prettier eslint-plugin-prettier
- OR
npm install --save-dev eslint prettier eslint-config-prettier eslint-plugin-prettier
- Setup config file for ESlint
- Create it
echo $'{\n "extends": ["plugin:prettier/recommended"]\n}' > .eslintrc.json
- OR
npx eslint --init
and then manually add
- OPTIONAL: Set logical defaults
- You usually want to tweak
parserOptions -> ecmaVersion
if you are using any "newer" features, such as theconst
keyword "env": { "browser": true }
- You usually want to tweak
- Create it
- Tweak defaults to desired
- For formatting rules, you should really make changes to Prettier directly in Prettier config files, not ESLint
touch .prettierrc
- Modify
.prettierrc
:{ "tabWidth": 4, "useTabs": true, "singleQuote": true, "endOfLine": "lf" }
- Optionally, could set some of these settings in
.vscode/settings.json
too- Example:
"editor.insertSpaces": false
- Example:
- Pure ESLint changes can go under
.eslintrc.json
->rules: {}
- For formatting rules, you should really make changes to Prettier directly in Prettier config files, not ESLint
- OPTIONAL: Integrate with other ESLint configs
- Reminder: Prettier is really concerned with code formatting (spacing, indenting, etc.), and not linting rules (no-unused-vars, etc.)
- They try to make this clear, but when combining with ESLint, it can be easy to forget that you still need to add pure lint rules to get more value out of ESLint
- The most popular option is to combine with
eslint-config-airbnb
- See my guide for that here
- Reminder: Prettier is really concerned with code formatting (spacing, indenting, etc.), and not linting rules (no-unused-vars, etc.)
- OPTIONAL: Integrate into dev pipeline
- You can have VSCode auto-format with prettier every time you hit save:
.vscode/settings.json
:// For more granular formatOnSave, nest under per-language settings with `"[javascript]": { ... }` "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode",
- You can add formatting and/or linting as build or test step
{ "lint": "eslint", "lint:fix": "eslint --fix" }
You might need to tweak these to target specific glob patterns or file types, depending on how other config files are setup
Also, note thateslint --fix
will actually use Prettier for formatting if you have followed the above steps correctly. This is preferred overprettier --write
- You can add formatting and/or linting as pre-commit hooks
- Tons of guides out there.
husky
is the go-to for running git hooks - prettier docs
- Bare-bones instructions:
yarn add --dev husky
package.json
:"husky": { "hooks": { // call your lint and format script entries below "pre-commit": "___" } }
- Tons of guides out there.
- You can call your lint script entry from your CI/CD pipeline, to check code quality on every push
- This ensures that code that violates a linting rule can't make it past, even if a developer has a broken dev environment and their local machine is not catching things
- You can have VSCode auto-format with prettier every time you hit save:
Prettier Notes
Prettier - Common CLI Commands
📘 Docs: CLI
- Format and write out changes
prettier --write {inputGlobOrFiles}
- Examples:
prettier --write .
prettier --write index.js
prettier --write "src/**/*.js"
- Check only, and don't write out changes
prettier --check {inputGlobOrFiles}
- Since this will emit an exit code, you can use this as part of a commit hook, CI/CD pipeline, etc.
Prettier - Multiple Glob Patterns and File Inputs
If you wan to pass multiple glob patterns and file inputs to Prettier, there are a couple ways to go about doing it.
As a CLI argument, you can pass multiple glob patterns at the end - for best results, you might want to wrap each glob in quotes so that the expansion is handled by prettier, instead of your OS.
Example:
# Multiple globs
prettier --write "src/**/*.ts" "*.js"
# Can use advanced globs
prettier --write "src/**/*.{js,ts}" "*.json" "components/*.jsx"
# and/or multiple files
prettier --write file-a.js file-b.js # ...
If you are storing this as a command in
package.json
, escape the double quotes with\"
If you have tons of different files to match, instead of explicitly including every file you need to format or lint, it might be easier to include everything and only explicitly exclude certain files, with a .prettierignore
file.
Prettier - Config file
Options:
.prettierrc
package.json
->prettier: {}
.prettierrc.json
|.prettierrc.yml
|.prettierrc.yaml
.prettierrc.js
|.prettierrc.config.js
- Must export object with config
.prettierrc.toml
- Less Documented:
.vscode/settings.json
- Prefixed with
prettier
: e.g.{ "prettier.tabWidth": 4 }
- NOTE: This really only affects the Prettier VSCode extension - not the core dependency / engine itself
- Prefixed with
Prettier - Per File Overrides
The easiest and cleanest way to set per-file overrides with Prettier is through the config file, under the overrides
section.
If you are trying to override only through VSCode settings (for the Prettier extension, rather than at the project level), you are pretty much out of luck when it comes to per-file-overrides. There is no setting exposed for this (no prettier.overrides
or equivalent), and the extension does not support nesting prettier settings below file specifiers. The only workaround would be to just use a different formatter for situations where you want to avoid Prettier touching the files. Example:
{
"prettier.enable": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[json]": {
"editor.defaultFormatter": "vscode.json-language-features"
}
}
And/or disable editor.formatOnSave
if that is what is why you are trying to override the Prettier extension.
Prettier - Troubleshooting
- Actually getting Prettier to work
- Make sure you have setup a config (see config)
- Make sure you have IDE extension installed and enabled (at least for workspace)
- Make sure you have dependencies installed
npm install --global prettier
yarn global add prettier
- Or, manage with repo
package.json
file
- Getting the linters to kick-in
- You might have noticed that, even with the Prettier IDE extension and dependency installed, you still don't get inline problems reported
- Prettier stopped shipping with a built-in linter, so you always need to install a linter and have prettier use it
- See my own notes above on Prettier + ESLint
- See Docs: Integrating with Linters and prettier-vscode Docs -> Linter Integration
- Still having issues? Try (assuming using ESLint):
- Checking the prettier output panel in VSCode
- Checking the eslint output panel in VSCode
- Make sure you have all dependencies and peer-dependencies installed for Prettier + ESLint configs
- Make sure you have
eslint-plugin-prettier
installed and configured correctly in your eslint config.- For example, usually you want
"prettier"
in theplugins
array, andplugin:prettier/recommended
(which requires theeslint-config-prettier
dependency) in theextends
array. - See the eslint-plugin-prettier docs for detailed instructions
- For example, usually you want
- Last resort: If you are using
eslint
, try modifying the VSCode setting:eslint.validate: ["javascript", ...]
to make sure your language is there
- Prettier is not respecting config settings
- Check for typos in your config settings (e.g., accidentally using
usetabs
instead of the correctuseTabs
) - Check that you don't have a local config file that is overriding another (e.g., a
.pretterrc
overriding a VSCode setting, or vice-versa) - If you are in a multi-project directory and/or with nested project(s), make sure that the config file is at the right level.
- If you are using Prettier as a VSCode extension, in order to get it to work properly with a nested setup, you might need to create a workspace, which has both the root folder open, as well as the project sub-folder
- Check for typos in your config settings (e.g., accidentally using
- When running via CLI, getting a "No files matching the pattern "___" were found"
- Usually a globbing issue: make sure that you are passing the glob as a quoted string, so that Prettier takes care of parsing it, instead of your shell.
- E.g., use
prettier --check "**/*.{js,html}"
, instead ofprettier --check **/*.{js,html}
- E.g., use
- Usually a globbing issue: make sure that you are passing the glob as a quoted string, so that Prettier takes care of parsing it, instead of your shell.
- When running via CLI, Prettier complains with "code style issues found in the above file(s)", but won't tell you what the issues are
- This is by design
- Ideally, your IDE should be showing you this info; if not, run through docs + troubleshooting notes above
- You can use the
--write
option with Prettier to have it write the changes it things you should make to the file
- Prettier is parsing with the wrong language (e.g. trying to parse and format SCSS as TypeScript)
- Check that someone hasn't manually overridden the automatic parser detector by specifying the
parser
option manually
- Check that someone hasn't manually overridden the automatic parser detector by specifying the
Prettier - Other Resources and Guides
- Testing: Checkout the prettier playground
- Config options: prettier options
This is a really good comprehensive guide on using Prettier with VSCode, including with CI steps.
This is another good guide on ESLint + Prettier.
MDN even has a guide on ESLint + Prettier.
This guide, covers ESLint + Prettier, plus how you can combine it with the AirBnB style rules.
Prettier - Disabling
Main docs - "Ignoring Code"
- Whole files
.prettierignore
file--ignore-path
CLI option
- Section of code
- JS / TS:
// prettier-ignore
- JSX:
{/* prettier-ignore */}
- HTML:
<!-- prettier-ignore -->
- CSS:
/* prettier-ignore */
- Markdown:
<!-- prettier-ignore -->
- GraphQL:
# prettier-ignore
- Handlebars:
{{! prettier-ignore }}
- JS / TS:
💡 - Tip: You can use re-use your
.gitignore
as Prettier's ignore rule set by using the--ignore-path
😞 - Prettier's ignore defaults and config options are somewhat weak, and there are current requests to improve them.
JSON Schema
Moved to new page: JSON Schema.
Bundlers
See separate cheatsheet - Bundlers