Skip to content

Commit

Permalink
Ensure head content rendered once with lazy layouts (#4892)
Browse files Browse the repository at this point in the history
* Ensure head content rendered once with lazy layouts

* Add changeset
  • Loading branch information
matthewp authored Sep 28, 2022
1 parent 649e908 commit ff7ba0e
Show file tree
Hide file tree
Showing 9 changed files with 59 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .changeset/popular-horses-lie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Prevent multiple rendering of head content
1 change: 1 addition & 0 deletions packages/astro/src/@types/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1212,6 +1212,7 @@ export interface SSRMetadata {
pathname: string;
hasHydrationScript: boolean;
hasDirectives: Set<string>;
hasRenderedHead: boolean;
}

export interface SSRResult {
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 @@ -270,6 +270,7 @@ const canonicalURL = new URL(Astro.url.pathname, Astro.site);
renderers,
pathname,
hasHydrationScript: false,
hasRenderedHead: false,
hasDirectives: new Set(),
},
response,
Expand Down
5 changes: 2 additions & 3 deletions packages/astro/src/runtime/server/render/head.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions packages/astro/test/fixtures/lazy-layout/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "@test/lazy-layout",
"dependencies": {
"astro": "workspace:*"
}
}
13 changes: 13 additions & 0 deletions packages/astro/test/fixtures/lazy-layout/src/layouts/Main.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<html>
<head>
<title>Testing</title>
<style>
body {
background: green;
}
</style>
</head>
<body>
<slot />
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
const Layout = (await import('../layouts/Main.astro')).default;
---
<Layout>
<div>Stuff here</div>
</Layout>
19 changes: 19 additions & 0 deletions packages/astro/test/lazy-layout.test.js
Original file line number Diff line number Diff line change
@@ -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);
});
});
6 changes: 6 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit ff7ba0e

Please sign in to comment.