Rush StackShopBlogEvents
Skip to main content

ESlint / TSLint plugins

Plugin package:@rushstack/heft-lint-plugin
Plugin name:lint-plugin implemented by LintPlugin.ts
Plugin config file:(none)
heft.json options:(none)

This plugin invokes the ESLint or TSLint linters, which check your code for stylistic issues and common mistakes.

When to use it

TSLint is deprecated (see below). We recommend to use ESLint for all projects, as part of the following combined approach to code validation:

  • Prettier: This tool manages trivial syntax aspects such as spaces, commas, and semicolons. Because these aspects normally don't affect code semantics, we never bother the developer with error messages about it, nor is it part of the build. Instead, Prettier reformats the code automatically via a git commit hook. To set this up, see the Enabling Prettier tutorial on the Rush website.

  • TypeScript: The TypeScript compiler performs sophisticated type checking and semantic analysis that is the most important safeguard for program correctness.

  • ESLint: The lint rules supplement the compiler's checks with additional stylistic rules that are more subjective and highly customizable. Whereas TypeScript might detect that "This function parameter is a string but was declared as a number", the linter might detect an issue such as "This class name should use PascalCase instead of camelCase." However operationally ESLint's validation is very similar to type checking, and some ESLint rules require TypeScript semantic analysis, which may depend on project-specific compiler configurations. Therefore we recommend to run ESLint as part of the build, not as a Git commit hook or global analysis.

  • API Extractor: This is an additional validation check for library packages only. It ensures their API contracts are well-formed and properly documented.

Although it's recommended to set up your build system in this way, Heft doesn't enforce a particular approach. Each of these components is optional, and other configurations are possible. For example, older code bases may need to use TSLint instead of ESLint.

package.json dependencies

You will need to add the eslint package to your project:

# If you are using Rush, run this shell command in your project folder:
rush add --package eslint --dev

If you're using a rig, the eslint dependency could be omitted and obtained via rig resolution. However, if you use the ESLint extension for VS Code, it will try to resolve the eslint package from your project folder. Thus it is still useful to add ESLint to your package.json file. (The extension is able to load a globally installed eslint package; however, its version may not match the version required by the local branch.)

If you are using a standard rig such as @rushstack/heft-node-rig or @rushstack/heft-web-rig, then @rushstack/heft-lint-plugin will already be loaded and configured.

Otherwise, you'll need to add the packages to your project:

# If you are using Rush, run this shell command in your project folder:
rush add --package @rushstack/heft-lint-plugin --dev


If the plugin is not already being provided by a rig, your heft.json config file could invoke it like in this example:

<project folder>/config/heft.json

"$schema": "",
. . .
"phasesByName": {
"build": {
"cleanFiles": [{ "sourcePath": "dist" }, { "sourcePath": "lib" }, { "sourcePath": "lib-commonjs" }],
"tasksByName": {
"typescript": {
"taskPlugin": {
"pluginPackage": "@rushstack/heft-typescript-plugin"
"lint": {
"taskDependencies": ["typescript"],
"taskPlugin": {
"pluginPackage": "@rushstack/heft-lint-plugin"
. . .

Heft only invokes the linter if it finds an ESLint config file. Although ESLint supports 7 different names/formats for this file, Heft requires it to be named ".eslintrc.js". This has a couple benefits:

  • Consistency: Using one standard name ".eslintrc.js" makes it easy to search for these files, perform bulk edits, and copy configuration recipes between projects.
  • Workarounds: Using the .js file extension enables JavaScript expressions in the file. This practice is generally discouraged because code expressions are harder to validate, and expressions can depend on environmental inputs that are invisible to caches. However, for historical reasons, ESLint's config file format has some limitations that can only be solved with scripts (for example using __dirname to resolve file paths).

It's not recommended to place a centralized .eslintrc.js in the monorepo root folder. This violates Rush's principle that projects should be independent and easily movable between monorepos.

Instead, each project should have its own .eslintrc.js file. We recommend to use the @rushstack/eslint-config shared configuration, which is specifically tailored for large scale monorepos, and based on the typescript-eslint parser and ruleset. If you need additional custom lint rules, it's recommended to create a custom NPM package that extends from @rushstack/eslint-config.

With this approach, a typical ESLint config file will have very minimal boilerplate. For example:

<project folder>/.eslintrc.js

// This is a workaround for

module.exports = {
extends: ['@rushstack/eslint-config/profile/node'],
parserOptions: { tsconfigRootDir: __dirname }

Profiles and mixins

The @rushstack/eslint-config package currently provides three different lint profiles. Choose one:

  • @rushstack/eslint-config/profile/node - for Node.js services
  • @rushstack/eslint-config/profile/node-trusted-tool - for Node.js tools
  • @rushstack/eslint-config/profile/web-app - for web browser applications

It also supports lint mixins. Add as many as you like:

  • @rushstack/eslint-config/mixins/react - if you use the React framework
  • @rushstack/eslint-config/mixins/friendly-locals - if you prefer more verbose declarations to make
  • @rushstack/eslint-config/mixins/tsdoc - if you are using API Extractor in your workspace

The @rushstack/eslint-config documentation explains these options in more detail.


The TSLint tool predates ESLint and is now deprecated, but may still be used in some older code bases.

The lint-plugin supports both tools: If <project folder>/tslint.json is found, then TSLint will be invoked. If both config files are present, then both TSLint and ESLint will be invoked.