| 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 |