@modern-js/server 1.1.3 → 1.1.4-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/js/modern/dev-tools/babel/register.js +2 -2
  3. package/dist/js/modern/dev-tools/mock/getMockData.js +2 -2
  4. package/dist/js/modern/libs/proxy.js +2 -2
  5. package/dist/js/modern/libs/render/cache/__tests__/cache.test.js +2 -2
  6. package/dist/js/modern/libs/render/cache/index.js +2 -2
  7. package/dist/js/modern/server/dev-server/dev-server-split.js +2 -6
  8. package/dist/js/modern/server/dev-server/index.js +1 -1
  9. package/dist/js/modern/server/index.js +15 -13
  10. package/dist/js/modern/server/modern-server-split.js +55 -6
  11. package/dist/js/modern/server/modern-server.js +84 -52
  12. package/dist/js/modern/utils.js +9 -1
  13. package/dist/js/node/dev-tools/babel/register.js +2 -2
  14. package/dist/js/node/dev-tools/mock/getMockData.js +2 -2
  15. package/dist/js/node/libs/proxy.js +2 -2
  16. package/dist/js/node/libs/render/cache/__tests__/cache.test.js +2 -2
  17. package/dist/js/node/libs/render/cache/index.js +2 -2
  18. package/dist/js/node/server/dev-server/dev-server-split.js +5 -9
  19. package/dist/js/node/server/dev-server/index.js +4 -4
  20. package/dist/js/node/server/index.js +14 -12
  21. package/dist/js/node/server/modern-server-split.js +61 -9
  22. package/dist/js/node/server/modern-server.js +85 -53
  23. package/dist/js/node/utils.js +12 -2
  24. package/dist/types/server/dev-server/dev-server-split.d.ts +3 -4
  25. package/dist/types/server/dev-server/index.d.ts +1 -1
  26. package/dist/types/server/modern-server-split.d.ts +15 -5
  27. package/dist/types/server/modern-server.d.ts +13 -6
  28. package/dist/types/type.d.ts +5 -0
  29. package/dist/types/utils.d.ts +2 -1
  30. package/package.json +9 -8
  31. package/src/server/dev-server/dev-server-split.ts +3 -7
  32. package/src/server/dev-server/dev-server.ts +6 -10
  33. package/src/server/dev-server/index.ts +1 -1
  34. package/src/server/index.ts +19 -12
  35. package/src/server/modern-server-split.ts +59 -7
  36. package/src/server/modern-server.ts +95 -58
  37. package/src/type.ts +5 -0
  38. package/src/utils.ts +14 -0
@@ -4,7 +4,7 @@ import { mergeExtension } from '@/utils';
4
4
  import { ModernRouteInterface } from '@/libs/route';
5
5
  import { ApiServerMode } from '@/constants';
6
6
 
7
- export class WebModernDevServer extends ModernDevServer {
7
+ export class ModernSSRDevServer extends ModernDevServer {
8
8
  protected prepareAPIHandler(
9
9
  _m: ApiServerMode,
10
10
  _: APIServerStartInput['config'],
@@ -23,14 +23,10 @@ export class WebModernDevServer extends ModernDevServer {
23
23
  }
24
24
  }
25
25
 
26
- export class APIModernDevServer extends ModernDevServer {
27
- protected prepareWebHandler(_: ReturnType<typeof mergeExtension>) {
28
- return null as any;
29
- }
30
-
26
+ export class ModernAPIDevServer extends ModernDevServer {
31
27
  protected async prepareAPIHandler(
32
28
  mode: ApiServerMode,
33
- extension: ReturnType<typeof mergeExtension>,
29
+ extension: APIServerStartInput['config'],
34
30
  ) {
35
31
  return super.prepareAPIHandler(mode, extension);
36
32
  }
@@ -13,9 +13,7 @@ import {
13
13
  SHARED_DIR,
14
14
  } from '@modern-js/utils';
15
15
  import type { MultiCompiler, Compiler } from 'webpack';
16
- import webpackDevMiddleware, {
17
- WebpackDevMiddleware,
18
- } from 'webpack-dev-middleware';
16
+ import webpackDevMiddleware from 'webpack-dev-middleware';
19
17
  import { ModernServer } from '../modern-server';
20
18
  import { createMockHandler } from '@/dev-tools/mock';
21
19
  import { createProxyHandler, ProxyOptions } from '@/libs/proxy';
@@ -61,12 +59,10 @@ export class ModernDevServer extends ModernServer {
61
59
 
62
60
  private watcher!: Watcher;
63
61
 
64
- private devMiddleware!: WebpackDevMiddleware &
65
- ((
66
- req: http.IncomingMessage,
67
- res: http.ServerResponse,
68
- next: NextFunction,
69
- ) => void);
62
+ private devMiddleware!: webpackDevMiddleware.API<
63
+ http.IncomingMessage,
64
+ http.ServerResponse
65
+ >;
70
66
 
71
67
  constructor(options: ModernServerOptions) {
72
68
  super(options);
@@ -239,7 +235,7 @@ export class ModernDevServer extends ModernServer {
239
235
  const bundles = this.router.getBundles();
240
236
 
241
237
  bundles.forEach(bundle => {
242
- const filepath = path.join(distDir, bundle!);
238
+ const filepath = path.join(distDir, bundle as string);
243
239
  if (require.cache[filepath]) {
244
240
  delete require.cache[filepath];
245
241
  }
@@ -1,2 +1,2 @@
1
- export { APIModernDevServer, WebModernDevServer } from './dev-server-split';
1
+ export { ModernAPIDevServer, ModernSSRDevServer } from './dev-server-split';
2
2
  export { ModernDevServer } from './dev-server';
@@ -12,7 +12,11 @@ import {
12
12
  } from '@modern-js/core';
13
13
  import { ModernServer } from './modern-server';
14
14
  import type { ModernDevServer } from './dev-server';
15
- import { APIModernServer, WebModernServer } from './modern-server-split';
15
+ import {
16
+ ModernAPIServer,
17
+ ModernSSRServer,
18
+ ModernWebServer,
19
+ } from './modern-server-split';
16
20
  import { ModernServerOptions, ServerHookRunner, ReadyOptions } from '@/type';
17
21
  import { measure as defaultMeasure } from '@/libs/measure';
18
22
 
@@ -90,9 +94,11 @@ export class Server {
90
94
  const { options } = this;
91
95
 
92
96
  if (options.apiOnly) {
93
- return new APIModernServer(options);
97
+ return new ModernAPIServer(options);
98
+ } else if (options.ssrOnly) {
99
+ return new ModernSSRServer(options);
94
100
  } else if (options.webOnly) {
95
- return new WebModernServer(options);
101
+ return new ModernWebServer(options);
96
102
  } else {
97
103
  return new ModernServer(options);
98
104
  }
@@ -101,15 +107,15 @@ export class Server {
101
107
  private createDevServer() {
102
108
  const { options } = this;
103
109
  const {
104
- APIModernDevServer,
105
- WebModernDevServer,
110
+ ModernAPIDevServer,
111
+ ModernSSRDevServer,
106
112
  ModernDevServer,
107
113
  } = require('./dev-server');
108
114
 
109
115
  if (options.apiOnly) {
110
- return new APIModernDevServer(options);
111
- } else if (options.webOnly) {
112
- return new WebModernDevServer(options);
116
+ return new ModernAPIDevServer(options);
117
+ } else if (options.ssrOnly) {
118
+ return new ModernSSRDevServer(options);
113
119
  } else {
114
120
  return new ModernDevServer(options);
115
121
  }
@@ -117,6 +123,11 @@ export class Server {
117
123
 
118
124
  private async createHookRunner() {
119
125
  const { options } = this;
126
+
127
+ options.plugins?.forEach(p => {
128
+ serverManager.usePlugin(p);
129
+ });
130
+
120
131
  const appContext = await this.initAppContext();
121
132
  serverManager.run(() => {
122
133
  ConfigContext.set(this.options.config as UserConfig);
@@ -129,10 +140,6 @@ export class Server {
129
140
  });
130
141
  });
131
142
 
132
- options.plugins?.forEach(p => {
133
- serverManager.usePlugin(p);
134
- });
135
-
136
143
  return serverManager.init({});
137
144
  }
138
145
 
@@ -1,13 +1,26 @@
1
1
  import { APIServerStartInput } from '@modern-js/server-plugin';
2
2
  import { ModernServer } from './modern-server';
3
3
  import { mergeExtension } from '@/utils';
4
- import { ModernRouteInterface } from '@/libs/route';
4
+ import { ModernRoute, ModernRouteInterface, RouteMatcher } from '@/libs/route';
5
5
  import { ApiServerMode } from '@/constants';
6
+ import { ModernServerContext } from '@/libs/context';
7
+
8
+ export class ModernSSRServer extends ModernServer {
9
+ // Todo should not invoke any route hook in modernSSRServer
10
+
11
+ protected async warmupSSRBundle() {
12
+ // empty
13
+ }
14
+
15
+ protected verifyMatch(context: ModernServerContext, matched: RouteMatcher) {
16
+ if (matched.generate().isApi) {
17
+ this.render404(context);
18
+ }
19
+ }
6
20
 
7
- export class WebModernServer extends ModernServer {
8
21
  protected prepareAPIHandler(
9
22
  _m: ApiServerMode,
10
- _: ReturnType<typeof mergeExtension>,
23
+ _: APIServerStartInput['config'],
11
24
  ) {
12
25
  return null as any;
13
26
  }
@@ -18,12 +31,24 @@ export class WebModernServer extends ModernServer {
18
31
  return super.prepareWebHandler(extension);
19
32
  }
20
33
 
21
- protected filterRoutes(routes: ModernRouteInterface[]) {
22
- return routes.filter(route => route.entryName);
34
+ // protected filterRoutes(routes: ModernRouteInterface[]) {
35
+ // return routes.filter(route => route.entryName);
36
+ // }
37
+
38
+ protected async preServerInit() {
39
+ // empty
23
40
  }
24
41
  }
25
42
 
26
- export class APIModernServer extends ModernServer {
43
+ export class ModernAPIServer extends ModernServer {
44
+ protected async emitRouteHook(_: string, _input: any) {
45
+ // empty
46
+ }
47
+
48
+ protected async warmupSSRBundle() {
49
+ // empty
50
+ }
51
+
27
52
  protected prepareWebHandler(_: ReturnType<typeof mergeExtension>) {
28
53
  return null as any;
29
54
  }
@@ -40,6 +65,33 @@ export class APIModernServer extends ModernServer {
40
65
  }
41
66
 
42
67
  protected async preServerInit() {
43
- // noop
68
+ // empty
69
+ }
70
+ }
71
+
72
+ export class ModernWebServer extends ModernServer {
73
+ protected async warmupSSRBundle() {
74
+ // empty
75
+ }
76
+
77
+ protected async handleAPI(context: ModernServerContext) {
78
+ const { proxyTarget } = this;
79
+ if (!proxyTarget?.api) {
80
+ this.proxy();
81
+ } else {
82
+ this.render404(context);
83
+ }
84
+ }
85
+
86
+ protected async handleWeb(context: ModernServerContext, route: ModernRoute) {
87
+ const { proxyTarget } = this;
88
+
89
+ if (route.isSSR && proxyTarget?.ssr) {
90
+ return this.proxy();
91
+ } else {
92
+ // if no proxyTarget but access web server, degradation to csr
93
+ route.isSSR = false;
94
+ return super.handleWeb(context, route);
95
+ }
44
96
  }
45
97
  }
@@ -16,13 +16,18 @@ import {
16
16
  Logger,
17
17
  ReadyOptions,
18
18
  ConfWithBFF,
19
- } from '../type';
20
- import { RouteMatchManager, ModernRouteInterface } from '../libs/route';
21
- import { createRenderHandler } from '../libs/render';
22
- import { createStaticFileHandler } from '../libs/serve-file';
23
- import { createErrorDocument, mergeExtension, noop } from '../utils';
24
- import * as reader from '../libs/render/reader';
25
- import { createProxyHandler, ProxyOptions } from '../libs/proxy';
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 { createErrorDocument, mergeExtension, noop } from '@/utils';
29
+ import * as reader from '@/libs/render/reader';
30
+ import { createProxyHandler, ProxyOptions } from '@/libs/proxy';
26
31
  import { createContext, ModernServerContext } from '@/libs/context';
27
32
  import {
28
33
  AGGRED_DIR,
@@ -70,6 +75,8 @@ export class ModernServer {
70
75
 
71
76
  protected readonly measure: Measure;
72
77
 
78
+ protected readonly proxyTarget: ModernServerOptions['proxyTarget'];
79
+
73
80
  private readonly isDev: boolean = false;
74
81
 
75
82
  private staticFileHandler!: ReturnType<typeof createStaticFileHandler>;
@@ -94,18 +101,20 @@ export class ModernServer {
94
101
  staticGenerate,
95
102
  logger,
96
103
  measure,
104
+ proxyTarget,
97
105
  }: ModernServerOptions) {
98
106
  require('ignore-styles');
99
107
  this.isDev = Boolean(dev);
100
108
 
101
109
  this.pwd = pwd;
102
- this.distDir = path.join(pwd, config.output.path || '');
110
+ this.distDir = path.join(pwd, config.output.path || 'dist');
103
111
  this.workDir = this.isDev ? pwd : this.distDir;
104
112
  this.conf = config;
105
113
  this.logger = logger!;
106
114
  this.measure = measure!;
107
115
  this.router = new RouteMatchManager();
108
116
  this.presetRoutes = routes;
117
+ this.proxyTarget = proxyTarget;
109
118
 
110
119
  if (staticGenerate) {
111
120
  this.staticGenerate = staticGenerate;
@@ -207,18 +216,6 @@ export class ModernServer {
207
216
  return createServer(handler);
208
217
  }
209
218
 
210
- // warmup ssr function
211
- protected warmupSSRBundle() {
212
- const { distDir } = this;
213
- const bundles = this.router.getBundles();
214
-
215
- bundles.forEach(bundle => {
216
- const filepath = path.join(distDir, bundle!);
217
- // if error, just throw and let process die
218
- require(filepath);
219
- });
220
- }
221
-
222
219
  // read route spec from route.json
223
220
  protected readRouteSpec() {
224
221
  const file = path.join(this.distDir, ROUTE_SPEC_FILE);
@@ -276,6 +273,11 @@ export class ModernServer {
276
273
  }
277
274
  }
278
275
 
276
+ // Todo
277
+ protected async proxy() {
278
+ return null as any;
279
+ }
280
+
279
281
  /* —————————————————————— function will be overwrite —————————————————————— */
280
282
  protected async prepareWebHandler(
281
283
  extension: ReturnType<typeof mergeExtension>,
@@ -314,75 +316,89 @@ export class ModernServer {
314
316
  return routes;
315
317
  }
316
318
 
319
+ protected async emitRouteHook(
320
+ eventName: 'beforeMatch' | 'afterMatch' | 'beforeRender' | 'afterRender',
321
+ input: any,
322
+ ) {
323
+ return this.runner[eventName](input, { onLast: noop as any });
324
+ }
325
+
326
+ // warmup ssr function
327
+ protected warmupSSRBundle() {
328
+ const { distDir } = this;
329
+ const bundles = this.router.getBundles();
330
+
331
+ bundles.forEach(bundle => {
332
+ const filepath = path.join(distDir, bundle as string);
333
+ // if error, just throw and let process die
334
+ require(filepath);
335
+ });
336
+ }
337
+
317
338
  protected async preServerInit() {
318
- const { conf } = this;
339
+ const { conf, runner } = this;
319
340
  const preMiddleware: ModernServerAsyncHandler[] =
320
- await this.runner.preServerInit(conf);
341
+ await runner.preServerInit(conf);
321
342
 
322
343
  preMiddleware.flat().forEach(mid => {
323
344
  this.addHandler(mid);
324
345
  });
325
346
  }
326
347
 
327
- private prepareFavicons(
328
- favicon: string | undefined,
329
- faviconByEntries?: Record<string, string | undefined>,
330
- ) {
331
- const faviconNames = [];
332
- if (favicon) {
333
- faviconNames.push(favicon.substring(favicon.lastIndexOf('/') + 1));
334
- }
335
- if (faviconByEntries) {
336
- Object.keys(faviconByEntries).forEach(f => {
337
- const curFavicon = faviconByEntries[f];
338
- if (curFavicon) {
339
- faviconNames.push(
340
- curFavicon.substring(curFavicon.lastIndexOf('/') + 1),
341
- );
342
- }
343
- });
348
+ protected async handleAPI(context: ModernServerContext) {
349
+ const { req, res } = context;
350
+
351
+ if (!this.frameAPIHandler) {
352
+ throw new Error('can not found api hanlder');
344
353
  }
345
- return faviconNames;
354
+
355
+ await this.frameAPIHandler(req, res);
346
356
  }
347
357
 
348
- /* —————————————————————— private function —————————————————————— */
358
+ protected async handleWeb(context: ModernServerContext, route: ModernRoute) {
359
+ return this.routeRenderHandler(context, route);
360
+ }
361
+
362
+ protected verifyMatch(_c: ModernServerContext, _m: RouteMatcher) {
363
+ // empty
364
+ }
349
365
 
366
+ /* —————————————————————— private function —————————————————————— */
350
367
  // handler route.json, include api / csr / ssr
351
368
  // eslint-disable-next-line max-statements
352
369
  private async routeHandler(context: ModernServerContext) {
353
370
  const { req, res } = context;
354
371
 
355
- await this.runner.beforeMatch({ context }, { onLast: noop as any });
372
+ await this.emitRouteHook('beforeMatch', { context });
356
373
 
357
374
  // match routes in the route spec
358
375
  const matched = this.router.match(context.url);
359
376
  if (!matched) {
360
377
  this.render404(context);
361
378
  return;
379
+ } else {
380
+ this.verifyMatch(context, matched);
381
+ }
382
+
383
+ if (res.headersSent) {
384
+ return;
362
385
  }
363
386
 
364
387
  const routeAPI = createRouteAPI(matched, this.router);
365
- await this.runner.afterMatch(
366
- { context, routeAPI },
367
- { onLast: noop as any },
368
- );
388
+ await this.emitRouteHook('afterMatch', { context, routeAPI });
369
389
 
370
390
  if (res.headersSent) {
371
391
  return;
372
392
  }
373
393
 
374
394
  const { current } = routeAPI as any;
375
- const route = current.generate();
395
+ const route: ModernRoute = current.generate();
376
396
  const params = current.parseURLParams(context.url);
377
397
  context.setParams(params);
378
398
 
379
399
  // route is api service
380
400
  if (route.isApi) {
381
- if (!this.frameAPIHandler) {
382
- throw new Error('can not found api hanlder');
383
- }
384
-
385
- await this.frameAPIHandler(req, res);
401
+ this.handleAPI(context);
386
402
  return;
387
403
  }
388
404
 
@@ -395,8 +411,11 @@ export class ModernServer {
395
411
  return;
396
412
  }
397
413
 
398
- await this.runner.beforeRender({ context }, { onLast: noop as any });
399
- const file = await this.routeRenderHandler(context, route);
414
+ if (route.entryName) {
415
+ await this.emitRouteHook('beforeRender', { context });
416
+ }
417
+
418
+ const file = await this.handleWeb(context, route);
400
419
  if (!file) {
401
420
  this.render404(context);
402
421
  return;
@@ -412,10 +431,7 @@ export class ModernServer {
412
431
  let response = file.content;
413
432
  if (route.entryName) {
414
433
  const templateAPI = createTemplateAPI(file.content.toString());
415
- await this.runner.afterRender(
416
- { context, templateAPI },
417
- { onLast: noop as any },
418
- );
434
+ await this.emitRouteHook('afterRender', { context, templateAPI });
419
435
  await this.injectMicroFE(context, templateAPI);
420
436
  response = templateAPI.get();
421
437
  }
@@ -578,5 +594,26 @@ export class ModernServer {
578
594
  const text = ERROR_PAGE_TEXT[status] || ERROR_PAGE_TEXT[500];
579
595
  res.end(createErrorDocument(status, text));
580
596
  }
597
+
598
+ private prepareFavicons(
599
+ favicon: string | undefined,
600
+ faviconByEntries?: Record<string, string | undefined>,
601
+ ) {
602
+ const faviconNames = [];
603
+ if (favicon) {
604
+ faviconNames.push(favicon.substring(favicon.lastIndexOf('/') + 1));
605
+ }
606
+ if (faviconByEntries) {
607
+ Object.keys(faviconByEntries).forEach(f => {
608
+ const curFavicon = faviconByEntries[f];
609
+ if (curFavicon) {
610
+ faviconNames.push(
611
+ curFavicon.substring(curFavicon.lastIndexOf('/') + 1),
612
+ );
613
+ }
614
+ });
615
+ }
616
+ return faviconNames;
617
+ }
581
618
  }
582
619
  /* eslint-enable max-lines */
package/src/type.ts CHANGED
@@ -49,7 +49,12 @@ export type ModernServerOptions = {
49
49
  logger?: Logger;
50
50
  measure?: Measure;
51
51
  apiOnly?: boolean;
52
+ ssrOnly?: boolean;
52
53
  webOnly?: boolean;
54
+ proxyTarget?: {
55
+ ssr?: string;
56
+ api?: string;
57
+ };
53
58
  };
54
59
 
55
60
  export type RenderResult = {
package/src/utils.ts CHANGED
@@ -49,3 +49,17 @@ export const createErrorDocument = (status: number, text: string) => {
49
49
  </html>
50
50
  `;
51
51
  };
52
+
53
+ // This can live anywhere in your codebase:
54
+ export function applyMixins(derivedCtor: any, constructors: any[]) {
55
+ constructors.forEach(baseCtor => {
56
+ Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
57
+ Object.defineProperty(
58
+ derivedCtor.prototype,
59
+ name,
60
+ Object.getOwnPropertyDescriptor(baseCtor.prototype, name) ||
61
+ Object.create(null),
62
+ );
63
+ });
64
+ });
65
+ }