UNPKG

@adguard/agtree

Version:
120 lines (117 loc) 4.69 kB
/* * AGTree v3.2.2 (build date: Tue, 08 Jul 2025 13:39:47 GMT) * (c) 2025 Adguard Software Ltd. * Released under the MIT license * https://github.com/AdguardTeam/tsurlfilter/tree/master/packages/agtree#readme */ import { OPEN_SQUARE_BRACKET, CLOSE_SQUARE_BRACKET, SEMICOLON } from '../../utils/constants.js'; import { StringUtils } from '../../utils/string.js'; import { RuleCategory, CommentRuleType } from '../../nodes/index.js'; import { AgentParser } from './agent-parser.js'; import { AdblockSyntaxError } from '../../errors/adblock-syntax-error.js'; import { AdblockSyntax } from '../../utils/adblockers.js'; import { CosmeticRuleSeparatorUtils } from '../../utils/cosmetic-rule-separator.js'; import { BaseParser } from '../base-parser.js'; import { defaultParserOptions } from '../options.js'; import { isNull } from '../../utils/type-guards.js'; /** * `AgentParser` is responsible for parsing an Adblock agent rules. * Adblock agent comment marks that the filter list is supposed to * be used by the specified ad blockers. * * @example * - ```adblock * [AdGuard] * ``` * - ```adblock * [Adblock Plus 2.0] * ``` * - ```adblock * [uBlock Origin] * ``` * - ```adblock * [uBlock Origin 1.45.3] * ``` * - ```adblock * [Adblock Plus 2.0; AdGuard] * ``` */ class AgentCommentParser extends BaseParser { /** * Checks if the raw rule is an adblock agent comment. * * @param raw Raw rule * @returns `true` if the rule is an adblock agent, `false` otherwise */ static isAgentRule(raw) { const rawTrimmed = raw.trim(); if (rawTrimmed.startsWith(OPEN_SQUARE_BRACKET) && rawTrimmed.endsWith(CLOSE_SQUARE_BRACKET)) { // Avoid this case: [$adg-modifier]##[class^="adg-"] return isNull(CosmeticRuleSeparatorUtils.find(rawTrimmed)); } return false; } /** * Parses a raw rule as an adblock agent comment. * * @param raw Raw input to parse. * @param options Global parser options. * @param baseOffset Starting offset of the input. Node locations are calculated relative to this offset. * @returns Agent rule AST or null (if the raw rule cannot be parsed as an adblock agent comment) */ static parse(raw, options = defaultParserOptions, baseOffset = 0) { // Ignore non-agent rules if (!AgentCommentParser.isAgentRule(raw)) { return null; } let offset = 0; // Skip whitespace characters before the rule offset = StringUtils.skipWS(raw, offset); // Skip opening bracket offset += 1; // last character should be a closing bracket const closingBracketIndex = StringUtils.skipWSBack(raw, raw.length - 1); if (closingBracketIndex === -1 || raw[closingBracketIndex] !== CLOSE_SQUARE_BRACKET) { throw new AdblockSyntaxError('Missing closing bracket', offset, offset + raw.length); } // Initialize the agent list const result = { type: CommentRuleType.AgentCommentRule, syntax: AdblockSyntax.Common, category: RuleCategory.Comment, children: [], }; if (options.includeRaws) { result.raws = { text: raw, }; } if (options.isLocIncluded) { result.start = baseOffset; result.end = baseOffset + raw.length; } while (offset < closingBracketIndex) { // Skip whitespace characters before the agent offset = StringUtils.skipWS(raw, offset); // Find the separator or the closing bracket let separatorIndex = raw.indexOf(SEMICOLON, offset); if (separatorIndex === -1) { separatorIndex = closingBracketIndex; } // Find the last non-whitespace character of the agent // [AdGuard ; Adblock Plus 2.0] // ^ // (if we have spaces between the agent name and the separator) const agentEndIndex = StringUtils.findLastNonWhitespaceCharacter(raw.slice(offset, separatorIndex)) + offset + 1; // Collect the agent result.children.push(AgentParser.parse(raw.slice(offset, agentEndIndex), options, baseOffset + offset)); // Set the offset to the next agent or the end of the rule offset = separatorIndex + 1; } if (result.children.length === 0) { throw new AdblockSyntaxError('Empty agent list', baseOffset, baseOffset + raw.length); } return result; } } export { AgentCommentParser };