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

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

Issue 433913002: Strip bindings before calling Uri.parse (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: made new path building a bit more efficient 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 | « no previous file | pkg/polymer/test/build/import_inliner_test.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
(...skipping 250 matching lines...) Expand 10 before | Expand all | Expand 10 after
261 /// 261 ///
262 /// myPkgName|lib/foo/bar.html -> myPkgName.foo.bar_html 262 /// myPkgName|lib/foo/bar.html -> myPkgName.foo.bar_html
263 /// myPkgName|web/foo/bar.html -> myPkgName.web.foo.bar_html 263 /// myPkgName|web/foo/bar.html -> myPkgName.web.foo.bar_html
264 /// 264 ///
265 /// This should roughly match the recommended library name conventions. 265 /// This should roughly match the recommended library name conventions.
266 String _libraryNameFor(AssetId id, int suffix) { 266 String _libraryNameFor(AssetId id, int suffix) {
267 var name = '${path.withoutExtension(id.path)}_' 267 var name = '${path.withoutExtension(id.path)}_'
268 '${path.extension(id.path).substring(1)}'; 268 '${path.extension(id.path).substring(1)}';
269 if (name.startsWith('lib/')) name = name.substring(4); 269 if (name.startsWith('lib/')) name = name.substring(4);
270 name = name.split('/').map((part) { 270 name = name.split('/').map((part) {
271 part = part.replaceAll(INVALID_LIB_CHARS_REGEX, '_'); 271 part = part.replaceAll(_INVALID_LIB_CHARS_REGEX, '_');
272 if (part.startsWith(NUM_REGEX)) part = '_${part}'; 272 if (part.startsWith(_NUM_REGEX)) part = '_${part}';
273 return part; 273 return part;
274 }).join("."); 274 }).join(".");
275 return '${id.package}.${name}_$suffix'; 275 return '${id.package}.${name}_$suffix';
276 } 276 }
277 277
278 /// Parse [code] and determine whether it has a library directive. 278 /// Parse [code] and determine whether it has a library directive.
279 bool _hasLibraryDirective(String code) => 279 bool _hasLibraryDirective(String code) =>
280 parseDirectives(code, suppressErrors: true) 280 parseDirectives(code, suppressErrors: true)
281 .directives.any((d) => d is LibraryDirective); 281 .directives.any((d) => d is LibraryDirective);
282 282
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
336 return changed; 336 return changed;
337 } 337 }
338 338
339 visitElement(Element node) { 339 visitElement(Element node) {
340 // TODO(jakemac): Support custom elements that extend html elements which 340 // TODO(jakemac): Support custom elements that extend html elements which
341 // have url-like attributes. This probably means keeping a list of which 341 // have url-like attributes. This probably means keeping a list of which
342 // html elements support each url-like attribute. 342 // html elements support each url-like attribute.
343 if (!isCustomTagName(node.localName)) { 343 if (!isCustomTagName(node.localName)) {
344 node.attributes.forEach((name, value) { 344 node.attributes.forEach((name, value) {
345 if (_urlAttributes.contains(name)) { 345 if (_urlAttributes.contains(name)) {
346 if (!name.startsWith('_') && value.contains(_BINDINGS)) { 346 if (!name.startsWith('_') && value.contains(_BINDING_REGEX)) {
347 transform.logger.warning( 347 transform.logger.warning(
348 'When using bindings with the "$name" attribute you may ' 348 'When using bindings with the "$name" attribute you may '
349 'experience errors in certain browsers. Please use the ' 349 'experience errors in certain browsers. Please use the '
350 '"_$name" attribute instead. For more information, see ' 350 '"_$name" attribute instead. For more information, see '
351 'http://goo.gl/5av8cU', span: node.sourceSpan, asset: sourceId); 351 'http://goo.gl/5av8cU', span: node.sourceSpan, asset: sourceId);
352 } else if (name.startsWith('_') && !value.contains(_BINDINGS)) { 352 } else if (name.startsWith('_') && !value.contains(_BINDING_REGEX)) {
353 transform.logger.warning( 353 transform.logger.warning(
354 'The "$name" attribute is only supported when using bindings. ' 354 'The "$name" attribute is only supported when using bindings. '
355 'Please change to the "${name.substring(1)}" attribute.', 355 'Please change to the "${name.substring(1)}" attribute.',
356 span: node.sourceSpan, asset: sourceId); 356 span: node.sourceSpan, asset: sourceId);
357 } 357 }
358 if (value != '' && !value.trim().startsWith(_BINDINGS)) { 358 if (value != '' && !value.trim().startsWith(_BINDING_REGEX)) {
359 node.attributes[name] = _newUrl(value, node.sourceSpan); 359 node.attributes[name] = _newUrl(value, node.sourceSpan);
360 changed = changed || value != node.attributes[name]; 360 changed = changed || value != node.attributes[name];
361 } 361 }
362 } 362 }
363 }); 363 });
364 } 364 }
365 if (node.localName == 'style') { 365 if (node.localName == 'style') {
366 node.text = visitCss(node.text); 366 node.text = visitCss(node.text);
367 changed = true; 367 changed = true;
368 } else if (node.localName == 'script' && 368 } else if (node.localName == 'script' &&
369 node.attributes['type'] == TYPE_DART && 369 node.attributes['type'] == TYPE_DART &&
370 !node.attributes.containsKey('src')) { 370 !node.attributes.containsKey('src')) {
371 // TODO(jmesserly): we might need to visit JS too to handle ES Harmony 371 // TODO(jmesserly): we might need to visit JS too to handle ES Harmony
372 // modules. 372 // modules.
373 node.text = visitInlineDart(node.text); 373 node.text = visitInlineDart(node.text);
374 changed = true; 374 changed = true;
375 } 375 }
376 return super.visitElement(node); 376 return super.visitElement(node);
377 } 377 }
378 378
379 static final _URL = new RegExp(r'url\(([^)]*)\)', multiLine: true); 379 static final _URL = new RegExp(r'url\(([^)]*)\)', multiLine: true);
380 static final _QUOTE = new RegExp('["\']', multiLine: true); 380 static final _QUOTE = new RegExp('["\']', multiLine: true);
381 static final _BINDINGS = new RegExp(r'({{)|(\[\[)');
382 381
383 /// Visit the CSS text and replace any relative URLs so we can inline it. 382 /// Visit the CSS text and replace any relative URLs so we can inline it.
384 // Ported from: 383 // Ported from:
385 // https://github.com/Polymer/vulcanize/blob/c14f63696797cda18dc3d372b78aa3378 acc691f/lib/vulcan.js#L149 384 // https://github.com/Polymer/vulcanize/blob/c14f63696797cda18dc3d372b78aa3378 acc691f/lib/vulcan.js#L149
386 // TODO(jmesserly): use csslib here instead? Parsing with RegEx is sadness. 385 // TODO(jmesserly): use csslib here instead? Parsing with RegEx is sadness.
387 // Maybe it's reliable enough for finding URLs in CSS? I'm not sure. 386 // Maybe it's reliable enough for finding URLs in CSS? I'm not sure.
388 String visitCss(String cssText) { 387 String visitCss(String cssText) {
389 var url = spanUrlFor(sourceId, transform); 388 var url = spanUrlFor(sourceId, transform);
390 var src = new SourceFile(cssText, url: url); 389 var src = new SourceFile(cssText, url: url);
391 return cssText.replaceAllMapped(_URL, (match) { 390 return cssText.replaceAllMapped(_URL, (match) {
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
428 } 427 }
429 428
430 if (!output.hasEdits) return code; 429 if (!output.hasEdits) return code;
431 430
432 // TODO(sigmund): emit source maps when barback supports it (see 431 // TODO(sigmund): emit source maps when barback supports it (see
433 // dartbug.com/12340) 432 // dartbug.com/12340)
434 return (output.commit()..build(file.url.toString())).text; 433 return (output.commit()..build(file.url.toString())).text;
435 } 434 }
436 435
437 String _newUrl(String href, SourceSpan span) { 436 String _newUrl(String href, SourceSpan span) {
438 // Uri.parse blows up on invalid characters (like {{). Encoding the uri 437 // We only want to parse the part of the href leading up to the first
439 // allows it to be parsed, which does the correct thing in the general case. 438 // binding, anything after that is not informative.
440 // This uri not used to build the new uri, so it never needs to be decoded. 439 var hrefToParse;
441 var uri = Uri.parse(Uri.encodeFull(href)); 440 var firstBinding = href.indexOf(_BINDING_REGEX);
441 if (firstBinding == -1) {
442 hrefToParse = href;
443 } else if (firstBinding == 0) {
444 return href;
445 } else {
446 hrefToParse = '${href.substring(0, firstBinding)}$_BINDING_PLACEHOLDER';
447 }
448
449 var uri = Uri.parse(hrefToParse);
442 if (uri.isAbsolute) return href; 450 if (uri.isAbsolute) return href;
443 if (!uri.scheme.isEmpty) return href; 451 if (!uri.scheme.isEmpty) return href;
444 if (!uri.host.isEmpty) return href; 452 if (!uri.host.isEmpty) return href;
445 if (uri.path.isEmpty) return href; // Implies standalone ? or # in URI. 453 if (uri.path.isEmpty) return href; // Implies standalone ? or # in URI.
446 if (path.isAbsolute(href)) return href; 454 if (path.isAbsolute(href)) return href;
447 455
448 var id = uriToAssetId(sourceId, href, transform.logger, span); 456 var id = uriToAssetId(sourceId, hrefToParse, transform.logger, span);
449 if (id == null) return href; 457 if (id == null) return href;
450 var primaryId = transform.primaryInput.id; 458 var primaryId = transform.primaryInput.id;
451 459
452 if (id.path.startsWith('lib/')) { 460 // Build the new path, placing back any suffixes that we stripped earlier.
453 return '${topLevelPath}packages/${id.package}/${id.path.substring(4)}'; 461 var prefix = (firstBinding < 1) ? id.path
Siggi Cherem (dart-lang) 2014/08/04 19:39:38 nit: since we return automatically with == 0 upfro
jakemac 2014/08/04 19:55:01 Done, I think it is a bit better
462 : id.path.substring(0, id.path.length - _BINDING_PLACEHOLDER.length);
463 var suffix = (firstBinding < 1) ? '' : href.substring(firstBinding);
464 var newPath = '$prefix$suffix';
465
466 if (newPath.startsWith('lib/')) {
467 return '${topLevelPath}packages/${id.package}/${newPath.substring(4)}';
454 } 468 }
455 469
456 if (id.path.startsWith('asset/')) { 470 if (newPath.startsWith('asset/')) {
457 return '${topLevelPath}assets/${id.package}/${id.path.substring(6)}'; 471 return '${topLevelPath}assets/${id.package}/${newPath.substring(6)}';
458 } 472 }
459 473
460 if (primaryId.package != id.package) { 474 if (primaryId.package != id.package) {
461 // Techincally we shouldn't get there 475 // Techincally we shouldn't get there
462 transform.logger.error("don't know how to include $id from $primaryId", 476 transform.logger.error("don't know how to include $id from $primaryId",
463 span: span); 477 span: span);
464 return href; 478 return href;
465 } 479 }
466 480
467 var builder = path.url; 481 var builder = path.url;
468 return builder.relative(builder.join('/', id.path), 482 return builder.relative(builder.join('/', newPath),
469 from: builder.join('/', builder.dirname(primaryId.path))); 483 from: builder.join('/', builder.dirname(primaryId.path)));
470 } 484 }
471 } 485 }
472 486
473 /// HTML attributes that expect a URL value. 487 /// HTML attributes that expect a URL value.
474 /// <http://dev.w3.org/html5/spec/section-index.html#attributes-1> 488 /// <http://dev.w3.org/html5/spec/section-index.html#attributes-1>
475 /// 489 ///
476 /// Every one of these attributes is a URL in every context where it is used in 490 /// Every one of these attributes is a URL in every context where it is used in
477 /// the DOM. The comments show every DOM element where an attribute can be used. 491 /// the DOM. The comments show every DOM element where an attribute can be used.
478 /// 492 ///
(...skipping 10 matching lines...) Expand all
489 'poster', '_poster', // in video 503 'poster', '_poster', // in video
490 'src', '_src', // in audio, embed, iframe, img, input, script, 504 'src', '_src', // in audio, embed, iframe, img, input, script,
491 // source, track,video 505 // source, track,video
492 ]; 506 ];
493 507
494 /// When inlining <link rel="stylesheet"> tags copy over all attributes to the 508 /// When inlining <link rel="stylesheet"> tags copy over all attributes to the
495 /// style tag except these ones. 509 /// style tag except these ones.
496 const IGNORED_LINKED_STYLE_ATTRS = 510 const IGNORED_LINKED_STYLE_ATTRS =
497 const ['charset', 'href', 'href-lang', 'rel', 'rev']; 511 const ['charset', 'href', 'href-lang', 'rel', 'rev'];
498 512
499 /// Global RegExp objects for validating generated library names. 513 /// Global RegExp objects.
500 final INVALID_LIB_CHARS_REGEX = new RegExp('[^a-z0-9_]'); 514 final _INVALID_LIB_CHARS_REGEX = new RegExp('[^a-z0-9_]');
501 final NUM_REGEX = new RegExp('[0-9]'); 515 final _NUM_REGEX = new RegExp('[0-9]');
516 final _BINDING_REGEX = new RegExp(r'(({{.*}})|(\[\[.*\]\]))');
517
518 /// Placeholder for bindings in urls for [Uri.parse].
519 final _BINDING_PLACEHOLDER = '_';
Siggi Cherem (dart-lang) 2014/08/04 19:39:38 nit: maybe rename or rephrase the comment a big ("
jakemac 2014/08/04 19:55:01 Done, went with the const local
502 520
503 _getSpan(SourceFile file, AstNode node) => file.span(node.offset, node.end); 521 _getSpan(SourceFile file, AstNode node) => file.span(node.offset, node.end);
OLDNEW
« no previous file with comments | « no previous file | pkg/polymer/test/build/import_inliner_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698