| 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);
|
|
|