-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(rendering): prevent removal of necessary <astro-slot>
elements
#10317
Conversation
🦋 Changeset detectedLatest commit: 1ff765a The changes in this PR will be included in the next version bump. Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
e713ce0
to
765caf8
Compare
it('Astro slot tags are cleaned', async () => { | ||
it('Astro slot tags are kept', async () => { | ||
const html = await fixture.readFile('/index.html'); | ||
const $ = cheerio.load(html); | ||
assert.equal($('astro-slot').length, 0); | ||
assert.equal($('astro-slot').length, 1); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test was checking for the conditions that cause the bug. We need <astro-slot>
elements to exist to allow slots to render after hydration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
function removeStaticAstroSlot(html: string, supportsAstroStaticSlot: boolean) { | ||
const exp = supportsAstroStaticSlot ? ASTRO_STATIC_SLOT_EXP : ASTRO_SLOT_EXP; | ||
return html.replace(exp, ''); | ||
} | ||
|
||
// An HTML string may be processed by the parent of a parent, and if it isn't to be hydrated, astro-slot tags will be incorrectly removed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"parent of a parent" -> "grandparent"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice explanation!
Given that this is a sticky part of the code, I think we should release this in its own patch so it's easier to spot regressions if they happen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternating the slots feels like working around the issue and could have a performance impact compared to if we can render it directly correctly. Could this be fixed instead by only stripping away <astro-slot>
for non-slot/child templates? e.g.
astro/packages/astro/src/runtime/server/render/component.ts
Lines 281 to 287 in 1d9fb15
const childSlots = Object.values(children).join(''); | |
const renderTemplateResult = renderTemplate`<${Tag}${internalSpreadAttributes( | |
props | |
)}${markHTMLString( | |
childSlots === '' && voidElementNames.test(Tag) ? `/>` : `>${childSlots}</${Tag}>` | |
)}`; |
Could we only replace <astro-slot>
for non childSlots
rendered strings? Is this issue also affected by those html
that're assigned by renderToStaticMarkup
too?
@bluwy Do you mean to make an exception for the "tagname as component" case? |
I mean that If so, I was thinking we can make the fix directly in the if block for the "tagname as component" handling. Or if not, then I think this PR is probably the easiest fix, since I'm not sure if there's a reliable way to say "this chunk belongs to a slot, and we skip replacing |
I don't think the issue is limited to it. but I'll take another look. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Works for me! If there's a cleaner way to do the transformations as Blu suggested, I'm all for it. But this solution is aligned with our current hacky code, so I'm cool if the RegExp approach is the one that gets merged.
First-party framework components are not affected because they signal using "supportsAstroStaticSlots" that they will render A much simpler 90% fix could be assuming that renderers do that by default. |
The simpler fix works. Required slots could still be removed if there's a third-party renderer that explicitly sets the "supportsAstroStaticSlots" flag to For Astro 5.0, we should remove the flag to assume renderers that will always distinguish between hydratable and non-hydratable slots on their own. This was originally intended for Astro 3.0, but wasn't tracked. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome! Thanks for looking into it. This looks a ton simpler and makes sense to me too.
Changes
<astro-slot>
happens multiple times - once per parent.<astro-slot>
gets removed, even though a direct parent needs it.The solution is to mark the tag as "definitely needed", so that distant parents don't evaluate it.This is done by replacing<astro-slot>
with<astro-preserved-slot>
in the HTML temporarily.<astro-preserved-slot>
is converted back into<astro-slot>
once there's no potential of being evaluated for removal - in the code forRenderDestination
.<astro-slot>
elements #10317 (comment)Testing
Docs