feat(dart/transform): Generate ChangeDetector classes
Use the `ProtoViewDto` created by the render `Compiler` to create a `ChangeDetectorDefinition`. From there, generate a subclass of `AbstractChangeDetector` for each `ChangeDetectorDefinition`. Run some basic unit tests for the dynamic and JIT change detectors on pre-generated change detectors.
This commit is contained in:
parent
383f0a1f30
commit
8a3b0b366f
36
gulpfile.js
36
gulpfile.js
|
@ -178,6 +178,12 @@ gulp.task('!build/tree.dart', function() {
|
|||
// Run a top-level `pub get` for this project.
|
||||
gulp.task('pubget.dart', pubget.dir(gulp, gulpPlugins, { dir: '.', command: DART_SDK.PUB }));
|
||||
|
||||
// Run `pub get` only on the angular2 dir of CONFIG.dest.dart
|
||||
gulp.task('!build/pubget.angular2.dart', pubget.dir(gulp, gulpPlugins, {
|
||||
dir: path.join(CONFIG.dest.dart, 'angular2'),
|
||||
command: DART_SDK.PUB
|
||||
}));
|
||||
|
||||
// Run `pub get` over CONFIG.dest.dart
|
||||
gulp.task('build/pubspec.dart', pubget.subDir(gulp, gulpPlugins, {
|
||||
dir: CONFIG.dest.dart,
|
||||
|
@ -588,6 +594,8 @@ gulp.task('build/packages.dart', function(done) {
|
|||
runSequence(
|
||||
'build/tree.dart',
|
||||
// Run after 'build/tree.dart' because broccoli clears the dist/dart folder
|
||||
'!build/pubget.angular2.dart',
|
||||
'!build/change_detect.dart',
|
||||
'build/pure-packages.dart',
|
||||
'build/format.dart',
|
||||
done);
|
||||
|
@ -811,6 +819,34 @@ gulp.task('clean', ['build/clean.tools', 'build/clean.js', 'build/clean.dart', '
|
|||
|
||||
gulp.task('build', ['build.js', 'build.dart']);
|
||||
|
||||
// ------------
|
||||
// change detection codegen
|
||||
|
||||
|
||||
gulp.task('build.change_detect.dart', function(done) {
|
||||
return runSequence('build/packages.dart', '!build/pubget.angular2.dart',
|
||||
'!build/change_detect.dart', done);
|
||||
});
|
||||
|
||||
gulp.task('!build/change_detect.dart', function(done) {
|
||||
var fs = require('fs');
|
||||
var changeDetectDir = path.join(CONFIG.dest.dart, 'angular2/test/change_detection/');
|
||||
var srcDir = path.join(changeDetectDir, 'generator');
|
||||
var destDir = path.join(changeDetectDir, 'generated');
|
||||
|
||||
var dartStream = fs.createWriteStream(path.join(destDir, 'simple_watch_classes.dart'));
|
||||
var genMain = path.join(srcDir, 'gen_change_detectors.dart');
|
||||
var proc = spawn(DART_SDK.VM, [genMain], { stdio:['ignore', 'pipe', 'inherit'] });
|
||||
proc.on('error', function(code) {
|
||||
done(new Error('Failed while generating change detector classes. Please run manually: ' +
|
||||
DART_SDK.VM + ' ' + dartArgs.join(' ')));
|
||||
});
|
||||
proc.on('close', function() {
|
||||
dartStream.close();
|
||||
done();
|
||||
});
|
||||
proc.stdout.pipe(dartStream);
|
||||
});
|
||||
|
||||
// ------------
|
||||
// angular material testing rules
|
||||
|
|
|
@ -27,6 +27,10 @@ import {
|
|||
* The code generator takes a list of proto records and creates a function/class
|
||||
* that "emulates" what the developer would write by hand to implement the same
|
||||
* kind of behaviour.
|
||||
*
|
||||
* This code should be kept in sync with the Dart transformer's
|
||||
* `angular2.transform.template_compiler.change_detector_codegen` library. If you make updates
|
||||
* here, please make equivalent changes there.
|
||||
*/
|
||||
var ABSTRACT_CHANGE_DETECTOR = "AbstractChangeDetector";
|
||||
var UTIL = "ChangeDetectionUtil";
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
library angular2.src.change_detection.pregen_proto_change_detector;
|
||||
|
||||
import 'package:angular2/src/change_detection/coalesce.dart';
|
||||
import 'package:angular2/src/change_detection/directive_record.dart';
|
||||
import 'package:angular2/src/change_detection/interfaces.dart';
|
||||
import 'package:angular2/src/change_detection/pipes/pipe_registry.dart';
|
||||
import 'package:angular2/src/change_detection/proto_change_detector.dart';
|
||||
import 'package:angular2/src/change_detection/proto_record.dart';
|
||||
|
||||
export 'dart:core' show List;
|
||||
export 'package:angular2/src/change_detection/abstract_change_detector.dart'
|
||||
show AbstractChangeDetector;
|
||||
export 'package:angular2/src/change_detection/directive_record.dart'
|
||||
show DirectiveIndex, DirectiveRecord;
|
||||
export 'package:angular2/src/change_detection/interfaces.dart'
|
||||
show ChangeDetector, ChangeDetectorDefinition, ProtoChangeDetector;
|
||||
export 'package:angular2/src/change_detection/pipes/pipe_registry.dart'
|
||||
show PipeRegistry;
|
||||
export 'package:angular2/src/change_detection/proto_record.dart'
|
||||
show ProtoRecord;
|
||||
export 'package:angular2/src/change_detection/change_detection_util.dart'
|
||||
show ChangeDetectionUtil;
|
||||
export 'package:angular2/src/facade/lang.dart' show looseIdentical;
|
||||
|
||||
typedef ChangeDetector InstantiateMethod(dynamic dispatcher,
|
||||
PipeRegistry registry, List<ProtoRecord> protoRecords,
|
||||
List<DirectiveRecord> directiveRecords);
|
||||
|
||||
/// Implementation of [ProtoChangeDetector] for use by pre-generated change
|
||||
/// detectors in Angular 2 Dart.
|
||||
/// Classes generated by the `TemplateCompiler` use this. The `export`s above
|
||||
/// allow the generated code to `import` a single library and get all
|
||||
/// dependencies.
|
||||
class PregenProtoChangeDetector extends ProtoChangeDetector {
|
||||
/// The [ChangeDetectorDefinition#id]. Strictly informational.
|
||||
final String id;
|
||||
|
||||
/// Closure used to generate an actual [ChangeDetector].
|
||||
final InstantiateMethod _instantiateMethod;
|
||||
|
||||
// [ChangeDetector] dependencies.
|
||||
final PipeRegistry _pipeRegistry;
|
||||
final List<ProtoRecord> _protoRecords;
|
||||
final List<DirectiveRecord> _directiveRecords;
|
||||
|
||||
/// Internal ctor.
|
||||
PregenProtoChangeDetector._(this.id, this._instantiateMethod,
|
||||
this._pipeRegistry, this._protoRecords, this._directiveRecords);
|
||||
|
||||
factory PregenProtoChangeDetector(InstantiateMethod instantiateMethod,
|
||||
PipeRegistry registry, ChangeDetectorDefinition def) {
|
||||
// TODO(kegluneq): Pre-generate these (#2067).
|
||||
var recordBuilder = new ProtoRecordBuilder();
|
||||
def.bindingRecords.forEach((b) {
|
||||
recordBuilder.add(b, def.variableNames);
|
||||
});
|
||||
var protoRecords = coalesce(recordBuilder.records);
|
||||
return new PregenProtoChangeDetector._(def.id, instantiateMethod, registry,
|
||||
protoRecords, def.directiveRecords);
|
||||
}
|
||||
|
||||
@override
|
||||
instantiate(dynamic dispatcher) => _instantiateMethod(
|
||||
dispatcher, _pipeRegistry, _protoRecords, _directiveRecords);
|
||||
}
|
|
@ -0,0 +1,440 @@
|
|||
library angular2.transform.template_compiler.change_detector_codegen;
|
||||
|
||||
import 'dart:convert' show JSON;
|
||||
import 'package:angular2/src/change_detection/change_detection_util.dart';
|
||||
import 'package:angular2/src/change_detection/coalesce.dart';
|
||||
import 'package:angular2/src/change_detection/directive_record.dart';
|
||||
import 'package:angular2/src/change_detection/interfaces.dart';
|
||||
import 'package:angular2/src/change_detection/proto_change_detector.dart';
|
||||
import 'package:angular2/src/change_detection/proto_record.dart';
|
||||
|
||||
/// Responsible for generating change detector classes for Angular 2.
|
||||
///
|
||||
/// This code should be kept in sync with the `ChangeDetectorJITGenerator`
|
||||
/// class. If you make updates here, please make equivalent changes there.
|
||||
class Codegen {
|
||||
final StringBuffer _buf = new StringBuffer();
|
||||
|
||||
void generate(String name, ChangeDetectorDefinition def) {
|
||||
new _CodegenState(name, def)._writeToBuf(_buf);
|
||||
}
|
||||
|
||||
String get imports {
|
||||
return _buf.isEmpty
|
||||
? ''
|
||||
: '''import '$_PREGEN_PROTO_CHANGE_DETECTOR_IMPORT' as $_GEN_PREFIX;''';
|
||||
}
|
||||
|
||||
bool get isEmpty => _buf.isEmpty;
|
||||
|
||||
@override
|
||||
String toString() => '$_buf';
|
||||
}
|
||||
|
||||
/// The state needed to generate a change detector for a single `Component`.
|
||||
class _CodegenState {
|
||||
final String _typeName;
|
||||
final String _changeDetectionMode;
|
||||
final List<ProtoRecord> _records;
|
||||
final List<DirectiveRecord> _directiveRecords;
|
||||
final List<String> _localNames;
|
||||
final List<String> _changeNames;
|
||||
final List<String> _fieldNames;
|
||||
final List<String> _pipeNames;
|
||||
|
||||
_CodegenState._(this._typeName, String changeDetectionStrategy, this._records,
|
||||
this._directiveRecords, List<String> localNames)
|
||||
: this._localNames = localNames,
|
||||
_changeNames = _getChangeNames(localNames),
|
||||
_fieldNames = _getFieldNames(localNames),
|
||||
_pipeNames = _getPipeNames(localNames),
|
||||
_changeDetectionMode = ChangeDetectionUtil
|
||||
.changeDetectionMode(changeDetectionStrategy);
|
||||
|
||||
factory _CodegenState(String typeName, ChangeDetectorDefinition def) {
|
||||
var protoRecords = new ProtoRecordBuilder();
|
||||
def.bindingRecords
|
||||
.forEach((rec) => protoRecords.add(rec, def.variableNames));
|
||||
var records = coalesce(protoRecords.records);
|
||||
return new _CodegenState._(typeName, def.strategy, records,
|
||||
def.directiveRecords, _getLocalNames(records));
|
||||
}
|
||||
|
||||
/// Generates sanitized names for use as local variables.
|
||||
static List<String> _getLocalNames(List<ProtoRecord> records) {
|
||||
var localNames = new List<String>(records.length + 1);
|
||||
localNames[0] = 'context';
|
||||
for (var i = 0; i < records.length; ++i) {
|
||||
var sanitizedName = records[i].name.replaceAll(new RegExp(r'\W'), '');
|
||||
localNames[i + 1] = '$sanitizedName$i';
|
||||
}
|
||||
return localNames;
|
||||
}
|
||||
|
||||
/// Generates names for use as local change variables.
|
||||
static List<String> _getChangeNames(List<String> localNames) =>
|
||||
localNames.map((name) => 'change_$name').toList();
|
||||
|
||||
/// Generates names for use as private fields.
|
||||
static List<String> _getFieldNames(List<String> localNames) =>
|
||||
localNames.map((name) => '_$name').toList();
|
||||
|
||||
/// Generates names for use as private pipe variables.
|
||||
static List<String> _getPipeNames(List<String> localNames) =>
|
||||
localNames.map((name) => '_${name}_pipe').toList();
|
||||
|
||||
void _writeToBuf(StringBuffer buf) {
|
||||
buf.write('''
|
||||
class $_typeName extends $_BASE_CLASS {
|
||||
final dynamic $_DISPATCHER_ACCESSOR;
|
||||
final $_GEN_PREFIX.PipeRegistry $_PIPE_REGISTRY_ACCESSOR;
|
||||
final $_GEN_PREFIX.List<$_GEN_PREFIX.ProtoRecord> $_PROTOS_ACCESSOR;
|
||||
final $_GEN_PREFIX.List<$_GEN_PREFIX.DirectiveRecord>
|
||||
$_DIRECTIVES_ACCESSOR;
|
||||
dynamic $_LOCALS_ACCESSOR = null;
|
||||
${_allFields().map(
|
||||
(f) => 'dynamic $f = $_UTIL.uninitialized();').join('')}
|
||||
|
||||
$_typeName(
|
||||
this.$_DISPATCHER_ACCESSOR,
|
||||
this.$_PIPE_REGISTRY_ACCESSOR,
|
||||
this.$_PROTOS_ACCESSOR,
|
||||
this.$_DIRECTIVES_ACCESSOR) : super();
|
||||
|
||||
void detectChangesInRecords(throwOnChange) {
|
||||
${_genLocalDefinitions()}
|
||||
${_genChangeDefinitons()}
|
||||
var $_IS_CHANGED_LOCAL = false;
|
||||
var $_CURRENT_PROTO;
|
||||
var $_CHANGES_LOCAL = null;
|
||||
|
||||
context = $_CONTEXT_ACCESSOR;
|
||||
${_records.map(_genRecord).join('')}
|
||||
}
|
||||
|
||||
void callOnAllChangesDone() {
|
||||
${_getCallOnAllChangesDoneBody()}
|
||||
}
|
||||
|
||||
void hydrate(context, locals, directives) {
|
||||
$_MODE_ACCESSOR = '$_changeDetectionMode';
|
||||
$_CONTEXT_ACCESSOR = context;
|
||||
$_LOCALS_ACCESSOR = locals;
|
||||
${_genHydrateDirectives()}
|
||||
${_genHydrateDetectors()}
|
||||
}
|
||||
|
||||
void dehydrate() {
|
||||
${_genPipeOnDestroy()}
|
||||
${_allFields().map((f) => '$f = $_UTIL.uninitialized();').join('')}
|
||||
$_LOCALS_ACCESSOR = null;
|
||||
}
|
||||
|
||||
hydrated() => !$_IDENTICAL_CHECK_FN(
|
||||
$_CONTEXT_ACCESSOR, $_UTIL.uninitialized());
|
||||
|
||||
static $_GEN_PREFIX.ProtoChangeDetector
|
||||
$PROTO_CHANGE_DETECTOR_FACTORY_METHOD(
|
||||
$_GEN_PREFIX.PipeRegistry registry,
|
||||
$_GEN_PREFIX.ChangeDetectorDefinition def) {
|
||||
return new $_GEN_PREFIX.PregenProtoChangeDetector(
|
||||
(a, b, c, d) => new $_typeName(a, b, c, d),
|
||||
registry, def);
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
List<String> _genGetDirectiveFieldNames() {
|
||||
return _directiveRecords
|
||||
.map((d) => _genGetDirective(d.directiveIndex))
|
||||
.toList();
|
||||
}
|
||||
|
||||
List<String> _genGetDetectorFieldNames() {
|
||||
return _directiveRecords
|
||||
.where((d) => d.isOnPushChangeDetection())
|
||||
.map((d) => _genGetDetector(d.directiveIndex))
|
||||
.toList();
|
||||
}
|
||||
|
||||
String _genGetDirective(DirectiveIndex d) => '_directive_${d.name}';
|
||||
String _genGetDetector(DirectiveIndex d) => '_detector_${d.name}';
|
||||
|
||||
List<String> _getNonNullPipeNames() {
|
||||
return _records
|
||||
.where((r) =>
|
||||
r.mode == RECORD_TYPE_PIPE || r.mode == RECORD_TYPE_BINDING_PIPE)
|
||||
.map((r) => _pipeNames[r.selfIndex])
|
||||
.toList();
|
||||
}
|
||||
|
||||
List<String> _allFields() {
|
||||
return new List.from(_fieldNames)
|
||||
..addAll(_getNonNullPipeNames())
|
||||
..addAll(_genGetDirectiveFieldNames())
|
||||
..addAll(_genGetDetectorFieldNames());
|
||||
}
|
||||
|
||||
String _genHydrateDirectives() {
|
||||
var buf = new StringBuffer();
|
||||
var directiveFieldNames = _genGetDirectiveFieldNames();
|
||||
for (var i = 0; i < directiveFieldNames.length; ++i) {
|
||||
buf.writeln('${directiveFieldNames[i]} = directives.getDirectiveFor('
|
||||
'$_DIRECTIVES_ACCESSOR[$i].directiveIndex);');
|
||||
}
|
||||
return '$buf';
|
||||
}
|
||||
|
||||
String _genHydrateDetectors() {
|
||||
var buf = new StringBuffer();
|
||||
var detectorFieldNames = _genGetDetectorFieldNames();
|
||||
for (var i = 0; i < detectorFieldNames.length; ++i) {
|
||||
buf.writeln('${detectorFieldNames[i]} = directives.getDetectorFor('
|
||||
'$_DIRECTIVES_ACCESSOR[$i].directiveIndex)');
|
||||
}
|
||||
return '$buf';
|
||||
}
|
||||
|
||||
String _genPipeOnDestroy() =>
|
||||
_getNonNullPipeNames().map((p) => '$p.onDestroy();').join('');
|
||||
|
||||
/// Generates calls to `onAllChangesDone` for all `Directive`s that request
|
||||
/// them.
|
||||
String _getCallOnAllChangesDoneBody() {
|
||||
// NOTE(kegluneq): Order is important!
|
||||
return _directiveRecords.reversed
|
||||
.where((rec) => rec.callOnAllChangesDone)
|
||||
.map((rec) =>
|
||||
'${_genGetDirective(rec.directiveIndex)}.onAllChangesDone();')
|
||||
.join('');
|
||||
}
|
||||
|
||||
String _genLocalDefinitions() =>
|
||||
_localNames.map((name) => 'var $name = null;').join('');
|
||||
|
||||
String _genChangeDefinitons() =>
|
||||
_changeNames.map((name) => 'var $name = false;').join('');
|
||||
|
||||
String _genRecord(ProtoRecord r) {
|
||||
if (r.mode == RECORD_TYPE_PIPE || r.mode == RECORD_TYPE_BINDING_PIPE) {
|
||||
return _genPipeCheck(r);
|
||||
} else {
|
||||
return _genReferenceCheck(r);
|
||||
}
|
||||
}
|
||||
|
||||
String _genPipeCheck(ProtoRecord r) {
|
||||
var context = _localNames[r.contextIndex];
|
||||
var oldValue = _fieldNames[r.selfIndex];
|
||||
var newValue = _localNames[r.selfIndex];
|
||||
var change = _changeNames[r.selfIndex];
|
||||
|
||||
var pipe = _pipeNames[r.selfIndex];
|
||||
var cdRef = r.mode == RECORD_TYPE_BINDING_PIPE ? 'this.ref' : 'null';
|
||||
|
||||
var protoIndex = r.selfIndex - 1;
|
||||
var pipeType = r.name;
|
||||
return '''
|
||||
$_CURRENT_PROTO = $_PROTOS_ACCESSOR[$protoIndex];
|
||||
if ($_IDENTICAL_CHECK_FN($pipe, $_UTIL.uninitialized())) {
|
||||
$pipe = $_PIPE_REGISTRY_ACCESSOR.get('$pipeType', $context, $cdRef);
|
||||
} else if (!$pipe.supports($context)) {
|
||||
$pipe.onDestroy();
|
||||
$pipe = $_PIPE_REGISTRY_ACCESSOR.get('$pipeType', $context, $cdRef);
|
||||
}
|
||||
|
||||
$newValue = $pipe.transform($context);
|
||||
if (!$_IDENTICAL_CHECK_FN($oldValue, $newValue)) {
|
||||
$newValue = $_UTIL.unwrapValue($newValue);
|
||||
$change = true;
|
||||
${_genUpdateDirectiveOrElement(r)}
|
||||
${_genAddToChanges(r)}
|
||||
$oldValue = $newValue;
|
||||
}
|
||||
${_genLastInDirective(r)}
|
||||
''';
|
||||
}
|
||||
|
||||
String _genReferenceCheck(ProtoRecord r) {
|
||||
var oldValue = _fieldNames[r.selfIndex];
|
||||
var newValue = _localNames[r.selfIndex];
|
||||
|
||||
var protoIndex = r.selfIndex - 1;
|
||||
var check = '''
|
||||
$_CURRENT_PROTO = $_PROTOS_ACCESSOR[$protoIndex];
|
||||
${_genUpdateCurrentValue(r)}
|
||||
if (!$_IDENTICAL_CHECK_FN($newValue, $oldValue)) {
|
||||
${_changeNames[r.selfIndex]} = true;
|
||||
${_genUpdateDirectiveOrElement(r)}
|
||||
${_genAddToChanges(r)}
|
||||
$oldValue = $newValue;
|
||||
}
|
||||
${_genLastInDirective(r)}
|
||||
''';
|
||||
if (r.isPureFunction()) {
|
||||
// Add an "if changed guard"
|
||||
var condition = r.args.map((a) => _changeNames[a]).join(' || ');
|
||||
return 'if ($condition) { $check }';
|
||||
} else {
|
||||
return check;
|
||||
}
|
||||
}
|
||||
|
||||
String _genUpdateCurrentValue(ProtoRecord r) {
|
||||
var context = r.contextIndex == -1
|
||||
? _genGetDirective(r.directiveIndex)
|
||||
: _localNames[r.contextIndex];
|
||||
|
||||
var newValue = _localNames[r.selfIndex];
|
||||
var argString = r.args.map((arg) => _localNames[arg]).join(', ');
|
||||
|
||||
var rhs;
|
||||
switch (r.mode) {
|
||||
case RECORD_TYPE_SELF:
|
||||
rhs = context;
|
||||
break;
|
||||
|
||||
case RECORD_TYPE_CONST:
|
||||
rhs = JSON.encode(r.funcOrValue);
|
||||
break;
|
||||
|
||||
case RECORD_TYPE_PROPERTY:
|
||||
rhs = '$context.${r.name}';
|
||||
break;
|
||||
|
||||
case RECORD_TYPE_LOCAL:
|
||||
rhs = '$_LOCALS_ACCESSOR.get("${r.name}")';
|
||||
break;
|
||||
|
||||
case RECORD_TYPE_INVOKE_METHOD:
|
||||
rhs = '$context.${r.name}($argString)';
|
||||
break;
|
||||
|
||||
case RECORD_TYPE_INVOKE_CLOSURE:
|
||||
rhs = '$context($argString)';
|
||||
break;
|
||||
|
||||
case RECORD_TYPE_PRIMITIVE_OP:
|
||||
rhs = '$_UTIL.${r.name}($argString)';
|
||||
break;
|
||||
|
||||
case RECORD_TYPE_INTERPOLATE:
|
||||
rhs = _genInterpolation(r);
|
||||
break;
|
||||
|
||||
case RECORD_TYPE_KEYED_ACCESS:
|
||||
rhs = '$context[${_localNames[r.args[0]]}]';
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new FormatException(
|
||||
'Unknown operation ${r.mode}', r.expressionAsString);
|
||||
}
|
||||
return '$newValue = $rhs;';
|
||||
}
|
||||
|
||||
String _genInterpolation(ProtoRecord r) {
|
||||
var res = new StringBuffer();
|
||||
for (var i = 0; i < r.args.length; ++i) {
|
||||
res.write('${JSON.encode(r.fixedArgs[i])} + ${_localNames[r.args[i]]} +');
|
||||
}
|
||||
res.write(JSON.encode(r.fixedArgs[r.args.length]));
|
||||
return '$res';
|
||||
}
|
||||
|
||||
String _genUpdateDirectiveOrElement(ProtoRecord r) {
|
||||
if (!r.lastInBinding) return '';
|
||||
|
||||
var newValue = _localNames[r.selfIndex];
|
||||
var oldValue = _fieldNames[r.selfIndex];
|
||||
|
||||
var br = r.bindingRecord;
|
||||
if (br.isDirective()) {
|
||||
var directiveProperty =
|
||||
'${_genGetDirective(br.directiveRecord.directiveIndex)}.${br.propertyName}';
|
||||
return '''
|
||||
${_genThrowOnChangeCheck(oldValue, newValue)}
|
||||
$directiveProperty = $newValue;
|
||||
$_IS_CHANGED_LOCAL = true;
|
||||
''';
|
||||
} else {
|
||||
return '''
|
||||
${_genThrowOnChangeCheck(oldValue, newValue)}
|
||||
$_DISPATCHER_ACCESSOR.notifyOnBinding(
|
||||
$_CURRENT_PROTO.bindingRecord, ${newValue});
|
||||
''';
|
||||
}
|
||||
}
|
||||
|
||||
String _genThrowOnChangeCheck(String oldValue, String newValue) {
|
||||
return '''
|
||||
if(throwOnChange) {
|
||||
$_UTIL.throwOnChange(
|
||||
$_CURRENT_PROTO, $_UTIL.simpleChange(${oldValue}, ${newValue}));
|
||||
}
|
||||
''';
|
||||
}
|
||||
|
||||
String _genAddToChanges(ProtoRecord r) {
|
||||
var newValue = _localNames[r.selfIndex];
|
||||
var oldValue = _fieldNames[r.selfIndex];
|
||||
if (!r.bindingRecord.callOnChange()) return '';
|
||||
return '''
|
||||
$_CHANGES_LOCAL = $_UTIL.addChange(
|
||||
$_CHANGES_LOCAL,
|
||||
$_CURRENT_PROTO.bindingRecord.propertyName,
|
||||
$_UTIL.simpleChange($oldValue, $newValue));
|
||||
''';
|
||||
}
|
||||
|
||||
String _genLastInDirective(ProtoRecord r) {
|
||||
return '''
|
||||
${_genNotifyOnChanges(r)}
|
||||
${_genNotifyOnPushDetectors(r)}
|
||||
$_IS_CHANGED_LOCAL = false;
|
||||
''';
|
||||
}
|
||||
|
||||
String _genNotifyOnChanges(ProtoRecord r) {
|
||||
var br = r.bindingRecord;
|
||||
if (!r.lastInDirective || !br.callOnChange()) return '';
|
||||
return '''
|
||||
if($_CHANGES_LOCAL) {
|
||||
${_genGetDirective(br.directiveRecord.directiveIndex)}
|
||||
.onChange($_CHANGES_LOCAL);
|
||||
$_CHANGES_LOCAL = null;
|
||||
}
|
||||
''';
|
||||
}
|
||||
|
||||
String _genNotifyOnPushDetectors(ProtoRecord r) {
|
||||
var br = r.bindingRecord;
|
||||
if (!r.lastInDirective || !br.isOnPushChangeDetection()) return '';
|
||||
return '''
|
||||
if($_IS_CHANGED_LOCAL) {
|
||||
${_genGetDetector(br.directiveRecord.directiveIndex)}.markAsCheckOnce();
|
||||
}
|
||||
''';
|
||||
}
|
||||
}
|
||||
|
||||
const PROTO_CHANGE_DETECTOR_FACTORY_METHOD = 'newProtoChangeDetector';
|
||||
|
||||
const _BASE_CLASS = '$_GEN_PREFIX.AbstractChangeDetector';
|
||||
const _CHANGES_LOCAL = 'changes';
|
||||
const _CONTEXT_ACCESSOR = '_context';
|
||||
const _CURRENT_PROTO = 'currentProto';
|
||||
const _DIRECTIVES_ACCESSOR = '_directiveRecords';
|
||||
const _DISPATCHER_ACCESSOR = '_dispatcher';
|
||||
const _GEN_PREFIX = '_gen';
|
||||
const _GEN_RECORDS_METHOD_NAME = '_createRecords';
|
||||
const _IDENTICAL_CHECK_FN = '$_GEN_PREFIX.looseIdentical';
|
||||
const _IS_CHANGED_LOCAL = 'isChanged';
|
||||
const _LOCALS_ACCESSOR = '_locals';
|
||||
const _MODE_ACCESSOR = 'mode';
|
||||
const _PREGEN_PROTO_CHANGE_DETECTOR_IMPORT =
|
||||
'package:angular2/src/change_detection/pregen_proto_change_detector.dart';
|
||||
const _PIPE_REGISTRY_ACCESSOR = '_pipeRegistry';
|
||||
const _PROTOS_ACCESSOR = '_protos';
|
||||
const _UTIL = '$_GEN_PREFIX.ChangeDetectionUtil';
|
|
@ -4,6 +4,7 @@ import 'dart:async';
|
|||
|
||||
import 'package:angular2/src/change_detection/parser/lexer.dart' as ng;
|
||||
import 'package:angular2/src/change_detection/parser/parser.dart' as ng;
|
||||
import 'package:angular2/src/core/compiler/proto_view_factory.dart';
|
||||
import 'package:angular2/src/render/api.dart';
|
||||
import 'package:angular2/src/render/dom/compiler/compiler.dart';
|
||||
import 'package:angular2/src/render/dom/compiler/template_loader.dart';
|
||||
|
@ -11,13 +12,13 @@ import 'package:angular2/src/services/xhr.dart' show XHR;
|
|||
import 'package:angular2/src/reflection/reflection.dart';
|
||||
import 'package:angular2/src/services/url_resolver.dart';
|
||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||
import 'package:angular2/src/transform/common/names.dart';
|
||||
import 'package:angular2/src/transform/common/property_utils.dart' as prop;
|
||||
import 'package:angular2/src/transform/common/xhr_impl.dart';
|
||||
import 'package:barback/barback.dart';
|
||||
|
||||
import 'change_detector_codegen.dart' as change;
|
||||
import 'compile_step_factory.dart';
|
||||
import 'recording_reflection_capabilities.dart';
|
||||
import 'reflector_register_codegen.dart' as reg;
|
||||
import 'view_definition_creator.dart';
|
||||
|
||||
/// Reads the `.ng_deps.dart` file represented by `entryPoint` and parses any
|
||||
|
@ -25,73 +26,62 @@ import 'view_definition_creator.dart';
|
|||
/// `setter`s, and `method`s that would otherwise be reflectively accessed.
|
||||
///
|
||||
/// This method assumes a {@link DomAdapter} has been registered.
|
||||
Future<String> processTemplates(AssetReader reader, AssetId entryPoint) async {
|
||||
Future<String> processTemplates(AssetReader reader, AssetId entryPoint,
|
||||
{bool generateRegistrations: true,
|
||||
bool generateChangeDetectors: true}) async {
|
||||
var viewDefResults = await createViewDefinitions(reader, entryPoint);
|
||||
var extractor = new _TemplateExtractor(new XhrImpl(reader, entryPoint));
|
||||
|
||||
var registrations = new StringBuffer();
|
||||
for (var viewDef in viewDefResults.viewDefinitions.values) {
|
||||
var values = await extractor.extractTemplates(viewDef);
|
||||
if (values == null) continue;
|
||||
var calls = _generateGetters(values.getterNames);
|
||||
if (calls.isNotEmpty) {
|
||||
registrations.write('..${REGISTER_GETTERS_METHOD_NAME}'
|
||||
'({${calls.join(', ')}})');
|
||||
}
|
||||
calls = _generateSetters(values.setterNames);
|
||||
if (calls.isNotEmpty) {
|
||||
registrations.write('..${REGISTER_SETTERS_METHOD_NAME}'
|
||||
'({${calls.join(', ')}})');
|
||||
}
|
||||
calls = _generateMethods(values.methodNames);
|
||||
if (calls.isNotEmpty) {
|
||||
registrations.write('..${REGISTER_METHODS_METHOD_NAME}'
|
||||
'({${calls.join(', ')}})');
|
||||
var registrations = new reg.Codegen();
|
||||
var changeDetectorClasses = new change.Codegen();
|
||||
for (var rType in viewDefResults.viewDefinitions.keys) {
|
||||
var viewDefEntry = viewDefResults.viewDefinitions[rType];
|
||||
var result = await extractor.extractTemplates(viewDefEntry.viewDef);
|
||||
if (result == null) continue;
|
||||
|
||||
registrations.generate(result.recording);
|
||||
if (result.protoView != null && generateChangeDetectors) {
|
||||
var savedReflectionCapabilities = reflector.reflectionCapabilities;
|
||||
var recordingCapabilities = new RecordingReflectionCapabilities();
|
||||
reflector.reflectionCapabilities = recordingCapabilities;
|
||||
|
||||
var defs = getChangeDetectorDefinitions(viewDefEntry.hostMetadata,
|
||||
result.protoView, viewDefEntry.viewDef.directives);
|
||||
for (var i = 0; i < defs.length; ++i) {
|
||||
changeDetectorClasses.generate(
|
||||
'_${rType.typeName}_ChangeDetector$i', defs[i]);
|
||||
}
|
||||
|
||||
// Check that getters, setters, methods are the same as above.
|
||||
assert(recordingCapabilities.getterNames
|
||||
.containsAll(result.recording.getterNames));
|
||||
assert(result.recording.getterNames
|
||||
.containsAll(recordingCapabilities.getterNames));
|
||||
assert(recordingCapabilities.setterNames
|
||||
.containsAll(result.recording.setterNames));
|
||||
assert(result.recording.setterNames
|
||||
.containsAll(recordingCapabilities.setterNames));
|
||||
assert(recordingCapabilities.methodNames
|
||||
.containsAll(result.recording.methodNames));
|
||||
assert(result.recording.methodNames
|
||||
.containsAll(recordingCapabilities.methodNames));
|
||||
|
||||
reflector.reflectionCapabilities = savedReflectionCapabilities;
|
||||
}
|
||||
}
|
||||
|
||||
var code = viewDefResults.ngDeps.code;
|
||||
if (registrations.length == 0) return code;
|
||||
if (registrations.isEmpty && changeDetectorClasses.isEmpty) return code;
|
||||
var importInjectIdx =
|
||||
viewDefResults.ngDeps.lib != null ? viewDefResults.ngDeps.lib.end : 0;
|
||||
var codeInjectIdx =
|
||||
viewDefResults.ngDeps.registeredTypes.last.registerMethod.end;
|
||||
return '${code.substring(0, codeInjectIdx)}'
|
||||
return '${code.substring(0, importInjectIdx)}'
|
||||
'${changeDetectorClasses.imports}'
|
||||
'${code.substring(importInjectIdx, codeInjectIdx)}'
|
||||
'${registrations}'
|
||||
'${code.substring(codeInjectIdx)}';
|
||||
}
|
||||
|
||||
Iterable<String> _generateGetters(Iterable<String> getterNames) {
|
||||
return getterNames.map((getterName) {
|
||||
if (!prop.isValid(getterName)) {
|
||||
// TODO(kegluenq): Eagerly throw here once #1295 is addressed.
|
||||
return prop.lazyInvalidGetter(getterName);
|
||||
} else {
|
||||
return ''' '${prop.sanitize(getterName)}': (o) => o.$getterName''';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Iterable<String> _generateSetters(Iterable<String> setterName) {
|
||||
return setterName.map((setterName) {
|
||||
if (!prop.isValid(setterName)) {
|
||||
// TODO(kegluenq): Eagerly throw here once #1295 is addressed.
|
||||
return prop.lazyInvalidSetter(setterName);
|
||||
} else {
|
||||
return ''' '${prop.sanitize(setterName)}': '''
|
||||
''' (o, v) => o.$setterName = v ''';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Iterable<String> _generateMethods(Iterable<String> methodNames) {
|
||||
return methodNames.map((methodName) {
|
||||
if (!prop.isValid(methodName)) {
|
||||
// TODO(kegluenq): Eagerly throw here once #1295 is addressed.
|
||||
return prop.lazyInvalidMethod(methodName);
|
||||
} else {
|
||||
return ''' '${prop.sanitize(methodName)}': '''
|
||||
'(o, List args) => Function.apply(o.$methodName, args) ';
|
||||
}
|
||||
});
|
||||
'${code.substring(codeInjectIdx)}'
|
||||
'$changeDetectorClasses';
|
||||
}
|
||||
|
||||
/// Extracts `template` and `url` values from `View` annotations, reads
|
||||
|
@ -104,8 +94,7 @@ class _TemplateExtractor {
|
|||
new CompileStepFactory(new ng.Parser(new ng.Lexer())),
|
||||
new TemplateLoader(xhr, new UrlResolver()));
|
||||
|
||||
Future<RecordingReflectionCapabilities> extractTemplates(
|
||||
ViewDefinition viewDef) async {
|
||||
Future<_ExtractResult> extractTemplates(ViewDefinition viewDef) async {
|
||||
// Check for "imperative views".
|
||||
if (viewDef.template == null && viewDef.absUrl == null) return null;
|
||||
|
||||
|
@ -114,9 +103,16 @@ class _TemplateExtractor {
|
|||
reflector.reflectionCapabilities = recordingCapabilities;
|
||||
|
||||
// TODO(kegluneq): Rewrite url to inline `template` where possible.
|
||||
await _compiler.compile(viewDef);
|
||||
var protoViewDto = await _compiler.compile(viewDef);
|
||||
|
||||
reflector.reflectionCapabilities = savedReflectionCapabilities;
|
||||
return recordingCapabilities;
|
||||
return new _ExtractResult(recordingCapabilities, protoViewDto);
|
||||
}
|
||||
}
|
||||
|
||||
class _ExtractResult {
|
||||
final RecordingReflectionCapabilities recording;
|
||||
final ProtoViewDto protoView;
|
||||
|
||||
_ExtractResult(this.recording, this.protoView);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
library angular2.transform.template_compiler.reflector_register_codegen;
|
||||
|
||||
import 'package:angular2/src/transform/common/names.dart';
|
||||
import 'package:angular2/src/transform/common/property_utils.dart' as prop;
|
||||
import 'recording_reflection_capabilities.dart';
|
||||
|
||||
class Codegen {
|
||||
final StringBuffer _buf = new StringBuffer();
|
||||
|
||||
void generate(RecordingReflectionCapabilities recording) {
|
||||
if (recording != null) {
|
||||
var calls = _generateGetters(recording.getterNames);
|
||||
if (calls.isNotEmpty) {
|
||||
_buf.write('..${REGISTER_GETTERS_METHOD_NAME}'
|
||||
'({${calls.join(', ')}})');
|
||||
}
|
||||
calls = _generateSetters(recording.setterNames);
|
||||
if (calls.isNotEmpty) {
|
||||
_buf.write('..${REGISTER_SETTERS_METHOD_NAME}'
|
||||
'({${calls.join(', ')}})');
|
||||
}
|
||||
calls = _generateMethods(recording.methodNames);
|
||||
if (calls.isNotEmpty) {
|
||||
_buf.write('..${REGISTER_METHODS_METHOD_NAME}'
|
||||
'({${calls.join(', ')}})');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool get isEmpty => _buf.isEmpty;
|
||||
|
||||
@override
|
||||
String toString() => '$_buf';
|
||||
}
|
||||
|
||||
Iterable<String> _generateGetters(Iterable<String> getterNames) {
|
||||
return getterNames.map((getterName) {
|
||||
if (!prop.isValid(getterName)) {
|
||||
// TODO(kegluenq): Eagerly throw here once #1295 is addressed.
|
||||
return prop.lazyInvalidGetter(getterName);
|
||||
} else {
|
||||
return ''' '${prop.sanitize(getterName)}': (o) => o.$getterName''';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Iterable<String> _generateSetters(Iterable<String> setterName) {
|
||||
return setterName.map((setterName) {
|
||||
if (!prop.isValid(setterName)) {
|
||||
// TODO(kegluenq): Eagerly throw here once #1295 is addressed.
|
||||
return prop.lazyInvalidSetter(setterName);
|
||||
} else {
|
||||
return ''' '${prop.sanitize(setterName)}': '''
|
||||
''' (o, v) => o.$setterName = v ''';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Iterable<String> _generateMethods(Iterable<String> methodNames) {
|
||||
return methodNames.map((methodName) {
|
||||
if (!prop.isValid(methodName)) {
|
||||
// TODO(kegluenq): Eagerly throw here once #1295 is addressed.
|
||||
return prop.lazyInvalidMethod(methodName);
|
||||
} else {
|
||||
return ''' '${prop.sanitize(methodName)}': '''
|
||||
'(o, List args) => Function.apply(o.$methodName, args) ';
|
||||
}
|
||||
});
|
||||
}
|
|
@ -22,10 +22,17 @@ Future<ViewDefinitionResults> createViewDefinitions(
|
|||
|
||||
class ViewDefinitionResults {
|
||||
final NgDeps ngDeps;
|
||||
final Map<RegisteredType, ViewDefinition> viewDefinitions;
|
||||
final Map<RegisteredType, ViewDefinitionEntry> viewDefinitions;
|
||||
ViewDefinitionResults._(this.ngDeps, this.viewDefinitions);
|
||||
}
|
||||
|
||||
class ViewDefinitionEntry {
|
||||
final DirectiveMetadata hostMetadata;
|
||||
final ViewDefinition viewDef;
|
||||
|
||||
ViewDefinitionEntry._(this.hostMetadata, this.viewDef);
|
||||
}
|
||||
|
||||
String _getComponentId(AssetId assetId, String className) =>
|
||||
'$assetId:$className';
|
||||
|
||||
|
@ -48,21 +55,24 @@ class _ViewDefinitionCreator {
|
|||
Future<ViewDefinitionResults> createViewDefs() async {
|
||||
var ngDeps = await ngDepsFuture;
|
||||
|
||||
var retVal = <RegisteredType, ViewDefinition>{};
|
||||
var retVal = <RegisteredType, ViewDefinitionEntry>{};
|
||||
var visitor = new _TemplateExtractVisitor(await _createMetadataMap());
|
||||
ngDeps.registeredTypes.forEach((rType) {
|
||||
visitor.reset();
|
||||
rType.annotations.accept(visitor);
|
||||
if (visitor.viewDef != null) {
|
||||
var typeName = '${rType.typeName}';
|
||||
var hostMetadata = null;
|
||||
if (visitor._metadataMap.containsKey(typeName)) {
|
||||
visitor.viewDef.componentId = visitor._metadataMap[typeName].id;
|
||||
hostMetadata = visitor._metadataMap[typeName];
|
||||
visitor.viewDef.componentId = hostMetadata.id;
|
||||
} else {
|
||||
logger.warning('Missing component "$typeName" in metadata map',
|
||||
logger.error('Missing component "$typeName" in metadata map',
|
||||
asset: entryPoint);
|
||||
visitor.viewDef.componentId = _getComponentId(entryPoint, typeName);
|
||||
}
|
||||
retVal[rType] = visitor.viewDef;
|
||||
retVal[rType] =
|
||||
new ViewDefinitionEntry._(hostMetadata, visitor.viewDef);
|
||||
}
|
||||
});
|
||||
return new ViewDefinitionResults._(ngDeps, retVal);
|
||||
|
|
|
@ -44,8 +44,120 @@ import {
|
|||
ProtoChangeDetector
|
||||
} from 'angular2/change_detection';
|
||||
|
||||
import {getDefinition} from './simple_watch_config';
|
||||
import {getFactoryById} from './generated/simple_watch_classes';
|
||||
|
||||
|
||||
export function main() {
|
||||
// These tests also run against pre-generated Dart Change Detectors. We will move tests up from
|
||||
// the loop below as they are converted.
|
||||
ListWrapper.forEach(['dynamic', 'JIT', 'Pregen'], (cdType) => {
|
||||
|
||||
if (cdType == "JIT" && IS_DARTIUM) return;
|
||||
if (cdType == "Pregen" && !IS_DARTIUM) return;
|
||||
|
||||
describe(`${cdType} Change Detector`, () => {
|
||||
|
||||
function _getProtoChangeDetector(def: ChangeDetectorDefinition) {
|
||||
var registry = null;
|
||||
switch (cdType) {
|
||||
case 'dynamic':
|
||||
return new DynamicProtoChangeDetector(registry, def);
|
||||
case 'JIT':
|
||||
return new JitProtoChangeDetector(registry, def);
|
||||
case 'Pregen':
|
||||
return getFactoryById(def.id)(registry, def);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function _bindConstValue(expression: string) {
|
||||
var dispatcher = new TestDispatcher();
|
||||
var protoCd = _getProtoChangeDetector(getDefinition(expression, 'propName'));
|
||||
var cd = protoCd.instantiate(dispatcher);
|
||||
|
||||
var context = null;
|
||||
var locals = null;
|
||||
cd.hydrate(context, locals, null);
|
||||
cd.detectChanges();
|
||||
return dispatcher.log;
|
||||
}
|
||||
|
||||
it('should support literals',
|
||||
() => { expect(_bindConstValue('10')).toEqual(['propName=10']); });
|
||||
it('should strip quotes from literals',
|
||||
() => { expect(_bindConstValue('"str"')).toEqual(['propName=str']); });
|
||||
it('should support newlines in literals',
|
||||
() => { expect(_bindConstValue('"a\n\nb"')).toEqual(['propName=a\n\nb']); });
|
||||
it('should support + operations',
|
||||
() => { expect(_bindConstValue('10 + 2')).toEqual(['propName=12']); });
|
||||
it('should support - operations',
|
||||
() => { expect(_bindConstValue('10 - 2')).toEqual(['propName=8']); });
|
||||
it('should support * operations',
|
||||
() => { expect(_bindConstValue('10 * 2')).toEqual(['propName=20']); });
|
||||
it('should support / operations', () => {
|
||||
expect(_bindConstValue('10 / 2')).toEqual([`propName=${5.0}`]);
|
||||
}); // dart exp=5.0, js exp=5
|
||||
it('should support % operations',
|
||||
() => { expect(_bindConstValue('11 % 2')).toEqual(['propName=1']); });
|
||||
it('should support == operations on identical',
|
||||
() => { expect(_bindConstValue('1 == 1')).toEqual(['propName=true']); });
|
||||
it('should support != operations',
|
||||
() => { expect(_bindConstValue('1 != 1')).toEqual(['propName=false']); });
|
||||
it('should support == operations on coerceible', () => {
|
||||
var expectedValue = IS_DARTIUM ? 'false' : 'true';
|
||||
expect(_bindConstValue('1 == true')).toEqual([`propName=${expectedValue}`]);
|
||||
});
|
||||
it('should support === operations on identical',
|
||||
() => { expect(_bindConstValue('1 === 1')).toEqual(['propName=true']); });
|
||||
it('should support !== operations',
|
||||
() => { expect(_bindConstValue('1 !== 1')).toEqual(['propName=false']); });
|
||||
it('should support === operations on coerceible',
|
||||
() => { expect(_bindConstValue('1 === true')).toEqual(['propName=false']); });
|
||||
it('should support true < operations',
|
||||
() => { expect(_bindConstValue('1 < 2')).toEqual(['propName=true']); });
|
||||
it('should support false < operations',
|
||||
() => { expect(_bindConstValue('2 < 1')).toEqual(['propName=false']); });
|
||||
it('should support false > operations',
|
||||
() => { expect(_bindConstValue('1 > 2')).toEqual(['propName=false']); });
|
||||
it('should support true > operations',
|
||||
() => { expect(_bindConstValue('2 > 1')).toEqual(['propName=true']); });
|
||||
it('should support true <= operations',
|
||||
() => { expect(_bindConstValue('1 <= 2')).toEqual(['propName=true']); });
|
||||
it('should support equal <= operations',
|
||||
() => { expect(_bindConstValue('2 <= 2')).toEqual(['propName=true']); });
|
||||
it('should support false <= operations',
|
||||
() => { expect(_bindConstValue('2 <= 1')).toEqual(['propName=false']); });
|
||||
it('should support true >= operations',
|
||||
() => { expect(_bindConstValue('2 >= 1')).toEqual(['propName=true']); });
|
||||
it('should support equal >= operations',
|
||||
() => { expect(_bindConstValue('2 >= 2')).toEqual(['propName=true']); });
|
||||
it('should support false >= operations',
|
||||
() => { expect(_bindConstValue('1 >= 2')).toEqual(['propName=false']); });
|
||||
it('should support true && operations',
|
||||
() => { expect(_bindConstValue('true && true')).toEqual(['propName=true']); });
|
||||
it('should support false && operations',
|
||||
() => { expect(_bindConstValue('true && false')).toEqual(['propName=false']); });
|
||||
it('should support true || operations',
|
||||
() => { expect(_bindConstValue('true || false')).toEqual(['propName=true']); });
|
||||
it('should support false || operations',
|
||||
() => { expect(_bindConstValue('false || false')).toEqual(['propName=false']); });
|
||||
it('should support negate',
|
||||
() => { expect(_bindConstValue('!true')).toEqual(['propName=false']); });
|
||||
it('should support double negate',
|
||||
() => { expect(_bindConstValue('!!true')).toEqual(['propName=true']); });
|
||||
it('should support true conditionals',
|
||||
() => { expect(_bindConstValue('1 < 2 ? 1 : 2')).toEqual(['propName=1']); });
|
||||
it('should support false conditionals',
|
||||
() => { expect(_bindConstValue('1 > 2 ? 1 : 2')).toEqual(['propName=2']); });
|
||||
it('should support keyed access to a list item',
|
||||
() => { expect(_bindConstValue('["foo", "bar"][0]')).toEqual(['propName=foo']); });
|
||||
it('should support keyed access to a map item',
|
||||
() => { expect(_bindConstValue('{"foo": "bar"}["foo"]')).toEqual(['propName=bar']); });
|
||||
});
|
||||
});
|
||||
|
||||
describe("change detection", () => {
|
||||
StringMapWrapper.forEach(
|
||||
{
|
||||
|
@ -145,12 +257,6 @@ export function main() {
|
|||
expect(executeWatch('a', 'a', td)).toEqual(['a=null']);
|
||||
});
|
||||
|
||||
it("should support literals", () => {
|
||||
expect(executeWatch('const', '10')).toEqual(['const=10']);
|
||||
expect(executeWatch('const', '"str"')).toEqual(['const=str']);
|
||||
expect(executeWatch('const', '"a\n\nb"')).toEqual(['const=a\n\nb']);
|
||||
});
|
||||
|
||||
it('should support simple chained property access', () => {
|
||||
var address = new Address('Grenoble');
|
||||
var person = new Person('Victor', address);
|
||||
|
@ -207,68 +313,6 @@ export function main() {
|
|||
expect(c["dispatcher"].loggedValues[0]['z']).toEqual(1);
|
||||
});
|
||||
|
||||
it("should support binary operations", () => {
|
||||
expect(executeWatch('exp', '10 + 2')).toEqual(['exp=12']);
|
||||
expect(executeWatch('exp', '10 - 2')).toEqual(['exp=8']);
|
||||
|
||||
expect(executeWatch('exp', '10 * 2')).toEqual(['exp=20']);
|
||||
expect(executeWatch('exp', '10 / 2'))
|
||||
.toEqual([`exp=${5.0}`]); // dart exp=5.0, js exp=5
|
||||
expect(executeWatch('exp', '11 % 2')).toEqual(['exp=1']);
|
||||
|
||||
expect(executeWatch('exp', '1 == 1')).toEqual(['exp=true']);
|
||||
if (IS_DARTIUM) {
|
||||
expect(executeWatch('exp', '1 == "1"')).toEqual(['exp=false']);
|
||||
} else {
|
||||
expect(executeWatch('exp', '1 == "1"')).toEqual(['exp=true']);
|
||||
}
|
||||
expect(executeWatch('exp', '1 != 1')).toEqual(['exp=false']);
|
||||
|
||||
expect(executeWatch('exp', '1 === 1')).toEqual(['exp=true']);
|
||||
expect(executeWatch('exp', '1 !== 1')).toEqual(['exp=false']);
|
||||
expect(executeWatch('exp', '1 === "1"')).toEqual(['exp=false']);
|
||||
|
||||
expect(executeWatch('exp', '1 < 2')).toEqual(['exp=true']);
|
||||
expect(executeWatch('exp', '2 < 1')).toEqual(['exp=false']);
|
||||
|
||||
expect(executeWatch('exp', '2 > 1')).toEqual(['exp=true']);
|
||||
expect(executeWatch('exp', '2 < 1')).toEqual(['exp=false']);
|
||||
|
||||
expect(executeWatch('exp', '1 <= 2')).toEqual(['exp=true']);
|
||||
expect(executeWatch('exp', '2 <= 2')).toEqual(['exp=true']);
|
||||
expect(executeWatch('exp', '2 <= 1')).toEqual(['exp=false']);
|
||||
|
||||
expect(executeWatch('exp', '2 >= 1')).toEqual(['exp=true']);
|
||||
expect(executeWatch('exp', '2 >= 2')).toEqual(['exp=true']);
|
||||
expect(executeWatch('exp', '1 >= 2')).toEqual(['exp=false']);
|
||||
|
||||
expect(executeWatch('exp', 'true && true')).toEqual(['exp=true']);
|
||||
expect(executeWatch('exp', 'true && false')).toEqual(['exp=false']);
|
||||
|
||||
expect(executeWatch('exp', 'true || false')).toEqual(['exp=true']);
|
||||
expect(executeWatch('exp', 'false || false')).toEqual(['exp=false']);
|
||||
});
|
||||
|
||||
it("should support negate", () => {
|
||||
expect(executeWatch('exp', '!true')).toEqual(['exp=false']);
|
||||
expect(executeWatch('exp', '!!true')).toEqual(['exp=true']);
|
||||
});
|
||||
|
||||
it("should support conditionals", () => {
|
||||
expect(executeWatch('m', '1 < 2 ? 1 : 2')).toEqual(['m=1']);
|
||||
expect(executeWatch('m', '1 > 2 ? 1 : 2')).toEqual(['m=2']);
|
||||
});
|
||||
|
||||
describe("keyed access", () => {
|
||||
it("should support accessing a list item", () => {
|
||||
expect(executeWatch('array[0]', '["foo", "bar"][0]')).toEqual(['array[0]=foo']);
|
||||
});
|
||||
|
||||
it("should support accessing a map item", () => {
|
||||
expect(executeWatch('map[foo]', '{"foo": "bar"}["foo"]')).toEqual(['map[foo]=bar']);
|
||||
});
|
||||
});
|
||||
|
||||
it("should support interpolation", () => {
|
||||
var ast = parser.parseInterpolation("B{{a}}A", "location");
|
||||
var pcd = createProtoChangeDetector([BindingRecord.createForElement(ast, 0, "memo")]);
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
// Ignore me, needed to support Angular 2 Dart.
|
||||
|
||||
export function getFactoryById(id: string) {
|
||||
return null;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
library angular2.src.transform.di_transformer;
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dart_style/dart_style.dart';
|
||||
import 'package:angular2/src/transform/template_compiler/change_detector_codegen.dart';
|
||||
import '../simple_watch_config.dart';
|
||||
|
||||
void main(List<String> args) {
|
||||
var buf = new StringBuffer('var $_MAP_NAME = {');
|
||||
var codegen = new Codegen();
|
||||
var allDefs = getAllDefinitions('propName');
|
||||
for (var i = 0; i < allDefs.length; ++i) {
|
||||
var className = 'ChangeDetector${i}';
|
||||
codegen.generate(className, allDefs[i]);
|
||||
if (i > 0) {
|
||||
buf.write(',');
|
||||
}
|
||||
buf.write(" '''${allDefs[i].id}''': "
|
||||
"$className.$PROTO_CHANGE_DETECTOR_FACTORY_METHOD");
|
||||
}
|
||||
buf.write('};');
|
||||
print(new DartFormatter().format('''
|
||||
library dart_gen_change_detectors;
|
||||
|
||||
${codegen.imports}
|
||||
|
||||
$codegen
|
||||
$buf
|
||||
|
||||
getFactoryById(String id) => $_MAP_NAME[id];
|
||||
'''));
|
||||
}
|
||||
|
||||
const _MAP_NAME = '_idToProtoMap';
|
|
@ -0,0 +1,72 @@
|
|||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {BindingRecord, ChangeDetectorDefinition, Lexer, Parser} from 'angular2/change_detection';
|
||||
|
||||
var _parser = new Parser(new Lexer());
|
||||
|
||||
function _createChangeDetectorDefinition(id: string, propName: string,
|
||||
expression: string): ChangeDetectorDefinition {
|
||||
var ast = _parser.parseBinding(expression, 'location');
|
||||
var bindingRecords = [BindingRecord.createForElement(ast, 0, propName)];
|
||||
|
||||
var strategy = null;
|
||||
var variableBindings = [];
|
||||
var directiveRecords = [];
|
||||
return new ChangeDetectorDefinition(id, strategy, variableBindings, bindingRecords,
|
||||
directiveRecords);
|
||||
}
|
||||
|
||||
/**
|
||||
* In this case, we expect `id` and `expression` to be the same string.
|
||||
*/
|
||||
export function getDefinition(id: string, propName: string): ChangeDetectorDefinition {
|
||||
// TODO(kegluneq): Remove `propName`?
|
||||
if (ListWrapper.indexOf(_availableDefinitions, id) < 0) {
|
||||
throw `No ChangeDetectorDefinition for ${id} available. Please modify this file if necessary.`;
|
||||
}
|
||||
return _createChangeDetectorDefinition(id, propName, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available ChangeDetectorDefinition objects. Used to pre-generate Dart
|
||||
* `ChangeDetector` classes.
|
||||
*/
|
||||
export function getAllDefinitions(propName: string): List<ChangeDetectorDefinition> {
|
||||
return ListWrapper.map(_availableDefinitions, (id) => getDefinition(id, propName));
|
||||
}
|
||||
|
||||
var _availableDefinitions = [
|
||||
'10',
|
||||
'"str"',
|
||||
'"a\n\nb"',
|
||||
'10 + 2',
|
||||
'10 - 2',
|
||||
'10 * 2',
|
||||
'10 / 2',
|
||||
'11 % 2',
|
||||
'1 == 1',
|
||||
'1 != 1',
|
||||
'1 == true',
|
||||
'1 === 1',
|
||||
'1 !== 1',
|
||||
'1 === true',
|
||||
'1 < 2',
|
||||
'2 < 1',
|
||||
'1 > 2',
|
||||
'2 > 1',
|
||||
'1 <= 2',
|
||||
'2 <= 2',
|
||||
'2 <= 1',
|
||||
'2 >= 1',
|
||||
'2 >= 2',
|
||||
'1 >= 2',
|
||||
'true && true',
|
||||
'true && false',
|
||||
'true || false',
|
||||
'false || false',
|
||||
'!true',
|
||||
'!!true',
|
||||
'1 < 2 ? 1 : 2',
|
||||
'1 > 2 ? 1 : 2',
|
||||
'["foo", "bar"][0]',
|
||||
'{"foo": "bar"}["foo"]'
|
||||
];
|
|
@ -1,5 +1,8 @@
|
|||
library bar.ng_deps.dart;
|
||||
|
||||
import 'package:angular2/src/change_detection/pregen_proto_change_detector.dart'
|
||||
as _gen;
|
||||
|
||||
import 'bar.dart';
|
||||
import 'package:angular2/src/core/annotations_impl/annotations.dart';
|
||||
import 'package:angular2/src/core/annotations_impl/view.dart';
|
||||
|
@ -18,3 +21,48 @@ void initReflector(reflector) {
|
|||
]
|
||||
});
|
||||
}
|
||||
class _MyComponent_ChangeDetector0 extends _gen.AbstractChangeDetector {
|
||||
final dynamic _dispatcher;
|
||||
final _gen.PipeRegistry _pipeRegistry;
|
||||
final _gen.List<_gen.ProtoRecord> _protos;
|
||||
final _gen.List<_gen.DirectiveRecord> _directiveRecords;
|
||||
dynamic _locals = null;
|
||||
dynamic _context = _gen.ChangeDetectionUtil.uninitialized();
|
||||
|
||||
_MyComponent_ChangeDetector0(this._dispatcher, this._pipeRegistry,
|
||||
this._protos, this._directiveRecords)
|
||||
: super();
|
||||
|
||||
void detectChangesInRecords(throwOnChange) {
|
||||
var context = null;
|
||||
var change_context = false;
|
||||
var isChanged = false;
|
||||
var currentProto;
|
||||
var changes = null;
|
||||
|
||||
context = _context;
|
||||
}
|
||||
|
||||
void callOnAllChangesDone() {}
|
||||
|
||||
void hydrate(context, locals, directives) {
|
||||
mode = 'ALWAYS_CHECK';
|
||||
_context = context;
|
||||
_locals = locals;
|
||||
}
|
||||
|
||||
void dehydrate() {
|
||||
_context = _gen.ChangeDetectionUtil.uninitialized();
|
||||
_locals = null;
|
||||
}
|
||||
|
||||
hydrated() =>
|
||||
!_gen.looseIdentical(_context, _gen.ChangeDetectionUtil.uninitialized());
|
||||
|
||||
static _gen.ProtoChangeDetector newProtoChangeDetector(
|
||||
_gen.PipeRegistry registry, _gen.ChangeDetectorDefinition def) {
|
||||
return new _gen.PregenProtoChangeDetector(
|
||||
(a, b, c, d) => new _MyComponent_ChangeDetector0(a, b, c, d), registry,
|
||||
def);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
library angular2.test.transform.template_compiler.all_tests;
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:barback/barback.dart';
|
||||
import 'package:angular2/src/dom/html_adapter.dart';
|
||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||
|
@ -18,71 +19,79 @@ void allTests() {
|
|||
|
||||
beforeEach(() => setLogger(new PrintLogger()));
|
||||
|
||||
it('should parse simple expressions in inline templates.', () async {
|
||||
var inputPath =
|
||||
'template_compiler/inline_expression_files/hello.ng_deps.dart';
|
||||
var expected = readFile(
|
||||
'template_compiler/inline_expression_files/expected/hello.ng_deps.dart');
|
||||
var output = await processTemplates(reader, new AssetId('a', inputPath));
|
||||
_formatThenExpectEquals(output, expected);
|
||||
});
|
||||
describe('registrations', () {
|
||||
Future<String> process(AssetId assetId) =>
|
||||
processTemplates(reader, assetId, generateChangeDetectors: false);
|
||||
|
||||
it('should parse simple methods in inline templates.', () async {
|
||||
var inputPath = 'template_compiler/inline_method_files/hello.ng_deps.dart';
|
||||
var expected = readFile(
|
||||
'template_compiler/inline_method_files/expected/hello.ng_deps.dart');
|
||||
var output = await processTemplates(reader, new AssetId('a', inputPath));
|
||||
_formatThenExpectEquals(output, expected);
|
||||
});
|
||||
it('should parse simple expressions in inline templates.', () async {
|
||||
var inputPath =
|
||||
'template_compiler/inline_expression_files/hello.ng_deps.dart';
|
||||
var expected = readFile(
|
||||
'template_compiler/inline_expression_files/expected/hello.ng_deps.dart');
|
||||
var output = await process(new AssetId('a', inputPath));
|
||||
_formatThenExpectEquals(output, expected);
|
||||
});
|
||||
|
||||
it('should parse simple expressions in linked templates.', () async {
|
||||
var inputPath = 'template_compiler/url_expression_files/hello.ng_deps.dart';
|
||||
var expected = readFile(
|
||||
'template_compiler/url_expression_files/expected/hello.ng_deps.dart');
|
||||
var output = await processTemplates(reader, new AssetId('a', inputPath));
|
||||
_formatThenExpectEquals(output, expected);
|
||||
});
|
||||
it('should parse simple methods in inline templates.', () async {
|
||||
var inputPath =
|
||||
'template_compiler/inline_method_files/hello.ng_deps.dart';
|
||||
var expected = readFile(
|
||||
'template_compiler/inline_method_files/expected/hello.ng_deps.dart');
|
||||
var output = await process(new AssetId('a', inputPath));
|
||||
_formatThenExpectEquals(output, expected);
|
||||
});
|
||||
|
||||
it('should parse simple methods in linked templates.', () async {
|
||||
var inputPath = 'template_compiler/url_method_files/hello.ng_deps.dart';
|
||||
var expected = readFile(
|
||||
'template_compiler/url_method_files/expected/hello.ng_deps.dart');
|
||||
var output = await processTemplates(reader, new AssetId('a', inputPath));
|
||||
_formatThenExpectEquals(output, expected);
|
||||
});
|
||||
it('should parse simple expressions in linked templates.', () async {
|
||||
var inputPath =
|
||||
'template_compiler/url_expression_files/hello.ng_deps.dart';
|
||||
var expected = readFile(
|
||||
'template_compiler/url_expression_files/expected/hello.ng_deps.dart');
|
||||
var output = await process(new AssetId('a', inputPath));
|
||||
_formatThenExpectEquals(output, expected);
|
||||
});
|
||||
|
||||
it('should not generated duplicate getters/setters', () async {
|
||||
var inputPath = 'template_compiler/duplicate_files/hello.ng_deps.dart';
|
||||
var expected = readFile(
|
||||
'template_compiler/duplicate_files/expected/hello.ng_deps.dart');
|
||||
var output = await processTemplates(reader, new AssetId('a', inputPath));
|
||||
_formatThenExpectEquals(output, expected);
|
||||
});
|
||||
it('should parse simple methods in linked templates.', () async {
|
||||
var inputPath = 'template_compiler/url_method_files/hello.ng_deps.dart';
|
||||
var expected = readFile(
|
||||
'template_compiler/url_method_files/expected/hello.ng_deps.dart');
|
||||
var output = await process(new AssetId('a', inputPath));
|
||||
_formatThenExpectEquals(output, expected);
|
||||
});
|
||||
|
||||
it('should parse `View` directives with a single dependency.', () async {
|
||||
var inputPath = 'template_compiler/one_directive_files/hello.ng_deps.dart';
|
||||
var expected = readFile(
|
||||
'template_compiler/one_directive_files/expected/hello.ng_deps.dart');
|
||||
it('should not generated duplicate getters/setters', () async {
|
||||
var inputPath = 'template_compiler/duplicate_files/hello.ng_deps.dart';
|
||||
var expected = readFile(
|
||||
'template_compiler/duplicate_files/expected/hello.ng_deps.dart');
|
||||
var output = await process(new AssetId('a', inputPath));
|
||||
_formatThenExpectEquals(output, expected);
|
||||
});
|
||||
|
||||
var output = await processTemplates(reader, new AssetId('a', inputPath));
|
||||
_formatThenExpectEquals(output, expected);
|
||||
});
|
||||
it('should parse `View` directives with a single dependency.', () async {
|
||||
var inputPath =
|
||||
'template_compiler/one_directive_files/hello.ng_deps.dart';
|
||||
var expected = readFile(
|
||||
'template_compiler/one_directive_files/expected/hello.ng_deps.dart');
|
||||
|
||||
it('should parse `View` directives with a single prefixed dependency.',
|
||||
() async {
|
||||
var inputPath = 'template_compiler/with_prefix_files/hello.ng_deps.dart';
|
||||
var expected = readFile(
|
||||
'template_compiler/with_prefix_files/expected/hello.ng_deps.dart');
|
||||
var output = await process(new AssetId('a', inputPath));
|
||||
_formatThenExpectEquals(output, expected);
|
||||
});
|
||||
|
||||
var output = await processTemplates(reader, new AssetId('a', inputPath));
|
||||
_formatThenExpectEquals(output, expected);
|
||||
it('should parse `View` directives with a single prefixed dependency.',
|
||||
() async {
|
||||
var inputPath = 'template_compiler/with_prefix_files/hello.ng_deps.dart';
|
||||
var expected = readFile(
|
||||
'template_compiler/with_prefix_files/expected/hello.ng_deps.dart');
|
||||
|
||||
inputPath = 'template_compiler/with_prefix_files/goodbye.ng_deps.dart';
|
||||
expected = readFile(
|
||||
'template_compiler/with_prefix_files/expected/goodbye.ng_deps.dart');
|
||||
var output = await process(new AssetId('a', inputPath));
|
||||
_formatThenExpectEquals(output, expected);
|
||||
|
||||
output = await processTemplates(reader, new AssetId('a', inputPath));
|
||||
_formatThenExpectEquals(output, expected);
|
||||
inputPath = 'template_compiler/with_prefix_files/goodbye.ng_deps.dart';
|
||||
expected = readFile(
|
||||
'template_compiler/with_prefix_files/expected/goodbye.ng_deps.dart');
|
||||
|
||||
output = await process(new AssetId('a', inputPath));
|
||||
_formatThenExpectEquals(output, expected);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -46,11 +46,11 @@ module.exports = function(gulp, plugins, config) {
|
|||
|
||||
function analyze(dirName, done) {
|
||||
//TODO remove --package-warnings once dartanalyzer handles transitive libraries
|
||||
var args = ['--fatal-warnings', '--package-warnings'].concat(tempFile);
|
||||
var args = ['--fatal-warnings', '--package-warnings', '--format=machine'].concat(tempFile);
|
||||
|
||||
var stream = spawn(config.command, args, {
|
||||
// inherit stdin and stderr, but filter stdout
|
||||
stdio: [process.stdin, 'pipe', process.stderr],
|
||||
stdio: [process.stdin, process.stdout, 'pipe'],
|
||||
cwd: dirName
|
||||
});
|
||||
// Filter out unused imports from our generated file.
|
||||
|
@ -58,7 +58,7 @@ module.exports = function(gulp, plugins, config) {
|
|||
// as this could lead to name clashes when two files
|
||||
// export the same thing.
|
||||
var rl = readline.createInterface({
|
||||
input: stream.stdout,
|
||||
input: stream.stderr,
|
||||
output: process.stdout,
|
||||
terminal: false
|
||||
});
|
||||
|
@ -66,32 +66,29 @@ module.exports = function(gulp, plugins, config) {
|
|||
var errorCount = 0;
|
||||
var warningCount = 0;
|
||||
rl.on('line', function(line) {
|
||||
//TODO remove once dartanalyzer handles transitive libraries
|
||||
//skip errors in third-party packages
|
||||
if (line.indexOf(dirName) == -1) {
|
||||
var parsedLine = _AnalyzerOutputLine.parse(line);
|
||||
if (!parsedLine) {
|
||||
errorCount++;
|
||||
console.log('Unexpected output: ' + line);
|
||||
return;
|
||||
}
|
||||
if (line.match(/Unused import/)) {
|
||||
if (line.match(/_analyzer\.dart/)) {
|
||||
return;
|
||||
}
|
||||
//TODO remove once dartanalyzer handles transitive libraries
|
||||
//skip errors in third-party packages
|
||||
if (parsedLine.source.indexOf(dirName) == -1) {
|
||||
return;
|
||||
}
|
||||
// TODO: https://github.com/angular/ts2dart/issues/168
|
||||
if (line.match(/_stack' is not used/)) {
|
||||
if (parsedLine.shouldIgnore()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var skip = false;
|
||||
if (!skip) {
|
||||
if (line.match(/\[hint\]/)) {
|
||||
hintCount++;
|
||||
} else if (line.match(/\[warning\]/)) {
|
||||
warningCount++;
|
||||
} else {
|
||||
errorCount ++;
|
||||
}
|
||||
if (parsedLine.isHint) {
|
||||
hintCount++;
|
||||
} else if (parsedLine.isWarning) {
|
||||
warningCount++;
|
||||
} else {
|
||||
errorCount ++;
|
||||
}
|
||||
console.log(dirName + ':' + line);
|
||||
console.log(dirName + ':' + parsedLine);
|
||||
});
|
||||
stream.on('close', function() {
|
||||
var error;
|
||||
|
@ -113,3 +110,58 @@ module.exports = function(gulp, plugins, config) {
|
|||
}
|
||||
};
|
||||
};
|
||||
|
||||
// See https://github.com/dart-lang/analyzer_cli/blob/master/lib/src/error_formatter.dart
|
||||
function _AnalyzerOutputLine(result) {
|
||||
this.severity = result[1];
|
||||
this.errorType = result[2];
|
||||
this.errorCode = result[3];
|
||||
this.source = result[4];
|
||||
this.lineNum = result[5];
|
||||
this.colNum = result[6];
|
||||
this.errorMsg = result[7];
|
||||
|
||||
this.isError = Boolean(this.severity.match(/ERROR/i));
|
||||
this.isHint = Boolean(this.severity.match(/INFO/i));
|
||||
this.isWarning = Boolean(this.severity.match(/WARNING/i));
|
||||
}
|
||||
|
||||
_AnalyzerOutputLine.parse = function(line) {
|
||||
var result = _AnalyzerOutputLine._analyzerParseRegExp.exec(line);
|
||||
return result ? new _AnalyzerOutputLine(result) : null;
|
||||
};
|
||||
|
||||
_AnalyzerOutputLine._analyzerParseRegExp = new RegExp(
|
||||
'([^\|]+)\\|' + // #1, severity (NONE, INFO, WARNING, ERROR)
|
||||
'([^\|]+)\\|' + // #2, errorCode.type (HINT, *_WARNING, *_ERROR, etc)
|
||||
'([^\|]+)\\|' + // #3, errorCode (UNUSED_IMPORT, UNUSED_CATCH_STACK, etc)
|
||||
'([^\|]+)\\|' + // #4, source path
|
||||
'([^\|]+)\\|' + // #5, line number
|
||||
'([^\|]+)\\|' + // #6, column number
|
||||
'[^\|]+\\|' + // length of the ASCII line to draw (ignored)
|
||||
'(.*)$'); // #7, error message
|
||||
|
||||
_AnalyzerOutputLine.prototype = {
|
||||
toString: function() {
|
||||
return '[' + this.errorCode + '] ' + this.errorMsg +
|
||||
' (' + this.source + ', line ' + this.lineNum + ', col ' + this.colNum + ')';
|
||||
},
|
||||
|
||||
shouldIgnore: function() {
|
||||
if (this.errorCode.match(/UNUSED_IMPORT/i)) {
|
||||
if (this.source.match(/_analyzer\.dart/)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// TODO: https://github.com/angular/ts2dart/issues/168
|
||||
if (this.errorCode.match(/UNUSED_CATCH_STACK/i)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Don't worry about hints in generated files.
|
||||
if (this.isHint && this.source.match(/generated/i)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -7,15 +7,17 @@ module.exports = function(gulp) {
|
|||
console.log('Dart SDK detected');
|
||||
if (process.platform === 'win32') {
|
||||
DART_SDK = {
|
||||
PUB: 'pub.bat',
|
||||
ANALYZER: 'dartanalyzer.bat',
|
||||
DARTFMT: 'dartfmt.bat'
|
||||
DARTFMT: 'dartfmt.bat',
|
||||
PUB: 'pub.bat',
|
||||
VM: 'dart.bat'
|
||||
};
|
||||
} else {
|
||||
DART_SDK = {
|
||||
PUB: 'pub',
|
||||
ANALYZER: 'dartanalyzer',
|
||||
DARTFMT: 'dartfmt'
|
||||
DARTFMT: 'dartfmt',
|
||||
PUB: 'pub',
|
||||
VM: 'dart'
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
Loading…
Reference in New Issue