| 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 /// Logic to validate that developers are correctly using Polymer constructs. | 5 /// Logic to validate that developers are correctly using Polymer constructs. |
| 6 /// This is mainly used to produce warnings for feedback in the editor. | 6 /// This is mainly used to produce warnings for feedback in the editor. |
| 7 library polymer.src.build.linter; | 7 library polymer.src.build.linter; |
| 8 | 8 |
| 9 import 'dart:async'; | 9 import 'dart:async'; |
| 10 | 10 |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 133 | 133 |
| 134 _ElementSummary(this.tagName, this.extendsTag, this.span); | 134 _ElementSummary(this.tagName, this.extendsTag, this.span); |
| 135 | 135 |
| 136 String toString() => "($tagName <: $extendsTag)"; | 136 String toString() => "($tagName <: $extendsTag)"; |
| 137 } | 137 } |
| 138 | 138 |
| 139 class _LinterVisitor extends TreeVisitor { | 139 class _LinterVisitor extends TreeVisitor { |
| 140 TransformLogger _logger; | 140 TransformLogger _logger; |
| 141 bool _inPolymerElement = false; | 141 bool _inPolymerElement = false; |
| 142 bool _dartTagSeen = false; | 142 bool _dartTagSeen = false; |
| 143 bool _polymerHtmlSeen = false; |
| 143 bool _isEntrypoint; | 144 bool _isEntrypoint; |
| 144 Map<String, _ElementSummary> _elements; | 145 Map<String, _ElementSummary> _elements; |
| 145 | 146 |
| 146 _LinterVisitor(this._logger, this._elements, this._isEntrypoint) { | 147 _LinterVisitor(this._logger, this._elements, this._isEntrypoint) { |
| 147 // We normalize the map, so each element has a direct reference to any | 148 // We normalize the map, so each element has a direct reference to any |
| 148 // element it extends from. | 149 // element it extends from. |
| 149 for (var tag in _elements.values) { | 150 for (var tag in _elements.values) { |
| 150 var extendsTag = tag.extendsTag; | 151 var extendsTag = tag.extendsTag; |
| 151 if (extendsTag == null) continue; | 152 if (extendsTag == null) continue; |
| 152 tag.extendsType = _elements[extendsTag]; | 153 tag.extendsType = _elements[extendsTag]; |
| 153 } | 154 } |
| 154 } | 155 } |
| 155 | 156 |
| 156 void visitElement(Element node) { | 157 void visitElement(Element node) { |
| 157 switch (node.localName) { | 158 switch (node.localName) { |
| 158 case 'link': _validateLinkElement(node); break; | 159 case 'link': _validateLinkElement(node); break; |
| 159 case 'element': _validateElementElement(node); break; | 160 case 'element': _validateElementElement(node); break; |
| 160 case 'polymer-element': _validatePolymerElement(node); break; | 161 case 'polymer-element': _validatePolymerElement(node); break; |
| 161 case 'script': _validateScriptElement(node); break; | 162 case 'script': _validateScriptElement(node); break; |
| 162 default: | 163 default: |
| 163 _validateNormalElement(node); | 164 _validateNormalElement(node); |
| 164 super.visitElement(node); | 165 super.visitElement(node); |
| 165 break; | 166 break; |
| 166 } | 167 } |
| 167 } | 168 } |
| 168 | 169 |
| 169 void run(Document doc) { | 170 void run(Document doc) { |
| 170 visit(doc); | 171 visit(doc); |
| 171 | 172 |
| 172 if (_isEntrypoint && !_dartTagSeen) { | 173 if (_isEntrypoint && !_polymerHtmlSeen) { |
| 173 _logger.error(USE_INIT_DART, span: doc.body.sourceSpan); | 174 _logger.warning(USE_POLYMER_HTML, span: doc.body.sourceSpan); |
| 174 } | 175 } |
| 175 } | 176 } |
| 176 | 177 |
| 177 /// Produce warnings for invalid link-rel tags. | 178 /// Produce warnings for invalid link-rel tags. |
| 178 void _validateLinkElement(Element node) { | 179 void _validateLinkElement(Element node) { |
| 179 var rel = node.attributes['rel']; | 180 var rel = node.attributes['rel']; |
| 180 if (rel != 'import' && rel != 'stylesheet') return; | 181 if (rel != 'import' && rel != 'stylesheet') return; |
| 181 | 182 |
| 182 if (rel == 'import' && _dartTagSeen) { | 183 if (rel == 'import' && _dartTagSeen) { |
| 183 _logger.warning( | 184 _logger.warning( |
| 184 "Move HTML imports above your Dart script tag.", | 185 "Move HTML imports above your Dart script tag.", |
| 185 span: node.sourceSpan); | 186 span: node.sourceSpan); |
| 186 } | 187 } |
| 187 | 188 |
| 188 var href = node.attributes['href']; | 189 var href = node.attributes['href']; |
| 189 if (href != null && href != '') return; | 190 if (href == null || href == '') { |
| 191 _logger.warning('link rel="$rel" missing href.', span: node.sourceSpan); |
| 192 return; |
| 193 } |
| 190 | 194 |
| 195 if (href == 'packages/polymer/polymer.html') { |
| 196 _polymerHtmlSeen = true; |
| 197 } |
| 191 // TODO(sigmund): warn also if href can't be resolved. | 198 // TODO(sigmund): warn also if href can't be resolved. |
| 192 _logger.warning('link rel="$rel" missing href.', span: node.sourceSpan); | |
| 193 } | 199 } |
| 194 | 200 |
| 195 /// Produce warnings if using `<element>` instead of `<polymer-element>`. | 201 /// Produce warnings if using `<element>` instead of `<polymer-element>`. |
| 196 void _validateElementElement(Element node) { | 202 void _validateElementElement(Element node) { |
| 197 _logger.warning('<element> elements are not supported, use' | 203 _logger.warning('<element> elements are not supported, use' |
| 198 ' <polymer-element> instead', span: node.sourceSpan); | 204 ' <polymer-element> instead', span: node.sourceSpan); |
| 199 } | 205 } |
| 200 | 206 |
| 201 /// Produce warnings if using `<polymer-element>` in the wrong place or if the | 207 /// Produce warnings if using `<polymer-element>` in the wrong place or if the |
| 202 /// definition is not complete. | 208 /// definition is not complete. |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 238 } | 244 } |
| 239 | 245 |
| 240 var oldValue = _inPolymerElement; | 246 var oldValue = _inPolymerElement; |
| 241 _inPolymerElement = true; | 247 _inPolymerElement = true; |
| 242 super.visitElement(node); | 248 super.visitElement(node); |
| 243 _inPolymerElement = oldValue; | 249 _inPolymerElement = oldValue; |
| 244 } | 250 } |
| 245 | 251 |
| 246 /// Checks for multiple Dart script tags in the same page, which is invalid. | 252 /// Checks for multiple Dart script tags in the same page, which is invalid. |
| 247 void _validateScriptElement(Element node) { | 253 void _validateScriptElement(Element node) { |
| 248 var scriptType = node.attributes['type']; | |
| 249 var isDart = scriptType == 'application/dart'; | |
| 250 var src = node.attributes['src']; | 254 var src = node.attributes['src']; |
| 251 | |
| 252 if (isDart) { | |
| 253 if (_dartTagSeen) { | |
| 254 _logger.warning('Only one "application/dart" script tag per document ' | |
| 255 'is allowed.', span: node.sourceSpan); | |
| 256 } | |
| 257 _dartTagSeen = true; | |
| 258 } | |
| 259 | |
| 260 if (src == null) return; | 255 if (src == null) return; |
| 261 | 256 var type = node.attributes['type']; |
| 262 if (src == 'packages/polymer/boot.js') { | 257 bool isDart = type == 'application/dart;component=1' || |
| 263 _logger.warning(BOOT_JS_DEPRECATED, span: node.sourceSpan); | 258 type == 'application/dart'; |
| 264 return; | |
| 265 } | |
| 266 | 259 |
| 267 if (src.endsWith('.dart') && !isDart) { | 260 if (src.endsWith('.dart') && !isDart) { |
| 268 _logger.warning('Wrong script type, expected type="application/dart".', | 261 _logger.warning('Wrong script type, expected type="application/dart" ' |
| 269 span: node.sourceSpan); | 262 'or type="application/dart;component=1".', span: node.sourceSpan); |
| 270 return; | 263 return; |
| 271 } | 264 } |
| 272 | 265 |
| 273 if (!src.endsWith('.dart') && isDart) { | 266 if (!src.endsWith('.dart') && isDart) { |
| 274 _logger.warning('"application/dart" scripts should ' | 267 _logger.warning('"$type" scripts should use the .dart file extension.', |
| 275 'use the .dart file extension.', | |
| 276 span: node.sourceSpan); | 268 span: node.sourceSpan); |
| 277 return; | 269 return; |
| 278 } | 270 } |
| 279 | 271 |
| 280 if (node.innerHtml.trim() != '') { | 272 if (node.innerHtml.trim() != '') { |
| 281 _logger.warning('script tag has "src" attribute and also has script ' | 273 _logger.warning('script tag has "src" attribute and also has script ' |
| 282 'text.', span: node.sourceSpan); | 274 'text.', span: node.sourceSpan); |
| 283 } | 275 } |
| 284 } | 276 } |
| 285 | 277 |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 378 | 370 |
| 379 if (value.contains('(')) { | 371 if (value.contains('(')) { |
| 380 _logger.warning('Invalid event handler body "$value". Declare a method ' | 372 _logger.warning('Invalid event handler body "$value". Declare a method ' |
| 381 'in your custom element "void handlerName(event, detail, target)" ' | 373 'in your custom element "void handlerName(event, detail, target)" ' |
| 382 'and use the form $name="handlerName".', | 374 'and use the form $name="handlerName".', |
| 383 span: node.attributeSpans[name]); | 375 span: node.attributeSpans[name]); |
| 384 } | 376 } |
| 385 } | 377 } |
| 386 } | 378 } |
| 387 | 379 |
| 388 const String USE_INIT_DART = | 380 const String USE_POLYMER_HTML = |
| 389 'To run a polymer application, you need to call "initPolymer". You can ' | 381 'To run a polymer application you need to include the following HTML ' |
| 390 'either include a generic script tag that does this for you:' | 382 'import: <link rel="import" href="packages/polymer/polymer.html">. This ' |
| 391 '\'<script type="application/dart">export "package:polymer/init.dart";' | 383 'will include the common polymer logic needed to boostrap your ' |
| 392 '</script>\' or add your own script tag and call that function. ' | 384 'application. The old style of initializing polymer with boot.js or ' |
| 393 'Make sure the script tag is placed after all HTML imports.'; | 385 'initPolymer are now deprecated. '; |
| 394 | 386 |
| 395 const String BOOT_JS_DEPRECATED = | |
| 396 '"boot.js" is now deprecated. Instead, you can initialize your polymer ' | |
| 397 'application by calling "initPolymer()" in your main. If you don\'t have a ' | |
| 398 'main, then you can include our generic main by adding the following ' | |
| 399 'script tag to your page: \'<script type="application/dart">export ' | |
| 400 '"package:polymer/init.dart";</script>\'. Additionally you need to ' | |
| 401 'include: \'<script src="packages/browser/dart.js"></script>\' in the page ' | |
| 402 'too. Make sure these script tags come after all HTML imports.'; | |
| OLD | NEW |