Chromium Code Reviews| 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; | 9 import 'dart:json' as json; |
| 10 | 10 |
| 11 import 'package:analyzer_experimental/src/generated/ast.dart' show Directive, Ur iBasedDirective; | 11 import 'package:analyzer_experimental/src/generated/ast.dart' show Directive, Ur iBasedDirective; |
| 12 import 'package:csslib/visitor.dart' show StyleSheet, treeToDebugString; | 12 import 'package:csslib/visitor.dart' show StyleSheet, treeToDebugString; |
| 13 import 'package:html5lib/dom.dart'; | 13 import 'package:html5lib/dom.dart'; |
| 14 import 'package:html5lib/parser.dart'; | 14 import 'package:html5lib/parser.dart'; |
| 15 import 'package:observe/transform.dart' show transformObservables; | |
| 16 import 'package:source_maps/span.dart' show Span; | 15 import 'package:source_maps/span.dart' show Span; |
| 17 import 'package:source_maps/refactor.dart' show TextEditTransaction; | 16 import 'package:source_maps/refactor.dart' show TextEditTransaction; |
| 18 import 'package:source_maps/printer.dart'; | 17 import 'package:source_maps/printer.dart'; |
| 19 | 18 |
| 20 import 'analyzer.dart'; | 19 import 'analyzer.dart'; |
| 21 import 'css_analyzer.dart' show analyzeCss, findUrlsImported, | 20 import 'css_analyzer.dart' show analyzeCss, findUrlsImported, |
| 22 findImportsInStyleSheet, parseCss; | 21 findImportsInStyleSheet, parseCss; |
| 23 import 'css_emitters.dart' show rewriteCssUris, | 22 import 'css_emitters.dart' show rewriteCssUris, |
| 24 emitComponentStyleSheet, emitOriginalCss, emitStyleSheet; | 23 emitComponentStyleSheet, emitOriginalCss, emitStyleSheet; |
| 25 import 'dart_parser.dart'; | 24 import 'dart_parser.dart'; |
| 26 import 'emitters.dart'; | |
| 27 import 'file_system.dart'; | 25 import 'file_system.dart'; |
| 28 import 'files.dart'; | 26 import 'files.dart'; |
| 29 import 'info.dart'; | 27 import 'info.dart'; |
| 30 import 'messages.dart'; | 28 import 'messages.dart'; |
| 31 import 'compiler_options.dart'; | 29 import 'compiler_options.dart'; |
| 32 import 'paths.dart'; | |
| 33 import 'utils.dart'; | 30 import 'utils.dart'; |
| 34 | 31 |
| 35 /** | 32 /** |
| 36 * Parses an HTML file [contents] and returns a DOM-like tree. | 33 * Parses an HTML file [contents] and returns a DOM-like tree. |
| 37 * Note that [contents] will be a [String] if coming from a browser-based | 34 * Note that [contents] will be a [String] if coming from a browser-based |
| 38 * [FileSystem], or it will be a [List<int>] if running on the command line. | 35 * [FileSystem], or it will be a [List<int>] if running on the command line. |
| 39 * | 36 * |
| 40 * Adds emitted error/warning to [messages], if [messages] is supplied. | 37 * Adds emitted error/warning to [messages], if [messages] is supplied. |
| 41 */ | 38 */ |
| 42 Document parseHtml(contents, String sourcePath, Messages messages) { | 39 Document parseHtml(contents, String sourcePath, Messages messages) { |
| 43 var parser = new HtmlParser(contents, generateSpans: true, | 40 var parser = new HtmlParser(contents, generateSpans: true, |
| 44 sourceUrl: sourcePath); | 41 sourceUrl: sourcePath); |
| 45 var document = parser.parse(); | 42 var document = parser.parse(); |
| 46 | 43 |
| 47 // Note: errors aren't fatal in HTML (unless strict mode is on). | 44 // Note: errors aren't fatal in HTML (unless strict mode is on). |
| 48 // So just print them as warnings. | 45 // So just print them as warnings. |
| 49 for (var e in parser.errors) { | 46 for (var e in parser.errors) { |
| 50 messages.warning(e.message, e.span); | 47 messages.warning(e.message, e.span); |
| 51 } | 48 } |
| 52 return document; | 49 return document; |
| 53 } | 50 } |
| 54 | 51 |
| 55 /** Compiles an application written with Dart web components. */ | 52 /** Compiles an application written with Dart web components. */ |
| 56 class Compiler { | 53 class Compiler { |
| 57 final FileSystem fileSystem; | 54 final FileSystem fileSystem; |
| 58 final CompilerOptions options; | 55 final CompilerOptions options; |
| 59 final List<SourceFile> files = <SourceFile>[]; | 56 final List<SourceFile> files = <SourceFile>[]; |
| 60 final List<OutputFile> output = <OutputFile>[]; | |
| 61 | 57 |
| 62 String _mainPath; | 58 String _mainPath; |
| 63 String _resetCssFile; | 59 String _resetCssFile; |
| 64 StyleSheet _cssResetStyleSheet; | 60 StyleSheet _cssResetStyleSheet; |
| 65 PathMapper _pathMapper; | |
| 66 Messages _messages; | 61 Messages _messages; |
| 67 | 62 |
| 68 FutureGroup _tasks; | 63 FutureGroup _tasks; |
| 69 Set _processed; | 64 Set _processed; |
| 70 | 65 |
| 71 /** Information about source [files] given their href. */ | 66 /** Information about source [files] given their href. */ |
| 72 final Map<String, FileInfo> info = new SplayTreeMap<String, FileInfo>(); | 67 final Map<String, FileInfo> info = new SplayTreeMap<String, FileInfo>(); |
| 73 final _edits = new Map<DartCodeInfo, TextEditTransaction>(); | 68 final _edits = new Map<DartCodeInfo, TextEditTransaction>(); |
| 74 | 69 |
| 75 final GlobalInfo global = new GlobalInfo(); | 70 final GlobalInfo global = new GlobalInfo(); |
| 76 | 71 |
| 77 /** Creates a compiler with [options] using [fileSystem]. */ | 72 /** Creates a compiler with [options] using [fileSystem]. */ |
| 78 Compiler(this.fileSystem, this.options, this._messages) { | 73 Compiler(this.fileSystem, this.options, this._messages) { |
| 79 _mainPath = options.inputFile; | 74 _mainPath = options.inputFile; |
| 80 var mainDir = path.dirname(_mainPath); | 75 var mainDir = path.dirname(_mainPath); |
| 81 var baseDir = options.baseDir != null ? options.baseDir : mainDir; | 76 var baseDir = options.baseDir != null ? options.baseDir : mainDir; |
| 82 var outputDir = options.outputDir != null ? options.outputDir : mainDir; | |
| 83 var packageRoot = options.packageRoot != null ? options.packageRoot | |
|
Jennifer Messerly
2013/08/28 22:25:45
we might want pkg root back now?
Siggi Cherem (dart-lang)
2013/08/28 23:17:06
Done.
| |
| 84 : path.join(path.dirname(_mainPath), 'packages'); | |
| 85 | 77 |
| 86 if (options.resetCssFile != null) { | 78 if (options.resetCssFile != null) { |
| 87 _resetCssFile = options.resetCssFile; | 79 _resetCssFile = options.resetCssFile; |
| 88 if (path.isRelative(_resetCssFile)) { | 80 if (path.isRelative(_resetCssFile)) { |
| 89 // If CSS reset file path is relative from our current path. | 81 // If CSS reset file path is relative from our current path. |
| 90 _resetCssFile = path.resolve(_resetCssFile); | 82 _resetCssFile = path.resolve(_resetCssFile); |
| 91 } | 83 } |
| 92 } | 84 } |
| 93 | 85 |
| 94 // Normalize paths - all should be relative or absolute paths. | 86 // Normalize paths - all should be relative or absolute paths. |
| 95 if (path.isAbsolute(_mainPath) || path.isAbsolute(baseDir) || | 87 if (path.isAbsolute(_mainPath) || path.isAbsolute(baseDir)) { |
| 96 path.isAbsolute(outputDir) || path.isAbsolute(packageRoot)) { | |
| 97 if (path.isRelative(_mainPath)) _mainPath = path.resolve(_mainPath); | 88 if (path.isRelative(_mainPath)) _mainPath = path.resolve(_mainPath); |
| 98 if (path.isRelative(baseDir)) baseDir = path.resolve(baseDir); | 89 if (path.isRelative(baseDir)) baseDir = path.resolve(baseDir); |
| 99 if (path.isRelative(outputDir)) outputDir = path.resolve(outputDir); | |
| 100 if (path.isRelative(packageRoot)) { | |
| 101 packageRoot = path.resolve(packageRoot); | |
| 102 } | |
| 103 } | 90 } |
| 104 _pathMapper = new PathMapper( | |
| 105 baseDir, outputDir, packageRoot, options.forceMangle, | |
| 106 options.rewriteUrls); | |
| 107 } | 91 } |
| 108 | 92 |
| 109 /** Compile the application starting from the given input file. */ | 93 /** Compile the application starting from the given input file. */ |
| 110 Future run() { | 94 Future run() { |
| 111 if (path.basename(_mainPath).endsWith('.dart')) { | 95 if (path.basename(_mainPath).endsWith('.dart')) { |
| 112 _messages.error("Please provide an HTML file as your entry point.", | 96 _messages.error("Please provide an HTML file as your entry point.", |
| 113 null); | 97 null); |
| 114 return new Future.value(null); | 98 return new Future.value(null); |
| 115 } | 99 } |
| 116 return _parseAndDiscover(_mainPath).then((_) { | 100 return _parseAndDiscover(_mainPath).then((_) { |
| 117 _analyze(); | 101 _analyze(); |
| 118 | 102 |
| 119 // Analyze all CSS files. | 103 // Analyze all CSS files. |
| 120 _time('Analyzed Style Sheets', '', () => | 104 _time('Analyzed Style Sheets', '', () => |
| 121 analyzeCss(_pathMapper.packageRoot, files, info, | 105 analyzeCss(files, info, |
| 122 global.pseudoElements, _messages, | 106 global.pseudoElements, _messages, |
| 123 warningsAsErrors: options.warningsAsErrors)); | 107 warningsAsErrors: options.warningsAsErrors)); |
| 124 | |
| 125 // TODO(jmesserly): need to go through our errors, and figure out if some | |
| 126 // of them should be warnings instead. | |
| 127 if (_messages.hasErrors || options.analysisOnly) return; | |
| 128 _transformDart(); | |
| 129 _emit(); | |
| 130 }); | 108 }); |
| 131 } | 109 } |
| 132 | 110 |
| 133 /** | 111 /** |
| 134 * Asynchronously parse [inputFile] and transitively discover web components | 112 * Asynchronously parse [inputFile] and transitively discover web components |
| 135 * to load and parse. Returns a future that completes when all files are | 113 * to load and parse. Returns a future that completes when all files are |
| 136 * processed. | 114 * processed. |
| 137 */ | 115 */ |
| 138 Future _parseAndDiscover(String inputFile) { | 116 Future _parseAndDiscover(String inputFile) { |
| 139 _tasks = new FutureGroup(); | 117 _tasks = new FutureGroup(); |
| 140 _processed = new Set(); | 118 _processed = new Set(); |
| 141 _processed.add(inputFile); | 119 _processed.add(inputFile); |
| 142 _tasks.add(_parseHtmlFile(new UrlInfo(inputFile, inputFile, null))); | 120 _tasks.add(_parseHtmlFile(new UrlInfo(inputFile, inputFile, null))); |
| 143 return _tasks.future; | 121 return _tasks.future; |
| 144 } | 122 } |
| 145 | 123 |
| 146 void _processHtmlFile(UrlInfo inputUrl, SourceFile file) { | 124 void _processHtmlFile(UrlInfo inputUrl, SourceFile file) { |
| 147 if (file == null) return; | 125 if (file == null) return; |
| 148 | 126 |
| 149 bool isEntryPoint = _processed.length == 1; | 127 bool isEntryPoint = _processed.length == 1; |
| 150 | 128 |
| 151 files.add(file); | 129 files.add(file); |
| 152 | 130 |
| 153 var fileInfo = _time('Analyzed definitions', inputUrl.url, () { | 131 var fileInfo = _time('Analyzed definitions', inputUrl.url, () { |
| 154 return analyzeDefinitions(global, inputUrl, file.document, | 132 return analyzeDefinitions(global, inputUrl, file.document, |
| 155 _pathMapper.packageRoot, _messages, isEntryPoint: isEntryPoint); | 133 _messages, isEntryPoint: isEntryPoint); |
| 156 }); | 134 }); |
| 157 info[inputUrl.resolvedPath] = fileInfo; | 135 info[inputUrl.resolvedPath] = fileInfo; |
| 158 | 136 |
| 159 if (isEntryPoint && _resetCssFile != null) { | 137 if (isEntryPoint && _resetCssFile != null) { |
| 160 _processed.add(_resetCssFile); | 138 _processed.add(_resetCssFile); |
| 161 _tasks.add(_parseCssFile(new UrlInfo(_resetCssFile, _resetCssFile, | 139 _tasks.add(_parseCssFile(new UrlInfo(_resetCssFile, _resetCssFile, |
| 162 null))); | 140 null))); |
| 163 } | 141 } |
| 164 | 142 |
| 165 _setOutputFilenames(fileInfo); | |
| 166 _processImports(fileInfo); | 143 _processImports(fileInfo); |
| 167 | 144 |
| 168 // Load component files referenced by [file]. | 145 // Load component files referenced by [file]. |
| 169 for (var link in fileInfo.componentLinks) { | 146 for (var link in fileInfo.componentLinks) { |
| 170 _loadFile(link, _parseHtmlFile); | 147 _loadFile(link, _parseHtmlFile); |
| 171 } | 148 } |
| 172 | 149 |
| 173 // Load stylesheet files referenced by [file]. | 150 // Load stylesheet files referenced by [file]. |
| 174 for (var link in fileInfo.styleSheetHrefs) { | 151 for (var link in fileInfo.styleSheetHrefs) { |
| 175 _loadFile(link, _parseCssFile); | 152 _loadFile(link, _parseCssFile); |
| 176 } | 153 } |
| 177 | 154 |
| 178 // Load .dart files being referenced in the page. | 155 // Load .dart files being referenced in the page. |
| 179 _loadFile(fileInfo.externalFile, _parseDartFile); | 156 _loadFile(fileInfo.externalFile, _parseDartFile); |
| 180 | 157 |
| 181 // Process any @imports inside of a <style> tag. | 158 // Process any @imports inside of a <style> tag. |
| 182 var urlInfos = findUrlsImported(fileInfo, fileInfo.inputUrl, | 159 var urlInfos = findUrlsImported(fileInfo, fileInfo.inputUrl, |
| 183 _pathMapper.packageRoot, file.document, _messages, options); | 160 file.document, _messages, options); |
| 184 for (var urlInfo in urlInfos) { | 161 for (var urlInfo in urlInfos) { |
| 185 _loadFile(urlInfo, _parseCssFile); | 162 _loadFile(urlInfo, _parseCssFile); |
| 186 } | 163 } |
| 187 | 164 |
| 188 // Load .dart files being referenced in components. | 165 // Load .dart files being referenced in components. |
| 189 for (var component in fileInfo.declaredComponents) { | 166 for (var component in fileInfo.declaredComponents) { |
| 190 if (component.externalFile != null) { | 167 if (component.externalFile != null) { |
| 191 _loadFile(component.externalFile, _parseDartFile); | 168 _loadFile(component.externalFile, _parseDartFile); |
| 192 } else if (component.userCode != null) { | 169 } else if (component.userCode != null) { |
| 193 _processImports(component); | 170 _processImports(component); |
| 194 } | 171 } |
| 195 | 172 |
| 196 // Process any @imports inside of the <style> tag in a component. | 173 // Process any @imports inside of the <style> tag in a component. |
| 197 var urlInfos = findUrlsImported(component, | 174 var urlInfos = findUrlsImported(component, |
| 198 component.declaringFile.inputUrl, _pathMapper.packageRoot, | 175 component.declaringFile.inputUrl, component.element, _messages, |
| 199 component.element, _messages, options); | 176 options); |
| 200 for (var urlInfo in urlInfos) { | 177 for (var urlInfo in urlInfos) { |
| 201 _loadFile(urlInfo, _parseCssFile); | 178 _loadFile(urlInfo, _parseCssFile); |
| 202 } | 179 } |
| 203 } | 180 } |
| 204 } | 181 } |
| 205 | 182 |
| 206 /** | 183 /** |
| 207 * Helper function to load [urlInfo] and parse it using [loadAndParse] if it | 184 * Helper function to load [urlInfo] and parse it using [loadAndParse] if it |
| 208 * hasn't been loaded before. | 185 * hasn't been loaded before. |
| 209 */ | 186 */ |
| 210 void _loadFile(UrlInfo urlInfo, Future loadAndParse(UrlInfo inputUrl)) { | 187 void _loadFile(UrlInfo urlInfo, Future loadAndParse(UrlInfo inputUrl)) { |
| 211 if (urlInfo == null) return; | 188 if (urlInfo == null) return; |
| 212 var resolvedPath = urlInfo.resolvedPath; | 189 var resolvedPath = urlInfo.resolvedPath; |
| 213 if (!_processed.contains(resolvedPath)) { | 190 if (!_processed.contains(resolvedPath)) { |
| 214 _processed.add(resolvedPath); | 191 _processed.add(resolvedPath); |
| 215 _tasks.add(loadAndParse(urlInfo)); | 192 _tasks.add(loadAndParse(urlInfo)); |
| 216 } | 193 } |
| 217 } | 194 } |
| 218 | 195 |
| 219 void _setOutputFilenames(FileInfo fileInfo) { | |
| 220 var filePath = fileInfo.dartCodeUrl.resolvedPath; | |
| 221 fileInfo.outputFilename = _pathMapper.mangle(path.basename(filePath), | |
| 222 '.dart', path.extension(filePath) == '.html'); | |
| 223 for (var component in fileInfo.declaredComponents) { | |
| 224 var externalFile = component.externalFile; | |
| 225 var name = null; | |
| 226 if (externalFile != null) { | |
| 227 name = _pathMapper.mangle( | |
| 228 path.basename(externalFile.resolvedPath), '.dart'); | |
| 229 } else { | |
| 230 var declaringFile = component.declaringFile; | |
| 231 var prefix = path.basename(declaringFile.inputUrl.resolvedPath); | |
| 232 if (declaringFile.declaredComponents.length == 1 | |
| 233 && !declaringFile.codeAttached && !declaringFile.isEntryPoint) { | |
| 234 name = _pathMapper.mangle(prefix, '.dart', true); | |
| 235 } else { | |
| 236 var componentName = component.tagName.replaceAll('-', '_'); | |
| 237 name = _pathMapper.mangle('${prefix}_$componentName', '.dart', true); | |
| 238 } | |
| 239 } | |
| 240 component.outputFilename = name; | |
| 241 } | |
| 242 } | |
| 243 | |
| 244 /** Parse an HTML file. */ | 196 /** Parse an HTML file. */ |
| 245 Future _parseHtmlFile(UrlInfo inputUrl) { | 197 Future _parseHtmlFile(UrlInfo inputUrl) { |
| 246 if (!_pathMapper.checkInputPath(inputUrl, _messages)) { | |
| 247 return new Future<SourceFile>.value(null); | |
| 248 } | |
| 249 var filePath = inputUrl.resolvedPath; | 198 var filePath = inputUrl.resolvedPath; |
| 250 return fileSystem.readTextOrBytes(filePath) | 199 return fileSystem.readTextOrBytes(filePath) |
| 251 .catchError((e) => _readError(e, inputUrl)) | 200 .catchError((e) => _readError(e, inputUrl)) |
| 252 .then((source) { | 201 .then((source) { |
| 253 if (source == null) return; | 202 if (source == null) return; |
| 254 var file = new SourceFile(filePath); | 203 var file = new SourceFile(filePath); |
| 255 file.document = _time('Parsed', filePath, | 204 file.document = _time('Parsed', filePath, |
| 256 () => parseHtml(source, filePath, _messages)); | 205 () => parseHtml(source, filePath, _messages)); |
| 257 _processHtmlFile(inputUrl, file); | 206 _processHtmlFile(inputUrl, file); |
| 258 }); | 207 }); |
| 259 } | 208 } |
| 260 | 209 |
| 261 /** Parse a Dart file. */ | 210 /** Parse a Dart file. */ |
| 262 Future _parseDartFile(UrlInfo inputUrl) { | 211 Future _parseDartFile(UrlInfo inputUrl) { |
| 263 if (!_pathMapper.checkInputPath(inputUrl, _messages)) { | |
| 264 return new Future<SourceFile>.value(null); | |
| 265 } | |
| 266 var filePath = inputUrl.resolvedPath; | 212 var filePath = inputUrl.resolvedPath; |
| 267 return fileSystem.readText(filePath) | 213 return fileSystem.readText(filePath) |
| 268 .catchError((e) => _readError(e, inputUrl)) | 214 .catchError((e) => _readError(e, inputUrl)) |
| 269 .then((code) { | 215 .then((code) { |
| 270 if (code == null) return; | 216 if (code == null) return; |
| 271 var file = new SourceFile(filePath, type: SourceFile.DART); | 217 var file = new SourceFile(filePath, type: SourceFile.DART); |
| 272 file.code = code; | 218 file.code = code; |
| 273 _processDartFile(inputUrl, file); | 219 _processDartFile(inputUrl, file); |
| 274 }); | 220 }); |
| 275 } | 221 } |
| 276 | 222 |
| 277 /** Parse a stylesheet file. */ | 223 /** Parse a stylesheet file. */ |
| 278 Future _parseCssFile(UrlInfo inputUrl) { | 224 Future _parseCssFile(UrlInfo inputUrl) { |
| 279 if (!options.emulateScopedCss || | 225 if (!options.emulateScopedCss) { |
| 280 !_pathMapper.checkInputPath(inputUrl, _messages)) { | |
| 281 return new Future<SourceFile>.value(null); | 226 return new Future<SourceFile>.value(null); |
| 282 } | 227 } |
| 283 var filePath = inputUrl.resolvedPath; | 228 var filePath = inputUrl.resolvedPath; |
| 284 return fileSystem.readText(filePath) | 229 return fileSystem.readText(filePath) |
| 285 .catchError((e) => _readError(e, inputUrl, isWarning: true)) | 230 .catchError((e) => _readError(e, inputUrl, isWarning: true)) |
| 286 .then((code) { | 231 .then((code) { |
| 287 if (code == null) return; | 232 if (code == null) return; |
| 288 var file = new SourceFile(filePath, type: SourceFile.STYLESHEET); | 233 var file = new SourceFile(filePath, type: SourceFile.STYLESHEET); |
| 289 file.code = code; | 234 file.code = code; |
| 290 _processCssFile(inputUrl, file); | 235 _processCssFile(inputUrl, file); |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 307 | 252 |
| 308 void _processDartFile(UrlInfo inputUrl, SourceFile dartFile) { | 253 void _processDartFile(UrlInfo inputUrl, SourceFile dartFile) { |
| 309 if (dartFile == null) return; | 254 if (dartFile == null) return; |
| 310 | 255 |
| 311 files.add(dartFile); | 256 files.add(dartFile); |
| 312 | 257 |
| 313 var resolvedPath = inputUrl.resolvedPath; | 258 var resolvedPath = inputUrl.resolvedPath; |
| 314 var fileInfo = new FileInfo(inputUrl); | 259 var fileInfo = new FileInfo(inputUrl); |
| 315 info[resolvedPath] = fileInfo; | 260 info[resolvedPath] = fileInfo; |
| 316 fileInfo.inlinedCode = parseDartCode(resolvedPath, dartFile.code); | 261 fileInfo.inlinedCode = parseDartCode(resolvedPath, dartFile.code); |
| 317 fileInfo.outputFilename = | |
| 318 _pathMapper.mangle(path.basename(resolvedPath), '.dart', false); | |
| 319 | |
| 320 _processImports(fileInfo); | 262 _processImports(fileInfo); |
| 321 } | 263 } |
| 322 | 264 |
| 323 void _processImports(LibraryInfo library) { | 265 void _processImports(LibraryInfo library) { |
| 324 if (library.userCode == null) return; | 266 if (library.userCode == null) return; |
| 325 | 267 |
| 326 for (var directive in library.userCode.directives) { | 268 for (var directive in library.userCode.directives) { |
| 327 _loadFile(_getDirectiveUrlInfo(library, directive), _parseDartFile); | 269 _loadFile(_getDirectiveUrlInfo(library, directive), _parseDartFile); |
| 328 } | 270 } |
| 329 } | 271 } |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 342 } else if (styleSheet != null) { | 284 } else if (styleSheet != null) { |
| 343 _resolveStyleSheetImports(inputUrl, cssFile.path, styleSheet); | 285 _resolveStyleSheetImports(inputUrl, cssFile.path, styleSheet); |
| 344 fileInfo.styleSheets.add(styleSheet); | 286 fileInfo.styleSheets.add(styleSheet); |
| 345 } | 287 } |
| 346 } | 288 } |
| 347 | 289 |
| 348 /** Load and parse all style sheets referenced with an @imports. */ | 290 /** Load and parse all style sheets referenced with an @imports. */ |
| 349 void _resolveStyleSheetImports(UrlInfo inputUrl, String processingFile, | 291 void _resolveStyleSheetImports(UrlInfo inputUrl, String processingFile, |
| 350 StyleSheet styleSheet) { | 292 StyleSheet styleSheet) { |
| 351 var urlInfos = _time('CSS imports', processingFile, () => | 293 var urlInfos = _time('CSS imports', processingFile, () => |
| 352 findImportsInStyleSheet(styleSheet, _pathMapper.packageRoot, inputUrl, | 294 findImportsInStyleSheet(styleSheet, inputUrl, _messages)); |
| 353 _messages)); | |
| 354 | 295 |
| 355 for (var urlInfo in urlInfos) { | 296 for (var urlInfo in urlInfos) { |
| 356 if (urlInfo == null) break; | 297 if (urlInfo == null) break; |
| 357 // Load any @imported stylesheet files referenced in this style sheet. | 298 // Load any @imported stylesheet files referenced in this style sheet. |
| 358 _loadFile(urlInfo, _parseCssFile); | 299 _loadFile(urlInfo, _parseCssFile); |
| 359 } | 300 } |
| 360 } | 301 } |
| 361 | 302 |
| 362 String _directiveUri(Directive directive) { | 303 String _directiveUri(Directive directive) { |
| 363 var uriDirective = (directive as UriBasedDirective).uri; | 304 var uriDirective = (directive as UriBasedDirective).uri; |
| 364 return (uriDirective as dynamic).value; | 305 return (uriDirective as dynamic).value; |
| 365 } | 306 } |
| 366 | 307 |
| 367 UrlInfo _getDirectiveUrlInfo(LibraryInfo library, Directive directive) { | 308 UrlInfo _getDirectiveUrlInfo(LibraryInfo library, Directive directive) { |
| 368 var uri = _directiveUri(directive); | 309 var uri = _directiveUri(directive); |
| 369 if (uri.startsWith('dart:')) return null; | 310 if (uri.startsWith('dart:')) return null; |
| 370 if (uri.startsWith('package:') && uri.startsWith('package:polymer/')) { | 311 if (uri.startsWith('package:') && uri.startsWith('package:polymer/')) { |
| 371 // Don't process our own package -- we'll implement @observable manually. | 312 // Don't process our own package -- we'll implement @observable manually. |
| 372 return null; | 313 return null; |
| 373 } | 314 } |
| 374 | 315 |
| 375 var span = library.userCode.sourceFile.span( | 316 var span = library.userCode.sourceFile.span( |
| 376 directive.offset, directive.end); | 317 directive.offset, directive.end); |
| 377 return UrlInfo.resolve(uri, library.dartCodeUrl, span, | 318 return UrlInfo.resolve(uri, library.dartCodeUrl, span, _messages); |
| 378 _pathMapper.packageRoot, _messages); | |
| 379 } | 319 } |
| 380 | 320 |
| 381 /** | 321 /** |
| 382 * Transform Dart source code. | |
| 383 * Currently, the only transformation is [transformObservables]. | |
| 384 * Calls _emitModifiedDartFiles to write the transformed files. | |
| 385 */ | |
| 386 void _transformDart() { | |
| 387 var libraries = _findAllDartLibraries(); | |
| 388 | |
| 389 var transformed = []; | |
| 390 for (var lib in libraries) { | |
| 391 var userCode = lib.userCode; | |
| 392 var transaction = transformObservables(userCode.compilationUnit, | |
| 393 userCode.sourceFile, userCode.code, _messages); | |
| 394 if (transaction != null) { | |
| 395 _edits[lib.userCode] = transaction; | |
| 396 if (transaction.hasEdits) { | |
| 397 transformed.add(lib); | |
| 398 } else if (lib.htmlFile != null) { | |
| 399 // All web components will be transformed too. Track that. | |
| 400 transformed.add(lib); | |
| 401 } | |
| 402 } | |
| 403 } | |
| 404 | |
| 405 _findModifiedDartFiles(libraries, transformed); | |
| 406 | |
| 407 libraries.forEach(_fixImports); | |
| 408 | |
| 409 _emitModifiedDartFiles(libraries); | |
| 410 } | |
| 411 | |
| 412 /** | |
| 413 * Finds all Dart code libraries. | 322 * Finds all Dart code libraries. |
| 414 * Each library will have [LibraryInfo.inlinedCode] that is non-null. | 323 * Each library will have [LibraryInfo.inlinedCode] that is non-null. |
| 415 * Also each inlinedCode will be unique. | 324 * Also each inlinedCode will be unique. |
| 416 */ | 325 */ |
| 417 List<LibraryInfo> _findAllDartLibraries() { | 326 List<LibraryInfo> _findAllDartLibraries() { |
| 418 var libs = <LibraryInfo>[]; | 327 var libs = <LibraryInfo>[]; |
| 419 void _addLibrary(LibraryInfo lib) { | 328 void _addLibrary(LibraryInfo lib) { |
| 420 if (lib.inlinedCode != null) libs.add(lib); | 329 if (lib.inlinedCode != null) libs.add(lib); |
| 421 } | 330 } |
| 422 | 331 |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 438 throw new StateError('internal error: ' | 347 throw new StateError('internal error: ' |
| 439 'duplicate user code for ${lib.dartCodeUrl.resolvedPath}.' | 348 'duplicate user code for ${lib.dartCodeUrl.resolvedPath}.' |
| 440 ' Files were: $files'); | 349 ' Files were: $files'); |
| 441 } | 350 } |
| 442 seen.add(lib.inlinedCode); | 351 seen.add(lib.inlinedCode); |
| 443 } | 352 } |
| 444 return true; | 353 return true; |
| 445 } | 354 } |
| 446 | 355 |
| 447 /** | 356 /** |
| 448 * Queue modified Dart files to be written. | |
| 449 * This will not write files that are handled by [WebComponentEmitter] and | |
| 450 * [EntryPointEmitter]. | |
| 451 */ | |
| 452 void _emitModifiedDartFiles(List<LibraryInfo> libraries) { | |
| 453 for (var lib in libraries) { | |
| 454 // Components will get emitted by WebComponentEmitter, and the | |
| 455 // entry point will get emitted by MainPageEmitter. | |
| 456 // So we only need to worry about other .dart files. | |
| 457 if (lib.modified && lib is FileInfo && | |
| 458 lib.htmlFile == null && !lib.isEntryPoint) { | |
| 459 var transaction = _edits[lib.userCode]; | |
| 460 | |
| 461 // Save imports that were modified by _fixImports. | |
| 462 for (var d in lib.userCode.directives) { | |
| 463 transaction.edit(d.offset, d.end, d.toString()); | |
| 464 } | |
| 465 | |
| 466 if (!lib.userCode.isPart) { | |
| 467 var pos = lib.userCode.firstPartOffset; | |
| 468 // Note: we use a different prefix than "autogenerated" to make | |
| 469 // ChangeRecord unambiguous. Otherwise it would be imported by this | |
| 470 // and polymer, resulting in a collision. | |
| 471 // TODO(jmesserly): only generate this for libraries that need it. | |
| 472 transaction.edit(pos, pos, "\nimport " | |
| 473 "'package:observe/observe.dart' as __observe;\n"); | |
| 474 } | |
| 475 _emitFileAndSourceMaps(lib, transaction.commit(), lib.dartCodeUrl); | |
| 476 } | |
| 477 } | |
| 478 } | |
| 479 | |
| 480 /** | |
| 481 * This method computes which Dart files have been modified, starting | 357 * This method computes which Dart files have been modified, starting |
| 482 * from [transformed] and marking recursively through all files that import | 358 * from [transformed] and marking recursively through all files that import |
| 483 * the modified files. | 359 * the modified files. |
| 484 */ | 360 */ |
| 485 void _findModifiedDartFiles(List<LibraryInfo> libraries, | 361 void _findModifiedDartFiles(List<LibraryInfo> libraries, |
| 486 List<FileInfo> transformed) { | 362 List<FileInfo> transformed) { |
| 487 | 363 |
| 488 if (transformed.length == 0) return; | 364 if (transformed.length == 0) return; |
| 489 | 365 |
| 490 // Compute files that reference each file, then use this information to | 366 // Compute files that reference each file, then use this information to |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 509 library.referencedBy.forEach(setModified); | 385 library.referencedBy.forEach(setModified); |
| 510 } | 386 } |
| 511 transformed.forEach(setModified); | 387 transformed.forEach(setModified); |
| 512 | 388 |
| 513 for (var lib in libraries) { | 389 for (var lib in libraries) { |
| 514 // We don't need this anymore, so free it. | 390 // We don't need this anymore, so free it. |
| 515 lib.referencedBy = null; | 391 lib.referencedBy = null; |
| 516 } | 392 } |
| 517 } | 393 } |
| 518 | 394 |
| 519 void _fixImports(LibraryInfo library) { | |
| 520 // Fix imports. Modified files must use the generated path, otherwise | |
| 521 // we need to make the path relative to the input. | |
| 522 for (var directive in library.userCode.directives) { | |
| 523 var importPath = _getDirectiveUrlInfo(library, directive); | |
| 524 if (importPath == null) continue; | |
| 525 var importInfo = info[importPath.resolvedPath]; | |
| 526 if (importInfo == null) continue; | |
| 527 | |
| 528 String newUri = null; | |
| 529 if (importInfo.modified) { | |
| 530 // Use the generated URI for this file. | |
| 531 newUri = _pathMapper.importUrlFor(library, importInfo); | |
| 532 } else if (options.rewriteUrls) { | |
| 533 // Get the relative path to the input file. | |
| 534 newUri = _pathMapper.transformUrl( | |
| 535 library.dartCodeUrl.resolvedPath, directive.uri.value); | |
| 536 } | |
| 537 if (newUri != null) { | |
| 538 directive.uri = createStringLiteral(newUri); | |
| 539 } | |
| 540 } | |
| 541 } | |
| 542 | |
| 543 /** Run the analyzer on every input html file. */ | 395 /** Run the analyzer on every input html file. */ |
| 544 void _analyze() { | 396 void _analyze() { |
| 545 var uniqueIds = new IntIterator(); | 397 var uniqueIds = new IntIterator(); |
| 546 for (var file in files) { | 398 for (var file in files) { |
| 547 if (file.isHtml) { | 399 if (file.isHtml) { |
| 548 _time('Analyzed contents', file.path, () => | 400 _time('Analyzed contents', file.path, () => |
| 549 analyzeFile(file, info, uniqueIds, global, _messages, | 401 analyzeFile(file, info, uniqueIds, global, _messages, |
| 550 options.emulateScopedCss)); | 402 options.emulateScopedCss)); |
| 551 } | 403 } |
| 552 } | 404 } |
| 553 } | 405 } |
| 554 | 406 |
| 555 /** Emit the generated code corresponding to each input file. */ | |
| 556 void _emit() { | |
| 557 for (var file in files) { | |
| 558 if (file.isDart || file.isStyleSheet) continue; | |
| 559 _time('Codegen', file.path, () { | |
| 560 var fileInfo = info[file.path]; | |
| 561 _emitComponents(fileInfo); | |
| 562 }); | |
| 563 } | |
| 564 | |
| 565 var entryPoint = files[0]; | |
| 566 assert(info[entryPoint.path].isEntryPoint); | |
| 567 _emitMainDart(entryPoint); | |
| 568 _emitMainHtml(entryPoint); | |
| 569 | |
| 570 assert(_unqiueOutputs()); | |
| 571 } | |
| 572 | |
| 573 bool _unqiueOutputs() { | |
| 574 var seen = new Set(); | |
| 575 for (var file in output) { | |
| 576 if (seen.contains(file.path)) { | |
| 577 throw new StateError('internal error: ' | |
| 578 'duplicate output file ${file.path}. Files were: $output'); | |
| 579 } | |
| 580 seen.add(file.path); | |
| 581 } | |
| 582 return true; | |
| 583 } | |
| 584 | |
| 585 /** Emit the main .dart file. */ | |
| 586 void _emitMainDart(SourceFile file) { | |
| 587 var fileInfo = info[file.path]; | |
| 588 | |
| 589 var codeInfo = fileInfo.userCode; | |
| 590 if (codeInfo != null) { | |
| 591 var printer = new NestedPrinter(0); | |
| 592 if (codeInfo.libraryName == null) { | |
| 593 printer.addLine('library ${fileInfo.libraryName};'); | |
| 594 } | |
| 595 printer.add(codeInfo.code); | |
| 596 _emitFileAndSourceMaps(fileInfo, printer, fileInfo.dartCodeUrl); | |
| 597 } | |
| 598 } | |
| 599 | |
| 600 // TODO(jmesserly): refactor this out of Compiler. | |
| 601 /** Generate an html file with the (trimmed down) main html page. */ | |
| 602 void _emitMainHtml(SourceFile file) { | |
| 603 var fileInfo = info[file.path]; | |
| 604 | |
| 605 var bootstrapName = '${path.basename(file.path)}_bootstrap.dart'; | |
| 606 var bootstrapPath = path.join(path.dirname(file.path), bootstrapName); | |
| 607 var bootstrapOutPath = _pathMapper.outputPath(bootstrapPath, ''); | |
| 608 var bootstrapOutName = path.basename(bootstrapOutPath); | |
| 609 var bootstrapInfo = new FileInfo(new UrlInfo('', bootstrapPath, null)); | |
| 610 var printer = generateBootstrapCode(bootstrapInfo, fileInfo, global, | |
| 611 _pathMapper, options); | |
| 612 printer.build(bootstrapOutPath); | |
| 613 output.add(new OutputFile( | |
| 614 bootstrapOutPath, printer.text, source: file.path)); | |
| 615 | |
| 616 var document = file.document; | |
| 617 var hasCss = _emitAllCss(); | |
| 618 transformMainHtml(document, fileInfo, _pathMapper, hasCss, | |
| 619 options.rewriteUrls, _messages, global, bootstrapOutName); | |
| 620 output.add(new OutputFile(_pathMapper.outputPath(file.path, '.html'), | |
| 621 document.outerHtml, source: file.path)); | |
| 622 } | |
| 623 | |
| 624 // TODO(jmesserly): refactor this and other CSS related transforms out of | 407 // TODO(jmesserly): refactor this and other CSS related transforms out of |
| 625 // Compiler. | 408 // Compiler. |
| 626 /** | 409 /** |
| 627 * Generate an CSS file for all style sheets (main and components). | 410 * Generate an CSS file for all style sheets (main and components). |
| 628 * Returns true if a file was generated, otherwise false. | 411 * Returns true if a file was generated, otherwise false. |
| 629 */ | 412 */ |
| 630 bool _emitAllCss() { | 413 bool _emitAllCss() { |
|
Siggi Cherem (dart-lang)
2013/08/27 23:30:14
On a future CL I plan to go deeper in removing som
| |
| 631 if (!options.emulateScopedCss) return false; | 414 if (!options.emulateScopedCss) return false; |
| 632 | 415 |
| 633 var buff = new StringBuffer(); | 416 var buff = new StringBuffer(); |
| 634 | 417 |
| 635 // Emit all linked style sheet files first. | 418 // Emit all linked style sheet files first. |
| 636 for (var file in files) { | 419 for (var file in files) { |
| 637 var css = new StringBuffer(); | 420 var css = new StringBuffer(); |
| 638 var fileInfo = info[file.path]; | 421 var fileInfo = info[file.path]; |
| 639 if (file.isStyleSheet) { | 422 if (file.isStyleSheet) { |
| 640 for (var styleSheet in fileInfo.styleSheets) { | 423 for (var styleSheet in fileInfo.styleSheets) { |
| 641 // Translate any URIs in CSS. | 424 // Translate any URIs in CSS. |
| 642 rewriteCssUris(_pathMapper, fileInfo.inputUrl.resolvedPath, | 425 rewriteCssUris(_pathMapper, fileInfo.inputUrl.resolvedPath, |
| 643 options.rewriteUrls, styleSheet); | 426 options.rewriteUrls, styleSheet); |
| 644 css.write( | 427 css.write( |
| 645 '/* Auto-generated from style sheet href = ${file.path} */\n' | 428 '/* Auto-generated from style sheet href = ${file.path} */\n' |
| 646 '/* DO NOT EDIT. */\n\n'); | 429 '/* DO NOT EDIT. */\n\n'); |
| 647 css.write(emitStyleSheet(styleSheet, fileInfo)); | 430 css.write(emitStyleSheet(styleSheet, fileInfo)); |
| 648 css.write('\n\n'); | 431 css.write('\n\n'); |
| 649 } | 432 } |
| 650 | |
| 651 // Emit the linked style sheet in the output directory. | |
| 652 if (fileInfo.inputUrl.url != _resetCssFile) { | |
| 653 var outCss = _pathMapper.outputPath(fileInfo.inputUrl.resolvedPath, | |
| 654 ''); | |
| 655 output.add(new OutputFile(outCss, css.toString())); | |
| 656 } | |
| 657 } | 433 } |
| 658 } | 434 } |
| 659 | 435 |
| 660 // Emit all CSS for each component (style scoped). | 436 // Emit all CSS for each component (style scoped). |
| 661 for (var file in files) { | 437 for (var file in files) { |
| 662 if (file.isHtml) { | 438 if (file.isHtml) { |
| 663 var fileInfo = info[file.path]; | 439 var fileInfo = info[file.path]; |
| 664 for (var component in fileInfo.declaredComponents) { | 440 for (var component in fileInfo.declaredComponents) { |
| 665 for (var styleSheet in component.styleSheets) { | 441 for (var styleSheet in component.styleSheets) { |
| 666 // Translate any URIs in CSS. | 442 // Translate any URIs in CSS. |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 705 } | 481 } |
| 706 } | 482 } |
| 707 | 483 |
| 708 if (buff.isEmpty) return false; | 484 if (buff.isEmpty) return false; |
| 709 | 485 |
| 710 var cssPath = _pathMapper.outputPath(_mainPath, '.css', true); | 486 var cssPath = _pathMapper.outputPath(_mainPath, '.css', true); |
| 711 output.add(new OutputFile(cssPath, buff.toString())); | 487 output.add(new OutputFile(cssPath, buff.toString())); |
| 712 return true; | 488 return true; |
| 713 } | 489 } |
| 714 | 490 |
| 715 /** Emits the Dart code for all components in [fileInfo]. */ | |
| 716 void _emitComponents(FileInfo fileInfo) { | |
| 717 for (var component in fileInfo.declaredComponents) { | |
| 718 // TODO(terry): Handle more than one stylesheet per component | |
| 719 if (component.styleSheets.length > 1 && options.emulateScopedCss) { | |
| 720 var span = component.externalFile != null | |
| 721 ? component.externalFile.sourceSpan : null; | |
| 722 _messages.warning( | |
| 723 'Component has more than one stylesheet - first stylesheet used.', | |
| 724 span); | |
| 725 } | |
| 726 var printer = emitPolymerElement( | |
| 727 component, _pathMapper, _edits[component.userCode], options); | |
| 728 _emitFileAndSourceMaps(component, printer, component.externalFile); | |
| 729 } | |
| 730 } | |
| 731 | |
| 732 /** | |
| 733 * Emits a file that was created using [NestedPrinter] and it's corresponding | |
| 734 * source map file. | |
| 735 */ | |
| 736 void _emitFileAndSourceMaps( | |
| 737 LibraryInfo lib, NestedPrinter printer, UrlInfo dartCodeUrl) { | |
| 738 // Bail if we had an error generating the code for the file. | |
| 739 if (printer == null) return; | |
| 740 | |
| 741 var libPath = _pathMapper.outputLibraryPath(lib); | |
| 742 var dir = path.dirname(libPath); | |
| 743 var filename = path.basename(libPath); | |
| 744 printer.add('\n//# sourceMappingURL=$filename.map'); | |
| 745 printer.build(libPath); | |
| 746 var sourcePath = dartCodeUrl != null ? dartCodeUrl.resolvedPath : null; | |
| 747 output.add(new OutputFile(libPath, printer.text, source: sourcePath)); | |
| 748 // Fix-up the paths in the source map file | |
| 749 var sourceMap = json.parse(printer.map); | |
| 750 var urls = sourceMap['sources']; | |
| 751 for (int i = 0; i < urls.length; i++) { | |
| 752 urls[i] = path.relative(urls[i], from: dir); | |
| 753 } | |
| 754 output.add(new OutputFile(path.join(dir, '$filename.map'), | |
| 755 json.stringify(sourceMap))); | |
| 756 } | |
| 757 | |
| 758 _time(String logMessage, String filePath, callback(), | 491 _time(String logMessage, String filePath, callback(), |
| 759 {bool printTime: false}) { | 492 {bool printTime: false}) { |
| 760 var message = new StringBuffer(); | 493 var message = new StringBuffer(); |
| 761 message.write(logMessage); | 494 message.write(logMessage); |
| 762 var filename = path.basename(filePath); | 495 var filename = path.basename(filePath); |
| 763 for (int i = (60 - logMessage.length - filename.length); i > 0 ; i--) { | 496 for (int i = (60 - logMessage.length - filename.length); i > 0 ; i--) { |
| 764 message.write(' '); | 497 message.write(' '); |
| 765 } | 498 } |
| 766 message.write(filename); | 499 message.write(filename); |
| 767 return time(message.toString(), callback, | 500 return time(message.toString(), callback, |
| 768 printTime: options.verbose || printTime); | 501 printTime: options.verbose || printTime); |
| 769 } | 502 } |
| 770 } | 503 } |
| OLD | NEW |