@@ -217,9 +217,9 @@ namespace ts.projectSystem {
217217 constructor ( ) {
218218 super ( host ) ;
219219 }
220- enqueueInstallTypingsRequest ( project : server . Project , typingOptions : TypingOptions ) {
220+ enqueueInstallTypingsRequest ( project : server . Project , typingOptions : TypingOptions , unresolvedImports : server . SortedReadonlyArray < string > ) {
221221 enqueueIsCalled = true ;
222- super . enqueueInstallTypingsRequest ( project , typingOptions ) ;
222+ super . enqueueInstallTypingsRequest ( project , typingOptions , unresolvedImports ) ;
223223 }
224224 executeRequest ( requestKind : TI . RequestKind , _requestId : number , _args : string [ ] , _cwd : string , cb : TI . RequestCompletedAction ) : void {
225225 const installedTypings = [ "@types/jquery" ] ;
@@ -319,9 +319,9 @@ namespace ts.projectSystem {
319319 constructor ( ) {
320320 super ( host ) ;
321321 }
322- enqueueInstallTypingsRequest ( project : server . Project , typingOptions : TypingOptions ) {
322+ enqueueInstallTypingsRequest ( project : server . Project , typingOptions : TypingOptions , unresolvedImports : server . SortedReadonlyArray < string > ) {
323323 enqueueIsCalled = true ;
324- super . enqueueInstallTypingsRequest ( project , typingOptions ) ;
324+ super . enqueueInstallTypingsRequest ( project , typingOptions , unresolvedImports ) ;
325325 }
326326 executeRequest ( requestKind : TI . RequestKind , _requestId : number , _args : string [ ] , _cwd : string , cb : TI . RequestCompletedAction ) : void {
327327 const installedTypings : string [ ] = [ ] ;
@@ -724,7 +724,7 @@ namespace ts.projectSystem {
724724 it ( "Malformed package.json should be watched" , ( ) => {
725725 const f = {
726726 path : "/a/b/app.js" ,
727- content : "var x = require('commander') "
727+ content : "var x = 1 "
728728 } ;
729729 const brokenPackageJson = {
730730 path : "/a/b/package.json" ,
@@ -763,6 +763,133 @@ namespace ts.projectSystem {
763763 service . checkNumberOfProjects ( { inferredProjects : 1 } ) ;
764764 checkProjectActualFiles ( service . inferredProjects [ 0 ] , [ f . path , commander . path ] ) ;
765765 } ) ;
766+
767+ it ( "should install typings for unresolved imports" , ( ) => {
768+ const file = {
769+ path : "/a/b/app.js" ,
770+ content : `
771+ import * as fs from "fs";
772+ import * as commander from "commander";`
773+ } ;
774+ const cachePath = "/a/cache" ;
775+ const node = {
776+ path : cachePath + "/node_modules/@types/node/index.d.ts" ,
777+ content : "export let x: number"
778+ } ;
779+ const commander = {
780+ path : cachePath + "/node_modules/@types/commander/index.d.ts" ,
781+ content : "export let y: string"
782+ } ;
783+ const host = createServerHost ( [ file ] ) ;
784+ const installer = new ( class extends Installer {
785+ constructor ( ) {
786+ super ( host , { globalTypingsCacheLocation : cachePath } ) ;
787+ }
788+ executeRequest ( requestKind : TI . RequestKind , _requestId : number , _args : string [ ] , _cwd : string , cb : server . typingsInstaller . RequestCompletedAction ) {
789+ const installedTypings = [ "@types/node" , "@types/commander" ] ;
790+ const typingFiles = [ node , commander ] ;
791+ executeCommand ( this , host , installedTypings , typingFiles , requestKind , cb ) ;
792+ }
793+ } ) ( ) ;
794+ const service = createProjectService ( host , { typingsInstaller : installer } ) ;
795+ service . openClientFile ( file . path ) ;
796+
797+ service . checkNumberOfProjects ( { inferredProjects : 1 } ) ;
798+ checkProjectActualFiles ( service . inferredProjects [ 0 ] , [ file . path ] ) ;
799+
800+ installer . installAll ( [ TI . NpmViewRequest , TI . NpmViewRequest ] , [ TI . NpmInstallRequest ] ) ;
801+
802+ assert . isTrue ( host . fileExists ( node . path ) , "typings for 'node' should be created" ) ;
803+ assert . isTrue ( host . fileExists ( commander . path ) , "typings for 'commander' should be created" ) ;
804+
805+ checkProjectActualFiles ( service . inferredProjects [ 0 ] , [ file . path , node . path , commander . path ] ) ;
806+ } ) ;
807+
808+ it ( "should pick typing names from non-relative unresolved imports" , ( ) => {
809+ const f1 = {
810+ path : "/a/b/app.js" ,
811+ content : `
812+ import * as a from "foo/a/a";
813+ import * as b from "foo/a/b";
814+ import * as c from "foo/a/c";
815+ import * as d from "@bar/router/";
816+ import * as e from "@bar/common/shared";
817+ import * as e from "@bar/common/apps";
818+ import * as f from "./lib"
819+ `
820+ } ;
821+
822+ const host = createServerHost ( [ f1 ] ) ;
823+ const installer = new ( class extends Installer {
824+ constructor ( ) {
825+ super ( host , { globalTypingsCacheLocation : "/tmp" } ) ;
826+ }
827+ executeRequest ( requestKind : TI . RequestKind , _requestId : number , args : string [ ] , _cwd : string , cb : server . typingsInstaller . RequestCompletedAction ) {
828+ if ( requestKind === TI . NpmViewRequest ) {
829+ // args should have only non-scoped packages - scoped packages are not yet supported
830+ assert . deepEqual ( args , [ "foo" ] ) ;
831+ }
832+ executeCommand ( this , host , [ "foo" ] , [ ] , requestKind , cb ) ;
833+ }
834+ } ) ( ) ;
835+ const projectService = createProjectService ( host , { typingsInstaller : installer } ) ;
836+ projectService . openClientFile ( f1 . path ) ;
837+ projectService . checkNumberOfProjects ( { inferredProjects : 1 } ) ;
838+
839+ const proj = projectService . inferredProjects [ 0 ] ;
840+ proj . updateGraph ( ) ;
841+
842+ assert . deepEqual (
843+ proj . getCachedUnresolvedImportsPerFile_TestOnly ( ) . get ( < Path > f1 . path ) ,
844+ [ "foo" , "foo" , "foo" , "@bar/router" , "@bar/common" , "@bar/common" ]
845+ ) ;
846+
847+ installer . installAll ( [ TI . NpmViewRequest ] , [ TI . NpmInstallRequest ] ) ;
848+ } ) ;
849+
850+ it ( "cached unresolved typings are not recomputed if program structure did not change" , ( ) => {
851+ const host = createServerHost ( [ ] ) ;
852+ const session = createSession ( host ) ;
853+ const f = {
854+ path : "/a/app.js" ,
855+ content : `
856+ import * as fs from "fs";
857+ import * as cmd from "commander
858+ `
859+ } ;
860+ session . executeCommand ( < server . protocol . OpenRequest > {
861+ seq : 1 ,
862+ type : "request" ,
863+ command : "open" ,
864+ arguments : {
865+ file : f . path ,
866+ fileContent : f . content
867+ }
868+ } ) ;
869+ const projectService = session . getProjectService ( ) ;
870+ checkNumberOfProjects ( projectService , { inferredProjects : 1 } ) ;
871+ const proj = projectService . inferredProjects [ 0 ] ;
872+ const version1 = proj . getCachedUnresolvedImportsPerFile_TestOnly ( ) . getVersion ( ) ;
873+
874+ // make a change that should not affect the structure of the program
875+ session . executeCommand ( < server . protocol . ChangeRequest > {
876+ seq : 2 ,
877+ type : "request" ,
878+ command : "change" ,
879+ arguments : {
880+ file : f . path ,
881+ insertString : "\nlet x = 1;" ,
882+ line : 2 ,
883+ offset : 0 ,
884+ endLine : 2 ,
885+ endOffset : 0
886+ }
887+ } ) ;
888+ host . checkTimeoutQueueLength ( 1 ) ;
889+ host . runQueuedTimeoutCallbacks ( ) ;
890+ const version2 = proj . getCachedUnresolvedImportsPerFile_TestOnly ( ) . getVersion ( ) ;
891+ assert . equal ( version1 , version2 , "set of unresolved imports should not change" ) ;
892+ } ) ;
766893 } ) ;
767894
768895 describe ( "Validate package name:" , ( ) => {
@@ -820,4 +947,35 @@ namespace ts.projectSystem {
820947 assert . isTrue ( messages . indexOf ( "Package name '; say ‘Hello from TypeScript!’ #' contains non URI safe characters" ) > 0 , "should find package with invalid name" ) ;
821948 } ) ;
822949 } ) ;
950+
951+ describe ( "discover typings" , ( ) => {
952+ it ( "should return node for core modules" , ( ) => {
953+ const f = {
954+ path : "/a/b/app.js" ,
955+ content : ""
956+ } ;
957+ const host = createServerHost ( [ f ] ) ;
958+ const cache = createMap < string > ( ) ;
959+ for ( const name of JsTyping . nodeCoreModuleList ) {
960+ const result = JsTyping . discoverTypings ( host , [ f . path ] , getDirectoryPath ( < Path > f . path ) , /*safeListPath*/ undefined , cache , { enableAutoDiscovery : true } , [ name , "somename" ] ) ;
961+ assert . deepEqual ( result . newTypingNames . sort ( ) , [ "node" , "somename" ] ) ;
962+ }
963+ } ) ;
964+
965+ it ( "should use cached locaitons" , ( ) => {
966+ const f = {
967+ path : "/a/b/app.js" ,
968+ content : ""
969+ } ;
970+ const node = {
971+ path : "/a/b/node.d.ts" ,
972+ content : ""
973+ } ;
974+ const host = createServerHost ( [ f , node ] ) ;
975+ const cache = createMap < string > ( { "node" : node . path } ) ;
976+ const result = JsTyping . discoverTypings ( host , [ f . path ] , getDirectoryPath ( < Path > f . path ) , /*safeListPath*/ undefined , cache , { enableAutoDiscovery : true } , [ "fs" , "bar" ] ) ;
977+ assert . deepEqual ( result . cachedTypingPaths , [ node . path ] ) ;
978+ assert . deepEqual ( result . newTypingNames , [ "bar" ] ) ;
979+ } ) ;
980+ } ) ;
823981}
0 commit comments