feat(core): add support for ambient directives to dart transformers
Closes #5129
This commit is contained in:
parent
adc0e32cff
commit
4909feddde
|
@ -17,7 +17,7 @@ dependency_overrides:
|
|||
path: ../angular2_material
|
||||
transformers:
|
||||
- angular2:
|
||||
ambient_directives: 'angular2/lib/src/core/directives.dart:CORE_DIRECTIVES'
|
||||
ambient_directives: 'package:angular2/src/core/directives.dart#CORE_DIRECTIVES'
|
||||
entry_points:
|
||||
- web/src/gestures/index.dart
|
||||
- web/src/hello_world/index.dart
|
||||
|
|
|
@ -11,6 +11,7 @@ const FORMAT_CODE_PARAM = 'format_code';
|
|||
const REFLECT_PROPERTIES_AS_ATTRIBUTES = 'reflect_properties_as_attributes';
|
||||
// TODO(kegluenq): Remove this after 30 Nov (i/5108).
|
||||
const REFLECT_PROPERTIES_AS_ATTRIBUTES_OLD = 'reflectPropertiesAsAttributes';
|
||||
const AMBIENT_DIRECTIVES = 'ambient_directives';
|
||||
const INIT_REFLECTOR_PARAM = 'init_reflector';
|
||||
const INLINE_VIEWS_PARAM = 'inline_views';
|
||||
const MIRROR_MODE_PARAM = 'mirror_mode';
|
||||
|
@ -39,6 +40,10 @@ class TransformerOptions {
|
|||
/// as attributes on DOM elements, which may aid in application debugging.
|
||||
final bool reflectPropertiesAsAttributes;
|
||||
|
||||
/// A set of directives that will be automatically passed-in to the template compiler
|
||||
/// Format of an item in the list: angular2/lib/src/core/directives.dart#CORE_DIRECTIVES
|
||||
final List<String> ambientDirectives;
|
||||
|
||||
/// Whether to format generated code.
|
||||
/// Code that is only modified will never be formatted because doing so may
|
||||
/// invalidate the source maps generated by `dart2js` and/or other tools.
|
||||
|
@ -59,6 +64,7 @@ class TransformerOptions {
|
|||
this.initReflector,
|
||||
this.annotationMatcher,
|
||||
{this.reflectPropertiesAsAttributes,
|
||||
this.ambientDirectives,
|
||||
this.inlineViews,
|
||||
this.formatCode});
|
||||
|
||||
|
@ -69,6 +75,7 @@ class TransformerOptions {
|
|||
List<ClassDescriptor> customAnnotationDescriptors: const [],
|
||||
bool inlineViews: false,
|
||||
bool reflectPropertiesAsAttributes: true,
|
||||
List<String> ambientDirectives,
|
||||
bool formatCode: false}) {
|
||||
var annotationMatcher = new AnnotationMatcher()
|
||||
..addAll(customAnnotationDescriptors);
|
||||
|
@ -78,6 +85,7 @@ class TransformerOptions {
|
|||
return new TransformerOptions._internal(entryPoints, entryPointGlobs,
|
||||
modeName, mirrorMode, initReflector, annotationMatcher,
|
||||
reflectPropertiesAsAttributes: reflectPropertiesAsAttributes,
|
||||
ambientDirectives: ambientDirectives,
|
||||
inlineViews: inlineViews,
|
||||
formatCode: formatCode);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import 'options.dart';
|
|||
TransformerOptions parseBarbackSettings(BarbackSettings settings) {
|
||||
var config = settings.configuration;
|
||||
_warnDeprecated(config);
|
||||
var entryPoints = _readFileList(config, ENTRY_POINT_PARAM);
|
||||
var entryPoints = _readStringList(config, ENTRY_POINT_PARAM);
|
||||
var initReflector =
|
||||
_readBool(config, INIT_REFLECTOR_PARAM, defaultValue: true);
|
||||
var reflectPropertiesAsAttributes =
|
||||
|
@ -18,6 +18,7 @@ TransformerOptions parseBarbackSettings(BarbackSettings settings) {
|
|||
config, REFLECT_PROPERTIES_AS_ATTRIBUTES_OLD,
|
||||
defaultValue: false);
|
||||
}
|
||||
var ambientDirectives = _readStringList(config, AMBIENT_DIRECTIVES);
|
||||
var formatCode = _readBool(config, FORMAT_CODE_PARAM, defaultValue: false);
|
||||
String mirrorModeVal =
|
||||
config.containsKey(MIRROR_MODE_PARAM) ? config[MIRROR_MODE_PARAM] : '';
|
||||
|
@ -39,6 +40,7 @@ TransformerOptions parseBarbackSettings(BarbackSettings settings) {
|
|||
initReflector: initReflector,
|
||||
customAnnotationDescriptors: _readCustomAnnotations(config),
|
||||
reflectPropertiesAsAttributes: reflectPropertiesAsAttributes,
|
||||
ambientDirectives: ambientDirectives,
|
||||
inlineViews: _readBool(config, INLINE_VIEWS_PARAM, defaultValue: false),
|
||||
formatCode: formatCode);
|
||||
}
|
||||
|
@ -51,16 +53,16 @@ bool _readBool(Map config, String paramName, {bool defaultValue}) {
|
|||
|
||||
/// Cribbed from the polymer project.
|
||||
/// {@link https://github.com/dart-lang/polymer-dart}
|
||||
List<String> _readFileList(Map config, String paramName) {
|
||||
List<String> _readStringList(Map config, String paramName) {
|
||||
var value = config[paramName];
|
||||
if (value == null) return null;
|
||||
var files = [];
|
||||
var result = [];
|
||||
bool error = false;
|
||||
if (value is List) {
|
||||
files = value;
|
||||
result = value;
|
||||
error = value.any((e) => e is! String);
|
||||
} else if (value is String) {
|
||||
files = [value];
|
||||
result = [value];
|
||||
error = false;
|
||||
} else {
|
||||
error = true;
|
||||
|
@ -68,7 +70,7 @@ List<String> _readFileList(Map config, String paramName) {
|
|||
if (error) {
|
||||
print('Invalid value for "$paramName" in the Angular 2 transformer.');
|
||||
}
|
||||
return files;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Parse the [CUSTOM_ANNOTATIONS_PARAM] options out of the transformer into
|
||||
|
|
|
@ -20,9 +20,9 @@ import 'package:barback/barback.dart';
|
|||
/// The returned value wraps the [NgDepsModel] at `assetId` as well as these
|
||||
/// created objects.
|
||||
Future<CompileDataResults> createCompileData(
|
||||
AssetReader reader, AssetId assetId) async {
|
||||
AssetReader reader, AssetId assetId, List<String> ambientDirectives) async {
|
||||
return logElapsedAsync(() async {
|
||||
final creator = await _CompileDataCreator.create(reader, assetId);
|
||||
final creator = await _CompileDataCreator.create(reader, assetId, ambientDirectives);
|
||||
return creator != null ? creator.createCompileData() : null;
|
||||
}, operationName: 'createCompileData', assetId: assetId);
|
||||
}
|
||||
|
@ -41,17 +41,18 @@ class _CompileDataCreator {
|
|||
final AssetReader reader;
|
||||
final AssetId entryPoint;
|
||||
final NgMeta ngMeta;
|
||||
final List<String> ambientDirectives;
|
||||
|
||||
_CompileDataCreator(this.reader, this.entryPoint, this.ngMeta);
|
||||
_CompileDataCreator(this.reader, this.entryPoint, this.ngMeta, this.ambientDirectives);
|
||||
|
||||
static Future<_CompileDataCreator> create(
|
||||
AssetReader reader, AssetId assetId) async {
|
||||
AssetReader reader, AssetId assetId, List<String> ambientDirectives) async {
|
||||
if (!(await reader.hasInput(assetId))) return null;
|
||||
final json = await reader.readAsString(assetId);
|
||||
if (json == null || json.isEmpty) return null;
|
||||
|
||||
final ngMeta = new NgMeta.fromJson(JSON.decode(json));
|
||||
return new _CompileDataCreator(reader, assetId, ngMeta);
|
||||
return new _CompileDataCreator(reader, assetId, ngMeta, ambientDirectives);
|
||||
}
|
||||
|
||||
NgDepsModel get ngDeps => ngMeta.ngDeps;
|
||||
|
@ -64,6 +65,7 @@ class _CompileDataCreator {
|
|||
final compileData =
|
||||
<ReflectionInfoModel, NormalizedComponentWithViewDirectives>{};
|
||||
final ngMetaMap = await _extractNgMeta();
|
||||
final ambientDirectives = await _readAmbientDirectives();
|
||||
|
||||
for (var reflectable in ngDeps.reflectables) {
|
||||
if (ngMeta.types.containsKey(reflectable.name)) {
|
||||
|
@ -71,6 +73,8 @@ class _CompileDataCreator {
|
|||
if (compileDirectiveMetadata.template != null) {
|
||||
final compileDatum = new NormalizedComponentWithViewDirectives(
|
||||
compileDirectiveMetadata, <CompileDirectiveMetadata>[]);
|
||||
compileDatum.directives.addAll(ambientDirectives);
|
||||
|
||||
for (var dep in reflectable.directives) {
|
||||
if (!ngMetaMap.containsKey(dep.prefix)) {
|
||||
logger.warning(
|
||||
|
@ -99,6 +103,50 @@ class _CompileDataCreator {
|
|||
return new CompileDataResults._(ngMeta, compileData);
|
||||
}
|
||||
|
||||
Future<List<CompileDirectiveMetadata>> _readAmbientDirectives() async {
|
||||
if (ambientDirectives == null) return const [];
|
||||
|
||||
final res = [];
|
||||
for (var ad in ambientDirectives) {
|
||||
final parts = ad.split("#");
|
||||
if (parts.length != 2) {
|
||||
logger.warning('The ambient directives configuration option '
|
||||
'must be in the following format: "URI#TOKEN"');
|
||||
return const [];
|
||||
}
|
||||
res.addAll(await _readAmbientDirectivesFromUri(parts[0], parts[1]));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<List<CompileDirectiveMetadata>> _readAmbientDirectivesFromUri(String uri, String token) async {
|
||||
final metaAssetId = fromUri(toMetaExtension(uri));
|
||||
if (await reader.hasInput(metaAssetId)) {
|
||||
try {
|
||||
var jsonString = await reader.readAsString(metaAssetId);
|
||||
if (jsonString != null && jsonString.isNotEmpty) {
|
||||
var newMetadata = new NgMeta.fromJson(JSON.decode(jsonString));
|
||||
|
||||
if (newMetadata.types.containsKey(token)) {
|
||||
return [newMetadata.types[token]];
|
||||
|
||||
} else if (newMetadata.aliases.containsKey(token)) {
|
||||
return newMetadata.flatten(token);
|
||||
|
||||
} else {
|
||||
logger.warning('Could not resolve ambient directive ${token} in ${uri}',
|
||||
asset: metaAssetId);
|
||||
}
|
||||
|
||||
}
|
||||
} catch (ex, stackTrace) {
|
||||
logger.warning('Failed to decode: $ex, $stackTrace',
|
||||
asset: metaAssetId);
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/// Creates a map from import prefix to the asset: uris of all `.dart`
|
||||
/// libraries visible from `entryPoint`, excluding `dart:` and `.ng_deps.dart`
|
||||
/// files it imports. Unprefixed imports have the empty string as their key.
|
||||
|
|
|
@ -26,8 +26,8 @@ import 'compile_data_creator.dart';
|
|||
///
|
||||
/// This method assumes a {@link DomAdapter} has been registered.
|
||||
Future<Outputs> processTemplates(AssetReader reader, AssetId assetId,
|
||||
{bool reflectPropertiesAsAttributes: false}) async {
|
||||
var viewDefResults = await createCompileData(reader, assetId);
|
||||
{bool reflectPropertiesAsAttributes: false, List<String> ambientDirectives}) async {
|
||||
var viewDefResults = await createCompileData(reader, assetId, ambientDirectives);
|
||||
if (viewDefResults == null) return null;
|
||||
final directiveMetadatas = viewDefResults.ngMeta.types.values;
|
||||
if (directiveMetadatas.isNotEmpty) {
|
||||
|
|
|
@ -37,7 +37,8 @@ class TemplateCompiler extends Transformer {
|
|||
var primaryId = transform.primaryInput.id;
|
||||
var reader = new AssetReader.fromTransform(transform);
|
||||
var outputs = await processTemplates(reader, primaryId,
|
||||
reflectPropertiesAsAttributes: options.reflectPropertiesAsAttributes);
|
||||
reflectPropertiesAsAttributes: options.reflectPropertiesAsAttributes,
|
||||
ambientDirectives: options.ambientDirectives);
|
||||
var ngDepsCode = _emptyNgDepsContents;
|
||||
var templatesCode = '';
|
||||
if (outputs != null) {
|
||||
|
|
|
@ -74,9 +74,10 @@ void allTests() {
|
|||
updateReader();
|
||||
});
|
||||
|
||||
Future<String> process(AssetId assetId) {
|
||||
Future<String> process(AssetId assetId, {List<String> ambientDirectives}) {
|
||||
logger = new RecordingLogger();
|
||||
return log.setZoned(logger, () => processTemplates(reader, assetId));
|
||||
return log.setZoned(logger, () => processTemplates(reader, assetId,
|
||||
ambientDirectives: ambientDirectives));
|
||||
}
|
||||
|
||||
// TODO(tbosch): This is just a temporary test that makes sure that the dart
|
||||
|
@ -346,6 +347,65 @@ void allTests() {
|
|||
|
||||
expect(didThrow).toBeFalse();
|
||||
});
|
||||
|
||||
it('should include ambient directives.', () async {
|
||||
fooComponentMeta.template = new CompileTemplateMetadata(template: '<bar/>');
|
||||
final viewAnnotation = new AnnotationModel()
|
||||
..name = 'View'
|
||||
..isView = true;
|
||||
|
||||
barNgMeta.aliases['AMBIENT'] = [barComponentMeta.type.name];
|
||||
updateReader();
|
||||
|
||||
final outputs = await process(fooAssetId, ambientDirectives: ['package:a/bar.dart#AMBIENT']);
|
||||
final ngDeps = outputs.ngDeps;
|
||||
expect(ngDeps).toBeNotNull();
|
||||
expect(outputs.templatesCode)
|
||||
..toBeNotNull()
|
||||
..toContain(barComponentMeta.template.template);
|
||||
});
|
||||
|
||||
it('should include ambient directives when it it a list.', () async {
|
||||
fooComponentMeta.template = new CompileTemplateMetadata(template: '<bar/>');
|
||||
final viewAnnotation = new AnnotationModel()
|
||||
..name = 'View'
|
||||
..isView = true;
|
||||
|
||||
barNgMeta.types['AMBIENT'] = barComponentMeta;
|
||||
updateReader();
|
||||
|
||||
final outputs = await process(fooAssetId, ambientDirectives: ['package:a/bar.dart#AMBIENT']);
|
||||
final ngDeps = outputs.ngDeps;
|
||||
expect(ngDeps).toBeNotNull();
|
||||
expect(outputs.templatesCode)
|
||||
..toBeNotNull()
|
||||
..toContain(barComponentMeta.template.template);
|
||||
});
|
||||
|
||||
it('should work when ambient directives config is null.', () async {
|
||||
final outputs = await process(fooAssetId, ambientDirectives: null);
|
||||
final ngDeps = outputs.ngDeps;
|
||||
expect(ngDeps).toBeNotNull();
|
||||
});
|
||||
|
||||
it('should work when the ambient directives config is not formatted properly.', () async {
|
||||
final outputs = await process(fooAssetId, ambientDirectives: ['INVALID']);
|
||||
final ngDeps = outputs.ngDeps;
|
||||
expect(ngDeps).toBeNotNull();
|
||||
});
|
||||
|
||||
it('should work when the file with ambient directives cannot be found.', () async {
|
||||
final outputs = await process(
|
||||
fooAssetId, ambientDirectives: ['package:a/invalid.dart#AMBIENT']);
|
||||
final ngDeps = outputs.ngDeps;
|
||||
expect(ngDeps).toBeNotNull();
|
||||
});
|
||||
|
||||
it('should work when the ambient directives token cannot be found.', () async {
|
||||
final outputs = await process(fooAssetId, ambientDirectives: ['package:a/bar.dart#AMBIENT']);
|
||||
final ngDeps = outputs.ngDeps;
|
||||
expect(ngDeps).toBeNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
void _formatThenExpectEquals(String actual, String expected) {
|
||||
|
|
Loading…
Reference in New Issue