@adguard/filters-compiler 3.2.9 → 3.2.10-beta.0

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/README.md CHANGED
@@ -1,119 +1,255 @@
1
1
  # AdGuard Filters Compiler
2
2
 
3
- Filters compiler package is a tool for compiling ad blocking filters into a supported format.
4
- It is used in [FiltersRegistry].
5
-
6
- - [Usage](#usage)
7
- - [Tests](#tests)
8
- - [Development](#development)
9
- - [Schemas maintenance](#schemas-maintenance)
10
- - [Filters metadata](#filters-metadata)
3
+ > A Node.js library that compiles, converts, validates, and optimizes
4
+ > ad-blocking filter lists into platform-specific formats for AdGuard
5
+ > products across all supported platforms.
6
+
7
+ > **Note:** This package is developed in [AdGuardSoftwareLimited/ext-compiler].
8
+ > The [AdguardTeam/FiltersCompiler] repository is a public mirror.
9
+
10
+ ## Description
11
+
12
+ **AdGuard Filters Compiler** is a library for developers who maintain
13
+ ad-blocking filter lists and the build pipelines that ship them. It
14
+ transforms source filter lists — written in the AdGuard filter rules
15
+ syntax — into compiled output tailored to each target platform (browser
16
+ extensions, desktop and mobile apps, and the AdGuard CLI).
17
+
18
+ AdGuard's filter rules are authored once but must run on many engines
19
+ with different capabilities: not every platform supports scriptlets,
20
+ HTML filtering, `$redirect` modifiers, extended CSS, or regex-based
21
+ `$domain` selectors. The compiler closes that gap. It resolves
22
+ `@include` and `!#include` preprocessor directives, converts rules
23
+ between AdGuard and uBlock Origin formats, validates each rule against
24
+ the `@adguard/tsurlfilter` engine, optimizes blocking rules using
25
+ hit-count statistics, and strips rules that a given platform cannot
26
+ apply.
27
+
28
+ The library is consumed by [FiltersRegistry] to produce production
29
+ filter builds, but it can be used directly by anyone who needs to
30
+ compile filter lists for AdGuard platforms. The expected source
31
+ directory structure and the filters metadata format are documented in
32
+ the [FiltersRegistry README][filters-metadata].
33
+
34
+ ## Table of Contents
35
+
36
+ - [Installation](#installation)
37
+ - [Quick Start](#quick-start)
38
+ - [API Overview](#api-overview)
39
+ - [Usage Examples](#usage-examples)
40
+ - [Compiling filter lists](#compiling-filter-lists)
41
+ - [Validating built filters](#validating-built-filters)
42
+ - [Validating locale translations](#validating-locale-translations)
11
43
  - [`@include` directive and its options](#include-directive)
12
- - [Logging](#logging)
13
- - [Additional resources](#additional-resources)
44
+ - [Configuration](#configuration)
45
+ - [Custom platforms](#custom-platforms)
46
+ - [Logging](#logging)
47
+ - [Environment variables](#environment-variables)
48
+ - [Supported platforms](#supported-platforms)
49
+ - [Documentation](#documentation)
14
50
 
15
- ## Usage
51
+ ---
16
52
 
17
- This package is suggested to be used with filters repository with directory structure presented in tests here.
53
+ ## Installation
18
54
 
19
- The package could be run with the following command:
55
+ ```bash
56
+ npm install @adguard/filters-compiler
57
+ ```
20
58
 
21
- ```javascript
22
- const whitelist = [1, 3];
23
- const blacklist = [2];
59
+ Also available via pnpm or yarn:
24
60
 
25
- const path = require('path');
26
- const compiler = require("adguard-filters-compiler");
61
+ ```bash
62
+ pnpm add @adguard/filters-compiler
63
+ # or
64
+ yarn add @adguard/filters-compiler
65
+ ```
27
66
 
28
- const filtersDir = path.join(__dirname, './filters');
29
- const logPath = path.join(__dirname, './log.txt');
30
- const reportPath = path.join(__dirname, './report.txt');
67
+ The package ships as dual ESM + CJS, so both `import` and `require`
68
+ work out of the box.
31
69
 
32
- const platformsPath = path.join(__dirname, './platforms');
70
+ ## Quick Start
33
71
 
34
- const customPlatformsConfig = {
35
- // Here you can redefine some of the platforms from platforms.json
36
- // or add new platforms if you need it.
37
- "MAC_V3": {
38
- "platform": "mac",
39
- "path": "mac_v3",
40
- "configuration": {
41
- "ignoreRuleHints": false,
42
- "removeRulePatterns": [
43
- "^\\/.*" // remove regex rules for some reason.
44
- ],
45
- "replacements": [
46
- {
47
- "from": "regex",
48
- "to": "repl"
49
- }
50
- ]
51
- },
52
- "defines": {
53
- "adguard": true,
54
- "adguard_app_mac": true
55
- }
56
- },
57
- };
72
+ ```js
73
+ import { compile } from '@adguard/filters-compiler';
74
+
75
+ const filtersDir = './filters';
76
+ const logPath = './log.txt';
77
+ const reportPath = './report.txt';
78
+ const platformsPath = './platforms';
58
79
 
59
- compiler.compile(filtersDir, logPath, reportPath, platformsPath, whitelist, blacklist, customPlatformsConfig);
80
+ // Compile every filter for every platform, keeping only filters
81
+ // with IDs 1 and 3, and excluding filter ID 2.
82
+ await compile(filtersDir, logPath, reportPath, platformsPath, [1, 3], [2]);
60
83
  ```
61
84
 
62
- The built filters for the platforms can be validated by schemas.
63
- And there is `validateJSONSchema()` method for that:
85
+ The compiler reads the source filter lists from `filtersDir` and writes
86
+ platform-specific output to subdirectories under `platformsPath`. A
87
+ human-readable compilation report is written to `reportPath`, and a
88
+ timestamped log to `logPath`.
89
+
90
+ ## API Overview
91
+
92
+ The library exports three functions:
93
+
94
+ | Function | Purpose |
95
+ | ------------------------- | ---------------------------------------------------- |
96
+ | `compile(...)` | Compiles filter lists into platform-specific output |
97
+ | `validateJSONSchema(...)` | Validates built platform output against JSON schemas |
98
+ | `validateLocales(...)` | Validates locale translation files for completeness |
99
+
100
+ ### `compile(...)`
101
+
102
+ ```ts
103
+ function compile(
104
+ path: string,
105
+ logPath: string | undefined,
106
+ reportFile: string | undefined,
107
+ platformsPath: string,
108
+ whitelist: number[],
109
+ blacklist: number[],
110
+ customPlatformsConfig?: CustomPlatformsConfig,
111
+ ): Promise<void>;
112
+ ```
113
+
114
+ Compiles the filter lists in `path` for all configured platforms and
115
+ writes the results to `platformsPath`.
116
+
117
+ - `whitelist` — compile only the filter IDs listed here (empty array
118
+ compiles all filters).
119
+ - `blacklist` — exclude the filter IDs listed here.
120
+ - `customPlatformsConfig` — overrides or extends the built-in platform
121
+ definitions (see [Custom platforms](#custom-platforms)).
64
122
 
65
- ```javascript
66
- const compiler = require("adguard-filters-compiler");
123
+ ### `validateJSONSchema(...)`
67
124
 
68
- const validationResult = compiler.validateJSONSchema(<platformsPath>, <FILTERS_REQUIRED_AMOUNT>);
125
+ ```ts
126
+ function validateJSONSchema(
127
+ platformsPath: string,
128
+ requiredFiltersAmount: number,
129
+ ): boolean;
69
130
  ```
70
131
 
71
- where `<platformsPath>` is the path to the platforms directory
72
- and `<FILTERS_REQUIRED_AMOUNT>` is an expected minimum number of filters.
132
+ Recursively validates the built JSON files in `platformsPath` against
133
+ the schemas bundled with the library (`filters.schema.json` and
134
+ `filters_i18n.schema.json`). `requiredFiltersAmount` is the minimum
135
+ number of filters expected in each platform's `filters.json`.
73
136
 
74
- ## Tests
137
+ Returns `true` when all files are valid. On validation failure it logs
138
+ the errors and returns `false` instead of throwing, so callers must
139
+ check the return value.
75
140
 
76
- ```bash
77
- pnpm test
141
+ ### `validateLocales(...)`
142
+
143
+ ```ts
144
+ function validateLocales(
145
+ localesDirPath: string,
146
+ requiredLocales: string[],
147
+ ): ValidateLocalesResult;
148
+
149
+ interface ValidateLocalesResult {
150
+ ok: boolean;
151
+ data?: unknown[]; // per-locale warning details, present when warnings exist
152
+ log?: string; // formatted warnings log, present when warnings exist
153
+ }
78
154
  ```
79
155
 
80
- ## Development
156
+ Validates that the locale files in `localesDirPath` are complete — that
157
+ every filter, group, and tag has a translated name and description for
158
+ all locales listed in `requiredLocales`.
81
159
 
82
- > No new fields should be added to the metadata files for old `mac` and current `mac_v2` platforms,
83
- > check [generator.js](./src/main/platforms/generator.js) for more details.
160
+ Returns `{ ok: true }` when no problems are found. When warnings are
161
+ found, `data` and `log` contain the per-locale details and `ok` is
162
+ `false` only if at least one warning is critical. Throws when the
163
+ locales directory is missing or empty.
84
164
 
85
- In order to add support for new scriptlets and redirects,
86
- you should update `@adguard/tsurlfilter` with updated scriptlets.
165
+ ## Usage Examples
87
166
 
88
- For fixing scriptlets converting or validation you should update `@adguard/scriptlets`.
167
+ ### Compiling filter lists
89
168
 
90
- ### Schemas maintenance
169
+ Compile all filters with the default platform configuration:
91
170
 
92
- Schemas which are used for `validateJSONSchema()` method are located in `schemas/` directory:
171
+ ```js
172
+ import { compile } from '@adguard/filters-compiler';
93
173
 
94
- - `filters.schema.json` — schema for *filters* metadata;
95
- - `filters_i18n.schema.json` schema for *filters_i18n* metadata.
174
+ await compile(
175
+ './filters', // source filter lists
176
+ './log.txt', // log file (omit to disable logging)
177
+ './report.txt', // compilation report
178
+ './platforms', // platform output directory
179
+ [], // whitelist (empty = compile all)
180
+ [], // blacklist (empty = exclude none)
181
+ );
182
+ ```
96
183
 
97
- > Schemas in `schemas/mac/` directory are needed for legacy macOS v1 platform, so they should not be changed.
98
- > The same is true for `schemas/mac_v2/` directory.
184
+ Compile a single filter into a custom platform:
99
185
 
100
- If any changes should be made in the schemas, e.g. adding a new locale or filter or tag,
101
- **never edit them directly in `schemas/` manually**.
186
+ ```js
187
+ import { compile } from '@adguard/filters-compiler';
102
188
 
103
- Instead of that, you should edit scripts in `tasks/build-schemas/` directory
104
- and use the following command to generate the schemas:
189
+ const customPlatformsConfig = {
190
+ // Here you can redefine some of the platforms from platforms.json
191
+ // or add new platforms if you need it.
192
+ MAC_V3: {
193
+ platform: 'mac',
194
+ path: 'mac_v3',
195
+ configuration: {
196
+ ignoreRuleHints: false,
197
+ removeRulePatterns: [
198
+ '^\\/.*', // drop regex rules
199
+ ],
200
+ replacements: [
201
+ { from: 'regex', to: 'repl' },
202
+ ],
203
+ },
204
+ defines: {
205
+ adguard: true,
206
+ adguard_app_mac: true,
207
+ },
208
+ },
209
+ };
105
210
 
106
- ```bash
107
- pnpm build-schemas
211
+ await compile(
212
+ './filters',
213
+ undefined, // no log file
214
+ './report.txt',
215
+ './platforms',
216
+ [1], // only filter ID 1
217
+ [],
218
+ customPlatformsConfig,
219
+ );
108
220
  ```
109
221
 
110
- ## Filters metadata
222
+ ### Validating built filters
223
+
224
+ After compiling, validate the built output against the bundled schemas:
111
225
 
112
- Description of the filters metadata is available in the [FiltersRegistry][filters-metadata] repository.
226
+ ```js
227
+ import { validateJSONSchema } from '@adguard/filters-compiler';
228
+
229
+ // Each platform build must contain at least 50 filters
230
+ const valid = validateJSONSchema('./platforms', 50);
231
+ if (!valid) {
232
+ throw new Error('Schema validation failed, see log for details');
233
+ }
234
+ ```
235
+
236
+ ### Validating locale translations
237
+
238
+ ```js
239
+ import { validateLocales } from '@adguard/filters-compiler';
240
+
241
+ const result = validateLocales('./locales', ['en', 'fr', 'ko']);
242
+ if (!result.ok) {
243
+ console.error(result.log);
244
+ }
245
+ ```
113
246
 
114
247
  ## <a name="include-directive"></a> `@include` directive and its options
115
248
 
116
- The `@include` directive provides the ability to include content from the specified address.
249
+ The `@include` directive provides the ability to include content from
250
+ the specified address. Filter source files use it to pull in content
251
+ from other files or remote URLs during compilation, letting you compose
252
+ filter lists from shared fragments.
117
253
 
118
254
  ### Syntax
119
255
 
@@ -121,152 +257,187 @@ The `@include` directive provides the ability to include content from the specif
121
257
  @include <filepath> [<options>]
122
258
  ```
123
259
 
124
- where:
125
-
126
- - `<filepath>` — required, URL or same origin relative file path to be included;
127
- - `<options>` — optional, a list of options separated by spaces.
128
- Available options:
129
-
130
- - `/stripComments` removes AdBlock-style syntax comments from the included file — lines which start with `!`;
131
- - `/notOptimized` adds a `!+ NOT_OPTIMIZED` hint to the rules;
132
- - `/exclude="<filepath>"` excludes from the included file rules
133
- listed in the exceptions file available by `filepath`;
134
- - `/addModifiers="<modifiers>"` adds the specified `modifiers` (string as is) to the rules in the included file.
135
- The addModifiers option can also work with the host-rule format files.
136
- In this case, host-file comments are to be replaced `#` by AdBlock-style syntax comments `!`;
137
- - `/ignoreTrustLevel` disables the check of the trust level of the included file.
138
- Allowed only for the same origin files.
139
- - `/optimizeDomainBlockingRules` remove redundant rules for domain blocking of the included file.
140
- Base rules with modifiers and rules of other format will be ignored.
141
- Comments are not checked separately and may not be relevant after optimization.
260
+ - `<filepath>` — required, URL or same-origin relative file path to
261
+ include.
262
+ - `<options>` — optional, space-separated flags:
263
+
264
+ - `/stripComments` — removes AdBlock-style comment lines (starting
265
+ with `!`) from the included file.
266
+ - `/notOptimized` adds a `!+ NOT_OPTIMIZED` hint to the rules.
267
+ - `/exclude="<filepath>"` excludes rules listed in the exception
268
+ file at `filepath`.
269
+ - `/addModifiers="<modifiers>"` appends the given modifiers (as
270
+ is) to every rule in the included file. Works with host-rule
271
+ files too, converting `#` host-file comments to `!` AdBlock-style
272
+ comments.
273
+ - `/ignoreTrustLevel` skips the trust-level check for the
274
+ included file. Only allowed for same-origin files.
275
+ - `/optimizeDomainBlockingRules` removes redundant
276
+ domain-blocking rules. Rules with modifiers and rules of other
277
+ formats are ignored.
142
278
 
143
279
  > [!IMPORTANT]
144
- > The content of the included file is formatted by the options due to the order of their mention in the directive,
145
- > except `/ignoreTrustLevel`.
280
+ > Options are applied in the order they appear in the directive, except
281
+ > `/ignoreTrustLevel`.
146
282
 
147
283
  ### Examples
148
284
 
149
- - Include a file with domains, add modifiers to the rules, exclude some rules,
150
- add a hint to the rules, and remove comments from the prepared rules:
151
-
152
- ```adblock
153
- @include ../input.txt /addModifiers="script" /exclude="../exclusions.txt" /notOptimized /stripComments /optimizeDomainBlockingRules
154
- ```
155
-
156
- The order of execution of the options is as follows:
285
+ Apply all options to an included file:
157
286
 
158
- 1. `@include ../input.txt`: Includes the content of the file named `input.txt` from the parent directory.
159
-
160
- ```adblock
161
- # comment
162
- example.com
163
- example.org
164
- ```
165
-
166
- 1. `/addModifiers="script"`: Adds the `$script` modifier to all rules in the included file.
167
-
168
- Result of adding modifiers:
287
+ <!-- markdownlint-disable line-length -->
288
+ ```adblock
289
+ @include ../input.txt /addModifiers="script" /exclude="../exclusions.txt" /notOptimized /stripComments /optimizeDomainBlockingRules
290
+ ```
291
+ <!-- markdownlint-enable line-length -->
169
292
 
170
- ```adblock
171
- ! comment
172
- example.com$script
173
- example.org$script
174
- ```
293
+ Given `../input.txt`:
175
294
 
176
- > Used to restrict rules with modifiers when blocking the entire domain would result in a breakage.
177
- > [issue example](https://github.com/AdguardTeam/FiltersCompiler/issues/190)
295
+ ```adblock
296
+ # comment
297
+ example.com
298
+ example.org
299
+ ```
178
300
 
179
- 1. `/exclude="../exclusions.txt"`: Excludes rules listed in the exception list from the file named `exclusions.txt`, if they match.
301
+ **1. `/addModifiers="script"`** appends `$script` to every rule and
302
+ converts host-file `#` comments to AdBlock-style `!`:
180
303
 
181
- Due to the content of `exclusions.txt`:
304
+ ```adblock
305
+ ! comment
306
+ example.com$script
307
+ example.org$script
308
+ ```
182
309
 
183
- ```adblock
184
- example.com$script
185
- example2.com$script
186
- ```
310
+ **2. `/exclude="../exclusions.txt"`** removes rules listed in
311
+ `exclusions.txt` (for example, `example.com$script`):
187
312
 
188
- Result of excluding:
313
+ ```adblock
314
+ ! comment
315
+ example.org$script
316
+ ```
189
317
 
190
- ```adblock
191
- ! comment
192
- example.org$script
193
- ```
318
+ **3. `/notOptimized`** adds the hint:
194
319
 
195
- > Used to exclude problematic rules in the filter
320
+ ```adblock
321
+ ! comment
322
+ !+ NOT_OPTIMIZED
323
+ example.org$script
324
+ ```
196
325
 
197
- 1. `/notOptimized`: Adds the `!+ NOT_OPTIMIZED` hint to the rules.
326
+ **4. `/stripComments`** removes comment lines:
198
327
 
199
- Result of adding the hint:
328
+ ```adblock
329
+ !+ NOT_OPTIMIZED
330
+ example.org$script
331
+ ```
200
332
 
201
- ```adblock
202
- ! comment
203
- !+ NOT_OPTIMIZED
204
- example.org$script
205
- ```
333
+ **5. `/optimizeDomainBlockingRules`** removes redundant domain-blocking
334
+ rules. For example, when `||sub.example.com^` is already covered by
335
+ `||example.com^`, it is dropped, while rules with modifiers such as
336
+ `||test.com^$script` are kept:
206
337
 
207
- > Used in cases where the filter is designed for mobile site layout and some rules may be removed,
208
- > due to the lack of ability to collect statistics on mobile platforms.
338
+ ```adblock
339
+ ||example.com^
340
+ ||domain.com^
341
+ ||test.com^$script
342
+ ```
209
343
 
210
- 1. `/stripComments`: Removes comments in AdBlock style from the included file.
344
+ Include a remote file with comment stripping:
211
345
 
212
- ```adblock
213
- !+ NOT_OPTIMIZED
214
- example.org$script
215
- ```
346
+ ```adblock
347
+ @include "https://easylist.github.io/easylist/easylist.txt" /stripComments
348
+ ```
216
349
 
217
- 1. `/optimizeDomainBlockingRules`: Remove only domain blocking redundant rules from the included file.
350
+ Skip the trust-level check for a same-origin included file:
218
351
 
219
- Due to the optimization:
352
+ ```adblock
353
+ @include ./input.txt /ignoreTrustLevel
354
+ ```
220
355
 
221
- ```adblock
222
- ||example.com^
223
- ||sub.example.com^
224
- ||domain.com^
225
- ||test.com^$script
226
- ```
356
+ ## Configuration
227
357
 
228
- Result of optimization:
358
+ ### Custom platforms
229
359
 
230
- ```adblock
231
- ||example.com^
232
- ||domain.com^
233
- ||test.com^$script
234
- ```
360
+ Pass `customPlatformsConfig` to `compile()` to redefine existing
361
+ platforms or add new ones. Each entry mirrors the structure in
362
+ `src/main/platforms-config.js`:
235
363
 
236
- - Ignore the trust level of the filter list (specified in the metadata) during the file including —
237
- include the file rules as is:
364
+ ```js
365
+ const customPlatformsConfig = {
366
+ MAC_V3: {
367
+ platform: 'mac', // platform family
368
+ path: 'mac_v3', // output subdirectory under platformsPath
369
+ expires: '12 hours', // optional cache TTL written to metadata
370
+ configuration: {
371
+ ignoreRuleHints: false, // honour `!+ HINT` hints when false
372
+ removeRulePatterns: [ // regexes of rules to drop
373
+ '^\\/.*',
374
+ ],
375
+ replacements: [ // regex replacements applied to rules
376
+ { from: 'regex', to: 'repl' },
377
+ ],
378
+ },
379
+ defines: { // !#if preprocessor flags for this platform
380
+ adguard: true,
381
+ adguard_app_mac: true,
382
+ },
383
+ },
384
+ };
385
+ ```
238
386
 
239
- ```adblock
240
- @include ./input.txt /ignoreTrustLevel
241
- ```
387
+ `replacements[].from` is treated as a regular-expression pattern (it is
388
+ passed to `new RegExp(from, 'g')`), not as literal text. Escape regex
389
+ metacharacters in `from` when a literal match is intended.
242
390
 
243
- ## Logging
391
+ ### Logging
244
392
 
245
- In order for the compiler to write logs, you need to pass `logPath` to the `compile` function as the second argument:
393
+ The compiler writes a timestamped log file when `logPath` is passed to
394
+ `compile()`. If `logPath` is omitted, no log file is written. The parent
395
+ directory is created automatically when it does not exist.
246
396
 
247
- ```javascript
248
- compile = (path, logPath, reportFile, platformsPath, whitelist, blacklist, customPlatformsConfig)
249
- ```
397
+ Log levels:
250
398
 
251
- If the `logPath` argument is not passed, the log file will not be written. If the directory with the file does not exist, it will be created.
399
+ - `INFO` general information about the process
400
+ - `WARN` — warnings that do not stop the process
401
+ - `ERROR` — errors indicating failures
252
402
 
253
- ### Log Levels
403
+ ### Environment variables
254
404
 
255
- The logger supports the following log levels:
405
+ | Variable | Default | Purpose |
406
+ | -------- | ----------------- | ------------------------------------------------------------------------------------------------- |
407
+ | `TLS` | (system defaults) | Set to `insecure` to bypass TLS certificate verification when downloading external filter sources |
256
408
 
257
- - `INFO`: General information about the process
258
- - `WARN`: Warning messages that don't stop the process
259
- - `ERROR`: Error messages indicating failures
409
+ ## Supported platforms
260
410
 
261
- ### Logger Initialization
411
+ The compiler ships built-in configurations for the following platforms.
412
+ Each writes its output to a subdirectory under `platformsPath`.
262
413
 
263
- The logger is initialized during compilation when a valid `logPath` is provided.
414
+ | Platform ID | Output directory | Target |
415
+ | ----------------------------------- | ----------------------------------- | ----------------------------------------- |
416
+ | `WINDOWS` | `windows` | AdGuard for Windows |
417
+ | `MAC` | `mac` | AdGuard for macOS (legacy v1) |
418
+ | `MAC_V2` | `mac_v2` | AdGuard for macOS v2 |
419
+ | `MAC_V3` | `mac_v3` | AdGuard for macOS v3 |
420
+ | `ANDROID` | `android` | AdGuard for Android |
421
+ | `IOS` | `ios` | AdGuard for iOS |
422
+ | `CLI` | `cli` | AdGuard CLI / CoreLibs |
423
+ | `EXTENSION_CHROMIUM` | `extension/chromium` | AdGuard Browser Extension (Chromium, MV2) |
424
+ | `EXTENSION_CHROMIUM_MV3` | `extension/chromium-mv3` | AdGuard Browser Extension (Chromium, MV3) |
425
+ | `EXTENSION_EDGE` | `extension/edge` | AdGuard Browser Extension (Edge, MV2) |
426
+ | `EXTENSION_OPERA` | `extension/opera` | AdGuard Browser Extension (Opera, MV2) |
427
+ | `EXTENSION_OPERA_MV3` | `extension/opera-mv3` | AdGuard Browser Extension (Opera, MV3) |
428
+ | `EXTENSION_FIREFOX` | `extension/firefox` | AdGuard Browser Extension (Firefox) |
429
+ | `EXTENSION_SAFARI` | `extension/safari` | AdGuard Browser Extension (Safari) |
430
+ | `EXTENSION_ANDROID_CONTENT_BLOCKER` | `extension/android-content-blocker` | AdGuard content blocker (Android) |
431
+ | `EXTENSION_UBLOCK` | `extension/ublock` | uBlock Origin-compatible output |
264
432
 
265
- ## Additional resources
433
+ ## Documentation
266
434
 
267
- - [AGENTS.md](AGENTS.md) — AI agent instructions and code guidelines
268
- - [DEVELOPMENT.md](DEVELOPMENT.md) — Development environment setup guide
269
- - [CHANGELOG.md](CHANGELOG.md) — Version history
435
+ - [Development](DEVELOPMENT.md) — setup, build, and contributing workflow
436
+ - [Deployment and configuration](DEPLOYMENT.md) — release pipeline and runtime environment
437
+ - [Changelog](CHANGELOG.md) — version history
438
+ - [LLM agent rules](AGENTS.md) — AI-assisted development guidelines
270
439
 
271
440
  [FiltersRegistry]: https://github.com/AdguardTeam/FiltersRegistry/
272
441
  [filters-metadata]: https://github.com/AdguardTeam/FiltersRegistry/blob/master/README.md#filters-metadata
442
+ [AdGuardSoftwareLimited/ext-compiler]: https://github.com/AdGuardSoftwareLimited/ext-compiler
443
+ [AdguardTeam/FiltersCompiler]: https://github.com/AdguardTeam/FiltersCompiler
package/dist/index.cjs CHANGED
@@ -23,57 +23,47 @@ var Ajv = require('ajv');
23
23
 
24
24
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
25
25
  /**
26
- * Version utility functions
26
+ * Version utility class.
27
27
  */
28
- const version = {
28
+ class Version {
29
29
  /**
30
- * Parses version from string
30
+ * Parses version from string.
31
31
  *
32
- * @param v version string
33
- * @returns {Array}
32
+ * @param v Version string.
33
+ * @returns Array of numeric version parts.
34
34
  */
35
- parse(v) {
36
- const version = [];
37
- const parts = String(v || '').split('.');
38
-
35
+ static parse(v) {
36
+ const parts = String(v ?? '').split('.');
39
37
  const parseVersionPart = (part) => {
40
- if (Number.isNaN(part)) {
38
+ const n = Number(part);
39
+ if (Number.isNaN(n)) {
41
40
  return 0;
42
41
  }
43
- return Math.max(part - 0, 0);
42
+ return Math.max(n, 0);
44
43
  };
45
-
46
- // eslint-disable-next-line no-restricted-syntax
47
- for (const part of parts) {
48
- version.push(parseVersionPart(part));
49
- }
50
-
51
- return version;
52
- },
53
-
44
+ return parts.map(parseVersionPart);
45
+ }
54
46
  /**
55
- * Increments build part of version '0.0.0.0'
47
+ * Increments the build (last) part of a version string `'0.0.0.0'`.
48
+ * Carries over when a part reaches 100.
56
49
  *
57
- * @param v version string
58
- * @returns {string}
50
+ * @param v Version string.
51
+ * @returns Incremented version string.
59
52
  */
60
- increment(v) {
61
- const version = this.parse(v);
62
-
63
- if (version.length > 0) {
64
- version[version.length - 1] = version[version.length - 1] + 1;
53
+ static increment(v) {
54
+ const parts = Version.parse(v);
55
+ if (parts.length > 0) {
56
+ parts[parts.length - 1] += 1;
65
57
  }
66
-
67
- for (let i = version.length; i > 0; i -= 1) {
68
- if (version[i] === 100) {
69
- version[i] = 0;
70
- version[i - 1] += 1;
58
+ for (let i = parts.length; i > 0; i -= 1) {
59
+ if (parts[i] === 100) {
60
+ parts[i] = 0;
61
+ parts[i - 1] += 1;
71
62
  }
72
63
  }
73
-
74
- return version.join('.');
75
- },
76
- };
64
+ return parts.join('.');
65
+ }
66
+ }
77
67
 
78
68
  /**
79
69
  * Extend logger implementation
@@ -3413,7 +3403,7 @@ const makeRevision = function (path, hash) {
3413
3403
  }
3414
3404
 
3415
3405
  if (!currentRevision.hash || currentRevision.hash !== result.hash) {
3416
- result.version = version.increment(currentRevision.version);
3406
+ result.version = Version.increment(currentRevision.version);
3417
3407
  result.timeUpdated = new Date().getTime();
3418
3408
  }
3419
3409
  }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Type declarations for the public API of @adguard/filters-compiler.
3
+ *
4
+ * These declarations provide TypeScript consumers with typed signatures for
5
+ * the three exported functions. They are hand-written because the entry point
6
+ * (src/index.js) is JavaScript and is not processed by the TypeScript compiler.
7
+ *
8
+ * When src/index.js is eventually migrated to TypeScript, this file should be
9
+ * removed — the compiler will generate declarations automatically.
10
+ */
11
+
12
+ /**
13
+ * Platform configuration for a single platform.
14
+ */
15
+ export interface PlatformConfig {
16
+ [key: string]: unknown;
17
+ }
18
+
19
+ /**
20
+ * Custom platform configurations keyed by platform name.
21
+ */
22
+ export type CustomPlatformsConfig = Record<string, PlatformConfig>;
23
+
24
+ /**
25
+ * Compiles filter lists for all platforms.
26
+ *
27
+ * @param path Path to the filter lists directory.
28
+ * @param logPath Path for the compilation log file (logging disabled if omitted).
29
+ * @param reportFile Path for the compilation report file.
30
+ * @param platformsPath Path for platform-specific output.
31
+ * @param whitelist Whitelisted filter IDs.
32
+ * @param blacklist Blacklisted filter IDs.
33
+ * @param customPlatformsConfig Optional custom platform configurations.
34
+ */
35
+ export function compile(
36
+ path: string,
37
+ logPath: string | undefined,
38
+ reportFile: string | undefined,
39
+ platformsPath: string,
40
+ whitelist: number[],
41
+ blacklist: number[],
42
+ customPlatformsConfig?: CustomPlatformsConfig,
43
+ ): Promise<void>;
44
+
45
+ /**
46
+ * Validates built filter files against JSON schemas.
47
+ *
48
+ * Validation failures are logged, not thrown — check the return value.
49
+ *
50
+ * @param platformsPath Path to the built platform output.
51
+ * @param requiredFiltersAmount Minimum number of filters expected.
52
+ * @returns `true` when all files are valid, `false` on validation failure.
53
+ */
54
+ export function validateJSONSchema(
55
+ platformsPath: string,
56
+ requiredFiltersAmount: number,
57
+ ): boolean;
58
+
59
+ /**
60
+ * Result of locale validation.
61
+ */
62
+ export interface ValidateLocalesResult {
63
+ /** `false` when at least one critical warning was found. */
64
+ ok: boolean;
65
+ /** Per-locale warning details. Present when warnings exist. */
66
+ data?: unknown[];
67
+ /** Formatted warnings log. Present when warnings exist. */
68
+ log?: string;
69
+ }
70
+
71
+ /**
72
+ * Validates locale translation files.
73
+ *
74
+ * @param localesDirPath Path to the locales directory.
75
+ * @param requiredLocales List of required locale codes.
76
+ * @returns `{ ok: true }` when no problems are found; when warnings exist,
77
+ * the result includes `data` and `log`, and `ok` is `false` only for
78
+ * critical warnings.
79
+ * @throws Error when the locales directory is missing or empty.
80
+ */
81
+ export function validateLocales(
82
+ localesDirPath: string,
83
+ requiredLocales: string[],
84
+ ): ValidateLocalesResult;
package/dist/index.js CHANGED
@@ -20,57 +20,47 @@ import { parse as parse$1 } from 'tldts';
20
20
  import Ajv from 'ajv';
21
21
 
22
22
  /**
23
- * Version utility functions
23
+ * Version utility class.
24
24
  */
25
- const version = {
25
+ class Version {
26
26
  /**
27
- * Parses version from string
27
+ * Parses version from string.
28
28
  *
29
- * @param v version string
30
- * @returns {Array}
29
+ * @param v Version string.
30
+ * @returns Array of numeric version parts.
31
31
  */
32
- parse(v) {
33
- const version = [];
34
- const parts = String(v || '').split('.');
35
-
32
+ static parse(v) {
33
+ const parts = String(v ?? '').split('.');
36
34
  const parseVersionPart = (part) => {
37
- if (Number.isNaN(part)) {
35
+ const n = Number(part);
36
+ if (Number.isNaN(n)) {
38
37
  return 0;
39
38
  }
40
- return Math.max(part - 0, 0);
39
+ return Math.max(n, 0);
41
40
  };
42
-
43
- // eslint-disable-next-line no-restricted-syntax
44
- for (const part of parts) {
45
- version.push(parseVersionPart(part));
46
- }
47
-
48
- return version;
49
- },
50
-
41
+ return parts.map(parseVersionPart);
42
+ }
51
43
  /**
52
- * Increments build part of version '0.0.0.0'
44
+ * Increments the build (last) part of a version string `'0.0.0.0'`.
45
+ * Carries over when a part reaches 100.
53
46
  *
54
- * @param v version string
55
- * @returns {string}
47
+ * @param v Version string.
48
+ * @returns Incremented version string.
56
49
  */
57
- increment(v) {
58
- const version = this.parse(v);
59
-
60
- if (version.length > 0) {
61
- version[version.length - 1] = version[version.length - 1] + 1;
50
+ static increment(v) {
51
+ const parts = Version.parse(v);
52
+ if (parts.length > 0) {
53
+ parts[parts.length - 1] += 1;
62
54
  }
63
-
64
- for (let i = version.length; i > 0; i -= 1) {
65
- if (version[i] === 100) {
66
- version[i] = 0;
67
- version[i - 1] += 1;
55
+ for (let i = parts.length; i > 0; i -= 1) {
56
+ if (parts[i] === 100) {
57
+ parts[i] = 0;
58
+ parts[i - 1] += 1;
68
59
  }
69
60
  }
70
-
71
- return version.join('.');
72
- },
73
- };
61
+ return parts.join('.');
62
+ }
63
+ }
74
64
 
75
65
  /**
76
66
  * Extend logger implementation
@@ -3410,7 +3400,7 @@ const makeRevision = function (path, hash) {
3410
3400
  }
3411
3401
 
3412
3402
  if (!currentRevision.hash || currentRevision.hash !== result.hash) {
3413
- result.version = version.increment(currentRevision.version);
3403
+ result.version = Version.increment(currentRevision.version);
3414
3404
  result.timeUpdated = new Date().getTime();
3415
3405
  }
3416
3406
  }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Version utility class.
3
+ */
4
+ export declare class Version {
5
+ /**
6
+ * Parses version from string.
7
+ *
8
+ * @param v Version string.
9
+ * @returns Array of numeric version parts.
10
+ */
11
+ static parse(v: string | null | undefined): number[];
12
+ /**
13
+ * Increments the build (last) part of a version string `'0.0.0.0'`.
14
+ * Carries over when a part reaches 100.
15
+ *
16
+ * @param v Version string.
17
+ * @returns Incremented version string.
18
+ */
19
+ static increment(v: string | null | undefined): string;
20
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,5 @@
1
1
  {
2
2
  "name": "@adguard/filters-compiler",
3
- "version": "3.2.9",
4
3
  "description": "AdGuard filters compiler",
5
4
  "homepage": "http://adguard.com",
6
5
  "type": "module",
@@ -10,48 +9,61 @@
10
9
  ],
11
10
  "exports": {
12
11
  ".": {
12
+ "types": "./dist/index.d.ts",
13
13
  "import": "./dist/index.js",
14
14
  "require": "./dist/index.cjs"
15
15
  }
16
16
  },
17
17
  "engines": {
18
- "node": ">=22"
18
+ "node": ">=22",
19
+ "pnpm": ">=10.33.4 <11"
19
20
  },
20
21
  "dependencies": {
21
22
  "@adguard/agtree": "4.1.0-beta.0",
22
- "@adguard/css-tokenizer": "^1.2.0",
23
- "@adguard/ecss-tree": "^2.0.1",
23
+ "@adguard/css-tokenizer": "1.2.0",
24
+ "@adguard/ecss-tree": "2.0.1",
25
+ "@adguard/extended-css": "2.1.1",
26
+ "@adguard/filters-downloader": "2.4.4",
27
+ "@adguard/logger": "2.0.0",
28
+ "@adguard/scriptlets": "2.3.1",
29
+ "@adguard/tsurlfilter": "4.0.5",
24
30
  "@eslint/css-tree": "3.6.6",
25
- "@adguard/extended-css": "^2.1.1",
26
- "@adguard/filters-downloader": "^2.4.0",
27
- "@adguard/logger": "^2.0.0",
28
- "@adguard/scriptlets": "^2.3.1",
29
- "@adguard/tsurlfilter": "^4.0.5",
30
- "ajv": "^8.17.1",
31
- "child_process": ">=1.0.2",
32
- "jsdom": "^21.1.1",
31
+ "ajv": "8.17.1",
32
+ "child_process": "1.0.2",
33
+ "jsdom": "21.1.2",
33
34
  "md5": "2.3.0",
34
- "moment": "^2.29.4",
35
- "tldts": "^5.7.112",
36
- "utf8": "^3.0.0"
35
+ "moment": "2.30.1",
36
+ "tldts": "5.7.112",
37
+ "utf8": "3.0.0"
37
38
  },
38
39
  "devDependencies": {
39
- "@types/jsdom": "^21.1.7",
40
+ "@rollup/plugin-typescript": "12.3.0",
41
+ "@types/jsdom": "21.1.7",
42
+ "@typescript-eslint/eslint-plugin": "8.59.0",
43
+ "@typescript-eslint/parser": "8.59.0",
40
44
  "eslint": "8.57.1",
41
- "eslint-config-airbnb-base": "^15.0.0",
42
- "eslint-import-resolver-exports": "^1.0.0-beta.5",
43
- "eslint-plugin-import": "^2.31.0",
44
- "husky": "^8.0.2",
45
- "rollup": "^4.39.0",
46
- "rollup-plugin-copy": "^3.5.0",
47
- "vitest": "^3.0.5"
45
+ "eslint-config-airbnb-base": "15.0.0",
46
+ "eslint-import-resolver-exports": "1.0.0-beta.5",
47
+ "eslint-plugin-import": "2.31.0",
48
+ "husky": "8.0.2",
49
+ "markdownlint": "0.40.0",
50
+ "markdownlint-cli": "0.48.0",
51
+ "rimraf": "6.1.3",
52
+ "rollup": "4.39.0",
53
+ "rollup-plugin-copy": "3.5.0",
54
+ "tslib": "2.8.1",
55
+ "typescript": "6.0.3",
56
+ "vitest": "3.0.5"
48
57
  },
58
+ "version": "3.2.10-beta.0",
49
59
  "scripts": {
60
+ "prebuild": "rimraf dist",
50
61
  "build": "rollup --config rollup.config.js --silent",
51
62
  "test": "vitest",
52
- "lint": "eslint --cache .",
53
- "increment": "pnpm version patch --no-git-tag-version",
54
- "build-txt": "node tasks/build-txt.mjs",
63
+ "lint": "pnpm lint:code && pnpm lint:types && pnpm lint:md",
64
+ "lint:code": "eslint --cache .",
65
+ "lint:types": "tsc --noEmit",
66
+ "lint:md": "markdownlint .",
55
67
  "build-schemas": "node --experimental-specifier-resolution=node tasks/build-schemas/index.js",
56
68
  "tgz": "pnpm pack --out filters-compiler.tgz"
57
69
  }
package/dist/build.txt DELETED
@@ -1 +0,0 @@
1
- version=3.2.9