| 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:analyzer/src/generated/source.dart'; |
| 9 import 'package:analyzer/src/generated/element.dart'; | 9 import 'package:analyzer/src/generated/element.dart'; |
| 10 import 'package:args/args.dart'; | |
| 11 import 'package:di/generator.dart'; | 10 import 'package:di/generator.dart'; |
| 12 | 11 |
| 13 const String PACKAGE_PREFIX = 'package:'; | 12 const String PACKAGE_PREFIX = 'package:'; |
| 14 const String DART_PACKAGE_PREFIX = 'dart:'; | 13 const String DART_PACKAGE_PREFIX = 'dart:'; |
| 15 | 14 |
| 16 String fileHeader(String library) => '''// GENERATED, DO NOT EDIT! | 15 String fileHeader(String library) => '''// GENERATED, DO NOT EDIT! |
| 17 library ${library}; | 16 library ${library}; |
| 18 | 17 |
| 19 import 'package:angular/angular.dart'; | 18 import 'package:angular/angular.dart'; |
| 20 | 19 |
| 21 primeTemplateCache(TemplateCache tc) { | 20 primeTemplateCache(TemplateCache tc) { |
| 22 '''; | 21 '''; |
| 23 | 22 |
| 24 const String FILE_FOOTER = '}'; | 23 const String FILE_FOOTER = '}'; |
| 24 const SYSTEM_PACKAGE_ROOT = '%SYSTEM_PACKAGE_ROOT%'; |
| 25 | 25 |
| 26 main(List arguments) { | 26 main(args) { |
| 27 Options options = parseArgs(arguments); | 27 if (args.length < 4) { |
| 28 if (options.verbose) { | 28 print('Usage: templace_cache_generator path_to_entry_point sdk_path ' |
| 29 print('entryPoint: ${options.entryPoint}'); | 29 'output package_root1,package_root2,...|$SYSTEM_PACKAGE_ROOT ' |
| 30 print('outputLibrary: ${options.outputLibrary}'); | 30 'patternUrl1,rewriteTo1;patternUrl2,rewriteTo2 ' |
| 31 print('output: ${options.output}'); | 31 'blacklistClass1,blacklistClass2'); |
| 32 print('sdk-path: ${options.sdkPath}'); | 32 exit(1); |
| 33 print('package-root: ${options.packageRoots.join(",")}'); | |
| 34 print('template-root: ${options.templateRoots.join(",")}'); | |
| 35 var rewrites = options.urlRewrites.keys | |
| 36 .map((k) => '${k.pattern},${options.urlRewrites[k]}') | |
| 37 .join(';'); | |
| 38 print('url-rewrites: $rewrites'); | |
| 39 print('skip-classes: ${options.skippedClasses.join(",")}'); | |
| 40 } | 33 } |
| 41 | 34 |
| 35 var entryPoint = args[0]; |
| 36 var sdkPath = args[1]; |
| 37 var output = args[2]; |
| 38 var outputLibrary = args[3]; |
| 39 var packageRoots = args[4] == SYSTEM_PACKAGE_ROOT ? |
| 40 [Platform.packageRoot] : args[4].split(','); |
| 41 Map<RegExp, String> urlRewriters = parseUrlRemapping(args[5]); |
| 42 Set<String> blacklistedClasses = (args.length > 6) |
| 43 ? new Set.from(args[6].split(',')) |
| 44 : new Set(); |
| 45 |
| 46 print('sdkPath: $sdkPath'); |
| 47 print('entryPoint: $entryPoint'); |
| 48 print('output: $output'); |
| 49 print('outputLibrary: $outputLibrary'); |
| 50 print('packageRoots: $packageRoots'); |
| 51 print('url rewritters: ' + args[5]); |
| 52 print('blacklistedClasses: ' + blacklistedClasses.join(', ')); |
| 53 |
| 54 |
| 42 Map<String, String> templates = {}; | 55 Map<String, String> templates = {}; |
| 43 | 56 |
| 44 var c = new SourceCrawler(options.sdkPath, options.packageRoots); | 57 var c = new SourceCrawler(sdkPath, packageRoots); |
| 45 var visitor = new TemplateCollectingVisitor(templates, options.skippedClasses, | 58 var visitor = |
| 46 c, options.templateRoots); | 59 new TemplateCollectingVisitor(templates, blacklistedClasses, c); |
| 47 c.crawl(options.entryPoint, | 60 c.crawl(entryPoint, |
| 48 (CompilationUnitElement compilationUnit, SourceFile source) => | 61 (CompilationUnitElement compilationUnit, SourceFile source) => |
| 49 visitor(compilationUnit, source.canonicalPath)); | 62 visitor(compilationUnit, source.canonicalPath)); |
| 50 | 63 |
| 51 var sink; | 64 var sink = new File(output).openWrite(); |
| 52 if (options.output == '-') { | |
| 53 sink = stdout; | |
| 54 } else { | |
| 55 var f = new File(options.output)..createSync(recursive: true); | |
| 56 sink = f.openWrite(); | |
| 57 } | |
| 58 return printTemplateCache( | 65 return printTemplateCache( |
| 59 templates, options.urlRewrites, options.outputLibrary, sink) | 66 templates, urlRewriters, outputLibrary, sink).then((_) { |
| 60 .then((_) => sink.flush()); | 67 return sink.flush(); |
| 68 }); |
| 61 } | 69 } |
| 62 | 70 |
| 63 class Options { | 71 Map<RegExp, String> parseUrlRemapping(String argument) { |
| 64 String entryPoint; | 72 Map<RegExp, String> result = new LinkedHashMap(); |
| 65 String outputLibrary; | 73 if (argument.isEmpty) { |
| 66 String sdkPath; | 74 return result; |
| 67 List<String> packageRoots; | |
| 68 List<String> templateRoots; | |
| 69 String output; | |
| 70 Map<RegExp, String> urlRewrites; | |
| 71 Set<String> skippedClasses; | |
| 72 bool verbose; | |
| 73 } | |
| 74 | |
| 75 Options parseArgs(List arguments) { | |
| 76 var parser = new ArgParser() | |
| 77 ..addOption('sdk-path', abbr: 's', | |
| 78 defaultsTo: Platform.environment['DART_SDK'], | |
| 79 help: 'Dart SDK Path') | |
| 80 ..addOption('package-root', abbr: 'p', defaultsTo: Platform.packageRoot, | |
| 81 help: 'comma-separated list of package roots') | |
| 82 ..addOption('template-root', abbr: 't', defaultsTo: '.', | |
| 83 help: 'comma-separated list of paths from which templates with' | |
| 84 'absolute paths can be fetched') | |
| 85 ..addOption('out', abbr: 'o', defaultsTo: '-', | |
| 86 help: 'output file or "-" for stdout') | |
| 87 ..addOption('url-rewrites', abbr: 'u', | |
| 88 help: 'semicolon-separated list of URL rewrite rules, of the form: ' | |
| 89 'patternUrl,rewriteTo') | |
| 90 ..addOption('skip-classes', abbr: 'b', | |
| 91 help: 'comma-separated list of classes to skip templating') | |
| 92 ..addFlag('verbose', abbr: 'v', help: 'verbose output') | |
| 93 ..addFlag('help', abbr: 'h', negatable: false, help: 'show this help'); | |
| 94 | |
| 95 printUsage() { | |
| 96 print('Usage: dart template_cache_generator.dart ' | |
| 97 '--sdk-path=path [OPTION...] entryPoint libraryName'); | |
| 98 print(parser.getUsage()); | |
| 99 } | 75 } |
| 100 | 76 |
| 101 fail(message) { | 77 argument.split(";").forEach((String pair) { |
| 102 print('Error: $message\n'); | 78 List<String> remapping = pair.split(","); |
| 103 printUsage(); | 79 result[new RegExp(remapping[0])] = remapping[1]; |
| 104 exit(1); | 80 }); |
| 105 } | 81 return result; |
| 106 | |
| 107 var args; | |
| 108 try { | |
| 109 args = parser.parse(arguments); | |
| 110 } catch (e) { | |
| 111 fail('failed to parse arguments'); | |
| 112 } | |
| 113 | |
| 114 if (args['help']) { | |
| 115 printUsage(); | |
| 116 exit(0); | |
| 117 } | |
| 118 | |
| 119 if (args['sdk-path'] == null) { | |
| 120 fail('--sdk-path must be specified'); | |
| 121 } | |
| 122 | |
| 123 var options = new Options(); | |
| 124 options.sdkPath = args['sdk-path']; | |
| 125 options.packageRoots = args['package-root'].split(','); | |
| 126 options.templateRoots = args['template-root'].split(','); | |
| 127 options.output = args['out']; | |
| 128 if (args['url-rewrites'] != null) { | |
| 129 options.urlRewrites = new LinkedHashMap.fromIterable( | |
| 130 args['url-rewrites'].split(';').map((p) => p.split(',')), | |
| 131 key: (p) => new RegExp(p[0]), | |
| 132 value: (p) => p[1]); | |
| 133 } else { | |
| 134 options.urlRewrites = {}; | |
| 135 } | |
| 136 if (args['skip-classes'] != null) { | |
| 137 options.skippedClasses = new Set.from(args['skip-classes'].split(',')); | |
| 138 } else { | |
| 139 options.skippedClasses = new Set(); | |
| 140 } | |
| 141 options.verbose = args['verbose']; | |
| 142 if (args.rest.length != 2) { | |
| 143 fail('unexpected arguments: ${args.rest.join(' ')}'); | |
| 144 } | |
| 145 options.entryPoint = args.rest[0]; | |
| 146 options.outputLibrary = args.rest[1]; | |
| 147 return options; | |
| 148 } | 82 } |
| 149 | 83 |
| 150 printTemplateCache(Map<String, String> templateKeyMap, | 84 printTemplateCache(Map<String, String> templateKeyMap, |
| 151 Map<RegExp, String> urlRewriters, | 85 Map<RegExp, String> urlRewriters, |
| 152 String outputLibrary, | 86 String outputLibrary, |
| 153 IOSink outSink) { | 87 IOSink outSink) { |
| 154 | 88 |
| 155 outSink.write(fileHeader(outputLibrary)); | 89 outSink.write(fileHeader(outputLibrary)); |
| 156 | 90 |
| 157 Future future = new Future.value(0); | 91 Future future = new Future.value(0); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 171 }); | 105 }); |
| 172 | 106 |
| 173 // Wait until all templates files are processed. | 107 // Wait until all templates files are processed. |
| 174 return future.then((_) { | 108 return future.then((_) { |
| 175 outSink.write(FILE_FOOTER); | 109 outSink.write(FILE_FOOTER); |
| 176 }); | 110 }); |
| 177 } | 111 } |
| 178 | 112 |
| 179 class TemplateCollectingVisitor { | 113 class TemplateCollectingVisitor { |
| 180 Map<String, String> templates; | 114 Map<String, String> templates; |
| 181 Set<String> skippedClasses; | 115 Set<String> blacklistedClasses; |
| 182 SourceCrawler sourceCrawler; | 116 SourceCrawler sourceCrawler; |
| 183 List<String> templateRoots; | |
| 184 | 117 |
| 185 TemplateCollectingVisitor(this.templates, this.skippedClasses, | 118 TemplateCollectingVisitor(this.templates, this.blacklistedClasses, |
| 186 this.sourceCrawler, this.templateRoots); | 119 this.sourceCrawler); |
| 187 | 120 |
| 188 void call(CompilationUnitElement cue, String srcPath) { | 121 call(CompilationUnitElement cue, String srcPath) { |
| 189 processDeclarations(cue, srcPath); | |
| 190 | |
| 191 cue.enclosingElement.parts.forEach((CompilationUnitElement part) { | |
| 192 processDeclarations(part, srcPath); | |
| 193 }); | |
| 194 } | |
| 195 | |
| 196 void processDeclarations(CompilationUnitElement cue, String srcPath) { | |
| 197 CompilationUnit cu = sourceCrawler.context | 122 CompilationUnit cu = sourceCrawler.context |
| 198 .resolveCompilationUnit(cue.source, cue.library); | 123 .resolveCompilationUnit(cue.source, cue.library); |
| 199 cu.declarations.forEach((CompilationUnitMember declaration) { | 124 cu.declarations.forEach((CompilationUnitMember declaration) { |
| 200 // We only care about classes. | 125 // We only care about classes. |
| 201 if (declaration is! ClassDeclaration) return; | 126 if (declaration is! ClassDeclaration) return; |
| 202 ClassDeclaration clazz = declaration; | 127 ClassDeclaration clazz = declaration; |
| 203 List<String> cacheUris = []; | 128 List<String> cacheUris = []; |
| 204 bool cache = true; | 129 bool cache = true; |
| 205 clazz.metadata.forEach((Annotation ann) { | 130 clazz.metadata.forEach((Annotation ann) { |
| 206 if (ann.arguments == null) return; // Ignore non-class annotations. | 131 if (ann.arguments == null) return; // Ignore non-class annotations. |
| 207 if (skippedClasses.contains(clazz.name.name)) return; | 132 if (blacklistedClasses.contains(clazz.name.name)) return; |
| 208 | 133 |
| 209 switch (ann.name.name) { | 134 switch (ann.name.name) { |
| 210 case 'Component': | 135 case 'NgComponent': |
| 211 extractComponentMetadata(ann, cacheUris); break; | 136 extractNgComponentMetadata(ann, cacheUris); break; |
| 212 case 'NgTemplateCache': | 137 case 'NgTemplateCache': |
| 213 cache = extractNgTemplateCache(ann, cacheUris); break; | 138 cache = extractNgTemplateCache(ann, cacheUris); break; |
| 214 } | 139 } |
| 215 }); | 140 }); |
| 216 if (cache && cacheUris.isNotEmpty) { | 141 if (cache && cacheUris.isNotEmpty) { |
| 142 var srcDirUri = new Uri.file(srcPath); |
| 217 Source currentSrcDir = sourceCrawler.context.sourceFactory | 143 Source currentSrcDir = sourceCrawler.context.sourceFactory |
| 218 .resolveUri(null, 'file://$srcPath'); | 144 .resolveUri2(null, srcDirUri); |
| 219 cacheUris..sort()..forEach( | 145 cacheUris..sort()..forEach((uri) => storeUriAsset(uri, currentSrcDir)); |
| 220 (uri) => storeUriAsset(uri, currentSrcDir, templateRoots)); | |
| 221 } | 146 } |
| 222 }); | 147 }); |
| 223 } | 148 } |
| 224 | 149 |
| 225 void extractComponentMetadata(Annotation ann, List<String> cacheUris) { | 150 void extractNgComponentMetadata(Annotation ann, List<String> cacheUris) { |
| 226 ann.arguments.arguments.forEach((Expression arg) { | 151 ann.arguments.arguments.forEach((Expression arg) { |
| 227 if (arg is NamedExpression) { | 152 if (arg is NamedExpression) { |
| 228 NamedExpression namedArg = arg; | 153 NamedExpression namedArg = arg; |
| 229 var paramName = namedArg.name.label.name; | 154 var paramName = namedArg.name.label.name; |
| 230 if (paramName == 'templateUrl') { | 155 if (paramName == 'templateUrl') { |
| 231 cacheUris.add(assertString(namedArg.expression).stringValue); | 156 cacheUris.add(assertString(namedArg.expression).stringValue); |
| 232 } else if (paramName == 'cssUrl') { | 157 } else if (paramName == 'cssUrl') { |
| 233 if (namedArg.expression is StringLiteral) { | 158 if (namedArg.expression is StringLiteral) { |
| 234 cacheUris.add(assertString(namedArg.expression).stringValue); | 159 cacheUris.add(assertString(namedArg.expression).stringValue); |
| 235 } else { | 160 } else { |
| 236 cacheUris.addAll(assertList(namedArg.expression).elements.map((e) => | 161 cacheUris.addAll(assertList(namedArg.expression).elements.map((e) => |
| 237 assertString(e).stringValue)); | 162 assertString(e).stringValue)); |
| 238 } | 163 } |
| 239 } | 164 } |
| 240 } | 165 } |
| 241 }); | 166 }); |
| 242 } | 167 } |
| 243 | 168 |
| 244 bool extractNgTemplateCache(Annotation ann, List<String> cacheUris) { | 169 bool extractNgTemplateCache( |
| 170 Annotation ann, List<String> cacheUris) { |
| 245 bool cache = true; | 171 bool cache = true; |
| 246 ann.arguments.arguments.forEach((Expression arg) { | 172 ann.arguments.arguments.forEach((Expression arg) { |
| 247 if (arg is NamedExpression) { | 173 if (arg is NamedExpression) { |
| 248 NamedExpression namedArg = arg; | 174 NamedExpression namedArg = arg; |
| 249 var paramName = namedArg.name.label.name; | 175 var paramName = namedArg.name.label.name; |
| 250 if (paramName == 'preCacheUrls') { | 176 if (paramName == 'preCacheUrls') { |
| 251 assertList(namedArg.expression).elements | 177 assertList(namedArg.expression).elements |
| 252 ..forEach((expression) => | 178 ..forEach((expression) => |
| 253 cacheUris.add(assertString(expression).stringValue)); | 179 cacheUris.add(assertString(expression).stringValue)); |
| 254 } | 180 } |
| 255 if (paramName == 'cache') { | 181 if (paramName == 'cache') { |
| 256 cache = assertBoolean(namedArg.expression).value; | 182 cache = assertBoolean(namedArg.expression).value; |
| 257 } | 183 } |
| 258 } | 184 } |
| 259 }); | 185 }); |
| 260 return cache; | 186 return cache; |
| 261 } | 187 } |
| 262 | 188 |
| 263 void storeUriAsset(String uri, Source srcPath, templateRoots) { | 189 void storeUriAsset(String uri, Source srcPath) { |
| 264 String assetFileLocation = findAssetLocation(uri, srcPath, templateRoots); | 190 String assetFileLocation = findAssetFileLocation(uri, srcPath); |
| 265 if (assetFileLocation == null) { | 191 if (assetFileLocation == null) { |
| 266 print("Could not find asset for uri: $uri"); | 192 print("Could not find asset for uri: $uri"); |
| 267 } else { | 193 } else { |
| 268 templates[uri] = assetFileLocation; | 194 templates[uri] = assetFileLocation; |
| 269 } | 195 } |
| 270 } | 196 } |
| 271 | 197 |
| 272 String findAssetLocation(String uri, Source srcPath, List<String> | 198 String findAssetFileLocation(String uri, Source srcPath) { |
| 273 templateRoots) { | |
| 274 if (uri.startsWith('/')) { | 199 if (uri.startsWith('/')) { |
| 275 var paths = templateRoots.map((r) => '$r/$uri'); | 200 // Absolute Path from working directory. |
| 276 return paths.firstWhere((p) => new File(p).existsSync(), | 201 return '.${uri}'; |
| 277 orElse: () => paths.first); | |
| 278 } | 202 } |
| 279 // Otherwise let the sourceFactory resolve for packages, and relative paths. | 203 // Otherwise let the sourceFactory resolve for packages, and relative paths. |
| 280 Source source = sourceCrawler.context.sourceFactory | 204 Source source = sourceCrawler.context.sourceFactory |
| 281 .resolveUri(srcPath, uri); | 205 .resolveUri(srcPath, uri); |
| 282 return (source != null) ? source.fullName : null; | 206 return (source != null) ? source.fullName : null; |
| 283 } | 207 } |
| 284 | 208 |
| 285 BooleanLiteral assertBoolean(Expression key) { | 209 BooleanLiteral assertBoolean(Expression key) { |
| 286 if (key is! BooleanLiteral) { | 210 if (key is! BooleanLiteral) { |
| 287 throw 'must be a boolean literal: ${key.runtimeType}'; | 211 throw 'must be a boolean literal: ${key.runtimeType}'; |
| 288 } | 212 } |
| 289 return key; | 213 return key; |
| 290 } | 214 } |
| 291 | 215 |
| 292 ListLiteral assertList(Expression key) { | 216 ListLiteral assertList(Expression key) { |
| 293 if (key is! ListLiteral) { | 217 if (key is! ListLiteral) { |
| 294 throw 'must be a list literal: ${key.runtimeType}'; | 218 throw 'must be a list literal: ${key.runtimeType}'; |
| 295 } | 219 } |
| 296 return key; | 220 return key; |
| 297 } | 221 } |
| 298 | 222 |
| 299 StringLiteral assertString(Expression key) { | 223 StringLiteral assertString(Expression key) { |
| 300 if (key is! StringLiteral) { | 224 if (key is! StringLiteral) { |
| 301 throw 'must be a string literal: ${key.runtimeType}'; | 225 throw 'must be a string literal: ${key.runtimeType}'; |
| 302 } | 226 } |
| 303 return key; | 227 return key; |
| 304 } | 228 } |
| 305 } | 229 } |
| OLD | NEW |