Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(365)

Side by Side Diff: pkg/polymer/lib/src/build/import_inliner.dart

Issue 427623002: Polymer transformer logs now show on the frontend for pub serve. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Moved some logic to the isPrimary function Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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:analyzer/analyzer.dart'; 11 import 'package:analyzer/analyzer.dart';
12 import 'package:analyzer/src/generated/ast.dart'; 12 import 'package:analyzer/src/generated/ast.dart';
13 import 'package:barback/barback.dart'; 13 import 'package:barback/barback.dart';
14 import 'package:code_transformers/assets.dart'; 14 import 'package:code_transformers/assets.dart';
15 import 'package:path/path.dart' as path; 15 import 'package:path/path.dart' as path;
16 import 'package:html5lib/dom.dart' show 16 import 'package:html5lib/dom.dart' show
17 Document, DocumentFragment, Element, Node; 17 Document, DocumentFragment, Element, Node;
18 import 'package:html5lib/dom_parsing.dart' show TreeVisitor; 18 import 'package:html5lib/dom_parsing.dart' show TreeVisitor;
19 import 'package:source_maps/refactor.dart' show TextEditTransaction; 19 import 'package:source_maps/refactor.dart' show TextEditTransaction;
20 import 'package:source_maps/span.dart'; 20 import 'package:source_maps/span.dart';
21 21
22 import 'common.dart'; 22 import 'common.dart';
23 import 'wrapped_logger.dart';
23 24
24 // TODO(sigmund): move to web_components package (dartbug.com/18037). 25 // TODO(sigmund): move to web_components package (dartbug.com/18037).
25 class _HtmlInliner extends PolymerTransformer { 26 class _HtmlInliner extends PolymerTransformer {
26 final TransformOptions options; 27 final TransformOptions options;
27 final Transform transform; 28 final Transform transform;
28 final TransformLogger logger; 29 final TransformLogger logger;
29 final AssetId docId; 30 final AssetId docId;
30 final seen = new Set<AssetId>(); 31 final seen = new Set<AssetId>();
31 final scriptIds = <AssetId>[]; 32 final scriptIds = <AssetId>[];
32 final extractedFiles = new Set<AssetId>(); 33 final extractedFiles = new Set<AssetId>();
33 bool experimentalBootstrap = false; 34 bool experimentalBootstrap = false;
34 35
35 /// The number of extracted inline Dart scripts. Used as a counter to give 36 /// The number of extracted inline Dart scripts. Used as a counter to give
36 /// unique-ish filenames. 37 /// unique-ish filenames.
37 int inlineScriptCounter = 0; 38 int inlineScriptCounter = 0;
38 39
39 _HtmlInliner(this.options, Transform transform) 40 _HtmlInliner(TransformOptions options, Transform transform)
40 : transform = transform, 41 : options = options,
41 logger = transform.logger, 42 transform = transform,
43 logger = (options.releaseMode) ? transform.logger :
44 new WrappedLogger(transform, convertErrorsToWarnings: true),
42 docId = transform.primaryInput.id; 45 docId = transform.primaryInput.id;
43 46
44 Future apply() { 47 Future apply() {
45 seen.add(docId); 48 seen.add(docId);
46 49
47 Document document; 50 Document document;
48 bool changed = false; 51 bool changed = false;
49 52
50 return readPrimaryAsHtml(transform).then((doc) { 53 return readPrimaryAsHtml(transform).then((doc) {
51 document = doc; 54 document = doc;
52 changed = new _UrlNormalizer(transform, docId).visit(document) || changed; 55 changed = new _UrlNormalizer(transform, docId, logger).visit(document)
56 || changed;
53 57
54 experimentalBootstrap = document.querySelectorAll('link').any((link) => 58 experimentalBootstrap = document.querySelectorAll('link').any((link) =>
55 link.attributes['rel'] == 'import' && 59 link.attributes['rel'] == 'import' &&
56 link.attributes['href'] == POLYMER_EXPERIMENTAL_HTML); 60 link.attributes['href'] == POLYMER_EXPERIMENTAL_HTML);
57 changed = _extractScripts(document) || changed; 61 changed = _extractScripts(document) || changed;
58 return _visitImports(document); 62 return _visitImports(document);
59 }).then((importsFound) { 63 }).then((importsFound) {
60 changed = changed || importsFound; 64 changed = changed || importsFound;
61 return _removeScripts(document); 65 return _removeScripts(document);
62 }).then((scriptsRemoved) { 66 }).then((scriptsRemoved) {
63 changed = changed || scriptsRemoved; 67 changed = changed || scriptsRemoved;
64 68
65 var output = transform.primaryInput; 69 var output = transform.primaryInput;
66 if (changed) output = new Asset.fromString(docId, document.outerHtml); 70 if (changed) output = new Asset.fromString(docId, document.outerHtml);
67 transform.addOutput(output); 71 transform.addOutput(output);
68 72
69 // We produce a secondary asset with extra information for later phases. 73 // We produce a secondary asset with extra information for later phases.
70 transform.addOutput(new Asset.fromString( 74 transform.addOutput(new Asset.fromString(
71 docId.addExtension('._data'), 75 docId.addExtension('._data'),
72 JSON.encode({ 76 JSON.encode({
73 'experimental_bootstrap': experimentalBootstrap, 77 'experimental_bootstrap': experimentalBootstrap,
74 'script_ids': scriptIds, 78 'script_ids': scriptIds,
75 }, toEncodable: (id) => id.serialize()))); 79 }, toEncodable: (id) => id.serialize())));
80
81 // Write out the logs collected by our [WrappedLogger].
82 if (logger is WrappedLogger) return logger.writeOutput();
76 }); 83 });
77 } 84 }
78 85
79 /// Visits imports in [document] and add the imported documents to documents. 86 /// Visits imports in [document] and add the imported documents to documents.
80 /// Documents are added in the order they appear, transitive imports are added 87 /// Documents are added in the order they appear, transitive imports are added
81 /// first. 88 /// first.
82 /// 89 ///
83 /// Returns `true` if and only if the document was changed and should be 90 /// Returns `true` if and only if the document was changed and should be
84 /// written out. 91 /// written out.
85 Future<bool> _visitImports(Document document) { 92 Future<bool> _visitImports(Document document) {
86 bool changed = false; 93 bool changed = false;
87 94
88 _moveHeadToBody(document); 95 _moveHeadToBody(document);
89 96
90 // Note: we need to preserve the import order in the generated output. 97 // Note: we need to preserve the import order in the generated output.
91 return Future.forEach(document.querySelectorAll('link'), (Element tag) { 98 return Future.forEach(document.querySelectorAll('link'), (Element tag) {
92 var rel = tag.attributes['rel']; 99 var rel = tag.attributes['rel'];
93 if (rel != 'import' && rel != 'stylesheet') return null; 100 if (rel != 'import' && rel != 'stylesheet') return null;
94 101
95 // Note: URL has already been normalized so use docId. 102 // Note: URL has already been normalized so use docId.
96 var href = tag.attributes['href']; 103 var href = tag.attributes['href'];
97 var id = uriToAssetId(docId, href, transform.logger, tag.sourceSpan, 104 var id = uriToAssetId(docId, href, logger, tag.sourceSpan,
98 errorOnAbsolute: rel != 'stylesheet'); 105 errorOnAbsolute: rel != 'stylesheet');
99 106
100 if (rel == 'import') { 107 if (rel == 'import') {
101 changed = true; 108 changed = true;
102 if (id == null || !seen.add(id)) { 109 if (id == null || !seen.add(id)) {
103 tag.remove(); 110 tag.remove();
104 return null; 111 return null;
105 } 112 }
106 return _inlineImport(id, tag); 113 return _inlineImport(id, tag);
107 114
(...skipping 30 matching lines...) Expand all
138 // Move the node into the body, where its contents will be placed. 145 // Move the node into the body, where its contents will be placed.
139 doc.body.insertBefore(node, insertionPoint); 146 doc.body.insertBefore(node, insertionPoint);
140 } 147 }
141 } 148 }
142 } 149 }
143 150
144 /// Loads an asset identified by [id], visits its imports and collects its 151 /// Loads an asset identified by [id], visits its imports and collects its
145 /// html imports. Then inlines it into the main document. 152 /// html imports. Then inlines it into the main document.
146 Future _inlineImport(AssetId id, Element link) { 153 Future _inlineImport(AssetId id, Element link) {
147 return readAsHtml(id, transform).catchError((error) { 154 return readAsHtml(id, transform).catchError((error) {
148 transform.logger.error( 155 logger.error(
149 "Failed to inline html import: $error", asset: id, 156 "Failed to inline html import: $error", asset: id,
150 span: link.sourceSpan); 157 span: link.sourceSpan);
151 }).then((doc) { 158 }).then((doc) {
152 if (doc == null) return false; 159 if (doc == null) return false;
153 new _UrlNormalizer(transform, id).visit(doc); 160 new _UrlNormalizer(transform, id, logger).visit(doc);
154 return _visitImports(doc).then((_) { 161 return _visitImports(doc).then((_) {
155 // _UrlNormalizer already ensures there is a library name. 162 // _UrlNormalizer already ensures there is a library name.
156 _extractScripts(doc, injectLibraryName: false); 163 _extractScripts(doc, injectLibraryName: false);
157 164
158 // TODO(jmesserly): figure out how this is working in vulcanizer. 165 // TODO(jmesserly): figure out how this is working in vulcanizer.
159 // Do they produce a <body> tag with a <head> and <body> inside? 166 // Do they produce a <body> tag with a <head> and <body> inside?
160 var imported = new DocumentFragment(); 167 var imported = new DocumentFragment();
161 imported.nodes..addAll(doc.head.nodes)..addAll(doc.body.nodes); 168 imported.nodes..addAll(doc.head.nodes)..addAll(doc.body.nodes);
162 link.replaceWith(imported); 169 link.replaceWith(imported);
163 }); 170 });
164 }); 171 });
165 } 172 }
166 173
167 Future _inlineStylesheet(AssetId id, Element link) { 174 Future _inlineStylesheet(AssetId id, Element link) {
168 return transform.readInputAsString(id).catchError((error) { 175 return transform.readInputAsString(id).catchError((error) {
169 // TODO(jakemac): Move this warning to the linter once we can make it run 176 // TODO(jakemac): Move this warning to the linter once we can make it run
170 // always (see http://dartbug.com/17199). Then hide this error and replace 177 // always (see http://dartbug.com/17199). Then hide this error and replace
171 // with a comment pointing to the linter error (so we don't double warn). 178 // with a comment pointing to the linter error (so we don't double warn).
172 transform.logger.warning( 179 logger.warning(
173 "Failed to inline stylesheet: $error", asset: id, 180 "Failed to inline stylesheet: $error", asset: id,
174 span: link.sourceSpan); 181 span: link.sourceSpan);
175 }).then((css) { 182 }).then((css) {
176 if (css == null) return; 183 if (css == null) return;
177 css = new _UrlNormalizer(transform, id).visitCss(css); 184 css = new _UrlNormalizer(transform, id, logger).visitCss(css);
178 var styleElement = new Element.tag('style')..text = css; 185 var styleElement = new Element.tag('style')..text = css;
179 // Copy over the extra attributes from the link tag to the style tag. 186 // Copy over the extra attributes from the link tag to the style tag.
180 // This adds support for no-shim, shim-shadowdom, etc. 187 // This adds support for no-shim, shim-shadowdom, etc.
181 link.attributes.forEach((key, value) { 188 link.attributes.forEach((key, value) {
182 if (!IGNORED_LINKED_STYLE_ATTRS.contains(key)) { 189 if (!IGNORED_LINKED_STYLE_ATTRS.contains(key)) {
183 styleElement.attributes[key] = value; 190 styleElement.attributes[key] = value;
184 } 191 }
185 }); 192 });
186 link.replaceWith(styleElement); 193 link.replaceWith(styleElement);
187 }); 194 });
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
313 /// Counter used to ensure that every library name we inject is unique. 320 /// Counter used to ensure that every library name we inject is unique.
314 int _count = 0; 321 int _count = 0;
315 322
316 /// Path to the top level folder relative to the transform primaryInput. 323 /// Path to the top level folder relative to the transform primaryInput.
317 /// This should just be some arbitrary # of ../'s. 324 /// This should just be some arbitrary # of ../'s.
318 final String topLevelPath; 325 final String topLevelPath;
319 326
320 /// Whether or not the normalizer has changed something in the tree. 327 /// Whether or not the normalizer has changed something in the tree.
321 bool changed = false; 328 bool changed = false;
322 329
323 _UrlNormalizer(transform, this.sourceId) 330 final TransformLogger logger;
331
332 _UrlNormalizer(transform, this.sourceId, this.logger)
324 : transform = transform, 333 : transform = transform,
325 topLevelPath = 334 topLevelPath =
326 '../' * (transform.primaryInput.id.path.split('/').length - 2); 335 '../' * (transform.primaryInput.id.path.split('/').length - 2);
327 336
328 visit(Node node) { 337 visit(Node node) {
329 super.visit(node); 338 super.visit(node);
330 return changed; 339 return changed;
331 } 340 }
332 341
333 visitElement(Element node) { 342 visitElement(Element node) {
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
382 String visitInlineDart(String code) { 391 String visitInlineDart(String code) {
383 var unit = parseDirectives(code, suppressErrors: true); 392 var unit = parseDirectives(code, suppressErrors: true);
384 var file = new SourceFile.text(spanUrlFor(sourceId, transform), code); 393 var file = new SourceFile.text(spanUrlFor(sourceId, transform), code);
385 var output = new TextEditTransaction(code, file); 394 var output = new TextEditTransaction(code, file);
386 var foundLibraryDirective = false; 395 var foundLibraryDirective = false;
387 for (Directive directive in unit.directives) { 396 for (Directive directive in unit.directives) {
388 if (directive is UriBasedDirective) { 397 if (directive is UriBasedDirective) {
389 var uri = directive.uri.stringValue; 398 var uri = directive.uri.stringValue;
390 var span = _getSpan(file, directive.uri); 399 var span = _getSpan(file, directive.uri);
391 400
392 var id = uriToAssetId(sourceId, uri, transform.logger, span, 401 var id =
393 errorOnAbsolute: false); 402 uriToAssetId(sourceId, uri, logger, span, errorOnAbsolute: false);
Siggi Cherem (dart-lang) 2014/08/01 21:31:51 minor nit: let's keep the original wrapping
jakemac 2014/08/04 19:49:58 Done.
394 if (id == null) continue; 403 if (id == null) continue;
395 404
396 var primaryId = transform.primaryInput.id; 405 var primaryId = transform.primaryInput.id;
397 var newUri = assetUrlFor(id, primaryId, transform.logger); 406 var newUri = assetUrlFor(id, primaryId, logger);
398 if (newUri != uri) { 407 if (newUri != uri) {
399 output.edit(span.start.offset, span.end.offset, "'$newUri'"); 408 output.edit(span.start.offset, span.end.offset, "'$newUri'");
400 } 409 }
401 } else if (directive is LibraryDirective) { 410 } else if (directive is LibraryDirective) {
402 foundLibraryDirective = true; 411 foundLibraryDirective = true;
403 } 412 }
404 } 413 }
405 414
406 if (!foundLibraryDirective) { 415 if (!foundLibraryDirective) {
407 // Ensure all inline scripts also have a library name. 416 // Ensure all inline scripts also have a library name.
(...skipping 12 matching lines...) Expand all
420 // Uri.parse blows up on invalid characters (like {{). Encoding the uri 429 // Uri.parse blows up on invalid characters (like {{). Encoding the uri
421 // allows it to be parsed, which does the correct thing in the general case. 430 // allows it to be parsed, which does the correct thing in the general case.
422 // This uri not used to build the new uri, so it never needs to be decoded. 431 // This uri not used to build the new uri, so it never needs to be decoded.
423 var uri = Uri.parse(Uri.encodeFull(href)); 432 var uri = Uri.parse(Uri.encodeFull(href));
424 if (uri.isAbsolute) return href; 433 if (uri.isAbsolute) return href;
425 if (!uri.scheme.isEmpty) return href; 434 if (!uri.scheme.isEmpty) return href;
426 if (!uri.host.isEmpty) return href; 435 if (!uri.host.isEmpty) return href;
427 if (uri.path.isEmpty) return href; // Implies standalone ? or # in URI. 436 if (uri.path.isEmpty) return href; // Implies standalone ? or # in URI.
428 if (path.isAbsolute(href)) return href; 437 if (path.isAbsolute(href)) return href;
429 438
430 var id = uriToAssetId(sourceId, href, transform.logger, span); 439 var id = uriToAssetId(sourceId, href, logger, span);
431 if (id == null) return href; 440 if (id == null) return href;
432 var primaryId = transform.primaryInput.id; 441 var primaryId = transform.primaryInput.id;
433 442
434 if (id.path.startsWith('lib/')) { 443 if (id.path.startsWith('lib/')) {
435 return '${topLevelPath}packages/${id.package}/${id.path.substring(4)}'; 444 return '${topLevelPath}packages/${id.package}/${id.path.substring(4)}';
436 } 445 }
437 446
438 if (id.path.startsWith('asset/')) { 447 if (id.path.startsWith('asset/')) {
439 return '${topLevelPath}assets/${id.package}/${id.path.substring(6)}'; 448 return '${topLevelPath}assets/${id.package}/${id.path.substring(6)}';
440 } 449 }
441 450
442 if (primaryId.package != id.package) { 451 if (primaryId.package != id.package) {
443 // Techincally we shouldn't get there 452 // Techincally we shouldn't get there
444 transform.logger.error("don't know how to include $id from $primaryId", 453 logger.error("don't know how to include $id from $primaryId", span: span);
445 span: span);
446 return href; 454 return href;
447 } 455 }
448 456
449 var builder = path.url; 457 var builder = path.url;
450 return builder.relative(builder.join('/', id.path), 458 return builder.relative(builder.join('/', id.path),
451 from: builder.join('/', builder.dirname(primaryId.path))); 459 from: builder.join('/', builder.dirname(primaryId.path)));
452 } 460 }
453 } 461 }
454 462
455 /// HTML attributes that expect a URL value. 463 /// HTML attributes that expect a URL value.
(...skipping 18 matching lines...) Expand all
474 /// When inlining <link rel="stylesheet"> tags copy over all attributes to the 482 /// When inlining <link rel="stylesheet"> tags copy over all attributes to the
475 /// style tag except these ones. 483 /// style tag except these ones.
476 const IGNORED_LINKED_STYLE_ATTRS = 484 const IGNORED_LINKED_STYLE_ATTRS =
477 const ['charset', 'href', 'href-lang', 'rel', 'rev']; 485 const ['charset', 'href', 'href-lang', 'rel', 'rev'];
478 486
479 /// Global RegExp objects for validating generated library names. 487 /// Global RegExp objects for validating generated library names.
480 final INVALID_LIB_CHARS_REGEX = new RegExp('[^a-z0-9_]'); 488 final INVALID_LIB_CHARS_REGEX = new RegExp('[^a-z0-9_]');
481 final NUM_REGEX = new RegExp('[0-9]'); 489 final NUM_REGEX = new RegExp('[0-9]');
482 490
483 _getSpan(SourceFile file, AstNode node) => file.span(node.offset, node.end); 491 _getSpan(SourceFile file, AstNode node) => file.span(node.offset, node.end);
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698