| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 /** Transfomer that inlines polymer-element definitions from html imports. */ | 5 /** Transfomer that inlines polymer-element definitions from html imports. */ |
| 6 library polymer.src.build.import_inliner; | 6 library polymer.src.build.import_inliner; |
| 7 | 7 |
| 8 import 'dart:async'; | 8 import 'dart:async'; |
| 9 | 9 |
| 10 import 'package:barback/barback.dart'; | 10 import 'package:barback/barback.dart'; |
| 11 import 'package:path/path.dart' as path; | 11 import 'package:path/path.dart' as path; |
| 12 import 'package:html5lib/dom.dart' show Document, Node, DocumentFragment; | 12 import 'package:html5lib/dom.dart' show Document, Node, DocumentFragment; |
| 13 import 'package:html5lib/dom_parsing.dart' show TreeVisitor; | 13 import 'package:html5lib/dom_parsing.dart' show TreeVisitor; |
| 14 import 'common.dart'; | 14 import 'common.dart'; |
| 15 | 15 |
| 16 /** Recursively inlines polymer-element definitions from html imports. */ | 16 /** Recursively inlines the contents of HTML imports. */ |
| 17 // TODO(sigmund): make sure we match semantics of html-imports for tags other | 17 // TODO(sigmund): currently we just inline polymer-element and script tags, we |
| 18 // than polymer-element (see dartbug.com/12613). | 18 // need to make sure we match semantics of html-imports for other tags too. |
| 19 class ImportedElementInliner extends Transformer with PolymerTransformer { | 19 // (see dartbug.com/12613). |
| 20 class ImportInliner extends Transformer with PolymerTransformer { |
| 20 final TransformOptions options; | 21 final TransformOptions options; |
| 21 | 22 |
| 22 ImportedElementInliner(this.options); | 23 ImportInliner(this.options); |
| 23 | 24 |
| 24 /** Only run on entry point .html files. */ | 25 /** Only run on entry point .html files. */ |
| 25 Future<bool> isPrimary(Asset input) => | 26 Future<bool> isPrimary(Asset input) => |
| 26 new Future.value(options.isHtmlEntryPoint(input.id)); | 27 new Future.value(options.isHtmlEntryPoint(input.id)); |
| 27 | 28 |
| 28 Future apply(Transform transform) { | 29 Future apply(Transform transform) { |
| 29 var seen = new Set<AssetId>(); | 30 var seen = new Set<AssetId>(); |
| 30 var elements = []; | 31 var elements = []; |
| 31 var id = transform.primaryInput.id; | 32 var id = transform.primaryInput.id; |
| 32 seen.add(id); | 33 seen.add(id); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 46 var fragment = new DocumentFragment()..nodes.addAll(elements); | 47 var fragment = new DocumentFragment()..nodes.addAll(elements); |
| 47 document.body.insertBefore(fragment, | 48 document.body.insertBefore(fragment, |
| 48 //TODO(jmesserly): add Node.firstChild to html5lib | 49 //TODO(jmesserly): add Node.firstChild to html5lib |
| 49 document.body.nodes.length == 0 ? null : document.body.nodes[0]); | 50 document.body.nodes.length == 0 ? null : document.body.nodes[0]); |
| 50 transform.addOutput(new Asset.fromString(id, document.outerHtml)); | 51 transform.addOutput(new Asset.fromString(id, document.outerHtml)); |
| 51 }); | 52 }); |
| 52 }); | 53 }); |
| 53 } | 54 } |
| 54 | 55 |
| 55 /** | 56 /** |
| 56 * Visits imports in [document] and add their polymer-element definitions to | 57 * Visits imports in [document] and add their polymer-element and script tags |
| 57 * [elements], unless they have already been [seen]. Elements are added in the | 58 * to [elements], unless they have already been [seen]. Elements are added in |
| 58 * order they appear, transitive imports are added first. | 59 * the order they appear, transitive imports are added first. |
| 59 */ | 60 */ |
| 60 Future<bool> _visitImports(Document document, AssetId sourceId, | 61 Future<bool> _visitImports(Document document, AssetId sourceId, |
| 61 Transform transform, Set<AssetId> seen, List<Node> elements) { | 62 Transform transform, Set<AssetId> seen, List<Node> elements) { |
| 62 var importIds = []; | 63 var importIds = []; |
| 63 bool hasImports = false; | 64 bool hasImports = false; |
| 64 for (var tag in document.queryAll('link')) { | 65 for (var tag in document.queryAll('link')) { |
| 65 if (tag.attributes['rel'] != 'import') continue; | 66 if (tag.attributes['rel'] != 'import') continue; |
| 66 var href = tag.attributes['href']; | 67 var href = tag.attributes['href']; |
| 67 var id = resolve(sourceId, href, transform.logger, tag.sourceSpan); | 68 var id = resolve(sourceId, href, transform.logger, tag.sourceSpan); |
| 68 hasImports = true; | 69 hasImports = true; |
| 69 if (id == null || seen.contains(id)) continue; | 70 if (id == null || seen.contains(id) || |
| 71 (id.package == 'polymer' && id.path == 'lib/init.html')) continue; |
| 70 importIds.add(id); | 72 importIds.add(id); |
| 71 } | 73 } |
| 72 | 74 |
| 73 if (importIds.isEmpty) return new Future.value(hasImports); | 75 if (importIds.isEmpty) return new Future.value(hasImports); |
| 74 | 76 |
| 75 // Note: we need to preserve the import order in the generated output. | 77 // Note: we need to preserve the import order in the generated output. |
| 76 return Future.forEach(importIds, (id) { | 78 return Future.forEach(importIds, (id) { |
| 77 if (seen.contains(id)) return new Future.value(null); | 79 if (seen.contains(id)) return new Future.value(null); |
| 78 seen.add(id); | 80 seen.add(id); |
| 79 return _collectPolymerElements(id, transform, seen, elements); | 81 return _collectElements(id, transform, seen, elements); |
| 80 }).then((_) => true); | 82 }).then((_) => true); |
| 81 } | 83 } |
| 82 | 84 |
| 83 /** | 85 /** |
| 84 * Loads an asset identified by [id], visits its imports and collects it's | 86 * Loads an asset identified by [id], visits its imports and collects it's |
| 85 * polymer-element definitions. | 87 * polymer-element definitions and script tags. |
| 86 */ | 88 */ |
| 87 Future _collectPolymerElements(AssetId id, Transform transform, | 89 Future _collectElements(AssetId id, Transform transform, |
| 88 Set<AssetId> seen, List elements) { | 90 Set<AssetId> seen, List elements) { |
| 89 return readAsHtml(id, transform).then((document) { | 91 return readAsHtml(id, transform).then((document) { |
| 90 return _visitImports(document, id, transform, seen, elements).then((_) { | 92 return _visitImports(document, id, transform, seen, elements).then((_) { |
| 91 var normalizer = new _UrlNormalizer(transform, id); | 93 new _UrlNormalizer(transform, id).visit(document); |
| 92 for (var element in document.queryAll('polymer-element')) { | 94 new _InlineQuery(elements).visit(document); |
| 93 normalizer.visit(document); | |
| 94 elements.add(element); | |
| 95 } | |
| 96 }); | 95 }); |
| 97 }); | 96 }); |
| 98 } | 97 } |
| 99 } | 98 } |
| 100 | 99 |
| 100 /** Implements document.queryAll('polymer-element,script'). */ |
| 101 // TODO(sigmund): delete this (dartbug.com/14135) |
| 102 class _InlineQuery extends TreeVisitor { |
| 103 final List<Element> elements; |
| 104 _InlineQuery(this.elements); |
| 105 |
| 106 visitElement(Element node) { |
| 107 if (node.tagName == 'polymer-element' || node.tagName == 'script') { |
| 108 elements.add(node); |
| 109 } else { |
| 110 super.visitElement(node); |
| 111 } |
| 112 } |
| 113 } |
| 114 |
| 101 /** Internally adjusts urls in the html that we are about to inline. */ | 115 /** Internally adjusts urls in the html that we are about to inline. */ |
| 102 class _UrlNormalizer extends TreeVisitor { | 116 class _UrlNormalizer extends TreeVisitor { |
| 103 final Transform transform; | 117 final Transform transform; |
| 104 | 118 |
| 105 /** Asset where the original content (and original url) was found. */ | 119 /** Asset where the original content (and original url) was found. */ |
| 106 final AssetId sourceId; | 120 final AssetId sourceId; |
| 107 | 121 |
| 108 _UrlNormalizer(this.transform, this.sourceId); | 122 _UrlNormalizer(this.transform, this.sourceId); |
| 109 | 123 |
| 110 visitElement(Element node) { | 124 visitElement(Element node) { |
| 111 for (var key in node.attributes.keys) { | 125 for (var key in node.attributes.keys) { |
| 112 if (_urlAttributes.contains(key)) { | 126 if (_urlAttributes.contains(key)) { |
| 113 var url = node.attributes[key]; | 127 var url = node.attributes[key]; |
| 114 if (url != null && url != '') { | 128 if (url != null && url != '' && !url.startsWith('{{')) { |
| 115 node.attributes[key] = _newUrl(url, node.sourceSpan); | 129 node.attributes[key] = _newUrl(url, node.sourceSpan); |
| 116 } | 130 } |
| 117 } | 131 } |
| 118 } | 132 } |
| 119 super.visitElement(node); | 133 super.visitElement(node); |
| 120 } | 134 } |
| 121 | 135 |
| 122 _newUrl(String href, Span span) { | 136 _newUrl(String href, Span span) { |
| 123 var uri = Uri.parse(href); | 137 var uri = Uri.parse(href); |
| 124 if (uri.isAbsolute) return href; | 138 if (uri.isAbsolute) return href; |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 163 'cite', // in blockquote, del, ins, q | 177 'cite', // in blockquote, del, ins, q |
| 164 'data', // in object | 178 'data', // in object |
| 165 'formaction', // in button, input | 179 'formaction', // in button, input |
| 166 'href', // in a, area, link, base, command | 180 'href', // in a, area, link, base, command |
| 167 'icon', // in command | 181 'icon', // in command |
| 168 'manifest', // in html | 182 'manifest', // in html |
| 169 'poster', // in video | 183 'poster', // in video |
| 170 'src', // in audio, embed, iframe, img, input, script, source, track, | 184 'src', // in audio, embed, iframe, img, input, script, source, track, |
| 171 // video | 185 // video |
| 172 ]; | 186 ]; |
| OLD | NEW |