@adguard/agtree 2.1.2 → 2.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agtree.d.ts +4 -2
- package/dist/agtree.js +98 -16
- package/dist/agtree.mjs +98 -16
- package/package.json +1 -1
package/dist/agtree.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* AGTree v2.1.
|
|
2
|
+
* AGTree v2.1.4 (build date: Mon, 25 Nov 2024 16:56:07 GMT)
|
|
3
3
|
* (c) 2024 Adguard Software Ltd.
|
|
4
4
|
* Released under the MIT license
|
|
5
5
|
* https://github.com/AdguardTeam/tsurlfilter/tree/master/packages/agtree#readme
|
|
@@ -2357,9 +2357,11 @@ declare class FilterListParser extends ParserBase {
|
|
|
2357
2357
|
* @param ast AST to generate
|
|
2358
2358
|
* @param preferRaw If `true`, then the parser will use `raws.text` property of each rule
|
|
2359
2359
|
* if it is available. Default is `false`.
|
|
2360
|
+
* @param tolerant If `true`, errors during rule generation will be logged to the console and invalid rules
|
|
2361
|
+
* will be skipped. If `false`, an error will be thrown on the first invalid rule. Default is `true`.
|
|
2360
2362
|
* @returns Serialized filter list
|
|
2361
2363
|
*/
|
|
2362
|
-
static generate(ast: FilterList, preferRaw?: boolean): string;
|
|
2364
|
+
static generate(ast: FilterList, preferRaw?: boolean, tolerant?: boolean): string;
|
|
2363
2365
|
/**
|
|
2364
2366
|
* Serializes a filter list node to binary format.
|
|
2365
2367
|
*
|
package/dist/agtree.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* AGTree v2.1.
|
|
2
|
+
* AGTree v2.1.4 (build date: Mon, 25 Nov 2024 16:56:07 GMT)
|
|
3
3
|
* (c) 2024 Adguard Software Ltd.
|
|
4
4
|
* Released under the MIT license
|
|
5
5
|
* https://github.com/AdguardTeam/tsurlfilter/tree/master/packages/agtree#readme
|
|
@@ -343,10 +343,11 @@ class StringUtils {
|
|
|
343
343
|
* @param searchedCharacter - Searched character
|
|
344
344
|
* @param start - Start index
|
|
345
345
|
* @param escapeCharacter - Escape character, \ by default
|
|
346
|
+
* @param end - End index (excluded)
|
|
346
347
|
* @returns Index or -1 if the character not found
|
|
347
348
|
*/
|
|
348
|
-
static findNextUnescapedCharacter(pattern, searchedCharacter, start = 0, escapeCharacter = ESCAPE_CHARACTER) {
|
|
349
|
-
for (let i = start; i <
|
|
349
|
+
static findNextUnescapedCharacter(pattern, searchedCharacter, start = 0, escapeCharacter = ESCAPE_CHARACTER, end = pattern.length) {
|
|
350
|
+
for (let i = start; i < end; i += 1) {
|
|
350
351
|
// The searched character cannot be preceded by an escape
|
|
351
352
|
if (pattern[i] === searchedCharacter && pattern[i - 1] !== escapeCharacter) {
|
|
352
353
|
return i;
|
|
@@ -361,10 +362,11 @@ class StringUtils {
|
|
|
361
362
|
* @param searchedCharacter - Searched character
|
|
362
363
|
* @param start - Start index
|
|
363
364
|
* @param escapeCharacter - Escape character, \ by default
|
|
365
|
+
* @param end - End index (Included)
|
|
364
366
|
* @returns Index or -1 if the character not found
|
|
365
367
|
*/
|
|
366
|
-
static findNextUnescapedCharacterBackwards(pattern, searchedCharacter, start = pattern.length - 1, escapeCharacter = ESCAPE_CHARACTER) {
|
|
367
|
-
for (let i = start; i >=
|
|
368
|
+
static findNextUnescapedCharacterBackwards(pattern, searchedCharacter, start = pattern.length - 1, escapeCharacter = ESCAPE_CHARACTER, end = 0) {
|
|
369
|
+
for (let i = start; i >= end; i -= 1) {
|
|
368
370
|
// The searched character cannot be preceded by an escape
|
|
369
371
|
if (pattern[i] === searchedCharacter && pattern[i - 1] !== escapeCharacter) {
|
|
370
372
|
return i;
|
|
@@ -1911,6 +1913,9 @@ class AgentCommentRuleParser extends ParserBase {
|
|
|
1911
1913
|
static serialize(node, buffer) {
|
|
1912
1914
|
buffer.writeUint8(BinaryTypeMap.AgentRuleNode);
|
|
1913
1915
|
const count = node.children.length;
|
|
1916
|
+
// If there are no children, we do not write any data related to them, to avoid using unnecessary storage,
|
|
1917
|
+
// but children is a required field, so during deserialization we should initialize it as an empty array,
|
|
1918
|
+
// if there are no children in the binary data.
|
|
1914
1919
|
if (count) {
|
|
1915
1920
|
buffer.writeUint8(AgentRuleSerializationMap.Children);
|
|
1916
1921
|
// note: we store the count, because re-construction of the array is faster if we know the length
|
|
@@ -1965,6 +1970,11 @@ class AgentCommentRuleParser extends ParserBase {
|
|
|
1965
1970
|
}
|
|
1966
1971
|
prop = buffer.readUint8();
|
|
1967
1972
|
}
|
|
1973
|
+
// Maybe children are not present in the binary data,
|
|
1974
|
+
// in this case, we should initialize it as an empty array.
|
|
1975
|
+
if (!node.children) {
|
|
1976
|
+
node.children = [];
|
|
1977
|
+
}
|
|
1968
1978
|
}
|
|
1969
1979
|
}
|
|
1970
1980
|
|
|
@@ -2860,6 +2870,9 @@ class HintCommentRuleParser extends ParserBase {
|
|
|
2860
2870
|
buffer.writeUint8(SYNTAX_SERIALIZATION_MAP.get(exports.AdblockSyntax.Adg) ?? 0);
|
|
2861
2871
|
}
|
|
2862
2872
|
const count = node.children.length;
|
|
2873
|
+
// If there are no children, we do not write any data related to them, to avoid using unnecessary storage,
|
|
2874
|
+
// but children is a required field, so during deserialization we should initialize it as an empty array,
|
|
2875
|
+
// if there are no children in the binary data.
|
|
2863
2876
|
if (count) {
|
|
2864
2877
|
buffer.writeUint8(HintRuleSerializationMap.Children);
|
|
2865
2878
|
// note: we store the count, because re-construction of the array is faster if we know the length
|
|
@@ -2913,6 +2926,11 @@ class HintCommentRuleParser extends ParserBase {
|
|
|
2913
2926
|
}
|
|
2914
2927
|
prop = buffer.readUint8();
|
|
2915
2928
|
}
|
|
2929
|
+
// Maybe children are not present in the binary data,
|
|
2930
|
+
// in this case, we should initialize it as an empty array.
|
|
2931
|
+
if (!node.children) {
|
|
2932
|
+
node.children = [];
|
|
2933
|
+
}
|
|
2916
2934
|
}
|
|
2917
2935
|
}
|
|
2918
2936
|
|
|
@@ -5346,6 +5364,9 @@ class ModifierListParser extends ParserBase {
|
|
|
5346
5364
|
static serialize(node, buffer) {
|
|
5347
5365
|
buffer.writeUint8(BinaryTypeMap.ModifierListNode);
|
|
5348
5366
|
const count = node.children.length;
|
|
5367
|
+
// If there are no children, we do not write any data related to them, to avoid using unnecessary storage,
|
|
5368
|
+
// but children is a required field, so during deserialization we should initialize it as an empty array,
|
|
5369
|
+
// if there are no children in the binary data.
|
|
5349
5370
|
if (count) {
|
|
5350
5371
|
buffer.writeUint8(ModifierListNodeSerializationMap.Children);
|
|
5351
5372
|
// note: we store the count, because re-construction of the array is faster if we know the length
|
|
@@ -5397,6 +5418,11 @@ class ModifierListParser extends ParserBase {
|
|
|
5397
5418
|
}
|
|
5398
5419
|
prop = buffer.readUint8();
|
|
5399
5420
|
}
|
|
5421
|
+
// Maybe children are not present in the binary data,
|
|
5422
|
+
// in this case, we should initialize it as an empty array.
|
|
5423
|
+
if (!node.children) {
|
|
5424
|
+
node.children = [];
|
|
5425
|
+
}
|
|
5400
5426
|
}
|
|
5401
5427
|
}
|
|
5402
5428
|
|
|
@@ -7052,9 +7078,6 @@ class UboParameterListParser extends ParameterListParser {
|
|
|
7052
7078
|
// Next non-whitespace character after the closing quote should be the separator
|
|
7053
7079
|
const nextSeparatorIndex = StringUtils.skipWS(raw, possibleClosingQuoteIndex + 1);
|
|
7054
7080
|
if (nextSeparatorIndex === length) {
|
|
7055
|
-
if (requireQuotes) {
|
|
7056
|
-
throw new AdblockSyntaxError('Expected separator, got end of string', baseOffset + nextSeparatorIndex, baseOffset + length);
|
|
7057
|
-
}
|
|
7058
7081
|
// If the separator is not found, the param end is the end of the string
|
|
7059
7082
|
paramEnd = StringUtils.skipWSBack(raw, length - 1) + 1;
|
|
7060
7083
|
offset = length;
|
|
@@ -7068,9 +7091,41 @@ class UboParameterListParser extends ParameterListParser {
|
|
|
7068
7091
|
if (requireQuotes) {
|
|
7069
7092
|
throw new AdblockSyntaxError(`Expected separator, got: '${raw[nextSeparatorIndex]}'`, baseOffset + nextSeparatorIndex, baseOffset + length);
|
|
7070
7093
|
}
|
|
7071
|
-
|
|
7072
|
-
|
|
7073
|
-
|
|
7094
|
+
/**
|
|
7095
|
+
* At that point found `possibleClosingQuoteIndex` is wrong
|
|
7096
|
+
* | is `offset`
|
|
7097
|
+
* ~ is `possibleClosingQuoteIndex`
|
|
7098
|
+
* ^ is `nextSeparatorIndex`
|
|
7099
|
+
*
|
|
7100
|
+
* Example 1: "abc, ').cba='1'"
|
|
7101
|
+
* | ~^
|
|
7102
|
+
* Example 2: "abc, ').cba, '1'"
|
|
7103
|
+
* | ~^
|
|
7104
|
+
* Example 3: "abc, ').cba='1', cba"
|
|
7105
|
+
* | ~^
|
|
7106
|
+
*
|
|
7107
|
+
* Search for separator before `possibleClosingQuoteIndex`
|
|
7108
|
+
*/
|
|
7109
|
+
const separatorIndexBeforeQuote = StringUtils.findNextUnescapedCharacterBackwards(raw, separator, possibleClosingQuoteIndex, ESCAPE_CHARACTER, offset + 1);
|
|
7110
|
+
if (separatorIndexBeforeQuote !== -1) {
|
|
7111
|
+
// Found separator before (Example 2)
|
|
7112
|
+
paramEnd = StringUtils.skipWSBack(raw, separatorIndexBeforeQuote - 1) + 1;
|
|
7113
|
+
offset = separatorIndexBeforeQuote + 1;
|
|
7114
|
+
}
|
|
7115
|
+
else {
|
|
7116
|
+
// Didn't found separator before, search after
|
|
7117
|
+
const separatorIndexAfterQuote = StringUtils.findNextUnescapedCharacter(raw, separator, possibleClosingQuoteIndex);
|
|
7118
|
+
if (separatorIndexAfterQuote !== -1) {
|
|
7119
|
+
// We found separator after (Example 3)
|
|
7120
|
+
paramEnd = StringUtils.skipWSBack(raw, separatorIndexAfterQuote - 1) + 1;
|
|
7121
|
+
offset = separatorIndexAfterQuote + 1;
|
|
7122
|
+
}
|
|
7123
|
+
else {
|
|
7124
|
+
// If the separator is not found, the param end is the end of the string (Example 1)
|
|
7125
|
+
paramEnd = StringUtils.skipWSBack(raw, length - 1) + 1;
|
|
7126
|
+
offset = length;
|
|
7127
|
+
}
|
|
7128
|
+
}
|
|
7074
7129
|
}
|
|
7075
7130
|
}
|
|
7076
7131
|
else {
|
|
@@ -8580,10 +8635,16 @@ class NetworkRuleParser extends ParserBase {
|
|
|
8580
8635
|
const pattern = ValueParser.parse(raw.slice(patternStart, patternEnd), options, baseOffset + patternStart);
|
|
8581
8636
|
// Parse modifiers (if any)
|
|
8582
8637
|
let modifiers;
|
|
8638
|
+
// Get a last non-whitespace index
|
|
8639
|
+
const lastNonWsIndex = StringUtils.skipWSBack(raw);
|
|
8583
8640
|
// Find start and end index of the modifiers
|
|
8584
8641
|
const modifiersStart = separatorIndex + 1;
|
|
8585
|
-
const modifiersEnd =
|
|
8642
|
+
const modifiersEnd = lastNonWsIndex + 1;
|
|
8586
8643
|
if (separatorIndex !== -1) {
|
|
8644
|
+
// Check for empty modifiers
|
|
8645
|
+
if (separatorIndex === lastNonWsIndex) {
|
|
8646
|
+
throw new AdblockSyntaxError('Empty modifiers are not allowed', baseOffset + separatorIndex, baseOffset + raw.length);
|
|
8647
|
+
}
|
|
8587
8648
|
modifiers = ModifierListParser.parse(raw.slice(modifiersStart, modifiersEnd), options, baseOffset + modifiersStart);
|
|
8588
8649
|
}
|
|
8589
8650
|
// Throw error if there is no pattern and no modifiers
|
|
@@ -8902,6 +8963,9 @@ class HostRuleParser extends ParserBase {
|
|
|
8902
8963
|
buffer.writeUint32(node.end);
|
|
8903
8964
|
}
|
|
8904
8965
|
const count = node.children.length;
|
|
8966
|
+
// If there are no children, we do not write any data related to them, to avoid using unnecessary storage,
|
|
8967
|
+
// but children is a required field, so during deserialization we should initialize it as an empty array,
|
|
8968
|
+
// if there are no children in the binary data.
|
|
8905
8969
|
if (count) {
|
|
8906
8970
|
// note: we store the count, because re-construction of the array is faster if we know the length
|
|
8907
8971
|
if (count > UINT16_MAX) {
|
|
@@ -8945,6 +9009,11 @@ class HostRuleParser extends ParserBase {
|
|
|
8945
9009
|
}
|
|
8946
9010
|
prop = buffer.readUint8();
|
|
8947
9011
|
}
|
|
9012
|
+
// Maybe children are not present in the binary data,
|
|
9013
|
+
// in this case, we should initialize it as an empty array.
|
|
9014
|
+
if (!node.children) {
|
|
9015
|
+
node.children = [];
|
|
9016
|
+
}
|
|
8948
9017
|
}
|
|
8949
9018
|
/**
|
|
8950
9019
|
* Serializes a host rule node to binary format.
|
|
@@ -9712,9 +9781,11 @@ class FilterListParser extends ParserBase {
|
|
|
9712
9781
|
* @param ast AST to generate
|
|
9713
9782
|
* @param preferRaw If `true`, then the parser will use `raws.text` property of each rule
|
|
9714
9783
|
* if it is available. Default is `false`.
|
|
9784
|
+
* @param tolerant If `true`, errors during rule generation will be logged to the console and invalid rules
|
|
9785
|
+
* will be skipped. If `false`, an error will be thrown on the first invalid rule. Default is `true`.
|
|
9715
9786
|
* @returns Serialized filter list
|
|
9716
9787
|
*/
|
|
9717
|
-
static generate(ast, preferRaw = false) {
|
|
9788
|
+
static generate(ast, preferRaw = false, tolerant = true) {
|
|
9718
9789
|
let result = EMPTY;
|
|
9719
9790
|
for (let i = 0; i < ast.children.length; i += 1) {
|
|
9720
9791
|
const rule = ast.children[i];
|
|
@@ -9722,7 +9793,18 @@ class FilterListParser extends ParserBase {
|
|
|
9722
9793
|
result += rule.raws.text;
|
|
9723
9794
|
}
|
|
9724
9795
|
else {
|
|
9725
|
-
|
|
9796
|
+
try {
|
|
9797
|
+
result += RuleParser.generate(rule);
|
|
9798
|
+
}
|
|
9799
|
+
catch (error) {
|
|
9800
|
+
if (tolerant) {
|
|
9801
|
+
// eslint-disable-next-line no-console
|
|
9802
|
+
console.error(`Error when generating: ${error}`);
|
|
9803
|
+
}
|
|
9804
|
+
else {
|
|
9805
|
+
throw new Error(String(error));
|
|
9806
|
+
}
|
|
9807
|
+
}
|
|
9726
9808
|
}
|
|
9727
9809
|
switch (rule.raws?.nl) {
|
|
9728
9810
|
case 'crlf':
|
|
@@ -14669,7 +14751,7 @@ class RawFilterListConverter extends ConverterBase {
|
|
|
14669
14751
|
return createConversionResult(rawFilterList, false);
|
|
14670
14752
|
}
|
|
14671
14753
|
// Otherwise, serialize the filter list and return the result
|
|
14672
|
-
return createConversionResult(FilterListParser.generate(conversionResult.result), true);
|
|
14754
|
+
return createConversionResult(FilterListParser.generate(conversionResult.result, false, tolerant), true);
|
|
14673
14755
|
}
|
|
14674
14756
|
}
|
|
14675
14757
|
|
|
@@ -15617,7 +15699,7 @@ class RuleCategorizer {
|
|
|
15617
15699
|
}
|
|
15618
15700
|
}
|
|
15619
15701
|
|
|
15620
|
-
const version = "2.1.
|
|
15702
|
+
const version = "2.1.4";
|
|
15621
15703
|
|
|
15622
15704
|
/**
|
|
15623
15705
|
* @file AGTree version
|
package/dist/agtree.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* AGTree v2.1.
|
|
2
|
+
* AGTree v2.1.4 (build date: Mon, 25 Nov 2024 16:56:07 GMT)
|
|
3
3
|
* (c) 2024 Adguard Software Ltd.
|
|
4
4
|
* Released under the MIT license
|
|
5
5
|
* https://github.com/AdguardTeam/tsurlfilter/tree/master/packages/agtree#readme
|
|
@@ -323,10 +323,11 @@ class StringUtils {
|
|
|
323
323
|
* @param searchedCharacter - Searched character
|
|
324
324
|
* @param start - Start index
|
|
325
325
|
* @param escapeCharacter - Escape character, \ by default
|
|
326
|
+
* @param end - End index (excluded)
|
|
326
327
|
* @returns Index or -1 if the character not found
|
|
327
328
|
*/
|
|
328
|
-
static findNextUnescapedCharacter(pattern, searchedCharacter, start = 0, escapeCharacter = ESCAPE_CHARACTER) {
|
|
329
|
-
for (let i = start; i <
|
|
329
|
+
static findNextUnescapedCharacter(pattern, searchedCharacter, start = 0, escapeCharacter = ESCAPE_CHARACTER, end = pattern.length) {
|
|
330
|
+
for (let i = start; i < end; i += 1) {
|
|
330
331
|
// The searched character cannot be preceded by an escape
|
|
331
332
|
if (pattern[i] === searchedCharacter && pattern[i - 1] !== escapeCharacter) {
|
|
332
333
|
return i;
|
|
@@ -341,10 +342,11 @@ class StringUtils {
|
|
|
341
342
|
* @param searchedCharacter - Searched character
|
|
342
343
|
* @param start - Start index
|
|
343
344
|
* @param escapeCharacter - Escape character, \ by default
|
|
345
|
+
* @param end - End index (Included)
|
|
344
346
|
* @returns Index or -1 if the character not found
|
|
345
347
|
*/
|
|
346
|
-
static findNextUnescapedCharacterBackwards(pattern, searchedCharacter, start = pattern.length - 1, escapeCharacter = ESCAPE_CHARACTER) {
|
|
347
|
-
for (let i = start; i >=
|
|
348
|
+
static findNextUnescapedCharacterBackwards(pattern, searchedCharacter, start = pattern.length - 1, escapeCharacter = ESCAPE_CHARACTER, end = 0) {
|
|
349
|
+
for (let i = start; i >= end; i -= 1) {
|
|
348
350
|
// The searched character cannot be preceded by an escape
|
|
349
351
|
if (pattern[i] === searchedCharacter && pattern[i - 1] !== escapeCharacter) {
|
|
350
352
|
return i;
|
|
@@ -1891,6 +1893,9 @@ class AgentCommentRuleParser extends ParserBase {
|
|
|
1891
1893
|
static serialize(node, buffer) {
|
|
1892
1894
|
buffer.writeUint8(BinaryTypeMap.AgentRuleNode);
|
|
1893
1895
|
const count = node.children.length;
|
|
1896
|
+
// If there are no children, we do not write any data related to them, to avoid using unnecessary storage,
|
|
1897
|
+
// but children is a required field, so during deserialization we should initialize it as an empty array,
|
|
1898
|
+
// if there are no children in the binary data.
|
|
1894
1899
|
if (count) {
|
|
1895
1900
|
buffer.writeUint8(AgentRuleSerializationMap.Children);
|
|
1896
1901
|
// note: we store the count, because re-construction of the array is faster if we know the length
|
|
@@ -1945,6 +1950,11 @@ class AgentCommentRuleParser extends ParserBase {
|
|
|
1945
1950
|
}
|
|
1946
1951
|
prop = buffer.readUint8();
|
|
1947
1952
|
}
|
|
1953
|
+
// Maybe children are not present in the binary data,
|
|
1954
|
+
// in this case, we should initialize it as an empty array.
|
|
1955
|
+
if (!node.children) {
|
|
1956
|
+
node.children = [];
|
|
1957
|
+
}
|
|
1948
1958
|
}
|
|
1949
1959
|
}
|
|
1950
1960
|
|
|
@@ -2840,6 +2850,9 @@ class HintCommentRuleParser extends ParserBase {
|
|
|
2840
2850
|
buffer.writeUint8(SYNTAX_SERIALIZATION_MAP.get(AdblockSyntax.Adg) ?? 0);
|
|
2841
2851
|
}
|
|
2842
2852
|
const count = node.children.length;
|
|
2853
|
+
// If there are no children, we do not write any data related to them, to avoid using unnecessary storage,
|
|
2854
|
+
// but children is a required field, so during deserialization we should initialize it as an empty array,
|
|
2855
|
+
// if there are no children in the binary data.
|
|
2843
2856
|
if (count) {
|
|
2844
2857
|
buffer.writeUint8(HintRuleSerializationMap.Children);
|
|
2845
2858
|
// note: we store the count, because re-construction of the array is faster if we know the length
|
|
@@ -2893,6 +2906,11 @@ class HintCommentRuleParser extends ParserBase {
|
|
|
2893
2906
|
}
|
|
2894
2907
|
prop = buffer.readUint8();
|
|
2895
2908
|
}
|
|
2909
|
+
// Maybe children are not present in the binary data,
|
|
2910
|
+
// in this case, we should initialize it as an empty array.
|
|
2911
|
+
if (!node.children) {
|
|
2912
|
+
node.children = [];
|
|
2913
|
+
}
|
|
2896
2914
|
}
|
|
2897
2915
|
}
|
|
2898
2916
|
|
|
@@ -5326,6 +5344,9 @@ class ModifierListParser extends ParserBase {
|
|
|
5326
5344
|
static serialize(node, buffer) {
|
|
5327
5345
|
buffer.writeUint8(BinaryTypeMap.ModifierListNode);
|
|
5328
5346
|
const count = node.children.length;
|
|
5347
|
+
// If there are no children, we do not write any data related to them, to avoid using unnecessary storage,
|
|
5348
|
+
// but children is a required field, so during deserialization we should initialize it as an empty array,
|
|
5349
|
+
// if there are no children in the binary data.
|
|
5329
5350
|
if (count) {
|
|
5330
5351
|
buffer.writeUint8(ModifierListNodeSerializationMap.Children);
|
|
5331
5352
|
// note: we store the count, because re-construction of the array is faster if we know the length
|
|
@@ -5377,6 +5398,11 @@ class ModifierListParser extends ParserBase {
|
|
|
5377
5398
|
}
|
|
5378
5399
|
prop = buffer.readUint8();
|
|
5379
5400
|
}
|
|
5401
|
+
// Maybe children are not present in the binary data,
|
|
5402
|
+
// in this case, we should initialize it as an empty array.
|
|
5403
|
+
if (!node.children) {
|
|
5404
|
+
node.children = [];
|
|
5405
|
+
}
|
|
5380
5406
|
}
|
|
5381
5407
|
}
|
|
5382
5408
|
|
|
@@ -7032,9 +7058,6 @@ class UboParameterListParser extends ParameterListParser {
|
|
|
7032
7058
|
// Next non-whitespace character after the closing quote should be the separator
|
|
7033
7059
|
const nextSeparatorIndex = StringUtils.skipWS(raw, possibleClosingQuoteIndex + 1);
|
|
7034
7060
|
if (nextSeparatorIndex === length) {
|
|
7035
|
-
if (requireQuotes) {
|
|
7036
|
-
throw new AdblockSyntaxError('Expected separator, got end of string', baseOffset + nextSeparatorIndex, baseOffset + length);
|
|
7037
|
-
}
|
|
7038
7061
|
// If the separator is not found, the param end is the end of the string
|
|
7039
7062
|
paramEnd = StringUtils.skipWSBack(raw, length - 1) + 1;
|
|
7040
7063
|
offset = length;
|
|
@@ -7048,9 +7071,41 @@ class UboParameterListParser extends ParameterListParser {
|
|
|
7048
7071
|
if (requireQuotes) {
|
|
7049
7072
|
throw new AdblockSyntaxError(`Expected separator, got: '${raw[nextSeparatorIndex]}'`, baseOffset + nextSeparatorIndex, baseOffset + length);
|
|
7050
7073
|
}
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
|
|
7074
|
+
/**
|
|
7075
|
+
* At that point found `possibleClosingQuoteIndex` is wrong
|
|
7076
|
+
* | is `offset`
|
|
7077
|
+
* ~ is `possibleClosingQuoteIndex`
|
|
7078
|
+
* ^ is `nextSeparatorIndex`
|
|
7079
|
+
*
|
|
7080
|
+
* Example 1: "abc, ').cba='1'"
|
|
7081
|
+
* | ~^
|
|
7082
|
+
* Example 2: "abc, ').cba, '1'"
|
|
7083
|
+
* | ~^
|
|
7084
|
+
* Example 3: "abc, ').cba='1', cba"
|
|
7085
|
+
* | ~^
|
|
7086
|
+
*
|
|
7087
|
+
* Search for separator before `possibleClosingQuoteIndex`
|
|
7088
|
+
*/
|
|
7089
|
+
const separatorIndexBeforeQuote = StringUtils.findNextUnescapedCharacterBackwards(raw, separator, possibleClosingQuoteIndex, ESCAPE_CHARACTER, offset + 1);
|
|
7090
|
+
if (separatorIndexBeforeQuote !== -1) {
|
|
7091
|
+
// Found separator before (Example 2)
|
|
7092
|
+
paramEnd = StringUtils.skipWSBack(raw, separatorIndexBeforeQuote - 1) + 1;
|
|
7093
|
+
offset = separatorIndexBeforeQuote + 1;
|
|
7094
|
+
}
|
|
7095
|
+
else {
|
|
7096
|
+
// Didn't found separator before, search after
|
|
7097
|
+
const separatorIndexAfterQuote = StringUtils.findNextUnescapedCharacter(raw, separator, possibleClosingQuoteIndex);
|
|
7098
|
+
if (separatorIndexAfterQuote !== -1) {
|
|
7099
|
+
// We found separator after (Example 3)
|
|
7100
|
+
paramEnd = StringUtils.skipWSBack(raw, separatorIndexAfterQuote - 1) + 1;
|
|
7101
|
+
offset = separatorIndexAfterQuote + 1;
|
|
7102
|
+
}
|
|
7103
|
+
else {
|
|
7104
|
+
// If the separator is not found, the param end is the end of the string (Example 1)
|
|
7105
|
+
paramEnd = StringUtils.skipWSBack(raw, length - 1) + 1;
|
|
7106
|
+
offset = length;
|
|
7107
|
+
}
|
|
7108
|
+
}
|
|
7054
7109
|
}
|
|
7055
7110
|
}
|
|
7056
7111
|
else {
|
|
@@ -8560,10 +8615,16 @@ class NetworkRuleParser extends ParserBase {
|
|
|
8560
8615
|
const pattern = ValueParser.parse(raw.slice(patternStart, patternEnd), options, baseOffset + patternStart);
|
|
8561
8616
|
// Parse modifiers (if any)
|
|
8562
8617
|
let modifiers;
|
|
8618
|
+
// Get a last non-whitespace index
|
|
8619
|
+
const lastNonWsIndex = StringUtils.skipWSBack(raw);
|
|
8563
8620
|
// Find start and end index of the modifiers
|
|
8564
8621
|
const modifiersStart = separatorIndex + 1;
|
|
8565
|
-
const modifiersEnd =
|
|
8622
|
+
const modifiersEnd = lastNonWsIndex + 1;
|
|
8566
8623
|
if (separatorIndex !== -1) {
|
|
8624
|
+
// Check for empty modifiers
|
|
8625
|
+
if (separatorIndex === lastNonWsIndex) {
|
|
8626
|
+
throw new AdblockSyntaxError('Empty modifiers are not allowed', baseOffset + separatorIndex, baseOffset + raw.length);
|
|
8627
|
+
}
|
|
8567
8628
|
modifiers = ModifierListParser.parse(raw.slice(modifiersStart, modifiersEnd), options, baseOffset + modifiersStart);
|
|
8568
8629
|
}
|
|
8569
8630
|
// Throw error if there is no pattern and no modifiers
|
|
@@ -8882,6 +8943,9 @@ class HostRuleParser extends ParserBase {
|
|
|
8882
8943
|
buffer.writeUint32(node.end);
|
|
8883
8944
|
}
|
|
8884
8945
|
const count = node.children.length;
|
|
8946
|
+
// If there are no children, we do not write any data related to them, to avoid using unnecessary storage,
|
|
8947
|
+
// but children is a required field, so during deserialization we should initialize it as an empty array,
|
|
8948
|
+
// if there are no children in the binary data.
|
|
8885
8949
|
if (count) {
|
|
8886
8950
|
// note: we store the count, because re-construction of the array is faster if we know the length
|
|
8887
8951
|
if (count > UINT16_MAX) {
|
|
@@ -8925,6 +8989,11 @@ class HostRuleParser extends ParserBase {
|
|
|
8925
8989
|
}
|
|
8926
8990
|
prop = buffer.readUint8();
|
|
8927
8991
|
}
|
|
8992
|
+
// Maybe children are not present in the binary data,
|
|
8993
|
+
// in this case, we should initialize it as an empty array.
|
|
8994
|
+
if (!node.children) {
|
|
8995
|
+
node.children = [];
|
|
8996
|
+
}
|
|
8928
8997
|
}
|
|
8929
8998
|
/**
|
|
8930
8999
|
* Serializes a host rule node to binary format.
|
|
@@ -9692,9 +9761,11 @@ class FilterListParser extends ParserBase {
|
|
|
9692
9761
|
* @param ast AST to generate
|
|
9693
9762
|
* @param preferRaw If `true`, then the parser will use `raws.text` property of each rule
|
|
9694
9763
|
* if it is available. Default is `false`.
|
|
9764
|
+
* @param tolerant If `true`, errors during rule generation will be logged to the console and invalid rules
|
|
9765
|
+
* will be skipped. If `false`, an error will be thrown on the first invalid rule. Default is `true`.
|
|
9695
9766
|
* @returns Serialized filter list
|
|
9696
9767
|
*/
|
|
9697
|
-
static generate(ast, preferRaw = false) {
|
|
9768
|
+
static generate(ast, preferRaw = false, tolerant = true) {
|
|
9698
9769
|
let result = EMPTY;
|
|
9699
9770
|
for (let i = 0; i < ast.children.length; i += 1) {
|
|
9700
9771
|
const rule = ast.children[i];
|
|
@@ -9702,7 +9773,18 @@ class FilterListParser extends ParserBase {
|
|
|
9702
9773
|
result += rule.raws.text;
|
|
9703
9774
|
}
|
|
9704
9775
|
else {
|
|
9705
|
-
|
|
9776
|
+
try {
|
|
9777
|
+
result += RuleParser.generate(rule);
|
|
9778
|
+
}
|
|
9779
|
+
catch (error) {
|
|
9780
|
+
if (tolerant) {
|
|
9781
|
+
// eslint-disable-next-line no-console
|
|
9782
|
+
console.error(`Error when generating: ${error}`);
|
|
9783
|
+
}
|
|
9784
|
+
else {
|
|
9785
|
+
throw new Error(String(error));
|
|
9786
|
+
}
|
|
9787
|
+
}
|
|
9706
9788
|
}
|
|
9707
9789
|
switch (rule.raws?.nl) {
|
|
9708
9790
|
case 'crlf':
|
|
@@ -14649,7 +14731,7 @@ class RawFilterListConverter extends ConverterBase {
|
|
|
14649
14731
|
return createConversionResult(rawFilterList, false);
|
|
14650
14732
|
}
|
|
14651
14733
|
// Otherwise, serialize the filter list and return the result
|
|
14652
|
-
return createConversionResult(FilterListParser.generate(conversionResult.result), true);
|
|
14734
|
+
return createConversionResult(FilterListParser.generate(conversionResult.result, false, tolerant), true);
|
|
14653
14735
|
}
|
|
14654
14736
|
}
|
|
14655
14737
|
|
|
@@ -15597,7 +15679,7 @@ class RuleCategorizer {
|
|
|
15597
15679
|
}
|
|
15598
15680
|
}
|
|
15599
15681
|
|
|
15600
|
-
const version = "2.1.
|
|
15682
|
+
const version = "2.1.4";
|
|
15601
15683
|
|
|
15602
15684
|
/**
|
|
15603
15685
|
* @file AGTree version
|