| 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 import 'dart:math' show min, max; | 9 import 'dart:math' show min, max; |
| 10 | 10 |
| 11 import 'package:barback/barback.dart'; | 11 import 'package:barback/barback.dart'; |
| 12 import 'package:html5lib/dom.dart' show Document; | 12 import 'package:html5lib/dom.dart' show Document; |
| 13 import 'package:html5lib/parser.dart' show HtmlParser; | 13 import 'package:html5lib/parser.dart' show HtmlParser; |
| 14 import 'package:path/path.dart' as path; | 14 import 'package:path/path.dart' as path; |
| 15 import 'package:observe/transformer.dart' show ObservableTransformer; | 15 import 'package:observe/transformer.dart' show ObservableTransformer; |
| 16 import 'package:source_maps/span.dart' show Span; | 16 import 'package:source_maps/span.dart' show Span; |
| 17 | 17 |
| 18 /** | 18 /// Parses an HTML file [contents] and returns a DOM-like tree. Adds emitted |
| 19 * Parses an HTML file [contents] and returns a DOM-like tree. Adds emitted | 19 /// error/warning to [logger]. |
| 20 * error/warning to [logger]. | |
| 21 */ | |
| 22 Document _parseHtml(String contents, String sourcePath, TransformLogger logger, | 20 Document _parseHtml(String contents, String sourcePath, TransformLogger logger, |
| 23 {bool checkDocType: true}) { | 21 {bool checkDocType: true}) { |
| 24 // TODO(jmesserly): make HTTP encoding configurable | 22 // TODO(jmesserly): make HTTP encoding configurable |
| 25 var parser = new HtmlParser(contents, encoding: 'utf8', generateSpans: true, | 23 var parser = new HtmlParser(contents, encoding: 'utf8', generateSpans: true, |
| 26 sourceUrl: sourcePath); | 24 sourceUrl: sourcePath); |
| 27 var document = parser.parse(); | 25 var document = parser.parse(); |
| 28 | 26 |
| 29 // Note: errors aren't fatal in HTML (unless strict mode is on). | 27 // Note: errors aren't fatal in HTML (unless strict mode is on). |
| 30 // So just print them as warnings. | 28 // So just print them as warnings. |
| 31 for (var e in parser.errors) { | 29 for (var e in parser.errors) { |
| 32 if (checkDocType || e.errorCode != 'expected-doctype-but-got-start-tag') { | 30 if (checkDocType || e.errorCode != 'expected-doctype-but-got-start-tag') { |
| 33 logger.warning(e.message, span: e.span); | 31 logger.warning(e.message, span: e.span); |
| 34 } | 32 } |
| 35 } | 33 } |
| 36 return document; | 34 return document; |
| 37 } | 35 } |
| 38 | 36 |
| 39 /** Additional options used by polymer transformers */ | 37 /// Additional options used by polymer transformers |
| 40 class TransformOptions { | 38 class TransformOptions { |
| 41 /** | 39 /// List of entrypoints paths. The paths are relative to the package root and |
| 42 * List of entrypoints paths. The paths are relative to the package root and | 40 /// are represented using posix style, which matches the representation used |
| 43 * are represented using posix style, which matches the representation used in | 41 /// in asset ids in barback. If null, anything under 'web/' or 'test/' is |
| 44 * asset ids in barback. If null, anything under 'web/' or 'test/' is | 42 /// considered an entry point. |
| 45 * considered an entry point. | |
| 46 */ | |
| 47 final List<String> entryPoints; | 43 final List<String> entryPoints; |
| 48 | 44 |
| 49 /** | 45 /// True to enable Content Security Policy. |
| 50 * True to enable Content Security Policy. | 46 /// This means the HTML page will include *.dart.precompiled.js |
| 51 * This means the HTML page will include *.dart.precompiled.js | 47 /// |
| 52 * | 48 /// This flag has no effect unless [directlyIncludeJS] is enabled. |
| 53 * This flag has no effect unless [directlyIncludeJS] is enabled. | |
| 54 */ | |
| 55 final bool contentSecurityPolicy; | 49 final bool contentSecurityPolicy; |
| 56 | 50 |
| 57 /** | 51 /// True to include the compiled JavaScript directly from the HTML page. |
| 58 * True to include the compiled JavaScript directly from the HTML page. | 52 /// If enabled this will remove "packages/browser/dart.js" and replace |
| 59 * If enabled this will remove "packages/browser/dart.js" and replace | 53 /// `type="application/dart"` scripts with equivalent *.dart.js files. |
| 60 * `type="application/dart"` scripts with equivalent *.dart.js files. | 54 /// |
| 61 * | 55 /// If [contentSecurityPolicy] enabled, this will reference files |
| 62 * If [contentSecurityPolicy] enabled, this will reference files | 56 /// named *.dart.precompiled.js. |
| 63 * named *.dart.precompiled.js. | |
| 64 */ | |
| 65 final bool directlyIncludeJS; | 57 final bool directlyIncludeJS; |
| 66 | 58 |
| 67 /** | 59 /// Run transformers to create a releasable app. For example, include the |
| 68 * Run transformers to create a releasable app. For example, include the | 60 /// minified versions of the polyfills rather than the debug versions. |
| 69 * minified versions of the polyfills rather than the debug versions. | |
| 70 */ | |
| 71 final bool releaseMode; | 61 final bool releaseMode; |
| 72 | 62 |
| 73 TransformOptions({entryPoints, this.contentSecurityPolicy: false, | 63 TransformOptions({entryPoints, this.contentSecurityPolicy: false, |
| 74 this.directlyIncludeJS: true, this.releaseMode: true}) | 64 this.directlyIncludeJS: true, this.releaseMode: true}) |
| 75 : entryPoints = entryPoints == null ? null | 65 : entryPoints = entryPoints == null ? null |
| 76 : entryPoints.map(_systemToAssetPath).toList(); | 66 : entryPoints.map(_systemToAssetPath).toList(); |
| 77 | 67 |
| 78 /** Whether an asset with [id] is an entry point HTML file. */ | 68 /// Whether an asset with [id] is an entry point HTML file. |
| 79 bool isHtmlEntryPoint(AssetId id) { | 69 bool isHtmlEntryPoint(AssetId id) { |
| 80 if (id.extension != '.html') return false; | 70 if (id.extension != '.html') return false; |
| 81 | 71 |
| 82 // Note: [id.path] is a relative path from the root of a package. | 72 // Note: [id.path] is a relative path from the root of a package. |
| 83 if (entryPoints == null) { | 73 if (entryPoints == null) { |
| 84 return id.path.startsWith('web/') || id.path.startsWith('test/'); | 74 return id.path.startsWith('web/') || id.path.startsWith('test/'); |
| 85 } | 75 } |
| 86 | 76 |
| 87 return entryPoints.contains(id.path); | 77 return entryPoints.contains(id.path); |
| 88 } | 78 } |
| 89 } | 79 } |
| 90 | 80 |
| 91 /** Mixin for polymer transformers. */ | 81 /// Mixin for polymer transformers. |
| 92 abstract class PolymerTransformer { | 82 abstract class PolymerTransformer { |
| 93 TransformOptions get options; | 83 TransformOptions get options; |
| 94 | 84 |
| 95 Future<Document> readPrimaryAsHtml(Transform transform) { | 85 Future<Document> readPrimaryAsHtml(Transform transform) { |
| 96 var asset = transform.primaryInput; | 86 var asset = transform.primaryInput; |
| 97 var id = asset.id; | 87 var id = asset.id; |
| 98 return asset.readAsString().then((content) { | 88 return asset.readAsString().then((content) { |
| 99 return _parseHtml(content, id.path, transform.logger, | 89 return _parseHtml(content, id.path, transform.logger, |
| 100 checkDocType: options.isHtmlEntryPoint(id)); | 90 checkDocType: options.isHtmlEntryPoint(id)); |
| 101 }); | 91 }); |
| 102 } | 92 } |
| 103 | 93 |
| 104 Future<Document> readAsHtml(AssetId id, Transform transform) { | 94 Future<Document> readAsHtml(AssetId id, Transform transform) { |
| 105 var primaryId = transform.primaryInput.id; | 95 var primaryId = transform.primaryInput.id; |
| 106 bool samePackage = id.package == primaryId.package; | 96 bool samePackage = id.package == primaryId.package; |
| 107 var url = spanUrlFor(id, transform); | 97 var url = spanUrlFor(id, transform); |
| 108 return transform.readInputAsString(id).then((content) { | 98 return transform.readInputAsString(id).then((content) { |
| 109 return _parseHtml(content, url, transform.logger, | 99 return _parseHtml(content, url, transform.logger, |
| 110 checkDocType: samePackage && options.isHtmlEntryPoint(id)); | 100 checkDocType: samePackage && options.isHtmlEntryPoint(id)); |
| 111 }); | 101 }); |
| 112 } | 102 } |
| 113 | 103 |
| 114 /** | 104 /// Gets the appropriate URL to use in a [Span] to produce messages |
| 115 * Gets the appropriate URL to use in a [Span] to produce messages | 105 /// (e.g. warnings) for users. This will attempt to format the URL in the most |
| 116 * (e.g. warnings) for users. This will attempt to format the URL in the most | 106 /// useful way: |
| 117 * useful way: | 107 /// |
| 118 * | 108 /// - If the asset is within the primary package, then use the [id.path], |
| 119 * - If the asset is within the primary package, then use the [id.path], | 109 /// the user will know it is a file from their own code. |
| 120 * the user will know it is a file from their own code. | 110 /// - If the asset is from another package, then use [assetUrlFor], this will |
| 121 * - If the asset is from another package, then use [assetUrlFor], this will | 111 /// likely be a "package:" url to the file in the other package, which is |
| 122 * likely be a "package:" url to the file in the other package, which is | 112 /// enough for users to identify where the error is. |
| 123 * enough for users to identify where the error is. | |
| 124 */ | |
| 125 String spanUrlFor(AssetId id, Transform transform) { | 113 String spanUrlFor(AssetId id, Transform transform) { |
| 126 var primaryId = transform.primaryInput.id; | 114 var primaryId = transform.primaryInput.id; |
| 127 bool samePackage = id.package == primaryId.package; | 115 bool samePackage = id.package == primaryId.package; |
| 128 return samePackage ? id.path | 116 return samePackage ? id.path |
| 129 : assetUrlFor(id, primaryId, transform.logger, allowAssetUrl: true); | 117 : assetUrlFor(id, primaryId, transform.logger, allowAssetUrl: true); |
| 130 } | 118 } |
| 131 | 119 |
| 132 Future<bool> assetExists(AssetId id, Transform transform) => | 120 Future<bool> assetExists(AssetId id, Transform transform) => |
| 133 transform.getInput(id).then((_) => true).catchError((_) => false); | 121 transform.getInput(id).then((_) => true).catchError((_) => false); |
| 134 | 122 |
| 135 String toString() => 'polymer ($runtimeType)'; | 123 String toString() => 'polymer ($runtimeType)'; |
| 136 } | 124 } |
| 137 | 125 |
| 138 /** Transformer phases which should be applied to the Polymer package. */ | 126 /// Transformer phases which should be applied to the Polymer package. |
| 139 List<List<Transformer>> get phasesForPolymer => | 127 List<List<Transformer>> get phasesForPolymer => |
| 140 [[new ObservableTransformer(['lib/src/instance.dart'])]]; | 128 [[new ObservableTransformer(['lib/src/instance.dart'])]]; |
| 141 | 129 |
| 142 /** | 130 /// Create an [AssetId] for a [url] seen in the [source] asset. By default this |
| 143 * Create an [AssetId] for a [url] seen in the [source] asset. By default this | 131 /// is used to resolve relative urls that occur in HTML assets, including |
| 144 * is used to resolve relative urls that occur in HTML assets, including | 132 /// cross-package urls of the form "packages/foo/bar.html". Dart "package:" |
| 145 * cross-package urls of the form "packages/foo/bar.html". Dart-style "package:" | 133 /// urls are not resolved unless [source] is Dart file (has a .dart extension). |
| 146 * urls are not resolved unless [source] is Dart file (has a .dart extension). | |
| 147 */ | |
| 148 // TODO(sigmund): delete once this is part of barback (dartbug.com/12610) | 134 // TODO(sigmund): delete once this is part of barback (dartbug.com/12610) |
| 149 AssetId resolve(AssetId source, String url, TransformLogger logger, Span span, | 135 AssetId resolve(AssetId source, String url, TransformLogger logger, Span span, |
| 150 {bool allowAbsolute: false}) { | 136 {bool allowAbsolute: false}) { |
| 151 if (url == null || url == '') return null; | 137 if (url == null || url == '') return null; |
| 152 var uri = Uri.parse(url); | 138 var uri = Uri.parse(url); |
| 153 var urlBuilder = path.url; | 139 var urlBuilder = path.url; |
| 154 if (uri.host != '' || uri.scheme != '' || urlBuilder.isAbsolute(url)) { | 140 if (uri.host != '' || uri.scheme != '' || urlBuilder.isAbsolute(url)) { |
| 155 if (source.extension == '.dart' && uri.scheme == 'package') { | 141 if (source.extension == '.dart' && uri.scheme == 'package') { |
| 156 var index = uri.path.indexOf('/'); | 142 var index = uri.path.indexOf('/'); |
| 157 if (index != -1) { | 143 if (index != -1) { |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 221 var folder = prefix == 'packages' ? 'lib' : 'asset'; | 207 var folder = prefix == 'packages' ? 'lib' : 'asset'; |
| 222 if (segments.length < index + 3) { | 208 if (segments.length < index + 3) { |
| 223 logger.error("incomplete $prefix/ path. It should have at least 3 " | 209 logger.error("incomplete $prefix/ path. It should have at least 3 " |
| 224 "segments $prefix/name/path-from-name's-$folder-dir", span: span); | 210 "segments $prefix/name/path-from-name's-$folder-dir", span: span); |
| 225 return null; | 211 return null; |
| 226 } | 212 } |
| 227 return new AssetId(segments[index + 1], | 213 return new AssetId(segments[index + 1], |
| 228 path.url.join(folder, path.url.joinAll(segments.sublist(index + 2)))); | 214 path.url.join(folder, path.url.joinAll(segments.sublist(index + 2)))); |
| 229 } | 215 } |
| 230 | 216 |
| 231 /** | 217 /// Generate the import url for a file described by [id], referenced by a file |
| 232 * Generate the import url for a file described by [id], referenced by a file | 218 /// with [sourceId]. |
| 233 * with [sourceId]. | |
| 234 */ | |
| 235 // TODO(sigmund): this should also be in barback (dartbug.com/12610) | 219 // TODO(sigmund): this should also be in barback (dartbug.com/12610) |
| 236 String assetUrlFor(AssetId id, AssetId sourceId, TransformLogger logger, | 220 String assetUrlFor(AssetId id, AssetId sourceId, TransformLogger logger, |
| 237 {bool allowAssetUrl: false}) { | 221 {bool allowAssetUrl: false}) { |
| 238 // use package: and asset: urls if possible | 222 // use package: and asset: urls if possible |
| 239 if (id.path.startsWith('lib/')) { | 223 if (id.path.startsWith('lib/')) { |
| 240 return 'package:${id.package}/${id.path.substring(4)}'; | 224 return 'package:${id.package}/${id.path.substring(4)}'; |
| 241 } | 225 } |
| 242 | 226 |
| 243 if (id.path.startsWith('asset/')) { | 227 if (id.path.startsWith('asset/')) { |
| 244 if (!allowAssetUrl) { | 228 if (!allowAssetUrl) { |
| 245 logger.error("asset urls not allowed. " | 229 logger.error("asset urls not allowed. " |
| 246 "Don't know how to refer to $id from $sourceId"); | 230 "Don't know how to refer to $id from $sourceId"); |
| 247 return null; | 231 return null; |
| 248 } | 232 } |
| 249 return 'asset:${id.package}/${id.path.substring(6)}'; | 233 return 'asset:${id.package}/${id.path.substring(6)}'; |
| 250 } | 234 } |
| 251 | 235 |
| 252 // Use relative urls only if it's possible. | 236 // Use relative urls only if it's possible. |
| 253 if (id.package != sourceId.package) { | 237 if (id.package != sourceId.package) { |
| 254 logger.error("don't know how to refer to $id from $sourceId"); | 238 logger.error("don't know how to refer to $id from $sourceId"); |
| 255 return null; | 239 return null; |
| 256 } | 240 } |
| 257 | 241 |
| 258 var builder = path.url; | 242 var builder = path.url; |
| 259 return builder.relative(builder.join('/', id.path), | 243 return builder.relative(builder.join('/', id.path), |
| 260 from: builder.join('/', builder.dirname(sourceId.path))); | 244 from: builder.join('/', builder.dirname(sourceId.path))); |
| 261 } | 245 } |
| 262 | 246 |
| 263 | 247 |
| 264 /** Convert system paths to asset paths (asset paths are posix style). */ | 248 /// Convert system paths to asset paths (asset paths are posix style). |
| 265 String _systemToAssetPath(String assetPath) { | 249 String _systemToAssetPath(String assetPath) { |
| 266 if (path.Style.platform != path.Style.windows) return assetPath; | 250 if (path.Style.platform != path.Style.windows) return assetPath; |
| 267 return path.posix.joinAll(path.split(assetPath)); | 251 return path.posix.joinAll(path.split(assetPath)); |
| 268 } | 252 } |
| OLD | NEW |