Repository navigation
Development

This page is for people who want to read, run or change Preen. It assumes you know JavaScript and the basics of browser extensions. Questions go to the issue tracker.
Version 0.0.29 sends no usage statistics and no debug submissions. The code for both is in packages/shared/core/telemetry.js and debug.js, but it has no server address and no host permission, so nothing leaves the computer. Releasing has the details.
- A Thunderbird MailExtension, manifest version 3. It needs Thunderbird 140 or newer, set by
strict_min_versionis 140.0 inapps/thunderbird/manifest.json. - Plain ES modules. No runtime dependencies. The build step only copies files:
npm run build:thunderbirdputs the add-on and the shared code intodist/thunderbird/, which is exactly what ships. - Tests use Node's built-in runner (
node:test). There is no test framework to install. - The background script,
background/main.js, runs as a module.
| Path | Purpose |
|---|---|
apps/thunderbird/ |
The Thunderbird add-on: manifest, background/, mail/, storage/, ui/, icons/, and its CHANGELOG.md. core and providers here are links to the shared code. |
apps/<other>/ |
Planned apps (betterbird, seamonkey, outlook, kmail, claws-mail, evolution, gmail, apple-mail). Each holds only a README.md for now. |
packages/shared/ |
Code every app shares, with no mail-app calls: core/ and providers/. |
tests/ |
Unit and UI tests (node:test). Also fixtures/ (sample emails and provider responses) and mocks/ (fake messenger, DOM and IndexedDB). |
scripts/ |
build.sh builds an app into dist/<app>/. package.sh builds and zips the .xpi. dev.sh sets up a dev install. |
docs/ |
Developer notes (jev-api-notes.md) and README images. |
dist/ |
Build output. Ignored by git. |
.github/workflows/ |
ci.yml and release.yml. See Releasing. |
.github/labels.json |
The issue and pull request labels: one app:<name> per app, plus shared. |
One repository holds every Preen app. Each app lives in apps/<name>/ and links in packages/shared/. Thunderbird is the first, and later apps are added the same way.
Architecture explains how the code is organised.
You need Node.js. The version is pinned in .nvmrc as 24. package.json has no dependencies, so there is nothing to npm install for the add-on or the tests.
node --version # should match .nvmrc
npm test # runs: node --test "tests/**/*.test.js"npm test runs every file under tests/ that ends in .test.js.
-
tests/unit/covers the pure logic inpackages/shared/core/and the background and mail code, using the mocks. -
tests/ui/loads the options, popup and activity pages against a fake DOM. -
tests/providers/checks the provider client and the host permissions. -
tests/unit/package-script.test.jsrunsscripts/package.shin a copy of the repo with a stubzip, and checks the key scan. - A test fails if a script uses
innerHTML,outerHTMLorinsertAdjacentHTML. -
tests/ui/page-guard.test.jschecks that every page'spreen-versionmeta tag matches the manifest version.
CI runs npm test on every push to main, on pull requests and on manual dispatch.
npx eslint apps packages tests # JavaScript (eslint.config.js, no plugins)
npx stylelint "apps/**/*.css" # CSS (.stylelintrc.json; stylelint is a devDependency)
npm run build:thunderbird
npx web-ext lint --source-dir dist/thunderbird # add-on checks, on the buildweb-ext lint is a Firefox linter, so it warns about Thunderbird-only permissions. Those warnings are expected. There is no lint script in package.json.
npm run build:thunderbird # copies the add-on into dist/thunderbird/
npm run package # builds, then writes dist/preen-thunderbird-<version>.xpi
sh scripts/package.sh --downloads # also copies it to ~/Downloads/preen-dev.xpiWhat the script does:
- Reads the version from
apps/thunderbird/manifest.json. - Builds
dist/thunderbird/: copiesapps/thunderbird/with the shared code as real files, leaving outCHANGELOG.md,.gitkeepand.DS_Store. - Scans
dist/thunderbird/for anything that looks like an API key or private key, and stops if it finds one. It scans the build, not the.xpi, because zip compresses the files. - Zips
dist/thunderbird/intodist/preen-thunderbird-<version>.xpi, withmanifest.jsonat the top. It useszip, or Python'szipfilewhenzipis not installed.
PREEN_DIST changes the output folder.
Quickest: run npm run build:thunderbird, then in Thunderbird open Tools, Developer Tools, Debug Add-ons, Load Temporary Add-on and pick dist/thunderbird/manifest.json. Rebuild and reload after each change.
For a full dev profile:
bash scripts/dev.shThis one script, safe to run after every change:
- Installs Node.js if it is missing after asking first, and it needs sudo.
- Runs the unit tests.
- Builds the package.
- Creates a separate
preen-devThunderbird profile on the first run only. - Installs the add-on into that profile.
- Starts Thunderbird with that profile.
Your normal profile is never touched. Options: --skip-tests, --no-launch, --profile NAME, where the name must start with preen-. The script uses the Flatpak build of Thunderbird (org.mozilla.thunderbird).
- Every source file starts with
// SPDX-License-Identifier: PolyForm-Shield-1.0.0. - The comments say why, not what.
- Pure logic goes in
packages/shared/core/and has no browser calls, so tests can run it directly and every app can use it. Anything that touches Thunderbird goes inapps/thunderbird/mail/,storage/orbackground/. - Jev judges and code decides. The model returns probabilities.
core/rules.jsturns them into tags using the user's thresholds. When unsure, Preen does less, never more. - Add a setting in
core/settings-schema.js, with its default and validation. Anything shown on the settings page has its help text inui/options/options.htmlorui/options/section-help.js. - The settings help is the documentation for most users, and this wiki is written from it, so keep the wording plain.
-
packages/shared/core/settings-schema.jsholds every setting, its default and its validation. -
packages/shared/core/rules.jsshows how scores become tags. -
apps/thunderbird/background/pipeline.jshas the five-step flow for one message. -
packages/shared/providers/systemone.jsmakes the request to the provider.
PolyForm Shield License 1.0.0. See the LICENSE file. It is source-available, not open source. You may use the code for any purpose except to provide a product or service that competes with Preen.
Preen is licensed under the PolyForm Shield License 1.0.0. It is source-available, not open source. | Photos: hjburatynski, edmondlafoto, monika1607, u_4kqnhxzlh0 and yuri_b via Pixabay.