@@ -2,6 +2,7 @@ package incremental
2
2
3
3
import (
4
4
"context"
5
+ "sync/atomic"
5
6
"time"
6
7
7
8
"github.com/microsoft/typescript-go/internal/ast"
@@ -26,6 +27,7 @@ type emitFilesHandler struct {
26
27
latestChangedDtsFiles collections.SyncMap [tspath.Path , string ]
27
28
deletedPendingKinds collections.Set [tspath.Path ]
28
29
emitUpdates collections.SyncMap [tspath.Path , * emitUpdate ]
30
+ hasEmitDiagnostics atomic.Bool
29
31
}
30
32
31
33
// Determining what all is pending to be emitted based on previous options or previous file emit flags
@@ -44,14 +46,57 @@ func (h *emitFilesHandler) getPendingEmitKindForEmitOptions(emitKind FileEmitKin
44
46
// The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host
45
47
// in that order would be used to write the files
46
48
func (h * emitFilesHandler ) emitAllAffectedFiles (options compiler.EmitOptions ) * compiler.EmitResult {
49
+ // Emit all affected files
50
+ if h .program .snapshot .canUseIncrementalState () {
51
+ results := h .emitFilesIncremental (options )
52
+ if h .isForDtsErrors {
53
+ if options .TargetSourceFile != nil {
54
+ // Result from cache
55
+ diagnostics , _ := h .program .snapshot .emitDiagnosticsPerFile .Load (options .TargetSourceFile .Path ())
56
+ return & compiler.EmitResult {
57
+ EmitSkipped : true ,
58
+ Diagnostics : diagnostics .getDiagnostics (h .program .program , options .TargetSourceFile ),
59
+ }
60
+ }
61
+ return compiler .CombineEmitResults (results )
62
+ } else {
63
+ // Combine results and update buildInfo
64
+ result := compiler .CombineEmitResults (results )
65
+ h .emitBuildInfo (options , result )
66
+ return result
67
+ }
68
+ } else if ! h .isForDtsErrors {
69
+ result := h .program .program .Emit (h .ctx , h .getEmitOptions (options ))
70
+ h .updateSnapshot ()
71
+ h .emitBuildInfo (options , result )
72
+ return result
73
+ } else {
74
+ result := & compiler.EmitResult {
75
+ EmitSkipped : true ,
76
+ Diagnostics : h .program .program .GetDeclarationDiagnostics (h .ctx , options .TargetSourceFile ),
77
+ }
78
+ if len (result .Diagnostics ) != 0 {
79
+ h .program .snapshot .hasEmitDiagnostics = true
80
+ }
81
+ return result
82
+ }
83
+ }
84
+
85
+ func (h * emitFilesHandler ) emitBuildInfo (options compiler.EmitOptions , result * compiler.EmitResult ) {
86
+ buildInfoResult := h .program .emitBuildInfo (h .ctx , options )
87
+ if buildInfoResult != nil {
88
+ result .Diagnostics = append (result .Diagnostics , buildInfoResult .Diagnostics ... )
89
+ result .EmittedFiles = append (result .EmittedFiles , buildInfoResult .EmittedFiles ... )
90
+ }
91
+ }
92
+
93
+ func (h * emitFilesHandler ) emitFilesIncremental (options compiler.EmitOptions ) []* compiler.EmitResult {
47
94
// Get all affected files
48
95
collectAllAffectedFiles (h .ctx , h .program )
49
96
if h .ctx .Err () != nil {
50
97
return nil
51
98
}
52
99
53
- // Emit all affected files
54
- var results []* compiler.EmitResult
55
100
wg := core .NewWorkGroup (h .program .program .SingleThreaded ())
56
101
h .program .snapshot .affectedFilesPendingEmit .Range (func (path tspath.Path , emitKind FileEmitKind ) bool {
57
102
affectedFile := h .program .program .GetSourceFileByPath (path )
@@ -120,58 +165,42 @@ func (h *emitFilesHandler) emitAllAffectedFiles(options compiler.EmitOptions) *c
120
165
return true
121
166
})
122
167
123
- results = h .updateSnapshot ()
124
-
125
- // Combine results and update buildInfo
126
- if h .isForDtsErrors && options .TargetSourceFile != nil {
127
- // Result from cache
128
- diagnostics , _ := h .program .snapshot .emitDiagnosticsPerFile .Load (options .TargetSourceFile .Path ())
129
- return & compiler.EmitResult {
130
- EmitSkipped : true ,
131
- Diagnostics : diagnostics .getDiagnostics (h .program .program , options .TargetSourceFile ),
132
- }
133
- }
134
-
135
- result := compiler .CombineEmitResults (results )
136
- if ! h .isForDtsErrors {
137
- buildInfoResult := h .program .emitBuildInfo (h .ctx , options )
138
- if buildInfoResult != nil {
139
- result .Diagnostics = append (result .Diagnostics , buildInfoResult .Diagnostics ... )
140
- result .EmittedFiles = append (result .EmittedFiles , buildInfoResult .EmittedFiles ... )
141
- }
142
- }
143
-
144
- return result
168
+ return h .updateSnapshot ()
145
169
}
146
170
147
171
func (h * emitFilesHandler ) getEmitOptions (options compiler.EmitOptions ) compiler.EmitOptions {
148
172
if ! h .program .snapshot .options .GetEmitDeclarations () {
149
173
return options
150
174
}
175
+ canUseIncrementalState := h .program .snapshot .canUseIncrementalState ()
151
176
return compiler.EmitOptions {
152
177
TargetSourceFile : options .TargetSourceFile ,
153
178
EmitOnly : options .EmitOnly ,
154
179
WriteFile : func (fileName string , text string , writeByteOrderMark bool , data * compiler.WriteFileData ) error {
155
180
var differsOnlyInMap bool
156
181
if tspath .IsDeclarationFileName (fileName ) {
157
- var emitSignature string
158
- info , _ := h .program .snapshot .fileInfos .Load (options .TargetSourceFile .Path ())
159
- if info .signature == info .version {
160
- signature := h .program .snapshot .computeSignatureWithDiagnostics (options .TargetSourceFile , text , data )
161
- // With d.ts diagnostics they are also part of the signature so emitSignature will be different from it since its just hash of d.ts
162
- if len (data .Diagnostics ) == 0 {
163
- emitSignature = signature
182
+ if canUseIncrementalState {
183
+ var emitSignature string
184
+ info , _ := h .program .snapshot .fileInfos .Load (options .TargetSourceFile .Path ())
185
+ if info .signature == info .version {
186
+ signature := h .program .snapshot .computeSignatureWithDiagnostics (options .TargetSourceFile , text , data )
187
+ // With d.ts diagnostics they are also part of the signature so emitSignature will be different from it since its just hash of d.ts
188
+ if len (data .Diagnostics ) == 0 {
189
+ emitSignature = signature
190
+ }
191
+ if signature != info .version { // Update it
192
+ h .signatures .Store (options .TargetSourceFile .Path (), signature )
193
+ }
164
194
}
165
- if signature != info .version { // Update it
166
- h .signatures .Store (options .TargetSourceFile .Path (), signature )
167
- }
168
- }
169
195
170
- // Store d.ts emit hash so later can be compared to check if d.ts has changed.
171
- // Currently we do this only for composite projects since these are the only projects that can be referenced by other projects
172
- // and would need their d.ts change time in --build mode
173
- if h .skipDtsOutputOfComposite (options .TargetSourceFile , fileName , text , data , emitSignature , & differsOnlyInMap ) {
174
- return nil
196
+ // Store d.ts emit hash so later can be compared to check if d.ts has changed.
197
+ // Currently we do this only for composite projects since these are the only projects that can be referenced by other projects
198
+ // and would need their d.ts change time in --build mode
199
+ if h .skipDtsOutputOfComposite (options .TargetSourceFile , fileName , text , data , emitSignature , & differsOnlyInMap ) {
200
+ return nil
201
+ }
202
+ } else if len (data .Diagnostics ) > 0 {
203
+ h .hasEmitDiagnostics .Store (true )
175
204
}
176
205
}
177
206
@@ -231,56 +260,62 @@ func (h *emitFilesHandler) skipDtsOutputOfComposite(file *ast.SourceFile, output
231
260
}
232
261
233
262
func (h * emitFilesHandler ) updateSnapshot () []* compiler.EmitResult {
234
- h .signatures .Range (func (file tspath.Path , signature string ) bool {
235
- info , _ := h .program .snapshot .fileInfos .Load (file )
236
- info .signature = signature
237
- if h .program .testingData != nil {
238
- h .program .testingData .UpdatedSignatureKinds [file ] = SignatureUpdateKindStoredAtEmit
239
- }
240
- h .program .snapshot .buildInfoEmitPending .Store (true )
241
- return true
242
- })
243
- h .emitSignatures .Range (func (file tspath.Path , signature * emitSignature ) bool {
244
- h .program .snapshot .emitSignatures .Store (file , signature )
245
- h .program .snapshot .buildInfoEmitPending .Store (true )
246
- return true
247
- })
248
- for file := range h .deletedPendingKinds .Keys () {
249
- h .program .snapshot .affectedFilesPendingEmit .Delete (file )
250
- h .program .snapshot .buildInfoEmitPending .Store (true )
251
- }
252
- // Always use correct order when to collect the result
253
- var results []* compiler.EmitResult
254
- for _ , file := range h .program .GetSourceFiles () {
255
- if latestChangedDtsFile , ok := h .latestChangedDtsFiles .Load (file .Path ()); ok {
256
- h .program .snapshot .latestChangedDtsFile = latestChangedDtsFile
263
+ if h .program .snapshot .canUseIncrementalState () {
264
+ h .signatures .Range (func (file tspath.Path , signature string ) bool {
265
+ info , _ := h .program .snapshot .fileInfos .Load (file )
266
+ info .signature = signature
267
+ if h .program .testingData != nil {
268
+ h .program .testingData .UpdatedSignatureKinds [file ] = SignatureUpdateKindStoredAtEmit
269
+ }
270
+ h .program .snapshot .buildInfoEmitPending .Store (true )
271
+ return true
272
+ })
273
+ h .emitSignatures .Range (func (file tspath.Path , signature * emitSignature ) bool {
274
+ h .program .snapshot .emitSignatures .Store (file , signature )
275
+ h .program .snapshot .buildInfoEmitPending .Store (true )
276
+ return true
277
+ })
278
+ for file := range h .deletedPendingKinds .Keys () {
279
+ h .program .snapshot .affectedFilesPendingEmit .Delete (file )
257
280
h .program .snapshot .buildInfoEmitPending .Store (true )
258
- h .program .snapshot .hasChangedDtsFile = true
259
281
}
260
- if update , ok := h .emitUpdates .Load (file .Path ()); ok {
261
- if ! update .dtsErrorsFromCache {
262
- if update .pendingKind == 0 {
263
- h .program .snapshot .affectedFilesPendingEmit .Delete (file .Path ())
264
- } else {
265
- h .program .snapshot .affectedFilesPendingEmit .Store (file .Path (), update .pendingKind )
266
- }
282
+ // Always use correct order when to collect the result
283
+ var results []* compiler.EmitResult
284
+ for _ , file := range h .program .GetSourceFiles () {
285
+ if latestChangedDtsFile , ok := h .latestChangedDtsFiles .Load (file .Path ()); ok {
286
+ h .program .snapshot .latestChangedDtsFile = latestChangedDtsFile
267
287
h .program .snapshot .buildInfoEmitPending .Store (true )
288
+ h .program .snapshot .hasChangedDtsFile = true
268
289
}
269
- if update .result != nil {
270
- results = append (results , update .result )
271
- if len (update .result .Diagnostics ) != 0 {
272
- h .program .snapshot .emitDiagnosticsPerFile .Store (file .Path (), & diagnosticsOrBuildInfoDiagnosticsWithFileName {diagnostics : update .result .Diagnostics })
290
+ if update , ok := h .emitUpdates .Load (file .Path ()); ok {
291
+ if ! update .dtsErrorsFromCache {
292
+ if update .pendingKind == 0 {
293
+ h .program .snapshot .affectedFilesPendingEmit .Delete (file .Path ())
294
+ } else {
295
+ h .program .snapshot .affectedFilesPendingEmit .Store (file .Path (), update .pendingKind )
296
+ }
297
+ h .program .snapshot .buildInfoEmitPending .Store (true )
298
+ }
299
+ if update .result != nil {
300
+ results = append (results , update .result )
301
+ if len (update .result .Diagnostics ) != 0 {
302
+ h .program .snapshot .emitDiagnosticsPerFile .Store (file .Path (), & diagnosticsOrBuildInfoDiagnosticsWithFileName {diagnostics : update .result .Diagnostics })
303
+ }
273
304
}
274
305
}
275
306
}
307
+ return results
308
+ } else if h .hasEmitDiagnostics .Load () {
309
+ h .program .snapshot .hasEmitDiagnostics = true
276
310
}
277
- return results
311
+ return nil
278
312
}
279
313
280
314
func emitFiles (ctx context.Context , program * Program , options compiler.EmitOptions , isForDtsErrors bool ) * compiler.EmitResult {
281
315
emitHandler := & emitFilesHandler {ctx : ctx , program : program , isForDtsErrors : isForDtsErrors }
282
316
283
- if options .TargetSourceFile != nil {
317
+ // Single file emit - do direct from program
318
+ if ! isForDtsErrors && options .TargetSourceFile != nil {
284
319
result := program .program .Emit (ctx , emitHandler .getEmitOptions (options ))
285
320
if ctx .Err () != nil {
286
321
return nil
0 commit comments