| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 /** Collects several code emitters for the template tool. */ | 5 /** Collects several code emitters for the template tool. */ |
| 6 // TODO(sigmund): add visitor that applies all emitters on a component | 6 // TODO(sigmund): add visitor that applies all emitters on a component |
| 7 // TODO(sigmund): add support for conditionals, so context is changed at that | 7 // TODO(sigmund): add support for conditionals, so context is changed at that |
| 8 // point. | 8 // point. |
| 9 library emitters; | 9 library emitters; |
| 10 | 10 |
| 11 import 'package:html5lib/dom.dart'; | 11 import 'package:html5lib/dom.dart'; |
| 12 import 'package:html5lib/dom_parsing.dart'; | 12 import 'package:html5lib/dom_parsing.dart'; |
| 13 | 13 |
| 14 import 'code_printer.dart'; | 14 import 'code_printer.dart'; |
| 15 import 'codegen.dart' as codegen; | 15 import 'codegen.dart' as codegen; |
| 16 import 'messages.dart'; | 16 import 'file_system/path.dart'; |
| 17 import 'files.dart'; | 17 import 'files.dart'; |
| 18 import 'info.dart'; | 18 import 'info.dart'; |
| 19 import 'messages.dart'; |
| 19 | 20 |
| 20 /** | 21 /** |
| 21 * An emitter for a web component feature. It collects all the logic for | 22 * An emitter for a web component feature. It collects all the logic for |
| 22 * emitting a particular feature (such as data-binding, event hookup) with | 23 * emitting a particular feature (such as data-binding, event hookup) with |
| 23 * respect to a single HTML element. | 24 * respect to a single HTML element. |
| 24 */ | 25 */ |
| 25 abstract class Emitter<T extends ElementInfo> { | 26 abstract class Emitter<T extends ElementInfo> { |
| 26 /** Element for which code is being emitted. */ | 27 /** Element for which code is being emitted. */ |
| 27 Element elem; | 28 Element elem; |
| 28 | 29 |
| (...skipping 491 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 520 super.visitElement(elem); | 521 super.visitElement(elem); |
| 521 | 522 |
| 522 _context = oldContext; | 523 _context = oldContext; |
| 523 } | 524 } |
| 524 } | 525 } |
| 525 | 526 |
| 526 /** Generates the class corresponding to a single web component. */ | 527 /** Generates the class corresponding to a single web component. */ |
| 527 class WebComponentEmitter extends RecursiveEmitter { | 528 class WebComponentEmitter extends RecursiveEmitter { |
| 528 WebComponentEmitter(FileInfo info) : super(info); | 529 WebComponentEmitter(FileInfo info) : super(info); |
| 529 | 530 |
| 530 String run(ComponentInfo info) { | 531 String run(ComponentInfo info, PathInfo pathInfo) { |
| 531 // If this derives from another component, ensure the lifecycle methods are | 532 // If this derives from another component, ensure the lifecycle methods are |
| 532 // called in the superclass. | 533 // called in the superclass. |
| 533 if (info.extendsComponent != null) { | 534 if (info.extendsComponent != null) { |
| 534 _context.createdMethod.add('super.created_autogenerated();'); | 535 _context.createdMethod.add('super.created_autogenerated();'); |
| 535 _context.insertedMethod.add('super.inserted_autogenerated();'); | 536 _context.insertedMethod.add('super.inserted_autogenerated();'); |
| 536 _context.removedMethod.add('super.removed_autogenerated();'); | 537 _context.removedMethod.add('super.removed_autogenerated();'); |
| 537 } | 538 } |
| 538 | 539 |
| 539 if (info.element.attributes['apply-author-styles'] != null) { | 540 if (info.element.attributes['apply-author-styles'] != null) { |
| 540 _context.createdMethod.add('if (_root is autogenerated.ShadowRoot) ' | 541 _context.createdMethod.add('if (_root is autogenerated.ShadowRoot) ' |
| (...skipping 13 matching lines...) Expand all Loading... |
| 554 // We have a similar issue in mainDartCode. | 555 // We have a similar issue in mainDartCode. |
| 555 _context.createdMethod.add(""" | 556 _context.createdMethod.add(""" |
| 556 _root.innerHTML = r''' | 557 _root.innerHTML = r''' |
| 557 ${info.template.innerHTML.trim()} | 558 ${info.template.innerHTML.trim()} |
| 558 '''; | 559 '''; |
| 559 """); | 560 """); |
| 560 } | 561 } |
| 561 | 562 |
| 562 visit(info.element); | 563 visit(info.element); |
| 563 | 564 |
| 564 var code = info.userCode; | 565 bool hasExtends = info.extendsComponent != null; |
| 565 if (code == null) { | 566 var codeInfo = info.userCode; |
| 566 if (info.extendsComponent != null) { | 567 if (codeInfo == null) { |
| 567 var superclass = info.extendsComponent.constructor; | 568 var superclass = hasExtends ? info.extendsComponent.constructor |
| 568 // TODO(jmesserly): should we add this import even if you have your own | 569 : 'autogenerated.WebComponent'; |
| 569 // script tag? | 570 var imports = hasExtends ? [] : [new DartDirectiveInfo('import', |
| 570 var relativePath = PathInfo.relativePath(info, info.extendsComponent); | 571 'package:web_components/web_component.dart', 'autogenerated')]; |
| 571 code = ''' | 572 codeInfo = new DartCodeInfo(null, null, imports, |
| 572 ${codegen.importList([relativePath])} | 573 'class ${info.constructor} extends $superclass {\n}'); |
| 573 class ${info.constructor} extends $superclass {\n} | |
| 574 '''; | |
| 575 } else { | |
| 576 code = ''' | |
| 577 import 'package:web_components/web_component.dart' as autogenerated; | |
| 578 class ${info.constructor} extends autogenerated.WebComponent {\n} | |
| 579 '''; | |
| 580 } | |
| 581 } | 574 } |
| 575 |
| 576 var code = codeInfo.code; |
| 582 var match = new RegExp('class ${info.constructor}[^{]*{').firstMatch(code); | 577 var match = new RegExp('class ${info.constructor}[^{]*{').firstMatch(code); |
| 583 if (match != null) { | 578 if (match != null) { |
| 584 // TODO(sigmund): clean up and make this more robust. Issue #59. | |
| 585 var printer = new CodePrinter(); | 579 var printer = new CodePrinter(); |
| 586 var libMatch = const RegExp('^library .*;').firstMatch(code); | 580 var libraryName = (codeInfo.libraryName != null) |
| 587 int startPos = 0; | 581 ? codeInfo.libraryName |
| 588 if (libMatch == null) { | 582 : info.tagName.replaceAll(const RegExp('[-./]'), '_'); |
| 589 var libraryName = info.tagName.replaceAll(const RegExp('[-./]'), '_'); | 583 printer.add(codegen.header(info.declaringFile.path, libraryName)); |
| 590 printer.add(codegen.header(info.declaringFile.path, libraryName)); | 584 |
| 591 } else { | 585 // Add exisitng import, export, and part directives. |
| 592 printer.add('// Generated from ${info.inputPath.filename}\n' | 586 for (var directive in codeInfo.directives) { |
| 593 '// DO NOT EDIT.'); | 587 printer.add(codegen.directiveText(directive, info, pathInfo)); |
| 594 printer.add(code.substring(0, libMatch.end())); | |
| 595 printer.add(codegen.imports); | |
| 596 startPos = libMatch.end(); | |
| 597 } | 588 } |
| 598 // Import only those components used by this component. | 589 |
| 590 // Add imports only for those components used by this component. |
| 599 var imports = info.usedComponents.getKeys().map( | 591 var imports = info.usedComponents.getKeys().map( |
| 600 (c) => PathInfo.relativePath(info, c)); | 592 (c) => PathInfo.relativePath(info, c)); |
| 593 |
| 594 if (hasExtends) { |
| 595 // Inject an import to the base component. |
| 596 printer.add(codegen.importList( |
| 597 [PathInfo.relativePath(info, info.extendsComponent)])); |
| 598 } |
| 599 |
| 601 printer.add(codegen.importList(imports)) | 600 printer.add(codegen.importList(imports)) |
| 602 .add(code.substring(startPos, match.end())) | 601 .add(code.substring(0, match.end())) |
| 603 .add('\n') | 602 .add('\n') |
| 604 .add(codegen.componentCode(info.constructor, | 603 .add(codegen.componentCode(info.constructor, |
| 605 _context.declarations.formatString(1), | 604 _context.declarations.formatString(1), |
| 606 _context.createdMethod.formatString(2), | 605 _context.createdMethod.formatString(2), |
| 607 _context.insertedMethod.formatString(2), | 606 _context.insertedMethod.formatString(2), |
| 608 _context.removedMethod.formatString(2))) | 607 _context.removedMethod.formatString(2))) |
| 609 .add(code.substring(match.end())); | 608 .add(code.substring(match.end())); |
| 610 return printer.formatString(); | 609 return printer.formatString(); |
| 611 } else { | 610 } else { |
| 612 messages.error('please provide a class definition ' | 611 messages.error('please provide a class definition ' |
| 613 'for ${info.constructor}:\n $code', info.element.span, | 612 'for ${info.constructor}:\n $code', info.element.span, |
| 614 file: info.inputPath); | 613 file: info.inputPath); |
| 615 return code; | 614 return ''; |
| 616 } | 615 } |
| 617 } | 616 } |
| 618 } | 617 } |
| 619 | 618 |
| 620 /** Generates the class corresponding to the main html page. */ | 619 /** Generates the class corresponding to the main html page. */ |
| 621 class MainPageEmitter extends RecursiveEmitter { | 620 class MainPageEmitter extends RecursiveEmitter { |
| 622 MainPageEmitter(FileInfo info) : super(info); | 621 MainPageEmitter(FileInfo info) : super(info); |
| 623 | 622 |
| 624 String run(Document document) { | 623 String run(Document document, PathInfo pathInfo) { |
| 625 // The body of an element tag will not be part of the main HTML page. Each | 624 // The body of an element tag will not be part of the main HTML page. Each |
| 626 // element will be generated programatically as a dart web component by | 625 // element will be generated programatically as a dart web component by |
| 627 // [WebComponentEmitter] above. | 626 // [WebComponentEmitter] above. |
| 628 document.queryAll('element').forEach((tag) => tag.remove()); | 627 document.queryAll('element').forEach((tag) => tag.remove()); |
| 628 |
| 629 // fix up the URLs to content that is not modified by the compiler |
| 630 document.queryAll('script').forEach((tag) { |
| 631 var src = tag.attributes["src"]; |
| 632 if (tag.attributes['type'] != 'application/dart' && src != null) { |
| 633 tag.attributes["src"] = pathInfo.transferDirectiveUrl(_info, src); |
| 634 } |
| 635 }); |
| 636 document.queryAll('link').forEach((tag) { |
| 637 var href = tag.attributes['href']; |
| 638 if (tag.attributes['rel'] != 'components' && href != null) { |
| 639 tag.attributes['href'] = pathInfo.transferDirectiveUrl(_info, href); |
| 640 } |
| 641 }); |
| 642 |
| 629 visit(document); | 643 visit(document); |
| 630 | 644 |
| 631 var printer = new CodePrinter(); | 645 var printer = new CodePrinter(); |
| 632 var startPos = 0; | |
| 633 | 646 |
| 634 // Inject library name if not pressent. | 647 // Inject library name if not pressent. |
| 635 // TODO(sigmund): consider parsing the top-level syntax of a dart file | 648 var codeInfo = _info.userCode; |
| 636 // instead of this ad-hoc regex (issue #95). | 649 var libraryName = codeInfo.libraryName != null |
| 637 var code = _info.userCode; | 650 ? codeInfo.libraryName : _info.libraryName; |
| 638 var match = const RegExp('^library .*;').firstMatch(code); | 651 printer.add(codegen.header(_info.path, libraryName)); |
| 639 if (match == null) { | 652 |
| 640 printer.add(codegen.header(_info.path, _info.libraryName)); | 653 // Add exisitng import, export, and part directives. |
| 641 } else { | 654 for (var directive in codeInfo.directives) { |
| 642 printer.add('// Generated from ${_info.inputPath}\n// DO NOT EDIT.') | 655 printer.add(codegen.directiveText(directive, _info, pathInfo)); |
| 643 .add(code.substring(0, match.end())) | |
| 644 .add(codegen.imports); | |
| 645 startPos = match.end(); | |
| 646 } | 656 } |
| 647 | 657 |
| 648 // Import only those components used by the page. | 658 // Import only those components used by the page. |
| 649 var imports = _info.usedComponents.getKeys().map( | 659 var imports = _info.usedComponents.getKeys().map( |
| 650 (c) => PathInfo.relativePath(_info, c)); | 660 (c) => PathInfo.relativePath(_info, c)); |
| 651 printer.add(codegen.importList(imports)) | 661 printer.add(codegen.importList(imports)) |
| 652 .add(codegen.mainDartCode(code.substring(startPos), | 662 .add(codegen.mainDartCode(codeInfo.code, |
| 653 _context.declarations.formatString(0), | 663 _context.declarations.formatString(0), |
| 654 _context.createdMethod.formatString(1), | 664 _context.createdMethod.formatString(1), |
| 655 _context.insertedMethod.formatString(1), | 665 _context.insertedMethod.formatString(1), |
| 656 document.body.innerHTML.trim())); | 666 document.body.innerHTML.trim())); |
| 657 return printer.formatString(); | 667 return printer.formatString(); |
| 658 } | 668 } |
| 659 } | 669 } |
| OLD | NEW |