vite-plugin-vue: Debugging vite+vue+typescript using HMR is broken. Breakpoint on incorrect line numbers

Describe the bug

Trying to debug a vite+vue+ts app doesn’t work for me. As soon as HMR runs the vscode debugger no longer stops on breakpoints. Possible the sourcemap gets out of sync.

In addition, chrome devtools doesn’t show the source code but the hard to read minified code.

Similar bugs:

  1. https://github.com/vitejs/vite/issues/5834 although this report mentions that “the application does indeed break at the expected moment, and both Chrome Debugger and VSCode highlight the same (correct) lines”, which is different behavior than mentioned in this report.

  2. https://github.com/vitejs/vite-plugin-react/issues/23 might be the same issue but uses react. It does mention that a browser refresh fixes it, but does not mention the problem that setting breakpoints elsewhere in the code is no longer always possible.

The issue at hand seems that after HMR the sourcemaps get messed up. I suspect that the issue goes deeper than that as I have a more complex app where the source map remains incorrect even after clearing the cache. Anyway lets fix get the simple case addressed.

Reproduction

  1. Create a new vite app as follows

yarn create @vitejs/app; select vue as framework and vue-ts as the variant

  1. Build and run the dev server:

yarn yarn dev

  1. start vscode (I’m using 1.62.3) and add the project folder to the workspace
  2. configure the launcher for chrome - run - add configuration - chrome; change the url port to 3000
  3. Start debugging. This should launch chrome displaying the vite demo app
  4. Open src/components/HelloWorld.vue in vscode and set a breakpoint on line 29, eg the button that has count++
  5. Click on the button: -> result vscode stops at the breakpoint as expected
  6. Edit the line to increment count by two, eg count+=2. Save
  7. Click on the button again Expected behavior: vscode stops at the breakpoint Actual behavior: breakpoint is ignored

After reloading the browser (F5) the breakpoint works again in this simple example until the code changes.

To spice it up a bit, replace the line with count++ to invoke a function 'incrementCount as follows:

  <button type="button" @click="() => incrementCount()">count is: {{ count }}</button>

and add this function in the script section:

const incrementCount = () => {
  count.value += 22
}

Repeat the test, this time setting the breakpoint in the function.-> this works as expected

Now try to set the breakpoint on the line with the button click. vscode will add a breakpoint in HelloWorld.vue?import=&t=1638336354075 (or something like it).

Hit the count button again… => expected behavior, the debugger stops. Actual behavior: the debugger does not stop. Hit F5 in the browser. => expected behavior, the browser refreshes and shows the app. Actual behavior, the debugger stops somewhere randomly.

System Info

System:
    OS: Linux 5.13 Ubuntu 21.10 21.10 (Impish Indri)
    CPU: (4) x64 Intel(R) Core(TM) i5-4570S CPU @ 2.90GHz
    Memory: 5.43 GB / 23.32 GB
    Container: Yes
    Shell: 5.1.8 - /bin/bash
  Browsers:
    Chrome: 94.0.4606.61
    Firefox: 94.0
  npmPackages:
    @vitejs/plugin-vue: ^1.9.0 => 1.10.1 
    vite: ^2.5.10 => 2.6.14

Used Package Manager

yarn

Logs

No response

Validations

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 55
  • Comments: 35 (1 by maintainers)

Most upvoted comments

This is a major deal breaker issue for our team and is blocking our migration from Webpack.

I don’t understand how such a critical bug can go unresolved for so long, did we miss a workaround or something?

Any feedback from the vite.js team would be much appreciated.

This is a very serious issue! It is not jus TS, but for JS also. My Vite + Vue + Js project is completly undebuggable.

can’t believe this bug has been hanging for nearly half years and nothing changed is vite just a toy or something

Started playing around with Vite+Vue tonight - and came across this error. I just don’t get it - how can anyone use Vite when it’s impossible to attach a debugger (Chrome // VSCode)…? This seems like a critical show stopper bug issue to me. Hope it gets fixed soon. Will have to revert to Webpack for now.

Based on issue https://github.com/vitejs/vite/issues/673, it seems that disabling HMR works around the issue. In vite.config.ts:

export default defineConfig({
  server: {
    hmr: false
  },
  plugins: [
...

It looks like the choice is to have HMR or debugging, you cannot have both.

If you don’t mind restarting the dev server after each edit, this might be a viable (although somewhat tedious) workaround.

I too am affected by this issue. I walked through the sequence below while playing around with this problem today. I was working on private code but suspect the same holds on a simpler repo.

Specific test:

  1. Baseline file = no changes - set a breakpoint at line 70 – that line is just a console.log("test") – this is a Typescript + React functional component = TSX file – the breakpoint is in the middle of the function.
  2. Trigger the break point, all is good
  3. Add a single line of code at line 71 (just copy the console.log() from line 70), save the file, let the HMR trigger, breakpoint triggers – breakpoint is still at line 70
  4. VS Code opens the ?import file which is the Vite processed version of the raw source
  5. The breakpoint triggers at line 71 in the ?import file. This is a completely different spot than intended since the file has been transformed.
  6. Remove the added line at 71, the file is now the exact same as before – breakpoint is still at line 70 in the source file
  7. Save and trigger the breakpoint
  8. VS Code opens the ?import version of the file with the breakpoint at line 70 – this is still nowhere near the intended spot since the ?import version is processed (not raw source)

So, between steps 1 and 9, the source code is the exact same, but the resulting breakpoint trigger location has changed when VS Code triggers. I don’t know the internals well enough, but it seems that Vite is capable of handling the mapping correctly on first load, but then something changes post HMR. In particular, the thing that changes is independent of the actual source since my source files are identical at start and end.

I’m purely guessing, but it seems that the source map is missing the last hop to get from the ?import version of the page back to the raw source. This is my hunch since the triggered breakpoint is at the correct line in the wrong file. Further, it seems that the mapping is correct up until the HMR transform takes place.

If there are any pointers on how to debug these various steps, I’d give it a whirl. Unfortunately, I don’t know the debugging process or source maps well enough to reason it out otherwise. That is, I’d love to help, but it’s not clear how.

Hmm, not saying you’re wrong, but this is a bit of a stretch. Saying that ‘since it works in chrome it can’t be vite’ has a few shortcomings.

  1. This is just an assumption. Without understanding the root cause this statement is unfounded.
  2. The problem exists for both VSCode and Jetbrains Intellij. Are both tools to blame for not handling typescript/HMR properly?

I think it is incorrect to close this issue as nothing has been resolved.

Update: In my case this is only a problem for Single File Components. Debugging in typescript files seems to have correct line numbers. It does suggest that the problem is in sourcemap generation for SFCs. Is there a way to get some more eyes on this?

To clarify, debugging is currently working fine with Chrome Devtools’ debugging for the following cases:

  1. debugger statements in source code, e.g. @click="debugger; count++"
  2. Breakpoints attached via Chrome Devtools’ Source panel.

It only breaks after HMR if the debugging is done by launching via VSCode. So the solution is to prefer Chrome’s native debugger instead of VSCode’s debugger. You’ll also find that Chrome’s native debugger is more reliable and much faster.

As for the VSCode issue, it is not really caused by Vue or plugin-vue, but an incompatibility between VSCode’s debugger and how Vite HMR works. I’m not sure if this is something we can fix - if anything, this seems to be something VSCode should be fixing since Vite is working fine with Chrome’s native debugger as far as I can tell.

Any update on this? I can connect the debugger to the compiled JS files in vscode, but unable to link it all the way to the original TS files.

I made an attempt at upgrading from vue2 to vue3+vite a few days ago. Debugging worked fine for a few hours - until it suddenly didn’t. Haven’t been able to quite pinpoint what causes it yet.

BUT I have been able to make it work in Firefox (with HMR) by:

  1. Add a console.log statement somewhere in the vue file you want to debug
  2. Goto Console in Vue - click on the Filename:line that logged. This will open the correct source file that logged (Component.vue?t=123123423423)
  3. Attach debugger on given line

You can also press Ctrl-P in Debugger window in Firefox and open the component/file there - but you’ll see several files (same name, different url) but make sure you take the cachebusted version (the one with ?t=123123123123).

This is also a problem in react (see https://github.com/vitejs/vite-plugin-react/issues/23), so maybe it should be on a higher level than this plugin?

我的项目也出现了相同问题;经过多次测试。发现使用了element-plus全局自动按需引入后会出现这个问题;具体是因为unplugin-vue-components、unplugin-auto-import这两个包。当安装了这两个包后发现node_modules里面会多出几个webpack相关的包; 卸载后断点正常;

hmr: false

That worked for me. I’m using Vite 4.x and React with Typescript. Until that change, Vite was unusable for me. I was very close to switching. Thanks

Looks like no one cares to fix this. I recently updated my Vue project to use Vite and many nasty random vite errors pop up during page load that seem to originate out of nowhere (reloading the page several times and it miraculously works). Debugging is not possible because breakpoints are not binding. Completely out of ideas…

Found my issue! I was using vite-plugin-require because we had a couple of them. I manage to remove all except one so I use the exclude option:

  return defineConfig({
    plugins: [
      react(),
      vitePluginRequire({
        fileRegex:/(config.js)$/
      }),

When analysing source maps with this plugin, they come out quite differently. Some newline are inserted that are messing up source maps.

You can repro with a very simple project testvite.zip

I hope this solve the issue for a bunch of you!

Cheers!

Update on my bug. I’ve analyze the sourcemaps for a simple file (http://sokra.github.io/source-map-visualization/#custom is a very awesome tool). Every file containing an import of react has bad sourcemaps, without react it’s fine. Those file contains generated code to enable HMR:

let prevRefreshReg;
let prevRefreshSig;

if (import.meta.hot) {
  if (!window.__vite_plugin_react_preamble_installed__) {
    throw new Error("@vitejs/plugin-react can't detect preamble. Something is wrong. " + "See https://github.com/vitejs/vite-plugin-react/pull/11#discussion_r430879201");
  }

  prevRefreshReg = window.$RefreshReg$;
  prevRefreshSig = window.$RefreshSig$;

  window.$RefreshReg$ = (type, id) => {
    RefreshRuntime.register(type, "C:/blablabla/AttributeEditor/index.js" + " " + id);
  };

  window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform;
}

var _s = $RefreshSig$();

from what I’ve seen sourceMaps are not taking into account those lines and it offset the sourceMaps by something like 12 to 15 lines. This is my observation, I might be wrong, but removing the generated code HMR setup fixes the source maps completely.

I’m trying to disable HMR to see source maps, but setting:

server: { hmr: false },

does not do anything. This code is still being generated and source maps are off.

I can’t speak for everyone because my setup is slightly unusual, but I’m able to debug a Vue 3 SFC TS application in VSCode using the Firefox extension. Here is my raw config (note that my frontend project is a subfolder of the workspace folder).

My config is unusual because in development, a Django application serves a HTML which includes scripts from another virtual host, where Vite is running.

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "firefox",
            "request": "launch",
            "name": "vuejs: firefox",
            "url": "https://theproject.dev.somedomain.nl",
            "pathMappings": [
                {
                    "url": "file:///app", // /app is the path where my frontend project is mounted in a docker container running Vite
                    "path": "${workspaceFolder}/frontend"
                },
                {
                    "url": "https://frontend.theproject.dev.somedomain.nl/node_modules",
                    "path": "${workspaceFolder}/frontend/node_modules"
                }
            ],
            "profile": "default",
            "keepProfileChanges": true,
            "log": {
                "consoleLevel": {
                    "PathConversion": "Debug",
                    "default": "Error"
                },
            },
            "skipFiles": [
                "${workspaceFolder}/frontend/node_modules/**/*.js"
            ],
        }
    ]
}

This is what it should look like if I adjust the paths to those of a regular setup

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "firefox",
            "request": "launch",
            "name": "vuejs: firefox",
            "url": "http://localhost:5173",
            "pathMappings": [
                {
                    "url": "file://${workspaceFolder}",
                    // "url": "file:///home/me/repos/myviteproject", // Alternatively...
                    "path": "${workspaceFolder}"
                },
                // Only mapping node_modules so we are able to ignore exceptions originating from there (see skipFiles)
                {
                    "url": "http://localhost:5173/node_modules",
                    "path": "${workspaceFolder}/node_modules"
                }
            ],
            "profile": "default",
            "keepProfileChanges": true, // Don't forget my cookies every time
            "log": {
                "consoleLevel": {
                    "PathConversion": "Debug", // Show debug messages about path mapping
                    "default": "Error"
                },
            },
            "skipFiles": [
                "${workspaceFolder}/node_modules/**/*.js" // Ignore node_modules exceptions
            ],
        }
    ]
}

@cryptoGF Yes I think that Vite doesn’t do HMR. Source maps are correct until a HMR takes place. As @matija2209 confirmed, disabling mhr does often make debugging workable.