Completely fixes head/doctype stuff

This commit is contained in:
Matthew Phillips 2021-08-03 09:49:57 -04:00
parent b2075cc137
commit 5e568571f0
7 changed files with 74 additions and 48 deletions

View file

@ -146,8 +146,8 @@ async function __render(props, ...children) {
value: props, value: props,
enumerable: true enumerable: true
}, },
css: { pageCSS: {
value: (props[__astroInternal] && props[__astroInternal].css) || [], value: (props[__astroContext] && props[__astroContext].pageCSS) || [],
enumerable: true enumerable: true
}, },
isPage: { isPage: {
@ -177,6 +177,7 @@ 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,
@ -185,7 +186,6 @@ 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.css.map(css => (', '))'], codeChunks: ['Astro.pageCSS.map(css => (', '))'],
children: [ children: [
{ {
type: 'Element', type: 'Element',
@ -162,22 +162,15 @@ export default function (opts: TransformOptions): Transformer {
); );
} }
const conditionalNode = { if(eoh.foundHeadOrHtmlElement) {
start: 0, const topLevelFragment = {
end: 0, start: 0,
type: 'Expression', end: 0,
codeChunks: ['Astro.isPage ? (', ') : null'], type: 'Fragment',
children: [ children,
{ };
start: 0, eoh.append(topLevelFragment);
end: 0, }
type: 'Fragment',
children,
},
],
};
eoh.append(conditionalNode);
}, },
}; };
} }

View file

@ -1,12 +1,15 @@
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(['!doctype', '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[] = [];
private foundHeadElements: boolean = false;
public append: (...node: TemplateNode[]) => void = () => void 0; public append: (...node: TemplateNode[]) => void = () => void 0;
@ -14,6 +17,14 @@ export class EndOfHead {
return !!(this.head || this.firstNonHead); return !!(this.head || this.firstNonHead);
} }
get foundHeadContent(): boolean {
return !!this.head || this.foundHeadElements;
}
get foundHeadOrHtmlElement(): boolean {
return !!(this.html || this.head);
}
enter(node: TemplateNode) { enter(node: TemplateNode) {
if (this.found) { if (this.found) {
return; return;
@ -35,6 +46,14 @@ 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)) {
this.firstNonHead = node; this.firstNonHead = node;
this.parent = this.stack[this.stack.length - 2]; this.parent = this.stack[this.stack.length - 2];

View file

@ -1,36 +1,11 @@
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 { loadConfig } from '#astro/config'; import { doc } from './test-utils.js';
import { createRuntime } from '#astro/runtime'; import { setup } from './helpers.js';
const DType = suite('doctype'); const DType = suite('doctype');
let runtime, setupError; setup(DType, './fixtures/astro-doctype');
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 () => { DType('Automatically prepends the standards mode doctype', async () => {
const result = await runtime.load('/prepend'); const result = await runtime.load('/prepend');
@ -65,4 +40,23 @@ DType('User provided doctype is case insensitive', async () => {
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

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

View file

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

View file

@ -0,0 +1,14 @@
---
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>