diff options
author | 2023-08-23 22:55:18 -0700 | |
---|---|---|
committer | 2023-08-24 20:11:20 -0700 | |
commit | 526f7d85416b7310a1fa7b9886f8bb645df3695c (patch) | |
tree | 67e011981486fbe2668561881e7260cd38d1cecf /packages/bun-debug-adapter-protocol | |
parent | df6cca0fc26521fb0b4c5fd6dfdc16c0b7eca478 (diff) | |
download | bun-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.ts | 101 | ||||
-rw-r--r-- | packages/bun-debug-adapter-protocol/debugger/sourcemap.ts | 78 | ||||
-rw-r--r-- | packages/bun-debug-adapter-protocol/tsconfig.json | 2 |
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"] } |