Compare commits
3 commits
main
...
head-bubbl
Author | SHA1 | Date | |
---|---|---|---|
|
919e22d99a | ||
|
9fcc9fb526 | ||
|
515b378e73 |
13 changed files with 176 additions and 11 deletions
15
examples/head-bubbling/package.json
Normal file
15
examples/head-bubbling/package.json
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"name": "@example/head-bubbling",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"astro": "^2.0.11"
|
||||
}
|
||||
}
|
14
examples/head-bubbling/src/components/Post.astro
Normal file
14
examples/head-bubbling/src/components/Post.astro
Normal file
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
const title = 'Blog Post';
|
||||
const description = 'Some description';
|
||||
---
|
||||
|
||||
<head>
|
||||
<meta property="og:title" content={title} />
|
||||
<meta property="og:description" content={description} /></head>
|
||||
</head>
|
||||
|
||||
<article>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eget commodo lacus. Sed hendrerit vel tortor in viverra. Praesent a lectus ex. Cras hendrerit ligula in sapien euismod maximus. Duis vel consectetur nibh, sed tempus est. Nullam sit amet iaculis nisl. Suspendisse efficitur libero eu magna varius faucibus a a nulla. Aliquam pretium diam ut bibendum convallis. Nullam vel mi nunc. Duis rutrum odio a magna posuere, in semper nisl dictum. Proin malesuada arcu in mi finibus, eget blandit urna varius. Ut pulvinar malesuada euismod.</p>
|
||||
<p>Suspendisse sed erat neque. Donec est ante, venenatis ut urna a, efficitur finibus leo. Cras ut pellentesque mi. Sed non nunc tincidunt, euismod erat eu, fringilla augue. Nunc in sem a nisi luctus lacinia ut interdum turpis. Pellentesque at bibendum nunc. Etiam id felis in erat egestas ultrices. Proin cursus nulla et ligula elementum iaculis. Aenean volutpat ullamcorper purus, vitae elementum urna interdum at. Aenean ex elit, porta vitae dictum ac, mattis at justo. Aenean non augue tincidunt tellus aliquam condimentum. Nullam eu ante sed turpis lobortis iaculis.</p>
|
||||
</article>
|
1
examples/head-bubbling/src/env.d.ts
vendored
Normal file
1
examples/head-bubbling/src/env.d.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/// <reference types="astro/client" />
|
22
examples/head-bubbling/src/pages/index.astro
Normal file
22
examples/head-bubbling/src/pages/index.astro
Normal file
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
import Post from '../components/Post.astro';
|
||||
---
|
||||
<html>
|
||||
<head>
|
||||
<title>Index page</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: salmon;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>My Site</h1>
|
||||
|
||||
<Post />
|
||||
</body>
|
||||
</html>
|
|
@ -99,7 +99,7 @@
|
|||
"test:e2e:match": "playwright test -g"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/compiler": "^1.1.0",
|
||||
"@astrojs/compiler": "0.0.0-head-bubbling-20230216202235",
|
||||
"@astrojs/language-server": "^0.28.3",
|
||||
"@astrojs/markdown-remark": "^2.0.1",
|
||||
"@astrojs/telemetry": "^2.0.0",
|
||||
|
|
|
@ -43,6 +43,7 @@ export async function compile({
|
|||
internalURL: 'astro/server/index.js',
|
||||
astroGlobalArgs: JSON.stringify(astroConfig.site),
|
||||
resultScopedSlot: true,
|
||||
implicitHeadInjection: false,
|
||||
preprocessStyle: createStylePreprocessor({
|
||||
filename,
|
||||
viteConfig,
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
import { renderAllHeadContent } from './head.js';
|
||||
import { hasScopeFlag, ScopeFlags } from './scope.js';
|
||||
import { isSlotString, type SlotString } from './slot.js';
|
||||
import { renderChild } from './any.js';
|
||||
|
||||
export const Fragment = Symbol.for('astro:fragment');
|
||||
export const Renderer = Symbol.for('astro:renderer');
|
||||
|
@ -18,12 +19,16 @@ export const Renderer = Symbol.for('astro:renderer');
|
|||
export const encoder = new TextEncoder();
|
||||
export const decoder = new TextDecoder();
|
||||
|
||||
export function isRenderInstruction(chunk: unknown): chunk is RenderInstruction {
|
||||
return typeof chunk != null && typeof (chunk as any).type === 'string';
|
||||
}
|
||||
|
||||
// Rendering produces either marked strings of HTML or instructions for hydration.
|
||||
// These directive instructions bubble all the way up to renderPage so that we
|
||||
// can ensure they are added only once, and as soon as possible.
|
||||
export function stringifyChunk(result: SSRResult, chunk: string | SlotString | RenderInstruction) {
|
||||
if (typeof (chunk as any).type === 'string') {
|
||||
const instruction = chunk as RenderInstruction;
|
||||
if (isRenderInstruction(chunk)) {
|
||||
const instruction = chunk;
|
||||
switch (instruction.type) {
|
||||
case 'directive': {
|
||||
const { hydration } = instruction;
|
||||
|
@ -47,6 +52,7 @@ export function stringifyChunk(result: SSRResult, chunk: string | SlotString | R
|
|||
if (result._metadata.hasRenderedHead) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return renderAllHeadContent(result);
|
||||
}
|
||||
case 'maybe-head': {
|
||||
|
@ -147,3 +153,11 @@ export function chunkToByteArray(
|
|||
let stringified = stringifyChunk(result, chunk);
|
||||
return encoder.encode(stringified.toString());
|
||||
}
|
||||
|
||||
export async function renderToStringAsync(result: SSRResult, part: unknown): Promise<string> {
|
||||
let out = '';
|
||||
for await(const chunk of renderChild(part)) {
|
||||
out += stringifyChunk(result, chunk);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
|
|
@ -12,10 +12,11 @@ import {
|
|||
isRenderTemplateResult,
|
||||
renderAstroTemplateResult,
|
||||
} from './astro/index.js';
|
||||
import { chunkToByteArray, encoder, HTMLParts } from './common.js';
|
||||
import { chunkToByteArray, encoder, HTMLParts, renderToStringAsync } from './common.js';
|
||||
import { renderComponent } from './component.js';
|
||||
import { maybeRenderHead } from './head.js';
|
||||
import { createScopedResult, ScopeFlags } from './scope.js';
|
||||
import { renderChild } from './any.js';
|
||||
|
||||
const needsHeadRenderingSymbol = Symbol.for('astro.needsHeadRendering');
|
||||
|
||||
|
@ -64,7 +65,13 @@ async function bufferHeadContent(result: SSRResult) {
|
|||
}
|
||||
const returnValue = await value.init(scoped);
|
||||
if (isHeadAndContent(returnValue)) {
|
||||
result.extraHead.push(returnValue.head);
|
||||
if(typeof returnValue.head === 'string') {
|
||||
result.extraHead.push(returnValue.head);
|
||||
} else {
|
||||
const head = await renderToStringAsync(result, returnValue.head);
|
||||
result.extraHead.push(head);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -151,6 +158,7 @@ export async function renderPage(
|
|||
|
||||
const bytes = chunkToByteArray(result, chunk);
|
||||
controller.enqueue(bytes);
|
||||
|
||||
i++;
|
||||
}
|
||||
controller.close();
|
||||
|
|
15
packages/astro/test/fixtures/head-bubbling/package.json
vendored
Normal file
15
packages/astro/test/fixtures/head-bubbling/package.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"name": "@test/head-bubbling",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"astro": "workspace:*"
|
||||
}
|
||||
}
|
14
packages/astro/test/fixtures/head-bubbling/src/components/Post.astro
vendored
Normal file
14
packages/astro/test/fixtures/head-bubbling/src/components/Post.astro
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
const title = 'Blog Post';
|
||||
const description = 'Some description';
|
||||
---
|
||||
|
||||
<head>
|
||||
<meta property="og:title" content={title} />
|
||||
<meta property="og:description" content={description} /></head>
|
||||
</head>
|
||||
|
||||
<article>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eget commodo lacus. Sed hendrerit vel tortor in viverra. Praesent a lectus ex. Cras hendrerit ligula in sapien euismod maximus. Duis vel consectetur nibh, sed tempus est. Nullam sit amet iaculis nisl. Suspendisse efficitur libero eu magna varius faucibus a a nulla. Aliquam pretium diam ut bibendum convallis. Nullam vel mi nunc. Duis rutrum odio a magna posuere, in semper nisl dictum. Proin malesuada arcu in mi finibus, eget blandit urna varius. Ut pulvinar malesuada euismod.</p>
|
||||
<p>Suspendisse sed erat neque. Donec est ante, venenatis ut urna a, efficitur finibus leo. Cras ut pellentesque mi. Sed non nunc tincidunt, euismod erat eu, fringilla augue. Nunc in sem a nisi luctus lacinia ut interdum turpis. Pellentesque at bibendum nunc. Etiam id felis in erat egestas ultrices. Proin cursus nulla et ligula elementum iaculis. Aenean volutpat ullamcorper purus, vitae elementum urna interdum at. Aenean ex elit, porta vitae dictum ac, mattis at justo. Aenean non augue tincidunt tellus aliquam condimentum. Nullam eu ante sed turpis lobortis iaculis.</p>
|
||||
</article>
|
13
packages/astro/test/fixtures/head-bubbling/src/pages/index.astro
vendored
Normal file
13
packages/astro/test/fixtures/head-bubbling/src/pages/index.astro
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
import Post from '../components/Post.astro';
|
||||
---
|
||||
<html>
|
||||
<head>
|
||||
<title>Index page</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>My Site</h1>
|
||||
|
||||
<Post />
|
||||
</body>
|
||||
</html>
|
36
packages/astro/test/head-bubbling.test.js
Normal file
36
packages/astro/test/head-bubbling.test.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
import { expect } from 'chai';
|
||||
import * as cheerio from 'cheerio';
|
||||
import { loadFixture } from './test-utils.js';
|
||||
|
||||
describe('Head bubbling', () => {
|
||||
/** @type {import('./test-utils').Fixture} */
|
||||
let fixture;
|
||||
|
||||
before(async () => {
|
||||
fixture = await loadFixture({
|
||||
root: './fixtures/head-bubbling/',
|
||||
});
|
||||
await fixture.build();
|
||||
});
|
||||
|
||||
describe('index page', () => {
|
||||
/** @type {string} */
|
||||
let html;
|
||||
/** @type {ReturnType<typeof cheerio.load>} */
|
||||
let $;
|
||||
before(async () => {
|
||||
html = await fixture.readFile(`/index.html`);
|
||||
$ = cheerio.load(html);
|
||||
});
|
||||
|
||||
it('Renders component head contents into the head', async () => {
|
||||
const $metas = $('head meta');
|
||||
|
||||
expect($metas).to.have.a.lengthOf(2);
|
||||
});
|
||||
|
||||
it('Body contents in the body', async () => {
|
||||
expect($('body article')).to.have.a.lengthOf(1);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -243,6 +243,12 @@ importers:
|
|||
'@astrojs/node': link:../../packages/integrations/node
|
||||
astro: link:../../packages/astro
|
||||
|
||||
examples/head-bubbling:
|
||||
specifiers:
|
||||
astro: ^2.0.11
|
||||
dependencies:
|
||||
astro: link:../../packages/astro
|
||||
|
||||
examples/integration:
|
||||
specifiers:
|
||||
astro: ^2.0.11
|
||||
|
@ -375,7 +381,7 @@ importers:
|
|||
|
||||
packages/astro:
|
||||
specifiers:
|
||||
'@astrojs/compiler': ^1.1.0
|
||||
'@astrojs/compiler': 0.0.0-head-bubbling-20230216202235
|
||||
'@astrojs/language-server': ^0.28.3
|
||||
'@astrojs/markdown-remark': ^2.0.1
|
||||
'@astrojs/telemetry': ^2.0.0
|
||||
|
@ -465,7 +471,7 @@ importers:
|
|||
yargs-parser: ^21.0.1
|
||||
zod: ^3.17.3
|
||||
dependencies:
|
||||
'@astrojs/compiler': 1.1.0
|
||||
'@astrojs/compiler': 0.0.0-head-bubbling-20230216202235
|
||||
'@astrojs/language-server': 0.28.3
|
||||
'@astrojs/markdown-remark': link:../markdown/remark
|
||||
'@astrojs/telemetry': link:../telemetry
|
||||
|
@ -1857,6 +1863,12 @@ importers:
|
|||
dependencies:
|
||||
astro: link:../../..
|
||||
|
||||
packages/astro/test/fixtures/head-bubbling:
|
||||
specifiers:
|
||||
astro: workspace:*
|
||||
dependencies:
|
||||
astro: link:../../..
|
||||
|
||||
packages/astro/test/fixtures/head-injection:
|
||||
specifiers:
|
||||
astro: workspace:*
|
||||
|
@ -3902,13 +3914,13 @@ packages:
|
|||
sisteransi: 1.0.5
|
||||
dev: false
|
||||
|
||||
/@astrojs/compiler/0.0.0-head-bubbling-20230216202235:
|
||||
resolution: {integrity: sha512-3IFx8Y3T16yjlVLSElAOVvLrCFSs9pmLBD9quLM48MyKmJjamPmIctTGJ02ixNCOcZZrZPBjEPefTyrqNQ64Zw==}
|
||||
dev: false
|
||||
|
||||
/@astrojs/compiler/0.31.4:
|
||||
resolution: {integrity: sha512-6bBFeDTtPOn4jZaiD3p0f05MEGQL9pw2Zbfj546oFETNmjJFWO3nzHz6/m+P53calknCvyVzZ5YhoBLIvzn5iw==}
|
||||
|
||||
/@astrojs/compiler/1.1.0:
|
||||
resolution: {integrity: sha512-C4kTwirys+HafufMqaxCbML2wqkGaXJM+5AekXh/v1IIOnMIdcEON9GBYsG6qa8aAmLhZ58aUZGPhzcA3Dx7Uw==}
|
||||
dev: false
|
||||
|
||||
/@astrojs/language-server/0.28.3:
|
||||
resolution: {integrity: sha512-fPovAX/X46eE2w03jNRMpQ7W9m2mAvNt4Ay65lD9wl1Z5vIQYxlg7Enp9qP225muTr4jSVB5QiLumFJmZMAaVA==}
|
||||
hasBin: true
|
||||
|
|
Loading…
Reference in a new issue