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

Unified Diff: pkg/polymer/lib/src/build/script_compactor.dart

Issue 151893003: Use a transformer to initialize custom elements and call initMethod. This is the (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 11 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 | « pkg/polymer/lib/src/build/common.dart ('k') | pkg/polymer/lib/src/loader.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/polymer/lib/src/build/script_compactor.dart
diff --git a/pkg/polymer/lib/src/build/script_compactor.dart b/pkg/polymer/lib/src/build/script_compactor.dart
index e869a2cb55c253bf7ffcc6f26bde5078241dcec8..a9a4d595cba2c24af36233bdf5ebc9dd0aa9be22 100644
--- a/pkg/polymer/lib/src/build/script_compactor.dart
+++ b/pkg/polymer/lib/src/build/script_compactor.dart
@@ -8,9 +8,15 @@ library polymer.src.build.script_compactor;
import 'dart:async';
import 'dart:convert';
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/error.dart';
+import 'package:analyzer/src/generated/java_core.dart' show CharSequence;
+import 'package:analyzer/src/generated/parser.dart';
+import 'package:analyzer/src/generated/scanner.dart';
import 'package:barback/barback.dart';
import 'package:html5lib/parser.dart' show parseFragment;
import 'package:path/path.dart' as path;
+import 'package:source_maps/span.dart' show SourceFile;
import 'code_extractor.dart'; // import just for documentation.
import 'common.dart';
@@ -77,6 +83,7 @@ class ScriptCompactor extends Transformer with PolymerTransformer {
return null;
}
+ // Emit the bootstrap .dart file
var bootstrapId = id.addExtension('_bootstrap.dart');
mainScriptTag.attributes['src'] =
path.url.basename(bootstrapId.path);
@@ -92,20 +99,187 @@ class ScriptCompactor extends Transformer with PolymerTransformer {
buffer..write('\n')
..writeln('void main() {')
- ..writeln(' configureForDeployment([')
- ..writeAll(urls.map((url) => " '$url',\n"))
- ..writeln(' ]);')
- ..writeln(' i${i - 1}.main();')
- ..writeln('}');
-
- transform.addOutput(new Asset.fromString(
- bootstrapId, buffer.toString()));
- transform.addOutput(new Asset.fromString(id, document.outerHtml));
+ ..writeln(' configureForDeployment([');
+
+ // Inject @CustomTag and @initMethod initializations for each library
+ // that is sourced in a script tag.
+ i = 0;
+ return Future.forEach(libraries, (lib) {
+ return _initializersOf(lib, transform, logger).then((initializers) {
+ for (var init in initializers) {
+ var code = init.asCode('i$i');
+ buffer.write(" $code,\n");
+ }
+ i++;
+ });
+ }).then((_) {
+ buffer..writeln(' ]);')
+ ..writeln(' i${urls.length - 1}.main();')
+ ..writeln('}');
+
+ transform.addOutput(new Asset.fromString(
+ bootstrapId, buffer.toString()));
+ transform.addOutput(new Asset.fromString(id, document.outerHtml));
+ });
});
});
}
+
+ /**
+ * Computes the initializers of [dartLibrary]. That is, a closure that calls
+ * Polymer.register for each @CustomTag, and any public top-level methods
+ * labeled with @initMethod.
+ */
+ Future<List<_Initializer>> _initializersOf(
+ AssetId dartLibrary, Transform transform, TransformLogger logger) {
+ var initializers = [];
+ return transform.readInputAsString(dartLibrary).then((code) {
+ var file = new SourceFile.text(_simpleUriForSource(dartLibrary), code);
+ var unit = _parseCompilationUnit(code);
+
+ return Future.forEach(unit.directives, (directive) {
+ // Include anything from parts.
+ if (directive is PartDirective) {
+ var targetId = resolve(dartLibrary, directive.uri.stringValue,
+ logger, _getSpan(file, directive));
+ return _initializersOf(targetId, transform, logger)
+ .then(initializers.addAll);
+ }
+
+ // Similarly, include anything from exports except what's filtered by
+ // the show/hide combinators.
+ if (directive is ExportDirective) {
+ var targetId = resolve(dartLibrary, directive.uri.stringValue,
+ logger, _getSpan(file, directive));
+ return _initializersOf(targetId, transform, logger)
+ .then((r) => _processExportDirective(directive, r, initializers));
+ }
+ }).then((_) {
+ // Scan the code for classes and top-level functions.
+ for (var node in unit.declarations) {
+ if (node is ClassDeclaration) {
+ _processClassDeclaration(node, initializers, file, logger);
+ } else if (node is FunctionDeclaration &&
+ node.metadata.any(_isInitMethodAnnotation)) {
+ _processFunctionDeclaration(node, initializers, file, logger);
+ }
+ }
+ return initializers;
+ });
+ });
+ }
+
+ static String _simpleUriForSource(AssetId source) =>
+ source.path.startsWith('lib/')
+ ? 'package:${source.package}/${source.path.substring(4)}' : source.path;
+
+ /**
+ * Filter [exportedInitializers] according to [directive]'s show/hide
+ * combinators and add the result to [initializers].
+ */
+ // TODO(sigmund): call the analyzer's resolver instead?
+ static _processExportDirective(ExportDirective directive,
+ List<_Initializer> exportedInitializers,
+ List<_Initializer> initializers) {
+ for (var combinator in directive.combinators) {
+ if (combinator is ShowCombinator) {
+ var show = combinator.shownNames.map((n) => n.name).toSet();
+ exportedInitializers.retainWhere((e) => show.contains(e.symbolName));
+ } else if (combinator is HideCombinator) {
+ var hide = combinator.hiddenNames.map((n) => n.name).toSet();
+ exportedInitializers.removeWhere((e) => hide.contains(e.symbolName));
+ }
+ }
+ initializers.addAll(exportedInitializers);
+ }
+
+ /**
+ * Add an initializer to register [node] as a polymer element if it contains
+ * an appropriate [CustomTag] annotation.
+ */
+ static _processClassDeclaration(ClassDeclaration node,
+ List<_Initializer> initializers, SourceFile file,
+ TransformLogger logger) {
+ for (var meta in node.metadata) {
+ if (!_isCustomTagAnnotation(meta)) continue;
+ var args = meta.arguments.arguments;
+ if (args == null || args.length == 0) {
+ logger.error('Missing argument in @CustomTag annotation',
+ span: _getSpan(file, meta));
+ continue;
+ }
+
+ var tagName = args[0].stringValue;
+ var typeName = node.name.name;
+ if (typeName.startsWith('_')) {
+ logger.error('@CustomTag is no longer supported on private '
+ 'classes: $tagName', span: _getSpan(file, node.name));
+ continue;
+ }
+ initializers.add(new _CustomTagInitializer(tagName, typeName));
+ }
+ }
+
+ /** a method initializer for [function]. */
+ static _processFunctionDeclaration(FunctionDeclaration function,
+ List<_Initializer> initializers, SourceFile file,
+ TransformLogger logger) {
+ var name = function.name.name;
+ if (name.startsWith('_')) {
+ logger.error('@initMethod is no longer supported on private '
+ 'functions: $name', span: _getSpan(file, function.name));
+ return;
+ }
+ initializers.add(new _InitMethodInitializer(name));
+ }
+}
+
+/** Parse [code] using analyzer. */
+CompilationUnit _parseCompilationUnit(String code) {
+ var errorListener = new _ErrorCollector();
+ var reader = new CharSequenceReader(new CharSequence(code));
+ var scanner = new Scanner(null, reader, errorListener);
+ var token = scanner.tokenize();
+ var parser = new Parser(null, errorListener);
+ return parser.parseCompilationUnit(token);
}
+class _ErrorCollector extends AnalysisErrorListener {
+ final errors = <AnalysisError>[];
+ onError(error) => errors.add(error);
+}
+
+// TODO(sigmund): consider support for importing annotations with prefixes.
+bool _isInitMethodAnnotation(Annotation node) =>
+ node.name.name == 'initMethod' && node.constructorName == null &&
+ node.arguments == null;
+bool _isCustomTagAnnotation(Annotation node) => node.name.name == 'CustomTag';
+
+abstract class _Initializer {
+ String get symbolName;
+ String asCode(String prefix);
+}
+
+class _InitMethodInitializer implements _Initializer {
+ String methodName;
+ String get symbolName => methodName;
+ _InitMethodInitializer(this.methodName);
+
+ String asCode(String prefix) => "$prefix.$methodName";
+}
+
+class _CustomTagInitializer implements _Initializer {
+ String tagName;
+ String typeName;
+ String get symbolName => typeName;
+ _CustomTagInitializer(this.tagName, this.typeName);
+
+ String asCode(String prefix) =>
+ "() => Polymer.register('$tagName', $prefix.$typeName)";
+}
+
+_getSpan(SourceFile file, ASTNode node) => file.span(node.offset, node.end);
+
const MAIN_HEADER = """
library app_bootstrap;
« no previous file with comments | « pkg/polymer/lib/src/build/common.dart ('k') | pkg/polymer/lib/src/loader.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698