UNPKG

@adguard/agtree

Version:
107 lines (104 loc) 4.17 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 { AdblockSyntax } from '../../utils/adblockers.js'; import { CosmeticRuleSeparatorUtils } from '../../utils/cosmetic-rule-separator.js'; import { StringUtils } from '../../utils/string.js'; import { CommentMarker, CommentRuleType, RuleCategory } from '../../nodes/index.js'; import { BaseParser } from '../base-parser.js'; import { ValueParser } from '../misc/value-parser.js'; import { defaultParserOptions } from '../options.js'; /** * `SimpleCommentParser` is responsible for parsing simple comments. * Some comments have a special meaning in adblock syntax, like agent comments or hints, * but this parser is only responsible for parsing regular comments, * whose only purpose is to provide some human-readable information. * * @example * ```adblock * ! This is a simple comment * # This is a simple comment, but in host-like syntax * ``` */ class SimpleCommentParser extends BaseParser { /** * Checks if the raw rule is a simple comment. * * @param raw Raw input to check. * @returns `true` if the input is a simple comment, `false` otherwise. * @note This method does not check for adblock agent comments. */ static isSimpleComment(raw) { const trimmed = raw.trim(); // Exclamation mark based comments if (trimmed.startsWith(CommentMarker.Regular)) { return true; } // Hashmark based comments // Note: in this case, we must be sure that we do not mistakenly parse a cosmetic rule as a #-like comment, // since most cosmetic rule separators also start with # if (trimmed.startsWith(CommentMarker.Hashmark)) { const result = CosmeticRuleSeparatorUtils.find(trimmed); // If we cannot find a separator, it means that the rule is definitely a comment if (result === null) { return true; } // Otherwise, we must check if the separator is followed by a valid selector const { end } = result; // No valid selector if (!trimmed[end + 1] || StringUtils.isWhitespace(trimmed[end + 1]) || (trimmed[end + 1] === CommentMarker.Hashmark && trimmed[end + 2] === CommentMarker.Hashmark)) { return true; } } return false; } /** * Parses a raw rule as a simple 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 Comment rule node or null (if the raw rule cannot be parsed as a simple comment). */ static parse(raw, options = defaultParserOptions, baseOffset = 0) { // Ignore non-comment rules if (!this.isSimpleComment(raw)) { return null; } // If we are here, it means that the rule is a regular comment let offset = 0; // Skip leading whitespace (if any) offset = StringUtils.skipWS(raw, offset); // Get comment marker const marker = ValueParser.parse(raw[offset], options, baseOffset + offset); // Skip marker offset += 1; // Get comment text const text = ValueParser.parse(raw.slice(offset), options, baseOffset + offset); // Regular comment rule const result = { category: RuleCategory.Comment, type: CommentRuleType.CommentRule, // TODO: Change syntax when hashmark is used syntax: AdblockSyntax.Common, marker, text, }; if (options.includeRaws) { result.raws = { text: raw, }; } if (options.isLocIncluded) { result.start = baseOffset; result.end = baseOffset + raw.length; } return result; } } export { SimpleCommentParser };