linter-bundle
Version:
Ready-to use bundle of linting tools, containing configurations for ESLint, stylelint and markdownlint.
106 lines (92 loc) • 2.91 kB
JavaScript
// @ts-nocheck
import isStandardSyntaxCombinator from 'stylelint/lib/utils/isStandardSyntaxCombinator.mjs';
import isStandardSyntaxRule from 'stylelint/lib/utils/isStandardSyntaxRule.mjs';
import parseSelector from 'stylelint/lib/utils/parseSelector.mjs';
import report from 'stylelint/lib/utils/report.mjs';
/**
* @typedef {(args: { source: string, index: number, errTarget: string, err: (message: string) => void }) => void} LocationChecker
* @param {{
* root: import('postcss').Root,
* result: import('stylelint').PostcssResult,
* locationChecker: LocationChecker,
* locationType: 'before' | 'after',
* checkedRuleName: string,
* fix: ((combinator: import('postcss-selector-parser').Combinator) => boolean) | null
* }} options
* @returns {void}
*/
export default function selectorCombinatorSpaceChecker (options) {
let hasFixed;
options.root.walkRules((rule) => {
if (!isStandardSyntaxRule(rule)) {
return;
}
hasFixed = false;
const selector = rule.raws.selector ? rule.raws.selector.raw : rule.selector;
const fixedSelector = parseSelector(selector, options.result, rule, (selectorTree) => {
selectorTree.walkCombinators((node) => {
// Ignore non-standard combinators
if (!isStandardSyntaxCombinator(node)) {
return;
}
// Ignore spaced descendant combinator
if ((/\s/u).test(node.value)) {
return;
}
// Check the exist of node in prev of the combinator.
// in case some that aren't the first begin with combinators (nesting syntax)
if (options.locationType === 'before' && !node.prev()) {
return;
}
const parentParentNode = node.parent?.parent;
// Ignore pseudo-classes selector like `.foo:nth-child(2n + 1) {}`
if (parentParentNode && parentParentNode.type === 'pseudo') {
return;
}
const sourceIndex = node.sourceIndex;
const index =
node.value.length > 1 && options.locationType === 'before' ?
sourceIndex
: sourceIndex + node.value.length - 1;
check(selector, node, index, rule, sourceIndex);
});
});
if (hasFixed && fixedSelector) {
if (!rule.raws.selector) {
rule.selector = fixedSelector;
}
else {
rule.raws.selector.raw = fixedSelector;
}
}
});
/**
* @param {string} source
* @param {import('postcss-selector-parser').Combinator} combinator
* @param {number} index
* @param {import('postcss').Node} node
* @param {number} sourceIndex
*/
function check (source, combinator, index, node, sourceIndex) {
options.locationChecker({
source,
index,
errTarget: combinator.value,
err: (message) => {
report({
message,
node,
index: sourceIndex,
endIndex: sourceIndex,
result: options.result,
ruleName: options.checkedRuleName,
fix: (options.fix ? () => {
if (options.fix(combinator)) {
hasFixed = true;
}
} : undefined)
});
}
});
}
}