From b3b6319187c7bafd624a987608c802c26b07a25d Mon Sep 17 00:00:00 2001 From: chbdetta Date: Wed, 29 Jun 2022 22:33:27 -0700 Subject: [PATCH 01/21] replicate eslint rule and add base test case --- .../src/rules/lines-around-comment.ts | 92 +++++++++++++++++++ .../src/util/getESLintCoreRule.ts | 1 + .../tests/rules/lines-around-comment.test.ts | 21 +++++ .../eslint-plugin/typings/eslint-rules.d.ts | 30 ++++++ 4 files changed, 144 insertions(+) create mode 100644 packages/eslint-plugin/src/rules/lines-around-comment.ts create mode 100644 packages/eslint-plugin/tests/rules/lines-around-comment.test.ts diff --git a/packages/eslint-plugin/src/rules/lines-around-comment.ts b/packages/eslint-plugin/src/rules/lines-around-comment.ts new file mode 100644 index 000000000000..3ab19b20762d --- /dev/null +++ b/packages/eslint-plugin/src/rules/lines-around-comment.ts @@ -0,0 +1,92 @@ +import * as util from '../util'; +import { getESLintCoreRule } from '../util/getESLintCoreRule'; + +const baseRule = getESLintCoreRule('lines-around-comment'); + +export type Options = util.InferOptionsTypeFromRule; +export type MessageIds = util.InferMessageIdsTypeFromRule; + +export default util.createRule({ + name: 'lines-around-comment', + meta: { + type: 'layout', + docs: { + description: 'Require empty lines around comments', + recommended: false, + extendsBaseRule: true, + }, + schema: { + type: 'array', + items: [ + { + type: 'object', + properties: { + beforeBlockComment: { + type: 'boolean', + default: true, + }, + afterBlockComment: { + type: 'boolean', + default: false, + }, + beforeLineComment: { + type: 'boolean', + default: false, + }, + afterLineComment: { + type: 'boolean', + default: false, + }, + allowBlockStart: { + type: 'boolean', + default: false, + }, + allowBlockEnd: { + type: 'boolean', + default: false, + }, + allowClassStart: { + type: 'boolean', + }, + allowClassEnd: { + type: 'boolean', + }, + allowObjectStart: { + type: 'boolean', + }, + allowObjectEnd: { + type: 'boolean', + }, + allowArrayStart: { + type: 'boolean', + }, + allowArrayEnd: { + type: 'boolean', + }, + ignorePattern: { + type: 'string', + }, + applyDefaultIgnorePatterns: { + type: 'boolean', + }, + }, + additionalProperties: false, + }, + ], + }, + fixable: baseRule.meta.fixable, + hasSuggestions: baseRule.meta.hasSuggestions, + messages: baseRule.meta.messages, + }, + defaultOptions: [ + { + beforeBlockComment: true, + }, + ], + create(context, [options]) { + const rules = baseRule.create(context); + return { + ...rules, + }; + }, +}); diff --git a/packages/eslint-plugin/src/util/getESLintCoreRule.ts b/packages/eslint-plugin/src/util/getESLintCoreRule.ts index 5ba9ae369659..f133d85317ac 100644 --- a/packages/eslint-plugin/src/util/getESLintCoreRule.ts +++ b/packages/eslint-plugin/src/util/getESLintCoreRule.ts @@ -12,6 +12,7 @@ interface RuleMap { indent: typeof import('eslint/lib/rules/indent'); 'init-declarations': typeof import('eslint/lib/rules/init-declarations'); 'keyword-spacing': typeof import('eslint/lib/rules/keyword-spacing'); + 'lines-around-comment': typeof import('eslint/lib/rules/lines-around-comment'); 'lines-between-class-members': typeof import('eslint/lib/rules/lines-between-class-members'); 'no-dupe-args': typeof import('eslint/lib/rules/no-dupe-args'); 'no-dupe-class-members': typeof import('eslint/lib/rules/no-dupe-class-members'); diff --git a/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts b/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts new file mode 100644 index 000000000000..9baeec2baadc --- /dev/null +++ b/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts @@ -0,0 +1,21 @@ +import rule from '../../src/rules/lines-around-comment'; +import { RuleTester } from '../RuleTester'; + +const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser', +}); + +ruleTester.run('lines-around-comment', rule, { + valid: [ + // https://github.com/typescript-eslint/typescript-eslint/issues/1150 + ` +interface T { + /** + * No error + */ + a: boolean; +} + `, + ], + invalid: [], +}); diff --git a/packages/eslint-plugin/typings/eslint-rules.d.ts b/packages/eslint-plugin/typings/eslint-rules.d.ts index 08f172717355..614272ae5fc7 100644 --- a/packages/eslint-plugin/typings/eslint-rules.d.ts +++ b/packages/eslint-plugin/typings/eslint-rules.d.ts @@ -663,6 +663,36 @@ declare module 'eslint/lib/rules/no-extra-semi' { export = rule; } +declare module 'eslint/lib/rules/lines-around-comment' { + import { TSESLint } from '@typescript-eslint/utils'; + + const rule: TSESLint.RuleModule< + 'after' | 'before', + [ + { + beforeBlockComment?: boolean; + afterBlockComment?: boolean; + beforeLineComment?: boolean; + afterLineComment?: boolean; + allowBlockStart?: boolean; + allowBlockEnd?: boolean; + allowClassStart?: boolean; + allowClassEnd?: boolean; + allowObjectStart?: boolean; + allowObjectEnd?: boolean; + allowArrayStart?: boolean; + allowArrayEnd?: boolean; + ignorePattern?: string; + applyDefaultIgnorePatterns?: boolean; + }?, + ], + { + Program(): void; + } + >; + export = rule; +} + declare module 'eslint/lib/rules/lines-between-class-members' { import { TSESLint, TSESTree } from '@typescript-eslint/utils'; From 62efad99d0a4ba7d1120be47d3edc6e37240862d Mon Sep 17 00:00:00 2001 From: chbdetta Date: Fri, 8 Jul 2022 23:03:16 -0700 Subject: [PATCH 02/21] port the eslint lines-around-comment --- .../src/rules/lines-around-comment.ts | 389 ++- .../tests/rules/lines-around-comment.test.ts | 2757 ++++++++++++++++- 2 files changed, 3136 insertions(+), 10 deletions(-) diff --git a/packages/eslint-plugin/src/rules/lines-around-comment.ts b/packages/eslint-plugin/src/rules/lines-around-comment.ts index 3ab19b20762d..805f7399af4d 100644 --- a/packages/eslint-plugin/src/rules/lines-around-comment.ts +++ b/packages/eslint-plugin/src/rules/lines-around-comment.ts @@ -1,4 +1,9 @@ import * as util from '../util'; +import { + AST_NODE_TYPES, + AST_TOKEN_TYPES, + TSESTree, +} from '@typescript-eslint/utils'; import { getESLintCoreRule } from '../util/getESLintCoreRule'; const baseRule = getESLintCoreRule('lines-around-comment'); @@ -6,6 +11,39 @@ const baseRule = getESLintCoreRule('lines-around-comment'); export type Options = util.InferOptionsTypeFromRule; export type MessageIds = util.InferMessageIdsTypeFromRule; +const COMMENTS_IGNORE_PATTERN = + /^\s*(?:eslint|jshint\s+|jslint\s+|istanbul\s+|globals?\s+|exported\s+|jscs)/u; + +/** + * Return an array with with any line numbers that are empty. + */ +function getEmptyLineNums(lines: string[]): number[] { + const emptyLines = lines + .map((line, i) => ({ + code: line.trim(), + num: i + 1, + })) + .filter(line => !line.code) + .map(line => line.num); + + return emptyLines; +} + +/** + * Return an array with with any line numbers that contain comments. + */ +function getCommentLineNums(comments: TSESTree.Comment[]): number[] { + const lines: number[] = []; + + comments.forEach(token => { + const start = token.loc.start.line; + const end = token.loc.end.line; + + lines.push(start, end); + }); + return lines; +} + export default util.createRule({ name: 'lines-around-comment', meta: { @@ -83,10 +121,355 @@ export default util.createRule({ beforeBlockComment: true, }, ], - create(context, [options]) { - const rules = baseRule.create(context); + create(context, [_options]) { + const options = _options!; + const defaultIgnoreRegExp = COMMENTS_IGNORE_PATTERN; + const customIgnoreRegExp = new RegExp(options.ignorePattern ?? '', 'u'); + + const sourceCode = context.getSourceCode(); + const comments = sourceCode.getAllComments(); + + const lines = sourceCode.lines; + const numLines = lines.length + 1; + const commentLines = getCommentLineNums(comments); + const emptyLines = getEmptyLineNums(lines); + const commentAndEmptyLines = new Set(commentLines.concat(emptyLines)); + + /** + * Returns whether or not comments are on lines starting with or ending with code + */ + function codeAroundComment(token: TSESTree.Token): boolean { + let currentToken: TSESTree.Token | null = token; + + do { + currentToken = sourceCode.getTokenBefore(currentToken, { + includeComments: true, + }); + } while (currentToken && util.isCommentToken(currentToken)); + + if (currentToken && util.isTokenOnSameLine(currentToken, token)) { + return true; + } + + currentToken = token; + do { + currentToken = sourceCode.getTokenAfter(currentToken, { + includeComments: true, + }); + } while (currentToken && util.isCommentToken(currentToken)); + + if (currentToken && util.isTokenOnSameLine(token, currentToken)) { + return true; + } + + return false; + } + + /** + * Returns whether or not comments are inside a node type or not. + */ + function isParentNodeType( + parent: TSESTree.Node, + nodeType: T, + ): parent is Extract { + return ( + parent.type === nodeType || + ('body' in parent && + !Array.isArray(parent.body) && + parent.body?.type === nodeType) || + ('consequent' in parent && + !Array.isArray(parent.consequent) && + parent.consequent.type === nodeType) + ); + } + + /** + * Returns the parent node that contains the given token. + */ + function getParentNodeOfToken(token: TSESTree.Token): TSESTree.Node | null { + const node = sourceCode.getNodeByRangeIndex(token.range[0]); + + /* + * For the purpose of this rule, the comment token is in a `StaticBlock` node only + * if it's inside the braces of that `StaticBlock` node. + * + * Example where this function returns `null`: + * + * static + * // comment + * { + * } + * + * Example where this function returns `StaticBlock` node: + * + * static + * { + * // comment + * } + * + */ + if (node && node.type === AST_NODE_TYPES.StaticBlock) { + const openingBrace = sourceCode.getFirstToken(node, { skip: 1 })!; // skip the `static` token + + return token.range[0] >= openingBrace.range[0] ? node : null; + } + + return node; + } + + /** + * Returns whether or not comments are at the parent start or not. + */ + function isCommentAtParentStart( + token: TSESTree.Token, + nodeType: TSESTree.AST_NODE_TYPES, + ): boolean { + const parent = getParentNodeOfToken(token); + + if (parent && isParentNodeType(parent, nodeType)) { + const parentStartNodeOrToken = + parent.type === AST_NODE_TYPES.StaticBlock + ? sourceCode.getFirstToken(parent, { skip: 1 })! // opening brace of the static block + : parent; + + return ( + token.loc.start.line - parentStartNodeOrToken.loc.start.line === 1 + ); + } + + return false; + } + + /** + * Returns whether or not comments are at the parent end or not. + */ + function isCommentAtParentEnd( + token: TSESTree.Token, + nodeType: TSESTree.AST_NODE_TYPES, + ): boolean { + const parent = getParentNodeOfToken(token); + + return ( + !!parent && + isParentNodeType(parent, nodeType) && + parent.loc.end.line - token.loc.end.line === 1 + ); + } + + /** + * Returns whether or not comments are at the block start or not. + */ + function isCommentAtBlockStart(token: TSESTree.Token): boolean { + return ( + isCommentAtParentStart(token, AST_NODE_TYPES.ClassBody) || + isCommentAtParentStart(token, AST_NODE_TYPES.BlockStatement) || + isCommentAtParentStart(token, AST_NODE_TYPES.StaticBlock) || + isCommentAtParentStart(token, AST_NODE_TYPES.SwitchCase) + ); + } + + /** + * Returns whether or not comments are at the block end or not. + */ + function isCommentAtBlockEnd(token: TSESTree.Token): boolean { + return ( + isCommentAtParentEnd(token, AST_NODE_TYPES.ClassBody) || + isCommentAtParentEnd(token, AST_NODE_TYPES.BlockStatement) || + isCommentAtParentEnd(token, AST_NODE_TYPES.StaticBlock) || + isCommentAtParentEnd(token, AST_NODE_TYPES.SwitchCase) || + isCommentAtParentEnd(token, AST_NODE_TYPES.SwitchStatement) + ); + } + + /** + * Returns whether or not comments are at the class start or not. + */ + function isCommentAtClassStart(token: TSESTree.Token): boolean { + return isCommentAtParentStart(token, AST_NODE_TYPES.ClassBody); + } + + /** + * Returns whether or not comments are at the class end or not. + */ + function isCommentAtClassEnd(token: TSESTree.Token): boolean { + return isCommentAtParentEnd(token, AST_NODE_TYPES.ClassBody); + } + + /** + * Returns whether or not comments are at the object start or not. + */ + function isCommentAtObjectStart(token: TSESTree.Token): boolean { + return ( + isCommentAtParentStart(token, AST_NODE_TYPES.ObjectExpression) || + isCommentAtParentStart(token, AST_NODE_TYPES.ObjectPattern) + ); + } + + /** + * Returns whether or not comments are at the object end or not. + */ + function isCommentAtObjectEnd(token: TSESTree.Token): boolean { + return ( + isCommentAtParentEnd(token, AST_NODE_TYPES.ObjectExpression) || + isCommentAtParentEnd(token, AST_NODE_TYPES.ObjectPattern) + ); + } + + /** + * Returns whether or not comments are at the array start or not. + */ + function isCommentAtArrayStart(token: TSESTree.Token): boolean { + return ( + isCommentAtParentStart(token, AST_NODE_TYPES.ArrayExpression) || + isCommentAtParentStart(token, AST_NODE_TYPES.ArrayPattern) + ); + } + + /** + * Returns whether or not comments are at the array end or not. + */ + function isCommentAtArrayEnd(token: TSESTree.Token): boolean { + return ( + isCommentAtParentEnd(token, AST_NODE_TYPES.ArrayExpression) || + isCommentAtParentEnd(token, AST_NODE_TYPES.ArrayPattern) + ); + } + + function checkForEmptyLine( + token: TSESTree.Comment, + opts: { before?: boolean; after?: boolean }, + ): void { + if ( + options.applyDefaultIgnorePatterns !== false && + defaultIgnoreRegExp.test(token.value) + ) { + return; + } + + if (options.ignorePattern && customIgnoreRegExp.test(token.value)) { + return; + } + + let after = opts.after, + before = opts.before; + + const prevLineNum = token.loc.start.line - 1; + const nextLineNum = token.loc.end.line + 1; + + // we ignore all inline comments + if (codeAroundComment(token)) { + return; + } + + const blockStartAllowed = + Boolean(options.allowBlockStart) && + isCommentAtBlockStart(token) && + !(options.allowClassStart === false && isCommentAtClassStart(token)), + blockEndAllowed = + Boolean(options.allowBlockEnd) && + isCommentAtBlockEnd(token) && + !(options.allowClassEnd === false && isCommentAtClassEnd(token)), + classStartAllowed = + Boolean(options.allowClassStart) && isCommentAtClassStart(token), + classEndAllowed = + Boolean(options.allowClassEnd) && isCommentAtClassEnd(token), + objectStartAllowed = + Boolean(options.allowObjectStart) && isCommentAtObjectStart(token), + objectEndAllowed = + Boolean(options.allowObjectEnd) && isCommentAtObjectEnd(token), + arrayStartAllowed = + Boolean(options.allowArrayStart) && isCommentAtArrayStart(token), + arrayEndAllowed = + Boolean(options.allowArrayEnd) && isCommentAtArrayEnd(token); + + const exceptionStartAllowed = + blockStartAllowed || + classStartAllowed || + objectStartAllowed || + arrayStartAllowed; + const exceptionEndAllowed = + blockEndAllowed || + classEndAllowed || + objectEndAllowed || + arrayEndAllowed; + + // ignore top of the file and bottom of the file + if (prevLineNum < 1) { + before = false; + } + if (nextLineNum >= numLines) { + after = false; + } + + const previousTokenOrComment = sourceCode.getTokenBefore(token, { + includeComments: true, + }); + const nextTokenOrComment = sourceCode.getTokenAfter(token, { + includeComments: true, + }); + + // check for newline before + if ( + !exceptionStartAllowed && + before && + !commentAndEmptyLines.has(prevLineNum) && + !( + util.isCommentToken(previousTokenOrComment!) && + util.isTokenOnSameLine(previousTokenOrComment, token) + ) + ) { + const lineStart = token.range[0] - token.loc.start.column; + const range = [lineStart, lineStart] as const; + + context.report({ + node: token, + messageId: 'before', + fix(fixer) { + return fixer.insertTextBeforeRange(range, '\n'); + }, + }); + } + + // check for newline after + if ( + !exceptionEndAllowed && + after && + !commentAndEmptyLines.has(nextLineNum) && + !( + util.isCommentToken(nextTokenOrComment!) && + util.isTokenOnSameLine(token, nextTokenOrComment) + ) + ) { + context.report({ + node: token, + messageId: 'after', + fix(fixer) { + return fixer.insertTextAfter(token, '\n'); + }, + }); + } + } + return { - ...rules, + Program(): void { + comments.forEach(token => { + if (token.type === AST_TOKEN_TYPES.Line) { + if (options.beforeLineComment || options.afterLineComment) { + checkForEmptyLine(token, { + after: options.afterLineComment, + before: options.beforeLineComment, + }); + } + } else if (token.type === AST_TOKEN_TYPES.Block) { + if (options.beforeBlockComment || options.afterBlockComment) { + checkForEmptyLine(token, { + after: options.afterBlockComment, + before: options.beforeBlockComment, + }); + } + } + }); + }, }; }, }); diff --git a/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts b/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts index 9baeec2baadc..c396ebdc1ce7 100644 --- a/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts +++ b/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts @@ -1,5 +1,7 @@ import rule from '../../src/rules/lines-around-comment'; -import { RuleTester } from '../RuleTester'; +import { unIndent } from './indent/utils'; +import { AST_TOKEN_TYPES } from '@typescript-eslint/utils'; +import { noFormat, RuleTester } from '../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser', @@ -7,15 +9,2756 @@ const ruleTester = new RuleTester({ ruleTester.run('lines-around-comment', rule, { valid: [ - // https://github.com/typescript-eslint/typescript-eslint/issues/1150 + // default rules ` -interface T { +bar(); + +/** block block block + * block + */ + +var a = 1; + `, + ` +bar(); + +/** block block block + * block + */ +var a = 1; + `, + ` +bar(); +// line line line +var a = 1; + `, + ` +bar(); + +// line line line +var a = 1; + `, + ` +bar(); +// line line line + +var a = 1; + `, + + // line comments + { + code: ` +bar(); +// line line line + +var a = 1; + `, + options: [{ afterLineComment: true }], + }, + { + code: ` +foo(); + +// line line line +var a = 1; + `, + options: [{ beforeLineComment: true }], + }, + { + code: ` +foo(); + +// line line line + +var a = 1; + `, + options: [{ beforeLineComment: true, afterLineComment: true }], + }, + { + code: ` +foo(); + +// line line line +// line line + +var a = 1; + `, + options: [{ beforeLineComment: true, afterLineComment: true }], + }, + { + code: '// line line line\n// line line', + options: [{ beforeLineComment: true, afterLineComment: true }], + }, + + // block comments + { + code: ` +bar(); + +/** A Block comment with a an empty line after + * + */ +var a = 1; + `, + options: [{ afterBlockComment: false, beforeBlockComment: true }], + }, + { + code: ` +bar(); + +/** block block block + * block + */ +var a = 1; + `, + options: [{ afterBlockComment: false }], + }, + { + code: '/** \nblock \nblock block\n */\n/* block \n block \n */', + options: [{ afterBlockComment: true, beforeBlockComment: true }], + }, + { + code: ` +bar(); + +/** block block block + * block + */ + +var a = 1; + `, + options: [{ afterBlockComment: true, beforeBlockComment: true }], + }, + + // inline comments (should not ever warn) + { + code: ` +foo(); // An inline comment with a an empty line after +var a = 1; + `, + options: [{ afterLineComment: true, beforeLineComment: true }], + }, + { + code: noFormat` +foo(); +bar(); /* An inline block comment with a an empty line after + * + */ +var a = 1; + `, + options: [{ beforeBlockComment: true }], + }, + + // mixed comment (some block & some line) + { + code: ` +bar(); + +/** block block block + * block + */ +//line line line +var a = 1; + `, + options: [{ afterBlockComment: true }], + }, + { + code: ` +bar(); + +/** block block block + * block + */ +//line line line +var a = 1; + `, + options: [{ beforeLineComment: true }], + }, + + // check for block start comments + { + code: unIndent` +var a, + +// line +b; + `, + options: [ + { + beforeLineComment: true, + allowBlockStart: true, + }, + ], + }, + { + code: ` +function foo() { + // line at block start + var g = 1; +} + `, + options: [ + { + beforeLineComment: true, + allowBlockStart: true, + }, + ], + }, + { + code: ` +var foo = function () { + // line at block start + var g = 1; +}; + `, + options: [ + { + beforeLineComment: true, + allowBlockStart: true, + }, + ], + }, + { + code: ` +var foo = function () { + // line at block start +}; + `, + options: [ + { + beforeLineComment: true, + allowBlockStart: true, + }, + ], + }, + { + code: ` +if (true) { + // line at block start + var g = 1; +} + `, + options: [ + { + beforeLineComment: true, + allowBlockStart: true, + }, + ], + }, + { + code: ` +if (true) { + // line at block start +} + `, + options: [ + { + beforeLineComment: true, + allowBlockStart: true, + }, + ], + }, + { + code: ` +if (true) { + bar(); +} else { + // line at block start +} + `, + options: [ + { + beforeLineComment: true, + allowBlockStart: true, + }, + ], + }, + { + code: ` +switch ('foo') { + case 'foo': + // line at switch case start + break; +} + `, + options: [ + { + beforeLineComment: true, + allowBlockStart: true, + }, + ], + }, + { + code: ` +switch ('foo') { + case 'foo': + break; + + default: + // line at switch case start + break; +} + `, + options: [ + { + beforeLineComment: true, + allowBlockStart: true, + }, + ], + }, + { + code: ` +function foo() { + /* block comment at block start */ + var g = 1; +} + `, + options: [ + { + allowBlockStart: true, + }, + ], + }, + { + code: ` +var foo = function () { + /* block comment at block start */ + var g = 1; +}; + `, + options: [ + { + allowBlockStart: true, + }, + ], + }, + { + code: ` +if (true) { + /* block comment at block start */ + var g = 1; +} + `, + options: [ + { + allowBlockStart: true, + }, + ], + }, + { + code: ` +while (true) { + /* +block comment at block start + */ + var g = 1; +} + `, + options: [ + { + allowBlockStart: true, + }, + ], + }, + { + code: ` +class A { /** - * No error + * hi */ - a: boolean; + constructor() {} } - `, + `, + options: [ + { + allowBlockStart: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: ` +class A { + /** + * hi + */ + constructor() {} +} + `, + options: [ + { + allowClassStart: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: ` +class A { + /** + * hi + */ + constructor() {} +} + `, + options: [ + { + allowBlockStart: false, + allowClassStart: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: ` +switch ('foo') { + case 'foo': + /* block comment at switch case start */ + break; +} + `, + options: [ + { + allowBlockStart: true, + }, + ], + }, + { + code: ` +switch ('foo') { + case 'foo': + break; + + default: + /* block comment at switch case start */ + break; +} + `, + options: [ + { + allowBlockStart: true, + }, + ], + }, + { + code: ` +class C { + static { + // line comment + } + + static { + // line comment + foo(); + } +} + `, + options: [ + { + beforeLineComment: true, + allowBlockStart: true, + }, + ], + parserOptions: { ecmaVersion: 2022 }, + }, + { + code: ` +class C { + static { + /* block comment */ + } + + static { + /* block + comment */ + } + + static { + /* block comment */ + foo(); + } + + static { + /* block + comment */ + foo(); + } +} + `, + options: [ + { + beforeBlockComment: true, + allowBlockStart: true, + }, + ], + parserOptions: { ecmaVersion: 2022 }, + }, + + // check for block end comments + { + code: ` +var a, + // line + + b; + `, + options: [ + { + afterLineComment: true, + allowBlockEnd: true, + }, + ], + }, + { + code: ` +function foo() { + var g = 91; + // line at block end +} + `, + options: [ + { + afterLineComment: true, + allowBlockEnd: true, + }, + ], + }, + { + code: ` +function foo() { + var g = 61; + + // line at block end +} + `, + options: [ + { + afterLineComment: true, + allowBlockEnd: true, + }, + ], + }, + { + code: ` +var foo = function () { + var g = 1; + + // line at block end +}; + `, + options: [ + { + afterLineComment: true, + allowBlockEnd: true, + }, + ], + }, + { + code: ` +if (true) { + var g = 1; + // line at block end +} + `, + options: [ + { + afterLineComment: true, + allowBlockEnd: true, + }, + ], + }, + { + code: ` +if (true) { + var g = 1; + + // line at block end +} + `, + options: [ + { + afterLineComment: true, + allowBlockEnd: true, + }, + ], + }, + { + code: ` +switch ('foo') { + case 'foo': + var g = 1; + + // line at switch case end +} + `, + options: [ + { + afterLineComment: true, + allowBlockEnd: true, + }, + ], + }, + { + code: ` +switch ('foo') { + case 'foo': + break; + + default: + var g = 1; + + // line at switch case end +} + `, + options: [ + { + afterLineComment: true, + allowBlockEnd: true, + }, + ], + }, + { + code: ` +while (true) { + // line at block start and end +} + `, + options: [ + { + afterLineComment: true, + allowBlockEnd: true, + }, + ], + }, + { + code: ` +while (true) { + // line at block start and end +} + `, + options: [ + { + afterLineComment: true, + allowBlockStart: true, + allowBlockEnd: true, + }, + ], + }, + { + code: ` +while (true) { + // line at block start and end +} + `, + options: [ + { + beforeLineComment: true, + allowBlockStart: true, + allowBlockEnd: true, + }, + ], + }, + { + code: ` +while (true) { + // line at block start and end +} + `, + options: [ + { + afterLineComment: true, + beforeLineComment: true, + allowBlockStart: true, + allowBlockEnd: true, + }, + ], + }, + { + code: ` +while (true) { + // line at block start and end +} + `, + options: [ + { + beforeLineComment: true, + allowBlockStart: true, + }, + ], + }, + { + code: ` +function foo() { + var g = 1; + /* block comment at block end */ +} + `, + options: [ + { + beforeBlockComment: false, + afterBlockComment: true, + allowBlockEnd: true, + }, + ], + }, + { + code: ` +var foo = function () { + var g = 1; + /* block comment at block end */ +}; + `, + options: [ + { + beforeBlockComment: false, + afterBlockComment: true, + allowBlockEnd: true, + }, + ], + }, + { + code: ` +if (true) { + var g = 1; + /* block comment at block end */ +} + `, + options: [ + { + beforeBlockComment: false, + afterBlockComment: true, + allowBlockEnd: true, + }, + ], + }, + { + code: ` +if (true) { + var g = 1; + + /* block comment at block end */ +} + `, + options: [ + { + afterBlockComment: true, + allowBlockEnd: true, + }, + ], + }, + { + code: ` +while (true) { + var g = 1; + + /* +block comment at block end + */ +} + `, + options: [ + { + afterBlockComment: true, + allowBlockEnd: true, + }, + ], + }, + { + code: ` +class B { + constructor() {} + + /** + * hi + */ +} + `, + options: [ + { + afterBlockComment: true, + allowBlockEnd: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: ` +class B { + constructor() {} + + /** + * hi + */ +} + `, + options: [ + { + afterBlockComment: true, + allowClassEnd: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: ` +class B { + constructor() {} + + /** + * hi + */ +} + `, + options: [ + { + afterBlockComment: true, + allowBlockEnd: false, + allowClassEnd: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: ` +switch ('foo') { + case 'foo': + var g = 1; + + /* block comment at switch case end */ +} + `, + options: [ + { + afterBlockComment: true, + allowBlockEnd: true, + }, + ], + }, + { + code: ` +switch ('foo') { + case 'foo': + break; + + default: + var g = 1; + + /* block comment at switch case end */ +} + `, + options: [ + { + afterBlockComment: true, + allowBlockEnd: true, + }, + ], + }, + { + code: ` +class C { + static { + // line comment + } + + static { + foo(); + // line comment + } +} + `, + options: [ + { + afterLineComment: true, + allowBlockEnd: true, + }, + ], + parserOptions: { ecmaVersion: 2022 }, + }, + { + code: ` +class C { + static { + /* block comment */ + } + + static { + /* block + comment */ + } + + static { + foo(); + /* block comment */ + } + + static { + foo(); + /* block + comment */ + } +} + `, + options: [ + { + beforeBlockComment: false, // default is `true` + afterBlockComment: true, + allowBlockEnd: true, + }, + ], + parserOptions: { ecmaVersion: 2022 }, + }, + + // check for object start comments + { + code: 'var a,\n\n' + '// line\n' + 'b;', + options: [ + { + beforeLineComment: true, + allowObjectStart: true, + }, + ], + }, + { + code: 'var obj = {\n' + ' // line at object start\n' + ' g: 1\n' + '};', + options: [ + { + beforeLineComment: true, + allowObjectStart: true, + }, + ], + }, + { + code: + 'function hi() {\n' + + ' return {\n' + + ' // hi\n' + + ' test: function() {\n' + + ' }\n' + + ' }\n' + + '}', + options: [ + { + beforeLineComment: true, + allowObjectStart: true, + }, + ], + }, + { + code: + 'var obj = {\n' + + ' /* block comment at object start*/\n' + + ' g: 1\n' + + '};', + options: [ + { + beforeBlockComment: true, + allowObjectStart: true, + }, + ], + }, + { + code: + 'function hi() {\n' + + ' return {\n' + + ' /**\n' + + ' * hi\n' + + ' */\n' + + ' test: function() {\n' + + ' }\n' + + ' }\n' + + '}', + options: [ + { + beforeLineComment: true, + allowObjectStart: true, + }, + ], + }, + { + code: + 'const {\n' + ' // line at object start\n' + ' g: a\n' + '} = {};', + options: [ + { + beforeLineComment: true, + allowObjectStart: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: 'const {\n' + ' // line at object start\n' + ' g\n' + '} = {};', + options: [ + { + beforeLineComment: true, + allowObjectStart: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: + 'const {\n' + + ' /* block comment at object-like start*/\n' + + ' g: a\n' + + '} = {};', + options: [ + { + beforeBlockComment: true, + allowObjectStart: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: + 'const {\n' + + ' /* block comment at object-like start*/\n' + + ' g\n' + + '} = {};', + options: [ + { + beforeBlockComment: true, + allowObjectStart: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + }, + + // check for object end comments + { + code: 'var a,\n' + '// line\n\n' + 'b;', + options: [ + { + afterLineComment: true, + allowObjectEnd: true, + }, + ], + }, + { + code: 'var obj = {\n' + ' g: 1\n' + ' // line at object end\n' + '};', + options: [ + { + afterLineComment: true, + allowObjectEnd: true, + }, + ], + }, + { + code: + 'function hi() {\n' + + ' return {\n' + + ' test: function() {\n' + + ' }\n' + + ' // hi\n' + + ' }\n' + + '}', + options: [ + { + afterLineComment: true, + allowObjectEnd: true, + }, + ], + }, + { + code: + 'var obj = {\n' + + ' g: 1\n' + + ' \n' + + ' /* block comment at object end*/\n' + + '};', + options: [ + { + afterBlockComment: true, + allowObjectEnd: true, + }, + ], + }, + { + code: + 'function hi() {\n' + + ' return {\n' + + ' test: function() {\n' + + ' }\n' + + ' \n' + + ' /**\n' + + ' * hi\n' + + ' */\n' + + ' }\n' + + '}', + options: [ + { + afterBlockComment: true, + allowObjectEnd: true, + }, + ], + }, + { + code: 'const {\n' + ' g: a\n' + ' // line at object end\n' + '} = {};', + options: [ + { + afterLineComment: true, + allowObjectEnd: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: 'const {\n' + ' g\n' + ' // line at object end\n' + '} = {};', + options: [ + { + afterLineComment: true, + allowObjectEnd: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: + 'const {\n' + + ' g: a\n' + + ' \n' + + ' /* block comment at object-like end*/\n' + + '} = {};', + options: [ + { + afterBlockComment: true, + allowObjectEnd: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: + 'const {\n' + + ' g\n' + + ' \n' + + ' /* block comment at object-like end*/\n' + + '} = {};', + options: [ + { + afterBlockComment: true, + allowObjectEnd: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + }, + + // check for array start comments + { + code: 'var a,\n\n' + '// line\n' + 'b;', + options: [ + { + beforeLineComment: true, + allowArrayStart: true, + }, + ], + }, + { + code: 'var arr = [\n' + ' // line at array start\n' + ' 1\n' + '];', + options: [ + { + beforeLineComment: true, + allowArrayStart: true, + }, + ], + }, + { + code: + 'var arr = [\n' + + ' /* block comment at array start*/\n' + + ' 1\n' + + '];', + options: [ + { + beforeBlockComment: true, + allowArrayStart: true, + }, + ], + }, + { + code: 'const [\n' + ' // line at array start\n' + ' a\n' + '] = [];', + options: [ + { + beforeLineComment: true, + allowArrayStart: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: + 'const [\n' + + ' /* block comment at array start*/\n' + + ' a\n' + + '] = [];', + options: [ + { + beforeBlockComment: true, + allowArrayStart: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + }, + + // check for array end comments + { + code: 'var a,\n' + '// line\n\n' + 'b;', + options: [ + { + afterLineComment: true, + allowArrayEnd: true, + }, + ], + }, + { + code: 'var arr = [\n' + ' 1\n' + ' // line at array end\n' + '];', + options: [ + { + afterLineComment: true, + allowArrayEnd: true, + }, + ], + }, + { + code: + 'var arr = [\n' + + ' 1\n' + + ' \n' + + ' /* block comment at array end*/\n' + + '];', + options: [ + { + afterBlockComment: true, + allowArrayEnd: true, + }, + ], + }, + { + code: 'const [\n' + ' a\n' + ' // line at array end\n' + '] = [];', + options: [ + { + afterLineComment: true, + allowArrayEnd: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: + 'const [\n' + + ' a\n' + + ' \n' + + ' /* block comment at array end*/\n' + + '] = [];', + options: [ + { + afterBlockComment: true, + allowArrayEnd: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + }, + + // ignorePattern + { + code: + 'foo;\n\n' + + '/* eslint-disable no-underscore-dangle */\n\n' + + 'this._values = values;\n' + + 'this._values2 = true;\n' + + '/* eslint-enable no-underscore-dangle */\n' + + 'bar', + options: [ + { + beforeBlockComment: true, + afterBlockComment: true, + }, + ], + }, + 'foo;\n/* eslint */', + 'foo;\n/* jshint */', + 'foo;\n/* jslint */', + 'foo;\n/* istanbul */', + 'foo;\n/* global */', + 'foo;\n/* globals */', + 'foo;\n/* exported */', + 'foo;\n/* jscs */', + { + code: ` +foo; +/* this is pragmatic */ + `, + options: [{ ignorePattern: 'pragma' }], + }, + { + code: ` +foo; +/* this is pragmatic */ + `, + options: [{ applyDefaultIgnorePatterns: false, ignorePattern: 'pragma' }], + }, + ], + invalid: [ + // default rules + { + code: ` +bar(); +/** block block block + * block + */ +var a = 1; + `, + output: ` +bar(); + +/** block block block + * block + */ +var a = 1; + `, + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block }], + }, + + // line comments + { + code: ` +baz(); +// A line comment with no empty line after +var a = 1; + `, + output: ` +baz(); +// A line comment with no empty line after + +var a = 1; + `, + options: [{ afterLineComment: true }], + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line }], + }, + { + code: ` +baz(); +// A line comment with no empty line after +var a = 1; + `, + output: ` +baz(); + +// A line comment with no empty line after +var a = 1; + `, + options: [{ beforeLineComment: true, afterLineComment: false }], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line }], + }, + { + code: '// A line comment with no empty line after\nvar a = 1;', + output: '// A line comment with no empty line after\n\nvar a = 1;', + options: [{ beforeLineComment: true, afterLineComment: true }], + errors: [ + { messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 1, column: 1 }, + ], + }, + { + code: unIndent` +baz(); +// A line comment with no empty line after +var a = 1; + `, + output: unIndent` +baz(); + +// A line comment with no empty line after + +var a = 1; + `, + options: [{ beforeLineComment: true, afterLineComment: true }], + errors: [ + { messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 2 }, + { messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 2 }, + ], + }, + + // block comments + { + code: unIndent` +bar(); +/** + * block block block + */ +var a = 1; + `, + output: unIndent` +bar(); + +/** + * block block block + */ + +var a = 1; + `, + options: [{ afterBlockComment: true, beforeBlockComment: true }], + errors: [ + { messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 2 }, + { messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 2 }, + ], + }, + { + code: unIndent` +bar(); +/* first block comment */ /* second block comment */ +var a = 1; + `, + output: unIndent` +bar(); + +/* first block comment */ /* second block comment */ + +var a = 1; + `, + options: [{ afterBlockComment: true, beforeBlockComment: true }], + errors: [ + { messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 2 }, + { messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 2 }, + ], + }, + { + code: unIndent` +bar(); +/* first block comment */ /* second block + comment */ +var a = 1; + `, + output: unIndent` +bar(); + +/* first block comment */ /* second block + comment */ + +var a = 1; + `, + options: [{ afterBlockComment: true, beforeBlockComment: true }], + errors: [ + { messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 2 }, + { messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 2 }, + ], + }, + { + code: unIndent` +bar(); +/** + * block block block + */ +var a = 1; + `, + output: unIndent` +bar(); +/** + * block block block + */ + +var a = 1; + `, + options: [{ afterBlockComment: true, beforeBlockComment: false }], + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 2 }], + }, + { + code: unIndent` +bar(); +/** + * block block block + */ +var a = 1; + `, + output: unIndent` +bar(); + +/** + * block block block + */ +var a = 1; + `, + options: [{ afterBlockComment: false, beforeBlockComment: true }], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 2 }], + }, + { + code: unIndent` +var a, +// line +b; + `, + output: unIndent` +var a, + +// line +b; + `, + options: [ + { + beforeLineComment: true, + allowBlockStart: true, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 2 }], + }, + { + code: unIndent` +function foo() { + var a = 1; + // line at block start + var g = 1; +} + `, + output: unIndent` +function foo() { + var a = 1; + + // line at block start + var g = 1; +} + `, + options: [ + { + beforeLineComment: true, + allowBlockStart: true, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 3 }], + }, + { + code: unIndent` +var a, +// line +b; + `, + output: unIndent` +var a, +// line + +b; + `, + options: [ + { + afterLineComment: true, + allowBlockEnd: true, + }, + ], + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 2 }], + }, + { + code: unIndent` +function foo() { + var a = 1; + + // line at block start + var g = 1; +} + `, + output: unIndent` +function foo() { + var a = 1; + + // line at block start + + var g = 1; +} + `, + options: [ + { + afterLineComment: true, + allowBlockEnd: true, + }, + ], + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 4 }], + }, + { + code: unIndent` +switch ('foo') { + case 'foo': + // line at switch case start + break; +} + `, + output: unIndent` +switch ('foo') { + case 'foo': + + // line at switch case start + break; +} + `, + options: [ + { + beforeLineComment: true, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 3 }], + }, + { + code: unIndent` +switch ('foo') { + case 'foo': + break; + + default: + // line at switch case start + break; +} + `, + output: unIndent` +switch ('foo') { + case 'foo': + break; + + default: + + // line at switch case start + break; +} + `, + options: [ + { + beforeLineComment: true, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 6 }], + }, + { + code: unIndent` +while (true) { + // line at block start and end +} + `, + output: unIndent` +while (true) { + // line at block start and end + +} + `, + options: [ + { + afterLineComment: true, + allowBlockStart: true, + }, + ], + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 2 }], + }, + { + code: unIndent` +while (true) { + // line at block start and end +} + `, + output: unIndent` +while (true) { + + // line at block start and end +} + `, + options: [ + { + beforeLineComment: true, + allowBlockEnd: true, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 2 }], + }, + { + code: unIndent` +class A { + // line at class start + constructor() {} +} + `, + output: unIndent` +class A { + + // line at class start + constructor() {} +} + `, + options: [ + { + beforeLineComment: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 2 }], + }, + { + code: unIndent` +class A { + // line at class start + constructor() {} +} + `, + output: unIndent` +class A { + + // line at class start + constructor() {} +} + `, + options: [ + { + allowBlockStart: true, + allowClassStart: false, + beforeLineComment: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 2 }], + }, + { + code: unIndent` +class B { + constructor() {} + + // line at class end +} + `, + output: unIndent` +class B { + constructor() {} + + // line at class end + +} + `, + options: [ + { + afterLineComment: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 4 }], + }, + { + code: unIndent` +class B { + constructor() {} + + // line at class end +} + `, + output: unIndent` +class B { + constructor() {} + + // line at class end + +} + `, + options: [ + { + afterLineComment: true, + allowBlockEnd: true, + allowClassEnd: false, + }, + ], + parserOptions: { ecmaVersion: 6 }, + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 4 }], + }, + { + code: unIndent` +switch ('foo') { + case 'foo': + var g = 1; + + // line at switch case end +} + `, + output: unIndent` +switch ('foo') { + case 'foo': + var g = 1; + + // line at switch case end + +} + `, + options: [ + { + afterLineComment: true, + }, + ], + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 5 }], + }, + { + code: unIndent` +switch ('foo') { + case 'foo': + break; + + default: + var g = 1; + + // line at switch case end +} + `, + output: unIndent` +switch ('foo') { + case 'foo': + break; + + default: + var g = 1; + + // line at switch case end + +} + `, + options: [ + { + afterLineComment: true, + }, + ], + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 8 }], + }, + { + code: unIndent` +class C { + // line comment + static {} +} + `, + output: unIndent` +class C { + // line comment + + static {} +} + `, + options: [ + { + beforeLineComment: true, + afterLineComment: true, + allowBlockStart: true, + allowBlockEnd: true, + allowClassStart: true, + allowClassEnd: true, + }, + ], + parserOptions: { ecmaVersion: 2022 }, + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 2 }], + }, + { + code: unIndent` +class C { + /* block + comment */ + static {} +} + `, + output: unIndent` +class C { + /* block + comment */ + + static {} +} + `, + options: [ + { + beforeBlockComment: true, + afterBlockComment: true, + allowBlockStart: true, + allowBlockEnd: true, + allowClassStart: true, + allowClassEnd: true, + }, + ], + parserOptions: { ecmaVersion: 2022 }, + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 2 }], + }, + { + code: unIndent` +class C { + static + // line comment + {} +} + `, + output: unIndent` +class C { + static + + // line comment + + {} +} + `, + options: [ + { + beforeLineComment: true, + afterLineComment: true, + allowBlockStart: true, + allowBlockEnd: true, + allowClassStart: true, + allowClassEnd: true, + }, + ], + parserOptions: { ecmaVersion: 2022 }, + errors: [ + { messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 3 }, + { messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 3 }, + ], + }, + { + code: unIndent` +class C { + static + /* block + comment */ + {} +} + `, + output: unIndent` +class C { + static + + /* block + comment */ + + {} +} + `, + options: [ + { + beforeBlockComment: true, + afterBlockComment: true, + allowBlockStart: true, + allowBlockEnd: true, + allowClassStart: true, + allowClassEnd: true, + }, + ], + parserOptions: { ecmaVersion: 2022 }, + errors: [ + { messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 3 }, + { messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 3 }, + ], + }, + { + code: unIndent` +class C { + static { + // line comment + foo(); + } +} + `, + output: unIndent` +class C { + static { + // line comment + + foo(); + } +} + `, + options: [ + { + beforeLineComment: true, + afterLineComment: true, + allowBlockStart: true, + allowBlockEnd: true, + }, + ], + parserOptions: { ecmaVersion: 2022 }, + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 3 }], + }, + { + code: unIndent` +class C { + static { + /* block + comment */ + foo(); + } +} + `, + output: unIndent` +class C { + static { + /* block + comment */ + + foo(); + } +} + `, + options: [ + { + beforeBlockComment: true, + afterBlockComment: true, + allowBlockStart: true, + allowBlockEnd: true, + }, + ], + parserOptions: { ecmaVersion: 2022 }, + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 3 }], + }, + { + code: unIndent` +class C { + static { + foo(); + // line comment + } +} + `, + output: unIndent` +class C { + static { + foo(); + + // line comment + } +} + `, + options: [ + { + beforeLineComment: true, + afterLineComment: true, + allowBlockStart: true, + allowBlockEnd: true, + }, + ], + parserOptions: { ecmaVersion: 2022 }, + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 4 }], + }, + { + code: unIndent` +class C { + static { + foo(); + /* block + comment */ + } +} + `, + output: unIndent` +class C { + static { + foo(); + + /* block + comment */ + } +} + `, + options: [ + { + beforeBlockComment: true, + afterBlockComment: true, + allowBlockStart: true, + allowBlockEnd: true, + }, + ], + parserOptions: { ecmaVersion: 2022 }, + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 4 }], + }, + { + code: unIndent` +class C { + static { + foo(); + // line comment + bar(); + } +} + `, + output: unIndent` +class C { + static { + foo(); + + // line comment + + bar(); + } +} + `, + options: [ + { + beforeLineComment: true, + afterLineComment: true, + allowBlockStart: true, + allowBlockEnd: true, + }, + ], + parserOptions: { ecmaVersion: 2022 }, + errors: [ + { messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 4 }, + { messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 4 }, + ], + }, + { + code: unIndent` +class C { + static { + foo(); + /* block + comment */ + bar(); + } +} + `, + output: unIndent` +class C { + static { + foo(); + + /* block + comment */ + + bar(); + } +} + `, + options: [ + { + beforeBlockComment: true, + afterBlockComment: true, + allowBlockStart: true, + allowBlockEnd: true, + }, + ], + parserOptions: { ecmaVersion: 2022 }, + errors: [ + { messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 4 }, + { messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 4 }, + ], + }, + { + code: unIndent` +class C { + static {} + // line comment +} + `, + output: unIndent` +class C { + static {} + + // line comment +} + `, + options: [ + { + beforeLineComment: true, + afterLineComment: true, + allowBlockStart: true, + allowBlockEnd: true, + allowClassStart: true, + allowClassEnd: true, + }, + ], + parserOptions: { ecmaVersion: 2022 }, + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 3 }], + }, + { + code: unIndent` +class C { + static {} + /* block + comment */ +} + `, + output: unIndent` +class C { + static {} + + /* block + comment */ +} + `, + options: [ + { + beforeBlockComment: true, + afterBlockComment: true, + allowBlockStart: true, + allowBlockEnd: true, + allowClassStart: true, + allowClassEnd: true, + }, + ], + parserOptions: { ecmaVersion: 2022 }, + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 3 }], + }, + + // object start comments + { + code: 'var obj = {\n' + ' // line at object start\n' + ' g: 1\n' + '};', + output: + 'var obj = {\n' + + '\n' + + ' // line at object start\n' + + ' g: 1\n' + + '};', + options: [ + { + beforeLineComment: true, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 2 }], + }, + { + code: + 'function hi() {\n' + + ' return {\n' + + ' // hi\n' + + ' test: function() {\n' + + ' }\n' + + ' }\n' + + '}', + output: + 'function hi() {\n' + + ' return {\n' + + '\n' + + ' // hi\n' + + ' test: function() {\n' + + ' }\n' + + ' }\n' + + '}', + options: [ + { + beforeLineComment: true, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 3 }], + }, + { + code: + 'var obj = {\n' + + ' /* block comment at object start*/\n' + + ' g: 1\n' + + '};', + output: + 'var obj = {\n' + + '\n' + + ' /* block comment at object start*/\n' + + ' g: 1\n' + + '};', + options: [ + { + beforeBlockComment: true, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 2 }], + }, + { + code: + 'function hi() {\n' + + ' return {\n' + + ' /**\n' + + ' * hi\n' + + ' */\n' + + ' test: function() {\n' + + ' }\n' + + ' }\n' + + '}', + output: + 'function hi() {\n' + + ' return {\n' + + '\n' + + ' /**\n' + + ' * hi\n' + + ' */\n' + + ' test: function() {\n' + + ' }\n' + + ' }\n' + + '}', + options: [ + { + beforeLineComment: true, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 3 }], + }, + { + code: + 'const {\n' + ' // line at object start\n' + ' g: a\n' + '} = {};', + output: + 'const {\n' + + '\n' + + ' // line at object start\n' + + ' g: a\n' + + '} = {};', + options: [ + { + beforeLineComment: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 2 }], + }, + { + code: 'const {\n' + ' // line at object start\n' + ' g\n' + '} = {};', + output: + 'const {\n' + + '\n' + + ' // line at object start\n' + + ' g\n' + + '} = {};', + options: [ + { + beforeLineComment: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 2 }], + }, + { + code: + 'const {\n' + + ' /* block comment at object-like start*/\n' + + ' g: a\n' + + '} = {};', + output: + 'const {\n' + + '\n' + + ' /* block comment at object-like start*/\n' + + ' g: a\n' + + '} = {};', + options: [ + { + beforeBlockComment: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 2 }], + }, + { + code: + 'const {\n' + + ' /* block comment at object-like start*/\n' + + ' g\n' + + '} = {};', + output: + 'const {\n' + + '\n' + + ' /* block comment at object-like start*/\n' + + ' g\n' + + '} = {};', + options: [ + { + beforeBlockComment: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 2 }], + }, + + // object end comments + { + code: 'var obj = {\n' + ' g: 1\n' + ' // line at object end\n' + '};', + output: + 'var obj = {\n' + + ' g: 1\n' + + ' // line at object end\n' + + '\n' + + '};', + options: [ + { + afterLineComment: true, + }, + ], + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 3 }], + }, + { + code: + 'function hi() {\n' + + ' return {\n' + + ' test: function() {\n' + + ' }\n' + + ' // hi\n' + + ' }\n' + + '}', + output: + 'function hi() {\n' + + ' return {\n' + + ' test: function() {\n' + + ' }\n' + + ' // hi\n' + + '\n' + + ' }\n' + + '}', + options: [ + { + afterLineComment: true, + }, + ], + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 5 }], + }, + { + code: + 'var obj = {\n' + + ' g: 1\n' + + ' \n' + + ' /* block comment at object end*/\n' + + '};', + output: + 'var obj = {\n' + + ' g: 1\n' + + ' \n' + + ' /* block comment at object end*/\n' + + '\n' + + '};', + options: [ + { + afterBlockComment: true, + }, + ], + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 4 }], + }, + { + code: + 'function hi() {\n' + + ' return {\n' + + ' test: function() {\n' + + ' }\n' + + ' \n' + + ' /**\n' + + ' * hi\n' + + ' */\n' + + ' }\n' + + '}', + output: + 'function hi() {\n' + + ' return {\n' + + ' test: function() {\n' + + ' }\n' + + ' \n' + + ' /**\n' + + ' * hi\n' + + ' */\n' + + '\n' + + ' }\n' + + '}', + options: [ + { + afterBlockComment: true, + }, + ], + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 6 }], + }, + { + code: 'const {\n' + ' g: a\n' + ' // line at object end\n' + '} = {};', + output: + 'const {\n' + + ' g: a\n' + + ' // line at object end\n' + + '\n' + + '} = {};', + options: [ + { + afterLineComment: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 3 }], + }, + { + code: 'const {\n' + ' g\n' + ' // line at object end\n' + '} = {};', + output: + 'const {\n' + ' g\n' + ' // line at object end\n' + '\n' + '} = {};', + options: [ + { + afterLineComment: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 3 }], + }, + { + code: + 'const {\n' + + ' g: a\n' + + ' \n' + + ' /* block comment at object-like end*/\n' + + '} = {};', + output: + 'const {\n' + + ' g: a\n' + + ' \n' + + ' /* block comment at object-like end*/\n' + + '\n' + + '} = {};', + options: [ + { + afterBlockComment: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 4 }], + }, + { + code: + 'const {\n' + + ' g\n' + + ' \n' + + ' /* block comment at object-like end*/\n' + + '} = {};', + output: + 'const {\n' + + ' g\n' + + ' \n' + + ' /* block comment at object-like end*/\n' + + '\n' + + '} = {};', + options: [ + { + afterBlockComment: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 4 }], + }, + + // array start comments + { + code: 'var arr = [\n' + ' // line at array start\n' + ' 1\n' + '];', + output: + 'var arr = [\n' + '\n' + ' // line at array start\n' + ' 1\n' + '];', + options: [ + { + beforeLineComment: true, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 2 }], + }, + { + code: + 'var arr = [\n' + + ' /* block comment at array start*/\n' + + ' 1\n' + + '];', + output: + 'var arr = [\n' + + '\n' + + ' /* block comment at array start*/\n' + + ' 1\n' + + '];', + options: [ + { + beforeBlockComment: true, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 2 }], + }, + { + code: 'const [\n' + ' // line at array start\n' + ' a\n' + '] = [];', + output: + 'const [\n' + '\n' + ' // line at array start\n' + ' a\n' + '] = [];', + options: [ + { + beforeLineComment: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 2 }], + }, + { + code: + 'const [\n' + + ' /* block comment at array start*/\n' + + ' a\n' + + '] = [];', + output: + 'const [\n' + + '\n' + + ' /* block comment at array start*/\n' + + ' a\n' + + '] = [];', + options: [ + { + beforeBlockComment: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 2 }], + }, + + // array end comments + { + code: 'var arr = [\n' + ' 1\n' + ' // line at array end\n' + '];', + output: + 'var arr = [\n' + ' 1\n' + ' // line at array end\n' + '\n' + '];', + options: [ + { + afterLineComment: true, + }, + ], + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 3 }], + }, + { + code: + 'var arr = [\n' + + ' 1\n' + + ' \n' + + ' /* block comment at array end*/\n' + + '];', + output: + 'var arr = [\n' + + ' 1\n' + + ' \n' + + ' /* block comment at array end*/\n' + + '\n' + + '];', + options: [ + { + afterBlockComment: true, + }, + ], + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 4 }], + }, + { + code: 'const [\n' + ' a\n' + ' // line at array end\n' + '] = [];', + output: + 'const [\n' + ' a\n' + ' // line at array end\n' + '\n' + '] = [];', + options: [ + { + afterLineComment: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 3 }], + }, + { + code: + 'const [\n' + + ' a\n' + + ' \n' + + ' /* block comment at array end*/\n' + + '] = [];', + output: + 'const [\n' + + ' a\n' + + ' \n' + + ' /* block comment at array end*/\n' + + '\n' + + '] = [];', + options: [ + { + afterBlockComment: true, + }, + ], + parserOptions: { ecmaVersion: 6 }, + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 4 }], + }, + + // ignorePattern + { + code: + 'foo;\n\n' + + '/* eslint-disable no-underscore-dangle */\n\n' + + 'this._values = values;\n' + + 'this._values2 = true;\n' + + '/* eslint-enable no-underscore-dangle */\n' + + 'bar', + output: + 'foo;\n\n' + + '/* eslint-disable no-underscore-dangle */\n\n' + + 'this._values = values;\n' + + 'this._values2 = true;\n' + + '\n' + + '/* eslint-enable no-underscore-dangle */\n' + + '\n' + + 'bar', + options: [ + { + beforeBlockComment: true, + afterBlockComment: true, + applyDefaultIgnorePatterns: false, + }, + ], + errors: [ + { messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 7 }, + { messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 7 }, + ], + }, + { + code: 'foo;\n/* eslint */', + output: 'foo;\n\n/* eslint */', + options: [{ applyDefaultIgnorePatterns: false }], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block }], + }, + { + code: 'foo;\n/* jshint */', + output: 'foo;\n\n/* jshint */', + options: [{ applyDefaultIgnorePatterns: false }], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block }], + }, + { + code: 'foo;\n/* jslint */', + output: 'foo;\n\n/* jslint */', + options: [{ applyDefaultIgnorePatterns: false }], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block }], + }, + { + code: 'foo;\n/* istanbul */', + output: 'foo;\n\n/* istanbul */', + options: [{ applyDefaultIgnorePatterns: false }], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block }], + }, + { + code: 'foo;\n/* global */', + output: 'foo;\n\n/* global */', + options: [{ applyDefaultIgnorePatterns: false }], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block }], + }, + { + code: 'foo;\n/* globals */', + output: 'foo;\n\n/* globals */', + options: [{ applyDefaultIgnorePatterns: false }], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block }], + }, + { + code: 'foo;\n/* exported */', + output: 'foo;\n\n/* exported */', + options: [{ applyDefaultIgnorePatterns: false }], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block }], + }, + { + code: 'foo;\n/* jscs */', + output: 'foo;\n\n/* jscs */', + options: [{ applyDefaultIgnorePatterns: false }], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block }], + }, + { + code: ` +foo; +/* something else */ + `, + output: ` +foo; + +/* something else */ + `, + options: [{ ignorePattern: 'pragma' }], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block }], + }, + { + code: ` +foo; +/* eslint */ + `, + output: ` +foo; + +/* eslint */ + `, + options: [{ applyDefaultIgnorePatterns: false }], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block }], + }, + + // "fallthrough" patterns are not ignored by default + { + code: 'foo;\n/* fallthrough */', + output: 'foo;\n\n/* fallthrough */', + options: [], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block }], + }, ], - invalid: [], }); From c2433b458604b6d70f68946a840a61fdf8a79e62 Mon Sep 17 00:00:00 2001 From: chbdetta Date: Fri, 8 Jul 2022 23:39:30 -0700 Subject: [PATCH 03/21] check for comment in interface and allow options --- .../src/rules/lines-around-comment.ts | 33 ++- .../tests/rules/lines-around-comment.test.ts | 201 ++++++++++++++++++ .../eslint-plugin/typings/eslint-rules.d.ts | 2 + 3 files changed, 233 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin/src/rules/lines-around-comment.ts b/packages/eslint-plugin/src/rules/lines-around-comment.ts index 805f7399af4d..8dbd3bb7f86e 100644 --- a/packages/eslint-plugin/src/rules/lines-around-comment.ts +++ b/packages/eslint-plugin/src/rules/lines-around-comment.ts @@ -101,6 +101,12 @@ export default util.createRule({ allowArrayEnd: { type: 'boolean', }, + allowInterfaceStart: { + type: 'boolean', + }, + allowInterfaceEnd: { + type: 'boolean', + }, ignorePattern: { type: 'string', }, @@ -335,6 +341,20 @@ export default util.createRule({ ); } + /** + * Returns whether or not comments are at the interface start or not. + */ + function isCommentAtInterfaceStart(token: TSESTree.Token): boolean { + return isCommentAtParentStart(token, AST_NODE_TYPES.TSInterfaceBody); + } + + /** + * Returns whether or not comments are at the interface end or not. + */ + function isCommentAtInterfaceEnd(token: TSESTree.Token): boolean { + return isCommentAtParentEnd(token, AST_NODE_TYPES.TSInterfaceBody); + } + function checkForEmptyLine( token: TSESTree.Comment, opts: { before?: boolean; after?: boolean }, @@ -380,18 +400,25 @@ export default util.createRule({ arrayStartAllowed = Boolean(options.allowArrayStart) && isCommentAtArrayStart(token), arrayEndAllowed = - Boolean(options.allowArrayEnd) && isCommentAtArrayEnd(token); + Boolean(options.allowArrayEnd) && isCommentAtArrayEnd(token), + interfaceStartAllowed = + Boolean(options.allowInterfaceStart) && + isCommentAtInterfaceStart(token), + interfaceEndAllowed = + Boolean(options.allowInterfaceEnd) && isCommentAtInterfaceEnd(token); const exceptionStartAllowed = blockStartAllowed || classStartAllowed || objectStartAllowed || - arrayStartAllowed; + arrayStartAllowed || + interfaceStartAllowed; const exceptionEndAllowed = blockEndAllowed || classEndAllowed || objectEndAllowed || - arrayEndAllowed; + arrayEndAllowed || + interfaceEndAllowed; // ignore top of the file and bottom of the file if (prevLineNum < 1) { diff --git a/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts b/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts index c396ebdc1ce7..7676013f07ea 100644 --- a/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts +++ b/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts @@ -1258,6 +1258,67 @@ class C { ], parserOptions: { ecmaVersion: 6 }, }, + // Interface + { + code: unIndent` +interface A { + // line + a: string; +} +`, + options: [ + { + beforeLineComment: true, + allowInterfaceStart: true, + }, + ], + }, + { + code: unIndent` +interface A { + /* block + comment */ + a: string; +} +`, + options: [ + { + beforeBlockComment: true, + allowInterfaceStart: true, + }, + ], + }, + { + code: unIndent` +interface A { + a: string; + // line +} +`, + options: [ + { + afterLineComment: true, + allowInterfaceEnd: true, + }, + ], + }, + { + code: unIndent` +interface A { + a: string; + /* block + comment */ +} +`, + options: [ + { + beforeBlockComment: false, + afterBlockComment: true, + allowInterfaceEnd: true, + }, + ], + }, + // Type object // ignorePattern { @@ -2647,6 +2708,146 @@ class C { parserOptions: { ecmaVersion: 6 }, errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 4 }], }, + // interface + { + code: unIndent` +interface A { + a: string; + // line +} +`, + output: unIndent` +interface A { + a: string; + + // line +} +`, + options: [ + { + beforeLineComment: true, + allowInterfaceStart: true, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 3 }], + }, + { + code: unIndent` +interface A { + a: string; + /* block + comment */ +} +`, + output: unIndent` +interface A { + a: string; + + /* block + comment */ +} +`, + options: [ + { + beforeBlockComment: true, + allowInterfaceStart: true, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 3 }], + }, + { + code: unIndent` +interface A { + // line + a: string; +} +`, + output: unIndent` +interface A { + + // line + a: string; +} +`, + options: [ + { + beforeLineComment: true, + allowInterfaceStart: false, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 2 }], + }, + { + code: unIndent` +interface A { + /* block + comment */ + a: string; +} +`, + output: unIndent` +interface A { + + /* block + comment */ + a: string; +} +`, + options: [ + { + beforeBlockComment: true, + allowInterfaceStart: false, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 2 }], + }, + { + code: unIndent` +interface A { + a: string; + // line +} +`, + output: unIndent` +interface A { + a: string; + // line + +} +`, + options: [ + { + afterLineComment: true, + allowInterfaceEnd: false, + }, + ], + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 3 }], + }, + { + code: unIndent` +interface A { + a: string; + /* block + comment */ +} +`, + output: unIndent` +interface A { + a: string; + /* block + comment */ + +} +`, + options: [ + { + beforeBlockComment: false, + afterBlockComment: true, + allowInterfaceEnd: false, + }, + ], + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 3 }], + }, // ignorePattern { diff --git a/packages/eslint-plugin/typings/eslint-rules.d.ts b/packages/eslint-plugin/typings/eslint-rules.d.ts index 614272ae5fc7..5555292b8e1c 100644 --- a/packages/eslint-plugin/typings/eslint-rules.d.ts +++ b/packages/eslint-plugin/typings/eslint-rules.d.ts @@ -682,6 +682,8 @@ declare module 'eslint/lib/rules/lines-around-comment' { allowObjectEnd?: boolean; allowArrayStart?: boolean; allowArrayEnd?: boolean; + allowInterfaceStart?: boolean; + allowInterfaceEnd?: boolean; ignorePattern?: string; applyDefaultIgnorePatterns?: boolean; }?, From d97237e16cfd7a46eed706b3928ac1672a9d41b9 Mon Sep 17 00:00:00 2001 From: chbdetta Date: Fri, 8 Jul 2022 23:51:28 -0700 Subject: [PATCH 04/21] check for comment in type literals and allow options --- .../src/rules/lines-around-comment.ts | 32 ++- .../tests/rules/lines-around-comment.test.ts | 202 +++++++++++++++++- .../eslint-plugin/typings/eslint-rules.d.ts | 2 + 3 files changed, 232 insertions(+), 4 deletions(-) diff --git a/packages/eslint-plugin/src/rules/lines-around-comment.ts b/packages/eslint-plugin/src/rules/lines-around-comment.ts index 8dbd3bb7f86e..751a78e413e4 100644 --- a/packages/eslint-plugin/src/rules/lines-around-comment.ts +++ b/packages/eslint-plugin/src/rules/lines-around-comment.ts @@ -107,6 +107,12 @@ export default util.createRule({ allowInterfaceEnd: { type: 'boolean', }, + allowTypeStart: { + type: 'boolean', + }, + allowTypeEnd: { + type: 'boolean', + }, ignorePattern: { type: 'string', }, @@ -355,6 +361,20 @@ export default util.createRule({ return isCommentAtParentEnd(token, AST_NODE_TYPES.TSInterfaceBody); } + /** + * Returns whether or not comments are at the type start or not. + */ + function isCommentAtTypeStart(token: TSESTree.Token): boolean { + return isCommentAtParentStart(token, AST_NODE_TYPES.TSTypeLiteral); + } + + /** + * Returns whether or not comments are at the type end or not. + */ + function isCommentAtTypeEnd(token: TSESTree.Token): boolean { + return isCommentAtParentEnd(token, AST_NODE_TYPES.TSTypeLiteral); + } + function checkForEmptyLine( token: TSESTree.Comment, opts: { before?: boolean; after?: boolean }, @@ -405,20 +425,26 @@ export default util.createRule({ Boolean(options.allowInterfaceStart) && isCommentAtInterfaceStart(token), interfaceEndAllowed = - Boolean(options.allowInterfaceEnd) && isCommentAtInterfaceEnd(token); + Boolean(options.allowInterfaceEnd) && isCommentAtInterfaceEnd(token), + typeStartAllowed = + Boolean(options.allowTypeStart) && isCommentAtTypeStart(token), + typeEndAllowed = + Boolean(options.allowTypeEnd) && isCommentAtTypeEnd(token); const exceptionStartAllowed = blockStartAllowed || classStartAllowed || objectStartAllowed || arrayStartAllowed || - interfaceStartAllowed; + interfaceStartAllowed || + typeStartAllowed; const exceptionEndAllowed = blockEndAllowed || classEndAllowed || objectEndAllowed || arrayEndAllowed || - interfaceEndAllowed; + interfaceEndAllowed || + typeEndAllowed; // ignore top of the file and bottom of the file if (prevLineNum < 1) { diff --git a/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts b/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts index 7676013f07ea..1f2472066208 100644 --- a/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts +++ b/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts @@ -1318,7 +1318,66 @@ interface A { }, ], }, - // Type object + // Type + { + code: unIndent` +type A = { + // line + a: string; +} +`, + options: [ + { + beforeLineComment: true, + allowTypeStart: true, + }, + ], + }, + { + code: unIndent` +type A = { + /* block + comment */ + a: string; +} +`, + options: [ + { + beforeBlockComment: true, + allowTypeStart: true, + }, + ], + }, + { + code: unIndent` +type A = { + a: string; + // line +} +`, + options: [ + { + afterLineComment: true, + allowTypeEnd: true, + }, + ], + }, + { + code: unIndent` +type A = { + a: string; + /* block + comment */ +} +`, + options: [ + { + beforeBlockComment: false, + afterBlockComment: true, + allowTypeEnd: true, + }, + ], + }, // ignorePattern { @@ -2837,6 +2896,147 @@ interface A { /* block comment */ +} +`, + options: [ + { + beforeBlockComment: false, + afterBlockComment: true, + allowInterfaceEnd: false, + }, + ], + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 3 }], + }, + + // type + { + code: unIndent` +type A = { + a: string; + // line +} +`, + output: unIndent` +type A = { + a: string; + + // line +} +`, + options: [ + { + beforeLineComment: true, + allowInterfaceStart: true, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 3 }], + }, + { + code: unIndent` +type A = { + a: string; + /* block + comment */ +} +`, + output: unIndent` +type A = { + a: string; + + /* block + comment */ +} +`, + options: [ + { + beforeBlockComment: true, + allowInterfaceStart: true, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 3 }], + }, + { + code: unIndent` +type A = { + // line + a: string; +} +`, + output: unIndent` +type A = { + + // line + a: string; +} +`, + options: [ + { + beforeLineComment: true, + allowInterfaceStart: false, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 2 }], + }, + { + code: unIndent` +type A = { + /* block + comment */ + a: string; +} +`, + output: unIndent` +type A = { + + /* block + comment */ + a: string; +} +`, + options: [ + { + beforeBlockComment: true, + allowInterfaceStart: false, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 2 }], + }, + { + code: unIndent` +type A = { + a: string; + // line +} +`, + output: unIndent` +type A = { + a: string; + // line + +} +`, + options: [ + { + afterLineComment: true, + allowInterfaceEnd: false, + }, + ], + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 3 }], + }, + { + code: unIndent` +type A = { + a: string; + /* block + comment */ +} +`, + output: unIndent` +type A = { + a: string; + /* block + comment */ + } `, options: [ diff --git a/packages/eslint-plugin/typings/eslint-rules.d.ts b/packages/eslint-plugin/typings/eslint-rules.d.ts index 5555292b8e1c..547dc1c128dc 100644 --- a/packages/eslint-plugin/typings/eslint-rules.d.ts +++ b/packages/eslint-plugin/typings/eslint-rules.d.ts @@ -684,6 +684,8 @@ declare module 'eslint/lib/rules/lines-around-comment' { allowArrayEnd?: boolean; allowInterfaceStart?: boolean; allowInterfaceEnd?: boolean; + allowTypeStart?: boolean; + allowTypeEnd?: boolean; ignorePattern?: string; applyDefaultIgnorePatterns?: boolean; }?, From e9df0ecb3f92d302a205981c1d46734e7caa6729 Mon Sep 17 00:00:00 2001 From: chbdetta Date: Sat, 9 Jul 2022 00:06:21 -0700 Subject: [PATCH 05/21] add lines-around-comment in index --- packages/eslint-plugin/src/rules/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index 29a47ec2384a..0bc7e7f8226a 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -23,6 +23,7 @@ import funcCallSpacing from './func-call-spacing'; import indent from './indent'; import initDeclarations from './init-declarations'; import keywordSpacing from './keyword-spacing'; +import linesAroundComment from './lines-around-comment'; import linesBetweenClassMembers from './lines-between-class-members'; import memberDelimiterStyle from './member-delimiter-style'; import memberOrdering from './member-ordering'; @@ -152,6 +153,7 @@ export default { indent: indent, 'init-declarations': initDeclarations, 'keyword-spacing': keywordSpacing, + 'lines-around-comment': linesAroundComment, 'lines-between-class-members': linesBetweenClassMembers, 'member-delimiter-style': memberDelimiterStyle, 'member-ordering': memberOrdering, From 14db2f78097b9686321383e3d59cecec40130b83 Mon Sep 17 00:00:00 2001 From: chbdetta Date: Sat, 9 Jul 2022 00:10:47 -0700 Subject: [PATCH 06/21] add lines-around-comment in all config --- packages/eslint-plugin/src/configs/all.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/eslint-plugin/src/configs/all.ts b/packages/eslint-plugin/src/configs/all.ts index 8742d36b2089..d79482aaba11 100644 --- a/packages/eslint-plugin/src/configs/all.ts +++ b/packages/eslint-plugin/src/configs/all.ts @@ -39,6 +39,8 @@ export = { '@typescript-eslint/init-declarations': 'error', 'keyword-spacing': 'off', '@typescript-eslint/keyword-spacing': 'error', + 'lines-around-comment': 'off', + '@typescript-eslint/lines-around-comment': 'error', 'lines-between-class-members': 'off', '@typescript-eslint/lines-between-class-members': 'error', '@typescript-eslint/member-delimiter-style': 'error', From 7f2661fd0a8f73064a68b5c6872bbc8f0a3c0594 Mon Sep 17 00:00:00 2001 From: chbdetta Date: Sat, 9 Jul 2022 00:22:54 -0700 Subject: [PATCH 07/21] add lines-around-comment doc --- .../docs/rules/lines-around-comment.md | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 packages/eslint-plugin/docs/rules/lines-around-comment.md diff --git a/packages/eslint-plugin/docs/rules/lines-around-comment.md b/packages/eslint-plugin/docs/rules/lines-around-comment.md new file mode 100644 index 000000000000..ff07edfb23ee --- /dev/null +++ b/packages/eslint-plugin/docs/rules/lines-around-comment.md @@ -0,0 +1,37 @@ +# `lines-around-comment` + +Requires empty lines around comments. + +## Rule Details + +This rule extends the base [`eslint/lines-around-comment`](https://eslint.org/docs/rules/lines-around-comment) rule. +It adds support for TypeScript syntax. + +See the [ESLint documentation](https://eslint.org/docs/rules/lines-around-comment) for more details on the `comma-dangle` rule. + +## Rule Changes + +```jsonc +{ + // note you must disable the base rule as it can report incorrect errors + "lines-around-comment": "off", + "@typescript-eslint/lines-around-comment": ["error"] +} +``` + +In addition to the options supported by the `lines-around-comment` rule in ESLint core, the rule adds the following options: + +## Options + +- `allowInterfaceStart: true` doesn't require a blank line after the interface body block start +- `allowInterfaceEnd: true` doesn't require a blank line before the interface body block end +- `allowTypeStart: true` doesn't require a blank line after the type literal block start +- `allowTypeEnd: true` doesn't require a blank line after the type literal block end + +[See the other options allowed](https://eslint.org/docs/rules/comma-dangle#options) + + + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/main/docs/rules/lines-around-comment.md) + + From 30825c6f8072ea09a2a63737ffdd78e844401be6 Mon Sep 17 00:00:00 2001 From: chbdetta Date: Sun, 25 Sep 2022 01:21:51 -0700 Subject: [PATCH 08/21] delegate non-TS check to the base rule --- .../src/rules/lines-around-comment.ts | 195 +++++++----------- 1 file changed, 77 insertions(+), 118 deletions(-) diff --git a/packages/eslint-plugin/src/rules/lines-around-comment.ts b/packages/eslint-plugin/src/rules/lines-around-comment.ts index 751a78e413e4..a1373f54a2dc 100644 --- a/packages/eslint-plugin/src/rules/lines-around-comment.ts +++ b/packages/eslint-plugin/src/rules/lines-around-comment.ts @@ -268,117 +268,65 @@ export default util.createRule({ ); } - /** - * Returns whether or not comments are at the block start or not. - */ - function isCommentAtBlockStart(token: TSESTree.Token): boolean { - return ( - isCommentAtParentStart(token, AST_NODE_TYPES.ClassBody) || - isCommentAtParentStart(token, AST_NODE_TYPES.BlockStatement) || - isCommentAtParentStart(token, AST_NODE_TYPES.StaticBlock) || - isCommentAtParentStart(token, AST_NODE_TYPES.SwitchCase) - ); - } - - /** - * Returns whether or not comments are at the block end or not. - */ - function isCommentAtBlockEnd(token: TSESTree.Token): boolean { - return ( - isCommentAtParentEnd(token, AST_NODE_TYPES.ClassBody) || - isCommentAtParentEnd(token, AST_NODE_TYPES.BlockStatement) || - isCommentAtParentEnd(token, AST_NODE_TYPES.StaticBlock) || - isCommentAtParentEnd(token, AST_NODE_TYPES.SwitchCase) || - isCommentAtParentEnd(token, AST_NODE_TYPES.SwitchStatement) - ); - } - - /** - * Returns whether or not comments are at the class start or not. - */ - function isCommentAtClassStart(token: TSESTree.Token): boolean { - return isCommentAtParentStart(token, AST_NODE_TYPES.ClassBody); - } - - /** - * Returns whether or not comments are at the class end or not. - */ - function isCommentAtClassEnd(token: TSESTree.Token): boolean { - return isCommentAtParentEnd(token, AST_NODE_TYPES.ClassBody); - } - - /** - * Returns whether or not comments are at the object start or not. - */ - function isCommentAtObjectStart(token: TSESTree.Token): boolean { - return ( - isCommentAtParentStart(token, AST_NODE_TYPES.ObjectExpression) || - isCommentAtParentStart(token, AST_NODE_TYPES.ObjectPattern) - ); - } - - /** - * Returns whether or not comments are at the object end or not. - */ - function isCommentAtObjectEnd(token: TSESTree.Token): boolean { - return ( - isCommentAtParentEnd(token, AST_NODE_TYPES.ObjectExpression) || - isCommentAtParentEnd(token, AST_NODE_TYPES.ObjectPattern) - ); - } - - /** - * Returns whether or not comments are at the array start or not. - */ - function isCommentAtArrayStart(token: TSESTree.Token): boolean { - return ( - isCommentAtParentStart(token, AST_NODE_TYPES.ArrayExpression) || - isCommentAtParentStart(token, AST_NODE_TYPES.ArrayPattern) - ); - } - - /** - * Returns whether or not comments are at the array end or not. - */ - function isCommentAtArrayEnd(token: TSESTree.Token): boolean { - return ( - isCommentAtParentEnd(token, AST_NODE_TYPES.ArrayExpression) || - isCommentAtParentEnd(token, AST_NODE_TYPES.ArrayPattern) - ); - } - /** * Returns whether or not comments are at the interface start or not. */ - function isCommentAtInterfaceStart(token: TSESTree.Token): boolean { + function isCommentAtInterfaceStart(token: TSESTree.Comment): boolean { return isCommentAtParentStart(token, AST_NODE_TYPES.TSInterfaceBody); } /** * Returns whether or not comments are at the interface end or not. */ - function isCommentAtInterfaceEnd(token: TSESTree.Token): boolean { + function isCommentAtInterfaceEnd(token: TSESTree.Comment): boolean { return isCommentAtParentEnd(token, AST_NODE_TYPES.TSInterfaceBody); } /** * Returns whether or not comments are at the type start or not. */ - function isCommentAtTypeStart(token: TSESTree.Token): boolean { + function isCommentAtTypeStart(token: TSESTree.Comment): boolean { return isCommentAtParentStart(token, AST_NODE_TYPES.TSTypeLiteral); } /** * Returns whether or not comments are at the type end or not. */ - function isCommentAtTypeEnd(token: TSESTree.Token): boolean { + function isCommentAtTypeEnd(token: TSESTree.Comment): boolean { return isCommentAtParentEnd(token, AST_NODE_TYPES.TSTypeLiteral); } + function isCommentNearTSConstruct(token: TSESTree.Comment): boolean { + return ( + isCommentAtInterfaceStart(token) || + isCommentAtInterfaceEnd(token) || + isCommentAtTypeStart(token) || + isCommentAtTypeEnd(token) + ); + } + + function isCommentInsideTSConstruct(token: TSESTree.Comment): boolean { + const parent = getParentNodeOfToken(token); + if ( + parent && + (isParentNodeType(parent, AST_NODE_TYPES.TSInterfaceBody) || + isParentNodeType(parent, AST_NODE_TYPES.TSTypeLiteral) || + isParentNodeType(parent, AST_NODE_TYPES.TSModuleBlock)) + ) { + return true; + } + return false; + } + function checkForEmptyLine( token: TSESTree.Comment, opts: { before?: boolean; after?: boolean }, ): void { + // the base rule handles non-TS codes, we skip those + if (!isCommentInsideTSConstruct(token)) { + return; + } + if ( options.applyDefaultIgnorePatterns !== false && defaultIgnoreRegExp.test(token.value) @@ -401,27 +349,7 @@ export default util.createRule({ return; } - const blockStartAllowed = - Boolean(options.allowBlockStart) && - isCommentAtBlockStart(token) && - !(options.allowClassStart === false && isCommentAtClassStart(token)), - blockEndAllowed = - Boolean(options.allowBlockEnd) && - isCommentAtBlockEnd(token) && - !(options.allowClassEnd === false && isCommentAtClassEnd(token)), - classStartAllowed = - Boolean(options.allowClassStart) && isCommentAtClassStart(token), - classEndAllowed = - Boolean(options.allowClassEnd) && isCommentAtClassEnd(token), - objectStartAllowed = - Boolean(options.allowObjectStart) && isCommentAtObjectStart(token), - objectEndAllowed = - Boolean(options.allowObjectEnd) && isCommentAtObjectEnd(token), - arrayStartAllowed = - Boolean(options.allowArrayStart) && isCommentAtArrayStart(token), - arrayEndAllowed = - Boolean(options.allowArrayEnd) && isCommentAtArrayEnd(token), - interfaceStartAllowed = + const interfaceStartAllowed = Boolean(options.allowInterfaceStart) && isCommentAtInterfaceStart(token), interfaceEndAllowed = @@ -431,20 +359,8 @@ export default util.createRule({ typeEndAllowed = Boolean(options.allowTypeEnd) && isCommentAtTypeEnd(token); - const exceptionStartAllowed = - blockStartAllowed || - classStartAllowed || - objectStartAllowed || - arrayStartAllowed || - interfaceStartAllowed || - typeStartAllowed; - const exceptionEndAllowed = - blockEndAllowed || - classEndAllowed || - objectEndAllowed || - arrayEndAllowed || - interfaceEndAllowed || - typeEndAllowed; + const exceptionStartAllowed = interfaceStartAllowed || typeStartAllowed; + const exceptionEndAllowed = interfaceEndAllowed || typeEndAllowed; // ignore top of the file and bottom of the file if (prevLineNum < 1) { @@ -503,8 +419,51 @@ export default util.createRule({ } } + /** + * A custom report function for the baseRule to ignore false positive errors + * caused by TS-specific codes + */ + const customReport: typeof context.report = descriptor => { + if ('node' in descriptor) { + if ( + descriptor.node.type !== AST_TOKEN_TYPES.Line && + descriptor.node.type !== AST_TOKEN_TYPES.Block + ) { + throw new TypeError( + 'The node reported by the base rule must always be a Comment token', + ); + } + + if (isCommentNearTSConstruct(descriptor.node)) { + return; + } + } + return context.report(descriptor); + }; + + const customContext = { report: customReport }; + + // we can't directly proxy context because it's `report` property is non-configurable + // and non-writable. So we proxy on a customContext and redirect all other + // property access to the original context except for `report` + const proxiedContext = new Proxy( + customContext as typeof context, + { + get(target, path, receiver): unknown { + if (path !== 'report') { + return Reflect.get(context, path, receiver); + } + return Reflect.get(target, path, receiver); + }, + }, + ); + + const rules = baseRule.create(proxiedContext); + return { Program(): void { + rules.Program(); + comments.forEach(token => { if (token.type === AST_TOKEN_TYPES.Line) { if (options.beforeLineComment || options.afterLineComment) { From 793d2e7984c0a1c93efb547463ec0f84ff161f66 Mon Sep 17 00:00:00 2001 From: chbdetta Date: Sun, 25 Sep 2022 01:45:10 -0700 Subject: [PATCH 09/21] fix lint errors --- packages/eslint-plugin/src/rules/lines-around-comment.ts | 8 +++----- .../tests/rules/lines-around-comment.test.ts | 5 +++-- packages/eslint-plugin/typings/eslint-rules.d.ts | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/eslint-plugin/src/rules/lines-around-comment.ts b/packages/eslint-plugin/src/rules/lines-around-comment.ts index a1373f54a2dc..51e880e8054c 100644 --- a/packages/eslint-plugin/src/rules/lines-around-comment.ts +++ b/packages/eslint-plugin/src/rules/lines-around-comment.ts @@ -1,9 +1,7 @@ +import type { TSESTree } from '@typescript-eslint/utils'; +import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; + import * as util from '../util'; -import { - AST_NODE_TYPES, - AST_TOKEN_TYPES, - TSESTree, -} from '@typescript-eslint/utils'; import { getESLintCoreRule } from '../util/getESLintCoreRule'; const baseRule = getESLintCoreRule('lines-around-comment'); diff --git a/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts b/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts index 1f2472066208..bc055b3091dd 100644 --- a/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts +++ b/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts @@ -1,7 +1,8 @@ -import rule from '../../src/rules/lines-around-comment'; -import { unIndent } from './indent/utils'; import { AST_TOKEN_TYPES } from '@typescript-eslint/utils'; + +import rule from '../../src/rules/lines-around-comment'; import { noFormat, RuleTester } from '../RuleTester'; +import { unIndent } from './indent/utils'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser', diff --git a/packages/eslint-plugin/typings/eslint-rules.d.ts b/packages/eslint-plugin/typings/eslint-rules.d.ts index dbc23fd6bff3..e8564aef6361 100644 --- a/packages/eslint-plugin/typings/eslint-rules.d.ts +++ b/packages/eslint-plugin/typings/eslint-rules.d.ts @@ -664,7 +664,7 @@ declare module 'eslint/lib/rules/no-extra-semi' { } declare module 'eslint/lib/rules/lines-around-comment' { - import { TSESLint } from '@typescript-eslint/utils'; + import type { TSESLint } from '@typescript-eslint/utils'; const rule: TSESLint.RuleModule< 'after' | 'before', From 3b742310a46a9fdb4fd1fa51c4e533c24f33f2ae Mon Sep 17 00:00:00 2001 From: chbdetta Date: Sun, 25 Sep 2022 01:50:15 -0700 Subject: [PATCH 10/21] fix doc lint errors --- packages/eslint-plugin/docs/rules/lines-around-comment.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/lines-around-comment.md b/packages/eslint-plugin/docs/rules/lines-around-comment.md index ff07edfb23ee..602503230328 100644 --- a/packages/eslint-plugin/docs/rules/lines-around-comment.md +++ b/packages/eslint-plugin/docs/rules/lines-around-comment.md @@ -1,6 +1,10 @@ -# `lines-around-comment` +--- +description: 'Require empty lines around comments.' +--- -Requires empty lines around comments. +> 🛑 This file is source code, not the primary documentation location! 🛑 +> +> See **https://typescript-eslint.io/rules/lines-around-comment** for documentation. ## Rule Details From b367c615cc975fa76fb70b438f8dc8ca05850bd3 Mon Sep 17 00:00:00 2001 From: chbdetta Date: Sun, 25 Sep 2022 01:54:04 -0700 Subject: [PATCH 11/21] address comments --- .../src/rules/lines-around-comment.ts | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/packages/eslint-plugin/src/rules/lines-around-comment.ts b/packages/eslint-plugin/src/rules/lines-around-comment.ts index 51e880e8054c..3e38c4e52cc1 100644 --- a/packages/eslint-plugin/src/rules/lines-around-comment.ts +++ b/packages/eslint-plugin/src/rules/lines-around-comment.ts @@ -266,30 +266,18 @@ export default util.createRule({ ); } - /** - * Returns whether or not comments are at the interface start or not. - */ function isCommentAtInterfaceStart(token: TSESTree.Comment): boolean { return isCommentAtParentStart(token, AST_NODE_TYPES.TSInterfaceBody); } - /** - * Returns whether or not comments are at the interface end or not. - */ function isCommentAtInterfaceEnd(token: TSESTree.Comment): boolean { return isCommentAtParentEnd(token, AST_NODE_TYPES.TSInterfaceBody); } - /** - * Returns whether or not comments are at the type start or not. - */ function isCommentAtTypeStart(token: TSESTree.Comment): boolean { return isCommentAtParentStart(token, AST_NODE_TYPES.TSTypeLiteral); } - /** - * Returns whether or not comments are at the type end or not. - */ function isCommentAtTypeEnd(token: TSESTree.Comment): boolean { return isCommentAtParentEnd(token, AST_NODE_TYPES.TSTypeLiteral); } @@ -318,7 +306,7 @@ export default util.createRule({ function checkForEmptyLine( token: TSESTree.Comment, - opts: { before?: boolean; after?: boolean }, + { before, after }: { before?: boolean; after?: boolean }, ): void { // the base rule handles non-TS codes, we skip those if (!isCommentInsideTSConstruct(token)) { @@ -336,9 +324,6 @@ export default util.createRule({ return; } - let after = opts.after, - before = opts.before; - const prevLineNum = token.loc.start.line - 1; const nextLineNum = token.loc.end.line + 1; From b763e3abeae75a1f951dbee044c689d10de41042 Mon Sep 17 00:00:00 2001 From: chbdetta Date: Sun, 25 Sep 2022 01:59:36 -0700 Subject: [PATCH 12/21] address jsdoc comments --- .../src/rules/lines-around-comment.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/eslint-plugin/src/rules/lines-around-comment.ts b/packages/eslint-plugin/src/rules/lines-around-comment.ts index 3e38c4e52cc1..4b83138ab3c1 100644 --- a/packages/eslint-plugin/src/rules/lines-around-comment.ts +++ b/packages/eslint-plugin/src/rules/lines-around-comment.ts @@ -13,7 +13,7 @@ const COMMENTS_IGNORE_PATTERN = /^\s*(?:eslint|jshint\s+|jslint\s+|istanbul\s+|globals?\s+|exported\s+|jscs)/u; /** - * Return an array with with any line numbers that are empty. + * @returns an array with with any line numbers that are empty. */ function getEmptyLineNums(lines: string[]): number[] { const emptyLines = lines @@ -28,7 +28,7 @@ function getEmptyLineNums(lines: string[]): number[] { } /** - * Return an array with with any line numbers that contain comments. + * @returns an array with with any line numbers that contain comments. */ function getCommentLineNums(comments: TSESTree.Comment[]): number[] { const lines: number[] = []; @@ -146,7 +146,7 @@ export default util.createRule({ const commentAndEmptyLines = new Set(commentLines.concat(emptyLines)); /** - * Returns whether or not comments are on lines starting with or ending with code + * @returns whether or not comments are on lines starting with or ending with code */ function codeAroundComment(token: TSESTree.Token): boolean { let currentToken: TSESTree.Token | null = token; @@ -176,7 +176,7 @@ export default util.createRule({ } /** - * Returns whether or not comments are inside a node type or not. + * @returns whether or not comments are inside a node type or not. */ function isParentNodeType( parent: TSESTree.Node, @@ -194,7 +194,7 @@ export default util.createRule({ } /** - * Returns the parent node that contains the given token. + * @returns the parent node that contains the given token. */ function getParentNodeOfToken(token: TSESTree.Token): TSESTree.Node | null { const node = sourceCode.getNodeByRangeIndex(token.range[0]); @@ -228,7 +228,7 @@ export default util.createRule({ } /** - * Returns whether or not comments are at the parent start or not. + * @returns whether or not comments are at the parent start or not. */ function isCommentAtParentStart( token: TSESTree.Token, @@ -251,7 +251,7 @@ export default util.createRule({ } /** - * Returns whether or not comments are at the parent end or not. + * @returns whether or not comments are at the parent end or not. */ function isCommentAtParentEnd( token: TSESTree.Token, @@ -426,8 +426,8 @@ export default util.createRule({ const customContext = { report: customReport }; - // we can't directly proxy context because it's `report` property is non-configurable - // and non-writable. So we proxy on a customContext and redirect all other + // we can't directly proxy `context` because its `report` property is non-configurable + // and non-writable. So we proxy `customContext` and redirect all // property access to the original context except for `report` const proxiedContext = new Proxy( customContext as typeof context, From 59541edcbb1eaf8893ca15c330e95c618efb2bfa Mon Sep 17 00:00:00 2001 From: chbdetta Date: Sun, 25 Sep 2022 02:01:42 -0700 Subject: [PATCH 13/21] proper comment language --- packages/eslint-plugin/src/rules/lines-around-comment.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/eslint-plugin/src/rules/lines-around-comment.ts b/packages/eslint-plugin/src/rules/lines-around-comment.ts index 4b83138ab3c1..72adda888315 100644 --- a/packages/eslint-plugin/src/rules/lines-around-comment.ts +++ b/packages/eslint-plugin/src/rules/lines-around-comment.ts @@ -146,7 +146,7 @@ export default util.createRule({ const commentAndEmptyLines = new Set(commentLines.concat(emptyLines)); /** - * @returns whether or not comments are on lines starting with or ending with code + * @returns whether comments are on lines starting with or ending with code. */ function codeAroundComment(token: TSESTree.Token): boolean { let currentToken: TSESTree.Token | null = token; @@ -176,7 +176,7 @@ export default util.createRule({ } /** - * @returns whether or not comments are inside a node type or not. + * @returns whether comments are inside a node type. */ function isParentNodeType( parent: TSESTree.Node, @@ -228,7 +228,7 @@ export default util.createRule({ } /** - * @returns whether or not comments are at the parent start or not. + * @returns whether comments are at the parent start. */ function isCommentAtParentStart( token: TSESTree.Token, @@ -251,7 +251,7 @@ export default util.createRule({ } /** - * @returns whether or not comments are at the parent end or not. + * @returns whether comments are at the parent end. */ function isCommentAtParentEnd( token: TSESTree.Token, From f92776d9b1ee8aaa3d9578cdc3fce0034ac1aef2 Mon Sep 17 00:00:00 2001 From: Hayden Date: Sat, 8 Oct 2022 22:17:58 -0700 Subject: [PATCH 14/21] Update packages/eslint-plugin/src/rules/lines-around-comment.ts Co-authored-by: Brad Zacher --- .../eslint-plugin/src/rules/lines-around-comment.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/eslint-plugin/src/rules/lines-around-comment.ts b/packages/eslint-plugin/src/rules/lines-around-comment.ts index 72adda888315..2d7d71cbdf8b 100644 --- a/packages/eslint-plugin/src/rules/lines-around-comment.ts +++ b/packages/eslint-plugin/src/rules/lines-around-comment.ts @@ -334,12 +334,12 @@ export default util.createRule({ const interfaceStartAllowed = Boolean(options.allowInterfaceStart) && - isCommentAtInterfaceStart(token), - interfaceEndAllowed = - Boolean(options.allowInterfaceEnd) && isCommentAtInterfaceEnd(token), - typeStartAllowed = - Boolean(options.allowTypeStart) && isCommentAtTypeStart(token), - typeEndAllowed = + isCommentAtInterfaceStart(token); + const interfaceEndAllowed = + Boolean(options.allowInterfaceEnd) && isCommentAtInterfaceEnd(token); + const typeStartAllowed = + Boolean(options.allowTypeStart) && isCommentAtTypeStart(token); + const typeEndAllowed = Boolean(options.allowTypeEnd) && isCommentAtTypeEnd(token); const exceptionStartAllowed = interfaceStartAllowed || typeStartAllowed; From 01371be77a51251a8b8f5bcd9a476b8205a49404 Mon Sep 17 00:00:00 2001 From: chbdetta Date: Sun, 9 Oct 2022 19:47:35 -0700 Subject: [PATCH 15/21] remove irrevalant lines and improve test cov --- .../src/rules/lines-around-comment.ts | 14 +-- .../tests/rules/lines-around-comment.test.ts | 117 ++++++++++++++++++ 2 files changed, 118 insertions(+), 13 deletions(-) diff --git a/packages/eslint-plugin/src/rules/lines-around-comment.ts b/packages/eslint-plugin/src/rules/lines-around-comment.ts index 72adda888315..39829336bb53 100644 --- a/packages/eslint-plugin/src/rules/lines-around-comment.ts +++ b/packages/eslint-plugin/src/rules/lines-around-comment.ts @@ -140,7 +140,6 @@ export default util.createRule({ const comments = sourceCode.getAllComments(); const lines = sourceCode.lines; - const numLines = lines.length + 1; const commentLines = getCommentLineNums(comments); const emptyLines = getEmptyLineNums(lines); const commentAndEmptyLines = new Set(commentLines.concat(emptyLines)); @@ -237,10 +236,7 @@ export default util.createRule({ const parent = getParentNodeOfToken(token); if (parent && isParentNodeType(parent, nodeType)) { - const parentStartNodeOrToken = - parent.type === AST_NODE_TYPES.StaticBlock - ? sourceCode.getFirstToken(parent, { skip: 1 })! // opening brace of the static block - : parent; + const parentStartNodeOrToken = parent; return ( token.loc.start.line - parentStartNodeOrToken.loc.start.line === 1 @@ -345,14 +341,6 @@ export default util.createRule({ const exceptionStartAllowed = interfaceStartAllowed || typeStartAllowed; const exceptionEndAllowed = interfaceEndAllowed || typeEndAllowed; - // ignore top of the file and bottom of the file - if (prevLineNum < 1) { - before = false; - } - if (nextLineNum >= numLines) { - after = false; - } - const previousTokenOrComment = sourceCode.getTokenBefore(token, { includeComments: true, }); diff --git a/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts b/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts index bc055b3091dd..41be43ea8851 100644 --- a/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts +++ b/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts @@ -1418,6 +1418,123 @@ foo; `, options: [{ applyDefaultIgnorePatterns: false, ignorePattern: 'pragma' }], }, + { + code: ` +interface A { + foo: string; + /* eslint */ +} + `, + options: [{ applyDefaultIgnorePatterns: true }], + }, + { + code: ` +interface A { + foo: string; + /* jshint foo */ +} + `, + options: [{ applyDefaultIgnorePatterns: true }], + }, + { + code: ` +interface A { + foo: string; + /* jslint foo */ +} + `, + options: [{ applyDefaultIgnorePatterns: true }], + }, + { + code: ` +interface A { + foo: string; + /* istanbul foo */ +} + `, + options: [{ applyDefaultIgnorePatterns: true }], + }, + { + code: ` +interface A { + foo: string; + /* globals foo */ +} + `, + options: [{ applyDefaultIgnorePatterns: true }], + }, + { + code: ` +interface A { + foo: string; + /* exported foo */ +} + `, + options: [{ applyDefaultIgnorePatterns: true }], + }, + { + code: ` +interface A { + foo: string; + /* jscs */ +} + `, + options: [{ applyDefaultIgnorePatterns: true }], + }, + { + code: ` +interface A { + foo: string; + /* this is pragmatic */ +} + `, + options: [{ ignorePattern: 'pragma' }], + }, + { + code: ` +interface A { + foo: string; + /* this is pragmatic */ +} + `, + options: [{ applyDefaultIgnorePatterns: false, ignorePattern: 'pragma' }], + }, + { + code: ` +interface A { + foo: string; // this is inline line comment +} + `, + options: [{ beforeLineComment: true }], + }, + { + code: ` +interface A { + foo: string /* this is inline block comment */; +} + `, + }, + { + code: ` +interface A { + /* this is inline block comment */ foo: string; +} + `, + }, + { + code: ` +interface A { + /* this is inline block comment */ foo: string /* this is inline block comment */; +} + `, + }, + { + code: ` +interface A { + /* this is inline block comment */ foo: string; // this is inline line comment ; +} + `, + }, ], invalid: [ // default rules From 7a3943743c5ea2feb793f667d54787de0e3afc7e Mon Sep 17 00:00:00 2001 From: chbdetta Date: Sun, 9 Oct 2022 19:50:06 -0700 Subject: [PATCH 16/21] skip more cases --- .../src/rules/lines-around-comment.ts | 17 +-- .../tests/rules/lines-around-comment.test.ts | 115 ++++++------------ 2 files changed, 42 insertions(+), 90 deletions(-) diff --git a/packages/eslint-plugin/src/rules/lines-around-comment.ts b/packages/eslint-plugin/src/rules/lines-around-comment.ts index 39829336bb53..101e0104b20a 100644 --- a/packages/eslint-plugin/src/rules/lines-around-comment.ts +++ b/packages/eslint-plugin/src/rules/lines-around-comment.ts @@ -287,25 +287,12 @@ export default util.createRule({ ); } - function isCommentInsideTSConstruct(token: TSESTree.Comment): boolean { - const parent = getParentNodeOfToken(token); - if ( - parent && - (isParentNodeType(parent, AST_NODE_TYPES.TSInterfaceBody) || - isParentNodeType(parent, AST_NODE_TYPES.TSTypeLiteral) || - isParentNodeType(parent, AST_NODE_TYPES.TSModuleBlock)) - ) { - return true; - } - return false; - } - function checkForEmptyLine( token: TSESTree.Comment, { before, after }: { before?: boolean; after?: boolean }, ): void { - // the base rule handles non-TS codes, we skip those - if (!isCommentInsideTSConstruct(token)) { + // the base rule handles comments away from TS constructs blocks correctly, we skip those + if (!isCommentNearTSConstruct(token)) { return; } diff --git a/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts b/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts index 41be43ea8851..6e92438ca4b6 100644 --- a/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts +++ b/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts @@ -1383,12 +1383,14 @@ type A = { // ignorePattern { code: - 'foo;\n\n' + + 'interface A {' + + 'foo: string;\n\n' + '/* eslint-disable no-underscore-dangle */\n\n' + - 'this._values = values;\n' + - 'this._values2 = true;\n' + + '_values: 2;\n' + + '_values2: true;\n' + '/* eslint-enable no-underscore-dangle */\n' + - 'bar', + 'bar: boolean' + + '}', options: [ { beforeBlockComment: true, @@ -1396,95 +1398,58 @@ type A = { }, ], }, - 'foo;\n/* eslint */', - 'foo;\n/* jshint */', - 'foo;\n/* jslint */', - 'foo;\n/* istanbul */', - 'foo;\n/* global */', - 'foo;\n/* globals */', - 'foo;\n/* exported */', - 'foo;\n/* jscs */', - { - code: ` -foo; -/* this is pragmatic */ - `, - options: [{ ignorePattern: 'pragma' }], - }, - { - code: ` -foo; -/* this is pragmatic */ - `, - options: [{ applyDefaultIgnorePatterns: false, ignorePattern: 'pragma' }], - }, - { - code: ` + ` interface A { - foo: string; + foo; /* eslint */ } - `, - options: [{ applyDefaultIgnorePatterns: true }], - }, - { - code: ` + `, + ` interface A { - foo: string; - /* jshint foo */ + foo; + /* jshint */ } - `, - options: [{ applyDefaultIgnorePatterns: true }], - }, - { - code: ` + `, + ` interface A { - foo: string; - /* jslint foo */ + foo; + /* jslint */ } - `, - options: [{ applyDefaultIgnorePatterns: true }], - }, - { - code: ` + `, + ` interface A { - foo: string; - /* istanbul foo */ + foo; + /* istanbul */ } - `, - options: [{ applyDefaultIgnorePatterns: true }], - }, - { - code: ` + `, + ` interface A { - foo: string; - /* globals foo */ + foo; + /* global */ } - `, - options: [{ applyDefaultIgnorePatterns: true }], - }, - { - code: ` + `, + ` interface A { - foo: string; - /* exported foo */ + foo; + /* globals */ } - `, - options: [{ applyDefaultIgnorePatterns: true }], - }, - { - code: ` + `, + ` interface A { - foo: string; + foo; + /* exported */ +} + `, + ` +interface A { + foo; /* jscs */ } - `, - options: [{ applyDefaultIgnorePatterns: true }], - }, + `, { code: ` interface A { - foo: string; + foo: boolean; /* this is pragmatic */ } `, @@ -1493,7 +1458,7 @@ interface A { { code: ` interface A { - foo: string; + foo; /* this is pragmatic */ } `, From 567420a031df3467951ea6626369f96b20a336eb Mon Sep 17 00:00:00 2001 From: chbdetta Date: Sun, 9 Oct 2022 23:44:52 -0700 Subject: [PATCH 17/21] add enum and module tests --- .../tests/rules/lines-around-comment.test.ts | 402 ++++++++++++++++++ 1 file changed, 402 insertions(+) diff --git a/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts b/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts index 6e92438ca4b6..b99c05399cb4 100644 --- a/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts +++ b/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts @@ -1380,6 +1380,127 @@ type A = { ], }, + // Enum + { + code: unIndent` +enum A { + // line + a, +} +`, + options: [ + { + beforeLineComment: true, + allowEnumStart: true, + }, + ], + }, + { + code: unIndent` +enum A { + /* block + comment */ + a, +} +`, + options: [ + { + beforeBlockComment: true, + allowEnumStart: true, + }, + ], + }, + { + code: unIndent` +enum A { + a, + // line +} +`, + options: [ + { + afterLineComment: true, + allowEnumEnd: true, + }, + ], + }, + { + code: unIndent` +enum A { + a, + /* block + comment */ +} +`, + options: [ + { + beforeBlockComment: false, + afterBlockComment: true, + allowEnumEnd: true, + }, + ], + }, + + // TS module + { + code: unIndent` +declare module A { + // line + const a: string; +} +`, + options: [ + { + beforeLineComment: true, + allowModuleStart: true, + }, + ], + }, + { + code: unIndent` +declare module A { + /* block + comment */ + const a: string; +} +`, + options: [ + { + beforeBlockComment: true, + allowModuleStart: true, + }, + ], + }, + { + code: unIndent` +declare module A { + const a: string; + // line +} +`, + options: [ + { + afterLineComment: true, + allowModuleEnd: true, + }, + ], + }, + { + code: unIndent` +declare module A { + const a: string; + /* block + comment */ +} +`, + options: [ + { + beforeBlockComment: false, + afterBlockComment: true, + allowModuleEnd: true, + }, + ], + }, // ignorePattern { code: @@ -3132,6 +3253,287 @@ type A = { errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 3 }], }, + // Enum + { + code: unIndent` +enum A { + a, + // line +} +`, + output: unIndent` +enum A { + a, + + // line +} +`, + options: [ + { + beforeLineComment: true, + allowEnumStart: true, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 3 }], + }, + { + code: unIndent` +enum A { + a, + /* block + comment */ +} +`, + output: unIndent` +enum A { + a, + + /* block + comment */ +} +`, + options: [ + { + beforeBlockComment: true, + allowEnumStart: true, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 3 }], + }, + { + code: unIndent` +enum A { + // line + a, +} +`, + output: unIndent` +enum A { + + // line + a, +} +`, + options: [ + { + beforeLineComment: true, + allowEnumStart: false, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 2 }], + }, + { + code: unIndent` +enum A { + /* block + comment */ + a, +} +`, + output: unIndent` +enum A { + + /* block + comment */ + a, +} +`, + options: [ + { + beforeBlockComment: true, + allowEnumStart: false, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 2 }], + }, + { + code: unIndent` +enum A { + a, + // line +} +`, + output: unIndent` +enum A { + a, + // line + +} +`, + options: [ + { + afterLineComment: true, + allowEnumEnd: false, + }, + ], + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 3 }], + }, + { + code: unIndent` +enum A { + a, + /* block + comment */ +} +`, + output: unIndent` +enum A { + a, + /* block + comment */ + +} +`, + options: [ + { + beforeBlockComment: false, + afterBlockComment: true, + allowEnumEnd: false, + }, + ], + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 3 }], + }, + + // TS module + { + code: unIndent` +module A { + const a: string; + // line +} +`, + output: unIndent` +module A { + const a: string; + + // line +} +`, + options: [ + { + beforeLineComment: true, + allowModuleStart: true, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 3 }], + }, + { + code: unIndent` +module A { + const a: string; + /* block + comment */ +} +`, + output: unIndent` +module A { + const a: string; + + /* block + comment */ +} +`, + options: [ + { + beforeBlockComment: true, + allowModuleStart: true, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 3 }], + }, + { + code: unIndent` +module A { + // line + const a: string; +} +`, + output: unIndent` +module A { + + // line + const a: string; +} +`, + options: [ + { + beforeLineComment: true, + allowModuleStart: false, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 2 }], + }, + { + code: unIndent` +module A { + /* block + comment */ + const a: string; +} +`, + output: unIndent` +module A { + + /* block + comment */ + const a: string; +} +`, + options: [ + { + beforeBlockComment: true, + allowModuleStart: false, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 2 }], + }, + { + code: unIndent` +module A { + const a: string; + // line +} +`, + output: unIndent` +module A { + const a: string; + // line + +} +`, + options: [ + { + afterLineComment: true, + allowModuleEnd: false, + }, + ], + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 3 }], + }, + { + code: unIndent` +module A { + const a: string; + /* block + comment */ +} +`, + output: unIndent` +module A { + const a: string; + /* block + comment */ + +} +`, + options: [ + { + beforeBlockComment: false, + afterBlockComment: true, + allowModuleEnd: false, + }, + ], + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 3 }], + }, // ignorePattern { code: From 9cf58f2f440576447ddb065d3c5b6e57489207de Mon Sep 17 00:00:00 2001 From: chbdetta Date: Mon, 10 Oct 2022 01:11:00 -0700 Subject: [PATCH 18/21] implement enum and module --- .../src/rules/lines-around-comment.ts | 72 +++++++++++++++---- .../eslint-plugin/typings/eslint-rules.d.ts | 4 ++ 2 files changed, 64 insertions(+), 12 deletions(-) diff --git a/packages/eslint-plugin/src/rules/lines-around-comment.ts b/packages/eslint-plugin/src/rules/lines-around-comment.ts index 101e0104b20a..ffad8798a841 100644 --- a/packages/eslint-plugin/src/rules/lines-around-comment.ts +++ b/packages/eslint-plugin/src/rules/lines-around-comment.ts @@ -111,6 +111,18 @@ export default util.createRule({ allowTypeEnd: { type: 'boolean', }, + allowEnumStart: { + type: 'boolean', + }, + allowEnumEnd: { + type: 'boolean', + }, + allowModuleStart: { + type: 'boolean', + }, + allowModuleEnd: { + type: 'boolean', + }, ignorePattern: { type: 'string', }, @@ -278,12 +290,32 @@ export default util.createRule({ return isCommentAtParentEnd(token, AST_NODE_TYPES.TSTypeLiteral); } + function isCommentAtEnumStart(token: TSESTree.Comment): boolean { + return isCommentAtParentStart(token, AST_NODE_TYPES.TSEnumDeclaration); + } + + function isCommentAtEnumEnd(token: TSESTree.Comment): boolean { + return isCommentAtParentEnd(token, AST_NODE_TYPES.TSEnumDeclaration); + } + + function isCommentAtModuleStart(token: TSESTree.Comment): boolean { + return isCommentAtParentStart(token, AST_NODE_TYPES.TSModuleBlock); + } + + function isCommentAtModuleEnd(token: TSESTree.Comment): boolean { + return isCommentAtParentEnd(token, AST_NODE_TYPES.TSModuleBlock); + } + function isCommentNearTSConstruct(token: TSESTree.Comment): boolean { return ( isCommentAtInterfaceStart(token) || isCommentAtInterfaceEnd(token) || isCommentAtTypeStart(token) || - isCommentAtTypeEnd(token) + isCommentAtTypeEnd(token) || + isCommentAtEnumStart(token) || + isCommentAtEnumEnd(token) || + isCommentAtModuleStart(token) || + isCommentAtModuleEnd(token) ); } @@ -316,17 +348,33 @@ export default util.createRule({ } const interfaceStartAllowed = - Boolean(options.allowInterfaceStart) && - isCommentAtInterfaceStart(token), - interfaceEndAllowed = - Boolean(options.allowInterfaceEnd) && isCommentAtInterfaceEnd(token), - typeStartAllowed = - Boolean(options.allowTypeStart) && isCommentAtTypeStart(token), - typeEndAllowed = - Boolean(options.allowTypeEnd) && isCommentAtTypeEnd(token); - - const exceptionStartAllowed = interfaceStartAllowed || typeStartAllowed; - const exceptionEndAllowed = interfaceEndAllowed || typeEndAllowed; + Boolean(options.allowInterfaceStart) && + isCommentAtInterfaceStart(token); + const interfaceEndAllowed = + Boolean(options.allowInterfaceEnd) && isCommentAtInterfaceEnd(token); + const typeStartAllowed = + Boolean(options.allowTypeStart) && isCommentAtTypeStart(token); + const typeEndAllowed = + Boolean(options.allowTypeEnd) && isCommentAtTypeEnd(token); + const enumStartAllowed = + Boolean(options.allowEnumStart) && isCommentAtEnumStart(token); + const enumEndAllowed = + Boolean(options.allowEnumEnd) && isCommentAtEnumEnd(token); + const moduleStartAllowed = + Boolean(options.allowModuleStart) && isCommentAtModuleStart(token); + const moduleEndAllowed = + Boolean(options.allowModuleEnd) && isCommentAtModuleEnd(token); + + const exceptionStartAllowed = + interfaceStartAllowed || + typeStartAllowed || + enumStartAllowed || + moduleStartAllowed; + const exceptionEndAllowed = + interfaceEndAllowed || + typeEndAllowed || + enumEndAllowed || + moduleEndAllowed; const previousTokenOrComment = sourceCode.getTokenBefore(token, { includeComments: true, diff --git a/packages/eslint-plugin/typings/eslint-rules.d.ts b/packages/eslint-plugin/typings/eslint-rules.d.ts index e8564aef6361..a0d737051ebe 100644 --- a/packages/eslint-plugin/typings/eslint-rules.d.ts +++ b/packages/eslint-plugin/typings/eslint-rules.d.ts @@ -686,6 +686,10 @@ declare module 'eslint/lib/rules/lines-around-comment' { allowInterfaceEnd?: boolean; allowTypeStart?: boolean; allowTypeEnd?: boolean; + allowEnumStart?: boolean; + allowEnumEnd?: boolean; + allowModuleStart?: boolean; + allowModuleEnd?: boolean; ignorePattern?: string; applyDefaultIgnorePatterns?: boolean; }?, From 88584c6a87a48d7a65b8b58322a07fd9a5cce97d Mon Sep 17 00:00:00 2001 From: chbdetta Date: Mon, 10 Oct 2022 02:33:47 -0700 Subject: [PATCH 19/21] improve coverage --- .../src/rules/lines-around-comment.ts | 10 +- .../tests/rules/lines-around-comment.test.ts | 92 +++++++++++++++++++ 2 files changed, 93 insertions(+), 9 deletions(-) diff --git a/packages/eslint-plugin/src/rules/lines-around-comment.ts b/packages/eslint-plugin/src/rules/lines-around-comment.ts index ffad8798a841..34e61a0044ce 100644 --- a/packages/eslint-plugin/src/rules/lines-around-comment.ts +++ b/packages/eslint-plugin/src/rules/lines-around-comment.ts @@ -193,15 +193,7 @@ export default util.createRule({ parent: TSESTree.Node, nodeType: T, ): parent is Extract { - return ( - parent.type === nodeType || - ('body' in parent && - !Array.isArray(parent.body) && - parent.body?.type === nodeType) || - ('consequent' in parent && - !Array.isArray(parent.consequent) && - parent.consequent.type === nodeType) - ); + return parent.type === nodeType; } /** diff --git a/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts b/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts index b99c05399cb4..a02944dd85f6 100644 --- a/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts +++ b/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts @@ -3534,6 +3534,98 @@ module A { ], errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 3 }], }, + + // multiple comments in one line + { + code: unIndent` +interface A { + a: string; + /* block */ /* block */ +} +`, + output: unIndent` +interface A { + a: string; + + /* block */ /* block */ +} +`, + options: [ + { + beforeBlockComment: true, + allowInterfaceEnd: false, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 3 }], + }, + { + code: unIndent` +interface A { + a: string; + /* block */ // line +} +`, + output: unIndent` +interface A { + a: string; + + /* block */ // line +} +`, + options: [ + { + beforeBlockComment: true, + allowInterfaceEnd: false, + }, + ], + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 3 }], + }, + { + code: unIndent` +interface A { + /* block */ /* block */ + a: string; +} +`, + output: unIndent` +interface A { + /* block */ /* block */ + + a: string; +} +`, + options: [ + { + beforeBlockComment: false, + afterBlockComment: true, + allowInterfaceStart: false, + }, + ], + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 2 }], + }, + { + code: unIndent` +interface A { + /* block */ // line + a: string; +} +`, + output: unIndent` +interface A { + /* block */ // line + + a: string; +} +`, + options: [ + { + beforeBlockComment: false, + afterLineComment: true, + allowInterfaceStart: false, + }, + ], + errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 2 }], + }, // ignorePattern { code: From b6b475ab7a3ff1ccfcd672020f401d95d030ddca Mon Sep 17 00:00:00 2001 From: chbdetta Date: Tue, 24 Jan 2023 00:33:12 -0800 Subject: [PATCH 20/21] remove ESilnt base rule test casese --- .../tests/rules/lines-around-comment.test.ts | 3169 ++--------------- 1 file changed, 230 insertions(+), 2939 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts b/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts index a02944dd85f6..ce2a7f6702bf 100644 --- a/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts +++ b/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts @@ -10,2967 +10,370 @@ const ruleTester = new RuleTester({ ruleTester.run('lines-around-comment', rule, { valid: [ - // default rules - ` -bar(); - -/** block block block - * block - */ - -var a = 1; - `, - ` -bar(); - -/** block block block - * block - */ -var a = 1; - `, - ` -bar(); -// line line line -var a = 1; - `, - ` -bar(); - -// line line line -var a = 1; - `, - ` -bar(); -// line line line - -var a = 1; - `, - - // line comments - { - code: ` -bar(); -// line line line - -var a = 1; - `, - options: [{ afterLineComment: true }], - }, - { - code: ` -foo(); - -// line line line -var a = 1; - `, - options: [{ beforeLineComment: true }], - }, - { - code: ` -foo(); - -// line line line - -var a = 1; - `, - options: [{ beforeLineComment: true, afterLineComment: true }], - }, - { - code: ` -foo(); - -// line line line -// line line - -var a = 1; - `, - options: [{ beforeLineComment: true, afterLineComment: true }], - }, - { - code: '// line line line\n// line line', - options: [{ beforeLineComment: true, afterLineComment: true }], - }, - - // block comments - { - code: ` -bar(); - -/** A Block comment with a an empty line after - * - */ -var a = 1; - `, - options: [{ afterBlockComment: false, beforeBlockComment: true }], - }, - { - code: ` -bar(); - -/** block block block - * block - */ -var a = 1; - `, - options: [{ afterBlockComment: false }], - }, - { - code: '/** \nblock \nblock block\n */\n/* block \n block \n */', - options: [{ afterBlockComment: true, beforeBlockComment: true }], - }, - { - code: ` -bar(); - -/** block block block - * block - */ - -var a = 1; - `, - options: [{ afterBlockComment: true, beforeBlockComment: true }], - }, - - // inline comments (should not ever warn) - { - code: ` -foo(); // An inline comment with a an empty line after -var a = 1; - `, - options: [{ afterLineComment: true, beforeLineComment: true }], - }, - { - code: noFormat` -foo(); -bar(); /* An inline block comment with a an empty line after - * - */ -var a = 1; - `, - options: [{ beforeBlockComment: true }], - }, - - // mixed comment (some block & some line) - { - code: ` -bar(); - -/** block block block - * block - */ -//line line line -var a = 1; - `, - options: [{ afterBlockComment: true }], - }, - { - code: ` -bar(); - -/** block block block - * block - */ -//line line line -var a = 1; - `, - options: [{ beforeLineComment: true }], - }, - - // check for block start comments - { - code: unIndent` -var a, - -// line -b; - `, - options: [ - { - beforeLineComment: true, - allowBlockStart: true, - }, - ], - }, - { - code: ` -function foo() { - // line at block start - var g = 1; -} - `, - options: [ - { - beforeLineComment: true, - allowBlockStart: true, - }, - ], - }, - { - code: ` -var foo = function () { - // line at block start - var g = 1; -}; - `, - options: [ - { - beforeLineComment: true, - allowBlockStart: true, - }, - ], - }, - { - code: ` -var foo = function () { - // line at block start -}; - `, - options: [ - { - beforeLineComment: true, - allowBlockStart: true, - }, - ], - }, - { - code: ` -if (true) { - // line at block start - var g = 1; -} - `, - options: [ - { - beforeLineComment: true, - allowBlockStart: true, - }, - ], - }, - { - code: ` -if (true) { - // line at block start -} - `, - options: [ - { - beforeLineComment: true, - allowBlockStart: true, - }, - ], - }, - { - code: ` -if (true) { - bar(); -} else { - // line at block start -} - `, - options: [ - { - beforeLineComment: true, - allowBlockStart: true, - }, - ], - }, - { - code: ` -switch ('foo') { - case 'foo': - // line at switch case start - break; -} - `, - options: [ - { - beforeLineComment: true, - allowBlockStart: true, - }, - ], - }, - { - code: ` -switch ('foo') { - case 'foo': - break; - - default: - // line at switch case start - break; -} - `, - options: [ - { - beforeLineComment: true, - allowBlockStart: true, - }, - ], - }, - { - code: ` -function foo() { - /* block comment at block start */ - var g = 1; -} - `, - options: [ - { - allowBlockStart: true, - }, - ], - }, - { - code: ` -var foo = function () { - /* block comment at block start */ - var g = 1; -}; - `, - options: [ - { - allowBlockStart: true, - }, - ], - }, - { - code: ` -if (true) { - /* block comment at block start */ - var g = 1; -} - `, - options: [ - { - allowBlockStart: true, - }, - ], - }, - { - code: ` -while (true) { - /* -block comment at block start - */ - var g = 1; -} - `, - options: [ - { - allowBlockStart: true, - }, - ], - }, - { - code: ` -class A { - /** - * hi - */ - constructor() {} -} - `, - options: [ - { - allowBlockStart: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - }, - { - code: ` -class A { - /** - * hi - */ - constructor() {} -} - `, - options: [ - { - allowClassStart: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - }, - { - code: ` -class A { - /** - * hi - */ - constructor() {} -} - `, - options: [ - { - allowBlockStart: false, - allowClassStart: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - }, - { - code: ` -switch ('foo') { - case 'foo': - /* block comment at switch case start */ - break; -} - `, - options: [ - { - allowBlockStart: true, - }, - ], - }, - { - code: ` -switch ('foo') { - case 'foo': - break; - - default: - /* block comment at switch case start */ - break; -} - `, - options: [ - { - allowBlockStart: true, - }, - ], - }, - { - code: ` -class C { - static { - // line comment - } - - static { - // line comment - foo(); - } -} - `, - options: [ - { - beforeLineComment: true, - allowBlockStart: true, - }, - ], - parserOptions: { ecmaVersion: 2022 }, - }, - { - code: ` -class C { - static { - /* block comment */ - } - - static { - /* block - comment */ - } - - static { - /* block comment */ - foo(); - } - - static { - /* block - comment */ - foo(); - } -} - `, - options: [ - { - beforeBlockComment: true, - allowBlockStart: true, - }, - ], - parserOptions: { ecmaVersion: 2022 }, - }, - - // check for block end comments - { - code: ` -var a, - // line - - b; - `, - options: [ - { - afterLineComment: true, - allowBlockEnd: true, - }, - ], - }, - { - code: ` -function foo() { - var g = 91; - // line at block end -} - `, - options: [ - { - afterLineComment: true, - allowBlockEnd: true, - }, - ], - }, - { - code: ` -function foo() { - var g = 61; - - // line at block end -} - `, - options: [ - { - afterLineComment: true, - allowBlockEnd: true, - }, - ], - }, - { - code: ` -var foo = function () { - var g = 1; - - // line at block end -}; - `, - options: [ - { - afterLineComment: true, - allowBlockEnd: true, - }, - ], - }, - { - code: ` -if (true) { - var g = 1; - // line at block end -} - `, - options: [ - { - afterLineComment: true, - allowBlockEnd: true, - }, - ], - }, - { - code: ` -if (true) { - var g = 1; - - // line at block end -} - `, - options: [ - { - afterLineComment: true, - allowBlockEnd: true, - }, - ], - }, - { - code: ` -switch ('foo') { - case 'foo': - var g = 1; - - // line at switch case end -} - `, - options: [ - { - afterLineComment: true, - allowBlockEnd: true, - }, - ], - }, - { - code: ` -switch ('foo') { - case 'foo': - break; - - default: - var g = 1; - - // line at switch case end -} - `, - options: [ - { - afterLineComment: true, - allowBlockEnd: true, - }, - ], - }, - { - code: ` -while (true) { - // line at block start and end -} - `, - options: [ - { - afterLineComment: true, - allowBlockEnd: true, - }, - ], - }, - { - code: ` -while (true) { - // line at block start and end -} - `, - options: [ - { - afterLineComment: true, - allowBlockStart: true, - allowBlockEnd: true, - }, - ], - }, - { - code: ` -while (true) { - // line at block start and end -} - `, - options: [ - { - beforeLineComment: true, - allowBlockStart: true, - allowBlockEnd: true, - }, - ], - }, - { - code: ` -while (true) { - // line at block start and end -} - `, - options: [ - { - afterLineComment: true, - beforeLineComment: true, - allowBlockStart: true, - allowBlockEnd: true, - }, - ], - }, - { - code: ` -while (true) { - // line at block start and end -} - `, - options: [ - { - beforeLineComment: true, - allowBlockStart: true, - }, - ], - }, - { - code: ` -function foo() { - var g = 1; - /* block comment at block end */ -} - `, - options: [ - { - beforeBlockComment: false, - afterBlockComment: true, - allowBlockEnd: true, - }, - ], - }, - { - code: ` -var foo = function () { - var g = 1; - /* block comment at block end */ -}; - `, - options: [ - { - beforeBlockComment: false, - afterBlockComment: true, - allowBlockEnd: true, - }, - ], - }, - { - code: ` -if (true) { - var g = 1; - /* block comment at block end */ -} - `, - options: [ - { - beforeBlockComment: false, - afterBlockComment: true, - allowBlockEnd: true, - }, - ], - }, - { - code: ` -if (true) { - var g = 1; - - /* block comment at block end */ -} - `, - options: [ - { - afterBlockComment: true, - allowBlockEnd: true, - }, - ], - }, - { - code: ` -while (true) { - var g = 1; - - /* -block comment at block end - */ -} - `, - options: [ - { - afterBlockComment: true, - allowBlockEnd: true, - }, - ], - }, - { - code: ` -class B { - constructor() {} - - /** - * hi - */ -} - `, - options: [ - { - afterBlockComment: true, - allowBlockEnd: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - }, - { - code: ` -class B { - constructor() {} - - /** - * hi - */ -} - `, - options: [ - { - afterBlockComment: true, - allowClassEnd: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - }, - { - code: ` -class B { - constructor() {} - - /** - * hi - */ -} - `, - options: [ - { - afterBlockComment: true, - allowBlockEnd: false, - allowClassEnd: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - }, - { - code: ` -switch ('foo') { - case 'foo': - var g = 1; - - /* block comment at switch case end */ -} - `, - options: [ - { - afterBlockComment: true, - allowBlockEnd: true, - }, - ], - }, - { - code: ` -switch ('foo') { - case 'foo': - break; - - default: - var g = 1; - - /* block comment at switch case end */ -} - `, - options: [ - { - afterBlockComment: true, - allowBlockEnd: true, - }, - ], - }, - { - code: ` -class C { - static { - // line comment - } - - static { - foo(); - // line comment - } -} - `, - options: [ - { - afterLineComment: true, - allowBlockEnd: true, - }, - ], - parserOptions: { ecmaVersion: 2022 }, - }, - { - code: ` -class C { - static { - /* block comment */ - } - - static { - /* block - comment */ - } - - static { - foo(); - /* block comment */ - } - - static { - foo(); - /* block - comment */ - } -} - `, - options: [ - { - beforeBlockComment: false, // default is `true` - afterBlockComment: true, - allowBlockEnd: true, - }, - ], - parserOptions: { ecmaVersion: 2022 }, - }, - - // check for object start comments - { - code: 'var a,\n\n' + '// line\n' + 'b;', - options: [ - { - beforeLineComment: true, - allowObjectStart: true, - }, - ], - }, - { - code: 'var obj = {\n' + ' // line at object start\n' + ' g: 1\n' + '};', - options: [ - { - beforeLineComment: true, - allowObjectStart: true, - }, - ], - }, - { - code: - 'function hi() {\n' + - ' return {\n' + - ' // hi\n' + - ' test: function() {\n' + - ' }\n' + - ' }\n' + - '}', - options: [ - { - beforeLineComment: true, - allowObjectStart: true, - }, - ], - }, - { - code: - 'var obj = {\n' + - ' /* block comment at object start*/\n' + - ' g: 1\n' + - '};', - options: [ - { - beforeBlockComment: true, - allowObjectStart: true, - }, - ], - }, - { - code: - 'function hi() {\n' + - ' return {\n' + - ' /**\n' + - ' * hi\n' + - ' */\n' + - ' test: function() {\n' + - ' }\n' + - ' }\n' + - '}', - options: [ - { - beforeLineComment: true, - allowObjectStart: true, - }, - ], - }, - { - code: - 'const {\n' + ' // line at object start\n' + ' g: a\n' + '} = {};', - options: [ - { - beforeLineComment: true, - allowObjectStart: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - }, - { - code: 'const {\n' + ' // line at object start\n' + ' g\n' + '} = {};', - options: [ - { - beforeLineComment: true, - allowObjectStart: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - }, - { - code: - 'const {\n' + - ' /* block comment at object-like start*/\n' + - ' g: a\n' + - '} = {};', - options: [ - { - beforeBlockComment: true, - allowObjectStart: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - }, - { - code: - 'const {\n' + - ' /* block comment at object-like start*/\n' + - ' g\n' + - '} = {};', - options: [ - { - beforeBlockComment: true, - allowObjectStart: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - }, - - // check for object end comments - { - code: 'var a,\n' + '// line\n\n' + 'b;', - options: [ - { - afterLineComment: true, - allowObjectEnd: true, - }, - ], - }, - { - code: 'var obj = {\n' + ' g: 1\n' + ' // line at object end\n' + '};', - options: [ - { - afterLineComment: true, - allowObjectEnd: true, - }, - ], - }, - { - code: - 'function hi() {\n' + - ' return {\n' + - ' test: function() {\n' + - ' }\n' + - ' // hi\n' + - ' }\n' + - '}', - options: [ - { - afterLineComment: true, - allowObjectEnd: true, - }, - ], - }, - { - code: - 'var obj = {\n' + - ' g: 1\n' + - ' \n' + - ' /* block comment at object end*/\n' + - '};', - options: [ - { - afterBlockComment: true, - allowObjectEnd: true, - }, - ], - }, - { - code: - 'function hi() {\n' + - ' return {\n' + - ' test: function() {\n' + - ' }\n' + - ' \n' + - ' /**\n' + - ' * hi\n' + - ' */\n' + - ' }\n' + - '}', - options: [ - { - afterBlockComment: true, - allowObjectEnd: true, - }, - ], - }, - { - code: 'const {\n' + ' g: a\n' + ' // line at object end\n' + '} = {};', - options: [ - { - afterLineComment: true, - allowObjectEnd: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - }, - { - code: 'const {\n' + ' g\n' + ' // line at object end\n' + '} = {};', - options: [ - { - afterLineComment: true, - allowObjectEnd: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - }, - { - code: - 'const {\n' + - ' g: a\n' + - ' \n' + - ' /* block comment at object-like end*/\n' + - '} = {};', - options: [ - { - afterBlockComment: true, - allowObjectEnd: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - }, - { - code: - 'const {\n' + - ' g\n' + - ' \n' + - ' /* block comment at object-like end*/\n' + - '} = {};', - options: [ - { - afterBlockComment: true, - allowObjectEnd: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - }, - - // check for array start comments - { - code: 'var a,\n\n' + '// line\n' + 'b;', - options: [ - { - beforeLineComment: true, - allowArrayStart: true, - }, - ], - }, - { - code: 'var arr = [\n' + ' // line at array start\n' + ' 1\n' + '];', - options: [ - { - beforeLineComment: true, - allowArrayStart: true, - }, - ], - }, - { - code: - 'var arr = [\n' + - ' /* block comment at array start*/\n' + - ' 1\n' + - '];', - options: [ - { - beforeBlockComment: true, - allowArrayStart: true, - }, - ], - }, - { - code: 'const [\n' + ' // line at array start\n' + ' a\n' + '] = [];', - options: [ - { - beforeLineComment: true, - allowArrayStart: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - }, - { - code: - 'const [\n' + - ' /* block comment at array start*/\n' + - ' a\n' + - '] = [];', - options: [ - { - beforeBlockComment: true, - allowArrayStart: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - }, - - // check for array end comments - { - code: 'var a,\n' + '// line\n\n' + 'b;', - options: [ - { - afterLineComment: true, - allowArrayEnd: true, - }, - ], - }, - { - code: 'var arr = [\n' + ' 1\n' + ' // line at array end\n' + '];', - options: [ - { - afterLineComment: true, - allowArrayEnd: true, - }, - ], - }, - { - code: - 'var arr = [\n' + - ' 1\n' + - ' \n' + - ' /* block comment at array end*/\n' + - '];', - options: [ - { - afterBlockComment: true, - allowArrayEnd: true, - }, - ], - }, - { - code: 'const [\n' + ' a\n' + ' // line at array end\n' + '] = [];', - options: [ - { - afterLineComment: true, - allowArrayEnd: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - }, - { - code: - 'const [\n' + - ' a\n' + - ' \n' + - ' /* block comment at array end*/\n' + - '] = [];', - options: [ - { - afterBlockComment: true, - allowArrayEnd: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - }, - // Interface - { - code: unIndent` -interface A { - // line - a: string; -} -`, - options: [ - { - beforeLineComment: true, - allowInterfaceStart: true, - }, - ], - }, - { - code: unIndent` -interface A { - /* block - comment */ - a: string; -} -`, - options: [ - { - beforeBlockComment: true, - allowInterfaceStart: true, - }, - ], - }, - { - code: unIndent` -interface A { - a: string; - // line -} -`, - options: [ - { - afterLineComment: true, - allowInterfaceEnd: true, - }, - ], - }, - { - code: unIndent` -interface A { - a: string; - /* block - comment */ -} -`, - options: [ - { - beforeBlockComment: false, - afterBlockComment: true, - allowInterfaceEnd: true, - }, - ], - }, - // Type - { - code: unIndent` -type A = { - // line - a: string; -} -`, - options: [ - { - beforeLineComment: true, - allowTypeStart: true, - }, - ], - }, - { - code: unIndent` -type A = { - /* block - comment */ - a: string; -} -`, - options: [ - { - beforeBlockComment: true, - allowTypeStart: true, - }, - ], - }, - { - code: unIndent` -type A = { - a: string; - // line -} -`, - options: [ - { - afterLineComment: true, - allowTypeEnd: true, - }, - ], - }, - { - code: unIndent` -type A = { - a: string; - /* block - comment */ -} -`, - options: [ - { - beforeBlockComment: false, - afterBlockComment: true, - allowTypeEnd: true, - }, - ], - }, - - // Enum - { - code: unIndent` -enum A { - // line - a, -} -`, - options: [ - { - beforeLineComment: true, - allowEnumStart: true, - }, - ], - }, - { - code: unIndent` -enum A { - /* block - comment */ - a, -} -`, - options: [ - { - beforeBlockComment: true, - allowEnumStart: true, - }, - ], - }, - { - code: unIndent` -enum A { - a, - // line -} -`, - options: [ - { - afterLineComment: true, - allowEnumEnd: true, - }, - ], - }, - { - code: unIndent` -enum A { - a, - /* block - comment */ -} -`, - options: [ - { - beforeBlockComment: false, - afterBlockComment: true, - allowEnumEnd: true, - }, - ], - }, - - // TS module - { - code: unIndent` -declare module A { - // line - const a: string; -} -`, - options: [ - { - beforeLineComment: true, - allowModuleStart: true, - }, - ], - }, - { - code: unIndent` -declare module A { - /* block - comment */ - const a: string; -} -`, - options: [ - { - beforeBlockComment: true, - allowModuleStart: true, - }, - ], - }, - { - code: unIndent` -declare module A { - const a: string; - // line -} -`, - options: [ - { - afterLineComment: true, - allowModuleEnd: true, - }, - ], - }, - { - code: unIndent` -declare module A { - const a: string; - /* block - comment */ -} -`, - options: [ - { - beforeBlockComment: false, - afterBlockComment: true, - allowModuleEnd: true, - }, - ], - }, - // ignorePattern - { - code: - 'interface A {' + - 'foo: string;\n\n' + - '/* eslint-disable no-underscore-dangle */\n\n' + - '_values: 2;\n' + - '_values2: true;\n' + - '/* eslint-enable no-underscore-dangle */\n' + - 'bar: boolean' + - '}', - options: [ - { - beforeBlockComment: true, - afterBlockComment: true, - }, - ], - }, - ` -interface A { - foo; - /* eslint */ -} - `, - ` -interface A { - foo; - /* jshint */ -} - `, - ` -interface A { - foo; - /* jslint */ -} - `, - ` -interface A { - foo; - /* istanbul */ -} - `, - ` -interface A { - foo; - /* global */ -} - `, - ` -interface A { - foo; - /* globals */ -} - `, - ` -interface A { - foo; - /* exported */ -} - `, - ` -interface A { - foo; - /* jscs */ -} - `, - { - code: ` -interface A { - foo: boolean; - /* this is pragmatic */ -} - `, - options: [{ ignorePattern: 'pragma' }], - }, - { - code: ` -interface A { - foo; - /* this is pragmatic */ -} - `, - options: [{ applyDefaultIgnorePatterns: false, ignorePattern: 'pragma' }], - }, - { - code: ` -interface A { - foo: string; // this is inline line comment -} - `, - options: [{ beforeLineComment: true }], - }, - { - code: ` -interface A { - foo: string /* this is inline block comment */; -} - `, - }, - { - code: ` -interface A { - /* this is inline block comment */ foo: string; -} - `, - }, - { - code: ` -interface A { - /* this is inline block comment */ foo: string /* this is inline block comment */; -} - `, - }, - { - code: ` -interface A { - /* this is inline block comment */ foo: string; // this is inline line comment ; -} - `, - }, - ], - invalid: [ - // default rules - { - code: ` -bar(); -/** block block block - * block - */ -var a = 1; - `, - output: ` -bar(); - -/** block block block - * block - */ -var a = 1; - `, - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block }], - }, - - // line comments - { - code: ` -baz(); -// A line comment with no empty line after -var a = 1; - `, - output: ` -baz(); -// A line comment with no empty line after - -var a = 1; - `, - options: [{ afterLineComment: true }], - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line }], - }, - { - code: ` -baz(); -// A line comment with no empty line after -var a = 1; - `, - output: ` -baz(); - -// A line comment with no empty line after -var a = 1; - `, - options: [{ beforeLineComment: true, afterLineComment: false }], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line }], - }, - { - code: '// A line comment with no empty line after\nvar a = 1;', - output: '// A line comment with no empty line after\n\nvar a = 1;', - options: [{ beforeLineComment: true, afterLineComment: true }], - errors: [ - { messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 1, column: 1 }, - ], - }, - { - code: unIndent` -baz(); -// A line comment with no empty line after -var a = 1; - `, - output: unIndent` -baz(); - -// A line comment with no empty line after - -var a = 1; - `, - options: [{ beforeLineComment: true, afterLineComment: true }], - errors: [ - { messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 2 }, - { messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 2 }, - ], - }, - - // block comments - { - code: unIndent` -bar(); -/** - * block block block - */ -var a = 1; - `, - output: unIndent` -bar(); - -/** - * block block block - */ - -var a = 1; - `, - options: [{ afterBlockComment: true, beforeBlockComment: true }], - errors: [ - { messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 2 }, - { messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 2 }, - ], - }, - { - code: unIndent` -bar(); -/* first block comment */ /* second block comment */ -var a = 1; - `, - output: unIndent` -bar(); - -/* first block comment */ /* second block comment */ - -var a = 1; - `, - options: [{ afterBlockComment: true, beforeBlockComment: true }], - errors: [ - { messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 2 }, - { messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 2 }, - ], - }, - { - code: unIndent` -bar(); -/* first block comment */ /* second block - comment */ -var a = 1; - `, - output: unIndent` -bar(); - -/* first block comment */ /* second block - comment */ - -var a = 1; - `, - options: [{ afterBlockComment: true, beforeBlockComment: true }], - errors: [ - { messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 2 }, - { messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 2 }, - ], - }, - { - code: unIndent` -bar(); -/** - * block block block - */ -var a = 1; - `, - output: unIndent` -bar(); -/** - * block block block - */ - -var a = 1; - `, - options: [{ afterBlockComment: true, beforeBlockComment: false }], - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 2 }], - }, - { - code: unIndent` -bar(); -/** - * block block block - */ -var a = 1; - `, - output: unIndent` -bar(); - -/** - * block block block - */ -var a = 1; - `, - options: [{ afterBlockComment: false, beforeBlockComment: true }], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 2 }], - }, - { - code: unIndent` -var a, -// line -b; - `, - output: unIndent` -var a, - -// line -b; - `, - options: [ - { - beforeLineComment: true, - allowBlockStart: true, - }, - ], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 2 }], - }, - { - code: unIndent` -function foo() { - var a = 1; - // line at block start - var g = 1; -} - `, - output: unIndent` -function foo() { - var a = 1; - - // line at block start - var g = 1; -} - `, - options: [ - { - beforeLineComment: true, - allowBlockStart: true, - }, - ], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 3 }], - }, - { - code: unIndent` -var a, -// line -b; - `, - output: unIndent` -var a, -// line - -b; - `, - options: [ - { - afterLineComment: true, - allowBlockEnd: true, - }, - ], - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 2 }], - }, - { - code: unIndent` -function foo() { - var a = 1; - - // line at block start - var g = 1; -} - `, - output: unIndent` -function foo() { - var a = 1; - - // line at block start - - var g = 1; -} - `, - options: [ - { - afterLineComment: true, - allowBlockEnd: true, - }, - ], - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 4 }], - }, - { - code: unIndent` -switch ('foo') { - case 'foo': - // line at switch case start - break; -} - `, - output: unIndent` -switch ('foo') { - case 'foo': - - // line at switch case start - break; -} - `, - options: [ - { - beforeLineComment: true, - }, - ], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 3 }], - }, - { - code: unIndent` -switch ('foo') { - case 'foo': - break; - - default: - // line at switch case start - break; -} - `, - output: unIndent` -switch ('foo') { - case 'foo': - break; - - default: - - // line at switch case start - break; -} - `, - options: [ - { - beforeLineComment: true, - }, - ], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 6 }], - }, - { - code: unIndent` -while (true) { - // line at block start and end -} - `, - output: unIndent` -while (true) { - // line at block start and end - -} - `, - options: [ - { - afterLineComment: true, - allowBlockStart: true, - }, - ], - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 2 }], - }, - { - code: unIndent` -while (true) { - // line at block start and end -} - `, - output: unIndent` -while (true) { - - // line at block start and end -} - `, - options: [ - { - beforeLineComment: true, - allowBlockEnd: true, - }, - ], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 2 }], - }, - { - code: unIndent` -class A { - // line at class start - constructor() {} -} - `, - output: unIndent` -class A { - - // line at class start - constructor() {} -} - `, - options: [ - { - beforeLineComment: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 2 }], - }, - { - code: unIndent` -class A { - // line at class start - constructor() {} -} - `, - output: unIndent` -class A { - - // line at class start - constructor() {} -} - `, - options: [ - { - allowBlockStart: true, - allowClassStart: false, - beforeLineComment: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 2 }], - }, - { - code: unIndent` -class B { - constructor() {} - - // line at class end -} - `, - output: unIndent` -class B { - constructor() {} - - // line at class end - -} - `, - options: [ - { - afterLineComment: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 4 }], - }, - { - code: unIndent` -class B { - constructor() {} - - // line at class end -} - `, - output: unIndent` -class B { - constructor() {} - - // line at class end - -} - `, - options: [ - { - afterLineComment: true, - allowBlockEnd: true, - allowClassEnd: false, - }, - ], - parserOptions: { ecmaVersion: 6 }, - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 4 }], - }, - { - code: unIndent` -switch ('foo') { - case 'foo': - var g = 1; - - // line at switch case end -} - `, - output: unIndent` -switch ('foo') { - case 'foo': - var g = 1; - - // line at switch case end - -} - `, - options: [ - { - afterLineComment: true, - }, - ], - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 5 }], - }, - { - code: unIndent` -switch ('foo') { - case 'foo': - break; - - default: - var g = 1; - - // line at switch case end -} - `, - output: unIndent` -switch ('foo') { - case 'foo': - break; - - default: - var g = 1; - - // line at switch case end - -} - `, - options: [ - { - afterLineComment: true, - }, - ], - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 8 }], - }, - { - code: unIndent` -class C { - // line comment - static {} -} - `, - output: unIndent` -class C { - // line comment - - static {} -} - `, - options: [ - { - beforeLineComment: true, - afterLineComment: true, - allowBlockStart: true, - allowBlockEnd: true, - allowClassStart: true, - allowClassEnd: true, - }, - ], - parserOptions: { ecmaVersion: 2022 }, - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 2 }], - }, - { - code: unIndent` -class C { - /* block - comment */ - static {} -} - `, - output: unIndent` -class C { - /* block - comment */ - - static {} -} - `, - options: [ - { - beforeBlockComment: true, - afterBlockComment: true, - allowBlockStart: true, - allowBlockEnd: true, - allowClassStart: true, - allowClassEnd: true, - }, - ], - parserOptions: { ecmaVersion: 2022 }, - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 2 }], - }, - { - code: unIndent` -class C { - static - // line comment - {} -} - `, - output: unIndent` -class C { - static - - // line comment - - {} -} - `, - options: [ - { - beforeLineComment: true, - afterLineComment: true, - allowBlockStart: true, - allowBlockEnd: true, - allowClassStart: true, - allowClassEnd: true, - }, - ], - parserOptions: { ecmaVersion: 2022 }, - errors: [ - { messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 3 }, - { messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 3 }, - ], - }, - { - code: unIndent` -class C { - static - /* block - comment */ - {} -} - `, - output: unIndent` -class C { - static - - /* block - comment */ - - {} -} - `, - options: [ - { - beforeBlockComment: true, - afterBlockComment: true, - allowBlockStart: true, - allowBlockEnd: true, - allowClassStart: true, - allowClassEnd: true, - }, - ], - parserOptions: { ecmaVersion: 2022 }, - errors: [ - { messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 3 }, - { messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 3 }, - ], - }, + // Interface { code: unIndent` -class C { - static { - // line comment - foo(); - } -} - `, - output: unIndent` -class C { - static { - // line comment - - foo(); - } +interface A { + // line + a: string; } - `, +`, options: [ { beforeLineComment: true, - afterLineComment: true, - allowBlockStart: true, - allowBlockEnd: true, + allowInterfaceStart: true, }, ], - parserOptions: { ecmaVersion: 2022 }, - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 3 }], }, { code: unIndent` -class C { - static { - /* block - comment */ - foo(); - } -} - `, - output: unIndent` -class C { - static { - /* block - comment */ - - foo(); - } +interface A { + /* block + comment */ + a: string; } - `, +`, options: [ { beforeBlockComment: true, - afterBlockComment: true, - allowBlockStart: true, - allowBlockEnd: true, + allowInterfaceStart: true, }, ], - parserOptions: { ecmaVersion: 2022 }, - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 3 }], }, { code: unIndent` -class C { - static { - foo(); - // line comment - } -} - `, - output: unIndent` -class C { - static { - foo(); - - // line comment - } +interface A { + a: string; + // line } - `, +`, options: [ { - beforeLineComment: true, afterLineComment: true, - allowBlockStart: true, - allowBlockEnd: true, + allowInterfaceEnd: true, }, ], - parserOptions: { ecmaVersion: 2022 }, - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 4 }], }, { code: unIndent` -class C { - static { - foo(); - /* block - comment */ - } -} - `, - output: unIndent` -class C { - static { - foo(); - - /* block - comment */ - } +interface A { + a: string; + /* block + comment */ } - `, +`, options: [ { - beforeBlockComment: true, + beforeBlockComment: false, afterBlockComment: true, - allowBlockStart: true, - allowBlockEnd: true, + allowInterfaceEnd: true, }, ], - parserOptions: { ecmaVersion: 2022 }, - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 4 }], }, + // Type { code: unIndent` -class C { - static { - foo(); - // line comment - bar(); - } -} - `, - output: unIndent` -class C { - static { - foo(); - - // line comment - - bar(); - } +type A = { + // line + a: string; } - `, +`, options: [ { beforeLineComment: true, - afterLineComment: true, - allowBlockStart: true, - allowBlockEnd: true, + allowTypeStart: true, }, ], - parserOptions: { ecmaVersion: 2022 }, - errors: [ - { messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 4 }, - { messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 4 }, - ], }, { code: unIndent` -class C { - static { - foo(); - /* block - comment */ - bar(); - } -} - `, - output: unIndent` -class C { - static { - foo(); - - /* block - comment */ - - bar(); - } +type A = { + /* block + comment */ + a: string; } - `, +`, options: [ { beforeBlockComment: true, - afterBlockComment: true, - allowBlockStart: true, - allowBlockEnd: true, + allowTypeStart: true, }, ], - parserOptions: { ecmaVersion: 2022 }, - errors: [ - { messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 4 }, - { messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 4 }, - ], }, { code: unIndent` -class C { - static {} - // line comment -} - `, - output: unIndent` -class C { - static {} - - // line comment +type A = { + a: string; + // line } - `, +`, options: [ { - beforeLineComment: true, afterLineComment: true, - allowBlockStart: true, - allowBlockEnd: true, - allowClassStart: true, - allowClassEnd: true, + allowTypeEnd: true, }, ], - parserOptions: { ecmaVersion: 2022 }, - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 3 }], }, { code: unIndent` -class C { - static {} - /* block - comment */ -} - `, - output: unIndent` -class C { - static {} - +type A = { + a: string; /* block comment */ } - `, - options: [ - { - beforeBlockComment: true, - afterBlockComment: true, - allowBlockStart: true, - allowBlockEnd: true, - allowClassStart: true, - allowClassEnd: true, - }, - ], - parserOptions: { ecmaVersion: 2022 }, - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 3 }], - }, - - // object start comments - { - code: 'var obj = {\n' + ' // line at object start\n' + ' g: 1\n' + '};', - output: - 'var obj = {\n' + - '\n' + - ' // line at object start\n' + - ' g: 1\n' + - '};', - options: [ - { - beforeLineComment: true, - }, - ], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 2 }], - }, - { - code: - 'function hi() {\n' + - ' return {\n' + - ' // hi\n' + - ' test: function() {\n' + - ' }\n' + - ' }\n' + - '}', - output: - 'function hi() {\n' + - ' return {\n' + - '\n' + - ' // hi\n' + - ' test: function() {\n' + - ' }\n' + - ' }\n' + - '}', - options: [ - { - beforeLineComment: true, - }, - ], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 3 }], - }, - { - code: - 'var obj = {\n' + - ' /* block comment at object start*/\n' + - ' g: 1\n' + - '};', - output: - 'var obj = {\n' + - '\n' + - ' /* block comment at object start*/\n' + - ' g: 1\n' + - '};', - options: [ - { - beforeBlockComment: true, - }, - ], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 2 }], - }, - { - code: - 'function hi() {\n' + - ' return {\n' + - ' /**\n' + - ' * hi\n' + - ' */\n' + - ' test: function() {\n' + - ' }\n' + - ' }\n' + - '}', - output: - 'function hi() {\n' + - ' return {\n' + - '\n' + - ' /**\n' + - ' * hi\n' + - ' */\n' + - ' test: function() {\n' + - ' }\n' + - ' }\n' + - '}', - options: [ - { - beforeLineComment: true, - }, - ], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 3 }], - }, - { - code: - 'const {\n' + ' // line at object start\n' + ' g: a\n' + '} = {};', - output: - 'const {\n' + - '\n' + - ' // line at object start\n' + - ' g: a\n' + - '} = {};', - options: [ - { - beforeLineComment: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 2 }], - }, - { - code: 'const {\n' + ' // line at object start\n' + ' g\n' + '} = {};', - output: - 'const {\n' + - '\n' + - ' // line at object start\n' + - ' g\n' + - '} = {};', - options: [ - { - beforeLineComment: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 2 }], - }, - { - code: - 'const {\n' + - ' /* block comment at object-like start*/\n' + - ' g: a\n' + - '} = {};', - output: - 'const {\n' + - '\n' + - ' /* block comment at object-like start*/\n' + - ' g: a\n' + - '} = {};', - options: [ - { - beforeBlockComment: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 2 }], - }, - { - code: - 'const {\n' + - ' /* block comment at object-like start*/\n' + - ' g\n' + - '} = {};', - output: - 'const {\n' + - '\n' + - ' /* block comment at object-like start*/\n' + - ' g\n' + - '} = {};', - options: [ - { - beforeBlockComment: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 2 }], - }, - - // object end comments - { - code: 'var obj = {\n' + ' g: 1\n' + ' // line at object end\n' + '};', - output: - 'var obj = {\n' + - ' g: 1\n' + - ' // line at object end\n' + - '\n' + - '};', - options: [ - { - afterLineComment: true, - }, - ], - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 3 }], - }, - { - code: - 'function hi() {\n' + - ' return {\n' + - ' test: function() {\n' + - ' }\n' + - ' // hi\n' + - ' }\n' + - '}', - output: - 'function hi() {\n' + - ' return {\n' + - ' test: function() {\n' + - ' }\n' + - ' // hi\n' + - '\n' + - ' }\n' + - '}', - options: [ - { - afterLineComment: true, - }, - ], - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 5 }], - }, - { - code: - 'var obj = {\n' + - ' g: 1\n' + - ' \n' + - ' /* block comment at object end*/\n' + - '};', - output: - 'var obj = {\n' + - ' g: 1\n' + - ' \n' + - ' /* block comment at object end*/\n' + - '\n' + - '};', - options: [ - { - afterBlockComment: true, - }, - ], - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 4 }], - }, - { - code: - 'function hi() {\n' + - ' return {\n' + - ' test: function() {\n' + - ' }\n' + - ' \n' + - ' /**\n' + - ' * hi\n' + - ' */\n' + - ' }\n' + - '}', - output: - 'function hi() {\n' + - ' return {\n' + - ' test: function() {\n' + - ' }\n' + - ' \n' + - ' /**\n' + - ' * hi\n' + - ' */\n' + - '\n' + - ' }\n' + - '}', +`, options: [ { + beforeBlockComment: false, afterBlockComment: true, + allowTypeEnd: true, }, ], - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 6 }], - }, - { - code: 'const {\n' + ' g: a\n' + ' // line at object end\n' + '} = {};', - output: - 'const {\n' + - ' g: a\n' + - ' // line at object end\n' + - '\n' + - '} = {};', - options: [ - { - afterLineComment: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 3 }], - }, - { - code: 'const {\n' + ' g\n' + ' // line at object end\n' + '} = {};', - output: - 'const {\n' + ' g\n' + ' // line at object end\n' + '\n' + '} = {};', - options: [ - { - afterLineComment: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 3 }], }, + + // Enum { - code: - 'const {\n' + - ' g: a\n' + - ' \n' + - ' /* block comment at object-like end*/\n' + - '} = {};', - output: - 'const {\n' + - ' g: a\n' + - ' \n' + - ' /* block comment at object-like end*/\n' + - '\n' + - '} = {};', + code: unIndent` +enum A { + // line + a, +} +`, options: [ { - afterBlockComment: true, + beforeLineComment: true, + allowEnumStart: true, }, ], - parserOptions: { ecmaVersion: 6 }, - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 4 }], }, { - code: - 'const {\n' + - ' g\n' + - ' \n' + - ' /* block comment at object-like end*/\n' + - '} = {};', - output: - 'const {\n' + - ' g\n' + - ' \n' + - ' /* block comment at object-like end*/\n' + - '\n' + - '} = {};', + code: unIndent` +enum A { + /* block + comment */ + a, +} +`, options: [ { - afterBlockComment: true, + beforeBlockComment: true, + allowEnumStart: true, }, ], - parserOptions: { ecmaVersion: 6 }, - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 4 }], }, - - // array start comments { - code: 'var arr = [\n' + ' // line at array start\n' + ' 1\n' + '];', - output: - 'var arr = [\n' + '\n' + ' // line at array start\n' + ' 1\n' + '];', + code: unIndent` +enum A { + a, + // line +} +`, options: [ { - beforeLineComment: true, + afterLineComment: true, + allowEnumEnd: true, }, ], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 2 }], }, { - code: - 'var arr = [\n' + - ' /* block comment at array start*/\n' + - ' 1\n' + - '];', - output: - 'var arr = [\n' + - '\n' + - ' /* block comment at array start*/\n' + - ' 1\n' + - '];', + code: unIndent` +enum A { + a, + /* block + comment */ +} +`, options: [ { - beforeBlockComment: true, + beforeBlockComment: false, + afterBlockComment: true, + allowEnumEnd: true, }, ], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 2 }], }, + + // TS module { - code: 'const [\n' + ' // line at array start\n' + ' a\n' + '] = [];', - output: - 'const [\n' + '\n' + ' // line at array start\n' + ' a\n' + '] = [];', + code: unIndent` +declare module A { + // line + const a: string; +} +`, options: [ { beforeLineComment: true, + allowModuleStart: true, }, ], - parserOptions: { ecmaVersion: 6 }, - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 2 }], }, { - code: - 'const [\n' + - ' /* block comment at array start*/\n' + - ' a\n' + - '] = [];', - output: - 'const [\n' + - '\n' + - ' /* block comment at array start*/\n' + - ' a\n' + - '] = [];', + code: unIndent` +declare module A { + /* block + comment */ + const a: string; +} +`, options: [ { beforeBlockComment: true, + allowModuleStart: true, }, ], - parserOptions: { ecmaVersion: 6 }, - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 2 }], }, - - // array end comments { - code: 'var arr = [\n' + ' 1\n' + ' // line at array end\n' + '];', - output: - 'var arr = [\n' + ' 1\n' + ' // line at array end\n' + '\n' + '];', + code: unIndent` +declare module A { + const a: string; + // line +} +`, options: [ { afterLineComment: true, + allowModuleEnd: true, }, ], - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 3 }], }, { - code: - 'var arr = [\n' + - ' 1\n' + - ' \n' + - ' /* block comment at array end*/\n' + - '];', - output: - 'var arr = [\n' + - ' 1\n' + - ' \n' + - ' /* block comment at array end*/\n' + - '\n' + - '];', + code: unIndent` +declare module A { + const a: string; + /* block + comment */ +} +`, options: [ { + beforeBlockComment: false, afterBlockComment: true, + allowModuleEnd: true, }, ], - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 4 }], - }, - { - code: 'const [\n' + ' a\n' + ' // line at array end\n' + '] = [];', - output: - 'const [\n' + ' a\n' + ' // line at array end\n' + '\n' + '] = [];', - options: [ - { - afterLineComment: true, - }, - ], - parserOptions: { ecmaVersion: 6 }, - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 3 }], }, + // ignorePattern { code: - 'const [\n' + - ' a\n' + - ' \n' + - ' /* block comment at array end*/\n' + - '] = [];', - output: - 'const [\n' + - ' a\n' + - ' \n' + - ' /* block comment at array end*/\n' + - '\n' + - '] = [];', + 'interface A {' + + 'foo: string;\n\n' + + '/* eslint-disable no-underscore-dangle */\n\n' + + '_values: 2;\n' + + '_values2: true;\n' + + '/* eslint-enable no-underscore-dangle */\n' + + 'bar: boolean' + + '}', options: [ { + beforeBlockComment: true, afterBlockComment: true, }, ], - parserOptions: { ecmaVersion: 6 }, - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 4 }], }, + ` +interface A { + foo; + /* eslint */ +} + `, + ` +interface A { + foo; + /* jshint */ +} + `, + ` +interface A { + foo; + /* jslint */ +} + `, + ` +interface A { + foo; + /* istanbul */ +} + `, + ` +interface A { + foo; + /* global */ +} + `, + ` +interface A { + foo; + /* globals */ +} + `, + ` +interface A { + foo; + /* exported */ +} + `, + ` +interface A { + foo; + /* jscs */ +} + `, + { + code: ` +interface A { + foo: boolean; + /* this is pragmatic */ +} + `, + options: [{ ignorePattern: 'pragma' }], + }, + { + code: ` +interface A { + foo; + /* this is pragmatic */ +} + `, + options: [{ applyDefaultIgnorePatterns: false, ignorePattern: 'pragma' }], + }, + { + code: ` +interface A { + foo: string; // this is inline line comment +} + `, + options: [{ beforeLineComment: true }], + }, + { + code: ` +interface A { + foo: string /* this is inline block comment */; +} + `, + }, + { + code: ` +interface A { + /* this is inline block comment */ foo: string; +} + `, + }, + { + code: ` +interface A { + /* this is inline block comment */ foo: string /* this is inline block comment */; +} + `, + }, + { + code: ` +interface A { + /* this is inline block comment */ foo: string; // this is inline line comment ; +} + `, + }, + ], + invalid: [ // interface { code: unIndent` @@ -3626,117 +1029,5 @@ interface A { ], errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 2 }], }, - // ignorePattern - { - code: - 'foo;\n\n' + - '/* eslint-disable no-underscore-dangle */\n\n' + - 'this._values = values;\n' + - 'this._values2 = true;\n' + - '/* eslint-enable no-underscore-dangle */\n' + - 'bar', - output: - 'foo;\n\n' + - '/* eslint-disable no-underscore-dangle */\n\n' + - 'this._values = values;\n' + - 'this._values2 = true;\n' + - '\n' + - '/* eslint-enable no-underscore-dangle */\n' + - '\n' + - 'bar', - options: [ - { - beforeBlockComment: true, - afterBlockComment: true, - applyDefaultIgnorePatterns: false, - }, - ], - errors: [ - { messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 7 }, - { messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 7 }, - ], - }, - { - code: 'foo;\n/* eslint */', - output: 'foo;\n\n/* eslint */', - options: [{ applyDefaultIgnorePatterns: false }], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block }], - }, - { - code: 'foo;\n/* jshint */', - output: 'foo;\n\n/* jshint */', - options: [{ applyDefaultIgnorePatterns: false }], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block }], - }, - { - code: 'foo;\n/* jslint */', - output: 'foo;\n\n/* jslint */', - options: [{ applyDefaultIgnorePatterns: false }], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block }], - }, - { - code: 'foo;\n/* istanbul */', - output: 'foo;\n\n/* istanbul */', - options: [{ applyDefaultIgnorePatterns: false }], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block }], - }, - { - code: 'foo;\n/* global */', - output: 'foo;\n\n/* global */', - options: [{ applyDefaultIgnorePatterns: false }], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block }], - }, - { - code: 'foo;\n/* globals */', - output: 'foo;\n\n/* globals */', - options: [{ applyDefaultIgnorePatterns: false }], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block }], - }, - { - code: 'foo;\n/* exported */', - output: 'foo;\n\n/* exported */', - options: [{ applyDefaultIgnorePatterns: false }], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block }], - }, - { - code: 'foo;\n/* jscs */', - output: 'foo;\n\n/* jscs */', - options: [{ applyDefaultIgnorePatterns: false }], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block }], - }, - { - code: ` -foo; -/* something else */ - `, - output: ` -foo; - -/* something else */ - `, - options: [{ ignorePattern: 'pragma' }], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block }], - }, - { - code: ` -foo; -/* eslint */ - `, - output: ` -foo; - -/* eslint */ - `, - options: [{ applyDefaultIgnorePatterns: false }], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block }], - }, - - // "fallthrough" patterns are not ignored by default - { - code: 'foo;\n/* fallthrough */', - output: 'foo;\n\n/* fallthrough */', - options: [], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block }], - }, ], }); From f32e927e19bb5214328b6cd272e399a04b651542 Mon Sep 17 00:00:00 2001 From: chbdetta Date: Tue, 24 Jan 2023 00:45:59 -0800 Subject: [PATCH 21/21] improve coverage --- .../src/rules/lines-around-comment.ts | 39 +++---------------- .../tests/rules/lines-around-comment.test.ts | 22 ++++++++++- 2 files changed, 26 insertions(+), 35 deletions(-) diff --git a/packages/eslint-plugin/src/rules/lines-around-comment.ts b/packages/eslint-plugin/src/rules/lines-around-comment.ts index 34e61a0044ce..478667040c5a 100644 --- a/packages/eslint-plugin/src/rules/lines-around-comment.ts +++ b/packages/eslint-plugin/src/rules/lines-around-comment.ts @@ -202,31 +202,6 @@ export default util.createRule({ function getParentNodeOfToken(token: TSESTree.Token): TSESTree.Node | null { const node = sourceCode.getNodeByRangeIndex(token.range[0]); - /* - * For the purpose of this rule, the comment token is in a `StaticBlock` node only - * if it's inside the braces of that `StaticBlock` node. - * - * Example where this function returns `null`: - * - * static - * // comment - * { - * } - * - * Example where this function returns `StaticBlock` node: - * - * static - * { - * // comment - * } - * - */ - if (node && node.type === AST_NODE_TYPES.StaticBlock) { - const openingBrace = sourceCode.getFirstToken(node, { skip: 1 })!; // skip the `static` token - - return token.range[0] >= openingBrace.range[0] ? node : null; - } - return node; } @@ -424,16 +399,12 @@ export default util.createRule({ const customReport: typeof context.report = descriptor => { if ('node' in descriptor) { if ( - descriptor.node.type !== AST_TOKEN_TYPES.Line && - descriptor.node.type !== AST_TOKEN_TYPES.Block + descriptor.node.type === AST_TOKEN_TYPES.Line || + descriptor.node.type === AST_TOKEN_TYPES.Block ) { - throw new TypeError( - 'The node reported by the base rule must always be a Comment token', - ); - } - - if (isCommentNearTSConstruct(descriptor.node)) { - return; + if (isCommentNearTSConstruct(descriptor.node)) { + return; + } } } return context.report(descriptor); diff --git a/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts b/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts index ce2a7f6702bf..a312bb19cd0a 100644 --- a/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts +++ b/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts @@ -1,7 +1,7 @@ import { AST_TOKEN_TYPES } from '@typescript-eslint/utils'; import rule from '../../src/rules/lines-around-comment'; -import { noFormat, RuleTester } from '../RuleTester'; +import { RuleTester } from '../RuleTester'; import { unIndent } from './indent/utils'; const ruleTester = new RuleTester({ @@ -374,6 +374,26 @@ interface A { }, ], invalid: [ + // ESLint base rule test to cover the usage of the original reporter + { + code: ` +bar(); +/** block block block + * block + */ +var a = 1; + `, + output: ` +bar(); + +/** block block block + * block + */ +var a = 1; + `, + errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block }], + }, + // interface { code: unIndent`