@@ -44,6 +44,7 @@ import { findContainingNode, getComponentAtPosition, isPartOfImportStatement } f
4444
4545export interface CompletionEntryWithIdentifier extends ts . CompletionEntry , TextDocumentIdentifier {
4646 position : Position ;
47+ __is_sveltekit$typeImport ?: boolean ;
4748}
4849
4950type validTriggerCharacter = '.' | '"' | "'" | '`' | '/' | '@' | '<' | '#' ;
@@ -257,6 +258,42 @@ export class CompletionsProviderImpl implements CompletionsProvider<CompletionEn
257258 . map ( ( comp ) => this . fixTextEditRange ( wordRangeStartPosition , comp ) )
258259 . concat ( eventAndSlotLetCompletions ) ;
259260
261+ // Add ./$types imports for SvelteKit since TypeScript is bad at it
262+ if ( basename ( filePath ) . startsWith ( '+' ) ) {
263+ const $typeImports = new Map < string , CompletionItem > ( ) ;
264+ for ( const c of completionItems ) {
265+ if ( c . data . source ?. includes ( '.svelte-kit/types' ) ) {
266+ $typeImports . set ( c . label , c ) ;
267+ }
268+ }
269+ for ( const $typeImport of $typeImports . values ( ) ) {
270+ // resolve path from filePath to svelte-kit/types
271+ // src/routes/foo/+page.svelte -> .svelte-kit/types/foo/$types.d.ts
272+ const routesFolder = document . config ?. kit ?. files ?. routes || 'src/routes' ;
273+ const relativeFilePath = filePath . split ( routesFolder ) [ 1 ] ?. slice ( 1 ) ;
274+ if ( relativeFilePath ) {
275+ completionItems . push ( {
276+ ...$typeImport ,
277+ // Ensure it's sorted above the other imports
278+ sortText : ! isNaN ( Number ( $typeImport . sortText ) )
279+ ? String ( Number ( $typeImport . sortText ) - 1 )
280+ : $typeImport . sortText ,
281+ data : {
282+ ...$typeImport . data ,
283+ __is_sveltekit$typeImport : true ,
284+ source :
285+ $typeImport . data . source . split ( '.svelte-kit/types' ) [ 0 ] +
286+ // note the missing .d.ts at the end - TS wants it that way for some reason
287+ `.svelte-kit/types/${ routesFolder } /${ dirname (
288+ relativeFilePath
289+ ) } /$types`,
290+ data : undefined
291+ }
292+ } ) ;
293+ }
294+ }
295+ }
296+
260297 const completionList = CompletionList . create ( completionItems , ! ! tsDoc . parserError ) ;
261298 this . lastCompletion = { key : document . getFilePath ( ) || '' , position, completionList } ;
262299
@@ -516,7 +553,7 @@ export class CompletionsProviderImpl implements CompletionsProvider<CompletionEn
516553 completionItem : AppCompletionItem < CompletionEntryWithIdentifier > ,
517554 cancellationToken ?: CancellationToken
518555 ) : Promise < AppCompletionItem < CompletionEntryWithIdentifier > > {
519- let { data : comp } = completionItem ;
556+ const { data : comp } = completionItem ;
520557 const { tsDoc, lang, userPreferences } = await this . lsAndTsDocResolver . getLSAndTSDoc (
521558 document
522559 ) ;
@@ -527,44 +564,21 @@ export class CompletionsProviderImpl implements CompletionsProvider<CompletionEn
527564 return completionItem ;
528565 }
529566
567+ const is$typeImport = ! ! comp . __is_sveltekit$typeImport ;
568+
530569 const errorPreventingUserPreferences = comp . source ?. endsWith ( '.svelte' )
531570 ? this . fixUserPreferencesForSvelteComponentImport ( userPreferences )
532571 : userPreferences ;
533572
534- let is$typeImport = false ;
535- const originalComp = { ...comp } ;
536- if ( basename ( filePath ) . startsWith ( '+' ) && comp . source ?. includes ( '.svelte-kit/types' ) ) {
537- // resolve path from filePath to svelte-kit/types
538- // src/routes/foo/+page.svelte -> .svelte-kit/types/foo/$types.d.ts
539- const routesFolder = document . config ?. kit ?. files ?. routes || 'src/routes' ;
540- const relativeFilePath = filePath . split ( routesFolder ) [ 1 ] ?. slice ( 1 ) ;
541- if ( relativeFilePath ) {
542- is$typeImport = true ;
543- comp . source =
544- comp . source . split ( '.svelte-kit/types' ) [ 0 ] +
545- // note the missing .d.ts at the end - TS wants it that way for some reason
546- `.svelte-kit/types/${ routesFolder } /${ dirname ( relativeFilePath ) } /$types` ;
547- comp . data = undefined ;
548- }
549- }
550-
551- const getDetail = ( ) =>
552- lang . getCompletionEntryDetails (
553- filePath ,
554- tsDoc . offsetAt ( tsDoc . getGeneratedPosition ( comp ! . position ) ) ,
555- comp ! . name ,
556- { } ,
557- comp ! . source ,
558- errorPreventingUserPreferences ,
559- comp ! . data
560- ) ;
561- let detail = getDetail ( ) ;
562- if ( ! detail && is$typeImport ) {
563- // try again
564- is$typeImport = false ;
565- comp = originalComp ;
566- detail = getDetail ( ) ;
567- }
573+ const detail = lang . getCompletionEntryDetails (
574+ filePath ,
575+ tsDoc . offsetAt ( tsDoc . getGeneratedPosition ( comp ! . position ) ) ,
576+ comp ! . name ,
577+ { } ,
578+ comp ! . source ,
579+ errorPreventingUserPreferences ,
580+ comp ! . data
581+ ) ;
568582
569583 if ( detail ) {
570584 const { detail : itemDetail , documentation : itemDocumentation } =
0 commit comments