11'use strict' ;
22
3- const tmpdir = require ( './utilities/tmpdir' ) ;
4- const mkdirp = require ( 'mkdirp' ) ;
3+ const tmpdir = require ( '../utilities/tmpdir' ) ;
54const Funnel = require ( 'broccoli-funnel' ) ;
65const MergeTrees = require ( 'broccoli-merge-trees' ) ;
7- const symlinkOrCopy = require ( 'symlink-or-copy' ) ;
8- const Plugin = require ( 'broccoli-plugin' ) ;
9- const RSVP = require ( 'rsvp' ) ;
106const path = require ( 'path' ) ;
11- const fs = require ( 'fs' ) ;
7+ const fs = require ( 'fs-extra ' ) ;
128const resolve = require ( 'resolve' ) ;
13- const compile = require ( './utilities/compile' ) ;
9+ const compile = require ( '.. /utilities/compile' ) ;
1410const ts = require ( 'typescript' ) ;
11+ const TypescriptOutput = require ( './typescript-output-plugin' ) ;
12+ const CompilerState = require ( './compiler-state' ) ;
1513
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' ) ;
1815
1916module . exports = class IncrementalTypescriptCompiler {
2017 constructor ( app , project ) {
@@ -29,21 +26,12 @@ module.exports = class IncrementalTypescriptCompiler {
2926 this . app = app ;
3027 this . project = project ;
3128 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+
4131 this . _watchProgram = null ;
4232 }
4333
4434 treeForHost ( ) {
45- let triggerTree = new Funnel ( this . _triggerDir , { destDir : 'app' } ) ;
46-
4735 let appTree = new TypescriptOutput ( this , {
4836 [ `${ this . _relativeAppRoot ( ) } /app` ] : 'app' ,
4937 } ) ;
@@ -53,7 +41,7 @@ module.exports = class IncrementalTypescriptCompiler {
5341 [ mirage ] : 'app/mirage' ,
5442 } ) ;
5543
56- let tree = new MergeTrees ( [ triggerTree , mirageTree , appTree ] . filter ( Boolean ) , { overwrite : true } ) ;
44+ let tree = new MergeTrees ( [ mirageTree , appTree ] . filter ( Boolean ) , { overwrite : true } ) ;
5745 return new Funnel ( tree , { srcDir : 'app' } ) ;
5846 }
5947
@@ -85,14 +73,14 @@ module.exports = class IncrementalTypescriptCompiler {
8573 }
8674
8775 buildPromise ( ) {
88- return this . _buildDeferred . promise ;
76+ return this . state . buildDeferred . promise ;
8977 }
9078
9179 outDir ( ) {
9280 if ( ! this . _outDir ) {
9381 let outDir = path . join ( tmpdir ( ) , `e-c-ts-${ process . pid } ` ) ;
9482 this . _outDir = outDir ;
95- mkdirp . sync ( outDir ) ;
83+ fs . mkdirsSync ( outDir ) ;
9684 }
9785
9886 return this . _outDir ;
@@ -104,47 +92,26 @@ module.exports = class IncrementalTypescriptCompiler {
10492 return ;
10593 }
10694
107- mkdirp . sync ( this . _triggerDir ) ;
108- this . _touchRebuildTrigger ( ) ;
109-
11095 let project = this . project ;
11196 let outDir = this . outDir ( ) ;
11297
11398 this . _watchProgram = compile ( project , { outDir, watch : true } , {
99+ watchedFileChanged : ( ) => this . state . tscDidStart ( ) ,
100+
114101 reportWatchStatus : ( diagnostic ) => {
115102 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 ) ;
122104
123105 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 ( ) ;
135107 }
136108 } ,
137109
138110 reportDiagnostic : ( diagnostic ) => {
139111 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 ) ;
146113 if ( this . _shouldFailOnTypeError ( ) ) {
147- this . didError ( message ) ;
114+ this . state . didError ( message ) ;
148115 } else {
149116 this . project . ui . write ( message ) ;
150117 }
@@ -153,42 +120,18 @@ module.exports = class IncrementalTypescriptCompiler {
153120 } ) ;
154121 }
155122
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-
188123 getProgram ( ) {
189124 return this . _watchProgram . getProgram ( ) ;
190125 }
191126
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+
192135 _shouldFailOnTypeError ( ) {
193136 let options = this . getProgram ( ) . getCompilerOptions ( ) ;
194137 return ! ! options . noEmitOnError ;
@@ -219,11 +162,6 @@ module.exports = class IncrementalTypescriptCompiler {
219162 }
220163 }
221164
222- _touchRebuildTrigger ( ) {
223- debugAutoresolve ( 'touching rebuild trigger.' ) ;
224- fs . writeFileSync ( `${ this . _triggerDir } /tsc-delayed-rebuild` , '' , 'utf-8' ) ;
225- }
226-
227165 _discoverAddons ( node , addons ) {
228166 for ( let addon of node . addons ) {
229167 let devDeps = addon . pkg . devDependencies || { } ;
@@ -261,43 +199,3 @@ module.exports = class IncrementalTypescriptCompiler {
261199 }
262200} ;
263201
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