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

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: dont wrap the logger in release mode 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
« no previous file with comments | « pkg/polymer/lib/src/build/common.dart ('k') | pkg/polymer/lib/src/build/linter.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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_span/source_span.dart'; 20 import 'package:source_span/source_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 (options.injectBuildLogsInOutput && logger is WrappedLogger) {
Siggi Cherem (dart-lang) 2014/08/06 17:25:22 I'm guessing our invariants guarantee that the sec
jakemac 2014/08/06 19:41:22 Technically not any more, when in release mode we
Siggi Cherem (dart-lang) 2014/08/06 19:50:22 Ah ok - for some reason I thought we didn't let th
jakemac 2014/08/06 19:59:37 I do want to keep the same semantics around conver
83 return logger.writeOutput();
84 }
76 }); 85 });
77 } 86 }
78 87
79 /// Visits imports in [document] and add the imported documents to documents. 88 /// Visits imports in [document] and add the imported documents to documents.
80 /// Documents are added in the order they appear, transitive imports are added 89 /// Documents are added in the order they appear, transitive imports are added
81 /// first. 90 /// first.
82 /// 91 ///
83 /// Returns `true` if and only if the document was changed and should be 92 /// Returns `true` if and only if the document was changed and should be
84 /// written out. 93 /// written out.
85 Future<bool> _visitImports(Document document) { 94 Future<bool> _visitImports(Document document) {
86 bool changed = false; 95 bool changed = false;
87 96
88 _moveHeadToBody(document); 97 _moveHeadToBody(document);
89 98
90 // Note: we need to preserve the import order in the generated output. 99 // Note: we need to preserve the import order in the generated output.
91 return Future.forEach(document.querySelectorAll('link'), (Element tag) { 100 return Future.forEach(document.querySelectorAll('link'), (Element tag) {
92 var rel = tag.attributes['rel']; 101 var rel = tag.attributes['rel'];
93 if (rel != 'import' && rel != 'stylesheet') return null; 102 if (rel != 'import' && rel != 'stylesheet') return null;
94 103
95 // Note: URL has already been normalized so use docId. 104 // Note: URL has already been normalized so use docId.
96 var href = tag.attributes['href']; 105 var href = tag.attributes['href'];
97 var id = uriToAssetId(docId, href, transform.logger, tag.sourceSpan, 106 var id = uriToAssetId(docId, href, logger, tag.sourceSpan,
98 errorOnAbsolute: rel != 'stylesheet'); 107 errorOnAbsolute: rel != 'stylesheet');
99 108
100 if (rel == 'import') { 109 if (rel == 'import') {
101 changed = true; 110 changed = true;
102 if (id == null || !seen.add(id)) { 111 if (id == null || !seen.add(id)) {
103 tag.remove(); 112 tag.remove();
104 return null; 113 return null;
105 } 114 }
106 return _inlineImport(id, tag); 115 return _inlineImport(id, tag);
107 116
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
139 // Move the node into the body, where its contents will be placed. 148 // Move the node into the body, where its contents will be placed.
140 doc.body.insertBefore(node, insertionPoint); 149 doc.body.insertBefore(node, insertionPoint);
141 } 150 }
142 } 151 }
143 } 152 }
144 153
145 /// Loads an asset identified by [id], visits its imports and collects its 154 /// Loads an asset identified by [id], visits its imports and collects its
146 /// html imports. Then inlines it into the main document. 155 /// html imports. Then inlines it into the main document.
147 Future _inlineImport(AssetId id, Element link) { 156 Future _inlineImport(AssetId id, Element link) {
148 return readAsHtml(id, transform).catchError((error) { 157 return readAsHtml(id, transform).catchError((error) {
149 transform.logger.error( 158 logger.error(
150 "Failed to inline html import: $error", asset: id, 159 "Failed to inline html import: $error", asset: id,
151 span: link.sourceSpan); 160 span: link.sourceSpan);
152 }).then((doc) { 161 }).then((doc) {
153 if (doc == null) return false; 162 if (doc == null) return false;
154 new _UrlNormalizer(transform, id).visit(doc); 163 new _UrlNormalizer(transform, id, logger).visit(doc);
155 return _visitImports(doc).then((_) { 164 return _visitImports(doc).then((_) {
156 // _UrlNormalizer already ensures there is a library name. 165 // _UrlNormalizer already ensures there is a library name.
157 _extractScripts(doc, injectLibraryName: false); 166 _extractScripts(doc, injectLibraryName: false);
158 167
159 // TODO(jmesserly): figure out how this is working in vulcanizer. 168 // TODO(jmesserly): figure out how this is working in vulcanizer.
160 // Do they produce a <body> tag with a <head> and <body> inside? 169 // Do they produce a <body> tag with a <head> and <body> inside?
161 var imported = new DocumentFragment(); 170 var imported = new DocumentFragment();
162 imported.nodes..addAll(doc.head.nodes)..addAll(doc.body.nodes); 171 imported.nodes..addAll(doc.head.nodes)..addAll(doc.body.nodes);
163 link.replaceWith(imported); 172 link.replaceWith(imported);
164 }); 173 });
165 }); 174 });
166 } 175 }
167 176
168 Future _inlineStylesheet(AssetId id, Element link) { 177 Future _inlineStylesheet(AssetId id, Element link) {
169 return transform.readInputAsString(id).catchError((error) { 178 return transform.readInputAsString(id).catchError((error) {
170 // TODO(jakemac): Move this warning to the linter once we can make it run 179 // TODO(jakemac): Move this warning to the linter once we can make it run
171 // always (see http://dartbug.com/17199). Then hide this error and replace 180 // always (see http://dartbug.com/17199). Then hide this error and replace
172 // with a comment pointing to the linter error (so we don't double warn). 181 // with a comment pointing to the linter error (so we don't double warn).
173 transform.logger.warning( 182 logger.warning(
174 "Failed to inline stylesheet: $error", asset: id, 183 "Failed to inline stylesheet: $error", asset: id,
175 span: link.sourceSpan); 184 span: link.sourceSpan);
176 }).then((css) { 185 }).then((css) {
177 if (css == null) return; 186 if (css == null) return;
178 css = new _UrlNormalizer(transform, id).visitCss(css); 187 css = new _UrlNormalizer(transform, id, logger).visitCss(css);
179 var styleElement = new Element.tag('style')..text = css; 188 var styleElement = new Element.tag('style')..text = css;
180 // Copy over the extra attributes from the link tag to the style tag. 189 // Copy over the extra attributes from the link tag to the style tag.
181 // This adds support for no-shim, shim-shadowdom, etc. 190 // This adds support for no-shim, shim-shadowdom, etc.
182 link.attributes.forEach((key, value) { 191 link.attributes.forEach((key, value) {
183 if (!IGNORED_LINKED_STYLE_ATTRS.contains(key)) { 192 if (!IGNORED_LINKED_STYLE_ATTRS.contains(key)) {
184 styleElement.attributes[key] = value; 193 styleElement.attributes[key] = value;
185 } 194 }
186 }); 195 });
187 link.replaceWith(styleElement); 196 link.replaceWith(styleElement);
188 }); 197 });
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after
320 /// Counter used to ensure that every library name we inject is unique. 329 /// Counter used to ensure that every library name we inject is unique.
321 int _count = 0; 330 int _count = 0;
322 331
323 /// Path to the top level folder relative to the transform primaryInput. 332 /// Path to the top level folder relative to the transform primaryInput.
324 /// This should just be some arbitrary # of ../'s. 333 /// This should just be some arbitrary # of ../'s.
325 final String topLevelPath; 334 final String topLevelPath;
326 335
327 /// Whether or not the normalizer has changed something in the tree. 336 /// Whether or not the normalizer has changed something in the tree.
328 bool changed = false; 337 bool changed = false;
329 338
330 _UrlNormalizer(transform, this.sourceId) 339 final TransformLogger logger;
340
341 _UrlNormalizer(transform, this.sourceId, this.logger)
331 : transform = transform, 342 : transform = transform,
332 topLevelPath = 343 topLevelPath =
333 '../' * (transform.primaryInput.id.path.split('/').length - 2); 344 '../' * (transform.primaryInput.id.path.split('/').length - 2);
334 345
335 visit(Node node) { 346 visit(Node node) {
336 super.visit(node); 347 super.visit(node);
337 return changed; 348 return changed;
338 } 349 }
339 350
340 visitElement(Element node) { 351 visitElement(Element node) {
341 // TODO(jakemac): Support custom elements that extend html elements which 352 // TODO(jakemac): Support custom elements that extend html elements which
342 // have url-like attributes. This probably means keeping a list of which 353 // have url-like attributes. This probably means keeping a list of which
343 // html elements support each url-like attribute. 354 // html elements support each url-like attribute.
344 if (!isCustomTagName(node.localName)) { 355 if (!isCustomTagName(node.localName)) {
345 node.attributes.forEach((name, value) { 356 node.attributes.forEach((name, value) {
346 if (_urlAttributes.contains(name)) { 357 if (_urlAttributes.contains(name)) {
347 if (!name.startsWith('_') && value.contains(_BINDING_REGEX)) { 358 if (!name.startsWith('_') && value.contains(_BINDING_REGEX)) {
348 transform.logger.warning( 359 logger.warning(
349 'When using bindings with the "$name" attribute you may ' 360 'When using bindings with the "$name" attribute you may '
350 'experience errors in certain browsers. Please use the ' 361 'experience errors in certain browsers. Please use the '
351 '"_$name" attribute instead. For more information, see ' 362 '"_$name" attribute instead. For more information, see '
352 'http://goo.gl/5av8cU', span: node.sourceSpan, asset: sourceId); 363 'http://goo.gl/5av8cU', span: node.sourceSpan, asset: sourceId);
353 } else if (name.startsWith('_') && !value.contains(_BINDING_REGEX)) { 364 } else if (name.startsWith('_') && !value.contains(_BINDING_REGEX)) {
354 transform.logger.warning( 365 logger.warning(
355 'The "$name" attribute is only supported when using bindings. ' 366 'The "$name" attribute is only supported when using bindings. '
356 'Please change to the "${name.substring(1)}" attribute.', 367 'Please change to the "${name.substring(1)}" attribute.',
357 span: node.sourceSpan, asset: sourceId); 368 span: node.sourceSpan, asset: sourceId);
358 } 369 }
359 if (value != '' && !value.trim().startsWith(_BINDING_REGEX)) { 370 if (value != '' && !value.trim().startsWith(_BINDING_REGEX)) {
360 node.attributes[name] = _newUrl(value, node.sourceSpan); 371 node.attributes[name] = _newUrl(value, node.sourceSpan);
361 changed = changed || value != node.attributes[name]; 372 changed = changed || value != node.attributes[name];
362 } 373 }
363 } 374 }
364 }); 375 });
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
400 String visitInlineDart(String code) { 411 String visitInlineDart(String code) {
401 var unit = parseDirectives(code, suppressErrors: true); 412 var unit = parseDirectives(code, suppressErrors: true);
402 var file = new SourceFile(code, url: spanUrlFor(sourceId, transform)); 413 var file = new SourceFile(code, url: spanUrlFor(sourceId, transform));
403 var output = new TextEditTransaction(code, file); 414 var output = new TextEditTransaction(code, file);
404 var foundLibraryDirective = false; 415 var foundLibraryDirective = false;
405 for (Directive directive in unit.directives) { 416 for (Directive directive in unit.directives) {
406 if (directive is UriBasedDirective) { 417 if (directive is UriBasedDirective) {
407 var uri = directive.uri.stringValue; 418 var uri = directive.uri.stringValue;
408 var span = _getSpan(file, directive.uri); 419 var span = _getSpan(file, directive.uri);
409 420
410 var id = uriToAssetId(sourceId, uri, transform.logger, span, 421 var id = uriToAssetId(sourceId, uri, logger, span,
411 errorOnAbsolute: false); 422 errorOnAbsolute: false);
412 if (id == null) continue; 423 if (id == null) continue;
413 424
414 var primaryId = transform.primaryInput.id; 425 var primaryId = transform.primaryInput.id;
415 var newUri = assetUrlFor(id, primaryId, transform.logger); 426 var newUri = assetUrlFor(id, primaryId, logger);
416 if (newUri != uri) { 427 if (newUri != uri) {
417 output.edit(span.start.offset, span.end.offset, "'$newUri'"); 428 output.edit(span.start.offset, span.end.offset, "'$newUri'");
418 } 429 }
419 } else if (directive is LibraryDirective) { 430 } else if (directive is LibraryDirective) {
420 foundLibraryDirective = true; 431 foundLibraryDirective = true;
421 } 432 }
422 } 433 }
423 434
424 if (!foundLibraryDirective) { 435 if (!foundLibraryDirective) {
425 // Ensure all inline scripts also have a library name. 436 // Ensure all inline scripts also have a library name.
(...skipping 23 matching lines...) Expand all
449 hrefToParse = '${href.substring(0, firstBinding)}$placeholder'; 460 hrefToParse = '${href.substring(0, firstBinding)}$placeholder';
450 } 461 }
451 462
452 var uri = Uri.parse(hrefToParse); 463 var uri = Uri.parse(hrefToParse);
453 if (uri.isAbsolute) return href; 464 if (uri.isAbsolute) return href;
454 if (!uri.scheme.isEmpty) return href; 465 if (!uri.scheme.isEmpty) return href;
455 if (!uri.host.isEmpty) return href; 466 if (!uri.host.isEmpty) return href;
456 if (uri.path.isEmpty) return href; // Implies standalone ? or # in URI. 467 if (uri.path.isEmpty) return href; // Implies standalone ? or # in URI.
457 if (path.isAbsolute(href)) return href; 468 if (path.isAbsolute(href)) return href;
458 469
459 var id = uriToAssetId(sourceId, hrefToParse, transform.logger, span); 470 var id = uriToAssetId(sourceId, hrefToParse, logger, span);
460 if (id == null) return href; 471 if (id == null) return href;
461 var primaryId = transform.primaryInput.id; 472 var primaryId = transform.primaryInput.id;
462 473
463 // Build the new path, placing back any suffixes that we stripped earlier. 474 // Build the new path, placing back any suffixes that we stripped earlier.
464 var prefix = (firstBinding == -1) ? id.path 475 var prefix = (firstBinding == -1) ? id.path
465 : id.path.substring(0, id.path.length - placeholder.length); 476 : id.path.substring(0, id.path.length - placeholder.length);
466 var suffix = (firstBinding == -1) ? '' : href.substring(firstBinding); 477 var suffix = (firstBinding == -1) ? '' : href.substring(firstBinding);
467 var newPath = '$prefix$suffix'; 478 var newPath = '$prefix$suffix';
468 479
469 if (newPath.startsWith('lib/')) { 480 if (newPath.startsWith('lib/')) {
470 return '${topLevelPath}packages/${id.package}/${newPath.substring(4)}'; 481 return '${topLevelPath}packages/${id.package}/${newPath.substring(4)}';
471 } 482 }
472 483
473 if (newPath.startsWith('asset/')) { 484 if (newPath.startsWith('asset/')) {
474 return '${topLevelPath}assets/${id.package}/${newPath.substring(6)}'; 485 return '${topLevelPath}assets/${id.package}/${newPath.substring(6)}';
475 } 486 }
476 487
477 if (primaryId.package != id.package) { 488 if (primaryId.package != id.package) {
478 // Techincally we shouldn't get there 489 // Techincally we shouldn't get there
479 transform.logger.error("don't know how to include $id from $primaryId", 490 logger.error("don't know how to include $id from $primaryId", span: span);
480 span: span);
481 return href; 491 return href;
482 } 492 }
483 493
484 var builder = path.url; 494 var builder = path.url;
485 return builder.relative(builder.join('/', newPath), 495 return builder.relative(builder.join('/', newPath),
486 from: builder.join('/', builder.dirname(primaryId.path))); 496 from: builder.join('/', builder.dirname(primaryId.path)));
487 } 497 }
488 } 498 }
489 499
490 /// HTML attributes that expect a URL value. 500 /// HTML attributes that expect a URL value.
(...skipping 21 matching lines...) Expand all
512 /// style tag except these ones. 522 /// style tag except these ones.
513 const IGNORED_LINKED_STYLE_ATTRS = 523 const IGNORED_LINKED_STYLE_ATTRS =
514 const ['charset', 'href', 'href-lang', 'rel', 'rev']; 524 const ['charset', 'href', 'href-lang', 'rel', 'rev'];
515 525
516 /// Global RegExp objects. 526 /// Global RegExp objects.
517 final _INVALID_LIB_CHARS_REGEX = new RegExp('[^a-z0-9_]'); 527 final _INVALID_LIB_CHARS_REGEX = new RegExp('[^a-z0-9_]');
518 final _NUM_REGEX = new RegExp('[0-9]'); 528 final _NUM_REGEX = new RegExp('[0-9]');
519 final _BINDING_REGEX = new RegExp(r'(({{.*}})|(\[\[.*\]\]))'); 529 final _BINDING_REGEX = new RegExp(r'(({{.*}})|(\[\[.*\]\]))');
520 530
521 _getSpan(SourceFile file, AstNode node) => file.span(node.offset, node.end); 531 _getSpan(SourceFile file, AstNode node) => file.span(node.offset, node.end);
OLDNEW
« no previous file with comments | « pkg/polymer/lib/src/build/common.dart ('k') | pkg/polymer/lib/src/build/linter.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698