Skip to content

Commit

Permalink
AGTree converter README, improve JSDoc
Browse files Browse the repository at this point in the history
Merge in ADGUARD-FILTERS/tsurlfilter from feature/agtree-converter-improvements to master

Squashed commit of the following:

commit ee8e98a
Author: scripthunter7 <[email protected]>
Date:   Thu Aug 10 10:16:19 2023 +0200

    Improve README

commit 0c98baf
Author: scripthunter7 <[email protected]>
Date:   Thu Aug 10 09:42:38 2023 +0200

    Remove redundant import

commit 0958908
Merge: f373b7d 02e795e
Author: scripthunter7 <[email protected]>
Date:   Thu Aug 10 09:40:36 2023 +0200

    Merge branch 'master' into feature/agtree-converter-improvements

commit f373b7d
Author: scripthunter7 <[email protected]>
Date:   Wed Aug 9 15:40:45 2023 +0200

    Fix nit, improve example

commit f9e5a1c
Author: scripthunter7 <[email protected]>
Date:   Wed Aug 9 14:53:00 2023 +0200

    Consistent JSDoc, fix typos

commit 6202bbb
Author: scripthunter7 <[email protected]>
Date:   Wed Aug 9 14:49:53 2023 +0200

    Add README

commit 9b46807
Author: scripthunter7 <[email protected]>
Date:   Wed Aug 9 14:49:18 2023 +0200

    Add TODO list
  • Loading branch information
scripthunter7 committed Aug 10, 2023
1 parent 02e795e commit e91c717
Show file tree
Hide file tree
Showing 21 changed files with 260 additions and 91 deletions.
4 changes: 2 additions & 2 deletions packages/agtree/src/ast-utils/filter-list.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
/**
* @file Utility functions for working with filter list ASTs
* @file Utility functions for working with filter list nodes
*/

import cloneDeep from 'clone-deep';

import { type AnyRule, type FilterList } from '../parser/common';

/**
* Create a filter list node
* Creates a filter list node
*
* @param rules Rules to put in the list (optional, defaults to an empty list)
* @returns Filter list node
Expand Down
6 changes: 3 additions & 3 deletions packages/agtree/src/ast-utils/modifiers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* @file Utility functions for working with modifier ASTs
* @file Utility functions for working with modifier nodes
*/

import cloneDeep from 'clone-deep';
Expand All @@ -8,7 +8,7 @@ import { type Modifier, type ModifierList } from '../parser/common';
import { isUndefined } from '../utils/common';

/**
* Create a modifier node
* Creates a modifier node
*
* @param name Name of the modifier
* @param value Value of the modifier
Expand Down Expand Up @@ -36,7 +36,7 @@ export function createModifierNode(name: string, value: string | undefined = und
}

/**
* Create a modifier list node
* Creates a modifier list node
*
* @param modifiers Modifiers to put in the list (optional, defaults to an empty list)
* @returns Modifier list node
Expand Down
2 changes: 1 addition & 1 deletion packages/agtree/src/ast-utils/network-rules.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* @file Utility functions for working with network rule ASTs
* @file Utility functions for working with network rule nodes
*/

import cloneDeep from 'clone-deep';
Expand Down
2 changes: 1 addition & 1 deletion packages/agtree/src/ast-utils/scriptlets.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* @file Utility functions for working with scriptlets ASTs
* @file Utility functions for working with scriptlet nodes
*/

import cloneDeep from 'clone-deep';
Expand Down
160 changes: 160 additions & 0 deletions packages/agtree/src/converter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# Adblock rule converter

This directory contains adblock rule converter that can be used to convert
rules from one adblocker format to another. It is used by AdGuard to convert
rules to AdGuard format, but in the future we plan to extend it to support other
formats.

>
> :warning: **This converter is still in development, currently it only supports
> converting Adblock Plus and uBlock Origin rules to AdGuard format.**
> Later we will plan to add support for other formats.
>
Table of contents:

- [Adblock rule converter](#adblock-rule-converter)
- [Converter API](#converter-api)
- [Rule converter signature](#rule-converter-signature)
- [Filter list converter signature](#filter-list-converter-signature)
- [Examples](#examples)
- [Examples of converting a single rule](#examples-of-converting-a-single-rule)
- [Examples of converting a filter list](#examples-of-converting-a-filter-list)
- [Limitations](#limitations)

## Converter API

The converter API is available in the `@adguard/agtree` package:

```ts
import { RuleConverter, FilterListConverter } from '@adguard/agtree';
```

The idea is quite simple, we provide two converter classes:

- `RuleConverter`: converts a single adblock filtering rule
- `FilterListConverter`: converts a complete adblock filter list (this is just a
wrapper around `RuleConverter`)

Converter classes have the following methods:

- `convertToAdg`: converts to AdGuard format
- `convertToAbp`: converts to Adblock Plus format *(not implemented yet)*
- `convertToUbo`: converts to uBlock Origin format *(not implemented yet)*

Each converter method expects an AST (Abstract Syntax Tree) node as an input
and returns the converted AST node(s) as an output. **This means that you should
parse the rule/filter list first**, then pass the AST node to the converter,
which will also return AST node(s). If necessary, you can serialize the
converted AST node(s) back to a string afterwards.

### Rule converter signature

Rule converter has the following signature:

```ts
RuleConverter.convertToAdg(rule: AnyRule): AnyRule[];
```

The reason why the converter returns an array of nodes is that sometimes a
single rule can be converted to multiple rules in another adblocker format. For
example, the following Adblock Plus rule:

```adblock
example.com#$#abp-snippet0 arg00 arg01; abp-snippet1 arg10 arg11
```

will be converted to the following AdGuard rules:

```adblock
example.com#%#//scriptlet('abp-snippet0', 'arg00', 'arg01')
example.com#%#//scriptlet('abp-snippet1', 'arg10', 'arg11')
```

So the general concept is that the rule converter always returns an array of
nodes, even if the array contains only one node.

### Filter list converter signature

Filter list converter has the following signature:

```ts
FilterListConverter.convertToAdg(filterList: FilterList): FilterList;
```

Filter list converter returns a single filter list node, not an array of nodes,
because it doesn't make sense to convert a filter list to multiple filter lists,
simply the converted filter list may contains a few more rules than the original
one.

## Examples

In this section we will show some examples of using the converter API.

### Examples of converting a single rule

```ts
import { RuleParser, RuleConverter } from '@adguard/agtree';

const rawRuleToConvert = 'example.com#$#abp-snippet0 arg00 arg01; abp-snippet1 arg10 arg11';

// Parse the rule to get an AST rule node.
// Please note that the parser will throw an error if the rule is
// syntactically incorrect.
const ruleNode = RuleParser.parse(rawRuleToConvert);

// Now you can use the converter API by passing the AST node as an input.
// Please note that the converter API returns an array of rule nodes,
// not a single rule node.
// Please also note that the converter API will throw an error if the
// rule is invalid or cannot be converted.
const conversionResult = RuleConverter.convertToAdg(ruleNode);

// You can simply serialize the rule nodes, then print them to the console
// this way:
console.log(conversionResult.map(RuleParser.generate).join('\n'));
```

### Examples of converting a filter list

```ts
import { FilterListParser, FilterListConverter } from '@adguard/agtree';
import { readFileSync, writeFileSync } from 'fs';

const filterListToConvert = `[Adblock Plus 3.1]
! Title: Example filter list
! Description: This is an example filter list
! Expires: 1 day
! Homepage: https://example.com
! Version: 1.0
! License: MIT
example.com#$#abp-snippet0 arg00 arg01; abp-snippet1 arg10 arg11
||example.com/foo.js^$script,rewrite=blank-js`;

// Or you can read the filter list from a file:
// const filterListToConvert = readFileSync('filter-list-to-convert.txt', 'utf8');

// Filter list converter is a special case and returns a single filter list node,
// not an array of nodes, because it doesn't make sense to convert a filter list
// to multiple filter lists.
const convertedFilterList = FilterListConverter.convertToAdg(
FilterListParser.parse(filterListToConvert),
);

// You can simply serialize the filter list node, then print it to the console
console.log(FilterListParser.generate(convertedFilterList));

// Or you can serialize the filter list node to a string and write it to a file
// writeFileSync('converted-filter-list.txt', FilterListParser.generate(convertedFilterList));
```

## Limitations

Please note that the converter has some limitations:

- **Rule converter is not a full-fledged validator**, it only checks necessary
conditions for conversion. It simply tries to everything possible to
convert, but maybe the conversion result will be invalid. You should use a
separate validator to check whether the rule is valid or not.
- Rule converter doesn't support all possible cases, for example currently it
cannot convert multiple rules to a single rule.
5 changes: 5 additions & 0 deletions packages/agtree/src/converter/TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# TODO list for the converter

- [x] Implement `RuleConverter.convertToAdg` method
- [ ] Implement `RuleConverter.convertToAbp` method
- [ ] Implement `RuleConverter.convertToUbo` method
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { NotImplementedError } from '../../errors/not-implemented-error';
*/
export class ConverterBase {
/**
* Convert some data to AdGuard format
* Converts some data to AdGuard format
*
* @param data Data to convert
* @returns Converted data
Expand All @@ -26,7 +26,7 @@ export class ConverterBase {
}

/**
* Convert some data to Adblock Plus format
* Converts some data to Adblock Plus format
*
* @param data Data to convert
* @returns Converted data
Expand All @@ -37,7 +37,7 @@ export class ConverterBase {
}

/**
* Convert some data to uBlock Origin format
* Converts some data to uBlock Origin format
*
* @param data Data to convert
* @returns Converted data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,35 +17,33 @@ import { ConverterBase } from './converter-base';
*/
export class RuleConverterBase extends ConverterBase {
/**
* Convert rule to AdGuard format
* Converts an adblock filtering rule to AdGuard format, if possible.
*
* @param rule Rule to convert, can be a string or an AST
* @returns Array of converted rules ASTs
* @throws If the rule is invalid or incompatible
* @param rule Rule node to convert
* @returns Array of converted rule nodes
* @throws If the rule is invalid or cannot be converted
*/
public static convertToAdg(rule: Node): Node[] {
throw new NotImplementedError();
}

/**
* Convert rule to Adblock Plus format
* Converts an adblock filtering rule to Adblock Plus format, if possible.
*
* @param rule Rule to convert, can be a string or an AST
* @returns Array of converted rules ASTs
* @throws If the rule is invalid or incompatible
* @todo Currently not implemented in the library and temporary optional
* @param rule Rule node to convert
* @returns Array of converted rule nodes
* @throws If the rule is invalid or cannot be converted
*/
public static convertToAbp(rule: Node): Node[] {
throw new NotImplementedError();
}

/**
* Convert rule to uBlock Origin format
* Converts an adblock filtering rule to uBlock Origin format, if possible.
*
* @param rule Rule to convert, can be a string or an AST
* @returns Array of converted rules ASTs
* @throws If the rule is invalid or incompatible
* @todo Currently not implemented in the library and temporary optional
* @param rule Rule node to convert
* @returns Array of converted rule nodes
* @throws If the rule is invalid or cannot be converted
*/
public static convertToUbo(rule: Node): Node[] {
throw new NotImplementedError();
Expand Down
12 changes: 6 additions & 6 deletions packages/agtree/src/converter/comment/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@ import { RuleConverterBase } from '../base-interfaces/rule-converter-base';
/**
* Comment rule converter class
*
* @todo Implement convertToUbo and convertToAbp
* @todo Implement `convertToUbo` and `convertToAbp`
*/
export class CommentRuleConverter extends RuleConverterBase {
/**
* Convert a comment rule to AdGuard format
* Converts a comment rule to AdGuard format, if possible.
*
* @param rule Rule to convert, can be a string or an AST
* @returns Array of converted rules ASTs
* @throws If the rule is invalid or incompatible
* @param rule Rule node to convert
* @returns Array of converted rule nodes
* @throws If the rule is invalid or cannot be converted
*/
public static convertToAdg(rule: AnyCommentRule): AnyCommentRule[] {
// Clone the provided AST node to avoid side effects
const ruleNode = cloneDeep(rule);

// TODO: Support other comment types, if needed
// TODO: Add support for other comment types, if needed
// Main task is # -> ! conversion
switch (ruleNode.type) {
case CommentRuleType.CommentRule:
Expand Down
10 changes: 5 additions & 5 deletions packages/agtree/src/converter/cosmetic/css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ import { AdblockSyntax } from '../../utils/adblockers';
import { CssTree } from '../../utils/csstree';

/**
* Rule converter class
* CSS injection rule converter class
*
* @todo Implement `convertToUbo` and `convertToAbp`
*/
export class CssInjectionRuleConverter extends RuleConverterBase {
/**
* Convert rule to AdGuard format
* Converts a CSS injection rule to AdGuard format, if possible.
*
* @param rule Rule to convert, can be a string or an AST
* @returns Array of converted rules ASTs
* @throws If the rule is invalid or incompatible
* @param rule Rule node to convert
* @returns Array of converted rule nodes
* @throws If the rule is invalid or cannot be converted
*/
public static convertToAdg(rule: CssInjectionRule): CssInjectionRule[] {
// Clone the provided AST node to avoid side effects
Expand Down
10 changes: 5 additions & 5 deletions packages/agtree/src/converter/cosmetic/element-hiding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ import { CssSelectorConverter } from '../css';
import { AdblockSyntax } from '../../utils/adblockers';

/**
* Rule converter class
* Element hiding rule converter class
*
* @todo Implement `convertToUbo` and `convertToAbp`
*/
export class ElementHidingRuleConverter extends RuleConverterBase {
/**
* Converts rule to AdGuard format
* Converts an element hiding rule to AdGuard format, if possible.
*
* @param rule Rule to convert, can be a string or an AST
* @returns Array of converted rules ASTs
* @throws If the rule is invalid or incompatible
* @param rule Rule node to convert
* @returns Array of converted rule nodes
* @throws If the rule is invalid or cannot be converted
*/
public static convertToAdg(rule: ElementHidingRule): ElementHidingRule[] {
// Clone the provided AST node to avoid side effects
Expand Down
11 changes: 6 additions & 5 deletions packages/agtree/src/converter/cosmetic/header-removal.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* @file Header removal rule converter
* @file Converter for request header removal rules
*/

import cloneDeep from 'clone-deep';
Expand All @@ -25,16 +25,17 @@ export const UBO_RESPONSEHEADER_MARKER = 'responseheader';
const ADG_REMOVEHEADER_MODIFIER = 'removeheader';

/**
* Header removal rule converter class
* Converter for request header removal rules
*
* @todo Implement `convertToUbo`
* @todo Implement `convertToUbo` (ABP currently doesn't support header removal rules)
*/
export class HeaderRemovalRuleConverter extends RuleConverterBase {
/**
* Converts a header removal rule to AdGuard syntax, if possible.
*
* @param rule Rule to convert
* @returns Array of converted rules ASTs
* @param rule Rule node to convert
* @returns Array of converted rule nodes
* @throws If the rule is invalid or cannot be converted
*/
public static convertToAdg(rule: AnyRule): NetworkRule[] {
// Clone the provided AST node to avoid side effects
Expand Down
Loading

0 comments on commit e91c717

Please sign in to comment.