Summary
@vitejs/plugin-vue injects __file on every SFC component in development, which Vue DevTools uses to show the Open in Editor button and to resolve component file paths. @vitejs/plugin-vue-jsx never injects __file, so this button is always absent for JSX/TSX components regardless of how they are defined.
Reproduction
Any Vue app using JSX/TSX components. Select a TSX component in Vue DevTools — the Open in Editor icon (⬡) is missing from the component panel header because instance.type.__file is undefined.
Root cause
@vitejs/plugin-vue (line ~1657 of its dist):
// Injected on every SFC in dev or when devToolsEnabled:
if (devToolsEnabled || (devServer && !isProduction))
attachedProps.push(['__file', JSON.stringify(isProduction ? path.basename(filename) : filename)])
@vitejs/plugin-vue-jsx has no equivalent. Its only devtools-related injection is __hmrId, and even that is limited to components detected via parseComponentDecls — which only matches defineComponent() call expressions. Plain function/arrow components receive nothing.
// plugin-vue-jsx/dist/index.js ~line 161-163
// hotComponents is only populated when defineComponent() is detected
for (const { local, exported, id: id$1 } of hotComponents) {
code$1 += `\n${local}.__hmrId = "${id$1}"\n__VUE_HMR_RUNTIME__.createRecord("${id$1}", ${local})`
// __file is never written here or anywhere else in the plugin
}
So the current behaviour is:
| Pattern |
__hmrId |
__file |
.vue SFC |
✅ |
✅ |
.tsx with defineComponent() |
✅ |
❌ |
.tsx plain function/arrow |
❌ |
❌ |
Expected behaviour
All JSX/TSX components should receive __file in development, matching what plugin-vue does for SFCs. For components already detected by parseComponentDecls, the fix is a one-liner alongside the existing __hmrId injection:
for (const { local, exported, id: id$1 } of hotComponents) {
code$1 += `\n${local}.__file = ${JSON.stringify(id)}` // add this
code$1 += `\n${local}.__hmrId = "${id$1}"\n__VUE_HMR_RUNTIME__.createRecord("${id$1}", ${local})`
}
For plain functional components not detected by parseComponentDecls, a broader injection (e.g. based on export declarations) would be needed for full parity.
Notes
- Verified against
@vitejs/plugin-vue-jsx@5.1.1 and @vitejs/plugin-vue@6.0.1
@vue/babel-plugin-jsx (used internally by plugin-vue-jsx) also does not inject __file
- There is no plugin option or workaround to enable this behaviour today
- Affects Vue DevTools (both the Vite plugin and the Chrome extension), Nuxt DevTools, and any tool that reads
instance.type.__file
Summary
@vitejs/plugin-vueinjects__fileon every SFC component in development, which Vue DevTools uses to show the Open in Editor button and to resolve component file paths.@vitejs/plugin-vue-jsxnever injects__file, so this button is always absent for JSX/TSX components regardless of how they are defined.Reproduction
Any Vue app using JSX/TSX components. Select a TSX component in Vue DevTools — the Open in Editor icon (⬡) is missing from the component panel header because
instance.type.__fileisundefined.Root cause
@vitejs/plugin-vue(line ~1657 of its dist):@vitejs/plugin-vue-jsxhas no equivalent. Its only devtools-related injection is__hmrId, and even that is limited to components detected viaparseComponentDecls— which only matchesdefineComponent()call expressions. Plain function/arrow components receive nothing.So the current behaviour is:
__hmrId__file.vueSFC.tsxwithdefineComponent().tsxplain function/arrowExpected behaviour
All JSX/TSX components should receive
__filein development, matching whatplugin-vuedoes for SFCs. For components already detected byparseComponentDecls, the fix is a one-liner alongside the existing__hmrIdinjection:For plain functional components not detected by
parseComponentDecls, a broader injection (e.g. based on export declarations) would be needed for full parity.Notes
@vitejs/plugin-vue-jsx@5.1.1and@vitejs/plugin-vue@6.0.1@vue/babel-plugin-jsx(used internally by plugin-vue-jsx) also does not inject__fileinstance.type.__file