| 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 library compiler; | 5 library compiler; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:collection' show SplayTreeMap; | 8 import 'dart:collection' show SplayTreeMap; |
| 9 import 'dart:json' as json; | |
| 10 | 9 |
| 11 import 'package:analyzer_experimental/src/generated/ast.dart' show Directive, Ur
iBasedDirective; | |
| 12 import 'package:csslib/visitor.dart' show StyleSheet, treeToDebugString; | 10 import 'package:csslib/visitor.dart' show StyleSheet, treeToDebugString; |
| 13 import 'package:html5lib/dom.dart'; | 11 import 'package:html5lib/dom.dart'; |
| 14 import 'package:html5lib/parser.dart'; | 12 import 'package:html5lib/parser.dart'; |
| 15 import 'package:source_maps/span.dart' show Span; | |
| 16 import 'package:source_maps/refactor.dart' show TextEditTransaction; | |
| 17 import 'package:source_maps/printer.dart'; | |
| 18 | 13 |
| 19 import 'analyzer.dart'; | 14 import 'analyzer.dart'; |
| 20 import 'css_analyzer.dart' show analyzeCss, findUrlsImported, | 15 import 'css_analyzer.dart' show analyzeCss, findUrlsImported, |
| 21 findImportsInStyleSheet, parseCss; | 16 findImportsInStyleSheet, parseCss; |
| 22 import 'css_emitters.dart' show rewriteCssUris, | 17 import 'css_emitters.dart' show rewriteCssUris, |
| 23 emitComponentStyleSheet, emitOriginalCss, emitStyleSheet; | 18 emitComponentStyleSheet, emitOriginalCss, emitStyleSheet; |
| 24 import 'dart_parser.dart'; | |
| 25 import 'file_system.dart'; | 19 import 'file_system.dart'; |
| 26 import 'files.dart'; | 20 import 'files.dart'; |
| 27 import 'info.dart'; | 21 import 'info.dart'; |
| 28 import 'messages.dart'; | 22 import 'messages.dart'; |
| 29 import 'compiler_options.dart'; | 23 import 'compiler_options.dart'; |
| 30 import 'utils.dart'; | 24 import 'utils.dart'; |
| 31 | 25 |
| 32 /** | 26 /** |
| 33 * Parses an HTML file [contents] and returns a DOM-like tree. | 27 * Parses an HTML file [contents] and returns a DOM-like tree. |
| 34 * Note that [contents] will be a [String] if coming from a browser-based | 28 * Note that [contents] will be a [String] if coming from a browser-based |
| (...skipping 23 matching lines...) Expand all Loading... |
| 58 String _mainPath; | 52 String _mainPath; |
| 59 String _resetCssFile; | 53 String _resetCssFile; |
| 60 StyleSheet _cssResetStyleSheet; | 54 StyleSheet _cssResetStyleSheet; |
| 61 Messages _messages; | 55 Messages _messages; |
| 62 | 56 |
| 63 FutureGroup _tasks; | 57 FutureGroup _tasks; |
| 64 Set _processed; | 58 Set _processed; |
| 65 | 59 |
| 66 /** Information about source [files] given their href. */ | 60 /** Information about source [files] given their href. */ |
| 67 final Map<String, FileInfo> info = new SplayTreeMap<String, FileInfo>(); | 61 final Map<String, FileInfo> info = new SplayTreeMap<String, FileInfo>(); |
| 68 final _edits = new Map<DartCodeInfo, TextEditTransaction>(); | |
| 69 | 62 |
| 70 final GlobalInfo global = new GlobalInfo(); | 63 final GlobalInfo global = new GlobalInfo(); |
| 71 | 64 |
| 72 /** Creates a compiler with [options] using [fileSystem]. */ | 65 /** Creates a compiler with [options] using [fileSystem]. */ |
| 73 Compiler(this.fileSystem, this.options, this._messages) { | 66 Compiler(this.fileSystem, this.options, this._messages) { |
| 74 _mainPath = options.inputFile; | 67 _mainPath = options.inputFile; |
| 75 var mainDir = path.dirname(_mainPath); | 68 var mainDir = path.dirname(_mainPath); |
| 76 var baseDir = options.baseDir != null ? options.baseDir : mainDir; | 69 var baseDir = options.baseDir != null ? options.baseDir : mainDir; |
| 77 | 70 |
| 78 if (options.resetCssFile != null) { | 71 if (options.resetCssFile != null) { |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 122 } | 115 } |
| 123 | 116 |
| 124 void _processHtmlFile(UrlInfo inputUrl, SourceFile file) { | 117 void _processHtmlFile(UrlInfo inputUrl, SourceFile file) { |
| 125 if (file == null) return; | 118 if (file == null) return; |
| 126 | 119 |
| 127 bool isEntryPoint = _processed.length == 1; | 120 bool isEntryPoint = _processed.length == 1; |
| 128 | 121 |
| 129 files.add(file); | 122 files.add(file); |
| 130 | 123 |
| 131 var fileInfo = _time('Analyzed definitions', inputUrl.url, () { | 124 var fileInfo = _time('Analyzed definitions', inputUrl.url, () { |
| 132 return analyzeDefinitions(global, inputUrl, file.document, | 125 return analyzeDefinitions(global, inputUrl, file.document, _messages); |
| 133 _messages, isEntryPoint: isEntryPoint); | |
| 134 }); | 126 }); |
| 135 info[inputUrl.resolvedPath] = fileInfo; | 127 info[inputUrl.resolvedPath] = fileInfo; |
| 136 | 128 |
| 137 if (isEntryPoint && _resetCssFile != null) { | 129 if (isEntryPoint && _resetCssFile != null) { |
| 138 _processed.add(_resetCssFile); | 130 _processed.add(_resetCssFile); |
| 139 _tasks.add(_parseCssFile(new UrlInfo(_resetCssFile, _resetCssFile, | 131 _tasks.add(_parseCssFile(new UrlInfo(_resetCssFile, _resetCssFile, |
| 140 null))); | 132 null))); |
| 141 } | 133 } |
| 142 | 134 |
| 143 _processImports(fileInfo); | |
| 144 | |
| 145 // Load component files referenced by [file]. | 135 // Load component files referenced by [file]. |
| 146 for (var link in fileInfo.componentLinks) { | 136 for (var link in fileInfo.componentLinks) { |
| 147 _loadFile(link, _parseHtmlFile); | 137 _loadFile(link, _parseHtmlFile); |
| 148 } | 138 } |
| 149 | 139 |
| 150 // Load stylesheet files referenced by [file]. | 140 // Load stylesheet files referenced by [file]. |
| 151 for (var link in fileInfo.styleSheetHrefs) { | 141 for (var link in fileInfo.styleSheetHrefs) { |
| 152 _loadFile(link, _parseCssFile); | 142 _loadFile(link, _parseCssFile); |
| 153 } | 143 } |
| 154 | 144 |
| 155 // Load .dart files being referenced in the page. | |
| 156 _loadFile(fileInfo.externalFile, _parseDartFile); | |
| 157 | |
| 158 // Process any @imports inside of a <style> tag. | 145 // Process any @imports inside of a <style> tag. |
| 159 var urlInfos = findUrlsImported(fileInfo, fileInfo.inputUrl, | 146 var urlInfos = findUrlsImported(fileInfo, fileInfo.inputUrl, |
| 160 file.document, _messages, options); | 147 file.document, _messages, options); |
| 161 for (var urlInfo in urlInfos) { | 148 for (var urlInfo in urlInfos) { |
| 162 _loadFile(urlInfo, _parseCssFile); | 149 _loadFile(urlInfo, _parseCssFile); |
| 163 } | 150 } |
| 164 | 151 |
| 165 // Load .dart files being referenced in components. | 152 // Load .dart files being referenced in components. |
| 166 for (var component in fileInfo.declaredComponents) { | 153 for (var component in fileInfo.declaredComponents) { |
| 167 if (component.externalFile != null) { | |
| 168 _loadFile(component.externalFile, _parseDartFile); | |
| 169 } else if (component.userCode != null) { | |
| 170 _processImports(component); | |
| 171 } | |
| 172 | |
| 173 // Process any @imports inside of the <style> tag in a component. | 154 // Process any @imports inside of the <style> tag in a component. |
| 174 var urlInfos = findUrlsImported(component, | 155 var urlInfos = findUrlsImported(component, |
| 175 component.declaringFile.inputUrl, component.element, _messages, | 156 fileInfo.inputUrl, component.element, _messages, |
| 176 options); | 157 options); |
| 177 for (var urlInfo in urlInfos) { | 158 for (var urlInfo in urlInfos) { |
| 178 _loadFile(urlInfo, _parseCssFile); | 159 _loadFile(urlInfo, _parseCssFile); |
| 179 } | 160 } |
| 180 } | 161 } |
| 181 } | 162 } |
| 182 | 163 |
| 183 /** | 164 /** |
| 184 * Helper function to load [urlInfo] and parse it using [loadAndParse] if it | 165 * Helper function to load [urlInfo] and parse it using [loadAndParse] if it |
| 185 * hasn't been loaded before. | 166 * hasn't been loaded before. |
| (...skipping 14 matching lines...) Expand all Loading... |
| 200 .catchError((e) => _readError(e, inputUrl)) | 181 .catchError((e) => _readError(e, inputUrl)) |
| 201 .then((source) { | 182 .then((source) { |
| 202 if (source == null) return; | 183 if (source == null) return; |
| 203 var file = new SourceFile(filePath); | 184 var file = new SourceFile(filePath); |
| 204 file.document = _time('Parsed', filePath, | 185 file.document = _time('Parsed', filePath, |
| 205 () => parseHtml(source, filePath, _messages)); | 186 () => parseHtml(source, filePath, _messages)); |
| 206 _processHtmlFile(inputUrl, file); | 187 _processHtmlFile(inputUrl, file); |
| 207 }); | 188 }); |
| 208 } | 189 } |
| 209 | 190 |
| 210 /** Parse a Dart file. */ | |
| 211 Future _parseDartFile(UrlInfo inputUrl) { | |
| 212 var filePath = inputUrl.resolvedPath; | |
| 213 return fileSystem.readText(filePath) | |
| 214 .catchError((e) => _readError(e, inputUrl)) | |
| 215 .then((code) { | |
| 216 if (code == null) return; | |
| 217 var file = new SourceFile(filePath, type: SourceFile.DART); | |
| 218 file.code = code; | |
| 219 _processDartFile(inputUrl, file); | |
| 220 }); | |
| 221 } | |
| 222 | |
| 223 /** Parse a stylesheet file. */ | 191 /** Parse a stylesheet file. */ |
| 224 Future _parseCssFile(UrlInfo inputUrl) { | 192 Future _parseCssFile(UrlInfo inputUrl) { |
| 225 if (!options.emulateScopedCss) { | 193 if (!options.emulateScopedCss) { |
| 226 return new Future<SourceFile>.value(null); | 194 return new Future<SourceFile>.value(null); |
| 227 } | 195 } |
| 228 var filePath = inputUrl.resolvedPath; | 196 var filePath = inputUrl.resolvedPath; |
| 229 return fileSystem.readText(filePath) | 197 return fileSystem.readText(filePath) |
| 230 .catchError((e) => _readError(e, inputUrl, isWarning: true)) | 198 .catchError((e) => _readError(e, inputUrl, isWarning: true)) |
| 231 .then((code) { | 199 .then((code) { |
| 232 if (code == null) return; | 200 if (code == null) return; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 243 message = '$message. original message:\n $error'; | 211 message = '$message. original message:\n $error'; |
| 244 } | 212 } |
| 245 if (isWarning) { | 213 if (isWarning) { |
| 246 _messages.warning(message, inputUrl.sourceSpan); | 214 _messages.warning(message, inputUrl.sourceSpan); |
| 247 } else { | 215 } else { |
| 248 _messages.error(message, inputUrl.sourceSpan); | 216 _messages.error(message, inputUrl.sourceSpan); |
| 249 } | 217 } |
| 250 return null; | 218 return null; |
| 251 } | 219 } |
| 252 | 220 |
| 253 void _processDartFile(UrlInfo inputUrl, SourceFile dartFile) { | |
| 254 if (dartFile == null) return; | |
| 255 | |
| 256 files.add(dartFile); | |
| 257 | |
| 258 var resolvedPath = inputUrl.resolvedPath; | |
| 259 var fileInfo = new FileInfo(inputUrl); | |
| 260 info[resolvedPath] = fileInfo; | |
| 261 fileInfo.inlinedCode = parseDartCode(resolvedPath, dartFile.code); | |
| 262 _processImports(fileInfo); | |
| 263 } | |
| 264 | |
| 265 void _processImports(LibraryInfo library) { | |
| 266 if (library.userCode == null) return; | |
| 267 | |
| 268 for (var directive in library.userCode.directives) { | |
| 269 _loadFile(_getDirectiveUrlInfo(library, directive), _parseDartFile); | |
| 270 } | |
| 271 } | |
| 272 | |
| 273 void _processCssFile(UrlInfo inputUrl, SourceFile cssFile) { | 221 void _processCssFile(UrlInfo inputUrl, SourceFile cssFile) { |
| 274 if (cssFile == null) return; | 222 if (cssFile == null) return; |
| 275 | 223 |
| 276 files.add(cssFile); | 224 files.add(cssFile); |
| 277 | 225 |
| 278 var fileInfo = new FileInfo(inputUrl); | 226 var fileInfo = new FileInfo(inputUrl); |
| 279 info[inputUrl.resolvedPath] = fileInfo; | 227 info[inputUrl.resolvedPath] = fileInfo; |
| 280 | 228 |
| 281 var styleSheet = parseCss(cssFile.code, _messages, options); | 229 var styleSheet = parseCss(cssFile.code, _messages, options); |
| 282 if (inputUrl.url == _resetCssFile) { | 230 if (inputUrl.url == _resetCssFile) { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 293 var urlInfos = _time('CSS imports', processingFile, () => | 241 var urlInfos = _time('CSS imports', processingFile, () => |
| 294 findImportsInStyleSheet(styleSheet, inputUrl, _messages)); | 242 findImportsInStyleSheet(styleSheet, inputUrl, _messages)); |
| 295 | 243 |
| 296 for (var urlInfo in urlInfos) { | 244 for (var urlInfo in urlInfos) { |
| 297 if (urlInfo == null) break; | 245 if (urlInfo == null) break; |
| 298 // Load any @imported stylesheet files referenced in this style sheet. | 246 // Load any @imported stylesheet files referenced in this style sheet. |
| 299 _loadFile(urlInfo, _parseCssFile); | 247 _loadFile(urlInfo, _parseCssFile); |
| 300 } | 248 } |
| 301 } | 249 } |
| 302 | 250 |
| 303 String _directiveUri(Directive directive) { | |
| 304 var uriDirective = (directive as UriBasedDirective).uri; | |
| 305 return (uriDirective as dynamic).value; | |
| 306 } | |
| 307 | |
| 308 UrlInfo _getDirectiveUrlInfo(LibraryInfo library, Directive directive) { | |
| 309 var uri = _directiveUri(directive); | |
| 310 if (uri.startsWith('dart:')) return null; | |
| 311 if (uri.startsWith('package:') && uri.startsWith('package:polymer/')) { | |
| 312 // Don't process our own package -- we'll implement @observable manually. | |
| 313 return null; | |
| 314 } | |
| 315 | |
| 316 var span = library.userCode.sourceFile.span( | |
| 317 directive.offset, directive.end); | |
| 318 return UrlInfo.resolve(uri, library.dartCodeUrl, span, _messages); | |
| 319 } | |
| 320 | |
| 321 /** | |
| 322 * Finds all Dart code libraries. | |
| 323 * Each library will have [LibraryInfo.inlinedCode] that is non-null. | |
| 324 * Also each inlinedCode will be unique. | |
| 325 */ | |
| 326 List<LibraryInfo> _findAllDartLibraries() { | |
| 327 var libs = <LibraryInfo>[]; | |
| 328 void _addLibrary(LibraryInfo lib) { | |
| 329 if (lib.inlinedCode != null) libs.add(lib); | |
| 330 } | |
| 331 | |
| 332 for (var sourceFile in files) { | |
| 333 var file = info[sourceFile.path]; | |
| 334 _addLibrary(file); | |
| 335 file.declaredComponents.forEach(_addLibrary); | |
| 336 } | |
| 337 | |
| 338 // Assert that each file path is unique. | |
| 339 assert(_uniquePaths(libs)); | |
| 340 return libs; | |
| 341 } | |
| 342 | |
| 343 bool _uniquePaths(List<LibraryInfo> libs) { | |
| 344 var seen = new Set(); | |
| 345 for (var lib in libs) { | |
| 346 if (seen.contains(lib.inlinedCode)) { | |
| 347 throw new StateError('internal error: ' | |
| 348 'duplicate user code for ${lib.dartCodeUrl.resolvedPath}.' | |
| 349 ' Files were: $files'); | |
| 350 } | |
| 351 seen.add(lib.inlinedCode); | |
| 352 } | |
| 353 return true; | |
| 354 } | |
| 355 | |
| 356 /** | |
| 357 * This method computes which Dart files have been modified, starting | |
| 358 * from [transformed] and marking recursively through all files that import | |
| 359 * the modified files. | |
| 360 */ | |
| 361 void _findModifiedDartFiles(List<LibraryInfo> libraries, | |
| 362 List<FileInfo> transformed) { | |
| 363 | |
| 364 if (transformed.length == 0) return; | |
| 365 | |
| 366 // Compute files that reference each file, then use this information to | |
| 367 // flip the modified bit transitively. This is a lot simpler than trying | |
| 368 // to compute it the other way because of circular references. | |
| 369 for (var lib in libraries) { | |
| 370 for (var directive in lib.userCode.directives) { | |
| 371 var importPath = _getDirectiveUrlInfo(lib, directive); | |
| 372 if (importPath == null) continue; | |
| 373 | |
| 374 var importInfo = info[importPath.resolvedPath]; | |
| 375 if (importInfo != null) { | |
| 376 importInfo.referencedBy.add(lib); | |
| 377 } | |
| 378 } | |
| 379 } | |
| 380 | |
| 381 // Propegate the modified bit to anything that references a modified file. | |
| 382 void setModified(LibraryInfo library) { | |
| 383 if (library.modified) return; | |
| 384 library.modified = true; | |
| 385 library.referencedBy.forEach(setModified); | |
| 386 } | |
| 387 transformed.forEach(setModified); | |
| 388 | |
| 389 for (var lib in libraries) { | |
| 390 // We don't need this anymore, so free it. | |
| 391 lib.referencedBy = null; | |
| 392 } | |
| 393 } | |
| 394 | |
| 395 /** Run the analyzer on every input html file. */ | 251 /** Run the analyzer on every input html file. */ |
| 396 void _analyze() { | 252 void _analyze() { |
| 397 var uniqueIds = new IntIterator(); | 253 var uniqueIds = new IntIterator(); |
| 398 for (var file in files) { | 254 for (var file in files) { |
| 399 if (file.isHtml) { | 255 if (file.isHtml) { |
| 400 _time('Analyzed contents', file.path, () => | 256 _time('Analyzed contents', file.path, () => |
| 401 analyzeFile(file, info, uniqueIds, global, _messages, | 257 analyzeFile(file, info, uniqueIds, global, _messages, |
| 402 options.emulateScopedCss)); | 258 options.emulateScopedCss)); |
| 403 } | 259 } |
| 404 } | 260 } |
| 405 } | 261 } |
| 406 | 262 |
| 407 // TODO(jmesserly): refactor this and other CSS related transforms out of | 263 // TODO(jmesserly): refactor this and other CSS related transforms out of |
| 408 // Compiler. | 264 // Compiler. |
| 409 /** | 265 /** |
| 410 * Generate an CSS file for all style sheets (main and components). | 266 * Generate an CSS file for all style sheets (main and components). |
| 411 * Returns true if a file was generated, otherwise false. | 267 * Returns true if a file was generated, otherwise false. |
| 412 */ | 268 */ |
| 413 bool _emitAllCss() { | 269 bool _emitAllCss() { |
| 414 if (!options.emulateScopedCss) return false; | 270 if (!options.emulateScopedCss) return false; |
| 415 | 271 |
| 416 var buff = new StringBuffer(); | 272 var buff = new StringBuffer(); |
| 417 | 273 |
| 418 // Emit all linked style sheet files first. | 274 // Emit all linked style sheet files first. |
| 419 for (var file in files) { | 275 for (var file in files) { |
| 420 var css = new StringBuffer(); | 276 var css = new StringBuffer(); |
| 421 var fileInfo = info[file.path]; | 277 var fileInfo = info[file.path]; |
| 422 if (file.isStyleSheet) { | 278 if (file.isStyleSheet) { |
| 423 for (var styleSheet in fileInfo.styleSheets) { | 279 for (var styleSheet in fileInfo.styleSheets) { |
| 424 // Translate any URIs in CSS. | |
| 425 rewriteCssUris(_pathMapper, fileInfo.inputUrl.resolvedPath, | |
| 426 options.rewriteUrls, styleSheet); | |
| 427 css.write( | 280 css.write( |
| 428 '/* Auto-generated from style sheet href = ${file.path} */\n' | 281 '/* Auto-generated from style sheet href = ${file.path} */\n' |
| 429 '/* DO NOT EDIT. */\n\n'); | 282 '/* DO NOT EDIT. */\n\n'); |
| 430 css.write(emitStyleSheet(styleSheet, fileInfo)); | 283 css.write(emitStyleSheet(styleSheet, fileInfo)); |
| 431 css.write('\n\n'); | 284 css.write('\n\n'); |
| 432 } | 285 } |
| 433 } | 286 } |
| 434 } | 287 } |
| 435 | 288 |
| 436 // Emit all CSS for each component (style scoped). | 289 // Emit all CSS for each component (style scoped). |
| 437 for (var file in files) { | 290 for (var file in files) { |
| 438 if (file.isHtml) { | 291 if (file.isHtml) { |
| 439 var fileInfo = info[file.path]; | 292 var fileInfo = info[file.path]; |
| 440 for (var component in fileInfo.declaredComponents) { | 293 for (var component in fileInfo.declaredComponents) { |
| 441 for (var styleSheet in component.styleSheets) { | 294 for (var styleSheet in component.styleSheets) { |
| 442 // Translate any URIs in CSS. | 295 // Translate any URIs in CSS. |
| 443 rewriteCssUris(_pathMapper, fileInfo.inputUrl.resolvedPath, | |
| 444 options.rewriteUrls, styleSheet); | |
| 445 | |
| 446 if (buff.isEmpty) { | 296 if (buff.isEmpty) { |
| 447 buff.write( | 297 buff.write( |
| 448 '/* Auto-generated from components style tags. */\n' | 298 '/* Auto-generated from components style tags. */\n' |
| 449 '/* DO NOT EDIT. */\n\n'); | 299 '/* DO NOT EDIT. */\n\n'); |
| 450 } | 300 } |
| 451 buff.write( | 301 buff.write( |
| 452 '/* ==================================================== \n' | 302 '/* ==================================================== \n' |
| 453 ' Component ${component.tagName} stylesheet \n' | 303 ' Component ${component.tagName} stylesheet \n' |
| 454 ' ==================================================== */\n'); | 304 ' ==================================================== */\n'); |
| 455 | 305 |
| (...skipping 19 matching lines...) Expand all Loading... |
| 475 } else { | 325 } else { |
| 476 buff.write(emitOriginalCss(styleSheet)); | 326 buff.write(emitOriginalCss(styleSheet)); |
| 477 } | 327 } |
| 478 buff.write('\n\n'); | 328 buff.write('\n\n'); |
| 479 } | 329 } |
| 480 } | 330 } |
| 481 } | 331 } |
| 482 } | 332 } |
| 483 | 333 |
| 484 if (buff.isEmpty) return false; | 334 if (buff.isEmpty) return false; |
| 485 | |
| 486 var cssPath = _pathMapper.outputPath(_mainPath, '.css', true); | |
| 487 output.add(new OutputFile(cssPath, buff.toString())); | |
| 488 return true; | 335 return true; |
| 489 } | 336 } |
| 490 | 337 |
| 491 _time(String logMessage, String filePath, callback(), | 338 _time(String logMessage, String filePath, callback(), |
| 492 {bool printTime: false}) { | 339 {bool printTime: false}) { |
| 493 var message = new StringBuffer(); | 340 var message = new StringBuffer(); |
| 494 message.write(logMessage); | 341 message.write(logMessage); |
| 495 var filename = path.basename(filePath); | 342 var filename = path.basename(filePath); |
| 496 for (int i = (60 - logMessage.length - filename.length); i > 0 ; i--) { | 343 for (int i = (60 - logMessage.length - filename.length); i > 0 ; i--) { |
| 497 message.write(' '); | 344 message.write(' '); |
| 498 } | 345 } |
| 499 message.write(filename); | 346 message.write(filename); |
| 500 return time(message.toString(), callback, | 347 return time(message.toString(), callback, |
| 501 printTime: options.verbose || printTime); | 348 printTime: options.verbose || printTime); |
| 502 } | 349 } |
| 503 } | 350 } |
| OLD | NEW |