@modern-js/prod-server 1.1.2 → 1.1.5
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 +34 -0
- package/dist/js/modern/libs/context/context.js +21 -22
- package/dist/js/modern/libs/hook-api/route.js +8 -3
- package/dist/js/modern/libs/hook-api/template.js +4 -1
- package/dist/js/modern/libs/metrics.js +2 -2
- package/dist/js/modern/libs/proxy.js +2 -2
- package/dist/js/modern/libs/render/cache/page-caches/lru.js +6 -2
- package/dist/js/modern/libs/render/cache/spr.js +7 -4
- package/dist/js/modern/libs/render/measure.js +59 -0
- package/dist/js/modern/libs/render/reader.js +4 -1
- package/dist/js/modern/libs/render/ssr.js +5 -2
- package/dist/js/modern/libs/route/index.js +6 -2
- package/dist/js/modern/libs/route/matcher.js +10 -4
- package/dist/js/modern/libs/route/route.js +20 -9
- package/dist/js/modern/server/index.js +16 -8
- package/dist/js/modern/server/modern-server-split.js +4 -4
- package/dist/js/modern/server/modern-server.js +61 -33
- package/dist/js/modern/utils.js +0 -9
- package/dist/js/node/libs/context/context.js +21 -23
- package/dist/js/node/libs/hook-api/route.js +8 -3
- package/dist/js/node/libs/hook-api/template.js +5 -1
- package/dist/js/node/libs/metrics.js +2 -2
- package/dist/js/node/libs/proxy.js +2 -2
- package/dist/js/node/libs/render/cache/page-caches/lru.js +6 -2
- package/dist/js/node/libs/render/cache/spr.js +6 -6
- package/dist/js/node/libs/render/measure.js +71 -0
- package/dist/js/node/libs/render/reader.js +4 -1
- package/dist/js/node/libs/render/ssr.js +6 -2
- package/dist/js/node/libs/route/index.js +6 -2
- package/dist/js/node/libs/route/matcher.js +10 -4
- package/dist/js/node/libs/route/route.js +20 -9
- package/dist/js/node/server/index.js +15 -7
- package/dist/js/node/server/modern-server-split.js +4 -4
- package/dist/js/node/server/modern-server.js +61 -33
- package/dist/js/node/utils.js +1 -13
- package/dist/types/libs/context/context.d.ts +3 -8
- package/dist/types/libs/render/measure.d.ts +10 -0
- package/dist/types/server/modern-server.d.ts +2 -2
- package/dist/types/utils.d.ts +0 -1
- package/package.json +30 -9
- package/.eslintrc.js +0 -8
- package/dist/js/styles/tsconfig.json +0 -12
- package/jest.config.js +0 -8
- package/modern.config.js +0 -2
- package/src/constants.ts +0 -31
- package/src/index.ts +0 -19
- package/src/libs/context/context.ts +0 -183
- package/src/libs/context/index.ts +0 -7
- package/src/libs/hook-api/route.ts +0 -42
- package/src/libs/hook-api/template.ts +0 -53
- package/src/libs/loadConfig.ts +0 -59
- package/src/libs/metrics.ts +0 -15
- package/src/libs/proxy.ts +0 -44
- 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 -73
- 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 -300
- package/src/libs/render/cache/type.ts +0 -59
- package/src/libs/render/cache/util.ts +0 -94
- package/src/libs/render/index.ts +0 -79
- 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 -66
- package/src/libs/render/static.ts +0 -52
- package/src/libs/render/type.ts +0 -38
- package/src/libs/route/index.ts +0 -76
- package/src/libs/route/matcher.ts +0 -108
- package/src/libs/route/route.ts +0 -34
- package/src/libs/serve-file.ts +0 -34
- package/src/server/index.ts +0 -227
- package/src/server/modern-server-split.ts +0 -93
- package/src/server/modern-server.ts +0 -620
- package/src/tsconfig.json +0 -12
- package/src/type.ts +0 -95
- package/src/utils.ts +0 -125
- package/tsconfig.json +0 -11
package/src/libs/render/ssr.ts
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import { mime, SERVER_RENDER_FUNCTION_NAME } from '@modern-js/utils';
|
|
3
|
-
import cookie from 'cookie';
|
|
4
|
-
import { ModernServerContext } from '../context';
|
|
5
|
-
import { RenderResult, ServerHookRunner } from '../../type';
|
|
6
|
-
import cache from './cache';
|
|
7
|
-
import { SSRServerContext } from './type';
|
|
8
|
-
|
|
9
|
-
export const render = async (
|
|
10
|
-
ctx: ModernServerContext,
|
|
11
|
-
renderOptions: {
|
|
12
|
-
distDir: string;
|
|
13
|
-
bundle: string;
|
|
14
|
-
urlPath: string;
|
|
15
|
-
template: string;
|
|
16
|
-
entryName: string;
|
|
17
|
-
staticGenerate: boolean;
|
|
18
|
-
},
|
|
19
|
-
runner: ServerHookRunner,
|
|
20
|
-
): Promise<RenderResult> => {
|
|
21
|
-
const { urlPath, bundle, distDir, template, entryName, staticGenerate } =
|
|
22
|
-
renderOptions;
|
|
23
|
-
const bundleJS = path.join(distDir, bundle);
|
|
24
|
-
|
|
25
|
-
const context: SSRServerContext = {
|
|
26
|
-
request: {
|
|
27
|
-
baseUrl: urlPath,
|
|
28
|
-
params: ctx.params,
|
|
29
|
-
pathname: ctx.path,
|
|
30
|
-
host: ctx.host,
|
|
31
|
-
query: ctx.query as Record<string, string>,
|
|
32
|
-
url: ctx.href,
|
|
33
|
-
cookieMap: cookie.parse(ctx.headers.cookie || ''),
|
|
34
|
-
headers: ctx.headers,
|
|
35
|
-
},
|
|
36
|
-
redirection: {},
|
|
37
|
-
template,
|
|
38
|
-
entryName,
|
|
39
|
-
distDir,
|
|
40
|
-
staticGenerate,
|
|
41
|
-
logger: ctx.logger,
|
|
42
|
-
metrics: ctx.metrics,
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
runner.extendSSRContext(context);
|
|
46
|
-
|
|
47
|
-
const serverRender = require(bundleJS)[SERVER_RENDER_FUNCTION_NAME];
|
|
48
|
-
|
|
49
|
-
const html = await cache(serverRender, ctx)(context);
|
|
50
|
-
|
|
51
|
-
const { url, status = 302 } = context.redirection;
|
|
52
|
-
|
|
53
|
-
if (url) {
|
|
54
|
-
return {
|
|
55
|
-
content: url,
|
|
56
|
-
contentType: '',
|
|
57
|
-
statusCode: status,
|
|
58
|
-
redirect: true,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return {
|
|
63
|
-
content: html,
|
|
64
|
-
contentType: mime.contentType('html') as string,
|
|
65
|
-
};
|
|
66
|
-
};
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import { mime } from '@modern-js/utils';
|
|
3
|
-
import { RenderResult } from '../../type';
|
|
4
|
-
import { ModernServerContext } from '../context';
|
|
5
|
-
import { readFile } from './reader';
|
|
6
|
-
|
|
7
|
-
export async function handleDirectory(
|
|
8
|
-
ctx: ModernServerContext,
|
|
9
|
-
entryPath: string,
|
|
10
|
-
urlPath: string,
|
|
11
|
-
): Promise<RenderResult | null> {
|
|
12
|
-
const { path: pathname } = ctx;
|
|
13
|
-
const filepath = path.join(entryPath, trimLeft(pathname, urlPath));
|
|
14
|
-
|
|
15
|
-
// If can match accurately, always return the one that matches accurately
|
|
16
|
-
let content = await readFile(filepath);
|
|
17
|
-
let contentType = mime.contentType(path.extname(filepath) || '');
|
|
18
|
-
|
|
19
|
-
// automatic addressing
|
|
20
|
-
if (!content) {
|
|
21
|
-
if (pathname.endsWith('/')) {
|
|
22
|
-
content = await readFile(`${filepath}index.html`);
|
|
23
|
-
} else if (!pathname.includes('.')) {
|
|
24
|
-
content = await readFile(`${filepath}.html`);
|
|
25
|
-
if (!content) {
|
|
26
|
-
content = await readFile(`${filepath}/index.html`);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// set content-type as html
|
|
31
|
-
if (content) {
|
|
32
|
-
contentType = mime.contentType('html');
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (!content) {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return {
|
|
41
|
-
content,
|
|
42
|
-
contentType: contentType || '',
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const trimLeft = (str: string, prefix: string): string => {
|
|
47
|
-
if (str.startsWith(prefix)) {
|
|
48
|
-
return str.substring(prefix.length);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return str;
|
|
52
|
-
};
|
package/src/libs/render/type.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { BaseSSRServerContext } from '@modern-js/types/server';
|
|
2
|
-
|
|
3
|
-
type MetaKeyMap = {
|
|
4
|
-
header?: string[];
|
|
5
|
-
query?: string[];
|
|
6
|
-
};
|
|
7
|
-
type MetaKeyMatch = {
|
|
8
|
-
header?: MatchMap;
|
|
9
|
-
query?: MatchMap;
|
|
10
|
-
};
|
|
11
|
-
type MatchMap = Record<string, Record<string, string>>;
|
|
12
|
-
|
|
13
|
-
export type CacheConfig = {
|
|
14
|
-
interval: number;
|
|
15
|
-
staleLimit: number | boolean;
|
|
16
|
-
level: number;
|
|
17
|
-
includes?: MetaKeyMap | null;
|
|
18
|
-
excludes?: MetaKeyMap | null;
|
|
19
|
-
matches?: MetaKeyMatch | null;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export enum RenderLevel {
|
|
23
|
-
CLIENT_RENDER,
|
|
24
|
-
SERVER_PREFETCH,
|
|
25
|
-
SERVER_RENDER,
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export type SSRServerContext = BaseSSRServerContext & {
|
|
29
|
-
cacheConfig?: CacheConfig;
|
|
30
|
-
staticGenerate?: boolean;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
export type ModernSSRReactComponent = React.ComponentType<any> & {
|
|
34
|
-
init: (context: SSRServerContext) => Promise<void>;
|
|
35
|
-
prefetch: (context: SSRServerContext) => Promise<Record<string, any>>;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
export type RenderFunction = (context: SSRServerContext) => Promise<string>;
|
package/src/libs/route/index.ts
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { RouteMatcher } from './matcher';
|
|
2
|
-
import { ModernRoute, ModernRouteInterface } from './route';
|
|
3
|
-
|
|
4
|
-
export class RouteMatchManager {
|
|
5
|
-
public matchers: RouteMatcher[];
|
|
6
|
-
|
|
7
|
-
private specs: ModernRouteInterface[] = [];
|
|
8
|
-
|
|
9
|
-
constructor() {
|
|
10
|
-
this.matchers = [];
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// get all routes matches pathname
|
|
14
|
-
private filter(pathname: string) {
|
|
15
|
-
return this.matchers.reduce((matches, matcher) => {
|
|
16
|
-
if (matcher.matchUrlPath(pathname)) {
|
|
17
|
-
matches.push(matcher);
|
|
18
|
-
}
|
|
19
|
-
return matches;
|
|
20
|
-
}, [] as RouteMatcher[]);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// get best match from a set of matches
|
|
24
|
-
private best(pathname: string, matches: RouteMatcher[]) {
|
|
25
|
-
let best: RouteMatcher | undefined;
|
|
26
|
-
let matchedLen = 0;
|
|
27
|
-
|
|
28
|
-
for (const match of matches) {
|
|
29
|
-
const len = match.matchLength(pathname);
|
|
30
|
-
|
|
31
|
-
if (len === null) {
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (len > matchedLen) {
|
|
36
|
-
best = match;
|
|
37
|
-
matchedLen = len;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return best;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// reset routes matcher
|
|
45
|
-
public reset(specs: ModernRouteInterface[]) {
|
|
46
|
-
this.specs = specs;
|
|
47
|
-
const matchers = specs.reduce((ms, spec) => {
|
|
48
|
-
ms.push(new RouteMatcher(spec));
|
|
49
|
-
return ms;
|
|
50
|
-
}, [] as RouteMatcher[]);
|
|
51
|
-
|
|
52
|
-
this.matchers = matchers;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// get best match from all matcher in manager
|
|
56
|
-
public match(pathname: string) {
|
|
57
|
-
const matches = this.filter(pathname);
|
|
58
|
-
const best = this.best(pathname, matches);
|
|
59
|
-
return best;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
public matchEntry(entryname: string) {
|
|
63
|
-
return this.matchers.find(matcher => matcher.matchEntry(entryname));
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
public getBundles() {
|
|
67
|
-
const bundles = this.specs
|
|
68
|
-
.filter(route => route.isSSR)
|
|
69
|
-
.map(route => route.bundle);
|
|
70
|
-
|
|
71
|
-
return bundles;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export type { ModernRouteInterface, ModernRoute };
|
|
76
|
-
export { RouteMatcher };
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { removeTailSlash } from '@modern-js/utils';
|
|
2
|
-
import {
|
|
3
|
-
MatchFunction,
|
|
4
|
-
MatchResult,
|
|
5
|
-
match,
|
|
6
|
-
pathToRegexp,
|
|
7
|
-
} from 'path-to-regexp';
|
|
8
|
-
import { toPath } from '../../utils';
|
|
9
|
-
import { ModernRoute, ModernRouteInterface } from './route';
|
|
10
|
-
|
|
11
|
-
// eslint-disable-next-line no-useless-escape
|
|
12
|
-
const regCharsDetector = /[^a-zA-Z\-_0-9\/\.]/;
|
|
13
|
-
export class RouteMatcher {
|
|
14
|
-
public spec: ModernRouteInterface;
|
|
15
|
-
|
|
16
|
-
public urlPath: string = '';
|
|
17
|
-
|
|
18
|
-
public urlMatcher?: MatchFunction;
|
|
19
|
-
|
|
20
|
-
public urlReg?: RegExp;
|
|
21
|
-
|
|
22
|
-
constructor(spec: ModernRouteInterface) {
|
|
23
|
-
this.spec = spec;
|
|
24
|
-
this.setupUrlPath();
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// generate modern route object
|
|
28
|
-
public generate(url: string) {
|
|
29
|
-
const route = new ModernRoute(this.spec);
|
|
30
|
-
|
|
31
|
-
if (this.urlPath) {
|
|
32
|
-
const params = this.parseURLParams(url);
|
|
33
|
-
route.urlPath = toPath(route.urlPath, params);
|
|
34
|
-
route.params = params;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return route;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
public parseURLParams(pathname: string) {
|
|
41
|
-
if (!this.urlMatcher) {
|
|
42
|
-
return {};
|
|
43
|
-
} else {
|
|
44
|
-
const matchResult = this.urlMatcher(pathname);
|
|
45
|
-
|
|
46
|
-
return (matchResult as MatchResult<Record<string, string>>).params;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// get match url length
|
|
51
|
-
public matchLength(pathname: string): number | null {
|
|
52
|
-
if (!this.urlReg) {
|
|
53
|
-
return this.urlPath.length;
|
|
54
|
-
} else {
|
|
55
|
-
const result = this.urlReg.exec(pathname);
|
|
56
|
-
return result?.[0]?.length || null;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// if match url path
|
|
61
|
-
public matchUrlPath(requestUrl: string): boolean {
|
|
62
|
-
const urlWithoutSlash =
|
|
63
|
-
requestUrl.endsWith('/') && requestUrl !== '/'
|
|
64
|
-
? requestUrl.slice(0, -1)
|
|
65
|
-
: requestUrl;
|
|
66
|
-
|
|
67
|
-
if (this.urlMatcher) {
|
|
68
|
-
return Boolean(this.urlMatcher(urlWithoutSlash));
|
|
69
|
-
} else {
|
|
70
|
-
if (urlWithoutSlash.startsWith(this.urlPath)) {
|
|
71
|
-
// avoid /abcd match /a
|
|
72
|
-
if (
|
|
73
|
-
this.urlPath !== '/' &&
|
|
74
|
-
urlWithoutSlash.length > this.urlPath.length &&
|
|
75
|
-
!urlWithoutSlash.startsWith(`${this.urlPath}/`)
|
|
76
|
-
) {
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return true;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
public matchEntry(entryName: string): boolean {
|
|
88
|
-
return this.spec.entryName === entryName;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// compiler urlPath to regexp if necessary
|
|
92
|
-
private setupUrlPath() {
|
|
93
|
-
const { urlPath } = this.spec;
|
|
94
|
-
this.urlPath = urlPath === '/' ? urlPath : removeTailSlash(urlPath);
|
|
95
|
-
|
|
96
|
-
const useReg = regCharsDetector.test(urlPath);
|
|
97
|
-
if (useReg) {
|
|
98
|
-
this.urlMatcher = match(urlPath, {
|
|
99
|
-
end: false,
|
|
100
|
-
decode: decodeURIComponent,
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
this.urlReg = pathToRegexp(urlPath, [], {
|
|
104
|
-
end: false,
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
package/src/libs/route/route.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { ServerRoute as ModernRouteInterface } from '@modern-js/types';
|
|
2
|
-
|
|
3
|
-
export type { ModernRouteInterface };
|
|
4
|
-
|
|
5
|
-
export class ModernRoute implements ModernRouteInterface {
|
|
6
|
-
public entryName: string;
|
|
7
|
-
|
|
8
|
-
public urlPath: string;
|
|
9
|
-
|
|
10
|
-
public entryPath: string;
|
|
11
|
-
|
|
12
|
-
public bundle: string;
|
|
13
|
-
|
|
14
|
-
public isApi: boolean;
|
|
15
|
-
|
|
16
|
-
public isSSR: boolean;
|
|
17
|
-
|
|
18
|
-
public isSPA: boolean;
|
|
19
|
-
|
|
20
|
-
public enableModernMode?: boolean;
|
|
21
|
-
|
|
22
|
-
public params: Record<string, any> = {};
|
|
23
|
-
|
|
24
|
-
constructor(routeSpec: ModernRouteInterface) {
|
|
25
|
-
this.entryName = routeSpec.entryName || '';
|
|
26
|
-
this.urlPath = routeSpec.urlPath;
|
|
27
|
-
this.entryPath = routeSpec.entryPath || '';
|
|
28
|
-
this.isSSR = routeSpec.isSSR || false;
|
|
29
|
-
this.isSPA = routeSpec.isSPA || false;
|
|
30
|
-
this.isApi = routeSpec.isApi || false;
|
|
31
|
-
this.bundle = routeSpec.bundle || '';
|
|
32
|
-
this.enableModernMode = routeSpec.enableModernMode ?? false;
|
|
33
|
-
}
|
|
34
|
-
}
|
package/src/libs/serve-file.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
// Todo 看看是不是能 fork 一份,即使命中也返回
|
|
2
|
-
import serve from 'serve-static';
|
|
3
|
-
import { isString, isRegExp } from '@modern-js/utils';
|
|
4
|
-
import { NextFunction } from '../type';
|
|
5
|
-
import { ModernServerContext } from './context';
|
|
6
|
-
|
|
7
|
-
type Rule = {
|
|
8
|
-
path: string | RegExp;
|
|
9
|
-
target: string;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export const createStaticFileHandler =
|
|
13
|
-
(rules: Rule[]) =>
|
|
14
|
-
// eslint-disable-next-line consistent-return
|
|
15
|
-
async (context: ModernServerContext, next: NextFunction) => {
|
|
16
|
-
const { url: requestUrl, req, res } = context;
|
|
17
|
-
|
|
18
|
-
const hitRule = rules.find(item => {
|
|
19
|
-
if (isString(item.path) && requestUrl.startsWith(item.path)) {
|
|
20
|
-
return true;
|
|
21
|
-
} else if (isRegExp(item.path) && item.path.test(requestUrl)) {
|
|
22
|
-
return true;
|
|
23
|
-
}
|
|
24
|
-
return false;
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
if (hitRule) {
|
|
28
|
-
serve(hitRule.target)(req, res, () => {
|
|
29
|
-
next();
|
|
30
|
-
});
|
|
31
|
-
} else {
|
|
32
|
-
return next();
|
|
33
|
-
}
|
|
34
|
-
};
|
package/src/server/index.ts
DELETED
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
import { IncomingMessage, ServerResponse, Server as httpServer } from 'http';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import {
|
|
4
|
-
serverManager,
|
|
5
|
-
AppContext,
|
|
6
|
-
ConfigContext,
|
|
7
|
-
loadPlugins,
|
|
8
|
-
ServerConfig,
|
|
9
|
-
} from '@modern-js/server-core';
|
|
10
|
-
import {
|
|
11
|
-
logger as defaultLogger,
|
|
12
|
-
SHARED_DIR,
|
|
13
|
-
OUTPUT_CONFIG_FILE,
|
|
14
|
-
} from '@modern-js/utils';
|
|
15
|
-
import type { UserConfig } from '@modern-js/core';
|
|
16
|
-
import { ISAppContext } from '@modern-js/types';
|
|
17
|
-
import {
|
|
18
|
-
ModernServerOptions,
|
|
19
|
-
ServerHookRunner,
|
|
20
|
-
ServerConstructor,
|
|
21
|
-
ModernServerInterface,
|
|
22
|
-
} from '../type';
|
|
23
|
-
import { metrics as defaultMetrics } from '../libs/metrics';
|
|
24
|
-
import {
|
|
25
|
-
loadConfig,
|
|
26
|
-
getServerConfigPath,
|
|
27
|
-
requireConfig,
|
|
28
|
-
} from '../libs/loadConfig';
|
|
29
|
-
import { debug } from '../utils';
|
|
30
|
-
import { createProdServer } from './modern-server-split';
|
|
31
|
-
|
|
32
|
-
export class Server {
|
|
33
|
-
public options: ModernServerOptions;
|
|
34
|
-
|
|
35
|
-
protected serverImpl: ServerConstructor = createProdServer;
|
|
36
|
-
|
|
37
|
-
private server!: ModernServerInterface;
|
|
38
|
-
|
|
39
|
-
private app!: httpServer;
|
|
40
|
-
|
|
41
|
-
private runner!: ServerHookRunner;
|
|
42
|
-
|
|
43
|
-
private serverConfig: ServerConfig;
|
|
44
|
-
|
|
45
|
-
constructor(options: ModernServerOptions) {
|
|
46
|
-
options.logger = options.logger || defaultLogger;
|
|
47
|
-
options.metrics = options.metrics || defaultMetrics;
|
|
48
|
-
|
|
49
|
-
this.options = options;
|
|
50
|
-
this.serverConfig = {};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* 初始化顺序
|
|
55
|
-
* - 获取 server runtime config
|
|
56
|
-
* - 设置 context
|
|
57
|
-
* - 创建 hooksRunner
|
|
58
|
-
* - 合并插件,内置插件和 serverConfig 中配置的插件
|
|
59
|
-
* - 执行 config hook
|
|
60
|
-
* - 获取最终的配置
|
|
61
|
-
* - 设置配置到 context
|
|
62
|
-
* - 初始化 server
|
|
63
|
-
* - 执行 prepare hook
|
|
64
|
-
* - 执行 server init
|
|
65
|
-
*/
|
|
66
|
-
public async init() {
|
|
67
|
-
const { options } = this;
|
|
68
|
-
|
|
69
|
-
this.initServerConfig(options);
|
|
70
|
-
|
|
71
|
-
await this.injectContext(this.runner, options);
|
|
72
|
-
|
|
73
|
-
// initialize server runner
|
|
74
|
-
this.runner = await this.createHookRunner();
|
|
75
|
-
|
|
76
|
-
// init config and execute config hook
|
|
77
|
-
await this.initConfig(this.runner, options);
|
|
78
|
-
|
|
79
|
-
await this.injectContext(this.runner, options);
|
|
80
|
-
|
|
81
|
-
// initialize server
|
|
82
|
-
this.server = this.serverImpl(options);
|
|
83
|
-
|
|
84
|
-
await this.runPrepareHook(this.runner);
|
|
85
|
-
|
|
86
|
-
// create http-server
|
|
87
|
-
this.app = await this.server.createHTTPServer(this.getRequestHandler());
|
|
88
|
-
|
|
89
|
-
// runner can only be used after server init
|
|
90
|
-
await this.server.onInit(this.runner);
|
|
91
|
-
|
|
92
|
-
return this;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Execute config hooks
|
|
97
|
-
* @param runner
|
|
98
|
-
* @param options
|
|
99
|
-
*/
|
|
100
|
-
private runConfigHook(runner: ServerHookRunner, serverConfig: ServerConfig) {
|
|
101
|
-
const newServerConfig = runner.config(serverConfig || {});
|
|
102
|
-
return newServerConfig;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
private async runPrepareHook(runner: ServerHookRunner) {
|
|
106
|
-
runner.prepare();
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
private initServerConfig(options: ModernServerOptions) {
|
|
110
|
-
const { pwd, serverConfigFile } = options;
|
|
111
|
-
const distDirectory = path.join(pwd, options.config.output?.path || 'dist');
|
|
112
|
-
const serverConfigPath = getServerConfigPath(
|
|
113
|
-
distDirectory,
|
|
114
|
-
serverConfigFile,
|
|
115
|
-
);
|
|
116
|
-
const serverConfig = requireConfig(serverConfigPath);
|
|
117
|
-
this.serverConfig = serverConfig;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
*
|
|
122
|
-
* merge cliConfig and serverConfig
|
|
123
|
-
*/
|
|
124
|
-
private async initConfig(
|
|
125
|
-
runner: ServerHookRunner,
|
|
126
|
-
options: ModernServerOptions,
|
|
127
|
-
) {
|
|
128
|
-
const { pwd, config } = options;
|
|
129
|
-
|
|
130
|
-
const { serverConfig } = this;
|
|
131
|
-
|
|
132
|
-
const finalServerConfig = this.runConfigHook(runner, serverConfig);
|
|
133
|
-
|
|
134
|
-
const resolvedConfigPath = path.join(
|
|
135
|
-
pwd,
|
|
136
|
-
config?.output?.path || 'dist',
|
|
137
|
-
OUTPUT_CONFIG_FILE,
|
|
138
|
-
);
|
|
139
|
-
|
|
140
|
-
options.config = loadConfig({
|
|
141
|
-
cliConfig: config,
|
|
142
|
-
serverConfig: finalServerConfig,
|
|
143
|
-
resolvedConfigPath,
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
public async close() {
|
|
148
|
-
await this.server.onClose();
|
|
149
|
-
await new Promise<void>(resolve =>
|
|
150
|
-
this.app.close(() => {
|
|
151
|
-
resolve();
|
|
152
|
-
}),
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
public listen(port = 8080, listener: any) {
|
|
157
|
-
this.app.listen(process.env.PORT || port, () => {
|
|
158
|
-
if (listener) {
|
|
159
|
-
listener();
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
this.server.onListening(this.app);
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
public getRequestHandler() {
|
|
167
|
-
return (req: IncomingMessage, res: ServerResponse, next?: () => void) => {
|
|
168
|
-
const requestHandler = this.server.getRequestHandler();
|
|
169
|
-
return requestHandler(req, res, next);
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
private async createHookRunner() {
|
|
174
|
-
// clear server manager every create time
|
|
175
|
-
serverManager.clear();
|
|
176
|
-
|
|
177
|
-
const { options } = this;
|
|
178
|
-
// TODO: 确认下这里是不是可以不从 options 中取插件,而是从 config 中取和过滤
|
|
179
|
-
const { plugins = [], pwd, config } = options;
|
|
180
|
-
|
|
181
|
-
const serverPlugins = this.serverConfig.plugins || [];
|
|
182
|
-
|
|
183
|
-
// server app context for serve plugin
|
|
184
|
-
const loadedPlugins = loadPlugins(plugins.concat(serverPlugins), pwd);
|
|
185
|
-
|
|
186
|
-
debug('plugins', config.plugins, loadedPlugins);
|
|
187
|
-
loadedPlugins.forEach(p => {
|
|
188
|
-
serverManager.usePlugin(p);
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
// create runner
|
|
192
|
-
const hooksRunner = await serverManager.init({});
|
|
193
|
-
|
|
194
|
-
return hooksRunner;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
private async injectContext(
|
|
198
|
-
runner: ServerHookRunner,
|
|
199
|
-
options: ModernServerOptions,
|
|
200
|
-
) {
|
|
201
|
-
const appContext = this.initAppContext();
|
|
202
|
-
const { config, pwd } = options;
|
|
203
|
-
|
|
204
|
-
serverManager.run(() => {
|
|
205
|
-
ConfigContext.set(config as UserConfig);
|
|
206
|
-
AppContext.set({
|
|
207
|
-
...appContext,
|
|
208
|
-
distDirectory: path.join(pwd, config.output?.path || 'dist'),
|
|
209
|
-
});
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
private initAppContext(): ISAppContext {
|
|
214
|
-
const { options } = this;
|
|
215
|
-
const { pwd: appDirectory, plugins = [], config } = options;
|
|
216
|
-
const serverPlugins = plugins.map(p => ({
|
|
217
|
-
server: p,
|
|
218
|
-
}));
|
|
219
|
-
|
|
220
|
-
return {
|
|
221
|
-
appDirectory,
|
|
222
|
-
distDirectory: path.join(appDirectory, config.output?.path || 'dist'),
|
|
223
|
-
sharedDirectory: path.resolve(appDirectory, SHARED_DIR),
|
|
224
|
-
plugins: serverPlugins,
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
}
|