AdGuard Filters Registry is the canonical repository of filter list
subscriptions available to AdGuard users. It stores AdGuard's own filters
and re-hosted third-party filter lists (served via filters.adtidy.org).
The build pipeline compiles filter templates into platform-specific outputs, generates patches (diffs), and produces localized metadata for all supported AdGuard products.
- Language / Version: TypeScript and JavaScript, Node.js >= 24, ESM (
"type": "module"). New code must be written in TypeScript. The project is gradually migrating all scripts to TypeScript; existing.jsfiles should be converted to.tswhen touched. - Primary Dependencies:
@adguard/filters-compiler— compiles filter templates into final filter lists@adguard/agtree— AdGuard filter rule parser / AST@adguard/diff-builder— generates incremental patches between filter versions@adguard/dead-domains-linter— detects rules targeting dead domainszod— schema validationcrypto-js— checksum computation
- Storage: File-system based; no database
- Testing: Vitest (unit + integration + e2e tests under
scripts/*/__tests__/) - Target Platform: Node.js CLI tooling; build outputs target 8 top-level AdGuard product platforms (Android, CLI, Extension, iOS, Mac, Mac v2, Mac v3, Windows). The Extension platform has 10 sub-targets: Chromium, Chromium MV3, Edge, Edge MV3, Firefox, Opera, Opera MV3, Safari, Android Content Blocker, uBlock.
- Project Type: Single repository (build tooling + data)
- CI: GitHub Actions workflows:
build-adguard.yamlbuild-3p.yamltest.yaml(runsyarn teston every pull request)compression-tests.yaml(runs the repository compression tests in a container on pull requests that touchscripts/repository/, and monthly)update-translations.yaml(translations download from Crowdin that opens a PR; runs every Monday at 06:00 UTC and on manual dispatch)
- Translations upload: manual only, via the legacy
scripts/translations/upload.sh(there is no automated or Crowdin-CLI-based upload) - Performance Goals: N/A
- Constraints: Filter lists must remain compatible with AdGuard's rule syntax; third-party filters follow an acceptance policy documented in README.md
- Scale / Scope: dozens of AdGuard filters, dozens of third-party filters, multiple platform targets, many locale translations
├── filters/ # Filter list sources
│ ├── filter_<ID>_<Name>/ # AdGuard filters (dozens of dirs) — each contains template.txt,
│ │ # metadata.json, revision.json, filter.txt, diff.txt
│ ├── ThirdParty/ # Third-party filters (dozens of dirs, same structure)
│ └── exclusions.txt # Global rule exclusions
├── platforms/ # Built platform-specific outputs (multiple top-level platforms)
│ └── <platform>/ # android, cli, extension, ios, mac, mac_v2, mac_v3, windows
│ ├── filters/ # Compiled filter files per platform
│ ├── patches/ # Incremental diff patches
│ ├── filters.json # Filter metadata for the platform
│ ├── filters_i18n.json # Localized filter metadata
│ └── <sub-target>/ # extension/ only: chromium, chromium-mv3, edge, edge-mv3,
│ # firefox, opera, opera-mv3, safari, android-content-blocker, ublock
├── scripts/ # All build and utility scripts
│ ├── build/ # build.ts, build-config.ts, constants.js,
│ │ # custom_platforms.ts, patches.ts, strip-generated-meta.ts
│ ├── checksum/ # Checksum generation (index.ts)
│ ├── repository/ # compress.ts — repository compression
│ ├── translations/ # Locale download/upload tooling, PR validation reporting
│ ├── utils/ # Shared utilities (find_files.js, splitter.ts, strings.js)
│ ├── validation/ # validate_platforms.ts, validate_locales.ts
│ ├── wildcard-domain-processor/ # TS module with CLI, unit tests (__tests__/)
│ └── auto_build.sh # Automated build entry point
├── locales/ # Translations (45+ language dirs)
├── groups/ # Filter group definitions (metadata.json)
├── tags/ # Filter tag taxonomy (metadata.json)
├── temp/ # Temporary build artifacts
├── crowdin.yml # Crowdin project config: project 365951,
│ # folder `miscellaneous/filters-registry`
├── package.json # Project manifest and scripts
├── tsconfig.json # TypeScript config (ES2022, nodenext)
├── vitest.config.ts # Test runner config
├── optimization_config.json # Per-filter optimization parameters
├── eslint.config.js # ESLint flat config (typescript-eslint, import-x, stylistic)
├── .markdownlint.json # Markdownlint config
└── README.md # Project documentation
| Command | Description |
|---|---|
yarn build |
Build all filters (tsx scripts/build/build.ts) |
yarn build:local |
Build from cached filter.txt (build.ts --use-cache) |
yarn auto-build |
Full automated build via bash scripts/auto_build.sh |
yarn build:patches |
Build incremental patches |
yarn generate-cache |
Generate cached filter.txt from templates (tsx scripts/build/build.ts --generate-cache) |
yarn download-stats |
Download per-filter stats.json to temp/optimization/stats/ |
yarn strip-generated-meta <dir> |
Strip generated meta from .txt/filters.json/filters.js in <dir> in place |
yarn test |
Run unit tests (vitest run) |
yarn lint |
Run all linters (code + types + markdown) |
yarn lint:code |
ESLint check (eslint .) |
yarn lint:types |
TypeScript type check (tsc --noEmit) |
yarn lint:md |
Markdown lint (markdownlint **/*.md) |
yarn validate |
Validate platforms and locales |
yarn validate:platforms |
Validate platform build outputs |
yarn validate:locales |
Validate locale files |
yarn compare-build-output |
Build comparison vs. master. Requires interactive manual inputs; not for CI/automation |
yarn update-wildcard-domains |
Scan filters for wildcard domains |
yarn expand-wildcard-domains |
Expand wildcard domains in platform builds |
yarn compress |
Compress repository data |
yarn crowdin |
Run the Crowdin CLI (used by the translations download script) |
yarn build:local reuses temp/optimization/stats/ when it's non-empty and its
temp/optimization/stats.scope marker matches the current --include/--skip
selection (or was a full, unscoped download); otherwise stats are fetched from the
remote server. Plain yarn build always fetches remotely — the local cache is
only applied under --use-cache.
yarn download-stats and an applied stats cache take the set of optimizable filters from
optimization_config.json instead of the remote percent.json.
yarn download-stats stages fetches in a temp directory and swaps in the whole stats directory
only after every fetch succeeds. --include / --skip scope which filters are fetched,
so a scoped yarn download-stats drops every other filter's cached stats; --report,
--strip-generated-meta, and --no-patches-prepare error when combined with it.
See DEVELOPMENT.md — Command Compatibility for which flag combinations are allowed.
After completing any task that modifies code in scripts/:
-
Run the linter. Fix all errors before committing.
yarn lint
Important:
yarn lintruns three checks sequentially:yarn lint:code→yarn lint:types→yarn lint:md. A failure in any one stage aborts the rest. Always verify that all three pass, including Markdown lint on edited.mdfiles. -
Run unit tests. All tests must pass.
yarn test -
Update tests for changed code. Add or update tests in the
__tests__/directory alongside the code you changed:scripts/wildcard-domain-processor/__tests__/for wildcard-domain-processor changesscripts/build/__tests__/for build script changesscripts/repository/__tests__/for repository script changesscripts/translations/__tests__/for translation script changes
-
Validate platform outputs if filters, templates, or build scripts changed.
yarn validate
-
Verify Code Guidelines compliance. Review your changes against the Code Guidelines section below.
-
Do not modify generated files by hand. Files in
platforms/,filter.txt,diff.txt, andrevision.jsoninside filter directories are generated by the build pipeline. -
Do not modify third-party filter sources. Files under
filters/ThirdParty/are managed by the upstream filter lists workflow and should not be edited manually. -
Synchronise code, tests, and documentation for CLI changes. When adding or modifying command-line arguments, build flags, or their compatibility rules:
- Add or update tests in
scripts/build/__tests__/covering the new behaviour. - Update the Command Compatibility section in
DEVELOPMENT.md. - Never merge CLI changes without matching tests and documentation.
- Add or update tests in
-
Coordinate new filters with the MV3 browser extension. When adding a new filter (new
filterId), keep it out of the MV3 extension platforms (ext_chromium_mv3,ext_edge_mv3,ext_opera_mv3) viaplatformsExcludedin itsmetadata.jsonuntil@adguard/dnr-rulesetsincludes the new ruleset (tasks/validator-data.jsonand related files updated) and an MV3 extension release bundling it has shipped. See DEVELOPMENT.md — Adding a New Filter.
- Scripts live under
scripts/and are organized by function:build/,checksum/,repository/,translations/,utils/,validation/,wildcard-domain-processor/. - Filter data is stored under
filters/— one directory per filter with a consistent structure (template.txt,metadata.json,revision.json,filter.txt,diff.txt). - Platform outputs in
platforms/are generated artifacts, not source files. - The project uses ESM (
"type": "module"in package.json); useimport/export, notrequire.
General code style guidelines for JavaScript are available via link: https://github.com/AdguardTeam/CodeGuidelines/blob/master/JavaScript/Javascript.md.
- TypeScript: Strict mode enabled (
strict: truein tsconfig). Use proper types; avoidany. - Markdown: Follow
.markdownlint.json— dash-style unordered lists, asterisk emphasis, 120-char line limit. - All other style rules (indentation, line length, import rules, etc.)
are enforced by
eslint.config.js. Runyarn lintto check. See DEVELOPMENT.md — ESLint configuration for how that file relates to the project's pre-migration ESLint 8 configuration.
Before adding any runtime check, decide whether the data is trusted or external. Data the system itself produced — own build artifacts, generated JSON, config files the project owns — is trusted: JSON.parse + as cast and move on. If such data is malformed, the code SHOULD fail loudly; never silently skip, default, or return-early on an unexpected shape.
Genuinely external input (user input, network responses, third-party files) gets validated ONCE at the boundary — with a schema library (zod/valibot) when the project already has one — and the parsed type is trusted downstream. Do not re-validate past the boundary.
Never write hand-rolled type guards (isRecord, typeof x === 'object' && x !== null && !Array.isArray(x) chains).
A guard justified only by a hypothetical "what if the data is corrupt" is a smell:
answer whether that corruption is real for THIS data source before writing anything.
- Test framework: Vitest.
- Tests live alongside the code they cover in
__tests__/directories. - Test files use the
.test.tsextension. - Test fixtures go in
__tests__/resources/.
- Node.js version: >= 24. Do not use APIs unavailable in Node 24.
- Script execution: Use
tsxto run TypeScript scripts directly (do not pre-compile). - No
packageManagerfield: Do not add thepackageManagerfield topackage.json. The repository uses yarn 1 (classic), which ignores the field; it may confuse tooling that honours it (e.g. Corepack) and must not be introduced. - Filter syntax: AdGuard-specific rule syntax. Refer to AdGuard knowledge base for rule format documentation.