Skip to content

Commit

Permalink
Track added links/styles/scripts to prevent duplicates (#7843)
Browse files Browse the repository at this point in the history
* temporary change

* Track rendered styles and links to prevent duplicates

* Adding a changeset

* Fix names
  • Loading branch information
matthewp authored Jul 27, 2023
1 parent 73eb4df commit 7dbcbc8
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/yellow-olives-sing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Fixes head propagation regression
2 changes: 2 additions & 0 deletions packages/astro/src/@types/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
4 changes: 2 additions & 2 deletions packages/astro/src/content/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
createComponent,
createHeadAndContent,
renderComponent,
renderScriptElement,
renderUniqueScriptElement,
renderTemplate,
renderUniqueStylesheet,
unescapeHTML,
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions packages/astro/src/core/render/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ export function createResult(args: CreateResultArgs): SSRResult {
headInTree: false,
extraHead: [],
propagators: new Map(),
contentKeys: new Set(),
},
};

Expand Down
1 change: 1 addition & 0 deletions packages/astro/src/runtime/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export {
renderTemplate,
renderToString,
renderUniqueStylesheet,
renderUniqueScriptElement,
voidElementNames,
} from './render/index.js';
export type {
Expand Down
20 changes: 20 additions & 0 deletions packages/astro/src/runtime/server/render/astro/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down Expand Up @@ -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);
}
}
}
2 changes: 1 addition & 1 deletion packages/astro/src/runtime/server/render/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
31 changes: 30 additions & 1 deletion packages/astro/src/runtime/server/render/tags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

0 comments on commit 7dbcbc8

Please sign in to comment.