diff --git a/extensions/css-language-features/server/src/test/links.test.ts b/extensions/css-language-features/server/src/test/links.test.ts index f6b1a349c7062..65cedc0dba4c3 100644 --- a/extensions/css-language-features/server/src/test/links.test.ts +++ b/extensions/css-language-features/server/src/test/links.test.ts @@ -97,4 +97,24 @@ suite('Links', () => { [{ offset: 29, value: '"~foo/hello.html"', target: getTestResource('node_modules/foo/hello.html') }], testUri, folders ); }); + + test('bare module specifier resolving without tilde', async function () { + + const testUri = getTestResource('about.css'); + const folders = [{ name: 'x', uri: getTestResource('') }]; + + await assertLinks('@import "foo/hello.html|"', + [{ offset: 9, value: '"foo/hello.html"', target: getTestResource('node_modules/foo/hello.html') }], testUri, folders + ); + }); + + test('bare module specifier resolving from subfolder', async function () { + + const testUri = getTestResource('subdir/about.css'); + const folders = [{ name: 'x', uri: getTestResource('') }]; + + await assertLinks('@import "foo/hello.html|"', + [{ offset: 9, value: '"foo/hello.html"', target: getTestResource('node_modules/foo/hello.html') }], testUri, folders + ); + }); }); diff --git a/extensions/css-language-features/server/src/utils/documentContext.ts b/extensions/css-language-features/server/src/utils/documentContext.ts index c9f46fb75789b..6dc42dd57fcce 100644 --- a/extensions/css-language-features/server/src/utils/documentContext.ts +++ b/extensions/css-language-features/server/src/utils/documentContext.ts @@ -8,6 +8,11 @@ import { endsWith, startsWith } from '../utils/strings'; import { WorkspaceFolder } from 'vscode-languageserver'; import { Utils, URI } from 'vscode-uri'; +function isBareModuleSpecifier(ref: string): boolean { + // A bare module specifier doesn't start with '.', '..', '/', '~', or a protocol + return !/^(\.\.?\/|\/|~|[a-z][a-z0-9+\-.]*:)/i.test(ref); +} + export function getDocumentContext(documentUri: string, workspaceFolders: WorkspaceFolder[]): DocumentContext { function getRootFolder(): string | undefined { for (const folder of workspaceFolders) { @@ -30,6 +35,15 @@ export function getDocumentContext(documentUri: string, workspaceFolders: Worksp return folderUri + ref.substring(1); } } + // For bare module specifiers (e.g., "some-module/style.css"), + // resolve against node_modules in the workspace root as a + // fallback, similar to how bundlers like Vite resolve imports. + if (isBareModuleSpecifier(ref)) { + const folderUri = getRootFolder(); + if (folderUri) { + return Utils.resolvePath(URI.parse(folderUri), 'node_modules', ref).toString(true); + } + } const baseUri = URI.parse(base); const baseUriDir = baseUri.path.endsWith('/') ? baseUri : Utils.dirname(baseUri); return Utils.resolvePath(baseUriDir, ref).toString(true);