1+ import { basename , dirname } from 'path' ;
12import ts from 'typescript' ;
23import {
34 CancellationToken ,
@@ -515,7 +516,7 @@ export class CompletionsProviderImpl implements CompletionsProvider<CompletionEn
515516 completionItem : AppCompletionItem < CompletionEntryWithIdentifier > ,
516517 cancellationToken ?: CancellationToken
517518 ) : Promise < AppCompletionItem < CompletionEntryWithIdentifier > > {
518- const { data : comp } = completionItem ;
519+ let { data : comp } = completionItem ;
519520 const { tsDoc, lang, userPreferences } = await this . lsAndTsDocResolver . getLSAndTSDoc (
520521 document
521522 ) ;
@@ -530,19 +531,44 @@ export class CompletionsProviderImpl implements CompletionsProvider<CompletionEn
530531 ? this . fixUserPreferencesForSvelteComponentImport ( userPreferences )
531532 : userPreferences ;
532533
533- const detail = lang . getCompletionEntryDetails (
534- filePath ,
535- tsDoc . offsetAt ( tsDoc . getGeneratedPosition ( comp . position ) ) ,
536- comp . name ,
537- { } ,
538- comp . source ,
539- errorPreventingUserPreferences ,
540- comp . data
541- ) ;
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+ }
542568
543569 if ( detail ) {
544570 const { detail : itemDetail , documentation : itemDocumentation } =
545- this . getCompletionDocument ( detail ) ;
571+ this . getCompletionDocument ( detail , is$typeImport ) ;
546572
547573 completionItem . detail = itemDetail ;
548574 completionItem . documentation = itemDocumentation ;
@@ -562,7 +588,8 @@ export class CompletionsProviderImpl implements CompletionsProvider<CompletionEn
562588 tsDoc ,
563589 change ,
564590 isImport ,
565- comp . position
591+ comp . position ,
592+ is$typeImport
566593 )
567594 ) ;
568595 }
@@ -576,12 +603,16 @@ export class CompletionsProviderImpl implements CompletionsProvider<CompletionEn
576603 return completionItem ;
577604 }
578605
579- private getCompletionDocument ( compDetail : ts . CompletionEntryDetails ) {
606+ private getCompletionDocument ( compDetail : ts . CompletionEntryDetails , is$typeImport : boolean ) {
580607 const { sourceDisplay, documentation : tsDocumentation , displayParts, tags } = compDetail ;
581608 let detail : string = changeSvelteComponentName ( ts . displayPartsToString ( displayParts ) ) ;
582609
583610 if ( sourceDisplay ) {
584- const importPath = ts . displayPartsToString ( sourceDisplay ) ;
611+ let importPath = ts . displayPartsToString ( sourceDisplay ) ;
612+ if ( is$typeImport ) {
613+ // Take into account Node16 moduleResolution
614+ importPath = `'./$types${ importPath . endsWith ( '.js' ) ? '.js' : '' } '` ;
615+ }
585616 detail = `Auto import from ${ importPath } \n${ detail } ` ;
586617 }
587618
@@ -601,15 +632,17 @@ export class CompletionsProviderImpl implements CompletionsProvider<CompletionEn
601632 snapshot : SvelteDocumentSnapshot ,
602633 changes : ts . FileTextChanges ,
603634 isImport : boolean ,
604- originalTriggerPosition : Position
635+ originalTriggerPosition : Position ,
636+ is$typeImport ?: boolean
605637 ) : TextEdit [ ] {
606638 return changes . textChanges . map ( ( change ) =>
607639 this . codeActionChangeToTextEdit (
608640 doc ,
609641 snapshot ,
610642 change ,
611643 isImport ,
612- originalTriggerPosition
644+ originalTriggerPosition ,
645+ is$typeImport
613646 )
614647 ) ;
615648 }
@@ -619,11 +652,13 @@ export class CompletionsProviderImpl implements CompletionsProvider<CompletionEn
619652 snapshot : SvelteDocumentSnapshot ,
620653 change : ts . TextChange ,
621654 isImport : boolean ,
622- originalTriggerPosition : Position
655+ originalTriggerPosition : Position ,
656+ is$typeImport ?: boolean
623657 ) : TextEdit {
624658 change . newText = this . changeComponentImport (
625659 change . newText ,
626- isInScript ( originalTriggerPosition , doc )
660+ isInScript ( originalTriggerPosition , doc ) ,
661+ is$typeImport
627662 ) ;
628663
629664 const scriptTagInfo = snapshot . scriptInfo || snapshot . moduleScriptInfo ;
@@ -701,7 +736,19 @@ export class CompletionsProviderImpl implements CompletionsProvider<CompletionEn
701736 return className . endsWith ( '__SvelteComponent_' ) ;
702737 }
703738
704- private changeComponentImport ( importText : string , actionTriggeredInScript : boolean ) {
739+ private changeComponentImport (
740+ importText : string ,
741+ actionTriggeredInScript : boolean ,
742+ is$typeImport ?: boolean
743+ ) {
744+ if ( is$typeImport && importText . startsWith ( 'import ' ) ) {
745+ // Take into account Node16 moduleResolution
746+ return importText . replace (
747+ / ( [ ' " ] ) ( .+ ?) [ ' " ] / ,
748+ ( _match , quote , path ) =>
749+ `${ quote } ./$types${ path . endsWith ( '.js' ) ? '.js' : '' } ${ quote } `
750+ ) ;
751+ }
705752 const changedName = changeSvelteComponentName ( importText ) ;
706753 if ( importText !== changedName || ! actionTriggeredInScript ) {
707754 // For some reason, TS sometimes adds the `type` modifier. Remove it
0 commit comments