# 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.
