Index: lib/src/server/server.dart |
diff --git a/lib/devc.dart b/lib/src/server/server.dart |
similarity index 64% |
copy from lib/devc.dart |
copy to lib/src/server/server.dart |
index ae22b89cdb66e14d92748ab54fd1639bf81ffa27..b9a69d510fed22e1c59cdb4b7381a1c5bcea2a98 100644 |
--- a/lib/devc.dart |
+++ b/lib/src/server/server.dart |
@@ -2,100 +2,66 @@ |
// for details. All rights reserved. Use of this source code is governed by a |
// BSD-style license that can be found in the LICENSE file. |
-/// Command line tool to run the checker on a Dart program. |
-library dev_compiler.devc; |
+/// Development server that compiles Dart to JS on the fly. |
+library dev_compiler.src.server; |
import 'dart:async'; |
import 'dart:convert'; |
import 'dart:io'; |
+import 'package:analyzer/file_system/file_system.dart' show ResourceUriResolver; |
+import 'package:analyzer/file_system/memory_file_system.dart'; |
import 'package:analyzer/src/generated/engine.dart' |
show AnalysisContext, ChangeSet; |
-import 'package:analyzer/src/generated/error.dart' |
- show AnalysisError, AnalysisErrorListener, ErrorSeverity, ErrorType; |
-import 'package:analyzer/src/generated/source.dart' show Source; |
+import 'package:analyzer/src/generated/error.dart'; |
+import 'package:analyzer/src/generated/source.dart'; |
import 'package:logging/logging.dart' show Level, Logger, LogRecord; |
import 'package:path/path.dart' as path; |
import 'package:shelf/shelf.dart' as shelf; |
import 'package:shelf/shelf_io.dart' as shelf; |
import 'package:shelf_static/shelf_static.dart' as shelf_static; |
-import 'src/analysis_context.dart'; |
-import 'src/checker/checker.dart'; |
-import 'src/checker/rules.dart'; |
-import 'src/codegen/code_generator.dart' show CodeGenerator; |
-import 'src/codegen/html_codegen.dart'; |
-import 'src/codegen/js_codegen.dart'; |
-import 'src/dependency_graph.dart'; |
-import 'src/info.dart' |
+import 'package:dev_compiler/src/codegen/code_generator.dart' |
+ show CodeGenerator; |
+import 'package:dev_compiler/src/codegen/html_codegen.dart' |
+ show generateEntryHtml; |
+import 'package:dev_compiler/src/codegen/js_codegen.dart'; |
+import 'package:dev_compiler/src/analysis_context.dart'; |
+import 'package:dev_compiler/src/compiler.dart' show AbstractCompiler; |
+import 'package:dev_compiler/src/info.dart' |
show AnalyzerMessage, CheckerResults, LibraryInfo, LibraryUnit; |
-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. |
-StreamSubscription setupLogger(Level level, printFn) { |
- Logger.root.level = level; |
- return Logger.root.onRecord.listen((LogRecord rec) { |
- printFn('${rec.level.name.toLowerCase()}: ${rec.message}'); |
- }); |
-} |
+import 'package:dev_compiler/src/options.dart'; |
+import 'package:dev_compiler/src/report.dart'; |
+import 'package:dev_compiler/src/utils.dart'; |
-abstract class AbstractCompiler { |
- CompilerOptions get options; |
- AnalysisContext get context; |
- TypeRules get rules; |
- Uri get entryPointUri; |
-} |
+import 'dependency_graph.dart'; |
-/// Encapsulates the logic to do a one-off compilation or a partial compilation |
-/// when the compiler is run as a development server. |
-class Compiler implements AbstractCompiler { |
- final CompilerOptions options; |
- final AnalysisContext context; |
- final AnalysisErrorListener _reporter; |
- final TypeRules rules; |
- final CodeChecker _checker; |
+/// Encapsulates the logic when the compiler is run as a development server. |
+class ServerCompiler extends AbstractCompiler { |
final SourceNode _entryNode; |
List<LibraryInfo> _libraries = <LibraryInfo>[]; |
final _generators = <CodeGenerator>[]; |
bool _hashing; |
bool _failure = false; |
- factory Compiler(CompilerOptions options, |
- {AnalysisContext context, AnalysisErrorListener reporter}) { |
- var strongOpts = options.strongOptions; |
- var sourceOpts = options.sourceOptions; |
- if (context == null) { |
- context = createAnalysisContextWithSources(strongOpts, sourceOpts); |
- } |
- |
- if (reporter == null) { |
- reporter = options.dumpInfo |
- ? new SummaryReporter(context, options.logLevel) |
- : new LogReporter(context, useColors: options.useColors); |
- } |
- var graph = new SourceGraph(context, reporter, options); |
- var rules = new RestrictedRules(context.typeProvider, |
- options: options.strongOptions); |
- var checker = new CodeChecker(rules, reporter, strongOpts); |
- |
- var inputFile = sourceOpts.entryPointFile; |
+ factory ServerCompiler(AnalysisContext context, CompilerOptions options, |
+ {AnalysisErrorListener reporter}) { |
+ var srcOpts = options.sourceOptions; |
+ var inputFile = options.inputs[0]; |
var inputUri = inputFile.startsWith('dart:') || |
inputFile.startsWith('package:') |
? Uri.parse(inputFile) |
- : new Uri.file(path.absolute(sourceOpts.useImplicitHtml |
+ : new Uri.file(path.absolute(srcOpts.useImplicitHtml |
? SourceResolverOptions.implicitHtmlFile |
: inputFile)); |
+ var graph = new SourceGraph(context, reporter, options); |
var entryNode = graph.nodeFromUri(inputUri); |
- return new Compiler._( |
- options, context, reporter, rules, checker, entryNode); |
+ return new ServerCompiler._(options, context, reporter, entryNode); |
} |
- Compiler._(this.options, this.context, this._reporter, this.rules, |
- this._checker, this._entryNode) { |
+ ServerCompiler._(context, options, reporter, this._entryNode) |
+ : super(options, context, reporter) { |
if (outputDir != null) { |
_generators.add(new JSGenerator(this)); |
} |
@@ -104,7 +70,23 @@ class Compiler implements AbstractCompiler { |
} |
Uri get entryPointUri => _entryNode.uri; |
- String get outputDir => options.codegenOptions.outputDir; |
+ |
+ CheckerResults run() { |
+ var clock = new Stopwatch()..start(); |
+ |
+ // TODO(sigmund): we are missing a couple failures here. The |
+ // dependency_graph now detects broken imports or unsupported features |
+ // like more than one script tag (see .severe messages in |
+ // dependency_graph.dart). Such failures should be reported back |
+ // here so we can mark failure=true in the CheckerResults. |
+ rebuild(_entryNode, _buildSource); |
+ _dumpInfoIfRequested(); |
+ clock.stop(); |
+ var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2); |
+ _log.fine('Compiled ${_libraries.length} libraries in ${time} s\n'); |
+ return new CheckerResults( |
+ _libraries, rules, _failure || options.codegenOptions.forceCompile); |
+ } |
bool _buildSource(SourceNode node) { |
if (node is HtmlSourceNode) { |
@@ -117,18 +99,19 @@ class Compiler implements AbstractCompiler { |
assert(false); // should not get a build request on PartSourceNode |
} |
- // TODO(sigmund): don't always return true. Use summarization to better |
- // determine when rebuilding is needed. |
+ // TODO(sigmund): don't always return true. |
+ // Use summaries to determine when rebuilding is needed. |
return true; |
} |
void _buildHtmlFile(HtmlSourceNode node) { |
if (outputDir == null) return; |
- var output = generateEntryHtml(node, options); |
+ var output = generateEntryHtml(node, this); |
if (output == null) { |
_failure = true; |
return; |
} |
+ |
var filename = path.basename(node.uri.path); |
String outputFile = path.join(outputDir, filename); |
new File(outputFile).writeAsStringSync(output); |
@@ -149,11 +132,6 @@ class Compiler implements AbstractCompiler { |
if (_hashing) node.cachingHash = computeHashFromFile(filepath); |
} |
- bool _isEntry(DartSourceNode node) { |
- if (_entryNode is DartSourceNode) return _entryNode == node; |
- return (_entryNode as HtmlSourceNode).scripts.contains(node); |
- } |
- |
void _buildDartLibrary(DartSourceNode node) { |
var source = node.source; |
// TODO(sigmund): find out from analyzer team if there is a better way |
@@ -165,10 +143,9 @@ class Compiler implements AbstractCompiler { |
if (current != null) { |
assert(current.library == lib); |
} else { |
- node.info = current = new LibraryInfo(lib, _isEntry(node)); |
+ node.info = current = new LibraryInfo(lib); |
} |
_libraries.add(current); |
- rules.currentLibraryInfo = current; |
var resolvedParts = node.parts |
.map((p) => context.resolveCompilationUnit2(p.source, source)) |
@@ -177,9 +154,10 @@ class Compiler implements AbstractCompiler { |
bool failureInLib = false; |
for (var unit in libraryUnit.libraryThenParts) { |
var unitSource = unit.element.source; |
+ // TODO(sigmund): integrate analyzer errors with static-info (issue #6). |
failureInLib = logErrors(unitSource) || failureInLib; |
- _checker.visitCompilationUnit(unit); |
- if (_checker.failure) failureInLib = true; |
+ checker.visitCompilationUnit(unit); |
+ if (checker.failure) failureInLib = true; |
} |
if (failureInLib) { |
_failure = true; |
@@ -187,53 +165,11 @@ class Compiler implements AbstractCompiler { |
} |
for (var cg in _generators) { |
- var hash = cg.generateLibrary(libraryUnit, current); |
+ var hash = cg.generateLibrary(libraryUnit); |
if (_hashing) node.cachingHash = hash; |
} |
} |
- /// Log any errors encountered when resolving [source] and return whether any |
- /// errors were found. |
- bool logErrors(Source source) { |
- context.computeErrors(source); |
- List<AnalysisError> errors = context.getErrors(source).errors; |
- bool failure = false; |
- if (errors.isNotEmpty) { |
- for (var error in errors) { |
- // Always skip TODOs. |
- if (error.errorCode.type == ErrorType.TODO) continue; |
- |
- // Skip hints for now. In the future these could be turned on via flags. |
- if (error.errorCode.errorSeverity.ordinal < |
- ErrorSeverity.WARNING.ordinal) { |
- continue; |
- } |
- |
- // All analyzer warnings or errors are errors for DDC. |
- failure = true; |
- _reporter.onError(error); |
- } |
- } |
- return failure; |
- } |
- |
- CheckerResults run() { |
- var clock = new Stopwatch()..start(); |
- |
- // TODO(sigmund): we are missing a couple failures here. The |
- // dependency_graph now detects broken imports or unsupported features |
- // like more than one script tag (see .severe messages in |
- // dependency_graph.dart). Such failures should be reported back |
- // here so we can mark failure=true in the CheckerResutls. |
- rebuild(_entryNode, _buildSource); |
- _dumpInfoIfRequested(); |
- clock.stop(); |
- var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2); |
- _log.fine('Compiled ${_libraries.length} libraries in ${time} s\n'); |
- return new CheckerResults( |
- _libraries, rules, _failure || options.codegenOptions.forceCompile); |
- } |
- |
void _runAgain() { |
var clock = new Stopwatch()..start(); |
_libraries = <LibraryInfo>[]; |
@@ -251,8 +187,8 @@ class Compiler implements AbstractCompiler { |
} |
_dumpInfoIfRequested() { |
- if (!options.dumpInfo || _reporter is! SummaryReporter) return; |
- var result = (_reporter as SummaryReporter).result; |
+ if (!options.dumpInfo || reporter is! SummaryReporter) return; |
+ var result = (reporter as SummaryReporter).result; |
if (!options.serverMode) print(summaryToString(result)); |
var filepath = options.serverMode |
? path.join(outputDir, 'messages.json') |
@@ -262,15 +198,26 @@ class Compiler implements AbstractCompiler { |
} |
} |
-class CompilerServer { |
- final Compiler compiler; |
+class DevServer { |
+ final ServerCompiler compiler; |
final String outDir; |
final String host; |
final int port; |
final String _entryPath; |
- factory CompilerServer(CompilerOptions options) { |
- var entryPath = path.basename(options.sourceOptions.entryPointFile); |
+ factory DevServer(CompilerOptions options) { |
+ assert(options.inputs.length == 1); |
+ |
+ var fileResolvers = createFileResolvers(options.sourceOptions); |
+ if (options.sourceOptions.useImplicitHtml) { |
+ fileResolvers.insert(0, _createImplicitEntryResolver(options.inputs[0])); |
+ } |
+ |
+ var context = createAnalysisContextWithSources( |
+ options.strongOptions, options.sourceOptions, |
+ fileResolvers: fileResolvers); |
+ |
+ var entryPath = path.basename(options.inputs[0]); |
var extension = path.extension(entryPath); |
if (extension != '.html' && !options.sourceOptions.useImplicitHtml) { |
print('error: devc in server mode requires an HTML or Dart entry point.'); |
@@ -286,12 +233,12 @@ class CompilerServer { |
} |
var port = options.port; |
var host = options.host; |
- var compiler = new Compiler(options); |
- return new CompilerServer._(compiler, outDir, host, port, entryPath); |
+ var compiler = new ServerCompiler(context, options); |
+ return new DevServer._(compiler, outDir, host, port, entryPath); |
} |
- CompilerServer._( |
- Compiler compiler, this.outDir, this.host, this.port, String entryPath) |
+ DevServer._(ServerCompiler compiler, this.outDir, this.host, this.port, |
+ String entryPath) |
: this.compiler = compiler, |
// TODO(jmesserly): this logic is duplicated in a few places |
this._entryPath = compiler.options.sourceOptions.useImplicitHtml |
@@ -335,5 +282,28 @@ class CompilerServer { |
}; |
} |
-final _log = new Logger('dev_compiler'); |
+UriResolver _createImplicitEntryResolver(String entryPath) { |
+ var entry = path.absolute(SourceResolverOptions.implicitHtmlFile); |
+ var src = path.absolute(entryPath); |
+ var provider = new MemoryResourceProvider(); |
+ provider.newFile( |
+ entry, '<body><script type="application/dart" src="$src"></script>'); |
+ return new _ExistingSourceUriResolver(new ResourceUriResolver(provider)); |
+} |
+ |
+/// A UriResolver that continues to the next one if it fails to find an existing |
+/// source file. This is unlike normal URI resolvers, that always return |
+/// something, even if it is a non-existing file. |
+class _ExistingSourceUriResolver implements UriResolver { |
+ final UriResolver resolver; |
+ _ExistingSourceUriResolver(this.resolver); |
+ |
+ Source resolveAbsolute(Uri uri) { |
+ var src = resolver.resolveAbsolute(uri); |
+ return src.exists() ? src : null; |
+ } |
+ Uri restoreAbsolute(Source source) => resolver.restoreAbsolute(source); |
+} |
+ |
+final _log = new Logger('dev_compiler.src.server'); |
final _earlyErrorResult = new CheckerResults(const [], null, true); |