UNPKG

@adguard/agtree

Version:
68 lines (65 loc) 3.08 kB
/* * AGTree v4.1.1 (build date: Thu, 23 Apr 2026 09:15:37 GMT) * (c) 2026 Adguard Software Ltd. * Released under the MIT license * https://github.com/AdguardTeam/tsurlfilter/tree/master/packages/agtree#readme */ import { sprintf } from 'sprintf-js'; import { getFormattedTokenName } from '@adguard/css-tokenizer'; import { AdblockSyntaxError } from '../../../../errors/adblock-syntax-error.js'; import { CompoundSelectorHandler } from './compound-selector-handler.js'; /** * Handles complex selector parsing in selector list. */ class ComplexSelectorHandler { /** * Finishes the current complex selector by: * 1. Finishing current compound selector node via {@link CompoundSelectorHandler}, * 2. Validating current complex selector node, * 3. Appending current complex selector node to the selector list node, * If `isEof` is `false`: * 4. Constructing next complex selector node. * * @param context Selector list parser context. * @param isEof Indicates whether the end of the file has been reached. * * @throws If the current compound / complex selector has no simple selectors / compound selectors. */ static handle(context, isEof = true) { const { raw, options, baseOffset, stream, token, result, complexSelector, } = context; // Get current complex selector end token const currentEndToken = stream.lookbehindForNonWs(); // Finish current compound selector node CompoundSelectorHandler.handle(context); // Throw error if current complex selector node has no compound selector nodes (empty) if (!currentEndToken || complexSelector.children.length === 0) { throw new AdblockSyntaxError(sprintf("Unexpected token '%s' with value '%s'", getFormattedTokenName(token.type), raw.slice(token.start, token.end)), baseOffset + token.start, baseOffset + token.end); } // Include current complex selector node end location if needed if (options.isLocIncluded) { complexSelector.end = baseOffset + currentEndToken.end; } // Append current complex selector node to selector list node result.children.push(complexSelector); // If EOF is reached, just return, as we don't need to construct a next complex selector node if (isEof) { return; } // Get next complex selector node start token const nextStartToken = stream.getOrFail(); // Construct next complex selector node const nextComplexSelector = { type: 'ComplexSelector', children: [], }; // Include next complex selector node start location if needed if (options.isLocIncluded) { nextComplexSelector.start = baseOffset + nextStartToken.start; } // Update context with new complex selector context.complexSelector = nextComplexSelector; // Reset type selector set tracker for new selector context.isTypeSelectorSet = false; } } export { ComplexSelectorHandler };