| OLD | NEW |
| 1 library angular.template_cache_generator; | 1 library angular.template_cache_generator; |
| 2 | 2 |
| 3 import 'dart:io'; | 3 import 'dart:io'; |
| 4 import 'dart:async'; | 4 import 'dart:async'; |
| 5 import 'dart:collection'; | 5 import 'dart:collection'; |
| 6 | 6 |
| 7 import 'package:analyzer/src/generated/ast.dart'; | 7 import 'package:analyzer/src/generated/ast.dart'; |
| 8 import 'package:angular/tools/source_crawler_impl.dart'; | 8 import 'package:analyzer/src/generated/source.dart'; |
| 9 import 'package:analyzer/src/generated/element.dart'; |
| 10 import 'package:di/generator.dart'; |
| 9 | 11 |
| 10 const String PACKAGE_PREFIX = 'package:'; | 12 const String PACKAGE_PREFIX = 'package:'; |
| 11 const String DART_PACKAGE_PREFIX = 'dart:'; | 13 const String DART_PACKAGE_PREFIX = 'dart:'; |
| 12 | 14 |
| 13 String fileHeader(String library) => '''// GENERATED, DO NOT EDIT! | 15 String fileHeader(String library) => '''// GENERATED, DO NOT EDIT! |
| 14 library ${library}; | 16 library ${library}; |
| 15 | 17 |
| 16 import 'package:angular/angular.dart'; | 18 import 'package:angular/angular.dart'; |
| 17 | 19 |
| 18 primeTemplateCache(TemplateCache tc) { | 20 primeTemplateCache(TemplateCache tc) { |
| 19 '''; | 21 '''; |
| 20 | 22 |
| 21 const String FILE_FOOTER = '}'; | 23 const String FILE_FOOTER = '}'; |
| 22 const SYSTEM_PACKAGE_ROOT = '%SYSTEM_PACKAGE_ROOT%'; | 24 const SYSTEM_PACKAGE_ROOT = '%SYSTEM_PACKAGE_ROOT%'; |
| 23 | 25 |
| 24 main(args) { | 26 main(args) { |
| 25 if (args.length < 4) { | 27 if (args.length < 4) { |
| 26 print('Usage: templace_cache_generator path_to_entry_point output ' | 28 print('Usage: templace_cache_generator path_to_entry_point sdk_path ' |
| 27 'package_root1,package_root2,...|$SYSTEM_PACKAGE_ROOT ' | 29 'output package_root1,package_root2,...|$SYSTEM_PACKAGE_ROOT ' |
| 28 'patternUrl1,rewriteTo1;patternUrl2,rewriteTo2 ' | 30 'patternUrl1,rewriteTo1;patternUrl2,rewriteTo2 ' |
| 29 'blacklistClass1,blacklistClass2'); | 31 'blacklistClass1,blacklistClass2'); |
| 30 exit(1); | 32 exit(1); |
| 31 } | 33 } |
| 32 | 34 |
| 33 var entryPoint = args[0]; | 35 var entryPoint = args[0]; |
| 34 var output = args[1]; | 36 var sdkPath = args[1]; |
| 35 var outputLibrary = args[2]; | 37 var output = args[2]; |
| 36 var packageRoots = args[3] == SYSTEM_PACKAGE_ROOT ? | 38 var outputLibrary = args[3]; |
| 37 [Platform.packageRoot] : args[3].split(','); | 39 var packageRoots = args[4] == SYSTEM_PACKAGE_ROOT ? |
| 38 Map<RegExp, String> urlRewriters = parseUrlRemapping(args[4]); | 40 [Platform.packageRoot] : args[4].split(','); |
| 39 Set<String> blacklistedClasses = (args.length > 5) | 41 Map<RegExp, String> urlRewriters = parseUrlRemapping(args[5]); |
| 40 ? new Set.from(args[5].split(',')) | 42 Set<String> blacklistedClasses = (args.length > 6) |
| 43 ? new Set.from(args[6].split(',')) |
| 41 : new Set(); | 44 : new Set(); |
| 42 | 45 |
| 46 print('sdkPath: $sdkPath'); |
| 43 print('entryPoint: $entryPoint'); | 47 print('entryPoint: $entryPoint'); |
| 44 print('output: $output'); | 48 print('output: $output'); |
| 45 print('outputLibrary: $outputLibrary'); | 49 print('outputLibrary: $outputLibrary'); |
| 46 print('packageRoots: $packageRoots'); | 50 print('packageRoots: $packageRoots'); |
| 47 print('url rewritters: ' + args[4]); | 51 print('url rewritters: ' + args[5]); |
| 48 print('blacklistedClasses: ' + blacklistedClasses.join(', ')); | 52 print('blacklistedClasses: ' + blacklistedClasses.join(', ')); |
| 49 | 53 |
| 50 | 54 |
| 51 Map<String, String> templates = {}; | 55 Map<String, String> templates = {}; |
| 52 | 56 |
| 53 var c = new SourceCrawlerImpl(packageRoots); | 57 var c = new SourceCrawler(sdkPath, packageRoots); |
| 54 var visitor = | 58 var visitor = |
| 55 new TemplateCollectingVisitor(templates, blacklistedClasses, c); | 59 new TemplateCollectingVisitor(templates, blacklistedClasses, c); |
| 56 c.crawl(entryPoint, (CompilationUnit compilationUnit) => | 60 c.crawl(entryPoint, |
| 57 visitor(compilationUnit)); | 61 (CompilationUnitElement compilationUnit, SourceFile source) => |
| 62 visitor(compilationUnit, source.canonicalPath)); |
| 58 | 63 |
| 59 var sink = new File(output).openWrite(); | 64 var sink = new File(output).openWrite(); |
| 60 return printTemplateCache( | 65 return printTemplateCache( |
| 61 templates, urlRewriters, outputLibrary, sink).then((_) { | 66 templates, urlRewriters, outputLibrary, sink).then((_) { |
| 62 return sink.flush(); | 67 return sink.flush(); |
| 63 }); | 68 }); |
| 64 } | 69 } |
| 65 | 70 |
| 66 Map<RegExp, String> parseUrlRemapping(String argument) { | 71 Map<RegExp, String> parseUrlRemapping(String argument) { |
| 67 Map<RegExp, String> result = new LinkedHashMap(); | 72 Map<RegExp, String> result = new LinkedHashMap(); |
| 68 if (argument.isEmpty) { | 73 if (argument.isEmpty) { |
| 69 return result; | 74 return result; |
| 70 } | 75 } |
| 71 | 76 |
| 72 argument.split(";").forEach((String pair) { | 77 argument.split(";").forEach((String pair) { |
| 73 List<String> remapping = pair.split(","); | 78 List<String> remapping = pair.split(","); |
| 74 result[new RegExp(remapping[0])] = remapping[1]; | 79 result[new RegExp(remapping[0])] = remapping[1]; |
| 75 }); | 80 }); |
| 76 return result; | 81 return result; |
| 77 } | 82 } |
| 78 | 83 |
| 79 printTemplateCache(Map<String, String> templateKeyMap, | 84 printTemplateCache(Map<String, String> templateKeyMap, |
| 80 Map<RegExp, String> urlRewriters, | 85 Map<RegExp, String> urlRewriters, |
| 81 String outputLibrary, | 86 String outputLibrary, |
| 82 IOSink outSink) { | 87 IOSink outSink) { |
| 83 | 88 |
| 84 outSink.write(fileHeader(outputLibrary)); | 89 outSink.write(fileHeader(outputLibrary)); |
| 85 | 90 |
| 86 List<Future> reads = <Future>[]; | 91 Future future = new Future.value(0); |
| 87 templateKeyMap.forEach((uri, templateFile) { | 92 List uris = templateKeyMap.keys.toList()..sort()..forEach((uri) { |
| 88 reads.add(new File(templateFile).readAsString().then((fileStr) { | 93 var templateFile = templateKeyMap[uri]; |
| 89 fileStr = fileStr.replaceAll('"""', r'\"\"\"'); | 94 future = future.then((_) { |
| 90 String resultUri = uri; | 95 return new File(templateFile).readAsString().then((fileStr) { |
| 91 urlRewriters.forEach((regexp, replacement) { | 96 fileStr = fileStr.replaceAll('"""', r'\"\"\"'); |
| 92 resultUri = resultUri.replaceFirst(regexp, replacement); | 97 String resultUri = uri; |
| 98 urlRewriters.forEach((regexp, replacement) { |
| 99 resultUri = resultUri.replaceFirst(regexp, replacement); |
| 100 }); |
| 101 outSink.write( |
| 102 'tc.put("$resultUri", new HttpResponse(200, r"""$fileStr"""));\n'); |
| 93 }); | 103 }); |
| 94 outSink.write( | 104 }); |
| 95 'tc.put("$resultUri", new HttpResponse(200, r"""$fileStr"""));\n'); | |
| 96 })); | |
| 97 }); | 105 }); |
| 98 | 106 |
| 99 // Wait until all templates files are processed. | 107 // Wait until all templates files are processed. |
| 100 return Future.wait(reads).then((_) { | 108 return future.then((_) { |
| 101 outSink.write(FILE_FOOTER); | 109 outSink.write(FILE_FOOTER); |
| 102 }); | 110 }); |
| 103 } | 111 } |
| 104 | 112 |
| 105 class TemplateCollectingVisitor { | 113 class TemplateCollectingVisitor { |
| 106 Map<String, String> templates; | 114 Map<String, String> templates; |
| 107 Set<String> blacklistedClasses; | 115 Set<String> blacklistedClasses; |
| 108 SourceCrawlerImpl sourceCrawlerImpl; | 116 SourceCrawler sourceCrawler; |
| 109 | 117 |
| 110 TemplateCollectingVisitor(this.templates, this.blacklistedClasses, | 118 TemplateCollectingVisitor(this.templates, this.blacklistedClasses, |
| 111 this.sourceCrawlerImpl); | 119 this.sourceCrawler); |
| 112 | 120 |
| 113 call(CompilationUnit cu) { | 121 call(CompilationUnitElement cue, String srcPath) { |
| 122 CompilationUnit cu = sourceCrawler.context |
| 123 .resolveCompilationUnit(cue.source, cue.library); |
| 114 cu.declarations.forEach((CompilationUnitMember declaration) { | 124 cu.declarations.forEach((CompilationUnitMember declaration) { |
| 115 // We only care about classes. | 125 // We only care about classes. |
| 116 if (declaration is! ClassDeclaration) return; | 126 if (declaration is! ClassDeclaration) return; |
| 117 ClassDeclaration clazz = declaration; | 127 ClassDeclaration clazz = declaration; |
| 118 List<String> cacheUris = []; | 128 List<String> cacheUris = []; |
| 119 bool cache = true; | 129 bool cache = true; |
| 120 clazz.metadata.forEach((Annotation ann) { | 130 clazz.metadata.forEach((Annotation ann) { |
| 121 if (ann.arguments == null) return; // Ignore non-class annotations. | 131 if (ann.arguments == null) return; // Ignore non-class annotations. |
| 122 // TODO(tsander): Add library name as class name could conflict. | |
| 123 if (blacklistedClasses.contains(clazz.name.name)) return; | 132 if (blacklistedClasses.contains(clazz.name.name)) return; |
| 124 | 133 |
| 125 switch (ann.name.name) { | 134 switch (ann.name.name) { |
| 126 case 'NgComponent': | 135 case 'NgComponent': |
| 127 extractNgComponentMetadata(ann, cacheUris); break; | 136 extractNgComponentMetadata(ann, cacheUris); break; |
| 128 case 'NgTemplateCache': | 137 case 'NgTemplateCache': |
| 129 cache = extractNgTemplateCache(ann, cacheUris); break; | 138 cache = extractNgTemplateCache(ann, cacheUris); break; |
| 130 } | 139 } |
| 131 }); | 140 }); |
| 132 if (cache && cacheUris.isNotEmpty) { | 141 if (cache && cacheUris.isNotEmpty) { |
| 133 cacheUris.forEach((uri) => storeUriAsset(uri)); | 142 var srcDirUri = new Uri.file(srcPath); |
| 143 Source currentSrcDir = sourceCrawler.context.sourceFactory |
| 144 .resolveUri2(null, srcDirUri); |
| 145 cacheUris..sort()..forEach((uri) => storeUriAsset(uri, currentSrcDir)); |
| 134 } | 146 } |
| 135 }); | 147 }); |
| 136 } | 148 } |
| 137 | 149 |
| 138 void extractNgComponentMetadata(Annotation ann, List<String> cacheUris) { | 150 void extractNgComponentMetadata(Annotation ann, List<String> cacheUris) { |
| 139 ann.arguments.arguments.forEach((Expression arg) { | 151 ann.arguments.arguments.forEach((Expression arg) { |
| 140 if (arg is NamedExpression) { | 152 if (arg is NamedExpression) { |
| 141 NamedExpression namedArg = arg; | 153 NamedExpression namedArg = arg; |
| 142 var paramName = namedArg.name.label.name; | 154 var paramName = namedArg.name.label.name; |
| 143 if (paramName == 'templateUrl' || paramName == 'cssUrl') { | 155 if (paramName == 'templateUrl') { |
| 144 cacheUris.add(assertString(namedArg.expression).stringValue); | 156 cacheUris.add(assertString(namedArg.expression).stringValue); |
| 157 } else if (paramName == 'cssUrl') { |
| 158 if (namedArg.expression is StringLiteral) { |
| 159 cacheUris.add(assertString(namedArg.expression).stringValue); |
| 160 } else { |
| 161 cacheUris.addAll(assertList(namedArg.expression).elements.map((e) => |
| 162 assertString(e).stringValue)); |
| 163 } |
| 145 } | 164 } |
| 146 } | 165 } |
| 147 }); | 166 }); |
| 148 } | 167 } |
| 149 | 168 |
| 150 bool extractNgTemplateCache( | 169 bool extractNgTemplateCache( |
| 151 Annotation ann, List<String> cacheUris) { | 170 Annotation ann, List<String> cacheUris) { |
| 152 bool cache = true; | 171 bool cache = true; |
| 153 ann.arguments.arguments.forEach((Expression arg) { | 172 ann.arguments.arguments.forEach((Expression arg) { |
| 154 if (arg is NamedExpression) { | 173 if (arg is NamedExpression) { |
| 155 NamedExpression namedArg = arg; | 174 NamedExpression namedArg = arg; |
| 156 var paramName = namedArg.name.label.name; | 175 var paramName = namedArg.name.label.name; |
| 157 if (paramName == 'preCacheUrls') { | 176 if (paramName == 'preCacheUrls') { |
| 158 assertList(namedArg.expression).elements | 177 assertList(namedArg.expression).elements |
| 159 .forEach((expression) => | 178 ..forEach((expression) => |
| 160 cacheUris.add(assertString(expression).stringValue)); | 179 cacheUris.add(assertString(expression).stringValue)); |
| 161 } | 180 } |
| 162 if (paramName == 'cache') { | 181 if (paramName == 'cache') { |
| 163 cache = assertBoolean(namedArg.expression).value; | 182 cache = assertBoolean(namedArg.expression).value; |
| 164 } | 183 } |
| 165 } | 184 } |
| 166 }); | 185 }); |
| 167 return cache; | 186 return cache; |
| 168 } | 187 } |
| 169 | 188 |
| 170 void storeUriAsset(String uri) { | 189 void storeUriAsset(String uri, Source srcPath) { |
| 171 String assetFileLocation = | 190 String assetFileLocation = findAssetFileLocation(uri, srcPath); |
| 172 uri.startsWith('package:') ? | |
| 173 sourceCrawlerImpl.resolvePackagePath(uri) : uri; | |
| 174 if (assetFileLocation == null) { | 191 if (assetFileLocation == null) { |
| 175 print("Could not find asset for uri: $uri"); | 192 print("Could not find asset for uri: $uri"); |
| 176 } else { | 193 } else { |
| 177 templates[uri] = assetFileLocation; | 194 templates[uri] = assetFileLocation; |
| 178 } | 195 } |
| 179 } | 196 } |
| 180 | 197 |
| 198 String findAssetFileLocation(String uri, Source srcPath) { |
| 199 if (uri.startsWith('/')) { |
| 200 // Absolute Path from working directory. |
| 201 return '.${uri}'; |
| 202 } |
| 203 // Otherwise let the sourceFactory resolve for packages, and relative paths. |
| 204 Source source = sourceCrawler.context.sourceFactory |
| 205 .resolveUri(srcPath, uri); |
| 206 return (source != null) ? source.fullName : null; |
| 207 } |
| 208 |
| 181 BooleanLiteral assertBoolean(Expression key) { | 209 BooleanLiteral assertBoolean(Expression key) { |
| 182 if (key is! BooleanLiteral) { | 210 if (key is! BooleanLiteral) { |
| 183 throw 'must be a boolean literal: ${key.runtimeType}'; | 211 throw 'must be a boolean literal: ${key.runtimeType}'; |
| 184 } | 212 } |
| 185 return key; | 213 return key; |
| 186 } | 214 } |
| 187 | 215 |
| 188 ListLiteral assertList(Expression key) { | 216 ListLiteral assertList(Expression key) { |
| 189 if (key is! ListLiteral) { | 217 if (key is! ListLiteral) { |
| 190 throw 'must be a list literal: ${key.runtimeType}'; | 218 throw 'must be a list literal: ${key.runtimeType}'; |
| 191 } | 219 } |
| 192 return key; | 220 return key; |
| 193 } | 221 } |
| 194 | 222 |
| 195 StringLiteral assertString(Expression key) { | 223 StringLiteral assertString(Expression key) { |
| 196 if (key is! StringLiteral) { | 224 if (key is! StringLiteral) { |
| 197 throw 'must be a string literal: ${key.runtimeType}'; | 225 throw 'must be a string literal: ${key.runtimeType}'; |
| 198 } | 226 } |
| 199 return key; | 227 return key; |
| 200 } | 228 } |
| 201 } | 229 } |
| OLD | NEW |