@lomray/vite-ssr-boost 2.5.2 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/node/entry.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Express, Request } from 'express';
2
2
  import { Response as ExpressResponse } from "express";
3
3
  import { FC, PropsWithChildren } from 'react';
4
+ import { Logger } from 'vite';
4
5
  import { TRouteObject } from "../interfaces/route-object.js";
5
6
  import { IRenderOptions, TRender } from "./render.js";
6
7
  import ServerApi from "../services/server-api.js";
@@ -24,6 +25,8 @@ interface IPrepareRenderOut<TAppProps = Record<string, any>> {
24
25
  init: IEntryServerOptions<TAppProps>['init'];
25
26
  routes: TRouteObject[];
26
27
  abortDelay?: number;
28
+ loggerProd?: Logger;
29
+ loggerDev?: Logger;
27
30
  }
28
31
  interface IAppServerProps<T = Record<string, any>> {
29
32
  server: T;
@@ -31,13 +34,14 @@ interface IAppServerProps<T = Record<string, any>> {
31
34
  type TApp<T> = FC<PropsWithChildren<Record<string, any> & IAppServerProps<T>>>;
32
35
  interface IEntryServerOptions<TAppProps = Record<string, any>> {
33
36
  abortDelay?: number;
34
- hasEarlyHints?: boolean;
35
37
  init?: (params: {
36
38
  config: ServerConfig;
37
39
  }) => IEntrypointOptions<TAppProps> | Promise<IEntrypointOptions<TAppProps>>;
40
+ loggerProd?: IPrepareRenderOut['loggerProd'];
41
+ loggerDev?: IPrepareRenderOut['loggerDev'];
38
42
  }
39
43
  /**
40
44
  * Render server side application
41
45
  */
42
- declare function entry<TAppProps>(App: TApp<TAppProps>, routes: TRouteObject[], { init, abortDelay }?: IEntryServerOptions<TAppProps>): IPrepareRenderOut<TAppProps>;
46
+ declare function entry<TAppProps>(App: TApp<TAppProps>, routes: TRouteObject[], { init, ...rest }?: IEntryServerOptions<TAppProps>): IPrepareRenderOut<TAppProps>;
43
47
  export { entry as default, IInitServerRequestOut, IEntrypointOptions, IPrepareRenderOut, IAppServerProps, TApp, IEntryServerOptions };
package/node/entry.js CHANGED
@@ -1,2 +1,2 @@
1
- import{createStaticHandler as r}from"react-router-dom/server.mjs";import e from"./render.js";function t(t,o,{init:n,abortDelay:a}={}){const i=r(o);return{render:e.bind(null,{handler:i,App:t}),init:n,routes:o,abortDelay:a}}export{t as default};
1
+ import{createStaticHandler as r}from"react-router-dom/server.mjs";import e from"./render.js";function t(t,n,{init:o,...i}={}){const d=r(n);return{render:e.bind(null,{handler:d,App:t}),init:o,routes:n,...i}}export{t as default};
2
2
  //# sourceMappingURL=entry.js.map
package/node/entry.js.map CHANGED
@@ -1 +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 { 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}\n\nexport interface IEntrypointOptions<TAppProps = Record<string, any>> {\n onServerCreated?: (app: Express, serverApi: ServerApi) => 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}\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 hasEarlyHints?: boolean;\n init?: (params: {\n config: ServerConfig;\n }) => IEntrypointOptions<TAppProps> | Promise<IEntrypointOptions<TAppProps>>;\n}\n\n/**\n * Render server side application\n */\nfunction entry<TAppProps>(\n App: TApp<TAppProps>,\n routes: TRouteObject[],\n { init, abortDelay }: IEntryServerOptions<TAppProps> = {},\n): IPrepareRenderOut<TAppProps> {\n const handler = createStaticHandler(routes as RouteObject[]);\n\n return {\n render: render.bind(null, { handler, App } as IRenderParams<TAppProps>) as TRender,\n init,\n routes,\n abortDelay,\n };\n}\n\nexport default entry;\n"],"names":["entry","App","routes","init","abortDelay","handler","createStaticHandler","render","bind"],"mappings":"6FAqDA,SAASA,EACPC,EACAC,GACAC,KAAEA,EAAIC,WAAEA,GAA+C,IAEvD,MAAMC,EAAUC,EAAoBJ,GAEpC,MAAO,CACLK,OAAQA,EAAOC,KAAK,KAAM,CAAEH,UAASJ,QACrCE,OACAD,SACAE,aAEJ"}
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 { 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}\n\nexport interface IEntrypointOptions<TAppProps = Record<string, any>> {\n onServerCreated?: (app: Express, serverApi: ServerApi) => 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}\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}\n\n/**\n * Render server side application\n */\nfunction entry<TAppProps>(\n App: TApp<TAppProps>,\n routes: TRouteObject[],\n { init, ...rest }: IEntryServerOptions<TAppProps> = {},\n): IPrepareRenderOut<TAppProps> {\n const handler = createStaticHandler(routes as RouteObject[]);\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","rest","handler","createStaticHandler","render","bind"],"mappings":"6FAyDA,SAASA,EACPC,EACAC,GACAC,KAAEA,KAASC,GAAyC,IAEpD,MAAMC,EAAUC,EAAoBJ,GAEpC,MAAO,CACLK,OAAQA,EAAOC,KAAK,KAAM,CAAEH,UAASJ,QACrCE,OACAD,YACGE,EAEP"}
package/node/server.js CHANGED
@@ -1,2 +1,2 @@
1
- import e from"node:http";import r from"node:https";import t from"path";import s from"compression";import o from"express";import i from"../helpers/print-server-info.js";import a from"../services/prepare-server.js";import n from"../services/server-api.js";async function p(p){const m=o().disable("x-powered-by"),c=new n;if(p.setApp(m),p.isProd){const{root:e,publicDir:r,isSPA:i}=p.getParams();m.use(s()),i||m.use(((e,r,t)=>{"/index.html"!==e.url||c.hasAccessIndexHtml()||(e.url="/index-not-found.html"),t()})),m.use(o.static(t.resolve(`${e}/${r}`),{index:!!i&&void 0}))}else{const e=await(await import("vite")).createServer({server:{middlewareMode:!0,watch:{usePolling:!0,interval:100}},appType:"custom",mode:p.mode});m.use(e.middlewares),p.setVite(e)}const l=a.init(p,c);return p.isSPA?m.use("*",((e,r,t)=>{(async()=>{try{const t=(await l.loadHtml(e)).join("");r.send(t)}catch(e){t(e)}})()})):(await l.onAppCreated(),m.use("*",((e,r,t)=>{(async()=>{try{const[{render:t,onRequest:s,...o},i]=await Promise.all([l.loadEntrypoint(),l.loadHtml(e)]),{appProps:a,hasEarlyHints:n}=await(s?.(e,r))??{},[m,c]=i,d={req:e,res:r,hasEarlyHints:n,appProps:a??{},html:{header:m,footer:c}};await t(p,d,o)}catch(e){t(e)}})()}))),{run:({version:t,isPrintInfo:s=!0}={})=>{const{port:o,host:a}=p.getParams(),n=Boolean(p.getVite()?.config?.server?.https);p.isHost&&!p.isProd&&(p.getVite().config.server.host=a);const c=(n?r.createServer(p.getVite().config.server.https,m):e.createServer(m)).listen(o,a,(()=>{s&&i(p,{version:t,server:c})}));return c},app:m}}export{p as default};
1
+ import e from"node:http";import r from"node:https";import t from"path";import s from"compression";import o from"express";import i from"../helpers/print-server-info.js";import a from"../services/prepare-server.js";import n from"../services/server-api.js";async function p(p){const l=o().disable("x-powered-by"),m=new n;if(p.setApp(l),p.isProd){const{root:e,publicDir:r,isSPA:i}=p.getParams();l.use(s()),i||l.use(((e,r,t)=>{"/index.html"!==e.url||m.hasAccessIndexHtml()||(e.url="/index-not-found.html"),t()})),l.use(o.static(t.resolve(`${e}/${r}`),{index:!!i&&void 0}))}else{const e=await(await import("vite")).createServer({server:{middlewareMode:!0,watch:{usePolling:!0,interval:100}},appType:"custom",mode:p.mode});l.use(e.middlewares),p.setVite(e)}const c=a.init(p,m);return p.isSPA?l.use("*",((e,r,t)=>{(async()=>{try{const t=(await c.loadHtml(e)).join("");r.send(t)}catch(e){p.getLogger().error("Failed to handle request",{error:e}),t()}})()})):(await c.onAppCreated(),l.use("*",((e,r,t)=>{(async()=>{try{const[{render:t,onRequest:s,...o},i]=await Promise.all([c.loadEntrypoint(),c.loadHtml(e)]),{appProps:a,hasEarlyHints:n}=await(s?.(e,r))??{},[l,m]=i,d={req:e,res:r,hasEarlyHints:n,appProps:a??{},html:{header:l,footer:m}};await t(p,d,o)}catch(e){p.getLogger().error("Failed to handle request",{error:e}),t()}})()}))),{run:({version:t,isPrintInfo:s=!0}={})=>{const{port:o,host:a}=p.getParams(),n=Boolean(p.getVite()?.config?.server?.https);p.isHost&&!p.isProd&&(p.getVite().config.server.host=a);const m=(n?r.createServer(p.getVite().config.server.https,l):e.createServer(l)).listen(o,a,(()=>{s&&i(p,{version:t,server:m})}));return m},app:l}}export{p as default};
2
2
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sources":["../../src/node/server.ts"],"sourcesContent":["import http from 'node:http';\nimport https from 'node:https';\nimport type { Server } from 'node:net';\nimport path from 'path';\nimport compression from 'compression';\nimport type { Express } from 'express';\nimport express from 'express';\nimport printServerInfo from '@helpers/print-server-info';\nimport type { IRequestContext } from '@node/render';\nimport PrepareServer from '@services/prepare-server';\nimport ServerApi from '@services/server-api';\nimport type ServerConfig from '@services/server-config';\n\nexport interface ICreateServerOut {\n run: (options?: { version?: string; isPrintInfo?: boolean }) => Server;\n app: Express;\n}\n\n/**\n * Create SSR server\n */\nasync function createServer(config: ServerConfig): Promise<ICreateServerOut> {\n const app = express().disable('x-powered-by');\n const serverApi = new ServerApi();\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 mode: config.mode,\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, _, next) => {\n if (req.url === '/index.html' && !serverApi.hasAccessIndexHtml()) {\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, serverApi);\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 [{ render, onRequest, ...renderParams }, clientHtml] = await Promise.all([\n prepareServer.loadEntrypoint(),\n prepareServer.loadHtml(req),\n ]);\n const { appProps, hasEarlyHints } = (await onRequest?.(req, res)) ?? {};\n const [header, footer] = clientHtml;\n\n const context: IRequestContext = {\n req,\n res,\n hasEarlyHints,\n appProps: appProps ?? {},\n html: { header, footer },\n };\n\n await render(config, context, renderParams);\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 const isHTTPS = Boolean(config.getVite()?.config?.server?.https);\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 = (\n isHTTPS\n ? https.createServer(config.getVite()!.config.server.https!, app)\n : http.createServer(app)\n ).listen(port, host, () => {\n if (!isPrintInfo) {\n return;\n }\n\n void printServerInfo(config, { version, server });\n });\n\n return server;\n },\n app,\n };\n}\n\nexport default createServer;\n"],"names":["async","createServer","config","app","express","disable","serverApi","ServerApi","setApp","isProd","root","publicDir","isSPA","getParams","use","compression","req","_","next","url","hasAccessIndexHtml","static","path","resolve","index","undefined","vite","import","server","middlewareMode","watch","usePolling","interval","appType","mode","middlewares","setVite","prepareServer","PrepareServer","init","res","html","loadHtml","join","send","e","onAppCreated","render","onRequest","renderParams","clientHtml","Promise","all","loadEntrypoint","appProps","hasEarlyHints","header","footer","context","run","version","isPrintInfo","port","host","isHTTPS","Boolean","getVite","https","isHost","http","listen","printServerInfo"],"mappings":"8PAqBAA,eAAeC,EAAaC,GAC1B,MAAMC,EAAMC,IAAUC,QAAQ,gBACxBC,EAAY,IAAIC,EAItB,GAFAL,EAAOM,OAAOL,GAETD,EAAOO,OAwBL,CACL,MAAMC,KAAEA,EAAIC,UAAEA,EAASC,MAAEA,GAAUV,EAAOW,YAE1CV,EAAIW,IAAIC,KAEHH,GAEHT,EAAIW,KAAI,CAACE,EAAKC,EAAGC,KACC,gBAAZF,EAAIG,KAA0Bb,EAAUc,uBAC1CJ,EAAIG,IAAM,yBAGZD,GAAM,IAIVf,EAAIW,IACFV,EAAQiB,OAAOC,EAAKC,QAAQ,GAAGb,KAAQC,KAAc,CACnDa,QAAOZ,QAAQa,IAGpB,KA7CmB,CAIlB,MAAMC,cACEC,OAAO,SACb1B,aAAa,CACb2B,OAAQ,CACNC,gBAAgB,EAChBC,MAAO,CAGLC,YAAY,EACZC,SAAU,MAGdC,QAAS,SACTC,KAAMhC,EAAOgC,OAIf/B,EAAIW,IAAIY,EAAKS,aAEbjC,EAAOkC,QAAQV,EAChB,CAuBD,MAAMW,EAAgBC,EAAcC,KAAKrC,EAAQI,GA6CjD,OA1CKJ,EAAOU,MA6BVT,EAAIW,IAAI,KAAK,CAACE,EAAKwB,EAAKtB,KACjB,WACH,IACE,MAAMuB,SAAcJ,EAAcK,SAAS1B,IAAM2B,KAAK,IAEtDH,EAAII,KAAKH,EACV,CAAC,MAAOI,GACP3B,EAAK2B,EACN,CACF,EARI,EAQD,WArCAR,EAAcS,eAEpB3C,EAAIW,IAAI,KAAK,CAACE,EAAKwB,EAAKtB,KACjB,WACH,IACE,OAAO6B,OAAEA,EAAMC,UAAEA,KAAcC,GAAgBC,SAAoBC,QAAQC,IAAI,CAC7Ef,EAAcgB,iBACdhB,EAAcK,SAAS1B,MAEnBsC,SAAEA,EAAQC,cAAEA,SAAyBP,IAAYhC,EAAKwB,KAAS,IAC9DgB,EAAQC,GAAUP,EAEnBQ,EAA2B,CAC/B1C,MACAwB,MACAe,gBACAD,SAAUA,GAAY,CAAE,EACxBb,KAAM,CAAEe,SAAQC,iBAGZV,EAAO7C,EAAQwD,EAAST,EAC/B,CAAC,MAAOJ,GACP3B,EAAK2B,EACN,CACF,EArBI,EAqBD,KAiBD,CACLc,IAAK,EAAGC,UAASC,eAAc,GAAS,CAAA,KACtC,MAAMC,KAAEA,EAAIC,KAAEA,GAAS7D,EAAOW,YACxBmD,EAAUC,QAAQ/D,EAAOgE,WAAWhE,QAAQ0B,QAAQuC,OAGtDjE,EAAOkE,SAAWlE,EAAOO,SAC3BP,EAAOgE,UAAWhE,OAAO0B,OAAOmC,KAAOA,GAGzC,MAAMnC,GACJoC,EACIG,EAAMlE,aAAaC,EAAOgE,UAAWhE,OAAO0B,OAAOuC,MAAQhE,GAC3DkE,EAAKpE,aAAaE,IACtBmE,OAAOR,EAAMC,GAAM,KACdF,GAIAU,EAAgBrE,EAAQ,CAAE0D,UAAShC,UAAS,IAGnD,OAAOA,CAAM,EAEfzB,MAEJ"}
1
+ {"version":3,"file":"server.js","sources":["../../src/node/server.ts"],"sourcesContent":["import http from 'node:http';\nimport https from 'node:https';\nimport type { Server } from 'node:net';\nimport path from 'path';\nimport compression from 'compression';\nimport type { Express } from 'express';\nimport express from 'express';\nimport printServerInfo from '@helpers/print-server-info';\nimport type { IRequestContext } from '@node/render';\nimport PrepareServer from '@services/prepare-server';\nimport ServerApi from '@services/server-api';\nimport type ServerConfig from '@services/server-config';\n\nexport interface ICreateServerOut {\n run: (options?: { version?: string; isPrintInfo?: boolean }) => Server;\n app: Express;\n}\n\n/**\n * Create SSR server\n */\nasync function createServer(config: ServerConfig): Promise<ICreateServerOut> {\n const app = express().disable('x-powered-by');\n const serverApi = new ServerApi();\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 mode: config.mode,\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, _, next) => {\n if (req.url === '/index.html' && !serverApi.hasAccessIndexHtml()) {\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, serverApi);\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 [{ render, onRequest, ...renderParams }, clientHtml] = await Promise.all([\n prepareServer.loadEntrypoint(),\n prepareServer.loadHtml(req),\n ]);\n const { appProps, hasEarlyHints } = (await onRequest?.(req, res)) ?? {};\n const [header, footer] = clientHtml;\n\n const context: IRequestContext = {\n req,\n res,\n hasEarlyHints,\n appProps: appProps ?? {},\n html: { header, footer },\n };\n\n await render(config, context, renderParams);\n } catch (e) {\n config.getLogger().error('Failed to handle request', { error: e as Error });\n next();\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 config.getLogger().error('Failed to handle request', { error: e as Error });\n next();\n }\n })();\n });\n }\n\n return {\n run: ({ version, isPrintInfo = true } = {}): Server => {\n const { port, host } = config.getParams();\n const isHTTPS = Boolean(config.getVite()?.config?.server?.https);\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 = (\n isHTTPS\n ? https.createServer(config.getVite()!.config.server.https!, app)\n : http.createServer(app)\n ).listen(port, host, () => {\n if (!isPrintInfo) {\n return;\n }\n\n void printServerInfo(config, { version, server });\n });\n\n return server;\n },\n app,\n };\n}\n\nexport default createServer;\n"],"names":["async","createServer","config","app","express","disable","serverApi","ServerApi","setApp","isProd","root","publicDir","isSPA","getParams","use","compression","req","_","next","url","hasAccessIndexHtml","static","path","resolve","index","undefined","vite","import","server","middlewareMode","watch","usePolling","interval","appType","mode","middlewares","setVite","prepareServer","PrepareServer","init","res","html","loadHtml","join","send","e","getLogger","error","onAppCreated","render","onRequest","renderParams","clientHtml","Promise","all","loadEntrypoint","appProps","hasEarlyHints","header","footer","context","run","version","isPrintInfo","port","host","isHTTPS","Boolean","getVite","https","isHost","http","listen","printServerInfo"],"mappings":"8PAqBAA,eAAeC,EAAaC,GAC1B,MAAMC,EAAMC,IAAUC,QAAQ,gBACxBC,EAAY,IAAIC,EAItB,GAFAL,EAAOM,OAAOL,GAETD,EAAOO,OAwBL,CACL,MAAMC,KAAEA,EAAIC,UAAEA,EAASC,MAAEA,GAAUV,EAAOW,YAE1CV,EAAIW,IAAIC,KAEHH,GAEHT,EAAIW,KAAI,CAACE,EAAKC,EAAGC,KACC,gBAAZF,EAAIG,KAA0Bb,EAAUc,uBAC1CJ,EAAIG,IAAM,yBAGZD,GAAM,IAIVf,EAAIW,IACFV,EAAQiB,OAAOC,EAAKC,QAAQ,GAAGb,KAAQC,KAAc,CACnDa,QAAOZ,QAAQa,IAGpB,KA7CmB,CAIlB,MAAMC,cACEC,OAAO,SACb1B,aAAa,CACb2B,OAAQ,CACNC,gBAAgB,EAChBC,MAAO,CAGLC,YAAY,EACZC,SAAU,MAGdC,QAAS,SACTC,KAAMhC,EAAOgC,OAIf/B,EAAIW,IAAIY,EAAKS,aAEbjC,EAAOkC,QAAQV,EAChB,CAuBD,MAAMW,EAAgBC,EAAcC,KAAKrC,EAAQI,GA+CjD,OA5CKJ,EAAOU,MA8BVT,EAAIW,IAAI,KAAK,CAACE,EAAKwB,EAAKtB,KACjB,WACH,IACE,MAAMuB,SAAcJ,EAAcK,SAAS1B,IAAM2B,KAAK,IAEtDH,EAAII,KAAKH,EACV,CAAC,MAAOI,GACP3C,EAAO4C,YAAYC,MAAM,2BAA4B,CAAEA,MAAOF,IAC9D3B,GACD,CACF,EATI,EASD,WAvCAmB,EAAcW,eAEpB7C,EAAIW,IAAI,KAAK,CAACE,EAAKwB,EAAKtB,KACjB,WACH,IACE,OAAO+B,OAAEA,EAAMC,UAAEA,KAAcC,GAAgBC,SAAoBC,QAAQC,IAAI,CAC7EjB,EAAckB,iBACdlB,EAAcK,SAAS1B,MAEnBwC,SAAEA,EAAQC,cAAEA,SAAyBP,IAAYlC,EAAKwB,KAAS,IAC9DkB,EAAQC,GAAUP,EAEnBQ,EAA2B,CAC/B5C,MACAwB,MACAiB,gBACAD,SAAUA,GAAY,CAAE,EACxBf,KAAM,CAAEiB,SAAQC,iBAGZV,EAAO/C,EAAQ0D,EAAST,EAC/B,CAAC,MAAON,GACP3C,EAAO4C,YAAYC,MAAM,2BAA4B,CAAEA,MAAOF,IAC9D3B,GACD,CACF,EAtBI,EAsBD,KAkBD,CACL2C,IAAK,EAAGC,UAASC,eAAc,GAAS,CAAA,KACtC,MAAMC,KAAEA,EAAIC,KAAEA,GAAS/D,EAAOW,YACxBqD,EAAUC,QAAQjE,EAAOkE,WAAWlE,QAAQ0B,QAAQyC,OAGtDnE,EAAOoE,SAAWpE,EAAOO,SAC3BP,EAAOkE,UAAWlE,OAAO0B,OAAOqC,KAAOA,GAGzC,MAAMrC,GACJsC,EACIG,EAAMpE,aAAaC,EAAOkE,UAAWlE,OAAO0B,OAAOyC,MAAQlE,GAC3DoE,EAAKtE,aAAaE,IACtBqE,OAAOR,EAAMC,GAAM,KACdF,GAIAU,EAAgBvE,EAAQ,CAAE4D,UAASlC,UAAS,IAGnD,OAAOA,CAAM,EAEfzB,MAEJ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lomray/vite-ssr-boost",
3
- "version": "2.5.2",
3
+ "version": "2.6.0",
4
4
  "description": "Vite plugin for create awesome SSR or SPA applications on React.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -1,4 +1,19 @@
1
- import { Logger as ViteLogger } from 'vite';
1
+ import { LogErrorOptions, LogOptions } from 'vite';
2
+ import { Logger as ViteLogger } from "vite";
3
+ declare const LogLevels: {
4
+ error: number;
5
+ warn: number;
6
+ info: number;
7
+ };
8
+ interface ILoggerOptions {
9
+ logLevel?: number;
10
+ logFilter?: (params: ILogParams) => boolean;
11
+ }
12
+ interface ILogParams {
13
+ level: keyof typeof LogLevels;
14
+ msg?: string;
15
+ options?: LogErrorOptions;
16
+ }
2
17
  /**
3
18
  * Default production logger
4
19
  */
@@ -7,6 +22,22 @@ declare class Logger implements ViteLogger {
7
22
  * @inheritDoc
8
23
  */
9
24
  hasWarned: boolean;
25
+ /**
26
+ * Current log level
27
+ */
28
+ loglevel: number;
29
+ /**
30
+ * Custom log filter
31
+ * Return 'true' to skip output
32
+ */
33
+ logFilter: ILoggerOptions['logFilter'] | undefined;
34
+ /**
35
+ * @constructor
36
+ */
37
+ /**
38
+ * @constructor
39
+ */
40
+ constructor({ logFilter, logLevel }?: ILoggerOptions);
10
41
  /**
11
42
  * @inheritDoc
12
43
  */
@@ -14,13 +45,27 @@ declare class Logger implements ViteLogger {
14
45
  * @inheritDoc
15
46
  */
16
47
  clearScreen(): void;
48
+ /**
49
+ * Should we skip current log depends on current log level
50
+ */
51
+ /**
52
+ * Should we skip current log depends on current log level
53
+ */
54
+ protected shouldSkipLog(level: number): boolean;
55
+ /**
56
+ * Common log
57
+ */
58
+ /**
59
+ * Common log
60
+ */
61
+ protected log(params: ILogParams): void;
17
62
  /**
18
63
  * @inheritDoc
19
64
  */
20
65
  /**
21
66
  * @inheritDoc
22
67
  */
23
- error(msg: string): void;
68
+ error(msg: string, options?: LogErrorOptions): void;
24
69
  /**
25
70
  * @inheritDoc
26
71
  */
@@ -34,20 +79,20 @@ declare class Logger implements ViteLogger {
34
79
  /**
35
80
  * @inheritDoc
36
81
  */
37
- info(msg: string): void;
82
+ info(msg: string, options: LogOptions): void;
38
83
  /**
39
84
  * @inheritDoc
40
85
  */
41
86
  /**
42
87
  * @inheritDoc
43
88
  */
44
- warn(msg: string): void;
89
+ warn(msg: string, options: LogOptions): void;
45
90
  /**
46
91
  * @inheritDoc
47
92
  */
48
93
  /**
49
94
  * @inheritDoc
50
95
  */
51
- warnOnce(msg: string): void;
96
+ warnOnce(msg: string, options: LogOptions): void;
52
97
  }
53
98
  export { Logger as default };
@@ -1,2 +1,2 @@
1
- class r{hasWarned;clearScreen(){}error(r){console.error(r)}hasErrorLogged(){return!1}info(r){console.info(r)}warn(r){console.warn(r)}warnOnce(r){console.warn(r)}}export{r as default};
1
+ const o={error:1,warn:2,info:3};class l{hasWarned;loglevel;logFilter;constructor({logFilter:o,logLevel:l=3}={}){this.loglevel=l,this.logFilter=o}clearScreen(){}shouldSkipLog(o){return o>this.loglevel}log(l){const{level:e,msg:r,options:s}=l;if(this.shouldSkipLog(o[e])||this.logFilter?.(l))return;const t=[r,s?.error].filter(Boolean);console[e](...t)}error(o,l){this.log({msg:o,options:l,level:"error"})}hasErrorLogged(){return!1}info(o,l){this.log({msg:o,options:l,level:"info"})}warn(o,l){this.log({msg:o,options:l,level:"warn"})}warnOnce(o,l){this.warn(o,l)}}export{l as default};
2
2
  //# sourceMappingURL=logger.js.map
@@ -1 +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"}
1
+ {"version":3,"file":"logger.js","sources":["../../src/services/logger.ts"],"sourcesContent":["import type { LogErrorOptions, Logger as ViteLogger, LogOptions } from 'vite';\n\nconst LogLevels = {\n error: 1,\n warn: 2,\n info: 3,\n};\n\ninterface ILoggerOptions {\n logLevel?: number;\n logFilter?: (params: ILogParams) => boolean;\n}\n\ninterface ILogParams {\n level: keyof typeof LogLevels;\n msg?: string;\n options?: LogErrorOptions;\n}\n\n/**\n * Default production logger\n */\nclass Logger implements ViteLogger {\n /**\n * @inheritDoc\n */\n public hasWarned: boolean;\n\n /**\n * Current log level\n */\n public loglevel: number;\n\n /**\n * Custom log filter\n * Return 'true' to skip output\n */\n public logFilter: ILoggerOptions['logFilter'] | undefined;\n\n /**\n * @constructor\n */\n public constructor({ logFilter, logLevel = 3 }: ILoggerOptions = {}) {\n this.loglevel = logLevel;\n this.logFilter = logFilter;\n }\n\n /**\n * @inheritDoc\n */\n public clearScreen(): void {\n //\n }\n\n /**\n * Should we skip current log depends on current log level\n */\n protected shouldSkipLog(level: number): boolean {\n return level > this.loglevel;\n }\n\n /**\n * Common log\n */\n protected log(params: ILogParams): void {\n const { level, msg, options } = params;\n\n if (this.shouldSkipLog(LogLevels[level]) || this.logFilter?.(params)) {\n return;\n }\n\n const args = [msg, options?.error].filter(Boolean);\n\n console[level](...args);\n }\n\n /**\n * @inheritDoc\n */\n public error(msg: string, options?: LogErrorOptions): void {\n this.log({ msg, options, level: 'error' });\n }\n\n /**\n * @inheritDoc\n */\n public hasErrorLogged(): boolean {\n return false;\n }\n\n /**\n * @inheritDoc\n */\n public info(msg: string, options: LogOptions): void {\n this.log({ msg, options, level: 'info' });\n }\n\n /**\n * @inheritDoc\n */\n public warn(msg: string, options: LogOptions): void {\n this.log({ msg, options, level: 'warn' });\n }\n\n /**\n * @inheritDoc\n */\n public warnOnce(msg: string, options: LogOptions): void {\n this.warn(msg, options);\n }\n}\n\nexport default Logger;\n"],"names":["LogLevels","error","warn","info","Logger","hasWarned","loglevel","logFilter","constructor","logLevel","this","clearScreen","shouldSkipLog","level","log","params","msg","options","args","filter","Boolean","console","hasErrorLogged","warnOnce"],"mappings":"AAEA,MAAMA,EAAY,CAChBC,MAAO,EACPC,KAAM,EACNC,KAAM,GAiBR,MAAMC,EAIGC,UAKAC,SAMAC,UAKPC,aAAmBD,UAAEA,EAASE,SAAEA,EAAW,GAAsB,CAAA,GAC/DC,KAAKJ,SAAWG,EAChBC,KAAKH,UAAYA,CAClB,CAKMI,cAEN,CAKSC,cAAcC,GACtB,OAAOA,EAAQH,KAAKJ,QACrB,CAKSQ,IAAIC,GACZ,MAAMF,MAAEA,EAAKG,IAAEA,EAAGC,QAAEA,GAAYF,EAEhC,GAAIL,KAAKE,cAAcZ,EAAUa,KAAWH,KAAKH,YAAYQ,GAC3D,OAGF,MAAMG,EAAO,CAACF,EAAKC,GAAShB,OAAOkB,OAAOC,SAE1CC,QAAQR,MAAUK,EACnB,CAKMjB,MAAMe,EAAaC,GACxBP,KAAKI,IAAI,CAAEE,MAAKC,UAASJ,MAAO,SACjC,CAKMS,iBACL,OAAO,CACR,CAKMnB,KAAKa,EAAaC,GACvBP,KAAKI,IAAI,CAAEE,MAAKC,UAASJ,MAAO,QACjC,CAKMX,KAAKc,EAAaC,GACvBP,KAAKI,IAAI,CAAEE,MAAKC,UAASJ,MAAO,QACjC,CAKMU,SAASP,EAAaC,GAC3BP,KAAKR,KAAKc,EAAKC,EAChB"}
@@ -1,2 +1,2 @@
1
- import t from"fs";import e from"node:process";import{pathToFileURL as r}from"node:url";import i from"path";import o from"chalk";import s from"./server-api.js";class n{config;serverApi;entrypoint;onServerCreated;html;constructor(t,e){this.config=t,this.serverApi=e??new s}static init(t,e){return new n(t,e)}async loadEntrypoint(t=!0){if(this.entrypoint&&this.config.isProd)return this.entrypoint;const{root:s,isProd:n,serverFile:a}=this.config.getParams(),l=i.resolve(`${s}/${a}`);let d;try{d=n?(await import(r(l).toString())).default:(await this.config.getVite().ssrLoadModule(l,{fixStacktrace:!0})).default}catch(t){if(t instanceof Error&&t.message.includes("Cannot find module")&&t.message.includes("/build/"))return this.config.getLogger().error(o.red(`Before starting the server, you need to create a build: ${o.yellow("ssr-boost build")} or provide path to build dir: ${o.yellow("ssr-boost start --build-dir build")}`)),e.exit(1);throw t}!t&&d.init&&delete d.init;const{render:c,init:h,routes:p,abortDelay:f}=d,{onServerCreated:m,...u}=await(h?.({config:this.config}))??{};return this.entrypoint={render:c,routes:p,abortDelay:f,...u},this.onServerCreated=m,this.entrypoint}async loadHtml(e){const{isProd:r,root:o,indexFile:s}=this.config.getParams();this.html&&r||(this.html=t.readFileSync(i.resolve(`${o}/${s}`),"utf-8"));let n=this.html;return r||(n=(await this.config.getVite().transformIndexHtml(e.originalUrl,this.html)).replace(/(<script.+)(>[\s\S]+injectIntoGlobalHook.+)/,"$1async$2")),n.split("\x3c!--ssr-outlet--\x3e")}async onAppCreated(){return await this.loadEntrypoint(),await(this.onServerCreated?.(this.config.getApp(),this.serverApi)),this}}export{n as default};
1
+ import t from"fs";import e from"node:process";import{pathToFileURL as r}from"node:url";import i from"path";import o from"chalk";import s from"./server-api.js";class n{config;serverApi;entrypoint;onServerCreated;html;constructor(t,e){this.config=t,this.serverApi=e??new s}static init(t,e){return new n(t,e)}async loadEntrypoint(t=!0){if(this.entrypoint&&this.config.isProd)return this.entrypoint;const{root:s,isProd:n,serverFile:a}=this.config.getParams(),l=i.resolve(`${s}/${a}`);let c;try{c=n?(await import(r(l).toString())).default:(await this.config.getVite().ssrLoadModule(l,{fixStacktrace:!0})).default}catch(t){if(t instanceof Error&&t.message.includes("Cannot find module")&&t.message.includes("/build/"))return this.config.getLogger().error(o.red(`Before starting the server, you need to create a build: ${o.yellow("ssr-boost build")} or provide path to build dir: ${o.yellow("ssr-boost start --build-dir build")}`)),e.exit(1);throw t}!t&&c.init&&delete c.init;const{render:d,init:h,routes:g,abortDelay:f,loggerProd:p,loggerDev:m}=c;p&&n?this.config.setLogger(p):m&&!n&&this.config.setLogger(m);const{onServerCreated:u,...y}=await(h?.({config:this.config}))??{};return this.entrypoint={render:d,routes:g,abortDelay:f,...y},this.onServerCreated=u,this.entrypoint}async loadHtml(e){const{isProd:r,root:o,indexFile:s}=this.config.getParams();this.html&&r||(this.html=t.readFileSync(i.resolve(`${o}/${s}`),"utf-8"));let n=this.html;return r||(n=(await this.config.getVite().transformIndexHtml(e.originalUrl,this.html)).replace(/(<script.+)(>[\s\S]+injectIntoGlobalHook.+)/,"$1async$2")),n.split("\x3c!--ssr-outlet--\x3e")}async onAppCreated(){return await this.loadEntrypoint(),await(this.onServerCreated?.(this.config.getApp(),this.serverApi)),this}}export{n as default};
2
2
  //# sourceMappingURL=prepare-server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"prepare-server.js","sources":["../../src/services/prepare-server.ts"],"sourcesContent":["import fs from 'fs';\nimport process from 'node:process';\nimport { pathToFileURL } from 'node:url';\nimport path from 'path';\nimport chalk from 'chalk';\nimport type { Request } from 'express';\nimport type { TRouteObject } from '@interfaces/route-object';\nimport type { IEntrypointOptions, IPrepareRenderOut } from '@node/entry';\nimport type { TRender } from '@node/render';\nimport ServerApi from '@services/server-api';\nimport type ServerConfig from '@services/server-config';\n\ninterface IPrepareServerEntrypointLoadOut<TAppProps = Record<string, any>> {\n render: TRender;\n routes: TRouteObject[];\n abortDelay?: number;\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 * Server API\n */\n protected readonly serverApi: ServerApi;\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 */\n protected constructor(config: ServerConfig, serverApi?: ServerApi) {\n this.config = config;\n this.serverApi = serverApi ?? new ServerApi();\n }\n\n /**\n * Init service\n */\n public static init(config: ServerConfig, serverApi?: ServerApi): PrepareServer {\n return new PrepareServer(config, serverApi);\n }\n\n /**\n * Resolve and return entrypoint params\n */\n public async loadEntrypoint(shouldInit = true): 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 try {\n if (!isProd) {\n resolvedEntrypoint = (\n await this.config.getVite()!.ssrLoadModule(entrypointPath, {\n fixStacktrace: true,\n })\n ).default as IPrepareRenderOut;\n } else {\n resolvedEntrypoint = (\n (await import(pathToFileURL(entrypointPath).toString())) as { default: IPrepareRenderOut }\n ).default;\n }\n } catch (e) {\n if (\n e instanceof Error &&\n e.message.includes('Cannot find module') &&\n e.message.includes('/build/')\n ) {\n this.config\n .getLogger()\n .error(\n chalk.red(\n `Before starting the server, you need to create a build: ${chalk.yellow(\n 'ssr-boost build',\n )} or provide path to build dir: ${chalk.yellow(\n 'ssr-boost start --build-dir build',\n )}`,\n ),\n );\n\n return process.exit(1);\n }\n\n throw e;\n }\n\n if (!shouldInit && resolvedEntrypoint.init) {\n delete resolvedEntrypoint.init;\n }\n\n const { render, init, routes, abortDelay } = resolvedEntrypoint;\n const { onServerCreated, ...renderParams } =\n (await init?.({\n config: this.config,\n })) ?? {};\n\n this.entrypoint = {\n render,\n routes,\n abortDelay,\n ...renderParams,\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()!, this.serverApi);\n\n return this;\n }\n}\n\nexport default PrepareServer;\n"],"names":["PrepareServer","config","serverApi","entrypoint","onServerCreated","html","constructor","this","ServerApi","static","async","shouldInit","isProd","root","serverFile","getParams","entrypointPath","path","resolve","resolvedEntrypoint","import","pathToFileURL","toString","default","getVite","ssrLoadModule","fixStacktrace","e","Error","message","includes","getLogger","error","chalk","red","yellow","process","exit","init","render","routes","abortDelay","renderParams","req","indexFile","fs","readFileSync","modifiedHtml","transformIndexHtml","originalUrl","replace","split","loadEntrypoint","getApp"],"mappings":"+JA6BA,MAAMA,EAIeC,OAKAC,UAKTC,WAKAC,gBAKAC,KAKVC,YAAsBL,EAAsBC,GAC1CK,KAAKN,OAASA,EACdM,KAAKL,UAAYA,GAAa,IAAIM,CACnC,CAKMC,YAAYR,EAAsBC,GACvC,OAAO,IAAIF,EAAcC,EAAQC,EAClC,CAKMQ,qBAAqBC,GAAa,GAEvC,GAAIJ,KAAKJ,YAAcI,KAAKN,OAAOW,OACjC,OAAOL,KAAKJ,WAGd,MAAMU,KAAEA,EAAID,OAAEA,EAAME,WAAEA,GAAeP,KAAKN,OAAOc,YAC3CC,EAAiBC,EAAKC,QAAQ,GAAGL,KAAQC,KAE/C,IAAIK,EAEJ,IAQIA,EAPGP,SAQMQ,OAAOC,EAAcL,GAAgBM,aAC5CC,eAPMhB,KAAKN,OAAOuB,UAAWC,cAAcT,EAAgB,CACzDU,eAAe,KAEjBH,OAML,CAAC,MAAOI,GACP,GACEA,aAAaC,OACbD,EAAEE,QAAQC,SAAS,uBACnBH,EAAEE,QAAQC,SAAS,WAcnB,OAZAvB,KAAKN,OACF8B,YACAC,MACCC,EAAMC,IACJ,2DAA2DD,EAAME,OAC/D,oDACiCF,EAAME,OACvC,yCAKDC,EAAQC,KAAK,GAGtB,MAAMV,CACP,EAEIhB,GAAcQ,EAAmBmB,aAC7BnB,EAAmBmB,KAG5B,MAAMC,OAAEA,EAAMD,KAAEA,EAAIE,OAAEA,EAAMC,WAAEA,GAAetB,GACvCf,gBAAEA,KAAoBsC,SACnBJ,IAAO,CACZrC,OAAQM,KAAKN,WACR,CAAA,EAUT,OARAM,KAAKJ,WAAa,CAChBoC,SACAC,SACAC,gBACGC,GAELnC,KAAKH,gBAAkBA,EAEhBG,KAAKJ,UACb,CAKMO,eAAeiC,GACpB,MAAM/B,OAAEA,EAAMC,KAAEA,EAAI+B,UAAEA,GAAcrC,KAAKN,OAAOc,YAE3CR,KAAKF,MAASO,IACjBL,KAAKF,KAAOwC,EAAGC,aAAa7B,EAAKC,QAAQ,GAAGL,KAAQ+B,KAAc,UAGpE,IAAIG,EAAexC,KAAKF,KAWxB,OATKO,IAIHmC,SAAsBxC,KAAKN,OAAOuB,UAAWwB,mBAAmBL,EAAIM,YAAa1C,KAAKF,OAEnF6C,QAAQ,8CAA+C,cAGrDH,EAAaI,MAAM,0BAC3B,CAKMzC,qBAIL,aAHMH,KAAK6C,uBACL7C,KAAKH,kBAAkBG,KAAKN,OAAOoD,SAAW9C,KAAKL,YAElDK,IACR"}
1
+ {"version":3,"file":"prepare-server.js","sources":["../../src/services/prepare-server.ts"],"sourcesContent":["import fs from 'fs';\nimport process from 'node:process';\nimport { pathToFileURL } from 'node:url';\nimport path from 'path';\nimport chalk from 'chalk';\nimport type { Request } from 'express';\nimport type { TRouteObject } from '@interfaces/route-object';\nimport type { IEntrypointOptions, IPrepareRenderOut } from '@node/entry';\nimport type { TRender } from '@node/render';\nimport ServerApi from '@services/server-api';\nimport type ServerConfig from '@services/server-config';\n\ninterface IPrepareServerEntrypointLoadOut<TAppProps = Record<string, any>> {\n render: TRender;\n routes: TRouteObject[];\n abortDelay?: number;\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 * Server API\n */\n protected readonly serverApi: ServerApi;\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 */\n protected constructor(config: ServerConfig, serverApi?: ServerApi) {\n this.config = config;\n this.serverApi = serverApi ?? new ServerApi();\n }\n\n /**\n * Init service\n */\n public static init(config: ServerConfig, serverApi?: ServerApi): PrepareServer {\n return new PrepareServer(config, serverApi);\n }\n\n /**\n * Resolve and return entrypoint params\n */\n public async loadEntrypoint(shouldInit = true): 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 try {\n if (!isProd) {\n resolvedEntrypoint = (\n await this.config.getVite()!.ssrLoadModule(entrypointPath, {\n fixStacktrace: true,\n })\n ).default as IPrepareRenderOut;\n } else {\n resolvedEntrypoint = (\n (await import(pathToFileURL(entrypointPath).toString())) as { default: IPrepareRenderOut }\n ).default;\n }\n } catch (e) {\n if (\n e instanceof Error &&\n e.message.includes('Cannot find module') &&\n e.message.includes('/build/')\n ) {\n this.config\n .getLogger()\n .error(\n chalk.red(\n `Before starting the server, you need to create a build: ${chalk.yellow(\n 'ssr-boost build',\n )} or provide path to build dir: ${chalk.yellow(\n 'ssr-boost start --build-dir build',\n )}`,\n ),\n );\n\n return process.exit(1);\n }\n\n throw e;\n }\n\n if (!shouldInit && resolvedEntrypoint.init) {\n delete resolvedEntrypoint.init;\n }\n\n const { render, init, routes, abortDelay, loggerProd, loggerDev } = resolvedEntrypoint;\n\n if (loggerProd && isProd) {\n this.config.setLogger(loggerProd);\n } else if (loggerDev && !isProd) {\n this.config.setLogger(loggerDev);\n }\n\n const { onServerCreated, ...renderParams } =\n (await init?.({\n config: this.config,\n })) ?? {};\n\n this.entrypoint = {\n render,\n routes,\n abortDelay,\n ...renderParams,\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()!, this.serverApi);\n\n return this;\n }\n}\n\nexport default PrepareServer;\n"],"names":["PrepareServer","config","serverApi","entrypoint","onServerCreated","html","constructor","this","ServerApi","static","async","shouldInit","isProd","root","serverFile","getParams","entrypointPath","path","resolve","resolvedEntrypoint","import","pathToFileURL","toString","default","getVite","ssrLoadModule","fixStacktrace","e","Error","message","includes","getLogger","error","chalk","red","yellow","process","exit","init","render","routes","abortDelay","loggerProd","loggerDev","setLogger","renderParams","req","indexFile","fs","readFileSync","modifiedHtml","transformIndexHtml","originalUrl","replace","split","loadEntrypoint","getApp"],"mappings":"+JA6BA,MAAMA,EAIeC,OAKAC,UAKTC,WAKAC,gBAKAC,KAKVC,YAAsBL,EAAsBC,GAC1CK,KAAKN,OAASA,EACdM,KAAKL,UAAYA,GAAa,IAAIM,CACnC,CAKMC,YAAYR,EAAsBC,GACvC,OAAO,IAAIF,EAAcC,EAAQC,EAClC,CAKMQ,qBAAqBC,GAAa,GAEvC,GAAIJ,KAAKJ,YAAcI,KAAKN,OAAOW,OACjC,OAAOL,KAAKJ,WAGd,MAAMU,KAAEA,EAAID,OAAEA,EAAME,WAAEA,GAAeP,KAAKN,OAAOc,YAC3CC,EAAiBC,EAAKC,QAAQ,GAAGL,KAAQC,KAE/C,IAAIK,EAEJ,IAQIA,EAPGP,SAQMQ,OAAOC,EAAcL,GAAgBM,aAC5CC,eAPMhB,KAAKN,OAAOuB,UAAWC,cAAcT,EAAgB,CACzDU,eAAe,KAEjBH,OAML,CAAC,MAAOI,GACP,GACEA,aAAaC,OACbD,EAAEE,QAAQC,SAAS,uBACnBH,EAAEE,QAAQC,SAAS,WAcnB,OAZAvB,KAAKN,OACF8B,YACAC,MACCC,EAAMC,IACJ,2DAA2DD,EAAME,OAC/D,oDACiCF,EAAME,OACvC,yCAKDC,EAAQC,KAAK,GAGtB,MAAMV,CACP,EAEIhB,GAAcQ,EAAmBmB,aAC7BnB,EAAmBmB,KAG5B,MAAMC,OAAEA,EAAMD,KAAEA,EAAIE,OAAEA,EAAMC,WAAEA,EAAUC,WAAEA,EAAUC,UAAEA,GAAcxB,EAEhEuB,GAAc9B,EAChBL,KAAKN,OAAO2C,UAAUF,GACbC,IAAc/B,GACvBL,KAAKN,OAAO2C,UAAUD,GAGxB,MAAMvC,gBAAEA,KAAoByC,SACnBP,IAAO,CACZrC,OAAQM,KAAKN,WACR,CAAA,EAUT,OARAM,KAAKJ,WAAa,CAChBoC,SACAC,SACAC,gBACGI,GAELtC,KAAKH,gBAAkBA,EAEhBG,KAAKJ,UACb,CAKMO,eAAeoC,GACpB,MAAMlC,OAAEA,EAAMC,KAAEA,EAAIkC,UAAEA,GAAcxC,KAAKN,OAAOc,YAE3CR,KAAKF,MAASO,IACjBL,KAAKF,KAAO2C,EAAGC,aAAahC,EAAKC,QAAQ,GAAGL,KAAQkC,KAAc,UAGpE,IAAIG,EAAe3C,KAAKF,KAWxB,OATKO,IAIHsC,SAAsB3C,KAAKN,OAAOuB,UAAW2B,mBAAmBL,EAAIM,YAAa7C,KAAKF,OAEnFgD,QAAQ,8CAA+C,cAGrDH,EAAaI,MAAM,0BAC3B,CAKM5C,qBAIL,aAHMH,KAAKgD,uBACLhD,KAAKH,kBAAkBG,KAAKN,OAAOuD,SAAWjD,KAAKL,YAElDK,IACR"}
@@ -149,5 +149,12 @@ declare class ServerConfig {
149
149
  * Get server logger
150
150
  */
151
151
  getLogger(): Logger;
152
+ /**
153
+ * Set custom logger
154
+ */
155
+ /**
156
+ * Set custom logger
157
+ */
158
+ setLogger(logger: Logger): void;
152
159
  }
153
160
  export { ServerConfig as default };
@@ -1,2 +1,2 @@
1
- import i from"node:fs";import t from"node:path";import s from"../helpers/plugin-config.js";import e from"./logger.js";class r{isProd;isHost;isModulePreload;isSPA;mode;vite;app;params;prodParams;logger;defaultBuildRoots=["./build","./dist"];constructor({isProd:i=!1,isHost:t=!1,isOnlyClient:s=!1,isModulePreload:e=!1,mode:r="production"},o){this.isProd=i,this.isHost=t,this.isSPA=s,this.isModulePreload=e,this.mode=r,this.prodParams={publicDir:"/client",indexFile:"/client/index.html",serverFile:"/server/server.js",host:"127.0.0.1",port:3e3,...o},this.makeParams()}static init(i={},t={}){return new r(i,t)}getBuildDir(t){for(const s of[t,...this.defaultBuildRoots])if(s&&i.existsSync(s))return s;return t??this.defaultBuildRoots[0]}makeParams(){const i=this.getPluginConfig()??{},{config:s}=this.vite??{},r=s?.root??this.getBuildDir(this.prodParams.root),o=s?.publicDir??this.prodParams.publicDir,a=new URL(import.meta.url),l=i.pluginPath??t.resolve(t.dirname(a.pathname),"../"),h=i.indexFile??this.prodParams.indexFile,d=i.serverFile??this.prodParams.serverFile,n=i.clientFile??this.prodParams.clientFile,p="boolean"==typeof s?.server.host||this.isHost?"0.0.0.0":s?.server.host??this.prodParams.host,m=s?.server.port??(this.isProd?this.prodParams.port:5173);this.params={root:r,publicDir:o,pluginPath:l,indexFile:h,clientFile:n,serverFile:d,host:p,port:m,isSPA:this.isSPA,isProd:this.isProd},this.logger=this.vite?.config.logger??new e}setVite(i){this.vite=i,this.makeParams()}setApp(i){this.app=i}getVite(){return this.vite}getApp(){return this.app}getPluginConfig(){return this.vite?s(this.vite.config):void 0}getParams(){return this.params}getLogger(){return this.logger}}export{r as default};
1
+ import i from"node:fs";import t from"node:path";import s from"../helpers/plugin-config.js";import e from"./logger.js";class r{isProd;isHost;isModulePreload;isSPA;mode;vite;app;params;prodParams;logger;defaultBuildRoots=["./build","./dist"];constructor({isProd:i=!1,isHost:t=!1,isOnlyClient:s=!1,isModulePreload:e=!1,mode:r="production"},o){this.isProd=i,this.isHost=t,this.isSPA=s,this.isModulePreload=e,this.mode=r,this.prodParams={publicDir:"/client",indexFile:"/client/index.html",serverFile:"/server/server.js",host:"127.0.0.1",port:3e3,...o},this.makeParams()}static init(i={},t={}){return new r(i,t)}getBuildDir(t){for(const s of[t,...this.defaultBuildRoots])if(s&&i.existsSync(s))return s;return t??this.defaultBuildRoots[0]}makeParams(){const i=this.getPluginConfig()??{},{config:s}=this.vite??{},r=s?.root??this.getBuildDir(this.prodParams.root),o=s?.publicDir??this.prodParams.publicDir,a=new URL(import.meta.url),l=i.pluginPath??t.resolve(t.dirname(a.pathname),"../"),h=i.indexFile??this.prodParams.indexFile,d=i.serverFile??this.prodParams.serverFile,n=i.clientFile??this.prodParams.clientFile,p="boolean"==typeof s?.server.host||this.isHost?"0.0.0.0":s?.server.host??this.prodParams.host,g=s?.server.port??(this.isProd?this.prodParams.port:5173);this.params={root:r,publicDir:o,pluginPath:l,indexFile:h,clientFile:n,serverFile:d,host:p,port:g,isSPA:this.isSPA,isProd:this.isProd},this.logger=this.vite?.config.logger??new e}setVite(i){this.vite=i,this.makeParams()}setApp(i){this.app=i}getVite(){return this.vite}getApp(){return this.app}getPluginConfig(){return this.vite?s(this.vite.config):void 0}getParams(){return this.params}getLogger(){return this.logger}setLogger(i){this.logger=i}}export{r as default};
2
2
  //# sourceMappingURL=server-config.js.map
@@ -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 { Express } from 'express';\nimport type { Logger, ViteDevServer } from 'vite';\nimport type { IPluginConfig } from '@helpers/plugin-config';\nimport getPluginConfig from '@helpers/plugin-config';\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}\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 * SPA mode\n */\n public readonly isSPA: boolean;\n\n /**\n * Env mode\n */\n public readonly mode: string;\n\n /**\n * Vite config - only for development\n */\n protected vite?: ViteDevServer;\n\n /**\n * Express application\n */\n protected app?: Express;\n\n /**\n * Config params\n */\n protected params: IConfigParams;\n\n /**\n * Default production params\n */\n protected prodParams: 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 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.isSPA = isOnlyClient;\n this.isModulePreload = isModulePreload;\n this.mode = mode;\n this.prodParams = {\n publicDir: '/client', // default for production,\n indexFile: '/client/index.html',\n serverFile: '/server/server.js',\n host: '127.0.0.1',\n port: 3000,\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 const pluginConfig = (this.getPluginConfig() ?? {}) as Partial<IPluginConfig>;\n const { config } = this.vite ?? {};\n\n const root = config?.root ?? this.getBuildDir(this.prodParams.root);\n const publicDir = config?.publicDir ?? this.prodParams.publicDir!;\n const dirInfo = new URL(import.meta.url);\n const pluginPath =\n pluginConfig.pluginPath ?? path.resolve(path.dirname(dirInfo.pathname), '../');\n const indexFile = pluginConfig.indexFile ?? this.prodParams.indexFile!;\n const serverFile = pluginConfig.serverFile ?? this.prodParams.serverFile!;\n const clientFile = pluginConfig.clientFile ?? this.prodParams.clientFile!;\n const host =\n typeof config?.server.host === 'boolean' || this.isHost\n ? '0.0.0.0'\n : config?.server.host ?? this.prodParams.host!;\n const port = config?.server.port ?? (this.isProd ? this.prodParams.port! : 5173);\n\n this.params = {\n root,\n publicDir,\n pluginPath,\n indexFile,\n clientFile,\n serverFile,\n host,\n port,\n isSPA: this.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(express: Express): void {\n this.app = express;\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(): Express | undefined {\n return this.app;\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\nexport default ServerConfig;\n"],"names":["ServerConfig","isProd","isHost","isModulePreload","isSPA","mode","vite","app","params","prodParams","logger","defaultBuildRoots","constructor","isOnlyClient","this","publicDir","indexFile","serverFile","host","port","makeParams","static","options","prodOptions","getBuildDir","root","dir","fs","existsSync","pluginConfig","getPluginConfig","config","dirInfo","URL","url","pluginPath","path","resolve","dirname","pathname","clientFile","server","DefaultLogger","setVite","setApp","express","getVite","getApp","undefined","getParams","getLogger"],"mappings":"sHAgCA,MAAMA,EAIYC,OAKAC,OAKAC,gBAKAC,MAKAC,KAKNC,KAKAC,IAKAC,OAKAC,WAKAC,OAKAC,kBAAoB,CAAC,UAAW,UAK1CC,aACEX,OACEA,GAAS,EAAKC,OACdA,GAAS,EAAKW,aACdA,GAAe,EAAKV,gBACpBA,GAAkB,EAAKE,KACvBA,EAAO,cAETI,GAEAK,KAAKb,OAASA,EACda,KAAKZ,OAASA,EACdY,KAAKV,MAAQS,EACbC,KAAKX,gBAAkBA,EACvBW,KAAKT,KAAOA,EACZS,KAAKL,WAAa,CAChBM,UAAW,UACXC,UAAW,qBACXC,WAAY,oBACZC,KAAM,YACNC,KAAM,OACHV,GAGLK,KAAKM,YACN,CAKMC,YACLC,EAA0B,GAC1BC,EAAsC,CAAA,GAEtC,OAAO,IAAIvB,EAAasB,EAASC,EAClC,CAKSC,YAAYC,GACpB,IAAK,MAAMC,IAAO,CAACD,KAASX,KAAKH,mBAC/B,GAAIe,GAAOC,EAAGC,WAAWF,GACvB,OAAOA,EAIX,OAAOD,GAAQX,KAAKH,kBAAkB,EACvC,CAKSS,aACR,MAAMS,EAAgBf,KAAKgB,mBAAqB,CAAE,GAC5CC,OAAEA,GAAWjB,KAAKR,MAAQ,CAAA,EAE1BmB,EAAOM,GAAQN,MAAQX,KAAKU,YAAYV,KAAKL,WAAWgB,MACxDV,EAAYgB,GAAQhB,WAAaD,KAAKL,WAAWM,UACjDiB,EAAU,IAAIC,gBAAgBC,KAC9BC,EACJN,EAAaM,YAAcC,EAAKC,QAAQD,EAAKE,QAAQN,EAAQO,UAAW,OACpEvB,EAAYa,EAAab,WAAaF,KAAKL,WAAWO,UACtDC,EAAaY,EAAaZ,YAAcH,KAAKL,WAAWQ,WACxDuB,EAAaX,EAAaW,YAAc1B,KAAKL,WAAW+B,WACxDtB,EAC2B,kBAAxBa,GAAQU,OAAOvB,MAAsBJ,KAAKZ,OAC7C,UACA6B,GAAQU,OAAOvB,MAAQJ,KAAKL,WAAWS,KACvCC,EAAOY,GAAQU,OAAOtB,OAASL,KAAKb,OAASa,KAAKL,WAAWU,KAAQ,MAE3EL,KAAKN,OAAS,CACZiB,OACAV,YACAoB,aACAnB,YACAwB,aACAvB,aACAC,OACAC,OACAf,MAAOU,KAAKV,MACZH,OAAQa,KAAKb,QAEfa,KAAKJ,OAASI,KAAKR,MAAMyB,OAAOrB,QAAU,IAAIgC,CAC/C,CAKMC,QAAQrC,GACbQ,KAAKR,KAAOA,EAEZQ,KAAKM,YACN,CAKMwB,OAAOC,GACZ/B,KAAKP,IAAMsC,CACZ,CAMMC,UACL,OAAOhC,KAAKR,IACb,CAKMyC,SACL,OAAOjC,KAAKP,GACb,CAMMuB,kBACL,OAAOhB,KAAKR,KAAOwB,EAAgBhB,KAAKR,KAAKyB,aAAUiB,CACxD,CAKMC,YACL,OAAOnC,KAAKN,MACb,CAKM0C,YACL,OAAOpC,KAAKJ,MACb"}
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 { Express } from 'express';\nimport type { Logger, ViteDevServer } from 'vite';\nimport type { IPluginConfig } from '@helpers/plugin-config';\nimport getPluginConfig from '@helpers/plugin-config';\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}\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 * SPA mode\n */\n public readonly isSPA: boolean;\n\n /**\n * Env mode\n */\n public readonly mode: string;\n\n /**\n * Vite config - only for development\n */\n protected vite?: ViteDevServer;\n\n /**\n * Express application\n */\n protected app?: Express;\n\n /**\n * Config params\n */\n protected params: IConfigParams;\n\n /**\n * Default production params\n */\n protected prodParams: 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 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.isSPA = isOnlyClient;\n this.isModulePreload = isModulePreload;\n this.mode = mode;\n this.prodParams = {\n publicDir: '/client', // default for production,\n indexFile: '/client/index.html',\n serverFile: '/server/server.js',\n host: '127.0.0.1',\n port: 3000,\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 const pluginConfig = (this.getPluginConfig() ?? {}) as Partial<IPluginConfig>;\n const { config } = this.vite ?? {};\n\n const root = config?.root ?? this.getBuildDir(this.prodParams.root);\n const publicDir = config?.publicDir ?? this.prodParams.publicDir!;\n const dirInfo = new URL(import.meta.url);\n const pluginPath =\n pluginConfig.pluginPath ?? path.resolve(path.dirname(dirInfo.pathname), '../');\n const indexFile = pluginConfig.indexFile ?? this.prodParams.indexFile!;\n const serverFile = pluginConfig.serverFile ?? this.prodParams.serverFile!;\n const clientFile = pluginConfig.clientFile ?? this.prodParams.clientFile!;\n const host =\n typeof config?.server.host === 'boolean' || this.isHost\n ? '0.0.0.0'\n : config?.server.host ?? this.prodParams.host!;\n const port = config?.server.port ?? (this.isProd ? this.prodParams.port! : 5173);\n\n this.params = {\n root,\n publicDir,\n pluginPath,\n indexFile,\n clientFile,\n serverFile,\n host,\n port,\n isSPA: this.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(express: Express): void {\n this.app = express;\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(): Express | undefined {\n return this.app;\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\nexport default ServerConfig;\n"],"names":["ServerConfig","isProd","isHost","isModulePreload","isSPA","mode","vite","app","params","prodParams","logger","defaultBuildRoots","constructor","isOnlyClient","this","publicDir","indexFile","serverFile","host","port","makeParams","static","options","prodOptions","getBuildDir","root","dir","fs","existsSync","pluginConfig","getPluginConfig","config","dirInfo","URL","url","pluginPath","path","resolve","dirname","pathname","clientFile","server","DefaultLogger","setVite","setApp","express","getVite","getApp","undefined","getParams","getLogger","setLogger"],"mappings":"sHAgCA,MAAMA,EAIYC,OAKAC,OAKAC,gBAKAC,MAKAC,KAKNC,KAKAC,IAKAC,OAKAC,WAKAC,OAKAC,kBAAoB,CAAC,UAAW,UAK1CC,aACEX,OACEA,GAAS,EAAKC,OACdA,GAAS,EAAKW,aACdA,GAAe,EAAKV,gBACpBA,GAAkB,EAAKE,KACvBA,EAAO,cAETI,GAEAK,KAAKb,OAASA,EACda,KAAKZ,OAASA,EACdY,KAAKV,MAAQS,EACbC,KAAKX,gBAAkBA,EACvBW,KAAKT,KAAOA,EACZS,KAAKL,WAAa,CAChBM,UAAW,UACXC,UAAW,qBACXC,WAAY,oBACZC,KAAM,YACNC,KAAM,OACHV,GAGLK,KAAKM,YACN,CAKMC,YACLC,EAA0B,GAC1BC,EAAsC,CAAA,GAEtC,OAAO,IAAIvB,EAAasB,EAASC,EAClC,CAKSC,YAAYC,GACpB,IAAK,MAAMC,IAAO,CAACD,KAASX,KAAKH,mBAC/B,GAAIe,GAAOC,EAAGC,WAAWF,GACvB,OAAOA,EAIX,OAAOD,GAAQX,KAAKH,kBAAkB,EACvC,CAKSS,aACR,MAAMS,EAAgBf,KAAKgB,mBAAqB,CAAE,GAC5CC,OAAEA,GAAWjB,KAAKR,MAAQ,CAAA,EAE1BmB,EAAOM,GAAQN,MAAQX,KAAKU,YAAYV,KAAKL,WAAWgB,MACxDV,EAAYgB,GAAQhB,WAAaD,KAAKL,WAAWM,UACjDiB,EAAU,IAAIC,gBAAgBC,KAC9BC,EACJN,EAAaM,YAAcC,EAAKC,QAAQD,EAAKE,QAAQN,EAAQO,UAAW,OACpEvB,EAAYa,EAAab,WAAaF,KAAKL,WAAWO,UACtDC,EAAaY,EAAaZ,YAAcH,KAAKL,WAAWQ,WACxDuB,EAAaX,EAAaW,YAAc1B,KAAKL,WAAW+B,WACxDtB,EAC2B,kBAAxBa,GAAQU,OAAOvB,MAAsBJ,KAAKZ,OAC7C,UACA6B,GAAQU,OAAOvB,MAAQJ,KAAKL,WAAWS,KACvCC,EAAOY,GAAQU,OAAOtB,OAASL,KAAKb,OAASa,KAAKL,WAAWU,KAAQ,MAE3EL,KAAKN,OAAS,CACZiB,OACAV,YACAoB,aACAnB,YACAwB,aACAvB,aACAC,OACAC,OACAf,MAAOU,KAAKV,MACZH,OAAQa,KAAKb,QAEfa,KAAKJ,OAASI,KAAKR,MAAMyB,OAAOrB,QAAU,IAAIgC,CAC/C,CAKMC,QAAQrC,GACbQ,KAAKR,KAAOA,EAEZQ,KAAKM,YACN,CAKMwB,OAAOC,GACZ/B,KAAKP,IAAMsC,CACZ,CAMMC,UACL,OAAOhC,KAAKR,IACb,CAKMyC,SACL,OAAOjC,KAAKP,GACb,CAMMuB,kBACL,OAAOhB,KAAKR,KAAOwB,EAAgBhB,KAAKR,KAAKyB,aAAUiB,CACxD,CAKMC,YACL,OAAOnC,KAAKN,MACb,CAKM0C,YACL,OAAOpC,KAAKJ,MACb,CAKMyC,UAAUzC,GACfI,KAAKJ,OAASA,CACf"}