fix(transformer): remove classDefParser in favor of hardcoded strings to speed up build
This commit is contained in:
157
modules/angular2/src/transform/common/annotation_matcher.dart
Normal file
157
modules/angular2/src/transform/common/annotation_matcher.dart
Normal file
@ -0,0 +1,157 @@
|
||||
library angular2.transform.common.annotation_matcher;
|
||||
|
||||
import 'package:analyzer/src/generated/ast.dart';
|
||||
import 'package:barback/barback.dart' show AssetId;
|
||||
import 'package:code_transformers/assets.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'logging.dart' show logger;
|
||||
|
||||
/// [AnnotationDescriptor]s for the default angular annotations that can appear
|
||||
/// on a class. These classes are re-exported in many places so this covers all
|
||||
/// the possible libraries which could provide them.
|
||||
const INJECTABLES = const [
|
||||
const AnnotationDescriptor(
|
||||
'Injectable', 'package:angular2/src/di/annotations.dart', null),
|
||||
const AnnotationDescriptor(
|
||||
'Injectable', 'package:angular2/src/di/annotations_impl.dart', null),
|
||||
const AnnotationDescriptor(
|
||||
'Injectable', 'package:angular2/src/di/decorators.dart', null),
|
||||
const AnnotationDescriptor('Injectable', 'package:angular2/di.dart', null),
|
||||
const AnnotationDescriptor(
|
||||
'Injectable', 'package:angular2/angular2.dart', null),
|
||||
];
|
||||
|
||||
const DIRECTIVES = const [
|
||||
const AnnotationDescriptor('Directive',
|
||||
'package:angular2/src/core/annotations/annotations.dart', 'Injectable'),
|
||||
const AnnotationDescriptor('Directive',
|
||||
'package:angular2/src/core/annotations_impl/annotations.dart',
|
||||
'Injectable'),
|
||||
const AnnotationDescriptor(
|
||||
'Directive', 'package:angular2/annotations.dart', 'Injectable'),
|
||||
const AnnotationDescriptor(
|
||||
'Directive', 'package:angular2/angular2.dart', 'Injectable'),
|
||||
const AnnotationDescriptor(
|
||||
'Directive', 'package:angular2/core.dart', 'Injectable'),
|
||||
];
|
||||
|
||||
const COMPONENTS = const [
|
||||
const AnnotationDescriptor('Component',
|
||||
'package:angular2/src/core/annotations/annotations.dart', 'Directive'),
|
||||
const AnnotationDescriptor('Component',
|
||||
'package:angular2/src/core/annotations_impl/annotations.dart',
|
||||
'Directive'),
|
||||
const AnnotationDescriptor(
|
||||
'Component', 'package:angular2/annotations.dart', 'Directive'),
|
||||
const AnnotationDescriptor(
|
||||
'Component', 'package:angular2/angular2.dart', 'Directive'),
|
||||
const AnnotationDescriptor(
|
||||
'Component', 'package:angular2/core.dart', 'Directive'),
|
||||
];
|
||||
|
||||
const VIEWS = const [
|
||||
const AnnotationDescriptor('View', 'package:angular2/view.dart', null),
|
||||
const AnnotationDescriptor('View', 'package:angular2/angular2.dart', null),
|
||||
const AnnotationDescriptor('View', 'package:angular2/core.dart', null),
|
||||
const AnnotationDescriptor(
|
||||
'View', 'package:angular2/src/core/annotations/view.dart', null),
|
||||
const AnnotationDescriptor(
|
||||
'View', 'package:angular2/src/core/annotations_impl/view.dart', null),
|
||||
];
|
||||
|
||||
/// Checks if a given [Annotation] matches any of the given
|
||||
/// [AnnotationDescriptors].
|
||||
class AnnotationMatcher {
|
||||
/// Always start out with the default angular [AnnotationDescriptor]s.
|
||||
final List<AnnotationDescriptor> _annotations = []
|
||||
..addAll(VIEWS)
|
||||
..addAll(COMPONENTS)
|
||||
..addAll(INJECTABLES)
|
||||
..addAll(DIRECTIVES);
|
||||
|
||||
AnnotationMatcher();
|
||||
|
||||
/// Adds a new [AnnotationDescriptor].
|
||||
void add(AnnotationDescriptor annotation) => _annotations.add(annotation);
|
||||
|
||||
/// Adds a number of [AnnotationDescriptor]s.
|
||||
void addAll(Iterable<AnnotationDescriptor> annotations) =>
|
||||
_annotations.addAll(annotations);
|
||||
|
||||
/// Returns the first [AnnotationDescriptor] that matches the given
|
||||
/// [Annotation] node which appears in `assetId`.
|
||||
AnnotationDescriptor firstMatch(Annotation annotation, AssetId assetId) =>
|
||||
_annotations.firstWhere((a) => _matchAnnotation(annotation, a, assetId),
|
||||
orElse: () => null);
|
||||
|
||||
/// Checks whether an [Annotation] node matches any [AnnotationDescriptor].
|
||||
bool hasMatch(Annotation annotation, AssetId assetId) =>
|
||||
_annotations.any((a) => _matchAnnotation(annotation, a, assetId));
|
||||
|
||||
/// Checks if an [Annotation] node implements [Injectable].
|
||||
bool isInjectable(Annotation annotation, AssetId assetId) =>
|
||||
_implements(firstMatch(annotation, assetId), INJECTABLES);
|
||||
|
||||
/// Checks if an [Annotation] node implements [Directive].
|
||||
bool isDirective(Annotation annotation, AssetId assetId) =>
|
||||
_implements(firstMatch(annotation, assetId), DIRECTIVES);
|
||||
|
||||
/// Checks if an [Annotation] node implements [Component].
|
||||
bool isComponent(Annotation annotation, AssetId assetId) =>
|
||||
_implements(firstMatch(annotation, assetId), COMPONENTS);
|
||||
|
||||
/// Checks if an [Annotation] node implements [View].
|
||||
bool isView(Annotation annotation, AssetId assetId) =>
|
||||
_implements(firstMatch(annotation, assetId), VIEWS);
|
||||
|
||||
/// Checks if `descriptor` extends or is any of the supplied `interfaces`.
|
||||
bool _implements(
|
||||
AnnotationDescriptor descriptor, List<AnnotationDescriptor> interfaces) {
|
||||
if (descriptor == null) return false;
|
||||
if (interfaces.contains(descriptor)) return true;
|
||||
if (descriptor.superClass == null) return false;
|
||||
var superClass = _annotations.firstWhere(
|
||||
(a) => a.name == descriptor.superClass, orElse: () => null);
|
||||
if (superClass == null) {
|
||||
logger.warning(
|
||||
'Missing `custom_annotation` entry for `${descriptor.superClass}`.');
|
||||
return false;
|
||||
}
|
||||
return _implements(superClass, interfaces);
|
||||
}
|
||||
|
||||
// Checks if an [Annotation] matches an [AnnotationDescriptor].
|
||||
static bool _matchAnnotation(
|
||||
Annotation annotation, AnnotationDescriptor descriptor, AssetId assetId) {
|
||||
if (annotation.name.name != descriptor.name) return false;
|
||||
return (annotation.root as CompilationUnit).directives
|
||||
.where((d) => d is ImportDirective)
|
||||
.any((ImportDirective i) {
|
||||
var uriString = i.uri.stringValue;
|
||||
if (uriString == descriptor.import) return true;
|
||||
if (uriString.startsWith('package:') || uriString.startsWith('dart:')) {
|
||||
return false;
|
||||
}
|
||||
return descriptor.assetId ==
|
||||
uriToAssetId(assetId, uriString, logger, null);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// String based description of an annotation class and its location.
|
||||
class AnnotationDescriptor {
|
||||
/// The name of the class.
|
||||
final String name;
|
||||
/// A `package:` style import path to the file where the class is defined.
|
||||
final String import;
|
||||
/// The class that this class extends or implements. This is the only optional
|
||||
/// field.
|
||||
final String superClass;
|
||||
|
||||
AssetId get assetId => new AssetId(package, packagePath);
|
||||
String get package => path.split(import.replaceFirst('package:', '')).first;
|
||||
String get packagePath => path.joinAll(['lib']
|
||||
..addAll(path.split(import.replaceFirst('package:', ''))..removeAt(0)));
|
||||
|
||||
const AnnotationDescriptor(this.name, this.import, this.superClass);
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
library angular2.transform.common.options;
|
||||
|
||||
import 'annotation_matcher.dart';
|
||||
import 'mirror_mode.dart';
|
||||
|
||||
const ENTRY_POINT_PARAM = 'entry_points';
|
||||
const REFLECTION_ENTRY_POINT_PARAM = 'reflection_entry_points';
|
||||
const CUSTOM_ANNOTATIONS_PARAM = 'custom_annotations';
|
||||
|
||||
/// Provides information necessary to transform an Angular2 app.
|
||||
class TransformerOptions {
|
||||
@ -23,16 +25,23 @@ class TransformerOptions {
|
||||
/// Whether to generate calls to our generated `initReflector` code
|
||||
final bool initReflector;
|
||||
|
||||
/// The [AnnotationMatcher] which is used to identify angular annotations.
|
||||
final AnnotationMatcher annotationMatcher;
|
||||
|
||||
TransformerOptions._internal(this.entryPoints, this.reflectionEntryPoints,
|
||||
this.modeName, this.mirrorMode, this.initReflector);
|
||||
this.modeName, this.mirrorMode, this.initReflector,
|
||||
this.annotationMatcher);
|
||||
|
||||
factory TransformerOptions(List<String> entryPoints,
|
||||
{List<String> reflectionEntryPoints, String modeName: 'release',
|
||||
MirrorMode mirrorMode: MirrorMode.none, bool initReflector: true}) {
|
||||
MirrorMode mirrorMode: MirrorMode.none, bool initReflector: true,
|
||||
List<AnnotationDescriptor> customAnnotationDescriptors: const []}) {
|
||||
if (reflectionEntryPoints == null || reflectionEntryPoints.isEmpty) {
|
||||
reflectionEntryPoints = entryPoints;
|
||||
}
|
||||
var annotationMatcher = new AnnotationMatcher()
|
||||
..addAll(customAnnotationDescriptors);
|
||||
return new TransformerOptions._internal(entryPoints, reflectionEntryPoints,
|
||||
modeName, mirrorMode, initReflector);
|
||||
modeName, mirrorMode, initReflector, annotationMatcher);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
library angular2.transform.common.options_reader;
|
||||
|
||||
import 'package:barback/barback.dart';
|
||||
import 'annotation_matcher.dart';
|
||||
import 'mirror_mode.dart';
|
||||
import 'options.dart';
|
||||
|
||||
@ -29,7 +30,8 @@ TransformerOptions parseBarbackSettings(BarbackSettings settings) {
|
||||
reflectionEntryPoints: reflectionEntryPoints,
|
||||
modeName: settings.mode.name,
|
||||
mirrorMode: mirrorMode,
|
||||
initReflector: initReflector);
|
||||
initReflector: initReflector,
|
||||
customAnnotationDescriptors: _readCustomAnnotations(config));
|
||||
}
|
||||
|
||||
/// Cribbed from the polymer project.
|
||||
@ -53,3 +55,48 @@ List<String> _readFileList(Map config, String paramName) {
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
/// Parse the [CUSTOM_ANNOTATIONS_PARAM] options out of the transformer into
|
||||
/// [AnnotationDescriptor]s.
|
||||
List<AnnotationDescriptor> _readCustomAnnotations(Map config) {
|
||||
var descriptors = [];
|
||||
var customAnnotations = config[CUSTOM_ANNOTATIONS_PARAM];
|
||||
if (customAnnotations == null) return descriptors;
|
||||
var error = false;
|
||||
if (customAnnotations is! List) {
|
||||
error = true;
|
||||
} else {
|
||||
for (var description in customAnnotations) {
|
||||
if (description is! Map) {
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
var name = description['name'];
|
||||
var import = description['import'];
|
||||
var superClass = description['superClass'];
|
||||
if (name == null || import == null || superClass == null) {
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
descriptors.add(new AnnotationDescriptor(name, import, superClass));
|
||||
}
|
||||
}
|
||||
if (error) {
|
||||
print(CUSTOM_ANNOTATIONS_ERROR);
|
||||
}
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
const CUSTOM_ANNOTATIONS_ERROR = '''
|
||||
Invalid value for $CUSTOM_ANNOTATIONS_PARAM in the Angular2 transformer.
|
||||
Expected something that looks like the following:
|
||||
|
||||
transformers:
|
||||
- angular2:
|
||||
custom_annotations:
|
||||
- name: MyAnnotation
|
||||
import: 'package:my_package/my_annotation.dart'
|
||||
superClass: Component
|
||||
- name: ...
|
||||
import: ...
|
||||
superClass: ...''';
|
||||
|
Reference in New Issue
Block a user