1
1
'use strict' ;
2
2
3
- const tmpdir = require ( './utilities/tmpdir' ) ;
4
- const mkdirp = require ( 'mkdirp' ) ;
3
+ const tmpdir = require ( '../utilities/tmpdir' ) ;
5
4
const Funnel = require ( 'broccoli-funnel' ) ;
6
5
const MergeTrees = require ( 'broccoli-merge-trees' ) ;
7
- const symlinkOrCopy = require ( 'symlink-or-copy' ) ;
8
- const Plugin = require ( 'broccoli-plugin' ) ;
9
- const RSVP = require ( 'rsvp' ) ;
10
6
const path = require ( 'path' ) ;
11
- const fs = require ( 'fs' ) ;
7
+ const fs = require ( 'fs-extra ' ) ;
12
8
const resolve = require ( 'resolve' ) ;
13
- const compile = require ( './utilities/compile' ) ;
9
+ const compile = require ( '.. /utilities/compile' ) ;
14
10
const ts = require ( 'typescript' ) ;
11
+ const TypescriptOutput = require ( './typescript-output-plugin' ) ;
12
+ const CompilerState = require ( './compiler-state' ) ;
15
13
16
- const debugCompiler = require ( 'debug' ) ( 'ember-cli-typescript:compiler' ) ;
17
- const debugAutoresolve = require ( 'debug' ) ( 'ember-cli-typescript:autoresolve' ) ;
14
+ const debugTsc = require ( 'debug' ) ( 'ember-cli-typescript:tsc' ) ;
18
15
19
16
module . exports = class IncrementalTypescriptCompiler {
20
17
constructor ( app , project ) {
@@ -29,21 +26,12 @@ module.exports = class IncrementalTypescriptCompiler {
29
26
this . app = app ;
30
27
this . project = project ;
31
28
this . addons = this . _discoverAddons ( project , [ ] ) ;
32
- this . maxBuildCount = 1 ;
33
- this . autoresolveThreshold = 250 ;
34
-
35
- this . _buildDeferred = RSVP . defer ( ) ;
36
- this . _isSynced = false ;
37
- this . _pendingErrors = [ ] ;
38
- this . _triggerDir = `${ this . outDir ( ) } /.rebuild` ;
39
- this . _pendingAutoresolve = null ;
40
- this . _didAutoresolve = false ;
29
+ this . state = new CompilerState ( ) ;
30
+
41
31
this . _watchProgram = null ;
42
32
}
43
33
44
34
treeForHost ( ) {
45
- let triggerTree = new Funnel ( this . _triggerDir , { destDir : 'app' } ) ;
46
-
47
35
let appTree = new TypescriptOutput ( this , {
48
36
[ `${ this . _relativeAppRoot ( ) } /app` ] : 'app' ,
49
37
} ) ;
@@ -53,7 +41,7 @@ module.exports = class IncrementalTypescriptCompiler {
53
41
[ mirage ] : 'app/mirage' ,
54
42
} ) ;
55
43
56
- let tree = new MergeTrees ( [ triggerTree , mirageTree , appTree ] . filter ( Boolean ) , { overwrite : true } ) ;
44
+ let tree = new MergeTrees ( [ mirageTree , appTree ] . filter ( Boolean ) , { overwrite : true } ) ;
57
45
return new Funnel ( tree , { srcDir : 'app' } ) ;
58
46
}
59
47
@@ -85,14 +73,14 @@ module.exports = class IncrementalTypescriptCompiler {
85
73
}
86
74
87
75
buildPromise ( ) {
88
- return this . _buildDeferred . promise ;
76
+ return this . state . buildDeferred . promise ;
89
77
}
90
78
91
79
outDir ( ) {
92
80
if ( ! this . _outDir ) {
93
81
let outDir = path . join ( tmpdir ( ) , `e-c-ts-${ process . pid } ` ) ;
94
82
this . _outDir = outDir ;
95
- mkdirp . sync ( outDir ) ;
83
+ fs . mkdirsSync ( outDir ) ;
96
84
}
97
85
98
86
return this . _outDir ;
@@ -104,47 +92,26 @@ module.exports = class IncrementalTypescriptCompiler {
104
92
return ;
105
93
}
106
94
107
- mkdirp . sync ( this . _triggerDir ) ;
108
- this . _touchRebuildTrigger ( ) ;
109
-
110
95
let project = this . project ;
111
96
let outDir = this . outDir ( ) ;
112
97
113
98
this . _watchProgram = compile ( project , { outDir, watch : true } , {
99
+ watchedFileChanged : ( ) => this . state . tscDidStart ( ) ,
100
+
114
101
reportWatchStatus : ( diagnostic ) => {
115
102
let text = diagnostic . messageText ;
116
-
117
- if ( text . indexOf ( 'Starting incremental compilation' ) !== - 1 ) {
118
- debugCompiler ( 'tsc detected a file change' ) ;
119
- this . willRebuild ( ) ;
120
- clearTimeout ( this . _pendingAutoresolve ) ;
121
- }
103
+ debugTsc ( text ) ;
122
104
123
105
if ( text . indexOf ( 'Compilation complete' ) !== - 1 ) {
124
- debugCompiler ( 'rebuild completed' ) ;
125
-
126
- this . didSync ( ) ;
127
-
128
- if ( this . _didAutoresolve ) {
129
- this . _touchRebuildTrigger ( ) ;
130
- this . maxBuildCount ++ ;
131
- }
132
-
133
- clearTimeout ( this . _pendingAutoresolve ) ;
134
- this . _didAutoresolve = false ;
106
+ this . state . tscDidEnd ( ) ;
135
107
}
136
108
} ,
137
109
138
110
reportDiagnostic : ( diagnostic ) => {
139
111
if ( diagnostic . category !== 2 ) {
140
- let message = ts . formatDiagnostic ( diagnostic , {
141
- getCanonicalFileName : path => path ,
142
- getCurrentDirectory : ts . sys . getCurrentDirectory ,
143
- getNewLine : ( ) => ts . sys . newLine ,
144
- } ) ;
145
-
112
+ let message = this . _formatDiagnosticMessage ( diagnostic ) ;
146
113
if ( this . _shouldFailOnTypeError ( ) ) {
147
- this . didError ( message ) ;
114
+ this . state . didError ( message ) ;
148
115
} else {
149
116
this . project . ui . write ( message ) ;
150
117
}
@@ -153,42 +120,18 @@ module.exports = class IncrementalTypescriptCompiler {
153
120
} ) ;
154
121
}
155
122
156
- willRebuild ( ) {
157
- if ( this . _isSynced ) {
158
- this . _isSynced = false ;
159
- this . _buildDeferred = RSVP . defer ( ) ;
160
-
161
- // Schedule a timer to automatically resolve if tsc doesn't pick up any file changes in a
162
- // short period. This may happen if a non-TS file changed, or if the tsc watcher is
163
- // drastically behind watchman. If the latter happens, we'll explicitly touch a file in the
164
- // broccoli output in order to ensure the changes are picked up.
165
- this . _pendingAutoresolve = setTimeout ( ( ) => {
166
- debugAutoresolve ( 'no tsc rebuild; autoresolving...' ) ;
167
-
168
- this . didSync ( ) ;
169
- this . _didAutoresolve = true ;
170
- } , this . autoresolveThreshold ) ;
171
- }
172
- }
173
-
174
- didError ( message ) {
175
- this . _pendingErrors . push ( message ) ;
176
- }
177
-
178
- didSync ( ) {
179
- this . _isSynced = true ;
180
- if ( this . _pendingErrors . length ) {
181
- this . _buildDeferred . reject ( new Error ( this . _pendingErrors . join ( '\n' ) ) ) ;
182
- this . _pendingErrors = [ ] ;
183
- } else {
184
- this . _buildDeferred . resolve ( ) ;
185
- }
186
- }
187
-
188
123
getProgram ( ) {
189
124
return this . _watchProgram . getProgram ( ) ;
190
125
}
191
126
127
+ _formatDiagnosticMessage ( diagnostic ) {
128
+ return ts . formatDiagnostic ( diagnostic , {
129
+ getCanonicalFileName : path => path ,
130
+ getCurrentDirectory : ts . sys . getCurrentDirectory ,
131
+ getNewLine : ( ) => ts . sys . newLine ,
132
+ } ) ;
133
+ }
134
+
192
135
_shouldFailOnTypeError ( ) {
193
136
let options = this . getProgram ( ) . getCompilerOptions ( ) ;
194
137
return ! ! options . noEmitOnError ;
@@ -219,11 +162,6 @@ module.exports = class IncrementalTypescriptCompiler {
219
162
}
220
163
}
221
164
222
- _touchRebuildTrigger ( ) {
223
- debugAutoresolve ( 'touching rebuild trigger.' ) ;
224
- fs . writeFileSync ( `${ this . _triggerDir } /tsc-delayed-rebuild` , '' , 'utf-8' ) ;
225
- }
226
-
227
165
_discoverAddons ( node , addons ) {
228
166
for ( let addon of node . addons ) {
229
167
let devDeps = addon . pkg . devDependencies || { } ;
@@ -261,43 +199,3 @@ module.exports = class IncrementalTypescriptCompiler {
261
199
}
262
200
} ;
263
201
264
- class TypescriptOutput extends Plugin {
265
- constructor ( compiler , paths ) {
266
- super ( [ ] ) ;
267
- this . compiler = compiler ;
268
- this . paths = paths ;
269
- this . buildCount = 0 ;
270
- }
271
-
272
- build ( ) {
273
- this . buildCount ++ ;
274
-
275
- // We use this to keep track of the build state between the various
276
- // Broccoli trees and tsc; when either tsc or broccoli notices a file
277
- // change, we immediately invalidate the previous build output.
278
- if ( this . buildCount > this . compiler . maxBuildCount ) {
279
- debugCompiler ( 'broccoli detected a file change' ) ;
280
- this . compiler . maxBuildCount = this . buildCount ;
281
- this . compiler . willRebuild ( ) ;
282
- }
283
-
284
- debugCompiler ( 'waiting for tsc output' , this . paths ) ;
285
- return this . compiler . buildPromise ( ) . then ( ( ) => {
286
- debugCompiler ( 'tsc build complete' , this . paths ) ;
287
- for ( let relativeSrc of Object . keys ( this . paths ) ) {
288
- let src = `${ this . compiler . outDir ( ) } /${ relativeSrc } ` ;
289
- let dest = `${ this . outputPath } /${ this . paths [ relativeSrc ] } ` ;
290
- if ( fs . existsSync ( src ) ) {
291
- let dir = path . dirname ( dest ) ;
292
- if ( dir !== '.' ) {
293
- mkdirp . sync ( dir ) ;
294
- }
295
-
296
- symlinkOrCopy . sync ( src , dest ) ;
297
- } else {
298
- mkdirp . sync ( dest ) ;
299
- }
300
- }
301
- } ) ;
302
- }
303
- }
0 commit comments