dom-to-image-more
Version:
Generates an image from a DOM node using HTML5 canvas and SVG
93 lines (75 loc) • 7 kB
Markdown
# Fork Analysis — what forks are "ahead" of `main`
Generated 2026-06-11. Compared all **124** forks' default branches against `1904labs/dom-to-image-more:main`
via the GitHub compare API. **33** were ahead by ≥1 commit; the rest are behind/even (mostly forks
created only to file PRs we've already merged). Each ahead-fork's source patch was diffed against the
current `src/dom-to-image-more.js`.
No rewrites are adopted (per request). The lists below separate **genuinely novel** work from work that
is **already superseded** in our current source.
---
## A. Genuine candidates (not in current `main`)
### 1. `ignoreCSSRuleErrors` — suppress the cross-origin cssRules `console.error` ⭐ strongest
- **Forks:** daveytran, mgenware (`dom-to-image-more-wc`), nathanfiscus — **three independent forks** hit this.
- **Where:** `getCssRules()` (current `src/dom-to-image-more.js:1785-1804`). When a cross-origin stylesheet's
`cssRules` throws on access, we unconditionally `console.error(...)`. On font-heavy pages with CDN stylesheets
this spams the console on every capture.
- **Proposal:** add a boolean option (`ignoreCSSRuleErrors`, default `false`) that gates the `console.error`.
Trivial, backwards-compatible. daveytran's exact patch already does this.
- **Note:** nathanfiscus instead tries to *pre-filter* sheets by origin (`ownerNode.href.indexOf(hostname)...`)
— buggy (throws on `<style>` with null `ownerNode`/`href`); use the option-gated approach, not theirs.
### 2. `requestInterceptor` — intercept any resource fetch (medium)
- **Fork:** mgenware (`dom-to-image-more-wc`).
- **What:** before the XHR in `getAndEncode`, call `options.requestInterceptor(url)`; if it returns a defined
value, resolve with it and skip the network. More general than `corsImg`/`imagePlaceholder` — lets callers
supply data from a cache/test harness/custom resolver for *any* URL (fonts + images).
- **Trade-off:** overlaps partially with existing `corsImg` + `imagePlaceholder`. Worth it if we want a single
general hook; otherwise low priority.
### 3. External stylesheet loading (`skipExternalFileMatch` + fetch fallback) (medium value / medium complexity)
- **Forks:** SizlePtyLtd, kbasten (subset) — origin: MichalBryxi/dom-to-image.
- **What:** `loadExternalStyleSheets()` `fetch()`es cross-origin stylesheets and re-parses them into a local
`<style>` so their `@font-face` rules become readable (working around the same cssRules CORS block as #1,
but recovering the fonts instead of just silencing the error). `skipExternalFileMatch` regex skips font files.
- **Caveats:** ships company-specific cruft (`eztees-fonts`, `fontStretch='normal'`, `offsetWidth` vs
`scrollWidth`) that must NOT be carried over. The stylesheet-fetch core is the valuable part. This is the most
substantial real feature found, but also the riskiest to integrate cleanly. Directly addresses our documented
"cannot wait for / read a cross-origin stylesheet" limitation.
### 4. `pseudoElementFilter` — choose which `::before`/`::after` get recreated (low–medium)
- **Fork:** techvalidate (2019).
- **What:** `if (pseudoElementFilter && !pseudoElementFilter(style)) return;` in pseudo-element cloning. We have
`filterStyles(node, prop)` but no pseudo-element-level skip. Niche; include only if someone asks.
### 5. `onMakeNodeCopy` / `afterClone` — extra clone hooks (low — overlaps existing)
- **Forks:** Heyana (`onMakeNodeCopy`, per-node clone *factory* run before default clone), mgenware (`afterClone`,
whole-tree hook between clone and font-embed).
- **Assessment:** both overlap our existing `adjustClonedNode(node, clone, after)` and `onclone`. The only gap is
a true *factory* (return a replacement element, e.g. hand-rasterize a canvas yourself). Low demand; skip unless
requested.
---
## B. Already superseded in current `main` (no action)
| Fork(s) | Their change | Why it's already covered |
|---|---|---|
| daveytran, ITfanheng, lukebrody | guard `readAsDataURL(response)` when response is null/non-Blob | `src:1455-1483` already checks `xhr.response !== null`, `response instanceof Blob`, wraps `readAsDataURL` in try/catch, and routes to `imagePlaceholder` + `onImageError`. lukebrody's baked-in "CORS Error" SVG = our generic `imagePlaceholder`. |
| ITfanheng | `url()` regex with matched-quote backreference | Our `URL_REGEX` (`src:1673`) already uses `\1` backreference and is more robust (handles escapes). |
| joswhite | sandbox default-style perf optimization (`copyUserComputedStyleFast`, `getDefaultStyle`, 20s sandbox teardown) | This is exactly our `styleCaching` feature (Joseph White already credited). Ours adds strict/relaxed tuning. |
| orangeswim | background inlining passed value as priority (bug) | `src:1925-1927` already reads `getPropertyPriority` correctly. |
| Printfly | inline SVG `<image>` xlink:href | Done in commit `319d036` "Inline nested SVG `<image>` href". (mask-attr reflection is the only leftover — niche.) |
| techvalidate, Hammster | shadowRoot children flattened into clone | Full open-shadow-DOM + slot support already present. |
| mfyuce | iframe support via `html2canvas` | We have native same-origin/blob iframe support; pulls an external dep. |
| borkdude | force-read `cssRules` (issue #21) | Our `getCssRules` checks the prototype `hasOwnProperty`, which fixes the same case. |
| npvn, dmytropidhaiko | `%`/`#`/newline encoding in the SVG data URI | Our `escapeXhtml` already does `%25`/`%23`/`%0A` (dmytropidhaiko's is byte-identical). |
| davidburns573 | `disableEmbedFonts` option | Already shipped (he's credited). |
| nathanfiscus | origin-check before reading cssRules | Buggy; intent folded into candidate #1. |
## C. Not viable / out of scope (no action)
- **Rewrites (explicitly excluded):** Dynaruid (`dom-to-canvas`, Bun/TS monorepo), kufii (`dom-to-image-even-more`,
webpack/babel), dmytropidhaiko (`dom-to-image-improved`, Safari-focused rename).
- **Rename/CI/publish only:** hriks (`dom-to-image-thor`), Bojagi, wrick17, ecancil ("testing"), Freepik-Labs ("wip"),
phanirithvij (test infra), Facil-Espaider (eslint indent), scrpgil (Angular AOT no-op var decls).
- **Company-specific hacks:** PlaypowerLabs (`window[url]` global cache, `mathComponentFonts`), scrive
(`fontStretch` toggle), Printfly (`eztees-fonts` filter), SizlePtyLtd's non-core bits.
- **thinkanymore:** `cloneSvgImgToPngImg` rasterizes an SVG-sourced `<img>` to PNG via canvas — niche and loses
vector scalability; Chrome renders SVG `<img>` in foreignObject fine. Skip.
---
## Recommended order
1. **`ignoreCSSRuleErrors`** — ship it; tiny, multi-fork demand, fixes real console spam.
2. **`requestInterceptor`** — consider as a general resource hook if we want one.
3. **External stylesheet fetch** — evaluate seriously (addresses a documented limitation) but integrate only the
clean core, drop the company cruft.
4. `pseudoElementFilter`, clone-factory hooks — only on request.