diff --git a/.gitignore b/.gitignore index 4175924..0f59a68 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ node_modules dist - +test-results diff --git a/.tool-versions b/.tool-versions index 431bb17..fc3ac29 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -bun 1.0.1 +bun 1.1.17 diff --git a/bun.lockb b/bun.lockb index da0d06c..296ec18 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 6fc6cb6..55633bb 100644 --- a/package.json +++ b/package.json @@ -11,11 +11,12 @@ "test.e2e": "playwright test test/e2e/index.spec.js" }, "dependencies": { - "@adguard/tsurlfilter": "2.2.15", + "@adguard/scriptlets": "^1.11.16", + "@adguard/tsurlfilter": "2.2.23", "@eyeo/webext-ad-filtering-solution": "1.5.0" }, "devDependencies": { - "@ghostery/trackerdb": "^1.0.36", - "@playwright/test": "^1.38.0" + "@ghostery/trackerdb": "^1.0.208", + "@playwright/test": "^1.45.3" } } diff --git a/src/converters/helpers.js b/src/converters/helpers.js index 85f0b4e..a5eb8dd 100644 --- a/src/converters/helpers.js +++ b/src/converters/helpers.js @@ -1,5 +1,57 @@ +import * as path from 'node:path' +import redirects from '@adguard/scriptlets/dist/redirects.json' with { type: 'json' } + +export function generateResourcesMapping() { + const resourcesMapping = new Map(); + const allowedResourceExtensions = [ + 'html', + 'js', + 'css', + 'mp4', + 'mp3', + 'xml', + 'txt', + 'json', + 'empty', + ]; + + function getPreferredResource(aliases) { + // ignore non-supported files and manually created uBO aliases by AdGuard + return aliases.find(alias => { + const extension = alias.split('.').pop(); + return allowedResourceExtensions.includes(extension) && !alias.startsWith('ubo-'); + }); + } + + for (const redirect of redirects) { + // Skip, in case of AdGuard-only resource + if (redirect.aliases === undefined) { + continue; + } + + const preferredResourceName = getPreferredResource(redirect.aliases); + + // Skip, in case of safe redirect resource name that's safe to use wasn't found + if (preferredResourceName === undefined) { + continue; + } + + // Register to mapping + resourcesMapping.set(redirect.title, preferredResourceName); + for (const alias of redirect.aliases) { + if (alias !== preferredResourceName) { + resourcesMapping.set(alias, preferredResourceName); + } + } + } + + return resourcesMapping; +} + export const DEFAULT_PARAM_MAPPING = { '3p': 'third-party', + 'xhr': 'xmlhttprequest', + 'frame': 'subdocument' }; export function normalizeFilter(filter, { mapping = DEFAULT_PARAM_MAPPING } = {}) { @@ -30,7 +82,9 @@ export function normalizeFilter(filter, { mapping = DEFAULT_PARAM_MAPPING } = {} return `${front}$${params.join(',')}`; } -export function normalizeRule(rule) { +export const DEFAULT_RESOURCE_MAPPING = generateResourcesMapping(); + +export function normalizeRule(rule, { resourcesMapping = DEFAULT_RESOURCE_MAPPING } = {}) { if (!rule) { return; } @@ -67,5 +121,15 @@ export function normalizeRule(rule) { delete newRule.condition.domains; } + if (newRule.action && newRule.action.type === 'redirect') { + const filename = path.basename(newRule.action.redirect.extensionPath); + const preferredFilename = resourcesMapping.get(filename); + + if (preferredFilename !== undefined) { + newRule.action.redirect.extensionPath = + path.dirname(newRule.action.redirect.extensionPath) + '/' + preferredFilename; + } + } + return newRule; } diff --git a/test/unit/converters/helpers.spec.js b/test/unit/converters/helpers.spec.js index b87fe34..4ec0309 100644 --- a/test/unit/converters/helpers.spec.js +++ b/test/unit/converters/helpers.spec.js @@ -1,6 +1,6 @@ import { describe, it, expect } from "bun:test"; -import { normalizeFilter, normalizeRule } from "../../../src/converters/helpers.js"; +import { generateResourcesMapping, normalizeFilter, normalizeRule } from "../../../src/converters/helpers.js"; describe("normalizeFilter", () => { it("format params", () => { @@ -121,4 +121,39 @@ describe('normalizeRule', () => { }, }); }); + + it('replaces extensionPath respecting existing dirname', () => { + expect(normalizeRule({ + action: { + type: 'redirect', + redirect: { + extensionPath: '/rule_resources/redirects/alias', + }, + }, + }, { + resourcesMapping: new Map([ + [ + 'alias', + 'test.js', + ], + ]), + })).toEqual({ + action: { + type: 'redirect', + redirect: { + extensionPath: '/rule_resources/redirects/test.js', + }, + }, + }); + }); +}); + +describe('generateResourcesMapping', () => { + it('filters resources without file extension', () => { + const mapping = generateResourcesMapping(); + + for (const destination of mapping.values()) { + expect(destination.match(/\w+\.\w+|empty/)).not.toBe(null); + } + }); }); diff --git a/test/unit/helpers.js b/test/unit/helpers.js index 55869a8..81b6f42 100644 --- a/test/unit/helpers.js +++ b/test/unit/helpers.js @@ -7,6 +7,9 @@ function normalize(rule) { if (!rule) { return undefined; } + if (rule.condition?.resourceTypes !== undefined) { + rule.condition.resourceTypes = rule.condition.resourceTypes.sort(); + } delete rule.priority; delete rule.id; return rule; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..6344649 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + // https://bun.sh/docs/runtime/modules#path-re-mapping + "paths": { + "@adguard/scriptlets/dist/redirects.json": ["./node_modules/@adguard/scriptlets/dist/redirects.json"] + } + } +}