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