final pass at Outlet approach

This commit is contained in:
Nate Moore 2023-05-16 09:46:49 -05:00
parent d8608bb947
commit 976b6ccf6e
10 changed files with 74 additions and 43 deletions

View file

@ -0,0 +1 @@
Hello world!

View file

@ -1 +1,2 @@
/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" /> /// <reference types="astro/client" />

View file

@ -15,16 +15,31 @@ import { Outlet } from 'astro/components';
<h1>My Website</h1> <h1>My Website</h1>
</header> </header>
<slot name="nav" /> <Outlet id="nav">
<slot name="nav" />
</Outlet>
<main> <main>
{() => new Promise((resolve) => setTimeout(resolve, 1000))} {() => new Promise((resolve) => setTimeout(resolve, 1000))}
<slot /> <Outlet id="main">
<slot />
</Outlet>
{() => new Promise((resolve) => setTimeout(resolve, 1000))} {() => new Promise((resolve) => setTimeout(resolve, 1000))}
<slot name="tabs" />
</main> </main>
<style is:global>
nav ul {
list-style: none;
margin: 0;
padding: 0;
display: flex;
gap: 1rem;
}
a.active {
color: red;
}
</style>
<script> <script>
function getOutlets() { function getOutlets() {
const query = "//comment()[contains(., 'astro:outlet')]"; const query = "//comment()[contains(., 'astro:outlet')]";
@ -98,6 +113,7 @@ import { Outlet } from 'astro/components';
async function update() { async function update() {
const promises: any[] = []; const promises: any[] = [];
for (const [outlet, range] of Object.entries(getOutlets())) { for (const [outlet, range] of Object.entries(getOutlets())) {
console.log({ outlet });
promises.push( promises.push(
fetch(event.destination.url, { fetch(event.destination.url, {
headers: { 'x-astro-outlet': outlet }, headers: { 'x-astro-outlet': outlet },
@ -115,18 +131,5 @@ import { Outlet } from 'astro/components';
}); });
}); });
</script> </script>
<style is:global>
nav ul {
list-style: none;
margin: 0;
padding: 0;
display: flex;
gap: 1rem;
}
a.active {
color: red;
}
</style>
</body> </body>
</html> </html>

View file

@ -1,11 +1,18 @@
--- ---
import Layout from '../layouts/Default.astro'; import Layout from '../layouts/Default.astro';
import { getEntryBySlug } from 'astro:content';
const entry = await getEntryBySlug('posts', 'intro');
const { Content } = await entry.render();
--- ---
<Layout> <Layout>
<div> <div>
<h2>Home</h2> <h2>Home</h2>
<p>Streaming partials!</p> <p>Streaming partials!</p>
<Content />
</div> </div>
<nav slot="nav"> <nav slot="nav">

View file

@ -31,6 +31,11 @@ const { title, description } = Astro.props;
addEventListener('load', () => document.documentElement.classList.add('loaded')); addEventListener('load', () => document.documentElement.classList.add('loaded'));
</script> </script>
<script>
import listen from 'micromorph/spa';
listen();
</script>
<style> <style>
:root { :root {
--_placeholder-bg: linear-gradient(transparent, transparent); --_placeholder-bg: linear-gradient(transparent, transparent);

View file

@ -6,7 +6,7 @@ interface Props {
const { id } = Astro.props; const { id } = Astro.props;
// @ts-ignore untyped internals // @ts-ignore untyped internals
// $$result.outlets.set(id, $$slots.default); $$result.outlets.set(id, () => Astro.slots.render('default'));
--- ---
<Fragment set:html={`<!--astro:outlet ${id}-->`} /><slot /><Fragment <Fragment set:html={`<!--astro:outlet ${id}-->`} /><slot /><Fragment

View file

@ -113,11 +113,11 @@
"test:e2e:match": "playwright test -g" "test:e2e:match": "playwright test -g"
}, },
"dependencies": { "dependencies": {
"@astrojs/compiler": "^1.4.0", "@astrojs/compiler": "^1.4.1",
"@astrojs/language-server": "^1.0.0", "@astrojs/language-server": "^0.28.3",
"@astrojs/markdown-remark": "^2.2.0", "@astrojs/markdown-remark": "^2.1.4",
"@astrojs/telemetry": "^2.1.1", "@astrojs/telemetry": "^2.1.0",
"@astrojs/webapi": "^2.1.1", "@astrojs/webapi": "^2.1.0",
"@babel/core": "^7.18.2", "@babel/core": "^7.18.2",
"@babel/generator": "^7.18.2", "@babel/generator": "^7.18.2",
"@babel/parser": "^7.18.4", "@babel/parser": "^7.18.4",

View file

@ -83,7 +83,7 @@ export function createAstroComponentInstance(
const instance = new AstroComponentInstance(result, props, slots, factory); const instance = new AstroComponentInstance(result, props, slots, factory);
if (result._metadata.request.headers.has('x-astro-outlet')) { if (result._metadata.request.headers.has('x-astro-outlet')) {
for (const [name, slot] of Object.entries(slots)) { for (const [name, slot] of Object.entries(slots)) {
result.outletPropagators.set(`${factory.hash} ${name}`, slot); result.outletPropagators.set(name, slot);
} }
return instance; return instance;
} }

View file

@ -70,14 +70,13 @@ async function bufferHeadContent(result: SSRResult) {
// Recursively calls component instances that might have slots to be propagated up. // Recursively calls component instances that might have slots to be propagated up.
async function bufferSlottedContent(result: SSRResult, outlet: string) { async function bufferSlottedContent(result: SSRResult, outlet: string) {
const iterator = result.outletPropagators.entries(); const iterator = result.outletPropagators.entries();
let promises = [];
while (true) { while (true) {
const { value: [name, slot] = [], done } = iterator.next(); const { value: [name, slot] = [], done } = iterator.next();
if (done) { if (done) {
break; break;
} }
const returnValue = await slot(result); const returnValue = await slot(result);
result.outlets.set(name, returnValue); // TODO: exhaust all expressions
if (name === outlet) { if (name === outlet) {
break; break;
} }
@ -153,6 +152,8 @@ export async function renderPage(
result.scripts.clear(); result.scripts.clear();
await bufferSlottedContent(result, outlet); await bufferSlottedContent(result, outlet);
console.log(result.outlets);
if (!result.outlets.get(outlet)) { if (!result.outlets.get(outlet)) {
let init = result.response; let init = result.response;
let headers = new Headers(init.headers); let headers = new Headers(init.headers);

47
pnpm-lock.yaml generated
View file

@ -535,19 +535,19 @@ importers:
packages/astro: packages/astro:
dependencies: dependencies:
'@astrojs/compiler': '@astrojs/compiler':
specifier: ^1.4.0 specifier: ^1.4.1
version: 1.4.0 version: 1.4.1
'@astrojs/language-server': '@astrojs/language-server':
specifier: ^1.0.0 specifier: ^0.28.3
version: 1.0.0 version: 0.28.3
'@astrojs/markdown-remark': '@astrojs/markdown-remark':
specifier: ^2.2.0 specifier: ^2.1.4
version: link:../markdown/remark version: link:../markdown/remark
'@astrojs/telemetry': '@astrojs/telemetry':
specifier: ^2.1.1 specifier: ^2.1.0
version: link:../telemetry version: link:../telemetry
'@astrojs/webapi': '@astrojs/webapi':
specifier: ^2.1.1 specifier: ^2.1.0
version: link:../webapi version: link:../webapi
'@babel/core': '@babel/core':
specifier: ^7.18.2 specifier: ^7.18.2
@ -5393,20 +5393,22 @@ packages:
sisteransi: 1.0.5 sisteransi: 1.0.5
dev: false dev: false
/@astrojs/compiler@1.4.0: /@astrojs/compiler@0.31.4:
resolution: {integrity: sha512-Vav3a32Ct+omowV9X9kDM2ghWAvFdjZkv5BdvBjZCKYbFVT6//IZApDIVbHI1UPuLuD2sKyLWx2T+E7clqUJdg==} resolution: {integrity: sha512-6bBFeDTtPOn4jZaiD3p0f05MEGQL9pw2Zbfj546oFETNmjJFWO3nzHz6/m+P53calknCvyVzZ5YhoBLIvzn5iw==}
dev: false
/@astrojs/language-server@1.0.0: /@astrojs/compiler@1.4.1:
resolution: {integrity: sha512-oEw7AwJmzjgy6HC9f5IdrphZ1GVgfV/+7xQuyf52cpTiRWd/tJISK3MsKP0cDkVlfodmNABNFnAaAWuLZEiiiA==} resolution: {integrity: sha512-aXAxapNWZwGN41P+Am/ma/2kAzKOhMNaY6YuvLkUHFv+UZkmDHD6F0fE1sQA2Up0bLjgPQa1VQzoAaii5tZWaA==}
/@astrojs/language-server@0.28.3:
resolution: {integrity: sha512-fPovAX/X46eE2w03jNRMpQ7W9m2mAvNt4Ay65lD9wl1Z5vIQYxlg7Enp9qP225muTr4jSVB5QiLumFJmZMAaVA==}
hasBin: true hasBin: true
dependencies: dependencies:
'@astrojs/compiler': 1.4.0
'@jridgewell/trace-mapping': 0.3.18
'@vscode/emmet-helper': 2.8.8 '@vscode/emmet-helper': 2.8.8
events: 3.3.0 events: 3.3.0
prettier: 2.8.8 prettier: 2.8.8
prettier-plugin-astro: 0.8.0 prettier-plugin-astro: 0.7.2
synckit: 0.8.5 source-map: 0.7.4
vscode-css-languageservice: 6.2.5 vscode-css-languageservice: 6.2.5
vscode-html-languageservice: 5.0.5 vscode-html-languageservice: 5.0.5
vscode-languageserver: 8.1.0 vscode-languageserver: 8.1.0
@ -8436,7 +8438,7 @@ packages:
/@ts-morph/common@0.16.0: /@ts-morph/common@0.16.0:
resolution: {integrity: sha512-SgJpzkTgZKLKqQniCjLaE3c2L2sdL7UShvmTmPBejAKd2OKV/yfMpQ2IWpAuA+VY5wy7PkSUaEObIqEK6afFuw==} resolution: {integrity: sha512-SgJpzkTgZKLKqQniCjLaE3c2L2sdL7UShvmTmPBejAKd2OKV/yfMpQ2IWpAuA+VY5wy7PkSUaEObIqEK6afFuw==}
dependencies: dependencies:
fast-glob: 3.2.11 fast-glob: 3.2.12
minimatch: 5.1.6 minimatch: 5.1.6
mkdirp: 1.0.4 mkdirp: 1.0.4
path-browserify: 1.0.1 path-browserify: 1.0.1
@ -15247,14 +15249,25 @@ packages:
fast-diff: 1.2.0 fast-diff: 1.2.0
dev: true dev: true
/prettier-plugin-astro@0.7.2:
resolution: {integrity: sha512-mmifnkG160BtC727gqoimoxnZT/dwr8ASxpoGGl6EHevhfblSOeu+pwH1LAm5Qu1MynizktztFujHHaijLCkww==}
engines: {node: ^14.15.0 || >=16.0.0, pnpm: '>=7.14.0'}
dependencies:
'@astrojs/compiler': 0.31.4
prettier: 2.8.8
sass-formatter: 0.7.6
synckit: 0.8.5
dev: false
/prettier-plugin-astro@0.8.0: /prettier-plugin-astro@0.8.0:
resolution: {integrity: sha512-kt9wk33J7HvFGwFaHb8piwy4zbUmabC8Nu+qCw493jhe96YkpjscqGBPy4nJ9TPy9pd7+kEx1zM81rp+MIdrXg==} resolution: {integrity: sha512-kt9wk33J7HvFGwFaHb8piwy4zbUmabC8Nu+qCw493jhe96YkpjscqGBPy4nJ9TPy9pd7+kEx1zM81rp+MIdrXg==}
engines: {node: ^14.15.0 || >=16.0.0, pnpm: '>=7.14.0'} engines: {node: ^14.15.0 || >=16.0.0, pnpm: '>=7.14.0'}
dependencies: dependencies:
'@astrojs/compiler': 1.4.0 '@astrojs/compiler': 1.4.1
prettier: 2.8.8 prettier: 2.8.8
sass-formatter: 0.7.6 sass-formatter: 0.7.6
synckit: 0.8.5 synckit: 0.8.5
dev: true
/prettier@2.8.8: /prettier@2.8.8:
resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==}