@adguard/dnr-converter

中文文档
2.0.0发布于 2026/09/29GPL-3.0-onlyTypeScriptESM + CJS近 30 天下载 2514

A converter that transforms adblock-style filtering rules into rules compatible with the Declarative Net Request (DNR) API.

原始使用文档
文档目录19 节
  1. Key concepts
  2. Supported rule types
  3. Installation
  4. Peer dependencies
  5. API overview
  6. IFilter
  7. Filter
  8. ConverterOptions
  9. Simple flow: FilterConverter + Ruleset
  10. Advanced flow: FilterConverter with withSourceMap: true + R…
  11. Compact ruleset file format
  12. MetadataRuleset
  13. isSafeRule(rule)
  14. DNR_CONVERTER_VERSION
  15. Network rule types
  16. CLI
  17. Commands
  18. Programmatic API
  19. Documentation

DNR Converter

npm-badge license-badge

A TypeScript library that converts adblock-style filtering rules into rules compatible with Chrome's Declarative Net Request (DNR) API. It is designed for developers building Manifest V3 browser extensions that need to translate existing AdGuard or other adblock filter lists into the DNR format required by modern Chrome extensions.

Key concepts

  • Filter — an adblock filter list represented as an object with an id and text content. Each filter list (e.g. AdGuard Base, EasyList) becomes one Filter input.
  • Declarative rule — a chrome.declarativeNetRequest.Rule object that the browser evaluates natively. The converter produces these from filter text.
  • Source map — a mapping from each generated declarative rule back to its originating filter and rule index, enabling reverse lookup when a declarative rule fires.
  • Rule safety — Chrome classifies DNR rules as safe (block, allow, allowAllRequests, upgradeScheme) or unsafe (redirect, modifyHeaders). The library exposes a helper to check safety.
  • Converter options — limits and paths that control conversion output (maximum rule counts, resource paths for redirects).

Supported rule types

The converter supports virtually all adblock network rule modifiers. The known MV3 limitations (inherent to the Declarative Net Request API) are:

Supported modifiers (with MV3 notes):

  • Basic blocking/allowing rules, $third-party, $domain (no regexps / .* TLDs), all content types ($script, $image, $stylesheet, etc.)
  • $important, $match-case, $method, $to, $denyallow, $header (no regex values), $all
  • $redirect — allowlist rules not supported
  • $csp, $removeparam, $removeheader, $permissions — allowlist rules not supported; rules with identical conditions are combined only within the same filter, not across filters
  • $urltransform — limited; complex transforms produce multiple declarative rules via pipeline stages
  • $cookie — limited; only supported without parameters (bare $cookie)
  • $badfilter — partial: does not handle $domain intersections correctly

Not yet supported / not convertible to DNR:

  • $popup, $redirect-rule, $referrerpolicy — not yet implemented
  • $replace, $jsonprune, $xmlprune, $hls, $network, DNS modifiers ($client, $dnsrewrite, $dnstype, $ctag) — not expressible in DNR; produce conversion errors and are skipped
  • Exception modifiers $jsinject, $stealth, $urlblock, $genericblock — not yet implemented
  • $webrtc — deprecated and not supported

For detailed conversion examples with output, see Conversion Examples.

Installation

# pnpm
pnpm install @adguard/dnr-converter

# yarn
yarn add @adguard/dnr-converter

# npm
npm install @adguard/dnr-converter

Peer dependencies

The package requires @adguard/re2-wasm (1.2.0) as a peer dependency for regex validation.

API overview

The library uses a single FilterConverter class with two conversion modes selected by the withSourceMap option:

Simple mode (default)Advanced mode (withSourceMap: true)
ConverterFilterConverterFilterConverter
RulesetRuleset (sync, in-memory)RulesetWithSourceMap (lazy-load, source map)
Input filterIFilterIFilter
Use caseWhen you only need DeclarativeRule[] output from plain filter textWhen you need source maps, $badfilter cross-filter application, and serialization

IFilter

import type { IFilter } from '@adguard/dnr-converter';

Single interface used by both conversion flows:

MethodTypeDescription
getId()numberUnique filter identifier
getContent()Promise<string>Returns the full text of the filter list
getRuleByIndex(index)Promise<string>Returns original rule text by character offset
unloadContent()voidReleases loaded content from memory

Filter

The single concrete IFilter implementation shipped by the library supports two construction modes:

  • Pre-loaded (new Filter(id, content: string)) — accepts the filter text directly as a string. getContent() resolves immediately; unloadContent() is a no-op. Use for simple conversion or when content is already in memory.

  • Lazy-loaded (new Filter(id, source: () => Promise<string>)) — accepts an async callback that fetches the content on demand. Supports promise deduplication across concurrent callers, and unloadContent() releases the cached content. Use with withSourceMap: true (advanced mode).

import { Filter } from '@adguard/dnr-converter';

// Pre-loaded (simple mode)
const filter = new Filter(1, '||example.com^');

// Lazy-loaded (advanced mode with withSourceMap: true)
const lazyFilter = new Filter(1, async () => fetchFilterText());

ConverterOptions

import type { ConverterOptions } from '@adguard/dnr-converter';

Configuration for the conversion process:

PropertyTypeDescription
resourcesPathstring?Path to web-accessible resources relative to the extension root (starts with /, no trailing /). Required for $redirect rules.
maxNumberOfRulesnumber?Maximum total declarative rules to produce. Excess rules are trimmed.
maxNumberOfUnsafeRulesnumber?Maximum unsafe (dynamic) rules allowed.
maxNumberOfRegexpRulesnumber?Maximum rules using regexFilter.
combineboolean?Merge all input filters into a single combined rule set.
withSourceMapboolean?When true, returns RulesetWithSourceMap instead of Ruleset. Enables source maps, $badfilter cross-filter support, and lazy loading.
badFilterRulesRule[]?Static $badfilter rules to apply at scan time (withSourceMap: true only).

Simple flow: FilterConverter + Ruleset

Use this flow when you only need DeclarativeRule[] output from plain filter text, without source maps or lazy loading.

import { FilterConverter, Filter } from '@adguard/dnr-converter';

const filter = new Filter(1, '||example.com^\n@@||example.com/path^');

const converter = new FilterConverter();
const [{ ruleset, errors, limitations }] = await converter.convert([filter]);

console.log(ruleset.getDeclarativeRules()); // DeclarativeRule[]
console.log(ruleset.getSafeRulesCount());    // 2
console.log(errors.length);                 // 0

Combine multiple filters into one ruleset:

const [{ ruleset }] = await converter.convert(
    [filter1, filter2],
    { combine: true },
);
// ruleset.getId() === FilterConverter.COMBINED_RULESET_ID

Serialize and restore:

const json = ruleset.serialize();   // JSON string of DeclarativeRule[]

const { Ruleset } = await import('@adguard/dnr-converter');
const restored = Ruleset.deserialize(ruleset.getId(), json);

Ruleset (returned by default, without withSourceMap) implements IRuleset:

MethodReturnsDescription
getId()stringRule set identifier (e.g. "ruleset_1")
getSafeRulesCount()numberCount of safe declarative rules (excludes unsafe)
getUnsafeRulesCount()numberCount of unsafe rules
getRegexpRulesCount()numberCount of regexp-based rules
getDeclarativeRules()DeclarativeRule[]All converted DNR rules (synchronous)
serialize()stringJSON serialization of declarative rules
Ruleset.deserialize(id, json)RulesetStatic: reconstruct from serialized JSON

Advanced flow: FilterConverter with withSourceMap: true + RulesetWithSourceMap

Use this mode in browser extension internals when you need source maps, $badfilter cross-filter application, serialization with hash maps, and lazy content loading. Pass withSourceMap: true in the options to get RulesetWithSourceMap results.

import { Filter, FilterConverter } from '@adguard/dnr-converter';

const filter = new Filter(1, async () => '||example.com^\n@@||example.com/path^');

const converter = new FilterConverter();
const [{ ruleset, errors }] = await converter.convert([filter], { withSourceMap: true });

const declarativeRules = await ruleset.getDeclarativeRules(); // Promise<DeclarativeRule[]>
const sources = await ruleset.getRulesById(declarativeRules[0].id);
console.log(sources[0].sourceRule); // '||example.com^'

Apply $badfilter from dynamic filters against static rule sets:

const [{ ruleset: dynamicRuleset }] = await converter.convert(
    [dynamicFilter],
    { withSourceMap: true },
);
const rulesToDisable = await converter.computeRulesToDisable(
    [dynamicRuleset],
    [staticRuleset],
);
// rulesToDisable: UpdateStaticRulesOptions[]

RulesetWithSourceMap (returned when withSourceMap: true) implements IRulesetWithSourceMap:

MethodReturnsDescription
getId()stringRule set identifier
getSafeRulesCount()numberCount of safe declarative rules (excludes unsafe)
getUnsafeRulesCount()numberCount of unsafe rules
getRegexpRulesCount()numberCount of regexp-based rules
getDeclarativeRules()Promise<DeclarativeRule[]>All converted DNR rules (lazy)
getUnsafeRules()Promise<DeclarativeRule[]>Unsafe rules subset (lazy)
getRulesById(id)Promise<SourceRuleAndFilterId[]>Source rules for a DNR rule
getBadFilterRules()NetworkRule[]$badfilter rules in this set
getRulesHashMap()IRulesHashMapHash map for fast $badfilter matching
getMetadataRulesCount()numberMetadata rules in the ruleset file the rule set was read from (0 if not read from a file)
serializeCompact(unsafeRules, prettyPrint?)Promise<string>Ruleset file in the compact ruleset file format
unloadContent()voidRelease lazy-loaded content
RulesetWithSourceMap.fromCompact(id, content, filters)RulesetWithSourceMapBuild a rule set from parseCompactRuleset() output
RulesetWithSourceMap.fromDeserialized(deserialized, metadataRulesCount)RulesetWithSourceMapBuild a rule set from RulesetWithSourceMap.deserialize() output
import type { ConversionResult } from '@adguard/dnr-converter';

Result returned by converter methods:

PropertyTypeDescription
rulesetIRuleset / IRulesetWithSourceMapThe converted rule set
errors(ConversionError | Error)[]Rules that could not be converted
limitationsLimitationError[]Warnings about exceeded limits
declarativeRulesToCancelUpdateStaticRulesOptions[]?Static rule IDs to disable (from computeRulesToDisable)

Compact ruleset file format

serializeCompact() and MetadataRuleset.serialize() write a JSON array of DNR rules. The array starts with one or more metadata rules: block rules whose condition.urlFilter is dummy.rule.adguard.com (they never match a request) and whose metadata value is { "chunk": "<fragment>" }. Joining the fragments in array order gives the serialized metadata. Every metadata value, including JSON escaping and the wrapper, is at most 65,536 UTF-8 bytes: Edge Add-ons rejects packages with larger values. The ordinary declarative rules follow the metadata rules.

A metadata rule is recognized by the marker urlFilter together with the metadata key. A converted rule for the host dummy.rule.adguard.com without a metadata key is an ordinary rule.

Metadata rules are static DNR rules: they count towards the static rule quota and get the smallest IDs not used by any other rule of the ruleset. Ordinary rule IDs are never changed.

[
    { "id": 1, "action": { "type": "block" }, "condition": { "urlFilter": "dummy.rule.adguard.com", "resourceTypes": ["xmlhttprequest"] }, "metadata": { "chunk": "{\"metadata\":{...},\"lazyMetadata\":{...},\"filterCon" } },
    { "id": 2, "action": { "type": "block" }, "condition": { "urlFilter": "dummy.rule.adguard.com", "resourceTypes": ["xmlhttprequest"] }, "metadata": { "chunk": "tent\":\"||example.com^\"}" } },
    { "id": 1234567, "action": { "type": "block" }, "condition": { "urlFilter": "||example.com^" } }
]

Read a filter ruleset file with parseCompactRuleset():

import { Filter, parseCompactRuleset, RulesetWithSourceMap } from '@adguard/dnr-converter';

const content = parseCompactRuleset('ruleset_1', JSON.parse(fileText));
content.metadata;           // SerializedRulesetData
content.lazyMetadata;       // SerializedRulesetLazyData
content.filterContent;      // original filter text
content.declarativeRules;   // ordinary DNR rules, without the metadata rules
content.metadataRulesCount; // number of metadata rules, for static rule quota accounting

// Build the rule set directly from the parsed content.
const ruleset = RulesetWithSourceMap.fromCompact('ruleset_1', content, [new Filter(1, content.filterContent)]);

fromCompact() validates metadata at once and the lazy metadata and ordinary rules on first load, with the same schemas as RulesetWithSourceMap.deserialize(). The rule set keeps the parsed content in memory for its whole lifetime; unloadContent() does not release it.

When the parts are stored as strings, for example in IndexedDB, load them lazily with deserialize() and build the rule set with fromDeserialized(), passing the stored metadataRulesCount:

const ruleset = RulesetWithSourceMap.fromDeserialized(
    await RulesetWithSourceMap.deserialize(id, rawData, loadLazyData, loadDeclarativeRules, filters),
    metadataRulesCount,
);

The reader takes only the leading run of metadata rules, joins them and parses the result once. It throws InvalidMetadataChunksError (with a rulesetId property) when the input is not an array, has no leading metadata rule, has a metadata rule without a string chunk, or when the joined fragments are not valid JSON.

MetadataRuleset

import { MetadataRuleset, METADATA_RULESET_ID } from '@adguard/dnr-converter';

A specialized ruleset that stores checksums and additional properties for a collection of DNR rule sets. It serializes in the compact ruleset file format: a JSON array that contains only metadata rules carrying { checksums, additionalProperties } in metadata.chunk fragments.

METADATA_RULESET_ID is the constant 0; the ruleset's string ID is always "ruleset_0".

Checksum methods:

MethodReturnsDescription
getId()stringAlways "ruleset_0"
setChecksum(rulesetId, checksum)voidStore checksum for a rule set
getChecksum(rulesetId)string | undefinedRetrieve checksum, or undefined if not set
getRulesetIds()string[]All rule set IDs that have checksums

Additional-property methods:

MethodReturnsDescription
setAdditionalProperty(key, value)voidStore an arbitrary JSON-serializable property
getAdditionalProperty(key)unknown | undefinedRetrieve a property value
hasAdditionalProperty(key)booleanCheck whether a property exists
removeAdditionalProperty(key)voidRemove a property (no-op if missing)

Serialization:

MethodReturnsDescription
serialize(pretty?)stringJSON string; pretty=true for human-readable output
MetadataRuleset.deserialize(json)MetadataRulesetReconstruct from a serialized string; throws SyntaxError on invalid JSON, InvalidMetadataChunksError on malformed metadata rules, a validation error on a bad payload
const meta = new MetadataRuleset();
meta.setChecksum('ruleset_1', 'abc123');
meta.setAdditionalProperty('version', '2.0');

const json = meta.serialize();
const restored = MetadataRuleset.deserialize(json);

console.log(restored.getChecksum('ruleset_1')); // "abc123"
console.log(restored.getAdditionalProperty('version')); // "2.0"

isSafeRule(rule)

import { isSafeRule } from '@adguard/dnr-converter';

// DeclarativeRule from the DNR API
const rule = {
    id: 2,
    priority: 1,
    action: { type: 'block' },
    condition: { urlFilter: 'example.com' },
};

isSafeRule(rule); // true — "block" is a safe action

Returns true if the declarative rule's action is one of the safe rule actions (block, allow, allowAllRequests, upgradeScheme). Useful for separating safe static rules from unsafe dynamic rules that require additional review.

DNR_CONVERTER_VERSION

import { DNR_CONVERTER_VERSION } from '@adguard/dnr-converter';

console.log(DNR_CONVERTER_VERSION); // e.g. "0.0.1"

A string constant with the current library version.

Network rule types

The package exports types related to network rule parsing and validation:

import {
    Rule,
    HttpHeaderMatcher,
    RuleDeclarativeValidator,
} from '@adguard/dnr-converter';
  • Rule — parsed network rule with accessors for domains, resource types, methods, advanced modifier values, priority, and option flags. Create instances via Rule.createFromText(filterListId, index, text).
  • HttpHeaderMatcher — type describing the parsed $header modifier value.
  • RuleDeclarativeValidator — static helper that checks whether a Rule can be converted to a DNR rule. Call RuleDeclarativeValidator.shouldConvertRule(rule) — returns true if the rule is convertible, false if it should be silently skipped, or throws UnsupportedModifierError if the rule uses an unsupported modifier.

CLI

The package ships a dnr-converter command-line tool for converting filter lists to DNR rulesets and extracting filter content back from compiled rulesets.

Commands

convert

Converts AdGuard filter lists from a metadata directory into DNR rulesets.

dnr-converter convert \
  ./filters \
  ./resources \
  ./dist/rulesets

Arguments:

ArgumentDescriptionDefault
<filters_and_metadata_dir>Directory containing filters.json and filter list files(required)
<resources_dir>Directory with redirect resources(required)
[dest_rule_sets_dir]Output directory for generated rulesets./build/rulesets

Options:

OptionDescriptionDefault
--debugEnable debug loggingfalse
--prettify-json <bool>Pretty-print JSON output filestrue
--additional-properties <json>Additional properties to include in the metadata ruleset as JSON string{}

The filters.json metadata file must be an array of filter metadata objects each with at minimum filterId and name fields.

extract-filters

Extracts original filter list content that was embedded in compiled DNR rulesets by the convert command.

dnr-converter extract-filters \
  ./dist/rulesets \
  ./extracted-filters

Arguments:

ArgumentDescription
<path-to-rulesets>Directory containing compiled ruleset files
<path-to-output>Directory to write extracted filter files

Programmatic API

The CLI logic is also available as a programmatic API via the @adguard/dnr-converter/cli subpath export:

import { convertFilters, generateMD5Hash, type ConvertFiltersOptions } from '@adguard/dnr-converter/cli';

await convertFilters(
    './filters',
    './resources',
    './dist/rulesets',
    { debug: true, prettifyJson: true },
);

Documentation

版本与发布记录

@adguard/dnr-converter 共发布 6 个版本,首次发布于 2026/07/15;过去 12 个月发布了 6 个版本。

版本发布日期距今
2.0.0latest2026/09/291 个月前
2.0.0-beta.0beta2026/09/291 个月前
1.1.22026/08/252 个月前
1.1.12026/08/242 个月前
1.1.02026/07/293 个月前
1.0.0-alpha.22026/07/153 个月前

关于 @adguard/dnr-converter 的常见问题

@adguard/dnr-converter 是什么?

A converter that transforms adblock-style filtering rules into rules compatible with the Declarative Net Request (DNR) API.

如何安装 @adguard/dnr-converter?

在项目目录运行 npm install @adguard/dnr-converter,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:@adguard/re2-wasm(1.2.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。它还提供命令行工具 dnr-converter,可以用 npx dnr-converter 直接运行。

@adguard/dnr-converter 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@adguard/dnr-converter 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@adguard/dnr-converter 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@adguard/dnr-converter 可以商用吗?

@adguard/dnr-converter 使用 GPL-3.0-only 许可。强 Copyleft:分发包含该代码的软件时,整个作品需以 GPL-3.0 兼容许可开源,并附带专利与反锁定条款。仅内部使用不触发此义务。以上为概括,不构成法律意见,请以许可原文为准。

@adguard/dnr-converter 的下载量有多少?

根据 npm 官方下载统计,@adguard/dnr-converter 在 2026/10/05 – 2026/11/03 这 30 天内被下载 2,514 次,日均约 84 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@adguard/dnr-converter 还在维护吗?

最新版本 2.0.0 发布于 2026/09/29(1 个月前);至今共发布 6 个版本,其中过去 12 个月发布了 6 个;首次发布于 2026/07/15。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。