diff --git a/packages/language-server/src/plugins/typescript/module-loader.ts b/packages/language-server/src/plugins/typescript/module-loader.ts index 915e50b26..5c440dbf1 100644 --- a/packages/language-server/src/plugins/typescript/module-loader.ts +++ b/packages/language-server/src/plugins/typescript/module-loader.ts @@ -104,10 +104,13 @@ class ImpliedNodeFormatResolver { return undefined; } - let mode = undefined; + let mode: ReturnType = undefined; if (sourceFile) { this.cacheImpliedNodeFormat(sourceFile, compilerOptions); mode = ts.getModeForResolutionAtIndex(sourceFile, importIdxInFile, compilerOptions); + if (!mode && isSvelteFilePath(importPath)) { + mode = ts.ModuleKind.ESNext; // necessary for TS' module resolution to go into the right branches + } } return mode; } @@ -293,6 +296,21 @@ export function createSvelteModuleLoader( const snapshot = getSnapshot(resolvedFileName); + // Align with TypeScript behavior: If the Svelte file is not using TypeScript, + // mark it as unresolved so that people need to provide a .d.ts file. + // For backwards compatibility we're not doing this for files from packages + // without an exports map, because that may break too many existing projects. + if ( + resolvedModule.isExternalLibraryImport && + resolvedModule.extension === '.d.svelte.ts' && // this tells us it's from an exports map + snapshot.scriptKind !== ts.ScriptKind.TS + ) { + return { + ...resolvedModuleWithFailedLookup, + resolvedModule: undefined + }; + } + const resolvedSvelteModule: ts.ResolvedModuleFull = { extension: getExtensionFromScriptKind(snapshot && snapshot.scriptKind), resolvedFileName, diff --git a/packages/language-server/src/plugins/typescript/service.ts b/packages/language-server/src/plugins/typescript/service.ts index 8f5720bb0..f94e8e6ff 100644 --- a/packages/language-server/src/plugins/typescript/service.ts +++ b/packages/language-server/src/plugins/typescript/service.ts @@ -736,6 +736,13 @@ async function createLanguageService( } } + // Necessary to be able to resolve export maps that only contain a "svelte" condition without an accompanying "types" condition + // https://www.typescriptlang.org/tsconfig/#customConditions + if (!compilerOptions.customConditions?.includes('svelte')) { + compilerOptions.customConditions = compilerOptions.customConditions ?? []; + compilerOptions.customConditions.push('svelte'); + } + const svelteConfigDiagnostics = checkSvelteInput(parsedConfig); if (svelteConfigDiagnostics.length > 0) { docContext.reportConfigError?.({ diff --git a/packages/language-server/src/plugins/typescript/utils.ts b/packages/language-server/src/plugins/typescript/utils.ts index 62fa6359d..9cb8b7f0f 100644 --- a/packages/language-server/src/plugins/typescript/utils.ts +++ b/packages/language-server/src/plugins/typescript/utils.ts @@ -74,7 +74,13 @@ export function isVirtualSvelteFilePath(filePath: string) { } export function toRealSvelteFilePath(filePath: string) { - return filePath.slice(0, -'.ts'.length); + filePath = filePath.slice(0, -'.ts'.length); + // When a .svelte file referenced inside an exports map of a package.json is tried to be resolved, + // TypeScript will probe for the file with a .d.svelte.ts extension. + if (filePath.endsWith('.d.svelte')) { + filePath = filePath.slice(0, -8) + 'svelte'; + } + return filePath; } export function toVirtualSvelteFilePath(filePath: string) { diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte.only/expectedv2.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte.only/expectedv2.json new file mode 100644 index 000000000..001dff42d --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte.only/expectedv2.json @@ -0,0 +1,19 @@ +[ + { + "code": 2307, + "message": "Cannot find module 'package/y' or its corresponding type declarations.", + "range": { + "start": { + "character": 38, + "line": 3 + }, + "end": { + "character": 49, + "line": 3 + } + }, + "severity": 1, + "source": "ts", + "tags": [] + } +] diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte.only/input.svelte b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte.only/input.svelte new file mode 100644 index 000000000..3a6782e1b --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte.only/input.svelte @@ -0,0 +1,9 @@ + + + + + diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte.only/tsconfig.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte.only/tsconfig.json new file mode 100644 index 000000000..d5e498207 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/exports-map-svelte.only/tsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "module": "esnext", + "target": "esnext", + "moduleResolution": "Bundler" + } +}