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 /// Common methods used by transfomers. | 5 /// Common methods used by transfomers. |
6 library polymer.src.build.common; | 6 library polymer.src.build.common; |
7 | 7 |
8 import 'dart:async'; | 8 import 'dart:async'; |
9 | 9 |
10 import 'package:analyzer/src/generated/ast.dart'; | 10 import 'package:analyzer/src/generated/ast.dart'; |
11 import 'package:analyzer/src/generated/error.dart'; | 11 import 'package:analyzer/src/generated/error.dart'; |
12 import 'package:analyzer/src/generated/parser.dart'; | 12 import 'package:analyzer/src/generated/parser.dart'; |
13 import 'package:analyzer/src/generated/scanner.dart'; | 13 import 'package:analyzer/src/generated/scanner.dart'; |
14 import 'package:barback/barback.dart'; | 14 import 'package:barback/barback.dart'; |
| 15 import 'package:code_transformers/messages/build_logger.dart'; |
15 import 'package:html5lib/dom.dart' show Document; | 16 import 'package:html5lib/dom.dart' show Document; |
16 import 'package:html5lib/parser.dart' show HtmlParser; | 17 import 'package:html5lib/parser.dart' show HtmlParser; |
| 18 import 'package:observe/transformer.dart' show ObservableTransformer; |
17 import 'package:path/path.dart' as path; | 19 import 'package:path/path.dart' as path; |
18 import 'package:observe/transformer.dart' show ObservableTransformer; | 20 |
| 21 import 'constants.dart'; |
| 22 import 'messages.dart'; |
| 23 |
| 24 export 'constants.dart'; |
19 | 25 |
20 const _ignoredErrors = const [ | 26 const _ignoredErrors = const [ |
21 'unexpected-dash-after-double-dash-in-comment', | 27 'unexpected-dash-after-double-dash-in-comment', |
22 'unexpected-char-in-comment', | 28 'unexpected-char-in-comment', |
23 ]; | 29 ]; |
24 | 30 |
25 /// Parses an HTML file [contents] and returns a DOM-like tree. Adds emitted | 31 /// Parses an HTML file [contents] and returns a DOM-like tree. Adds emitted |
26 /// error/warning to [logger]. | 32 /// error/warning to [logger]. |
27 Document _parseHtml(String contents, String sourcePath, TransformLogger logger, | 33 Document _parseHtml(String contents, String sourcePath, BuildLogger logger, |
28 {bool checkDocType: true, bool showWarnings: true}) { | 34 {bool checkDocType: true, bool showWarnings: true}) { |
29 // TODO(jmesserly): make HTTP encoding configurable | 35 // TODO(jmesserly): make HTTP encoding configurable |
30 var parser = new HtmlParser(contents, encoding: 'utf8', | 36 var parser = new HtmlParser(contents, encoding: 'utf8', |
31 generateSpans: true, sourceUrl: sourcePath); | 37 generateSpans: true, sourceUrl: sourcePath); |
32 var document = parser.parse(); | 38 var document = parser.parse(); |
33 | 39 |
34 // Note: errors aren't fatal in HTML (unless strict mode is on). | 40 // Note: errors aren't fatal in HTML (unless strict mode is on). |
35 // So just print them as warnings. | 41 // So just print them as warnings. |
36 if (showWarnings) { | 42 if (showWarnings) { |
37 for (var e in parser.errors) { | 43 for (var e in parser.errors) { |
38 if (_ignoredErrors.contains(e.errorCode)) continue; | 44 if (_ignoredErrors.contains(e.errorCode)) continue; |
39 if (checkDocType || e.errorCode != 'expected-doctype-but-got-start-tag') { | 45 if (checkDocType || e.errorCode != 'expected-doctype-but-got-start-tag') { |
40 logger.warning(e.message, span: e.span); | 46 logger.warning(HTML5_WARNING.create({'message': e.message}), |
| 47 span: e.span); |
41 } | 48 } |
42 } | 49 } |
43 } | 50 } |
44 return document; | 51 return document; |
45 } | 52 } |
46 | 53 |
47 /// Additional options used by polymer transformers | 54 /// Additional options used by polymer transformers |
48 class TransformOptions { | 55 class TransformOptions { |
49 /// List of entrypoints paths. The paths are relative to the package root and | 56 /// List of entrypoints paths. The paths are relative to the package root and |
50 /// are represented using posix style, which matches the representation used | 57 /// are represented using posix style, which matches the representation used |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
119 // Then check the global default setting. | 126 // Then check the global default setting. |
120 var globalDefault = inlineStylesheets['default']; | 127 var globalDefault = inlineStylesheets['default']; |
121 return (globalDefault != null) ? globalDefault : true; | 128 return (globalDefault != null) ? globalDefault : true; |
122 } | 129 } |
123 } | 130 } |
124 | 131 |
125 /// Mixin for polymer transformers. | 132 /// Mixin for polymer transformers. |
126 abstract class PolymerTransformer { | 133 abstract class PolymerTransformer { |
127 TransformOptions get options; | 134 TransformOptions get options; |
128 | 135 |
129 Future<Document> readPrimaryAsHtml(Transform transform) { | 136 Future<Document> readPrimaryAsHtml(Transform transform, BuildLogger logger) { |
130 var asset = transform.primaryInput; | 137 var asset = transform.primaryInput; |
131 var id = asset.id; | 138 var id = asset.id; |
132 return asset.readAsString().then((content) { | 139 return asset.readAsString().then((content) { |
133 return _parseHtml(content, id.path, transform.logger, | 140 return _parseHtml(content, id.path, logger, |
134 checkDocType: options.isHtmlEntryPoint(id)); | 141 checkDocType: options.isHtmlEntryPoint(id)); |
135 }); | 142 }); |
136 } | 143 } |
137 | 144 |
138 Future<Document> readAsHtml(AssetId id, Transform transform, | 145 Future<Document> readAsHtml(AssetId id, Transform transform, |
| 146 BuildLogger logger, |
139 {bool showWarnings: true}) { | 147 {bool showWarnings: true}) { |
140 var primaryId = transform.primaryInput.id; | 148 var primaryId = transform.primaryInput.id; |
141 bool samePackage = id.package == primaryId.package; | 149 bool samePackage = id.package == primaryId.package; |
142 var url = spanUrlFor(id, transform); | 150 var url = spanUrlFor(id, transform, logger); |
143 return transform.readInputAsString(id).then((content) { | 151 return transform.readInputAsString(id).then((content) { |
144 return _parseHtml(content, url, transform.logger, | 152 return _parseHtml(content, url, logger, |
145 checkDocType: samePackage && options.isHtmlEntryPoint(id), | 153 checkDocType: samePackage && options.isHtmlEntryPoint(id), |
146 showWarnings: showWarnings); | 154 showWarnings: showWarnings); |
147 }); | 155 }); |
148 } | 156 } |
149 | 157 |
150 Future<bool> assetExists(AssetId id, Transform transform) => | 158 Future<bool> assetExists(AssetId id, Transform transform) => |
151 transform.getInput(id).then((_) => true).catchError((_) => false); | 159 transform.getInput(id).then((_) => true).catchError((_) => false); |
152 | 160 |
153 String toString() => 'polymer ($runtimeType)'; | 161 String toString() => 'polymer ($runtimeType)'; |
154 } | 162 } |
155 | 163 |
156 /// Gets the appropriate URL to use in a span to produce messages (e.g. | 164 /// Gets the appropriate URL to use in a span to produce messages (e.g. |
157 /// warnings) for users. This will attempt to format the URL in the most useful | 165 /// warnings) for users. This will attempt to format the URL in the most useful |
158 /// way: | 166 /// way: |
159 /// | 167 /// |
160 /// - If the asset is within the primary package, then use the [id.path], | 168 /// - If the asset is within the primary package, then use the [id.path], |
161 /// the user will know it is a file from their own code. | 169 /// the user will know it is a file from their own code. |
162 /// - If the asset is from another package, then use [assetUrlFor], this will | 170 /// - If the asset is from another package, then use [assetUrlFor], this will |
163 /// likely be a "package:" url to the file in the other package, which is | 171 /// likely be a "package:" url to the file in the other package, which is |
164 /// enough for users to identify where the error is. | 172 /// enough for users to identify where the error is. |
165 String spanUrlFor(AssetId id, Transform transform) { | 173 String spanUrlFor(AssetId id, Transform transform, logger) { |
166 var primaryId = transform.primaryInput.id; | 174 var primaryId = transform.primaryInput.id; |
167 bool samePackage = id.package == primaryId.package; | 175 bool samePackage = id.package == primaryId.package; |
168 return samePackage ? id.path | 176 return samePackage ? id.path |
169 : assetUrlFor(id, primaryId, transform.logger, allowAssetUrl: true); | 177 : assetUrlFor(id, primaryId, logger, allowAssetUrl: true); |
170 } | 178 } |
171 | 179 |
172 /// Transformer phases which should be applied to the Polymer package. | 180 /// Transformer phases which should be applied to the Polymer package. |
173 List<List<Transformer>> get phasesForPolymer => | 181 List<List<Transformer>> get phasesForPolymer => |
174 [[new ObservableTransformer(['lib/src/instance.dart'])]]; | 182 [[new ObservableTransformer(['lib/src/instance.dart'])]]; |
175 | 183 |
176 /// Generate the import url for a file described by [id], referenced by a file | 184 /// Generate the import url for a file described by [id], referenced by a file |
177 /// with [sourceId]. | 185 /// with [sourceId]. |
178 // TODO(sigmund): this should also be in barback (dartbug.com/12610) | 186 // TODO(sigmund): this should also be in barback (dartbug.com/12610) |
179 String assetUrlFor(AssetId id, AssetId sourceId, TransformLogger logger, | 187 String assetUrlFor(AssetId id, AssetId sourceId, BuildLogger logger, |
180 {bool allowAssetUrl: false}) { | 188 {bool allowAssetUrl: false}) { |
181 // use package: and asset: urls if possible | 189 // use package: and asset: urls if possible |
182 if (id.path.startsWith('lib/')) { | 190 if (id.path.startsWith('lib/')) { |
183 return 'package:${id.package}/${id.path.substring(4)}'; | 191 return 'package:${id.package}/${id.path.substring(4)}'; |
184 } | 192 } |
185 | 193 |
186 if (id.path.startsWith('asset/')) { | 194 if (id.path.startsWith('asset/')) { |
187 if (!allowAssetUrl) { | 195 if (!allowAssetUrl) { |
188 logger.error("asset urls not allowed. " | 196 logger.error(INTERNAL_ERROR_DONT_KNOW_HOW_TO_IMPORT.create({ |
189 "Don't know how to refer to $id from $sourceId"); | 197 'target': id, |
| 198 'source': sourceId, |
| 199 'extra': ' (asset urls not allowed.)'})); |
190 return null; | 200 return null; |
191 } | 201 } |
192 return 'asset:${id.package}/${id.path.substring(6)}'; | 202 return 'asset:${id.package}/${id.path.substring(6)}'; |
193 } | 203 } |
194 | 204 |
195 // Use relative urls only if it's possible. | 205 // Use relative urls only if it's possible. |
196 if (id.package != sourceId.package) { | 206 if (id.package != sourceId.package) { |
197 logger.error("don't know how to refer to $id from $sourceId"); | 207 logger.error("don't know how to refer to $id from $sourceId"); |
198 return null; | 208 return null; |
199 } | 209 } |
200 | 210 |
201 var builder = path.url; | 211 var builder = path.url; |
202 return builder.relative(builder.join('/', id.path), | 212 return builder.relative(builder.join('/', id.path), |
203 from: builder.join('/', builder.dirname(sourceId.path))); | 213 from: builder.join('/', builder.dirname(sourceId.path))); |
204 } | 214 } |
205 | 215 |
206 | 216 |
207 /// Convert system paths to asset paths (asset paths are posix style). | 217 /// Convert system paths to asset paths (asset paths are posix style). |
208 String systemToAssetPath(String assetPath) { | 218 String systemToAssetPath(String assetPath) { |
209 if (path.Style.platform != path.Style.windows) return assetPath; | 219 if (path.Style.platform != path.Style.windows) return assetPath; |
210 return path.posix.joinAll(path.split(assetPath)); | 220 return path.posix.joinAll(path.split(assetPath)); |
211 } | 221 } |
212 | 222 |
213 /// These names have meaning in SVG or MathML, so they aren't allowed as custom | |
214 /// tags. See [isCustomTagName]. | |
215 const invalidTagNames = const { | |
216 'annotation-xml': '', | |
217 'color-profile': '', | |
218 'font-face': '', | |
219 'font-face-src': '', | |
220 'font-face-uri': '', | |
221 'font-face-format': '', | |
222 'font-face-name': '', | |
223 'missing-glyph': '', | |
224 }; | |
225 | |
226 /// Returns true if this is a valid custom element name. See: | 223 /// Returns true if this is a valid custom element name. See: |
227 /// <http://w3c.github.io/webcomponents/spec/custom/#dfn-custom-element-type> | 224 /// <http://w3c.github.io/webcomponents/spec/custom/#dfn-custom-element-type> |
228 bool isCustomTagName(String name) { | 225 bool isCustomTagName(String name) { |
229 if (name == null || !name.contains('-')) return false; | 226 if (name == null || !name.contains('-')) return false; |
230 return !invalidTagNames.containsKey(name); | 227 return !invalidTagNames.containsKey(name); |
231 } | 228 } |
232 | 229 |
233 /// Regex to split names in the 'attributes' attribute, which supports 'a b c', | 230 /// Regex to split names in the 'attributes' attribute, which supports 'a b c', |
234 /// 'a,b,c', or even 'a b,c'. This is the same as in `lib/src/declaration.dart`. | 231 /// 'a,b,c', or even 'a b,c'. This is the same as in `lib/src/declaration.dart`. |
235 final ATTRIBUTES_REGEX = new RegExp(r'\s|,'); | 232 final ATTRIBUTES_REGEX = new RegExp(r'\s|,'); |
236 | |
237 const POLYMER_EXPERIMENTAL_HTML = 'packages/polymer/polymer_experimental.html'; | |
238 | |
239 const String LOG_EXTENSION = '._buildLogs'; | |
OLD | NEW |