From 12bfcfa15f9004fd06f8b2aea4e177e23f5b14e0 Mon Sep 17 00:00:00 2001 From: Abdulaziz Ghuloum Date: Thu, 2 Jan 2025 20:52:27 +0300 Subject: [PATCH] sourcemaps now map properly in prettified code --- package-lock.json | 17 +++++++ package.json | 2 + src/library-manager.ts | 2 +- src/pprint.ts | 57 +++++++++++++++++++--- test-project/.rts/main.rts.json | 2 +- test-project/.rts/main.rts.ts | 9 +++- test-project/.rts/mod.rts.json | 2 +- test-project/.rts/mod.rts.ts | 7 ++- test-project/.rts/sourcemap1.test.rts.json | 2 +- test-project/.rts/sourcemap1.test.rts.ts | 12 ++++- 10 files changed, 96 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0ac1fce..2091ebd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,11 +11,13 @@ "@babel/code-frame": "^7.26.2", "@codemirror/lang-javascript": "^6.2.2", "@types/babel__code-frame": "^7.0.6", + "@types/diff": "^6.0.0", "@types/json-stable-stringify": "^1.1.0", "@types/node": "^22.10.2", "@uiw/codemirror-theme-abcdef": "^4.23.7", "@uiw/react-codemirror": "^4.23.7", "commander": "^12.1.0", + "diff": "^7.0.0", "ignore": "^6.0.2", "index-to-position": "^1.0.0", "js-base64": "^3.7.7", @@ -1582,6 +1584,12 @@ "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", "license": "MIT" }, + "node_modules/@types/diff": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@types/diff/-/diff-6.0.0.tgz", + "integrity": "sha512-dhVCYGv3ZSbzmQaBSagrv1WJ6rXCdkyTcDyoNu1MD8JohI7pR7k8wdZEm+mvdxRKXyHVwckFzWU1vJc+Z29MlA==", + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -2512,6 +2520,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", diff --git a/package.json b/package.json index a0ea3a0..f30990b 100644 --- a/package.json +++ b/package.json @@ -24,11 +24,13 @@ "@babel/code-frame": "^7.26.2", "@codemirror/lang-javascript": "^6.2.2", "@types/babel__code-frame": "^7.0.6", + "@types/diff": "^6.0.0", "@types/json-stable-stringify": "^1.1.0", "@types/node": "^22.10.2", "@uiw/codemirror-theme-abcdef": "^4.23.7", "@uiw/react-codemirror": "^4.23.7", "commander": "^12.1.0", + "diff": "^7.0.0", "ignore": "^6.0.2", "index-to-position": "^1.0.0", "js-base64": "^3.7.7", diff --git a/src/library-manager.ts b/src/library-manager.ts index 68d3e56..4f84e2f 100644 --- a/src/library-manager.ts +++ b/src/library-manager.ts @@ -21,7 +21,7 @@ import stringify from "json-stringify-pretty-compact"; import { init_global_context } from "./global-module"; import { parse_dts } from "./parse-dts"; -const cookie = "rewrite-ts-021"; +const cookie = "rewrite-ts-022"; type module_state = | { type: "initial" } diff --git a/src/pprint.ts b/src/pprint.ts index adc4192..dcd9276 100644 --- a/src/pprint.ts +++ b/src/pprint.ts @@ -8,6 +8,7 @@ import * as prettier_estree from "prettier/plugins/estree"; import { SourceMapGenerator } from "source-map"; import { Base64 } from "js-base64"; import { assert } from "./assert"; +import * as Diff from "diff"; type n = { val: string; src: source | false }; type ns = n | ns[]; @@ -202,7 +203,9 @@ async function add_src_map(code: string, ls: n[], options: map_options): Promise }); const map_string = srcmap.toString(); const base64 = Base64.encode(map_string); - return `${code}\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,${base64}\n`; + return ( + `${code}\n` + `//# sourceMappingURL=data:application/json;charset=utf-8;base64,${base64}\n` + ); } export async function pretty_print(code: string) { @@ -223,12 +226,54 @@ type options = { map?: map_options; }; +type d = { added: boolean; removed: boolean; value: string }; + +function remap(ls: n[], diffs: d[]): n[] { + const new_ls: n[] = []; + let i = 0, + j = 0; + while (i < diffs.length) { + const { added, removed, value } = diffs[i]; + if (added) { + if (removed) { + throw new Error("invalid diff"); + } else { + // new text added + new_ls.push({ src: false, val: value }); + } + } else { + if (removed) { + /* nothing added but stuff removed */ + let k = 0; + while (k < value.length) { + const item = ls[j]; + k += item.val.length; + j += 1; + } + assert(k === value.length); + } else { + /* nothing added and nothing removed */ + let k = 0; + while (k < value.length) { + const item = ls[j]; + new_ls.push(item); + k += item.val.length; + j += 1; + } + assert(k === value.length); + } + } + i += 1; + } + assert(j === ls.length); + return new_ls; +} + export async function pprint(loc: Loc, options: options): Promise { const ls = ns_flatten(loc_to_ns(loc)); const code = ls.map((x) => x.val).join(""); - return options.prettify - ? await pretty_print(code) - : options.map - ? add_src_map(code, ls, options.map) - : code; + const pretty = await pretty_print(code); + const diff = Diff.diffWordsWithSpace(code, pretty); + const pretty_ls = remap(ls, diff); + return options.map ? add_src_map(pretty, pretty_ls, options.map) : pretty; } diff --git a/test-project/.rts/main.rts.json b/test-project/.rts/main.rts.json index baa79b5..617dc10 100644 --- a/test-project/.rts/main.rts.json +++ b/test-project/.rts/main.rts.json @@ -1,6 +1,6 @@ { "cid": "test-project/main.rts rewrite-ts-visualized 0.0.0", - "cookie": "rewrite-ts-021", + "cookie": "rewrite-ts-022", "imports": [ { "pkg": {"name": "rewrite-ts-visualized", "version": "0.0.0"}, diff --git a/test-project/.rts/main.rts.ts b/test-project/.rts/main.rts.ts index 379024c..2a4a997 100644 --- a/test-project/.rts/main.rts.ts +++ b/test-project/.rts/main.rts.ts @@ -1,2 +1,7 @@ -import { type t_2 as t_2 , x_1 as x_3 , f_3 as f_4 } from "./mod.rts.ts" ; import { expect as expect_5 } from "vitest" ; export const y_1 : t_2 = ( x_3 + f_4 ) ; console . log ( expect_5 ) ; console . log ( y_1 ) ; -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL21haW4ucnRzIiwiLi4vbW9kLnJ0cyJdLCJuYW1lcyI6WyJ5IiwidCIsIngiLCJmIiwiY29uc29sZSIsImxvZyIsImV4cGVjdCJdLCJtYXBwaW5ncyI6ImdJQUVBLE1BQU1BLElBQUMsRUFBRUMsSUFBRSxJQUFFQyxJQUFFLEVDR3lCQyxRREZ4Q0MsUUFBTyxFQUFDQyxJQUFHLEVBQUNDLFNBQU0sSUFDbEJGLFFBQU8sRUFBQ0MsSUFBRyxFQUFDTCxJQUFDIiwiZmlsZSI6Im1haW4ucnRzLnRzIn0= +import { type t_2 as t_2, x_1 as x_3, f_3 as f_4 } from "./mod.rts.ts"; +import { expect as expect_5 } from "vitest"; +export const y_1: t_2 = x_3 + f_4; +console.log(expect_5); +console.log(y_1); + +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL21haW4ucnRzIiwiLi4vbW9kLnJ0cyJdLCJuYW1lcyI6WyJ5IiwidCIsIngiLCJmIiwiY29uc29sZSIsImxvZyIsImV4cGVjdCJdLCJtYXBwaW5ncyI6Ijs7T0FFQSxNQUFNQSxHQUFDLEVBQUVDLElBQUUsRUFBRUMsSUFBRSxFQ0d5QkM7QURGeENDLE9BQU8sQ0FBQ0MsR0FBRyxDQUFDQyxRQUFNO0FBQ2xCRixPQUFPLENBQUNDLEdBQUcsQ0FBQ0wsR0FBQyIsImZpbGUiOiJtYWluLnJ0cy50cyJ9 diff --git a/test-project/.rts/mod.rts.json b/test-project/.rts/mod.rts.json index 997a8c7..8eec97e 100644 --- a/test-project/.rts/mod.rts.json +++ b/test-project/.rts/mod.rts.json @@ -1,6 +1,6 @@ { "cid": "test-project/mod.rts rewrite-ts-visualized 0.0.0", - "cookie": "rewrite-ts-021", + "cookie": "rewrite-ts-022", "imports": [], "exported_identifiers": { "x": [ diff --git a/test-project/.rts/mod.rts.ts b/test-project/.rts/mod.rts.ts index a73863c..cc8548f 100644 --- a/test-project/.rts/mod.rts.ts +++ b/test-project/.rts/mod.rts.ts @@ -1,2 +1,5 @@ -export const x_1 = 12 ; export type t_2 = number ; export const f_3 = 13 ; -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL21vZC5ydHMiXSwibmFtZXMiOlsieCIsInQiLCJmIl0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLE1BQU1BLElBQUUsRUFBRSxHQUFFLEVBQ25CLE9BQU8sS0FBS0MsSUFBRSxFQUFFLE9BQU0sU0FFdEIsTUFBTUMsSUFBRSxFQUFFIiwiZmlsZSI6Im1vZC5ydHMudHMifQ== +export const x_1 = 12; +export type t_2 = number; +export const f_3 = 13; + +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL21vZC5ydHMiXSwibmFtZXMiOlsieCIsInQiLCJmIl0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLE1BQU1BLElBQUUsRUFBRSxFQUFFO0FBQ25CLE9BQU8sS0FBS0MsSUFBRSxFQUFFLE1BQU07T0FFdEIsTUFBTUMsSUFBRSxFQUFFIiwiZmlsZSI6Im1vZC5ydHMudHMifQ== diff --git a/test-project/.rts/sourcemap1.test.rts.json b/test-project/.rts/sourcemap1.test.rts.json index 3130a54..eade80d 100644 --- a/test-project/.rts/sourcemap1.test.rts.json +++ b/test-project/.rts/sourcemap1.test.rts.json @@ -1,6 +1,6 @@ { "cid": "test-project/sourcemap1.test.rts rewrite-ts-visualized 0.0.0", - "cookie": "rewrite-ts-021", + "cookie": "rewrite-ts-022", "imports": [ { "pkg": {"name": "vitest", "version": "2.1.8"}, diff --git a/test-project/.rts/sourcemap1.test.rts.ts b/test-project/.rts/sourcemap1.test.rts.ts index 87bbdc1..85e54a7 100644 --- a/test-project/.rts/sourcemap1.test.rts.ts +++ b/test-project/.rts/sourcemap1.test.rts.ts @@ -1,2 +1,10 @@ -import { suite as suite_1 , test as test_2 , expect as expect_5 } from "vitest" ; suite_1 ( "source mapping for errors" , ( ( ) => { test_2 ( "simple error" , ( ( ) => { const err_3 = new Error ( "HERE" ) ; const trace_4 = ( ( err_3 . stack || "" ) ) . split ( "\n" ) ; expect_5 ( trace_4 [ 1 ] ) . toMatch ( /\/sourcemap1\.test\.rts:5:17$/ ) ; } ) ) ; } ) ) ; -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NvdXJjZW1hcDEudGVzdC5ydHMiXSwibmFtZXMiOlsic3VpdGUiLCJ0ZXN0IiwiZXJyIiwiRXJyb3IiLCJ0cmFjZSIsInN0YWNrIiwic3BsaXQiLCJleHBlY3QiLCJ0b01hdGNoIl0sIm1hcHBpbmdzIjoia0ZBRUFBLFFBQUssRUFBQyw0QkFBMkIsSUFBRSxFQUFDLEVBQUUsR0FBRyxFQUN2Q0MsT0FBSSxFQUFDLGVBQWMsSUFBRSxFQUFDLEVBQUUsR0FBRyxFQUN6QixNQUFNQyxNQUFJLEVBQUUsSUFBSUMsTUFBSyxFQUFDLE9BQU0sSUFHNUIsTUFBTUMsUUFBTSxFQUFFLElBQUNGLE1BQUcsRUFBQ0csTUFBTSxHQUFHLEtBQUUsRUFBQyxFQUFDQyxNQUFLLEVBQUMsS0FBSSxJQUMxQ0MsU0FBTSxFQUFDSCxRQUFLLEVBQUMsRUFBQyxFQUFDLEVBQUMsRUFBQ0ksUUFBTyxFQUFDLGdDQUErQixJQUMxRCxJQUFDLElBQ0gsSUFBQyIsImZpbGUiOiJzb3VyY2VtYXAxLnRlc3QucnRzLnRzIn0= +import { suite as suite_1, test as test_2, expect as expect_5 } from "vitest"; +suite_1("source mapping for errors", () => { + test_2("simple error", () => { + const err_3 = new Error("HERE"); + const trace_4 = (err_3.stack || "").split("\n"); + expect_5(trace_4[1]).toMatch(/\/sourcemap1\.test\.rts:5:17$/); + }); +}); + +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NvdXJjZW1hcDEudGVzdC5ydHMiXSwibmFtZXMiOlsic3VpdGUiLCJ0ZXN0IiwiZXJyIiwiRXJyb3IiLCJ0cmFjZSIsInN0YWNrIiwic3BsaXQiLCJleHBlY3QiLCJ0b01hdGNoIl0sIm1hcHBpbmdzIjoiO0FBRUFBLE9BQUssQ0FBQywyQkFBMkIsR0FBRyxFQUFFLEdBQUc7RUFDdkNDLE1BQUksQ0FBQyxjQUFjLEdBQUcsRUFBRSxHQUFHO0lBQ3pCLE1BQU1DLE1BQUksRUFBRSxJQUFJQyxLQUFLLENBQUMsTUFBTTtJQUc1QixNQUFNQyxRQUFNLEVBQUUsQ0FBQ0YsS0FBRyxDQUFDRyxNQUFNLEdBQUcsR0FBRyxDQUFDQyxLQUFLLENBQUMsSUFBSTtJQUMxQ0MsUUFBTSxDQUFDSCxPQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQ0ksT0FBTyxDQUFDLCtCQUErQjtFQUMxRDtBQUNGIiwiZmlsZSI6InNvdXJjZW1hcDEudGVzdC5ydHMudHMifQ==