Inline define:var styles rendered after the head (#3724)
* Inline define:var styles rendered after the head * Adds a changeset * Remove tests that don't work with streaming
This commit is contained in:
parent
c8dda94125
commit
86635e035b
12 changed files with 53 additions and 65 deletions
5
.changeset/plenty-kids-own.md
Normal file
5
.changeset/plenty-kids-own.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fixes define:vars w/ styles used inside of components
|
|
@ -183,7 +183,26 @@ export async function renderComponent(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Component && (Component as any).isAstroComponentFactory) {
|
if (Component && (Component as any).isAstroComponentFactory) {
|
||||||
return renderToIterable(result, Component as any, _props, slots);
|
async function * renderAstroComponentInline(): AsyncGenerator<string, void, undefined> {
|
||||||
|
let iterable = await renderToIterable(result, Component as any, _props, slots);
|
||||||
|
// If this component added any define:vars styles and the head has already been
|
||||||
|
// sent out, we need to include those inline.
|
||||||
|
if(result.styles.size && alreadyHeadRenderedResults.has(result)) {
|
||||||
|
let styles = Array.from(result.styles);
|
||||||
|
result.styles.clear();
|
||||||
|
for(const style of styles) {
|
||||||
|
if('define:vars' in style.props) {
|
||||||
|
// We only want to render the property value and not the full stylesheet
|
||||||
|
// which is bundled in the head.
|
||||||
|
style.children = '';
|
||||||
|
yield markHTMLString(renderElement('style', style));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yield * iterable;
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderAstroComponentInline();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Component && !_props['client:only']) {
|
if (!Component && !_props['client:only']) {
|
||||||
|
@ -408,6 +427,7 @@ If you're still stuck, please open an issue on GitHub or join us at https://astr
|
||||||
|
|
||||||
island.children = `${html ?? ''}${template}`;
|
island.children = `${html ?? ''}${template}`;
|
||||||
|
|
||||||
|
// Scripts to prepend
|
||||||
let prescriptType: PrescriptType = needsHydrationScript
|
let prescriptType: PrescriptType = needsHydrationScript
|
||||||
? 'both'
|
? 'both'
|
||||||
: needsDirectiveScript
|
: needsDirectiveScript
|
||||||
|
@ -655,6 +675,7 @@ export async function renderToIterable(
|
||||||
const Component = await componentFactory(result, props, children);
|
const Component = await componentFactory(result, props, children);
|
||||||
|
|
||||||
if (!isAstroComponent(Component)) {
|
if (!isAstroComponent(Component)) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.warn(
|
console.warn(
|
||||||
`Returning a Response is only supported inside of page components. Consider refactoring this logic into something like a function that can be used in the page.`
|
`Returning a Response is only supported inside of page components. Consider refactoring this logic into something like a function that can be used in the page.`
|
||||||
);
|
);
|
||||||
|
@ -719,6 +740,8 @@ export async function renderHead(result: SSRResult): Promise<string> {
|
||||||
const styles = Array.from(result.styles)
|
const styles = Array.from(result.styles)
|
||||||
.filter(uniqueElements)
|
.filter(uniqueElements)
|
||||||
.map((style) => renderElement('style', style));
|
.map((style) => renderElement('style', style));
|
||||||
|
// Clear result.styles so that any new styles added will be inlined.
|
||||||
|
result.styles.clear();
|
||||||
const scripts = Array.from(result.scripts)
|
const scripts = Array.from(result.scripts)
|
||||||
.filter(uniqueElements)
|
.filter(uniqueElements)
|
||||||
.map((script, i) => {
|
.map((script, i) => {
|
||||||
|
|
|
@ -22,7 +22,7 @@ describe('Directives', async () => {
|
||||||
const html = await fixture.readFile('/define-vars/index.html');
|
const html = await fixture.readFile('/define-vars/index.html');
|
||||||
const $ = cheerio.load(html);
|
const $ = cheerio.load(html);
|
||||||
|
|
||||||
expect($('style')).to.have.lengthOf(1);
|
expect($('style')).to.have.lengthOf(2);
|
||||||
expect($('style').toString()).to.include('--bg: white;');
|
expect($('style').toString()).to.include('--bg: white;');
|
||||||
expect($('style').toString()).to.include('--fg: black;');
|
expect($('style').toString()).to.include('--fg: black;');
|
||||||
|
|
||||||
|
@ -31,9 +31,18 @@ describe('Directives', async () => {
|
||||||
.split(' ')
|
.split(' ')
|
||||||
.find((name) => /^astro-[A-Za-z0-9-]+/.test(name));
|
.find((name) => /^astro-[A-Za-z0-9-]+/.test(name));
|
||||||
|
|
||||||
expect($('style').toString().replace(/\s+/g, '')).to.equal(
|
expect($($('style').get(0)).toString().replace(/\s+/g, '')).to.equal(
|
||||||
`<style>.${scopedClass}{--bg:white;--fg:black;}body{background:var(--bg);color:var(--fg)}</style>`
|
`<style>.${scopedClass}{--bg:white;--fg:black;}body{background:var(--bg);color:var(--fg)}</style>`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const scopedTitleClass = $('.title')
|
||||||
|
.attr('class')
|
||||||
|
.split(' ')
|
||||||
|
.find((name) => /^astro-[A-Za-z0-9-]+/.test(name));
|
||||||
|
|
||||||
|
expect($($('style').get(1)).toString().replace(/\s+/g, '')).to.equal(
|
||||||
|
`<style>.${scopedTitleClass}{--textColor:red;}</style>`
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('set:html', async () => {
|
it('set:html', async () => {
|
||||||
|
|
10
packages/astro/test/fixtures/astro-directives/src/components/Title.astro
vendored
Normal file
10
packages/astro/test/fixtures/astro-directives/src/components/Title.astro
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
---
|
||||||
|
const textColor = 'red'
|
||||||
|
---
|
||||||
|
<h1 class="title">hello there</h1>
|
||||||
|
|
||||||
|
<style define:vars={{textColor: textColor}}>
|
||||||
|
.title {
|
||||||
|
color: var(--textColor);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,4 +1,5 @@
|
||||||
---
|
---
|
||||||
|
import Title from "../components/Title.astro"
|
||||||
let foo = 'bar'
|
let foo = 'bar'
|
||||||
let bg = 'white'
|
let bg = 'white'
|
||||||
let fg = 'black'
|
let fg = 'black'
|
||||||
|
@ -17,5 +18,7 @@ let fg = 'black'
|
||||||
<script id="inline" define:vars={{ foo }}>
|
<script id="inline" define:vars={{ foo }}>
|
||||||
console.log(foo);
|
console.log(foo);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<Title />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
---
|
|
||||||
let gotError = false;
|
|
||||||
try {
|
|
||||||
Astro.response.headers = new Headers();
|
|
||||||
} catch(err) {
|
|
||||||
gotError = true;
|
|
||||||
}
|
|
||||||
---
|
|
||||||
<div id="overwrite-error">{gotError}</div>
|
|
|
@ -1,3 +0,0 @@
|
||||||
---
|
|
||||||
Astro.response.headers.set('Seven-Eight', 'nine');
|
|
||||||
---
|
|
|
@ -1,3 +0,0 @@
|
||||||
---
|
|
||||||
Astro.response.status = 403;
|
|
||||||
---
|
|
|
@ -1,12 +0,0 @@
|
||||||
---
|
|
||||||
import SetStatus from '../components/SetStatus.astro';
|
|
||||||
---
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Testing</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Testing</h1>
|
|
||||||
<SetStatus />
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,14 +0,0 @@
|
||||||
---
|
|
||||||
import SetAHeader from '../components/SetAHeader.astro';
|
|
||||||
import OverwriteHeader from '../components/OverwriteHeader.astro';
|
|
||||||
---
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Testing</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Testing</h1>
|
|
||||||
<SetAHeader />
|
|
||||||
<OverwriteHeader />
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,5 +1,4 @@
|
||||||
---
|
---
|
||||||
import SetAHeader from '../components/SetAHeader.astro';
|
|
||||||
Astro.response.headers.set('One-Two', 'three');
|
Astro.response.headers.set('One-Two', 'three');
|
||||||
Astro.response.headers.set('Four-Five', 'six');
|
Astro.response.headers.set('Four-Five', 'six');
|
||||||
---
|
---
|
||||||
|
@ -9,6 +8,5 @@ Astro.response.headers.set('Four-Five', 'six');
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Testing</h1>
|
<h1>Testing</h1>
|
||||||
<SetAHeader />
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -32,13 +32,6 @@ describe('Using Astro.response in SSR', () => {
|
||||||
expect(response.statusText).to.equal('Oops');
|
expect(response.statusText).to.equal('Oops');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Child component can set status', async () => {
|
|
||||||
const app = await fixture.loadTestAdapterApp();
|
|
||||||
const request = new Request('http://example.com/child-set-status');
|
|
||||||
const response = await app.render(request);
|
|
||||||
expect(response.status).to.equal(403);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Can add headers', async () => {
|
it('Can add headers', async () => {
|
||||||
const app = await fixture.loadTestAdapterApp();
|
const app = await fixture.loadTestAdapterApp();
|
||||||
const request = new Request('http://example.com/some-header');
|
const request = new Request('http://example.com/some-header');
|
||||||
|
@ -46,17 +39,5 @@ describe('Using Astro.response in SSR', () => {
|
||||||
const headers = response.headers;
|
const headers = response.headers;
|
||||||
expect(headers.get('one-two')).to.equal('three');
|
expect(headers.get('one-two')).to.equal('three');
|
||||||
expect(headers.get('four-five')).to.equal('six');
|
expect(headers.get('four-five')).to.equal('six');
|
||||||
expect(headers.get('seven-eight')).to.equal('nine');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Child component cannot override headers object', async () => {
|
|
||||||
const app = await fixture.loadTestAdapterApp();
|
|
||||||
const request = new Request('http://example.com/child-tries-to-overwrite');
|
|
||||||
const response = await app.render(request);
|
|
||||||
const headers = response.headers;
|
|
||||||
expect(headers.get('seven-eight')).to.equal('nine');
|
|
||||||
const html = await response.text();
|
|
||||||
const $ = cheerioLoad(html);
|
|
||||||
expect($('#overwrite-error').html()).to.equal('true');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue