Compare commits
48 Commits
2.0.0-beta
...
2.0.0-beta
Author | SHA1 | Date | |
---|---|---|---|
1cebd318e5 | |||
020d01cdf3 | |||
3412aba46e | |||
347e71af7d | |||
d24df799d3 | |||
01e6b8c7ed | |||
60727c4d2b | |||
03627aa84d | |||
83b8f59297 | |||
c6f454f51d | |||
ccff17599a | |||
fb2773b8f3 | |||
5110121f6e | |||
27cf897239 | |||
5d33a12af4 | |||
08b295603c | |||
3b60503d2b | |||
3c2473bac6 | |||
f9426709ef | |||
e1e44a910e | |||
f371c9066d | |||
85c1927993 | |||
a596b887ff | |||
6cbf99086e | |||
26a3390549 | |||
9a1959f77a | |||
226e662cf1 | |||
7a1a1b80ed | |||
529988bc81 | |||
c17dc1c057 | |||
09a95a692e | |||
247964af62 | |||
5e2bc5c593 | |||
28e657d857 | |||
06ad112998 | |||
cfa1d17afe | |||
3ca6df87b8 | |||
e310bee9e2 | |||
4902244cce | |||
8db97b0b7a | |||
9be04f8d38 | |||
74e2bd7e3e | |||
52d3980d02 | |||
4e9809bcb2 | |||
bd8a4215dd | |||
d23b973e7a | |||
0dbf959548 | |||
20812f446f |
@ -64,8 +64,6 @@ matrix:
|
||||
allow_failures:
|
||||
- env: "MODE=saucelabs_optional"
|
||||
- env: "MODE=browserstack_optional"
|
||||
# Tracked in https://github.com/angular/angular/issues/7050
|
||||
- env: "MODE=typescript_next"
|
||||
|
||||
addons:
|
||||
firefox: "38.0"
|
||||
|
61
CHANGELOG.md
61
CHANGELOG.md
@ -1,3 +1,54 @@
|
||||
<a name="2.0.0-beta.15"></a>
|
||||
# 2.0.0-beta.15 (2016-04-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **7837:** MetadataCollector takes no parameters for the constructor. ([c17dc1c](https://github.com/angular/angular/commit/c17dc1c)), closes [#7838](https://github.com/angular/angular/issues/7838)
|
||||
* **7987:** Incremental build works with new trees ([08b2956](https://github.com/angular/angular/commit/08b2956)), closes [#7989](https://github.com/angular/angular/issues/7989)
|
||||
* **build:** ignore dart warnings `The name … is shown, but not used` ([01e6b8c](https://github.com/angular/angular/commit/01e6b8c)), closes [#8045](https://github.com/angular/angular/issues/8045)
|
||||
* **payload:** increase payload size limit temporarily ([28e657d](https://github.com/angular/angular/commit/28e657d))
|
||||
* **RouterLink:** ignore optional parameters when checking for active routes ([5e2bc5c](https://github.com/angular/angular/commit/5e2bc5c)), closes [#6459](https://github.com/angular/angular/issues/6459) [#7834](https://github.com/angular/angular/issues/7834)
|
||||
* **select:** set value individually from ngModel ([e1e44a9](https://github.com/angular/angular/commit/e1e44a9)), closes [#7975](https://github.com/angular/angular/issues/7975) [#7978](https://github.com/angular/angular/issues/7978)
|
||||
* **upgrade:** make upgradeAdapter upgrade angular 1 components correctly ([247964a](https://github.com/angular/angular/commit/247964a)), closes [#7951](https://github.com/angular/angular/issues/7951)
|
||||
|
||||
### Features
|
||||
|
||||
* **compiler:** Add an implementation for XHR that uses a template cache to load template files. ([a596b88](https://github.com/angular/angular/commit/a596b88)), closes [#7940](https://github.com/angular/angular/issues/7940)
|
||||
* **gestures:** allow override of Hammer default configuration ([6cbf990](https://github.com/angular/angular/commit/6cbf990)), closes [#7924](https://github.com/angular/angular/issues/7924)
|
||||
* **ngFor:** Support convenience view local in ngFor ([ccff175](https://github.com/angular/angular/commit/ccff175)), closes [#8013](https://github.com/angular/angular/issues/8013)
|
||||
* **parser:** TemplateParser.tryParse() returns both the AST and errors ([226e662](https://github.com/angular/angular/commit/226e662)), closes [#7858](https://github.com/angular/angular/issues/7858)
|
||||
* **transformers:** changes transformers to collect information about providers and resolve identifi ([3b60503](https://github.com/angular/angular/commit/3b60503))
|
||||
* **transformers:** special case Profiler ([83b8f59](https://github.com/angular/angular/commit/83b8f59))
|
||||
* **typescript:** update to 1.9 nightly. ([3412aba](https://github.com/angular/angular/commit/3412aba)), closes [#8003](https://github.com/angular/angular/issues/8003)
|
||||
|
||||
### Reverts
|
||||
|
||||
* **format): Revert "chore(format:** update to latest formatter" ([60727c4](https://github.com/angular/angular/commit/60727c4))
|
||||
|
||||
|
||||
|
||||
<a name="2.0.0-beta.14"></a>
|
||||
# 2.0.0-beta.14 (2016-04-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **forms:** support both value and ng-value ([8db97b0](https://github.com/angular/angular/commit/8db97b0))
|
||||
* **router:** allow forward slashes in query parameters ([4902244](https://github.com/angular/angular/commit/4902244)), closes [#7824](https://github.com/angular/angular/issues/7824)
|
||||
* **select:** support objects as select values ([74e2bd7](https://github.com/angular/angular/commit/74e2bd7)), closes [#4843](https://github.com/angular/angular/issues/4843) [#7842](https://github.com/angular/angular/issues/7842)
|
||||
* **select:** update name from ng-value to ngValue ([3ca6df8](https://github.com/angular/angular/commit/3ca6df8)), closes [#7939](https://github.com/angular/angular/issues/7939)
|
||||
* **upgrade:** leak when angular1 destroys element ([9be04f8](https://github.com/angular/angular/commit/9be04f8)), closes [#6401](https://github.com/angular/angular/issues/6401) [#7935](https://github.com/angular/angular/issues/7935)
|
||||
|
||||
### Features
|
||||
|
||||
* **dart/transform:** Avoid `print` in transformer code. ([e310bee](https://github.com/angular/angular/commit/e310bee)), closes [#7855](https://github.com/angular/angular/issues/7855)
|
||||
* **static-reflector:** Added StaticReflector ([0dbf959](https://github.com/angular/angular/commit/0dbf959))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="2.0.0-beta.13"></a>
|
||||
# 2.0.0-beta.13 (2016-03-31)
|
||||
|
||||
@ -30,14 +81,20 @@
|
||||
### BREAKING CHANGES
|
||||
|
||||
* For static content projection, elements with *-directives are now matched against the element itself vs the template before.
|
||||
<p *ngIf="condition" foo></p>
|
||||
|
||||
`<p *ngIf="condition" foo></p>`
|
||||
|
||||
Before:
|
||||
```html
|
||||
// Use the implicit template for projection
|
||||
<ng-content select="template"></ng-content>
|
||||
```
|
||||
|
||||
After:
|
||||
```html
|
||||
// Use the actual element for projection
|
||||
<ng-content select="p[foo]"></ng-content>
|
||||
|
||||
```
|
||||
|
||||
<a name="2.0.0-beta.12"></a>
|
||||
# 2.0.0-beta.12 (2016-03-23)
|
||||
|
14
gulpfile.js
14
gulpfile.js
@ -177,7 +177,7 @@ var PAYLOAD_TESTS_CONFIG = {
|
||||
return path.join(__dirname, CONFIG.dest.js.prod.es5, 'payload_tests', caseName,
|
||||
'ts/' + packaging);
|
||||
},
|
||||
systemjs: {sizeLimits: {'uncompressed': 850 * 1024, 'gzip level=9': 165 * 1024}},
|
||||
systemjs: {sizeLimits: {'uncompressed': 870 * 1024, 'gzip level=9': 165 * 1024}},
|
||||
webpack: {sizeLimits: {'uncompressed': 550 * 1024, 'gzip level=9': 120 * 1024}}
|
||||
}
|
||||
};
|
||||
@ -981,9 +981,9 @@ gulp.task('static-checks', ['!build.tools'], function(done) {
|
||||
// distributed in our npm package, and loaded from node_modules by
|
||||
// the typescript compiler.
|
||||
|
||||
// Make sure the two typings tests are isolated, by running this one in a tempdir
|
||||
// Make sure the typings tests are isolated, by running in a tempdir
|
||||
var tmpdir = path.join(os.tmpdir(), 'test.typings', new Date().getTime().toString());
|
||||
gulp.task('!pre.test.typings.layoutNodeModule', ['build.js.cjs'], function() {
|
||||
gulp.task('!pre.test.typings.layoutNodeModule', function() {
|
||||
return gulp.src(['dist/js/cjs/angular2/**/*', 'node_modules/rxjs/**/*'], {base: 'dist/js/cjs'})
|
||||
.pipe(gulp.dest(path.join(tmpdir, 'node_modules')));
|
||||
});
|
||||
@ -1003,7 +1003,7 @@ gulp.task('!pre.test.typings.copyTypingsSpec', function() {
|
||||
return gulp.src(['modules/angular2/examples/**/*.ts']).pipe(gulp.dest(tmpdir));
|
||||
});
|
||||
|
||||
gulp.task('test.typings',
|
||||
gulp.task('!test.typings',
|
||||
[
|
||||
'!pre.test.typings.layoutNodeModule',
|
||||
'!pre.test.typings.copyTypingsSpec',
|
||||
@ -1023,6 +1023,9 @@ gulp.task('test.typings',
|
||||
}));
|
||||
});
|
||||
|
||||
gulp.task('test.typings', ['build.js.cjs'],
|
||||
function(done) { runSequence('!test.typings', sequenceComplete(done)); });
|
||||
|
||||
// -----------------
|
||||
// orchestrated targets
|
||||
|
||||
@ -1203,7 +1206,8 @@ gulp.task('!bundle.js.prod', ['build.js.prod'], function() {
|
||||
// minified production build
|
||||
gulp.task('!bundle.js.min', ['build.js.prod'], function() {
|
||||
var bundler = require('./tools/build/bundle');
|
||||
var bundlerConfig = {sourceMaps: true, minify: true};
|
||||
var bundlerConfig =
|
||||
{sourceMaps: true, minify: true, mangle: false, uglify: {compress: {keep_fnames: true}}};
|
||||
|
||||
return bundler.bundle(bundleConfig, NG2_BUNDLE_CONTENT, './dist/build/angular2.min.js',
|
||||
bundlerConfig)
|
||||
|
@ -1,6 +0,0 @@
|
||||
/**
|
||||
* See {@link bootstrap} for more information.
|
||||
* @deprecated
|
||||
*/
|
||||
export {bootstrap} from 'angular2/platform/browser';
|
||||
export {AngularEntrypoint} from 'angular2/src/core/angular_entrypoint';
|
@ -1,6 +0,0 @@
|
||||
/**
|
||||
* See {@link bootstrap} for more information.
|
||||
* @deprecated
|
||||
*/
|
||||
export {bootstrapStatic} from 'angular2/platform/browser_static';
|
||||
export {AngularEntrypoint} from 'angular2/src/core/angular_entrypoint';
|
@ -4,7 +4,7 @@ Class decorators
|
||||
@description
|
||||
{@target ts}`import {Directive, ...} from 'angular2/core';`{@endtarget}
|
||||
{@target js}Available from the `ng.core` namespace{@endtarget}
|
||||
{@target dart}`import 'package:angular2/angular2.dart';`{@endtarget}
|
||||
{@target dart}`import 'package:angular2/core.dart';`{@endtarget}
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
|
@ -4,7 +4,7 @@ Dependency injection configuration
|
||||
@description
|
||||
{@target ts}`import {provide} from 'angular2/core';`{@endtarget}
|
||||
{@target js}Available from the `ng.core` namespace{@endtarget}
|
||||
{@target dart}`import 'package:angular2/angular2.dart';`{@endtarget}
|
||||
{@target dart}`import 'package:angular2/core.dart';`{@endtarget}
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
|
@ -4,7 +4,7 @@ Class field decorators for directives and components
|
||||
@description
|
||||
{@target ts}`import {Input, ...} from 'angular2/core';`{@endtarget}
|
||||
{@target js}Available from the `ng.core` namespace{@endtarget}
|
||||
{@target dart}`import 'package:angular2/angular2.dart';`{@endtarget}
|
||||
{@target dart}`import 'package:angular2/core.dart';`{@endtarget}
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
|
@ -4,7 +4,7 @@ Routing and navigation
|
||||
@description
|
||||
{@target ts}`import {RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS, ...} from 'angular2/router';`{@endtarget}
|
||||
{@target js}Available from the `ng.router` namespace{@endtarget}
|
||||
{@target dart}`import 'package:angular2/angular2.dart';`{@endtarget}
|
||||
{@target dart}`import 'package:angular2/router.dart';`{@endtarget}
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
|
@ -1,6 +1,7 @@
|
||||
export {AngularEntrypoint} from 'angular2/src/core/angular_entrypoint';
|
||||
export {
|
||||
BROWSER_PROVIDERS,
|
||||
CACHED_TEMPLATE_PROVIDER,
|
||||
ELEMENT_PROBE_PROVIDERS,
|
||||
ELEMENT_PROBE_PROVIDERS_PROD_MODE,
|
||||
inspectNativeElement,
|
||||
|
@ -2,12 +2,15 @@ import {
|
||||
TEST_BROWSER_STATIC_PLATFORM_PROVIDERS,
|
||||
ADDITIONAL_TEST_BROWSER_PROVIDERS
|
||||
} from 'angular2/platform/testing/browser_static';
|
||||
|
||||
import {BROWSER_APP_PROVIDERS} from 'angular2/platform/browser';
|
||||
|
||||
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
|
||||
/**
|
||||
* Providers for using template cache to avoid actual XHR.
|
||||
* Re-exported here so that tests import from a single place.
|
||||
*/
|
||||
export {CACHED_TEMPLATE_PROVIDER} from 'angular2/platform/browser';
|
||||
|
||||
/**
|
||||
* Default patform providers for testing.
|
||||
*/
|
||||
|
@ -26,6 +26,8 @@ import {BaseException} from "../../facade/exceptions";
|
||||
* `NgFor` provides several exported values that can be aliased to local variables:
|
||||
*
|
||||
* * `index` will be set to the current loop iteration for each template context.
|
||||
* * `first` will be set to a boolean value indicating whether the item is the first one in the
|
||||
* iteration.
|
||||
* * `last` will be set to a boolean value indicating whether the item is the last one in the
|
||||
* iteration.
|
||||
* * `even` will be set to a boolean value indicating whether this item has an even index.
|
||||
@ -69,6 +71,7 @@ import {BaseException} from "../../facade/exceptions";
|
||||
export class NgFor implements DoCheck {
|
||||
/** @internal */
|
||||
_ngForOf: any;
|
||||
/** @internal */
|
||||
_ngForTrackBy: TrackByFn;
|
||||
private _differ: IterableDiffer;
|
||||
|
||||
@ -125,6 +128,7 @@ export class NgFor implements DoCheck {
|
||||
|
||||
for (var i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
|
||||
var viewRef = <EmbeddedViewRef>this._viewContainer.get(i);
|
||||
viewRef.setLocal('first', i === 0);
|
||||
viewRef.setLocal('last', i === ilen - 1);
|
||||
}
|
||||
|
||||
|
@ -74,6 +74,7 @@ export abstract class NgLocalization { abstract getPluralCategory(value: any): s
|
||||
|
||||
@Directive({selector: '[ngPluralCase]'})
|
||||
export class NgPluralCase {
|
||||
/** @internal */
|
||||
_view: SwitchView;
|
||||
constructor(@Attribute('ngPluralCase') public value: string, template: TemplateRef,
|
||||
viewContainer: ViewContainerRef) {
|
||||
|
@ -52,8 +52,7 @@ const controlGroupProvider =
|
||||
* <pre>{{valueOf(f)}}</pre>
|
||||
* </form>
|
||||
* </div>
|
||||
* `,
|
||||
* directives: [FORM_DIRECTIVES]
|
||||
* `
|
||||
* })
|
||||
* export class App {
|
||||
* valueOf(cg: NgControlGroup): string {
|
||||
|
@ -88,9 +88,12 @@ export class RadioButtonState {
|
||||
})
|
||||
export class RadioControlValueAccessor implements ControlValueAccessor,
|
||||
OnDestroy, OnInit {
|
||||
/** @internal */
|
||||
_state: RadioButtonState;
|
||||
/** @internal */
|
||||
_control: NgControl;
|
||||
@Input() name: string;
|
||||
/** @internal */
|
||||
_fn: Function;
|
||||
onChange = () => {};
|
||||
onTouched = () => {};
|
||||
|
@ -1,21 +1,88 @@
|
||||
import {
|
||||
Query,
|
||||
Directive,
|
||||
Renderer,
|
||||
Self,
|
||||
forwardRef,
|
||||
Provider,
|
||||
ElementRef,
|
||||
QueryList
|
||||
Input,
|
||||
Host,
|
||||
OnDestroy,
|
||||
Optional
|
||||
} from 'angular2/core';
|
||||
|
||||
import {ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {NG_VALUE_ACCESSOR, ControlValueAccessor} from './control_value_accessor';
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {
|
||||
CONST_EXPR,
|
||||
StringWrapper,
|
||||
isPrimitive,
|
||||
isPresent,
|
||||
isBlank,
|
||||
looseIdentical
|
||||
} from 'angular2/src/facade/lang';
|
||||
|
||||
import {MapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
const SELECT_VALUE_ACCESSOR = CONST_EXPR(new Provider(
|
||||
NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => SelectControlValueAccessor), multi: true}));
|
||||
|
||||
function _buildValueString(id: string, value: any): string {
|
||||
if (isBlank(id)) return `${value}`;
|
||||
if (!isPrimitive(value)) value = "Object";
|
||||
return StringWrapper.slice(`${id}: ${value}`, 0, 50);
|
||||
}
|
||||
|
||||
function _extractId(valueString: string): string {
|
||||
return valueString.split(":")[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* The accessor for writing a value and listening to changes on a select element.
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'select[ngControl],select[ngFormControl],select[ngModel]',
|
||||
host: {'(input)': 'onChange($event.target.value)', '(blur)': 'onTouched()'},
|
||||
providers: [SELECT_VALUE_ACCESSOR]
|
||||
})
|
||||
export class SelectControlValueAccessor implements ControlValueAccessor {
|
||||
value: any;
|
||||
/** @internal */
|
||||
_optionMap: Map<string, any> = new Map<string, any>();
|
||||
/** @internal */
|
||||
_idCounter: number = 0;
|
||||
|
||||
onChange = (_: any) => {};
|
||||
onTouched = () => {};
|
||||
|
||||
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
|
||||
|
||||
writeValue(value: any): void {
|
||||
this.value = value;
|
||||
var valueString = _buildValueString(this._getOptionId(value), value);
|
||||
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', valueString);
|
||||
}
|
||||
|
||||
registerOnChange(fn: (value: any) => any): void {
|
||||
this.onChange = (valueString: string) => { fn(this._getOptionValue(valueString)); };
|
||||
}
|
||||
registerOnTouched(fn: () => any): void { this.onTouched = fn; }
|
||||
|
||||
/** @internal */
|
||||
_registerOption(): string { return (this._idCounter++).toString(); }
|
||||
|
||||
/** @internal */
|
||||
_getOptionId(value: any): string {
|
||||
for (let id of MapWrapper.keys(this._optionMap)) {
|
||||
if (looseIdentical(this._optionMap.get(id), value)) return id;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_getOptionValue(valueString: string): any {
|
||||
let value = this._optionMap.get(_extractId(valueString));
|
||||
return isPresent(value) ? value : valueString;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks `<option>` as dynamic, so Angular can be notified when options change.
|
||||
*
|
||||
@ -28,36 +95,37 @@ const SELECT_VALUE_ACCESSOR = CONST_EXPR(new Provider(
|
||||
* ```
|
||||
*/
|
||||
@Directive({selector: 'option'})
|
||||
export class NgSelectOption {
|
||||
}
|
||||
export class NgSelectOption implements OnDestroy {
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The accessor for writing a value and listening to changes on a select element.
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'select[ngControl],select[ngFormControl],select[ngModel]',
|
||||
host: {'(input)': 'onChange($event.target.value)', '(blur)': 'onTouched()'},
|
||||
bindings: [SELECT_VALUE_ACCESSOR]
|
||||
})
|
||||
export class SelectControlValueAccessor implements ControlValueAccessor {
|
||||
value: string;
|
||||
onChange = (_: any) => {};
|
||||
onTouched = () => {};
|
||||
|
||||
constructor(private _renderer: Renderer, private _elementRef: ElementRef,
|
||||
@Query(NgSelectOption, {descendants: true}) query: QueryList<NgSelectOption>) {
|
||||
this._updateValueWhenListOfOptionsChanges(query);
|
||||
constructor(private _element: ElementRef, private _renderer: Renderer,
|
||||
@Optional() @Host() private _select: SelectControlValueAccessor) {
|
||||
if (isPresent(this._select)) this.id = this._select._registerOption();
|
||||
}
|
||||
|
||||
writeValue(value: any): void {
|
||||
this.value = value;
|
||||
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', value);
|
||||
@Input('ngValue')
|
||||
set ngValue(value: any) {
|
||||
if (this._select == null) return;
|
||||
this._select._optionMap.set(this.id, value);
|
||||
this._setElementValue(_buildValueString(this.id, value));
|
||||
this._select.writeValue(this._select.value);
|
||||
}
|
||||
|
||||
registerOnChange(fn: () => any): void { this.onChange = fn; }
|
||||
registerOnTouched(fn: () => any): void { this.onTouched = fn; }
|
||||
@Input('value')
|
||||
set value(value: any) {
|
||||
this._setElementValue(value);
|
||||
if (isPresent(this._select)) this._select.writeValue(this._select.value);
|
||||
}
|
||||
|
||||
private _updateValueWhenListOfOptionsChanges(query: QueryList<NgSelectOption>) {
|
||||
ObservableWrapper.subscribe(query.changes, (_) => this.writeValue(this.value));
|
||||
/** @internal */
|
||||
_setElementValue(value: string): void {
|
||||
this._renderer.setElementProperty(this._element.nativeElement, 'value', value);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (isPresent(this._select)) {
|
||||
this._select._optionMap.delete(this.id);
|
||||
this._select.writeValue(this._select.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,8 +26,10 @@ import {NumberWrapper} from "angular2/src/facade/lang";
|
||||
*/
|
||||
export interface Validator { validate(c: modelModule.AbstractControl): {[key: string]: any}; }
|
||||
|
||||
const REQUIRED = Validators.required;
|
||||
|
||||
const REQUIRED_VALIDATOR =
|
||||
CONST_EXPR(new Provider(NG_VALIDATORS, {useValue: Validators.required, multi: true}));
|
||||
CONST_EXPR(new Provider(NG_VALIDATORS, {useValue: REQUIRED, multi: true}));
|
||||
|
||||
/**
|
||||
* A Directive that adds the `required` validator to any controls marked with the
|
||||
|
@ -94,12 +94,14 @@ export class LexedCssResult {
|
||||
constructor(public error: CssScannerError, public token: CssToken) {}
|
||||
}
|
||||
|
||||
export function generateErrorMessage(input, message, errorValue, index, row, column) {
|
||||
export function generateErrorMessage(input: string, message: string, errorValue: string,
|
||||
index: number, row: number, column: number): string {
|
||||
return `${message} at column ${row}:${column} in expression [` +
|
||||
findProblemCode(input, errorValue, index, column) + ']';
|
||||
}
|
||||
|
||||
export function findProblemCode(input, errorValue, index, column) {
|
||||
export function findProblemCode(input: string, errorValue: string, index: number,
|
||||
column: number): string {
|
||||
var endOfProblemLine = index;
|
||||
var current = charCode(input, index);
|
||||
while (current > 0 && !isNewline(current)) {
|
||||
@ -163,7 +165,9 @@ export class CssScanner {
|
||||
column: number = -1;
|
||||
line: number = 0;
|
||||
|
||||
/** @internal */
|
||||
_currentMode: CssLexerMode = CssLexerMode.BLOCK;
|
||||
/** @internal */
|
||||
_currentError: CssScannerError = null;
|
||||
|
||||
constructor(public input: string, private _trackComments: boolean = false) {
|
||||
@ -196,7 +200,7 @@ export class CssScanner {
|
||||
this.peekPeek = this.peekAt(this.index + 1);
|
||||
}
|
||||
|
||||
peekAt(index): number {
|
||||
peekAt(index: number): number {
|
||||
return index >= this.length ? $EOF : StringWrapper.charCodeAt(this.input, index);
|
||||
}
|
||||
|
||||
@ -295,6 +299,7 @@ export class CssScanner {
|
||||
return new LexedCssResult(error, token);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_scan(): CssToken {
|
||||
var peek = this.peek;
|
||||
var peekPeek = this.peekPeek;
|
||||
@ -348,7 +353,7 @@ export class CssScanner {
|
||||
return this.error(`Unexpected character [${StringWrapper.fromCharCode(peek)}]`);
|
||||
}
|
||||
|
||||
scanComment() {
|
||||
scanComment(): CssToken {
|
||||
if (this.assertCondition(isCommentStart(this.peek, this.peekPeek),
|
||||
"Expected comment start value")) {
|
||||
return null;
|
||||
@ -375,7 +380,7 @@ export class CssScanner {
|
||||
return new CssToken(start, startingColumn, startingLine, CssTokenType.Comment, str);
|
||||
}
|
||||
|
||||
scanWhitespace() {
|
||||
scanWhitespace(): CssToken {
|
||||
var start = this.index;
|
||||
var startingColumn = this.column;
|
||||
var startingLine = this.line;
|
||||
@ -386,7 +391,7 @@ export class CssScanner {
|
||||
return new CssToken(start, startingColumn, startingLine, CssTokenType.Whitespace, str);
|
||||
}
|
||||
|
||||
scanString() {
|
||||
scanString(): CssToken {
|
||||
if (this.assertCondition(isStringStart(this.peek, this.peekPeek),
|
||||
"Unexpected non-string starting value")) {
|
||||
return null;
|
||||
@ -416,7 +421,7 @@ export class CssScanner {
|
||||
return new CssToken(start, startingColumn, startingLine, CssTokenType.String, str);
|
||||
}
|
||||
|
||||
scanNumber() {
|
||||
scanNumber(): CssToken {
|
||||
var start = this.index;
|
||||
var startingColumn = this.column;
|
||||
if (this.peek == $PLUS || this.peek == $MINUS) {
|
||||
@ -436,7 +441,7 @@ export class CssScanner {
|
||||
return new CssToken(start, startingColumn, this.line, CssTokenType.Number, strValue);
|
||||
}
|
||||
|
||||
scanIdentifier() {
|
||||
scanIdentifier(): CssToken {
|
||||
if (this.assertCondition(isIdentifierStart(this.peek, this.peekPeek),
|
||||
'Expected identifier starting value')) {
|
||||
return null;
|
||||
@ -451,7 +456,7 @@ export class CssScanner {
|
||||
return new CssToken(start, startingColumn, this.line, CssTokenType.Identifier, strValue);
|
||||
}
|
||||
|
||||
scanCssValueFunction() {
|
||||
scanCssValueFunction(): CssToken {
|
||||
var start = this.index;
|
||||
var startingColumn = this.column;
|
||||
while (this.peek != $EOF && this.peek != $RPAREN) {
|
||||
@ -461,7 +466,7 @@ export class CssScanner {
|
||||
return new CssToken(start, startingColumn, this.line, CssTokenType.Identifier, strValue);
|
||||
}
|
||||
|
||||
scanCharacter() {
|
||||
scanCharacter(): CssToken {
|
||||
var start = this.index;
|
||||
var startingColumn = this.column;
|
||||
if (this.assertCondition(isValidCssCharacter(this.peek, this._currentMode),
|
||||
@ -475,7 +480,7 @@ export class CssScanner {
|
||||
return new CssToken(start, startingColumn, this.line, CssTokenType.Character, c);
|
||||
}
|
||||
|
||||
scanAtExpression() {
|
||||
scanAtExpression(): CssToken {
|
||||
if (this.assertCondition(this.peek == $AT, 'Expected @ value')) {
|
||||
return null;
|
||||
}
|
||||
@ -521,7 +526,7 @@ function isAtKeyword(current: CssToken, next: CssToken): boolean {
|
||||
return current.numValue == $AT && next.type == CssTokenType.Identifier;
|
||||
}
|
||||
|
||||
function isCharMatch(target: number, previous: number, code: number) {
|
||||
function isCharMatch(target: number, previous: number, code: number): boolean {
|
||||
return code == target && previous != $BACKSLASH;
|
||||
}
|
||||
|
||||
@ -529,11 +534,11 @@ function isDigit(code: number): boolean {
|
||||
return $0 <= code && code <= $9;
|
||||
}
|
||||
|
||||
function isCommentStart(code: number, next: number) {
|
||||
function isCommentStart(code: number, next: number): boolean {
|
||||
return code == $SLASH && next == $STAR;
|
||||
}
|
||||
|
||||
function isCommentEnd(code: number, next: number) {
|
||||
function isCommentEnd(code: number, next: number): boolean {
|
||||
return code == $STAR && next == $SLASH;
|
||||
}
|
||||
|
||||
@ -555,12 +560,12 @@ function isIdentifierStart(code: number, next: number): boolean {
|
||||
target == $MINUS || target == $_;
|
||||
}
|
||||
|
||||
function isIdentifierPart(target: number) {
|
||||
function isIdentifierPart(target: number): boolean {
|
||||
return ($a <= target && target <= $z) || ($A <= target && target <= $Z) || target == $BACKSLASH ||
|
||||
target == $MINUS || target == $_ || isDigit(target);
|
||||
}
|
||||
|
||||
function isValidPseudoSelectorCharacter(code: number) {
|
||||
function isValidPseudoSelectorCharacter(code: number): boolean {
|
||||
switch (code) {
|
||||
case $LPAREN:
|
||||
case $RPAREN:
|
||||
@ -570,11 +575,11 @@ function isValidPseudoSelectorCharacter(code: number) {
|
||||
}
|
||||
}
|
||||
|
||||
function isValidKeyframeBlockCharacter(code: number) {
|
||||
function isValidKeyframeBlockCharacter(code: number): boolean {
|
||||
return code == $PERCENT;
|
||||
}
|
||||
|
||||
function isValidAttributeSelectorCharacter(code: number) {
|
||||
function isValidAttributeSelectorCharacter(code: number): boolean {
|
||||
// value^*|$~=something
|
||||
switch (code) {
|
||||
case $$:
|
||||
@ -589,7 +594,7 @@ function isValidAttributeSelectorCharacter(code: number) {
|
||||
}
|
||||
}
|
||||
|
||||
function isValidSelectorCharacter(code: number) {
|
||||
function isValidSelectorCharacter(code: number): boolean {
|
||||
// selector [ key = value ]
|
||||
// IDENT C IDENT C IDENT C
|
||||
// #id, .class, *+~>
|
||||
@ -610,7 +615,7 @@ function isValidSelectorCharacter(code: number) {
|
||||
}
|
||||
}
|
||||
|
||||
function isValidStyleBlockCharacter(code: number) {
|
||||
function isValidStyleBlockCharacter(code: number): boolean {
|
||||
// key:value;
|
||||
// key:calc(something ... )
|
||||
switch (code) {
|
||||
@ -630,7 +635,7 @@ function isValidStyleBlockCharacter(code: number) {
|
||||
}
|
||||
}
|
||||
|
||||
function isValidMediaQueryRuleCharacter(code: number) {
|
||||
function isValidMediaQueryRuleCharacter(code: number): boolean {
|
||||
// (min-width: 7.5em) and (orientation: landscape)
|
||||
switch (code) {
|
||||
case $LPAREN:
|
||||
@ -644,7 +649,7 @@ function isValidMediaQueryRuleCharacter(code: number) {
|
||||
}
|
||||
}
|
||||
|
||||
function isValidAtRuleCharacter(code: number) {
|
||||
function isValidAtRuleCharacter(code: number): boolean {
|
||||
// @document url(http://www.w3.org/page?something=on#hash),
|
||||
switch (code) {
|
||||
case $LPAREN:
|
||||
@ -668,7 +673,7 @@ function isValidAtRuleCharacter(code: number) {
|
||||
}
|
||||
}
|
||||
|
||||
function isValidStyleFunctionCharacter(code: number) {
|
||||
function isValidStyleFunctionCharacter(code: number): boolean {
|
||||
switch (code) {
|
||||
case $PERIOD:
|
||||
case $MINUS:
|
||||
@ -684,7 +689,7 @@ function isValidStyleFunctionCharacter(code: number) {
|
||||
}
|
||||
}
|
||||
|
||||
function isValidBlockCharacter(code: number) {
|
||||
function isValidBlockCharacter(code: number): boolean {
|
||||
// @something { }
|
||||
// IDENT
|
||||
return code == $AT;
|
||||
|
@ -95,7 +95,7 @@ function getDelimFromCharacter(code: number): number {
|
||||
}
|
||||
}
|
||||
|
||||
function characterContainsDelimiter(code: number, delimiters: number) {
|
||||
function characterContainsDelimiter(code: number, delimiters: number): boolean {
|
||||
return bitWiseAnd([getDelimFromCharacter(code), delimiters]) > 0;
|
||||
}
|
||||
|
||||
@ -129,6 +129,7 @@ export class CssParser {
|
||||
this._file = new ParseSourceFile(this._scanner.input, _fileName);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_resolveBlockType(token: CssToken): BlockType {
|
||||
switch (token.strValue) {
|
||||
case '@-o-keyframes':
|
||||
@ -179,6 +180,7 @@ export class CssParser {
|
||||
return new ParsedCssResult(errors, ast);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_parseStyleSheet(delimiters): CssStyleSheetAST {
|
||||
var results = [];
|
||||
this._scanner.consumeEmptyStatements();
|
||||
@ -189,6 +191,7 @@ export class CssParser {
|
||||
return new CssStyleSheetAST(results);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_parseRule(delimiters: number): CssRuleAST {
|
||||
if (this._scanner.peek == $AT) {
|
||||
return this._parseAtRule(delimiters);
|
||||
@ -196,6 +199,7 @@ export class CssParser {
|
||||
return this._parseSelectorRule(delimiters);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_parseAtRule(delimiters: number): CssRuleAST {
|
||||
this._scanner.setMode(CssLexerMode.BLOCK);
|
||||
|
||||
@ -259,6 +263,7 @@ export class CssParser {
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_parseSelectorRule(delimiters: number): CssSelectorRuleAST {
|
||||
var selectors = this._parseSelectors(delimiters);
|
||||
var block = this._parseStyleBlock(delimiters);
|
||||
@ -267,6 +272,7 @@ export class CssParser {
|
||||
return new CssSelectorRuleAST(selectors, block);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_parseSelectors(delimiters: number): CssSelectorAST[] {
|
||||
delimiters = bitWiseOr([delimiters, LBRACE_DELIM]);
|
||||
|
||||
@ -286,6 +292,7 @@ export class CssParser {
|
||||
return selectors;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_scan(): CssToken {
|
||||
var output = this._scanner.scan();
|
||||
var token = output.token;
|
||||
@ -296,6 +303,7 @@ export class CssParser {
|
||||
return token;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_consume(type: CssTokenType, value: string = null): CssToken {
|
||||
var output = this._scanner.consume(type, value);
|
||||
var token = output.token;
|
||||
@ -306,6 +314,7 @@ export class CssParser {
|
||||
return token;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_parseKeyframeBlock(delimiters: number): CssBlockAST {
|
||||
delimiters = bitWiseOr([delimiters, RBRACE_DELIM]);
|
||||
this._scanner.setMode(CssLexerMode.KEYFRAME_BLOCK);
|
||||
@ -322,6 +331,7 @@ export class CssParser {
|
||||
return new CssBlockAST(definitions);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_parseKeyframeDefinition(delimiters: number): CssKeyframeDefinitionAST {
|
||||
var stepTokens = [];
|
||||
delimiters = bitWiseOr([delimiters, LBRACE_DELIM]);
|
||||
@ -336,11 +346,13 @@ export class CssParser {
|
||||
return new CssKeyframeDefinitionAST(stepTokens, styles);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_parseKeyframeLabel(delimiters: number): CssToken {
|
||||
this._scanner.setMode(CssLexerMode.KEYFRAME_BLOCK);
|
||||
return mergeTokens(this._collectUntilDelim(delimiters));
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_parseSelector(delimiters: number): CssSelectorAST {
|
||||
delimiters = bitWiseOr([delimiters, COMMA_DELIM, LBRACE_DELIM]);
|
||||
this._scanner.setMode(CssLexerMode.SELECTOR);
|
||||
@ -435,6 +447,7 @@ export class CssParser {
|
||||
return new CssSelectorAST(selectorCssTokens, isComplex);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_parseValue(delimiters: number): CssStyleValueAST {
|
||||
delimiters = bitWiseOr([delimiters, RBRACE_DELIM, SEMICOLON_DELIM, NEWLINE_DELIM]);
|
||||
|
||||
@ -489,6 +502,7 @@ export class CssParser {
|
||||
return new CssStyleValueAST(tokens, strValue);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_collectUntilDelim(delimiters: number, assertType: CssTokenType = null): CssToken[] {
|
||||
var tokens = [];
|
||||
while (!characterContainsDelimiter(this._scanner.peek, delimiters)) {
|
||||
@ -498,6 +512,7 @@ export class CssParser {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_parseBlock(delimiters: number): CssBlockAST {
|
||||
delimiters = bitWiseOr([delimiters, RBRACE_DELIM]);
|
||||
|
||||
@ -519,6 +534,7 @@ export class CssParser {
|
||||
return new CssBlockAST(results);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_parseStyleBlock(delimiters: number): CssBlockAST {
|
||||
delimiters = bitWiseOr([delimiters, RBRACE_DELIM, LBRACE_DELIM]);
|
||||
|
||||
@ -541,6 +557,7 @@ export class CssParser {
|
||||
return new CssBlockAST(definitions);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_parseDefinition(delimiters: number): CssDefinitionAST {
|
||||
this._scanner.setMode(CssLexerMode.STYLE_BLOCK);
|
||||
|
||||
@ -600,6 +617,7 @@ export class CssParser {
|
||||
return new CssDefinitionAST(prop, value);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_assertCondition(status: boolean, errorMessage: string, problemToken: CssToken): boolean {
|
||||
if (!status) {
|
||||
this._error(errorMessage, problemToken);
|
||||
@ -608,6 +626,7 @@ export class CssParser {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_error(message: string, problemToken: CssToken) {
|
||||
var length = problemToken.strValue.length;
|
||||
var error = CssParseError.create(this._file, 0, problemToken.line, problemToken.column, length,
|
||||
|
@ -1,13 +1,16 @@
|
||||
import {
|
||||
isPresent,
|
||||
isBlank,
|
||||
isNumber,
|
||||
isBoolean,
|
||||
normalizeBool,
|
||||
normalizeBlank,
|
||||
serializeEnum,
|
||||
Type,
|
||||
isString,
|
||||
RegExpWrapper,
|
||||
StringWrapper
|
||||
StringWrapper,
|
||||
isArray
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
@ -25,20 +28,12 @@ import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/linker/i
|
||||
var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g;
|
||||
|
||||
export abstract class CompileMetadataWithIdentifier {
|
||||
static fromJson(data: {[key: string]: any}): CompileMetadataWithIdentifier {
|
||||
return _COMPILE_METADATA_FROM_JSON[data['class']](data);
|
||||
}
|
||||
|
||||
abstract toJson(): {[key: string]: any};
|
||||
|
||||
get identifier(): CompileIdentifierMetadata { return <CompileIdentifierMetadata>unimplemented(); }
|
||||
}
|
||||
|
||||
export abstract class CompileMetadataWithType extends CompileMetadataWithIdentifier {
|
||||
static fromJson(data: {[key: string]: any}): CompileMetadataWithType {
|
||||
return _COMPILE_METADATA_FROM_JSON[data['class']](data);
|
||||
}
|
||||
|
||||
abstract toJson(): {[key: string]: any};
|
||||
|
||||
get type(): CompileTypeMetadata { return <CompileTypeMetadata>unimplemented(); }
|
||||
@ -46,43 +41,56 @@ export abstract class CompileMetadataWithType extends CompileMetadataWithIdentif
|
||||
get identifier(): CompileIdentifierMetadata { return <CompileIdentifierMetadata>unimplemented(); }
|
||||
}
|
||||
|
||||
export function metadataFromJson(data: {[key: string]: any}): any {
|
||||
return _COMPILE_METADATA_FROM_JSON[data['class']](data);
|
||||
}
|
||||
|
||||
export class CompileIdentifierMetadata implements CompileMetadataWithIdentifier {
|
||||
runtime: any;
|
||||
name: string;
|
||||
prefix: string;
|
||||
moduleUrl: string;
|
||||
constConstructor: boolean;
|
||||
constructor({runtime, name, moduleUrl, prefix, constConstructor}: {
|
||||
value: any;
|
||||
|
||||
constructor({runtime, name, moduleUrl, prefix, constConstructor, value}: {
|
||||
runtime?: any,
|
||||
name?: string,
|
||||
moduleUrl?: string,
|
||||
prefix?: string,
|
||||
constConstructor?: boolean
|
||||
constConstructor?: boolean,
|
||||
value?: any
|
||||
} = {}) {
|
||||
this.runtime = runtime;
|
||||
this.name = name;
|
||||
this.prefix = prefix;
|
||||
this.moduleUrl = moduleUrl;
|
||||
this.constConstructor = constConstructor;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
static fromJson(data: {[key: string]: any}): CompileIdentifierMetadata {
|
||||
let value = isArray(data['value']) ? arrayFromJson(data['value'], metadataFromJson) :
|
||||
objFromJson(data['value'], metadataFromJson);
|
||||
return new CompileIdentifierMetadata({
|
||||
name: data['name'],
|
||||
prefix: data['prefix'],
|
||||
moduleUrl: data['moduleUrl'],
|
||||
constConstructor: data['constConstructor']
|
||||
constConstructor: data['constConstructor'],
|
||||
value: value
|
||||
});
|
||||
}
|
||||
|
||||
toJson(): {[key: string]: any} {
|
||||
let value = isArray(this.value) ? arrayToJson(this.value) : objToJson(this.value);
|
||||
return {
|
||||
// Note: Runtime type can't be serialized...
|
||||
'class': 'Identifier',
|
||||
'name': this.name,
|
||||
'moduleUrl': this.moduleUrl,
|
||||
'prefix': this.prefix,
|
||||
'constConstructor': this.constConstructor
|
||||
'constConstructor': this.constConstructor,
|
||||
'value': value
|
||||
};
|
||||
}
|
||||
|
||||
@ -177,44 +185,78 @@ export class CompileProviderMetadata {
|
||||
static fromJson(data: {[key: string]: any}): CompileProviderMetadata {
|
||||
return new CompileProviderMetadata({
|
||||
token: objFromJson(data['token'], CompileIdentifierMetadata.fromJson),
|
||||
useClass: objFromJson(data['useClass'], CompileTypeMetadata.fromJson)
|
||||
useClass: objFromJson(data['useClass'], CompileTypeMetadata.fromJson),
|
||||
useExisting: objFromJson(data['useExisting'], CompileIdentifierMetadata.fromJson),
|
||||
useValue: objFromJson(data['useValue'], CompileIdentifierMetadata.fromJson),
|
||||
useFactory: objFromJson(data['useFactory'], CompileFactoryMetadata.fromJson)
|
||||
});
|
||||
}
|
||||
|
||||
toJson(): {[key: string]: any} {
|
||||
return {
|
||||
// Note: Runtime type can't be serialized...
|
||||
'class': 'Provider',
|
||||
'token': objToJson(this.token),
|
||||
'useClass': objToJson(this.useClass)
|
||||
'useClass': objToJson(this.useClass),
|
||||
'useExisting': objToJson(this.useExisting),
|
||||
'useValue': objToJson(this.useValue),
|
||||
'useFactory': objToJson(this.useFactory)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class CompileFactoryMetadata implements CompileIdentifierMetadata {
|
||||
export class CompileFactoryMetadata implements CompileIdentifierMetadata,
|
||||
CompileMetadataWithIdentifier {
|
||||
runtime: Function;
|
||||
name: string;
|
||||
prefix: string;
|
||||
moduleUrl: string;
|
||||
constConstructor: boolean;
|
||||
value: any;
|
||||
diDeps: CompileDiDependencyMetadata[];
|
||||
|
||||
constructor({runtime, name, moduleUrl, constConstructor, diDeps}: {
|
||||
constructor({runtime, name, moduleUrl, prefix, constConstructor, diDeps, value}: {
|
||||
runtime?: Function,
|
||||
name?: string,
|
||||
prefix?: string,
|
||||
moduleUrl?: string,
|
||||
constConstructor?: boolean,
|
||||
value?: boolean,
|
||||
diDeps?: CompileDiDependencyMetadata[]
|
||||
}) {
|
||||
this.runtime = runtime;
|
||||
this.name = name;
|
||||
this.prefix = prefix;
|
||||
this.moduleUrl = moduleUrl;
|
||||
this.diDeps = diDeps;
|
||||
this.constConstructor = constConstructor;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
get identifier(): CompileIdentifierMetadata { return this; }
|
||||
|
||||
toJson() { return null; }
|
||||
static fromJson(data: {[key: string]: any}): CompileFactoryMetadata {
|
||||
return new CompileFactoryMetadata({
|
||||
name: data['name'],
|
||||
prefix: data['prefix'],
|
||||
moduleUrl: data['moduleUrl'],
|
||||
constConstructor: data['constConstructor'],
|
||||
value: data['value'],
|
||||
diDeps: arrayFromJson(data['diDeps'], CompileDiDependencyMetadata.fromJson)
|
||||
});
|
||||
}
|
||||
|
||||
toJson(): {[key: string]: any} {
|
||||
return {
|
||||
'class': 'Factory',
|
||||
'name': this.name,
|
||||
'prefix': this.prefix,
|
||||
'moduleUrl': this.moduleUrl,
|
||||
'constConstructor': this.constConstructor,
|
||||
'value': this.value,
|
||||
'diDeps': arrayToJson(this.diDeps)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -227,15 +269,17 @@ export class CompileTypeMetadata implements CompileIdentifierMetadata, CompileMe
|
||||
moduleUrl: string;
|
||||
isHost: boolean;
|
||||
constConstructor: boolean;
|
||||
value: any;
|
||||
diDeps: CompileDiDependencyMetadata[];
|
||||
|
||||
constructor({runtime, name, moduleUrl, prefix, isHost, constConstructor, diDeps}: {
|
||||
constructor({runtime, name, moduleUrl, prefix, isHost, constConstructor, value, diDeps}: {
|
||||
runtime?: Type,
|
||||
name?: string,
|
||||
moduleUrl?: string,
|
||||
prefix?: string,
|
||||
isHost?: boolean,
|
||||
constConstructor?: boolean,
|
||||
value?: any,
|
||||
diDeps?: CompileDiDependencyMetadata[]
|
||||
} = {}) {
|
||||
this.runtime = runtime;
|
||||
@ -244,6 +288,7 @@ export class CompileTypeMetadata implements CompileIdentifierMetadata, CompileMe
|
||||
this.prefix = prefix;
|
||||
this.isHost = normalizeBool(isHost);
|
||||
this.constConstructor = constConstructor;
|
||||
this.value = value;
|
||||
this.diDeps = normalizeBlank(diDeps);
|
||||
}
|
||||
|
||||
@ -254,6 +299,7 @@ export class CompileTypeMetadata implements CompileIdentifierMetadata, CompileMe
|
||||
prefix: data['prefix'],
|
||||
isHost: data['isHost'],
|
||||
constConstructor: data['constConstructor'],
|
||||
value: data['value'],
|
||||
diDeps: arrayFromJson(data['diDeps'], CompileDiDependencyMetadata.fromJson)
|
||||
});
|
||||
}
|
||||
@ -270,6 +316,7 @@ export class CompileTypeMetadata implements CompileIdentifierMetadata, CompileMe
|
||||
'prefix': this.prefix,
|
||||
'isHost': this.isHost,
|
||||
'constConstructor': this.constConstructor,
|
||||
'value': this.value,
|
||||
'diDeps': arrayToJson(this.diDeps)
|
||||
};
|
||||
}
|
||||
@ -382,8 +429,10 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
|
||||
outputs?: string[],
|
||||
host?: {[key: string]: string},
|
||||
lifecycleHooks?: LifecycleHooks[],
|
||||
providers?: Array<CompileProviderMetadata | CompileTypeMetadata | any[]>,
|
||||
viewProviders?: Array<CompileProviderMetadata | CompileTypeMetadata | any[]>,
|
||||
providers?:
|
||||
Array<CompileProviderMetadata | CompileTypeMetadata | CompileIdentifierMetadata | any[]>,
|
||||
viewProviders?:
|
||||
Array<CompileProviderMetadata | CompileTypeMetadata | CompileIdentifierMetadata | any[]>,
|
||||
queries?: CompileQueryMetadata[],
|
||||
viewQueries?: CompileQueryMetadata[],
|
||||
template?: CompileTemplateMetadata
|
||||
@ -474,8 +523,10 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
|
||||
hostProperties?: {[key: string]: string},
|
||||
hostAttributes?: {[key: string]: string},
|
||||
lifecycleHooks?: LifecycleHooks[],
|
||||
providers?: Array<CompileProviderMetadata | CompileTypeMetadata | any[]>,
|
||||
viewProviders?: Array<CompileProviderMetadata | CompileTypeMetadata | any[]>,
|
||||
providers?:
|
||||
Array<CompileProviderMetadata | CompileTypeMetadata | CompileIdentifierMetadata | any[]>,
|
||||
viewProviders?:
|
||||
Array<CompileProviderMetadata | CompileTypeMetadata | CompileIdentifierMetadata | any[]>,
|
||||
queries?: CompileQueryMetadata[],
|
||||
viewQueries?: CompileQueryMetadata[],
|
||||
template?: CompileTemplateMetadata
|
||||
@ -494,8 +545,8 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
|
||||
this.lifecycleHooks = lifecycleHooks;
|
||||
this.providers = normalizeBlank(providers);
|
||||
this.viewProviders = normalizeBlank(viewProviders);
|
||||
this.queries = queries;
|
||||
this.viewQueries = viewQueries;
|
||||
this.queries = normalizeBlank(queries);
|
||||
this.viewQueries = normalizeBlank(viewQueries);
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
@ -520,7 +571,10 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
|
||||
(<any[]>data['lifecycleHooks']).map(hookValue => LIFECYCLE_HOOKS_VALUES[hookValue]),
|
||||
template: isPresent(data['template']) ? CompileTemplateMetadata.fromJson(data['template']) :
|
||||
data['template'],
|
||||
providers: arrayFromJson(data['providers'], CompileProviderMetadata.fromJson)
|
||||
providers: arrayFromJson(data['providers'], metadataFromJson),
|
||||
viewProviders: arrayFromJson(data['viewProviders'], metadataFromJson),
|
||||
queries: arrayFromJson(data['queries'], CompileQueryMetadata.fromJson),
|
||||
viewQueries: arrayFromJson(data['viewQueries'], CompileQueryMetadata.fromJson)
|
||||
});
|
||||
}
|
||||
|
||||
@ -541,7 +595,10 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
|
||||
'hostAttributes': this.hostAttributes,
|
||||
'lifecycleHooks': this.lifecycleHooks.map(hook => serializeEnum(hook)),
|
||||
'template': isPresent(this.template) ? this.template.toJson() : this.template,
|
||||
'providers': arrayToJson(this.providers)
|
||||
'providers': arrayToJson(this.providers),
|
||||
'viewProviders': arrayToJson(this.viewProviders),
|
||||
'queries': arrayToJson(this.queries),
|
||||
'viewQueries': arrayToJson(this.viewQueries)
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -611,7 +668,9 @@ var _COMPILE_METADATA_FROM_JSON = {
|
||||
'Directive': CompileDirectiveMetadata.fromJson,
|
||||
'Pipe': CompilePipeMetadata.fromJson,
|
||||
'Type': CompileTypeMetadata.fromJson,
|
||||
'Identifier': CompileIdentifierMetadata.fromJson
|
||||
'Provider': CompileProviderMetadata.fromJson,
|
||||
'Identifier': CompileIdentifierMetadata.fromJson,
|
||||
'Factory': CompileFactoryMetadata.fromJson
|
||||
};
|
||||
|
||||
function arrayFromJson(obj: any[], fn: (a: {[key: string]: any}) => any): any {
|
||||
@ -623,9 +682,13 @@ function arrayToJson(obj: any[]): string | {[key: string]: any} {
|
||||
}
|
||||
|
||||
function objFromJson(obj: any, fn: (a: {[key: string]: any}) => any): any {
|
||||
return (isString(obj) || isBlank(obj)) ? obj : fn(obj);
|
||||
if (isArray(obj)) return arrayFromJson(obj, fn);
|
||||
if (isString(obj) || isBlank(obj) || isBoolean(obj) || isNumber(obj)) return obj;
|
||||
return fn(obj);
|
||||
}
|
||||
|
||||
function objToJson(obj: any): string | {[key: string]: any} {
|
||||
return (isString(obj) || isBlank(obj)) ? obj : obj.toJson();
|
||||
if (isArray(obj)) return arrayToJson(obj);
|
||||
if (isString(obj) || isBlank(obj) || isBoolean(obj) || isNumber(obj)) return obj;
|
||||
return obj.toJson();
|
||||
}
|
||||
|
500
modules/angular2/src/compiler/static_reflector.ts
Normal file
500
modules/angular2/src/compiler/static_reflector.ts
Normal file
@ -0,0 +1,500 @@
|
||||
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {
|
||||
isArray,
|
||||
isBlank,
|
||||
isNumber,
|
||||
isPresent,
|
||||
isPrimitive,
|
||||
isString,
|
||||
Type
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {
|
||||
AttributeMetadata,
|
||||
DirectiveMetadata,
|
||||
ComponentMetadata,
|
||||
ContentChildrenMetadata,
|
||||
ContentChildMetadata,
|
||||
InputMetadata,
|
||||
HostBindingMetadata,
|
||||
HostListenerMetadata,
|
||||
OutputMetadata,
|
||||
PipeMetadata,
|
||||
ViewMetadata,
|
||||
ViewChildMetadata,
|
||||
ViewChildrenMetadata,
|
||||
ViewQueryMetadata,
|
||||
QueryMetadata,
|
||||
} from 'angular2/src/core/metadata';
|
||||
|
||||
/**
|
||||
* The host of the static resolver is expected to be able to provide module metadata in the form of
|
||||
* ModuleMetadata. Angular 2 CLI will produce this metadata for a module whenever a .d.ts files is
|
||||
* produced and the module has exported variables or classes with decorators. Module metadata can
|
||||
* also be produced directly from TypeScript sources by using MetadataCollector in tools/metadata.
|
||||
*/
|
||||
export interface StaticReflectorHost {
|
||||
/**
|
||||
* Return a ModuleMetadata for the give module.
|
||||
*
|
||||
* @param moduleId is a string identifier for a module in the form that would expected in a
|
||||
* module import of an import statement.
|
||||
* @returns the metadata for the given module.
|
||||
*/
|
||||
getMetadataFor(moduleId: string): {[key: string]: any};
|
||||
}
|
||||
|
||||
/**
|
||||
* A token representing the a reference to a static type.
|
||||
*
|
||||
* This token is unique for a moduleId and name and can be used as a hash table key.
|
||||
*/
|
||||
export class StaticType {
|
||||
constructor(public moduleId: string, public name: string) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* A static reflector implements enough of the Reflector API that is necessary to compile
|
||||
* templates statically.
|
||||
*/
|
||||
export class StaticReflector {
|
||||
private typeCache = new Map<string, StaticType>();
|
||||
private annotationCache = new Map<StaticType, any[]>();
|
||||
private propertyCache = new Map<StaticType, {[key: string]: any}>();
|
||||
private parameterCache = new Map<StaticType, any[]>();
|
||||
private metadataCache = new Map<string, {[key: string]: any}>();
|
||||
|
||||
constructor(private host: StaticReflectorHost) { this.initializeConversionMap(); }
|
||||
|
||||
/**
|
||||
* getStatictype produces a Type whose metadata is known but whose implementation is not loaded.
|
||||
* All types passed to the StaticResolver should be pseudo-types returned by this method.
|
||||
*
|
||||
* @param moduleId the module identifier as would be passed to an import statement.
|
||||
* @param name the name of the type.
|
||||
*/
|
||||
public getStaticType(moduleId: string, name: string): StaticType {
|
||||
let key = `"${moduleId}".${name}`;
|
||||
let result = this.typeCache.get(key);
|
||||
if (!isPresent(result)) {
|
||||
result = new StaticType(moduleId, name);
|
||||
this.typeCache.set(key, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public annotations(type: StaticType): any[] {
|
||||
let annotations = this.annotationCache.get(type);
|
||||
if (!isPresent(annotations)) {
|
||||
let classMetadata = this.getTypeMetadata(type);
|
||||
if (isPresent(classMetadata['decorators'])) {
|
||||
annotations = (<any[]>classMetadata['decorators'])
|
||||
.map(decorator => this.convertKnownDecorator(type.moduleId, decorator))
|
||||
.filter(decorator => isPresent(decorator));
|
||||
}
|
||||
this.annotationCache.set(type, annotations);
|
||||
}
|
||||
return annotations;
|
||||
}
|
||||
|
||||
public propMetadata(type: StaticType): {[key: string]: any} {
|
||||
let propMetadata = this.propertyCache.get(type);
|
||||
if (!isPresent(propMetadata)) {
|
||||
let classMetadata = this.getTypeMetadata(type);
|
||||
propMetadata = this.getPropertyMetadata(type.moduleId, classMetadata['members']);
|
||||
this.propertyCache.set(type, propMetadata);
|
||||
}
|
||||
return propMetadata;
|
||||
}
|
||||
|
||||
public parameters(type: StaticType): any[] {
|
||||
let parameters = this.parameterCache.get(type);
|
||||
if (!isPresent(parameters)) {
|
||||
let classMetadata = this.getTypeMetadata(type);
|
||||
let ctorData = classMetadata['members']['__ctor__'];
|
||||
if (isPresent(ctorData)) {
|
||||
let ctor = (<any[]>ctorData).find(a => a['__symbolic'] === 'constructor');
|
||||
parameters = this.simplify(type.moduleId, ctor['parameters']);
|
||||
this.parameterCache.set(type, parameters);
|
||||
}
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
|
||||
private conversionMap = new Map<StaticType, (moduleContext: string, expression: any) => any>();
|
||||
private initializeConversionMap(): any {
|
||||
let core_metadata = 'angular2/src/core/metadata';
|
||||
let conversionMap = this.conversionMap;
|
||||
conversionMap.set(this.getStaticType(core_metadata, 'Directive'),
|
||||
(moduleContext, expression) => {
|
||||
let p0 = this.getDecoratorParameter(moduleContext, expression, 0);
|
||||
if (!isPresent(p0)) {
|
||||
p0 = {};
|
||||
}
|
||||
return new DirectiveMetadata({
|
||||
selector: p0['selector'],
|
||||
inputs: p0['inputs'],
|
||||
outputs: p0['outputs'],
|
||||
events: p0['events'],
|
||||
host: p0['host'],
|
||||
bindings: p0['bindings'],
|
||||
providers: p0['providers'],
|
||||
exportAs: p0['exportAs'],
|
||||
queries: p0['queries'],
|
||||
});
|
||||
});
|
||||
conversionMap.set(this.getStaticType(core_metadata, 'Component'),
|
||||
(moduleContext, expression) => {
|
||||
let p0 = this.getDecoratorParameter(moduleContext, expression, 0);
|
||||
if (!isPresent(p0)) {
|
||||
p0 = {};
|
||||
}
|
||||
return new ComponentMetadata({
|
||||
selector: p0['selector'],
|
||||
inputs: p0['inputs'],
|
||||
outputs: p0['outputs'],
|
||||
properties: p0['properties'],
|
||||
events: p0['events'],
|
||||
host: p0['host'],
|
||||
exportAs: p0['exportAs'],
|
||||
moduleId: p0['moduleId'],
|
||||
bindings: p0['bindings'],
|
||||
providers: p0['providers'],
|
||||
viewBindings: p0['viewBindings'],
|
||||
viewProviders: p0['viewProviders'],
|
||||
changeDetection: p0['changeDetection'],
|
||||
queries: p0['queries'],
|
||||
templateUrl: p0['templateUrl'],
|
||||
template: p0['template'],
|
||||
styleUrls: p0['styleUrls'],
|
||||
styles: p0['styles'],
|
||||
directives: p0['directives'],
|
||||
pipes: p0['pipes'],
|
||||
encapsulation: p0['encapsulation']
|
||||
});
|
||||
});
|
||||
conversionMap.set(this.getStaticType(core_metadata, 'Input'),
|
||||
(moduleContext, expression) => new InputMetadata(
|
||||
this.getDecoratorParameter(moduleContext, expression, 0)));
|
||||
conversionMap.set(this.getStaticType(core_metadata, 'Output'),
|
||||
(moduleContext, expression) => new OutputMetadata(
|
||||
this.getDecoratorParameter(moduleContext, expression, 0)));
|
||||
conversionMap.set(this.getStaticType(core_metadata, 'View'), (moduleContext, expression) => {
|
||||
let p0 = this.getDecoratorParameter(moduleContext, expression, 0);
|
||||
if (!isPresent(p0)) {
|
||||
p0 = {};
|
||||
}
|
||||
return new ViewMetadata({
|
||||
templateUrl: p0['templateUrl'],
|
||||
template: p0['template'],
|
||||
directives: p0['directives'],
|
||||
pipes: p0['pipes'],
|
||||
encapsulation: p0['encapsulation'],
|
||||
styles: p0['styles'],
|
||||
});
|
||||
});
|
||||
conversionMap.set(this.getStaticType(core_metadata, 'Attribute'),
|
||||
(moduleContext, expression) => new AttributeMetadata(
|
||||
this.getDecoratorParameter(moduleContext, expression, 0)));
|
||||
conversionMap.set(this.getStaticType(core_metadata, 'Query'), (moduleContext, expression) => {
|
||||
let p0 = this.getDecoratorParameter(moduleContext, expression, 0);
|
||||
let p1 = this.getDecoratorParameter(moduleContext, expression, 1);
|
||||
if (!isPresent(p1)) {
|
||||
p1 = {};
|
||||
}
|
||||
return new QueryMetadata(p0, {descendants: p1.descendants, first: p1.first});
|
||||
});
|
||||
conversionMap.set(this.getStaticType(core_metadata, 'ContentChildren'),
|
||||
(moduleContext, expression) => new ContentChildrenMetadata(
|
||||
this.getDecoratorParameter(moduleContext, expression, 0)));
|
||||
conversionMap.set(this.getStaticType(core_metadata, 'ContentChild'),
|
||||
(moduleContext, expression) => new ContentChildMetadata(
|
||||
this.getDecoratorParameter(moduleContext, expression, 0)));
|
||||
conversionMap.set(this.getStaticType(core_metadata, 'ViewChildren'),
|
||||
(moduleContext, expression) => new ViewChildrenMetadata(
|
||||
this.getDecoratorParameter(moduleContext, expression, 0)));
|
||||
conversionMap.set(this.getStaticType(core_metadata, 'ViewChild'),
|
||||
(moduleContext, expression) => new ViewChildMetadata(
|
||||
this.getDecoratorParameter(moduleContext, expression, 0)));
|
||||
conversionMap.set(this.getStaticType(core_metadata, 'ViewQuery'),
|
||||
(moduleContext, expression) => {
|
||||
let p0 = this.getDecoratorParameter(moduleContext, expression, 0);
|
||||
let p1 = this.getDecoratorParameter(moduleContext, expression, 1);
|
||||
if (!isPresent(p1)) {
|
||||
p1 = {};
|
||||
}
|
||||
return new ViewQueryMetadata(p0, {
|
||||
descendants: p1['descendants'],
|
||||
first: p1['first'],
|
||||
});
|
||||
});
|
||||
conversionMap.set(this.getStaticType(core_metadata, 'Pipe'), (moduleContext, expression) => {
|
||||
let p0 = this.getDecoratorParameter(moduleContext, expression, 0);
|
||||
if (!isPresent(p0)) {
|
||||
p0 = {};
|
||||
}
|
||||
return new PipeMetadata({
|
||||
name: p0['name'],
|
||||
pure: p0['pure'],
|
||||
});
|
||||
});
|
||||
conversionMap.set(this.getStaticType(core_metadata, 'HostBinding'),
|
||||
(moduleContext, expression) => new HostBindingMetadata(
|
||||
this.getDecoratorParameter(moduleContext, expression, 0)));
|
||||
conversionMap.set(this.getStaticType(core_metadata, 'HostListener'),
|
||||
(moduleContext, expression) => new HostListenerMetadata(
|
||||
this.getDecoratorParameter(moduleContext, expression, 0),
|
||||
this.getDecoratorParameter(moduleContext, expression, 1)));
|
||||
return null;
|
||||
}
|
||||
|
||||
private convertKnownDecorator(moduleContext: string, expression: {[key: string]: any}): any {
|
||||
let converter = this.conversionMap.get(this.getDecoratorType(moduleContext, expression));
|
||||
if (isPresent(converter)) return converter(moduleContext, expression);
|
||||
return null;
|
||||
}
|
||||
|
||||
private getDecoratorType(moduleContext: string, expression: {[key: string]: any}): StaticType {
|
||||
if (isMetadataSymbolicCallExpression(expression)) {
|
||||
let target = expression['expression'];
|
||||
if (isMetadataSymbolicReferenceExpression(target)) {
|
||||
let moduleId = this.normalizeModuleName(moduleContext, target['module']);
|
||||
return this.getStaticType(moduleId, target['name']);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private getDecoratorParameter(moduleContext: string, expression: {[key: string]: any},
|
||||
index: number): any {
|
||||
if (isMetadataSymbolicCallExpression(expression) && isPresent(expression['arguments']) &&
|
||||
(<any[]>expression['arguments']).length <= index + 1) {
|
||||
return this.simplify(moduleContext, (<any[]>expression['arguments'])[index]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private getPropertyMetadata(moduleContext: string,
|
||||
value: {[key: string]: any}): {[key: string]: any} {
|
||||
if (isPresent(value)) {
|
||||
let result = {};
|
||||
StringMapWrapper.forEach(value, (value, name) => {
|
||||
let data = this.getMemberData(moduleContext, value);
|
||||
if (isPresent(data)) {
|
||||
let propertyData = data.filter(d => d['kind'] == "property")
|
||||
.map(d => d['directives'])
|
||||
.reduce((p, c) => (<any[]>p).concat(<any[]>c), []);
|
||||
if (propertyData.length != 0) {
|
||||
StringMapWrapper.set(result, name, propertyData);
|
||||
}
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
private getMemberData(moduleContext: string, member: { [key: string]: any }[]): { [key: string]: any }[] {
|
||||
// clang-format on
|
||||
let result = [];
|
||||
if (isPresent(member)) {
|
||||
for (let item of member) {
|
||||
result.push({
|
||||
kind: item['__symbolic'],
|
||||
directives:
|
||||
isPresent(item['decorators']) ?
|
||||
(<any[]>item['decorators'])
|
||||
.map(decorator => this.convertKnownDecorator(moduleContext, decorator))
|
||||
.filter(d => isPresent(d)) :
|
||||
null
|
||||
});
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
public simplify(moduleContext: string, value: any): any {
|
||||
let _this = this;
|
||||
|
||||
function simplify(expression: any): any {
|
||||
if (isPrimitive(expression)) {
|
||||
return expression;
|
||||
}
|
||||
if (isArray(expression)) {
|
||||
let result = [];
|
||||
for (let item of(<any>expression)) {
|
||||
result.push(simplify(item));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
if (isPresent(expression)) {
|
||||
if (isPresent(expression['__symbolic'])) {
|
||||
switch (expression['__symbolic']) {
|
||||
case "binop":
|
||||
let left = simplify(expression['left']);
|
||||
let right = simplify(expression['right']);
|
||||
switch (expression['operator']) {
|
||||
case '&&':
|
||||
return left && right;
|
||||
case '||':
|
||||
return left || right;
|
||||
case '|':
|
||||
return left | right;
|
||||
case '^':
|
||||
return left ^ right;
|
||||
case '&':
|
||||
return left & right;
|
||||
case '==':
|
||||
return left == right;
|
||||
case '!=':
|
||||
return left != right;
|
||||
case '===':
|
||||
return left === right;
|
||||
case '!==':
|
||||
return left !== right;
|
||||
case '<':
|
||||
return left < right;
|
||||
case '>':
|
||||
return left > right;
|
||||
case '<=':
|
||||
return left <= right;
|
||||
case '>=':
|
||||
return left >= right;
|
||||
case '<<':
|
||||
return left << right;
|
||||
case '>>':
|
||||
return left >> right;
|
||||
case '+':
|
||||
return left + right;
|
||||
case '-':
|
||||
return left - right;
|
||||
case '*':
|
||||
return left * right;
|
||||
case '/':
|
||||
return left / right;
|
||||
case '%':
|
||||
return left % right;
|
||||
}
|
||||
return null;
|
||||
case "pre":
|
||||
let operand = simplify(expression['operand']);
|
||||
switch (expression['operator']) {
|
||||
case '+':
|
||||
return operand;
|
||||
case '-':
|
||||
return -operand;
|
||||
case '!':
|
||||
return !operand;
|
||||
case '~':
|
||||
return ~operand;
|
||||
}
|
||||
return null;
|
||||
case "index":
|
||||
let indexTarget = simplify(expression['expression']);
|
||||
let index = simplify(expression['index']);
|
||||
if (isPresent(indexTarget) && isPrimitive(index)) return indexTarget[index];
|
||||
return null;
|
||||
case "select":
|
||||
let selectTarget = simplify(expression['expression']);
|
||||
let member = simplify(expression['member']);
|
||||
if (isPresent(selectTarget) && isPrimitive(member)) return selectTarget[member];
|
||||
return null;
|
||||
case "reference":
|
||||
let referenceModuleName =
|
||||
_this.normalizeModuleName(moduleContext, expression['module']);
|
||||
let referenceModule = _this.getModuleMetadata(referenceModuleName);
|
||||
let referenceValue = referenceModule['metadata'][expression['name']];
|
||||
if (isClassMetadata(referenceValue)) {
|
||||
// Convert to a pseudo type
|
||||
return _this.getStaticType(referenceModuleName, expression['name']);
|
||||
}
|
||||
return _this.simplify(referenceModuleName, referenceValue);
|
||||
case "call":
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
let result = {};
|
||||
StringMapWrapper.forEach(expression, (value, name) => { result[name] = simplify(value); });
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return simplify(value);
|
||||
}
|
||||
|
||||
private getModuleMetadata(module: string): {[key: string]: any} {
|
||||
let moduleMetadata = this.metadataCache.get(module);
|
||||
if (!isPresent(moduleMetadata)) {
|
||||
moduleMetadata = this.host.getMetadataFor(module);
|
||||
if (!isPresent(moduleMetadata)) {
|
||||
moduleMetadata = {__symbolic: "module", module: module, metadata: {}};
|
||||
}
|
||||
this.metadataCache.set(module, moduleMetadata);
|
||||
}
|
||||
return moduleMetadata;
|
||||
}
|
||||
|
||||
private getTypeMetadata(type: StaticType): {[key: string]: any} {
|
||||
let moduleMetadata = this.getModuleMetadata(type.moduleId);
|
||||
let result = moduleMetadata['metadata'][type.name];
|
||||
if (!isPresent(result)) {
|
||||
result = {__symbolic: "class"};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private normalizeModuleName(from: string, to: string): string {
|
||||
if (to.startsWith('.')) {
|
||||
return pathTo(from, to);
|
||||
}
|
||||
return to;
|
||||
}
|
||||
}
|
||||
|
||||
function isMetadataSymbolicCallExpression(expression: any): boolean {
|
||||
return !isPrimitive(expression) && !isArray(expression) && expression['__symbolic'] == 'call';
|
||||
}
|
||||
|
||||
function isMetadataSymbolicReferenceExpression(expression: any): boolean {
|
||||
return !isPrimitive(expression) && !isArray(expression) &&
|
||||
expression['__symbolic'] == 'reference';
|
||||
}
|
||||
|
||||
function isClassMetadata(expression: any): boolean {
|
||||
return !isPrimitive(expression) && !isArray(expression) && expression['__symbolic'] == 'class';
|
||||
}
|
||||
|
||||
function splitPath(path: string): string[] {
|
||||
return path.split(/\/|\\/g);
|
||||
}
|
||||
|
||||
function resolvePath(pathParts: string[]): string {
|
||||
let result = [];
|
||||
ListWrapper.forEachWithIndex(pathParts, (part, index) => {
|
||||
switch (part) {
|
||||
case '':
|
||||
case '.':
|
||||
if (index > 0) return;
|
||||
break;
|
||||
case '..':
|
||||
if (index > 0 && result.length != 0) result.pop();
|
||||
return;
|
||||
}
|
||||
result.push(part);
|
||||
});
|
||||
return result.join('/');
|
||||
}
|
||||
|
||||
function pathTo(from: string, to: string): string {
|
||||
let result = to;
|
||||
if (to.startsWith('.')) {
|
||||
let fromParts = splitPath(from);
|
||||
fromParts.pop(); // remove the file name.
|
||||
let toParts = splitPath(to);
|
||||
result = resolvePath(fromParts.concat(toParts));
|
||||
}
|
||||
return result;
|
||||
}
|
@ -110,6 +110,9 @@ export class TemplateCompiler {
|
||||
hostAttributes: directive.hostAttributes,
|
||||
lifecycleHooks: directive.lifecycleHooks,
|
||||
providers: directive.providers,
|
||||
viewProviders: directive.viewProviders,
|
||||
queries: directive.queries,
|
||||
viewQueries: directive.viewQueries,
|
||||
template: normalizedTemplate
|
||||
}));
|
||||
}
|
||||
|
@ -83,6 +83,10 @@ export class TemplateParseError extends ParseError {
|
||||
constructor(message: string, span: ParseSourceSpan) { super(span, message); }
|
||||
}
|
||||
|
||||
export class TemplateParseResult {
|
||||
constructor(public templateAst?: TemplateAst[], public errors?: ParseError[]) {}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class TemplateParser {
|
||||
constructor(private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry,
|
||||
@ -91,20 +95,29 @@ export class TemplateParser {
|
||||
|
||||
parse(template: string, directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[],
|
||||
templateUrl: string): TemplateAst[] {
|
||||
var result = this.tryParse(template, directives, pipes, templateUrl);
|
||||
if (isPresent(result.errors)) {
|
||||
var errorString = result.errors.join('\n');
|
||||
throw new BaseException(`Template parse errors:\n${errorString}`);
|
||||
}
|
||||
return result.templateAst;
|
||||
}
|
||||
|
||||
tryParse(template: string, directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[],
|
||||
templateUrl: string): TemplateParseResult {
|
||||
var parseVisitor =
|
||||
new TemplateParseVisitor(directives, pipes, this._exprParser, this._schemaRegistry);
|
||||
var htmlAstWithErrors = this._htmlParser.parse(template, templateUrl);
|
||||
var result = htmlVisitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_COMPONENT);
|
||||
var errors: ParseError[] = htmlAstWithErrors.errors.concat(parseVisitor.errors);
|
||||
if (errors.length > 0) {
|
||||
var errorString = errors.join('\n');
|
||||
throw new BaseException(`Template parse errors:\n${errorString}`);
|
||||
return new TemplateParseResult(result, errors);
|
||||
}
|
||||
if (isPresent(this.transforms)) {
|
||||
this.transforms.forEach(
|
||||
(transform: TemplateAstVisitor) => { result = templateVisitAll(transform, result); });
|
||||
}
|
||||
return result;
|
||||
return new TemplateParseResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,7 +200,7 @@ class CodeGenViewFactory implements ViewFactory<Expression, Statement> {
|
||||
|
||||
createElementEventListener(renderer: Expression, appView: Expression, boundElementIndex: number,
|
||||
renderNode: Expression, eventAst: BoundEventAst,
|
||||
targetStatements: Statement[]) {
|
||||
targetStatements: Statement[]): Expression {
|
||||
var disposableVar = this._nextDisposableVar();
|
||||
var eventHandlerExpr = codeGenEventHandler(appView, boundElementIndex, eventAst.fullName);
|
||||
targetStatements.push(new Statement(
|
||||
|
@ -250,7 +250,7 @@ export class PlatformRef_ extends PlatformRef {
|
||||
provide(ApplicationRef, {useFactory: (): ApplicationRef => app, deps: []})
|
||||
]);
|
||||
|
||||
var exceptionHandler: Function;
|
||||
var exceptionHandler: ExceptionHandler;
|
||||
try {
|
||||
injector = this.injector.resolveAndCreateChild(providers);
|
||||
exceptionHandler = injector.get(ExceptionHandler);
|
||||
|
@ -109,6 +109,7 @@ export class DefaultIterableDiffer implements IterableDiffer {
|
||||
|
||||
onDestroy() {}
|
||||
|
||||
// todo(vicb): optim for UnmodifiableListView (frozen arrays)
|
||||
check(collection: any): boolean {
|
||||
this._reset();
|
||||
|
||||
@ -118,27 +119,24 @@ export class DefaultIterableDiffer implements IterableDiffer {
|
||||
var item;
|
||||
var itemTrackBy;
|
||||
if (isArray(collection)) {
|
||||
if (collection !== this._collection || !ListWrapper.isImmutable(collection)) {
|
||||
var list = collection;
|
||||
this._length = collection.length;
|
||||
var list = collection;
|
||||
this._length = collection.length;
|
||||
|
||||
for (index = 0; index < this._length; index++) {
|
||||
item = list[index];
|
||||
itemTrackBy = this._trackByFn(index, item);
|
||||
if (record === null || !looseIdentical(record.trackById, itemTrackBy)) {
|
||||
record = this._mismatch(record, item, itemTrackBy, index);
|
||||
mayBeDirty = true;
|
||||
} else {
|
||||
if (mayBeDirty) {
|
||||
// TODO(misko): can we limit this to duplicates only?
|
||||
record = this._verifyReinsertion(record, item, itemTrackBy, index);
|
||||
}
|
||||
if (!looseIdentical(record.item, item)) this._addIdentityChange(record, item);
|
||||
for (index = 0; index < this._length; index++) {
|
||||
item = list[index];
|
||||
itemTrackBy = this._trackByFn(index, item);
|
||||
if (record === null || !looseIdentical(record.trackById, itemTrackBy)) {
|
||||
record = this._mismatch(record, item, itemTrackBy, index);
|
||||
mayBeDirty = true;
|
||||
} else {
|
||||
if (mayBeDirty) {
|
||||
// TODO(misko): can we limit this to duplicates only?
|
||||
record = this._verifyReinsertion(record, item, itemTrackBy, index);
|
||||
}
|
||||
|
||||
record = record._next;
|
||||
if (!looseIdentical(record.item, item)) this._addIdentityChange(record, item);
|
||||
}
|
||||
this._truncate(record);
|
||||
|
||||
record = record._next;
|
||||
}
|
||||
} else {
|
||||
index = 0;
|
||||
@ -158,9 +156,9 @@ export class DefaultIterableDiffer implements IterableDiffer {
|
||||
index++;
|
||||
});
|
||||
this._length = index;
|
||||
this._truncate(record);
|
||||
}
|
||||
|
||||
this._truncate(record);
|
||||
this._collection = collection;
|
||||
return this.isDirty;
|
||||
}
|
||||
|
@ -31,7 +31,6 @@ export interface IterableDifferFactory {
|
||||
/**
|
||||
* A repository of different iterable diffing strategies used by NgFor, NgClass, and others.
|
||||
*/
|
||||
@Injectable()
|
||||
@CONST()
|
||||
export class IterableDiffers {
|
||||
constructor(public factories: IterableDifferFactory[]) {}
|
||||
|
@ -23,7 +23,6 @@ export interface KeyValueDifferFactory {
|
||||
/**
|
||||
* A repository of different Map diffing strategies used by NgClass, NgStyle, and others.
|
||||
*/
|
||||
@Injectable()
|
||||
@CONST()
|
||||
export class KeyValueDiffers {
|
||||
constructor(public factories: KeyValueDifferFactory[]) {}
|
||||
|
@ -63,9 +63,10 @@ export class DebugDomRenderer implements Renderer {
|
||||
projectNodes(parentElement: any, nodes: any[]) {
|
||||
var debugParent = getDebugNode(parentElement);
|
||||
if (isPresent(debugParent) && debugParent instanceof DebugElement) {
|
||||
nodes.forEach((node) => { debugParent.addChild(getDebugNode(node)); });
|
||||
let debugElement = debugParent;
|
||||
nodes.forEach((node) => { debugElement.addChild(getDebugNode(node)); });
|
||||
}
|
||||
return this._delegate.projectNodes(parentElement, nodes);
|
||||
this._delegate.projectNodes(parentElement, nodes);
|
||||
}
|
||||
|
||||
attachViewAfter(node: any, viewRootNodes: any[]) {
|
||||
@ -78,7 +79,7 @@ export class DebugDomRenderer implements Renderer {
|
||||
debugParent.insertChildrenAfter(debugNode, debugViewRootNodes);
|
||||
}
|
||||
}
|
||||
return this._delegate.attachViewAfter(node, viewRootNodes);
|
||||
this._delegate.attachViewAfter(node, viewRootNodes);
|
||||
}
|
||||
|
||||
detachView(viewRootNodes: any[]) {
|
||||
@ -88,15 +89,15 @@ export class DebugDomRenderer implements Renderer {
|
||||
debugNode.parent.removeChild(debugNode);
|
||||
}
|
||||
});
|
||||
return this._delegate.detachView(viewRootNodes);
|
||||
this._delegate.detachView(viewRootNodes);
|
||||
}
|
||||
|
||||
destroyView(hostElement: any, viewAllNodes: any[]) {
|
||||
viewAllNodes.forEach((node) => { removeDebugNodeFromIndex(getDebugNode(node)); });
|
||||
return this._delegate.destroyView(hostElement, viewAllNodes);
|
||||
this._delegate.destroyView(hostElement, viewAllNodes);
|
||||
}
|
||||
|
||||
listen(renderElement: any, name: string, callback: Function) {
|
||||
listen(renderElement: any, name: string, callback: Function): Function {
|
||||
var debugEl = getDebugNode(renderElement);
|
||||
if (isPresent(debugEl)) {
|
||||
debugEl.listeners.push(new EventListener(name, callback));
|
||||
@ -113,7 +114,7 @@ export class DebugDomRenderer implements Renderer {
|
||||
if (isPresent(debugEl) && debugEl instanceof DebugElement) {
|
||||
debugEl.properties.set(propertyName, propertyValue);
|
||||
}
|
||||
return this._delegate.setElementProperty(renderElement, propertyName, propertyValue);
|
||||
this._delegate.setElementProperty(renderElement, propertyName, propertyValue);
|
||||
}
|
||||
|
||||
setElementAttribute(renderElement: any, attributeName: string, attributeValue: string) {
|
||||
@ -121,7 +122,7 @@ export class DebugDomRenderer implements Renderer {
|
||||
if (isPresent(debugEl) && debugEl instanceof DebugElement) {
|
||||
debugEl.attributes.set(attributeName, attributeValue);
|
||||
}
|
||||
return this._delegate.setElementAttribute(renderElement, attributeName, attributeValue);
|
||||
this._delegate.setElementAttribute(renderElement, attributeName, attributeValue);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -129,7 +130,7 @@ export class DebugDomRenderer implements Renderer {
|
||||
* such as <template> placeholders.
|
||||
*/
|
||||
setBindingDebugInfo(renderElement: any, propertyName: string, propertyValue: string) {
|
||||
return this._delegate.setBindingDebugInfo(renderElement, propertyName, propertyValue);
|
||||
this._delegate.setBindingDebugInfo(renderElement, propertyName, propertyValue);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -138,20 +139,20 @@ export class DebugDomRenderer implements Renderer {
|
||||
setElementDebugInfo(renderElement: any, info: RenderDebugInfo) {
|
||||
var debugEl = getDebugNode(renderElement);
|
||||
debugEl.setDebugInfo(info);
|
||||
return this._delegate.setElementDebugInfo(renderElement, info);
|
||||
this._delegate.setElementDebugInfo(renderElement, info);
|
||||
}
|
||||
|
||||
setElementClass(renderElement: any, className: string, isAdd: boolean) {
|
||||
return this._delegate.setElementClass(renderElement, className, isAdd);
|
||||
this._delegate.setElementClass(renderElement, className, isAdd);
|
||||
}
|
||||
|
||||
setElementStyle(renderElement: any, styleName: string, styleValue: string) {
|
||||
return this._delegate.setElementStyle(renderElement, styleName, styleValue);
|
||||
this._delegate.setElementStyle(renderElement, styleName, styleValue);
|
||||
}
|
||||
|
||||
invokeElementMethod(renderElement: any, methodName: string, args: any[]) {
|
||||
return this._delegate.invokeElementMethod(renderElement, methodName, args);
|
||||
this._delegate.invokeElementMethod(renderElement, methodName, args);
|
||||
}
|
||||
|
||||
setText(renderNode: any, text: string) { return this._delegate.setText(renderNode, text); }
|
||||
setText(renderNode: any, text: string) { this._delegate.setText(renderNode, text); }
|
||||
}
|
||||
|
@ -988,7 +988,7 @@ export var Attribute: AttributeFactory = makeParamDecorator(AttributeMetadata);
|
||||
* <div #findme>...</div>
|
||||
* </seeker>
|
||||
*
|
||||
* @Component({ selector: 'foo' })
|
||||
* @Component({ selector: 'seeker' })
|
||||
* class seeker {
|
||||
* constructor(@Query('findme') elList: QueryList<ElementRef>) {...}
|
||||
* }
|
||||
@ -1006,7 +1006,7 @@ export var Attribute: AttributeFactory = makeParamDecorator(AttributeMetadata);
|
||||
* </seeker>
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'foo'
|
||||
* selector: 'seeker'
|
||||
* })
|
||||
* class Seeker {
|
||||
* constructor(@Query('findMe, findMeToo') elList: QueryList<ElementRef>) {...}
|
||||
|
@ -253,7 +253,7 @@ export class ContentChildMetadata extends QueryMetadata {
|
||||
* class MyComponent {
|
||||
* shown: boolean;
|
||||
*
|
||||
* constructor(private @Query(Item) items:QueryList<Item>) {
|
||||
* constructor(private @ViewQuery(Item) items:QueryList<Item>) {
|
||||
* items.changes.subscribe(() => console.log(items.length));
|
||||
* }
|
||||
* }
|
||||
|
@ -246,8 +246,8 @@ class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||
List _convertParameter(ParameterMirror p) {
|
||||
var t = p.type;
|
||||
var res = (!t.hasReflectedType || t.reflectedType == dynamic)
|
||||
? []
|
||||
: [t.reflectedType];
|
||||
? <Object>[]
|
||||
: <Object>[t.reflectedType];
|
||||
res.addAll(p.metadata.map((m) => m.reflectee));
|
||||
return res;
|
||||
}
|
||||
|
@ -26,27 +26,29 @@ export abstract class Renderer implements ParentRenderer {
|
||||
|
||||
abstract createText(parentElement: any, value: string): any;
|
||||
|
||||
abstract projectNodes(parentElement: any, nodes: any[]);
|
||||
abstract projectNodes(parentElement: any, nodes: any[]): void;
|
||||
|
||||
abstract attachViewAfter(node: any, viewRootNodes: any[]);
|
||||
abstract attachViewAfter(node: any, viewRootNodes: any[]): void;
|
||||
|
||||
abstract detachView(viewRootNodes: any[]);
|
||||
abstract detachView(viewRootNodes: any[]): void;
|
||||
|
||||
abstract destroyView(hostElement: any, viewAllNodes: any[]);
|
||||
abstract destroyView(hostElement: any, viewAllNodes: any[]): void;
|
||||
|
||||
abstract listen(renderElement: any, name: string, callback: Function): Function;
|
||||
|
||||
abstract listenGlobal(target: string, name: string, callback: Function): Function;
|
||||
|
||||
abstract setElementProperty(renderElement: any, propertyName: string, propertyValue: any);
|
||||
abstract setElementProperty(renderElement: any, propertyName: string, propertyValue: any): void;
|
||||
|
||||
abstract setElementAttribute(renderElement: any, attributeName: string, attributeValue: string);
|
||||
abstract setElementAttribute(renderElement: any, attributeName: string,
|
||||
attributeValue: string): void;
|
||||
|
||||
/**
|
||||
* Used only in debug mode to serialize property changes to comment nodes,
|
||||
* such as <template> placeholders.
|
||||
*/
|
||||
abstract setBindingDebugInfo(renderElement: any, propertyName: string, propertyValue: string);
|
||||
abstract setBindingDebugInfo(renderElement: any, propertyName: string,
|
||||
propertyValue: string): void;
|
||||
|
||||
abstract setElementDebugInfo(renderElement: any, info: RenderDebugInfo);
|
||||
|
||||
|
@ -15,6 +15,7 @@ import {ObservableWrapper} from 'angular2/src/facade/async';
|
||||
export class Testability {
|
||||
/** @internal */
|
||||
_pendingCount: number = 0;
|
||||
/** @internal */
|
||||
_isZoneStable: boolean = true;
|
||||
/**
|
||||
* Whether any work was done since the last 'whenStable' callback. This is
|
||||
|
@ -1,6 +1,6 @@
|
||||
library facade.collection;
|
||||
|
||||
import 'dart:collection' show IterableBase, UnmodifiableListView;
|
||||
import 'dart:collection' show IterableBase;
|
||||
import 'dart:convert' show JsonEncoder;
|
||||
export 'dart:core' show Iterator, Map, List, Set;
|
||||
import 'dart:math' show max, min;
|
||||
@ -110,9 +110,6 @@ class ListWrapper {
|
||||
static List/*<T>*/ createFixedSize/*<T>*/(int size) => new List(size);
|
||||
static List/*<T>*/ createGrowableSize/*<T>*/(int size) =>
|
||||
new List.generate(size, (_) => null, growable: true);
|
||||
static UnmodifiableListView createImmutable(List input) {
|
||||
return new UnmodifiableListView(input);
|
||||
}
|
||||
|
||||
static bool contains(List m, k) => m.contains(k);
|
||||
static int indexOf(List list, value, [int startIndex = 0]) =>
|
||||
@ -229,10 +226,6 @@ class ListWrapper {
|
||||
return solution;
|
||||
}
|
||||
|
||||
static bool isImmutable(List l) {
|
||||
return l is UnmodifiableListView;
|
||||
}
|
||||
|
||||
static List flatten(List l) {
|
||||
final res = [];
|
||||
l.forEach((item) {
|
||||
|
@ -184,11 +184,6 @@ export class ListWrapper {
|
||||
static createFixedSize(size: number): any[] { return new Array(size); }
|
||||
static createGrowableSize(size: number): any[] { return new Array(size); }
|
||||
static clone<T>(array: T[]): T[] { return array.slice(0); }
|
||||
static createImmutable<T>(array: T[]): T[] {
|
||||
var result = ListWrapper.clone(array);
|
||||
Object.seal(result);
|
||||
return result;
|
||||
}
|
||||
static forEachWithIndex<T>(array: T[], fn: (t: T, n: number) => void) {
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
fn(array[i], i);
|
||||
@ -277,7 +272,6 @@ export class ListWrapper {
|
||||
return solution;
|
||||
}
|
||||
|
||||
static isImmutable(list: any[]): boolean { return Object.isSealed(list); }
|
||||
static flatten<T>(array: T[][]): T[] {
|
||||
let res = [];
|
||||
array.forEach((a) => res = res.concat(a));
|
||||
|
@ -33,6 +33,7 @@ bool isStringMap(Object obj) => obj is Map;
|
||||
bool isArray(Object obj) => obj is List;
|
||||
bool isPromise(Object obj) => obj is Future;
|
||||
bool isNumber(Object obj) => obj is num;
|
||||
bool isBoolean(Object obj) => obj is bool;
|
||||
bool isDate(Object obj) => obj is DateTime;
|
||||
|
||||
String stringify(obj) {
|
||||
|
@ -124,6 +124,14 @@ export function isBlank(obj: any): boolean {
|
||||
return obj === undefined || obj === null;
|
||||
}
|
||||
|
||||
export function isBoolean(obj: any): boolean {
|
||||
return typeof obj === "boolean";
|
||||
}
|
||||
|
||||
export function isNumber(obj: any): boolean {
|
||||
return typeof obj === "number";
|
||||
}
|
||||
|
||||
export function isString(obj: any): boolean {
|
||||
return typeof obj === "string";
|
||||
}
|
||||
@ -148,10 +156,6 @@ export function isArray(obj: any): boolean {
|
||||
return Array.isArray(obj);
|
||||
}
|
||||
|
||||
export function isNumber(obj): boolean {
|
||||
return typeof obj === 'number';
|
||||
}
|
||||
|
||||
export function isDate(obj): boolean {
|
||||
return obj instanceof Date && !isNaN(obj.valueOf());
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ export class MockConnection implements Connection {
|
||||
response: ReplaySubject<Response>;
|
||||
|
||||
constructor(req: Request) {
|
||||
this.response = take.call(new ReplaySubject(1), 1);
|
||||
this.response = <any>take.call(new ReplaySubject(1), 1);
|
||||
this.readyState = ReadyState.Open;
|
||||
this.request = req;
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ export function removeInterpolation(value: string, source: ParseSourceSpan,
|
||||
}
|
||||
}
|
||||
|
||||
export function stringifyNodes(nodes: HtmlAst[], parser: Parser) {
|
||||
export function stringifyNodes(nodes: HtmlAst[], parser: Parser): string {
|
||||
let visitor = new _StringifyVisitor(parser);
|
||||
return htmlVisitAll(visitor, nodes).join("");
|
||||
}
|
||||
|
48
modules/angular2/src/platform/browser/xhr_cache.dart
Normal file
48
modules/angular2/src/platform/browser/xhr_cache.dart
Normal file
@ -0,0 +1,48 @@
|
||||
library angular2.src.services.xhr_cache;
|
||||
|
||||
import 'dart:async' show Future;
|
||||
import 'dart:html';
|
||||
import 'dart:js' as js;
|
||||
import 'package:angular2/core.dart';
|
||||
import 'package:angular2/src/compiler/xhr.dart';
|
||||
import 'package:angular2/src/facade/exceptions.dart' show BaseException;
|
||||
|
||||
/**
|
||||
* An implementation of XHR that uses a template cache to avoid doing an actual
|
||||
* XHR.
|
||||
*
|
||||
* The template cache needs to be built and loaded into window.$templateCache
|
||||
* via a separate mechanism.
|
||||
*/
|
||||
@Injectable()
|
||||
class CachedXHR extends XHR {
|
||||
js.JsObject _cache;
|
||||
String _baseUri;
|
||||
|
||||
CachedXHR() {
|
||||
if (js.context.hasProperty(r'$templateCache')) {
|
||||
this._cache = js.context[r'$templateCache'];
|
||||
} else {
|
||||
throw new BaseException(
|
||||
r'CachedXHR: Template cache was not found in $templateCache.');
|
||||
}
|
||||
this._baseUri = window.location.protocol +
|
||||
'//' +
|
||||
window.location.host +
|
||||
window.location.pathname;
|
||||
int lastSlash = this._baseUri.lastIndexOf('/');
|
||||
this._baseUri = this._baseUri.substring(0, lastSlash + 1);
|
||||
}
|
||||
|
||||
Future<String> get(String url) {
|
||||
if (url.startsWith(this._baseUri)) {
|
||||
url = url.substring(this._baseUri.length);
|
||||
}
|
||||
if (this._cache.hasProperty(url)) {
|
||||
return new Future.value(this._cache[url]);
|
||||
} else {
|
||||
return new Future.error(
|
||||
'CachedXHR: Did not find cached template for ' + url);
|
||||
}
|
||||
}
|
||||
}
|
31
modules/angular2/src/platform/browser/xhr_cache.ts
Normal file
31
modules/angular2/src/platform/browser/xhr_cache.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import {XHR} from 'angular2/src/compiler/xhr';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {global} from 'angular2/src/facade/lang';
|
||||
import {PromiseWrapper} from 'angular2/src/facade/promise';
|
||||
|
||||
/**
|
||||
* An implementation of XHR that uses a template cache to avoid doing an actual
|
||||
* XHR.
|
||||
*
|
||||
* The template cache needs to be built and loaded into window.$templateCache
|
||||
* via a separate mechanism.
|
||||
*/
|
||||
export class CachedXHR extends XHR {
|
||||
private _cache: {[url: string]: string};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._cache = (<any>global).$templateCache;
|
||||
if (this._cache == null) {
|
||||
throw new BaseException('CachedXHR: Template cache was not found in $templateCache.');
|
||||
}
|
||||
}
|
||||
|
||||
get(url: string): Promise<string> {
|
||||
if (this._cache.hasOwnProperty(url)) {
|
||||
return PromiseWrapper.resolve(this._cache[url]);
|
||||
} else {
|
||||
return PromiseWrapper.reject('CachedXHR: Did not find cached template for ' + url, null);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import {CONST_EXPR, IS_DART} from 'angular2/src/facade/lang';
|
||||
import {provide, Provider, Injector, OpaqueToken} from 'angular2/src/core/di';
|
||||
|
||||
import {XHR} from 'angular2/src/compiler/xhr';
|
||||
import {
|
||||
PLATFORM_INITIALIZER,
|
||||
PLATFORM_DIRECTIVES,
|
||||
@ -28,8 +28,13 @@ import {BrowserDetails} from "angular2/src/animate/browser_details";
|
||||
import {AnimationBuilder} from "angular2/src/animate/animation_builder";
|
||||
import {BrowserDomAdapter} from './browser/browser_adapter';
|
||||
import {BrowserGetTestability} from 'angular2/src/platform/browser/testability';
|
||||
import {CachedXHR} from 'angular2/src/platform/browser/xhr_cache';
|
||||
import {wtfInit} from 'angular2/src/core/profile/wtf_init';
|
||||
import {EventManager, EVENT_MANAGER_PLUGINS} from "angular2/src/platform/dom/events/event_manager";
|
||||
import {
|
||||
HAMMER_GESTURE_CONFIG,
|
||||
HammerGestureConfig
|
||||
} from 'angular2/src/platform/dom/events/hammer_gestures';
|
||||
import {ELEMENT_PROBE_PROVIDERS} from 'angular2/platform/common_dom';
|
||||
export {DOCUMENT} from 'angular2/src/platform/dom/dom_tokens';
|
||||
export {Title} from 'angular2/src/platform/browser/title';
|
||||
@ -41,6 +46,7 @@ export {
|
||||
} from 'angular2/platform/common_dom';
|
||||
export {BrowserDomAdapter} from './browser/browser_adapter';
|
||||
export {enableDebugTools, disableDebugTools} from 'angular2/src/platform/browser/tools/tools';
|
||||
export {HAMMER_GESTURE_CONFIG, HammerGestureConfig} from './dom/events/hammer_gestures';
|
||||
|
||||
/**
|
||||
* A set of providers to initialize the Angular platform in a web browser.
|
||||
@ -77,6 +83,7 @@ export const BROWSER_APP_COMMON_PROVIDERS: Array<any /*Type | Provider | any[]*/
|
||||
new Provider(EVENT_MANAGER_PLUGINS, {useClass: DomEventsPlugin, multi: true}),
|
||||
new Provider(EVENT_MANAGER_PLUGINS, {useClass: KeyEventsPlugin, multi: true}),
|
||||
new Provider(EVENT_MANAGER_PLUGINS, {useClass: HammerGesturesPlugin, multi: true}),
|
||||
new Provider(HAMMER_GESTURE_CONFIG, {useClass: HammerGestureConfig}),
|
||||
new Provider(DomRootRenderer, {useClass: DomRootRenderer_}),
|
||||
new Provider(RootRenderer, {useExisting: DomRootRenderer}),
|
||||
new Provider(SharedStylesHost, {useExisting: DomSharedStylesHost}),
|
||||
@ -88,6 +95,9 @@ export const BROWSER_APP_COMMON_PROVIDERS: Array<any /*Type | Provider | any[]*/
|
||||
ELEMENT_PROBE_PROVIDERS
|
||||
]);
|
||||
|
||||
export const CACHED_TEMPLATE_PROVIDER: Array<any /*Type | Provider | any[]*/> =
|
||||
CONST_EXPR([new Provider(XHR, {useClass: CachedXHR})]);
|
||||
|
||||
export function initDomAdapter() {
|
||||
BrowserDomAdapter.makeCurrent();
|
||||
wtfInit();
|
||||
|
@ -3,14 +3,42 @@ library angular.events;
|
||||
import 'dart:html';
|
||||
import './hammer_common.dart';
|
||||
import 'package:angular2/src/facade/exceptions.dart' show BaseException;
|
||||
import "package:angular2/src/core/di.dart" show Injectable;
|
||||
import "package:angular2/src/core/di.dart" show Injectable, Inject, OpaqueToken;
|
||||
|
||||
import 'dart:js' as js;
|
||||
|
||||
const OpaqueToken HAMMER_GESTURE_CONFIG = const OpaqueToken("HammerGestureConfig");
|
||||
|
||||
overrideDefault(js.JsObject mc, String eventName, Object config) {
|
||||
var jsObj = mc.callMethod('get', [eventName]);
|
||||
jsObj.callMethod('set', [
|
||||
new js.JsObject.jsify(config)
|
||||
]);
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class HammerGestureConfig {
|
||||
List<String> events = [];
|
||||
Map overrides = {};
|
||||
|
||||
buildHammer(Element element) {
|
||||
var mc = new js.JsObject(js.context['Hammer'], [element]);
|
||||
overrideDefault(mc, 'pinch', {'enable': true});
|
||||
overrideDefault(mc, 'rotate', {'enable': true});
|
||||
this.overrides.forEach((Object config, String eventName) => overrideDefault(mc, eventName, config));
|
||||
return mc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class HammerGesturesPlugin extends HammerGesturesPluginCommon {
|
||||
HammerGestureConfig _config;
|
||||
|
||||
HammerGesturesPlugin(@Inject(HAMMER_GESTURE_CONFIG) this._config) {}
|
||||
|
||||
bool supports(String eventName) {
|
||||
if (!super.supports(eventName)) return false;
|
||||
if (!super.supports(eventName) && !this.isCustomEvent(eventName)) return false;
|
||||
|
||||
if (!js.context.hasProperty('Hammer')) {
|
||||
throw new BaseException(
|
||||
@ -26,16 +54,7 @@ class HammerGesturesPlugin extends HammerGesturesPluginCommon {
|
||||
|
||||
zone.runOutsideAngular(() {
|
||||
// Creating the manager bind events, must be done outside of angular
|
||||
var mc = new js.JsObject(js.context['Hammer'], [element]);
|
||||
|
||||
var jsObj = mc.callMethod('get', ['pinch']);
|
||||
jsObj.callMethod('set', [
|
||||
new js.JsObject.jsify({'enable': true})
|
||||
]);
|
||||
jsObj = mc.callMethod('get', ['rotate']);
|
||||
jsObj.callMethod('set', [
|
||||
new js.JsObject.jsify({'enable': true})
|
||||
]);
|
||||
var mc = this._config.buildHammer(element);
|
||||
|
||||
mc.callMethod('on', [
|
||||
eventName,
|
||||
@ -48,6 +67,9 @@ class HammerGesturesPlugin extends HammerGesturesPluginCommon {
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
isCustomEvent(String eventName) { return this._config.events.indexOf(eventName) > -1; }
|
||||
|
||||
}
|
||||
|
||||
class HammerEvent {
|
||||
|
@ -1,12 +1,42 @@
|
||||
import {HammerGesturesPluginCommon} from './hammer_common';
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
import {isPresent, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
import {Injectable, Inject, OpaqueToken} from 'angular2/core';
|
||||
|
||||
export const HAMMER_GESTURE_CONFIG: OpaqueToken =
|
||||
CONST_EXPR(new OpaqueToken("HammerGestureConfig"));
|
||||
|
||||
export interface HammerInstance {
|
||||
on(eventName: string, callback: Function): void;
|
||||
off(eventName: string, callback: Function): void;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class HammerGestureConfig {
|
||||
events: string[] = [];
|
||||
|
||||
overrides: {[key: string]: Object} = {};
|
||||
|
||||
buildHammer(element: HTMLElement): HammerInstance {
|
||||
var mc = new Hammer(element);
|
||||
|
||||
mc.get('pinch').set({enable: true});
|
||||
mc.get('rotate').set({enable: true});
|
||||
|
||||
for (let eventName in this.overrides) {
|
||||
mc.get(eventName).set(this.overrides[eventName]);
|
||||
}
|
||||
|
||||
return mc;
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class HammerGesturesPlugin extends HammerGesturesPluginCommon {
|
||||
constructor(@Inject(HAMMER_GESTURE_CONFIG) private _config: HammerGestureConfig) { super(); }
|
||||
|
||||
supports(eventName: string): boolean {
|
||||
if (!super.supports(eventName)) return false;
|
||||
if (!super.supports(eventName) && !this.isCustomEvent(eventName)) return false;
|
||||
|
||||
if (!isPresent(window['Hammer'])) {
|
||||
throw new BaseException(`Hammer.js is not loaded, can not bind ${eventName} event`);
|
||||
@ -19,14 +49,14 @@ export class HammerGesturesPlugin extends HammerGesturesPluginCommon {
|
||||
var zone = this.manager.getZone();
|
||||
eventName = eventName.toLowerCase();
|
||||
|
||||
return zone.runOutsideAngular(function() {
|
||||
return zone.runOutsideAngular(() => {
|
||||
// Creating the manager bind events, must be done outside of angular
|
||||
var mc = new Hammer(element);
|
||||
mc.get('pinch').set({enable: true});
|
||||
mc.get('rotate').set({enable: true});
|
||||
var mc = this._config.buildHammer(element);
|
||||
var callback = function(eventObj) { zone.run(function() { handler(eventObj); }); };
|
||||
mc.on(eventName, callback);
|
||||
return () => { mc.off(eventName, callback); };
|
||||
});
|
||||
}
|
||||
|
||||
isCustomEvent(eventName: string): boolean { return this._config.events.indexOf(eventName) > -1; }
|
||||
}
|
||||
|
@ -159,7 +159,7 @@ export class Parse5DomAdapter extends DomAdapter {
|
||||
var evt = <Event>{
|
||||
type: eventType,
|
||||
defaultPrevented: false,
|
||||
preventDefault: () => { evt.defaultPrevented = true; }
|
||||
preventDefault: () => { (<any>evt).defaultPrevented = true; }
|
||||
};
|
||||
return evt;
|
||||
}
|
||||
|
@ -59,7 +59,6 @@ class UIMessageBusSource extends IsolateMessageBusSource {
|
||||
* Wrapper class that exposes the Isolate
|
||||
* and underlying {@link MessageBus} for lower level message passing.
|
||||
*/
|
||||
@Injectable()
|
||||
class WebWorkerInstance {
|
||||
Isolate worker;
|
||||
MessageBus bus;
|
||||
|
@ -48,6 +48,7 @@ import {
|
||||
import {Serializer} from 'angular2/src/web_workers/shared/serializer';
|
||||
import {ON_WEB_WORKER} from 'angular2/src/web_workers/shared/api';
|
||||
import {RenderStore} from 'angular2/src/web_workers/shared/render_store';
|
||||
import {HAMMER_GESTURE_CONFIG, HammerGestureConfig} from './dom/events/hammer_gestures';
|
||||
|
||||
export const WORKER_SCRIPT: OpaqueToken = CONST_EXPR(new OpaqueToken("WebWorkerScript"));
|
||||
|
||||
@ -77,6 +78,7 @@ export const WORKER_RENDER_APPLICATION_COMMON: Array<any /*Type | Provider | any
|
||||
new Provider(EVENT_MANAGER_PLUGINS, {useClass: DomEventsPlugin, multi: true}),
|
||||
new Provider(EVENT_MANAGER_PLUGINS, {useClass: KeyEventsPlugin, multi: true}),
|
||||
new Provider(EVENT_MANAGER_PLUGINS, {useClass: HammerGesturesPlugin, multi: true}),
|
||||
new Provider(HAMMER_GESTURE_CONFIG, {useClass: HammerGestureConfig}),
|
||||
new Provider(DomRootRenderer, {useClass: DomRootRenderer_}),
|
||||
new Provider(RootRenderer, {useExisting: DomRootRenderer}),
|
||||
new Provider(SharedStylesHost, {useExisting: DomSharedStylesHost}),
|
||||
|
@ -310,7 +310,7 @@ export class ComponentInstruction {
|
||||
*/
|
||||
constructor(public urlPath: string, public urlParams: string[], data: RouteData,
|
||||
public componentType, public terminal: boolean, public specificity: string,
|
||||
public params: {[key: string]: string} = null) {
|
||||
public params: {[key: string]: string} = null, public routeName: string) {
|
||||
this.routeData = isPresent(data) ? data : BLANK_ROUTE_DATA;
|
||||
}
|
||||
}
|
||||
|
@ -486,7 +486,7 @@ export class RouteRegistry {
|
||||
* Given: ['/a/b', {c: 2}]
|
||||
* Returns: ['', 'a', 'b', {c: 2}]
|
||||
*/
|
||||
function splitAndFlattenLinkParams(linkParams: any[]) {
|
||||
function splitAndFlattenLinkParams(linkParams: any[]): any[] {
|
||||
var accumulation = [];
|
||||
linkParams.forEach(function(item: any) {
|
||||
if (isString(item)) {
|
||||
|
@ -135,12 +135,33 @@ export class Router {
|
||||
*/
|
||||
isRouteActive(instruction: Instruction): boolean {
|
||||
var router: Router = this;
|
||||
|
||||
if (isBlank(this.currentInstruction)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// `instruction` corresponds to the root router
|
||||
while (isPresent(router.parent) && isPresent(instruction.child)) {
|
||||
router = router.parent;
|
||||
instruction = instruction.child;
|
||||
}
|
||||
return isPresent(this.currentInstruction) &&
|
||||
this.currentInstruction.component == instruction.component;
|
||||
|
||||
if (isBlank(instruction.component) || isBlank(this.currentInstruction.component) ||
|
||||
this.currentInstruction.component.routeName != instruction.component.routeName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let paramEquals = true;
|
||||
|
||||
if (isPresent(this.currentInstruction.component.params)) {
|
||||
StringMapWrapper.forEach(instruction.component.params, (value, key) => {
|
||||
if (this.currentInstruction.component.params[key] !== value) {
|
||||
paramEquals = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return paramEquals;
|
||||
}
|
||||
|
||||
|
||||
@ -260,6 +281,7 @@ export class Router {
|
||||
}
|
||||
|
||||
private _emitNavigationFinish(url): void { ObservableWrapper.callEmit(this._subject, url); }
|
||||
/** @internal */
|
||||
_emitNavigationFail(url): void { ObservableWrapper.callError(this._subject, url); }
|
||||
|
||||
private _afterPromiseFinishNavigating(promise: Promise<any>): Promise<any> {
|
||||
@ -317,7 +339,9 @@ export class Router {
|
||||
return false;
|
||||
}
|
||||
if (isPresent(this._childRouter)) {
|
||||
return this._childRouter._routerCanDeactivate(childInstruction);
|
||||
// TODO: ideally, this closure would map to async-await in Dart.
|
||||
// For now, casting to any to suppress an error.
|
||||
return <any>this._childRouter._routerCanDeactivate(childInstruction);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
@ -465,9 +489,11 @@ export class RootRouter extends Router {
|
||||
emitPath = '/' + emitPath;
|
||||
}
|
||||
|
||||
// Because we've opted to use All hashchange events occur outside Angular.
|
||||
// We've opted to use pushstate and popState APIs regardless of whether you
|
||||
// an app uses HashLocationStrategy or PathLocationStrategy.
|
||||
// However, apps that are migrating might have hash links that operate outside
|
||||
// angular to which routing must respond.
|
||||
// Therefore we know that all hashchange events occur outside Angular.
|
||||
// To support these cases where we respond to hashchanges and redirect as a
|
||||
// result, we need to replace the top item on the stack.
|
||||
if (change['type'] == 'hashchange') {
|
||||
|
@ -36,5 +36,5 @@ export class RegexRoutePath implements RoutePath {
|
||||
|
||||
generateUrl(params: {[key: string]: any}): GeneratedUrl { return this._serializer(params); }
|
||||
|
||||
toString() { return this._reString; }
|
||||
toString(): string { return this._reString; }
|
||||
}
|
@ -59,7 +59,7 @@ export class RuleSet {
|
||||
if (config instanceof AuxRoute) {
|
||||
handler = new SyncRouteHandler(config.component, config.data);
|
||||
let routePath = this._getRoutePath(config);
|
||||
let auxRule = new RouteRule(routePath, handler);
|
||||
let auxRule = new RouteRule(routePath, handler, config.name);
|
||||
this.auxRulesByPath.set(routePath.toString(), auxRule);
|
||||
if (isPresent(config.name)) {
|
||||
this.auxRulesByName.set(config.name, auxRule);
|
||||
@ -85,7 +85,7 @@ export class RuleSet {
|
||||
useAsDefault = isPresent(config.useAsDefault) && config.useAsDefault;
|
||||
}
|
||||
let routePath = this._getRoutePath(config);
|
||||
let newRule = new RouteRule(routePath, handler);
|
||||
let newRule = new RouteRule(routePath, handler, config.name);
|
||||
|
||||
this._assertNoHashCollision(newRule.hash, config.path);
|
||||
|
||||
|
@ -69,7 +69,8 @@ export class RouteRule implements AbstractRule {
|
||||
|
||||
// TODO: cache component instruction instances by params and by ParsedUrl instance
|
||||
|
||||
constructor(private _routePath: RoutePath, public handler: RouteHandler) {
|
||||
constructor(private _routePath: RoutePath, public handler: RouteHandler,
|
||||
private _routeName: string) {
|
||||
this.specificity = this._routePath.specificity;
|
||||
this.hash = this._routePath.hash;
|
||||
this.terminal = this._routePath.terminal;
|
||||
@ -112,7 +113,7 @@ export class RouteRule implements AbstractRule {
|
||||
}
|
||||
var instruction =
|
||||
new ComponentInstruction(urlPath, urlParams, this.handler.data, this.handler.componentType,
|
||||
this.terminal, this.specificity, params);
|
||||
this.terminal, this.specificity, params, this._routeName);
|
||||
this._cache.set(hashKey, instruction);
|
||||
|
||||
return instruction;
|
||||
|
@ -84,6 +84,11 @@ function matchUrlSegment(str: string): string {
|
||||
var match = RegExpWrapper.firstMatch(SEGMENT_RE, str);
|
||||
return isPresent(match) ? match[0] : '';
|
||||
}
|
||||
var QUERY_PARAM_VALUE_RE = RegExpWrapper.create('^[^\\(\\)\\?;&#]+');
|
||||
function matchUrlQueryParamValue(str: string): string {
|
||||
var match = RegExpWrapper.firstMatch(QUERY_PARAM_VALUE_RE, str);
|
||||
return isPresent(match) ? match[0] : '';
|
||||
}
|
||||
|
||||
export class UrlParser {
|
||||
private _remaining: string;
|
||||
@ -163,10 +168,10 @@ export class UrlParser {
|
||||
parseQueryParams(): {[key: string]: any} {
|
||||
var params: {[key: string]: any} = {};
|
||||
this.capture('?');
|
||||
this.parseParam(params);
|
||||
this.parseQueryParam(params);
|
||||
while (this._remaining.length > 0 && this.peekStartsWith('&')) {
|
||||
this.capture('&');
|
||||
this.parseParam(params);
|
||||
this.parseQueryParam(params);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
@ -199,6 +204,25 @@ export class UrlParser {
|
||||
params[key] = value;
|
||||
}
|
||||
|
||||
parseQueryParam(params: {[key: string]: any}): void {
|
||||
var key = matchUrlSegment(this._remaining);
|
||||
if (isBlank(key)) {
|
||||
return;
|
||||
}
|
||||
this.capture(key);
|
||||
var value: any = true;
|
||||
if (this.peekStartsWith('=')) {
|
||||
this.capture('=');
|
||||
var valueMatch = matchUrlQueryParamValue(this._remaining);
|
||||
if (isPresent(valueMatch)) {
|
||||
value = valueMatch;
|
||||
this.capture(value);
|
||||
}
|
||||
}
|
||||
|
||||
params[key] = value;
|
||||
}
|
||||
|
||||
parseAuxiliaryRoutes(): Url[] {
|
||||
var routes: Url[] = [];
|
||||
this.capture('(');
|
||||
|
@ -131,16 +131,17 @@ function _it(jsmFn: Function, name: string, testFn: FunctionWithParamTokens | An
|
||||
var timeOut = testTimeOut;
|
||||
|
||||
if (testFn instanceof FunctionWithParamTokens) {
|
||||
let testFnT = testFn;
|
||||
jsmFn(name, (done) => {
|
||||
var returnedTestValue;
|
||||
try {
|
||||
returnedTestValue = testInjector.execute(testFn);
|
||||
returnedTestValue = testInjector.execute(testFnT);
|
||||
} catch (err) {
|
||||
done.fail(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (testFn.isAsync) {
|
||||
if (testFnT.isAsync) {
|
||||
if (_isPromiseLike(returnedTestValue)) {
|
||||
(<Promise<any>>returnedTestValue).then(() => { done(); }, (err) => { done.fail(err); });
|
||||
} else {
|
||||
@ -178,16 +179,17 @@ export function beforeEach(fn: FunctionWithParamTokens | AnyTestFn): void {
|
||||
if (fn instanceof FunctionWithParamTokens) {
|
||||
// The test case uses inject(). ie `beforeEach(inject([ClassA], (a) => { ...
|
||||
// }));`
|
||||
let fnT = fn;
|
||||
jsmBeforeEach((done) => {
|
||||
|
||||
var returnedTestValue;
|
||||
try {
|
||||
returnedTestValue = testInjector.execute(fn);
|
||||
returnedTestValue = testInjector.execute(fnT);
|
||||
} catch (err) {
|
||||
done.fail(err);
|
||||
return;
|
||||
}
|
||||
if (fn.isAsync) {
|
||||
if (fnT.isAsync) {
|
||||
if (_isPromiseLike(returnedTestValue)) {
|
||||
(<Promise<any>>returnedTestValue).then(() => { done(); }, (err) => { done.fail(err); });
|
||||
} else {
|
||||
|
@ -135,6 +135,7 @@ function _it(jsmFn: Function, name: string, testFn: FunctionWithParamTokens | An
|
||||
if (testFn instanceof FunctionWithParamTokens) {
|
||||
// The test case uses inject(). ie `it('test', inject([AsyncTestCompleter], (async) => { ...
|
||||
// }));`
|
||||
let testFnT = testFn;
|
||||
|
||||
if (testFn.hasToken(AsyncTestCompleter)) {
|
||||
jsmFn(name, (done) => {
|
||||
@ -150,13 +151,13 @@ function _it(jsmFn: Function, name: string, testFn: FunctionWithParamTokens | An
|
||||
runner.run();
|
||||
|
||||
inIt = true;
|
||||
testInjector.execute(testFn);
|
||||
testInjector.execute(testFnT);
|
||||
inIt = false;
|
||||
}, timeOut);
|
||||
} else {
|
||||
jsmFn(name, () => {
|
||||
runner.run();
|
||||
testInjector.execute(testFn);
|
||||
testInjector.execute(testFnT);
|
||||
}, timeOut);
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
export interface IModule {
|
||||
config(fn: any): IModule;
|
||||
directive(selector: string, factory: any): IModule;
|
||||
component(selector: string, component: IComponent): IModule;
|
||||
controller(name: string, type: any): IModule;
|
||||
factory(key: string, factoryFn: any): IModule;
|
||||
value(key: string, value: any): IModule;
|
||||
@ -59,6 +60,15 @@ export interface IDirectiveLinkFn {
|
||||
(scope: IScope, instanceElement: IAugmentedJQuery, instanceAttributes: IAttributes,
|
||||
controller: any, transclude: ITranscludeFunction): void;
|
||||
}
|
||||
export interface IComponent {
|
||||
bindings?: Object;
|
||||
controller?: any;
|
||||
controllerAs?: string;
|
||||
require?: any;
|
||||
template?: any;
|
||||
templateUrl?: any;
|
||||
transclude?: any;
|
||||
}
|
||||
export interface IAttributes { $observe(attr: string, fn: (v: string) => void): void; }
|
||||
export interface ITranscludeFunction {
|
||||
// If the scope is provided, then the cloneAttachFn must be as well.
|
||||
|
@ -160,7 +160,7 @@ export class DowngradeNg2ComponentAdapter {
|
||||
}
|
||||
|
||||
registerCleanup() {
|
||||
this.element.bind('$remove', () => this.viewManager.destroyRootHostView(this.hostViewRef));
|
||||
this.element.bind('$destroy', () => this.viewManager.destroyRootHostView(this.hostViewRef));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,6 +53,7 @@ export class UpgradeNg1ComponentAdapterBuilder {
|
||||
self.outputs, self.propertyOutputs, self.checkProperties, self.propertyMap);
|
||||
}
|
||||
],
|
||||
ngOnInit: function() { /* needs to be here for ng2 to properly detect it */ },
|
||||
ngOnChanges: function() { /* needs to be here for ng2 to properly detect it */ },
|
||||
ngDoCheck: function() { /* needs to be here for ng2 to properly detect it */ }
|
||||
});
|
||||
@ -106,6 +107,8 @@ export class UpgradeNg1ComponentAdapterBuilder {
|
||||
this.propertyMap[outputName] = localName;
|
||||
// don't break; let it fall through to '@'
|
||||
case '@':
|
||||
// handle the '<' binding of angular 1.5 components
|
||||
case '<':
|
||||
this.inputs.push(inputName);
|
||||
this.inputsRename.push(inputNameRename);
|
||||
this.propertyMap[inputName] = localName;
|
||||
@ -231,6 +234,13 @@ class UpgradeNg1ComponentAdapter implements OnChanges, DoCheck {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ngOnInit() {
|
||||
if (this.destinationObj.$onInit) {
|
||||
this.destinationObj.$onInit();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: {[name: string]: SimpleChange}) {
|
||||
for (var name in changes) {
|
||||
if ((<Object>changes).hasOwnProperty(name)) {
|
||||
|
@ -298,6 +298,25 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display first item correctly',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template =
|
||||
'<div><copy-me template="ngFor: var item of items; var isFirst=first">{{isFirst.toString()}}</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.items = [0, 1, 2];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('truefalsefalse');
|
||||
|
||||
fixture.debugElement.componentInstance.items = [2, 1];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('truefalse');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display last item correctly',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template =
|
||||
|
@ -94,14 +94,14 @@ export function main() {
|
||||
});
|
||||
|
||||
it("should return select accessor when provided", () => {
|
||||
var selectAccessor = new SelectControlValueAccessor(null, null, new QueryList<any>());
|
||||
var selectAccessor = new SelectControlValueAccessor(null, null);
|
||||
expect(selectValueAccessor(dir, [defaultAccessor, selectAccessor]))
|
||||
.toEqual(selectAccessor);
|
||||
});
|
||||
|
||||
it("should throw when more than one build-in accessor is provided", () => {
|
||||
var checkboxAccessor = new CheckboxControlValueAccessor(null, null);
|
||||
var selectAccessor = new SelectControlValueAccessor(null, null, new QueryList<any>());
|
||||
var selectAccessor = new SelectControlValueAccessor(null, null);
|
||||
expect(() => selectValueAccessor(dir, [checkboxAccessor, selectAccessor])).toThrowError();
|
||||
});
|
||||
|
||||
|
@ -38,7 +38,7 @@ import {
|
||||
import {Provider, forwardRef, Input} from 'angular2/core';
|
||||
import {By} from 'angular2/platform/browser';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {ObservableWrapper, TimerWrapper} from 'angular2/src/facade/async';
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {PromiseWrapper} from "angular2/src/facade/promise";
|
||||
|
||||
@ -357,56 +357,279 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
it("should support <select>",
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var t = `<div [ngFormModel]="form">
|
||||
describe("should support <select>", () => {
|
||||
it("with basic selection",
|
||||
inject([TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async) => {
|
||||
var t = `<select>
|
||||
<option value="SF"></option>
|
||||
<option value="NYC"></option>
|
||||
</select>`;
|
||||
|
||||
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
|
||||
var select = fixture.debugElement.query(By.css("select"));
|
||||
var sfOption = fixture.debugElement.query(By.css("option"));
|
||||
|
||||
expect(select.nativeElement.value).toEqual("SF");
|
||||
expect(sfOption.nativeElement.selected).toBe(true);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("with basic selection and value bindings",
|
||||
inject([TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async) => {
|
||||
var t = `<select>
|
||||
<option *ngFor="#city of list" [value]="city['id']">
|
||||
{{ city['name'] }}
|
||||
</option>
|
||||
</select>`;
|
||||
|
||||
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((fixture) => {
|
||||
var testComp = fixture.debugElement.componentInstance;
|
||||
testComp.list = [{"id": "0", "name": "SF"}, {"id": "1", "name": "NYC"}];
|
||||
fixture.detectChanges();
|
||||
|
||||
var sfOption = fixture.debugElement.query(By.css("option"));
|
||||
expect(sfOption.nativeElement.value).toEqual('0');
|
||||
|
||||
testComp.list[0]['id'] = '2';
|
||||
fixture.detectChanges();
|
||||
expect(sfOption.nativeElement.value).toEqual('2');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("with ngControl",
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder,
|
||||
async) => {
|
||||
var t = `<div [ngFormModel]="form">
|
||||
<select ngControl="city">
|
||||
<option value="SF"></option>
|
||||
<option value="NYC"></option>
|
||||
</select>
|
||||
</div>`;
|
||||
|
||||
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((fixture) => {
|
||||
fixture.debugElement.componentInstance.form =
|
||||
new ControlGroup({"city": new Control("SF")});
|
||||
fixture.detectChanges();
|
||||
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((fixture) => {
|
||||
fixture.debugElement.componentInstance.form =
|
||||
new ControlGroup({"city": new Control("SF")});
|
||||
fixture.detectChanges();
|
||||
|
||||
var select = fixture.debugElement.query(By.css("select"));
|
||||
var sfOption = fixture.debugElement.query(By.css("option"));
|
||||
expect(select.nativeElement.value).toEqual('SF');
|
||||
expect(sfOption.nativeElement.selected).toBe(true);
|
||||
var select = fixture.debugElement.query(By.css("select"));
|
||||
var sfOption = fixture.debugElement.query(By.css("option"));
|
||||
|
||||
select.nativeElement.value = 'NYC';
|
||||
dispatchEvent(select.nativeElement, "input");
|
||||
|
||||
expect(fixture.debugElement.componentInstance.form.value).toEqual({"city": 'NYC'});
|
||||
expect(sfOption.nativeElement.selected).toBe(false);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
expect(select.nativeElement.value).toEqual("SF");
|
||||
expect(sfOption.nativeElement.selected).toBe(true);
|
||||
|
||||
it("should support <select> with a dynamic list of options",
|
||||
inject([TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => {
|
||||
var t = `<div [ngFormModel]="form">
|
||||
select.nativeElement.value = "NYC";
|
||||
dispatchEvent(select.nativeElement, "input");
|
||||
|
||||
expect(fixture.debugElement.componentInstance.form.value).toEqual({"city": 'NYC'});
|
||||
expect(sfOption.nativeElement.selected).toBe(false);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("with a dynamic list of options",
|
||||
inject([TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => {
|
||||
var t = `<div [ngFormModel]="form">
|
||||
<select ngControl="city">
|
||||
<option *ngFor="#c of data" [value]="c"></option>
|
||||
</select>
|
||||
</div>`;
|
||||
|
||||
var fixture;
|
||||
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then(
|
||||
(compFixture) => fixture = compFixture);
|
||||
tick();
|
||||
var fixture;
|
||||
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then(
|
||||
(compFixture) => fixture = compFixture);
|
||||
tick();
|
||||
|
||||
fixture.debugElement.componentInstance.form =
|
||||
new ControlGroup({"city": new Control("NYC")});
|
||||
fixture.debugElement.componentInstance.data = ['SF', 'NYC'];
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
fixture.debugElement.componentInstance.form =
|
||||
new ControlGroup({"city": new Control("NYC")});
|
||||
|
||||
var select = fixture.debugElement.query(By.css('select'));
|
||||
expect(select.nativeElement.value).toEqual('NYC');
|
||||
})));
|
||||
fixture.debugElement.componentInstance.data = ['SF', 'NYC'];
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
var select = fixture.debugElement.query(By.css("select"));
|
||||
expect(select.nativeElement.value).toEqual("NYC");
|
||||
})));
|
||||
|
||||
it("with option values that are objects",
|
||||
inject([TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async) => {
|
||||
var t = `<div>
|
||||
<select [(ngModel)]="selectedCity">
|
||||
<option *ngFor="#c of list" [ngValue]="c">{{c['name']}}</option>
|
||||
</select>
|
||||
</div>`;
|
||||
|
||||
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((fixture) => {
|
||||
|
||||
var testComp = fixture.debugElement.componentInstance;
|
||||
testComp.list = [{"name": "SF"}, {"name": "NYC"}, {"name": "Buffalo"}];
|
||||
testComp.selectedCity = testComp.list[1];
|
||||
fixture.detectChanges();
|
||||
|
||||
var select = fixture.debugElement.query(By.css("select"));
|
||||
var nycOption = fixture.debugElement.queryAll(By.css("option"))[1];
|
||||
|
||||
expect(select.nativeElement.value).toEqual("1: Object");
|
||||
expect(nycOption.nativeElement.selected).toBe(true);
|
||||
|
||||
select.nativeElement.value = "2: Object";
|
||||
dispatchEvent(select.nativeElement, "input");
|
||||
fixture.detectChanges();
|
||||
TimerWrapper.setTimeout(() => {
|
||||
expect(testComp.selectedCity['name']).toEqual("Buffalo");
|
||||
async.done();
|
||||
}, 0);
|
||||
});
|
||||
}));
|
||||
|
||||
it("when new options are added",
|
||||
inject([TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async) => {
|
||||
var t = `<div>
|
||||
<select [(ngModel)]="selectedCity">
|
||||
<option *ngFor="#c of list" [ngValue]="c">{{c['name']}}</option>
|
||||
</select>
|
||||
</div>`;
|
||||
|
||||
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((fixture) => {
|
||||
|
||||
var testComp: MyComp = fixture.debugElement.componentInstance;
|
||||
testComp.list = [{"name": "SF"}, {"name": "NYC"}];
|
||||
testComp.selectedCity = testComp.list[1];
|
||||
fixture.detectChanges();
|
||||
|
||||
testComp.list.push({"name": "Buffalo"});
|
||||
testComp.selectedCity = testComp.list[2];
|
||||
fixture.detectChanges();
|
||||
|
||||
var select = fixture.debugElement.query(By.css("select"));
|
||||
var buffalo = fixture.debugElement.queryAll(By.css("option"))[2];
|
||||
expect(select.nativeElement.value).toEqual("2: Object");
|
||||
expect(buffalo.nativeElement.selected).toBe(true);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("when options are removed",
|
||||
inject([TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async) => {
|
||||
var t = `<div>
|
||||
<select [(ngModel)]="selectedCity">
|
||||
<option *ngFor="#c of list" [ngValue]="c">{{c}}</option>
|
||||
</select>
|
||||
</div>`;
|
||||
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((fixture) => {
|
||||
|
||||
var testComp: MyComp = fixture.debugElement.componentInstance;
|
||||
testComp.list = [{"name": "SF"}, {"name": "NYC"}];
|
||||
testComp.selectedCity = testComp.list[1];
|
||||
fixture.detectChanges();
|
||||
|
||||
var select = fixture.debugElement.query(By.css("select"));
|
||||
expect(select.nativeElement.value).toEqual("1: Object");
|
||||
|
||||
testComp.list.pop();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(select.nativeElement.value).not.toEqual("1: Object");
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("when option values change identity while tracking by index",
|
||||
inject([TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async) => {
|
||||
var t = `<div>
|
||||
<select [(ngModel)]="selectedCity">
|
||||
<option *ngFor="#c of list; trackBy:customTrackBy" [ngValue]="c">{{c}}</option>
|
||||
</select>
|
||||
</div>`;
|
||||
|
||||
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((fixture) => {
|
||||
|
||||
var testComp = fixture.debugElement.componentInstance;
|
||||
|
||||
testComp.list = [{"name": "SF"}, {"name": "NYC"}];
|
||||
testComp.selectedCity = testComp.list[0];
|
||||
fixture.detectChanges();
|
||||
|
||||
testComp.list[1] = "Buffalo";
|
||||
testComp.selectedCity = testComp.list[1];
|
||||
fixture.detectChanges();
|
||||
|
||||
var select = fixture.debugElement.query(By.css("select"));
|
||||
var buffalo = fixture.debugElement.queryAll(By.css("option"))[1];
|
||||
|
||||
expect(select.nativeElement.value).toEqual("1: Buffalo");
|
||||
expect(buffalo.nativeElement.selected).toBe(true);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("with duplicate option values",
|
||||
inject([TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async) => {
|
||||
var t = `<div>
|
||||
<select [(ngModel)]="selectedCity">
|
||||
<option *ngFor="#c of list" [ngValue]="c">{{c}}</option>
|
||||
</select>
|
||||
</div>`;
|
||||
|
||||
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((fixture) => {
|
||||
|
||||
var testComp = fixture.debugElement.componentInstance;
|
||||
|
||||
testComp.list = [{"name": "NYC"}, {"name": "SF"}, {"name": "SF"}];
|
||||
testComp.selectedCity = testComp.list[0];
|
||||
fixture.detectChanges();
|
||||
|
||||
testComp.selectedCity = testComp.list[1];
|
||||
fixture.detectChanges();
|
||||
|
||||
var select = fixture.debugElement.query(By.css("select"));
|
||||
var firstSF = fixture.debugElement.queryAll(By.css("option"))[1];
|
||||
|
||||
expect(select.nativeElement.value).toEqual("1: Object");
|
||||
expect(firstSF.nativeElement.selected).toBe(true);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("when option values have same content, but different identities",
|
||||
inject([TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async) => {
|
||||
var t = `<div>
|
||||
<select [(ngModel)]="selectedCity">
|
||||
<option *ngFor="#c of list" [ngValue]="c">{{c['name']}}</option>
|
||||
</select>
|
||||
</div>`;
|
||||
|
||||
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((fixture) => {
|
||||
|
||||
var testComp = fixture.debugElement.componentInstance;
|
||||
testComp.list = [{"name": "SF"}, {"name": "NYC"}, {"name": "NYC"}];
|
||||
testComp.selectedCity = testComp.list[0];
|
||||
fixture.detectChanges();
|
||||
|
||||
testComp.selectedCity = testComp.list[2];
|
||||
fixture.detectChanges();
|
||||
|
||||
var select = fixture.debugElement.query(By.css("select"));
|
||||
var secondNYC = fixture.debugElement.queryAll(By.css("option"))[2];
|
||||
|
||||
expect(select.nativeElement.value).toEqual("2: Object");
|
||||
expect(secondNYC.nativeElement.selected).toBe(true);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
it("should support custom value accessors",
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
@ -849,7 +1072,6 @@ export function main() {
|
||||
<input type="radio" name="food" ngControl="chicken" [(ngModel)]="data['chicken1']">
|
||||
<input type="radio" name="food" ngControl="fish" [(ngModel)]="data['fish1']">
|
||||
</form>
|
||||
|
||||
<form>
|
||||
<input type="radio" name="food" ngControl="chicken" [(ngModel)]="data['chicken2']">
|
||||
<input type="radio" name="food" ngControl="fish" [(ngModel)]="data['fish2']">
|
||||
@ -1134,6 +1356,9 @@ class MyComp {
|
||||
form: any;
|
||||
name: string;
|
||||
data: any;
|
||||
list: any[];
|
||||
selectedCity: any;
|
||||
customTrackBy(index: number, obj: any): number { return index; };
|
||||
}
|
||||
|
||||
function sortedClassList(el) {
|
||||
|
@ -18,7 +18,9 @@ import {
|
||||
CompileTemplateMetadata,
|
||||
CompileProviderMetadata,
|
||||
CompileDiDependencyMetadata,
|
||||
CompileQueryMetadata
|
||||
CompileQueryMetadata,
|
||||
CompileIdentifierMetadata,
|
||||
CompileFactoryMetadata
|
||||
} from 'angular2/src/compiler/directive_metadata';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
||||
import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection';
|
||||
@ -31,25 +33,21 @@ export function main() {
|
||||
var fullDirectiveMeta: CompileDirectiveMetadata;
|
||||
|
||||
beforeEach(() => {
|
||||
fullTypeMeta = new CompileTypeMetadata({
|
||||
name: 'SomeType',
|
||||
moduleUrl: 'someUrl',
|
||||
var diDep = new CompileDiDependencyMetadata({
|
||||
isAttribute: true,
|
||||
isSelf: true,
|
||||
isHost: true,
|
||||
diDeps: [
|
||||
new CompileDiDependencyMetadata({
|
||||
isAttribute: true,
|
||||
isSelf: true,
|
||||
isHost: true,
|
||||
isSkipSelf: true,
|
||||
isOptional: true,
|
||||
token: 'someToken',
|
||||
query: new CompileQueryMetadata(
|
||||
{selectors: ['one'], descendants: true, first: true, propertyName: 'one'}),
|
||||
viewQuery: new CompileQueryMetadata(
|
||||
{selectors: ['one'], descendants: true, first: true, propertyName: 'one'})
|
||||
})
|
||||
]
|
||||
isSkipSelf: true,
|
||||
isOptional: true,
|
||||
token: 'someToken',
|
||||
query: new CompileQueryMetadata(
|
||||
{selectors: ['one'], descendants: true, first: true, propertyName: 'one'}),
|
||||
viewQuery: new CompileQueryMetadata(
|
||||
{selectors: ['one'], descendants: true, first: true, propertyName: 'one'})
|
||||
});
|
||||
|
||||
fullTypeMeta = new CompileTypeMetadata(
|
||||
{name: 'SomeType', moduleUrl: 'someUrl', isHost: true, diDeps: [diDep]});
|
||||
fullTemplateMeta = new CompileTemplateMetadata({
|
||||
encapsulation: ViewEncapsulation.Emulated,
|
||||
template: '<a></a>',
|
||||
@ -69,11 +67,49 @@ export function main() {
|
||||
outputs: ['someEvent'],
|
||||
host: {'(event1)': 'handler1', '[prop1]': 'expr1', 'attr1': 'attrValue2'},
|
||||
lifecycleHooks: [LifecycleHooks.OnChanges],
|
||||
providers: [new CompileProviderMetadata({token: 'token', useClass: fullTypeMeta})]
|
||||
providers: [
|
||||
new CompileProviderMetadata({
|
||||
token: 'token',
|
||||
useClass: fullTypeMeta,
|
||||
useExisting: new CompileIdentifierMetadata({name: 'someName'}),
|
||||
useFactory: new CompileFactoryMetadata({name: 'someName', diDeps: [diDep]}),
|
||||
useValue: 'someValue',
|
||||
})
|
||||
],
|
||||
viewProviders: [
|
||||
new CompileProviderMetadata({
|
||||
token: 'token',
|
||||
useClass: fullTypeMeta,
|
||||
useExisting: new CompileIdentifierMetadata({name: 'someName'}),
|
||||
useFactory: new CompileFactoryMetadata({name: 'someName', diDeps: [diDep]}),
|
||||
useValue: 'someValue',
|
||||
})
|
||||
],
|
||||
queries: [
|
||||
new CompileQueryMetadata(
|
||||
{selectors: ['selector'], descendants: true, first: false, propertyName: 'prop'})
|
||||
],
|
||||
viewQueries: [
|
||||
new CompileQueryMetadata(
|
||||
{selectors: ['selector'], descendants: true, first: false, propertyName: 'prop'})
|
||||
]
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('CompileIdentifierMetadata', () => {
|
||||
it('should serialize with full data', () => {
|
||||
let full = new CompileIdentifierMetadata(
|
||||
{name: 'name', moduleUrl: 'module', constConstructor: true, value: ['one', ['two']]});
|
||||
expect(CompileIdentifierMetadata.fromJson(full.toJson())).toEqual(full);
|
||||
});
|
||||
|
||||
it('should serialize with no data', () => {
|
||||
let empty = new CompileIdentifierMetadata();
|
||||
expect(CompileIdentifierMetadata.fromJson(empty.toJson())).toEqual(empty);
|
||||
});
|
||||
});
|
||||
|
||||
describe('DirectiveMetadata', () => {
|
||||
it('should serialize with full data', () => {
|
||||
expect(CompileDirectiveMetadata.fromJson(fullDirectiveMeta.toJson()))
|
||||
|
441
modules/angular2/test/compiler/static_reflector_spec.ts
Normal file
441
modules/angular2/test/compiler/static_reflector_spec.ts
Normal file
@ -0,0 +1,441 @@
|
||||
import {
|
||||
ddescribe,
|
||||
describe,
|
||||
xdescribe,
|
||||
it,
|
||||
iit,
|
||||
xit,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
inject,
|
||||
beforeEachProviders
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
import {StaticReflector, StaticReflectorHost} from 'angular2/src/compiler/static_reflector';
|
||||
|
||||
export function main() {
|
||||
describe('StaticRefelector', () => {
|
||||
it('should get annotations for NgFor', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
let NgFor = reflector.getStaticType('angular2/src/common/directives/ng_for', 'NgFor');
|
||||
let annotations = reflector.annotations(NgFor);
|
||||
expect(annotations.length).toEqual(1);
|
||||
let annotation = annotations[0];
|
||||
expect(annotation.selector).toEqual('[ngFor][ngForOf]');
|
||||
expect(annotation.inputs).toEqual(['ngForTrackBy', 'ngForOf', 'ngForTemplate']);
|
||||
});
|
||||
|
||||
it('should get constructor for NgFor', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
let NgFor = reflector.getStaticType('angular2/src/common/directives/ng_for', 'NgFor');
|
||||
let ViewContainerRef = reflector.getStaticType('angular2/src/core/linker/view_container_ref',
|
||||
'ViewContainerRef');
|
||||
let TemplateRef =
|
||||
reflector.getStaticType('angular2/src/core/linker/template_ref', 'TemplateRef');
|
||||
let IterableDiffers = reflector.getStaticType(
|
||||
'angular2/src/core/change_detection/differs/iterable_differs', 'IterableDiffers');
|
||||
let ChangeDetectorRef = reflector.getStaticType(
|
||||
'angular2/src/core/change_detection/change_detector_ref', 'ChangeDetectorRef');
|
||||
|
||||
let parameters = reflector.parameters(NgFor);
|
||||
expect(parameters)
|
||||
.toEqual([ViewContainerRef, TemplateRef, IterableDiffers, ChangeDetectorRef]);
|
||||
});
|
||||
|
||||
it('should get annotations for HeroDetailComponent', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
let HeroDetailComponent =
|
||||
reflector.getStaticType('./app/hero-detail.component', 'HeroDetailComponent');
|
||||
let annotations = reflector.annotations(HeroDetailComponent);
|
||||
expect(annotations.length).toEqual(1);
|
||||
let annotation = annotations[0];
|
||||
expect(annotation.selector).toEqual('my-hero-detail');
|
||||
});
|
||||
|
||||
it('should get propMetadata for HeroDetailComponent', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
let HeroDetailComponent =
|
||||
reflector.getStaticType('./app/hero-detail.component', 'HeroDetailComponent');
|
||||
let props = reflector.propMetadata(HeroDetailComponent);
|
||||
expect(props['hero']).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should simplify primitive into itself', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
expect(reflector.simplify('', 1)).toBe(1);
|
||||
expect(reflector.simplify('', true)).toBe(true);
|
||||
expect(reflector.simplify('', "some value")).toBe("some value");
|
||||
});
|
||||
|
||||
it('should simplify an array into a copy of the array', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
expect(reflector.simplify('', [1, 2, 3])).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it('should simplify an object to a copy of the object', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
let expr = {a: 1, b: 2, c: 3};
|
||||
expect(reflector.simplify('', expr)).toEqual(expr);
|
||||
});
|
||||
|
||||
it('should simplify &&', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '&&', left: true, right: true}))).toBe(true);
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '&&', left: true, right: false}))).toBe(false);
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '&&', left: false, right: true}))).toBe(false);
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '&&', left: false, right: false}))).toBe(false);
|
||||
});
|
||||
|
||||
it('should simplify ||', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '||', left: true, right: true}))).toBe(true);
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '||', left: true, right: false}))).toBe(true);
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '||', left: false, right: true}))).toBe(true);
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '||', left: false, right: false}))).toBe(false);
|
||||
});
|
||||
|
||||
it('should simplify &', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '&', left: 0x22, right: 0x0F}))).toBe(0x22 & 0x0F);
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '&', left: 0x22, right: 0xF0}))).toBe(0x22 & 0xF0);
|
||||
});
|
||||
|
||||
it('should simplify |', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '|', left: 0x22, right: 0x0F}))).toBe(0x22 | 0x0F);
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '|', left: 0x22, right: 0xF0}))).toBe(0x22 | 0xF0);
|
||||
});
|
||||
|
||||
it('should simplify ^', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '|', left: 0x22, right: 0x0F}))).toBe(0x22 | 0x0F);
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '|', left: 0x22, right: 0xF0}))).toBe(0x22 | 0xF0);
|
||||
});
|
||||
|
||||
it('should simplify ==', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '==', left: 0x22, right: 0x22}))).toBe(0x22 == 0x22);
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '==', left: 0x22, right: 0xF0}))).toBe(0x22 == 0xF0);
|
||||
});
|
||||
|
||||
it('should simplify !=', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '!=', left: 0x22, right: 0x22}))).toBe(0x22 != 0x22);
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '!=', left: 0x22, right: 0xF0}))).toBe(0x22 != 0xF0);
|
||||
});
|
||||
|
||||
it('should simplify ===', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '===', left: 0x22, right: 0x22}))).toBe(0x22 === 0x22);
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '===', left: 0x22, right: 0xF0}))).toBe(0x22 === 0xF0);
|
||||
});
|
||||
|
||||
it('should simplify !==', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '!==', left: 0x22, right: 0x22}))).toBe(0x22 !== 0x22);
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '!==', left: 0x22, right: 0xF0}))).toBe(0x22 !== 0xF0);
|
||||
});
|
||||
|
||||
it('should simplify >', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '>', left: 1, right: 1}))).toBe(1 > 1);
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '>', left: 1, right: 0}))).toBe(1 > 0);
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '>', left: 0, right: 1}))).toBe(0 > 1);
|
||||
});
|
||||
|
||||
it('should simplify >=', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '>=', left: 1, right: 1}))).toBe(1 >= 1);
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '>=', left: 1, right: 0}))).toBe(1 >= 0);
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '>=', left: 0, right: 1}))).toBe(0 >= 1);
|
||||
});
|
||||
|
||||
it('should simplify <=', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '<=', left: 1, right: 1}))).toBe(1 <= 1);
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '<=', left: 1, right: 0}))).toBe(1 <= 0);
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '<=', left: 0, right: 1}))).toBe(0 <= 1);
|
||||
});
|
||||
|
||||
it('should simplify <', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '<', left: 1, right: 1}))).toBe(1 < 1);
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '<', left: 1, right: 0}))).toBe(1 < 0);
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '<', left: 0, right: 1}))).toBe(0 < 1);
|
||||
});
|
||||
|
||||
it('should simplify <<', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '<<', left: 0x55, right: 2}))).toBe(0x55 << 2);
|
||||
});
|
||||
|
||||
it('should simplify >>', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '>>', left: 0x55, right: 2}))).toBe(0x55 >> 2);
|
||||
});
|
||||
|
||||
it('should simplify +', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '+', left: 0x55, right: 2}))).toBe(0x55 + 2);
|
||||
});
|
||||
|
||||
it('should simplify -', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '-', left: 0x55, right: 2}))).toBe(0x55 - 2);
|
||||
});
|
||||
|
||||
it('should simplify *', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '*', left: 0x55, right: 2}))).toBe(0x55 * 2);
|
||||
});
|
||||
|
||||
it('should simplify /', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '/', left: 0x55, right: 2}))).toBe(0x55 / 2);
|
||||
});
|
||||
|
||||
it('should simplify %', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '%', left: 0x55, right: 2}))).toBe(0x55 % 2);
|
||||
});
|
||||
|
||||
it('should simplify prefix -', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
expect(reflector.simplify('', ({ __symbolic: 'pre', operator: '-', operand: 2}))).toBe(-2);
|
||||
});
|
||||
|
||||
it('should simplify prefix ~', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
expect(reflector.simplify('', ({ __symbolic: 'pre', operator: '~', operand: 2}))).toBe(~2);
|
||||
});
|
||||
|
||||
it('should simplify prefix !', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
expect(reflector.simplify('', ({ __symbolic: 'pre', operator: '!', operand: true}))).toBe(!true);
|
||||
expect(reflector.simplify('', ({ __symbolic: 'pre', operator: '!', operand: false}))).toBe(!false);
|
||||
});
|
||||
|
||||
it('should simpify an array index', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
expect(reflector.simplify('', ({__symbolic: "index", expression: [1, 2, 3], index: 2})))
|
||||
.toBe(3);
|
||||
});
|
||||
|
||||
it('should simplify an object index', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
let expr = {__symbolic: "select", expression: {a: 1, b: 2, c: 3}, member: "b"};
|
||||
expect(reflector.simplify('', expr)).toBe(2);
|
||||
});
|
||||
|
||||
it('should simplify a module reference', () => {
|
||||
let host = new MockReflectorHost();
|
||||
let reflector = new StaticReflector(host);
|
||||
|
||||
expect(
|
||||
reflector.simplify('./cases', ({__symbolic: "reference", module: "./extern", name: "s"})))
|
||||
.toEqual("s");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class MockReflectorHost implements StaticReflectorHost {
|
||||
getMetadataFor(moduleId: string): any {
|
||||
return {
|
||||
'angular2/src/common/directives/ng_for':
|
||||
{
|
||||
"__symbolic": "module",
|
||||
"module": "./ng_for",
|
||||
"metadata":
|
||||
{
|
||||
"NgFor":
|
||||
{
|
||||
"__symbolic": "class",
|
||||
"decorators":
|
||||
[
|
||||
{
|
||||
"__symbolic": "call",
|
||||
"expression": {
|
||||
"__symbolic": "reference",
|
||||
"name": "Directive",
|
||||
"module": "../../core/metadata"
|
||||
},
|
||||
"arguments":
|
||||
[
|
||||
{
|
||||
"selector": "[ngFor][ngForOf]",
|
||||
"inputs": ["ngForTrackBy", "ngForOf", "ngForTemplate"]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"members":
|
||||
{
|
||||
"__ctor__": [
|
||||
{
|
||||
"__symbolic": "constructor",
|
||||
"parameters":
|
||||
[
|
||||
{
|
||||
"__symbolic": "reference",
|
||||
"module": "../../core/linker/view_container_ref",
|
||||
"name": "ViewContainerRef"
|
||||
},
|
||||
{
|
||||
"__symbolic": "reference",
|
||||
"module": "../../core/linker/template_ref",
|
||||
"name": "TemplateRef"
|
||||
},
|
||||
{
|
||||
"__symbolic": "reference",
|
||||
"module":
|
||||
"../../core/change_detection/differs/iterable_differs",
|
||||
"name": "IterableDiffers"
|
||||
},
|
||||
{
|
||||
"__symbolic": "reference",
|
||||
"module":
|
||||
"../../core/change_detection/change_detector_ref",
|
||||
"name": "ChangeDetectorRef"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'angular2/src/core/linker/view_container_ref':
|
||||
{
|
||||
"module": "./view_container_ref",
|
||||
"metadata": {"ViewContainerRef": {"__symbolic": "class"}}
|
||||
},
|
||||
'angular2/src/core/linker/template_ref':
|
||||
{"module": "./template_ref", "metadata": {"TemplateRef": {"__symbolic": "class"}}},
|
||||
'angular2/src/core/change_detection/differs/iterable_differs':
|
||||
{
|
||||
"module": "./iterable_differs",
|
||||
"metadata": {"IterableDiffers": {"__symbolic": "class"}}
|
||||
},
|
||||
'angular2/src/core/change_detection/change_detector_ref':
|
||||
{
|
||||
"module": "./change_detector_ref",
|
||||
"metadata": {"ChangeDetectorRef": {"__symbolic": "class"}}
|
||||
},
|
||||
'./app/hero-detail.component':
|
||||
{
|
||||
"__symbolic": "module",
|
||||
"module": "./hero-detail.component",
|
||||
"metadata":
|
||||
{
|
||||
"HeroDetailComponent":
|
||||
{
|
||||
"__symbolic": "class",
|
||||
"decorators":
|
||||
[
|
||||
{
|
||||
"__symbolic": "call",
|
||||
"expression": {
|
||||
"__symbolic": "reference",
|
||||
"name": "Component",
|
||||
"module": "angular2/src/core/metadata"
|
||||
},
|
||||
"arguments": [
|
||||
{
|
||||
"selector": "my-hero-detail",
|
||||
"template": "\n <div *ngIf=\"hero\">\n <h2>{{hero.name}} details!</h2>\n <div><label>id: </label>{{hero.id}}</div>\n <div>\n <label>name: </label>\n <input [(ngModel)]=\"hero.name\" placeholder=\"name\"/>\n </div>\n </div>\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"members":
|
||||
{
|
||||
"hero": [
|
||||
{
|
||||
"__symbolic": "property",
|
||||
"decorators":
|
||||
[
|
||||
{
|
||||
"__symbolic": "call",
|
||||
"expression":
|
||||
{
|
||||
"__symbolic": "reference",
|
||||
"name": "Input",
|
||||
"module": "angular2/src/core/metadata"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'./extern': {
|
||||
"__symbolic": "module", module: './extern', metadata: { s: "s" }
|
||||
}
|
||||
}
|
||||
[moduleId];
|
||||
}
|
||||
}
|
@ -31,6 +31,7 @@ class ComplexItem {
|
||||
toString() { return `{id: ${this.id}, color: ${this.color}}` }
|
||||
}
|
||||
|
||||
// todo(vicb): UnmodifiableListView / frozen object when implemented
|
||||
export function main() {
|
||||
describe('iterable differ', function() {
|
||||
describe('DefaultIterableDiffer', function() {
|
||||
@ -313,36 +314,6 @@ export function main() {
|
||||
}));
|
||||
});
|
||||
|
||||
it('should not diff immutable collections if they are the same', () => {
|
||||
// Note: Use trackBy to know if diffing happened
|
||||
var trackByCount = 0;
|
||||
var trackBy = (index: number, item: any): any => {
|
||||
trackByCount++;
|
||||
return item;
|
||||
};
|
||||
var differ = new DefaultIterableDiffer(trackBy);
|
||||
var l1 = ListWrapper.createImmutable([1]);
|
||||
|
||||
differ.check(l1);
|
||||
expect(trackByCount).toBe(1);
|
||||
expect(differ.toString())
|
||||
.toEqual(
|
||||
iterableChangesAsString({collection: ['1[null->0]'], additions: ['1[null->0]']}));
|
||||
|
||||
|
||||
trackByCount = 0;
|
||||
differ.check(l1);
|
||||
expect(trackByCount).toBe(0);
|
||||
expect(differ.toString())
|
||||
.toEqual(iterableChangesAsString({collection: ['1'], previous: ['1']}));
|
||||
|
||||
trackByCount = 0;
|
||||
differ.check(l1);
|
||||
expect(trackByCount).toBe(0);
|
||||
expect(differ.toString())
|
||||
.toEqual(iterableChangesAsString({collection: ['1'], previous: ['1']}));
|
||||
});
|
||||
|
||||
describe('diff', () => {
|
||||
it('should return self when there is a change',
|
||||
() => { expect(differ.diff(['a', 'b'])).toBe(differ); });
|
||||
|
16
modules/angular2/test/platform/browser/xhr_cache_setter.dart
Normal file
16
modules/angular2/test/platform/browser/xhr_cache_setter.dart
Normal file
@ -0,0 +1,16 @@
|
||||
import 'dart:js' as js;
|
||||
|
||||
void setTemplateCache(Map cache) {
|
||||
if (cache == null) {
|
||||
if (js.context.hasProperty(r'$templateCache')) {
|
||||
js.context.deleteProperty(r'$templateCache');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
js.JsObject jsMap = new js.JsObject(js.context['Object']);
|
||||
for (String key in cache.keys) {
|
||||
jsMap[key] = cache[key];
|
||||
}
|
||||
js.context[r'$templateCache'] = jsMap;
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
export function setTemplateCache(cache): void {
|
||||
(<any>window).$templateCache = cache;
|
||||
}
|
83
modules/angular2/test/platform/browser/xhr_cache_spec.ts
Normal file
83
modules/angular2/test/platform/browser/xhr_cache_spec.ts
Normal file
@ -0,0 +1,83 @@
|
||||
import {Component, provide} from 'angular2/core';
|
||||
import {UrlResolver, XHR} from 'angular2/compiler';
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
beforeEachProviders,
|
||||
ComponentFixture,
|
||||
ddescribe,
|
||||
describe,
|
||||
expect,
|
||||
fakeAsync,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
TestComponentBuilder,
|
||||
tick,
|
||||
xit
|
||||
} from 'angular2/testing_internal';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {CachedXHR} from 'angular2/src/platform/browser/xhr_cache';
|
||||
import {setTemplateCache} from './xhr_cache_setter';
|
||||
|
||||
export function main() {
|
||||
describe('CachedXHR', () => {
|
||||
var xhr: CachedXHR;
|
||||
|
||||
function createCachedXHR(): CachedXHR {
|
||||
setTemplateCache({'test.html': '<div>Hello</div>'});
|
||||
return new CachedXHR();
|
||||
}
|
||||
beforeEachProviders(() => [
|
||||
provide(UrlResolver, {useClass: TestUrlResolver}),
|
||||
provide(XHR, {useFactory: createCachedXHR})
|
||||
]);
|
||||
|
||||
it('should throw exception if $templateCache is not found', () => {
|
||||
setTemplateCache(null);
|
||||
expect(() => { xhr = new CachedXHR(); })
|
||||
.toThrowErrorWith('CachedXHR: Template cache was not found in $templateCache.');
|
||||
});
|
||||
|
||||
it('should resolve the Promise with the cached file content on success',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
setTemplateCache({'test.html': '<div>Hello</div>'});
|
||||
xhr = new CachedXHR();
|
||||
xhr.get('test.html')
|
||||
.then((text) => {
|
||||
expect(text).toEqual('<div>Hello</div>');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should reject the Promise on failure', inject([AsyncTestCompleter], (async) => {
|
||||
xhr = new CachedXHR();
|
||||
xhr.get('unknown.html')
|
||||
.then((text) => { throw new BaseException('Not expected to succeed.'); })
|
||||
.catch((error) => { async.done(); });
|
||||
}));
|
||||
|
||||
it('should allow fakeAsync Tests to load components with templateUrl synchronously',
|
||||
inject([TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => {
|
||||
let fixture: ComponentFixture;
|
||||
tcb.createAsync(TestComponent).then((f) => { fixture = f; });
|
||||
|
||||
// This should initialize the fixture.
|
||||
tick();
|
||||
|
||||
expect(fixture.debugElement.children[0].nativeElement).toHaveText('Hello');
|
||||
})));
|
||||
});
|
||||
}
|
||||
|
||||
@Component({selector: 'test-cmp', templateUrl: 'test.html'})
|
||||
class TestComponent {
|
||||
}
|
||||
|
||||
class TestUrlResolver extends UrlResolver {
|
||||
resolve(baseUrl: string, url: string): string {
|
||||
// Don't use baseUrl to get the same URL as templateUrl.
|
||||
// This is to remove any difference between Dart and TS tests.
|
||||
return url;
|
||||
}
|
||||
}
|
@ -288,6 +288,7 @@ var NG_PLATFORM_BROWSER = [
|
||||
'BROWSER_PROVIDERS',
|
||||
'BrowserDomAdapter',
|
||||
'By',
|
||||
'CACHED_TEMPLATE_PROVIDER',
|
||||
'DOCUMENT',
|
||||
'ELEMENT_PROBE_PROVIDERS',
|
||||
'ELEMENT_PROBE_PROVIDERS_PROD_MODE',
|
||||
|
@ -34,7 +34,7 @@ import {DOM} from 'angular2/src/platform/dom/dom_adapter';
|
||||
import {ResolvedInstruction} from 'angular2/src/router/instruction';
|
||||
|
||||
let dummyInstruction = new ResolvedInstruction(
|
||||
new ComponentInstruction('detail', [], null, null, true, '0'), null, {});
|
||||
new ComponentInstruction('detail', [], null, null, true, '0', null, 'Detail'), null, {});
|
||||
|
||||
export function main() {
|
||||
describe('routerLink directive', function() {
|
||||
|
@ -259,7 +259,7 @@ export function main() {
|
||||
|
||||
async.done();
|
||||
});
|
||||
router.navigateByUrl('/better-child');
|
||||
router.navigateByUrl('/better-child?extra=0');
|
||||
});
|
||||
}));
|
||||
|
||||
@ -300,7 +300,7 @@ export function main() {
|
||||
|
||||
async.done();
|
||||
});
|
||||
router.navigateByUrl('/child-with-grandchild/grandchild');
|
||||
router.navigateByUrl('/child-with-grandchild/grandchild?extra=0');
|
||||
});
|
||||
}));
|
||||
|
||||
|
@ -124,5 +124,12 @@ export function main() {
|
||||
var url = urlParser.parse('hello/there;sort=asc(modal)?friend=true');
|
||||
expect(url.toString()).toEqual('hello/there;sort=asc(modal)?friend=true');
|
||||
});
|
||||
it('should allow slashes within query parameters', () => {
|
||||
var url = urlParser.parse(
|
||||
'hello?code=4/B8o0n_Y7XZTb-pVKBw5daZyGAUbMljyLf7uNgTy6ja8&scope=https://www.googleapis.com/auth/analytics');
|
||||
expect(url.toString())
|
||||
.toEqual(
|
||||
'hello?code=4/B8o0n_Y7XZTb-pVKBw5daZyGAUbMljyLf7uNgTy6ja8&scope=https://www.googleapis.com/auth/analytics');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -230,6 +230,35 @@ export function main() {
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
it('should properly run cleanup when ng1 directive is destroyed',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var adapter: UpgradeAdapter = new UpgradeAdapter();
|
||||
var ng1Module = angular.module('ng1', []);
|
||||
var onDestroyed: EventEmitter<string> = new EventEmitter<string>();
|
||||
|
||||
ng1Module.directive('ng1', () => {
|
||||
return {
|
||||
template: '<div ng-if="!destroyIt"><ng2></ng2></div>',
|
||||
controller: function($rootScope, $timeout) {
|
||||
$timeout(function() { $rootScope.destroyIt = true; });
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
var Ng2 = Component({selector: 'ng2', template: 'test'})
|
||||
.Class({
|
||||
constructor: function() {},
|
||||
ngOnDestroy: function() { onDestroyed.emit('destroyed'); }
|
||||
});
|
||||
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
||||
var element = html('<ng1></ng1>');
|
||||
adapter.bootstrap(element, ['ng1'])
|
||||
.ready((ref) => {onDestroyed.subscribe(() => {
|
||||
ref.dispose();
|
||||
async.done();
|
||||
})});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('upgrade ng1 component', () => {
|
||||
@ -532,6 +561,65 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should call $onInit of components', inject([AsyncTestCompleter], (async) => {
|
||||
var adapter = new UpgradeAdapter();
|
||||
var ng1Module = angular.module('ng1', []);
|
||||
var valueToFind = '$onInit';
|
||||
|
||||
var ng1 = {
|
||||
bindings: {},
|
||||
template: '{{$ctrl.value}}',
|
||||
controller: Class(
|
||||
{constructor: function() {}, $onInit: function() { this.value = valueToFind; }})
|
||||
};
|
||||
ng1Module.component('ng1', ng1);
|
||||
|
||||
var Ng2 = Component({
|
||||
selector: 'ng2',
|
||||
template: '<ng1></ng1>',
|
||||
directives: [adapter.upgradeNg1Component('ng1')]
|
||||
}).Class({constructor: function() {}});
|
||||
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
||||
|
||||
var element = html(`<div><ng2></ng2></div>`);
|
||||
adapter.bootstrap(element, ['ng1'])
|
||||
.ready((ref) => {
|
||||
expect(multiTrim(document.body.textContent)).toEqual(valueToFind);
|
||||
ref.dispose();
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should bind input properties (<) of components', inject([AsyncTestCompleter], (async) => {
|
||||
var adapter = new UpgradeAdapter();
|
||||
var ng1Module = angular.module('ng1', []);
|
||||
|
||||
var ng1 = {
|
||||
bindings: {personProfile: '<'},
|
||||
template: 'Hello {{$ctrl.personProfile.firstName}} {{$ctrl.personProfile.lastName}}',
|
||||
controller: Class({constructor: function() {}})
|
||||
};
|
||||
ng1Module.component('ng1', ng1);
|
||||
|
||||
var Ng2 =
|
||||
Component({
|
||||
selector: 'ng2',
|
||||
template: '<ng1 [personProfile]="goku"></ng1>',
|
||||
directives: [adapter.upgradeNg1Component('ng1')]
|
||||
})
|
||||
.Class({
|
||||
constructor: function() { this.goku = {firstName: 'GOKU', lastName: 'SAN'}; }
|
||||
});
|
||||
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
||||
|
||||
var element = html(`<div><ng2></ng2></div>`);
|
||||
adapter.bootstrap(element, ['ng1'])
|
||||
.ready((ref) => {
|
||||
expect(multiTrim(document.body.textContent)).toEqual(`Hello GOKU SAN`);
|
||||
ref.dispose();
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('injection', () => {
|
||||
|
@ -15,7 +15,6 @@ const _INJECTABLES = const [
|
||||
const ClassDescriptor('Injectable', 'package:angular2/core.dart'),
|
||||
const ClassDescriptor('Injectable', 'package:angular2/src/core/di.dart'),
|
||||
const ClassDescriptor('Injectable', 'package:angular2/angular2.dart'),
|
||||
const ClassDescriptor('Injectable', 'package:angular2/bootstrap_static.dart'),
|
||||
const ClassDescriptor(
|
||||
'Injectable', 'package:angular2/web_worker/worker.dart'),
|
||||
];
|
||||
@ -65,7 +64,6 @@ const _PIPES = const [
|
||||
const _VIEWS = const [
|
||||
const ClassDescriptor('View', 'package:angular2/angular2.dart'),
|
||||
const ClassDescriptor('View', 'package:angular2/web_worker/worker.dart'),
|
||||
const ClassDescriptor('View', 'package:angular2/bootstrap_static.dart'),
|
||||
const ClassDescriptor('View', 'package:angular2/core.dart'),
|
||||
const ClassDescriptor('View', 'package:angular2/src/core/metadata/view.dart'),
|
||||
const ClassDescriptor('View', 'package:angular2/src/core/metadata.dart'),
|
||||
@ -74,9 +72,6 @@ const _VIEWS = const [
|
||||
const _ENTRYPOINTS = const [
|
||||
const ClassDescriptor('AngularEntrypoint', 'package:angular2/angular2.dart'),
|
||||
const ClassDescriptor('AngularEntrypoint', 'package:angular2/core.dart'),
|
||||
const ClassDescriptor('AngularEntrypoint', 'package:angular2/bootstrap.dart'),
|
||||
const ClassDescriptor(
|
||||
'AngularEntrypoint', 'package:angular2/bootstrap_static.dart'),
|
||||
const ClassDescriptor(
|
||||
'AngularEntrypoint', 'package:angular2/platform/browser.dart'),
|
||||
const ClassDescriptor(
|
||||
|
@ -1,6 +1,7 @@
|
||||
library angular2.transform.common.async_string_writer;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:analyzer/src/generated/java_core.dart';
|
||||
|
||||
/// [PrintWriter] implementation that allows asynchronous printing via
|
||||
@ -18,6 +19,7 @@ class AsyncStringWriter extends PrintWriter {
|
||||
|
||||
AsyncStringWriter([Object content = ""]) : this._(new StringBuffer(content));
|
||||
|
||||
@override
|
||||
void print(x) {
|
||||
_curr.write(x);
|
||||
}
|
||||
@ -54,6 +56,7 @@ class AsyncStringWriter extends PrintWriter {
|
||||
}).whenComplete(_semaphoreDecrementAndCleanup);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => _bufs.map((buf) => '$buf').join('(async gap)');
|
||||
|
||||
void _semaphoreIncrement() {
|
||||
|
@ -166,11 +166,11 @@ abstract class NgDepsWriterMixin
|
||||
..writeln('void ${SETUP_METHOD_NAME}() {')
|
||||
..writeln('if (_visited) return; _visited = true;');
|
||||
|
||||
final needsReceiver = (model.reflectables != null &&
|
||||
model.reflectables.isNotEmpty) ||
|
||||
(model.getters != null && model.getters.isNotEmpty) ||
|
||||
(model.setters != null && model.setters.isNotEmpty) ||
|
||||
(model.methods != null && model.methods.isNotEmpty);
|
||||
final needsReceiver =
|
||||
(model.reflectables != null && model.reflectables.isNotEmpty) ||
|
||||
(model.getters != null && model.getters.isNotEmpty) ||
|
||||
(model.setters != null && model.setters.isNotEmpty) ||
|
||||
(model.methods != null && model.methods.isNotEmpty);
|
||||
|
||||
if (needsReceiver) {
|
||||
buffer.writeln('$REFLECTOR_PREFIX.$REFLECTOR_VAR_NAME');
|
||||
|
@ -1,6 +1,7 @@
|
||||
library angular2.src.transform.common.logging;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io' show stderr;
|
||||
|
||||
import 'package:barback/barback.dart';
|
||||
import 'package:source_span/source_span.dart';
|
||||
@ -49,12 +50,16 @@ void _logElapsed(Stopwatch timer, String operationName, AssetId assetId) {
|
||||
log.fine(buf.toString(), asset: assetId);
|
||||
}
|
||||
|
||||
/// Prints logged messages to the console.
|
||||
/// Writes logged messages to the provided [StringSink].
|
||||
///
|
||||
/// A simple implementation of [TransformLogger] that prints messages to the
|
||||
/// console and discards `asset` and `span` information.
|
||||
class PrintLogger implements TransformLogger {
|
||||
void _printWithPrefix(prefix, msg) => print('$prefix: $msg');
|
||||
/// A simple implementation of [TransformLogger] that writes messages to a
|
||||
/// [StringSink] and discards `asset` and `span` information.
|
||||
class SinkLogger implements TransformLogger {
|
||||
final StringSink _sink;
|
||||
|
||||
SinkLogger(this._sink);
|
||||
|
||||
void _printWithPrefix(prefix, msg) => _sink.writeln('$prefix: $msg');
|
||||
|
||||
@override
|
||||
void info(msg, {AssetId asset, SourceSpan span}) =>
|
||||
@ -74,6 +79,58 @@ class PrintLogger implements TransformLogger {
|
||||
}
|
||||
}
|
||||
|
||||
/// Prints logged messages to stderr.
|
||||
///
|
||||
/// A simple implementation of [TransformLogger] that prints messages to
|
||||
/// [stderr] and discards `asset` and `span` information.
|
||||
class PrintLogger extends SinkLogger {
|
||||
PrintLogger() : super(stderr);
|
||||
}
|
||||
|
||||
/// Wraps the logger and prints the messages
|
||||
/// only if they have not been printed before
|
||||
class DeduppingLogger implements TransformLogger {
|
||||
Set<String> _printedMessages;
|
||||
|
||||
final TransformLogger _logger;
|
||||
|
||||
DeduppingLogger(this._logger, this._printedMessages);
|
||||
|
||||
String _key(msg, AssetId asset) => "$msg $asset";
|
||||
|
||||
@override
|
||||
void info(msg, {AssetId asset, SourceSpan span}) {
|
||||
if (!_printedMessages.contains(_key(msg, asset))) {
|
||||
_printedMessages.add(_key(msg, asset));
|
||||
_logger.info(msg, asset: asset, span: span);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void fine(msg, {AssetId asset, SourceSpan span}) {
|
||||
if (!_printedMessages.contains(_key(msg, asset))) {
|
||||
_printedMessages.add(_key(msg, asset));
|
||||
_logger.fine(msg, asset: asset, span: span);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void warning(msg, {AssetId asset, SourceSpan span}) {
|
||||
if (!_printedMessages.contains(_key(msg, asset))) {
|
||||
_printedMessages.add(_key(msg, asset));
|
||||
_logger.warning(msg, asset: asset, span: span);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void error(msg, {AssetId asset, SourceSpan span}) {
|
||||
if (!_printedMessages.contains(_key(msg, asset))) {
|
||||
_printedMessages.add(_key(msg, asset));
|
||||
_logger.error(msg, asset: asset, span: span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PrintLoggerError extends Error {
|
||||
final String message;
|
||||
final AssetId asset;
|
||||
|
@ -3,14 +3,11 @@ library angular2.transform.common.mirror_matcher;
|
||||
import 'package:analyzer/src/generated/ast.dart';
|
||||
import 'package:angular2/src/transform/common/names.dart';
|
||||
|
||||
/// Files from which `bootstrap` is exported.
|
||||
/// File from which `bootstrap` is exported.
|
||||
///
|
||||
/// These files transitively imports dart:mirrors.
|
||||
/// They should be replaced with [BOOTSTRAP_STATIC_URI] in production apps.
|
||||
const _BOOTSTRAP_URIS = const <String>[
|
||||
'package:angular2/bootstrap.dart',
|
||||
'package:angular2/platform/browser.dart',
|
||||
];
|
||||
/// This file transitively imports dart:mirrors.
|
||||
/// It should be replaced with [BOOTSTRAP_STATIC_URI] in production apps.
|
||||
const _BOOTSTRAP_URI = 'package:angular2/platform/browser.dart';
|
||||
|
||||
/// File from which `ReflectionCapabilities` is exported.
|
||||
///
|
||||
@ -23,7 +20,7 @@ const _REFLECTION_CAPABILITIES_URI =
|
||||
/// File from which `bootstrapStatic` is exported.
|
||||
///
|
||||
/// This file does not transitively import dart:mirrors.
|
||||
/// It should be used in place of [_BOOTSTRAP_URIS] in production apps.
|
||||
/// It should be used in place of [_BOOTSTRAP_URI] in production apps.
|
||||
const BOOTSTRAP_STATIC_URI = 'package:angular2/platform/browser_static.dart';
|
||||
|
||||
/// Syntactially checks for code related to the use of `dart:mirrors`.
|
||||
@ -42,5 +39,5 @@ class MirrorMatcher {
|
||||
}
|
||||
|
||||
bool hasBootstrapUri(UriBasedDirective node) =>
|
||||
_BOOTSTRAP_URIS.contains(node.uri.stringValue);
|
||||
_BOOTSTRAP_URI == node.uri.stringValue;
|
||||
}
|
||||
|
@ -23,8 +23,8 @@ class NamedParameter extends GeneratedMessage {
|
||||
static PbList<NamedParameter> createRepeated() =>
|
||||
new PbList<NamedParameter>();
|
||||
static NamedParameter getDefault() {
|
||||
if (_defaultInstance == null) _defaultInstance =
|
||||
new _ReadonlyNamedParameter();
|
||||
if (_defaultInstance == null)
|
||||
_defaultInstance = new _ReadonlyNamedParameter();
|
||||
return _defaultInstance;
|
||||
}
|
||||
|
||||
@ -78,8 +78,8 @@ class AnnotationModel extends GeneratedMessage {
|
||||
static PbList<AnnotationModel> createRepeated() =>
|
||||
new PbList<AnnotationModel>();
|
||||
static AnnotationModel getDefault() {
|
||||
if (_defaultInstance == null) _defaultInstance =
|
||||
new _ReadonlyAnnotationModel();
|
||||
if (_defaultInstance == null)
|
||||
_defaultInstance = new _ReadonlyAnnotationModel();
|
||||
return _defaultInstance;
|
||||
}
|
||||
|
||||
|
@ -26,8 +26,8 @@ class ParameterModel extends GeneratedMessage {
|
||||
static PbList<ParameterModel> createRepeated() =>
|
||||
new PbList<ParameterModel>();
|
||||
static ParameterModel getDefault() {
|
||||
if (_defaultInstance == null) _defaultInstance =
|
||||
new _ReadonlyParameterModel();
|
||||
if (_defaultInstance == null)
|
||||
_defaultInstance = new _ReadonlyParameterModel();
|
||||
return _defaultInstance;
|
||||
}
|
||||
|
||||
|
@ -27,15 +27,15 @@ class PropertyMetadataModel extends GeneratedMessage {
|
||||
static PbList<PropertyMetadataModel> createRepeated() =>
|
||||
new PbList<PropertyMetadataModel>();
|
||||
static PropertyMetadataModel getDefault() {
|
||||
if (_defaultInstance == null) _defaultInstance =
|
||||
new _ReadonlyPropertyMetadataModel();
|
||||
if (_defaultInstance == null)
|
||||
_defaultInstance = new _ReadonlyPropertyMetadataModel();
|
||||
return _defaultInstance;
|
||||
}
|
||||
|
||||
static PropertyMetadataModel _defaultInstance;
|
||||
static void $checkItem(PropertyMetadataModel v) {
|
||||
if (v
|
||||
is! PropertyMetadataModel) checkItemFailed(v, 'PropertyMetadataModel');
|
||||
if (v is! PropertyMetadataModel)
|
||||
checkItemFailed(v, 'PropertyMetadataModel');
|
||||
}
|
||||
|
||||
String get name => $_get(0, 1, '');
|
||||
@ -70,8 +70,8 @@ class PrefixedType extends GeneratedMessage {
|
||||
static PrefixedType create() => new PrefixedType();
|
||||
static PbList<PrefixedType> createRepeated() => new PbList<PrefixedType>();
|
||||
static PrefixedType getDefault() {
|
||||
if (_defaultInstance == null) _defaultInstance =
|
||||
new _ReadonlyPrefixedType();
|
||||
if (_defaultInstance == null)
|
||||
_defaultInstance = new _ReadonlyPrefixedType();
|
||||
return _defaultInstance;
|
||||
}
|
||||
|
||||
@ -130,8 +130,8 @@ class ReflectionInfoModel extends GeneratedMessage {
|
||||
static PbList<ReflectionInfoModel> createRepeated() =>
|
||||
new PbList<ReflectionInfoModel>();
|
||||
static ReflectionInfoModel getDefault() {
|
||||
if (_defaultInstance == null) _defaultInstance =
|
||||
new _ReadonlyReflectionInfoModel();
|
||||
if (_defaultInstance == null)
|
||||
_defaultInstance = new _ReadonlyReflectionInfoModel();
|
||||
return _defaultInstance;
|
||||
}
|
||||
|
||||
|
@ -28,10 +28,8 @@ import 'url_resolver.dart' show isDartCoreUri;
|
||||
/// `.ng_meta.json` files as intermediate assets during the compilation process.
|
||||
class NgMeta {
|
||||
static const _ALIAS_VALUE = 'alias';
|
||||
static const _KIND_KEY = 'kind';
|
||||
static const _NG_DEPS_KEY = 'ngDeps';
|
||||
static const _TYPE_VALUE = 'type';
|
||||
static const _VALUE_KEY = 'value';
|
||||
|
||||
/// Metadata for each identifier
|
||||
/// Type: [CompileDirectiveMetadata]|[CompilePipeMetadata]|[CompileTypeMetadata]|[CompileIdentifierMetadata]
|
||||
@ -43,11 +41,9 @@ class NgMeta {
|
||||
// The NgDeps generated from
|
||||
final NgDepsModel ngDeps;
|
||||
|
||||
bool definesAlias;
|
||||
|
||||
NgMeta({Map<String, List<String>> aliases,
|
||||
Map<String, dynamic> identifiers,
|
||||
this.ngDeps: null, this.definesAlias: false})
|
||||
Map<String, dynamic> identifiers,
|
||||
this.ngDeps: null})
|
||||
:this.aliases = aliases != null ? aliases : {},
|
||||
this.identifiers = identifiers != null ? identifiers : {};
|
||||
|
||||
@ -72,48 +68,43 @@ class NgMeta {
|
||||
|
||||
bool get isEmpty => identifiers.isEmpty && aliases.isEmpty && isNgDepsEmpty;
|
||||
|
||||
List<String> get linkingUris {
|
||||
final r = ngDeps.exports.map((r) => r.uri).toList();
|
||||
if (definesAlias) {
|
||||
r.addAll(ngDeps.imports.map((r) => r.uri));
|
||||
}
|
||||
return r;
|
||||
bool get needsResolution {
|
||||
return identifiers.values.any((id) =>
|
||||
id is CompileDirectiveMetadata || id is CompilePipeMetadata || id is CompileTypeMetadata || id is CompileFactoryMetadata
|
||||
|| (id is CompileIdentifierMetadata && id.value != null));
|
||||
}
|
||||
|
||||
/// Parse from the serialized form produced by [toJson].
|
||||
factory NgMeta.fromJson(Map json) {
|
||||
var ngDeps = null;
|
||||
final aliases = {};
|
||||
final identifiers = {};
|
||||
var definesAlias = false;
|
||||
for (var key in json.keys) {
|
||||
if (key == _NG_DEPS_KEY) {
|
||||
var ngDepsJsonMap = json[key];
|
||||
if (ngDepsJsonMap == null) continue;
|
||||
|
||||
if (json.containsKey(_NG_DEPS_KEY)) {
|
||||
var ngDepsJsonMap = json[_NG_DEPS_KEY];
|
||||
if (ngDepsJsonMap != null) {
|
||||
if (ngDepsJsonMap is! Map) {
|
||||
log.warning(
|
||||
'Unexpected value $ngDepsJsonMap for key "$key" in NgMeta.');
|
||||
continue;
|
||||
'Unexpected value $ngDepsJsonMap for key "$_NG_DEPS_KEY" in NgMeta.');
|
||||
} else {
|
||||
ngDeps = new NgDepsModel()..mergeFromJsonMap(ngDepsJsonMap);
|
||||
}
|
||||
ngDeps = new NgDepsModel()
|
||||
..mergeFromJsonMap(ngDepsJsonMap);
|
||||
} else if (key == 'definesAlias') {
|
||||
definesAlias = json[key];
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
var entry = json[key];
|
||||
final aliases = json[_ALIAS_VALUE] != null ? json[_ALIAS_VALUE] : {};
|
||||
|
||||
final identifiers = {};
|
||||
if (json.containsKey(_TYPE_VALUE)) {
|
||||
for (var key in json[_TYPE_VALUE].keys) {
|
||||
var entry = json[_TYPE_VALUE][key];
|
||||
if (entry is! Map) {
|
||||
log.warning('Unexpected value $entry for key "$key" in NgMeta.');
|
||||
continue;
|
||||
}
|
||||
if (entry[_KIND_KEY] == _TYPE_VALUE) {
|
||||
identifiers[key] = CompileMetadataWithIdentifier.fromJson(entry[_VALUE_KEY]);
|
||||
} else if (entry[_KIND_KEY] == _ALIAS_VALUE) {
|
||||
aliases[key] = entry[_VALUE_KEY];
|
||||
}
|
||||
identifiers[key] = metadataFromJson(entry);
|
||||
}
|
||||
}
|
||||
return new NgMeta(identifiers: identifiers, aliases: aliases, ngDeps: ngDeps, definesAlias: definesAlias);
|
||||
|
||||
return new NgMeta(identifiers: identifiers, aliases: aliases, ngDeps: ngDeps);
|
||||
}
|
||||
|
||||
/// Serialized representation of this instance.
|
||||
@ -121,16 +112,11 @@ class NgMeta {
|
||||
var result = {};
|
||||
result[_NG_DEPS_KEY] = isNgDepsEmpty ? null : ngDeps.writeToJsonMap();
|
||||
|
||||
result[_TYPE_VALUE] = {};
|
||||
identifiers.forEach((k, v) {
|
||||
result[k] = {_KIND_KEY: _TYPE_VALUE, _VALUE_KEY: v.toJson()};
|
||||
result[_TYPE_VALUE][k] = v.toJson();
|
||||
});
|
||||
|
||||
aliases.forEach((k, v) {
|
||||
result[k] = {_KIND_KEY: _ALIAS_VALUE, _VALUE_KEY: v};
|
||||
});
|
||||
|
||||
result['definesAlias'] = definesAlias;
|
||||
|
||||
result[_ALIAS_VALUE] = aliases;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -150,10 +136,10 @@ class NgMeta {
|
||||
log.error('Circular alias dependency for "$name". Cycle: ${newPath.join(' -> ')}.');
|
||||
return;
|
||||
}
|
||||
if (identifiers.containsKey(name)) {
|
||||
result.add(identifiers[name]);
|
||||
} else if (aliases.containsKey(name)) {
|
||||
if (aliases.containsKey(name)) {
|
||||
aliases[name].forEach((n) => helper(n, newPath));
|
||||
} else if (identifiers.containsKey(name)) {
|
||||
result.add(identifiers[name]);
|
||||
} else {
|
||||
log.error('Unknown alias: ${newPath.join(' -> ')}. Make sure you export ${name} from the file where ${path.last} is defined.');
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ const REFLECT_PROPERTIES_AS_ATTRIBUTES = 'reflect_properties_as_attributes';
|
||||
const PLATFORM_DIRECTIVES = 'platform_directives';
|
||||
const PLATFORM_PIPES = 'platform_pipes';
|
||||
const RESOLVED_IDENTIFIERS = 'resolved_identifiers';
|
||||
const ERROR_ON_MISSING_IDENTIFIERS = 'error_on_missing_identifiers';
|
||||
const INIT_REFLECTOR_PARAM = 'init_reflector';
|
||||
const INLINE_VIEWS_PARAM = 'inline_views';
|
||||
const MIRROR_MODE_PARAM = 'mirror_mode';
|
||||
@ -60,6 +61,9 @@ class TransformerOptions {
|
||||
/// A map of identifier/asset pairs used when resolving identifiers.
|
||||
final Map<String, String> resolvedIdentifiers;
|
||||
|
||||
/// when set ot false, the transformer will warn about missing identifiers but not error
|
||||
final bool errorOnMissingIdentifiers;
|
||||
|
||||
/// 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.
|
||||
@ -108,6 +112,7 @@ class TransformerOptions {
|
||||
this.platformDirectives,
|
||||
this.platformPipes,
|
||||
this.resolvedIdentifiers,
|
||||
this.errorOnMissingIdentifiers,
|
||||
this.translations,
|
||||
this.reflectPropertiesAsAttributes});
|
||||
|
||||
@ -120,6 +125,7 @@ class TransformerOptions {
|
||||
bool genChangeDetectionDebugInfo: false,
|
||||
bool genCompiledTemplates: true,
|
||||
bool reflectPropertiesAsAttributes: false,
|
||||
bool errorOnMissingIdentifiers: true,
|
||||
List<String> platformDirectives,
|
||||
List<String> platformPipes,
|
||||
Map<String, String> resolvedIdentifiers,
|
||||
@ -139,6 +145,7 @@ class TransformerOptions {
|
||||
platformDirectives: platformDirectives,
|
||||
platformPipes: platformPipes,
|
||||
resolvedIdentifiers: resolvedIdentifiers,
|
||||
errorOnMissingIdentifiers: errorOnMissingIdentifiers,
|
||||
inlineViews: inlineViews,
|
||||
lazyTransformers: lazyTransformers,
|
||||
translations: translations,
|
||||
|
@ -1,12 +1,15 @@
|
||||
library angular2.transform.common.options_reader;
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:barback/barback.dart';
|
||||
|
||||
import 'annotation_matcher.dart';
|
||||
import 'mirror_mode.dart';
|
||||
import 'options.dart';
|
||||
import './url_resolver.dart';
|
||||
|
||||
TransformerOptions parseBarbackSettings(BarbackSettings settings) {
|
||||
TransformerOptions parseBarbackSettings(BarbackSettings settings) {
|
||||
var config = settings.configuration;
|
||||
var entryPoints = _readStringList(config, ENTRY_POINT_PARAM);
|
||||
var initReflector =
|
||||
@ -16,6 +19,7 @@ import './url_resolver.dart';
|
||||
var platformDirectives = _readStringList(config, PLATFORM_DIRECTIVES);
|
||||
var platformPipes = _readStringList(config, PLATFORM_PIPES);
|
||||
var resolvedIdentifiers = config[RESOLVED_IDENTIFIERS];
|
||||
var errorOnMissingIdentifiers = _readBool(config, ERROR_ON_MISSING_IDENTIFIERS, defaultValue: true);
|
||||
var formatCode = _readBool(config, FORMAT_CODE_PARAM, defaultValue: false);
|
||||
String mirrorModeVal =
|
||||
config.containsKey(MIRROR_MODE_PARAM) ? config[MIRROR_MODE_PARAM] : '';
|
||||
@ -41,6 +45,7 @@ import './url_resolver.dart';
|
||||
platformDirectives: platformDirectives,
|
||||
platformPipes: platformPipes,
|
||||
resolvedIdentifiers: resolvedIdentifiers,
|
||||
errorOnMissingIdentifiers: errorOnMissingIdentifiers,
|
||||
inlineViews: _readBool(config, INLINE_VIEWS_PARAM, defaultValue: false),
|
||||
lazyTransformers:
|
||||
_readBool(config, LAZY_TRANSFORMERS, defaultValue: false),
|
||||
@ -79,7 +84,8 @@ List<String> _readStringList(Map config, String paramName) {
|
||||
error = true;
|
||||
}
|
||||
if (error) {
|
||||
print('Invalid value for "$paramName" in the Angular 2 transformer.');
|
||||
stderr.writeln(
|
||||
'Invalid value for "$paramName" in the Angular 2 transformer.');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -111,7 +117,7 @@ List<ClassDescriptor> _readCustomAnnotations(Map config) {
|
||||
}
|
||||
}
|
||||
if (error) {
|
||||
print(CUSTOM_ANNOTATIONS_ERROR);
|
||||
stderr.writeln(CUSTOM_ANNOTATIONS_ERROR);
|
||||
}
|
||||
return descriptors;
|
||||
}
|
||||
@ -121,7 +127,7 @@ const CUSTOM_ANNOTATIONS_ERROR = '''
|
||||
Expected something that looks like the following:
|
||||
|
||||
transformers:
|
||||
- angular2:
|
||||
- angular2[/transform/codegen]:
|
||||
custom_annotations:
|
||||
- name: MyAnnotation
|
||||
import: 'package:my_package/my_annotation.dart'
|
||||
|
@ -22,10 +22,11 @@ class TypeMetadataReader {
|
||||
final _DirectiveMetadataVisitor _directiveVisitor;
|
||||
final _PipeMetadataVisitor _pipeVisitor;
|
||||
final _CompileTypeMetadataVisitor _typeVisitor;
|
||||
final _CompileFactoryMetadataVisitor _factoryVisitor;
|
||||
final TemplateCompiler _templateCompiler;
|
||||
|
||||
TypeMetadataReader._(
|
||||
this._directiveVisitor, this._pipeVisitor, this._templateCompiler, this._typeVisitor);
|
||||
TypeMetadataReader._(this._directiveVisitor, this._pipeVisitor,
|
||||
this._templateCompiler, this._typeVisitor, this._factoryVisitor);
|
||||
|
||||
/// Accepts an [AnnotationMatcher] which tests that an [Annotation]
|
||||
/// is a [Directive], [Component], or [View].
|
||||
@ -33,12 +34,13 @@ class TypeMetadataReader {
|
||||
InterfaceMatcher interfaceMatcher, TemplateCompiler templateCompiler) {
|
||||
var lifecycleVisitor = new _LifecycleHookVisitor(interfaceMatcher);
|
||||
var typeVisitor = new _CompileTypeMetadataVisitor(annotationMatcher);
|
||||
var directiveVisitor =
|
||||
new _DirectiveMetadataVisitor(annotationMatcher, lifecycleVisitor, typeVisitor);
|
||||
var directiveVisitor = new _DirectiveMetadataVisitor(
|
||||
annotationMatcher, lifecycleVisitor, typeVisitor);
|
||||
var pipeVisitor = new _PipeMetadataVisitor(annotationMatcher);
|
||||
var factoryVisitor = new _CompileFactoryMetadataVisitor(annotationMatcher);
|
||||
|
||||
return new TypeMetadataReader._(
|
||||
directiveVisitor, pipeVisitor, templateCompiler, typeVisitor);
|
||||
directiveVisitor, pipeVisitor, templateCompiler, typeVisitor, factoryVisitor);
|
||||
}
|
||||
|
||||
/// Reads *un-normalized* [CompileDirectiveMetadata]/[CompilePipeMetadata] from the
|
||||
@ -71,6 +73,47 @@ class TypeMetadataReader {
|
||||
return new Future.value(null);
|
||||
}
|
||||
}
|
||||
|
||||
Future<dynamic> readFactoryMetadata(FunctionDeclaration node, AssetId assetId) {
|
||||
_factoryVisitor.reset(assetId);
|
||||
|
||||
node.accept(_factoryVisitor);
|
||||
|
||||
if (_factoryVisitor.isInjectable) {
|
||||
return new Future.value(_factoryVisitor.factory);
|
||||
} else {
|
||||
return new Future.value(null);
|
||||
}
|
||||
}
|
||||
|
||||
CompileIdentifierMetadata readIdentifierMetadata(
|
||||
VariableDeclaration decl, AssetId assetId) {
|
||||
final name = decl.name.name;
|
||||
return new CompileIdentifierMetadata(
|
||||
name: name, moduleUrl: toAssetUri(assetId), value: _readValue(decl.initializer));
|
||||
}
|
||||
|
||||
dynamic _readValue(dynamic initializer) {
|
||||
try {
|
||||
if (initializer is InstanceCreationExpression &&
|
||||
((initializer as InstanceCreationExpression)
|
||||
.constructorName
|
||||
.toString() ==
|
||||
"Provider" ||
|
||||
(initializer as InstanceCreationExpression)
|
||||
.constructorName
|
||||
.toString() ==
|
||||
"Binding")) {
|
||||
return _readProvider(initializer);
|
||||
} else if (initializer is ListLiteral) {
|
||||
return _readProviders(initializer);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluates the [Map] represented by `expression` and adds all `key`,
|
||||
@ -134,7 +177,6 @@ bool _expressionToBool(Expression node, String nodeDescription) {
|
||||
|
||||
class _CompileTypeMetadataVisitor extends Object
|
||||
with RecursiveAstVisitor<CompileTypeMetadata> {
|
||||
|
||||
bool _isInjectable = false;
|
||||
CompileTypeMetadata _type;
|
||||
AssetId _assetId;
|
||||
@ -166,58 +208,79 @@ class _CompileTypeMetadataVisitor extends Object
|
||||
@override
|
||||
Object visitClassDeclaration(ClassDeclaration node) {
|
||||
node.metadata.accept(this);
|
||||
final fieldTypes = _readFields(node);
|
||||
if (this._isInjectable) {
|
||||
final constructor = node.getConstructor(null);
|
||||
final diDeps = constructor == null ? [] :
|
||||
_getCompileDiDependencyMetadata(constructor.parameters, fieldTypes);
|
||||
|
||||
_type = new CompileTypeMetadata(
|
||||
moduleUrl: toAssetUri(_assetId),
|
||||
name: node.name.toString(),
|
||||
diDeps: _getCompileDiDependencyMetadata(node),
|
||||
diDeps: diDeps,
|
||||
runtime: null // Intentionally `null`, cannot be provided here.
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, TypeName> _readFields(ClassDeclaration clazz) {
|
||||
final res = {};
|
||||
clazz.members
|
||||
.where((member) => member is FieldDeclaration)
|
||||
.forEach((FieldDeclaration field) {
|
||||
var type = field.fields.type;
|
||||
if (type != null) {
|
||||
field.fields.variables.forEach((VariableDeclaration decl) {
|
||||
var key = '${decl.name}';
|
||||
res[key] = type;
|
||||
});
|
||||
}
|
||||
});
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
class _CompileFactoryMetadataVisitor extends Object
|
||||
with RecursiveAstVisitor<CompileFactoryMetadata> {
|
||||
bool _isInjectable = false;
|
||||
CompileFactoryMetadata _factory;
|
||||
AssetId _assetId;
|
||||
final AnnotationMatcher _annotationMatcher;
|
||||
|
||||
_CompileFactoryMetadataVisitor(this._annotationMatcher);
|
||||
|
||||
bool get isInjectable => _isInjectable;
|
||||
|
||||
CompileFactoryMetadata get factory => _factory;
|
||||
|
||||
void reset(AssetId assetId) {
|
||||
this._assetId = assetId;
|
||||
this._isInjectable = false;
|
||||
this._factory = null;
|
||||
}
|
||||
|
||||
@override
|
||||
Object visitAnnotation(Annotation node) {
|
||||
_isInjectable = _annotationMatcher.isInjectable(node, _assetId);
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Object visitFunctionDeclaration(FunctionDeclaration node) {
|
||||
node.metadata.accept(this);
|
||||
if (this._isInjectable) {
|
||||
_factory = new CompileFactoryMetadata(
|
||||
moduleUrl: toAssetUri(_assetId),
|
||||
name: node.name.toString(),
|
||||
diDeps: _getCompileDiDependencyMetadata(node.functionExpression.parameters, {}),
|
||||
runtime: null // Intentionally `null`, cannot be provided here.
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
List<CompileDiDependencyMetadata> _getCompileDiDependencyMetadata(ClassDeclaration node) {
|
||||
final constructor = node.getConstructor(null);
|
||||
if (constructor == null) return [];
|
||||
|
||||
return constructor.parameters.parameters.map((p) {
|
||||
final typeToken = p is SimpleFormalParameter && p.type != null ? _readIdentifier(p.type.name) : null;
|
||||
final injectTokens = p.metadata.where((m) => m.name.toString() == "Inject").map((m) => _readIdentifier(m.arguments.arguments[0]));
|
||||
final token = injectTokens.isNotEmpty ? injectTokens.first : typeToken;
|
||||
final query = _hasAnnotation(p, "Query") ? _createQueryMetadata(_getAnnotation(p, "Query")) : null;
|
||||
final viewQuery = _hasAnnotation(p, "ViewQuery") ? _createQueryMetadata(_getAnnotation(p, "ViewQuery")) : null;
|
||||
|
||||
return new CompileDiDependencyMetadata(
|
||||
token: token,
|
||||
isAttribute: _hasAnnotation(p, "Attribute"),
|
||||
isSelf: _hasAnnotation(p, "Self"),
|
||||
isHost: _hasAnnotation(p, "Host"),
|
||||
isSkipSelf: _hasAnnotation(p, "SkipSelf"),
|
||||
isOptional: _hasAnnotation(p, "Optional"),
|
||||
query: query,
|
||||
viewQuery: viewQuery);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
_getAnnotation(p, String attrName) => p.metadata.where((m) => m.name.toString() == attrName).first;
|
||||
_hasAnnotation(p, String attrName) => p.metadata.where((m) => m.name.toString() == attrName).isNotEmpty;
|
||||
_createQueryMetadata(Annotation a) {
|
||||
final selector = _readIdentifier(a.arguments.arguments.first);
|
||||
var descendants = false;
|
||||
a.arguments.arguments.skip(0).forEach((arg) {
|
||||
if (arg is NamedExpression && arg.name.toString() == "descendants:")
|
||||
descendants = naiveEval(arg.expression);
|
||||
});
|
||||
|
||||
final selectors = selector is String ? selector.split(",") : [selector];
|
||||
return new CompileQueryMetadata(selectors: selectors, descendants: descendants);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// Visitor responsible for processing a [Directive] annotated
|
||||
/// [ClassDeclaration] and creating a [CompileDirectiveMetadata] object.
|
||||
class _DirectiveMetadataVisitor extends Object
|
||||
@ -233,7 +296,8 @@ class _DirectiveMetadataVisitor extends Object
|
||||
/// The [AssetId] we are currently processing.
|
||||
AssetId _assetId;
|
||||
|
||||
_DirectiveMetadataVisitor(this._annotationMatcher, this._lifecycleVisitor, this._typeVisitor) {
|
||||
_DirectiveMetadataVisitor(
|
||||
this._annotationMatcher, this._lifecycleVisitor, this._typeVisitor) {
|
||||
reset(null);
|
||||
}
|
||||
|
||||
@ -250,7 +314,10 @@ class _DirectiveMetadataVisitor extends Object
|
||||
List<String> _inputs;
|
||||
List<String> _outputs;
|
||||
Map<String, String> _host;
|
||||
List<CompileProviderMetadata> _providers;
|
||||
List _providers;
|
||||
List _viewProviders;
|
||||
List _queries;
|
||||
List _viewQueries;
|
||||
List<LifecycleHooks> _lifecycleHooks;
|
||||
CompileTemplateMetadata _cmpTemplate;
|
||||
CompileTemplateMetadata _viewTemplate;
|
||||
@ -269,7 +336,10 @@ class _DirectiveMetadataVisitor extends Object
|
||||
_inputs = <String>[];
|
||||
_outputs = <String>[];
|
||||
_host = <String, String>{};
|
||||
_providers = <CompileProviderMetadata>[];
|
||||
_providers = [];
|
||||
_viewProviders = [];
|
||||
_queries = [];
|
||||
_viewQueries = [];
|
||||
_lifecycleHooks = null;
|
||||
_cmpTemplate = null;
|
||||
_viewTemplate = null;
|
||||
@ -292,6 +362,9 @@ class _DirectiveMetadataVisitor extends Object
|
||||
outputs: _outputs,
|
||||
host: _host,
|
||||
providers: _providers,
|
||||
viewProviders: _viewProviders,
|
||||
queries: _queries,
|
||||
viewQueries: _viewQueries,
|
||||
lifecycleHooks: _lifecycleHooks,
|
||||
template: _template);
|
||||
}
|
||||
@ -367,6 +440,19 @@ class _DirectiveMetadataVisitor extends Object
|
||||
_host['[${variable.name}]'] = '${variable.name}';
|
||||
}
|
||||
}
|
||||
|
||||
if (_isAnnotation(meta, 'ContentChild')) {
|
||||
this._queries.add(_createQueryMetadata(meta, false, true, variable.name.toString()));
|
||||
}
|
||||
if (_isAnnotation(meta, 'ContentChildren')) {
|
||||
this._queries.add(_createQueryMetadata(meta, false, false, variable.name.toString()));
|
||||
}
|
||||
if (_isAnnotation(meta, 'ViewChild')) {
|
||||
this._viewQueries.add(_createQueryMetadata(meta, true, true, variable.name.toString()));
|
||||
}
|
||||
if (_isAnnotation(meta, 'ViewChildren')) {
|
||||
this._viewQueries.add(_createQueryMetadata(meta, false, false, variable.name.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@ -383,6 +469,19 @@ class _DirectiveMetadataVisitor extends Object
|
||||
_addPropertyToType(_inputs, node.name.toString(), meta);
|
||||
}
|
||||
|
||||
if (_isAnnotation(meta, 'ContentChild') && node.isSetter) {
|
||||
this._queries.add(_createQueryMetadata(meta, false, true, node.name.toString()));
|
||||
}
|
||||
if (_isAnnotation(meta, 'ContentChildren') && node.isSetter) {
|
||||
this._queries.add(_createQueryMetadata(meta, false, false, node.name.toString()));
|
||||
}
|
||||
if (_isAnnotation(meta, 'ViewChild') && node.isSetter) {
|
||||
this._viewQueries.add(_createQueryMetadata(meta, true, true, node.name.toString()));
|
||||
}
|
||||
if (_isAnnotation(meta, 'ViewChildren') && node.isSetter) {
|
||||
this._viewQueries.add(_createQueryMetadata(meta, false, false, node.name.toString()));
|
||||
}
|
||||
|
||||
if (_isAnnotation(meta, 'HostListener')) {
|
||||
if (meta.arguments.arguments.length == 0 ||
|
||||
meta.arguments.arguments.length > 2) {
|
||||
@ -416,38 +515,13 @@ class _DirectiveMetadataVisitor extends Object
|
||||
}
|
||||
}
|
||||
|
||||
void _populateProviders(Expression providerValues) {
|
||||
void _populateProviders(Expression providerValues, List providers) {
|
||||
_checkMeta();
|
||||
|
||||
if (providerValues is ListLiteral) {
|
||||
final providers = providerValues.elements.map((el) {
|
||||
if (el is PrefixedIdentifier || el is SimpleIdentifier) {
|
||||
return new CompileProviderMetadata(token: _readIdentifier(el));
|
||||
|
||||
} else if (el is InstanceCreationExpression &&
|
||||
(el.constructorName.toString() == "Provider" ||
|
||||
el.constructorName.toString() == "Binding")
|
||||
|
||||
) {
|
||||
final token = el.argumentList.arguments.first;
|
||||
|
||||
var useClass;
|
||||
el.argumentList.arguments.skip(1).forEach((arg) {
|
||||
if (arg.name.toString() == "useClass:") {
|
||||
final id = _readIdentifier(arg.expression);
|
||||
useClass = new CompileTypeMetadata(prefix: id.prefix, name: id.name);
|
||||
}
|
||||
});
|
||||
return new CompileProviderMetadata(token: _readIdentifier(token), useClass: useClass);
|
||||
|
||||
} else {
|
||||
throw new ArgumentError(
|
||||
'Incorrect value. Expected a Provider or a String, but got "${el}".');
|
||||
}
|
||||
});
|
||||
_providers.addAll(providers);
|
||||
providers.addAll(_readProviders(providerValues));
|
||||
} else {
|
||||
_providers.add(new CompileProviderMetadata(token: _readIdentifier(providerValues)));
|
||||
providers.add(_readIdentifier(providerValues));
|
||||
}
|
||||
}
|
||||
|
||||
@ -539,7 +613,16 @@ class _DirectiveMetadataVisitor extends Object
|
||||
_populateEvents(node.expression);
|
||||
break;
|
||||
case 'providers':
|
||||
_populateProviders(node.expression);
|
||||
_populateProviders(node.expression, _providers);
|
||||
break;
|
||||
case 'bindings':
|
||||
_populateProviders(node.expression, _providers);
|
||||
break;
|
||||
case 'viewProviders':
|
||||
_populateProviders(node.expression, _viewProviders);
|
||||
break;
|
||||
case 'viewBindings':
|
||||
_populateProviders(node.expression, _viewProviders);
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
@ -607,27 +690,30 @@ class _LifecycleHookVisitor extends SimpleAstVisitor<List<LifecycleHooks>> {
|
||||
List<LifecycleHooks> visitImplementsClause(ImplementsClause node) {
|
||||
if (node == null || node.interfaces == null) return const [];
|
||||
|
||||
return node.interfaces.map((TypeName ifaceTypeName) {
|
||||
var id = ifaceTypeName.name;
|
||||
if (_ifaceMatcher.isAfterContentChecked(id, _assetId)) {
|
||||
return LifecycleHooks.AfterContentChecked;
|
||||
} else if (_ifaceMatcher.isAfterContentInit(id, _assetId)) {
|
||||
return LifecycleHooks.AfterContentInit;
|
||||
} else if (_ifaceMatcher.isAfterViewChecked(id, _assetId)) {
|
||||
return LifecycleHooks.AfterViewChecked;
|
||||
} else if (_ifaceMatcher.isAfterViewInit(id, _assetId)) {
|
||||
return LifecycleHooks.AfterViewInit;
|
||||
} else if (_ifaceMatcher.isDoCheck(id, _assetId)) {
|
||||
return LifecycleHooks.DoCheck;
|
||||
} else if (_ifaceMatcher.isOnChange(id, _assetId)) {
|
||||
return LifecycleHooks.OnChanges;
|
||||
} else if (_ifaceMatcher.isOnDestroy(id, _assetId)) {
|
||||
return LifecycleHooks.OnDestroy;
|
||||
} else if (_ifaceMatcher.isOnInit(id, _assetId)) {
|
||||
return LifecycleHooks.OnInit;
|
||||
}
|
||||
return null;
|
||||
}).where((e) => e != null).toList(growable: false);
|
||||
return node.interfaces
|
||||
.map((TypeName ifaceTypeName) {
|
||||
var id = ifaceTypeName.name;
|
||||
if (_ifaceMatcher.isAfterContentChecked(id, _assetId)) {
|
||||
return LifecycleHooks.AfterContentChecked;
|
||||
} else if (_ifaceMatcher.isAfterContentInit(id, _assetId)) {
|
||||
return LifecycleHooks.AfterContentInit;
|
||||
} else if (_ifaceMatcher.isAfterViewChecked(id, _assetId)) {
|
||||
return LifecycleHooks.AfterViewChecked;
|
||||
} else if (_ifaceMatcher.isAfterViewInit(id, _assetId)) {
|
||||
return LifecycleHooks.AfterViewInit;
|
||||
} else if (_ifaceMatcher.isDoCheck(id, _assetId)) {
|
||||
return LifecycleHooks.DoCheck;
|
||||
} else if (_ifaceMatcher.isOnChange(id, _assetId)) {
|
||||
return LifecycleHooks.OnChanges;
|
||||
} else if (_ifaceMatcher.isOnDestroy(id, _assetId)) {
|
||||
return LifecycleHooks.OnDestroy;
|
||||
} else if (_ifaceMatcher.isOnInit(id, _assetId)) {
|
||||
return LifecycleHooks.OnInit;
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.where((e) => e != null)
|
||||
.toList(growable: false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -824,20 +910,198 @@ class _PipeMetadataVisitor extends Object with RecursiveAstVisitor<Object> {
|
||||
}
|
||||
}
|
||||
|
||||
List _readProviders(ListLiteral providerValues) {
|
||||
return providerValues.elements.map((el) {
|
||||
if (el is PrefixedIdentifier || el is SimpleIdentifier) {
|
||||
return _readIdentifier(el);
|
||||
} else if (el is InstanceCreationExpression &&
|
||||
(el.constructorName.toString() == "Provider" ||
|
||||
el.constructorName.toString() == "Binding")) {
|
||||
return _readProvider(el);
|
||||
} else {
|
||||
throw new ArgumentError(
|
||||
'Incorrect value. Expected a Provider or a String, but got "${el}".');
|
||||
}
|
||||
}).toList();
|
||||
}
|
||||
|
||||
|
||||
CompileProviderMetadata _readProvider(InstanceCreationExpression el) {
|
||||
final token = el.argumentList.arguments.first;
|
||||
|
||||
var useClass, useExisting, useValue, useFactory, deps;
|
||||
el.argumentList.arguments.skip(1).forEach((arg) {
|
||||
switch (arg.name.toString()) {
|
||||
case "useClass:":
|
||||
final id = _readIdentifier(arg.expression);
|
||||
useClass = new CompileTypeMetadata(prefix: id.prefix, name: id.name);
|
||||
break;
|
||||
case "toClass:":
|
||||
final id = _readIdentifier(arg.expression);
|
||||
useClass = new CompileTypeMetadata(prefix: id.prefix, name: id.name);
|
||||
break;
|
||||
case "useExisting:":
|
||||
useExisting = _readIdentifier(arg.expression);
|
||||
break;
|
||||
case "toAlias:":
|
||||
useExisting = _readIdentifier(arg.expression);
|
||||
break;
|
||||
case "useValue:":
|
||||
useValue = _readIdentifier(arg.expression);
|
||||
break;
|
||||
case "toValue:":
|
||||
useValue = _readIdentifier(arg.expression);
|
||||
break;
|
||||
case "useFactory:":
|
||||
final id = _readIdentifier(arg.expression);
|
||||
useFactory = new CompileFactoryMetadata(
|
||||
name: id.name, prefix: id.prefix);
|
||||
break;
|
||||
case "toFactory:":
|
||||
final id = _readIdentifier(arg.expression);
|
||||
useFactory = new CompileFactoryMetadata(
|
||||
name: id.name, prefix: id.prefix);
|
||||
break;
|
||||
case "deps:":
|
||||
deps = _readDeps(arg.expression);
|
||||
break;
|
||||
}
|
||||
});
|
||||
return new CompileProviderMetadata(
|
||||
token: _readIdentifier(token),
|
||||
useClass: useClass,
|
||||
useExisting: useExisting,
|
||||
useValue: useValue,
|
||||
useFactory: useFactory,
|
||||
deps: deps);
|
||||
}
|
||||
|
||||
List<CompileDiDependencyMetadata> _readDeps(ListLiteral deps) {
|
||||
if (deps is! ListLiteral) {
|
||||
throw new ArgumentError('Incorrect value is set as deps. '
|
||||
'Expected type is ListLiteral');
|
||||
}
|
||||
|
||||
return deps.elements.map((p) {
|
||||
final list = p is ListLiteral ? p.elements : [p];
|
||||
final first = list.first;
|
||||
|
||||
var token;
|
||||
if (first is InstanceCreationExpression &&
|
||||
(first as InstanceCreationExpression).constructorName.toString() ==
|
||||
"Inject") {
|
||||
token = _readIdentifier(first.argumentList.arguments[0]);
|
||||
} else {
|
||||
token = _readIdentifier(first);
|
||||
}
|
||||
|
||||
return new CompileDiDependencyMetadata(
|
||||
token: token,
|
||||
isSelf: _hasConst(list, "Self"),
|
||||
isHost: _hasConst(list, "Host"),
|
||||
isSkipSelf: _hasConst(list, "SkipSelf"),
|
||||
isOptional: _hasConst(list, "Optional"));
|
||||
}).toList();
|
||||
}
|
||||
|
||||
_createQueryMetadata(Annotation a, bool defaultDescendantsValue, bool first, String propertyName) {
|
||||
final selector = _readIdentifier(a.arguments.arguments.first);
|
||||
var descendants = defaultDescendantsValue;
|
||||
a.arguments.arguments.skip(0).forEach((arg) {
|
||||
if (arg is NamedExpression && arg.name.toString() == "descendants:")
|
||||
descendants = naiveEval(arg.expression);
|
||||
});
|
||||
|
||||
final selectors = selector is String ? selector.split(",") : [selector];
|
||||
return new CompileQueryMetadata(
|
||||
selectors: selectors, descendants: descendants, first: first, propertyName: propertyName);
|
||||
}
|
||||
|
||||
List<CompileDiDependencyMetadata> _getCompileDiDependencyMetadata(
|
||||
FormalParameterList params, Map<String, TypeName> fieldTypes) {
|
||||
return params.parameters.map((p) {
|
||||
if (p is DefaultFormalParameter) {
|
||||
p = p.parameter;
|
||||
}
|
||||
|
||||
var token;
|
||||
final isAttribute = _hasAnnotation(p, "Attribute");
|
||||
if (isAttribute) {
|
||||
token = _readIdentifier(_getAnnotation(p, "Attribute").arguments.arguments.first);
|
||||
} else {
|
||||
var type = null;
|
||||
if (p is SimpleFormalParameter) {
|
||||
type = p.type;
|
||||
} else if (p is FieldFormalParameter) {
|
||||
type = fieldTypes[p.identifier.toString()];
|
||||
}
|
||||
final typeToken = type != null ? _readIdentifier(type.name) : null;
|
||||
final injectTokens = p.metadata
|
||||
.where((m) => m.name.toString() == "Inject")
|
||||
.map((m) => _readIdentifier(m.arguments.arguments[0]));
|
||||
token = injectTokens.isNotEmpty ? injectTokens.first : typeToken;
|
||||
}
|
||||
|
||||
var query;
|
||||
if(_hasAnnotation(p, "Query")) {
|
||||
query = _createQueryMetadata(_getAnnotation(p, "Query"), false, false, null);
|
||||
}
|
||||
if(_hasAnnotation(p, "ContentChildren")) {
|
||||
query = _createQueryMetadata(_getAnnotation(p, "ContentChildren"), true, false, null);
|
||||
}
|
||||
|
||||
var viewQuery;
|
||||
if(_hasAnnotation(p, "ViewQuery")) {
|
||||
viewQuery = _createQueryMetadata(_getAnnotation(p, "ViewQuery"), false, false, null);
|
||||
}
|
||||
if(_hasAnnotation(p, "ViewChildren")) {
|
||||
viewQuery = _createQueryMetadata(_getAnnotation(p, "ViewChildren"), true, false, null);
|
||||
}
|
||||
|
||||
return new CompileDiDependencyMetadata(
|
||||
token: token,
|
||||
isAttribute: _hasAnnotation(p, "Attribute"),
|
||||
isSelf: _hasAnnotation(p, "Self"),
|
||||
isHost: _hasAnnotation(p, "Host"),
|
||||
isSkipSelf: _hasAnnotation(p, "SkipSelf"),
|
||||
isOptional: _hasAnnotation(p, "Optional"),
|
||||
query: query,
|
||||
viewQuery: viewQuery);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
_getAnnotation(p, String attrName) =>
|
||||
p.metadata.where((m) => m.name.toString() == attrName).first;
|
||||
|
||||
_hasAnnotation(p, String attrName) =>
|
||||
p.metadata.where((m) => m.name.toString() == attrName).isNotEmpty;
|
||||
|
||||
bool _hasConst(List list, String name) => list
|
||||
.where((m) =>
|
||||
m is InstanceCreationExpression && m.constructorName.toString() == name)
|
||||
.isNotEmpty;
|
||||
|
||||
dynamic _readIdentifier(dynamic el) {
|
||||
if (el is PrefixedIdentifier) {
|
||||
return new CompileIdentifierMetadata(name: '${el.identifier}', prefix: '${el.prefix}');
|
||||
|
||||
final prefix = '${el.prefix}';
|
||||
if (prefix.length > 0 && prefix.toUpperCase()[0] == prefix[0]) {
|
||||
throw new ArgumentError('Incorrect identifier "${el}".');
|
||||
} else {
|
||||
return new CompileIdentifierMetadata(
|
||||
name: '${el.identifier}', prefix: prefix);
|
||||
}
|
||||
} else if (el is SimpleIdentifier) {
|
||||
return new CompileIdentifierMetadata(name: '$el');
|
||||
|
||||
} else if (el is SimpleStringLiteral){
|
||||
} else if (el is DoubleLiteral ||
|
||||
el is IntegerLiteral ||
|
||||
el is SimpleStringLiteral ||
|
||||
el is BooleanLiteral) {
|
||||
return el.value;
|
||||
|
||||
} else if (el is InstanceCreationExpression){
|
||||
return new CompileIdentifierMetadata(name: '${el.constructorName}', constConstructor: true);
|
||||
|
||||
} else if (el is NullLiteral) {
|
||||
return null;
|
||||
} else if (el is InstanceCreationExpression) {
|
||||
return new CompileIdentifierMetadata(
|
||||
name: '${el.constructorName}', constConstructor: true);
|
||||
} else {
|
||||
throw new ArgumentError('Incorrect identifier "${el}".');
|
||||
}
|
||||
|
@ -13,8 +13,8 @@ class TransformerUrlResolver implements UrlResolver {
|
||||
|
||||
if (!uri.isAbsolute) {
|
||||
if (baseUrl == null) throw new ArgumentError.notNull('baseUrl');
|
||||
if (baseUrl.isEmpty) throw new ArgumentError.value(
|
||||
'(empty string)', 'baseUrl');
|
||||
if (baseUrl.isEmpty)
|
||||
throw new ArgumentError.value('(empty string)', 'baseUrl');
|
||||
uri = Uri.parse(baseUrl).resolveUri(uri);
|
||||
}
|
||||
|
||||
@ -29,8 +29,8 @@ String toAssetUri(AssetId assetId) {
|
||||
|
||||
AssetId fromUri(String assetUri) {
|
||||
if (assetUri == null) throw new ArgumentError.notNull('assetUri');
|
||||
if (assetUri.isEmpty) throw new ArgumentError.value(
|
||||
'(empty string)', 'assetUri');
|
||||
if (assetUri.isEmpty)
|
||||
throw new ArgumentError.value('(empty string)', 'assetUri');
|
||||
var uri = toAssetScheme(Uri.parse(assetUri));
|
||||
return new AssetId(
|
||||
uri.pathSegments.first, uri.pathSegments.skip(1).join('/'));
|
||||
|
@ -64,20 +64,20 @@ Future<Map<String, String>> _processNgImports(NgDepsModel model,
|
||||
return Future
|
||||
.wait(
|
||||
importsAndExports.where(_isNotDartDirective).map((dynamic directive) {
|
||||
// Check whether the import or export generated summary NgMeta information.
|
||||
final summaryJsonUri =
|
||||
resolver.resolve(assetUri, toSummaryExtension(directive.uri));
|
||||
return reader.hasInput(fromUri(summaryJsonUri)).then((hasInput) {
|
||||
if (hasInput) {
|
||||
retVal[directive.uri] = summaryJsonUri;
|
||||
}
|
||||
}, onError: (err, stack) {
|
||||
log.warning(
|
||||
'Error while looking for $summaryJsonUri. '
|
||||
'Message: $err\n'
|
||||
'Stack: $stack',
|
||||
asset: assetId);
|
||||
});
|
||||
}))
|
||||
// Check whether the import or export generated summary NgMeta information.
|
||||
final summaryJsonUri =
|
||||
resolver.resolve(assetUri, toSummaryExtension(directive.uri));
|
||||
return reader.hasInput(fromUri(summaryJsonUri)).then((hasInput) {
|
||||
if (hasInput) {
|
||||
retVal[directive.uri] = summaryJsonUri;
|
||||
}
|
||||
}, onError: (err, stack) {
|
||||
log.warning(
|
||||
'Error while looking for $summaryJsonUri. '
|
||||
'Message: $err\n'
|
||||
'Stack: $stack',
|
||||
asset: assetId);
|
||||
});
|
||||
}))
|
||||
.then((_) => retVal);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ library angular2.transform.directive_metadata_linker.linker;
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:angular2/src/compiler/directive_metadata.dart';
|
||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||
import 'package:angular2/src/transform/common/logging.dart';
|
||||
import 'package:angular2/src/transform/common/names.dart';
|
||||
@ -12,36 +13,40 @@ import 'package:barback/barback.dart';
|
||||
|
||||
import 'ng_deps_linker.dart';
|
||||
|
||||
/// Returns [NgMeta] associated with [entryPoint] combined with the [NgMeta] of
|
||||
/// Returns [NgMeta] associated with the provided asset combined with the [NgMeta] of
|
||||
/// all files `export`ed from the original file.
|
||||
///
|
||||
/// This includes entries for every `Directive`-annotated class and
|
||||
/// constants that match the directive-aliases pattern.
|
||||
/// The returned NgMeta has all the identifiers resolved.
|
||||
///
|
||||
/// There are entries for each of these which is visible from a file importing
|
||||
/// the original .dart file that produced `entryPoint`. That is, this includes
|
||||
/// all `Directive` annotated public classes in that file, all `DirectiveAlias`
|
||||
/// annotated public variables, and any of those entries which are visible from
|
||||
/// files which the .dart file `export`ed.
|
||||
///
|
||||
/// Returns an empty [NgMeta] if there are no `Directive`-annotated classes or
|
||||
/// `DirectiveAlias` annotated constants in `entryPoint`.
|
||||
Future<NgMeta> linkDirectiveMetadata(
|
||||
AssetReader reader, AssetId assetId) async {
|
||||
var ngMeta = await _readNgMeta(reader, assetId);
|
||||
/// `summaryAssetId` - the unlinked asset id (source)
|
||||
/// `summaryAssetId` - the linked asset id (dest)
|
||||
/// `resolvedIdentifiers` - preresolved identifiers (e.g., Window)
|
||||
/// `ngMetas` - in memory cache of linked ngMeta files
|
||||
Future<NgMeta> linkDirectiveMetadata(AssetReader reader, AssetId summaryAssetId,
|
||||
AssetId metaAssetId, Map<String, String> resolvedIdentifiers,
|
||||
[bool errorOnMissingIdentifiers = true, Map<AssetId, NgMeta> ngMetas]) async {
|
||||
if (ngMetas == null) ngMetas = {};
|
||||
|
||||
var ngMeta = await _readNgMeta(reader, summaryAssetId, ngMetas);
|
||||
if (ngMeta == null || ngMeta.isEmpty) return null;
|
||||
|
||||
await Future.wait([
|
||||
linkNgDeps(ngMeta.ngDeps, reader, assetId, _urlResolver),
|
||||
linkNgDeps(ngMeta.ngDeps, reader, summaryAssetId, _urlResolver),
|
||||
logElapsedAsync(() async {
|
||||
await _linkRecursive(ngMeta, reader, assetId, new Set<String>());
|
||||
final linker = new _Linker(reader, ngMetas, resolvedIdentifiers, errorOnMissingIdentifiers);
|
||||
await linker.linkRecursive(ngMeta, metaAssetId, new Set<AssetId>());
|
||||
return ngMeta;
|
||||
}, operationName: 'linkDirectiveMetadata', assetId: assetId)
|
||||
}, operationName: 'linkDirectiveMetadata', assetId: summaryAssetId)
|
||||
]);
|
||||
|
||||
return ngMeta;
|
||||
}
|
||||
|
||||
Future<NgMeta> _readNgMeta(AssetReader reader, AssetId ngMetaAssetId) async {
|
||||
final _urlResolver = const TransformerUrlResolver();
|
||||
|
||||
Future<NgMeta> _readNgMeta(AssetReader reader, AssetId ngMetaAssetId,
|
||||
Map<AssetId, NgMeta> ngMetas) async {
|
||||
if (ngMetas.containsKey(ngMetaAssetId)) return ngMetas[ngMetaAssetId];
|
||||
if (!(await reader.hasInput(ngMetaAssetId))) return null;
|
||||
|
||||
var ngMetaJson = await reader.readAsString(ngMetaAssetId);
|
||||
@ -50,34 +55,401 @@ Future<NgMeta> _readNgMeta(AssetReader reader, AssetId ngMetaAssetId) async {
|
||||
return new NgMeta.fromJson(JSON.decode(ngMetaJson));
|
||||
}
|
||||
|
||||
final _urlResolver = const TransformerUrlResolver();
|
||||
class _Linker {
|
||||
final AssetReader reader;
|
||||
final Map<AssetId, NgMeta> ngMetas;
|
||||
final Map<String, String> resolvedIdentifiers;
|
||||
final bool errorOnMissingIdentifiers;
|
||||
|
||||
_Linker(this.reader, this.ngMetas, this.resolvedIdentifiers, this.errorOnMissingIdentifiers);
|
||||
|
||||
Future<NgMeta> linkRecursive(NgMeta ngMeta, AssetId assetId, Set<AssetId> seen) async {
|
||||
if (seen.contains(assetId)) return ngMeta;
|
||||
|
||||
final newSeen = new Set.from(seen)
|
||||
..add(assetId);
|
||||
|
||||
await _resolveDeps(ngMeta, assetId, newSeen);
|
||||
await _resolveIdentifiers(ngMeta, assetId);
|
||||
await _mergeExports(ngMeta, assetId);
|
||||
|
||||
ngMetas[assetId] = ngMeta;
|
||||
|
||||
Future _linkRecursive(NgMeta ngMeta, AssetReader reader, AssetId assetId,
|
||||
Set<String> seen) async {
|
||||
if (ngMeta == null ||
|
||||
ngMeta.ngDeps == null ||
|
||||
ngMeta.ngDeps.exports == null) {
|
||||
return ngMeta;
|
||||
}
|
||||
var assetUri = toAssetUri(assetId);
|
||||
|
||||
return Future.wait(ngMeta.linkingUris
|
||||
.where((uri) => !isDartCoreUri(uri))
|
||||
.map((uri) =>
|
||||
_urlResolver.resolve(assetUri, toSummaryExtension(uri)))
|
||||
.where((uri) => !seen.contains(uri))
|
||||
.map((uri) async {
|
||||
seen.add(uri);
|
||||
try {
|
||||
final exportAssetId = fromUri(uri);
|
||||
final exportNgMeta = await _readNgMeta(reader, exportAssetId);
|
||||
if (exportNgMeta != null) {
|
||||
await _linkRecursive(exportNgMeta, reader, exportAssetId, seen);
|
||||
ngMeta.addAll(exportNgMeta);
|
||||
Future _resolveDeps(NgMeta ngMeta, AssetId assetId, Set<AssetId> seen) async {
|
||||
final importsAndExports = [];
|
||||
if (ngMeta != null &&
|
||||
ngMeta.ngDeps != null &&
|
||||
ngMeta.ngDeps.exports != null)
|
||||
importsAndExports.addAll(ngMeta.ngDeps.exports);
|
||||
|
||||
if (ngMeta != null &&
|
||||
ngMeta.needsResolution &&
|
||||
ngMeta.ngDeps != null &&
|
||||
ngMeta.ngDeps.imports != null)
|
||||
importsAndExports
|
||||
.addAll(ngMeta.ngDeps.imports.where((i) => !i.isDeferred));
|
||||
|
||||
final assetUri = toAssetUri(assetId);
|
||||
for (var withUri in importsAndExports) {
|
||||
if (isDartCoreUri(withUri.uri)) continue;
|
||||
final metaAsset =
|
||||
fromUri(_urlResolver.resolve(assetUri, toMetaExtension(withUri.uri)));
|
||||
final summaryAsset = fromUri(
|
||||
_urlResolver.resolve(assetUri, toSummaryExtension(withUri.uri)));
|
||||
|
||||
if (!await _hasMeta(metaAsset)) {
|
||||
final ngMeta = await _readSummary(summaryAsset);
|
||||
if (ngMeta != null) {
|
||||
await linkRecursive(ngMeta, metaAsset, seen);
|
||||
}
|
||||
}
|
||||
} catch (err, st) {
|
||||
// Log and continue.
|
||||
log.warning('Failed to fetch $uri. Message: $err.\n$st', asset: assetId);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
Future _resolveIdentifiers(NgMeta ngMeta, AssetId assetId) async {
|
||||
if (ngMeta.needsResolution) {
|
||||
final resolver = new _NgMetaIdentifierResolver(
|
||||
assetId, reader, ngMetas, resolvedIdentifiers, errorOnMissingIdentifiers);
|
||||
return resolver.resolveNgMeta(ngMeta);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future _mergeExports(NgMeta ngMeta, AssetId assetId) async {
|
||||
if (ngMeta == null ||
|
||||
ngMeta.ngDeps == null ||
|
||||
ngMeta.ngDeps.exports == null) {
|
||||
return ngMeta;
|
||||
}
|
||||
var assetUri = toAssetUri(assetId);
|
||||
|
||||
return Future.wait(ngMeta.ngDeps.exports.map((r) => r.uri)
|
||||
.where((export) => !isDartCoreUri(export))
|
||||
.map((export) =>
|
||||
_urlResolver.resolve(assetUri, toMetaExtension(export)))
|
||||
.map((uri) async {
|
||||
try {
|
||||
final exportAssetId = fromUri(uri);
|
||||
final exportNgMeta = await _readMeta(exportAssetId);
|
||||
if (exportNgMeta != null) {
|
||||
ngMeta.addAll(exportNgMeta);
|
||||
}
|
||||
} catch (err, st) {
|
||||
// Log and continue.
|
||||
log.warning('Failed to fetch $uri. Message: $err.\n$st',
|
||||
asset: assetId);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
Future<NgMeta> _readSummary(AssetId summaryAssetId) async {
|
||||
if (!(await reader.hasInput(summaryAssetId))) return null;
|
||||
|
||||
var ngMetaJson = await reader.readAsString(summaryAssetId);
|
||||
if (ngMetaJson == null || ngMetaJson.isEmpty) return null;
|
||||
return new NgMeta.fromJson(JSON.decode(ngMetaJson));
|
||||
}
|
||||
|
||||
Future<NgMeta> _readMeta(AssetId metaAssetId) async {
|
||||
final content = await _readNgMeta(reader, metaAssetId, ngMetas);
|
||||
if (content != null) {
|
||||
ngMetas[metaAssetId] = content;
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
Future<bool> _hasMeta(AssetId ngMetaAssetId) async {
|
||||
return ngMetas.containsKey(ngMetaAssetId) ||
|
||||
await reader.hasInput(ngMetaAssetId);
|
||||
}
|
||||
}
|
||||
|
||||
class _NgMetaIdentifierResolver {
|
||||
final Map<String, String> resolvedIdentifiers;
|
||||
final Map<AssetId, NgMeta> ngMetas;
|
||||
final AssetReader reader;
|
||||
final AssetId entryPoint;
|
||||
final bool errorOnMissingIdentifiers;
|
||||
|
||||
_NgMetaIdentifierResolver(this.entryPoint, this.reader, this.ngMetas, this.resolvedIdentifiers, this.errorOnMissingIdentifiers);
|
||||
|
||||
Future resolveNgMeta(NgMeta ngMeta) async {
|
||||
final ngMetaMap = await _extractNgMetaMap(ngMeta);
|
||||
ngMeta.identifiers.forEach((_, meta) {
|
||||
if (meta is CompileIdentifierMetadata && meta.value != null) {
|
||||
meta.value = _resolveProviders(ngMetaMap, meta.value, "root");
|
||||
}
|
||||
});
|
||||
|
||||
ngMeta.identifiers.forEach((_, meta) {
|
||||
if (meta is CompileDirectiveMetadata) {
|
||||
_resolveProviderMetadata(ngMetaMap, meta);
|
||||
_resolveQueryMetadata(ngMetaMap, meta);
|
||||
_resolveDiDependencyMetadata(ngMetaMap, meta.type.name, meta.type.diDeps);
|
||||
} else if (meta is CompileTypeMetadata) {
|
||||
_resolveDiDependencyMetadata(ngMetaMap, meta.name, meta.diDeps);
|
||||
} else if (meta is CompileFactoryMetadata) {
|
||||
_resolveDiDependencyMetadata(ngMetaMap, meta.name, meta.diDeps);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
List<CompileProviderMetadata> _resolveProviders(Map<String, NgMeta> ngMetaMap, Object value, String neededBy) {
|
||||
|
||||
if (value is List) {
|
||||
final res = [];
|
||||
for (var v in value) {
|
||||
res.addAll(_resolveProviders(ngMetaMap, v, neededBy));
|
||||
}
|
||||
return res;
|
||||
|
||||
} else if (value is CompileProviderMetadata) {
|
||||
_resolveProvider(ngMetaMap, neededBy, value);
|
||||
|
||||
return [value];
|
||||
|
||||
} else if (value is CompileIdentifierMetadata) {
|
||||
final resolved = _resolveIdentifier(ngMetaMap, neededBy, value);
|
||||
if (resolved == null) return [];
|
||||
|
||||
if (resolved is CompileTypeMetadata) {
|
||||
return [new CompileProviderMetadata(token: resolved, useClass: resolved)];
|
||||
|
||||
} else if (resolved is CompileIdentifierMetadata && resolved.value is List) {
|
||||
return _resolveProviders(ngMetaMap, resolved.value, neededBy);
|
||||
|
||||
} else if (resolved is CompileIdentifierMetadata && resolved.value is CompileProviderMetadata) {
|
||||
return [_resolveProviders(ngMetaMap, resolved.value, neededBy)];
|
||||
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
void _resolveProviderMetadata(Map<String, NgMeta> ngMetaMap, CompileDirectiveMetadata dirMeta) {
|
||||
final neededBy = dirMeta.type.name;
|
||||
if (dirMeta.providers != null) {
|
||||
dirMeta.providers =
|
||||
_resolveProviders(ngMetaMap, dirMeta.providers, neededBy);
|
||||
}
|
||||
|
||||
if (dirMeta.viewProviders != null) {
|
||||
dirMeta.viewProviders =
|
||||
_resolveProviders(ngMetaMap, dirMeta.viewProviders, neededBy);
|
||||
}
|
||||
}
|
||||
|
||||
void _resolveQueryMetadata(Map<String, NgMeta> ngMetaMap, CompileDirectiveMetadata dirMeta) {
|
||||
final neededBy = dirMeta.type.name;
|
||||
if (dirMeta.queries != null) {
|
||||
_resolveQueries(ngMetaMap, dirMeta.queries, neededBy);
|
||||
}
|
||||
if (dirMeta.viewQueries != null) {
|
||||
_resolveQueries(ngMetaMap, dirMeta.viewQueries, neededBy);
|
||||
}
|
||||
}
|
||||
|
||||
void _resolveQueries(Map<String, NgMeta> ngMetaMap, List queries, String neededBy) {
|
||||
queries.forEach((q) {
|
||||
q.selectors = q.selectors.map((s) => _resolveIdentifier(ngMetaMap, neededBy, s)).toList();
|
||||
});
|
||||
}
|
||||
|
||||
void _resolveProvider(Map<String, NgMeta> ngMetaMap,
|
||||
String neededBy, CompileProviderMetadata provider) {
|
||||
provider.token = _resolveIdentifier(ngMetaMap, neededBy, provider.token);
|
||||
if (provider.useClass != null) {
|
||||
provider.useClass =
|
||||
_resolveIdentifier(ngMetaMap, neededBy, provider.useClass);
|
||||
}
|
||||
if (provider.useExisting != null) {
|
||||
provider.useExisting =
|
||||
_resolveIdentifier(ngMetaMap, neededBy, provider.useExisting);
|
||||
}
|
||||
if (provider.useValue != null) {
|
||||
provider.useValue =
|
||||
_resolveIdentifier(ngMetaMap, neededBy, provider.useValue);
|
||||
}
|
||||
if (provider.useFactory != null) {
|
||||
provider.useFactory = _resolveIdentifier(ngMetaMap, neededBy, provider.useFactory);
|
||||
}
|
||||
if (provider.deps != null) {
|
||||
_resolveDiDependencyMetadata(ngMetaMap, neededBy, provider.deps);
|
||||
};;
|
||||
}
|
||||
|
||||
void _resolveDiDependencyMetadata(Map<String, NgMeta> ngMetaMap,
|
||||
String neededBy, List<CompileDiDependencyMetadata> deps) {
|
||||
if (deps == null) return;
|
||||
for (var dep in deps) {
|
||||
_setModuleUrl(ngMetaMap, neededBy, dep.token);
|
||||
if (dep.query != null) {
|
||||
dep.query.selectors
|
||||
.forEach((s) => _setModuleUrl(ngMetaMap, neededBy, s));
|
||||
}
|
||||
if (dep.viewQuery != null) {
|
||||
dep.viewQuery.selectors
|
||||
.forEach((s) => _setModuleUrl(ngMetaMap, neededBy, s));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _setModuleUrl(Map<String, NgMeta> ngMetaMap, String neededBy, dynamic id) {
|
||||
final resolved = _resolveIdentifier(ngMetaMap, neededBy, id);
|
||||
if (resolved != null && id is CompileIdentifierMetadata) {
|
||||
id.moduleUrl = resolved.moduleUrl;
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolves an identifier using the provided ngMetaMap.
|
||||
///
|
||||
/// ngMetaMap - a map of prefixes to the symbols available via those prefixes
|
||||
/// neededBy - a type using the unresolved symbol. It's used to generate
|
||||
/// good error message.
|
||||
/// id - an unresolved id.
|
||||
dynamic _resolveIdentifier(Map<String, NgMeta> ngMetaMap, String neededBy, dynamic id) {
|
||||
if (id is String || id is bool || id is num || id == null) return id;
|
||||
if (id is CompileMetadataWithIdentifier) {
|
||||
id = id.identifier;
|
||||
}
|
||||
|
||||
if (id.moduleUrl != null) return id;
|
||||
|
||||
final prefix = id.prefix == null ? "" : id.prefix;
|
||||
|
||||
if (!ngMetaMap.containsKey(prefix)) {
|
||||
final resolved = _resolveSpecialCases(id);
|
||||
if (resolved != null) {
|
||||
return resolved;
|
||||
} else {
|
||||
final message = 'Missing prefix "${prefix}" '
|
||||
'needed by "${neededBy}" from metadata map';
|
||||
if (errorOnMissingIdentifiers) {
|
||||
log.error(message, asset: entryPoint);
|
||||
} else {
|
||||
log.warning(message, asset: entryPoint);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
final depNgMeta = ngMetaMap[prefix];
|
||||
if (depNgMeta.identifiers.containsKey(id.name)) {
|
||||
final res = depNgMeta.identifiers[id.name];
|
||||
if (res is CompileMetadataWithIdentifier) {
|
||||
return res.identifier;
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
} else if (_isPrimitive(id.name)) {
|
||||
return id;
|
||||
} else {
|
||||
final resolved = _resolveSpecialCases(id);
|
||||
if (resolved != null) {
|
||||
return resolved;
|
||||
} else {
|
||||
final message = 'Missing identifier "${id.name}" '
|
||||
'needed by "${neededBy}" from metadata map';
|
||||
if (errorOnMissingIdentifiers) {
|
||||
log.error(message, asset: entryPoint);
|
||||
} else {
|
||||
log.warning(message, asset: entryPoint);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic _resolveSpecialCases(CompileIdentifierMetadata id) {
|
||||
if (resolvedIdentifiers != null &&
|
||||
resolvedIdentifiers.containsKey(id.name)) {
|
||||
return new CompileIdentifierMetadata(
|
||||
name: id.name, moduleUrl: resolvedIdentifiers[id.name]);
|
||||
|
||||
// these are so common that we special case them in the transformer
|
||||
} else if (id.name == "Window" || id.name == "Document") {
|
||||
return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'dart:html');
|
||||
} else if (id.name == "Profiler") {
|
||||
return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'asset:perf_api/lib/perf_api.dart');
|
||||
} else if (id.name == "Logger") {
|
||||
return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'asset:logging/lib/logging.dart');
|
||||
} else if (id.name == "Clock") {
|
||||
return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'asset:quiver/lib/src/time/clock.dart');
|
||||
} else if (id.name == "Log") {
|
||||
return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'asset:angular2/lib/src/testing/utils.dart');
|
||||
} else if (id.name == "TestComponentBuilder") {
|
||||
return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'asset:angular2/lib/src/testing/test_component_builder.dart');
|
||||
} else if (id.name == "Stream") {
|
||||
return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'dart:async');
|
||||
} else if (id.name == "StreamController") {
|
||||
return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'dart:async');
|
||||
} else if (id.name == "FakeAsync") {
|
||||
return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'asset:angular2/lib/src/testing/fake_async.dart');
|
||||
} else if (id.name == "StreamTracer") {
|
||||
return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'asset:streamy/lib/src/core/tracing.dart');
|
||||
} else if (id.name == "Tracer") {
|
||||
return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'asset:streamy/lib/src/core/tracing.dart');
|
||||
} else if (id.name == "RequestHandler") {
|
||||
return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'asset:streamy/lib/src/core/request_handler.dart');
|
||||
} else if (id.name == "BatchingStrategy") {
|
||||
return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'asset:streamy/lib/src/extra/request_handler/batching.dart');
|
||||
} else if (id.name == "ProxyClient") {
|
||||
return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'asset:streamy/lib/src/extra/request_handler/proxy.dart');
|
||||
} else if (id.name == "StreamyHttpService") {
|
||||
return new CompileIdentifierMetadata(name: id.name, moduleUrl: 'asset:streamy/lib/src/toolbox/http.dart');
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
bool _isPrimitive(String typeName) =>
|
||||
typeName == "String" ||
|
||||
typeName == "Object" ||
|
||||
typeName == "num" ||
|
||||
typeName == "int" ||
|
||||
typeName == "double" ||
|
||||
typeName == "bool" ||
|
||||
typeName == "dynamic";
|
||||
|
||||
/// Walks all the imports and creates a map from prefixes to
|
||||
/// all the symbols available through those prefixes
|
||||
Future<Map<String, NgMeta>> _extractNgMetaMap(NgMeta ngMeta) async {
|
||||
final res = {"": new NgMeta.empty()};
|
||||
res[""].addAll(ngMeta);
|
||||
|
||||
if (ngMeta.ngDeps == null || ngMeta.ngDeps.imports == null) return res;
|
||||
|
||||
for (var import in ngMeta.ngDeps.imports) {
|
||||
if (isDartCoreUri(import.uri)) continue;
|
||||
|
||||
final assetUri = toAssetUri(entryPoint);
|
||||
final metaAsset =
|
||||
fromUri(_urlResolver.resolve(assetUri, toMetaExtension(import.uri)));
|
||||
final newMeta = await _readNgMeta(reader, metaAsset, ngMetas);
|
||||
|
||||
if (!res.containsKey(import.prefix)) {
|
||||
res[import.prefix] = new NgMeta.empty();
|
||||
}
|
||||
|
||||
if (newMeta != null) {
|
||||
res[import.prefix].addAll(newMeta);
|
||||
} else {
|
||||
final summaryAsset =
|
||||
fromUri(_urlResolver.resolve(assetUri, toSummaryExtension(import.uri)));
|
||||
final summary = await _readNgMeta(reader, summaryAsset, {});
|
||||
if (summary != null) {
|
||||
res[import.prefix].addAll(summary);
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ import 'package:barback/barback.dart';
|
||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||
import 'package:angular2/src/transform/common/names.dart';
|
||||
import 'package:angular2/src/transform/common/zone.dart' as zone;
|
||||
import 'package:angular2/src/transform/common/options.dart';
|
||||
import 'package:angular2/src/transform/common/logging.dart';
|
||||
|
||||
import 'ng_meta_linker.dart';
|
||||
|
||||
@ -33,6 +35,12 @@ import 'ng_meta_linker.dart';
|
||||
class DirectiveMetadataLinker extends Transformer implements LazyTransformer {
|
||||
final _encoder = const JsonEncoder.withIndent(' ');
|
||||
|
||||
final TransformerOptions options;
|
||||
final Map ngMetasCache = {};
|
||||
final Set<String> errorMessages = new Set<String>();
|
||||
|
||||
DirectiveMetadataLinker(this.options);
|
||||
|
||||
@override
|
||||
bool isPrimary(AssetId id) => id.path.endsWith(SUMMARY_META_EXTENSION);
|
||||
|
||||
@ -47,7 +55,12 @@ class DirectiveMetadataLinker extends Transformer implements LazyTransformer {
|
||||
var primaryId = transform.primaryInput.id;
|
||||
|
||||
return linkDirectiveMetadata(
|
||||
new AssetReader.fromTransform(transform), primaryId).then((ngMeta) {
|
||||
new AssetReader.fromTransform(transform),
|
||||
primaryId,
|
||||
_ngLinkedAssetId(primaryId),
|
||||
options.resolvedIdentifiers,
|
||||
options.errorOnMissingIdentifiers,
|
||||
ngMetasCache).then((ngMeta) {
|
||||
if (ngMeta != null) {
|
||||
final outputId = _ngLinkedAssetId(primaryId);
|
||||
// Not outputting an asset could confuse barback.
|
||||
@ -55,7 +68,7 @@ class DirectiveMetadataLinker extends Transformer implements LazyTransformer {
|
||||
transform.addOutput(new Asset.fromString(outputId, output));
|
||||
}
|
||||
});
|
||||
}, log: transform.logger);
|
||||
}, log: new DeduppingLogger(transform.logger, errorMessages));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,8 @@ import 'dart:async';
|
||||
import 'package:analyzer/analyzer.dart';
|
||||
import 'package:barback/barback.dart' show AssetId;
|
||||
|
||||
import 'package:angular2/src/compiler/directive_metadata.dart' show CompileIdentifierMetadata;
|
||||
import 'package:angular2/src/compiler/directive_metadata.dart'
|
||||
show CompileIdentifierMetadata, CompileProviderMetadata;
|
||||
import 'package:angular2/src/compiler/template_compiler.dart';
|
||||
import 'package:angular2/src/transform/common/annotation_matcher.dart';
|
||||
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||
@ -90,13 +91,15 @@ class _NgMetaVisitor extends Object with SimpleAstVisitor<Object> {
|
||||
|
||||
@override
|
||||
Object visitClassDeclaration(ClassDeclaration node) {
|
||||
_normalizations.add(
|
||||
_reader.readTypeMetadata(node, assetId).then((compileMetadataWithIdentifier) {
|
||||
if (compileMetadataWithIdentifier!= null) {
|
||||
_normalizations.add(_reader
|
||||
.readTypeMetadata(node, assetId)
|
||||
.then((compileMetadataWithIdentifier) {
|
||||
if (compileMetadataWithIdentifier != null) {
|
||||
ngMeta.identifiers[compileMetadataWithIdentifier.identifier.name] =
|
||||
compileMetadataWithIdentifier;
|
||||
} else {
|
||||
ngMeta.identifiers[node.name.name] = new CompileIdentifierMetadata(name: node.name.name, moduleUrl: toAssetUri(assetId));
|
||||
ngMeta.identifiers[node.name.name] = new CompileIdentifierMetadata(
|
||||
name: node.name.name, moduleUrl: toAssetUri(assetId));
|
||||
}
|
||||
}).catchError((err) {
|
||||
log.error('ERROR: $err', asset: assetId);
|
||||
@ -114,8 +117,8 @@ class _NgMetaVisitor extends Object with SimpleAstVisitor<Object> {
|
||||
// angular/angular#1747 and angular/ts2dart#249 for context).
|
||||
outer: for (var variable in node.variables.variables) {
|
||||
if (variable.isConst) {
|
||||
ngMeta.identifiers[variable.name.name] =
|
||||
new CompileIdentifierMetadata(name: variable.name.name, moduleUrl: toAssetUri(assetId));
|
||||
final id = _reader.readIdentifierMetadata(variable, assetId);
|
||||
ngMeta.identifiers[variable.name.name] = id;
|
||||
}
|
||||
|
||||
var initializer = variable.initializer;
|
||||
@ -127,7 +130,6 @@ class _NgMetaVisitor extends Object with SimpleAstVisitor<Object> {
|
||||
if (exp is! SimpleIdentifier) continue outer;
|
||||
otherNames.add(exp.name);
|
||||
}
|
||||
ngMeta.definesAlias = true;
|
||||
ngMeta.aliases[variable.name.name] = otherNames;
|
||||
}
|
||||
}
|
||||
@ -136,15 +138,33 @@ class _NgMetaVisitor extends Object with SimpleAstVisitor<Object> {
|
||||
|
||||
@override
|
||||
Object visitFunctionTypeAlias(FunctionTypeAlias node) {
|
||||
ngMeta.identifiers[node.name.name] =
|
||||
new CompileIdentifierMetadata(name: node.name.name, moduleUrl: toAssetUri(assetId));
|
||||
ngMeta.identifiers[node.name.name] = new CompileIdentifierMetadata(
|
||||
name: node.name.name, moduleUrl: toAssetUri(assetId));
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Object visitFunctionDeclaration(FunctionDeclaration node) {
|
||||
_normalizations.add(_reader
|
||||
.readFactoryMetadata(node, assetId)
|
||||
.then((compileMetadataWithIdentifier) {
|
||||
if (compileMetadataWithIdentifier != null) {
|
||||
ngMeta.identifiers[compileMetadataWithIdentifier.identifier.name] =
|
||||
compileMetadataWithIdentifier;
|
||||
} else {
|
||||
ngMeta.identifiers[node.name.name] = new CompileIdentifierMetadata(
|
||||
name: node.name.name, moduleUrl: toAssetUri(assetId));
|
||||
}
|
||||
}).catchError((err) {
|
||||
log.error('ERROR: $err', asset: assetId);
|
||||
}));
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Object visitEnumDeclaration(EnumDeclaration node) {
|
||||
ngMeta.identifiers[node.name.name] =
|
||||
new CompileIdentifierMetadata(name: node.name.name, moduleUrl: toAssetUri(assetId));
|
||||
ngMeta.identifiers[node.name.name] = new CompileIdentifierMetadata(
|
||||
name: node.name.name, moduleUrl: toAssetUri(assetId));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -123,8 +123,8 @@ class _CodegenState {
|
||||
|
||||
var names = new CodegenNameUtil(
|
||||
protoRecords, eventBindings, def.directiveRecords, '$genPrefix$_UTIL');
|
||||
var logic = new CodegenLogicUtil(
|
||||
names, '$genPrefix$_UTIL', '$genPrefix$_STATE');
|
||||
var logic =
|
||||
new CodegenLogicUtil(names, '$genPrefix$_UTIL', '$genPrefix$_STATE');
|
||||
return new _CodegenState._(
|
||||
genPrefix,
|
||||
def.id,
|
||||
|
@ -29,20 +29,18 @@ Future<CompileDataResults> createCompileData(
|
||||
AssetReader reader,
|
||||
AssetId assetId,
|
||||
List<String> platformDirectives,
|
||||
List<String> platformPipes,
|
||||
Map<String, String> resolvedIdentifiers
|
||||
) async {
|
||||
List<String> platformPipes) async {
|
||||
return logElapsedAsync(() async {
|
||||
final creator = await _CompileDataCreator.create(
|
||||
reader, assetId, platformDirectives, platformPipes, resolvedIdentifiers);
|
||||
reader, assetId, platformDirectives, platformPipes);
|
||||
return creator != null ? creator.createCompileData() : null;
|
||||
}, operationName: 'createCompileData', assetId: assetId);
|
||||
}
|
||||
|
||||
class CompileDataResults {
|
||||
final NgMeta ngMeta;
|
||||
final Map<ReflectionInfoModel,
|
||||
NormalizedComponentWithViewDirectives> viewDefinitions;
|
||||
final Map<ReflectionInfoModel, NormalizedComponentWithViewDirectives>
|
||||
viewDefinitions;
|
||||
|
||||
CompileDataResults._(this.ngMeta, this.viewDefinitions);
|
||||
}
|
||||
@ -55,20 +53,19 @@ class _CompileDataCreator {
|
||||
final NgMeta ngMeta;
|
||||
final List<String> platformDirectives;
|
||||
final List<String> platformPipes;
|
||||
final Map<String, String> resolvedIdentifiers;
|
||||
|
||||
_CompileDataCreator(this.reader, this.entryPoint, this.ngMeta,
|
||||
this.platformDirectives, this.platformPipes, this.resolvedIdentifiers);
|
||||
this.platformDirectives, this.platformPipes);
|
||||
|
||||
static Future<_CompileDataCreator> create(AssetReader reader, AssetId assetId,
|
||||
List<String> platformDirectives, List<String> platformPipes, Map<String, String> resolvedIdentifiers) async {
|
||||
List<String> platformDirectives, List<String> platformPipes) 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, platformDirectives, platformPipes, resolvedIdentifiers);
|
||||
reader, assetId, ngMeta, platformDirectives, platformPipes);
|
||||
}
|
||||
|
||||
NgDepsModel get ngDeps => ngMeta.ngDeps;
|
||||
@ -84,14 +81,15 @@ class _CompileDataCreator {
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!hasTemplate) return new CompileDataResults._(ngMeta, const {});
|
||||
|
||||
final compileData =
|
||||
<ReflectionInfoModel, NormalizedComponentWithViewDirectives>{};
|
||||
final ngMetaMap = await _extractNgMeta();
|
||||
final platformDirectives =
|
||||
await _readPlatformTypes(this.platformDirectives, 'directives');
|
||||
final platformPipes = await _readPlatformTypes(this.platformPipes, 'pipes');
|
||||
final ngMetaMap = await _extractNgMeta();
|
||||
|
||||
for (var reflectable in ngDeps.reflectables) {
|
||||
if (ngMeta.identifiers.containsKey(reflectable.name)) {
|
||||
@ -109,9 +107,6 @@ class _CompileDataCreator {
|
||||
compileDatum.pipes
|
||||
.addAll(_resolveTypeMetadata(ngMetaMap, reflectable.pipes));
|
||||
compileData[reflectable] = compileDatum;
|
||||
|
||||
_resolveDiDependencyMetadata(ngMetaMap, compileDirectiveMetadata.type, compileDirectiveMetadata.type.diDeps);
|
||||
_resolveProviderMetadata(ngMetaMap, compileDirectiveMetadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -130,11 +125,12 @@ class _CompileDataCreator {
|
||||
return null;
|
||||
}
|
||||
final depNgMeta = ngMetaMap[dep.prefix];
|
||||
|
||||
if (depNgMeta.identifiers.containsKey(dep.name)) {
|
||||
resolvedMetadata.add(depNgMeta.identifiers[dep.name]);
|
||||
} else if (depNgMeta.aliases.containsKey(dep.name)) {
|
||||
if (depNgMeta.aliases.containsKey(dep.name)) {
|
||||
resolvedMetadata.addAll(depNgMeta.flatten(dep.name));
|
||||
|
||||
} else if (depNgMeta.identifiers.containsKey(dep.name)) {
|
||||
resolvedMetadata.add(depNgMeta.identifiers[dep.name]);
|
||||
|
||||
} else {
|
||||
log.error(
|
||||
'Could not find Directive/Pipe entry for $dep. '
|
||||
@ -147,98 +143,6 @@ class _CompileDataCreator {
|
||||
return resolvedMetadata;
|
||||
}
|
||||
|
||||
void _resolveProviderMetadata(Map<String, NgMeta> ngMetaMap, CompileDirectiveMetadata dirMeta) {
|
||||
final neededBy = dirMeta.type;
|
||||
|
||||
if (dirMeta.providers == null) return;
|
||||
|
||||
final resolvedProviders = [];
|
||||
for (var provider in dirMeta.providers) {
|
||||
final alias = _resolveAlias(ngMetaMap, neededBy, provider.token);
|
||||
if (alias != null) {
|
||||
resolvedProviders.addAll(alias.map((a) => new CompileProviderMetadata(token:a)));
|
||||
} else {
|
||||
provider.token = _resolveIdentifier(ngMetaMap, neededBy, provider.token);
|
||||
if (provider.useClass != null) {
|
||||
provider.useClass = _resolveIdentifier(ngMetaMap, neededBy, provider.useClass);
|
||||
}
|
||||
resolvedProviders.add(provider);
|
||||
}
|
||||
}
|
||||
|
||||
dirMeta.providers = resolvedProviders;
|
||||
}
|
||||
|
||||
void _resolveDiDependencyMetadata(
|
||||
Map<String, NgMeta> ngMetaMap,CompileTypeMetadata neededBy, List<CompileDiDependencyMetadata> deps) {
|
||||
if (deps == null) return;
|
||||
for (var dep in deps) {
|
||||
dep.token = _resolveIdentifier(ngMetaMap, neededBy, dep.token);
|
||||
if (dep.query != null) {
|
||||
dep.query.selectors = dep.query.selectors.map((s) => _resolveIdentifier(ngMetaMap, neededBy, s)).toList();
|
||||
}
|
||||
if (dep.viewQuery != null) {
|
||||
dep.viewQuery.selectors = dep.viewQuery.selectors.map((s) => _resolveIdentifier(ngMetaMap, neededBy, s)).toList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic _resolveAlias(Map<String, NgMeta> ngMetaMap, CompileTypeMetadata neededBy, dynamic id) {
|
||||
if (id is String || id == null) return null;
|
||||
|
||||
final prefix = id.prefix == null ? "" : id.prefix;
|
||||
|
||||
if (!ngMetaMap.containsKey(prefix)) {
|
||||
log.error(
|
||||
'Missing prefix "${prefix}" '
|
||||
'needed by "${neededBy.name}" from metadata map',
|
||||
asset: entryPoint);
|
||||
return null;
|
||||
}
|
||||
|
||||
final depNgMeta = ngMetaMap[prefix];
|
||||
if (depNgMeta.aliases.containsKey(id.name)) {
|
||||
return depNgMeta.flatten(id.name);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
dynamic _resolveIdentifier(Map<String, NgMeta> ngMetaMap, CompileTypeMetadata neededBy, dynamic id) {
|
||||
if (id is String || id == null) return id;
|
||||
|
||||
final prefix = id.prefix == null ? "" : id.prefix;
|
||||
|
||||
if (!ngMetaMap.containsKey(prefix)) {
|
||||
log.error(
|
||||
'Missing prefix "${prefix}" '
|
||||
'needed by "${neededBy.name}" from metadata map',
|
||||
asset: entryPoint);
|
||||
return null;
|
||||
}
|
||||
|
||||
final depNgMeta = ngMetaMap[prefix];
|
||||
if (depNgMeta.identifiers.containsKey(id.name)) {
|
||||
return depNgMeta.identifiers[id.name];
|
||||
|
||||
} else if (_isPrimitive(id.name)) {
|
||||
return id;
|
||||
|
||||
} else if (resolvedIdentifiers != null && resolvedIdentifiers.containsKey(id.name)) {
|
||||
return new CompileIdentifierMetadata(name: id.name, moduleUrl: resolvedIdentifiers[id.name]);
|
||||
|
||||
} else {
|
||||
log.error(
|
||||
'Missing identifier "${id.name}" '
|
||||
'needed by "${neededBy.name}" from metadata map',
|
||||
asset: entryPoint);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
bool _isPrimitive(String typeName) =>
|
||||
typeName == "String" || typeName == "Object" || typeName == "num" || typeName == "int" || typeName == "double" || typeName == "bool";
|
||||
|
||||
Future<List<dynamic>> _readPlatformTypes(
|
||||
List<String> inputPlatformTypes, String configOption) async {
|
||||
if (inputPlatformTypes == null) return const [];
|
||||
@ -266,10 +170,12 @@ class _CompileDataCreator {
|
||||
if (jsonString != null && jsonString.isNotEmpty) {
|
||||
var newMetadata = new NgMeta.fromJson(JSON.decode(jsonString));
|
||||
|
||||
if (newMetadata.identifiers.containsKey(token)) {
|
||||
return [newMetadata.identifiers[token]];
|
||||
} else if (newMetadata.aliases.containsKey(token)) {
|
||||
if (newMetadata.aliases.containsKey(token)) {
|
||||
return newMetadata.flatten(token);
|
||||
|
||||
} else if (newMetadata.identifiers.containsKey(token)) {
|
||||
return [newMetadata.identifiers[token]];
|
||||
|
||||
} else {
|
||||
log.warning('Could not resolve platform type ${token} in ${uri}',
|
||||
asset: metaAssetId);
|
||||
|
@ -44,7 +44,7 @@ Future<Outputs> processTemplates(AssetReader reader, AssetId assetId,
|
||||
Map<String, String> resolvedIdentifiers
|
||||
}) async {
|
||||
var viewDefResults = await createCompileData(
|
||||
reader, assetId, platformDirectives, platformPipes, resolvedIdentifiers);
|
||||
reader, assetId, platformDirectives, platformPipes);
|
||||
if (viewDefResults == null) return null;
|
||||
final compileTypeMetadatas = viewDefResults.ngMeta.identifiers.values;
|
||||
if (compileTypeMetadatas.isNotEmpty) {
|
||||
|
@ -34,7 +34,7 @@ class AngularTransformerGroup extends TransformerGroup {
|
||||
} else {
|
||||
phases = [
|
||||
[new DirectiveProcessor(options)],
|
||||
[new DirectiveMetadataLinker()],
|
||||
[new DirectiveMetadataLinker(options)],
|
||||
[new ReflectionRemover(options)],
|
||||
[
|
||||
new DeferredRewriter(),
|
||||
|
@ -39,7 +39,7 @@ class CodegenTransformer extends TransformerGroup {
|
||||
} else {
|
||||
phases = [
|
||||
[new DirectiveProcessor(options)],
|
||||
[new DirectiveMetadataLinker()],
|
||||
[new DirectiveMetadataLinker(options)],
|
||||
[new StylesheetCompiler(), new TemplateCompiler(options),],
|
||||
];
|
||||
}
|
||||
|
@ -55,7 +55,8 @@ void allTests() {
|
||||
a.aliases['a3'] = ['T3', 'a2'];
|
||||
a.aliases['a4'] = ['a3', 'T0'];
|
||||
|
||||
expect(a.flatten('a4'), equals([mockDirMetadata[3], mockDirMetadata[1], mockDirMetadata[0]]));
|
||||
expect(a.flatten('a4'),
|
||||
equals([mockDirMetadata[3], mockDirMetadata[1], mockDirMetadata[0]]));
|
||||
});
|
||||
|
||||
test('should detect cycles.', () {
|
||||
@ -64,7 +65,10 @@ void allTests() {
|
||||
a.aliases['a1'] = ['T0', 'a2'];
|
||||
a.aliases['a2'] = ['a1'];
|
||||
|
||||
expect(() => a.flatten('a1'), throwsA(predicate((ex) => new RegExp('Cycle: a1 -> a2 -> a1.').hasMatch(ex.message))));
|
||||
expect(
|
||||
() => a.flatten('a1'),
|
||||
throwsA(predicate((ex) =>
|
||||
new RegExp('Cycle: a1 -> a2 -> a1.').hasMatch(ex.message))));
|
||||
});
|
||||
|
||||
test('should allow duplicates.', () {
|
||||
@ -98,6 +102,44 @@ void allTests() {
|
||||
expect(a.aliases['b'], equals(['y']));
|
||||
});
|
||||
});
|
||||
|
||||
group('needsResolution', () {
|
||||
test('should be true if there is a provider', () {
|
||||
var a = new NgMeta.empty();
|
||||
a.identifiers["MyIdentifier"] = new CompileIdentifierMetadata(name: 'MyIdentifier', value: new CompileProviderMetadata());
|
||||
expect(a.needsResolution, isTrue);
|
||||
});
|
||||
|
||||
test('should be true if there is an injectable service', () {
|
||||
var a = new NgMeta.empty();
|
||||
a.identifiers["MyIdentifier"] = new CompileTypeMetadata();
|
||||
expect(a.needsResolution, isTrue);
|
||||
});
|
||||
|
||||
test('should be true if there is an directive', () {
|
||||
var a = new NgMeta.empty();
|
||||
a.identifiers["MyIdentifier"] = new CompileDirectiveMetadata();
|
||||
expect(a.needsResolution, isTrue);
|
||||
});
|
||||
|
||||
test('should be true if there is a pipe', () {
|
||||
var a = new NgMeta.empty();
|
||||
a.identifiers["MyIdentifier"] = new CompilePipeMetadata();
|
||||
expect(a.needsResolution, isTrue);
|
||||
});
|
||||
|
||||
test('should be true if there is a factory', () {
|
||||
var a = new NgMeta.empty();
|
||||
a.identifiers["MyIdentifier"] = new CompileFactoryMetadata();
|
||||
expect(a.needsResolution, isTrue);
|
||||
});
|
||||
|
||||
test('should be false otherwise', () {
|
||||
var a = new NgMeta.empty();
|
||||
a.identifiers["MyIdentifier"] = "some value";
|
||||
expect(a.needsResolution, isFalse);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_checkSimilar(NgMeta a, NgMeta b) {
|
||||
|
@ -41,6 +41,10 @@ class TestAssetReader implements AssetReader {
|
||||
return new Future.value(exists);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
this._overrideAssets.clear();
|
||||
}
|
||||
|
||||
void addAsset(AssetId id, String contents) {
|
||||
_overrideAssets[id] = contents;
|
||||
}
|
||||
|
@ -21,46 +21,53 @@ var formatter = new DartFormatter();
|
||||
|
||||
main() => allTests();
|
||||
|
||||
var oldTest = test;
|
||||
void allTests() {
|
||||
var test = (name, fn) {
|
||||
// if (name.contains('indirection')) {
|
||||
oldTest(name, fn);
|
||||
// }
|
||||
};
|
||||
|
||||
TestAssetReader reader = null;
|
||||
final moduleBase = 'asset:a';
|
||||
var fooNgMeta, fooAssetId;
|
||||
var barNgMeta, barAssetId;
|
||||
var bazNgMeta, bazAssetId;
|
||||
var aliasNgMeta, aliasAssetId;
|
||||
var fooNgMeta, fooAssetId, fooMetaAssetId, fooComponentMeta;
|
||||
var barNgMeta, barAssetId, barMetaAssetId, barComponentMeta;
|
||||
var bazNgMeta, bazAssetId, bazMetaAssetId, bazComponentMeta;
|
||||
|
||||
/// Call after making changes to `fooNgMeta`, `barNgMeta`, or `bazNgMeta` and
|
||||
/// before trying to read them from `reader`.
|
||||
final updateReader = () => reader
|
||||
..addAsset(fooAssetId, JSON.encode(fooNgMeta.toJson()))
|
||||
..addAsset(barMetaAssetId, JSON.encode(fooNgMeta.toJson()))
|
||||
..addAsset(barAssetId, JSON.encode(barNgMeta.toJson()))
|
||||
..addAsset(barMetaAssetId, JSON.encode(barNgMeta.toJson()))
|
||||
..addAsset(bazAssetId, JSON.encode(bazNgMeta.toJson()))
|
||||
..addAsset(aliasAssetId, JSON.encode(aliasNgMeta.toJson()));
|
||||
..addAsset(bazMetaAssetId, JSON.encode(bazNgMeta.toJson()));
|
||||
|
||||
setUp(() {
|
||||
reader = new TestAssetReader();
|
||||
|
||||
// Establish some test NgMeta objects with one Component each.
|
||||
var fooComponentMeta = createFoo(moduleBase);
|
||||
fooComponentMeta = createFoo(moduleBase);
|
||||
fooNgMeta = new NgMeta(ngDeps: new NgDepsModel());
|
||||
fooNgMeta.identifiers[fooComponentMeta.type.name] = fooComponentMeta;
|
||||
|
||||
var barComponentMeta = createBar(moduleBase);
|
||||
barComponentMeta = createBar(moduleBase);
|
||||
barNgMeta = new NgMeta(ngDeps: new NgDepsModel());
|
||||
barNgMeta.identifiers[barComponentMeta.type.name] = barComponentMeta;
|
||||
|
||||
var bazComponentMeta = createBaz(moduleBase);
|
||||
bazComponentMeta = createBaz(moduleBase);
|
||||
bazNgMeta = new NgMeta(ngDeps: new NgDepsModel());
|
||||
barNgMeta.identifiers[bazComponentMeta.type.name] = bazComponentMeta;
|
||||
|
||||
aliasNgMeta = new NgMeta(ngDeps: new NgDepsModel());
|
||||
aliasNgMeta.aliases["Providers"] = ["someAlias"];
|
||||
aliasNgMeta.definesAlias = true;
|
||||
|
||||
fooAssetId = new AssetId('a', toSummaryExtension('lib/foo.dart'));
|
||||
fooMetaAssetId = new AssetId('a', toMetaExtension('lib/foo.dart'));
|
||||
barAssetId = new AssetId('a', toSummaryExtension('lib/bar.dart'));
|
||||
barMetaAssetId = new AssetId('a', toMetaExtension('lib/bar.dart'));
|
||||
bazAssetId = new AssetId('a', toSummaryExtension('lib/baz.dart'));
|
||||
aliasAssetId = new AssetId('a', toSummaryExtension('lib/alais.dart'));
|
||||
bazMetaAssetId = new AssetId('a', toMetaExtension('lib/baz.dart'));
|
||||
|
||||
updateReader();
|
||||
});
|
||||
|
||||
@ -69,9 +76,9 @@ void allTests() {
|
||||
fooNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'bar.dart');
|
||||
updateReader();
|
||||
|
||||
var extracted = await _testLink(reader, fooAssetId);
|
||||
expect(extracted.identifiers, contains('FooComponent'));
|
||||
expect(extracted.identifiers, contains('BarComponent'));
|
||||
var extracted = await _testLink(reader, fooAssetId, fooMetaAssetId);
|
||||
expect(extracted.identifiers['FooComponent'], isNotNull);
|
||||
expect(extracted.identifiers['BarComponent'], isNotNull);
|
||||
|
||||
expect(extracted.identifiers['FooComponent'].selector, equals('foo'));
|
||||
expect(extracted.identifiers['BarComponent'].selector, equals('bar'));
|
||||
@ -83,68 +90,527 @@ void allTests() {
|
||||
barNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'baz.dart');
|
||||
updateReader();
|
||||
|
||||
var extracted = await _testLink(reader, fooAssetId);
|
||||
expect(extracted.identifiers, contains('FooComponent'));
|
||||
expect(extracted.identifiers, contains('BarComponent'));
|
||||
expect(extracted.identifiers, contains('BazComponent'));
|
||||
var extracted = await _testLink(reader, fooAssetId, fooMetaAssetId);
|
||||
expect(extracted.identifiers['FooComponent'], isNotNull);
|
||||
expect(extracted.identifiers['BarComponent'], isNotNull);
|
||||
expect(extracted.identifiers['BazComponent'], isNotNull);
|
||||
|
||||
expect(extracted.identifiers['FooComponent'].selector, equals('foo'));
|
||||
expect(extracted.identifiers['BarComponent'].selector, equals('bar'));
|
||||
expect(extracted.identifiers['BazComponent'].selector, equals('baz'));
|
||||
});
|
||||
|
||||
test(
|
||||
'should include metadata recursively from imported files when they are aliases.',
|
||||
() async {
|
||||
aliasNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'bar.dart');
|
||||
updateReader();
|
||||
|
||||
var extracted = await _testLink(reader, aliasAssetId);
|
||||
expect(extracted.identifiers, contains('BarComponent'));
|
||||
});
|
||||
|
||||
test(
|
||||
'should NOT include metadata recursively from imported files when no aliases defined.',
|
||||
() async {
|
||||
fooNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'bar.dart');
|
||||
barNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'baz.dart');
|
||||
updateReader();
|
||||
|
||||
var extracted = await _testLink(reader, fooAssetId);
|
||||
expect(extracted.identifiers, isNot(contains('BarComponent')));
|
||||
expect(extracted.identifiers, isNot(contains('BazComponent')));
|
||||
});
|
||||
|
||||
test('should handle `DirectiveMetadata` export cycles gracefully.',
|
||||
() async {
|
||||
test('should handle `DirectiveMetadata` export cycles gracefully.', () async {
|
||||
fooNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'bar.dart');
|
||||
barNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'baz.dart');
|
||||
bazNgMeta.ngDeps.exports.add(new ExportModel()..uri = 'foo.dart');
|
||||
updateReader();
|
||||
|
||||
var extracted = await _testLink(reader, bazAssetId);
|
||||
expect(extracted.identifiers, contains('FooComponent'));
|
||||
expect(extracted.identifiers, contains('BarComponent'));
|
||||
expect(extracted.identifiers, contains('BazComponent'));
|
||||
var extracted = await _testLink(reader, bazAssetId, bazMetaAssetId);
|
||||
expect(extracted.identifiers['FooComponent'], isNotNull);
|
||||
expect(extracted.identifiers['BarComponent'], isNotNull);
|
||||
expect(extracted.identifiers['BazComponent'], isNotNull);
|
||||
});
|
||||
|
||||
test(
|
||||
'should include `DirectiveMetadata` from exported files '
|
||||
'expressed as absolute uris', () async {
|
||||
test('should include `DirectiveMetadata` from exported files '
|
||||
'expressed as absolute uris', () async {
|
||||
fooNgMeta.ngDeps.exports
|
||||
.add(new ExportModel()..uri = 'package:bar/bar.dart');
|
||||
updateReader();
|
||||
reader.addAsset(new AssetId('bar', toSummaryExtension('lib/bar.dart')),
|
||||
reader.addAsset(new AssetId('bar', toMetaExtension('lib/bar.dart')),
|
||||
JSON.encode(barNgMeta.toJson()));
|
||||
|
||||
var extracted = await _testLink(reader, fooAssetId);
|
||||
var extracted = await _testLink(reader, fooAssetId, fooMetaAssetId);
|
||||
|
||||
expect(extracted.identifiers, contains('FooComponent'));
|
||||
expect(extracted.identifiers, contains('BarComponent'));
|
||||
expect(extracted.identifiers['FooComponent'], isNotNull);
|
||||
expect(extracted.identifiers['BarComponent'], isNotNull);
|
||||
|
||||
expect(extracted.identifiers['FooComponent'].selector, equals('foo'));
|
||||
expect(extracted.identifiers['BarComponent'].selector, equals('bar'));
|
||||
});
|
||||
|
||||
test('should resolve queries from types.', () async {
|
||||
barNgMeta.identifiers['Service'] =
|
||||
new CompileTypeMetadata(name: 'Service', moduleUrl: 'moduleUrl');
|
||||
|
||||
fooComponentMeta.queries = [
|
||||
new CompileQueryMetadata(selectors: [new CompileIdentifierMetadata(name: 'Service')]),
|
||||
new CompileQueryMetadata(selectors: ['one'])
|
||||
];
|
||||
|
||||
fooComponentMeta.viewQueries = [
|
||||
new CompileQueryMetadata(selectors: [new CompileIdentifierMetadata(name: 'Service')]),
|
||||
new CompileQueryMetadata(selectors: ['one'])
|
||||
];
|
||||
|
||||
fooNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'bar.dart');
|
||||
|
||||
updateReader();
|
||||
|
||||
final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId);
|
||||
final cmp = extracted.identifiers["FooComponent"];
|
||||
|
||||
expect(cmp.queries.length, equals(2));
|
||||
expect(cmp.viewQueries.length, equals(2));
|
||||
|
||||
expect(cmp.queries[0].selectors[0].name, equals("Service"));
|
||||
expect(cmp.queries[0].selectors[0].moduleUrl, equals("moduleUrl"));
|
||||
expect(cmp.queries[1].selectors[0], equals("one"));
|
||||
|
||||
expect(cmp.viewQueries[0].selectors[0].name, equals("Service"));
|
||||
expect(cmp.viewQueries[0].selectors[0].moduleUrl, equals("moduleUrl"));
|
||||
expect(cmp.viewQueries[1].selectors[0], equals("one"));
|
||||
});
|
||||
|
||||
test('should resolve providers from types.', () async {
|
||||
barNgMeta.identifiers['Service'] =
|
||||
new CompileTypeMetadata(name: 'Service', moduleUrl: 'moduleUrl');
|
||||
|
||||
fooComponentMeta.providers = [
|
||||
new CompileIdentifierMetadata(name: 'Service')
|
||||
];
|
||||
fooComponentMeta.type.diDeps = [
|
||||
new CompileDiDependencyMetadata(
|
||||
token: new CompileIdentifierMetadata(name: 'Service'))
|
||||
];
|
||||
|
||||
fooNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'bar.dart');
|
||||
|
||||
updateReader();
|
||||
|
||||
final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId);
|
||||
final cmp = extracted.identifiers["FooComponent"];
|
||||
|
||||
expect(cmp.providers.length, equals(1));
|
||||
|
||||
expect(cmp.providers[0].token.name, equals("Service"));
|
||||
expect(cmp.providers[0].token.moduleUrl, equals("moduleUrl"));
|
||||
expect(cmp.providers[0].useClass.name, equals("Service"));
|
||||
expect(cmp.providers[0].useClass.moduleUrl, equals("moduleUrl"));
|
||||
|
||||
expect(cmp.type.diDeps.length, equals(1));
|
||||
expect(cmp.type.diDeps[0].token.name, equals("Service"));
|
||||
expect(cmp.type.diDeps[0].token.moduleUrl, equals("moduleUrl"));
|
||||
});
|
||||
|
||||
test('should resolve providers from functions.', () async {
|
||||
barNgMeta.identifiers['Service'] =
|
||||
new CompileTypeMetadata(name: 'Service', moduleUrl: 'moduleUrl');
|
||||
|
||||
fooNgMeta.identifiers['factory'] =
|
||||
new CompileFactoryMetadata(name: 'factory', moduleUrl: 'moduleUrl', diDeps: [
|
||||
new CompileDiDependencyMetadata(
|
||||
token: new CompileIdentifierMetadata(name: 'Service'))
|
||||
]);
|
||||
|
||||
fooComponentMeta.providers = [
|
||||
new CompileProviderMetadata(token: 'someFunc', useFactory:
|
||||
new CompileFactoryMetadata(name: 'factory'))
|
||||
];
|
||||
|
||||
fooNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'bar.dart');
|
||||
|
||||
updateReader();
|
||||
|
||||
final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId);
|
||||
final cmp = extracted.identifiers["FooComponent"];
|
||||
|
||||
expect(cmp.providers.length, equals(1));
|
||||
|
||||
expect(cmp.providers[0].token, equals("someFunc"));
|
||||
expect(cmp.providers[0].useFactory.name, equals("factory"));
|
||||
expect(cmp.providers[0].useFactory.moduleUrl, equals("moduleUrl"));
|
||||
|
||||
expect(cmp.providers[0].useFactory.diDeps[0].token.name, equals("Service"));
|
||||
expect(cmp.providers[0].useFactory.diDeps[0].token.moduleUrl, equals("moduleUrl"));
|
||||
});
|
||||
|
||||
test('should resolve viewProviders from types.', () async {
|
||||
barNgMeta.identifiers['Service'] =
|
||||
new CompileTypeMetadata(name: 'Service', moduleUrl: 'moduleUrl');
|
||||
|
||||
fooComponentMeta.viewProviders = [
|
||||
new CompileProviderMetadata(
|
||||
token: new CompileIdentifierMetadata(name: 'Service'))
|
||||
];
|
||||
fooNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'bar.dart');
|
||||
|
||||
updateReader();
|
||||
|
||||
final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId);
|
||||
final cmp = extracted.identifiers["FooComponent"];
|
||||
|
||||
expect(cmp.viewProviders.length, equals(1));
|
||||
expect(cmp.viewProviders[0].token.name, equals("Service"));
|
||||
expect(cmp.viewProviders[0].token.moduleUrl, equals("moduleUrl"));
|
||||
});
|
||||
|
||||
test('should resolve providers from Provider objects (literals).', () async {
|
||||
barNgMeta.identifiers['Service'] =
|
||||
new CompileTypeMetadata(name: 'Service', moduleUrl: 'moduleUrl');
|
||||
|
||||
fooComponentMeta.template =
|
||||
new CompileTemplateMetadata(template: "import 'bar.dart';");
|
||||
fooComponentMeta.providers = [
|
||||
new CompileProviderMetadata(
|
||||
token: "StrService",
|
||||
useClass: new CompileTypeMetadata(name: 'Service'))
|
||||
];
|
||||
fooComponentMeta.type.diDeps = [
|
||||
new CompileDiDependencyMetadata(token: "StrService")
|
||||
];
|
||||
|
||||
fooNgMeta.ngDeps.imports
|
||||
.add(new ImportModel()..uri = 'package:a/bar.dart');
|
||||
|
||||
updateReader();
|
||||
|
||||
final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId);
|
||||
final cmp = extracted.identifiers["FooComponent"];
|
||||
|
||||
expect(cmp.providers.length, equals(1));
|
||||
|
||||
expect(cmp.providers[0].token, equals("StrService"));
|
||||
expect(cmp.providers[0].useClass.name, equals("Service"));
|
||||
expect(cmp.providers[0].useClass.moduleUrl, equals("moduleUrl"));
|
||||
|
||||
expect(cmp.type.diDeps.length, equals(1));
|
||||
expect(cmp.type.diDeps[0].token, equals("StrService"));
|
||||
});
|
||||
|
||||
test('should resolve providers from references', () async {
|
||||
barNgMeta.identifiers['Service'] =
|
||||
new CompileTypeMetadata(name: 'Service', moduleUrl: 'moduleUrl');
|
||||
|
||||
fooNgMeta.identifiers["PROVIDER"] = new CompileIdentifierMetadata(
|
||||
name: 'PROVIDER',
|
||||
value: new CompileProviderMetadata(
|
||||
token: new CompileIdentifierMetadata(name: 'Service')));
|
||||
|
||||
fooComponentMeta.providers = [
|
||||
new CompileIdentifierMetadata(name: 'PROVIDER')
|
||||
];
|
||||
fooNgMeta.ngDeps.imports
|
||||
.add(new ImportModel()..uri = 'package:a/bar.dart');
|
||||
|
||||
updateReader();
|
||||
|
||||
final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId);
|
||||
final cmp = extracted.identifiers["FooComponent"];
|
||||
|
||||
expect(cmp.providers.length, equals(1));
|
||||
expect(cmp.providers[0].token.name, equals("Service"));
|
||||
expect(cmp.providers[0].token.moduleUrl, equals("moduleUrl"));
|
||||
});
|
||||
|
||||
test('should resolve providers from lists.', () async {
|
||||
barNgMeta.identifiers['Service'] =
|
||||
new CompileTypeMetadata(name: 'Service', moduleUrl: 'moduleUrl');
|
||||
|
||||
fooNgMeta.identifiers["PROVIDERS"] = new CompileIdentifierMetadata(name: 'PROVIDERS',
|
||||
value: [
|
||||
new CompileIdentifierMetadata(name: "Service")
|
||||
]);
|
||||
|
||||
fooComponentMeta.providers = [
|
||||
new CompileIdentifierMetadata(name: 'PROVIDERS')
|
||||
];
|
||||
fooNgMeta.ngDeps.imports
|
||||
.add(new ImportModel()..uri = 'package:a/bar.dart');
|
||||
|
||||
updateReader();
|
||||
|
||||
final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId);
|
||||
final cmp = extracted.identifiers["FooComponent"];
|
||||
|
||||
expect(cmp.providers.length, equals(1));
|
||||
expect(cmp.providers[0].token.name, equals("Service"));
|
||||
expect(cmp.providers[0].token.moduleUrl, equals("moduleUrl"));
|
||||
});
|
||||
|
||||
test('should resolve providers from lists (two lists in the same file).', () async {
|
||||
barNgMeta.identifiers['Service'] =
|
||||
new CompileTypeMetadata(name: 'Service', moduleUrl: 'moduleUrl');
|
||||
|
||||
|
||||
fooNgMeta.identifiers["OUTER_PROVIDERS"] = new CompileIdentifierMetadata(name: 'PROVIDERS',
|
||||
value: [
|
||||
new CompileIdentifierMetadata(name: "INNER_PROVIDERS")
|
||||
]);
|
||||
|
||||
fooNgMeta.identifiers["INNER_PROVIDERS"] = new CompileIdentifierMetadata(name: 'PROVIDERS',
|
||||
value: [
|
||||
new CompileIdentifierMetadata(name: "Service")
|
||||
]);
|
||||
|
||||
fooComponentMeta.providers = [
|
||||
new CompileIdentifierMetadata(name: 'OUTER_PROVIDERS')
|
||||
];
|
||||
fooNgMeta.ngDeps.imports
|
||||
.add(new ImportModel()..uri = 'package:a/bar.dart');
|
||||
|
||||
updateReader();
|
||||
|
||||
final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId);
|
||||
final cmp = extracted.identifiers["FooComponent"];
|
||||
|
||||
expect(cmp.providers.length, equals(1));
|
||||
expect(cmp.providers[0].token.name, equals("Service"));
|
||||
expect(cmp.providers[0].token.moduleUrl, equals("moduleUrl"));
|
||||
});
|
||||
|
||||
test('should resolve providers when there is a level of indirection.', () async {
|
||||
bazNgMeta.identifiers['Service'] =
|
||||
new CompileTypeMetadata(name: 'Service', moduleUrl: 'moduleUrl');
|
||||
|
||||
barNgMeta.identifiers['BAR_PROVIDERS'] = new CompileIdentifierMetadata(name: 'BAR_PROVIDERS',
|
||||
moduleUrl: 'moduleUrl',
|
||||
value: [
|
||||
new CompileIdentifierMetadata(name: "Service")
|
||||
]);
|
||||
barNgMeta.ngDeps.imports
|
||||
.add(new ImportModel()..uri = 'package:a/baz.dart');
|
||||
|
||||
|
||||
fooNgMeta.identifiers["PROVIDERS"] = new CompileIdentifierMetadata(name: 'PROVIDERS', moduleUrl: 'moduleUrl',
|
||||
value: new CompileIdentifierMetadata(name: 'BAR_PROVIDERS'));
|
||||
fooNgMeta.ngDeps.imports
|
||||
.add(new ImportModel()..uri = 'package:a/bar.dart');
|
||||
fooComponentMeta.providers = [new CompileIdentifierMetadata(name: 'PROVIDERS')];
|
||||
|
||||
reader.clear();
|
||||
reader
|
||||
..addAsset(fooAssetId, JSON.encode(fooNgMeta.toJson()))
|
||||
..addAsset(barAssetId, JSON.encode(barNgMeta.toJson()))
|
||||
..addAsset(bazAssetId, JSON.encode(bazNgMeta.toJson()));
|
||||
|
||||
final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId);
|
||||
final cmp = extracted.identifiers["FooComponent"];
|
||||
|
||||
expect(cmp.providers.length, equals(1));
|
||||
expect(cmp.providers[0].token.name, equals("Service"));
|
||||
expect(cmp.providers[0].token.moduleUrl, equals("moduleUrl"));
|
||||
});
|
||||
|
||||
test('should generate generate diDeps of injectable services.', () async {
|
||||
fooNgMeta.identifiers['Service2'] =
|
||||
new CompileTypeMetadata(name: 'Service2', moduleUrl: 'moduleUrl');
|
||||
|
||||
fooNgMeta.identifiers['Service'] = new CompileTypeMetadata(
|
||||
name: 'Service',
|
||||
moduleUrl: 'moduleUrl',
|
||||
diDeps: [
|
||||
new CompileDiDependencyMetadata(
|
||||
token: new CompileIdentifierMetadata(name: 'Service2'))
|
||||
]);
|
||||
|
||||
fooComponentMeta.providers = [
|
||||
new CompileProviderMetadata(
|
||||
token: new CompileIdentifierMetadata(name: 'Service'),
|
||||
useClass: new CompileTypeMetadata(name: 'Service'))
|
||||
];
|
||||
|
||||
updateReader();
|
||||
|
||||
final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId);
|
||||
final cmp = extracted.identifiers["FooComponent"];
|
||||
|
||||
expect(cmp.providers.length, equals(1));
|
||||
|
||||
expect(cmp.providers[0].useClass.name, equals("Service"));
|
||||
expect(cmp.providers[0].useClass.moduleUrl, equals("moduleUrl"));
|
||||
expect(cmp.providers[0].useClass.diDeps.first.token.name, equals("Service2"));
|
||||
expect(cmp.providers[0].useClass.diDeps.first.token.moduleUrl, equals("moduleUrl"));
|
||||
});
|
||||
|
||||
test('should resolve queries and viewQueries.', () async {
|
||||
barNgMeta.identifiers['Service'] =
|
||||
new CompileTypeMetadata(name: 'Service', moduleUrl: 'moduleUrl');
|
||||
|
||||
fooComponentMeta.template =
|
||||
new CompileTemplateMetadata(template: "import 'bar.dart';");
|
||||
fooComponentMeta.type.diDeps = [
|
||||
new CompileDiDependencyMetadata(
|
||||
token: 'someToken',
|
||||
query: new CompileQueryMetadata(
|
||||
selectors: [new CompileIdentifierMetadata(name: 'Service')])),
|
||||
new CompileDiDependencyMetadata(
|
||||
token: 'someToken',
|
||||
viewQuery: new CompileQueryMetadata(
|
||||
selectors: [new CompileIdentifierMetadata(name: 'Service')]))
|
||||
];
|
||||
|
||||
fooNgMeta.ngDeps.imports
|
||||
.add(new ImportModel()..uri = 'package:a/bar.dart');
|
||||
|
||||
updateReader();
|
||||
|
||||
final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId);
|
||||
final cmp = extracted.identifiers["FooComponent"];
|
||||
|
||||
expect(cmp.type.diDeps.length, equals(2));
|
||||
|
||||
expect(cmp.type.diDeps[0].query.selectors[0].name, equals("Service"));
|
||||
expect(cmp.type.diDeps[0].query.selectors[0].moduleUrl, equals("moduleUrl"));
|
||||
expect(cmp.type.diDeps[1].viewQuery.selectors[0].name, equals("Service"));
|
||||
expect(cmp.type.diDeps[1].viewQuery.selectors[0].moduleUrl, equals("moduleUrl"));
|
||||
});
|
||||
|
||||
test('should generate providers from Provider objects (references).',
|
||||
() async {
|
||||
barNgMeta.identifiers['Service1'] =
|
||||
new CompileTypeMetadata(name: 'Service1', moduleUrl: 'moduleUrl');
|
||||
barNgMeta.identifiers['Service2'] =
|
||||
new CompileTypeMetadata(name: 'Service2', moduleUrl: 'moduleUrl');
|
||||
barNgMeta.identifiers['factory'] =
|
||||
new CompileFactoryMetadata(name: 'factory', moduleUrl: 'moduleUrl', diDeps: [
|
||||
new CompileDiDependencyMetadata(
|
||||
token: new CompileIdentifierMetadata(name: 'Service2', moduleUrl: 'moduleUrl'))
|
||||
]);
|
||||
|
||||
fooComponentMeta.template =
|
||||
new CompileTemplateMetadata(template: "import 'bar.dart';");
|
||||
fooComponentMeta.providers = [
|
||||
new CompileProviderMetadata(
|
||||
token: new CompileIdentifierMetadata(name: 'Service1'),
|
||||
useClass: new CompileTypeMetadata(name: 'Service2')),
|
||||
new CompileProviderMetadata(
|
||||
token: new CompileIdentifierMetadata(name: 'Service1'),
|
||||
useExisting: new CompileIdentifierMetadata(name: 'Service2')),
|
||||
new CompileProviderMetadata(
|
||||
token: new CompileIdentifierMetadata(name: 'Service1'),
|
||||
useValue: new CompileIdentifierMetadata(name: 'Service2')),
|
||||
new CompileProviderMetadata(
|
||||
token: new CompileIdentifierMetadata(name: 'Service1'),
|
||||
useFactory: new CompileFactoryMetadata(name: 'factory'))
|
||||
];
|
||||
|
||||
fooNgMeta.ngDeps.imports
|
||||
.add(new ImportModel()..uri = 'package:a/bar.dart');
|
||||
|
||||
updateReader();
|
||||
|
||||
final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId);
|
||||
final cmp = extracted.identifiers["FooComponent"];
|
||||
|
||||
expect(cmp.providers.length, equals(4));
|
||||
|
||||
expect(cmp.providers[0].token.name, equals("Service1"));
|
||||
expect(cmp.providers[0].token.moduleUrl, equals("moduleUrl"));
|
||||
expect(cmp.providers[0].useClass.name, equals("Service2"));
|
||||
expect(cmp.providers[0].useClass.moduleUrl, equals("moduleUrl"));
|
||||
|
||||
expect(cmp.providers[1].token.name, equals("Service1"));
|
||||
expect(cmp.providers[1].token.moduleUrl, equals("moduleUrl"));
|
||||
expect(cmp.providers[1].useExisting.name, equals("Service2"));
|
||||
expect(cmp.providers[1].useExisting.moduleUrl, equals("moduleUrl"));
|
||||
|
||||
expect(cmp.providers[2].token.name, equals("Service1"));
|
||||
expect(cmp.providers[2].token.moduleUrl, equals("moduleUrl"));
|
||||
expect(cmp.providers[2].useValue.name, equals("Service2"));
|
||||
expect(cmp.providers[2].useValue.moduleUrl, equals("moduleUrl"));
|
||||
|
||||
expect(cmp.providers[3].token.name, equals("Service1"));
|
||||
expect(cmp.providers[3].token.moduleUrl, equals("moduleUrl"));
|
||||
expect(cmp.providers[3].useFactory.name, equals("factory"));
|
||||
expect(cmp.providers[3].useFactory.moduleUrl, equals("moduleUrl"));
|
||||
expect(cmp.providers[3].useFactory.diDeps[0].token.name, equals("Service2"));
|
||||
expect(cmp.providers[3].useFactory.diDeps[0].token.moduleUrl, equals("moduleUrl"));
|
||||
});
|
||||
|
||||
test('should fallback to the list of resolved identifiers.', () async {
|
||||
fooNgMeta.identifiers['Service2'] =
|
||||
new CompileTypeMetadata(name: 'Service2', moduleUrl: 'moduleUrl');
|
||||
|
||||
fooComponentMeta.providers = [
|
||||
new CompileProviderMetadata(
|
||||
token: new CompileIdentifierMetadata(name: 'Service1'),
|
||||
useClass: new CompileTypeMetadata(name: 'Service2'))
|
||||
];
|
||||
|
||||
updateReader();
|
||||
|
||||
final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId,
|
||||
{"Service1": "someModuleUrl", "Service2": "someModuleUrl"});
|
||||
final cmp = extracted.identifiers["FooComponent"];
|
||||
|
||||
expect(cmp.providers.length, equals(1));
|
||||
|
||||
expect(cmp.providers[0].token.name, equals("Service1"));
|
||||
expect(cmp.providers[0].token.moduleUrl, equals("someModuleUrl"));
|
||||
expect(cmp.providers[0].useClass.name, equals("Service2"));
|
||||
expect(cmp.providers[0].useClass.moduleUrl, equals("moduleUrl"));
|
||||
});
|
||||
|
||||
test('should resolve circular references.', () async {
|
||||
barNgMeta.identifiers['Service1'] =
|
||||
new CompileTypeMetadata(name: 'Service1', moduleUrl: 'moduleUrl',
|
||||
diDeps: [new CompileDiDependencyMetadata(token: new CompileIdentifierMetadata(name: "Service2"))]);
|
||||
barNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'foo.dart');
|
||||
|
||||
fooNgMeta.identifiers['Service2'] =
|
||||
new CompileTypeMetadata(name: 'Service2', moduleUrl: 'moduleUrl',
|
||||
diDeps: [new CompileDiDependencyMetadata(token: new CompileIdentifierMetadata(name: "Service1"))]);
|
||||
fooNgMeta.ngDeps.imports.add(new ImportModel()..uri = 'bar.dart');
|
||||
|
||||
updateReader();
|
||||
|
||||
final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId);
|
||||
final service2 = extracted.identifiers["Service2"];
|
||||
|
||||
expect(service2.diDeps[0].token.name, equals("Service1"));
|
||||
expect(service2.diDeps[0].token.moduleUrl, equals("moduleUrl"));
|
||||
});
|
||||
|
||||
test('should link dependencies (imports and exports first).', () async {
|
||||
bazNgMeta.identifiers['Service2'] =
|
||||
new CompileTypeMetadata(name: 'Service2', moduleUrl: 'moduleUrl');
|
||||
|
||||
barNgMeta.identifiers['Service1'] = new CompileTypeMetadata(
|
||||
name: 'Service1',
|
||||
moduleUrl: 'moduleUrl',
|
||||
diDeps: [
|
||||
new CompileDiDependencyMetadata(
|
||||
token: new CompileIdentifierMetadata(name: 'Service2'))
|
||||
]);
|
||||
barNgMeta.ngDeps..imports.add(new ImportModel()..uri = 'baz.dart');
|
||||
|
||||
fooComponentMeta.providers = [
|
||||
new CompileProviderMetadata(
|
||||
token: new CompileIdentifierMetadata(name: 'Service1'))
|
||||
];
|
||||
fooNgMeta.ngDeps..imports.add(new ImportModel()..uri = 'bar.dart');
|
||||
|
||||
reader.clear();
|
||||
reader
|
||||
..addAsset(fooAssetId, JSON.encode(fooNgMeta.toJson()))
|
||||
..addAsset(barAssetId, JSON.encode(barNgMeta.toJson()))
|
||||
..addAsset(bazAssetId, JSON.encode(bazNgMeta.toJson()));
|
||||
|
||||
final extracted = await _testLink(reader, fooAssetId, fooMetaAssetId);
|
||||
|
||||
final cmp = extracted.identifiers["FooComponent"];
|
||||
|
||||
expect(cmp.providers.length, equals(1));
|
||||
final firstProvider = cmp.providers[0];
|
||||
|
||||
expect(firstProvider.token.diDeps[0].token.name, equals("Service2"));
|
||||
expect(firstProvider.token.diDeps[0].token.moduleUrl, equals("moduleUrl"));
|
||||
});
|
||||
|
||||
test('should not resolve when not needed', () async {
|
||||
fooNgMeta.identifiers['SomeId'] = new CompileIdentifierMetadata(name: 'SomeId');
|
||||
fooNgMeta.ngDeps..imports.add(new ImportModel()..uri = 'bar.dart');
|
||||
|
||||
fooNgMeta.identifiers.clear();
|
||||
reader.clear();
|
||||
// there is no bar, so it should throw when trying to resolve
|
||||
reader
|
||||
..addAsset(fooAssetId, JSON.encode(fooNgMeta.toJson()))
|
||||
..addAsset(barAssetId, "Invalid");
|
||||
|
||||
await _testLink(reader, fooAssetId, fooMetaAssetId);
|
||||
});
|
||||
});
|
||||
|
||||
group('NgDeps linker', () {
|
||||
@ -157,7 +623,7 @@ void allTests() {
|
||||
barNgMeta.ngDeps.libraryUri = 'test.bar';
|
||||
updateReader();
|
||||
|
||||
var linked = (await _testLink(reader, fooAssetId)).ngDeps;
|
||||
var linked = (await _testLink(reader, fooAssetId, fooMetaAssetId)).ngDeps;
|
||||
expect(linked, isNotNull);
|
||||
var linkedImport = linked.depImports
|
||||
.firstWhere((i) => i.uri.endsWith('bar.template.dart'));
|
||||
@ -172,7 +638,7 @@ void allTests() {
|
||||
barNgMeta.ngDeps.libraryUri = 'test.bar';
|
||||
updateReader();
|
||||
|
||||
var linked = (await _testLink(reader, fooAssetId)).ngDeps;
|
||||
var linked = (await _testLink(reader, fooAssetId, fooMetaAssetId)).ngDeps;
|
||||
expect(linked, isNotNull);
|
||||
var linkedImport = linked.depImports
|
||||
.firstWhere((i) => i.uri.endsWith('bar.template.dart'));
|
||||
@ -190,7 +656,7 @@ void allTests() {
|
||||
barNgMeta.ngDeps.libraryUri = 'test.bar';
|
||||
updateReader();
|
||||
|
||||
var linked = (await _testLink(reader, fooAssetId)).ngDeps;
|
||||
var linked = (await _testLink(reader, fooAssetId, fooMetaAssetId)).ngDeps;
|
||||
expect(linked, isNotNull);
|
||||
var linkedImport = linked.depImports.firstWhere(
|
||||
(i) => i.uri.endsWith('bar.template.dart'),
|
||||
@ -200,7 +666,11 @@ void allTests() {
|
||||
});
|
||||
}
|
||||
|
||||
Future<NgMeta> _testLink(AssetReader reader, AssetId assetId) {
|
||||
return zone.exec(() => linkDirectiveMetadata(reader, assetId),
|
||||
Future<NgMeta> _testLink(
|
||||
AssetReader reader, AssetId summaryAssetId, AssetId metaAssetId,
|
||||
[Map<String, String> resolvedIdentifiers]) {
|
||||
return zone.exec(
|
||||
() => linkDirectiveMetadata(
|
||||
reader, summaryAssetId, metaAssetId, resolvedIdentifiers),
|
||||
log: new RecordingLogger());
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import 'package:angular2/angular2.dart' show Injectable;
|
||||
|
||||
@Injectable()
|
||||
abstract class Service {
|
||||
factory Service(){
|
||||
factory Service() {
|
||||
return null;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user