@@ -11,12 +11,12 @@ import { ensureRealSvelteFilePath, isSvelteFilePath, isVirtualSvelteFilePath } f
1111class ModuleResolutionCache {
1212 constructor ( private readonly projectService : ts . server . ProjectService ) { }
1313
14- private cache = new Map < string , ts . ResolvedModuleFull > ( ) ;
14+ private cache = new Map < string , ts . ResolvedModuleFull | null > ( ) ;
1515
1616 /**
1717 * Tries to get a cached module.
1818 */
19- get ( moduleName : string , containingFile : string ) : ts . ResolvedModuleFull | undefined {
19+ get ( moduleName : string , containingFile : string ) : ts . ResolvedModuleFull | null | undefined {
2020 return this . cache . get ( this . getKey ( moduleName , containingFile ) ) ;
2121 }
2222
@@ -28,10 +28,14 @@ class ModuleResolutionCache {
2828 containingFile : string ,
2929 resolvedModule : ts . ResolvedModuleFull | undefined
3030 ) {
31- if ( ! resolvedModule ) {
31+ if ( ! resolvedModule && moduleName [ 0 ] === '.' ) {
32+ // We cache unresolved modules for non-relative imports, too, because it's very likely that they don't change
33+ // and we don't want to resolve them every time. If they do change, the original resolution mode will notice
34+ // most of the time, and the only time this would result in a stale cache entry is if a node_modules package
35+ // is added with a "svelte" condition and no "types" condition, which is rare enough.
3236 return ;
3337 }
34- this . cache . set ( this . getKey ( moduleName , containingFile ) , resolvedModule ) ;
38+ this . cache . set ( this . getKey ( moduleName , containingFile ) , resolvedModule ?? null ) ;
3539 }
3640
3741 /**
@@ -42,6 +46,7 @@ class ModuleResolutionCache {
4246 resolvedModuleName = this . projectService . toCanonicalFileName ( resolvedModuleName ) ;
4347 this . cache . forEach ( ( val , key ) => {
4448 if (
49+ val &&
4550 this . projectService . toCanonicalFileName ( val . resolvedFileName ) === resolvedModuleName
4651 ) {
4752 this . cache . delete ( key ) ;
@@ -87,6 +92,8 @@ export function patchModuleLoader(
8792 if ( lsHost . resolveModuleNameLiterals ) {
8893 lsHost . resolveModuleNameLiterals = resolveModuleNameLiterals ;
8994 } else {
95+ // TODO do we need to keep this around? We're requiring 5.0 now, so TS doesn't need it,
96+ // but would this break when other TS plugins are used and we no longer provide it?
9097 lsHost . resolveModuleNames = resolveModuleNames ;
9198 }
9299
@@ -139,7 +146,9 @@ export function patchModuleLoader(
139146 return resolved . map ( ( tsResolvedModule , idx ) => {
140147 const moduleName = moduleNames [ idx ] ;
141148 if (
142- ! isSvelteFilePath ( moduleName ) ||
149+ // Only recheck relative Svelte imports or unresolved non-relative paths (which hint at node_modules,
150+ // where an exports map with "svelte" but not "types" could be present)
151+ ( ! isSvelteFilePath ( moduleName ) && ( moduleName [ 0 ] === '.' || tsResolvedModule ) ) ||
143152 // corresponding .d.ts files take precedence over .svelte files
144153 tsResolvedModule ?. resolvedFileName . endsWith ( '.d.ts' ) ||
145154 tsResolvedModule ?. resolvedFileName . endsWith ( '.d.svelte.ts' )
@@ -165,7 +174,8 @@ export function patchModuleLoader(
165174 const svelteResolvedModule = typescript . resolveModuleName (
166175 name ,
167176 containingFile ,
168- compilerOptions ,
177+ // customConditions makes the TS algorithm look at the "svelte" condition in exports maps
178+ { ...compilerOptions , customConditions : [ 'svelte' ] } ,
169179 svelteSys
170180 // don't set mode or else .svelte imports couldn't be resolved
171181 ) . resolvedModule ;
@@ -225,19 +235,22 @@ export function patchModuleLoader(
225235
226236 return resolved . map ( ( tsResolvedModule , idx ) => {
227237 const moduleName = moduleLiterals [ idx ] . text ;
238+ const resolvedModule = tsResolvedModule . resolvedModule ;
228239
229240 if (
230- ! isSvelteFilePath ( moduleName ) ||
241+ // Only recheck relative Svelte imports or unresolved non-relative paths (which hint at node_modules,
242+ // where an exports map with "svelte" but not "types" could be present)
243+ ( ! isSvelteFilePath ( moduleName ) && ( moduleName [ 0 ] === '.' || resolvedModule ) ) ||
231244 // corresponding .d.ts files take precedence over .svelte files
232- tsResolvedModule ?. resolvedModule ?. resolvedFileName . endsWith ( '.d.ts' ) ||
233- tsResolvedModule ?. resolvedModule ?. resolvedFileName . endsWith ( '.d.svelte.ts' )
245+ resolvedModule ?. resolvedFileName . endsWith ( '.d.ts' ) ||
246+ resolvedModule ?. resolvedFileName . endsWith ( '.d.svelte.ts' )
234247 ) {
235248 return tsResolvedModule ;
236249 }
237250
238251 const result = resolveSvelteModuleNameFromCache ( moduleName , containingFile , options ) ;
239252 // .svelte takes precedence over .svelte.ts etc
240- return result ?? tsResolvedModule ;
253+ return result . resolvedModule ? result : tsResolvedModule ;
241254 } ) ;
242255 }
243256
@@ -247,13 +260,29 @@ export function patchModuleLoader(
247260 options : ts . CompilerOptions
248261 ) {
249262 const cachedModule = moduleCache . get ( moduleName , containingFile ) ;
250- if ( cachedModule ) {
263+ if ( typeof cachedModule === 'object' ) {
251264 return {
252- resolvedModule : cachedModule
265+ resolvedModule : cachedModule ?? undefined
253266 } ;
254267 }
255268
256269 const resolvedModule = resolveSvelteModuleName ( moduleName , containingFile , options ) ;
270+
271+ // Align with TypeScript behavior: If the Svelte file is not using TypeScript,
272+ // mark it as unresolved so that people need to provide a .d.ts file.
273+ // For backwards compatibility we're not doing this for files from packages
274+ // without an exports map, because that may break too many existing projects.
275+ if (
276+ resolvedModule ?. isExternalLibraryImport && // TODO how to check this is not from a non-exports map?
277+ // TODO check what happens if this resolves to a real .d.svelte.ts file
278+ resolvedModule . extension === '.ts' // this tells us it's from an exports map
279+ ) {
280+ moduleCache . set ( moduleName , containingFile , undefined ) ;
281+ return {
282+ resolvedModule : undefined
283+ } ;
284+ }
285+
257286 moduleCache . set ( moduleName , containingFile , resolvedModule ) ;
258287 return {
259288 resolvedModule : resolvedModule
0 commit comments