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
|
path: ../angular2_material
|
||||||
transformers:
|
transformers:
|
||||||
- angular2:
|
- angular2:
|
||||||
ambient_directives: 'angular2/lib/src/core/directives.dart:CORE_DIRECTIVES'
|
ambient_directives: 'package:angular2/src/core/directives.dart#CORE_DIRECTIVES'
|
||||||
entry_points:
|
entry_points:
|
||||||
- web/src/gestures/index.dart
|
- web/src/gestures/index.dart
|
||||||
- web/src/hello_world/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';
|
const REFLECT_PROPERTIES_AS_ATTRIBUTES = 'reflect_properties_as_attributes';
|
||||||
// TODO(kegluenq): Remove this after 30 Nov (i/5108).
|
// TODO(kegluenq): Remove this after 30 Nov (i/5108).
|
||||||
const REFLECT_PROPERTIES_AS_ATTRIBUTES_OLD = 'reflectPropertiesAsAttributes';
|
const REFLECT_PROPERTIES_AS_ATTRIBUTES_OLD = 'reflectPropertiesAsAttributes';
|
||||||
|
const AMBIENT_DIRECTIVES = 'ambient_directives';
|
||||||
const INIT_REFLECTOR_PARAM = 'init_reflector';
|
const INIT_REFLECTOR_PARAM = 'init_reflector';
|
||||||
const INLINE_VIEWS_PARAM = 'inline_views';
|
const INLINE_VIEWS_PARAM = 'inline_views';
|
||||||
const MIRROR_MODE_PARAM = 'mirror_mode';
|
const MIRROR_MODE_PARAM = 'mirror_mode';
|
||||||
@ -39,6 +40,10 @@ class TransformerOptions {
|
|||||||
/// as attributes on DOM elements, which may aid in application debugging.
|
/// as attributes on DOM elements, which may aid in application debugging.
|
||||||
final bool reflectPropertiesAsAttributes;
|
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.
|
/// Whether to format generated code.
|
||||||
/// Code that is only modified will never be formatted because doing so may
|
/// Code that is only modified will never be formatted because doing so may
|
||||||
/// invalidate the source maps generated by `dart2js` and/or other tools.
|
/// invalidate the source maps generated by `dart2js` and/or other tools.
|
||||||
@ -59,6 +64,7 @@ class TransformerOptions {
|
|||||||
this.initReflector,
|
this.initReflector,
|
||||||
this.annotationMatcher,
|
this.annotationMatcher,
|
||||||
{this.reflectPropertiesAsAttributes,
|
{this.reflectPropertiesAsAttributes,
|
||||||
|
this.ambientDirectives,
|
||||||
this.inlineViews,
|
this.inlineViews,
|
||||||
this.formatCode});
|
this.formatCode});
|
||||||
|
|
||||||
@ -69,6 +75,7 @@ class TransformerOptions {
|
|||||||
List<ClassDescriptor> customAnnotationDescriptors: const [],
|
List<ClassDescriptor> customAnnotationDescriptors: const [],
|
||||||
bool inlineViews: false,
|
bool inlineViews: false,
|
||||||
bool reflectPropertiesAsAttributes: true,
|
bool reflectPropertiesAsAttributes: true,
|
||||||
|
List<String> ambientDirectives,
|
||||||
bool formatCode: false}) {
|
bool formatCode: false}) {
|
||||||
var annotationMatcher = new AnnotationMatcher()
|
var annotationMatcher = new AnnotationMatcher()
|
||||||
..addAll(customAnnotationDescriptors);
|
..addAll(customAnnotationDescriptors);
|
||||||
@ -78,6 +85,7 @@ class TransformerOptions {
|
|||||||
return new TransformerOptions._internal(entryPoints, entryPointGlobs,
|
return new TransformerOptions._internal(entryPoints, entryPointGlobs,
|
||||||
modeName, mirrorMode, initReflector, annotationMatcher,
|
modeName, mirrorMode, initReflector, annotationMatcher,
|
||||||
reflectPropertiesAsAttributes: reflectPropertiesAsAttributes,
|
reflectPropertiesAsAttributes: reflectPropertiesAsAttributes,
|
||||||
|
ambientDirectives: ambientDirectives,
|
||||||
inlineViews: inlineViews,
|
inlineViews: inlineViews,
|
||||||
formatCode: formatCode);
|
formatCode: formatCode);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import 'options.dart';
|
|||||||
TransformerOptions parseBarbackSettings(BarbackSettings settings) {
|
TransformerOptions parseBarbackSettings(BarbackSettings settings) {
|
||||||
var config = settings.configuration;
|
var config = settings.configuration;
|
||||||
_warnDeprecated(config);
|
_warnDeprecated(config);
|
||||||
var entryPoints = _readFileList(config, ENTRY_POINT_PARAM);
|
var entryPoints = _readStringList(config, ENTRY_POINT_PARAM);
|
||||||
var initReflector =
|
var initReflector =
|
||||||
_readBool(config, INIT_REFLECTOR_PARAM, defaultValue: true);
|
_readBool(config, INIT_REFLECTOR_PARAM, defaultValue: true);
|
||||||
var reflectPropertiesAsAttributes =
|
var reflectPropertiesAsAttributes =
|
||||||
@ -18,6 +18,7 @@ TransformerOptions parseBarbackSettings(BarbackSettings settings) {
|
|||||||
config, REFLECT_PROPERTIES_AS_ATTRIBUTES_OLD,
|
config, REFLECT_PROPERTIES_AS_ATTRIBUTES_OLD,
|
||||||
defaultValue: false);
|
defaultValue: false);
|
||||||
}
|
}
|
||||||
|
var ambientDirectives = _readStringList(config, AMBIENT_DIRECTIVES);
|
||||||
var formatCode = _readBool(config, FORMAT_CODE_PARAM, defaultValue: false);
|
var formatCode = _readBool(config, FORMAT_CODE_PARAM, defaultValue: false);
|
||||||
String mirrorModeVal =
|
String mirrorModeVal =
|
||||||
config.containsKey(MIRROR_MODE_PARAM) ? config[MIRROR_MODE_PARAM] : '';
|
config.containsKey(MIRROR_MODE_PARAM) ? config[MIRROR_MODE_PARAM] : '';
|
||||||
@ -39,6 +40,7 @@ TransformerOptions parseBarbackSettings(BarbackSettings settings) {
|
|||||||
initReflector: initReflector,
|
initReflector: initReflector,
|
||||||
customAnnotationDescriptors: _readCustomAnnotations(config),
|
customAnnotationDescriptors: _readCustomAnnotations(config),
|
||||||
reflectPropertiesAsAttributes: reflectPropertiesAsAttributes,
|
reflectPropertiesAsAttributes: reflectPropertiesAsAttributes,
|
||||||
|
ambientDirectives: ambientDirectives,
|
||||||
inlineViews: _readBool(config, INLINE_VIEWS_PARAM, defaultValue: false),
|
inlineViews: _readBool(config, INLINE_VIEWS_PARAM, defaultValue: false),
|
||||||
formatCode: formatCode);
|
formatCode: formatCode);
|
||||||
}
|
}
|
||||||
@ -51,16 +53,16 @@ bool _readBool(Map config, String paramName, {bool defaultValue}) {
|
|||||||
|
|
||||||
/// Cribbed from the polymer project.
|
/// Cribbed from the polymer project.
|
||||||
/// {@link https://github.com/dart-lang/polymer-dart}
|
/// {@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];
|
var value = config[paramName];
|
||||||
if (value == null) return null;
|
if (value == null) return null;
|
||||||
var files = [];
|
var result = [];
|
||||||
bool error = false;
|
bool error = false;
|
||||||
if (value is List) {
|
if (value is List) {
|
||||||
files = value;
|
result = value;
|
||||||
error = value.any((e) => e is! String);
|
error = value.any((e) => e is! String);
|
||||||
} else if (value is String) {
|
} else if (value is String) {
|
||||||
files = [value];
|
result = [value];
|
||||||
error = false;
|
error = false;
|
||||||
} else {
|
} else {
|
||||||
error = true;
|
error = true;
|
||||||
@ -68,7 +70,7 @@ List<String> _readFileList(Map config, String paramName) {
|
|||||||
if (error) {
|
if (error) {
|
||||||
print('Invalid value for "$paramName" in the Angular 2 transformer.');
|
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
|
/// 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
|
/// The returned value wraps the [NgDepsModel] at `assetId` as well as these
|
||||||
/// created objects.
|
/// created objects.
|
||||||
Future<CompileDataResults> createCompileData(
|
Future<CompileDataResults> createCompileData(
|
||||||
AssetReader reader, AssetId assetId) async {
|
AssetReader reader, AssetId assetId, List<String> ambientDirectives) async {
|
||||||
return logElapsedAsync(() 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;
|
return creator != null ? creator.createCompileData() : null;
|
||||||
}, operationName: 'createCompileData', assetId: assetId);
|
}, operationName: 'createCompileData', assetId: assetId);
|
||||||
}
|
}
|
||||||
@ -41,17 +41,18 @@ class _CompileDataCreator {
|
|||||||
final AssetReader reader;
|
final AssetReader reader;
|
||||||
final AssetId entryPoint;
|
final AssetId entryPoint;
|
||||||
final NgMeta ngMeta;
|
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(
|
static Future<_CompileDataCreator> create(
|
||||||
AssetReader reader, AssetId assetId) async {
|
AssetReader reader, AssetId assetId, List<String> ambientDirectives) async {
|
||||||
if (!(await reader.hasInput(assetId))) return null;
|
if (!(await reader.hasInput(assetId))) return null;
|
||||||
final json = await reader.readAsString(assetId);
|
final json = await reader.readAsString(assetId);
|
||||||
if (json == null || json.isEmpty) return null;
|
if (json == null || json.isEmpty) return null;
|
||||||
|
|
||||||
final ngMeta = new NgMeta.fromJson(JSON.decode(json));
|
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;
|
NgDepsModel get ngDeps => ngMeta.ngDeps;
|
||||||
@ -64,6 +65,7 @@ class _CompileDataCreator {
|
|||||||
final compileData =
|
final compileData =
|
||||||
<ReflectionInfoModel, NormalizedComponentWithViewDirectives>{};
|
<ReflectionInfoModel, NormalizedComponentWithViewDirectives>{};
|
||||||
final ngMetaMap = await _extractNgMeta();
|
final ngMetaMap = await _extractNgMeta();
|
||||||
|
final ambientDirectives = await _readAmbientDirectives();
|
||||||
|
|
||||||
for (var reflectable in ngDeps.reflectables) {
|
for (var reflectable in ngDeps.reflectables) {
|
||||||
if (ngMeta.types.containsKey(reflectable.name)) {
|
if (ngMeta.types.containsKey(reflectable.name)) {
|
||||||
@ -71,6 +73,8 @@ class _CompileDataCreator {
|
|||||||
if (compileDirectiveMetadata.template != null) {
|
if (compileDirectiveMetadata.template != null) {
|
||||||
final compileDatum = new NormalizedComponentWithViewDirectives(
|
final compileDatum = new NormalizedComponentWithViewDirectives(
|
||||||
compileDirectiveMetadata, <CompileDirectiveMetadata>[]);
|
compileDirectiveMetadata, <CompileDirectiveMetadata>[]);
|
||||||
|
compileDatum.directives.addAll(ambientDirectives);
|
||||||
|
|
||||||
for (var dep in reflectable.directives) {
|
for (var dep in reflectable.directives) {
|
||||||
if (!ngMetaMap.containsKey(dep.prefix)) {
|
if (!ngMetaMap.containsKey(dep.prefix)) {
|
||||||
logger.warning(
|
logger.warning(
|
||||||
@ -99,6 +103,50 @@ class _CompileDataCreator {
|
|||||||
return new CompileDataResults._(ngMeta, compileData);
|
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`
|
/// Creates a map from import prefix to the asset: uris of all `.dart`
|
||||||
/// libraries visible from `entryPoint`, excluding `dart:` and `.ng_deps.dart`
|
/// libraries visible from `entryPoint`, excluding `dart:` and `.ng_deps.dart`
|
||||||
/// files it imports. Unprefixed imports have the empty string as their key.
|
/// 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.
|
/// This method assumes a {@link DomAdapter} has been registered.
|
||||||
Future<Outputs> processTemplates(AssetReader reader, AssetId assetId,
|
Future<Outputs> processTemplates(AssetReader reader, AssetId assetId,
|
||||||
{bool reflectPropertiesAsAttributes: false}) async {
|
{bool reflectPropertiesAsAttributes: false, List<String> ambientDirectives}) async {
|
||||||
var viewDefResults = await createCompileData(reader, assetId);
|
var viewDefResults = await createCompileData(reader, assetId, ambientDirectives);
|
||||||
if (viewDefResults == null) return null;
|
if (viewDefResults == null) return null;
|
||||||
final directiveMetadatas = viewDefResults.ngMeta.types.values;
|
final directiveMetadatas = viewDefResults.ngMeta.types.values;
|
||||||
if (directiveMetadatas.isNotEmpty) {
|
if (directiveMetadatas.isNotEmpty) {
|
||||||
|
@ -37,7 +37,8 @@ class TemplateCompiler extends Transformer {
|
|||||||
var primaryId = transform.primaryInput.id;
|
var primaryId = transform.primaryInput.id;
|
||||||
var reader = new AssetReader.fromTransform(transform);
|
var reader = new AssetReader.fromTransform(transform);
|
||||||
var outputs = await processTemplates(reader, primaryId,
|
var outputs = await processTemplates(reader, primaryId,
|
||||||
reflectPropertiesAsAttributes: options.reflectPropertiesAsAttributes);
|
reflectPropertiesAsAttributes: options.reflectPropertiesAsAttributes,
|
||||||
|
ambientDirectives: options.ambientDirectives);
|
||||||
var ngDepsCode = _emptyNgDepsContents;
|
var ngDepsCode = _emptyNgDepsContents;
|
||||||
var templatesCode = '';
|
var templatesCode = '';
|
||||||
if (outputs != null) {
|
if (outputs != null) {
|
||||||
|
@ -74,9 +74,10 @@ void allTests() {
|
|||||||
updateReader();
|
updateReader();
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<String> process(AssetId assetId) {
|
Future<String> process(AssetId assetId, {List<String> ambientDirectives}) {
|
||||||
logger = new RecordingLogger();
|
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
|
// TODO(tbosch): This is just a temporary test that makes sure that the dart
|
||||||
@ -346,6 +347,65 @@ void allTests() {
|
|||||||
|
|
||||||
expect(didThrow).toBeFalse();
|
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) {
|
void _formatThenExpectEquals(String actual, String expected) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user