@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,192 +0,0 @@
|
|
|
1
|
-
import { Server } from 'http';
|
|
2
|
-
import { Socket } from 'net';
|
|
3
|
-
import ws from 'ws';
|
|
4
|
-
import type { Stats } from 'webpack';
|
|
5
|
-
import { logger } from '@modern-js/utils';
|
|
6
|
-
import { DevServerOptions } from '../type';
|
|
7
|
-
import { noop } from '../utils';
|
|
8
|
-
|
|
9
|
-
interface ExtWebSocket extends ws {
|
|
10
|
-
isAlive: boolean;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export default class SocketServer {
|
|
14
|
-
private wsServer!: ws.Server;
|
|
15
|
-
|
|
16
|
-
private readonly sockets: ws[] = [];
|
|
17
|
-
|
|
18
|
-
private readonly options: DevServerOptions;
|
|
19
|
-
|
|
20
|
-
private app?: Server;
|
|
21
|
-
|
|
22
|
-
private stats?: Stats;
|
|
23
|
-
|
|
24
|
-
constructor(options: DevServerOptions) {
|
|
25
|
-
this.options = options;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// create socket, install socket handler, bind socket event
|
|
29
|
-
public prepare(app: Server) {
|
|
30
|
-
this.app = app;
|
|
31
|
-
|
|
32
|
-
this.wsServer = new ws.Server({
|
|
33
|
-
noServer: true,
|
|
34
|
-
path: this.options.client.path,
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
// listen upgrade event to handle socket
|
|
38
|
-
this.app.on('upgrade', (req, sock, head) => {
|
|
39
|
-
if (!this.wsServer.shouldHandle(req)) {
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
this.wsServer.handleUpgrade(req, sock as Socket, head, connection => {
|
|
44
|
-
this.wsServer.emit('connection', connection, req);
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
this.wsServer.on('error', (err: Error) => {
|
|
49
|
-
// only dev server, use default logger
|
|
50
|
-
logger.error(err);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
setInterval(() => {
|
|
54
|
-
this.wsServer.clients.forEach(socket => {
|
|
55
|
-
const extWs = socket as ExtWebSocket;
|
|
56
|
-
if (!extWs.isAlive) {
|
|
57
|
-
extWs.terminate();
|
|
58
|
-
} else {
|
|
59
|
-
extWs.isAlive = false;
|
|
60
|
-
extWs.ping(noop);
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
}, 30000);
|
|
64
|
-
|
|
65
|
-
this.wsServer.on('connection', socket => {
|
|
66
|
-
const connection = socket as ExtWebSocket;
|
|
67
|
-
|
|
68
|
-
connection.isAlive = true;
|
|
69
|
-
connection.on('pong', () => {
|
|
70
|
-
connection.isAlive = true;
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
if (!connection) {
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
this.sockets.push(connection);
|
|
78
|
-
|
|
79
|
-
connection.on('close', () => {
|
|
80
|
-
const idx = this.sockets.indexOf(connection);
|
|
81
|
-
|
|
82
|
-
if (idx >= 0) {
|
|
83
|
-
this.sockets.splice(idx, 1);
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
if (this.options.client.logging) {
|
|
88
|
-
this.sockWrite('logging', this.options.client.logging);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (this.options.hot || this.options.hot === 'only') {
|
|
92
|
-
this.sockWrite('hot');
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (this.options.liveReload) {
|
|
96
|
-
this.sockWrite('liveReload');
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (this.options.client.progress) {
|
|
100
|
-
this.sockWrite('progress', this.options.client.progress);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (this.options.client.overlay) {
|
|
104
|
-
this.sockWrite('overlay', this.options.client.overlay);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// send first stats to active client sock if stats exist
|
|
108
|
-
if (this.stats) {
|
|
109
|
-
this.sendStats(true);
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
public updateStats(stats: Stats) {
|
|
115
|
-
this.stats = stats;
|
|
116
|
-
this.sendStats();
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// write message to each socket
|
|
120
|
-
public sockWrite(
|
|
121
|
-
type: string,
|
|
122
|
-
data?: Record<string, any> | string | boolean,
|
|
123
|
-
) {
|
|
124
|
-
this.sockets.forEach(socket => {
|
|
125
|
-
this.send(socket, JSON.stringify({ type, data }));
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
public close(connection: ws) {
|
|
130
|
-
connection.close();
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// get standard stats
|
|
134
|
-
private getStats() {
|
|
135
|
-
const curStats = this.stats;
|
|
136
|
-
|
|
137
|
-
if (!curStats) {
|
|
138
|
-
return null;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const defaultStats: Record<string, boolean> = {
|
|
142
|
-
all: false,
|
|
143
|
-
hash: true,
|
|
144
|
-
assets: true,
|
|
145
|
-
warnings: true,
|
|
146
|
-
errors: true,
|
|
147
|
-
errorDetails: false,
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
return curStats.toJson(defaultStats);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// determine what message should send by stats
|
|
154
|
-
private sendStats(force = false) {
|
|
155
|
-
const stats = this.getStats();
|
|
156
|
-
|
|
157
|
-
// this should never happend
|
|
158
|
-
if (!stats) {
|
|
159
|
-
return null;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const shouldEmit =
|
|
163
|
-
!force &&
|
|
164
|
-
stats &&
|
|
165
|
-
(!stats.errors || stats.errors.length === 0) &&
|
|
166
|
-
stats.assets &&
|
|
167
|
-
stats.assets.every((asset: any) => !asset.emitted);
|
|
168
|
-
|
|
169
|
-
if (shouldEmit) {
|
|
170
|
-
return this.sockWrite('still-ok');
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
this.sockWrite('hash', stats.hash);
|
|
174
|
-
|
|
175
|
-
if (stats.errors && stats.errors.length > 0) {
|
|
176
|
-
return this.sockWrite('errors', stats.errors);
|
|
177
|
-
} else if (stats.warnings && stats.warnings.length > 0) {
|
|
178
|
-
return this.sockWrite('warnings', stats.warnings);
|
|
179
|
-
} else {
|
|
180
|
-
return this.sockWrite('ok');
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// send message to connecting socket
|
|
185
|
-
private send(connection: ws, message: string) {
|
|
186
|
-
if (connection.readyState !== 1) {
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
connection.send(message);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import minimatch from 'minimatch';
|
|
2
|
-
|
|
3
|
-
export const defaultIgnores = [
|
|
4
|
-
'**/bower_components/**',
|
|
5
|
-
'**/coverage/**',
|
|
6
|
-
'**/node_modules/**',
|
|
7
|
-
'**/.*/**',
|
|
8
|
-
'**/*.d.ts',
|
|
9
|
-
'**/*.log',
|
|
10
|
-
];
|
|
11
|
-
|
|
12
|
-
export interface DependencyTreeOptions {
|
|
13
|
-
root: string;
|
|
14
|
-
ignore?: string[];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface TreeNode {
|
|
18
|
-
module: NodeModule;
|
|
19
|
-
parent: Set<TreeNode>;
|
|
20
|
-
children: Set<TreeNode>;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* `require.cache` already is a dependency tree, however require cache's
|
|
25
|
-
* `module.parent` is the module that first required. so we have to implement
|
|
26
|
-
* a new tree which revisit the cache tree to find all parent node
|
|
27
|
-
*/
|
|
28
|
-
export class DependencyTree {
|
|
29
|
-
private readonly tree: Map<string, TreeNode>;
|
|
30
|
-
|
|
31
|
-
private readonly ignore: string[];
|
|
32
|
-
|
|
33
|
-
constructor() {
|
|
34
|
-
this.tree = new Map<string, TreeNode>();
|
|
35
|
-
this.ignore = [...defaultIgnores];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
public getNode(path: string) {
|
|
39
|
-
return this.tree.get(path);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* update dependency tree
|
|
44
|
-
*
|
|
45
|
-
* @param cache
|
|
46
|
-
*/
|
|
47
|
-
public update(cache: any) {
|
|
48
|
-
this.tree.clear();
|
|
49
|
-
|
|
50
|
-
// insert all module that not ignored
|
|
51
|
-
Object.keys(cache).forEach(path => {
|
|
52
|
-
if (!this.shouldIgnore(path)) {
|
|
53
|
-
const module = cache[path];
|
|
54
|
-
this.tree.set(module.filename, {
|
|
55
|
-
module,
|
|
56
|
-
parent: new Set<TreeNode>(),
|
|
57
|
-
children: new Set<TreeNode>(),
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
// update treeNode parent and children
|
|
63
|
-
for (const treeNode of this.tree.values()) {
|
|
64
|
-
const { parent } = treeNode.module;
|
|
65
|
-
const { children } = treeNode.module;
|
|
66
|
-
|
|
67
|
-
if (parent && !this.shouldIgnore(parent.filename)) {
|
|
68
|
-
const parentTreeNode = this.tree.get(parent.filename)!;
|
|
69
|
-
if (parentTreeNode) {
|
|
70
|
-
treeNode.parent.add(parentTreeNode);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
children.forEach(child => {
|
|
75
|
-
if (!this.shouldIgnore(child.filename)) {
|
|
76
|
-
const childTreeNode = this.tree.get(child.filename)!;
|
|
77
|
-
if (childTreeNode) {
|
|
78
|
-
treeNode.children.add(childTreeNode);
|
|
79
|
-
childTreeNode.parent.add(treeNode);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
private shouldIgnore(path: string): boolean {
|
|
87
|
-
return (
|
|
88
|
-
!path ||
|
|
89
|
-
Boolean(
|
|
90
|
-
this.ignore.find(rule => minimatch.match([path], rule).length > 0),
|
|
91
|
-
)
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import chokidar, { FSWatcher, WatchOptions } from 'chokidar';
|
|
2
|
-
import { DependencyTree } from './dependency-tree';
|
|
3
|
-
import { StatsCache } from './stats-cache';
|
|
4
|
-
|
|
5
|
-
const getWatchedFiles = (watcher: chokidar.FSWatcher) => {
|
|
6
|
-
const watched = watcher.getWatched();
|
|
7
|
-
const files: string[] = [];
|
|
8
|
-
Object.keys(watched).forEach(dir => {
|
|
9
|
-
watched[dir].forEach((fileName: string) => {
|
|
10
|
-
files.push(`${dir}/${fileName}`);
|
|
11
|
-
});
|
|
12
|
-
});
|
|
13
|
-
return files;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export default class Watcher {
|
|
17
|
-
private dependencyTree: DependencyTree | null = null;
|
|
18
|
-
|
|
19
|
-
private watcher!: FSWatcher;
|
|
20
|
-
|
|
21
|
-
public listen(
|
|
22
|
-
files: string[],
|
|
23
|
-
options: WatchOptions,
|
|
24
|
-
callback: (changed: string) => void,
|
|
25
|
-
) {
|
|
26
|
-
const watched = files.filter(Boolean);
|
|
27
|
-
const filenames = watched.map(filename => filename.replace(/\\/g, '/'));
|
|
28
|
-
// eslint-disable-next-line no-console
|
|
29
|
-
console.log('watched files:', filenames);
|
|
30
|
-
|
|
31
|
-
const cache = new StatsCache();
|
|
32
|
-
const watcher = chokidar.watch(filenames, options);
|
|
33
|
-
|
|
34
|
-
watcher.on('ready', () => {
|
|
35
|
-
cache.add(getWatchedFiles(watcher));
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
watcher.on('change', changed => {
|
|
39
|
-
if (cache.isDiff(changed)) {
|
|
40
|
-
cache.refresh(changed);
|
|
41
|
-
callback(changed);
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
watcher.on('add', changed => {
|
|
46
|
-
if (!cache.has(changed)) {
|
|
47
|
-
cache.add([changed]);
|
|
48
|
-
callback(changed);
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
watcher.on('unlink', changed => {
|
|
53
|
-
cache.del(changed);
|
|
54
|
-
callback(changed);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
this.watcher = watcher;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
public createDepTree() {
|
|
61
|
-
this.dependencyTree = new DependencyTree();
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
public updateDepTree() {
|
|
65
|
-
this.dependencyTree?.update(require.cache);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
public cleanDepCache(filepath: string) {
|
|
69
|
-
const node = this.dependencyTree?.getNode(filepath);
|
|
70
|
-
if (node && require.cache[filepath]) {
|
|
71
|
-
delete require.cache[filepath];
|
|
72
|
-
for (const parentNode of node.parent.values()) {
|
|
73
|
-
this.cleanDepCache(parentNode.module.filename);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
public close() {
|
|
79
|
-
return this.watcher.close();
|
|
80
|
-
}
|
|
81
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
|
|
3
|
-
export class StatsCache {
|
|
4
|
-
private readonly cachedStats: Record<string, number> = {};
|
|
5
|
-
|
|
6
|
-
public add(files: string[]) {
|
|
7
|
-
const { cachedStats } = this;
|
|
8
|
-
for (const filename of files) {
|
|
9
|
-
if (fs.existsSync(filename)) {
|
|
10
|
-
const stat = fs.statSync(filename);
|
|
11
|
-
if (stat.isFile() && !cachedStats[filename]) {
|
|
12
|
-
cachedStats[filename] = this.sign(stat);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
public refresh(filename: string) {
|
|
19
|
-
const { cachedStats } = this;
|
|
20
|
-
if (fs.existsSync(filename)) {
|
|
21
|
-
const stat = fs.statSync(filename);
|
|
22
|
-
if (stat.isFile()) {
|
|
23
|
-
cachedStats[filename] = this.sign(stat);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
public del(filename: string) {
|
|
29
|
-
if (this.cachedStats[filename]) {
|
|
30
|
-
delete this.cachedStats[filename];
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
public isDiff(filename: string) {
|
|
35
|
-
const { cachedStats } = this;
|
|
36
|
-
const stat = fs.statSync(filename);
|
|
37
|
-
const cachedStat = cachedStats[filename];
|
|
38
|
-
|
|
39
|
-
if (this.sign(stat) !== cachedStat) {
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
public has(filename: string) {
|
|
46
|
-
return Boolean(this.cachedStats[filename]);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Todo size 其实有点问题,修改单个字符会导致触发不了 change
|
|
50
|
-
private sign(stat: fs.Stats) {
|
|
51
|
-
return stat.size;
|
|
52
|
-
}
|
|
53
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { ModernServerOptions } from './type';
|
|
2
|
-
import { Server } from './server';
|
|
3
|
-
|
|
4
|
-
export type { SSRServerContext } from './libs/render/type';
|
|
5
|
-
export { Server };
|
|
6
|
-
export type { ModernServerOptions };
|
|
7
|
-
|
|
8
|
-
export default (options: ModernServerOptions): Promise<Server> => {
|
|
9
|
-
if (options == null) {
|
|
10
|
-
throw new Error('can not start mserver without options');
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const server = new Server(options);
|
|
14
|
-
|
|
15
|
-
return server.init();
|
|
16
|
-
};
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
import { IncomingMessage, ServerResponse } from 'http';
|
|
2
|
-
import { URL } from 'url';
|
|
3
|
-
import qs from 'querystring';
|
|
4
|
-
import type {
|
|
5
|
-
ModernServerContext as ModernServerContextInterface,
|
|
6
|
-
Metrics,
|
|
7
|
-
Logger,
|
|
8
|
-
} from '@modern-js/types/server';
|
|
9
|
-
import { toMessage } from '../../utils';
|
|
10
|
-
|
|
11
|
-
export type ContextOptions = {
|
|
12
|
-
logger?: Logger;
|
|
13
|
-
metrics?: Metrics;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export class ModernServerContext implements ModernServerContextInterface {
|
|
17
|
-
/**
|
|
18
|
-
* http request
|
|
19
|
-
*/
|
|
20
|
-
public req: IncomingMessage;
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* http response
|
|
24
|
-
*/
|
|
25
|
-
public res: ServerResponse;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* url params
|
|
29
|
-
*/
|
|
30
|
-
public params: Record<string, string> = {};
|
|
31
|
-
|
|
32
|
-
public logger: Logger;
|
|
33
|
-
|
|
34
|
-
public metrics?: Metrics;
|
|
35
|
-
|
|
36
|
-
constructor(req: IncomingMessage, res: ServerResponse) {
|
|
37
|
-
this.req = req;
|
|
38
|
-
this.res = res;
|
|
39
|
-
this.logger = req.logger;
|
|
40
|
-
this.metrics = req.metrics;
|
|
41
|
-
|
|
42
|
-
this.bind();
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
private bind() {
|
|
46
|
-
const { req, res } = this as any;
|
|
47
|
-
req.get = (key: string) => this.getReqHeader(key);
|
|
48
|
-
res.set = (key: string, value: any) => this.res.setHeader(key, value);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
public setParams(params: Record<string, string>) {
|
|
52
|
-
this.params = params;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
public getReqHeader(key: string) {
|
|
56
|
-
const { req } = this;
|
|
57
|
-
const field = key.toLowerCase();
|
|
58
|
-
switch (field) {
|
|
59
|
-
case 'referer':
|
|
60
|
-
case 'referrer':
|
|
61
|
-
return req.headers.referrer || req.headers.referer || '';
|
|
62
|
-
default:
|
|
63
|
-
return req.headers[field] || '';
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/* request property */
|
|
68
|
-
public get headers() {
|
|
69
|
-
return this.req.headers;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
public get method(): string {
|
|
73
|
-
return this.req.method!;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
public get url() {
|
|
77
|
-
return this.req.url || '';
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
public set url(val) {
|
|
81
|
-
this.req.url = val;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
public get host() {
|
|
85
|
-
let host = this.getReqHeader('X-Forwarded-Host');
|
|
86
|
-
if (!host) {
|
|
87
|
-
host = this.getReqHeader('Host');
|
|
88
|
-
}
|
|
89
|
-
return (host as string).split(/\s*,\s*/, 1)[0] || '';
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
public get protocol() {
|
|
93
|
-
if ((this.req.socket as any).encrypted) {
|
|
94
|
-
return 'https';
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const proto = this.getReqHeader('X-Forwarded-Proto');
|
|
98
|
-
return proto ? (proto as string).split(/\s*,\s*/, 1)[0] : 'http';
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
public get origin() {
|
|
102
|
-
return `${this.protocol}://${this.host}`;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
public get href() {
|
|
106
|
-
return this.origin + this.url;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
public get parsedURL() {
|
|
110
|
-
const url = new URL(this.req.url!, this.origin);
|
|
111
|
-
return url;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
public get path() {
|
|
115
|
-
return this.parsedURL.pathname;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
public set path(p) {
|
|
119
|
-
const url = new URL(this.req.url!, this.origin);
|
|
120
|
-
// this should never happend
|
|
121
|
-
if (!url || !p) {
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (url.pathname === p) {
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
url.pathname = p;
|
|
130
|
-
|
|
131
|
-
this.url = url.toString();
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
public get querystring() {
|
|
135
|
-
if (!this.req) {
|
|
136
|
-
return '';
|
|
137
|
-
}
|
|
138
|
-
return this.parsedURL.search.replace(/^\?/, '') || '';
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
public get query() {
|
|
142
|
-
const str = this.querystring;
|
|
143
|
-
return qs.parse(str);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/* response property */
|
|
147
|
-
public get status() {
|
|
148
|
-
return this.res.statusCode;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
public set status(statusCode) {
|
|
152
|
-
this.res.statusCode = statusCode;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* 判断链接是否已经关闭
|
|
157
|
-
*/
|
|
158
|
-
public resHasHandled(): boolean {
|
|
159
|
-
return this.res.writableEnded;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
public logInfo() {
|
|
163
|
-
return {
|
|
164
|
-
headers: this.headers,
|
|
165
|
-
href: this.href,
|
|
166
|
-
url: this.url,
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
public error(dig: string, err: Error | string = '') {
|
|
171
|
-
const message = toMessage(dig, err);
|
|
172
|
-
const reqInfo = this.logInfo();
|
|
173
|
-
|
|
174
|
-
this.logger.error(`${reqInfo.url} - ${message}`, reqInfo);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { RouteMatchManager, RouteMatcher } from '../route';
|
|
2
|
-
|
|
3
|
-
class RouteAPI {
|
|
4
|
-
private readonly router: RouteMatchManager;
|
|
5
|
-
|
|
6
|
-
private current: RouteMatcher;
|
|
7
|
-
|
|
8
|
-
constructor(matched: RouteMatcher, router: RouteMatchManager) {
|
|
9
|
-
this.current = matched;
|
|
10
|
-
this.router = router;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
public cur() {
|
|
14
|
-
return this.current.generate();
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
public get(entryName: string) {
|
|
18
|
-
const { router } = this;
|
|
19
|
-
const matched = router.matchEntry(entryName);
|
|
20
|
-
return matched ? matched.generate() : null;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
public use(entryName: string) {
|
|
24
|
-
const { router } = this;
|
|
25
|
-
const matched = router.matchEntry(entryName);
|
|
26
|
-
if (matched) {
|
|
27
|
-
this.current = matched;
|
|
28
|
-
return true;
|
|
29
|
-
} else {
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export const createRouteAPI = (
|
|
36
|
-
matched: RouteMatcher,
|
|
37
|
-
router: RouteMatchManager,
|
|
38
|
-
) => new RouteAPI(matched, router);
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
const RegList = {
|
|
2
|
-
before: {
|
|
3
|
-
head: '<head>',
|
|
4
|
-
body: '<body>',
|
|
5
|
-
},
|
|
6
|
-
after: {
|
|
7
|
-
head: '</head>',
|
|
8
|
-
body: '</body>',
|
|
9
|
-
},
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
class TemplateAPI {
|
|
13
|
-
private content: string;
|
|
14
|
-
|
|
15
|
-
constructor(content: string) {
|
|
16
|
-
this.content = content;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
public get() {
|
|
20
|
-
return this.content;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
public set(content: string) {
|
|
24
|
-
this.content = content;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
public prependHead(fragment: string) {
|
|
28
|
-
const { head } = RegList.before;
|
|
29
|
-
return this.replace(head, `${head}${fragment}`);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
public appendHead(fragment: string) {
|
|
33
|
-
const { head } = RegList.after;
|
|
34
|
-
return this.replace(head, `${fragment}${head}`);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
public prependBody(fragment: string) {
|
|
38
|
-
const { body } = RegList.before;
|
|
39
|
-
return this.replace(body, `${body}${fragment}`);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
public appendBody(fragment: string) {
|
|
43
|
-
const { body } = RegList.after;
|
|
44
|
-
return this.replace(body, `${fragment}${body}`);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
public replace(reg: RegExp | string, text: string) {
|
|
48
|
-
this.content = this.content.replace(reg, text);
|
|
49
|
-
return this;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export const createTemplateAPI = (content: string) => new TemplateAPI(content);
|