Skip to content

Latest commit

 

History

History
182 lines (136 loc) · 9.18 KB

File metadata and controls

182 lines (136 loc) · 9.18 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

adblockplus.org — The public-facing website for the Adblock Plus browser extension. A hybrid static + dynamic architecture:

  • Static pages: Generated by the eyeo Python CMS (Jinja2 templates), deployed to Firebase Hosting
  • Dynamic service: Node.js/Express on Google Cloud Run (services/cloudrun/index.js) handling robots.txt, legacy redirects, locale routing
  • Key config: services/firebase/firebase.json (redirects, rewrites, headers), services/firebase/.firebaserc (staging/production aliases)

Repository Structure

pages/          # HTML/Markdown content pages with key=value header metadata
templates/      # Jinja2 templates (minimal, default, fixed-toc, raw)
includes/       # HTML/template partials (navbar, footer, analytics, cookie, scripts)
locales/        # Translation files — 27 languages, Chrome i18n JSON format
static/         # Static assets + per-country settings
  ALL_{cc}/     # Per-country settings.js (generated by scripts/generate-settings.mjs)
  js/           # Shared page logic (premium checkout, cookie prompt, analytics, utilities, vendor libs, etc.)
  css/          # Stylesheets
services/
  firebase/     # Firebase Hosting config + public/ output dir
  cloudrun/     # Express service (index.js + handlers/)
scripts/        # Build and utility scripts (Node.js + Python)
tests/          # Playwright E2E test suite
  *.spec.js     # Test specs
  test-pages/   # Page object models
  test-helpers/ # Mocking helpers (extension, payment, email)
  snapshots/    # Visual regression PNG baselines (Linux Chromium only)
filters/        # Python CMS filter plugins
globals/        # Python CMS global variables (subscriptions, browsers)
macros/         # Jinja2 macros (toc, install_button, social_icons)
settings.ini    # CMS configuration: site URL, locales list, RTL settings

Development Setup

Prerequisites:

  1. Install and start Docker Desktop
  2. Python 3.9 via pyenv: pyenv install 3.9 && pyenv local 3.9
  3. Clone the CMS: git clone https://gitlab.com/eyeo/websites/cms.git
  4. Set PYTHONPATH in shell profile: export PYTHONPATH="$HOME/path/to/cms:$PYTHONPATH"
  5. Install npm dependencies: npm install

Development Commands

# Start CMS dev server (http://localhost:8000 — use 127.0.0.1 on macOS if issues)
npm run fast

# Generate per-country settings.js files (after editing the settings template)
node scripts/generate-settings.mjs

# E2E tests (Playwright)
npx playwright install                    # Install browsers once
npx playwright install chrome msedge     # Additional install for Chrome/Edge projects
npx playwright test --project chromium   # Run on Chromium only (fastest)
npx playwright test                      # Run on all browsers

# Run against a staging URL
STAGING=1 STAGING_URL=<url> npx playwright test --project chromium

# Filter by tag
npx playwright test --grep @all_browsers
npx playwright test --project chromium --grep-invert "@third_party_link|@all_browsers"

# Update visual regression baselines locally
npx playwright test --grep @visual_regression --update-snapshots

Code Conventions

EditorConfig (enforced for all files)

  • Indentation: 2 spaces (HTML, JS, CSS, JSON, Markdown, shell)
  • Line endings: LF;
  • Trailing whitespace: trimmed;
  • Final newline: required

Page Authoring

Pages use plain key=value lines at the top of the file (no YAML --- delimiters):

title=My Page Title
template=fixed-toc

Available templates: minimal, default, fixed-toc, raw. Navbar, header, toc, and footer can each be suppressed via no{component} page attributes.

Partials in includes/ are pulled in via <? include partial_name ?>.

Translations (27 languages)

  • Files: locales/{locale}/{section}.json (Chrome i18n JSON format)
  • Two syntax forms for referencing strings in templates:
    • Function call: {{ get_string("key-name", "section") }} — reads from locales/{locale}/section.json
    • Inline: {{ key-name[optional-attr] Fallback English text }} — used widely in pages (e.g. {{ what-is-abp[heading] What is Adblock Plus }})
  • Arabic (ar) and Hebrew (he) are RTL — configured in settings.ini under [rtl]
  • CMS auto-detects available locales per page from the presence of JSON files
  • Crowdin manages translations; helper scripts in scripts/:
    • scripts/export-locale-from-html.mjs — export strings for translation
    • scripts/import-locale-strings.mjs — import from Crowdin export
    • scripts/copy-locale-strings.mjs — copy strings between locales

Analytics Events

Events sent to /access?{params} via adblock.log() in includes/scripts/analytics-functions.html (current logVersion: 2.1.1):

Event Source Purpose
load includes/scripts/load-tracking.html Page load, browser/OS, screen size, timing
click includes/scripts/click-tracking.html Clicks on .track-click / [data-click] elements
script-error includes/scripts/error-reporting.html JS errors with stack traces
experiment.loaded includes/scripts/frontend-experiments.html A/B variant assignments

GDPR countries get a no-op setup.js — do not add direct GA calls. Users can opt out via eyeo-ga-opt-out cookie. Pages suppress the cookie banner with prevent_cookie_prompt.

Country-Specific Logic

Pages load /settings.js (via includes/late-head-scripts.html) which sets country, currency, privacy restrictions, and VAT state. Firebase i18n rewrites serve a different file per visitor IP from static/ALL_{cc}/settings.js. The fallback (unknown country) is at static/ALL_ALL/settings.js. Run node scripts/generate-settings.mjs to regenerate after editing the template.

Do not hardcode country/currency/VAT logic in pages — use the settings infrastructure.

Testing

Framework: Playwright — projects: chromium, firefox, webkit, Google Chrome, Microsoft Edge

Test tags:

  • @all_browsers — behavior differs across browsers (runs on all browsers in CI)
  • @third_party_link — depends on external sites (excluded from standard CI runs)
  • @visual_regression — screenshot comparison against Linux Chromium baselines

Page objects: Use/extend models in tests/test-pages/ rather than writing raw selectors in spec files.

Extension mocking: Call await ExtensionHelper.mockExtensionData(page, '4.31.0', false) (from tests/test-helpers/extension-helper.js) before opening a page. Second arg is version string, third is whether Premium is enabled. Prefer mocking over full extension installation for speed.

Visual regression: Baselines are Linux Chromium only (tests/snapshots/). To update after a CI failure: download the visual_regression_tests:archive artifact, rename the file to just the browser name, replace the existing file in the linux/ snapshot folder. Do not update baselines locally on macOS — they will mismatch in CI.

Debugging CI failures: Download the test-results/ artifact and open trace.zip at https://trace.playwright.dev.

Supported Query Parameters

Sitewide

  • variant (number) — experiment variant to apply
  • country (string) — 2-letter country code override
  • defaultCurrency (string) — 3-letter currency code override
  • restrictPremium (boolean) — disable premium purchasing
  • restrictPrivacy (boolean) — disable some third-party services
  • sid (string) — 32-char session ID for correlating access logs
  • s (string) — traffic source name
  • dev / design (flag) — development/design mode

Payment pages

  • testmode (flag) — Paddle sandbox instead of live
  • has-subscription — fake subscription state: yes, no, error, timeout, finding, found

Update page

  • bc (number) — show alternate CTA with block count

Premium page

  • premium-checkout__* — handoff values: handoff, flow, page, product, premiumId, currency, frequency, amount, country, locale, timestamp
  • reenroll (flag) — show re-enroll CTA variant

CI/CD

GitLab CI/CD (.gitlab-ci.yml). Pipeline stages:

  1. prebuild_firebase — generate preview URLs
  2. build_firebase — CMS generates static pages
  3. deploy_cloud_run — deploy Express service (named b-<branch-name> on non-master)
  4. deploy_firebase — deploy to Firebase Hosting (preview channel on non-master, expires 30 days)
  5. test — Playwright E2E tests against the deployed URL

Production: Merging to master auto-deploys immediately — no manual gate. To rollback: git revert <hash> + open MR. For static-only emergency rollback: revert a release in the Firebase console (does not revert Cloud Run).

Key Notes

  • services/firebase/public/ is generated output — never edit files there directly
  • .htaccess files are legacy Apache artifacts, not used in production
  • npm run slow is a legacy Apache setup — ignore it
  • .devcontainer/ exists but is not actively supported; use the local setup
  • static/js/testing/pre-approved.js and includes/pre-approved-analytics.tmpl are unused legacy files from the old three-tier consent system