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