33/// <reference path="session.ts" />
44
55namespace ts . server {
6- export function shouldEmitFile ( scriptInfo : ScriptInfo ) {
7- return ! scriptInfo . hasMixedContent ;
8- }
9-
106 export interface Builder {
117 /**
128 * This is the callback when file infos in the builder are updated
139 */
14- onProjectUpdateGraph ( ) : void ;
15- getFilesAffectedBy ( scriptInfo : ScriptInfo ) : string [ ] ;
16- /**
17- * @returns {boolean } whether the emit was conducted or not
18- */
19- emitFile ( scriptInfo : ScriptInfo , writeFile : ( path : string , data : string , writeByteOrderMark ?: boolean ) => void ) : boolean ;
10+ onProgramUpdateGraph ( program : Program ) : void ;
11+ getFilesAffectedBy ( program : Program , path : Path ) : string [ ] ;
12+ emitFile ( program : Program , path : Path ) : EmitOutput ;
2013 clear ( ) : void ;
2114 }
2215
2316 interface EmitHandler {
24- addScriptInfo ( scriptInfo : ScriptInfo ) : void ;
17+ addScriptInfo ( program : Program , sourceFile : SourceFile ) : void ;
2518 removeScriptInfo ( path : Path ) : void ;
26- updateScriptInfo ( scriptInfo : ScriptInfo ) : void ;
19+ updateScriptInfo ( program : Program , sourceFile : SourceFile ) : void ;
2720 /**
2821 * Gets the files affected by the script info which has updated shape from the known one
2922 */
30- getFilesAffectedByUpdatedShape ( scriptInfo : ScriptInfo , singleFileResult : string [ ] ) : string [ ] ;
23+ getFilesAffectedByUpdatedShape ( program : Program , sourceFile : SourceFile , singleFileResult : string [ ] ) : string [ ] ;
3124 }
3225
33- export function createBuilder ( project : Project ) : Builder {
26+ export function createBuilder (
27+ getCanonicalFileName : ( fileName : string ) => string ,
28+ getEmitOutput : ( program : Program , sourceFile : SourceFile , emitOnlyDtsFiles ?: boolean ) => EmitOutput ,
29+ computeHash : ( data : string ) => string ,
30+ shouldEmitFile : ( sourceFile : SourceFile ) => boolean
31+ ) : Builder {
3432 let isModuleEmit : boolean | undefined ;
35- let projectVersionForDependencyGraph : string ;
3633 // Last checked shape signature for the file info
3734 let fileInfos : Map < string > ;
3835 let emitHandler : EmitHandler ;
3936 return {
40- onProjectUpdateGraph ,
37+ onProgramUpdateGraph ,
4138 getFilesAffectedBy,
4239 emitFile,
4340 clear
4441 } ;
4542
46- function createProjectGraph ( ) {
47- const currentIsModuleEmit = project . getCompilerOptions ( ) . module !== ModuleKind . None ;
43+ function createProgramGraph ( program : Program ) {
44+ const currentIsModuleEmit = program . getCompilerOptions ( ) . module !== ModuleKind . None ;
4845 if ( isModuleEmit !== currentIsModuleEmit ) {
4946 isModuleEmit = currentIsModuleEmit ;
5047 emitHandler = isModuleEmit ? getModuleEmitHandler ( ) : getNonModuleEmitHandler ( ) ;
5148 fileInfos = undefined ;
5249 }
5350
5451 fileInfos = mutateExistingMap (
55- fileInfos , arrayToMap ( project . getScriptInfos ( ) , info => info . path ) ,
56- ( _path , info ) => {
57- emitHandler . addScriptInfo ( info ) ;
52+ fileInfos , arrayToMap ( program . getSourceFiles ( ) , sourceFile => sourceFile . path ) ,
53+ ( _path , sourceFile ) => {
54+ emitHandler . addScriptInfo ( program , sourceFile ) ;
5855 return "" ;
5956 } ,
6057 ( path : Path , _value ) => emitHandler . removeScriptInfo ( path ) ,
6158 /*isSameValue*/ undefined ,
6259 /*OnDeleteExistingMismatchValue*/ undefined ,
63- ( _prevValue , scriptInfo ) => emitHandler . updateScriptInfo ( scriptInfo )
60+ ( _prevValue , sourceFile ) => emitHandler . updateScriptInfo ( program , sourceFile )
6461 ) ;
65- projectVersionForDependencyGraph = project . getProjectVersion ( ) ;
6662 }
6763
68- function ensureFileInfos ( ) {
64+ function ensureProgramGraph ( program : Program ) {
6965 if ( ! emitHandler ) {
70- createProjectGraph ( ) ;
66+ createProgramGraph ( program ) ;
7167 }
72- Debug . assert ( projectVersionForDependencyGraph === project . getProjectVersion ( ) ) ;
7368 }
7469
75- function onProjectUpdateGraph ( ) {
70+ function onProgramUpdateGraph ( program : Program ) {
7671 if ( emitHandler ) {
77- createProjectGraph ( ) ;
72+ createProgramGraph ( program ) ;
7873 }
7974 }
8075
81- function getFilesAffectedBy ( scriptInfo : ScriptInfo ) : string [ ] {
82- ensureFileInfos ( ) ;
76+ function getFilesAffectedBy ( program : Program , path : Path ) : string [ ] {
77+ ensureProgramGraph ( program ) ;
8378
84- const singleFileResult = scriptInfo . hasMixedContent ? [ ] : [ scriptInfo . fileName ] ;
85- const path = scriptInfo . path ;
86- if ( ! fileInfos || ! fileInfos . has ( path ) || ! updateShapeSignature ( scriptInfo ) ) {
79+ const sourceFile = program . getSourceFile ( path ) ;
80+ const singleFileResult = sourceFile && shouldEmitFile ( sourceFile ) ? [ sourceFile . fileName ] : [ ] ;
81+ if ( ! fileInfos || ! fileInfos . has ( path ) || ! updateShapeSignature ( program , sourceFile ) ) {
8782 return singleFileResult ;
8883 }
8984
90- return emitHandler . getFilesAffectedByUpdatedShape ( scriptInfo , singleFileResult ) ;
85+ return emitHandler . getFilesAffectedByUpdatedShape ( program , sourceFile , singleFileResult ) ;
9186 }
9287
93- /**
94- * @returns {boolean } whether the emit was conducted or not
95- */
96- function emitFile ( scriptInfo : ScriptInfo , writeFile : ( path : string , data : string , writeByteOrderMark ?: boolean ) => void ) : boolean {
97- ensureFileInfos ( ) ;
98- if ( ! fileInfos || ! fileInfos . has ( scriptInfo . path ) ) {
99- return false ;
88+ function emitFile ( program : Program , path : Path ) : EmitOutput {
89+ ensureProgramGraph ( program ) ;
90+ if ( ! fileInfos || ! fileInfos . has ( path ) ) {
91+ return { outputFiles : [ ] , emitSkipped : true } ;
10092 }
10193
102- const { emitSkipped, outputFiles } = project . getFileEmitOutput ( scriptInfo , /*emitOnlyDtsFiles*/ false ) ;
103- if ( ! emitSkipped ) {
104- const projectRootPath = project . getProjectRootPath ( ) ;
105- for ( const outputFile of outputFiles ) {
106- const outputFileAbsoluteFileName = getNormalizedAbsolutePath ( outputFile . name , projectRootPath ? projectRootPath : getDirectoryPath ( scriptInfo . fileName ) ) ;
107- writeFile ( outputFileAbsoluteFileName , outputFile . text , outputFile . writeByteOrderMark ) ;
108- }
109- }
110- return ! emitSkipped ;
94+ return getEmitOutput ( program , program . getSourceFileByPath ( path ) , /*emitOnlyDtsFiles*/ false ) ;
11195 }
11296
11397 function clear ( ) {
11498 isModuleEmit = undefined ;
11599 emitHandler = undefined ;
116100 fileInfos = undefined ;
117- projectVersionForDependencyGraph = undefined ;
118- }
119-
120- function getSourceFile ( path : Path ) {
121- return project . getSourceFile ( path ) ;
122- }
123-
124- function getScriptInfo ( path : Path ) {
125- return project . projectService . getScriptInfoForPath ( path ) ;
126101 }
127102
128103 function isExternalModuleOrHasOnlyAmbientExternalModules ( sourceFile : SourceFile ) {
@@ -147,12 +122,8 @@ namespace ts.server {
147122 /**
148123 * @return {boolean } indicates if the shape signature has changed since last update.
149124 */
150- function updateShapeSignature ( scriptInfo : ScriptInfo ) {
151- const path = scriptInfo . path ;
152- const sourceFile = getSourceFile ( path ) ;
153- if ( ! sourceFile ) {
154- return true ;
155- }
125+ function updateShapeSignature ( program : Program , sourceFile : SourceFile ) {
126+ const path = sourceFile . path ;
156127
157128 const prevSignature = fileInfos . get ( path ) ;
158129 let latestSignature = prevSignature ;
@@ -161,7 +132,7 @@ namespace ts.server {
161132 fileInfos . set ( path , latestSignature ) ;
162133 }
163134 else {
164- const emitOutput = project . getFileEmitOutput ( scriptInfo , /*emitOnlyDtsFiles*/ true ) ;
135+ const emitOutput = getEmitOutput ( program , sourceFile , /*emitOnlyDtsFiles*/ true ) ;
165136 if ( emitOutput . outputFiles && emitOutput . outputFiles . length > 0 ) {
166137 latestSignature = computeHash ( emitOutput . outputFiles [ 0 ] . text ) ;
167138 fileInfos . set ( path , latestSignature ) ;
@@ -171,8 +142,67 @@ namespace ts.server {
171142 return ! prevSignature || latestSignature !== prevSignature ;
172143 }
173144
174- function computeHash ( text : string ) {
175- return project . projectService . host . createHash ( text ) ;
145+ /**
146+ * Gets the referenced files for a file from the program
147+ * @param program
148+ * @param path
149+ */
150+ function getReferencedFiles ( program : Program , sourceFile : SourceFile ) : Map < true > {
151+ const referencedFiles = createMap < true > ( ) ;
152+ // We need to use a set here since the code can contain the same import twice,
153+ // but that will only be one dependency.
154+ // To avoid invernal conversion, the key of the referencedFiles map must be of type Path
155+ if ( sourceFile . imports && sourceFile . imports . length > 0 ) {
156+ const checker : TypeChecker = program . getTypeChecker ( ) ;
157+ for ( const importName of sourceFile . imports ) {
158+ const symbol = checker . getSymbolAtLocation ( importName ) ;
159+ if ( symbol && symbol . declarations && symbol . declarations [ 0 ] ) {
160+ const declarationSourceFile = symbol . declarations [ 0 ] . getSourceFile ( ) ;
161+ if ( declarationSourceFile ) {
162+ referencedFiles . set ( declarationSourceFile . path , true ) ;
163+ }
164+ }
165+ }
166+ }
167+
168+ const sourceFileDirectory = getDirectoryPath ( sourceFile . path ) ;
169+ // Handle triple slash references
170+ if ( sourceFile . referencedFiles && sourceFile . referencedFiles . length > 0 ) {
171+ for ( const referencedFile of sourceFile . referencedFiles ) {
172+ const referencedPath = toPath ( referencedFile . fileName , sourceFileDirectory , getCanonicalFileName ) ;
173+ referencedFiles . set ( referencedPath , true ) ;
174+ }
175+ }
176+
177+ // Handle type reference directives
178+ if ( sourceFile . resolvedTypeReferenceDirectiveNames ) {
179+ sourceFile . resolvedTypeReferenceDirectiveNames . forEach ( ( resolvedTypeReferenceDirective ) => {
180+ if ( ! resolvedTypeReferenceDirective ) {
181+ return ;
182+ }
183+
184+ const fileName = resolvedTypeReferenceDirective . resolvedFileName ;
185+ const typeFilePath = toPath ( fileName , sourceFileDirectory , getCanonicalFileName ) ;
186+ referencedFiles . set ( typeFilePath , true ) ;
187+ } ) ;
188+ }
189+
190+ return referencedFiles ;
191+ }
192+
193+ /**
194+ * Gets all the emittable files from the program
195+ */
196+ function getAllEmittableFiles ( program : Program ) {
197+ const defaultLibraryFileName = getDefaultLibFileName ( program . getCompilerOptions ( ) ) ;
198+ const sourceFiles = program . getSourceFiles ( ) ;
199+ const result : string [ ] = [ ] ;
200+ for ( const sourceFile of sourceFiles ) {
201+ if ( getBaseFileName ( sourceFile . fileName ) !== defaultLibraryFileName && shouldEmitFile ( sourceFile ) ) {
202+ result . push ( sourceFile . fileName ) ;
203+ }
204+ }
205+ return result ;
176206 }
177207
178208 function noop ( ) { }
@@ -185,14 +215,14 @@ namespace ts.server {
185215 getFilesAffectedByUpdatedShape
186216 } ;
187217
188- function getFilesAffectedByUpdatedShape ( _scriptInfo : ScriptInfo , singleFileResult : string [ ] ) : string [ ] {
189- const options = project . getCompilerOptions ( ) ;
218+ function getFilesAffectedByUpdatedShape ( program : Program , _sourceFile : SourceFile , singleFileResult : string [ ] ) : string [ ] {
219+ const options = program . getCompilerOptions ( ) ;
190220 // If `--out` or `--outFile` is specified, any new emit will result in re-emitting the entire project,
191221 // so returning the file itself is good enough.
192222 if ( options && ( options . out || options . outFile ) ) {
193223 return singleFileResult ;
194224 }
195- return project . getAllEmittableFiles ( ) ;
225+ return getAllEmittableFiles ( program ) ;
196226 }
197227 }
198228
@@ -207,47 +237,47 @@ namespace ts.server {
207237 getFilesAffectedByUpdatedShape
208238 } ;
209239
210- function setReferences ( path : Path , latestVersion : string , existingMap : Map < true > ) {
211- existingMap = mutateExistingMapWithNewSet < true > ( existingMap , project . getReferencedFiles ( path ) ,
240+ function setReferences ( program : Program , sourceFile : SourceFile , existingMap : Map < true > ) {
241+ const path = sourceFile . path ;
242+ existingMap = mutateExistingMapWithNewSet < true > (
243+ existingMap ,
244+ getReferencedFiles ( program , sourceFile ) ,
212245 // Creating new Reference: Also add referenced by
213246 key => { referencedBy . add ( key , path ) ; return true ; } ,
214247 // Remove existing reference
215248 ( key , _existingValue ) => { referencedBy . remove ( key , path ) ; }
216249 ) ;
217250 references . set ( path , existingMap ) ;
218- scriptVersionForReferences . set ( path , latestVersion ) ;
251+ scriptVersionForReferences . set ( path , sourceFile . version ) ;
219252 }
220253
221- function addScriptInfo ( info : ScriptInfo ) {
222- setReferences ( info . path , info . getLatestVersion ( ) , undefined ) ;
254+ function addScriptInfo ( program : Program , sourceFile : SourceFile ) {
255+ setReferences ( program , sourceFile , undefined ) ;
223256 }
224257
225258 function removeScriptInfo ( path : Path ) {
226259 references . delete ( path ) ;
227260 scriptVersionForReferences . delete ( path ) ;
228261 }
229262
230- function updateScriptInfo ( scriptInfo : ScriptInfo ) {
231- const path = scriptInfo . path ;
263+ function updateScriptInfo ( program : Program , sourceFile : SourceFile ) {
264+ const path = sourceFile . path ;
232265 const lastUpdatedVersion = scriptVersionForReferences . get ( path ) ;
233- const latestVersion = scriptInfo . getLatestVersion ( ) ;
234- if ( lastUpdatedVersion !== latestVersion ) {
235- setReferences ( path , latestVersion , references . get ( path ) ) ;
266+ if ( lastUpdatedVersion !== sourceFile . version ) {
267+ setReferences ( program , sourceFile , references . get ( path ) ) ;
236268 }
237269 }
238270
239271 function getReferencedByPaths ( path : Path ) {
240272 return referencedBy . get ( path ) || [ ] ;
241273 }
242274
243- function getFilesAffectedByUpdatedShape ( scriptInfo : ScriptInfo , singleFileResult : string [ ] ) : string [ ] {
244- const path = scriptInfo . path ;
245- const sourceFile = getSourceFile ( path ) ;
275+ function getFilesAffectedByUpdatedShape ( program : Program , sourceFile : SourceFile , singleFileResult : string [ ] ) : string [ ] {
246276 if ( ! isExternalModuleOrHasOnlyAmbientExternalModules ( sourceFile ) ) {
247- return project . getAllEmittableFiles ( ) ;
277+ return getAllEmittableFiles ( program ) ;
248278 }
249279
250- const options = project . getCompilerOptions ( ) ;
280+ const options = program . getCompilerOptions ( ) ;
251281 if ( options && ( options . isolatedModules || options . out || options . outFile ) ) {
252282 return singleFileResult ;
253283 }
@@ -256,22 +286,23 @@ namespace ts.server {
256286 // Because if so, its own referencedBy files need to be saved as well to make the
257287 // emitting result consistent with files on disk.
258288
259- const fileNamesMap = createMap < NormalizedPath > ( ) ;
260- const setFileName = ( path : Path , scriptInfo : ScriptInfo ) => {
261- fileNamesMap . set ( path , scriptInfo && shouldEmitFile ( scriptInfo ) ? scriptInfo . fileName : undefined ) ;
289+ const fileNamesMap = createMap < string > ( ) ;
290+ const setFileName = ( path : Path , sourceFile : SourceFile ) => {
291+ fileNamesMap . set ( path , sourceFile && shouldEmitFile ( sourceFile ) ? sourceFile . fileName : undefined ) ;
262292 } ;
263293
264294 // Start with the paths this file was referenced by
265- setFileName ( path , scriptInfo ) ;
295+ const path = sourceFile . path ;
296+ setFileName ( path , sourceFile ) ;
266297 const queue = getReferencedByPaths ( path ) . slice ( ) ;
267298 while ( queue . length > 0 ) {
268299 const currentPath = queue . pop ( ) ;
269300 if ( ! fileNamesMap . has ( currentPath ) ) {
270- const currentScriptInfo = getScriptInfo ( currentPath ) ;
271- if ( currentScriptInfo && updateShapeSignature ( currentScriptInfo ) ) {
301+ const currentSourceFile = program . getSourceFileByPath ( currentPath ) ;
302+ if ( currentSourceFile && updateShapeSignature ( program , currentSourceFile ) ) {
272303 queue . push ( ...getReferencedByPaths ( currentPath ) ) ;
273304 }
274- setFileName ( currentPath , currentScriptInfo ) ;
305+ setFileName ( currentPath , currentSourceFile ) ;
275306 }
276307 }
277308
0 commit comments