@funstack/static 0.0.10 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/bin/skill-installer.mjs +5 -4
- package/dist/bin/skill-installer.mjs.map +1 -1
- package/dist/build/buildApp.mjs +7 -8
- package/dist/build/buildApp.mjs.map +1 -1
- package/dist/build/contentHash.mjs +1 -1
- package/dist/build/dependencyGraph.mjs +1 -1
- package/dist/build/dependencyGraph.mjs.map +1 -1
- package/dist/build/rscPath.mjs +3 -4
- package/dist/build/rscPath.mjs.map +1 -1
- package/dist/build/rscProcessor.mjs +4 -4
- package/dist/build/rscProcessor.mjs.map +1 -1
- package/dist/build/validateEntryPath.mjs +1 -1
- package/dist/client/entry.d.mts +1 -1
- package/dist/client/entry.mjs +2 -3
- package/dist/client/entry.mjs.map +1 -1
- package/dist/client/error-boundary.mjs +2 -4
- package/dist/client/error-boundary.mjs.map +1 -1
- package/dist/client/globals.mjs +2 -4
- package/dist/client/globals.mjs.map +1 -1
- package/dist/docs/GettingStarted.md +10 -6
- package/dist/docs/MigratingFromViteSPA.md +5 -5
- package/dist/docs/{learn → advanced}/MultipleEntrypoints.md +14 -42
- package/dist/docs/{learn → advanced}/SSR.md +3 -3
- package/dist/docs/api/EntryDefinition.md +2 -2
- package/dist/docs/api/FunstackStatic.md +26 -7
- package/dist/docs/index.md +5 -2
- package/dist/docs/learn/DeferAndActivity.md +3 -3
- package/dist/docs/learn/HowItWorks.md +3 -3
- package/dist/docs/learn/LazyServerComponents.md +3 -3
- package/dist/docs/learn/OptimizingPayloads.md +4 -4
- package/dist/docs/learn/RSC.md +3 -3
- package/dist/entries/client.d.mts +1 -1
- package/dist/entries/client.mjs +1 -2
- package/dist/entries/rsc-client.mjs +1 -3
- package/dist/entries/rsc.mjs +1 -2
- package/dist/entries/server.mjs +1 -2
- package/dist/entries/ssr.mjs +1 -2
- package/dist/entryDefinition.mjs +1 -1
- package/dist/index.mjs +1 -2
- package/dist/plugin/getRSCEntryPoint.mjs +1 -1
- package/dist/plugin/getRSCEntryPoint.mjs.map +1 -1
- package/dist/plugin/index.d.mts +14 -0
- package/dist/plugin/index.d.mts.map +1 -1
- package/dist/plugin/index.mjs +6 -5
- package/dist/plugin/index.mjs.map +1 -1
- package/dist/plugin/server.mjs +1 -2
- package/dist/plugin/server.mjs.map +1 -1
- package/dist/rsc/defer.d.mts.map +1 -1
- package/dist/rsc/defer.mjs +3 -3
- package/dist/rsc/defer.mjs.map +1 -1
- package/dist/rsc/entry.mjs +4 -5
- package/dist/rsc/entry.mjs.map +1 -1
- package/dist/rsc/marker.mjs +1 -1
- package/dist/rsc/request.mjs +1 -1
- package/dist/rsc/resolveEntry.mjs +1 -2
- package/dist/rsc/resolveEntry.mjs.map +1 -1
- package/dist/rsc/rscModule.mjs +8 -8
- package/dist/rsc/rscModule.mjs.map +1 -1
- package/dist/rsc-client/clientWrapper.mjs +2 -3
- package/dist/rsc-client/clientWrapper.mjs.map +1 -1
- package/dist/rsc-client/entry.d.mts +1 -0
- package/dist/rsc-client/entry.mjs +2 -4
- package/dist/ssr/entry.mjs +2 -2
- package/dist/ssr/entry.mjs.map +1 -1
- package/dist/util/basePath.mjs +1 -1
- package/dist/util/drainStream.mjs +1 -1
- package/dist/util/urlPath.mjs +1 -1
- package/package.json +5 -5
- package/skills/funstack-static-knowledge/SKILL.md +1 -1
|
@@ -51,7 +51,7 @@ export default defineConfig({
|
|
|
51
51
|
});
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
See [Multiple Entrypoints](/
|
|
54
|
+
See [Multiple Entrypoints](/advanced/multiple-entrypoints) for a full guide.
|
|
55
55
|
|
|
56
56
|
## Options
|
|
57
57
|
|
|
@@ -158,7 +158,7 @@ export default function getEntries(): EntryDefinition[] {
|
|
|
158
158
|
}
|
|
159
159
|
```
|
|
160
160
|
|
|
161
|
-
See [Multiple Entrypoints](/
|
|
161
|
+
See [Multiple Entrypoints](/advanced/multiple-entrypoints) for details on the `EntryDefinition` type and advanced usage patterns like async generators.
|
|
162
162
|
|
|
163
163
|
### publicOutDir (optional)
|
|
164
164
|
|
|
@@ -226,13 +226,32 @@ Sentry.init({
|
|
|
226
226
|
|
|
227
227
|
**Note:** Errors in the client init module will propagate normally and prevent the app from rendering.
|
|
228
228
|
|
|
229
|
+
### rscPayloadDir (optional)
|
|
230
|
+
|
|
231
|
+
**Type:** `string`
|
|
232
|
+
**Default:** `"fun:rsc-payload"`
|
|
233
|
+
|
|
234
|
+
Directory name used for RSC payload files in the build output. The final file paths follow the pattern `/funstack__/{rscPayloadDir}/{hash}.txt`.
|
|
235
|
+
|
|
236
|
+
Change this if your hosting platform has issues with the default directory name. For example, Cloudflare Workers redirects URLs containing colons to percent-encoded equivalents, adding an extra round trip.
|
|
237
|
+
|
|
238
|
+
**Important:** The value is used as a marker for string replacement during the build process. Choose a value that is unique enough that it does not appear in your application's source code. The default value `"fun:rsc-payload"` is designed to be unlikely to collide with user code.
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
funstackStatic({
|
|
242
|
+
root: "./src/root.tsx",
|
|
243
|
+
app: "./src/App.tsx",
|
|
244
|
+
rscPayloadDir: "fun-rsc-payload", // Avoid colons for Cloudflare Workers
|
|
245
|
+
});
|
|
246
|
+
```
|
|
247
|
+
|
|
229
248
|
## Full Example
|
|
230
249
|
|
|
231
250
|
### Single-Entry
|
|
232
251
|
|
|
233
252
|
```typescript
|
|
234
253
|
// vite.config.ts
|
|
235
|
-
import
|
|
254
|
+
import funstackStatic from "@funstack/static";
|
|
236
255
|
import { defineConfig } from "vite";
|
|
237
256
|
|
|
238
257
|
export default defineConfig({
|
|
@@ -274,7 +293,7 @@ You can use the same Vite commands you would use in a normal Vite project:
|
|
|
274
293
|
|
|
275
294
|
## See Also
|
|
276
295
|
|
|
277
|
-
- [Getting Started](/
|
|
278
|
-
- [Multiple Entrypoints](/
|
|
279
|
-
- [defer()](/
|
|
280
|
-
- [React Server Components](/
|
|
296
|
+
- [Getting Started](/getting-started) - Quick start guide
|
|
297
|
+
- [Multiple Entrypoints](/advanced/multiple-entrypoints) - Multi-page static site generation
|
|
298
|
+
- [defer()](/api/defer) - Deferred rendering for streaming
|
|
299
|
+
- [React Server Components](/learn/rsc) - Understanding RSC
|
package/dist/docs/index.md
CHANGED
|
@@ -8,6 +8,11 @@ A Vite plugin for building static sites with React Server Components.
|
|
|
8
8
|
- [Getting Started](./GettingStarted.md) - Welcome to **FUNSTACK Static**! Build high-performance Single Page Applications powered by React Server Components - no server required at runtime.
|
|
9
9
|
- [Migrating from Vite SPA](./MigratingFromViteSPA.md) - Already have a Vite-powered React SPA? This guide walks you through migrating to FUNSTACK Static to unlock React Server Components and improved performance.
|
|
10
10
|
|
|
11
|
+
### Advanced
|
|
12
|
+
|
|
13
|
+
- [Multiple Entrypoints (SSG)](./advanced/MultipleEntrypoints.md) - By default, FUNSTACK Static produces a single `index.html` from one `root` + `app` pair. The **multiple entries** feature lets you produce multiple HTML pages from a single project, targeting SSG (Static Site Generation) use cases where a site has distinct pages like `index.html`, `about.html`, and `blog/post-1.html`.
|
|
14
|
+
- [Server-Side Rendering](./advanced/SSR.md) - In FUNSTACK Static, **Server-Side Rendering (SSR)** means a build-time process that pre-renders your React components (including client components) to HTML. This can make the initial paint faster.
|
|
15
|
+
|
|
11
16
|
### API
|
|
12
17
|
|
|
13
18
|
- [defer()](./api/Defer.md) - The `defer()` function enables deferred rendering for React Server Components, reducing initial data load.
|
|
@@ -19,7 +24,5 @@ A Vite plugin for building static sites with React Server Components.
|
|
|
19
24
|
- [Prefetching with defer() and Activity](./learn/DeferAndActivity.md) - When using `defer()` to split RSC payloads, content is fetched on-demand as it renders. But what if you want to start fetching _before_ the user actually needs it? By combining `defer()` with React 19's `<Activity>` component, you can prefetch deferred payloads in the background so they're ready instantly when revealed.
|
|
20
25
|
- [How It Works](./learn/HowItWorks.md) - FUNSTACK Static is a React framework that leverages React Server Components (RSC) to build a fully static Single Page Application. The result is a set of files that can be deployed to **any static file hosting service** - no server required at runtime.
|
|
21
26
|
- [Using lazy() in Server Components](./learn/LazyServerComponents.md) - React's `lazy()` API is typically associated with client-side code splitting. However, it can also be used in server environments to reduce the initial response time of the development server by deferring the work needed to compute your application.
|
|
22
|
-
- [Multiple Entrypoints](./learn/MultipleEntrypoints.md) - By default, FUNSTACK Static produces a single `index.html` from one `root` + `app` pair. The **multiple entries** feature lets you produce multiple HTML pages from a single project, targeting SSG (Static Site Generation) use cases where a site has distinct pages like `index.html`, `about.html`, and `blog/post-1.html`.
|
|
23
27
|
- [Optimizing RSC Payloads](./learn/OptimizingPayloads.md) - FUNSTACK Static uses React Server Components (RSC) to pre-render your application at build time. By default, all content is bundled into a single RSC payload. This page explains how to split that payload into smaller chunks for better loading performance.
|
|
24
28
|
- [React Server Components](./learn/RSC.md) - [React Server Components (RSC)](https://react.dev/reference/rsc/server-components) are a new paradigm for building React applications where components can run on the server (or at build time) rather than in the browser.
|
|
25
|
-
- [Server-Side Rendering](./learn/SSR.md) - In FUNSTACK Static, **Server-Side Rendering (SSR)** means a build-time process that pre-renders your React components (including client components) to HTML. This can make the initial paint faster.
|
|
@@ -171,6 +171,6 @@ Avoid this pattern when:
|
|
|
171
171
|
|
|
172
172
|
## See Also
|
|
173
173
|
|
|
174
|
-
- [Optimizing RSC Payloads](/
|
|
175
|
-
- [defer()](/
|
|
176
|
-
- [React Server Components](/
|
|
174
|
+
- [Optimizing RSC Payloads](/learn/optimizing-payloads) - Using `defer()` to split RSC payloads
|
|
175
|
+
- [defer()](/api/defer) - API reference with full signature and technical details
|
|
176
|
+
- [React Server Components](/learn/rsc) - Understanding RSC fundamentals
|
|
@@ -50,7 +50,7 @@ dist/public
|
|
|
50
50
|
└── index.html
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
The RSC payload files under `funstack__` are loaded by the client-side code to bootstrap the application with server-rendered content.
|
|
53
|
+
The RSC payload files under `funstack__` are loaded by the client-side code to bootstrap the application with server-rendered content. The `fun:rsc-payload` directory name is [configurable](/api/funstack-static#rscpayloaddir-optional) via the `rscPayloadDir` option.
|
|
54
54
|
|
|
55
55
|
This can been seen as an **optimized version of traditional client-only SPAs**, where the entire application is bundled into JavaScript files. By using RSC, some of the rendering work is offloaded to the build time, resulting in smaller JavaScript bundles combined with RSC payloads that require less client-side processing (parsing is easier, no JavaScript execution needed).
|
|
56
56
|
|
|
@@ -105,5 +105,5 @@ Note that in both modes, React Server Components are still used - the `ssr` opti
|
|
|
105
105
|
|
|
106
106
|
## See Also
|
|
107
107
|
|
|
108
|
-
- [React Server Components](/
|
|
109
|
-
- [Getting Started](/
|
|
108
|
+
- [React Server Components](/learn/rsc) - Understanding RSC in depth
|
|
109
|
+
- [Getting Started](/getting-started) - Set up your first project
|
|
@@ -115,6 +115,6 @@ For small applications with few routes, the overhead of `lazy()` may not be wort
|
|
|
115
115
|
|
|
116
116
|
## See Also
|
|
117
117
|
|
|
118
|
-
- [Optimizing RSC Payloads](/
|
|
119
|
-
- [How It Works](/
|
|
120
|
-
- [defer()](/
|
|
118
|
+
- [Optimizing RSC Payloads](/learn/optimizing-payloads) - Using `defer()` to split RSC payloads
|
|
119
|
+
- [How It Works](/learn/how-it-works) - Overall FUNSTACK Static architecture
|
|
120
|
+
- [defer()](/api/defer) - API reference for the defer function
|
|
@@ -98,10 +98,10 @@ Each payload file has a **content-based hash** in its filename. This enables agg
|
|
|
98
98
|
|
|
99
99
|
**Consider below-the-fold content** - Content hidden in collapsed sections, tabs, or modals is a good candidate for `defer()` since users may never need it.
|
|
100
100
|
|
|
101
|
-
> **Tip:** You can combine `defer()` with React 19's `<Activity>` component to prefetch content in the background. When a `defer()`ed node is rendered under `<Activity mode="hidden">`, it won't be shown in the UI, but the fetch of the RSC payload will start immediately. This is useful when hidden content (like inactive tabs or collapsed sections) should be fetched ahead of time so it's ready when the user needs it. See [Prefetching with defer() and Activity](/
|
|
101
|
+
> **Tip:** You can combine `defer()` with React 19's `<Activity>` component to prefetch content in the background. When a `defer()`ed node is rendered under `<Activity mode="hidden">`, it won't be shown in the UI, but the fetch of the RSC payload will start immediately. This is useful when hidden content (like inactive tabs or collapsed sections) should be fetched ahead of time so it's ready when the user needs it. See [Prefetching with defer() and Activity](/learn/defer-and-activity) for a detailed guide.
|
|
102
102
|
|
|
103
103
|
## See Also
|
|
104
104
|
|
|
105
|
-
- [defer()](/
|
|
106
|
-
- [How It Works](/
|
|
107
|
-
- [React Server Components](/
|
|
105
|
+
- [defer()](/api/defer) - API reference with full signature and technical details
|
|
106
|
+
- [How It Works](/learn/how-it-works) - Overall FUNSTACK Static architecture
|
|
107
|
+
- [React Server Components](/learn/rsc) - Understanding RSC fundamentals
|
package/dist/docs/learn/RSC.md
CHANGED
|
@@ -174,6 +174,6 @@ This makes FUNSTACK Static ideal for developers looking to leverage RSC benefits
|
|
|
174
174
|
|
|
175
175
|
## See Also
|
|
176
176
|
|
|
177
|
-
- [Getting Started](/
|
|
178
|
-
- [defer()](/
|
|
179
|
-
- [funstackStatic()](/
|
|
177
|
+
- [Getting Started](/getting-started) - Set up your first project
|
|
178
|
+
- [defer()](/api/defer) - Stream content progressively
|
|
179
|
+
- [funstackStatic()](/api/funstack-static) - Plugin configuration
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
export { };
|
package/dist/entries/client.mjs
CHANGED
package/dist/entries/rsc.mjs
CHANGED
package/dist/entries/server.mjs
CHANGED
package/dist/entries/ssr.mjs
CHANGED
package/dist/entryDefinition.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {};
|
package/dist/index.mjs
CHANGED
|
@@ -10,7 +10,7 @@ async function getRSCEntryPoint(environment) {
|
|
|
10
10
|
if (!resolved) throw new Error(`Cannot resolve RSC entry: ${source}`);
|
|
11
11
|
return await environment.runner.import(resolved.id);
|
|
12
12
|
}
|
|
13
|
-
|
|
14
13
|
//#endregion
|
|
15
14
|
export { getRSCEntryPoint };
|
|
15
|
+
|
|
16
16
|
//# sourceMappingURL=getRSCEntryPoint.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getRSCEntryPoint.mjs","names":[],"sources":["../../src/plugin/getRSCEntryPoint.ts"],"sourcesContent":["import type { RunnableDevEnvironment } from \"vite\";\n\n/**\n * Get the entry point module of the RSC environment.\n */\nexport async function getRSCEntryPoint(environment: RunnableDevEnvironment) {\n const rscInput = environment.config.build.rollupOptions?.input;\n const source =\n rscInput !== undefined &&\n typeof rscInput !== \"string\" &&\n !Array.isArray(rscInput)\n ? rscInput.index\n : undefined;\n if (source === undefined) {\n throw new Error(\"Cannot determine RSC entry point\");\n }\n const resolved = await environment.pluginContainer.resolveId(source);\n if (!resolved) {\n throw new Error(`Cannot resolve RSC entry: ${source}`);\n }\n const rscEntry = await environment.runner.import<\n typeof import(\"../rsc/entry\")\n >(resolved.id);\n return rscEntry;\n}\n"],"mappings":";;;;AAKA,eAAsB,iBAAiB,aAAqC;CAC1E,MAAM,WAAW,YAAY,OAAO,MAAM,eAAe;CACzD,MAAM,SACJ,aAAa,
|
|
1
|
+
{"version":3,"file":"getRSCEntryPoint.mjs","names":[],"sources":["../../src/plugin/getRSCEntryPoint.ts"],"sourcesContent":["import type { RunnableDevEnvironment } from \"vite\";\n\n/**\n * Get the entry point module of the RSC environment.\n */\nexport async function getRSCEntryPoint(environment: RunnableDevEnvironment) {\n const rscInput = environment.config.build.rollupOptions?.input;\n const source =\n rscInput !== undefined &&\n typeof rscInput !== \"string\" &&\n !Array.isArray(rscInput)\n ? rscInput.index\n : undefined;\n if (source === undefined) {\n throw new Error(\"Cannot determine RSC entry point\");\n }\n const resolved = await environment.pluginContainer.resolveId(source);\n if (!resolved) {\n throw new Error(`Cannot resolve RSC entry: ${source}`);\n }\n const rscEntry = await environment.runner.import<\n typeof import(\"../rsc/entry\")\n >(resolved.id);\n return rscEntry;\n}\n"],"mappings":";;;;AAKA,eAAsB,iBAAiB,aAAqC;CAC1E,MAAM,WAAW,YAAY,OAAO,MAAM,eAAe;CACzD,MAAM,SACJ,aAAa,KAAA,KACb,OAAO,aAAa,YACpB,CAAC,MAAM,QAAQ,SAAS,GACpB,SAAS,QACT,KAAA;AACN,KAAI,WAAW,KAAA,EACb,OAAM,IAAI,MAAM,mCAAmC;CAErD,MAAM,WAAW,MAAM,YAAY,gBAAgB,UAAU,OAAO;AACpE,KAAI,CAAC,SACH,OAAM,IAAI,MAAM,6BAA6B,SAAS;AAKxD,QAHiB,MAAM,YAAY,OAAO,OAExC,SAAS,GAAG"}
|
package/dist/plugin/index.d.mts
CHANGED
|
@@ -22,6 +22,20 @@ interface FunstackStaticBaseOptions {
|
|
|
22
22
|
* The module is imported for its side effects only (no exports needed).
|
|
23
23
|
*/
|
|
24
24
|
clientInit?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Directory name used for RSC payload files in the build output.
|
|
27
|
+
* The final path will be `/funstack__/{rscPayloadDir}/{hash}.txt`.
|
|
28
|
+
*
|
|
29
|
+
* Change this if your hosting platform has issues with the default
|
|
30
|
+
* directory name (e.g. Cloudflare Workers redirects URLs containing colons).
|
|
31
|
+
*
|
|
32
|
+
* The value is used as a marker for string replacement during the build
|
|
33
|
+
* process, so it should be unique enough that it does not appear in your
|
|
34
|
+
* application's source code.
|
|
35
|
+
*
|
|
36
|
+
* @default "fun:rsc-payload"
|
|
37
|
+
*/
|
|
38
|
+
rscPayloadDir?: string;
|
|
25
39
|
}
|
|
26
40
|
interface SingleEntryOptions {
|
|
27
41
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/plugin/index.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/plugin/index.ts"],"mappings":";;;UAOU,yBAAA;;AANyB;;;;EAYjC,YAAA;EAQA;;;;;AAoBa;;EApBb,GAAA;EAuB0B;;;;;EAjB1B,UAAA;EAgCQ;;;;;;;;;AAUV;;;;EA5BE,aAAA;AAAA;AAAA,UAGQ,kBAAA;EA0BoC;;;;;EApB5C,IAAA;EAsBsB;;;;EAjBtB,GAAA;EACA,OAAA;AAAA;AAAA,UAGQ,sBAAA;EACR,IAAA;EACA,GAAA;EAaE;;;;EARF,OAAA;AAAA;AAAA,KAGU,qBAAA,GAAwB,yBAAA,IACjC,kBAAA,GAAqB,sBAAA;AAAA,iBAEA,cAAA,CACtB,OAAA,EAAS,qBAAA,IACP,MAAA,GAAS,MAAA"}
|
package/dist/plugin/index.mjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
+
import { defaultRscPayloadDir } from "../rsc/rscModule.mjs";
|
|
1
2
|
import { buildApp } from "../build/buildApp.mjs";
|
|
2
3
|
import { serverPlugin } from "./server.mjs";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import rsc from "@vitejs/plugin-rsc";
|
|
5
|
-
|
|
6
6
|
//#region src/plugin/index.ts
|
|
7
7
|
function funstackStatic(options) {
|
|
8
|
-
const { publicOutDir = "dist/public", ssr = false, clientInit } = options;
|
|
8
|
+
const { publicOutDir = "dist/public", ssr = false, clientInit, rscPayloadDir = defaultRscPayloadDir } = options;
|
|
9
|
+
if (!rscPayloadDir || rscPayloadDir.includes("/") || rscPayloadDir.includes("\\") || rscPayloadDir === ".." || rscPayloadDir === ".") throw new Error(`[funstack] Invalid rscPayloadDir: "${rscPayloadDir}". Must be a non-empty single path segment without slashes.`);
|
|
9
10
|
let resolvedEntriesModule = "__uninitialized__";
|
|
10
11
|
let resolvedClientInitEntry;
|
|
11
12
|
const isMultiEntry = "entries" in options && options.entries !== void 0;
|
|
@@ -68,7 +69,7 @@ function funstackStatic(options) {
|
|
|
68
69
|
`}`
|
|
69
70
|
].join("\n");
|
|
70
71
|
}
|
|
71
|
-
if (id === "\0virtual:funstack/config") return `export const ssr = ${JSON.stringify(ssr)}
|
|
72
|
+
if (id === "\0virtual:funstack/config") return [`export const ssr = ${JSON.stringify(ssr)};`, `export const rscPayloadDir = ${JSON.stringify(rscPayloadDir)};`].join("\n");
|
|
72
73
|
if (id === "\0virtual:funstack/client-init") {
|
|
73
74
|
if (resolvedClientInitEntry) return `import "${resolvedClientInitEntry}";`;
|
|
74
75
|
return "";
|
|
@@ -78,12 +79,12 @@ function funstackStatic(options) {
|
|
|
78
79
|
{
|
|
79
80
|
name: "@funstack/static:build",
|
|
80
81
|
async buildApp(builder) {
|
|
81
|
-
await buildApp(builder, this);
|
|
82
|
+
await buildApp(builder, this, { rscPayloadDir });
|
|
82
83
|
}
|
|
83
84
|
}
|
|
84
85
|
];
|
|
85
86
|
}
|
|
86
|
-
|
|
87
87
|
//#endregion
|
|
88
88
|
export { funstackStatic as default };
|
|
89
|
+
|
|
89
90
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/plugin/index.ts"],"sourcesContent":["import path from \"node:path\";\nimport type { Plugin } from \"vite\";\nimport rsc from \"@vitejs/plugin-rsc\";\nimport { buildApp } from \"../build/buildApp\";\nimport { serverPlugin } from \"./server\";\n\ninterface FunstackStaticBaseOptions {\n /**\n * Output directory for build.\n *\n * @default dist/public\n */\n publicOutDir?: string;\n /**\n * Enable server-side rendering of the App component.\n * When false, only the Root shell is SSR'd and the App renders client-side.\n * When true, both Root and App are SSR'd and the client hydrates.\n *\n * @default false\n */\n ssr?: boolean;\n /**\n * Path to a module that runs on the client side before React hydration.\n * Use this for client-side instrumentation like Sentry, analytics, or feature flags.\n * The module is imported for its side effects only (no exports needed).\n */\n clientInit?: string;\n}\n\ninterface SingleEntryOptions {\n /**\n * Root component of the page.\n * The file should `export default` a React component that renders the whole page.\n * (`<html>...</html>`).\n */\n root: string;\n /**\n * Entry point of your application.\n * The file should `export default` a React component that renders the application content.\n */\n app: string;\n entries?: never;\n}\n\ninterface MultipleEntriesOptions {\n root?: never;\n app?: never;\n /**\n * Path to a module that exports a function returning entry definitions.\n * Mutually exclusive with `root`+`app`.\n */\n entries: string;\n}\n\nexport type FunstackStaticOptions = FunstackStaticBaseOptions &\n (SingleEntryOptions | MultipleEntriesOptions);\n\nexport default function funstackStatic(\n options: FunstackStaticOptions,\n): (Plugin | Plugin[])[] {\n const {
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/plugin/index.ts"],"sourcesContent":["import path from \"node:path\";\nimport type { Plugin } from \"vite\";\nimport rsc from \"@vitejs/plugin-rsc\";\nimport { buildApp } from \"../build/buildApp\";\nimport { serverPlugin } from \"./server\";\nimport { defaultRscPayloadDir } from \"../rsc/rscModule\";\n\ninterface FunstackStaticBaseOptions {\n /**\n * Output directory for build.\n *\n * @default dist/public\n */\n publicOutDir?: string;\n /**\n * Enable server-side rendering of the App component.\n * When false, only the Root shell is SSR'd and the App renders client-side.\n * When true, both Root and App are SSR'd and the client hydrates.\n *\n * @default false\n */\n ssr?: boolean;\n /**\n * Path to a module that runs on the client side before React hydration.\n * Use this for client-side instrumentation like Sentry, analytics, or feature flags.\n * The module is imported for its side effects only (no exports needed).\n */\n clientInit?: string;\n /**\n * Directory name used for RSC payload files in the build output.\n * The final path will be `/funstack__/{rscPayloadDir}/{hash}.txt`.\n *\n * Change this if your hosting platform has issues with the default\n * directory name (e.g. Cloudflare Workers redirects URLs containing colons).\n *\n * The value is used as a marker for string replacement during the build\n * process, so it should be unique enough that it does not appear in your\n * application's source code.\n *\n * @default \"fun:rsc-payload\"\n */\n rscPayloadDir?: string;\n}\n\ninterface SingleEntryOptions {\n /**\n * Root component of the page.\n * The file should `export default` a React component that renders the whole page.\n * (`<html>...</html>`).\n */\n root: string;\n /**\n * Entry point of your application.\n * The file should `export default` a React component that renders the application content.\n */\n app: string;\n entries?: never;\n}\n\ninterface MultipleEntriesOptions {\n root?: never;\n app?: never;\n /**\n * Path to a module that exports a function returning entry definitions.\n * Mutually exclusive with `root`+`app`.\n */\n entries: string;\n}\n\nexport type FunstackStaticOptions = FunstackStaticBaseOptions &\n (SingleEntryOptions | MultipleEntriesOptions);\n\nexport default function funstackStatic(\n options: FunstackStaticOptions,\n): (Plugin | Plugin[])[] {\n const {\n publicOutDir = \"dist/public\",\n ssr = false,\n clientInit,\n rscPayloadDir = defaultRscPayloadDir,\n } = options;\n\n // Validate rscPayloadDir to prevent path traversal or invalid segments\n if (\n !rscPayloadDir ||\n rscPayloadDir.includes(\"/\") ||\n rscPayloadDir.includes(\"\\\\\") ||\n rscPayloadDir === \"..\" ||\n rscPayloadDir === \".\"\n ) {\n throw new Error(\n `[funstack] Invalid rscPayloadDir: \"${rscPayloadDir}\". Must be a non-empty single path segment without slashes.`,\n );\n }\n\n let resolvedEntriesModule: string = \"__uninitialized__\";\n let resolvedClientInitEntry: string | undefined;\n\n // Determine whether user specified entries or root+app\n const isMultiEntry = \"entries\" in options && options.entries !== undefined;\n\n return [\n {\n name: \"@funstack/static:config-pre\",\n // Placed early because the rsc plugin sets the outDir to the default value\n config(config) {\n return {\n environments: {\n client: {\n build: {\n outDir:\n config.environments?.client?.build?.outDir ?? publicOutDir,\n },\n },\n },\n };\n },\n },\n serverPlugin(),\n rsc({\n entries: {\n rsc: \"@funstack/static/entries/rsc\",\n ssr: \"@funstack/static/entries/ssr\",\n client: \"@funstack/static/entries/client\",\n },\n serverHandler: false,\n }),\n {\n name: \"@funstack/static:config\",\n configResolved(config) {\n if (isMultiEntry) {\n resolvedEntriesModule = path.resolve(config.root, options.entries);\n } else {\n // For single-entry, we store both resolved paths to generate a\n // synthetic entries module in the virtual module loader.\n const resolvedRoot = path.resolve(config.root, options.root);\n const resolvedApp = path.resolve(config.root, options.app);\n // Encode as JSON for safe embedding in generated code\n resolvedEntriesModule = JSON.stringify({\n root: resolvedRoot,\n app: resolvedApp,\n });\n }\n if (clientInit) {\n resolvedClientInitEntry = path.resolve(config.root, clientInit);\n }\n },\n configEnvironment(_name, config) {\n if (!config.optimizeDeps) {\n config.optimizeDeps = {};\n }\n // Needed for properly bundling @vitejs/plugin-rsc for browser.\n // See: https://github.com/vitejs/vite-plugin-react/tree/79bf57cc8b9c77e33970ec2e876bd6d2f1568d5d/packages/plugin-rsc#using-vitejsplugin-rsc-as-a-framework-packages-dependencies\n if (config.optimizeDeps.include) {\n config.optimizeDeps.include = config.optimizeDeps.include.map(\n (entry) => {\n if (entry.startsWith(\"@vitejs/plugin-rsc\")) {\n entry = `@funstack/static > ${entry}`;\n }\n return entry;\n },\n );\n }\n if (!config.optimizeDeps.exclude) {\n config.optimizeDeps.exclude = [];\n }\n // Since code includes imports to virtual modules, we need to exclude\n // us from Optimize Deps.\n config.optimizeDeps.exclude.push(\"@funstack/static\");\n },\n },\n {\n name: \"@funstack/static:virtual-entry\",\n resolveId(id) {\n if (id === \"virtual:funstack/entries\") {\n return \"\\0virtual:funstack/entries\";\n }\n if (id === \"virtual:funstack/config\") {\n return \"\\0virtual:funstack/config\";\n }\n if (id === \"virtual:funstack/client-init\") {\n return \"\\0virtual:funstack/client-init\";\n }\n },\n load(id) {\n if (id === \"\\0virtual:funstack/entries\") {\n if (isMultiEntry) {\n // Re-export the user's entries module\n return `export { default } from \"${resolvedEntriesModule}\";`;\n }\n // Synthesize a single-entry array from root+app\n const { root, app } = JSON.parse(resolvedEntriesModule);\n return [\n `import Root from \"${root}\";`,\n `import App from \"${app}\";`,\n `export default function getEntries() {`,\n ` return [{ path: \"index.html\", root: { default: Root }, app: { default: App } }];`,\n `}`,\n ].join(\"\\n\");\n }\n if (id === \"\\0virtual:funstack/config\") {\n return [\n `export const ssr = ${JSON.stringify(ssr)};`,\n `export const rscPayloadDir = ${JSON.stringify(rscPayloadDir)};`,\n ].join(\"\\n\");\n }\n if (id === \"\\0virtual:funstack/client-init\") {\n if (resolvedClientInitEntry) {\n return `import \"${resolvedClientInitEntry}\";`;\n }\n return \"\";\n }\n },\n },\n {\n name: \"@funstack/static:build\",\n async buildApp(builder) {\n await buildApp(builder, this, { rscPayloadDir });\n },\n },\n ];\n}\n"],"mappings":";;;;;;AAwEA,SAAwB,eACtB,SACuB;CACvB,MAAM,EACJ,eAAe,eACf,MAAM,OACN,YACA,gBAAgB,yBACd;AAGJ,KACE,CAAC,iBACD,cAAc,SAAS,IAAI,IAC3B,cAAc,SAAS,KAAK,IAC5B,kBAAkB,QAClB,kBAAkB,IAElB,OAAM,IAAI,MACR,sCAAsC,cAAc,6DACrD;CAGH,IAAI,wBAAgC;CACpC,IAAI;CAGJ,MAAM,eAAe,aAAa,WAAW,QAAQ,YAAY,KAAA;AAEjE,QAAO;EACL;GACE,MAAM;GAEN,OAAO,QAAQ;AACb,WAAO,EACL,cAAc,EACZ,QAAQ,EACN,OAAO,EACL,QACE,OAAO,cAAc,QAAQ,OAAO,UAAU,cACjD,EACF,EACF,EACF;;GAEJ;EACD,cAAc;EACd,IAAI;GACF,SAAS;IACP,KAAK;IACL,KAAK;IACL,QAAQ;IACT;GACD,eAAe;GAChB,CAAC;EACF;GACE,MAAM;GACN,eAAe,QAAQ;AACrB,QAAI,aACF,yBAAwB,KAAK,QAAQ,OAAO,MAAM,QAAQ,QAAQ;SAC7D;KAGL,MAAM,eAAe,KAAK,QAAQ,OAAO,MAAM,QAAQ,KAAK;KAC5D,MAAM,cAAc,KAAK,QAAQ,OAAO,MAAM,QAAQ,IAAI;AAE1D,6BAAwB,KAAK,UAAU;MACrC,MAAM;MACN,KAAK;MACN,CAAC;;AAEJ,QAAI,WACF,2BAA0B,KAAK,QAAQ,OAAO,MAAM,WAAW;;GAGnE,kBAAkB,OAAO,QAAQ;AAC/B,QAAI,CAAC,OAAO,aACV,QAAO,eAAe,EAAE;AAI1B,QAAI,OAAO,aAAa,QACtB,QAAO,aAAa,UAAU,OAAO,aAAa,QAAQ,KACvD,UAAU;AACT,SAAI,MAAM,WAAW,qBAAqB,CACxC,SAAQ,sBAAsB;AAEhC,YAAO;MAEV;AAEH,QAAI,CAAC,OAAO,aAAa,QACvB,QAAO,aAAa,UAAU,EAAE;AAIlC,WAAO,aAAa,QAAQ,KAAK,mBAAmB;;GAEvD;EACD;GACE,MAAM;GACN,UAAU,IAAI;AACZ,QAAI,OAAO,2BACT,QAAO;AAET,QAAI,OAAO,0BACT,QAAO;AAET,QAAI,OAAO,+BACT,QAAO;;GAGX,KAAK,IAAI;AACP,QAAI,OAAO,8BAA8B;AACvC,SAAI,aAEF,QAAO,4BAA4B,sBAAsB;KAG3D,MAAM,EAAE,MAAM,QAAQ,KAAK,MAAM,sBAAsB;AACvD,YAAO;MACL,qBAAqB,KAAK;MAC1B,oBAAoB,IAAI;MACxB;MACA;MACA;MACD,CAAC,KAAK,KAAK;;AAEd,QAAI,OAAO,4BACT,QAAO,CACL,sBAAsB,KAAK,UAAU,IAAI,CAAC,IAC1C,gCAAgC,KAAK,UAAU,cAAc,CAAC,GAC/D,CAAC,KAAK,KAAK;AAEd,QAAI,OAAO,kCAAkC;AAC3C,SAAI,wBACF,QAAO,WAAW,wBAAwB;AAE5C,YAAO;;;GAGZ;EACD;GACE,MAAM;GACN,MAAM,SAAS,SAAS;AACtB,UAAM,SAAS,SAAS,MAAM,EAAE,eAAe,CAAC;;GAEnD;EACF"}
|
package/dist/plugin/server.mjs
CHANGED
|
@@ -4,7 +4,6 @@ import path from "node:path";
|
|
|
4
4
|
import { readFile } from "node:fs/promises";
|
|
5
5
|
import { isRunnableDevEnvironment } from "vite";
|
|
6
6
|
import { toNodeHandler } from "srvx/node";
|
|
7
|
-
|
|
8
7
|
//#region src/plugin/server.ts
|
|
9
8
|
const serverPlugin = () => {
|
|
10
9
|
let resolvedOutDir = "__uninitialized__";
|
|
@@ -70,7 +69,7 @@ const serverPlugin = () => {
|
|
|
70
69
|
}
|
|
71
70
|
};
|
|
72
71
|
};
|
|
73
|
-
|
|
74
72
|
//#endregion
|
|
75
73
|
export { serverPlugin };
|
|
74
|
+
|
|
76
75
|
//# sourceMappingURL=server.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.mjs","names":[],"sources":["../../src/plugin/server.ts"],"sourcesContent":["import { isRunnableDevEnvironment, type Plugin } from \"vite\";\nimport { readFile } from \"node:fs/promises\";\nimport { toNodeHandler } from \"srvx/node\";\nimport path from \"node:path\";\nimport { getRSCEntryPoint } from \"./getRSCEntryPoint\";\nimport { urlPathToFileCandidates } from \"../util/urlPath\";\n\nexport const serverPlugin = (): Plugin => {\n let resolvedOutDir = \"__uninitialized__\";\n return {\n name: \"@funstack/static:server\",\n configResolved(config) {\n resolvedOutDir = path.resolve(\n config.root,\n config.environments.client.build.outDir,\n );\n },\n configureServer(server) {\n const rscEnv = server.environments.rsc;\n if (!isRunnableDevEnvironment(rscEnv)) {\n throw new Error(\"The rsc environment is not runnable\");\n }\n\n return () => {\n server.middlewares.use(async (req, res, next) => {\n try {\n const rscEntry = await getRSCEntryPoint(rscEnv);\n if (req.headers.accept?.includes(\"text/html\")) {\n // serveHTML now accepts a Request and routes by URL path\n const fetchHandler = toNodeHandler(rscEntry.serveHTML);\n await fetchHandler(req as any, res as any);\n return;\n }\n const fetchHandler = toNodeHandler(rscEntry.serveRSC);\n try {\n await fetchHandler(req as any, res as any);\n } catch (error) {\n if (rscEntry.isServeRSCError(error) && error.status === 404) {\n next();\n return;\n }\n next(error);\n }\n } catch (error) {\n next(error);\n }\n });\n };\n },\n configurePreviewServer(server) {\n return () => {\n server.middlewares.use(async (req, res, next) => {\n try {\n if (req.headers.accept?.includes(\"text/html\")) {\n const urlPath = new URL(req.url!, `http://${req.headers.host}`)\n .pathname;\n const candidates = urlPathToFileCandidates(urlPath);\n for (const candidate of candidates) {\n try {\n const html = await readFile(\n path.join(resolvedOutDir, candidate),\n \"utf-8\",\n );\n res.end(html);\n return;\n } catch {\n // Try next candidate\n }\n }\n // SPA fallback: try serving index.html or index.htm for unmatched routes\n for (const indexFile of [\"index.html\", \"index.htm\"]) {\n try {\n const html = await readFile(\n path.join(resolvedOutDir, indexFile),\n \"utf-8\",\n );\n res.end(html);\n return;\n } catch {\n // Try next candidate\n }\n }\n next();\n return;\n }\n } catch (error) {\n next(error);\n return;\n }\n next();\n });\n };\n },\n };\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.mjs","names":[],"sources":["../../src/plugin/server.ts"],"sourcesContent":["import { isRunnableDevEnvironment, type Plugin } from \"vite\";\nimport { readFile } from \"node:fs/promises\";\nimport { toNodeHandler } from \"srvx/node\";\nimport path from \"node:path\";\nimport { getRSCEntryPoint } from \"./getRSCEntryPoint\";\nimport { urlPathToFileCandidates } from \"../util/urlPath\";\n\nexport const serverPlugin = (): Plugin => {\n let resolvedOutDir = \"__uninitialized__\";\n return {\n name: \"@funstack/static:server\",\n configResolved(config) {\n resolvedOutDir = path.resolve(\n config.root,\n config.environments.client.build.outDir,\n );\n },\n configureServer(server) {\n const rscEnv = server.environments.rsc;\n if (!isRunnableDevEnvironment(rscEnv)) {\n throw new Error(\"The rsc environment is not runnable\");\n }\n\n return () => {\n server.middlewares.use(async (req, res, next) => {\n try {\n const rscEntry = await getRSCEntryPoint(rscEnv);\n if (req.headers.accept?.includes(\"text/html\")) {\n // serveHTML now accepts a Request and routes by URL path\n const fetchHandler = toNodeHandler(rscEntry.serveHTML);\n await fetchHandler(req as any, res as any);\n return;\n }\n const fetchHandler = toNodeHandler(rscEntry.serveRSC);\n try {\n await fetchHandler(req as any, res as any);\n } catch (error) {\n if (rscEntry.isServeRSCError(error) && error.status === 404) {\n next();\n return;\n }\n next(error);\n }\n } catch (error) {\n next(error);\n }\n });\n };\n },\n configurePreviewServer(server) {\n return () => {\n server.middlewares.use(async (req, res, next) => {\n try {\n if (req.headers.accept?.includes(\"text/html\")) {\n const urlPath = new URL(req.url!, `http://${req.headers.host}`)\n .pathname;\n const candidates = urlPathToFileCandidates(urlPath);\n for (const candidate of candidates) {\n try {\n const html = await readFile(\n path.join(resolvedOutDir, candidate),\n \"utf-8\",\n );\n res.end(html);\n return;\n } catch {\n // Try next candidate\n }\n }\n // SPA fallback: try serving index.html or index.htm for unmatched routes\n for (const indexFile of [\"index.html\", \"index.htm\"]) {\n try {\n const html = await readFile(\n path.join(resolvedOutDir, indexFile),\n \"utf-8\",\n );\n res.end(html);\n return;\n } catch {\n // Try next candidate\n }\n }\n next();\n return;\n }\n } catch (error) {\n next(error);\n return;\n }\n next();\n });\n };\n },\n };\n};\n"],"mappings":";;;;;;;AAOA,MAAa,qBAA6B;CACxC,IAAI,iBAAiB;AACrB,QAAO;EACL,MAAM;EACN,eAAe,QAAQ;AACrB,oBAAiB,KAAK,QACpB,OAAO,MACP,OAAO,aAAa,OAAO,MAAM,OAClC;;EAEH,gBAAgB,QAAQ;GACtB,MAAM,SAAS,OAAO,aAAa;AACnC,OAAI,CAAC,yBAAyB,OAAO,CACnC,OAAM,IAAI,MAAM,sCAAsC;AAGxD,gBAAa;AACX,WAAO,YAAY,IAAI,OAAO,KAAK,KAAK,SAAS;AAC/C,SAAI;MACF,MAAM,WAAW,MAAM,iBAAiB,OAAO;AAC/C,UAAI,IAAI,QAAQ,QAAQ,SAAS,YAAY,EAAE;AAG7C,aADqB,cAAc,SAAS,UAAU,CACnC,KAAY,IAAW;AAC1C;;MAEF,MAAM,eAAe,cAAc,SAAS,SAAS;AACrD,UAAI;AACF,aAAM,aAAa,KAAY,IAAW;eACnC,OAAO;AACd,WAAI,SAAS,gBAAgB,MAAM,IAAI,MAAM,WAAW,KAAK;AAC3D,cAAM;AACN;;AAEF,YAAK,MAAM;;cAEN,OAAO;AACd,WAAK,MAAM;;MAEb;;;EAGN,uBAAuB,QAAQ;AAC7B,gBAAa;AACX,WAAO,YAAY,IAAI,OAAO,KAAK,KAAK,SAAS;AAC/C,SAAI;AACF,UAAI,IAAI,QAAQ,QAAQ,SAAS,YAAY,EAAE;OAC7C,MAAM,UAAU,IAAI,IAAI,IAAI,KAAM,UAAU,IAAI,QAAQ,OAAO,CAC5D;OACH,MAAM,aAAa,wBAAwB,QAAQ;AACnD,YAAK,MAAM,aAAa,WACtB,KAAI;QACF,MAAM,OAAO,MAAM,SACjB,KAAK,KAAK,gBAAgB,UAAU,EACpC,QACD;AACD,YAAI,IAAI,KAAK;AACb;eACM;AAKV,YAAK,MAAM,aAAa,CAAC,cAAc,YAAY,CACjD,KAAI;QACF,MAAM,OAAO,MAAM,SACjB,KAAK,KAAK,gBAAgB,UAAU,EACpC,QACD;AACD,YAAI,IAAI,KAAK;AACb;eACM;AAIV,aAAM;AACN;;cAEK,OAAO;AACd,WAAK,MAAM;AACX;;AAEF,WAAM;MACN;;;EAGP"}
|
package/dist/rsc/defer.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defer.d.mts","names":[],"sources":["../../src/rsc/defer.tsx"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"defer.d.mts","names":[],"sources":["../../src/rsc/defer.tsx"],"mappings":";;;UAOiB,UAAA;EACf,KAAA,EAAO,eAAA;EACP,IAAA;EACA,YAAA,GAAe,OAAA;AAAA;;;;UAMA,YAAA;EANf;;;;AAMF;EAME,IAAA;AAAA;AAAA,UAGe,gBAAA,SAAyB,UAAA;EACxC,KAAA,EAAO,OAAA,CAAQ,eAAA;IAAmB,KAAA;EAAA;EAClC,YAAA,EAAc,OAAA;AAAA;AAAA,KAGX,eAAA;EAEC,KAAA;EACA,OAAA,EAAS,YAAA;AAAA;EAGT,KAAA;EACA,MAAA,EAAQ,cAAA,CAAe,UAAA;AAAA;EAGvB,KAAA;AAAA;EAGA,KAAA;EACA,KAAA;AAAA;AAAA,cAeO,aAAA;EAAA;EAGX,QAAA,CAAS,OAAA,EAAS,YAAA,EAAc,EAAA,UAAY,IAAA;EAI5C,IAAA,CAAK,EAAA,WAAa,gBAAA;EAkClB,GAAA,CAAI,EAAA;EAnES;;;;EA2EN,OAAA,CAAA,GAAO,cAAA;;;;;;;;;;;;AAjDhB;;;;iBAuHgB,KAAA,CACd,OAAA,EAAS,YAAA,EACT,OAAA,GAAU,YAAA,GACT,SAAA"}
|
package/dist/rsc/defer.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { getPayloadIDFor } from "./rscModule.mjs";
|
|
2
2
|
import { drainStream } from "../util/drainStream.mjs";
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
import { rscPayloadDir } from "virtual:funstack/config";
|
|
4
5
|
import { renderToReadableStream } from "@vitejs/plugin-rsc/react/rsc";
|
|
5
6
|
import { DeferredComponent } from "#rsc-client";
|
|
6
|
-
|
|
7
7
|
//#region src/rsc/defer.tsx
|
|
8
8
|
/**
|
|
9
9
|
* Sanitizes a name for use in file paths.
|
|
@@ -111,11 +111,11 @@ const deferRegistry = new DeferRegistry();
|
|
|
111
111
|
function defer(element, options) {
|
|
112
112
|
const name = options?.name;
|
|
113
113
|
const sanitizedName = name ? sanitizeName(name) : void 0;
|
|
114
|
-
const id = getPayloadIDFor(sanitizedName ? `${sanitizedName}-${crypto.randomUUID()}` : crypto.randomUUID());
|
|
114
|
+
const id = getPayloadIDFor(sanitizedName ? `${sanitizedName}-${crypto.randomUUID()}` : crypto.randomUUID(), rscPayloadDir);
|
|
115
115
|
deferRegistry.register(element, id, name);
|
|
116
116
|
return /* @__PURE__ */ jsx(DeferredComponent, { moduleID: id });
|
|
117
117
|
}
|
|
118
|
-
|
|
119
118
|
//#endregion
|
|
120
119
|
export { defer, deferRegistry };
|
|
120
|
+
|
|
121
121
|
//# sourceMappingURL=defer.mjs.map
|
package/dist/rsc/defer.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defer.mjs","names":["#registry","#loadEntry"],"sources":["../../src/rsc/defer.tsx"],"sourcesContent":["import type { ReactElement, ReactNode } from \"react\";\nimport { renderToReadableStream } from \"@vitejs/plugin-rsc/react/rsc\";\nimport { DeferredComponent } from \"#rsc-client\";\nimport { drainStream } from \"../util/drainStream\";\nimport { getPayloadIDFor } from \"./rscModule\";\n\nexport interface DeferEntry {\n state: DeferEntryState;\n name?: string;\n drainPromise?: Promise<string>;\n}\n\n/**\n * Options for the defer function.\n */\nexport interface DeferOptions {\n /**\n * Optional name for debugging purposes.\n * In development: included in the RSC payload file name.\n * In production: logged when the payload file is emitted.\n */\n name?: string;\n}\n\nexport interface LoadedDeferEntry extends DeferEntry {\n state: Exclude<DeferEntryState, { state: \"pending\" }>;\n drainPromise: Promise<string>;\n}\n\ntype DeferEntryState =\n | {\n state: \"pending\";\n element: ReactElement;\n }\n | {\n state: \"streaming\";\n stream: ReadableStream<Uint8Array>;\n }\n | {\n state: \"ready\";\n }\n | {\n state: \"error\";\n error: unknown;\n };\n\n/**\n * Sanitizes a name for use in file paths.\n * Replaces non-alphanumeric characters with underscores and limits length.\n */\nfunction sanitizeName(name: string): string {\n return name\n .replace(/[^a-zA-Z0-9_-]/g, \"_\")\n .replace(/_+/g, \"_\")\n .replace(/^_|_$/g, \"\")\n .slice(0, 50);\n}\n\nexport class DeferRegistry {\n #registry = new Map<string, DeferEntry>();\n\n register(element: ReactElement, id: string, name?: string) {\n this.#registry.set(id, { state: { element, state: \"pending\" }, name });\n }\n\n load(id: string): LoadedDeferEntry | undefined {\n const entry = this.#registry.get(id);\n if (!entry) {\n return undefined;\n }\n return this.#loadEntry(entry);\n }\n\n #loadEntry(entry: DeferEntry): LoadedDeferEntry {\n const { state } = entry;\n switch (state.state) {\n case \"pending\": {\n const stream = renderToReadableStream<ReactNode>(state.element);\n const [stream1, stream2] = stream.tee();\n entry.state = { state: \"streaming\", stream: stream1 };\n const drainPromise = drainStream(stream2);\n entry.drainPromise = drainPromise;\n drainPromise.then(\n () => {\n entry.state = { state: \"ready\" };\n },\n (error) => {\n entry.state = { state: \"error\", error };\n },\n );\n return entry as LoadedDeferEntry;\n }\n case \"streaming\":\n case \"ready\":\n case \"error\":\n return entry as LoadedDeferEntry;\n }\n }\n\n has(id: string): boolean {\n return this.#registry.has(id);\n }\n\n /**\n * Iterates over all entries in parallel.\n * Yields results as each stream completes.\n */\n async *loadAll() {\n const errors: unknown[] = [];\n\n // Phase 1: Start all entries loading and collect drain promises.\n // We use drain promises (which drain stream2 from tee) instead of\n // draining stream1 directly, because stream1 may have been locked\n // by createFromReadableStream during SSR.\n const loadedEntries = Array.from(this.#registry, ([id, entry]) => {\n const loaded = this.#loadEntry(entry);\n return [id, loaded.drainPromise, entry.name] as const;\n });\n\n if (loadedEntries.length === 0) return;\n\n type Result = { id: string; data: string; name?: string };\n\n // Completion queue\n const completed: Array<Result | { error: unknown }> = [];\n let waiting: (() => void) | undefined;\n let remainingCount = loadedEntries.length;\n\n const onComplete = (result: Result | { error: unknown }) => {\n completed.push(result);\n remainingCount--;\n waiting?.();\n };\n\n // Phase 2: Await drain promises\n for (const [id, drainPromise, name] of loadedEntries) {\n drainPromise.then(\n (data) => onComplete({ id, data, name }),\n (error) => onComplete({ error }),\n );\n }\n\n // Phase 3: Yield from queue as results arrive\n while (remainingCount > 0 || completed.length > 0) {\n if (completed.length === 0) {\n await new Promise<void>((r) => {\n waiting = r;\n });\n waiting = undefined;\n }\n for (const result of completed.splice(0)) {\n if (\"error\" in result) {\n errors.push(result.error);\n } else {\n yield result;\n }\n }\n }\n\n if (errors.length > 0) {\n throw new AggregateError(errors);\n }\n }\n}\n\nexport const deferRegistry = new DeferRegistry();\n\n/**\n * Renders given Server Component into a separate RSC payload.\n *\n * During the client side rendering, fetching of the payload will be\n * deferred until the returned ReactNode is actually rendered.\n *\n * @param element - The React element to defer.\n * @param options - Optional configuration for the deferred payload.\n * @returns A ReactNode that virtually contains the result of rendering the given component.\n */\nexport function defer(\n element: ReactElement,\n options?: DeferOptions,\n): ReactNode {\n const name = options?.name;\n const sanitizedName = name ? sanitizeName(name) : undefined;\n const rawId = sanitizedName\n ? `${sanitizedName}-${crypto.randomUUID()}`\n : crypto.randomUUID();\n const id = getPayloadIDFor(rawId);\n deferRegistry.register(element, id, name);\n\n return <DeferredComponent moduleID={id} />;\n}\n"],"mappings":";;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"defer.mjs","names":["#registry","#loadEntry"],"sources":["../../src/rsc/defer.tsx"],"sourcesContent":["import type { ReactElement, ReactNode } from \"react\";\nimport { renderToReadableStream } from \"@vitejs/plugin-rsc/react/rsc\";\nimport { DeferredComponent } from \"#rsc-client\";\nimport { drainStream } from \"../util/drainStream\";\nimport { getPayloadIDFor } from \"./rscModule\";\nimport { rscPayloadDir } from \"virtual:funstack/config\";\n\nexport interface DeferEntry {\n state: DeferEntryState;\n name?: string;\n drainPromise?: Promise<string>;\n}\n\n/**\n * Options for the defer function.\n */\nexport interface DeferOptions {\n /**\n * Optional name for debugging purposes.\n * In development: included in the RSC payload file name.\n * In production: logged when the payload file is emitted.\n */\n name?: string;\n}\n\nexport interface LoadedDeferEntry extends DeferEntry {\n state: Exclude<DeferEntryState, { state: \"pending\" }>;\n drainPromise: Promise<string>;\n}\n\ntype DeferEntryState =\n | {\n state: \"pending\";\n element: ReactElement;\n }\n | {\n state: \"streaming\";\n stream: ReadableStream<Uint8Array>;\n }\n | {\n state: \"ready\";\n }\n | {\n state: \"error\";\n error: unknown;\n };\n\n/**\n * Sanitizes a name for use in file paths.\n * Replaces non-alphanumeric characters with underscores and limits length.\n */\nfunction sanitizeName(name: string): string {\n return name\n .replace(/[^a-zA-Z0-9_-]/g, \"_\")\n .replace(/_+/g, \"_\")\n .replace(/^_|_$/g, \"\")\n .slice(0, 50);\n}\n\nexport class DeferRegistry {\n #registry = new Map<string, DeferEntry>();\n\n register(element: ReactElement, id: string, name?: string) {\n this.#registry.set(id, { state: { element, state: \"pending\" }, name });\n }\n\n load(id: string): LoadedDeferEntry | undefined {\n const entry = this.#registry.get(id);\n if (!entry) {\n return undefined;\n }\n return this.#loadEntry(entry);\n }\n\n #loadEntry(entry: DeferEntry): LoadedDeferEntry {\n const { state } = entry;\n switch (state.state) {\n case \"pending\": {\n const stream = renderToReadableStream<ReactNode>(state.element);\n const [stream1, stream2] = stream.tee();\n entry.state = { state: \"streaming\", stream: stream1 };\n const drainPromise = drainStream(stream2);\n entry.drainPromise = drainPromise;\n drainPromise.then(\n () => {\n entry.state = { state: \"ready\" };\n },\n (error) => {\n entry.state = { state: \"error\", error };\n },\n );\n return entry as LoadedDeferEntry;\n }\n case \"streaming\":\n case \"ready\":\n case \"error\":\n return entry as LoadedDeferEntry;\n }\n }\n\n has(id: string): boolean {\n return this.#registry.has(id);\n }\n\n /**\n * Iterates over all entries in parallel.\n * Yields results as each stream completes.\n */\n async *loadAll() {\n const errors: unknown[] = [];\n\n // Phase 1: Start all entries loading and collect drain promises.\n // We use drain promises (which drain stream2 from tee) instead of\n // draining stream1 directly, because stream1 may have been locked\n // by createFromReadableStream during SSR.\n const loadedEntries = Array.from(this.#registry, ([id, entry]) => {\n const loaded = this.#loadEntry(entry);\n return [id, loaded.drainPromise, entry.name] as const;\n });\n\n if (loadedEntries.length === 0) return;\n\n type Result = { id: string; data: string; name?: string };\n\n // Completion queue\n const completed: Array<Result | { error: unknown }> = [];\n let waiting: (() => void) | undefined;\n let remainingCount = loadedEntries.length;\n\n const onComplete = (result: Result | { error: unknown }) => {\n completed.push(result);\n remainingCount--;\n waiting?.();\n };\n\n // Phase 2: Await drain promises\n for (const [id, drainPromise, name] of loadedEntries) {\n drainPromise.then(\n (data) => onComplete({ id, data, name }),\n (error) => onComplete({ error }),\n );\n }\n\n // Phase 3: Yield from queue as results arrive\n while (remainingCount > 0 || completed.length > 0) {\n if (completed.length === 0) {\n await new Promise<void>((r) => {\n waiting = r;\n });\n waiting = undefined;\n }\n for (const result of completed.splice(0)) {\n if (\"error\" in result) {\n errors.push(result.error);\n } else {\n yield result;\n }\n }\n }\n\n if (errors.length > 0) {\n throw new AggregateError(errors);\n }\n }\n}\n\nexport const deferRegistry = new DeferRegistry();\n\n/**\n * Renders given Server Component into a separate RSC payload.\n *\n * During the client side rendering, fetching of the payload will be\n * deferred until the returned ReactNode is actually rendered.\n *\n * @param element - The React element to defer.\n * @param options - Optional configuration for the deferred payload.\n * @returns A ReactNode that virtually contains the result of rendering the given component.\n */\nexport function defer(\n element: ReactElement,\n options?: DeferOptions,\n): ReactNode {\n const name = options?.name;\n const sanitizedName = name ? sanitizeName(name) : undefined;\n const rawId = sanitizedName\n ? `${sanitizedName}-${crypto.randomUUID()}`\n : crypto.randomUUID();\n const id = getPayloadIDFor(rawId, rscPayloadDir);\n deferRegistry.register(element, id, name);\n\n return <DeferredComponent moduleID={id} />;\n}\n"],"mappings":";;;;;;;;;;;AAmDA,SAAS,aAAa,MAAsB;AAC1C,QAAO,KACJ,QAAQ,mBAAmB,IAAI,CAC/B,QAAQ,OAAO,IAAI,CACnB,QAAQ,UAAU,GAAG,CACrB,MAAM,GAAG,GAAG;;AAGjB,IAAa,gBAAb,MAA2B;CACzB,4BAAY,IAAI,KAAyB;CAEzC,SAAS,SAAuB,IAAY,MAAe;AACzD,QAAA,SAAe,IAAI,IAAI;GAAE,OAAO;IAAE;IAAS,OAAO;IAAW;GAAE;GAAM,CAAC;;CAGxE,KAAK,IAA0C;EAC7C,MAAM,QAAQ,MAAA,SAAe,IAAI,GAAG;AACpC,MAAI,CAAC,MACH;AAEF,SAAO,MAAA,UAAgB,MAAM;;CAG/B,WAAW,OAAqC;EAC9C,MAAM,EAAE,UAAU;AAClB,UAAQ,MAAM,OAAd;GACE,KAAK,WAAW;IAEd,MAAM,CAAC,SAAS,WADD,uBAAkC,MAAM,QAAQ,CAC7B,KAAK;AACvC,UAAM,QAAQ;KAAE,OAAO;KAAa,QAAQ;KAAS;IACrD,MAAM,eAAe,YAAY,QAAQ;AACzC,UAAM,eAAe;AACrB,iBAAa,WACL;AACJ,WAAM,QAAQ,EAAE,OAAO,SAAS;QAEjC,UAAU;AACT,WAAM,QAAQ;MAAE,OAAO;MAAS;MAAO;MAE1C;AACD,WAAO;;GAET,KAAK;GACL,KAAK;GACL,KAAK,QACH,QAAO;;;CAIb,IAAI,IAAqB;AACvB,SAAO,MAAA,SAAe,IAAI,GAAG;;;;;;CAO/B,OAAO,UAAU;EACf,MAAM,SAAoB,EAAE;EAM5B,MAAM,gBAAgB,MAAM,KAAK,MAAA,WAAiB,CAAC,IAAI,WAAW;AAEhE,UAAO;IAAC;IADO,MAAA,UAAgB,MAAM,CAClB;IAAc,MAAM;IAAK;IAC5C;AAEF,MAAI,cAAc,WAAW,EAAG;EAKhC,MAAM,YAAgD,EAAE;EACxD,IAAI;EACJ,IAAI,iBAAiB,cAAc;EAEnC,MAAM,cAAc,WAAwC;AAC1D,aAAU,KAAK,OAAO;AACtB;AACA,cAAW;;AAIb,OAAK,MAAM,CAAC,IAAI,cAAc,SAAS,cACrC,cAAa,MACV,SAAS,WAAW;GAAE;GAAI;GAAM;GAAM,CAAC,GACvC,UAAU,WAAW,EAAE,OAAO,CAAC,CACjC;AAIH,SAAO,iBAAiB,KAAK,UAAU,SAAS,GAAG;AACjD,OAAI,UAAU,WAAW,GAAG;AAC1B,UAAM,IAAI,SAAe,MAAM;AAC7B,eAAU;MACV;AACF,cAAU,KAAA;;AAEZ,QAAK,MAAM,UAAU,UAAU,OAAO,EAAE,CACtC,KAAI,WAAW,OACb,QAAO,KAAK,OAAO,MAAM;OAEzB,OAAM;;AAKZ,MAAI,OAAO,SAAS,EAClB,OAAM,IAAI,eAAe,OAAO;;;AAKtC,MAAa,gBAAgB,IAAI,eAAe;;;;;;;;;;;AAYhD,SAAgB,MACd,SACA,SACW;CACX,MAAM,OAAO,SAAS;CACtB,MAAM,gBAAgB,OAAO,aAAa,KAAK,GAAG,KAAA;CAIlD,MAAM,KAAK,gBAHG,gBACV,GAAG,cAAc,GAAG,OAAO,YAAY,KACvC,OAAO,YAAY,EACW,cAAc;AAChD,eAAc,SAAS,SAAS,IAAI,KAAK;AAEzC,QAAO,oBAAC,mBAAD,EAAmB,UAAU,IAAM,CAAA"}
|
package/dist/rsc/entry.mjs
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { extractIDFromModulePath } from "./rscModule.mjs";
|
|
2
2
|
import { urlPathToFileCandidates } from "../util/urlPath.mjs";
|
|
3
|
-
import
|
|
3
|
+
import "./request.mjs";
|
|
4
4
|
import { stripBasePath } from "../util/basePath.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import { deferRegistry } from "./defer.mjs";
|
|
6
6
|
import { generateAppMarker } from "./marker.mjs";
|
|
7
7
|
import { resolveApp, resolveRoot } from "./resolveEntry.mjs";
|
|
8
8
|
import { jsx } from "react/jsx-runtime";
|
|
9
9
|
import { ssr } from "virtual:funstack/config";
|
|
10
10
|
import { renderToReadableStream } from "@vitejs/plugin-rsc/rsc";
|
|
11
|
-
|
|
12
11
|
//#region src/rsc/entry.tsx
|
|
13
12
|
async function loadEntriesList() {
|
|
14
13
|
const getEntries = (await import("virtual:funstack/entries")).default;
|
|
@@ -114,7 +113,7 @@ function isServeRSCError(error) {
|
|
|
114
113
|
async function serveRSC(request) {
|
|
115
114
|
const timings = [];
|
|
116
115
|
const pathname = stripBasePath(new URL(request.url).pathname);
|
|
117
|
-
if (pathname ===
|
|
116
|
+
if (pathname === "/funstack__/rsc") {
|
|
118
117
|
const entriesStart = performance.now();
|
|
119
118
|
const entries = await loadEntriesList();
|
|
120
119
|
timings.push(`entries;dur=${performance.now() - entriesStart}`);
|
|
@@ -199,7 +198,7 @@ async function build() {
|
|
|
199
198
|
};
|
|
200
199
|
}
|
|
201
200
|
if (import.meta.hot) import.meta.hot.accept();
|
|
202
|
-
|
|
203
201
|
//#endregion
|
|
204
202
|
export { build, isServeRSCError, serveHTML, serveRSC };
|
|
203
|
+
|
|
205
204
|
//# sourceMappingURL=entry.mjs.map
|
package/dist/rsc/entry.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entry.mjs","names":["ssrEnabled"],"sources":["../../src/rsc/entry.tsx"],"sourcesContent":["import \"./defer\";\nimport { renderToReadableStream } from \"@vitejs/plugin-rsc/rsc\";\nimport { devMainRscPath } from \"./request\";\nimport { generateAppMarker } from \"./marker\";\nimport { deferRegistry } from \"./defer\";\nimport { extractIDFromModulePath } from \"./rscModule\";\nimport { stripBasePath } from \"../util/basePath\";\nimport { urlPathToFileCandidates } from \"../util/urlPath\";\nimport { resolveRoot, resolveApp } from \"./resolveEntry\";\nimport type { EntryDefinition, GetEntriesResult } from \"../entryDefinition\";\n\nexport type RscPayload = {\n root: React.ReactNode;\n};\n\nexport type EntryBuildResult = {\n path: string;\n html: ReadableStream<Uint8Array>;\n appRsc: ReadableStream<Uint8Array>;\n};\n\nimport { ssr as ssrEnabled } from \"virtual:funstack/config\";\n\nasync function loadEntriesList(): Promise<EntryDefinition[]> {\n const getEntries = (await import(\"virtual:funstack/entries\")).default;\n const result: GetEntriesResult = getEntries();\n const entries: EntryDefinition[] = [];\n for await (const entry of result) {\n entries.push(entry);\n }\n return entries;\n}\n\n/**\n * Find the entry matching a URL path from a list of entries.\n */\nfunction findEntryForUrlPath(\n entries: EntryDefinition[],\n urlPath: string,\n): EntryDefinition | undefined {\n const candidates = urlPathToFileCandidates(urlPath);\n for (const candidate of candidates) {\n const entry = entries.find((e) => e.path === candidate);\n if (entry) {\n return entry;\n }\n }\n return undefined;\n}\n\n/**\n * Renders a single entry to an HTML response.\n */\nasync function renderEntryToResponse(\n entry: EntryDefinition,\n timings: string[],\n): Promise<Response> {\n const marker = generateAppMarker();\n\n const resolveStart = performance.now();\n const Root = await resolveRoot(entry.root);\n const appNode = await resolveApp(entry.app);\n timings.push(`resolve;dur=${performance.now() - resolveStart}`);\n\n const ssrModuleStart = performance.now();\n const ssrEntryModule = await import.meta.viteRsc.loadModule<\n typeof import(\"../ssr/entry\")\n >(\"ssr\");\n timings.push(`ssr-module;dur=${performance.now() - ssrModuleStart}`);\n\n if (ssrEnabled) {\n // SSR on: single RSC stream with full tree\n const rscStart = performance.now();\n const rootRscStream = renderToReadableStream<RscPayload>({\n root: <Root>{appNode}</Root>,\n });\n timings.push(`rsc;dur=${performance.now() - rscStart}`);\n\n const ssrStart = performance.now();\n const ssrResult = await ssrEntryModule.renderHTML(rootRscStream, {\n appEntryMarker: marker,\n build: false,\n ssr: true,\n deferRegistry,\n });\n timings.push(`ssr;dur=${performance.now() - ssrStart}`);\n\n return new Response(ssrResult.stream, {\n status: ssrResult.status,\n headers: {\n \"Content-type\": \"text/html\",\n \"Server-Timing\": timings.join(\", \"),\n },\n });\n } else {\n // SSR off: shell RSC for SSR, full RSC for client\n const rscStart = performance.now();\n const shellRscStream = renderToReadableStream<RscPayload>({\n root: (\n <Root>\n <span id={marker} />\n </Root>\n ),\n });\n const clientRscStream = renderToReadableStream<RscPayload>({\n root: <Root>{appNode}</Root>,\n });\n timings.push(`rsc;dur=${performance.now() - rscStart}`);\n\n const ssrStart = performance.now();\n const ssrResult = await ssrEntryModule.renderHTML(shellRscStream, {\n appEntryMarker: marker,\n build: false,\n ssr: false,\n clientRscStream,\n });\n timings.push(`ssr;dur=${performance.now() - ssrStart}`);\n\n return new Response(ssrResult.stream, {\n status: ssrResult.status,\n headers: {\n \"Content-type\": \"text/html\",\n \"Server-Timing\": timings.join(\", \"),\n },\n });\n }\n}\n\n/**\n * Entrypoint to serve HTML response in dev environment.\n * Accepts a Request to determine which entry to render based on URL path.\n */\nexport async function serveHTML(request: Request): Promise<Response> {\n const timings: string[] = [];\n\n const entriesStart = performance.now();\n const entries = await loadEntriesList();\n timings.push(`entries;dur=${performance.now() - entriesStart}`);\n\n const url = new URL(request.url);\n const urlPath = stripBasePath(url.pathname);\n let entry = findEntryForUrlPath(entries, urlPath);\n\n // SPA fallback: if no entry matched, fall back to index.html or index.htm entry\n if (!entry) {\n entry = entries.find(\n (e) => e.path === \"index.html\" || e.path === \"index.htm\",\n );\n }\n\n if (!entry) {\n return new Response(\"Not Found\", {\n status: 404,\n headers: { \"Content-type\": \"text/plain\" },\n });\n }\n\n return renderEntryToResponse(entry, timings);\n}\n\nclass ServeRSCError extends Error {\n status: 404 | 500;\n constructor(message: string, status: 404 | 500) {\n super(message);\n this.name = \"ServeRSCError\";\n this.status = status;\n }\n}\n\nexport function isServeRSCError(error: unknown): error is ServeRSCError {\n return error instanceof Error && error.name === \"ServeRSCError\";\n}\n\n/**\n * Serves an RSC stream response\n */\nexport async function serveRSC(request: Request): Promise<Response> {\n const timings: string[] = [];\n const url = new URL(request.url);\n const pathname = stripBasePath(url.pathname);\n if (pathname === devMainRscPath) {\n // root RSC stream is requested (HMR re-fetch always sends full tree)\n // For HMR, re-render the first entry (single-entry mode) or index.html entry\n const entriesStart = performance.now();\n const entries = await loadEntriesList();\n timings.push(`entries;dur=${performance.now() - entriesStart}`);\n\n // Use the first entry for HMR re-fetch\n const entry = entries[0];\n if (!entry) {\n throw new ServeRSCError(\"No entries defined\", 404);\n }\n\n const resolveStart = performance.now();\n const Root = await resolveRoot(entry.root);\n const appNode = await resolveApp(entry.app);\n timings.push(`resolve;dur=${performance.now() - resolveStart}`);\n\n const rscStart = performance.now();\n const rootRscStream = renderToReadableStream<RscPayload>({\n root: <Root>{appNode}</Root>,\n });\n timings.push(`rsc;dur=${performance.now() - rscStart}`);\n\n return new Response(rootRscStream, {\n status: 200,\n headers: {\n \"content-type\": \"text/x-component;charset=utf-8\",\n \"Server-Timing\": timings.join(\", \"),\n },\n });\n }\n\n const moduleId = extractIDFromModulePath(pathname);\n if (!moduleId) {\n throw new ServeRSCError(`Invalid RSC module path: ${pathname}`, 404);\n }\n\n const deferLoadStart = performance.now();\n const entry = deferRegistry.load(moduleId);\n if (!entry) {\n throw new ServeRSCError(`RSC component not found: ${moduleId}`, 404);\n }\n timings.push(`defer-load;dur=${performance.now() - deferLoadStart}`);\n\n const { state } = entry;\n switch (state.state) {\n case \"streaming\": {\n return new Response(state.stream, {\n status: 200,\n headers: {\n \"content-type\": \"text/x-component;charset=utf-8\",\n \"Server-Timing\": timings.join(\", \"),\n },\n });\n }\n case \"ready\": {\n return new Response(await entry.drainPromise, {\n status: 200,\n headers: {\n \"content-type\": \"text/x-component;charset=utf-8\",\n \"Server-Timing\": timings.join(\", \"),\n },\n });\n }\n case \"error\": {\n throw new ServeRSCError(\n `Failed to load RSC component: ${state.error}`,\n 500,\n );\n }\n }\n}\n\n/**\n * Build handler — iterates over all entries and returns per-entry results\n * along with the shared defer registry.\n */\nexport async function build() {\n const getEntries = (await import(\"virtual:funstack/entries\")).default;\n\n const ssrEntryModule = await import.meta.viteRsc.loadModule<\n typeof import(\"../ssr/entry\")\n >(\"ssr\");\n\n const results: EntryBuildResult[] = [];\n for await (const entry of getEntries()) {\n const Root = await resolveRoot(entry.root);\n const appNode = await resolveApp(entry.app);\n\n const marker = generateAppMarker();\n\n let rootRscStream: ReadableStream<Uint8Array>;\n let appRscStream: ReadableStream<Uint8Array>;\n\n if (ssrEnabled) {\n // SSR on: both streams have full tree\n rootRscStream = renderToReadableStream<RscPayload>({\n root: <Root>{appNode}</Root>,\n });\n appRscStream = renderToReadableStream<RscPayload>({\n root: <Root>{appNode}</Root>,\n });\n } else {\n // SSR off: root stream has shell, app stream has App only\n rootRscStream = renderToReadableStream<RscPayload>({\n root: (\n <Root>\n <span id={marker} />\n </Root>\n ),\n });\n appRscStream = renderToReadableStream<RscPayload>({\n root: appNode,\n });\n }\n\n const ssrResult = await ssrEntryModule.renderHTML(rootRscStream, {\n appEntryMarker: marker,\n build: true,\n ssr: ssrEnabled,\n deferRegistry,\n });\n\n results.push({\n path: entry.path,\n html: ssrResult.stream,\n appRsc: appRscStream,\n });\n }\n\n return {\n entries: results,\n deferRegistry,\n };\n}\n\nexport { defer } from \"./defer\";\n\nif (import.meta.hot) {\n import.meta.hot.accept();\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"entry.mjs","names":["ssrEnabled"],"sources":["../../src/rsc/entry.tsx"],"sourcesContent":["import \"./defer\";\nimport { renderToReadableStream } from \"@vitejs/plugin-rsc/rsc\";\nimport { devMainRscPath } from \"./request\";\nimport { generateAppMarker } from \"./marker\";\nimport { deferRegistry } from \"./defer\";\nimport { extractIDFromModulePath } from \"./rscModule\";\nimport { stripBasePath } from \"../util/basePath\";\nimport { urlPathToFileCandidates } from \"../util/urlPath\";\nimport { resolveRoot, resolveApp } from \"./resolveEntry\";\nimport type { EntryDefinition, GetEntriesResult } from \"../entryDefinition\";\n\nexport type RscPayload = {\n root: React.ReactNode;\n};\n\nexport type EntryBuildResult = {\n path: string;\n html: ReadableStream<Uint8Array>;\n appRsc: ReadableStream<Uint8Array>;\n};\n\nimport { ssr as ssrEnabled } from \"virtual:funstack/config\";\n\nasync function loadEntriesList(): Promise<EntryDefinition[]> {\n const getEntries = (await import(\"virtual:funstack/entries\")).default;\n const result: GetEntriesResult = getEntries();\n const entries: EntryDefinition[] = [];\n for await (const entry of result) {\n entries.push(entry);\n }\n return entries;\n}\n\n/**\n * Find the entry matching a URL path from a list of entries.\n */\nfunction findEntryForUrlPath(\n entries: EntryDefinition[],\n urlPath: string,\n): EntryDefinition | undefined {\n const candidates = urlPathToFileCandidates(urlPath);\n for (const candidate of candidates) {\n const entry = entries.find((e) => e.path === candidate);\n if (entry) {\n return entry;\n }\n }\n return undefined;\n}\n\n/**\n * Renders a single entry to an HTML response.\n */\nasync function renderEntryToResponse(\n entry: EntryDefinition,\n timings: string[],\n): Promise<Response> {\n const marker = generateAppMarker();\n\n const resolveStart = performance.now();\n const Root = await resolveRoot(entry.root);\n const appNode = await resolveApp(entry.app);\n timings.push(`resolve;dur=${performance.now() - resolveStart}`);\n\n const ssrModuleStart = performance.now();\n const ssrEntryModule = await import.meta.viteRsc.loadModule<\n typeof import(\"../ssr/entry\")\n >(\"ssr\");\n timings.push(`ssr-module;dur=${performance.now() - ssrModuleStart}`);\n\n if (ssrEnabled) {\n // SSR on: single RSC stream with full tree\n const rscStart = performance.now();\n const rootRscStream = renderToReadableStream<RscPayload>({\n root: <Root>{appNode}</Root>,\n });\n timings.push(`rsc;dur=${performance.now() - rscStart}`);\n\n const ssrStart = performance.now();\n const ssrResult = await ssrEntryModule.renderHTML(rootRscStream, {\n appEntryMarker: marker,\n build: false,\n ssr: true,\n deferRegistry,\n });\n timings.push(`ssr;dur=${performance.now() - ssrStart}`);\n\n return new Response(ssrResult.stream, {\n status: ssrResult.status,\n headers: {\n \"Content-type\": \"text/html\",\n \"Server-Timing\": timings.join(\", \"),\n },\n });\n } else {\n // SSR off: shell RSC for SSR, full RSC for client\n const rscStart = performance.now();\n const shellRscStream = renderToReadableStream<RscPayload>({\n root: (\n <Root>\n <span id={marker} />\n </Root>\n ),\n });\n const clientRscStream = renderToReadableStream<RscPayload>({\n root: <Root>{appNode}</Root>,\n });\n timings.push(`rsc;dur=${performance.now() - rscStart}`);\n\n const ssrStart = performance.now();\n const ssrResult = await ssrEntryModule.renderHTML(shellRscStream, {\n appEntryMarker: marker,\n build: false,\n ssr: false,\n clientRscStream,\n });\n timings.push(`ssr;dur=${performance.now() - ssrStart}`);\n\n return new Response(ssrResult.stream, {\n status: ssrResult.status,\n headers: {\n \"Content-type\": \"text/html\",\n \"Server-Timing\": timings.join(\", \"),\n },\n });\n }\n}\n\n/**\n * Entrypoint to serve HTML response in dev environment.\n * Accepts a Request to determine which entry to render based on URL path.\n */\nexport async function serveHTML(request: Request): Promise<Response> {\n const timings: string[] = [];\n\n const entriesStart = performance.now();\n const entries = await loadEntriesList();\n timings.push(`entries;dur=${performance.now() - entriesStart}`);\n\n const url = new URL(request.url);\n const urlPath = stripBasePath(url.pathname);\n let entry = findEntryForUrlPath(entries, urlPath);\n\n // SPA fallback: if no entry matched, fall back to index.html or index.htm entry\n if (!entry) {\n entry = entries.find(\n (e) => e.path === \"index.html\" || e.path === \"index.htm\",\n );\n }\n\n if (!entry) {\n return new Response(\"Not Found\", {\n status: 404,\n headers: { \"Content-type\": \"text/plain\" },\n });\n }\n\n return renderEntryToResponse(entry, timings);\n}\n\nclass ServeRSCError extends Error {\n status: 404 | 500;\n constructor(message: string, status: 404 | 500) {\n super(message);\n this.name = \"ServeRSCError\";\n this.status = status;\n }\n}\n\nexport function isServeRSCError(error: unknown): error is ServeRSCError {\n return error instanceof Error && error.name === \"ServeRSCError\";\n}\n\n/**\n * Serves an RSC stream response\n */\nexport async function serveRSC(request: Request): Promise<Response> {\n const timings: string[] = [];\n const url = new URL(request.url);\n const pathname = stripBasePath(url.pathname);\n if (pathname === devMainRscPath) {\n // root RSC stream is requested (HMR re-fetch always sends full tree)\n // For HMR, re-render the first entry (single-entry mode) or index.html entry\n const entriesStart = performance.now();\n const entries = await loadEntriesList();\n timings.push(`entries;dur=${performance.now() - entriesStart}`);\n\n // Use the first entry for HMR re-fetch\n const entry = entries[0];\n if (!entry) {\n throw new ServeRSCError(\"No entries defined\", 404);\n }\n\n const resolveStart = performance.now();\n const Root = await resolveRoot(entry.root);\n const appNode = await resolveApp(entry.app);\n timings.push(`resolve;dur=${performance.now() - resolveStart}`);\n\n const rscStart = performance.now();\n const rootRscStream = renderToReadableStream<RscPayload>({\n root: <Root>{appNode}</Root>,\n });\n timings.push(`rsc;dur=${performance.now() - rscStart}`);\n\n return new Response(rootRscStream, {\n status: 200,\n headers: {\n \"content-type\": \"text/x-component;charset=utf-8\",\n \"Server-Timing\": timings.join(\", \"),\n },\n });\n }\n\n const moduleId = extractIDFromModulePath(pathname);\n if (!moduleId) {\n throw new ServeRSCError(`Invalid RSC module path: ${pathname}`, 404);\n }\n\n const deferLoadStart = performance.now();\n const entry = deferRegistry.load(moduleId);\n if (!entry) {\n throw new ServeRSCError(`RSC component not found: ${moduleId}`, 404);\n }\n timings.push(`defer-load;dur=${performance.now() - deferLoadStart}`);\n\n const { state } = entry;\n switch (state.state) {\n case \"streaming\": {\n return new Response(state.stream, {\n status: 200,\n headers: {\n \"content-type\": \"text/x-component;charset=utf-8\",\n \"Server-Timing\": timings.join(\", \"),\n },\n });\n }\n case \"ready\": {\n return new Response(await entry.drainPromise, {\n status: 200,\n headers: {\n \"content-type\": \"text/x-component;charset=utf-8\",\n \"Server-Timing\": timings.join(\", \"),\n },\n });\n }\n case \"error\": {\n throw new ServeRSCError(\n `Failed to load RSC component: ${state.error}`,\n 500,\n );\n }\n }\n}\n\n/**\n * Build handler — iterates over all entries and returns per-entry results\n * along with the shared defer registry.\n */\nexport async function build() {\n const getEntries = (await import(\"virtual:funstack/entries\")).default;\n\n const ssrEntryModule = await import.meta.viteRsc.loadModule<\n typeof import(\"../ssr/entry\")\n >(\"ssr\");\n\n const results: EntryBuildResult[] = [];\n for await (const entry of getEntries()) {\n const Root = await resolveRoot(entry.root);\n const appNode = await resolveApp(entry.app);\n\n const marker = generateAppMarker();\n\n let rootRscStream: ReadableStream<Uint8Array>;\n let appRscStream: ReadableStream<Uint8Array>;\n\n if (ssrEnabled) {\n // SSR on: both streams have full tree\n rootRscStream = renderToReadableStream<RscPayload>({\n root: <Root>{appNode}</Root>,\n });\n appRscStream = renderToReadableStream<RscPayload>({\n root: <Root>{appNode}</Root>,\n });\n } else {\n // SSR off: root stream has shell, app stream has App only\n rootRscStream = renderToReadableStream<RscPayload>({\n root: (\n <Root>\n <span id={marker} />\n </Root>\n ),\n });\n appRscStream = renderToReadableStream<RscPayload>({\n root: appNode,\n });\n }\n\n const ssrResult = await ssrEntryModule.renderHTML(rootRscStream, {\n appEntryMarker: marker,\n build: true,\n ssr: ssrEnabled,\n deferRegistry,\n });\n\n results.push({\n path: entry.path,\n html: ssrResult.stream,\n appRsc: appRscStream,\n });\n }\n\n return {\n entries: results,\n deferRegistry,\n };\n}\n\nexport { defer } from \"./defer\";\n\nif (import.meta.hot) {\n import.meta.hot.accept();\n}\n"],"mappings":"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"}
|
package/dist/rsc/marker.mjs
CHANGED
package/dist/rsc/request.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { createElement } from "react";
|
|
2
|
-
|
|
3
2
|
//#region src/rsc/resolveEntry.ts
|
|
4
3
|
/**
|
|
5
4
|
* Resolves the root field of an EntryDefinition to a concrete React component.
|
|
@@ -23,7 +22,7 @@ async function resolveApp(app) {
|
|
|
23
22
|
if (isAppModule(resolved)) return createElement(resolved.default);
|
|
24
23
|
return resolved;
|
|
25
24
|
}
|
|
26
|
-
|
|
27
25
|
//#endregion
|
|
28
26
|
export { resolveApp, resolveRoot };
|
|
27
|
+
|
|
29
28
|
//# sourceMappingURL=resolveEntry.mjs.map
|