Compare commits
130 Commits
Author | SHA1 | Date | |
---|---|---|---|
07bd4b0630 | |||
df1718d624 | |||
17e3410d98 | |||
5effc330ed | |||
3df00828d7 | |||
8c477b2f45 | |||
7787771aba | |||
7275e1beb3 | |||
12ba62e5e2 | |||
e6e007e2f1 | |||
91dd138fa5 | |||
d972d82354 | |||
bdcf46f82e | |||
79e1c7b807 | |||
d22eeb70b8 | |||
aa92512ac6 | |||
f782b08f58 | |||
4202936bbf | |||
e1faca6386 | |||
f5b0e22d35 | |||
00693d70a2 | |||
bcef5efffe | |||
13ecc140e8 | |||
709a6dea06 | |||
16cfb88c00 | |||
efee6f5199 | |||
2aa8aae76d | |||
afb4bd9ef6 | |||
d641c36a45 | |||
f4566f8128 | |||
a67c06708d | |||
d9d57d71dd | |||
e06303a987 | |||
40b92ddf21 | |||
1681e4f57f | |||
71b7654660 | |||
eaaec6979c | |||
c587c63591 | |||
f50c1da4e2 | |||
0254ce1f6c | |||
c9b765f5c0 | |||
8c975ed156 | |||
bb35fcb562 | |||
57230b70a9 | |||
43dc60ce4f | |||
230b3b73d8 | |||
0b7dc2f9ff | |||
de1f44f51f | |||
f1cfddf6d6 | |||
ef621a2f00 | |||
df9761951b | |||
f786c560f1 | |||
c5557de3e7 | |||
ec3a5b54de | |||
cf269d9ff4 | |||
5fa5ffb82a | |||
4a57dcfd8d | |||
43923ffcf5 | |||
50c37d45dc | |||
a63359689f | |||
43d3a84df3 | |||
8310c91823 | |||
b64b5ece65 | |||
ed9c2b6281 | |||
1cf5f5fa38 | |||
a32078f85e | |||
decd129a4d | |||
c3c9ecb302 | |||
af520947aa | |||
040bf57966 | |||
65a60b7456 | |||
756ef09d12 | |||
9316f95467 | |||
83d94b7504 | |||
a121136fae | |||
a6bb84e02b | |||
3898dc488e | |||
ca3f9926f9 | |||
1c012a035f | |||
38c5304b7f | |||
9a049be67f | |||
2045c9e8ee | |||
6c4ec05a4a | |||
f7bfda31ff | |||
a92b573309 | |||
4fd13d71c8 | |||
bf7b82b658 | |||
c143fee849 | |||
0286956107 | |||
e884f4854d | |||
df1822fc2a | |||
42b4b6d21b | |||
36bc2ff269 | |||
1564042fe8 | |||
41c8c30973 | |||
61129fa12d | |||
3a5b4882bc | |||
425c1e6042 | |||
58605cf350 | |||
34b31dea7c | |||
a241ab7c07 | |||
745e10e6d2 | |||
33340dbbd1 | |||
52812c08e2 | |||
52f5ae1961 | |||
9be895b6da | |||
9f1c82537e | |||
5ab5cc77bb | |||
f1b6c6efa1 | |||
45ad13560b | |||
2045268cec | |||
fb1076b44a | |||
6fc46526ae | |||
3ef5ede6d6 | |||
136621ebc9 | |||
f23b22a0f4 | |||
0ca971c5bd | |||
3a6fcee0e6 | |||
8972137c29 | |||
cc6481077f | |||
c041b93418 | |||
bc33765913 | |||
31dce72b7b | |||
212f8dbde7 | |||
44da4984f9 | |||
d95344430c | |||
131626fc61 | |||
676bb0fa7d | |||
5a849829c4 | |||
671f73448c |
20
.github/ISSUE_TEMPLATE.md
vendored
20
.github/ISSUE_TEMPLATE.md
vendored
@ -1,3 +1,7 @@
|
||||
<!--
|
||||
IF YOU DON'T FILL OUT THE FOLLOWING INFORMATION WE MIGHT CLOSE YOUR ISSUE WITHOUT INVESTIGATING
|
||||
-->
|
||||
|
||||
**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**
|
||||
<!-- Describe how the bug manifests. -->
|
||||
|
||||
**Expected behavior**
|
||||
<!-- Describe what the behavior would be without the bug. -->
|
||||
|
||||
**Reproduction of the problem**
|
||||
<!-- If the current behavior is a bug or you can illustrate your feature request better with an example, please provide the steps to reproduce and if possible a minimal demo of the problem via https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5). -->
|
||||
**Minimal reproduction of the problem with instructions**
|
||||
<!--
|
||||
If the current behavior is a bug or you can illustrate your feature request better with an example,
|
||||
please provide the *STEPS TO REPRODUCE* and if possible a *MINIMAL DEMO* of the problem via
|
||||
https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5).
|
||||
-->
|
||||
|
||||
**What is the motivation / use case for changing the behavior?**
|
||||
<!-- Describe the motivation or the concrete use case -->
|
||||
@ -20,12 +28,12 @@
|
||||
**Please tell us about your environment:**
|
||||
<!-- Operating system, IDE, package manager, HTTP server, ... -->
|
||||
|
||||
* **Angular version:** 2.0.X
|
||||
* **Angular version:** 2.0.X
|
||||
<!-- Check whether this is still an issue in the most recent Angular version -->
|
||||
|
||||
* **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 ]
|
||||
<!-- All browsers where this could be reproduced -->
|
||||
|
||||
* **Language:** [all | TypeScript X.X | ES6/7 | ES5]
|
||||
|
||||
|
||||
* **Node (for AoT issues):** `node --version` =
|
||||
|
95
CHANGELOG.md
95
CHANGELOG.md
@ -1,3 +1,78 @@
|
||||
<a name="2.1.0"></a>
|
||||
# [2.1.0](https://github.com/angular/angular/compare/2.1.0-rc.0...2.1.0) (2016-10-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **benchmarks:** allow ng2_switch benchmark to be used with AoT. ([#12124](https://github.com/angular/angular/issues/12124)) ([de1f44f](https://github.com/angular/angular/commit/de1f44f))
|
||||
* **compiler:** allow whitespace as `<ng-content>` content ([#12225](https://github.com/angular/angular/issues/12225)) ([df1718d](https://github.com/angular/angular/commit/df1718d))
|
||||
* **compiler:** interpolation expressions report the correct offset ([#12125](https://github.com/angular/angular/issues/12125)) ([d641c36](https://github.com/angular/angular/commit/d641c36))
|
||||
* **compiler:** properly shim `:host:before` and `:host(:before)` ([#12171](https://github.com/angular/angular/issues/12171)) ([aa92512](https://github.com/angular/angular/commit/aa92512)), closes [#12165](https://github.com/angular/angular/issues/12165)
|
||||
* **compiler:** validate `[@HostBinding](https://github.com/HostBinding)` name ([#12139](https://github.com/angular/angular/issues/12139)) ([13ecc14](https://github.com/angular/angular/commit/13ecc14))
|
||||
* **compiler-cli:** don't clone static symbols when simplifying annotation metadata ([#12158](https://github.com/angular/angular/issues/12158)) ([8c477b2](https://github.com/angular/angular/commit/8c477b2))
|
||||
* **compiler-cli:** remove peerDependency on [@angular](https://github.com/angular)/platform-server ([#12122](https://github.com/angular/angular/issues/12122)) ([71b7654](https://github.com/angular/angular/commit/71b7654))
|
||||
* **compiler-cli:** remove unused parse5 dependency from package.json ([eaaec69](https://github.com/angular/angular/commit/eaaec69))
|
||||
* **forms:** allow optional fields with pattern and minlength validators ([#12147](https://github.com/angular/angular/issues/12147)) ([d22eeb7](https://github.com/angular/angular/commit/d22eeb7))
|
||||
* **forms:** properly validate blank strings with minlength ([#12091](https://github.com/angular/angular/issues/12091)) ([f50c1da](https://github.com/angular/angular/commit/f50c1da))
|
||||
* **http:** fix Headers initialization from Headers and Object ([#12106](https://github.com/angular/angular/issues/12106)) ([f4566f8](https://github.com/angular/angular/commit/f4566f8))
|
||||
* **http:** Headers.append should append to the list ([a67c067](https://github.com/angular/angular/commit/a67c067))
|
||||
* **platform-browser-dynamic:** mark platformBrowserDynamic as stable API ([#12154](https://github.com/angular/angular/issues/12154)) ([bcef5ef](https://github.com/angular/angular/commit/bcef5ef))
|
||||
* **router:** improve error message ([#12102](https://github.com/angular/angular/issues/12102)) ([e06303a](https://github.com/angular/angular/commit/e06303a))
|
||||
* **router:** parent resolve should complete before merging resolved data ([1681e4f](https://github.com/angular/angular/commit/1681e4f)), closes [#12032](https://github.com/angular/angular/issues/12032)
|
||||
* **router:** wildcards routes should support lazy loading ([40b92dd](https://github.com/angular/angular/commit/40b92dd)), closes [#12024](https://github.com/angular/angular/issues/12024)
|
||||
* **upgrade:** allow compilerOptions in bootstrap ([#10575](https://github.com/angular/angular/issues/10575)) ([5effc33](https://github.com/angular/angular/commit/5effc33))
|
||||
|
||||
|
||||
<a name="2.1.0-rc.0"></a>
|
||||
# [2.1.0-rc.0](https://github.com/angular/angular/compare/2.1.0-beta.0...2.1.0-rc.0) (2016-10-05)
|
||||
|
||||
### Features
|
||||
|
||||
* **animations:** provide aliases for `:enter` and `:leave` transitions ([#11991](https://github.com/angular/angular/issues/11991)) ([e884f48](https://github.com/angular/angular/commit/e884f48))
|
||||
|
||||
Note: 2.1.0-rc.0 release also contains all the changes present in the 2.0.2 release.
|
||||
|
||||
|
||||
|
||||
<a name="2.0.2"></a>
|
||||
## [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))
|
||||
* **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` and `@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)
|
||||
* **compiler:** fix `<x>` ctype names ([7578d85](https://github.com/angular/angular/commit/7578d85)), closes [#12000](https://github.com/angular/angular/issues/12000)
|
||||
* **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))
|
||||
* **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)
|
||||
* **http:** remove url params if provided value is null or undefined ([#11990](https://github.com/angular/angular/issues/11990)) ([9cc0a4e](https://github.com/angular/angular/commit/9cc0a4e))
|
||||
* **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))
|
||||
* **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)
|
||||
|
||||
|
||||
|
||||
<a name="2.1.0-beta.0"></a>
|
||||
# [2.1.0-beta.0](https://github.com/angular/angular/compare/2.0.0...2.1.0-beta.0) (2016-09-23)
|
||||
|
||||
### Features
|
||||
|
||||
* **router:** add router preloader to optimistically preload routes ([5a84982](https://github.com/angular/angular/commit/5a84982))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
* **router:** update the router not to reset router state when updating root component ([#11799](https://github.com/angular/angular/issues/11799)) ([31dce72](https://github.com/angular/angular/commit/31dce72))
|
||||
|
||||
Note: 2.1.0-beta.0 release also contains all the changes present in the 2.0.1 release.
|
||||
|
||||
|
||||
|
||||
<a name="2.0.1"></a>
|
||||
## [2.0.1](https://github.com/angular/angular/compare/2.0.0...2.0.1) (2016-09-23)
|
||||
|
||||
@ -9,7 +84,7 @@
|
||||
* **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 [#1645](https://github.com/angular/angular/issues/1645)
|
||||
* **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))
|
||||
@ -231,7 +306,7 @@
|
||||
use `Type<any>` in place of `Type`.
|
||||
|
||||
We don't expect that any user applications use the `Type` type directly.
|
||||
|
||||
|
||||
* core: Previously inconsistently named APIs SanitizationService and DomSanitizationService were renamed to Sanitizer and DomSanitizer
|
||||
|
||||
* core: previously deprecated @Component.directives and @Component.pipes support was removed.
|
||||
@ -245,11 +320,11 @@ use `Type<any>` in place of `Type`.
|
||||
* core: deprecated ComponentResolver was removed. Please use ComponentFactoryResolver instead.
|
||||
|
||||
* core: animations defined using an at-symbol prefix that are not property bound are now invalid.
|
||||
|
||||
|
||||
```html
|
||||
<!-- this is now invalid -->
|
||||
<div @flip="flipState"></div>
|
||||
|
||||
|
||||
<!-- change that to -->
|
||||
<div [@flip]="flipState"></div>
|
||||
```
|
||||
@ -275,7 +350,7 @@ prefix using `animate-` must now be preixed using `bind-animate-`.
|
||||
```
|
||||
{provide: MyClass, useFactory: ...}
|
||||
```
|
||||
|
||||
|
||||
* core: previously deprecated NgZoneError has been removed
|
||||
|
||||
* core: Exceptions are no longer part of the public API. We don't expect that anyone should be referring to the Exception types.
|
||||
@ -287,7 +362,7 @@ prefix using `animate-` must now be preixed using `bind-animate-`.
|
||||
```
|
||||
ErrorHandler.handleError(error: any): void;
|
||||
```
|
||||
|
||||
|
||||
* core: deprecated DynamicComponentLoader was removed; see deprecation notice for migration instructions.
|
||||
|
||||
* core: deprecated SystemJsComponentResolver and SystemJsCmpFactoryResolver have been removed.
|
||||
@ -321,13 +396,13 @@ prefix using `animate-` must now be preixed using `bind-animate-`.
|
||||
* platform-browser-dynamic: `CACHED_TEMPLATE_PROVIDER` is now renamed to `RESOURCE_CACHE_PROVIDER`
|
||||
|
||||
Before:
|
||||
|
||||
|
||||
```js
|
||||
import {CACHED_TEMPLATE_PROVIDER} from '@angular/platform-browser-dynamic';
|
||||
```
|
||||
|
||||
|
||||
After:
|
||||
|
||||
|
||||
```js
|
||||
import {RESOURCE_CACHE_PROVIDER} from '@angular/platform-browser-dynamic';
|
||||
```
|
||||
@ -345,7 +420,7 @@ prefix using `animate-` must now be preixed using `bind-animate-`.
|
||||
* webworkers: web worker platform is now exported via separate packages.
|
||||
|
||||
Please use @angular/platform-webworker and @angular/platform-webworker-dynamic
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="2.0.0-rc.5"></a>
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Contributing to Angular 2
|
||||
# Contributing to Angular
|
||||
|
||||
We would love for you to contribute to Angular 2 and help make it even better than it is
|
||||
We would love for you to contribute to Angular and help make it even better than it is
|
||||
today! As a contributor, here are the guidelines we would like you to follow:
|
||||
|
||||
- [Code of Conduct](#coc)
|
||||
@ -17,17 +17,26 @@ Help us keep Angular open and inclusive. Please read and follow our [Code of Con
|
||||
|
||||
## <a name="question"></a> 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 the Angular team's capacity to answer usage questions is limited. We are also available on [Gitter][gitter].
|
||||
Please, do not open issues for the general support questions as we want to keep GitHub issues for bug reports and feature requests. You've got much better chances of getting your question answered on [StackOverflow](stackoverflow.com/questions/tagged/angular) where the questions should be tagged with tag `angular`.
|
||||
|
||||
## <a name="issue"></a> Found an Issue?
|
||||
StackOverflow is a much better place to ask questions since:
|
||||
|
||||
- there are thousands of people willing to help on StackOverflow
|
||||
- questions and answers stay available for public viewing so your question / answer might help someone else
|
||||
- StackOverflow's voting system assures that the best answers are prominently visible.
|
||||
|
||||
To save your and our time we will be systematically closing all the issues that are requests for general support and redirecting people to StackOverflow.
|
||||
|
||||
If you would like to chat about the question in real-time, you can reach out via [our gitter channel][gitter].
|
||||
|
||||
## <a name="issue"></a> Found a Bug?
|
||||
If you find a bug in the source code, you can help us by
|
||||
[submitting an issue](#submit-issue) to our [GitHub Repository][github]. Even better, you can
|
||||
[submit a Pull Request](#submit-pr) with a fix.
|
||||
|
||||
## <a name="feature"></a> 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 name="feature"></a> Missing a Feature?
|
||||
You can *request* a new feature by [submitting an issue](#submit-issue) to our GitHub
|
||||
Repository. 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.
|
||||
Please consider what kind of change it is:
|
||||
|
||||
@ -39,24 +48,22 @@ and help you to craft the change so that it is successfully accepted into the pr
|
||||
## <a name="submit"></a> Submission Guidelines
|
||||
|
||||
### <a name="submit-issue"></a> Submitting an Issue
|
||||
Before you submit an issue, search the archive, maybe your question was already answered.
|
||||
|
||||
If your issue appears to be a bug, and hasn't been reported, open a new issue.
|
||||
Help us to maximize the effort we can spend fixing issues and adding new
|
||||
features, by not reporting duplicate issues. Providing the following information will increase the
|
||||
chances of your issue being dealt with quickly:
|
||||
Before you submit an issue, please search the issue tracker, maybe an issue for your problem already exists and the discussion might inform you of workarounds readily available.
|
||||
|
||||
* **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps
|
||||
* **Angular Version** - what version of Angular is affected (e.g. 2.0.0-alpha.53)
|
||||
* **Motivation for or Use Case** - explain what are you trying to do and why the current behavior is a bug for you
|
||||
* **Browsers and Operating System** - is this a problem with all browsers?
|
||||
* **Reproduce the Error** - provide a live example (using [Plunker][plunker],
|
||||
[JSFiddle][jsfiddle] or [Runnable][runnable]) or a unambiguous set of steps
|
||||
* **Related Issues** - has a similar issue been reported before?
|
||||
* **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be
|
||||
causing the problem (line of code or commit)
|
||||
We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it. In order to reproduce bugs we will systematically ask you to provide a minimal reproduction scenario using http://plnkr.co. Having a live, reproducible scenario gives us wealth of important information without going back & forth to you with additional questions like:
|
||||
|
||||
You can file new issues by providing the above information [here](https://github.com/angular/angular/issues/new).
|
||||
- version of Angular used
|
||||
- 3rd-party libraries and their versions
|
||||
- and most importantly - a use-case that fails
|
||||
|
||||
A minimal reproduce scenario using http://plnkr.co/ allows us to quickly confirm a bug (or point out coding problem) as well as confirm that we are fixing the right problem. If plunker is not a suitable way to demostrate the problem (for example for issues related to our npm packaging), please create a standalone git repository demostrating the problem.
|
||||
|
||||
We will be insisting on a minimal reproduce scenario in order to save maintainers time and ultimately be able to fix more bugs. Interestingly, from our experience users often find coding problems themselves while preparing a minimal plunk. We understand that sometimes it might be hard to extract essentials bits of code from a larger code-base but we really need to isolate the problem before we can fix it.
|
||||
|
||||
Unfortunately we are not able to investigate / fix bugs without a minimal reproductions, so if we don't hear back from you we are going to close an issue that don't have enough info to be reproduced.
|
||||
|
||||
You can file new issues by filling out our [new issue form](https://github.com/angular/angular/issues/new).
|
||||
|
||||
|
||||
### <a name="submit-pr"></a> Submitting a Pull Request (PR)
|
||||
@ -94,7 +101,7 @@ Before you submit your Pull Request (PR) consider the following guidelines:
|
||||
* In GitHub, send a pull request to `angular:master`.
|
||||
* If we suggest changes then:
|
||||
* Make the required updates.
|
||||
* Re-run the Angular 2 test suites to ensure tests are still passing.
|
||||
* Re-run the Angular test suites to ensure tests are still passing.
|
||||
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
|
||||
|
||||
```shell
|
||||
|
@ -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
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
[](http://issuestats.com/github/angular/angular)
|
||||
[](http://issuestats.com/github/angular/angular)
|
||||
[](https://badge.fury.io/js/%40angular%2Fcore)
|
||||
[](https://npmjs.org/package/angular2)
|
||||
|
||||
[](https://saucelabs.com/u/angular2-ci)
|
||||
*Safari (7+), iOS (7+), Edge (14) and IE mobile (11) are tested on [BrowserStack][browserstack].*
|
||||
|
62
SAVED_REPLIES.md
Normal file
62
SAVED_REPLIES.md
Normal file
@ -0,0 +1,62 @@
|
||||
# Saved Responses for Angular's Issue Tracker
|
||||
|
||||
The following are canned responses that the Angular team should use to close issues on our issue tracker that fall into the listed resolution categories.
|
||||
|
||||
Since GitHub currently doesn't allow us to have a repository-wide or organization-wide list of [saved replies](https://help.github.com/articles/working-with-saved-replies/), these replies need to be maintained by individual team members. Since the responses can be modified in the future, all responses are versioned to simplify the process of keeping the responses up to date.
|
||||
|
||||
|
||||
## Angular: Already Fixed (v1)
|
||||
```
|
||||
Thanks for reporting this issue. Luckily it has already been fixed in one of the recent releases. Please update to the most recent version to resolve the problem.
|
||||
|
||||
If after upgrade the problem still exists in your application please open a new issue and provide a plunker reproducing the problem and describing the difference between the expected and current behavior. You can use this plunker template: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5?p=catalogue
|
||||
```
|
||||
|
||||
## Angular: Don't Understand (v1)
|
||||
```
|
||||
I'm sorry but we don't understand the problem you are reporting.
|
||||
|
||||
If the problem still exists please open a new issue and provide a plunker reproducing the problem and describing the difference between the expected and current behavior. You can use this plunker template: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5?p=catalogue
|
||||
```
|
||||
|
||||
|
||||
## Angular: Duplicate (v1)
|
||||
```
|
||||
Thanks for reporting this issue. However this issue is a duplicate of an existing issue #<ISSUE_NUMBER>. Please subscribe to that issue for future updates.
|
||||
```
|
||||
|
||||
|
||||
## Angular: Insufficient Information Provided (v1)
|
||||
```
|
||||
Thanks for reporting this issue. However, you didn't provide sufficient information for us to understand and reproduce the problem. Please check out [our submission guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-submitting-an-issue) to understand why we can't act on issues that are lacking important information.
|
||||
|
||||
If the problem still persists, please file a new issue and ensure you provide all of the required information when filling out the issue template.
|
||||
```
|
||||
|
||||
## Angular: Issue Outside of Angular (v1)
|
||||
```
|
||||
I'm sorry but this issue is not caused by Angular. Please contact the author(s) of project <PROJECT NAME> or file issue on their issue tracker.
|
||||
```
|
||||
|
||||
|
||||
## Angular: Non-reproducible (v1)
|
||||
```
|
||||
I'm sorry but we can't reproduce the problem following the instructions you provided.
|
||||
|
||||
If the problem still exists please open a new issue following [our submission guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-submitting-an-issue).
|
||||
```
|
||||
|
||||
## Angular: Obsolete (v1)
|
||||
```
|
||||
Thanks for reporting this issue. This issue is now obsolete due to changes in the recent releases. Please update to the most recent Angular version.
|
||||
|
||||
If the problem still persists, please file a new issue and ensure you provide the version of Angular affected and include the steps to reproduce the problem when filling out the issue template.
|
||||
```
|
||||
|
||||
|
||||
## Angular: Support Request (v1)
|
||||
```
|
||||
Hello, we reviewed this issue and determined that it doesn't fall into the bug report or feature request category. This issue tracker is not suitable for support requests, please repost your issue on [StackOverflow](http://stackoverflow.com/) using tag `angular`.
|
||||
|
||||
If you are wondering why we don't resolve support issues via the issue tracker, please [check out this explanation](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-got-a-question-or-problem).
|
||||
```
|
@ -1,7 +1,7 @@
|
||||
# Triage Process and Github Labels for Angular 2
|
||||
|
||||
This document describes how the Angular team uses labels and milestones
|
||||
to triage issues on github. The basic idea of the new process is that
|
||||
to triage issues on github. The basic idea of the process is that
|
||||
caretaker only assigns a component and type (bug, feature) label. The
|
||||
owner of the component than is in full control of how the issues should
|
||||
be triaged further.
|
||||
@ -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: docs`: `@naomiblack`
|
||||
* `comp: packaging`: `@IgorMinar`
|
||||
* `comp: performance`: `@tbosch`
|
||||
* `comp: security`: `@IgorMinar`
|
||||
|
||||
@ -53,11 +53,11 @@ What kind of problem is this?
|
||||
|
||||
## Caretaker Triage Process
|
||||
|
||||
It is the caretaker's responsibility to assign `comp: *` and `type: *`
|
||||
to each new issue as they come in. The reason why we limit the
|
||||
responsibility of the caretaker to these two labels is that it is
|
||||
unlikely that without domain knowledge the caretaker could add any
|
||||
additional labels of value.
|
||||
It is the caretaker's responsibility to assign `comp: *` to each new
|
||||
issue as they come in. The reason why we limit the responsibility of the
|
||||
caretaker to this one label is that it is likely that without domain
|
||||
knowledge the caretaker could mislabel issues or lack knowledge of
|
||||
duplicate issues.
|
||||
|
||||
|
||||
## Component's owner Triage Process
|
||||
@ -68,11 +68,37 @@ process for their component.
|
||||
It will be up to the component owner to determine the order in which the
|
||||
issues within the component will be resolved.
|
||||
|
||||
Several owners have adopted the issue categorization based on
|
||||
[user pain](http://www.lostgarden.com/2008/05/improving-bug-triage-with-user-pain.html)
|
||||
used by Angular 1. In this system every issue is assigned frequency and
|
||||
severity based on which the total user pain score is calculated.
|
||||
|
||||
Following is the definition of various frequency and severity levels:
|
||||
|
||||
1. `freq(score): *` – How often does this issue come up? How many developers does this affect?
|
||||
* low (1) - obscure issue affecting a handful of developers
|
||||
* moderate (2) - impacts auxiliary usage patterns, only small number of applications are affected
|
||||
* high (3) - impacts primary usage patterns, affecting most Angular apps
|
||||
* critical (4) - impacts all Angular apps
|
||||
1. `severity(score): *` - How bad is the issue?
|
||||
* inconvenience (1) - causes ugly/boilerplate code in apps
|
||||
* confusing (2) - unexpected or inconsistent behavior; hard-to-debug
|
||||
* broken expected use (3) - it's hard or impossible for a developer using Angular to accomplish something that Angular should be able to do
|
||||
* memory leak (4)
|
||||
* regression (5) - functionality that used to work no longer works in a new release due to an unintentional change
|
||||
* security issue (6)
|
||||
|
||||
|
||||
These criteria are then used to calculate a "user pain" score as follows:
|
||||
|
||||
`pain = severity × frequency`
|
||||
|
||||
|
||||
### Assigning Issues to Milestones
|
||||
|
||||
Any issue that is being worked on must have:
|
||||
|
||||
* An `assignee`: The person doing the work.
|
||||
* An `Assignee`: The person doing the work.
|
||||
* A `Milestone`: When we expect to complete this work.
|
||||
|
||||
We aim to only have at most three milestones open at a time:
|
||||
|
@ -1,242 +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}},
|
||||
'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}}
|
||||
'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_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'
|
||||
},
|
||||
'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_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_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',
|
||||
@ -251,54 +107,31 @@ 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', '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'],
|
||||
'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', 'SL_IOS10'],
|
||||
@ -310,9 +143,16 @@ 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', 'BS_SAFARI10'],
|
||||
'MOBILE': ['BS_ANDROID4.3', 'BS_ANDROID4.4', 'BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_IOS10', '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', 'BS_IOS10'],
|
||||
@ -329,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());
|
||||
}
|
||||
|
40
docs/PUBLIC_API.md
Normal file
40
docs/PUBLIC_API.md
Normal file
@ -0,0 +1,40 @@
|
||||
# Supported Public API Surface of Angular
|
||||
|
||||
Our SemVer, timed-release cycle and deprecation policy currently applies to these npm packages:
|
||||
|
||||
- `@angular/core`
|
||||
- `@angular/common`
|
||||
- `@angular/platform-browser`
|
||||
- `@angular/platform-browser-dynamic`
|
||||
- `@angular/platform-server`
|
||||
- `@angular/platform-webworker`
|
||||
- `@angular/platform-webworker-dynamic`
|
||||
- `@angular/upgrade`
|
||||
- `@angular/router`
|
||||
- `@angular/forms`
|
||||
- `@angular/http`
|
||||
|
||||
|
||||
One intentional omission from this list is `@angular/compiler`, which is currently considered a low level api and is subject to internal changes. These changes will not affect any applications or libraries using the higher-level apis (the command line interface or JIT compilation via `@angular/platform-browser-dynamic`). Only very specific use-cases require direct access to the compiler API (mostly tooling integration for IDEs, linters, etc). If you are working on this kind of integration, please reach out to us first.
|
||||
|
||||
Additionally only the command line usage (not direct use of APIs) of `@angular/compiler-cli` is covered.
|
||||
|
||||
Other projects developed by the Angular team like angular-cli, Angular Material, benchpress, will be covered by these or similar guarantees in the future as they mature.
|
||||
|
||||
Within the supported packages, we provide guarantees for:
|
||||
|
||||
- symbols exported via the main entry point (e.g. `@angular/core`) and testing entry point (e.g. `@angular/core/testing`). This applies to both runtime/JavaScript values and TypeScript types.
|
||||
- symbols exported via global namespace `ng` (e.g. `ng.core`)
|
||||
- bundles located in the `bundles/` directory of our npm packages (e.g. `@angular/core/bundles/core.umd.js`)
|
||||
|
||||
|
||||
We explicitly don't consider the following to be our public API surface:
|
||||
|
||||
- any file/import paths within our package except for the `/`, `/testing` and `/bundles/*`
|
||||
- constructors of injectable classes (services and directives) - please use DI to obtain instances of these classes
|
||||
- any class members or symbols marked as `private` or prefixed with underscore
|
||||
- extending any of our classes unless the support for this is specifically documented in the API docs
|
||||
- the contents and API surface of the code generated by Angular's compiler (with one notable exception: the existence and name of `NgModuleFactory` instances exported from generated code is guaranteed)
|
||||
|
||||
|
||||
Our peer dependencies (e.g. typescript, zone.js, or rxjs) are not considered part of our API surface, but they are included in our SemVer policies. We might update the required version of any of these dependencies in minor releases if the update doesn't cause breaking changes for Angular applications. Peer dependency updates that result in non-trivial breaking changes must be deferred to major Angular releases.
|
146
gulpfile.js
146
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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -5,7 +5,7 @@ See [here for an example project](https://github.com/angular/benchpress-tree).
|
||||
|
||||
The sources for this package are in the main [Angular2](https://github.com/angular/angular) repo. Please file issues and pull requests against that repo.
|
||||
|
||||
License: Apache MIT 2.0
|
||||
License: MIT
|
||||
|
||||
# Why?
|
||||
|
||||
|
@ -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},
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
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}`)))
|
||||
|
@ -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';
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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)}%`;
|
||||
}
|
||||
}
|
||||
|
@ -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';
|
||||
|
@ -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]; });
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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';
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
import {StringWrapper, isPresent} from '../facade/lang';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {WebDriverAdapter} from '../web_driver_adapter';
|
||||
import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_extension';
|
||||
|
||||
@ -48,6 +48,6 @@ export class FirefoxDriverExtension extends WebDriverExtension {
|
||||
perfLogFeatures(): PerfLogFeatures { return new PerfLogFeatures({render: true, gc: true}); }
|
||||
|
||||
supports(capabilities: {[key: string]: any}): boolean {
|
||||
return StringWrapper.equals(capabilities['browserName'].toLowerCase(), 'firefox');
|
||||
return capabilities['browserName'].toLowerCase() === 'firefox';
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
import {StringWrapper, isBlank, isPresent} from '../facade/lang';
|
||||
import {isBlank, isPresent} from '../facade/lang';
|
||||
import {WebDriverAdapter} from '../web_driver_adapter';
|
||||
import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_extension';
|
||||
|
||||
@ -42,7 +42,7 @@ export class IOsDriverExtension extends WebDriverExtension {
|
||||
var records: any[] = [];
|
||||
entries.forEach(entry => {
|
||||
var message = JSON.parse(entry['message'])['message'];
|
||||
if (StringWrapper.equals(message['method'], 'Timeline.eventRecorded')) {
|
||||
if (message['method'] === 'Timeline.eventRecorded') {
|
||||
records.push(message['params']['record']);
|
||||
}
|
||||
});
|
||||
@ -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) => {
|
||||
@ -62,19 +62,16 @@ export class IOsDriverExtension extends WebDriverExtension {
|
||||
var startTime = record['startTime'];
|
||||
var endTime = record['endTime'];
|
||||
|
||||
if (StringWrapper.equals(type, 'FunctionCall') &&
|
||||
(isBlank(data) || !StringWrapper.equals(data['scriptName'], 'InjectedScript'))) {
|
||||
if (type === 'FunctionCall' && (isBlank(data) || data['scriptName'] !== 'InjectedScript')) {
|
||||
events.push(createStartEvent('script', startTime));
|
||||
endEvent = createEndEvent('script', endTime);
|
||||
} else if (StringWrapper.equals(type, 'Time')) {
|
||||
} else if (type === 'Time') {
|
||||
events.push(createMarkStartEvent(data['message'], startTime));
|
||||
} else if (StringWrapper.equals(type, 'TimeEnd')) {
|
||||
} else if (type === 'TimeEnd') {
|
||||
events.push(createMarkEndEvent(data['message'], startTime));
|
||||
} else if (
|
||||
StringWrapper.equals(type, 'RecalculateStyles') || StringWrapper.equals(type, 'Layout') ||
|
||||
StringWrapper.equals(type, 'UpdateLayerTree') || StringWrapper.equals(type, 'Paint') ||
|
||||
StringWrapper.equals(type, 'Rasterize') ||
|
||||
StringWrapper.equals(type, 'CompositeLayers')) {
|
||||
type === 'RecalculateStyles' || type === 'Layout' || type === 'UpdateLayerTree' ||
|
||||
type === 'Paint' || type === 'Rasterize' || type === 'CompositeLayers') {
|
||||
events.push(createStartEvent('render', startTime));
|
||||
endEvent = createEndEvent('render', endTime);
|
||||
}
|
||||
@ -92,7 +89,7 @@ export class IOsDriverExtension extends WebDriverExtension {
|
||||
perfLogFeatures(): PerfLogFeatures { return new PerfLogFeatures({render: true}); }
|
||||
|
||||
supports(capabilities: {[key: string]: any}): boolean {
|
||||
return StringWrapper.equals(capabilities['browserName'].toLowerCase(), 'safari');
|
||||
return capabilities['browserName'].toLowerCase() === 'safari';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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,11 +27,11 @@ 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)) {
|
||||
if (!microMetrics) {
|
||||
microMetrics = {};
|
||||
}
|
||||
var providers: Provider[] = [
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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,11 +17,11 @@ 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)) {
|
||||
if (!userMetrics) {
|
||||
userMetrics = {};
|
||||
}
|
||||
wdAdapter = new MockDriverAdapter();
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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'])
|
||||
|
@ -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([
|
||||
|
@ -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(
|
||||
@ -212,7 +212,7 @@ function createCountingValidator(
|
||||
return new MockValidator(log, (completeSample: MeasureValues[]) => {
|
||||
count--;
|
||||
if (count === 0) {
|
||||
return isPresent(validSample) ? validSample : completeSample;
|
||||
return validSample || completeSample;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@ -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 {
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 {Options, ReflectiveInjector, WebDriverExtension} from '../index';
|
||||
import {StringWrapper, isPresent} from '../src/facade/lang';
|
||||
import {isPresent} from '../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
function createExtension(ids: any[], caps: any) {
|
||||
@ -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)
|
||||
])
|
||||
@ -52,6 +52,6 @@ class MockExtension extends WebDriverExtension {
|
||||
constructor(public id: string) { super(); }
|
||||
|
||||
supports(capabilities: {[key: string]: any}): boolean {
|
||||
return StringWrapper.equals(capabilities['browser'], this.id);
|
||||
return capabilities['browser'] === this.id;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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};
|
||||
|
@ -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'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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',
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -67,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);
|
||||
}
|
||||
|
@ -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', () => {
|
||||
|
@ -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';
|
||||
|
@ -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<any>;
|
||||
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) => {
|
||||
|
@ -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');
|
||||
});
|
||||
|
||||
|
@ -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', () => {
|
||||
|
@ -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', () => {
|
||||
|
@ -11,7 +11,7 @@ import {Component} from '@angular/core';
|
||||
import {TestBed, async} from '@angular/core/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
|
||||
import {Json, StringWrapper} from '../../src/facade/lang';
|
||||
import {Json} from '../../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
describe('JsonPipe', () => {
|
||||
@ -20,7 +20,7 @@ export function main() {
|
||||
var inceptionObjString: string;
|
||||
var pipe: JsonPipe;
|
||||
|
||||
function normalize(obj: string): string { return StringWrapper.replace(obj, regNewLine, ''); }
|
||||
function normalize(obj: string): string { return obj.replace(regNewLine, ''); }
|
||||
|
||||
beforeEach(() => {
|
||||
inceptionObj = {dream: {dream: {dream: 'Limbo'}}};
|
||||
|
@ -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', () => {
|
||||
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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', () => {
|
||||
|
@ -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() {
|
||||
|
@ -0,0 +1,6 @@
|
||||
# Overview
|
||||
|
||||
This folder will be filled with the benchmark sources
|
||||
so that we can do offline compilation for them.
|
||||
|
||||
|
@ -36,25 +36,27 @@ const EXPECTED_XMB = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<messagebundle>
|
||||
<msg id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" desc="desc" meaning="meaning">translate me</msg>
|
||||
<msg id="65cc4ab3b4c438e07c89be2b677d08369fb62da2">Welcome</msg>
|
||||
</messagebundle>`;
|
||||
</messagebundle>
|
||||
`;
|
||||
|
||||
const EXPECTED_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<file source-language="en" datatype="plaintext" original="ng2.template">
|
||||
<body>
|
||||
<trans-unit id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" datatype="html">
|
||||
<source>translate me</source>
|
||||
<target/>
|
||||
<note priority="1" from="description">desc</note>
|
||||
<note priority="1" from="meaning">meaning</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="65cc4ab3b4c438e07c89be2b677d08369fb62da2" datatype="html">
|
||||
<source>Welcome</source>
|
||||
<target/>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>`;
|
||||
<file source-language="en" datatype="plaintext" original="ng2.template">
|
||||
<body>
|
||||
<trans-unit id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" datatype="html">
|
||||
<source>translate me</source>
|
||||
<target/>
|
||||
<note priority="1" from="description">desc</note>
|
||||
<note priority="1" from="meaning">meaning</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="65cc4ab3b4c438e07c89be2b677d08369fb62da2" datatype="html">
|
||||
<source>Welcome</source>
|
||||
<target/>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
`;
|
||||
|
||||
describe('template i18n extraction output', () => {
|
||||
const outDir = '';
|
||||
|
@ -15,5 +15,15 @@
|
||||
"declaration": true,
|
||||
"lib": ["es6", "dom"],
|
||||
"baseUrl": "."
|
||||
}
|
||||
},
|
||||
|
||||
"files": [
|
||||
"src/module",
|
||||
"src/bootstrap",
|
||||
"test/all_spec",
|
||||
"benchmarks/src/tree/ng2/index_aot.ts",
|
||||
"benchmarks/src/tree/ng2_switch/index_aot.ts",
|
||||
"benchmarks/src/largetable/ng2/index_aot.ts",
|
||||
"benchmarks/src/largetable/ng2_switch/index_aot.ts"
|
||||
]
|
||||
}
|
||||
|
@ -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']},
|
||||
};
|
||||
|
@ -11,13 +11,11 @@
|
||||
"dependencies": {
|
||||
"@angular/tsc-wrapped": "^0.3.0",
|
||||
"reflect-metadata": "^0.1.2",
|
||||
"parse5": "1.3.2",
|
||||
"minimist": "^1.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^2.0.2",
|
||||
"@angular/compiler": "0.0.0-PLACEHOLDER",
|
||||
"@angular/platform-server": "0.0.0-PLACEHOLDER",
|
||||
"@angular/core": "0.0.0-PLACEHOLDER"
|
||||
},
|
||||
"repository": {
|
||||
|
@ -21,7 +21,7 @@ import {CompileMetadataResolver, DirectiveNormalizer, DomElementSchemaRegistry,
|
||||
import {Console} from './private_import_core';
|
||||
import {ReflectorHost, ReflectorHostContext} from './reflector_host';
|
||||
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
||||
import {StaticReflector, StaticSymbol} from './static_reflector';
|
||||
import {StaticReflector, StaticReflectorHost, StaticSymbol} from './static_reflector';
|
||||
|
||||
const nodeFs = require('fs');
|
||||
|
||||
@ -36,11 +36,26 @@ const PREAMBLE = `/**
|
||||
|
||||
`;
|
||||
|
||||
export class CodeGenerator {
|
||||
export class CodeGeneratorModuleCollector {
|
||||
constructor(
|
||||
private options: AngularCompilerOptions, private program: ts.Program,
|
||||
public host: ts.CompilerHost, private staticReflector: StaticReflector,
|
||||
private compiler: compiler.OfflineCompiler, private reflectorHost: ReflectorHost) {}
|
||||
private staticReflector: StaticReflector, private reflectorHost: StaticReflectorHost,
|
||||
private program: ts.Program, private options: AngularCompilerOptions) {}
|
||||
|
||||
getModuleSymbols(program: ts.Program): {fileMetas: FileMetadata[], ngModules: StaticSymbol[]} {
|
||||
// Compare with false since the default should be true
|
||||
const skipFileNames = (this.options.generateCodeForLibraries === false) ?
|
||||
GENERATED_OR_DTS_FILES :
|
||||
GENERATED_FILES;
|
||||
let filePaths = this.program.getSourceFiles()
|
||||
.filter(sf => !skipFileNames.test(sf.fileName))
|
||||
.map(sf => this.reflectorHost.getCanonicalFileName(sf.fileName));
|
||||
const fileMetas = filePaths.map((filePath) => this.readFileMetadata(filePath));
|
||||
const ngModules = fileMetas.reduce((ngModules, fileMeta) => {
|
||||
ngModules.push(...fileMeta.ngModules);
|
||||
return ngModules;
|
||||
}, <StaticSymbol[]>[]);
|
||||
return {fileMetas, ngModules};
|
||||
}
|
||||
|
||||
private readFileMetadata(absSourcePath: string): FileMetadata {
|
||||
const moduleMetadata = this.staticReflector.getModuleMetadata(absSourcePath);
|
||||
@ -71,6 +86,18 @@ export class CodeGenerator {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export class CodeGenerator {
|
||||
private moduleCollector: CodeGeneratorModuleCollector;
|
||||
|
||||
constructor(
|
||||
private options: AngularCompilerOptions, private program: ts.Program,
|
||||
public host: ts.CompilerHost, private staticReflector: StaticReflector,
|
||||
private compiler: compiler.OfflineCompiler, private reflectorHost: StaticReflectorHost) {
|
||||
this.moduleCollector =
|
||||
new CodeGeneratorModuleCollector(staticReflector, reflectorHost, program, options);
|
||||
}
|
||||
|
||||
// Write codegen in a directory structure matching the sources.
|
||||
private calculateEmitPath(filePath: string): string {
|
||||
@ -95,18 +122,7 @@ export class CodeGenerator {
|
||||
}
|
||||
|
||||
codegen(): Promise<any> {
|
||||
// Compare with false since the default should be true
|
||||
const skipFileNames = (this.options.generateCodeForLibraries === false) ?
|
||||
GENERATED_OR_DTS_FILES :
|
||||
GENERATED_FILES;
|
||||
let filePaths = this.program.getSourceFiles()
|
||||
.filter(sf => !skipFileNames.test(sf.fileName))
|
||||
.map(sf => this.reflectorHost.getCanonicalFileName(sf.fileName));
|
||||
const fileMetas = filePaths.map((filePath) => this.readFileMetadata(filePath));
|
||||
const ngModules = fileMetas.reduce((ngModules, fileMeta) => {
|
||||
ngModules.push(...fileMeta.ngModules);
|
||||
return ngModules;
|
||||
}, <StaticSymbol[]>[]);
|
||||
const {fileMetas, ngModules} = this.moduleCollector.getModuleSymbols(this.program);
|
||||
const analyzedNgModules = this.compiler.analyzeModules(ngModules);
|
||||
return Promise.all(fileMetas.map(
|
||||
(fileMeta) =>
|
||||
@ -126,7 +142,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 +164,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 =
|
||||
@ -183,7 +201,7 @@ export class CodeGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
interface FileMetadata {
|
||||
export interface FileMetadata {
|
||||
fileUrl: string;
|
||||
components: StaticSymbol[];
|
||||
ngModules: StaticSymbol[];
|
||||
|
@ -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';
|
||||
|
||||
@ -44,6 +44,8 @@ export interface StaticReflectorHost {
|
||||
animationMetadata: string,
|
||||
provider: string
|
||||
};
|
||||
|
||||
getCanonicalFileName(fileName: string): string;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -366,6 +368,9 @@ export class StaticReflector implements ReflectorReader {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
if (expression instanceof StaticSymbol) {
|
||||
return expression;
|
||||
}
|
||||
if (expression) {
|
||||
if (expression['__symbolic']) {
|
||||
let staticSymbol: StaticSymbol;
|
||||
|
@ -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';
|
||||
|
@ -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';
|
||||
|
||||
@ -120,6 +119,11 @@ describe('StaticReflector', () => {
|
||||
expect(simplify(noContext, 'some value')).toBe('some value');
|
||||
});
|
||||
|
||||
it('should simplify a static symbol into itself', () => {
|
||||
const staticSymbol = new StaticSymbol('', '');
|
||||
expect(simplify(noContext, staticSymbol)).toBe(staticSymbol);
|
||||
});
|
||||
|
||||
it('should simplify an array into a copy of the array', () => {
|
||||
expect(simplify(noContext, [1, 2, 3])).toEqual([1, 2, 3]);
|
||||
});
|
||||
@ -451,10 +455,13 @@ class MockReflectorHost implements StaticReflectorHost {
|
||||
provider: 'angular2/src/core/di/provider'
|
||||
};
|
||||
}
|
||||
|
||||
getCanonicalFileName(fileName: string): string { return fileName; }
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -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'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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 }),
|
||||
]
|
||||
}
|
||||
};
|
||||
|
@ -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 {ANY_STATE, DEFAULT_STATE, EMPTY_STATE} from '../private_import_core';
|
||||
|
||||
import {AnimationAst, AnimationAstVisitor, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStepAst, AnimationStylesAst} from './animation_ast';
|
||||
import {AnimationParseError, ParsedAnimationResult, parseAnimationEntry, parseAnimationOutputName} from './animation_parser';
|
||||
import {AnimationAst, AnimationAstVisitor, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStepAst, AnimationStylesAst} from './animation_ast';
|
||||
|
||||
const animationCompilationCache =
|
||||
new Map<CompileDirectiveMetadata, CompiledAnimationTriggerResult[]>();
|
||||
|
||||
export class CompiledAnimationTriggerResult {
|
||||
constructor(
|
||||
public name: string, public statesMapStatement: o.Statement,
|
||||
public statesVariableName: string, public fnStatement: o.Statement,
|
||||
public fnVariable: o.Expression) {}
|
||||
}
|
||||
|
||||
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,9 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
}
|
||||
|
||||
ast.styles.forEach(entry => {
|
||||
stylesArr.push(
|
||||
o.literalMap(StringMapWrapper.keys(entry).map(key => [key, o.literal(entry[key])])));
|
||||
const entries =
|
||||
Object.keys(entry).map((key): [string, o.Expression] => [key, o.literal(entry[key])]);
|
||||
stylesArr.push(o.literalMap(entries));
|
||||
});
|
||||
|
||||
return o.importExpr(resolveIdentifier(Identifiers.AnimationStyles)).instantiate([
|
||||
@ -182,9 +134,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 +285,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]);
|
||||
});
|
||||
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]);
|
||||
});
|
||||
|
||||
var compiledStatesMapExpr = this._statesMapVar.set(o.literalMap(lookupMap)).toDeclStmt();
|
||||
return new CompiledAnimationTriggerResult(
|
||||
this.animationName, compiledStatesMapExpr, this._statesMapVarName, fnStatement, fnVariable);
|
||||
const compiledStatesMapStmt = this._statesMapVar.set(o.literalMap(lookupMap)).toDeclStmt();
|
||||
const statements: o.Statement[] = [compiledStatesMapStmt, fnStatement];
|
||||
|
||||
return new AnimationEntryCompileResult(this.animationName, statements, fnVariable);
|
||||
}
|
||||
}
|
||||
|
||||
@ -371,7 +321,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 +347,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 +355,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 {}
|
||||
}
|
||||
|
@ -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[] = [];
|
||||
export class AnimationParser {
|
||||
parseComponent(component: CompileDirectiveMetadata): AnimationEntryAst[] {
|
||||
const errors: string[] = [];
|
||||
const componentName = component.type.name;
|
||||
const animationTriggerNames = new Set<string>();
|
||||
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;
|
||||
});
|
||||
|
||||
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(<CompileAnimationStateTransitionMetadata>def);
|
||||
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));
|
||||
|
||||
var ast = new AnimationEntryAst(entry.name, stateDeclarationAsts, stateTransitionAsts);
|
||||
return new ParsedAnimationResult(ast, errors);
|
||||
}
|
||||
|
||||
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)`));
|
||||
return asts;
|
||||
}
|
||||
|
||||
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(<CompileAnimationStateTransitionMetadata>def);
|
||||
}
|
||||
});
|
||||
|
||||
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`));
|
||||
@ -103,13 +115,10 @@ function _parseAnimationStateTransition(
|
||||
stateStyles: {[key: string]: AnimationStylesAst},
|
||||
errors: AnimationParseError[]): AnimationStateTransitionAst {
|
||||
var styles = new StylesCollection();
|
||||
var transitionExprs: any[] /** TODO #9100 */ = [];
|
||||
var transitionExprs: AnimationStateTransitionExpression[] = [];
|
||||
var transitionStates = transitionStateMetadata.stateChangeExpr.split(/\s*,\s*/);
|
||||
transitionStates.forEach(expr => {
|
||||
_parseAnimationTransitionExpr(expr, errors).forEach(transExpr => {
|
||||
transitionExprs.push(transExpr);
|
||||
});
|
||||
});
|
||||
transitionStates.forEach(
|
||||
expr => { transitionExprs.push(..._parseAnimationTransitionExpr(expr, errors)); });
|
||||
var entry = _normalizeAnimationEntry(transitionStateMetadata.steps);
|
||||
var animation = _normalizeStyleSteps(entry, stateStyles, errors);
|
||||
var animationAst = _parseTransitionAnimation(animation, 0, styles, stateStyles, errors);
|
||||
@ -124,9 +133,25 @@ function _parseAnimationStateTransition(
|
||||
return new AnimationStateTransitionAst(transitionExprs, stepsAst);
|
||||
}
|
||||
|
||||
function _parseAnimationAlias(alias: string, errors: AnimationParseError[]): string {
|
||||
switch (alias) {
|
||||
case ':enter':
|
||||
return 'void => *';
|
||||
case ':leave':
|
||||
return '* => void';
|
||||
default:
|
||||
errors.push(
|
||||
new AnimationParseError(`the transition alias value "${alias}" is not supported`));
|
||||
return '* => *';
|
||||
}
|
||||
}
|
||||
|
||||
function _parseAnimationTransitionExpr(
|
||||
eventStr: string, errors: AnimationParseError[]): AnimationStateTransitionExpression[] {
|
||||
var expressions: any[] /** TODO #9100 */ = [];
|
||||
var expressions: AnimationStateTransitionExpression[] = [];
|
||||
if (eventStr[0] == ':') {
|
||||
eventStr = _parseAnimationAlias(eventStr, errors);
|
||||
}
|
||||
var match = eventStr.match(/^(\*|[-\w]+)\s*(<?[=-]>)\s*(\*|[-\w]+)$/);
|
||||
if (!isPresent(match) || match.length < 4) {
|
||||
errors.push(new AnimationParseError(`the provided ${eventStr} is not of a supported format`));
|
||||
@ -145,16 +170,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(<CompileAnimationMetadata[]>entry) :
|
||||
@ -163,8 +178,8 @@ function _normalizeAnimationEntry(entry: CompileAnimationMetadata | CompileAnima
|
||||
|
||||
function _normalizeStyleMetadata(
|
||||
entry: CompileAnimationStyleMetadata, stateStyles: {[key: string]: AnimationStylesAst},
|
||||
errors: AnimationParseError[]): Array<{[key: string]: string | number}> {
|
||||
var normalizedStyles: any[] /** TODO #9100 */ = [];
|
||||
errors: AnimationParseError[]): {[key: string]: string | number}[] {
|
||||
var normalizedStyles: {[key: string]: string | number}[] = [];
|
||||
entry.styles.forEach(styleEntry => {
|
||||
if (isString(styleEntry)) {
|
||||
ListWrapper.addAll(
|
||||
@ -210,7 +225,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 +281,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 +293,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 +327,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)) {
|
||||
@ -338,7 +351,6 @@ function _parseAnimationKeyframes(
|
||||
ListWrapper.sort(rawKeyframes, (a, b) => a[0] <= b[0] ? -1 : 1);
|
||||
}
|
||||
|
||||
var i: any /** TODO #9100 */;
|
||||
var firstKeyframe = rawKeyframes[0];
|
||||
if (firstKeyframe[0] != _INITIAL_KEYFRAME) {
|
||||
ListWrapper.insert(rawKeyframes, 0, firstKeyframe = [_INITIAL_KEYFRAME, {}]);
|
||||
@ -353,28 +365,26 @@ function _parseAnimationKeyframes(
|
||||
}
|
||||
|
||||
var lastKeyframeStyles = lastKeyframe[1];
|
||||
for (i = 1; i <= limit; i++) {
|
||||
for (let i = 1; i <= limit; i++) {
|
||||
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--) {
|
||||
for (let 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 +408,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 +456,7 @@ function _parseTransitionAnimation(
|
||||
} else {
|
||||
let styleData = <CompileAnimationStyleMetadata>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 +468,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 +542,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);
|
||||
|
@ -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 {
|
||||
@ -305,7 +304,7 @@ export class CompileTemplateMetadata {
|
||||
this.styleUrls = _normalizeArray(styleUrls);
|
||||
this.externalStylesheets = _normalizeArray(externalStylesheets);
|
||||
this.animations = isPresent(animations) ? ListWrapper.flatten(animations) : [];
|
||||
this.ngContentSelectors = isPresent(ngContentSelectors) ? ngContentSelectors : [];
|
||||
this.ngContentSelectors = ngContentSelectors || [];
|
||||
if (isPresent(interpolation) && interpolation.length != 2) {
|
||||
throw new Error(`'interpolation' should have a start and an end symbol.`);
|
||||
}
|
||||
@ -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;
|
||||
@ -590,7 +590,7 @@ export function removeIdentifierDuplicates<T extends CompileMetadataWithIdentifi
|
||||
}
|
||||
|
||||
function _normalizeArray(obj: any[]): any[] {
|
||||
return isPresent(obj) ? obj : [];
|
||||
return obj || [];
|
||||
}
|
||||
|
||||
export function isStaticSymbol(value: any): value is StaticSymbol {
|
||||
|
@ -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';
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
import * as chars from '../chars';
|
||||
import {BaseError} from '../facade/errors';
|
||||
import {StringWrapper, isPresent, resolveEnumToken} from '../facade/lang';
|
||||
import {isPresent} from '../facade/lang';
|
||||
|
||||
export enum CssTokenType {
|
||||
EOF,
|
||||
@ -155,7 +155,7 @@ export class CssScanner {
|
||||
}
|
||||
|
||||
peekAt(index: number): number {
|
||||
return index >= this.length ? chars.$EOF : StringWrapper.charCodeAt(this.input, index);
|
||||
return index >= this.length ? chars.$EOF : this.input.charCodeAt(index);
|
||||
}
|
||||
|
||||
consumeEmptyStatements(): void {
|
||||
@ -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 + '")';
|
||||
@ -310,7 +310,7 @@ export class CssScanner {
|
||||
return this.scanCharacter();
|
||||
}
|
||||
|
||||
return this.error(`Unexpected character [${StringWrapper.fromCharCode(peek)}]`);
|
||||
return this.error(`Unexpected character [${String.fromCharCode(peek)}]`);
|
||||
}
|
||||
|
||||
scanComment(): CssToken {
|
||||
@ -476,8 +476,7 @@ export class CssScanner {
|
||||
var index: number = this.index;
|
||||
var column: number = this.column;
|
||||
var line: number = this.line;
|
||||
errorTokenValue =
|
||||
isPresent(errorTokenValue) ? errorTokenValue : StringWrapper.fromCharCode(this.peek);
|
||||
errorTokenValue = errorTokenValue || String.fromCharCode(this.peek);
|
||||
var invalidToken = new CssToken(index, column, line, CssTokenType.Invalid, errorTokenValue);
|
||||
var errorMessage =
|
||||
generateErrorMessage(this.input, message, errorTokenValue, index, line, column);
|
||||
@ -696,11 +695,11 @@ function isValidCssCharacter(code: number, mode: CssLexerMode): boolean {
|
||||
}
|
||||
|
||||
function charCode(input: string, index: number): number {
|
||||
return index >= input.length ? chars.$EOF : StringWrapper.charCodeAt(input, index);
|
||||
return index >= input.length ? chars.$EOF : input.charCodeAt(index);
|
||||
}
|
||||
|
||||
function charStr(code: number): string {
|
||||
return StringWrapper.fromCharCode(code);
|
||||
return String.fromCharCode(code);
|
||||
}
|
||||
|
||||
export function isNewline(code: number): boolean {
|
||||
|
@ -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,64 @@ export class DirectiveResolver {
|
||||
* Return {@link Directive} for a given `Type`.
|
||||
*/
|
||||
resolve(type: Type<any>, 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<any>): 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} = {};
|
||||
|
||||
StringMapWrapper.forEach(propertyMetadata, (metadata: any[], propName: string) => {
|
||||
metadata.forEach(a => {
|
||||
Object.keys(propertyMetadata).forEach((propName: string) => {
|
||||
|
||||
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) {
|
||||
const startWith = hostBinding.hostPropertyName[0];
|
||||
if (startWith === '(') {
|
||||
throw new Error(`@HostBinding can not bind to events. Use @HostListener instead.`);
|
||||
} else if (startWith === '[') {
|
||||
throw new Error(
|
||||
`@HostBinding parameter should be a property name, 'class.<name>', or 'attr.<name>'.`);
|
||||
}
|
||||
host[`[${hostBinding.hostPropertyName}]`] = propName;
|
||||
} else {
|
||||
host[`[${propName}]`] = propName;
|
||||
}
|
||||
} else if (a instanceof HostListener) {
|
||||
const hostListener: HostListener = a;
|
||||
var args = isPresent(hostListener.args) ? (<any[]>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 +97,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<any>): 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 +112,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 +129,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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {Injectable} from '@angular/core';
|
||||
import * as chars from '../chars';
|
||||
import {NumberWrapper, StringJoiner, StringWrapper, isPresent} from '../facade/lang';
|
||||
import {NumberWrapper, StringJoiner, isPresent} from '../facade/lang';
|
||||
|
||||
export enum TokenType {
|
||||
Character,
|
||||
@ -93,7 +93,7 @@ export class Token {
|
||||
}
|
||||
|
||||
function newCharacterToken(index: number, code: number): Token {
|
||||
return new Token(index, TokenType.Character, code, StringWrapper.fromCharCode(code));
|
||||
return new Token(index, TokenType.Character, code, String.fromCharCode(code));
|
||||
}
|
||||
|
||||
function newIdentifierToken(index: number, text: string): Token {
|
||||
@ -133,8 +133,7 @@ class _Scanner {
|
||||
}
|
||||
|
||||
advance() {
|
||||
this.peek =
|
||||
++this.index >= this.length ? chars.$EOF : StringWrapper.charCodeAt(this.input, this.index);
|
||||
this.peek = ++this.index >= this.length ? chars.$EOF : this.input.charCodeAt(this.index);
|
||||
}
|
||||
|
||||
scanToken(): Token {
|
||||
@ -146,7 +145,7 @@ class _Scanner {
|
||||
peek = chars.$EOF;
|
||||
break;
|
||||
} else {
|
||||
peek = StringWrapper.charCodeAt(input, index);
|
||||
peek = input.charCodeAt(index);
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,16 +186,16 @@ class _Scanner {
|
||||
case chars.$SLASH:
|
||||
case chars.$PERCENT:
|
||||
case chars.$CARET:
|
||||
return this.scanOperator(start, StringWrapper.fromCharCode(peek));
|
||||
return this.scanOperator(start, String.fromCharCode(peek));
|
||||
case chars.$QUESTION:
|
||||
return this.scanComplexOperator(start, '?', chars.$PERIOD, '.');
|
||||
case chars.$LT:
|
||||
case chars.$GT:
|
||||
return this.scanComplexOperator(start, StringWrapper.fromCharCode(peek), chars.$EQ, '=');
|
||||
return this.scanComplexOperator(start, String.fromCharCode(peek), chars.$EQ, '=');
|
||||
case chars.$BANG:
|
||||
case chars.$EQ:
|
||||
return this.scanComplexOperator(
|
||||
start, StringWrapper.fromCharCode(peek), chars.$EQ, '=', chars.$EQ, '=');
|
||||
start, String.fromCharCode(peek), chars.$EQ, '=', chars.$EQ, '=');
|
||||
case chars.$AMPERSAND:
|
||||
return this.scanComplexOperator(start, '&', chars.$AMPERSAND, '&');
|
||||
case chars.$BAR:
|
||||
@ -207,7 +206,7 @@ class _Scanner {
|
||||
}
|
||||
|
||||
this.advance();
|
||||
return this.error(`Unexpected character [${StringWrapper.fromCharCode(peek)}]`, 0);
|
||||
return this.error(`Unexpected character [${String.fromCharCode(peek)}]`, 0);
|
||||
}
|
||||
|
||||
scanCharacter(start: number, code: number): Token {
|
||||
@ -310,7 +309,7 @@ class _Scanner {
|
||||
unescapedCode = unescape(this.peek);
|
||||
this.advance();
|
||||
}
|
||||
buffer.add(StringWrapper.fromCharCode(unescapedCode));
|
||||
buffer.add(String.fromCharCode(unescapedCode));
|
||||
marker = this.index;
|
||||
} else if (this.peek == chars.$EOF) {
|
||||
return this.error('Unterminated quote', 0);
|
||||
|
@ -9,7 +9,7 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
import * as chars from '../chars';
|
||||
import {StringWrapper, escapeRegExp, isBlank, isPresent} from '../facade/lang';
|
||||
import {escapeRegExp, isBlank, isPresent} from '../facade/lang';
|
||||
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../ml_parser/interpolation_config';
|
||||
|
||||
import {AST, ASTWithSource, AstVisitor, Binary, BindingPipe, Chain, Conditional, EmptyExpr, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, ParseSpan, ParserError, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead, TemplateBinding} from './ast';
|
||||
@ -17,7 +17,7 @@ import {EOF, Lexer, Token, TokenType, isIdentifier, isQuote} from './lexer';
|
||||
|
||||
|
||||
export class SplitInterpolation {
|
||||
constructor(public strings: string[], public expressions: string[]) {}
|
||||
constructor(public strings: string[], public expressions: string[], public offsets: number[]) {}
|
||||
}
|
||||
|
||||
export class TemplateBindingParseResult {
|
||||
@ -41,8 +41,12 @@ export class Parser {
|
||||
input: string, location: any,
|
||||
interpolationConfig: InterpolationConfig = DEFAULT_INTERPOLATION_CONFIG): ASTWithSource {
|
||||
this._checkNoInterpolation(input, location, interpolationConfig);
|
||||
const sourceToLex = this._stripComments(input);
|
||||
const tokens = this._lexer.tokenize(this._stripComments(input));
|
||||
const ast = new _ParseAST(input, location, tokens, true, this.errors).parseChain();
|
||||
const ast = new _ParseAST(
|
||||
input, location, tokens, sourceToLex.length, true, this.errors,
|
||||
input.length - sourceToLex.length)
|
||||
.parseChain();
|
||||
return new ASTWithSource(ast, input, location, this.errors);
|
||||
}
|
||||
|
||||
@ -79,8 +83,12 @@ export class Parser {
|
||||
}
|
||||
|
||||
this._checkNoInterpolation(input, location, interpolationConfig);
|
||||
var tokens = this._lexer.tokenize(this._stripComments(input));
|
||||
return new _ParseAST(input, location, tokens, false, this.errors).parseChain();
|
||||
const sourceToLex = this._stripComments(input);
|
||||
const tokens = this._lexer.tokenize(sourceToLex);
|
||||
return new _ParseAST(
|
||||
input, location, tokens, sourceToLex.length, false, this.errors,
|
||||
input.length - sourceToLex.length)
|
||||
.parseChain();
|
||||
}
|
||||
|
||||
private _parseQuote(input: string, location: any): AST {
|
||||
@ -95,7 +103,8 @@ export class Parser {
|
||||
|
||||
parseTemplateBindings(input: string, location: any): TemplateBindingParseResult {
|
||||
var tokens = this._lexer.tokenize(input);
|
||||
return new _ParseAST(input, location, tokens, false, this.errors).parseTemplateBindings();
|
||||
return new _ParseAST(input, location, tokens, input.length, false, this.errors, 0)
|
||||
.parseTemplateBindings();
|
||||
}
|
||||
|
||||
parseInterpolation(
|
||||
@ -107,8 +116,13 @@ export class Parser {
|
||||
let expressions: AST[] = [];
|
||||
|
||||
for (let i = 0; i < split.expressions.length; ++i) {
|
||||
var tokens = this._lexer.tokenize(this._stripComments(split.expressions[i]));
|
||||
var ast = new _ParseAST(input, location, tokens, false, this.errors).parseChain();
|
||||
const expressionText = split.expressions[i];
|
||||
const sourceToLex = this._stripComments(expressionText);
|
||||
const tokens = this._lexer.tokenize(this._stripComments(split.expressions[i]));
|
||||
const ast = new _ParseAST(
|
||||
input, location, tokens, sourceToLex.length, false, this.errors,
|
||||
split.offsets[i] + (expressionText.length - sourceToLex.length))
|
||||
.parseChain();
|
||||
expressions.push(ast);
|
||||
}
|
||||
|
||||
@ -122,20 +136,25 @@ export class Parser {
|
||||
input: string, location: string,
|
||||
interpolationConfig: InterpolationConfig = DEFAULT_INTERPOLATION_CONFIG): SplitInterpolation {
|
||||
const regexp = _createInterpolateRegExp(interpolationConfig);
|
||||
const parts = StringWrapper.split(input, regexp);
|
||||
const parts = input.split(regexp);
|
||||
if (parts.length <= 1) {
|
||||
return null;
|
||||
}
|
||||
const strings: string[] = [];
|
||||
const expressions: string[] = [];
|
||||
|
||||
const offsets: number[] = [];
|
||||
let offset = 0;
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
var part: string = parts[i];
|
||||
if (i % 2 === 0) {
|
||||
// fixed string
|
||||
strings.push(part);
|
||||
offset += part.length;
|
||||
} else if (part.trim().length > 0) {
|
||||
offset += interpolationConfig.start.length;
|
||||
expressions.push(part);
|
||||
offsets.push(offset);
|
||||
offset += part.length + interpolationConfig.end.length;
|
||||
} else {
|
||||
this._reportError(
|
||||
'Blank expressions are not allowed in interpolated strings', input,
|
||||
@ -143,7 +162,7 @@ export class Parser {
|
||||
location);
|
||||
}
|
||||
}
|
||||
return new SplitInterpolation(strings, expressions);
|
||||
return new SplitInterpolation(strings, expressions, offsets);
|
||||
}
|
||||
|
||||
wrapLiteralPrimitive(input: string, location: any): ASTWithSource {
|
||||
@ -160,8 +179,8 @@ export class Parser {
|
||||
private _commentStart(input: string): number {
|
||||
var outerQuote: number = null;
|
||||
for (let i = 0; i < input.length - 1; i++) {
|
||||
const char = StringWrapper.charCodeAt(input, i);
|
||||
const nextChar = StringWrapper.charCodeAt(input, i + 1);
|
||||
const char = input.charCodeAt(i);
|
||||
const nextChar = input.charCodeAt(i + 1);
|
||||
|
||||
if (char === chars.$SLASH && nextChar == chars.$SLASH && isBlank(outerQuote)) return i;
|
||||
|
||||
@ -177,7 +196,7 @@ export class Parser {
|
||||
private _checkNoInterpolation(
|
||||
input: string, location: any, interpolationConfig: InterpolationConfig): void {
|
||||
var regexp = _createInterpolateRegExp(interpolationConfig);
|
||||
var parts = StringWrapper.split(input, regexp);
|
||||
var parts = input.split(regexp);
|
||||
if (parts.length > 1) {
|
||||
this._reportError(
|
||||
`Got interpolation (${interpolationConfig.start}${interpolationConfig.end}) where expression was expected`,
|
||||
@ -208,8 +227,9 @@ export class _ParseAST {
|
||||
index: number = 0;
|
||||
|
||||
constructor(
|
||||
public input: string, public location: any, public tokens: any[], public parseAction: boolean,
|
||||
private errors: ParserError[]) {}
|
||||
public input: string, public location: any, public tokens: Token[],
|
||||
public inputLength: number, public parseAction: boolean, private errors: ParserError[],
|
||||
private offset: number) {}
|
||||
|
||||
peek(offset: number): Token {
|
||||
var i = this.index + offset;
|
||||
@ -219,7 +239,8 @@ export class _ParseAST {
|
||||
get next(): Token { return this.peek(0); }
|
||||
|
||||
get inputIndex(): number {
|
||||
return (this.index < this.tokens.length) ? this.next.index : this.input.length;
|
||||
return (this.index < this.tokens.length) ? this.next.index + this.offset :
|
||||
this.inputLength + this.offset;
|
||||
}
|
||||
|
||||
span(start: number) { return new ParseSpan(start, this.inputIndex); }
|
||||
@ -239,7 +260,7 @@ export class _ParseAST {
|
||||
|
||||
expectCharacter(code: number) {
|
||||
if (this.optionalCharacter(code)) return;
|
||||
this.error(`Missing expected ${StringWrapper.fromCharCode(code)}`);
|
||||
this.error(`Missing expected ${String.fromCharCode(code)}`);
|
||||
}
|
||||
|
||||
optionalOperator(op: string): boolean {
|
||||
@ -311,7 +332,7 @@ export class _ParseAST {
|
||||
while (this.optionalCharacter(chars.$COLON)) {
|
||||
args.push(this.parseExpression());
|
||||
}
|
||||
result = new BindingPipe(this.span(result.span.start), result, name, args);
|
||||
result = new BindingPipe(this.span(result.span.start - this.offset), result, name, args);
|
||||
} while (this.optionalOperator('|'));
|
||||
}
|
||||
|
||||
|
@ -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}`;
|
||||
}
|
||||
}
|
@ -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(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -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, '"'],
|
||||
|
@ -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) {
|
||||
|
@ -34,7 +34,9 @@ export class ExpansionCase implements Node {
|
||||
}
|
||||
|
||||
export class Attribute implements Node {
|
||||
constructor(public name: string, public value: string, public sourceSpan: ParseSourceSpan) {}
|
||||
constructor(
|
||||
public name: string, public value: string, public sourceSpan: ParseSourceSpan,
|
||||
public valueSpan?: ParseSourceSpan) {}
|
||||
visit(visitor: Visitor, context: any): any { return visitor.visitAttribute(this, context); }
|
||||
}
|
||||
|
||||
@ -52,6 +54,10 @@ export class Comment implements Node {
|
||||
}
|
||||
|
||||
export interface Visitor {
|
||||
// Returning a truthy value from `visit()` will prevent `visitAll()` from the call to the typed
|
||||
// method and result returned will become the result included in `visitAll()`s result array.
|
||||
visit?(node: Node, context: any): any;
|
||||
|
||||
visitElement(element: Element, context: any): any;
|
||||
visitAttribute(attribute: Attribute, context: any): any;
|
||||
visitText(text: Text, context: any): any;
|
||||
@ -62,8 +68,12 @@ export interface Visitor {
|
||||
|
||||
export function visitAll(visitor: Visitor, nodes: Node[], context: any = null): any[] {
|
||||
let result: any[] = [];
|
||||
|
||||
let visit = visitor.visit ?
|
||||
(ast: Node) => visitor.visit(ast, context) || ast.visit(visitor, context) :
|
||||
(ast: Node) => ast.visit(visitor, context);
|
||||
nodes.forEach(ast => {
|
||||
const astResult = ast.visit(visitor, context);
|
||||
const astResult = visit(ast);
|
||||
if (astResult) {
|
||||
result.push(astResult);
|
||||
}
|
||||
|
@ -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));
|
||||
@ -331,12 +331,15 @@ class _TreeBuilder {
|
||||
const fullName = mergeNsAndName(attrName.parts[0], attrName.parts[1]);
|
||||
let end = attrName.sourceSpan.end;
|
||||
let value = '';
|
||||
let valueSpan: ParseSourceSpan;
|
||||
if (this._peek.type === lex.TokenType.ATTR_VALUE) {
|
||||
const valueToken = this._advance();
|
||||
value = valueToken.parts[0];
|
||||
end = valueToken.sourceSpan.end;
|
||||
valueSpan = valueToken.sourceSpan;
|
||||
}
|
||||
return new html.Attribute(fullName, value, new ParseSourceSpan(attrName.sourceSpan.start, end));
|
||||
return new html.Attribute(
|
||||
fullName, value, new ParseSourceSpan(attrName.sourceSpan.start, end), valueSpan);
|
||||
}
|
||||
|
||||
private _getParentElement(): html.Element {
|
||||
|
@ -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';
|
||||
@ -163,11 +163,11 @@ class _InjectorBuilder {
|
||||
if (isPresent(provider.useExisting)) {
|
||||
result = this._getDependency(new CompileDiDependencyMetadata({token: provider.useExisting}));
|
||||
} else if (isPresent(provider.useFactory)) {
|
||||
var deps = isPresent(provider.deps) ? provider.deps : provider.useFactory.diDeps;
|
||||
var deps = provider.deps || provider.useFactory.diDeps;
|
||||
var depsExpr = deps.map((dep) => this._getDependency(dep));
|
||||
result = o.importExpr(provider.useFactory).callFn(depsExpr);
|
||||
} else if (isPresent(provider.useClass)) {
|
||||
var deps = isPresent(provider.deps) ? provider.deps : provider.useClass.diDeps;
|
||||
var deps = provider.deps || provider.useClass.diDeps;
|
||||
var depsExpr = deps.map((dep) => this._getDependency(dep));
|
||||
result =
|
||||
o.importExpr(provider.useClass).instantiate(depsExpr, o.importType(provider.useClass));
|
||||
@ -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);
|
||||
|
@ -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';
|
||||
@ -24,10 +26,32 @@ export class SourceModule {
|
||||
}
|
||||
|
||||
export class NgModulesSummary {
|
||||
constructor(public ngModuleByComponent: Map<StaticSymbol, CompileNgModuleMetadata>) {}
|
||||
constructor(
|
||||
public ngModuleByComponent: Map<StaticSymbol, CompileNgModuleMetadata>,
|
||||
public ngModules: CompileNgModuleMetadata[]) {}
|
||||
}
|
||||
|
||||
export function analyzeModules(
|
||||
ngModules: StaticSymbol[], metadataResolver: CompileMetadataResolver) {
|
||||
const ngModuleByComponent = new Map<StaticSymbol, CompileNgModuleMetadata>();
|
||||
const modules: CompileNgModuleMetadata[] = [];
|
||||
|
||||
ngModules.forEach((ngModule) => {
|
||||
const ngModuleMeta = metadataResolver.getNgModuleMetadata(<any>ngModule);
|
||||
modules.push(ngModuleMeta);
|
||||
ngModuleMeta.declaredDirectives.forEach((dirMeta: CompileDirectiveMetadata) => {
|
||||
if (dirMeta.isComponent) {
|
||||
ngModuleByComponent.set(dirMeta.type.reference, ngModuleMeta);
|
||||
}
|
||||
});
|
||||
});
|
||||
return new NgModulesSummary(ngModuleByComponent, modules);
|
||||
}
|
||||
|
||||
export class OfflineCompiler {
|
||||
private _animationParser = new AnimationParser();
|
||||
private _animationCompiler = new AnimationCompiler();
|
||||
|
||||
constructor(
|
||||
private _metadataResolver: CompileMetadataResolver,
|
||||
private _directiveNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser,
|
||||
@ -36,17 +60,7 @@ export class OfflineCompiler {
|
||||
private _localeId: string, private _translationFormat: string) {}
|
||||
|
||||
analyzeModules(ngModules: StaticSymbol[]): NgModulesSummary {
|
||||
const ngModuleByComponent = new Map<StaticSymbol, CompileNgModuleMetadata>();
|
||||
|
||||
ngModules.forEach((ngModule) => {
|
||||
const ngModuleMeta = this._metadataResolver.getNgModuleMetadata(<any>ngModule);
|
||||
ngModuleMeta.declaredDirectives.forEach((dirMeta) => {
|
||||
if (dirMeta.isComponent) {
|
||||
ngModuleByComponent.set(dirMeta.type.reference, ngModuleMeta);
|
||||
}
|
||||
});
|
||||
});
|
||||
return new NgModulesSummary(ngModuleByComponent);
|
||||
return analyzeModules(ngModules, this._metadataResolver);
|
||||
}
|
||||
|
||||
clearCache() {
|
||||
@ -162,14 +176,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;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {StringWrapper, isBlank, isPresent, isString} from '../facade/lang';
|
||||
import {isBlank, isPresent, isString} from '../facade/lang';
|
||||
|
||||
import * as o from './output_ast';
|
||||
|
||||
@ -368,7 +368,7 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
|
||||
var useNewLine = ast.entries.length > 1;
|
||||
ctx.print(`{`, useNewLine);
|
||||
ctx.incIndent();
|
||||
this.visitAllObjects((entry: any /** TODO #9100 */) => {
|
||||
this.visitAllObjects(entry => {
|
||||
ctx.print(`${escapeIdentifier(entry[0], this._escapeDollarInStrings, false)}: `);
|
||||
entry[1].visitExpression(this, ctx);
|
||||
}, ast.entries, ctx, ',', useNewLine);
|
||||
@ -381,12 +381,11 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
|
||||
expressions: o.Expression[], ctx: EmitterVisitorContext, separator: string,
|
||||
newLine: boolean = false): void {
|
||||
this.visitAllObjects(
|
||||
(expr: any /** TODO #9100 */) => expr.visitExpression(this, ctx), expressions, ctx,
|
||||
separator, newLine);
|
||||
expr => expr.visitExpression(this, ctx), expressions, ctx, separator, newLine);
|
||||
}
|
||||
|
||||
visitAllObjects(
|
||||
handler: Function, expressions: any, ctx: EmitterVisitorContext, separator: string,
|
||||
visitAllObjects<T>(
|
||||
handler: (t: T) => void, expressions: T[], ctx: EmitterVisitorContext, separator: string,
|
||||
newLine: boolean = false): void {
|
||||
for (var i = 0; i < expressions.length; i++) {
|
||||
if (i > 0) {
|
||||
@ -400,7 +399,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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -409,18 +408,17 @@ export function escapeIdentifier(
|
||||
if (isBlank(input)) {
|
||||
return null;
|
||||
}
|
||||
var body = StringWrapper.replaceAllMapped(
|
||||
input, _SINGLE_QUOTE_ESCAPE_STRING_RE, (match: any /** TODO #9100 */) => {
|
||||
if (match[0] == '$') {
|
||||
return escapeDollar ? '\\$' : '$';
|
||||
} else if (match[0] == '\n') {
|
||||
return '\\n';
|
||||
} else if (match[0] == '\r') {
|
||||
return '\\r';
|
||||
} else {
|
||||
return `\\${match[0]}`;
|
||||
}
|
||||
});
|
||||
var body = input.replace(_SINGLE_QUOTE_ESCAPE_STRING_RE, (...match: string[]) => {
|
||||
if (match[0] == '$') {
|
||||
return escapeDollar ? '\\$' : '$';
|
||||
} else if (match[0] == '\n') {
|
||||
return '\\n';
|
||||
} else if (match[0] == '\r') {
|
||||
return '\\r';
|
||||
} else {
|
||||
return `\\${match[0]}`;
|
||||
}
|
||||
});
|
||||
let requiresQuotes = alwaysQuote || !_LEGAL_IDENTIFIER_RE.test(body);
|
||||
return requiresQuotes ? `'${body}'` : body;
|
||||
}
|
||||
|
@ -144,11 +144,11 @@ export abstract class AbstractJsEmitterVisitor extends AbstractEmitterVisitor {
|
||||
}
|
||||
|
||||
private _visitParams(params: o.FnParam[], ctx: EmitterVisitorContext): void {
|
||||
this.visitAllObjects((param: any /** TODO #9100 */) => ctx.print(param.name), params, ctx, ',');
|
||||
this.visitAllObjects(param => ctx.print(param.name), params, ctx, ',');
|
||||
}
|
||||
|
||||
getBuiltinMethodName(method: o.BuiltinMethod): string {
|
||||
var name: any /** TODO #9100 */;
|
||||
var name: string;
|
||||
switch (method) {
|
||||
case o.BuiltinMethod.ConcatArray:
|
||||
name = 'concat';
|
||||
|
@ -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';
|
||||
@ -20,7 +20,7 @@ export class JavaScriptEmitter implements OutputEmitter {
|
||||
var converter = new JsEmitterVisitor(moduleUrl);
|
||||
var ctx = EmitterVisitorContext.createRoot(exportedVars);
|
||||
converter.visitAllStatements(stmts, ctx);
|
||||
var srcParts: any[] /** TODO #9100 */ = [];
|
||||
var srcParts: string[] = [];
|
||||
converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => {
|
||||
// Note: can't write the real word for import as it screws up system.js auto detection...
|
||||
srcParts.push(
|
||||
|
@ -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 = [];
|
||||
}
|
||||
}
|
||||
@ -217,7 +215,7 @@ export class ReadVarExpr extends Expression {
|
||||
export class WriteVarExpr extends Expression {
|
||||
public value: Expression;
|
||||
constructor(public name: string, value: Expression, type: Type = null) {
|
||||
super(isPresent(type) ? type : value.type);
|
||||
super(type || value.type);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@ -235,7 +233,7 @@ export class WriteKeyExpr extends Expression {
|
||||
public value: Expression;
|
||||
constructor(
|
||||
public receiver: Expression, public index: Expression, value: Expression, type: Type = null) {
|
||||
super(isPresent(type) ? type : value.type);
|
||||
super(type || value.type);
|
||||
this.value = value;
|
||||
}
|
||||
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||
@ -248,7 +246,7 @@ export class WritePropExpr extends Expression {
|
||||
public value: Expression;
|
||||
constructor(
|
||||
public receiver: Expression, public name: string, value: Expression, type: Type = null) {
|
||||
super(isPresent(type) ? type : value.type);
|
||||
super(type || value.type);
|
||||
this.value = value;
|
||||
}
|
||||
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||
@ -324,7 +322,7 @@ export class ConditionalExpr extends Expression {
|
||||
constructor(
|
||||
public condition: Expression, trueCase: Expression, public falseCase: Expression = null,
|
||||
type: Type = null) {
|
||||
super(isPresent(type) ? type : trueCase.type);
|
||||
super(type || trueCase.type);
|
||||
this.trueCase = trueCase;
|
||||
}
|
||||
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||
@ -371,7 +369,7 @@ export class BinaryOperatorExpr extends Expression {
|
||||
public lhs: Expression;
|
||||
constructor(
|
||||
public operator: BinaryOperator, lhs: Expression, public rhs: Expression, type: Type = null) {
|
||||
super(isPresent(type) ? type : lhs.type);
|
||||
super(type || lhs.type);
|
||||
this.lhs = lhs;
|
||||
}
|
||||
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||
@ -418,7 +416,7 @@ export class LiteralArrayExpr extends Expression {
|
||||
|
||||
export class LiteralMapExpr extends Expression {
|
||||
public valueType: Type = null;
|
||||
constructor(public entries: Array<Array<string|Expression>>, type: MapType = null) {
|
||||
constructor(public entries: [string, Expression][], type: MapType = null) {
|
||||
super(type);
|
||||
if (isPresent(type)) {
|
||||
this.valueType = type.valueType;
|
||||
@ -464,7 +462,7 @@ export enum StmtModifier {
|
||||
|
||||
export abstract class Statement {
|
||||
constructor(public modifiers: StmtModifier[] = null) {
|
||||
if (isBlank(modifiers)) {
|
||||
if (!modifiers) {
|
||||
this.modifiers = [];
|
||||
}
|
||||
}
|
||||
@ -481,7 +479,7 @@ export class DeclareVarStmt extends Statement {
|
||||
public name: string, public value: Expression, type: Type = null,
|
||||
modifiers: StmtModifier[] = null) {
|
||||
super(modifiers);
|
||||
this.type = isPresent(type) ? type : value.type;
|
||||
this.type = type || value.type;
|
||||
}
|
||||
|
||||
visitStatement(visitor: StatementVisitor, context: any): any {
|
||||
@ -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 = [];
|
||||
}
|
||||
}
|
||||
@ -627,7 +625,7 @@ export class ExpressionTransformer implements StatementVisitor, ExpressionVisito
|
||||
expr.value.visitExpression(this, context));
|
||||
}
|
||||
visitInvokeMethodExpr(ast: InvokeMethodExpr, context: any): any {
|
||||
var method = isPresent(ast.builtin) ? ast.builtin : ast.name;
|
||||
var method = ast.builtin || ast.name;
|
||||
return new InvokeMethodExpr(
|
||||
ast.receiver.visitExpression(this, context), method,
|
||||
this.visitAllExpressions(ast.args, context), ast.type);
|
||||
@ -675,9 +673,11 @@ export class ExpressionTransformer implements StatementVisitor, ExpressionVisito
|
||||
visitLiteralArrayExpr(ast: LiteralArrayExpr, context: any): any {
|
||||
return new LiteralArrayExpr(this.visitAllExpressions(ast.entries, context));
|
||||
}
|
||||
|
||||
visitLiteralMapExpr(ast: LiteralMapExpr, context: any): any {
|
||||
return new LiteralMapExpr(ast.entries.map(
|
||||
(entry) => [entry[0], (<Expression>entry[1]).visitExpression(this, context)]));
|
||||
const entries = ast.entries.map(
|
||||
(entry): [string, Expression] => [entry[0], entry[1].visitExpression(this, context), ]);
|
||||
return new LiteralMapExpr(entries);
|
||||
}
|
||||
visitAllExpressions(exprs: Expression[], context: any): Expression[] {
|
||||
return exprs.map(expr => expr.visitExpression(this, context));
|
||||
@ -883,8 +883,7 @@ export function literalArr(values: Expression[], type: Type = null): LiteralArra
|
||||
return new LiteralArrayExpr(values, type);
|
||||
}
|
||||
|
||||
export function literalMap(
|
||||
values: Array<Array<string|Expression>>, type: MapType = null): LiteralMapExpr {
|
||||
export function literalMap(values: [string, Expression][], type: MapType = null): LiteralMapExpr {
|
||||
return new LiteralMapExpr(values, type);
|
||||
}
|
||||
|
||||
|
@ -26,9 +26,9 @@ class JitEmitterVisitor extends AbstractJsEmitterVisitor {
|
||||
private _evalArgValues: any[] = [];
|
||||
|
||||
getArgs(): {[key: string]: any} {
|
||||
var result = {};
|
||||
var result: {[key: string]: any} = {};
|
||||
for (var i = 0; i < this._evalArgNames.length; i++) {
|
||||
(result as any /** TODO #9100 */)[this._evalArgNames[i]] = this._evalArgValues[i];
|
||||
result[this._evalArgNames[i]] = this._evalArgValues[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -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:<package-name>/<realm>/<path-to-module>
|
||||
var _ASSET_URL_RE = /asset:([^\/]+)\/([^\/]+)\/(.+)/;
|
||||
|
||||
|
@ -46,7 +46,7 @@ export class TypeScriptEmitter implements OutputEmitter {
|
||||
var converter = new _TsEmitterVisitor(moduleUrl);
|
||||
var ctx = EmitterVisitorContext.createRoot(exportedVars);
|
||||
converter.visitAllStatements(stmts, ctx);
|
||||
var srcParts: any[] /** TODO #9100 */ = [];
|
||||
var srcParts: string[] = [];
|
||||
converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => {
|
||||
// Note: can't write the real word for import as it screws up system.js auto detection...
|
||||
srcParts.push(
|
||||
@ -75,6 +75,22 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||
super.visitLiteralExpr(ast, ctx, '(null as any)');
|
||||
}
|
||||
|
||||
|
||||
// Temporary workaround to support strictNullCheck enabled consumers of ngc emit.
|
||||
// In SNC mode, [] have the type never[], so we cast here to any[].
|
||||
// TODO: narrow the cast to a more explicit type, or use a pattern that does not
|
||||
// start with [].concat. see https://github.com/angular/angular/pull/11846
|
||||
visitLiteralArrayExpr(ast: o.LiteralArrayExpr, ctx: EmitterVisitorContext): any {
|
||||
if (ast.entries.length === 0) {
|
||||
ctx.print('(');
|
||||
}
|
||||
const result = super.visitLiteralArrayExpr(ast, ctx);
|
||||
if (ast.entries.length === 0) {
|
||||
ctx.print(' as any[])');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
|
||||
this._visitIdentifier(ast.value, ast.typeParams, ctx);
|
||||
return null;
|
||||
@ -227,7 +243,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||
}
|
||||
|
||||
visitBuiltintType(type: o.BuiltinType, ctx: EmitterVisitorContext): any {
|
||||
var typeStr: any /** TODO #9100 */;
|
||||
var typeStr: string;
|
||||
switch (type.name) {
|
||||
case o.BuiltinTypeName.Bool:
|
||||
typeStr = 'boolean';
|
||||
@ -291,7 +307,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||
}
|
||||
|
||||
private _visitParams(params: o.FnParam[], ctx: EmitterVisitorContext): void {
|
||||
this.visitAllObjects((param: any /** TODO #9100 */) => {
|
||||
this.visitAllObjects(param => {
|
||||
ctx.print(param.name);
|
||||
ctx.print(':');
|
||||
this.visitType(param.type, ctx);
|
||||
@ -320,8 +336,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||
}
|
||||
if (isPresent(typeParams) && typeParams.length > 0) {
|
||||
ctx.print(`<`);
|
||||
this.visitAllObjects(
|
||||
(type: any /** TODO #9100 */) => type.visitType(this, ctx), typeParams, ctx, ',');
|
||||
this.visitAllObjects(type => type.visitType(this, ctx), typeParams, ctx, ',');
|
||||
ctx.print(`>`);
|
||||
}
|
||||
}
|
||||
|
@ -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';
|
||||
@ -23,10 +22,8 @@ class _ValueOutputAstTransformer implements ValueTransformer {
|
||||
}
|
||||
|
||||
visitStringMap(map: {[key: string]: any}, type: o.MapType): o.Expression {
|
||||
var entries: Array<string|o.Expression>[] = [];
|
||||
StringMapWrapper.forEach(map, (value: any, key: string) => {
|
||||
entries.push([key, visitValue(value, this, null)]);
|
||||
});
|
||||
const entries: [string, o.Expression][] = [];
|
||||
Object.keys(map).forEach(key => { entries.push([key, visitValue(map[key], this, null)]); });
|
||||
return o.literalMap(entries, type);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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<any, boolean>();
|
||||
MapWrapper.values(this._allProviders).forEach((provider) => {
|
||||
@ -102,7 +102,7 @@ export class ProviderElementContext {
|
||||
|
||||
private _addQueryReadsTo(token: CompileTokenMetadata, queryReadTokens: Map<any, boolean>) {
|
||||
this._getQueriesFor(token).forEach((query) => {
|
||||
const queryReadToken = isPresent(query.read) ? query.read : token;
|
||||
const queryReadToken = query.read || token;
|
||||
if (isBlank(queryReadTokens.get(queryReadToken.reference))) {
|
||||
queryReadTokens.set(queryReadToken.reference, true);
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -170,11 +169,11 @@ export class ProviderElementContext {
|
||||
transformedUseValue = existingDiDep.value;
|
||||
}
|
||||
} else if (isPresent(provider.useFactory)) {
|
||||
var deps = isPresent(provider.deps) ? provider.deps : provider.useFactory.diDeps;
|
||||
var deps = provider.deps || provider.useFactory.diDeps;
|
||||
transformedDeps =
|
||||
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager));
|
||||
} else if (isPresent(provider.useClass)) {
|
||||
var deps = isPresent(provider.deps) ? provider.deps : provider.useClass.diDeps;
|
||||
var deps = provider.deps || provider.useClass.diDeps;
|
||||
transformedDeps =
|
||||
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager));
|
||||
}
|
||||
@ -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);
|
||||
@ -339,11 +338,11 @@ export class NgModuleProviderAnalyzer {
|
||||
transformedUseValue = existingDiDep.value;
|
||||
}
|
||||
} else if (isPresent(provider.useFactory)) {
|
||||
var deps = isPresent(provider.deps) ? provider.deps : provider.useFactory.diDeps;
|
||||
var deps = provider.deps || provider.useFactory.diDeps;
|
||||
transformedDeps =
|
||||
deps.map((dep) => this._getDependency(dep, eager, resolvedProvider.sourceSpan));
|
||||
} else if (isPresent(provider.useClass)) {
|
||||
var deps = isPresent(provider.deps) ? provider.deps : provider.useClass.diDeps;
|
||||
var deps = provider.deps || provider.useClass.diDeps;
|
||||
transformedDeps =
|
||||
deps.map((dep) => this._getDependency(dep, eager, resolvedProvider.sourceSpan));
|
||||
}
|
||||
@ -414,7 +413,7 @@ function _normalizeProviders(
|
||||
providers: Array<CompileProviderMetadata|CompileTypeMetadata|any[]>,
|
||||
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<any, CompileQueryMetadata[]>, query: CompileQueryMetadata) {
|
||||
query.selectors.forEach((token: CompileTokenMetadata) => {
|
||||
var entry = map.get(token.reference);
|
||||
if (isBlank(entry)) {
|
||||
if (!entry) {
|
||||
entry = [];
|
||||
map.set(token.reference, entry);
|
||||
}
|
||||
|
@ -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<Type<any>, CompiledTemplate>();
|
||||
private _compiledHostTemplateCache = new Map<Type<any>, CompiledTemplate>();
|
||||
private _compiledNgModuleCache = new Map<Type<any>, NgModuleFactory<any>>();
|
||||
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<any>): 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);
|
||||
|
@ -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};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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};
|
||||
}
|
||||
|
@ -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}></${tagName}>`;
|
||||
}
|
||||
|
||||
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<string, SelectorContext[]>();
|
||||
private _elementPartialMap = new Map<string, SelectorMatcher>();
|
||||
private _classMap = new Map<string, SelectorContext[]>();
|
||||
private _classPartialMap = new Map<string, SelectorMatcher>();
|
||||
private _attrValueMap = new Map<string, Map<string, SelectorContext[]>>();
|
||||
private _attrValuePartialMap = new Map<string, Map<string, SelectorMatcher>>();
|
||||
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);
|
||||
let matcher: SelectorMatcher = this;
|
||||
const element = cssSelector.element;
|
||||
const classNames = cssSelector.classNames;
|
||||
const attrs = cssSelector.attrs;
|
||||
const selectable = new SelectorContext(cssSelector, callbackCtxt, listContext);
|
||||
|
||||
if (isPresent(element)) {
|
||||
var isTerminal = attrs.length === 0 && classNames.length === 0;
|
||||
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<string, SelectorContext[]>();
|
||||
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<string, SelectorMatcher>();
|
||||
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<string, SelectorContext[]>, 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<string, SelectorMatcher>, 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;
|
||||
}
|
||||
result = this._matchTerminal(terminalValuesMap, attrValue, 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 terminalValuesMap = this._attrValueMap[name];
|
||||
if (value) {
|
||||
result =
|
||||
this._matchTerminal(terminalValuesMap, '', cssSelector, matchedCallback) || result;
|
||||
}
|
||||
result =
|
||||
this._matchPartial(partialValuesMap, attrValue, cssSelector, matchedCallback) || result;
|
||||
this._matchTerminal(terminalValuesMap, value, cssSelector, matchedCallback) || result;
|
||||
|
||||
const partialValuesMap = this._attrValuePartialMap[name];
|
||||
if (value) {
|
||||
result = this._matchPartial(partialValuesMap, '', cssSelector, matchedCallback) || result;
|
||||
}
|
||||
result =
|
||||
this._matchPartial(partialValuesMap, value, cssSelector, matchedCallback) || result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@ -314,24 +291,24 @@ export class SelectorMatcher {
|
||||
|
||||
/** @internal */
|
||||
_matchTerminal(
|
||||
map: Map<string, SelectorContext[]>, 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<string, SelectorMatcher>, 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);
|
||||
|
@ -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<scopeName> > .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<scopeName> > .bar, .foo scopeName > .bar { }
|
||||
*
|
||||
* and
|
||||
*
|
||||
@ -280,7 +273,7 @@ export class ShadowCss {
|
||||
*
|
||||
* to
|
||||
*
|
||||
* scopeName.foo .bar { ... }
|
||||
* .foo<scopeName> .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,17 @@ 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[0] === ':' ? replaceBy + selector : selector + replaceBy)
|
||||
.replace(_polyfillHostRe, replaceBy + ' ');
|
||||
}
|
||||
|
||||
return scopeSelector + ' ' + selector;
|
||||
}
|
||||
|
||||
// return a selector with [name] suffix on each simple selector
|
||||
@ -404,9 +401,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 +423,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 +447,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 +463,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 +476,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 +493,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 +514,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 +537,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) {
|
||||
|
@ -9,7 +9,7 @@
|
||||
// Some of the code comes from WebComponents.JS
|
||||
// https://github.com/webcomponents/webcomponentsjs/blob/master/src/HTMLImports/path.js
|
||||
|
||||
import {StringWrapper, isBlank, isPresent} from './facade/lang';
|
||||
import {isBlank, isPresent} from './facade/lang';
|
||||
|
||||
import {UrlResolver} from './url_resolver';
|
||||
|
||||
@ -30,8 +30,8 @@ export function isStyleUrlResolvable(url: string): boolean {
|
||||
export function extractStyleUrls(
|
||||
resolver: UrlResolver, baseUrl: string, cssText: string): StyleWithImports {
|
||||
var foundUrls: string[] = [];
|
||||
var modifiedCssText = StringWrapper.replaceAllMapped(cssText, _cssImportRe, (m: string[]) => {
|
||||
var url = isPresent(m[1]) ? m[1] : m[2];
|
||||
var modifiedCssText = cssText.replace(_cssImportRe, function(...m: string[]) {
|
||||
const url = m[1] || m[2];
|
||||
if (!isStyleUrlResolvable(url)) {
|
||||
// Do not attempt to resolve non-package absolute URLs with URI scheme
|
||||
return m[0];
|
||||
|
@ -10,13 +10,9 @@ import {SecurityContext} from '@angular/core';
|
||||
|
||||
import {CompileDirectiveMetadata, CompileProviderMetadata, CompileTokenMetadata} from '../compile_metadata';
|
||||
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 +57,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,25 +68,28 @@ 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);
|
||||
}
|
||||
get fullName() {
|
||||
if (isPresent(this.target)) {
|
||||
if (this.target) {
|
||||
return `${this.target}:${this.name}`;
|
||||
} else {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
get isAnimation(): boolean { return !!this.phase; }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -123,7 +123,8 @@ export class ElementAst implements TemplateAst {
|
||||
public outputs: BoundEventAst[], public references: ReferenceAst[],
|
||||
public directives: DirectiveAst[], public providers: ProviderAst[],
|
||||
public hasViewContainer: boolean, public children: TemplateAst[],
|
||||
public ngContentIndex: number, public sourceSpan: ParseSourceSpan) {}
|
||||
public ngContentIndex: number, public sourceSpan: ParseSourceSpan,
|
||||
public endSourceSpan: ParseSourceSpan) {}
|
||||
|
||||
visit(visitor: TemplateAstVisitor, context: any): any {
|
||||
return visitor.visitElement(this, context);
|
||||
@ -240,6 +241,11 @@ export enum PropertyBindingType {
|
||||
* A visitor for {@link TemplateAst} trees that will process each node.
|
||||
*/
|
||||
export interface TemplateAstVisitor {
|
||||
// Returning a truthy value from `visit()` will prevent `templateVisitAll()` from the call to
|
||||
// the typed method and result returned will become the result included in `visitAll()`s
|
||||
// result array.
|
||||
visit?(ast: TemplateAst, context: any): any;
|
||||
|
||||
visitNgContent(ast: NgContentAst, context: any): any;
|
||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any;
|
||||
visitElement(ast: ElementAst, context: any): any;
|
||||
@ -259,10 +265,13 @@ export interface TemplateAstVisitor {
|
||||
*/
|
||||
export function templateVisitAll(
|
||||
visitor: TemplateAstVisitor, asts: TemplateAst[], context: any = null): any[] {
|
||||
var result: any[] = [];
|
||||
const result: any[] = [];
|
||||
const visit = visitor.visit ?
|
||||
(ast: TemplateAst) => visitor.visit(ast, context) || ast.visit(visitor, context) :
|
||||
(ast: TemplateAst) => ast.visit(visitor, context);
|
||||
asts.forEach(ast => {
|
||||
var astResult = ast.visit(visitor, context);
|
||||
if (isPresent(astResult)) {
|
||||
const astResult = visit(ast);
|
||||
if (astResult) {
|
||||
result.push(astResult);
|
||||
}
|
||||
});
|
||||
|
@ -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-/#"
|
||||
@ -118,22 +118,18 @@ export class TemplateParser {
|
||||
component: CompileDirectiveMetadata, template: string, directives: CompileDirectiveMetadata[],
|
||||
pipes: CompilePipeMetadata[], schemas: SchemaMetadata[],
|
||||
templateUrl: string): TemplateParseResult {
|
||||
let interpolationConfig: any;
|
||||
if (component.template) {
|
||||
interpolationConfig = InterpolationConfig.fromArray(component.template.interpolation);
|
||||
}
|
||||
let htmlAstWithErrors =
|
||||
this._htmlParser.parse(template, templateUrl, true, interpolationConfig);
|
||||
const errors: ParseError[] = htmlAstWithErrors.errors;
|
||||
let result: TemplateAst[];
|
||||
|
||||
if (errors.length == 0) {
|
||||
// Transform ICU messages to angular directives
|
||||
const expandedHtmlAst = expandNodes(htmlAstWithErrors.rootNodes);
|
||||
errors.push(...expandedHtmlAst.errors);
|
||||
htmlAstWithErrors = new ParseTreeResult(expandedHtmlAst.nodes, errors);
|
||||
}
|
||||
return this.tryParseHtml(
|
||||
this.expandHtml(this._htmlParser.parse(
|
||||
template, templateUrl, true, this.getInterpolationConfig(component))),
|
||||
component, template, directives, pipes, schemas, templateUrl);
|
||||
}
|
||||
|
||||
tryParseHtml(
|
||||
htmlAstWithErrors: ParseTreeResult, component: CompileDirectiveMetadata, template: string,
|
||||
directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[],
|
||||
schemas: SchemaMetadata[], templateUrl: string): TemplateParseResult {
|
||||
var result: TemplateAst[];
|
||||
var errors = htmlAstWithErrors.errors;
|
||||
if (htmlAstWithErrors.rootNodes.length > 0) {
|
||||
const uniqDirectives = removeIdentifierDuplicates(directives);
|
||||
const uniqPipes = removeIdentifierDuplicates(pipes);
|
||||
@ -142,13 +138,11 @@ 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 {
|
||||
result = [];
|
||||
}
|
||||
|
||||
this._assertNoReferenceDuplicationOnTemplate(result, errors);
|
||||
|
||||
if (errors.length > 0) {
|
||||
@ -163,6 +157,24 @@ export class TemplateParser {
|
||||
return new TemplateParseResult(result, errors);
|
||||
}
|
||||
|
||||
expandHtml(htmlAstWithErrors: ParseTreeResult, forced: boolean = false): ParseTreeResult {
|
||||
const errors: ParseError[] = htmlAstWithErrors.errors;
|
||||
|
||||
if (errors.length == 0 || forced) {
|
||||
// Transform ICU messages to angular directives
|
||||
const expandedHtmlAst = expandNodes(htmlAstWithErrors.rootNodes);
|
||||
errors.push(...expandedHtmlAst.errors);
|
||||
htmlAstWithErrors = new ParseTreeResult(expandedHtmlAst.nodes, errors);
|
||||
}
|
||||
return htmlAstWithErrors;
|
||||
}
|
||||
|
||||
getInterpolationConfig(component: CompileDirectiveMetadata): InterpolationConfig {
|
||||
if (component.template) {
|
||||
return InterpolationConfig.fromArray(component.template.interpolation);
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_assertNoReferenceDuplicationOnTemplate(result: TemplateAst[], errors: TemplateParseError[]):
|
||||
void {
|
||||
@ -416,10 +428,8 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
let parsedElement: TemplateAst;
|
||||
|
||||
if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
|
||||
if (isPresent(element.children) && element.children.length > 0) {
|
||||
this._reportError(
|
||||
`<ng-content> element cannot have content. <ng-content> must be immediately followed by </ng-content>`,
|
||||
element.sourceSpan);
|
||||
if (element.children && !element.children.every(_isEmptyTextNode)) {
|
||||
this._reportError(`<ng-content> element cannot have content.`, element.sourceSpan);
|
||||
}
|
||||
|
||||
parsedElement = new NgContentAst(
|
||||
@ -443,7 +453,16 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
nodeName, attrs, elementProps, events, references,
|
||||
providerContext.transformedDirectiveAsts, providerContext.transformProviders,
|
||||
providerContext.transformedHasViewContainer, children,
|
||||
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
|
||||
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan, element.endSourceSpan);
|
||||
|
||||
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 +488,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<string>();
|
||||
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 +579,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 +590,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 +611,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 +654,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 +681,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 +709,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 +842,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 +861,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 +881,14 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
const boundPropsByName = new Map<string, BoundElementOrDirectiveProperty>();
|
||||
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 +913,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 +932,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 +940,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
boundPropertyName = this._schemaRegistry.getMappedPropName(partValue);
|
||||
securityContext = this._schemaRegistry.securityContext(elementName, boundPropertyName);
|
||||
bindingType = PropertyBindingType.Property;
|
||||
this._assertNoEventBinding(boundPropertyName, sourceSpan, false);
|
||||
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 +955,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
} else {
|
||||
if (parts[0] == ATTRIBUTE_PREFIX) {
|
||||
boundPropertyName = parts[1];
|
||||
this._assertNoEventBinding(boundPropertyName, sourceSpan, true);
|
||||
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,35 +988,29 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
boundPropertyName, bindingType, securityContext, ast, unit, sourceSpan);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param propName the name of the property / attribute
|
||||
* @param sourceSpan
|
||||
* @param isAttr true when binding to an attribute
|
||||
* @private
|
||||
*/
|
||||
private _assertNoEventBinding(propName: string, sourceSpan: ParseSourceSpan, isAttr: boolean):
|
||||
void {
|
||||
if (propName.toLowerCase().startsWith('on')) {
|
||||
let msg = `Binding to event attribute '${propName}' is disallowed for security reasons, ` +
|
||||
`please use (${propName.slice(2)})=...`;
|
||||
if (!isAttr) {
|
||||
msg +=
|
||||
`\nIf '${propName}' is a directive input, make sure the directive is imported by the` +
|
||||
` current module.`;
|
||||
}
|
||||
this._reportError(msg, sourceSpan, ParseErrorLevel.FATAL);
|
||||
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) {
|
||||
@ -980,7 +1060,8 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
const allDirectiveEvents = new Set<string>();
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
@ -1013,7 +1094,7 @@ class NonBindableVisitor implements html.Visitor {
|
||||
const children = html.visitAll(this, ast.children, EMPTY_ELEMENT_CONTEXT);
|
||||
return new ElementAst(
|
||||
ast.name, html.visitAll(this, ast.attrs), [], [], [], [], [], false, children,
|
||||
ngContentIndex, ast.sourceSpan);
|
||||
ngContentIndex, ast.sourceSpan, ast.endSourceSpan);
|
||||
}
|
||||
visitComment(comment: html.Comment, context: any): any { return null; }
|
||||
|
||||
@ -1114,3 +1195,11 @@ export class PipeCollector extends RecursiveAstVisitor {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function _isAnimationLabel(name: string): boolean {
|
||||
return name[0] == '@';
|
||||
}
|
||||
|
||||
function _isEmptyTextNode(node: html.Node): boolean {
|
||||
return node instanceof html.Text && node.value.trim().length == 0;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user