Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
a66dd0a
refactor: use post transfer module ids
stormslowly Jan 8, 2026
8149c31
test: skip lazy active method
stormslowly Jan 8, 2026
f8e3322
Merge remote-tracking branch 'origin/main' into refactor/lazy_active_…
stormslowly Jan 8, 2026
cef1dc7
test: add lazy post request help
stormslowly Jan 8, 2026
093bf25
test: add large module id container
stormslowly Jan 8, 2026
cf5349f
test: add case introduction
stormslowly Jan 9, 2026
b253637
refactor: remove about controller
stormslowly Jan 9, 2026
78d904c
Merge remote-tracking branch 'origin/main' into refactor/lazy_active_…
stormslowly Jan 9, 2026
0c136db
test: we are using post method now
stormslowly Jan 9, 2026
890aede
refactor: rename
stormslowly Jan 9, 2026
5ace8c0
Merge remote-tracking branch 'origin/main' into refactor/lazy_active_…
stormslowly Jan 12, 2026
f9f8d98
fix body parser (vibe-kanban 09797829)
stormslowly Jan 12, 2026
332146a
refactor read module ids from body (vibe-kanban a7874ac1)
stormslowly Jan 12, 2026
a0619ae
refactor: downgrading to lower web api
stormslowly Jan 13, 2026
f4f10d6
test: ✅ add lazy compilation active cors cases
stormslowly Jan 13, 2026
2ba3cd6
chore: update test case doc
stormslowly Jan 13, 2026
5d8eeb5
fix: we all need cors header no matter is simple request or not
stormslowly Jan 13, 2026
401d886
refactor:delete cors header setting
stormslowly Jan 14, 2026
363dfa5
refactor: set cors header should set by user
stormslowly Jan 14, 2026
3be7a91
Merge branch 'main' into refactor/lazy_active_module_id_goes_to_post_…
stormslowly Jan 14, 2026
bd739dd
Update packages/rspack/hot/lazy-compilation-web.js
stormslowly Jan 14, 2026
bfd543e
test: fix case name
stormslowly Jan 14, 2026
0487659
Merge branch 'refactor/lazy_active_module_id_goes_to_post_body' of gi…
stormslowly Jan 14, 2026
2064a1b
chore: api-extract update
stormslowly Jan 14, 2026
0c66e27
fix: memory leak of requst listeners
stormslowly Jan 14, 2026
5b2ff93
refactor: remove event source handle logic
stormslowly Jan 14, 2026
bf0fc1f
fix: jsdom XMLHTTPRequets need strict cors header
stormslowly Jan 14, 2026
c0078ad
refactor: node lazy compilation client use post too
stormslowly Jan 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ Rspack is a high-performance JavaScript bundler written in Rust that offers stro
- Update snapshots: `npm run test -- -u`
- Filter tests: `npm run test -- -t configCases/asset`

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.

## Debugging

- **VS Code**: `.vscode/launch.json` with `Debug Rspack` and `Attach` options
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ export class LazyCompilationTestPlugin {
resolve(null);
});
server.on('request', (req, res) => {
// Set CORS headers for jsdom's XMLHttpRequest
res.setHeader('Access-Control-Allow-Origin', '*');

middleware(req, res, () => {});
});
server.on('connection', (socket) => {
Expand Down
7 changes: 6 additions & 1 deletion packages/rspack-test-tools/src/runner/web/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,12 @@ export class WebRunner extends NodeRunner {
protected createBaseModuleScope() {
const moduleScope = super.createBaseModuleScope();
moduleScope.EventSource = EventSource;
moduleScope.fetch = async (url: string) => {
moduleScope.fetch = async (url: string, options: any) => {
// For Lazy Compilation Proxy the POST request to the real dev server.
if (options?.method === 'POST') {
return fetch(url, options as any);
}

try {
const filePath = this.urlToPath(url);
this.log(`fetch: ${url} -> ${filePath}`);
Expand Down
124 changes: 90 additions & 34 deletions packages/rspack/hot/lazy-compilation-node.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,79 @@
var urlBase = decodeURIComponent(__resourceQuery.slice(1));
var compiling = new Set();
var errorHandlers = new Set();

/** @type {import("http").ClientRequest | undefined} */
var pendingRequest;
/** @type {boolean} */
var hasPendingUpdate = false;

function sendRequest() {
if (compiling.size === 0) {
hasPendingUpdate = false;
return;
}

var modules = Array.from(compiling);
var data = modules.join('\n');

var httpModule = urlBase.startsWith('https')
? require('https')
: require('http');

var request = httpModule.request(
urlBase,
{
method: 'POST',
agent: false,
headers: {
'Content-Type': 'text/plain',
},
},
function (res) {
pendingRequest = undefined;
if (res.statusCode < 200 || res.statusCode >= 300) {
var error = new Error(
'Problem communicating active modules to the server: HTTP ' +
res.statusCode,
);
errorHandlers.forEach(function (onError) {
onError(error);
});
}
// Consume response data to free up memory
res.resume();
if (hasPendingUpdate) {
hasPendingUpdate = false;
sendRequest();
}
},
);

pendingRequest = request;

request.on('error', function (err) {
pendingRequest = undefined;
var error = new Error(
'Problem communicating active modules to the server: ' + err.message,
);
errorHandlers.forEach(function (onError) {
onError(error);
});
});

request.write(data);
request.end();
}

function sendActiveRequest() {
hasPendingUpdate = true;

// If no request is pending, start one
if (!pendingRequest) {
hasPendingUpdate = false;
sendRequest();
}
}

/**
* @param {{ data: string, onError: (err: Error) => void, active: boolean, module: module }} options options
Expand All @@ -9,42 +84,23 @@ exports.activate = function (options) {
var onError = options.onError;
var active = options.active;
var module = options.module;
/** @type {import("http").IncomingMessage} */
var response;
var request = (
urlBase.startsWith('https') ? require('https') : require('http')
).request(
urlBase + encodeURIComponent(data),
{
agent: false,
headers: { accept: 'text/event-stream' },
},
function (res) {
response = res;
response.on('error', errorHandler);
if (!active && !module.hot) {
console.log(
'Hot Module Replacement is not enabled. Waiting for process restart...',
);
}
},
);

/**
* @param {Error} err error
*/
function errorHandler(err) {
err.message =
'Problem communicating active modules to the server' +
(err.message ? ': ' + err.message : '') +
'\nRequest: ' +
urlBase +
data;
onError(err);
errorHandlers.add(onError);

if (!compiling.has(data)) {
compiling.add(data);
sendActiveRequest();
}
request.on('error', errorHandler);
request.end();

if (!active && !module.hot) {
console.log(
'Hot Module Replacement is not enabled. Waiting for process restart...',
);
}

return function () {
response.destroy();
errorHandlers.delete(onError);
compiling.delete(data);
sendActiveRequest();
};
};
96 changes: 61 additions & 35 deletions packages/rspack/hot/lazy-compilation-web.js
Original file line number Diff line number Diff line change
@@ -1,47 +1,73 @@
if (typeof EventSource !== 'function') {
if (typeof XMLHttpRequest === 'undefined') {
throw new Error(
"Environment doesn't support lazy compilation (requires EventSource)",
"Environment doesn't support lazy compilation (requires XMLHttpRequest)",
);
}

var urlBase = decodeURIComponent(__resourceQuery.slice(1));
/** @type {EventSource | undefined} */
var activeEventSource;
var compiling = new Set();
var errorHandlers = new Set();

var updateEventSource = function updateEventSource() {
if (activeEventSource) activeEventSource.close();
if (compiling.size) {
activeEventSource = new EventSource(
urlBase +
Array.from(compiling, function (module) {
return encodeURIComponent(module);
}).join('@'),
);
/**
* @this {EventSource}
* @param {Event & { message?: string, filename?: string, lineno?: number, colno?: number, error?: Error }} event event
*/
activeEventSource.onerror = function (event) {
errorHandlers.forEach(function (onError) {
onError(
new Error(
'Problem communicating active modules to the server' +
(event.message ? `: ${event.message} ` : '') +
(event.filename ? `: ${event.filename} ` : '') +
(event.lineno ? `: ${event.lineno} ` : '') +
(event.colno ? `: ${event.colno} ` : '') +
(event.error ? `: ${event.error}` : ''),
),
);
});
};
} else {
activeEventSource = undefined;
/** @type {XMLHttpRequest | undefined} */
var pendingXhr;
/** @type {boolean} */
var hasPendingUpdate = false;

var sendRequest = function sendRequest() {
if (compiling.size === 0) {
hasPendingUpdate = false;
return;
}

var modules = Array.from(compiling);
var data = modules.join('\n');

var xhr = new XMLHttpRequest();
pendingXhr = xhr;
xhr.open('POST', urlBase, true);
// text/plain Content-Type is simple request header
xhr.setRequestHeader('Content-Type', 'text/plain');

xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
pendingXhr = undefined;
if (xhr.status < 200 || xhr.status >= 300) {
var error = new Error(
'Problem communicating active modules to the server: HTTP ' +
xhr.status,
);
errorHandlers.forEach(function (onError) {
onError(error);
});
}
if (hasPendingUpdate) {
hasPendingUpdate = false;
sendRequest();
}
}
};

xhr.onerror = function () {
pendingXhr = undefined;
var error = new Error('Problem communicating active modules to the server');
errorHandlers.forEach(function (onError) {
onError(error);
});
};

xhr.send(data);
};

function sendActiveRequest() {
hasPendingUpdate = true;

// If no request is pending, start one
if (!pendingXhr) {
hasPendingUpdate = false;
sendRequest();
}
}

/**
* @param {{ data: string, onError: (err: Error) => void, active: boolean, module: module }} options options
* @returns {() => void} function to destroy response
Expand All @@ -55,7 +81,7 @@ exports.activate = function (options) {

if (!compiling.has(data)) {
compiling.add(data);
updateEventSource();
sendActiveRequest();
}

if (!active && !module.hot) {
Expand All @@ -67,6 +93,6 @@ exports.activate = function (options) {
return function () {
errorHandlers.delete(onError);
compiling.delete(data);
updateEventSource();
sendActiveRequest();
};
};
Loading
Loading