DEV Community

Cover image for A fallback that writes a file must never report success
José Paulino
José Paulino

Posted on

A fallback that writes a file must never report success

I maintain NoCodeExport, which turns a published Framer, Webflow, Wix or Squarespace site into static HTML you can host anywhere.

The hard part isn't downloading files. It's that a broken export and a working one look identical — same file count, same page weight, same screenshot above the fold. Four real failures, each of which passed every check I had at the time.

1. Ten broken pages, reported as success

A ten-page Webflow export finished and reported Pages: 10.

All ten had fallen back to raw captured DOM — the pipeline errored on each and, rather than fail, wrote the unprocessed HTML and moved on. No asset localization, no badge removal, no link rewriting. Ten of ten degraded. Status: success.

The fallback wasn't wrong; a raw page beats no page. What was wrong is that it was indistinguishable from the happy path:

// before — outputWritten is true either way
return { links: [], metrics: {}, outputWritten: true };

// after — the caller can tell which path ran
return { links: [], metrics: {}, outputWritten, degraded: outputWritten, error: message };
Enter fullscreen mode Exit fullscreen mode

Degraded pages are now counted separately, named in the ZIP's README, and if every page degrades the export fails outright.

If your error path writes a file, something downstream has to know it was the error path.

2. A Wix export where no page could scroll

Six pages, all status: success, no warnings. Every one shipped:

<body class="responsive siteScrollingBlocked">
Enter fullscreen mode Exit fullscreen mode

Wix's own stylesheet:

body.siteScrollingBlocked { width: 100%; position: fixed }
Enter fullscreen mode Exit fullscreen mode

document.documentElement.scrollHeight read 900px — exactly the viewport — over 4,993px of content. Nothing below the hero was reachable, on any page. The live site is fine: its body class is just responsive. The lock is boot-time state the runtime clears on hydrate. We captured before it cleared.

Fixed three ways: strip the classes at clean time, wait for the class to clear during capture (its disappearance is the hydration signal), and probe generically — any page that can't scroll but has >1.5× viewport of content gets logged. The third matters most: the first two fix Wix, the third tells me when a different platform hits the same bug.

900px and frozen → 6,890px, scrolled to 5,856.

The obvious generalization was wrong. "Strip the vendor runtime everywhere" is safe on Wix — it renders pixel-identically while dropping 69 requests to 8 Wix hosts per pageview (including frog.wix.com telemetry) and 665KB → 263KB. Wix's runtime can't work in an export anyway: it boots a Worker from the original origin, so it throws SecurityError and dies. But Squarespace's runtime does load-bearing layout work — strip it and section-background images vanish behind their own solid-black backdrop. Same intuition, opposite correct answer. Measure the specific runtime.

3. Signals rot, and the replacement rotted faster

All of the above depends on detecting which platform built the page. Get it wrong and every platform-specific step silently switches off.

Squarespace, measured across 6 real sites: .squarespace-credit fired 0/6. The generator meta, 0/6.

So I replaced both with Squarespace.afterBodyLoad, reasoning a bootstrap hook outlives branding. Re-measured a year later across 8 template demos: 0/8.

Static.SQUARESPACE_CONTEXT fires 8/8 — it holds the site's whole runtime config, so a working page can't omit it. Detection went 65 → 95.

I don't think that one is permanent either. Webflow taught the same lesson harder: its badge is injected at runtime, so it's neither in the served HTML to detect nor there to strip — it reappears when the runtime runs on your exported copy. A current Webflow site scored exactly 30, my minimum threshold, on the generator meta alone.

Stop hunting for the one durable signal. Keep several scoring, and re-measure against live pages.

4. The bug that was my own instrument

I spent real time convinced Webflow's motion engine had outrun us — an export was reproducing "1 of 27" scroll animations.

My browser window was 439 pixels wide. At 1440px it was exact parity.

Most scroll and hover behaviour is breakpoint-gated. I'd have "fixed" something that was never broken.

When a measurement says something surprising, check the instrument before the code.


The failures that cost most aren't crashes. They're the ones where every check passes and the page is dead.

Happy to go deeper on any of these in the comments.

Top comments (0)