@modern-js/prod-server 1.21.5 → 2.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/CHANGELOG.md +91 -10
  2. package/dist/js/modern/constants.js +0 -2
  3. package/dist/js/modern/index.js +0 -1
  4. package/dist/js/modern/libs/context/context.js +10 -56
  5. package/dist/js/modern/libs/hook-api/index.js +125 -0
  6. package/dist/js/modern/libs/hook-api/route.js +13 -36
  7. package/dist/js/modern/libs/hook-api/template.js +38 -15
  8. package/dist/js/modern/libs/loadConfig.js +3 -10
  9. package/dist/js/modern/libs/metrics.js +6 -6
  10. package/dist/js/modern/libs/proxy.js +7 -12
  11. package/dist/js/modern/libs/render/cache/__tests__/cache.fun.test.js +0 -1
  12. package/dist/js/modern/libs/render/cache/__tests__/cache.test.js +0 -9
  13. package/dist/js/modern/libs/render/cache/index.js +37 -28
  14. package/dist/js/modern/libs/render/cache/page-caches/lru.js +0 -10
  15. package/dist/js/modern/libs/render/cache/spr.js +12 -62
  16. package/dist/js/modern/libs/render/cache/util.js +0 -6
  17. package/dist/js/modern/libs/render/index.js +6 -11
  18. package/dist/js/modern/libs/render/measure.js +7 -10
  19. package/dist/js/modern/libs/render/modern/index.js +2 -13
  20. package/dist/js/modern/libs/render/reader.js +13 -24
  21. package/dist/js/modern/libs/render/ssr.js +24 -12
  22. package/dist/js/modern/libs/render/static.js +6 -9
  23. package/dist/js/modern/libs/render/type.js +0 -1
  24. package/dist/js/modern/libs/route/index.js +8 -19
  25. package/dist/js/modern/libs/route/matcher.js +21 -29
  26. package/dist/js/modern/libs/route/route.js +0 -13
  27. package/dist/js/modern/libs/serve-file.js +13 -6
  28. package/dist/js/modern/server/index.js +27 -43
  29. package/dist/js/modern/server/modern-server-split.js +5 -55
  30. package/dist/js/modern/server/modern-server.js +179 -250
  31. package/dist/js/modern/utils.js +2 -21
  32. package/dist/js/modern/worker-server.js +34 -0
  33. package/dist/js/node/constants.js +0 -2
  34. package/dist/js/node/index.js +0 -10
  35. package/dist/js/node/libs/context/context.js +10 -65
  36. package/dist/js/node/libs/context/index.js +0 -3
  37. package/dist/js/node/libs/hook-api/index.js +136 -0
  38. package/dist/js/node/libs/hook-api/route.js +13 -38
  39. package/dist/js/node/libs/hook-api/template.js +40 -18
  40. package/dist/js/node/libs/loadConfig.js +3 -22
  41. package/dist/js/node/libs/metrics.js +6 -6
  42. package/dist/js/node/libs/proxy.js +7 -17
  43. package/dist/js/node/libs/render/cache/__tests__/cache.fun.test.js +0 -5
  44. package/dist/js/node/libs/render/cache/__tests__/cache.test.js +0 -12
  45. package/dist/js/node/libs/render/cache/index.js +37 -33
  46. package/dist/js/node/libs/render/cache/page-caches/index.js +0 -2
  47. package/dist/js/node/libs/render/cache/page-caches/lru.js +0 -14
  48. package/dist/js/node/libs/render/cache/spr.js +12 -71
  49. package/dist/js/node/libs/render/cache/util.js +0 -18
  50. package/dist/js/node/libs/render/index.js +6 -26
  51. package/dist/js/node/libs/render/measure.js +5 -15
  52. package/dist/js/node/libs/render/modern/index.js +2 -20
  53. package/dist/js/node/libs/render/reader.js +12 -39
  54. package/dist/js/node/libs/render/ssr.js +23 -22
  55. package/dist/js/node/libs/render/static.js +6 -18
  56. package/dist/js/node/libs/render/type.js +0 -1
  57. package/dist/js/node/libs/route/index.js +8 -22
  58. package/dist/js/node/libs/route/matcher.js +18 -34
  59. package/dist/js/node/libs/route/route.js +0 -15
  60. package/dist/js/node/libs/serve-file.js +15 -12
  61. package/dist/js/node/server/index.js +26 -62
  62. package/dist/js/node/server/modern-server-split.js +5 -58
  63. package/dist/js/node/server/modern-server.js +178 -280
  64. package/dist/js/node/utils.js +3 -46
  65. package/dist/js/node/worker-server.js +41 -0
  66. package/dist/js/treeshaking/constants.js +28 -0
  67. package/dist/js/treeshaking/index.js +13 -0
  68. package/dist/js/treeshaking/libs/context/context.js +243 -0
  69. package/dist/js/treeshaking/libs/context/index.js +5 -0
  70. package/dist/js/treeshaking/libs/hook-api/index.js +157 -0
  71. package/dist/js/treeshaking/libs/hook-api/route.js +33 -0
  72. package/dist/js/treeshaking/libs/hook-api/template.js +91 -0
  73. package/dist/js/treeshaking/libs/loadConfig.js +39 -0
  74. package/dist/js/treeshaking/libs/metrics.js +12 -0
  75. package/dist/js/treeshaking/libs/proxy.js +80 -0
  76. package/dist/js/treeshaking/libs/render/cache/__tests__/cache.fun.test.js +124 -0
  77. package/dist/js/treeshaking/libs/render/cache/__tests__/cache.test.js +464 -0
  78. package/dist/js/treeshaking/libs/render/cache/__tests__/cacheable.js +53 -0
  79. package/dist/js/treeshaking/libs/render/cache/__tests__/error-configuration.js +35 -0
  80. package/dist/js/treeshaking/libs/render/cache/__tests__/matched-cache.js +121 -0
  81. package/dist/js/treeshaking/libs/render/cache/index.js +184 -0
  82. package/dist/js/treeshaking/libs/render/cache/page-caches/index.js +30 -0
  83. package/dist/js/treeshaking/libs/render/cache/page-caches/lru.js +46 -0
  84. package/dist/js/treeshaking/libs/render/cache/spr.js +362 -0
  85. package/dist/js/treeshaking/libs/render/cache/type.js +1 -0
  86. package/dist/js/treeshaking/libs/render/cache/util.js +101 -0
  87. package/dist/js/treeshaking/libs/render/index.js +100 -0
  88. package/dist/js/treeshaking/libs/render/measure.js +61 -0
  89. package/dist/js/treeshaking/libs/render/modern/browser-list.js +7 -0
  90. package/dist/js/treeshaking/libs/render/modern/index.js +39 -0
  91. package/dist/js/treeshaking/libs/render/reader.js +191 -0
  92. package/dist/js/treeshaking/libs/render/ssr.js +98 -0
  93. package/dist/js/treeshaking/libs/render/static.js +84 -0
  94. package/dist/js/treeshaking/libs/render/type.js +6 -0
  95. package/dist/js/treeshaking/libs/route/index.js +94 -0
  96. package/dist/js/treeshaking/libs/route/matcher.js +113 -0
  97. package/dist/js/treeshaking/libs/route/route.js +26 -0
  98. package/dist/js/treeshaking/libs/serve-file.js +75 -0
  99. package/dist/js/treeshaking/server/index.js +343 -0
  100. package/dist/js/treeshaking/server/modern-server-split.js +152 -0
  101. package/dist/js/treeshaking/server/modern-server.js +945 -0
  102. package/dist/js/treeshaking/type.js +1 -0
  103. package/dist/js/treeshaking/utils.js +87 -0
  104. package/dist/js/treeshaking/worker-server.js +56 -0
  105. package/dist/types/index.d.ts +0 -2
  106. package/dist/types/libs/context/context.d.ts +0 -3
  107. package/dist/types/libs/hook-api/index.d.ts +5 -0
  108. package/dist/types/libs/hook-api/route.d.ts +9 -14
  109. package/dist/types/libs/hook-api/template.d.ts +19 -9
  110. package/dist/types/libs/loadConfig.d.ts +0 -1
  111. package/dist/types/libs/render/cache/index.d.ts +4 -4
  112. package/dist/types/libs/render/cache/spr.d.ts +0 -2
  113. package/dist/types/libs/render/type.d.ts +3 -1
  114. package/dist/types/libs/serve-file.d.ts +2 -1
  115. package/dist/types/server/index.d.ts +2 -3
  116. package/dist/types/server/modern-server.d.ts +11 -11
  117. package/dist/types/type.d.ts +8 -10
  118. package/dist/types/utils.d.ts +0 -1
  119. package/dist/types/worker-server.d.ts +16 -0
  120. package/package.json +28 -35
@@ -1,38 +1,33 @@
1
1
  const _excluded = ["getMiddlewares"];
2
-
3
2
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
4
-
5
3
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
6
-
7
4
  function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
8
-
9
5
  function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
10
-
11
6
  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
12
-
13
7
  /* eslint-disable max-lines */
14
8
  import { createServer } from 'http';
15
9
  import util from 'util';
16
10
  import path from 'path';
17
11
  import { fs, mime, ROUTE_SPEC_FILE } from '@modern-js/utils';
18
- import axios from 'axios';
19
- import { clone } from '@modern-js/utils/lodash';
20
12
  import { RouteMatchManager } from "../libs/route";
21
13
  import { createRenderHandler } from "../libs/render";
22
- import { createStaticFileHandler } from "../libs/serve-file";
14
+ import { createStaticFileHandler, faviconFallbackHandler } from "../libs/serve-file";
23
15
  import { createErrorDocument, createMiddlewareCollecter, getStaticReg, mergeExtension, noop, debug, isRedirect } from "../utils";
24
16
  import * as reader from "../libs/render/reader";
25
17
  import { createProxyHandler } from "../libs/proxy";
26
18
  import { createContext } from "../libs/context";
19
+ import { templateInjectableStream } from "../libs/hook-api/template";
27
20
  import { AGGRED_DIR, ERROR_DIGEST, ERROR_PAGE_TEXT, RUN_MODE } from "../constants";
28
- import { createTemplateAPI } from "../libs/hook-api/template";
29
- import { createRouteAPI } from "../libs/hook-api/route";
21
+ import { createAfterMatchContext, createAfterRenderContext, createMiddlewareContext } from "../libs/hook-api";
30
22
  const API_DIR = './api';
31
23
  const SERVER_DIR = './server';
32
24
  export class ModernServer {
33
25
  // appDirectory
26
+
34
27
  // product dist dir
28
+
35
29
  // work on src or dist
30
+
36
31
  constructor({
37
32
  pwd,
38
33
  config,
@@ -44,49 +39,28 @@ export class ModernServer {
44
39
  proxyTarget
45
40
  }) {
46
41
  var _config$output;
47
-
48
42
  _defineProperty(this, "pwd", void 0);
49
-
50
43
  _defineProperty(this, "distDir", void 0);
51
-
52
44
  _defineProperty(this, "workDir", void 0);
53
-
54
45
  _defineProperty(this, "router", void 0);
55
-
56
46
  _defineProperty(this, "conf", void 0);
57
-
58
47
  _defineProperty(this, "handlers", []);
59
-
60
48
  _defineProperty(this, "presetRoutes", void 0);
61
-
62
49
  _defineProperty(this, "runner", void 0);
63
-
64
50
  _defineProperty(this, "logger", void 0);
65
-
66
51
  _defineProperty(this, "metrics", void 0);
67
-
68
52
  _defineProperty(this, "runMode", void 0);
69
-
70
53
  _defineProperty(this, "reader", reader);
71
-
72
54
  _defineProperty(this, "proxyTarget", void 0);
73
-
74
55
  _defineProperty(this, "staticFileHandler", void 0);
75
-
76
56
  _defineProperty(this, "routeRenderHandler", void 0);
77
-
57
+ _defineProperty(this, "beforeRouteHandler", null);
78
58
  _defineProperty(this, "frameWebHandler", null);
79
-
80
59
  _defineProperty(this, "frameAPIHandler", null);
81
-
82
60
  _defineProperty(this, "proxyHandler", null);
83
-
84
61
  _defineProperty(this, "_handler", void 0);
85
-
86
62
  _defineProperty(this, "staticGenerate", void 0);
87
-
88
63
  require('ignore-styles');
89
-
90
64
  this.pwd = pwd;
91
65
  this.distDir = path.join(pwd, ((_config$output = config.output) === null || _config$output === void 0 ? void 0 : _config$output.path) || 'dist');
92
66
  this.workDir = this.distDir;
@@ -98,43 +72,45 @@ export class ModernServer {
98
72
  this.presetRoutes = routes;
99
73
  this.proxyTarget = proxyTarget;
100
74
  this.staticGenerate = staticGenerate || false;
101
- this.runMode = runMode || RUN_MODE.TYPE;
102
- process.env.BUILD_TYPE = `${this.staticGenerate ? 'ssg' : 'ssr'}`;
103
- } // server prepare
104
-
75
+ this.runMode = runMode || RUN_MODE.FULL;
76
+ // process.env.BUILD_TYPE = `${this.staticGenerate ? 'ssg' : 'ssr'}`;
77
+ }
105
78
 
79
+ // server prepare
106
80
  async onInit(runner, app) {
107
81
  var _conf$bff;
108
-
109
82
  this.runner = runner;
110
83
  const {
111
84
  distDir,
112
85
  staticGenerate,
113
86
  conf
114
87
  } = this;
115
- debug('final server conf', this.conf); // proxy handler, each proxy has own handler
116
-
88
+ debug('final server conf', this.conf);
89
+ // proxy handler, each proxy has own handler
117
90
  this.proxyHandler = createProxyHandler((_conf$bff = conf.bff) === null || _conf$bff === void 0 ? void 0 : _conf$bff.proxy);
118
-
119
91
  if (this.proxyHandler) {
120
92
  this.proxyHandler.forEach(handler => {
121
93
  this.addHandler(handler);
122
94
  });
123
- } // start file reader
124
-
95
+ }
125
96
 
97
+ // start file reader
126
98
  this.reader.init();
127
99
  app.on('close', () => {
128
100
  this.reader.close();
129
- }); // use preset routes priority
101
+ });
130
102
 
103
+ // use preset routes priority
131
104
  const usageRoutes = this.filterRoutes(this.getRoutes());
132
- this.router.reset(usageRoutes); // warmup ssr bundle in production env
105
+ this.router.reset(usageRoutes);
133
106
 
107
+ // warmup ssr bundle in production env
134
108
  this.warmupSSRBundle();
135
- await this.prepareFrameHandler(); // Only work when without setting `assetPrefix`.
136
- // Setting `assetPrefix` means these resources should be uploaded to CDN.
109
+ await this.prepareFrameHandler();
110
+ await this.prepareBeforeRouteHandler(usageRoutes, distDir);
137
111
 
112
+ // Only work when without setting `assetPrefix`.
113
+ // Setting `assetPrefix` means these resources should be uploaded to CDN.
138
114
  const staticPathRegExp = getStaticReg(this.conf.output || {});
139
115
  this.staticFileHandler = createStaticFileHandler([{
140
116
  path: staticPathRegExp,
@@ -146,15 +122,32 @@ export class ModernServer {
146
122
  });
147
123
  await this.setupBeforeProdMiddleware();
148
124
  this.addHandler(this.staticFileHandler);
149
- this.addHandler(this.routeHandler.bind(this)); // compose middlewares to http handler
150
-
151
- this.compose();
152
- } // server ready
153
125
 
126
+ // execute after staticFileHandler, can rename to staticFallbackHandler if needed.
127
+ this.addHandler(faviconFallbackHandler);
128
+ this.addBeforeRouteHandler();
129
+ this.addHandler(this.routeHandler.bind(this));
154
130
 
155
- onRepack(_) {// empty
131
+ // compose middlewares to http handler
132
+ this.compose();
156
133
  }
157
134
 
135
+ // server ready
136
+ onRepack(_) {
137
+ // empty
138
+ }
139
+ addBeforeRouteHandler() {
140
+ this.addHandler(async (context, next) => {
141
+ if (this.beforeRouteHandler) {
142
+ await this.beforeRouteHandler(context);
143
+ if (this.isSend(context.res)) {
144
+ return;
145
+ }
146
+ }
147
+ // eslint-disable-next-line consistent-return
148
+ return next();
149
+ });
150
+ }
158
151
  onServerChange({
159
152
  filepath
160
153
  }) {
@@ -173,54 +166,77 @@ export class ModernServer {
173
166
  onlyWeb,
174
167
  onlyApi
175
168
  });
176
- } // exposed requestHandler
177
-
169
+ }
178
170
 
171
+ // exposed requestHandler
179
172
  getRequestHandler() {
180
173
  return this.requestHandler.bind(this);
181
174
  }
182
-
175
+ async render(req, res, url) {
176
+ req.logger = this.logger;
177
+ req.metrics = this.metrics;
178
+ const context = createContext(req, res);
179
+ const matched = this.router.match(url || context.path);
180
+ if (!matched) {
181
+ return null;
182
+ }
183
+ const route = matched.generate(context.url);
184
+ const result = await this.handleWeb(context, route);
185
+ if (!result) {
186
+ return null;
187
+ }
188
+ return result.content.toString();
189
+ }
183
190
  async createHTTPServer(handler) {
184
191
  return createServer(handler);
185
192
  }
193
+
186
194
  /* —————————————————————— function will be overwrite —————————————————————— */
187
195
  // get routes info
188
-
189
-
190
196
  getRoutes() {
191
197
  // Preferred to use preset routes
192
198
  if (this.presetRoutes) {
193
199
  return this.presetRoutes;
194
- } // read routes from spec file
195
-
200
+ }
196
201
 
202
+ // read routes from spec file
197
203
  const file = path.join(this.distDir, ROUTE_SPEC_FILE);
198
-
199
204
  if (fs.existsSync(file)) {
200
205
  const content = fs.readJSONSync(file);
201
206
  return content.routes;
202
207
  }
203
-
204
208
  return [];
205
- } // add promisify request handler to server
206
- // handler should do not do more things after invoke next
207
-
209
+ }
208
210
 
211
+ // add promisify request handler to server
212
+ // handler should do not do more things after invoke next
209
213
  addHandler(handler) {
210
214
  if (handler[Symbol.toStringTag] === 'AsyncFunction') {
211
215
  this.handlers.push(handler);
212
216
  } else {
213
217
  this.handlers.push(util.promisify(handler));
214
218
  }
215
- } // return 404 page
216
-
219
+ }
217
220
 
221
+ // return 404 page
218
222
  render404(context) {
219
223
  context.error(ERROR_DIGEST.ENOTF, '404 Not Found');
220
224
  this.renderErrorPage(context, 404);
221
- } // gather frame extension and get framework handler
222
-
225
+ }
226
+ async prepareBeforeRouteHandler(specs, distDir) {
227
+ const {
228
+ runner
229
+ } = this;
230
+ const handler = await runner.preparebeforeRouteHandler({
231
+ serverRoutes: specs,
232
+ distDir
233
+ }, {
234
+ onLast: () => null
235
+ });
236
+ this.beforeRouteHandler = handler;
237
+ }
223
238
 
239
+ // gather frame extension and get framework handler
224
240
  async prepareFrameHandler(options) {
225
241
  const {
226
242
  workDir,
@@ -229,46 +245,45 @@ export class ModernServer {
229
245
  const {
230
246
  onlyApi,
231
247
  onlyWeb
232
- } = options || {}; // server hook, gather plugin inject
248
+ } = options || {};
233
249
 
250
+ // server hook, gather plugin inject
234
251
  const _createMiddlewareColl = createMiddlewareCollecter(),
235
- {
236
- getMiddlewares
237
- } = _createMiddlewareColl,
238
- collector = _objectWithoutProperties(_createMiddlewareColl, _excluded);
239
-
252
+ {
253
+ getMiddlewares
254
+ } = _createMiddlewareColl,
255
+ collector = _objectWithoutProperties(_createMiddlewareColl, _excluded);
240
256
  await runner.gather(collector);
241
257
  const {
242
258
  api: pluginAPIExt,
243
259
  web: pluginWebExt
244
260
  } = getMiddlewares();
245
261
  const apiDir = path.join(workDir, API_DIR);
246
- const serverDir = path.join(workDir, SERVER_DIR); // get api or web server handler from server-framework plugin
262
+ const serverDir = path.join(workDir, SERVER_DIR);
247
263
 
264
+ // get api or web server handler from server-framework plugin
248
265
  if ((await fs.pathExists(path.join(serverDir))) && !onlyApi) {
249
266
  const webExtension = mergeExtension(pluginWebExt);
250
267
  this.frameWebHandler = await this.prepareWebHandler(webExtension);
251
268
  }
252
-
253
269
  if (fs.existsSync(apiDir) && !onlyWeb) {
254
270
  const apiExtension = mergeExtension(pluginAPIExt);
255
271
  this.frameAPIHandler = await this.prepareAPIHandler(apiExtension);
256
272
  }
257
273
  }
258
-
259
274
  async prepareWebHandler(extension) {
260
275
  const {
261
276
  workDir,
262
277
  runner
263
278
  } = this;
264
- return runner.prepareWebServer({
279
+ const handler = await runner.prepareWebServer({
265
280
  pwd: workDir,
266
281
  config: extension
267
282
  }, {
268
283
  onLast: () => null
269
284
  });
285
+ return handler;
270
286
  }
271
-
272
287
  async prepareAPIHandler(extension) {
273
288
  const {
274
289
  workDir,
@@ -287,18 +302,9 @@ export class ModernServer {
287
302
  onLast: () => null
288
303
  });
289
304
  }
290
-
291
305
  filterRoutes(routes) {
292
306
  return routes;
293
307
  }
294
-
295
- async emitRouteHook(eventName, input) {
296
- input.context = clone(input.context);
297
- return this.runner[eventName](input, {
298
- onLast: noop
299
- });
300
- }
301
-
302
308
  async setupBeforeProdMiddleware() {
303
309
  const {
304
310
  conf,
@@ -309,20 +315,16 @@ export class ModernServer {
309
315
  this.addHandler(mid);
310
316
  });
311
317
  }
312
-
313
318
  async handleAPI(context) {
314
319
  const {
315
320
  req,
316
321
  res
317
322
  } = context;
318
-
319
323
  if (!this.frameAPIHandler) {
320
324
  throw new Error('can not found api handler');
321
325
  }
322
-
323
326
  await this.frameAPIHandler(req, res);
324
327
  }
325
-
326
328
  async handleWeb(context, route) {
327
329
  return this.routeRenderHandler({
328
330
  ctx: context,
@@ -330,272 +332,198 @@ export class ModernServer {
330
332
  runner: this.runner
331
333
  });
332
334
  }
333
-
334
335
  async proxy() {
335
336
  return null;
336
- } // warmup ssr function
337
-
337
+ }
338
338
 
339
+ // warmup ssr function
339
340
  warmupSSRBundle() {
340
341
  const {
341
342
  distDir
342
343
  } = this;
343
344
  const bundles = this.router.getBundles();
344
345
  bundles.forEach(bundle => {
345
- const filepath = path.join(distDir, bundle); // if error, just throw and let process die
346
-
346
+ const filepath = path.join(distDir, bundle);
347
+ // if error, just throw and let process die
347
348
  require(filepath);
348
349
  });
349
350
  }
350
-
351
351
  createContext(req, res, options = {}) {
352
352
  return createContext(req, res, options);
353
353
  }
354
+
354
355
  /* —————————————————————— private function —————————————————————— */
355
356
  // handler route.json, include api / csr / ssr
356
-
357
-
358
357
  async routeHandler(context) {
359
358
  const {
360
- req,
361
359
  res
362
360
  } = context;
363
- await this.emitRouteHook('beforeMatch', {
364
- context
365
- }); // match routes in the route spec
366
361
 
362
+ // match routes in the route spec
367
363
  const matched = this.router.match(context.path);
368
-
369
364
  if (!matched) {
370
365
  this.render404(context);
371
366
  return;
372
367
  }
373
368
 
374
- if (res.headersSent) {
369
+ // route is api service
370
+ let route = matched.generate(context.url);
371
+ if (route.isApi) {
372
+ await this.handleAPI(context);
375
373
  return;
376
374
  }
375
+ const afterMatchContext = createAfterMatchContext(context, route.entryName);
377
376
 
378
- const routeAPI = createRouteAPI(matched, this.router, context.url);
379
- await this.emitRouteHook('afterMatch', {
380
- context,
381
- routeAPI
382
- });
383
-
377
+ // only full mode run server hook
378
+ if (this.runMode === RUN_MODE.FULL) {
379
+ await this.runner.afterMatch(afterMatchContext, {
380
+ onLast: noop
381
+ });
382
+ }
384
383
  if (this.isSend(res)) {
385
384
  return;
386
385
  }
387
-
388
386
  const {
389
- current
390
- } = routeAPI;
391
- const route = current.generate(context.url);
387
+ current,
388
+ url,
389
+ status
390
+ } = afterMatchContext.router;
391
+ // redirect to another url
392
+ if (url) {
393
+ this.redirect(res, url, status);
394
+ return;
395
+ }
396
+
397
+ // rewrite to another entry
398
+ if (route.entryName !== current) {
399
+ const matched = this.router.matchEntry(current);
400
+ if (!matched) {
401
+ this.render404(context);
402
+ return;
403
+ }
404
+ route = matched.generate(context.url);
405
+ }
392
406
  context.setParams(route.params);
393
407
  context.setServerData('router', {
394
408
  baseUrl: route.urlPath,
395
409
  params: route.params
396
- }); // route is api service
397
-
398
- if (route.isApi) {
399
- await this.handleAPI(context);
400
- return;
401
- }
402
-
410
+ });
403
411
  if (this.frameWebHandler) {
404
- await this.frameWebHandler(req, res);
405
- } // frameWebHandler has process request
406
-
412
+ res.locals = res.locals || {};
413
+ const middlewareContext = createMiddlewareContext(context);
414
+ await this.frameWebHandler(middlewareContext);
415
+ res.locals = _objectSpread(_objectSpread({}, res.locals), middlewareContext.response.locals);
416
+ }
407
417
 
418
+ // frameWebHandler has process request
408
419
  if (this.isSend(res)) {
409
420
  return;
410
421
  }
411
-
412
422
  if (route.responseHeaders) {
413
423
  Object.keys(route.responseHeaders).forEach(key => {
414
424
  const value = route.responseHeaders[key];
415
-
416
425
  if (value) {
417
426
  context.res.setHeader(key, value);
418
427
  }
419
428
  });
420
429
  }
421
-
422
- if (route.entryName) {
423
- await this.emitRouteHook('beforeRender', {
424
- context
425
- });
426
- }
427
-
428
- const file = await this.handleWeb(context, route);
429
-
430
- if (!file) {
430
+ const renderResult = await this.handleWeb(context, route);
431
+ if (!renderResult) {
431
432
  this.render404(context);
432
433
  return;
433
434
  }
434
435
 
435
- if (file.redirect) {
436
- res.statusCode = file.statusCode;
437
- res.setHeader('Location', file.content);
438
- res.end();
436
+ // React Router navigation
437
+ if (renderResult.redirect) {
438
+ this.redirect(res, renderResult.content, renderResult.statusCode);
439
439
  return;
440
440
  }
441
-
442
441
  if (this.isSend(res)) {
443
442
  return;
444
443
  }
445
-
446
- let response = file.content;
447
-
444
+ res.setHeader('content-type', renderResult.contentType);
445
+ const {
446
+ contentStream
447
+ } = renderResult;
448
+ if (contentStream) {
449
+ contentStream.pipe(templateInjectableStream({
450
+ prependHead: route.entryName ? `<script>window._SERVER_DATA=${JSON.stringify(context.serverData)}</script>` : undefined
451
+ })).pipe(res);
452
+ return;
453
+ }
454
+ let response = renderResult.content;
448
455
  if (route.entryName) {
449
- const templateAPI = createTemplateAPI(file.content.toString());
450
- await this.emitRouteHook('afterRender', {
451
- context,
452
- templateAPI
453
- });
454
-
456
+ const afterRenderContext = createAfterRenderContext(context, response.toString());
457
+
458
+ // only full mode run server hook
459
+ // FIXME: how to run server hook in streaming
460
+ if (this.runMode === RUN_MODE.FULL) {
461
+ await this.runner.afterRender(afterRenderContext, {
462
+ onLast: noop
463
+ });
464
+ }
455
465
  if (this.isSend(res)) {
456
466
  return;
457
467
  }
458
468
 
459
- await this.injectMicroFE(context, templateAPI); // It will inject _SERVER_DATA twice, when SSG mode.
469
+ // It will inject _SERVER_DATA twice, when SSG mode.
460
470
  // The first time was in ssg html created, the seoncd time was in prod-server start.
461
471
  // but the second wound causes route error.
462
472
  // To ensure that the second injection fails, the _SERVER_DATA inject at the front of head,
463
-
464
- templateAPI.prependHead(`<script>window._SERVER_DATA=${JSON.stringify(context.serverData)}</script>`);
465
- response = templateAPI.get();
473
+ afterRenderContext.template.prependHead(`<script>window._SERVER_DATA=${JSON.stringify(context.serverData)}</script>`);
474
+ response = afterRenderContext.template.get();
466
475
  }
467
-
468
- res.setHeader('content-type', file.contentType);
469
476
  res.end(response);
470
477
  }
471
-
472
478
  isSend(res) {
473
479
  if (res.headersSent) {
474
480
  return true;
475
481
  }
476
-
477
482
  if (res.getHeader('Location') && isRedirect(res.statusCode)) {
478
483
  res.end();
479
484
  return true;
480
485
  }
481
-
482
486
  return false;
483
487
  }
484
488
 
485
- async injectMicroFE(context, templateAPI) {
486
- var _conf$runtime, _conf$server;
487
-
488
- const {
489
- conf
490
- } = this;
491
- const masterApp = (_conf$runtime = conf.runtime) === null || _conf$runtime === void 0 ? void 0 : _conf$runtime.masterApp; // no inject if not master App
492
-
493
- if (!masterApp) {
494
- return;
495
- }
496
-
497
- const manifest = masterApp.manifest || {};
498
- let modules = [];
499
- const {
500
- modules: configModules = []
501
- } = manifest; // while config modules is an string, fetch data from remote
502
-
503
- if (typeof configModules === 'string') {
504
- const moduleRequestUrl = configModules;
505
-
506
- try {
507
- const {
508
- data: remoteModules
509
- } = await axios.get(moduleRequestUrl);
510
-
511
- if (Array.isArray(remoteModules)) {
512
- modules.push(...remoteModules);
513
- }
514
- } catch (e) {
515
- context.error(ERROR_DIGEST.EMICROINJ, e);
516
- }
517
- } else if (Array.isArray(configModules)) {
518
- modules.push(...configModules);
519
- }
520
-
521
- const {
522
- headers
523
- } = context.req;
524
- const debugName = headers['x-micro-frontend-module-name'] || context.query['__debug__micro-frontend-module-name'];
525
- const debugEntry = headers['x-micro-frontend-module-entry'] || context.query['__debug__micro-frontend-module-entry']; // add debug micro App to first
526
-
527
- if (debugName && debugEntry && (_conf$server = conf.server) !== null && _conf$server !== void 0 && _conf$server.enableMicroFrontendDebug) {
528
- modules = modules.map(m => {
529
- if (m.name === debugName) {
530
- return {
531
- name: debugName,
532
- entry: debugEntry
533
- };
534
- }
535
-
536
- return m;
537
- });
538
- }
539
-
540
- try {
541
- // Todo Safety xss
542
- const injection = JSON.stringify(_objectSpread(_objectSpread({}, manifest), {}, {
543
- modules
544
- }));
545
- templateAPI.appendHead(`<script>window.modern_manifest=${injection}</script>`);
546
- } catch (e) {
547
- context.error(ERROR_DIGEST.EMICROINJ, e);
548
- }
549
- } // compose handlers and create the final handler
550
-
551
-
489
+ // compose handlers and create the final handler
552
490
  compose() {
553
491
  const {
554
492
  handlers
555
493
  } = this;
556
-
557
494
  if (!Array.isArray(handlers)) {
558
495
  throw new TypeError('Middleware stack must be an array!');
559
496
  }
560
-
561
497
  for (const fn of handlers) {
562
498
  if (typeof fn !== 'function') {
563
499
  throw new TypeError('Middleware must be composed of functions!');
564
500
  }
565
501
  }
566
-
567
502
  this._handler = (context, next) => {
568
503
  let i = 0;
569
-
570
504
  const dispatch = error => {
571
505
  if (error) {
572
506
  return this.onError(context, error);
573
507
  }
574
-
575
508
  const handler = handlers[i++];
576
-
577
509
  if (!handler) {
578
510
  return next();
579
511
  }
580
-
581
512
  return handler(context, dispatch).catch(onError);
582
513
  };
583
-
584
514
  const onError = err => {
585
515
  this.onError(context, err);
586
516
  };
587
-
588
517
  return dispatch();
589
518
  };
590
519
  }
591
-
592
- requestHandler(req, res, next = () => {// empty
520
+ requestHandler(req, res, next = () => {
521
+ // empty
593
522
  }) {
594
523
  res.statusCode = 200;
595
524
  req.logger = this.logger;
596
525
  req.metrics = this.metrics;
597
526
  let context;
598
-
599
527
  try {
600
528
  context = this.createContext(req, res);
601
529
  } catch (e) {
@@ -604,19 +532,21 @@ export class ModernServer {
604
532
  res.setHeader('content-type', mime.contentType('html'));
605
533
  return res.end(createErrorDocument(500, ERROR_PAGE_TEXT[500]));
606
534
  }
607
-
608
535
  try {
609
536
  return this._handler(context, next);
610
537
  } catch (err) {
611
538
  return this.onError(context, err);
612
539
  }
613
540
  }
614
-
541
+ redirect(res, url, status = 302) {
542
+ res.setHeader('Location', url);
543
+ res.statusCode = status;
544
+ res.end();
545
+ }
615
546
  onError(context, err) {
616
547
  context.error(ERROR_DIGEST.EINTER, err);
617
548
  this.renderErrorPage(context, 500);
618
549
  }
619
-
620
550
  async renderErrorPage(context, status) {
621
551
  const {
622
552
  res
@@ -625,14 +555,15 @@ export class ModernServer {
625
555
  res.setHeader('content-type', mime.contentType('html'));
626
556
  const statusPage = `/${status}`;
627
557
  const customErrorPage = `/_error`;
628
- const matched = this.router.match(statusPage) || this.router.match(customErrorPage); // if no custom status page find
558
+ const matched = this.router.match(statusPage) || this.router.match(customErrorPage);
559
+ // if no custom status page find
629
560
 
630
561
  if (matched) {
631
562
  const route = matched.generate(context.url);
632
563
  const {
633
564
  entryName
634
- } = route; // check entryName, avoid matched '/' route
635
-
565
+ } = route;
566
+ // check entryName, avoid matched '/' route
636
567
  if (entryName === status.toString() || entryName === '_error') {
637
568
  try {
638
569
  const file = await this.routeRenderHandler({
@@ -640,19 +571,17 @@ export class ModernServer {
640
571
  ctx: context,
641
572
  runner: this.runner
642
573
  });
643
-
644
574
  if (file) {
645
575
  context.res.end(file.content);
646
576
  return;
647
577
  }
648
- } catch (e) {// just catch error when the rendering error occurred in the custom error page.
578
+ } catch (e) {
579
+ // just catch error when the rendering error occurred in the custom error page.
649
580
  }
650
581
  }
651
582
  }
652
-
653
583
  const text = ERROR_PAGE_TEXT[status] || ERROR_PAGE_TEXT[500];
654
584
  context.res.end(createErrorDocument(status, text));
655
585
  }
656
-
657
586
  }
658
587
  /* eslint-enable max-lines */