@@ -101,6 +101,10 @@ export interface TsConfigInfo {
101
101
extendedConfigPaths ?: Set < string > ;
102
102
}
103
103
104
+ enum TsconfigSvelteDiagnostics {
105
+ NO_SVELTE_INPUT = 100_001
106
+ }
107
+
104
108
const maxProgramSizeForNonTsFiles = 20 * 1024 * 1024 ; // 20 MB
105
109
const services = new FileMap < Promise < LanguageServiceContainer > > ( ) ;
106
110
const serviceSizeMap = new FileMap < number > ( ) ;
@@ -173,12 +177,23 @@ export async function getService(
173
177
return service ;
174
178
}
175
179
180
+ // First try to find a service whose includes config matches our file
176
181
const defaultService = await findDefaultServiceForFile ( service , triedTsConfig ) ;
177
182
if ( defaultService ) {
178
183
configFileForOpenFiles . set ( path , defaultService . tsconfigPath ) ;
179
184
return defaultService ;
180
185
}
181
186
187
+ // If no such service found, see if the file is part of any existing service indirectly.
188
+ // This can happen if the includes doesn't match the file but it was imported from one of the included files.
189
+ for ( const configPath of triedTsConfig ) {
190
+ const service = await getConfiguredService ( configPath ) ;
191
+ const ls = service . getService ( ) ;
192
+ if ( ls . getProgram ( ) ?. getSourceFile ( path ) ) {
193
+ return service ;
194
+ }
195
+ }
196
+
182
197
tsconfigPath = '' ;
183
198
}
184
199
@@ -217,6 +232,8 @@ export async function getService(
217
232
return ;
218
233
}
219
234
235
+ triedTsConfig . add ( service . tsconfigPath ) ;
236
+
220
237
// TODO: maybe add support for ts 5.6's ancestor searching
221
238
return findDefaultFromProjectReferences ( service , triedTsConfig ) ;
222
239
}
@@ -315,6 +332,8 @@ async function createLanguageService(
315
332
316
333
const projectConfig = getParsedConfig ( ) ;
317
334
const { options : compilerOptions , raw, errors : configErrors } = projectConfig ;
335
+ const allowJs = compilerOptions . allowJs ?? ! ! compilerOptions . checkJs ;
336
+ const virtualDocuments = new FileMap < Document > ( tsSystem . useCaseSensitiveFileNames ) ;
318
337
319
338
const getCanonicalFileName = createGetCanonicalFileName ( tsSystem . useCaseSensitiveFileNames ) ;
320
339
watchWildCardDirectories ( projectConfig ) ;
@@ -360,6 +379,7 @@ async function createLanguageService(
360
379
let languageServiceReducedMode = false ;
361
380
let projectVersion = 0 ;
362
381
let dirty = projectConfig . fileNames . length > 0 ;
382
+ let skipSvelteInputCheck = ! tsconfigPath ;
363
383
364
384
const host : ts . LanguageServiceHost = {
365
385
log : ( message ) => Logger . debug ( `[ts] ${ message } ` ) ,
@@ -529,12 +549,19 @@ async function createLanguageService(
529
549
return prevSnapshot ;
530
550
}
531
551
552
+ const newSnapshot = DocumentSnapshot . fromDocument ( document , transformationConfig ) ;
553
+
532
554
if ( ! prevSnapshot ) {
533
555
svelteModuleLoader . deleteUnresolvedResolutionsFromCache ( filePath ) ;
556
+ if ( configFileForOpenFiles . get ( filePath ) === '' && services . size > 1 ) {
557
+ configFileForOpenFiles . delete ( filePath ) ;
558
+ }
559
+ } else if ( prevSnapshot . scriptKind !== newSnapshot . scriptKind && ! allowJs ) {
560
+ // if allowJs is false, we need to invalid the cache so that js svelte files can be loaded through module resolution
561
+ svelteModuleLoader . deleteFromModuleCache ( filePath ) ;
562
+ configFileForOpenFiles . delete ( filePath ) ;
534
563
}
535
564
536
- const newSnapshot = DocumentSnapshot . fromDocument ( document , transformationConfig ) ;
537
-
538
565
snapshotManager . set ( filePath , newSnapshot ) ;
539
566
540
567
return newSnapshot ;
@@ -640,14 +667,22 @@ async function createLanguageService(
640
667
: snapshotManager . getProjectFileNames ( ) ;
641
668
const canonicalProjectFileNames = new Set ( projectFiles . map ( getCanonicalFileName ) ) ;
642
669
670
+ // We only assign project files (i.e. those found through includes config) and virtual files to getScriptFileNames.
671
+ // We don't to include other client files otherwise they stay in the program and are never removed
672
+ const clientFiles = tsconfigPath
673
+ ? Array . from ( virtualDocuments . values ( ) )
674
+ . map ( ( v ) => v . getFilePath ( ) )
675
+ . filter ( isNotNullOrUndefined )
676
+ : snapshotManager . getClientFileNames ( ) ;
677
+
643
678
return Array . from (
644
679
new Set ( [
645
680
...projectFiles ,
646
681
// project file is read from the file system so it's more likely to have
647
682
// the correct casing
648
- ...snapshotManager
649
- . getClientFileNames ( )
650
- . filter ( ( file ) => ! canonicalProjectFileNames . has ( getCanonicalFileName ( file ) ) ) ,
683
+ ...clientFiles . filter (
684
+ ( file ) => ! canonicalProjectFileNames . has ( getCanonicalFileName ( file ) )
685
+ ) ,
651
686
...svelteTsxFiles
652
687
] )
653
688
) ;
@@ -736,20 +771,6 @@ async function createLanguageService(
736
771
}
737
772
}
738
773
739
- const svelteConfigDiagnostics = checkSvelteInput ( parsedConfig ) ;
740
- if ( svelteConfigDiagnostics . length > 0 ) {
741
- docContext . reportConfigError ?.( {
742
- uri : pathToUrl ( tsconfigPath ) ,
743
- diagnostics : svelteConfigDiagnostics . map ( ( d ) => ( {
744
- message : d . messageText as string ,
745
- range : { start : { line : 0 , character : 0 } , end : { line : 0 , character : 0 } } ,
746
- severity : ts . DiagnosticCategory . Error ,
747
- source : 'svelte'
748
- } ) )
749
- } ) ;
750
- parsedConfig . errors . push ( ...svelteConfigDiagnostics ) ;
751
- }
752
-
753
774
return {
754
775
...parsedConfig ,
755
776
fileNames : parsedConfig . fileNames . map ( normalizePath ) ,
@@ -758,22 +779,32 @@ async function createLanguageService(
758
779
} ;
759
780
}
760
781
761
- function checkSvelteInput ( config : ts . ParsedCommandLine ) {
782
+ function checkSvelteInput ( program : ts . Program | undefined , config : ts . ParsedCommandLine ) {
762
783
if ( ! tsconfigPath || config . raw . references || config . raw . files ) {
763
784
return [ ] ;
764
785
}
765
786
766
- const svelteFiles = config . fileNames . filter ( isSvelteFilePath ) ;
767
- if ( svelteFiles . length > 0 ) {
787
+ const configFileName = basename ( tsconfigPath ) ;
788
+ // Only report to possible nearest config file since referenced project might not be a svelte project
789
+ if ( configFileName !== 'tsconfig.json' && configFileName !== 'jsconfig.json' ) {
790
+ return [ ] ;
791
+ }
792
+
793
+ const hasSvelteFiles =
794
+ config . fileNames . some ( isSvelteFilePath ) ||
795
+ program ?. getSourceFiles ( ) . some ( ( file ) => isSvelteFilePath ( file . fileName ) ) ;
796
+
797
+ if ( hasSvelteFiles ) {
768
798
return [ ] ;
769
799
}
800
+
770
801
const { include, exclude } = config . raw ;
771
802
const inputText = JSON . stringify ( include ) ;
772
803
const excludeText = JSON . stringify ( exclude ) ;
773
804
const svelteConfigDiagnostics : ts . Diagnostic [ ] = [
774
805
{
775
806
category : ts . DiagnosticCategory . Error ,
776
- code : 0 ,
807
+ code : TsconfigSvelteDiagnostics . NO_SVELTE_INPUT ,
777
808
file : undefined ,
778
809
start : undefined ,
779
810
length : undefined ,
@@ -933,6 +964,28 @@ async function createLanguageService(
933
964
dirty = false ;
934
965
compilerHost = undefined ;
935
966
967
+ if ( ! skipSvelteInputCheck ) {
968
+ const svelteConfigDiagnostics = checkSvelteInput ( program , projectConfig ) ;
969
+ const codes = svelteConfigDiagnostics . map ( ( d ) => d . code ) ;
970
+ if ( ! svelteConfigDiagnostics . length ) {
971
+ // stop checking once it passed once
972
+ skipSvelteInputCheck = true ;
973
+ }
974
+ // report even if empty to clear previous diagnostics
975
+ docContext . reportConfigError ?.( {
976
+ uri : pathToUrl ( tsconfigPath ) ,
977
+ diagnostics : svelteConfigDiagnostics . map ( ( d ) => ( {
978
+ message : d . messageText as string ,
979
+ range : { start : { line : 0 , character : 0 } , end : { line : 0 , character : 0 } } ,
980
+ severity : ts . DiagnosticCategory . Error ,
981
+ source : 'svelte'
982
+ } ) )
983
+ } ) ;
984
+ projectConfig . errors = projectConfig . errors
985
+ . filter ( ( e ) => ! codes . includes ( e . code ) )
986
+ . concat ( svelteConfigDiagnostics ) ;
987
+ }
988
+
936
989
// https://github.com/microsoft/TypeScript/blob/23faef92703556567ddbcb9afb893f4ba638fc20/src/server/project.ts#L1624
937
990
// host.getCachedExportInfoMap will create the cache if it doesn't exist
938
991
// so we need to check the property instead
@@ -1135,6 +1188,7 @@ async function createLanguageService(
1135
1188
if ( ! filePath ) {
1136
1189
return ;
1137
1190
}
1191
+ virtualDocuments . set ( filePath , document ) ;
1138
1192
configFileForOpenFiles . set ( filePath , tsconfigPath || workspacePath ) ;
1139
1193
updateSnapshot ( document ) ;
1140
1194
scheduleUpdate ( filePath ) ;
0 commit comments