@modern-js/server 1.3.1-beta.0 → 1.4.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.
- package/CHANGELOG.md +68 -0
- package/dist/js/modern/dev-tools/babel/register.js +1 -0
- package/dist/js/modern/dev-tools/dev-server-plugin.js +1 -2
- package/dist/js/modern/dev-tools/mock/getMockData.js +24 -1
- package/dist/js/modern/dev-tools/mock/index.js +1 -26
- package/dist/js/modern/dev-tools/socket-server.js +4 -2
- package/dist/js/modern/dev-tools/watcher/index.js +4 -7
- package/dist/js/modern/dev-tools/watcher/stats-cache.js +32 -20
- package/dist/js/modern/libs/context/context.js +6 -0
- package/dist/js/modern/libs/hook-api/route.js +6 -4
- package/dist/js/modern/libs/render/index.js +1 -0
- package/dist/js/modern/libs/render/reader.js +1 -2
- package/dist/js/modern/libs/render/ssr.js +7 -2
- package/dist/js/modern/libs/route/index.js +0 -1
- package/dist/js/modern/libs/route/matcher.js +15 -3
- package/dist/js/modern/libs/route/route.js +1 -0
- package/dist/js/modern/server/dev-server/dev-server.js +3 -0
- package/dist/js/modern/server/index.js +5 -4
- package/dist/js/modern/server/modern-server-split.js +1 -1
- package/dist/js/modern/server/modern-server.js +13 -34
- package/dist/js/modern/utils.js +39 -0
- package/dist/js/node/dev-tools/babel/register.js +1 -0
- package/dist/js/node/dev-tools/dev-server-plugin.js +1 -2
- package/dist/js/node/dev-tools/mock/getMockData.js +29 -2
- package/dist/js/node/dev-tools/mock/index.js +5 -26
- package/dist/js/node/dev-tools/socket-server.js +4 -2
- package/dist/js/node/dev-tools/watcher/index.js +7 -5
- package/dist/js/node/dev-tools/watcher/stats-cache.js +33 -20
- package/dist/js/node/libs/context/context.js +6 -0
- package/dist/js/node/libs/hook-api/route.js +6 -4
- package/dist/js/node/libs/render/index.js +1 -0
- package/dist/js/node/libs/render/reader.js +2 -1
- package/dist/js/node/libs/render/ssr.js +8 -2
- package/dist/js/node/libs/route/index.js +0 -1
- package/dist/js/node/libs/route/matcher.js +16 -3
- package/dist/js/node/libs/route/route.js +1 -0
- package/dist/js/node/server/dev-server/dev-server.js +3 -0
- package/dist/js/node/server/index.js +9 -6
- package/dist/js/node/server/modern-server-split.js +1 -1
- package/dist/js/node/server/modern-server.js +12 -33
- package/dist/js/node/utils.js +51 -2
- package/dist/types/dev-tools/mock/getMockData.d.ts +2 -1
- package/dist/types/dev-tools/socket-server.d.ts +1 -2
- package/dist/types/dev-tools/watcher/index.d.ts +2 -1
- package/dist/types/dev-tools/watcher/stats-cache.d.ts +3 -2
- package/dist/types/libs/context/context.d.ts +2 -0
- package/dist/types/libs/hook-api/route.d.ts +3 -2
- package/dist/types/libs/render/reader.d.ts +13 -0
- package/dist/types/libs/render/ssr.d.ts +1 -0
- package/dist/types/libs/route/matcher.d.ts +1 -1
- package/dist/types/libs/route/route.d.ts +1 -0
- package/dist/types/server/dev-server/dev-server-split.d.ts +3 -3
- package/dist/types/server/modern-server-split.d.ts +3 -3
- package/dist/types/server/modern-server.d.ts +1 -2
- package/dist/types/type.d.ts +6 -4
- package/dist/types/utils.d.ts +5 -1
- package/package.json +13 -12
- package/tests/context.test.ts +12 -1
- package/tests/dev.test.ts +306 -7
- package/tests/fixtures/mock/exist/config/mock/index.ts +11 -0
- package/tests/fixtures/mock/zero/config/mock/index.ts +1 -0
- package/tests/fixtures/pure/tsconfig.json +0 -1
- package/tests/fixtures/reader/index.ts +3 -0
- package/tests/fixtures/route-spec/dynamic.json +13 -0
- package/tests/fixtures/ssr/bundle.js +5 -0
- package/tests/fixtures/static-dir/bar.html +11 -0
- package/tests/fixtures/static-dir/baz/index.html +11 -0
- package/tests/fixtures/static-dir/foo/index.html +11 -0
- package/tests/fixtures/watch/a.ts +3 -0
- package/tests/fixtures/watch/index.ts +5 -0
- package/tests/fixtures/watch/stats.txt +1 -0
- package/tests/hook.test.ts +1 -1
- package/tests/render.test.ts +102 -0
- package/tests/route.test.ts +26 -3
- package/tests/utils.test.ts +35 -0
- package/tests/watcher.test.ts +6 -4
- package/src/constants.ts +0 -26
- package/src/dev-tools/babel/register.ts +0 -37
- package/src/dev-tools/dev-server-plugin.ts +0 -48
- package/src/dev-tools/https/global.d.ts +0 -3
- package/src/dev-tools/https/index.ts +0 -12
- package/src/dev-tools/launch-editor/index.ts +0 -29
- package/src/dev-tools/mock/getMockData.ts +0 -109
- package/src/dev-tools/mock/index.ts +0 -63
- package/src/dev-tools/socket-server.ts +0 -192
- package/src/dev-tools/watcher/dependency-tree.ts +0 -94
- package/src/dev-tools/watcher/index.ts +0 -81
- package/src/dev-tools/watcher/stats-cache.ts +0 -53
- package/src/index.ts +0 -16
- package/src/libs/context/context.ts +0 -176
- package/src/libs/context/index.ts +0 -7
- package/src/libs/hook-api/route.ts +0 -38
- package/src/libs/hook-api/template.ts +0 -53
- package/src/libs/metrics.ts +0 -15
- package/src/libs/proxy.ts +0 -85
- package/src/libs/render/cache/__tests__/cache.fun.test.ts +0 -94
- package/src/libs/render/cache/__tests__/cache.test.ts +0 -240
- package/src/libs/render/cache/__tests__/cacheable.ts +0 -44
- package/src/libs/render/cache/__tests__/error-configuration.ts +0 -34
- package/src/libs/render/cache/__tests__/matched-cache.ts +0 -88
- package/src/libs/render/cache/index.ts +0 -75
- package/src/libs/render/cache/page-caches/index.ts +0 -11
- package/src/libs/render/cache/page-caches/lru.ts +0 -38
- package/src/libs/render/cache/spr.ts +0 -301
- package/src/libs/render/cache/type.ts +0 -59
- package/src/libs/render/cache/util.ts +0 -97
- package/src/libs/render/index.ts +0 -78
- package/src/libs/render/modern/browser-list.ts +0 -7
- package/src/libs/render/modern/index.ts +0 -41
- package/src/libs/render/modern/module.d.ts +0 -4
- package/src/libs/render/reader.ts +0 -119
- package/src/libs/render/ssr.ts +0 -62
- package/src/libs/render/static.ts +0 -52
- package/src/libs/render/type.ts +0 -38
- package/src/libs/route/index.ts +0 -77
- package/src/libs/route/matcher.ts +0 -93
- package/src/libs/route/route.ts +0 -32
- package/src/libs/serve-file.ts +0 -34
- package/src/server/dev-server/dev-server-split.ts +0 -41
- package/src/server/dev-server/dev-server.ts +0 -300
- package/src/server/dev-server/index.ts +0 -2
- package/src/server/index.ts +0 -163
- package/src/server/modern-server-split.ts +0 -97
- package/src/server/modern-server.ts +0 -636
- package/src/type.ts +0 -88
- package/src/utils.ts +0 -79
|
@@ -1,636 +0,0 @@
|
|
|
1
|
-
/* eslint-disable max-lines */
|
|
2
|
-
import { IncomingMessage, ServerResponse, Server, createServer } from 'http';
|
|
3
|
-
import util from 'util';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import { fs, ROUTE_SPEC_FILE } from '@modern-js/utils';
|
|
6
|
-
import { Adapter, APIServerStartInput } from '@modern-js/server-plugin';
|
|
7
|
-
import type { NormalizedConfig } from '@modern-js/core';
|
|
8
|
-
import mime from 'mime-types';
|
|
9
|
-
import axios from 'axios';
|
|
10
|
-
import clone from 'lodash.clone';
|
|
11
|
-
import {
|
|
12
|
-
ModernServerOptions,
|
|
13
|
-
NextFunction,
|
|
14
|
-
ServerHookRunner,
|
|
15
|
-
Metrics,
|
|
16
|
-
Logger,
|
|
17
|
-
ReadyOptions,
|
|
18
|
-
ConfWithBFF,
|
|
19
|
-
} from '../type';
|
|
20
|
-
import {
|
|
21
|
-
RouteMatchManager,
|
|
22
|
-
ModernRouteInterface,
|
|
23
|
-
ModernRoute,
|
|
24
|
-
RouteMatcher,
|
|
25
|
-
} from '../libs/route';
|
|
26
|
-
import { createRenderHandler } from '../libs/render';
|
|
27
|
-
import { createStaticFileHandler } from '../libs/serve-file';
|
|
28
|
-
import {
|
|
29
|
-
createErrorDocument,
|
|
30
|
-
createMiddlewareCollecter,
|
|
31
|
-
mergeExtension,
|
|
32
|
-
noop,
|
|
33
|
-
} from '../utils';
|
|
34
|
-
import * as reader from '../libs/render/reader';
|
|
35
|
-
import { createProxyHandler, ProxyOptions } from '../libs/proxy';
|
|
36
|
-
import { createContext, ModernServerContext } from '../libs/context';
|
|
37
|
-
import {
|
|
38
|
-
AGGRED_DIR,
|
|
39
|
-
ApiServerMode,
|
|
40
|
-
ERROR_DIGEST,
|
|
41
|
-
ERROR_PAGE_TEXT,
|
|
42
|
-
} from '../constants';
|
|
43
|
-
import { createTemplateAPI } from '../libs/hook-api/template';
|
|
44
|
-
import { createRouteAPI } from '../libs/hook-api/route';
|
|
45
|
-
|
|
46
|
-
type ModernServerHandler = (
|
|
47
|
-
context: ModernServerContext,
|
|
48
|
-
next: NextFunction,
|
|
49
|
-
) => Promise<void> | void;
|
|
50
|
-
|
|
51
|
-
type ModernServerAsyncHandler = (
|
|
52
|
-
context: ModernServerContext,
|
|
53
|
-
next: NextFunction,
|
|
54
|
-
) => Promise<void>;
|
|
55
|
-
|
|
56
|
-
const API_DIR = './api';
|
|
57
|
-
const SERVER_DIR = './server';
|
|
58
|
-
|
|
59
|
-
export class ModernServer {
|
|
60
|
-
// appDirectory
|
|
61
|
-
protected pwd: string;
|
|
62
|
-
|
|
63
|
-
// product dist dir
|
|
64
|
-
protected distDir: string;
|
|
65
|
-
|
|
66
|
-
// work on src or dist
|
|
67
|
-
protected workDir: string;
|
|
68
|
-
|
|
69
|
-
protected router!: RouteMatchManager;
|
|
70
|
-
|
|
71
|
-
protected conf: NormalizedConfig;
|
|
72
|
-
|
|
73
|
-
protected handlers: ModernServerAsyncHandler[] = [];
|
|
74
|
-
|
|
75
|
-
protected presetRoutes?: ModernRouteInterface[];
|
|
76
|
-
|
|
77
|
-
protected runner!: ServerHookRunner;
|
|
78
|
-
|
|
79
|
-
protected readonly logger: Logger;
|
|
80
|
-
|
|
81
|
-
protected readonly metrics: Metrics;
|
|
82
|
-
|
|
83
|
-
protected readonly proxyTarget: ModernServerOptions['proxyTarget'];
|
|
84
|
-
|
|
85
|
-
private readonly isDev: boolean = false;
|
|
86
|
-
|
|
87
|
-
private staticFileHandler!: ReturnType<typeof createStaticFileHandler>;
|
|
88
|
-
|
|
89
|
-
private routeRenderHandler!: ReturnType<typeof createRenderHandler>;
|
|
90
|
-
|
|
91
|
-
private frameWebHandler: Adapter | null = null;
|
|
92
|
-
|
|
93
|
-
private frameAPIHandler: Adapter | null = null;
|
|
94
|
-
|
|
95
|
-
private proxyHandler: ReturnType<typeof createProxyHandler> = null;
|
|
96
|
-
|
|
97
|
-
private _handler!: (context: ModernServerContext, next: NextFunction) => void;
|
|
98
|
-
|
|
99
|
-
private readonly staticGenerate: boolean = false;
|
|
100
|
-
|
|
101
|
-
constructor({
|
|
102
|
-
pwd,
|
|
103
|
-
config,
|
|
104
|
-
dev,
|
|
105
|
-
routes,
|
|
106
|
-
staticGenerate,
|
|
107
|
-
logger,
|
|
108
|
-
metrics,
|
|
109
|
-
proxyTarget,
|
|
110
|
-
}: ModernServerOptions) {
|
|
111
|
-
require('ignore-styles');
|
|
112
|
-
this.isDev = Boolean(dev);
|
|
113
|
-
|
|
114
|
-
this.pwd = pwd;
|
|
115
|
-
this.distDir = path.join(pwd, config.output?.path || 'dist');
|
|
116
|
-
this.workDir = this.isDev ? pwd : this.distDir;
|
|
117
|
-
this.conf = config;
|
|
118
|
-
this.logger = logger!;
|
|
119
|
-
this.metrics = metrics!;
|
|
120
|
-
this.router = new RouteMatchManager();
|
|
121
|
-
this.presetRoutes = routes;
|
|
122
|
-
this.proxyTarget = proxyTarget;
|
|
123
|
-
|
|
124
|
-
if (staticGenerate) {
|
|
125
|
-
this.staticGenerate = staticGenerate;
|
|
126
|
-
}
|
|
127
|
-
process.env.BUILD_TYPE = `${this.staticGenerate ? 'ssg' : 'ssr'}`;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// exposed requestHandler
|
|
131
|
-
public getRequestHandler() {
|
|
132
|
-
return this.requestHandler.bind(this);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// server prepare
|
|
136
|
-
public async init(runner: ServerHookRunner) {
|
|
137
|
-
this.runner = runner;
|
|
138
|
-
|
|
139
|
-
const { distDir, isDev, staticGenerate, conf } = this;
|
|
140
|
-
|
|
141
|
-
this.addHandler((ctx: ModernServerContext, next: NextFunction) => {
|
|
142
|
-
ctx.res.setHeader('Access-Control-Allow-Origin', '*');
|
|
143
|
-
ctx.res.setHeader('Access-Control-Allow-Credentials', 'false');
|
|
144
|
-
next();
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// proxy handler, each proxy has own handler
|
|
148
|
-
this.proxyHandler = createProxyHandler(conf.bff?.proxy as ProxyOptions);
|
|
149
|
-
if (this.proxyHandler) {
|
|
150
|
-
this.proxyHandler.forEach(handler => {
|
|
151
|
-
this.addHandler(handler);
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// start reader, include an time interval
|
|
156
|
-
reader.init();
|
|
157
|
-
|
|
158
|
-
// use preset routes priority
|
|
159
|
-
this.router.reset(
|
|
160
|
-
this.filterRoutes(this.presetRoutes || this.readRouteSpec()),
|
|
161
|
-
);
|
|
162
|
-
|
|
163
|
-
if (!isDev) {
|
|
164
|
-
// The route spec may not be produced at this phase in development
|
|
165
|
-
this.warmupSSRBundle();
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
await this.prepareFrameHandler();
|
|
169
|
-
|
|
170
|
-
const { favicon, faviconByEntries } = this.conf.output || {};
|
|
171
|
-
const favicons = this.prepareFavicons(favicon, faviconByEntries);
|
|
172
|
-
// Only work when without setting `assetPrefix`.
|
|
173
|
-
// Setting `assetPrefix` means these resources should be uploaded to CDN.
|
|
174
|
-
const staticPathRegExp = new RegExp(
|
|
175
|
-
`^/(static/|upload/|favicon.ico|icon.png${
|
|
176
|
-
favicons.length > 0 ? `|${favicons.join('|')}` : ''
|
|
177
|
-
})`,
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
this.staticFileHandler = createStaticFileHandler([
|
|
181
|
-
{
|
|
182
|
-
path: staticPathRegExp,
|
|
183
|
-
target: distDir,
|
|
184
|
-
},
|
|
185
|
-
]);
|
|
186
|
-
|
|
187
|
-
this.routeRenderHandler = createRenderHandler({
|
|
188
|
-
distDir,
|
|
189
|
-
staticGenerate,
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
await this.preServerInit();
|
|
193
|
-
|
|
194
|
-
this.addHandler(this.staticFileHandler);
|
|
195
|
-
this.addHandler(this.routeHandler.bind(this));
|
|
196
|
-
|
|
197
|
-
this.compose();
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// server ready
|
|
201
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
202
|
-
public ready(_: ReadyOptions) {}
|
|
203
|
-
|
|
204
|
-
// invoke when http server listen
|
|
205
|
-
public onListening(_: Server) {
|
|
206
|
-
// empty
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// close any thing run in server
|
|
210
|
-
public close() {
|
|
211
|
-
reader.close();
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
public async createHTTPServer(
|
|
215
|
-
handler: (
|
|
216
|
-
req: IncomingMessage,
|
|
217
|
-
res: ServerResponse,
|
|
218
|
-
next?: () => void,
|
|
219
|
-
) => void,
|
|
220
|
-
) {
|
|
221
|
-
return createServer(handler);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// read route spec from route.json
|
|
225
|
-
protected readRouteSpec() {
|
|
226
|
-
const file = path.join(this.distDir, ROUTE_SPEC_FILE);
|
|
227
|
-
|
|
228
|
-
if (fs.existsSync(file)) {
|
|
229
|
-
const content: { routes: ModernRouteInterface[] } = fs.readJSONSync(file);
|
|
230
|
-
return content.routes;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
return [];
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// add promisify request handler to server
|
|
237
|
-
// handler should do not do more things after invoke next
|
|
238
|
-
protected addHandler(handler: ModernServerHandler) {
|
|
239
|
-
if ((handler as any)[Symbol.toStringTag] === 'AsyncFunction') {
|
|
240
|
-
this.handlers.push(handler as ModernServerAsyncHandler);
|
|
241
|
-
} else {
|
|
242
|
-
this.handlers.push(util.promisify(handler));
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// return 404 page
|
|
247
|
-
protected render404(context: ModernServerContext) {
|
|
248
|
-
context.error(ERROR_DIGEST.ENOTF);
|
|
249
|
-
this.renderErrorPage(context, 404);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// gather frame extension and get framework handler
|
|
253
|
-
protected async prepareFrameHandler() {
|
|
254
|
-
const { workDir, runner } = this;
|
|
255
|
-
|
|
256
|
-
// server hook, gather plugin inject
|
|
257
|
-
const { getMiddlewares, ...collector } = createMiddlewareCollecter();
|
|
258
|
-
|
|
259
|
-
await runner.gather(collector);
|
|
260
|
-
const { api: pluginAPIExt, web: pluginWebExt } = getMiddlewares();
|
|
261
|
-
|
|
262
|
-
const apiDir = path.join(workDir, API_DIR);
|
|
263
|
-
const serverDir = path.join(workDir, SERVER_DIR);
|
|
264
|
-
|
|
265
|
-
// get api or web server handler from server-framework plugin
|
|
266
|
-
if (await fs.pathExists(path.join(serverDir))) {
|
|
267
|
-
const webExtension = mergeExtension(pluginWebExt);
|
|
268
|
-
this.frameWebHandler = await this.prepareWebHandler(webExtension);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
if (fs.existsSync(apiDir)) {
|
|
272
|
-
const mode = fs.existsSync(path.join(apiDir, AGGRED_DIR.lambda))
|
|
273
|
-
? ApiServerMode.frame
|
|
274
|
-
: ApiServerMode.func;
|
|
275
|
-
|
|
276
|
-
// if use lambda/, mean framework style of writing, then discard user extension
|
|
277
|
-
const apiExtension = mergeExtension(pluginAPIExt);
|
|
278
|
-
this.frameAPIHandler = await this.prepareAPIHandler(mode, apiExtension);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// Todo
|
|
283
|
-
protected async proxy() {
|
|
284
|
-
return null as any;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/* —————————————————————— function will be overwrite —————————————————————— */
|
|
288
|
-
protected async prepareWebHandler(
|
|
289
|
-
extension: ReturnType<typeof mergeExtension>,
|
|
290
|
-
) {
|
|
291
|
-
const { workDir, runner } = this;
|
|
292
|
-
|
|
293
|
-
return runner.prepareWebServer(
|
|
294
|
-
{
|
|
295
|
-
pwd: workDir,
|
|
296
|
-
config: extension,
|
|
297
|
-
},
|
|
298
|
-
{ onLast: () => null as any },
|
|
299
|
-
);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
protected async prepareAPIHandler(
|
|
303
|
-
mode: ApiServerMode,
|
|
304
|
-
extension: APIServerStartInput['config'],
|
|
305
|
-
) {
|
|
306
|
-
const { workDir, runner, conf } = this;
|
|
307
|
-
const { bff } = conf as ConfWithBFF;
|
|
308
|
-
const prefix = bff?.prefix || '/api';
|
|
309
|
-
|
|
310
|
-
return runner.prepareApiServer(
|
|
311
|
-
{
|
|
312
|
-
pwd: workDir,
|
|
313
|
-
mode,
|
|
314
|
-
config: extension,
|
|
315
|
-
prefix: Array.isArray(prefix) ? prefix[0] : prefix,
|
|
316
|
-
},
|
|
317
|
-
{ onLast: () => null as any },
|
|
318
|
-
);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
protected filterRoutes(routes: ModernRouteInterface[]) {
|
|
322
|
-
return routes;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
protected async emitRouteHook(
|
|
326
|
-
eventName: 'beforeMatch' | 'afterMatch' | 'beforeRender' | 'afterRender',
|
|
327
|
-
input: {
|
|
328
|
-
context: ModernServerContext;
|
|
329
|
-
[propsName: string]: any;
|
|
330
|
-
},
|
|
331
|
-
) {
|
|
332
|
-
input.context = clone(input.context);
|
|
333
|
-
return this.runner[eventName](input as any, { onLast: noop as any });
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// warmup ssr function
|
|
337
|
-
protected warmupSSRBundle() {
|
|
338
|
-
const { distDir } = this;
|
|
339
|
-
const bundles = this.router.getBundles();
|
|
340
|
-
|
|
341
|
-
bundles.forEach(bundle => {
|
|
342
|
-
const filepath = path.join(distDir, bundle as string);
|
|
343
|
-
// if error, just throw and let process die
|
|
344
|
-
require(filepath);
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
protected async preServerInit() {
|
|
349
|
-
const { conf, runner } = this;
|
|
350
|
-
const preMiddleware: ModernServerAsyncHandler[] =
|
|
351
|
-
await runner.preServerInit(conf);
|
|
352
|
-
|
|
353
|
-
preMiddleware.flat().forEach(mid => {
|
|
354
|
-
this.addHandler(mid);
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
protected async handleAPI(context: ModernServerContext) {
|
|
359
|
-
const { req, res } = context;
|
|
360
|
-
|
|
361
|
-
if (!this.frameAPIHandler) {
|
|
362
|
-
throw new Error('can not found api hanlder');
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
await this.frameAPIHandler(req, res);
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
protected async handleWeb(context: ModernServerContext, route: ModernRoute) {
|
|
369
|
-
return this.routeRenderHandler({
|
|
370
|
-
ctx: context,
|
|
371
|
-
route,
|
|
372
|
-
runner: this.runner,
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
protected verifyMatch(_c: ModernServerContext, _m: RouteMatcher) {
|
|
377
|
-
// empty
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
/* —————————————————————— private function —————————————————————— */
|
|
381
|
-
// handler route.json, include api / csr / ssr
|
|
382
|
-
// eslint-disable-next-line max-statements
|
|
383
|
-
private async routeHandler(context: ModernServerContext) {
|
|
384
|
-
const { req, res } = context;
|
|
385
|
-
|
|
386
|
-
await this.emitRouteHook('beforeMatch', { context });
|
|
387
|
-
|
|
388
|
-
// match routes in the route spec
|
|
389
|
-
const matched = this.router.match(context.path);
|
|
390
|
-
if (!matched) {
|
|
391
|
-
this.render404(context);
|
|
392
|
-
return;
|
|
393
|
-
} else {
|
|
394
|
-
this.verifyMatch(context, matched);
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
if (res.headersSent) {
|
|
398
|
-
return;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
const routeAPI = createRouteAPI(matched, this.router);
|
|
402
|
-
await this.emitRouteHook('afterMatch', { context, routeAPI });
|
|
403
|
-
|
|
404
|
-
if (res.headersSent) {
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
const { current } = routeAPI as any;
|
|
409
|
-
const route: ModernRoute = current.generate();
|
|
410
|
-
const params = current.parseURLParams(context.url);
|
|
411
|
-
context.setParams(params);
|
|
412
|
-
|
|
413
|
-
// route is api service
|
|
414
|
-
if (route.isApi) {
|
|
415
|
-
this.handleAPI(context);
|
|
416
|
-
return;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
if (this.frameWebHandler) {
|
|
420
|
-
await this.frameWebHandler(req, res);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// frameWebHandler has process request
|
|
424
|
-
if (res.headersSent) {
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
if (route.entryName) {
|
|
429
|
-
await this.emitRouteHook('beforeRender', { context });
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
const file = await this.handleWeb(context, route);
|
|
433
|
-
if (!file) {
|
|
434
|
-
this.render404(context);
|
|
435
|
-
return;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
if (file.redirect) {
|
|
439
|
-
res.statusCode = file.statusCode!;
|
|
440
|
-
res.setHeader('Location', file.content as string);
|
|
441
|
-
res.end();
|
|
442
|
-
return;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
let response = file.content;
|
|
446
|
-
if (route.entryName) {
|
|
447
|
-
const templateAPI = createTemplateAPI(file.content.toString());
|
|
448
|
-
await this.emitRouteHook('afterRender', { context, templateAPI });
|
|
449
|
-
await this.injectMicroFE(context, templateAPI);
|
|
450
|
-
response = templateAPI.get();
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
res.setHeader('content-type', file.contentType);
|
|
454
|
-
res.end(response);
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
// eslint-disable-next-line max-statements
|
|
458
|
-
private async injectMicroFE(
|
|
459
|
-
context: ModernServerContext,
|
|
460
|
-
templateAPI: ReturnType<typeof createTemplateAPI>,
|
|
461
|
-
) {
|
|
462
|
-
const { conf } = this;
|
|
463
|
-
const masterApp = conf.runtime?.masterApp;
|
|
464
|
-
// no inject if not master App
|
|
465
|
-
if (!masterApp) {
|
|
466
|
-
return;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
const manifest = masterApp.manifest || {};
|
|
470
|
-
let modules = [];
|
|
471
|
-
const { modules: configModules = [] } = manifest;
|
|
472
|
-
|
|
473
|
-
// while config modules is an string, fetch data from remote
|
|
474
|
-
if (typeof configModules === 'string') {
|
|
475
|
-
const moduleRequestUrl = configModules;
|
|
476
|
-
try {
|
|
477
|
-
const { data: remoteModules } = await axios.get(moduleRequestUrl);
|
|
478
|
-
if (Array.isArray(remoteModules)) {
|
|
479
|
-
modules.push(...remoteModules);
|
|
480
|
-
}
|
|
481
|
-
} catch (e) {
|
|
482
|
-
context.error(ERROR_DIGEST.EMICROINJ, e as Error);
|
|
483
|
-
}
|
|
484
|
-
} else if (Array.isArray(configModules)) {
|
|
485
|
-
modules.push(...configModules);
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
const { headers } = context.req;
|
|
489
|
-
|
|
490
|
-
const debugName =
|
|
491
|
-
headers['x-micro-frontend-module-name'] ||
|
|
492
|
-
context.query['__debug__micro-frontend-module-name'];
|
|
493
|
-
|
|
494
|
-
const debugEntry =
|
|
495
|
-
headers['x-micro-frontend-module-entry'] ||
|
|
496
|
-
context.query['__debug__micro-frontend-module-entry'];
|
|
497
|
-
|
|
498
|
-
// add debug micro App to first
|
|
499
|
-
if (debugName && debugEntry && conf.server?.enableMicroFrontendDebug) {
|
|
500
|
-
modules = modules.map(m => {
|
|
501
|
-
if (m.name === debugName) {
|
|
502
|
-
return {
|
|
503
|
-
name: debugName,
|
|
504
|
-
entry: debugEntry,
|
|
505
|
-
};
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
return m;
|
|
509
|
-
});
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
try {
|
|
513
|
-
// Todo Safety xss
|
|
514
|
-
const injection = JSON.stringify({ ...manifest, modules });
|
|
515
|
-
templateAPI.appendHead(
|
|
516
|
-
`<script>window.modern_manifest=${injection}</script>`,
|
|
517
|
-
);
|
|
518
|
-
} catch (e) {
|
|
519
|
-
context.error(ERROR_DIGEST.EMICROINJ, e as Error);
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
// compose handlers and create the final handler
|
|
524
|
-
private compose() {
|
|
525
|
-
const { handlers } = this;
|
|
526
|
-
|
|
527
|
-
if (!Array.isArray(handlers)) {
|
|
528
|
-
throw new TypeError('Middleware stack must be an array!');
|
|
529
|
-
}
|
|
530
|
-
for (const fn of handlers) {
|
|
531
|
-
if (typeof fn !== 'function') {
|
|
532
|
-
throw new TypeError('Middleware must be composed of functions!');
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
this._handler = (context: ModernServerContext, next: NextFunction) => {
|
|
537
|
-
let i = 0;
|
|
538
|
-
const dispatch = () => {
|
|
539
|
-
const handler = handlers[i++];
|
|
540
|
-
if (!handler) {
|
|
541
|
-
return next();
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
// eslint-disable-next-line promise/prefer-await-to-then
|
|
545
|
-
return handler(context, dispatch as NextFunction).catch(onError);
|
|
546
|
-
};
|
|
547
|
-
|
|
548
|
-
const onError = (err: Error) => {
|
|
549
|
-
this.onError(context, err);
|
|
550
|
-
};
|
|
551
|
-
return dispatch();
|
|
552
|
-
};
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
private requestHandler(
|
|
556
|
-
req: IncomingMessage,
|
|
557
|
-
res: ServerResponse,
|
|
558
|
-
next = () => {
|
|
559
|
-
// empty
|
|
560
|
-
},
|
|
561
|
-
) {
|
|
562
|
-
res.statusCode = 200;
|
|
563
|
-
req.logger = req.logger || this.logger;
|
|
564
|
-
req.metrics = req.metrics || this.metrics;
|
|
565
|
-
const context: ModernServerContext = createContext(req, res);
|
|
566
|
-
|
|
567
|
-
try {
|
|
568
|
-
this._handler(context, next);
|
|
569
|
-
} catch (err) {
|
|
570
|
-
this.onError(context, err as Error);
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
private onError(context: ModernServerContext, err: Error) {
|
|
575
|
-
context.error(ERROR_DIGEST.EINTER, err);
|
|
576
|
-
this.renderErrorPage(context, 500);
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
private async renderErrorPage(context: ModernServerContext, status: number) {
|
|
580
|
-
const { res } = context;
|
|
581
|
-
context.status = status;
|
|
582
|
-
res.setHeader('content-type', mime.contentType('html') as string);
|
|
583
|
-
|
|
584
|
-
const statusPage = `/${status}`;
|
|
585
|
-
const customErrorPage = `/_error`;
|
|
586
|
-
|
|
587
|
-
const matched =
|
|
588
|
-
this.router.match(statusPage) || this.router.match(customErrorPage);
|
|
589
|
-
// if no custom status page find
|
|
590
|
-
if (matched) {
|
|
591
|
-
const route = matched.generate();
|
|
592
|
-
const { entryName } = route;
|
|
593
|
-
// check entryName, aviod matched '/' route
|
|
594
|
-
if (entryName === status.toString() || entryName === '_error') {
|
|
595
|
-
try {
|
|
596
|
-
const file = await this.routeRenderHandler({
|
|
597
|
-
route,
|
|
598
|
-
ctx: context,
|
|
599
|
-
runner: this.runner,
|
|
600
|
-
});
|
|
601
|
-
if (file) {
|
|
602
|
-
context.res.end(file.content);
|
|
603
|
-
return;
|
|
604
|
-
}
|
|
605
|
-
} catch (e) {
|
|
606
|
-
// just catch error when the rendering error occurred in the custom error page.
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
const text = ERROR_PAGE_TEXT[status] || ERROR_PAGE_TEXT[500];
|
|
612
|
-
res.end(createErrorDocument(status, text));
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
private prepareFavicons(
|
|
616
|
-
favicon: string | undefined,
|
|
617
|
-
faviconByEntries?: Record<string, string | undefined>,
|
|
618
|
-
) {
|
|
619
|
-
const faviconNames = [];
|
|
620
|
-
if (favicon) {
|
|
621
|
-
faviconNames.push(favicon.substring(favicon.lastIndexOf('/') + 1));
|
|
622
|
-
}
|
|
623
|
-
if (faviconByEntries) {
|
|
624
|
-
Object.keys(faviconByEntries).forEach(f => {
|
|
625
|
-
const curFavicon = faviconByEntries[f];
|
|
626
|
-
if (curFavicon) {
|
|
627
|
-
faviconNames.push(
|
|
628
|
-
curFavicon.substring(curFavicon.lastIndexOf('/') + 1),
|
|
629
|
-
);
|
|
630
|
-
}
|
|
631
|
-
});
|
|
632
|
-
}
|
|
633
|
-
return faviconNames;
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
/* eslint-enable max-lines */
|
package/src/type.ts
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { Buffer } from 'buffer';
|
|
2
|
-
import type Webpack from 'webpack';
|
|
3
|
-
import { serverManager } from '@modern-js/server-plugin';
|
|
4
|
-
import type { NormalizedConfig } from '@modern-js/core';
|
|
5
|
-
import type { Metrics, Logger, NextFunction } from '@modern-js/types/server';
|
|
6
|
-
import { ModernRouteInterface } from './libs/route';
|
|
7
|
-
|
|
8
|
-
declare module 'http' {
|
|
9
|
-
interface IncomingMessage {
|
|
10
|
-
logger: Logger;
|
|
11
|
-
metrics: Metrics;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
declare module '@modern-js/core' {
|
|
16
|
-
interface UserConfig {
|
|
17
|
-
bff: {
|
|
18
|
-
proxy: Record<string, any>;
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export type DevServerOptions = {
|
|
24
|
-
// hmr client 配置
|
|
25
|
-
client: {
|
|
26
|
-
port: string;
|
|
27
|
-
overlay: boolean;
|
|
28
|
-
logging: string;
|
|
29
|
-
path: string;
|
|
30
|
-
host: string;
|
|
31
|
-
progress?: boolean;
|
|
32
|
-
};
|
|
33
|
-
dev: {
|
|
34
|
-
writeToDisk: boolean | ((filename: string) => boolean);
|
|
35
|
-
};
|
|
36
|
-
// 是否监听文件变化
|
|
37
|
-
watch: boolean;
|
|
38
|
-
// 是否开启 hot reload
|
|
39
|
-
hot: boolean | string;
|
|
40
|
-
// 是否开启 page reload
|
|
41
|
-
liveReload: boolean;
|
|
42
|
-
// 是否开启 https
|
|
43
|
-
https?: boolean | { key: string; cert: string };
|
|
44
|
-
[propName: string]: any;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export type ModernServerOptions = {
|
|
48
|
-
pwd: string;
|
|
49
|
-
config: NormalizedConfig;
|
|
50
|
-
plugins?: any[];
|
|
51
|
-
dev?: boolean | Partial<DevServerOptions>;
|
|
52
|
-
compiler?: Webpack.MultiCompiler | Webpack.Compiler;
|
|
53
|
-
routes?: ModernRouteInterface[];
|
|
54
|
-
staticGenerate?: boolean;
|
|
55
|
-
customServer?: boolean;
|
|
56
|
-
loggerOptions?: Record<string, string>;
|
|
57
|
-
metricsOptions?: Record<string, string>;
|
|
58
|
-
logger?: Logger;
|
|
59
|
-
metrics?: Metrics;
|
|
60
|
-
apiOnly?: boolean;
|
|
61
|
-
ssrOnly?: boolean;
|
|
62
|
-
webOnly?: boolean;
|
|
63
|
-
proxyTarget?: {
|
|
64
|
-
ssr?: string;
|
|
65
|
-
api?: string;
|
|
66
|
-
};
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
export type RenderResult = {
|
|
70
|
-
content: string | Buffer;
|
|
71
|
-
contentType: string;
|
|
72
|
-
statusCode?: number;
|
|
73
|
-
redirect?: boolean;
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
export type ConfWithBFF = {
|
|
77
|
-
bff?: {
|
|
78
|
-
prefix: string;
|
|
79
|
-
};
|
|
80
|
-
} & NormalizedConfig;
|
|
81
|
-
|
|
82
|
-
export type Then<T> = T extends PromiseLike<infer U> ? U : T;
|
|
83
|
-
|
|
84
|
-
export type ServerHookRunner = Then<ReturnType<typeof serverManager.init>>;
|
|
85
|
-
|
|
86
|
-
export type ReadyOptions = { routes?: ModernRouteInterface[] };
|
|
87
|
-
|
|
88
|
-
export type { Metrics, Logger, NextFunction };
|