@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveEntry.mjs","names":[],"sources":["../../src/rsc/resolveEntry.ts"],"sourcesContent":["import { createElement } from \"react\";\nimport type { EntryDefinition } from \"../entryDefinition\";\n\n/**\n * Resolves the root field of an EntryDefinition to a concrete React component.\n */\nexport async function resolveRoot(\n root: EntryDefinition[\"root\"],\n): Promise<React.ComponentType<{ children: React.ReactNode }>> {\n const module = typeof root === \"function\" ? await root() : await root;\n return module.default;\n}\n\n/**\n * Checks whether a value is an AppModule (has a `default` property that is a function).\n */\nfunction isAppModule(\n value: unknown,\n): value is { default: React.ComponentType } {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"default\" in value &&\n typeof (value as Record<string, unknown>).default === \"function\"\n );\n}\n\n/**\n * Resolves the app field of an EntryDefinition to a React node.\n */\nexport async function resolveApp(\n app: EntryDefinition[\"app\"],\n): Promise<React.ReactNode> {\n if (typeof app === \"function\") {\n // Lazy import: () => Promise<{ default: Component }>\n const module = await app();\n return createElement(module.default);\n }\n if (isAppModule(app)) {\n // Sync module object: { default: Component }\n return createElement(app.default);\n }\n // Could be a Promise<AppModule> or a ReactNode (including Promise<ReactNode>).\n // Await it and check the resolved value.\n const resolved = await app;\n if (isAppModule(resolved)) {\n return createElement(resolved.default);\n }\n // ReactNode (JSX of a server component, or a resolved ReactNode)\n return resolved;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"resolveEntry.mjs","names":[],"sources":["../../src/rsc/resolveEntry.ts"],"sourcesContent":["import { createElement } from \"react\";\nimport type { EntryDefinition } from \"../entryDefinition\";\n\n/**\n * Resolves the root field of an EntryDefinition to a concrete React component.\n */\nexport async function resolveRoot(\n root: EntryDefinition[\"root\"],\n): Promise<React.ComponentType<{ children: React.ReactNode }>> {\n const module = typeof root === \"function\" ? await root() : await root;\n return module.default;\n}\n\n/**\n * Checks whether a value is an AppModule (has a `default` property that is a function).\n */\nfunction isAppModule(\n value: unknown,\n): value is { default: React.ComponentType } {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"default\" in value &&\n typeof (value as Record<string, unknown>).default === \"function\"\n );\n}\n\n/**\n * Resolves the app field of an EntryDefinition to a React node.\n */\nexport async function resolveApp(\n app: EntryDefinition[\"app\"],\n): Promise<React.ReactNode> {\n if (typeof app === \"function\") {\n // Lazy import: () => Promise<{ default: Component }>\n const module = await app();\n return createElement(module.default);\n }\n if (isAppModule(app)) {\n // Sync module object: { default: Component }\n return createElement(app.default);\n }\n // Could be a Promise<AppModule> or a ReactNode (including Promise<ReactNode>).\n // Await it and check the resolved value.\n const resolved = await app;\n if (isAppModule(resolved)) {\n return createElement(resolved.default);\n }\n // ReactNode (JSX of a server component, or a resolved ReactNode)\n return resolved;\n}\n"],"mappings":";;;;;AAMA,eAAsB,YACpB,MAC6D;AAE7D,SADe,OAAO,SAAS,aAAa,MAAM,MAAM,GAAG,MAAM,MACnD;;;;;AAMhB,SAAS,YACP,OAC2C;AAC3C,QACE,OAAO,UAAU,YACjB,UAAU,QACV,aAAa,SACb,OAAQ,MAAkC,YAAY;;;;;AAO1D,eAAsB,WACpB,KAC0B;AAC1B,KAAI,OAAO,QAAQ,WAGjB,QAAO,eADQ,MAAM,KAAK,EACE,QAAQ;AAEtC,KAAI,YAAY,IAAI,CAElB,QAAO,cAAc,IAAI,QAAQ;CAInC,MAAM,WAAW,MAAM;AACvB,KAAI,YAAY,SAAS,CACvB,QAAO,cAAc,SAAS,QAAQ;AAGxC,QAAO"}
|
package/dist/rsc/rscModule.mjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
//#region src/rsc/rscModule.ts
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Default directory name for RSC payload files.
|
|
4
4
|
*/
|
|
5
|
-
const
|
|
5
|
+
const defaultRscPayloadDir = "fun:rsc-payload";
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Combines the RSC payload directory with a raw ID to form a
|
|
8
|
+
* namespaced payload ID (e.g. "fun:rsc-payload/abc123").
|
|
9
9
|
*/
|
|
10
|
-
function getPayloadIDFor(rawId) {
|
|
11
|
-
return `${
|
|
10
|
+
function getPayloadIDFor(rawId, rscPayloadDir = defaultRscPayloadDir) {
|
|
11
|
+
return `${rscPayloadDir}/${rawId}`;
|
|
12
12
|
}
|
|
13
13
|
const rscModulePathPrefix = "/funstack__/";
|
|
14
14
|
const rscModulePathSuffix = ".txt";
|
|
@@ -19,7 +19,7 @@ function extractIDFromModulePath(modulePath) {
|
|
|
19
19
|
if (!modulePath.startsWith(rscModulePathPrefix) || !modulePath.endsWith(rscModulePathSuffix)) return;
|
|
20
20
|
return modulePath.slice(12, -4);
|
|
21
21
|
}
|
|
22
|
-
|
|
23
22
|
//#endregion
|
|
24
|
-
export { extractIDFromModulePath, getModulePathFor, getPayloadIDFor };
|
|
23
|
+
export { defaultRscPayloadDir, extractIDFromModulePath, getModulePathFor, getPayloadIDFor };
|
|
24
|
+
|
|
25
25
|
//# sourceMappingURL=rscModule.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rscModule.mjs","names":[],"sources":["../../src/rsc/rscModule.ts"],"sourcesContent":["/**\n *
|
|
1
|
+
{"version":3,"file":"rscModule.mjs","names":[],"sources":["../../src/rsc/rscModule.ts"],"sourcesContent":["/**\n * Default directory name for RSC payload files.\n */\nexport const defaultRscPayloadDir = \"fun:rsc-payload\";\n\n/**\n * Combines the RSC payload directory with a raw ID to form a\n * namespaced payload ID (e.g. \"fun:rsc-payload/abc123\").\n */\nexport function getPayloadIDFor(\n rawId: string,\n rscPayloadDir: string = defaultRscPayloadDir,\n): string {\n return `${rscPayloadDir}/${rawId}`;\n}\n\nconst rscModulePathPrefix = \"/funstack__/\";\nconst rscModulePathSuffix = \".txt\";\n\nexport function getModulePathFor(id: string): string {\n return `${rscModulePathPrefix}${id}${rscModulePathSuffix}`;\n}\n\nexport function extractIDFromModulePath(\n modulePath: string,\n): string | undefined {\n if (\n !modulePath.startsWith(rscModulePathPrefix) ||\n !modulePath.endsWith(rscModulePathSuffix)\n ) {\n return undefined;\n }\n return modulePath.slice(\n rscModulePathPrefix.length,\n -rscModulePathSuffix.length,\n );\n}\n"],"mappings":";;;;AAGA,MAAa,uBAAuB;;;;;AAMpC,SAAgB,gBACd,OACA,gBAAwB,sBAChB;AACR,QAAO,GAAG,cAAc,GAAG;;AAG7B,MAAM,sBAAsB;AAC5B,MAAM,sBAAsB;AAE5B,SAAgB,iBAAiB,IAAoB;AACnD,QAAO,GAAG,sBAAsB,KAAK;;AAGvC,SAAgB,wBACd,YACoB;AACpB,KACE,CAAC,WAAW,WAAW,oBAAoB,IAC3C,CAAC,WAAW,SAAS,oBAAoB,CAEzC;AAEF,QAAO,WAAW,MAChB,IACA,GACD"}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { getModulePathFor } from "../rsc/rscModule.mjs";
|
|
2
2
|
import { withBasePath } from "../util/basePath.mjs";
|
|
3
3
|
import { createFromFetch } from "@vitejs/plugin-rsc/browser";
|
|
4
|
-
import
|
|
5
|
-
|
|
4
|
+
import { createContext, use } from "react";
|
|
6
5
|
//#region src/rsc-client/clientWrapper.tsx
|
|
7
6
|
const RegistryContext = createContext(void 0);
|
|
8
7
|
const DeferredComponent = ({ moduleID }) => {
|
|
@@ -38,7 +37,7 @@ function getClientRSCStream(modulePath) {
|
|
|
38
37
|
}
|
|
39
38
|
return stream;
|
|
40
39
|
}
|
|
41
|
-
|
|
42
40
|
//#endregion
|
|
43
41
|
export { DeferredComponent, RegistryContext };
|
|
42
|
+
|
|
44
43
|
//# sourceMappingURL=clientWrapper.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clientWrapper.mjs","names":[],"sources":["../../src/rsc-client/clientWrapper.tsx"],"sourcesContent":["import React from \"react\";\nimport { createFromFetch } from \"@vitejs/plugin-rsc/browser\";\nimport { getModulePathFor } from \"../rsc/rscModule\";\nimport { createContext, use } from \"react\";\nimport type { LoadedDeferEntry, DeferRegistry } from \"../rsc/defer\";\nimport { withBasePath } from \"../util/basePath\";\n\ninterface DeferContextValue {\n registry: DeferRegistry;\n createFromReadableStream: <T>(\n stream: ReadableStream<Uint8Array>,\n ) => Promise<T>;\n}\n\nexport const RegistryContext = createContext<DeferContextValue | undefined>(\n undefined,\n);\n\ninterface DeferredComponentProps {\n moduleID: string;\n}\n\nexport const DeferredComponent: React.FC<DeferredComponentProps> = ({\n moduleID,\n}) => {\n const deferContext = use(RegistryContext);\n const modulePath = getModulePathFor(moduleID);\n if (deferContext) {\n const entry = deferContext.registry.load(moduleID);\n if (!entry) {\n throw new Error(`Module entry not found for ID '${moduleID}'`);\n }\n return getRSCStreamFromRegistry(\n entry,\n deferContext.createFromReadableStream,\n );\n }\n const stream = getClientRSCStream(withBasePath(modulePath));\n return use(stream);\n};\n\nconst moduleToStreamMap = new Map<string, Promise<React.ReactNode>>();\n\nasync function getRSCStreamFromRegistry(\n entry: LoadedDeferEntry,\n createFromReadableStream: <T>(\n stream: ReadableStream<Uint8Array>,\n ) => Promise<T>,\n): Promise<React.ReactNode> {\n switch (entry.state.state) {\n case \"streaming\": {\n return createFromReadableStream<React.ReactNode>(entry.state.stream);\n }\n case \"ready\": {\n const data = await entry.drainPromise;\n const stream = new ReadableStream<Uint8Array>({\n start(controller) {\n const encoder = new TextEncoder();\n controller.enqueue(encoder.encode(data));\n controller.close();\n },\n });\n return createFromReadableStream<React.ReactNode>(stream);\n }\n case \"error\": {\n return Promise.reject(entry.state.error);\n }\n }\n}\n\nfunction getClientRSCStream(modulePath: string) {\n let stream = moduleToStreamMap.get(modulePath);\n if (!stream) {\n stream = createFromFetch<React.ReactNode>(fetch(modulePath));\n moduleToStreamMap.set(modulePath, stream);\n }\n return stream;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"clientWrapper.mjs","names":[],"sources":["../../src/rsc-client/clientWrapper.tsx"],"sourcesContent":["import React from \"react\";\nimport { createFromFetch } from \"@vitejs/plugin-rsc/browser\";\nimport { getModulePathFor } from \"../rsc/rscModule\";\nimport { createContext, use } from \"react\";\nimport type { LoadedDeferEntry, DeferRegistry } from \"../rsc/defer\";\nimport { withBasePath } from \"../util/basePath\";\n\ninterface DeferContextValue {\n registry: DeferRegistry;\n createFromReadableStream: <T>(\n stream: ReadableStream<Uint8Array>,\n ) => Promise<T>;\n}\n\nexport const RegistryContext = createContext<DeferContextValue | undefined>(\n undefined,\n);\n\ninterface DeferredComponentProps {\n moduleID: string;\n}\n\nexport const DeferredComponent: React.FC<DeferredComponentProps> = ({\n moduleID,\n}) => {\n const deferContext = use(RegistryContext);\n const modulePath = getModulePathFor(moduleID);\n if (deferContext) {\n const entry = deferContext.registry.load(moduleID);\n if (!entry) {\n throw new Error(`Module entry not found for ID '${moduleID}'`);\n }\n return getRSCStreamFromRegistry(\n entry,\n deferContext.createFromReadableStream,\n );\n }\n const stream = getClientRSCStream(withBasePath(modulePath));\n return use(stream);\n};\n\nconst moduleToStreamMap = new Map<string, Promise<React.ReactNode>>();\n\nasync function getRSCStreamFromRegistry(\n entry: LoadedDeferEntry,\n createFromReadableStream: <T>(\n stream: ReadableStream<Uint8Array>,\n ) => Promise<T>,\n): Promise<React.ReactNode> {\n switch (entry.state.state) {\n case \"streaming\": {\n return createFromReadableStream<React.ReactNode>(entry.state.stream);\n }\n case \"ready\": {\n const data = await entry.drainPromise;\n const stream = new ReadableStream<Uint8Array>({\n start(controller) {\n const encoder = new TextEncoder();\n controller.enqueue(encoder.encode(data));\n controller.close();\n },\n });\n return createFromReadableStream<React.ReactNode>(stream);\n }\n case \"error\": {\n return Promise.reject(entry.state.error);\n }\n }\n}\n\nfunction getClientRSCStream(modulePath: string) {\n let stream = moduleToStreamMap.get(modulePath);\n if (!stream) {\n stream = createFromFetch<React.ReactNode>(fetch(modulePath));\n moduleToStreamMap.set(modulePath, stream);\n }\n return stream;\n}\n"],"mappings":";;;;;AAcA,MAAa,kBAAkB,cAC7B,KAAA,EACD;AAMD,MAAa,qBAAuD,EAClE,eACI;CACJ,MAAM,eAAe,IAAI,gBAAgB;CACzC,MAAM,aAAa,iBAAiB,SAAS;AAC7C,KAAI,cAAc;EAChB,MAAM,QAAQ,aAAa,SAAS,KAAK,SAAS;AAClD,MAAI,CAAC,MACH,OAAM,IAAI,MAAM,kCAAkC,SAAS,GAAG;AAEhE,SAAO,yBACL,OACA,aAAa,yBACd;;AAGH,QAAO,IADQ,mBAAmB,aAAa,WAAW,CAAC,CACzC;;AAGpB,MAAM,oCAAoB,IAAI,KAAuC;AAErE,eAAe,yBACb,OACA,0BAG0B;AAC1B,SAAQ,MAAM,MAAM,OAApB;EACE,KAAK,YACH,QAAO,yBAA0C,MAAM,MAAM,OAAO;EAEtE,KAAK,SAAS;GACZ,MAAM,OAAO,MAAM,MAAM;AAQzB,UAAO,yBAPQ,IAAI,eAA2B,EAC5C,MAAM,YAAY;IAChB,MAAM,UAAU,IAAI,aAAa;AACjC,eAAW,QAAQ,QAAQ,OAAO,KAAK,CAAC;AACxC,eAAW,OAAO;MAErB,CAAC,CACsD;;EAE1D,KAAK,QACH,QAAO,QAAQ,OAAO,MAAM,MAAM,MAAM;;;AAK9C,SAAS,mBAAmB,YAAoB;CAC9C,IAAI,SAAS,kBAAkB,IAAI,WAAW;AAC9C,KAAI,CAAC,QAAQ;AACX,WAAS,gBAAiC,MAAM,WAAW,CAAC;AAC5D,oBAAkB,IAAI,YAAY,OAAO;;AAE3C,QAAO"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { DeferredComponent, RegistryContext } from "./clientWrapper.mjs";
|
package/dist/ssr/entry.mjs
CHANGED
|
@@ -8,7 +8,6 @@ import { renderToReadableStream } from "react-dom/server.edge";
|
|
|
8
8
|
import { prerender } from "react-dom/static";
|
|
9
9
|
import { injectRSCPayload } from "rsc-html-stream/server";
|
|
10
10
|
import { preload } from "react-dom";
|
|
11
|
-
|
|
12
11
|
//#region src/ssr/entry.tsx
|
|
13
12
|
async function renderHTML(rscStream, options) {
|
|
14
13
|
const [rscStream1, rscStream2] = rscStream.tee();
|
|
@@ -43,6 +42,7 @@ async function renderHTML(rscStream, options) {
|
|
|
43
42
|
nonce: options?.nonce
|
|
44
43
|
});
|
|
45
44
|
} catch (e) {
|
|
45
|
+
if (options.build) throw e;
|
|
46
46
|
status = 500;
|
|
47
47
|
htmlStream = await renderToReadableStream(/* @__PURE__ */ jsx("html", { children: /* @__PURE__ */ jsx("body", { children: /* @__PURE__ */ jsx("noscript", { children: "Internal Server Error: SSR failed" }) }) }), {
|
|
48
48
|
bootstrapScriptContent: `globalThis.__NO_HYDRATE=1;` + bootstrapScriptContent,
|
|
@@ -59,7 +59,7 @@ async function renderHTML(rscStream, options) {
|
|
|
59
59
|
status
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
|
-
|
|
63
62
|
//#endregion
|
|
64
63
|
export { renderHTML };
|
|
64
|
+
|
|
65
65
|
//# sourceMappingURL=entry.mjs.map
|
package/dist/ssr/entry.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entry.mjs","names":[],"sources":["../../src/ssr/entry.tsx"],"sourcesContent":["import { createFromReadableStream } from \"@vitejs/plugin-rsc/ssr\";\nimport { use } from \"react\";\nimport { renderToReadableStream } from \"react-dom/server.edge\";\nimport { prerender } from \"react-dom/static\";\nimport { injectRSCPayload } from \"rsc-html-stream/server\";\nimport type { RscPayload } from \"../rsc/entry\";\nimport { appClientManifestVar } from \"../client/globals\";\nimport { rscPayloadPlaceholder } from \"../build/rscPath\";\nimport { preload } from \"react-dom\";\nimport type { DeferRegistry } from \"../rsc/defer\";\nimport { RegistryContext } from \"#rsc-client\";\n\nexport async function renderHTML(\n rscStream: ReadableStream<Uint8Array>,\n options: {\n appEntryMarker: string;\n build: boolean;\n ssr?: boolean;\n nonce?: string;\n deferRegistry?: DeferRegistry;\n clientRscStream?: ReadableStream<Uint8Array>;\n },\n): Promise<{ stream: ReadableStream<Uint8Array>; status?: number }> {\n const [rscStream1, rscStream2] = rscStream.tee();\n\n let payload: Promise<RscPayload> | undefined;\n function SsrRoot() {\n // Tip: calling `createFromReadableStream` inside a component\n // makes `preinit`/`preload` work properly.\n payload ??= createFromReadableStream<RscPayload>(rscStream1);\n if (options.build) {\n preload(rscPayloadPlaceholder, {\n crossOrigin: \"anonymous\",\n as: \"fetch\",\n });\n }\n return (\n <RegistryContext\n value={\n options.deferRegistry\n ? {\n registry: options.deferRegistry,\n createFromReadableStream,\n }\n : undefined\n }\n >\n {use(payload).root}\n </RegistryContext>\n );\n }\n\n let bootstrapScriptContent: string = \"\";\n if (options.build) {\n if (options.ssr) {\n // SSR on: no marker needed, client hydrates full document\n bootstrapScriptContent += `globalThis.${appClientManifestVar}={stream:\"${rscPayloadPlaceholder}\"};\\n`;\n } else {\n // SSR off: marker needed for client to find mount point\n bootstrapScriptContent += `globalThis.${appClientManifestVar}={marker:\"${options.appEntryMarker}\",stream:\"${rscPayloadPlaceholder}\"};\\n`;\n }\n }\n bootstrapScriptContent +=\n await import.meta.viteRsc.loadBootstrapScriptContent(\"index\");\n\n let htmlStream: ReadableStream<Uint8Array>;\n let status: number | undefined;\n try {\n if (options.build) {\n const { prelude, postponed } = await prerender(<SsrRoot />, {\n bootstrapScriptContent,\n });\n if (postponed !== null) {\n throw new Error(\"Unexpected postponed state during prerendering\");\n }\n\n htmlStream = prelude;\n } else {\n htmlStream = await renderToReadableStream(<SsrRoot />, {\n bootstrapScriptContent,\n nonce: options?.nonce,\n });\n }\n } catch (e) {\n // In
|
|
1
|
+
{"version":3,"file":"entry.mjs","names":[],"sources":["../../src/ssr/entry.tsx"],"sourcesContent":["import { createFromReadableStream } from \"@vitejs/plugin-rsc/ssr\";\nimport { use } from \"react\";\nimport { renderToReadableStream } from \"react-dom/server.edge\";\nimport { prerender } from \"react-dom/static\";\nimport { injectRSCPayload } from \"rsc-html-stream/server\";\nimport type { RscPayload } from \"../rsc/entry\";\nimport { appClientManifestVar } from \"../client/globals\";\nimport { rscPayloadPlaceholder } from \"../build/rscPath\";\nimport { preload } from \"react-dom\";\nimport type { DeferRegistry } from \"../rsc/defer\";\nimport { RegistryContext } from \"#rsc-client\";\n\nexport async function renderHTML(\n rscStream: ReadableStream<Uint8Array>,\n options: {\n appEntryMarker: string;\n build: boolean;\n ssr?: boolean;\n nonce?: string;\n deferRegistry?: DeferRegistry;\n clientRscStream?: ReadableStream<Uint8Array>;\n },\n): Promise<{ stream: ReadableStream<Uint8Array>; status?: number }> {\n const [rscStream1, rscStream2] = rscStream.tee();\n\n let payload: Promise<RscPayload> | undefined;\n function SsrRoot() {\n // Tip: calling `createFromReadableStream` inside a component\n // makes `preinit`/`preload` work properly.\n payload ??= createFromReadableStream<RscPayload>(rscStream1);\n if (options.build) {\n preload(rscPayloadPlaceholder, {\n crossOrigin: \"anonymous\",\n as: \"fetch\",\n });\n }\n return (\n <RegistryContext\n value={\n options.deferRegistry\n ? {\n registry: options.deferRegistry,\n createFromReadableStream,\n }\n : undefined\n }\n >\n {use(payload).root}\n </RegistryContext>\n );\n }\n\n let bootstrapScriptContent: string = \"\";\n if (options.build) {\n if (options.ssr) {\n // SSR on: no marker needed, client hydrates full document\n bootstrapScriptContent += `globalThis.${appClientManifestVar}={stream:\"${rscPayloadPlaceholder}\"};\\n`;\n } else {\n // SSR off: marker needed for client to find mount point\n bootstrapScriptContent += `globalThis.${appClientManifestVar}={marker:\"${options.appEntryMarker}\",stream:\"${rscPayloadPlaceholder}\"};\\n`;\n }\n }\n bootstrapScriptContent +=\n await import.meta.viteRsc.loadBootstrapScriptContent(\"index\");\n\n let htmlStream: ReadableStream<Uint8Array>;\n let status: number | undefined;\n try {\n if (options.build) {\n const { prelude, postponed } = await prerender(<SsrRoot />, {\n bootstrapScriptContent,\n });\n if (postponed !== null) {\n throw new Error(\"Unexpected postponed state during prerendering\");\n }\n\n htmlStream = prelude;\n } else {\n htmlStream = await renderToReadableStream(<SsrRoot />, {\n bootstrapScriptContent,\n nonce: options?.nonce,\n });\n }\n } catch (e) {\n if (options.build) {\n // In build mode, abort the build so the error is not silently swallowed.\n throw e;\n }\n // In dev mode, RSC payload is still sent to client and we let client render from scratch anyway.\n // This triggers the error boundary on client side.\n status = 500;\n htmlStream = await renderToReadableStream(\n <html>\n <body>\n <noscript>Internal Server Error: SSR failed</noscript>\n </body>\n </html>,\n {\n bootstrapScriptContent:\n `globalThis.__NO_HYDRATE=1;` + bootstrapScriptContent,\n nonce: options?.nonce,\n },\n );\n }\n\n let responseStream = htmlStream;\n\n // Inject RSC payload into HTML for client consumption.\n // In dev: always inject (client reads from inline stream).\n // In build+SSR: skip (HTML already has full content, client hydrates directly).\n // In build+no-SSR: skip (client fetches RSC from separate file).\n if (!options.build) {\n const streamToInject = options.clientRscStream ?? rscStream2;\n responseStream = responseStream.pipeThrough(\n injectRSCPayload(streamToInject, {\n nonce: options?.nonce,\n }),\n );\n }\n\n return { stream: responseStream, status };\n}\n"],"mappings":";;;;;;;;;;;AAYA,eAAsB,WACpB,WACA,SAQkE;CAClE,MAAM,CAAC,YAAY,cAAc,UAAU,KAAK;CAEhD,IAAI;CACJ,SAAS,UAAU;AAGjB,cAAY,yBAAqC,WAAW;AAC5D,MAAI,QAAQ,MACV,SAAQ,uBAAuB;GAC7B,aAAa;GACb,IAAI;GACL,CAAC;AAEJ,SACE,oBAAC,iBAAD;GACE,OACE,QAAQ,gBACJ;IACE,UAAU,QAAQ;IAClB;IACD,GACD,KAAA;aAGL,IAAI,QAAQ,CAAC;GACE,CAAA;;CAItB,IAAI,yBAAiC;AACrC,KAAI,QAAQ,MACV,KAAI,QAAQ,IAEV,2BAA0B,cAAc,qBAAqB,YAAY,sBAAsB;KAG/F,2BAA0B,cAAc,qBAAqB,YAAY,QAAQ,eAAe,YAAY,sBAAsB;AAGtI,2BACE,MAAM,OAAO,KAAK,QAAQ,2BAA2B,QAAQ;CAE/D,IAAI;CACJ,IAAI;AACJ,KAAI;AACF,MAAI,QAAQ,OAAO;GACjB,MAAM,EAAE,SAAS,cAAc,MAAM,UAAU,oBAAC,SAAD,EAAW,CAAA,EAAE,EAC1D,wBACD,CAAC;AACF,OAAI,cAAc,KAChB,OAAM,IAAI,MAAM,iDAAiD;AAGnE,gBAAa;QAEb,cAAa,MAAM,uBAAuB,oBAAC,SAAD,EAAW,CAAA,EAAE;GACrD;GACA,OAAO,SAAS;GACjB,CAAC;UAEG,GAAG;AACV,MAAI,QAAQ,MAEV,OAAM;AAIR,WAAS;AACT,eAAa,MAAM,uBACjB,oBAAC,QAAD,EAAA,UACE,oBAAC,QAAD,EAAA,UACE,oBAAC,YAAD,EAAA,UAAU,qCAA4C,CAAA,EACjD,CAAA,EACF,CAAA,EACP;GACE,wBACE,+BAA+B;GACjC,OAAO,SAAS;GACjB,CACF;;CAGH,IAAI,iBAAiB;AAMrB,KAAI,CAAC,QAAQ,OAAO;EAClB,MAAM,iBAAiB,QAAQ,mBAAmB;AAClD,mBAAiB,eAAe,YAC9B,iBAAiB,gBAAgB,EAC/B,OAAO,SAAS,OACjB,CAAC,CACH;;AAGH,QAAO;EAAE,QAAQ;EAAgB;EAAQ"}
|
package/dist/util/basePath.mjs
CHANGED
package/dist/util/urlPath.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@funstack/static",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "FUNSTACK static library",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -47,23 +47,23 @@
|
|
|
47
47
|
"license": "MIT",
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@playwright/test": "^1.58.2",
|
|
50
|
-
"@types/node": "^25.
|
|
50
|
+
"@types/node": "^25.3.5",
|
|
51
51
|
"@types/react": "^19.2.14",
|
|
52
52
|
"@types/react-dom": "^19.2.3",
|
|
53
53
|
"jsdom": "^28.1.0",
|
|
54
54
|
"react": "^19.2.4",
|
|
55
55
|
"react-dom": "^19.2.4",
|
|
56
|
-
"tsdown": "^0.
|
|
56
|
+
"tsdown": "^0.21.0",
|
|
57
57
|
"typescript": "^5.9.3",
|
|
58
58
|
"vite": "^7.3.1",
|
|
59
59
|
"vitest": "^4.0.18"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"@funstack/skill-installer": "^1.0.0",
|
|
63
|
-
"@vitejs/plugin-rsc": "^0.5.
|
|
63
|
+
"@vitejs/plugin-rsc": "^0.5.21",
|
|
64
64
|
"react-error-boundary": "^6.1.1",
|
|
65
65
|
"rsc-html-stream": "^0.0.7",
|
|
66
|
-
"srvx": "^0.11.
|
|
66
|
+
"srvx": "^0.11.8"
|
|
67
67
|
},
|
|
68
68
|
"peerDependencies": {
|
|
69
69
|
"react": "^19.2.3",
|
|
@@ -16,7 +16,7 @@ FUNSTACk Static is served as a Vite plugin. See your app's `vite.config.ts` file
|
|
|
16
16
|
```ts
|
|
17
17
|
import { defineConfig } from "vite";
|
|
18
18
|
import react from "@vitejs/plugin-react";
|
|
19
|
-
import
|
|
19
|
+
import funstackStatic from "@funstack/static";
|
|
20
20
|
|
|
21
21
|
export default defineConfig({
|
|
22
22
|
plugins: [
|