Skip to content
This repository has been archived by the owner on Feb 22, 2018. It is now read-only.

feat(template_cache_generator): Support custom template path resolution #923

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ packages:
route_hierarchical:
description: route_hierarchical
source: hosted
version: "0.4.15"
version: "0.4.18"
shadow_dom:
description: shadow_dom
source: hosted
Expand Down
182 changes: 126 additions & 56 deletions lib/tools/template_cache_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'dart:collection';
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/element.dart';
import 'package:args/args.dart';
import 'package:di/generator.dart';

const String PACKAGE_PREFIX = 'package:';
Expand All @@ -21,64 +22,129 @@ primeTemplateCache(TemplateCache tc) {
''';

const String FILE_FOOTER = '}';
const SYSTEM_PACKAGE_ROOT = '%SYSTEM_PACKAGE_ROOT%';

main(args) {
if (args.length < 4) {
print('Usage: templace_cache_generator path_to_entry_point sdk_path '
'output package_root1,package_root2,...|$SYSTEM_PACKAGE_ROOT '
'patternUrl1,rewriteTo1;patternUrl2,rewriteTo2 '
'blacklistClass1,blacklistClass2');
exit(1);
}

var entryPoint = args[0];
var sdkPath = args[1];
var output = args[2];
var outputLibrary = args[3];
var packageRoots = args[4] == SYSTEM_PACKAGE_ROOT ?
[Platform.packageRoot] : args[4].split(',');
Map<RegExp, String> urlRewriters = parseUrlRemapping(args[5]);
Set<String> blacklistedClasses = (args.length > 6)
? new Set.from(args[6].split(','))
: new Set();

print('sdkPath: $sdkPath');
print('entryPoint: $entryPoint');
print('output: $output');
print('outputLibrary: $outputLibrary');
print('packageRoots: $packageRoots');
print('url rewritters: ' + args[5]);
print('blacklistedClasses: ' + blacklistedClasses.join(', '));

main(List arguments) {
Options options = parseArgs(arguments);
if (options.verbose) {
print('entryPoint: ${options.entryPoint}');
print('outputLibrary: ${options.outputLibrary}');
print('output: ${options.output}');
print('sdk-path: ${options.sdkPath}');
print('package-root: ${options.packageRoots.join(",")}');
print('template-root: ${options.templateRoots.join(",")}');
var rewrites = options.urlRewrites.keys
.map((k) => '${k.pattern},${options.urlRewrites[k]}')
.join(';');
print('url-rewrites: $rewrites');
print('skip-classes: ${options.skippedClasses.join(",")}');
}

Map<String, String> templates = {};

var c = new SourceCrawler(sdkPath, packageRoots);
var visitor =
new TemplateCollectingVisitor(templates, blacklistedClasses, c);
c.crawl(entryPoint,
var c = new SourceCrawler(options.sdkPath, options.packageRoots);
var visitor = new TemplateCollectingVisitor(templates, options.skippedClasses,
c, options.templateRoots);
c.crawl(options.entryPoint,
(CompilationUnitElement compilationUnit, SourceFile source) =>
visitor(compilationUnit, source.canonicalPath));

var sink = new File(output).openWrite();
var sink;
if (options.output == '-') {
sink = stdout;
} else {
var f = new File(options.output)..createSync(recursive: true);
sink = f.openWrite();
}
return printTemplateCache(
templates, urlRewriters, outputLibrary, sink).then((_) {
return sink.flush();
});
templates, options.urlRewrites, options.outputLibrary, sink)
.then((_) => sink.flush());
}

class Options {
String entryPoint;
String outputLibrary;
String sdkPath;
List<String> packageRoots;
List<String> templateRoots;
String output;
Map<RegExp, String> urlRewrites;
Set<String> skippedClasses;
bool verbose;
}

Map<RegExp, String> parseUrlRemapping(String argument) {
Map<RegExp, String> result = new LinkedHashMap();
if (argument.isEmpty) {
return result;
Options parseArgs(List arguments) {
var parser = new ArgParser()
..addOption('sdk-path', abbr: 's',
defaultsTo: Platform.environment['DART_SDK'],
help: 'Dart SDK Path')
..addOption('package-root', abbr: 'p', defaultsTo: Platform.packageRoot,
help: 'comma-separated list of package roots')
..addOption('template-root', abbr: 't', defaultsTo: '.',
help: 'comma-separated list of paths from which templates with'
'absolute paths can be fetched')
..addOption('out', abbr: 'o', defaultsTo: '-',
help: 'output file or "-" for stdout')
..addOption('url-rewrites', abbr: 'u',
help: 'semicolon-separated list of URL rewrite rules, of the form: '
'patternUrl,rewriteTo')
..addOption('skip-classes', abbr: 'b',
help: 'comma-separated list of classes to skip templating')
..addFlag('verbose', abbr: 'v', help: 'verbose output')
..addFlag('help', abbr: 'h', negatable: false, help: 'show this help');

printUsage() {
print('Usage: dart template_cache_generator.dart '
'--sdk-path=path [OPTION...] entryPoint libraryName');
print(parser.getUsage());
}

argument.split(";").forEach((String pair) {
List<String> remapping = pair.split(",");
result[new RegExp(remapping[0])] = remapping[1];
});
return result;
fail(message) {
print('Error: $message\n');
printUsage();
exit(1);
}

var args;
try {
args = parser.parse(arguments);
} catch (e) {
fail('failed to parse arguments');
}

if (args['help']) {
printUsage();
exit(0);
}

if (args['sdk-path'] == null) {
fail('--sdk-path must be specified');
}

var options = new Options();
options.sdkPath = args['sdk-path'];
options.packageRoots = args['package-root'].split(',');
options.templateRoots = args['template-root'].split(',');
options.output = args['out'];
if (args['url-rewrites'] != null) {
options.urlRewrites = new LinkedHashMap.fromIterable(
args['url-rewrites'].split(';').map((p) => p.split(',')),
key: (p) => new RegExp(p[0]),
value: (p) => p[1]);
} else {
options.urlRewrites = {};
}
if (args['skip-classes'] != null) {
options.skippedClasses = new Set.from(args['skip-classes'].split(','));
} else {
options.skippedClasses = new Set();
}
options.verbose = args['verbose'];
if (args.rest.length != 2) {
fail('unexpected arguments: ${args.rest.join(' ')}');
}
options.entryPoint = args.rest[0];
options.outputLibrary = args.rest[1];
return options;
}

printTemplateCache(Map<String, String> templateKeyMap,
Expand Down Expand Up @@ -112,11 +178,12 @@ printTemplateCache(Map<String, String> templateKeyMap,

class TemplateCollectingVisitor {
Map<String, String> templates;
Set<String> blacklistedClasses;
Set<String> skippedClasses;
SourceCrawler sourceCrawler;
List<String> templateRoots;

TemplateCollectingVisitor(this.templates, this.blacklistedClasses,
this.sourceCrawler);
TemplateCollectingVisitor(this.templates, this.skippedClasses,
this.sourceCrawler, this.templateRoots);

void call(CompilationUnitElement cue, String srcPath) {
processDeclarations(cue, srcPath);
Expand All @@ -137,7 +204,7 @@ class TemplateCollectingVisitor {
bool cache = true;
clazz.metadata.forEach((Annotation ann) {
if (ann.arguments == null) return; // Ignore non-class annotations.
if (blacklistedClasses.contains(clazz.name.name)) return;
if (skippedClasses.contains(clazz.name.name)) return;

switch (ann.name.name) {
case 'NgComponent':
Expand All @@ -149,7 +216,8 @@ class TemplateCollectingVisitor {
if (cache && cacheUris.isNotEmpty) {
Source currentSrcDir = sourceCrawler.context.sourceFactory
.resolveUri(null, 'file://$srcPath');
cacheUris..sort()..forEach((uri) => storeUriAsset(uri, currentSrcDir));
cacheUris..sort()..forEach(
(uri) => storeUriAsset(uri, currentSrcDir, templateRoots));
}
});
}
Expand Down Expand Up @@ -192,19 +260,21 @@ class TemplateCollectingVisitor {
return cache;
}

void storeUriAsset(String uri, Source srcPath) {
String assetFileLocation = findAssetFileLocation(uri, srcPath);
void storeUriAsset(String uri, Source srcPath, templateRoots) {
String assetFileLocation = findAssetLocation(uri, srcPath, templateRoots);
if (assetFileLocation == null) {
print("Could not find asset for uri: $uri");
} else {
templates[uri] = assetFileLocation;
}
}

String findAssetFileLocation(String uri, Source srcPath) {
String findAssetLocation(String uri, Source srcPath, List<String>
templateRoots) {
if (uri.startsWith('/')) {
// Absolute Path from working directory.
return '.${uri}';
var paths = templateRoots.map((r) => '$r/$uri');
return paths.firstWhere((p) => new File(p).existsSync(),
orElse: () => paths.first);
}
// Otherwise let the sourceFactory resolve for packages, and relative paths.
Source source = sourceCrawler.context.sourceFactory
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ homepage: https://angulardart.org
environment:
sdk: '>=1.2.0'
dependencies:
args: '>=0.10.0 < 0.11.0'
analyzer: '>=0.13.0 <0.14.0'
browser: '>=0.10.0 <0.11.0'
code_transformers: '>=0.1.0 <0.2.0'
Expand Down
22 changes: 12 additions & 10 deletions test/io/template_cache_generator_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ void main() {
var tmpDir = Directory.systemTemp.createTempSync();
Future flush;
try {
flush = generator.main(['test/io/test_files/templates/main.dart',
Platform.environment['DART_SDK'],
'${tmpDir.path}/generated.dart', 'generated',
'%SYSTEM_PACKAGE_ROOT%',
'/test/io/test_files,rewritten', 'MyComponent3']);
flush = generator.main([
'--out=${tmpDir.path}/generated.dart',
'--url-rewrites=/test/io/test_files,rewritten',
'--skip-classes=MyComponent3',
'test/io/test_files/templates/main.dart',
'generated']);
} catch(_) {
tmpDir.deleteSync(recursive: true);
rethrow;
Expand All @@ -42,11 +43,12 @@ void main() {
var tmpDir = Directory.systemTemp.createTempSync();
Future flush;
try {
flush = generator.main(['test/io/test_files/cssUrls/main.dart',
Platform.environment['DART_SDK'],
'${tmpDir.path}/generated.dart', 'generated',
'%SYSTEM_PACKAGE_ROOT%',
'/test/io/test_files,rewritten', 'MyComponent3']);
flush = generator.main([
'--out=${tmpDir.path}/generated.dart',
'--url-rewrites=/test/io/test_files,rewritten',
'--skip-classes=MyComponent3',
'test/io/test_files/cssUrls/main.dart',
'generated']);
} catch(_) {
tmpDir.deleteSync(recursive: true);
rethrow;
Expand Down