Revert "Fixes duplicate head content (#995)" (#1001)

* Revert "Fixes duplicate head content (#995)"

This reverts commit 268a36f399.

* Changeset
This commit is contained in:
Matthew Phillips 2021-08-03 16:57:04 -04:00 committed by GitHub
parent 57b736211a
commit b1959f0fed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 78 additions and 121 deletions

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Reverts a change to head content that was breaking docs site

View file

@ -662,7 +662,7 @@ async function compileHtml(enterNode: TemplateNode, state: CodegenState, compile
buffers[curr] += `h(__astro_slot_content, { name: ${attributes.slot} },`; buffers[curr] += `h(__astro_slot_content, { name: ${attributes.slot} },`;
paren++; paren++;
} }
buffers[curr] += `h("${name}", ${generateAttributes(attributes)},`; buffers[curr] += `h("${name}", ${generateAttributes(attributes)}`;
paren++; paren++;
return; return;
} }
@ -692,7 +692,7 @@ async function compileHtml(enterNode: TemplateNode, state: CodegenState, compile
buffers[curr] += `h(__astro_slot_content, { name: ${attributes.slot} },`; buffers[curr] += `h(__astro_slot_content, { name: ${attributes.slot} },`;
paren++; paren++;
} }
buffers[curr] += `h(${componentName}, ${generateAttributes(attributes)},`; buffers[curr] += `h(${componentName}, ${generateAttributes(attributes)}`;
paren++; paren++;
return; return;
} else if (!componentInfo && !isCustomElementTag(componentName)) { } else if (!componentInfo && !isCustomElementTag(componentName)) {

View file

@ -150,8 +150,8 @@ async function __render(props, ...children) {
value: props, value: props,
enumerable: true enumerable: true
}, },
pageCSS: { css: {
value: (props[__astroContext] && props[__astroContext].pageCSS) || [], value: (props[__astroInternal] && props[__astroInternal].css) || [],
enumerable: true enumerable: true
}, },
isPage: { isPage: {
@ -181,7 +181,6 @@ export async function __renderPage({request, children, props, css}) {
Object.defineProperty(props, __astroContext, { Object.defineProperty(props, __astroContext, {
value: { value: {
pageCSS: css,
request request
}, },
writable: false, writable: false,
@ -190,6 +189,7 @@ export async function __renderPage({request, children, props, css}) {
Object.defineProperty(props, __astroInternal, { Object.defineProperty(props, __astroInternal, {
value: { value: {
css,
isPage: true isPage: true
}, },
writable: false, writable: false,

View file

@ -73,7 +73,7 @@ export default function (opts: TransformOptions): Transformer {
start: 0, start: 0,
end: 0, end: 0,
type: 'Expression', type: 'Expression',
codeChunks: ['Astro.pageCSS.map(css => (', '))'], codeChunks: ['Astro.css.map(css => (', '))'],
children: [ children: [
{ {
type: 'Element', type: 'Element',
@ -162,15 +162,22 @@ export default function (opts: TransformOptions): Transformer {
); );
} }
if (eoh.foundHeadOrHtmlElement || eoh.foundHeadAndBodyContent) { const conditionalNode = {
const topLevelFragment = { start: 0,
start: 0, end: 0,
end: 0, type: 'Expression',
type: 'Fragment', codeChunks: ['Astro.isPage ? (', ') : null'],
children, children: [
}; {
eoh.append(topLevelFragment); start: 0,
} end: 0,
type: 'Fragment',
children,
},
],
};
eoh.append(conditionalNode);
}, },
}; };
} }

View file

@ -1,44 +1,21 @@
import type { TemplateNode } from '@astrojs/parser'; import type { TemplateNode } from '@astrojs/parser';
const beforeHeadElements = new Set(['!doctype', 'html']); const validHeadElements = new Set(['!doctype', 'title', 'meta', 'link', 'style', 'script', 'noscript', 'base']);
const validHeadElements = new Set(['title', 'meta', 'link', 'style', 'script', 'noscript', 'base']);
export class EndOfHead { export class EndOfHead {
private html: TemplateNode | null = null;
private head: TemplateNode | null = null; private head: TemplateNode | null = null;
private firstNonHead: TemplateNode | null = null; private firstNonHead: TemplateNode | null = null;
private parent: TemplateNode | null = null; private parent: TemplateNode | null = null;
private stack: TemplateNode[] = []; private stack: TemplateNode[] = [];
public foundHeadElements = false;
public foundBodyElements = false;
public append: (...node: TemplateNode[]) => void = () => void 0; public append: (...node: TemplateNode[]) => void = () => void 0;
get found(): boolean { get found(): boolean {
return !!(this.head || this.firstNonHead); return !!(this.head || this.firstNonHead);
} }
get foundHeadContent(): boolean {
return !!this.head || this.foundHeadElements;
}
get foundHeadAndBodyContent(): boolean {
return this.foundHeadContent && this.foundBodyElements;
}
get foundHeadOrHtmlElement(): boolean {
return !!(this.html || this.head);
}
enter(node: TemplateNode) { enter(node: TemplateNode) {
const name = node.name ? node.name.toLowerCase() : null;
if (this.found) { if (this.found) {
if (!validHeadElements.has(name)) {
if (node.type === 'Element') {
this.foundBodyElements = true;
}
}
return; return;
} }
@ -49,6 +26,8 @@ export class EndOfHead {
return; return;
} }
const name = node.name.toLowerCase();
if (name === 'head') { if (name === 'head') {
this.head = node; this.head = node;
this.parent = this.stack[this.stack.length - 2]; this.parent = this.stack[this.stack.length - 2];
@ -56,24 +35,11 @@ export class EndOfHead {
return; return;
} }
// Skip !doctype and html elements
if (beforeHeadElements.has(name)) {
if (name === 'html') {
this.html = node;
}
return;
}
if (!validHeadElements.has(name)) { if (!validHeadElements.has(name)) {
if (node.type === 'Element') {
this.foundBodyElements = true;
}
this.firstNonHead = node; this.firstNonHead = node;
this.parent = this.stack[this.stack.length - 2]; this.parent = this.stack[this.stack.length - 2];
this.append = this.prependToFirstNonHead; this.append = this.prependToFirstNonHead;
return; return;
} else {
this.foundHeadElements = true;
} }
} }

View file

@ -1,13 +1,38 @@
import { fileURLToPath } from 'url';
import { suite } from 'uvu'; import { suite } from 'uvu';
import * as assert from 'uvu/assert'; import * as assert from 'uvu/assert';
import { doc } from './test-utils.js'; import { loadConfig } from '#astro/config';
import { setup } from './helpers.js'; import { createRuntime } from '#astro/runtime';
const DType = suite('doctype'); const DType = suite('doctype');
setup(DType, './fixtures/astro-doctype'); let runtime, setupError;
DType('Automatically prepends the standards mode doctype', async ({ runtime }) => { DType.before(async () => {
try {
const astroConfig = await loadConfig(fileURLToPath(new URL('./fixtures/astro-doctype', import.meta.url)));
const logging = {
level: 'error',
dest: process.stderr,
};
runtime = await createRuntime(astroConfig, { logging });
} catch (err) {
console.error(err);
setupError = err;
}
});
DType.after(async () => {
(await runtime) && runtime.shutdown();
});
DType('No errors creating a runtime', () => {
assert.equal(setupError, undefined);
});
DType('Automatically prepends the standards mode doctype', async () => {
const result = await runtime.load('/prepend'); const result = await runtime.load('/prepend');
assert.ok(!result.error, `build error: ${result.error}`); assert.ok(!result.error, `build error: ${result.error}`);
@ -15,7 +40,7 @@ DType('Automatically prepends the standards mode doctype', async ({ runtime }) =
assert.ok(html.startsWith('<!doctype html>'), 'Doctype always included'); assert.ok(html.startsWith('<!doctype html>'), 'Doctype always included');
}); });
DType('No attributes added when doctype is provided by user', async ({ runtime }) => { DType('No attributes added when doctype is provided by user', async () => {
const result = await runtime.load('/provided'); const result = await runtime.load('/provided');
assert.ok(!result.error, `build error: ${result.error}`); assert.ok(!result.error, `build error: ${result.error}`);
@ -23,7 +48,7 @@ DType('No attributes added when doctype is provided by user', async ({ runtime }
assert.ok(html.startsWith('<!doctype html>'), 'Doctype always included'); assert.ok(html.startsWith('<!doctype html>'), 'Doctype always included');
}); });
DType.skip('Preserves user provided doctype', async ({ runtime }) => { DType.skip('Preserves user provided doctype', async () => {
const result = await runtime.load('/preserve'); const result = await runtime.load('/preserve');
assert.ok(!result.error, `build error: ${result.error}`); assert.ok(!result.error, `build error: ${result.error}`);
@ -31,7 +56,7 @@ DType.skip('Preserves user provided doctype', async ({ runtime }) => {
assert.ok(html.startsWith('<!doctype HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">'), 'Doctype included was preserved'); assert.ok(html.startsWith('<!doctype HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">'), 'Doctype included was preserved');
}); });
DType('User provided doctype is case insensitive', async ({ runtime }) => { DType('User provided doctype is case insensitive', async () => {
const result = await runtime.load('/capital'); const result = await runtime.load('/capital');
assert.ok(!result.error, `build error: ${result.error}`); assert.ok(!result.error, `build error: ${result.error}`);
@ -40,23 +65,4 @@ DType('User provided doctype is case insensitive', async ({ runtime }) => {
assert.not.ok(html.includes('</!DOCTYPE>'), 'There should not be a closing tag'); assert.not.ok(html.includes('</!DOCTYPE>'), 'There should not be a closing tag');
}); });
DType('Doctype can be provided in a layout', async ({ runtime }) => {
const result = await runtime.load('/in-layout');
assert.ok(!result.error, `build error: ${result.error}`);
const html = result.contents.toString('utf-8');
assert.ok(html.startsWith('<!doctype html>'), 'doctype is at the front');
const $ = doc(html);
assert.equal($('head link').length, 1, 'A link inside of the head');
});
DType('Doctype is added in a layout without one', async ({ runtime }) => {
const result = await runtime.load('/in-layout-no-doctype');
assert.ok(!result.error, `build error: ${result.error}`);
const html = result.contents.toString('utf-8');
assert.ok(html.startsWith('<!doctype html>'), 'doctype is at the front');
});
DType.run(); DType.run();

View file

@ -1,5 +0,0 @@
---
import SubMeta from './SubMeta.astro';
---
<meta name="author" content="Astro Fan">
<SubMeta />

View file

@ -1 +0,0 @@
<meta name="keywords" content="JavaScript,Astro">

View file

@ -1,14 +0,0 @@
---
import '../styles/global.css';
import Meta from '../components/Meta.astro';
---
<!doctype html>
<html lang="en">
<head>
<title>My App</title>
<Meta />
</head>
<body>
</body>
</html>

View file

@ -1,11 +0,0 @@
---
import '../styles/global.css'
---
<html lang="en">
<head>
<title>My App</title>
</head>
<body>
</body>
</html>

View file

@ -1,4 +0,0 @@
---
import WithoutDoctype from '../layouts/WithoutDoctype.astro';
---
<WithoutDoctype />

View file

@ -1,4 +0,0 @@
---
import WithDoctype from '../layouts/WithDoctype.astro';
---
<WithDoctype />

View file

@ -1,3 +0,0 @@
body {
background: green;
}

View file

@ -2,7 +2,6 @@
import Something from '../components/Something.jsx'; import Something from '../components/Something.jsx';
import Child from '../components/Child.astro'; import Child from '../components/Child.astro';
--- ---
<title>My page</title> <title>My page</title>
<style> <style>
.h1 { .h1 {

View file

@ -0,0 +1,6 @@
---
import Something from '../components/Something.jsx';
import Child from '../components/Child.astro';
---
<Something client:load />
<Child />

View file

@ -25,4 +25,14 @@ NoHeadEl('Places style and scripts before the first non-head element', async ({
assert.equal($('script[src="/_snowpack/hmr-client.js"]').length, 1, 'Only the hmr client for the page'); assert.equal($('script[src="/_snowpack/hmr-client.js"]').length, 1, 'Only the hmr client for the page');
}); });
NoHeadEl('Injects HMR script even when there are no elements on the page', async ({ runtime }) => {
const result = await runtime.load('/no-elements');
assert.ok(!result.error, `build error: ${result.error}`);
const html = result.contents;
const $ = doc(html);
assert.equal($('script[src="/_snowpack/hmr-client.js"]').length, 1, 'Only the hmr client for the page');
});
NoHeadEl.run(); NoHeadEl.run();