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
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"]
      }
  • 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:
  • 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 your tsconfig file & project info

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
  • 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"]

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

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

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
    • 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 for typos in your config settings (e.g., accidentally using usetabs instead of the correct useTabs)
    • 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
  • 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 of prettier --check **/*.{js,html}
  • 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)

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

Moved to new page: JSON Schema.

Bundlers

See separate cheatsheet - Bundlers

Markdown Source Last Updated:
Sat Feb 18 2023 23:59:32 GMT+0000 (Coordinated Universal Time)
Markdown Source Created:
Wed Aug 21 2019 00:46:11 GMT+0000 (Coordinated Universal Time)
© 2024 Joshua Tzucker, Built with Gatsby
Feedback