@adguard/aglint
Version:
Universal adblock filter list linter.
91 lines (88 loc) • 3.72 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 { RuleCategory, CommentRuleType } from '@adguard/agtree';
import { SEVERITY } from '../severity.js';
import { IF_DIRECTIVE, ENDIF_DIRECTIVE, ELSE_DIRECTIVE } from '../../common/constants.js';
/**
* Rule that checks if all if directives are closed
*/
const IfClosed = {
meta: {
severity: SEVERITY.error,
},
events: {
onStartFilterList: (context) => {
// Each rule ONLY sees its own storage. At the beginning of the filter list,
// we just initialize the storage.
context.storage.openIfs = [];
},
onRule: (context) => {
// Get actually iterated adblock rule
const rule = context.getActualAdblockRuleAst();
// Check adblock rule category and type
if (rule.category !== RuleCategory.Comment
|| rule.type !== CommentRuleType.PreProcessorCommentRule) {
return;
}
const directive = rule.name.value;
switch (directive) {
case IF_DIRECTIVE:
// Collect open "if"
context.storage.openIfs.push(rule);
break;
case ELSE_DIRECTIVE:
// '!#else' can only be used alone without any parameters
if (rule.params) {
context.report({
message: `Invalid usage of preprocessor directive: "${ELSE_DIRECTIVE}"`,
node: rule,
});
}
// Check if there is an open "!#if" before "!#else"
if (context.storage.openIfs.length === 0) {
context.report({
// eslint-disable-next-line max-len
message: `Using an "${ELSE_DIRECTIVE}" directive without an opening "${IF_DIRECTIVE}" directive`,
node: rule,
});
}
// otherwise do nothing
break;
case ENDIF_DIRECTIVE:
// '!#endif' can only be used alone without any parameters
if (rule.params) {
context.report({
message: `Invalid usage of preprocessor directive: "${ENDIF_DIRECTIVE}"`,
node: rule,
});
}
if (context.storage.openIfs.length === 0) {
context.report({
// eslint-disable-next-line max-len
message: `Using an "${ENDIF_DIRECTIVE}" directive without an opening "${IF_DIRECTIVE}" directive`,
node: rule,
});
}
else {
// Mark "if" as closed (simply delete it from collection)
context.storage.openIfs.pop();
}
break;
}
},
onEndFilterList: (context) => {
// If there are any collected "if"s, that means they aren't closed, so a problem must be reported for them
for (const rule of context.storage.openIfs) {
context.report({
message: `Unclosed "${IF_DIRECTIVE}" directive`,
node: rule,
});
}
},
},
};
export { IfClosed };