@modern-js/server 1.5.0 → 1.6.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.
- package/CHANGELOG.md +40 -0
- package/dist/js/modern/dev-tools/dev-server-plugin.js +1 -1
- package/dist/js/modern/dev-tools/watcher/index.js +3 -3
- package/dist/js/modern/hmr-client/createSocketUrl.js +49 -0
- package/dist/js/modern/hmr-client/index.js +201 -0
- package/dist/js/modern/server/dev-server.js +18 -6
- package/dist/js/node/dev-tools/dev-server-plugin.js +1 -1
- package/dist/js/node/dev-tools/watcher/index.js +3 -3
- package/dist/js/node/hmr-client/createSocketUrl.js +59 -0
- package/dist/js/node/hmr-client/index.js +207 -0
- package/dist/js/node/server/dev-server.js +18 -6
- package/dist/types/dev-tools/watcher/index.d.ts +2 -1
- package/dist/types/hmr-client/createSocketUrl.d.ts +12 -0
- package/dist/types/hmr-client/index.d.ts +1 -0
- package/dist/types/server/dev-server.d.ts +4 -1
- package/package.json +9 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
# @modern-js/server
|
|
2
2
|
|
|
3
|
+
## 1.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 83660b6: chore(server): merge `@modern-js/hmr-client` to `@modern-js/server`
|
|
8
|
+
|
|
9
|
+
chore(server): 合并 `@modern-js/hmr-client` 到 `@modern-js/server`
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Updated dependencies [1421965]
|
|
14
|
+
- Updated dependencies [02647d2]
|
|
15
|
+
- Updated dependencies [4fc801f]
|
|
16
|
+
- Updated dependencies [9d60891]
|
|
17
|
+
- Updated dependencies [2ed8f7d]
|
|
18
|
+
- Updated dependencies [e4b73b2]
|
|
19
|
+
- Updated dependencies [c8614b8]
|
|
20
|
+
- Updated dependencies [df73691]
|
|
21
|
+
- @modern-js/webpack@1.12.2
|
|
22
|
+
- @modern-js/utils@1.8.0
|
|
23
|
+
- @modern-js/prod-server@1.2.1
|
|
24
|
+
- @modern-js/server-utils@1.2.11
|
|
25
|
+
|
|
26
|
+
## 1.5.1
|
|
27
|
+
|
|
28
|
+
### Patch Changes
|
|
29
|
+
|
|
30
|
+
- a27ab8d: feat: add onApiChange hook for bff hot reload
|
|
31
|
+
feat: 为 BFF 热更新优化,添加 onApiChange 钩子
|
|
32
|
+
- Updated dependencies [b74b0b6]
|
|
33
|
+
- Updated dependencies [7b902b3]
|
|
34
|
+
- Updated dependencies [3d64b2f]
|
|
35
|
+
- Updated dependencies [8b2aa56]
|
|
36
|
+
- Updated dependencies [3e4a34f]
|
|
37
|
+
- Updated dependencies [33cebd2]
|
|
38
|
+
- @modern-js/webpack@1.12.0
|
|
39
|
+
- @modern-js/prod-server@1.2.0
|
|
40
|
+
- @modern-js/server-utils@1.2.11
|
|
41
|
+
- @modern-js/utils@1.7.12
|
|
42
|
+
|
|
3
43
|
## 1.5.0
|
|
4
44
|
|
|
5
45
|
### Minor Changes
|
|
@@ -18,7 +18,7 @@ export default class DevServerPlugin {
|
|
|
18
18
|
const host = client.host ? `&host=${client.host}` : '';
|
|
19
19
|
const path = client.path ? `&path=${client.path}` : '';
|
|
20
20
|
const port = client.port ? `&port=${client.port}` : '';
|
|
21
|
-
const clientEntry = `${require.resolve(
|
|
21
|
+
const clientEntry = `${require.resolve("../hmr-client")}?${host}${path}${port}`; // use a hook to add entries if available
|
|
22
22
|
|
|
23
23
|
new EntryPlugin(compiler.context, clientEntry, {
|
|
24
24
|
name: undefined
|
|
@@ -59,18 +59,18 @@ export default class Watcher {
|
|
|
59
59
|
watcher.on('change', changed => {
|
|
60
60
|
if (!fs.existsSync(changed) || cache.isDiff(changed)) {
|
|
61
61
|
cache.refresh(changed);
|
|
62
|
-
callback(changed);
|
|
62
|
+
callback(changed, 'change');
|
|
63
63
|
}
|
|
64
64
|
});
|
|
65
65
|
watcher.on('add', changed => {
|
|
66
66
|
if (!cache.has(changed)) {
|
|
67
67
|
cache.add([changed]);
|
|
68
|
-
callback(changed);
|
|
68
|
+
callback(changed, 'add');
|
|
69
69
|
}
|
|
70
70
|
});
|
|
71
71
|
watcher.on('unlink', changed => {
|
|
72
72
|
cache.del(changed);
|
|
73
|
-
callback(changed);
|
|
73
|
+
callback(changed, 'unlink');
|
|
74
74
|
});
|
|
75
75
|
this.watcher = watcher;
|
|
76
76
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { HMR_SOCK_PATH } from '@modern-js/utils/constants';
|
|
2
|
+
export function createSocketUrl(resourceQuery) {
|
|
3
|
+
// ?host=localhost&port=8080&path=modern_js_hmr_ws
|
|
4
|
+
const searchParams = resourceQuery.substr(1).split('&');
|
|
5
|
+
const options = {};
|
|
6
|
+
|
|
7
|
+
for (const pair of searchParams) {
|
|
8
|
+
const ary = pair.split('=');
|
|
9
|
+
options[ary[0]] = decodeURIComponent(ary[1]);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const currentLocation = self.location;
|
|
13
|
+
return getSocketUrl(options, currentLocation);
|
|
14
|
+
}
|
|
15
|
+
export function formatURL({
|
|
16
|
+
port,
|
|
17
|
+
protocol,
|
|
18
|
+
hostname,
|
|
19
|
+
pathname
|
|
20
|
+
}) {
|
|
21
|
+
if (window.URL) {
|
|
22
|
+
// eslint-disable-next-line node/prefer-global/url, node/no-unsupported-features/node-builtins
|
|
23
|
+
const url = new URL('http://localhost');
|
|
24
|
+
url.port = port;
|
|
25
|
+
url.hostname = hostname;
|
|
26
|
+
url.protocol = protocol;
|
|
27
|
+
url.pathname = pathname;
|
|
28
|
+
return url.toString();
|
|
29
|
+
} // compatible with IE11
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
const colon = protocol.indexOf(':') === -1 ? ':' : '';
|
|
33
|
+
return `${protocol}${colon}//${hostname}:${port}${pathname}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function getSocketUrl(urlParts, location) {
|
|
37
|
+
const {
|
|
38
|
+
host,
|
|
39
|
+
port,
|
|
40
|
+
path,
|
|
41
|
+
protocol
|
|
42
|
+
} = urlParts;
|
|
43
|
+
return formatURL({
|
|
44
|
+
protocol: protocol || location.protocol === 'https:' ? 'wss' : 'ws',
|
|
45
|
+
hostname: host || location.hostname,
|
|
46
|
+
port: port || location.port,
|
|
47
|
+
pathname: path || HMR_SOCK_PATH
|
|
48
|
+
});
|
|
49
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This has been adapted from `create-react-app`, authored by Facebook, Inc.
|
|
3
|
+
* see: https://github.com/facebookincubator/create-react-app/tree/master/packages/react-dev-utils
|
|
4
|
+
*
|
|
5
|
+
* Tips: this package will be bundled and running in the browser, do not import from the entry of @modern-js/utils.
|
|
6
|
+
*/
|
|
7
|
+
import stripAnsi from '@modern-js/utils/strip-ansi';
|
|
8
|
+
import { formatWebpackMessages } from '@modern-js/utils/format';
|
|
9
|
+
import { createSocketUrl } from "./createSocketUrl"; // declare any to fix the type of `module.hot`
|
|
10
|
+
|
|
11
|
+
// We need to keep track of if there has been a runtime error.
|
|
12
|
+
// Essentially, we cannot guarantee application state was not corrupted by the
|
|
13
|
+
// runtime error. To prevent confusing behavior, we forcibly reload the entire
|
|
14
|
+
// application. This is handled below when we are notified of a compile (code
|
|
15
|
+
// change).
|
|
16
|
+
// See https://github.com/facebook/create-react-app/issues/3096
|
|
17
|
+
const hadRuntimeError = false; // Connect to Dev Server
|
|
18
|
+
|
|
19
|
+
const socketUrl = createSocketUrl(__resourceQuery);
|
|
20
|
+
const connection = new WebSocket(socketUrl); // Unlike WebpackDevServer client, we won't try to reconnect
|
|
21
|
+
// to avoid spamming the console. Disconnect usually happens
|
|
22
|
+
// when developer stops the server.
|
|
23
|
+
|
|
24
|
+
connection.onclose = function () {
|
|
25
|
+
if (typeof console !== 'undefined' && typeof console.info === 'function') {
|
|
26
|
+
console.info('The development server has disconnected.\nRefresh the page if necessary.');
|
|
27
|
+
}
|
|
28
|
+
}; // Remember some state related to hot module replacement.
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
let isFirstCompilation = true;
|
|
32
|
+
let mostRecentCompilationHash = null;
|
|
33
|
+
let hasCompileErrors = false;
|
|
34
|
+
|
|
35
|
+
function clearOutdatedErrors() {
|
|
36
|
+
// Clean up outdated compile errors, if any.
|
|
37
|
+
// eslint-disable-next-line node/no-unsupported-features/node-builtins, no-console
|
|
38
|
+
if (typeof console !== 'undefined' && typeof console.clear === 'function') {
|
|
39
|
+
if (hasCompileErrors) {
|
|
40
|
+
// eslint-disable-next-line node/no-unsupported-features/node-builtins, no-console
|
|
41
|
+
console.clear();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
} // Successful compilation.
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
function handleSuccess() {
|
|
48
|
+
clearOutdatedErrors();
|
|
49
|
+
const isHotUpdate = !isFirstCompilation;
|
|
50
|
+
isFirstCompilation = false;
|
|
51
|
+
hasCompileErrors = false; // Attempt to apply hot updates or reload.
|
|
52
|
+
|
|
53
|
+
if (isHotUpdate) {
|
|
54
|
+
tryApplyUpdates();
|
|
55
|
+
}
|
|
56
|
+
} // Compilation with warnings (e.g. ESLint).
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
function handleWarnings(warnings) {
|
|
60
|
+
clearOutdatedErrors();
|
|
61
|
+
const isHotUpdate = !isFirstCompilation;
|
|
62
|
+
isFirstCompilation = false;
|
|
63
|
+
hasCompileErrors = false;
|
|
64
|
+
|
|
65
|
+
function printWarnings() {
|
|
66
|
+
// Print warnings to the console.
|
|
67
|
+
const formatted = formatWebpackMessages({
|
|
68
|
+
warnings,
|
|
69
|
+
errors: []
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
if (typeof console !== 'undefined' && typeof console.warn === 'function') {
|
|
73
|
+
for (let i = 0; i < formatted.warnings.length; i++) {
|
|
74
|
+
if (i === 5) {
|
|
75
|
+
console.warn('There were more warnings in other files.\n' + 'You can find a complete log in the terminal.');
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
console.warn(stripAnsi(formatted.warnings[i]));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
printWarnings(); // Attempt to apply hot updates or reload.
|
|
85
|
+
|
|
86
|
+
if (isHotUpdate) {
|
|
87
|
+
tryApplyUpdates();
|
|
88
|
+
}
|
|
89
|
+
} // Compilation with errors (e.g. syntax error or missing modules).
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
function handleErrors(errors) {
|
|
93
|
+
clearOutdatedErrors();
|
|
94
|
+
isFirstCompilation = false;
|
|
95
|
+
hasCompileErrors = true; // "Massage" webpack messages.
|
|
96
|
+
|
|
97
|
+
const formatted = formatWebpackMessages({
|
|
98
|
+
errors,
|
|
99
|
+
warnings: []
|
|
100
|
+
}); // Also log them to the console.
|
|
101
|
+
|
|
102
|
+
if (typeof console !== 'undefined' && typeof console.error === 'function') {
|
|
103
|
+
for (const error of formatted.errors) {
|
|
104
|
+
console.error(stripAnsi(error));
|
|
105
|
+
}
|
|
106
|
+
} // Do not attempt to reload now.
|
|
107
|
+
// We will reload on next success instead.
|
|
108
|
+
|
|
109
|
+
} // There is a newer version of the code available.
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
function handleAvailableHash(hash) {
|
|
113
|
+
// Update last known compilation hash.
|
|
114
|
+
mostRecentCompilationHash = hash;
|
|
115
|
+
} // Handle messages from the server.
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
connection.onmessage = function (e) {
|
|
119
|
+
const message = JSON.parse(e.data);
|
|
120
|
+
|
|
121
|
+
switch (message.type) {
|
|
122
|
+
case 'hash':
|
|
123
|
+
handleAvailableHash(message.data);
|
|
124
|
+
break;
|
|
125
|
+
|
|
126
|
+
case 'still-ok':
|
|
127
|
+
case 'ok':
|
|
128
|
+
handleSuccess();
|
|
129
|
+
break;
|
|
130
|
+
|
|
131
|
+
case 'content-changed':
|
|
132
|
+
// Triggered when a file from `contentBase` changed.
|
|
133
|
+
window.location.reload();
|
|
134
|
+
break;
|
|
135
|
+
|
|
136
|
+
case 'warnings':
|
|
137
|
+
handleWarnings(message.data);
|
|
138
|
+
break;
|
|
139
|
+
|
|
140
|
+
case 'errors':
|
|
141
|
+
handleErrors(message.data);
|
|
142
|
+
break;
|
|
143
|
+
|
|
144
|
+
default: // Do nothing.
|
|
145
|
+
|
|
146
|
+
}
|
|
147
|
+
}; // Is there a newer version of this code available?
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
function isUpdateAvailable() {
|
|
151
|
+
// __webpack_hash__ is the hash of the current compilation.
|
|
152
|
+
// It's a global variable injected by webpack.
|
|
153
|
+
return mostRecentCompilationHash !== __webpack_hash__;
|
|
154
|
+
} // webpack disallows updates in other states.
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
function canApplyUpdates() {
|
|
158
|
+
return module.hot.status() === 'idle';
|
|
159
|
+
} // Attempt to update code on the fly, fall back to a hard reload.
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
function tryApplyUpdates() {
|
|
163
|
+
if (!module.hot) {
|
|
164
|
+
// HotModuleReplacementPlugin is not in webpack configuration.
|
|
165
|
+
window.location.reload();
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (!isUpdateAvailable() || !canApplyUpdates()) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function handleApplyUpdates(err, updatedModules) {
|
|
174
|
+
// NOTE: This var is injected by Webpack's DefinePlugin, and is a boolean instead of string.
|
|
175
|
+
const hasReactRefresh = process.env.FAST_REFRESH;
|
|
176
|
+
const wantsForcedReload = err || !updatedModules || hadRuntimeError; // React refresh can handle hot-reloading over errors.
|
|
177
|
+
|
|
178
|
+
if (!hasReactRefresh && wantsForcedReload) {
|
|
179
|
+
window.location.reload();
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (isUpdateAvailable()) {
|
|
184
|
+
// While we were updating, there was a new update! Do it again.
|
|
185
|
+
tryApplyUpdates();
|
|
186
|
+
}
|
|
187
|
+
} // https://webpack.github.io/docs/hot-module-replacement.html#check
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
const result = module.hot.check(
|
|
191
|
+
/* autoApply */
|
|
192
|
+
true, handleApplyUpdates); // // webpack 2 returns a Promise instead of invoking a callback
|
|
193
|
+
|
|
194
|
+
if (result !== null && result !== void 0 && result.then) {
|
|
195
|
+
result.then(updatedModules => {
|
|
196
|
+
handleApplyUpdates(null, updatedModules);
|
|
197
|
+
}, err => {
|
|
198
|
+
handleApplyUpdates(err, null);
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -195,7 +195,8 @@ export class ModernDevServer extends ModernServer {
|
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
onServerChange({
|
|
198
|
-
filepath
|
|
198
|
+
filepath,
|
|
199
|
+
event
|
|
199
200
|
}) {
|
|
200
201
|
const {
|
|
201
202
|
pwd
|
|
@@ -212,9 +213,19 @@ export class ModernDevServer extends ModernServer {
|
|
|
212
213
|
});
|
|
213
214
|
} else {
|
|
214
215
|
try {
|
|
215
|
-
|
|
216
|
-
filepath
|
|
217
|
-
|
|
216
|
+
const success = this.runner.onApiChange([{
|
|
217
|
+
filename: filepath,
|
|
218
|
+
event
|
|
219
|
+
}]); // onApiChange 钩子被调用,且返回 true,则表示无需重新编译
|
|
220
|
+
// onApiChange 的类型是 WaterFall,WaterFall 钩子的返回值类型目前有问题
|
|
221
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
222
|
+
// @ts-expect-error
|
|
223
|
+
|
|
224
|
+
if (success !== true) {
|
|
225
|
+
super.onServerChange({
|
|
226
|
+
filepath
|
|
227
|
+
});
|
|
228
|
+
}
|
|
218
229
|
} catch (e) {
|
|
219
230
|
this.logger.error(e);
|
|
220
231
|
}
|
|
@@ -354,11 +365,12 @@ export class ModernDevServer extends ModernServer {
|
|
|
354
365
|
const watcher = new Watcher();
|
|
355
366
|
watcher.createDepTree(); // 监听文件变动,如果有变动则给 client,也就是 start 启动的插件发消息
|
|
356
367
|
|
|
357
|
-
watcher.listen(defaultWatchedPaths, watchOptions, filepath => {
|
|
368
|
+
watcher.listen(defaultWatchedPaths, watchOptions, (filepath, event) => {
|
|
358
369
|
watcher.updateDepTree();
|
|
359
370
|
watcher.cleanDepCache(filepath);
|
|
360
371
|
this.onServerChange({
|
|
361
|
-
filepath
|
|
372
|
+
filepath,
|
|
373
|
+
event
|
|
362
374
|
});
|
|
363
375
|
});
|
|
364
376
|
this.watcher = watcher;
|
|
@@ -27,7 +27,7 @@ class DevServerPlugin {
|
|
|
27
27
|
const host = client.host ? `&host=${client.host}` : '';
|
|
28
28
|
const path = client.path ? `&path=${client.path}` : '';
|
|
29
29
|
const port = client.port ? `&port=${client.port}` : '';
|
|
30
|
-
const clientEntry = `${require.resolve(
|
|
30
|
+
const clientEntry = `${require.resolve("../hmr-client")}?${host}${path}${port}`; // use a hook to add entries if available
|
|
31
31
|
|
|
32
32
|
new EntryPlugin(compiler.context, clientEntry, {
|
|
33
33
|
name: undefined
|
|
@@ -82,18 +82,18 @@ class Watcher {
|
|
|
82
82
|
watcher.on('change', changed => {
|
|
83
83
|
if (!_utils.fs.existsSync(changed) || cache.isDiff(changed)) {
|
|
84
84
|
cache.refresh(changed);
|
|
85
|
-
callback(changed);
|
|
85
|
+
callback(changed, 'change');
|
|
86
86
|
}
|
|
87
87
|
});
|
|
88
88
|
watcher.on('add', changed => {
|
|
89
89
|
if (!cache.has(changed)) {
|
|
90
90
|
cache.add([changed]);
|
|
91
|
-
callback(changed);
|
|
91
|
+
callback(changed, 'add');
|
|
92
92
|
}
|
|
93
93
|
});
|
|
94
94
|
watcher.on('unlink', changed => {
|
|
95
95
|
cache.del(changed);
|
|
96
|
-
callback(changed);
|
|
96
|
+
callback(changed, 'unlink');
|
|
97
97
|
});
|
|
98
98
|
this.watcher = watcher;
|
|
99
99
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.createSocketUrl = createSocketUrl;
|
|
7
|
+
exports.formatURL = formatURL;
|
|
8
|
+
|
|
9
|
+
var _constants = require("@modern-js/utils/constants");
|
|
10
|
+
|
|
11
|
+
function createSocketUrl(resourceQuery) {
|
|
12
|
+
// ?host=localhost&port=8080&path=modern_js_hmr_ws
|
|
13
|
+
const searchParams = resourceQuery.substr(1).split('&');
|
|
14
|
+
const options = {};
|
|
15
|
+
|
|
16
|
+
for (const pair of searchParams) {
|
|
17
|
+
const ary = pair.split('=');
|
|
18
|
+
options[ary[0]] = decodeURIComponent(ary[1]);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const currentLocation = self.location;
|
|
22
|
+
return getSocketUrl(options, currentLocation);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function formatURL({
|
|
26
|
+
port,
|
|
27
|
+
protocol,
|
|
28
|
+
hostname,
|
|
29
|
+
pathname
|
|
30
|
+
}) {
|
|
31
|
+
if (window.URL) {
|
|
32
|
+
// eslint-disable-next-line node/prefer-global/url, node/no-unsupported-features/node-builtins
|
|
33
|
+
const url = new URL('http://localhost');
|
|
34
|
+
url.port = port;
|
|
35
|
+
url.hostname = hostname;
|
|
36
|
+
url.protocol = protocol;
|
|
37
|
+
url.pathname = pathname;
|
|
38
|
+
return url.toString();
|
|
39
|
+
} // compatible with IE11
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
const colon = protocol.indexOf(':') === -1 ? ':' : '';
|
|
43
|
+
return `${protocol}${colon}//${hostname}:${port}${pathname}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getSocketUrl(urlParts, location) {
|
|
47
|
+
const {
|
|
48
|
+
host,
|
|
49
|
+
port,
|
|
50
|
+
path,
|
|
51
|
+
protocol
|
|
52
|
+
} = urlParts;
|
|
53
|
+
return formatURL({
|
|
54
|
+
protocol: protocol || location.protocol === 'https:' ? 'wss' : 'ws',
|
|
55
|
+
hostname: host || location.hostname,
|
|
56
|
+
port: port || location.port,
|
|
57
|
+
pathname: path || _constants.HMR_SOCK_PATH
|
|
58
|
+
});
|
|
59
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _stripAnsi = _interopRequireDefault(require("@modern-js/utils/strip-ansi"));
|
|
4
|
+
|
|
5
|
+
var _format = require("@modern-js/utils/format");
|
|
6
|
+
|
|
7
|
+
var _createSocketUrl = require("./createSocketUrl");
|
|
8
|
+
|
|
9
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* This has been adapted from `create-react-app`, authored by Facebook, Inc.
|
|
13
|
+
* see: https://github.com/facebookincubator/create-react-app/tree/master/packages/react-dev-utils
|
|
14
|
+
*
|
|
15
|
+
* Tips: this package will be bundled and running in the browser, do not import from the entry of @modern-js/utils.
|
|
16
|
+
*/
|
|
17
|
+
// We need to keep track of if there has been a runtime error.
|
|
18
|
+
// Essentially, we cannot guarantee application state was not corrupted by the
|
|
19
|
+
// runtime error. To prevent confusing behavior, we forcibly reload the entire
|
|
20
|
+
// application. This is handled below when we are notified of a compile (code
|
|
21
|
+
// change).
|
|
22
|
+
// See https://github.com/facebook/create-react-app/issues/3096
|
|
23
|
+
const hadRuntimeError = false; // Connect to Dev Server
|
|
24
|
+
|
|
25
|
+
const socketUrl = (0, _createSocketUrl.createSocketUrl)(__resourceQuery);
|
|
26
|
+
const connection = new WebSocket(socketUrl); // Unlike WebpackDevServer client, we won't try to reconnect
|
|
27
|
+
// to avoid spamming the console. Disconnect usually happens
|
|
28
|
+
// when developer stops the server.
|
|
29
|
+
|
|
30
|
+
connection.onclose = function () {
|
|
31
|
+
if (typeof console !== 'undefined' && typeof console.info === 'function') {
|
|
32
|
+
console.info('The development server has disconnected.\nRefresh the page if necessary.');
|
|
33
|
+
}
|
|
34
|
+
}; // Remember some state related to hot module replacement.
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
let isFirstCompilation = true;
|
|
38
|
+
let mostRecentCompilationHash = null;
|
|
39
|
+
let hasCompileErrors = false;
|
|
40
|
+
|
|
41
|
+
function clearOutdatedErrors() {
|
|
42
|
+
// Clean up outdated compile errors, if any.
|
|
43
|
+
// eslint-disable-next-line node/no-unsupported-features/node-builtins, no-console
|
|
44
|
+
if (typeof console !== 'undefined' && typeof console.clear === 'function') {
|
|
45
|
+
if (hasCompileErrors) {
|
|
46
|
+
// eslint-disable-next-line node/no-unsupported-features/node-builtins, no-console
|
|
47
|
+
console.clear();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
} // Successful compilation.
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
function handleSuccess() {
|
|
54
|
+
clearOutdatedErrors();
|
|
55
|
+
const isHotUpdate = !isFirstCompilation;
|
|
56
|
+
isFirstCompilation = false;
|
|
57
|
+
hasCompileErrors = false; // Attempt to apply hot updates or reload.
|
|
58
|
+
|
|
59
|
+
if (isHotUpdate) {
|
|
60
|
+
tryApplyUpdates();
|
|
61
|
+
}
|
|
62
|
+
} // Compilation with warnings (e.g. ESLint).
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
function handleWarnings(warnings) {
|
|
66
|
+
clearOutdatedErrors();
|
|
67
|
+
const isHotUpdate = !isFirstCompilation;
|
|
68
|
+
isFirstCompilation = false;
|
|
69
|
+
hasCompileErrors = false;
|
|
70
|
+
|
|
71
|
+
function printWarnings() {
|
|
72
|
+
// Print warnings to the console.
|
|
73
|
+
const formatted = (0, _format.formatWebpackMessages)({
|
|
74
|
+
warnings,
|
|
75
|
+
errors: []
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
if (typeof console !== 'undefined' && typeof console.warn === 'function') {
|
|
79
|
+
for (let i = 0; i < formatted.warnings.length; i++) {
|
|
80
|
+
if (i === 5) {
|
|
81
|
+
console.warn('There were more warnings in other files.\n' + 'You can find a complete log in the terminal.');
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
console.warn((0, _stripAnsi.default)(formatted.warnings[i]));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
printWarnings(); // Attempt to apply hot updates or reload.
|
|
91
|
+
|
|
92
|
+
if (isHotUpdate) {
|
|
93
|
+
tryApplyUpdates();
|
|
94
|
+
}
|
|
95
|
+
} // Compilation with errors (e.g. syntax error or missing modules).
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
function handleErrors(errors) {
|
|
99
|
+
clearOutdatedErrors();
|
|
100
|
+
isFirstCompilation = false;
|
|
101
|
+
hasCompileErrors = true; // "Massage" webpack messages.
|
|
102
|
+
|
|
103
|
+
const formatted = (0, _format.formatWebpackMessages)({
|
|
104
|
+
errors,
|
|
105
|
+
warnings: []
|
|
106
|
+
}); // Also log them to the console.
|
|
107
|
+
|
|
108
|
+
if (typeof console !== 'undefined' && typeof console.error === 'function') {
|
|
109
|
+
for (const error of formatted.errors) {
|
|
110
|
+
console.error((0, _stripAnsi.default)(error));
|
|
111
|
+
}
|
|
112
|
+
} // Do not attempt to reload now.
|
|
113
|
+
// We will reload on next success instead.
|
|
114
|
+
|
|
115
|
+
} // There is a newer version of the code available.
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
function handleAvailableHash(hash) {
|
|
119
|
+
// Update last known compilation hash.
|
|
120
|
+
mostRecentCompilationHash = hash;
|
|
121
|
+
} // Handle messages from the server.
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
connection.onmessage = function (e) {
|
|
125
|
+
const message = JSON.parse(e.data);
|
|
126
|
+
|
|
127
|
+
switch (message.type) {
|
|
128
|
+
case 'hash':
|
|
129
|
+
handleAvailableHash(message.data);
|
|
130
|
+
break;
|
|
131
|
+
|
|
132
|
+
case 'still-ok':
|
|
133
|
+
case 'ok':
|
|
134
|
+
handleSuccess();
|
|
135
|
+
break;
|
|
136
|
+
|
|
137
|
+
case 'content-changed':
|
|
138
|
+
// Triggered when a file from `contentBase` changed.
|
|
139
|
+
window.location.reload();
|
|
140
|
+
break;
|
|
141
|
+
|
|
142
|
+
case 'warnings':
|
|
143
|
+
handleWarnings(message.data);
|
|
144
|
+
break;
|
|
145
|
+
|
|
146
|
+
case 'errors':
|
|
147
|
+
handleErrors(message.data);
|
|
148
|
+
break;
|
|
149
|
+
|
|
150
|
+
default: // Do nothing.
|
|
151
|
+
|
|
152
|
+
}
|
|
153
|
+
}; // Is there a newer version of this code available?
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
function isUpdateAvailable() {
|
|
157
|
+
// __webpack_hash__ is the hash of the current compilation.
|
|
158
|
+
// It's a global variable injected by webpack.
|
|
159
|
+
return mostRecentCompilationHash !== __webpack_hash__;
|
|
160
|
+
} // webpack disallows updates in other states.
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
function canApplyUpdates() {
|
|
164
|
+
return module.hot.status() === 'idle';
|
|
165
|
+
} // Attempt to update code on the fly, fall back to a hard reload.
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
function tryApplyUpdates() {
|
|
169
|
+
if (!module.hot) {
|
|
170
|
+
// HotModuleReplacementPlugin is not in webpack configuration.
|
|
171
|
+
window.location.reload();
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (!isUpdateAvailable() || !canApplyUpdates()) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function handleApplyUpdates(err, updatedModules) {
|
|
180
|
+
// NOTE: This var is injected by Webpack's DefinePlugin, and is a boolean instead of string.
|
|
181
|
+
const hasReactRefresh = process.env.FAST_REFRESH;
|
|
182
|
+
const wantsForcedReload = err || !updatedModules || hadRuntimeError; // React refresh can handle hot-reloading over errors.
|
|
183
|
+
|
|
184
|
+
if (!hasReactRefresh && wantsForcedReload) {
|
|
185
|
+
window.location.reload();
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (isUpdateAvailable()) {
|
|
190
|
+
// While we were updating, there was a new update! Do it again.
|
|
191
|
+
tryApplyUpdates();
|
|
192
|
+
}
|
|
193
|
+
} // https://webpack.github.io/docs/hot-module-replacement.html#check
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
const result = module.hot.check(
|
|
197
|
+
/* autoApply */
|
|
198
|
+
true, handleApplyUpdates); // // webpack 2 returns a Promise instead of invoking a callback
|
|
199
|
+
|
|
200
|
+
if (result !== null && result !== void 0 && result.then) {
|
|
201
|
+
result.then(updatedModules => {
|
|
202
|
+
handleApplyUpdates(null, updatedModules);
|
|
203
|
+
}, err => {
|
|
204
|
+
handleApplyUpdates(err, null);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
@@ -220,7 +220,8 @@ class ModernDevServer extends _prodServer.ModernServer {
|
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
onServerChange({
|
|
223
|
-
filepath
|
|
223
|
+
filepath,
|
|
224
|
+
event
|
|
224
225
|
}) {
|
|
225
226
|
const {
|
|
226
227
|
pwd
|
|
@@ -239,9 +240,19 @@ class ModernDevServer extends _prodServer.ModernServer {
|
|
|
239
240
|
});
|
|
240
241
|
} else {
|
|
241
242
|
try {
|
|
242
|
-
|
|
243
|
-
filepath
|
|
244
|
-
|
|
243
|
+
const success = this.runner.onApiChange([{
|
|
244
|
+
filename: filepath,
|
|
245
|
+
event
|
|
246
|
+
}]); // onApiChange 钩子被调用,且返回 true,则表示无需重新编译
|
|
247
|
+
// onApiChange 的类型是 WaterFall,WaterFall 钩子的返回值类型目前有问题
|
|
248
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
249
|
+
// @ts-expect-error
|
|
250
|
+
|
|
251
|
+
if (success !== true) {
|
|
252
|
+
super.onServerChange({
|
|
253
|
+
filepath
|
|
254
|
+
});
|
|
255
|
+
}
|
|
245
256
|
} catch (e) {
|
|
246
257
|
this.logger.error(e);
|
|
247
258
|
}
|
|
@@ -381,11 +392,12 @@ class ModernDevServer extends _prodServer.ModernServer {
|
|
|
381
392
|
const watcher = new _watcher.default();
|
|
382
393
|
watcher.createDepTree(); // 监听文件变动,如果有变动则给 client,也就是 start 启动的插件发消息
|
|
383
394
|
|
|
384
|
-
watcher.listen(defaultWatchedPaths, watchOptions, filepath => {
|
|
395
|
+
watcher.listen(defaultWatchedPaths, watchOptions, (filepath, event) => {
|
|
385
396
|
watcher.updateDepTree();
|
|
386
397
|
watcher.cleanDepCache(filepath);
|
|
387
398
|
this.onServerChange({
|
|
388
|
-
filepath
|
|
399
|
+
filepath,
|
|
400
|
+
event
|
|
389
401
|
});
|
|
390
402
|
});
|
|
391
403
|
this.watcher = watcher;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FSWatcher, WatchOptions } from '@modern-js/utils';
|
|
2
|
+
export declare type WatchEvent = 'add' | 'change' | 'unlink';
|
|
2
3
|
export declare const defaultWatchOptions: {
|
|
3
4
|
ignoreInitial: boolean;
|
|
4
5
|
ignored: RegExp;
|
|
@@ -24,7 +25,7 @@ export declare const mergeWatchOptions: (options?: WatchOptions) => {
|
|
|
24
25
|
export default class Watcher {
|
|
25
26
|
private dependencyTree;
|
|
26
27
|
private watcher;
|
|
27
|
-
listen(files: string[], options: WatchOptions, callback: (changed: string) => void): void;
|
|
28
|
+
listen(files: string[], options: WatchOptions, callback: (changed: string, event: WatchEvent) => void): void;
|
|
28
29
|
createDepTree(): void;
|
|
29
30
|
updateDepTree(): void;
|
|
30
31
|
cleanDepCache(filepath: string): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/// <reference types="node" />
|
|
3
3
|
import http, { Server, IncomingMessage, ServerResponse } from 'http';
|
|
4
4
|
import { ServerHookRunner, ModernServer, BuildOptions } from '@modern-js/prod-server';
|
|
5
|
+
import { WatchEvent } from '../dev-tools/watcher';
|
|
5
6
|
import { ModernDevServerOptions } from '../types';
|
|
6
7
|
export declare class ModernDevServer extends ModernServer {
|
|
7
8
|
private mockHandler;
|
|
@@ -19,9 +20,11 @@ export declare class ModernDevServer extends ModernServer {
|
|
|
19
20
|
createHTTPServer(handler: (req: IncomingMessage, res: ServerResponse, next?: () => void) => void): Promise<http.Server | import("https").Server>;
|
|
20
21
|
protected warmupSSRBundle(): void;
|
|
21
22
|
protected onServerChange({
|
|
22
|
-
filepath
|
|
23
|
+
filepath,
|
|
24
|
+
event
|
|
23
25
|
}: {
|
|
24
26
|
filepath: string;
|
|
27
|
+
event: WatchEvent;
|
|
25
28
|
}): void;
|
|
26
29
|
private setupCompiler;
|
|
27
30
|
private setupDevServerPlugin;
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"modern",
|
|
12
12
|
"modern.js"
|
|
13
13
|
],
|
|
14
|
-
"version": "1.
|
|
14
|
+
"version": "1.6.0",
|
|
15
15
|
"jsnext:source": "./src/index.ts",
|
|
16
16
|
"types": "./dist/types/index.d.ts",
|
|
17
17
|
"main": "./dist/js/node/index.js",
|
|
@@ -30,20 +30,19 @@
|
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@babel/core": "^7.18.0",
|
|
32
32
|
"@babel/register": "^7.17.7",
|
|
33
|
-
"@modern-js/
|
|
34
|
-
"@modern-js/prod-server": "^1.1.9",
|
|
33
|
+
"@modern-js/prod-server": "^1.2.1",
|
|
35
34
|
"@modern-js/server-utils": "^1.2.11",
|
|
36
|
-
"@modern-js/webpack": "^1.
|
|
37
|
-
"@modern-js/utils": "^1.
|
|
35
|
+
"@modern-js/webpack": "^1.12.2",
|
|
36
|
+
"@modern-js/utils": "^1.8.0",
|
|
38
37
|
"devcert": "^1.2.2",
|
|
39
38
|
"minimatch": "^3.0.4",
|
|
40
39
|
"path-to-regexp": "^6.2.0",
|
|
41
40
|
"ws": "^8.2.0"
|
|
42
41
|
},
|
|
43
42
|
"devDependencies": {
|
|
44
|
-
"@modern-js/core": "1.
|
|
45
|
-
"@modern-js/server-core": "1.4.
|
|
46
|
-
"@modern-js/types": "1.
|
|
43
|
+
"@modern-js/core": "1.13.1",
|
|
44
|
+
"@modern-js/server-core": "1.4.1",
|
|
45
|
+
"@modern-js/types": "1.6.0",
|
|
47
46
|
"@scripts/build": "0.0.0",
|
|
48
47
|
"@scripts/jest-config": "0.0.0",
|
|
49
48
|
"@types/jest": "^27",
|
|
@@ -52,7 +51,8 @@
|
|
|
52
51
|
"@types/ws": "^7.4.7",
|
|
53
52
|
"jest": "^27",
|
|
54
53
|
"typescript": "^4",
|
|
55
|
-
"websocket": "^1"
|
|
54
|
+
"websocket": "^1",
|
|
55
|
+
"webpack": "^5.71.0"
|
|
56
56
|
},
|
|
57
57
|
"sideEffects": false,
|
|
58
58
|
"modernConfig": {
|