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

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.
Files changed (83) hide show
  1. package/README.md +207 -0
  2. package/browser/entry.d.ts +8 -7
  3. package/browser/entry.js +1 -1
  4. package/browser/entry.js.map +1 -1
  5. package/cli/build.d.ts +4 -1
  6. package/cli/build.js +1 -1
  7. package/cli/build.js.map +1 -1
  8. package/cli/run-dev.d.ts +2 -1
  9. package/cli/run-dev.js +1 -1
  10. package/cli/run-dev.js.map +1 -1
  11. package/cli/run-docker-build.d.ts +12 -0
  12. package/cli/run-docker-build.js +2 -0
  13. package/cli/run-docker-build.js.map +1 -0
  14. package/cli/run-prod.d.ts +1 -0
  15. package/cli/run-prod.js +1 -1
  16. package/cli/run-prod.js.map +1 -1
  17. package/cli.js +1 -1
  18. package/cli.js.map +1 -1
  19. package/components/with-suspense.d.ts +7 -0
  20. package/components/with-suspense.js +2 -0
  21. package/components/with-suspense.js.map +1 -0
  22. package/constants/cli-actions.d.ts +1 -0
  23. package/constants/cli-actions.js +1 -1
  24. package/constants/cli-actions.js.map +1 -1
  25. package/helpers/dev-marker.d.ts +9 -0
  26. package/helpers/dev-marker.js +2 -0
  27. package/helpers/dev-marker.js.map +1 -0
  28. package/helpers/get-server-state.d.ts +5 -0
  29. package/helpers/get-server-state.js +2 -0
  30. package/helpers/get-server-state.js.map +1 -0
  31. package/helpers/import-route.d.ts +252 -0
  32. package/helpers/import-route.js +2 -0
  33. package/helpers/import-route.js.map +1 -0
  34. package/helpers/is-route-file.d.ts +5 -0
  35. package/helpers/is-route-file.js +2 -0
  36. package/helpers/is-route-file.js.map +1 -0
  37. package/helpers/print-server-info.js +1 -1
  38. package/helpers/print-server-info.js.map +1 -1
  39. package/helpers/print-server-urls.js +1 -1
  40. package/helpers/print-server-urls.js.map +1 -1
  41. package/helpers/unlock-robots.d.ts +5 -0
  42. package/helpers/unlock-robots.js +2 -0
  43. package/helpers/unlock-robots.js.map +1 -0
  44. package/helpers/vite-aliases.d.ts +6 -0
  45. package/helpers/vite-aliases.js +2 -0
  46. package/helpers/vite-aliases.js.map +1 -0
  47. package/interfaces/fc-route.d.ts +19 -0
  48. package/interfaces/fc-route.js +2 -0
  49. package/interfaces/fc-route.js.map +1 -0
  50. package/interfaces/fc.d.ts +4 -0
  51. package/interfaces/fc.js +2 -0
  52. package/interfaces/fc.js.map +1 -0
  53. package/interfaces/route-object.d.ts +8 -0
  54. package/interfaces/route-object.js +2 -0
  55. package/interfaces/route-object.js.map +1 -0
  56. package/node/entry.d.ts +11 -9
  57. package/node/entry.js +1 -1
  58. package/node/entry.js.map +1 -1
  59. package/node/render.d.ts +5 -4
  60. package/node/render.js +1 -1
  61. package/node/render.js.map +1 -1
  62. package/node/server.js +1 -1
  63. package/node/server.js.map +1 -1
  64. package/package.json +8 -4
  65. package/plugin.d.ts +6 -4
  66. package/plugin.js +1 -1
  67. package/plugin.js.map +1 -1
  68. package/plugins/make-aliases.d.ts +11 -0
  69. package/plugins/make-aliases.js +2 -0
  70. package/plugins/make-aliases.js.map +1 -0
  71. package/plugins/normalize-route.d.ts +13 -0
  72. package/plugins/normalize-route.js +2 -0
  73. package/plugins/normalize-route.js.map +1 -0
  74. package/services/prepare-server.d.ts +4 -3
  75. package/services/prepare-server.js +1 -1
  76. package/services/prepare-server.js.map +1 -1
  77. package/services/server-config.d.ts +6 -9
  78. package/services/server-config.js +1 -1
  79. package/services/server-config.js.map +1 -1
  80. package/services/ssr-manifest.d.ts +157 -0
  81. package/services/ssr-manifest.js +2 -0
  82. package/services/ssr-manifest.js.map +1 -0
  83. package/workflow/Dockerfile +23 -0
@@ -0,0 +1,252 @@
1
+ /// <reference types="node" />
2
+ import { IndexRouteObject, NonIndexRouteObject } from 'react-router-dom';
3
+ import { FCCRoute, FCRoute } from "../interfaces/fc-route.js";
4
+ declare enum ResultType {
5
+ data = "data",
6
+ deferred = "deferred",
7
+ redirect = "redirect",
8
+ error = "error"
9
+ }
10
+ /**
11
+ * Successful result from a loader or action
12
+ */
13
+ interface SuccessResult {
14
+ type: ResultType.data;
15
+ data: any;
16
+ statusCode?: number;
17
+ headers?: Headers;
18
+ }
19
+ /**
20
+ * Successful defer() result from a loader or action
21
+ */
22
+ interface DeferredResult {
23
+ type: ResultType.deferred;
24
+ deferredData: DeferredData;
25
+ statusCode?: number;
26
+ headers?: Headers;
27
+ }
28
+ /**
29
+ * Redirect result from a loader or action
30
+ */
31
+ interface RedirectResult {
32
+ type: ResultType.redirect;
33
+ status: number;
34
+ location: string;
35
+ revalidate: boolean;
36
+ }
37
+ /**
38
+ * Unsuccessful result from a loader or action
39
+ */
40
+ interface ErrorResult {
41
+ type: ResultType.error;
42
+ error: any;
43
+ headers?: Headers;
44
+ }
45
+ /**
46
+ * Result from a loader or action - potentially successful or unsuccessful
47
+ */
48
+ type DataResult = SuccessResult | DeferredResult | RedirectResult | ErrorResult;
49
+ type LowerCaseFormMethod = "get" | "post" | "put" | "patch" | "delete";
50
+ type UpperCaseFormMethod = Uppercase<LowerCaseFormMethod>;
51
+ /**
52
+ * Active navigation/fetcher form methods are exposed in lowercase on the
53
+ * RouterState
54
+ */
55
+ type FormMethod = LowerCaseFormMethod;
56
+ /**
57
+ * In v7, active navigation/fetcher form methods are exposed in uppercase on the
58
+ * RouterState. This is to align with the normalization done via fetch().
59
+ */
60
+ type V7_FormMethod = UpperCaseFormMethod;
61
+ type FormEncType = "application/x-www-form-urlencoded" | "multipart/form-data";
62
+ /**
63
+ * @private
64
+ * Internal interface to pass around for action submissions, not intended for
65
+ * external consumption
66
+ */
67
+ interface Submission {
68
+ formMethod: FormMethod | V7_FormMethod;
69
+ formAction: string;
70
+ formEncType: FormEncType;
71
+ formData: FormData;
72
+ }
73
+ /**
74
+ * @private
75
+ * Arguments passed to route loader/action functions. Same for now but we keep
76
+ * this as a private implementation detail in case they diverge in the future.
77
+ */
78
+ interface DataFunctionArgs {
79
+ request: Request;
80
+ params: Params;
81
+ context?: any;
82
+ }
83
+ /**
84
+ * Arguments passed to loader functions
85
+ */
86
+ interface LoaderFunctionArgs extends DataFunctionArgs {
87
+ }
88
+ /**
89
+ * Arguments passed to action functions
90
+ */
91
+ interface ActionFunctionArgs extends DataFunctionArgs {
92
+ }
93
+ /**
94
+ * Loaders and actions can return anything except `undefined` (`null` is a
95
+ * valid return value if there is no data to return). Responses are preferred
96
+ * and will ease any future migration to Remix
97
+ */
98
+ type DataFunctionValue = Response | NonNullable<unknown> | null;
99
+ /**
100
+ * Route loader function signature
101
+ */
102
+ interface LoaderFunction {
103
+ (args: LoaderFunctionArgs): Promise<DataFunctionValue> | DataFunctionValue;
104
+ }
105
+ /**
106
+ * Route action function signature
107
+ */
108
+ interface ActionFunction {
109
+ (args: ActionFunctionArgs): Promise<DataFunctionValue> | DataFunctionValue;
110
+ }
111
+ /**
112
+ * Route shouldRevalidate function signature. This runs after any submission
113
+ * (navigation or fetcher), so we flatten the navigation/fetcher submission
114
+ * onto the arguments. It shouldn't matter whether it came from a navigation
115
+ * or a fetcher, what really matters is the URLs and the formData since loaders
116
+ * have to re-run based on the data models that were potentially mutated.
117
+ */
118
+ interface ShouldRevalidateFunction {
119
+ (args: {
120
+ currentUrl: URL;
121
+ currentParams: AgnosticDataRouteMatch["params"];
122
+ nextUrl: URL;
123
+ nextParams: AgnosticDataRouteMatch["params"];
124
+ formMethod?: Submission["formMethod"];
125
+ formAction?: Submission["formAction"];
126
+ formEncType?: Submission["formEncType"];
127
+ formData?: Submission["formData"];
128
+ actionResult?: DataResult;
129
+ defaultShouldRevalidate: boolean;
130
+ }): boolean;
131
+ }
132
+ /**
133
+ * Keys we cannot change from within a lazy() function. We spread all other keys
134
+ * onto the route. Either they're meaningful to the router, or they'll get
135
+ * ignored.
136
+ */
137
+ type ImmutableRouteKey = "lazy" | "caseSensitive" | "path" | "id" | "index" | "children";
138
+ /**
139
+ * lazy() function to load a route definition, which can add non-matching
140
+ * related properties to a route
141
+ */
142
+ interface LazyRouteFunction<R extends AgnosticRouteObject> {
143
+ (): Promise<Omit<R, ImmutableRouteKey>>;
144
+ }
145
+ /**
146
+ * Base RouteObject with common props shared by all types of routes
147
+ */
148
+ type AgnosticBaseRouteObject = {
149
+ caseSensitive?: boolean;
150
+ path?: string;
151
+ id?: string;
152
+ loader?: LoaderFunction;
153
+ action?: ActionFunction;
154
+ hasErrorBoundary?: boolean;
155
+ shouldRevalidate?: ShouldRevalidateFunction;
156
+ handle?: any;
157
+ lazy?: LazyRouteFunction<AgnosticBaseRouteObject>;
158
+ };
159
+ /**
160
+ * Index routes must not have children
161
+ */
162
+ type AgnosticIndexRouteObject = AgnosticBaseRouteObject & {
163
+ children?: undefined;
164
+ index: true;
165
+ };
166
+ /**
167
+ * Non-index routes may have children, but cannot have index
168
+ */
169
+ type AgnosticNonIndexRouteObject = AgnosticBaseRouteObject & {
170
+ children?: AgnosticRouteObject[];
171
+ index?: false;
172
+ };
173
+ /**
174
+ * A route object represents a logical route, with (optionally) its child
175
+ * routes organized in a tree-like structure.
176
+ */
177
+ type AgnosticRouteObject = AgnosticIndexRouteObject | AgnosticNonIndexRouteObject;
178
+ type AgnosticDataIndexRouteObject = AgnosticIndexRouteObject & {
179
+ id: string;
180
+ };
181
+ type AgnosticDataNonIndexRouteObject = AgnosticNonIndexRouteObject & {
182
+ children?: AgnosticDataRouteObject[];
183
+ id: string;
184
+ };
185
+ /**
186
+ * A data route object, which is just a RouteObject with a required unique ID
187
+ */
188
+ type AgnosticDataRouteObject = AgnosticDataIndexRouteObject | AgnosticDataNonIndexRouteObject;
189
+ /**
190
+ * The parameters that were parsed from the URL path.
191
+ */
192
+ type Params<Key extends string = string> = {
193
+ readonly [key in Key]: string | undefined;
194
+ };
195
+ /**
196
+ * A RouteMatch contains info about how a route matched a URL.
197
+ */
198
+ interface AgnosticRouteMatch<ParamKey extends string = string, RouteObjectType extends AgnosticRouteObject = AgnosticRouteObject> {
199
+ /**
200
+ * The names and values of dynamic parameters in the URL.
201
+ */
202
+ params: Params<ParamKey>;
203
+ /**
204
+ * The portion of the URL pathname that was matched.
205
+ */
206
+ pathname: string;
207
+ /**
208
+ * The portion of the URL pathname that was matched before child routes.
209
+ */
210
+ pathnameBase: string;
211
+ /**
212
+ * The route object that was used to match.
213
+ */
214
+ route: RouteObjectType;
215
+ }
216
+ interface AgnosticDataRouteMatch extends AgnosticRouteMatch<string, AgnosticDataRouteObject> {
217
+ }
218
+ declare class DeferredData {
219
+ private pendingKeysSet;
220
+ private controller;
221
+ private abortPromise;
222
+ private unlistenAbortSignal;
223
+ private subscribers;
224
+ data: Record<string, unknown>;
225
+ init?: ResponseInit;
226
+ deferredKeys: string[];
227
+ constructor(data: Record<string, unknown>, responseInit?: ResponseInit);
228
+ private trackPromise;
229
+ private onSettle;
230
+ private emit;
231
+ subscribe(fn: (aborted: boolean, settledKey?: string) => void): () => boolean;
232
+ cancel(): void;
233
+ resolveData(signal: AbortSignal): Promise<boolean>;
234
+ get done(): boolean;
235
+ get unwrappedData(): {};
236
+ get pendingKeys(): string[];
237
+ }
238
+ type RedirectFunction = (url: string, init?: number | ResponseInit) => Response;
239
+ /**
240
+ * A redirect response. Sets the status code and the `Location` header.
241
+ * Defaults to "302 Found".
242
+ */
243
+ declare const redirect: RedirectFunction;
244
+ type IDynamicRoute = () => Promise<{
245
+ default: FCRoute | FCCRoute<any>;
246
+ }>;
247
+ type IAsyncRoute = Omit<IndexRouteObject, ImmutableRouteKey> | Omit<NonIndexRouteObject, ImmutableRouteKey>;
248
+ /**
249
+ * Import dynamic route
250
+ */
251
+ declare const importRoute: (route: IDynamicRoute, id?: string) => Promise<IAsyncRoute>;
252
+ export { importRoute as default, IDynamicRoute, IAsyncRoute };
@@ -0,0 +1,2 @@
1
+ import t from"../components/with-suspense.js";import{keys as e}from"../interfaces/fc-route.js";const n=(t,e)=>{e&&(t.pathId=e)},o=async(o,s)=>{const r=await o();if(r.Component)return n(r,s),r;const p=r.default,a={Component:p};return e.forEach((t=>{p[t]&&(a[t]=p[t])})),p.Suspense&&(a.Component=t(p,p.Suspense)),n(a,s),a};export{o as default};
2
+ //# sourceMappingURL=import-route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import-route.js","sources":["../../src/helpers/import-route.ts"],"sourcesContent":["import type { ImmutableRouteKey } from '@remix-run/router/utils';\nimport type { IndexRouteObject, NonIndexRouteObject } from 'react-router-dom';\nimport withSuspense from '@components/with-suspense';\nimport type { FCCRoute, FCRoute } from '@interfaces/fc-route';\nimport { keys } from '@interfaces/fc-route';\n\nexport type IDynamicRoute = () => Promise<{ default: FCRoute | FCCRoute<any> }>;\n\nexport type IAsyncRoute =\n | Omit<IndexRouteObject, ImmutableRouteKey>\n | Omit<NonIndexRouteObject, ImmutableRouteKey>;\n\n/**\n * Assign route path id to component\n */\nconst assignId = (response: Record<string, any>, id?: string) => {\n if (!id) {\n return;\n }\n\n response['pathId'] = id;\n};\n\n/**\n * Import dynamic route\n */\nconst importRoute = async (route: IDynamicRoute, id?: string): Promise<IAsyncRoute> => {\n const resolved = await route();\n\n // fallback to react router export style\n if (resolved['Component']) {\n assignId(resolved, id);\n\n return resolved as IAsyncRoute;\n }\n\n const Component = resolved.default;\n const result = { Component };\n\n keys.forEach((key) => {\n if (Component[key]) {\n result[key] = Component[key];\n }\n });\n\n if (Component.Suspense) {\n result.Component = withSuspense(Component, Component.Suspense);\n }\n\n assignId(result, id);\n\n return result;\n};\n\nexport default importRoute;\n"],"names":["assignId","response","id","importRoute","async","route","resolved","Component","default","result","keys","forEach","key","Suspense","withSuspense"],"mappings":"+FAeA,MAAMA,EAAW,CAACC,EAA+BC,KAC1CA,IAILD,EAAiB,OAAIC,EAAE,EAMnBC,EAAcC,MAAOC,EAAsBH,KAC/C,MAAMI,QAAiBD,IAGvB,GAAIC,EAAoB,UAGtB,OAFAN,EAASM,EAAUJ,GAEZI,EAGT,MAAMC,EAAYD,EAASE,QACrBC,EAAS,CAAEF,aAcjB,OAZAG,EAAKC,SAASC,IACRL,EAAUK,KACZH,EAAOG,GAAOL,EAAUK,GACzB,IAGCL,EAAUM,WACZJ,EAAOF,UAAYO,EAAaP,EAAWA,EAAUM,WAGvDb,EAASS,EAAQP,GAEVO,CAAM"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Detect route file
3
+ */
4
+ declare const isRoutesFile: (code: string) => boolean;
5
+ export { isRoutesFile as default };
@@ -0,0 +1,2 @@
1
+ const t=t=>/\[.*{.*path:.*lazyNR:.+import/s.test(t);export{t as default};
2
+ //# sourceMappingURL=is-route-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-route-file.js","sources":["../../src/helpers/is-route-file.ts"],"sourcesContent":["/**\n * Detect route file\n */\nconst isRoutesFile = (code: string): boolean => /\\[.*{.*path:.*lazyNR:.+import/s.test(code);\n\nexport default isRoutesFile;\n"],"names":["isRoutesFile","code","test"],"mappings":"AAGA,MAAMA,EAAgBC,GAA0B,iCAAiCC,KAAKD"}
@@ -1,2 +1,2 @@
1
- import{performance as o}from"node:perf_hooks";import e from"chalk";import r from"../constants/cli-actions.js";import s from"../constants/cli-name.js";import t from"./print-server-urls.js";import n from"./resolve-server-urls.js";async function i(i,a,{version:l="unknown"}){const{action:m}=a.getPluginConfig()??{},{isProd:p,host:f}=a.getParams(),c=a.getLogger(),d=global.viteBoostStartTime??o.now(),g=e.dim(`ready in ${e.reset(e.bold(Math.ceil(o.now()-d)))} ms`);c.info(`\n ${e.green(`${e.bold(s.toUpperCase())} v${l}${p?e.blue(" PRODUCTION"):""}`)} ${g}\n`,{clear:!c.hasWarned});const h=a.getVite()?.config,v=await n(i,{host:f,isHttps:"boolean"==typeof h?.server.https&&h?.server.https,rawBase:h?.rawBase});if(p)t(v,(o=>c.info(o)));else{const o=a.getVite();o.resolvedUrls=v,o.printUrls()}m===r.dev&&c.info(e.dim(e.green(" ➜"))+e.dim(" press ")+e.bold("h")+e.dim(" to show help"))}export{i as default};
1
+ import o from"node:fs";import{performance as e}from"node:perf_hooks";import r from"chalk";import t from"../constants/cli-actions.js";import s from"../constants/cli-name.js";import{markerFileName as i}from"./dev-marker.js";import n from"./print-server-urls.js";import m from"./resolve-server-urls.js";async function a(a,d,{version:l="unknown"}){const{action:p}=d.getPluginConfig()??{},{isProd:f,host:c,root:g}=d.getParams(),v=`${g}/${i}`,h=d.getLogger(),u=global.viteBoostStartTime??e.now(),$=r.dim(`ready in ${r.reset(r.bold(Math.ceil(e.now()-u)))} ms`);h.info(`\n ${r.green(`${r.bold(s.toUpperCase())} v${l}`)} ${$}\n`,{clear:!h.hasWarned});const w=d.getVite()?.config,b=!w?.mode&&!o.existsSync(v),j=await m(a,{host:c,isHttps:"boolean"==typeof w?.server.https&&w?.server.https,rawBase:w?.rawBase}),k=w?.mode||b?d.mode:`production ${r.red("NODE_ENV=development")}`;if(h.info(r.dim(r.green(" ➜"))+r.dim(" Mode: ")+r.blue(k)),f)n(j,(o=>h.info(o)));else{const o=d.getVite();o.resolvedUrls=j,o.printUrls()}p===t.dev&&h.info(r.dim(r.green(" ➜"))+r.dim(" press ")+r.bold("h")+r.dim(" to show help"))}export{a as default};
2
2
  //# sourceMappingURL=print-server-info.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"print-server-info.js","sources":["../../src/helpers/print-server-info.ts"],"sourcesContent":["import type { Server } from 'node:net';\nimport { performance } from 'node:perf_hooks';\nimport chalk from 'chalk';\nimport CliActions from '@constants/cli-actions';\nimport cliName from '@constants/cli-name';\nimport printServerUrls from '@helpers/print-server-urls';\nimport resolveServerUrls from '@helpers/resolve-server-urls';\nimport type ServerConfig from '@services/server-config';\n\ninterface IPrintServerInfoParams {\n version?: string;\n}\n\n/**\n * Print server info\n */\nasync function printServerInfo(\n server: Server,\n config: ServerConfig,\n { version = 'unknown' }: IPrintServerInfoParams,\n): Promise<void> {\n const { action } = config.getPluginConfig() ?? {};\n const { isProd, host } = config.getParams();\n\n const Logger = config.getLogger();\n const perfStart = global.viteBoostStartTime ?? performance.now();\n const startupDurationString = chalk.dim(\n `ready in ${chalk.reset(chalk.bold(Math.ceil(performance.now() - perfStart)))} ms`,\n );\n\n Logger.info(\n `\\n ${chalk.green(\n `${chalk.bold(cliName.toUpperCase())} v${version}${isProd ? chalk.blue(' PRODUCTION') : ''}`,\n )} ${startupDurationString}\\n`,\n { clear: !Logger.hasWarned },\n );\n\n const viteConfig = config.getVite()?.config;\n const resolvedUrls = await resolveServerUrls(server, {\n host,\n isHttps: typeof viteConfig?.server.https === 'boolean' ? viteConfig?.server.https : false,\n rawBase: viteConfig?.['rawBase'],\n });\n\n if (!isProd) {\n const vite = config.getVite()!;\n\n vite.resolvedUrls = resolvedUrls;\n vite.printUrls();\n } else {\n printServerUrls(resolvedUrls, (msg) => Logger.info(msg));\n }\n\n if (action === CliActions.dev) {\n Logger.info(\n chalk.dim(chalk.green(' ➜')) +\n chalk.dim(' press ') +\n chalk.bold('h') +\n chalk.dim(' to show help'),\n );\n }\n}\n\nexport default printServerInfo;\n"],"names":["async","printServerInfo","server","config","version","action","getPluginConfig","isProd","host","getParams","Logger","getLogger","perfStart","global","viteBoostStartTime","performance","now","startupDurationString","chalk","dim","reset","bold","Math","ceil","info","green","cliName","toUpperCase","blue","clear","hasWarned","viteConfig","getVite","resolvedUrls","resolveServerUrls","isHttps","https","rawBase","printServerUrls","msg","vite","printUrls","CliActions","dev"],"mappings":"oOAgBAA,eAAeC,EACbC,EACAC,GACAC,QAAEA,EAAU,YAEZ,MAAMC,OAAEA,GAAWF,EAAOG,mBAAqB,CAAA,GACzCC,OAAEA,EAAMC,KAAEA,GAASL,EAAOM,YAE1BC,EAASP,EAAOQ,YAChBC,EAAYC,OAAOC,oBAAsBC,EAAYC,MACrDC,EAAwBC,EAAMC,IAClC,YAAYD,EAAME,MAAMF,EAAMG,KAAKC,KAAKC,KAAKR,EAAYC,MAAQJ,WAGnEF,EAAOc,KACL,OAAON,EAAMO,MACX,GAAGP,EAAMG,KAAKK,EAAQC,mBAAmBvB,IAAUG,EAASW,EAAMU,KAAK,eAAiB,UACpFX,MACN,CAAEY,OAAQnB,EAAOoB,YAGnB,MAAMC,EAAa5B,EAAO6B,WAAW7B,OAC/B8B,QAAqBC,EAAkBhC,EAAQ,CACnDM,OACA2B,QAA6C,kBAA7BJ,GAAY7B,OAAOkC,OAAsBL,GAAY7B,OAAOkC,MAC5EC,QAASN,GAAsB,UAGjC,GAAKxB,EAMH+B,EAAgBL,GAAeM,GAAQ7B,EAAOc,KAAKe,SANxC,CACX,MAAMC,EAAOrC,EAAO6B,UAEpBQ,EAAKP,aAAeA,EACpBO,EAAKC,WACN,CAIGpC,IAAWqC,EAAWC,KACxBjC,EAAOc,KACLN,EAAMC,IAAID,EAAMO,MAAM,QACpBP,EAAMC,IAAI,YACVD,EAAMG,KAAK,KACXH,EAAMC,IAAI,iBAGlB"}
1
+ {"version":3,"file":"print-server-info.js","sources":["../../src/helpers/print-server-info.ts"],"sourcesContent":["import fs from 'node:fs';\nimport type { Server } from 'node:net';\nimport { performance } from 'node:perf_hooks';\nimport chalk from 'chalk';\nimport CliActions from '@constants/cli-actions';\nimport cliName from '@constants/cli-name';\nimport { markerFileName } from '@helpers/dev-marker';\nimport printServerUrls from '@helpers/print-server-urls';\nimport resolveServerUrls from '@helpers/resolve-server-urls';\nimport type ServerConfig from '@services/server-config';\n\ninterface IPrintServerInfoParams {\n version?: string;\n}\n\n/**\n * Print server info\n */\nasync function printServerInfo(\n server: Server,\n config: ServerConfig,\n { version = 'unknown' }: IPrintServerInfoParams,\n): Promise<void> {\n const { action } = config.getPluginConfig() ?? {};\n const { isProd, host, root } = config.getParams();\n const devMarker = `${root}/${markerFileName}`;\n\n const Logger = config.getLogger();\n const perfStart = global.viteBoostStartTime ?? performance.now();\n const startupDurationString = chalk.dim(\n `ready in ${chalk.reset(chalk.bold(Math.ceil(performance.now() - perfStart)))} ms`,\n );\n\n Logger.info(\n `\\n ${chalk.green(\n `${chalk.bold(cliName.toUpperCase())} v${version}`,\n )} ${startupDurationString}\\n`,\n { clear: !Logger.hasWarned },\n );\n\n const viteConfig = config.getVite()?.config;\n const isProdBuild = !viteConfig?.mode && !fs.existsSync(devMarker);\n const resolvedUrls = await resolveServerUrls(server, {\n host,\n isHttps: typeof viteConfig?.server.https === 'boolean' ? viteConfig?.server.https : false,\n rawBase: viteConfig?.['rawBase'],\n });\n const mode =\n viteConfig?.mode || isProdBuild\n ? config.mode\n : `production ${chalk.red('NODE_ENV=development')}`;\n\n Logger.info(chalk.dim(chalk.green(' ➜')) + chalk.dim(' Mode: ') + chalk.blue(mode));\n\n if (!isProd) {\n const vite = config.getVite()!;\n\n vite.resolvedUrls = resolvedUrls;\n vite.printUrls();\n } else {\n printServerUrls(resolvedUrls, (msg) => Logger.info(msg));\n }\n\n if (action === CliActions.dev) {\n Logger.info(\n chalk.dim(chalk.green(' ➜')) +\n chalk.dim(' press ') +\n chalk.bold('h') +\n chalk.dim(' to show help'),\n );\n }\n}\n\nexport default printServerInfo;\n"],"names":["async","printServerInfo","server","config","version","action","getPluginConfig","isProd","host","root","getParams","devMarker","markerFileName","Logger","getLogger","perfStart","global","viteBoostStartTime","performance","now","startupDurationString","chalk","dim","reset","bold","Math","ceil","info","green","cliName","toUpperCase","clear","hasWarned","viteConfig","getVite","isProdBuild","mode","fs","existsSync","resolvedUrls","resolveServerUrls","isHttps","https","rawBase","red","blue","printServerUrls","msg","vite","printUrls","CliActions","dev"],"mappings":"4SAkBAA,eAAeC,EACbC,EACAC,GACAC,QAAEA,EAAU,YAEZ,MAAMC,OAAEA,GAAWF,EAAOG,mBAAqB,CAAA,GACzCC,OAAEA,EAAMC,KAAEA,EAAIC,KAAEA,GAASN,EAAOO,YAChCC,EAAY,GAAGF,KAAQG,IAEvBC,EAASV,EAAOW,YAChBC,EAAYC,OAAOC,oBAAsBC,EAAYC,MACrDC,EAAwBC,EAAMC,IAClC,YAAYD,EAAME,MAAMF,EAAMG,KAAKC,KAAKC,KAAKR,EAAYC,MAAQJ,WAGnEF,EAAOc,KACL,OAAON,EAAMO,MACX,GAAGP,EAAMG,KAAKK,EAAQC,mBAAmB1B,SACrCgB,MACN,CAAEW,OAAQlB,EAAOmB,YAGnB,MAAMC,EAAa9B,EAAO+B,WAAW/B,OAC/BgC,GAAeF,GAAYG,OAASC,EAAGC,WAAW3B,GAClD4B,QAAqBC,EAAkBtC,EAAQ,CACnDM,OACAiC,QAA6C,kBAA7BR,GAAY/B,OAAOwC,OAAsBT,GAAY/B,OAAOwC,MAC5EC,QAASV,GAAsB,UAE3BG,EACJH,GAAYG,MAAQD,EAChBhC,EAAOiC,KACP,cAAcf,EAAMuB,IAAI,0BAI9B,GAFA/B,EAAOc,KAAKN,EAAMC,IAAID,EAAMO,MAAM,QAAUP,EAAMC,IAAI,eAAiBD,EAAMwB,KAAKT,IAE7E7B,EAMHuC,EAAgBP,GAAeQ,GAAQlC,EAAOc,KAAKoB,SANxC,CACX,MAAMC,EAAO7C,EAAO+B,UAEpBc,EAAKT,aAAeA,EACpBS,EAAKC,WACN,CAIG5C,IAAW6C,EAAWC,KACxBtC,EAAOc,KACLN,EAAMC,IAAID,EAAMO,MAAM,QACpBP,EAAMC,IAAI,YACVD,EAAMG,KAAK,KACXH,EAAMC,IAAI,iBAGlB"}
@@ -1,2 +1,2 @@
1
- import o from"chalk";function e(e,r){const c=e=>o.cyan(e.replace(/:(\d+)\//,((e,r)=>`:${o.bold(r)}/`)));for(const l of e.local)r(` ${o.green("➜")} ${o.bold("Local")}: ${c(l)}`);for(const l of e.network)r(` ${o.green("➜")} ${o.bold("Network")}: ${c(l)}`)}export{e as default};
1
+ import o from"chalk";function e(e,n){const r=e=>o.cyan(e.replace(/:(\d+)\//,((e,n)=>`:${o.bold(n)}/`)));for(const c of e.local)n(` ${o.green("➜")} ${o.bold("Local")}: ${r(c)}`);for(const c of e.network)n(` ${o.green("➜")} ${o.bold("Network")}: ${r(c)}`);n("\n")}export{e as default};
2
2
  //# sourceMappingURL=print-server-urls.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"print-server-urls.js","sources":["../../src/helpers/print-server-urls.ts"],"sourcesContent":["import chalk from 'chalk';\nimport type { Logger, ResolvedServerUrls } from 'vite';\n\n/**\n * Print server urls\n * @see https://github.com/vitejs/vite/blob/711dd807610b39538e9955970145d52e4ca1d8c0/packages/vite/src/node/logger.ts#LL142C1-L162C2\n * vite not export this function\n */\nfunction printServerUrls(urls: ResolvedServerUrls, info: Logger['info']): void {\n const colorUrl = (url: string) =>\n chalk.cyan(url.replace(/:(\\d+)\\//, (_, port) => `:${chalk.bold(port)}/`));\n for (const url of urls.local) {\n info(` ${chalk.green('➜')} ${chalk.bold('Local')}: ${colorUrl(url)}`);\n }\n for (const url of urls.network) {\n info(` ${chalk.green('➜')} ${chalk.bold('Network')}: ${colorUrl(url)}`);\n }\n}\n\nexport default printServerUrls;\n"],"names":["printServerUrls","urls","info","colorUrl","url","chalk","cyan","replace","_","port","bold","local","green","network"],"mappings":"qBAQA,SAASA,EAAgBC,EAA0BC,GACjD,MAAMC,EAAYC,GAChBC,EAAMC,KAAKF,EAAIG,QAAQ,YAAY,CAACC,EAAGC,IAAS,IAAIJ,EAAMK,KAAKD,SACjE,IAAK,MAAML,KAAOH,EAAKU,MACrBT,EAAK,KAAKG,EAAMO,MAAM,SAASP,EAAMK,KAAK,eAAeP,EAASC,MAEpE,IAAK,MAAMA,KAAOH,EAAKY,QACrBX,EAAK,KAAKG,EAAMO,MAAM,SAASP,EAAMK,KAAK,eAAeP,EAASC,KAEtE"}
1
+ {"version":3,"file":"print-server-urls.js","sources":["../../src/helpers/print-server-urls.ts"],"sourcesContent":["import chalk from 'chalk';\nimport type { Logger, ResolvedServerUrls } from 'vite';\n\n/**\n * Print server urls\n * @see https://github.com/vitejs/vite/blob/711dd807610b39538e9955970145d52e4ca1d8c0/packages/vite/src/node/logger.ts#LL142C1-L162C2\n * vite not export this function\n */\nfunction printServerUrls(urls: ResolvedServerUrls, info: Logger['info']): void {\n const colorUrl = (url: string) =>\n chalk.cyan(url.replace(/:(\\d+)\\//, (_, port) => `:${chalk.bold(port)}/`));\n for (const url of urls.local) {\n info(` ${chalk.green('➜')} ${chalk.bold('Local')}: ${colorUrl(url)}`);\n }\n for (const url of urls.network) {\n info(` ${chalk.green('➜')} ${chalk.bold('Network')}: ${colorUrl(url)}`);\n }\n\n info('\\n');\n}\n\nexport default printServerUrls;\n"],"names":["printServerUrls","urls","info","colorUrl","url","chalk","cyan","replace","_","port","bold","local","green","network"],"mappings":"qBAQA,SAASA,EAAgBC,EAA0BC,GACjD,MAAMC,EAAYC,GAChBC,EAAMC,KAAKF,EAAIG,QAAQ,YAAY,CAACC,EAAGC,IAAS,IAAIJ,EAAMK,KAAKD,SACjE,IAAK,MAAML,KAAOH,EAAKU,MACrBT,EAAK,KAAKG,EAAMO,MAAM,SAASP,EAAMK,KAAK,eAAeP,EAASC,MAEpE,IAAK,MAAMA,KAAOH,EAAKY,QACrBX,EAAK,KAAKG,EAAMO,MAAM,SAASP,EAAMK,KAAK,eAAeP,EAASC,MAGpEF,EAAK,KACP"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Change general directive Disallow to Allow in robots.txt.
3
+ */
4
+ declare const unlockRobots: (root: string, buildFolder: string) => void;
5
+ export { unlockRobots as default };
@@ -0,0 +1,2 @@
1
+ import o from"node:fs";import t from"node:path";import e from"chalk";const n=(n,l)=>{const i=`${t.resolve(n,l)}/client/robots.txt`;if(!o.existsSync(i))return void console.warn(`Failed to unlock robots.txt, file not exist: ${i}`);const r=o.readFileSync(i,{encoding:"utf-8"}).replace(/Disallow: \/$/m,"Allow: /");o.writeFileSync(i,r,{encoding:"utf-8"}),console.info(e.blue("\nrobots.txt unlocked."))};export{n as default};
2
+ //# sourceMappingURL=unlock-robots.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unlock-robots.js","sources":["../../src/helpers/unlock-robots.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport chalk from 'chalk';\n\n/**\n * Change general directive Disallow to Allow in robots.txt.\n */\nconst unlockRobots = (root: string, buildFolder: string): void => {\n const buildPath = path.resolve(root, buildFolder);\n const robotsFile = `${buildPath}/client/robots.txt`;\n\n if (!fs.existsSync(robotsFile)) {\n console.warn(`Failed to unlock robots.txt, file not exist: ${robotsFile}`);\n\n return;\n }\n\n const data = fs\n .readFileSync(robotsFile, { encoding: 'utf-8' })\n .replace(/Disallow: \\/$/m, 'Allow: /');\n\n fs.writeFileSync(robotsFile, data, { encoding: 'utf-8' });\n\n console.info(chalk.blue('\\nrobots.txt unlocked.'));\n};\n\nexport default unlockRobots;\n"],"names":["unlockRobots","root","buildFolder","robotsFile","path","resolve","fs","existsSync","console","warn","data","readFileSync","encoding","replace","writeFileSync","info","chalk","blue"],"mappings":"qEAOA,MAAMA,EAAe,CAACC,EAAcC,KAClC,MACMC,EAAa,GADDC,EAAKC,QAAQJ,EAAMC,uBAGrC,IAAKI,EAAGC,WAAWJ,GAGjB,YAFAK,QAAQC,KAAK,gDAAgDN,KAK/D,MAAMO,EAAOJ,EACVK,aAAaR,EAAY,CAAES,SAAU,UACrCC,QAAQ,iBAAkB,YAE7BP,EAAGQ,cAAcX,EAAYO,EAAM,CAAEE,SAAU,UAE/CJ,QAAQO,KAAKC,EAAMC,KAAK,0BAA0B"}
@@ -0,0 +1,6 @@
1
+ import { Alias } from 'vite';
2
+ /**
3
+ * Set vite aliases
4
+ */
5
+ declare const viteAliases: (aliases: [string, string][], root?: string) => Alias[];
6
+ export { viteAliases as default };
@@ -0,0 +1,2 @@
1
+ import{fileURLToPath as e,URL as r}from"node:url";const a=e=>e.replace("./","/").replace(/([^:]\/)\/+/g,"$1"),p=(p,t="")=>p.map((([p,l])=>({find:p,replacement:e(new r(`${t}${a(l)}`,import.meta.url))})));export{p as default};
2
+ //# sourceMappingURL=vite-aliases.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-aliases.js","sources":["../../src/helpers/vite-aliases.ts"],"sourcesContent":["import { fileURLToPath, URL } from 'node:url';\nimport type { Alias } from 'vite';\n\nconst cleanupPath = (path: string) => path.replace('./', '/').replace(/([^:]\\/)\\/+/g, '$1');\n\n/**\n * Set vite aliases\n */\nconst viteAliases = (aliases: [string, string][], root = ''): Alias[] =>\n aliases.map(([find, path]) => ({\n find,\n replacement: fileURLToPath(new URL(`${root}${cleanupPath(path)}`, import.meta.url)),\n }));\n\nexport default viteAliases;\n"],"names":["cleanupPath","path","replace","viteAliases","aliases","root","map","find","replacement","fileURLToPath","URL","url"],"mappings":"kDAGA,MAAMA,EAAeC,GAAiBA,EAAKC,QAAQ,KAAM,KAAKA,QAAQ,eAAgB,MAKhFC,EAAc,CAACC,EAA6BC,EAAO,KACvDD,EAAQE,KAAI,EAAEC,EAAMN,MAAW,CAC7BM,OACAC,YAAaC,EAAc,IAAIC,EAAI,GAAGL,IAAOL,EAAYC,iBAAqBU"}
@@ -0,0 +1,19 @@
1
+ import { FC, PropsWithChildren } from 'react';
2
+ import { RouteObject } from 'react-router/dist/lib/context';
3
+ import { IRequestContext } from "../node/render.js";
4
+ declare module '@remix-run/router' {
5
+ interface LoaderFunctionArgs {
6
+ context?: IRequestContext;
7
+ }
8
+ interface ActionFunctionArgs {
9
+ context?: IRequestContext;
10
+ }
11
+ }
12
+ declare const keys: readonly ["loader", "action", "ErrorBoundary", "errorElement"];
13
+ type IRouteParams = Pick<RouteObject, (typeof keys)[number]> & {
14
+ Suspense?: FC;
15
+ };
16
+ type FCRoute<TProps = Record<string, any>> = FC<TProps> & IRouteParams;
17
+ type FCCRoute<TProps = Record<string, any>> = FC<PropsWithChildren<TProps>> & IRouteParams;
18
+ export type { FCRoute, FCCRoute, IRouteParams };
19
+ export { keys };
@@ -0,0 +1,2 @@
1
+ const r=["loader","action","ErrorBoundary","errorElement"];export{r as keys};
2
+ //# sourceMappingURL=fc-route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fc-route.js","sources":["../../src/interfaces/fc-route.ts"],"sourcesContent":["import type { FC, PropsWithChildren } from 'react';\nimport type { RouteObject } from 'react-router/dist/lib/context';\nimport type { IRequestContext } from '@node/render';\n\ndeclare module '@remix-run/router' {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n export interface LoaderFunctionArgs {\n context?: IRequestContext;\n }\n\n // eslint-disable-next-line @typescript-eslint/naming-convention\n export interface ActionFunctionArgs {\n context?: IRequestContext;\n }\n}\n\nconst keys = ['loader', 'action', 'ErrorBoundary', 'errorElement'] as const;\n\ntype IRouteParams = Pick<RouteObject, (typeof keys)[number]> & { Suspense?: FC };\n\ntype FCRoute<TProps = Record<string, any>> = FC<TProps> & IRouteParams;\ntype FCCRoute<TProps = Record<string, any>> = FC<PropsWithChildren<TProps>> & IRouteParams;\n\nexport type { FCRoute, FCCRoute, IRouteParams };\n\nexport { keys };\n"],"names":["keys"],"mappings":"AAgBM,MAAAA,EAAO,CAAC,SAAU,SAAU,gBAAiB"}
@@ -0,0 +1,4 @@
1
+ import { FC, PropsWithChildren } from 'react';
2
+ type FCC<T extends Record<string, any>> = FC<PropsWithChildren<T>>;
3
+ type FCAny<T extends Record<string, any>> = FC<T> | FCC<T>;
4
+ export { FCC, FCAny };
@@ -0,0 +1,2 @@
1
+
2
+ //# sourceMappingURL=fc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fc.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,8 @@
1
+ import { RouteObject } from 'react-router-dom';
2
+ import { IDynamicRoute } from "../helpers/import-route.js";
3
+ type TRouteObjectNR = Omit<RouteObject, 'lazy' | 'children'> & {
4
+ lazyNR?: IDynamicRoute;
5
+ children?: TRouteObject[];
6
+ };
7
+ type TRouteObject = RouteObject | TRouteObjectNR;
8
+ export { TRouteObjectNR, TRouteObject };
@@ -0,0 +1,2 @@
1
+
2
+ //# sourceMappingURL=route-object.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-object.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
package/node/entry.d.ts CHANGED
@@ -1,11 +1,12 @@
1
1
  import { Express, Request } from 'express';
2
2
  import { Response as ExpressResponse } from "express";
3
3
  import { FC, PropsWithChildren } from 'react';
4
- import { RouteObject } from 'react-router-dom';
4
+ import { TRouteObject } from "../interfaces/route-object.js";
5
5
  import { IRenderOptions, TRender } from "./render.js";
6
6
  import ServerConfig from "../services/server-config.js";
7
7
  interface IInitServerRequestOut<T = Record<string, any>> {
8
8
  appProps?: T;
9
+ hasEarlyHints?: boolean;
9
10
  }
10
11
  interface IEntrypointOptions<TAppProps = Record<string, any>> {
11
12
  onServerCreated?: (app: Express) => Promise<void> | void;
@@ -19,22 +20,23 @@ interface IEntrypointOptions<TAppProps = Record<string, any>> {
19
20
  }
20
21
  interface IPrepareRenderOut<TAppProps = Record<string, any>> {
21
22
  render: TRender;
22
- initServer: IEntryServerOptions<TAppProps>['initServer'];
23
+ init: IEntryServerOptions<TAppProps>['init'];
24
+ routes: TRouteObject[];
25
+ abortDelay?: number;
23
26
  }
24
27
  interface IAppServerProps<T = Record<string, any>> {
25
- server: T & {
26
- req: Request;
27
- };
28
+ server: T;
28
29
  }
29
- type TApp<T> = FC<PropsWithChildren<IAppServerProps<T>>>;
30
+ type TApp<T> = FC<PropsWithChildren<Record<string, any> & IAppServerProps<T>>>;
30
31
  interface IEntryServerOptions<TAppProps = Record<string, any>> {
31
- routes: RouteObject[];
32
- initServer?: (params: {
32
+ abortDelay?: number;
33
+ hasEarlyHints?: boolean;
34
+ init?: (params: {
33
35
  config: ServerConfig;
34
36
  }) => IEntrypointOptions<TAppProps> | Promise<IEntrypointOptions<TAppProps>>;
35
37
  }
36
38
  /**
37
39
  * Render server side application
38
40
  */
39
- declare function entry<TAppProps>({ routes, initServer }: IEntryServerOptions<TAppProps>, App: TApp<TAppProps>): IPrepareRenderOut<TAppProps>;
41
+ declare function entry<TAppProps>(App: TApp<TAppProps>, routes: TRouteObject[], { init, abortDelay }?: IEntryServerOptions<TAppProps>): IPrepareRenderOut<TAppProps>;
40
42
  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 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};
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 { TRouteObject } from '@interfaces/route-object';\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 hasEarlyHints?: boolean;\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 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":"6FAoDA,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"}
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;
@@ -18,6 +18,7 @@ interface IRequestContext<TAppProps = Record<any, any>> {
18
18
  routerContext?: StaticHandlerContext;
19
19
  serverContext?: IServerContext;
20
20
  isStream?: boolean;
21
+ hasEarlyHints?: boolean;
21
22
  didError?: StreamError;
22
23
  }
23
24
  type TRender<TAppProps = Record<any, any>> = (config: ServerConfig, context: IRequestContext<TAppProps>, options: IRenderOptions) => Promise<void>;
@@ -26,12 +27,12 @@ interface IRenderParams<TAppProps = Record<string, any>> {
26
27
  handler: StaticHandler;
27
28
  }
28
29
  interface IRenderOptions<TAppProps = Record<string, any>> {
29
- appProps?: TAppProps;
30
+ abortDelay?: number;
30
31
  onRouterReady?: (params: {
31
32
  context: IRequestContext<TAppProps>;
32
33
  }) => Promise<IRouterReadyOut> | IRouterReadyOut;
33
34
  onShellReady?: (params: {
34
- context: IRequestContext;
35
+ context: IRequestContext<TAppProps>;
35
36
  }) => IShellReadyOut;
36
37
  onShellError?: (params: {
37
38
  context: IRequestContext<TAppProps>;
@@ -60,5 +61,5 @@ interface IShellReadyOut {
60
61
  * Render application
61
62
  */
62
63
  declare function render({ App, handler }: IRenderParams, // @see entry (bind)
63
- config: ServerConfig, context: IRequestContext, { onRouterReady, onShellReady, onResponse, onShellError, onError, getState }: IRenderOptions): Promise<void>;
64
+ config: ServerConfig, context: IRequestContext, { onRouterReady, onShellReady, onResponse, onShellError, onError, getState, abortDelay, }: IRenderOptions): Promise<void>;
64
65
  export { render as default, IRequestContext, TRender, IRenderParams, IRenderOptions, IRouterReadyOut, IShellReadyOut };
package/node/render.js CHANGED
@@ -1,2 +1,2 @@
1
- import e from"chalk";import r from"react";import{renderToPipeableStream as t}from"react-dom/server";import{createStaticRouter as o,StaticRouterProvider as n}from"react-router-dom/server.mjs";import s from"../constants/stream-error.js";import{ServerProvider as a}from"../context/server.js";import m from"../helpers/handle-response.js";import i from"../helpers/obtain-stream-error.js";import c from"./create-fetch-request.js";import d from"./write-response.js";async function l({App:l,handler:p},u,f,{onRouterReady:h,onShellReady:x,onResponse:S,onShellError:R,onError:g,getState:y}){const{req:C,res:E}=f,j=c(C);f.routerContext=await p.query(j);const v=m(E,f.routerContext);if(!v)return;const{isStream:w=!0}=await(h?.({context:f}))??{};f.isStream=w,f.serverContext={response:null,isServer:!0};const b=o(p.dataRoutes,f.routerContext),T=E.write.bind(E),q=u.getLogger();let $;E.write=(e,...r)=>{const t="string"==typeof e,o=t?e:Buffer.from(e).toString(),n=S?.({context:f,html:o});return n?T(t?`${n}${e}`:Buffer.concat([Buffer.from(n),e]),...r):T(e,...r)};const{serverContext:A,routerContext:B,appProps:P}=f,{pipe:k,abort:D}=t(r.createElement(a,{context:A},r.createElement(l,{server:{...P,req:C}},r.createElement(n,{router:b,context:B,hydrate:!1}))),{onShellReady(){w&&d(f,{pipe:k,statusCode:v,onShellReady:x,getState:y})},onAllReady(){clearTimeout($),w||d(f,{pipe:k,statusCode:v,onShellReady:x,getState:y})},onShellError(e){const r=R?.({context:f,error:e})||`<!doctype html><p>Something went wrong: ${e.message}</p>`;E.status(500),E.setHeader("content-type","text/html"),E.send(r)},onError(r){clearTimeout($);const t=i(r),{code:o,message:n}=t,{didError:a}=f;f.didError=a??o,g?.({context:f,error:t}),q.info(e.red(`Stream error. Code: ${o}`)),[s.RenderAborted,s.RenderTimeout,s.RenderCancel].includes(o)?q.info(e.dim(n)):q.error(r)}});$=setTimeout((()=>{f.didError=s.RenderTimeout,D()}),u.getParams().abortDelay),C.on("close",(()=>{f.didError=s.RenderCancel,D()}))}export{l as default};
1
+ import e from"chalk";import r from"react";import{renderToPipeableStream as t}from"react-dom/server";import{createStaticRouter as o,StaticRouterProvider as n}from"react-router-dom/server.mjs";import s from"../constants/stream-error.js";import{ServerProvider as a}from"../context/server.js";import m from"../helpers/handle-response.js";import i from"../helpers/obtain-stream-error.js";import c from"./create-fetch-request.js";import d from"./write-response.js";import l from"../services/ssr-manifest.js";async function p({App:p,handler:u},f,h,{onRouterReady:x,onShellReady:S,onResponse:g,onShellError:R,onError:y,getState:C,abortDelay:E=15e3}){const{req:j,res:v}=h,w=c(j);h.routerContext=await u.query(w,{requestContext:h});const b=m(v,h.routerContext);if(!b)return;l.get(f.getParams().root).injectAssets(h);const{isStream:q=!0}=await(x?.({context:h}))??{};h.isStream=q,h.serverContext={response:null,isServer:!0};const T=o(u.dataRoutes,h.routerContext),A=v.write.bind(v),$=f.getLogger();let B;v.write=(e,...r)=>{const t="string"==typeof e,o=t?e:Buffer.from(e).toString(),n=g?.({context:h,html:o});return n?A(t?`${n}${e}`:Buffer.concat([Buffer.from(n),e]),...r):A(e,...r)};const{serverContext:P,routerContext:k,appProps:D}=h,{pipe:H,abort:L}=t(r.createElement(a,{context:P},r.createElement(p,{server:{...D,req:j}},r.createElement(n,{router:T,context:k,hydrate:!1}))),{onShellReady(){q&&d(h,{pipe:H,statusCode:b,onShellReady:S,getState:C})},onAllReady(){clearTimeout(B),q||d(h,{pipe:H,statusCode:b,onShellReady:S,getState:C})},onShellError(e){const r=R?.({context:h,error:e})||`<!doctype html><p>Something went wrong: ${e.message}</p>`;v.status(500),v.setHeader("content-type","text/html"),v.send(r)},onError(r){clearTimeout(B);const t=i(r),{code:o,message:n}=t,{didError:a}=h;h.didError=a??o,y?.({context:h,error:t}),$.info(e.red(`Stream error. Code: ${o}`)),[s.RenderAborted,s.RenderTimeout,s.RenderCancel].includes(o)?$.info(e.dim(n)):$.error(r)}});B=setTimeout((()=>{h.didError=s.RenderTimeout,L()}),E),j.on("close",(()=>{h.didError=s.RenderCancel,L()}))}export{p as default};
2
2
  //# sourceMappingURL=render.js.map
@@ -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';\nimport SsrManifest from '@services/ssr-manifest';\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 hasEarlyHints?: 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 abortDelay?: number;\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 {\n onRouterReady,\n onShellReady,\n onResponse,\n onShellError,\n onError,\n getState,\n abortDelay = 15000,\n }: IRenderOptions,\n): Promise<void> {\n const { req, res } = context;\n const fetchRequest = createFetchRequest(req);\n\n context.routerContext = (await handler.query(fetchRequest, {\n requestContext: context,\n })) as StaticHandlerContext;\n\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 SsrManifest.get(config.getParams().root).injectAssets(context);\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 }, 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","abortDelay","req","res","fetchRequest","createFetchRequest","routerContext","query","requestContext","statusCode","handleResponse","SsrManifest","get","getParams","root","injectAssets","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","on"],"mappings":"sfA0EAA,eAAeC,GACbC,IAAEA,EAAGC,QAAEA,GACPC,EACAC,GACAC,cACEA,EAAaC,aACbA,EAAYC,WACZA,EAAUC,aACVA,EAAYC,QACZA,EAAOC,SACPA,EAAQC,WACRA,EAAa,OAGf,MAAMC,IAAEA,EAAGC,IAAEA,GAAQT,EACfU,EAAeC,EAAmBH,GAExCR,EAAQY,oBAAuBd,EAAQe,MAAMH,EAAc,CACzDI,eAAgBd,IAMlB,MAAMe,EAAaC,EAAeP,EAAKT,EAAQY,eAE/C,IAAKG,EACH,OAGFE,EAAYC,IAAInB,EAAOoB,YAAYC,MAAMC,aAAarB,GAEtD,MAAMsB,SAAEA,GAAW,SAAgBrB,IAAgB,CAAED,cAAe,GAEpEA,EAAQsB,SAAWA,EACnBtB,EAAQuB,cAAgB,CAAEC,SAAU,KAAMC,UAAU,GAEpD,MAAMC,EAASC,EAAmB7B,EAAQ8B,WAAY5B,EAAQY,eACxDiB,EAAQpB,EAAIoB,MAAMC,KAAKrB,GACvBsB,EAAShC,EAAOiC,YACtB,IAAIC,EAMJxB,EAAIoB,MAAQ,CAACK,KAA8BC,KACzC,MAAMC,EAA2B,iBAATF,EAClBG,EAAOD,EAAWF,EAAOI,OAAOC,KAAKL,GAAMM,WAC3CC,EAAiBtC,IAAa,CAAEH,UAASqC,SAE/C,OAAII,EACKZ,EACLO,EAAW,GAAGK,IAAiBP,IAASI,OAAOI,OAAO,CAACJ,OAAOC,KAAKE,GAAiBP,OACjFC,GAIAN,EAAMK,KAASC,EAAgB,EAGxC,MAAMZ,cAAEA,EAAaX,cAAEA,EAAa+B,SAAEA,GAAa3C,GAE7C4C,KAAEA,EAAIC,MAAEA,GAAUC,EACtBC,EAACC,cAAAC,EAAe,CAAAjD,QAASuB,GACvBwB,EAACC,cAAAnD,GAAIqD,OAAQ,IAAKP,EAAUnC,QAC1BuC,EAAAC,cAACG,EAAqB,CAAAzB,OAAQA,EAAQ1B,QAASY,EAAewC,SAAS,MAG3E,CACElD,eACOoB,GAIL+B,EAAcrD,EAAS,CACrB4C,OACA7B,aACAb,eACAI,YAEH,EACDgD,aACEC,aAAatB,GAETX,GAIJ+B,EAAcrD,EAAS,CACrB4C,OACA7B,aACAb,eACAI,YAEH,EACDF,aAAaoD,GACX,MAAMC,EACJrD,IAAe,CAAEJ,UAAS0D,MAAOF,KACjC,2CAA2CA,EAAEG,cAE/ClD,EAAImD,OAAO,KACXnD,EAAIoD,UAAU,eAAgB,aAC9BpD,EAAIqD,KAAKL,EACV,EACDpD,QAAQ0D,GACNR,aAAatB,GAEb,MAAMyB,EAAQM,EAAkBD,IAC1BE,KAAEA,EAAIN,QAAEA,GAAYD,GACpBQ,SAAEA,GAAalE,EAErBA,EAAQkE,SAAWA,GAAYD,EAE/B5D,IAAU,CAAEL,UAAS0D,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,KACtB5E,EAAQkE,SAAWI,EAAYE,cAC/B3B,GAAO,GACNtC,GAGHC,EAAIqE,GAAG,SAAS,KACd7E,EAAQkE,SAAWI,EAAYG,aAC/B5B,GAAO,GAEX"}