Ensure head content rendered once with lazy layouts (#4892)

* Ensure head content rendered once with lazy layouts

* Add changeset
This commit is contained in:
Matthew Phillips 2022-09-28 10:55:06 -04:00 committed by GitHub
parent 649e9080cb
commit ff7ba0ee0f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 59 additions and 3 deletions

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Prevent multiple rendering of head content

View file

@ -1212,6 +1212,7 @@ export interface SSRMetadata {
pathname: string;
hasHydrationScript: boolean;
hasDirectives: Set<string>;
hasRenderedHead: boolean;
}
export interface SSRResult {

View file

@ -270,6 +270,7 @@ const canonicalURL = new URL(Astro.url.pathname, Astro.site);
renderers,
pathname,
hasHydrationScript: false,
hasRenderedHead: false,
hasDirectives: new Set(),
},
response,

View file

@ -12,9 +12,8 @@ const uniqueElements = (item: any, index: number, all: any[]) => {
);
};
const alreadyHeadRenderedResults = new WeakSet<SSRResult>();
export function renderHead(result: SSRResult): Promise<string> {
alreadyHeadRenderedResults.add(result);
result._metadata.hasRenderedHead = true;
const styles = Array.from(result.styles)
.filter(uniqueElements)
.map((style) => renderElement('style', style));
@ -36,7 +35,7 @@ export function renderHead(result: SSRResult): Promise<string> {
// is called before a component's first non-head HTML element. If the head was
// already injected it is a noop.
export async function* maybeRenderHead(result: SSRResult): AsyncIterable<string> {
if (alreadyHeadRenderedResults.has(result)) {
if (result._metadata.hasRenderedHead) {
return;
}
yield renderHead(result);

View file

@ -0,0 +1,6 @@
{
"name": "@test/lazy-layout",
"dependencies": {
"astro": "workspace:*"
}
}

View file

@ -0,0 +1,13 @@
<html>
<head>
<title>Testing</title>
<style>
body {
background: green;
}
</style>
</head>
<body>
<slot />
</body>
</html>

View file

@ -0,0 +1,6 @@
---
const Layout = (await import('../layouts/Main.astro')).default;
---
<Layout>
<div>Stuff here</div>
</Layout>

View file

@ -0,0 +1,19 @@
import { expect } from 'chai';
import * as cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Lazily imported layouts', () => {
/** @type {import('./test-utils').Fixture} */
let fixture;
before(async () => {
fixture = await loadFixture({ root: './fixtures/lazy-layout/' });
await fixture.build();
});
it('Renders styles only once', async () => {
const html = await fixture.readFile('/index.html');
const $ = cheerio.load(html);
expect($('link')).to.have.a.lengthOf(1);
});
});

View file

@ -1664,6 +1664,12 @@ importers:
'@astrojs/solid-js': link:../../../../integrations/solid
astro: link:../../..
packages/astro/test/fixtures/lazy-layout:
specifiers:
astro: workspace:*
dependencies:
astro: link:../../..
packages/astro/test/fixtures/legacy-astro-flavored-markdown:
specifiers:
'@astrojs/preact': workspace:*