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 |