OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 library web_components.build.import_inliner; | 4 library web_components.build.import_inliner; |
5 | 5 |
6 import 'dart:async'; | 6 import 'dart:async'; |
7 import 'dart:collection' show LinkedHashMap; | 7 import 'dart:collection' show LinkedHashMap; |
8 import 'package:barback/barback.dart'; | 8 import 'package:barback/barback.dart'; |
9 import 'package:code_transformers/assets.dart'; | 9 import 'package:code_transformers/assets.dart'; |
10 import 'package:code_transformers/messages/build_logger.dart'; | 10 import 'package:code_transformers/messages/build_logger.dart'; |
11 import 'package:html5lib/dom.dart'; | 11 import 'package:html5lib/dom.dart'; |
12 import 'package:html5lib/dom_parsing.dart' show TreeVisitor; | 12 import 'package:html5lib/dom_parsing.dart' show TreeVisitor; |
13 import 'package:path/path.dart' as path; | 13 import 'package:path/path.dart' as path; |
14 import 'package:source_span/source_span.dart'; | 14 import 'package:source_span/source_span.dart'; |
15 import 'common.dart'; | 15 import 'common.dart'; |
16 import 'import_crawler.dart'; | 16 import 'import_crawler.dart'; |
17 import 'messages.dart'; | 17 import 'messages.dart'; |
18 | 18 |
19 /// Transformer which inlines all html imports found from the entry points. This | 19 /// Transformer which inlines all html imports found from the entry points. This |
20 /// deletes all dart scripts found during the inlining, so the | 20 /// deletes all dart scripts found during the inlining, so the |
21 /// [ScriptCompactorTransformer] should be ran first if there are any dart files | 21 /// [ScriptCompactorTransformer] should be ran first if there are any dart files |
22 /// in html imports. | 22 /// in html imports. |
23 class ImportInlinerTransformer extends Transformer { | 23 class ImportInlinerTransformer extends Transformer { |
24 final List<String> entryPoints; | 24 final List<String> entryPoints; |
| 25 final List<String> bindingStartDelimiters; |
25 | 26 |
26 ImportInlinerTransformer([this.entryPoints]); | 27 ImportInlinerTransformer( |
| 28 [this.entryPoints, this.bindingStartDelimiters = const []]); |
27 | 29 |
28 bool isPrimary(AssetId id) { | 30 bool isPrimary(AssetId id) { |
29 if (entryPoints != null) return entryPoints.contains(id.path); | 31 if (entryPoints != null) return entryPoints.contains(id.path); |
30 // If no entry point is supplied, then any html file under web/ or test/ is | 32 // If no entry point is supplied, then any html file under web/ or test/ is |
31 // an entry point. | 33 // an entry point. |
32 return (id.path.startsWith('web/') || id.path.startsWith('test/')) && | 34 return (id.path.startsWith('web/') || id.path.startsWith('test/')) && |
33 id.path.endsWith('.html'); | 35 id.path.endsWith('.html'); |
34 } | 36 } |
35 | 37 |
36 apply(Transform transform) { | 38 apply(Transform transform) { |
37 var logger = new BuildLogger(transform, convertErrorsToWarnings: true); | 39 var logger = new BuildLogger(transform, convertErrorsToWarnings: true); |
38 return new ImportInliner(transform, transform.primaryInput.id, logger) | 40 return new ImportInliner(transform, transform.primaryInput.id, logger, |
39 .run(); | 41 bindingStartDelimiters: bindingStartDelimiters).run(); |
40 } | 42 } |
41 } | 43 } |
42 | 44 |
43 /// Helper class which actually does all the inlining of html imports for a | 45 /// Helper class which actually does all the inlining of html imports for a |
44 /// single entry point. | 46 /// single entry point. |
45 class ImportInliner { | 47 class ImportInliner { |
46 // Can be an AggregateTransform or Transform | 48 // Can be an AggregateTransform or Transform |
47 final transform; | 49 final transform; |
48 // The primary input to start from. | 50 // The primary input to start from. |
49 final AssetId primaryInput; | 51 final AssetId primaryInput; |
50 // The logger to use. | 52 // The logger to use. |
51 final BuildLogger logger; | 53 final BuildLogger logger; |
| 54 // The start delimiters for template bindings, such as '{{' or '[['. |
| 55 final List<String> bindingStartDelimiters; |
52 | 56 |
53 ImportInliner(this.transform, this.primaryInput, this.logger); | 57 ImportInliner(this.transform, this.primaryInput, this.logger, |
| 58 {this.bindingStartDelimiters: const []}); |
54 | 59 |
55 Future run() { | 60 Future run() { |
56 var crawler = new ImportCrawler(transform, primaryInput, logger); | 61 var crawler = new ImportCrawler(transform, primaryInput, logger); |
57 return crawler.crawlImports().then((imports) { | 62 return crawler.crawlImports().then((imports) { |
58 var primaryDocument = imports[primaryInput].document; | 63 var primaryDocument = imports[primaryInput].document; |
59 | 64 |
60 // Normalize urls in the entry point. | 65 // Normalize urls in the entry point. |
61 var changed = new _UrlNormalizer(primaryInput, primaryInput, logger) | 66 var changed = new _UrlNormalizer( |
| 67 primaryInput, primaryInput, logger, bindingStartDelimiters) |
62 .visit(primaryDocument); | 68 .visit(primaryDocument); |
63 | 69 |
64 // Inline things if needed, always have at least one (the entry point). | 70 // Inline things if needed, always have at least one (the entry point). |
65 if (imports.length > 1) { | 71 if (imports.length > 1) { |
66 _inlineImports(primaryDocument, imports); | 72 _inlineImports(primaryDocument, imports); |
67 } else if (!changed && | 73 } else if (!changed && |
68 primaryDocument.querySelectorAll('link[rel="import"]').length == 0) { | 74 primaryDocument.querySelectorAll('link[rel="import"]').length == 0) { |
69 // If there were no url changes and no imports, then we are done. | 75 // If there were no url changes and no imports, then we are done. |
70 return; | 76 return; |
71 } | 77 } |
(...skipping 25 matching lines...) Expand all Loading... |
97 | 103 |
98 // Add all the other imports! | 104 // Add all the other imports! |
99 imports.forEach((AssetId asset, ImportData data) { | 105 imports.forEach((AssetId asset, ImportData data) { |
100 if (asset == primaryInput) return; | 106 if (asset == primaryInput) return; |
101 var document = data.document; | 107 var document = data.document; |
102 // Remove all dart script tags. | 108 // Remove all dart script tags. |
103 document | 109 document |
104 .querySelectorAll('script[type="$dartType"]') | 110 .querySelectorAll('script[type="$dartType"]') |
105 .forEach((script) => script.remove()); | 111 .forEach((script) => script.remove()); |
106 // Normalize urls in attributes and inline css. | 112 // Normalize urls in attributes and inline css. |
107 new _UrlNormalizer(data.fromId, asset, logger).visit(document); | 113 new _UrlNormalizer(data.fromId, asset, logger, bindingStartDelimiters) |
| 114 .visit(document); |
108 // Replace the import with its contents by appending the nodes | 115 // Replace the import with its contents by appending the nodes |
109 // immediately before the import one at a time, and then removing the | 116 // immediately before the import one at a time, and then removing the |
110 // import from the document. | 117 // import from the document. |
111 var element = data.element; | 118 var element = data.element; |
112 var parent = element.parent; | 119 var parent = element.parent; |
113 document.head.nodes | 120 document.head.nodes |
114 .toList(growable: false) | 121 .toList(growable: false) |
115 .forEach((child) => parent.insertBefore(child, element)); | 122 .forEach((child) => parent.insertBefore(child, element)); |
116 document.body.nodes | 123 document.body.nodes |
117 .toList(growable: false) | 124 .toList(growable: false) |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
166 /// Counter used to ensure that every library name we inject is unique. | 173 /// Counter used to ensure that every library name we inject is unique. |
167 int _count = 0; | 174 int _count = 0; |
168 | 175 |
169 /// Path to the top level folder relative to the transform primaryInput. | 176 /// Path to the top level folder relative to the transform primaryInput. |
170 /// This should just be some arbitrary # of ../'s. | 177 /// This should just be some arbitrary # of ../'s. |
171 final String topLevelPath; | 178 final String topLevelPath; |
172 | 179 |
173 /// Whether or not the normalizer has changed something in the tree. | 180 /// Whether or not the normalizer has changed something in the tree. |
174 bool changed = false; | 181 bool changed = false; |
175 | 182 |
| 183 // The start delimiters for template bindings, such as '{{' or '[['. If these |
| 184 // are found before the first `/` in a url, then the url will not be |
| 185 // normalized. |
| 186 final List<String> bindingStartDelimiters; |
| 187 |
176 final BuildLogger logger; | 188 final BuildLogger logger; |
177 | 189 |
178 _UrlNormalizer(AssetId primaryInput, this.sourceId, this.logger) | 190 _UrlNormalizer(AssetId primaryInput, this.sourceId, this.logger, |
| 191 this.bindingStartDelimiters) |
179 : primaryInput = primaryInput, | 192 : primaryInput = primaryInput, |
180 topLevelPath = '../' * (path.url.split(primaryInput.path).length - 2); | 193 topLevelPath = '../' * (path.url.split(primaryInput.path).length - 2); |
181 | 194 |
182 bool visit(Node node) { | 195 bool visit(Node node) { |
183 super.visit(node); | 196 super.visit(node); |
184 return changed; | 197 return changed; |
185 } | 198 } |
186 | 199 |
187 visitElement(Element node) { | 200 visitElement(Element node) { |
188 // TODO(jakemac): Support custom elements that extend html elements which | 201 // TODO(jakemac): Support custom elements that extend html elements which |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
242 var suffix = href.substring(href.indexOf('packages/') + 9); | 255 var suffix = href.substring(href.indexOf('packages/') + 9); |
243 return '${topLevelPath}packages/$suffix'; | 256 return '${topLevelPath}packages/$suffix'; |
244 } else if (href.contains('assets/')) { | 257 } else if (href.contains('assets/')) { |
245 var suffix = href.substring(href.indexOf('assets/') + 7); | 258 var suffix = href.substring(href.indexOf('assets/') + 7); |
246 return '${topLevelPath}packages/$suffix'; | 259 return '${topLevelPath}packages/$suffix'; |
247 } | 260 } |
248 | 261 |
249 hrefToParse = '${href.substring(0, firstFolder + 1)}'; | 262 hrefToParse = '${href.substring(0, firstFolder + 1)}'; |
250 } | 263 } |
251 | 264 |
| 265 // If we found a binding before the first `/`, then just return the original |
| 266 // href, we can't determine anything about it. |
| 267 if (bindingStartDelimiters.any((d) => hrefToParse.contains(d))) return href; |
| 268 |
252 Uri uri; | 269 Uri uri; |
253 // Various template systems introduce invalid characters to uris which would | 270 // Various template systems introduce invalid characters to uris which would |
254 // be typically replaced at runtime. Parse errors are assumed to be caused | 271 // be typically replaced at runtime. Parse errors are assumed to be caused |
255 // by this, and we just return the original href in that case. | 272 // by this, and we just return the original href in that case. |
256 try { | 273 try { |
257 uri = Uri.parse(hrefToParse); | 274 uri = Uri.parse(hrefToParse); |
258 } catch (e) { | 275 } catch (e) { |
259 return href; | 276 return href; |
260 } | 277 } |
261 if (uri.isAbsolute) return href; | 278 if (uri.isAbsolute) return href; |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
337 '_href', // in a, area, link, base, command | 354 '_href', // in a, area, link, base, command |
338 'icon', | 355 'icon', |
339 '_icon', // in command | 356 '_icon', // in command |
340 'manifest', | 357 'manifest', |
341 '_manifest', // in html | 358 '_manifest', // in html |
342 'poster', | 359 'poster', |
343 '_poster', // in video | 360 '_poster', // in video |
344 'src', | 361 'src', |
345 '_src', // in audio, embed, iframe, img, input, script, source, track,video | 362 '_src', // in audio, embed, iframe, img, input, script, source, track,video |
346 ]; | 363 ]; |
OLD | NEW |