Track added links/styles/scripts to prevent duplicates (#7843)
* temporary change * Track rendered styles and links to prevent duplicates * Adding a changeset * Fix names
This commit is contained in:
parent
73eb4dfe2f
commit
7dbcbc86b3
8 changed files with 62 additions and 4 deletions
5
.changeset/yellow-olives-sing.md
Normal file
5
.changeset/yellow-olives-sing.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Fixes head propagation regression
|
|
@ -2033,6 +2033,8 @@ export interface SSRMetadata {
|
|||
headInTree: boolean;
|
||||
extraHead: string[];
|
||||
propagators: Map<AstroComponentFactory, AstroComponentInstance>;
|
||||
// Used to key track of unique content; links and script tags
|
||||
contentKeys: Set<string>;
|
||||
}
|
||||
|
||||
/* Preview server stuff */
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
createComponent,
|
||||
createHeadAndContent,
|
||||
renderComponent,
|
||||
renderScriptElement,
|
||||
renderUniqueScriptElement,
|
||||
renderTemplate,
|
||||
renderUniqueStylesheet,
|
||||
unescapeHTML,
|
||||
|
@ -303,7 +303,7 @@ async function render({
|
|||
.join('');
|
||||
}
|
||||
if (Array.isArray(collectedScripts)) {
|
||||
scripts = collectedScripts.map((script: any) => renderScriptElement(script)).join('');
|
||||
scripts = collectedScripts.map((script: any) => renderUniqueScriptElement(result, script)).join('');
|
||||
}
|
||||
|
||||
let props = baseProps;
|
||||
|
|
|
@ -258,6 +258,7 @@ export function createResult(args: CreateResultArgs): SSRResult {
|
|||
headInTree: false,
|
||||
extraHead: [],
|
||||
propagators: new Map(),
|
||||
contentKeys: new Set(),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ export {
|
|||
renderTemplate,
|
||||
renderToString,
|
||||
renderUniqueStylesheet,
|
||||
renderUniqueScriptElement,
|
||||
voidElementNames,
|
||||
} from './render/index.js';
|
||||
export type {
|
||||
|
|
|
@ -71,6 +71,10 @@ export async function renderToReadableStream(
|
|||
// If the Astro component returns a Response on init, return that response
|
||||
if (templateResult instanceof Response) return templateResult;
|
||||
|
||||
if (isPage) {
|
||||
await bufferHeadContent(result);
|
||||
}
|
||||
|
||||
let renderedFirstPageChunk = false;
|
||||
|
||||
return new ReadableStream({
|
||||
|
@ -144,3 +148,19 @@ async function callComponentAsTemplateResultOrResponse(
|
|||
|
||||
return isHeadAndContent(factoryResult) ? factoryResult.content : factoryResult;
|
||||
}
|
||||
|
||||
// Recursively calls component instances that might have head content
|
||||
// to be propagated up.
|
||||
async function bufferHeadContent(result: SSRResult) {
|
||||
const iterator = result._metadata.propagators.values();
|
||||
while (true) {
|
||||
const { value, done } = iterator.next();
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
const returnValue = await value.init(result);
|
||||
if (isHeadAndContent(returnValue)) {
|
||||
result._metadata.extraHead.push(returnValue.head);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,6 @@ export { renderHTMLElement } from './dom.js';
|
|||
export { maybeRenderHead, renderHead } from './head.js';
|
||||
export { renderPage } from './page.js';
|
||||
export { renderSlot, renderSlotToString, type ComponentSlots } from './slot.js';
|
||||
export { renderScriptElement, renderUniqueStylesheet } from './tags.js';
|
||||
export { renderScriptElement, renderUniqueScriptElement, renderUniqueStylesheet } from './tags.js';
|
||||
export type { RenderInstruction } from './types';
|
||||
export { addAttribute, defineScriptVars, voidElementNames } from './util.js';
|
||||
|
|
|
@ -9,14 +9,43 @@ 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) {
|
||||
if (sheet.type === 'external') {
|
||||
if (Array.from(result.styles).some((s) => s.props.href === sheet.src)) return '';
|
||||
return renderElement('link', { props: { rel: 'stylesheet', href: sheet.src }, children: '' });
|
||||
const key = 'link-external-' + sheet.src;
|
||||
if(checkOrAddContentKey(result, key)) return '';
|
||||
return renderElement('link', {
|
||||
props: {
|
||||
rel: 'stylesheet',
|
||||
href: sheet.src
|
||||
},
|
||||
children: ''
|
||||
});
|
||||
}
|
||||
|
||||
if (sheet.type === 'inline') {
|
||||
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 });
|
||||
}
|
||||
}
|
||||
|
||||
function checkOrAddContentKey(result: SSRResult, key: string): boolean {
|
||||
if(result._metadata.contentKeys.has(key)) return true;
|
||||
result._metadata.contentKeys.add(key);
|
||||
return false;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue