Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 3 additions & 2 deletions src/js/internal/inspector/cdp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,7 @@ class InspectorCDPAdapter {
endLine: params.endLine ?? 0,
endColumn: params.endColumn ?? 0,
});
const { scriptType } = params;
this.#emitToClient("Debugger.scriptParsed", {
scriptId: params.scriptId,
url: cdpUrl,
Expand All @@ -580,10 +581,10 @@ class InspectorCDPAdapter {
endColumn: params.endColumn ?? 0,
executionContextId: EXECUTION_CONTEXT_ID,
hash: "",
isModule: !!params.module,
isModule: scriptType === "module",
sourceMapURL: params.sourceMapURL,
embedderName: cdpUrl,
scriptLanguage: "JavaScript",
scriptLanguage: scriptType === "webassembly" ? "WebAssembly" : "JavaScript",
});
return;
}
Expand Down
73 changes: 73 additions & 0 deletions test/js/node/inspector/inspector.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,79 @@ export { after };
expect(await proc.exited).toBe(0);
});

// JSC's Debugger.scriptParsed classifies a script with scriptType ("program",
// "module" or "webassembly"); V8 clients read isModule and scriptLanguage. The
// fixture is its own CDP client: it enables the Debugger domain, then loads one
// script of each kind and prints what the adapter reported for them.
const scriptParsedFixture = `
import inspector from "node:inspector";

inspector.open(0, "127.0.0.1", false);
const ws = new WebSocket(inspector.url());
const pending = new Map();
const scripts = [];
let nextId = 1;
ws.onmessage = event => {
const message = JSON.parse(event.data);
if (message.id) {
pending.get(message.id)(message);
pending.delete(message.id);
} else if (message.method === "Debugger.scriptParsed") {
scripts.push(message.params);
}
};
const send = (method, params) =>
new Promise(resolve => {
const id = nextId++;
pending.set(id, resolve);
ws.send(JSON.stringify({ id, method, params }));
});
await new Promise(resolve => (ws.onopen = resolve));

await send("Debugger.enable", {});
await import("./esm.mjs");
await import("./lib.cjs");
// The empty module: just the wasm magic and version.
new WebAssembly.Module(new Uint8Array([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]));
// The backend answers commands through the same ordered queue it emits events
// on, so this reply arriving proves the scriptParsed events for the three
// scripts above have arrived too.
await send("Debugger.setBreakpointsActive", { active: true });
inspector.close();

console.log(
JSON.stringify(
scripts
.filter(({ url }) => /\\/esm\\.mjs$|\\/lib\\.cjs$|\\.wasm$/.test(url))
.map(({ url, isModule, scriptLanguage }) => ({ url, isModule, scriptLanguage })),
),
);
`;

test("Debugger.scriptParsed reports isModule and scriptLanguage from JSC's scriptType", async () => {
using dir = tempDir("inspector-script-parsed", {
"fixture.mjs": scriptParsedFixture,
"esm.mjs": `export const esm = true;\n`,
"lib.cjs": `module.exports = { cjs: true };\n`,
});

await using proc = Bun.spawn({
cmd: [bunExe(), "fixture.mjs"],
env: bunEnv,
cwd: String(dir),
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect({ stderrIfFailed: exitCode === 0 ? "" : stderr, exitCode }).toEqual({ stderrIfFailed: "", exitCode: 0 });

expect(JSON.parse(stdout.trim().split("\n").at(-1)!)).toEqual([
{ url: expect.stringMatching(/^file:\/\/.*\/esm\.mjs$/), isModule: true, scriptLanguage: "JavaScript" },
{ url: expect.stringMatching(/^file:\/\/.*\/lib\.cjs$/), isModule: false, scriptLanguage: "JavaScript" },
// JSC names a WebAssembly.Module compiled from bytes <n>.wasm itself.
{ url: expect.stringMatching(/\.wasm$/), isModule: false, scriptLanguage: "WebAssembly" },
]);
});

test("disconnect does not clobber a console method reassigned by user code", () => {
const session = new inspector.Session();
session.connect();
Expand Down
Loading