Development Guide
September 17, 2026 · View on GitHub
Table of Contents
- Prerequisites
- Getting Started
- Development Workflow
- Common Tasks
- Running the Full CI Pipeline Locally
- Troubleshooting
- Additional Resources
Prerequisites
| Tool | Version | Notes |
|---|---|---|
| Node.js | >= 22.x | LTS recommended |
| pnpm | 10.x (>=10.33.4 <11) | npm install -g pnpm@10.33.4 |
Verify your setup:
node --version # v22.x or higher
pnpm --version # 10.x
Getting Started
Clone the Repository
Clone the private repository — see the README for repository and mirror details.
git clone https://github.com/AdGuardSoftwareLimited/ext-popup-blocker.git
cd ext-popup-blocker
Install Dependencies
pnpm install
Build the Userscript
Build a development version with logging enabled:
pnpm userscript-dev
The output is written to the build/ directory. Install the
resulting build/userscript/popupblocker.user.js in your
userscript manager (Tampermonkey, Violentmonkey, Greasemonkey,
or AdGuard).
Development Workflow
Build Targets
The build system supports four targets, each producing output
in the build/ directory:
- userscript — the main popup blocker userscript
- options — standalone options page (Preact SPA)
- tests — browser-based Mocha test runner
- bundle — all of the above combined
Available Scripts
| Command | Description |
|---|---|
pnpm userscript-dev | Build userscript with logging |
pnpm userscript-beta | Build userscript (beta) |
pnpm userscript-release | Build userscript (minified) |
pnpm bundle:dev | Build all targets (dev) |
pnpm bundle:beta | Build all targets (beta) |
pnpm bundle:release | Build all targets (release) |
pnpm options-page | Build the options page |
pnpm options-page:beta | Build the beta options page |
pnpm options-page:release | Build the release options page |
pnpm tests | Build the test runner |
pnpm lint | Run ESLint |
pnpm lint:md | Run Markdownlint |
Versions are driven by CHANGELOG.md via the tag-from-changelog.yml
reusable workflow — no manual bump script is needed.
You can also invoke the builder directly:
cross-env NODE_ENV=<channel> ts-node tasks/builder --target=<target>
where
<channel>isdev | beta | release;<target>isuserscript | options | tests | bundle.
Build-time Environment
The build channel is controlled by the NODE_ENV environment
variable. The Rollup config injects these compile-time flags
based on the channel:
| Flag | dev | beta / release | Purpose |
|---|---|---|---|
DEBUG | true | false | Enable console logging |
RECORD | true | false | Enable timeline recording |
NO_PROXY | false | true | Disable extra proxy wrapping |
Beta and release builds are minified via Terser and have all debug code stripped.
Running Tests
Tests run in the browser — there is no CLI test runner.
-
Build the test runner:
pnpm tests -
Open
test/index.htmlin a browser to execute the Mocha suite. It loads the compiled test bundle fromtest/build/index.js.
The test entry point is test/index.ts. Test files mirror the
src/ structure (e.g., test/events/verify.ts tests
src/events/verify.ts). Mocks are located in test/mocks/.
Linting
Run all linters before submitting changes:
pnpm lint # ESLint
pnpm lint:md # Markdownlint
Also verify that the project compiles without TypeScript errors:
pnpm userscript-dev
See the Code Quality section in
AGENTS.md for coding standards and ESLint configuration
details.
Commit Message Convention
See the Contribution Instructions in
AGENTS.md for the required commit message format (AG-XXX prefix).
Common Tasks
Debugging the Options Page
-
Build the options page:
pnpm options-page -
Serve the
build/directory with a local HTTP server:cd build && npx serve . -
isOptionsPageinsrc/init/utils.tsalready whitelistslocalhost:andhttp://127.0.0.1addresses ending in/options.html, so no source change is needed for those. For any other address, add it there. -
Ensure the userscript (e.g., installed in AdGuard) also references the same local address.
-
Verify that your ad blocker is filtering the debug page so the userscript is active.
Previewing the Options Page Deployment
The options page is deployed to GitHub Pages by
.github/workflows/deploy-pages.yml when a release tag (v*) is pushed to the
public mirror, so no manual step is needed for a normal release.
To preview exactly what the workflow publishes:
pnpm install --frozen-lockfile
rm -rf site
pnpm options-page:beta
mkdir -p site/beta/v1
cp -R build/. site/beta/v1/
pnpm options-page:release
mkdir -p site/release/v1
cp -R build/. site/release/v1/
cp -R build/. site/
# The workflow derives this from the release tag; any version works for a preview.
version=0.0.0
for dir in site/beta/v1 site/release/v1 site; do
echo "version=${version}" > "$dir/build.txt"
done
find site -maxdepth 3 -type f | sort
To re-deploy without a release, run the Deploy options page to GitHub Pages
workflow manually from the Actions tab of the public repository
(AdguardTeam/PopupBlocker). Manual runs must pass the version input, which
the workflow records in build.txt.
Manual Testing
An easy way to verify popup blocking is to visit a test page such as http://code.ptcong.com/better-js-popunder-script/ and click anywhere on the page. Popups should be blocked with a notification in the top-right corner.
Managing Exclusions
AdGuard exclusions are manually maintained in
/exclusions.ts. Edit the file and rebuild.
TinyShield exclusions are auto-updated:
pnpm update-tinyshield-websites
This downloads the latest TinyShield domains and writes them to
tasks/tinyShieldWebsites.json. Both sets of exclusions are
added to the userscript metadata (@exclude) during the build.
Working with Locales
Translation strings live in src/locales/. To sync with the
Crowdin translation platform:
pnpm locales:download # Pull latest translations
pnpm locales:upload # Push source strings
Running the Full CI Pipeline Locally
The GitHub Actions CI (.github/workflows/ci.yml) runs lint, test, and
build inside Docker. To reproduce the same pipeline locally:
# Lint + test + dev build (matches the test-output Docker target)
DOCKER_BUILDKIT=1 docker build --progress plain --target test-output --output ./artifacts .
# Build release artifacts (matches the build-output Docker target)
DOCKER_BUILDKIT=1 docker build --progress plain --target build-output --output ./artifacts .
The compiled userscript files (popupblocker.user.js, popupblocker.meta.js,
and assets/) appear in ./artifacts/.
Troubleshooting
-
ts-nodeerrors on build — ensure you are using Node.js 22.x or higher. Older versions may lack required ES module support. -
Tests page is blank — run
pnpm tests, make suretest/build/index.jsexists, and then opentest/index.html. -
Lint errors after pulling — run
pnpm installto ensure dependencies are up to date, thenpnpm lint. -
Build fails with missing module — delete
node_modules/, then runpnpm installagain. -
Options page not loading settings — the options page communicates with the userscript via message passing. Ensure the userscript is installed and active on the page you are testing.
Additional Resources
- AGENTS.md — code guidelines, architecture, and project structure
- README.md — project overview and installation
- CHANGELOG.md — release history
- GitHub Issues — bug reports and feature requests
- Crowdin — translation contributions