| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 library web_components.build.script_compactor; | 4 library web_components.build.script_compactor; |
| 5 | 5 |
| 6 import 'dart:async'; | 6 import 'dart:async'; |
| 7 import 'package:analyzer/analyzer.dart'; | 7 import 'package:analyzer/analyzer.dart'; |
| 8 import 'package:barback/barback.dart'; | 8 import 'package:barback/barback.dart'; |
| 9 import 'package:code_transformers/assets.dart'; | 9 import 'package:code_transformers/assets.dart'; |
| 10 import 'package:code_transformers/messages/build_logger.dart'; | 10 import 'package:code_transformers/messages/build_logger.dart'; |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 101 }); | 101 }); |
| 102 } | 102 } |
| 103 | 103 |
| 104 /// Builds the bootstrap file and returns the path to it relative to | 104 /// Builds the bootstrap file and returns the path to it relative to |
| 105 /// [primaryInput]. | 105 /// [primaryInput]. |
| 106 Asset _buildBootstrapFile(AssetId mainScript, Set<AssetId> importScripts) { | 106 Asset _buildBootstrapFile(AssetId mainScript, Set<AssetId> importScripts) { |
| 107 var bootstrapId = new AssetId(primaryInput.package, | 107 var bootstrapId = new AssetId(primaryInput.package, |
| 108 primaryInput.path.replaceFirst('.html', '.bootstrap.dart')); | 108 primaryInput.path.replaceFirst('.html', '.bootstrap.dart')); |
| 109 | 109 |
| 110 var buffer = new StringBuffer(); | 110 var buffer = new StringBuffer(); |
| 111 buffer.writeln('library ${_libraryNameFor(bootstrapId)};'); | 111 buffer.writeln('library ${_libraryNameFor(bootstrapId, logger)};'); |
| 112 buffer.writeln(); | 112 buffer.writeln(); |
| 113 var i = 0; | 113 var i = 0; |
| 114 for (var script in importScripts) { | 114 for (var script in importScripts) { |
| 115 var path = _importPath(script, primaryInput); | 115 var path = _importPath(script, primaryInput); |
| 116 buffer.writeln("import '$path' as i$i;"); | 116 buffer.writeln("import '$path' as i$i;"); |
| 117 i++; | 117 i++; |
| 118 } | 118 } |
| 119 var mainScriptPath = _importPath(mainScript, primaryInput); | 119 var mainScriptPath = _importPath(mainScript, primaryInput); |
| 120 buffer.writeln("import '$mainScriptPath' as i$i;"); | 120 buffer.writeln("import '$mainScriptPath' as i$i;"); |
| 121 buffer.writeln(); | 121 buffer.writeln(); |
| 122 buffer.writeln('main() => i$i.main();'); | 122 buffer.writeln('main() => i$i.main();'); |
| 123 | 123 |
| 124 var bootstrap = new Asset.fromString(bootstrapId, '$buffer'); | 124 var bootstrap = new Asset.fromString(bootstrapId, '$buffer'); |
| 125 transform.addOutput(bootstrap); | 125 transform.addOutput(bootstrap); |
| 126 return bootstrap; | 126 return bootstrap; |
| 127 } | 127 } |
| 128 | 128 |
| 129 /// Split inline scripts into their own files. We need to do this for dart2js | 129 /// Split inline scripts into their own files. We need to do this for dart2js |
| 130 /// to be able to compile them. | 130 /// to be able to compile them. |
| 131 /// | 131 /// |
| 132 /// This also validates that there weren't any duplicate scripts. | 132 /// This also validates that there weren't any duplicate scripts. |
| 133 Future _extractInlineScripts(AssetId asset, dom.Document doc) { | 133 Future _extractInlineScripts(AssetId asset, dom.Document doc) { |
| 134 var scripts = doc.querySelectorAll('script[type="$dartType"]'); | 134 var scripts = doc.querySelectorAll('script[type="$dartType"]'); |
| 135 return Future.forEach(scripts, (script) { | 135 return Future.forEach(scripts, (script) { |
| 136 var type = script.attributes['type']; | |
| 137 var src = script.attributes['src']; | 136 var src = script.attributes['src']; |
| 138 | 137 |
| 139 if (src != null) { | 138 if (src != null) { |
| 140 return _addScript( | 139 return _addScript( |
| 141 asset, uriToAssetId(asset, src, logger, script.sourceSpan), | 140 asset, uriToAssetId(asset, src, logger, script.sourceSpan), |
| 142 span: script.sourceSpan); | 141 span: script.sourceSpan); |
| 143 } | 142 } |
| 144 | 143 |
| 145 final count = inlineScriptCounter++; | 144 final count = inlineScriptCounter++; |
| 146 var code = script.text; | 145 var code = script.text; |
| 147 // TODO(sigmund): ensure this path is unique (dartbug.com/12618). | 146 // TODO(sigmund): ensure this path is unique (dartbug.com/12618). |
| 148 var newId = primaryInput.addExtension('.$count.dart'); | 147 var newId = primaryInput.addExtension('.$count.dart'); |
| 149 if (!_hasLibraryDirective(code)) { | 148 if (!_hasLibraryDirective(code)) { |
| 150 var libName = _libraryNameFor(primaryInput, count); | 149 var libName = _libraryNameFor(primaryInput, logger, count); |
| 151 code = "library $libName;\n$code"; | 150 code = "library $libName;\n$code"; |
| 152 } | 151 } |
| 153 | 152 |
| 154 // Normalize dart import paths. | 153 // Normalize dart import paths. |
| 155 code = _normalizeDartImports(code, asset, primaryInput); | 154 code = _normalizeDartImports(code, asset, primaryInput); |
| 156 | 155 |
| 157 // Write out the file and record it. | 156 // Write out the file and record it. |
| 158 transform.addOutput(new Asset.fromString(newId, code)); | 157 transform.addOutput(new Asset.fromString(newId, code)); |
| 159 | 158 |
| 160 return _addScript(asset, newId, validate: false).then((_) { | 159 return _addScript(asset, newId, validate: false).then((_) { |
| 161 // If in the entry point, replace the inline script with one pointing to | 160 // If in the entry point, replace the inline script with one pointing to |
| 162 // the new source file. | 161 // the new source file. |
| 163 if (primaryInput == asset) { | 162 if (primaryInput == asset) { |
| 164 script.text = ''; | 163 script.text = ''; |
| 165 script.attributes['src'] = path.url.relative(newId.path, | 164 script.attributes['src'] = path.url |
| 166 from: path.url.dirname(primaryInput.path)); | 165 .relative(newId.path, from: path.url.dirname(primaryInput.path)); |
| 167 } | 166 } |
| 168 }); | 167 }); |
| 169 }); | 168 }); |
| 170 } | 169 } |
| 171 | 170 |
| 172 // Normalize dart import paths when moving code from one asset to another. | 171 // Normalize dart import paths when moving code from one asset to another. |
| 173 String _normalizeDartImports(String code, AssetId from, AssetId to) { | 172 String _normalizeDartImports(String code, AssetId from, AssetId to) { |
| 174 var unit = parseDirectives(code, suppressErrors: true); | 173 var unit = parseDirectives(code, suppressErrors: true); |
| 175 var file = new SourceFile(code, url: spanUrlFor(from, to, logger)); | 174 var file = new SourceFile(code, url: spanUrlFor(from, to, logger)); |
| 176 var output = new TextEditTransaction(code, file); | 175 var output = new TextEditTransaction(code, file); |
| 177 var foundLibraryDirective = false; | |
| 178 for (Directive directive in unit.directives) { | 176 for (Directive directive in unit.directives) { |
| 179 if (directive is UriBasedDirective) { | 177 if (directive is UriBasedDirective) { |
| 180 var uri = directive.uri.stringValue; | 178 var uri = directive.uri.stringValue; |
| 181 var span = getSpan(file, directive.uri); | 179 var span = getSpan(file, directive.uri); |
| 182 | 180 |
| 183 var id = uriToAssetId(from, uri, logger, span, errorOnAbsolute: false); | 181 var id = uriToAssetId(from, uri, logger, span, errorOnAbsolute: false); |
| 184 if (id == null) continue; | 182 if (id == null) continue; |
| 185 | 183 |
| 186 var primaryId = primaryInput; | 184 var primaryId = primaryInput; |
| 187 var newUri = assetUrlFor(id, primaryId, logger); | 185 var newUri = assetUrlFor(id, primaryId, logger); |
| 188 if (newUri != uri) { | 186 if (newUri != uri) { |
| 189 output.edit(span.start.offset, span.end.offset, "'$newUri'"); | 187 output.edit(span.start.offset, span.end.offset, "'$newUri'"); |
| 190 } | 188 } |
| 191 } else if (directive is LibraryDirective) { | |
| 192 foundLibraryDirective = true; | |
| 193 } | 189 } |
| 194 } | 190 } |
| 195 | 191 |
| 196 if (!output.hasEdits) return code; | 192 if (!output.hasEdits) return code; |
| 197 | 193 |
| 198 // TODO(sigmund): emit source maps when barback supports it (see | 194 // TODO(sigmund): emit source maps when barback supports it (see |
| 199 // dartbug.com/12340) | 195 // dartbug.com/12340) |
| 200 return (output.commit()..build(file.url.toString())).text; | 196 return (output.commit()..build(file.url.toString())).text; |
| 201 } | 197 } |
| 202 | 198 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 221 } | 217 } |
| 222 mainScript = scriptId; | 218 mainScript = scriptId; |
| 223 } else { | 219 } else { |
| 224 importScripts.add(scriptId); | 220 importScripts.add(scriptId); |
| 225 } | 221 } |
| 226 }); | 222 }); |
| 227 } | 223 } |
| 228 } | 224 } |
| 229 | 225 |
| 230 /// Generate a library name for an asset. | 226 /// Generate a library name for an asset. |
| 231 String _libraryNameFor(AssetId id, [int suffix]) { | 227 String _libraryNameFor(AssetId id, BuildLogger logger, [int suffix]) { |
| 228 if (_isInvalidPackageName(id.package)) { |
| 229 logger.error('Invalid package name `${id.package}`. Package names should ' |
| 230 'be valid dart identifiers, as indicated at ' |
| 231 'https://www.dartlang.org/tools/pub/pubspec.html#name.'); |
| 232 } |
| 232 var name = '${path.withoutExtension(id.path)}_' | 233 var name = '${path.withoutExtension(id.path)}_' |
| 233 '${path.extension(id.path).substring(1)}'; | 234 '${path.extension(id.path).substring(1)}'; |
| 234 if (name.startsWith('lib/')) name = name.substring(4); | 235 if (name.startsWith('lib/')) name = name.substring(4); |
| 235 name = name.split('/').map((part) { | 236 validLibName(String name) { |
| 236 part = part.replaceAll(_invalidLibCharsRegex, '_'); | 237 name = name.replaceAll(_invalidLibCharsRegex, '_'); |
| 237 if (part.startsWith(_numRegex)) part = '_${part}'; | 238 if (name.startsWith(_numRegex)) name = '_${name}'; |
| 238 return part; | 239 return name; |
| 239 }).join("."); | 240 } |
| 241 name = name.split('/').map(validLibName).join("."); |
| 240 var suffixString = suffix != null ? '_$suffix' : ''; | 242 var suffixString = suffix != null ? '_$suffix' : ''; |
| 241 return '${id.package}.${name}$suffixString'; | 243 return '${id.package}.${name}$suffixString'; |
| 242 } | 244 } |
| 243 | 245 |
| 244 /// Parse [code] and determine whether it has a library directive. | 246 /// Parse [code] and determine whether it has a library directive. |
| 245 bool _hasLibraryDirective(String code) => | 247 bool _hasLibraryDirective(String code) => |
| 246 parseDirectives(code, suppressErrors: true).directives | 248 parseDirectives(code, suppressErrors: true) |
| 249 .directives |
| 247 .any((d) => d is LibraryDirective); | 250 .any((d) => d is LibraryDirective); |
| 248 | 251 |
| 249 /// Returns the dart import path to reach [id] relative to [primaryInput]. | 252 /// Returns the dart import path to reach [id] relative to [primaryInput]. |
| 250 String _importPath(AssetId id, AssetId primaryInput) { | 253 String _importPath(AssetId id, AssetId primaryInput) { |
| 251 var parts = path.url.split(id.path); | 254 var parts = path.url.split(id.path); |
| 252 if (parts[0] == 'lib') { | 255 if (parts[0] == 'lib') { |
| 253 parts[0] = id.package; | 256 parts[0] = id.package; |
| 254 return 'package:${path.url.joinAll(parts)}'; | 257 return 'package:${path.url.joinAll(parts)}'; |
| 255 } | 258 } |
| 256 return path.url.relative(id.path, from: path.url.dirname(primaryInput.path)); | 259 return path.url.relative(id.path, from: path.url.dirname(primaryInput.path)); |
| 257 } | 260 } |
| 258 | 261 |
| 262 bool _isInvalidPackageName(String name) { |
| 263 return name.split('.').any((part) { |
| 264 return part.isEmpty || part.contains(_invalidLibCharsRegex); |
| 265 }); |
| 266 } |
| 267 |
| 259 // Constant and final variables | 268 // Constant and final variables |
| 260 final _invalidLibCharsRegex = new RegExp('[^a-z0-9_]'); | 269 final _invalidLibCharsRegex = new RegExp('[^a-z0-9_]'); |
| 261 final _numRegex = new RegExp('[0-9]'); | 270 final _numRegex = new RegExp('[0-9]'); |
| OLD | NEW |