diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3d8189b..88f5457 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -25,3 +25,6 @@ jobs: - name: Run e2e tests run: bun run test.e2e + + - name: Run lint + run: bun run lint diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..305d48b --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,10 @@ +{ + "printWidth": 99, + "semi": true, + "useTabs": false, + "singleQuote": true, + "trailingComma": "all", + "bracketSpacing": true, + "arrowParens": "always", + "quoteProps": "preserve" +} diff --git a/bun.lockb b/bun.lockb index e3eaef1..daf3599 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..cde726d --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,24 @@ +import globals from 'globals'; +import pluginJs from '@eslint/js'; +import pluginPrettier from 'eslint-plugin-prettier/recommended'; + +export default [ + { + files: ['**/*.js'], + languageOptions: { + globals: { + ...globals.node, + ...globals.browser, + Bun: 'readonly', + }, + parserOptions: { + ecmaVersion: 'latest', + }, + }, + }, + pluginJs.configs.recommended, + pluginPrettier, + { + ignores: ['node_modules/*', 'dist/*'], + }, +]; diff --git a/package.json b/package.json index 530b9ce..6af8e38 100644 --- a/package.json +++ b/package.json @@ -4,11 +4,13 @@ "author": "Ghostery GmbH.", "description": "Converts urlfilters to DNR format", "license": "GPL-3.0", + "type": "module", "scripts": { "build": "bun ./scripts/build.js", "serve": "bun --watch scripts/serve.js", "test.unit": "bun test test/unit", - "test.e2e": "playwright test test/e2e/index.spec.js" + "test.e2e": "playwright test test/e2e/index.spec.js", + "lint": "eslint" }, "dependencies": { "@adguard/scriptlets": "^1.12.1", @@ -16,7 +18,14 @@ "@eyeo/webext-ad-filtering-solution": "1.5.0" }, "devDependencies": { + "@eslint/js": "^9.13.0", "@ghostery/trackerdb": "^1.0.208", - "@playwright/test": "^1.45.3" + "@playwright/test": "^1.45.3", + "@types/bun": "^1.1.11", + "eslint": "^9.13.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.2.1", + "globals": "^15.11.0", + "prettier": "^3.3.3" } } diff --git a/scripts/build.js b/scripts/build.js index ed4aefd..6c75e58 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -1,4 +1,4 @@ -import build from "./helpers/build.js"; +import build from './helpers/build.js'; const startAt = Date.now(); diff --git a/scripts/helpers/build.js b/scripts/helpers/build.js index d44f3ef..414ae18 100644 --- a/scripts/helpers/build.js +++ b/scripts/helpers/build.js @@ -1,26 +1,23 @@ -import fs from "node:fs"; -import path from "node:path"; +import fs from 'node:fs'; +import path from 'node:path'; -import { SOURCE_PATH, DIST_PATH } from "./paths.js"; +import { SOURCE_PATH, DIST_PATH } from './paths.js'; export default async function build({ debug = false } = {}) { fs.rmSync(DIST_PATH, { recursive: true, force: true }); fs.mkdirSync(DIST_PATH); - fs.copyFileSync( - path.join(SOURCE_PATH, "index.html"), - path.join(DIST_PATH, "index.html") - ); + fs.copyFileSync(path.join(SOURCE_PATH, 'index.html'), path.join(DIST_PATH, 'index.html')); const result = await Bun.build({ - entrypoints: [path.join(SOURCE_PATH, "index.js")], + entrypoints: [path.join(SOURCE_PATH, 'index.js')], outdir: DIST_PATH, - target: "browser", + target: 'browser', minify: !debug, - sourcemap: debug ? "inline" : "external", + sourcemap: debug ? 'inline' : 'external', }); if (!result.success) { - console.error("Build failed"); + console.error('Build failed'); for (const message of result.logs) { // Bun will pretty print the message object console.error(message); @@ -28,4 +25,3 @@ export default async function build({ debug = false } = {}) { throw new Error(result.logs.join('\n')); } } - diff --git a/scripts/helpers/paths.js b/scripts/helpers/paths.js index 11ed7ec..4683a20 100644 --- a/scripts/helpers/paths.js +++ b/scripts/helpers/paths.js @@ -1,5 +1,5 @@ -import path from "node:path"; +import path from 'node:path'; -export const ROOT_PATH = path.join(import.meta.dir, "..", ".."); -export const DIST_PATH = path.join(ROOT_PATH, "dist"); -export const SOURCE_PATH = path.join(ROOT_PATH, "src"); +export const ROOT_PATH = path.join(import.meta.dir, '..', '..'); +export const DIST_PATH = path.join(ROOT_PATH, 'dist'); +export const SOURCE_PATH = path.join(ROOT_PATH, 'src'); diff --git a/scripts/serve.js b/scripts/serve.js index 5c8e8d6..5d28bab 100644 --- a/scripts/serve.js +++ b/scripts/serve.js @@ -1,33 +1,30 @@ -import { watch } from "node:fs"; -import path from "node:path"; +import { watch } from 'node:fs'; +import path from 'node:path'; -import build from "./helpers/build.js"; -import { SOURCE_PATH, DIST_PATH } from "./helpers/paths.js"; +import build from './helpers/build.js'; +import { SOURCE_PATH, DIST_PATH } from './helpers/paths.js'; const PORT = 3000; await build(); -const watcher = watch( - SOURCE_PATH, - { recursive: true }, - async (event, filename) => { - console.log(`Detected ${event} in ${filename}`); - try { - await build({ debug: true }); - } catch (e) { - // no need to do anything as build logs errors already - } +const watcher = watch(SOURCE_PATH, { recursive: true }, async (event, filename) => { + console.log(`Detected ${event} in ${filename}`); + try { + await build({ debug: true }); + // eslint-disable-next-line no-unused-vars + } catch (e) { + // no need to do anything as build logs errors already } -); +}); Bun.serve({ port: PORT, development: true, async fetch(req) { let filePath = new URL(req.url).pathname; - if (filePath === "/") { - filePath += "index.html"; + if (filePath === '/') { + filePath += 'index.html'; } const file = Bun.file(path.join(DIST_PATH, filePath)); return new Response(file); @@ -39,9 +36,9 @@ Bun.serve({ console.log(`Starting dev server at port: ${PORT}`); -process.on("SIGINT", () => { +process.on('SIGINT', () => { // close watcher when Ctrl-C is pressed - console.log("Closing server..."); + console.log('Closing server...'); watcher.close(); process.exit(0); diff --git a/scripts/update.js b/scripts/update.js index 6862340..72d9af4 100644 --- a/scripts/update.js +++ b/scripts/update.js @@ -1,13 +1,13 @@ -import { writeFileSync } from "node:fs"; -import { join, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; +import { writeFileSync } from 'node:fs'; +import { join, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; -import adguardDialects from "@adguard/scriptlets/dist/redirects.json" with { type: "json" }; +import adguardDialects from '@adguard/scriptlets/dist/redirects.json'; const CWD = dirname(fileURLToPath(import.meta.url)); async function downloadResource(resourceName) { - console.log("Downloading resources..."); + console.log('Downloading resources...'); const { revisions } = await fetch( `https://cdn.ghostery.com/adblocker/resources/${resourceName}/metadata.json`, @@ -24,22 +24,17 @@ async function downloadResource(resourceName) { `https://cdn.ghostery.com/adblocker/resources/${resourceName}/${latestRevision}/list.txt`, ).then((result) => { if (!result.ok) { - throw new Error( - `Failed to fetch ${resourceName}: ${result.status}: ${result.statusText}`, - ); + throw new Error(`Failed to fetch ${resourceName}: ${result.status}: ${result.statusText}`); } return result.text(); }); } function extractRedirects(data) { - console.log("Extracting resources..."); + console.log('Extracting resources...'); const resources = JSON.parse(data); - const mappings = resources.redirects.map((redirect) => [ - redirect.name, - redirect.aliases ?? [], - ]); + const mappings = resources.redirects.map((redirect) => [redirect.name, redirect.aliases ?? []]); // Integrate adguard mappings for (const dialect of adguardDialects) { @@ -77,7 +72,7 @@ function extractRedirects(data) { } writeFileSync( - join(CWD, "..", "src", "mappings.json"), - extractRedirects(await downloadResource("ublock-resources-json")), - "utf-8", + join(CWD, '..', 'src', 'mappings.json'), + extractRedirects(await downloadResource('ublock-resources-json')), + 'utf-8', ); diff --git a/src/converters/abp.js b/src/converters/abp.js index 141489d..76bf356 100644 --- a/src/converters/abp.js +++ b/src/converters/abp.js @@ -1,6 +1,9 @@ -import { FilterParsingError, normalize } from "@eyeo/webext-ad-filtering-solution/adblockpluscore/lib/filters/index.js"; -import { createConverter } from "@eyeo/webext-ad-filtering-solution/adblockpluscore/lib/dnr/index.js"; -import { normalizeFilter, normalizeRule, DEFAULT_PARAM_MAPPING } from "./helpers"; +import { + FilterParsingError, + normalize, +} from '@eyeo/webext-ad-filtering-solution/adblockpluscore/lib/filters/index.js'; +import { createConverter } from '@eyeo/webext-ad-filtering-solution/adblockpluscore/lib/dnr/index.js'; +import { normalizeFilter, normalizeRule, DEFAULT_PARAM_MAPPING } from './helpers'; const PARAM_MAPPING = { ...DEFAULT_PARAM_MAPPING, @@ -27,7 +30,7 @@ export default async function convert(filters) { rules.push(rule); } } else { - throw new Error("Unknown problem"); + throw new Error('Unknown problem'); } } catch (e) { errors.push(`Error: "${e.message}" in rule: "${filter}"`); diff --git a/src/converters/adguard.js b/src/converters/adguard.js index 5b24224..1733e5f 100644 --- a/src/converters/adguard.js +++ b/src/converters/adguard.js @@ -1,16 +1,10 @@ -import { DeclarativeFilterConverter, Filter } from "@adguard/tsurlfilter/es/declarative-converter"; -import { normalizeFilter, normalizeRule } from "./helpers.js"; +import { DeclarativeFilterConverter, Filter } from '@adguard/tsurlfilter/es/declarative-converter'; +import { normalizeFilter, normalizeRule } from './helpers.js'; const converter = new DeclarativeFilterConverter(); -const createFilter = ( - rules, - filterId = 0, -) => { - return new Filter( - filterId, - { getContent: async () => rules }, - ); +const createFilter = (rules, filterId = 0) => { + return new Filter(filterId, { getContent: async () => rules }); }; export default async function convert(rules, { resourcesPath } = {}) { @@ -19,7 +13,7 @@ export default async function convert(rules, { resourcesPath } = {}) { const declarativeRules = await conversionResult.ruleSet.getDeclarativeRules(); return { - rules: declarativeRules.map(rule => normalizeRule(rule)), + rules: declarativeRules.map((rule) => normalizeRule(rule)), errors: conversionResult.errors, limitations: conversionResult.limitations, }; diff --git a/src/converters/helpers.js b/src/converters/helpers.js index 9f8b459..82b8429 100644 --- a/src/converters/helpers.js +++ b/src/converters/helpers.js @@ -1,7 +1,7 @@ -import mappings from "../mappings.json" with { type: "json" }; +import mappings from '../mappings.json'; function getPathBasename(path) { - const lastIndex = path.lastIndexOf("/"); + const lastIndex = path.lastIndexOf('/'); if (lastIndex === -1) { return path; } @@ -19,24 +19,21 @@ export function generateResourcesMapping() { } export const DEFAULT_PARAM_MAPPING = { - "3p": "third-party", - xhr: "xmlhttprequest", - frame: "subdocument", + '3p': 'third-party', + xhr: 'xmlhttprequest', + frame: 'subdocument', }; export const DEFAULT_RESOURCES_MAPPING = generateResourcesMapping(); export function normalizeFilter( filter, - { - mapping = DEFAULT_PARAM_MAPPING, - resourcesMapping = DEFAULT_RESOURCES_MAPPING, - } = {}, + { mapping = DEFAULT_PARAM_MAPPING, resourcesMapping = DEFAULT_RESOURCES_MAPPING } = {}, ) { - let [front, ...back] = filter.split("$"); - let params = back.join(",").split(","); + let [front, ...back] = filter.split('$'); + let params = back.join(',').split(','); params.forEach((param, index) => { - const [key, value] = param.split("="); + const [key, value] = param.split('='); const alias = mapping[key]; if (alias) { params[index] = value ? `${alias}=${value}` : alias; @@ -48,29 +45,27 @@ export function normalizeFilter( }); // by default easylist syntax is case-insensitve - if (!params.find((p) => p === "match-case")) { + if (!params.find((p) => p === 'match-case')) { front = front.toLowerCase(); } // adguard converter doesn't work with $redirect with slash value // replace possible $redirect params including a slash - const indexOfRedirect = params.findIndex( - (p) => p.startsWith("redirect=") && p.includes("/"), - ); + const indexOfRedirect = params.findIndex((p) => p.startsWith('redirect=') && p.includes('/')); if (indexOfRedirect !== -1) { const name = resourcesMapping.get(params[indexOfRedirect].slice(9)); if (name !== undefined) { - params[indexOfRedirect] = "redirect=" + name; + params[indexOfRedirect] = 'redirect=' + name; } } const indexOfRedirectRule = params.findIndex( - (p) => p.startsWith("redirect-rule=") && p.includes("/"), + (p) => p.startsWith('redirect-rule=') && p.includes('/'), ); if (indexOfRedirectRule !== -1) { const name = resourcesMapping.get(params[indexOfRedirectRule].slice(14)); if (name !== undefined) { - params[indexOfRedirectRule] = "redirect-rule=" + name; + params[indexOfRedirectRule] = 'redirect-rule=' + name; } } @@ -78,20 +73,17 @@ export function normalizeFilter( return front; } - return `${front}$${params.join(",")}`; + return `${front}$${params.join(',')}`; } -export function normalizeRule( - rule, - { resourcesMapping = DEFAULT_RESOURCES_MAPPING } = {}, -) { +export function normalizeRule(rule, { resourcesMapping = DEFAULT_RESOURCES_MAPPING } = {}) { if (!rule) { return; } const newRule = structuredClone(rule); if (newRule.condition && newRule.condition.urlFilter) { - if (newRule.condition.urlFilter.endsWith("*")) { + if (newRule.condition.urlFilter.endsWith('*')) { newRule.condition.urlFilter = newRule.condition.urlFilter.slice(0, -1); } if (newRule.condition.isUrlFilterCaseSensitive === undefined) { @@ -102,15 +94,14 @@ export function normalizeRule( if ( newRule.condition && newRule.condition.regexFilter && - newRule.condition.regexFilter.startsWith("/") && - newRule.condition.regexFilter.endsWith("/") + newRule.condition.regexFilter.startsWith('/') && + newRule.condition.regexFilter.endsWith('/') ) { - newRule.condition.regexFilter = newRule.condition.regexFilter.slice(1,-1) + newRule.condition.regexFilter = newRule.condition.regexFilter.slice(1, -1); } if (newRule.condition && newRule.condition.excludedDomains) { - newRule.condition.excludedInitiatorDomains = - newRule.condition.excludedDomains; + newRule.condition.excludedInitiatorDomains = newRule.condition.excludedDomains; delete newRule.condition.excludedDomains; } @@ -119,17 +110,16 @@ export function normalizeRule( delete newRule.condition.domains; } - if (newRule.action && newRule.action.type === "redirect") { + if (newRule.action && newRule.action.type === 'redirect') { const filename = getPathBasename(newRule.action.redirect.extensionPath); const preferredFilename = resourcesMapping.get(filename) ?? // try searching without an extension // adguard converter attaches an file extension at the end - resourcesMapping.get(filename.slice(0, filename.lastIndexOf("."))); + resourcesMapping.get(filename.slice(0, filename.lastIndexOf('.'))); if (preferredFilename !== undefined) { newRule.action.redirect.extensionPath = - newRule.action.redirect.extensionPath.slice(0, -filename.length) + - preferredFilename; + newRule.action.redirect.extensionPath.slice(0, -filename.length) + preferredFilename; } } diff --git a/src/index.js b/src/index.js index 34f784c..bb23288 100644 --- a/src/index.js +++ b/src/index.js @@ -1,35 +1,35 @@ -import convertWithAdguard from "./converters/adguard.js"; -import convertWithAbp from "./converters/abp.js"; +import convertWithAdguard from './converters/adguard.js'; +import convertWithAbp from './converters/abp.js'; -const $input = document.querySelector("#input textarea"); -const $submitButton = document.querySelector("#input input[type=submit]"); -const $outputAdguard = document.querySelector("#output-adguard"); -const $outputAbp = document.querySelector("#output-abp"); -const $errorsAdguard = document.querySelector("#errors-adguard"); -const $errorsAbp = document.querySelector("#errors-abp"); +const $input = document.querySelector('#input textarea'); +const $submitButton = document.querySelector('#input input[type=submit]'); +const $outputAdguard = document.querySelector('#output-adguard'); +const $outputAbp = document.querySelector('#output-abp'); +const $errorsAdguard = document.querySelector('#errors-adguard'); +const $errorsAbp = document.querySelector('#errors-abp'); const ADGUARD_CONVERTER_OPTIONS = { - resourcesPath: "/rule_resources/redirects", + resourcesPath: '/rule_resources/redirects', }; -$submitButton.addEventListener("click", async (ev) => { +$submitButton.addEventListener('click', async (ev) => { ev.preventDefault(); - const rules = $input.value.split("\n").filter(Boolean); + const rules = $input.value.split('\n').filter(Boolean); - const { rules: convertedRulesAdguard, errors: errorsAdguard } = - await convertWithAdguard(rules, ADGUARD_CONVERTER_OPTIONS); - const { rules: convertedRulesAbp, errors: errorsAbp } = await convertWithAbp( - rules + const { rules: convertedRulesAdguard, errors: errorsAdguard } = await convertWithAdguard( + rules, + ADGUARD_CONVERTER_OPTIONS, ); + const { rules: convertedRulesAbp, errors: errorsAbp } = await convertWithAbp(rules); $outputAdguard.innerHTML = JSON.stringify(convertedRulesAdguard, null, 2); $outputAbp.innerHTML = JSON.stringify(convertedRulesAbp, null, 2); - $errorsAdguard.innerHTML = errorsAdguard.join("\n"); - $errorsAbp.innerHTML = errorsAbp.join("\n"); + $errorsAdguard.innerHTML = errorsAdguard.join('\n'); + $errorsAbp.innerHTML = errorsAbp.join('\n'); }); -window.addEventListener("message", async (event) => { - if (!event.data || event.data.action !== "convert") { +window.addEventListener('message', async (event) => { + if (!event.data || event.data.action !== 'convert') { return; } @@ -38,12 +38,9 @@ window.addEventListener("message", async (event) => { let rules, errors; try { - if (converter === "adguard") { - ({ rules, errors } = await convertWithAdguard( - filters, - ADGUARD_CONVERTER_OPTIONS - )); - } else if (converter == "abp") { + if (converter === 'adguard') { + ({ rules, errors } = await convertWithAdguard(filters, ADGUARD_CONVERTER_OPTIONS)); + } else if (converter == 'abp') { ({ rules, errors } = await convertWithAbp(filters)); } } catch (e) { @@ -55,6 +52,6 @@ window.addEventListener("message", async (event) => { rules, errors, }, - event.origin + event.origin, ); }); diff --git a/test/e2e/index.spec.js b/test/e2e/index.spec.js index 14ef89b..f628363 100644 --- a/test/e2e/index.spec.js +++ b/test/e2e/index.spec.js @@ -1,30 +1,30 @@ -import { test, expect } from "@playwright/test"; +import { test, expect } from '@playwright/test'; let messages = []; async function setup(page) { - await page.goto("/"); - page.exposeFunction("logMessage", (msg) => messages.push(msg)); + await page.goto('/'); + page.exposeFunction('logMessage', (msg) => messages.push(msg)); await page.evaluate(() => - window.addEventListener("message", (msg) => { + window.addEventListener('message', (msg) => { window.logMessage(msg.data); - }) + }), ); } -test.describe("converts rules with postMessage", () => { +test.describe('converts rules with postMessage', () => { test.beforeEach(() => { messages = []; }); - test("with adguard converter", async ({ page }) => { + test('with adguard converter', async ({ page }) => { await setup(page); await page.evaluate(() => window.postMessage({ - action: "convert", - converter: "adguard", - filters: ["||example.com"], - }) + action: 'convert', + converter: 'adguard', + filters: ['||example.com'], + }), ); await expect(() => { expect(messages.at(-1)).toEqual({ @@ -33,10 +33,10 @@ test.describe("converts rules with postMessage", () => { { id: 1, action: { - type: "block", + type: 'block', }, condition: { - urlFilter: "||example.com", + urlFilter: '||example.com', isUrlFilterCaseSensitive: false, }, priority: 1, @@ -46,14 +46,14 @@ test.describe("converts rules with postMessage", () => { }).toPass(); }); - test("with abp converter", async ({ page }) => { + test('with abp converter', async ({ page }) => { await setup(page); await page.evaluate(() => window.postMessage({ - action: "convert", - converter: "abp", - filters: ["||example.com"], - }) + action: 'convert', + converter: 'abp', + filters: ['||example.com'], + }), ); await expect(() => { expect(messages.at(-1)).toEqual({ @@ -64,10 +64,10 @@ test.describe("converts rules with postMessage", () => { priority: 1000, condition: { isUrlFilterCaseSensitive: false, - urlFilter: "||example.com", + urlFilter: '||example.com', }, action: { - type: "block", + type: 'block', }, }, ], diff --git a/test/unit/converters/abp.spec.js b/test/unit/converters/abp.spec.js index 52ed917..ea6e38c 100644 --- a/test/unit/converters/abp.spec.js +++ b/test/unit/converters/abp.spec.js @@ -1,4 +1,4 @@ -import { describe, it, expect } from "bun:test"; +import { describe, it, expect } from 'bun:test'; import convertWithAbp from '../../../src/converters/abp.js'; @@ -16,27 +16,25 @@ describe('abp converter', () => { expect(rules[0]).not.toEqual(undefined); }); - it("||tinypass.com^$3p,domain=~foreignpolicy.com", async () => { + it('||tinypass.com^$3p,domain=~foreignpolicy.com', async () => { const { rules } = await convertWithAbp(['tinypass.com$3p,domain=x.z']); expect(rules[0]).toEqual({ action: { - type: "block" + type: 'block', }, condition: { - domainType: "thirdParty", - initiatorDomains: [ - "x.z" - ], + domainType: 'thirdParty', + initiatorDomains: ['x.z'], isUrlFilterCaseSensitive: false, - urlFilter: "tinypass.com" + urlFilter: 'tinypass.com', }, priority: 2000, - id: 1 + id: 1, }); }); - it("handles regexp rules", async () => { - const { rules } = await convertWithAbp(["/js/"]); + it('handles regexp rules', async () => { + const { rules } = await convertWithAbp(['/js/']); expect(rules[0]).not.toEqual(undefined); }); }); diff --git a/test/unit/converters/adguard.spec.js b/test/unit/converters/adguard.spec.js index f91ef97..a65e6e0 100644 --- a/test/unit/converters/adguard.spec.js +++ b/test/unit/converters/adguard.spec.js @@ -1,69 +1,67 @@ -import { describe, it, expect } from "bun:test"; +import { describe, it, expect } from 'bun:test'; import convertWithAdguard from '../../../src/converters/adguard.js'; describe('adguard converter', () => { - it("||t.a3cloud.net/AM-141112/tag.js", async () => { + it('||t.a3cloud.net/AM-141112/tag.js', async () => { const { rules } = await convertWithAdguard(['||t.a3cloud.net/AM-141112/tag.js']); expect(rules[0]).toEqual({ action: { - type: "block" + type: 'block', }, id: 1, priority: 1, condition: { isUrlFilterCaseSensitive: false, - urlFilter: "||t.a3cloud.net/am-141112/tag.js" - } + urlFilter: '||t.a3cloud.net/am-141112/tag.js', + }, }); }); // to be fixed with https://github.com/AdguardTeam/tsurlfilter/pull/109 - it("/baynote(-observer)?([0-9]+)\.js/", async () => { + it('/baynote(-observer)?([0-9]+).js/', async () => { const { rules } = await convertWithAdguard([String.raw`/baynote(-observer)?([0-9]+)\.js/`]); expect(rules[0]).toEqual({ action: { - type: "block" + type: 'block', }, condition: { isUrlFilterCaseSensitive: false, - regexFilter: String.raw`baynote(-observer)?([0-9]+)\.js` + regexFilter: String.raw`baynote(-observer)?([0-9]+)\.js`, }, id: 1, - priority: 1 + priority: 1, }); }); - it("handles regexp with ?", async () => { + it('handles regexp with ?', async () => { const { rules } = await convertWithAdguard(['/a?/']); expect(rules[0]).toEqual({ action: { - type: "block" + type: 'block', }, condition: { isUrlFilterCaseSensitive: false, - regexFilter: "a?" + regexFilter: 'a?', }, id: 1, - priority: 1 + priority: 1, }); }); - it("handles regexp escaping", async () => { + it('handles regexp escaping', async () => { const { rules } = await convertWithAdguard([String.raw`/\\d/$doc`]); expect(rules[0]).toEqual({ action: { - type: "block" + type: 'block', }, condition: { isUrlFilterCaseSensitive: false, regexFilter: String.raw`\\d`, - resourceTypes: [ - "main_frame" - ] + resourceTypes: ['main_frame'], }, id: 1, - priority: 101 + priority: 101, }); }); }); diff --git a/test/unit/converters/helpers.spec.js b/test/unit/converters/helpers.spec.js index 69731b9..7404ddf 100644 --- a/test/unit/converters/helpers.spec.js +++ b/test/unit/converters/helpers.spec.js @@ -1,60 +1,52 @@ -import { describe, it, expect } from "bun:test"; +import { describe, it, expect } from 'bun:test'; -import { generateResourcesMapping, normalizeFilter, normalizeRule } from "../../../src/converters/helpers.js"; +import { + generateResourcesMapping, + normalizeFilter, + normalizeRule, +} from '../../../src/converters/helpers.js'; -describe("normalizeFilter", () => { - it("format params", () => { +describe('normalizeFilter', () => { + it('format params', () => { expect( - normalizeFilter( - "||tags.tiqcdn.com^$script,domain=firstdirect.com|santander.pl|swisscom.ch" - ) - ).toEqual( - "||tags.tiqcdn.com^$script,domain=firstdirect.com|santander.pl|swisscom.ch" - ); + normalizeFilter('||tags.tiqcdn.com^$script,domain=firstdirect.com|santander.pl|swisscom.ch'), + ).toEqual('||tags.tiqcdn.com^$script,domain=firstdirect.com|santander.pl|swisscom.ch'); - expect( - normalizeFilter( - "||test$param1$params2$param3" - ) - ).toEqual( - "||test$param1,params2,param3" + expect(normalizeFilter('||test$param1$params2$param3')).toEqual( + '||test$param1,params2,param3', ); }); - it("replaces 3p with third-party", () => { - expect( - normalizeFilter("||tinypass.com^$3p,domain=~foreignpolicy.com") - ).toEqual("||tinypass.com^$third-party,domain=~foreignpolicy.com") + it('replaces 3p with third-party', () => { + expect(normalizeFilter('||tinypass.com^$3p,domain=~foreignpolicy.com')).toEqual( + '||tinypass.com^$third-party,domain=~foreignpolicy.com', + ); }); - it("removes duplicate params", () => { - expect( - normalizeFilter("||tealiumiq.com^$3p$third-party") - ).toEqual("||tealiumiq.com^$third-party") + it('removes duplicate params', () => { + expect(normalizeFilter('||tealiumiq.com^$3p$third-party')).toEqual( + '||tealiumiq.com^$third-party', + ); }); - describe("with case-sesitive filters", () => { - it("is casesensitive by default", () => { - expect( - normalizeFilter("TEST") - ).toEqual("test"); + describe('with case-sesitive filters', () => { + it('is casesensitive by default', () => { + expect(normalizeFilter('TEST')).toEqual('test'); }); - it("keeps the case with match-case param", () => { - expect( - normalizeFilter("TEST$match-case") - ).toEqual("TEST$match-case"); + it('keeps the case with match-case param', () => { + expect(normalizeFilter('TEST$match-case')).toEqual('TEST$match-case'); }); }); - describe("with redirect param", () => { - it("replaces values with slashes", () => { - expect( - normalizeFilter("test$redirect=scorecardresearch.com/beacon.js") - ).toEqual("test$redirect=scorecardresearch_beacon.js"); - expect( - normalizeFilter("test$redirect-rule=scorecardresearch.com/beacon.js") - ).toEqual("test$redirect-rule=scorecardresearch_beacon.js"); + describe('with redirect param', () => { + it('replaces values with slashes', () => { + expect(normalizeFilter('test$redirect=scorecardresearch.com/beacon.js')).toEqual( + 'test$redirect=scorecardresearch_beacon.js', + ); + expect(normalizeFilter('test$redirect-rule=scorecardresearch.com/beacon.js')).toEqual( + 'test$redirect-rule=scorecardresearch_beacon.js', + ); }); }); }); @@ -66,16 +58,20 @@ describe('normalizeRule', () => { describe('with urlFilter', () => { it('sets isUrlFilterCaseSensitive default value', () => { - expect(normalizeRule({ - condition: {}, - })).toEqual({ + expect( + normalizeRule({ + condition: {}, + }), + ).toEqual({ condition: {}, }); - expect(normalizeRule({ - condition: { - urlFilter: 'test', - }, - })).toEqual({ + expect( + normalizeRule({ + condition: { + urlFilter: 'test', + }, + }), + ).toEqual({ condition: { urlFilter: 'test', isUrlFilterCaseSensitive: false, @@ -83,26 +79,30 @@ describe('normalizeRule', () => { }); }); - it("removes trailing *", () => { - expect(normalizeRule({ - condition: { - urlFilter: "test*", - }, - })).toEqual({ + it('removes trailing *', () => { + expect( + normalizeRule({ + condition: { + urlFilter: 'test*', + }, + }), + ).toEqual({ condition: { isUrlFilterCaseSensitive: false, - urlFilter: "test", + urlFilter: 'test', }, }); - }) - }) + }); + }); it('does not wraps regex rules in //', () => { - expect(normalizeRule({ - condition: { - regexFilter: 'test', - }, - })).toEqual({ + expect( + normalizeRule({ + condition: { + regexFilter: 'test', + }, + }), + ).toEqual({ condition: { regexFilter: 'test', }, @@ -110,11 +110,13 @@ describe('normalizeRule', () => { }); it('replaces domains with initiatorDomains', () => { - expect(normalizeRule({ - condition: { - domains: ['test'], - }, - })).toEqual({ + expect( + normalizeRule({ + condition: { + domains: ['test'], + }, + }), + ).toEqual({ condition: { initiatorDomains: ['test'], }, @@ -122,11 +124,13 @@ describe('normalizeRule', () => { }); it('replaces excludedDomains with excludedInitiatorDomains', () => { - expect(normalizeRule({ - condition: { - excludedDomains: ['test'], - }, - })).toEqual({ + expect( + normalizeRule({ + condition: { + excludedDomains: ['test'], + }, + }), + ).toEqual({ condition: { excludedInitiatorDomains: ['test'], }, @@ -134,21 +138,21 @@ describe('normalizeRule', () => { }); it('replaces extensionPath respecting existing dirname', () => { - expect(normalizeRule({ - action: { - type: 'redirect', - redirect: { - extensionPath: '/rule_resources/redirects/alias', + expect( + normalizeRule( + { + action: { + type: 'redirect', + redirect: { + extensionPath: '/rule_resources/redirects/alias', + }, + }, }, - }, - }, { - resourcesMapping: new Map([ - [ - 'alias', - 'test.js', - ], - ]), - })).toEqual({ + { + resourcesMapping: new Map([['alias', 'test.js']]), + }, + ), + ).toEqual({ action: { type: 'redirect', redirect: { diff --git a/test/unit/helpers.js b/test/unit/helpers.js index 81b6f42..bddf557 100644 --- a/test/unit/helpers.js +++ b/test/unit/helpers.js @@ -1,7 +1,7 @@ -import { expect } from "bun:test"; +import { expect } from 'bun:test'; -import convertWithAbp from "../../src/converters/abp.js"; -import convertWithAdguard from "../../src/converters/adguard.js"; +import convertWithAbp from '../../src/converters/abp.js'; +import convertWithAdguard from '../../src/converters/adguard.js'; function normalize(rule) { if (!rule) { diff --git a/test/unit/helpers.spec.js b/test/unit/helpers.spec.js index a3558aa..f9b87e3 100644 --- a/test/unit/helpers.spec.js +++ b/test/unit/helpers.spec.js @@ -1,6 +1,6 @@ -import { describe, it, expect } from "bun:test"; +import { describe, it, expect } from 'bun:test'; -import { testRule } from "./helpers"; +import { testRule } from './helpers'; describe('testRule', () => { it('passes on compatible rule', async () => { diff --git a/test/unit/index.spec.js b/test/unit/index.spec.js index 9914ab9..2e9dcff 100644 --- a/test/unit/index.spec.js +++ b/test/unit/index.spec.js @@ -1,6 +1,6 @@ -import { describe, it } from "bun:test"; +import { describe, it } from 'bun:test'; -import { testRule } from "./helpers"; +import { testRule } from './helpers'; describe('converters', () => { it('generate same rules', async () => { diff --git a/test/unit/trackerdb.spec.js b/test/unit/trackerdb.spec.js index 4a0deb5..4ca648d 100644 --- a/test/unit/trackerdb.spec.js +++ b/test/unit/trackerdb.spec.js @@ -1,37 +1,30 @@ -import { test } from "bun:test"; +import { test } from 'bun:test'; -import { readFileSync } from "node:fs"; -import path from "node:path"; -import loadTrackerDB from "@ghostery/trackerdb"; -import { detectFilterType } from "@cliqz/adblocker"; +import { readFileSync } from 'node:fs'; +import path from 'node:path'; +import loadTrackerDB from '@ghostery/trackerdb'; +import { detectFilterType } from '@cliqz/adblocker'; -import { ROOT_PATH } from "../../scripts/helpers/paths.js"; -import { testRule } from "./helpers.js"; +import { ROOT_PATH } from '../../scripts/helpers/paths.js'; +import { testRule } from './helpers.js'; const engine = readFileSync( - path.join( - ROOT_PATH, - "node_modules", - "@ghostery", - "trackerdb", - "dist", - "trackerdb.engine" - ) + path.join(ROOT_PATH, 'node_modules', '@ghostery', 'trackerdb', 'dist', 'trackerdb.engine'), ); const trackerDB = await loadTrackerDB(engine); const UNSUPPORTED_FILTERS = [ - "/baynote(-observer)?([0-9]+)\\.js/", - "/facebook\\.com\\/(v2\\.0\\/)?(plugins|widgets)\\/.*\\.php/", + '/baynote(-observer)?([0-9]+)\\.js/', + '/facebook\\.com\\/(v2\\.0\\/)?(plugins|widgets)\\/.*\\.php/', ]; -test("TrackerDB filters", async () => { +test('TrackerDB filters', async () => { for (const pattern of trackerDB.engine.metadata.getPatterns()) { for (const filter of pattern.filters) { if ( UNSUPPORTED_FILTERS.includes(filter) || // not supported - https://gitlab.com/eyeo/adblockplus/abc/webext-ad-filtering-solution/-/issues/572 - filter.includes(".*") || + filter.includes('.*') || // ignore cosmetic filters detectFilterType(filter) === 2 ) {