@@ -6,6 +6,8 @@ import * as child_process from 'child_process';
6
6
import * as http from 'http' ;
7
7
import mkdirp from 'mkdirp' ;
8
8
import rimraf from 'rimraf' ;
9
+ import format_messages from 'webpack-format-messages' ;
10
+ import prettyMs from 'pretty-ms' ;
9
11
import { wait_for_port } from './utils' ;
10
12
import { dest } from '../config' ;
11
13
import { create_compilers , create_app , create_routes , create_serviceworker } from 'sapper/core.js' ;
@@ -69,13 +71,12 @@ function create_hot_update_server(port: number, interval = 10000) {
69
71
}
70
72
71
73
export default async function dev ( ) {
72
- const dir = dest ( ) ;
74
+ process . env . NODE_ENV = 'development' ;
73
75
76
+ const dir = dest ( ) ;
74
77
rimraf . sync ( dir ) ;
75
78
mkdirp . sync ( dir ) ;
76
79
77
- const chokidar = require ( 'chokidar' ) ;
78
-
79
80
// initial build
80
81
const dev_port = await require ( 'get-port' ) ( 10000 ) ;
81
82
@@ -84,20 +85,6 @@ export default async function dev() {
84
85
85
86
const hot_update_server = create_hot_update_server ( dev_port ) ;
86
87
87
- // TODO watch the configs themselves?
88
- const compilers = create_compilers ( ) ;
89
-
90
- function watch_files ( pattern : string , events : string [ ] , callback : ( ) => void ) {
91
- const watcher = chokidar . watch ( pattern , {
92
- persistent : true ,
93
- ignoreInitial : true
94
- } ) ;
95
-
96
- events . forEach ( event => {
97
- watcher . on ( event , callback ) ;
98
- } ) ;
99
- }
100
-
101
88
watch_files ( 'routes/**/*' , [ 'add' , 'unlink' ] , ( ) => {
102
89
const routes = create_routes ( ) ;
103
90
create_app ( { routes, dev_port } ) ;
@@ -116,31 +103,106 @@ export default async function dev() {
116
103
client : deferred ( )
117
104
} ;
118
105
119
- const times = {
120
- client_start : Date . now ( ) ,
121
- server_start : Date . now ( ) ,
122
- serviceworker_start : Date . now ( )
106
+ let restarting = false ;
107
+ let build = {
108
+ unique_warnings : new Set ( ) ,
109
+ unique_errors : new Set ( )
123
110
} ;
124
111
125
- compilers . server . plugin ( 'invalid' , ( ) => {
126
- times . server_start = Date . now ( ) ;
127
- // TODO print message
128
- deferreds . server = deferred ( ) ;
129
- } ) ;
112
+ function restart_build ( filename ) {
113
+ if ( restarting ) return ;
130
114
131
- compilers . server . watch ( { } , ( err : Error , stats : any ) => {
132
- if ( err ) {
133
- console . error ( chalk . red ( err . message ) ) ;
134
- } else if ( stats . hasErrors ( ) ) {
135
- // print errors. TODO notify client
136
- stats . toJson ( ) . errors . forEach ( ( error : Error ) => {
137
- console . error ( error ) ; // TODO make this look nice
138
- } ) ;
139
- } else {
140
- console . log ( `built server in ${ Date . now ( ) - times . server_start } ms` ) ; // TODO prettify
115
+ restarting = true ;
116
+ build = {
117
+ unique_warnings : new Set ( ) ,
118
+ unique_errors : new Set ( )
119
+ } ;
120
+
121
+ process . nextTick ( ( ) => {
122
+ restarting = false ;
123
+ } ) ;
124
+
125
+ console . log ( `\n${ chalk . bold . cyan ( path . relative ( process . cwd ( ) , filename ) ) } changed. rebuilding...` ) ;
126
+ }
127
+
128
+ // TODO watch the configs themselves?
129
+ const compilers = create_compilers ( ) ;
130
+
131
+ function watch ( compiler : any , { name, invalid = noop , error = noop , result } : {
132
+ name : string ,
133
+ invalid ?: ( filename : string ) => void ;
134
+ error ?: ( error : Error ) => void ;
135
+ result : ( stats : any ) => void ;
136
+ } ) {
137
+ compiler . plugin ( 'invalid' , ( filename : string ) => {
138
+ invalid ( filename ) ;
139
+ } ) ;
140
+
141
+ compiler . watch ( { } , ( err : Error , stats : any ) => {
142
+ if ( err ) {
143
+ console . error ( chalk . red ( `✗ ${ name } ` ) ) ;
144
+ console . error ( chalk . red ( err . message ) ) ;
145
+ error ( err ) ;
146
+ } else {
147
+ const messages = format_messages ( stats ) ;
148
+ const info = stats . toJson ( ) ;
149
+
150
+ if ( messages . errors . length > 0 ) {
151
+ console . log ( chalk . bold . red ( `✗ ${ name } ` ) ) ;
141
152
142
- const server_info = stats . toJson ( ) ;
143
- fs . writeFileSync ( path . join ( dir , 'server_info.json' ) , JSON . stringify ( server_info , null , ' ' ) ) ;
153
+ const filtered = messages . errors . filter ( ( message : string ) => {
154
+ return ! build . unique_errors . has ( message ) ;
155
+ } ) ;
156
+
157
+ filtered . forEach ( ( message : string ) => {
158
+ build . unique_errors . add ( message ) ;
159
+ console . log ( message ) ;
160
+ } ) ;
161
+
162
+ const hidden = messages . errors . length - filtered . length ;
163
+ if ( hidden > 0 ) {
164
+ console . log ( `${ hidden } duplicate ${ hidden === 1 ? 'error' : 'errors' } hidden\n` ) ;
165
+ }
166
+ } else {
167
+ if ( messages . warnings . length > 0 ) {
168
+ console . log ( chalk . bold . yellow ( `• ${ name } ` ) ) ;
169
+
170
+ const filtered = messages . warnings . filter ( ( message : string ) => {
171
+ return ! build . unique_warnings . has ( message ) ;
172
+ } ) ;
173
+
174
+ filtered . forEach ( ( message : string ) => {
175
+ build . unique_warnings . add ( message ) ;
176
+ console . log ( `${ message } \n` ) ;
177
+ } ) ;
178
+
179
+ const hidden = messages . warnings . length - filtered . length ;
180
+ if ( hidden > 0 ) {
181
+ console . log ( `${ hidden } duplicate ${ hidden === 1 ? 'warning' : 'warnings' } hidden\n` ) ;
182
+ }
183
+ } else {
184
+ console . log ( `${ chalk . bold . green ( `✔ ${ name } ` ) } ${ chalk . grey ( `(${ prettyMs ( info . time ) } )` ) } ` ) ;
185
+ }
186
+
187
+ result ( info ) ;
188
+ }
189
+ }
190
+ } ) ;
191
+ }
192
+
193
+ watch ( compilers . server , {
194
+ name : 'server' ,
195
+
196
+ invalid : filename => {
197
+ restart_build ( filename ) ;
198
+ // TODO print message
199
+ deferreds . server = deferred ( ) ;
200
+ } ,
201
+
202
+ result : info => {
203
+ // TODO log compile errors/warnings
204
+
205
+ fs . writeFileSync ( path . join ( dir , 'server_info.json' ) , JSON . stringify ( info , null , ' ' ) ) ;
144
206
145
207
deferreds . client . promise . then ( ( ) => {
146
208
function restart ( ) {
@@ -162,32 +224,23 @@ export default async function dev() {
162
224
}
163
225
} ) ;
164
226
165
- compilers . client . plugin ( 'invalid' , ( filename : string ) => {
166
- times . client_start = Date . now ( ) ;
227
+ watch ( compilers . client , {
228
+ name : 'client' ,
167
229
168
- deferreds . client = deferred ( ) ;
230
+ invalid : filename => {
231
+ restart_build ( filename ) ;
232
+ deferreds . client = deferred ( ) ;
169
233
170
- // TODO we should delete old assets. due to a webpack bug
171
- // i don't even begin to comprehend, this is apparently
172
- // quite difficult
173
- } ) ;
234
+ // TODO we should delete old assets. due to a webpack bug
235
+ // i don't even begin to comprehend, this is apparently
236
+ // quite difficult
237
+ } ,
174
238
175
- compilers . client . watch ( { } , ( err : Error , stats : any ) => {
176
- if ( err ) {
177
- console . error ( chalk . red ( err . message ) ) ;
178
- } else if ( stats . hasErrors ( ) ) {
179
- // print errors. TODO notify client
180
- stats . toJson ( ) . errors . forEach ( ( error : Error ) => {
181
- console . error ( error ) ; // TODO make this look nice
182
- } ) ;
183
- } else {
184
- console . log ( `built client in ${ Date . now ( ) - times . client_start } ms` ) ; // TODO prettify
185
-
186
- const client_info = stats . toJson ( ) ;
187
- fs . writeFileSync ( path . join ( dir , 'client_info.json' ) , JSON . stringify ( client_info , null , ' ' ) ) ;
239
+ result : info => {
240
+ fs . writeFileSync ( path . join ( dir , 'client_info.json' ) , JSON . stringify ( info , null , ' ' ) ) ;
188
241
deferreds . client . fulfil ( ) ;
189
242
190
- const client_files = client_info . assets . map ( ( chunk : { name : string } ) => `/client/${ chunk . name } ` ) ;
243
+ const client_files = info . assets . map ( ( chunk : { name : string } ) => `/client/${ chunk . name } ` ) ;
191
244
192
245
deferreds . server . promise . then ( ( ) => {
193
246
hot_update_server . send ( {
@@ -208,27 +261,28 @@ export default async function dev() {
208
261
? function ( ) {
209
262
watch_serviceworker = noop ;
210
263
211
- compilers . serviceworker . plugin ( 'invalid' , ( filename : string ) => {
212
- times . serviceworker_start = Date . now ( ) ;
213
- } ) ;
214
-
215
- compilers . serviceworker . watch ( { } , ( err : Error , stats : any ) => {
216
- if ( err ) {
217
- // TODO notify client
218
- } else if ( stats . hasErrors ( ) ) {
219
- // print errors. TODO notify client
220
- stats . toJson ( ) . errors . forEach ( ( error : Error ) => {
221
- console . error ( error ) ; // TODO make this look nice
222
- } ) ;
223
- } else {
224
- console . log ( `built service worker in ${ Date . now ( ) - times . serviceworker_start } ms` ) ; // TODO prettify
264
+ watch ( compilers . serviceworker , {
265
+ name : 'service worker' ,
225
266
226
- const serviceworker_info = stats . toJson ( ) ;
227
- fs . writeFileSync ( path . join ( dir , 'serviceworker_info.json' ) , JSON . stringify ( serviceworker_info , null , ' ' ) ) ;
267
+ result : info => {
268
+ fs . writeFileSync ( path . join ( dir , 'serviceworker_info.json' ) , JSON . stringify ( info , null , ' ' ) ) ;
228
269
}
229
270
} ) ;
230
271
}
231
272
: noop ;
232
273
}
233
274
234
- function noop ( ) { }
275
+ function noop ( ) { }
276
+
277
+ function watch_files ( pattern : string , events : string [ ] , callback : ( ) => void ) {
278
+ const chokidar = require ( 'chokidar' ) ;
279
+
280
+ const watcher = chokidar . watch ( pattern , {
281
+ persistent : true ,
282
+ ignoreInitial : true
283
+ } ) ;
284
+
285
+ events . forEach ( event => {
286
+ watcher . on ( event , callback ) ;
287
+ } ) ;
288
+ }
0 commit comments