Repository navigation
fix: allow custom elements to opt into live-preview morphing - #125
thelazylamaGit wants to merge 4 commits into
Conversation
|
Thanks for this, and for the E2E test. An opt-in attribute fits the problem well: custom elements are skipped on purpose (ADR 0008 §4), and a page author needs a way to say "this one is mine to morph". Plan: this ships with 2.1.0, not as a patch release. It adds a new template-level contract, so the When we port it, your commits and authorship stay, and our changes go on top as separate commits: in 2.1 the hyphen rule lives in One docs point we'll add to the README: we'll document that the morph also replaces attributes and children the component set itself. CI: the E2E overhead check fails because the runtime grew ( |
…payload-morph The keyed morph treats every custom element as a boundary it never enters, so an Astro component written as a custom element got no live-preview update of its host attributes or its server-rendered light DOM, and a host that wanted one had no way to say so. A host with `data-payload-morph` on both the current and the newly rendered element is morphed: it keeps its identity, its attributes and light DOM follow the server, and its shadow root stays untouched. The value is ignored. A host marked on one side only is replaced as before, and islands, `contenteditable` and `data-payload-owned` descendants stay boundaries inside it. The sanitizer lets the attribute through in template markup only; CMS content keeps it stripped. The rule sits in `isMorphBoundary`, which the structural applier, the fragment strategy and the route strategy share through `MorphOptions.boundary`; ADR 0008 gets section 10, and the README names the cost: attributes and children an opted-in host set itself are overwritten. Adapted from #125, which changed `isMorphBoundary` in morph.ts, where the rule lived before 2.1; it now lives in islands.ts. Its unit cases pass; this adds the one-sided case, the fragment and route strategies, the island/owned/contenteditable guards and an end-to-end spec that follows a probe element through a fragment render and a route refresh in Chromium, Firefox and WebKit. Seven of the new unit tests fail on the unchanged source. Budgets move by paired builds at one epoch, each row by its measured difference with its cushion kept. The tree-shaking budgets and their log move into their own module because check-tree-shaking.ts stood at the 600-line limit, and the trusted core grows 2 063 -> 2 064 with a dated reason. The Next.js authorized-delivery overhead moves 14 133 -> 14 309: 131 B of that come from the integrated tree, where the row was already red before this change, and 45 B from the opt-in. In the core mutation scope sanitizer.ts reports 430 mutants, 429 killed and one timed out. Refs #125
…payload-morph The keyed morph treats every custom element as a boundary it never enters, so an Astro component written as a custom element got no live-preview update of its host attributes or its server-rendered light DOM, and a host that wanted one had no way to say so. A host with `data-payload-morph` on both the current and the newly rendered element is morphed: it keeps its identity, its attributes and light DOM follow the server, and its shadow root stays untouched. The value is ignored. A host marked on one side only is replaced as before, and islands, `contenteditable` and `data-payload-owned` descendants stay boundaries inside it. The sanitizer lets the attribute through in template markup only; CMS content keeps it stripped. The rule sits in `isMorphBoundary`, which the structural applier, the fragment strategy and the route strategy share through `MorphOptions.boundary`; ADR 0008 gets section 10, and the README names the cost: attributes and children an opted-in host set itself are overwritten. Adapted from #125, which changed `isMorphBoundary` in morph.ts, where the rule lived before 2.1; it now lives in islands.ts. Its unit cases pass; this adds the one-sided case, the fragment and route strategies, the island/owned/contenteditable guards and an end-to-end spec that follows a probe element through a fragment render and a route refresh in Chromium, Firefox and WebKit. Seven of the new unit tests fail on the unchanged source. Budgets move by paired builds at one epoch, each row by its measured difference with its cushion kept. The tree-shaking budgets and their log move into their own module because check-tree-shaking.ts stood at the 600-line limit, and the trusted core grows 2 063 -> 2 064 with a dated reason. The Next.js authorized-delivery overhead moves 14 133 -> 14 309: 131 B of that come from the integrated tree, where the row was already red before this change, and 45 B from the opt-in. In the core mutation scope sanitizer.ts reports 430 mutants, 429 killed and one timed out. Refs #125
…payload-morph The keyed morph treats every custom element as a boundary it never enters, so an Astro component written as a custom element got no live-preview update of its host attributes or its server-rendered light DOM, and a host that wanted one had no way to say so. A host with `data-payload-morph` on both the current and the newly rendered element is morphed: it keeps its identity, its attributes and light DOM follow the server, and its shadow root stays untouched. The value is ignored. A host marked on one side only is replaced as before, and islands, `contenteditable` and `data-payload-owned` descendants stay boundaries inside it. The sanitizer lets the attribute through in template markup only; CMS content keeps it stripped. The rule sits in `isMorphBoundary`, which the structural applier, the fragment strategy and the route strategy share through `MorphOptions.boundary`; ADR 0008 gets section 10, and the README names the cost: attributes and children an opted-in host set itself are overwritten. Adapted from #125, which changed `isMorphBoundary` in morph.ts, where the rule lived before 2.1; it now lives in islands.ts. Its unit cases pass; this adds the one-sided case, the fragment and route strategies, the island/owned/contenteditable guards and an end-to-end spec that follows a probe element through a fragment render and a route refresh in Chromium, Firefox and WebKit. Seven of the new unit tests fail on the unchanged source. Budgets move by paired builds at one epoch, each row by its measured difference with its cushion kept. The tree-shaking budgets and their log move into their own module because check-tree-shaking.ts stood at the 600-line limit, and the trusted core grows 2 063 -> 2 064 with a dated reason. The Next.js authorized-delivery overhead moves 14 133 -> 14 309: 131 B of that come from the integrated tree, where the row was already red before this change, and 45 B from the opt-in. In the core mutation scope sanitizer.ts reports 430 mutants, 429 killed and one timed out. Refs #125
|
Thanks for this, @thelazylamaGit. Two things differ from the PR. In 2.1 the ownership rule lives in Added on top: a host marked on one side only is replaced as before, islands, Closing because the change landed through that PR rather than this one. |
Summary
A pattern I like to do quite a bit in Astro is to create Web Components / Custom-Elements as Astro components, this even fits in quite nicely with live-preview fragment updates as it automatically handles components being added and removed from the dom.
However, currently live-preview updates skip custom elements because the morphing logic rejects tag names containing a hyphen.
This PR adds an opt-in
data-payload-morphattribute so custom elements can receive live-preview updates. Custom elements without the attribute retain the existing behaviour.There's a lot of files here but the core fix is just a small change to
isMorphBoundaryinmorph.tsLinked issue
No linked issue. This fixes live-preview updates for custom elements that explicitly opt into morphing.
Checklist