fix(core): align sub-composition scoping across runtime and bundler#897
fix(core): align sub-composition scoping across runtime and bundler#897func25 wants to merge 1 commit into
Conversation
b07bfee to
97dee97
Compare
97dee97 to
f51bdd1
Compare
miguel-heygen
left a comment
There was a problem hiding this comment.
Review: fix(core): align sub-composition scoping across runtime and bundler
Verdict: Approve — this is a real issue with a well-structured fix.
The problem is real
When the same sub-composition mounts multiple times, the previous code had several divergence bugs between runtime and bundled output:
- Duplicate DOM
idattributes —getElementByIdreturns the wrong element for the second instance - CSS scoping collision — both instances shared the same
[data-composition-id]scope - Variable collision —
__hfVariablesByComp["scene"]can only hold one set of values - Timeline collision —
__timelines["scene"]overwrites the first instance
The fix
The data-hf-authored-id approach is elegant: strip the literal id (no more DOM duplicates), preserve it as a data attribute for selector compatibility, and uniquify data-composition-id per instance (scene__hf1, scene__hf2). The CSS/JS scoping rewrites #scene-root → [data-hf-authored-id="scene-root"] at both compile time and runtime.
What's good
- ~30 new tests covering bundler, runtime loader, and scoping — including repeat loads, host removal, mixed inline+external duplicates, variable cleanup, and edge cases like attribute values inside CSS selectors
prepareFlattenedInnerRootcorrectly strips timing/composition attrs from the inner wrapperreplaceAuthoredRootIdSelectorsparser handles edge cases well: quoted strings in attribute selectors, bracket depth tracking, CSS escape sequences- Stale variable cleanup when hosts are removed or re-numbered is thorough
- The
getVariables()fix to use__hfTimelineCompIdinstead of__hfCompIdis the key per-instance variable isolation
Minor observations (non-blocking)
-
Duplicated constants —
FLATTENED_INNER_ROOT_STRIP_ATTRSis defined identically in bothhtmlBundler.tsandcompositionLoader.ts. Consider extracting to a shared module if they drift apart. Low risk since the list is stable. -
Duplicated selector parser —
replaceAuthoredRootIdSelectorsexists as TypeScript (compile-time) and as an embedded JS string (runtime IIFE). This is inherent to the architecture, but bugs would need fixing in two places. -
gsap.utils.selector()behavioral change — The override now useswindow.document.querySelectorAll+root.contains()filter instead ofroot.querySelectorAll. This is necessary (authored root IDs aren't realidattrs), but queries the full document. Fine for composition-sized DOMs; worth noting if compositions grow very large. -
Silent catch —
try { root.querySelector(...) } catch {}in thegetElementByIdoverride swallows all errors. Fine for a fallback path, but a debug-level log could help future troubleshooting.
All CI checks pass (including all 8 regression shards, perf suite, Windows, preview parity). The test coverage is thorough enough that I'm confident in the correctness.
What
Align sub-composition scoping and identity handling across runtime and bundler
Why
Runtime and bundled output could diverge for duplicate sub-composition instances. That affected scoped roots, per-instance variables, repeat loads, and inline template mounts.
How
data-hf-authored-id.window.__hfVariablesByComp.Test plan
How was this tested?