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,
|
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(
|
||||||
|
|
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