@adguard/aglint
Version:
Universal adblock filter list linter.
65 lines (62 loc) • 2.93 kB
JavaScript
/*
* AGLint v3.0.0 (build date: Wed, 21 May 2025 13:24:14 GMT)
* (c) 2025 AdGuard Software Ltd.
* Released under the MIT license
* https://github.com/AdguardTeam/AGLint#readme
*/
import cloneDeep from 'clone-deep';
import { RuleCategory, CosmeticRuleType } from '@adguard/agtree';
import { SEVERITY } from '../severity.js';
import { isNull } from '../../utils/type-guards.js';
import { CssTreeParsingContext } from '../helpers/css-tree-types.js';
import { generateSelector } from '../helpers/css-generate.js';
/**
* Rule that checks if a cosmetic rule contains multiple selectors
*/
const SingleSelector = {
meta: {
severity: SEVERITY.warn,
},
events: {
onRule: (context) => {
// Get actually iterated adblock rule
const ast = context.getActualAdblockRuleAst();
// Check if the rule is an element hiding rule
if (ast.category === RuleCategory.Cosmetic && ast.type === CosmeticRuleType.ElementHidingRule) {
const rawSelectorList = ast.body.selectorList;
const selectorListNode = context.getCssNode(rawSelectorList, CssTreeParsingContext.SelectorList);
if (isNull(selectorListNode)) {
return;
}
// Only makes sense to check this, if there are at least two selectors within the rule
if (selectorListNode.children.size < 2) {
return;
}
// Prepare the problem report
const report = {
message: 'An element hiding rule should contain only one selector',
node: ast.body.selectorList,
};
// Suggest a fix for the problem if the user wants to fix the problem automatically
if (context.fixingEnabled()) {
// Collect selectors into different rules (one selector per rule)
report.fix = [];
// Iterate over all selectors in the current rule
for (const selector of selectorListNode.children) {
// Create a new rule with the same properties as the current rule.
const clone = cloneDeep(ast);
// Replace the selector list with a new selector list containing only
// the currently iterated selector
clone.body.selectorList.value = generateSelector(selector);
// The only difference is that the new rule only contains one selector,
// which has the currently iterated selector in its body.
report.fix.push(clone);
}
}
// Report the problem to the linting context
context.report(report);
}
},
},
};
export { SingleSelector };