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

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
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 });
124 });
125 });
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 url = dartLibrary.path.startsWith('lib/')
Jennifer Messerly 2014/02/03 19:09:32 refactor this into a function?
Siggi Cherem (dart-lang) 2014/02/04 00:20:35 Done.
138 ? 'package:${dartLibrary.package}/${dartLibrary.path.substring(4)}'
139 : dartLibrary.path;
140 var file = new SourceFile.text(url, code);
141 var unit = _parseCompilationUnit(code);
142
143 return Future.forEach(unit.directives, (directive) {
144 // Include anything from parts.
145 if (directive is PartDirective) {
146 var targetId = resolve(dartLibrary, directive.uri.stringValue,
147 logger, _getSpan(file, directive), allowPackageColonUrls: true);
148 return _initializersOf(targetId, transform, logger)
149 .then(initializers.addAll);
150 }
151
152 // Similarly, include anything from exports except what's filtered by
153 // the show/hide combinators.
Jennifer Messerly 2014/02/03 19:09:32 hmm. This seems to be reimplementing top level nam
Siggi Cherem (dart-lang) 2014/02/04 00:20:35 I hear you - I've added a TODO. I'm afraid of usin
154 if (directive is ExportDirective) {
155 var targetId = resolve(dartLibrary, directive.uri.stringValue,
Jennifer Messerly 2014/02/03 19:09:32 idea: pull this out into a separate method? e.g. _
Siggi Cherem (dart-lang) 2014/02/04 00:20:35 Funny, I thought of it too, but wasn't sure it was
156 logger, _getSpan(file, directive), allowPackageColonUrls: true);
157 return _initializersOf(targetId, transform, logger).then((result) {
158 for (var combinator in directive.combinators) {
159 if (combinator is ShowCombinator) {
160 var show = combinator.shownNames.map((n) => n.name).toSet();
161 result.retainWhere((e) => show.contains(e.symbolName));
162 } else if (combinator is HideCombinator) {
163 var hide = combinator.hiddenNames.map((n) => n.name).toSet();
164 result.removeWhere((e) => hide.contains(e.symbolName));
165 }
166 }
167 initializers.addAll(result);
168 });
169 }
170 }).then((_) {
171 // Scan the code for classes and top-level functions.
172 for (var node in unit.declarations) {
Jennifer Messerly 2014/02/03 19:09:32 is it possible for the library to conflict with an
Siggi Cherem (dart-lang) 2014/02/04 00:20:35 yeah, I think that would be caught as a Dart error
173 if (node is ClassDeclaration) {
174 for (var meta in node.metadata) {
175 if (!_isCustomTagAnnotation(meta)) continue;
176 var args = meta.arguments.arguments;
177 if (args == null || args.length == 0) {
178 logger.error('Missing argument in @CustomTag annotation',
179 span: _getSpan(file, meta));
180 continue;
181 }
182
183 var tagName = args[0].stringValue;
184 var typeName = node.name.name;
185 if (typeName.startsWith('_')) {
186 logger.error('@CustomTag is no longer supported on private '
187 'classes: $tagName', span: _getSpan(file, node.name));
188 continue;
189 }
190 initializers.add(new _CustomTagInitializer(tagName, typeName));
191 }
192 } else if (node is FunctionDeclaration &&
193 node.metadata.any(_isInitMethodAnnotation)) {
194 var methodName = node.name.name;
195 if (methodName.startsWith('_')) {
196 logger.error('@initMethod is no longer supported on private '
197 'functions: $methodName', span: _getSpan(file, node.name));
198 continue;
199 }
200 initializers.add(new _InitMethodInitializer(methodName));
201 }
202 }
203 return initializers;
104 }); 204 });
105 }); 205 });
106 } 206 }
107 } 207 }
108 208
209 /** Parse [code] using analyzer. */
210 CompilationUnit _parseCompilationUnit(String code) {
211 var errorListener = new _ErrorCollector();
212 var reader = new CharSequenceReader(new CharSequence(code));
213 var scanner = new Scanner(null, reader, errorListener);
214 var token = scanner.tokenize();
215 var parser = new Parser(null, errorListener);
216 return parser.parseCompilationUnit(token);
217 }
218
219 class _ErrorCollector extends AnalysisErrorListener {
220 final errors = <AnalysisError>[];
221 onError(error) => errors.add(error);
222 }
223
224 // TODO(sigmund): consider support for importing annotations with prefixes.
225 bool _isInitMethodAnnotation(Annotation node) =>
226 node.name.name == 'initMethod' && node.constructorName == null &&
227 node.arguments == null;
228 bool _isCustomTagAnnotation(Annotation node) => node.name.name == 'CustomTag';
229
230 abstract class _Initializer {
231 String get symbolName;
232 String asCode(String prefix);
233 }
234
235 class _InitMethodInitializer implements _Initializer {
236 String methodName;
237 String get symbolName => methodName;
238 _InitMethodInitializer(this.methodName);
239
240 String asCode(String prefix) => "$prefix.$methodName";
241 }
242
243 class _CustomTagInitializer implements _Initializer {
244 String tagName;
245 String typeName;
246 String get symbolName => typeName;
247 _CustomTagInitializer(this.tagName, this.typeName);
248
249 String asCode(String prefix) =>
250 "() => Polymer.register('$tagName', $prefix.$typeName)";
251 }
252
253 _getSpan(SourceFile file, ASTNode node) => file.span(node.offset, node.end);
254
109 const MAIN_HEADER = """ 255 const MAIN_HEADER = """
110 library app_bootstrap; 256 library app_bootstrap;
111 257
112 import 'package:polymer/polymer.dart'; 258 import 'package:polymer/polymer.dart';
113 """; 259 """;
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698