OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 /** Collects several code emitters for the template tool. */ | 5 /** Collects several code emitters for the template tool. */ |
6 library emitters; | 6 library emitters; |
7 | 7 |
8 import 'package:csslib/parser.dart' as css; | |
9 import 'package:csslib/visitor.dart'; | |
10 import 'package:html5lib/dom.dart'; | 8 import 'package:html5lib/dom.dart'; |
11 import 'package:html5lib/dom_parsing.dart'; | 9 import 'package:html5lib/dom_parsing.dart' show TreeVisitor; |
12 import 'package:html5lib/parser.dart'; | 10 import 'package:html5lib/parser.dart' show parseFragment; |
13 import 'package:source_maps/span.dart' show Span, FileLocation; | 11 import 'package:source_maps/printer.dart'; |
| 12 import 'package:source_maps/refactor.dart'; |
14 | 13 |
15 import 'code_printer.dart'; | 14 import 'compiler_options.dart'; |
16 import 'compiler.dart'; | 15 import 'css_emitters.dart' show emitStyleSheet, emitOriginalCss; |
17 import 'dart_parser.dart' show DartCodeInfo; | |
18 import 'html5_utils.dart'; | 16 import 'html5_utils.dart'; |
19 import 'html_css_fixup.dart'; | 17 import 'info.dart' show ComponentInfo, FileInfo, GlobalInfo; |
20 import 'info.dart'; | |
21 import 'messages.dart'; | 18 import 'messages.dart'; |
22 import 'compiler_options.dart'; | 19 import 'paths.dart' show PathMapper; |
23 import 'paths.dart'; | 20 import 'utils.dart' show escapeDartString, path; |
24 import 'refactor.dart'; | |
25 import 'utils.dart'; | |
26 | |
27 | |
28 /** Only x-tag name element selectors are emitted as [is="x-"]. */ | |
29 class CssEmitter extends CssPrinter { | |
30 final Set _componentsTag; | |
31 | |
32 CssEmitter(this._componentsTag); | |
33 | |
34 /** | |
35 * If element selector is a component's tag name, then change selector to | |
36 * find element who's is attribute's the component's name. | |
37 */ | |
38 bool _emitComponentElement(ElementSelector node) { | |
39 if (_componentsTag.contains(node.name)) { | |
40 emit('[is="${node.name}"]'); | |
41 return true; | |
42 } | |
43 return false; | |
44 } | |
45 | |
46 void visitElementSelector(ElementSelector node) { | |
47 if (_componentsTag.isNotEmpty && _emitComponentElement(node)) return; | |
48 super.visitElementSelector(node); | |
49 } | |
50 | |
51 } | |
52 | |
53 /** | |
54 * Style sheet polyfill for a component, each CSS class name/id selector | |
55 * referenced (selector) is prepended with prefix_ (if prefix is non-null). In | |
56 * addition an element selector this is a component's tag name is transformed | |
57 * to the attribute selector [is="x-"] where x- is the component's tag name. | |
58 */ | |
59 class ComponentCssEmitter extends CssPrinter { | |
60 final String _componentTagName; | |
61 final CssPolyfillKind _polyfillKind; | |
62 bool _inHostDirective = false; | |
63 bool _selectorStartInHostDirective = false; | |
64 | |
65 ComponentCssEmitter(this._componentTagName, this._polyfillKind); | |
66 | |
67 /** Is the element selector an x-tag name. */ | |
68 bool _isSelectorElementXTag(Selector node) { | |
69 if (node.simpleSelectorSequences.length > 0) { | |
70 var selector = node.simpleSelectorSequences[0].simpleSelector; | |
71 return selector is ElementSelector && selector.name == _componentTagName; | |
72 } | |
73 return false; | |
74 } | |
75 | |
76 /** | |
77 * If element selector is the component's tag name, then change selector to | |
78 * find element who's is attribute is the component's name. | |
79 */ | |
80 bool _emitComponentElement(ElementSelector node) { | |
81 if (_polyfillKind == CssPolyfillKind.SCOPED_POLYFILL && | |
82 _componentTagName == node.name) { | |
83 emit('[is="$_componentTagName"]'); | |
84 return true; | |
85 } | |
86 return false; | |
87 } | |
88 | |
89 void visitSelector(Selector node) { | |
90 // If the selector starts with an x-tag name don't emit it twice. | |
91 if (!_isSelectorElementXTag(node) && | |
92 _polyfillKind == CssPolyfillKind.SCOPED_POLYFILL) { | |
93 if (_inHostDirective) { | |
94 // Style the element that's hosting the component, therefore don't emit | |
95 // the descendent combinator (first space after the [is="x-..."]). | |
96 emit('[is="$_componentTagName"]'); | |
97 // Signal that first simpleSelector must be checked. | |
98 _selectorStartInHostDirective = true; | |
99 } else { | |
100 // Emit its scoped as a descendent (space at end). | |
101 emit('[is="$_componentTagName"] '); | |
102 } | |
103 } | |
104 super.visitSelector(node); | |
105 } | |
106 | |
107 /** | |
108 * If first simple selector of a ruleset in a @host directive is a wildcard | |
109 * then don't emit the wildcard. | |
110 */ | |
111 void visitSimpleSelectorSequence(SimpleSelectorSequence node) { | |
112 if (_selectorStartInHostDirective) { | |
113 _selectorStartInHostDirective = false; | |
114 if (_polyfillKind == CssPolyfillKind.SCOPED_POLYFILL && | |
115 node.simpleSelector.isWildcard) { | |
116 // Skip the wildcard if first item in the sequence. | |
117 return; | |
118 } | |
119 assert(node.isCombinatorNone); | |
120 } | |
121 | |
122 super.visitSimpleSelectorSequence(node); | |
123 } | |
124 | |
125 void visitClassSelector(ClassSelector node) { | |
126 if (_polyfillKind == CssPolyfillKind.MANGLED_POLYFILL) { | |
127 emit('.${_componentTagName}_${node.name}'); | |
128 } else { | |
129 super.visitClassSelector(node); | |
130 } | |
131 } | |
132 | |
133 void visitIdSelector(IdSelector node) { | |
134 if (_polyfillKind == CssPolyfillKind.MANGLED_POLYFILL) { | |
135 emit('#${_componentTagName}_${node.name}'); | |
136 } else { | |
137 super.visitIdSelector(node); | |
138 } | |
139 } | |
140 | |
141 void visitElementSelector(ElementSelector node) { | |
142 if (_emitComponentElement(node)) return; | |
143 super.visitElementSelector(node); | |
144 } | |
145 | |
146 /** | |
147 * If we're polyfilling scoped styles the @host directive is stripped. Any | |
148 * ruleset(s) processed in an @host will fixup the first selector. See | |
149 * visitSelector and visitSimpleSelectorSequence in this class, they adjust | |
150 * the selectors so it styles the element hosting the compopnent. | |
151 */ | |
152 void visitHostDirective(HostDirective node) { | |
153 if (_polyfillKind == CssPolyfillKind.SCOPED_POLYFILL) { | |
154 _inHostDirective = true; | |
155 emit('/* @host */'); | |
156 for (var ruleset in node.rulesets) { | |
157 ruleset.visit(this); | |
158 } | |
159 _inHostDirective = false; | |
160 emit('/* end of @host */\n'); | |
161 } else { | |
162 super.visitHostDirective(node); | |
163 } | |
164 } | |
165 } | |
166 | |
167 /** | |
168 * Helper function to emit the contents of the style tag outside of a component. | |
169 */ | |
170 String emitStyleSheet(StyleSheet ss, FileInfo file) => | |
171 (new CssEmitter(file.components.keys.toSet()) | |
172 ..visitTree(ss, pretty: true)).toString(); | |
173 | |
174 /** Helper function to emit a component's style tag content. */ | |
175 String emitComponentStyleSheet(StyleSheet ss, String tagName, | |
176 CssPolyfillKind polyfillKind) => | |
177 (new ComponentCssEmitter(tagName, polyfillKind) | |
178 ..visitTree(ss, pretty: true)).toString(); | |
179 | 21 |
180 /** Generates the class corresponding to a single web component. */ | 22 /** Generates the class corresponding to a single web component. */ |
181 class WebComponentEmitter { | 23 NestedPrinter emitPolymerElement(ComponentInfo info, PathMapper pathMapper, |
182 final Messages messages; | 24 TextEditTransaction transaction, CompilerOptions options) { |
183 final FileInfo _fileInfo; | 25 if (info.classDeclaration == null) return null; |
184 final CssPolyfillKind cssPolyfillKind; | |
185 | 26 |
186 WebComponentEmitter(this._fileInfo, this.messages, this.cssPolyfillKind); | 27 var codeInfo = info.userCode; |
187 | 28 if (transaction == null) { |
188 CodePrinter run(ComponentInfo info, PathMapper pathMapper, | 29 // TODO(sigmund): avoid emitting this file if we don't need to do any |
189 TextEditTransaction transaction) { | 30 // modifications (e.g. no @observable and not adding the libraryName). |
190 var templateNode = info.element.nodes.firstWhere( | 31 transaction = new TextEditTransaction(codeInfo.code, codeInfo.sourceFile); |
191 (n) => n.tagName == 'template', orElse: () => null); | |
192 | |
193 if (templateNode != null) { | |
194 if (!info.styleSheets.isEmpty && !messages.options.processCss) { | |
195 // TODO(terry): Only one style tag per component. | |
196 | |
197 // TODO(jmesserly): csslib + html5lib should work together. | |
198 // We shouldn't need to call a different function to serialize CSS. | |
199 // Calling innerHtml on a StyleElement should be enought - like a real | |
200 // browser. CSSOM and DOM should work together in the same tree. | |
201 var styleText = emitComponentStyleSheet( | |
202 info.styleSheets[0], info.tagName, cssPolyfillKind); | |
203 | |
204 templateNode.insertBefore( | |
205 new Element.html('<style>\n$styleText\n</style>'), | |
206 templateNode.hasChildNodes() ? templateNode.children[0] : null); | |
207 } | |
208 } | |
209 | |
210 bool hasExtends = info.extendsComponent != null; | |
211 var codeInfo = info.userCode; | |
212 var classDecl = info.classDeclaration; | |
213 if (classDecl == null) return null; | |
214 | |
215 if (transaction == null) { | |
216 transaction = new TextEditTransaction(codeInfo.code, codeInfo.sourceFile); | |
217 } | |
218 | |
219 // Expand the headers to include polymer imports, unless they are already | |
220 // present. | |
221 var libraryName = (codeInfo.libraryName != null) | |
222 ? codeInfo.libraryName | |
223 : info.tagName.replaceAll(new RegExp('[-./]'), '_'); | |
224 var header = new CodePrinter(0); | |
225 header.add(_header(path.basename(info.declaringFile.inputUrl.resolvedPath), | |
226 libraryName)); | |
227 emitImports(codeInfo, info, pathMapper, header); | |
228 header.addLine(''); | |
229 transaction.edit(0, codeInfo.directivesEnd, header); | |
230 | |
231 var classBody = new CodePrinter(1) | |
232 ..add('\n') | |
233 ..addLine('/** Original code from the component. */'); | |
234 | |
235 var pos = classDecl.leftBracket.end; | |
236 transaction.edit(pos, pos, classBody); | |
237 | |
238 // Emit all the code in a single printer, keeping track of source-maps. | |
239 return transaction.commit(); | |
240 } | 32 } |
| 33 if (codeInfo.libraryName == null) { |
| 34 // For deploy, we need to import the library associated with the component, |
| 35 // so we need to ensure there is a library directive. |
| 36 var libraryName = info.tagName.replaceAll(new RegExp('[-./]'), '_'); |
| 37 transaction.edit(0, 0, 'library $libraryName;'); |
| 38 } |
| 39 return transaction.commit(); |
241 } | 40 } |
242 | 41 |
243 /** The code that will be used to bootstrap the application. */ | 42 /** The code that will be used to bootstrap the application. */ |
244 CodePrinter generateBootstrapCode( | 43 NestedPrinter generateBootstrapCode( |
245 FileInfo info, FileInfo userMainInfo, GlobalInfo global, | 44 FileInfo info, FileInfo userMainInfo, GlobalInfo global, |
246 PathMapper pathMapper, CompilerOptions options) { | 45 PathMapper pathMapper, CompilerOptions options) { |
247 | 46 |
248 var printer = new CodePrinter(0) | 47 var printer = new NestedPrinter(0) |
249 ..addLine('library app_bootstrap;') | 48 ..addLine('library app_bootstrap;') |
250 ..addLine('') | 49 ..addLine('') |
251 ..addLine("import 'package:polymer/polymer.dart';") | 50 ..addLine("import 'package:polymer/polymer.dart';") |
252 ..addLine("import 'dart:mirrors' show currentMirrorSystem;"); | 51 ..addLine("import 'dart:mirrors' show currentMirrorSystem;"); |
253 | |
254 if (userMainInfo.userCode != null) { | 52 if (userMainInfo.userCode != null) { |
255 printer..addLine('') | 53 printer..addLine("import '${pathMapper.importUrlFor(info, userMainInfo)}' " |
256 ..addLine("import '${pathMapper.importUrlFor(info, userMainInfo)}' " | 54 "as userMain;\n"); |
257 "as userMain;\n"); | |
258 } | 55 } |
259 | 56 |
260 int i = 0; | 57 int i = 0; |
261 for (var c in global.components.values) { | 58 for (var c in global.components.values) { |
262 if (c.hasConflict) continue; | 59 if (c.hasConflict) continue; |
263 printer.addLine("import '${pathMapper.importUrlFor(info, c)}' as i$i;"); | 60 printer.addLine("import '${pathMapper.importUrlFor(info, c)}' as i$i;"); |
264 i++; | 61 i++; |
265 } | 62 } |
266 | 63 |
267 printer..addLine('') | 64 printer..addLine('') |
268 ..addLine('void main() {') | 65 ..addLine('void main() {') |
269 ..indent += 1 | 66 ..indent += 1 |
270 ..addLine("initPolymer([") | 67 ..addLine("initPolymer([") |
271 ..indent += 2; | 68 ..indent += 2; |
272 | 69 |
273 for (var c in global.components.values) { | 70 for (var c in global.components.values) { |
274 if (c.hasConflict) continue; | 71 if (c.hasConflict) continue; |
275 var tagName = escapeDartString(c.tagName); | |
276 var cssMapExpression = createCssSelectorsExpression(c, | |
277 CssPolyfillKind.of(options, c)); | |
278 printer.addLine("'${pathMapper.importUrlFor(info, c)}',"); | 72 printer.addLine("'${pathMapper.importUrlFor(info, c)}',"); |
279 } | 73 } |
280 | 74 |
281 return printer | 75 return printer |
282 ..indent -= 1 | 76 ..indent -= 1 |
283 ..addLine('],') | 77 ..addLine('],') |
284 ..addLine(userMainInfo.userCode != null ? 'userMain.main,' : '() {},') | 78 ..addLine(userMainInfo.userCode != null ? 'userMain.main,' : '() {},') |
285 ..addLine( | 79 ..addLine( |
286 "currentMirrorSystem().findLibrary(const Symbol('app_bootstrap'))") | 80 "currentMirrorSystem().findLibrary(const Symbol('app_bootstrap'))") |
287 ..indent += 2 | 81 ..indent += 2 |
288 ..addLine(".first.uri.toString());") | 82 ..addLine(".first.uri.toString());") |
289 ..indent -= 4 | 83 ..indent -= 4 |
290 ..addLine('}'); | 84 ..addLine('}'); |
291 } | 85 } |
292 | 86 |
293 | 87 |
294 | |
295 /** | 88 /** |
296 * List of HTML4 elements which could have relative URL resource: | 89 * Rewrites attributes that contain relative URL (excluding src urls in script |
297 * | 90 * and link tags which are already rewritten by other parts of the compiler). |
298 * <body background=url>, <img src=url>, <input src=url> | |
299 * | |
300 * HTML 5: | |
301 * | |
302 * <audio src=url>, <command icon=url>, <embed src=url>, | |
303 * <source src=url>, <video poster=url> and <video src=url> | |
304 */ | 91 */ |
305 class AttributeUrlTransform extends TreeVisitor { | 92 class _AttributeUrlTransform extends TreeVisitor { |
306 final String filePath; | 93 final String filePath; |
307 final PathMapper pathMapper; | 94 final PathMapper pathMapper; |
308 | 95 |
309 AttributeUrlTransform(this.filePath, this.pathMapper); | 96 _AttributeUrlTransform(this.filePath, this.pathMapper); |
310 | 97 |
311 visitElement(Element node) { | 98 visitElement(Element node) { |
312 if (node.tagName == 'script') return; | 99 if (node.tagName == 'script') return; |
313 if (node.tagName == 'link') return; | 100 if (node.tagName == 'link') return; |
314 | 101 |
315 for (var key in node.attributes.keys) { | 102 for (var key in node.attributes.keys) { |
316 if (urlAttributes.contains(key)) { | 103 if (urlAttributes.contains(key)) { |
317 // Rewrite the URL attribute. | 104 node.attributes[key] = |
318 node.attributes[key] = pathMapper.transformUrl(filePath, | 105 pathMapper.transformUrl(filePath, node.attributes[key]); |
319 node.attributes[key]); | |
320 } | 106 } |
321 } | 107 } |
322 | |
323 super.visitElement(node); | 108 super.visitElement(node); |
324 } | 109 } |
325 } | 110 } |
326 | 111 |
327 void _transformRelativeUrlAttributes(Document document, PathMapper pathMapper, | 112 final _shadowDomJS = new RegExp(r'shadowdom\..*\.js', caseSensitive: false); |
328 String filePath) { | 113 final _bootJS = new RegExp(r'.*/polymer/boot.js', caseSensitive: false); |
329 // Transform any element's attribute which is a relative URL. | |
330 new AttributeUrlTransform(filePath, pathMapper).visit(document); | |
331 } | |
332 | |
333 void emitImports(DartCodeInfo codeInfo, LibraryInfo info, PathMapper pathMapper, | |
334 CodePrinter printer, [GlobalInfo global]) { | |
335 var seenImports = new Set(); | |
336 addUnique(String importString, [location]) { | |
337 if (!seenImports.contains(importString)) { | |
338 printer.addLine(importString, location: location); | |
339 seenImports.add(importString); | |
340 } | |
341 } | |
342 | |
343 // Add imports only for those components used by this component. | |
344 for (var c in info.usedComponents.keys) { | |
345 addUnique("import '${pathMapper.importUrlFor(info, c)}';"); | |
346 } | |
347 | |
348 if (global != null) { | |
349 for (var c in global.components.values) { | |
350 addUnique("import '${pathMapper.importUrlFor(info, c)}';"); | |
351 } | |
352 } | |
353 | |
354 if (info is ComponentInfo) { | |
355 // Inject an import to the base component. | |
356 var base = (info as ComponentInfo).extendsComponent; | |
357 if (base != null) { | |
358 addUnique("import '${pathMapper.importUrlFor(info, base)}';"); | |
359 } | |
360 } | |
361 | |
362 // Add existing import, export, and part directives. | |
363 var file = codeInfo.sourceFile; | |
364 for (var d in codeInfo.directives) { | |
365 addUnique(d.toString(), file != null ? file.location(d.offset) : null); | |
366 } | |
367 } | |
368 | |
369 final shadowDomJS = new RegExp(r'shadowdom\..*\.js', caseSensitive: false); | |
370 final bootJS = new RegExp(r'.*/polymer/boot.js', caseSensitive: false); | |
371 | 114 |
372 /** Trim down the html for the main html page. */ | 115 /** Trim down the html for the main html page. */ |
373 void transformMainHtml(Document document, FileInfo fileInfo, | 116 void transformMainHtml(Document document, FileInfo fileInfo, |
374 PathMapper pathMapper, bool hasCss, bool rewriteUrls, | 117 PathMapper pathMapper, bool hasCss, bool rewriteUrls, |
375 Messages messages, GlobalInfo global) { | 118 Messages messages, GlobalInfo global) { |
376 var filePath = fileInfo.inputUrl.resolvedPath; | 119 var filePath = fileInfo.inputUrl.resolvedPath; |
377 | 120 |
378 bool dartLoaderFound = false; | 121 bool dartLoaderFound = false; |
379 bool shadowDomFound = false; | 122 bool shadowDomFound = false; |
380 for (var tag in document.queryAll('script')) { | 123 for (var tag in document.queryAll('script')) { |
381 var src = tag.attributes['src']; | 124 var src = tag.attributes['src']; |
382 if (src != null) { | 125 if (src != null) { |
383 var last = src.split('/').last; | 126 var last = src.split('/').last; |
384 if (last == 'dart.js' || last == 'testing.js') { | 127 if (last == 'dart.js' || last == 'testing.js') { |
385 dartLoaderFound = true; | 128 dartLoaderFound = true; |
386 } else if (shadowDomJS.hasMatch(last)) { | 129 } else if (_shadowDomJS.hasMatch(last)) { |
387 shadowDomFound = true; | 130 shadowDomFound = true; |
388 } | 131 } |
389 } | 132 } |
390 if (tag.attributes['type'] == 'application/dart') { | 133 if (tag.attributes['type'] == 'application/dart') { |
391 tag.remove(); | 134 tag.remove(); |
392 } else if (src != null) { | 135 } else if (src != null) { |
393 if (bootJS.hasMatch(src)) { | 136 if (_bootJS.hasMatch(src)) { |
394 tag.remove(); | 137 tag.remove(); |
395 } else if (rewriteUrls) { | 138 } else if (rewriteUrls) { |
396 tag.attributes["src"] = pathMapper.transformUrl(filePath, src); | 139 tag.attributes["src"] = pathMapper.transformUrl(filePath, src); |
397 } | 140 } |
398 } | 141 } |
399 } | 142 } |
400 | 143 |
401 for (var tag in document.queryAll('link')) { | 144 for (var tag in document.queryAll('link')) { |
402 var href = tag.attributes['href']; | 145 var href = tag.attributes['href']; |
403 var rel = tag.attributes['rel']; | 146 var rel = tag.attributes['rel']; |
404 if (rel == 'component' || rel == 'components' || rel == 'import') { | 147 if (rel == 'component' || rel == 'components' || rel == 'import') { |
405 tag.remove(); | 148 tag.remove(); |
406 } else if (href != null && rewriteUrls && !hasCss) { | 149 } else if (href != null && rewriteUrls && !hasCss) { |
407 // Only rewrite URL if rewrite on and we're not CSS polyfilling. | 150 // Only rewrite URL if rewrite on and we're not CSS polyfilling. |
408 tag.attributes['href'] = pathMapper.transformUrl(filePath, href); | 151 tag.attributes['href'] = pathMapper.transformUrl(filePath, href); |
409 } | 152 } |
410 } | 153 } |
411 | 154 |
412 if (rewriteUrls) { | 155 if (rewriteUrls) { |
413 // Transform any element's attribute which is a relative URL. | 156 // Transform any element's attribute which is a relative URL. |
414 _transformRelativeUrlAttributes(document, pathMapper, filePath); | 157 new _AttributeUrlTransform(filePath, pathMapper).visit(document); |
415 } | 158 } |
416 | 159 |
417 if (hasCss) { | 160 if (hasCss) { |
418 var newCss = pathMapper.mangle(path.basename(filePath), '.css', true); | 161 var newCss = pathMapper.mangle(path.basename(filePath), '.css', true); |
419 var linkElem = new Element.html( | 162 var linkElem = new Element.html( |
420 '<link rel="stylesheet" type="text/css" href="$newCss">'); | 163 '<link rel="stylesheet" type="text/css" href="$newCss">'); |
421 document.head.insertBefore(linkElem, null); | 164 document.head.insertBefore(linkElem, null); |
422 } | 165 } |
423 | 166 |
424 var styles = document.queryAll('style'); | 167 var styles = document.queryAll('style'); |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
464 // 'Please add this line to your HTML file: $dartLoader', | 207 // 'Please add this line to your HTML file: $dartLoader', |
465 // document.body.sourceSpan); | 208 // document.body.sourceSpan); |
466 // TODO(sigmund): switch to 'boot.js' | 209 // TODO(sigmund): switch to 'boot.js' |
467 document.body.nodes.add(parseFragment('<script type="text/javascript" ' | 210 document.body.nodes.add(parseFragment('<script type="text/javascript" ' |
468 'src="packages/browser/dart.js"></script>\n')); | 211 'src="packages/browser/dart.js"></script>\n')); |
469 } | 212 } |
470 | 213 |
471 // Insert the "auto-generated" comment after the doctype, otherwise IE will | 214 // Insert the "auto-generated" comment after the doctype, otherwise IE will |
472 // go into quirks mode. | 215 // go into quirks mode. |
473 int commentIndex = 0; | 216 int commentIndex = 0; |
474 DocumentType doctype = find(document.nodes, (n) => n is DocumentType); | 217 DocumentType doctype = |
| 218 document.nodes.firstWhere((n) => n is DocumentType, orElse: () => null); |
475 if (doctype != null) { | 219 if (doctype != null) { |
476 commentIndex = document.nodes.indexOf(doctype) + 1; | 220 commentIndex = document.nodes.indexOf(doctype) + 1; |
477 // TODO(jmesserly): the html5lib parser emits a warning for missing | 221 // TODO(jmesserly): the html5lib parser emits a warning for missing |
478 // doctype, but it allows you to put it after comments. Presumably they do | 222 // doctype, but it allows you to put it after comments. Presumably they do |
479 // this because some comments won't force IE into quirks mode (sigh). See | 223 // this because some comments won't force IE into quirks mode (sigh). See |
480 // this link for more info: | 224 // this link for more info: |
481 // http://bugzilla.validator.nu/show_bug.cgi?id=836 | 225 // http://bugzilla.validator.nu/show_bug.cgi?id=836 |
482 // For simplicity we emit the warning always, like validator.nu does. | 226 // For simplicity we emit the warning always, like validator.nu does. |
483 if (doctype.tagName != 'html' || commentIndex != 1) { | 227 if (doctype.tagName != 'html' || commentIndex != 1) { |
484 messages.warning('file should start with <!DOCTYPE html> ' | 228 messages.warning('file should start with <!DOCTYPE html> ' |
485 'to avoid the possibility of it being parsed in quirks mode in IE. ' | 229 'to avoid the possibility of it being parsed in quirks mode in IE. ' |
486 'See http://www.w3.org/TR/html5-diff/#doctype', doctype.sourceSpan); | 230 'See http://www.w3.org/TR/html5-diff/#doctype', doctype.sourceSpan); |
487 } | 231 } |
488 } | 232 } |
489 document.nodes.insert(commentIndex, parseFragment( | 233 document.nodes.insert(commentIndex, parseFragment( |
490 '\n<!-- This file was auto-generated from $filePath. -->\n')); | 234 '\n<!-- This file was auto-generated from $filePath. -->\n')); |
491 } | 235 } |
492 | |
493 /** Header with common imports, used in every generated .dart file. */ | |
494 String _header(String filename, String libraryName) { | |
495 var lib = libraryName != null ? '\nlibrary $libraryName;\n' : ''; | |
496 return """ | |
497 // Auto-generated from $filename. | |
498 // DO NOT EDIT. | |
499 $lib | |
500 import 'dart:html' as autogenerated; | |
501 import 'dart:svg' as autogenerated_svg; | |
502 import 'package:mdv/mdv.dart' as autogenerated_mdv; | |
503 import 'package:observe/observe.dart' as __observe; | |
504 import 'package:polymer/polymer.dart' as autogenerated; | |
505 """; | |
506 } | |
OLD | NEW |