Fix style injection (#2011)

This commit is contained in:
Drew Powers 2021-11-29 12:33:40 -07:00 committed by GitHub
parent 4436592d22
commit 50f3b8d7ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 101 additions and 10 deletions

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Bugfix: improve style and script injection for partial pages

View file

@ -27,8 +27,9 @@ export function injectTags(html: string, tags: vite.HtmlTagDescriptor[]): string
const lastToFirst = Object.entries(pos).sort((a, b) => b[1] - a[1]);
lastToFirst.forEach(([name, i]) => {
if (i === -1) {
// TODO: warn on missing tag? Is this an HTML partial?
return;
// if page didnt generate <head> or <body>, guess
if (name === 'head-prepend' || name === 'head') i = 0;
if (name === 'body-prepend' || name === 'body') i = html.length;
}
let selected = tags.filter(({ injectTo }) => {
if (name === 'head-prepend' && !injectTo) {

View file

@ -225,11 +225,6 @@ export async function render(renderers: Renderer[], mod: ComponentInstance, ssrO
let html = await renderPage(result, Component, pageProps, null);
// inject <!doctype html> if missing (TODO: is a more robust check needed for comments, etc.?)
if (!/<!doctype html/i.test(html)) {
html = '<!DOCTYPE html>\n' + html;
}
// inject tags
const tags: vite.HtmlTagDescriptor[] = [];
@ -274,6 +269,11 @@ export async function render(renderers: Renderer[], mod: ComponentInstance, ssrO
html = await viteServer.transformIndexHtml(viteifyURL(filePath), html, pathname);
}
// inject <!doctype html> if missing (TODO: is a more robust check needed for comments, etc.?)
if (!/<!doctype html/i.test(html)) {
html = '<!DOCTYPE html>\n' + html;
}
return html;
}

View file

@ -146,7 +146,7 @@ export async function renderComponent(result: SSRResult, displayName: string, Co
const probableRendererNames = guessRenderers(metadata.componentUrl);
if (Array.isArray(renderers) && renderers.length === 0 && typeof Component !== 'string') {
const message = `Unable to render ${metadata.displayName}!
const message = `Unable to render ${metadata.displayName}!
There are no \`renderers\` set in your \`astro.config.mjs\` file.
Did you mean to enable ${formatList(probableRendererNames.map((r) => '`' + r + '`'))}?`;
@ -384,7 +384,13 @@ export async function renderPage(result: SSRResult, Component: AstroComponentFac
if (needsHydrationStyles) {
styles.push(renderElement('style', { props: { 'astro-style': true }, children: 'astro-root, astro-fragment { display: contents; }' }));
}
return template.replace('</head>', styles.join('\n') + scripts.join('\n') + '</head>');
// inject styles & scripts at end of <head>
let headPos = template.indexOf('</head>');
if (headPos === -1) {
return styles.join('\n') + scripts.join('\n') + template; // if no </head>, prepend styles & scripts
}
return template.substring(0, headPos) + styles.join('\n') + scripts.join('\n') + template.substring(headPos);
}
export async function renderAstroComponent(component: InstanceType<typeof AstroComponent>) {

View file

@ -0,0 +1,41 @@
import { expect } from 'chai';
import cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Partial HTML ', async () => {
let fixture;
let devServer;
before(async () => {
fixture = await loadFixture({ projectRoot: './fixtures/astro-partial-html/' });
devServer = await fixture.startDevServer();
});
after(async () => {
devServer && devServer.stop();
});
it('injects Astro styles and scripts', async () => {
const html = await fixture.fetch('/astro').then((res) => res.text());
const $ = cheerio.load(html);
// test 1: Doctype first
expect(html).to.match(/^<!DOCTYPE html/);
// test 2: correct CSS present
const css = $('style[astro-style]').html();
expect(css).to.match(/\.astro-[^{]+{color:red;}/);
});
it('injects framework styles', async () => {
const html = await fixture.fetch('/jsx').then((res) => res.text());
const $ = cheerio.load(html);
// test 1: Doctype first
expect(html).to.match(/^<!DOCTYPE html/);
// test 2: link tag present
const href = $('link[rel=stylesheet][data-astro-injected]').attr('href');
expect(href).to.be.ok;
});
});

View file

@ -0,0 +1,3 @@
h1 {
color: red;
}

View file

@ -0,0 +1,6 @@
import React from 'react';
import './Component.css';
export default function({ children }) {
return (<>{children}</>);
}

View file

@ -0,0 +1 @@
<slot />

View file

@ -0,0 +1,15 @@
---
import Layout from '../components/Layout.astro';
// note: this test requires <Layout> to be the very first element
---
<Layout>
<h1>Astro Partial HTML</h1>
</Layout>
<style>
h1 {
color: red;
}
</style>

View file

@ -0,0 +1,12 @@
---
import Layout from '../components/Layout.astro';
import Component from '../components/Component.jsx';
// note: this test requires <Layout> to be the very first element
---
<Layout>
<Component>
<h1>JSX Partial HTML</h1>
</Component>
</Layout>

View file

@ -60,7 +60,8 @@ export async function loadFixture(inlineConfig) {
build: (opts = {}) => build(config, { mode: 'development', logging: 'error', ...opts }),
startDevServer: async (opts = {}) => {
const devServer = await dev(config, { logging: 'error', ...opts });
inlineConfig.devOptions.port = devServer.port; // update port
config.devOptions.port = devServer.port; // update port
inlineConfig.devOptions.port = devServer.port;
return devServer;
},
config,