Fix style injection (#2011)
This commit is contained in:
parent
4436592d22
commit
50f3b8d7ec
11 changed files with 101 additions and 10 deletions
5
.changeset/warm-students-melt.md
Normal file
5
.changeset/warm-students-melt.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Bugfix: improve style and script injection for partial pages
|
|
@ -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 didn’t 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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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>) {
|
||||
|
|
41
packages/astro/test/astro-partial-html.test.js
Normal file
41
packages/astro/test/astro-partial-html.test.js
Normal 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;
|
||||
});
|
||||
});
|
3
packages/astro/test/fixtures/astro-partial-html/src/components/Component.css
vendored
Normal file
3
packages/astro/test/fixtures/astro-partial-html/src/components/Component.css
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
h1 {
|
||||
color: red;
|
||||
}
|
6
packages/astro/test/fixtures/astro-partial-html/src/components/Component.jsx
vendored
Normal file
6
packages/astro/test/fixtures/astro-partial-html/src/components/Component.jsx
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
import React from 'react';
|
||||
import './Component.css';
|
||||
|
||||
export default function({ children }) {
|
||||
return (<>{children}</>);
|
||||
}
|
1
packages/astro/test/fixtures/astro-partial-html/src/components/Layout.astro
vendored
Normal file
1
packages/astro/test/fixtures/astro-partial-html/src/components/Layout.astro
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<slot />
|
15
packages/astro/test/fixtures/astro-partial-html/src/pages/astro.astro
vendored
Normal file
15
packages/astro/test/fixtures/astro-partial-html/src/pages/astro.astro
vendored
Normal 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>
|
12
packages/astro/test/fixtures/astro-partial-html/src/pages/jsx.astro
vendored
Normal file
12
packages/astro/test/fixtures/astro-partial-html/src/pages/jsx.astro
vendored
Normal 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>
|
||||
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue