@lomray/vite-ssr-boost 1.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/browser/entry.d.ts +20 -0
- package/browser/entry.js +2 -0
- package/browser/entry.js.map +1 -0
- package/cli/build.d.ts +11 -0
- package/cli/build.js +2 -0
- package/cli/build.js.map +1 -0
- package/cli/keyboard-input.d.ts +5 -0
- package/cli/keyboard-input.js +2 -0
- package/cli/keyboard-input.js.map +1 -0
- package/cli/run-dev.d.ts +17 -0
- package/cli/run-dev.js +2 -0
- package/cli/run-dev.js.map +1 -0
- package/cli/run-prod.d.ts +19 -0
- package/cli/run-prod.js +2 -0
- package/cli/run-prod.js.map +1 -0
- package/cli/vite-reset-cache.d.ts +5 -0
- package/cli/vite-reset-cache.js +2 -0
- package/cli/vite-reset-cache.js.map +1 -0
- package/cli.d.ts +2 -0
- package/cli.js +3 -0
- package/cli.js.map +1 -0
- package/components/navigate.d.ts +12 -0
- package/components/navigate.js +2 -0
- package/components/navigate.js.map +1 -0
- package/components/response-status.d.ts +11 -0
- package/components/response-status.js +2 -0
- package/components/response-status.js.map +1 -0
- package/constants/cli-actions.d.ts +7 -0
- package/constants/cli-actions.js +2 -0
- package/constants/cli-actions.js.map +1 -0
- package/constants/cli-context.d.ts +14 -0
- package/constants/cli-context.js +2 -0
- package/constants/cli-context.js.map +1 -0
- package/constants/cli-name.d.ts +2 -0
- package/constants/cli-name.js +2 -0
- package/constants/cli-name.js.map +1 -0
- package/constants/cli-shortcuts.d.ts +6 -0
- package/constants/cli-shortcuts.js +2 -0
- package/constants/cli-shortcuts.js.map +1 -0
- package/constants/common.d.ts +2 -0
- package/constants/common.js +2 -0
- package/constants/common.js.map +1 -0
- package/constants/plugin-name.d.ts +2 -0
- package/constants/plugin-name.js +2 -0
- package/constants/plugin-name.js.map +1 -0
- package/constants/stream-error.d.ts +10 -0
- package/constants/stream-error.js +2 -0
- package/constants/stream-error.js.map +1 -0
- package/context/server.d.ts +21 -0
- package/context/server.js +2 -0
- package/context/server.js.map +1 -0
- package/helpers/build-custom-state.d.ts +6 -0
- package/helpers/build-custom-state.js +2 -0
- package/helpers/build-custom-state.js.map +1 -0
- package/helpers/build-router-state.d.ts +6 -0
- package/helpers/build-router-state.js +2 -0
- package/helpers/build-router-state.js.map +1 -0
- package/helpers/handle-response.d.ts +7 -0
- package/helpers/handle-response.js +2 -0
- package/helpers/handle-response.js.map +1 -0
- package/helpers/obtain-stream-error.d.ts +11 -0
- package/helpers/obtain-stream-error.js +2 -0
- package/helpers/obtain-stream-error.js.map +1 -0
- package/helpers/plugin-config.d.ts +13 -0
- package/helpers/plugin-config.js +2 -0
- package/helpers/plugin-config.js.map +1 -0
- package/helpers/print-server-info.d.ts +11 -0
- package/helpers/print-server-info.js +2 -0
- package/helpers/print-server-info.js.map +1 -0
- package/helpers/print-server-urls.d.ts +8 -0
- package/helpers/print-server-urls.js +2 -0
- package/helpers/print-server-urls.js.map +1 -0
- package/helpers/process-stop.d.ts +5 -0
- package/helpers/process-stop.js +2 -0
- package/helpers/process-stop.js.map +1 -0
- package/helpers/resolve-server-urls.d.ts +15 -0
- package/helpers/resolve-server-urls.js +2 -0
- package/helpers/resolve-server-urls.js.map +1 -0
- package/helpers/serialize-errors.d.ts +7 -0
- package/helpers/serialize-errors.js +2 -0
- package/helpers/serialize-errors.js.map +1 -0
- package/node/create-fetch-request.d.ts +7 -0
- package/node/create-fetch-request.js +2 -0
- package/node/create-fetch-request.js.map +1 -0
- package/node/entry.d.ts +40 -0
- package/node/entry.js +2 -0
- package/node/entry.js.map +1 -0
- package/node/render.d.ts +64 -0
- package/node/render.js +2 -0
- package/node/render.js.map +1 -0
- package/node/server.d.ts +14 -0
- package/node/server.js +2 -0
- package/node/server.js.map +1 -0
- package/node/write-response.d.ts +14 -0
- package/node/write-response.js +2 -0
- package/node/write-response.js.map +1 -0
- package/package.json +76 -0
- package/plugin.d.ts +19 -0
- package/plugin.js +2 -0
- package/plugin.js.map +1 -0
- package/services/logger.d.ts +53 -0
- package/services/logger.js +2 -0
- package/services/logger.js.map +1 -0
- package/services/prepare-server.d.ts +74 -0
- package/services/prepare-server.js +2 -0
- package/services/prepare-server.js.map +1 -0
- package/services/server-config.d.ts +140 -0
- package/services/server-config.js +2 -0
- package/services/server-config.js.map +1 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Server } from 'node:net';
|
|
3
|
+
import { ResolvedServerUrls } from 'vite';
|
|
4
|
+
interface IResolveServerUrlsOptions {
|
|
5
|
+
host: string;
|
|
6
|
+
isHttps?: boolean;
|
|
7
|
+
rawBase?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Resolve server urls
|
|
11
|
+
* @see https://github.com/vitejs/vite/blob/main/packages/vite/src/node/utils.ts#L880
|
|
12
|
+
* vite not export this function
|
|
13
|
+
*/
|
|
14
|
+
declare function resolveServerUrls(server: Server, options: IResolveServerUrlsOptions): Promise<ResolvedServerUrls>;
|
|
15
|
+
export { resolveServerUrls as default };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{promises as s}from"node:dns";import o from"node:os";const t=new Set(["localhost","127.0.0.1","::1","0000:0000:0000:0000:0000:0000:0000:0001"]),a=new Set(["0.0.0.0","::","0000:0000:0000:0000:0000:0000:0000:0000"]);async function e(o){let t;t=void 0===o||!1===o?"localhost":!0===o?void 0:o;let e=void 0===t||a.has(t)?"localhost":t;if("localhost"===t){const o=await async function(){const[o,t]=await Promise.all([s.lookup("localhost"),s.lookup("localhost",{verbatim:!0})]);return o.family===t.family&&o.address===t.address?void 0:o.address}();o&&(e=o)}return{host:t,name:e}}async function l(s,l){const n=s.address();if("object"!=typeof(r=n)||!Boolean(r?.address))return{local:[],network:[]};var r;const{host:c,isHttps:d,rawBase:i}=l,h=[],f=[],u=await e(c),p=d?"https":"http",{port:m}=n,$="./"!==i&&""!==i&&i?i:"/";if(void 0===u.host||a.has(u.host))Object.values(o.networkInterfaces()).flatMap((s=>s??[])).filter((s=>s&&s.address&&("IPv4"===s.family||4===s.family))).forEach((s=>{let o=s.address.replace("127.0.0.1",u.name);o.includes(":")&&(o=`[${o}]`);const t=`${p}://${o}:${m}${$}`;s.address.includes("127.0.0.1")?h.push(t):f.push(t)}));else{let s=u.name;s.includes(":")&&(s=`[${s}]`);const o=`${p}://${s}:${m}${$}`;t.has(u.host)?h.push(o):f.push(o)}return{local:h,network:f}}export{l as default};
|
|
2
|
+
//# sourceMappingURL=resolve-server-urls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-server-urls.js","sources":["../../src/helpers/resolve-server-urls.ts"],"sourcesContent":["import { promises as dns } from 'node:dns';\nimport type { AddressInfo, Server } from 'node:net';\nimport os from 'node:os';\nimport type { ResolvedServerUrls } from 'vite';\n\ninterface IHostname {\n host: string | undefined;\n name: string;\n}\n\nconst loopbackHosts = new Set([\n 'localhost',\n '127.0.0.1',\n '::1',\n '0000:0000:0000:0000:0000:0000:0000:0001',\n]);\n\nconst wildcardHosts = new Set(['0.0.0.0', '::', '0000:0000:0000:0000:0000:0000:0000:0000']);\n\n/**\n * @see https://github.com/vitejs/vite/blob/main/packages/vite/src/node/utils.ts#LL819C8-L830C2\n */\nasync function getLocalhostAddressIfDiffersFromDNS(): Promise<string | undefined> {\n const [nodeResult, dnsResult] = await Promise.all([\n dns.lookup('localhost'),\n dns.lookup('localhost', { verbatim: true }),\n ]);\n const isSame = nodeResult.family === dnsResult.family && nodeResult.address === dnsResult.address;\n\n return isSame ? undefined : nodeResult.address;\n}\n\n/**\n * Resolve hostname\n * @see https://github.com/vitejs/vite/blob/main/packages/vite/src/node/utils.ts#LL852C1-L878C2\n * vite not export this function\n */\nasync function resolveHostname(optionsHost: string | boolean | undefined): Promise<IHostname> {\n let host: string | undefined;\n\n if (optionsHost === undefined || optionsHost === false) {\n // Use a secure default\n host = 'localhost';\n } else if (optionsHost === true) {\n // If passed --host in the CLI without arguments\n host = undefined; // undefined typically means 0.0.0.0 or :: (listen on all IPs)\n } else {\n host = optionsHost;\n }\n\n // Set host name to localhost when possible\n let name = host === undefined || wildcardHosts.has(host) ? 'localhost' : host;\n\n if (host === 'localhost') {\n // See #8647 for more details.\n const localhostAddr = await getLocalhostAddressIfDiffersFromDNS();\n\n if (localhostAddr) {\n name = localhostAddr;\n }\n }\n\n return { host, name };\n}\n\ninterface IResolveServerUrlsOptions {\n host: string;\n isHttps?: boolean;\n rawBase?: string;\n}\n\n/**\n * Resolve server urls\n * @see https://github.com/vitejs/vite/blob/main/packages/vite/src/node/utils.ts#L880\n * vite not export this function\n */\nasync function resolveServerUrls(\n server: Server,\n options: IResolveServerUrlsOptions,\n): Promise<ResolvedServerUrls> {\n const address = server.address();\n\n const isAddressInfo = (x: AddressInfo | null | string): x is AddressInfo =>\n (typeof x === 'object' && Boolean(x?.address)) || false;\n\n if (!isAddressInfo(address)) {\n return { local: [], network: [] };\n }\n\n const { host, isHttps, rawBase } = options;\n\n const local: string[] = [];\n const network: string[] = [];\n const hostname = await resolveHostname(host);\n const protocol = isHttps ? 'https' : 'http';\n const { port } = address;\n const base = rawBase === './' || rawBase === '' || !rawBase ? '/' : rawBase;\n\n if (hostname.host !== undefined && !wildcardHosts.has(hostname.host)) {\n let hostnameName = hostname.name;\n\n // ipv6 host\n if (hostnameName.includes(':')) {\n hostnameName = `[${hostnameName}]`;\n }\n\n const addressUrl = `${protocol}://${hostnameName}:${port}${base}`;\n\n if (loopbackHosts.has(hostname.host)) {\n local.push(addressUrl);\n } else {\n network.push(addressUrl);\n }\n } else {\n Object.values(os.networkInterfaces())\n .flatMap((nInterface) => nInterface ?? [])\n .filter(\n (detail) =>\n detail &&\n detail.address &&\n (detail.family === 'IPv4' ||\n // @ts-expect-error Node 18.0 - 18.3 returns number\n detail.family === 4),\n )\n .forEach((detail) => {\n let resultHost = detail.address.replace('127.0.0.1', hostname.name);\n\n // ipv6 host\n if (resultHost.includes(':')) {\n resultHost = `[${resultHost}]`;\n }\n\n const url = `${protocol}://${resultHost}:${port}${base}`;\n\n if (detail.address.includes('127.0.0.1')) {\n local.push(url);\n } else {\n network.push(url);\n }\n });\n }\n\n return { local, network };\n}\n\nexport default resolveServerUrls;\n"],"names":["loopbackHosts","Set","wildcardHosts","async","resolveHostname","optionsHost","host","undefined","name","has","localhostAddr","nodeResult","dnsResult","Promise","all","dns","lookup","verbatim","family","address","getLocalhostAddressIfDiffersFromDNS","resolveServerUrls","server","options","x","Boolean","local","network","isHttps","rawBase","hostname","protocol","port","base","Object","values","os","networkInterfaces","flatMap","nInterface","filter","detail","forEach","resultHost","replace","includes","url","push","hostnameName","addressUrl"],"mappings":"2DAUA,MAAMA,EAAgB,IAAIC,IAAI,CAC5B,YACA,YACA,MACA,4CAGIC,EAAgB,IAAID,IAAI,CAAC,UAAW,KAAM,4CAoBhDE,eAAeC,EAAgBC,GAC7B,IAAIC,EAIFA,OAFkBC,IAAhBF,IAA6C,IAAhBA,EAExB,aACkB,IAAhBA,OAEFE,EAEAF,EAIT,IAAIG,OAAgBD,IAATD,GAAsBJ,EAAcO,IAAIH,GAAQ,YAAcA,EAEzE,GAAa,cAATA,EAAsB,CAExB,MAAMI,QAjCVP,iBACE,MAAOQ,EAAYC,SAAmBC,QAAQC,IAAI,CAChDC,EAAIC,OAAO,aACXD,EAAIC,OAAO,YAAa,CAAEC,UAAU,MAItC,OAFeN,EAAWO,SAAWN,EAAUM,QAAUP,EAAWQ,UAAYP,EAAUO,aAE1EZ,EAAYI,EAAWQ,OACzC,CAyBgCC,GAExBV,IACFF,EAAOE,EAEV,CAED,MAAO,CAAEJ,OAAME,OACjB,CAaAL,eAAekB,EACbC,EACAC,GAEA,MAAMJ,EAAUG,EAAOH,UAKvB,GAFgB,iBADOK,EAGJL,KAFSM,QAAQD,GAAGL,SAGrC,MAAO,CAAEO,MAAO,GAAIC,QAAS,IAJT,IAACH,EAOvB,MAAMlB,KAAEA,EAAIsB,QAAEA,EAAOC,QAAEA,GAAYN,EAE7BG,EAAkB,GAClBC,EAAoB,GACpBG,QAAiB1B,EAAgBE,GACjCyB,EAAWH,EAAU,QAAU,QAC/BI,KAAEA,GAASb,EACXc,EAAmB,OAAZJ,GAAgC,KAAZA,GAAmBA,EAAgBA,EAAN,IAE9D,QAAsBtB,IAAlBuB,EAASxB,MAAuBJ,EAAcO,IAAIqB,EAASxB,MAgB7D4B,OAAOC,OAAOC,EAAGC,qBACdC,SAASC,GAAeA,GAAc,KACtCC,QACEC,GACCA,GACAA,EAAOtB,UACY,SAAlBsB,EAAOvB,QAEY,IAAlBuB,EAAOvB,UAEZwB,SAASD,IACR,IAAIE,EAAaF,EAAOtB,QAAQyB,QAAQ,YAAad,EAAStB,MAG1DmC,EAAWE,SAAS,OACtBF,EAAa,IAAIA,MAGnB,MAAMG,EAAM,GAAGf,OAAcY,KAAcX,IAAOC,IAE9CQ,EAAOtB,QAAQ0B,SAAS,aAC1BnB,EAAMqB,KAAKD,GAEXnB,EAAQoB,KAAKD,EACd,QAxC+D,CACpE,IAAIE,EAAelB,EAAStB,KAGxBwC,EAAaH,SAAS,OACxBG,EAAe,IAAIA,MAGrB,MAAMC,EAAa,GAAGlB,OAAciB,KAAgBhB,IAAOC,IAEvDjC,EAAcS,IAAIqB,EAASxB,MAC7BoB,EAAMqB,KAAKE,GAEXtB,EAAQoB,KAAKE,EAEhB,CA6BD,MAAO,CAAEvB,QAAOC,UAClB"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { StaticHandlerContext } from 'react-router-dom/server';
|
|
2
|
+
/**
|
|
3
|
+
* Serialize react router errors
|
|
4
|
+
* @see https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/server.tsx#LL166C1-L188C2
|
|
5
|
+
*/
|
|
6
|
+
declare function serializeErrors(errors: StaticHandlerContext['errors']): StaticHandlerContext['errors'];
|
|
7
|
+
export { serializeErrors as default };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{isRouteErrorResponse as r}from"react-router-dom";function e(e){if(!e)return null;const o=Object.entries(e),t={};for(const[e,n]of o)r(n)?t[e]={...n,__type:"RouteErrorResponse"}:n instanceof Error?t[e]={message:n.message,__type:"Error"}:t[e]=n;return t}export{e as default};
|
|
2
|
+
//# sourceMappingURL=serialize-errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialize-errors.js","sources":["../../src/helpers/serialize-errors.ts"],"sourcesContent":["import { isRouteErrorResponse } from 'react-router-dom';\nimport type { StaticHandlerContext } from 'react-router-dom/server';\n\n/**\n * Serialize react router errors\n * @see https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/server.tsx#LL166C1-L188C2\n */\nfunction serializeErrors(errors: StaticHandlerContext['errors']): StaticHandlerContext['errors'] {\n if (!errors) {\n return null;\n }\n\n const entries = Object.entries(errors);\n const serialized: StaticHandlerContext['errors'] = {};\n for (const [key, val] of entries) {\n // Hey you! If you change this, please change the corresponding logic in\n // deserializeErrors in react-router-dom/index.tsx :)\n if (isRouteErrorResponse(val)) {\n serialized[key] = { ...val, __type: 'RouteErrorResponse' };\n } else if (val instanceof Error) {\n // Do not serialize stack traces from SSR for security reasons\n serialized[key] = {\n message: val.message,\n __type: 'Error',\n };\n } else {\n serialized[key] = val;\n }\n }\n\n return serialized;\n}\n\nexport default serializeErrors;\n"],"names":["serializeErrors","errors","entries","Object","serialized","key","val","isRouteErrorResponse","__type","Error","message"],"mappings":"wDAOA,SAASA,EAAgBC,GACvB,IAAKA,EACH,OAAO,KAGT,MAAMC,EAAUC,OAAOD,QAAQD,GACzBG,EAA6C,CAAA,EACnD,IAAK,MAAOC,EAAKC,KAAQJ,EAGnBK,EAAqBD,GACvBF,EAAWC,GAAO,IAAKC,EAAKE,OAAQ,sBAC3BF,aAAeG,MAExBL,EAAWC,GAAO,CAChBK,QAASJ,EAAII,QACbF,OAAQ,SAGVJ,EAAWC,GAAOC,EAItB,OAAOF,CACT"}
|
|
@@ -0,0 +1,7 @@
|
|
|
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 };
|
|
@@ -0,0 +1,2 @@
|
|
|
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};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
|
|
@@ -0,0 +1 @@
|
|
|
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 = {\n method: req.method,\n headers,\n signal: controller.signal,\n };\n\n if (req.method !== 'GET' && req.method !== 'HEAD') {\n init['body'] = req.body;\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","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,EAAO,CACXC,OAAQvB,EAAIuB,OACZZ,UACAa,OAAQjB,EAAWiB,QAOrB,MAJmB,QAAfxB,EAAIuB,QAAmC,SAAfvB,EAAIuB,SAC9BD,EAAW,KAAItB,EAAIyB,MAGd,IAAIC,QAAQtB,EAAIuB,KAAML,EAC/B"}
|
package/node/entry.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Express, Request } from 'express';
|
|
2
|
+
import { Response as ExpressResponse } from "express";
|
|
3
|
+
import { FC, PropsWithChildren } from 'react';
|
|
4
|
+
import { RouteObject } from 'react-router-dom';
|
|
5
|
+
import { IRenderOptions, TRender } from "./render.js";
|
|
6
|
+
import ServerConfig from "../services/server-config.js";
|
|
7
|
+
interface IInitServerRequestOut<T = Record<string, any>> {
|
|
8
|
+
appProps?: T;
|
|
9
|
+
}
|
|
10
|
+
interface IEntrypointOptions<TAppProps = Record<string, any>> {
|
|
11
|
+
onServerCreated?: (app: Express) => Promise<void> | void;
|
|
12
|
+
onRequest?: (req: Request, res: ExpressResponse) => Promise<IInitServerRequestOut<TAppProps>> | IInitServerRequestOut<TAppProps>;
|
|
13
|
+
onRouterReady?: IRenderOptions<TAppProps>['onRouterReady'];
|
|
14
|
+
onShellReady?: IRenderOptions<TAppProps>['onShellReady'];
|
|
15
|
+
onShellError?: IRenderOptions<TAppProps>['onShellError'];
|
|
16
|
+
onResponse?: IRenderOptions<TAppProps>['onResponse'];
|
|
17
|
+
onError?: IRenderOptions<TAppProps>['onError'];
|
|
18
|
+
getState?: IRenderOptions<TAppProps>['getState'];
|
|
19
|
+
}
|
|
20
|
+
interface IPrepareRenderOut<TAppProps = Record<string, any>> {
|
|
21
|
+
render: TRender;
|
|
22
|
+
initServer: IEntryServerOptions<TAppProps>['initServer'];
|
|
23
|
+
}
|
|
24
|
+
interface IAppServerProps<T = Record<string, any>> {
|
|
25
|
+
server: T & {
|
|
26
|
+
req: Request;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
type TApp<T> = FC<PropsWithChildren<IAppServerProps<T>>>;
|
|
30
|
+
interface IEntryServerOptions<TAppProps = Record<string, any>> {
|
|
31
|
+
routes: RouteObject[];
|
|
32
|
+
initServer?: (params: {
|
|
33
|
+
config: ServerConfig;
|
|
34
|
+
}) => IEntrypointOptions<TAppProps> | Promise<IEntrypointOptions<TAppProps>>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Render server side application
|
|
38
|
+
*/
|
|
39
|
+
declare function entry<TAppProps>({ routes, initServer }: IEntryServerOptions<TAppProps>, App: TApp<TAppProps>): IPrepareRenderOut<TAppProps>;
|
|
40
|
+
export { entry as default, IInitServerRequestOut, IEntrypointOptions, IPrepareRenderOut, IAppServerProps, TApp, IEntryServerOptions };
|
package/node/entry.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry.js","sources":["../../src/node/entry.tsx"],"sourcesContent":["import type { Express, Request, Response as ExpressResponse } from 'express';\nimport type { FC, PropsWithChildren } from 'react';\nimport type { RouteObject } from 'react-router-dom';\nimport { createStaticHandler } from 'react-router-dom/server.mjs';\nimport type { IRenderOptions, IRenderParams, TRender } from '@node/render';\nimport render from '@node/render';\nimport type ServerConfig from '@services/server-config';\n\nexport interface IInitServerRequestOut<T = Record<string, any>> {\n appProps?: T;\n}\n\nexport interface IEntrypointOptions<TAppProps = Record<string, any>> {\n onServerCreated?: (app: Express) => 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 initServer: IEntryServerOptions<TAppProps>['initServer'];\n}\n\nexport interface IAppServerProps<T = Record<string, any>> {\n server: T & {\n req: Request;\n };\n}\n\nexport type TApp<T> = FC<PropsWithChildren<IAppServerProps<T>>>;\n\nexport interface IEntryServerOptions<TAppProps = Record<string, any>> {\n routes: RouteObject[];\n initServer?: (params: {\n config: ServerConfig;\n }) => IEntrypointOptions<TAppProps> | Promise<IEntrypointOptions<TAppProps>>;\n}\n\n/**\n * Render server side application\n */\nfunction entry<TAppProps>(\n { routes, initServer }: IEntryServerOptions<TAppProps>,\n App: TApp<TAppProps>,\n): IPrepareRenderOut<TAppProps> {\n const handler = createStaticHandler(routes);\n\n return {\n render: render.bind(null, { handler, App } as IRenderParams<TAppProps>) as TRender,\n initServer,\n };\n}\n\nexport default entry;\n"],"names":["entry","routes","initServer","App","handler","createStaticHandler","render","bind"],"mappings":"6FAiDA,SAASA,GACPC,OAAEA,EAAMC,WAAEA,GACVC,GAEA,MAAMC,EAAUC,EAAoBJ,GAEpC,MAAO,CACLK,OAAQA,EAAOC,KAAK,KAAM,CAAEH,UAASD,QACrCD,aAEJ"}
|
package/node/render.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { StaticHandler } from '@remix-run/router';
|
|
2
|
+
import { Request } from 'express';
|
|
3
|
+
import { Response as ExpressResponse } from "express";
|
|
4
|
+
import { StaticHandlerContext } from 'react-router-dom/server';
|
|
5
|
+
import StreamError from "../constants/stream-error.js";
|
|
6
|
+
import { IServerContext } from "../context/server.js";
|
|
7
|
+
import { IObtainStreamErrorOut } from "../helpers/obtain-stream-error.js";
|
|
8
|
+
import { TApp } from "./entry.js";
|
|
9
|
+
import ServerConfig from "../services/server-config.js";
|
|
10
|
+
interface IRequestContext<TAppProps = Record<any, any>> {
|
|
11
|
+
req: Request;
|
|
12
|
+
res: ExpressResponse;
|
|
13
|
+
appProps: TAppProps;
|
|
14
|
+
html: {
|
|
15
|
+
header: string;
|
|
16
|
+
footer: string;
|
|
17
|
+
};
|
|
18
|
+
routerContext?: StaticHandlerContext;
|
|
19
|
+
serverContext?: IServerContext;
|
|
20
|
+
isStream?: boolean;
|
|
21
|
+
didError?: StreamError;
|
|
22
|
+
}
|
|
23
|
+
type TRender<TAppProps = Record<any, any>> = (config: ServerConfig, context: IRequestContext<TAppProps>, options: IRenderOptions) => Promise<void>;
|
|
24
|
+
interface IRenderParams<TAppProps = Record<string, any>> {
|
|
25
|
+
App: TApp<TAppProps>;
|
|
26
|
+
handler: StaticHandler;
|
|
27
|
+
}
|
|
28
|
+
interface IRenderOptions<TAppProps = Record<string, any>> {
|
|
29
|
+
appProps?: TAppProps;
|
|
30
|
+
onRouterReady?: (params: {
|
|
31
|
+
context: IRequestContext<TAppProps>;
|
|
32
|
+
}) => Promise<IRouterReadyOut> | IRouterReadyOut;
|
|
33
|
+
onShellReady?: (params: {
|
|
34
|
+
context: IRequestContext;
|
|
35
|
+
}) => IShellReadyOut;
|
|
36
|
+
onShellError?: (params: {
|
|
37
|
+
context: IRequestContext<TAppProps>;
|
|
38
|
+
error: Error;
|
|
39
|
+
}) => string | undefined;
|
|
40
|
+
onError?: (params: {
|
|
41
|
+
context: IRequestContext<TAppProps>;
|
|
42
|
+
error: IObtainStreamErrorOut;
|
|
43
|
+
}) => void;
|
|
44
|
+
onResponse?: (params: {
|
|
45
|
+
context: IRequestContext<TAppProps>;
|
|
46
|
+
html: string;
|
|
47
|
+
}) => string | undefined;
|
|
48
|
+
getState?: (params: {
|
|
49
|
+
context: IRequestContext<TAppProps>;
|
|
50
|
+
}) => Record<string, Record<string, any>> | undefined;
|
|
51
|
+
}
|
|
52
|
+
interface IRouterReadyOut {
|
|
53
|
+
isStream?: boolean;
|
|
54
|
+
}
|
|
55
|
+
interface IShellReadyOut {
|
|
56
|
+
header?: string;
|
|
57
|
+
footer?: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Render application
|
|
61
|
+
*/
|
|
62
|
+
declare function render({ App, handler }: IRenderParams, // @see entry (bind)
|
|
63
|
+
config: ServerConfig, context: IRequestContext, { onRouterReady, onShellReady, onResponse, onShellError, onError, getState }: IRenderOptions): Promise<void>;
|
|
64
|
+
export { render as default, IRequestContext, TRender, IRenderParams, IRenderOptions, IRouterReadyOut, IShellReadyOut };
|
package/node/render.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
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-dom/server.mjs";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";async function l({App:l,handler:p},u,f,{onRouterReady:h,onShellReady:x,onResponse:S,onShellError:R,onError:g,getState:y}){const{req:C,res:E}=f,j=c(C);f.routerContext=await p.query(j);const v=m(E,f.routerContext);if(!v)return;const{isStream:w=!0}=await(h?.({context:f}))??{};f.isStream=w,f.serverContext={response:null,isServer:!0};const b=o(p.dataRoutes,f.routerContext),T=E.write.bind(E),q=u.getLogger();let $;E.write=(e,...r)=>{const t="string"==typeof e,o=t?e:Buffer.from(e).toString(),n=S?.({context:f,html:o});return n?T(t?`${n}${e}`:Buffer.concat([Buffer.from(n),e]),...r):T(e,...r)};const{serverContext:A,routerContext:B,appProps:P}=f,{pipe:k,abort:D}=t(r.createElement(a,{context:A},r.createElement(l,{server:{...P,req:C}},r.createElement(n,{router:b,context:B,hydrate:!1}))),{onShellReady(){w&&d(f,{pipe:k,statusCode:v,onShellReady:x,getState:y})},onAllReady(){clearTimeout($),w||d(f,{pipe:k,statusCode:v,onShellReady:x,getState:y})},onShellError(e){const r=R?.({context:f,error:e})||`<!doctype html><p>Something went wrong: ${e.message}</p>`;E.status(500),E.setHeader("content-type","text/html"),E.send(r)},onError(r){clearTimeout($);const t=i(r),{code:o,message:n}=t,{didError:a}=f;f.didError=a??o,g?.({context:f,error:t}),q.info(e.red(`Stream error. Code: ${o}`)),[s.RenderAborted,s.RenderTimeout,s.RenderCancel].includes(o)?q.info(e.dim(n)):q.error(r)}});$=setTimeout((()=>{f.didError=s.RenderTimeout,D()}),u.getParams().abortDelay),C.on("close",(()=>{f.didError=s.RenderCancel,D()}))}export{l as default};
|
|
2
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sources":["../../src/node/render.tsx"],"sourcesContent":["import type { StaticHandler } from '@remix-run/router';\nimport chalk from 'chalk';\nimport type { Request, Response as ExpressResponse } from 'express';\nimport React from 'react';\nimport { renderToPipeableStream } from 'react-dom/server';\nimport type { StaticHandlerContext } from 'react-router-dom/server';\nimport { createStaticRouter, StaticRouterProvider } from 'react-router-dom/server.mjs';\nimport StreamError from '@constants/stream-error';\nimport type { IServerContext } from '@context/server';\nimport { ServerProvider } from '@context/server';\nimport handleResponse from '@helpers/handle-response';\nimport type { IObtainStreamErrorOut } from '@helpers/obtain-stream-error';\nimport obtainStreamError from '@helpers/obtain-stream-error';\nimport createFetchRequest from '@node/create-fetch-request';\nimport type { TApp } from '@node/entry';\nimport writeResponse from '@node/write-response';\nimport type ServerConfig from '@services/server-config';\n\nexport interface IRequestContext<TAppProps = Record<any, any>> {\n req: Request;\n res: ExpressResponse;\n appProps: TAppProps;\n html: { header: string; footer: string };\n routerContext?: StaticHandlerContext;\n serverContext?: IServerContext;\n isStream?: boolean;\n didError?: StreamError;\n}\n\nexport type TRender<TAppProps = Record<any, any>> = (\n config: ServerConfig,\n context: IRequestContext<TAppProps>,\n options: IRenderOptions,\n) => Promise<void>;\n\nexport interface IRenderParams<TAppProps = Record<string, any>> {\n App: TApp<TAppProps>;\n handler: StaticHandler;\n}\n\nexport interface IRenderOptions<TAppProps = Record<string, any>> {\n appProps?: TAppProps;\n onRouterReady?: (params: {\n context: IRequestContext<TAppProps>;\n }) => Promise<IRouterReadyOut> | IRouterReadyOut;\n onShellReady?: (params: { context: IRequestContext }) => IShellReadyOut;\n onShellError?: (params: {\n context: IRequestContext<TAppProps>;\n error: Error;\n }) => string | undefined; // return html or undefined\n onError?: (params: { context: IRequestContext<TAppProps>; error: IObtainStreamErrorOut }) => void;\n onResponse?: (params: {\n context: IRequestContext<TAppProps>;\n html: string;\n }) => string | undefined;\n getState?: (params: {\n context: IRequestContext<TAppProps>;\n }) => Record<string, Record<string, any>> | undefined;\n}\n\nexport interface IRouterReadyOut {\n isStream?: boolean;\n}\n\nexport interface IShellReadyOut {\n header?: string;\n footer?: string;\n}\n\n/**\n * Render application\n */\nasync function render(\n { App, handler }: IRenderParams, // @see entry (bind)\n config: ServerConfig,\n context: IRequestContext,\n { onRouterReady, onShellReady, onResponse, onShellError, onError, getState }: IRenderOptions,\n): Promise<void> {\n const { req, res } = context;\n const fetchRequest = createFetchRequest(req);\n\n context.routerContext = (await handler.query(fetchRequest)) as StaticHandlerContext;\n /**\n * Handle response from page loader, router context can be Response\n */\n const statusCode = handleResponse(res, context.routerContext);\n\n if (!statusCode) {\n return;\n }\n\n const { isStream = true } = (await onRouterReady?.({ context })) ?? {};\n\n context.isStream = isStream;\n context.serverContext = { response: null, isServer: true };\n\n const router = createStaticRouter(handler.dataRoutes, context.routerContext);\n const write = res.write.bind(res);\n const Logger = config.getLogger();\n let abortTimer: NodeJS.Timer | undefined = undefined;\n\n /**\n * Listen response and stream to add possibility modify html on fly\n * E.g. listen stream and append some data\n */\n res.write = (data: string | Uint8Array, ...args): boolean => {\n const isString = typeof data === 'string';\n const html = isString ? data : Buffer.from(data).toString();\n const additionalHtml = onResponse?.({ context, html });\n\n if (additionalHtml) {\n return write(\n isString ? `${additionalHtml}${data}` : Buffer.concat([Buffer.from(additionalHtml), data]),\n ...args,\n ) as boolean;\n }\n\n return write(data, ...args) as boolean;\n };\n\n const { serverContext, routerContext, appProps } = context;\n\n const { pipe, abort } = renderToPipeableStream(\n <ServerProvider context={serverContext}>\n <App server={{ ...appProps, req }}>\n <StaticRouterProvider router={router} context={routerContext} hydrate={false} />\n </App>\n </ServerProvider>,\n {\n onShellReady(): void {\n if (!isStream) {\n return;\n }\n\n writeResponse(context, {\n pipe,\n statusCode,\n onShellReady,\n getState,\n });\n },\n onAllReady(): void {\n clearTimeout(abortTimer);\n\n if (isStream) {\n return;\n }\n\n writeResponse(context, {\n pipe,\n statusCode,\n onShellReady,\n getState,\n });\n },\n onShellError(e: Error): void {\n const htmlError =\n onShellError?.({ context, error: e }) ||\n `<!doctype html><p>Something went wrong: ${e.message}</p>`;\n\n res.status(500);\n res.setHeader('content-type', 'text/html');\n res.send(htmlError);\n },\n onError(err): void {\n clearTimeout(abortTimer);\n\n const error = obtainStreamError(err);\n const { code, message } = error;\n const { didError } = context;\n\n context.didError = didError ?? code;\n\n onError?.({ context, error });\n Logger.info(chalk.red(`Stream error. Code: ${code}`));\n\n if (\n [StreamError.RenderAborted, StreamError.RenderTimeout, StreamError.RenderCancel].includes(\n code,\n )\n ) {\n Logger.info(chalk.dim(message));\n\n return;\n }\n\n Logger.error(err as string);\n },\n },\n );\n\n // Abandon and switch to client rendering if enough time passes.\n abortTimer = setTimeout(() => {\n context.didError = StreamError.RenderTimeout;\n abort();\n }, config.getParams().abortDelay);\n\n // Detect cancel request\n req.on('close', () => {\n context.didError = StreamError.RenderCancel;\n abort();\n });\n}\n\nexport default render;\n"],"names":["async","render","App","handler","config","context","onRouterReady","onShellReady","onResponse","onShellError","onError","getState","req","res","fetchRequest","createFetchRequest","routerContext","query","statusCode","handleResponse","isStream","serverContext","response","isServer","router","createStaticRouter","dataRoutes","write","bind","Logger","getLogger","abortTimer","data","args","isString","html","Buffer","from","toString","additionalHtml","concat","appProps","pipe","abort","renderToPipeableStream","React","createElement","ServerProvider","server","StaticRouterProvider","hydrate","writeResponse","onAllReady","clearTimeout","e","htmlError","error","message","status","setHeader","send","err","obtainStreamError","code","didError","info","chalk","red","StreamError","RenderAborted","RenderTimeout","RenderCancel","includes","dim","setTimeout","getParams","abortDelay","on"],"mappings":"2cAwEAA,eAAeC,GACbC,IAAEA,EAAGC,QAAEA,GACPC,EACAC,GACAC,cAAEA,EAAaC,aAAEA,EAAYC,WAAEA,EAAUC,aAAEA,EAAYC,QAAEA,EAAOC,SAAEA,IAElE,MAAMC,IAAEA,EAAGC,IAAEA,GAAQR,EACfS,EAAeC,EAAmBH,GAExCP,EAAQW,oBAAuBb,EAAQc,MAAMH,GAI7C,MAAMI,EAAaC,EAAeN,EAAKR,EAAQW,eAE/C,IAAKE,EACH,OAGF,MAAME,SAAEA,GAAW,SAAgBd,IAAgB,CAAED,cAAe,GAEpEA,EAAQe,SAAWA,EACnBf,EAAQgB,cAAgB,CAAEC,SAAU,KAAMC,UAAU,GAEpD,MAAMC,EAASC,EAAmBtB,EAAQuB,WAAYrB,EAAQW,eACxDW,EAAQd,EAAIc,MAAMC,KAAKf,GACvBgB,EAASzB,EAAO0B,YACtB,IAAIC,EAMJlB,EAAIc,MAAQ,CAACK,KAA8BC,KACzC,MAAMC,EAA2B,iBAATF,EAClBG,EAAOD,EAAWF,EAAOI,OAAOC,KAAKL,GAAMM,WAC3CC,EAAiB/B,IAAa,CAAEH,UAAS8B,SAE/C,OAAII,EACKZ,EACLO,EAAW,GAAGK,IAAiBP,IAASI,OAAOI,OAAO,CAACJ,OAAOC,KAAKE,GAAiBP,OACjFC,GAIAN,EAAMK,KAASC,EAAgB,EAGxC,MAAMZ,cAAEA,EAAaL,cAAEA,EAAayB,SAAEA,GAAapC,GAE7CqC,KAAEA,EAAIC,MAAEA,GAAUC,EACtBC,EAACC,cAAAC,EAAe,CAAA1C,QAASgB,GACvBwB,EAACC,cAAA5C,GAAI8C,OAAQ,IAAKP,EAAU7B,QAC1BiC,EAAAC,cAACG,EAAqB,CAAAzB,OAAQA,EAAQnB,QAASW,EAAekC,SAAS,MAG3E,CACE3C,eACOa,GAIL+B,EAAc9C,EAAS,CACrBqC,OACAxB,aACAX,eACAI,YAEH,EACDyC,aACEC,aAAatB,GAETX,GAIJ+B,EAAc9C,EAAS,CACrBqC,OACAxB,aACAX,eACAI,YAEH,EACDF,aAAa6C,GACX,MAAMC,EACJ9C,IAAe,CAAEJ,UAASmD,MAAOF,KACjC,2CAA2CA,EAAEG,cAE/C5C,EAAI6C,OAAO,KACX7C,EAAI8C,UAAU,eAAgB,aAC9B9C,EAAI+C,KAAKL,EACV,EACD7C,QAAQmD,GACNR,aAAatB,GAEb,MAAMyB,EAAQM,EAAkBD,IAC1BE,KAAEA,EAAIN,QAAEA,GAAYD,GACpBQ,SAAEA,GAAa3D,EAErBA,EAAQ2D,SAAWA,GAAYD,EAE/BrD,IAAU,CAAEL,UAASmD,UACrB3B,EAAOoC,KAAKC,EAAMC,IAAI,uBAAuBJ,MAG3C,CAACK,EAAYC,cAAeD,EAAYE,cAAeF,EAAYG,cAAcC,SAC/ET,GAGFlC,EAAOoC,KAAKC,EAAMO,IAAIhB,IAKxB5B,EAAO2B,MAAMK,EACd,IAKL9B,EAAa2C,YAAW,KACtBrE,EAAQ2D,SAAWI,EAAYE,cAC/B3B,GAAO,GACNvC,EAAOuE,YAAYC,YAGtBhE,EAAIiE,GAAG,SAAS,KACdxE,EAAQ2D,SAAWI,EAAYG,aAC/B5B,GAAO,GAEX"}
|
package/node/server.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Server } from 'node:net';
|
|
3
|
+
import ServerConfig from "../services/server-config.js";
|
|
4
|
+
interface ICreateServerOut {
|
|
5
|
+
run: (options?: {
|
|
6
|
+
version?: string;
|
|
7
|
+
isPrintInfo?: boolean;
|
|
8
|
+
}) => Server;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Create SSR server
|
|
12
|
+
*/
|
|
13
|
+
declare function createServer(config: ServerConfig): Promise<ICreateServerOut>;
|
|
14
|
+
export { createServer as default, ICreateServerOut };
|
package/node/server.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import e from"path";import r from"compression";import o from"express";import t from"../helpers/print-server-info.js";import s from"../services/prepare-server.js";async function n(n){const i=o();if(n.setApp(i),n.isProd){const{root:t,publicDir:s,isSPA:a}=n.getParams();i.use(r()),a||i.use(((e,r,o)=>{"/index.html"===e.url&&(e.url="/index-not-found.html"),o()})),i.use(o.static(e.resolve(`${t}/${s}`),{index:!!a&&void 0}))}else{const e=await(await import("vite")).createServer({server:{middlewareMode:!0,watch:{usePolling:!0,interval:100}},appType:"custom"});i.use(e.middlewares),n.setVite(e)}const a=s.init(n);return n.isSPA?i.use("*",((e,r,o)=>{(async()=>{try{const o=(await a.loadHtml(e)).join("");r.send(o)}catch(e){o(e)}})()})):(await a.onAppCreated(),i.use("*",((e,r,o)=>{(async()=>{try{const[{render:o,onRequest:t,onRouterReady:s,onShellReady:i,onResponse:p,onShellError:l,onError:c,getState:d},m]=await Promise.all([a.loadEntrypoint(),a.loadHtml(e)]),{appProps:u}=await(t?.(e,r))??{},[h,f]=m,v={req:e,res:r,appProps:u??{},html:{header:h,footer:f}};await o(n,v,{onRouterReady:s,onShellReady:i,onShellError:l,onResponse:p,onError:c,getState:d})}catch(e){o(e)}})()}))),{run:({version:e,isPrintInfo:r=!0}={})=>{const{port:o,host:s}=n.getParams();n.isHost&&!n.isProd&&(n.getVite().config.server.host=s);const a=i.listen(o,s,(()=>{r&&t(a,n,{version:e})}));return a}}}export{n as default};
|
|
2
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sources":["../../src/node/server.ts"],"sourcesContent":["import type { Server } from 'node:net';\nimport path from 'path';\nimport compression from 'compression';\nimport express from 'express';\nimport printServerInfo from '@helpers/print-server-info';\nimport type { IRequestContext } from '@node/render';\nimport PrepareServer from '@services/prepare-server';\nimport type ServerConfig from '@services/server-config';\n\nexport interface ICreateServerOut {\n run: (options?: { version?: string; isPrintInfo?: boolean }) => Server;\n}\n\n/**\n * Create SSR server\n */\nasync function createServer(config: ServerConfig): Promise<ICreateServerOut> {\n const app = express();\n\n config.setApp(app);\n\n if (!config.isProd) {\n // Create Vite server in middleware mode and configure the app type as\n // 'custom', disabling Vite's own HTML serving logic so parent server\n // can take control\n const vite = await (\n await import('vite')\n ).createServer({\n server: {\n middlewareMode: true,\n watch: {\n // During tests, we edit the files too fast and sometimes chokidar\n // misses change events, so enforce polling for consistency\n usePolling: true,\n interval: 100,\n },\n },\n appType: 'custom',\n });\n\n // Use vite's connect instance as middleware\n app.use(vite.middlewares);\n\n config.setVite(vite);\n } else {\n const { root, publicDir, isSPA } = config.getParams();\n\n app.use(compression());\n\n if (!isSPA) {\n // ignore index.html file in SSR mode\n app.use((req, res, next) => {\n if (req.url === '/index.html') {\n req.url = '/index-not-found.html';\n }\n\n next();\n });\n }\n\n app.use(\n express.static(path.resolve(`${root}/${publicDir}`), {\n index: isSPA ? undefined : false,\n }),\n );\n }\n\n const prepareServer = PrepareServer.init(config);\n\n // SSR mode\n if (!config.isSPA) {\n await prepareServer.onAppCreated();\n\n app.use('*', (req, res, next) => {\n void (async () => {\n try {\n const [\n {\n render,\n onRequest,\n onRouterReady,\n onShellReady,\n onResponse,\n onShellError,\n onError,\n getState,\n },\n clientHtml,\n ] = await Promise.all([prepareServer.loadEntrypoint(), prepareServer.loadHtml(req)]);\n const { appProps } = (await onRequest?.(req, res)) ?? {};\n const [header, footer] = clientHtml;\n\n const context: IRequestContext = {\n req,\n res,\n appProps: appProps ?? {},\n html: { header, footer },\n };\n\n await render(config, context, {\n onRouterReady,\n onShellReady,\n onShellError,\n onResponse,\n onError,\n getState,\n });\n } catch (e) {\n next(e);\n }\n })();\n });\n } else {\n // SPA mode, redirect any request to index.html\n app.use('*', (req, res, next) => {\n void (async () => {\n try {\n const html = (await prepareServer.loadHtml(req)).join('');\n\n res.send(html);\n } catch (e) {\n next(e);\n }\n })();\n });\n }\n\n return {\n run: ({ version, isPrintInfo = true } = {}): Server => {\n const { port, host } = config.getParams();\n\n // update resolved host for print network link\n if (config.isHost && !config.isProd) {\n config.getVite()!.config.server.host = host;\n }\n\n const server = app.listen(port, host, () => {\n if (!isPrintInfo) {\n return;\n }\n\n void printServerInfo(server, config, { version });\n });\n\n return server;\n },\n };\n}\n\nexport default createServer;\n"],"names":["async","createServer","config","app","express","setApp","isProd","root","publicDir","isSPA","getParams","use","compression","req","res","next","url","static","path","resolve","index","undefined","vite","import","server","middlewareMode","watch","usePolling","interval","appType","middlewares","setVite","prepareServer","PrepareServer","init","html","loadHtml","join","send","e","onAppCreated","render","onRequest","onRouterReady","onShellReady","onResponse","onShellError","onError","getState","clientHtml","Promise","all","loadEntrypoint","appProps","header","footer","context","run","version","isPrintInfo","port","host","isHost","getVite","listen","printServerInfo"],"mappings":"kKAgBAA,eAAeC,EAAaC,GAC1B,MAAMC,EAAMC,IAIZ,GAFAF,EAAOG,OAAOF,GAETD,EAAOI,OAuBL,CACL,MAAMC,KAAEA,EAAIC,UAAEA,EAASC,MAAEA,GAAUP,EAAOQ,YAE1CP,EAAIQ,IAAIC,KAEHH,GAEHN,EAAIQ,KAAI,CAACE,EAAKC,EAAKC,KACD,gBAAZF,EAAIG,MACNH,EAAIG,IAAM,yBAGZD,GAAM,IAIVZ,EAAIQ,IACFP,EAAQa,OAAOC,EAAKC,QAAQ,GAAGZ,KAAQC,KAAc,CACnDY,QAAOX,QAAQY,IAGpB,KA5CmB,CAIlB,MAAMC,cACEC,OAAO,SACbtB,aAAa,CACbuB,OAAQ,CACNC,gBAAgB,EAChBC,MAAO,CAGLC,YAAY,EACZC,SAAU,MAGdC,QAAS,WAIX1B,EAAIQ,IAAIW,EAAKQ,aAEb5B,EAAO6B,QAAQT,EAChB,CAuBD,MAAMU,EAAgBC,EAAcC,KAAKhC,GA4DzC,OAzDKA,EAAOO,MA4CVN,EAAIQ,IAAI,KAAK,CAACE,EAAKC,EAAKC,KACjB,WACH,IACE,MAAMoB,SAAcH,EAAcI,SAASvB,IAAMwB,KAAK,IAEtDvB,EAAIwB,KAAKH,EACV,CAAC,MAAOI,GACPxB,EAAKwB,EACN,CACF,EARI,EAQD,WApDAP,EAAcQ,eAEpBrC,EAAIQ,IAAI,KAAK,CAACE,EAAKC,EAAKC,KACjB,WACH,IACE,OACE0B,OACEA,EAAMC,UACNA,EAASC,cACTA,EAAaC,aACbA,EAAYC,WACZA,EAAUC,aACVA,EAAYC,QACZA,EAAOC,SACPA,GAEFC,SACQC,QAAQC,IAAI,CAACnB,EAAcoB,iBAAkBpB,EAAcI,SAASvB,MACxEwC,SAAEA,SAAoBX,IAAY7B,EAAKC,KAAS,IAC/CwC,EAAQC,GAAUN,EAEnBO,EAA2B,CAC/B3C,MACAC,MACAuC,SAAUA,GAAY,CAAE,EACxBlB,KAAM,CAAEmB,SAAQC,iBAGZd,EAAOvC,EAAQsD,EAAS,CAC5Bb,gBACAC,eACAE,eACAD,aACAE,UACAC,YAEH,CAAC,MAAOT,GACPxB,EAAKwB,EACN,CACF,EApCI,EAoCD,KAiBD,CACLkB,IAAK,EAAGC,UAASC,eAAc,GAAS,CAAA,KACtC,MAAMC,KAAEA,EAAIC,KAAEA,GAAS3D,EAAOQ,YAG1BR,EAAO4D,SAAW5D,EAAOI,SAC3BJ,EAAO6D,UAAW7D,OAAOsB,OAAOqC,KAAOA,GAGzC,MAAMrC,EAASrB,EAAI6D,OAAOJ,EAAMC,GAAM,KAC/BF,GAIAM,EAAgBzC,EAAQtB,EAAQ,CAAEwD,WAAU,IAGnD,OAAOlC,CAAM,EAGnB"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/// <reference types="react-dom" />
|
|
2
|
+
import { PipeableStream } from 'react-dom/server';
|
|
3
|
+
import { IRenderOptions, IRequestContext } from "./render.js";
|
|
4
|
+
interface IWriteResponseParams {
|
|
5
|
+
pipe: PipeableStream['pipe'];
|
|
6
|
+
onShellReady: IRenderOptions['onShellReady'];
|
|
7
|
+
getState: IRenderOptions['getState'];
|
|
8
|
+
statusCode?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Send response to client
|
|
12
|
+
*/
|
|
13
|
+
declare const writeResponse: (context: IRequestContext, params: IWriteResponseParams) => void;
|
|
14
|
+
export { writeResponse as default };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import e from"../helpers/build-custom-state.js";import t from"../helpers/build-router-state.js";import r from"../helpers/handle-response.js";const o=(o,s)=>{const{res:n,didError:d,serverContext:i,routerContext:a,html:l}=o,{pipe:p,onShellReady:f,getState:h}=s;let{statusCode:m}=s;if(m=r(n,i.response,m),!m)return;if(d){const e=n.end.bind(n);n.end=(...t)=>(n.write(c||l.footer),e(...t))}n.status(m),n.setHeader("content-type","text/html");const{header:u,footer:c}=f?.({context:o})??{},x=t(a),b=e(h?.({context:o}));l.footer=x+b+l.footer,n.write(u||l.header),p(n),d||n.write(c||l.footer)};export{o as default};
|
|
2
|
+
//# sourceMappingURL=write-response.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-response.js","sources":["../../src/node/write-response.ts"],"sourcesContent":["import type { Response as ExpressResponse } from 'express';\nimport type { PipeableStream } from 'react-dom/server';\nimport buildCustomState from '@helpers/build-custom-state';\nimport buildRouterState from '@helpers/build-router-state';\nimport handleResponse from '@helpers/handle-response';\nimport type { IRenderOptions, IRequestContext } from '@node/render';\n\ninterface IWriteResponseParams {\n pipe: PipeableStream['pipe'];\n onShellReady: IRenderOptions['onShellReady'];\n getState: IRenderOptions['getState'];\n statusCode?: number; // default status\n}\n\n/**\n * Send response to client\n */\nconst writeResponse = (context: IRequestContext, params: IWriteResponseParams): void => {\n const { res, didError, serverContext, routerContext, html } = context;\n const { pipe, onShellReady, getState } = params;\n let { statusCode } = params;\n\n // handle response from server components (navigate, status)\n statusCode = handleResponse(res, serverContext!.response, statusCode);\n\n if (!statusCode) {\n return;\n }\n\n // catch close connection from react and write footer\n if (didError) {\n const end = res.end.bind(res);\n\n res.end = (...args: unknown[]): ExpressResponse => {\n // send second part of app shell\n res.write(modifiedFooter || html.footer);\n\n return end(...args) as ExpressResponse;\n };\n }\n\n res.status(statusCode);\n res.setHeader('content-type', 'text/html');\n\n const { header: modifiedHeader, footer: modifiedFooter } = onShellReady?.({ context }) ?? {};\n const routerState = buildRouterState(routerContext!);\n const customState = buildCustomState(getState?.({ context }));\n\n html.footer = routerState + customState + html.footer;\n\n // send first part of app shell\n res.write(modifiedHeader || html.header);\n // start streaming app\n pipe(res);\n\n if (!didError) {\n // send second part of app shell\n res.write(modifiedFooter || html.footer);\n }\n};\n\nexport default writeResponse;\n"],"names":["writeResponse","context","params","res","didError","serverContext","routerContext","html","pipe","onShellReady","getState","statusCode","handleResponse","response","end","bind","args","write","modifiedFooter","footer","status","setHeader","header","modifiedHeader","routerState","buildRouterState","customState","buildCustomState"],"mappings":"6IAiBA,MAAMA,EAAgB,CAACC,EAA0BC,KAC/C,MAAMC,IAAEA,EAAGC,SAAEA,EAAQC,cAAEA,EAAaC,cAAEA,EAAaC,KAAEA,GAASN,GACxDO,KAAEA,EAAIC,aAAEA,EAAYC,SAAEA,GAAaR,EACzC,IAAIS,WAAEA,GAAeT,EAKrB,GAFAS,EAAaC,EAAeT,EAAKE,EAAeQ,SAAUF,IAErDA,EACH,OAIF,GAAIP,EAAU,CACZ,MAAMU,EAAMX,EAAIW,IAAIC,KAAKZ,GAEzBA,EAAIW,IAAM,IAAIE,KAEZb,EAAIc,MAAMC,GAAkBX,EAAKY,QAE1BL,KAAOE,GAEjB,CAEDb,EAAIiB,OAAOT,GACXR,EAAIkB,UAAU,eAAgB,aAE9B,MAAQC,OAAQC,EAAgBJ,OAAQD,GAAmBT,IAAe,CAAER,aAAc,GACpFuB,EAAcC,EAAiBnB,GAC/BoB,EAAcC,EAAiBjB,IAAW,CAAET,aAElDM,EAAKY,OAASK,EAAcE,EAAcnB,EAAKY,OAG/ChB,EAAIc,MAAMM,GAAkBhB,EAAKe,QAEjCd,EAAKL,GAEAC,GAEHD,EAAIc,MAAMC,GAAkBX,EAAKY,OAClC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lomray/vite-ssr-boost",
|
|
3
|
+
"version": "1.0.0-beta.1",
|
|
4
|
+
"description": "Vite plugin for create awesome SSR or SPA applications on React.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"js",
|
|
8
|
+
"vite",
|
|
9
|
+
"plugin",
|
|
10
|
+
"react",
|
|
11
|
+
"ssr"
|
|
12
|
+
],
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/Lomray-Software/vite-ssr-boost.git"
|
|
19
|
+
},
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/Lomray-Software/vite-ssr-boost/issues"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/Lomray-Software/vite-ssr-boost",
|
|
24
|
+
"author": "Mikhail Yarmaliuk",
|
|
25
|
+
"license": "Apache-2.0",
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "rollup -c",
|
|
28
|
+
"build:watch": "rollup -c -w",
|
|
29
|
+
"release": "npm run build && cd lib && npm publish",
|
|
30
|
+
"lint:check": "eslint --ext \".ts,.tsx\" \"src/**/*.{ts,tsx,*.ts,*tsx}\"",
|
|
31
|
+
"lint:format": "eslint --fix --ext \".ts,.tsx\" \"src/**/*.{ts,tsx,*.ts,*tsx}\"",
|
|
32
|
+
"ts:check": "tsc --project ./tsconfig.json --skipLibCheck --noemit"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"chalk": "^5.2.0",
|
|
36
|
+
"commander": "^10.0.1",
|
|
37
|
+
"compression": "^1.7.4",
|
|
38
|
+
"express": "^4.18.2",
|
|
39
|
+
"react-dom": "^18.2.0",
|
|
40
|
+
"react-router-dom": "^6.12.1"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@commitlint/cli": "^17.6.5",
|
|
44
|
+
"@commitlint/config-conventional": "^17.6.5",
|
|
45
|
+
"@lomray/eslint-config": "^3.0.0",
|
|
46
|
+
"@lomray/prettier-config": "^1.2.0",
|
|
47
|
+
"@rollup/plugin-terser": "^0.4.3",
|
|
48
|
+
"@types/compression": "^1.7.2",
|
|
49
|
+
"@types/express": "^4.17.17",
|
|
50
|
+
"@types/react-dom": "^18.2.5",
|
|
51
|
+
"@typescript-eslint/eslint-plugin": "^5.59.11",
|
|
52
|
+
"@zerollup/ts-transform-paths": "^1.7.18",
|
|
53
|
+
"eslint": "^8.42.0",
|
|
54
|
+
"eslint-config-prettier": "^8.8.0",
|
|
55
|
+
"eslint-plugin-import": "^2.27.5",
|
|
56
|
+
"eslint-plugin-jsx-a11y": "^6.7.1",
|
|
57
|
+
"eslint-plugin-prettier": "^4.2.1",
|
|
58
|
+
"husky": "^8.0.3",
|
|
59
|
+
"lint-staged": "^13.2.2",
|
|
60
|
+
"prettier": "^2.8.8",
|
|
61
|
+
"rollup": "^3.25.1",
|
|
62
|
+
"rollup-plugin-copy": "^3.4.0",
|
|
63
|
+
"rollup-plugin-folder-input": "^1.0.1",
|
|
64
|
+
"rollup-plugin-peer-deps-external": "^2.2.4",
|
|
65
|
+
"rollup-plugin-preserve-shebangs": "^0.2.0",
|
|
66
|
+
"rollup-plugin-ts": "^3.2.0",
|
|
67
|
+
"semantic-release": "^21.0.5",
|
|
68
|
+
"typescript": "^4.9.5"
|
|
69
|
+
},
|
|
70
|
+
"peerDependencies": {
|
|
71
|
+
"vite": "^4.3.9"
|
|
72
|
+
},
|
|
73
|
+
"bin": {
|
|
74
|
+
"ssr-boost": "cli.js"
|
|
75
|
+
}
|
|
76
|
+
}
|
package/plugin.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
import { ICliContext } from "./constants/cli-context.js";
|
|
3
|
+
interface IPluginOptions {
|
|
4
|
+
indexFile?: string;
|
|
5
|
+
serverFile?: string;
|
|
6
|
+
abortDelay?: number;
|
|
7
|
+
customShortcuts?: {
|
|
8
|
+
key: string;
|
|
9
|
+
description: string;
|
|
10
|
+
action: (cliContext: ICliContext) => Promise<void> | void;
|
|
11
|
+
isOnlyDev?: boolean;
|
|
12
|
+
}[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Init insane vite ssr plugin
|
|
16
|
+
* @constructor
|
|
17
|
+
*/
|
|
18
|
+
declare function ViteSsrInsanePlugin(options?: IPluginOptions): Plugin[];
|
|
19
|
+
export { ViteSsrInsanePlugin as default, IPluginOptions };
|
package/plugin.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import e from"node:path";import n from"./constants/cli-actions.js";import t from"./constants/plugin-name.js";const i={indexFile:"index.html",serverFile:"server.ts",abortDelay:1e4};function o(o={}){const r=new URL(import.meta.url),s=global.viteBoostAction,a={...i,...o};return[{name:t,enforce:"pre",pluginOptions:{...a,pluginPath:e.dirname(r.pathname),action:s,isDev:s===n.dev},config:(e,{ssrBuild:n})=>(e.define={...e.define??{},__IS_SSR__:"1"===process.env.SSR_BOOST_IS_SSR||"dev"===s},n?{...e,publicDir:!1}:e)}]}export{o as default};
|
|
2
|
+
//# sourceMappingURL=plugin.js.map
|
package/plugin.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sources":["../src/plugin.ts"],"sourcesContent":["import path from 'node:path';\nimport type { Plugin } from 'vite';\nimport CliActions from '@constants/cli-actions';\nimport type { ICliContext } from '@constants/cli-context';\nimport PLUGIN_NAME from '@constants/plugin-name';\n\nexport interface IPluginOptions {\n indexFile?: string; // default: index.html\n serverFile?: string; // default: server.ts\n abortDelay?: number; // How long the server waits for data before giving up. default: 10000 (10 sec)\n customShortcuts?: {\n key: string;\n description: string;\n action: (cliContext: ICliContext) => Promise<void> | void;\n isOnlyDev?: boolean;\n }[];\n}\n\nconst defaultOptions: IPluginOptions = {\n indexFile: 'index.html',\n serverFile: 'server.ts',\n abortDelay: 10000,\n};\n\n/**\n * Init insane vite ssr plugin\n * @constructor\n */\nfunction ViteSsrInsanePlugin(options: IPluginOptions = {}): Plugin[] {\n const dirInfo = new URL(import.meta.url);\n const action = global.viteBoostAction as CliActions;\n const mergedOptions: IPluginOptions = { ...defaultOptions, ...options };\n\n return [\n {\n name: PLUGIN_NAME,\n enforce: 'pre',\n // @ts-ignore save custom options\n pluginOptions: {\n ...mergedOptions,\n pluginPath: path.dirname(dirInfo.pathname),\n action,\n isDev: action === CliActions.dev,\n },\n config(config, { ssrBuild }) {\n config.define = {\n ...(config.define ?? {}),\n __IS_SSR__: process.env.SSR_BOOST_IS_SSR === '1' || action === 'dev',\n };\n\n if (!ssrBuild) {\n return config;\n }\n\n return {\n ...config,\n publicDir: false,\n };\n },\n },\n ];\n}\n\nexport default ViteSsrInsanePlugin;\n"],"names":["defaultOptions","indexFile","serverFile","abortDelay","ViteSsrInsanePlugin","options","dirInfo","URL","url","action","global","viteBoostAction","mergedOptions","name","PLUGIN_NAME","enforce","pluginOptions","pluginPath","path","dirname","pathname","isDev","CliActions","dev","config","ssrBuild","define","__IS_SSR__","process","env","SSR_BOOST_IS_SSR","publicDir"],"mappings":"6GAkBA,MAAMA,EAAiC,CACrCC,UAAW,aACXC,WAAY,YACZC,WAAY,KAOd,SAASC,EAAoBC,EAA0B,IACrD,MAAMC,EAAU,IAAIC,gBAAgBC,KAC9BC,EAASC,OAAOC,gBAChBC,EAAgC,IAAKZ,KAAmBK,GAE9D,MAAO,CACL,CACEQ,KAAMC,EACNC,QAAS,MAETC,cAAe,IACVJ,EACHK,WAAYC,EAAKC,QAAQb,EAAQc,UACjCX,SACAY,MAAOZ,IAAWa,EAAWC,KAE/BC,OAAM,CAACA,GAAQC,SAAEA,MACfD,EAAOE,OAAS,IACVF,EAAOE,QAAU,GACrBC,WAA6C,MAAjCC,QAAQC,IAAIC,kBAAuC,QAAXrB,GAGjDgB,EAIE,IACFD,EACHO,WAAW,GALJP,IAUjB"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Logger as ViteLogger } from 'vite';
|
|
2
|
+
/**
|
|
3
|
+
* Default production logger
|
|
4
|
+
*/
|
|
5
|
+
declare class Logger implements ViteLogger {
|
|
6
|
+
/**
|
|
7
|
+
* @inheritDoc
|
|
8
|
+
*/
|
|
9
|
+
hasWarned: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* @inheritDoc
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* @inheritDoc
|
|
15
|
+
*/
|
|
16
|
+
clearScreen(): void;
|
|
17
|
+
/**
|
|
18
|
+
* @inheritDoc
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* @inheritDoc
|
|
22
|
+
*/
|
|
23
|
+
error(msg: string): void;
|
|
24
|
+
/**
|
|
25
|
+
* @inheritDoc
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* @inheritDoc
|
|
29
|
+
*/
|
|
30
|
+
hasErrorLogged(): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* @inheritDoc
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* @inheritDoc
|
|
36
|
+
*/
|
|
37
|
+
info(msg: string): void;
|
|
38
|
+
/**
|
|
39
|
+
* @inheritDoc
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* @inheritDoc
|
|
43
|
+
*/
|
|
44
|
+
warn(msg: string): void;
|
|
45
|
+
/**
|
|
46
|
+
* @inheritDoc
|
|
47
|
+
*/
|
|
48
|
+
/**
|
|
49
|
+
* @inheritDoc
|
|
50
|
+
*/
|
|
51
|
+
warnOnce(msg: string): void;
|
|
52
|
+
}
|
|
53
|
+
export { Logger as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sources":["../../src/services/logger.ts"],"sourcesContent":["import type { Logger as ViteLogger } from 'vite';\n\n/**\n * Default production logger\n */\nclass Logger implements ViteLogger {\n /**\n * @inheritDoc\n */\n hasWarned: boolean;\n\n /**\n * @inheritDoc\n */\n clearScreen(): void {\n //\n }\n\n /**\n * @inheritDoc\n */\n error(msg: string): void {\n console.error(msg);\n }\n\n /**\n * @inheritDoc\n */\n hasErrorLogged(): boolean {\n return false;\n }\n\n /**\n * @inheritDoc\n */\n info(msg: string): void {\n console.info(msg);\n }\n\n /**\n * @inheritDoc\n */\n warn(msg: string): void {\n console.warn(msg);\n }\n\n /**\n * @inheritDoc\n */\n warnOnce(msg: string): void {\n console.warn(msg);\n }\n}\n\nexport default Logger;\n"],"names":["Logger","hasWarned","clearScreen","error","msg","console","hasErrorLogged","info","warn","warnOnce"],"mappings":"AAKA,MAAMA,EAIJC,UAKAC,cAEC,CAKDC,MAAMC,GACJC,QAAQF,MAAMC,EACf,CAKDE,iBACE,OAAO,CACR,CAKDC,KAAKH,GACHC,QAAQE,KAAKH,EACd,CAKDI,KAAKJ,GACHC,QAAQG,KAAKJ,EACd,CAKDK,SAASL,GACPC,QAAQG,KAAKJ,EACd"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Request } from 'express';
|
|
2
|
+
import { IEntrypointOptions } from "../node/entry.js";
|
|
3
|
+
import { TRender } from "../node/render.js";
|
|
4
|
+
import ServerConfig from "./server-config.js";
|
|
5
|
+
interface IPrepareServerEntrypointLoadOut<TAppProps = Record<string, any>> {
|
|
6
|
+
render: TRender;
|
|
7
|
+
onRequest?: IEntrypointOptions<TAppProps>['onRequest'];
|
|
8
|
+
onRouterReady?: IEntrypointOptions<TAppProps>['onRouterReady'];
|
|
9
|
+
onShellReady?: IEntrypointOptions<TAppProps>['onShellReady'];
|
|
10
|
+
onShellError?: IEntrypointOptions<TAppProps>['onShellError'];
|
|
11
|
+
onResponse?: IEntrypointOptions<TAppProps>['onResponse'];
|
|
12
|
+
onError?: IEntrypointOptions<TAppProps>['onError'];
|
|
13
|
+
getState?: IEntrypointOptions<TAppProps>['getState'];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Load server entrypoint and template
|
|
17
|
+
* DEV MODE: refresh entrypoint and template
|
|
18
|
+
*/
|
|
19
|
+
declare class PrepareServer {
|
|
20
|
+
/**
|
|
21
|
+
* Server configuration
|
|
22
|
+
*/
|
|
23
|
+
protected readonly config: ServerConfig;
|
|
24
|
+
/**
|
|
25
|
+
* Entrypoint resolved params
|
|
26
|
+
*/
|
|
27
|
+
protected entrypoint?: IPrepareServerEntrypointLoadOut;
|
|
28
|
+
/**
|
|
29
|
+
* Hook which calls after express server created
|
|
30
|
+
*/
|
|
31
|
+
protected onServerCreated?: IEntrypointOptions['onServerCreated'];
|
|
32
|
+
/**
|
|
33
|
+
* Html shell
|
|
34
|
+
*/
|
|
35
|
+
protected html: string;
|
|
36
|
+
/**
|
|
37
|
+
* @constructor
|
|
38
|
+
* @protected
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* @constructor
|
|
42
|
+
* @protected
|
|
43
|
+
*/
|
|
44
|
+
protected constructor(config: ServerConfig);
|
|
45
|
+
/**
|
|
46
|
+
* Init service
|
|
47
|
+
*/
|
|
48
|
+
/**
|
|
49
|
+
* Init service
|
|
50
|
+
*/
|
|
51
|
+
static init(config: ServerConfig): PrepareServer;
|
|
52
|
+
/**
|
|
53
|
+
* Resolve and return entrypoint params
|
|
54
|
+
*/
|
|
55
|
+
/**
|
|
56
|
+
* Resolve and return entrypoint params
|
|
57
|
+
*/
|
|
58
|
+
loadEntrypoint(): Promise<IPrepareServerEntrypointLoadOut>;
|
|
59
|
+
/**
|
|
60
|
+
* Load and return html shell
|
|
61
|
+
*/
|
|
62
|
+
/**
|
|
63
|
+
* Load and return html shell
|
|
64
|
+
*/
|
|
65
|
+
loadHtml(req: Request): Promise<[string, string]>;
|
|
66
|
+
/**
|
|
67
|
+
* Run server created hook
|
|
68
|
+
*/
|
|
69
|
+
/**
|
|
70
|
+
* Run server created hook
|
|
71
|
+
*/
|
|
72
|
+
onAppCreated(): Promise<PrepareServer>;
|
|
73
|
+
}
|
|
74
|
+
export { PrepareServer as default };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import t from"fs";import e from"path";class r{config;entrypoint;onServerCreated;html;constructor(t){this.config=t}static init(t){return new r(t)}async loadEntrypoint(){if(this.entrypoint&&this.config.isProd)return this.entrypoint;const{root:t,isProd:r,serverFile:o}=this.config.getParams(),n=e.resolve(`${t}/${o}`);let i;i=r?(await import(n)).default:(await this.config.getVite().ssrLoadModule(n,{fixStacktrace:!0})).default;const{render:s,initServer:a}=i,{onServerCreated:l,onRequest:h,onRouterReady:c,onShellReady:d,onResponse:p,onShellError:f,onError:g,getState:u}=await(a?.({config:this.config}))??{};return this.entrypoint={render:s,onRequest:h,onRouterReady:c,onShellReady:d,onShellError:f,onResponse:p,onError:g,getState:u},this.onServerCreated=l,this.entrypoint}async loadHtml(r){const{isProd:o,root:n,indexFile:i}=this.config.getParams();this.html&&o||(this.html=t.readFileSync(e.resolve(`${n}/${i}`),"utf-8"));let s=this.html;return o||(s=(await this.config.getVite().transformIndexHtml(r.originalUrl,this.html)).replace(/(<script.+)(>[\s\S]+injectIntoGlobalHook.+)/,"$1async$2")),s.split("\x3c!--ssr-outlet--\x3e")}async onAppCreated(){return await this.loadEntrypoint(),await(this.onServerCreated?.(this.config.getApp())),this}}export{r as default};
|
|
2
|
+
//# sourceMappingURL=prepare-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prepare-server.js","sources":["../../src/services/prepare-server.ts"],"sourcesContent":["import fs from 'fs';\nimport path from 'path';\nimport type { Request } from 'express';\nimport type { IEntrypointOptions, IPrepareRenderOut } from '@node/entry';\nimport type { TRender } from '@node/render';\nimport type ServerConfig from '@services/server-config';\n\ninterface IPrepareServerEntrypointLoadOut<TAppProps = Record<string, any>> {\n render: TRender;\n onRequest?: IEntrypointOptions<TAppProps>['onRequest'];\n onRouterReady?: IEntrypointOptions<TAppProps>['onRouterReady'];\n onShellReady?: IEntrypointOptions<TAppProps>['onShellReady'];\n onShellError?: IEntrypointOptions<TAppProps>['onShellError'];\n onResponse?: IEntrypointOptions<TAppProps>['onResponse'];\n onError?: IEntrypointOptions<TAppProps>['onError'];\n getState?: IEntrypointOptions<TAppProps>['getState'];\n}\n\n/**\n * Load server entrypoint and template\n * DEV MODE: refresh entrypoint and template\n */\nclass PrepareServer {\n /**\n * Server configuration\n */\n protected readonly config: ServerConfig;\n\n /**\n * Entrypoint resolved params\n */\n protected entrypoint?: IPrepareServerEntrypointLoadOut;\n\n /**\n * Hook which calls after express server created\n */\n protected onServerCreated?: IEntrypointOptions['onServerCreated'];\n\n /**\n * Html shell\n */\n protected html: string;\n\n /**\n * @constructor\n * @protected\n */\n protected constructor(config: ServerConfig) {\n this.config = config;\n }\n\n /**\n * Init service\n */\n public static init(config: ServerConfig): PrepareServer {\n return new PrepareServer(config);\n }\n\n /**\n * Resolve and return entrypoint params\n */\n public async loadEntrypoint(): Promise<IPrepareServerEntrypointLoadOut> {\n // load server entrypoint each time only in development mode (for fast refresh)\n if (this.entrypoint && this.config.isProd) {\n return this.entrypoint;\n }\n\n const { root, isProd, serverFile } = this.config.getParams();\n const entrypointPath = path.resolve(`${root}/${serverFile}`);\n\n let resolvedEntrypoint: IPrepareRenderOut;\n\n if (!isProd) {\n resolvedEntrypoint = (\n await this.config.getVite()!.ssrLoadModule(entrypointPath, {\n fixStacktrace: true,\n })\n ).default;\n } else {\n resolvedEntrypoint = (await import(entrypointPath)).default;\n }\n\n const { render, initServer } = resolvedEntrypoint;\n const {\n onServerCreated,\n onRequest,\n onRouterReady,\n onShellReady,\n onResponse,\n onShellError,\n onError,\n getState,\n } =\n (await initServer?.({\n config: this.config,\n })) ?? {};\n\n this.entrypoint = {\n render,\n onRequest,\n onRouterReady,\n onShellReady,\n onShellError,\n onResponse,\n onError,\n getState,\n };\n this.onServerCreated = onServerCreated;\n\n return this.entrypoint;\n }\n\n /**\n * Load and return html shell\n */\n public async loadHtml(req: Request): Promise<[string, string]> {\n const { isProd, root, indexFile } = this.config.getParams();\n\n if (!this.html || !isProd) {\n this.html = fs.readFileSync(path.resolve(`${root}/${indexFile}`), 'utf-8');\n }\n\n let modifiedHtml = this.html;\n\n if (!isProd) {\n // Apply Vite HTML transforms. This injects the Vite HMR client,\n // and also applies HTML transforms from Vite plugins, e.g. global\n // preambles from @vitejs/plugin-react\n modifiedHtml = (await this.config.getVite()!.transformIndexHtml(req.originalUrl, this.html))\n // Make vite script 'async'\n .replace(/(<script.+)(>[\\s\\S]+injectIntoGlobalHook.+)/, '$1async$2');\n }\n\n return modifiedHtml.split('<!--ssr-outlet-->') as [string, string];\n }\n\n /**\n * Run server created hook\n */\n public async onAppCreated(): Promise<PrepareServer> {\n await this.loadEntrypoint();\n await this.onServerCreated?.(this.config.getApp()!);\n\n return this;\n }\n}\n\nexport default PrepareServer;\n"],"names":["PrepareServer","config","entrypoint","onServerCreated","html","constructor","this","static","async","isProd","root","serverFile","getParams","entrypointPath","path","resolve","resolvedEntrypoint","import","default","getVite","ssrLoadModule","fixStacktrace","render","initServer","onRequest","onRouterReady","onShellReady","onResponse","onShellError","onError","getState","req","indexFile","fs","readFileSync","modifiedHtml","transformIndexHtml","originalUrl","replace","split","loadEntrypoint","getApp"],"mappings":"sCAsBA,MAAMA,EAIeC,OAKTC,WAKAC,gBAKAC,KAMVC,YAAsBJ,GACpBK,KAAKL,OAASA,CACf,CAKMM,YAAYN,GACjB,OAAO,IAAID,EAAcC,EAC1B,CAKMO,uBAEL,GAAIF,KAAKJ,YAAcI,KAAKL,OAAOQ,OACjC,OAAOH,KAAKJ,WAGd,MAAMQ,KAAEA,EAAID,OAAEA,EAAME,WAAEA,GAAeL,KAAKL,OAAOW,YAC3CC,EAAiBC,EAAKC,QAAQ,GAAGL,KAAQC,KAE/C,IAAIK,EASFA,EAPGP,SAOyBQ,OAAOJ,IAAiBK,eAL5CZ,KAAKL,OAAOkB,UAAWC,cAAcP,EAAgB,CACzDQ,eAAe,KAEjBH,QAKJ,MAAMI,OAAEA,EAAMC,WAAEA,GAAeP,GACzBb,gBACJA,EAAeqB,UACfA,EAASC,cACTA,EAAaC,aACbA,EAAYC,WACZA,EAAUC,aACVA,EAAYC,QACZA,EAAOC,SACPA,SAEOP,IAAa,CAClBtB,OAAQK,KAAKL,WACR,CAAA,EAcT,OAZAK,KAAKJ,WAAa,CAChBoB,SACAE,YACAC,gBACAC,eACAE,eACAD,aACAE,UACAC,YAEFxB,KAAKH,gBAAkBA,EAEhBG,KAAKJ,UACb,CAKMM,eAAeuB,GACpB,MAAMtB,OAAEA,EAAMC,KAAEA,EAAIsB,UAAEA,GAAc1B,KAAKL,OAAOW,YAE3CN,KAAKF,MAASK,IACjBH,KAAKF,KAAO6B,EAAGC,aAAapB,EAAKC,QAAQ,GAAGL,KAAQsB,KAAc,UAGpE,IAAIG,EAAe7B,KAAKF,KAWxB,OATKK,IAIH0B,SAAsB7B,KAAKL,OAAOkB,UAAWiB,mBAAmBL,EAAIM,YAAa/B,KAAKF,OAEnFkC,QAAQ,8CAA+C,cAGrDH,EAAaI,MAAM,0BAC3B,CAKM/B,qBAIL,aAHMF,KAAKkC,uBACLlC,KAAKH,kBAAkBG,KAAKL,OAAOwC,WAElCnC,IACR"}
|