UNPKG

@adguard/agtree

Version:
90 lines (87 loc) 3.89 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 { NEGATION_MARKER, MODIFIER_ASSIGN_OPERATOR } from '../../utils/constants.js'; import { StringUtils } from '../../utils/string.js'; import { AdblockSyntaxError } from '../../errors/adblock-syntax-error.js'; import { defaultParserOptions } from '../options.js'; import { BaseParser } from '../base-parser.js'; import { ValueParser } from './value-parser.js'; /** * `ModifierParser` is responsible for parsing modifiers. * * @example * `match-case`, `~third-party`, `domain=example.com|~example.org` */ class ModifierParser extends BaseParser { /** * Parses a modifier. * * @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 Parsed modifier * @throws An error if modifier name or value is empty. */ static parse(raw, options = defaultParserOptions, baseOffset = 0) { let offset = 0; // Skip leading whitespace offset = StringUtils.skipWS(raw, offset); // Save the offset of the first character of the modifier (whole modifier) const modifierStart = offset; // Check if the modifier is an exception let exception = false; if (raw[offset] === NEGATION_MARKER) { offset += NEGATION_MARKER.length; exception = true; } // Skip whitespace after the exception marker (if any) offset = StringUtils.skipWS(raw, offset); // Save the offset of the first character of the modifier name const modifierNameStart = offset; // Find assignment operator const assignmentIndex = StringUtils.findNextUnescapedCharacter(raw, MODIFIER_ASSIGN_OPERATOR); // Find the end of the modifier const modifierEnd = Math.max(StringUtils.skipWSBack(raw) + 1, modifierNameStart); // Modifier name can't be empty if (modifierNameStart === modifierEnd) { throw new AdblockSyntaxError('Modifier name cannot be empty', baseOffset, baseOffset + raw.length); } let modifier; let value; // If there is no assignment operator, the whole modifier is the name // without a value if (assignmentIndex === -1) { modifier = ValueParser.parse(raw.slice(modifierNameStart, modifierEnd), options, baseOffset + modifierNameStart); } else { // If there is an assignment operator, first we need to find the // end of the modifier name, then we can parse the value const modifierNameEnd = StringUtils.skipWSBack(raw, assignmentIndex - 1) + 1; modifier = ValueParser.parse(raw.slice(modifierNameStart, modifierNameEnd), options, baseOffset + modifierNameStart); // Value can't be empty if (assignmentIndex + 1 === modifierEnd) { throw new AdblockSyntaxError('Modifier value cannot be empty', baseOffset, baseOffset + raw.length); } // Skip whitespace after the assignment operator const valueStart = StringUtils.skipWS(raw, assignmentIndex + MODIFIER_ASSIGN_OPERATOR.length); value = ValueParser.parse(raw.slice(valueStart, modifierEnd), options, baseOffset + valueStart); } const result = { type: 'Modifier', name: modifier, value, exception, }; if (options.isLocIncluded) { result.start = baseOffset + modifierStart; result.end = baseOffset + modifierEnd; } return result; } } export { ModifierParser };