@@ -17,22 +17,10 @@ const inquirer = require('inquirer');
1717const clearConsole = require ( './clearConsole' ) ;
1818const formatWebpackMessages = require ( './formatWebpackMessages' ) ;
1919const getProcessForPort = require ( './getProcessForPort' ) ;
20+ const typescriptFormatter = require ( './typescriptFormatter' ) ;
21+ const forkTsCheckerWebpackPlugin = require ( 'fork-ts-checker-webpack-plugin' ) ;
2022
2123const isInteractive = process . stdout . isTTY ;
22- let handleCompile ;
23-
24- // You can safely remove this after ejecting.
25- // We only use this block for testing of Create React App itself:
26- const isSmokeTest = process . argv . some ( arg => arg . indexOf ( '--smoke-test' ) > - 1 ) ;
27- if ( isSmokeTest ) {
28- handleCompile = ( err , stats ) => {
29- if ( err || stats . hasErrors ( ) || stats . hasWarnings ( ) ) {
30- process . exit ( 1 ) ;
31- } else {
32- process . exit ( 0 ) ;
33- }
34- } ;
35- }
3624
3725function prepareUrls ( protocol , host , port ) {
3826 const formatUrl = hostname =>
@@ -113,12 +101,20 @@ function printInstructions(appName, urls, useYarn) {
113101 console . log ( ) ;
114102}
115103
116- function createCompiler ( webpack , config , appName , urls , useYarn ) {
104+ function createCompiler (
105+ webpack ,
106+ config ,
107+ appName ,
108+ urls ,
109+ useYarn ,
110+ useTypeScript ,
111+ devSocket
112+ ) {
117113 // "Compiler" is a low-level interface to Webpack.
118114 // It lets us listen to some events and provide our own custom messages.
119115 let compiler ;
120116 try {
121- compiler = webpack ( config , handleCompile ) ;
117+ compiler = webpack ( config ) ;
122118 } catch ( err ) {
123119 console . log ( chalk . red ( 'Failed to compile.' ) ) ;
124120 console . log ( ) ;
@@ -139,10 +135,35 @@ function createCompiler(webpack, config, appName, urls, useYarn) {
139135 } ) ;
140136
141137 let isFirstCompile = true ;
138+ let tsMessagesPromise ;
139+ let tsMessagesResolver ;
140+
141+ if ( useTypeScript ) {
142+ compiler . hooks . beforeCompile . tap ( 'beforeCompile' , ( ) => {
143+ tsMessagesPromise = new Promise ( resolve => {
144+ tsMessagesResolver = msgs => resolve ( msgs ) ;
145+ } ) ;
146+ } ) ;
147+
148+ forkTsCheckerWebpackPlugin
149+ . getCompilerHooks ( compiler )
150+ . receive . tap ( 'afterTypeScriptCheck' , ( diagnostics , lints ) => {
151+ const allMsgs = [ ...diagnostics , ...lints ] ;
152+ const format = message =>
153+ `${ message . file } \n${ typescriptFormatter ( message , true ) } ` ;
154+
155+ tsMessagesResolver ( {
156+ errors : allMsgs . filter ( msg => msg . severity === 'error' ) . map ( format ) ,
157+ warnings : allMsgs
158+ . filter ( msg => msg . severity === 'warning' )
159+ . map ( format ) ,
160+ } ) ;
161+ } ) ;
162+ }
142163
143164 // "done" event fires when Webpack has finished recompiling the bundle.
144165 // Whether or not you have warnings or errors, you will get this event.
145- compiler . hooks . done . tap ( 'done' , stats => {
166+ compiler . hooks . done . tap ( 'done' , async stats => {
146167 if ( isInteractive ) {
147168 clearConsole ( ) ;
148169 }
@@ -152,9 +173,43 @@ function createCompiler(webpack, config, appName, urls, useYarn) {
152173 // them in a readable focused way.
153174 // We only construct the warnings and errors for speed:
154175 // https://github.com/facebook/create-react-app/issues/4492#issuecomment-421959548
155- const messages = formatWebpackMessages (
156- stats . toJson ( { all : false , warnings : true , errors : true } )
157- ) ;
176+ const statsData = stats . toJson ( {
177+ all : false ,
178+ warnings : true ,
179+ errors : true ,
180+ } ) ;
181+
182+ if ( useTypeScript && statsData . errors . length === 0 ) {
183+ const delayedMsg = setTimeout ( ( ) => {
184+ console . log (
185+ chalk . yellow (
186+ 'Files successfully emitted, waiting for typecheck results...'
187+ )
188+ ) ;
189+ } , 100 ) ;
190+
191+ const messages = await tsMessagesPromise ;
192+ clearTimeout ( delayedMsg ) ;
193+ statsData . errors . push ( ...messages . errors ) ;
194+ statsData . warnings . push ( ...messages . warnings ) ;
195+
196+ // Push errors and warnings into compilation result
197+ // to show them after page refresh triggered by user.
198+ stats . compilation . errors . push ( ...messages . errors ) ;
199+ stats . compilation . warnings . push ( ...messages . warnings ) ;
200+
201+ if ( messages . errors . length > 0 ) {
202+ devSocket . errors ( messages . errors ) ;
203+ } else if ( messages . warnings . length > 0 ) {
204+ devSocket . warnings ( messages . warnings ) ;
205+ }
206+
207+ if ( isInteractive ) {
208+ clearConsole ( ) ;
209+ }
210+ }
211+
212+ const messages = formatWebpackMessages ( statsData ) ;
158213 const isSuccessful = ! messages . errors . length && ! messages . warnings . length ;
159214 if ( isSuccessful ) {
160215 console . log ( chalk . green ( 'Compiled successfully!' ) ) ;
@@ -194,6 +249,27 @@ function createCompiler(webpack, config, appName, urls, useYarn) {
194249 ) ;
195250 }
196251 } ) ;
252+
253+ // You can safely remove this after ejecting.
254+ // We only use this block for testing of Create React App itself:
255+ const isSmokeTest = process . argv . some (
256+ arg => arg . indexOf ( '--smoke-test' ) > - 1
257+ ) ;
258+ if ( isSmokeTest ) {
259+ compiler . hooks . failed . tap ( 'smokeTest' , async ( ) => {
260+ await tsMessagesPromise ;
261+ process . exit ( 1 ) ;
262+ } ) ;
263+ compiler . hooks . done . tap ( 'smokeTest' , async stats => {
264+ await tsMessagesPromise ;
265+ if ( stats . hasErrors ( ) || stats . hasWarnings ( ) ) {
266+ process . exit ( 1 ) ;
267+ } else {
268+ process . exit ( 0 ) ;
269+ }
270+ } ) ;
271+ }
272+
197273 return compiler ;
198274}
199275
0 commit comments