@lomray/vite-ssr-boost 7.1.0 → 8.0.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 +17 -1
- package/adapters/body.d.ts +5 -0
- package/adapters/body.js +2 -0
- package/adapters/body.js.map +1 -0
- package/adapters/compression.d.ts +11 -0
- package/adapters/compression.js +2 -0
- package/adapters/compression.js.map +1 -0
- package/adapters/edge.d.ts +11 -0
- package/adapters/edge.js +2 -0
- package/adapters/edge.js.map +1 -0
- package/adapters/express/create-request.d.ts +11 -0
- package/adapters/express/create-request.js +2 -0
- package/adapters/express/create-request.js.map +1 -0
- package/{node → adapters/express}/entry.d.ts +8 -14
- package/adapters/express/entry.js.map +1 -0
- package/{services → adapters/express}/prepare-server.d.ts +14 -21
- package/adapters/express/prepare-server.js +2 -0
- package/adapters/express/prepare-server.js.map +1 -0
- package/{node → adapters/express}/render.d.ts +8 -8
- package/adapters/express/render.js +2 -0
- package/adapters/express/render.js.map +1 -0
- package/{node → adapters/express}/server.d.ts +1 -1
- package/adapters/express/server.js +2 -0
- package/adapters/express/server.js.map +1 -0
- package/adapters/express.d.ts +11 -0
- package/adapters/express.js +2 -0
- package/adapters/express.js.map +1 -0
- package/adapters/fastify.d.ts +23 -0
- package/adapters/fastify.js +2 -0
- package/adapters/fastify.js.map +1 -0
- package/adapters/hono.d.ts +13 -0
- package/adapters/hono.js +2 -0
- package/adapters/hono.js.map +1 -0
- package/adapters/node.d.ts +18 -0
- package/adapters/node.js +2 -0
- package/adapters/node.js.map +1 -0
- package/cli/run-dev.js +1 -1
- package/cli/run-dev.js.map +1 -1
- package/cli/run-prod.d.ts +1 -1
- package/cli/run-prod.js +1 -1
- package/cli/run-prod.js.map +1 -1
- package/cli/run-serverless.d.ts +2 -2
- package/cli/run-serverless.js +1 -1
- package/cli/run-serverless.js.map +1 -1
- package/components/response-status.js +1 -1
- package/components/response-status.js.map +1 -1
- package/core/compose-html.d.ts +5 -0
- package/core/compose-html.js +2 -0
- package/core/compose-html.js.map +1 -0
- package/core/early-hints.d.ts +6 -0
- package/core/early-hints.js +2 -0
- package/core/early-hints.js.map +1 -0
- package/core/handler.d.ts +30 -0
- package/core/handler.js +2 -0
- package/core/handler.js.map +1 -0
- package/core/head-response.d.ts +5 -0
- package/core/head-response.js +2 -0
- package/core/head-response.js.map +1 -0
- package/core/headers.d.ts +13 -0
- package/core/headers.js +2 -0
- package/core/headers.js.map +1 -0
- package/core/render.d.ts +101 -0
- package/core/render.js +2 -0
- package/core/render.js.map +1 -0
- package/core/transform-html.d.ts +7 -0
- package/core/transform-html.js +2 -0
- package/core/transform-html.js.map +1 -0
- package/core/types.d.ts +8 -0
- package/core/types.js +2 -0
- package/core/types.js.map +1 -0
- package/edge/render-to-stream.d.ts +6 -0
- package/edge/render-to-stream.js +2 -0
- package/edge/render-to-stream.js.map +1 -0
- package/helpers/build-custom-state.d.ts +1 -2
- package/helpers/build-custom-state.js +1 -1
- package/helpers/build-custom-state.js.map +1 -1
- package/helpers/obtain-stream-error.js +1 -1
- package/helpers/obtain-stream-error.js.map +1 -1
- package/interfaces/fc-route.d.ts +1 -1
- package/interfaces/fc-route.js.map +1 -1
- package/node/compress-response.d.ts +6 -0
- package/node/compress-response.js +2 -0
- package/node/compress-response.js.map +1 -0
- package/node/create-headers.d.ts +6 -0
- package/node/create-headers.js +2 -0
- package/node/create-headers.js.map +1 -0
- package/node/create-request.d.ts +13 -0
- package/node/create-request.js +2 -0
- package/node/create-request.js.map +1 -0
- package/node/http.d.ts +6 -0
- package/node/http.js +2 -0
- package/node/http.js.map +1 -0
- package/node/render-to-stream.d.ts +6 -0
- package/node/render-to-stream.js +2 -0
- package/node/render-to-stream.js.map +1 -0
- package/node/request-signal.d.ts +10 -0
- package/node/request-signal.js +2 -0
- package/node/request-signal.js.map +1 -0
- package/node/split-link-header.d.ts +5 -0
- package/node/split-link-header.js +2 -0
- package/node/split-link-header.js.map +1 -0
- package/node/write-early-hints.d.ts +6 -0
- package/node/write-early-hints.js +2 -0
- package/node/write-early-hints.js.map +1 -0
- package/node/write-fetch-response.d.ts +10 -0
- package/node/write-fetch-response.js +2 -0
- package/node/write-fetch-response.js.map +1 -0
- package/package.json +45 -3
- package/services/server-config.d.ts +3 -4
- package/services/server-config.js.map +1 -1
- package/services/ssr-manifest.d.ts +25 -8
- package/services/ssr-manifest.js +1 -1
- package/services/ssr-manifest.js.map +1 -1
- package/helpers/handle-response.d.ts +0 -7
- package/helpers/handle-response.js +0 -2
- package/helpers/handle-response.js.map +0 -1
- package/node/create-fetch-request.d.ts +0 -7
- package/node/create-fetch-request.js +0 -2
- package/node/create-fetch-request.js.map +0 -1
- package/node/entry.js.map +0 -1
- package/node/render.js +0 -2
- package/node/render.js.map +0 -1
- package/node/server.js +0 -2
- package/node/server.js.map +0 -1
- package/node/write-response.d.ts +0 -14
- package/node/write-response.js +0 -2
- package/node/write-response.js.map +0 -1
- package/services/prepare-server.js +0 -2
- package/services/prepare-server.js.map +0 -1
- /package/{node → adapters/express}/entry.js +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-to-stream.js","sources":["../../src/node/render-to-stream.ts"],"sourcesContent":["import { PassThrough, Readable } from 'node:stream';\nimport { renderToPipeableStream } from 'react-dom/server';\nimport type { TRenderToStream } from '@core/render';\n\n/**\n * Adapt React pipeable rendering to Fetch streams with byte-based backpressure.\n */\nconst renderToStream: TRenderToStream = (node, options) => {\n const destination = new PassThrough();\n const stream = Readable.toWeb(destination, {\n strategy: {\n highWaterMark: destination.readableHighWaterMark,\n\n /**\n * Measure queued data in bytes, matching the Node stream high-water mark.\n */\n size: (chunk: Uint8Array) => chunk.byteLength,\n },\n }) as ReadableStream<Uint8Array>;\n let rejectAll!: (error: Error) => void;\n let rejectShell!: (error: Error) => void;\n let resolveAll!: () => void;\n let resolveShell!: () => void;\n\n /**\n * Track completion independently from the first available shell.\n */\n const allReady = new Promise<void>((resolve, reject) => {\n rejectAll = reject;\n resolveAll = resolve;\n });\n\n /**\n * Let the core wait for the shell without waiting for suspended content.\n */\n const shellReady = new Promise<void>((resolve, reject) => {\n rejectShell = reject;\n resolveShell = resolve;\n });\n\n void allReady.catch(() => undefined);\n void shellReady.catch(() => undefined);\n\n let hasShellSettled = false;\n let hasAborted = false;\n const rendered = renderToPipeableStream(node, {\n onAllReady: resolveAll,\n onError: options.onError,\n\n /**\n * Reject both readiness promises when React cannot create a shell.\n */\n onShellError: (error) => {\n const shellError = error instanceof Error ? error : new Error(String(error));\n\n hasShellSettled = true;\n rejectAll(shellError);\n rejectShell(shellError);\n },\n\n /**\n * Allow the core to commit headers once the shell is ready.\n */\n onShellReady: () => {\n hasShellSettled = true;\n resolveShell();\n },\n });\n\n /**\n * Settle pending React 18 readiness promises before aborting the renderer.\n */\n const abort = (reason?: unknown): void => {\n if (hasAborted) {\n return;\n }\n\n hasAborted = true;\n\n /**\n * React 18 may omit shell callbacks when the root task is aborted.\n */\n if (!hasShellSettled) {\n hasShellSettled = true;\n const message = typeof reason === 'string' ? reason : 'Render aborted';\n const error = reason instanceof Error ? reason : new Error(message);\n\n rejectShell(error);\n rejectAll(error);\n }\n\n rendered.abort(reason);\n };\n\n /**\n * Forward the transport cancellation reason to React.\n */\n const onAbort = (): void => abort(options.signal.reason);\n\n /**\n * Detach the transport signal after React settles.\n */\n const cleanup = (): void => options.signal.removeEventListener('abort', onAbort);\n\n if (options.signal.aborted) {\n onAbort();\n } else {\n options.signal.addEventListener('abort', onAbort, { once: true });\n }\n\n void allReady.then(cleanup, cleanup);\n let hasStarted = false;\n\n return {\n allReady,\n abort,\n shellReady,\n\n /**\n * Pipe only after the core has finalized response headers and hooks.\n */\n start: () => {\n if (!hasStarted) {\n hasStarted = true;\n rendered.pipe(destination);\n }\n },\n stream,\n };\n};\n\nexport default renderToStream;\n"],"names":["renderToStream","node","options","destination","PassThrough","stream","Readable","toWeb","strategy","highWaterMark","readableHighWaterMark","size","chunk","byteLength","rejectAll","rejectShell","resolveAll","resolveShell","allReady","Promise","resolve","reject","shellReady","catch","hasShellSettled","hasAborted","rendered","renderToPipeableStream","onAllReady","onError","onShellError","error","shellError","Error","String","onShellReady","abort","reason","onAbort","signal","cleanup","removeEventListener","aborted","addEventListener","once","then","hasStarted","start","pipe"],"mappings":"kHAOA,MAAMA,EAAkC,CAACC,EAAMC,KAC7C,MAAMC,EAAc,IAAIC,EAClBC,EAASC,EAASC,MAAMJ,EAAa,CACzCK,SAAU,CACRC,cAAeN,EAAYO,sBAK3BC,KAAOC,GAAsBA,EAAMC,cAGvC,IAAIC,EACAC,EACAC,EACAC,EAKJ,MAAMC,EAAW,IAAIC,SAAc,CAACC,EAASC,KAC3CP,EAAYO,EACZL,EAAaI,CAAO,IAMhBE,EAAa,IAAIH,SAAc,CAACC,EAASC,KAC7CN,EAAcM,EACdJ,EAAeG,CAAO,IAGnBF,EAASK,OAAM,KAAe,IAC9BD,EAAWC,OAAM,KAAe,IAErC,IAAIC,GAAkB,EAClBC,GAAa,EACjB,MAAMC,EAAWC,EAAuB1B,EAAM,CAC5C2B,WAAYZ,EACZa,QAAS3B,EAAQ2B,QAKjBC,aAAeC,IACb,MAAMC,EAAaD,aAAiBE,MAAQF,EAAQ,IAAIE,MAAMC,OAAOH,IAErEP,GAAkB,EAClBV,EAAUkB,GACVjB,EAAYiB,EAAW,EAMzBG,aAAc,KACZX,GAAkB,EAClBP,GAAc,IAOZmB,EAASC,IACb,IAAIZ,EAAJ,CASA,GALAA,GAAa,GAKRD,EAAiB,CACpBA,GAAkB,EAClB,MACMO,EAAQM,aAAkBJ,MAAQI,EAAS,IAAIJ,MADnB,iBAAXI,EAAsBA,EAAS,kBAGtDtB,EAAYgB,GACZjB,EAAUiB,EACZ,CAEAL,EAASU,MAAMC,EAhBf,CAgBsB,EAMlBC,EAAU,IAAYF,EAAMlC,EAAQqC,OAAOF,QAK3CG,EAAU,IAAYtC,EAAQqC,OAAOE,oBAAoB,QAASH,GAEpEpC,EAAQqC,OAAOG,QACjBJ,IAEApC,EAAQqC,OAAOI,iBAAiB,QAASL,EAAS,CAAEM,MAAM,IAGvD1B,EAAS2B,KAAKL,EAASA,GAC5B,IAAIM,GAAa,EAEjB,MAAO,CACL5B,WACAkB,QACAd,aAKAyB,MAAO,KACAD,IACHA,GAAa,EACbpB,EAASsB,KAAK7C,GAChB,EAEFE,SACD"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { TIncomingMessage, TServerResponse } from "./http.js";
|
|
2
|
+
interface IRequestSignal {
|
|
3
|
+
dispose: () => void;
|
|
4
|
+
signal: AbortSignal;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Expose incomplete Node requests and disconnected responses as an AbortSignal.
|
|
8
|
+
*/
|
|
9
|
+
declare const createRequestSignal: (req: TIncomingMessage, res: TServerResponse) => IRequestSignal;
|
|
10
|
+
export { createRequestSignal as default };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const o=(o,e)=>{const t=new AbortController,r=()=>t.abort(),n=()=>{e.writableEnded||r()};return o.once("aborted",r),e.once("close",n),(o.aborted||e.destroyed)&&r(),{dispose:()=>{o.off("aborted",r),e.off("close",n)},signal:t.signal}};export{o as default};
|
|
2
|
+
//# sourceMappingURL=request-signal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-signal.js","sources":["../../src/node/request-signal.ts"],"sourcesContent":["import type { TIncomingMessage, TServerResponse } from '@node/http';\n\ninterface IRequestSignal {\n dispose: () => void;\n signal: AbortSignal;\n}\n\n/**\n * Expose incomplete Node requests and disconnected responses as an AbortSignal.\n */\nconst createRequestSignal = (req: TIncomingMessage, res: TServerResponse): IRequestSignal => {\n const controller = new AbortController();\n\n /**\n * Cancel downstream work when the incoming request is aborted.\n */\n const abort = (): void => controller.abort();\n\n /**\n * Distinguish an interrupted response from normal completion.\n */\n const onClose = (): void => {\n if (!res.writableEnded) {\n abort();\n }\n };\n\n /**\n * Remove transport listeners after the handler finishes.\n */\n const dispose = (): void => {\n req.off('aborted', abort);\n res.off('close', onClose);\n };\n\n req.once('aborted', abort);\n res.once('close', onClose);\n\n if (req.aborted || res.destroyed) {\n abort();\n }\n\n return { dispose, signal: controller.signal };\n};\n\nexport default createRequestSignal;\n"],"names":["createRequestSignal","req","res","controller","AbortController","abort","onClose","writableEnded","once","aborted","destroyed","dispose","off","signal"],"mappings":"AAUA,MAAMA,EAAsB,CAACC,EAAuBC,KAClD,MAAMC,EAAa,IAAIC,gBAKjBC,EAAQ,IAAYF,EAAWE,QAK/BC,EAAU,KACTJ,EAAIK,eACPF,GACF,EAkBF,OAPAJ,EAAIO,KAAK,UAAWH,GACpBH,EAAIM,KAAK,QAASF,IAEdL,EAAIQ,SAAWP,EAAIQ,YACrBL,IAGK,CAAEM,QAZO,KACdV,EAAIW,IAAI,UAAWP,GACnBH,EAAIU,IAAI,QAASN,EAAQ,EAUTO,OAAQV,EAAWU,OAAQ"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const t=t=>{const e=[];let l=0,s=!1,o=!1,r=0;for(let n=0;n<t.length;n+=1){const a=t[n];s?s=!1:"\\"===a&&o?s=!0:'"'===a?o=!o:o||"<"!==a?o||">"!==a?o||0!==l||","!==a||(e.push(t.slice(r,n).trim()),r=n+1):l=Math.max(0,l-1):l+=1}return e.push(t.slice(r).trim()),e.filter(Boolean)};export{t as default};
|
|
2
|
+
//# sourceMappingURL=split-link-header.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"split-link-header.js","sources":["../../src/node/split-link-header.ts"],"sourcesContent":["/**\n * Split Link entries while preserving commas inside URLs and quoted parameters.\n */\nconst splitLinkHeader = (header: string): string[] => {\n const links: string[] = [];\n let angleDepth = 0;\n let isEscaped = false;\n let isQuoted = false;\n let start = 0;\n\n for (let index = 0; index < header.length; index += 1) {\n const char = header[index];\n\n if (isEscaped) {\n isEscaped = false;\n } else if (char === '\\\\' && isQuoted) {\n isEscaped = true;\n } else if (char === '\"') {\n isQuoted = !isQuoted;\n } else if (!isQuoted && char === '<') {\n angleDepth += 1;\n } else if (!isQuoted && char === '>') {\n angleDepth = Math.max(0, angleDepth - 1);\n } else if (!isQuoted && angleDepth === 0 && char === ',') {\n links.push(header.slice(start, index).trim());\n start = index + 1;\n }\n }\n\n links.push(header.slice(start).trim());\n\n return links.filter(Boolean);\n};\n\nexport default splitLinkHeader;\n"],"names":["splitLinkHeader","header","links","angleDepth","isEscaped","isQuoted","start","index","length","char","push","slice","trim","Math","max","filter","Boolean"],"mappings":"AAGA,MAAMA,EAAmBC,IACvB,MAAMC,EAAkB,GACxB,IAAIC,EAAa,EACbC,GAAY,EACZC,GAAW,EACXC,EAAQ,EAEZ,IAAK,IAAIC,EAAQ,EAAGA,EAAQN,EAAOO,OAAQD,GAAS,EAAG,CACrD,MAAME,EAAOR,EAAOM,GAEhBH,EACFA,GAAY,EACM,OAATK,GAAiBJ,EAC1BD,GAAY,EACM,MAATK,EACTJ,GAAYA,EACFA,GAAqB,MAATI,EAEZJ,GAAqB,MAATI,EAEZJ,GAA2B,IAAfF,GAA6B,MAATM,IAC1CP,EAAMQ,KAAKT,EAAOU,MAAML,EAAOC,GAAOK,QACtCN,EAAQC,EAAQ,GAHhBJ,EAAaU,KAAKC,IAAI,EAAGX,EAAa,GAFtCA,GAAc,CAOlB,CAIA,OAFAD,EAAMQ,KAAKT,EAAOU,MAAML,GAAOM,QAExBV,EAAMa,OAAOC,QAAQ"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{getHeaderEntries as e}from"../core/headers.js";import t from"./split-link-header.js";const r=(r,i)=>{if(r.headersSent||"function"!=typeof r.writeEarlyHints)return;const n=Object.fromEntries(e(i)),s=i.get("Link");s&&(n.link=t(s)),Object.keys(n).length&&r.writeEarlyHints(n)};export{r as default};
|
|
2
|
+
//# sourceMappingURL=write-early-hints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-early-hints.js","sources":["../../src/node/write-early-hints.ts"],"sourcesContent":["import { getHeaderEntries } from '@core/headers';\nimport type { TServerResponse } from '@node/http';\nimport splitLinkHeader from '@node/split-link-header';\n\n/**\n * Send Node early hints with distinct Link entries when the transport supports them.\n */\nconst writeEarlyHints = (res: TServerResponse, headers: Headers): void => {\n if (res.headersSent || typeof res.writeEarlyHints !== 'function') {\n return;\n }\n\n const hints: Record<string, string | string[]> = Object.fromEntries(getHeaderEntries(headers));\n const link = headers.get('Link');\n\n if (link) {\n hints.link = splitLinkHeader(link);\n }\n\n if (Object.keys(hints).length) {\n res.writeEarlyHints(hints);\n }\n};\n\nexport default writeEarlyHints;\n"],"names":["writeEarlyHints","res","headers","headersSent","hints","Object","fromEntries","getHeaderEntries","link","get","splitLinkHeader","keys","length"],"mappings":"4FAOA,MAAMA,EAAkB,CAACC,EAAsBC,KAC7C,GAAID,EAAIE,aAA8C,mBAAxBF,EAAID,gBAChC,OAGF,MAAMI,EAA2CC,OAAOC,YAAYC,EAAiBL,IAC/EM,EAAON,EAAQO,IAAI,QAErBD,IACFJ,EAAMI,KAAOE,EAAgBF,IAG3BH,OAAOM,KAAKP,GAAOQ,QACrBX,EAAID,gBAAgBI,EACtB"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { TServerResponse } from "./http.js";
|
|
2
|
+
/**
|
|
3
|
+
* Write Fetch status and headers while preserving existing transport cookies.
|
|
4
|
+
*/
|
|
5
|
+
declare const writeFetchHeaders: (res: TServerResponse, response: Response) => void;
|
|
6
|
+
/**
|
|
7
|
+
* Write the Fetch body with backpressure and cancel unread data on disconnect.
|
|
8
|
+
*/
|
|
9
|
+
declare const writeFetchResponse: (res: TServerResponse, response: Response) => Promise<void>;
|
|
10
|
+
export { writeFetchResponse as default, writeFetchHeaders };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{getHeaderEntries as e,getSetCookieHeaders as r}from"../core/headers.js";const a="Set-Cookie",o=(o,d)=>{o.statusCode=d.status,e(d.headers).forEach((([e,r])=>o.setHeader(e,r)));const t=r(d.headers);if("function"==typeof o.appendHeader)t.forEach((e=>o.appendHeader(a,e)));else if(t.length){const e=o.getHeader(a),r=Array.isArray(e)?e:[String(e)];o.setHeader(a,e?[...r,...t]:t)}},d=e=>new Promise(((r,a)=>{const o=()=>{e.off("close",d),e.off("drain",t),e.off("error",n)},d=()=>{o(),a(new Error("Response closed before drain."))},t=()=>{o(),r()},n=e=>{o(),a(e)};e.once("close",d),e.once("drain",t),e.once("error",n)})),t=async(e,r)=>{if(e.destroyed||e.writableEnded)return void await(r.body?.cancel());if(e.headersSent||o(e,r),!r.body||"HEAD"===e.req?.method||[204,205,304].includes(e.statusCode))return await(r.body?.cancel()),void e.end();const a=r.body.getReader(),t=()=>{e.writableEnded||a.cancel().catch((()=>{}))};e.once("close",t);try{for(;;){const r=await a.read();if(r.done)break;if(e.destroyed||e.writableEnded)return void await a.cancel();e.write(r.value)||await d(e),e.flush?.()}e.destroyed||e.writableEnded||e.end()}catch(r){await a.cancel(r).catch((()=>{})),e.destroyed||e.destroy(r)}finally{e.off("close",t),a.releaseLock()}};export{t as default,o as writeFetchHeaders};
|
|
2
|
+
//# sourceMappingURL=write-fetch-response.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-fetch-response.js","sources":["../../src/node/write-fetch-response.ts"],"sourcesContent":["import { getHeaderEntries, getSetCookieHeaders } from '@core/headers';\nimport type { TServerResponse } from '@node/http';\n\nconst SET_COOKIE = 'Set-Cookie';\n\n/**\n * Write Fetch status and headers while preserving existing transport cookies.\n */\nconst writeFetchHeaders = (res: TServerResponse, response: Response): void => {\n res.statusCode = response.status;\n getHeaderEntries(response.headers).forEach(([name, value]) => res.setHeader(name, value));\n const cookies = getSetCookieHeaders(response.headers);\n\n if (typeof res.appendHeader === 'function') {\n cookies.forEach((cookie) => res.appendHeader(SET_COOKIE, cookie));\n } else if (cookies.length) {\n /**\n * The HTTP/2 compatibility response has setHeader(), but no appendHeader().\n */\n const existing = res.getHeader(SET_COOKIE);\n const previousCookies = Array.isArray(existing) ? existing : [String(existing)];\n\n res.setHeader(SET_COOKIE, existing ? [...previousCookies, ...cookies] : cookies);\n }\n};\n\n/**\n * Wait for response capacity and release listeners on drain, close or failure.\n */\nconst waitForDrain = (res: TServerResponse): Promise<void> =>\n new Promise((resolve, reject) => {\n /**\n * Remove every listener registered for this drain wait.\n */\n const cleanup = (): void => {\n res.off('close', onClose);\n res.off('drain', onDrain);\n res.off('error', onError);\n };\n\n /**\n * Stop waiting for drain when the client closes the response.\n */\n const onClose = (): void => {\n cleanup();\n reject(new Error('Response closed before drain.'));\n };\n\n /**\n * Resume body consumption when the transport has capacity.\n */\n const onDrain = (): void => {\n cleanup();\n resolve();\n };\n\n /**\n * Release drain listeners before rejecting a transport failure.\n */\n const onError = (error: Error): void => {\n cleanup();\n reject(error);\n };\n\n res.once('close', onClose);\n res.once('drain', onDrain);\n res.once('error', onError);\n });\n\n/**\n * Write the Fetch body with backpressure and cancel unread data on disconnect.\n */\nconst writeFetchResponse = async (res: TServerResponse, response: Response): Promise<void> => {\n if (res.destroyed || res.writableEnded) {\n await response.body?.cancel();\n\n return;\n }\n\n if (!res.headersSent) {\n writeFetchHeaders(res, response);\n }\n\n if (!response.body || res.req?.method === 'HEAD' || [204, 205, 304].includes(res.statusCode)) {\n await response.body?.cancel();\n res.end();\n\n return;\n }\n\n const reader = response.body.getReader();\n\n /**\n * Cancel unread Fetch bytes when the response is interrupted.\n */\n const onClose = (): void => {\n if (!res.writableEnded) {\n void reader.cancel().catch(() => undefined);\n }\n };\n\n res.once('close', onClose);\n\n try {\n while (true) {\n const chunk = await reader.read();\n\n if (chunk.done) {\n break;\n }\n\n if (res.destroyed || res.writableEnded) {\n await reader.cancel();\n\n return;\n }\n\n if (!res.write(chunk.value)) {\n await waitForDrain(res);\n }\n\n /**\n * Express compression buffers otherwise: a gzip header alone is not a usable SSR shell.\n */\n (res as TServerResponse & { flush?: () => void }).flush?.();\n }\n\n if (!res.destroyed && !res.writableEnded) {\n res.end();\n }\n } catch (error) {\n await reader.cancel(error).catch(() => undefined);\n\n if (!res.destroyed) {\n res.destroy(error as Error);\n }\n } finally {\n res.off('close', onClose);\n reader.releaseLock();\n }\n};\n\nexport { writeFetchHeaders };\n\nexport default writeFetchResponse;\n"],"names":["SET_COOKIE","writeFetchHeaders","res","response","statusCode","status","getHeaderEntries","headers","forEach","name","value","setHeader","cookies","getSetCookieHeaders","appendHeader","cookie","length","existing","getHeader","previousCookies","Array","isArray","String","waitForDrain","Promise","resolve","reject","cleanup","off","onClose","onDrain","onError","Error","error","once","writeFetchResponse","async","destroyed","writableEnded","body","cancel","headersSent","req","method","includes","end","reader","getReader","catch","chunk","read","done","write","flush","destroy","releaseLock"],"mappings":"+EAGA,MAAMA,EAAa,aAKbC,EAAoB,CAACC,EAAsBC,KAC/CD,EAAIE,WAAaD,EAASE,OAC1BC,EAAiBH,EAASI,SAASC,SAAQ,EAAEC,EAAMC,KAAWR,EAAIS,UAAUF,EAAMC,KAClF,MAAME,EAAUC,EAAoBV,EAASI,SAE7C,GAAgC,mBAArBL,EAAIY,aACbF,EAAQJ,SAASO,GAAWb,EAAIY,aAAad,EAAYe,UACpD,GAAIH,EAAQI,OAAQ,CAIzB,MAAMC,EAAWf,EAAIgB,UAAUlB,GACzBmB,EAAkBC,MAAMC,QAAQJ,GAAYA,EAAW,CAACK,OAAOL,IAErEf,EAAIS,UAAUX,EAAYiB,EAAW,IAAIE,KAAoBP,GAAWA,EAC1E,GAMIW,EAAgBrB,GACpB,IAAIsB,SAAQ,CAACC,EAASC,KAIpB,MAAMC,EAAU,KACdzB,EAAI0B,IAAI,QAASC,GACjB3B,EAAI0B,IAAI,QAASE,GACjB5B,EAAI0B,IAAI,QAASG,EAAQ,EAMrBF,EAAU,KACdF,IACAD,EAAO,IAAIM,MAAM,iCAAiC,EAM9CF,EAAU,KACdH,IACAF,GAAS,EAMLM,EAAWE,IACfN,IACAD,EAAOO,EAAM,EAGf/B,EAAIgC,KAAK,QAASL,GAClB3B,EAAIgC,KAAK,QAASJ,GAClB5B,EAAIgC,KAAK,QAASH,EAAQ,IAMxBI,EAAqBC,MAAOlC,EAAsBC,KACtD,GAAID,EAAImC,WAAanC,EAAIoC,cAGvB,kBAFMnC,EAASoC,MAAMC,UASvB,GAJKtC,EAAIuC,aACPxC,EAAkBC,EAAKC,IAGpBA,EAASoC,MAA4B,SAApBrC,EAAIwC,KAAKC,QAAqB,CAAC,IAAK,IAAK,KAAKC,SAAS1C,EAAIE,YAI/E,aAHMD,EAASoC,MAAMC,eACrBtC,EAAI2C,MAKN,MAAMC,EAAS3C,EAASoC,KAAKQ,YAKvBlB,EAAU,KACT3B,EAAIoC,eACFQ,EAAON,SAASQ,OAAM,KAAe,GAC5C,EAGF9C,EAAIgC,KAAK,QAASL,GAElB,IACE,OAAa,CACX,MAAMoB,QAAcH,EAAOI,OAE3B,GAAID,EAAME,KACR,MAGF,GAAIjD,EAAImC,WAAanC,EAAIoC,cAGvB,kBAFMQ,EAAON,SAKVtC,EAAIkD,MAAMH,EAAMvC,cACba,EAAarB,GAMpBA,EAAiDmD,SACpD,CAEKnD,EAAImC,WAAcnC,EAAIoC,eACzBpC,EAAI2C,KAER,CAAE,MAAOZ,SACDa,EAAON,OAAOP,GAAOe,OAAM,KAAe,IAE3C9C,EAAImC,WACPnC,EAAIoD,QAAQrB,EAEhB,SACE/B,EAAI0B,IAAI,QAASC,GACjBiB,EAAOS,aACT"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lomray/vite-ssr-boost",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.0-beta.1",
|
|
4
4
|
"description": "Vite plugin for create awesome SSR or SPA applications on React.",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
"./*.js": {
|
|
8
|
+
"types": "./*.d.ts",
|
|
9
|
+
"default": "./*.js"
|
|
10
|
+
},
|
|
11
|
+
"./*.d.ts": "./*.d.ts",
|
|
12
|
+
"./*.map": "./*.map",
|
|
13
|
+
"./*.json": "./*.json",
|
|
14
|
+
"./*.md": "./*.md",
|
|
15
|
+
"./LICENSE": "./LICENSE",
|
|
16
|
+
"./workflow/*": "./workflow/*",
|
|
17
|
+
"./*": {
|
|
18
|
+
"types": "./*.d.ts",
|
|
19
|
+
"default": "./*.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
6
22
|
"keywords": [
|
|
7
23
|
"js",
|
|
8
24
|
"vite",
|
|
@@ -34,16 +50,22 @@
|
|
|
34
50
|
"lint:format": "eslint . --fix",
|
|
35
51
|
"ts:check": "tsc --project ./tsconfig.json --skipLibCheck --noemit",
|
|
36
52
|
"test": "vitest run",
|
|
53
|
+
"test:core:no-optional": "node scripts/test-core-no-optional.mjs",
|
|
54
|
+
"test:edge:packed": "node scripts/test-packed-edge.mjs",
|
|
55
|
+
"test:bun": "bun scripts/test-bun.mjs",
|
|
56
|
+
"test:template": "npm run build && node scripts/test-template.mjs",
|
|
37
57
|
"test:coverage": "vitest run --coverage"
|
|
38
58
|
},
|
|
39
59
|
"dependencies": {
|
|
40
60
|
"chalk": "^5.6.2",
|
|
41
61
|
"commander": "^14.0.3",
|
|
42
|
-
"compression": "^1.8.1",
|
|
43
|
-
"express": "^5.2.1",
|
|
44
62
|
"hoist-non-react-statics": "^3.3.2",
|
|
45
63
|
"json5": "^2.2.3"
|
|
46
64
|
},
|
|
65
|
+
"optionalDependencies": {
|
|
66
|
+
"compression": "^1.8.1",
|
|
67
|
+
"express": "^5.2.1"
|
|
68
|
+
},
|
|
47
69
|
"devDependencies": {
|
|
48
70
|
"@commitlint/cli": "^20.5.0",
|
|
49
71
|
"@commitlint/config-conventional": "^20.5.0",
|
|
@@ -59,11 +81,17 @@
|
|
|
59
81
|
"@types/sinon": "^21.0.0",
|
|
60
82
|
"@vitest/coverage-v8": "^4.1.0",
|
|
61
83
|
"@zerollup/ts-transform-paths": "^1.7.18",
|
|
84
|
+
"conventional-changelog-conventionalcommits": "^9.3.0",
|
|
85
|
+
"esbuild": "^0.25.12",
|
|
62
86
|
"eslint": "^9.39.4",
|
|
87
|
+
"fastify": "^5.10.0",
|
|
88
|
+
"hono": "^4.12.31",
|
|
63
89
|
"husky": "^9.1.7",
|
|
64
90
|
"jsdom": "^29.0.1",
|
|
65
91
|
"lint-staged": "^16.4.0",
|
|
92
|
+
"miniflare": "^4.20260721.0",
|
|
66
93
|
"prettier": "^3.8.1",
|
|
94
|
+
"react-router": "^7.18.3",
|
|
67
95
|
"rollup": "^4.59.1",
|
|
68
96
|
"rollup-plugin-copy": "^3.5.0",
|
|
69
97
|
"rollup-plugin-folder-input": "^1.0.1",
|
|
@@ -73,6 +101,7 @@
|
|
|
73
101
|
"semantic-release": "^25.0.3",
|
|
74
102
|
"sinon": "^21.0.3",
|
|
75
103
|
"typescript": "^5.3.3",
|
|
104
|
+
"vite": "^6.4.3",
|
|
76
105
|
"vitepress": "^1.6.4",
|
|
77
106
|
"vitest": "^4.1.0"
|
|
78
107
|
},
|
|
@@ -80,11 +109,24 @@
|
|
|
80
109
|
"@babel/generator": ">=7.23.0",
|
|
81
110
|
"@babel/parser": ">=7.23.0",
|
|
82
111
|
"@babel/traverse": ">=7.23.0",
|
|
112
|
+
"@types/compression": ">=1.7.5",
|
|
83
113
|
"@types/express": ">=4.17.21",
|
|
114
|
+
"@types/serve-static": ">=1.15.5",
|
|
84
115
|
"react-dom": ">=18.2.0",
|
|
85
116
|
"react-router": ">=7.0.1",
|
|
86
117
|
"vite": ">=5"
|
|
87
118
|
},
|
|
119
|
+
"peerDependenciesMeta": {
|
|
120
|
+
"@types/compression": {
|
|
121
|
+
"optional": true
|
|
122
|
+
},
|
|
123
|
+
"@types/express": {
|
|
124
|
+
"optional": true
|
|
125
|
+
},
|
|
126
|
+
"@types/serve-static": {
|
|
127
|
+
"optional": true
|
|
128
|
+
}
|
|
129
|
+
},
|
|
88
130
|
"overrides": {
|
|
89
131
|
"rollup-plugin-ts": {
|
|
90
132
|
"browserslist-generator": "^3.0.0"
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Express } from 'express';
|
|
2
1
|
import { Logger, ViteDevServer } from 'vite';
|
|
3
2
|
import { IPluginConfig } from "../helpers/plugin-config.js";
|
|
4
3
|
import { IBuildEntrypoint } from "./build.js";
|
|
@@ -53,7 +52,7 @@ declare class ServerConfig {
|
|
|
53
52
|
/**
|
|
54
53
|
* Express application
|
|
55
54
|
*/
|
|
56
|
-
protected app?:
|
|
55
|
+
protected app?: unknown;
|
|
57
56
|
/**
|
|
58
57
|
* Config params
|
|
59
58
|
*/
|
|
@@ -111,7 +110,7 @@ declare class ServerConfig {
|
|
|
111
110
|
/**
|
|
112
111
|
* Set express server
|
|
113
112
|
*/
|
|
114
|
-
setApp(
|
|
113
|
+
setApp<T>(app: T): void;
|
|
115
114
|
/**
|
|
116
115
|
* Return vite dev server
|
|
117
116
|
* NOTE: only on development mode
|
|
@@ -127,7 +126,7 @@ declare class ServerConfig {
|
|
|
127
126
|
/**
|
|
128
127
|
* Return express server
|
|
129
128
|
*/
|
|
130
|
-
getApp():
|
|
129
|
+
getApp<T = unknown>(): T | undefined;
|
|
131
130
|
/**
|
|
132
131
|
* Return plugin config
|
|
133
132
|
* NOTE: only on development mode
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-config.js","sources":["../../src/services/server-config.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport type {
|
|
1
|
+
{"version":3,"file":"server-config.js","sources":["../../src/services/server-config.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport type { Logger, ViteDevServer } from 'vite';\nimport type { IPluginConfig } from '@helpers/plugin-config';\nimport getPluginConfig from '@helpers/plugin-config';\nimport type { IBuildEntrypoint } from '@services/build';\nimport DefaultLogger from '@services/logger';\n\ninterface IConfigOptions {\n isProd?: boolean;\n isHost?: boolean;\n isOnlyClient?: boolean; // SPA mode\n isModulePreload?: boolean;\n mode?: string;\n entrypointName?: string;\n}\n\ninterface IConfigParams {\n root: string;\n publicDir: string;\n pluginPath: string;\n isProd: boolean;\n isSPA: boolean;\n indexFile: string;\n clientFile: string;\n serverFile: string;\n host: string;\n port: number;\n}\n\n/**\n * Server config\n */\nclass ServerConfig {\n /**\n * Production build\n */\n public readonly isProd: boolean;\n\n /**\n * Server host mode\n */\n public readonly isHost: boolean;\n\n /**\n * Add module preload scripts to server output\n */\n public readonly isModulePreload: boolean;\n\n /**\n * Env mode\n */\n public readonly mode: string;\n\n /**\n * Run specified entrypoint\n */\n protected readonly entrypointName?: string;\n\n /**\n * Vite config - only for development\n */\n protected vite?: ViteDevServer;\n\n /**\n * Express application\n */\n protected app?: unknown;\n\n /**\n * Config params\n */\n protected params: IConfigParams;\n\n /**\n * Default params\n */\n protected defaultParams: Partial<IConfigParams>;\n\n /**\n * Vite logger for dev mode or console for production\n */\n protected logger: Logger;\n\n /**\n * Default root dir\n */\n protected defaultBuildRoots = ['./build', './dist'];\n\n /**\n * @constructor\n */\n protected constructor(\n {\n entrypointName,\n isProd = false,\n isHost = false,\n isOnlyClient = false,\n isModulePreload = false,\n mode = 'production',\n }: IConfigOptions,\n prodParams: Partial<IConfigParams>,\n ) {\n this.isProd = isProd;\n this.isHost = isHost;\n this.isModulePreload = isModulePreload;\n this.mode = mode;\n this.entrypointName = entrypointName;\n this.defaultParams = {\n publicDir: '/client', // default for production,\n indexFile: '/client/index.html',\n serverFile: '/server/server.js',\n host: '127.0.0.1',\n isSPA: isOnlyClient,\n ...prodParams,\n };\n\n this.makeParams();\n }\n\n /**\n * Initialize service\n */\n public static init(\n options: IConfigOptions = {},\n prodOptions: Partial<IConfigParams> = {},\n ): ServerConfig {\n return new ServerConfig(options, prodOptions);\n }\n\n /**\n * Lookup build folder\n */\n protected getBuildDir(root?: string): string {\n for (const dir of [root, ...this.defaultBuildRoots]) {\n if (dir && fs.existsSync(dir)) {\n return dir;\n }\n }\n\n return root ?? this.defaultBuildRoots[0];\n }\n\n /**\n * Make config params\n */\n protected makeParams(): void {\n this.applyEntrypointConfig();\n\n const pluginConfig = (this.getPluginConfig() ?? {}) as Partial<IPluginConfig>;\n const { config } = this.vite ?? {};\n const {\n root,\n publicDir,\n indexFile,\n clientFile,\n serverFile,\n host: defaultHost,\n isSPA,\n } = this.defaultParams;\n const dirInfo = new URL(import.meta.url);\n const pluginPath =\n pluginConfig.pluginPath ?? path.resolve(path.dirname(dirInfo.pathname), '../');\n const entrypoint = this.getEntrypoint();\n\n const host =\n typeof config?.server.host === 'boolean' || this.isHost\n ? '0.0.0.0'\n : (config?.server.host ?? defaultHost!);\n const port = Number(config?.env.VITE_PORT ?? config?.server.port ?? this.defaultParams.port!);\n\n this.params = {\n root: config?.root ?? this.getBuildDir(root),\n publicDir: config?.publicDir ?? publicDir!,\n indexFile: pluginConfig.indexFile ?? indexFile!,\n clientFile: pluginConfig.clientFile ?? clientFile!,\n serverFile: pluginConfig.serverFile ?? serverFile!,\n pluginPath,\n host,\n port,\n isSPA: entrypoint ? entrypoint.type === 'spa' : isSPA!,\n isProd: this.isProd,\n };\n this.logger = this.vite?.config.logger ?? new DefaultLogger();\n }\n\n /**\n * Set vite server\n */\n public setVite(vite: ViteDevServer): void {\n this.vite = vite;\n\n this.makeParams();\n }\n\n /**\n * Set express server\n */\n public setApp<T>(app: T): void {\n this.app = app;\n }\n\n /**\n * Return vite dev server\n * NOTE: only on development mode\n */\n public getVite(): ViteDevServer | undefined {\n return this.vite;\n }\n\n /**\n * Return express server\n */\n public getApp<T = unknown>(): T | undefined {\n return this.app as T | undefined;\n }\n\n /**\n * Return plugin config\n * NOTE: only on development mode\n */\n public getPluginConfig(): IPluginConfig | undefined {\n return this.vite ? getPluginConfig(this.vite.config) : undefined;\n }\n\n /**\n * Return config params\n */\n public getParams(): IConfigParams {\n return this.params;\n }\n\n /**\n * Get server logger\n */\n public getLogger(): Logger {\n return this.logger;\n }\n\n /**\n * Set custom logger\n */\n public setLogger(logger: Logger): void {\n this.logger = logger;\n }\n\n /**\n * Apply config to specified entrypoint\n */\n protected applyEntrypointConfig(): void {\n const config = this.getPluginConfig();\n\n if (!this.vite || !config || !this.entrypointName) {\n return;\n }\n\n const entrypointConfig = this.getEntrypoint();\n\n // apply specified entrypoint config\n if (entrypointConfig) {\n (['indexFile', 'clientFile', 'serverFile'] as const).forEach((optName) => {\n if (entrypointConfig[optName]) {\n config[optName] = entrypointConfig[optName]!;\n }\n });\n }\n }\n\n /**\n * Get current entrypoint\n */\n protected getEntrypoint(): IBuildEntrypoint | undefined {\n const config = this.vite ? getPluginConfig(this.vite.config) : undefined;\n\n return config?.entrypoint?.find(({ name }) => name === this.entrypointName);\n }\n}\n\nexport default ServerConfig;\n"],"names":["ServerConfig","isProd","isHost","isModulePreload","mode","entrypointName","vite","app","params","defaultParams","logger","defaultBuildRoots","constructor","isOnlyClient","prodParams","this","publicDir","indexFile","serverFile","host","isSPA","makeParams","static","options","prodOptions","getBuildDir","root","dir","fs","existsSync","applyEntrypointConfig","pluginConfig","getPluginConfig","config","clientFile","defaultHost","dirInfo","URL","url","pluginPath","path","resolve","dirname","pathname","entrypoint","getEntrypoint","server","port","Number","env","VITE_PORT","type","DefaultLogger","setVite","setApp","getVite","getApp","undefined","getParams","getLogger","setLogger","entrypointConfig","forEach","optName","find","name"],"mappings":"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"}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
import { Socket } from 'node:net';
|
|
3
|
-
import { RouterState } from 'react-router';
|
|
1
|
+
import { RouterState, StaticHandlerContext } from 'react-router';
|
|
4
2
|
import { Alias, ModuleNode, RenderBuiltAssetUrl } from 'vite';
|
|
5
|
-
import { IRequestContext } from "../node/render.js";
|
|
6
3
|
import { TRoutesTree } from "./parse-routes.js";
|
|
7
4
|
import PathNormalize from "./path-normalize.js";
|
|
8
5
|
import ServerConfig from "./server-config.js";
|
|
@@ -38,6 +35,12 @@ interface IAsset {
|
|
|
38
35
|
type TAssets = {
|
|
39
36
|
[id: string]: IAsset;
|
|
40
37
|
};
|
|
38
|
+
interface IInjectAssetsContext {
|
|
39
|
+
html: {
|
|
40
|
+
header: string;
|
|
41
|
+
};
|
|
42
|
+
routerContext?: StaticHandlerContext;
|
|
43
|
+
}
|
|
41
44
|
/**
|
|
42
45
|
* Working with SSR Manifest file
|
|
43
46
|
*/
|
|
@@ -169,6 +172,20 @@ declare class SsrManifest {
|
|
|
169
172
|
/**
|
|
170
173
|
* Get development route assets
|
|
171
174
|
*/
|
|
175
|
+
protected getDevModules(routes?: RouterState['matches']): ModuleNode[];
|
|
176
|
+
/**
|
|
177
|
+
* Compile SSR graph styles before the browser has populated the client graph.
|
|
178
|
+
*/
|
|
179
|
+
/**
|
|
180
|
+
* Compile SSR graph styles before the browser has populated the client graph.
|
|
181
|
+
*/
|
|
182
|
+
prepareDevAssets(routes?: RouterState['matches']): Promise<void>;
|
|
183
|
+
/**
|
|
184
|
+
* Collect development assets from the current server and client module graphs.
|
|
185
|
+
*/
|
|
186
|
+
/**
|
|
187
|
+
* Collect development assets from the current server and client module graphs.
|
|
188
|
+
*/
|
|
172
189
|
protected getAssetsDev(routes?: RouterState['matches']): IAsset[];
|
|
173
190
|
/**
|
|
174
191
|
* Get module assets
|
|
@@ -192,18 +209,18 @@ declare class SsrManifest {
|
|
|
192
209
|
*/
|
|
193
210
|
protected getAssetType(asset: string): AssetType | null;
|
|
194
211
|
/**
|
|
195
|
-
*
|
|
212
|
+
* Build preload hints without depending on a particular HTTP transport.
|
|
196
213
|
*/
|
|
197
214
|
/**
|
|
198
|
-
*
|
|
215
|
+
* Build preload hints without depending on a particular HTTP transport.
|
|
199
216
|
*/
|
|
200
|
-
|
|
217
|
+
getEarlyHints(assets: IAsset[]): Headers;
|
|
201
218
|
/**
|
|
202
219
|
* Inject route assets to head html
|
|
203
220
|
*/
|
|
204
221
|
/**
|
|
205
222
|
* Inject route assets to head html
|
|
206
223
|
*/
|
|
207
|
-
injectAssets({ routerContext, html
|
|
224
|
+
injectAssets({ routerContext, html }: IInjectAssetsContext): Headers;
|
|
208
225
|
}
|
|
209
226
|
export { SsrManifest as default };
|
package/services/ssr-manifest.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e from"node:fs";import
|
|
1
|
+
import e from"node:fs";import s from"node:path";import t from"chalk";import i from"./parse-routes.js";import r from"./path-normalize.js";var n;!function(e){e.style="style",e.script="script",e.image="image",e.font="font"}(n||(n={}));class o{static instance=null;config;pathNormalize;root;buildDir;manifestName="manifest.json";assetsManifest="assets-manifest.json";viteAliases;basename;renderBuiltUrl;routesAssets=null;constructor(e,{buildDir:s,viteAliases:t,basename:i,renderBuiltUrl:n}={}){this.config=e,this.root=e.getParams().root,this.buildDir=s,this.viteAliases=t??e.getVite()?.config?.resolve.alias,this.pathNormalize=new r(e,t),this.basename=i,this.renderBuiltUrl=n}static get(e,s={}){return null===o.instance&&(o.instance=new o(e,s)),o.instance}getOutDir(){return s.resolve(this.root,this.buildDir||"")}getAssetsManifestFile(){return`${this.getOutDir()}/server/${this.assetsManifest}`}loadClientManifest(){const t=s.resolve(this.root,`${this.buildDir||""}/client/.vite`),i=`${t}/${this.manifestName}`;if(!e.existsSync(i))return{};const r=JSON.parse(e.readFileSync(i,{encoding:"utf-8"}));return e.rmSync(i),0===e.readdirSync(t).length&&e.rmSync(t,{recursive:!0}),r}loadAssetsManifest(){if(null!==this.routesAssets)return this.routesAssets;const s=this.getAssetsManifestFile();return e.existsSync(s)?(this.routesAssets=JSON.parse(e.readFileSync(s,{encoding:"utf-8"})),this.routesAssets):{}}getRoutesTreeIds(e,s){const t={};return e.forEach(((e,i)=>{const r=[s,String(i)].filter(Boolean).join("-");e.import&&(t[r]=this.pathNormalize.getAppPath(e.import)),e.children.length>0&&Object.assign(t,this.getRoutesTreeIds(e.children,r))})),t}sortAssets(e){return e.sort(((e,s)=>e.weight===s.weight?Number(e.isNested)-Number(s.isNested):e.weight-s.weight))}getRouteAssets(e,t,i=!1){const r=[...t?.assets??[],...t?.css??[],t?.file].reduce(((e,r)=>{if(r){const n=this.getAssetType(r),o=t.isEntry&&t.file===r;if(n){const t=s.posix.normalize(`${this.basename}/${r}`),a=this.renderBuiltUrl?.(t,{type:"asset",ssr:!0,hostId:"",hostType:t.split(".").at(-1)?.toLowerCase()});e[r]={url:"string"==typeof a?a:t,weight:o?1.9:this.getAssetWeight(r),type:n,isNested:i,isPreload:!o}}}return e}),{});return t?.imports?.length&&t.imports.forEach((s=>{const t=e[s];t&&Object.assign(r,this.getRouteAssets(e,t,!0))})),r}buildRoutesManifest(){const s=this.loadClientManifest(),t=new i(this.config,this.viteAliases),r=this.getRoutesTreeIds(t.parse()),n=this.pathNormalize.getImportPostfix(),o={};Object.entries(r).forEach((([e,t])=>{const i=n.find((e=>void 0!==s[`${t}${e}`])),r=s[`${t}${i||""}`];o[e]=this.sortAssets(Object.values(this.getRouteAssets(s,r)))})),e.writeFileSync(this.getAssetsManifestFile(),JSON.stringify(o,null,2),{encoding:"utf-8"})}getAssets(e){if(this.config.getVite())return this.getAssetsDev(e);const s=e?.map((({route:e})=>e.id)).filter(Boolean)??[];if(!s.length)return[];const t=this.loadAssetsManifest();return this.sortAssets(s.map((e=>t[e])).flat().filter(Boolean))}getDevModules(e){const t=e?.map((({route:e})=>this.pathNormalize.getAppPath(e?.pathId,!0))).filter(Boolean)??[],i=this.pathNormalize.getImportPostfix(),r=this.config.getPluginConfig(),n=[r?.clientFile??"client.ts",r?.serverFile??"server.ts"].map((e=>s.resolve(this.root,e))),o=[];return[...n,...t].forEach((e=>{for(const s of i){const t=this.config.getVite()?.moduleGraph.getModuleById(`${e}${s}`);if(t){o.push(t);break}}})),o}async prepareDevAssets(e){const s=this.config.getVite();if(!s)return;const t=new Set,i=async e=>{t.has(e.url)||(t.add(e.url),e.file&&/\.(?:css|less|s[ac]ss|styl(?:us)?|pcss|postcss)$/.test(e.file)&&await s.transformRequest(e.url),await Promise.all([...e.importedModules].map(i)))};await Promise.all(this.getDevModules(e).map(i))}getAssetsDev(e){const s=Object.assign({},...this.getDevModules(e).map((e=>this.getModuleAssets(e))));return Object.values(s)}getModuleAssets(e,s=new Set){const i=e?.importedModules??e?.clientImportedModules;if(!i?.size||s.has(e.file))return{};let r={};return s.add(e.file),i.forEach((e=>{const{file:i,transformResult:o}=e,a=i?.split(".").at(-1);if(i&&a&&["css","scss","sass","less","styl","stylus","pcss","postcss"].includes(a)){const e=o?.code.match(/__vite__css\s+=\s+"(?<css>.+)"/)?.groups?.css;if(e)try{r[i]={type:n.style,url:i,weight:this.getAssetWeight(i),content:JSON.parse(`{"style": "${e}"}`).style,isNested:Boolean(s.size),isPreload:!1}}catch{console.warn(t.yellowBright("Failed to parse style: ",i))}}else r={...r,...this.getModuleAssets(e,s)}})),r}getAssetWeight(e){switch(this.getAssetType(e)){case n.style:return 1;case n.script:return 2;default:return 3}}getAssetType(e){const s=e.split(".").at(-1)?.toLowerCase();switch(s){case"css":case"scss":return n.style;case"js":return n.script;case"svg":case"jpg":case"jpeg":case"png":case"webp":case"gif":case"ico":return n.image;case"ttf":case"otf":case"woff":case"woff2":return n.font;default:return null}}getEarlyHints(e){const s=new Headers;return e.forEach((({type:e,url:t})=>{e&&["style","script"].includes(e)&&s.append("Link",`<${t}>; rel=preload; as=${e}`)})),s}injectAssets({routerContext:e,html:s}){const t=this.getAssets(e?.matches),i=t.map((({type:e,url:s,isPreload:t,content:i=""})=>{switch(e){case n.style:return this.config.getVite()?`<style data-vite-dev-id="${s}">${i}</style>`:`<link rel="stylesheet" href="${s}">`;case n.script:return t?this.config.isModulePreload?`<link rel="modulepreload" as="script" crossorigin href="${s}">`:null:`<script async type="module" crossorigin src="${s}"><\/script>`}return null})).filter(Boolean);return s.header=s.header.replace("</head>",`${i.join("\n")}</head>`),this.getEarlyHints(t)}}export{o as default};
|
|
2
2
|
//# sourceMappingURL=ssr-manifest.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr-manifest.js","sources":["../../src/services/ssr-manifest.ts"],"sourcesContent":["import fs from 'node:fs';\nimport type { Socket } from 'node:net';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport type { RouterState } from 'react-router';\nimport type { Alias, ModuleNode, RenderBuiltAssetUrl } from 'vite';\nimport type { IAsyncRoute } from '@helpers/import-route';\nimport type { IRequestContext } from '@node/render';\nimport type { TRoutesTree } from '@services/parse-routes';\nimport ParseRoutes from '@services/parse-routes';\nimport PathNormalize from '@services/path-normalize';\nimport type ServerConfig from '@services/server-config';\n\ninterface ISsrManifestParams {\n buildDir?: string;\n viteAliases?: Alias[];\n basename?: string;\n renderBuiltUrl?: RenderBuiltAssetUrl;\n}\n\ninterface IManifest {\n [path: string]: {\n assets: string[];\n css: string[];\n file: string;\n isEntry?: boolean;\n imports: string[];\n };\n}\n\nenum AssetType {\n style = 'style',\n script = 'script',\n image = 'image',\n font = 'font',\n}\n\ninterface IAsset {\n type: AssetType;\n url: string;\n weight: number;\n isNested: boolean;\n isPreload: boolean;\n content?: string;\n}\n\ntype TAssets = { [id: string]: IAsset };\n\nconst CRLF = '\\r\\n';\n\n/**\n * Working with SSR Manifest file\n */\nclass SsrManifest {\n /**\n * Singleton\n */\n protected static instance: SsrManifest | null = null;\n\n /**\n * Server config\n */\n protected readonly config: ServerConfig;\n\n /**\n * Path normalize service\n */\n protected readonly pathNormalize: PathNormalize;\n\n /**\n * Project root path\n */\n protected readonly root: string;\n\n /**\n * Build dir\n */\n protected readonly buildDir?: string;\n\n /**\n * Client manifest file name\n */\n protected readonly manifestName = 'manifest.json';\n\n /**\n * Assets manifest file name\n */\n protected readonly assetsManifest = 'assets-manifest.json';\n\n /**\n * Vite resolve aliases\n */\n protected readonly viteAliases?: Alias[];\n\n /**\n * Vite base\n */\n protected readonly basename?: string;\n\n /**\n * Vite renderBuiltUrl config func\n */\n protected readonly renderBuiltUrl?: RenderBuiltAssetUrl;\n\n /**\n * Loaded assets manifest file\n */\n protected routesAssets: Record<string, IAsset[]> | null = null;\n\n /**\n * @constructor\n */\n protected constructor(\n config: ServerConfig,\n { buildDir, viteAliases, basename, renderBuiltUrl }: ISsrManifestParams = {},\n ) {\n this.config = config;\n this.root = config.getParams().root;\n this.buildDir = buildDir;\n this.viteAliases = viteAliases ?? config.getVite()?.config?.resolve.alias;\n this.pathNormalize = new PathNormalize(config, viteAliases);\n this.basename = basename;\n this.renderBuiltUrl = renderBuiltUrl;\n }\n\n /**\n * Get singleton instance\n */\n public static get(config: ServerConfig, params: ISsrManifestParams = {}): SsrManifest {\n if (SsrManifest.instance === null) {\n SsrManifest.instance = new SsrManifest(config, params);\n }\n\n return SsrManifest.instance;\n }\n\n /**\n * Get output dir\n */\n protected getOutDir() {\n return path.resolve(this.root, this.buildDir || '');\n }\n\n /**\n * Get assets manifest file name\n */\n protected getAssetsManifestFile(): string {\n return `${this.getOutDir()}/server/${this.assetsManifest}`;\n }\n\n /**\n * Load client ssr manifest\n */\n protected loadClientManifest(): IManifest {\n const clientManifestDir = path.resolve(this.root, `${this.buildDir || ''}/client/.vite`);\n const clientSsrManifest = `${clientManifestDir}/${this.manifestName}`;\n\n if (!fs.existsSync(clientSsrManifest)) {\n return {};\n }\n\n const result = JSON.parse(\n fs.readFileSync(clientSsrManifest, { encoding: 'utf-8' }),\n ) as IManifest;\n\n fs.rmSync(clientSsrManifest);\n\n // try to remove empty .vite dir\n if (fs.readdirSync(clientManifestDir).length === 0) {\n fs.rmSync(clientManifestDir, { recursive: true });\n }\n\n return result;\n }\n\n /**\n * Load assets manifest\n */\n protected loadAssetsManifest(): Record<string, IAsset[]> {\n if (this.routesAssets !== null) {\n return this.routesAssets;\n }\n\n const manifestFile = this.getAssetsManifestFile();\n\n if (!fs.existsSync(manifestFile)) {\n return {};\n }\n\n this.routesAssets = JSON.parse(fs.readFileSync(manifestFile, { encoding: 'utf-8' })) as Record<\n string,\n IAsset[]\n >;\n\n return this.routesAssets;\n }\n\n /**\n * Same as 'getAsyncRoutesIds' but for routes tree from 'ParseRoutes'\n */\n protected getRoutesTreeIds(\n routes: TRoutesTree[],\n index?: string,\n ): Record<string, string | undefined> {\n const result: Record<string, string | undefined> = {};\n\n routes.forEach((route, routeIndex) => {\n const routeId = [index, String(routeIndex)].filter(Boolean).join('-');\n\n if (route.import) {\n result[routeId] = this.pathNormalize.getAppPath(route.import);\n }\n\n if (route.children.length > 0) {\n Object.assign(result, this.getRoutesTreeIds(route.children, routeId));\n }\n });\n\n return result;\n }\n\n /**\n * Sort assets\n */\n protected sortAssets(assets: IAsset[]): IAsset[] {\n return assets.sort((a, b) =>\n a.weight === b.weight ? Number(a.isNested) - Number(b.isNested) : a.weight - b.weight,\n );\n }\n\n /**\n * Get recursive module assets\n */\n protected getRouteAssets(\n manifest: IManifest,\n module: IManifest[string],\n isNested = false,\n ): Record<string, IAsset> {\n const rootAssets = [...(module?.assets ?? []), ...(module?.css ?? []), module?.file];\n\n const assets = rootAssets.reduce(\n (res, asset) => {\n if (asset) {\n const type = this.getAssetType(asset);\n const isEntry = module.isEntry && module.file === asset;\n\n // keep only js,css,image,fonts files\n if (type) {\n const filename = path.posix.normalize(`${this.basename}/${asset}`);\n const modifiedFilename = this.renderBuiltUrl?.(filename, {\n type: 'asset',\n ssr: true,\n hostId: '',\n hostType: filename.split('.').at(-1)?.toLowerCase() as 'js',\n });\n\n res[asset] = {\n url: typeof modifiedFilename === 'string' ? modifiedFilename : filename,\n weight: isEntry ? 1.9 : this.getAssetWeight(asset),\n type,\n isNested,\n isPreload: !isEntry,\n };\n }\n }\n\n return res;\n },\n {} as Record<string, IAsset>,\n );\n\n // nested assets\n if (module?.imports?.length) {\n module.imports.forEach((nestedAsset) => {\n const nestedModule = manifest[nestedAsset];\n\n if (nestedModule) {\n Object.assign(assets, this.getRouteAssets(manifest, nestedModule, true));\n }\n });\n }\n\n return assets;\n }\n\n /**\n * Build routes manifest file\n */\n public buildRoutesManifest(): void {\n const manifest = this.loadClientManifest();\n const routesService = new ParseRoutes(this.config, this.viteAliases);\n const routesPaths = this.getRoutesTreeIds(routesService.parse());\n\n const postfixes = this.pathNormalize.getImportPostfix();\n const result: Record<string, IAsset[]> = {};\n\n // find route assets\n Object.entries(routesPaths).forEach(([routeId, routePath]) => {\n const routePostfix = postfixes.find((postfix) => {\n const filePath = `${routePath}${postfix}`;\n\n return manifest[filePath] !== undefined;\n });\n const routeFile = `${routePath}${routePostfix || ''}`;\n const routeMeta = manifest[routeFile];\n\n result[routeId] = this.sortAssets(Object.values(this.getRouteAssets(manifest, routeMeta)));\n });\n\n fs.writeFileSync(this.getAssetsManifestFile(), JSON.stringify(result, null, 2), {\n encoding: 'utf-8',\n });\n }\n\n /**\n * Get route assets\n */\n protected getAssets(routes?: RouterState['matches']): IAsset[] {\n if (this.config.getVite()) {\n return this.getAssetsDev(routes);\n }\n\n const routeIds = routes?.map(({ route }) => route.id).filter(Boolean) ?? [];\n\n if (!routeIds.length) {\n return [];\n }\n\n const routesAssets = this.loadAssetsManifest();\n\n return this.sortAssets(\n routeIds\n .map((routeId) => routesAssets[routeId])\n .flat()\n .filter(Boolean),\n );\n }\n\n /**\n * Get development route assets\n */\n protected getAssetsDev(routes?: RouterState['matches']): IAsset[] {\n const routeIds =\n (routes\n ?.map(({ route }) => this.pathNormalize.getAppPath((route as IAsyncRoute)?.pathId, true))\n .filter(Boolean) as string[]) ?? [];\n\n if (!routeIds.length) {\n return [];\n }\n\n let assets: TAssets = {};\n const postfixes = this.pathNormalize.getImportPostfix();\n const rootId = path.resolve(\n this.root,\n this.config.getPluginConfig()?.clientFile ?? 'client.ts',\n );\n\n [rootId, ...routeIds].forEach((moduleId) => {\n for (const ext of postfixes) {\n const module = this.config.getVite()?.moduleGraph.getModuleById(`${moduleId}${ext}`);\n\n if (module) {\n assets = { ...assets, ...this.getModuleAssets(module) };\n break;\n }\n }\n });\n\n return Object.values(assets);\n }\n\n /**\n * Get module assets\n */\n protected getModuleAssets(module?: ModuleNode, skipModules: Set<string> = new Set()): TAssets {\n if (!module?.clientImportedModules.size || skipModules.has(module.file!)) {\n return {};\n }\n\n let assets: TAssets = {};\n\n skipModules.add(module.file!);\n\n module.clientImportedModules.forEach((subModule) => {\n const { file, clientImportedModules, transformResult } = subModule;\n const ext = file?.split('.').at(-1);\n\n if (file && ext && ['css', 'scss'].includes(ext)) {\n // @TODO investigate better method?\n const code = transformResult?.code.match(/__vite__css\\s+=\\s+\"(?<css>.+)\"/)?.groups?.css;\n\n if (code) {\n try {\n assets[file] = {\n type: AssetType.style,\n url: file,\n weight: this.getAssetWeight(file),\n content: (JSON.parse(`{\"style\": \"${code}\"}`) as { style: string }).style,\n isNested: Boolean(skipModules.size),\n isPreload: false,\n };\n } catch {\n console.warn(chalk.yellowBright('Failed to parse style: ', file));\n }\n }\n } else if (clientImportedModules.size) {\n assets = {\n ...assets,\n ...this.getModuleAssets(subModule, skipModules),\n };\n }\n });\n\n return assets;\n }\n\n /**\n * Get asset weight\n */\n protected getAssetWeight(asset: string): number {\n const type = this.getAssetType(asset);\n\n switch (type) {\n case AssetType.style:\n return 1;\n\n case AssetType.script:\n return 2;\n\n default:\n return 3;\n }\n }\n\n /**\n * Get asset type\n */\n protected getAssetType(asset: string): AssetType | null {\n const ext = asset.split('.').at(-1)?.toLowerCase();\n\n switch (ext) {\n case 'css':\n case 'scss':\n return AssetType.style;\n\n case 'js':\n return AssetType.script;\n\n case 'svg':\n case 'jpg':\n case 'jpeg':\n case 'png':\n case 'webp':\n case 'gif':\n case 'ico':\n return AssetType.image;\n\n case 'ttf':\n case 'otf':\n case 'woff':\n case 'woff2':\n return AssetType.font;\n\n default:\n return null;\n }\n }\n\n /**\n * Write 103 Early Hits header\n */\n public writeEarlyHits(assets: IAsset[], socket: Socket): void {\n socket.write(`HTTP/1.1 103 Early Hints${CRLF}`);\n assets.forEach(({ type, url }) => {\n if (!type || !['style', 'script'].includes(type)) {\n return;\n }\n\n socket.write(`Link: <${url}>; rel=preload; as=${type}${CRLF}`);\n });\n socket.write(CRLF);\n }\n\n /**\n * Inject route assets to head html\n */\n public injectAssets({ routerContext, html, res, hasEarlyHints = false }: IRequestContext): void {\n const assets = this.getAssets(routerContext?.matches);\n const htmlAssets = assets\n .map(({ type, url, isPreload, content = '' }) => {\n switch (type) {\n case AssetType.style:\n return this.config.getVite()\n ? `<style data-vite-dev-id=\"${url}\">${content}</style>`\n : `<link rel=\"stylesheet\" href=\"${url}\">`;\n\n case AssetType.script:\n return isPreload\n ? this.config.isModulePreload\n ? // can reduce lighthouse performance\n `<link rel=\"modulepreload\" as=\"script\" crossorigin href=\"${url}\">`\n : null\n : `<script async type=\"module\" crossorigin src=\"${url}\"></script>`;\n }\n\n return null;\n })\n .filter(Boolean);\n\n html.header = html.header.replace('</head>', `${htmlAssets.join('\\n')}</head>`);\n\n if (hasEarlyHints && htmlAssets.length && res.socket) {\n this.writeEarlyHits(assets, res.socket);\n }\n }\n}\n\nexport default SsrManifest;\n"],"names":["AssetType","CRLF","SsrManifest","static","config","pathNormalize","root","buildDir","manifestName","assetsManifest","viteAliases","basename","renderBuiltUrl","routesAssets","constructor","this","getParams","getVite","resolve","alias","PathNormalize","params","instance","getOutDir","path","getAssetsManifestFile","loadClientManifest","clientManifestDir","clientSsrManifest","fs","existsSync","result","JSON","parse","readFileSync","encoding","rmSync","readdirSync","length","recursive","loadAssetsManifest","manifestFile","getRoutesTreeIds","routes","index","forEach","route","routeIndex","routeId","String","filter","Boolean","join","import","getAppPath","children","Object","assign","sortAssets","assets","sort","a","b","weight","Number","isNested","getRouteAssets","manifest","module","css","file","reduce","res","asset","type","getAssetType","isEntry","filename","posix","normalize","modifiedFilename","ssr","hostId","hostType","split","at","toLowerCase","url","getAssetWeight","isPreload","imports","nestedAsset","nestedModule","buildRoutesManifest","routesService","ParseRoutes","routesPaths","postfixes","getImportPostfix","entries","routePath","routePostfix","find","postfix","undefined","routeMeta","values","writeFileSync","stringify","getAssets","getAssetsDev","routeIds","map","id","flat","pathId","getPluginConfig","clientFile","moduleId","ext","moduleGraph","getModuleById","getModuleAssets","skipModules","Set","clientImportedModules","size","has","add","subModule","transformResult","includes","code","match","groups","style","content","console","warn","chalk","yellowBright","script","image","font","writeEarlyHits","socket","write","injectAssets","routerContext","html","hasEarlyHints","matches","htmlAssets","isModulePreload","header","replace"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ssr-manifest.js","sources":["../../src/services/ssr-manifest.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport type { RouterState, StaticHandlerContext } from 'react-router';\nimport type { Alias, ModuleNode, RenderBuiltAssetUrl } from 'vite';\nimport type { IAsyncRoute } from '@helpers/import-route';\nimport type { TRoutesTree } from '@services/parse-routes';\nimport ParseRoutes from '@services/parse-routes';\nimport PathNormalize from '@services/path-normalize';\nimport type ServerConfig from '@services/server-config';\n\ninterface ISsrManifestParams {\n buildDir?: string;\n viteAliases?: Alias[];\n basename?: string;\n renderBuiltUrl?: RenderBuiltAssetUrl;\n}\n\ninterface IManifest {\n [path: string]: {\n assets: string[];\n css: string[];\n file: string;\n isEntry?: boolean;\n imports: string[];\n };\n}\n\nenum AssetType {\n style = 'style',\n script = 'script',\n image = 'image',\n font = 'font',\n}\n\ninterface IAsset {\n type: AssetType;\n url: string;\n weight: number;\n isNested: boolean;\n isPreload: boolean;\n content?: string;\n}\n\ntype TAssets = { [id: string]: IAsset };\n\ninterface IInjectAssetsContext {\n html: {\n header: string;\n };\n routerContext?: StaticHandlerContext;\n}\n\n/**\n * Working with SSR Manifest file\n */\nclass SsrManifest {\n /**\n * Singleton\n */\n protected static instance: SsrManifest | null = null;\n\n /**\n * Server config\n */\n protected readonly config: ServerConfig;\n\n /**\n * Path normalize service\n */\n protected readonly pathNormalize: PathNormalize;\n\n /**\n * Project root path\n */\n protected readonly root: string;\n\n /**\n * Build dir\n */\n protected readonly buildDir?: string;\n\n /**\n * Client manifest file name\n */\n protected readonly manifestName = 'manifest.json';\n\n /**\n * Assets manifest file name\n */\n protected readonly assetsManifest = 'assets-manifest.json';\n\n /**\n * Vite resolve aliases\n */\n protected readonly viteAliases?: Alias[];\n\n /**\n * Vite base\n */\n protected readonly basename?: string;\n\n /**\n * Vite renderBuiltUrl config func\n */\n protected readonly renderBuiltUrl?: RenderBuiltAssetUrl;\n\n /**\n * Loaded assets manifest file\n */\n protected routesAssets: Record<string, IAsset[]> | null = null;\n\n /**\n * @constructor\n */\n protected constructor(\n config: ServerConfig,\n { buildDir, viteAliases, basename, renderBuiltUrl }: ISsrManifestParams = {},\n ) {\n this.config = config;\n this.root = config.getParams().root;\n this.buildDir = buildDir;\n this.viteAliases = viteAliases ?? config.getVite()?.config?.resolve.alias;\n this.pathNormalize = new PathNormalize(config, viteAliases);\n this.basename = basename;\n this.renderBuiltUrl = renderBuiltUrl;\n }\n\n /**\n * Get singleton instance\n */\n public static get(config: ServerConfig, params: ISsrManifestParams = {}): SsrManifest {\n if (SsrManifest.instance === null) {\n SsrManifest.instance = new SsrManifest(config, params);\n }\n\n return SsrManifest.instance;\n }\n\n /**\n * Get output dir\n */\n protected getOutDir() {\n return path.resolve(this.root, this.buildDir || '');\n }\n\n /**\n * Get assets manifest file name\n */\n protected getAssetsManifestFile(): string {\n return `${this.getOutDir()}/server/${this.assetsManifest}`;\n }\n\n /**\n * Load client ssr manifest\n */\n protected loadClientManifest(): IManifest {\n const clientManifestDir = path.resolve(this.root, `${this.buildDir || ''}/client/.vite`);\n const clientSsrManifest = `${clientManifestDir}/${this.manifestName}`;\n\n if (!fs.existsSync(clientSsrManifest)) {\n return {};\n }\n\n const result = JSON.parse(\n fs.readFileSync(clientSsrManifest, { encoding: 'utf-8' }),\n ) as IManifest;\n\n fs.rmSync(clientSsrManifest);\n\n // try to remove empty .vite dir\n if (fs.readdirSync(clientManifestDir).length === 0) {\n fs.rmSync(clientManifestDir, { recursive: true });\n }\n\n return result;\n }\n\n /**\n * Load assets manifest\n */\n protected loadAssetsManifest(): Record<string, IAsset[]> {\n if (this.routesAssets !== null) {\n return this.routesAssets;\n }\n\n const manifestFile = this.getAssetsManifestFile();\n\n if (!fs.existsSync(manifestFile)) {\n return {};\n }\n\n this.routesAssets = JSON.parse(fs.readFileSync(manifestFile, { encoding: 'utf-8' })) as Record<\n string,\n IAsset[]\n >;\n\n return this.routesAssets;\n }\n\n /**\n * Same as 'getAsyncRoutesIds' but for routes tree from 'ParseRoutes'\n */\n protected getRoutesTreeIds(\n routes: TRoutesTree[],\n index?: string,\n ): Record<string, string | undefined> {\n const result: Record<string, string | undefined> = {};\n\n routes.forEach((route, routeIndex) => {\n const routeId = [index, String(routeIndex)].filter(Boolean).join('-');\n\n if (route.import) {\n result[routeId] = this.pathNormalize.getAppPath(route.import);\n }\n\n if (route.children.length > 0) {\n Object.assign(result, this.getRoutesTreeIds(route.children, routeId));\n }\n });\n\n return result;\n }\n\n /**\n * Sort assets\n */\n protected sortAssets(assets: IAsset[]): IAsset[] {\n return assets.sort((a, b) =>\n a.weight === b.weight ? Number(a.isNested) - Number(b.isNested) : a.weight - b.weight,\n );\n }\n\n /**\n * Get recursive module assets\n */\n protected getRouteAssets(\n manifest: IManifest,\n module: IManifest[string],\n isNested = false,\n ): Record<string, IAsset> {\n const rootAssets = [...(module?.assets ?? []), ...(module?.css ?? []), module?.file];\n\n const assets = rootAssets.reduce(\n (res, asset) => {\n if (asset) {\n const type = this.getAssetType(asset);\n const isEntry = module.isEntry && module.file === asset;\n\n // keep only js,css,image,fonts files\n if (type) {\n const filename = path.posix.normalize(`${this.basename}/${asset}`);\n const modifiedFilename = this.renderBuiltUrl?.(filename, {\n type: 'asset',\n ssr: true,\n hostId: '',\n hostType: filename.split('.').at(-1)?.toLowerCase() as 'js',\n });\n\n res[asset] = {\n url: typeof modifiedFilename === 'string' ? modifiedFilename : filename,\n weight: isEntry ? 1.9 : this.getAssetWeight(asset),\n type,\n isNested,\n isPreload: !isEntry,\n };\n }\n }\n\n return res;\n },\n {} as Record<string, IAsset>,\n );\n\n // nested assets\n if (module?.imports?.length) {\n module.imports.forEach((nestedAsset) => {\n const nestedModule = manifest[nestedAsset];\n\n if (nestedModule) {\n Object.assign(assets, this.getRouteAssets(manifest, nestedModule, true));\n }\n });\n }\n\n return assets;\n }\n\n /**\n * Build routes manifest file\n */\n public buildRoutesManifest(): void {\n const manifest = this.loadClientManifest();\n const routesService = new ParseRoutes(this.config, this.viteAliases);\n const routesPaths = this.getRoutesTreeIds(routesService.parse());\n\n const postfixes = this.pathNormalize.getImportPostfix();\n const result: Record<string, IAsset[]> = {};\n\n // find route assets\n Object.entries(routesPaths).forEach(([routeId, routePath]) => {\n const routePostfix = postfixes.find((postfix) => {\n const filePath = `${routePath}${postfix}`;\n\n return manifest[filePath] !== undefined;\n });\n const routeFile = `${routePath}${routePostfix || ''}`;\n const routeMeta = manifest[routeFile];\n\n result[routeId] = this.sortAssets(Object.values(this.getRouteAssets(manifest, routeMeta)));\n });\n\n fs.writeFileSync(this.getAssetsManifestFile(), JSON.stringify(result, null, 2), {\n encoding: 'utf-8',\n });\n }\n\n /**\n * Get route assets\n */\n protected getAssets(routes?: RouterState['matches']): IAsset[] {\n if (this.config.getVite()) {\n return this.getAssetsDev(routes);\n }\n\n const routeIds = routes?.map(({ route }) => route.id).filter(Boolean) ?? [];\n\n if (!routeIds.length) {\n return [];\n }\n\n const routesAssets = this.loadAssetsManifest();\n\n return this.sortAssets(\n routeIds\n .map((routeId) => routesAssets[routeId])\n .flat()\n .filter(Boolean),\n );\n }\n\n /**\n * Get development route assets\n */\n protected getDevModules(routes?: RouterState['matches']): ModuleNode[] {\n const routeIds =\n (routes\n ?.map(({ route }) => this.pathNormalize.getAppPath((route as IAsyncRoute)?.pathId, true))\n .filter(Boolean) as string[]) ?? [];\n\n const postfixes = this.pathNormalize.getImportPostfix();\n const pluginConfig = this.config.getPluginConfig();\n const rootIds = [\n pluginConfig?.clientFile ?? 'client.ts',\n pluginConfig?.serverFile ?? 'server.ts',\n ].map((file) => path.resolve(this.root, file));\n const modules: ModuleNode[] = [];\n\n [...rootIds, ...routeIds].forEach((moduleId) => {\n for (const ext of postfixes) {\n const module = this.config.getVite()?.moduleGraph.getModuleById(`${moduleId}${ext}`);\n\n if (module) {\n modules.push(module);\n break;\n }\n }\n });\n\n return modules;\n }\n\n /**\n * Compile SSR graph styles before the browser has populated the client graph.\n */\n public async prepareDevAssets(routes?: RouterState['matches']): Promise<void> {\n const vite = this.config.getVite();\n\n if (!vite) {\n return;\n }\n\n const visited = new Set<string>();\n\n /**\n * Transform each stylesheet once, including dependencies shared by several routes.\n */\n const visit = async (module: ModuleNode): Promise<void> => {\n if (visited.has(module.url)) {\n return;\n }\n\n visited.add(module.url);\n\n if (module.file && /\\.(?:css|less|s[ac]ss|styl(?:us)?|pcss|postcss)$/.test(module.file)) {\n await vite.transformRequest(module.url);\n }\n\n await Promise.all([...module.importedModules].map(visit));\n };\n\n await Promise.all(this.getDevModules(routes).map(visit));\n }\n\n /**\n * Collect development assets from the current server and client module graphs.\n */\n protected getAssetsDev(routes?: RouterState['matches']): IAsset[] {\n const assets = Object.assign(\n {},\n ...this.getDevModules(routes).map((module) => this.getModuleAssets(module)),\n ) as TAssets;\n\n return Object.values(assets);\n }\n\n /**\n * Get module assets\n */\n protected getModuleAssets(module?: ModuleNode, skipModules: Set<string> = new Set()): TAssets {\n const imports = module?.importedModules ?? module?.clientImportedModules;\n\n if (!imports?.size || skipModules.has(module!.file!)) {\n return {};\n }\n\n let assets: TAssets = {};\n\n skipModules.add(module!.file!);\n\n imports.forEach((subModule) => {\n const { file, transformResult } = subModule;\n const ext = file?.split('.').at(-1);\n\n if (\n file &&\n ext &&\n ['css', 'scss', 'sass', 'less', 'styl', 'stylus', 'pcss', 'postcss'].includes(ext)\n ) {\n // @TODO investigate better method?\n const code = transformResult?.code.match(/__vite__css\\s+=\\s+\"(?<css>.+)\"/)?.groups?.css;\n\n if (code) {\n try {\n assets[file] = {\n type: AssetType.style,\n url: file,\n weight: this.getAssetWeight(file),\n content: (JSON.parse(`{\"style\": \"${code}\"}`) as { style: string }).style,\n isNested: Boolean(skipModules.size),\n isPreload: false,\n };\n } catch {\n console.warn(chalk.yellowBright('Failed to parse style: ', file));\n }\n }\n } else {\n assets = {\n ...assets,\n ...this.getModuleAssets(subModule, skipModules),\n };\n }\n });\n\n return assets;\n }\n\n /**\n * Get asset weight\n */\n protected getAssetWeight(asset: string): number {\n const type = this.getAssetType(asset);\n\n switch (type) {\n case AssetType.style:\n return 1;\n\n case AssetType.script:\n return 2;\n\n default:\n return 3;\n }\n }\n\n /**\n * Get asset type\n */\n protected getAssetType(asset: string): AssetType | null {\n const ext = asset.split('.').at(-1)?.toLowerCase();\n\n switch (ext) {\n case 'css':\n case 'scss':\n return AssetType.style;\n\n case 'js':\n return AssetType.script;\n\n case 'svg':\n case 'jpg':\n case 'jpeg':\n case 'png':\n case 'webp':\n case 'gif':\n case 'ico':\n return AssetType.image;\n\n case 'ttf':\n case 'otf':\n case 'woff':\n case 'woff2':\n return AssetType.font;\n\n default:\n return null;\n }\n }\n\n /**\n * Build preload hints without depending on a particular HTTP transport.\n */\n public getEarlyHints(assets: IAsset[]): Headers {\n const headers = new Headers();\n\n assets.forEach(({ type, url }) => {\n if (!type || !['style', 'script'].includes(type)) {\n return;\n }\n\n headers.append('Link', `<${url}>; rel=preload; as=${type}`);\n });\n\n return headers;\n }\n\n /**\n * Inject route assets to head html\n */\n public injectAssets({ routerContext, html }: IInjectAssetsContext): Headers {\n const assets = this.getAssets(routerContext?.matches);\n const htmlAssets = assets\n .map(({ type, url, isPreload, content = '' }) => {\n switch (type) {\n case AssetType.style:\n return this.config.getVite()\n ? `<style data-vite-dev-id=\"${url}\">${content}</style>`\n : `<link rel=\"stylesheet\" href=\"${url}\">`;\n\n case AssetType.script:\n return isPreload\n ? this.config.isModulePreload\n ? // can reduce lighthouse performance\n `<link rel=\"modulepreload\" as=\"script\" crossorigin href=\"${url}\">`\n : null\n : `<script async type=\"module\" crossorigin src=\"${url}\"></script>`;\n }\n\n return null;\n })\n .filter(Boolean);\n\n html.header = html.header.replace('</head>', `${htmlAssets.join('\\n')}</head>`);\n\n return this.getEarlyHints(assets);\n }\n}\n\nexport default SsrManifest;\n"],"names":["AssetType","SsrManifest","static","config","pathNormalize","root","buildDir","manifestName","assetsManifest","viteAliases","basename","renderBuiltUrl","routesAssets","constructor","this","getParams","getVite","resolve","alias","PathNormalize","params","instance","getOutDir","path","getAssetsManifestFile","loadClientManifest","clientManifestDir","clientSsrManifest","fs","existsSync","result","JSON","parse","readFileSync","encoding","rmSync","readdirSync","length","recursive","loadAssetsManifest","manifestFile","getRoutesTreeIds","routes","index","forEach","route","routeIndex","routeId","String","filter","Boolean","join","import","getAppPath","children","Object","assign","sortAssets","assets","sort","a","b","weight","Number","isNested","getRouteAssets","manifest","module","css","file","reduce","res","asset","type","getAssetType","isEntry","filename","posix","normalize","modifiedFilename","ssr","hostId","hostType","split","at","toLowerCase","url","getAssetWeight","isPreload","imports","nestedAsset","nestedModule","buildRoutesManifest","routesService","ParseRoutes","routesPaths","postfixes","getImportPostfix","entries","routePath","routePostfix","find","postfix","undefined","routeMeta","values","writeFileSync","stringify","getAssets","getAssetsDev","routeIds","map","id","flat","getDevModules","pathId","pluginConfig","getPluginConfig","rootIds","clientFile","serverFile","modules","moduleId","ext","moduleGraph","getModuleById","push","async","vite","visited","Set","visit","has","add","test","transformRequest","Promise","all","importedModules","getModuleAssets","skipModules","clientImportedModules","size","subModule","transformResult","includes","code","match","groups","style","content","console","warn","chalk","yellowBright","script","image","font","getEarlyHints","headers","Headers","append","injectAssets","routerContext","html","matches","htmlAssets","isModulePreload","header","replace"],"mappings":"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"}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { Response as ExpressResponse } from 'express';
|
|
2
|
-
import { StaticHandlerContext } from 'react-router';
|
|
3
|
-
/**
|
|
4
|
-
* Handle router or server context response
|
|
5
|
-
*/
|
|
6
|
-
declare const handleResponse: (res: ExpressResponse, response: Response | StaticHandlerContext | null, defaultStatus?: number) => number | undefined;
|
|
7
|
-
export { handleResponse as default };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"handle-response.js","sources":["../../src/helpers/handle-response.ts"],"sourcesContent":["import type { Response as ExpressResponse } from 'express';\nimport type { StaticHandlerContext } from 'react-router';\n\n/**\n * Handle router or server context response\n */\nconst handleResponse = (\n res: ExpressResponse,\n response: Response | StaticHandlerContext | null,\n defaultStatus = 200,\n): number | undefined => {\n if (!(response instanceof Response)) {\n return defaultStatus;\n }\n\n // redirect\n if (response.status >= 300 && response.status < 400) {\n res.redirect(response.status, response.headers.get('Location')!);\n\n return;\n }\n\n return response.status ?? defaultStatus;\n};\n\nexport default handleResponse;\n"],"names":["handleResponse","res","response","defaultStatus","Response","status","redirect","headers","get"],"mappings":"AAMA,MAAMA,EAAiB,CACrBC,EACAC,EACAC,EAAgB,MAEVD,aAAoBE,SAKtBF,EAASG,QAAU,KAAOH,EAASG,OAAS,SAC9CJ,EAAIK,SAASJ,EAASG,OAAQH,EAASK,QAAQC,IAAI,aAK9CN,EAASG,QAAUF,EAVjBA"}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { Request as ExpressRequest } from 'express';
|
|
2
|
-
/**
|
|
3
|
-
* Convert the incoming Express request into a Fetch request, which is what the static handler methods operate on.
|
|
4
|
-
* @see https://reactrouter.com/en/main/guides/ssr
|
|
5
|
-
*/
|
|
6
|
-
declare function createFetchRequest(req: ExpressRequest): Request;
|
|
7
|
-
export { createFetchRequest as default };
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
function e(e){const o=`${e.protocol}://${e.get("host")}`,t=new URL(e.originalUrl||e.url,o),r=new AbortController;e.on("close",(()=>r.abort()));const n=new Headers;for(const[o,t]of Object.entries(e.headers))if(t)if(Array.isArray(t))for(const e of t)n.append(o,e);else n.set(o,t);const s={method:e.method,headers:n,signal:r.signal,body:void 0};return"GET"!==e.method&&"HEAD"!==e.method&&(s.body=e.body),new Request(t.href,s)}export{e as default};
|
|
2
|
-
//# sourceMappingURL=create-fetch-request.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"create-fetch-request.js","sources":["../../src/node/create-fetch-request.ts"],"sourcesContent":["import type { Request as ExpressRequest } from 'express';\n\n/**\n * Convert the incoming Express request into a Fetch request, which is what the static handler methods operate on.\n * @see https://reactrouter.com/en/main/guides/ssr\n */\nfunction createFetchRequest(req: ExpressRequest): Request {\n const origin = `${req.protocol}://${req.get('host') as string}`;\n // Note: This had to take originalUrl into account for presumably vite's proxying\n const url = new URL(req.originalUrl || req.url, origin);\n const controller = new AbortController();\n\n req.on('close', () => controller.abort());\n\n const headers = new Headers();\n\n for (const [key, values] of Object.entries(req.headers)) {\n if (values) {\n if (Array.isArray(values)) {\n for (const value of values) {\n headers.append(key, value);\n }\n } else {\n headers.set(key, values);\n }\n }\n }\n\n const init: RequestInit = {\n method: req.method,\n headers,\n signal: controller.signal,\n body: undefined,\n };\n\n if (req.method !== 'GET' && req.method !== 'HEAD') {\n init.body = req.body as BodyInit;\n }\n\n return new Request(url.href, init);\n}\n\nexport default createFetchRequest;\n"],"names":["createFetchRequest","req","origin","protocol","get","url","URL","originalUrl","controller","AbortController","on","abort","headers","Headers","key","values","Object","entries","Array","isArray","value","append","set","init","method","signal","body","undefined","Request","href"],"mappings":"AAMA,SAASA,EAAmBC,GAC1B,MAAMC,EAAS,GAAGD,EAAIE,cAAcF,EAAIG,IAAI,UAEtCC,EAAM,IAAIC,IAAIL,EAAIM,aAAeN,EAAII,IAAKH,GAC1CM,EAAa,IAAIC,gBAEvBR,EAAIS,GAAG,SAAS,IAAMF,EAAWG,UAEjC,MAAMC,EAAU,IAAIC,QAEpB,IAAK,MAAOC,EAAKC,KAAWC,OAAOC,QAAQhB,EAAIW,SAC7C,GAAIG,EACF,GAAIG,MAAMC,QAAQJ,GAChB,IAAK,MAAMK,KAASL,EAClBH,EAAQS,OAAOP,EAAKM,QAGtBR,EAAQU,IAAIR,EAAKC,GAKvB,MAAMQ,EAAoB,CACxBC,OAAQvB,EAAIuB,OACZZ,UACAa,OAAQjB,EAAWiB,OACnBC,UAAMC,GAOR,MAJmB,QAAf1B,EAAIuB,QAAmC,SAAfvB,EAAIuB,SAC9BD,EAAKG,KAAOzB,EAAIyB,MAGX,IAAIE,QAAQvB,EAAIwB,KAAMN,EAC/B"}
|
package/node/entry.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"entry.js","sources":["../../src/node/entry.tsx"],"sourcesContent":["import type { Server } from 'node:net';\nimport type { CompressionOptions } from 'compression';\nimport type { Express, Request, Response as ExpressResponse } from 'express';\nimport type { FC, PropsWithChildren } from 'react';\nimport type { RouteObject } from 'react-router';\nimport { createStaticHandler } from 'react-router';\nimport type { ServeStaticOptions } from 'serve-static';\nimport type { Logger } from 'vite';\nimport type { TRouteObject } from '@interfaces/route-object';\nimport type { IRenderOptions, IRenderParams, TRender } from '@node/render';\nimport render from '@node/render';\nimport type ServerApi from '@services/server-api';\nimport type ServerConfig from '@services/server-config';\n\nexport interface IInitServerRequestOut<T = Record<string, any>> {\n appProps?: T;\n hasEarlyHints?: boolean;\n shouldSkip?: boolean;\n shouldCancel?: boolean;\n}\n\nexport interface IEntrypointOptions<TAppProps = Record<string, any>> {\n onServerCreated?: (app: Express, serverApi: ServerApi) => Promise<void> | void;\n onServerStarted?: (app: Express, serverApi: ServerApi, server: Server) => Promise<void> | void;\n onRequest?: (\n req: Request,\n res: ExpressResponse,\n ) => Promise<IInitServerRequestOut<TAppProps>> | IInitServerRequestOut<TAppProps>;\n onRouterReady?: IRenderOptions<TAppProps>['onRouterReady'];\n onShellReady?: IRenderOptions<TAppProps>['onShellReady'];\n onShellError?: IRenderOptions<TAppProps>['onShellError'];\n onResponse?: IRenderOptions<TAppProps>['onResponse'];\n onError?: IRenderOptions<TAppProps>['onError'];\n getState?: IRenderOptions<TAppProps>['getState'];\n}\n\nexport interface IPrepareRenderOut<TAppProps = Record<string, any>> {\n render: TRender;\n init: IEntryServerOptions<TAppProps>['init'];\n routes: TRouteObject[];\n abortDelay?: number;\n loggerProd?: Logger;\n loggerDev?: Logger;\n middlewares?: {\n compression?: CompressionOptions | false;\n // basename should be same as vite 'base' config\n expressStatic?: (ServeStaticOptions & { basename?: string }) | false;\n };\n}\n\nexport interface IAppServerProps<T = Record<string, any>> {\n server: T;\n}\n\nexport type TApp<T> = FC<PropsWithChildren<Record<string, any> & IAppServerProps<T>>>;\n\nexport interface IEntryServerOptions<TAppProps = Record<string, any>> {\n abortDelay?: number;\n init?: (params: {\n config: ServerConfig;\n }) => IEntrypointOptions<TAppProps> | Promise<IEntrypointOptions<TAppProps>>;\n loggerProd?: IPrepareRenderOut['loggerProd'];\n loggerDev?: IPrepareRenderOut['loggerDev'];\n middlewares?: IPrepareRenderOut['middlewares'];\n routerOptions?: Parameters<typeof createStaticHandler>[1];\n}\n\n/**\n * Render server side application\n */\nfunction entry<TAppProps>(\n App: TApp<TAppProps>,\n routes: TRouteObject[],\n { init, routerOptions, ...rest }: IEntryServerOptions<TAppProps> = {},\n): IPrepareRenderOut<TAppProps> {\n const handler = createStaticHandler(routes as RouteObject[], routerOptions);\n\n return {\n render: render.bind(null, { handler, App } as IRenderParams<TAppProps>) as TRender,\n init,\n routes,\n ...rest,\n };\n}\n\nexport default entry;\n"],"names":["entry","App","routes","init","routerOptions","rest","handler","createStaticHandler","render","bind"],"mappings":"8EAsEA,SAASA,EACPC,EACAC,GACAC,KAAEA,EAAIC,cAAEA,KAAkBC,GAAyC,IAEnE,MAAMC,EAAUC,EAAoBL,EAAyBE,GAE7D,MAAO,CACLI,OAAQA,EAAOC,KAAK,KAAM,CAAEH,UAASL,QACrCE,OACAD,YACGG,EAEP"}
|
package/node/render.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import e from"chalk";import r from"react";import{renderToPipeableStream as t}from"react-dom/server";import{createStaticRouter as o,StaticRouterProvider as n}from"react-router";import s from"../constants/stream-error.js";import{ServerProvider as a}from"../context/server.js";import m from"../helpers/handle-response.js";import i from"../helpers/obtain-stream-error.js";import c from"./create-fetch-request.js";import d from"./write-response.js";import l from"../services/ssr-manifest.js";async function p({App:p,handler:u},f,h,{onRouterReady:x,onShellReady:S,onResponse:R,onShellError:g,onError:y,getState:C,abortDelay:E=15e3}){const{req:j,res:b}=h,v=c(j);h.routerContext=await u.query(v,{requestContext:h});const w=m(b,h.routerContext);if(!w)return;l.get(f).injectAssets(h);const{isStream:q=!0}=await(x?.({context:h}))??{};h.isStream=q,h.serverContext={response:null,isServer:!0,basename:h.routerContext?.basename};const T=o(u.dataRoutes,h.routerContext),A=b.write.bind(b),B=f.getLogger();let $;b.write=(e,...r)=>{const t="string"==typeof e,o=t?e:Buffer.from(e).toString(),n=R?.({context:h,html:o});return n?A(t?n:Buffer.from(n),...r):A(e,...r)};const{serverContext:k,routerContext:D,appProps:H}=h,{pipe:L,abort:P}=t(r.createElement(a,{context:k},r.createElement(p,{server:{...H,req:j}},r.createElement(n,{router:T,context:D,hydrate:!1}))),{onShellReady(){q&&d(h,{pipe:L,statusCode:w,onShellReady:S,getState:C})},onAllReady(){clearTimeout($),q||d(h,{pipe:L,statusCode:w,onShellReady:S,getState:C})},onShellError(e){const r=g?.({context:h,error:e})||`<!doctype html><p>Something went wrong: ${e.message}</p>`;b.status(500),b.setHeader("content-type","text/html"),b.send(r)},onError(r){clearTimeout($);const t=i(r),{code:o,message:n}=t,{didError:a}=h;h.didError=a??o,y?.({context:h,error:t}),B.info(e.red(`Stream error. Code: ${o}`)),[s.RenderAborted,s.RenderTimeout,s.RenderCancel].includes(o)?B.info(e.dim(n)):B.error(r)}});$=setTimeout((()=>{h.didError=s.RenderTimeout,P()}),E),j.on("close",(()=>{h.didError=s.RenderCancel,P()}))}export{p as default};
|
|
2
|
-
//# sourceMappingURL=render.js.map
|