| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 | 'use strict';const beforeBlockString = require('../../utils/beforeBlockString');const blockString = require('../../utils/blockString');const hasBlock = require('../../utils/hasBlock');const hasEmptyBlock = require('../../utils/hasEmptyBlock');const optionsMatches = require('../../utils/optionsMatches');const rawNodeString = require('../../utils/rawNodeString');const report = require('../../utils/report');const ruleMessages = require('../../utils/ruleMessages');const validateOptions = require('../../utils/validateOptions');const whitespaceChecker = require('../../utils/whitespaceChecker');const ruleName = 'block-opening-brace-newline-after';const messages = ruleMessages(ruleName, {	expectedAfter: () => 'Expected newline after "{"',	expectedAfterMultiLine: () => 'Expected newline after "{" of a multi-line block',	rejectedAfterMultiLine: () => 'Unexpected whitespace after "{" of a multi-line block',});const meta = {	url: 'https://stylelint.io/user-guide/rules/block-opening-brace-newline-after',	fixable: true,};/** @type {import('stylelint').Rule} */const rule = (primary, secondaryOptions, context) => {	const checker = whitespaceChecker('newline', primary, messages);	return (root, result) => {		const validOptions = validateOptions(			result,			ruleName,			{				actual: primary,				possible: ['always', 'rules', 'always-multi-line', 'never-multi-line'],			},			{				actual: secondaryOptions,				possible: {					ignore: ['rules'],				},				optional: true,			},		);		if (!validOptions) {			return;		}		// Check both kinds of statement: rules and at-rules		if (!optionsMatches(secondaryOptions, 'ignore', 'rules')) {			root.walkRules(check);		}		root.walkAtRules(check);		/**		 * @param {import('postcss').Rule | import('postcss').AtRule} statement		 */		function check(statement) {			// Return early if blockless or has an empty block			if (!hasBlock(statement) || hasEmptyBlock(statement)) {				return;			}			const backupCommentNextBefores = new Map();			/**			 * next node with checking newlines after comment			 *			 * @param {import('postcss').ChildNode | undefined} startNode			 * @returns {import('postcss').ChildNode | undefined}			 */			function nextNode(startNode) {				if (!startNode || !startNode.next) return;				if (startNode.type === 'comment') {					const reNewLine = /\r?\n/;					const newLineMatch = reNewLine.test(startNode.raws.before || '');					const next = startNode.next();					if (next && newLineMatch && !reNewLine.test(next.raws.before || '')) {						backupCommentNextBefores.set(next, next.raws.before);						next.raws.before = startNode.raws.before;					}					return nextNode(next);				}				return startNode;			}			// Allow an end-of-line comment			const nodeToCheck = nextNode(statement.first);			if (!nodeToCheck) {				return;			}			checker.afterOneOnly({				source: rawNodeString(nodeToCheck),				index: -1,				lineCheckStr: blockString(statement),				err: (m) => {					if (context.fix) {						const nodeToCheckRaws = nodeToCheck.raws;						if (typeof nodeToCheckRaws.before !== 'string') return;						if (primary.startsWith('always')) {							const index = nodeToCheckRaws.before.search(/\r?\n/);							nodeToCheckRaws.before =								index >= 0									? nodeToCheckRaws.before.slice(index)									: context.newline + nodeToCheckRaws.before;							backupCommentNextBefores.delete(nodeToCheck);							return;						}						if (primary === 'never-multi-line') {							// Restore the `before` of the node next to the comment node.							for (const [node, before] of backupCommentNextBefores.entries()) {								node.raws.before = before;							}							backupCommentNextBefores.clear();							// Fix							const reNewLine = /\r?\n/;							let fixTarget = statement.first;							while (fixTarget) {								const fixTargetRaws = fixTarget.raws;								if (typeof fixTargetRaws.before !== 'string') continue;								if (reNewLine.test(fixTargetRaws.before || '')) {									fixTargetRaws.before = fixTargetRaws.before.replace(/\r?\n/g, '');								}								if (fixTarget.type !== 'comment') {									break;								}								fixTarget = fixTarget.next();							}							nodeToCheckRaws.before = '';							return;						}					}					report({						message: m,						node: statement,						index: beforeBlockString(statement, { noRawBefore: true }).length + 1,						result,						ruleName,					});				},			});			// Restore the `before` of the node next to the comment node.			for (const [node, before] of backupCommentNextBefores.entries()) {				node.raws.before = before;			}		}	};};rule.ruleName = ruleName;rule.messages = messages;rule.meta = meta;module.exports = rule;
 |