OLD | NEW |
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:html5lib/dom.dart' show Document, Element, Text; | 11 import 'package:html5lib/dom.dart' show Document, Element, Text; |
12 import 'package:html5lib/dom_parsing.dart'; | 12 import 'package:html5lib/dom_parsing.dart'; |
13 import 'package:html5lib/parser.dart' show parseFragment; | 13 import 'package:html5lib/parser.dart' show parseFragment; |
14 import 'package:analyzer/src/generated/ast.dart'; | 14 import 'package:analyzer/src/generated/ast.dart'; |
15 import 'package:analyzer/src/generated/element.dart' hide Element; | 15 import 'package:analyzer/src/generated/element.dart' hide Element; |
16 import 'package:analyzer/src/generated/element.dart' as analyzer show Element; | 16 import 'package:analyzer/src/generated/element.dart' as analyzer show Element; |
17 import 'package:barback/barback.dart'; | 17 import 'package:barback/barback.dart'; |
| 18 import 'package:code_transformers/messages/build_logger.dart'; |
18 import 'package:path/path.dart' as path; | 19 import 'package:path/path.dart' as path; |
19 import 'package:source_span/source_span.dart'; | 20 import 'package:source_span/source_span.dart'; |
20 import 'package:smoke/codegen/generator.dart'; | 21 import 'package:smoke/codegen/generator.dart'; |
21 import 'package:smoke/codegen/recorder.dart'; | 22 import 'package:smoke/codegen/recorder.dart'; |
22 import 'package:code_transformers/resolver.dart'; | 23 import 'package:code_transformers/resolver.dart'; |
23 import 'package:code_transformers/src/dart_sdk.dart'; | 24 import 'package:code_transformers/src/dart_sdk.dart'; |
24 import 'package:template_binding/src/mustache_tokens.dart' show MustacheTokens; | 25 import 'package:template_binding/src/mustache_tokens.dart' show MustacheTokens; |
25 | 26 |
26 import 'package:polymer_expressions/expression.dart' as pe; | 27 import 'package:polymer_expressions/expression.dart' as pe; |
27 import 'package:polymer_expressions/parser.dart' as pe; | 28 import 'package:polymer_expressions/parser.dart' as pe; |
28 import 'package:polymer_expressions/visitor.dart' as pe; | 29 import 'package:polymer_expressions/visitor.dart' as pe; |
29 | 30 |
| 31 import 'common.dart'; |
30 import 'import_inliner.dart' show ImportInliner; // just for docs. | 32 import 'import_inliner.dart' show ImportInliner; // just for docs. |
31 import 'common.dart'; | 33 import 'messages.dart'; |
32 import 'wrapped_logger.dart'; | |
33 | 34 |
34 /// Combines Dart script tags into a single script tag, and creates a new Dart | 35 /// Combines Dart script tags into a single script tag, and creates a new Dart |
35 /// file that calls the main function of each of the original script tags. | 36 /// file that calls the main function of each of the original script tags. |
36 /// | 37 /// |
37 /// This transformer assumes that all script tags point to external files. To | 38 /// This transformer assumes that all script tags point to external files. To |
38 /// support script tags with inlined code, use this transformer after running | 39 /// support script tags with inlined code, use this transformer after running |
39 /// [ImportInliner] on an earlier phase. | 40 /// [ImportInliner] on an earlier phase. |
40 /// | 41 /// |
41 /// Internally, this transformer will convert each script tag into an import | 42 /// Internally, this transformer will convert each script tag into an import |
42 /// statement to a library, and then uses `initPolymer` (see polymer.dart) to | 43 /// statement to a library, and then uses `initPolymer` (see polymer.dart) to |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
101 } | 102 } |
102 | 103 |
103 Future apply(Transform transform) => | 104 Future apply(Transform transform) => |
104 new _ScriptCompactor(transform, options, resolvers).apply(); | 105 new _ScriptCompactor(transform, options, resolvers).apply(); |
105 } | 106 } |
106 | 107 |
107 /// Helper class mainly use to flatten the async code. | 108 /// Helper class mainly use to flatten the async code. |
108 class _ScriptCompactor extends PolymerTransformer { | 109 class _ScriptCompactor extends PolymerTransformer { |
109 final TransformOptions options; | 110 final TransformOptions options; |
110 final Transform transform; | 111 final Transform transform; |
111 final TransformLogger logger; | 112 final BuildLogger logger; |
112 final AssetId docId; | 113 final AssetId docId; |
113 final AssetId bootstrapId; | 114 final AssetId bootstrapId; |
114 | 115 |
115 /// HTML document parsed from [docId]. | 116 /// HTML document parsed from [docId]. |
116 Document document; | 117 Document document; |
117 | 118 |
118 /// List of ids for each Dart entry script tag (the main tag and any tag | 119 /// List of ids for each Dart entry script tag (the main tag and any tag |
119 /// included on each custom element definition). | 120 /// included on each custom element definition). |
120 List<AssetId> entryLibraries; | 121 List<AssetId> entryLibraries; |
121 | 122 |
(...skipping 17 matching lines...) Expand all Loading... |
139 Resolver resolver; | 140 Resolver resolver; |
140 | 141 |
141 /// Code generator used to create the static initialization for smoke. | 142 /// Code generator used to create the static initialization for smoke. |
142 final generator = new SmokeCodeGenerator(); | 143 final generator = new SmokeCodeGenerator(); |
143 | 144 |
144 _SubExpressionVisitor expressionVisitor; | 145 _SubExpressionVisitor expressionVisitor; |
145 | 146 |
146 _ScriptCompactor(Transform transform, options, this.resolvers) | 147 _ScriptCompactor(Transform transform, options, this.resolvers) |
147 : transform = transform, | 148 : transform = transform, |
148 options = options, | 149 options = options, |
149 logger = options.releaseMode ? transform.logger : | 150 logger = new BuildLogger( |
150 new WrappedLogger(transform, convertErrorsToWarnings: true), | 151 transform, convertErrorsToWarnings: !options.releaseMode), |
151 docId = transform.primaryInput.id, | 152 docId = transform.primaryInput.id, |
152 bootstrapId = transform.primaryInput.id.addExtension('_bootstrap.dart'); | 153 bootstrapId = transform.primaryInput.id.addExtension('_bootstrap.dart'); |
153 | 154 |
154 Future apply() => | 155 Future apply() => |
155 _loadDocument() | 156 _loadDocument() |
156 .then(_loadEntryLibraries) | 157 .then(_loadEntryLibraries) |
157 .then(_processHtml) | 158 .then(_processHtml) |
158 .then(_emitNewEntrypoint) | 159 .then(_emitNewEntrypoint) |
159 .then((_) { | 160 .then((_) { |
160 // Write out the logs collected by our [WrappedLogger]. | 161 // Write out the logs collected by our [BuildLogger]. |
161 if (options.injectBuildLogsInOutput && logger is WrappedLogger) { | 162 if (options.injectBuildLogsInOutput) return logger.writeOutput(); |
162 return (logger as WrappedLogger).writeOutput(); | |
163 } | |
164 }); | 163 }); |
165 | 164 |
166 /// Loads the primary input as an html document. | 165 /// Loads the primary input as an html document. |
167 Future _loadDocument() => | 166 Future _loadDocument() => |
168 readPrimaryAsHtml(transform).then((doc) { document = doc; }); | 167 readPrimaryAsHtml(transform, logger).then((doc) { document = doc; }); |
169 | 168 |
170 /// Populates [entryLibraries] as a list containing the asset ids of each | 169 /// Populates [entryLibraries] as a list containing the asset ids of each |
171 /// library loaded on a script tag. The actual work of computing this is done | 170 /// library loaded on a script tag. The actual work of computing this is done |
172 /// in an earlier phase and emited in the `entrypoint._data` asset. | 171 /// in an earlier phase and emited in the `entrypoint._data` asset. |
173 Future _loadEntryLibraries(_) => | 172 Future _loadEntryLibraries(_) => |
174 transform.readInputAsString(docId.addExtension('._data')).then((data) { | 173 transform.readInputAsString(docId.addExtension('._data')).then((data) { |
175 var map = JSON.decode(data); | 174 var map = JSON.decode(data); |
176 experimentalBootstrap = map['experimental_bootstrap']; | 175 experimentalBootstrap = map['experimental_bootstrap']; |
177 entryLibraries = map['script_ids'] | 176 entryLibraries = map['script_ids'] |
178 .map((id) => new AssetId.deserialize(id)) | 177 .map((id) => new AssetId.deserialize(id)) |
179 .toList(); | 178 .toList(); |
| 179 return Future.forEach(entryLibraries, logger.addLogFilesFromAsset); |
180 }); | 180 }); |
181 | 181 |
182 /// Removes unnecessary script tags, and identifies the main entry point Dart | 182 /// Removes unnecessary script tags, and identifies the main entry point Dart |
183 /// script tag (if any). | 183 /// script tag (if any). |
184 void _processHtml(_) { | 184 void _processHtml(_) { |
185 for (var tag in document.querySelectorAll('script')) { | 185 for (var tag in document.querySelectorAll('script')) { |
186 var src = tag.attributes['src']; | 186 var src = tag.attributes['src']; |
187 if (src == 'packages/polymer/boot.js') { | 187 if (src == 'packages/polymer/boot.js') { |
188 tag.remove(); | 188 tag.remove(); |
189 continue; | 189 continue; |
190 } | 190 } |
191 if (tag.attributes['type'] == 'application/dart') { | 191 if (tag.attributes['type'] == 'application/dart') { |
192 logger.warning('unexpected script. The ' | 192 logger.warning(INTERNAL_ERROR_UNEXPECTED_SCRIPT, span: tag.sourceSpan); |
193 'ScriptCompactor transformer should run after running the ' | |
194 'ImportInliner', span: tag.sourceSpan); | |
195 } | 193 } |
196 } | 194 } |
197 } | 195 } |
198 | 196 |
199 /// Emits the main HTML and Dart bootstrap code for the application. If there | 197 /// Emits the main HTML and Dart bootstrap code for the application. If there |
200 /// were not Dart entry point files, then this simply emits the original HTML. | 198 /// were not Dart entry point files, then this simply emits the original HTML. |
201 Future _emitNewEntrypoint(_) { | 199 Future _emitNewEntrypoint(_) { |
202 // If we don't find code, there is nothing to do. | 200 // If we don't find code, there is nothing to do. |
203 if (entryLibraries.isEmpty) return null; | 201 if (entryLibraries.isEmpty) return null; |
204 return _initResolver() | 202 return _initResolver() |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
280 // Check whether the class has a @CustomTag annotation. Typically we expect | 278 // Check whether the class has a @CustomTag annotation. Typically we expect |
281 // a single @CustomTag, but it's possible to have several. | 279 // a single @CustomTag, but it's possible to have several. |
282 var tagNames = []; | 280 var tagNames = []; |
283 for (var meta in cls.node.metadata) { | 281 for (var meta in cls.node.metadata) { |
284 var tagName = _extractTagName(meta, cls); | 282 var tagName = _extractTagName(meta, cls); |
285 if (tagName != null) tagNames.add(tagName); | 283 if (tagName != null) tagNames.add(tagName); |
286 } | 284 } |
287 | 285 |
288 if (cls.isPrivate && tagNames.isNotEmpty) { | 286 if (cls.isPrivate && tagNames.isNotEmpty) { |
289 var name = tagNames.first; | 287 var name = tagNames.first; |
290 logger.error('@CustomTag is not currently supported on private classes:' | 288 logger.error(PRIVATE_CUSTOM_TAG.create( |
291 ' $name. Consider making this class public, or create a ' | 289 {'name': name, 'class': cls.name}), |
292 'public initialization method marked with `@initMethod` that calls ' | |
293 '`Polymer.register($name, ${cls.name})`.', | |
294 span: _spanForNode(cls, cls.node.name)); | 290 span: _spanForNode(cls, cls.node.name)); |
295 return; | 291 return; |
296 } | 292 } |
297 | 293 |
298 // Include #registerCallback if it exists. Note that by default lookupMember | 294 // Include #registerCallback if it exists. Note that by default lookupMember |
299 // and query will also add the corresponding getters and setters. | 295 // and query will also add the corresponding getters and setters. |
300 recorder.lookupMember(cls, 'registerCallback'); | 296 recorder.lookupMember(cls, 'registerCallback'); |
301 | 297 |
302 // Include methods that end with *Changed. | 298 // Include methods that end with *Changed. |
303 recorder.runQuery(cls, new QueryOptions( | 299 recorder.runQuery(cls, new QueryOptions( |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
365 } | 361 } |
366 | 362 |
367 /// Extract the first argument of an annotation and validate that it's type is | 363 /// Extract the first argument of an annotation and validate that it's type is |
368 /// String. For instance, return "bar" from `@Foo("bar")`. | 364 /// String. For instance, return "bar" from `@Foo("bar")`. |
369 String _extractFirstAnnotationArgument(Annotation meta, String name, | 365 String _extractFirstAnnotationArgument(Annotation meta, String name, |
370 analyzer.Element context) { | 366 analyzer.Element context) { |
371 | 367 |
372 // Read argument from the AST | 368 // Read argument from the AST |
373 var args = meta.arguments.arguments; | 369 var args = meta.arguments.arguments; |
374 if (args == null || args.length == 0) { | 370 if (args == null || args.length == 0) { |
375 logger.warning('Missing argument in @$name annotation', | 371 logger.warning(MISSING_ANNOTATION_ARGUMENT.create({'name': name}), |
376 span: _spanForNode(context, meta)); | 372 span: _spanForNode(context, meta)); |
377 return null; | 373 return null; |
378 } | 374 } |
379 | 375 |
380 var lib = context; | 376 var lib = context; |
381 while (lib is! LibraryElement) lib = lib.enclosingElement; | 377 while (lib is! LibraryElement) lib = lib.enclosingElement; |
382 var res = resolver.evaluateConstant(lib, args[0]); | 378 var res = resolver.evaluateConstant(lib, args[0]); |
383 if (!res.isValid || res.value.type != types.stringType) { | 379 if (!res.isValid || res.value.type != types.stringType) { |
384 logger.warning('The parameter to @$name seems to be invalid.', | 380 logger.warning(INVALID_ANNOTATION_ARGUMENT.create({'name': name}), |
385 span: _spanForNode(context, args[0])); | 381 span: _spanForNode(context, args[0])); |
386 return null; | 382 return null; |
387 } | 383 } |
388 return res.value.stringValue; | 384 return res.value.stringValue; |
389 } | 385 } |
390 | 386 |
391 /// Adds the top-level [function] as an initalizer if it's marked with | 387 /// Adds the top-level [function] as an initalizer if it's marked with |
392 /// `@initMethod`. | 388 /// `@initMethod`. |
393 _processFunction(FunctionElement function, AssetId id) { | 389 _processFunction(FunctionElement function, AssetId id) { |
394 bool initMethodFound = false; | 390 bool initMethodFound = false; |
395 for (var meta in function.metadata) { | 391 for (var meta in function.metadata) { |
396 var e = meta.element; | 392 var e = meta.element; |
397 if (e is PropertyAccessorElement && | 393 if (e is PropertyAccessorElement && |
398 e.variable == types.initMethodElement) { | 394 e.variable == types.initMethodElement) { |
399 initMethodFound = true; | 395 initMethodFound = true; |
400 break; | 396 break; |
401 } | 397 } |
402 } | 398 } |
403 if (!initMethodFound) return; | 399 if (!initMethodFound) return; |
404 if (function.isPrivate) { | 400 if (function.isPrivate) { |
405 logger.error('@initMethod is no longer supported on private ' | 401 logger.error(PRIVATE_INIT_METHOD.create({'name': function.displayName}), |
406 'functions: ${function.displayName}', | |
407 span: _spanForNode(function, function.node.name)); | 402 span: _spanForNode(function, function.node.name)); |
408 return; | 403 return; |
409 } | 404 } |
410 initializers.add(new _InitMethodInitializer(id, function.displayName)); | 405 initializers.add(new _InitMethodInitializer(id, function.displayName)); |
411 } | 406 } |
412 | 407 |
413 /// Process members that are annotated with `@ComputedProperty` and records | 408 /// Process members that are annotated with `@ComputedProperty` and records |
414 /// the accessors of their expressions. | 409 /// the accessors of their expressions. |
415 _processComputedExpressions(List<analyzer.Element> computed) { | 410 _processComputedExpressions(List<analyzer.Element> computed) { |
416 var constructor = types.computedPropertyElement.constructors.first; | 411 var constructor = types.computedPropertyElement.constructors.first; |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
464 | 459 |
465 // Include initializers to switch from mirrors_loader to static_loader. | 460 // Include initializers to switch from mirrors_loader to static_loader. |
466 if (!initializers.isEmpty) { | 461 if (!initializers.isEmpty) { |
467 code.writeln(); | 462 code.writeln(); |
468 for (var init in initializers) { | 463 for (var init in initializers) { |
469 var initCode = init.asCode(prefixes[init.assetId]); | 464 var initCode = init.asCode(prefixes[init.assetId]); |
470 code.write(" $initCode,\n"); | 465 code.write(" $initCode,\n"); |
471 } | 466 } |
472 code.writeln(' ]);'); | 467 code.writeln(' ]);'); |
473 } else { | 468 } else { |
474 if (experimentalBootstrap) logger.warning(NO_INITIALIZERS_ERROR); | 469 if (experimentalBootstrap) logger.warning(NO_INITIALIZATION); |
475 code.writeln(']);'); | 470 code.writeln(']);'); |
476 } | 471 } |
477 if (!experimentalBootstrap) { | 472 if (!experimentalBootstrap) { |
478 code.writeln(' i${entryLibraries.length - 1}.main();'); | 473 code.writeln(' i${entryLibraries.length - 1}.main();'); |
479 } | 474 } |
480 | 475 |
481 // End of main(). | 476 // End of main(). |
482 code.writeln('}'); | 477 code.writeln('}'); |
483 transform.addOutput(new Asset.fromString(bootstrapId, code.toString())); | 478 transform.addOutput(new Asset.fromString(bootstrapId, code.toString())); |
484 | 479 |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
529 String asCode(String prefix) => | 524 String asCode(String prefix) => |
530 "() => Polymer.register('$tagName', $prefix.$typeName)"; | 525 "() => Polymer.register('$tagName', $prefix.$typeName)"; |
531 } | 526 } |
532 | 527 |
533 const MAIN_HEADER = """ | 528 const MAIN_HEADER = """ |
534 library app_bootstrap; | 529 library app_bootstrap; |
535 | 530 |
536 import 'package:polymer/polymer.dart'; | 531 import 'package:polymer/polymer.dart'; |
537 """; | 532 """; |
538 | 533 |
539 const NO_INITIALIZERS_ERROR = | |
540 'No polymer initializers were found. Make sure to either ' | |
541 'annotate your polymer elements with @CustomTag or include a ' | |
542 'top level method annotated with @initMethod that registers your ' | |
543 'elements. Both annotations are defined in the polymer library (' | |
544 'package:polymer/polymer.dart).'; | |
545 | 534 |
546 /// An html visitor that: | 535 /// An html visitor that: |
547 /// * finds all polymer expressions and records the getters and setters that | 536 /// * finds all polymer expressions and records the getters and setters that |
548 /// will be needed to evaluate them at runtime. | 537 /// will be needed to evaluate them at runtime. |
549 /// * extracts all attributes declared in the `attribute` attributes of | 538 /// * extracts all attributes declared in the `attribute` attributes of |
550 /// polymer elements. | 539 /// polymer elements. |
551 class _HtmlExtractor extends TreeVisitor { | 540 class _HtmlExtractor extends TreeVisitor { |
552 final Map<String, List<String>> publishedAttributes; | 541 final Map<String, List<String>> publishedAttributes; |
553 final SmokeCodeGenerator generator; | 542 final SmokeCodeGenerator generator; |
554 final _SubExpressionVisitor expressionVisitor; | 543 final _SubExpressionVisitor expressionVisitor; |
555 final TransformLogger logger; | 544 final BuildLogger logger; |
556 bool _inTemplate = false; | 545 bool _inTemplate = false; |
557 | 546 |
558 _HtmlExtractor(this.logger, this.generator, this.publishedAttributes, | 547 _HtmlExtractor(this.logger, this.generator, this.publishedAttributes, |
559 this.expressionVisitor); | 548 this.expressionVisitor); |
560 | 549 |
561 void visitElement(Element node) { | 550 void visitElement(Element node) { |
562 if (_inTemplate) _processNormalElement(node); | 551 if (_inTemplate) _processNormalElement(node); |
563 if (node.localName == 'polymer-element') { | 552 if (node.localName == 'polymer-element') { |
564 _processPolymerElement(node); | 553 _processPolymerElement(node); |
565 _processNormalElement(node); | 554 _processNormalElement(node); |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
618 _addExpression(exp, isEvent, isTwoWay, node.sourceSpan); | 607 _addExpression(exp, isEvent, isTwoWay, node.sourceSpan); |
619 } | 608 } |
620 }); | 609 }); |
621 } | 610 } |
622 | 611 |
623 void _addExpression(String stringExpression, bool inEvent, bool isTwoWay, | 612 void _addExpression(String stringExpression, bool inEvent, bool isTwoWay, |
624 SourceSpan span) { | 613 SourceSpan span) { |
625 | 614 |
626 if (inEvent) { | 615 if (inEvent) { |
627 if (stringExpression.startsWith('@')) { | 616 if (stringExpression.startsWith('@')) { |
628 logger.warning('event bindings with @ are no longer supported', | 617 logger.warning(AT_EXPRESSION_REMOVED, span: span); |
629 span: span); | |
630 return; | 618 return; |
631 } | 619 } |
632 | 620 |
633 if (stringExpression == '') return; | 621 if (stringExpression == '') return; |
634 if (stringExpression.startsWith('_')) { | 622 if (stringExpression.startsWith('_')) { |
635 logger.warning('private symbols cannot be used in event handlers', | 623 logger.warning(NO_PRIVATE_EVENT_HANDLERS, span: span); |
636 span: span); | |
637 return; | 624 return; |
638 } | 625 } |
639 generator.addGetter(stringExpression); | 626 generator.addGetter(stringExpression); |
640 generator.addSymbol(stringExpression); | 627 generator.addSymbol(stringExpression); |
641 } | 628 } |
642 expressionVisitor.run(pe.parse(stringExpression), isTwoWay, span); | 629 expressionVisitor.run(pe.parse(stringExpression), isTwoWay, span); |
643 } | 630 } |
644 } | 631 } |
645 | 632 |
646 /// A polymer-expression visitor that records every getter and setter that will | 633 /// A polymer-expression visitor that records every getter and setter that will |
647 /// be needed to evaluate a single expression at runtime. | 634 /// be needed to evaluate a single expression at runtime. |
648 class _SubExpressionVisitor extends pe.RecursiveVisitor { | 635 class _SubExpressionVisitor extends pe.RecursiveVisitor { |
649 final SmokeCodeGenerator generator; | 636 final SmokeCodeGenerator generator; |
650 final TransformLogger logger; | 637 final BuildLogger logger; |
651 bool _includeSetter; | 638 bool _includeSetter; |
652 SourceSpan _currentSpan; | 639 SourceSpan _currentSpan; |
653 | 640 |
654 _SubExpressionVisitor(this.generator, this.logger); | 641 _SubExpressionVisitor(this.generator, this.logger); |
655 | 642 |
656 /// Visit [exp], and record getters and setters that are needed in order to | 643 /// Visit [exp], and record getters and setters that are needed in order to |
657 /// evaluate it at runtime. [includeSetter] is only true if this expression | 644 /// evaluate it at runtime. [includeSetter] is only true if this expression |
658 /// occured in a context where it could be updated, for example in two-way | 645 /// occured in a context where it could be updated, for example in two-way |
659 /// bindings such as `<input value={{exp}}>`. | 646 /// bindings such as `<input value={{exp}}>`. |
660 void run(pe.Expression exp, bool includeSetter, span) { | 647 void run(pe.Expression exp, bool includeSetter, span) { |
661 _currentSpan = span; | 648 _currentSpan = span; |
662 _includeSetter = includeSetter; | 649 _includeSetter = includeSetter; |
663 visit(exp); | 650 visit(exp); |
664 } | 651 } |
665 | 652 |
666 /// Adds a getter and symbol for [name], and optionally a setter. | 653 /// Adds a getter and symbol for [name], and optionally a setter. |
667 _add(String name) { | 654 _add(String name) { |
668 if (name.startsWith('_')) { | 655 if (name.startsWith('_')) { |
669 logger.warning('private symbols are not supported', span: _currentSpan); | 656 logger.warning(NO_PRIVATE_SYMBOLS_IN_BINDINGS, span: _currentSpan); |
670 return; | 657 return; |
671 } | 658 } |
672 generator.addGetter(name); | 659 generator.addGetter(name); |
673 generator.addSymbol(name); | 660 generator.addSymbol(name); |
674 if (_includeSetter) generator.addSetter(name); | 661 if (_includeSetter) generator.addSetter(name); |
675 } | 662 } |
676 | 663 |
677 void preVisitExpression(e) { | 664 void preVisitExpression(e) { |
678 // For two-way bindings the outermost expression may be updated, so we need | 665 // For two-way bindings the outermost expression may be updated, so we need |
679 // both the getter and the setter, but we only need the getter for | 666 // both the getter and the setter, but we only need the getter for |
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
851 for (var c in combinators) { | 838 for (var c in combinators) { |
852 if (c is ShowElementCombinator) { | 839 if (c is ShowElementCombinator) { |
853 var show = c.shownNames.toSet(); | 840 var show = c.shownNames.toSet(); |
854 elements.retainWhere((e) => show.contains(e.displayName)); | 841 elements.retainWhere((e) => show.contains(e.displayName)); |
855 } else if (c is HideElementCombinator) { | 842 } else if (c is HideElementCombinator) { |
856 var hide = c.hiddenNames.toSet(); | 843 var hide = c.hiddenNames.toSet(); |
857 elements.removeWhere((e) => hide.contains(e.displayName)); | 844 elements.removeWhere((e) => hide.contains(e.displayName)); |
858 } | 845 } |
859 } | 846 } |
860 } | 847 } |
OLD | NEW |