@lomray/vite-ssr-boost 1.0.0-beta.2 → 1.0.0-beta.3

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.
@@ -1,20 +1,21 @@
1
1
  /// <reference types="react-dom" />
2
+ import { Router as RemixRouter } from '@remix-run/router/dist/router';
2
3
  import { FC, PropsWithChildren } from 'react';
3
4
  import ReactDOM from 'react-dom/client';
4
5
  import { RouteObject } from 'react-router-dom';
5
- interface IEntryClientOptions {
6
- routes: RouteObject[];
7
- }
8
6
  interface IAppClientProps<T = undefined> {
9
7
  client: T;
10
8
  }
11
9
  interface IInitPropsParams {
12
10
  isSSRMode: boolean;
11
+ router: RemixRouter;
13
12
  }
14
13
  type TApp<T> = FC<PropsWithChildren<IAppClientProps<T>>>;
15
- type TAppGetProps<T> = (params: IInitPropsParams) => Promise<T>;
14
+ interface IEntryClientOptions<T> {
15
+ init?: (params: IInitPropsParams) => Promise<T>;
16
+ }
16
17
  /**
17
18
  * Render client side application
18
19
  */
19
- declare function entry<TAppProps>({ routes }: IEntryClientOptions, App: TApp<TAppProps>, initProps?: TAppGetProps<TAppProps>): Promise<ReactDOM.Root | void>;
20
- export { entry as default, IEntryClientOptions, IAppClientProps, IInitPropsParams, TApp, TAppGetProps };
20
+ declare function entry<TAppProps>(App: TApp<TAppProps>, routes: RouteObject[], { init }?: IEntryClientOptions<TAppProps>): Promise<ReactDOM.Root | void>;
21
+ export { entry as default, IAppClientProps, IInitPropsParams, TApp, IEntryClientOptions };
package/browser/entry.js CHANGED
@@ -1,2 +1,2 @@
1
- import t from"react";import e from"react-dom/client";import{matchRoutes as o,createBrowserRouter as r,RouterProvider as a}from"react-router-dom";import{IS_SSR_MODE as n}from"../constants/common.js";async function c({routes:c},l,m){const i=o(c,window.location)?.filter((t=>t.route.lazy));i&&i?.length>0&&await Promise.all(i.map((async t=>{const e=await(t.route.lazy?.());Object.assign(t.route,{...e,lazy:void 0})})));const s=await(m?.({isSSRMode:n})),u=r(c),d=document.getElementById("root"),f=()=>t.createElement(l,{client:s},t.createElement(a,{router:u}));return n?e.hydrateRoot(d,t.createElement(f,null)):e.createRoot(d).render(t.createElement(f,null))}export{c as default};
1
+ import t from"react";import e from"react-dom/client";import{matchRoutes as o,createBrowserRouter as r,RouterProvider as a}from"react-router-dom";import{IS_SSR_MODE as n}from"../constants/common.js";async function c(c,l,{init:m}={}){const i=o(l,window.location)?.filter((t=>t.route.lazy));i&&i?.length>0&&await Promise.all(i.map((async t=>{const e=await(t.route.lazy?.());Object.assign(t.route,{...e,lazy:void 0})})));const s=r(l),u=document.getElementById("root"),d=await(m?.({isSSRMode:n,router:s})),f=()=>t.createElement(c,{client:d},t.createElement(a,{router:s}));return n?e.hydrateRoot(u,t.createElement(f,null)):e.createRoot(u).render(t.createElement(f,null))}export{c as default};
2
2
  //# sourceMappingURL=entry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"entry.js","sources":["../../src/browser/entry.tsx"],"sourcesContent":["import type { FC, PropsWithChildren } from 'react';\nimport React from 'react';\nimport ReactDOM from 'react-dom/client';\nimport type { RouteObject } from 'react-router-dom';\nimport { createBrowserRouter, matchRoutes, RouterProvider } from 'react-router-dom';\nimport { IS_SSR_MODE } from '@constants/common';\n\nexport interface IEntryClientOptions {\n routes: RouteObject[];\n}\n\nexport interface IAppClientProps<T = undefined> {\n client: T;\n}\n\nexport interface IInitPropsParams {\n isSSRMode: boolean;\n}\n\nexport type TApp<T> = FC<PropsWithChildren<IAppClientProps<T>>>;\n\nexport type TAppGetProps<T> = (params: IInitPropsParams) => Promise<T>;\n\n/**\n * Render client side application\n */\nasync function entry<TAppProps>(\n { routes }: IEntryClientOptions,\n App: TApp<TAppProps>,\n initProps?: TAppGetProps<TAppProps>,\n): Promise<ReactDOM.Root | void> {\n const lazyMatches = matchRoutes(routes, window.location)?.filter((m) => m.route.lazy);\n\n // Load the lazy matches and update the routes before creating router,\n // so we can hydrate the SSR-rendered content synchronously\n if (lazyMatches && lazyMatches?.length > 0) {\n await Promise.all(\n lazyMatches.map(async (m) => {\n const routeModule = await m.route.lazy?.();\n\n Object.assign(m.route, {\n ...routeModule,\n lazy: undefined,\n });\n }),\n );\n }\n\n const appProps = (await initProps?.({ isSSRMode: IS_SSR_MODE })) as TAppProps;\n const router = createBrowserRouter(routes);\n const root = document.getElementById('root') as HTMLElement;\n\n const AppComponent: FC = () => (\n <App client={appProps}>\n <RouterProvider router={router} />\n </App>\n );\n\n if (!IS_SSR_MODE) {\n return ReactDOM.createRoot(root).render(<AppComponent />);\n }\n\n return ReactDOM.hydrateRoot(root, <AppComponent />);\n}\n\nexport default entry;\n"],"names":["async","entry","routes","App","initProps","lazyMatches","matchRoutes","window","location","filter","m","route","lazy","length","Promise","all","map","routeModule","Object","assign","undefined","appProps","isSSRMode","IS_SSR_MODE","router","createBrowserRouter","root","document","getElementById","AppComponent","React","createElement","client","RouterProvider","ReactDOM","hydrateRoot","createRoot","render"],"mappings":"sMA0BAA,eAAeC,GACbC,OAAEA,GACFC,EACAC,GAEA,MAAMC,EAAcC,EAAYJ,EAAQK,OAAOC,WAAWC,QAAQC,GAAMA,EAAEC,MAAMC,OAI5EP,GAAeA,GAAaQ,OAAS,SACjCC,QAAQC,IACZV,EAAYW,KAAIhB,MAAOU,IACrB,MAAMO,QAAoBP,EAAEC,MAAMC,UAElCM,OAAOC,OAAOT,EAAEC,MAAO,IAClBM,EACHL,UAAMQ,GACN,KAKR,MAAMC,QAAkBjB,IAAY,CAAEkB,UAAWC,KAC3CC,EAASC,EAAoBvB,GAC7BwB,EAAOC,SAASC,eAAe,QAE/BC,EAAmB,IACvBC,EAAAC,cAAC5B,EAAG,CAAC6B,OAAQX,GACXS,EAACC,cAAAE,GAAeT,OAAQA,KAI5B,OAAKD,EAIEW,EAASC,YAAYT,EAAMI,EAACC,cAAAF,EAAe,OAHzCK,EAASE,WAAWV,GAAMW,OAAOP,EAAAC,cAACF,EAAY,MAIzD"}
1
+ {"version":3,"file":"entry.js","sources":["../../src/browser/entry.tsx"],"sourcesContent":["import type { Router as RemixRouter } from '@remix-run/router/dist/router';\nimport type { FC, PropsWithChildren } from 'react';\nimport React from 'react';\nimport ReactDOM from 'react-dom/client';\nimport type { RouteObject } from 'react-router-dom';\nimport { createBrowserRouter, matchRoutes, RouterProvider } from 'react-router-dom';\nimport { IS_SSR_MODE } from '@constants/common';\n\nexport interface IAppClientProps<T = undefined> {\n client: T;\n}\n\nexport interface IInitPropsParams {\n isSSRMode: boolean;\n router: RemixRouter;\n}\n\nexport type TApp<T> = FC<PropsWithChildren<IAppClientProps<T>>>;\n\nexport interface IEntryClientOptions<T> {\n init?: (params: IInitPropsParams) => Promise<T>;\n}\n\n/**\n * Render client side application\n */\nasync function entry<TAppProps>(\n App: TApp<TAppProps>,\n routes: RouteObject[],\n { init }: IEntryClientOptions<TAppProps> = {},\n): Promise<ReactDOM.Root | void> {\n const lazyMatches = matchRoutes(routes, window.location)?.filter((m) => m.route.lazy);\n\n // Load the lazy matches and update the routes before creating router,\n // so we can hydrate the SSR-rendered content synchronously\n if (lazyMatches && lazyMatches?.length > 0) {\n await Promise.all(\n lazyMatches.map(async (m) => {\n const routeModule = await m.route.lazy?.();\n\n Object.assign(m.route, {\n ...routeModule,\n lazy: undefined,\n });\n }),\n );\n }\n\n const router = createBrowserRouter(routes);\n const root = document.getElementById('root') as HTMLElement;\n const appProps = (await init?.({ isSSRMode: IS_SSR_MODE, router })) as TAppProps;\n\n const AppComponent: FC = () => (\n <App client={appProps}>\n <RouterProvider router={router} />\n </App>\n );\n\n if (!IS_SSR_MODE) {\n return ReactDOM.createRoot(root).render(<AppComponent />);\n }\n\n return ReactDOM.hydrateRoot(root, <AppComponent />);\n}\n\nexport default entry;\n"],"names":["async","entry","App","routes","init","lazyMatches","matchRoutes","window","location","filter","m","route","lazy","length","Promise","all","map","routeModule","Object","assign","undefined","router","createBrowserRouter","root","document","getElementById","appProps","isSSRMode","IS_SSR_MODE","AppComponent","React","createElement","client","RouterProvider","ReactDOM","hydrateRoot","createRoot","render"],"mappings":"sMA0BAA,eAAeC,EACbC,EACAC,GACAC,KAAEA,GAAyC,CAAA,GAE3C,MAAMC,EAAcC,EAAYH,EAAQI,OAAOC,WAAWC,QAAQC,GAAMA,EAAEC,MAAMC,OAI5EP,GAAeA,GAAaQ,OAAS,SACjCC,QAAQC,IACZV,EAAYW,KAAIhB,MAAOU,IACrB,MAAMO,QAAoBP,EAAEC,MAAMC,UAElCM,OAAOC,OAAOT,EAAEC,MAAO,IAClBM,EACHL,UAAMQ,GACN,KAKR,MAAMC,EAASC,EAAoBnB,GAC7BoB,EAAOC,SAASC,eAAe,QAC/BC,QAAkBtB,IAAO,CAAEuB,UAAWC,EAAaP,YAEnDQ,EAAmB,IACvBC,EAAAC,cAAC7B,EAAG,CAAC8B,OAAQN,GACXI,EAACC,cAAAE,GAAeZ,OAAQA,KAI5B,OAAKO,EAIEM,EAASC,YAAYZ,EAAMO,EAACC,cAAAF,EAAe,OAHzCK,EAASE,WAAWb,GAAMc,OAAOP,EAAAC,cAACF,EAAY,MAIzD"}
package/node/entry.d.ts CHANGED
@@ -19,22 +19,19 @@ interface IEntrypointOptions<TAppProps = Record<string, any>> {
19
19
  }
20
20
  interface IPrepareRenderOut<TAppProps = Record<string, any>> {
21
21
  render: TRender;
22
- initServer: IEntryServerOptions<TAppProps>['initServer'];
22
+ init: IEntryServerOptions<TAppProps>['init'];
23
23
  }
24
24
  interface IAppServerProps<T = Record<string, any>> {
25
- server: T & {
26
- req: Request;
27
- };
25
+ server: T;
28
26
  }
29
- type TApp<T> = FC<PropsWithChildren<IAppServerProps<T>>>;
27
+ type TApp<T> = FC<PropsWithChildren<Record<string, any> & IAppServerProps<T>>>;
30
28
  interface IEntryServerOptions<TAppProps = Record<string, any>> {
31
- routes: RouteObject[];
32
- initServer?: (params: {
29
+ init?: (params: {
33
30
  config: ServerConfig;
34
31
  }) => IEntrypointOptions<TAppProps> | Promise<IEntrypointOptions<TAppProps>>;
35
32
  }
36
33
  /**
37
34
  * Render server side application
38
35
  */
39
- declare function entry<TAppProps>({ routes, initServer }: IEntryServerOptions<TAppProps>, App: TApp<TAppProps>): IPrepareRenderOut<TAppProps>;
36
+ declare function entry<TAppProps>(App: TApp<TAppProps>, routes: RouteObject[], { init }?: IEntryServerOptions<TAppProps>): IPrepareRenderOut<TAppProps>;
40
37
  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({routes:t,initServer:n},o){const i=r(t);return{render:e.bind(null,{handler:i,App:o}),initServer:n}}export{t as default};
1
+ import{createStaticHandler as r}from"react-router-dom/server.mjs";import e from"./render.js";function n(n,t,{init:o}={}){const i=r(t);return{render:e.bind(null,{handler:i,App:n}),init:o}}export{n 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 { 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"}
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 init: IEntryServerOptions<TAppProps>['init'];\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 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: RouteObject[],\n { init }: IEntryServerOptions<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 init,\n };\n}\n\nexport default entry;\n"],"names":["entry","App","routes","init","handler","createStaticHandler","render","bind"],"mappings":"6FA8CA,SAASA,EACPC,EACAC,GACAC,KAAEA,GAAyC,CAAA,GAE3C,MAAMC,EAAUC,EAAoBH,GAEpC,MAAO,CACLI,OAAQA,EAAOC,KAAK,KAAM,CAAEH,UAASH,QACrCE,OAEJ"}
package/node/render.d.ts CHANGED
@@ -10,7 +10,7 @@ import ServerConfig from "../services/server-config.js";
10
10
  interface IRequestContext<TAppProps = Record<any, any>> {
11
11
  req: Request;
12
12
  res: ExpressResponse;
13
- appProps: TAppProps;
13
+ appProps: NonNullable<TAppProps>;
14
14
  html: {
15
15
  header: string;
16
16
  footer: string;
@@ -31,7 +31,7 @@ interface IRenderOptions<TAppProps = Record<string, any>> {
31
31
  context: IRequestContext<TAppProps>;
32
32
  }) => Promise<IRouterReadyOut> | IRouterReadyOut;
33
33
  onShellReady?: (params: {
34
- context: IRequestContext;
34
+ context: IRequestContext<TAppProps>;
35
35
  }) => IShellReadyOut;
36
36
  onShellError?: (params: {
37
37
  context: IRequestContext<TAppProps>;
@@ -1 +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 | void; // 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 | void;\n getState?: (params: {\n context: IRequestContext<TAppProps>;\n }) => Record<string, Record<string, any>> | undefined | void;\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"}
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: NonNullable<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<TAppProps> }) => IShellReadyOut;\n onShellError?: (params: {\n context: IRequestContext<TAppProps>;\n error: Error;\n }) => string | undefined | void; // 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 | void;\n getState?: (params: {\n context: IRequestContext<TAppProps>;\n }) => Record<string, Record<string, any>> | undefined | void;\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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lomray/vite-ssr-boost",
3
- "version": "1.0.0-beta.2",
3
+ "version": "1.0.0-beta.3",
4
4
  "description": "Vite plugin for create awesome SSR or SPA applications on React.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -1,2 +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};
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,init: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
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 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"}
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, init } = resolvedEntrypoint;\n const {\n onServerCreated,\n onRequest,\n onRouterReady,\n onShellReady,\n onResponse,\n onShellError,\n onError,\n getState,\n } =\n (await init?.({\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","init","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,KAAEA,GAASP,GACnBb,gBACJA,EAAeqB,UACfA,EAASC,cACTA,EAAaC,aACbA,EAAYC,WACZA,EAAUC,aACVA,EAAYC,QACZA,EAAOC,SACPA,SAEOP,IAAO,CACZtB,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"}