@lomray/vite-ssr-boost 8.1.0 → 8.2.0-beta.1
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 +102 -16
- package/adapters/express/prepare-server.js +1 -1
- package/adapters/express/prepare-server.js.map +1 -1
- package/adapters/express/render.js +1 -1
- package/adapters/express/render.js.map +1 -1
- package/core/compose-html.js +1 -1
- package/core/compose-html.js.map +1 -1
- package/core/handler.d.ts +5 -1
- package/core/handler.js +1 -1
- package/core/handler.js.map +1 -1
- package/core/render.d.ts +2 -0
- package/core/render.js +1 -1
- package/core/render.js.map +1 -1
- package/core/transform-html.d.ts +2 -1
- package/core/transform-html.js +1 -1
- package/core/transform-html.js.map +1 -1
- package/helpers/build-custom-state.d.ts +2 -1
- package/helpers/build-custom-state.js +1 -1
- package/helpers/build-custom-state.js.map +1 -1
- package/helpers/build-router-state.d.ts +2 -1
- package/helpers/build-router-state.js +1 -1
- package/helpers/build-router-state.js.map +1 -1
- package/node/production.js +1 -1
- package/node/production.js.map +1 -1
- package/package.json +2 -1
- package/services/diagnostics.d.ts +66 -0
- package/services/diagnostics.js +2 -0
- package/services/diagnostics.js.map +1 -0
package/README.md
CHANGED
|
@@ -1,6 +1,56 @@
|
|
|
1
1
|
# Vite SSR BOOST
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<p align="center"><img src="https://raw.githubusercontent.com/Lomray-Software/vite-ssr-boost/prod/logo.png" alt="Vite SSR BOOST logo" width="250" height="250"></p>
|
|
4
|
+
|
|
5
|
+
<p align="center">SSR for React Router apps in <a href="https://reactrouter.com/start/modes">Data mode</a>, without moving to Framework mode and without rewriting the app.</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://sonarcloud.io/dashboard?id=vite-ssr-boost"><img src="https://sonarcloud.io/api/project_badges/measure?project=vite-ssr-boost&metric=reliability_rating" alt="Reliability Rating"></a>
|
|
9
|
+
<a href="https://sonarcloud.io/dashboard?id=vite-ssr-boost"><img src="https://sonarcloud.io/api/project_badges/measure?project=vite-ssr-boost&metric=security_rating" alt="Security Rating"></a>
|
|
10
|
+
<a href="https://sonarcloud.io/dashboard?id=vite-ssr-boost"><img src="https://sonarcloud.io/api/project_badges/measure?project=vite-ssr-boost&metric=sqale_rating" alt="Maintainability Rating"></a>
|
|
11
|
+
<a href="https://sonarcloud.io/dashboard?id=vite-ssr-boost"><img src="https://sonarcloud.io/api/project_badges/measure?project=vite-ssr-boost&metric=vulnerabilities" alt="Vulnerabilities"></a>
|
|
12
|
+
<a href="https://sonarcloud.io/dashboard?id=vite-ssr-boost"><img src="https://sonarcloud.io/api/project_badges/measure?project=vite-ssr-boost&metric=bugs" alt="Bugs"></a>
|
|
13
|
+
<a href="https://sonarcloud.io/dashboard?id=vite-ssr-boost"><img src="https://sonarcloud.io/api/project_badges/measure?project=vite-ssr-boost&metric=ncloc" alt="Lines of Code"></a>
|
|
14
|
+
<a href="https://sonarcloud.io/dashboard?id=vite-ssr-boost"><img src="https://sonarcloud.io/api/project_badges/measure?project=vite-ssr-boost&metric=coverage" alt="Code Coverage"></a>
|
|
15
|
+
<a href="https://www.npmjs.com/package/@lomray/vite-ssr-boost"><img src="https://img.shields.io/npm/v/@lomray/vite-ssr-boost?label=semantic%20release&logo=semantic-release" alt="npm version"></a>
|
|
16
|
+
<a href="https://www.npmjs.com/package/@lomray/vite-ssr-boost"><img src="https://img.shields.io/npm/dm/@lomray/vite-ssr-boost" alt="npm monthly downloads"></a>
|
|
17
|
+
<a href="./LICENSE"><img src="https://img.shields.io/npm/l/@lomray/vite-ssr-boost" alt="MIT License"></a>
|
|
18
|
+
<a href="https://www.npmjs.com/package/@lomray/vite-ssr-boost"><img src="https://img.shields.io/node/v/@lomray/vite-ssr-boost" alt="Node version requirement"></a>
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
## Quick start
|
|
22
|
+
|
|
23
|
+
**Start a new app** with the `minimal` template (default):
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm create @lomray/ssr-app@latest my-app
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Choose the `full` template by passing the flag after `--`:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm create @lomray/ssr-app@latest my-app -- --template full
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Add to an existing app:**
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm i @lomray/vite-ssr-boost
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`@lomray/vite-ssr-boost` adds server rendering to your Vite project while keeping your route objects and components. Build SSR and SPA output from the same app, and choose a managed Express server or a Fetch handler for your own transport.
|
|
42
|
+
|
|
43
|
+
The [migration guide](./docs/guide/migrate-existing-spa.md) walks through the five-file change:
|
|
44
|
+
|
|
45
|
+
| File | Change |
|
|
46
|
+
| ---------------- | ---------------------------------------------------- |
|
|
47
|
+
| `vite.config.ts` | Add `SsrBoost()` to the plugins. |
|
|
48
|
+
| `src/index.html` | Add the SSR outlet inside the root element. |
|
|
49
|
+
| `src/client.ts` | Use the browser entry for hydration or SPA mounting. |
|
|
50
|
+
| `src/server.ts` | Add the server entry and request-scoped setup. |
|
|
51
|
+
| `package.json` | Use the `ssr-boost` dev, build and start commands. |
|
|
52
|
+
|
|
53
|
+
Starting a new app? Choose one of the [template branches](./docs/examples/index.md) with the [`npm create` command](./docs/guide/getting-started.md#create-a-new-app) or clone the branch directly.
|
|
4
54
|
|
|
5
55
|
## Who this is for
|
|
6
56
|
|
|
@@ -34,32 +84,68 @@ Add `SsrBoost()` to the Vite plugins, wire `client.ts` and `server.ts`, and repl
|
|
|
34
84
|
|
|
35
85
|
## Install and template quick start
|
|
36
86
|
|
|
87
|
+
The package declares `engines.node: ">=22.12.0"`. The example uses Node 22.23.2; React Router and build tools can raise the required Node version.
|
|
88
|
+
|
|
89
|
+
Start with the [minimal template](https://github.com/Lomray-Software/vite-template/tree/example/minimal):
|
|
90
|
+
|
|
37
91
|
```bash
|
|
38
|
-
npm
|
|
92
|
+
npm create @lomray/ssr-app@latest my-app
|
|
93
|
+
cd my-app
|
|
94
|
+
npm run develop
|
|
39
95
|
```
|
|
40
96
|
|
|
41
|
-
|
|
97
|
+
Or clone the template branch directly:
|
|
42
98
|
|
|
43
|
-
|
|
99
|
+
```bash
|
|
100
|
+
git clone --branch example/minimal https://github.com/Lomray-Software/vite-template.git
|
|
101
|
+
cd vite-template
|
|
102
|
+
npm ci
|
|
103
|
+
npm run develop
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Its six direct runtime dependencies are React, React DOM, React Router, vite-ssr-boost, `@lomray/react-head-manager` and `isbot`.
|
|
44
107
|
|
|
45
108
|
For production, run `npm run build` and `npm run start:ssr`. For a SPA build, run `npm run build:spa` and `npm run start:spa`. See [Example projects](./docs/examples/index.md) for the `prod` template with state management and deployment workflows.
|
|
46
109
|
|
|
47
110
|
Already running this in production? Point our [free performance audit](https://audit.lomray.com/?utm_source=github&utm_medium=readme-vite-ssr-boost&utm_campaign=owned-surface-github) at the URL. It reports Core Web Vitals, JavaScript bundle weight and whether the HTML really arrives server-rendered. No signup.
|
|
48
111
|
|
|
49
|
-
##
|
|
112
|
+
## Compatibility
|
|
50
113
|
|
|
51
|
-
|
|
114
|
+
The package is tested against the current and previous major of React, React Router and Vite, with an additional Vite 6 row. The [compatibility workflow](./.github/workflows/react-compatibility.yml) runs the full test suite and a built Worker check on Node 22.23.2 with these exact combinations:
|
|
52
115
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
116
|
+
| React / React DOM | React Router | Vite |
|
|
117
|
+
| ----------------- | ------------ | ------- |
|
|
118
|
+
| `18.2.0` | `7.18.3` | `6.4.3` |
|
|
119
|
+
| `19.2.8` | `7.18.3` | `7.3.6` |
|
|
120
|
+
| `19.2.8` | `8.3.1` | `8.2.2` |
|
|
121
|
+
|
|
122
|
+
[package.json](./package.json) declares Node `>=22.12.0` and these peer ranges:
|
|
123
|
+
|
|
124
|
+
| Peer | Range |
|
|
125
|
+
| -------------- | ---------- |
|
|
126
|
+
| `react` | `>=18.2.0` |
|
|
127
|
+
| `react-dom` | `>=18.2.0` |
|
|
128
|
+
| `react-router` | `>=7.0.1` |
|
|
129
|
+
| `vite` | `>=5` |
|
|
130
|
+
|
|
131
|
+
Peer ranges allow installation; the matrix tests only the combinations above. It does not test every version in those ranges or every combination of majors.
|
|
132
|
+
|
|
133
|
+
## Examples
|
|
134
|
+
|
|
135
|
+
The [example projects guide](./docs/examples/index.md) explains the wiring in each template branch.
|
|
136
|
+
|
|
137
|
+
| Template branch | What it includes |
|
|
138
|
+
| ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
139
|
+
| [`prod`](https://github.com/Lomray-Software/vite-template/tree/prod) | MobX state management, component-level data streaming, metadata and deployment workflows. |
|
|
140
|
+
| [`example/minimal`](https://github.com/Lomray-Software/vite-template/tree/example/minimal) | Six runtime dependencies, loaders, a lazy route with CSS, redirects, 404s, metadata and a browser-only route. |
|
|
141
|
+
| [`example/custom-server`](https://github.com/Lomray-Software/vite-template/tree/example/custom-server) | Managed CLI development and an application-owned Fastify 5 production server with static assets, compression, route assets and Early Hints. |
|
|
142
|
+
| [`example/localization`](https://github.com/Lomray-Software/vite-template/tree/example/localization) | Per-request i18next, cookie and header language selection, and language state restored before hydration. |
|
|
143
|
+
|
|
144
|
+
## Documentation
|
|
145
|
+
|
|
146
|
+
- [Documentation site](https://lomray-software.github.io/vite-ssr-boost/), [comparison guide](./docs/guide/choosing.md) and [FAQ](./docs/reference/faq.md).
|
|
147
|
+
- [Hydration order and streaming](./docs/reference/hydration-and-streaming.md).
|
|
148
|
+
- API: [Plugin](./docs/api/plugin.md), [Browser entry](./docs/api/browser-entry.md), [Server entry](./docs/api/server-entry.md), [Node production](./docs/api/node-production.md), [Components and helpers](./docs/api/components-and-helpers.md) and [CLI](./docs/api/cli.md).
|
|
63
149
|
|
|
64
150
|
## License
|
|
65
151
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e from"node:fs";import t from"node:path";import r from"node:process";import{pathToFileURL as i}from"node:url";import o from"chalk";import s from"../../services/server-api.js";class
|
|
1
|
+
import e from"node:fs";import t from"node:path";import r from"node:process";import{pathToFileURL as i}from"node:url";import o from"chalk";import{splitHtmlShell as s}from"../../services/diagnostics.js";import n from"../../services/server-api.js";class a{onServerStarted;config;serverApi;entrypoint;onServerCreated;html;middlewaresConfigs;constructor(e,t){this.config=e,this.serverApi=t??new n}static init(e,t){return new a(e,t)}async loadEntrypoint(e=!0){if(this.entrypoint&&this.config.isProd)return this.entrypoint;const{root:s,isProd:n,serverFile:a}=this.config.getParams(),d=t.resolve(`${s}/${a}`);let l;try{l=n?(await(import(i(d).toString()))).default:(await this.config.getVite().ssrLoadModule(d,{fixStacktrace:!0})).default}catch(e){if(e instanceof Error&&e.message.includes("Cannot find module")&&e.message.includes("/build/"))return this.config.getLogger().error(o.red(`Before starting the server, you need to create a build: ${o.yellow("ssr-boost build")} or provide path to build dir: ${o.yellow("ssr-boost start --build-dir build")}`)),r.exit(1);throw e}!e&&l.init&&delete l.init;const{render:c,init:g,routes:h,abortDelay:f,loggerProd:m,loggerDev:p,middlewares:u}=l;this.middlewaresConfigs=u,m&&n?this.config.setLogger(m):p&&!n&&this.config.setLogger(p);const{onServerCreated:v,onServerStarted:y,...w}=await(g?.({config:this.config}))??{};return this.entrypoint={render:c,routes:h,abortDelay:f,...w},this.onServerCreated=v,this.onServerStarted=y,this.entrypoint}async loadHtml(r){const{isProd:i,root:o,indexFile:n,clientFile:a}=this.config.getParams();this.html&&i||(this.html=e.readFileSync(t.resolve(`${o}/${n}`),"utf-8"));let d=this.html;if(!i){const e=t.posix.normalize(`${this.config.getVite()?.config.base}/${a}`);d=(await this.config.getVite().transformIndexHtml(r.originalUrl,this.html,n)).replace(new RegExp(String.raw`<script[^>]*?\bsrc=["']/?${e}([^"']*)["'][^>]*?\sasync\b`,"g"),e=>e.replace(/\sasync\b/,""))}return s(d,t.resolve(`${o}/${n}`))}async onAppCreated(){return await this.loadEntrypoint(),await(this.onServerCreated?.(this.config.getApp(),this.serverApi)),this}getMiddlewaresConfig(){const{compression:e,expressStatic:t}=this.middlewaresConfigs??{};return{compression:!1!==e&&{...e},expressStatic:!1!==t&&{...t,basename:t?.basename??"/"}}}}export{a as default};
|
|
2
2
|
//# sourceMappingURL=prepare-server.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prepare-server.js","sources":["../../../src/adapters/express/prepare-server.ts"],"sourcesContent":[null],"names":["PrepareServer","onServerStarted","config","serverApi","entrypoint","onServerCreated","html","middlewaresConfigs","constructor","this","ServerApi","init","loadEntrypoint","shouldInit","isProd","root","serverFile","getParams","entrypointPath","path","resolve","resolvedEntrypoint","pathToFileURL","toString","default","getVite","ssrLoadModule","fixStacktrace","e","Error","message","includes","getLogger","error","chalk","red","yellow","process","exit","render","routes","abortDelay","loggerProd","loggerDev","middlewares","setLogger","renderParams","loadHtml","req","indexFile","clientFile","fs","readFileSync","modifiedHtml","clientFileEntry","posix","normalize","base","transformIndexHtml","originalUrl","replace","RegExp","String","raw","match","
|
|
1
|
+
{"version":3,"file":"prepare-server.js","sources":["../../../src/adapters/express/prepare-server.ts"],"sourcesContent":[null],"names":["PrepareServer","onServerStarted","config","serverApi","entrypoint","onServerCreated","html","middlewaresConfigs","constructor","this","ServerApi","init","loadEntrypoint","shouldInit","isProd","root","serverFile","getParams","entrypointPath","path","resolve","resolvedEntrypoint","pathToFileURL","toString","default","getVite","ssrLoadModule","fixStacktrace","e","Error","message","includes","getLogger","error","chalk","red","yellow","process","exit","render","routes","abortDelay","loggerProd","loggerDev","middlewares","setLogger","renderParams","loadHtml","req","indexFile","clientFile","fs","readFileSync","modifiedHtml","clientFileEntry","posix","normalize","base","transformIndexHtml","originalUrl","replace","RegExp","String","raw","match","splitHtmlShell","onAppCreated","getApp","getMiddlewaresConfig","compression","expressStatic","basename"],"mappings":"qPA0BA,MAAMA,EAIGC,gBAKYC,OAKAC,UAKTC,WAKAC,gBAKAC,KAKAC,mBAKV,WAAAC,CAAsBN,EAAsBC,GAC1CM,KAAKP,OAASA,EACdO,KAAKN,UAAYA,GAAa,IAAIO,CACpC,CAKO,WAAOC,CAAKT,EAAsBC,GACvC,OAAO,IAAIH,EAAcE,EAAQC,EACnC,CAKO,oBAAMS,CAAeC,GAAa,GAIvC,GAAIJ,KAAKL,YAAcK,KAAKP,OAAOY,OACjC,OAAOL,KAAKL,WAGd,MAAMW,KAAEA,EAAID,OAAEA,EAAME,WAAEA,GAAeP,KAAKP,OAAOe,YAC3CC,EAAiBC,EAAKC,QAAQ,GAAGL,KAAQC,KAE/C,IAAIK,EAEJ,IAQIA,EAPGP,gBAQaQ,EAAcJ,GAAgBK,cAC5CC,eAPMf,KAAKP,OAAOuB,UAAWC,cAAcR,EAAgB,CACzDS,eAAe,KAEjBH,OAMN,CAAE,MAAOI,GACP,GACEA,aAAaC,OACbD,EAAEE,QAAQC,SAAS,uBACnBH,EAAEE,QAAQC,SAAS,WAcnB,OAZAtB,KAAKP,OACF8B,YACAC,MACCC,EAAMC,IACJ,2DAA2DD,EAAME,OAC/D,oDACiCF,EAAME,OACvC,yCAKDC,EAAQC,KAAK,GAGtB,MAAMV,CACR,EAEKf,GAAcQ,EAAmBV,aAC7BU,EAAmBV,KAG5B,MAAM4B,OAAEA,EAAM5B,KAAEA,EAAI6B,OAAEA,EAAMC,WAAEA,EAAUC,WAAEA,EAAUC,UAAEA,EAASC,YAAEA,GAC/DvB,EAEFZ,KAAKF,mBAAqBqC,EAEtBF,GAAc5B,EAChBL,KAAKP,OAAO2C,UAAUH,GACbC,IAAc7B,GACvBL,KAAKP,OAAO2C,UAAUF,GAGxB,MAAMtC,gBAAEA,EAAeJ,gBAAEA,KAAoB6C,SACpCnC,IAAO,CACZT,OAAQO,KAAKP,WACR,CAAA,EAWT,OATAO,KAAKL,WAAa,CAChBmC,SACAC,SACAC,gBACGK,GAELrC,KAAKJ,gBAAkBA,EACvBI,KAAKR,gBAAkBA,EAEhBQ,KAAKL,UACd,CAKO,cAAM2C,CAASC,GACpB,MAAMlC,OAAEA,EAAMC,KAAEA,EAAIkC,UAAEA,EAASC,WAAEA,GAAezC,KAAKP,OAAOe,YAEvDR,KAAKH,MAASQ,IACjBL,KAAKH,KAAO6C,EAAGC,aAAajC,EAAKC,QAAQ,GAAGL,KAAQkC,KAAc,UAGpE,IAAII,EAAe5C,KAAKH,KAExB,IAAKQ,EAAQ,CACX,MAAMwC,EAAkBnC,EAAKoC,MAAMC,UACjC,GAAG/C,KAAKP,OAAOuB,WAAWvB,OAAOuD,QAAQP,KAM3CG,SACQ5C,KAAKP,OAAOuB,UAAWiC,mBAAmBV,EAAIW,YAAalD,KAAKH,KAAM2C,IAC5EW,QACA,IAAIC,OACFC,OAAOC,GAAG,4BAA4BT,+BACtC,KAEDU,GAAUA,EAAMJ,QAAQ,YAAa,IAE1C,CAEA,OAAOK,EAAeZ,EAAclC,EAAKC,QAAQ,GAAGL,KAAQkC,KAC9D,CAKO,kBAAMiB,GAIX,aAHMzD,KAAKG,uBACLH,KAAKJ,kBAAkBI,KAAKP,OAAOiE,SAAoB1D,KAAKN,YAE3DM,IACT,CAKO,oBAAA2D,GACL,MAAMC,YAAEA,EAAWC,cAAEA,GAAkB7D,KAAKF,oBAAsB,CAAA,EAElE,MAAO,CACL8D,aACkB,IAAhBA,GACI,IACKA,GAGXC,eACoB,IAAlBA,GACI,IACKA,EACHC,SAAUD,GAAeC,UAAY,KAIjD"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e from"chalk";import r from"react";import t from"./create-request.js";import o from"../../constants/stream-error.js";import s from"../../core/early-hints.js";import{getHeaderEntries as n,getSetCookieHeaders as a}from"../../core/headers.js";import i from"../../core/render.js";import d from"../../node/render-to-stream.js";import c from"../../node/request-signal.js";import m from"../../node/write-early-hints.js";import p,{writeFetchHeaders as l}from"../../node/write-fetch-response.js";import u from"../../services/ssr-manifest.js";const
|
|
1
|
+
import e from"chalk";import r from"react";import t from"./create-request.js";import o from"../../constants/stream-error.js";import s from"../../core/early-hints.js";import{getHeaderEntries as n,getSetCookieHeaders as a}from"../../core/headers.js";import i from"../../core/render.js";import d from"../../node/render-to-stream.js";import c from"../../node/request-signal.js";import m from"../../node/write-early-hints.js";import p,{writeFetchHeaders as l}from"../../node/write-fetch-response.js";import u,{isDiagnosticsEnabled as h}from"../../services/diagnostics.js";import f from"../../services/ssr-manifest.js";const x=(e,r,o)=>{const s={signal:r};return o&&"GET"!==e.method&&"HEAD"!==e.method&&(s.body=o(e)??null),t(e,s)};async function y({App:t,handler:y},g,E,{onRouterReady:w,onShellReady:S,onResponse:j,onShellError:v,onError:C,getBody:R,getState:q,abortDelay:b=15e3}){const{appProps:A,html:H,req:k,res:D}=E,L=g.getLogger(),P=c(k,D);try{const c=Object.assign(E,{request:x(k,P.signal,R)}),T={appProps:A,diagnostics:h(!g.isProd)?new u(new URL(c.request.url).pathname,L):void 0,html:H,request:c.request,response:{headers:new Headers}},O=e=>(c.request=e.request,c.didError=e.didError,c.html=e.html,c.isStream=e.isStream,c.routerContext=e.routerContext,c.serverContext=e.serverContext,c),B=await i({createApp:(e,o)=>r.createElement(t,{server:{...o.appProps,req:k}},e),handler:y,renderToStream:d},T,{abortDelay:b,getState:q?({context:e})=>q({context:O(e)}):void 0,onError:({context:r,error:t})=>{const{code:s,message:n}=t;if(C?.({context:O(r),error:t}),L.info(e.red(`Stream error. Code: ${s}`)),[o.RenderAborted,o.RenderTimeout,o.RenderCancel].includes(s))return void L.info(e.dim(n));const{original:a}=t;L.error(a)},onResponse:j?({context:e,html:r,isEnd:t})=>j({context:O(e),html:r,isEnd:t}):void 0,onRouterReady:w?({context:e})=>w({context:O(e)}):void 0,onShellError:v?({context:e,error:r})=>v({context:O(e),error:r}):void 0,onShellReady:({context:e})=>{const r=O(e);((e,r)=>{if(r.headersSent||r.writableEnded)return;r.status(e.response.status??200),n(e.response.headers).forEach(([e,t])=>r.setHeader(e,t));const t=a(e.response.headers);t.length&&r.setHeader("Set-Cookie",t)})(e,D);const t=S?.({context:r});return((e,r)=>{const t=new Headers(e.response.headers);t.delete("Set-Cookie");for(const[e,o]of Object.entries(r.getHeaders()))if("set-cookie"!==e.toLowerCase()&&void 0!==o)if(Array.isArray(o))for(const r of o)t.append(e,r);else t.set(e,"number"==typeof o?String(o):o);e.response.headers=t,e.response.status=r.statusCode})(e,D),t??{}},prepare:async({context:e,executionContext:r})=>{const t=O(e),o=f.get(g);await o.prepareDevAssets(e.routerContext?.matches);const n=o.injectAssets(t);t.hasEarlyHints&&await s(r,n)},routerRequestContext:c},{onEarlyHints:e=>m(D,e)});O(T),await(async(e,r)=>{const t=r.headers.get("Location");if(t&&r.status>=300&&r.status<400&&!e.headersSent&&!e.writableEnded&&!e.destroyed)return l(e,r),await(r.body?.cancel()),void e.redirect(r.status,t);await p(e,r)})(D,B)}finally{P.dispose()}}export{y as default};
|
|
2
2
|
//# sourceMappingURL=render.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.js","sources":["../../../src/adapters/express/render.tsx"],"sourcesContent":[null],"names":["createRenderRequest","req","signal","getBody","options","method","body","createFetchRequest","async","render","App","handler","config","initialContext","onRouterReady","onShellReady","onResponse","onShellError","onError","getState","abortDelay","appProps","html","shellHtml","res","Logger","getLogger","requestSignal","createRequestSignal","context","Object","assign","request","coreContext","response","headers","Headers","syncContext","updated","didError","isStream","routerContext","serverContext","coreRender","createApp","children","React","createElement","server","renderToStream","
|
|
1
|
+
{"version":3,"file":"render.js","sources":["../../../src/adapters/express/render.tsx"],"sourcesContent":[null],"names":["createRenderRequest","req","signal","getBody","options","method","body","createFetchRequest","async","render","App","handler","config","initialContext","onRouterReady","onShellReady","onResponse","onShellError","onError","getState","abortDelay","appProps","html","shellHtml","res","Logger","getLogger","requestSignal","createRequestSignal","context","Object","assign","request","coreContext","diagnostics","isDiagnosticsEnabled","isProd","Diagnostics","URL","url","pathname","undefined","response","headers","Headers","syncContext","updated","didError","isStream","routerContext","serverContext","coreRender","createApp","children","React","createElement","server","renderToStream","error","code","message","info","chalk","red","StreamError","RenderAborted","RenderTimeout","RenderCancel","includes","dim","original","isEnd","legacyContext","headersSent","writableEnded","status","getHeaderEntries","forEach","name","value","setHeader","cookies","getSetCookieHeaders","length","prepareResponse","shell","delete","entries","getHeaders","toLowerCase","Array","isArray","item","append","set","String","statusCode","syncResponse","prepare","executionContext","manifest","SsrManifest","get","prepareDevAssets","matches","hints","injectAssets","hasEarlyHints","emitEarlyHints","routerRequestContext","onEarlyHints","writeEarlyHints","location","destroyed","writeFetchHeaders","cancel","redirect","writeFetchResponse","writeResponse","dispose"],"mappings":"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"}
|
package/core/compose-html.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=(e,a,r,o,
|
|
1
|
+
const e=(e,a,r,o,t)=>{const n=new TextEncoder,c=a.getReader();let l="header";return new ReadableStream({cancel:async e=>{if(o?.(e),"footer"!==l)try{await c.cancel(e)}finally{c.releaseLock(),t?.()}},async pull(a){if("header"===l&&(l="body",e))a.enqueue(n.encode(e));else{if("body"===l){let e;try{e=await c.read()}catch(e){return o?.(e),c.releaseLock(),t?.(),void a.error(e)}if(!e.done)return void a.enqueue(e.value);l="footer",c.releaseLock(),t?.()}r&&a.enqueue(n.encode(r)),a.close()}}},{highWaterMark:0})};export{e as default};
|
|
2
2
|
//# sourceMappingURL=compose-html.js.map
|
package/core/compose-html.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compose-html.js","sources":["../../src/core/compose-html.ts"],"sourcesContent":[null],"names":["composeHtml","header","body","footer","abort","onComplete","encoder","TextEncoder","reader","getReader","phase","ReadableStream","cancel","async","reason","releaseLock","pull","controller","enqueue","encode","chunk","read","error","done","value","close"],"mappings":"AAGA,MAAMA,EAAc,CAClBC,EACAC,EACAC,EACAC,EACAC,KAEA,MAAMC,EAAU,IAAIC,YACdC,EAASN,EAAKO,YACpB,IAAIC,EAAsC,SAE1C,OAAO,IAAIC,
|
|
1
|
+
{"version":3,"file":"compose-html.js","sources":["../../src/core/compose-html.ts"],"sourcesContent":[null],"names":["composeHtml","header","body","footer","abort","onComplete","encoder","TextEncoder","reader","getReader","phase","ReadableStream","cancel","async","reason","releaseLock","pull","controller","enqueue","encode","chunk","read","error","done","value","close","highWaterMark"],"mappings":"AAGA,MAAMA,EAAc,CAClBC,EACAC,EACAC,EACAC,EACAC,KAEA,MAAMC,EAAU,IAAIC,YACdC,EAASN,EAAKO,YACpB,IAAIC,EAAsC,SAE1C,OAAO,IAAIC,eACT,CAIEC,OAAQC,MAAOC,IAGb,GAFAV,IAAQU,GAEM,WAAVJ,EAIJ,UACQF,EAAOI,OAAOE,EACtB,SACEN,EAAOO,cACPV,KACF,GAMF,UAAMW,CAAKC,GACT,GAAc,WAAVP,IACFA,EAAQ,OAEJT,GACFgB,EAAWC,QAAQZ,EAAQa,OAAOlB,QAJtC,CAUA,GAAc,SAAVS,EAAkB,CACpB,IAAIU,EAEJ,IACEA,QAAcZ,EAAOa,MACvB,CAAE,MAAOC,GAMP,OALAlB,IAAQkB,GACRd,EAAOO,cACPV,WACAY,EAAWK,MAAMA,EAGnB,CAEA,IAAKF,EAAMG,KAGT,YAFAN,EAAWC,QAAQE,EAAMI,OAK3Bd,EAAQ,SACRF,EAAOO,cACPV,KACF,CAEIF,GACFc,EAAWC,QAAQZ,EAAQa,OAAOhB,IAGpCc,EAAWQ,OA/BX,CAgCF,GAGF,CAAEC,cAAe"}
|
package/core/handler.d.ts
CHANGED
|
@@ -10,6 +10,10 @@ interface IRequestInit<TAppProps> {
|
|
|
10
10
|
status?: number;
|
|
11
11
|
}
|
|
12
12
|
interface ICreateHandlerOptions<TAppProps> extends ICoreRenderOptions<TAppProps> {
|
|
13
|
+
/**
|
|
14
|
+
* Development checks; defaults to NODE_ENV !== 'production', overridden by SSR_BOOST_DIAGNOSTICS.
|
|
15
|
+
*/
|
|
16
|
+
diagnostics?: boolean;
|
|
13
17
|
/**
|
|
14
18
|
* Supply the document shell, including the application's browser entry script.
|
|
15
19
|
*/
|
|
@@ -25,6 +29,6 @@ interface ICreateHandlerOptions<TAppProps> extends ICoreRenderOptions<TAppProps>
|
|
|
25
29
|
/**
|
|
26
30
|
* Create isolated request state and allow request hooks to bypass rendering.
|
|
27
31
|
*/
|
|
28
|
-
declare const createHandler: <TAppProps = Record<string, any>>(params: ICoreRenderParams<TAppProps>, { getHtml, onRequest, ...options }: ICreateHandlerOptions<TAppProps>) => TSsrHandler;
|
|
32
|
+
declare const createHandler: <TAppProps = Record<string, any>>(params: ICoreRenderParams<TAppProps>, { diagnostics, getHtml, onRequest, ...options }: ICreateHandlerOptions<TAppProps>) => TSsrHandler;
|
|
29
33
|
export type { ICreateHandlerOptions, IHtmlShell, IRequestInit };
|
|
30
34
|
export default createHandler;
|
package/core/handler.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e from"./head-response.js";import s from"./render.js";const
|
|
1
|
+
import e from"./head-response.js";import s from"./render.js";import t,{isDiagnosticsEnabled as r}from"../services/diagnostics.js";const o=(o,{diagnostics:n,getHtml:a,onRequest:i,...p})=>async(c,d)=>{const u=await(i?.({executionContext:d,request:c}));if(u instanceof Response)return e(c,u);const m={appProps:u?.appProps??{},diagnostics:r(n)?new t(new URL(c.url).pathname):void 0,html:await a(c),request:c,response:{headers:new Headers(u?.headers),status:u?.status}};return s(o,m,p,d)};export{o as default};
|
|
2
2
|
//# sourceMappingURL=handler.js.map
|
package/core/handler.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler.js","sources":["../../src/core/handler.ts"],"sourcesContent":[null],"names":["createHandler","params","getHtml","onRequest","options","async","request","executionContext","requestInit","Response","headResponse","context","appProps","html","response","headers","Headers","status","render"],"mappings":"
|
|
1
|
+
{"version":3,"file":"handler.js","sources":["../../src/core/handler.ts"],"sourcesContent":[null],"names":["createHandler","params","diagnostics","getHtml","onRequest","options","async","request","executionContext","requestInit","Response","headResponse","context","appProps","isDiagnosticsEnabled","Diagnostics","URL","url","pathname","undefined","html","response","headers","Headers","status","render"],"mappings":"kIAwCA,MAAMA,EAAgB,CACpBC,GACEC,cAAaC,UAASC,eAAcC,KAK/BC,MAAOC,EAASC,KACrB,MAAMC,QAAoBL,IAAY,CAAEI,mBAAkBD,aAE1D,GAAIE,aAAuBC,SACzB,OAAOC,EAAaJ,EAASE,GAG/B,MAAMG,EAAyC,CAC7CC,SAAWJ,GAAaI,UAAY,GACpCX,YAAaY,EAAqBZ,GAC9B,IAAIa,EAAY,IAAIC,IAAIT,EAAQU,KAAKC,eACrCC,EACJC,WAAYjB,EAAQI,GACpBA,UACAc,SAAU,CACRC,QAAS,IAAIC,QAAQd,GAAaa,SAClCE,OAAQf,GAAae,SAIzB,OAAOC,EAAOxB,EAAQW,EAASP,EAASG"}
|
package/core/render.d.ts
CHANGED
|
@@ -4,8 +4,10 @@ import StreamError from '../constants/stream-error.js';
|
|
|
4
4
|
import type { IServerContext } from '../context/server.js';
|
|
5
5
|
import type { ISsrExecutionContext } from './types.js';
|
|
6
6
|
import type { IObtainStreamErrorOut } from '../helpers/obtain-stream-error.js';
|
|
7
|
+
import type Diagnostics from '../services/diagnostics.js';
|
|
7
8
|
export interface ISsrRequestContext<TAppProps = Record<string, any>> {
|
|
8
9
|
appProps: NonNullable<TAppProps>;
|
|
10
|
+
diagnostics?: Diagnostics;
|
|
9
11
|
/**
|
|
10
12
|
* First render failure, or the explicit timeout/cancellation classification.
|
|
11
13
|
*/
|
package/core/render.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e from"react";import{createStaticRouter as t,StaticRouterProvider as r}from"react-router";import
|
|
1
|
+
import e from"react";import{createStaticRouter as t,StaticRouterProvider as r}from"react-router";import s from"../constants/stream-error.js";import{ServerProvider as o}from"../context/server.js";import a from"./compose-html.js";import n from"./head-response.js";import{mergeResponseHeaders as c}from"./headers.js";import d from"./transform-html.js";import i from"../helpers/build-custom-state.js";import l from"../helpers/build-router-state.js";import u from"../helpers/obtain-stream-error.js";const m="text/html",h="Content-Type",p=async({createApp:p,handler:x,renderToStream:f},R,{abortDelay:C=15e3,getState:w,onError:E,onResponse:g,onRouterReady:y,onShellError:b,onShellReady:S,prepare:q,routerRequestContext:v},j)=>{const T=await x.query(R.request,{requestContext:v??R});if(T instanceof Response)return n(R.request,c(T,R.response.headers));R.routerContext=T,await(q?.({context:R,executionContext:j}));const{isStream:A=!0}=await(y?.({context:R}))??{};R.isStream=A,R.serverContext={basename:R.routerContext.basename,isServer:!0,response:null};const H=t(x.dataRoutes,R.routerContext),D=e.createElement(o,{context:R.serverContext},p(e.createElement(r,{router:H,context:R.routerContext,hydrate:!1}),R)),L=new AbortController;let k,B,I=!1;const z=()=>J(R.request.signal.reason),F=()=>clearTimeout(K),G=()=>{F(),R.request.signal.removeEventListener("abort",z)},J=e=>{I||(I=!0,k=e,G(),R.didError??=s.RenderCancel,L.abort(e),B?.abort(e))},K=setTimeout(()=>{R.didError=s.RenderTimeout,J()},C);R.request.signal.aborted?J(R.request.signal.reason):R.request.signal.addEventListener("abort",z,{once:!0});try{B=await f(D,{onError:e=>{const t=u(e);!I||t.code!==s.RenderAborted&&e!==L.signal.reason||(t.code=R.didError===s.RenderTimeout?s.RenderTimeout:s.RenderCancel);const{code:r}=t;R.didError??=r,E?.({context:R,error:t})},signal:L.signal}),I&&B.abort(k),B.allReady.then(F,F),await(A?B.shellReady:B.allReady)}catch(e){return J(e),await(B?.stream.cancel(e).catch(()=>{})),((e,t,r)=>{const s=t instanceof Error?t:new Error(String(t)),o=r?.({context:e,error:s})||"<!doctype html><p>Internal Server Error</p>",a=new Headers(e.response.headers);return a.set(h,m),new Response("HEAD"===e.request.method?null:o,{headers:a,status:500})})(R,e,b)}try{const e=R.serverContext.response;if(e&&(M=e,Boolean(M&&M.status>=300&&M.status<400&&M.headers.has("Location"))))return J(),await B.stream.cancel().catch(()=>{}),n(R.request,c(e,R.response.headers));const{header:t,footer:r,headers:s,status:o}=((e,{getState:t,onShellReady:r})=>{const s=e.serverContext.response;e.response.status=s?.status??e.response.status??e.routerContext.statusCode??200,e.response.headers.has(h)||e.response.headers.set(h,m);const o=r?.({context:e})??{},a=l(e.routerContext,e.diagnostics),n=i(t?.({context:e}),e.diagnostics),c=o.header||e.html.header,d=o.footer||e.html.footer;return e.diagnostics?.inspectShell({header:c,footer:d}),{header:c,footer:n+a+d,headers:new Headers(e.response.headers),status:e.response.status}})(R,{getState:w,onShellReady:S});if("HEAD"===R.request.method||[204,205,304].includes(o))return J(),await B.stream.cancel().catch(()=>{}),new Response(null,{headers:s,status:o});const u=a(t,B.stream,r,J,G),p=d(u,g?(e,t)=>g({context:R,html:e,isEnd:t}):void 0,R.diagnostics);return B.start(),new Response(p,{headers:s,status:o})}catch(e){throw J(e),B.stream.locked||await B.stream.cancel(e).catch(()=>{}),e}var M};export{p as default};
|
|
2
2
|
//# sourceMappingURL=render.js.map
|
package/core/render.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.js","sources":["../../src/core/render.tsx"],"sourcesContent":[null],"names":["HTML_CONTENT_TYPE","CONTENT_TYPE","render","async","createApp","handler","renderToStream","context","abortDelay","getState","onError","onResponse","onRouterReady","onShellError","onShellReady","prepare","routerRequestContext","executionContext","queried","query","request","requestContext","Response","headResponse","mergeResponseHeaders","response","headers","routerContext","isStream","serverContext","basename","isServer","router","createStaticRouter","dataRoutes","node","React","createElement","ServerProvider","StaticRouterProvider","hydrate","renderController","AbortController","abortReason","output","hasAborted","onRequestAbort","abort","signal","reason","clearAbortTimer","clearTimeout","abortTimer","cleanup","removeEventListener","didError","StreamError","RenderCancel","setTimeout","RenderTimeout","aborted","addEventListener","once","error","streamError","obtainStreamError","code","RenderAborted","allReady","then","shellReady","stream","cancel","catch","shellError","Error","String","html","Headers","set","method","status","createShellErrorResponse","serverResponse","Boolean","has","header","footer","statusCode","shell","routerState","buildRouterState","customState","buildCustomState","prepareHtmlResponse","includes","body","composeHtml","transformed","transformHtml","isEnd","undefined","start","locked"],"mappings":"
|
|
1
|
+
{"version":3,"file":"render.js","sources":["../../src/core/render.tsx"],"sourcesContent":[null],"names":["HTML_CONTENT_TYPE","CONTENT_TYPE","render","async","createApp","handler","renderToStream","context","abortDelay","getState","onError","onResponse","onRouterReady","onShellError","onShellReady","prepare","routerRequestContext","executionContext","queried","query","request","requestContext","Response","headResponse","mergeResponseHeaders","response","headers","routerContext","isStream","serverContext","basename","isServer","router","createStaticRouter","dataRoutes","node","React","createElement","ServerProvider","StaticRouterProvider","hydrate","renderController","AbortController","abortReason","output","hasAborted","onRequestAbort","abort","signal","reason","clearAbortTimer","clearTimeout","abortTimer","cleanup","removeEventListener","didError","StreamError","RenderCancel","setTimeout","RenderTimeout","aborted","addEventListener","once","error","streamError","obtainStreamError","code","RenderAborted","allReady","then","shellReady","stream","cancel","catch","shellError","Error","String","html","Headers","set","method","status","createShellErrorResponse","serverResponse","Boolean","has","header","footer","statusCode","shell","routerState","buildRouterState","diagnostics","customState","buildCustomState","shellFooter","inspectShell","prepareHtmlResponse","includes","body","composeHtml","transformed","transformHtml","isEnd","undefined","start","locked"],"mappings":"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"}
|
package/core/transform-html.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import type Diagnostics from '../services/diagnostics.js';
|
|
1
2
|
type TTransformHtml = (html: string, isEnd: boolean) => string | undefined | void;
|
|
2
3
|
/**
|
|
3
4
|
* Apply response hooks to decoded text without splitting UTF-8 characters.
|
|
4
5
|
*/
|
|
5
|
-
declare const transformHtml: (stream: ReadableStream<Uint8Array>, transform?: TTransformHtml) => ReadableStream<Uint8Array>;
|
|
6
|
+
declare const transformHtml: (stream: ReadableStream<Uint8Array>, transform?: TTransformHtml, diagnostics?: Diagnostics) => ReadableStream<Uint8Array>;
|
|
6
7
|
export type { TTransformHtml };
|
|
7
8
|
export default transformHtml;
|
package/core/transform-html.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=(e,n)=>{if(!n)return e;const r=new TextDecoder,
|
|
1
|
+
const e=(e,n,t)=>{if(!n&&!t)return e;const r=e.getReader(),a=new TextDecoder,c=n?new TextEncoder:void 0;let o=!1;const s=(e,n)=>{n.enqueue(c.encode(e)),t?.append(String(e))},l=(e,r)=>{if(!e)return;const a=n?.(e,!1);t?.inspectOnResponse(a),s(a??e,r)};return new ReadableStream({cancel:async e=>{o=!0;try{await r.cancel(e)}finally{r.releaseLock()}},pull:async e=>{try{for(;!o;){const c=await r.read();if(o)return;if(c.done){const c=a.decode();if(n){l(c,e);const r=n("",!0);t?.inspectOnResponse(r),r&&s(r,e)}else c&&t?.append(c);return t?.complete(),o=!0,r.releaseLock(),void e.close()}const i=a.decode(c.value,{stream:!0});if(n){if(!i)continue;l(i,e)}else t?.append(i),e.enqueue(c.value);return}}catch(n){if(o)return;o=!0,e.error(n);try{await r.cancel(n).catch(()=>{})}finally{r.releaseLock()}}}},{highWaterMark:0})};export{e as default};
|
|
2
2
|
//# sourceMappingURL=transform-html.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transform-html.js","sources":["../../src/core/transform-html.ts"],"sourcesContent":[null],"names":["transformHtml","stream","transform","decoder","TextDecoder","encoder","TextEncoder","
|
|
1
|
+
{"version":3,"file":"transform-html.js","sources":["../../src/core/transform-html.ts"],"sourcesContent":[null],"names":["transformHtml","stream","transform","diagnostics","reader","getReader","decoder","TextDecoder","encoder","TextEncoder","undefined","isStopped","emit","html","controller","enqueue","encode","append","String","apply","result","inspectOnResponse","ReadableStream","cancel","async","reason","releaseLock","pull","chunk","read","done","remaining","decode","complete","close","value","error","catch","highWaterMark"],"mappings":"AAOA,MAAMA,EAAgB,CACpBC,EACAC,EACAC,KAEA,IAAKD,IAAcC,EACjB,OAAOF,EAGT,MAAMG,EAASH,EAAOI,YAChBC,EAAU,IAAIC,YACdC,EAAUN,EAAY,IAAIO,iBAAgBC,EAChD,IAAIC,GAAY,EAKhB,MAAMC,EAAO,CAACC,EAAcC,KAC1BA,EAAWC,QAAQP,EAASQ,OAAOH,IACnCV,GAAac,OAAOC,OAAOL,KAMvBM,EAAQ,CAACN,EAAcC,KAC3B,IAAKD,EACH,OAGF,MAAMO,EAASlB,IAAYW,GAAM,GAEjCV,GAAakB,kBAAkBD,GAC/BR,EAAKQ,GAAUP,EAAMC,IAGvB,OAAO,IAAIQ,eACT,CAIEC,OAAQC,MAAOC,IACbd,GAAY,EAEZ,UACQP,EAAOmB,OAAOE,EACtB,SACErB,EAAOsB,aACT,GAMFC,KAAMH,MAAOV,IACX,IACE,MAAQH,GAAW,CACjB,MAAMiB,QAAcxB,EAAOyB,OAE3B,GAAIlB,EACF,OAGF,GAAIiB,EAAME,KAAM,CACd,MAAMC,EAAYzB,EAAQ0B,SAE1B,GAAI9B,EAAW,CACbiB,EAAMY,EAAWjB,GAEjB,MAAMD,EAAOX,EAAU,IAAI,GAE3BC,GAAakB,kBAAkBR,GAE3BA,GACFD,EAAKC,EAAMC,EAEf,MAAWiB,GACT5B,GAAac,OAAOc,GAQtB,OALA5B,GAAa8B,WACbtB,GAAY,EACZP,EAAOsB,mBACPZ,EAAWoB,OAGb,CAEA,MAAMrB,EAAOP,EAAQ0B,OAAOJ,EAAMO,MAAO,CAAElC,QAAQ,IAEnD,GAAIC,EAAW,CACb,IAAKW,EACH,SAGFM,EAAMN,EAAMC,EACd,MACEX,GAAac,OAAOJ,GACpBC,EAAWC,QAAQa,EAAMO,OAG3B,MACF,CACF,CAAE,MAAOC,GACP,GAAIzB,EACF,OAGFA,GAAY,EACZG,EAAWsB,MAAMA,GAEjB,UACQhC,EAAOmB,OAAOa,GAAOC,MAAM,OACnC,SACEjC,EAAOsB,aACT,CACF,IAIJ,CAAEY,cAAe"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type Diagnostics from '../services/diagnostics.js';
|
|
1
2
|
/**
|
|
2
3
|
* Build custom state
|
|
3
4
|
*/
|
|
4
|
-
declare function buildCustomState(initState?: Record<string, Record<string, any>> | void): string;
|
|
5
|
+
declare function buildCustomState(initState?: Record<string, Record<string, any>> | void, diagnostics?: Diagnostics): string;
|
|
5
6
|
export default buildCustomState;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import t from"./html-escape.js";function
|
|
1
|
+
import t from"./html-escape.js";function e(e,r){r?.inspectState(e);return Object.entries(e??{}).map(([e,r])=>{if(!e||!r||!Object.keys(r||{}).length)return"";const i=t(JSON.stringify(JSON.stringify(r)));return`<script async>window${/^[A-Za-z_$][\w$]*$/.test(e)?`.${e}`:`[${t(JSON.stringify(e))}]`} = JSON.parse(${i});<\/script>`}).join("").trim()}export{e as default};
|
|
2
2
|
//# sourceMappingURL=build-custom-state.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-custom-state.js","sources":["../../src/helpers/build-custom-state.ts"],"sourcesContent":[null],"names":["buildCustomState","initState","Object","entries","map","key","state","keys","length","json","htmlEscape","JSON","stringify","test","join","trim"],"mappings":"
|
|
1
|
+
{"version":3,"file":"build-custom-state.js","sources":["../../src/helpers/build-custom-state.ts"],"sourcesContent":[null],"names":["buildCustomState","initState","diagnostics","inspectState","Object","entries","map","key","state","keys","length","json","htmlEscape","JSON","stringify","test","join","trim"],"mappings":"gCAMA,SAASA,EACPC,EACAC,GAEAA,GAAaC,aAAaF,GAe1B,OAbqBG,OAAOC,QAAQJ,GAAa,CAAA,GAAIK,IAAI,EAAEC,EAAKC,MAC9D,IAAKD,IAAQC,IAAUJ,OAAOK,KAAKD,GAAS,IAAIE,OAC9C,MAAO,GAGT,MAAMC,EAAOC,EAAWC,KAAKC,UAAUD,KAAKC,UAAUN,KAKtD,MAAO,uBAJU,qBAAqBO,KAAKR,GACvC,IAAIA,IACJ,IAAIK,EAAWC,KAAKC,UAAUP,uBAEqBI,kBAGrCK,KAAK,IAAIC,MAC/B"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { StaticHandlerContext } from 'react-router';
|
|
2
|
+
import type Diagnostics from '../services/diagnostics.js';
|
|
2
3
|
/**
|
|
3
4
|
* Build router state
|
|
4
5
|
*/
|
|
5
|
-
declare function buildRouterState(context: StaticHandlerContext): string;
|
|
6
|
+
declare function buildRouterState(context: StaticHandlerContext, diagnostics?: Diagnostics): string;
|
|
6
7
|
export default buildRouterState;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import r from"./html-escape.js";import t from"./serialize-errors.js";function a(a){const{loaderData:
|
|
1
|
+
import r from"./html-escape.js";import t from"./serialize-errors.js";function a(a,o){o?.inspectRouterState(a);const{loaderData:e,actionData:i,errors:s}=a,n={loaderData:e,actionData:i,errors:t(s)};return`<script async>window.__staticRouterHydrationData = JSON.parse(${r(JSON.stringify(JSON.stringify(n)))});<\/script>`}export{a as default};
|
|
2
2
|
//# sourceMappingURL=build-router-state.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-router-state.js","sources":["../../src/helpers/build-router-state.ts"],"sourcesContent":[null],"names":["buildRouterState","context","loaderData","actionData","errors","routerState","serializeErrors","htmlEscape","JSON","stringify"],"mappings":"
|
|
1
|
+
{"version":3,"file":"build-router-state.js","sources":["../../src/helpers/build-router-state.ts"],"sourcesContent":[null],"names":["buildRouterState","context","diagnostics","inspectRouterState","loaderData","actionData","errors","routerState","serializeErrors","htmlEscape","JSON","stringify"],"mappings":"qEAQA,SAASA,EAAiBC,EAA+BC,GACvDA,GAAaC,mBAAmBF,GAEhC,MAAMG,WAAEA,EAAUC,WAAEA,EAAUC,OAAEA,GAAWL,EACrCM,EAAc,CAClBH,aACAC,aACAC,OAAQE,EAAgBF,IAI1B,MAAO,iEAFMG,EAAWC,KAAKC,UAAUD,KAAKC,UAAUJ,kBAGxD"}
|
package/node/production.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{readFile as t}from"node:fs/promises";import e from"../services/route-assets.js";const o=async({indexFile:
|
|
1
|
+
import{readFile as t}from"node:fs/promises";import{splitHtmlShell as e}from"../services/diagnostics.js";import s from"../services/route-assets.js";const o=async({indexFile:s,outlet:o="\x3c!--ssr-outlet--\x3e"})=>{const r=await t(s,"utf8"),[n,i]=e(r,s,o);return()=>({header:n,footer:i})},r=({buildDir:t,modulePreload:e=!1})=>{const o=new s(t,e);return async({context:t,executionContext:e})=>{const s=o.injectAssets(t);s.has("Link")&&await(e?.onEarlyHints?.(s))}};export{r as createRouteAssetPreparer,o as loadHtmlShell};
|
|
2
2
|
//# sourceMappingURL=production.js.map
|
package/node/production.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"production.js","sources":["../../src/node/production.ts"],"sourcesContent":[null],"names":["loadHtmlShell","async","indexFile","outlet","
|
|
1
|
+
{"version":3,"file":"production.js","sources":["../../src/node/production.ts"],"sourcesContent":[null],"names":["loadHtmlShell","async","indexFile","outlet","html","readFile","header","footer","splitHtmlShell","createRouteAssetPreparer","buildDir","modulePreload","assets","RouteAssets","context","executionContext","hints","injectAssets","has","onEarlyHints"],"mappings":"mJAkBA,MAAMA,EAAgBC,OACpBC,YACAC,SAAS,8BAET,MAAMC,QAAaC,EAASH,EAAW,SAChCI,EAAQC,GAAUC,EAAeJ,EAAMF,EAAWC,GAEzD,MAAO,KAAA,CAASG,SAAQC,YAMpBE,EAA2B,EAC/BC,WACAC,iBAAgB,MAEhB,MAAMC,EAAS,IAAIC,EAAYH,EAAUC,GAEzC,OAAOV,OAASa,UAASC,uBACvB,MAAMC,EAAQJ,EAAOK,aAAaH,GAE9BE,EAAME,IAAI,eACNH,GAAkBI,eAAeH"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lomray/vite-ssr-boost",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.2.0-beta.1",
|
|
4
4
|
"description": "Vite plugin for create awesome SSR or SPA applications on React.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
"lint:format": "eslint . --fix",
|
|
59
59
|
"ts:check": "tsc --project ./tsconfig.json --skipLibCheck --noemit",
|
|
60
60
|
"test": "vitest run",
|
|
61
|
+
"test:size": "node scripts/test-browser-size.mjs",
|
|
61
62
|
"test:core:no-optional": "node scripts/test-core-no-optional.mjs",
|
|
62
63
|
"test:edge:packed": "node scripts/test-packed-edge.mjs",
|
|
63
64
|
"test:bun": "bun scripts/test-bun.mjs",
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { StaticHandlerContext } from 'react-router';
|
|
2
|
+
import Logger from './logger.js';
|
|
3
|
+
type TDiagnosticCode = 'SSR_BOOST_LOADER_NOT_SERIALIZABLE' | 'SSR_BOOST_STATE_NOT_SERIALIZABLE' | 'SSR_BOOST_OUTLET_MISSING' | 'SSR_BOOST_HYDRATION_STATE_MISSING' | 'SSR_BOOST_DUPLICATE_OUTPUT' | 'SSR_BOOST_ONRESPONSE_INVALID_RETURN';
|
|
4
|
+
interface ISerializationIssue {
|
|
5
|
+
path: string;
|
|
6
|
+
reason: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Resolve overrides without requiring Node globals in Fetch runtimes.
|
|
10
|
+
*/
|
|
11
|
+
declare const isDiagnosticsEnabled: (enabled?: boolean) => boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Keep warnings and fatal shell errors linked to the same stable reference.
|
|
14
|
+
*/
|
|
15
|
+
declare const formatDiagnostic: (code: TDiagnosticCode, sentence: string) => string;
|
|
16
|
+
/**
|
|
17
|
+
* Reject unusable file-backed shells in every runtime mode.
|
|
18
|
+
*/
|
|
19
|
+
declare const splitHtmlShell: (html: string, file: string, outlet?: string) => [string, string];
|
|
20
|
+
/**
|
|
21
|
+
* Find values JSON cannot preserve, descending only into plain objects and arrays.
|
|
22
|
+
*/
|
|
23
|
+
declare const walkSerializable: (value: unknown, report: (issue: ISerializationIssue) => void, path?: string, ancestors?: Set<object>) => void;
|
|
24
|
+
/**
|
|
25
|
+
* Inspect one development response while allowing every chunk to stream immediately.
|
|
26
|
+
*/
|
|
27
|
+
declare class Diagnostics {
|
|
28
|
+
protected readonly route: string;
|
|
29
|
+
protected readonly logger: Pick<Logger, 'warn'>;
|
|
30
|
+
protected chunks: string[];
|
|
31
|
+
constructor(route: string, logger?: Pick<Logger, 'warn'>);
|
|
32
|
+
/**
|
|
33
|
+
* Emit each concrete message once per process, including across handler instances.
|
|
34
|
+
*/
|
|
35
|
+
protected warn(code: TDiagnosticCode, detail: string): void;
|
|
36
|
+
/**
|
|
37
|
+
* A split Fetch shell has one insertion boundary and must not retain raw outlets.
|
|
38
|
+
*/
|
|
39
|
+
inspectShell({ header, footer }: {
|
|
40
|
+
header: string;
|
|
41
|
+
footer: string;
|
|
42
|
+
}): void;
|
|
43
|
+
/**
|
|
44
|
+
* Include React Router's route id and the full loader/action key path.
|
|
45
|
+
*/
|
|
46
|
+
inspectRouterState({ loaderData, actionData }: StaticHandlerContext): void;
|
|
47
|
+
/**
|
|
48
|
+
* Check custom state before the serializer can omit or coerce it.
|
|
49
|
+
*/
|
|
50
|
+
inspectState(state: unknown): void;
|
|
51
|
+
/**
|
|
52
|
+
* Hooks are synchronous and can only keep, replace or withhold a chunk.
|
|
53
|
+
*/
|
|
54
|
+
inspectOnResponse(value: unknown): void;
|
|
55
|
+
/**
|
|
56
|
+
* Retain only the output that actually leaves the response transform.
|
|
57
|
+
*/
|
|
58
|
+
append(html: string): void;
|
|
59
|
+
/**
|
|
60
|
+
* Scan completed HTML so markers split across chunks are still recognized.
|
|
61
|
+
*/
|
|
62
|
+
complete(): void;
|
|
63
|
+
}
|
|
64
|
+
export { formatDiagnostic, isDiagnosticsEnabled, splitHtmlShell, walkSerializable };
|
|
65
|
+
export type { ISerializationIssue, TDiagnosticCode };
|
|
66
|
+
export default Diagnostics;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import t from"./logger.js";const e="\x3c!--ssr-outlet--\x3e",o="window.__staticRouterHydrationData",s=Symbol.for("@lomray/vite-ssr-boost/diagnostics"),r=globalThis,n=t=>{const e="undefined"==typeof process?void 0:process.env;return"0"===e?.SSR_BOOST_DIAGNOSTICS||"1"===e?.SSR_BOOST_DIAGNOSTICS?"1"===e.SSR_BOOST_DIAGNOSTICS:t??"production"!==e?.NODE_ENV},i=(t,e)=>`[ssr-boost] ${t}: ${e} See https://lomray-software.github.io/vite-ssr-boost/reference/diagnostics#${t.toLowerCase()}`,a=(t,o,s=e)=>{const r=t.split(s);if(!s||2!==r.length)throw new Error(i("SSR_BOOST_OUTLET_MISSING",`Invalid HTML shell in ${JSON.stringify(o)}: expected exactly one non-empty outlet ${JSON.stringify(s)}.`));return r},S=t=>{if(null===t)return"null";if("object"!=typeof t){return{bigint:"BigInt",symbol:"Symbol"}[typeof t]??typeof t}const e=Object.getPrototypeOf(t),o=e?.constructor?.name;return"string"==typeof o&&o?o:"object"},c=(t,e,o="$",s=new Set)=>{const r=typeof t;if("function"===r||"bigint"===r||"symbol"===r)return void e({path:o,reason:`${S(t)} is not JSON-serializable`});if(null===t||"object"!==r)return;const n=t,i=Object.getPrototypeOf(n);if(i!==Object.prototype&&null!==i&&(!Array.isArray(n)||i!==Array.prototype)){const t=S(n);return void e({path:o,reason:"Date"===t?"Date hydrates as a string":`${t} is not JSON-serializable`})}if(s.has(n))e({path:o,reason:"a circular reference is not JSON-serializable"});else{s.add(n);for(const[t,r]of Object.entries(n)){const i=Array.isArray(n)&&/^(0|[1-9]\d*)$/.test(t)?`[${t}]`:/^[A-Za-z_$][\w$]*$/.test(t)?`.${t}`:`[${JSON.stringify(t)}]`;c(r,e,o+i,s)}s.delete(n)}};class l{route;logger;chunks=[];constructor(e,o=new t){this.route=e,this.logger=o}warn(t,e){const o=i(t,e),n=r[s]??=new Set;n.has(o)||(n.add(o),this.logger.warn(o,{}))}inspectShell({header:t,footer:o}){("string"!=typeof t||"string"!=typeof o||t.includes(e)||o.includes(e))&&this.warn("SSR_BOOST_OUTLET_MISSING",`HTML shell for route ${JSON.stringify(this.route)} must supply both header and footer around exactly one ${e} outlet, with no outlet left in either half.`)}inspectRouterState({loaderData:t,actionData:e}){for(const[o,s]of Object.entries({loaderData:t,actionData:e}))for(const[t,e]of Object.entries(s??{}))c(e,({path:e,reason:o})=>this.warn("SSR_BOOST_LOADER_NOT_SERIALIZABLE",`Route ${JSON.stringify(t)} at ${e}: ${o}.`),`${o}[${JSON.stringify(t)}]`)}inspectState(t){c(t,({path:t,reason:e})=>this.warn("SSR_BOOST_STATE_NOT_SERIALIZABLE",`getState for route ${JSON.stringify(this.route)} at ${t}: ${e}.`))}inspectOnResponse(t){void 0!==t&&"string"!=typeof t&&this.warn("SSR_BOOST_ONRESPONSE_INVALID_RETURN",`onResponse for route ${JSON.stringify(this.route)} returned ${S(t)}; return a string or undefined.`)}append(t){this.chunks.push(t)}complete(){const t=this.chunks.join("");this.chunks=[];const e=new Map,s=t=>{e.set(t,(e.get(t)??0)+1)},r=t.replace(/<!--[\s\S]*?-->/g,"").replace(/(<(script|style|textarea|title)\b(?:[^"'<>]|"[^"]*"|'[^']*')*>)([\s\S]*?)<\/\2\s*>/gi,(t,e,r,n)=>{if("script"===r.toLowerCase()){/\bwindow\.__staticRouterHydrationData\s*=/.test(n)&&s(o);for(const t of n.matchAll(/\$RC\s*\(\s*(["'])(B:[^"']+)\1/g))s(`$RC("${t[2]}")`)}return e});for(const t of r.matchAll(/<([a-z][\w:-]*)\b((?:[^"'<>]|"[^"]*"|'[^']*')*)>/gi)){const e=t[1].toLowerCase();"html"!==e&&"head"!==e||s(`<${e}>`);for(const e of t[2].matchAll(/([^\s=]+)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/g)){const t=e[2]??e[3]??e[4];"id"===e[1].toLowerCase()&&/^(S|B):/.test(t)&&s(`id="${t}"`)}}e.has(o)||this.warn("SSR_BOOST_HYDRATION_STATE_MISSING",`Completed response for route ${JSON.stringify(this.route)} has no ${o} script; preserve the hydration footer in onResponse.`);for(const[t,o]of e)o>1&&this.warn("SSR_BOOST_DUPLICATE_OUTPUT",`Completed response for route ${JSON.stringify(this.route)} repeats ${t}; onResponse must return undefined to keep a chunk and '' to withhold one.`)}}export{l as default,i as formatDiagnostic,n as isDiagnosticsEnabled,a as splitHtmlShell,c as walkSerializable};
|
|
2
|
+
//# sourceMappingURL=diagnostics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.js","sources":["../../src/services/diagnostics.ts"],"sourcesContent":[null],"names":["OUTLET","HYDRATION","warnedKey","Symbol","for","registry","globalThis","isDiagnosticsEnabled","enabled","env","process","undefined","SSR_BOOST_DIAGNOSTICS","NODE_ENV","formatDiagnostic","code","sentence","toLowerCase","splitHtmlShell","html","file","outlet","parts","split","length","Error","JSON","stringify","valueType","value","bigint","symbol","prototype","Object","getPrototypeOf","name","constructor","walkSerializable","report","path","ancestors","Set","type","reason","object","Array","isArray","has","add","key","child","entries","suffix","test","delete","Diagnostics","route","logger","chunks","Logger","this","warn","detail","message","warned","inspectShell","header","footer","includes","inspectRouterState","loaderData","actionData","kind","data","inspectState","state","inspectOnResponse","append","push","complete","join","markers","Map","count","marker","set","get","markup","replace","_tag","opening","content","match","matchAll","attribute","total"],"mappings":"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"}
|