fix(vite,devtools-kit): Open in Editor for JSX/TSX components - #1122
Open
NikhilVerma wants to merge 4 commits into
Open
fix(vite,devtools-kit): Open in Editor for JSX/TSX components#1122NikhilVerma wants to merge 4 commits into
NikhilVerma wants to merge 4 commits into
Conversation
@vitejs/plugin-vue-jsx marks every Vue component it finds with __hmrId but never sets __file, so the devtools "Open in Editor" button was hidden for all JSX/TSX components (the button is gated on instance.type.__file). Add a post-enforce Vite transform that piggybacks on those __hmrId assignments: for each `LocalName.__hmrId = ...` the plugin has already injected, we prepend `LocalName.__file = "/abs/path/to/file.tsx"`. This makes JSX/TSX components behave identically to SFCs in the component tree panel. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two follow-up fixes for JSX/TSX component support: 1. Open in Editor did nothing when clicking the icon (devtools-kit) When the Vite plugin is active, __VUE_INSPECTOR__ is already set up with the correct launch-editor-middleware. The chrome-extension branch was bypassing it and falling back to a raw fetch that often failed silently. Now __VUE_INSPECTOR__ is preferred whenever vitePluginDetected is true, regardless of the devtools host. The fetch fallback is kept for cases where no Vite plugin is present, with a clearer console error message. 2. Plain function/arrow TSX components had no file icon (vite) The previous __hmrId piggyback only worked for defineComponent-based components. Plain exports like `export function HomeKpiGrid()` don't get __hmrId from @vitejs/plugin-vue-jsx. Added a second injection that derives the component name from the file name (PascalCase convention) and injects __file on it as a safe guard expression. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Once a build tool sets `__file` on a JSX/TSX component, as @vitejs/plugin-vue already does for SFCs (see vitejs/vite-plugin-vue#784), `getComponentFileName` still cannot read it: `basename(file, '.vue')` calls `lastIndexOf('.vue')` on `Foo.tsx`, gets -1, and `substring(0, -1)` clamps to an empty string. An anonymous `defineComponent({})` in a .tsx file therefore falls through to "Anonymous Component" instead of its filename. Handle .jsx and .tsx alongside .vue, and extend the existing index.vue suppression to index.jsx / index.tsx barrel files.
Move the injection out of the plugin factory into its own module so it can be unit tested, and fix two problems in the plain-function branch: - It stamped any binding whose name matched the file name, including one the module merely imported. `Button.tsx` re-exporting a `Button` from elsewhere would attribute that component to the wrong file. It now only stamps bindings the module declares itself. - Property assignment on a non-object binding throws under ESM strict mode, so the assignment is now narrowed to functions and objects. Also derive the basename from the normalized path so the behaviour does not depend on the host OS separator.
✅ Deploy Preview for vue-devtools-docs canceled.
|
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
"Open in Editor" never appears for JSX/TSX components. DevTools resolves a
component's source path from
instance.type.__file, and nothing sets it forJSX/TSX — so
activeTreeNodeFilePathis empty and the button is never rendered.@vitejs/plugin-vueattaches__fileto every SFC during serve, with thecomment "expose filename during serve for devtools to pickup":
@vitejs/plugin-vue-jsxhas no equivalent. It emits only__hmrId, which is asha256 hash with no path in it.
Reproduction
https://github.com/NikhilVerma/vue-devtools-tsx-repro
pnpm install && pnpm verifypnpm verifyboots the dev server and greps each transformed module — nobrowser needed.
pnpm devreproduces it in the UI: select<SfcButton>and thelaunch icon is there, select either TSX component and it is gone.
Changes
packages/vite— attach__fileto JSX/TSX components in serve mode(
src/jsx-file-injection.ts). Two cases:plugin-vue-jsxalready tagged with__hmrId(thedefineComponentpattern) — stamp the same local binding.__hmrIdat all, matched bythe convention of naming the component after its file. This only fires when
the module declares that binding itself, so a re-exported import is never
attributed to the wrong file, and the assignment is narrowed to functions and
objects so a same-named non-component binding cannot throw under ESM strict
mode.
packages/devtools-kit—openInEditornow prefers__VUE_INSPECTOR__whenever
vitePluginDetectedis true, regardless of host. Thechrome-extensionbranch was bypassing the already-configuredlaunch-editor-middleware and falling back to a raw fetch that failed silently.
The fetch fallback is kept for when no Vite plugin is present, with a clearer
error.
packages/devtools-kit—getComponentFileNamenow handles.jsx/.tsx.Once
__fileexists,basename(file, '.vue')still cannot read it:lastIndexOf('.vue')onFoo.tsxis-1andsubstring(0, -1)clamps to'',so an anonymous
defineComponent({})in a.tsxfile falls through to"Anonymous Component" instead of its filename. Also extends the existing
index.vuesuppression toindex.jsx/index.tsx.Relationship to vitejs/vite-plugin-vue#784
I also opened vitejs/vite-plugin-vue#784, which fixes
this in
plugin-vue-jsxvia the Babel AST. That is the better home for it —it works for anyone using JSX, including people on the browser extension who
never install this plugin.
These aren't redundant. The upstream fix only reaches components
plugin-vue-jsxtracks for HMR, so plain function components stay uncovered;and it only helps once released. The change here works today and covers both
shapes. If both land they assign
__fileto the same value, last write wins,and the result is identical.
Tests
packages/vite/__tests__/jsx-file-injection.spec.ts— 10 tests covering bothinjection paths, the imported-binding case, double-stamping, non-JSX files,
query-suffixed ids, and lowercase filenames
packages/devtools-kit/__tests__/component/utils.test.ts— 15 tests forgetComponentName/getInstanceNameagainst.jsxand.tsxfilesVerified manually
Against the reproduction, with this branch built and installed:
/__open-in-editor?file=/…/TsxButton.tsx:0:0— the sameshape as the SFC — which returns 200 and opens the file
Supersedes #1102
#1102 was pushed from the wrong branch: it contained a component-highlighter
change rather than any of this, which is why it looked like it was fixing
nothing. Closing it in favour of this. The highlighter change (DOM walking for
content the renderer never created, e.g.
v-html/innerHTML) is a separateissue and I'll raise it on its own if it's wanted.