Skip to content

SWC generates incorrect source maps when typescript inputSourceMap is provided #10504

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
tough-griff opened this issue May 21, 2025 · 1 comment
Labels
Milestone

Comments

@tough-griff
Copy link

tough-griff commented May 21, 2025

Describe the bug

When handling code that has already been transformed by tsc with a generated source map swc will generate an incorrect source map that points to the incorrect locations in the original file.

Input code

src/typescript.ts

interface MyObject {
  [key: string]: any;
}

let foo = 'foo';
let boo = 'boo';
let bar = 'bar';
let baz = 'baz';

foo += bar;

eval("function hi(name) { return `Hi ${name}` } hi('world!')");

const key: string = 'key';
const obj: MyObject = Object.create(null)
const dynamic = 'somedynamickey';

switch (key) {
  case obj[dynamic]:
  case dynamic:
  case 'literal':
}

const abc = boo + baz;
const def = boo == baz;
const ghi = boo === baz;
const jkl = boo != baz;
const mno = boo !== baz;

const word = 'world';
const str = `hello ${word}`;

tsconfig.json

{
  "extends": "@tsconfig/node16/tsconfig.json",
  "compilerOptions": {
    "sourceMap": true,
    "outDir": "./dist",
    "rootDir": "./src",
  },
  "include": [
    "src/**/*.ts"
  ],
}

dist/typescript.js

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
let foo = 'foo';
let boo = 'boo';
let bar = 'bar';
let baz = 'baz';
foo += bar;
eval("function hi(name) { return `Hi ${name}` } hi('world!')");
const key = 'key';
const obj = Object.create(null);
const dynamic = 'somedynamickey';
switch (key) {
    case obj[dynamic]:
    case dynamic:
    case 'literal':
}
const abc = boo + baz;
const def = boo == baz;
const ghi = boo === baz;
const jkl = boo != baz;
const mno = boo !== baz;
const word = 'world';
const str = `hello ${word}`;
//# sourceMappingURL=typescript.js.map

dist/typescript.js.map

{"version":3,"file":"typescript.js","sourceRoot":"","sources":["../src/typescript.ts"],"names":[],"mappings":";;AAIA,IAAI,GAAG,GAAG,KAAK,CAAC;AAChB,IAAI,GAAG,GAAG,KAAK,CAAC;AAChB,IAAI,GAAG,GAAG,KAAK,CAAC;AAChB,IAAI,GAAG,GAAG,KAAK,CAAC;AAEhB,GAAG,IAAI,GAAG,CAAC;AAEX,IAAI,CAAC,wDAAwD,CAAC,CAAC;AAE/D,MAAM,GAAG,GAAW,KAAK,CAAC;AAC1B,MAAM,GAAG,GAAa,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;AACzC,MAAM,OAAO,GAAG,gBAAgB,CAAC;AAEjC,QAAQ,GAAG,EAAE,CAAC;IACZ,KAAK,GAAG,CAAC,OAAO,CAAC,CAAC;IAClB,KAAK,OAAO,CAAC;IACb,KAAK,SAAS,CAAC;AACjB,CAAC;AAED,MAAM,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AACtB,MAAM,GAAG,GAAG,GAAG,IAAI,GAAG,CAAC;AACvB,MAAM,GAAG,GAAG,GAAG,KAAK,GAAG,CAAC;AACxB,MAAM,GAAG,GAAG,GAAG,IAAI,GAAG,CAAC;AACvB,MAAM,GAAG,GAAG,GAAG,KAAK,GAAG,CAAC;AAExB,MAAM,IAAI,GAAG,OAAO,CAAC;AACrB,MAAM,GAAG,GAAG,SAAS,IAAI,EAAE,CAAC"}

Config

{
  "env": {
    "targets": {
      "node": "20",
    },
  },
  "jsc": { "minify": { "compress": { "defaults": false } } },
  "minify": true,
  "sourceMaps": true
}

Link to the code that reproduces this issue

https://gist.github.com/tough-griff/e300d5d8020d59b8d89ad94cf56175c3

SWC Info output

Operating System:
    Platform: darwin
    Arch: arm64
    Machine Type: arm64
    Version: Darwin Kernel Version 24.5.0: Tue Apr 22 19:54:49 PDT 2025; root:xnu-11417.121.6~2/RELEASE_ARM64_T6000
    CPU: (10 cores)
        Models: Apple M1 Max

Binaries:
    Node: 20.19.0
    npm: 10.8.2
    Yarn: N/A
    pnpm: N/A

Relevant Packages:
    @swc/core: 1.11.24
    @swc/helpers: N/A
    @swc/types: 0.1.21
    typescript: 5.8.3

SWC Config:
    output: N/A
    .swcrc path: N/A

Next.js info:
    output: N/A

Expected behavior

Image

Actual behavior

While the source code is not visible, you can still see the error: the generated source map does not seem to handle the newlines in the typescript source correctly and puts the first span from a newline on the previous code line:

Image

This leads to further errors when implementing a transform plugin that replaces operators with functions. The source map for the generated code points to the incorrect line previous line.

Version

@swc/core: 1.11.24

Additional context

typescript example
See: https://evanw.github.io/source-map-visualization/#NDc0ACJ1c2Ugc3RyaWN0IjtPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywiX19lc01vZHVsZSIse3ZhbHVlOnRydWV9KTtsZXQgZm9vPSJmb28iO2xldCBib289ImJvbyI7bGV0IGJhcj0iYmFyIjtsZXQgYmF6PSJiYXoiO2Zvbys9YmFyO2V2YWwoImZ1bmN0aW9uIGhpKG5hbWUpIHsgcmV0dXJuIGBIaSAke25hbWV9YCB9IGhpKCd3b3JsZCEnKSIpO2NvbnN0IGtleT0ia2V5Ijtjb25zdCBvYmo9T2JqZWN0LmNyZWF0ZShudWxsKTtjb25zdCBkeW5hbWljPSJzb21lZHluYW1pY2tleSI7c3dpdGNoKGtleSl7Y2FzZSBvYmpbZHluYW1pY106Y2FzZSBkeW5hbWljOmNhc2UibGl0ZXJhbCI6fWNvbnN0IGFiYz1ib28rYmF6O2NvbnN0IGRlZj1ib289PWJhejtjb25zdCBnaGk9Ym9vPT09YmF6O2NvbnN0IGprbD1ib28hPWJhejtjb25zdCBtbm89Ym9vIT09YmF6O2NvbnN0IHdvcmQ9IndvcmxkIjtjb25zdCBzdHI9YGhlbGxvICR7d29yZH1gOzgyMwB7InZlcnNpb24iOjMsImZpbGUiOiJ0eXBlc2NyaXB0LmpzIiwic291cmNlcyI6WyIuLi9zcmMvdHlwZXNjcmlwdC50cyJdLCJzb3VyY2VSb290IjoiIiwibmFtZXMiOltdLCJtYXBwaW5ncyI6InNFQUlBLElBQUksR0FBRyxDQUFHLEtBQUssQ0FBQyxBQUNoQixJQUFJLEdBQUcsQ0FBRyxLQUFLLENBQUMsQUFDaEIsSUFBSSxHQUFHLENBQUcsS0FBSyxDQUFDLEFBQ2hCLElBQUksR0FBRyxDQUFHLEtBQUssQ0FBQyxBQUVoQixHQUFHLEVBQUksR0FBRyxDQUFDLEFBRVgsSUFBSSxDQUFDLHdEQUF3RCxDQUFDLENBQUMsQUFFL0QsTUFBTSxHQUFHLENBQVcsS0FBSyxDQUN6QixBQUQwQixNQUNwQixHQUFHLENBQWEsTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQSxBQUN6QyxNQUFNLE9BQU8sQ0FBRyxnQkFBZ0IsQ0FFaEMsQUFGaUMsT0FFekIsR0FBRyxFQUFFLEFBQ1gsQ0FEWSxJQUNQLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQyxBQUNsQixLQUFLLE9BQU8sQ0FBQyxBQUNiLElBQUssU0FBUyxDQUNoQixBQURpQixDQUNoQixBQUVELE1BQU0sR0FBRyxDQUFHLEdBQUcsQ0FBRyxHQUFHLENBQUMsQUFDdEIsTUFBTSxHQUFHLENBQUcsR0FBRyxFQUFJLEdBQUcsQ0FBQyxBQUN2QixNQUFNLEdBQUcsQ0FBRyxHQUFHLEdBQUssR0FBRyxDQUFDLEFBQ3hCLE1BQU0sR0FBRyxDQUFHLEdBQUcsRUFBSSxHQUFHLENBQUMsQUFDdkIsTUFBTSxHQUFHLENBQUcsR0FBRyxHQUFLLEdBQUcsQ0FBQyxBQUV4QixNQUFNLElBQUksQ0FBRyxPQUFPLENBQUMsQUFDckIsTUFBTSxHQUFHLENBQUcsQ0FBQSxNQUFBLEVBQVMsSUFBSSxDQUFBLENBQUUsQ0FBQyJ9

javascript example
https://evanw.github.io/source-map-visualization/#NDE3ACJ1c2Ugc3RyaWN0IjtsZXQgZm9vPSJmb28iO2xldCBib289ImJvbyI7bGV0IGJhcj0iYmFyIjtsZXQgYmF6PSJiYXoiO2Zvbys9YmFyO2V2YWwoImZ1bmN0aW9uIGhpKG5hbWUpIHsgcmV0dXJuIGBIaSAke25hbWV9YCB9IGhpKCd3b3JsZCEnKSIpO2NvbnN0IGtleT0ia2V5Ijtjb25zdCBvYmo9T2JqZWN0LmNyZWF0ZShudWxsKTtjb25zdCBkeW5hbWljPSJzb21lZHluYW1pY2tleSI7c3dpdGNoKGtleSl7Y2FzZSBvYmpbZHluYW1pY106Y2FzZSBkeW5hbWljOmNhc2UibGl0ZXJhbCI6fWNvbnN0IGFiYz1ib28rYmF6O2NvbnN0IGRlZj1ib289PWJhejtjb25zdCBnaGk9Ym9vPT09YmF6O2NvbnN0IGprbD1ib28hPWJhejtjb25zdCBtbm89Ym9vIT09YmF6O2NvbnN0IHdvcmQ9IndvcmxkIjtjb25zdCBzdHI9YGhlbGxvICR7d29yZH1gOzExODgAeyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9Vc2Vycy9ncmlmZmluL0NvbnRyYXN0L25vZGUtc3djLXBsdWdpbnMvdGVzdC9zd2MtYnVnL3NyYy9qYXZhc2NyaXB0LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIid1c2Ugc3RyaWN0JztcblxubGV0IGZvbyA9ICdmb28nO1xubGV0IGJvbyA9ICdib28nO1xubGV0IGJhciA9ICdiYXInO1xubGV0IGJheiA9ICdiYXonO1xuXG5mb28gKz0gYmFyO1xuXG5ldmFsKFwiZnVuY3Rpb24gaGkobmFtZSkgeyByZXR1cm4gYEhpICR7bmFtZX1gIH0gaGkoJ3dvcmxkIScpXCIpO1xuXG5jb25zdCBrZXkgPSAna2V5JztcbmNvbnN0IG9iaiA9IE9iamVjdC5jcmVhdGUobnVsbCk7XG5jb25zdCBkeW5hbWljID0gJ3NvbWVkeW5hbWlja2V5Jztcblxuc3dpdGNoIChrZXkpIHtcbiAgY2FzZSBvYmpbZHluYW1pY106XG4gIGNhc2UgZHluYW1pYzpcbiAgY2FzZSAnbGl0ZXJhbCc6XG59XG5cbmNvbnN0IGFiYyA9IGJvbyArIGJhejtcbmNvbnN0IGRlZiA9IGJvbyA9PSBiYXo7XG5jb25zdCBnaGkgPSBib28gPT09IGJhejtcbmNvbnN0IGprbCA9IGJvbyAhPSBiYXo7XG5jb25zdCBtbm8gPSBib28gIT09IGJhejtcblxuY29uc3Qgd29yZCA9ICd3b3JsZCc7XG5jb25zdCBzdHIgPSBgaGVsbG8gJHt3b3JkfWA7XG4iXSwibmFtZXMiOlsiZm9vIiwiYm9vIiwiYmFyIiwiYmF6IiwiZXZhbCIsImtleSIsIm9iaiIsIk9iamVjdCIsImNyZWF0ZSIsImR5bmFtaWMiLCJhYmMiLCJkZWYiLCJnaGkiLCJqa2wiLCJtbm8iLCJ3b3JkIiwic3RyIl0sIm1hcHBpbmdzIjoiQUFBQSxhQUVBLElBQUlBLElBQU0sTUFDVixJQUFJQyxJQUFNLE1BQ1YsSUFBSUMsSUFBTSxNQUNWLElBQUlDLElBQU0sTUFFVkgsS0FBT0UsSUFFUEUsS0FBSywwREFFTCxNQUFNQyxJQUFNLE1BQ1osTUFBTUMsSUFBTUMsT0FBT0MsTUFBTSxDQUFDLE1BQzFCLE1BQU1DLFFBQVUsaUJBRWhCLE9BQVFKLEtBQ04sS0FBS0MsR0FBRyxDQUFDRyxRQUFRLENBQ2pCLEtBQUtBLFFBQ0wsSUFBSyxVQUNQLENBRUEsTUFBTUMsSUFBTVQsSUFBTUUsSUFDbEIsTUFBTVEsSUFBTVYsS0FBT0UsSUFDbkIsTUFBTVMsSUFBTVgsTUFBUUUsSUFDcEIsTUFBTVUsSUFBTVosS0FBT0UsSUFDbkIsTUFBTVcsSUFBTWIsTUFBUUUsSUFFcEIsTUFBTVksS0FBTyxRQUNiLE1BQU1DLElBQU0sQ0FBQyxNQUFNLEVBQUVELEtBQUssQ0FBQyJ9

@kdy1 kdy1 added this to the Planned milestone May 21, 2025
@tough-griff
Copy link
Author

The tests linked above pass, but that's because they're a slightly modified version of some tests we run in our own codebase which implement an internal plugin. When the plugin is enabled, which replaces certain operators with function calls, such as a += b => a = addAssign(a, b), the tests fail because the location of the generated code no longer matches that of the original source.

Here's an example of the correct source maps handling javascript code directly:

Image

And the incorrect source code when handling a file that has previously been generated by tsc:

Image

The source map visualizer doesn't show the code text, but you can see here that the replaced line is pointing to the previous newline (the end of the red span) instead of the line it really came from below (the green span)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

No branches or pull requests

2 participants