svelte: Source map line numbers are wrong when using typescript

Describe the bug When using typescript within .svelte files, the generated source map references the wrong line number, it is too low.

To Reproduce

  • Checkout the template and install libraries:
npx degit sveltejs/template svelte-typescript-app
cd svelte-typescript-app
npm install
  • Modify the script in src/App.svelte like this:
<script>
    export let name;

    // test comment
    console.error("error logged from line five in App.svelte")
</script>
  • Run the app and visit it in a browser, the following message is in the JavaScript console:
error logged from line five in App.svelte     App.svelte:5
  • Now add TypeScript support according to the instructions in the blog:
node scripts/setupTypeScript.js
npm install
  • Mark the script in src/App.svelte as TypeScript:
<script lang="ts">
    export let name;

    // test comment
    console.error("error logged from line five in App.svelte")
</script>
  • Run the app, visit it again, find the following message in the browser console:
error logged from line five in App.svelte      App.svelte:3 
  • Clicking on the file name takes me to line 3 of App.svelte: svelte-error

I’ve published a reproduction repo here: https://github.com/fd0/svelte-bug-typescript-sourcemap (including package-lock.json).

Expected behavior

The error message should refer to the correct line in the source code (line 5 instead of line 3).

Information about your Svelte project:

  • Chrome 83.0.4103.116
  • Debian Buster
  • Svelte 3.24.0
  • Rollup 2.22.2

Severity

It’s very annoying, but does not block me (or anybody else). Debugging takes much longer.

Additional context

Thank you very much for adding TypeScript to Svelte, I love it!

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 9
  • Comments: 19 (14 by maintainers)

Most upvoted comments

@milahu this sounds like a bug in either Vite (e.g. this plugin) or @rollup/plugin-alias. It’d be great if you were able to narrow down which project is causing it! Thanks as always for your help improving source map support!

still an issue with import alias this should also affect svelte-kit where import alias is used by default

repro

cd $(mktemp -d)
git clone https://github.com/milahu/svelte-web-desktop --branch debug-typescript-wrong-line-numbers
cd svelte-web-desktop
pnpm install
npm run dev

errors start at line 50

wallpaper.config.ts:1 src/configs/wallpapers/wallpaper.config.ts - line 1
wallpaper.config.ts:10 src/configs/wallpapers/wallpaper.config.ts - line 10
wallpaper.config.ts:20 src/configs/wallpapers/wallpaper.config.ts - line 20
wallpaper.config.ts:40 src/configs/wallpapers/wallpaper.config.ts - line 40
wallpaper.config.ts:43 src/configs/wallpapers/wallpaper.config.ts - line 50 -> 43
wallpaper.config.ts:71 src/configs/wallpapers/wallpaper.config.ts - line 80 -> 71
wallpaper.config.ts:440 src/configs/wallpapers/wallpaper.config.ts - line 500 -> 440

src/components/apps/WallpaperApp/Wallpaper.svelte

  import { wallpapersConfig } from '$src/configs/wallpapers/wallpaper.config';

relevant config …

vite.config.ts

export default defineConfig({
  resolve: {
    alias: {
      '$src': path.resolve('./src')
    },
  },

tsconfig.json

{
  "compilerOptions": {
    //"baseUrl": ".",
    "paths": {
      "$src/*": ["src/*"]
    },
  },
}

i tried to patch alias imports to relative imports with https://stackoverflow.com/questions/57188027/refactor-aliased-imports-to-relative-paths but jscodeshift cannot parse *.svelte files

/*
https://stackoverflow.com/questions/57188027/refactor-aliased-imports-to-relative-paths

pnpm i -D jscodeshift @babel/core

# problem: jscodeshift cannot parse *.svelte files
npx jscodeshift --extensions=svelte --parser=svelte -t codemods/alias-to-relative-import.cjs src

# JS dryrun + print
npx jscodeshift -t codemods/alias-to-relative-import.cjs src -d -p

# JS
npx jscodeshift -t codemods/alias-to-relative-import.cjs src

# TS dryrun + print
npx jscodeshift --extensions=ts --parser=ts -t codemods/alias-to-relative-import.cjs src -d -p

# TS
npx jscodeshift --extensions=ts --parser=ts -t codemods/alias-to-relative-import.cjs src

*/

const path = require("path");

function replacePathAlias(currentFilePath, importPath, pathMap) {
  // if windows env, convert backslashes to "/" first
  currentFilePath = path.posix.join(...currentFilePath.split(path.sep));

  const regex = createRegex(pathMap);
  return importPath.replace(regex, replacer);

  function replacer(_, alias, rest) {
const mappedImportPath = pathMap[alias] + rest;

// use path.posix to also create foward slashes on windows environment
let mappedImportPathRelative = path.posix.relative(
  path.dirname(currentFilePath),
  mappedImportPath
);
// append "./" to make it a relative import path
if (!mappedImportPathRelative.startsWith("../")) {
  mappedImportPathRelative = `./${mappedImportPathRelative}`;
}

logReplace(currentFilePath, mappedImportPathRelative);

return mappedImportPathRelative;
  }
}

function createRegex(pathMap) {
  const mapKeysStr = Object.keys(pathMap).reduce((acc, cur) => `${acc}|${cur}`);
  const regexStr = `^(${mapKeysStr})(.*)$`;
  return new RegExp(regexStr, "g");
}

const log = true;
function logReplace(currentFilePath, mappedImportPathRelative) {
  if (log)
console.log(
  "current processed file:",
  currentFilePath,
  "; Mapped import path relative to current file:",
  mappedImportPathRelative
);
}

/**
 * Corresponds to tsconfig.json paths or webpack aliases
 * E.g. "@/app/store/AppStore" -> "./src/app/store/AppStore"
 */
const pathMapping = {
  //"@": "./src",
  //"foo": "bar",
  "$src": "./src",
};

module.exports = function transform(file, api) {
  const j = api.jscodeshift;
  const root = j(file.source);

  root.find(j.ImportDeclaration).forEach(replaceNodepathAliases);
  root.find(j.ExportAllDeclaration).forEach(replaceNodepathAliases);

  /**
   * Filter out normal module exports, like export function foo(){ ...}
   * Include export {a} from "mymodule" etc.
   */
  root
.find(j.ExportNamedDeclaration, (node) => node.source !== null)
.forEach(replaceNodepathAliases);

  return root.toSource();

  function replaceNodepathAliases(impExpDeclNodePath) {
impExpDeclNodePath.value.source.value = replacePathAlias(
  file.path,
  impExpDeclNodePath.value.source.value,
  pathMapping
);
  }
};