@@ -563,4 +563,287 @@ namespace ts.projectSystem {
563
563
assert . isTrue ( diagsAfterUpdate . length === 0 ) ;
564
564
} ) ;
565
565
} ) ;
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
+ } ) ;
566
849
}
0 commit comments