Joshua's Docs - JavaScript DevOps - Cheatsheet, Notes, Misc.

💡 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 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": "",
	"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

  • 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/eslint-plugin"]
  • You might need to explicitly add TypeScript as a language for ESlint to validate:

    • In .vscode/settings.json -> "eslint.validate": ["javascript", "javascriptreact", "typescript"]
  • 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"}
  • If you use import or exports, turn on module support:

    • parserOptions: {"sourceType": "module"}

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:

ESLint - Misc troubleshooting

  • Parsing error: The keyword '___' is reserved (where keyword is a native JS thing like const or import)

    • Make sure you have ecmaVersion set. For example:

      // .eslintrc.json
      	"extends": ["prettier"],
      	"parserOptions": {
      		"ecmaVersion": 2018
  • 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
  • Parsing error: Unexpected token <

    • Are you using React? If so, you need to take a few steps:

      1. Make sure eslint-plugin-react is installed (yarn add --dev eslint-plugin-react)
      2. Add plugin to eslint config: "plugins": ["react"]
      3. Add JSX support to eslint config: "parserOptions" -> "ecmaFeatures" -> "jsx": true
  • Something else / linting not kicking in

    • Very helpful: check what is showing up in the Output panel of VSCode, after selecting "ESLint" from the dropdown.

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": [

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) {

ESLint - AirBnB Config Setup

  1. 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.

  2. 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"]

ESLint + Prettier - Sample Workflow

  1. 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
  2. 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 the const keyword
      • "env": { "browser": true }
  3. Tweak defaults to desired

    • For formatting rules, you should really make changes to Prettier directly in Prettier config files, not ESLint

      1. touch .prettierrc
      2. 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
    • Pure ESLint changes can go under .eslintrc.json -> rules: {}
  4. 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
  5. 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 that eslint --fix will actually use Prettier for formatting if you have followed the above steps correctly. This is preferred over prettier --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:

        1. yarn add --dev husky
        2. package.json:

          "husky": {
          	"hooks": {
          		// call your lint and format script entries below
          		"pre-commit": "___"
    • 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

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 - Config file


  • .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

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

    • 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 the plugins array, and plugin:prettier/recommended (which requires the eslint-config-prettier dependency) in the extends array.
        • See the eslint-plugin-prettier docs for detailed instructions
    • 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 that you don't have a local config file that is overriding another (e.g., a .pretterrc overriding a VSCode setting)
    • 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

Prettier - Other Resources and Guides

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 }}

💡 - 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

Ever wondered how VSCode provides auto-complete and intellisense for standard config files, like tsconfig.json, or if you have the right extensions installed, .eslintrc.json, .prettierrc, etc.? Welcome to the magical world of JSON Schema!

The TLDR of this is that JSON Schema is a standardized way to write (with JSON) a specification for how a certain JSON file type should always look. It is powerful in that it can be used for both autocomplete and intellisense (providing hints to the user on what certain fields are for), as well as validation.


JSON Schema Resolver in VSCode

There are multiple ways in which VSCode resolves which Schema to apply against an open JSON file - the main docs covering this are here. To summarize though, the main options are:

  • In the file itself, via the $schema attribute (path)
  • In VSCode settings.json, under json.schemas:

    • json.schemas: Array<{ fileMatch: Array<fileGlobs>, url: schemaPath }>
  • Through a VSCode extension, by contributing through jsonValidation:

    • jsonValidation: Array<{ fileMatch: fileGlob, url: schemaPath }>

Note: For the $schema or url value, the path to the schema file does not have to be local; many extensions and plugins use a public URL, such as

Here are some practical examples:

  • Here is how the vscode-eslint extension contributes schemas

    • This is why .eslintrc has intellisense if the extension is installed
    • Here is the schema that gets merged with NPM for package.json - specifically for the eslintConfig key
  • Here is a similar contribution from prettier-vscode


See separate cheatsheet - Bundlers

Markdown Source Last Updated:
Mon Jan 18 2021 12:33:26 GMT+0000 (Coordinated Universal Time)
Markdown Source Created:
Wed Aug 21 2019 00:46:11 GMT+0000 (Coordinated Universal Time)
© 2021 Joshua Tzucker, Built with Gatsby