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...) 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')) return null ; | |
Siggi Cherem (dart-lang)
2014/02/13 00:08:18
80 :-(
Jennifer Messerly
2014/02/13 06:12:52
oops, missed this. fixed.
out of curiosity, why t
Siggi Cherem (dart-lang)
2014/02/13 17:46:33
Oh, this was from some old bootstrap we used to ha
Jennifer Messerly
2014/02/13 20:43:45
awesome. done!
| |
75 | 82 |
76 return _inlineImport(id); | 83 return _inlineImport(id); |
77 }).then((_) => hasImports); | 84 |
85 } else if (rel == 'stylesheet') { | |
86 if (id == null) return null; | |
87 changed = true; | |
88 return _inlineStylesheet(id, tag); | |
89 } | |
90 }).then((_) => changed); | |
78 } | 91 } |
79 | 92 |
80 // Loads an asset identified by [id], visits its imports and collects its | 93 // Loads an asset identified by [id], visits its imports and collects its |
81 // html imports. Then inlines it into the main document. | 94 // html imports. Then inlines it into the main document. |
82 Future _inlineImport(AssetId id) => | 95 Future _inlineImport(AssetId id) => |
83 readAsHtml(id, transform).then((doc) => _visitImports(doc, id).then((_) { | 96 readAsHtml(id, transform).then((doc) => _visitImports(doc, id).then((_) { |
84 | 97 |
85 new _UrlNormalizer(transform, id).visit(doc); | 98 new _UrlNormalizer(transform, id).visit(doc); |
86 _extractScripts(doc); | 99 _extractScripts(doc); |
87 | 100 |
88 // TODO(jmesserly): figure out how this is working in vulcanizer. | 101 // TODO(jmesserly): figure out how this is working in vulcanizer. |
89 // Do they produce a <body> tag with a <head> and <body> inside? | 102 // Do they produce a <body> tag with a <head> and <body> inside? |
90 imported.nodes | 103 imported.nodes..addAll(doc.head.nodes)..addAll(doc.body.nodes); |
91 ..addAll(doc.head.nodes) | |
92 ..addAll(doc.body.nodes); | |
93 })); | 104 })); |
94 | 105 |
106 Future _inlineStylesheet(AssetId id, Element link) => | |
Siggi Cherem (dart-lang)
2014/02/13 00:08:18
the formatting mixing => + then below is a bit odd
Jennifer Messerly
2014/02/13 06:11:22
okay, I'll change this one. But, I'm think we shou
| |
107 readAsString(id, transform).then((css) { | |
108 | |
109 css = new _UrlNormalizer(transform, id).visitCss(css); | |
110 link.replaceWith(new Element.tag('style')..text = css); | |
111 }); | |
112 | |
95 /** | 113 /** |
96 * Split Dart script tags from all the other elements. Now that Dartium | 114 * 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 | 115 * 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 | 116 * tags. Instead, we collect the urls of each script tag so we import |
99 * them directly from the Dart bootstrap code. | 117 * them directly from the Dart bootstrap code. |
100 */ | 118 */ |
101 void _extractScripts(Document document) { | 119 void _extractScripts(Document document) { |
102 bool first = true; | 120 bool first = true; |
103 for (var script in document.querySelectorAll('script')) { | 121 for (var script in document.querySelectorAll('script')) { |
104 if (script.attributes['type'] == 'application/dart') { | 122 if (script.attributes['type'] == 'application/dart') { |
(...skipping 60 matching lines...) Loading... | |
165 if (_urlAttributes.contains(key)) { | 183 if (_urlAttributes.contains(key)) { |
166 var url = node.attributes[key]; | 184 var url = node.attributes[key]; |
167 if (url != null && url != '' && !url.startsWith('{{')) { | 185 if (url != null && url != '' && !url.startsWith('{{')) { |
168 node.attributes[key] = _newUrl(url, node.sourceSpan); | 186 node.attributes[key] = _newUrl(url, node.sourceSpan); |
169 } | 187 } |
170 } | 188 } |
171 } | 189 } |
172 super.visitElement(node); | 190 super.visitElement(node); |
173 } | 191 } |
174 | 192 |
193 static final _URL = new RegExp(r'url\(([^)]*)\)', multiLine: true); | |
194 static final _QUOTE = new RegExp('["\']', multiLine: true); | |
195 | |
196 /** Visit the CSS text and replace any relative URLs so we can inline it. */ | |
197 // Ported from: | |
198 // https://github.com/Polymer/vulcanize/blob/c14f63696797cda18dc3d372b78aa3378 acc691f/lib/vulcan.js#L149 | |
199 // TODO(jmesserly): use csslib here instead? Parsing with RegEx is sadness. | |
200 // Maybe it's reliable enough for finding URLs in CSS? I'm not sure. | |
201 String visitCss(String cssText) { | |
202 var src = new SourceFile.text(sourceId.path, cssText); | |
203 return cssText.replaceAllMapped(_URL, (match) { | |
204 // Extract the URL, without any surrounding quotes. | |
205 var span = src.span(match.start, match.end); | |
206 var href = match[1].replaceAll(_QUOTE, ''); | |
207 href = _newUrl(href, span); | |
208 return 'url($href)'; | |
209 }); | |
210 } | |
211 | |
175 _newUrl(String href, Span span) { | 212 _newUrl(String href, Span span) { |
176 var uri = Uri.parse(href); | 213 var uri = Uri.parse(href); |
177 if (uri.isAbsolute) return href; | 214 if (uri.isAbsolute) return href; |
178 if (!uri.scheme.isEmpty) return href; | 215 if (!uri.scheme.isEmpty) return href; |
179 if (!uri.host.isEmpty) return href; | 216 if (!uri.host.isEmpty) return href; |
180 if (uri.path.isEmpty) return href; // Implies standalone ? or # in URI. | 217 if (uri.path.isEmpty) return href; // Implies standalone ? or # in URI. |
181 if (path.isAbsolute(href)) return href; | 218 if (path.isAbsolute(href)) return href; |
182 | 219 |
183 var id = resolve(sourceId, href, transform.logger, span); | 220 var id = resolve(sourceId, href, transform.logger, span); |
184 if (id == null) return href; | 221 if (id == null) return href; |
(...skipping 33 matching lines...) Loading... | |
218 'cite', // in blockquote, del, ins, q | 255 'cite', // in blockquote, del, ins, q |
219 'data', // in object | 256 'data', // in object |
220 'formaction', // in button, input | 257 'formaction', // in button, input |
221 'href', // in a, area, link, base, command | 258 'href', // in a, area, link, base, command |
222 'icon', // in command | 259 'icon', // in command |
223 'manifest', // in html | 260 'manifest', // in html |
224 'poster', // in video | 261 'poster', // in video |
225 'src', // in audio, embed, iframe, img, input, script, source, track, | 262 'src', // in audio, embed, iframe, img, input, script, source, track, |
226 // video | 263 // video |
227 ]; | 264 ]; |
OLD | NEW |