diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 202ffbdec415..c7b68e623491 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,3 +1,7 @@ + + **I'm submitting a ...** (check one with "x") ``` [ ] bug report => search github for a similar issue or PR before submitting @@ -5,14 +9,18 @@ [ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question ``` -**Current behavior** +**Current behavior** **Expected behavior** -**Reproduction of the problem** - +**Minimal reproduction of the problem with instructions** + **What is the motivation / use case for changing the behavior?** @@ -20,12 +28,12 @@ **Please tell us about your environment:** -* **Angular version:** 2.0.0-rc.X +* **Angular version:** 2.0.X -* **Browser:** [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ] +* **Browser:** [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ] * **Language:** [all | TypeScript X.X | ES6/7 | ES5] - + * **Node (for AoT issues):** `node --version` = diff --git a/CHANGELOG.md b/CHANGELOG.md index f864ee961570..5b6c83b7a768 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,45 @@ + +## [2.0.2](https://github.com/angular/angular/compare/2.0.1...2.0.2) (2016-10-05) + + +### Bug Fixes + +* **common:** correctly removes styles on IE ([#11953](https://github.com/angular/angular/pull/11953)), closes [#7916](https://github.com/angular/angular/issues/7916) +* **compiler:** do not embed templateUrl in view factories in non-debug mode. ([#11818](https://github.com/angular/angular/issues/11818)) ([51e1994](https://github.com/angular/angular/commit/51e1994)), closes [#11117](https://github.com/angular/angular/issues/11117) +* **compiler:** move detection of unsafe properties for binding to ElementSchemaRegistry ([#11378](https://github.com/angular/angular/issues/11378)) ([5911c3b](https://github.com/angular/angular/commit/5911c3b)) +* **forms:** properly validate empty strings with patterns ([#11450](https://github.com/angular/angular/issues/11450)) ([e00de0c](https://github.com/angular/angular/commit/e00de0c)) +* **http:** preserve case of the first init, `set()` or `append()` ([#12023](https://github.com/angular/angular/issues/12023)) ([adb17fe](https://github.com/angular/angular/commit/adb17fe)), closes [#11624](https://github.com/angular/angular/issues/11624) +* **compiler-cli:** allow ReflectorHost passed as argument to CodeGenerator#create ([#11951](https://github.com/angular/angular/issues/11951)) ([826c98e](https://github.com/angular/angular/commit/826c98e)) +* **router:** do not reset the router state when updating the component ([#11867](https://github.com/angular/angular/issues/11867)) ([cf750e1](https://github.com/angular/angular/commit/cf750e1)) +* **compiler:** fix `:host(tag)` and `:host-context(tag)` ([a6bb84e0](https://github.com/angular/angular/commit/a6bb84e02b7579f8d957ef6ba5b10d83482ed756)), closes [#11972](https://github.com/angular/angular/issues/11972) +* **compiler:** fix attribute selectors in :host and :host-context ([#12056](https://github.com/angular/angular/issues/12056)) ([6f7ed32](https://github.com/angular/angular/commit/6f7ed32)), closes [#11917](https://github.com/angular/angular/issues/11917) +* **compiler:** support `[@page](https://github.com/page)` and `[@document](https://github.com/document)` CSS rules ([#11878](https://github.com/angular/angular/issues/11878)) ([c99ef49](https://github.com/angular/angular/commit/c99ef49)), closes [#11860](https://github.com/angular/angular/issues/11860) +* **compiler:** support `[attr="value with space"]` ([bd012ef](https://github.com/angular/angular/commit/bd012ef)), closes [#6249](https://github.com/angular/angular/issues/6249) +* **compiler:** support quoted attribute values ([7395400](https://github.com/angular/angular/commit/7395400)), closes [#6085](https://github.com/angular/angular/issues/6085) +* **upgrade:** bind optional properties when upgrading from ng1 ([#11411](https://github.com/angular/angular/issues/11411)) ([0851238](https://github.com/angular/angular/commit/0851238)), closes [#10181](https://github.com/angular/angular/issues/10181) +* **http:** change a behavior when a param value is null or undefined ([#11990](https://github.com/angular/angular/issues/11990)) ([9cc0a4e](https://github.com/angular/angular/commit/9cc0a4e)) +* **compiler:** fix `` ctype names ([7578d85](https://github.com/angular/angular/commit/7578d85)), closes [#12000](https://github.com/angular/angular/issues/12000) + + + +## [2.0.1](https://github.com/angular/angular/compare/2.0.0...2.0.1) (2016-09-23) + + +### Bug Fixes + +* **common:** fix ngOnChanges signature of NgTemplateOutlet directive ([14ee759](https://github.com/angular/angular/commit/14ee759)) +* **compiler:** `[attribute~=value]` selector ([#11696](https://github.com/angular/angular/issues/11696)) ([734b8b8](https://github.com/angular/angular/commit/734b8b8)), closes [#9644](https://github.com/angular/angular/issues/9644) +* **compiler:** safe property access expressions work in event bindings ([#11724](https://github.com/angular/angular/issues/11724)) ([a95d652](https://github.com/angular/angular/commit/a95d652)) +* **compiler:** throw when Component.moduleId is not a string ([bd4045b](https://github.com/angular/angular/commit/bd4045b)), closes [#11590](https://github.com/angular/angular/issues/11590) +* **compiler:** do not provide I18N values when they're not specified ([03aedbe](https://github.com/angular/angular/commit/03aedbe)), closes [#11643](https://github.com/angular/angular/issues/11643) +* **core:** ContentChild descendants should be queried by default ([0dc15eb](https://github.com/angular/angular/commit/0dc15eb)), closes [#11645](https://github.com/angular/angular/issues/11645) +* **forms:** disable all radios with disable() ([2860418](https://github.com/angular/angular/commit/2860418)) +* **forms:** make setDisabledState optional for reactive form directives ([#11731](https://github.com/angular/angular/issues/11731)) ([51d73d3](https://github.com/angular/angular/commit/51d73d3)), closes [#11719](https://github.com/angular/angular/issues/11719) +* **forms:** support unbound disabled in ngModel ([#11736](https://github.com/angular/angular/issues/11736)) ([39e251e](https://github.com/angular/angular/commit/39e251e)) +* **upgrade:** allow attribute selectors for components in ng2 which are not part of upgrade ([#11808](https://github.com/angular/angular/issues/11808)) ([b81e2e7](https://github.com/angular/angular/commit/b81e2e7)), closes [#11280](https://github.com/angular/angular/issues/11280) + + + # [2.0.0](https://github.com/angular/angular/compare/2.0.0-rc.7...2.0.0) (2016-09-14) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ca3f837068ad..cadac6f60bd3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,7 +18,7 @@ Help us keep Angular open and inclusive. Please read and follow our [Code of Con ## Got a Question or Problem? If you have questions about how to *use* Angular, please direct them to the [Google Group][angular-group] -discussion list or [StackOverflow][stackoverflow]. Please note that Angular 2 is still in early developer preview, and the core team's capacity to answer usage questions is limited. We are also available on [Gitter][gitter]. +discussion list or [StackOverflow][stackoverflow]. Please note that the Angular team's capacity to answer usage questions is limited. We are also available on [Gitter][gitter]. ## Found an Issue? If you find a bug in the source code, you can help us by @@ -28,8 +28,7 @@ If you find a bug in the source code, you can help us by ## Want a Feature? You can *request* a new feature by [submitting an issue](#submit-issue) to our [GitHub Repository][github]. If you would like to *implement* a new feature, please submit an issue with -a proposal for your work first, to be sure that we can use it. Angular 2 is in developer preview -and we are not ready to accept major contributions ahead of the full release. +a proposal for your work first, to be sure that we can use it. Please consider what kind of change it is: * For a **Major Feature**, first open an issue and outline your proposal so that it can be diff --git a/DEVELOPER.md b/DEVELOPER.md index ac7e15f44f35..2ce368ab814f 100644 --- a/DEVELOPER.md +++ b/DEVELOPER.md @@ -114,7 +114,7 @@ You should execute the 3 test suites before submitting a PR to github. All the tests are executed on our Continuous Integration infrastructure and a PR could only be merged once the tests pass. - CircleCI fails if your code is not formatted properly, -- Travis CI fails if any of the test suite describe above fails. +- Travis CI fails if any of the test suites described above fails. ## Update the public API tests diff --git a/README.md b/README.md index c6d25eb346a5..65727f024bb5 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,6 @@ [![Issue Stats](http://issuestats.com/github/angular/angular/badge/pr?style=flat)](http://issuestats.com/github/angular/angular) [![Issue Stats](http://issuestats.com/github/angular/angular/badge/issue?style=flat)](http://issuestats.com/github/angular/angular) [![npm version](https://badge.fury.io/js/%40angular%2Fcore.svg)](https://badge.fury.io/js/%40angular%2Fcore) -[![Downloads](http://img.shields.io/npm/dm/angular2.svg)](https://npmjs.org/package/angular2) [![Sauce Test Status](https://saucelabs.com/browser-matrix/angular2-ci.svg)](https://saucelabs.com/u/angular2-ci) *Safari (7+), iOS (7+), Edge (14) and IE mobile (11) are tested on [BrowserStack][browserstack].* diff --git a/TRIAGE_AND_LABELS.md b/TRIAGE_AND_LABELS.md index d184f5df779b..e50dc9436f53 100644 --- a/TRIAGE_AND_LABELS.md +++ b/TRIAGE_AND_LABELS.md @@ -17,9 +17,9 @@ with it. * `comp: animations`: `@matsko` * `comp: benchpress`: `@tbosch` -* `comp: build/ci`: `@IgorMinar` -- All build and CI scripts +* `comp: build & ci`: `@IgorMinar` -- All build and CI scripts * `comp: common`: `@mhevery` -- This includes core components / pipes. -* `comp: core/compiler`: `@tbosch` -- Because core and compiler are very +* `comp: core & compiler`: `@tbosch` -- Because core and compiler are very intertwined, we will be treating them as one. * `comp: forms`: `@kara` * `comp: http`: `@jeffbcross` @@ -29,14 +29,14 @@ with it. * `comp: testing`: `@juliemr` * `comp: upgrade`: `@mhevery` * `comp: web-worker`: `@vicb` -* `comp: zone`: `@mhevery` +* `comp: zones`: `@mhevery` There are few components which are cross-cutting. They don't have a clear location in the source tree. We will treat them as a component even thought no specific source tree is associated with them. * `comp: documentation`: `@naomiblack` -* `comp: packaging`: `@mhevery` +* `comp: packaging`: `@IgorMinar` * `comp: performance`: `@tbosch` * `comp: security`: `@IgorMinar` diff --git a/browser-providers.conf.js b/browser-providers.conf.js index 5143e9adca6e..5e75463f1290 100644 --- a/browser-providers.conf.js +++ b/browser-providers.conf.js @@ -1,216 +1,98 @@ -// Unique place to configure the browsers which are used in the different CI jobs in Sauce Labs (SL) and BrowserStack (BS). +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +// Unique place to configure the browsers which are used in the different CI jobs in Sauce Labs (SL) +// and BrowserStack (BS). // If the target is set to null, then the browser is not run anywhere during CI. -// If a category becomes empty (e.g. BS and required), then the corresponding job must be commented out in Travis configuration. +// If a category becomes empty (e.g. BS and required), then the corresponding job must be commented +// out in Travis configuration. var CIconfiguration = { - 'Chrome': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}}, - 'Firefox': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}}, + 'Chrome': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}}, + 'Firefox': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}}, // FirefoxBeta and ChromeBeta should be target:'BS' or target:'SL', and required:true // Currently deactivated due to https://github.com/angular/angular/issues/7560 - 'ChromeBeta': { unitTest: {target: null, required: true}, e2e: {target: null, required: false}}, - 'FirefoxBeta': { unitTest: {target: null, required: false}, e2e: {target: null, required: false}}, - 'ChromeDev': { unitTest: {target: null, required: true}, e2e: {target: null, required: true}}, - 'FirefoxDev': { unitTest: {target: null, required: true}, e2e: {target: null, required: true}}, - 'IE9': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}}, - 'IE10': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}}, - 'IE11': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}}, - 'Edge': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}, - 'Android4.1': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}}, - 'Android4.2': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}}, - 'Android4.3': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}}, - 'Android4.4': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}}, - 'Android5': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}}, - 'Safari7': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}, - 'Safari8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}, - 'Safari9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}, - 'iOS7': { unitTest: {target: 'BS', required: true}, e2e: {target: null, required: true}}, - 'iOS8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}, - 'iOS9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}, - 'WindowsPhone': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}} + 'ChromeBeta': {unitTest: {target: null, required: true}, e2e: {target: null, required: false}}, + 'FirefoxBeta': {unitTest: {target: null, required: false}, e2e: {target: null, required: false}}, + 'ChromeDev': {unitTest: {target: null, required: true}, e2e: {target: null, required: true}}, + 'FirefoxDev': {unitTest: {target: null, required: true}, e2e: {target: null, required: true}}, + 'IE9': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}}, + 'IE10': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}}, + 'IE11': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}}, + 'Edge': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}, + 'Android4.1': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}}, + 'Android4.2': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}}, + 'Android4.3': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}}, + 'Android4.4': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}}, + 'Android5': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}}, + 'Safari7': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}, + 'Safari8': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}, + 'Safari9': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}, + 'Safari10': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}, + 'iOS7': {unitTest: {target: 'BS', required: true}, e2e: {target: null, required: true}}, + 'iOS8': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}, + 'iOS9': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}, + 'iOS10': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}, + 'WindowsPhone': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}} }; var customLaunchers = { - 'DartiumWithWebPlatform': { - base: 'Dartium', - flags: ['--enable-experimental-web-platform-features'] }, - 'ChromeNoSandbox': { - base: 'Chrome', - flags: ['--no-sandbox'] }, - 'SL_CHROME': { - base: 'SauceLabs', - browserName: 'chrome', - version: '52' - }, - 'SL_CHROMEBETA': { - base: 'SauceLabs', - browserName: 'chrome', - version: 'beta' - }, - 'SL_CHROMEDEV': { - base: 'SauceLabs', - browserName: 'chrome', - version: 'dev' - }, - 'SL_FIREFOX': { - base: 'SauceLabs', - browserName: 'firefox', - version: '46' - }, - 'SL_FIREFOXBETA': { - base: 'SauceLabs', - browserName: 'firefox', - version: 'beta' - }, - 'SL_FIREFOXDEV': { - base: 'SauceLabs', - browserName: 'firefox', - version: 'dev' - }, - 'SL_SAFARI7': { - base: 'SauceLabs', - browserName: 'safari', - platform: 'OS X 10.9', - version: '7.0' - }, - 'SL_SAFARI8': { - base: 'SauceLabs', - browserName: 'safari', - platform: 'OS X 10.10', - version: '8.0' - }, - 'SL_SAFARI9': { - base: 'SauceLabs', - browserName: 'safari', - platform: 'OS X 10.11', - version: '9.0' - }, - 'SL_IOS7': { - base: 'SauceLabs', - browserName: 'iphone', - platform: 'OS X 10.10', - version: '7.1' - }, - 'SL_IOS8': { - base: 'SauceLabs', - browserName: 'iphone', - platform: 'OS X 10.10', - version: '8.4' - }, - 'SL_IOS9': { - base: 'SauceLabs', - browserName: 'iphone', - platform: 'OS X 10.10', - version: '9.3' - }, - 'SL_IE9': { - base: 'SauceLabs', - browserName: 'internet explorer', - platform: 'Windows 2008', - version: '9' - }, + 'DartiumWithWebPlatform': + {base: 'Dartium', flags: ['--enable-experimental-web-platform-features']}, + 'ChromeNoSandbox': {base: 'Chrome', flags: ['--no-sandbox']}, + 'SL_CHROME': {base: 'SauceLabs', browserName: 'chrome', version: '52'}, + 'SL_CHROMEBETA': {base: 'SauceLabs', browserName: 'chrome', version: 'beta'}, + 'SL_CHROMEDEV': {base: 'SauceLabs', browserName: 'chrome', version: 'dev'}, + 'SL_FIREFOX': {base: 'SauceLabs', browserName: 'firefox', version: '46'}, + 'SL_FIREFOXBETA': {base: 'SauceLabs', browserName: 'firefox', version: 'beta'}, + 'SL_FIREFOXDEV': {base: 'SauceLabs', browserName: 'firefox', version: 'dev'}, + 'SL_SAFARI7': {base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.9', version: '7.0'}, + 'SL_SAFARI8': {base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.10', version: '8.0'}, + 'SL_SAFARI9': {base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.11', version: '9.0'}, + 'SL_SAFARI10': + {base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.12', version: '10.0'}, + 'SL_IOS7': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '7.1'}, + 'SL_IOS8': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '8.4'}, + 'SL_IOS9': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '9.3'}, + 'SL_IOS10': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '10.0'}, + 'SL_IE9': + {base: 'SauceLabs', browserName: 'internet explorer', platform: 'Windows 2008', version: '9'}, 'SL_IE10': { base: 'SauceLabs', browserName: 'internet explorer', platform: 'Windows 2012', version: '10' }, - 'SL_IE11': { - base: 'SauceLabs', - browserName: 'internet explorer', - platform: 'Windows 8.1', - version: '11' - }, + 'SL_IE11': + {base: 'SauceLabs', browserName: 'internet explorer', platform: 'Windows 8.1', version: '11'}, 'SL_EDGE': { base: 'SauceLabs', browserName: 'MicrosoftEdge', platform: 'Windows 10', version: '13.10586' }, - 'SL_ANDROID4.1': { - base: 'SauceLabs', - browserName: 'android', - platform: 'Linux', - version: '4.1' - }, - 'SL_ANDROID4.2': { - base: 'SauceLabs', - browserName: 'android', - platform: 'Linux', - version: '4.2' - }, - 'SL_ANDROID4.3': { - base: 'SauceLabs', - browserName: 'android', - platform: 'Linux', - version: '4.3' - }, - 'SL_ANDROID4.4': { - base: 'SauceLabs', - browserName: 'android', - platform: 'Linux', - version: '4.4' - }, - 'SL_ANDROID5': { - base: 'SauceLabs', - browserName: 'android', - platform: 'Linux', - version: '5.1' - }, + 'SL_ANDROID4.1': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.1'}, + 'SL_ANDROID4.2': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.2'}, + 'SL_ANDROID4.3': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.3'}, + 'SL_ANDROID4.4': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.4'}, + 'SL_ANDROID5': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '5.1'}, - 'BS_CHROME': { - base: 'BrowserStack', - browser: 'chrome', - os: 'OS X', - os_version: 'Yosemite' - }, - 'BS_FIREFOX': { - base: 'BrowserStack', - browser: 'firefox', - os: 'Windows', - os_version: '10' - }, - 'BS_SAFARI7': { - base: 'BrowserStack', - browser: 'safari', - os: 'OS X', - os_version: 'Mavericks' - }, - 'BS_SAFARI8': { - base: 'BrowserStack', - browser: 'safari', - os: 'OS X', - os_version: 'Yosemite' - }, - 'BS_SAFARI9': { - base: 'BrowserStack', - browser: 'safari', - os: 'OS X', - os_version: 'El Capitan' - }, - 'BS_IOS7': { - base: 'BrowserStack', - device: 'iPhone 5S', - os: 'ios', - os_version: '7.0' - }, - 'BS_IOS8': { - base: 'BrowserStack', - device: 'iPhone 6', - os: 'ios', - os_version: '8.3' - }, - 'BS_IOS9': { - base: 'BrowserStack', - device: 'iPhone 6S', - os: 'ios', - os_version: '9.1' - }, - 'BS_IE9': { - base: 'BrowserStack', - browser: 'ie', - browser_version: '9.0', - os: 'Windows', - os_version: '7' - }, + 'BS_CHROME': {base: 'BrowserStack', browser: 'chrome', os: 'OS X', os_version: 'Yosemite'}, + 'BS_FIREFOX': {base: 'BrowserStack', browser: 'firefox', os: 'Windows', os_version: '10'}, + 'BS_SAFARI7': {base: 'BrowserStack', browser: 'safari', os: 'OS X', os_version: 'Mavericks'}, + 'BS_SAFARI8': {base: 'BrowserStack', browser: 'safari', os: 'OS X', os_version: 'Yosemite'}, + 'BS_SAFARI9': {base: 'BrowserStack', browser: 'safari', os: 'OS X', os_version: 'El Capitan'}, + 'BS_SAFARI10': {base: 'BrowserStack', browser: 'safari', os: 'OS X', os_version: 'Sierra'}, + 'BS_IOS7': {base: 'BrowserStack', device: 'iPhone 5S', os: 'ios', os_version: '7.0'}, + 'BS_IOS8': {base: 'BrowserStack', device: 'iPhone 6', os: 'ios', os_version: '8.3'}, + 'BS_IOS9': {base: 'BrowserStack', device: 'iPhone 6S', os: 'ios', os_version: '9.1'}, + 'BS_IOS10': {base: 'BrowserStack', device: 'iPhone SE', os: 'ios', os_version: '10.0'}, + 'BS_IE9': + {base: 'BrowserStack', browser: 'ie', browser_version: '9.0', os: 'Windows', os_version: '7'}, 'BS_IE10': { base: 'BrowserStack', browser: 'ie', @@ -225,58 +107,35 @@ var customLaunchers = { os: 'Windows', os_version: '10' }, - 'BS_EDGE': { - base: 'BrowserStack', - browser: 'edge', - os: 'Windows', - os_version: '10' - }, - 'BS_WINDOWSPHONE' : { - base: 'BrowserStack', - device: 'Nokia Lumia 930', - os: 'winphone', - os_version: '8.1' - }, - 'BS_ANDROID5': { - base: 'BrowserStack', - device: 'Google Nexus 5', - os: 'android', - os_version: '5.0' - }, - 'BS_ANDROID4.4': { - base: 'BrowserStack', - device: 'HTC One M8', - os: 'android', - os_version: '4.4' - }, - 'BS_ANDROID4.3': { - base: 'BrowserStack', - device: 'Samsung Galaxy S4', - os: 'android', - os_version: '4.3' - }, - 'BS_ANDROID4.2': { - base: 'BrowserStack', - device: 'Google Nexus 4', - os: 'android', - os_version: '4.2' - }, - 'BS_ANDROID4.1': { - base: 'BrowserStack', - device: 'Google Nexus 7', - os: 'android', - os_version: '4.1' - } + 'BS_EDGE': {base: 'BrowserStack', browser: 'edge', os: 'Windows', os_version: '10'}, + 'BS_WINDOWSPHONE': + {base: 'BrowserStack', device: 'Nokia Lumia 930', os: 'winphone', os_version: '8.1'}, + 'BS_ANDROID5': {base: 'BrowserStack', device: 'Google Nexus 5', os: 'android', os_version: '5.0'}, + 'BS_ANDROID4.4': {base: 'BrowserStack', device: 'HTC One M8', os: 'android', os_version: '4.4'}, + 'BS_ANDROID4.3': + {base: 'BrowserStack', device: 'Samsung Galaxy S4', os: 'android', os_version: '4.3'}, + 'BS_ANDROID4.2': + {base: 'BrowserStack', device: 'Google Nexus 4', os: 'android', os_version: '4.2'}, + 'BS_ANDROID4.1': + {base: 'BrowserStack', device: 'Google Nexus 7', os: 'android', os_version: '4.1'} }; var sauceAliases = { - 'ALL': Object.keys(customLaunchers).filter(function(item) {return customLaunchers[item].base == 'SauceLabs';}), - 'DESKTOP': ['SL_CHROME', 'SL_FIREFOX', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9'], - 'MOBILE': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5', 'SL_IOS7', 'SL_IOS8', 'SL_IOS9'], + 'ALL': Object.keys(customLaunchers).filter(function(item) { + return customLaunchers[item].base == 'SauceLabs'; + }), + 'DESKTOP': [ + 'SL_CHROME', 'SL_FIREFOX', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI7', + 'SL_SAFARI8', 'SL_SAFARI9', 'SL_SAFARI10' + ], + 'MOBILE': [ + 'SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5', 'SL_IOS7', + 'SL_IOS8', 'SL_IOS9', 'SL_IOS10' + ], 'ANDROID': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5'], 'IE': ['SL_IE9', 'SL_IE10', 'SL_IE11'], - 'IOS': ['SL_IOS7', 'SL_IOS8', 'SL_IOS9'], - 'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9'], + 'IOS': ['SL_IOS7', 'SL_IOS8', 'SL_IOS9', 'SL_IOS10'], + 'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9', 'SL_SAFARI10'], 'BETA': ['SL_CHROMEBETA', 'SL_FIREFOXBETA'], 'DEV': ['SL_CHROMEDEV', 'SL_FIREFOXDEV'], 'CI_REQUIRED': buildConfiguration('unitTest', 'SL', true), @@ -284,13 +143,20 @@ var sauceAliases = { }; var browserstackAliases = { - 'ALL': Object.keys(customLaunchers).filter(function(item) {return customLaunchers[item].base == 'BrowserStack';}), - 'DESKTOP': ['BS_CHROME', 'BS_FIREFOX', 'BS_IE9', 'BS_IE10', 'BS_IE11', 'BS_EDGE', 'BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9'], - 'MOBILE': ['BS_ANDROID4.3', 'BS_ANDROID4.4', 'BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_WINDOWSPHONE'], + 'ALL': Object.keys(customLaunchers).filter(function(item) { + return customLaunchers[item].base == 'BrowserStack'; + }), + 'DESKTOP': [ + 'BS_CHROME', 'BS_FIREFOX', 'BS_IE9', 'BS_IE10', 'BS_IE11', 'BS_EDGE', 'BS_SAFARI7', + 'BS_SAFARI8', 'BS_SAFARI9', 'BS_SAFARI10' + ], + 'MOBILE': [ + 'BS_ANDROID4.3', 'BS_ANDROID4.4', 'BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_IOS10', 'BS_WINDOWSPHONE' + ], 'ANDROID': ['BS_ANDROID4.3', 'BS_ANDROID4.4'], 'IE': ['BS_IE9', 'BS_IE10', 'BS_IE11'], - 'IOS': ['BS_IOS7', 'BS_IOS8', 'BS_IOS9'], - 'SAFARI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9'], + 'IOS': ['BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_IOS10'], + 'SAFARI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9', 'BS_SAFARI10'], 'CI_REQUIRED': buildConfiguration('unitTest', 'BS', true), 'CI_OPTIONAL': buildConfiguration('unitTest', 'BS', false) }; @@ -303,11 +169,9 @@ module.exports = { function buildConfiguration(type, target, required) { return Object.keys(CIconfiguration) - .filter((item) => { - var conf = CIconfiguration[item][type]; - return conf.required === required && conf.target === target; - }) - .map((item) => { - return target + '_' + item.toUpperCase(); - }); + .filter((item) => { + var conf = CIconfiguration[item][type]; + return conf.required === required && conf.target === target; + }) + .map((item) => target + '_' + item.toUpperCase()); } diff --git a/gulpfile.js b/gulpfile.js index 99abcbec26c1..ce41eeeb2921 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + 'use strict'; // THIS CHECK SHOULD BE THE FIRST THING IN THIS FILE @@ -13,12 +21,14 @@ const os = require('os'); // clang-format entry points const srcsToFmt = [ - 'modules/@angular/**/*.ts', - 'modules/benchmarks/**/*.ts', - 'modules/e2e_util/**/*.ts', - 'modules/playground/**/*.ts', - 'tools/**/*.ts', + 'modules/@angular/**/*.{js,ts}', + 'modules/benchmarks/**/*.{js,ts}', + 'modules/e2e_util/**/*.{js,ts}', + 'modules/playground/**/*.{js,ts}', + 'tools/**/*.{js,ts}', '!tools/public_api_guard/**/*.d.ts', + './*.{js,ts}', + '!shims_for_IE.js', ]; // Check source code for formatting errors (clang-format) @@ -26,15 +36,16 @@ gulp.task('format:enforce', () => { const format = require('gulp-clang-format'); const clangFormat = require('clang-format'); return gulp.src(srcsToFmt).pipe( - format.checkFormat('file', clangFormat, {verbose: true, fail: true})); + format.checkFormat('file', clangFormat, {verbose: true, fail: true})); }); // Format the source code with clang-format (see .clang-format) gulp.task('format', () => { const format = require('gulp-clang-format'); const clangFormat = require('clang-format'); - return gulp.src(srcsToFmt, { base: '.' }).pipe( - format.format('file', clangFormat)).pipe(gulp.dest('.')); + return gulp.src(srcsToFmt, {base: '.'}) + .pipe(format.format('file', clangFormat)) + .pipe(gulp.dest('.')); }); const entrypoints = [ @@ -62,12 +73,18 @@ const entrypoints = [ ]; const publicApiDir = path.normalize('tools/public_api_guard'); const publicApiArgs = [ - '--rootDir', 'dist/packages-dist', - '--stripExportPattern', '^__', - '--allowModuleIdentifiers', 'jasmine', - '--allowModuleIdentifiers', 'protractor', - '--allowModuleIdentifiers', 'angular', - '--onStabilityMissing', 'error', + '--rootDir', + 'dist/packages-dist', + '--stripExportPattern', + '^__', + '--allowModuleIdentifiers', + 'jasmine', + '--allowModuleIdentifiers', + 'protractor', + '--allowModuleIdentifiers', + 'angular', + '--onStabilityMissing', + 'error', ].concat(entrypoints); // Build angular @@ -83,17 +100,17 @@ gulp.task('public-api:enforce', (done) => { const childProcess = require('child_process'); childProcess - .spawn( - path.join(__dirname, platformScriptPath(`/node_modules/.bin/ts-api-guardian`)), - ['--verifyDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'}) - .on('close', (errorCode) => { - if (errorCode !== 0) { - done(new Error( - 'Public API differs from golden file. Please run `gulp public-api:update`.')); - } else { - done(); - } - }); + .spawn( + path.join(__dirname, platformScriptPath(`/node_modules/.bin/ts-api-guardian`)), + ['--verifyDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'}) + .on('close', (errorCode) => { + if (errorCode !== 0) { + done(new Error( + 'Public API differs from golden file. Please run `gulp public-api:update`.')); + } else { + done(); + } + }); }); // Generate the public API golden files @@ -101,20 +118,24 @@ gulp.task('public-api:update', ['build.sh'], (done) => { const childProcess = require('child_process'); childProcess - .spawn( - path.join(__dirname, platformScriptPath(`/node_modules/.bin/ts-api-guardian`)), - ['--outDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'}) - .on('close', done); + .spawn( + path.join(__dirname, platformScriptPath(`/node_modules/.bin/ts-api-guardian`)), + ['--outDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'}) + .on('close', done); }); -// Checks tests for presence of ddescribe, fdescribe, fit, iit and fails the build if one of the focused tests is found. -// Currently xdescribe and xit are _not_ reported as errors since there are a couple of excluded tests in our code base. +// Checks tests for presence of ddescribe, fdescribe, fit, iit and fails the build if one of the +// focused tests is found. +// Currently xdescribe and xit are _not_ reported as errors since there are a couple of excluded +// tests in our code base. gulp.task('check-tests', function() { const ddescribeIit = require('gulp-ddescribe-iit'); - return gulp.src([ - 'modules/**/*.spec.ts', - 'modules/**/*_spec.ts', - ]).pipe(ddescribeIit({allowDisabledTests: true})); + return gulp + .src([ + 'modules/**/*.spec.ts', + 'modules/**/*_spec.ts', + ]) + .pipe(ddescribeIit({allowDisabledTests: true})); }); // Check the coding standards and programming errors @@ -123,14 +144,21 @@ gulp.task('lint', ['check-tests', 'format:enforce', 'tools:build'], () => { // Built-in rules are at // https://github.com/palantir/tslint#supported-rules const tslintConfig = require('./tslint.json'); - return gulp.src(['modules/@angular/**/*.ts', 'modules/benchpress/**/*.ts']) - .pipe(tslint({ - tslint: require('tslint').default, - configuration: tslintConfig, - rulesDirectory: 'dist/tools/tslint', - formatter: 'prose', - })) - .pipe(tslint.report({emitError: true})); + return gulp + .src([ + // todo(vicb): add .js files when supported + // see https://github.com/palantir/tslint/pull/1515 + 'modules/@angular/**/*.ts', + 'modules/benchpress/**/*.ts', + './*.ts', + ]) + .pipe(tslint({ + tslint: require('tslint').default, + configuration: tslintConfig, + rulesDirectory: 'dist/tools/tslint', + formatter: 'prose', + })) + .pipe(tslint.report({emitError: true})); }); gulp.task('tools:build', (done) => { tsc('tools/', done); }); @@ -142,7 +170,7 @@ gulp.task('check-cycle', (done) => { const dependencyObject = madge(['dist/all/'], { format: 'cjs', extensions: ['.js'], - onParseFile: function(data) { data.src = data.src.replace(/\/\* circular \*\//g, "//"); } + onParseFile: function(data) { data.src = data.src.replace(/\/\* circular \*\//g, '//'); } }); const circularDependencies = dependencyObject.circular().getArray(); if (circularDependencies.length > 0) { @@ -173,11 +201,11 @@ gulp.task('serve-examples', () => { const cors = require('cors'); connect.server({ - root: `${__dirname}/dist/examples`, - port: 8001, - livereload: false, - open: false, - middleware: (connect, opt) => [cors()], + root: `${__dirname}/dist/examples`, + port: 8001, + livereload: false, + open: false, + middleware: (connect, opt) => [cors()], }); }); @@ -187,16 +215,13 @@ gulp.task('changelog', () => { const conventionalChangelog = require('gulp-conventional-changelog'); return gulp.src('CHANGELOG.md') - .pipe(conventionalChangelog({ - preset: 'angular', - releaseCount: 1 - }, { - // Conventional Changelog Context - // We have to manually set version number so it doesn't get prefixed with `v` - // See https://github.com/conventional-changelog/conventional-changelog-core/issues/10 - currentTag: require('./package.json').version - })) - .pipe(gulp.dest('./')); + .pipe(conventionalChangelog({preset: 'angular', releaseCount: 1}, { + // Conventional Changelog Context + // We have to manually set version number so it doesn't get prefixed with `v` + // See https://github.com/conventional-changelog/conventional-changelog-core/issues/10 + currentTag: require('./package.json').version + })) + .pipe(gulp.dest('./')); }); function tsc(projectPath, done) { @@ -205,8 +230,7 @@ function tsc(projectPath, done) { childProcess .spawn( path.normalize(platformScriptPath(`${__dirname}/node_modules/.bin/tsc`)), - ['-p', path.join(__dirname, projectPath)], - {stdio: 'inherit'}) + ['-p', path.join(__dirname, projectPath)], {stdio: 'inherit'}) .on('close', done); } diff --git a/karma-js.conf.js b/karma-js.conf.js index 7a4d3cedc12a..443bf417df4e 100644 --- a/karma-js.conf.js +++ b/karma-js.conf.js @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + var browserProvidersConf = require('./browser-providers.conf.js'); var internalAngularReporter = require('./tools/karma/reporter.js'); @@ -17,24 +25,25 @@ module.exports = function(config) { // include Angular v1 for upgrade module testing 'node_modules/angular/angular.min.js', - 'node_modules/zone.js/dist/zone.js', - 'node_modules/zone.js/dist/long-stack-trace-zone.js', - 'node_modules/zone.js/dist/proxy.js', - 'node_modules/zone.js/dist/sync-test.js', - 'node_modules/zone.js/dist/jasmine-patch.js', - 'node_modules/zone.js/dist/async-test.js', + 'node_modules/zone.js/dist/zone.js', 'node_modules/zone.js/dist/long-stack-trace-zone.js', + 'node_modules/zone.js/dist/proxy.js', 'node_modules/zone.js/dist/sync-test.js', + 'node_modules/zone.js/dist/jasmine-patch.js', 'node_modules/zone.js/dist/async-test.js', 'node_modules/zone.js/dist/fake-async-test.js', // Including systemjs because it defines `__eval`, which produces correct stack traces. - 'shims_for_IE.js', - 'node_modules/systemjs/dist/system.src.js', + 'shims_for_IE.js', 'node_modules/systemjs/dist/system.src.js', {pattern: 'node_modules/rxjs/**', included: false, watched: false, served: true}, - 'node_modules/reflect-metadata/Reflect.js', - 'tools/build/file2modulename.js', - 'test-main.js', - {pattern: 'dist/all/empty.*', included: false, watched: false}, - {pattern: 'modules/@angular/platform-browser/test/static_assets/**', included: false, watched: false}, - {pattern: 'modules/@angular/platform-browser/test/browser/static_assets/**', included: false, watched: false} + 'node_modules/reflect-metadata/Reflect.js', 'tools/build/file2modulename.js', 'test-main.js', + {pattern: 'dist/all/empty.*', included: false, watched: false}, { + pattern: 'modules/@angular/platform-browser/test/static_assets/**', + included: false, + watched: false + }, + { + pattern: 'modules/@angular/platform-browser/test/browser/static_assets/**', + included: false, + watched: false, + } ], exclude: [ @@ -44,7 +53,7 @@ module.exports = function(config) { 'dist/all/@angular/benchpress/**', 'dist/all/angular1_router.js', 'dist/all/@angular/platform-browser/testing/e2e_util.js', - 'dist/examples/**/e2e_test/**' + 'dist/examples/**/e2e_test/**', ], customLaunchers: browserProvidersConf.customLaunchers, @@ -55,11 +64,11 @@ module.exports = function(config) { 'karma-sauce-launcher', 'karma-chrome-launcher', 'karma-sourcemap-loader', - internalAngularReporter + internalAngularReporter, ], preprocessors: { - '**/*.js': ['sourcemap'] + '**/*.js': ['sourcemap'], }, reporters: ['internal-angular'], @@ -73,7 +82,7 @@ module.exports = function(config) { 'selenium-version': '2.53.0', 'command-timeout': 600, 'idle-timeout': 600, - 'max-duration': 5400 + 'max-duration': 5400, } }, @@ -82,20 +91,21 @@ module.exports = function(config) { startTunnel: false, retryLimit: 3, timeout: 600, - pollingTimeout: 10000 + pollingTimeout: 10000, }, browsers: ['Chrome'], port: 9876, captureTimeout: 60000, - browserDisconnectTimeout : 60000, - browserDisconnectTolerance : 3, - browserNoActivityTimeout : 60000, + browserDisconnectTimeout: 60000, + browserDisconnectTolerance: 3, + browserNoActivityTimeout: 60000, }); if (process.env.TRAVIS) { - var buildId = 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')'; + var buildId = + 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')'; if (process.env.CI_MODE.startsWith('saucelabs')) { config.sauceLabs.build = buildId; config.sauceLabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER; diff --git a/modules/@angular/benchpress/src/common_options.ts b/modules/@angular/benchpress/src/common_options.ts index 7dc5b72730c3..255862e629d3 100644 --- a/modules/@angular/benchpress/src/common_options.ts +++ b/modules/@angular/benchpress/src/common_options.ts @@ -9,8 +9,6 @@ import {OpaqueToken} from '@angular/core'; import * as fs from 'fs'; -import {DateWrapper} from './facade/lang'; - export class Options { static SAMPLE_ID = new OpaqueToken('Options.sampleId'); static DEFAULT_DESCRIPTION = new OpaqueToken('Options.defaultDescription'); @@ -34,7 +32,7 @@ export class Options { {provide: Options.FORCE_GC, useValue: false}, {provide: Options.PREPARE, useValue: Options.NO_PREPARE}, {provide: Options.MICRO_METRICS, useValue: {}}, {provide: Options.USER_METRICS, useValue: {}}, - {provide: Options.NOW, useValue: () => DateWrapper.now()}, + {provide: Options.NOW, useValue: () => new Date()}, {provide: Options.RECEIVED_DATA, useValue: false}, {provide: Options.REQUEST_COUNT, useValue: false}, {provide: Options.CAPTURE_FRAMES, useValue: false}, diff --git a/modules/@angular/benchpress/src/measure_values.ts b/modules/@angular/benchpress/src/measure_values.ts index c8ef430a4086..13a67238dc41 100644 --- a/modules/@angular/benchpress/src/measure_values.ts +++ b/modules/@angular/benchpress/src/measure_values.ts @@ -6,18 +6,15 @@ * found in the LICENSE file at https://angular.io/license */ -import {Map} from './facade/collection'; -import {Date, DateWrapper} from './facade/lang'; - export class MeasureValues { constructor( public runIndex: number, public timeStamp: Date, public values: {[key: string]: any}) {} toJson() { return { - 'timeStamp': DateWrapper.toJson(this.timeStamp), + 'timeStamp': this.timeStamp.toJSON(), 'runIndex': this.runIndex, - 'values': this.values + 'values': this.values, }; } } diff --git a/modules/@angular/benchpress/src/metric/multi_metric.ts b/modules/@angular/benchpress/src/metric/multi_metric.ts index 09b4668836a4..1a92a3320023 100644 --- a/modules/@angular/benchpress/src/metric/multi_metric.ts +++ b/modules/@angular/benchpress/src/metric/multi_metric.ts @@ -7,7 +7,6 @@ */ import {Injector, OpaqueToken} from '@angular/core'; -import {StringMapWrapper} from '../facade/collection'; import {Metric} from '../metric'; @@ -57,8 +56,7 @@ export class MultiMetric extends Metric { function mergeStringMaps(maps: {[key: string]: string}[]): {[key: string]: string} { var result: {[key: string]: string} = {}; - maps.forEach( - map => { StringMapWrapper.forEach(map, (value, prop) => { result[prop] = value; }); }); + maps.forEach(map => { Object.keys(map).forEach(prop => { result[prop] = map[prop]; }); }); return result; } diff --git a/modules/@angular/benchpress/src/metric/user_metric.ts b/modules/@angular/benchpress/src/metric/user_metric.ts index 05d43e5d98e1..e1c221df268e 100644 --- a/modules/@angular/benchpress/src/metric/user_metric.ts +++ b/modules/@angular/benchpress/src/metric/user_metric.ts @@ -6,10 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ -import {Inject, Injectable, OpaqueToken, Provider} from '@angular/core'; +import {Inject, Injectable} from '@angular/core'; import {Options} from '../common_options'; -import {StringMapWrapper} from '../facade/collection'; import {isNumber} from '../facade/lang'; import {Metric} from '../metric'; import {WebDriverAdapter} from '../web_driver_adapter'; @@ -40,7 +39,7 @@ export class UserMetric extends Metric { reject = rej; }); let adapter = this._wdAdapter; - let names = StringMapWrapper.keys(this._userMetrics); + let names = Object.keys(this._userMetrics); function getAndClearValues() { Promise.all(names.map(name => adapter.executeScript(`return window.${name}`))) @@ -48,9 +47,9 @@ export class UserMetric extends Metric { if (values.every(isNumber)) { Promise.all(names.map(name => adapter.executeScript(`delete window.${name}`))) .then((_: any[]) => { - let map = StringMapWrapper.create(); + let map: {[k: string]: any} = {}; for (let i = 0, n = names.length; i < n; i++) { - StringMapWrapper.set(map, names[i], values[i]); + map[names[i]] = values[i]; } resolve(map); }, reject); diff --git a/modules/@angular/benchpress/src/reporter/console_reporter.ts b/modules/@angular/benchpress/src/reporter/console_reporter.ts index 8a4a5198afdf..96187a311f80 100644 --- a/modules/@angular/benchpress/src/reporter/console_reporter.ts +++ b/modules/@angular/benchpress/src/reporter/console_reporter.ts @@ -7,10 +7,7 @@ */ import {Inject, Injectable, OpaqueToken} from '@angular/core'; - -import {ListWrapper, StringMapWrapper} from '../facade/collection'; -import {NumberWrapper, isBlank, isPresent, print} from '../facade/lang'; -import {Math} from '../facade/math'; +import {print} from '../facade/lang'; import {MeasureValues} from '../measure_values'; import {Reporter} from '../reporter'; import {SampleDescription} from '../sample_description'; diff --git a/modules/@angular/benchpress/src/reporter/json_file_reporter.ts b/modules/@angular/benchpress/src/reporter/json_file_reporter.ts index 814aac37bc17..67c28a592b92 100644 --- a/modules/@angular/benchpress/src/reporter/json_file_reporter.ts +++ b/modules/@angular/benchpress/src/reporter/json_file_reporter.ts @@ -9,7 +9,7 @@ import {Inject, Injectable, OpaqueToken} from '@angular/core'; import {Options} from '../common_options'; -import {DateWrapper, Json, isBlank, isPresent} from '../facade/lang'; +import {Json} from '../facade/lang'; import {MeasureValues} from '../measure_values'; import {Reporter} from '../reporter'; import {SampleDescription} from '../sample_description'; @@ -45,8 +45,7 @@ export class JsonFileReporter extends Reporter { 'completeSample': completeSample, 'validSample': validSample, }); - var filePath = - `${this._path}/${this._description.id}_${DateWrapper.toMillis(this._now())}.json`; + var filePath = `${this._path}/${this._description.id}_${this._now().getTime()}.json`; return this._writeFile(filePath, content); } } diff --git a/modules/@angular/benchpress/src/reporter/util.ts b/modules/@angular/benchpress/src/reporter/util.ts index afe417c5ba57..d04371cc1ca6 100644 --- a/modules/@angular/benchpress/src/reporter/util.ts +++ b/modules/@angular/benchpress/src/reporter/util.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {StringMapWrapper} from '../facade/collection'; + import {NumberWrapper} from '../facade/lang'; import {MeasureValues} from '../measure_values'; import {Statistic} from '../statistic'; @@ -17,7 +17,7 @@ export function formatNum(n: number) { export function sortedProps(obj: {[key: string]: any}) { var props: string[] = []; - StringMapWrapper.forEach(obj, (value, prop) => props.push(prop)); + props.push(...Object.keys(obj)); props.sort(); return props; } @@ -30,4 +30,4 @@ export function formatStats(validSamples: MeasureValues[], metricName: string): // Note: Don't use the unicode character for +- as it might cause // hickups for consoles... return NumberWrapper.isNaN(cv) ? formattedMean : `${formattedMean}+-${Math.floor(cv)}%`; -} \ No newline at end of file +} diff --git a/modules/@angular/benchpress/src/runner.ts b/modules/@angular/benchpress/src/runner.ts index 5ad1674c9942..f684cb12a5ed 100644 --- a/modules/@angular/benchpress/src/runner.ts +++ b/modules/@angular/benchpress/src/runner.ts @@ -9,7 +9,7 @@ import {Provider, ReflectiveInjector} from '@angular/core'; import {Options} from './common_options'; -import {isBlank, isPresent} from './facade/lang'; +import {isPresent} from './facade/lang'; import {Metric} from './metric'; import {MultiMetric} from './metric/multi_metric'; import {PerflogMetric} from './metric/perflog_metric'; diff --git a/modules/@angular/benchpress/src/sample_description.ts b/modules/@angular/benchpress/src/sample_description.ts index 3cf5a99a3f41..a82e5e1b5eaa 100644 --- a/modules/@angular/benchpress/src/sample_description.ts +++ b/modules/@angular/benchpress/src/sample_description.ts @@ -9,7 +9,6 @@ import {OpaqueToken} from '@angular/core'; import {Options} from './common_options'; -import {StringMapWrapper} from './facade/collection'; import {Metric} from './metric'; import {Validator} from './validator'; @@ -42,7 +41,7 @@ export class SampleDescription { public metrics: {[key: string]: any}) { this.description = {}; descriptions.forEach(description => { - StringMapWrapper.forEach(description, (value, prop) => this.description[prop] = value); + Object.keys(description).forEach(prop => { this.description[prop] = description[prop]; }); }); } diff --git a/modules/@angular/benchpress/src/sampler.ts b/modules/@angular/benchpress/src/sampler.ts index 6576f0f7a1bd..f080827e6893 100644 --- a/modules/@angular/benchpress/src/sampler.ts +++ b/modules/@angular/benchpress/src/sampler.ts @@ -6,10 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ -import {Inject, Injectable, OpaqueToken} from '@angular/core'; +import {Inject, Injectable} from '@angular/core'; import {Options} from './common_options'; -import {Date, DateWrapper, isBlank, isPresent} from './facade/lang'; +import {isPresent} from './facade/lang'; import {MeasureValues} from './measure_values'; import {Metric} from './metric'; import {Reporter} from './reporter'; diff --git a/modules/@angular/benchpress/src/web_driver_extension.ts b/modules/@angular/benchpress/src/web_driver_extension.ts index afd680f37223..83e605c7f749 100644 --- a/modules/@angular/benchpress/src/web_driver_extension.ts +++ b/modules/@angular/benchpress/src/web_driver_extension.ts @@ -9,7 +9,6 @@ import {Injector, OpaqueToken} from '@angular/core'; import {Options} from './common_options'; -import {isBlank, isPresent} from './facade/lang'; export type PerfLogEvent = { [key: string]: any @@ -50,7 +49,7 @@ export abstract class WebDriverExtension { delegate = extension; } }); - if (isBlank(delegate)) { + if (!delegate) { throw new Error('Could not find a delegate for given capabilities!'); } return delegate; diff --git a/modules/@angular/benchpress/src/webdriver/chrome_driver_extension.ts b/modules/@angular/benchpress/src/webdriver/chrome_driver_extension.ts index 384ae4a0fdac..e89d4264e335 100644 --- a/modules/@angular/benchpress/src/webdriver/chrome_driver_extension.ts +++ b/modules/@angular/benchpress/src/webdriver/chrome_driver_extension.ts @@ -169,7 +169,7 @@ export class ChromeDriverExtension extends WebDriverExtension { eventCategories: string[], eventName: string, expectedCategories: string[], expectedName: string = null): boolean { var hasCategories = expectedCategories.reduce( - (value, cat) => { return value && eventCategories.indexOf(cat) !== -1; }, true); + (value, cat) => value && eventCategories.indexOf(cat) !== -1, true); return !expectedName ? hasCategories : hasCategories && eventName === expectedName; } diff --git a/modules/@angular/benchpress/src/webdriver/ios_driver_extension.ts b/modules/@angular/benchpress/src/webdriver/ios_driver_extension.ts index 0259485bcb0b..00bc1d98084a 100644 --- a/modules/@angular/benchpress/src/webdriver/ios_driver_extension.ts +++ b/modules/@angular/benchpress/src/webdriver/ios_driver_extension.ts @@ -52,7 +52,7 @@ export class IOsDriverExtension extends WebDriverExtension { /** @internal */ private _convertPerfRecordsToEvents(records: any[], events: PerfLogEvent[] = null) { - if (isBlank(events)) { + if (!events) { events = []; } records.forEach((record) => { diff --git a/modules/@angular/benchpress/test/metric/multi_metric_spec.ts b/modules/@angular/benchpress/test/metric/multi_metric_spec.ts index 95c05220d76f..9ee463253fc5 100644 --- a/modules/@angular/benchpress/test/metric/multi_metric_spec.ts +++ b/modules/@angular/benchpress/test/metric/multi_metric_spec.ts @@ -6,14 +6,14 @@ * found in the LICENSE file at https://angular.io/license */ -import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; +import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal'; import {Metric, MultiMetric, ReflectiveInjector} from '../../index'; export function main() { function createMetric(ids: any[]) { var m = ReflectiveInjector .resolveAndCreate([ - ids.map(id => { return {provide: id, useValue: new MockMetric(id)}; }), + ids.map(id => ({provide: id, useValue: new MockMetric(id)})), MultiMetric.provideWith(ids) ]) .get(MultiMetric); diff --git a/modules/@angular/benchpress/test/metric/perflog_metric_spec.ts b/modules/@angular/benchpress/test/metric/perflog_metric_spec.ts index d844fdc066e9..bf933e1ad880 100644 --- a/modules/@angular/benchpress/test/metric/perflog_metric_spec.ts +++ b/modules/@angular/benchpress/test/metric/perflog_metric_spec.ts @@ -7,11 +7,10 @@ */ import {Provider} from '@angular/core'; -import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; +import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@angular/core/testing/testing_internal'; import {Metric, Options, PerfLogEvent, PerfLogFeatures, PerflogMetric, ReflectiveInjector, WebDriverExtension} from '../../index'; -import {StringMapWrapper} from '../../src/facade/collection'; -import {isBlank, isPresent} from '../../src/facade/lang'; +import {isPresent} from '../../src/facade/lang'; import {TraceEventFactory} from '../trace_event_factory'; export function main() { @@ -28,12 +27,12 @@ export function main() { requestCount?: boolean } = {}): Metric { commandLog = []; - if (isBlank(perfLogFeatures)) { + if (!perfLogFeatures) { perfLogFeatures = new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true}); } - if (isBlank(microMetrics)) { - microMetrics = StringMapWrapper.create(); + if (!microMetrics) { + microMetrics = {}; } var providers: Provider[] = [ Options.DEFAULT_PROVIDERS, PerflogMetric.PROVIDERS, @@ -68,7 +67,7 @@ export function main() { function sortedKeys(stringMap: {[key: string]: any}) { var res: string[] = []; - StringMapWrapper.forEach(stringMap, (_, key) => { res.push(key); }); + res.push(...Object.keys(stringMap)); res.sort(); return res; } diff --git a/modules/@angular/benchpress/test/metric/user_metric_spec.ts b/modules/@angular/benchpress/test/metric/user_metric_spec.ts index 48b33db31f29..22ebb452763b 100644 --- a/modules/@angular/benchpress/test/metric/user_metric_spec.ts +++ b/modules/@angular/benchpress/test/metric/user_metric_spec.ts @@ -7,11 +7,9 @@ */ import {Provider, ReflectiveInjector} from '@angular/core'; -import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; +import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal'; -import {Injector, Metric, MultiMetric, Options, PerfLogEvent, PerfLogFeatures, PerflogMetric, UserMetric, WebDriverAdapter, WebDriverExtension} from '../../index'; -import {StringMapWrapper} from '../../src/facade/collection'; -import {Json, isBlank, isPresent} from '../../src/facade/lang'; +import {Options, PerfLogEvent, PerfLogFeatures, UserMetric, WebDriverAdapter} from '../../index'; export function main() { var wdAdapter: MockDriverAdapter; @@ -19,12 +17,12 @@ export function main() { function createMetric( perfLogs: PerfLogEvent[], perfLogFeatures: PerfLogFeatures, {userMetrics}: {userMetrics?: {[key: string]: string}} = {}): UserMetric { - if (isBlank(perfLogFeatures)) { + if (!perfLogFeatures) { perfLogFeatures = new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true}); } - if (isBlank(userMetrics)) { - userMetrics = StringMapWrapper.create(); + if (!userMetrics) { + userMetrics = {}; } wdAdapter = new MockDriverAdapter(); var providers: Provider[] = [ diff --git a/modules/@angular/benchpress/test/reporter/console_reporter_spec.ts b/modules/@angular/benchpress/test/reporter/console_reporter_spec.ts index f5bf67eac405..6420ba30c666 100644 --- a/modules/@angular/benchpress/test/reporter/console_reporter_spec.ts +++ b/modules/@angular/benchpress/test/reporter/console_reporter_spec.ts @@ -7,10 +7,10 @@ */ import {Provider} from '@angular/core'; -import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; +import {describe, expect, it} from '@angular/core/testing/testing_internal'; -import {ConsoleReporter, MeasureValues, ReflectiveInjector, Reporter, SampleDescription, SampleState} from '../../index'; -import {Date, DateWrapper, isBlank, isPresent} from '../../src/facade/lang'; +import {ConsoleReporter, MeasureValues, ReflectiveInjector, SampleDescription} from '../../index'; +import {isBlank, isPresent} from '../../src/facade/lang'; export function main() { describe('console reporter', () => { @@ -25,7 +25,7 @@ export function main() { metrics?: {[key: string]: any} }) { log = []; - if (isBlank(descriptions)) { + if (!descriptions) { descriptions = []; } if (isBlank(sampleId)) { @@ -90,5 +90,5 @@ export function main() { } function mv(runIndex: number, time: number, values: {[key: string]: number}) { - return new MeasureValues(runIndex, DateWrapper.fromMillis(time), values); + return new MeasureValues(runIndex, new Date(time), values); } diff --git a/modules/@angular/benchpress/test/reporter/json_file_reporter_spec.ts b/modules/@angular/benchpress/test/reporter/json_file_reporter_spec.ts index ba3f181d7534..3036f6c33e9a 100644 --- a/modules/@angular/benchpress/test/reporter/json_file_reporter_spec.ts +++ b/modules/@angular/benchpress/test/reporter/json_file_reporter_spec.ts @@ -6,10 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ -import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; +import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal'; import {JsonFileReporter, MeasureValues, Options, ReflectiveInjector, SampleDescription} from '../../index'; -import {DateWrapper, Json, isPresent} from '../../src/facade/lang'; +import {Json, isPresent} from '../../src/facade/lang'; export function main() { describe('file reporter', () => { @@ -27,7 +27,7 @@ export function main() { useValue: new SampleDescription(sampleId, descriptions, metrics) }, {provide: JsonFileReporter.PATH, useValue: path}, - {provide: Options.NOW, useValue: () => DateWrapper.fromMillis(1234)}, { + {provide: Options.NOW, useValue: () => new Date(1234)}, { provide: Options.WRITE_FILE, useValue: (filename: string, content: string) => { loggedFile = {'filename': filename, 'content': content}; @@ -77,5 +77,5 @@ export function main() { } function mv(runIndex: number, time: number, values: {[key: string]: number}) { - return new MeasureValues(runIndex, DateWrapper.fromMillis(time), values); + return new MeasureValues(runIndex, new Date(time), values); } diff --git a/modules/@angular/benchpress/test/reporter/multi_reporter_spec.ts b/modules/@angular/benchpress/test/reporter/multi_reporter_spec.ts index 9d4c1b9ac12d..f52588d808ec 100644 --- a/modules/@angular/benchpress/test/reporter/multi_reporter_spec.ts +++ b/modules/@angular/benchpress/test/reporter/multi_reporter_spec.ts @@ -6,16 +6,15 @@ * found in the LICENSE file at https://angular.io/license */ -import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; +import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal'; import {MeasureValues, MultiReporter, ReflectiveInjector, Reporter} from '../../index'; -import {DateWrapper} from '../../src/facade/lang'; export function main() { function createReporters(ids: any[]) { var r = ReflectiveInjector .resolveAndCreate([ - ids.map(id => { return {provide: id, useValue: new MockReporter(id)}; }), + ids.map(id => ({provide: id, useValue: new MockReporter(id)})), MultiReporter.provideWith(ids) ]) .get(MultiReporter); @@ -26,7 +25,7 @@ export function main() { it('should reportMeasureValues to all', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - var mv = new MeasureValues(0, DateWrapper.now(), {}); + var mv = new MeasureValues(0, new Date(), {}); createReporters(['m1', 'm2']).then((r) => r.reportMeasureValues(mv)).then((values) => { expect(values).toEqual([{'id': 'm1', 'values': mv}, {'id': 'm2', 'values': mv}]); @@ -35,9 +34,8 @@ export function main() { })); it('should reportSample to call', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - var completeSample = [ - new MeasureValues(0, DateWrapper.now(), {}), new MeasureValues(1, DateWrapper.now(), {}) - ]; + var completeSample = + [new MeasureValues(0, new Date(), {}), new MeasureValues(1, new Date(), {})]; var validSample = [completeSample[1]]; createReporters(['m1', 'm2']) diff --git a/modules/@angular/benchpress/test/runner_spec.ts b/modules/@angular/benchpress/test/runner_spec.ts index f63a8a569402..d5399a247e9a 100644 --- a/modules/@angular/benchpress/test/runner_spec.ts +++ b/modules/@angular/benchpress/test/runner_spec.ts @@ -6,10 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ -import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; +import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal'; import {Injector, Metric, Options, ReflectiveInjector, Runner, SampleDescription, SampleState, Sampler, Validator, WebDriverAdapter} from '../index'; -import {isBlank} from '../src/facade/lang'; export function main() { describe('runner', () => { @@ -17,7 +16,7 @@ export function main() { var runner: Runner; function createRunner(defaultProviders: any[] = null): Runner { - if (isBlank(defaultProviders)) { + if (!defaultProviders) { defaultProviders = []; } runner = new Runner([ diff --git a/modules/@angular/benchpress/test/sampler_spec.ts b/modules/@angular/benchpress/test/sampler_spec.ts index 5df862a3bae1..2f6bc0ad1273 100644 --- a/modules/@angular/benchpress/test/sampler_spec.ts +++ b/modules/@angular/benchpress/test/sampler_spec.ts @@ -6,10 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ -import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; +import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal'; import {MeasureValues, Metric, Options, ReflectiveInjector, Reporter, Sampler, Validator, WebDriverAdapter} from '../index'; -import {Date, DateWrapper, isBlank, isPresent, stringify} from '../src/facade/lang'; +import {isBlank, isPresent} from '../src/facade/lang'; export function main() { var EMPTY_EXECUTE = () => {}; @@ -26,10 +26,10 @@ export function main() { execute?: any } = {}) { var time = 1000; - if (isBlank(metric)) { + if (!metric) { metric = new MockMetric([]); } - if (isBlank(reporter)) { + if (!reporter) { reporter = new MockReporter([]); } if (isBlank(driver)) { @@ -39,7 +39,7 @@ export function main() { Options.DEFAULT_PROVIDERS, Sampler.PROVIDERS, {provide: Metric, useValue: metric}, {provide: Reporter, useValue: reporter}, {provide: WebDriverAdapter, useValue: driver}, {provide: Options.EXECUTE, useValue: execute}, {provide: Validator, useValue: validator}, - {provide: Options.NOW, useValue: () => DateWrapper.fromMillis(time++)} + {provide: Options.NOW, useValue: () => new Date(time++)} ]; if (isPresent(prepare)) { providers.push({provide: Options.PREPARE, useValue: prepare}); @@ -60,8 +60,8 @@ export function main() { createSampler({ driver: driver, validator: createCountingValidator(2), - prepare: () => { return count++; }, - execute: () => { return count++; } + prepare: () => count++, + execute: () => count++, }); sampler.sample().then((_) => { expect(count).toBe(4); @@ -204,7 +204,7 @@ export function main() { } function mv(runIndex: number, time: number, values: {[key: string]: number}) { - return new MeasureValues(runIndex, DateWrapper.fromMillis(time), values); + return new MeasureValues(runIndex, new Date(time), values); } function createCountingValidator( @@ -221,7 +221,7 @@ function createCountingValidator( function createCountingMetric(log: any[] = []) { var scriptTime = 0; - return new MockMetric(log, () => { return {'script': scriptTime++}; }); + return new MockMetric(log, () => ({'script': scriptTime++})); } class MockDriverAdapter extends WebDriverAdapter { diff --git a/modules/@angular/benchpress/test/statistic_spec.ts b/modules/@angular/benchpress/test/statistic_spec.ts index 075023d5033f..23124061a099 100644 --- a/modules/@angular/benchpress/test/statistic_spec.ts +++ b/modules/@angular/benchpress/test/statistic_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; +import {describe, expect, it} from '@angular/core/testing/testing_internal'; import {Statistic} from '../src/statistic'; export function main() { diff --git a/modules/@angular/benchpress/test/validator/regression_slope_validator_spec.ts b/modules/@angular/benchpress/test/validator/regression_slope_validator_spec.ts index c77459787064..9b7be2606334 100644 --- a/modules/@angular/benchpress/test/validator/regression_slope_validator_spec.ts +++ b/modules/@angular/benchpress/test/validator/regression_slope_validator_spec.ts @@ -6,11 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ -import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; +import {describe, expect, it} from '@angular/core/testing/testing_internal'; import {MeasureValues, ReflectiveInjector, RegressionSlopeValidator} from '../../index'; import {ListWrapper} from '../../src/facade/collection'; -import {Date, DateWrapper} from '../../src/facade/lang'; export function main() { describe('regression slope validator', () => { @@ -62,5 +61,5 @@ export function main() { } function mv(runIndex: number, time: number, values: {[key: string]: number}) { - return new MeasureValues(runIndex, DateWrapper.fromMillis(time), values); + return new MeasureValues(runIndex, new Date(time), values); } diff --git a/modules/@angular/benchpress/test/validator/size_validator_spec.ts b/modules/@angular/benchpress/test/validator/size_validator_spec.ts index e9d0e4e144ff..6cdb21eff80a 100644 --- a/modules/@angular/benchpress/test/validator/size_validator_spec.ts +++ b/modules/@angular/benchpress/test/validator/size_validator_spec.ts @@ -6,11 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ -import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; +import {describe, expect, it} from '@angular/core/testing/testing_internal'; -import {MeasureValues, ReflectiveInjector, SizeValidator, Validator} from '../../index'; +import {MeasureValues, ReflectiveInjector, SizeValidator} from '../../index'; import {ListWrapper} from '../../src/facade/collection'; -import {Date, DateWrapper} from '../../src/facade/lang'; export function main() { describe('size validator', () => { @@ -47,5 +46,5 @@ export function main() { } function mv(runIndex: number, time: number, values: {[key: string]: number}) { - return new MeasureValues(runIndex, DateWrapper.fromMillis(time), values); + return new MeasureValues(runIndex, new Date(time), values); } diff --git a/modules/@angular/benchpress/test/web_driver_extension_spec.ts b/modules/@angular/benchpress/test/web_driver_extension_spec.ts index 5eaa2865d5b5..eb968cfa5e34 100644 --- a/modules/@angular/benchpress/test/web_driver_extension_spec.ts +++ b/modules/@angular/benchpress/test/web_driver_extension_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; +import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal'; import {Options, ReflectiveInjector, WebDriverExtension} from '../index'; import {StringWrapper, isPresent} from '../src/facade/lang'; @@ -17,7 +17,7 @@ export function main() { try { res(ReflectiveInjector .resolveAndCreate([ - ids.map((id) => { return {provide: id, useValue: new MockExtension(id)}; }), + ids.map((id) => ({provide: id, useValue: new MockExtension(id)})), {provide: Options.CAPABILITIES, useValue: caps}, WebDriverExtension.provideFirstSupported(ids) ]) diff --git a/modules/@angular/benchpress/test/webdriver/chrome_driver_extension_spec.ts b/modules/@angular/benchpress/test/webdriver/chrome_driver_extension_spec.ts index ac7f054b8615..5c1376f08ec1 100644 --- a/modules/@angular/benchpress/test/webdriver/chrome_driver_extension_spec.ts +++ b/modules/@angular/benchpress/test/webdriver/chrome_driver_extension_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; +import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal'; import {ChromeDriverExtension, Options, ReflectiveInjector, WebDriverAdapter, WebDriverExtension} from '../../index'; import {Json, isBlank} from '../../src/facade/lang'; @@ -35,7 +35,7 @@ export function main() { function createExtension( perfRecords: any[] = null, userAgent: string = null, messageMethod = 'Tracing.dataCollected'): WebDriverExtension { - if (isBlank(perfRecords)) { + if (!perfRecords) { perfRecords = []; } if (isBlank(userAgent)) { @@ -396,11 +396,11 @@ class MockDriverAdapter extends WebDriverAdapter { logs(type: string) { this._log.push(['logs', type]); if (type === 'performance') { - return Promise.resolve(this._events.map((event) => { - return { - 'message': Json.stringify({'message': {'method': this._messageMethod, 'params': event}}) - }; - })); + return Promise.resolve(this._events.map( + (event) => ({ + 'message': + Json.stringify({'message': {'method': this._messageMethod, 'params': event}}) + }))); } else { return null; } diff --git a/modules/@angular/benchpress/test/webdriver/ios_driver_extension_spec.ts b/modules/@angular/benchpress/test/webdriver/ios_driver_extension_spec.ts index b749e939a59a..5ed46b5fd559 100644 --- a/modules/@angular/benchpress/test/webdriver/ios_driver_extension_spec.ts +++ b/modules/@angular/benchpress/test/webdriver/ios_driver_extension_spec.ts @@ -6,10 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ -import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; +import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal'; import {IOsDriverExtension, ReflectiveInjector, WebDriverAdapter, WebDriverExtension} from '../../index'; -import {Json, isBlank, isPresent} from '../../src/facade/lang'; +import {Json} from '../../src/facade/lang'; import {TraceEventFactory} from '../trace_event_factory'; export function main() { @@ -20,7 +20,7 @@ export function main() { var normEvents = new TraceEventFactory('timeline', 'pid0'); function createExtension(perfRecords: any[] = null): WebDriverExtension { - if (isBlank(perfRecords)) { + if (!perfRecords) { perfRecords = []; } log = []; @@ -156,7 +156,7 @@ function timeEndRecord(name: string, time: number) { } function durationRecord(type: string, startTime: number, endTime: number, children: any[] = null) { - if (isBlank(children)) { + if (!children) { children = []; } return {'type': type, 'startTime': startTime, 'endTime': endTime, 'children': children}; diff --git a/modules/@angular/common/rollup-testing.config.js b/modules/@angular/common/rollup-testing.config.js index 3a0e5d02362f..8c0e1d761806 100644 --- a/modules/@angular/common/rollup-testing.config.js +++ b/modules/@angular/common/rollup-testing.config.js @@ -1,3 +1,10 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ export default { entry: '../../../dist/packages-dist/common/testing/index.js', @@ -10,4 +17,4 @@ export default { 'rxjs/Observable': 'Rx', 'rxjs/Subject': 'Rx' } -} +}; diff --git a/modules/@angular/common/rollup.config.js b/modules/@angular/common/rollup.config.js index 0ff5c1a5df9a..1e6b494fc8e4 100644 --- a/modules/@angular/common/rollup.config.js +++ b/modules/@angular/common/rollup.config.js @@ -1,3 +1,10 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ export default { entry: '../../../dist/packages-dist/common/index.js', @@ -7,6 +14,6 @@ export default { globals: { '@angular/core': 'ng.core', 'rxjs/Observable': 'Rx', - 'rxjs/Subject': 'Rx' + 'rxjs/Subject': 'Rx', } -} +}; diff --git a/modules/@angular/common/src/directives/ng_plural.ts b/modules/@angular/common/src/directives/ng_plural.ts index a34919570f64..0f4aa616e30b 100644 --- a/modules/@angular/common/src/directives/ng_plural.ts +++ b/modules/@angular/common/src/directives/ng_plural.ts @@ -61,8 +61,7 @@ export class NgPlural { addCase(value: string, switchView: SwitchView): void { this._caseViews[value] = switchView; } - /** @internal */ - _updateView(): void { + private _updateView(): void { this._clearViews(); const cases = Object.keys(this._caseViews); @@ -70,13 +69,11 @@ export class NgPlural { this._activateView(this._caseViews[key]); } - /** @internal */ - _clearViews() { + private _clearViews() { if (this._activeView) this._activeView.destroy(); } - /** @internal */ - _activateView(view: SwitchView) { + private _activateView(view: SwitchView) { if (view) { this._activeView = view; this._activeView.create(); diff --git a/modules/@angular/common/src/directives/ng_style.ts b/modules/@angular/common/src/directives/ng_style.ts index dcedef2f4f59..26a57948086f 100644 --- a/modules/@angular/common/src/directives/ng_style.ts +++ b/modules/@angular/common/src/directives/ng_style.ts @@ -32,10 +32,8 @@ import {Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDif */ @Directive({selector: '[ngStyle]'}) export class NgStyle implements DoCheck { - /** @internal */ - _ngStyle: {[key: string]: string}; - /** @internal */ - _differ: KeyValueDiffer; + private _ngStyle: {[key: string]: string}; + private _differ: KeyValueDiffer; constructor( private _differs: KeyValueDiffers, private _ngEl: ElementRef, private _renderer: Renderer) {} @@ -69,7 +67,7 @@ export class NgStyle implements DoCheck { private _setStyle(nameAndUnit: string, value: string): void { const [name, unit] = nameAndUnit.split('.'); - value = value !== null && value !== void(0) && unit ? `${value}${unit}` : value; + value = value && unit ? `${value}${unit}` : value; this._renderer.setElementStyle(this._ngEl.nativeElement, name, value); } diff --git a/modules/@angular/common/src/directives/ng_switch.ts b/modules/@angular/common/src/directives/ng_switch.ts index 05a4d8bdee94..32b3254d05a1 100644 --- a/modules/@angular/common/src/directives/ng_switch.ts +++ b/modules/@angular/common/src/directives/ng_switch.ts @@ -111,8 +111,7 @@ export class NgSwitch { } } - /** @internal */ - _emptyAllActiveViews(): void { + private _emptyAllActiveViews(): void { const activeContainers = this._activeViews; for (var i = 0; i < activeContainers.length; i++) { activeContainers[i].destroy(); @@ -120,9 +119,7 @@ export class NgSwitch { this._activeViews = []; } - /** @internal */ - _activateViews(views: SwitchView[]): void { - // TODO(vicb): assert(this._activeViews.length === 0); + private _activateViews(views: SwitchView[]): void { if (views) { for (var i = 0; i < views.length; i++) { views[i].create(); @@ -141,8 +138,7 @@ export class NgSwitch { views.push(view); } - /** @internal */ - _deregisterView(value: any, view: SwitchView): void { + private _deregisterView(value: any, view: SwitchView): void { // `_CASE_DEFAULT` is used a marker for non-registered cases if (value === _CASE_DEFAULT) return; const views = this._valueViews.get(value); @@ -181,10 +177,8 @@ export class NgSwitch { @Directive({selector: '[ngSwitchCase]'}) export class NgSwitchCase { // `_CASE_DEFAULT` is used as a marker for a not yet initialized value - /** @internal */ - _value: any = _CASE_DEFAULT; - /** @internal */ - _view: SwitchView; + private _value: any = _CASE_DEFAULT; + private _view: SwitchView; private _switch: NgSwitch; constructor( diff --git a/modules/@angular/common/src/localization.ts b/modules/@angular/common/src/localization.ts index a84e73442a6b..a6c64c6fa2ef 100644 --- a/modules/@angular/common/src/localization.ts +++ b/modules/@angular/common/src/localization.ts @@ -67,7 +67,7 @@ export enum Plural { Two, Few, Many, - Other + Other, } /** diff --git a/modules/@angular/common/src/location/location.ts b/modules/@angular/common/src/location/location.ts index 8ff9dc494c2f..dab13b008745 100644 --- a/modules/@angular/common/src/location/location.ts +++ b/modules/@angular/common/src/location/location.ts @@ -49,16 +49,20 @@ export class Location { _subject: EventEmitter = new EventEmitter(); /** @internal */ _baseHref: string; - /** @internal */ _platformStrategy: LocationStrategy; constructor(platformStrategy: LocationStrategy) { this._platformStrategy = platformStrategy; - var browserBaseHref = this._platformStrategy.getBaseHref(); + const browserBaseHref = this._platformStrategy.getBaseHref(); this._baseHref = Location.stripTrailingSlash(_stripIndexHtml(browserBaseHref)); - this._platformStrategy.onPopState( - (ev) => { this._subject.emit({'url': this.path(true), 'pop': true, 'type': ev.type}); }); + this._platformStrategy.onPopState((ev) => { + this._subject.emit({ + 'url': this.path(true), + 'pop': true, + 'type': ev.type, + }); + }); } /** diff --git a/modules/@angular/common/test/directives/ng_class_spec.ts b/modules/@angular/common/test/directives/ng_class_spec.ts index 5c962840c0a6..15369390554a 100644 --- a/modules/@angular/common/test/directives/ng_class_spec.ts +++ b/modules/@angular/common/test/directives/ng_class_spec.ts @@ -8,7 +8,7 @@ import {Component} from '@angular/core'; import {ComponentFixture, TestBed, async} from '@angular/core/testing'; -import {beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; +import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal'; export function main() { describe('binding to CSS class list', () => { diff --git a/modules/@angular/common/test/localization_spec.ts b/modules/@angular/common/test/localization_spec.ts index afec2c0dcbba..28d82716fd31 100644 --- a/modules/@angular/common/test/localization_spec.ts +++ b/modules/@angular/common/test/localization_spec.ts @@ -8,7 +8,7 @@ import {LOCALE_ID} from '@angular/core'; import {TestBed} from '@angular/core/testing'; -import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; +import {beforeEach, describe, inject, it} from '@angular/core/testing/testing_internal'; import {expect} from '@angular/platform-browser/testing/matchers'; import {NgLocaleLocalization, NgLocalization, getPluralCategory} from '../src/localization'; diff --git a/modules/@angular/common/test/pipes/async_pipe_spec.ts b/modules/@angular/common/test/pipes/async_pipe_spec.ts index a0240a89e1b1..4f4f0309f692 100644 --- a/modules/@angular/common/test/pipes/async_pipe_spec.ts +++ b/modules/@angular/common/test/pipes/async_pipe_spec.ts @@ -8,12 +8,11 @@ import {AsyncPipe} from '@angular/common'; import {WrappedValue} from '@angular/core'; -import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; +import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@angular/core/testing/testing_internal'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {browserDetection} from '@angular/platform-browser/testing/browser_util'; import {EventEmitter} from '../../src/facade/async'; -import {isBlank} from '../../src/facade/lang'; import {SpyChangeDetectorRef} from '../spies'; export function main() { @@ -112,7 +111,7 @@ export function main() { var promise: Promise; var ref: SpyChangeDetectorRef; // adds longer timers for passing tests in IE - var timer = (!isBlank(getDOM()) && browserDetection.isIE) ? 50 : 10; + var timer = (getDOM() && browserDetection.isIE) ? 50 : 10; beforeEach(() => { promise = new Promise((res, rej) => { diff --git a/modules/@angular/common/test/pipes/date_pipe_spec.ts b/modules/@angular/common/test/pipes/date_pipe_spec.ts index 25e58c8bebb9..2667f88e1f99 100644 --- a/modules/@angular/common/test/pipes/date_pipe_spec.ts +++ b/modules/@angular/common/test/pipes/date_pipe_spec.ts @@ -8,11 +8,9 @@ import {DatePipe} from '@angular/common'; import {PipeResolver} from '@angular/compiler/src/pipe_resolver'; -import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal'; +import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal'; import {browserDetection} from '@angular/platform-browser/testing/browser_util'; -import {DateWrapper} from '../../src/facade/lang'; - export function main() { describe('DatePipe', () => { var date: Date; @@ -27,7 +25,7 @@ export function main() { // Tracking issue: https://github.com/angular/angular/issues/11187 beforeEach(() => { - date = DateWrapper.create(2015, 6, 15, 9, 3, 1); + date = new Date(2015, 5, 15, 9, 3, 1); pipe = new DatePipe('en-US'); }); diff --git a/modules/@angular/common/test/pipes/i18n_plural_pipe_spec.ts b/modules/@angular/common/test/pipes/i18n_plural_pipe_spec.ts index 5f0198bc132a..f042f777bfcd 100644 --- a/modules/@angular/common/test/pipes/i18n_plural_pipe_spec.ts +++ b/modules/@angular/common/test/pipes/i18n_plural_pipe_spec.ts @@ -8,7 +8,7 @@ import {I18nPluralPipe, NgLocalization} from '@angular/common'; import {PipeResolver} from '@angular/compiler/src/pipe_resolver'; -import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal'; +import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal'; export function main() { describe('I18nPluralPipe', () => { diff --git a/modules/@angular/common/test/pipes/i18n_select_pipe_spec.ts b/modules/@angular/common/test/pipes/i18n_select_pipe_spec.ts index bd4874dedaf9..2cecbf1bfd56 100644 --- a/modules/@angular/common/test/pipes/i18n_select_pipe_spec.ts +++ b/modules/@angular/common/test/pipes/i18n_select_pipe_spec.ts @@ -8,7 +8,7 @@ import {I18nSelectPipe} from '@angular/common'; import {PipeResolver} from '@angular/compiler/src/pipe_resolver'; -import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal'; +import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal'; export function main() { describe('I18nSelectPipe', () => { diff --git a/modules/@angular/common/test/pipes/lowercase_pipe_spec.ts b/modules/@angular/common/test/pipes/lowercase_pipe_spec.ts index 31d699007c5f..c508525e3f01 100644 --- a/modules/@angular/common/test/pipes/lowercase_pipe_spec.ts +++ b/modules/@angular/common/test/pipes/lowercase_pipe_spec.ts @@ -7,7 +7,7 @@ */ import {LowerCasePipe} from '@angular/common'; -import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal'; +import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal'; export function main() { describe('LowerCasePipe', () => { diff --git a/modules/@angular/common/test/pipes/number_pipe_spec.ts b/modules/@angular/common/test/pipes/number_pipe_spec.ts index e19a9e8786f1..29c8e80206ca 100644 --- a/modules/@angular/common/test/pipes/number_pipe_spec.ts +++ b/modules/@angular/common/test/pipes/number_pipe_spec.ts @@ -7,7 +7,7 @@ */ import {CurrencyPipe, DecimalPipe, PercentPipe} from '@angular/common'; -import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal'; +import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal'; import {browserDetection} from '@angular/platform-browser/testing/browser_util'; export function main() { diff --git a/modules/@angular/common/test/pipes/slice_pipe_spec.ts b/modules/@angular/common/test/pipes/slice_pipe_spec.ts index 683f9bb5a603..c3cbdea10eb3 100644 --- a/modules/@angular/common/test/pipes/slice_pipe_spec.ts +++ b/modules/@angular/common/test/pipes/slice_pipe_spec.ts @@ -9,7 +9,6 @@ import {CommonModule, SlicePipe} from '@angular/common'; import {Component} from '@angular/core'; import {TestBed, async} from '@angular/core/testing'; -import {browserDetection} from '@angular/platform-browser/testing/browser_util'; import {expect} from '@angular/platform-browser/testing/matchers'; export function main() { diff --git a/modules/@angular/common/test/pipes/uppercase_pipe_spec.ts b/modules/@angular/common/test/pipes/uppercase_pipe_spec.ts index e8bb9a469446..c4e391c78d76 100644 --- a/modules/@angular/common/test/pipes/uppercase_pipe_spec.ts +++ b/modules/@angular/common/test/pipes/uppercase_pipe_spec.ts @@ -7,7 +7,7 @@ */ import {UpperCasePipe} from '@angular/common'; -import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal'; +import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal'; export function main() { describe('UpperCasePipe', () => { diff --git a/modules/@angular/common/test/spies.ts b/modules/@angular/common/test/spies.ts index 6c4d6bb722d3..6338e5252c98 100644 --- a/modules/@angular/common/test/spies.ts +++ b/modules/@angular/common/test/spies.ts @@ -7,7 +7,7 @@ */ import {ChangeDetectorRef} from '@angular/core/src/change_detection/change_detector_ref'; -import {SpyObject, proxy} from '@angular/core/testing/testing_internal'; +import {SpyObject} from '@angular/core/testing/testing_internal'; export class SpyChangeDetectorRef extends SpyObject { constructor() { diff --git a/modules/@angular/common/testing/location_mock.ts b/modules/@angular/common/testing/location_mock.ts index 7b448bf44269..5b4d0c95908b 100644 --- a/modules/@angular/common/testing/location_mock.ts +++ b/modules/@angular/common/testing/location_mock.ts @@ -18,9 +18,7 @@ import {EventEmitter, Injectable} from '@angular/core'; @Injectable() export class SpyLocation implements Location { urlChanges: string[] = []; - /** @internal */ private _history: LocationState[] = [new LocationState('', '')]; - /** @internal */ private _historyIndex: number = 0; /** @internal */ _subject: EventEmitter = new EventEmitter(); diff --git a/modules/@angular/compiler-cli/integrationtest/test/i18n_spec.ts b/modules/@angular/compiler-cli/integrationtest/test/i18n_spec.ts index c4fdabd964cc..ace7ec35a584 100644 --- a/modules/@angular/compiler-cli/integrationtest/test/i18n_spec.ts +++ b/modules/@angular/compiler-cli/integrationtest/test/i18n_spec.ts @@ -36,25 +36,27 @@ const EXPECTED_XMB = ` translate me Welcome -`; + +`; const EXPECTED_XLIFF = ` - - - - translate me - - desc - meaning - - - Welcome - - - - -`; + + + + translate me + + desc + meaning + + + Welcome + + + + + +`; describe('template i18n extraction output', () => { const outDir = ''; diff --git a/modules/@angular/compiler-cli/integrationtest/webpack.config.js b/modules/@angular/compiler-cli/integrationtest/webpack.config.js index 5478a9431cc2..90d348e286bc 100644 --- a/modules/@angular/compiler-cli/integrationtest/webpack.config.js +++ b/modules/@angular/compiler-cli/integrationtest/webpack.config.js @@ -1,10 +1,14 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + module.exports = { target: 'node', entry: './test/all_spec.js', - output: { - filename: './all_spec.js' - }, - resolve: { - extensions: ['.js'] - }, + output: {filename: './all_spec.js'}, + resolve: {extensions: ['.js']}, }; diff --git a/modules/@angular/compiler-cli/package.json b/modules/@angular/compiler-cli/package.json index 85ed03595d0b..9cf0d04f6952 100644 --- a/modules/@angular/compiler-cli/package.json +++ b/modules/@angular/compiler-cli/package.json @@ -11,7 +11,7 @@ "dependencies": { "@angular/tsc-wrapped": "^0.3.0", "reflect-metadata": "^0.1.2", - "parse5": "1.3.2", + "parse5": "^2.2.1", "minimist": "^1.2.0" }, "peerDependencies": { diff --git a/modules/@angular/compiler-cli/src/codegen.ts b/modules/@angular/compiler-cli/src/codegen.ts index 91048668a019..354ee466c108 100644 --- a/modules/@angular/compiler-cli/src/codegen.ts +++ b/modules/@angular/compiler-cli/src/codegen.ts @@ -126,7 +126,7 @@ export class CodeGenerator { static create( options: AngularCompilerOptions, cliOptions: NgcCliOptions, program: ts.Program, compilerHost: ts.CompilerHost, reflectorHostContext?: ReflectorHostContext, - resourceLoader?: compiler.ResourceLoader): CodeGenerator { + resourceLoader?: compiler.ResourceLoader, reflectorHost?: ReflectorHost): CodeGenerator { resourceLoader = resourceLoader || { get: (s: string) => { if (!compilerHost.fileExists(s)) { @@ -148,10 +148,12 @@ export class CodeGenerator { } const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver(); - const usePathMapping = !!options.rootDirs && options.rootDirs.length > 0; - const reflectorHost = usePathMapping ? - new PathMappedReflectorHost(program, compilerHost, options, reflectorHostContext) : - new ReflectorHost(program, compilerHost, options, reflectorHostContext); + if (!reflectorHost) { + const usePathMapping = !!options.rootDirs && options.rootDirs.length > 0; + reflectorHost = usePathMapping ? + new PathMappedReflectorHost(program, compilerHost, options, reflectorHostContext) : + new ReflectorHost(program, compilerHost, options, reflectorHostContext); + } const staticReflector = new StaticReflector(reflectorHost); StaticAndDynamicReflectionCapabilities.install(staticReflector); const htmlParser = diff --git a/modules/@angular/compiler-cli/src/static_reflector.ts b/modules/@angular/compiler-cli/src/static_reflector.ts index 4bd14ddd4013..eac7121917af 100644 --- a/modules/@angular/compiler-cli/src/static_reflector.ts +++ b/modules/@angular/compiler-cli/src/static_reflector.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Attribute, Component, ContentChild, ContentChildren, Directive, Host, HostBinding, HostListener, Inject, Injectable, Input, NgModule, Optional, Output, Pipe, Query, Self, SkipSelf, ViewChild, ViewChildren, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core'; +import {Attribute, Component, ContentChild, ContentChildren, Directive, Host, HostBinding, HostListener, Inject, Injectable, Input, NgModule, Optional, Output, Pipe, Self, SkipSelf, ViewChild, ViewChildren, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core'; import {ReflectorReader} from './private_import_core'; diff --git a/modules/@angular/compiler-cli/test/reflector_host_spec.ts b/modules/@angular/compiler-cli/test/reflector_host_spec.ts index c37284bff31f..a0dc2c3e8d08 100644 --- a/modules/@angular/compiler-cli/test/reflector_host_spec.ts +++ b/modules/@angular/compiler-cli/test/reflector_host_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {beforeEach, ddescribe, describe, expect, iit, it} from '@angular/core/testing/testing_internal'; +import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal'; import * as ts from 'typescript'; import {ReflectorHost} from '../src/reflector_host'; diff --git a/modules/@angular/compiler-cli/test/static_reflector_spec.ts b/modules/@angular/compiler-cli/test/static_reflector_spec.ts index d44e8c212183..feb2d553eeb6 100644 --- a/modules/@angular/compiler-cli/test/static_reflector_spec.ts +++ b/modules/@angular/compiler-cli/test/static_reflector_spec.ts @@ -9,7 +9,6 @@ import {StaticReflector, StaticReflectorHost, StaticSymbol} from '@angular/compiler-cli/src/static_reflector'; import {HostListener, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core'; import {ListWrapper} from '@angular/facade/src/collection'; -import {isBlank} from '@angular/facade/src/lang'; import {MetadataCollector} from '@angular/tsc-wrapped'; import * as ts from 'typescript'; @@ -454,7 +453,7 @@ class MockReflectorHost implements StaticReflectorHost { getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol { var cacheKey = `${declarationFile}:${name}${members?'.'+members.join('.'):''}`; var result = this.staticTypeCache.get(cacheKey); - if (isBlank(result)) { + if (!result) { result = new StaticSymbol(declarationFile, name, members); this.staticTypeCache.set(cacheKey, result); } diff --git a/modules/@angular/compiler/rollup-testing.config.js b/modules/@angular/compiler/rollup-testing.config.js index dbc0d14d4e9b..8772530d02f6 100644 --- a/modules/@angular/compiler/rollup-testing.config.js +++ b/modules/@angular/compiler/rollup-testing.config.js @@ -1,3 +1,10 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ export default { entry: '../../../dist/packages-dist/compiler/testing/index.js', @@ -11,4 +18,4 @@ export default { 'rxjs/Observable': 'Rx', 'rxjs/Subject': 'Rx' } -} +}; diff --git a/modules/@angular/compiler/rollup.config.js b/modules/@angular/compiler/rollup.config.js index 3f6d4454907a..4aa577765164 100644 --- a/modules/@angular/compiler/rollup.config.js +++ b/modules/@angular/compiler/rollup.config.js @@ -1,3 +1,10 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ export default { entry: '../../../dist/packages-dist/compiler/index.js', @@ -7,9 +14,9 @@ export default { globals: { '@angular/core': 'ng.core', 'rxjs/Observable': 'Rx', - 'rxjs/Subject': 'Rx' + 'rxjs/Subject': 'Rx', }, plugins: [ -// nodeResolve({ jsnext: true, main: true }), + // nodeResolve({ jsnext: true, main: true }), ] -} +}; diff --git a/modules/@angular/compiler/src/animation/animation_compiler.ts b/modules/@angular/compiler/src/animation/animation_compiler.ts index f80ac3cc3e41..edb53987011b 100644 --- a/modules/@angular/compiler/src/animation/animation_compiler.ts +++ b/modules/@angular/compiler/src/animation/animation_compiler.ts @@ -6,75 +6,26 @@ * found in the LICENSE file at https://angular.io/license */ -import {CompileDirectiveMetadata} from '../compile_metadata'; -import {StringMapWrapper} from '../facade/collection'; -import {isBlank, isPresent} from '../facade/lang'; + +import {isPresent} from '../facade/lang'; import {Identifiers, resolveIdentifier} from '../identifiers'; import * as o from '../output/output_ast'; -import {ANY_STATE, AnimationOutput, DEFAULT_STATE, EMPTY_STATE} from '../private_import_core'; -import * as t from '../template_parser/template_ast'; - -import {AnimationAst, AnimationAstVisitor, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStepAst, AnimationStylesAst} from './animation_ast'; -import {AnimationParseError, ParsedAnimationResult, parseAnimationEntry, parseAnimationOutputName} from './animation_parser'; - -const animationCompilationCache = - new Map(); +import {ANY_STATE, DEFAULT_STATE, EMPTY_STATE} from '../private_import_core'; -export class CompiledAnimationTriggerResult { - constructor( - public name: string, public statesMapStatement: o.Statement, - public statesVariableName: string, public fnStatement: o.Statement, - public fnVariable: o.Expression) {} -} +import {AnimationAst, AnimationAstVisitor, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStepAst, AnimationStylesAst} from './animation_ast'; -export class CompiledComponentAnimationResult { - constructor( - public outputs: AnimationOutput[], public triggers: CompiledAnimationTriggerResult[]) {} +export class AnimationEntryCompileResult { + constructor(public name: string, public statements: o.Statement[], public fnExp: o.Expression) {} } export class AnimationCompiler { - compileComponent(component: CompileDirectiveMetadata, template: t.TemplateAst[]): - CompiledComponentAnimationResult { - var compiledAnimations: CompiledAnimationTriggerResult[] = []; - var groupedErrors: string[] = []; - var triggerLookup: {[key: string]: CompiledAnimationTriggerResult} = {}; - var componentName = component.type.name; - - component.template.animations.forEach(entry => { - var result = parseAnimationEntry(entry); - var triggerName = entry.name; - if (result.errors.length > 0) { - var errorMessage = - `Unable to parse the animation sequence for "${triggerName}" due to the following errors:`; - result.errors.forEach( - (error: AnimationParseError) => { errorMessage += '\n-- ' + error.msg; }); - groupedErrors.push(errorMessage); - } - - if (triggerLookup[triggerName]) { - groupedErrors.push( - `The animation trigger "${triggerName}" has already been registered on "${componentName}"`); - } else { - var factoryName = `${componentName}_${entry.name}`; - var visitor = new _AnimationBuilder(triggerName, factoryName); - var compileResult = visitor.build(result.ast); - compiledAnimations.push(compileResult); - triggerLookup[entry.name] = compileResult; - } + compile(factoryNamePrefix: string, parsedAnimations: AnimationEntryAst[]): + AnimationEntryCompileResult[] { + return parsedAnimations.map(entry => { + const factoryName = `${factoryNamePrefix}_${entry.name}`; + const visitor = new _AnimationBuilder(entry.name, factoryName); + return visitor.build(entry); }); - - var validatedProperties = _validateAnimationProperties(compiledAnimations, template); - validatedProperties.errors.forEach(error => { groupedErrors.push(error.msg); }); - - if (groupedErrors.length > 0) { - var errorMessageStr = - `Animation parsing for ${component.type.name} has failed due to the following errors:`; - groupedErrors.forEach(error => errorMessageStr += `\n- ${error}`); - throw new Error(errorMessageStr); - } - - animationCompilationCache.set(component, compiledAnimations); - return new CompiledComponentAnimationResult(validatedProperties.outputs, compiledAnimations); } } @@ -110,8 +61,7 @@ class _AnimationBuilder implements AnimationAstVisitor { } ast.styles.forEach(entry => { - stylesArr.push( - o.literalMap(StringMapWrapper.keys(entry).map(key => [key, o.literal(entry[key])]))); + stylesArr.push(o.literalMap(Object.keys(entry).map(key => [key, o.literal(entry[key])]))); }); return o.importExpr(resolveIdentifier(Identifiers.AnimationStyles)).instantiate([ @@ -182,9 +132,8 @@ class _AnimationBuilder implements AnimationAstVisitor { visitAnimationStateDeclaration( ast: AnimationStateDeclarationAst, context: _AnimationBuilderContext): void { var flatStyles: {[key: string]: string | number} = {}; - _getStylesArray(ast).forEach(entry => { - StringMapWrapper.forEach(entry, (value: string, key: string) => { flatStyles[key] = value; }); - }); + _getStylesArray(ast).forEach( + entry => { Object.keys(entry).forEach(key => { flatStyles[key] = entry[key]; }); }); context.stateMap.registerState(ast.stateName, flatStyles); } @@ -334,28 +283,27 @@ class _AnimationBuilder implements AnimationAstVisitor { statements); } - build(ast: AnimationAst): CompiledAnimationTriggerResult { + build(ast: AnimationAst): AnimationEntryCompileResult { var context = new _AnimationBuilderContext(); var fnStatement = ast.visit(this, context).toDeclStmt(this._fnVarName); var fnVariable = o.variable(this._fnVarName); var lookupMap: any[] = []; - StringMapWrapper.forEach( - context.stateMap.states, (value: {[key: string]: string}, stateName: string) => { - var variableValue = EMPTY_MAP; - if (isPresent(value)) { - let styleMap: any[] = []; - StringMapWrapper.forEach(value, (value: string, key: string) => { - styleMap.push([key, o.literal(value)]); - }); - variableValue = o.literalMap(styleMap); - } - lookupMap.push([stateName, variableValue]); - }); - - var compiledStatesMapExpr = this._statesMapVar.set(o.literalMap(lookupMap)).toDeclStmt(); - return new CompiledAnimationTriggerResult( - this.animationName, compiledStatesMapExpr, this._statesMapVarName, fnStatement, fnVariable); + Object.keys(context.stateMap.states).forEach(stateName => { + const value = context.stateMap.states[stateName]; + var variableValue = EMPTY_MAP; + if (isPresent(value)) { + let styleMap: any[] = []; + Object.keys(value).forEach(key => { styleMap.push([key, o.literal(value[key])]); }); + variableValue = o.literalMap(styleMap); + } + lookupMap.push([stateName, variableValue]); + }); + + const compiledStatesMapStmt = this._statesMapVar.set(o.literalMap(lookupMap)).toDeclStmt(); + const statements: o.Statement[] = [compiledStatesMapStmt, fnStatement]; + + return new AnimationEntryCompileResult(this.animationName, statements, fnVariable); } } @@ -371,7 +319,7 @@ class _AnimationBuilderStateMap { get states() { return this._states; } registerState(name: string, value: {[prop: string]: string | number} = null): void { var existingEntry = this._states[name]; - if (isBlank(existingEntry)) { + if (!existingEntry) { this._states[name] = value; } } @@ -397,7 +345,7 @@ function _isEndStateAnimateStep(step: AnimationAst): boolean { if (step instanceof AnimationStepAst && step.duration > 0 && step.keyframes.length == 2) { var styles1 = _getStylesArray(step.keyframes[0])[0]; var styles2 = _getStylesArray(step.keyframes[1])[0]; - return StringMapWrapper.isEmpty(styles1) && StringMapWrapper.isEmpty(styles2); + return Object.keys(styles1).length === 0 && Object.keys(styles2).length === 0; } return false; } @@ -405,99 +353,3 @@ function _isEndStateAnimateStep(step: AnimationAst): boolean { function _getStylesArray(obj: any): {[key: string]: any}[] { return obj.styles.styles; } - -function _validateAnimationProperties( - compiledAnimations: CompiledAnimationTriggerResult[], - template: t.TemplateAst[]): AnimationPropertyValidationOutput { - var visitor = new _AnimationTemplatePropertyVisitor(compiledAnimations); - t.templateVisitAll(visitor, template); - return new AnimationPropertyValidationOutput(visitor.outputs, visitor.errors); -} - -export class AnimationPropertyValidationOutput { - constructor(public outputs: AnimationOutput[], public errors: AnimationParseError[]) {} -} - -class _AnimationTemplatePropertyVisitor implements t.TemplateAstVisitor { - private _animationRegistry: {[key: string]: boolean}; - public errors: AnimationParseError[] = []; - public outputs: AnimationOutput[] = []; - - constructor(animations: CompiledAnimationTriggerResult[]) { - this._animationRegistry = this._buildCompileAnimationLookup(animations); - } - - private _buildCompileAnimationLookup(animations: CompiledAnimationTriggerResult[]): - {[key: string]: boolean} { - var map: {[key: string]: boolean} = {}; - animations.forEach(entry => { map[entry.name] = true; }); - return map; - } - - private _validateAnimationInputOutputPairs( - inputAsts: t.BoundElementPropertyAst[], outputAsts: t.BoundEventAst[], - animationRegistry: {[key: string]: any}, isHostLevel: boolean): void { - var detectedAnimationInputs: {[key: string]: boolean} = {}; - inputAsts.forEach(input => { - if (input.type == t.PropertyBindingType.Animation) { - var triggerName = input.name; - if (isPresent(animationRegistry[triggerName])) { - detectedAnimationInputs[triggerName] = true; - } else { - this.errors.push( - new AnimationParseError(`Couldn't find an animation entry for ${triggerName}`)); - } - } - }); - - outputAsts.forEach(output => { - if (output.name[0] == '@') { - var normalizedOutputData = parseAnimationOutputName(output.name.substr(1), this.errors); - let triggerName = normalizedOutputData.name; - let triggerEventPhase = normalizedOutputData.phase; - if (!animationRegistry[triggerName]) { - this.errors.push(new AnimationParseError( - `Couldn't find the corresponding ${isHostLevel ? 'host-level ' : '' }animation trigger definition for (@${triggerName})`)); - } else if (!detectedAnimationInputs[triggerName]) { - this.errors.push(new AnimationParseError( - `Unable to listen on (@${triggerName}.${triggerEventPhase}) because the animation trigger [@${triggerName}] isn't being used on the same element`)); - } else { - this.outputs.push(normalizedOutputData); - } - } - }); - } - - visitElement(ast: t.ElementAst, ctx: any): any { - this._validateAnimationInputOutputPairs( - ast.inputs, ast.outputs, this._animationRegistry, false); - - var componentOnElement: t.DirectiveAst = - ast.directives.find(directive => directive.directive.isComponent); - if (componentOnElement) { - let cachedComponentAnimations = animationCompilationCache.get(componentOnElement.directive); - if (cachedComponentAnimations) { - this._validateAnimationInputOutputPairs( - componentOnElement.hostProperties, componentOnElement.hostEvents, - this._buildCompileAnimationLookup(cachedComponentAnimations), true); - } - } - - t.templateVisitAll(this, ast.children); - } - - visitEmbeddedTemplate(ast: t.EmbeddedTemplateAst, ctx: any): any { - t.templateVisitAll(this, ast.children); - } - - visitEvent(ast: t.BoundEventAst, ctx: any): any {} - visitBoundText(ast: t.BoundTextAst, ctx: any): any {} - visitText(ast: t.TextAst, ctx: any): any {} - visitNgContent(ast: t.NgContentAst, ctx: any): any {} - visitAttr(ast: t.AttrAst, ctx: any): any {} - visitDirective(ast: t.DirectiveAst, ctx: any): any {} - visitReference(ast: t.ReferenceAst, ctx: any): any {} - visitVariable(ast: t.VariableAst, ctx: any): any {} - visitDirectiveProperty(ast: t.BoundDirectivePropertyAst, ctx: any): any {} - visitElementProperty(ast: t.BoundElementPropertyAst, ctx: any): any {} -} diff --git a/modules/@angular/compiler/src/animation/animation_parser.ts b/modules/@angular/compiler/src/animation/animation_parser.ts index 4870b2804d9d..d30456fcdbcb 100644 --- a/modules/@angular/compiler/src/animation/animation_parser.ts +++ b/modules/@angular/compiler/src/animation/animation_parser.ts @@ -6,13 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ -import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata} from '../compile_metadata'; +import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata, CompileDirectiveMetadata} from '../compile_metadata'; import {ListWrapper, StringMapWrapper} from '../facade/collection'; import {isArray, isBlank, isPresent, isString, isStringMap} from '../facade/lang'; import {Math} from '../facade/math'; import {ParseError} from '../parse_util'; +import {ANY_STATE, FILL_STYLE_FLAG} from '../private_import_core'; -import {ANY_STATE, AnimationOutput, FILL_STYLE_FLAG} from '../private_import_core'; import {AnimationAst, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStateTransitionExpression, AnimationStepAst, AnimationStylesAst, AnimationWithStepsAst} from './animation_ast'; import {StylesCollection} from './styles_collection'; @@ -20,73 +20,85 @@ const _INITIAL_KEYFRAME = 0; const _TERMINAL_KEYFRAME = 1; const _ONE_SECOND = 1000; +declare type Styles = { + [key: string]: string | number +}; + export class AnimationParseError extends ParseError { - constructor(message: any /** TODO #9100 */) { super(null, message); } + constructor(message: string) { super(null, message); } toString(): string { return `${this.msg}`; } } -export class ParsedAnimationResult { +export class AnimationEntryParseResult { constructor(public ast: AnimationEntryAst, public errors: AnimationParseError[]) {} } -export function parseAnimationEntry(entry: CompileAnimationEntryMetadata): ParsedAnimationResult { - var errors: AnimationParseError[] = []; - var stateStyles: {[key: string]: AnimationStylesAst} = {}; - var transitions: CompileAnimationStateTransitionMetadata[] = []; - - var stateDeclarationAsts: any[] /** TODO #9100 */ = []; - entry.definitions.forEach(def => { - if (def instanceof CompileAnimationStateDeclarationMetadata) { - _parseAnimationDeclarationStates(def, errors).forEach(ast => { - stateDeclarationAsts.push(ast); - stateStyles[ast.stateName] = ast.styles; - }); - } else { - transitions.push(def); +export class AnimationParser { + parseComponent(component: CompileDirectiveMetadata): AnimationEntryAst[] { + const errors: string[] = []; + const componentName = component.type.name; + const animationTriggerNames = new Set(); + const asts = component.template.animations.map(entry => { + const result = this.parseEntry(entry); + const ast = result.ast; + const triggerName = ast.name; + if (animationTriggerNames.has(triggerName)) { + result.errors.push(new AnimationParseError( + `The animation trigger "${triggerName}" has already been registered for the ${componentName} component`)); + } else { + animationTriggerNames.add(triggerName); + } + if (result.errors.length > 0) { + let errorMessage = + `- Unable to parse the animation sequence for "${triggerName}" on the ${componentName} component due to the following errors:`; + result.errors.forEach( + (error: AnimationParseError) => { errorMessage += '\n-- ' + error.msg; }); + errors.push(errorMessage); + } + return ast; + }); + + if (errors.length > 0) { + const errorString = errors.join('\n'); + throw new Error(`Animation parse errors:\n${errorString}`); } - }); - var stateTransitionAsts = - transitions.map(transDef => _parseAnimationStateTransition(transDef, stateStyles, errors)); + return asts; + } - var ast = new AnimationEntryAst(entry.name, stateDeclarationAsts, stateTransitionAsts); - return new ParsedAnimationResult(ast, errors); -} + parseEntry(entry: CompileAnimationEntryMetadata): AnimationEntryParseResult { + var errors: AnimationParseError[] = []; + var stateStyles: {[key: string]: AnimationStylesAst} = {}; + var transitions: CompileAnimationStateTransitionMetadata[] = []; + + var stateDeclarationAsts: AnimationStateDeclarationAst[] = []; + entry.definitions.forEach(def => { + if (def instanceof CompileAnimationStateDeclarationMetadata) { + _parseAnimationDeclarationStates(def, errors).forEach(ast => { + stateDeclarationAsts.push(ast); + stateStyles[ast.stateName] = ast.styles; + }); + } else { + transitions.push(def); + } + }); -export function parseAnimationOutputName( - outputName: string, errors: AnimationParseError[]): AnimationOutput { - var values = outputName.split('.'); - var name: string; - var phase: string = ''; - if (values.length > 1) { - name = values[0]; - let parsedPhase = values[1]; - switch (parsedPhase) { - case 'start': - case 'done': - phase = parsedPhase; - break; - - default: - errors.push(new AnimationParseError( - `The provided animation output phase value "${parsedPhase}" for "@${name}" is not supported (use start or done)`)); - } - } else { - name = outputName; - errors.push(new AnimationParseError( - `The animation trigger output event (@${name}) is missing its phase value name (start or done are currently supported)`)); + var stateTransitionAsts = + transitions.map(transDef => _parseAnimationStateTransition(transDef, stateStyles, errors)); + + var ast = new AnimationEntryAst(entry.name, stateDeclarationAsts, stateTransitionAsts); + return new AnimationEntryParseResult(ast, errors); } - return new AnimationOutput(name, phase, outputName); } function _parseAnimationDeclarationStates( stateMetadata: CompileAnimationStateDeclarationMetadata, errors: AnimationParseError[]): AnimationStateDeclarationAst[] { - var styleValues: {[key: string]: string | number}[] = []; + var styleValues: Styles[] = []; stateMetadata.styles.styles.forEach(stylesEntry => { // TODO (matsko): change this when we get CSS class integration support if (isStringMap(stylesEntry)) { - styleValues.push(<{[key: string]: string | number}>stylesEntry); + styleValues.push(stylesEntry as Styles); } else { errors.push(new AnimationParseError( `State based animations cannot contain references to other states`)); @@ -145,16 +157,6 @@ function _parseAnimationTransitionExpr( return expressions; } -function _fetchSylesFromState(stateName: string, stateStyles: {[key: string]: AnimationStylesAst}): - CompileAnimationStyleMetadata { - var entry = stateStyles[stateName]; - if (isPresent(entry)) { - var styles = <{[key: string]: string | number}[]>entry.styles; - return new CompileAnimationStyleMetadata(0, styles); - } - return null; -} - function _normalizeAnimationEntry(entry: CompileAnimationMetadata | CompileAnimationMetadata[]): CompileAnimationMetadata { return isArray(entry) ? new CompileAnimationSequenceMetadata(entry) : @@ -210,7 +212,7 @@ function _normalizeStyleStepEntry( } var newSteps: CompileAnimationMetadata[] = []; - var combinedStyles: {[key: string]: string | number}[]; + var combinedStyles: Styles[]; steps.forEach(step => { if (step instanceof CompileAnimationStyleMetadata) { // this occurs when a style step is followed by a previous style step @@ -266,7 +268,7 @@ function _normalizeStyleStepEntry( function _resolveStylesFromState( stateName: string, stateStyles: {[key: string]: AnimationStylesAst}, errors: AnimationParseError[]) { - var styles: {[key: string]: string | number}[] = []; + var styles: Styles[] = []; if (stateName[0] != ':') { errors.push(new AnimationParseError(`Animation states via styles must be prefixed with a ":"`)); } else { @@ -278,7 +280,7 @@ function _resolveStylesFromState( } else { value.styles.forEach(stylesEntry => { if (isStringMap(stylesEntry)) { - styles.push(<{[key: string]: string | number}>stylesEntry); + styles.push(stylesEntry as Styles); } }); } @@ -312,15 +314,13 @@ function _parseAnimationKeyframes( var lastOffset = 0; keyframeSequence.steps.forEach(styleMetadata => { var offset = styleMetadata.offset; - var keyframeStyles: {[key: string]: string | number} = {}; + var keyframeStyles: Styles = {}; styleMetadata.styles.forEach(entry => { - StringMapWrapper.forEach( - <{[key: string]: string | number}>entry, - (value: any /** TODO #9100 */, prop: any /** TODO #9100 */) => { - if (prop != 'offset') { - keyframeStyles[prop] = value; - } - }); + Object.keys(entry).forEach(prop => { + if (prop != 'offset') { + keyframeStyles[prop] = (entry as Styles)[prop]; + } + }); }); if (isPresent(offset)) { @@ -357,24 +357,22 @@ function _parseAnimationKeyframes( let entry = rawKeyframes[i]; let styles = entry[1]; - StringMapWrapper.forEach( - styles, (value: any /** TODO #9100 */, prop: any /** TODO #9100 */) => { - if (!isPresent(firstKeyframeStyles[prop])) { - firstKeyframeStyles[prop] = FILL_STYLE_FLAG; - } - }); + Object.keys(styles).forEach(prop => { + if (!isPresent(firstKeyframeStyles[prop])) { + firstKeyframeStyles[prop] = FILL_STYLE_FLAG; + } + }); } for (i = limit - 1; i >= 0; i--) { let entry = rawKeyframes[i]; let styles = entry[1]; - StringMapWrapper.forEach( - styles, (value: any /** TODO #9100 */, prop: any /** TODO #9100 */) => { - if (!isPresent(lastKeyframeStyles[prop])) { - lastKeyframeStyles[prop] = value; - } - }); + Object.keys(styles).forEach(prop => { + if (!isPresent(lastKeyframeStyles[prop])) { + lastKeyframeStyles[prop] = styles[prop]; + } + }); } return rawKeyframes.map( @@ -398,11 +396,9 @@ function _parseTransitionAnimation( if (entry instanceof CompileAnimationStyleMetadata) { entry.styles.forEach(stylesEntry => { // by this point we know that we only have stringmap values - var map = <{[key: string]: string | number}>stylesEntry; - StringMapWrapper.forEach( - map, (value: any /** TODO #9100 */, prop: any /** TODO #9100 */) => { - collectedStyles.insertAtTime(prop, time, value); - }); + var map = stylesEntry as Styles; + Object.keys(map).forEach( + prop => { collectedStyles.insertAtTime(prop, time, map[prop]); }); }); previousStyles = entry.styles; return; @@ -448,7 +444,7 @@ function _parseTransitionAnimation( } else { let styleData = styles; let offset = _TERMINAL_KEYFRAME; - let styleAst = new AnimationStylesAst(<{[key: string]: string | number}[]>styleData.styles); + let styleAst = new AnimationStylesAst(styleData.styles as Styles[]); var keyframe = new AnimationKeyframeAst(offset, styleAst); keyframes = [keyframe]; } @@ -460,9 +456,8 @@ function _parseTransitionAnimation( keyframes.forEach( (keyframe: any /** TODO #9100 */) => keyframe.styles.styles.forEach( - (entry: any /** TODO #9100 */) => StringMapWrapper.forEach( - entry, (value: any /** TODO #9100 */, prop: any /** TODO #9100 */) => - collectedStyles.insertAtTime(prop, currentTime, value)))); + (entry: any /** TODO #9100 */) => Object.keys(entry).forEach( + prop => { collectedStyles.insertAtTime(prop, currentTime, entry[prop]); }))); } else { // if the code reaches this stage then an error // has already been populated within the _normalizeStyleSteps() @@ -535,10 +530,11 @@ function _parseTimeExpression( function _createStartKeyframeFromEndKeyframe( endKeyframe: AnimationKeyframeAst, startTime: number, duration: number, collectedStyles: StylesCollection, errors: AnimationParseError[]): AnimationKeyframeAst { - var values: {[key: string]: string | number} = {}; + var values: Styles = {}; var endTime = startTime + duration; - endKeyframe.styles.styles.forEach((styleData: {[key: string]: string | number}) => { - StringMapWrapper.forEach(styleData, (val: any /** TODO #9100 */, prop: any /** TODO #9100 */) => { + endKeyframe.styles.styles.forEach((styleData: Styles) => { + Object.keys(styleData).forEach(prop => { + const val = styleData[prop]; if (prop == 'offset') return; var resultIndex = collectedStyles.indexOfAtOrBeforeTime(prop, startTime); diff --git a/modules/@angular/compiler/src/compile_metadata.ts b/modules/@angular/compiler/src/compile_metadata.ts index e7eddf4f4adf..a067044c6330 100644 --- a/modules/@angular/compiler/src/compile_metadata.ts +++ b/modules/@angular/compiler/src/compile_metadata.ts @@ -8,11 +8,10 @@ import {ChangeDetectionStrategy, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core'; -import {ListWrapper, MapWrapper, StringMapWrapper} from './facade/collection'; -import {isBlank, isPresent, isStringMap, normalizeBlank, normalizeBool} from './facade/lang'; -import {LifecycleHooks, reflector} from './private_import_core'; +import {ListWrapper, MapWrapper} from './facade/collection'; +import {isPresent, isStringMap, normalizeBlank, normalizeBool} from './facade/lang'; +import {LifecycleHooks} from './private_import_core'; import {CssSelector} from './selector'; -import {getUrlScheme} from './url_resolver'; import {sanitizeIdentifier, splitAtColon} from './util'; function unimplemented(): any { @@ -343,7 +342,8 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier { var hostProperties: {[key: string]: string} = {}; var hostAttributes: {[key: string]: string} = {}; if (isPresent(host)) { - StringMapWrapper.forEach(host, (value: string, key: string) => { + Object.keys(host).forEach(key => { + const value = host[key]; const matches = key.match(HOST_REG_EXP); if (matches === null) { hostAttributes[key] = value; diff --git a/modules/@angular/compiler/src/compiler.ts b/modules/@angular/compiler/src/compiler.ts index f23e7f20f6aa..b0f2633695c2 100644 --- a/modules/@angular/compiler/src/compiler.ts +++ b/modules/@angular/compiler/src/compiler.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {COMPILER_OPTIONS, ClassProvider, Compiler, CompilerFactory, CompilerOptions, Component, ExistingProvider, FactoryProvider, Inject, Injectable, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, TypeProvider, ValueProvider, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core'; +import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, Injectable, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core'; export * from './template_parser/template_ast'; export {TEMPLATE_TRANSFORMS} from './template_parser/template_parser'; diff --git a/modules/@angular/compiler/src/css_parser/css_lexer.ts b/modules/@angular/compiler/src/css_parser/css_lexer.ts index f616b85af87f..3966f203ed9d 100644 --- a/modules/@angular/compiler/src/css_parser/css_lexer.ts +++ b/modules/@angular/compiler/src/css_parser/css_lexer.ts @@ -9,7 +9,7 @@ import * as chars from '../chars'; import {BaseError} from '../facade/errors'; -import {StringWrapper, isPresent, resolveEnumToken} from '../facade/lang'; +import {StringWrapper, isPresent} from '../facade/lang'; export enum CssTokenType { EOF, @@ -223,8 +223,8 @@ export class CssScanner { var error: CssScannerError = null; if (!isMatchingType || (isPresent(value) && value != next.strValue)) { - var errorMessage = resolveEnumToken(CssTokenType, next.type) + ' does not match expected ' + - resolveEnumToken(CssTokenType, type) + ' value'; + var errorMessage = + CssTokenType[next.type] + ' does not match expected ' + CssTokenType[type] + ' value'; if (isPresent(value)) { errorMessage += ' ("' + next.strValue + '" should match "' + value + '")'; diff --git a/modules/@angular/compiler/src/directive_normalizer.ts b/modules/@angular/compiler/src/directive_normalizer.ts index a8e067d89cc7..729b55f37288 100644 --- a/modules/@angular/compiler/src/directive_normalizer.ts +++ b/modules/@angular/compiler/src/directive_normalizer.ts @@ -115,27 +115,27 @@ export class DirectiveNormalizer { const templateStyles = this.normalizeStylesheet(new CompileStylesheetMetadata( {styles: visitor.styles, styleUrls: visitor.styleUrls, moduleUrl: templateAbsUrl})); - const allStyles = templateMetadataStyles.styles.concat(templateStyles.styles); - const allStyleUrls = templateMetadataStyles.styleUrls.concat(templateStyles.styleUrls); - let encapsulation = templateMeta.encapsulation; if (isBlank(encapsulation)) { encapsulation = this._config.defaultEncapsulation; } - if (encapsulation === ViewEncapsulation.Emulated && allStyles.length === 0 && - allStyleUrls.length === 0) { + + const styles = templateMetadataStyles.styles.concat(templateStyles.styles); + const styleUrls = templateMetadataStyles.styleUrls.concat(templateStyles.styleUrls); + + if (encapsulation === ViewEncapsulation.Emulated && styles.length === 0 && + styleUrls.length === 0) { encapsulation = ViewEncapsulation.None; } + return new CompileTemplateMetadata({ encapsulation, - template: template, - templateUrl: templateAbsUrl, - styles: allStyles, - styleUrls: allStyleUrls, + template, + templateUrl: templateAbsUrl, styles, styleUrls, externalStylesheets: templateMeta.externalStylesheets, ngContentSelectors: visitor.ngContentSelectors, animations: templateMeta.animations, - interpolation: templateMeta.interpolation + interpolation: templateMeta.interpolation, }); } @@ -251,7 +251,6 @@ function _cloneDirectiveWithTemplate( viewProviders: directive.viewProviders, queries: directive.queries, viewQueries: directive.viewQueries, - entryComponents: directive.entryComponents, - template: template + entryComponents: directive.entryComponents, template, }); } diff --git a/modules/@angular/compiler/src/directive_resolver.ts b/modules/@angular/compiler/src/directive_resolver.ts index f01a86f3a43e..5b473c86a7ed 100644 --- a/modules/@angular/compiler/src/directive_resolver.ts +++ b/modules/@angular/compiler/src/directive_resolver.ts @@ -9,14 +9,10 @@ import {Component, Directive, HostBinding, HostListener, Injectable, Input, Output, Query, Type, resolveForwardRef} from '@angular/core'; import {StringMapWrapper} from './facade/collection'; -import {isPresent, stringify} from './facade/lang'; +import {stringify} from './facade/lang'; import {ReflectorReader, reflector} from './private_import_core'; import {splitAtColon} from './util'; -function _isDirectiveMetadata(type: any): type is Directive { - return type instanceof Directive; -} - /* * Resolve a `Type` for {@link Directive}. * @@ -32,54 +28,57 @@ export class DirectiveResolver { * Return {@link Directive} for a given `Type`. */ resolve(type: Type, throwIfNotFound = true): Directive { - var typeMetadata = this._reflector.annotations(resolveForwardRef(type)); - if (isPresent(typeMetadata)) { - var metadata = typeMetadata.find(_isDirectiveMetadata); - if (isPresent(metadata)) { - var propertyMetadata = this._reflector.propMetadata(type); + const typeMetadata = this._reflector.annotations(resolveForwardRef(type)); + if (typeMetadata) { + const metadata = typeMetadata.find(isDirectiveMetadata); + if (metadata) { + const propertyMetadata = this._reflector.propMetadata(type); return this._mergeWithPropertyMetadata(metadata, propertyMetadata, type); } } + if (throwIfNotFound) { throw new Error(`No Directive annotation found on ${stringify(type)}`); } + return null; } private _mergeWithPropertyMetadata( dm: Directive, propertyMetadata: {[key: string]: any[]}, directiveType: Type): Directive { - var inputs: string[] = []; - var outputs: string[] = []; - var host: {[key: string]: string} = {}; - var queries: {[key: string]: any} = {}; + const inputs: string[] = []; + const outputs: string[] = []; + const host: {[key: string]: string} = {}; + const queries: {[key: string]: any} = {}; + + Object.keys(propertyMetadata).forEach((propName: string) => { - StringMapWrapper.forEach(propertyMetadata, (metadata: any[], propName: string) => { - metadata.forEach(a => { + propertyMetadata[propName].forEach(a => { if (a instanceof Input) { - if (isPresent(a.bindingPropertyName)) { + if (a.bindingPropertyName) { inputs.push(`${propName}: ${a.bindingPropertyName}`); } else { inputs.push(propName); } } else if (a instanceof Output) { const output: Output = a; - if (isPresent(output.bindingPropertyName)) { + if (output.bindingPropertyName) { outputs.push(`${propName}: ${output.bindingPropertyName}`); } else { outputs.push(propName); } } else if (a instanceof HostBinding) { const hostBinding: HostBinding = a; - if (isPresent(hostBinding.hostPropertyName)) { + if (hostBinding.hostPropertyName) { host[`[${hostBinding.hostPropertyName}]`] = propName; } else { host[`[${propName}]`] = propName; } } else if (a instanceof HostListener) { const hostListener: HostListener = a; - var args = isPresent(hostListener.args) ? (hostListener.args).join(', ') : ''; - host[`(${hostListener.eventName})`] = `${propName}(${args})`; + const args = hostListener.args || []; + host[`(${hostListener.eventName})`] = `${propName}(${args.join(',')})`; } else if (a instanceof Query) { queries[propName] = a; } @@ -91,13 +90,14 @@ export class DirectiveResolver { private _extractPublicName(def: string) { return splitAtColon(def, [null, def])[1].trim(); } private _merge( - dm: Directive, inputs: string[], outputs: string[], host: {[key: string]: string}, + directive: Directive, inputs: string[], outputs: string[], host: {[key: string]: string}, queries: {[key: string]: any}, directiveType: Type): Directive { - let mergedInputs: string[]; + const mergedInputs: string[] = inputs; - if (isPresent(dm.inputs)) { + if (directive.inputs) { const inputNames: string[] = - dm.inputs.map((def: string): string => this._extractPublicName(def)); + directive.inputs.map((def: string): string => this._extractPublicName(def)); + inputs.forEach((inputDef: string) => { const publicName = this._extractPublicName(inputDef); if (inputNames.indexOf(publicName) > -1) { @@ -105,16 +105,15 @@ export class DirectiveResolver { `Input '${publicName}' defined multiple times in '${stringify(directiveType)}'`); } }); - mergedInputs = dm.inputs.concat(inputs); - } else { - mergedInputs = inputs; + + mergedInputs.unshift(...directive.inputs); } - let mergedOutputs: string[]; + let mergedOutputs: string[] = outputs; - if (isPresent(dm.outputs)) { + if (directive.outputs) { const outputNames: string[] = - dm.outputs.map((def: string): string => this._extractPublicName(def)); + directive.outputs.map((def: string): string => this._extractPublicName(def)); outputs.forEach((outputDef: string) => { const publicName = this._extractPublicName(outputDef); @@ -123,47 +122,48 @@ export class DirectiveResolver { `Output event '${publicName}' defined multiple times in '${stringify(directiveType)}'`); } }); - mergedOutputs = dm.outputs.concat(outputs); - } else { - mergedOutputs = outputs; + mergedOutputs.unshift(...directive.outputs); } - var mergedHost = isPresent(dm.host) ? StringMapWrapper.merge(dm.host, host) : host; - var mergedQueries = - isPresent(dm.queries) ? StringMapWrapper.merge(dm.queries, queries) : queries; + const mergedHost = directive.host ? StringMapWrapper.merge(directive.host, host) : host; + const mergedQueries = + directive.queries ? StringMapWrapper.merge(directive.queries, queries) : queries; - if (dm instanceof Component) { + if (directive instanceof Component) { return new Component({ - selector: dm.selector, + selector: directive.selector, inputs: mergedInputs, outputs: mergedOutputs, host: mergedHost, - exportAs: dm.exportAs, - moduleId: dm.moduleId, + exportAs: directive.exportAs, + moduleId: directive.moduleId, queries: mergedQueries, - changeDetection: dm.changeDetection, - providers: dm.providers, - viewProviders: dm.viewProviders, - entryComponents: dm.entryComponents, - template: dm.template, - templateUrl: dm.templateUrl, - styles: dm.styles, - styleUrls: dm.styleUrls, - encapsulation: dm.encapsulation, - animations: dm.animations, - interpolation: dm.interpolation + changeDetection: directive.changeDetection, + providers: directive.providers, + viewProviders: directive.viewProviders, + entryComponents: directive.entryComponents, + template: directive.template, + templateUrl: directive.templateUrl, + styles: directive.styles, + styleUrls: directive.styleUrls, + encapsulation: directive.encapsulation, + animations: directive.animations, + interpolation: directive.interpolation }); - } else { return new Directive({ - selector: dm.selector, + selector: directive.selector, inputs: mergedInputs, outputs: mergedOutputs, host: mergedHost, - exportAs: dm.exportAs, + exportAs: directive.exportAs, queries: mergedQueries, - providers: dm.providers + providers: directive.providers }); } } } + +function isDirectiveMetadata(type: any): type is Directive { + return type instanceof Directive; +} diff --git a/modules/@angular/compiler/src/expression_parser/ast.ts b/modules/@angular/compiler/src/expression_parser/ast.ts index 331fe62d66ef..340c47981ea2 100644 --- a/modules/@angular/compiler/src/expression_parser/ast.ts +++ b/modules/@angular/compiler/src/expression_parser/ast.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ListWrapper} from '../facade/collection'; + import {isBlank} from '../facade/lang'; export class ParserError { @@ -376,7 +376,7 @@ export class AstTransformer implements AstVisitor { } visitAll(asts: any[]): any[] { - var res = ListWrapper.createFixedSize(asts.length); + var res = new Array(asts.length); for (var i = 0; i < asts.length; ++i) { res[i] = asts[i].visit(this); } diff --git a/modules/@angular/compiler/src/i18n/serializers/xliff.ts b/modules/@angular/compiler/src/i18n/serializers/xliff.ts index c1fc9540bcb1..2600af3f7feb 100644 --- a/modules/@angular/compiler/src/i18n/serializers/xliff.ts +++ b/modules/@angular/compiler/src/i18n/serializers/xliff.ts @@ -27,7 +27,6 @@ const _PLACEHOLDER_TAG = 'x'; const _SOURCE_TAG = 'source'; const _TARGET_TAG = 'target'; const _UNIT_TAG = 'trans-unit'; -const _CR = (ws: number = 0) => new xml.Text(`\n${new Array(ws).join(' ')}`); // http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html // http://docs.oasis-open.org/xliff/v1.2/xliff-profile-html/xliff-profile-html-1.2.html @@ -44,34 +43,37 @@ export class Xliff implements Serializer { let transUnit = new xml.Tag(_UNIT_TAG, {id: id, datatype: 'html'}); transUnit.children.push( - _CR(8), new xml.Tag(_SOURCE_TAG, {}, visitor.serialize(message.nodes)), _CR(8), - new xml.Tag(_TARGET_TAG)); + new xml.CR(8), new xml.Tag(_SOURCE_TAG, {}, visitor.serialize(message.nodes)), + new xml.CR(8), new xml.Tag(_TARGET_TAG)); if (message.description) { transUnit.children.push( - _CR(8), + new xml.CR(8), new xml.Tag( 'note', {priority: '1', from: 'description'}, [new xml.Text(message.description)])); } if (message.meaning) { transUnit.children.push( - _CR(8), + new xml.CR(8), new xml.Tag('note', {priority: '1', from: 'meaning'}, [new xml.Text(message.meaning)])); } - transUnit.children.push(_CR(6)); + transUnit.children.push(new xml.CR(6)); - transUnits.push(_CR(6), transUnit); + transUnits.push(new xml.CR(6), transUnit); }); - const body = new xml.Tag('body', {}, [...transUnits, _CR(4)]); + const body = new xml.Tag('body', {}, [...transUnits, new xml.CR(4)]); const file = new xml.Tag( 'file', {'source-language': _SOURCE_LANG, datatype: 'plaintext', original: 'ng2.template'}, - [_CR(4), body, _CR(2)]); - const xliff = new xml.Tag('xliff', {version: _VERSION, xmlns: _XMLNS}, [_CR(2), file, _CR()]); + [new xml.CR(4), body, new xml.CR(2)]); + const xliff = new xml.Tag( + 'xliff', {version: _VERSION, xmlns: _XMLNS}, [new xml.CR(2), file, new xml.CR()]); - return xml.serialize([new xml.Declaration({version: '1.0', encoding: 'UTF-8'}), _CR(), xliff]); + return xml.serialize([ + new xml.Declaration({version: '1.0', encoding: 'UTF-8'}), new xml.CR(), xliff, new xml.CR() + ]); } load(content: string, url: string, messageBundle: MessageBundle): {[id: string]: ml.Node[]} { @@ -137,13 +139,15 @@ class _WriteVisitor implements i18n.Visitor { } visitTagPlaceholder(ph: i18n.TagPlaceholder, context?: any): xml.Node[] { - const startTagPh = new xml.Tag(_PLACEHOLDER_TAG, {id: ph.startName, ctype: ph.tag}); + const ctype = getCtypeForTag(ph.tag); + + const startTagPh = new xml.Tag(_PLACEHOLDER_TAG, {id: ph.startName, ctype}); if (ph.isVoid) { // void tags have no children nor closing tags return [startTagPh]; } - const closeTagPh = new xml.Tag(_PLACEHOLDER_TAG, {id: ph.closeName, ctype: ph.tag}); + const closeTagPh = new xml.Tag(_PLACEHOLDER_TAG, {id: ph.closeName, ctype}); return [startTagPh, ...this.serialize(ph.children), closeTagPh]; } @@ -287,3 +291,14 @@ class _LoadVisitor implements ml.Visitor { this._errors.push(new I18nError(node.sourceSpan, message)); } } + +function getCtypeForTag(tag: string): string { + switch (tag.toLowerCase()) { + case 'br': + return 'lb'; + case 'img': + return 'image'; + default: + return `x-${tag}`; + } +} \ No newline at end of file diff --git a/modules/@angular/compiler/src/i18n/serializers/xmb.ts b/modules/@angular/compiler/src/i18n/serializers/xmb.ts index e19391fd4b60..d582ef569bed 100644 --- a/modules/@angular/compiler/src/i18n/serializers/xmb.ts +++ b/modules/@angular/compiler/src/i18n/serializers/xmb.ts @@ -43,7 +43,6 @@ export class Xmb implements Serializer { write(messageMap: {[k: string]: i18n.Message}): string { const visitor = new _Visitor(); let rootNode = new xml.Tag(_MESSAGES_TAG); - rootNode.children.push(new xml.Text('\n')); Object.keys(messageMap).forEach((id) => { const message = messageMap[id]; @@ -58,16 +57,18 @@ export class Xmb implements Serializer { } rootNode.children.push( - new xml.Text(' '), new xml.Tag(_MESSAGE_TAG, attrs, visitor.serialize(message.nodes)), - new xml.Text('\n')); + new xml.CR(2), new xml.Tag(_MESSAGE_TAG, attrs, visitor.serialize(message.nodes))); }); + rootNode.children.push(new xml.CR()); + return xml.serialize([ new xml.Declaration({version: '1.0', encoding: 'UTF-8'}), - new xml.Text('\n'), + new xml.CR(), new xml.Doctype(_MESSAGES_TAG, _DOCTYPE), - new xml.Text('\n'), + new xml.CR(), rootNode, + new xml.CR(), ]); } diff --git a/modules/@angular/compiler/src/i18n/serializers/xml_helper.ts b/modules/@angular/compiler/src/i18n/serializers/xml_helper.ts index d5d142581d87..e7cbc8bbcbc1 100644 --- a/modules/@angular/compiler/src/i18n/serializers/xml_helper.ts +++ b/modules/@angular/compiler/src/i18n/serializers/xml_helper.ts @@ -88,6 +88,10 @@ export class Text implements Node { visit(visitor: IVisitor): any { return visitor.visitText(this); } } +export class CR extends Text { + constructor(ws: number = 0) { super(`\n${new Array(ws + 1).join(' ')}`); } +} + const _ESCAPED_CHARS: [RegExp, string][] = [ [/&/g, '&'], [/"/g, '"'], diff --git a/modules/@angular/compiler/src/identifiers.ts b/modules/@angular/compiler/src/identifiers.ts index a81eeb81c9e2..f1008b45c776 100644 --- a/modules/@angular/compiler/src/identifiers.ts +++ b/modules/@angular/compiler/src/identifiers.ts @@ -9,7 +9,7 @@ import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ElementRef, Injector, LOCALE_ID as LOCALE_ID_, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT as TRANSLATIONS_FORMAT_, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core'; import {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata'; -import {AnimationGroupPlayer, AnimationKeyframe, AnimationOutput, AnimationSequencePlayer, AnimationStyles, AppElement, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, DebugAppView, DebugContext, EMPTY_ARRAY, EMPTY_MAP, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, UNINITIALIZED, ValueUnwrapper, ViewType, ViewUtils, balanceAnimationKeyframes, castByValue, checkBinding, clearStyles, collectAndResolveStyles, devModeEqual, flattenNestedViewRenderNodes, interpolate, prepareFinalAnimationStyles, pureProxy1, pureProxy10, pureProxy2, pureProxy3, pureProxy4, pureProxy5, pureProxy6, pureProxy7, pureProxy8, pureProxy9, reflector, registerModuleFactory, renderStyles} from './private_import_core'; +import {AnimationGroupPlayer, AnimationKeyframe, AnimationSequencePlayer, AnimationStyles, AppElement, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, DebugAppView, DebugContext, EMPTY_ARRAY, EMPTY_MAP, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, UNINITIALIZED, ValueUnwrapper, ViewType, ViewUtils, balanceAnimationKeyframes, castByValue, checkBinding, clearStyles, collectAndResolveStyles, devModeEqual, flattenNestedViewRenderNodes, interpolate, prepareFinalAnimationStyles, pureProxy1, pureProxy10, pureProxy2, pureProxy3, pureProxy4, pureProxy5, pureProxy6, pureProxy7, pureProxy8, pureProxy9, reflector, registerModuleFactory, renderStyles} from './private_import_core'; import {assetUrl} from './util'; var APP_VIEW_MODULE_URL = assetUrl('core', 'linker/view'); @@ -266,11 +266,6 @@ export class Identifiers { moduleUrl: assetUrl('core', 'i18n/tokens'), runtime: TRANSLATIONS_FORMAT_ }; - static AnimationOutput: IdentifierSpec = { - name: 'AnimationOutput', - moduleUrl: assetUrl('core', 'animation/animation_output'), - runtime: AnimationOutput - }; } export function resolveIdentifier(identifier: IdentifierSpec) { diff --git a/modules/@angular/compiler/src/ml_parser/parser.ts b/modules/@angular/compiler/src/ml_parser/parser.ts index 1dcf48a14a9c..47e7c6272edf 100644 --- a/modules/@angular/compiler/src/ml_parser/parser.ts +++ b/modules/@angular/compiler/src/ml_parser/parser.ts @@ -123,7 +123,7 @@ class _TreeBuilder { // read = while (this._peek.type === lex.TokenType.EXPANSION_CASE_VALUE) { let expCase = this._parseExpansionCase(); - if (isBlank(expCase)) return; // error + if (!expCase) return; // error cases.push(expCase); } @@ -154,7 +154,7 @@ class _TreeBuilder { const start = this._advance(); const exp = this._collectExpansionExpTokens(start); - if (isBlank(exp)) return null; + if (!exp) return null; const end = this._advance(); exp.push(new lex.Token(lex.TokenType.EOF, [], end.sourceSpan)); diff --git a/modules/@angular/compiler/src/ng_module_compiler.ts b/modules/@angular/compiler/src/ng_module_compiler.ts index 4c0871818f3a..480ee0fadf7f 100644 --- a/modules/@angular/compiler/src/ng_module_compiler.ts +++ b/modules/@angular/compiler/src/ng_module_compiler.ts @@ -9,8 +9,8 @@ import {Injectable} from '@angular/core'; import {CompileDiDependencyMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTokenMetadata} from './compile_metadata'; -import {isBlank, isPresent} from './facade/lang'; -import {Identifiers, identifierToken, resolveIdentifier, resolveIdentifierToken} from './identifiers'; +import {isPresent} from './facade/lang'; +import {Identifiers, resolveIdentifier, resolveIdentifierToken} from './identifiers'; import * as o from './output/output_ast'; import {convertValueToOutputAst} from './output/value_util'; import {ParseLocation, ParseSourceFile, ParseSourceSpan} from './parse_util'; @@ -190,7 +190,7 @@ class _InjectorBuilder { resolvedProviderValueExpr = providerValueExpressions[0]; type = providerValueExpressions[0].type; } - if (isBlank(type)) { + if (!type) { type = o.DYNAMIC_TYPE; } if (isEager) { @@ -223,11 +223,11 @@ class _InjectorBuilder { resolveIdentifierToken(Identifiers.ComponentFactoryResolver).reference)) { result = o.THIS_EXPR; } - if (isBlank(result)) { + if (!result) { result = this._instances.get(dep.token.reference); } } - if (isBlank(result)) { + if (!result) { var args = [createDiTokenExpression(dep.token)]; if (dep.isOptional) { args.push(o.NULL_EXPR); diff --git a/modules/@angular/compiler/src/offline_compiler.ts b/modules/@angular/compiler/src/offline_compiler.ts index 90bd832096c4..e7cdaeb34f26 100644 --- a/modules/@angular/compiler/src/offline_compiler.ts +++ b/modules/@angular/compiler/src/offline_compiler.ts @@ -8,7 +8,9 @@ import {SchemaMetadata} from '@angular/core'; -import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, CompileTokenMetadata, StaticSymbol, createHostComponentMeta} from './compile_metadata'; +import {AnimationCompiler} from './animation/animation_compiler'; +import {AnimationParser} from './animation/animation_parser'; +import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, StaticSymbol, createHostComponentMeta} from './compile_metadata'; import {DirectiveNormalizer} from './directive_normalizer'; import {Identifiers, resolveIdentifier, resolveIdentifierToken} from './identifiers'; import {CompileMetadataResolver} from './metadata_resolver'; @@ -28,6 +30,9 @@ export class NgModulesSummary { } export class OfflineCompiler { + private _animationParser = new AnimationParser(); + private _animationCompiler = new AnimationCompiler(); + constructor( private _metadataResolver: CompileMetadataResolver, private _directiveNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser, @@ -162,14 +167,19 @@ export class OfflineCompiler { compMeta: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[], schemas: SchemaMetadata[], componentStyles: CompiledStylesheet, fileSuffix: string, targetStatements: o.Statement[]): string { + const parsedAnimations = this._animationParser.parseComponent(compMeta); const parsedTemplate = this._templateParser.parse( compMeta, compMeta.template.template, directives, pipes, schemas, compMeta.type.name); const stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]); - const viewResult = - this._viewCompiler.compileComponent(compMeta, parsedTemplate, stylesExpr, pipes); + const compiledAnimations = + this._animationCompiler.compile(compMeta.type.name, parsedAnimations); + const viewResult = this._viewCompiler.compileComponent( + compMeta, parsedTemplate, stylesExpr, pipes, compiledAnimations); if (componentStyles) { targetStatements.push(..._resolveStyleStatements(componentStyles, fileSuffix)); } + compiledAnimations.forEach( + entry => { entry.statements.forEach(statement => { targetStatements.push(statement); }); }); targetStatements.push(..._resolveViewStatements(viewResult)); return viewResult.viewFactoryVar; } diff --git a/modules/@angular/compiler/src/output/abstract_emitter.ts b/modules/@angular/compiler/src/output/abstract_emitter.ts index 54dc6b1981a9..f9ed12a5fea9 100644 --- a/modules/@angular/compiler/src/output/abstract_emitter.ts +++ b/modules/@angular/compiler/src/output/abstract_emitter.ts @@ -400,7 +400,7 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex } visitAllStatements(statements: o.Statement[], ctx: EmitterVisitorContext): void { - statements.forEach((stmt) => { return stmt.visitStatement(this, ctx); }); + statements.forEach((stmt) => stmt.visitStatement(this, ctx)); } } diff --git a/modules/@angular/compiler/src/output/js_emitter.ts b/modules/@angular/compiler/src/output/js_emitter.ts index c59e5584172d..2a34751137c0 100644 --- a/modules/@angular/compiler/src/output/js_emitter.ts +++ b/modules/@angular/compiler/src/output/js_emitter.ts @@ -7,7 +7,7 @@ */ -import {StringWrapper, evalExpression, isBlank, isPresent, isString} from '../facade/lang'; +import {isBlank, isPresent} from '../facade/lang'; import {EmitterVisitorContext, OutputEmitter} from './abstract_emitter'; import {AbstractJsEmitterVisitor} from './abstract_js_emitter'; diff --git a/modules/@angular/compiler/src/output/output_ast.ts b/modules/@angular/compiler/src/output/output_ast.ts index f148d402b3d4..d64eb1b4c9eb 100644 --- a/modules/@angular/compiler/src/output/output_ast.ts +++ b/modules/@angular/compiler/src/output/output_ast.ts @@ -8,9 +8,7 @@ import {CompileIdentifierMetadata} from '../compile_metadata'; -import {StringMapWrapper} from '../facade/collection'; -import {isBlank, isPresent, isString} from '../facade/lang'; -import {ValueTransformer, visitValue} from '../util'; +import {isPresent, isString} from '../facade/lang'; @@ -21,7 +19,7 @@ export enum TypeModifier { export abstract class Type { constructor(public modifiers: TypeModifier[] = null) { - if (isBlank(modifiers)) { + if (!modifiers) { this.modifiers = []; } } @@ -464,7 +462,7 @@ export enum StmtModifier { export abstract class Statement { constructor(public modifiers: StmtModifier[] = null) { - if (isBlank(modifiers)) { + if (!modifiers) { this.modifiers = []; } } @@ -519,7 +517,7 @@ export class ReturnStatement extends Statement { export class AbstractClassPart { constructor(public type: Type = null, public modifiers: StmtModifier[]) { - if (isBlank(modifiers)) { + if (!modifiers) { this.modifiers = []; } } diff --git a/modules/@angular/compiler/src/output/path_util.ts b/modules/@angular/compiler/src/output/path_util.ts index f9f762deb44b..aebfb7bbabb2 100644 --- a/modules/@angular/compiler/src/output/path_util.ts +++ b/modules/@angular/compiler/src/output/path_util.ts @@ -6,11 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {Injectable} from '@angular/core'; - -import {Math, isBlank, isPresent} from '../facade/lang'; - - // asset:// var _ASSET_URL_RE = /asset:([^\/]+)\/([^\/]+)\/(.+)/; diff --git a/modules/@angular/compiler/src/output/value_util.ts b/modules/@angular/compiler/src/output/value_util.ts index 9b0df14bc032..37ac5756ec2d 100644 --- a/modules/@angular/compiler/src/output/value_util.ts +++ b/modules/@angular/compiler/src/output/value_util.ts @@ -8,7 +8,6 @@ import {CompileIdentifierMetadata} from '../compile_metadata'; -import {StringMapWrapper} from '../facade/collection'; import {ValueTransformer, visitValue} from '../util'; import * as o from './output_ast'; @@ -24,9 +23,7 @@ class _ValueOutputAstTransformer implements ValueTransformer { visitStringMap(map: {[key: string]: any}, type: o.MapType): o.Expression { var entries: Array[] = []; - StringMapWrapper.forEach(map, (value: any, key: string) => { - entries.push([key, visitValue(value, this, null)]); - }); + Object.keys(map).forEach(key => { entries.push([key, visitValue(map[key], this, null)]); }); return o.literalMap(entries, type); } diff --git a/modules/@angular/compiler/src/private_import_core.ts b/modules/@angular/compiler/src/private_import_core.ts index 5bd4cd4df19e..8936e825f448 100644 --- a/modules/@angular/compiler/src/private_import_core.ts +++ b/modules/@angular/compiler/src/private_import_core.ts @@ -12,7 +12,6 @@ export const isDefaultChangeDetectionStrategy: typeof r.isDefaultChangeDetection r.isDefaultChangeDetectionStrategy; export type ChangeDetectorStatus = typeof r._ChangeDetectorStatus; export const ChangeDetectorStatus: typeof r.ChangeDetectorStatus = r.ChangeDetectorStatus; -r.CHANGE_DETECTION_STRATEGY_VALUES; export type LifecycleHooks = typeof r._LifecycleHooks; export const LifecycleHooks: typeof r.LifecycleHooks = r.LifecycleHooks; export const LIFECYCLE_HOOKS_VALUES: typeof r.LIFECYCLE_HOOKS_VALUES = r.LIFECYCLE_HOOKS_VALUES; @@ -75,8 +74,6 @@ export type AnimationKeyframe = typeof r._AnimationKeyframe; export const AnimationKeyframe: typeof r.AnimationKeyframe = r.AnimationKeyframe; export type AnimationStyles = typeof r._AnimationStyles; export const AnimationStyles: typeof r.AnimationStyles = r.AnimationStyles; -export type AnimationOutput = typeof r._AnimationOutput; -export const AnimationOutput: typeof r.AnimationOutput = r.AnimationOutput; export const ANY_STATE = r.ANY_STATE; export const DEFAULT_STATE = r.DEFAULT_STATE; export const EMPTY_STATE = r.EMPTY_STATE; diff --git a/modules/@angular/compiler/src/provider_analyzer.ts b/modules/@angular/compiler/src/provider_analyzer.ts index 52f7c7114226..f1c2901d997a 100644 --- a/modules/@angular/compiler/src/provider_analyzer.ts +++ b/modules/@angular/compiler/src/provider_analyzer.ts @@ -12,7 +12,7 @@ import {ListWrapper, MapWrapper} from './facade/collection'; import {isArray, isBlank, isPresent, normalizeBlank} from './facade/lang'; import {Identifiers, resolveIdentifierToken} from './identifiers'; import {ParseError, ParseSourceSpan} from './parse_util'; -import {AttrAst, DirectiveAst, ProviderAst, ProviderAstType, ReferenceAst, VariableAst} from './template_parser/template_ast'; +import {AttrAst, DirectiveAst, ProviderAst, ProviderAstType, ReferenceAst} from './template_parser/template_ast'; export class ProviderError extends ParseError { constructor(message: string, span: ParseSourceSpan) { super(span, message); } @@ -50,14 +50,14 @@ export class ProviderElementContext { private _hasViewContainer: boolean = false; constructor( - private _viewContext: ProviderViewContext, private _parent: ProviderElementContext, + public viewContext: ProviderViewContext, private _parent: ProviderElementContext, private _isViewRoot: boolean, private _directiveAsts: DirectiveAst[], attrs: AttrAst[], refs: ReferenceAst[], private _sourceSpan: ParseSourceSpan) { this._attrs = {}; attrs.forEach((attrAst) => this._attrs[attrAst.name] = attrAst.value); var directivesMeta = _directiveAsts.map(directiveAst => directiveAst.directive); this._allProviders = - _resolveProvidersFromDirectives(directivesMeta, _sourceSpan, _viewContext.errors); + _resolveProvidersFromDirectives(directivesMeta, _sourceSpan, viewContext.errors); this._contentQueries = _getContentQueries(directivesMeta); var queriedTokens = new Map(); MapWrapper.values(this._allProviders).forEach((provider) => { @@ -124,7 +124,7 @@ export class ProviderElementContext { } currentEl = currentEl._parent; } - queries = this._viewContext.viewQueries.get(token.reference); + queries = this.viewContext.viewQueries.get(token.reference); if (isPresent(queries)) { ListWrapper.addAll(result, queries); } @@ -136,10 +136,9 @@ export class ProviderElementContext { requestingProviderType: ProviderAstType, token: CompileTokenMetadata, eager: boolean): ProviderAst { var resolvedProvider = this._allProviders.get(token.reference); - if (isBlank(resolvedProvider) || - ((requestingProviderType === ProviderAstType.Directive || - requestingProviderType === ProviderAstType.PublicService) && - resolvedProvider.providerType === ProviderAstType.PrivateService) || + if (!resolvedProvider || ((requestingProviderType === ProviderAstType.Directive || + requestingProviderType === ProviderAstType.PublicService) && + resolvedProvider.providerType === ProviderAstType.PrivateService) || ((requestingProviderType === ProviderAstType.PrivateService || requestingProviderType === ProviderAstType.PublicService) && resolvedProvider.providerType === ProviderAstType.Builtin)) { @@ -150,7 +149,7 @@ export class ProviderElementContext { return transformedProviderAst; } if (isPresent(this._seenProviders.get(token.reference))) { - this._viewContext.errors.push(new ProviderError( + this.viewContext.errors.push(new ProviderError( `Cannot instantiate cyclic dependency! ${token.name}`, this._sourceSpan)); return null; } @@ -239,12 +238,12 @@ export class ProviderElementContext { result = this._getLocalDependency(requestingProviderType, dep, eager); } if (dep.isSelf) { - if (isBlank(result) && dep.isOptional) { + if (!result && dep.isOptional) { result = new CompileDiDependencyMetadata({isValue: true, value: null}); } } else { // check parent elements - while (isBlank(result) && isPresent(currElement._parent)) { + while (!result && isPresent(currElement._parent)) { var prevElement = currElement; currElement = currElement._parent; if (prevElement._isViewRoot) { @@ -253,10 +252,10 @@ export class ProviderElementContext { result = currElement._getLocalDependency(ProviderAstType.PublicService, dep, currEager); } // check @Host restriction - if (isBlank(result)) { - if (!dep.isHost || this._viewContext.component.type.isHost || - this._viewContext.component.type.reference === dep.token.reference || - isPresent(this._viewContext.viewProviders.get(dep.token.reference))) { + if (!result) { + if (!dep.isHost || this.viewContext.component.type.isHost || + this.viewContext.component.type.reference === dep.token.reference || + isPresent(this.viewContext.viewProviders.get(dep.token.reference))) { result = dep; } else { result = dep.isOptional ? @@ -265,8 +264,8 @@ export class ProviderElementContext { } } } - if (isBlank(result)) { - this._viewContext.errors.push( + if (!result) { + this.viewContext.errors.push( new ProviderError(`No provider for ${dep.token.name}`, this._sourceSpan)); } return result; @@ -311,7 +310,7 @@ export class NgModuleProviderAnalyzer { private _getOrCreateLocalProvider(token: CompileTokenMetadata, eager: boolean): ProviderAst { var resolvedProvider = this._allProviders.get(token.reference); - if (isBlank(resolvedProvider)) { + if (!resolvedProvider) { return null; } var transformedProviderAst = this._transformedProviders.get(token.reference); @@ -414,7 +413,7 @@ function _normalizeProviders( providers: Array, sourceSpan: ParseSourceSpan, targetErrors: ParseError[], targetProviders: CompileProviderMetadata[] = null): CompileProviderMetadata[] { - if (isBlank(targetProviders)) { + if (!targetProviders) { targetProviders = []; } if (isPresent(providers)) { @@ -479,7 +478,7 @@ function _resolveProviders( `Mixing multi and non multi provider is not possible for token ${resolvedProvider.token.name}`, sourceSpan)); } - if (isBlank(resolvedProvider)) { + if (!resolvedProvider) { const lifecycleHooks = provider.token.identifier && provider.token.identifier instanceof CompileTypeMetadata ? provider.token.identifier.lifecycleHooks : @@ -530,7 +529,7 @@ function _getContentQueries(directives: CompileDirectiveMetadata[]): function _addQueryToTokenMap(map: Map, query: CompileQueryMetadata) { query.selectors.forEach((token: CompileTokenMetadata) => { var entry = map.get(token.reference); - if (isBlank(entry)) { + if (!entry) { entry = []; map.set(token.reference, entry); } diff --git a/modules/@angular/compiler/src/runtime_compiler.ts b/modules/@angular/compiler/src/runtime_compiler.ts index 768380339ecc..f91af0272e00 100644 --- a/modules/@angular/compiler/src/runtime_compiler.ts +++ b/modules/@angular/compiler/src/runtime_compiler.ts @@ -6,12 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ -import {Compiler, ComponentFactory, Injectable, Injector, ModuleWithComponentFactories, NgModuleFactory, Optional, Provider, SchemaMetadata, SkipSelf, Type} from '@angular/core'; - +import {Compiler, ComponentFactory, Injectable, Injector, ModuleWithComponentFactories, NgModuleFactory, SchemaMetadata, Type} from '@angular/core'; +import {AnimationCompiler} from './animation/animation_compiler'; +import {AnimationParser} from './animation/animation_parser'; import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, ProviderMeta, createHostComponentMeta} from './compile_metadata'; import {CompilerConfig} from './config'; import {DirectiveNormalizer} from './directive_normalizer'; -import {isBlank, stringify} from './facade/lang'; +import {stringify} from './facade/lang'; import {CompileMetadataResolver} from './metadata_resolver'; import {NgModuleCompiler} from './ng_module_compiler'; import * as ir from './output/output_ast'; @@ -23,8 +24,6 @@ import {TemplateParser} from './template_parser/template_parser'; import {SyncAsyncResult} from './util'; import {ComponentFactoryDependency, ViewCompiler, ViewFactoryDependency} from './view_compiler/view_compiler'; - - /** * An internal module of the Angular compiler that begins with component types, * extracts templates, and eventually produces a compiled version of the component @@ -39,6 +38,8 @@ export class RuntimeCompiler implements Compiler { private _compiledTemplateCache = new Map, CompiledTemplate>(); private _compiledHostTemplateCache = new Map, CompiledTemplate>(); private _compiledNgModuleCache = new Map, NgModuleFactory>(); + private _animationParser = new AnimationParser(); + private _animationCompiler = new AnimationCompiler(); constructor( private _injector: Injector, private _metadataResolver: CompileMetadataResolver, @@ -190,7 +191,7 @@ export class RuntimeCompiler implements Compiler { private _createCompiledHostTemplate(compType: Type): CompiledTemplate { var compiledTemplate = this._compiledHostTemplateCache.get(compType); - if (isBlank(compiledTemplate)) { + if (!compiledTemplate) { var compMeta = this._metadataResolver.getDirectiveMetadata(compType); assertComponent(compMeta); var hostMeta = createHostComponentMeta(compMeta); @@ -205,7 +206,7 @@ export class RuntimeCompiler implements Compiler { private _createCompiledTemplate( compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata): CompiledTemplate { var compiledTemplate = this._compiledTemplateCache.get(compMeta.type.reference); - if (isBlank(compiledTemplate)) { + if (!compiledTemplate) { assertComponent(compMeta); compiledTemplate = new CompiledTemplate( false, compMeta.selector, compMeta.type, ngModule.transitiveModule.directives, @@ -253,12 +254,15 @@ export class RuntimeCompiler implements Compiler { stylesCompileResult.componentStylesheet, externalStylesheetsByModuleUrl); const viewCompMetas = template.viewComponentTypes.map( (compType) => this._assertComponentLoaded(compType, false).normalizedCompMeta); + const parsedAnimations = this._animationParser.parseComponent(compMeta); const parsedTemplate = this._templateParser.parse( compMeta, compMeta.template.template, template.viewDirectives.concat(viewCompMetas), template.viewPipes, template.schemas, compMeta.type.name); + const compiledAnimations = + this._animationCompiler.compile(compMeta.type.name, parsedAnimations); const compileResult = this._viewCompiler.compileComponent( compMeta, parsedTemplate, ir.variable(stylesCompileResult.componentStylesheet.stylesVar), - template.viewPipes); + template.viewPipes, compiledAnimations); compileResult.dependencies.forEach((dep) => { let depTemplate: CompiledTemplate; if (dep instanceof ViewFactoryDependency) { @@ -275,6 +279,8 @@ export class RuntimeCompiler implements Compiler { }); const statements = stylesCompileResult.componentStylesheet.statements.concat(compileResult.statements); + compiledAnimations.forEach( + entry => { entry.statements.forEach(statement => { statements.push(statement); }); }); let factory: any; if (!this._compilerConfig.useJit) { factory = interpretStatements(statements, compileResult.viewFactoryVar); diff --git a/modules/@angular/compiler/src/schema/dom_element_schema_registry.ts b/modules/@angular/compiler/src/schema/dom_element_schema_registry.ts index 90f83fee1b63..929b1f40aab9 100644 --- a/modules/@angular/compiler/src/schema/dom_element_schema_registry.ts +++ b/modules/@angular/compiler/src/schema/dom_element_schema_registry.ts @@ -344,4 +344,26 @@ export class DomElementSchemaRegistry extends ElementSchemaRegistry { getMappedPropName(propName: string): string { return _ATTR_TO_PROP[propName] || propName; } getDefaultComponentElementName(): string { return 'ng-component'; } + + validateProperty(name: string): {error: boolean, msg?: string} { + if (name.toLowerCase().startsWith('on')) { + const msg = `Binding to event property '${name}' is disallowed for security reasons, ` + + `please use (${name.slice(2)})=...` + + `\nIf '${name}' is a directive input, make sure the directive is imported by the` + + ` current module.`; + return {error: true, msg: msg}; + } else { + return {error: false}; + } + } + + validateAttribute(name: string): {error: boolean, msg?: string} { + if (name.toLowerCase().startsWith('on')) { + const msg = `Binding to event attribute '${name}' is disallowed for security reasons, ` + + `please use (${name.slice(2)})=...`; + return {error: true, msg: msg}; + } else { + return {error: false}; + } + } } diff --git a/modules/@angular/compiler/src/schema/element_schema_registry.ts b/modules/@angular/compiler/src/schema/element_schema_registry.ts index b16428f9e372..52ed080d9680 100644 --- a/modules/@angular/compiler/src/schema/element_schema_registry.ts +++ b/modules/@angular/compiler/src/schema/element_schema_registry.ts @@ -14,4 +14,6 @@ export abstract class ElementSchemaRegistry { abstract securityContext(tagName: string, propName: string): any; abstract getMappedPropName(propName: string): string; abstract getDefaultComponentElementName(): string; + abstract validateProperty(name: string): {error: boolean, msg?: string}; + abstract validateAttribute(name: string): {error: boolean, msg?: string}; } diff --git a/modules/@angular/compiler/src/selector.ts b/modules/@angular/compiler/src/selector.ts index b1408f8e9df0..df3878627db5 100644 --- a/modules/@angular/compiler/src/selector.ts +++ b/modules/@angular/compiler/src/selector.ts @@ -6,18 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ - -import {ListWrapper} from './facade/collection'; -import {StringWrapper, isBlank, isPresent} from './facade/lang'; import {getHtmlTagDefinition} from './ml_parser/html_tags'; -const _EMPTY_ATTR_VALUE = ''; - const _SELECTOR_REGEXP = new RegExp( '(\\:not\\()|' + //":not(" '([-\\w]+)|' + // "tag" '(?:\\.([-\\w]+))|' + // ".class" - '(?:\\[([-\\w*]+)(?:=([^\\]]*))?\\])|' + // "[name]", "[name=value]" or "[name*=value]" + '(?:\\[([-\\w*]+)(?:=([^\\]]*))?\\])|' + // "[name]", "[name=value]" '(\\))|' + // ")" '(\\s*,\\s*)', // "," 'g'); @@ -34,21 +29,21 @@ export class CssSelector { notSelectors: CssSelector[] = []; static parse(selector: string): CssSelector[] { - var results: CssSelector[] = []; - var _addResult = (res: CssSelector[], cssSel: CssSelector) => { - if (cssSel.notSelectors.length > 0 && isBlank(cssSel.element) && - ListWrapper.isEmpty(cssSel.classNames) && ListWrapper.isEmpty(cssSel.attrs)) { + const results: CssSelector[] = []; + const _addResult = (res: CssSelector[], cssSel: CssSelector) => { + if (cssSel.notSelectors.length > 0 && !cssSel.element && cssSel.classNames.length == 0 && + cssSel.attrs.length == 0) { cssSel.element = '*'; } res.push(cssSel); }; - var cssSelector = new CssSelector(); - var match: string[]; - var current = cssSelector; - var inNot = false; + let cssSelector = new CssSelector(); + let match: string[]; + let current = cssSelector; + let inNot = false; _SELECTOR_REGEXP.lastIndex = 0; - while (isPresent(match = _SELECTOR_REGEXP.exec(selector))) { - if (isPresent(match[1])) { + while (match = _SELECTOR_REGEXP.exec(selector)) { + if (match[1]) { if (inNot) { throw new Error('Nesting :not is not allowed in a selector'); } @@ -56,20 +51,20 @@ export class CssSelector { current = new CssSelector(); cssSelector.notSelectors.push(current); } - if (isPresent(match[2])) { + if (match[2]) { current.setElement(match[2]); } - if (isPresent(match[3])) { + if (match[3]) { current.addClassName(match[3]); } - if (isPresent(match[4])) { + if (match[4]) { current.addAttribute(match[4], match[5]); } - if (isPresent(match[6])) { + if (match[6]) { inNot = false; current = cssSelector; } - if (isPresent(match[7])) { + if (match[7]) { if (inNot) { throw new Error('Multiple selectors in :not are not supported'); } @@ -106,37 +101,22 @@ export class CssSelector { `<${tagName}${classAttr}${attrs}>`; } - addAttribute(name: string, value: string = _EMPTY_ATTR_VALUE) { - this.attrs.push(name); - if (isPresent(value)) { - value = value.toLowerCase(); - } else { - value = _EMPTY_ATTR_VALUE; - } - this.attrs.push(value); + addAttribute(name: string, value: string = '') { + this.attrs.push(name, value && value.toLowerCase() || ''); } addClassName(name: string) { this.classNames.push(name.toLowerCase()); } toString(): string { - var res = ''; - if (isPresent(this.element)) { - res += this.element; + let res: string = this.element || ''; + if (this.classNames) { + this.classNames.forEach(klass => res += `.${klass}`); } - if (isPresent(this.classNames)) { - for (var i = 0; i < this.classNames.length; i++) { - res += '.' + this.classNames[i]; - } - } - if (isPresent(this.attrs)) { - for (var i = 0; i < this.attrs.length;) { - var attrName = this.attrs[i++]; - var attrValue = this.attrs[i++]; - res += '[' + attrName; - if (attrValue.length > 0) { - res += '=' + attrValue; - } - res += ']'; + if (this.attrs) { + for (let i = 0; i < this.attrs.length; i += 2) { + const name = this.attrs[i]; + const value = this.attrs[i + 1]; + res += `[${name}${value ? '=' + value : ''}]`; } } this.notSelectors.forEach(notSelector => res += `:not(${notSelector})`); @@ -150,26 +130,26 @@ export class CssSelector { */ export class SelectorMatcher { static createNotMatcher(notSelectors: CssSelector[]): SelectorMatcher { - var notMatcher = new SelectorMatcher(); + const notMatcher = new SelectorMatcher(); notMatcher.addSelectables(notSelectors, null); return notMatcher; } - private _elementMap = new Map(); - private _elementPartialMap = new Map(); - private _classMap = new Map(); - private _classPartialMap = new Map(); - private _attrValueMap = new Map>(); - private _attrValuePartialMap = new Map>(); + private _elementMap: {[k: string]: SelectorContext[]} = {}; + private _elementPartialMap: {[k: string]: SelectorMatcher} = {}; + private _classMap: {[k: string]: SelectorContext[]} = {}; + private _classPartialMap: {[k: string]: SelectorMatcher} = {}; + private _attrValueMap: {[k: string]: {[k: string]: SelectorContext[]}} = {}; + private _attrValuePartialMap: {[k: string]: {[k: string]: SelectorMatcher}} = {}; private _listContexts: SelectorListContext[] = []; addSelectables(cssSelectors: CssSelector[], callbackCtxt?: any) { - var listContext: SelectorListContext = null; + let listContext: SelectorListContext = null; if (cssSelectors.length > 1) { listContext = new SelectorListContext(cssSelectors); this._listContexts.push(listContext); } - for (var i = 0; i < cssSelectors.length; i++) { + for (let i = 0; i < cssSelectors.length; i++) { this._addSelectable(cssSelectors[i], callbackCtxt, listContext); } } @@ -181,14 +161,14 @@ export class SelectorMatcher { */ private _addSelectable( cssSelector: CssSelector, callbackCtxt: any, listContext: SelectorListContext) { - var matcher: SelectorMatcher = this; - var element = cssSelector.element; - var classNames = cssSelector.classNames; - var attrs = cssSelector.attrs; - var selectable = new SelectorContext(cssSelector, callbackCtxt, listContext); - - if (isPresent(element)) { - var isTerminal = attrs.length === 0 && classNames.length === 0; + let matcher: SelectorMatcher = this; + const element = cssSelector.element; + const classNames = cssSelector.classNames; + const attrs = cssSelector.attrs; + const selectable = new SelectorContext(cssSelector, callbackCtxt, listContext); + + if (element) { + const isTerminal = attrs.length === 0 && classNames.length === 0; if (isTerminal) { this._addTerminal(matcher._elementMap, element, selectable); } else { @@ -196,10 +176,10 @@ export class SelectorMatcher { } } - if (isPresent(classNames)) { - for (var index = 0; index < classNames.length; index++) { - var isTerminal = attrs.length === 0 && index === classNames.length - 1; - var className = classNames[index]; + if (classNames) { + for (let i = 0; i < classNames.length; i++) { + const isTerminal = attrs.length === 0 && i === classNames.length - 1; + const className = classNames[i]; if (isTerminal) { this._addTerminal(matcher._classMap, className, selectable); } else { @@ -208,47 +188,47 @@ export class SelectorMatcher { } } - if (isPresent(attrs)) { - for (var index = 0; index < attrs.length;) { - var isTerminal = index === attrs.length - 2; - var attrName = attrs[index++]; - var attrValue = attrs[index++]; + if (attrs) { + for (let i = 0; i < attrs.length; i += 2) { + const isTerminal = i === attrs.length - 2; + const name = attrs[i]; + const value = attrs[i + 1]; if (isTerminal) { - var terminalMap = matcher._attrValueMap; - var terminalValuesMap = terminalMap.get(attrName); - if (isBlank(terminalValuesMap)) { - terminalValuesMap = new Map(); - terminalMap.set(attrName, terminalValuesMap); + const terminalMap = matcher._attrValueMap; + let terminalValuesMap = terminalMap[name]; + if (!terminalValuesMap) { + terminalValuesMap = {}; + terminalMap[name] = terminalValuesMap; } - this._addTerminal(terminalValuesMap, attrValue, selectable); + this._addTerminal(terminalValuesMap, value, selectable); } else { - var parttialMap = matcher._attrValuePartialMap; - var partialValuesMap = parttialMap.get(attrName); - if (isBlank(partialValuesMap)) { - partialValuesMap = new Map(); - parttialMap.set(attrName, partialValuesMap); + let partialMap = matcher._attrValuePartialMap; + let partialValuesMap = partialMap[name]; + if (!partialValuesMap) { + partialValuesMap = {}; + partialMap[name] = partialValuesMap; } - matcher = this._addPartial(partialValuesMap, attrValue); + matcher = this._addPartial(partialValuesMap, value); } } } } private _addTerminal( - map: Map, name: string, selectable: SelectorContext) { - var terminalList = map.get(name); - if (isBlank(terminalList)) { + map: {[k: string]: SelectorContext[]}, name: string, selectable: SelectorContext) { + let terminalList = map[name]; + if (!terminalList) { terminalList = []; - map.set(name, terminalList); + map[name] = terminalList; } terminalList.push(selectable); } - private _addPartial(map: Map, name: string): SelectorMatcher { - var matcher = map.get(name); - if (isBlank(matcher)) { + private _addPartial(map: {[k: string]: SelectorMatcher}, name: string): SelectorMatcher { + let matcher = map[name]; + if (!matcher) { matcher = new SelectorMatcher(); - map.set(name, matcher); + map[name] = matcher; } return matcher; } @@ -261,12 +241,12 @@ export class SelectorMatcher { * @return boolean true if a match was found */ match(cssSelector: CssSelector, matchedCallback: (c: CssSelector, a: any) => void): boolean { - var result = false; - var element = cssSelector.element; - var classNames = cssSelector.classNames; - var attrs = cssSelector.attrs; + let result = false; + const element = cssSelector.element; + const classNames = cssSelector.classNames; + const attrs = cssSelector.attrs; - for (var i = 0; i < this._listContexts.length; i++) { + for (let i = 0; i < this._listContexts.length; i++) { this._listContexts[i].alreadyMatched = false; } @@ -274,9 +254,9 @@ export class SelectorMatcher { result = this._matchPartial(this._elementPartialMap, element, cssSelector, matchedCallback) || result; - if (isPresent(classNames)) { - for (var index = 0; index < classNames.length; index++) { - var className = classNames[index]; + if (classNames) { + for (let i = 0; i < classNames.length; i++) { + const className = classNames[i]; result = this._matchTerminal(this._classMap, className, cssSelector, matchedCallback) || result; result = @@ -285,28 +265,25 @@ export class SelectorMatcher { } } - if (isPresent(attrs)) { - for (var index = 0; index < attrs.length;) { - var attrName = attrs[index++]; - var attrValue = attrs[index++]; + if (attrs) { + for (let i = 0; i < attrs.length; i += 2) { + const name = attrs[i]; + const value = attrs[i + 1]; - var terminalValuesMap = this._attrValueMap.get(attrName); - if (!StringWrapper.equals(attrValue, _EMPTY_ATTR_VALUE)) { - result = this._matchTerminal( - terminalValuesMap, _EMPTY_ATTR_VALUE, cssSelector, matchedCallback) || - result; + const terminalValuesMap = this._attrValueMap[name]; + if (value) { + result = + this._matchTerminal(terminalValuesMap, '', cssSelector, matchedCallback) || result; } - result = this._matchTerminal(terminalValuesMap, attrValue, cssSelector, matchedCallback) || - result; + result = + this._matchTerminal(terminalValuesMap, value, cssSelector, matchedCallback) || result; - var partialValuesMap = this._attrValuePartialMap.get(attrName); - if (!StringWrapper.equals(attrValue, _EMPTY_ATTR_VALUE)) { - result = this._matchPartial( - partialValuesMap, _EMPTY_ATTR_VALUE, cssSelector, matchedCallback) || - result; + const partialValuesMap = this._attrValuePartialMap[name]; + if (value) { + result = this._matchPartial(partialValuesMap, '', cssSelector, matchedCallback) || result; } result = - this._matchPartial(partialValuesMap, attrValue, cssSelector, matchedCallback) || result; + this._matchPartial(partialValuesMap, value, cssSelector, matchedCallback) || result; } } return result; @@ -314,24 +291,24 @@ export class SelectorMatcher { /** @internal */ _matchTerminal( - map: Map, name: string, cssSelector: CssSelector, + map: {[k: string]: SelectorContext[]}, name: string, cssSelector: CssSelector, matchedCallback: (c: CssSelector, a: any) => void): boolean { - if (isBlank(map) || isBlank(name)) { + if (!map || typeof name !== 'string') { return false; } - var selectables = map.get(name); - var starSelectables = map.get('*'); - if (isPresent(starSelectables)) { + let selectables = map[name]; + const starSelectables = map['*']; + if (starSelectables) { selectables = selectables.concat(starSelectables); } - if (isBlank(selectables)) { + if (!selectables) { return false; } - var selectable: SelectorContext; - var result = false; - for (var index = 0; index < selectables.length; index++) { - selectable = selectables[index]; + let selectable: SelectorContext; + let result = false; + for (let i = 0; i < selectables.length; i++) { + selectable = selectables[i]; result = selectable.finalize(cssSelector, matchedCallback) || result; } return result; @@ -339,13 +316,14 @@ export class SelectorMatcher { /** @internal */ _matchPartial( - map: Map, name: string, cssSelector: CssSelector, + map: {[k: string]: SelectorMatcher}, name: string, cssSelector: CssSelector, matchedCallback: (c: CssSelector, a: any) => void): boolean { - if (isBlank(map) || isBlank(name)) { + if (!map || typeof name !== 'string') { return false; } - var nestedSelector = map.get(name); - if (isBlank(nestedSelector)) { + + const nestedSelector = map[name]; + if (!nestedSelector) { return false; } // TODO(perf): get rid of recursion and measure again @@ -373,15 +351,13 @@ export class SelectorContext { } finalize(cssSelector: CssSelector, callback: (c: CssSelector, a: any) => void): boolean { - var result = true; - if (this.notSelectors.length > 0 && - (isBlank(this.listContext) || !this.listContext.alreadyMatched)) { - var notMatcher = SelectorMatcher.createNotMatcher(this.notSelectors); + let result = true; + if (this.notSelectors.length > 0 && (!this.listContext || !this.listContext.alreadyMatched)) { + const notMatcher = SelectorMatcher.createNotMatcher(this.notSelectors); result = !notMatcher.match(cssSelector, null); } - if (result && isPresent(callback) && - (isBlank(this.listContext) || !this.listContext.alreadyMatched)) { - if (isPresent(this.listContext)) { + if (result && callback && (!this.listContext || !this.listContext.alreadyMatched)) { + if (this.listContext) { this.listContext.alreadyMatched = true; } callback(this.selector, this.cbContext); diff --git a/modules/@angular/compiler/src/shadow_css.ts b/modules/@angular/compiler/src/shadow_css.ts index 03ab00b165a7..ddc30555e30d 100644 --- a/modules/@angular/compiler/src/shadow_css.ts +++ b/modules/@angular/compiler/src/shadow_css.ts @@ -6,8 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {StringWrapper, isBlank, isPresent} from './facade/lang'; - /** * This file is a port of shadowCSS from webcomponents.js to TypeScript. * @@ -175,9 +173,8 @@ export class ShadowCss { **/ private _insertPolyfillDirectivesInCssText(cssText: string): string { // Difference with webcomponents.js: does not handle comments - return StringWrapper.replaceAllMapped( - cssText, _cssContentNextSelectorRe, - function(m: any /** TODO #9100 */) { return m[1] + '{'; }); + return cssText.replace( + _cssContentNextSelectorRe, function(...m: string[]) { return m[2] + '{'; }); } /* @@ -197,13 +194,10 @@ export class ShadowCss { **/ private _insertPolyfillRulesInCssText(cssText: string): string { // Difference with webcomponents.js: does not handle comments - return StringWrapper.replaceAllMapped( - cssText, _cssContentRuleRe, function(m: any /** TODO #9100 */) { - let rule = m[0]; - rule = StringWrapper.replace(rule, m[1], ''); - rule = StringWrapper.replace(rule, m[2], ''); - return m[3] + rule; - }); + return cssText.replace(_cssContentRuleRe, (...m: string[]) => { + const rule = m[0].replace(m[1], '').replace(m[2], ''); + return m[4] + rule; + }); } /* Ensure styles are scoped. Pseudo-scoping takes a rule like: @@ -215,15 +209,16 @@ export class ShadowCss { * scopeName .foo { ... } */ private _scopeCssText(cssText: string, scopeSelector: string, hostSelector: string): string { - const unscoped = this._extractUnscopedRulesFromCssText(cssText); + const unscopedRules = this._extractUnscopedRulesFromCssText(cssText); + // replace :host and :host-context -shadowcsshost and -shadowcsshost respectively cssText = this._insertPolyfillHostInCssText(cssText); cssText = this._convertColonHost(cssText); cssText = this._convertColonHostContext(cssText); cssText = this._convertShadowDOMSelectors(cssText); - if (isPresent(scopeSelector)) { + if (scopeSelector) { cssText = this._scopeSelectors(cssText, scopeSelector, hostSelector); } - cssText = cssText + '\n' + unscoped; + cssText = cssText + '\n' + unscopedRules; return cssText.trim(); } @@ -248,9 +243,7 @@ export class ShadowCss { let m: RegExpExecArray; _cssContentUnscopedRuleRe.lastIndex = 0; while ((m = _cssContentUnscopedRuleRe.exec(cssText)) !== null) { - let rule = m[0]; - rule = StringWrapper.replace(rule, m[2], ''); - rule = StringWrapper.replace(rule, m[1], m[3]); + const rule = m[0].replace(m[2], '').replace(m[1], m[4]); r += rule + '\n\n'; } return r; @@ -261,7 +254,7 @@ export class ShadowCss { * * to * - * scopeName.foo > .bar + * .foo > .bar */ private _convertColonHost(cssText: string): string { return this._convertColonRule(cssText, _cssColonHostRe, this._colonHostPartReplacer); @@ -272,7 +265,7 @@ export class ShadowCss { * * to * - * scopeName.foo > .bar, .foo scopeName > .bar { } + * .foo > .bar, .foo scopeName > .bar { } * * and * @@ -280,7 +273,7 @@ export class ShadowCss { * * to * - * scopeName.foo .bar { ... } + * .foo .bar { ... } */ private _convertColonHostContext(cssText: string): string { return this._convertColonRule( @@ -288,14 +281,14 @@ export class ShadowCss { } private _convertColonRule(cssText: string, regExp: RegExp, partReplacer: Function): string { - // p1 = :host, p2 = contents of (), p3 rest of rule - return StringWrapper.replaceAllMapped(cssText, regExp, function(m: any /** TODO #9100 */) { - if (isPresent(m[2])) { - const parts = m[2].split(','), r: any[] /** TODO #9100 */ = []; + // m[1] = :host(-context), m[2] = contents of (), m[3] rest of rule + return cssText.replace(regExp, function(...m: string[]) { + if (m[2]) { + const parts = m[2].split(','); + const r: string[] = []; for (let i = 0; i < parts.length; i++) { - let p = parts[i]; - if (isBlank(p)) break; - p = p.trim(); + let p = parts[i].trim(); + if (!p) break; r.push(partReplacer(_polyfillHostNoCombinator, p, m[3])); } return r.join(','); @@ -306,7 +299,7 @@ export class ShadowCss { } private _colonHostContextPartReplacer(host: string, part: string, suffix: string): string { - if (StringWrapper.contains(part, _polyfillHost)) { + if (part.indexOf(_polyfillHost) > -1) { return this._colonHostPartReplacer(host, part, suffix); } else { return host + part + suffix + ', ' + part + ' ' + host + suffix; @@ -314,7 +307,7 @@ export class ShadowCss { } private _colonHostPartReplacer(host: string, part: string, suffix: string): string { - return host + StringWrapper.replace(part, _polyfillHost, '') + suffix; + return host + part.replace(_polyfillHost, '') + suffix; } /* @@ -322,8 +315,7 @@ export class ShadowCss { * by replacing with space. */ private _convertShadowDOMSelectors(cssText: string): string { - return _shadowDOMSelectorsRe.reduce( - (result, pattern) => { return StringWrapper.replaceAll(result, pattern, ' '); }, cssText); + return _shadowDOMSelectorsRe.reduce((result, pattern) => result.replace(pattern, ' '), cssText); } // change a selector like 'div' to 'name div' @@ -331,10 +323,12 @@ export class ShadowCss { return processRules(cssText, (rule: CssRule) => { let selector = rule.selector; let content = rule.content; - if (rule.selector[0] != '@' || rule.selector.startsWith('@page')) { + if (rule.selector[0] != '@') { selector = this._scopeSelector(rule.selector, scopeSelector, hostSelector, this.strictStyling); - } else if (rule.selector.startsWith('@media') || rule.selector.startsWith('@supports')) { + } else if ( + rule.selector.startsWith('@media') || rule.selector.startsWith('@supports') || + rule.selector.startsWith('@page') || rule.selector.startsWith('@document')) { content = this._scopeSelectors(rule.content, scopeSelector, hostSelector); } return new CssRule(selector, content); @@ -369,8 +363,7 @@ export class ShadowCss { private _makeScopeMatcher(scopeSelector: string): RegExp { const lre = /\[/g; const rre = /\]/g; - scopeSelector = StringWrapper.replaceAll(scopeSelector, lre, '\\['); - scopeSelector = StringWrapper.replaceAll(scopeSelector, rre, '\\]'); + scopeSelector = scopeSelector.replace(lre, '\\[').replace(rre, '\\]'); return new RegExp('^(' + scopeSelector + ')' + _selectorReSuffix, 'm'); } @@ -385,13 +378,14 @@ export class ShadowCss { string { // In Android browser, the lastIndex is not reset when the regex is used in String.replace() _polyfillHostRe.lastIndex = 0; + if (_polyfillHostRe.test(selector)) { const replaceBy = this.strictStyling ? `[${hostSelector}]` : scopeSelector; - selector = StringWrapper.replace(selector, _polyfillHostNoCombinator, replaceBy); - return StringWrapper.replaceAll(selector, _polyfillHostRe, replaceBy + ' '); - } else { - return scopeSelector + ' ' + selector; + return selector.replace(_polyfillHostNoCombinatorRe, (hnc, selector) => selector + replaceBy) + .replace(_polyfillHostRe, replaceBy + ' '); } + + return scopeSelector + ' ' + selector; } // return a selector with [name] suffix on each simple selector @@ -404,9 +398,9 @@ export class ShadowCss { const attrName = '[' + scopeSelector + ']'; const _scopeSelectorPart = (p: string) => { - var scopedP = p.trim(); + let scopedP = p.trim(); - if (scopedP.length == 0) { + if (!scopedP) { return ''; } @@ -426,12 +420,23 @@ export class ShadowCss { return scopedP; }; - const sep = /( |>|\+|~(?!=))\s*/g; - const scopeAfter = selector.indexOf(_polyfillHostNoCombinator); + let attrSelectorIndex = 0; + const attrSelectors: string[] = []; - let scoped = ''; + // replace attribute selectors with placeholders to avoid issue with white space being treated + // as separator + selector = selector.replace(/\[[^\]]*\]/g, (attrSelector) => { + const replaceBy = `__attr_sel_${attrSelectorIndex}__`; + attrSelectors.push(attrSelector); + attrSelectorIndex++; + return replaceBy; + }); + + let scopedSelector = ''; let startIndex = 0; let res: RegExpExecArray; + const sep = /( |>|\+|~(?!=))\s*/g; + const scopeAfter = selector.indexOf(_polyfillHostNoCombinator); while ((res = sep.exec(selector)) !== null) { const separator = res[1]; @@ -439,10 +444,14 @@ export class ShadowCss { // if a selector appears before :host-context it should not be shimmed as it // matches on ancestor elements and not on elements in the host's shadow const scopedPart = startIndex >= scopeAfter ? _scopeSelectorPart(part) : part; - scoped += `${scopedPart} ${separator} `; + scopedSelector += `${scopedPart} ${separator} `; startIndex = sep.lastIndex; } - return scoped + _scopeSelectorPart(selector.substring(startIndex)); + + scopedSelector += _scopeSelectorPart(selector.substring(startIndex)); + + // replace the placeholders with their original values + return scopedSelector.replace(/__attr_sel_(\d+)__/g, (ph, index) => attrSelectors[+index]); } private _insertPolyfillHostInCssText(selector: string): string { @@ -451,10 +460,10 @@ export class ShadowCss { } } const _cssContentNextSelectorRe = - /polyfill-next-selector[^}]*content:[\s]*?['"](.*?)['"][;\s]*}([^{]*?){/gim; -const _cssContentRuleRe = /(polyfill-rule)[^}]*(content:[\s]*['"](.*?)['"])[;\s]*[^}]*}/gim; + /polyfill-next-selector[^}]*content:[\s]*?(['"])(.*?)\1[;\s]*}([^{]*?){/gim; +const _cssContentRuleRe = /(polyfill-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim; const _cssContentUnscopedRuleRe = - /(polyfill-unscoped-rule)[^}]*(content:[\s]*['"](.*?)['"])[;\s]*[^}]*}/gim; + /(polyfill-unscoped-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim; const _polyfillHost = '-shadowcsshost'; // note: :host-context pre-processed to -shadowcsshostcontext. const _polyfillHostContext = '-shadowcsscontext'; @@ -464,6 +473,7 @@ const _parenSuffix = ')(?:\\((' + const _cssColonHostRe = new RegExp('(' + _polyfillHost + _parenSuffix, 'gim'); const _cssColonHostContextRe = new RegExp('(' + _polyfillHostContext + _parenSuffix, 'gim'); const _polyfillHostNoCombinator = _polyfillHost + '-no-combinator'; +const _polyfillHostNoCombinatorRe = /-shadowcsshost-no-combinator([^\s]*)/; const _shadowDOMSelectorsRe = [ /::shadow/g, /::content/g, @@ -480,7 +490,7 @@ const _colonHostContextRe = /:host-context/gim; const _commentRe = /\/\*\s*[\s\S]*?\*\//g; function stripComments(input: string): string { - return StringWrapper.replaceAllMapped(input, _commentRe, (_: any /** TODO #9100 */) => ''); + return input.replace(_commentRe, ''); } // all comments except inline source mapping @@ -501,23 +511,22 @@ export class CssRule { constructor(public selector: string, public content: string) {} } -export function processRules(input: string, ruleCallback: Function): string { +export function processRules(input: string, ruleCallback: (rule: CssRule) => CssRule): string { const inputWithEscapedBlocks = escapeBlocks(input); let nextBlockIndex = 0; - return StringWrapper.replaceAllMapped( - inputWithEscapedBlocks.escapedString, _ruleRe, function(m: any /** TODO #9100 */) { - const selector = m[2]; - let content = ''; - let suffix = m[4]; - let contentPrefix = ''; - if (isPresent(m[4]) && m[4].startsWith('{' + BLOCK_PLACEHOLDER)) { - content = inputWithEscapedBlocks.blocks[nextBlockIndex++]; - suffix = m[4].substring(BLOCK_PLACEHOLDER.length + 1); - contentPrefix = '{'; - } - const rule = ruleCallback(new CssRule(selector, content)); - return `${m[1]}${rule.selector}${m[3]}${contentPrefix}${rule.content}${suffix}`; - }); + return inputWithEscapedBlocks.escapedString.replace(_ruleRe, function(...m: string[]) { + const selector = m[2]; + let content = ''; + let suffix = m[4]; + let contentPrefix = ''; + if (suffix && suffix.startsWith('{' + BLOCK_PLACEHOLDER)) { + content = inputWithEscapedBlocks.blocks[nextBlockIndex++]; + suffix = suffix.substring(BLOCK_PLACEHOLDER.length + 1); + contentPrefix = '{'; + } + const rule = ruleCallback(new CssRule(selector, content)); + return `${m[1]}${rule.selector}${m[3]}${contentPrefix}${rule.content}${suffix}`; + }); } class StringWithEscapedBlocks { @@ -525,11 +534,11 @@ class StringWithEscapedBlocks { } function escapeBlocks(input: string): StringWithEscapedBlocks { - const inputParts = StringWrapper.split(input, _curlyRe); - const resultParts: any[] /** TODO #9100 */ = []; - const escapedBlocks: any[] /** TODO #9100 */ = []; + const inputParts = input.split(_curlyRe); + const resultParts: string[] = []; + const escapedBlocks: string[] = []; let bracketCount = 0; - let currentBlockParts: any[] /** TODO #9100 */ = []; + let currentBlockParts: string[] = []; for (let partIndex = 0; partIndex < inputParts.length; partIndex++) { const part = inputParts[partIndex]; if (part == CLOSE_CURLY) { diff --git a/modules/@angular/compiler/src/template_parser/template_ast.ts b/modules/@angular/compiler/src/template_parser/template_ast.ts index 11f57a0e43e8..d8d916e1739a 100644 --- a/modules/@angular/compiler/src/template_parser/template_ast.ts +++ b/modules/@angular/compiler/src/template_parser/template_ast.ts @@ -12,11 +12,8 @@ import {CompileDirectiveMetadata, CompileProviderMetadata, CompileTokenMetadata} import {AST} from '../expression_parser/ast'; import {isPresent} from '../facade/lang'; import {ParseSourceSpan} from '../parse_util'; - import {LifecycleHooks} from '../private_import_core'; - - /** * An Abstract Syntax Tree node representing part of a parsed Angular template. */ @@ -61,7 +58,8 @@ export class AttrAst implements TemplateAst { } /** - * A binding for an element property (e.g. `[property]="expression"`). + * A binding for an element property (e.g. `[property]="expression"`) or an animation trigger (e.g. + * `[@trigger]="stateExp"`) */ export class BoundElementPropertyAst implements TemplateAst { constructor( @@ -71,14 +69,16 @@ export class BoundElementPropertyAst implements TemplateAst { visit(visitor: TemplateAstVisitor, context: any): any { return visitor.visitElementProperty(this, context); } + get isAnimation(): boolean { return this.type === PropertyBindingType.Animation; } } /** - * A binding for an element event (e.g. `(event)="handler()"`). + * A binding for an element event (e.g. `(event)="handler()"`) or an animation trigger event (e.g. + * `(@trigger.phase)="callback($event)"`). */ export class BoundEventAst implements TemplateAst { constructor( - public name: string, public target: string, public handler: AST, + public name: string, public target: string, public phase: string, public handler: AST, public sourceSpan: ParseSourceSpan) {} visit(visitor: TemplateAstVisitor, context: any): any { return visitor.visitEvent(this, context); @@ -90,6 +90,7 @@ export class BoundEventAst implements TemplateAst { return this.name; } } + get isAnimation(): boolean { return !!this.phase; } } /** diff --git a/modules/@angular/compiler/src/template_parser/template_parser.ts b/modules/@angular/compiler/src/template_parser/template_parser.ts index 01fb272b1041..0f884f25e53c 100644 --- a/modules/@angular/compiler/src/template_parser/template_parser.ts +++ b/modules/@angular/compiler/src/template_parser/template_parser.ts @@ -8,11 +8,10 @@ import {Inject, Injectable, OpaqueToken, Optional, SchemaMetadata, SecurityContext} from '@angular/core'; -import {CompileDirectiveMetadata, CompilePipeMetadata, CompileTokenMetadata, removeIdentifierDuplicates} from '../compile_metadata'; +import {CompileDirectiveMetadata, CompilePipeMetadata, CompileTemplateMetadata, CompileTokenMetadata, removeIdentifierDuplicates} from '../compile_metadata'; import {AST, ASTWithSource, BindingPipe, EmptyExpr, Interpolation, ParserError, RecursiveAstVisitor, TemplateBinding} from '../expression_parser/ast'; import {Parser} from '../expression_parser/parser'; -import {StringMapWrapper} from '../facade/collection'; -import {isBlank, isPresent, isString} from '../facade/lang'; +import {isPresent, isString} from '../facade/lang'; import {I18NHtmlParser} from '../i18n/i18n_html_parser'; import {Identifiers, identifierToken, resolveIdentifierToken} from '../identifiers'; import * as html from '../ml_parser/ast'; @@ -26,12 +25,13 @@ import {ProviderElementContext, ProviderViewContext} from '../provider_analyzer' import {ElementSchemaRegistry} from '../schema/element_schema_registry'; import {CssSelector, SelectorMatcher} from '../selector'; import {isStyleUrlResolvable} from '../style_url_resolver'; -import {splitAtColon} from '../util'; +import {splitAtColon, splitAtPeriod} from '../util'; import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from './template_ast'; import {PreparsedElementType, preparseElement} from './template_preparser'; + // Group 1 = "bind-" // Group 2 = "let-" // Group 3 = "ref-/#" @@ -142,7 +142,6 @@ export class TemplateParser { const parseVisitor = new TemplateParseVisitor( providerViewContext, uniqDirectives, uniqPipes, schemas, this._exprParser, this._schemaRegistry); - result = html.visitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_ELEMENT_CONTEXT); errors.push(...parseVisitor.errors, ...providerViewContext.errors); } else { @@ -444,6 +443,15 @@ class TemplateParseVisitor implements html.Visitor { providerContext.transformedDirectiveAsts, providerContext.transformProviders, providerContext.transformedHasViewContainer, children, hasInlineTemplates ? null : ngContentIndex, element.sourceSpan); + + this._findComponentDirectives(directiveAsts) + .forEach( + componentDirectiveAst => this._validateElementAnimationInputOutputs( + componentDirectiveAst.hostProperties, componentDirectiveAst.hostEvents, + componentDirectiveAst.directive.template)); + + const componentTemplate = providerContext.viewContext.component.template; + this._validateElementAnimationInputOutputs(elementProps, events, componentTemplate); } if (hasInlineTemplates) { @@ -469,9 +477,36 @@ class TemplateParseVisitor implements html.Visitor { templateProviderContext.transformedHasViewContainer, [parsedElement], ngContentIndex, element.sourceSpan); } + return parsedElement; } + private _validateElementAnimationInputOutputs( + inputs: BoundElementPropertyAst[], outputs: BoundEventAst[], + template: CompileTemplateMetadata) { + const triggerLookup = new Set(); + template.animations.forEach(entry => { triggerLookup.add(entry.name); }); + + const animationInputs = inputs.filter(input => input.isAnimation); + animationInputs.forEach(input => { + const name = input.name; + if (!triggerLookup.has(name)) { + this._reportError(`Couldn't find an animation entry for "${name}"`, input.sourceSpan); + } + }); + + outputs.forEach(output => { + if (output.isAnimation) { + const found = animationInputs.find(input => input.name == output.name); + if (!found) { + this._reportError( + `Unable to listen on (@${output.name}.${output.phase}) because the animation trigger [@${output.name}] isn't being used on the same element`, + output.sourceSpan); + } + } + }); + } + private _parseInlineTemplateBinding( attr: html.Attribute, targetMatchableAttrs: string[][], targetProps: BoundElementOrDirectiveProperty[], targetVars: VariableAst[]): boolean { @@ -533,7 +568,7 @@ class TemplateParseVisitor implements html.Visitor { this._parseReference(identifier, value, srcSpan, targetRefs); } else if (bindParts[KW_ON_IDX]) { - this._parseEvent( + this._parseEventOrAnimationEvent( bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents); } else if (bindParts[KW_BINDON_IDX]) { @@ -544,7 +579,7 @@ class TemplateParseVisitor implements html.Visitor { bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents); } else if (bindParts[KW_AT_IDX]) { - if (name[0] == '@' && isPresent(value) && value.length > 0) { + if (_isAnimationLabel(name) && isPresent(value) && value.length > 0) { this._reportError( `Assigning animation triggers via @prop="exp" attributes with an expression is invalid.` + ` Use property bindings (e.g. [@prop]="exp") or use an attribute without a value (e.g. @prop) instead.`, @@ -565,7 +600,7 @@ class TemplateParseVisitor implements html.Visitor { targetAnimationProps); } else if (bindParts[IDENT_EVENT_IDX]) { - this._parseEvent( + this._parseEventOrAnimationEvent( bindParts[IDENT_EVENT_IDX], value, srcSpan, targetMatchableAttrs, targetEvents); } } else { @@ -608,7 +643,7 @@ class TemplateParseVisitor implements html.Visitor { targetMatchableAttrs: string[][], targetProps: BoundElementOrDirectiveProperty[], targetAnimationProps: BoundElementPropertyAst[]) { const animatePropLength = ANIMATE_PROP_PREFIX.length; - var isAnimationProp = name[0] == '@'; + var isAnimationProp = _isAnimationLabel(name); var animationPrefixLength = 1; if (name.substring(0, animatePropLength) == ANIMATE_PROP_PREFIX) { isAnimationProp = true; @@ -635,6 +670,7 @@ class TemplateParseVisitor implements html.Visitor { if (!isPresent(expression) || expression.length == 0) { expression = 'null'; } + const ast = this._parseBinding(expression, sourceSpan); targetMatchableAttrs.push([name, ast.source]); targetAnimationProps.push(new BoundElementPropertyAst( @@ -662,20 +698,56 @@ class TemplateParseVisitor implements html.Visitor { private _parseAssignmentEvent( name: string, expression: string, sourceSpan: ParseSourceSpan, targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) { - this._parseEvent( + this._parseEventOrAnimationEvent( `${name}Change`, `${expression}=$event`, sourceSpan, targetMatchableAttrs, targetEvents); } + private _parseEventOrAnimationEvent( + name: string, expression: string, sourceSpan: ParseSourceSpan, + targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) { + if (_isAnimationLabel(name)) { + name = name.substr(1); + this._parseAnimationEvent(name, expression, sourceSpan, targetEvents); + } else { + this._parseEvent(name, expression, sourceSpan, targetMatchableAttrs, targetEvents); + } + } + + private _parseAnimationEvent( + name: string, expression: string, sourceSpan: ParseSourceSpan, + targetEvents: BoundEventAst[]) { + const matches = splitAtPeriod(name, [name, '']); + const eventName = matches[0]; + const phase = matches[1].toLowerCase(); + if (phase) { + switch (phase) { + case 'start': + case 'done': + const ast = this._parseAction(expression, sourceSpan); + targetEvents.push(new BoundEventAst(eventName, null, phase, ast, sourceSpan)); + break; + + default: + this._reportError( + `The provided animation output phase value "${phase}" for "@${eventName}" is not supported (use start or done)`, + sourceSpan); + break; + } + } else { + this._reportError( + `The animation trigger output event (@${eventName}) is missing its phase value name (start or done are currently supported)`, + sourceSpan); + } + } + private _parseEvent( name: string, expression: string, sourceSpan: ParseSourceSpan, targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) { // long format: 'target: eventName' - const parts = splitAtColon(name, [null, name]); - const target = parts[0]; - const eventName = parts[1]; + const [target, eventName] = splitAtColon(name, [null, name]); const ast = this._parseAction(expression, sourceSpan); targetMatchableAttrs.push([name, ast.source]); - targetEvents.push(new BoundEventAst(eventName, target, ast, sourceSpan)); + targetEvents.push(new BoundEventAst(eventName, target, null, ast, sourceSpan)); // Don't detect directives for event names for now, // so don't add the event name to the matchableAttrs } @@ -759,7 +831,8 @@ class TemplateParseVisitor implements html.Visitor { elementName: string, hostProps: {[key: string]: string}, sourceSpan: ParseSourceSpan, targetPropertyAsts: BoundElementPropertyAst[]) { if (hostProps) { - StringMapWrapper.forEach(hostProps, (expression: string, propName: string) => { + Object.keys(hostProps).forEach(propName => { + const expression = hostProps[propName]; if (isString(expression)) { const exprAst = this._parseBinding(expression, sourceSpan); targetPropertyAsts.push( @@ -777,9 +850,10 @@ class TemplateParseVisitor implements html.Visitor { hostListeners: {[key: string]: string}, sourceSpan: ParseSourceSpan, targetEventAsts: BoundEventAst[]) { if (hostListeners) { - StringMapWrapper.forEach(hostListeners, (expression: string, propName: string) => { + Object.keys(hostListeners).forEach(propName => { + const expression = hostListeners[propName]; if (isString(expression)) { - this._parseEvent(propName, expression, sourceSpan, [], targetEventAsts); + this._parseEventOrAnimationEvent(propName, expression, sourceSpan, [], targetEventAsts); } else { this._reportError( `Value of the host listener "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`, @@ -796,13 +870,14 @@ class TemplateParseVisitor implements html.Visitor { const boundPropsByName = new Map(); boundProps.forEach(boundProp => { const prevValue = boundPropsByName.get(boundProp.name); - if (isBlank(prevValue) || prevValue.isLiteral) { + if (!prevValue || prevValue.isLiteral) { // give [a]="b" a higher precedence than a="b" on the same element boundPropsByName.set(boundProp.name, boundProp); } }); - StringMapWrapper.forEach(directiveProperties, (elProp: string, dirProp: string) => { + Object.keys(directiveProperties).forEach(dirProp => { + const elProp = directiveProperties[dirProp]; const boundProp = boundPropsByName.get(elProp); // Bindings are optional, so this binding only needs to be set up if an expression is given. @@ -827,7 +902,7 @@ class TemplateParseVisitor implements html.Visitor { }); props.forEach((prop: BoundElementOrDirectiveProperty) => { - if (!prop.isLiteral && isBlank(boundDirectivePropsIndex.get(prop.name))) { + if (!prop.isLiteral && !boundDirectivePropsIndex.get(prop.name)) { boundElementProps.push(this._createElementPropertyAst( elementName, prop.name, prop.expression, prop.sourceSpan)); } @@ -846,7 +921,7 @@ class TemplateParseVisitor implements html.Visitor { if (parts.length === 1) { var partValue = parts[0]; - if (partValue[0] == '@') { + if (_isAnimationLabel(partValue)) { boundPropertyName = partValue.substr(1); bindingType = PropertyBindingType.Animation; securityContext = SecurityContext.NONE; @@ -854,7 +929,7 @@ class TemplateParseVisitor implements html.Visitor { boundPropertyName = this._schemaRegistry.getMappedPropName(partValue); securityContext = this._schemaRegistry.securityContext(elementName, boundPropertyName); bindingType = PropertyBindingType.Property; - this._assertNoEventBinding(boundPropertyName, sourceSpan); + this._validatePropertyOrAttributeName(boundPropertyName, sourceSpan, false); if (!this._schemaRegistry.hasProperty(elementName, boundPropertyName, this._schemas)) { let errorMsg = `Can't bind to '${boundPropertyName}' since it isn't a known property of '${elementName}'.`; @@ -869,7 +944,7 @@ class TemplateParseVisitor implements html.Visitor { } else { if (parts[0] == ATTRIBUTE_PREFIX) { boundPropertyName = parts[1]; - this._assertNoEventBinding(boundPropertyName, sourceSpan); + this._validatePropertyOrAttributeName(boundPropertyName, sourceSpan, true); // NB: For security purposes, use the mapped property name, not the attribute name. const mapPropName = this._schemaRegistry.getMappedPropName(boundPropertyName); securityContext = this._schemaRegistry.securityContext(elementName, mapPropName); @@ -902,24 +977,29 @@ class TemplateParseVisitor implements html.Visitor { boundPropertyName, bindingType, securityContext, ast, unit, sourceSpan); } - private _assertNoEventBinding(propName: string, sourceSpan: ParseSourceSpan): void { - if (propName.toLowerCase().startsWith('on')) { - this._reportError( - `Binding to event attribute '${propName}' is disallowed ` + - `for security reasons, please use (${propName.slice(2)})=...`, - sourceSpan, ParseErrorLevel.FATAL); + + /** + * @param propName the name of the property / attribute + * @param sourceSpan + * @param isAttr true when binding to an attribute + * @private + */ + private _validatePropertyOrAttributeName( + propName: string, sourceSpan: ParseSourceSpan, isAttr: boolean): void { + const report = isAttr ? this._schemaRegistry.validateAttribute(propName) : + this._schemaRegistry.validateProperty(propName); + if (report.error) { + this._reportError(report.msg, sourceSpan, ParseErrorLevel.FATAL); } } + private _findComponentDirectives(directives: DirectiveAst[]): DirectiveAst[] { + return directives.filter(directive => directive.directive.isComponent); + } + private _findComponentDirectiveNames(directives: DirectiveAst[]): string[] { - const componentTypeNames: string[] = []; - directives.forEach(directive => { - const typeName = directive.directive.type.name; - if (directive.directive.isComponent) { - componentTypeNames.push(typeName); - } - }); - return componentTypeNames; + return this._findComponentDirectives(directives) + .map(directive => directive.directive.type.name); } private _assertOnlyOneComponent(directives: DirectiveAst[], sourceSpan: ParseSourceSpan) { @@ -969,7 +1049,8 @@ class TemplateParseVisitor implements html.Visitor { const allDirectiveEvents = new Set(); directives.forEach(directive => { - StringMapWrapper.forEach(directive.directive.outputs, (eventName: string) => { + Object.keys(directive.directive.outputs).forEach(k => { + const eventName = directive.directive.outputs[k]; allDirectiveEvents.add(eventName); }); }); @@ -1103,3 +1184,7 @@ export class PipeCollector extends RecursiveAstVisitor { return null; } } + +function _isAnimationLabel(name: string): boolean { + return name[0] == '@'; +} diff --git a/modules/@angular/compiler/src/util.ts b/modules/@angular/compiler/src/util.ts index fd8cf945f3d1..0cc67890327e 100644 --- a/modules/@angular/compiler/src/util.ts +++ b/modules/@angular/compiler/src/util.ts @@ -7,7 +7,6 @@ */ import {CompileTokenMetadata} from './compile_metadata'; -import {StringMapWrapper} from './facade/collection'; import {StringWrapper, isArray, isBlank, isPresent, isPrimitive, isStrictStringMap} from './facade/lang'; import * as o from './output/output_ast'; @@ -17,13 +16,21 @@ var CAMEL_CASE_REGEXP = /([A-Z])/g; export function camelCaseToDashCase(input: string): string { return StringWrapper.replaceAllMapped( - input, CAMEL_CASE_REGEXP, (m: string[]) => { return '-' + m[1].toLowerCase(); }); + input, CAMEL_CASE_REGEXP, (m: string[]) => '-' + m[1].toLowerCase()); } export function splitAtColon(input: string, defaultValues: string[]): string[] { - const colonIndex = input.indexOf(':'); - if (colonIndex == -1) return defaultValues; - return [input.slice(0, colonIndex).trim(), input.slice(colonIndex + 1).trim()]; + return _splitAt(input, ':', defaultValues); +} + +export function splitAtPeriod(input: string, defaultValues: string[]): string[] { + return _splitAt(input, '.', defaultValues); +} + +function _splitAt(input: string, character: string, defaultValues: string[]): string[] { + const characterIndex = input.indexOf(character); + if (characterIndex == -1) return defaultValues; + return [input.slice(0, characterIndex).trim(), input.slice(characterIndex + 1).trim()]; } export function sanitizeIdentifier(name: string): string { @@ -55,9 +62,8 @@ export class ValueTransformer implements ValueVisitor { } visitStringMap(map: {[key: string]: any}, context: any): any { var result = {}; - StringMapWrapper.forEach(map, (value: any /** TODO #9100 */, key: any /** TODO #9100 */) => { - (result as any /** TODO #9100 */)[key] = visitValue(value, this, context); - }); + Object.keys(map).forEach( + key => { (result as any /** TODO #9100 */)[key] = visitValue(map[key], this, context); }); return result; } visitPrimitive(value: any, context: any): any { return value; } diff --git a/modules/@angular/compiler/src/view_compiler/compile_element.ts b/modules/@angular/compiler/src/view_compiler/compile_element.ts index f394d68dd9ad..8986330e89f3 100644 --- a/modules/@angular/compiler/src/view_compiler/compile_element.ts +++ b/modules/@angular/compiler/src/view_compiler/compile_element.ts @@ -8,8 +8,8 @@ import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileIdentifierMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata} from '../compile_metadata'; -import {ListWrapper, MapWrapper, StringMapWrapper} from '../facade/collection'; -import {isBlank, isPresent} from '../facade/lang'; +import {ListWrapper, MapWrapper} from '../facade/collection'; +import {isPresent} from '../facade/lang'; import {Identifiers, identifierToken, resolveIdentifier, resolveIdentifierToken} from '../identifiers'; import * as o from '../output/output_ast'; import {convertValueToOutputAst} from '../output/value_util'; @@ -27,7 +27,7 @@ export class CompileNode { public parent: CompileElement, public view: CompileView, public nodeIndex: number, public renderNode: o.Expression, public sourceAst: TemplateAst) {} - isNull(): boolean { return isBlank(this.renderNode); } + isNull(): boolean { return !this.renderNode; } isRootElement(): boolean { return this.view != this.parent.view; } } @@ -117,7 +117,7 @@ export class CompileElement extends CompileNode { setComponentView(compViewExpr: o.Expression) { this._compViewExpr = compViewExpr; this.contentNodesByNgContentIndex = - ListWrapper.createFixedSize(this.component.template.ngContentSelectors.length); + new Array(this.component.template.ngContentSelectors.length); for (var i = 0; i < this.contentNodesByNgContentIndex.length; i++) { this.contentNodesByNgContentIndex[i] = []; } @@ -192,7 +192,7 @@ export class CompileElement extends CompileNode { queriesWithReads, queriesForProvider.map(query => new _QueryWithRead(query, resolvedProvider.token))); }); - StringMapWrapper.forEach(this.referenceTokens, (_: CompileTokenMetadata, varName: string) => { + Object.keys(this.referenceTokens).forEach(varName => { var token = this.referenceTokens[varName]; var varValue: o.Expression; if (isPresent(token)) { @@ -313,12 +313,12 @@ export class CompileElement extends CompileNode { requestingProviderType: ProviderAstType, dep: CompileDiDependencyMetadata): o.Expression { var result: o.Expression = null; // constructor content query - if (isBlank(result) && isPresent(dep.query)) { + if (!result && isPresent(dep.query)) { result = this._addQuery(dep.query, null).queryList; } // constructor view query - if (isBlank(result) && isPresent(dep.viewQuery)) { + if (!result && isPresent(dep.viewQuery)) { result = createQueryList( dep.viewQuery, null, `_viewQuery_${dep.viewQuery.selectors[0].name}_${this.nodeIndex}_${this._componentConstructorViewQueryLists.length}`, @@ -328,7 +328,7 @@ export class CompileElement extends CompileNode { if (isPresent(dep.token)) { // access builtins with special visibility - if (isBlank(result)) { + if (!result) { if (dep.token.reference === resolveIdentifierToken(Identifiers.ChangeDetectorRef).reference) { if (requestingProviderType === ProviderAstType.Component) { @@ -339,7 +339,7 @@ export class CompileElement extends CompileNode { } } // access regular providers on the element - if (isBlank(result)) { + if (!result) { let resolvedProvider = this._resolvedProviders.get(dep.token.reference); // don't allow directives / public services to access private services. // only components and private services can access private services. @@ -361,20 +361,20 @@ export class CompileElement extends CompileNode { if (dep.isValue) { result = o.literal(dep.value); } - if (isBlank(result) && !dep.isSkipSelf) { + if (!result && !dep.isSkipSelf) { result = this._getLocalDependency(requestingProviderType, dep); } // check parent elements - while (isBlank(result) && !currElement.parent.isNull()) { + while (!result && !currElement.parent.isNull()) { currElement = currElement.parent; result = currElement._getLocalDependency( ProviderAstType.PublicService, new CompileDiDependencyMetadata({token: dep.token})); } - if (isBlank(result)) { + if (!result) { result = injectFromViewParentInjector(dep.token, dep.isOptional); } - if (isBlank(result)) { + if (!result) { result = o.NULL_EXPR; } return getPropertyInView(result, this.view, currElement.view); @@ -411,7 +411,7 @@ function createProviderProperty( resolvedProviderValueExpr = providerValueExpressions[0]; type = providerValueExpressions[0].type; } - if (isBlank(type)) { + if (!type) { type = o.DYNAMIC_TYPE; } if (isEager) { diff --git a/modules/@angular/compiler/src/view_compiler/compile_pipe.ts b/modules/@angular/compiler/src/view_compiler/compile_pipe.ts index 56ba18b9398d..ff5a02983f95 100644 --- a/modules/@angular/compiler/src/view_compiler/compile_pipe.ts +++ b/modules/@angular/compiler/src/view_compiler/compile_pipe.ts @@ -8,8 +8,7 @@ import {CompilePipeMetadata} from '../compile_metadata'; -import {isBlank, isPresent} from '../facade/lang'; -import {Identifiers, identifierToken, resolveIdentifier, resolveIdentifierToken} from '../identifiers'; +import {Identifiers, resolveIdentifier, resolveIdentifierToken} from '../identifiers'; import * as o from '../output/output_ast'; import {CompileView} from './compile_view'; @@ -23,7 +22,7 @@ export class CompilePipe { if (meta.pure) { // pure pipes live on the component view pipe = compView.purePipes.get(name); - if (isBlank(pipe)) { + if (!pipe) { pipe = new CompilePipe(compView, meta); compView.purePipes.set(name, pipe); compView.pipes.push(pipe); @@ -85,7 +84,7 @@ function _findPipeMeta(view: CompileView, name: string): CompilePipeMetadata { break; } } - if (isBlank(pipeMeta)) { + if (!pipeMeta) { throw new Error( `Illegal state: Could not find pipe ${name} although the parser should have detected this error!`); } diff --git a/modules/@angular/compiler/src/view_compiler/compile_query.ts b/modules/@angular/compiler/src/view_compiler/compile_query.ts index 1069455bfa1d..ab7b71e77c3e 100644 --- a/modules/@angular/compiler/src/view_compiler/compile_query.ts +++ b/modules/@angular/compiler/src/view_compiler/compile_query.ts @@ -6,9 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ -import {CompileQueryMetadata, CompileTokenMetadata} from '../compile_metadata'; +import {CompileQueryMetadata} from '../compile_metadata'; import {ListWrapper} from '../facade/collection'; -import {isBlank, isPresent} from '../facade/lang'; +import {isPresent} from '../facade/lang'; import {Identifiers, resolveIdentifier} from '../identifiers'; import * as o from '../output/output_ast'; @@ -101,9 +101,8 @@ function createQueryValues(viewValues: ViewQueryValues): o.Expression[] { function mapNestedViews( declarationAppElement: o.Expression, view: CompileView, expressions: o.Expression[]): o.Expression { - var adjustedExpressions: o.Expression[] = expressions.map((expr) => { - return o.replaceVarInExpression(o.THIS_EXPR.name, o.variable('nestedView'), expr); - }); + var adjustedExpressions: o.Expression[] = expressions.map( + (expr) => o.replaceVarInExpression(o.THIS_EXPR.name, o.variable('nestedView'), expr)); return declarationAppElement.callMethod('mapNestedViews', [ o.variable(view.className), o.fn( @@ -129,7 +128,7 @@ export function createQueryList( export function addQueryToTokenMap(map: Map, query: CompileQuery) { query.meta.selectors.forEach((selector) => { var entry = map.get(selector.reference); - if (isBlank(entry)) { + if (!entry) { entry = []; map.set(selector.reference, entry); } diff --git a/modules/@angular/compiler/src/view_compiler/compile_view.ts b/modules/@angular/compiler/src/view_compiler/compile_view.ts index 9f78ba78b53d..eab5c45bf331 100644 --- a/modules/@angular/compiler/src/view_compiler/compile_view.ts +++ b/modules/@angular/compiler/src/view_compiler/compile_view.ts @@ -6,15 +6,14 @@ * found in the LICENSE file at https://angular.io/license */ -import {CompiledAnimationTriggerResult} from '../animation/animation_compiler'; -import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeMetadata, CompileTokenMetadata} from '../compile_metadata'; +import {AnimationEntryCompileResult} from '../animation/animation_compiler'; +import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeMetadata} from '../compile_metadata'; import {CompilerConfig} from '../config'; import {ListWrapper, MapWrapper} from '../facade/collection'; -import {isBlank, isPresent} from '../facade/lang'; +import {isPresent} from '../facade/lang'; import {Identifiers, resolveIdentifier} from '../identifiers'; import * as o from '../output/output_ast'; import {ViewType} from '../private_import_core'; -import {createDiTokenExpression} from '../util'; import {CompileBinding} from './compile_binding'; import {CompileElement, CompileNode} from './compile_element'; @@ -23,7 +22,7 @@ import {CompilePipe} from './compile_pipe'; import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query'; import {EventHandlerVars} from './constants'; import {NameResolver} from './expression_converter'; -import {createPureProxy, getPropertyInView, getViewFactoryName, injectFromViewParentInjector} from './util'; +import {createPureProxy, getPropertyInView, getViewFactoryName} from './util'; export class CompileView implements NameResolver { public viewType: ViewType; @@ -72,7 +71,7 @@ export class CompileView implements NameResolver { constructor( public component: CompileDirectiveMetadata, public genConfig: CompilerConfig, public pipeMetas: CompilePipeMetadata[], public styles: o.Expression, - public animations: CompiledAnimationTriggerResult[], public viewIndex: number, + public animations: AnimationEntryCompileResult[], public viewIndex: number, public declarationElement: CompileElement, public templateVariableBindings: string[][]) { this.createMethod = new CompileMethod(this); this.animationBindingsMethod = new CompileMethod(this); @@ -139,7 +138,7 @@ export class CompileView implements NameResolver { } var currView: CompileView = this; var result = currView.locals.get(name); - while (isBlank(result) && isPresent(currView.declarationElement.view)) { + while (!result && isPresent(currView.declarationElement.view)) { currView = currView.declarationElement.view; result = currView.locals.get(name); } diff --git a/modules/@angular/compiler/src/view_compiler/constants.ts b/modules/@angular/compiler/src/view_compiler/constants.ts index b6dc075f147f..f97a4ff565e4 100644 --- a/modules/@angular/compiler/src/view_compiler/constants.ts +++ b/modules/@angular/compiler/src/view_compiler/constants.ts @@ -9,7 +9,6 @@ import {ChangeDetectionStrategy, ViewEncapsulation} from '@angular/core'; import {CompileIdentifierMetadata} from '../compile_metadata'; -import {isBlank} from '../facade/lang'; import {Identifiers, resolveEnumIdentifier, resolveIdentifier} from '../identifiers'; import * as o from '../output/output_ast'; diff --git a/modules/@angular/compiler/src/view_compiler/event_binder.ts b/modules/@angular/compiler/src/view_compiler/event_binder.ts index 947e6241f447..504923a7dff1 100644 --- a/modules/@angular/compiler/src/view_compiler/event_binder.ts +++ b/modules/@angular/compiler/src/view_compiler/event_binder.ts @@ -7,11 +7,9 @@ */ import {CompileDirectiveMetadata} from '../compile_metadata'; -import {ListWrapper, StringMapWrapper} from '../facade/collection'; -import {StringWrapper, isBlank, isPresent} from '../facade/lang'; -import {Identifiers, identifierToken, resolveIdentifier} from '../identifiers'; +import {StringWrapper, isPresent} from '../facade/lang'; +import {identifierToken} from '../identifiers'; import * as o from '../output/output_ast'; -import {AnimationOutput} from '../private_import_core'; import {BoundEventAst, DirectiveAst} from '../template_parser/template_ast'; import {CompileBinding} from './compile_binding'; @@ -20,10 +18,6 @@ import {CompileMethod} from './compile_method'; import {EventHandlerVars, ViewProperties} from './constants'; import {convertCdStatementToIr} from './expression_converter'; -export class CompileElementAnimationOutput { - constructor(public listener: CompileEventListener, public output: AnimationOutput) {} -} - export class CompileEventListener { private _method: CompileMethod; private _hasComponentHostListener: boolean = false; @@ -32,13 +26,14 @@ export class CompileEventListener { private _actionResultExprs: o.Expression[] = []; static getOrCreate( - compileElement: CompileElement, eventTarget: string, eventName: string, + compileElement: CompileElement, eventTarget: string, eventName: string, eventPhase: string, targetEventListeners: CompileEventListener[]): CompileEventListener { var listener = targetEventListeners.find( - listener => listener.eventTarget == eventTarget && listener.eventName == eventName); - if (isBlank(listener)) { + listener => listener.eventTarget == eventTarget && listener.eventName == eventName && + listener.eventPhase == eventPhase); + if (!listener) { listener = new CompileEventListener( - compileElement, eventTarget, eventName, targetEventListeners.length); + compileElement, eventTarget, eventName, eventPhase, targetEventListeners.length); targetEventListeners.push(listener); } return listener; @@ -48,7 +43,7 @@ export class CompileEventListener { constructor( public compileElement: CompileElement, public eventTarget: string, public eventName: string, - listenerIndex: number) { + public eventPhase: string, listenerIndex: number) { this._method = new CompileMethod(compileElement.view); this._methodName = `_handle_${santitizeEventName(eventName)}_${compileElement.nodeIndex}_${listenerIndex}`; @@ -119,7 +114,7 @@ export class CompileEventListener { disposable.set(listenExpr).toDeclStmt(o.FUNCTION_TYPE, [o.StmtModifier.Private])); } - listenToAnimation(output: AnimationOutput) { + listenToAnimation() { var outputListener = o.THIS_EXPR.callMethod( 'eventHandler', [o.THIS_EXPR.prop(this._methodName).callMethod(o.BuiltinMethod.Bind, [o.THIS_EXPR])]); @@ -129,11 +124,8 @@ export class CompileEventListener { .callMethod( 'registerAnimationOutput', [ - this.compileElement.renderNode, - o.importExpr(resolveIdentifier(Identifiers.AnimationOutput)).instantiate([ - o.literal(output.name), o.literal(output.phase) - ]), - outputListener + this.compileElement.renderNode, o.literal(this.eventName), + o.literal(this.eventPhase), outputListener ]) .toStmt(); this.compileElement.view.createMethod.addStmt(stmt); @@ -160,7 +152,7 @@ export function collectEventListeners( hostEvents.forEach((hostEvent) => { compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent)); var listener = CompileEventListener.getOrCreate( - compileElement, hostEvent.target, hostEvent.name, eventListeners); + compileElement, hostEvent.target, hostEvent.name, hostEvent.phase, eventListeners); listener.addAction(hostEvent, null, null); }); dirs.forEach((directiveAst) => { @@ -169,7 +161,7 @@ export function collectEventListeners( directiveAst.hostEvents.forEach((hostEvent) => { compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent)); var listener = CompileEventListener.getOrCreate( - compileElement, hostEvent.target, hostEvent.name, eventListeners); + compileElement, hostEvent.target, hostEvent.name, hostEvent.phase, eventListeners); listener.addAction(hostEvent, directiveAst.directive, directiveInstance); }); }); @@ -180,21 +172,22 @@ export function collectEventListeners( export function bindDirectiveOutputs( directiveAst: DirectiveAst, directiveInstance: o.Expression, eventListeners: CompileEventListener[]) { - StringMapWrapper.forEach( - directiveAst.directive.outputs, - (eventName: any /** TODO #9100 */, observablePropName: any /** TODO #9100 */) => { - eventListeners.filter(listener => listener.eventName == eventName).forEach((listener) => { - listener.listenToDirective(directiveInstance, observablePropName); - }); - }); + Object.keys(directiveAst.directive.outputs).forEach(observablePropName => { + const eventName = directiveAst.directive.outputs[observablePropName]; + eventListeners.filter(listener => listener.eventName == eventName).forEach((listener) => { + listener.listenToDirective(directiveInstance, observablePropName); + }); + }); } export function bindRenderOutputs(eventListeners: CompileEventListener[]) { - eventListeners.forEach(listener => listener.listenToRenderer()); -} - -export function bindAnimationOutputs(eventListeners: CompileElementAnimationOutput[]) { - eventListeners.forEach(entry => { entry.listener.listenToAnimation(entry.output); }); + eventListeners.forEach(listener => { + if (listener.eventPhase) { + listener.listenToAnimation(); + } else { + listener.listenToRenderer(); + } + }); } function convertStmtIntoExpression(stmt: o.Statement): o.Expression { diff --git a/modules/@angular/compiler/src/view_compiler/expression_converter.ts b/modules/@angular/compiler/src/view_compiler/expression_converter.ts index fd44f95452ed..5fc635c58cc6 100644 --- a/modules/@angular/compiler/src/view_compiler/expression_converter.ts +++ b/modules/@angular/compiler/src/view_compiler/expression_converter.ts @@ -340,7 +340,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor { // Notice that the first guard condition is the left hand of the left most safe access node // which comes in as leftMostSafe to this routine. - let guardedExpression = this.visit(leftMostSafe.receiver, mode); + let guardedExpression = this.visit(leftMostSafe.receiver, _Mode.Expression); let temporary: o.ReadVarExpr; if (this.needsTemporary(leftMostSafe.receiver)) { // If the expression has method calls or pipes then we need to save the result into a @@ -369,7 +369,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor { } // Recursively convert the node now without the guarded member access. - const access = this.visit(ast, mode); + const access = this.visit(ast, _Mode.Expression); // Remove the mapping. This is not strictly required as the converter only traverses each node // once but is safer if the conversion is changed to traverse the nodes more than once. @@ -381,7 +381,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor { } // Produce the conditional - return condition.conditional(o.literal(null), access); + return convertToStatementIfNeeded(mode, condition.conditional(o.literal(null), access)); } // Given a expression of the form a?.b.c?.d.e the the left most safe node is diff --git a/modules/@angular/compiler/src/view_compiler/property_binder.ts b/modules/@angular/compiler/src/view_compiler/property_binder.ts index 026efece67f4..192ec98db2d2 100644 --- a/modules/@angular/compiler/src/view_compiler/property_binder.ts +++ b/modules/@angular/compiler/src/view_compiler/property_binder.ts @@ -9,7 +9,7 @@ import {SecurityContext} from '@angular/core'; import * as cdAst from '../expression_parser/ast'; -import {isBlank, isPresent} from '../facade/lang'; +import {isPresent} from '../facade/lang'; import {Identifiers, resolveIdentifier} from '../identifiers'; import * as o from '../output/output_ast'; import {EMPTY_STATE as EMPTY_ANIMATION_STATE, LifecycleHooks, isDefaultChangeDetectionStrategy} from '../private_import_core'; @@ -31,15 +31,13 @@ function createCurrValueExpr(exprIndex: number): o.ReadVarExpr { return o.variable(`currVal_${exprIndex}`); // fix syntax highlighting: ` } -const _animationViewCheckedFlagMap = new Map(); - function bind( view: CompileView, currValExpr: o.ReadVarExpr, fieldExpr: o.ReadPropExpr, parsedExpression: cdAst.AST, context: o.Expression, actions: o.Statement[], method: CompileMethod, bindingIndex: number) { var checkExpression = convertCdExpressionToIr( view, context, parsedExpression, DetectChangesVars.valUnwrapper, bindingIndex); - if (isBlank(checkExpression.expression)) { + if (!checkExpression.expression) { // e.g. an empty expression was given return; } diff --git a/modules/@angular/compiler/src/view_compiler/util.ts b/modules/@angular/compiler/src/view_compiler/util.ts index cb422b6e949a..59b66bf26d46 100644 --- a/modules/@angular/compiler/src/view_compiler/util.ts +++ b/modules/@angular/compiler/src/view_compiler/util.ts @@ -8,7 +8,7 @@ import {CompileDirectiveMetadata, CompileTokenMetadata} from '../compile_metadata'; -import {isBlank, isPresent} from '../facade/lang'; +import {isPresent} from '../facade/lang'; import {Identifiers, resolveIdentifier} from '../identifiers'; import * as o from '../output/output_ast'; import {createDiTokenExpression} from '../util'; @@ -84,7 +84,7 @@ export function createPureProxy( view.fields.push(new o.ClassField(pureProxyProp.name, null)); var pureProxyId = argCount < Identifiers.pureProxies.length ? Identifiers.pureProxies[argCount] : null; - if (isBlank(pureProxyId)) { + if (!pureProxyId) { throw new Error(`Unsupported number of argument for pure functions: ${argCount}`); } view.createMethod.addStmt(o.THIS_EXPR.prop(pureProxyProp.name) diff --git a/modules/@angular/compiler/src/view_compiler/view_binder.ts b/modules/@angular/compiler/src/view_compiler/view_binder.ts index 403e6cfe514c..5c81bbef54f1 100644 --- a/modules/@angular/compiler/src/view_compiler/view_binder.ts +++ b/modules/@angular/compiler/src/view_compiler/view_binder.ts @@ -5,20 +5,17 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {ListWrapper} from '../facade/collection'; -import {identifierToken} from '../identifiers'; -import {AnimationOutput} from '../private_import_core'; + import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast'; -import {CompileElement, CompileNode} from './compile_element'; +import {CompileElement} from './compile_element'; import {CompileView} from './compile_view'; -import {CompileElementAnimationOutput, CompileEventListener, bindAnimationOutputs, bindDirectiveOutputs, bindRenderOutputs, collectEventListeners} from './event_binder'; +import {CompileEventListener, bindDirectiveOutputs, bindRenderOutputs, collectEventListeners} from './event_binder'; import {bindDirectiveAfterContentLifecycleCallbacks, bindDirectiveAfterViewLifecycleCallbacks, bindDirectiveDetectChangesLifecycleCallbacks, bindInjectableDestroyLifecycleCallbacks, bindPipeDestroyLifecycleCallbacks} from './lifecycle_binder'; import {bindDirectiveHostProps, bindDirectiveInputs, bindRenderInputs, bindRenderText} from './property_binder'; -export function bindView( - view: CompileView, parsedTemplate: TemplateAst[], animationOutputs: AnimationOutput[]): void { - var visitor = new ViewBinderVisitor(view, animationOutputs); +export function bindView(view: CompileView, parsedTemplate: TemplateAst[]): void { + var visitor = new ViewBinderVisitor(view); templateVisitAll(visitor, parsedTemplate); view.pipes.forEach( (pipe) => { bindPipeDestroyLifecycleCallbacks(pipe.meta, pipe.instance, pipe.view); }); @@ -26,12 +23,8 @@ export function bindView( class ViewBinderVisitor implements TemplateAstVisitor { private _nodeIndex: number = 0; - private _animationOutputsMap: {[key: string]: AnimationOutput} = {}; - constructor(public view: CompileView, public animationOutputs: AnimationOutput[]) { - animationOutputs.forEach( - entry => { this._animationOutputsMap[entry.fullPropertyName] = entry; }); - } + constructor(public view: CompileView) {} visitBoundText(ast: BoundTextAst, parent: CompileElement): any { var node = this.view.nodes[this._nodeIndex++]; @@ -48,22 +41,9 @@ class ViewBinderVisitor implements TemplateAstVisitor { visitElement(ast: ElementAst, parent: CompileElement): any { var compileElement = this.view.nodes[this._nodeIndex++]; var eventListeners: CompileEventListener[] = []; - var animationEventListeners: CompileElementAnimationOutput[] = []; collectEventListeners(ast.outputs, ast.directives, compileElement).forEach(entry => { - // TODO: figure out how to abstract this `if` statement elsewhere - if (entry.eventName[0] == '@') { - let animationOutputName = entry.eventName.substr(1); - let output = this._animationOutputsMap[animationOutputName]; - // no need to report an error here since the parser will - // have caught the missing animation trigger definition - if (output) { - animationEventListeners.push(new CompileElementAnimationOutput(entry, output)); - } - } else { - eventListeners.push(entry); - } + eventListeners.push(entry); }); - bindAnimationOutputs(animationEventListeners); bindRenderInputs(ast.inputs, compileElement); bindRenderOutputs(eventListeners); ast.directives.forEach((directiveAst) => { @@ -108,7 +88,7 @@ class ViewBinderVisitor implements TemplateAstVisitor { var providerInstance = compileElement.instances.get(providerAst.token.reference); bindInjectableDestroyLifecycleCallbacks(providerAst, providerInstance, compileElement); }); - bindView(compileElement.embeddedView, ast.children, this.animationOutputs); + bindView(compileElement.embeddedView, ast.children); return null; } diff --git a/modules/@angular/compiler/src/view_compiler/view_builder.ts b/modules/@angular/compiler/src/view_compiler/view_builder.ts index ace3e903cb34..4fdb24f7d10d 100644 --- a/modules/@angular/compiler/src/view_compiler/view_builder.ts +++ b/modules/@angular/compiler/src/view_compiler/view_builder.ts @@ -6,16 +6,15 @@ * found in the LICENSE file at https://angular.io/license */ -import {ChangeDetectionStrategy, ViewEncapsulation} from '@angular/core'; +import {ViewEncapsulation} from '@angular/core'; -import {AnimationCompiler} from '../animation/animation_compiler'; -import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileTokenMetadata, CompileTypeMetadata} from '../compile_metadata'; -import {ListWrapper, SetWrapper, StringMapWrapper} from '../facade/collection'; +import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileTokenMetadata} from '../compile_metadata'; +import {ListWrapper} from '../facade/collection'; import {StringWrapper, isPresent} from '../facade/lang'; -import {Identifiers, identifierToken, resolveIdentifier, resolveIdentifierToken} from '../identifiers'; +import {Identifiers, identifierToken, resolveIdentifier} from '../identifiers'; import * as o from '../output/output_ast'; import {ChangeDetectorStatus, ViewType, isDefaultChangeDetectionStrategy} from '../private_import_core'; -import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ProviderAst, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast'; +import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast'; import {createDiTokenExpression} from '../util'; import {CompileElement, CompileNode} from './compile_element'; @@ -65,8 +64,6 @@ export function finishView(view: CompileView, targetStatements: o.Statement[]) { class ViewBuilderVisitor implements TemplateAstVisitor { nestedViewCount: number = 0; - private _animationCompiler = new AnimationCompiler(); - constructor( public view: CompileView, public targetDependencies: Array) {} @@ -279,12 +276,10 @@ class ViewBuilderVisitor implements TemplateAstVisitor { ast.hasViewContainer, true, ast.references); this.view.nodes.push(compileElement); - var compiledAnimations = this._animationCompiler.compileComponent(this.view.component, [ast]); - this.nestedViewCount++; var embeddedView = new CompileView( this.view.component, this.view.genConfig, this.view.pipeMetas, o.NULL_EXPR, - compiledAnimations.triggers, this.view.viewIndex + this.nestedViewCount, compileElement, + this.view.animations, this.view.viewIndex + this.nestedViewCount, compileElement, templateVariableBindings); this.nestedViewCount += buildView(embeddedView, ast.children, this.targetDependencies); @@ -351,10 +346,10 @@ function _mergeHtmlAndDirectiveAttrs( declaredHtmlAttrs: {[key: string]: string}, directives: CompileDirectiveMetadata[]): string[][] { var result: {[key: string]: string} = {}; - StringMapWrapper.forEach( - declaredHtmlAttrs, (value: string, key: string) => { result[key] = value; }); + Object.keys(declaredHtmlAttrs).forEach(key => { result[key] = declaredHtmlAttrs[key]; }); directives.forEach(directiveMeta => { - StringMapWrapper.forEach(directiveMeta.hostAttributes, (value: string, name: string) => { + Object.keys(directiveMeta.hostAttributes).forEach(name => { + const value = directiveMeta.hostAttributes[name]; var prevValue = result[name]; result[name] = isPresent(prevValue) ? mergeAttributeValue(name, prevValue, value) : value; }); @@ -378,9 +373,7 @@ function mergeAttributeValue(attrName: string, attrValue1: string, attrValue2: s function mapToKeyValueArray(data: {[key: string]: string}): string[][] { var entryArray: string[][] = []; - StringMapWrapper.forEach(data, (value: string, name: string) => { - entryArray.push([name, value]); - }); + Object.keys(data).forEach(name => { entryArray.push([name, data[name]]); }); // We need to sort to get a defined output order // for tests and for caching generated artifacts... ListWrapper.sort(entryArray, (entry1, entry2) => StringWrapper.compare(entry1[0], entry2[0])); @@ -426,11 +419,11 @@ function createStaticNodeDebugInfo(node: CompileNode): o.Expression { if (isPresent(compileElement.component)) { componentToken = createDiTokenExpression(identifierToken(compileElement.component.type)); } - StringMapWrapper.forEach( - compileElement.referenceTokens, (token: CompileTokenMetadata, varName: string) => { - varTokenEntries.push( - [varName, isPresent(token) ? createDiTokenExpression(token) : o.NULL_EXPR]); - }); + Object.keys(compileElement.referenceTokens).forEach(varName => { + const token = compileElement.referenceTokens[varName]; + varTokenEntries.push( + [varName, isPresent(token) ? createDiTokenExpression(token) : o.NULL_EXPR]); + }); } return o.importExpr(resolveIdentifier(Identifiers.StaticNodeDebugInfo)) .instantiate( @@ -518,19 +511,20 @@ function createViewFactory( templateUrlInfo = view.component.template.templateUrl; } if (view.viewIndex === 0) { - var animationsExpr = o.literalMap(view.animations.map(entry => [entry.name, entry.fnVariable])); - initRenderCompTypeStmts = [new o.IfStmt(renderCompTypeVar.identical(o.NULL_EXPR), [ - renderCompTypeVar - .set(ViewConstructorVars.viewUtils.callMethod( - 'createRenderComponentType', - [ - o.literal(templateUrlInfo), - o.literal(view.component.template.ngContentSelectors.length), - ViewEncapsulationEnum.fromValue(view.component.template.encapsulation), view.styles, - animationsExpr - ])) - .toStmt() - ])]; + var animationsExpr = o.literalMap(view.animations.map(entry => [entry.name, entry.fnExp])); + initRenderCompTypeStmts = [new o.IfStmt( + renderCompTypeVar.identical(o.NULL_EXPR), + [renderCompTypeVar + .set(ViewConstructorVars.viewUtils.callMethod( + 'createRenderComponentType', + [ + view.genConfig.genDebugInfo ? o.literal(templateUrlInfo) : o.literal(''), + o.literal(view.component.template.ngContentSelectors.length), + ViewEncapsulationEnum.fromValue(view.component.template.encapsulation), + view.styles, + animationsExpr, + ])) + .toStmt()])]; } return o .fn(viewFactoryArgs, initRenderCompTypeStmts.concat([new o.ReturnStatement( @@ -602,15 +596,15 @@ function generateDetectChangesMethod(view: CompileView): o.Statement[] { var varStmts: any[] = []; var readVars = o.findReadVarNames(stmts); - if (SetWrapper.has(readVars, DetectChangesVars.changed.name)) { + if (readVars.has(DetectChangesVars.changed.name)) { varStmts.push(DetectChangesVars.changed.set(o.literal(true)).toDeclStmt(o.BOOL_TYPE)); } - if (SetWrapper.has(readVars, DetectChangesVars.changes.name)) { + if (readVars.has(DetectChangesVars.changes.name)) { varStmts.push( DetectChangesVars.changes.set(o.NULL_EXPR) .toDeclStmt(new o.MapType(o.importType(resolveIdentifier(Identifiers.SimpleChange))))); } - if (SetWrapper.has(readVars, DetectChangesVars.valUnwrapper.name)) { + if (readVars.has(DetectChangesVars.valUnwrapper.name)) { varStmts.push( DetectChangesVars.valUnwrapper .set(o.importExpr(resolveIdentifier(Identifiers.ValueUnwrapper)).instantiate([])) diff --git a/modules/@angular/compiler/src/view_compiler/view_compiler.ts b/modules/@angular/compiler/src/view_compiler/view_compiler.ts index 073c92cb4167..2a67f7bdbd1e 100644 --- a/modules/@angular/compiler/src/view_compiler/view_compiler.ts +++ b/modules/@angular/compiler/src/view_compiler/view_compiler.ts @@ -8,7 +8,7 @@ import {Injectable} from '@angular/core'; -import {AnimationCompiler} from '../animation/animation_compiler'; +import {AnimationCompiler, AnimationEntryCompileResult} from '../animation/animation_compiler'; import {CompileDirectiveMetadata, CompilePipeMetadata} from '../compile_metadata'; import {CompilerConfig} from '../config'; import * as o from '../output/output_ast'; @@ -34,22 +34,18 @@ export class ViewCompiler { compileComponent( component: CompileDirectiveMetadata, template: TemplateAst[], styles: o.Expression, - pipes: CompilePipeMetadata[]): ViewCompileResult { - var dependencies: Array = []; - var compiledAnimations = this._animationCompiler.compileComponent(component, template); - var statements: o.Statement[] = []; - var animationTriggers = compiledAnimations.triggers; - animationTriggers.forEach(entry => { - statements.push(entry.statesMapStatement); - statements.push(entry.fnStatement); - }); - var view = new CompileView( - component, this._genConfig, pipes, styles, animationTriggers, 0, + pipes: CompilePipeMetadata[], + compiledAnimations: AnimationEntryCompileResult[]): ViewCompileResult { + const dependencies: Array = []; + const view = new CompileView( + component, this._genConfig, pipes, styles, compiledAnimations, 0, CompileElement.createNull(), []); + + const statements: o.Statement[] = []; buildView(view, template, dependencies); // Need to separate binding from creation to be able to refer to // variables that have been declared after usage. - bindView(view, template, compiledAnimations.outputs); + bindView(view, template); finishView(view, statements); return new ViewCompileResult(statements, view.viewFactory.name, dependencies); diff --git a/modules/@angular/compiler/test/animation/animation_compiler_spec.ts b/modules/@angular/compiler/test/animation/animation_compiler_spec.ts index 094a3da956a3..ca758524fb40 100644 --- a/modules/@angular/compiler/test/animation/animation_compiler_spec.ts +++ b/modules/@angular/compiler/test/animation/animation_compiler_spec.ts @@ -6,11 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ -import {AnimationMetadata, animate, group, sequence, style, transition, trigger} from '@angular/core'; -import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; - -import {StringMapWrapper} from '../../../platform-browser-dynamic/src/facade/collection'; -import {AnimationCompiler, CompiledAnimationTriggerResult} from '../../src/animation/animation_compiler'; +import {AnimationMetadata, animate, sequence, style, transition, trigger} from '@angular/core'; +import {beforeEach, describe, expect, inject, it} from '@angular/core/testing/testing_internal'; +import {AnimationCompiler, AnimationEntryCompileResult} from '../../src/animation/animation_compiler'; +import {AnimationParser} from '../../src/animation/animation_parser'; import {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileTemplateMetadata, CompileTypeMetadata} from '../../src/compile_metadata'; import {CompileMetadataResolver} from '../../src/metadata_resolver'; @@ -20,12 +19,13 @@ export function main() { beforeEach( inject([CompileMetadataResolver], (res: CompileMetadataResolver) => { resolver = res; })); - var compiler = new AnimationCompiler(); + const parser = new AnimationParser(); + const compiler = new AnimationCompiler(); var compileAnimations = - (component: CompileDirectiveMetadata): CompiledAnimationTriggerResult => { - var result = compiler.compileComponent(component, []); - return result.triggers[0]; + (component: CompileDirectiveMetadata): AnimationEntryCompileResult[] => { + const parsedAnimations = parser.parseComponent(component); + return compiler.compile(component.type.name, parsedAnimations); }; var compileTriggers = (input: any[]) => { @@ -66,14 +66,5 @@ export function main() { expect(capturedErrorMessage) .toMatch(/Animation states via styles must be prefixed with a ":"/); }); - - it('should throw an error when two or more animation triggers contain the same name', () => { - var t1Data: any[] = []; - var t2Data: any[] = []; - - expect(() => { - compileTriggers([['myTrigger', t1Data], ['myTrigger', t2Data]]); - }).toThrowError(/The animation trigger "myTrigger" has already been registered on "myCmp"/); - }); }); } diff --git a/modules/@angular/compiler/test/animation/animation_parser_spec.ts b/modules/@angular/compiler/test/animation/animation_parser_spec.ts index b5688715205a..a726fd23fb92 100644 --- a/modules/@angular/compiler/test/animation/animation_parser_spec.ts +++ b/modules/@angular/compiler/test/animation/animation_parser_spec.ts @@ -6,13 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ -import {AnimationAnimateMetadata, AnimationGroupMetadata, AnimationMetadata, AnimationSequenceMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core'; -import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, describe, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; +import {AnimationMetadata, animate, group, keyframes, sequence, style, transition, trigger} from '@angular/core'; +import {beforeEach, describe, inject, it} from '@angular/core/testing/testing_internal'; import {expect} from '@angular/platform-browser/testing/matchers'; -import {AnimationAst, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateTransitionAst, AnimationStepAst, AnimationStylesAst} from '../../src/animation/animation_ast'; -import {parseAnimationEntry} from '../../src/animation/animation_parser'; -import {StringMapWrapper} from '../../src/facade/collection'; +import {AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStepAst, AnimationStylesAst} from '../../src/animation/animation_ast'; +import {AnimationParser} from '../../src/animation/animation_parser'; import {CompileMetadataResolver} from '../../src/metadata_resolver'; import {FILL_STYLE_FLAG, flattenStyles} from '../private_import_core'; @@ -21,15 +20,13 @@ export function main() { var combineStyles = (styles: AnimationStylesAst): {[key: string]: string | number} => { var flatStyles: {[key: string]: string | number} = {}; styles.styles.forEach( - entry => StringMapWrapper.forEach( - entry, (val: any /** TODO #9100 */, prop: any /** TODO #9100 */) => { - flatStyles[prop] = val; - })); + entry => Object.keys(entry).forEach(prop => { flatStyles[prop] = entry[prop]; })); return flatStyles; }; - var collectKeyframeStyles = (keyframe: AnimationKeyframeAst): - {[key: string]: string | number} => { return combineStyles(keyframe.styles); }; + var collectKeyframeStyles = + (keyframe: AnimationKeyframeAst): {[key: string]: string | number} => + combineStyles(keyframe.styles); var collectStepStyles = (step: AnimationStepAst): Array<{[key: string]: string | number}> => { var keyframes = step.keyframes; @@ -46,17 +43,17 @@ export function main() { inject([CompileMetadataResolver], (res: CompileMetadataResolver) => { resolver = res; })); var parseAnimation = (data: AnimationMetadata[]) => { - var entry = trigger('myAnimation', [transition('state1 => state2', sequence(data))]); - var compiledAnimationEntry = resolver.getAnimationEntryMetadata(entry); - return parseAnimationEntry(compiledAnimationEntry); + const entry = trigger('myAnimation', [transition('state1 => state2', sequence(data))]); + const compiledAnimationEntry = resolver.getAnimationEntryMetadata(entry); + const parser = new AnimationParser(); + return parser.parseEntry(compiledAnimationEntry); }; var getAnimationAstFromEntryAst = (ast: AnimationEntryAst) => { return ast.stateTransitions[0].animation; }; - var parseAnimationAst = (data: AnimationMetadata[]) => { - return getAnimationAstFromEntryAst(parseAnimation(data).ast); - }; + var parseAnimationAst = (data: AnimationMetadata[]) => + getAnimationAstFromEntryAst(parseAnimation(data).ast); var parseAnimationAndGetErrors = (data: AnimationMetadata[]) => parseAnimation(data).errors; diff --git a/modules/@angular/compiler/test/css_parser/css_lexer_spec.ts b/modules/@angular/compiler/test/css_parser/css_lexer_spec.ts index 04539fbdde31..781a44a2d666 100644 --- a/modules/@angular/compiler/test/css_parser/css_lexer_spec.ts +++ b/modules/@angular/compiler/test/css_parser/css_lexer_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '../../../core/testing/testing_internal'; +import {describe, expect, it} from '../../../core/testing/testing_internal'; import {CssLexer, CssLexerMode, CssScannerError, CssToken, CssTokenType} from '../../src/css_parser/css_lexer'; import {isPresent} from '../../src/facade/lang'; diff --git a/modules/@angular/compiler/test/css_parser/css_parser_spec.ts b/modules/@angular/compiler/test/css_parser/css_parser_spec.ts index 86710adfb90c..024f59f03a02 100644 --- a/modules/@angular/compiler/test/css_parser/css_parser_spec.ts +++ b/modules/@angular/compiler/test/css_parser/css_parser_spec.ts @@ -7,7 +7,7 @@ */ -import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '../../../core/testing/testing_internal'; +import {describe, expect, it} from '../../../core/testing/testing_internal'; import {CssBlockAst, CssBlockDefinitionRuleAst, CssBlockRuleAst, CssDefinitionAst, CssInlineRuleAst, CssKeyframeDefinitionAst, CssKeyframeRuleAst, CssMediaQueryRuleAst, CssSelectorRuleAst, CssStyleSheetAst, CssStyleValueAst} from '../../src/css_parser/css_ast'; import {BlockType, CssParseError, CssParser, CssToken, ParsedCssResult} from '../../src/css_parser/css_parser'; import {ParseLocation} from '../../src/parse_util'; diff --git a/modules/@angular/compiler/test/css_parser/css_visitor_spec.ts b/modules/@angular/compiler/test/css_parser/css_visitor_spec.ts index e59014822e8c..19d2a63bf370 100644 --- a/modules/@angular/compiler/test/css_parser/css_visitor_spec.ts +++ b/modules/@angular/compiler/test/css_parser/css_visitor_spec.ts @@ -7,8 +7,8 @@ */ -import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '../../../core/testing/testing_internal'; -import {CssAst, CssAstVisitor, CssAtRulePredicateAst, CssBlockAst, CssBlockDefinitionRuleAst, CssBlockRuleAst, CssDefinitionAst, CssInlineRuleAst, CssKeyframeDefinitionAst, CssKeyframeRuleAst, CssMediaQueryRuleAst, CssPseudoSelectorAst, CssRuleAst, CssSelectorAst, CssSelectorRuleAst, CssSimpleSelectorAst, CssStyleSheetAst, CssStyleValueAst, CssStylesBlockAst, CssUnknownRuleAst, CssUnknownTokenListAst} from '../../src/css_parser/css_ast'; +import {beforeEach, describe, expect, it} from '../../../core/testing/testing_internal'; +import {CssAst, CssAstVisitor, CssAtRulePredicateAst, CssBlockAst, CssDefinitionAst, CssInlineRuleAst, CssKeyframeDefinitionAst, CssKeyframeRuleAst, CssMediaQueryRuleAst, CssPseudoSelectorAst, CssRuleAst, CssSelectorAst, CssSelectorRuleAst, CssSimpleSelectorAst, CssStyleSheetAst, CssStyleValueAst, CssStylesBlockAst, CssUnknownRuleAst, CssUnknownTokenListAst} from '../../src/css_parser/css_ast'; import {BlockType, CssParseError, CssParser, CssToken} from '../../src/css_parser/css_parser'; import {isPresent} from '../../src/facade/lang'; diff --git a/modules/@angular/compiler/test/directive_lifecycle_spec.ts b/modules/@angular/compiler/test/directive_lifecycle_spec.ts index d4b62ba40be4..aaee9d17edae 100644 --- a/modules/@angular/compiler/test/directive_lifecycle_spec.ts +++ b/modules/@angular/compiler/test/directive_lifecycle_spec.ts @@ -8,7 +8,7 @@ import {hasLifecycleHook} from '@angular/compiler/src/lifecycle_reflector'; import {LifecycleHooks} from '@angular/core/src/metadata/lifecycle_hooks'; -import {beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe} from '@angular/core/testing/testing_internal'; +import {describe, expect, it} from '@angular/core/testing/testing_internal'; export function main() { describe('Create Directive', () => { diff --git a/modules/@angular/compiler/test/directive_normalizer_spec.ts b/modules/@angular/compiler/test/directive_normalizer_spec.ts index 839b0be86213..6e5ca9547450 100644 --- a/modules/@angular/compiler/test/directive_normalizer_spec.ts +++ b/modules/@angular/compiler/test/directive_normalizer_spec.ts @@ -14,7 +14,7 @@ import {MockResourceLoader} from '@angular/compiler/testing/resource_loader_mock import {TEST_COMPILER_PROVIDERS} from '@angular/compiler/testing/test_bindings'; import {ViewEncapsulation} from '@angular/core/src/metadata/view'; import {TestBed} from '@angular/core/testing'; -import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; +import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@angular/core/testing/testing_internal'; import {SpyResourceLoader} from './spies'; diff --git a/modules/@angular/compiler/test/directive_resolver_spec.ts b/modules/@angular/compiler/test/directive_resolver_spec.ts index 28b4552435c6..7e75be9e1d71 100644 --- a/modules/@angular/compiler/test/directive_resolver_spec.ts +++ b/modules/@angular/compiler/test/directive_resolver_spec.ts @@ -108,16 +108,25 @@ class SomeDirectiveWithViewChild { class ComponentWithTemplate { } +@Directive({ + selector: 'someDirective', + host: {'[decorator]': 'decorator'}, + inputs: ['decorator'], +}) +class SomeDirectiveWithSameHostBindingAndInput { + @Input() @HostBinding() prop: any; +} + class SomeDirectiveWithoutMetadata {} export function main() { describe('DirectiveResolver', () => { - var resolver: DirectiveResolver; + let resolver: DirectiveResolver; beforeEach(() => { resolver = new DirectiveResolver(); }); it('should read out the Directive metadata', () => { - var directiveMetadata = resolver.resolve(SomeDirective); + const directiveMetadata = resolver.resolve(SomeDirective); expect(directiveMetadata) .toEqual(new Directive( {selector: 'someDirective', inputs: [], outputs: [], host: {}, queries: {}})); @@ -130,7 +139,7 @@ export function main() { }); it('should not read parent class Directive metadata', function() { - var directiveMetadata = resolver.resolve(SomeChildDirective); + const directiveMetadata = resolver.resolve(SomeChildDirective); expect(directiveMetadata) .toEqual(new Directive( {selector: 'someChildDirective', inputs: [], outputs: [], host: {}, queries: {}})); @@ -138,12 +147,12 @@ export function main() { describe('inputs', () => { it('should append directive inputs', () => { - var directiveMetadata = resolver.resolve(SomeDirectiveWithInputs); + const directiveMetadata = resolver.resolve(SomeDirectiveWithInputs); expect(directiveMetadata.inputs).toEqual(['c', 'a', 'b: renamed']); }); it('should work with getters and setters', () => { - var directiveMetadata = resolver.resolve(SomeDirectiveWithSetterProps); + const directiveMetadata = resolver.resolve(SomeDirectiveWithSetterProps); expect(directiveMetadata.inputs).toEqual(['a: renamed']); }); @@ -162,12 +171,12 @@ export function main() { describe('outputs', () => { it('should append directive outputs', () => { - var directiveMetadata = resolver.resolve(SomeDirectiveWithOutputs); + const directiveMetadata = resolver.resolve(SomeDirectiveWithOutputs); expect(directiveMetadata.outputs).toEqual(['c', 'a', 'b: renamed']); }); it('should work with getters and setters', () => { - var directiveMetadata = resolver.resolve(SomeDirectiveWithGetterOutputs); + const directiveMetadata = resolver.resolve(SomeDirectiveWithGetterOutputs); expect(directiveMetadata.outputs).toEqual(['a: renamed']); }); @@ -186,12 +195,18 @@ export function main() { describe('host', () => { it('should append host bindings', () => { - var directiveMetadata = resolver.resolve(SomeDirectiveWithHostBindings); + const directiveMetadata = resolver.resolve(SomeDirectiveWithHostBindings); expect(directiveMetadata.host).toEqual({'[c]': 'c', '[a]': 'a', '[renamed]': 'b'}); }); + it('should append host binding and input on the same property', () => { + const directiveMetadata = resolver.resolve(SomeDirectiveWithSameHostBindingAndInput); + expect(directiveMetadata.host).toEqual({'[decorator]': 'decorator', '[prop]': 'prop'}); + expect(directiveMetadata.inputs).toEqual(['decorator', 'prop']); + }); + it('should append host listeners', () => { - var directiveMetadata = resolver.resolve(SomeDirectiveWithHostListeners); + const directiveMetadata = resolver.resolve(SomeDirectiveWithHostListeners); expect(directiveMetadata.host) .toEqual({'(c)': 'onC()', '(a)': 'onA()', '(b)': 'onB($event.value)'}); }); @@ -199,33 +214,33 @@ export function main() { describe('queries', () => { it('should append ContentChildren', () => { - var directiveMetadata = resolver.resolve(SomeDirectiveWithContentChildren); + const directiveMetadata = resolver.resolve(SomeDirectiveWithContentChildren); expect(directiveMetadata.queries) .toEqual({'cs': new ContentChildren('c'), 'as': new ContentChildren('a')}); }); it('should append ViewChildren', () => { - var directiveMetadata = resolver.resolve(SomeDirectiveWithViewChildren); + const directiveMetadata = resolver.resolve(SomeDirectiveWithViewChildren); expect(directiveMetadata.queries) .toEqual({'cs': new ViewChildren('c'), 'as': new ViewChildren('a')}); }); it('should append ContentChild', () => { - var directiveMetadata = resolver.resolve(SomeDirectiveWithContentChild); + const directiveMetadata = resolver.resolve(SomeDirectiveWithContentChild); expect(directiveMetadata.queries) .toEqual({'c': new ContentChild('c'), 'a': new ContentChild('a')}); }); it('should append ViewChild', () => { - var directiveMetadata = resolver.resolve(SomeDirectiveWithViewChild); + const directiveMetadata = resolver.resolve(SomeDirectiveWithViewChild); expect(directiveMetadata.queries) .toEqual({'c': new ViewChild('c'), 'a': new ViewChild('a')}); }); }); - describe('view', () => { + describe('Component', () => { it('should read out the template related metadata from the Component metadata', () => { - var compMetadata = resolver.resolve(ComponentWithTemplate); + const compMetadata: Component = resolver.resolve(ComponentWithTemplate); expect(compMetadata.template).toEqual('some template'); expect(compMetadata.styles).toEqual(['some styles']); }); diff --git a/modules/@angular/compiler/test/expression_parser/unparser.ts b/modules/@angular/compiler/test/expression_parser/unparser.ts index 6d1c835d9c4f..54ddf0790cc9 100644 --- a/modules/@angular/compiler/test/expression_parser/unparser.ts +++ b/modules/@angular/compiler/test/expression_parser/unparser.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {AST, AstVisitor, Binary, BindingPipe, Chain, Conditional, EmptyExpr, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead} from '../../src/expression_parser/ast'; -import {StringWrapper, isPresent, isString} from '../../src/facade/lang'; +import {AST, AstVisitor, Binary, BindingPipe, Chain, Conditional, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead} from '../../src/expression_parser/ast'; +import {StringWrapper, isString} from '../../src/facade/lang'; import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../../src/ml_parser/interpolation_config'; class Unparser implements AstVisitor { diff --git a/modules/@angular/compiler/test/expression_parser/validator.ts b/modules/@angular/compiler/test/expression_parser/validator.ts index f386352bf536..5b176976b3d5 100644 --- a/modules/@angular/compiler/test/expression_parser/validator.ts +++ b/modules/@angular/compiler/test/expression_parser/validator.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AST, Binary, BindingPipe, Chain, Conditional, EmptyExpr, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, ParseSpan, PrefixNot, PropertyRead, PropertyWrite, Quote, RecursiveAstVisitor, SafeMethodCall, SafePropertyRead} from '../../src/expression_parser/ast'; +import {AST, Binary, BindingPipe, Chain, Conditional, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, ParseSpan, PrefixNot, PropertyRead, PropertyWrite, Quote, RecursiveAstVisitor, SafeMethodCall, SafePropertyRead} from '../../src/expression_parser/ast'; import {unparse} from './unparser'; diff --git a/modules/@angular/compiler/test/i18n/digest_spec.ts b/modules/@angular/compiler/test/i18n/digest_spec.ts index 08e45480a64c..b6c7c8055162 100644 --- a/modules/@angular/compiler/test/i18n/digest_spec.ts +++ b/modules/@angular/compiler/test/i18n/digest_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; +import {describe, expect, it} from '@angular/core/testing/testing_internal'; import {sha1} from '../../src/i18n/digest'; diff --git a/modules/@angular/compiler/test/i18n/extractor_merger_spec.ts b/modules/@angular/compiler/test/i18n/extractor_merger_spec.ts index fc51c963a239..dcb36154c78f 100644 --- a/modules/@angular/compiler/test/i18n/extractor_merger_spec.ts +++ b/modules/@angular/compiler/test/i18n/extractor_merger_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; +import {describe, expect, it} from '@angular/core/testing/testing_internal'; import {digestMessage, serializeNodes as serializeI18nNodes} from '../../src/i18n/digest'; import {extractMessages, mergeTranslations} from '../../src/i18n/extractor_merger'; diff --git a/modules/@angular/compiler/test/i18n/i18n_parser_spec.ts b/modules/@angular/compiler/test/i18n/i18n_parser_spec.ts index 7a0063ed3521..1c6ae8940358 100644 --- a/modules/@angular/compiler/test/i18n/i18n_parser_spec.ts +++ b/modules/@angular/compiler/test/i18n/i18n_parser_spec.ts @@ -8,7 +8,7 @@ import {extractMessages} from '@angular/compiler/src/i18n/extractor_merger'; import {Message} from '@angular/compiler/src/i18n/i18n_ast'; -import {ddescribe, describe, expect, iit, it} from '@angular/core/testing/testing_internal'; +import {describe, expect, it} from '@angular/core/testing/testing_internal'; import {serializeNodes} from '../../src/i18n/digest'; import {HtmlParser} from '../../src/ml_parser/html_parser'; diff --git a/modules/@angular/compiler/test/i18n/message_bundle_spec.ts b/modules/@angular/compiler/test/i18n/message_bundle_spec.ts index 2848bda17c28..c47c4e0bc426 100644 --- a/modules/@angular/compiler/test/i18n/message_bundle_spec.ts +++ b/modules/@angular/compiler/test/i18n/message_bundle_spec.ts @@ -8,7 +8,7 @@ import * as i18n from '@angular/compiler/src/i18n/i18n_ast'; import {Serializer} from '@angular/compiler/src/i18n/serializers/serializer'; -import {beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; +import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal'; import {serializeNodes} from '../../src/i18n/digest'; import {MessageBundle} from '../../src/i18n/message_bundle'; diff --git a/modules/@angular/compiler/test/i18n/serializers/placeholder_spec.ts b/modules/@angular/compiler/test/i18n/serializers/placeholder_spec.ts index 3d24f7a3d790..ce6e3b181147 100644 --- a/modules/@angular/compiler/test/i18n/serializers/placeholder_spec.ts +++ b/modules/@angular/compiler/test/i18n/serializers/placeholder_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; +import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal'; import {PlaceholderRegistry} from '../../../src/i18n/serializers/placeholder'; diff --git a/modules/@angular/compiler/test/i18n/serializers/xliff_spec.ts b/modules/@angular/compiler/test/i18n/serializers/xliff_spec.ts index 93457db3f02c..6a6b9a71cb08 100644 --- a/modules/@angular/compiler/test/i18n/serializers/xliff_spec.ts +++ b/modules/@angular/compiler/test/i18n/serializers/xliff_spec.ts @@ -7,7 +7,7 @@ */ import {Xliff} from '@angular/compiler/src/i18n/serializers/xliff'; -import {beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; +import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal'; import {MessageBundle} from '../../../src/i18n/message_bundle'; import {HtmlParser} from '../../../src/ml_parser/html_parser'; import {DEFAULT_INTERPOLATION_CONFIG} from '../../../src/ml_parser/interpolation_config'; @@ -17,51 +17,64 @@ const HTML = `

not translatable

translatable element with placeholders {{ interpolation}}

foo

+


`; const WRITE_XLIFF = ` - - - - translatable attribute - - - - translatable element with placeholders - - - - foo - - d - m - - - -`; + + + + translatable attribute + + + + translatable element with placeholders + + + + foo + + d + m + + + + + ph names + + + + +`; const LOAD_XLIFF = ` - - - - translatable attribute - etubirtta elbatalsnart - - - translatable element with placeholders - footnemele elbatalsnart sredlohecalp htiw - - - foo - oof - d - m - - - -`; + + + + translatable attribute + etubirtta elbatalsnart + + + translatable element with placeholders + footnemele elbatalsnart sredlohecalp htiw + + + foo + oof + d + m + + + + + ph names + + + + +`; export function main(): void { let serializer: Xliff; @@ -103,6 +116,7 @@ export function main(): void { 'ec1d033f2436133c14ab038286c4f5df4697484a': '{{ interpolation}} footnemele elbatalsnart sredlohecalp htiw', 'db3e0a6a5a96481f60aec61d98c3eecddef5ac23': 'oof', + 'd7fa2d59aaedcaa5309f13028c59af8c85b8c49d': '

', }); }); }); diff --git a/modules/@angular/compiler/test/i18n/serializers/xmb_spec.ts b/modules/@angular/compiler/test/i18n/serializers/xmb_spec.ts index 817351737c7d..89a5daa75ca9 100644 --- a/modules/@angular/compiler/test/i18n/serializers/xmb_spec.ts +++ b/modules/@angular/compiler/test/i18n/serializers/xmb_spec.ts @@ -6,12 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ +import {MessageBundle} from '@angular/compiler/src/i18n/message_bundle'; import {Xmb} from '@angular/compiler/src/i18n/serializers/xmb'; -import {beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; - -import {MessageBundle} from '../../../src/i18n/message_bundle'; -import {HtmlParser} from '../../../src/ml_parser/html_parser'; -import {DEFAULT_INTERPOLATION_CONFIG} from '../../../src/ml_parser/interpolation_config'; +import {HtmlParser} from '@angular/compiler/src/ml_parser/html_parser'; +import {DEFAULT_INTERPOLATION_CONFIG} from '@angular/compiler/src/ml_parser/interpolation_config'; export function main(): void { describe('XMB serializer', () => { @@ -49,7 +47,8 @@ export function main(): void { { count, plural, =0 {<p>test</p>} } foo { count, plural, =0 {{ sex, gender, other {<p>deeply nested</p>} } } } -`; + +`; it('should write a valid xmb file', () => { expect(toXmb(HTML)).toEqual(XMB); }); diff --git a/modules/@angular/compiler/test/i18n/serializers/xml_helper_spec.ts b/modules/@angular/compiler/test/i18n/serializers/xml_helper_spec.ts index 98972f05cf1e..8bcd7e3d302a 100644 --- a/modules/@angular/compiler/test/i18n/serializers/xml_helper_spec.ts +++ b/modules/@angular/compiler/test/i18n/serializers/xml_helper_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; +import {describe, expect, it} from '@angular/core/testing/testing_internal'; import * as xml from '../../../src/i18n/serializers/xml_helper'; diff --git a/modules/@angular/compiler/test/i18n/serializers/xtb_spec.ts b/modules/@angular/compiler/test/i18n/serializers/xtb_spec.ts index 6511a3c2b36b..f096b0863dd8 100644 --- a/modules/@angular/compiler/test/i18n/serializers/xtb_spec.ts +++ b/modules/@angular/compiler/test/i18n/serializers/xtb_spec.ts @@ -7,7 +7,7 @@ */ import {escapeRegExp} from '@angular/core/src/facade/lang'; -import {beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; +import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal'; import {MessageBundle} from '../../../src/i18n/message_bundle'; import {Xtb} from '../../../src/i18n/serializers/xtb'; diff --git a/modules/@angular/compiler/test/ml_parser/ast_serializer_spec.ts b/modules/@angular/compiler/test/ml_parser/ast_serializer_spec.ts index 4e5536f1da09..129e2e76584e 100644 --- a/modules/@angular/compiler/test/ml_parser/ast_serializer_spec.ts +++ b/modules/@angular/compiler/test/ml_parser/ast_serializer_spec.ts @@ -6,9 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ -import {beforeEach, ddescribe, describe, expect, it} from '../../../core/testing/testing_internal'; -import * as html from '../../src/ml_parser/ast'; -import {HtmlParser} from '../../src/ml_parser/html_parser'; +import * as html from '@angular/compiler/src/ml_parser/ast'; +import {HtmlParser} from '@angular/compiler/src/ml_parser/html_parser'; +import {getHtmlTagDefinition} from '@angular/compiler/src/ml_parser/html_tags'; export function main() { describe('Node serializer', () => { @@ -62,6 +62,10 @@ export function main() { class _SerializerVisitor implements html.Visitor { visitElement(element: html.Element, context: any): any { + if (getHtmlTagDefinition(element.name).isVoid) { + return `<${element.name}${this._visitAll(element.attrs, ' ')}/>`; + } + return `<${element.name}${this._visitAll(element.attrs, ' ')}>${this._visitAll(element.children)}`; } diff --git a/modules/@angular/compiler/test/ml_parser/html_parser_spec.ts b/modules/@angular/compiler/test/ml_parser/html_parser_spec.ts index 49bb0e2ce981..f185c179df47 100644 --- a/modules/@angular/compiler/test/ml_parser/html_parser_spec.ts +++ b/modules/@angular/compiler/test/ml_parser/html_parser_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '../../../core/testing/testing_internal'; +import {beforeEach, describe, expect, it} from '../../../core/testing/testing_internal'; import * as html from '../../src/ml_parser/ast'; import {HtmlParser, ParseTreeResult, TreeError} from '../../src/ml_parser/html_parser'; import {TokenType} from '../../src/ml_parser/lexer'; diff --git a/modules/@angular/compiler/test/ml_parser/icu_ast_expander_spec.ts b/modules/@angular/compiler/test/ml_parser/icu_ast_expander_spec.ts index ee1126ccba29..e216462e3281 100644 --- a/modules/@angular/compiler/test/ml_parser/icu_ast_expander_spec.ts +++ b/modules/@angular/compiler/test/ml_parser/icu_ast_expander_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ddescribe, describe, expect, iit, it} from '../../../core/testing/testing_internal'; +import {describe, expect, it} from '../../../core/testing/testing_internal'; import * as html from '../../src/ml_parser/ast'; import {HtmlParser} from '../../src/ml_parser/html_parser'; import {ExpansionResult, expandNodes} from '../../src/ml_parser/icu_ast_expander'; diff --git a/modules/@angular/compiler/test/ml_parser/lexer_spec.ts b/modules/@angular/compiler/test/ml_parser/lexer_spec.ts index c0910886bd4b..3ca4ced01358 100644 --- a/modules/@angular/compiler/test/ml_parser/lexer_spec.ts +++ b/modules/@angular/compiler/test/ml_parser/lexer_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '../../../core/testing/testing_internal'; +import {describe, expect, it} from '../../../core/testing/testing_internal'; import {getHtmlTagDefinition} from '../../src/ml_parser/html_tags'; import {InterpolationConfig} from '../../src/ml_parser/interpolation_config'; import * as lex from '../../src/ml_parser/lexer'; diff --git a/modules/@angular/compiler/test/ng_module_resolver_mock_spec.ts b/modules/@angular/compiler/test/ng_module_resolver_mock_spec.ts index eb409c350a89..6ad7df44d96b 100644 --- a/modules/@angular/compiler/test/ng_module_resolver_mock_spec.ts +++ b/modules/@angular/compiler/test/ng_module_resolver_mock_spec.ts @@ -7,9 +7,7 @@ */ import {Injector, NgModule} from '@angular/core'; -import {beforeEach, ddescribe, describe, expect, iit, inject, it} from '@angular/core/testing/testing_internal'; - -import {isBlank, stringify} from '../src/facade/lang'; +import {beforeEach, describe, expect, inject, it} from '@angular/core/testing/testing_internal'; import {MockNgModuleResolver} from '../testing/index'; export function main() { diff --git a/modules/@angular/compiler/test/output/abstract_emitter_spec.ts b/modules/@angular/compiler/test/output/abstract_emitter_spec.ts index d5b885e4b1c5..008c26a5f0ba 100644 --- a/modules/@angular/compiler/test/output/abstract_emitter_spec.ts +++ b/modules/@angular/compiler/test/output/abstract_emitter_spec.ts @@ -7,7 +7,7 @@ */ import {escapeIdentifier} from '@angular/compiler/src/output/abstract_emitter'; -import {beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; +import {describe, expect, it} from '@angular/core/testing/testing_internal'; export function main() { describe('AbstractEmitter', () => { diff --git a/modules/@angular/compiler/test/output/js_emitter_spec.ts b/modules/@angular/compiler/test/output/js_emitter_spec.ts index 77f0a28b700f..387292a2fbef 100644 --- a/modules/@angular/compiler/test/output/js_emitter_spec.ts +++ b/modules/@angular/compiler/test/output/js_emitter_spec.ts @@ -9,9 +9,7 @@ import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata'; import {JavaScriptEmitter} from '@angular/compiler/src/output/js_emitter'; import * as o from '@angular/compiler/src/output/output_ast'; -import {beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; - -import {isBlank} from '../../src/facade/lang'; +import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal'; import {SimpleJsImportGenerator} from './output_emitter_util'; @@ -39,7 +37,7 @@ export function main() { }); function emitStmt(stmt: o.Statement, exportedVars: string[] = null): string { - if (isBlank(exportedVars)) { + if (!exportedVars) { exportedVars = []; } return emitter.emitStatements(someModuleUrl, [stmt], exportedVars); diff --git a/modules/@angular/compiler/test/output/output_emitter_codegen_typed.ts b/modules/@angular/compiler/test/output/output_emitter_codegen_typed.ts index 40512a07725e..d614cc5160f3 100644 --- a/modules/@angular/compiler/test/output/output_emitter_codegen_typed.ts +++ b/modules/@angular/compiler/test/output/output_emitter_codegen_typed.ts @@ -7,7 +7,7 @@ */ // ATTENTION: This file will be overwritten with generated code by main() -import * as o from '@angular/compiler/src/output/output_ast'; + import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter'; import {print} from '../../src/facade/lang'; diff --git a/modules/@angular/compiler/test/output/output_emitter_spec.ts b/modules/@angular/compiler/test/output/output_emitter_spec.ts index 186f1dc32fa0..72ce118ebaff 100644 --- a/modules/@angular/compiler/test/output/output_emitter_spec.ts +++ b/modules/@angular/compiler/test/output/output_emitter_spec.ts @@ -10,7 +10,7 @@ import {interpretStatements} from '@angular/compiler/src/output/output_interpret import {jitStatements} from '@angular/compiler/src/output/output_jit'; import {EventEmitter} from '@angular/core'; import {ViewType} from '@angular/core/src/linker/view_type'; -import {beforeEach, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; +import {beforeEach, describe, it} from '@angular/core/testing/testing_internal'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {browserDetection} from '@angular/platform-browser/testing/browser_util'; import {expect} from '@angular/platform-browser/testing/matchers'; diff --git a/modules/@angular/compiler/test/output/ts_emitter_spec.ts b/modules/@angular/compiler/test/output/ts_emitter_spec.ts index 562c8dd10713..e60c3205bb0a 100644 --- a/modules/@angular/compiler/test/output/ts_emitter_spec.ts +++ b/modules/@angular/compiler/test/output/ts_emitter_spec.ts @@ -9,9 +9,7 @@ import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata'; import * as o from '@angular/compiler/src/output/output_ast'; import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter'; -import {beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; - -import {isBlank} from '../../src/facade/lang'; +import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal'; import {SimpleJsImportGenerator} from './output_emitter_util'; @@ -39,7 +37,7 @@ export function main() { }); function emitStmt(stmt: o.Statement, exportedVars: string[] = null): string { - if (isBlank(exportedVars)) { + if (!exportedVars) { exportedVars = []; } return emitter.emitStatements(someModuleUrl, [stmt], exportedVars); diff --git a/modules/@angular/compiler/test/pipe_resolver_mock_spec.ts b/modules/@angular/compiler/test/pipe_resolver_mock_spec.ts index 272767d98c84..4c5b51d77ce3 100644 --- a/modules/@angular/compiler/test/pipe_resolver_mock_spec.ts +++ b/modules/@angular/compiler/test/pipe_resolver_mock_spec.ts @@ -7,9 +7,7 @@ */ import {Injector, Pipe} from '@angular/core'; -import {beforeEach, ddescribe, describe, expect, iit, inject, it} from '@angular/core/testing/testing_internal'; - -import {isBlank, stringify} from '../src/facade/lang'; +import {beforeEach, describe, expect, inject, it} from '@angular/core/testing/testing_internal'; import {MockPipeResolver} from '../testing/index'; export function main() { diff --git a/modules/@angular/compiler/test/resource_loader_mock_spec.ts b/modules/@angular/compiler/test/resource_loader_mock_spec.ts index 9c22276e07b0..cda7fde65441 100644 --- a/modules/@angular/compiler/test/resource_loader_mock_spec.ts +++ b/modules/@angular/compiler/test/resource_loader_mock_spec.ts @@ -7,7 +7,7 @@ */ import {MockResourceLoader} from '@angular/compiler/testing/resource_loader_mock'; -import {AsyncTestCompleter, beforeEach, ddescribe, describe, expect, iit, inject, it} from '@angular/core/testing/testing_internal'; +import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@angular/core/testing/testing_internal'; import {isPresent} from '../src/facade/lang'; export function main() { diff --git a/modules/@angular/compiler/test/runtime_compiler_spec.ts b/modules/@angular/compiler/test/runtime_compiler_spec.ts index fc6bfe60e2a2..ca161ee9280b 100644 --- a/modules/@angular/compiler/test/runtime_compiler_spec.ts +++ b/modules/@angular/compiler/test/runtime_compiler_spec.ts @@ -7,9 +7,9 @@ */ import {DirectiveResolver, ResourceLoader} from '@angular/compiler'; -import {Compiler, Component, ComponentFactory, Injectable, Injector, Input, NgModule, NgModuleFactory, Type} from '@angular/core'; -import {ComponentFixture, TestBed, async, fakeAsync, getTestBed, tick} from '@angular/core/testing'; -import {beforeEach, beforeEachProviders, ddescribe, describe, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; +import {Compiler, Component, Injector, NgModule, NgModuleFactory} from '@angular/core'; +import {TestBed, async, fakeAsync, tick} from '@angular/core/testing'; +import {beforeEach, describe, inject, it} from '@angular/core/testing/testing_internal'; import {expect} from '@angular/platform-browser/testing/matchers'; import {stringify} from '../src/facade/lang'; diff --git a/modules/@angular/compiler/test/schema/dom_element_schema_registry_spec.ts b/modules/@angular/compiler/test/schema/dom_element_schema_registry_spec.ts index 4e4ecbce43bb..dee9bd9b4078 100644 --- a/modules/@angular/compiler/test/schema/dom_element_schema_registry_spec.ts +++ b/modules/@angular/compiler/test/schema/dom_element_schema_registry_spec.ts @@ -8,7 +8,7 @@ import {DomElementSchemaRegistry} from '@angular/compiler/src/schema/dom_element_schema_registry'; import {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, SecurityContext} from '@angular/core'; -import {beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; +import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal'; import {browserDetection} from '@angular/platform-browser/testing/browser_util'; import {Element} from '../../src/ml_parser/ast'; @@ -105,6 +105,47 @@ export function main() { expect(registry.getMappedPropName('exotic-unknown')).toEqual('exotic-unknown'); }); + it('should return an error message when asserting event properties', () => { + let report = registry.validateProperty('onClick'); + expect(report.error).toBeTruthy(); + expect(report.msg) + .toEqual( + `Binding to event property 'onClick' is disallowed for security reasons, please use (Click)=... +If 'onClick' is a directive input, make sure the directive is imported by the current module.`); + + report = registry.validateProperty('onAnything'); + expect(report.error).toBeTruthy(); + expect(report.msg) + .toEqual( + `Binding to event property 'onAnything' is disallowed for security reasons, please use (Anything)=... +If 'onAnything' is a directive input, make sure the directive is imported by the current module.`); + }); + + it('should return an error message when asserting event attributes', () => { + let report = registry.validateAttribute('onClick'); + expect(report.error).toBeTruthy(); + expect(report.msg) + .toEqual( + `Binding to event attribute 'onClick' is disallowed for security reasons, please use (Click)=...`); + + report = registry.validateAttribute('onAnything'); + expect(report.error).toBeTruthy(); + expect(report.msg) + .toEqual( + `Binding to event attribute 'onAnything' is disallowed for security reasons, please use (Anything)=...`); + }); + + it('should not return an error message when asserting non-event properties or attributes', + () => { + let report = registry.validateProperty('title'); + expect(report.error).toBeFalsy(); + expect(report.msg).not.toBeDefined(); + + report = registry.validateProperty('exotic-unknown'); + expect(report.error).toBeFalsy(); + expect(report.msg).not.toBeDefined(); + }); + it('should return security contexts for elements', () => { expect(registry.securityContext('iframe', 'srcdoc')).toBe(SecurityContext.HTML); expect(registry.securityContext('p', 'innerHTML')).toBe(SecurityContext.HTML); diff --git a/modules/@angular/compiler/test/shadow_css_spec.ts b/modules/@angular/compiler/test/shadow_css_spec.ts index 4298e7a18b0e..6afb87de01da 100644 --- a/modules/@angular/compiler/test/shadow_css_spec.ts +++ b/modules/@angular/compiler/test/shadow_css_spec.ts @@ -7,7 +7,6 @@ */ import {CssRule, ShadowCss, processRules} from '@angular/compiler/src/shadow_css'; -import {beforeEach, ddescribe, describe, expect, iit, it} from '@angular/core/testing/testing_internal'; import {normalizeCSS} from '@angular/platform-browser/testing/browser_util'; export function main() { @@ -53,6 +52,18 @@ export function main() { expect(s(css, 'a')).toEqual(expected); }); + it('should handle page rules', () => { + const css = '@page {div {font-size:50px;}}'; + const expected = '@page {div[a] {font-size:50px;}}'; + expect(s(css, 'a')).toEqual(expected); + }); + + it('should handle document rules', () => { + const css = '@document url(http://www.w3.org/) {div {font-size:50px;}}'; + const expected = '@document url(http://www.w3.org/) {div[a] {font-size:50px;}}'; + expect(s(css, 'a')).toEqual(expected); + }); + it('should handle media rules with simple rules', () => { const css = '@media screen and (max-width: 800px) {div {font-size: 50px;}} div {}'; const expected = '@media screen and (max-width:800px) {div[a] {font-size:50px;}} div[a] {}'; @@ -92,23 +103,66 @@ export function main() { expect(s('one[attr*="value"] {}', 'a')).toEqual('one[attr*="value"][a] {}'); expect(s('one[attr|="value"] {}', 'a')).toEqual('one[attr|="value"][a] {}'); expect(s('one[attr~="value"] {}', 'a')).toEqual('one[attr~="value"][a] {}'); + expect(s('one[attr="va lue"] {}', 'a')).toEqual('one[attr="va lue"][a] {}'); expect(s('one[attr] {}', 'a')).toEqual('one[attr][a] {}'); expect(s('[is="one"] {}', 'a')).toEqual('[is="one"][a] {}'); }); - it('should handle :host', () => { - expect(s(':host {}', 'a', 'a-host')).toEqual('[a-host] {}'); + describe((':host'), () => { + it('should handle no context', + () => { expect(s(':host {}', 'a', 'a-host')).toEqual('[a-host] {}'); }); + + it('should handle tag selector', () => { + expect(s(':host(ul) {}', 'a', 'a-host')).toEqual('ul[a-host] {}'); - expect(s(':host(.x,.y) {}', 'a', 'a-host')).toEqual('[a-host].x, [a-host].y {}'); + }); + + it('should handle class selector', + () => { expect(s(':host(.x) {}', 'a', 'a-host')).toEqual('.x[a-host] {}'); }); + + it('should handle attribute selector', () => { + expect(s(':host([a="b"]) {}', 'a', 'a-host')).toEqual('[a="b"][a-host] {}'); + expect(s(':host([a=b]) {}', 'a', 'a-host')).toEqual('[a="b"][a-host] {}'); + }); + + it('should handle multiple tag selectors', () => { + expect(s(':host(ul,li) {}', 'a', 'a-host')).toEqual('ul[a-host], li[a-host] {}'); + expect(s(':host(ul,li) > .z {}', 'a', 'a-host')) + .toEqual('ul[a-host] > .z[a], li[a-host] > .z[a] {}'); + }); - expect(s(':host(.x,.y) > .z {}', 'a', 'a-host')) - .toEqual('[a-host].x > .z[a], [a-host].y > .z[a] {}'); + it('should handle multiple class selectors', () => { + expect(s(':host(.x,.y) {}', 'a', 'a-host')).toEqual('.x[a-host], .y[a-host] {}'); + expect(s(':host(.x,.y) > .z {}', 'a', 'a-host')) + .toEqual('.x[a-host] > .z[a], .y[a-host] > .z[a] {}'); + }); + + it('should handle multiple attribute selectors', () => { + expect(s(':host([a="b"],[c=d]) {}', 'a', 'a-host')) + .toEqual('[a="b"][a-host], [c="d"][a-host] {}'); + }); }); - it('should handle :host-context', () => { - expect(s(':host-context(.x) {}', 'a', 'a-host')).toEqual('[a-host].x, .x [a-host] {}'); - expect(s(':host-context(.x) > .y {}', 'a', 'a-host')) - .toEqual('[a-host].x > .y[a], .x [a-host] > .y[a] {}'); + describe((':host-context'), () => { + it('should handle tag selector', () => { + expect(s(':host-context(div) {}', 'a', 'a-host')).toEqual('div[a-host], div [a-host] {}'); + expect(s(':host-context(ul) > .y {}', 'a', 'a-host')) + .toEqual('ul[a-host] > .y[a], ul [a-host] > .y[a] {}'); + }); + + it('should handle class selector', () => { + expect(s(':host-context(.x) {}', 'a', 'a-host')).toEqual('.x[a-host], .x [a-host] {}'); + + expect(s(':host-context(.x) > .y {}', 'a', 'a-host')) + .toEqual('.x[a-host] > .y[a], .x [a-host] > .y[a] {}'); + }); + + it('should handle attribute selector', () => { + expect(s(':host-context([a="b"]) {}', 'a', 'a-host')) + .toEqual('[a="b"][a-host], [a="b"] [a-host] {}'); + expect(s(':host-context([a=b]) {}', 'a', 'a-host')) + .toEqual('[a=b][a-host], [a="b"] [a-host] {}'); + }); }); it('should support polyfill-next-selector', () => { @@ -117,6 +171,9 @@ export function main() { css = s('polyfill-next-selector {content: "x > y"} z {}', 'a'); expect(css).toEqual('x[a] > y[a]{}'); + + css = s(`polyfill-next-selector {content: 'button[priority="1"]'} z {}`, 'a'); + expect(css).toEqual('button[priority="1"][a]{}'); }); it('should support polyfill-unscoped-rule', () => { @@ -125,6 +182,9 @@ export function main() { css = s('polyfill-unscoped-rule {content: "#menu > .bar";color: blue;}', 'a'); expect(css).toContain('#menu > .bar {;color:blue;}'); + + css = s(`polyfill-unscoped-rule {content: 'button[priority="1"]'}`, 'a'); + expect(css).toContain('button[priority="1"] {}'); }); it('should support multiple instances polyfill-unscoped-rule', () => { @@ -138,10 +198,13 @@ export function main() { it('should support polyfill-rule', () => { let css = s('polyfill-rule {content: \':host.foo .bar\';color: blue;}', 'a', 'a-host'); - expect(css).toEqual('[a-host].foo .bar[a] {;color:blue;}'); + expect(css).toEqual('.foo[a-host] .bar[a] {;color:blue;}'); css = s('polyfill-rule {content: ":host.foo .bar";color:blue;}', 'a', 'a-host'); - expect(css).toEqual('[a-host].foo .bar[a] {;color:blue;}'); + expect(css).toEqual('.foo[a-host] .bar[a] {;color:blue;}'); + + css = s(`polyfill-rule {content: 'button[priority="1"]'}`, 'a', 'a-host'); + expect(css).toEqual('button[priority="1"][a] {}'); }); it('should handle ::shadow', () => { @@ -196,8 +259,8 @@ export function main() { describe('processRules', () => { describe('parse rules', () => { function captureRules(input: string): CssRule[] { - const result: any[] /** TODO #9100 */ = []; - processRules(input, (cssRule: any /** TODO #9100 */) => { + const result: CssRule[] = []; + processRules(input, (cssRule) => { result.push(cssRule); return cssRule; }); @@ -228,15 +291,15 @@ export function main() { describe('modify rules', () => { it('should allow to change the selector while preserving whitespaces', () => { expect(processRules( - '@import a; b {c {d}} e {f}', (cssRule: any /** TODO #9100 */) => new CssRule( - cssRule.selector + '2', cssRule.content))) + '@import a; b {c {d}} e {f}', + (cssRule: CssRule) => new CssRule(cssRule.selector + '2', cssRule.content))) .toEqual('@import a2; b2 {c {d}} e2 {f}'); }); it('should allow to change the content', () => { expect(processRules( - 'a {b}', (cssRule: any /** TODO #9100 */) => - new CssRule(cssRule.selector, cssRule.content + '2'))) + 'a {b}', + (cssRule: CssRule) => new CssRule(cssRule.selector, cssRule.content + '2'))) .toEqual('a {b2}'); }); }); diff --git a/modules/@angular/compiler/test/spies.ts b/modules/@angular/compiler/test/spies.ts index 88d92fb263fa..118808a2c93b 100644 --- a/modules/@angular/compiler/test/spies.ts +++ b/modules/@angular/compiler/test/spies.ts @@ -8,7 +8,7 @@ import {ResourceLoader} from '@angular/compiler/src/resource_loader'; -import {SpyObject, proxy} from '@angular/core/testing/testing_internal'; +import {SpyObject} from '@angular/core/testing/testing_internal'; export class SpyResourceLoader extends SpyObject { constructor() { super(ResourceLoader); } diff --git a/modules/@angular/compiler/test/template_parser/template_parser_spec.ts b/modules/@angular/compiler/test/template_parser/template_parser_spec.ts index 00a4842d46c0..f3786927888e 100644 --- a/modules/@angular/compiler/test/template_parser/template_parser_spec.ts +++ b/modules/@angular/compiler/test/template_parser/template_parser_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompilePipeMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTemplateMetadata, CompileTokenMetadata, CompileTypeMetadata} from '@angular/compiler/src/compile_metadata'; +import {CompileAnimationEntryMetadata, CompileDiDependencyMetadata, CompileDirectiveMetadata, CompilePipeMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTemplateMetadata, CompileTokenMetadata, CompileTypeMetadata} from '@angular/compiler/src/compile_metadata'; import {DomElementSchemaRegistry} from '@angular/compiler/src/schema/dom_element_schema_registry'; import {ElementSchemaRegistry} from '@angular/compiler/src/schema/element_schema_registry'; import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ProviderAstType, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '@angular/compiler/src/template_parser/template_ast'; @@ -15,8 +15,7 @@ import {TEST_COMPILER_PROVIDERS} from '@angular/compiler/testing/test_bindings'; import {SchemaMetadata, SecurityContext, Type} from '@angular/core'; import {Console} from '@angular/core/src/console'; import {TestBed} from '@angular/core/testing'; -import {afterEach, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; - +import {beforeEach, describe, expect, inject, it} from '@angular/core/testing/testing_internal'; import {Identifiers, identifierToken, resolveIdentifierToken} from '../../src/identifiers'; import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../../src/ml_parser/interpolation_config'; import {MockSchemaRegistry} from '../../testing/index'; @@ -27,14 +26,15 @@ const someModuleUrl = 'package:someModule'; const MOCK_SCHEMA_REGISTRY = [{ provide: ElementSchemaRegistry, useValue: new MockSchemaRegistry( - {'invalidProp': false}, {'mappedAttr': 'mappedProp'}, {'unknown': false, 'un-known': false}), + {'invalidProp': false}, {'mappedAttr': 'mappedProp'}, {'unknown': false, 'un-known': false}, + ['onEvent'], ['onEvent']), }]; export function main() { var ngIf: CompileDirectiveMetadata; - var parse: - (template: string, directives: CompileDirectiveMetadata[], pipes?: CompilePipeMetadata[]) => - TemplateAst[]; + var parse: ( + template: string, directives: CompileDirectiveMetadata[], pipes?: CompilePipeMetadata[], + schemas?: SchemaMetadata[]) => TemplateAst[]; var console: ArrayConsole; function commonBeforeEach() { @@ -43,14 +43,18 @@ export function main() { TestBed.configureCompiler({providers: [{provide: Console, useValue: console}]}); }); beforeEach(inject([TemplateParser], (parser: TemplateParser) => { + var someAnimation = new CompileAnimationEntryMetadata('someAnimation', []); + var someTemplate = new CompileTemplateMetadata({animations: [someAnimation]}); var component = CompileDirectiveMetadata.create({ selector: 'root', + template: someTemplate, type: new CompileTypeMetadata( {moduleUrl: someModuleUrl, name: 'Root', reference: {} as Type}), isComponent: true }); ngIf = CompileDirectiveMetadata.create({ selector: '[ngIf]', + template: someTemplate, type: new CompileTypeMetadata( {moduleUrl: someModuleUrl, name: 'NgIf', reference: {} as Type}), inputs: ['ngIf'] @@ -138,1487 +142,1529 @@ export function main() { }); }); - describe('TemplateParser', () => { - beforeEach(() => { - TestBed.configureCompiler({providers: [TEST_COMPILER_PROVIDERS, MOCK_SCHEMA_REGISTRY]}); - }); + describe( + 'TemplateParser', () => { + beforeEach(() => { + TestBed.configureCompiler({providers: [TEST_COMPILER_PROVIDERS, MOCK_SCHEMA_REGISTRY]}); + }); - commonBeforeEach(); + commonBeforeEach(); - describe('parse', () => { - describe('nodes without bindings', () => { + describe('parse', () => { + describe('nodes without bindings', () => { - it('should parse text nodes', () => { - expect(humanizeTplAst(parse('a', []))).toEqual([[TextAst, 'a']]); - }); + it('should parse text nodes', () => { + expect(humanizeTplAst(parse('a', []))).toEqual([[TextAst, 'a']]); + }); - it('should parse elements with attributes', () => { - expect(humanizeTplAst(parse('
', [ - ]))).toEqual([[ElementAst, 'div'], [AttrAst, 'a', 'b']]); - }); - }); + it('should parse elements with attributes', () => { + expect(humanizeTplAst(parse('
', [ + ]))).toEqual([[ElementAst, 'div'], [AttrAst, 'a', 'b']]); + }); + }); - it('should parse ngContent', () => { - var parsed = parse('', []); - expect(humanizeTplAst(parsed)).toEqual([[NgContentAst]]); - }); + it('should parse ngContent', () => { + var parsed = parse('', []); + expect(humanizeTplAst(parsed)).toEqual([[NgContentAst]]); + }); - it('should parse ngContent regardless the namespace', () => { - var parsed = parse('', []); - expect(humanizeTplAst(parsed)).toEqual([ - [ElementAst, ':svg:svg'], - [NgContentAst], - ]); - }); + it('should parse ngContent regardless the namespace', () => { + var parsed = parse('', []); + expect(humanizeTplAst(parsed)).toEqual([ + [ElementAst, ':svg:svg'], + [NgContentAst], + ]); + }); - it('should parse bound text nodes', () => { - expect(humanizeTplAst(parse('{{a}}', []))).toEqual([[BoundTextAst, '{{ a }}']]); - }); + it('should parse bound text nodes', () => { + expect(humanizeTplAst(parse('{{a}}', []))).toEqual([[BoundTextAst, '{{ a }}']]); + }); - it('should parse with custom interpolation config', - inject([TemplateParser], (parser: TemplateParser) => { - const component = CompileDirectiveMetadata.create({ - selector: 'test', - type: new CompileTypeMetadata( - {moduleUrl: someModuleUrl, name: 'Test', reference: {} as Type}), - isComponent: true, - template: new CompileTemplateMetadata({interpolation: ['{%', '%}']}) - }); - expect(humanizeTplAst(parser.parse(component, '{%a%}', [], [], [], 'TestComp'), { - start: '{%', - end: '%}' - })).toEqual([[BoundTextAst, '{% a %}']]); - })); - - describe('bound properties', () => { - - it('should parse mixed case bound properties', () => { - expect(humanizeTplAst(parse('
', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Property, 'someProp', 'v', null] - ]); - }); + it('should parse with custom interpolation config', + inject([TemplateParser], (parser: TemplateParser) => { + const component = CompileDirectiveMetadata.create({ + selector: 'test', + type: new CompileTypeMetadata( + {moduleUrl: someModuleUrl, name: 'Test', reference: {} as Type}), + isComponent: true, + template: new CompileTemplateMetadata({interpolation: ['{%', '%}']}) + }); + expect(humanizeTplAst(parser.parse(component, '{%a%}', [], [], [], 'TestComp'), { + start: '{%', + end: '%}' + })).toEqual([[BoundTextAst, '{% a %}']]); + })); + + describe('bound properties', () => { + + it('should parse mixed case bound properties', () => { + expect(humanizeTplAst(parse('
', []))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Property, 'someProp', 'v', null] + ]); + }); - it('should parse dash case bound properties', () => { - expect(humanizeTplAst(parse('
', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Property, 'some-prop', 'v', null] - ]); - }); + it('should parse dash case bound properties', () => { + expect(humanizeTplAst(parse('
', []))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Property, 'some-prop', 'v', null] + ]); + }); - it('should normalize property names via the element schema', () => { - expect(humanizeTplAst(parse('
', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Property, 'mappedProp', 'v', null] - ]); - }); + it('should normalize property names via the element schema', () => { + expect(humanizeTplAst(parse('
', []))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Property, 'mappedProp', 'v', null] + ]); + }); - it('should parse mixed case bound attributes', () => { - expect(humanizeTplAst(parse('
', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Attribute, 'someAttr', 'v', null] - ]); - }); + it('should parse mixed case bound attributes', () => { + expect(humanizeTplAst(parse('
', []))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Attribute, 'someAttr', 'v', null] + ]); + }); - it('should parse and dash case bound classes', () => { - expect(humanizeTplAst(parse('
', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Class, 'some-class', 'v', null] - ]); - }); + it('should parse and dash case bound classes', () => { + expect(humanizeTplAst(parse('
', []))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Class, 'some-class', 'v', null] + ]); + }); - it('should parse mixed case bound classes', () => { - expect(humanizeTplAst(parse('
', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Class, 'someClass', 'v', null] - ]); - }); + it('should parse mixed case bound classes', () => { + expect(humanizeTplAst(parse('
', []))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Class, 'someClass', 'v', null] + ]); + }); - it('should parse mixed case bound styles', () => { - expect(humanizeTplAst(parse('
', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Style, 'someStyle', 'v', null] - ]); - }); + it('should parse mixed case bound styles', () => { + expect(humanizeTplAst(parse('
', []))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Style, 'someStyle', 'v', null] + ]); + }); - it('should report invalid prefixes', () => { - expect(() => parse('

', [])) - .toThrowError( - `Template parse errors:\nInvalid property name 'atTr.foo' ("

][atTr.foo]>"): TestComp@0:3`); - expect(() => parse('

', [])) - .toThrowError( - `Template parse errors:\nInvalid property name 'sTyle.foo' ("

][sTyle.foo]>"): TestComp@0:3`); - expect(() => parse('

', [])) - .toThrowError( - `Template parse errors:\nInvalid property name 'Class.foo' ("

][Class.foo]>"): TestComp@0:3`); - expect(() => parse('

', [])) - .toThrowError( - `Template parse errors:\nInvalid property name 'bar.foo' ("

][bar.foo]>"): TestComp@0:3`); - }); + it('should report invalid prefixes', () => { + expect(() => parse('

', [])) + .toThrowError( + `Template parse errors:\nInvalid property name 'atTr.foo' ("

][atTr.foo]>"): TestComp@0:3`); + expect(() => parse('

', [])) + .toThrowError( + `Template parse errors:\nInvalid property name 'sTyle.foo' ("

][sTyle.foo]>"): TestComp@0:3`); + expect(() => parse('

', [])) + .toThrowError( + `Template parse errors:\nInvalid property name 'Class.foo' ("

][Class.foo]>"): TestComp@0:3`); + expect(() => parse('

', [])) + .toThrowError( + `Template parse errors:\nInvalid property name 'bar.foo' ("

][bar.foo]>"): TestComp@0:3`); + }); - describe('errors', () => { - it('should throw error when binding to an unknown property', () => { - expect(() => parse('', [])) - .toThrowError(`Template parse errors: + describe('errors', () => { + it('should throw error when binding to an unknown property', () => { + expect(() => parse('', [])) + .toThrowError(`Template parse errors: Can't bind to 'invalidProp' since it isn't a known property of 'my-component'. 1. If 'my-component' is an Angular component and it has 'invalidProp' input, then verify that it is part of this module. 2. If 'my-component' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message. ("][invalidProp]="bar">"): TestComp@0:14`); - }); + }); - it('should throw error when binding to an unknown element w/o bindings', () => { - expect(() => parse('', [])).toThrowError(`Template parse errors: + it('should throw error when binding to an unknown element w/o bindings', () => { + expect(() => parse('', [])).toThrowError(`Template parse errors: 'unknown' is not a known element: 1. If 'unknown' is an Angular component, then verify that it is part of this module. 2. If 'unknown' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message. ("[ERROR ->]"): TestComp@0:0`); - }); + }); - it('should throw error when binding to an unknown custom element w/o bindings', () => { - expect(() => parse('', [])).toThrowError(`Template parse errors: + it('should throw error when binding to an unknown custom element w/o bindings', + () => { + expect(() => parse('', [])) + .toThrowError(`Template parse errors: 'un-known' is not a known element: 1. If 'un-known' is an Angular component, then verify that it is part of this module. 2. If 'un-known' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message. ("[ERROR ->]"): TestComp@0:0`); - }); - }); - - it('should parse bound properties via [...] and not report them as attributes', () => { - expect(humanizeTplAst(parse('

', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Property, 'prop', 'v', null] - ]); - }); + }); + + it('should throw error when binding to an invalid property', () => { + expect(() => parse('', [])) + .toThrowError(`Template parse errors: +Binding to property 'onEvent' is disallowed for security reasons ("][onEvent]="bar">"): TestComp@0:14`); + }); + + it('should throw error when binding to an invalid attribute', () => { + expect(() => parse('', [])) + .toThrowError(`Template parse errors: +Binding to attribute 'onEvent' is disallowed for security reasons ("][attr.onEvent]="bar">"): TestComp@0:14`); + }); + }); - it('should parse bound properties via bind- and not report them as attributes', () => { - expect(humanizeTplAst(parse('
', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Property, 'prop', 'v', null] - ]); - }); + it('should parse bound properties via [...] and not report them as attributes', () => { + expect(humanizeTplAst(parse('
', []))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Property, 'prop', 'v', null] + ]); + }); - it('should parse bound properties via {{...}} and not report them as attributes', () => { - expect(humanizeTplAst(parse('
', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Property, 'prop', '{{ v }}', null] - ]); - }); + it('should parse bound properties via bind- and not report them as attributes', () => { + expect(humanizeTplAst(parse('
', []))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Property, 'prop', 'v', null] + ]); + }); - it('should parse bound properties via bind-animate- and not report them as animation properties', - () => { - expect(humanizeTplAst(parse('
', []))).toEqual([ - [ElementAst, 'div'], - [ - BoundElementPropertyAst, PropertyBindingType.Animation, 'something', 'value2', null - ] - ]); - }); - - it('should throw an error when parsing detects non-bound properties via @ that contain a value', - () => { - expect(() => { parse('
', []); }) - .toThrowError( - /Assigning animation triggers via @prop="exp" attributes with an expression is invalid. Use property bindings \(e.g. \[@prop\]="exp"\) or use an attribute without a value \(e.g. @prop\) instead. \("
\]@something="value2">"\): TestComp@0:5/); - }); - - it('should not issue a warning when host attributes contain a valid property-bound animation trigger', - () => { - var dirA = CompileDirectiveMetadata.create({ - selector: 'div', - type: new CompileTypeMetadata( - {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}), - host: {'[@prop]': 'expr'} - }); + it('should parse bound properties via {{...}} and not report them as attributes', + () => { + expect(humanizeTplAst(parse('
', []))).toEqual([ + [ElementAst, 'div'], + [ + BoundElementPropertyAst, PropertyBindingType.Property, 'prop', '{{ v }}', null + ] + ]); + }); + + it('should parse bound properties via bind-animate- and not report them as attributes', + () => { + expect( + humanizeTplAst(parse('
', [], [], []))) + .toEqual([ + [ElementAst, 'div'], + [ + BoundElementPropertyAst, PropertyBindingType.Animation, 'someAnimation', + 'value2', null + ] + ]); + }); + + it('should throw an error when parsing detects non-bound properties via @ that contain a value', + () => { + expect(() => { parse('
', [], [], []); }) + .toThrowError( + /Assigning animation triggers via @prop="exp" attributes with an expression is invalid. Use property bindings \(e.g. \[@prop\]="exp"\) or use an attribute without a value \(e.g. @prop\) instead. \("
\]@someAnimation="value2">"\): TestComp@0:5/); + }); + + it('should not issue a warning when host attributes contain a valid property-bound animation trigger', + () => { + const animationEntries = [new CompileAnimationEntryMetadata('prop', [])]; + var dirA = CompileDirectiveMetadata.create({ + selector: 'div', + template: new CompileTemplateMetadata({animations: animationEntries}), + type: new CompileTypeMetadata( + {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}), + host: {'[@prop]': 'expr'} + }); + + humanizeTplAst(parse('
', [dirA])); + expect(console.warnings.length).toEqual(0); + }); + + it('should throw descriptive error when a host binding is not a string expression', () => { + var dirA = CompileDirectiveMetadata.create({ + selector: 'broken', + type: new CompileTypeMetadata( + {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}), + host: {'[class.foo]': null} + }); + + expect(() => { parse('', [dirA]); }) + .toThrowError( + `Template parse errors:\nValue of the host property binding "class.foo" needs to be a string representing an expression but got "null" (object) ("[ERROR ->]"): TestComp@0:0, Directive DirA`); + }); - humanizeTplAst(parse('
', [dirA])); - expect(console.warnings.length).toEqual(0); - }); + it('should throw descriptive error when a host event is not a string expression', () => { + var dirA = CompileDirectiveMetadata.create({ + selector: 'broken', + type: new CompileTypeMetadata( + {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}), + host: {'(click)': null} + }); + + expect(() => { parse('', [dirA]); }) + .toThrowError( + `Template parse errors:\nValue of the host listener "click" needs to be a string representing an expression but got "null" (object) ("[ERROR ->]"): TestComp@0:0, Directive DirA`); + }); - it('should throw descriptive error when a host binding is not a string expression', () => { - var dirA = CompileDirectiveMetadata.create({ - selector: 'broken', - type: new CompileTypeMetadata( - {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}), - host: {'[class.foo]': null} + it('should not issue a warning when an animation property is bound without an expression', + () => { + humanizeTplAst(parse('
', [], [], [])); + expect(console.warnings.length).toEqual(0); + }); + + it('should parse bound properties via [@] and not report them as attributes', () => { + expect(humanizeTplAst(parse('
', [], [], []))).toEqual([ + [ElementAst, 'div'], + [ + BoundElementPropertyAst, PropertyBindingType.Animation, 'someAnimation', 'value2', + null + ] + ]); + }); }); - expect(() => { parse('', [dirA]); }) - .toThrowError( - `Template parse errors:\nValue of the host property binding "class.foo" needs to be a string representing an expression but got "null" (object) ("[ERROR ->]"): TestComp@0:0, Directive DirA`); - }); + describe('events', () => { - it('should throw descriptive error when a host event is not a string expression', () => { - var dirA = CompileDirectiveMetadata.create({ - selector: 'broken', - type: new CompileTypeMetadata( - {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}), - host: {'(click)': null} - }); + it('should parse bound events with a target', () => { + expect(humanizeTplAst(parse('
', []))).toEqual([ + [ElementAst, 'div'], + [BoundEventAst, 'event', 'window', 'v'], + ]); + }); - expect(() => { parse('', [dirA]); }) - .toThrowError( - `Template parse errors:\nValue of the host listener "click" needs to be a string representing an expression but got "null" (object) ("[ERROR ->]"): TestComp@0:0, Directive DirA`); - }); + it('should report an error on empty expression', () => { + expect(() => parse('
', [])) + .toThrowError(/Empty expressions are not allowed/); - it('should not issue a warning when an animation property is bound without an expression', - () => { - humanizeTplAst(parse('
', [])); - expect(console.warnings.length).toEqual(0); - }); - - it('should parse bound properties via [@] and not report them as attributes', () => { - expect(humanizeTplAst(parse('
', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Animation, 'something', 'value2', null] - ]); - }); - }); + expect(() => parse('
', [])) + .toThrowError(/Empty expressions are not allowed/); + }); - describe('events', () => { + it('should parse bound events via (...) and not report them as attributes', () => { + expect(humanizeTplAst(parse('
', [ + ]))).toEqual([[ElementAst, 'div'], [BoundEventAst, 'event', null, 'v']]); + }); - it('should parse bound events with a target', () => { - expect(humanizeTplAst(parse('
', []))).toEqual([ - [ElementAst, 'div'], - [BoundEventAst, 'event', 'window', 'v'], - ]); - }); + it('should parse event names case sensitive', () => { + expect(humanizeTplAst(parse('
', [ + ]))).toEqual([[ElementAst, 'div'], [BoundEventAst, 'some-event', null, 'v']]); + expect(humanizeTplAst(parse('
', [ + ]))).toEqual([[ElementAst, 'div'], [BoundEventAst, 'someEvent', null, 'v']]); + }); - it('should report an error on empty expression', () => { - expect(() => parse('
', [])) - .toThrowError(/Empty expressions are not allowed/); + it('should parse bound events via on- and not report them as attributes', () => { + expect(humanizeTplAst(parse('
', [ + ]))).toEqual([[ElementAst, 'div'], [BoundEventAst, 'event', null, 'v']]); + }); - expect(() => parse('
', [])) - .toThrowError(/Empty expressions are not allowed/); - }); + it('should allow events on explicit embedded templates that are emitted by a directive', + () => { + var dirA = CompileDirectiveMetadata.create({ + selector: 'template', + outputs: ['e'], + type: new CompileTypeMetadata( + {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}) + }); + expect(humanizeTplAst(parse('', [dirA]))).toEqual([ + [EmbeddedTemplateAst], + [BoundEventAst, 'e', null, 'f'], + [DirectiveAst, dirA], + ]); + }); + }); - it('should parse bound events via (...) and not report them as attributes', () => { - expect(humanizeTplAst(parse('
', [ - ]))).toEqual([[ElementAst, 'div'], [BoundEventAst, 'event', null, 'v']]); - }); + describe('bindon', () => { + it('should parse bound events and properties via [(...)] and not report them as attributes', + () => { + expect(humanizeTplAst(parse('
', []))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Property, 'prop', 'v', null], + [BoundEventAst, 'propChange', null, 'v = $event'] + ]); + }); + + it('should parse bound events and properties via bindon- and not report them as attributes', + () => { + expect(humanizeTplAst(parse('
', []))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Property, 'prop', 'v', null], + [BoundEventAst, 'propChange', null, 'v = $event'] + ]); + }); - it('should parse event names case sensitive', () => { - expect(humanizeTplAst(parse('
', [ - ]))).toEqual([[ElementAst, 'div'], [BoundEventAst, 'some-event', null, 'v']]); - expect(humanizeTplAst(parse('
', [ - ]))).toEqual([[ElementAst, 'div'], [BoundEventAst, 'someEvent', null, 'v']]); - }); + }); - it('should parse bound events via on- and not report them as attributes', () => { - expect(humanizeTplAst(parse('
', [ - ]))).toEqual([[ElementAst, 'div'], [BoundEventAst, 'event', null, 'v']]); - }); + describe('directives', () => { + it('should order directives by the directives array in the View and match them only once', + () => { + var dirA = CompileDirectiveMetadata.create({ + selector: '[a]', + type: new CompileTypeMetadata( + {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}) + }); + var dirB = CompileDirectiveMetadata.create({ + selector: '[b]', + type: new CompileTypeMetadata( + {moduleUrl: someModuleUrl, name: 'DirB', reference: {} as Type}) + }); + var dirC = CompileDirectiveMetadata.create({ + selector: '[c]', + type: new CompileTypeMetadata( + {moduleUrl: someModuleUrl, name: 'DirC', reference: {} as Type}) + }); + expect(humanizeTplAst(parse('
', [dirA, dirB, dirC]))).toEqual([ + [ElementAst, 'div'], [AttrAst, 'a', ''], [AttrAst, 'c', ''], [AttrAst, 'b', ''], + [AttrAst, 'a', ''], [AttrAst, 'b', ''], [DirectiveAst, dirA], + [DirectiveAst, dirB], [DirectiveAst, dirC] + ]); + }); + + it('should locate directives in property bindings', () => { + var dirA = CompileDirectiveMetadata.create({ + selector: '[a=b]', + type: new CompileTypeMetadata( + {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}) + }); + var dirB = CompileDirectiveMetadata.create({ + selector: '[b]', + type: new CompileTypeMetadata( + {moduleUrl: someModuleUrl, name: 'DirB', reference: {} as Type}) + }); + expect(humanizeTplAst(parse('
', [dirA, dirB]))).toEqual([ + [ElementAst, 'div'], + [BoundElementPropertyAst, PropertyBindingType.Property, 'a', 'b', null], + [DirectiveAst, dirA] + ]); + }); - it('should allow events on explicit embedded templates that are emitted by a directive', - () => { - var dirA = CompileDirectiveMetadata.create({ - selector: 'template', - outputs: ['e'], - type: new CompileTypeMetadata( - {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}) - }); - expect(humanizeTplAst(parse('', [dirA]))).toEqual([ - [EmbeddedTemplateAst], - [BoundEventAst, 'e', null, 'f'], - [DirectiveAst, dirA], - ]); - }); - }); + it('should locate directives in event bindings', () => { + var dirA = CompileDirectiveMetadata.create({ + selector: '[a]', + type: new CompileTypeMetadata( + {moduleUrl: someModuleUrl, name: 'DirB', reference: {} as Type}) + }); - describe('bindon', () => { - it('should parse bound events and properties via [(...)] and not report them as attributes', - () => { - expect(humanizeTplAst(parse('
', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Property, 'prop', 'v', null], - [BoundEventAst, 'propChange', null, 'v = $event'] - ]); - }); - - it('should parse bound events and properties via bindon- and not report them as attributes', - () => { - expect(humanizeTplAst(parse('
', []))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Property, 'prop', 'v', null], - [BoundEventAst, 'propChange', null, 'v = $event'] - ]); - }); + expect(humanizeTplAst(parse('
', [dirA]))).toEqual([ + [ElementAst, 'div'], [BoundEventAst, 'a', null, 'b'], [DirectiveAst, dirA] + ]); + }); - }); + it('should parse directive host properties', () => { + var dirA = CompileDirectiveMetadata.create({ + selector: 'div', + type: new CompileTypeMetadata( + {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}), + host: {'[a]': 'expr'} + }); + expect(humanizeTplAst(parse('
', [dirA]))).toEqual([ + [ElementAst, 'div'], [DirectiveAst, dirA], + [BoundElementPropertyAst, PropertyBindingType.Property, 'a', 'expr', null] + ]); + }); - describe('directives', () => { - it('should order directives by the directives array in the View and match them only once', - () => { - var dirA = CompileDirectiveMetadata.create({ - selector: '[a]', - type: new CompileTypeMetadata( - {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}) - }); - var dirB = CompileDirectiveMetadata.create({ - selector: '[b]', - type: new CompileTypeMetadata( - {moduleUrl: someModuleUrl, name: 'DirB', reference: {} as Type}) - }); - var dirC = CompileDirectiveMetadata.create({ - selector: '[c]', - type: new CompileTypeMetadata( - {moduleUrl: someModuleUrl, name: 'DirC', reference: {} as Type}) - }); - expect(humanizeTplAst(parse('
', [dirA, dirB, dirC]))).toEqual([ - [ElementAst, 'div'], [AttrAst, 'a', ''], [AttrAst, 'c', ''], [AttrAst, 'b', ''], - [AttrAst, 'a', ''], [AttrAst, 'b', ''], [DirectiveAst, dirA], [DirectiveAst, dirB], - [DirectiveAst, dirC] - ]); - }); - - it('should locate directives in property bindings', () => { - var dirA = CompileDirectiveMetadata.create({ - selector: '[a=b]', - type: new CompileTypeMetadata( - {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}) - }); - var dirB = CompileDirectiveMetadata.create({ - selector: '[b]', - type: new CompileTypeMetadata( - {moduleUrl: someModuleUrl, name: 'DirB', reference: {} as Type}) - }); - expect(humanizeTplAst(parse('
', [dirA, dirB]))).toEqual([ - [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Property, 'a', 'b', null], - [DirectiveAst, dirA] - ]); - }); + it('should parse directive host listeners', () => { + var dirA = CompileDirectiveMetadata.create({ + selector: 'div', + type: new CompileTypeMetadata( + {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}), + host: {'(a)': 'expr'} + }); + expect(humanizeTplAst(parse('
', [dirA]))).toEqual([ + [ElementAst, 'div'], [DirectiveAst, dirA], [BoundEventAst, 'a', null, 'expr'] + ]); + }); - it('should locate directives in event bindings', () => { - var dirA = CompileDirectiveMetadata.create({ - selector: '[a]', - type: new CompileTypeMetadata( - {moduleUrl: someModuleUrl, name: 'DirB', reference: {} as Type}) - }); + it('should parse directive properties', () => { + var dirA = CompileDirectiveMetadata.create({ + selector: 'div', + type: new CompileTypeMetadata( + {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}), + inputs: ['aProp'] + }); + expect(humanizeTplAst(parse('
', [dirA]))).toEqual([ + [ElementAst, 'div'], [DirectiveAst, dirA], + [BoundDirectivePropertyAst, 'aProp', 'expr'] + ]); + }); - expect(humanizeTplAst(parse('
', [dirA]))).toEqual([ - [ElementAst, 'div'], [BoundEventAst, 'a', null, 'b'], [DirectiveAst, dirA] - ]); - }); + it('should parse renamed directive properties', () => { + var dirA = CompileDirectiveMetadata.create({ + selector: 'div', + type: new CompileTypeMetadata( + {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}), + inputs: ['b:a'] + }); + expect(humanizeTplAst(parse('
', [dirA]))).toEqual([ + [ElementAst, 'div'], [DirectiveAst, dirA], + [BoundDirectivePropertyAst, 'b', 'expr'] + ]); + }); - it('should parse directive host properties', () => { - var dirA = CompileDirectiveMetadata.create({ - selector: 'div', - type: new CompileTypeMetadata( - {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}), - host: {'[a]': 'expr'} - }); - expect(humanizeTplAst(parse('
', [dirA]))).toEqual([ - [ElementAst, 'div'], [DirectiveAst, dirA], - [BoundElementPropertyAst, PropertyBindingType.Property, 'a', 'expr', null] - ]); - }); + it('should parse literal directive properties', () => { + var dirA = CompileDirectiveMetadata.create({ + selector: 'div', + type: new CompileTypeMetadata( + {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}), + inputs: ['a'] + }); + expect(humanizeTplAst(parse('
', [dirA]))).toEqual([ + [ElementAst, 'div'], [AttrAst, 'a', 'literal'], [DirectiveAst, dirA], + [BoundDirectivePropertyAst, 'a', '"literal"'] + ]); + }); - it('should parse directive host listeners', () => { - var dirA = CompileDirectiveMetadata.create({ - selector: 'div', - type: new CompileTypeMetadata( - {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}), - host: {'(a)': 'expr'} - }); - expect(humanizeTplAst(parse('
', [dirA]))).toEqual([ - [ElementAst, 'div'], [DirectiveAst, dirA], [BoundEventAst, 'a', null, 'expr'] - ]); - }); + it('should favor explicit bound properties over literal properties', () => { + var dirA = CompileDirectiveMetadata.create({ + selector: 'div', + type: new CompileTypeMetadata( + {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}), + inputs: ['a'] + }); + expect(humanizeTplAst(parse('
', [dirA]))) + .toEqual([ + [ElementAst, 'div'], [AttrAst, 'a', 'literal'], [DirectiveAst, dirA], + [BoundDirectivePropertyAst, 'a', '"literal2"'] + ]); + }); - it('should parse directive properties', () => { - var dirA = CompileDirectiveMetadata.create({ - selector: 'div', - type: new CompileTypeMetadata( - {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}), - inputs: ['aProp'] - }); - expect(humanizeTplAst(parse('
', [dirA]))).toEqual([ - [ElementAst, 'div'], [DirectiveAst, dirA], - [BoundDirectivePropertyAst, 'aProp', 'expr'] - ]); - }); + it('should support optional directive properties', () => { + var dirA = CompileDirectiveMetadata.create({ + selector: 'div', + type: new CompileTypeMetadata( + {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}), + inputs: ['a'] + }); + expect(humanizeTplAst(parse('
', [dirA]))).toEqual([ + [ElementAst, 'div'], [DirectiveAst, dirA] + ]); + }); - it('should parse renamed directive properties', () => { - var dirA = CompileDirectiveMetadata.create({ - selector: 'div', - type: new CompileTypeMetadata( - {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}), - inputs: ['b:a'] }); - expect(humanizeTplAst(parse('
', [dirA]))).toEqual([ - [ElementAst, 'div'], [DirectiveAst, dirA], [BoundDirectivePropertyAst, 'b', 'expr'] - ]); - }); - it('should parse literal directive properties', () => { - var dirA = CompileDirectiveMetadata.create({ - selector: 'div', - type: new CompileTypeMetadata( - {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}), - inputs: ['a'] - }); - expect(humanizeTplAst(parse('
', [dirA]))).toEqual([ - [ElementAst, 'div'], [AttrAst, 'a', 'literal'], [DirectiveAst, dirA], - [BoundDirectivePropertyAst, 'a', '"literal"'] - ]); - }); + describe('providers', () => { + var nextProviderId: number; + + function createToken(value: string): CompileTokenMetadata { + let token: CompileTokenMetadata; + if (value.startsWith('type:')) { + const name = value.substring(5); + token = new CompileTokenMetadata({ + identifier: new CompileTypeMetadata( + {moduleUrl: someModuleUrl, name, reference: name as any as Type}) + }); + } else { + token = new CompileTokenMetadata({value: value}); + } + return token; + } - it('should favor explicit bound properties over literal properties', () => { - var dirA = CompileDirectiveMetadata.create({ - selector: 'div', - type: new CompileTypeMetadata( - {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}), - inputs: ['a'] - }); - expect(humanizeTplAst(parse('
', [dirA]))) - .toEqual([ - [ElementAst, 'div'], [AttrAst, 'a', 'literal'], [DirectiveAst, dirA], - [BoundDirectivePropertyAst, 'a', '"literal2"'] - ]); - }); + function createDep(value: string): CompileDiDependencyMetadata { + var isOptional = false; + if (value.startsWith('optional:')) { + isOptional = true; + value = value.substring(9); + } + var isSelf = false; + if (value.startsWith('self:')) { + isSelf = true; + value = value.substring(5); + } + var isHost = false; + if (value.startsWith('host:')) { + isHost = true; + value = value.substring(5); + } + return new CompileDiDependencyMetadata({ + token: createToken(value), + isOptional: isOptional, + isSelf: isSelf, + isHost: isHost + }); + } - it('should support optional directive properties', () => { - var dirA = CompileDirectiveMetadata.create({ - selector: 'div', - type: new CompileTypeMetadata( - {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}), - inputs: ['a'] - }); - expect(humanizeTplAst(parse('
', [dirA]))).toEqual([ - [ElementAst, 'div'], [DirectiveAst, dirA] - ]); - }); + function createProvider( + token: string, {multi = false, deps = []}: {multi?: boolean, deps?: string[]} = {}): + CompileProviderMetadata { + const name = `provider${nextProviderId++}`; + return new CompileProviderMetadata({ + token: createToken(token), + multi: multi, + useClass: new CompileTypeMetadata({name, reference: name as any as Type}), + deps: deps.map(createDep) + }); + } - }); + function createDir( + selector: string, + {providers = null, viewProviders = null, deps = [], queries = []}: { + providers?: CompileProviderMetadata[], + viewProviders?: CompileProviderMetadata[], + deps?: string[], + queries?: string[] + } = {}): CompileDirectiveMetadata { + var isComponent = !selector.startsWith('['); + return CompileDirectiveMetadata.create({ + selector: selector, + type: new CompileTypeMetadata({ + moduleUrl: someModuleUrl, + name: selector, + diDeps: deps.map(createDep), + reference: selector as any as Type + }), + isComponent: isComponent, + template: new CompileTemplateMetadata({ngContentSelectors: []}), + providers: providers, + viewProviders: viewProviders, + queries: queries.map( + (value) => new CompileQueryMetadata({selectors: [createToken(value)]})) + }); + } - describe('providers', () => { - var nextProviderId: number; + beforeEach(() => { nextProviderId = 0; }); - function createToken(value: string): CompileTokenMetadata { - let token: CompileTokenMetadata; - if (value.startsWith('type:')) { - const name = value.substring(5); - token = new CompileTokenMetadata({ - identifier: new CompileTypeMetadata( - {moduleUrl: someModuleUrl, name, reference: name as any as Type}) + it('should provide a component', () => { + var comp = createDir('my-comp'); + var elAst: ElementAst = parse('', [comp])[0]; + expect(elAst.providers.length).toBe(1); + expect(elAst.providers[0].providerType).toBe(ProviderAstType.Component); + expect(elAst.providers[0].providers[0].useClass).toBe(comp.type); }); - } else { - token = new CompileTokenMetadata({value: value}); - } - return token; - } - - function createDep(value: string): CompileDiDependencyMetadata { - var isOptional = false; - if (value.startsWith('optional:')) { - isOptional = true; - value = value.substring(9); - } - var isSelf = false; - if (value.startsWith('self:')) { - isSelf = true; - value = value.substring(5); - } - var isHost = false; - if (value.startsWith('host:')) { - isHost = true; - value = value.substring(5); - } - return new CompileDiDependencyMetadata( - {token: createToken(value), isOptional: isOptional, isSelf: isSelf, isHost: isHost}); - } - - function createProvider( - token: string, {multi = false, deps = []}: {multi?: boolean, deps?: string[]} = {}): - CompileProviderMetadata { - const name = `provider${nextProviderId++}`; - return new CompileProviderMetadata({ - token: createToken(token), - multi: multi, - useClass: new CompileTypeMetadata({name, reference: name as any as Type}), - deps: deps.map(createDep) - }); - } - - function createDir( - selector: string, {providers = null, viewProviders = null, deps = [], queries = []}: { - providers?: CompileProviderMetadata[], - viewProviders?: CompileProviderMetadata[], - deps?: string[], - queries?: string[] - } = {}): CompileDirectiveMetadata { - var isComponent = !selector.startsWith('['); - return CompileDirectiveMetadata.create({ - selector: selector, - type: new CompileTypeMetadata({ - moduleUrl: someModuleUrl, - name: selector, - diDeps: deps.map(createDep), - reference: selector as any as Type - }), - isComponent: isComponent, - template: new CompileTemplateMetadata({ngContentSelectors: []}), - providers: providers, - viewProviders: viewProviders, - queries: queries.map( - (value) => new CompileQueryMetadata({selectors: [createToken(value)]})) - }); - } - - beforeEach(() => { nextProviderId = 0; }); - - it('should provide a component', () => { - var comp = createDir('my-comp'); - var elAst: ElementAst = parse('', [comp])[0]; - expect(elAst.providers.length).toBe(1); - expect(elAst.providers[0].providerType).toBe(ProviderAstType.Component); - expect(elAst.providers[0].providers[0].useClass).toBe(comp.type); - }); - it('should provide a directive', () => { - var dirA = createDir('[dirA]'); - var elAst: ElementAst = parse('
', [dirA])[0]; - expect(elAst.providers.length).toBe(1); - expect(elAst.providers[0].providerType).toBe(ProviderAstType.Directive); - expect(elAst.providers[0].providers[0].useClass).toBe(dirA.type); - }); + it('should provide a directive', () => { + var dirA = createDir('[dirA]'); + var elAst: ElementAst = parse('
', [dirA])[0]; + expect(elAst.providers.length).toBe(1); + expect(elAst.providers[0].providerType).toBe(ProviderAstType.Directive); + expect(elAst.providers[0].providers[0].useClass).toBe(dirA.type); + }); - it('should use the public providers of a directive', () => { - var provider = createProvider('service'); - var dirA = createDir('[dirA]', {providers: [provider]}); - var elAst: ElementAst = parse('
', [dirA])[0]; - expect(elAst.providers.length).toBe(2); - expect(elAst.providers[1].providerType).toBe(ProviderAstType.PublicService); - expect(elAst.providers[1].providers).toEqual([provider]); - }); + it('should use the public providers of a directive', () => { + var provider = createProvider('service'); + var dirA = createDir('[dirA]', {providers: [provider]}); + var elAst: ElementAst = parse('
', [dirA])[0]; + expect(elAst.providers.length).toBe(2); + expect(elAst.providers[1].providerType).toBe(ProviderAstType.PublicService); + expect(elAst.providers[1].providers).toEqual([provider]); + }); - it('should use the private providers of a component', () => { - var provider = createProvider('service'); - var comp = createDir('my-comp', {viewProviders: [provider]}); - var elAst: ElementAst = parse('', [comp])[0]; - expect(elAst.providers.length).toBe(2); - expect(elAst.providers[1].providerType).toBe(ProviderAstType.PrivateService); - expect(elAst.providers[1].providers).toEqual([provider]); - }); + it('should use the private providers of a component', () => { + var provider = createProvider('service'); + var comp = createDir('my-comp', {viewProviders: [provider]}); + var elAst: ElementAst = parse('', [comp])[0]; + expect(elAst.providers.length).toBe(2); + expect(elAst.providers[1].providerType).toBe(ProviderAstType.PrivateService); + expect(elAst.providers[1].providers).toEqual([provider]); + }); - it('should support multi providers', () => { - var provider0 = createProvider('service0', {multi: true}); - var provider1 = createProvider('service1', {multi: true}); - var provider2 = createProvider('service0', {multi: true}); - var dirA = createDir('[dirA]', {providers: [provider0, provider1]}); - var dirB = createDir('[dirB]', {providers: [provider2]}); - var elAst: ElementAst = parse('
', [dirA, dirB])[0]; - expect(elAst.providers.length).toBe(4); - expect(elAst.providers[2].providers).toEqual([provider0, provider2]); - expect(elAst.providers[3].providers).toEqual([provider1]); - }); + it('should support multi providers', () => { + var provider0 = createProvider('service0', {multi: true}); + var provider1 = createProvider('service1', {multi: true}); + var provider2 = createProvider('service0', {multi: true}); + var dirA = createDir('[dirA]', {providers: [provider0, provider1]}); + var dirB = createDir('[dirB]', {providers: [provider2]}); + var elAst: ElementAst = parse('
', [dirA, dirB])[0]; + expect(elAst.providers.length).toBe(4); + expect(elAst.providers[2].providers).toEqual([provider0, provider2]); + expect(elAst.providers[3].providers).toEqual([provider1]); + }); - it('should overwrite non multi providers', () => { - var provider1 = createProvider('service0'); - var provider2 = createProvider('service1'); - var provider3 = createProvider('service0'); - var dirA = createDir('[dirA]', {providers: [provider1, provider2]}); - var dirB = createDir('[dirB]', {providers: [provider3]}); - var elAst: ElementAst = parse('
', [dirA, dirB])[0]; - expect(elAst.providers.length).toBe(4); - expect(elAst.providers[2].providers).toEqual([provider3]); - expect(elAst.providers[3].providers).toEqual([provider2]); - }); + it('should overwrite non multi providers', () => { + var provider1 = createProvider('service0'); + var provider2 = createProvider('service1'); + var provider3 = createProvider('service0'); + var dirA = createDir('[dirA]', {providers: [provider1, provider2]}); + var dirB = createDir('[dirB]', {providers: [provider3]}); + var elAst: ElementAst = parse('
', [dirA, dirB])[0]; + expect(elAst.providers.length).toBe(4); + expect(elAst.providers[2].providers).toEqual([provider3]); + expect(elAst.providers[3].providers).toEqual([provider2]); + }); - it('should overwrite component providers by directive providers', () => { - var compProvider = createProvider('service0'); - var dirProvider = createProvider('service0'); - var comp = createDir('my-comp', {providers: [compProvider]}); - var dirA = createDir('[dirA]', {providers: [dirProvider]}); - var elAst: ElementAst = parse('', [dirA, comp])[0]; - expect(elAst.providers.length).toBe(3); - expect(elAst.providers[2].providers).toEqual([dirProvider]); - }); + it('should overwrite component providers by directive providers', () => { + var compProvider = createProvider('service0'); + var dirProvider = createProvider('service0'); + var comp = createDir('my-comp', {providers: [compProvider]}); + var dirA = createDir('[dirA]', {providers: [dirProvider]}); + var elAst: ElementAst = parse('', [dirA, comp])[0]; + expect(elAst.providers.length).toBe(3); + expect(elAst.providers[2].providers).toEqual([dirProvider]); + }); - it('should overwrite view providers by directive providers', () => { - var viewProvider = createProvider('service0'); - var dirProvider = createProvider('service0'); - var comp = createDir('my-comp', {viewProviders: [viewProvider]}); - var dirA = createDir('[dirA]', {providers: [dirProvider]}); - var elAst: ElementAst = parse('', [dirA, comp])[0]; - expect(elAst.providers.length).toBe(3); - expect(elAst.providers[2].providers).toEqual([dirProvider]); - }); + it('should overwrite view providers by directive providers', () => { + var viewProvider = createProvider('service0'); + var dirProvider = createProvider('service0'); + var comp = createDir('my-comp', {viewProviders: [viewProvider]}); + var dirA = createDir('[dirA]', {providers: [dirProvider]}); + var elAst: ElementAst = parse('', [dirA, comp])[0]; + expect(elAst.providers.length).toBe(3); + expect(elAst.providers[2].providers).toEqual([dirProvider]); + }); - it('should overwrite directives by providers', () => { - var dirProvider = createProvider('type:my-comp'); - var comp = createDir('my-comp', {providers: [dirProvider]}); - var elAst: ElementAst = parse('', [comp])[0]; - expect(elAst.providers.length).toBe(1); - expect(elAst.providers[0].providers).toEqual([dirProvider]); - }); + it('should overwrite directives by providers', () => { + var dirProvider = createProvider('type:my-comp'); + var comp = createDir('my-comp', {providers: [dirProvider]}); + var elAst: ElementAst = parse('', [comp])[0]; + expect(elAst.providers.length).toBe(1); + expect(elAst.providers[0].providers).toEqual([dirProvider]); + }); - it('if mixing multi and non multi providers', () => { - var provider0 = createProvider('service0'); - var provider1 = createProvider('service0', {multi: true}); - var dirA = createDir('[dirA]', {providers: [provider0]}); - var dirB = createDir('[dirB]', {providers: [provider1]}); - expect(() => parse('
', [dirA, dirB])) - .toThrowError( - `Template parse errors:\n` + - `Mixing multi and non multi provider is not possible for token service0 ("[ERROR ->]
"): TestComp@0:0`); - }); + it('if mixing multi and non multi providers', () => { + var provider0 = createProvider('service0'); + var provider1 = createProvider('service0', {multi: true}); + var dirA = createDir('[dirA]', {providers: [provider0]}); + var dirB = createDir('[dirB]', {providers: [provider1]}); + expect(() => parse('
', [dirA, dirB])) + .toThrowError( + `Template parse errors:\n` + + `Mixing multi and non multi provider is not possible for token service0 ("[ERROR ->]
"): TestComp@0:0`); + }); - it('should sort providers by their DI order', () => { - var provider0 = createProvider('service0', {deps: ['type:[dir2]']}); - var provider1 = createProvider('service1'); - var dir2 = createDir('[dir2]', {deps: ['service1']}); - var comp = createDir('my-comp', {providers: [provider0, provider1]}); - var elAst: ElementAst = parse('', [comp, dir2])[0]; - expect(elAst.providers.length).toBe(4); - expect(elAst.providers[0].providers[0].useClass).toEqual(comp.type); - expect(elAst.providers[1].providers).toEqual([provider1]); - expect(elAst.providers[2].providers[0].useClass).toEqual(dir2.type); - expect(elAst.providers[3].providers).toEqual([provider0]); - }); + it('should sort providers by their DI order', () => { + var provider0 = createProvider('service0', {deps: ['type:[dir2]']}); + var provider1 = createProvider('service1'); + var dir2 = createDir('[dir2]', {deps: ['service1']}); + var comp = createDir('my-comp', {providers: [provider0, provider1]}); + var elAst: ElementAst = parse('', [comp, dir2])[0]; + expect(elAst.providers.length).toBe(4); + expect(elAst.providers[0].providers[0].useClass).toEqual(comp.type); + expect(elAst.providers[1].providers).toEqual([provider1]); + expect(elAst.providers[2].providers[0].useClass).toEqual(dir2.type); + expect(elAst.providers[3].providers).toEqual([provider0]); + }); - it('should sort directives by their DI order', () => { - var dir0 = createDir('[dir0]', {deps: ['type:my-comp']}); - var dir1 = createDir('[dir1]', {deps: ['type:[dir0]']}); - var dir2 = createDir('[dir2]', {deps: ['type:[dir1]']}); - var comp = createDir('my-comp'); - var elAst: ElementAst = - parse('', [comp, dir2, dir0, dir1])[0]; - expect(elAst.providers.length).toBe(4); - expect(elAst.directives[0].directive).toBe(comp); - expect(elAst.directives[1].directive).toBe(dir0); - expect(elAst.directives[2].directive).toBe(dir1); - expect(elAst.directives[3].directive).toBe(dir2); - }); + it('should sort directives by their DI order', () => { + var dir0 = createDir('[dir0]', {deps: ['type:my-comp']}); + var dir1 = createDir('[dir1]', {deps: ['type:[dir0]']}); + var dir2 = createDir('[dir2]', {deps: ['type:[dir1]']}); + var comp = createDir('my-comp'); + var elAst: ElementAst = + parse('', [comp, dir2, dir0, dir1])[0]; + expect(elAst.providers.length).toBe(4); + expect(elAst.directives[0].directive).toBe(comp); + expect(elAst.directives[1].directive).toBe(dir0); + expect(elAst.directives[2].directive).toBe(dir1); + expect(elAst.directives[3].directive).toBe(dir2); + }); - it('should mark directives and dependencies of directives as eager', () => { - var provider0 = createProvider('service0'); - var provider1 = createProvider('service1'); - var dirA = createDir('[dirA]', {providers: [provider0, provider1], deps: ['service0']}); - var elAst: ElementAst = parse('
', [dirA])[0]; - expect(elAst.providers.length).toBe(3); - expect(elAst.providers[0].providers).toEqual([provider0]); - expect(elAst.providers[0].eager).toBe(true); - expect(elAst.providers[1].providers[0].useClass).toEqual(dirA.type); - expect(elAst.providers[1].eager).toBe(true); - expect(elAst.providers[2].providers).toEqual([provider1]); - expect(elAst.providers[2].eager).toBe(false); - }); + it('should mark directives and dependencies of directives as eager', () => { + var provider0 = createProvider('service0'); + var provider1 = createProvider('service1'); + var dirA = + createDir('[dirA]', {providers: [provider0, provider1], deps: ['service0']}); + var elAst: ElementAst = parse('
', [dirA])[0]; + expect(elAst.providers.length).toBe(3); + expect(elAst.providers[0].providers).toEqual([provider0]); + expect(elAst.providers[0].eager).toBe(true); + expect(elAst.providers[1].providers[0].useClass).toEqual(dirA.type); + expect(elAst.providers[1].eager).toBe(true); + expect(elAst.providers[2].providers).toEqual([provider1]); + expect(elAst.providers[2].eager).toBe(false); + }); - it('should mark dependencies on parent elements as eager', () => { - var provider0 = createProvider('service0'); - var provider1 = createProvider('service1'); - var dirA = createDir('[dirA]', {providers: [provider0, provider1]}); - var dirB = createDir('[dirB]', {deps: ['service0']}); - var elAst: ElementAst = - parse('
', [dirA, dirB])[0]; - expect(elAst.providers.length).toBe(3); - expect(elAst.providers[0].providers[0].useClass).toEqual(dirA.type); - expect(elAst.providers[0].eager).toBe(true); - expect(elAst.providers[1].providers).toEqual([provider0]); - expect(elAst.providers[1].eager).toBe(true); - expect(elAst.providers[2].providers).toEqual([provider1]); - expect(elAst.providers[2].eager).toBe(false); - }); + it('should mark dependencies on parent elements as eager', () => { + var provider0 = createProvider('service0'); + var provider1 = createProvider('service1'); + var dirA = createDir('[dirA]', {providers: [provider0, provider1]}); + var dirB = createDir('[dirB]', {deps: ['service0']}); + var elAst: ElementAst = + parse('
', [dirA, dirB])[0]; + expect(elAst.providers.length).toBe(3); + expect(elAst.providers[0].providers[0].useClass).toEqual(dirA.type); + expect(elAst.providers[0].eager).toBe(true); + expect(elAst.providers[1].providers).toEqual([provider0]); + expect(elAst.providers[1].eager).toBe(true); + expect(elAst.providers[2].providers).toEqual([provider1]); + expect(elAst.providers[2].eager).toBe(false); + }); - it('should mark queried providers as eager', () => { - var provider0 = createProvider('service0'); - var provider1 = createProvider('service1'); - var dirA = - createDir('[dirA]', {providers: [provider0, provider1], queries: ['service0']}); - var elAst: ElementAst = parse('
', [dirA])[0]; - expect(elAst.providers.length).toBe(3); - expect(elAst.providers[0].providers[0].useClass).toEqual(dirA.type); - expect(elAst.providers[0].eager).toBe(true); - expect(elAst.providers[1].providers).toEqual([provider0]); - expect(elAst.providers[1].eager).toBe(true); - expect(elAst.providers[2].providers).toEqual([provider1]); - expect(elAst.providers[2].eager).toBe(false); - }); + it('should mark queried providers as eager', () => { + var provider0 = createProvider('service0'); + var provider1 = createProvider('service1'); + var dirA = + createDir('[dirA]', {providers: [provider0, provider1], queries: ['service0']}); + var elAst: ElementAst = parse('
', [dirA])[0]; + expect(elAst.providers.length).toBe(3); + expect(elAst.providers[0].providers[0].useClass).toEqual(dirA.type); + expect(elAst.providers[0].eager).toBe(true); + expect(elAst.providers[1].providers).toEqual([provider0]); + expect(elAst.providers[1].eager).toBe(true); + expect(elAst.providers[2].providers).toEqual([provider1]); + expect(elAst.providers[2].eager).toBe(false); + }); - it('should not mark dependencies accross embedded views as eager', () => { - var provider0 = createProvider('service0'); - var dirA = createDir('[dirA]', {providers: [provider0]}); - var dirB = createDir('[dirB]', {deps: ['service0']}); - var elAst: ElementAst = - parse('
', [dirA, dirB])[0]; - expect(elAst.providers.length).toBe(2); - expect(elAst.providers[0].providers[0].useClass).toEqual(dirA.type); - expect(elAst.providers[0].eager).toBe(true); - expect(elAst.providers[1].providers).toEqual([provider0]); - expect(elAst.providers[1].eager).toBe(false); - }); + it('should not mark dependencies accross embedded views as eager', () => { + var provider0 = createProvider('service0'); + var dirA = createDir('[dirA]', {providers: [provider0]}); + var dirB = createDir('[dirB]', {deps: ['service0']}); + var elAst: ElementAst = + parse('
', [dirA, dirB])[0]; + expect(elAst.providers.length).toBe(2); + expect(elAst.providers[0].providers[0].useClass).toEqual(dirA.type); + expect(elAst.providers[0].eager).toBe(true); + expect(elAst.providers[1].providers).toEqual([provider0]); + expect(elAst.providers[1].eager).toBe(false); + }); - it('should report missing @Self() deps as errors', () => { - var dirA = createDir('[dirA]', {deps: ['self:provider0']}); - expect(() => parse('
', [dirA])) - .toThrowError( - 'Template parse errors:\nNo provider for provider0 ("[ERROR ->]
"): TestComp@0:0'); - }); + it('should report missing @Self() deps as errors', () => { + var dirA = createDir('[dirA]', {deps: ['self:provider0']}); + expect(() => parse('
', [dirA])) + .toThrowError( + 'Template parse errors:\nNo provider for provider0 ("[ERROR ->]
"): TestComp@0:0'); + }); - it('should change missing @Self() that are optional to nulls', () => { - var dirA = createDir('[dirA]', {deps: ['optional:self:provider0']}); - var elAst: ElementAst = parse('
', [dirA])[0]; - expect(elAst.providers[0].providers[0].deps[0].isValue).toBe(true); - expect(elAst.providers[0].providers[0].deps[0].value).toBe(null); - }); + it('should change missing @Self() that are optional to nulls', () => { + var dirA = createDir('[dirA]', {deps: ['optional:self:provider0']}); + var elAst: ElementAst = parse('
', [dirA])[0]; + expect(elAst.providers[0].providers[0].deps[0].isValue).toBe(true); + expect(elAst.providers[0].providers[0].deps[0].value).toBe(null); + }); - it('should report missing @Host() deps as errors', () => { - var dirA = createDir('[dirA]', {deps: ['host:provider0']}); - expect(() => parse('
', [dirA])) - .toThrowError( - 'Template parse errors:\nNo provider for provider0 ("[ERROR ->]
"): TestComp@0:0'); - }); + it('should report missing @Host() deps as errors', () => { + var dirA = createDir('[dirA]', {deps: ['host:provider0']}); + expect(() => parse('
', [dirA])) + .toThrowError( + 'Template parse errors:\nNo provider for provider0 ("[ERROR ->]
"): TestComp@0:0'); + }); - it('should change missing @Host() that are optional to nulls', () => { - var dirA = createDir('[dirA]', {deps: ['optional:host:provider0']}); - var elAst: ElementAst = parse('
', [dirA])[0]; - expect(elAst.providers[0].providers[0].deps[0].isValue).toBe(true); - expect(elAst.providers[0].providers[0].deps[0].value).toBe(null); - }); - }); + it('should change missing @Host() that are optional to nulls', () => { + var dirA = createDir('[dirA]', {deps: ['optional:host:provider0']}); + var elAst: ElementAst = parse('
', [dirA])[0]; + expect(elAst.providers[0].providers[0].deps[0].isValue).toBe(true); + expect(elAst.providers[0].providers[0].deps[0].value).toBe(null); + }); + }); - describe('references', () => { + describe('references', () => { - it('should parse references via #... and not report them as attributes', () => { - expect(humanizeTplAst(parse('
', [ - ]))).toEqual([[ElementAst, 'div'], [ReferenceAst, 'a', null]]); - }); + it('should parse references via #... and not report them as attributes', () => { + expect(humanizeTplAst(parse('
', [ + ]))).toEqual([[ElementAst, 'div'], [ReferenceAst, 'a', null]]); + }); - it('should parse references via ref-... and not report them as attributes', () => { - expect(humanizeTplAst(parse('
', [ - ]))).toEqual([[ElementAst, 'div'], [ReferenceAst, 'a', null]]); - }); + it('should parse references via ref-... and not report them as attributes', () => { + expect(humanizeTplAst(parse('
', [ + ]))).toEqual([[ElementAst, 'div'], [ReferenceAst, 'a', null]]); + }); - it('should parse camel case references', () => { - expect(humanizeTplAst(parse('
', [ - ]))).toEqual([[ElementAst, 'div'], [ReferenceAst, 'someA', null]]); - }); + it('should parse camel case references', () => { + expect(humanizeTplAst(parse('
', [ + ]))).toEqual([[ElementAst, 'div'], [ReferenceAst, 'someA', null]]); + }); - it('should assign references with empty value to the element', () => { - expect(humanizeTplAst(parse('
', [ - ]))).toEqual([[ElementAst, 'div'], [ReferenceAst, 'a', null]]); - }); + it('should assign references with empty value to the element', () => { + expect(humanizeTplAst(parse('
', [ + ]))).toEqual([[ElementAst, 'div'], [ReferenceAst, 'a', null]]); + }); - it('should assign references to directives via exportAs', () => { - var dirA = CompileDirectiveMetadata.create({ - selector: '[a]', - type: new CompileTypeMetadata( - {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}), - exportAs: 'dirA' - }); - expect(humanizeTplAst(parse('
', [dirA]))).toEqual([ - [ElementAst, 'div'], - [AttrAst, 'a', ''], - [ReferenceAst, 'a', identifierToken(dirA.type)], - [DirectiveAst, dirA], - ]); - }); + it('should assign references to directives via exportAs', () => { + var dirA = CompileDirectiveMetadata.create({ + selector: '[a]', + type: new CompileTypeMetadata( + {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}), + exportAs: 'dirA' + }); + expect(humanizeTplAst(parse('
', [dirA]))).toEqual([ + [ElementAst, 'div'], + [AttrAst, 'a', ''], + [ReferenceAst, 'a', identifierToken(dirA.type)], + [DirectiveAst, dirA], + ]); + }); - it('should report references with values that dont match a directive as errors', () => { - expect(() => parse('
', [])).toThrowError(`Template parse errors: + it('should report references with values that dont match a directive as errors', () => { + expect(() => parse('
', [])).toThrowError(`Template parse errors: There is no directive with "exportAs" set to "dirA" ("
]#a="dirA">
"): TestComp@0:5`); - }); + }); - it('should report invalid reference names', () => { - expect(() => parse('
', [])).toThrowError(`Template parse errors: + it('should report invalid reference names', () => { + expect(() => parse('
', [])).toThrowError(`Template parse errors: "-" is not allowed in reference names ("
]#a-b>
"): TestComp@0:5`); - }); + }); - it('should report variables as errors', () => { - expect(() => parse('
', [])).toThrowError(`Template parse errors: + it('should report variables as errors', () => { + expect(() => parse('
', [])).toThrowError(`Template parse errors: "let-" is only supported on template elements. ("
]let-a>
"): TestComp@0:5`); - }); + }); - it('should report duplicate reference names', () => { - expect(() => parse('
', [])) - .toThrowError(`Template parse errors: + it('should report duplicate reference names', () => { + expect(() => parse('
', [])) + .toThrowError(`Template parse errors: Reference "#a" is defined several times ("
]#a>
"): TestComp@0:19`); - }); + }); - it('should not throw error when there is same reference name in different templates', - () => { - expect(() => parse('
', [])) - .not.toThrowError(); - - }); - - it('should assign references with empty value to components', () => { - var dirA = CompileDirectiveMetadata.create({ - selector: '[a]', - isComponent: true, - type: new CompileTypeMetadata( - {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}), - exportAs: 'dirA', - template: new CompileTemplateMetadata({ngContentSelectors: []}) - }); - expect(humanizeTplAst(parse('
', [dirA]))).toEqual([ - [ElementAst, 'div'], - [AttrAst, 'a', ''], - [ReferenceAst, 'a', identifierToken(dirA.type)], - [DirectiveAst, dirA], - ]); - }); + it('should not throw error when there is same reference name in different templates', + () => { + expect(() => parse('
', [])) + .not.toThrowError(); + + }); + + it('should assign references with empty value to components', () => { + var dirA = CompileDirectiveMetadata.create({ + selector: '[a]', + isComponent: true, + type: new CompileTypeMetadata( + {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}), + exportAs: 'dirA', + template: new CompileTemplateMetadata({ngContentSelectors: []}) + }); + expect(humanizeTplAst(parse('
', [dirA]))).toEqual([ + [ElementAst, 'div'], + [AttrAst, 'a', ''], + [ReferenceAst, 'a', identifierToken(dirA.type)], + [DirectiveAst, dirA], + ]); + }); - it('should not locate directives in references', () => { - var dirA = CompileDirectiveMetadata.create({ - selector: '[a]', - type: new CompileTypeMetadata( - {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}) + it('should not locate directives in references', () => { + var dirA = CompileDirectiveMetadata.create({ + selector: '[a]', + type: new CompileTypeMetadata( + {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}) + }); + expect(humanizeTplAst(parse('
', [dirA]))).toEqual([ + [ElementAst, 'div'], [ReferenceAst, 'a', null] + ]); + }); }); - expect(humanizeTplAst(parse('
', [dirA]))).toEqual([ - [ElementAst, 'div'], [ReferenceAst, 'a', null] - ]); - }); - }); - describe('explicit templates', () => { - it('should create embedded templates for