| Index: lib/src/compiler.dart
|
| diff --git a/lib/src/compiler.dart b/lib/src/compiler.dart
|
| index c12ffce6078049ec100e3d7c075624088d24817f..c526b27e6724a2e6621fb179b8027f9be5cf8133 100644
|
| --- a/lib/src/compiler.dart
|
| +++ b/lib/src/compiler.dart
|
| @@ -6,6 +6,7 @@ library compiler;
|
|
|
| import 'dart:async';
|
| import 'dart:collection' show SplayTreeMap;
|
| +import 'dart:uri';
|
| import 'package:html5lib/dom.dart';
|
| import 'package:html5lib/parser.dart';
|
|
|
| @@ -20,6 +21,7 @@ import 'files.dart';
|
| import 'html_cleaner.dart';
|
| import 'info.dart';
|
| import 'messages.dart';
|
| +import 'observable_transform.dart' show transformObservables;
|
| import 'options.dart';
|
| import 'utils.dart';
|
|
|
| @@ -53,6 +55,9 @@ class Compiler {
|
| PathInfo _pathInfo;
|
| Messages _messages;
|
|
|
| + FutureGroup _tasks;
|
| + Set _processed;
|
| +
|
| /** Information about source [files] given their href. */
|
| final Map<Path, FileInfo> info = new SplayTreeMap<Path, FileInfo>();
|
|
|
| @@ -99,6 +104,7 @@ class Compiler {
|
| }
|
| return _parseAndDiscover(_mainPath).then((_) {
|
| _analyze();
|
| + _transformDart();
|
| _emit();
|
| });
|
| }
|
| @@ -109,49 +115,52 @@ class Compiler {
|
| * processed.
|
| */
|
| Future _parseAndDiscover(Path inputFile) {
|
| - var tasks = new FutureGroup();
|
| - bool isEntry = !options.componentsOnly;
|
| -
|
| - var processed = new Set();
|
| - processHtmlFile(SourceFile file) {
|
| - if (file == null) return;
|
| - if (!_pathInfo.checkInputPath(file.path, _messages)) return;
|
| -
|
| - files.add(file);
|
| -
|
| - var fileInfo = _time('Analyzed definitions', file.path,
|
| - () => analyzeDefinitions(file, _messages, isEntryPoint: isEntry));
|
| - isEntry = false;
|
| - info[file.path] = fileInfo;
|
| -
|
| - // Load component files referenced by [file].
|
| - for (var href in fileInfo.componentLinks) {
|
| - if (!processed.contains(href)) {
|
| - processed.add(href);
|
| - tasks.add(_parseHtmlFile(href).then(processHtmlFile));
|
| - }
|
| - }
|
| + _tasks = new FutureGroup();
|
| + _processed = new Set();
|
| + _processed.add(inputFile);
|
| + _tasks.add(_parseHtmlFile(inputFile).then(_processHtmlFile));
|
| + return _tasks.future;
|
| + }
|
|
|
| - // Load .dart files being referenced in the page.
|
| - var src = fileInfo.externalFile;
|
| - if (src != null && !processed.contains(src)) {
|
| - processed.add(src);
|
| - tasks.add(_parseDartFile(src).then(_addDartFile));
|
| - }
|
| + bool _shouldProcessFile(SourceFile file) =>
|
| + file != null && _pathInfo.checkInputPath(file.path, _messages);
|
|
|
| - // Load .dart files being referenced in components.
|
| - for (var component in fileInfo.declaredComponents) {
|
| - var src = component.externalFile;
|
| - if (src != null && !processed.contains(src)) {
|
| - processed.add(src);
|
| - tasks.add(_parseDartFile(src).then(_addDartFile));
|
| - }
|
| + void _processHtmlFile(SourceFile file) {
|
| + if (!_shouldProcessFile(file)) return;
|
| +
|
| + bool isEntryPoint = _processed.length == 1;
|
| +
|
| + files.add(file);
|
| +
|
| + var fileInfo = _time('Analyzed definitions', file.path,
|
| + () => analyzeDefinitions(file, _messages, isEntryPoint: isEntryPoint));
|
| + info[file.path] = fileInfo;
|
| +
|
| + _processImports(fileInfo);
|
| +
|
| + // Load component files referenced by [file].
|
| + for (var href in fileInfo.componentLinks) {
|
| + if (!_processed.contains(href)) {
|
| + _processed.add(href);
|
| + _tasks.add(_parseHtmlFile(href).then(_processHtmlFile));
|
| }
|
| }
|
|
|
| - processed.add(inputFile);
|
| - tasks.add(_parseHtmlFile(inputFile).then(processHtmlFile));
|
| - return tasks.future;
|
| + // Load .dart files being referenced in the page.
|
| + var src = fileInfo.externalFile;
|
| + if (src != null && !_processed.contains(src)) {
|
| + _processed.add(src);
|
| + _tasks.add(_parseDartFile(src).then(_processDartFile));
|
| + }
|
| +
|
| + // Load .dart files being referenced in components.
|
| + for (var component in fileInfo.declaredComponents) {
|
| + var src = component.externalFile;
|
| + if (src != null && !_processed.contains(src)) {
|
| + _processed.add(src);
|
| + _tasks.add(_parseDartFile(src).then(_processDartFile));
|
| + }
|
| + }
|
| }
|
|
|
| /** Asynchronously parse [path] as an .html file. */
|
| @@ -179,20 +188,138 @@ class Compiler {
|
| return null;
|
| }
|
|
|
| - void _addDartFile(SourceFile dartFile) {
|
| - if (dartFile == null) return;
|
| - if (!_pathInfo.checkInputPath(dartFile.path, _messages)) return;
|
| + void _processDartFile(SourceFile dartFile) {
|
| + if (!_shouldProcessFile(dartFile)) return;
|
|
|
| files.add(dartFile);
|
|
|
| var fileInfo = new FileInfo(dartFile.path);
|
| info[dartFile.path] = fileInfo;
|
| - fileInfo.inlinedCode = dartFile.code;
|
| - fileInfo.userCode = parseDartCode(fileInfo.inlinedCode,
|
| - fileInfo.path, messages:_messages);
|
| - if (fileInfo.userCode.partOf != null) {
|
| - _messages.error('expected a library, not a part.', null,
|
| - file: dartFile.path);
|
| + fileInfo.userCode = parseDartCode(dartFile.code,
|
| + fileInfo.path, messages: _messages);
|
| +
|
| + _processImports(fileInfo);
|
| + }
|
| +
|
| + void _processImports(FileInfo fileInfo) {
|
| + if (fileInfo.userCode == null) return;
|
| +
|
| + for (var directive in fileInfo.userCode.directives) {
|
| + var src = _getDirectivePath(fileInfo, directive.uri);
|
| + if (src == null) continue;
|
| + if (!_processed.contains(src)) {
|
| + _processed.add(src);
|
| + _tasks.add(_parseDartFile(src).then(_processDartFile));
|
| + }
|
| + }
|
| + }
|
| +
|
| + Path _getDirectivePath(LibraryInfo libInfo, String uri) {
|
| + if (uri.startsWith('dart:')) return null;
|
| +
|
| + if (uri.startsWith('package:')) {
|
| + // Don't process our own package -- we'll implement @observable manually.
|
| + if (uri.startsWith('package:web_ui/')) return null;
|
| +
|
| + return _mainPath.directoryPath.join(new Path('packages'))
|
| + .join(new Path(uri.substring(8)));
|
| + } else {
|
| + return libInfo.inputPath.directoryPath.join(new Path(uri));
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Transform Dart source code.
|
| + * Currently, the only transformation is [transformObservables].
|
| + * Calls _emitModifiedDartFiles to write the transformed files.
|
| + */
|
| + void _transformDart() {
|
| + var libraries = <LibraryInfo>[];
|
| + for (var sourceFile in files) {
|
| + var file = info[sourceFile.path];
|
| + libraries.add(file);
|
| + libraries.addAll(file.declaredComponents);
|
| + }
|
| + // Prefer to process the .dart file if it is external.
|
| + for (var i = 0; i < libraries.length; i++) {
|
| + var external = libraries[i].externalCode;
|
| + if (external != null) libraries[i] = external;
|
| + }
|
| + libraries = libraries.where((lib) => lib.userCode != null).toList();
|
| +
|
| + var transformed = [];
|
| + for (var library in libraries) {
|
| + // TODO(jmesserly): does it make sense for us to warn about Dart parse
|
| + // errors? It seems useful, but the VM or dart2js would still issue these
|
| + // messages anyway later.
|
| + if (transformObservables(library, messages: _messages)) {
|
| + transformed.add(library);
|
| + }
|
| + }
|
| +
|
| + _emitModifiedDartFiles(libraries, transformed);
|
| + }
|
| +
|
| + /**
|
| + * This method rewrites imports transitively for any modified dart files,
|
| + * and queues the [outputs] to be written. This will not write files that
|
| + * are handled by [WebComponentEmitter] and [MainPageEmitter].
|
| + */
|
| + void _emitModifiedDartFiles(List<LibraryInfo> libraries,
|
| + List<FileInfo> transformed) {
|
| +
|
| + if (transformed.length == 0) return;
|
| +
|
| + // Compute files that reference each file, then use this information to
|
| + // flip the modified bit transitively. This is a lot simpler than trying
|
| + // to compute it the other way because of circular references.
|
| + for (var library in libraries) {
|
| + for (var directive in library.userCode.directives) {
|
| + var importPath = _getDirectivePath(library, directive.uri);
|
| + if (importPath == null) continue;
|
| +
|
| + info[importPath].referencedBy.add(library);
|
| + }
|
| + }
|
| +
|
| + // Propegate the modified bit to anything that references a modified file.
|
| + void setModified(LibraryInfo library) {
|
| + if (library.modified) return;
|
| + library.modified = true;
|
| + library.referencedBy.forEach(setModified);
|
| + }
|
| + transformed.forEach(setModified);
|
| +
|
| + for (var library in libraries) {
|
| + // We don't need this anymore, so free it.
|
| + library.referencedBy = null;
|
| +
|
| + if (!library.modified) continue;
|
| +
|
| + var fileOutputPath = _pathInfo.outputLibraryPath(library);
|
| +
|
| + // Fix imports of modified files to use the generated path.
|
| + for (var directive in library.userCode.directives) {
|
| + var importPath = _getDirectivePath(library, directive.uri);
|
| + if (importPath == null) continue;
|
| + var importInfo = info[importPath];
|
| +
|
| + if (importInfo.modified && !directive.generated) {
|
| + // Use the generated URI for this file.
|
| + directive.generated = true;
|
| + directive.uri = _pathInfo.outputLibraryPath(importInfo)
|
| + .relativeTo(fileOutputPath.directoryPath).toString();
|
| + }
|
| + }
|
| +
|
| + // Components will get emitted by WebComponentEmitter, and the
|
| + // entry point will get emitted by MainPageEmitter.
|
| + // So we only need to worry about other .dart files.
|
| + if (library is FileInfo && library.htmlFile == null) {
|
| + // Add an output file for the transformed .dart code:
|
| + output.add(new OutputFile(fileOutputPath,
|
| + emitDartFile(library, _pathInfo), source: library.inputPath));
|
| + }
|
| }
|
| }
|
|
|
| @@ -318,5 +445,3 @@ class Compiler {
|
| '\n<!-- This file was auto-generated from ${file.path}. -->\n'));
|
| }
|
| }
|
| -
|
| -
|
|
|