-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #20 from bholmesdev/feat/simple-stack-stream
Welcome simple-stack-stream
- Loading branch information
Showing
33 changed files
with
318 additions
and
384 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"simple-stack-stream": patch | ||
--- | ||
|
||
Simple stream initial release. Who said suspense had to be hard? |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
--- | ||
type Props = { | ||
ms: number, | ||
class?: string, | ||
} | ||
const {ms, class: cls} = Astro.props; | ||
await new Promise(resolve => setTimeout(resolve, ms)); | ||
--- | ||
<div class:list={["rounded p-2 border-gray-300 border-2", cls]}> | ||
<slot /> | ||
<p class="italic text-gray-600 text-sm">Loaded in {ms}ms</p> | ||
</div> |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
/// <reference path="../.astro/types.d.ts" /> | ||
/// <reference types="astro/client" /> | ||
/// <reference types="simple-stack-form/types" /> |
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
--- | ||
import Wait from "../components/Wait.astro"; | ||
import { Suspense, ResolveSuspended } from 'simple-stack-stream/components' | ||
--- | ||
|
||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>Suspense</title> | ||
</head> | ||
<body> | ||
<h1>Out of order streaming</h1> | ||
<!-- out-of-order streaming: fallback, | ||
JS to swap content --> | ||
<Suspense> | ||
<Wait ms={2000}> | ||
<p class="font-bold text-xl">Content</p> | ||
</Wait> | ||
<p slot="fallback">Loading...</p> | ||
</Suspense> | ||
|
||
<!-- in-order HTML streaming (no client JS) --> | ||
<Wait class="fixed bottom-0 left-0 right-0" ms={500}> | ||
<footer> | ||
<p>Follow us</p> | ||
<p>Join the newsletter</p> | ||
</footer> | ||
</Wait> | ||
|
||
<!-- render all suspended content --> | ||
<ResolveSuspended /> | ||
</body> | ||
</html> |
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
# Simple stream 🌊 | ||
|
||
Suspend Astro components with fallback content. Like React Server Components, but Just HTML ™️ | ||
|
||
```astro | ||
--- | ||
import { Suspense, ResolveSuspended } from 'simple-stack-stream/components'; | ||
--- | ||
<h1>Simple stream</h1> | ||
https://github.com/bholmesdev/simple-stack/assets/51384119/99ed15a4-5a70-4f19-bc2a-712d4039c0a7 | ||
<!--Suspend slow-to-load content--> | ||
<Suspense> | ||
<VideoPlayer /> | ||
<!--Show fallback content--> | ||
<LoadingSkeleton slot="fallback" /> | ||
</Suspense> | ||
<Footer /> | ||
<!--Render suspended content--> | ||
<ResolveSuspended /> | ||
``` | ||
|
||
## Installation | ||
|
||
Simple stream is an Astro integration. You can install and configure this via the Astro CLI using `astro add`: | ||
|
||
```bash | ||
npm run astro add simple-stack-stream | ||
``` | ||
|
||
## Usage | ||
|
||
Simple stream exposes a "Suspense" utility to show fallback content while your server-side components load. | ||
|
||
### `Suspense` | ||
|
||
`<Suspense>` is a wrapper component for any content you want to load out-of-order with a fallback. Pass any suspended content as children, and use `slot="fallback"` to define your fallback: | ||
|
||
```astro | ||
--- | ||
import { Suspense } from 'simple-stack-stream/components'; | ||
--- | ||
<Suspense> | ||
<VideoPlayer /> | ||
<p slot="fallback">Loading...</p> | ||
</Suspense> | ||
``` | ||
|
||
⚠️ **Client JS is required** for suspended content to render. For progressive enhancement, we recommend including `<noscript>` content as part of your fallback: | ||
|
||
```astro | ||
--- | ||
import { Suspense } from 'simple-stack-stream/components'; | ||
--- | ||
<Suspense> | ||
<VideoPlayer /> | ||
<div slot="fallback"> | ||
<noscript>JavaScript is required for video playback.</noscript> | ||
<p>Loading...</p> | ||
</div> | ||
</Suspense> | ||
``` | ||
|
||
### `ResolveSuspended` | ||
|
||
The `<ResolveSuspended />` component renders all suspended content. This component should be placed at the _end_ of your HTML document, ideally before the closing `</body>` tag. This prevents `ResolveSuspended` from blocking components below it when [using Astro SSR](https://docs.astro.build/en/guides/server-side-rendering/#html-streaming). | ||
|
||
We recommend [a reusable Layout](https://docs.astro.build/en/core-concepts/layouts/) to ensure this component is present wherever `<Suspense>` is used: | ||
|
||
```astro | ||
--- | ||
// src/layouts/Layout.astro | ||
import { ResolveSuspended } from 'simple-stack-form/components'; | ||
--- | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head>...</head> | ||
<body> | ||
<slot /> | ||
<ResolveSuspended /> | ||
</body> | ||
</html> | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
--- | ||
import type { LocalsWithStreamInternals } from "./types"; | ||
const { stream } = Astro.locals as LocalsWithStreamInternals; | ||
const entries = [...stream._internal.components.entries()]; | ||
const resolvedEntries = await Promise.all( | ||
entries.map( | ||
async ([id, slotPromise]) => [id, await slotPromise], | ||
) | ||
); | ||
--- | ||
|
||
{ | ||
resolvedEntries.map( ([id, html]) => ( | ||
<> | ||
<template data-suspense-id={id} set:html={html} /> | ||
<script is:inline define:vars={{ id }}> | ||
const template = document.querySelector(`[data-suspense-id="${id}"]`).content; | ||
const dest = document.getElementById(id); | ||
dest.replaceWith(template); | ||
</script> | ||
</> | ||
)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
--- | ||
import { customAlphabet, urlAlphabet } from "nanoid"; | ||
import type { LocalsWithStreamInternals } from "./types"; | ||
const safeId = customAlphabet(urlAlphabet, 10); | ||
const slotPromise = Astro.slots.render('default'); | ||
const id = safeId(); | ||
const { stream } = Astro.locals as LocalsWithStreamInternals; | ||
stream._internal.components.set(id, slotPromise); | ||
--- | ||
|
||
<simple-suspense id={id}> | ||
<slot name="fallback" /> | ||
</simple-suspense> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/// <reference types="astro/client" /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { default as ResolveSuspended } from "./ResolveSuspended.astro"; | ||
export { default as Suspense } from "./Suspense.astro"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"extends": "astro/tsconfigs/strictest" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
export type LocalsWithStreamInternals = { | ||
stream: { | ||
_internal: { | ||
components: Map<string, Promise<string>>; | ||
}; | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
{ | ||
"name": "simple-stack-stream", | ||
"version": "0.0.1", | ||
"description": "Suspend your Astro components with fallback content.", | ||
"type": "module", | ||
"scripts": { | ||
"build": "tsc", | ||
"dev": "tsc --watch" | ||
}, | ||
"exports": { | ||
"./components": "./components/index.ts", | ||
"./middleware": "./dist/middleware.js", | ||
".": "./dist/index.js" | ||
}, | ||
"keywords": ["withastro", "astro-integration"], | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/bholmesdev/simple-stack.git", | ||
"directory": "packages/form" | ||
}, | ||
"devDependencies": { | ||
"astro": "^4.0.7", | ||
"typescript": "^5.3.3" | ||
}, | ||
"peerDependencies": { | ||
"astro": "^3.6.0 || ^4.0.0" | ||
}, | ||
"author": "bholmesdev", | ||
"license": "MIT", | ||
"dependencies": { | ||
"nanoid": "^5.0.3" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import type { AstroIntegration } from "astro"; | ||
|
||
export default function integration(): AstroIntegration { | ||
return { | ||
name: "simple-form", | ||
hooks: { | ||
"astro:config:setup"({ addMiddleware }) { | ||
addMiddleware({ | ||
entrypoint: "simple-stack-stream/middleware", | ||
order: "pre", | ||
}); | ||
}, | ||
}, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { defineMiddleware } from "astro/middleware"; | ||
|
||
export const onRequest = defineMiddleware(({ request, locals }, next) => { | ||
locals.stream = { | ||
_internal: { | ||
components: new Map(), | ||
}, | ||
}; | ||
|
||
return next(); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"extends": "../../tsconfig.base.json", | ||
"include": ["src"], | ||
"compilerOptions": { | ||
"outDir": "./dist", | ||
"rootDir": "./src" | ||
} | ||
} |
Oops, something went wrong.