Welcome to NI's JavaScript and TypeScript linter rules for ESLint.
Install the package for your corresponding language as a development dependency:
-
For JavaScript run:
npm install -D @ni/eslint-config-javascript
Then follow the JavaScript configuration instructions.
-
For TypeScript run:
npm install -D @ni/eslint-config-typescript
Then follow the TypeScript configuration instructions.
-
For Angular run:
npm install -D @ni/eslint-config-angular
Then follow the Angular configuration instructions.
-
For Playwright run:
npm install -D @ni/eslint-config-playwright
Then follow the Playwright configuration instructions.
After installing the lint configuration packages, determine if the project is using CommonJS or ES modules by default. ES modules will specify "type": "module" in package.json. The following configuration examples are ES modules. Use the file extension mjs for ES modules in CommonJS projects, e.g. eslint.config.mjs will be common for Angular.
With flat configuration, the files declaration must use a glob to match files in subdirectories. See Glob-Based Configs.
Follow the instructions for your project's language:
Use @ni/eslint-config-javascript configurations in your ESLint flat configuration (eslint.config.js):
import { defineConfig } from 'eslint/config';
import { javascriptConfig } from '@ni/eslint-config-javascript';
export default defineConfig([
javascriptConfig,
]);Use @ni/eslint-config-typescript configurations in the ESLint flat configuration (eslint.config.js). Set the languageOptions.parserOptions.project to the project's TypeScript configuration to correctly enable linting with type information.
import { defineConfig } from 'eslint/config';
import { typescriptConfig } from '@ni/eslint-config-typescript';
export default defineConfig([
{
files: ['**/*.ts'],
extends: typescriptConfig,
languageOptions: {
parserOptions: {
project: ['./tsconfig.json'],
tsconfigRootDir: import.meta.dirname,
},
},
},
]);ESLint support for Angular is provided by angular-eslint.
- For single and multi-project workspaces, add angular-eslint.
ng add angular-eslint
- Remove the
angular-eslint,typescript-eslint, andeslintdevelopment dependencies frompackage.json. - For multi-project workspaces, configure each project, and then enable future generated projects to be configured as well.
> ng g angular-eslint:add-eslint-to-project <PROJECT NAME> > ng config cli.schematicCollections "[\"angular-eslint\"]"
- Replace
eslint.config.jswith the followingeslint.config.mjsflat configuration of rules for Typescript, Angular templates, and JavaScript.import { defineConfig } from 'eslint/config'; import { angularTypescriptConfig, angularTemplateConfig } from '@ni/eslint-config-angular'; import { javascriptConfig, importNodeEsmConfig } from '@ni/eslint-config-javascript'; export default defineConfig([ { // JavaScript rules fail to parse the HTML files that are added below. Therefore, the JavaScript // configuration must now match the correct files to avoid an error. files: ['**/*.js', '**/*.mjs'], extends: [javascriptConfig, importNodeEsmConfig] }, { files: ['**/*.ts'], extends: angularTypescriptConfig, languageOptions: { parserOptions: { // The `languageOptions.parserOptions.projectService` option is recommended but does not identify // tsconfig.*.json files. Use the older `project` configuration instead. The general `tsconfig.json` is // declared last in the hope that the application and test configurations will apply to their // applicable files correctly. // https://typescript-eslint.io/troubleshooting/typed-linting/#project-service-issues project: ['tsconfig.app.json', 'tsconfig.spec.json', 'tsconfig.json'] // In projects (e.g. libraries) using `parserOptions.project`, Angular requires that the paths be // relative to the root, but the VSCode extension requires them to be relative to the project // directory. Set the root to be the project directory to satisfy both. The `parserOptions.projectService` // configuration would likely resolve this, but is not used for reasons described above. // https://typescript-eslint.io/blog/project-service // tsconfigRootDir: import.meta.dirname } } }, { files: ['**/*.html'], extends: angularTemplateConfig } ]);
- Update the
lintFilePatternsinangular.jsonto include.ts,.html, and.js, and.mjsfiles.
"lintFilePatterns": [
"src/**/*.ts",
"src/**/*.js",
"src/**/*.mjs",
"src/**/*.html"
]- Evaluate the project specific rule groups to manually add to your lint configuration. For Angular applications in particular, consider enabling the
[application-prefix]rule group. - With flat configurations, configurations for subdirectories must be defined at the root except for projects which are already root configurations. ESLint loads only one flat configuration resolved relative to the working directory (e.g. root). Support for defining configurations in subdirectories is experimental behind an active feature flag. The angular-eslint builder does not support this feature flag. See ESLint issue #18385.
Use @ni/eslint-config-playwright configurations in the ESLint flat configuration (eslint.config.js). Set the parserOptions.project to the project's TypeScript configuration to correctly enable linting with type information.
import { defineConfig } from 'eslint/config';
import { playwrightConfig } from '@ni/eslint-config-playwright';
export default defineConfig([
{
// This files pattern should be updated to only match
// Playwright test files and not other TypeScript files
files: ['**/*.ts'],
extends: playwrightConfig,
languageOptions: {
parserOptions: {
project: ['./tsconfig.json'],
tsconfigRootDir: import.meta.dirname,
},
}
}
]);After following the above steps to install and configure the linter, you should be able to run it from the command line using npx eslint .
To avoid developers needing to remember tooling-specific commands, each project should add standard aliases to its package.json:
{
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix",
}
}This allows developers to lint using npm run lint and to run the automatic fixer using npm run lint:fix.
Each project's pull request build pipeline should ensure no lint errors can be committed to the repository. Invoke npm run lint from your GitHub Actions or Azure Pipelines YML after installing dependencies and building your application (not shown in the examples).
Ensure npm is present on the agent then run the lint command.
jobs:
build:
steps:
- uses: actions/setup-node@v1
with:
node-version: '16'
- run: npm run lintUse the npm task to run the lint command.
- task: Npm@1
displayName: 'Lint'
inputs:
command: custom
customCommand: 'run lint'New projects should turn on linting before writing any code. It's easier to fix violations as developers add new code than it is to fix large numbers of lint errors across an existing codebase.
Existing projects are likely to have numerous violations even if they already used a different linter (for example, the deprecated TSLint) as this ruleset is more strict than most. The recommended flow for adopting this ruleset in an existing repository is:
- Install the tooling as described above.
- Disable existing lint tooling.
- Fix as many simple violations as possible by running the automated fixer and doing targeted manual fixes.
- If necessary, suppress the remaining violations but fix them as soon as possible in follow up submissions.
Typically steps 1-3 will happen in a single pull request (or a few in quick succession) while step 4 will be split across many subsequent submissions as time permits.
Several sets of rules may be enabled based on requirements of a given project. By default the following sets of rules are in an inert / disabled state, but should be evaluated for your integration.
Text search for the tag associated with a specific rule group in the repository to find the related rules. If enabling a rule group, the rules should be toggled from 'off' to 'error' unless the rule comment says otherwise.
Tag: [application-prefix]
Prefixes are generally added to named objects such as the selector for Components in Angular applications. Projects should consider enabling this rule group so that names can be consistently prefixed making them easier to share between applications and to minimize the chance of conflicts when using shared libraries.
Tag: [strict-null-checks]
When strictNullChecks are enabled the values null and undefined are treated as distinct types by the compiler. For example, with strictNullChecks enabled, the value null could not be directly assigned to a binding of a Cat object, ie const cat: Cat = null would be a compile error. The null value is a distinct type and the binding would have to explicitly state that it can have a null value, ie const cat: Cat | null = null;.
strictNullChecks are a powerful tool for code correctness and give us a way to avoid "The Billion Dollar Mistake".
This style guide assumes strictNullChecks are enabled by default. However, it can be impractical to retrofit strictNullChecks configuration into an existing application and requires expanding your mental model for software development for use in new applications. To adopt this style guide in a project without strictNullChecks, configure every rule tagged with [strict-null-checks] to off unless specified in the rule comment.
Tag: [accessibility]
There currently isn't an NI organization wide requirement to enforce accessibility in applications. The rule group should be enabled if individual applications prioritize accessibility.
A project should strive to adopt this configuration as fully as possible, but there are valid reasons to disable a rule across a codebase or specific directory:
- As a temporary measure to stage adoption of the tooling.
- As a permanent measure if the rule is incompatible with a project's configuration. The rule configuration files in each package (
packages/eslint-config-*/index.js) contain comments on each rule if it might commonly be disabled. Some examples include:- consider disabling
@angular-eslint/template/i18nif a project will never be localized - consider disabling
func-namesin older JavaScript projects that make use of immediately-invoked function expressions (IIFEs) where providing a name is not useful
- consider disabling
- As a permanent measure for an existing codebase if the Technical Lead determines it is too costly to fix the violations of that rule.
Each disabled rule (or group of similar rules) should include a comment explaining why it is disabled.
To disable a rule globally, modify the rules section of the ESLint configuration:
rules: {
// This rule is disabled as an example
'import/prefer-default-export': 'off'
}To disable a rule for a specific file pattern or directory, update the rules section for that file pattern in the ESLint configuration:
import { defineConfig } from 'eslint/config';
export default defineConfig([
// ...other configs
{
// This rule is disabled as an example
files: ['*.stories.ts'],
rules: {
'import/no-default-export': 'off'
}
}
]);A project should strive to adopt this configuration as fully as possible, but there are valid reasons to disable a rule (also called suppressing a violation) for a specific line, block, or file of code.
The rule configuration files in this package (index.js, typescript.js, etc) contain comments on each rule if it might commonly be disabled.
ESLint offers several ways to disable a rule for a line or file. Suppressions should be as targeted as possible and should include a comment explaining the suppression.
ESLint’s flat config format is now the recommended way to configure ESLint. If your project is still using a legacy .eslintrc.* file, follow these steps to migrate:
-
Rename your configuration file Replace your
.eslintrc.jsor.eslintrc.jsonwith a neweslint.config.jsfile at the root of your project. -
Switch to using imports Instead of using the
extendsproperty, import the configuration packages you need and export an array of configurations:// eslint.config.js import { defineConfig } from 'eslint/config'; import { angularTypescriptConfig, angularTemplateConfig } from '@ni/eslint-config-angular'; export default defineConfig([ angularTypescriptConfig, angularTemplateConfig, // Add any project-specific overrides here ]);
Note: All rules that previously required type checking are now included in the main config export for each package. You no longer need to import a separate requiring-type-checking config—just import the main config to get all rules.
-
Set parser options as needed For TypeScript and Angular projects, ensure you set
parserOptions.projectin a config block to point to your TypeScript configuration:{ files: ['**/*.ts'], languageOptions: { parserOptions: { project: ['./tsconfig.json'], tsconfigRootDir: import.meta.dirname, }, } }
-
Remove legacy config fields The flat config format does not use
parser, orpluginsat the top level. All configuration should be handled through the imported arrays and objects. -
@stylistic rules The following rules are moved from
@typescript-eslintto@stylistic. Update any project config overrides and inline suppressions to the new names:@typescript-eslint/member-delimiter-style→@stylistic/member-delimiter-style@typescript-eslint/type-annotation-spacing→@stylistic/type-annotation-spacing
Other deprecated rules may also now reside under
@stylistic. Search your codebase for old suppressions (e.g.eslint-disable @typescript-eslint/...) and rename them as needed. -
Angular CLI linkage Configure the linter in
angular.jsonfor each project in Angular workspaces to use theeslint.config.jsESLint configuration. Example:"projects": { "my-app": { "architect": { "lint": { "builder": "@angular-eslint/builder:lint", "options": { "lintFilePatterns": ["src/**/*.ts", "src/**/*.html"], "eslintConfig": "./eslint.config.js" } } } } }
-
strictNullChecks is now enabled by default.
- If your project has
strictNullChecksenabled, remove previously overridden strict rules for null checks. - If your project has
strictNullChecksdisabled, disable strict rules for null checks to maintain previous behavior. Example:{ files: ['**/*.ts'], rules: { '@typescript-eslint/no-unnecessary-condition': 'off', '@typescript-eslint/strict-boolean-expressions': 'off' } }
- If your project has
Modern IDEs can be configured to provide live feedback about ESLint errors.
Install the ESLint Extension.
You can configure a repository to prompt developers to install this extension by adding a file called .vscode/extensions.json with the following contents:
{
"recommendations": [
"dbaeumer.vscode-eslint",
]
}Follow the angular-eslint instructions for linting HTML files and inline-templates with Angular.
Follow the instructions in the WebStorm documentation to activate and configure ESLint automatically in the Settings ≫ Preferences dialog.
Increase the heap allocation using the max_old_space_size option.
node --max_old_space_size=8196 ./node_modules/eslint/bin/eslintThis option can be adapted for npm scripts, for example.
"ng": "node --max_old_space_size=8196 ./node_modules/@angular/cli/bin/ng",
"lint": "npm run ng -- lint"@ni/eslint-config-typescript includes rules that require type checking that run slower as they utilize the TypeScript compiler for type information.
If there are situations where the analysis time for enabling the type checked rules is an excessive burden you may consider creating a separate ESLint configuration that avoids extending the type checked rules and omits the parserOptions.project configuration to run in specific scenarios.
See discussion in the performance section of the Getting Started - Linting with Type Information guide.
Deviations from the angular-eslint, @ni/eslint-config-angular, and the parserOptions.project configurations can result in significant performance degredation. Read angular-eslint's section on performance for information on addressing slow linting processes.
