From e1c10fe10f16250a0eb6895c468610a9b0bf6034 Mon Sep 17 00:00:00 2001 From: "Lyu, Wei Da" Date: Wed, 17 Dec 2025 09:12:53 +0800 Subject: [PATCH] perf: optimize path normalization --- .changeset/cozy-foxes-shine.md | 5 +++ .../src/plugins/typescript/module-loader.ts | 4 +-- .../src/plugins/typescript/svelte-sys.ts | 24 ++++++++------ packages/language-server/src/utils.ts | 22 ++++++++++--- packages/language-server/test/utils.test.ts | 32 ++++++++++++++++++- 5 files changed, 70 insertions(+), 17 deletions(-) create mode 100644 .changeset/cozy-foxes-shine.md diff --git a/.changeset/cozy-foxes-shine.md b/.changeset/cozy-foxes-shine.md new file mode 100644 index 000000000..8290388b6 --- /dev/null +++ b/.changeset/cozy-foxes-shine.md @@ -0,0 +1,5 @@ +--- +'svelte-language-server': patch +--- + +perf: optimize path normalization diff --git a/packages/language-server/src/plugins/typescript/module-loader.ts b/packages/language-server/src/plugins/typescript/module-loader.ts index 9d3f77f95..0237b7100 100644 --- a/packages/language-server/src/plugins/typescript/module-loader.ts +++ b/packages/language-server/src/plugins/typescript/module-loader.ts @@ -88,7 +88,7 @@ class ModuleResolutionCache { } oneOfResolvedModuleChanged(path: string) { - return this.pendingInvalidations.has(path); + return this.pendingInvalidations.size > 0 && this.pendingInvalidations.has(path); } } @@ -325,7 +325,7 @@ export function createSvelteModuleLoader( return ( moduleCache.oneOfResolvedModuleChanged(path) || // tried but failed file might now exist - failedLocationInvalidated.has(path) + (failedLocationInvalidated.size > 0 && failedLocationInvalidated.has(path)) ); } diff --git a/packages/language-server/src/plugins/typescript/svelte-sys.ts b/packages/language-server/src/plugins/typescript/svelte-sys.ts index 553bd17a3..c5456eec4 100644 --- a/packages/language-server/src/plugins/typescript/svelte-sys.ts +++ b/packages/language-server/src/plugins/typescript/svelte-sys.ts @@ -14,23 +14,29 @@ export function createSvelteSys(tsSystem: ts.System) { // First check if there's a `.svelte.d.ts` or `.d.svelte.ts` file, which should take precedence const dtsPath = sveltePath.slice(0, -7) + '.svelte.d.ts'; - const dtsPathExists = fileExistsCache.get(dtsPath) ?? tsSystem.fileExists(dtsPath); - fileExistsCache.set(dtsPath, dtsPathExists); + const dtsPathExists = getOrCreateFileExistCache(dtsPath); if (dtsPathExists) return false; - const svelteDtsPathExists = fileExistsCache.get(path) ?? tsSystem.fileExists(path); - fileExistsCache.set(path, svelteDtsPathExists); + const svelteDtsPathExists = getOrCreateFileExistCache(path); if (svelteDtsPathExists) return false; - const sveltePathExists = - fileExistsCache.get(sveltePath) ?? tsSystem.fileExists(sveltePath); - fileExistsCache.set(sveltePath, sveltePathExists); + const sveltePathExists = getOrCreateFileExistCache(sveltePath); return sveltePathExists; } else { return false; } } + function getOrCreateFileExistCache(path: string) { + const cached = fileExistsCache.get(path); + if (cached !== undefined) { + return cached; + } + const exists = tsSystem.fileExists(path); + fileExistsCache.set(path, exists); + return exists; + } + function getRealSveltePathIfExists(path: string) { return svelteFileExists(path) ? toRealSvelteFilePath(path) : path; } @@ -48,9 +54,7 @@ export function createSvelteSys(tsSystem: ts.System) { const sveltePathExists = svelteFileExists(path); if (sveltePathExists) return true; - const exists = fileExistsCache.get(path) ?? tsSystem.fileExists(path); - fileExistsCache.set(path, exists); - return exists; + return getOrCreateFileExistCache(path); }, readFile(path: string) { // No getSnapshot here, because TS will very rarely call this and only for files that are not in the project diff --git a/packages/language-server/src/utils.ts b/packages/language-server/src/utils.ts index 9a1ba01d2..215d69d0c 100644 --- a/packages/language-server/src/utils.ts +++ b/packages/language-server/src/utils.ts @@ -1,8 +1,7 @@ -import { isEqual, sum, uniqWith } from 'lodash'; -import { FoldingRange, Node } from 'vscode-html-languageservice'; +import { isEqual, uniqWith } from 'lodash'; +import { Node } from 'vscode-html-languageservice'; import { Position, Range } from 'vscode-languageserver'; import { URI } from 'vscode-uri'; -import { Document, TagInformation } from './lib/documents'; type Predicate = (x: T) => boolean; @@ -38,12 +37,27 @@ export function pathToUrl(path: string) { return URI.file(path).toString(); } +const backslashRegEx = /\\/g; + /** * Some paths (on windows) start with a upper case driver letter, some don't. * This is normalized here. */ export function normalizePath(path: string): string { - return URI.file(path).fsPath.replace(/\\/g, '/'); + return normalizeDriveLetter(path.replace(backslashRegEx, '/')); +} + +function normalizeDriveLetter(path: string): string { + if (path.charCodeAt(1) !== /*:*/ 58) { + return path; + } + + const driveLetter = path.charCodeAt(0); + if (driveLetter >= /*A*/ 65 && driveLetter <= /*Z*/ 90) { + return String.fromCharCode(driveLetter + 32) + path.slice(1); + } + + return path; } /** diff --git a/packages/language-server/test/utils.test.ts b/packages/language-server/test/utils.test.ts index f0e748e87..9dc8a7326 100644 --- a/packages/language-server/test/utils.test.ts +++ b/packages/language-server/test/utils.test.ts @@ -1,6 +1,12 @@ -import { isBeforeOrEqualToPosition, modifyLines, regexLastIndexOf } from '../src/utils'; +import { + isBeforeOrEqualToPosition, + modifyLines, + normalizePath, + regexLastIndexOf +} from '../src/utils'; import { Position } from 'vscode-languageserver'; import * as assert from 'assert'; +import { URI } from 'vscode-uri'; describe('utils', () => { describe('#isBeforeOrEqualToPosition', () => { @@ -61,4 +67,28 @@ describe('utils', () => { assert.deepStrictEqual(idxs, [0, 1, 2, 3]); }); }); + + describe('path normalization on Windows', () => { + it('should lowercase drive letters and normalize slashes', () => { + assert.strictEqual( + normalizePath('C:\\Users\\Test\\project\\file.ts'), + 'c:/Users/Test/project/file.ts' + ); + + assert.strictEqual( + normalizePath('D:/Some/Other/Path/file.ts'), + 'd:/Some/Other/Path/file.ts' + ); + + assert.strictEqual( + normalizePath('e:\\Mixed/Slashes\\Path/file.ts'), + 'e:/Mixed/Slashes/Path/file.ts' + ); + + assert.strictEqual( + normalizePath('/already/normalized/path/file.ts'), + '/already/normalized/path/file.ts' + ); + }); + }); });