613 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			613 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
/**
 | 
						|
 * @fileoverview Rule to require or disallow newlines between statements
 | 
						|
 * @author Toru Nagashima
 | 
						|
 * @deprecated in ESLint v8.53.0
 | 
						|
 */
 | 
						|
 | 
						|
"use strict";
 | 
						|
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
// Requirements
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
 | 
						|
const astUtils = require("./utils/ast-utils");
 | 
						|
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
// Helpers
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
 | 
						|
const LT = `[${Array.from(astUtils.LINEBREAKS).join("")}]`;
 | 
						|
const PADDING_LINE_SEQUENCE = new RegExp(
 | 
						|
	String.raw`^(\s*?${LT})\s*${LT}(\s*;?)$`,
 | 
						|
	"u",
 | 
						|
);
 | 
						|
const CJS_EXPORT = /^(?:module\s*\.\s*)?exports(?:\s*\.|\s*\[|$)/u;
 | 
						|
const CJS_IMPORT = /^require\(/u;
 | 
						|
 | 
						|
/**
 | 
						|
 * Creates tester which check if a node starts with specific keyword.
 | 
						|
 * @param {string} keyword The keyword to test.
 | 
						|
 * @returns {Object} the created tester.
 | 
						|
 * @private
 | 
						|
 */
 | 
						|
function newKeywordTester(keyword) {
 | 
						|
	return {
 | 
						|
		test: (node, sourceCode) =>
 | 
						|
			sourceCode.getFirstToken(node).value === keyword,
 | 
						|
	};
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Creates tester which check if a node starts with specific keyword and spans a single line.
 | 
						|
 * @param {string} keyword The keyword to test.
 | 
						|
 * @returns {Object} the created tester.
 | 
						|
 * @private
 | 
						|
 */
 | 
						|
function newSinglelineKeywordTester(keyword) {
 | 
						|
	return {
 | 
						|
		test: (node, sourceCode) =>
 | 
						|
			node.loc.start.line === node.loc.end.line &&
 | 
						|
			sourceCode.getFirstToken(node).value === keyword,
 | 
						|
	};
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Creates tester which check if a node starts with specific keyword and spans multiple lines.
 | 
						|
 * @param {string} keyword The keyword to test.
 | 
						|
 * @returns {Object} the created tester.
 | 
						|
 * @private
 | 
						|
 */
 | 
						|
function newMultilineKeywordTester(keyword) {
 | 
						|
	return {
 | 
						|
		test: (node, sourceCode) =>
 | 
						|
			node.loc.start.line !== node.loc.end.line &&
 | 
						|
			sourceCode.getFirstToken(node).value === keyword,
 | 
						|
	};
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Creates tester which check if a node is specific type.
 | 
						|
 * @param {string} type The node type to test.
 | 
						|
 * @returns {Object} the created tester.
 | 
						|
 * @private
 | 
						|
 */
 | 
						|
function newNodeTypeTester(type) {
 | 
						|
	return {
 | 
						|
		test: node => node.type === type,
 | 
						|
	};
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Checks the given node is an expression statement of IIFE.
 | 
						|
 * @param {ASTNode} node The node to check.
 | 
						|
 * @returns {boolean} `true` if the node is an expression statement of IIFE.
 | 
						|
 * @private
 | 
						|
 */
 | 
						|
function isIIFEStatement(node) {
 | 
						|
	if (node.type === "ExpressionStatement") {
 | 
						|
		let call = astUtils.skipChainExpression(node.expression);
 | 
						|
 | 
						|
		if (call.type === "UnaryExpression") {
 | 
						|
			call = astUtils.skipChainExpression(call.argument);
 | 
						|
		}
 | 
						|
		return (
 | 
						|
			call.type === "CallExpression" && astUtils.isFunction(call.callee)
 | 
						|
		);
 | 
						|
	}
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Checks whether the given node is a block-like statement.
 | 
						|
 * This checks the last token of the node is the closing brace of a block.
 | 
						|
 * @param {SourceCode} sourceCode The source code to get tokens.
 | 
						|
 * @param {ASTNode} node The node to check.
 | 
						|
 * @returns {boolean} `true` if the node is a block-like statement.
 | 
						|
 * @private
 | 
						|
 */
 | 
						|
function isBlockLikeStatement(sourceCode, node) {
 | 
						|
	// do-while with a block is a block-like statement.
 | 
						|
	if (
 | 
						|
		node.type === "DoWhileStatement" &&
 | 
						|
		node.body.type === "BlockStatement"
 | 
						|
	) {
 | 
						|
		return true;
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * IIFE is a block-like statement specially from
 | 
						|
	 * JSCS#disallowPaddingNewLinesAfterBlocks.
 | 
						|
	 */
 | 
						|
	if (isIIFEStatement(node)) {
 | 
						|
		return true;
 | 
						|
	}
 | 
						|
 | 
						|
	// Checks the last token is a closing brace of blocks.
 | 
						|
	const lastToken = sourceCode.getLastToken(
 | 
						|
		node,
 | 
						|
		astUtils.isNotSemicolonToken,
 | 
						|
	);
 | 
						|
	const belongingNode =
 | 
						|
		lastToken && astUtils.isClosingBraceToken(lastToken)
 | 
						|
			? sourceCode.getNodeByRangeIndex(lastToken.range[0])
 | 
						|
			: null;
 | 
						|
 | 
						|
	return (
 | 
						|
		Boolean(belongingNode) &&
 | 
						|
		(belongingNode.type === "BlockStatement" ||
 | 
						|
			belongingNode.type === "SwitchStatement")
 | 
						|
	);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Gets the actual last token.
 | 
						|
 *
 | 
						|
 * If a semicolon is semicolon-less style's semicolon, this ignores it.
 | 
						|
 * For example:
 | 
						|
 *
 | 
						|
 *     foo()
 | 
						|
 *     ;[1, 2, 3].forEach(bar)
 | 
						|
 * @param {SourceCode} sourceCode The source code to get tokens.
 | 
						|
 * @param {ASTNode} node The node to get.
 | 
						|
 * @returns {Token} The actual last token.
 | 
						|
 * @private
 | 
						|
 */
 | 
						|
function getActualLastToken(sourceCode, node) {
 | 
						|
	const semiToken = sourceCode.getLastToken(node);
 | 
						|
	const prevToken = sourceCode.getTokenBefore(semiToken);
 | 
						|
	const nextToken = sourceCode.getTokenAfter(semiToken);
 | 
						|
	const isSemicolonLessStyle = Boolean(
 | 
						|
		prevToken &&
 | 
						|
			nextToken &&
 | 
						|
			prevToken.range[0] >= node.range[0] &&
 | 
						|
			astUtils.isSemicolonToken(semiToken) &&
 | 
						|
			semiToken.loc.start.line !== prevToken.loc.end.line &&
 | 
						|
			semiToken.loc.end.line === nextToken.loc.start.line,
 | 
						|
	);
 | 
						|
 | 
						|
	return isSemicolonLessStyle ? prevToken : semiToken;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * This returns the concatenation of the first 2 captured strings.
 | 
						|
 * @param {string} _ Unused. Whole matched string.
 | 
						|
 * @param {string} trailingSpaces The trailing spaces of the first line.
 | 
						|
 * @param {string} indentSpaces The indentation spaces of the last line.
 | 
						|
 * @returns {string} The concatenation of trailingSpaces and indentSpaces.
 | 
						|
 * @private
 | 
						|
 */
 | 
						|
function replacerToRemovePaddingLines(_, trailingSpaces, indentSpaces) {
 | 
						|
	return trailingSpaces + indentSpaces;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Check and report statements for `any` configuration.
 | 
						|
 * It does nothing.
 | 
						|
 * @returns {void}
 | 
						|
 * @private
 | 
						|
 */
 | 
						|
function verifyForAny() {}
 | 
						|
 | 
						|
/**
 | 
						|
 * Check and report statements for `never` configuration.
 | 
						|
 * This autofix removes blank lines between the given 2 statements.
 | 
						|
 * However, if comments exist between 2 blank lines, it does not remove those
 | 
						|
 * blank lines automatically.
 | 
						|
 * @param {RuleContext} context The rule context to report.
 | 
						|
 * @param {ASTNode} _ Unused. The previous node to check.
 | 
						|
 * @param {ASTNode} nextNode The next node to check.
 | 
						|
 * @param {Array<Token[]>} paddingLines The array of token pairs that blank
 | 
						|
 * lines exist between the pair.
 | 
						|
 * @returns {void}
 | 
						|
 * @private
 | 
						|
 */
 | 
						|
function verifyForNever(context, _, nextNode, paddingLines) {
 | 
						|
	if (paddingLines.length === 0) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	context.report({
 | 
						|
		node: nextNode,
 | 
						|
		messageId: "unexpectedBlankLine",
 | 
						|
		fix(fixer) {
 | 
						|
			if (paddingLines.length >= 2) {
 | 
						|
				return null;
 | 
						|
			}
 | 
						|
 | 
						|
			const prevToken = paddingLines[0][0];
 | 
						|
			const nextToken = paddingLines[0][1];
 | 
						|
			const start = prevToken.range[1];
 | 
						|
			const end = nextToken.range[0];
 | 
						|
			const text = context.sourceCode.text
 | 
						|
				.slice(start, end)
 | 
						|
				.replace(PADDING_LINE_SEQUENCE, replacerToRemovePaddingLines);
 | 
						|
 | 
						|
			return fixer.replaceTextRange([start, end], text);
 | 
						|
		},
 | 
						|
	});
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Check and report statements for `always` configuration.
 | 
						|
 * This autofix inserts a blank line between the given 2 statements.
 | 
						|
 * If the `prevNode` has trailing comments, it inserts a blank line after the
 | 
						|
 * trailing comments.
 | 
						|
 * @param {RuleContext} context The rule context to report.
 | 
						|
 * @param {ASTNode} prevNode The previous node to check.
 | 
						|
 * @param {ASTNode} nextNode The next node to check.
 | 
						|
 * @param {Array<Token[]>} paddingLines The array of token pairs that blank
 | 
						|
 * lines exist between the pair.
 | 
						|
 * @returns {void}
 | 
						|
 * @private
 | 
						|
 */
 | 
						|
function verifyForAlways(context, prevNode, nextNode, paddingLines) {
 | 
						|
	if (paddingLines.length > 0) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	context.report({
 | 
						|
		node: nextNode,
 | 
						|
		messageId: "expectedBlankLine",
 | 
						|
		fix(fixer) {
 | 
						|
			const sourceCode = context.sourceCode;
 | 
						|
			let prevToken = getActualLastToken(sourceCode, prevNode);
 | 
						|
			const nextToken =
 | 
						|
				sourceCode.getFirstTokenBetween(prevToken, nextNode, {
 | 
						|
					includeComments: true,
 | 
						|
 | 
						|
					/**
 | 
						|
					 * Skip the trailing comments of the previous node.
 | 
						|
					 * This inserts a blank line after the last trailing comment.
 | 
						|
					 *
 | 
						|
					 * For example:
 | 
						|
					 *
 | 
						|
					 *     foo(); // trailing comment.
 | 
						|
					 *     // comment.
 | 
						|
					 *     bar();
 | 
						|
					 *
 | 
						|
					 * Get fixed to:
 | 
						|
					 *
 | 
						|
					 *     foo(); // trailing comment.
 | 
						|
					 *
 | 
						|
					 *     // comment.
 | 
						|
					 *     bar();
 | 
						|
					 * @param {Token} token The token to check.
 | 
						|
					 * @returns {boolean} `true` if the token is not a trailing comment.
 | 
						|
					 * @private
 | 
						|
					 */
 | 
						|
					filter(token) {
 | 
						|
						if (astUtils.isTokenOnSameLine(prevToken, token)) {
 | 
						|
							prevToken = token;
 | 
						|
							return false;
 | 
						|
						}
 | 
						|
						return true;
 | 
						|
					},
 | 
						|
				}) || nextNode;
 | 
						|
			const insertText = astUtils.isTokenOnSameLine(prevToken, nextToken)
 | 
						|
				? "\n\n"
 | 
						|
				: "\n";
 | 
						|
 | 
						|
			return fixer.insertTextAfter(prevToken, insertText);
 | 
						|
		},
 | 
						|
	});
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Types of blank lines.
 | 
						|
 * `any`, `never`, and `always` are defined.
 | 
						|
 * Those have `verify` method to check and report statements.
 | 
						|
 * @private
 | 
						|
 */
 | 
						|
const PaddingTypes = {
 | 
						|
	any: { verify: verifyForAny },
 | 
						|
	never: { verify: verifyForNever },
 | 
						|
	always: { verify: verifyForAlways },
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Types of statements.
 | 
						|
 * Those have `test` method to check it matches to the given statement.
 | 
						|
 * @private
 | 
						|
 */
 | 
						|
const StatementTypes = {
 | 
						|
	"*": { test: () => true },
 | 
						|
	"block-like": {
 | 
						|
		test: (node, sourceCode) => isBlockLikeStatement(sourceCode, node),
 | 
						|
	},
 | 
						|
	"cjs-export": {
 | 
						|
		test: (node, sourceCode) =>
 | 
						|
			node.type === "ExpressionStatement" &&
 | 
						|
			node.expression.type === "AssignmentExpression" &&
 | 
						|
			CJS_EXPORT.test(sourceCode.getText(node.expression.left)),
 | 
						|
	},
 | 
						|
	"cjs-import": {
 | 
						|
		test: (node, sourceCode) =>
 | 
						|
			node.type === "VariableDeclaration" &&
 | 
						|
			node.declarations.length > 0 &&
 | 
						|
			Boolean(node.declarations[0].init) &&
 | 
						|
			CJS_IMPORT.test(sourceCode.getText(node.declarations[0].init)),
 | 
						|
	},
 | 
						|
	directive: {
 | 
						|
		test: astUtils.isDirective,
 | 
						|
	},
 | 
						|
	expression: {
 | 
						|
		test: node =>
 | 
						|
			node.type === "ExpressionStatement" && !astUtils.isDirective(node),
 | 
						|
	},
 | 
						|
	iife: {
 | 
						|
		test: isIIFEStatement,
 | 
						|
	},
 | 
						|
	"multiline-block-like": {
 | 
						|
		test: (node, sourceCode) =>
 | 
						|
			node.loc.start.line !== node.loc.end.line &&
 | 
						|
			isBlockLikeStatement(sourceCode, node),
 | 
						|
	},
 | 
						|
	"multiline-expression": {
 | 
						|
		test: node =>
 | 
						|
			node.loc.start.line !== node.loc.end.line &&
 | 
						|
			node.type === "ExpressionStatement" &&
 | 
						|
			!astUtils.isDirective(node),
 | 
						|
	},
 | 
						|
 | 
						|
	"multiline-const": newMultilineKeywordTester("const"),
 | 
						|
	"multiline-let": newMultilineKeywordTester("let"),
 | 
						|
	"multiline-var": newMultilineKeywordTester("var"),
 | 
						|
	"singleline-const": newSinglelineKeywordTester("const"),
 | 
						|
	"singleline-let": newSinglelineKeywordTester("let"),
 | 
						|
	"singleline-var": newSinglelineKeywordTester("var"),
 | 
						|
 | 
						|
	block: newNodeTypeTester("BlockStatement"),
 | 
						|
	empty: newNodeTypeTester("EmptyStatement"),
 | 
						|
	function: newNodeTypeTester("FunctionDeclaration"),
 | 
						|
 | 
						|
	break: newKeywordTester("break"),
 | 
						|
	case: newKeywordTester("case"),
 | 
						|
	class: newKeywordTester("class"),
 | 
						|
	const: newKeywordTester("const"),
 | 
						|
	continue: newKeywordTester("continue"),
 | 
						|
	debugger: newKeywordTester("debugger"),
 | 
						|
	default: newKeywordTester("default"),
 | 
						|
	do: newKeywordTester("do"),
 | 
						|
	export: newKeywordTester("export"),
 | 
						|
	for: newKeywordTester("for"),
 | 
						|
	if: newKeywordTester("if"),
 | 
						|
	import: newKeywordTester("import"),
 | 
						|
	let: newKeywordTester("let"),
 | 
						|
	return: newKeywordTester("return"),
 | 
						|
	switch: newKeywordTester("switch"),
 | 
						|
	throw: newKeywordTester("throw"),
 | 
						|
	try: newKeywordTester("try"),
 | 
						|
	var: newKeywordTester("var"),
 | 
						|
	while: newKeywordTester("while"),
 | 
						|
	with: newKeywordTester("with"),
 | 
						|
};
 | 
						|
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
// Rule Definition
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
 | 
						|
/** @type {import('../types').Rule.RuleModule} */
 | 
						|
module.exports = {
 | 
						|
	meta: {
 | 
						|
		deprecated: {
 | 
						|
			message: "Formatting rules are being moved out of ESLint core.",
 | 
						|
			url: "https://eslint.org/blog/2023/10/deprecating-formatting-rules/",
 | 
						|
			deprecatedSince: "8.53.0",
 | 
						|
			availableUntil: "10.0.0",
 | 
						|
			replacedBy: [
 | 
						|
				{
 | 
						|
					message:
 | 
						|
						"ESLint Stylistic now maintains deprecated stylistic core rules.",
 | 
						|
					url: "https://eslint.style/guide/migration",
 | 
						|
					plugin: {
 | 
						|
						name: "@stylistic/eslint-plugin",
 | 
						|
						url: "https://eslint.style",
 | 
						|
					},
 | 
						|
					rule: {
 | 
						|
						name: "padding-line-between-statements",
 | 
						|
						url: "https://eslint.style/rules/padding-line-between-statements",
 | 
						|
					},
 | 
						|
				},
 | 
						|
			],
 | 
						|
		},
 | 
						|
		type: "layout",
 | 
						|
 | 
						|
		docs: {
 | 
						|
			description: "Require or disallow padding lines between statements",
 | 
						|
			recommended: false,
 | 
						|
			url: "https://eslint.org/docs/latest/rules/padding-line-between-statements",
 | 
						|
		},
 | 
						|
 | 
						|
		fixable: "whitespace",
 | 
						|
 | 
						|
		schema: {
 | 
						|
			definitions: {
 | 
						|
				paddingType: {
 | 
						|
					enum: Object.keys(PaddingTypes),
 | 
						|
				},
 | 
						|
				statementType: {
 | 
						|
					anyOf: [
 | 
						|
						{ enum: Object.keys(StatementTypes) },
 | 
						|
						{
 | 
						|
							type: "array",
 | 
						|
							items: { enum: Object.keys(StatementTypes) },
 | 
						|
							minItems: 1,
 | 
						|
							uniqueItems: true,
 | 
						|
						},
 | 
						|
					],
 | 
						|
				},
 | 
						|
			},
 | 
						|
			type: "array",
 | 
						|
			items: {
 | 
						|
				type: "object",
 | 
						|
				properties: {
 | 
						|
					blankLine: { $ref: "#/definitions/paddingType" },
 | 
						|
					prev: { $ref: "#/definitions/statementType" },
 | 
						|
					next: { $ref: "#/definitions/statementType" },
 | 
						|
				},
 | 
						|
				additionalProperties: false,
 | 
						|
				required: ["blankLine", "prev", "next"],
 | 
						|
			},
 | 
						|
		},
 | 
						|
 | 
						|
		messages: {
 | 
						|
			unexpectedBlankLine: "Unexpected blank line before this statement.",
 | 
						|
			expectedBlankLine: "Expected blank line before this statement.",
 | 
						|
		},
 | 
						|
	},
 | 
						|
 | 
						|
	create(context) {
 | 
						|
		const sourceCode = context.sourceCode;
 | 
						|
		const configureList = context.options || [];
 | 
						|
		let scopeInfo = null;
 | 
						|
 | 
						|
		/**
 | 
						|
		 * Processes to enter to new scope.
 | 
						|
		 * This manages the current previous statement.
 | 
						|
		 * @returns {void}
 | 
						|
		 * @private
 | 
						|
		 */
 | 
						|
		function enterScope() {
 | 
						|
			scopeInfo = {
 | 
						|
				upper: scopeInfo,
 | 
						|
				prevNode: null,
 | 
						|
			};
 | 
						|
		}
 | 
						|
 | 
						|
		/**
 | 
						|
		 * Processes to exit from the current scope.
 | 
						|
		 * @returns {void}
 | 
						|
		 * @private
 | 
						|
		 */
 | 
						|
		function exitScope() {
 | 
						|
			scopeInfo = scopeInfo.upper;
 | 
						|
		}
 | 
						|
 | 
						|
		/**
 | 
						|
		 * Checks whether the given node matches the given type.
 | 
						|
		 * @param {ASTNode} node The statement node to check.
 | 
						|
		 * @param {string|string[]} type The statement type to check.
 | 
						|
		 * @returns {boolean} `true` if the statement node matched the type.
 | 
						|
		 * @private
 | 
						|
		 */
 | 
						|
		function match(node, type) {
 | 
						|
			let innerStatementNode = node;
 | 
						|
 | 
						|
			while (innerStatementNode.type === "LabeledStatement") {
 | 
						|
				innerStatementNode = innerStatementNode.body;
 | 
						|
			}
 | 
						|
			if (Array.isArray(type)) {
 | 
						|
				return type.some(match.bind(null, innerStatementNode));
 | 
						|
			}
 | 
						|
			return StatementTypes[type].test(innerStatementNode, sourceCode);
 | 
						|
		}
 | 
						|
 | 
						|
		/**
 | 
						|
		 * Finds the last matched configure from configureList.
 | 
						|
		 * @param {ASTNode} prevNode The previous statement to match.
 | 
						|
		 * @param {ASTNode} nextNode The current statement to match.
 | 
						|
		 * @returns {Object} The tester of the last matched configure.
 | 
						|
		 * @private
 | 
						|
		 */
 | 
						|
		function getPaddingType(prevNode, nextNode) {
 | 
						|
			for (let i = configureList.length - 1; i >= 0; --i) {
 | 
						|
				const configure = configureList[i];
 | 
						|
				const matched =
 | 
						|
					match(prevNode, configure.prev) &&
 | 
						|
					match(nextNode, configure.next);
 | 
						|
 | 
						|
				if (matched) {
 | 
						|
					return PaddingTypes[configure.blankLine];
 | 
						|
				}
 | 
						|
			}
 | 
						|
			return PaddingTypes.any;
 | 
						|
		}
 | 
						|
 | 
						|
		/**
 | 
						|
		 * Gets padding line sequences between the given 2 statements.
 | 
						|
		 * Comments are separators of the padding line sequences.
 | 
						|
		 * @param {ASTNode} prevNode The previous statement to count.
 | 
						|
		 * @param {ASTNode} nextNode The current statement to count.
 | 
						|
		 * @returns {Array<Token[]>} The array of token pairs.
 | 
						|
		 * @private
 | 
						|
		 */
 | 
						|
		function getPaddingLineSequences(prevNode, nextNode) {
 | 
						|
			const pairs = [];
 | 
						|
			let prevToken = getActualLastToken(sourceCode, prevNode);
 | 
						|
 | 
						|
			if (nextNode.loc.start.line - prevToken.loc.end.line >= 2) {
 | 
						|
				do {
 | 
						|
					const token = sourceCode.getTokenAfter(prevToken, {
 | 
						|
						includeComments: true,
 | 
						|
					});
 | 
						|
 | 
						|
					if (token.loc.start.line - prevToken.loc.end.line >= 2) {
 | 
						|
						pairs.push([prevToken, token]);
 | 
						|
					}
 | 
						|
					prevToken = token;
 | 
						|
				} while (prevToken.range[0] < nextNode.range[0]);
 | 
						|
			}
 | 
						|
 | 
						|
			return pairs;
 | 
						|
		}
 | 
						|
 | 
						|
		/**
 | 
						|
		 * Verify padding lines between the given node and the previous node.
 | 
						|
		 * @param {ASTNode} node The node to verify.
 | 
						|
		 * @returns {void}
 | 
						|
		 * @private
 | 
						|
		 */
 | 
						|
		function verify(node) {
 | 
						|
			const parentType = node.parent.type;
 | 
						|
			const validParent =
 | 
						|
				astUtils.STATEMENT_LIST_PARENTS.has(parentType) ||
 | 
						|
				parentType === "SwitchStatement";
 | 
						|
 | 
						|
			if (!validParent) {
 | 
						|
				return;
 | 
						|
			}
 | 
						|
 | 
						|
			// Save this node as the current previous statement.
 | 
						|
			const prevNode = scopeInfo.prevNode;
 | 
						|
 | 
						|
			// Verify.
 | 
						|
			if (prevNode) {
 | 
						|
				const type = getPaddingType(prevNode, node);
 | 
						|
				const paddingLines = getPaddingLineSequences(prevNode, node);
 | 
						|
 | 
						|
				type.verify(context, prevNode, node, paddingLines);
 | 
						|
			}
 | 
						|
 | 
						|
			scopeInfo.prevNode = node;
 | 
						|
		}
 | 
						|
 | 
						|
		/**
 | 
						|
		 * Verify padding lines between the given node and the previous node.
 | 
						|
		 * Then process to enter to new scope.
 | 
						|
		 * @param {ASTNode} node The node to verify.
 | 
						|
		 * @returns {void}
 | 
						|
		 * @private
 | 
						|
		 */
 | 
						|
		function verifyThenEnterScope(node) {
 | 
						|
			verify(node);
 | 
						|
			enterScope();
 | 
						|
		}
 | 
						|
 | 
						|
		return {
 | 
						|
			Program: enterScope,
 | 
						|
			BlockStatement: enterScope,
 | 
						|
			SwitchStatement: enterScope,
 | 
						|
			StaticBlock: enterScope,
 | 
						|
			"Program:exit": exitScope,
 | 
						|
			"BlockStatement:exit": exitScope,
 | 
						|
			"SwitchStatement:exit": exitScope,
 | 
						|
			"StaticBlock:exit": exitScope,
 | 
						|
 | 
						|
			":statement": verify,
 | 
						|
 | 
						|
			SwitchCase: verifyThenEnterScope,
 | 
						|
			"SwitchCase:exit": exitScope,
 | 
						|
		};
 | 
						|
	},
 | 
						|
};
 |