@@ -563,4 +563,287 @@ namespace ts.projectSystem {
563563 assert . isTrue ( diagsAfterUpdate . length === 0 ) ;
564564 } ) ;
565565 } ) ;
566+
567+ describe ( "tsserver:: Project Errors for Configure file diagnostics events" , ( ) => {
568+ function getUnknownCompilerOptionDiagnostic ( configFile : File , prop : string ) : ConfigFileDiagnostic {
569+ const d = Diagnostics . Unknown_compiler_option_0 ;
570+ const start = configFile . content . indexOf ( prop ) - 1 ; // start at "prop"
571+ return {
572+ fileName : configFile . path ,
573+ start,
574+ length : prop . length + 2 ,
575+ messageText : formatStringFromArgs ( d . message , [ prop ] ) ,
576+ category : d . category ,
577+ code : d . code ,
578+ reportsUnnecessary : undefined
579+ } ;
580+ }
581+
582+ function getFileNotFoundDiagnostic ( configFile : File , relativeFileName : string ) : ConfigFileDiagnostic {
583+ const findString = `{"path":"./${ relativeFileName } "}` ;
584+ const d = Diagnostics . File_0_not_found ;
585+ const start = configFile . content . indexOf ( findString ) ;
586+ return {
587+ fileName : configFile . path ,
588+ start,
589+ length : findString . length ,
590+ messageText : formatStringFromArgs ( d . message , [ `${ getDirectoryPath ( configFile . path ) } /${ relativeFileName } ` ] ) ,
591+ category : d . category ,
592+ code : d . code ,
593+ reportsUnnecessary : undefined
594+ } ;
595+ }
596+
597+ it ( "are generated when the config file has errors" , ( ) => {
598+ const file : File = {
599+ path : "/a/b/app.ts" ,
600+ content : "let x = 10"
601+ } ;
602+ const configFile : File = {
603+ path : "/a/b/tsconfig.json" ,
604+ content : `{
605+ "compilerOptions": {
606+ "foo": "bar",
607+ "allowJS": true
608+ }
609+ }`
610+ } ;
611+ const serverEventManager = new TestServerEventManager ( [ file , libFile , configFile ] ) ;
612+ openFilesForSession ( [ file ] , serverEventManager . session ) ;
613+ serverEventManager . checkSingleConfigFileDiagEvent ( configFile . path , file . path , [
614+ getUnknownCompilerOptionDiagnostic ( configFile , "foo" ) ,
615+ getUnknownCompilerOptionDiagnostic ( configFile , "allowJS" )
616+ ] ) ;
617+ } ) ;
618+
619+ it ( "are generated when the config file doesn't have errors" , ( ) => {
620+ const file : File = {
621+ path : "/a/b/app.ts" ,
622+ content : "let x = 10"
623+ } ;
624+ const configFile : File = {
625+ path : "/a/b/tsconfig.json" ,
626+ content : `{
627+ "compilerOptions": {}
628+ }`
629+ } ;
630+ const serverEventManager = new TestServerEventManager ( [ file , libFile , configFile ] ) ;
631+ openFilesForSession ( [ file ] , serverEventManager . session ) ;
632+ serverEventManager . checkSingleConfigFileDiagEvent ( configFile . path , file . path , emptyArray ) ;
633+ } ) ;
634+
635+ it ( "are generated when the config file changes" , ( ) => {
636+ const file : File = {
637+ path : "/a/b/app.ts" ,
638+ content : "let x = 10"
639+ } ;
640+ const configFile = {
641+ path : "/a/b/tsconfig.json" ,
642+ content : `{
643+ "compilerOptions": {}
644+ }`
645+ } ;
646+
647+ const files = [ file , libFile , configFile ] ;
648+ const serverEventManager = new TestServerEventManager ( files ) ;
649+ openFilesForSession ( [ file ] , serverEventManager . session ) ;
650+ serverEventManager . checkSingleConfigFileDiagEvent ( configFile . path , file . path , emptyArray ) ;
651+
652+ configFile . content = `{
653+ "compilerOptions": {
654+ "haha": 123
655+ }
656+ }` ;
657+ serverEventManager . host . reloadFS ( files ) ;
658+ serverEventManager . host . runQueuedTimeoutCallbacks ( ) ;
659+ serverEventManager . checkSingleConfigFileDiagEvent ( configFile . path , configFile . path , [
660+ getUnknownCompilerOptionDiagnostic ( configFile , "haha" )
661+ ] ) ;
662+
663+ configFile . content = `{
664+ "compilerOptions": {}
665+ }` ;
666+ serverEventManager . host . reloadFS ( files ) ;
667+ serverEventManager . host . runQueuedTimeoutCallbacks ( ) ;
668+ serverEventManager . checkSingleConfigFileDiagEvent ( configFile . path , configFile . path , emptyArray ) ;
669+ } ) ;
670+
671+ it ( "are not generated when the config file does not include file opened and config file has errors" , ( ) => {
672+ const file : File = {
673+ path : "/a/b/app.ts" ,
674+ content : "let x = 10"
675+ } ;
676+ const file2 : File = {
677+ path : "/a/b/test.ts" ,
678+ content : "let x = 10"
679+ } ;
680+ const configFile : File = {
681+ path : "/a/b/tsconfig.json" ,
682+ content : `{
683+ "compilerOptions": {
684+ "foo": "bar",
685+ "allowJS": true
686+ },
687+ "files": ["app.ts"]
688+ }`
689+ } ;
690+ const serverEventManager = new TestServerEventManager ( [ file , file2 , libFile , configFile ] ) ;
691+ openFilesForSession ( [ file2 ] , serverEventManager . session ) ;
692+ serverEventManager . hasZeroEvent ( "configFileDiag" ) ;
693+ } ) ;
694+
695+ it ( "are not generated when the config file has errors but suppressDiagnosticEvents is true" , ( ) => {
696+ const file : File = {
697+ path : "/a/b/app.ts" ,
698+ content : "let x = 10"
699+ } ;
700+ const configFile : File = {
701+ path : "/a/b/tsconfig.json" ,
702+ content : `{
703+ "compilerOptions": {
704+ "foo": "bar",
705+ "allowJS": true
706+ }
707+ }`
708+ } ;
709+ const serverEventManager = new TestServerEventManager ( [ file , libFile , configFile ] , /*suppressDiagnosticEvents*/ true ) ;
710+ openFilesForSession ( [ file ] , serverEventManager . session ) ;
711+ serverEventManager . hasZeroEvent ( "configFileDiag" ) ;
712+ } ) ;
713+
714+ it ( "are not generated when the config file does not include file opened and doesnt contain any errors" , ( ) => {
715+ const file : File = {
716+ path : "/a/b/app.ts" ,
717+ content : "let x = 10"
718+ } ;
719+ const file2 : File = {
720+ path : "/a/b/test.ts" ,
721+ content : "let x = 10"
722+ } ;
723+ const configFile : File = {
724+ path : "/a/b/tsconfig.json" ,
725+ content : `{
726+ "files": ["app.ts"]
727+ }`
728+ } ;
729+
730+ const serverEventManager = new TestServerEventManager ( [ file , file2 , libFile , configFile ] ) ;
731+ openFilesForSession ( [ file2 ] , serverEventManager . session ) ;
732+ serverEventManager . hasZeroEvent ( "configFileDiag" ) ;
733+ } ) ;
734+
735+ it ( "contains the project reference errors" , ( ) => {
736+ const file : File = {
737+ path : "/a/b/app.ts" ,
738+ content : "let x = 10"
739+ } ;
740+ const noSuchTsconfig = "no-such-tsconfig.json" ;
741+ const configFile : File = {
742+ path : "/a/b/tsconfig.json" ,
743+ content : `{
744+ "files": ["app.ts"],
745+ "references": [{"path":"./${ noSuchTsconfig } "}]
746+ }`
747+ } ;
748+
749+ const serverEventManager = new TestServerEventManager ( [ file , libFile , configFile ] ) ;
750+ openFilesForSession ( [ file ] , serverEventManager . session ) ;
751+ serverEventManager . checkSingleConfigFileDiagEvent ( configFile . path , file . path , [
752+ getFileNotFoundDiagnostic ( configFile , noSuchTsconfig )
753+ ] ) ;
754+ } ) ;
755+ } ) ;
756+
757+ describe ( "tsserver:: Project Errors reports Options Diagnostic locations correctly with changes in configFile contents" , ( ) => {
758+ it ( "when options change" , ( ) => {
759+ const file = {
760+ path : "/a/b/app.ts" ,
761+ content : "let x = 10"
762+ } ;
763+ const configFileContentBeforeComment = `{` ;
764+ const configFileContentComment = `
765+ // comment` ;
766+ const configFileContentAfterComment = `
767+ "compilerOptions": {
768+ "allowJs": true,
769+ "declaration": true
770+ }
771+ }` ;
772+ const configFileContentWithComment = configFileContentBeforeComment + configFileContentComment + configFileContentAfterComment ;
773+ const configFileContentWithoutCommentLine = configFileContentBeforeComment + configFileContentAfterComment ;
774+
775+ const configFile = {
776+ path : "/a/b/tsconfig.json" ,
777+ content : configFileContentWithComment
778+ } ;
779+ const host = createServerHost ( [ file , libFile , configFile ] ) ;
780+ const session = createSession ( host ) ;
781+ openFilesForSession ( [ file ] , session ) ;
782+
783+ const projectService = session . getProjectService ( ) ;
784+ checkNumberOfProjects ( projectService , { configuredProjects : 1 } ) ;
785+ const projectName = configuredProjectAt ( projectService , 0 ) . getProjectName ( ) ;
786+
787+ const diags = session . executeCommand ( < server . protocol . SemanticDiagnosticsSyncRequest > {
788+ type : "request" ,
789+ command : server . CommandNames . SemanticDiagnosticsSync ,
790+ seq : 2 ,
791+ arguments : { file : configFile . path , projectFileName : projectName , includeLinePosition : true }
792+ } ) . response as ReadonlyArray < server . protocol . DiagnosticWithLinePosition > ;
793+ assert . isTrue ( diags . length === 2 ) ;
794+
795+ configFile . content = configFileContentWithoutCommentLine ;
796+ host . reloadFS ( [ file , configFile ] ) ;
797+
798+ const diagsAfterEdit = session . executeCommand ( < server . protocol . SemanticDiagnosticsSyncRequest > {
799+ type : "request" ,
800+ command : server . CommandNames . SemanticDiagnosticsSync ,
801+ seq : 2 ,
802+ arguments : { file : configFile . path , projectFileName : projectName , includeLinePosition : true }
803+ } ) . response as ReadonlyArray < server . protocol . DiagnosticWithLinePosition > ;
804+ assert . isTrue ( diagsAfterEdit . length === 2 ) ;
805+
806+ verifyDiagnostic ( diags [ 0 ] , diagsAfterEdit [ 0 ] ) ;
807+ verifyDiagnostic ( diags [ 1 ] , diagsAfterEdit [ 1 ] ) ;
808+
809+ function verifyDiagnostic ( beforeEditDiag : server . protocol . DiagnosticWithLinePosition , afterEditDiag : server . protocol . DiagnosticWithLinePosition ) {
810+ assert . equal ( beforeEditDiag . message , afterEditDiag . message ) ;
811+ assert . equal ( beforeEditDiag . code , afterEditDiag . code ) ;
812+ assert . equal ( beforeEditDiag . category , afterEditDiag . category ) ;
813+ assert . equal ( beforeEditDiag . startLocation . line , afterEditDiag . startLocation . line + 1 ) ;
814+ assert . equal ( beforeEditDiag . startLocation . offset , afterEditDiag . startLocation . offset ) ;
815+ assert . equal ( beforeEditDiag . endLocation . line , afterEditDiag . endLocation . line + 1 ) ;
816+ assert . equal ( beforeEditDiag . endLocation . offset , afterEditDiag . endLocation . offset ) ;
817+ }
818+ } ) ;
819+ } ) ;
820+
821+ describe ( "tsserver:: Project Errors with config file change" , ( ) => {
822+ it ( "Updates diagnostics when '--noUnusedLabels' changes" , ( ) => {
823+ const aTs : File = { path : "/a.ts" , content : "label: while (1) {}" } ;
824+ const options = ( allowUnusedLabels : boolean ) => `{ "compilerOptions": { "allowUnusedLabels": ${ allowUnusedLabels } } }` ;
825+ const tsconfig : File = { path : "/tsconfig.json" , content : options ( /*allowUnusedLabels*/ true ) } ;
826+
827+ const host = createServerHost ( [ aTs , tsconfig ] ) ;
828+ const session = createSession ( host ) ;
829+ openFilesForSession ( [ aTs ] , session ) ;
830+
831+ host . modifyFile ( tsconfig . path , options ( /*allowUnusedLabels*/ false ) ) ;
832+ host . runQueuedTimeoutCallbacks ( ) ;
833+
834+ const response = executeSessionRequest < protocol . SemanticDiagnosticsSyncRequest , protocol . SemanticDiagnosticsSyncResponse > ( session , protocol . CommandTypes . SemanticDiagnosticsSync , { file : aTs . path } ) as protocol . Diagnostic [ ] | undefined ;
835+ assert . deepEqual < protocol . Diagnostic [ ] | undefined > ( response , [
836+ {
837+ start : { line : 1 , offset : 1 } ,
838+ end : { line : 1 , offset : 1 + "label" . length } ,
839+ text : "Unused label." ,
840+ category : "error" ,
841+ code : Diagnostics . Unused_label . code ,
842+ relatedInformation : undefined ,
843+ reportsUnnecessary : true ,
844+ source : undefined ,
845+ } ,
846+ ] ) ;
847+ } ) ;
848+ } ) ;
566849}
0 commit comments