| Index: pkg/polymer/lib/src/compiler.dart
|
| diff --git a/pkg/polymer/lib/src/compiler.dart b/pkg/polymer/lib/src/compiler.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..5e2c98f4238019f05062a9c45941b43928d780fe
|
| --- /dev/null
|
| +++ b/pkg/polymer/lib/src/compiler.dart
|
| @@ -0,0 +1,770 @@
|
| +// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
| +// 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.
|
| +
|
| +library compiler;
|
| +
|
| +import 'dart:async';
|
| +import 'dart:collection' show SplayTreeMap;
|
| +import 'dart:json' as json;
|
| +
|
| +import 'package:analyzer_experimental/src/generated/ast.dart' show Directive, UriBasedDirective;
|
| +import 'package:csslib/visitor.dart' show StyleSheet, treeToDebugString;
|
| +import 'package:html5lib/dom.dart';
|
| +import 'package:html5lib/parser.dart';
|
| +import 'package:observe/transform.dart' show transformObservables;
|
| +import 'package:source_maps/span.dart' show Span;
|
| +import 'package:source_maps/refactor.dart' show TextEditTransaction;
|
| +import 'package:source_maps/printer.dart';
|
| +
|
| +import 'analyzer.dart';
|
| +import 'css_analyzer.dart' show analyzeCss, findUrlsImported,
|
| + findImportsInStyleSheet, parseCss;
|
| +import 'css_emitters.dart' show rewriteCssUris,
|
| + emitComponentStyleSheet, emitOriginalCss, emitStyleSheet;
|
| +import 'dart_parser.dart';
|
| +import 'emitters.dart';
|
| +import 'file_system.dart';
|
| +import 'files.dart';
|
| +import 'info.dart';
|
| +import 'messages.dart';
|
| +import 'compiler_options.dart';
|
| +import 'paths.dart';
|
| +import 'utils.dart';
|
| +
|
| +/**
|
| + * Parses an HTML file [contents] and returns a DOM-like tree.
|
| + * Note that [contents] will be a [String] if coming from a browser-based
|
| + * [FileSystem], or it will be a [List<int>] if running on the command line.
|
| + *
|
| + * Adds emitted error/warning to [messages], if [messages] is supplied.
|
| + */
|
| +Document parseHtml(contents, String sourcePath, Messages messages) {
|
| + var parser = new HtmlParser(contents, generateSpans: true,
|
| + sourceUrl: sourcePath);
|
| + var document = parser.parse();
|
| +
|
| + // Note: errors aren't fatal in HTML (unless strict mode is on).
|
| + // So just print them as warnings.
|
| + for (var e in parser.errors) {
|
| + messages.warning(e.message, e.span);
|
| + }
|
| + return document;
|
| +}
|
| +
|
| +/** Compiles an application written with Dart web components. */
|
| +class Compiler {
|
| + final FileSystem fileSystem;
|
| + final CompilerOptions options;
|
| + final List<SourceFile> files = <SourceFile>[];
|
| + final List<OutputFile> output = <OutputFile>[];
|
| +
|
| + String _mainPath;
|
| + String _resetCssFile;
|
| + StyleSheet _cssResetStyleSheet;
|
| + PathMapper _pathMapper;
|
| + Messages _messages;
|
| +
|
| + FutureGroup _tasks;
|
| + Set _processed;
|
| +
|
| + /** Information about source [files] given their href. */
|
| + final Map<String, FileInfo> info = new SplayTreeMap<String, FileInfo>();
|
| + final _edits = new Map<DartCodeInfo, TextEditTransaction>();
|
| +
|
| + final GlobalInfo global = new GlobalInfo();
|
| +
|
| + /** Creates a compiler with [options] using [fileSystem]. */
|
| + Compiler(this.fileSystem, this.options, this._messages) {
|
| + _mainPath = options.inputFile;
|
| + var mainDir = path.dirname(_mainPath);
|
| + var baseDir = options.baseDir != null ? options.baseDir : mainDir;
|
| + var outputDir = options.outputDir != null ? options.outputDir : mainDir;
|
| + var packageRoot = options.packageRoot != null ? options.packageRoot
|
| + : path.join(path.dirname(_mainPath), 'packages');
|
| +
|
| + if (options.resetCssFile != null) {
|
| + _resetCssFile = options.resetCssFile;
|
| + if (path.isRelative(_resetCssFile)) {
|
| + // If CSS reset file path is relative from our current path.
|
| + _resetCssFile = path.resolve(_resetCssFile);
|
| + }
|
| + }
|
| +
|
| + // Normalize paths - all should be relative or absolute paths.
|
| + if (path.isAbsolute(_mainPath) || path.isAbsolute(baseDir) ||
|
| + path.isAbsolute(outputDir) || path.isAbsolute(packageRoot)) {
|
| + if (path.isRelative(_mainPath)) _mainPath = path.resolve(_mainPath);
|
| + if (path.isRelative(baseDir)) baseDir = path.resolve(baseDir);
|
| + if (path.isRelative(outputDir)) outputDir = path.resolve(outputDir);
|
| + if (path.isRelative(packageRoot)) {
|
| + packageRoot = path.resolve(packageRoot);
|
| + }
|
| + }
|
| + _pathMapper = new PathMapper(
|
| + baseDir, outputDir, packageRoot, options.forceMangle,
|
| + options.rewriteUrls);
|
| + }
|
| +
|
| + /** Compile the application starting from the given input file. */
|
| + Future run() {
|
| + if (path.basename(_mainPath).endsWith('.dart')) {
|
| + _messages.error("Please provide an HTML file as your entry point.",
|
| + null);
|
| + return new Future.value(null);
|
| + }
|
| + return _parseAndDiscover(_mainPath).then((_) {
|
| + _analyze();
|
| +
|
| + // Analyze all CSS files.
|
| + _time('Analyzed Style Sheets', '', () =>
|
| + analyzeCss(_pathMapper.packageRoot, files, info,
|
| + global.pseudoElements, _messages,
|
| + warningsAsErrors: options.warningsAsErrors));
|
| +
|
| + // TODO(jmesserly): need to go through our errors, and figure out if some
|
| + // of them should be warnings instead.
|
| + if (_messages.hasErrors || options.analysisOnly) return;
|
| + _transformDart();
|
| + _emit();
|
| + });
|
| + }
|
| +
|
| + /**
|
| + * Asynchronously parse [inputFile] and transitively discover web components
|
| + * to load and parse. Returns a future that completes when all files are
|
| + * processed.
|
| + */
|
| + Future _parseAndDiscover(String inputFile) {
|
| + _tasks = new FutureGroup();
|
| + _processed = new Set();
|
| + _processed.add(inputFile);
|
| + _tasks.add(_parseHtmlFile(new UrlInfo(inputFile, inputFile, null)));
|
| + return _tasks.future;
|
| + }
|
| +
|
| + void _processHtmlFile(UrlInfo inputUrl, SourceFile file) {
|
| + if (file == null) return;
|
| +
|
| + bool isEntryPoint = _processed.length == 1;
|
| +
|
| + files.add(file);
|
| +
|
| + var fileInfo = _time('Analyzed definitions', inputUrl.url, () {
|
| + return analyzeDefinitions(global, inputUrl, file.document,
|
| + _pathMapper.packageRoot, _messages, isEntryPoint: isEntryPoint);
|
| + });
|
| + info[inputUrl.resolvedPath] = fileInfo;
|
| +
|
| + if (isEntryPoint && _resetCssFile != null) {
|
| + _processed.add(_resetCssFile);
|
| + _tasks.add(_parseCssFile(new UrlInfo(_resetCssFile, _resetCssFile,
|
| + null)));
|
| + }
|
| +
|
| + _setOutputFilenames(fileInfo);
|
| + _processImports(fileInfo);
|
| +
|
| + // Load component files referenced by [file].
|
| + for (var link in fileInfo.componentLinks) {
|
| + _loadFile(link, _parseHtmlFile);
|
| + }
|
| +
|
| + // Load stylesheet files referenced by [file].
|
| + for (var link in fileInfo.styleSheetHrefs) {
|
| + _loadFile(link, _parseCssFile);
|
| + }
|
| +
|
| + // Load .dart files being referenced in the page.
|
| + _loadFile(fileInfo.externalFile, _parseDartFile);
|
| +
|
| + // Process any @imports inside of a <style> tag.
|
| + var urlInfos = findUrlsImported(fileInfo, fileInfo.inputUrl,
|
| + _pathMapper.packageRoot, file.document, _messages, options);
|
| + for (var urlInfo in urlInfos) {
|
| + _loadFile(urlInfo, _parseCssFile);
|
| + }
|
| +
|
| + // Load .dart files being referenced in components.
|
| + for (var component in fileInfo.declaredComponents) {
|
| + if (component.externalFile != null) {
|
| + _loadFile(component.externalFile, _parseDartFile);
|
| + } else if (component.userCode != null) {
|
| + _processImports(component);
|
| + }
|
| +
|
| + // Process any @imports inside of the <style> tag in a component.
|
| + var urlInfos = findUrlsImported(component,
|
| + component.declaringFile.inputUrl, _pathMapper.packageRoot,
|
| + component.element, _messages, options);
|
| + for (var urlInfo in urlInfos) {
|
| + _loadFile(urlInfo, _parseCssFile);
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Helper function to load [urlInfo] and parse it using [loadAndParse] if it
|
| + * hasn't been loaded before.
|
| + */
|
| + void _loadFile(UrlInfo urlInfo, Future loadAndParse(UrlInfo inputUrl)) {
|
| + if (urlInfo == null) return;
|
| + var resolvedPath = urlInfo.resolvedPath;
|
| + if (!_processed.contains(resolvedPath)) {
|
| + _processed.add(resolvedPath);
|
| + _tasks.add(loadAndParse(urlInfo));
|
| + }
|
| + }
|
| +
|
| + void _setOutputFilenames(FileInfo fileInfo) {
|
| + var filePath = fileInfo.dartCodeUrl.resolvedPath;
|
| + fileInfo.outputFilename = _pathMapper.mangle(path.basename(filePath),
|
| + '.dart', path.extension(filePath) == '.html');
|
| + for (var component in fileInfo.declaredComponents) {
|
| + var externalFile = component.externalFile;
|
| + var name = null;
|
| + if (externalFile != null) {
|
| + name = _pathMapper.mangle(
|
| + path.basename(externalFile.resolvedPath), '.dart');
|
| + } else {
|
| + var declaringFile = component.declaringFile;
|
| + var prefix = path.basename(declaringFile.inputUrl.resolvedPath);
|
| + if (declaringFile.declaredComponents.length == 1
|
| + && !declaringFile.codeAttached && !declaringFile.isEntryPoint) {
|
| + name = _pathMapper.mangle(prefix, '.dart', true);
|
| + } else {
|
| + var componentName = component.tagName.replaceAll('-', '_');
|
| + name = _pathMapper.mangle('${prefix}_$componentName', '.dart', true);
|
| + }
|
| + }
|
| + component.outputFilename = name;
|
| + }
|
| + }
|
| +
|
| + /** Parse an HTML file. */
|
| + Future _parseHtmlFile(UrlInfo inputUrl) {
|
| + if (!_pathMapper.checkInputPath(inputUrl, _messages)) {
|
| + return new Future<SourceFile>.value(null);
|
| + }
|
| + var filePath = inputUrl.resolvedPath;
|
| + return fileSystem.readTextOrBytes(filePath)
|
| + .catchError((e) => _readError(e, inputUrl))
|
| + .then((source) {
|
| + if (source == null) return;
|
| + var file = new SourceFile(filePath);
|
| + file.document = _time('Parsed', filePath,
|
| + () => parseHtml(source, filePath, _messages));
|
| + _processHtmlFile(inputUrl, file);
|
| + });
|
| + }
|
| +
|
| + /** Parse a Dart file. */
|
| + Future _parseDartFile(UrlInfo inputUrl) {
|
| + if (!_pathMapper.checkInputPath(inputUrl, _messages)) {
|
| + return new Future<SourceFile>.value(null);
|
| + }
|
| + var filePath = inputUrl.resolvedPath;
|
| + return fileSystem.readText(filePath)
|
| + .catchError((e) => _readError(e, inputUrl))
|
| + .then((code) {
|
| + if (code == null) return;
|
| + var file = new SourceFile(filePath, type: SourceFile.DART);
|
| + file.code = code;
|
| + _processDartFile(inputUrl, file);
|
| + });
|
| + }
|
| +
|
| + /** Parse a stylesheet file. */
|
| + Future _parseCssFile(UrlInfo inputUrl) {
|
| + if (!options.emulateScopedCss ||
|
| + !_pathMapper.checkInputPath(inputUrl, _messages)) {
|
| + return new Future<SourceFile>.value(null);
|
| + }
|
| + var filePath = inputUrl.resolvedPath;
|
| + return fileSystem.readText(filePath)
|
| + .catchError((e) => _readError(e, inputUrl, isWarning: true))
|
| + .then((code) {
|
| + if (code == null) return;
|
| + var file = new SourceFile(filePath, type: SourceFile.STYLESHEET);
|
| + file.code = code;
|
| + _processCssFile(inputUrl, file);
|
| + });
|
| + }
|
| +
|
| +
|
| + SourceFile _readError(error, UrlInfo inputUrl, {isWarning: false}) {
|
| + var message = 'unable to open file "${inputUrl.resolvedPath}"';
|
| + if (options.verbose) {
|
| + message = '$message. original message:\n $error';
|
| + }
|
| + if (isWarning) {
|
| + _messages.warning(message, inputUrl.sourceSpan);
|
| + } else {
|
| + _messages.error(message, inputUrl.sourceSpan);
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + void _processDartFile(UrlInfo inputUrl, SourceFile dartFile) {
|
| + if (dartFile == null) return;
|
| +
|
| + files.add(dartFile);
|
| +
|
| + var resolvedPath = inputUrl.resolvedPath;
|
| + var fileInfo = new FileInfo(inputUrl);
|
| + info[resolvedPath] = fileInfo;
|
| + fileInfo.inlinedCode = parseDartCode(resolvedPath, dartFile.code);
|
| + fileInfo.outputFilename =
|
| + _pathMapper.mangle(path.basename(resolvedPath), '.dart', false);
|
| +
|
| + _processImports(fileInfo);
|
| + }
|
| +
|
| + void _processImports(LibraryInfo library) {
|
| + if (library.userCode == null) return;
|
| +
|
| + for (var directive in library.userCode.directives) {
|
| + _loadFile(_getDirectiveUrlInfo(library, directive), _parseDartFile);
|
| + }
|
| + }
|
| +
|
| + void _processCssFile(UrlInfo inputUrl, SourceFile cssFile) {
|
| + if (cssFile == null) return;
|
| +
|
| + files.add(cssFile);
|
| +
|
| + var fileInfo = new FileInfo(inputUrl);
|
| + info[inputUrl.resolvedPath] = fileInfo;
|
| +
|
| + var styleSheet = parseCss(cssFile.code, _messages, options);
|
| + if (inputUrl.url == _resetCssFile) {
|
| + _cssResetStyleSheet = styleSheet;
|
| + } else if (styleSheet != null) {
|
| + _resolveStyleSheetImports(inputUrl, cssFile.path, styleSheet);
|
| + fileInfo.styleSheets.add(styleSheet);
|
| + }
|
| + }
|
| +
|
| + /** Load and parse all style sheets referenced with an @imports. */
|
| + void _resolveStyleSheetImports(UrlInfo inputUrl, String processingFile,
|
| + StyleSheet styleSheet) {
|
| + var urlInfos = _time('CSS imports', processingFile, () =>
|
| + findImportsInStyleSheet(styleSheet, _pathMapper.packageRoot, inputUrl,
|
| + _messages));
|
| +
|
| + for (var urlInfo in urlInfos) {
|
| + if (urlInfo == null) break;
|
| + // Load any @imported stylesheet files referenced in this style sheet.
|
| + _loadFile(urlInfo, _parseCssFile);
|
| + }
|
| + }
|
| +
|
| + String _directiveUri(Directive directive) {
|
| + var uriDirective = (directive as UriBasedDirective).uri;
|
| + return (uriDirective as dynamic).value;
|
| + }
|
| +
|
| + UrlInfo _getDirectiveUrlInfo(LibraryInfo library, Directive directive) {
|
| + var uri = _directiveUri(directive);
|
| + if (uri.startsWith('dart:')) return null;
|
| + if (uri.startsWith('package:') && uri.startsWith('package:polymer/')) {
|
| + // Don't process our own package -- we'll implement @observable manually.
|
| + return null;
|
| + }
|
| +
|
| + var span = library.userCode.sourceFile.span(
|
| + directive.offset, directive.end);
|
| + return UrlInfo.resolve(uri, library.dartCodeUrl, span,
|
| + _pathMapper.packageRoot, _messages);
|
| + }
|
| +
|
| + /**
|
| + * Transform Dart source code.
|
| + * Currently, the only transformation is [transformObservables].
|
| + * Calls _emitModifiedDartFiles to write the transformed files.
|
| + */
|
| + void _transformDart() {
|
| + var libraries = _findAllDartLibraries();
|
| +
|
| + var transformed = [];
|
| + for (var lib in libraries) {
|
| + var userCode = lib.userCode;
|
| + var transaction = transformObservables(userCode.compilationUnit,
|
| + userCode.sourceFile, userCode.code, _messages);
|
| + if (transaction != null) {
|
| + _edits[lib.userCode] = transaction;
|
| + if (transaction.hasEdits) {
|
| + transformed.add(lib);
|
| + } else if (lib.htmlFile != null) {
|
| + // All web components will be transformed too. Track that.
|
| + transformed.add(lib);
|
| + }
|
| + }
|
| + }
|
| +
|
| + _findModifiedDartFiles(libraries, transformed);
|
| +
|
| + libraries.forEach(_fixImports);
|
| +
|
| + _emitModifiedDartFiles(libraries);
|
| + }
|
| +
|
| + /**
|
| + * Finds all Dart code libraries.
|
| + * Each library will have [LibraryInfo.inlinedCode] that is non-null.
|
| + * Also each inlinedCode will be unique.
|
| + */
|
| + List<LibraryInfo> _findAllDartLibraries() {
|
| + var libs = <LibraryInfo>[];
|
| + void _addLibrary(LibraryInfo lib) {
|
| + if (lib.inlinedCode != null) libs.add(lib);
|
| + }
|
| +
|
| + for (var sourceFile in files) {
|
| + var file = info[sourceFile.path];
|
| + _addLibrary(file);
|
| + file.declaredComponents.forEach(_addLibrary);
|
| + }
|
| +
|
| + // Assert that each file path is unique.
|
| + assert(_uniquePaths(libs));
|
| + return libs;
|
| + }
|
| +
|
| + bool _uniquePaths(List<LibraryInfo> libs) {
|
| + var seen = new Set();
|
| + for (var lib in libs) {
|
| + if (seen.contains(lib.inlinedCode)) {
|
| + throw new StateError('internal error: '
|
| + 'duplicate user code for ${lib.dartCodeUrl.resolvedPath}.'
|
| + ' Files were: $files');
|
| + }
|
| + seen.add(lib.inlinedCode);
|
| + }
|
| + return true;
|
| + }
|
| +
|
| + /**
|
| + * Queue modified Dart files to be written.
|
| + * This will not write files that are handled by [WebComponentEmitter] and
|
| + * [EntryPointEmitter].
|
| + */
|
| + void _emitModifiedDartFiles(List<LibraryInfo> libraries) {
|
| + for (var lib in libraries) {
|
| + // 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 (lib.modified && lib is FileInfo &&
|
| + lib.htmlFile == null && !lib.isEntryPoint) {
|
| + var transaction = _edits[lib.userCode];
|
| +
|
| + // Save imports that were modified by _fixImports.
|
| + for (var d in lib.userCode.directives) {
|
| + transaction.edit(d.offset, d.end, d.toString());
|
| + }
|
| +
|
| + if (!lib.userCode.isPart) {
|
| + var pos = lib.userCode.firstPartOffset;
|
| + // Note: we use a different prefix than "autogenerated" to make
|
| + // ChangeRecord unambiguous. Otherwise it would be imported by this
|
| + // and polymer, resulting in a collision.
|
| + // TODO(jmesserly): only generate this for libraries that need it.
|
| + transaction.edit(pos, pos, "\nimport "
|
| + "'package:observe/observe.dart' as __observe;\n");
|
| + }
|
| + _emitFileAndSourceMaps(lib, transaction.commit(), lib.dartCodeUrl);
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * This method computes which Dart files have been modified, starting
|
| + * from [transformed] and marking recursively through all files that import
|
| + * the modified files.
|
| + */
|
| + void _findModifiedDartFiles(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 lib in libraries) {
|
| + for (var directive in lib.userCode.directives) {
|
| + var importPath = _getDirectiveUrlInfo(lib, directive);
|
| + if (importPath == null) continue;
|
| +
|
| + var importInfo = info[importPath.resolvedPath];
|
| + if (importInfo != null) {
|
| + importInfo.referencedBy.add(lib);
|
| + }
|
| + }
|
| + }
|
| +
|
| + // 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 lib in libraries) {
|
| + // We don't need this anymore, so free it.
|
| + lib.referencedBy = null;
|
| + }
|
| + }
|
| +
|
| + void _fixImports(LibraryInfo library) {
|
| + // Fix imports. Modified files must use the generated path, otherwise
|
| + // we need to make the path relative to the input.
|
| + for (var directive in library.userCode.directives) {
|
| + var importPath = _getDirectiveUrlInfo(library, directive);
|
| + if (importPath == null) continue;
|
| + var importInfo = info[importPath.resolvedPath];
|
| + if (importInfo == null) continue;
|
| +
|
| + String newUri = null;
|
| + if (importInfo.modified) {
|
| + // Use the generated URI for this file.
|
| + newUri = _pathMapper.importUrlFor(library, importInfo);
|
| + } else if (options.rewriteUrls) {
|
| + // Get the relative path to the input file.
|
| + newUri = _pathMapper.transformUrl(
|
| + library.dartCodeUrl.resolvedPath, directive.uri.value);
|
| + }
|
| + if (newUri != null) {
|
| + directive.uri = createStringLiteral(newUri);
|
| + }
|
| + }
|
| + }
|
| +
|
| + /** Run the analyzer on every input html file. */
|
| + void _analyze() {
|
| + var uniqueIds = new IntIterator();
|
| + for (var file in files) {
|
| + if (file.isHtml) {
|
| + _time('Analyzed contents', file.path, () =>
|
| + analyzeFile(file, info, uniqueIds, global, _messages,
|
| + options.emulateScopedCss));
|
| + }
|
| + }
|
| + }
|
| +
|
| + /** Emit the generated code corresponding to each input file. */
|
| + void _emit() {
|
| + for (var file in files) {
|
| + if (file.isDart || file.isStyleSheet) continue;
|
| + _time('Codegen', file.path, () {
|
| + var fileInfo = info[file.path];
|
| + _emitComponents(fileInfo);
|
| + });
|
| + }
|
| +
|
| + var entryPoint = files[0];
|
| + assert(info[entryPoint.path].isEntryPoint);
|
| + _emitMainDart(entryPoint);
|
| + _emitMainHtml(entryPoint);
|
| +
|
| + assert(_unqiueOutputs());
|
| + }
|
| +
|
| + bool _unqiueOutputs() {
|
| + var seen = new Set();
|
| + for (var file in output) {
|
| + if (seen.contains(file.path)) {
|
| + throw new StateError('internal error: '
|
| + 'duplicate output file ${file.path}. Files were: $output');
|
| + }
|
| + seen.add(file.path);
|
| + }
|
| + return true;
|
| + }
|
| +
|
| + /** Emit the main .dart file. */
|
| + void _emitMainDart(SourceFile file) {
|
| + var fileInfo = info[file.path];
|
| +
|
| + var codeInfo = fileInfo.userCode;
|
| + if (codeInfo != null) {
|
| + var printer = new NestedPrinter(0);
|
| + if (codeInfo.libraryName == null) {
|
| + printer.addLine('library ${fileInfo.libraryName};');
|
| + }
|
| + printer.add(codeInfo.code);
|
| + _emitFileAndSourceMaps(fileInfo, printer, fileInfo.dartCodeUrl);
|
| + }
|
| + }
|
| +
|
| + // TODO(jmesserly): refactor this out of Compiler.
|
| + /** Generate an html file with the (trimmed down) main html page. */
|
| + void _emitMainHtml(SourceFile file) {
|
| + var fileInfo = info[file.path];
|
| +
|
| + var bootstrapName = '${path.basename(file.path)}_bootstrap.dart';
|
| + var bootstrapPath = path.join(path.dirname(file.path), bootstrapName);
|
| + var bootstrapOutPath = _pathMapper.outputPath(bootstrapPath, '');
|
| + var bootstrapOutName = path.basename(bootstrapOutPath);
|
| + var bootstrapInfo = new FileInfo(new UrlInfo('', bootstrapPath, null));
|
| + var printer = generateBootstrapCode(bootstrapInfo, fileInfo, global,
|
| + _pathMapper, options);
|
| + printer.build(bootstrapOutPath);
|
| + output.add(new OutputFile(
|
| + bootstrapOutPath, printer.text, source: file.path));
|
| +
|
| + var document = file.document;
|
| + var hasCss = _emitAllCss();
|
| + transformMainHtml(document, fileInfo, _pathMapper, hasCss,
|
| + options.rewriteUrls, _messages, global, bootstrapOutName);
|
| + output.add(new OutputFile(_pathMapper.outputPath(file.path, '.html'),
|
| + document.outerHtml, source: file.path));
|
| + }
|
| +
|
| + // TODO(jmesserly): refactor this and other CSS related transforms out of
|
| + // Compiler.
|
| + /**
|
| + * Generate an CSS file for all style sheets (main and components).
|
| + * Returns true if a file was generated, otherwise false.
|
| + */
|
| + bool _emitAllCss() {
|
| + if (!options.emulateScopedCss) return false;
|
| +
|
| + var buff = new StringBuffer();
|
| +
|
| + // Emit all linked style sheet files first.
|
| + for (var file in files) {
|
| + var css = new StringBuffer();
|
| + var fileInfo = info[file.path];
|
| + if (file.isStyleSheet) {
|
| + for (var styleSheet in fileInfo.styleSheets) {
|
| + // Translate any URIs in CSS.
|
| + rewriteCssUris(_pathMapper, fileInfo.inputUrl.resolvedPath,
|
| + options.rewriteUrls, styleSheet);
|
| + css.write(
|
| + '/* Auto-generated from style sheet href = ${file.path} */\n'
|
| + '/* DO NOT EDIT. */\n\n');
|
| + css.write(emitStyleSheet(styleSheet, fileInfo));
|
| + css.write('\n\n');
|
| + }
|
| +
|
| + // Emit the linked style sheet in the output directory.
|
| + if (fileInfo.inputUrl.url != _resetCssFile) {
|
| + var outCss = _pathMapper.outputPath(fileInfo.inputUrl.resolvedPath,
|
| + '');
|
| + output.add(new OutputFile(outCss, css.toString()));
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Emit all CSS for each component (style scoped).
|
| + for (var file in files) {
|
| + if (file.isHtml) {
|
| + var fileInfo = info[file.path];
|
| + for (var component in fileInfo.declaredComponents) {
|
| + for (var styleSheet in component.styleSheets) {
|
| + // Translate any URIs in CSS.
|
| + rewriteCssUris(_pathMapper, fileInfo.inputUrl.resolvedPath,
|
| + options.rewriteUrls, styleSheet);
|
| +
|
| + if (buff.isEmpty) {
|
| + buff.write(
|
| + '/* Auto-generated from components style tags. */\n'
|
| + '/* DO NOT EDIT. */\n\n');
|
| + }
|
| + buff.write(
|
| + '/* ==================================================== \n'
|
| + ' Component ${component.tagName} stylesheet \n'
|
| + ' ==================================================== */\n');
|
| +
|
| + var tagName = component.tagName;
|
| + if (!component.hasAuthorStyles) {
|
| + if (_cssResetStyleSheet != null) {
|
| + // If component doesn't have apply-author-styles then we need to
|
| + // reset the CSS the styles for the component (if css-reset file
|
| + // option was passed).
|
| + buff.write('\n/* Start CSS Reset */\n');
|
| + var style;
|
| + if (options.emulateScopedCss) {
|
| + style = emitComponentStyleSheet(_cssResetStyleSheet, tagName);
|
| + } else {
|
| + style = emitOriginalCss(_cssResetStyleSheet);
|
| + }
|
| + buff.write(style);
|
| + buff.write('/* End CSS Reset */\n\n');
|
| + }
|
| + }
|
| + if (options.emulateScopedCss) {
|
| + buff.write(emitComponentStyleSheet(styleSheet, tagName));
|
| + } else {
|
| + buff.write(emitOriginalCss(styleSheet));
|
| + }
|
| + buff.write('\n\n');
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + if (buff.isEmpty) return false;
|
| +
|
| + var cssPath = _pathMapper.outputPath(_mainPath, '.css', true);
|
| + output.add(new OutputFile(cssPath, buff.toString()));
|
| + return true;
|
| + }
|
| +
|
| + /** Emits the Dart code for all components in [fileInfo]. */
|
| + void _emitComponents(FileInfo fileInfo) {
|
| + for (var component in fileInfo.declaredComponents) {
|
| + // TODO(terry): Handle more than one stylesheet per component
|
| + if (component.styleSheets.length > 1 && options.emulateScopedCss) {
|
| + var span = component.externalFile != null
|
| + ? component.externalFile.sourceSpan : null;
|
| + _messages.warning(
|
| + 'Component has more than one stylesheet - first stylesheet used.',
|
| + span);
|
| + }
|
| + var printer = emitPolymerElement(
|
| + component, _pathMapper, _edits[component.userCode], options);
|
| + _emitFileAndSourceMaps(component, printer, component.externalFile);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Emits a file that was created using [NestedPrinter] and it's corresponding
|
| + * source map file.
|
| + */
|
| + void _emitFileAndSourceMaps(
|
| + LibraryInfo lib, NestedPrinter printer, UrlInfo dartCodeUrl) {
|
| + // Bail if we had an error generating the code for the file.
|
| + if (printer == null) return;
|
| +
|
| + var libPath = _pathMapper.outputLibraryPath(lib);
|
| + var dir = path.dirname(libPath);
|
| + var filename = path.basename(libPath);
|
| + printer.add('\n//# sourceMappingURL=$filename.map');
|
| + printer.build(libPath);
|
| + var sourcePath = dartCodeUrl != null ? dartCodeUrl.resolvedPath : null;
|
| + output.add(new OutputFile(libPath, printer.text, source: sourcePath));
|
| + // Fix-up the paths in the source map file
|
| + var sourceMap = json.parse(printer.map);
|
| + var urls = sourceMap['sources'];
|
| + for (int i = 0; i < urls.length; i++) {
|
| + urls[i] = path.relative(urls[i], from: dir);
|
| + }
|
| + output.add(new OutputFile(path.join(dir, '$filename.map'),
|
| + json.stringify(sourceMap)));
|
| + }
|
| +
|
| + _time(String logMessage, String filePath, callback(),
|
| + {bool printTime: false}) {
|
| + var message = new StringBuffer();
|
| + message.write(logMessage);
|
| + var filename = path.basename(filePath);
|
| + for (int i = (60 - logMessage.length - filename.length); i > 0 ; i--) {
|
| + message.write(' ');
|
| + }
|
| + message.write(filename);
|
| + return time(message.toString(), callback,
|
| + printTime: options.verbose || printTime);
|
| + }
|
| +}
|
|
|