| 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; |
| 59 String _packageRoot; |
| 63 String _resetCssFile; | 60 String _resetCssFile; |
| 64 StyleSheet _cssResetStyleSheet; | 61 StyleSheet _cssResetStyleSheet; |
| 65 PathMapper _pathMapper; | |
| 66 Messages _messages; | 62 Messages _messages; |
| 67 | 63 |
| 68 FutureGroup _tasks; | 64 FutureGroup _tasks; |
| 69 Set _processed; | 65 Set _processed; |
| 70 | 66 |
| 71 /** Information about source [files] given their href. */ | 67 /** Information about source [files] given their href. */ |
| 72 final Map<String, FileInfo> info = new SplayTreeMap<String, FileInfo>(); | 68 final Map<String, FileInfo> info = new SplayTreeMap<String, FileInfo>(); |
| 73 final _edits = new Map<DartCodeInfo, TextEditTransaction>(); | 69 final _edits = new Map<DartCodeInfo, TextEditTransaction>(); |
| 74 | 70 |
| 75 final GlobalInfo global = new GlobalInfo(); | 71 final GlobalInfo global = new GlobalInfo(); |
| 76 | 72 |
| 77 /** Creates a compiler with [options] using [fileSystem]. */ | 73 /** Creates a compiler with [options] using [fileSystem]. */ |
| 78 Compiler(this.fileSystem, this.options, this._messages) { | 74 Compiler(this.fileSystem, this.options, this._messages) { |
| 79 _mainPath = options.inputFile; | 75 _mainPath = options.inputFile; |
| 80 var mainDir = path.dirname(_mainPath); | 76 var mainDir = path.dirname(_mainPath); |
| 81 var baseDir = options.baseDir != null ? options.baseDir : mainDir; | 77 var baseDir = options.baseDir != null ? options.baseDir : mainDir; |
| 82 var outputDir = options.outputDir != null ? options.outputDir : mainDir; | 78 _packageRoot = options.packageRoot != null ? options.packageRoot |
| 83 var packageRoot = options.packageRoot != null ? options.packageRoot | |
| 84 : path.join(path.dirname(_mainPath), 'packages'); | 79 : path.join(path.dirname(_mainPath), 'packages'); |
| 85 | 80 |
| 86 if (options.resetCssFile != null) { | 81 if (options.resetCssFile != null) { |
| 87 _resetCssFile = options.resetCssFile; | 82 _resetCssFile = options.resetCssFile; |
| 88 if (path.isRelative(_resetCssFile)) { | 83 if (path.isRelative(_resetCssFile)) { |
| 89 // If CSS reset file path is relative from our current path. | 84 // If CSS reset file path is relative from our current path. |
| 90 _resetCssFile = path.resolve(_resetCssFile); | 85 _resetCssFile = path.resolve(_resetCssFile); |
| 91 } | 86 } |
| 92 } | 87 } |
| 93 | 88 |
| 94 // Normalize paths - all should be relative or absolute paths. | 89 // Normalize paths - all should be relative or absolute paths. |
| 95 if (path.isAbsolute(_mainPath) || path.isAbsolute(baseDir) || | 90 if (path.isAbsolute(_mainPath) || path.isAbsolute(baseDir) |
| 96 path.isAbsolute(outputDir) || path.isAbsolute(packageRoot)) { | 91 || path.isAbsolute(_packageRoot)) { |
| 97 if (path.isRelative(_mainPath)) _mainPath = path.resolve(_mainPath); | 92 if (path.isRelative(_mainPath)) _mainPath = path.resolve(_mainPath); |
| 98 if (path.isRelative(baseDir)) baseDir = path.resolve(baseDir); | 93 if (path.isRelative(baseDir)) baseDir = path.resolve(baseDir); |
| 99 if (path.isRelative(outputDir)) outputDir = path.resolve(outputDir); | 94 if (path.isRelative(_packageRoot)) { |
| 100 if (path.isRelative(packageRoot)) { | 95 _packageRoot = path.resolve(_packageRoot); |
| 101 packageRoot = path.resolve(packageRoot); | |
| 102 } | 96 } |
| 103 } | 97 } |
| 104 _pathMapper = new PathMapper( | |
| 105 baseDir, outputDir, packageRoot, options.forceMangle, | |
| 106 options.rewriteUrls); | |
| 107 } | 98 } |
| 108 | 99 |
| 109 /** Compile the application starting from the given input file. */ | 100 /** Compile the application starting from the given input file. */ |
| 110 Future run() { | 101 Future run() { |
| 111 if (path.basename(_mainPath).endsWith('.dart')) { | 102 if (path.basename(_mainPath).endsWith('.dart')) { |
| 112 _messages.error("Please provide an HTML file as your entry point.", | 103 _messages.error("Please provide an HTML file as your entry point.", |
| 113 null); | 104 null); |
| 114 return new Future.value(null); | 105 return new Future.value(null); |
| 115 } | 106 } |
| 116 return _parseAndDiscover(_mainPath).then((_) { | 107 return _parseAndDiscover(_mainPath).then((_) { |
| 117 _analyze(); | 108 _analyze(); |
| 118 | 109 |
| 119 // Analyze all CSS files. | 110 // Analyze all CSS files. |
| 120 _time('Analyzed Style Sheets', '', () => | 111 _time('Analyzed Style Sheets', '', () => |
| 121 analyzeCss(_pathMapper.packageRoot, files, info, | 112 analyzeCss(_packageRoot, files, info, |
| 122 global.pseudoElements, _messages, | 113 global.pseudoElements, _messages, |
| 123 warningsAsErrors: options.warningsAsErrors)); | 114 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 }); | 115 }); |
| 131 } | 116 } |
| 132 | 117 |
| 133 /** | 118 /** |
| 134 * Asynchronously parse [inputFile] and transitively discover web components | 119 * Asynchronously parse [inputFile] and transitively discover web components |
| 135 * to load and parse. Returns a future that completes when all files are | 120 * to load and parse. Returns a future that completes when all files are |
| 136 * processed. | 121 * processed. |
| 137 */ | 122 */ |
| 138 Future _parseAndDiscover(String inputFile) { | 123 Future _parseAndDiscover(String inputFile) { |
| 139 _tasks = new FutureGroup(); | 124 _tasks = new FutureGroup(); |
| 140 _processed = new Set(); | 125 _processed = new Set(); |
| 141 _processed.add(inputFile); | 126 _processed.add(inputFile); |
| 142 _tasks.add(_parseHtmlFile(new UrlInfo(inputFile, inputFile, null))); | 127 _tasks.add(_parseHtmlFile(new UrlInfo(inputFile, inputFile, null))); |
| 143 return _tasks.future; | 128 return _tasks.future; |
| 144 } | 129 } |
| 145 | 130 |
| 146 void _processHtmlFile(UrlInfo inputUrl, SourceFile file) { | 131 void _processHtmlFile(UrlInfo inputUrl, SourceFile file) { |
| 147 if (file == null) return; | 132 if (file == null) return; |
| 148 | 133 |
| 149 bool isEntryPoint = _processed.length == 1; | 134 bool isEntryPoint = _processed.length == 1; |
| 150 | 135 |
| 151 files.add(file); | 136 files.add(file); |
| 152 | 137 |
| 153 var fileInfo = _time('Analyzed definitions', inputUrl.url, () { | 138 var fileInfo = _time('Analyzed definitions', inputUrl.url, () { |
| 154 return analyzeDefinitions(global, inputUrl, file.document, | 139 return analyzeDefinitions(global, inputUrl, file.document, _packageRoot, |
| 155 _pathMapper.packageRoot, _messages, isEntryPoint: isEntryPoint); | 140 _messages, isEntryPoint: isEntryPoint); |
| 156 }); | 141 }); |
| 157 info[inputUrl.resolvedPath] = fileInfo; | 142 info[inputUrl.resolvedPath] = fileInfo; |
| 158 | 143 |
| 159 if (isEntryPoint && _resetCssFile != null) { | 144 if (isEntryPoint && _resetCssFile != null) { |
| 160 _processed.add(_resetCssFile); | 145 _processed.add(_resetCssFile); |
| 161 _tasks.add(_parseCssFile(new UrlInfo(_resetCssFile, _resetCssFile, | 146 _tasks.add(_parseCssFile(new UrlInfo(_resetCssFile, _resetCssFile, |
| 162 null))); | 147 null))); |
| 163 } | 148 } |
| 164 | 149 |
| 165 _setOutputFilenames(fileInfo); | |
| 166 _processImports(fileInfo); | 150 _processImports(fileInfo); |
| 167 | 151 |
| 168 // Load component files referenced by [file]. | 152 // Load component files referenced by [file]. |
| 169 for (var link in fileInfo.componentLinks) { | 153 for (var link in fileInfo.componentLinks) { |
| 170 _loadFile(link, _parseHtmlFile); | 154 _loadFile(link, _parseHtmlFile); |
| 171 } | 155 } |
| 172 | 156 |
| 173 // Load stylesheet files referenced by [file]. | 157 // Load stylesheet files referenced by [file]. |
| 174 for (var link in fileInfo.styleSheetHrefs) { | 158 for (var link in fileInfo.styleSheetHrefs) { |
| 175 _loadFile(link, _parseCssFile); | 159 _loadFile(link, _parseCssFile); |
| 176 } | 160 } |
| 177 | 161 |
| 178 // Load .dart files being referenced in the page. | 162 // Load .dart files being referenced in the page. |
| 179 _loadFile(fileInfo.externalFile, _parseDartFile); | 163 _loadFile(fileInfo.externalFile, _parseDartFile); |
| 180 | 164 |
| 181 // Process any @imports inside of a <style> tag. | 165 // Process any @imports inside of a <style> tag. |
| 182 var urlInfos = findUrlsImported(fileInfo, fileInfo.inputUrl, | 166 var urlInfos = findUrlsImported(fileInfo, fileInfo.inputUrl, _packageRoot, |
| 183 _pathMapper.packageRoot, file.document, _messages, options); | 167 file.document, _messages, options); |
| 184 for (var urlInfo in urlInfos) { | 168 for (var urlInfo in urlInfos) { |
| 185 _loadFile(urlInfo, _parseCssFile); | 169 _loadFile(urlInfo, _parseCssFile); |
| 186 } | 170 } |
| 187 | 171 |
| 188 // Load .dart files being referenced in components. | 172 // Load .dart files being referenced in components. |
| 189 for (var component in fileInfo.declaredComponents) { | 173 for (var component in fileInfo.declaredComponents) { |
| 190 if (component.externalFile != null) { | 174 if (component.externalFile != null) { |
| 191 _loadFile(component.externalFile, _parseDartFile); | 175 _loadFile(component.externalFile, _parseDartFile); |
| 192 } else if (component.userCode != null) { | 176 } else if (component.userCode != null) { |
| 193 _processImports(component); | 177 _processImports(component); |
| 194 } | 178 } |
| 195 | 179 |
| 196 // Process any @imports inside of the <style> tag in a component. | 180 // Process any @imports inside of the <style> tag in a component. |
| 197 var urlInfos = findUrlsImported(component, | 181 var urlInfos = findUrlsImported(component, |
| 198 component.declaringFile.inputUrl, _pathMapper.packageRoot, | 182 component.declaringFile.inputUrl, _packageRoot, component.element, |
| 199 component.element, _messages, options); | 183 _messages, options); |
| 200 for (var urlInfo in urlInfos) { | 184 for (var urlInfo in urlInfos) { |
| 201 _loadFile(urlInfo, _parseCssFile); | 185 _loadFile(urlInfo, _parseCssFile); |
| 202 } | 186 } |
| 203 } | 187 } |
| 204 } | 188 } |
| 205 | 189 |
| 206 /** | 190 /** |
| 207 * Helper function to load [urlInfo] and parse it using [loadAndParse] if it | 191 * Helper function to load [urlInfo] and parse it using [loadAndParse] if it |
| 208 * hasn't been loaded before. | 192 * hasn't been loaded before. |
| 209 */ | 193 */ |
| 210 void _loadFile(UrlInfo urlInfo, Future loadAndParse(UrlInfo inputUrl)) { | 194 void _loadFile(UrlInfo urlInfo, Future loadAndParse(UrlInfo inputUrl)) { |
| 211 if (urlInfo == null) return; | 195 if (urlInfo == null) return; |
| 212 var resolvedPath = urlInfo.resolvedPath; | 196 var resolvedPath = urlInfo.resolvedPath; |
| 213 if (!_processed.contains(resolvedPath)) { | 197 if (!_processed.contains(resolvedPath)) { |
| 214 _processed.add(resolvedPath); | 198 _processed.add(resolvedPath); |
| 215 _tasks.add(loadAndParse(urlInfo)); | 199 _tasks.add(loadAndParse(urlInfo)); |
| 216 } | 200 } |
| 217 } | 201 } |
| 218 | 202 |
| 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. */ | 203 /** Parse an HTML file. */ |
| 245 Future _parseHtmlFile(UrlInfo inputUrl) { | 204 Future _parseHtmlFile(UrlInfo inputUrl) { |
| 246 if (!_pathMapper.checkInputPath(inputUrl, _messages)) { | |
| 247 return new Future<SourceFile>.value(null); | |
| 248 } | |
| 249 var filePath = inputUrl.resolvedPath; | 205 var filePath = inputUrl.resolvedPath; |
| 250 return fileSystem.readTextOrBytes(filePath) | 206 return fileSystem.readTextOrBytes(filePath) |
| 251 .catchError((e) => _readError(e, inputUrl)) | 207 .catchError((e) => _readError(e, inputUrl)) |
| 252 .then((source) { | 208 .then((source) { |
| 253 if (source == null) return; | 209 if (source == null) return; |
| 254 var file = new SourceFile(filePath); | 210 var file = new SourceFile(filePath); |
| 255 file.document = _time('Parsed', filePath, | 211 file.document = _time('Parsed', filePath, |
| 256 () => parseHtml(source, filePath, _messages)); | 212 () => parseHtml(source, filePath, _messages)); |
| 257 _processHtmlFile(inputUrl, file); | 213 _processHtmlFile(inputUrl, file); |
| 258 }); | 214 }); |
| 259 } | 215 } |
| 260 | 216 |
| 261 /** Parse a Dart file. */ | 217 /** Parse a Dart file. */ |
| 262 Future _parseDartFile(UrlInfo inputUrl) { | 218 Future _parseDartFile(UrlInfo inputUrl) { |
| 263 if (!_pathMapper.checkInputPath(inputUrl, _messages)) { | |
| 264 return new Future<SourceFile>.value(null); | |
| 265 } | |
| 266 var filePath = inputUrl.resolvedPath; | 219 var filePath = inputUrl.resolvedPath; |
| 267 return fileSystem.readText(filePath) | 220 return fileSystem.readText(filePath) |
| 268 .catchError((e) => _readError(e, inputUrl)) | 221 .catchError((e) => _readError(e, inputUrl)) |
| 269 .then((code) { | 222 .then((code) { |
| 270 if (code == null) return; | 223 if (code == null) return; |
| 271 var file = new SourceFile(filePath, type: SourceFile.DART); | 224 var file = new SourceFile(filePath, type: SourceFile.DART); |
| 272 file.code = code; | 225 file.code = code; |
| 273 _processDartFile(inputUrl, file); | 226 _processDartFile(inputUrl, file); |
| 274 }); | 227 }); |
| 275 } | 228 } |
| 276 | 229 |
| 277 /** Parse a stylesheet file. */ | 230 /** Parse a stylesheet file. */ |
| 278 Future _parseCssFile(UrlInfo inputUrl) { | 231 Future _parseCssFile(UrlInfo inputUrl) { |
| 279 if (!options.emulateScopedCss || | 232 if (!options.emulateScopedCss) { |
| 280 !_pathMapper.checkInputPath(inputUrl, _messages)) { | |
| 281 return new Future<SourceFile>.value(null); | 233 return new Future<SourceFile>.value(null); |
| 282 } | 234 } |
| 283 var filePath = inputUrl.resolvedPath; | 235 var filePath = inputUrl.resolvedPath; |
| 284 return fileSystem.readText(filePath) | 236 return fileSystem.readText(filePath) |
| 285 .catchError((e) => _readError(e, inputUrl, isWarning: true)) | 237 .catchError((e) => _readError(e, inputUrl, isWarning: true)) |
| 286 .then((code) { | 238 .then((code) { |
| 287 if (code == null) return; | 239 if (code == null) return; |
| 288 var file = new SourceFile(filePath, type: SourceFile.STYLESHEET); | 240 var file = new SourceFile(filePath, type: SourceFile.STYLESHEET); |
| 289 file.code = code; | 241 file.code = code; |
| 290 _processCssFile(inputUrl, file); | 242 _processCssFile(inputUrl, file); |
| (...skipping 16 matching lines...) Expand all Loading... |
| 307 | 259 |
| 308 void _processDartFile(UrlInfo inputUrl, SourceFile dartFile) { | 260 void _processDartFile(UrlInfo inputUrl, SourceFile dartFile) { |
| 309 if (dartFile == null) return; | 261 if (dartFile == null) return; |
| 310 | 262 |
| 311 files.add(dartFile); | 263 files.add(dartFile); |
| 312 | 264 |
| 313 var resolvedPath = inputUrl.resolvedPath; | 265 var resolvedPath = inputUrl.resolvedPath; |
| 314 var fileInfo = new FileInfo(inputUrl); | 266 var fileInfo = new FileInfo(inputUrl); |
| 315 info[resolvedPath] = fileInfo; | 267 info[resolvedPath] = fileInfo; |
| 316 fileInfo.inlinedCode = parseDartCode(resolvedPath, dartFile.code); | 268 fileInfo.inlinedCode = parseDartCode(resolvedPath, dartFile.code); |
| 317 fileInfo.outputFilename = | |
| 318 _pathMapper.mangle(path.basename(resolvedPath), '.dart', false); | |
| 319 | |
| 320 _processImports(fileInfo); | 269 _processImports(fileInfo); |
| 321 } | 270 } |
| 322 | 271 |
| 323 void _processImports(LibraryInfo library) { | 272 void _processImports(LibraryInfo library) { |
| 324 if (library.userCode == null) return; | 273 if (library.userCode == null) return; |
| 325 | 274 |
| 326 for (var directive in library.userCode.directives) { | 275 for (var directive in library.userCode.directives) { |
| 327 _loadFile(_getDirectiveUrlInfo(library, directive), _parseDartFile); | 276 _loadFile(_getDirectiveUrlInfo(library, directive), _parseDartFile); |
| 328 } | 277 } |
| 329 } | 278 } |
| (...skipping 12 matching lines...) Expand all Loading... |
| 342 } else if (styleSheet != null) { | 291 } else if (styleSheet != null) { |
| 343 _resolveStyleSheetImports(inputUrl, cssFile.path, styleSheet); | 292 _resolveStyleSheetImports(inputUrl, cssFile.path, styleSheet); |
| 344 fileInfo.styleSheets.add(styleSheet); | 293 fileInfo.styleSheets.add(styleSheet); |
| 345 } | 294 } |
| 346 } | 295 } |
| 347 | 296 |
| 348 /** Load and parse all style sheets referenced with an @imports. */ | 297 /** Load and parse all style sheets referenced with an @imports. */ |
| 349 void _resolveStyleSheetImports(UrlInfo inputUrl, String processingFile, | 298 void _resolveStyleSheetImports(UrlInfo inputUrl, String processingFile, |
| 350 StyleSheet styleSheet) { | 299 StyleSheet styleSheet) { |
| 351 var urlInfos = _time('CSS imports', processingFile, () => | 300 var urlInfos = _time('CSS imports', processingFile, () => |
| 352 findImportsInStyleSheet(styleSheet, _pathMapper.packageRoot, inputUrl, | 301 findImportsInStyleSheet(styleSheet, _packageRoot, inputUrl, _messages)); |
| 353 _messages)); | |
| 354 | 302 |
| 355 for (var urlInfo in urlInfos) { | 303 for (var urlInfo in urlInfos) { |
| 356 if (urlInfo == null) break; | 304 if (urlInfo == null) break; |
| 357 // Load any @imported stylesheet files referenced in this style sheet. | 305 // Load any @imported stylesheet files referenced in this style sheet. |
| 358 _loadFile(urlInfo, _parseCssFile); | 306 _loadFile(urlInfo, _parseCssFile); |
| 359 } | 307 } |
| 360 } | 308 } |
| 361 | 309 |
| 362 String _directiveUri(Directive directive) { | 310 String _directiveUri(Directive directive) { |
| 363 var uriDirective = (directive as UriBasedDirective).uri; | 311 var uriDirective = (directive as UriBasedDirective).uri; |
| 364 return (uriDirective as dynamic).value; | 312 return (uriDirective as dynamic).value; |
| 365 } | 313 } |
| 366 | 314 |
| 367 UrlInfo _getDirectiveUrlInfo(LibraryInfo library, Directive directive) { | 315 UrlInfo _getDirectiveUrlInfo(LibraryInfo library, Directive directive) { |
| 368 var uri = _directiveUri(directive); | 316 var uri = _directiveUri(directive); |
| 369 if (uri.startsWith('dart:')) return null; | 317 if (uri.startsWith('dart:')) return null; |
| 370 if (uri.startsWith('package:') && uri.startsWith('package:polymer/')) { | 318 if (uri.startsWith('package:') && uri.startsWith('package:polymer/')) { |
| 371 // Don't process our own package -- we'll implement @observable manually. | 319 // Don't process our own package -- we'll implement @observable manually. |
| 372 return null; | 320 return null; |
| 373 } | 321 } |
| 374 | 322 |
| 375 var span = library.userCode.sourceFile.span( | 323 var span = library.userCode.sourceFile.span( |
| 376 directive.offset, directive.end); | 324 directive.offset, directive.end); |
| 377 return UrlInfo.resolve(uri, library.dartCodeUrl, span, | 325 return UrlInfo.resolve(uri, library.dartCodeUrl, span, _packageRoot, |
| 378 _pathMapper.packageRoot, _messages); | 326 _messages); |
| 379 } | 327 } |
| 380 | 328 |
| 381 /** | 329 /** |
| 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. | 330 * Finds all Dart code libraries. |
| 414 * Each library will have [LibraryInfo.inlinedCode] that is non-null. | 331 * Each library will have [LibraryInfo.inlinedCode] that is non-null. |
| 415 * Also each inlinedCode will be unique. | 332 * Also each inlinedCode will be unique. |
| 416 */ | 333 */ |
| 417 List<LibraryInfo> _findAllDartLibraries() { | 334 List<LibraryInfo> _findAllDartLibraries() { |
| 418 var libs = <LibraryInfo>[]; | 335 var libs = <LibraryInfo>[]; |
| 419 void _addLibrary(LibraryInfo lib) { | 336 void _addLibrary(LibraryInfo lib) { |
| 420 if (lib.inlinedCode != null) libs.add(lib); | 337 if (lib.inlinedCode != null) libs.add(lib); |
| 421 } | 338 } |
| 422 | 339 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 438 throw new StateError('internal error: ' | 355 throw new StateError('internal error: ' |
| 439 'duplicate user code for ${lib.dartCodeUrl.resolvedPath}.' | 356 'duplicate user code for ${lib.dartCodeUrl.resolvedPath}.' |
| 440 ' Files were: $files'); | 357 ' Files were: $files'); |
| 441 } | 358 } |
| 442 seen.add(lib.inlinedCode); | 359 seen.add(lib.inlinedCode); |
| 443 } | 360 } |
| 444 return true; | 361 return true; |
| 445 } | 362 } |
| 446 | 363 |
| 447 /** | 364 /** |
| 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 | 365 * This method computes which Dart files have been modified, starting |
| 482 * from [transformed] and marking recursively through all files that import | 366 * from [transformed] and marking recursively through all files that import |
| 483 * the modified files. | 367 * the modified files. |
| 484 */ | 368 */ |
| 485 void _findModifiedDartFiles(List<LibraryInfo> libraries, | 369 void _findModifiedDartFiles(List<LibraryInfo> libraries, |
| 486 List<FileInfo> transformed) { | 370 List<FileInfo> transformed) { |
| 487 | 371 |
| 488 if (transformed.length == 0) return; | 372 if (transformed.length == 0) return; |
| 489 | 373 |
| 490 // Compute files that reference each file, then use this information to | 374 // Compute files that reference each file, then use this information to |
| (...skipping 18 matching lines...) Expand all Loading... |
| 509 library.referencedBy.forEach(setModified); | 393 library.referencedBy.forEach(setModified); |
| 510 } | 394 } |
| 511 transformed.forEach(setModified); | 395 transformed.forEach(setModified); |
| 512 | 396 |
| 513 for (var lib in libraries) { | 397 for (var lib in libraries) { |
| 514 // We don't need this anymore, so free it. | 398 // We don't need this anymore, so free it. |
| 515 lib.referencedBy = null; | 399 lib.referencedBy = null; |
| 516 } | 400 } |
| 517 } | 401 } |
| 518 | 402 |
| 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. */ | 403 /** Run the analyzer on every input html file. */ |
| 544 void _analyze() { | 404 void _analyze() { |
| 545 var uniqueIds = new IntIterator(); | 405 var uniqueIds = new IntIterator(); |
| 546 for (var file in files) { | 406 for (var file in files) { |
| 547 if (file.isHtml) { | 407 if (file.isHtml) { |
| 548 _time('Analyzed contents', file.path, () => | 408 _time('Analyzed contents', file.path, () => |
| 549 analyzeFile(file, info, uniqueIds, global, _messages, | 409 analyzeFile(file, info, uniqueIds, global, _messages, |
| 550 options.emulateScopedCss)); | 410 options.emulateScopedCss)); |
| 551 } | 411 } |
| 552 } | 412 } |
| 553 } | 413 } |
| 554 | 414 |
| 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 | 415 // TODO(jmesserly): refactor this and other CSS related transforms out of |
| 625 // Compiler. | 416 // Compiler. |
| 626 /** | 417 /** |
| 627 * Generate an CSS file for all style sheets (main and components). | 418 * Generate an CSS file for all style sheets (main and components). |
| 628 * Returns true if a file was generated, otherwise false. | 419 * Returns true if a file was generated, otherwise false. |
| 629 */ | 420 */ |
| 630 bool _emitAllCss() { | 421 bool _emitAllCss() { |
| 631 if (!options.emulateScopedCss) return false; | 422 if (!options.emulateScopedCss) return false; |
| 632 | 423 |
| 633 var buff = new StringBuffer(); | 424 var buff = new StringBuffer(); |
| 634 | 425 |
| 635 // Emit all linked style sheet files first. | 426 // Emit all linked style sheet files first. |
| 636 for (var file in files) { | 427 for (var file in files) { |
| 637 var css = new StringBuffer(); | 428 var css = new StringBuffer(); |
| 638 var fileInfo = info[file.path]; | 429 var fileInfo = info[file.path]; |
| 639 if (file.isStyleSheet) { | 430 if (file.isStyleSheet) { |
| 640 for (var styleSheet in fileInfo.styleSheets) { | 431 for (var styleSheet in fileInfo.styleSheets) { |
| 641 // Translate any URIs in CSS. | 432 // Translate any URIs in CSS. |
| 642 rewriteCssUris(_pathMapper, fileInfo.inputUrl.resolvedPath, | 433 rewriteCssUris(_pathMapper, fileInfo.inputUrl.resolvedPath, |
| 643 options.rewriteUrls, styleSheet); | 434 options.rewriteUrls, styleSheet); |
| 644 css.write( | 435 css.write( |
| 645 '/* Auto-generated from style sheet href = ${file.path} */\n' | 436 '/* Auto-generated from style sheet href = ${file.path} */\n' |
| 646 '/* DO NOT EDIT. */\n\n'); | 437 '/* DO NOT EDIT. */\n\n'); |
| 647 css.write(emitStyleSheet(styleSheet, fileInfo)); | 438 css.write(emitStyleSheet(styleSheet, fileInfo)); |
| 648 css.write('\n\n'); | 439 css.write('\n\n'); |
| 649 } | 440 } |
| 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 } | 441 } |
| 658 } | 442 } |
| 659 | 443 |
| 660 // Emit all CSS for each component (style scoped). | 444 // Emit all CSS for each component (style scoped). |
| 661 for (var file in files) { | 445 for (var file in files) { |
| 662 if (file.isHtml) { | 446 if (file.isHtml) { |
| 663 var fileInfo = info[file.path]; | 447 var fileInfo = info[file.path]; |
| 664 for (var component in fileInfo.declaredComponents) { | 448 for (var component in fileInfo.declaredComponents) { |
| 665 for (var styleSheet in component.styleSheets) { | 449 for (var styleSheet in component.styleSheets) { |
| 666 // Translate any URIs in CSS. | 450 // Translate any URIs in CSS. |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 705 } | 489 } |
| 706 } | 490 } |
| 707 | 491 |
| 708 if (buff.isEmpty) return false; | 492 if (buff.isEmpty) return false; |
| 709 | 493 |
| 710 var cssPath = _pathMapper.outputPath(_mainPath, '.css', true); | 494 var cssPath = _pathMapper.outputPath(_mainPath, '.css', true); |
| 711 output.add(new OutputFile(cssPath, buff.toString())); | 495 output.add(new OutputFile(cssPath, buff.toString())); |
| 712 return true; | 496 return true; |
| 713 } | 497 } |
| 714 | 498 |
| 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(), | 499 _time(String logMessage, String filePath, callback(), |
| 759 {bool printTime: false}) { | 500 {bool printTime: false}) { |
| 760 var message = new StringBuffer(); | 501 var message = new StringBuffer(); |
| 761 message.write(logMessage); | 502 message.write(logMessage); |
| 762 var filename = path.basename(filePath); | 503 var filename = path.basename(filePath); |
| 763 for (int i = (60 - logMessage.length - filename.length); i > 0 ; i--) { | 504 for (int i = (60 - logMessage.length - filename.length); i > 0 ; i--) { |
| 764 message.write(' '); | 505 message.write(' '); |
| 765 } | 506 } |
| 766 message.write(filename); | 507 message.write(filename); |
| 767 return time(message.toString(), callback, | 508 return time(message.toString(), callback, |
| 768 printTime: options.verbose || printTime); | 509 printTime: options.verbose || printTime); |
| 769 } | 510 } |
| 770 } | 511 } |
| OLD | NEW |