.eslint-plugin-local/README.md
We use a set of custom ESLint to enforce repo specific coding rules and styles. These custom rules are run in addition to many standard ESLint rules we enable in the project. Some example custom rules includes:
test.only(...)vscode.d.tsCustom rules are mostly used for enforcing or banning certain coding patterns. We tend to leave stylistic choices up to area owners unless there's a good reason to enforce something project wide.
This doc provides a brief overview of how these rules are setup and how you can add a new one.
Custom rules are defined in the .eslint-plugin-local folder. Each rule is defined in its own TypeScript file. These follow the naming convention:
code-RULE-NAME.ts — General rules that apply to the entire repo.vscode-dts-RULE-NAME.ts — Rules that apply just to vscode.d.ts.These rules are then enabled in the eslint.config.js file. This is the main eslint configuration for our repo. It defines a set of file scopes which rules should apply to files in those scopes.
For example, here's a configuration that enables the no test.only rule in all *.test.ts files in the VS Code repo:
{
// Define which files these rules apply to
files: [
'**/*.test.ts'
],
languageOptions: { parser: tseslint.parser, },
plugins: {
'local': pluginLocal,
},
rules: {
// Enable the rule from .eslint-plugin-local/code-no-test-only.ts
'local/code-no-test-only': 'error',
}
}
This walks through the steps to create a new eslint rule:
Create a new rule file under .eslint-plugin-local. Generally you should call it code-YOUR-RULE-NAME.ts, for example, .eslint-plugin-local/code-no-not-null-assertions-on-undefined-values.ts
In this file, add the rule. Here's a template:
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as eslint from 'eslint';
export = new class YourRuleName implements eslint.Rule.RuleModule {
readonly meta: eslint.Rule.RuleMetaData = {
messages: {
customMessageName: 'message text shown in errors/warnings',
},
schema: false,
};
create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener {
return {
[SELECTOR]: (node: any) => {
// Report errors if needed
return context.report({
node,
messageId: 'customMessageName'
});
}
};
}
};
SELECTOR with the ESTree selector needed to target the nodes you are interested in. Use the TypeScript ESLint playground to figure out which nodes you need and debug selectorsRegister the rule in eslint.config.js
Generally this is just turning on the rule in the rule list like so:
rules: {
// Name should match file name
'local/code-no-not-null-assertions-on-undefined-values': 'warn',
...
}
Rules can also take custom arguments. For example, here's how we can pass arguments to a custom rule in the eslint.config.js:
rules: {
'local/code-no-not-null-assertions-on-undefined-values': ['warn', { testsOk: true }],
...
}
In these cases make sure to update the meta.schema property on your rule with the JSON schema for the arguments. You can access these arguments using context.options in the rule create function
Fixes are a useful way to mechanically fix basic linting issues, such as auto inserting semicolons. These fixes typically work at the AST level, so they are a more reliable way to perform bulk fixes compared to find/replaces.
To add a fix for a custom rule:
On the meta for your rule, add fixable: 'code'
When reporting an error in the rule, also include a fix. This is a function that takes a fixer argument and returns one or more fixes.
See the Double quoted to single quoted string covert fix for an example. The ESLint docs also have details on adding fixes and the fixer api
The fixes can be run using npx eslint --fix in the VS Code repo