Skip to content

Commit ad662bc

Browse files
refactor(lazy-compilation): use POST request to transfer ids of active modules (#12678)
* refactor: use post transfer module ids * test: skip lazy active method * test: add lazy post request help * test: add large module id container * test: add case introduction * refactor: remove about controller * test: we are using post method now * refactor: rename * fix body parser (vibe-kanban 09797829) 1. 如果其他中间件一个解析好了body,并挂载在req.body 上的花就直直接使用 req.body 2. packages/rspack/src/builtin-plugin/lazy-compilation/middleware.ts body 解析的时候不能简单的使用 string 的拼接,需要考虑多字节符号的截断的问题。 * refactor read module ids from body (vibe-kanban a7874ac1) packages/rspack/src/builtin-plugin/lazy-compilation/middleware.ts 当req.body 非空时,直接假body中时一组 module ids,如果没有 body 自行拼接完,之后也 parse json string,返回 module ids 数组 * refactor: downgrading to lower web api * test: ✅ add lazy compilation active cors cases * chore: update test case doc * fix: we all need cors header no matter is simple request or not * refactor:delete cors header setting * refactor: set cors header should set by user * Update packages/rspack/hot/lazy-compilation-web.js Co-authored-by: neverland <chenjiahan.jait@bytedance.com> * test: fix case name * chore: api-extract update * fix: memory leak of requst listeners * refactor: remove event source handle logic * fix: jsdom XMLHTTPRequets need strict cors header * refactor: node lazy compilation client use post too --------- Co-authored-by: neverland <chenjiahan.jait@bytedance.com>
1 parent d9915de commit ad662bc

File tree

14 files changed

+548
-87
lines changed

14 files changed

+548
-87
lines changed

AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ Rspack is a high-performance JavaScript bundler written in Rust that offers stro
3636
- Update snapshots: `npm run test -- -u`
3737
- Filter tests: `npm run test -- -t configCases/asset`
3838

39+
Depends on what you have modified, you need to rebuild by `pnpm run build:js` or `pnpm run build:binding:dev` or `pnpm run build:cli:dev` first, then run testing commands to verify the modification.
40+
3941
## Debugging
4042

4143
- **VS Code**: `.vscode/launch.json` with `Debug Rspack` and `Attach` options

packages/rspack-test-tools/src/plugin/lazy-compilation-test-plugin.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ export class LazyCompilationTestPlugin {
3838
resolve(null);
3939
});
4040
server.on('request', (req, res) => {
41+
// Set CORS headers for jsdom's XMLHttpRequest
42+
res.setHeader('Access-Control-Allow-Origin', '*');
43+
4144
middleware(req, res, () => {});
4245
});
4346
server.on('connection', (socket) => {

packages/rspack-test-tools/src/runner/web/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,12 @@ export class WebRunner extends NodeRunner {
175175
protected createBaseModuleScope() {
176176
const moduleScope = super.createBaseModuleScope();
177177
moduleScope.EventSource = EventSource;
178-
moduleScope.fetch = async (url: string) => {
178+
moduleScope.fetch = async (url: string, options: any) => {
179+
// For Lazy Compilation Proxy the POST request to the real dev server.
180+
if (options?.method === 'POST') {
181+
return fetch(url, options as any);
182+
}
183+
179184
try {
180185
const filePath = this.urlToPath(url);
181186
this.log(`fetch: ${url} -> ${filePath}`);
Lines changed: 90 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,79 @@
11
var urlBase = decodeURIComponent(__resourceQuery.slice(1));
2+
var compiling = new Set();
3+
var errorHandlers = new Set();
4+
5+
/** @type {import("http").ClientRequest | undefined} */
6+
var pendingRequest;
7+
/** @type {boolean} */
8+
var hasPendingUpdate = false;
9+
10+
function sendRequest() {
11+
if (compiling.size === 0) {
12+
hasPendingUpdate = false;
13+
return;
14+
}
15+
16+
var modules = Array.from(compiling);
17+
var data = modules.join('\n');
18+
19+
var httpModule = urlBase.startsWith('https')
20+
? require('https')
21+
: require('http');
22+
23+
var request = httpModule.request(
24+
urlBase,
25+
{
26+
method: 'POST',
27+
agent: false,
28+
headers: {
29+
'Content-Type': 'text/plain',
30+
},
31+
},
32+
function (res) {
33+
pendingRequest = undefined;
34+
if (res.statusCode < 200 || res.statusCode >= 300) {
35+
var error = new Error(
36+
'Problem communicating active modules to the server: HTTP ' +
37+
res.statusCode,
38+
);
39+
errorHandlers.forEach(function (onError) {
40+
onError(error);
41+
});
42+
}
43+
// Consume response data to free up memory
44+
res.resume();
45+
if (hasPendingUpdate) {
46+
hasPendingUpdate = false;
47+
sendRequest();
48+
}
49+
},
50+
);
51+
52+
pendingRequest = request;
53+
54+
request.on('error', function (err) {
55+
pendingRequest = undefined;
56+
var error = new Error(
57+
'Problem communicating active modules to the server: ' + err.message,
58+
);
59+
errorHandlers.forEach(function (onError) {
60+
onError(error);
61+
});
62+
});
63+
64+
request.write(data);
65+
request.end();
66+
}
67+
68+
function sendActiveRequest() {
69+
hasPendingUpdate = true;
70+
71+
// If no request is pending, start one
72+
if (!pendingRequest) {
73+
hasPendingUpdate = false;
74+
sendRequest();
75+
}
76+
}
277

378
/**
479
* @param {{ data: string, onError: (err: Error) => void, active: boolean, module: module }} options options
@@ -9,42 +84,23 @@ exports.activate = function (options) {
984
var onError = options.onError;
1085
var active = options.active;
1186
var module = options.module;
12-
/** @type {import("http").IncomingMessage} */
13-
var response;
14-
var request = (
15-
urlBase.startsWith('https') ? require('https') : require('http')
16-
).request(
17-
urlBase + encodeURIComponent(data),
18-
{
19-
agent: false,
20-
headers: { accept: 'text/event-stream' },
21-
},
22-
function (res) {
23-
response = res;
24-
response.on('error', errorHandler);
25-
if (!active && !module.hot) {
26-
console.log(
27-
'Hot Module Replacement is not enabled. Waiting for process restart...',
28-
);
29-
}
30-
},
31-
);
3287

33-
/**
34-
* @param {Error} err error
35-
*/
36-
function errorHandler(err) {
37-
err.message =
38-
'Problem communicating active modules to the server' +
39-
(err.message ? ': ' + err.message : '') +
40-
'\nRequest: ' +
41-
urlBase +
42-
data;
43-
onError(err);
88+
errorHandlers.add(onError);
89+
90+
if (!compiling.has(data)) {
91+
compiling.add(data);
92+
sendActiveRequest();
4493
}
45-
request.on('error', errorHandler);
46-
request.end();
94+
95+
if (!active && !module.hot) {
96+
console.log(
97+
'Hot Module Replacement is not enabled. Waiting for process restart...',
98+
);
99+
}
100+
47101
return function () {
48-
response.destroy();
102+
errorHandlers.delete(onError);
103+
compiling.delete(data);
104+
sendActiveRequest();
49105
};
50106
};
Lines changed: 61 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,73 @@
1-
if (typeof EventSource !== 'function') {
1+
if (typeof XMLHttpRequest === 'undefined') {
22
throw new Error(
3-
"Environment doesn't support lazy compilation (requires EventSource)",
3+
"Environment doesn't support lazy compilation (requires XMLHttpRequest)",
44
);
55
}
66

77
var urlBase = decodeURIComponent(__resourceQuery.slice(1));
8-
/** @type {EventSource | undefined} */
9-
var activeEventSource;
108
var compiling = new Set();
119
var errorHandlers = new Set();
1210

13-
var updateEventSource = function updateEventSource() {
14-
if (activeEventSource) activeEventSource.close();
15-
if (compiling.size) {
16-
activeEventSource = new EventSource(
17-
urlBase +
18-
Array.from(compiling, function (module) {
19-
return encodeURIComponent(module);
20-
}).join('@'),
21-
);
22-
/**
23-
* @this {EventSource}
24-
* @param {Event & { message?: string, filename?: string, lineno?: number, colno?: number, error?: Error }} event event
25-
*/
26-
activeEventSource.onerror = function (event) {
27-
errorHandlers.forEach(function (onError) {
28-
onError(
29-
new Error(
30-
'Problem communicating active modules to the server' +
31-
(event.message ? `: ${event.message} ` : '') +
32-
(event.filename ? `: ${event.filename} ` : '') +
33-
(event.lineno ? `: ${event.lineno} ` : '') +
34-
(event.colno ? `: ${event.colno} ` : '') +
35-
(event.error ? `: ${event.error}` : ''),
36-
),
37-
);
38-
});
39-
};
40-
} else {
41-
activeEventSource = undefined;
11+
/** @type {XMLHttpRequest | undefined} */
12+
var pendingXhr;
13+
/** @type {boolean} */
14+
var hasPendingUpdate = false;
15+
16+
var sendRequest = function sendRequest() {
17+
if (compiling.size === 0) {
18+
hasPendingUpdate = false;
19+
return;
4220
}
21+
22+
var modules = Array.from(compiling);
23+
var data = modules.join('\n');
24+
25+
var xhr = new XMLHttpRequest();
26+
pendingXhr = xhr;
27+
xhr.open('POST', urlBase, true);
28+
// text/plain Content-Type is simple request header
29+
xhr.setRequestHeader('Content-Type', 'text/plain');
30+
31+
xhr.onreadystatechange = function () {
32+
if (xhr.readyState === 4) {
33+
pendingXhr = undefined;
34+
if (xhr.status < 200 || xhr.status >= 300) {
35+
var error = new Error(
36+
'Problem communicating active modules to the server: HTTP ' +
37+
xhr.status,
38+
);
39+
errorHandlers.forEach(function (onError) {
40+
onError(error);
41+
});
42+
}
43+
if (hasPendingUpdate) {
44+
hasPendingUpdate = false;
45+
sendRequest();
46+
}
47+
}
48+
};
49+
50+
xhr.onerror = function () {
51+
pendingXhr = undefined;
52+
var error = new Error('Problem communicating active modules to the server');
53+
errorHandlers.forEach(function (onError) {
54+
onError(error);
55+
});
56+
};
57+
58+
xhr.send(data);
4359
};
4460

61+
function sendActiveRequest() {
62+
hasPendingUpdate = true;
63+
64+
// If no request is pending, start one
65+
if (!pendingXhr) {
66+
hasPendingUpdate = false;
67+
sendRequest();
68+
}
69+
}
70+
4571
/**
4672
* @param {{ data: string, onError: (err: Error) => void, active: boolean, module: module }} options options
4773
* @returns {() => void} function to destroy response
@@ -55,7 +81,7 @@ exports.activate = function (options) {
5581

5682
if (!compiling.has(data)) {
5783
compiling.add(data);
58-
updateEventSource();
84+
sendActiveRequest();
5985
}
6086

6187
if (!active && !module.hot) {
@@ -67,6 +93,6 @@ exports.activate = function (options) {
6793
return function () {
6894
errorHandlers.delete(onError);
6995
compiling.delete(data);
70-
updateEventSource();
96+
sendActiveRequest();
7197
};
7298
};

0 commit comments

Comments
 (0)