22/// <reference path="commandLineParser.ts"/>
33
44namespace ts {
5- export interface SourceFile {
6- fileWatcher ?: FileWatcher ;
5+ export interface CompilerHost {
6+ /** If this is the emit based on the graph builder, use it to emit */
7+ emitWithBuilder ?( program : Program ) : EmitResult ;
78 }
89
910 interface Statistic {
@@ -215,16 +216,17 @@ namespace ts {
215216
216217 function createWatchMode ( commandLine : ParsedCommandLine , configFileName ?: string , configFileRootFiles ?: string [ ] , configFileOptions ?: CompilerOptions , configFileSpecs ?: ConfigFileSpecs , configFileWildCardDirectories ?: MapLike < WatchDirectoryFlags > ) {
217218 let program : Program ;
218- let needsReload : boolean ;
219- let missingFilesMap : Map < FileWatcher > ;
220- let configFileWatcher : FileWatcher ;
221- let watchedWildCardDirectories : Map < WildCardDirectoryWatchers > ;
222- let timerToUpdateProgram : any ;
219+ let needsReload : boolean ; // true if the config file changed and needs to reload it from the disk
220+ let missingFilesMap : Map < FileWatcher > ; // Map of file watchers for the missing files
221+ let configFileWatcher : FileWatcher ; // watcher for the config file
222+ let watchedWildCardDirectories : Map < WildCardDirectoryWatchers > ; // map of watchers for the wild card directories in the config file
223+ let timerToUpdateProgram : any ; // timer callback to recompile the program
223224
224225 let compilerOptions : CompilerOptions ;
225226 let rootFileNames : string [ ] ;
226227
227- const sourceFilesCache = createMap < HostFileInfo | string > ( ) ;
228+ const sourceFilesCache = createMap < HostFileInfo | string > ( ) ; // Cache that stores the source file and version info
229+ let changedFilePaths : Path [ ] = [ ] ;
228230
229231 let host : System ;
230232 if ( configFileName ) {
@@ -241,6 +243,9 @@ namespace ts {
241243 const currentDirectory = host . getCurrentDirectory ( ) ;
242244 const getCanonicalFileName = createGetCanonicalFileName ( host . useCaseSensitiveFileNames ) ;
243245
246+ // There is no extra check needed since we can just rely on the program to decide emit
247+ const builder = createBuilder ( getCanonicalFileName , getDetailedEmitOutput , computeHash , _sourceFile => true ) ;
248+
244249 if ( compilerOptions . pretty ) {
245250 reportDiagnosticWorker = reportDiagnosticWithColorAndContext ;
246251 }
@@ -268,85 +273,6 @@ namespace ts {
268273 }
269274
270275 function createWatchedCompilerHost ( options : CompilerOptions ) : CompilerHost {
271- const existingDirectories = createMap < boolean > ( ) ;
272- function directoryExists ( directoryPath : string ) : boolean {
273- if ( existingDirectories . has ( directoryPath ) ) {
274- return true ;
275- }
276- if ( host . directoryExists ( directoryPath ) ) {
277- existingDirectories . set ( directoryPath , true ) ;
278- return true ;
279- }
280- return false ;
281- }
282-
283- function ensureDirectoriesExist ( directoryPath : string ) {
284- if ( directoryPath . length > getRootLength ( directoryPath ) && ! directoryExists ( directoryPath ) ) {
285- const parentDirectory = getDirectoryPath ( directoryPath ) ;
286- ensureDirectoriesExist ( parentDirectory ) ;
287- host . createDirectory ( directoryPath ) ;
288- }
289- }
290-
291- type OutputFingerprint = {
292- hash : string ;
293- byteOrderMark : boolean ;
294- mtime : Date ;
295- } ;
296- let outputFingerprints : Map < OutputFingerprint > ;
297-
298- function writeFileIfUpdated ( fileName : string , data : string , writeByteOrderMark : boolean ) : void {
299- if ( ! outputFingerprints ) {
300- outputFingerprints = createMap < OutputFingerprint > ( ) ;
301- }
302-
303- const hash = host . createHash ( data ) ;
304- const mtimeBefore = host . getModifiedTime ( fileName ) ;
305-
306- if ( mtimeBefore ) {
307- const fingerprint = outputFingerprints . get ( fileName ) ;
308- // If output has not been changed, and the file has no external modification
309- if ( fingerprint &&
310- fingerprint . byteOrderMark === writeByteOrderMark &&
311- fingerprint . hash === hash &&
312- fingerprint . mtime . getTime ( ) === mtimeBefore . getTime ( ) ) {
313- return ;
314- }
315- }
316-
317- host . writeFile ( fileName , data , writeByteOrderMark ) ;
318-
319- const mtimeAfter = host . getModifiedTime ( fileName ) ;
320-
321- outputFingerprints . set ( fileName , {
322- hash,
323- byteOrderMark : writeByteOrderMark ,
324- mtime : mtimeAfter
325- } ) ;
326- }
327-
328- function writeFile ( fileName : string , data : string , writeByteOrderMark : boolean , onError ?: ( message : string ) => void ) {
329- try {
330- performance . mark ( "beforeIOWrite" ) ;
331- ensureDirectoriesExist ( getDirectoryPath ( normalizePath ( fileName ) ) ) ;
332-
333- //if (isWatchSet(options) && sys.createHash && sys.getModifiedTime) {
334- writeFileIfUpdated ( fileName , data , writeByteOrderMark ) ;
335- //}
336- //else {
337- //host.writeFile(fileName, data, writeByteOrderMark);
338- //}
339-
340- performance . mark ( "afterIOWrite" ) ;
341- performance . measure ( "I/O Write" , "beforeIOWrite" , "afterIOWrite" ) ;
342- }
343- catch ( e ) {
344- if ( onError ) {
345- onError ( e . message ) ;
346- }
347- }
348- }
349-
350276 const newLine = getNewLineCharacter ( options ) ;
351277 const realpath = host . realpath && ( ( path : string ) => host . realpath ( path ) ) ;
352278
@@ -355,7 +281,7 @@ namespace ts {
355281 getSourceFileByPath : getVersionedSourceFileByPath ,
356282 getDefaultLibLocation,
357283 getDefaultLibFileName : options => combinePaths ( getDefaultLibLocation ( ) , getDefaultLibFileName ( options ) ) ,
358- writeFile,
284+ writeFile : ( _fileName , _data , _writeByteOrderMark , _onError ? , _sourceFiles ? ) => { } ,
359285 getCurrentDirectory : memoize ( ( ) => host . getCurrentDirectory ( ) ) ,
360286 useCaseSensitiveFileNames : ( ) => host . useCaseSensitiveFileNames ,
361287 getCanonicalFileName,
@@ -367,7 +293,8 @@ namespace ts {
367293 getEnvironmentVariable : name => host . getEnvironmentVariable ? host . getEnvironmentVariable ( name ) : "" ,
368294 getDirectories : ( path : string ) => host . getDirectories ( path ) ,
369295 realpath,
370- onReleaseOldSourceFile
296+ onReleaseOldSourceFile,
297+ emitWithBuilder
371298 } ;
372299
373300 // TODO: cache module resolution
@@ -379,6 +306,81 @@ namespace ts {
379306 // return host.resolveTypeReferenceDirectives(typeReferenceDirectiveNames, containingFile);
380307 // };
381308 //}
309+
310+ function ensureDirectoriesExist ( directoryPath : string ) {
311+ if ( directoryPath . length > getRootLength ( directoryPath ) && ! host . directoryExists ( directoryPath ) ) {
312+ const parentDirectory = getDirectoryPath ( directoryPath ) ;
313+ ensureDirectoriesExist ( parentDirectory ) ;
314+ host . createDirectory ( directoryPath ) ;
315+ }
316+ }
317+
318+ function writeFile ( fileName : string , data : string , writeByteOrderMark : boolean ) {
319+ try {
320+ performance . mark ( "beforeIOWrite" ) ;
321+ ensureDirectoriesExist ( getDirectoryPath ( normalizePath ( fileName ) ) ) ;
322+
323+ host . writeFile ( fileName , data , writeByteOrderMark ) ;
324+
325+ performance . mark ( "afterIOWrite" ) ;
326+ performance . measure ( "I/O Write" , "beforeIOWrite" , "afterIOWrite" ) ;
327+ }
328+ catch ( e ) {
329+ return createCompilerDiagnostic ( Diagnostics . Could_not_write_file_0_Colon_1 , fileName , e ) ;
330+ }
331+ }
332+
333+ function emitWithBuilder ( program : Program ) : EmitResult {
334+ builder . onProgramUpdateGraph ( program ) ;
335+ const filesPendingToEmit = changedFilePaths ;
336+ changedFilePaths = [ ] ;
337+
338+ const seenFiles = createMap < true > ( ) ;
339+
340+ let emitSkipped : boolean ;
341+ let diagnostics : Diagnostic [ ] ;
342+ const emittedFiles : string [ ] = program . getCompilerOptions ( ) . listEmittedFiles ? [ ] : undefined ;
343+ let sourceMaps : SourceMapData [ ] ;
344+ while ( filesPendingToEmit . length ) {
345+ const filePath = filesPendingToEmit . pop ( ) ;
346+ const affectedFiles = builder . getFilesAffectedBy ( program , filePath ) ;
347+ for ( const file of affectedFiles ) {
348+ if ( ! seenFiles . has ( file ) ) {
349+ seenFiles . set ( file , true ) ;
350+ const sourceFile = program . getSourceFile ( file ) ;
351+ if ( sourceFile ) {
352+ writeFiles ( < EmitOutputDetailed > builder . emitFile ( program , sourceFile . path ) ) ;
353+ }
354+ }
355+ }
356+ }
357+
358+ return { emitSkipped, diagnostics, emittedFiles, sourceMaps } ;
359+
360+ function writeFiles ( emitOutput : EmitOutputDetailed ) {
361+ if ( emitOutput . emitSkipped ) {
362+ emitSkipped = true ;
363+ }
364+
365+ diagnostics = concatenate ( diagnostics , emitOutput . diagnostics ) ;
366+ sourceMaps = concatenate ( sourceMaps , emitOutput . sourceMaps ) ;
367+ // If it emitted more than one source files, just mark all those source files as seen
368+ if ( emitOutput . emittedSourceFiles && emitOutput . emittedSourceFiles . length > 1 ) {
369+ for ( const file of emitOutput . emittedSourceFiles ) {
370+ seenFiles . set ( file . fileName , true ) ;
371+ }
372+ }
373+ for ( const outputFile of emitOutput . outputFiles ) {
374+ const error = writeFile ( outputFile . name , outputFile . text , outputFile . writeByteOrderMark ) ;
375+ if ( error ) {
376+ ( diagnostics || ( diagnostics = [ ] ) ) . push ( error ) ;
377+ }
378+ if ( emittedFiles ) {
379+ emittedFiles . push ( outputFile . name ) ;
380+ }
381+ }
382+ }
383+ }
382384 }
383385
384386 function fileExists ( fileName : string ) {
@@ -408,6 +410,7 @@ namespace ts {
408410
409411 // Create new source file if requested or the versions dont match
410412 if ( ! hostSourceFile ) {
413+ changedFilePaths . push ( path ) ;
411414 const sourceFile = getSourceFile ( fileName , languageVersion , onError ) ;
412415 if ( sourceFile ) {
413416 sourceFile . version = "0" ;
@@ -420,6 +423,7 @@ namespace ts {
420423 return sourceFile ;
421424 }
422425 else if ( shouldCreateNewSourceFile || hostSourceFile . version . toString ( ) !== hostSourceFile . sourceFile . version ) {
426+ changedFilePaths . push ( path ) ;
423427 if ( shouldCreateNewSourceFile ) {
424428 hostSourceFile . version ++ ;
425429 }
@@ -652,6 +656,10 @@ namespace ts {
652656 host . write ( s ) ;
653657 }
654658 }
659+
660+ function computeHash ( data : string ) {
661+ return sys . createHash ? sys . createHash ( data ) : data ;
662+ }
655663 }
656664
657665 interface CachedSystem extends System {
@@ -806,16 +814,20 @@ namespace ts {
806814 }
807815 }
808816
809- // TODO: in watch mode to emit only affected files
810-
811- // Otherwise, emit and report any errors we ran into.
812- const emitOutput = program . emit ( ) ;
817+ // Emit and report any errors we ran into.
818+ let emitOutput : EmitResult ;
819+ if ( compilerHost . emitWithBuilder ) {
820+ emitOutput = compilerHost . emitWithBuilder ( program ) ;
821+ }
822+ else {
823+ // Emit whole program
824+ emitOutput = program . emit ( ) ;
825+ }
813826 diagnostics = diagnostics . concat ( emitOutput . diagnostics ) ;
814827
815828 reportDiagnostics ( sortAndDeduplicateDiagnostics ( diagnostics ) , compilerHost ) ;
816829
817830 reportEmittedFiles ( emitOutput . emittedFiles ) ;
818-
819831 if ( emitOutput . emitSkipped && diagnostics . length > 0 ) {
820832 // If the emitter didn't emit anything, then pass that value along.
821833 return ExitStatus . DiagnosticsPresent_OutputsSkipped ;
0 commit comments