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 00000000000..60250323032
--- /dev/null
+++ b/packages/eslint-plugin/docs/rules/lines-around-comment.md
@@ -0,0 +1,41 @@
+---
+description: 'Require 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
+
+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)
+
+
diff --git a/packages/eslint-plugin/src/configs/all.ts b/packages/eslint-plugin/src/configs/all.ts
index 7a99a483025..63ce9b1305a 100644
--- a/packages/eslint-plugin/src/configs/all.ts
+++ b/packages/eslint-plugin/src/configs/all.ts
@@ -43,6 +43,8 @@ export = {
'@typescript-eslint/key-spacing': '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',
diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts
index 7dc5488142e..e1d871103ec 100644
--- a/packages/eslint-plugin/src/rules/index.ts
+++ b/packages/eslint-plugin/src/rules/index.ts
@@ -25,6 +25,7 @@ import indent from './indent';
import initDeclarations from './init-declarations';
import keySpacing from './key-spacing';
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';
@@ -160,6 +161,7 @@ export default {
'init-declarations': initDeclarations,
'key-spacing': keySpacing,
'keyword-spacing': keywordSpacing,
+ 'lines-around-comment': linesAroundComment,
'lines-between-class-members': linesBetweenClassMembers,
'member-delimiter-style': memberDelimiterStyle,
'member-ordering': memberOrdering,
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 00000000000..478667040c5
--- /dev/null
+++ b/packages/eslint-plugin/src/rules/lines-around-comment.ts
@@ -0,0 +1,456 @@
+import type { TSESTree } from '@typescript-eslint/utils';
+import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils';
+
+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;
+
+const COMMENTS_IGNORE_PATTERN =
+ /^\s*(?:eslint|jshint\s+|jslint\s+|istanbul\s+|globals?\s+|exported\s+|jscs)/u;
+
+/**
+ * @returns 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;
+}
+
+/**
+ * @returns 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: {
+ 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',
+ },
+ allowInterfaceStart: {
+ type: 'boolean',
+ },
+ allowInterfaceEnd: {
+ type: 'boolean',
+ },
+ allowTypeStart: {
+ type: 'boolean',
+ },
+ allowTypeEnd: {
+ type: 'boolean',
+ },
+ allowEnumStart: {
+ type: 'boolean',
+ },
+ allowEnumEnd: {
+ type: 'boolean',
+ },
+ allowModuleStart: {
+ type: 'boolean',
+ },
+ allowModuleEnd: {
+ 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 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 commentLines = getCommentLineNums(comments);
+ const emptyLines = getEmptyLineNums(lines);
+ const commentAndEmptyLines = new Set(commentLines.concat(emptyLines));
+
+ /**
+ * @returns whether 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 comments are inside a node type.
+ */
+ function isParentNodeType(
+ parent: TSESTree.Node,
+ nodeType: T,
+ ): parent is Extract {
+ return parent.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]);
+
+ return node;
+ }
+
+ /**
+ * @returns whether comments are at the parent start.
+ */
+ function isCommentAtParentStart(
+ token: TSESTree.Token,
+ nodeType: TSESTree.AST_NODE_TYPES,
+ ): boolean {
+ const parent = getParentNodeOfToken(token);
+
+ if (parent && isParentNodeType(parent, nodeType)) {
+ const parentStartNodeOrToken = parent;
+
+ return (
+ token.loc.start.line - parentStartNodeOrToken.loc.start.line === 1
+ );
+ }
+
+ return false;
+ }
+
+ /**
+ * @returns whether comments are at the parent end.
+ */
+ 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
+ );
+ }
+
+ function isCommentAtInterfaceStart(token: TSESTree.Comment): boolean {
+ return isCommentAtParentStart(token, AST_NODE_TYPES.TSInterfaceBody);
+ }
+
+ function isCommentAtInterfaceEnd(token: TSESTree.Comment): boolean {
+ return isCommentAtParentEnd(token, AST_NODE_TYPES.TSInterfaceBody);
+ }
+
+ function isCommentAtTypeStart(token: TSESTree.Comment): boolean {
+ return isCommentAtParentStart(token, AST_NODE_TYPES.TSTypeLiteral);
+ }
+
+ function isCommentAtTypeEnd(token: TSESTree.Comment): boolean {
+ 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) ||
+ isCommentAtEnumStart(token) ||
+ isCommentAtEnumEnd(token) ||
+ isCommentAtModuleStart(token) ||
+ isCommentAtModuleEnd(token)
+ );
+ }
+
+ function checkForEmptyLine(
+ token: TSESTree.Comment,
+ { before, after }: { before?: boolean; after?: boolean },
+ ): void {
+ // the base rule handles comments away from TS constructs blocks correctly, we skip those
+ if (!isCommentNearTSConstruct(token)) {
+ return;
+ }
+
+ if (
+ options.applyDefaultIgnorePatterns !== false &&
+ defaultIgnoreRegExp.test(token.value)
+ ) {
+ return;
+ }
+
+ if (options.ignorePattern && customIgnoreRegExp.test(token.value)) {
+ return;
+ }
+
+ const prevLineNum = token.loc.start.line - 1;
+ const nextLineNum = token.loc.end.line + 1;
+
+ // we ignore all inline comments
+ if (codeAroundComment(token)) {
+ return;
+ }
+
+ const interfaceStartAllowed =
+ 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,
+ });
+ 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');
+ },
+ });
+ }
+ }
+
+ /**
+ * 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
+ ) {
+ if (isCommentNearTSConstruct(descriptor.node)) {
+ return;
+ }
+ }
+ }
+ return context.report(descriptor);
+ };
+
+ const customContext = { report: customReport };
+
+ // 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,
+ {
+ 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) {
+ 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/src/util/getESLintCoreRule.ts b/packages/eslint-plugin/src/util/getESLintCoreRule.ts
index ae68a317e08..96785ad6f15 100644
--- a/packages/eslint-plugin/src/util/getESLintCoreRule.ts
+++ b/packages/eslint-plugin/src/util/getESLintCoreRule.ts
@@ -15,6 +15,7 @@ interface RuleMap {
'init-declarations': typeof import('eslint/lib/rules/init-declarations');
'key-spacing': typeof import('eslint/lib/rules/key-spacing');
'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 00000000000..a312bb19cd0
--- /dev/null
+++ b/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts
@@ -0,0 +1,1053 @@
+import { AST_TOKEN_TYPES } from '@typescript-eslint/utils';
+
+import rule from '../../src/rules/lines-around-comment';
+import { RuleTester } from '../RuleTester';
+import { unIndent } from './indent/utils';
+
+const ruleTester = new RuleTester({
+ parser: '@typescript-eslint/parser',
+});
+
+ruleTester.run('lines-around-comment', rule, {
+ valid: [
+ // 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: [
+ // 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`
+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 }],
+ },
+
+ // 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: [
+ {
+ beforeBlockComment: false,
+ afterBlockComment: true,
+ allowInterfaceEnd: false,
+ },
+ ],
+ 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 }],
+ },
+
+ // 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 }],
+ },
+ ],
+});
diff --git a/packages/eslint-plugin/typings/eslint-rules.d.ts b/packages/eslint-plugin/typings/eslint-rules.d.ts
index f5e0f715f0b..1726745df3f 100644
--- a/packages/eslint-plugin/typings/eslint-rules.d.ts
+++ b/packages/eslint-plugin/typings/eslint-rules.d.ts
@@ -730,6 +730,44 @@ declare module 'eslint/lib/rules/no-extra-semi' {
export = rule;
}
+declare module 'eslint/lib/rules/lines-around-comment' {
+ import type { 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;
+ allowInterfaceStart?: boolean;
+ allowInterfaceEnd?: boolean;
+ allowTypeStart?: boolean;
+ allowTypeEnd?: boolean;
+ allowEnumStart?: boolean;
+ allowEnumEnd?: boolean;
+ allowModuleStart?: boolean;
+ allowModuleEnd?: boolean;
+ ignorePattern?: string;
+ applyDefaultIgnorePatterns?: boolean;
+ }?,
+ ],
+ {
+ Program(): void;
+ }
+ >;
+ export = rule;
+}
+
declare module 'eslint/lib/rules/lines-between-class-members' {
import type { TSESLint, TSESTree } from '@typescript-eslint/utils';