@adguard/agtree
Version:
Tool set for working with adblock filter lists
68 lines (65 loc) • 3.08 kB
JavaScript
/*
* 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 };