@modern-js/server-core 2.49.1 → 2.49.3-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/cjs/base/adapters/node/bff.js +1 -1
  2. package/dist/cjs/base/adapters/node/middlewares/serverManifest.js +26 -25
  3. package/dist/cjs/base/index.js +4 -0
  4. package/dist/cjs/base/middlewares/customServer/context.js +3 -4
  5. package/dist/cjs/base/middlewares/renderHandler/index.js +11 -3
  6. package/dist/cjs/base/middlewares/renderHandler/render.js +33 -7
  7. package/dist/cjs/base/utils/error.js +17 -2
  8. package/dist/cjs/core/plugin.js +2 -0
  9. package/dist/esm/base/adapters/node/bff.js +1 -1
  10. package/dist/esm/base/adapters/node/middlewares/serverManifest.js +60 -30
  11. package/dist/esm/base/index.js +3 -1
  12. package/dist/esm/base/middlewares/customServer/context.js +3 -3
  13. package/dist/esm/base/middlewares/renderHandler/index.js +31 -5
  14. package/dist/esm/base/middlewares/renderHandler/render.js +181 -39
  15. package/dist/esm/base/utils/error.js +15 -1
  16. package/dist/esm/core/plugin.js +2 -0
  17. package/dist/esm-node/base/adapters/node/bff.js +1 -1
  18. package/dist/esm-node/base/adapters/node/middlewares/serverManifest.js +27 -26
  19. package/dist/esm-node/base/index.js +3 -1
  20. package/dist/esm-node/base/middlewares/customServer/context.js +3 -4
  21. package/dist/esm-node/base/middlewares/renderHandler/index.js +11 -3
  22. package/dist/esm-node/base/middlewares/renderHandler/render.js +34 -8
  23. package/dist/esm-node/base/utils/error.js +14 -1
  24. package/dist/esm-node/core/plugin.js +2 -0
  25. package/dist/types/base/adapters/node/middlewares/serverManifest.d.ts +2 -2
  26. package/dist/types/base/index.d.ts +1 -1
  27. package/dist/types/base/middlewares/renderHandler/index.d.ts +1 -1
  28. package/dist/types/base/middlewares/renderHandler/render.d.ts +9 -2
  29. package/dist/types/base/utils/error.d.ts +7 -0
  30. package/dist/types/core/plugin.d.ts +51 -2
  31. package/package.json +7 -7
@@ -2,7 +2,7 @@ import { _ as _async_to_generator } from "@swc/helpers/_/_async_to_generator";
2
2
  import { _ as _ts_generator } from "@swc/helpers/_/_ts_generator";
3
3
  import { cutNameByHyphen } from "@modern-js/utils/universal";
4
4
  import { REPLACE_REG } from "../../../base/constants";
5
- import { createErrorHtml, sortRoutes, parseQuery, transformResponse, getPathname } from "../../utils";
5
+ import { createErrorHtml, sortRoutes, parseQuery, transformResponse, getPathname, onError as onErrorFn, ErrorDigest } from "../../utils";
6
6
  import { dataHandler } from "./dataHandler";
7
7
  import { ssrRender } from "./ssrRender";
8
8
  function createRender(_) {
@@ -10,19 +10,36 @@ function createRender(_) {
10
10
  }
11
11
  function _createRender() {
12
12
  _createRender = _async_to_generator(function(param) {
13
- var routes, pwd, metaName, staticGenerate, forceCSR, nonce;
13
+ var routes, pwd, metaName, staticGenerate, forceCSR, nonce, onFallbackFn;
14
14
  return _ts_generator(this, function(_state) {
15
- routes = param.routes, pwd = param.pwd, metaName = param.metaName, staticGenerate = param.staticGenerate, forceCSR = param.forceCSR, nonce = param.nonce;
15
+ routes = param.routes, pwd = param.pwd, metaName = param.metaName, staticGenerate = param.staticGenerate, forceCSR = param.forceCSR, nonce = param.nonce, onFallbackFn = param.onFallback;
16
16
  return [
17
17
  2,
18
18
  function() {
19
19
  var _ref = _async_to_generator(function(req, param2) {
20
- var logger, nodeReq, reporter, templates, serverManifest, locals, metrics, routeInfo, html, renderMode, renderOptions, response;
20
+ var logger, nodeReq, reporter, templates, serverManifest, locals, metrics, routeInfo, onFallback, html, renderMode, onError, renderOptions, response;
21
21
  return _ts_generator(this, function(_state2) {
22
22
  switch (_state2.label) {
23
23
  case 0:
24
24
  logger = param2.logger, nodeReq = param2.nodeReq, reporter = param2.reporter, templates = param2.templates, serverManifest = param2.serverManifest, locals = param2.locals, metrics = param2.metrics;
25
25
  routeInfo = matchRoute(req, routes);
26
+ onFallback = function() {
27
+ var _ref2 = _async_to_generator(function(reason, error) {
28
+ return _ts_generator(this, function(_state3) {
29
+ return [
30
+ 2,
31
+ onFallbackFn === null || onFallbackFn === void 0 ? void 0 : onFallbackFn(reason, {
32
+ logger,
33
+ reporter,
34
+ metrics
35
+ }, error)
36
+ ];
37
+ });
38
+ });
39
+ return function onFallback2(reason, error) {
40
+ return _ref2.apply(this, arguments);
41
+ };
42
+ }();
26
43
  if (!routeInfo) {
27
44
  return [
28
45
  2,
@@ -46,7 +63,34 @@ function _createRender() {
46
63
  })
47
64
  ];
48
65
  }
49
- renderMode = getRenderMode(req, metaName || "modern-js", routeInfo.isSSR, forceCSR, nodeReq);
66
+ return [
67
+ 4,
68
+ getRenderMode(req, metaName || "modern-js", routeInfo.isSSR, forceCSR, nodeReq, onFallback)
69
+ ];
70
+ case 1:
71
+ renderMode = _state2.sent();
72
+ onError = function() {
73
+ var _ref2 = _async_to_generator(function(e) {
74
+ return _ts_generator(this, function(_state3) {
75
+ switch (_state3.label) {
76
+ case 0:
77
+ onErrorFn(logger, ErrorDigest.ERENDER, e, req);
78
+ return [
79
+ 4,
80
+ onFallback === null || onFallback === void 0 ? void 0 : onFallback("error", e)
81
+ ];
82
+ case 1:
83
+ _state3.sent();
84
+ return [
85
+ 2
86
+ ];
87
+ }
88
+ });
89
+ });
90
+ return function onError2(e) {
91
+ return _ref2.apply(this, arguments);
92
+ };
93
+ }();
50
94
  renderOptions = {
51
95
  pwd,
52
96
  html,
@@ -66,55 +110,55 @@ function _createRender() {
66
110
  case "data":
67
111
  return [
68
112
  3,
69
- 1
113
+ 2
70
114
  ];
71
115
  case "ssr":
72
116
  return [
73
117
  3,
74
- 5
118
+ 6
75
119
  ];
76
120
  case "csr":
77
121
  return [
78
122
  3,
79
- 5
123
+ 6
80
124
  ];
81
125
  }
82
126
  return [
83
127
  3,
84
- 6
128
+ 7
85
129
  ];
86
- case 1:
130
+ case 2:
87
131
  return [
88
132
  4,
89
133
  dataHandler(req, renderOptions)
90
134
  ];
91
- case 2:
135
+ case 3:
92
136
  response = _state2.sent();
93
137
  if (!!response)
94
138
  return [
95
139
  3,
96
- 4
140
+ 5
97
141
  ];
98
142
  return [
99
143
  4,
100
- renderHandler(req, renderOptions, "ssr")
144
+ renderHandler(req, renderOptions, "ssr", onError)
101
145
  ];
102
- case 3:
103
- response = _state2.sent();
104
- _state2.label = 4;
105
146
  case 4:
147
+ response = _state2.sent();
148
+ _state2.label = 5;
149
+ case 5:
106
150
  return [
107
151
  2,
108
152
  response
109
153
  ];
110
- case 5:
154
+ case 6:
111
155
  return [
112
156
  2,
113
- renderHandler(req, renderOptions, renderMode)
157
+ renderHandler(req, renderOptions, renderMode, onError)
114
158
  ];
115
- case 6:
116
- throw new Error("Unknown render mode: ".concat(renderMode));
117
159
  case 7:
160
+ throw new Error("Unknown render mode: ".concat(renderMode));
161
+ case 8:
118
162
  return [
119
163
  2
120
164
  ];
@@ -130,12 +174,12 @@ function _createRender() {
130
174
  });
131
175
  return _createRender.apply(this, arguments);
132
176
  }
133
- function renderHandler(request, options, mode) {
177
+ function renderHandler(request, options, mode, onError) {
134
178
  return _renderHandler.apply(this, arguments);
135
179
  }
136
180
  function _renderHandler() {
137
- _renderHandler = _async_to_generator(function(request, options, mode) {
138
- var serverData, response;
181
+ _renderHandler = _async_to_generator(function(request, options, mode, onError) {
182
+ var serverData, response, e;
139
183
  return _ts_generator(this, function(_state) {
140
184
  switch (_state.label) {
141
185
  case 0:
@@ -145,12 +189,51 @@ function _renderHandler() {
145
189
  params: {}
146
190
  }
147
191
  };
192
+ if (!(mode === "ssr"))
193
+ return [
194
+ 3,
195
+ 6
196
+ ];
197
+ _state.label = 1;
198
+ case 1:
199
+ _state.trys.push([
200
+ 1,
201
+ 3,
202
+ ,
203
+ 5
204
+ ]);
148
205
  return [
149
206
  4,
150
- mode === "ssr" ? ssrRender(request, options) : csrRender(options.html)
207
+ ssrRender(request, options)
151
208
  ];
152
- case 1:
209
+ case 2:
153
210
  response = _state.sent();
211
+ return [
212
+ 3,
213
+ 5
214
+ ];
215
+ case 3:
216
+ e = _state.sent();
217
+ return [
218
+ 4,
219
+ onError(e)
220
+ ];
221
+ case 4:
222
+ _state.sent();
223
+ response = csrRender(options.html);
224
+ return [
225
+ 3,
226
+ 5
227
+ ];
228
+ case 5:
229
+ return [
230
+ 3,
231
+ 7
232
+ ];
233
+ case 6:
234
+ response = csrRender(options.html);
235
+ _state.label = 7;
236
+ case 7:
154
237
  return [
155
238
  2,
156
239
  transformResponse(response, injectServerData(serverData))
@@ -187,20 +270,79 @@ function matchRoute(req, routes) {
187
270
  }
188
271
  return void 0;
189
272
  }
190
- function getRenderMode(req, framework, isSSR, forceCSR, nodeReq) {
191
- var query = parseQuery(req);
192
- var fallbackHeader = "x-".concat(cutNameByHyphen(framework), "-ssr-fallback");
193
- if (isSSR) {
194
- if (query.__loader) {
195
- return "data";
196
- }
197
- if (forceCSR && (query.csr || req.headers.get(fallbackHeader) || (nodeReq === null || nodeReq === void 0 ? void 0 : nodeReq.headers[fallbackHeader]))) {
198
- return "csr";
199
- }
200
- return "ssr";
201
- } else {
202
- return "csr";
203
- }
273
+ function getRenderMode(req, framework, isSSR, forceCSR, nodeReq, onFallback) {
274
+ return _getRenderMode.apply(this, arguments);
275
+ }
276
+ function _getRenderMode() {
277
+ _getRenderMode = _async_to_generator(function(req, framework, isSSR, forceCSR, nodeReq, onFallback) {
278
+ var query, fallbackHeader;
279
+ return _ts_generator(this, function(_state) {
280
+ switch (_state.label) {
281
+ case 0:
282
+ query = parseQuery(req);
283
+ fallbackHeader = "x-".concat(cutNameByHyphen(framework), "-ssr-fallback");
284
+ if (!isSSR)
285
+ return [
286
+ 3,
287
+ 6
288
+ ];
289
+ if (query.__loader) {
290
+ return [
291
+ 2,
292
+ "data"
293
+ ];
294
+ }
295
+ if (!(forceCSR && (query.csr || req.headers.get(fallbackHeader) || (nodeReq === null || nodeReq === void 0 ? void 0 : nodeReq.headers[fallbackHeader]))))
296
+ return [
297
+ 3,
298
+ 5
299
+ ];
300
+ if (!query.csr)
301
+ return [
302
+ 3,
303
+ 2
304
+ ];
305
+ return [
306
+ 4,
307
+ onFallback === null || onFallback === void 0 ? void 0 : onFallback("query")
308
+ ];
309
+ case 1:
310
+ _state.sent();
311
+ return [
312
+ 3,
313
+ 4
314
+ ];
315
+ case 2:
316
+ return [
317
+ 4,
318
+ onFallback === null || onFallback === void 0 ? void 0 : onFallback("header")
319
+ ];
320
+ case 3:
321
+ _state.sent();
322
+ _state.label = 4;
323
+ case 4:
324
+ return [
325
+ 2,
326
+ "csr"
327
+ ];
328
+ case 5:
329
+ return [
330
+ 2,
331
+ "ssr"
332
+ ];
333
+ case 6:
334
+ return [
335
+ 2,
336
+ "csr"
337
+ ];
338
+ case 7:
339
+ return [
340
+ 2
341
+ ];
342
+ }
343
+ });
344
+ });
345
+ return _getRenderMode.apply(this, arguments);
204
346
  }
205
347
  function csrRender(html) {
206
348
  return new Response(html, {
@@ -1,3 +1,4 @@
1
+ import { _ as _instanceof } from "@swc/helpers/_/_instanceof";
1
2
  var ERROR_PAGE_TEXT = {
2
3
  404: "This page could not be found.",
3
4
  500: "Internal Server Error."
@@ -7,6 +8,19 @@ var createErrorHtml = function(status) {
7
8
  var title = "".concat(status, ": ").concat(text);
8
9
  return '<!DOCTYPE html>\n <html lang="en">\n <head>\n <meta charset="utf-8">\n <meta name="viewport" content="width=device-width">\n <title>'.concat(title, '</title>\n <style>\n html,body {\n margin: 0;\n }\n\n .page-container {\n color: #000;\n background: #fff;\n height: 100vh;\n text-align: center;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n }\n </style>\n </head>\n <body>\n <div class="page-container">\n <h1>').concat(status, "</h1>\n <div>").concat(text, "</div>\n </body>\n </html>\n ");
9
10
  };
11
+ var ErrorDigest;
12
+ (function(ErrorDigest2) {
13
+ ErrorDigest2["ENOTF"] = "Page could not be found";
14
+ ErrorDigest2["EINTER"] = "Internal server error";
15
+ ErrorDigest2["ERENDER"] = "SSR render failed";
16
+ })(ErrorDigest || (ErrorDigest = {}));
17
+ function onError(logger, digest, error, req) {
18
+ var headers = req === null || req === void 0 ? void 0 : req.headers;
19
+ headers === null || headers === void 0 ? void 0 : headers.delete("cookie");
20
+ logger.error(req ? "Server Error - ".concat(digest, ", error = %s, req.url = %s, req.headers = %o") : "Server Error - ".concat(digest, ", error = %s"), _instanceof(error, Error) ? error.stack || error.message : error, req === null || req === void 0 ? void 0 : req.url, headers);
21
+ }
10
22
  export {
11
- createErrorHtml
23
+ ErrorDigest,
24
+ createErrorHtml,
25
+ onError
12
26
  };
@@ -3,6 +3,7 @@ var gather = createParallelWorkflow();
3
3
  var config = createWaterfall();
4
4
  var prepare = createWaterfall();
5
5
  var prepareWebServer = createAsyncPipeline();
6
+ var fallback = createParallelWorkflow();
6
7
  var prepareApiServer = createAsyncPipeline();
7
8
  var onApiChange = createAsyncWaterfall();
8
9
  var repack = createWaterfall();
@@ -32,6 +33,7 @@ var serverHooks = {
32
33
  gather,
33
34
  config,
34
35
  prepare,
36
+ fallback,
35
37
  prepareWebServer,
36
38
  prepareApiServer,
37
39
  repack,
@@ -21,7 +21,7 @@ const bindBFFHandler = async (server, options) => {
21
21
  await next();
22
22
  };
23
23
  } else {
24
- const renderHandler = enableHandleWeb ? await getRenderHandler(options) : null;
24
+ const renderHandler = enableHandleWeb ? await getRenderHandler(options, server) : null;
25
25
  handler = await server.runner.prepareApiServer({
26
26
  pwd: options.pwd,
27
27
  prefix,
@@ -1,33 +1,33 @@
1
1
  import path from "path";
2
- import { LOADABLE_STATS_FILE, MAIN_ENTRY_NAME, ROUTE_MANIFEST_FILE, SERVER_BUNDLE_DIRECTORY } from "@modern-js/utils";
3
- async function getServerManifest(pwd, routes) {
2
+ import { LOADABLE_STATS_FILE, MAIN_ENTRY_NAME, ROUTE_MANIFEST_FILE, SERVER_BUNDLE_DIRECTORY, fs } from "@modern-js/utils";
3
+ const dynamicImport = (filePath) => {
4
+ try {
5
+ const module = require(filePath);
6
+ return Promise.resolve(module);
7
+ } catch (e) {
8
+ return Promise.reject(e);
9
+ }
10
+ };
11
+ const loadBundle = async (filepath, logger) => {
12
+ if (!await fs.pathExists(filepath)) {
13
+ return void 0;
14
+ }
15
+ return dynamicImport(filepath).catch((e) => {
16
+ logger.error(`Load ${filepath} bundle failed, error = %s`, e instanceof Error ? e.stack || e.message : e);
17
+ return void 0;
18
+ });
19
+ };
20
+ async function getServerManifest(pwd, routes, logger) {
4
21
  const loaderBundles = {};
5
22
  const renderBundles = {};
6
- await Promise.all(routes.map(async (route) => {
23
+ await Promise.all(routes.filter((route) => Boolean(route.bundle)).map(async (route) => {
7
24
  const entryName = route.entryName || MAIN_ENTRY_NAME;
8
- const loaderBundlePath = path.join(pwd, SERVER_BUNDLE_DIRECTORY, `${entryName}-server-loaders.js`);
9
25
  const renderBundlePath = path.join(pwd, route.bundle || "");
10
- const dynamicImport = (filePath) => {
11
- try {
12
- const module = require(filePath);
13
- return Promise.resolve(module);
14
- } catch (e) {
15
- return Promise.reject(e);
16
- }
17
- };
18
- await Promise.allSettled([
19
- dynamicImport(loaderBundlePath),
20
- dynamicImport(renderBundlePath)
21
- ]).then((results) => {
22
- const { status: loaderStatus } = results[0];
23
- if (loaderStatus === "fulfilled") {
24
- loaderBundles[entryName] = results[0].value;
25
- }
26
- const { status: renderStatus } = results[1];
27
- if (renderStatus === "fulfilled") {
28
- renderBundles[entryName] = results[1].value;
29
- }
30
- });
26
+ const loaderBundlePath = path.join(pwd, SERVER_BUNDLE_DIRECTORY, `${entryName}-server-loaders.js`);
27
+ const renderBundle = await loadBundle(renderBundlePath, logger);
28
+ const loaderBundle = await loadBundle(loaderBundlePath, logger);
29
+ renderBundle && (renderBundles[entryName] = renderBundle);
30
+ loaderBundle && (loaderBundles[entryName] = loaderBundle);
31
31
  }));
32
32
  const loadableUri = path.join(pwd, LOADABLE_STATS_FILE);
33
33
  const loadableStats = await import(loadableUri).catch((_) => ({}));
@@ -43,7 +43,8 @@ async function getServerManifest(pwd, routes) {
43
43
  function injectServerManifest(pwd, routes) {
44
44
  return async (c, next) => {
45
45
  if (routes && !c.get("serverManifest")) {
46
- const serverManifest = await getServerManifest(pwd, routes);
46
+ const logger = c.get("logger");
47
+ const serverManifest = await getServerManifest(pwd, routes, logger);
47
48
  c.set("serverManifest", serverManifest);
48
49
  }
49
50
  await next();
@@ -1,9 +1,10 @@
1
- import { createErrorHtml } from "./utils";
1
+ import { createErrorHtml, onError, ErrorDigest } from "./utils";
2
2
  import { AGGRED_DIR } from "./constants";
3
3
  import { favionFallbackMiddleware, injectReporter, injectLogger, getRenderHandler, bindRenderHandler, logHandler, processedBy } from "./middlewares";
4
4
  import { createServerBase } from "./serverBase";
5
5
  export {
6
6
  AGGRED_DIR,
7
+ ErrorDigest,
7
8
  bindRenderHandler,
8
9
  createErrorHtml,
9
10
  createServerBase,
@@ -12,5 +13,6 @@ export {
12
13
  injectLogger,
13
14
  injectReporter,
14
15
  logHandler,
16
+ onError,
15
17
  processedBy
16
18
  };
@@ -17,13 +17,12 @@ function createCustomMiddlewaresCtx(c, locals) {
17
17
  var _c_env_node, _c_env_node1;
18
18
  const baseContext = createBaseHookContext(c);
19
19
  const reporter = c.get("reporter");
20
+ const response = baseContext.response;
21
+ response.locals = locals;
20
22
  return {
21
23
  ...baseContext,
22
24
  reporter,
23
- response: {
24
- ...baseContext.response,
25
- locals
26
- },
25
+ response,
27
26
  source: {
28
27
  req: (_c_env_node = c.env.node) === null || _c_env_node === void 0 ? void 0 : _c_env_node.req,
29
28
  res: (_c_env_node1 = c.env.node) === null || _c_env_node1 === void 0 ? void 0 : _c_env_node1.res
@@ -25,8 +25,15 @@ function createRenderHandler(render) {
25
25
  return res;
26
26
  };
27
27
  }
28
- async function getRenderHandler(options) {
28
+ async function getRenderHandler(options, serverBase) {
29
29
  const { routes, pwd, config } = options;
30
+ const onFallback = async (reason, utils, error) => {
31
+ await (serverBase === null || serverBase === void 0 ? void 0 : serverBase.runner.fallback({
32
+ reason,
33
+ error,
34
+ ...utils
35
+ }));
36
+ };
30
37
  if (routes && routes.length > 0) {
31
38
  var _config_server, _options_config_security;
32
39
  const ssrConfig = (_config_server = config.server) === null || _config_server === void 0 ? void 0 : _config_server.ssr;
@@ -37,7 +44,8 @@ async function getRenderHandler(options) {
37
44
  staticGenerate: options.staticGenerate,
38
45
  metaName: options.metaName || "modern-js",
39
46
  forceCSR,
40
- nonce: (_options_config_security = options.config.security) === null || _options_config_security === void 0 ? void 0 : _options_config_security.nonce
47
+ nonce: (_options_config_security = options.config.security) === null || _options_config_security === void 0 ? void 0 : _options_config_security.nonce,
48
+ onFallback
41
49
  });
42
50
  return render;
43
51
  }
@@ -54,7 +62,7 @@ async function bindRenderHandler(server, options) {
54
62
  await ssrCache.loadCacheMod(checkIsProd() ? pwd : void 0);
55
63
  }
56
64
  const pageRoutes = routes.filter((route) => !route.isApi).sort(sortRoutes);
57
- const render = await getRenderHandler(options);
65
+ const render = await getRenderHandler(options, server);
58
66
  for (const route of pageRoutes) {
59
67
  const { urlPath: originUrlPath, entryName } = route;
60
68
  const urlPath = originUrlPath.endsWith("/") ? `${originUrlPath}*` : `${originUrlPath}/*`;
@@ -1,11 +1,18 @@
1
1
  import { cutNameByHyphen } from "@modern-js/utils/universal";
2
2
  import { REPLACE_REG } from "../../../base/constants";
3
- import { createErrorHtml, sortRoutes, parseQuery, transformResponse, getPathname } from "../../utils";
3
+ import { createErrorHtml, sortRoutes, parseQuery, transformResponse, getPathname, onError as onErrorFn, ErrorDigest } from "../../utils";
4
4
  import { dataHandler } from "./dataHandler";
5
5
  import { ssrRender } from "./ssrRender";
6
- async function createRender({ routes, pwd, metaName, staticGenerate, forceCSR, nonce }) {
6
+ async function createRender({ routes, pwd, metaName, staticGenerate, forceCSR, nonce, onFallback: onFallbackFn }) {
7
7
  return async (req, { logger, nodeReq, reporter, templates, serverManifest, locals, metrics }) => {
8
8
  const routeInfo = matchRoute(req, routes);
9
+ const onFallback = async (reason, error) => {
10
+ return onFallbackFn === null || onFallbackFn === void 0 ? void 0 : onFallbackFn(reason, {
11
+ logger,
12
+ reporter,
13
+ metrics
14
+ }, error);
15
+ };
9
16
  if (!routeInfo) {
10
17
  return new Response(createErrorHtml(404), {
11
18
  status: 404,
@@ -23,7 +30,11 @@ async function createRender({ routes, pwd, metaName, staticGenerate, forceCSR, n
23
30
  }
24
31
  });
25
32
  }
26
- const renderMode = getRenderMode(req, metaName || "modern-js", routeInfo.isSSR, forceCSR, nodeReq);
33
+ const renderMode = await getRenderMode(req, metaName || "modern-js", routeInfo.isSSR, forceCSR, nodeReq, onFallback);
34
+ const onError = async (e) => {
35
+ onErrorFn(logger, ErrorDigest.ERENDER, e, req);
36
+ await (onFallback === null || onFallback === void 0 ? void 0 : onFallback("error", e));
37
+ };
27
38
  const renderOptions = {
28
39
  pwd,
29
40
  html,
@@ -43,25 +54,35 @@ async function createRender({ routes, pwd, metaName, staticGenerate, forceCSR, n
43
54
  case "data":
44
55
  let response = await dataHandler(req, renderOptions);
45
56
  if (!response) {
46
- response = await renderHandler(req, renderOptions, "ssr");
57
+ response = await renderHandler(req, renderOptions, "ssr", onError);
47
58
  }
48
59
  return response;
49
60
  case "ssr":
50
61
  case "csr":
51
- return renderHandler(req, renderOptions, renderMode);
62
+ return renderHandler(req, renderOptions, renderMode, onError);
52
63
  default:
53
64
  throw new Error(`Unknown render mode: ${renderMode}`);
54
65
  }
55
66
  };
56
67
  }
57
- async function renderHandler(request, options, mode) {
68
+ async function renderHandler(request, options, mode, onError) {
58
69
  const serverData = {
59
70
  router: {
60
71
  baseUrl: options.routeInfo.urlPath,
61
72
  params: {}
62
73
  }
63
74
  };
64
- const response = await (mode === "ssr" ? ssrRender(request, options) : csrRender(options.html));
75
+ let response;
76
+ if (mode === "ssr") {
77
+ try {
78
+ response = await ssrRender(request, options);
79
+ } catch (e) {
80
+ await onError(e);
81
+ response = csrRender(options.html);
82
+ }
83
+ } else {
84
+ response = csrRender(options.html);
85
+ }
65
86
  return transformResponse(response, injectServerData(serverData));
66
87
  }
67
88
  function matchRoute(req, routes) {
@@ -74,7 +95,7 @@ function matchRoute(req, routes) {
74
95
  }
75
96
  return void 0;
76
97
  }
77
- function getRenderMode(req, framework, isSSR, forceCSR, nodeReq) {
98
+ async function getRenderMode(req, framework, isSSR, forceCSR, nodeReq, onFallback) {
78
99
  const query = parseQuery(req);
79
100
  const fallbackHeader = `x-${cutNameByHyphen(framework)}-ssr-fallback`;
80
101
  if (isSSR) {
@@ -82,6 +103,11 @@ function getRenderMode(req, framework, isSSR, forceCSR, nodeReq) {
82
103
  return "data";
83
104
  }
84
105
  if (forceCSR && (query.csr || req.headers.get(fallbackHeader) || (nodeReq === null || nodeReq === void 0 ? void 0 : nodeReq.headers[fallbackHeader]))) {
106
+ if (query.csr) {
107
+ await (onFallback === null || onFallback === void 0 ? void 0 : onFallback("query"));
108
+ } else {
109
+ await (onFallback === null || onFallback === void 0 ? void 0 : onFallback("header"));
110
+ }
85
111
  return "csr";
86
112
  }
87
113
  return "ssr";
@@ -36,6 +36,19 @@ const createErrorHtml = (status) => {
36
36
  </html>
37
37
  `;
38
38
  };
39
+ var ErrorDigest;
40
+ (function(ErrorDigest2) {
41
+ ErrorDigest2["ENOTF"] = "Page could not be found";
42
+ ErrorDigest2["EINTER"] = "Internal server error";
43
+ ErrorDigest2["ERENDER"] = "SSR render failed";
44
+ })(ErrorDigest || (ErrorDigest = {}));
45
+ function onError(logger, digest, error, req) {
46
+ const headers = req === null || req === void 0 ? void 0 : req.headers;
47
+ headers === null || headers === void 0 ? void 0 : headers.delete("cookie");
48
+ logger.error(req ? `Server Error - ${digest}, error = %s, req.url = %s, req.headers = %o` : `Server Error - ${digest}, error = %s`, error instanceof Error ? error.stack || error.message : error, req === null || req === void 0 ? void 0 : req.url, headers);
49
+ }
39
50
  export {
40
- createErrorHtml
51
+ ErrorDigest,
52
+ createErrorHtml,
53
+ onError
41
54
  };
@@ -3,6 +3,7 @@ const gather = createParallelWorkflow();
3
3
  const config = createWaterfall();
4
4
  const prepare = createWaterfall();
5
5
  const prepareWebServer = createAsyncPipeline();
6
+ const fallback = createParallelWorkflow();
6
7
  const prepareApiServer = createAsyncPipeline();
7
8
  const onApiChange = createAsyncWaterfall();
8
9
  const repack = createWaterfall();
@@ -26,6 +27,7 @@ const serverHooks = {
26
27
  gather,
27
28
  config,
28
29
  prepare,
30
+ fallback,
29
31
  prepareWebServer,
30
32
  prepareApiServer,
31
33
  repack,
@@ -1,4 +1,4 @@
1
- import type { ServerRoute } from '@modern-js/types';
1
+ import type { ServerRoute, Logger } from '@modern-js/types';
2
2
  import { Middleware, ServerEnv, ServerManifest } from '../../../../core/server';
3
- export declare function getServerManifest(pwd: string, routes: ServerRoute[]): Promise<ServerManifest>;
3
+ export declare function getServerManifest(pwd: string, routes: ServerRoute[], logger: Logger): Promise<ServerManifest>;
4
4
  export declare function injectServerManifest(pwd: string, routes?: ServerRoute[]): Middleware<ServerEnv>;
@@ -1,4 +1,4 @@
1
- export { createErrorHtml } from './utils';
1
+ export { createErrorHtml, onError, ErrorDigest } from './utils';
2
2
  export { AGGRED_DIR } from './constants';
3
3
  export { favionFallbackMiddleware, injectReporter, injectLogger, getRenderHandler, bindRenderHandler, logHandler, processedBy, } from './middlewares';
4
4
  export type { BindRenderHandleOptions } from './middlewares';
@@ -5,5 +5,5 @@ export type BindRenderHandleOptions = {
5
5
  staticGenerate?: boolean;
6
6
  disableCustomHook?: boolean;
7
7
  };
8
- export declare function getRenderHandler(options: ServerBaseOptions & BindRenderHandleOptions): Promise<Render | null>;
8
+ export declare function getRenderHandler(options: ServerBaseOptions & BindRenderHandleOptions, serverBase?: ServerBase): Promise<Render | null>;
9
9
  export declare function bindRenderHandler(server: ServerBase, options: ServerBaseOptions & BindRenderHandleOptions): Promise<void>;