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 import 'dart:convert'; | 9 import 'dart:convert'; |
10 | 10 |
11 import 'package:barback/barback.dart'; | 11 import 'package:barback/barback.dart'; |
12 import 'package:path/path.dart' as path; | 12 import 'package:path/path.dart' as path; |
13 import 'package:html5lib/dom.dart' show | 13 import 'package:html5lib/dom.dart' show |
14 Document, DocumentFragment, Element, Node; | 14 Document, DocumentFragment, Element, Node; |
15 import 'package:html5lib/dom_parsing.dart' show TreeVisitor; | 15 import 'package:html5lib/dom_parsing.dart' show TreeVisitor; |
16 import 'package:source_maps/span.dart' show Span; | 16 import 'package:source_maps/span.dart'; |
17 | 17 |
18 import 'code_extractor.dart'; // import just for documentation. | 18 import 'code_extractor.dart'; // import just for documentation. |
19 import 'common.dart'; | 19 import 'common.dart'; |
20 | 20 |
21 class _HtmlInliner extends PolymerTransformer { | 21 class _HtmlInliner extends PolymerTransformer { |
22 final TransformOptions options; | 22 final TransformOptions options; |
23 final Transform transform; | 23 final Transform transform; |
24 final TransformLogger logger; | 24 final TransformLogger logger; |
25 final AssetId docId; | 25 final AssetId docId; |
26 final seen = new Set<AssetId>(); | 26 final seen = new Set<AssetId>(); |
(...skipping 21 matching lines...) Expand all Loading... |
48 } | 48 } |
49 | 49 |
50 // We produce a secondary asset with extra information for later phases. | 50 // We produce a secondary asset with extra information for later phases. |
51 transform.addOutput(new Asset.fromString( | 51 transform.addOutput(new Asset.fromString( |
52 docId.addExtension('.scriptUrls'), | 52 docId.addExtension('.scriptUrls'), |
53 JSON.encode(scriptIds, toEncodable: (id) => id.serialize()))); | 53 JSON.encode(scriptIds, toEncodable: (id) => id.serialize()))); |
54 })); | 54 })); |
55 } | 55 } |
56 | 56 |
57 /** | 57 /** |
58 * Visits imports in [document] and add the imported documents to [documents]. | 58 * Visits imports in [document] and add the imported documents to documents. |
59 * Documents are added in the order they appear, transitive imports are added | 59 * Documents are added in the order they appear, transitive imports are added |
60 * first. | 60 * first. |
| 61 * |
| 62 * Returns `true` if and only if the document was changed and should be |
| 63 * written out. |
61 */ | 64 */ |
62 Future<bool> _visitImports(Document document, AssetId sourceId) { | 65 Future<bool> _visitImports(Document document, AssetId sourceId) { |
63 bool hasImports = false; | 66 bool changed = false; |
64 | 67 |
65 // Note: we need to preserve the import order in the generated output. | 68 // Note: we need to preserve the import order in the generated output. |
66 return Future.forEach(document.querySelectorAll('link'), (Element tag) { | 69 return Future.forEach(document.querySelectorAll('link'), (Element tag) { |
67 if (tag.attributes['rel'] != 'import') return null; | 70 var rel = tag.attributes['rel']; |
| 71 if (rel != 'import' && rel != 'stylesheet') return null; |
| 72 |
68 var href = tag.attributes['href']; | 73 var href = tag.attributes['href']; |
69 var id = resolve(sourceId, href, transform.logger, tag.sourceSpan); | 74 var id = resolve(sourceId, href, transform.logger, tag.sourceSpan, |
70 hasImports = true; | 75 allowAbsolute: rel == 'stylesheet'); |
71 | 76 |
72 tag.remove(); | 77 if (rel == 'import') { |
73 if (id == null || !seen.add(id) || | 78 changed = true; |
74 (id.package == 'polymer' && id.path == 'lib/init.html')) return null; | 79 tag.remove(); |
| 80 if (id == null || !seen.add(id) || |
| 81 (id.package == 'polymer' && id.path == 'lib/init.html')) { |
| 82 return null; |
| 83 } |
| 84 return _inlineImport(id); |
75 | 85 |
76 return _inlineImport(id); | 86 } else if (rel == 'stylesheet') { |
77 }).then((_) => hasImports); | 87 if (id == null) return null; |
| 88 changed = true; |
| 89 return _inlineStylesheet(id, tag); |
| 90 } |
| 91 }).then((_) => changed); |
78 } | 92 } |
79 | 93 |
80 // Loads an asset identified by [id], visits its imports and collects its | 94 // Loads an asset identified by [id], visits its imports and collects its |
81 // html imports. Then inlines it into the main document. | 95 // html imports. Then inlines it into the main document. |
82 Future _inlineImport(AssetId id) => | 96 Future _inlineImport(AssetId id) => |
83 readAsHtml(id, transform).then((doc) => _visitImports(doc, id).then((_) { | 97 readAsHtml(id, transform).then((doc) => _visitImports(doc, id).then((_) { |
84 | 98 |
85 new _UrlNormalizer(transform, id).visit(doc); | 99 new _UrlNormalizer(transform, id).visit(doc); |
86 _extractScripts(doc); | 100 _extractScripts(doc); |
87 | 101 |
88 // TODO(jmesserly): figure out how this is working in vulcanizer. | 102 // TODO(jmesserly): figure out how this is working in vulcanizer. |
89 // Do they produce a <body> tag with a <head> and <body> inside? | 103 // Do they produce a <body> tag with a <head> and <body> inside? |
90 imported.nodes | 104 imported.nodes..addAll(doc.head.nodes)..addAll(doc.body.nodes); |
91 ..addAll(doc.head.nodes) | |
92 ..addAll(doc.body.nodes); | |
93 })); | 105 })); |
94 | 106 |
| 107 Future _inlineStylesheet(AssetId id, Element link) { |
| 108 return transform.readInputAsString(id).then((css) { |
| 109 var url = getAssetUrl(id, transform); |
| 110 css = new _UrlNormalizer(transform, id).visitCss(css, url); |
| 111 link.replaceWith(new Element.tag('style')..text = css); |
| 112 }); |
| 113 } |
| 114 |
95 /** | 115 /** |
96 * Split Dart script tags from all the other elements. Now that Dartium | 116 * Split Dart script tags from all the other elements. Now that Dartium |
97 * only allows a single script tag per page, we can't inline script | 117 * only allows a single script tag per page, we can't inline script |
98 * tags. Instead, we collect the urls of each script tag so we import | 118 * tags. Instead, we collect the urls of each script tag so we import |
99 * them directly from the Dart bootstrap code. | 119 * them directly from the Dart bootstrap code. |
100 */ | 120 */ |
101 void _extractScripts(Document document) { | 121 void _extractScripts(Document document) { |
102 bool first = true; | 122 bool first = true; |
103 for (var script in document.querySelectorAll('script')) { | 123 for (var script in document.querySelectorAll('script')) { |
104 if (script.attributes['type'] == 'application/dart') { | 124 if (script.attributes['type'] == 'application/dart') { |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
165 if (_urlAttributes.contains(key)) { | 185 if (_urlAttributes.contains(key)) { |
166 var url = node.attributes[key]; | 186 var url = node.attributes[key]; |
167 if (url != null && url != '' && !url.startsWith('{{')) { | 187 if (url != null && url != '' && !url.startsWith('{{')) { |
168 node.attributes[key] = _newUrl(url, node.sourceSpan); | 188 node.attributes[key] = _newUrl(url, node.sourceSpan); |
169 } | 189 } |
170 } | 190 } |
171 } | 191 } |
172 super.visitElement(node); | 192 super.visitElement(node); |
173 } | 193 } |
174 | 194 |
| 195 static final _URL = new RegExp(r'url\(([^)]*)\)', multiLine: true); |
| 196 static final _QUOTE = new RegExp('["\']', multiLine: true); |
| 197 |
| 198 /** Visit the CSS text and replace any relative URLs so we can inline it. */ |
| 199 // Ported from: |
| 200 // https://github.com/Polymer/vulcanize/blob/c14f63696797cda18dc3d372b78aa3378
acc691f/lib/vulcan.js#L149 |
| 201 // TODO(jmesserly): use csslib here instead? Parsing with RegEx is sadness. |
| 202 // Maybe it's reliable enough for finding URLs in CSS? I'm not sure. |
| 203 String visitCss(String cssText, String url) { |
| 204 var src = new SourceFile.text(url, cssText); |
| 205 return cssText.replaceAllMapped(_URL, (match) { |
| 206 // Extract the URL, without any surrounding quotes. |
| 207 var span = src.span(match.start, match.end); |
| 208 var href = match[1].replaceAll(_QUOTE, ''); |
| 209 href = _newUrl(href, span); |
| 210 return 'url($href)'; |
| 211 }); |
| 212 } |
| 213 |
175 _newUrl(String href, Span span) { | 214 _newUrl(String href, Span span) { |
176 var uri = Uri.parse(href); | 215 var uri = Uri.parse(href); |
177 if (uri.isAbsolute) return href; | 216 if (uri.isAbsolute) return href; |
178 if (!uri.scheme.isEmpty) return href; | 217 if (!uri.scheme.isEmpty) return href; |
179 if (!uri.host.isEmpty) return href; | 218 if (!uri.host.isEmpty) return href; |
180 if (uri.path.isEmpty) return href; // Implies standalone ? or # in URI. | 219 if (uri.path.isEmpty) return href; // Implies standalone ? or # in URI. |
181 if (path.isAbsolute(href)) return href; | 220 if (path.isAbsolute(href)) return href; |
182 | 221 |
183 var id = resolve(sourceId, href, transform.logger, span); | 222 var id = resolve(sourceId, href, transform.logger, span); |
184 if (id == null) return href; | 223 if (id == null) return href; |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
218 'cite', // in blockquote, del, ins, q | 257 'cite', // in blockquote, del, ins, q |
219 'data', // in object | 258 'data', // in object |
220 'formaction', // in button, input | 259 'formaction', // in button, input |
221 'href', // in a, area, link, base, command | 260 'href', // in a, area, link, base, command |
222 'icon', // in command | 261 'icon', // in command |
223 'manifest', // in html | 262 'manifest', // in html |
224 'poster', // in video | 263 'poster', // in video |
225 'src', // in audio, embed, iframe, img, input, script, source, track, | 264 'src', // in audio, embed, iframe, img, input, script, source, track, |
226 // video | 265 // video |
227 ]; | 266 ]; |
OLD | NEW |