OLD | NEW |
| (Empty) |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 /** Common methods used by transfomers. */ | |
6 library polymer.src.transform.common; | |
7 | |
8 import 'dart:async'; | |
9 | |
10 import 'package:barback/barback.dart'; | |
11 import 'package:html5lib/dom.dart' show Document; | |
12 import 'package:html5lib/parser.dart' show HtmlParser; | |
13 import 'package:path/path.dart' as path; | |
14 import 'package:source_maps/span.dart' show Span; | |
15 | |
16 /** | |
17 * Parses an HTML file [contents] and returns a DOM-like tree. Adds emitted | |
18 * error/warning to [logger]. | |
19 */ | |
20 Document _parseHtml(String contents, String sourcePath, TransformLogger logger, | |
21 {bool checkDocType: true}) { | |
22 // TODO(jmesserly): make HTTP encoding configurable | |
23 var parser = new HtmlParser(contents, encoding: 'utf8', generateSpans: true, | |
24 sourceUrl: sourcePath); | |
25 var document = parser.parse(); | |
26 | |
27 // Note: errors aren't fatal in HTML (unless strict mode is on). | |
28 // So just print them as warnings. | |
29 for (var e in parser.errors) { | |
30 if (checkDocType || e.errorCode != 'expected-doctype-but-got-start-tag') { | |
31 logger.warning(e.message, e.span); | |
32 } | |
33 } | |
34 return document; | |
35 } | |
36 | |
37 /** Additional options used by polymer transformers */ | |
38 class TransformOptions { | |
39 String currentPackage; | |
40 List<String> entryPoints; | |
41 | |
42 TransformOptions([this.currentPackage, this.entryPoints]); | |
43 | |
44 /** Whether an asset with [id] is an entry point HTML file. */ | |
45 bool isHtmlEntryPoint(AssetId id) { | |
46 if (id.extension != '.html') return false; | |
47 | |
48 // Note: [id.path] is a relative path from the root of a package. | |
49 if (!id.path.startsWith('web/') && | |
50 !id.path.startsWith('test/')) return false; | |
51 | |
52 if (currentPackage == null || entryPoints == null) return true; | |
53 return id.package == currentPackage && entryPoints.contains(id.path); | |
54 } | |
55 } | |
56 | |
57 /** Mixin for polymer transformers. */ | |
58 abstract class PolymerTransformer { | |
59 TransformOptions get options; | |
60 | |
61 Future<Document> readPrimaryAsHtml(Transform transform) { | |
62 var asset = transform.primaryInput; | |
63 var id = asset.id; | |
64 return asset.readAsString().then((content) { | |
65 return _parseHtml(content, id.path, transform.logger, | |
66 checkDocType: options.isHtmlEntryPoint(id)); | |
67 }); | |
68 } | |
69 | |
70 Future<Document> readAsHtml(AssetId id, Transform transform) { | |
71 var primaryId = transform.primaryInput.id; | |
72 var url = (id.package == primaryId.package) ? id.path | |
73 : assetUrlFor(id, primaryId, transform.logger, allowAssetUrl: true); | |
74 return transform.readInputAsString(id).then((content) { | |
75 return _parseHtml(content, url, transform.logger, | |
76 checkDocType: options.isHtmlEntryPoint(id)); | |
77 }); | |
78 } | |
79 } | |
80 | |
81 /** Create an [AssetId] for a [url] seen in the [source] asset. */ | |
82 // TODO(sigmund): delete once this is part of barback (dartbug.com/12610) | |
83 AssetId resolve(AssetId source, String url, TransformLogger logger, Span span) { | |
84 if (url == null || url == '') return null; | |
85 var uri = Uri.parse(url); | |
86 var urlBuilder = path.url; | |
87 if (uri.host != '' || uri.scheme != '' || urlBuilder.isAbsolute(url)) { | |
88 logger.error('absolute paths not allowed: "$url"', span); | |
89 return null; | |
90 } | |
91 | |
92 var package; | |
93 var targetPath; | |
94 var segments = urlBuilder.split(url); | |
95 if (segments[0] == 'packages') { | |
96 if (segments.length < 3) { | |
97 logger.error("incomplete packages/ path. It should have at least 3 " | |
98 "segments packages/name/path-from-name's-lib-dir", span); | |
99 return null; | |
100 } | |
101 package = segments[1]; | |
102 targetPath = urlBuilder.join('lib', | |
103 urlBuilder.joinAll(segments.sublist(2))); | |
104 } else if (segments[0] == 'assets') { | |
105 if (segments.length < 3) { | |
106 logger.error("incomplete assets/ path. It should have at least 3 " | |
107 "segments assets/name/path-from-name's-asset-dir", span); | |
108 } | |
109 package = segments[1]; | |
110 targetPath = urlBuilder.join('asset', | |
111 urlBuilder.joinAll(segments.sublist(2))); | |
112 } else { | |
113 package = source.package; | |
114 targetPath = urlBuilder.normalize( | |
115 urlBuilder.join(urlBuilder.dirname(source.path), url)); | |
116 } | |
117 return new AssetId(package, targetPath); | |
118 } | |
119 | |
120 /** | |
121 * Generate the import url for a file described by [id], referenced by a file | |
122 * with [sourceId]. | |
123 */ | |
124 // TODO(sigmund): this should also be in barback (dartbug.com/12610) | |
125 String assetUrlFor(AssetId id, AssetId sourceId, TransformLogger logger, | |
126 {bool allowAssetUrl: false}) { | |
127 // use package: and asset: urls if possible | |
128 if (id.path.startsWith('lib/')) { | |
129 return 'package:${id.package}/${id.path.substring(4)}'; | |
130 } | |
131 | |
132 if (id.path.startsWith('asset/')) { | |
133 if (!allowAssetUrl) { | |
134 logger.error("asset urls not allowed. " | |
135 "Don't know how to refer to $id from $sourceId"); | |
136 return null; | |
137 } | |
138 return 'asset:${id.package}/${id.path.substring(6)}'; | |
139 } | |
140 | |
141 // Use relative urls only if it's possible. | |
142 if (id.package != sourceId.package) { | |
143 logger.error("don't know how to refer to $id from $sourceId"); | |
144 return null; | |
145 } | |
146 | |
147 var builder = path.url; | |
148 return builder.relative(builder.join('/', id.path), | |
149 from: builder.join('/', builder.dirname(sourceId.path))); | |
150 } | |
OLD | NEW |