@modern-js/prod-server 1.0.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 (173) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/LICENSE +21 -0
  3. package/README.md +30 -0
  4. package/dist/js/modern/constants.js +26 -0
  5. package/dist/js/modern/index.js +14 -0
  6. package/dist/js/modern/libs/context/context.js +180 -0
  7. package/dist/js/modern/libs/context/index.js +3 -0
  8. package/dist/js/modern/libs/hook-api/route.js +39 -0
  9. package/dist/js/modern/libs/hook-api/template.js +61 -0
  10. package/dist/js/modern/libs/metrics.js +12 -0
  11. package/dist/js/modern/libs/proxy.js +33 -0
  12. package/dist/js/modern/libs/render/cache/__tests__/cache.fun.test.js +70 -0
  13. package/dist/js/modern/libs/render/cache/__tests__/cache.test.js +233 -0
  14. package/dist/js/modern/libs/render/cache/__tests__/cacheable.js +53 -0
  15. package/dist/js/modern/libs/render/cache/__tests__/error-configuration.js +35 -0
  16. package/dist/js/modern/libs/render/cache/__tests__/matched-cache.js +121 -0
  17. package/dist/js/modern/libs/render/cache/index.js +74 -0
  18. package/dist/js/modern/libs/render/cache/page-caches/index.js +9 -0
  19. package/dist/js/modern/libs/render/cache/page-caches/lru.js +35 -0
  20. package/dist/js/modern/libs/render/cache/spr.js +280 -0
  21. package/dist/js/modern/libs/render/cache/type.js +1 -0
  22. package/dist/js/modern/libs/render/cache/util.js +79 -0
  23. package/dist/js/modern/libs/render/index.js +65 -0
  24. package/dist/js/modern/libs/render/modern/browser-list.js +7 -0
  25. package/dist/js/modern/libs/render/modern/index.js +42 -0
  26. package/dist/js/modern/libs/render/reader.js +112 -0
  27. package/dist/js/modern/libs/render/ssr.js +58 -0
  28. package/dist/js/modern/libs/render/static.js +46 -0
  29. package/dist/js/modern/libs/render/type.js +7 -0
  30. package/dist/js/modern/libs/route/index.js +68 -0
  31. package/dist/js/modern/libs/route/matcher.js +94 -0
  32. package/dist/js/modern/libs/route/route.js +24 -0
  33. package/dist/js/modern/libs/serve-file.js +28 -0
  34. package/dist/js/modern/server/index.js +120 -0
  35. package/dist/js/modern/server/modern-server-split.js +81 -0
  36. package/dist/js/modern/server/modern-server.js +576 -0
  37. package/dist/js/modern/type.js +1 -0
  38. package/dist/js/modern/utils.js +112 -0
  39. package/dist/js/node/constants.js +36 -0
  40. package/dist/js/node/index.js +74 -0
  41. package/dist/js/node/libs/context/context.js +194 -0
  42. package/dist/js/node/libs/context/index.js +18 -0
  43. package/dist/js/node/libs/hook-api/route.js +48 -0
  44. package/dist/js/node/libs/hook-api/template.js +69 -0
  45. package/dist/js/node/libs/metrics.js +18 -0
  46. package/dist/js/node/libs/proxy.js +44 -0
  47. package/dist/js/node/libs/render/cache/__tests__/cache.fun.test.js +77 -0
  48. package/dist/js/node/libs/render/cache/__tests__/cache.test.js +238 -0
  49. package/dist/js/node/libs/render/cache/__tests__/cacheable.js +60 -0
  50. package/dist/js/node/libs/render/cache/__tests__/error-configuration.js +42 -0
  51. package/dist/js/node/libs/render/cache/__tests__/matched-cache.js +128 -0
  52. package/dist/js/node/libs/render/cache/index.js +86 -0
  53. package/dist/js/node/libs/render/cache/page-caches/index.js +17 -0
  54. package/dist/js/node/libs/render/cache/page-caches/lru.js +47 -0
  55. package/dist/js/node/libs/render/cache/spr.js +298 -0
  56. package/dist/js/node/libs/render/cache/type.js +5 -0
  57. package/dist/js/node/libs/render/cache/util.js +105 -0
  58. package/dist/js/node/libs/render/index.js +91 -0
  59. package/dist/js/node/libs/render/modern/browser-list.js +14 -0
  60. package/dist/js/node/libs/render/modern/index.js +58 -0
  61. package/dist/js/node/libs/render/reader.js +139 -0
  62. package/dist/js/node/libs/render/ssr.js +76 -0
  63. package/dist/js/node/libs/render/static.js +62 -0
  64. package/dist/js/node/libs/render/type.js +14 -0
  65. package/dist/js/node/libs/route/index.js +83 -0
  66. package/dist/js/node/libs/route/matcher.js +108 -0
  67. package/dist/js/node/libs/route/route.js +33 -0
  68. package/dist/js/node/libs/serve-file.js +41 -0
  69. package/dist/js/node/server/index.js +142 -0
  70. package/dist/js/node/server/modern-server-split.js +97 -0
  71. package/dist/js/node/server/modern-server.js +614 -0
  72. package/dist/js/node/type.js +5 -0
  73. package/dist/js/node/utils.js +143 -0
  74. package/dist/js/styles/tsconfig.json +12 -0
  75. package/dist/types/constants.d.ts +20 -0
  76. package/dist/types/index.d.ts +11 -0
  77. package/dist/types/libs/context/context.d.ts +61 -0
  78. package/dist/types/libs/context/index.d.ts +4 -0
  79. package/dist/types/libs/hook-api/route.d.ts +14 -0
  80. package/dist/types/libs/hook-api/template.d.ts +14 -0
  81. package/dist/types/libs/metrics.d.ts +3 -0
  82. package/dist/types/libs/proxy.d.ts +4 -0
  83. package/dist/types/libs/render/cache/__tests__/cache.fun.test.d.ts +1 -0
  84. package/dist/types/libs/render/cache/__tests__/cache.test.d.ts +1 -0
  85. package/dist/types/libs/render/cache/__tests__/cacheable.d.ts +62 -0
  86. package/dist/types/libs/render/cache/__tests__/error-configuration.d.ts +28 -0
  87. package/dist/types/libs/render/cache/__tests__/matched-cache.d.ts +124 -0
  88. package/dist/types/libs/render/cache/index.d.ts +6 -0
  89. package/dist/types/libs/render/cache/page-caches/index.d.ts +2 -0
  90. package/dist/types/libs/render/cache/page-caches/lru.d.ts +15 -0
  91. package/dist/types/libs/render/cache/spr.d.ts +24 -0
  92. package/dist/types/libs/render/cache/type.d.ts +48 -0
  93. package/dist/types/libs/render/cache/util.d.ts +17 -0
  94. package/dist/types/libs/render/index.d.ts +18 -0
  95. package/dist/types/libs/render/modern/browser-list.d.ts +1 -0
  96. package/dist/types/libs/render/modern/index.d.ts +3 -0
  97. package/dist/types/libs/render/reader.d.ts +18 -0
  98. package/dist/types/libs/render/ssr.d.ts +10 -0
  99. package/dist/types/libs/render/static.d.ts +3 -0
  100. package/dist/types/libs/render/type.d.ts +33 -0
  101. package/dist/types/libs/route/index.d.ts +15 -0
  102. package/dist/types/libs/route/matcher.d.ts +15 -0
  103. package/dist/types/libs/route/route.d.ts +14 -0
  104. package/dist/types/libs/serve-file.d.ts +8 -0
  105. package/dist/types/server/index.d.ts +20 -0
  106. package/dist/types/server/modern-server-split.d.ts +26 -0
  107. package/dist/types/server/modern-server.d.ts +72 -0
  108. package/dist/types/type.d.ts +56 -0
  109. package/dist/types/utils.d.ts +19 -0
  110. package/jest.config.js +9 -0
  111. package/modern.config.js +2 -0
  112. package/package.json +82 -0
  113. package/src/constants.ts +26 -0
  114. package/src/index.ts +18 -0
  115. package/src/libs/context/context.ts +183 -0
  116. package/src/libs/context/index.ts +7 -0
  117. package/src/libs/hook-api/route.ts +42 -0
  118. package/src/libs/hook-api/template.ts +53 -0
  119. package/src/libs/metrics.ts +15 -0
  120. package/src/libs/proxy.ts +42 -0
  121. package/src/libs/render/cache/__tests__/cache.fun.test.ts +94 -0
  122. package/src/libs/render/cache/__tests__/cache.test.ts +240 -0
  123. package/src/libs/render/cache/__tests__/cacheable.ts +44 -0
  124. package/src/libs/render/cache/__tests__/error-configuration.ts +34 -0
  125. package/src/libs/render/cache/__tests__/matched-cache.ts +88 -0
  126. package/src/libs/render/cache/index.ts +75 -0
  127. package/src/libs/render/cache/page-caches/index.ts +11 -0
  128. package/src/libs/render/cache/page-caches/lru.ts +38 -0
  129. package/src/libs/render/cache/spr.ts +301 -0
  130. package/src/libs/render/cache/type.ts +59 -0
  131. package/src/libs/render/cache/util.ts +97 -0
  132. package/src/libs/render/index.ts +79 -0
  133. package/src/libs/render/modern/browser-list.ts +7 -0
  134. package/src/libs/render/modern/index.ts +41 -0
  135. package/src/libs/render/modern/module.d.ts +4 -0
  136. package/src/libs/render/reader.ts +119 -0
  137. package/src/libs/render/ssr.ts +67 -0
  138. package/src/libs/render/static.ts +52 -0
  139. package/src/libs/render/type.ts +38 -0
  140. package/src/libs/route/index.ts +76 -0
  141. package/src/libs/route/matcher.ts +108 -0
  142. package/src/libs/route/route.ts +34 -0
  143. package/src/libs/serve-file.ts +34 -0
  144. package/src/server/index.ts +147 -0
  145. package/src/server/modern-server-split.ts +97 -0
  146. package/src/server/modern-server.ts +613 -0
  147. package/src/tsconfig.json +12 -0
  148. package/src/type.ts +61 -0
  149. package/src/utils.ts +122 -0
  150. package/tests/.eslintrc.js +6 -0
  151. package/tests/context.test.ts +52 -0
  152. package/tests/fixtures/hosting-files/static/index.js +1 -0
  153. package/tests/fixtures/pure/modern.config.js +5 -0
  154. package/tests/fixtures/pure/package.json +21 -0
  155. package/tests/fixtures/pure/src/App.css +119 -0
  156. package/tests/fixtures/pure/src/App.tsx +43 -0
  157. package/tests/fixtures/pure/tsconfig.json +12 -0
  158. package/tests/fixtures/reader/index.ts +3 -0
  159. package/tests/fixtures/route-spec/dynamic.json +13 -0
  160. package/tests/fixtures/route-spec/index.json +29 -0
  161. package/tests/fixtures/ssr/bundle.js +5 -0
  162. package/tests/fixtures/static-dir/bar.html +11 -0
  163. package/tests/fixtures/static-dir/baz/index.html +11 -0
  164. package/tests/fixtures/static-dir/foo/index.html +11 -0
  165. package/tests/helper.ts +8 -0
  166. package/tests/hook.test.ts +44 -0
  167. package/tests/middleware.test.ts +179 -0
  168. package/tests/render.test.ts +102 -0
  169. package/tests/route.test.ts +77 -0
  170. package/tests/server.test.ts +101 -0
  171. package/tests/tsconfig.json +12 -0
  172. package/tests/utils.test.ts +106 -0
  173. package/tsconfig.json +11 -0
@@ -0,0 +1,576 @@
1
+ const _excluded = ["getMiddlewares"];
2
+
3
+ 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
+ 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
+ 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; }
8
+
9
+ 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; }
10
+
11
+ 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; }
12
+
13
+ /* eslint-disable max-lines */
14
+ import { createServer } from 'http';
15
+ import util from 'util';
16
+ import path from 'path';
17
+ import { fs, ROUTE_SPEC_FILE } from '@modern-js/utils';
18
+ import mime from 'mime-types';
19
+ import axios from 'axios';
20
+ import clone from 'lodash.clone';
21
+ import { RouteMatchManager } from "../libs/route";
22
+ import { createRenderHandler } from "../libs/render";
23
+ import { createStaticFileHandler } from "../libs/serve-file";
24
+ import { createErrorDocument, createMiddlewareCollecter, getStaticReg, mergeExtension, noop } from "../utils";
25
+ import * as reader from "../libs/render/reader";
26
+ import { createProxyHandler } from "../libs/proxy";
27
+ import { createContext } from "../libs/context";
28
+ import { AGGRED_DIR, ApiServerMode, ERROR_DIGEST, ERROR_PAGE_TEXT } from "../constants";
29
+ import { createTemplateAPI } from "../libs/hook-api/template";
30
+ import { createRouteAPI } from "../libs/hook-api/route";
31
+ const API_DIR = './api';
32
+ const SERVER_DIR = './server';
33
+ export class ModernServer {
34
+ // appDirectory
35
+ // product dist dir
36
+ // work on src or dist
37
+ constructor({
38
+ pwd,
39
+ config,
40
+ routes,
41
+ staticGenerate,
42
+ logger,
43
+ metrics,
44
+ proxyTarget
45
+ }) {
46
+ var _config$output;
47
+
48
+ this.pwd = void 0;
49
+ this.distDir = void 0;
50
+ this.workDir = void 0;
51
+ this.router = void 0;
52
+ this.conf = void 0;
53
+ this.handlers = [];
54
+ this.presetRoutes = void 0;
55
+ this.runner = void 0;
56
+ this.logger = void 0;
57
+ this.metrics = void 0;
58
+ this.reader = reader;
59
+ this.proxyTarget = void 0;
60
+ this.staticFileHandler = void 0;
61
+ this.routeRenderHandler = void 0;
62
+ this.frameWebHandler = null;
63
+ this.frameAPIHandler = null;
64
+ this.proxyHandler = null;
65
+ this._handler = void 0;
66
+ this.staticGenerate = false;
67
+
68
+ require('ignore-styles');
69
+
70
+ this.pwd = pwd;
71
+ this.distDir = path.join(pwd, ((_config$output = config.output) === null || _config$output === void 0 ? void 0 : _config$output.path) || 'dist');
72
+ this.workDir = this.distDir;
73
+ this.conf = config;
74
+ this.logger = logger;
75
+ this.metrics = metrics;
76
+ this.router = new RouteMatchManager();
77
+ this.presetRoutes = routes;
78
+ this.proxyTarget = proxyTarget;
79
+
80
+ if (staticGenerate) {
81
+ this.staticGenerate = staticGenerate;
82
+ }
83
+
84
+ process.env.BUILD_TYPE = `${this.staticGenerate ? 'ssg' : 'ssr'}`;
85
+ } // exposed requestHandler
86
+
87
+
88
+ getRequestHandler() {
89
+ return this.requestHandler.bind(this);
90
+ } // server prepare
91
+
92
+
93
+ async init(runner) {
94
+ var _conf$bff;
95
+
96
+ this.runner = runner;
97
+ const {
98
+ distDir,
99
+ staticGenerate,
100
+ conf
101
+ } = this;
102
+ this.addHandler((ctx, next) => {
103
+ ctx.res.setHeader('Access-Control-Allow-Origin', '*');
104
+ ctx.res.setHeader('Access-Control-Allow-Credentials', 'false');
105
+ next();
106
+ }); // proxy handler, each proxy has own handler
107
+
108
+ this.proxyHandler = createProxyHandler((_conf$bff = conf.bff) === null || _conf$bff === void 0 ? void 0 : _conf$bff.proxy);
109
+
110
+ if (this.proxyHandler) {
111
+ this.proxyHandler.forEach(handler => {
112
+ this.addHandler(handler);
113
+ });
114
+ } // start reader, include an time interval
115
+
116
+
117
+ this.reader.init(); // use preset routes priority
118
+
119
+ this.router.reset(this.filterRoutes(this.presetRoutes || this.readRouteSpec()));
120
+ this.warmupSSRBundle();
121
+ await this.prepareFrameHandler(); // Only work when without setting `assetPrefix`.
122
+ // Setting `assetPrefix` means these resources should be uploaded to CDN.
123
+
124
+ const staticPathRegExp = getStaticReg(this.conf.output || {});
125
+ this.staticFileHandler = createStaticFileHandler([{
126
+ path: staticPathRegExp,
127
+ target: distDir
128
+ }]);
129
+ this.routeRenderHandler = createRenderHandler({
130
+ distDir,
131
+ staticGenerate
132
+ });
133
+ await this.preServerInit();
134
+ this.addHandler(this.staticFileHandler);
135
+ this.addHandler(this.routeHandler.bind(this));
136
+ this.compose();
137
+ } // server ready
138
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
139
+
140
+
141
+ ready(_) {} // invoke when http server listen
142
+
143
+
144
+ onListening(_) {// empty
145
+ } // close any thing run in server
146
+
147
+
148
+ async close() {
149
+ this.reader.close();
150
+ }
151
+
152
+ async createHTTPServer(handler) {
153
+ return createServer(handler);
154
+ } // read route spec from route.json
155
+
156
+
157
+ readRouteSpec() {
158
+ const file = path.join(this.distDir, ROUTE_SPEC_FILE);
159
+
160
+ if (fs.existsSync(file)) {
161
+ const content = fs.readJSONSync(file);
162
+ return content.routes;
163
+ }
164
+
165
+ return [];
166
+ } // add promisify request handler to server
167
+ // handler should do not do more things after invoke next
168
+
169
+
170
+ addHandler(handler) {
171
+ if (handler[Symbol.toStringTag] === 'AsyncFunction') {
172
+ this.handlers.push(handler);
173
+ } else {
174
+ this.handlers.push(util.promisify(handler));
175
+ }
176
+ } // return 404 page
177
+
178
+
179
+ render404(context) {
180
+ context.error(ERROR_DIGEST.ENOTF);
181
+ this.renderErrorPage(context, 404);
182
+ } // gather frame extension and get framework handler
183
+
184
+
185
+ async prepareFrameHandler() {
186
+ const {
187
+ workDir,
188
+ runner
189
+ } = this; // server hook, gather plugin inject
190
+
191
+ const _createMiddlewareColl = createMiddlewareCollecter(),
192
+ {
193
+ getMiddlewares
194
+ } = _createMiddlewareColl,
195
+ collector = _objectWithoutProperties(_createMiddlewareColl, _excluded);
196
+
197
+ await runner.gather(collector);
198
+ const {
199
+ api: pluginAPIExt,
200
+ web: pluginWebExt
201
+ } = getMiddlewares();
202
+ const apiDir = path.join(workDir, API_DIR);
203
+ const serverDir = path.join(workDir, SERVER_DIR); // get api or web server handler from server-framework plugin
204
+
205
+ if (await fs.pathExists(path.join(serverDir))) {
206
+ const webExtension = mergeExtension(pluginWebExt);
207
+ this.frameWebHandler = await this.prepareWebHandler(webExtension);
208
+ }
209
+
210
+ if (fs.existsSync(apiDir)) {
211
+ const mode = fs.existsSync(path.join(apiDir, AGGRED_DIR.lambda)) ? ApiServerMode.frame : ApiServerMode.func; // if use lambda/, mean framework style of writing, then discard user extension
212
+
213
+ const apiExtension = mergeExtension(pluginAPIExt);
214
+ this.frameAPIHandler = await this.prepareAPIHandler(mode, apiExtension);
215
+ }
216
+ } // Todo
217
+
218
+
219
+ async proxy() {
220
+ return null;
221
+ }
222
+ /* —————————————————————— function will be overwrite —————————————————————— */
223
+
224
+
225
+ async prepareWebHandler(extension) {
226
+ const {
227
+ workDir,
228
+ runner
229
+ } = this;
230
+ return runner.prepareWebServer({
231
+ pwd: workDir,
232
+ config: extension
233
+ }, {
234
+ onLast: () => null
235
+ });
236
+ }
237
+
238
+ async prepareAPIHandler(mode, extension) {
239
+ const {
240
+ workDir,
241
+ runner,
242
+ conf
243
+ } = this;
244
+ const {
245
+ bff
246
+ } = conf;
247
+ const prefix = (bff === null || bff === void 0 ? void 0 : bff.prefix) || '/api';
248
+ return runner.prepareApiServer({
249
+ pwd: workDir,
250
+ mode,
251
+ config: extension,
252
+ prefix: Array.isArray(prefix) ? prefix[0] : prefix
253
+ }, {
254
+ onLast: () => null
255
+ });
256
+ }
257
+
258
+ filterRoutes(routes) {
259
+ return routes;
260
+ }
261
+
262
+ async emitRouteHook(eventName, input) {
263
+ input.context = clone(input.context);
264
+ return this.runner[eventName](input, {
265
+ onLast: noop
266
+ });
267
+ } // warmup ssr function
268
+
269
+
270
+ warmupSSRBundle() {
271
+ const {
272
+ distDir
273
+ } = this;
274
+ const bundles = this.router.getBundles();
275
+ bundles.forEach(bundle => {
276
+ const filepath = path.join(distDir, bundle); // if error, just throw and let process die
277
+
278
+ require(filepath);
279
+ });
280
+ }
281
+
282
+ async preServerInit() {
283
+ const {
284
+ conf,
285
+ runner
286
+ } = this;
287
+ const preMiddleware = await runner.preServerInit(conf);
288
+ preMiddleware.flat().forEach(mid => {
289
+ this.addHandler(mid);
290
+ });
291
+ }
292
+
293
+ async handleAPI(context) {
294
+ const {
295
+ req,
296
+ res
297
+ } = context;
298
+
299
+ if (!this.frameAPIHandler) {
300
+ throw new Error('can not found api hanlder');
301
+ }
302
+
303
+ await this.frameAPIHandler(req, res);
304
+ }
305
+
306
+ async handleWeb(context, route) {
307
+ return this.routeRenderHandler({
308
+ ctx: context,
309
+ route,
310
+ runner: this.runner
311
+ });
312
+ }
313
+
314
+ verifyMatch(_c, _m) {// empty
315
+ }
316
+ /* —————————————————————— private function —————————————————————— */
317
+ // handler route.json, include api / csr / ssr
318
+ // eslint-disable-next-line max-statements
319
+
320
+
321
+ async routeHandler(context) {
322
+ const {
323
+ req,
324
+ res
325
+ } = context;
326
+ await this.emitRouteHook('beforeMatch', {
327
+ context
328
+ }); // match routes in the route spec
329
+
330
+ const matched = this.router.match(context.path);
331
+
332
+ if (!matched) {
333
+ this.render404(context);
334
+ return;
335
+ } else {
336
+ this.verifyMatch(context, matched);
337
+ }
338
+
339
+ if (res.headersSent) {
340
+ return;
341
+ }
342
+
343
+ const routeAPI = createRouteAPI(matched, this.router, context.url);
344
+ await this.emitRouteHook('afterMatch', {
345
+ context,
346
+ routeAPI
347
+ });
348
+
349
+ if (res.headersSent) {
350
+ return;
351
+ }
352
+
353
+ const {
354
+ current
355
+ } = routeAPI;
356
+ const route = current.generate(context.url);
357
+ context.setParams(route.params);
358
+ context.setServerData('router', {
359
+ baseUrl: route.urlPath,
360
+ params: route.params
361
+ }); // route is api service
362
+
363
+ if (route.isApi) {
364
+ await this.handleAPI(context);
365
+ return;
366
+ }
367
+
368
+ if (this.frameWebHandler) {
369
+ await this.frameWebHandler(req, res);
370
+ } // frameWebHandler has process request
371
+
372
+
373
+ if (res.headersSent) {
374
+ return;
375
+ }
376
+
377
+ if (route.entryName) {
378
+ await this.emitRouteHook('beforeRender', {
379
+ context
380
+ });
381
+ }
382
+
383
+ const file = await this.handleWeb(context, route);
384
+
385
+ if (!file) {
386
+ this.render404(context);
387
+ return;
388
+ }
389
+
390
+ if (file.redirect) {
391
+ res.statusCode = file.statusCode;
392
+ res.setHeader('Location', file.content);
393
+ res.end();
394
+ return;
395
+ }
396
+
397
+ let response = file.content;
398
+
399
+ if (route.entryName) {
400
+ const templateAPI = createTemplateAPI(file.content.toString());
401
+ await this.emitRouteHook('afterRender', {
402
+ context,
403
+ templateAPI
404
+ });
405
+ await this.injectMicroFE(context, templateAPI);
406
+ templateAPI.appendHead(`<script>window._SERVER_DATA=${JSON.stringify(context.serverData)}</script>`);
407
+ response = templateAPI.get();
408
+ }
409
+
410
+ res.setHeader('content-type', file.contentType);
411
+ res.end(response);
412
+ } // eslint-disable-next-line max-statements
413
+
414
+
415
+ async injectMicroFE(context, templateAPI) {
416
+ var _conf$runtime, _conf$server;
417
+
418
+ const {
419
+ conf
420
+ } = this;
421
+ const masterApp = (_conf$runtime = conf.runtime) === null || _conf$runtime === void 0 ? void 0 : _conf$runtime.masterApp; // no inject if not master App
422
+
423
+ if (!masterApp) {
424
+ return;
425
+ }
426
+
427
+ const manifest = masterApp.manifest || {};
428
+ let modules = [];
429
+ const {
430
+ modules: configModules = []
431
+ } = manifest; // while config modules is an string, fetch data from remote
432
+
433
+ if (typeof configModules === 'string') {
434
+ const moduleRequestUrl = configModules;
435
+
436
+ try {
437
+ const {
438
+ data: remoteModules
439
+ } = await axios.get(moduleRequestUrl);
440
+
441
+ if (Array.isArray(remoteModules)) {
442
+ modules.push(...remoteModules);
443
+ }
444
+ } catch (e) {
445
+ context.error(ERROR_DIGEST.EMICROINJ, e);
446
+ }
447
+ } else if (Array.isArray(configModules)) {
448
+ modules.push(...configModules);
449
+ }
450
+
451
+ const {
452
+ headers
453
+ } = context.req;
454
+ const debugName = headers['x-micro-frontend-module-name'] || context.query['__debug__micro-frontend-module-name'];
455
+ const debugEntry = headers['x-micro-frontend-module-entry'] || context.query['__debug__micro-frontend-module-entry']; // add debug micro App to first
456
+
457
+ if (debugName && debugEntry && (_conf$server = conf.server) !== null && _conf$server !== void 0 && _conf$server.enableMicroFrontendDebug) {
458
+ modules = modules.map(m => {
459
+ if (m.name === debugName) {
460
+ return {
461
+ name: debugName,
462
+ entry: debugEntry
463
+ };
464
+ }
465
+
466
+ return m;
467
+ });
468
+ }
469
+
470
+ try {
471
+ // Todo Safety xss
472
+ const injection = JSON.stringify(_objectSpread(_objectSpread({}, manifest), {}, {
473
+ modules
474
+ }));
475
+ templateAPI.appendHead(`<script>window.modern_manifest=${injection}</script>`);
476
+ } catch (e) {
477
+ context.error(ERROR_DIGEST.EMICROINJ, e);
478
+ }
479
+ } // compose handlers and create the final handler
480
+
481
+
482
+ compose() {
483
+ const {
484
+ handlers
485
+ } = this;
486
+
487
+ if (!Array.isArray(handlers)) {
488
+ throw new TypeError('Middleware stack must be an array!');
489
+ }
490
+
491
+ for (const fn of handlers) {
492
+ if (typeof fn !== 'function') {
493
+ throw new TypeError('Middleware must be composed of functions!');
494
+ }
495
+ }
496
+
497
+ this._handler = (context, next) => {
498
+ let i = 0;
499
+
500
+ const dispatch = () => {
501
+ const handler = handlers[i++];
502
+
503
+ if (!handler) {
504
+ return next();
505
+ } // eslint-disable-next-line promise/prefer-await-to-then
506
+
507
+
508
+ return handler(context, dispatch).catch(onError);
509
+ };
510
+
511
+ const onError = err => {
512
+ this.onError(context, err);
513
+ };
514
+
515
+ return dispatch();
516
+ };
517
+ }
518
+
519
+ requestHandler(req, res, next = () => {// empty
520
+ }) {
521
+ res.statusCode = 200;
522
+ req.logger = req.logger || this.logger;
523
+ req.metrics = req.metrics || this.metrics;
524
+ const context = createContext(req, res);
525
+
526
+ try {
527
+ this._handler(context, next);
528
+ } catch (err) {
529
+ this.onError(context, err);
530
+ }
531
+ }
532
+
533
+ onError(context, err) {
534
+ context.error(ERROR_DIGEST.EINTER, err);
535
+ this.renderErrorPage(context, 500);
536
+ }
537
+
538
+ async renderErrorPage(context, status) {
539
+ const {
540
+ res
541
+ } = context;
542
+ context.status = status;
543
+ res.setHeader('content-type', mime.contentType('html'));
544
+ const statusPage = `/${status}`;
545
+ const customErrorPage = `/_error`;
546
+ const matched = this.router.match(statusPage) || this.router.match(customErrorPage); // if no custom status page find
547
+
548
+ if (matched) {
549
+ const route = matched.generate(context.url);
550
+ const {
551
+ entryName
552
+ } = route; // check entryName, aviod matched '/' route
553
+
554
+ if (entryName === status.toString() || entryName === '_error') {
555
+ try {
556
+ const file = await this.routeRenderHandler({
557
+ route,
558
+ ctx: context,
559
+ runner: this.runner
560
+ });
561
+
562
+ if (file) {
563
+ context.res.end(file.content);
564
+ return;
565
+ }
566
+ } catch (e) {// just catch error when the rendering error occurred in the custom error page.
567
+ }
568
+ }
569
+ }
570
+
571
+ const text = ERROR_PAGE_TEXT[status] || ERROR_PAGE_TEXT[500];
572
+ res.end(createErrorDocument(status, text));
573
+ }
574
+
575
+ }
576
+ /* eslint-enable max-lines */
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,112 @@
1
+ import { compile } from 'path-to-regexp';
2
+ export const mergeExtension = users => {
3
+ const output = [];
4
+ return {
5
+ middleware: output.concat(users)
6
+ };
7
+ };
8
+ export const toMessage = (dig, e) => {
9
+ const message = e instanceof Error ? e.message : e;
10
+
11
+ if (message) {
12
+ return `${dig}: ${message}`;
13
+ } else {
14
+ return dig;
15
+ }
16
+ };
17
+ export const noop = () => {// noop
18
+ };
19
+ export const createErrorDocument = (status, text) => {
20
+ const title = `${status}: ${text}`;
21
+ return `<!DOCTYPE html>
22
+ <html lang="en">
23
+ <head>
24
+ <meta charset="utf-8">
25
+ <meta name="viewport" content="width=device-width">
26
+ <title>${title}</title>
27
+ <style>
28
+ html,body {
29
+ margin: 0;
30
+ }
31
+
32
+ .page-container {
33
+ color: #000;
34
+ background: #fff;
35
+ height: 100vh;
36
+ text-align: center;
37
+ display: flex;
38
+ flex-direction: column;
39
+ align-items: center;
40
+ justify-content: center;
41
+ }
42
+ </style>
43
+ </head>
44
+ <body>
45
+ <div class="page-container">
46
+ <h1>${status}</h1>
47
+ <div>${text}</div>
48
+ </body>
49
+ </html>
50
+ `;
51
+ };
52
+ export const createMiddlewareCollecter = () => {
53
+ const webMiddlewares = [];
54
+ const apiMiddlewares = [];
55
+
56
+ const addWebMiddleware = input => {
57
+ webMiddlewares.push(input);
58
+ };
59
+
60
+ const addAPIMiddleware = input => {
61
+ apiMiddlewares.push(input);
62
+ };
63
+
64
+ const getMiddlewares = () => ({
65
+ web: webMiddlewares,
66
+ api: apiMiddlewares
67
+ });
68
+
69
+ return {
70
+ getMiddlewares,
71
+ addWebMiddleware,
72
+ addAPIMiddleware
73
+ };
74
+ };
75
+ export const toPath = (reg, params) => {
76
+ const fn = compile(reg, {
77
+ encode: encodeURIComponent
78
+ });
79
+ return fn(params);
80
+ };
81
+ export const getStaticReg = (output = {}) => {
82
+ const {
83
+ favicon,
84
+ faviconByEntries,
85
+ cssPath,
86
+ jsPath,
87
+ mediaPath
88
+ } = output;
89
+ const favicons = prepareFavicons(favicon, faviconByEntries);
90
+ const staticFiles = [cssPath, jsPath, mediaPath].filter(v => Boolean(v));
91
+ const staticPathRegExp = new RegExp(`^/(static/|upload/|favicon.ico|icon.png${favicons.length > 0 ? `|${favicons.join('|')}` : ''}|${staticFiles.join('|')})`);
92
+ return staticPathRegExp;
93
+ };
94
+ export const prepareFavicons = (favicon, faviconByEntries) => {
95
+ const faviconNames = [];
96
+
97
+ if (favicon) {
98
+ faviconNames.push(favicon.substring(favicon.lastIndexOf('/') + 1));
99
+ }
100
+
101
+ if (faviconByEntries) {
102
+ Object.keys(faviconByEntries).forEach(f => {
103
+ const curFavicon = faviconByEntries[f];
104
+
105
+ if (curFavicon) {
106
+ faviconNames.push(curFavicon.substring(curFavicon.lastIndexOf('/') + 1));
107
+ }
108
+ });
109
+ }
110
+
111
+ return faviconNames;
112
+ };