0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-23 21:53:55 -05:00

Ensure that maybeRenderHead runs last (#3821)

* Ensure that maybeRenderHead runs last

* Adds a changeset

* Make work with MDX
This commit is contained in:
Matthew Phillips 2022-07-06 08:10:19 -04:00 committed by GitHub
parent 5ac0f78411
commit c2165c34a7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 67 additions and 8 deletions

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Fix for putting the <head> into its own component

View file

@ -707,7 +707,6 @@ export async function renderPage(
children: any, children: any,
streaming: boolean streaming: boolean
): Promise<Response> { ): Promise<Response> {
let iterable: AsyncIterable<any>;
if (!componentFactory.isAstroComponentFactory) { if (!componentFactory.isAstroComponentFactory) {
const pageProps: Record<string, any> = { ...(props ?? {}), 'server:root': true }; const pageProps: Record<string, any> = { ...(props ?? {}), 'server:root': true };
const output = await renderComponent( const output = await renderComponent(
@ -719,12 +718,17 @@ export async function renderPage(
); );
let html = output.toString(); let html = output.toString();
if (!/<!doctype html/i.test(html)) { if (!/<!doctype html/i.test(html)) {
html = `<!DOCTYPE html>\n${await maybeRenderHead(result)}${html}`; let rest = html;
html = `<!DOCTYPE html>`;
for await(let chunk of maybeRenderHead(result)) {
html += chunk;
}
html += rest;
} }
return new Response(html, { return new Response(html, {
headers: new Headers([ headers: new Headers([
['Content-Type', 'text/html; charset=utf-8'], ['Content-Type', 'text/html; charset=utf-8'],
['Content-Length', `${Buffer.byteLength(html, 'utf-8')}`], ['Content-Length', Buffer.byteLength(html, 'utf-8').toString()],
]), ]),
}); });
} }
@ -769,7 +773,7 @@ export async function renderPage(
i++; i++;
} }
const bytes = encoder.encode(body); const bytes = encoder.encode(body);
headers.set('Content-Length', `${bytes.byteLength}`); headers.set('Content-Length', bytes.byteLength.toString());
} }
let response = createResponse(body, { ...init, headers }); let response = createResponse(body, { ...init, headers });
@ -789,7 +793,7 @@ const uniqueElements = (item: any, index: number, all: any[]) => {
}; };
const alreadyHeadRenderedResults = new WeakSet<SSRResult>(); const alreadyHeadRenderedResults = new WeakSet<SSRResult>();
export async function renderHead(result: SSRResult): Promise<string> { export function renderHead(result: SSRResult): Promise<string> {
alreadyHeadRenderedResults.add(result); alreadyHeadRenderedResults.add(result);
const styles = Array.from(result.styles) const styles = Array.from(result.styles)
.filter(uniqueElements) .filter(uniqueElements)
@ -811,11 +815,11 @@ export async function renderHead(result: SSRResult): Promise<string> {
// This accomodates the fact that using a <head> is optional in Astro, so this // This accomodates the fact that using a <head> is optional in Astro, so this
// is called before a component's first non-head HTML element. If the head was // is called before a component's first non-head HTML element. If the head was
// already injected it is a noop. // already injected it is a noop.
export function maybeRenderHead(result: SSRResult): string | Promise<string> { export async function* maybeRenderHead(result: SSRResult): AsyncIterable<string> {
if (alreadyHeadRenderedResults.has(result)) { if (alreadyHeadRenderedResults.has(result)) {
return ''; return;
} }
return renderHead(result); yield renderHead(result);
} }
export async function* renderAstroComponent( export async function* renderAstroComponent(

View file

@ -0,0 +1,23 @@
import { expect } from 'chai';
import * as cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Head in its own component', () => {
let fixture;
before(async () => {
fixture = await loadFixture({
root: './fixtures/astro-head/',
site: 'https://mysite.dev/',
base: '/blog',
});
await fixture.build();
});
it('Styles are appended to the head and not the body', async () => {
let html = await fixture.readFile('/head-own-component/index.html');
let $ = cheerio.load(html);
expect($('link[rel=stylesheet]')).to.have.a.lengthOf(1, 'one stylesheet overall');
expect($('head link[rel=stylesheet]')).to.have.a.lengthOf(1, 'stylesheet is in the head');
});
});

View file

@ -0,0 +1,11 @@
---
// Head.astro
const { title } = Astro.props;
---
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<title>{title}</title>
</head>

View file

@ -0,0 +1,16 @@
---
// Layout.astro
import Head from "../components/Head.astro";
---
<!DOCTYPE html>
<html lang="en">
<Head title="title"/>
<body>
<h1>Title Here</h1>
<style>
body {
background: green;
}
</style>
</body>