| Index: lib/devc.dart
|
| diff --git a/lib/devc.dart b/lib/devc.dart
|
| index b257410496ccc5bc1d17a97eeb93f078c613c3c0..bfe3d5d43ff02e5e4ff391c2e6fcdf95886344a8 100644
|
| --- a/lib/devc.dart
|
| +++ b/lib/devc.dart
|
| @@ -28,6 +28,7 @@ import 'src/dependency_graph.dart';
|
| import 'src/info.dart' show LibraryInfo, CheckerResults;
|
| import 'src/options.dart';
|
| import 'src/report.dart';
|
| +import 'src/utils.dart';
|
|
|
| /// Sets up the type checker logger to print a span that highlights error
|
| /// messages.
|
| @@ -50,6 +51,7 @@ class Compiler {
|
| final SourceNode _entryNode;
|
| List<LibraryInfo> _libraries = <LibraryInfo>[];
|
| final List<CodeGenerator> _generators;
|
| + final bool _hashing;
|
| bool _failure = false;
|
|
|
| factory Compiler(CompilerOptions options,
|
| @@ -65,7 +67,7 @@ class Compiler {
|
| ? new SummaryReporter()
|
| : new LogReporter(options.useColors);
|
| }
|
| - var graph = new SourceGraph(resolver.context, reporter);
|
| + var graph = new SourceGraph(resolver.context, reporter, options);
|
| var rules = new RestrictedRules(resolver.context.typeProvider, reporter,
|
| options: options);
|
| var checker = new CodeChecker(rules, reporter, options);
|
| @@ -87,11 +89,14 @@ class Compiler {
|
| : new JSGenerator(outputDir, entryNode.uri, rules, options));
|
| }
|
| return new Compiler._(options, resolver, reporter, rules, checker, graph,
|
| - entryNode, generators);
|
| + entryNode, generators,
|
| + // TODO(sigmund): refactor to support hashing of the dart output?
|
| + options.serverMode && generators.length == 1 && !options.outputDart);
|
| }
|
|
|
| Compiler._(this._options, this._resolver, this._reporter, this._rules,
|
| - this._checker, this._graph, this._entryNode, this._generators);
|
| + this._checker, this._graph, this._entryNode, this._generators,
|
| + this._hashing);
|
|
|
| bool _buildSource(SourceNode node) {
|
| if (node is HtmlSourceNode) {
|
| @@ -132,10 +137,12 @@ class Compiler {
|
| // dev_compiler runtime.
|
| if (_options.outputDir == null || _options.outputDart) return;
|
| assert(node.uri.scheme == 'package');
|
| - var filepath = path.join(_options.outputDir, node.uri.path);
|
| + var filepath = path.join(_options.outputDir, resourceOutputPath(node.uri));
|
| var dir = path.dirname(filepath);
|
| new Directory(dir).createSync(recursive: true);
|
| - new File(filepath).writeAsStringSync(node.source.contents.data);
|
| + var text = node.source.contents.data;
|
| + new File(filepath).writeAsStringSync(text);
|
| + if (_hashing) node.cachingHash = computeHash(text);
|
| }
|
|
|
| bool _isEntry(DartSourceNode node) {
|
| @@ -177,8 +184,10 @@ class Compiler {
|
| _failure = true;
|
| if (!_options.forceCompile) return;
|
| }
|
| +
|
| for (var cg in _generators) {
|
| - cg.generateLibrary(units, current, _reporter);
|
| + var hash = cg.generateLibrary(units, current, _reporter);
|
| + if (_hashing) node.cachingHash = hash;
|
| }
|
| _reporter.leaveLibrary();
|
| }
|
| @@ -194,10 +203,8 @@ class Compiler {
|
| rebuild(_entryNode, _graph, _buildSource);
|
| _dumpInfoIfRequested();
|
| clock.stop();
|
| - if (_options.serverMode) {
|
| - var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2);
|
| - print('Compiled ${_libraries.length} libraries in ${time} s\n');
|
| - }
|
| + var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2);
|
| + _log.fine('Compiled ${_libraries.length} libraries in ${time} s\n');
|
| return new CheckerResults(
|
| _libraries, _rules, _failure || _options.forceCompile);
|
| }
|
| @@ -215,7 +222,7 @@ class Compiler {
|
| clock.stop();
|
| if (changed > 0) _dumpInfoIfRequested();
|
| var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2);
|
| - print("Compiled ${changed} libraries in ${time} s\n");
|
| + _log.fine("Compiled ${changed} libraries in ${time} s\n");
|
| }
|
|
|
| _dumpInfoIfRequested() {
|
| @@ -251,7 +258,7 @@ class CompilerServer {
|
| exit(1);
|
| }
|
| var port = options.port;
|
| - print('[dev_compiler]: Serving $entryPath at http://0.0.0.0:$port/');
|
| + _log.fine('Serving $entryPath at http://0.0.0.0:$port/');
|
| var compiler = new Compiler(options);
|
| return new CompilerServer._(compiler, outDir, port, entryPath);
|
| }
|
| @@ -260,17 +267,40 @@ class CompilerServer {
|
|
|
| Future start() async {
|
| var handler = const shelf.Pipeline()
|
| - .addMiddleware(shelf.createMiddleware(requestHandler: rebuildIfNeeded))
|
| + .addMiddleware(rebuildAndCache)
|
| .addHandler(shelf_static.createStaticHandler(outDir,
|
| defaultDocument: _entryPath));
|
| await shelf.serve(handler, '0.0.0.0', port);
|
| compiler.run();
|
| }
|
|
|
| - rebuildIfNeeded(shelf.Request request) {
|
| - var filepath = request.url.path;
|
| - if (filepath == '/$_entryPath' || filepath == '/') compiler._runAgain();
|
| - }
|
| + shelf.Handler rebuildAndCache(shelf.Handler handler) => (request) {
|
| + _log.fine('requested $GREEN_COLOR${request.url}$NO_COLOR');
|
| + // Trigger recompile only when requesting the HTML page.
|
| + var segments = request.url.pathSegments;
|
| + bool isEntryPage = segments.length == 0 || segments[0] == _entryPath;
|
| + if (isEntryPage) compiler._runAgain();
|
| +
|
| + // To help browsers cache resources that don't change, we serve these
|
| + // resources under a path containing their hash:
|
| + // /cached/{hash}/{path-to-file.js}
|
| + bool isCached = segments.length > 1 && segments[0] == 'cached';
|
| + if (isCached) {
|
| + // Changing the request lets us record that the hash prefix is handled
|
| + // here, and that the underlying handler should use the rest of the url to
|
| + // determine where to find the resource in the file system.
|
| + request = request.change(path: path.join('cached', segments[1]));
|
| + }
|
| + var response = handler(request);
|
| + var policy = isCached ? 'max-age=${24 * 60 * 60}' : 'no-cache';
|
| + var headers = {'cache-control': policy};
|
| + if (isCached) {
|
| + // Note: the cache-control header should be enough, but this doesn't hurt
|
| + // and can help renew the policy after it expires.
|
| + headers['ETag'] = segments[1];
|
| + }
|
| + return response.change(headers: headers);
|
| + };
|
| }
|
|
|
| final _log = new Logger('dev_compiler');
|
|
|