@adguard/agtree
Version:
Tool set for working with adblock filter lists
89 lines (86 loc) • 4.14 kB
JavaScript
/*
* AGTree v3.4.3 (build date: Thu, 11 Dec 2025 13:43:19 GMT)
* (c) 2025 Adguard Software Ltd.
* Released under the MIT license
* https://github.com/AdguardTeam/tsurlfilter/tree/master/packages/agtree#readme
*/
import { RuleConverter } from './rule.js';
import { clone } from '../utils/clone.js';
import { MultiValueMap } from '../utils/multi-value-map.js';
import { createConversionResult } from './base-interfaces/conversion-result.js';
import { BaseConverter } from './base-interfaces/base-converter.js';
/**
* @file Adblock filter list converter
*/
/**
* Adblock filter list converter class
*
* This class just provides an extra layer on top of the {@link RuleConverter}
* and can be used to convert entire filter lists.
*
* @todo Implement `convertToUbo` and `convertToAbp`
* @todo Implement tolerant mode, which will allow to convert a filter list
* even if some of its rules are invalid
*/
class FilterListConverter extends BaseConverter {
/**
* Converts an adblock filter list to AdGuard format, if possible.
*
* @param filterListNode Filter list node to convert
* @param tolerant Indicates whether the converter should be tolerant to invalid rules. If enabled and a rule is
* invalid, it will be left as is. If disabled and a rule is invalid, the whole filter list will be failed.
* Defaults to `true`.
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
* If the node was not converted, the result will contain the original node with the same object reference
* @throws If the filter list is invalid or cannot be converted (if the tolerant mode is disabled)
*/
static convertToAdg(filterListNode, tolerant = true) {
// Prepare a map to store the converted rules by their index in the filter list
const conversionMap = new MultiValueMap();
// Iterate over the filtering rules and convert them one by one, then add them to the result (one conversion may
// result in multiple rules)
for (let i = 0; i < filterListNode.children.length; i += 1) {
try {
const convertedRules = RuleConverter.convertToAdg(filterListNode.children[i]);
// Add the converted rules to the map if they were converted
if (convertedRules.isConverted) {
conversionMap.add(i, ...convertedRules.result);
}
}
catch (error) {
// If the tolerant mode is disabled, we should throw an error, this will fail the whole filter list
// conversion.
// Otherwise, we just ignore the error and leave the rule as is
if (!tolerant) {
throw error;
}
}
}
// If the conversion map is empty, it means that no rules were converted, so we can return the original filter
// list
if (conversionMap.size === 0) {
return createConversionResult(filterListNode, false);
}
// Otherwise, create a new filter list node with the converted rules
const convertedFilterList = {
type: 'FilterList',
children: [],
};
// Iterate over the original rules again and add them to the converted filter list, replacing the converted
// rules with the new ones at the specified indexes
for (let i = 0; i < filterListNode.children.length; i += 1) {
const rules = conversionMap.get(i);
if (rules) {
convertedFilterList.children.push(...rules);
}
else {
// We clone the unconverted rules to avoid mutating the original filter list if we return the converted
// one
convertedFilterList.children.push(clone(filterListNode.children[i]));
}
}
return createConversionResult(convertedFilterList, true);
}
}
export { FilterListConverter };