From 5e568571f0324e7f75683dad9a02cab7c527e1f0 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Tue, 3 Aug 2021 09:49:57 -0400 Subject: [PATCH] Completely fixes head/doctype stuff --- packages/astro/src/compiler/index.ts | 6 +-- packages/astro/src/compiler/transform/head.ts | 27 ++++------ .../compiler/transform/util/end-of-head.ts | 19 +++++++ packages/astro/test/astro-doctype.test.js | 50 ++++++++----------- .../astro-doctype/src/components/Meta.astro | 5 ++ .../src/components/SubMeta.astro | 1 + .../src/layouts/WithDoctype.astro | 14 ++++++ 7 files changed, 74 insertions(+), 48 deletions(-) create mode 100644 packages/astro/test/fixtures/astro-doctype/src/components/Meta.astro create mode 100644 packages/astro/test/fixtures/astro-doctype/src/components/SubMeta.astro create mode 100644 packages/astro/test/fixtures/astro-doctype/src/layouts/WithDoctype.astro diff --git a/packages/astro/src/compiler/index.ts b/packages/astro/src/compiler/index.ts index c7bd98655..3b95b8190 100644 --- a/packages/astro/src/compiler/index.ts +++ b/packages/astro/src/compiler/index.ts @@ -146,8 +146,8 @@ async function __render(props, ...children) { value: props, enumerable: true }, - css: { - value: (props[__astroInternal] && props[__astroInternal].css) || [], + pageCSS: { + value: (props[__astroContext] && props[__astroContext].pageCSS) || [], enumerable: true }, isPage: { @@ -177,6 +177,7 @@ export async function __renderPage({request, children, props, css}) { Object.defineProperty(props, __astroContext, { value: { + pageCSS: css, request }, writable: false, @@ -185,7 +186,6 @@ export async function __renderPage({request, children, props, css}) { Object.defineProperty(props, __astroInternal, { value: { - css, isPage: true }, writable: false, diff --git a/packages/astro/src/compiler/transform/head.ts b/packages/astro/src/compiler/transform/head.ts index e608f8135..16b3fda70 100644 --- a/packages/astro/src/compiler/transform/head.ts +++ b/packages/astro/src/compiler/transform/head.ts @@ -73,7 +73,7 @@ export default function (opts: TransformOptions): Transformer { start: 0, end: 0, type: 'Expression', - codeChunks: ['Astro.css.map(css => (', '))'], + codeChunks: ['Astro.pageCSS.map(css => (', '))'], children: [ { type: 'Element', @@ -162,22 +162,15 @@ export default function (opts: TransformOptions): Transformer { ); } - const conditionalNode = { - start: 0, - end: 0, - type: 'Expression', - codeChunks: ['Astro.isPage ? (', ') : null'], - children: [ - { - start: 0, - end: 0, - type: 'Fragment', - children, - }, - ], - }; - - eoh.append(conditionalNode); + if(eoh.foundHeadOrHtmlElement) { + const topLevelFragment = { + start: 0, + end: 0, + type: 'Fragment', + children, + }; + eoh.append(topLevelFragment); + } }, }; } diff --git a/packages/astro/src/compiler/transform/util/end-of-head.ts b/packages/astro/src/compiler/transform/util/end-of-head.ts index cdf4d3423..1ed537c30 100644 --- a/packages/astro/src/compiler/transform/util/end-of-head.ts +++ b/packages/astro/src/compiler/transform/util/end-of-head.ts @@ -1,12 +1,15 @@ import type { TemplateNode } from '@astrojs/parser'; +const beforeHeadElements = new Set(['!doctype', 'html']); const validHeadElements = new Set(['!doctype', 'title', 'meta', 'link', 'style', 'script', 'noscript', 'base']); export class EndOfHead { + private html: TemplateNode | null = null; private head: TemplateNode | null = null; private firstNonHead: TemplateNode | null = null; private parent: TemplateNode | null = null; private stack: TemplateNode[] = []; + private foundHeadElements: boolean = false; public append: (...node: TemplateNode[]) => void = () => void 0; @@ -14,6 +17,14 @@ export class EndOfHead { return !!(this.head || this.firstNonHead); } + get foundHeadContent(): boolean { + return !!this.head || this.foundHeadElements; + } + + get foundHeadOrHtmlElement(): boolean { + return !!(this.html || this.head); + } + enter(node: TemplateNode) { if (this.found) { return; @@ -35,6 +46,14 @@ export class EndOfHead { return; } + // Skip !doctype and html elements + if(beforeHeadElements.has(name)) { + if(name === 'html') { + this.html = node; + } + return; + } + if (!validHeadElements.has(name)) { this.firstNonHead = node; this.parent = this.stack[this.stack.length - 2]; diff --git a/packages/astro/test/astro-doctype.test.js b/packages/astro/test/astro-doctype.test.js index 86a7c57a6..a3ca639db 100644 --- a/packages/astro/test/astro-doctype.test.js +++ b/packages/astro/test/astro-doctype.test.js @@ -1,36 +1,11 @@ -import { fileURLToPath } from 'url'; import { suite } from 'uvu'; import * as assert from 'uvu/assert'; -import { loadConfig } from '#astro/config'; -import { createRuntime } from '#astro/runtime'; +import { doc } from './test-utils.js'; +import { setup } from './helpers.js'; const DType = suite('doctype'); -let runtime, setupError; - -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); -}); +setup(DType, './fixtures/astro-doctype'); DType('Automatically prepends the standards mode doctype', async () => { const result = await runtime.load('/prepend'); @@ -65,4 +40,23 @@ DType('User provided doctype is case insensitive', async () => { assert.not.ok(html.includes(''), '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 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 is at the front'); +}); + DType.run(); diff --git a/packages/astro/test/fixtures/astro-doctype/src/components/Meta.astro b/packages/astro/test/fixtures/astro-doctype/src/components/Meta.astro new file mode 100644 index 000000000..903b1bd79 --- /dev/null +++ b/packages/astro/test/fixtures/astro-doctype/src/components/Meta.astro @@ -0,0 +1,5 @@ +--- +import SubMeta from './SubMeta.astro'; +--- + + \ No newline at end of file diff --git a/packages/astro/test/fixtures/astro-doctype/src/components/SubMeta.astro b/packages/astro/test/fixtures/astro-doctype/src/components/SubMeta.astro new file mode 100644 index 000000000..87309290c --- /dev/null +++ b/packages/astro/test/fixtures/astro-doctype/src/components/SubMeta.astro @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/astro/test/fixtures/astro-doctype/src/layouts/WithDoctype.astro b/packages/astro/test/fixtures/astro-doctype/src/layouts/WithDoctype.astro new file mode 100644 index 000000000..4a3061d37 --- /dev/null +++ b/packages/astro/test/fixtures/astro-doctype/src/layouts/WithDoctype.astro @@ -0,0 +1,14 @@ +--- +import '../styles/global.css'; +import Meta from '../components/Meta.astro'; +--- + + + + My App + + + + + + \ No newline at end of file