stylelint-scss
Version:
A collection of SCSS-specific rules for Stylelint
138 lines (120 loc) • 3.29 kB
JavaScript
import { isBoolean } from "../../utils/validateTypes.js";
import isSingleLineString from "../../utils/isSingleLineString.js";
import namespace from "../../utils/namespace.js";
import ruleUrl from "../../utils/ruleUrl.js";
import stylelint from "stylelint";
const { utils } = stylelint;
const ruleName = namespace("at-if-closing-brace-newline-after");
const messages = utils.ruleMessages(ruleName, {
expected: 'Expected newline after "}" of @if statement',
rejected: 'Unexpected newline after "}" of @if statement'
});
const meta = {
url: ruleUrl(ruleName),
fixable: true
};
function rule(expectation, options, context) {
return (root, result) => {
const validOptions = utils.validateOptions(
result,
ruleName,
{
actual: expectation,
possible: ["always-last-in-chain"]
},
{
actual: options,
possible: {
disableFix: isBoolean
},
optional: true
}
);
if (!validOptions) {
return;
}
sassConditionalBraceNLAfterChecker({
root,
result,
ruleName,
atRuleName: "if",
expectation,
messages,
context,
options
});
};
}
/**
* The core logic for this rule. Can be imported by other rules with similar
* logic, namely at-else-closing-brace-newline-after
*
* @param {Object} args -- Named arguments object
* @param {PostCSS root} args.root
* @param {PostCSS result} args.result
* @param {String ruleName} args.ruleName - needed for `report` function
* @param {String} args.atRuleName - the name of the at-rule to be checked, e.g. "if", "else"
* @param {Object} args.messages - returned by stylelint.utils.ruleMessages
* @return {undefined}
*/
export function sassConditionalBraceNLAfterChecker({
root,
result,
ruleName: checkerRuleName,
atRuleName,
expectation,
messages: checkerMessages,
context,
options
}) {
const shouldFix = options?.disableFix !== true;
function complain(node, message, index, fixValue) {
const fix = () => {
node.next().raws.before = fixValue;
};
utils.report({
result,
ruleName: checkerRuleName,
node,
message,
index,
endIndex: index,
fix: shouldFix ? fix : undefined
});
}
root.walkAtRules(atrule => {
// Do nothing if it's not an @if
if (atrule.name !== atRuleName) {
return;
}
const nextNode = atrule.next();
if (!nextNode) {
return;
}
const nextBefore = nextNode.raws.before;
const hasNewLinesBeforeNext = nextBefore && !isSingleLineString(nextBefore);
const reportIndex = atrule.toString().length;
if (expectation === "always-last-in-chain") {
// If followed by @else, no newline is needed
if (
nextNode.type === "atrule" &&
(nextNode.name === "else" || nextNode.name === "elseif")
) {
if (hasNewLinesBeforeNext) {
complain(atrule, checkerMessages.rejected, reportIndex, " ");
}
} else if (!hasNewLinesBeforeNext) {
complain(
atrule,
checkerMessages.expected,
reportIndex,
context.newline
);
}
}
});
}
rule.ruleName = ruleName;
rule.messages = messages;
rule.meta = meta;
export default rule;