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

fix(eslint-plugin): [ban-ts-comment] support block comments #2644

Merged
merged 12 commits into from Oct 12, 2020
2 changes: 1 addition & 1 deletion packages/eslint-plugin/README.md
Expand Up @@ -100,7 +100,7 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int
| [`@typescript-eslint/adjacent-overload-signatures`](./docs/rules/adjacent-overload-signatures.md) | Require that member overloads be consecutive | :heavy_check_mark: | | |
| [`@typescript-eslint/array-type`](./docs/rules/array-type.md) | Requires using either `T[]` or `Array<T>` for arrays | | :wrench: | |
| [`@typescript-eslint/await-thenable`](./docs/rules/await-thenable.md) | Disallows awaiting a value that is not a Thenable | :heavy_check_mark: | | :thought_balloon: |
| [`@typescript-eslint/ban-ts-comment`](./docs/rules/ban-ts-comment.md) | Bans `// @ts-<directive>` comments from being used or requires descriptions after directive | :heavy_check_mark: | | |
| [`@typescript-eslint/ban-ts-comment`](./docs/rules/ban-ts-comment.md) | Bans `@ts-<directive>` comments from being used or requires descriptions after directive | :heavy_check_mark: | | |
| [`@typescript-eslint/ban-tslint-comment`](./docs/rules/ban-tslint-comment.md) | Bans `// tslint:<rule-flag>` comments from being used | | :wrench: | |
| [`@typescript-eslint/ban-types`](./docs/rules/ban-types.md) | Bans specific types from being used | :heavy_check_mark: | :wrench: | |
| [`@typescript-eslint/class-literal-property-style`](./docs/rules/class-literal-property-style.md) | Ensures that literals on classes are exposed in a consistent style | | :wrench: | |
Expand Down
24 changes: 20 additions & 4 deletions packages/eslint-plugin/docs/rules/ban-ts-comment.md
@@ -1,4 +1,4 @@
# Bans `// @ts-<directive>` comments from being used or requires descriptions after directive (`ban-ts-comment`)
# Bans `@ts-<directive>` comments from being used or requires descriptions after directive (`ban-ts-comment`)

TypeScript provides several directive comments that can be used to alter how it processes files.
Using these to suppress TypeScript Compiler Errors reduces the effectiveness of TypeScript overall.
Expand Down Expand Up @@ -41,13 +41,19 @@ const defaultOptions: Options = {

A value of `true` for a particular directive means that this rule will report if it finds any usage of said directive.

For example, with the defaults above the following patterns are considered warnings:
For example, with the defaults above the following patterns are considered warnings for single line or comment block lines:

```ts
if (false) {
// @ts-ignore: Unreachable code error
console.log('hello');
}
if (false) {
/*
@ts-ignore: Unreachable code error
*/
console.log('hello');
}
```

The following patterns are not warnings:
Expand All @@ -63,22 +69,32 @@ if (false) {

A value of `'allow-with-description'` for a particular directive means that this rule will report if it finds a directive that does not have a description following the directive (on the same line).

For example, with `{ 'ts-expect-error': 'allow-with-description' }` the following pattern is considered a warning:
For example, with `{ 'ts-expect-error': 'allow-with-description' }` the following patterns are considered a warning:

```ts
if (false) {
// @ts-expect-error
console.log('hello');
}
if (false) {
/* @ts-expect-error */
console.log('hello');
}
```

The following pattern is not a warning:
The following patterns are not a warning:

```ts
if (false) {
// @ts-expect-error: Unreachable code error
console.log('hello');
}
if (false) {
/*
@ts-expect-error: Unreachable code error
*/
console.log('hello');
}
```

### `minimumDescriptionLength`
Expand Down
17 changes: 9 additions & 8 deletions packages/eslint-plugin/src/rules/ban-ts-comment.ts
Expand Up @@ -21,15 +21,15 @@ export default util.createRule<[Options], MessageIds>({
type: 'problem',
docs: {
description:
'Bans `// @ts-<directive>` comments from being used or requires descriptions after directive',
'Bans `@ts-<directive>` comments from being used or requires descriptions after directive',
category: 'Best Practices',
recommended: 'error',
},
messages: {
tsDirectiveComment:
'Do not use "// @ts-{{directive}}" because it alters compilation errors.',
'Do not use "@ts-{{directive}}" because it alters compilation errors.',
tsDirectiveCommentRequiresDescription:
'Include a description after the "// @ts-{{directive}}" directive to explain why the @ts-{{directive}} is necessary. The description must be {{minimumDescriptionLength}} characters or longer.',
'Include a description after the "@ts-{{directive}}" directive to explain why the @ts-{{directive}} is necessary. The description must be {{minimumDescriptionLength}} characters or longer.',
},
schema: [
{
Expand Down Expand Up @@ -98,20 +98,21 @@ export default util.createRule<[Options], MessageIds>({
},
],
create(context, [options]) {
const tsCommentRegExp = /^\/*\s*@ts-(expect-error|ignore|check|nocheck)(.*)/;
const commentDirectiveRegExSingleLine = /^\/*\s*@ts-(expect-error|ignore|check|nocheck)(.*)/;
const commentDirectiveRegExMultiLine = /^\s*(?:\/|\*)*\s*@ts-(expect-error|ignore|check|nocheck)(.*)/;
dopecodez marked this conversation as resolved.
Show resolved Hide resolved
const sourceCode = context.getSourceCode();

return {
Program(): void {
const comments = sourceCode.getAllComments();

comments.forEach(comment => {
let regExp = commentDirectiveRegExSingleLine;

if (comment.type !== AST_TOKEN_TYPES.Line) {
return;
regExp = commentDirectiveRegExMultiLine;
}

const [, directive, description] =
tsCommentRegExp.exec(comment.value) ?? [];
const [, directive, description] = regExp.exec(comment.value) ?? [];

const fullDirective = `ts-${directive}` as keyof Options;

Expand Down
208 changes: 188 additions & 20 deletions packages/eslint-plugin/tests/rules/ban-ts-comment.test.ts
Expand Up @@ -8,11 +8,9 @@ const ruleTester = new RuleTester({
ruleTester.run('ts-expect-error', rule, {
valid: [
'// just a comment containing @ts-expect-error somewhere',
'/* @ts-expect-error */',
'/** @ts-expect-error */',
`
/*
// @ts-expect-error in a block
// @ts-expect-error running with long description in a block
*/
`,
{
Expand Down Expand Up @@ -50,6 +48,46 @@ ruleTester.run('ts-expect-error', rule, {
},
],
},
{
code: '/* @ts-expect-error */',
options: [{ 'ts-expect-error': true }],
errors: [
{
data: { directive: 'expect-error' },
messageId: 'tsDirectiveComment',
line: 1,
column: 1,
},
],
},
{
code: `
/*
@ts-expect-error
*/
`,
options: [{ 'ts-expect-error': true }],
errors: [
{
data: { directive: 'expect-error' },
messageId: 'tsDirectiveComment',
line: 2,
column: 1,
},
],
},
{
code: '/** @ts-expect-error */',
options: [{ 'ts-expect-error': true }],
errors: [
{
data: { directive: 'expect-error' },
messageId: 'tsDirectiveComment',
line: 1,
column: 1,
},
],
},
{
code: '// @ts-expect-error: Suppress next line',
options: [{ 'ts-expect-error': true }],
Expand Down Expand Up @@ -130,13 +168,6 @@ if (false) {
ruleTester.run('ts-ignore', rule, {
valid: [
'// just a comment containing @ts-ignore somewhere',
'/* @ts-ignore */',
'/** @ts-ignore */',
`
/*
// @ts-ignore in a block
*/
`,
{
code: '// @ts-ignore',
options: [{ 'ts-ignore': false }],
Expand All @@ -146,6 +177,19 @@ ruleTester.run('ts-ignore', rule, {
'// @ts-ignore I think that I am exempted from any need to follow the rules!',
options: [{ 'ts-ignore': 'allow-with-description' }],
},
{
code: `
/*
// @ts-ignore running with long description in a block
dopecodez marked this conversation as resolved.
Show resolved Hide resolved
*/
`,
options: [
{
'ts-ignore': 'allow-with-description',
minimumDescriptionLength: 21,
},
],
},
],
invalid: [
{
Expand All @@ -171,6 +215,46 @@ ruleTester.run('ts-ignore', rule, {
},
],
},
{
code: '/* @ts-ignore */',
options: [{ 'ts-ignore': true }],
errors: [
{
data: { directive: 'ignore' },
messageId: 'tsDirectiveComment',
line: 1,
column: 1,
},
],
},
{
code: `
/*
// @ts-ignore
*/
`,
options: [{ 'ts-ignore': true }],
errors: [
{
data: { directive: 'ignore' },
messageId: 'tsDirectiveComment',
line: 2,
column: 1,
},
],
},
{
code: '/** @ts-ignore */',
options: [{ 'ts-ignore': true }],
errors: [
{
data: { directive: 'ignore' },
messageId: 'tsDirectiveComment',
line: 1,
column: 1,
},
],
},
{
code: '// @ts-ignore: Suppress next line',
errors: [
Expand Down Expand Up @@ -251,13 +335,6 @@ if (false) {
ruleTester.run('ts-nocheck', rule, {
valid: [
'// just a comment containing @ts-nocheck somewhere',
'/* @ts-nocheck */',
'/** @ts-nocheck */',
`
/*
// @ts-nocheck in a block
*/
`,
{
code: '// @ts-nocheck',
options: [{ 'ts-nocheck': false }],
Expand All @@ -267,6 +344,19 @@ ruleTester.run('ts-nocheck', rule, {
'// @ts-nocheck no doubt, people will put nonsense here from time to time just to get the rule to stop reporting, perhaps even long messages with other nonsense in them like other // @ts-nocheck or // @ts-ignore things',
options: [{ 'ts-nocheck': 'allow-with-description' }],
},
{
code: `
/*
// @ts-nocheck running with long description in a block
*/
`,
options: [
{
'ts-nocheck': 'allow-with-description',
minimumDescriptionLength: 21,
},
],
},
],
invalid: [
{
Expand All @@ -292,6 +382,46 @@ ruleTester.run('ts-nocheck', rule, {
},
],
},
{
code: '/* @ts-nocheck */',
options: [{ 'ts-nocheck': true }],
errors: [
{
data: { directive: 'nocheck' },
messageId: 'tsDirectiveComment',
line: 1,
column: 1,
},
],
},
{
code: `
/*
// @ts-nocheck
*/
`,
options: [{ 'ts-nocheck': true }],
errors: [
{
data: { directive: 'nocheck' },
messageId: 'tsDirectiveComment',
line: 2,
column: 1,
},
],
},
{
code: '/** @ts-nocheck */',
options: [{ 'ts-nocheck': true }],
errors: [
{
data: { directive: 'nocheck' },
messageId: 'tsDirectiveComment',
line: 1,
column: 1,
},
],
},
{
code: '// @ts-nocheck: Suppress next line',
errors: [
Expand Down Expand Up @@ -348,11 +478,9 @@ if (false) {
ruleTester.run('ts-check', rule, {
valid: [
'// just a comment containing @ts-check somewhere',
'/* @ts-check */',
'/** @ts-check */',
`
/*
// @ts-check in a block
// @ts-check running with long description in a block
*/
`,
{
Expand Down Expand Up @@ -380,6 +508,46 @@ ruleTester.run('ts-check', rule, {
},
],
},
{
code: '/* @ts-check */',
options: [{ 'ts-check': true }],
errors: [
{
data: { directive: 'check' },
messageId: 'tsDirectiveComment',
line: 1,
column: 1,
},
],
},
{
code: `
/*
// @ts-check
*/
`,
options: [{ 'ts-check': true }],
errors: [
{
data: { directive: 'check' },
messageId: 'tsDirectiveComment',
line: 2,
column: 1,
},
],
},
{
code: '/** @ts-check */',
options: [{ 'ts-check': true }],
errors: [
{
data: { directive: 'check' },
messageId: 'tsDirectiveComment',
line: 1,
column: 1,
},
],
},
{
code: '// @ts-check: Suppress next line',
options: [{ 'ts-check': true }],
Expand Down