@emailens/engine is a single npm package that runs 8 analysis passes on your email HTML in one call:
1. CSS compatibility: 250+ properties checked against 12 email clients, with per-client scores (0–100). Each warning includes severity, the specific client, and a fix snippet tailored to your framework (React Email, MJML, Maizzle, or plain HTML).
2. Spam scoring: 45+ heuristic signals modeled after SpamAssassin, CAN-SPAM, and GDPR. Catches caps ratio, hidden text, URL shorteners, missing unsubscribe (with transactional email exemption), deceptive links, image-to-text ratio, and more.
3. Accessibility: WCAG contrast, alt text, heading hierarchy, layout table roles, small text, missing lang attribute.
4. Link validation: broken hrefs, insecure HTTP, javascript: protocols, deceptive URLs, empty mailto/tel, generic link text.
5. Image analysis: missing dimensions, oversized data URIs, WebP/SVG in email, tracking pixels, missing display:block.
6. Inbox preview: subject/preheader truncation per client, Gmail clipping detection (102KB threshold).
7. Domain deliverability: SPF, DKIM, DMARC, MX, and BIMI DNS validation. No external dependencies, uses node:dns/promises.
8. Template variables: catches unresolved merge tags across Handlebars, ERB, Mailchimp, Salesforce, etc.
The engine parses the HTML once and shares the DOM across all analyzers, running all 8 checks is basically the cost of one parse. Framework-aware fixes are where it gets interesting. Every CSS warning comes with a fix snippet tailored to your framework:
React Email JSX → component-level fixes (e.g., VML components for Outlook border-radius) MJML → <mj-*> element suggestions Maizzle → utility class alternatives Plain HTML → inline CSS / VML fallbacks
Fixes are classified as css (simple property swap) or structural (needs HTML restructuring). For structural issues, there's an AI fix layer, the engine builds a context-rich prompt from the warnings, scores, and original source, and delegates to whatever LLM you provide. It's BYOK: you pass a provider function, the engine handles the prompt engineering.
React Email compilation runs in a sandboxed environment (isolated-vm for true heap isolation on servers, node:vm for local/CLI use, or QuickJS via WASM). Maizzle blocks filesystem-accessing PostHTML directives at validation time. MJML uses the standard compiler. There's also a CSS transformation layer, not just flagging issues, but rewriting the HTML per client. Inlines <style> blocks for Gmail, strips unsupported properties for Outlook, and can simulate dark mode (full inversion for Gmail Android, partial for Apple Mail, none for Outlook Windows). 574 tests covering all of the above. MIT licensed. No accounts, no dashboards, no vendor lock-in.
I'd love feedback on:
The API surface, does auditEmail() (all-in-one) vs createSession() (selective checks, shared parse) make sense as the two entry points?
The scoring formula: 100 - (errors × 15) - (warnings × 5) - (info × 1), too aggressive? Too lenient?
Missing email clients or checks you'd want to see?
Repo: https://github.com/emailens/engine
Docs: https://docs.emailens.dev
If you'd rather not wire it up yourself: https://emailens.dev