aboutsummaryrefslogtreecommitdiff
path: root/packages/bun-debug-adapter-protocol
diff options
context:
space:
mode:
authorGravatar Ashcon Partovi <ashcon@partovi.net> 2023-08-23 22:55:18 -0700
committerGravatar Ashcon Partovi <ashcon@partovi.net> 2023-08-24 20:11:20 -0700
commit526f7d85416b7310a1fa7b9886f8bb645df3695c (patch)
tree67e011981486fbe2668561881e7260cd38d1cecf /packages/bun-debug-adapter-protocol
parentdf6cca0fc26521fb0b4c5fd6dfdc16c0b7eca478 (diff)
downloadbun-526f7d85416b7310a1fa7b9886f8bb645df3695c.tar.gz
bun-526f7d85416b7310a1fa7b9886f8bb645df3695c.tar.zst
bun-526f7d85416b7310a1fa7b9886f8bb645df3695c.zip
Fix source maps partly
Diffstat (limited to 'packages/bun-debug-adapter-protocol')
-rw-r--r--packages/bun-debug-adapter-protocol/debugger/adapter.ts101
-rw-r--r--packages/bun-debug-adapter-protocol/debugger/sourcemap.ts78
-rw-r--r--packages/bun-debug-adapter-protocol/tsconfig.json2
3 files changed, 117 insertions, 64 deletions
diff --git a/packages/bun-debug-adapter-protocol/debugger/adapter.ts b/packages/bun-debug-adapter-protocol/debugger/adapter.ts
index 32bbf0670..6af7d9ea6 100644
--- a/packages/bun-debug-adapter-protocol/debugger/adapter.ts
+++ b/packages/bun-debug-adapter-protocol/debugger/adapter.ts
@@ -1,4 +1,5 @@
import type { DAP } from "..";
+// @ts-ignore: FIXME - there is something wrong with the types
import type { JSC, InspectorListener } from "../../bun-inspector-protocol";
import { WebSocketInspector } from "../../bun-inspector-protocol";
import type { ChildProcess } from "node:child_process";
@@ -7,6 +8,10 @@ import capabilities from "./capabilities";
import { SourceMap } from "./sourcemap";
import { compare, parse } from "semver";
+type InitializeRequest = DAP.InitializeRequest & {
+ supportsConfigurationDoneRequest?: boolean;
+};
+
type LaunchRequest = DAP.LaunchRequest & {
runtime?: string;
program?: string;
@@ -90,6 +95,7 @@ export class DebugAdapter implements IDebugAdapter, InspectorListener {
#functionBreakpoints: Map<string, FunctionBreakpoint>;
#variables: (Variable | Variable[])[];
#process?: ChildProcess;
+ #initialized?: InitializeRequest;
#terminated?: boolean;
constructor({ sendToAdapter }: DebugAdapterOptions) {
@@ -115,6 +121,7 @@ export class DebugAdapter implements IDebugAdapter, InspectorListener {
this.#breakpoints.length = 0;
this.#functionBreakpoints.clear();
this.#variables.length = 1;
+ this.#initialized = undefined;
}
/**
@@ -207,8 +214,8 @@ export class DebugAdapter implements IDebugAdapter, InspectorListener {
});
}
- async initialize(request: DAP.InitializeRequest): Promise<DAP.InitializeResponse> {
- const { clientID, supportsConfigurationDoneRequest } = request as any;
+ async initialize(request: InitializeRequest): Promise<DAP.InitializeResponse> {
+ const { clientID, supportsConfigurationDoneRequest } = (this.#initialized = request);
this.#send("Inspector.enable");
this.#send("Runtime.enable");
@@ -264,7 +271,7 @@ export class DebugAdapter implements IDebugAdapter, InspectorListener {
throw new Error("Program must be a JavaScript or TypeScript file.");
}
- const subprocess = spawn(runtime, ["--inspect-wait", "--inspect=0", ...args, program], {
+ const subprocess = spawn(runtime, ["--inspect-wait=0", ...args, program], {
stdio: ["ignore", "pipe", "pipe", "pipe"],
cwd,
env: inheritEnv ? { ...process.env, ...env } : env,
@@ -502,7 +509,11 @@ export class DebugAdapter implements IDebugAdapter, InspectorListener {
#generatedLocation(source: Source, line?: number, column?: number): JSC.Debugger.Location {
const { sourceMap, scriptId, path } = source;
- const { line: line0, column: column0 } = sourceMap.generatedPosition(line, column, path);
+ const { line: line0, column: column0 } = sourceMap.generatedLocation(
+ this.#lineTo0BasedLine(line),
+ this.#columnTo0BasedColumn(column),
+ path,
+ );
return {
scriptId,
@@ -511,6 +522,20 @@ export class DebugAdapter implements IDebugAdapter, InspectorListener {
};
}
+ #lineTo0BasedLine(line?: number): number {
+ if (this.#initialized?.linesStartAt1) {
+ return line ? line - 1 : 0;
+ }
+ return line ?? 0;
+ }
+
+ #columnTo0BasedColumn(column?: number): number {
+ if (this.#initialized?.columnsStartAt1) {
+ return column ? column - 1 : 0;
+ }
+ return column ?? 0;
+ }
+
#originalLocation(
source: Source,
line?: number | JSC.Debugger.Location,
@@ -523,14 +548,28 @@ export class DebugAdapter implements IDebugAdapter, InspectorListener {
}
const { sourceMap } = source;
- const { line: line0, column: column0 } = sourceMap.originalPosition(line, column);
+ const { line: line0, column: column0 } = sourceMap.originalLocation(line, column);
return {
- line: line0,
- column: column0,
+ line: this.#lineFrom0BasedLine(line0),
+ column: this.#columnFrom0BasedColumn(column0),
};
}
+ #lineFrom0BasedLine(line?: number): number {
+ if (this.#initialized?.linesStartAt1) {
+ return line ? line + 1 : 1;
+ }
+ return line ?? 0;
+ }
+
+ #columnFrom0BasedColumn(column?: number): number {
+ if (this.#initialized?.columnsStartAt1) {
+ return column ? column + 1 : 1;
+ }
+ return column ?? 0;
+ }
+
async setBreakpoints(request: DAP.SetBreakpointsRequest): Promise<DAP.SetBreakpointsResponse> {
const { source: source0, breakpoints: requests } = request;
const sourceId = sourceToId(source0);
@@ -853,12 +892,6 @@ export class DebugAdapter implements IDebugAdapter, InspectorListener {
}
async ["Debugger.scriptParsed"](event: JSC.Debugger.ScriptParsedEvent): Promise<void> {
- // HACK: remove once Bun starts sending correct source map urls
- if (event.url && event.url.startsWith("/") && event.url.endsWith(".ts")) {
- event.sourceMapURL = generateSourceMapUrl(event.url);
- } else {
- event.sourceMapURL = undefined;
- }
const { url, scriptId, sourceMapURL } = event;
// If no url is present, the script is from a `evaluate` request.
@@ -966,10 +999,9 @@ export class DebugAdapter implements IDebugAdapter, InspectorListener {
["Console.messageAdded"](event: JSC.Console.MessageAddedEvent): void {
const { message } = event;
- const { type, text, parameters, line, column, stackTrace } = message;
+ const { type, level, text, parameters, line, column, stackTrace } = message;
let output: string;
- let isError: boolean | undefined;
let variablesReference: number | undefined;
if (parameters?.length) {
@@ -978,9 +1010,8 @@ export class DebugAdapter implements IDebugAdapter, InspectorListener {
const variables = parameters.map((parameter, i) => {
const variable = this.#addVariable(parameter, { name: `${i}` });
- const { value, type } = variable;
+ const { value } = variable;
output += value + " ";
- isError ||= type === "error";
return variable;
});
@@ -999,6 +1030,11 @@ export class DebugAdapter implements IDebugAdapter, InspectorListener {
output += "\n";
}
+ const color = consoleLevelToAnsiColor(level);
+ if (color) {
+ output = `${color}${output}`;
+ }
+
if (variablesReference) {
variablesReference = this.#setVariable([
{
@@ -1014,13 +1050,13 @@ export class DebugAdapter implements IDebugAdapter, InspectorListener {
if (stackTrace) {
const { callFrames } = stackTrace;
if (callFrames.length) {
- const [{ scriptId }] = callFrames.slice(0, -1);
+ const { scriptId } = callFrames.at(-1)!;
source = this.#getSourceIfPresent(scriptId);
}
}
this.#emit("output", {
- category: isError ? "stderr" : "debug console",
+ category: "debug console",
group: consoleMessageGroup(type),
output,
variablesReference,
@@ -1680,21 +1716,18 @@ function variablesSortBy(a: DAP.Variable, b: DAP.Variable): number {
return 0;
}
-// HACK: this will be removed once Bun starts sending source maps
-// with the `Debugger.scriptParsed` event.
-function generateSourceMapUrl(path: string): string | undefined {
- const { stdout } = spawnSync("bunx", ["esbuild", path, "--sourcemap=inline"], {
- stdio: "pipe",
- encoding: "utf-8",
- });
- const match = /sourceMappingURL=(.*)/im.exec(stdout);
- if (!match) {
- return undefined;
- }
- const [_, sourceMapUrl] = match;
- return sourceMapUrl;
-}
-
function isSameLocation(a: { line?: number; column?: number }, b: { line?: number; column?: number }): boolean {
return (a.line === b.line || (!a.line && !b.line)) && (a.column === b.column || (!a.column && !b.column));
}
+
+function consoleLevelToAnsiColor(level: JSC.Console.ConsoleMessage["level"]): string | undefined {
+ switch (level) {
+ case "warning":
+ return "\u001b[33m";
+ case "error":
+ return "\u001b[31m";
+ case "debug":
+ return "\u001b[36m";
+ }
+ return undefined;
+}
diff --git a/packages/bun-debug-adapter-protocol/debugger/sourcemap.ts b/packages/bun-debug-adapter-protocol/debugger/sourcemap.ts
index 1e04e56f4..eeceb520f 100644
--- a/packages/bun-debug-adapter-protocol/debugger/sourcemap.ts
+++ b/packages/bun-debug-adapter-protocol/debugger/sourcemap.ts
@@ -1,13 +1,13 @@
import { SourceMapConsumer } from "source-map-js";
-export type Position = {
+export type Location = {
line: number;
column: number;
};
export interface SourceMap {
- generatedPosition(line?: number, column?: number, url?: string): Position;
- originalPosition(line?: number, column?: number): Position;
+ generatedLocation(line?: number, column?: number, url?: string): Location;
+ originalLocation(line?: number, column?: number): Location;
}
class ActualSourceMap implements SourceMap {
@@ -16,8 +16,7 @@ class ActualSourceMap implements SourceMap {
constructor(sourceMap: SourceMapConsumer) {
this.#sourceMap = sourceMap;
- // @ts-ignore
- this.#sources = sourceMap._absoluteSources;
+ this.#sources = (sourceMap as any)._absoluteSources;
}
#getSource(url?: string): string {
@@ -36,61 +35,61 @@ class ActualSourceMap implements SourceMap {
return "";
}
- generatedPosition(line?: number, column?: number, url?: string): Position {
+ generatedLocation(line?: number, column?: number, url?: string): Location {
try {
const source = this.#getSource(url);
const { line: gline, column: gcolumn } = this.#sourceMap.generatedPositionFor({
- line: line ?? 0,
- column: column ?? 0,
+ line: lineTo1BasedLine(line),
+ column: columnToColumn(column),
source,
});
console.log(`[sourcemap] -->`, { source, url, line, column }, { gline, gcolumn });
return {
- line: gline || 0,
- column: gcolumn || 0,
+ line: lineTo0BasedLine(gline),
+ column: columnToColumn(gcolumn),
};
} catch (error) {
- console.error(error);
+ console.warn(error);
return {
- line: line || 0,
- column: column || 0,
+ line: lineToLine(line),
+ column: columnToColumn(column),
};
}
}
- originalPosition(line?: number, column?: number): Position {
+ originalLocation(line?: number, column?: number): Location {
try {
const { line: oline, column: ocolumn } = this.#sourceMap.originalPositionFor({
- line: line ?? 0,
- column: column ?? 0,
+ line: lineTo1BasedLine(line),
+ column: columnToColumn(column),
});
console.log(`[sourcemap] <--`, { line, column }, { oline, ocolumn });
return {
- line: oline || 0,
- column: ocolumn || 0,
+ line: lineTo0BasedLine(oline),
+ column: columnToColumn(ocolumn),
};
} catch (error) {
- console.error(error);
+ console.warn(error);
return {
- line: line || 0,
- column: column || 0,
+ line: lineToLine(line),
+ column: columnToColumn(column),
};
}
}
}
class NoopSourceMap implements SourceMap {
- generatedPosition(line?: number, column?: number, url?: string): Position {
+ generatedLocation(line?: number, column?: number, url?: string): Location {
return {
- line: line ?? 0,
- column: column ?? 0,
+ line: lineToLine(line),
+ column: columnToColumn(column),
};
}
- originalPosition(line?: number, column?: number): Position {
+ originalLocation(line?: number, column?: number): Location {
return {
- line: line ?? 0,
- column: column ?? 0,
+ line: lineToLine(line),
+ column: columnToColumn(column),
};
}
}
@@ -103,11 +102,32 @@ export function SourceMap(url?: string): SourceMap {
}
try {
const [_, base64] = url.split(",", 2);
- const decoded = Buffer.from(base64, "base64").toString("utf8");
- const sourceMap = new SourceMapConsumer(JSON.parse(decoded));
+ const decoded = Buffer.from(base64, "base64url").toString("utf8");
+ const schema = JSON.parse(decoded);
+ // HACK: Bun is sometimes sending invalid mappings
+ try {
+ schema.mappings = schema.mappings.replace(/[^a-z,;]/gi, "").slice(1);
+ } catch {}
+ const sourceMap = new SourceMapConsumer(schema);
return new ActualSourceMap(sourceMap);
} catch (error) {
console.warn("Failed to parse source map URL", url);
}
return defaultSourceMap;
}
+
+function lineTo1BasedLine(line?: number): number {
+ return line ? line + 1 : 1;
+}
+
+function lineTo0BasedLine(line?: number): number {
+ return line ? line - 1 : 0;
+}
+
+function lineToLine(line?: number): number {
+ return line ?? 0;
+}
+
+function columnToColumn(column?: number): number {
+ return column ?? 0;
+}
diff --git a/packages/bun-debug-adapter-protocol/tsconfig.json b/packages/bun-debug-adapter-protocol/tsconfig.json
index 443a2dc02..bada4ff8f 100644
--- a/packages/bun-debug-adapter-protocol/tsconfig.json
+++ b/packages/bun-debug-adapter-protocol/tsconfig.json
@@ -18,5 +18,5 @@
"types": ["bun-types"],
"outDir": "dist",
},
- "include": [".", "../bun-types/index.d.ts"]
+ "include": [".", "../bun-types/index.d.ts", "../bun-inspector-protocol/index"]
}