marked-mangle
A tiny marked.js extension that mangles mailto links into randomized HTML character references to block email-harvesting bots.
Repository Health
Technical Analysis
marked-mangle is an official extension for the marked Markdown parser that reimplements marked’s legacy, now-deprecated mangle option as a standalone plugin. It hooks into marked’s walkTokens lifecycle, finds every parsed link token whose href starts with mailto:, and rewrites both the href and (when the link’s visible text is just the raw email address) the displayed text into a string of randomized decimal and hexadecimal HTML character references.
The result renders identically in a browser to the original mailto link — the address is still clickable and readable — but the plain-text email address never appears in the served HTML, defeating simple regex-based scrapers that harvest addresses from server-rendered pages. It’s a single-purpose, zero-runtime-dependency plugin meant to be dropped in with marked.use(mangle()) wherever a project renders user- or author-supplied Markdown containing contact emails.
What You Get
- A
mangle()factory function that returns a ready-to-use markedMarkedExtensionobject - Automatic detection of
mailto:link tokens via marked’swalkTokenshook, with no configuration needed - Character-by-character HTML entity mangling (mixed decimal and hex references) applied to both the href and the visible link text
- Dual-format distribution: ESM source, a CommonJS build, and a UMD build for direct
<script>tag usage via CDN - Hand-written TypeScript declarations validated with
tsdso consumers get accurate editor autocomplete and type-checking
Common Use Cases
- Rendering author bios or contact sections in a statically-generated blog or docs site where email addresses appear in Markdown source
- Server-rendering user-submitted Markdown (comments, profiles, wiki pages) that may contain mailto links needing scraper resistance
- Adding email-harvesting protection to an existing marked-based rendering pipeline without writing custom token-walking logic
- Preventing accidental auto-launch of the user’s default mail client when a mailto link is clicked, by obscuring it from naive link-preview scanners
Under The Hood
Architecture
The package is a single ~40-line ES module exporting one factory function, mangle(), that returns a plain object conforming to marked’s MarkedExtension interface ({ mangle: false, walkTokens }). There is no internal layering or state — marked itself drives the control flow, invoking walkTokens once per parsed token during rendering. The extension’s own logic is a short guard-clause chain: skip anything that isn’t a link token, skip anything whose href doesn’t start with mailto:, then rewrite the href via a private mangleEmail() helper and, only if the link’s sole child token is unmodified plain text matching the raw address, rewrite the visible text too. Because it depends entirely on the exact shape of marked’s token objects (token.tokens[0].type/.text), it is tightly coupled to marked’s internal extension contract and would break silently if that contract changed.
Tech Stack
Written in plain JavaScript with zero runtime dependencies, declaring only a peerDependency on marked (>=4 <19) for the extension surface it targets. The build pipeline uses Rollup to produce a CJS bundle (lib/index.cjs) and a UMD bundle (lib/index.umd.js) for browser <script> usage, alongside the raw ESM source as the module entry point and a hand-maintained .d.ts file as the types entry. Tests run on Jest with a Babel transform for the ESM source, type declarations are checked with tsd, linting uses the shared @markedjs/eslint-config, and releases are fully automated via semantic-release triggered from GitHub Actions CI, with Dependabot keeping dependencies current.
Code Quality
A single spec file exercises every branch of the small token-walking function: plain non-mailto links are left untouched, bracketed mailto links have both href and text mangled, links with nested inline formatting (e.g. bold text) have only the href mangled, and both bracketed and bare autolink forms are covered — five Jest tests total, all using inline snapshots, with Math.random swapped for a seeded linear-congruential generator so the randomized output is deterministic in CI. There is no explicit error handling anywhere in the source; the code relies entirely on guard clauses and assumes marked has already produced well-formed tokens, which is a reasonable assumption given there’s no external input parsing happening in this module itself.
What Makes It Unique
The technique itself — obfuscating mailto addresses as randomized HTML character references — is a long-established, well-known anti-scraping pattern with no novel cryptographic or algorithmic contribution here. What the package does provide is a faithful, drop-in extraction of marked’s own legacy built-in mangle option (since deprecated from marked core) into an official, independently maintained plugin, preserving byte-for-byte compatible output so existing consumers of the old core option can migrate without behavior changes.