Fix head propagation for MDX components (#7838)

This commit is contained in:
Bjorn Lu 2023-07-28 18:00:53 +08:00 committed by GitHub
parent 365701f491
commit e50f646758
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 15 additions and 52 deletions

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Fix head propagation for MDX components

View file

@ -2033,8 +2033,6 @@ export interface SSRMetadata {
headInTree: boolean; headInTree: boolean;
extraHead: string[]; extraHead: string[];
propagators: Map<AstroComponentFactory, AstroComponentInstance>; propagators: Map<AstroComponentFactory, AstroComponentInstance>;
// Used to key track of unique content; links and script tags
contentKeys: Set<string>;
} }
/* Preview server stuff */ /* Preview server stuff */

View file

@ -7,7 +7,7 @@ import {
createHeadAndContent, createHeadAndContent,
renderComponent, renderComponent,
renderTemplate, renderTemplate,
renderUniqueScriptElement, renderScriptElement,
renderUniqueStylesheet, renderUniqueStylesheet,
unescapeHTML, unescapeHTML,
type AstroComponentFactory, type AstroComponentFactory,
@ -303,9 +303,7 @@ async function render({
.join(''); .join('');
} }
if (Array.isArray(collectedScripts)) { if (Array.isArray(collectedScripts)) {
scripts = collectedScripts scripts = collectedScripts.map((script: any) => renderScriptElement(script)).join('');
.map((script: any) => renderUniqueScriptElement(result, script))
.join('');
} }
let props = baseProps; let props = baseProps;

View file

@ -258,7 +258,6 @@ export function createResult(args: CreateResultArgs): SSRResult {
headInTree: false, headInTree: false,
extraHead: [], extraHead: [],
propagators: new Map(), propagators: new Map(),
contentKeys: new Set(),
}, },
}; };

View file

@ -27,7 +27,6 @@ export {
renderSlotToString, renderSlotToString,
renderTemplate, renderTemplate,
renderToString, renderToString,
renderUniqueScriptElement,
renderUniqueStylesheet, renderUniqueStylesheet,
voidElementNames, voidElementNames,
} from './render/index.js'; } from './render/index.js';

View file

@ -37,6 +37,7 @@ export class AstroComponentInstance {
} }
async init(result: SSRResult) { async init(result: SSRResult) {
if (this.returnValue !== undefined) return this.returnValue;
this.returnValue = this.factory(result, this.props, this.slotValues); this.returnValue = this.factory(result, this.props, this.slotValues);
return this.returnValue; return this.returnValue;
} }
@ -72,7 +73,7 @@ function validateComponentProps(props: any, displayName: string) {
} }
} }
export async function createAstroComponentInstance( export function createAstroComponentInstance(
result: SSRResult, result: SSRResult,
displayName: string, displayName: string,
factory: AstroComponentFactory, factory: AstroComponentFactory,
@ -81,16 +82,9 @@ export async function createAstroComponentInstance(
) { ) {
validateComponentProps(props, displayName); validateComponentProps(props, displayName);
const instance = new AstroComponentInstance(result, props, slots, factory); const instance = new AstroComponentInstance(result, props, slots, factory);
if (isAPropagatingComponent(result, factory) && !result._metadata.propagators.has(factory)) { if (isAPropagatingComponent(result, factory) && !result._metadata.propagators.has(factory)) {
result._metadata.propagators.set(factory, instance); result._metadata.propagators.set(factory, instance);
// Call component instances that might have head content to be propagated up.
const returnValue = await instance.init(result);
if (isHeadAndContent(returnValue)) {
result._metadata.extraHead.push(returnValue.head);
} }
}
return instance; return instance;
} }

View file

@ -71,12 +71,12 @@ export async function renderToReadableStream(
// If the Astro component returns a Response on init, return that response // If the Astro component returns a Response on init, return that response
if (templateResult instanceof Response) return templateResult; if (templateResult instanceof Response) return templateResult;
let renderedFirstPageChunk = false;
if (isPage) { if (isPage) {
await bufferHeadContent(result); await bufferHeadContent(result);
} }
let renderedFirstPageChunk = false;
return new ReadableStream({ return new ReadableStream({
start(controller) { start(controller) {
const destination: RenderDestination = { const destination: RenderDestination = {
@ -158,6 +158,7 @@ async function bufferHeadContent(result: SSRResult) {
if (done) { if (done) {
break; break;
} }
// Call component instances that might have head content to be propagated up.
const returnValue = await value.init(result); const returnValue = await value.init(result);
if (isHeadAndContent(returnValue)) { if (isHeadAndContent(returnValue)) {
result._metadata.extraHead.push(returnValue.head); result._metadata.extraHead.push(returnValue.head);

View file

@ -420,7 +420,7 @@ async function renderAstroComponent(
props: Record<string | number, any>, props: Record<string | number, any>,
slots: any = {} slots: any = {}
): Promise<RenderInstance> { ): Promise<RenderInstance> {
const instance = await createAstroComponentInstance(result, displayName, Component, props, slots); const instance = createAstroComponentInstance(result, displayName, Component, props, slots);
// Eagerly render the component so they are rendered in parallel // Eagerly render the component so they are rendered in parallel
const chunks: RenderDestinationChunk[] = []; const chunks: RenderDestinationChunk[] = [];

View file

@ -6,6 +6,6 @@ export { renderHTMLElement } from './dom.js';
export { maybeRenderHead, renderHead } from './head.js'; export { maybeRenderHead, renderHead } from './head.js';
export { renderPage } from './page.js'; export { renderPage } from './page.js';
export { renderSlot, renderSlotToString, type ComponentSlots } from './slot.js'; export { renderSlot, renderSlotToString, type ComponentSlots } from './slot.js';
export { renderScriptElement, renderUniqueScriptElement, renderUniqueStylesheet } from './tags.js'; export { renderScriptElement, renderUniqueStylesheet } from './tags.js';
export type { RenderInstruction } from './types'; export type { RenderInstruction } from './types';
export { addAttribute, defineScriptVars, voidElementNames } from './util.js'; export { addAttribute, defineScriptVars, voidElementNames } from './util.js';

View file

@ -9,45 +9,14 @@ export function renderScriptElement({ props, children }: SSRElement) {
}); });
} }
export function renderUniqueScriptElement(result: SSRResult, { props, children }: SSRElement) {
if (
Array.from(result.scripts).some((s) => {
if (s.props.type === props.type && s.props.src === props.src) {
return true;
}
if (!props.src && s.children === children) return true;
})
)
return '';
const key = `script-${props.type}-${props.src}-${children}`;
if (checkOrAddContentKey(result, key)) return '';
return renderScriptElement({ props, children });
}
export function renderUniqueStylesheet(result: SSRResult, sheet: StylesheetAsset) { export function renderUniqueStylesheet(result: SSRResult, sheet: StylesheetAsset) {
if (sheet.type === 'external') { if (sheet.type === 'external') {
if (Array.from(result.styles).some((s) => s.props.href === sheet.src)) return ''; if (Array.from(result.styles).some((s) => s.props.href === sheet.src)) return '';
const key = 'link-external-' + sheet.src; return renderElement('link', { props: { rel: 'stylesheet', href: sheet.src }, children: '' });
if (checkOrAddContentKey(result, key)) return '';
return renderElement('link', {
props: {
rel: 'stylesheet',
href: sheet.src,
},
children: '',
});
} }
if (sheet.type === 'inline') { if (sheet.type === 'inline') {
if (Array.from(result.styles).some((s) => s.children.includes(sheet.content))) return ''; if (Array.from(result.styles).some((s) => s.children.includes(sheet.content))) return '';
const key = `link-inline-` + sheet.content;
if (checkOrAddContentKey(result, key)) return '';
return renderElement('style', { props: { type: 'text/css' }, children: sheet.content }); return renderElement('style', { props: { type: 'text/css' }, children: sheet.content });
} }
} }
function checkOrAddContentKey(result: SSRResult, key: string): boolean {
if (result._metadata.contentKeys.has(key)) return true;
result._metadata.contentKeys.add(key);
return false;
}