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