| // Copyright (c) 2013, 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 web_components.build.import_crawler; |
| |
| import 'dart:async'; |
| import 'dart:collection' show LinkedHashMap; |
| import 'package:code_transformers/assets.dart'; |
| import 'package:code_transformers/messages/build_logger.dart'; |
| import 'package:barback/barback.dart'; |
| import 'package:html5lib/dom.dart' show Document, Element; |
| import 'common.dart'; |
| import 'messages.dart'; |
| |
| /// Information about an html import found in a document. |
| class ImportData { |
| /// The [Document] where the html import appeared. |
| final Document document; |
| |
| /// The html import element itself. |
| final Element element; |
| |
| ImportData(this.document, this.element); |
| } |
| |
| /// A crawler for html imports. |
| class ImportCrawler { |
| // Can be either an AggregateTransform or Transform. |
| final _transform; |
| final BuildLogger _logger; |
| final AssetId _primaryInputId; |
| |
| // Optional parsed document for the primary id if available. |
| final Document _primaryDocument; |
| |
| ImportCrawler(this._transform, this._primaryInputId, this._logger, |
| {Document primaryDocument}) |
| : _primaryDocument = primaryDocument; |
| |
| /// Returns a post-ordered map of [AssetId]'s to [ImportData]. The [AssetId]'s |
| /// represent an asset which was discovered via an html import, and the |
| /// [ImportData] represents the [Document] where it was discovered and the |
| /// html import [Element] itself. |
| Future<LinkedHashMap<AssetId, ImportData>> crawlImports() { |
| var documents = new LinkedHashMap<AssetId, ImportData>(); |
| var seen = new Set<AssetId>(); |
| |
| Future doCrawl(AssetId assetId, [Element import, Document document]) { |
| if (seen.contains(assetId)) return null; |
| seen.add(assetId); |
| |
| Future crawlImports(Document document) { |
| var imports = document.querySelectorAll('link[rel="import"]'); |
| var done = |
| Future.forEach(imports, (i) => doCrawl(_importId(assetId, i), i)); |
| |
| // Add this document after its dependencies. |
| return done.then((_) { |
| documents[assetId] = new ImportData(document, import); |
| }); |
| } |
| |
| if (document != null) { |
| return crawlImports(document); |
| } else { |
| return _transform.readInputAsString(assetId).then((html) { |
| return crawlImports(parseHtml(html, assetId.path)); |
| }).catchError((error) { |
| var span; |
| if (import != null) span = import.sourceSpan; |
| _logger.error(inlineImportFail.create({'error': error}), span: span); |
| }); |
| } |
| } |
| |
| return |
| doCrawl(_primaryInputId, null, _primaryDocument).then((_) => documents); |
| } |
| |
| AssetId _importId(AssetId source, Element import) { |
| var url = import.attributes['href']; |
| return uriToAssetId(source, url, _transform.logger, import.sourceSpan); |
| } |
| } |