mirror of
https://github.com/OliveTin/OliveTin
synced 2025-12-19 04:25:32 +00:00
166 lines
4.3 KiB
JavaScript
166 lines
4.3 KiB
JavaScript
// NOTICE: This file is generated by Rollup. To modify it,
|
|
// please instead edit the ESM counterpart and rebuild with Rollup (npm run build).
|
|
'use strict';
|
|
|
|
const cssTree = require('css-tree');
|
|
const validateTypes = require('../../utils/validateTypes.cjs');
|
|
const nodeFieldIndices = require('../../utils/nodeFieldIndices.cjs');
|
|
const regexes = require('../../utils/regexes.cjs');
|
|
const getAtRuleParams = require('../../utils/getAtRuleParams.cjs');
|
|
const getRuleSelector = require('../../utils/getRuleSelector.cjs');
|
|
const isStandardSyntaxAtRule = require('../../utils/isStandardSyntaxAtRule.cjs');
|
|
const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule.cjs');
|
|
const optionsMatches = require('../../utils/optionsMatches.cjs');
|
|
const report = require('../../utils/report.cjs');
|
|
const ruleMessages = require('../../utils/ruleMessages.cjs');
|
|
const validateOptions = require('../../utils/validateOptions.cjs');
|
|
|
|
const ruleName = 'nesting-selector-no-missing-scoping-root';
|
|
|
|
const messages = ruleMessages(ruleName, {
|
|
rejected: 'Unexpected missing scoping root',
|
|
});
|
|
|
|
const meta = {
|
|
url: 'https://stylelint.io/user-guide/rules/nesting-selector-no-missing-scoping-root',
|
|
};
|
|
|
|
/** @type {import('stylelint').CoreRules[ruleName]} */
|
|
const rule = (primary, secondaryOptions) => {
|
|
return (root, result) => {
|
|
const validOptions = validateOptions(
|
|
result,
|
|
ruleName,
|
|
{ actual: primary, possible: [true] },
|
|
{
|
|
actual: secondaryOptions,
|
|
possible: {
|
|
ignoreAtRules: [validateTypes.isString, validateTypes.isRegExp],
|
|
},
|
|
optional: true,
|
|
},
|
|
);
|
|
|
|
if (!validOptions) return;
|
|
|
|
root.walkRules(/&/, (ruleNode) => {
|
|
if (!isStandardSyntaxRule(ruleNode)) return;
|
|
|
|
// Check if the rule is nested within a scoping root
|
|
if (hasValidScopingRoot(ruleNode, secondaryOptions)) return;
|
|
|
|
let ast;
|
|
|
|
try {
|
|
ast = cssTree.parse(getRuleSelector(ruleNode), { context: 'selectorList', positions: true });
|
|
} catch {
|
|
// Cannot parse selector list, skip checking
|
|
return;
|
|
}
|
|
|
|
check(ruleNode, ast);
|
|
});
|
|
|
|
// Check @scope at-rules for nesting selectors in parameters
|
|
root.walkAtRules(regexes.atRuleRegexes.scopeName, (atRule) => {
|
|
if (!isStandardSyntaxAtRule(atRule)) return;
|
|
|
|
// Cheap check for nesting selector
|
|
if (!atRule.params.includes('&')) return;
|
|
|
|
// Only check @scope at-rules that don't have a parent scoping context
|
|
if (hasValidScopingRoot(atRule, secondaryOptions)) return;
|
|
|
|
let ast;
|
|
|
|
try {
|
|
ast = cssTree.parse(getAtRuleParams(atRule), {
|
|
atrule: 'scope',
|
|
context: 'atrulePrelude',
|
|
positions: true,
|
|
});
|
|
} catch {
|
|
// Cannot parse @scope at-rule, skip checking
|
|
return;
|
|
}
|
|
|
|
check(atRule, ast, nodeFieldIndices.atRuleParamIndex(atRule));
|
|
});
|
|
|
|
/**
|
|
* @param {import('postcss').Rule | import('postcss').AtRule} node
|
|
* @param {import('css-tree').CssNode} ast
|
|
* @param {number} [offset=0]
|
|
*/
|
|
function check(node, ast, offset = 0) {
|
|
cssTree.walk(ast, {
|
|
/**
|
|
* @param {import('css-tree').CssNode} cssNode
|
|
*/
|
|
enter(cssNode) {
|
|
if (cssNode.type !== 'NestingSelector') return;
|
|
|
|
if (!cssNode.loc) return;
|
|
|
|
const index = offset + cssNode.loc.start.offset;
|
|
const endIndex = index + 1;
|
|
|
|
report({
|
|
message: messages.rejected,
|
|
node,
|
|
result,
|
|
ruleName,
|
|
index,
|
|
endIndex,
|
|
});
|
|
},
|
|
});
|
|
}
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Check if a node has a valid scoping root
|
|
* @param {import('postcss').Rule | import('postcss').AtRule} node
|
|
* @param {object} secondaryOptions
|
|
* @returns {boolean}
|
|
*/
|
|
function hasValidScopingRoot(node, secondaryOptions) {
|
|
let current = node.parent;
|
|
|
|
while (current) {
|
|
// If we find a rule in the parent chain, it provides a scoping root
|
|
if (current.type === 'rule') {
|
|
return true;
|
|
}
|
|
|
|
// If we find an @scope at-rule, it provides a scoping root
|
|
if (current.type === 'atrule' && current.name === 'scope') {
|
|
return true;
|
|
}
|
|
|
|
// If we find an ignored at-rule, it provides a scoping root
|
|
if (
|
|
current.type === 'atrule' &&
|
|
optionsMatches(secondaryOptions, 'ignoreAtRules', current.name)
|
|
) {
|
|
return true;
|
|
}
|
|
|
|
// If we reach the root without finding a scoping root
|
|
if (current.type === 'root') {
|
|
return false;
|
|
}
|
|
|
|
current = current.parent;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
rule.ruleName = ruleName;
|
|
rule.messages = messages;
|
|
rule.meta = meta;
|
|
|
|
module.exports = rule;
|