@@ -880,6 +880,131 @@ namespace ts.projectSystem {
880880 assert . isFalse ( host . fileExists ( `${ tscWatch . projectRoot } /test/file2.d.ts` ) ) ;
881881 }
882882 } ) ;
883+
884+ describe ( "compile on save in global files" , ( ) => {
885+ describe ( "when program contains module" , ( ) => {
886+ it ( "when d.ts emit is enabled" , ( ) => {
887+ verifyGlobalSave ( /*declaration*/ true , /*hasModule*/ true ) ;
888+ } ) ;
889+ it ( "when d.ts emit is not enabled" , ( ) => {
890+ verifyGlobalSave ( /*declaration*/ false , /*hasModule*/ true ) ;
891+ } ) ;
892+ } ) ;
893+ describe ( "when program doesnt have module" , ( ) => {
894+ it ( "when d.ts emit is enabled" , ( ) => {
895+ verifyGlobalSave ( /*declaration*/ true , /*hasModule*/ false ) ;
896+ } ) ;
897+ it ( "when d.ts emit is not enabled" , ( ) => {
898+ verifyGlobalSave ( /*declaration*/ false , /*hasModule*/ false ) ;
899+ } ) ;
900+ } ) ;
901+ function verifyGlobalSave ( declaration : boolean , hasModule : boolean ) {
902+ const config : File = {
903+ path : `${ tscWatch . projectRoot } /tsconfig.json` ,
904+ content : JSON . stringify ( {
905+ compileOnSave : true ,
906+ compilerOptions : {
907+ declaration,
908+ module : hasModule ? undefined : "none"
909+ } ,
910+ } )
911+ } ;
912+ const file1 : File = {
913+ path : `${ tscWatch . projectRoot } /file1.ts` ,
914+ content : `const x = 1;
915+ function foo() {
916+ return "hello";
917+ }`
918+ } ;
919+ const file2 : File = {
920+ path : `${ tscWatch . projectRoot } /file2.ts` ,
921+ content : `const y = 2;
922+ function bar() {
923+ return "world";
924+ }`
925+ } ;
926+ const file3 : File = {
927+ path : `${ tscWatch . projectRoot } /file3.ts` ,
928+ content : "const xy = 3;"
929+ } ;
930+ const module : File = {
931+ path : `${ tscWatch . projectRoot } /module.ts` ,
932+ content : "export const xyz = 4;"
933+ } ;
934+ const files = [ file1 , file2 , file3 , ...( hasModule ? [ module ] : emptyArray ) ] ;
935+ const host = createServerHost ( [ ...files , config , libFile ] ) ;
936+ const session = createSession ( host ) ;
937+ openFilesForSession ( [ file1 , file2 ] , session ) ;
938+
939+ const affectedFileResponse = session . executeCommandSeq < protocol . CompileOnSaveAffectedFileListRequest > ( {
940+ command : protocol . CommandTypes . CompileOnSaveAffectedFileList ,
941+ arguments : { file : file1 . path }
942+ } ) . response as protocol . CompileOnSaveAffectedFileListSingleProject [ ] ;
943+ assert . deepEqual ( affectedFileResponse , [
944+ { fileNames : files . map ( f => f . path ) , projectFileName : config . path , projectUsesOutFile : false }
945+ ] ) ;
946+ verifyFileSave ( file1 ) ;
947+ verifyFileSave ( file2 ) ;
948+ verifyFileSave ( file3 ) ;
949+ if ( hasModule ) {
950+ verifyFileSave ( module ) ;
951+ }
952+
953+ // Change file1 get affected file list
954+ verifyLocalEdit ( file1 , "hello" , "world" ) ;
955+
956+ // Change file2 get affected file list = will return only file2 if --declaration otherwise all files
957+ verifyLocalEdit ( file2 , "world" , "hello" , /*returnsAllFilesAsAffected*/ ! declaration ) ;
958+
959+ function verifyFileSave ( file : File ) {
960+ const response = session . executeCommandSeq < protocol . CompileOnSaveEmitFileRequest > ( {
961+ command : protocol . CommandTypes . CompileOnSaveEmitFile ,
962+ arguments : { file : file . path }
963+ } ) . response ;
964+ assert . isTrue ( response ) ;
965+ assert . strictEqual (
966+ host . readFile ( changeExtension ( file . path , ".js" ) ) ,
967+ file === module ?
968+ `"use strict";\nexports.__esModule = true;\nexports.xyz = void 0;\nexports.xyz = 4;\n` :
969+ `${ file . content . replace ( "const" , "var" ) } \n`
970+ ) ;
971+ if ( declaration ) {
972+ assert . strictEqual (
973+ host . readFile ( changeExtension ( file . path , ".d.ts" ) ) ,
974+ ( file . content . substr ( 0 , file . content . indexOf ( " {" ) === - 1 ? file . content . length : file . content . indexOf ( " {" ) )
975+ . replace ( "const " , "declare const " )
976+ . replace ( "function " , "declare function " )
977+ . replace ( ")" , "): string;" ) ) + "\n"
978+ ) ;
979+ }
980+ }
981+
982+ function verifyLocalEdit ( file : File , oldText : string , newText : string , returnsAllFilesAsAffected ?: boolean ) {
983+ // Change file1 get affected file list
984+ session . executeCommandSeq < protocol . UpdateOpenRequest > ( {
985+ command : protocol . CommandTypes . UpdateOpen ,
986+ arguments : {
987+ changedFiles : [ {
988+ fileName : file . path ,
989+ textChanges : [ {
990+ newText,
991+ ...protocolTextSpanFromSubstring ( file . content , oldText )
992+ } ]
993+ } ]
994+ }
995+ } ) ;
996+ const affectedFileResponse = session . executeCommandSeq < protocol . CompileOnSaveAffectedFileListRequest > ( {
997+ command : protocol . CommandTypes . CompileOnSaveAffectedFileList ,
998+ arguments : { file : file . path }
999+ } ) . response as protocol . CompileOnSaveAffectedFileListSingleProject [ ] ;
1000+ assert . deepEqual ( affectedFileResponse , [
1001+ { fileNames : [ file . path , ...( returnsAllFilesAsAffected ? files . filter ( f => f !== file ) . map ( f => f . path ) : emptyArray ) ] , projectFileName : config . path , projectUsesOutFile : false }
1002+ ] ) ;
1003+ file . content = file . content . replace ( oldText , newText ) ;
1004+ verifyFileSave ( file ) ;
1005+ }
1006+ }
1007+ } ) ;
8831008 } ) ;
8841009
8851010 describe ( "unittests:: tsserver:: compileOnSave:: CompileOnSaveAffectedFileListRequest with and without projectFileName in request" , ( ) => {
0 commit comments