Files
Miguel Angel Simon Sierra 56080c5668 perf(studio): take the AST/DOM stack off the first-paint path
Studio's eagerly-loaded JS measured 855,747B gzip against a 600KiB target.
~1.85MB of the rendered module graph was a Node-only AST/DOM stack that the
browser downloaded before first paint via the modulepreloaded vendor chunk.

Two independent causes, neither of them the SDK's openComposition alone:

1. gsapParserExports.ts re-exported `isStudioHoldSet` from gsapParser.ts, the
   retired recast-based parser. That two-line predicate over an already-parsed
   animation put recast, @babel/parser, esprima, ast-types and source-map on
   the eager graph. Move it (and STUDIO_HOLD_MARKER) to gsapConstants.ts, the
   module that already exists so browser code can reach GSAP constants without
   gsapParser; gsapParser and gsapWriterAcorn now import it from there, which
   also drops gsapWriterAcorn's duplicate copy. Those five packages leave the
   browser graph entirely.

2. linkedom (+ htmlparser2 / css-select / domutils / cssom / entities) came in
   through the SDK's openComposition and through RenderQueue importing
   CANVAS_DIMENSIONS from the @hyperframes/parsers barrel, which re-exports
   hfIds.ts. Route every openComposition call site through a new lazy
   src/utils/sdkLazy.ts (all four were already inside awaited async functions,
   same pattern as mediaProbe.ts does for mediabunny), give parsers a narrow
   ./types subpath for the barrel import, and add an `sdk-parse` manualChunks
   rule so the /node_modules/ catch-all cannot fold the stack back into eager
   vendor.

Measured: 855,747B -> 604,332B gzip, target met. Ratchet lowered to 610,000B.
Verified against the built dist in Chrome: sdk-parse is not modulepreloaded and
arrives in a second wave at 115ms, after the eager set completes at 42ms; a
timeline clip drag still round-trips through openComposition and writes the
new data-start to disk, with no console errors.
2026-08-03 22:02:33 -07:00
..