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:
parent
5ac0f78411
commit
c2165c34a7
5 changed files with 67 additions and 8 deletions
5
.changeset/hip-months-invent.md
Normal file
5
.changeset/hip-months-invent.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Fix for putting the <head> into its own component
|
|
@ -707,7 +707,6 @@ export async function renderPage(
|
|||
children: any,
|
||||
streaming: boolean
|
||||
): Promise<Response> {
|
||||
let iterable: AsyncIterable<any>;
|
||||
if (!componentFactory.isAstroComponentFactory) {
|
||||
const pageProps: Record<string, any> = { ...(props ?? {}), 'server:root': true };
|
||||
const output = await renderComponent(
|
||||
|
@ -719,12 +718,17 @@ export async function renderPage(
|
|||
);
|
||||
let html = output.toString();
|
||||
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, {
|
||||
headers: new Headers([
|
||||
['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++;
|
||||
}
|
||||
const bytes = encoder.encode(body);
|
||||
headers.set('Content-Length', `${bytes.byteLength}`);
|
||||
headers.set('Content-Length', bytes.byteLength.toString());
|
||||
}
|
||||
|
||||
let response = createResponse(body, { ...init, headers });
|
||||
|
@ -789,7 +793,7 @@ const uniqueElements = (item: any, index: number, all: any[]) => {
|
|||
};
|
||||
|
||||
const alreadyHeadRenderedResults = new WeakSet<SSRResult>();
|
||||
export async function renderHead(result: SSRResult): Promise<string> {
|
||||
export function renderHead(result: SSRResult): Promise<string> {
|
||||
alreadyHeadRenderedResults.add(result);
|
||||
const styles = Array.from(result.styles)
|
||||
.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
|
||||
// is called before a component's first non-head HTML element. If the head was
|
||||
// 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)) {
|
||||
return '';
|
||||
return;
|
||||
}
|
||||
return renderHead(result);
|
||||
yield renderHead(result);
|
||||
}
|
||||
|
||||
export async function* renderAstroComponent(
|
||||
|
|
23
packages/astro/test/astro-head.test.js
Normal file
23
packages/astro/test/astro-head.test.js
Normal 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');
|
||||
});
|
||||
});
|
11
packages/astro/test/fixtures/astro-head/src/components/Head.astro
vendored
Normal file
11
packages/astro/test/fixtures/astro-head/src/components/Head.astro
vendored
Normal 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>
|
16
packages/astro/test/fixtures/astro-head/src/pages/head-own-component.astro
vendored
Normal file
16
packages/astro/test/fixtures/astro-head/src/pages/head-own-component.astro
vendored
Normal 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>
|
Loading…
Reference in a new issue