From 6c8fd5e97b886e9c42d3234ba26b6f905cf38e4a Mon Sep 17 00:00:00 2001 From: Niels <723299+dknn@users.noreply.github.com> Date: Wed, 17 Jun 2026 23:55:06 +0200 Subject: [PATCH] fix: normalize live preview URLs --- src/connectionInfo/connection.ts | 3 ++- src/editorPreview/webviewComm.ts | 22 +++++++++++++--------- src/test/suite/connectionInfo.test.ts | 11 ++++++++++- src/test/suite/preview.test.ts | 10 +++++++++- 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/connectionInfo/connection.ts b/src/connectionInfo/connection.ts index 7e8a8bb2..e6037ea9 100644 --- a/src/connectionInfo/connection.ts +++ b/src/connectionInfo/connection.ts @@ -157,7 +157,8 @@ export class Connection extends Disposable { const workspaceRoot = this.rootPath; if (workspaceRoot && this._absPathInWorkspace(path)) { - return PathUtil.ConvertToPosixPath(path.substring(workspaceRoot.length)); + const relativePath = PathUtil.ConvertToPosixPath(path.substring(workspaceRoot.length)); + return relativePath.startsWith('/') || relativePath === '' ? relativePath : `/${relativePath}`; } else { return undefined; } diff --git a/src/editorPreview/webviewComm.ts b/src/editorPreview/webviewComm.ts index c051f6bf..a2856c1b 100644 --- a/src/editorPreview/webviewComm.ts +++ b/src/editorPreview/webviewComm.ts @@ -80,15 +80,19 @@ export class WebviewComm extends Disposable { * @param {string} hostURI the (optional) URI of the host; alternatively, the function will manually generate the connection manager's HTTP URI if not provided with it initially. * @returns {Promise} a promise for the address for the content. */ - public async constructAddress( - URLExt: string, - connection: Connection = this.currentConnection, - hostURI?: vscode.Uri, - windowId?: number | undefined, - ): Promise { - if (URLExt.length > 0 && URLExt[0] == '/') { - URLExt = URLExt.substring(1); - } + public async constructAddress( + URLExt: string, + connection: Connection = this.currentConnection, + hostURI?: vscode.Uri, + windowId?: number | undefined, + ): Promise { + if (/^https?:\/\//i.test(URLExt)) { + return URLExt; + } + + if (URLExt.length > 0 && URLExt[0] == '/') { + URLExt = URLExt.substring(1); + } if (!hostURI) { hostURI = await this.resolveHost(connection); diff --git a/src/test/suite/connectionInfo.test.ts b/src/test/suite/connectionInfo.test.ts index 3d36374f..1235f456 100644 --- a/src/test/suite/connectionInfo.test.ts +++ b/src/test/suite/connectionInfo.test.ts @@ -5,6 +5,7 @@ import assert from 'assert'; import sinon from 'sinon'; import vscode from 'vscode'; +import { Connection } from '../../connectionInfo/connection'; import { ConnectionManager } from '../../connectionInfo/connectionManager'; import { PathUtil } from '../../utils/pathUtil'; @@ -87,6 +88,14 @@ describe('ConnectionInfo', () => { }); + it('should include a leading slash when workspace root already ends with a separator', () => { + const connection = new Connection(testWorkspaces[0], '', 3000, 3001, '127.0.0.1'); + sandbox.stub(connection, 'rootPath').get(() => 'C:\\Users\\TestUser\\workspace1\\'); + + assert.deepEqual(connection.getFileRelativeToWorkspace('C:\\Users\\TestUser\\workspace1\\index.html'), '/index.html'); + + connection.dispose(); + }); it('should be able to create a Connection with an undefined workspace', async () => { const target = sinon.spy(); sandbox.stub(SettingUtil, 'GetConfig').returns(makeSetting({})); @@ -116,4 +125,4 @@ describe('ConnectionInfo', () => { } ], target.args[0]); }); -}); \ No newline at end of file +}); diff --git a/src/test/suite/preview.test.ts b/src/test/suite/preview.test.ts index b2104619..cba53cff 100644 --- a/src/test/suite/preview.test.ts +++ b/src/test/suite/preview.test.ts @@ -95,4 +95,12 @@ describe('PreviewManager', () => { assert.ok(executeCommand.calledOnce); assert.ok(executeCommand.getCall(0).calledWith('extension.js-debug.debugLink', `http://${connection.host}:${connection.httpPort}/index.html`)); }); -}); \ No newline at end of file + + it("does not prefix absolute URLs with the preview host", async () => { + const webviewComm = Object.create(WebviewComm.prototype) as WebviewComm; + const hostUri = vscode.Uri.parse(`http://${connection.host}:${connection.httpPort}/`); + + assert.equal(await webviewComm.constructAddress('http://example.test/page.html', connection, hostUri), 'http://example.test/page.html'); + assert.equal(await webviewComm.constructAddress('https://example.test/page.html', connection, hostUri), 'https://example.test/page.html'); + }); +});