OLD | NEW |
---|---|
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 /// Development server that compiles Dart to JS on the fly. | 5 /// Development server that compiles Dart to JS on the fly. |
6 library dev_compiler.src.server; | 6 library dev_compiler.src.server; |
7 | 7 |
8 import 'dart:async'; | 8 import 'dart:async'; |
9 import 'dart:convert'; | 9 import 'dart:convert'; |
10 import 'dart:io'; | 10 import 'dart:io'; |
(...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
239 } | 239 } |
240 | 240 |
241 DevServer._(ServerCompiler compiler, this.outDir, this.host, this.port, | 241 DevServer._(ServerCompiler compiler, this.outDir, this.host, this.port, |
242 String entryPath) | 242 String entryPath) |
243 : this.compiler = compiler, | 243 : this.compiler = compiler, |
244 // TODO(jmesserly): this logic is duplicated in a few places | 244 // TODO(jmesserly): this logic is duplicated in a few places |
245 this._entryPath = compiler.options.sourceOptions.useImplicitHtml | 245 this._entryPath = compiler.options.sourceOptions.useImplicitHtml |
246 ? SourceResolverOptions.implicitHtmlFile | 246 ? SourceResolverOptions.implicitHtmlFile |
247 : entryPath; | 247 : entryPath; |
248 | 248 |
249 Future<bool> start() async { | 249 Future start() async { |
250 // Create output directory if needed. shelf_static will fail otherwise. | 250 // Create output directory if needed. shelf_static will fail otherwise. |
251 var out = new Directory(outDir); | 251 var out = new Directory(outDir); |
252 if (!await out.exists()) await out.create(recursive: true); | 252 if (!await out.exists()) await out.create(recursive: true); |
253 | 253 |
254 var handler = const shelf.Pipeline() | 254 var handler = const shelf.Pipeline() |
255 .addMiddleware(rebuildAndCache) | 255 .addMiddleware(rebuildAndCache) |
256 .addHandler(shelf_static.createStaticHandler(outDir, | 256 .addHandler(shelf_static.createStaticHandler(outDir, |
257 defaultDocument: _entryPath)); | 257 defaultDocument: _entryPath)); |
258 await shelf.serve(handler, host, port); | 258 await shelf.serve(handler, host, port); |
259 print('Serving $_entryPath at http://$host:$port/'); | 259 print('Serving $_entryPath at http://$host:$port/'); |
260 CheckerResults results = compiler.run(); | 260 // Never returns |
Leaf
2015/08/04 22:27:59
I don't follow this comment. Does shelf.serve nev
Jennifer Messerly
2015/08/04 22:41:36
Good catch, the comment is wrong. Removed. Also re
| |
261 return !results.failure; | |
262 } | 261 } |
263 | 262 |
264 shelf.Handler rebuildAndCache(shelf.Handler handler) => (request) { | 263 shelf.Handler rebuildAndCache(shelf.Handler handler) => (request) { |
265 print('requested $GREEN_COLOR${request.url}$NO_COLOR'); | 264 print('requested $GREEN_COLOR${request.url}$NO_COLOR'); |
266 // Trigger recompile only when requesting the HTML page. | 265 // Trigger recompile only when requesting the HTML page. |
267 var segments = request.url.pathSegments; | 266 var segments = request.url.pathSegments; |
268 bool isEntryPage = segments.length == 0 || segments[0] == _entryPath; | 267 bool isEntryPage = segments.length == 0 || segments[0] == _entryPath; |
269 if (isEntryPage) compiler._runAgain(); | 268 if (isEntryPage) compiler._runAgain(); |
270 | 269 |
271 // To help browsers cache resources that don't change, we serve these | 270 // To help browsers cache resources that don't change, we serve these |
272 // resources by adding a query parameter containing their hash: | 271 // resources by adding a query parameter containing their hash: |
273 // /{path-to-file.js}?____cached={hash} | 272 // /{path-to-file.js}?____cached={hash} |
274 var hash = request.url.queryParameters['____cached']; | 273 var hash = request.url.queryParameters['____cached']; |
275 var response = handler(request); | 274 var response = handler(request); |
276 var policy = hash != null ? 'max-age=${24 * 60 * 60}' : 'no-cache'; | 275 var policy = hash != null ? 'max-age=${24 * 60 * 60}' : 'no-cache'; |
277 var headers = {'cache-control': policy}; | 276 var headers = {'cache-control': policy}; |
278 if (hash != null) { | 277 if (hash != null) { |
279 // Note: the cache-control header should be enough, but this doesn't | 278 // Note: the cache-control header should be enough, but this doesn't |
280 // hurt and can help renew the policy after it expires. | 279 // hurt and can help renew the policy after it expires. |
281 headers['ETag'] = hash; | 280 headers['ETag'] = hash; |
282 } | 281 } |
283 return response.change(headers: headers); | 282 return response.change(headers: headers); |
284 }; | 283 }; |
285 } | 284 } |
286 | 285 |
287 UriResolver _createImplicitEntryResolver(String entryPath) { | 286 UriResolver _createImplicitEntryResolver(String entryPath) { |
288 var entry = path.absolute(SourceResolverOptions.implicitHtmlFile); | 287 var entry = path.toUri(path.absolute(SourceResolverOptions.implicitHtmlFile)); |
289 var src = path.absolute(entryPath); | 288 var src = path.toUri(path.absolute(entryPath)); |
290 var provider = new MemoryResourceProvider(); | 289 var provider = new MemoryResourceProvider(); |
291 provider.newFile( | 290 provider.newFile( |
292 entry, '<body><script type="application/dart" src="$src"></script>'); | 291 entry.path, '<body><script type="application/dart" src="$src"></script>'); |
293 return new _ExistingSourceUriResolver(new ResourceUriResolver(provider)); | 292 return new _ExistingSourceUriResolver(new ResourceUriResolver(provider)); |
294 } | 293 } |
295 | 294 |
296 /// A UriResolver that continues to the next one if it fails to find an existing | 295 /// A UriResolver that continues to the next one if it fails to find an existing |
297 /// source file. This is unlike normal URI resolvers, that always return | 296 /// source file. This is unlike normal URI resolvers, that always return |
298 /// something, even if it is a non-existing file. | 297 /// something, even if it is a non-existing file. |
299 class _ExistingSourceUriResolver implements UriResolver { | 298 class _ExistingSourceUriResolver implements UriResolver { |
300 final UriResolver resolver; | 299 final UriResolver resolver; |
301 _ExistingSourceUriResolver(this.resolver); | 300 _ExistingSourceUriResolver(this.resolver); |
302 | 301 |
303 Source resolveAbsolute(Uri uri, [Uri actualUri]) { | 302 Source resolveAbsolute(Uri uri, [Uri actualUri]) { |
304 var src = resolver.resolveAbsolute(uri, actualUri); | 303 var src = resolver.resolveAbsolute(uri, actualUri); |
305 return src.exists() ? src : null; | 304 return src.exists() ? src : null; |
306 } | 305 } |
307 | 306 |
308 Uri restoreAbsolute(Source source) => resolver.restoreAbsolute(source); | 307 Uri restoreAbsolute(Source source) => resolver.restoreAbsolute(source); |
309 } | 308 } |
310 | 309 |
311 final _log = new Logger('dev_compiler.src.server'); | 310 final _log = new Logger('dev_compiler.src.server'); |
312 final _earlyErrorResult = new CheckerResults(const [], null, true); | 311 final _earlyErrorResult = new CheckerResults(const [], null, true); |
OLD | NEW |