Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement eslint.rules.customizations - with overrides #1164

Merged
merged 12 commits into from
Apr 6, 2021
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,29 @@ This extension contributes the following variables to the [settings](https://cod
- `all`: fixes all possible problems by revalidating the file's content. This executes the same code path as running eslint with the `--fix` option in the terminal and therefore can take some time. This is the default value.
- `problems`: fixes only the currently known fixable problems as long as their textual edits are non overlapping. This mode is a lot faster but very likely only fixes parts of the problems.

- `eslint.rules.customizations`: force rules to report a different severity within VS Code compared to the project's true ESLint configuration. This is an array that allows two kinds of glob patterns:
- `"override`": Overrides for rules with names that match, factoring in asterisks: `{ "override": "no-*", "severity": "warn" }`
- `"ignore"`: Excludes rules matching a glob from previous overrides: `{ "ignore": "*jsx*" }`

In this example, all rules are overridden to warnings:

```json
"eslint.rules.customizations": [
{ "override": "*", "severity": "warn" }
]
```

In this example, with the exception of `semi` rules and `radix`, all all `no-` rules are warnings and other rules are info:

```json
"eslint.rules.customizations": [
{ "override": "*", "severity": "warn" },
{ "override": "no-*", "severity": "info" },
{ "ignore": "*semi*" },
{ "ignore": "radix" }
]
```

- `eslint.format.enable` (@since 2.0.0): uses ESlint as a formatter for files that are validated by ESLint. If enabled please ensure to disable other formatters if you want to make this the default. A good way to do so is to add the following setting `"[javascript]": { "editor.defaultFormatter": "dbaeumer.vscode-eslint" }` for JavaScript. For TypeScript you need to add `"[typescript]": { "editor.defaultFormatter": "dbaeumer.vscode-eslint" }`.
- `eslint.onIgnoredFiles` (@since 2.0.10): used to control whether warnings should be generated when trying to lint ignored files. Default is `off`. Can be set to `warn`.
- `editor.codeActionsOnSave` (@since 2.0.0): this setting now supports an entry `source.fixAll.eslint`. If set to true all auto-fixable ESLint errors from all plugins will be fixed on save. You can also selectively enable and disabled specific languages using VS Code's language scoped settings. To disable `codeActionsOnSave` for HTML files use the following setting:
Expand Down
19 changes: 19 additions & 0 deletions client/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,23 @@ enum ConfirmationSelection {
alwaysAllow = 4
}

enum RuleSeverity {
info = 'info',
warn = 'warn',
error = 'error'
}

type RuleCustomization = RuleIgnore | RuleOverride;

interface RuleIgnore {
ignore: string;
}

interface RuleOverride {
override: string;
severity: RuleSeverity;
}

interface ConfigurationSettings {
validate: Validate;
packageManager: 'npm' | 'yarn' | 'pnpm';
Expand All @@ -190,6 +207,7 @@ interface ConfigurationSettings {
quiet: boolean;
onIgnoredFiles: ESLintSeverity;
options: any | undefined;
rulesCustomizations: RuleCustomization[];
run: RunValues;
nodePath: string | null;
workspaceFolder: WorkspaceFolder | undefined;
Expand Down Expand Up @@ -1441,6 +1459,7 @@ function realActivate(context: ExtensionContext): void {
quiet: config.get('quiet', false),
onIgnoredFiles: ESLintSeverity.from(config.get<string>('onIgnoredFiles', ESLintSeverity.off)),
options: config.get('options', {}),
rulesCustomizations: config.get('rules.customizations', []),
run: config.get('run', 'onType'),
nodePath: config.get('nodePath', null),
workingDirectory: undefined,
Expand Down
44 changes: 44 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,50 @@
"type": "boolean",
"default": false,
"description": "Enables ESLint as a formatter."
},
"eslint.rules.customizations": {
"description": "Override the severity of one or more rules reported by this extension, regardless of the project's ESLint config. Use globs to apply default severities for multiple rules.",
"items": {
"properties": {
"anyOf": [
{
"properties": {
"ignore": {
"type": "string"
}
},
"type": "object"
},
{
"properties": {
"severity": {
"type": "string",
"enum": [
"off",
"messages",
"verbose"
]
},
"rule": {
"type": "string"
}
},
"type": "object"
}
]
},
"type": "object"
},
"scope": "resource",
"type": "array",
"additionalProperties": {
"type": "string",
"enum": [
"info",
"warn",
"error"
]
}
}
}
},
Expand Down
7 changes: 7 additions & 0 deletions playgrounds/6.0/.vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@
"location": "separateLine"
},
"eslint.quiet": false,
"eslint.rules.customizations": [
{ "override": "*", "severity": "info" },
{ "override": "no-*", "severity": "warn" },
{ "ignore": "*console*" },
{ "ignore": "*semi*" },
{ "ignore": "radix" }
],
"editor.codeActionsOnSaveTimeout": 2000,
"eslint.format.enable": true,
"eslint.onIgnoredFiles": "off",
Expand Down
53 changes: 51 additions & 2 deletions server/src/eslintServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,23 @@ interface CodeActionsOnSaveSettings {
mode: CodeActionsOnSaveMode
}

enum RuleSeverity {
info = 'info',
warn = 'warn',
error = 'error'
}

type RuleCustomization = RuleIgnore | RuleOverride;

interface RuleIgnore {
ignore: string;
}

interface RuleOverride {
override: string;
severity: RuleSeverity;
}

interface CommonSettings {
validate: Validate;
packageManager: 'npm' | 'yarn' | 'pnpm';
Expand All @@ -233,6 +250,7 @@ interface CommonSettings {
quiet: boolean;
onIgnoredFiles: ESLintSeverity;
options: ESLintOptions | undefined;
rulesCustomizations: RuleCustomization[];
run: RunValues;
nodePath: string | null;
workspaceFolder: WorkspaceFolder | undefined;
Expand Down Expand Up @@ -368,7 +386,27 @@ function loadNodeModule<T>(moduleName: string): T | undefined {
return undefined;
}

function makeDiagnostic(problem: ESLintProblem): Diagnostic {
function asteriskMatches(matcher: string, ruleId: string) {
return new RegExp(`^${matcher.replace(/\*/g, '.*')}$`, 'g').test(ruleId);
}

function getSeverityOverride(ruleId: string, customizations: RuleCustomization[]) {
let result: RuleSeverity | undefined;

for (const customization of customizations) {
if ('ignore' in customization) {
if (asteriskMatches(customization.ignore, ruleId)) {
result = undefined;
}
} else if (asteriskMatches(customization.override, ruleId)) {
result = customization.severity;
}
}

return result;
}

function makeDiagnostic(settings: TextDocumentSettings, problem: ESLintProblem): Diagnostic {
const message = problem.message;
const startLine = Is.nullOrUndefined(problem.line) ? 0 : Math.max(0, problem.line - 1);
const startChar = Is.nullOrUndefined(problem.column) ? 0 : Math.max(0, problem.column - 1);
Expand All @@ -395,6 +433,12 @@ function makeDiagnostic(problem: ESLintProblem): Diagnostic {
result.tags = [DiagnosticTag.Unnecessary];
}
}

const severityOverride = getSeverityOverride(problem.ruleId, settings.rulesCustomizations);
if (severityOverride) {
result.severity = convertSeverity(severityOverride);
}

return result;
}

Expand Down Expand Up @@ -465,13 +509,18 @@ function recordCodeAction(document: TextDocument, diagnostic: Diagnostic, proble
});
}

function convertSeverity(severity: number): DiagnosticSeverity {
function convertSeverity(severity: number | RuleSeverity): DiagnosticSeverity {
// RuleSeverity concerns an overridden rule. A number is direct from ESLint.
switch (severity) {
// Eslint 1 is warning
case 1:
case RuleSeverity.warn:
return DiagnosticSeverity.Warning;
case 2:
case RuleSeverity.error:
return DiagnosticSeverity.Error;
case RuleSeverity.info:
return DiagnosticSeverity.Information;
default:
return DiagnosticSeverity.Error;
}
Expand Down