diff --git a/packages/devtools-kit/__tests__/core/open-in-editor.test.ts b/packages/devtools-kit/__tests__/core/open-in-editor.test.ts new file mode 100644 index 000000000..a601c8dfe --- /dev/null +++ b/packages/devtools-kit/__tests__/core/open-in-editor.test.ts @@ -0,0 +1,48 @@ +import { target } from '@vue/devtools-shared' +import { afterEach, describe, expect, it, vi } from 'vitest' +import { openInEditor, setOpenInEditorBaseUrl } from '../../src/core/open-in-editor' +import { devtoolsState } from '../../src/ctx/state' + +describe('openInEditor', () => { + afterEach(() => { + vi.restoreAllMocks() + delete target.__VUE_INSPECTOR__ + delete target.__VUE_DEVTOOLS_OPEN_IN_EDITOR_BASE_URL__ + devtoolsState.vitePluginDetected = true + }) + + it('uses the inspector global when it is available', () => { + const fetch = vi.spyOn(globalThis, 'fetch').mockResolvedValue(new Response()) + const openInEditorMock = vi.fn() + + target.__VUE_INSPECTOR__ = { + openInEditor: openInEditorMock, + } + + openInEditor({ + baseUrl: 'http://localhost:5173', + file: '/project/src/App.vue', + line: 12, + column: 3, + }) + + expect(openInEditorMock).toHaveBeenCalledWith('http://localhost:5173', '/project/src/App.vue', 12, 3) + expect(fetch).not.toHaveBeenCalled() + }) + + it('falls back to the Vite open-in-editor endpoint when the inspector global is missing', () => { + const fetch = vi.spyOn(globalThis, 'fetch').mockResolvedValue(new Response()) + setOpenInEditorBaseUrl('http://localhost:5173') + + openInEditor({ + file: '/project/src/App.vue', + line: 12, + column: 3, + }) + + expect(fetch).toHaveBeenCalledWith( + 'http://localhost:5173/__open-in-editor?file=%2Fproject%2Fsrc%2FApp.vue%3A12%3A3', + { mode: 'no-cors' }, + ) + }) +}) diff --git a/packages/devtools-kit/src/core/open-in-editor/index.ts b/packages/devtools-kit/src/core/open-in-editor/index.ts index 65ca37852..2581c21f4 100644 --- a/packages/devtools-kit/src/core/open-in-editor/index.ts +++ b/packages/devtools-kit/src/core/open-in-editor/index.ts @@ -13,6 +13,12 @@ export function setOpenInEditorBaseUrl(url: string) { target.__VUE_DEVTOOLS_OPEN_IN_EDITOR_BASE_URL__ = url } +function openInEditorWithFetch(baseUrl: string, file: string, line: number, column: number) { + return fetch(`${baseUrl}/__open-in-editor?file=${encodeURIComponent(`${file}:${line}:${column}`)}`, { + mode: 'no-cors', + }) +} + export function openInEditor(options: OpenInEditorOptions = {}) { const { file, host, baseUrl = window.location.origin, line = 0, column = 0 } = options if (file) { @@ -29,7 +35,12 @@ export function openInEditor(options: OpenInEditorOptions = {}) { } else if (devtoolsState.vitePluginDetected) { const _baseUrl = target.__VUE_DEVTOOLS_OPEN_IN_EDITOR_BASE_URL__ ?? baseUrl - target.__VUE_INSPECTOR__.openInEditor(_baseUrl, file, line, column) + if (target.__VUE_INSPECTOR__) { + target.__VUE_INSPECTOR__.openInEditor(_baseUrl, file, line, column) + } + else { + openInEditorWithFetch(_baseUrl, file, line, column) + } } } }