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

Unified Diff: lib/src/emitters.dart

Issue 22962005: Merge pull request #581 from kevmoo/polymer (Closed) Base URL: https://github.com/dart-lang/web-ui.git@polymer
Patch Set: Created 7 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « lib/src/dart_parser.dart ('k') | lib/src/files.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/src/emitters.dart
diff --git a/lib/src/emitters.dart b/lib/src/emitters.dart
index f43ac046489d3c8a256e3a4b415bcec89baec5f4..d02476575c4b6fbd66e64f71faf3a80339de6fce 100644
--- a/lib/src/emitters.dart
+++ b/lib/src/emitters.dart
@@ -5,256 +5,53 @@
/** Collects several code emitters for the template tool. */
library emitters;
-import 'package:csslib/parser.dart' as css;
-import 'package:csslib/visitor.dart';
import 'package:html5lib/dom.dart';
-import 'package:html5lib/dom_parsing.dart';
-import 'package:html5lib/parser.dart';
-import 'package:source_maps/span.dart' show Span, FileLocation;
+import 'package:html5lib/dom_parsing.dart' show TreeVisitor;
+import 'package:html5lib/parser.dart' show parseFragment;
+import 'package:source_maps/printer.dart';
+import 'package:source_maps/refactor.dart';
-import 'code_printer.dart';
-import 'compiler.dart';
-import 'dart_parser.dart' show DartCodeInfo;
+import 'compiler_options.dart';
+import 'css_emitters.dart' show emitStyleSheet, emitOriginalCss;
import 'html5_utils.dart';
-import 'html_css_fixup.dart';
-import 'info.dart';
+import 'info.dart' show ComponentInfo, FileInfo, GlobalInfo;
import 'messages.dart';
-import 'compiler_options.dart';
-import 'paths.dart';
-import 'refactor.dart';
-import 'utils.dart';
-
-
-/** Only x-tag name element selectors are emitted as [is="x-"]. */
-class CssEmitter extends CssPrinter {
- final Set _componentsTag;
-
- CssEmitter(this._componentsTag);
-
- /**
- * If element selector is a component's tag name, then change selector to
- * find element who's is attribute's the component's name.
- */
- bool _emitComponentElement(ElementSelector node) {
- if (_componentsTag.contains(node.name)) {
- emit('[is="${node.name}"]');
- return true;
- }
- return false;
- }
-
- void visitElementSelector(ElementSelector node) {
- if (_componentsTag.isNotEmpty && _emitComponentElement(node)) return;
- super.visitElementSelector(node);
- }
-
-}
-
-/**
- * Style sheet polyfill for a component, each CSS class name/id selector
- * referenced (selector) is prepended with prefix_ (if prefix is non-null). In
- * addition an element selector this is a component's tag name is transformed
- * to the attribute selector [is="x-"] where x- is the component's tag name.
- */
-class ComponentCssEmitter extends CssPrinter {
- final String _componentTagName;
- final CssPolyfillKind _polyfillKind;
- bool _inHostDirective = false;
- bool _selectorStartInHostDirective = false;
-
- ComponentCssEmitter(this._componentTagName, this._polyfillKind);
-
- /** Is the element selector an x-tag name. */
- bool _isSelectorElementXTag(Selector node) {
- if (node.simpleSelectorSequences.length > 0) {
- var selector = node.simpleSelectorSequences[0].simpleSelector;
- return selector is ElementSelector && selector.name == _componentTagName;
- }
- return false;
- }
-
- /**
- * If element selector is the component's tag name, then change selector to
- * find element who's is attribute is the component's name.
- */
- bool _emitComponentElement(ElementSelector node) {
- if (_polyfillKind == CssPolyfillKind.SCOPED_POLYFILL &&
- _componentTagName == node.name) {
- emit('[is="$_componentTagName"]');
- return true;
- }
- return false;
- }
-
- void visitSelector(Selector node) {
- // If the selector starts with an x-tag name don't emit it twice.
- if (!_isSelectorElementXTag(node) &&
- _polyfillKind == CssPolyfillKind.SCOPED_POLYFILL) {
- if (_inHostDirective) {
- // Style the element that's hosting the component, therefore don't emit
- // the descendent combinator (first space after the [is="x-..."]).
- emit('[is="$_componentTagName"]');
- // Signal that first simpleSelector must be checked.
- _selectorStartInHostDirective = true;
- } else {
- // Emit its scoped as a descendent (space at end).
- emit('[is="$_componentTagName"] ');
- }
- }
- super.visitSelector(node);
- }
-
- /**
- * If first simple selector of a ruleset in a @host directive is a wildcard
- * then don't emit the wildcard.
- */
- void visitSimpleSelectorSequence(SimpleSelectorSequence node) {
- if (_selectorStartInHostDirective) {
- _selectorStartInHostDirective = false;
- if (_polyfillKind == CssPolyfillKind.SCOPED_POLYFILL &&
- node.simpleSelector.isWildcard) {
- // Skip the wildcard if first item in the sequence.
- return;
- }
- assert(node.isCombinatorNone);
- }
-
- super.visitSimpleSelectorSequence(node);
- }
-
- void visitClassSelector(ClassSelector node) {
- if (_polyfillKind == CssPolyfillKind.MANGLED_POLYFILL) {
- emit('.${_componentTagName}_${node.name}');
- } else {
- super.visitClassSelector(node);
- }
- }
-
- void visitIdSelector(IdSelector node) {
- if (_polyfillKind == CssPolyfillKind.MANGLED_POLYFILL) {
- emit('#${_componentTagName}_${node.name}');
- } else {
- super.visitIdSelector(node);
- }
- }
-
- void visitElementSelector(ElementSelector node) {
- if (_emitComponentElement(node)) return;
- super.visitElementSelector(node);
- }
-
- /**
- * If we're polyfilling scoped styles the @host directive is stripped. Any
- * ruleset(s) processed in an @host will fixup the first selector. See
- * visitSelector and visitSimpleSelectorSequence in this class, they adjust
- * the selectors so it styles the element hosting the compopnent.
- */
- void visitHostDirective(HostDirective node) {
- if (_polyfillKind == CssPolyfillKind.SCOPED_POLYFILL) {
- _inHostDirective = true;
- emit('/* @host */');
- for (var ruleset in node.rulesets) {
- ruleset.visit(this);
- }
- _inHostDirective = false;
- emit('/* end of @host */\n');
- } else {
- super.visitHostDirective(node);
- }
- }
-}
-
-/**
- * Helper function to emit the contents of the style tag outside of a component.
- */
-String emitStyleSheet(StyleSheet ss, FileInfo file) =>
- (new CssEmitter(file.components.keys.toSet())
- ..visitTree(ss, pretty: true)).toString();
-
-/** Helper function to emit a component's style tag content. */
-String emitComponentStyleSheet(StyleSheet ss, String tagName,
- CssPolyfillKind polyfillKind) =>
- (new ComponentCssEmitter(tagName, polyfillKind)
- ..visitTree(ss, pretty: true)).toString();
+import 'paths.dart' show PathMapper;
+import 'utils.dart' show escapeDartString, path;
/** Generates the class corresponding to a single web component. */
-class WebComponentEmitter {
- final Messages messages;
- final FileInfo _fileInfo;
- final CssPolyfillKind cssPolyfillKind;
-
- WebComponentEmitter(this._fileInfo, this.messages, this.cssPolyfillKind);
-
- CodePrinter run(ComponentInfo info, PathMapper pathMapper,
- TextEditTransaction transaction) {
- var templateNode = info.element.nodes.firstWhere(
- (n) => n.tagName == 'template', orElse: () => null);
-
- if (templateNode != null) {
- if (!info.styleSheets.isEmpty && !messages.options.processCss) {
- // TODO(terry): Only one style tag per component.
-
- // TODO(jmesserly): csslib + html5lib should work together.
- // We shouldn't need to call a different function to serialize CSS.
- // Calling innerHtml on a StyleElement should be enought - like a real
- // browser. CSSOM and DOM should work together in the same tree.
- var styleText = emitComponentStyleSheet(
- info.styleSheets[0], info.tagName, cssPolyfillKind);
-
- templateNode.insertBefore(
- new Element.html('<style>\n$styleText\n</style>'),
- templateNode.hasChildNodes() ? templateNode.children[0] : null);
- }
- }
-
- bool hasExtends = info.extendsComponent != null;
- var codeInfo = info.userCode;
- var classDecl = info.classDeclaration;
- if (classDecl == null) return null;
-
- if (transaction == null) {
- transaction = new TextEditTransaction(codeInfo.code, codeInfo.sourceFile);
- }
-
- // Expand the headers to include polymer imports, unless they are already
- // present.
- var libraryName = (codeInfo.libraryName != null)
- ? codeInfo.libraryName
- : info.tagName.replaceAll(new RegExp('[-./]'), '_');
- var header = new CodePrinter(0);
- header.add(_header(path.basename(info.declaringFile.inputUrl.resolvedPath),
- libraryName));
- emitImports(codeInfo, info, pathMapper, header);
- header.addLine('');
- transaction.edit(0, codeInfo.directivesEnd, header);
-
- var classBody = new CodePrinter(1)
- ..add('\n')
- ..addLine('/** Original code from the component. */');
-
- var pos = classDecl.leftBracket.end;
- transaction.edit(pos, pos, classBody);
-
- // Emit all the code in a single printer, keeping track of source-maps.
- return transaction.commit();
- }
+NestedPrinter emitPolymerElement(ComponentInfo info, PathMapper pathMapper,
+ TextEditTransaction transaction, CompilerOptions options) {
+ if (info.classDeclaration == null) return null;
+
+ var codeInfo = info.userCode;
+ if (transaction == null) {
+ // TODO(sigmund): avoid emitting this file if we don't need to do any
+ // modifications (e.g. no @observable and not adding the libraryName).
+ transaction = new TextEditTransaction(codeInfo.code, codeInfo.sourceFile);
+ }
+ if (codeInfo.libraryName == null) {
+ // For deploy, we need to import the library associated with the component,
+ // so we need to ensure there is a library directive.
+ var libraryName = info.tagName.replaceAll(new RegExp('[-./]'), '_');
+ transaction.edit(0, 0, 'library $libraryName;');
+ }
+ return transaction.commit();
}
/** The code that will be used to bootstrap the application. */
-CodePrinter generateBootstrapCode(
+NestedPrinter generateBootstrapCode(
FileInfo info, FileInfo userMainInfo, GlobalInfo global,
PathMapper pathMapper, CompilerOptions options) {
- var printer = new CodePrinter(0)
+ var printer = new NestedPrinter(0)
..addLine('library app_bootstrap;')
..addLine('')
..addLine("import 'package:polymer/polymer.dart';")
..addLine("import 'dart:mirrors' show currentMirrorSystem;");
-
if (userMainInfo.userCode != null) {
- printer..addLine('')
- ..addLine("import '${pathMapper.importUrlFor(info, userMainInfo)}' "
- "as userMain;\n");
+ printer..addLine("import '${pathMapper.importUrlFor(info, userMainInfo)}' "
+ "as userMain;\n");
}
int i = 0;
@@ -272,9 +69,6 @@ CodePrinter generateBootstrapCode(
for (var c in global.components.values) {
if (c.hasConflict) continue;
- var tagName = escapeDartString(c.tagName);
- var cssMapExpression = createCssSelectorsExpression(c,
- CssPolyfillKind.of(options, c));
printer.addLine("'${pathMapper.importUrlFor(info, c)}',");
}
@@ -291,22 +85,15 @@ CodePrinter generateBootstrapCode(
}
-
/**
- * List of HTML4 elements which could have relative URL resource:
- *
- * <body background=url>, <img src=url>, <input src=url>
- *
- * HTML 5:
- *
- * <audio src=url>, <command icon=url>, <embed src=url>,
- * <source src=url>, <video poster=url> and <video src=url>
+ * Rewrites attributes that contain relative URL (excluding src urls in script
+ * and link tags which are already rewritten by other parts of the compiler).
*/
-class AttributeUrlTransform extends TreeVisitor {
+class _AttributeUrlTransform extends TreeVisitor {
final String filePath;
final PathMapper pathMapper;
- AttributeUrlTransform(this.filePath, this.pathMapper);
+ _AttributeUrlTransform(this.filePath, this.pathMapper);
visitElement(Element node) {
if (node.tagName == 'script') return;
@@ -314,60 +101,16 @@ class AttributeUrlTransform extends TreeVisitor {
for (var key in node.attributes.keys) {
if (urlAttributes.contains(key)) {
- // Rewrite the URL attribute.
- node.attributes[key] = pathMapper.transformUrl(filePath,
- node.attributes[key]);
+ node.attributes[key] =
+ pathMapper.transformUrl(filePath, node.attributes[key]);
}
}
-
super.visitElement(node);
}
}
-void _transformRelativeUrlAttributes(Document document, PathMapper pathMapper,
- String filePath) {
- // Transform any element's attribute which is a relative URL.
- new AttributeUrlTransform(filePath, pathMapper).visit(document);
-}
-
-void emitImports(DartCodeInfo codeInfo, LibraryInfo info, PathMapper pathMapper,
- CodePrinter printer, [GlobalInfo global]) {
- var seenImports = new Set();
- addUnique(String importString, [location]) {
- if (!seenImports.contains(importString)) {
- printer.addLine(importString, location: location);
- seenImports.add(importString);
- }
- }
-
- // Add imports only for those components used by this component.
- for (var c in info.usedComponents.keys) {
- addUnique("import '${pathMapper.importUrlFor(info, c)}';");
- }
-
- if (global != null) {
- for (var c in global.components.values) {
- addUnique("import '${pathMapper.importUrlFor(info, c)}';");
- }
- }
-
- if (info is ComponentInfo) {
- // Inject an import to the base component.
- var base = (info as ComponentInfo).extendsComponent;
- if (base != null) {
- addUnique("import '${pathMapper.importUrlFor(info, base)}';");
- }
- }
-
- // Add existing import, export, and part directives.
- var file = codeInfo.sourceFile;
- for (var d in codeInfo.directives) {
- addUnique(d.toString(), file != null ? file.location(d.offset) : null);
- }
-}
-
-final shadowDomJS = new RegExp(r'shadowdom\..*\.js', caseSensitive: false);
-final bootJS = new RegExp(r'.*/polymer/boot.js', caseSensitive: false);
+final _shadowDomJS = new RegExp(r'shadowdom\..*\.js', caseSensitive: false);
+final _bootJS = new RegExp(r'.*/polymer/boot.js', caseSensitive: false);
/** Trim down the html for the main html page. */
void transformMainHtml(Document document, FileInfo fileInfo,
@@ -383,14 +126,14 @@ void transformMainHtml(Document document, FileInfo fileInfo,
var last = src.split('/').last;
if (last == 'dart.js' || last == 'testing.js') {
dartLoaderFound = true;
- } else if (shadowDomJS.hasMatch(last)) {
+ } else if (_shadowDomJS.hasMatch(last)) {
shadowDomFound = true;
}
}
if (tag.attributes['type'] == 'application/dart') {
tag.remove();
} else if (src != null) {
- if (bootJS.hasMatch(src)) {
+ if (_bootJS.hasMatch(src)) {
tag.remove();
} else if (rewriteUrls) {
tag.attributes["src"] = pathMapper.transformUrl(filePath, src);
@@ -411,7 +154,7 @@ void transformMainHtml(Document document, FileInfo fileInfo,
if (rewriteUrls) {
// Transform any element's attribute which is a relative URL.
- _transformRelativeUrlAttributes(document, pathMapper, filePath);
+ new _AttributeUrlTransform(filePath, pathMapper).visit(document);
}
if (hasCss) {
@@ -471,7 +214,8 @@ void transformMainHtml(Document document, FileInfo fileInfo,
// Insert the "auto-generated" comment after the doctype, otherwise IE will
// go into quirks mode.
int commentIndex = 0;
- DocumentType doctype = find(document.nodes, (n) => n is DocumentType);
+ DocumentType doctype =
+ document.nodes.firstWhere((n) => n is DocumentType, orElse: () => null);
if (doctype != null) {
commentIndex = document.nodes.indexOf(doctype) + 1;
// TODO(jmesserly): the html5lib parser emits a warning for missing
@@ -489,18 +233,3 @@ void transformMainHtml(Document document, FileInfo fileInfo,
document.nodes.insert(commentIndex, parseFragment(
'\n<!-- This file was auto-generated from $filePath. -->\n'));
}
-
-/** Header with common imports, used in every generated .dart file. */
-String _header(String filename, String libraryName) {
- var lib = libraryName != null ? '\nlibrary $libraryName;\n' : '';
- return """
-// Auto-generated from $filename.
-// DO NOT EDIT.
-$lib
-import 'dart:html' as autogenerated;
-import 'dart:svg' as autogenerated_svg;
-import 'package:mdv/mdv.dart' as autogenerated_mdv;
-import 'package:observe/observe.dart' as __observe;
-import 'package:polymer/polymer.dart' as autogenerated;
-""";
-}
« no previous file with comments | « lib/src/dart_parser.dart ('k') | lib/src/files.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698