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

Side by Side 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, 10 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/loader.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 combines multiple dart script tags into a single one. */ 5 /** Transfomer that combines multiple dart script tags into a single one. */
6 library polymer.src.build.script_compactor; 6 library polymer.src.build.script_compactor;
7 7
8 import 'dart:async'; 8 import 'dart:async';
9 import 'dart:convert'; 9 import 'dart:convert';
10 10
11 import 'package:analyzer/src/generated/ast.dart';
12 import 'package:analyzer/src/generated/error.dart';
13 import 'package:analyzer/src/generated/java_core.dart' show CharSequence;
14 import 'package:analyzer/src/generated/parser.dart';
15 import 'package:analyzer/src/generated/scanner.dart';
11 import 'package:barback/barback.dart'; 16 import 'package:barback/barback.dart';
12 import 'package:html5lib/parser.dart' show parseFragment; 17 import 'package:html5lib/parser.dart' show parseFragment;
13 import 'package:path/path.dart' as path; 18 import 'package:path/path.dart' as path;
19 import 'package:source_maps/span.dart' show SourceFile;
14 20
15 import 'code_extractor.dart'; // import just for documentation. 21 import 'code_extractor.dart'; // import just for documentation.
16 import 'common.dart'; 22 import 'common.dart';
17 23
18 /** 24 /**
19 * Combines Dart script tags into a single script tag, and creates a new Dart 25 * Combines Dart script tags into a single script tag, and creates a new Dart
20 * file that calls the main function of each of the original script tags. 26 * file that calls the main function of each of the original script tags.
21 * 27 *
22 * This transformer assumes that all script tags point to external files. To 28 * This transformer assumes that all script tags point to external files. To
23 * support script tags with inlined code, use this transformer after running 29 * support script tags with inlined code, use this transformer after running
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
70 mainLibraryId = resolve(id, src, logger, tag.sourceSpan); 76 mainLibraryId = resolve(id, src, logger, tag.sourceSpan);
71 mainScriptTag = tag; 77 mainScriptTag = tag;
72 } 78 }
73 79
74 if (mainScriptTag == null) { 80 if (mainScriptTag == null) {
75 // We didn't find any main library, nothing to do. 81 // We didn't find any main library, nothing to do.
76 transform.addOutput(transform.primaryInput); 82 transform.addOutput(transform.primaryInput);
77 return null; 83 return null;
78 } 84 }
79 85
86 // Emit the bootstrap .dart file
80 var bootstrapId = id.addExtension('_bootstrap.dart'); 87 var bootstrapId = id.addExtension('_bootstrap.dart');
81 mainScriptTag.attributes['src'] = 88 mainScriptTag.attributes['src'] =
82 path.url.basename(bootstrapId.path); 89 path.url.basename(bootstrapId.path);
83 90
84 libraries.add(mainLibraryId); 91 libraries.add(mainLibraryId);
85 var urls = libraries.map((id) => assetUrlFor(id, bootstrapId, logger)) 92 var urls = libraries.map((id) => assetUrlFor(id, bootstrapId, logger))
86 .where((url) => url != null).toList(); 93 .where((url) => url != null).toList();
87 var buffer = new StringBuffer()..writeln(MAIN_HEADER); 94 var buffer = new StringBuffer()..writeln(MAIN_HEADER);
88 int i = 0; 95 int i = 0;
89 for (; i < urls.length; i++) { 96 for (; i < urls.length; i++) {
90 buffer.writeln("import '${urls[i]}' as i$i;"); 97 buffer.writeln("import '${urls[i]}' as i$i;");
91 } 98 }
92 99
93 buffer..write('\n') 100 buffer..write('\n')
94 ..writeln('void main() {') 101 ..writeln('void main() {')
95 ..writeln(' configureForDeployment([') 102 ..writeln(' configureForDeployment([');
96 ..writeAll(urls.map((url) => " '$url',\n"))
97 ..writeln(' ]);')
98 ..writeln(' i${i - 1}.main();')
99 ..writeln('}');
100 103
101 transform.addOutput(new Asset.fromString( 104 // Inject @CustomTag and @initMethod initializations for each library
102 bootstrapId, buffer.toString())); 105 // that is sourced in a script tag.
103 transform.addOutput(new Asset.fromString(id, document.outerHtml)); 106 i = 0;
107 return Future.forEach(libraries, (lib) {
108 return _initializersOf(lib, transform, logger).then((initializers) {
109 for (var init in initializers) {
110 var code = init.asCode('i$i');
111 buffer.write(" $code,\n");
112 }
113 i++;
114 });
115 }).then((_) {
116 buffer..writeln(' ]);')
117 ..writeln(' i${urls.length - 1}.main();')
118 ..writeln('}');
119
120 transform.addOutput(new Asset.fromString(
121 bootstrapId, buffer.toString()));
122 transform.addOutput(new Asset.fromString(id, document.outerHtml));
123 });
104 }); 124 });
105 }); 125 });
106 } 126 }
127
128 /**
129 * Computes the initializers of [dartLibrary]. That is, a closure that calls
130 * Polymer.register for each @CustomTag, and any public top-level methods
131 * labeled with @initMethod.
132 */
133 Future<List<_Initializer>> _initializersOf(
134 AssetId dartLibrary, Transform transform, TransformLogger logger) {
135 var initializers = [];
136 return transform.readInputAsString(dartLibrary).then((code) {
137 var file = new SourceFile.text(_simpleUriForSource(dartLibrary), code);
138 var unit = _parseCompilationUnit(code);
139
140 return Future.forEach(unit.directives, (directive) {
141 // Include anything from parts.
142 if (directive is PartDirective) {
143 var targetId = resolve(dartLibrary, directive.uri.stringValue,
144 logger, _getSpan(file, directive));
145 return _initializersOf(targetId, transform, logger)
146 .then(initializers.addAll);
147 }
148
149 // Similarly, include anything from exports except what's filtered by
150 // the show/hide combinators.
151 if (directive is ExportDirective) {
152 var targetId = resolve(dartLibrary, directive.uri.stringValue,
153 logger, _getSpan(file, directive));
154 return _initializersOf(targetId, transform, logger)
155 .then((r) => _processExportDirective(directive, r, initializers));
156 }
157 }).then((_) {
158 // Scan the code for classes and top-level functions.
159 for (var node in unit.declarations) {
160 if (node is ClassDeclaration) {
161 _processClassDeclaration(node, initializers, file, logger);
162 } else if (node is FunctionDeclaration &&
163 node.metadata.any(_isInitMethodAnnotation)) {
164 _processFunctionDeclaration(node, initializers, file, logger);
165 }
166 }
167 return initializers;
168 });
169 });
170 }
171
172 static String _simpleUriForSource(AssetId source) =>
173 source.path.startsWith('lib/')
174 ? 'package:${source.package}/${source.path.substring(4)}' : source.path;
175
176 /**
177 * Filter [exportedInitializers] according to [directive]'s show/hide
178 * combinators and add the result to [initializers].
179 */
180 // TODO(sigmund): call the analyzer's resolver instead?
181 static _processExportDirective(ExportDirective directive,
182 List<_Initializer> exportedInitializers,
183 List<_Initializer> initializers) {
184 for (var combinator in directive.combinators) {
185 if (combinator is ShowCombinator) {
186 var show = combinator.shownNames.map((n) => n.name).toSet();
187 exportedInitializers.retainWhere((e) => show.contains(e.symbolName));
188 } else if (combinator is HideCombinator) {
189 var hide = combinator.hiddenNames.map((n) => n.name).toSet();
190 exportedInitializers.removeWhere((e) => hide.contains(e.symbolName));
191 }
192 }
193 initializers.addAll(exportedInitializers);
194 }
195
196 /**
197 * Add an initializer to register [node] as a polymer element if it contains
198 * an appropriate [CustomTag] annotation.
199 */
200 static _processClassDeclaration(ClassDeclaration node,
201 List<_Initializer> initializers, SourceFile file,
202 TransformLogger logger) {
203 for (var meta in node.metadata) {
204 if (!_isCustomTagAnnotation(meta)) continue;
205 var args = meta.arguments.arguments;
206 if (args == null || args.length == 0) {
207 logger.error('Missing argument in @CustomTag annotation',
208 span: _getSpan(file, meta));
209 continue;
210 }
211
212 var tagName = args[0].stringValue;
213 var typeName = node.name.name;
214 if (typeName.startsWith('_')) {
215 logger.error('@CustomTag is no longer supported on private '
216 'classes: $tagName', span: _getSpan(file, node.name));
217 continue;
218 }
219 initializers.add(new _CustomTagInitializer(tagName, typeName));
220 }
221 }
222
223 /** a method initializer for [function]. */
224 static _processFunctionDeclaration(FunctionDeclaration function,
225 List<_Initializer> initializers, SourceFile file,
226 TransformLogger logger) {
227 var name = function.name.name;
228 if (name.startsWith('_')) {
229 logger.error('@initMethod is no longer supported on private '
230 'functions: $name', span: _getSpan(file, function.name));
231 return;
232 }
233 initializers.add(new _InitMethodInitializer(name));
234 }
107 } 235 }
108 236
237 /** Parse [code] using analyzer. */
238 CompilationUnit _parseCompilationUnit(String code) {
239 var errorListener = new _ErrorCollector();
240 var reader = new CharSequenceReader(new CharSequence(code));
241 var scanner = new Scanner(null, reader, errorListener);
242 var token = scanner.tokenize();
243 var parser = new Parser(null, errorListener);
244 return parser.parseCompilationUnit(token);
245 }
246
247 class _ErrorCollector extends AnalysisErrorListener {
248 final errors = <AnalysisError>[];
249 onError(error) => errors.add(error);
250 }
251
252 // TODO(sigmund): consider support for importing annotations with prefixes.
253 bool _isInitMethodAnnotation(Annotation node) =>
254 node.name.name == 'initMethod' && node.constructorName == null &&
255 node.arguments == null;
256 bool _isCustomTagAnnotation(Annotation node) => node.name.name == 'CustomTag';
257
258 abstract class _Initializer {
259 String get symbolName;
260 String asCode(String prefix);
261 }
262
263 class _InitMethodInitializer implements _Initializer {
264 String methodName;
265 String get symbolName => methodName;
266 _InitMethodInitializer(this.methodName);
267
268 String asCode(String prefix) => "$prefix.$methodName";
269 }
270
271 class _CustomTagInitializer implements _Initializer {
272 String tagName;
273 String typeName;
274 String get symbolName => typeName;
275 _CustomTagInitializer(this.tagName, this.typeName);
276
277 String asCode(String prefix) =>
278 "() => Polymer.register('$tagName', $prefix.$typeName)";
279 }
280
281 _getSpan(SourceFile file, ASTNode node) => file.span(node.offset, node.end);
282
109 const MAIN_HEADER = """ 283 const MAIN_HEADER = """
110 library app_bootstrap; 284 library app_bootstrap;
111 285
112 import 'package:polymer/polymer.dart'; 286 import 'package:polymer/polymer.dart';
113 """; 287 """;
OLDNEW
« 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