Compare commits
183 Commits
4.3.4
...
5.0.0-beta
Author | SHA1 | Date | |
---|---|---|---|
cea02414b0 | |||
f0ec31e47f | |||
ff5c58be6b | |||
dca50deae4 | |||
7f2037f0b6 | |||
fd6ae571b8 | |||
b14250bef9 | |||
6fb5250185 | |||
2f9d8ff46d | |||
e54bd59f22 | |||
1e1833198d | |||
6f2038cc85 | |||
f0a55016af | |||
fcadbf4bf6 | |||
d9d00bd9b5 | |||
f69561b2de | |||
685cc26ab2 | |||
5b7432b6ea | |||
04b18a9f46 | |||
05472cb21b | |||
c0c03dc4ba | |||
f9f8924c49 | |||
10897d6473 | |||
340837aa46 | |||
42ef1be75c | |||
a5801b6020 | |||
70b62949de | |||
36161d99f6 | |||
0714139e37 | |||
bcb36d9b6d | |||
ca695e0632 | |||
9b015a95eb | |||
939dc44391 | |||
5651e4ac72 | |||
1dca575701 | |||
333a708bb6 | |||
3a227a1f6f | |||
81cb5bc3a7 | |||
1640d2aa0b | |||
5f501c722b | |||
ea07856cc5 | |||
7c47b62a96 | |||
e25b3dd163 | |||
6a88659c9a | |||
44ae6e94e3 | |||
1635a06bda | |||
3923c30df0 | |||
99017bf3ff | |||
4d117faf1a | |||
5cc9913ded | |||
1d09838622 | |||
9adf40aa77 | |||
89c616199f | |||
1e1af7ffcb | |||
a84b2bc945 | |||
7abcb99d57 | |||
49cd8513e4 | |||
82b067fc40 | |||
9479a106bb | |||
e64b54b67b | |||
cc2a4c41f9 | |||
a11542a375 | |||
b6c4af6495 | |||
67dff7bd5d | |||
381471d338 | |||
ebef5e697a | |||
d71ae278ef | |||
46207538ef | |||
71eb7437b6 | |||
b5ffbe342b | |||
0f79223008 | |||
a085223331 | |||
c383048259 | |||
b18eb04b46 | |||
c8c2ab012a | |||
ecff8e6c93 | |||
51f1da1b85 | |||
a5e18c4cdf | |||
cf6284656f | |||
3182ddaf3e | |||
416ed691e5 | |||
0fb7484d51 | |||
6a3454e81e | |||
c3fbe87012 | |||
24117d7a49 | |||
5808153359 | |||
9030c8a03e | |||
b14fc06fa2 | |||
a7f2468184 | |||
fae47d86b3 | |||
d20ac14fe2 | |||
cae3e6dca0 | |||
086f4aa72c | |||
82923a381d | |||
5152abb037 | |||
67f7032321 | |||
205abe8140 | |||
b582e2b311 | |||
91ab39cc55 | |||
38ec05f533 | |||
b3085e96c2 | |||
4cea2bd612 | |||
ce47546188 | |||
6279e50d78 | |||
8bcb268140 | |||
6fc5940959 | |||
0317c4c478 | |||
b7a6f52d59 | |||
7ae7573bc8 | |||
abee785821 | |||
619e625ee2 | |||
a6c635e69e | |||
e0a9625e46 | |||
fd0cc01eed | |||
1bfc77bf8c | |||
a094769bca | |||
b4c98305da | |||
a3a54299af | |||
15a3e2d307 | |||
54e0244954 | |||
43c33d5663 | |||
6d7799fce9 | |||
d31dc7b2b3 | |||
4cd4f7a208 | |||
72fe45db2b | |||
8d2819121b | |||
073e8ba2f2 | |||
5d1864fe68 | |||
eaa843b55f | |||
c6cf678a07 | |||
31cb418370 | |||
8de44cf5e3 | |||
c67bad4f43 | |||
410f21c75c | |||
54ea5b6ffd | |||
0af03beaed | |||
d71fa734f5 | |||
6f45519d6f | |||
65c9e13105 | |||
9208f0beea | |||
5344be5182 | |||
5db6f38b73 | |||
d22f8f54db | |||
23146c9201 | |||
a5205c686e | |||
807648251f | |||
5c62e300e1 | |||
256bc8acdd | |||
59c23c7bd7 | |||
e03adb9edd | |||
b399cb26d9 | |||
3b588fe2b0 | |||
95635c18c7 | |||
e20cfe1bbc | |||
eb6fb5f87e | |||
ad3029e786 | |||
2a2fe11e8d | |||
7d0f2cd51e | |||
36faba1aab | |||
92179bcc64 | |||
cdb069ab0e | |||
c453b7bcfa | |||
9d97163c64 | |||
f054c8360b | |||
758848961e | |||
99b666614d | |||
3f331b53b2 | |||
375d598a9f | |||
cd67fced1c | |||
a77cf7ee37 | |||
2150b45954 | |||
9f99f4fae2 | |||
c6ad212a98 | |||
47b3ecd9a3 | |||
8c81c62d46 | |||
7e72317059 | |||
0bb8423df9 | |||
95698d93ad | |||
c649da9f0a | |||
0bf0c35bca | |||
97e6901ded | |||
30e76fcd80 | |||
44b50427d9 |
@ -42,7 +42,7 @@ jobs:
|
||||
key: angular-{{ .Branch }}-{{ checksum "npm-shrinkwrap.json" }}
|
||||
|
||||
- run: bazel run @build_bazel_rules_typescript_node//:bin/npm install
|
||||
- run: bazel build ...
|
||||
- run: bazel build packages/...
|
||||
- save_cache:
|
||||
key: angular-{{ .Branch }}-{{ checksum "npm-shrinkwrap.json" }}
|
||||
paths:
|
||||
|
@ -69,8 +69,8 @@ groups:
|
||||
- "*.lock"
|
||||
- "tools/*"
|
||||
exclude:
|
||||
- "tools/@angular/tsc-wrapped/*"
|
||||
- "tools/public_api_guard/*"
|
||||
- "tools/ngc-wrapped/*"
|
||||
- "aio/*"
|
||||
users:
|
||||
- IgorMinar #primary
|
||||
@ -136,8 +136,9 @@ groups:
|
||||
compiler-cli:
|
||||
conditions:
|
||||
files:
|
||||
- "tools/@angular/tsc-wrapped/*"
|
||||
- "packages/tsc-wrapped/*"
|
||||
- "packages/compiler-cli/*"
|
||||
- "tools/ngc-wrapped/*"
|
||||
users:
|
||||
- alexeagle
|
||||
- chuckjaz
|
||||
|
148
CHANGELOG.md
148
CHANGELOG.md
@ -1,21 +1,126 @@
|
||||
<a name="4.3.4"></a>
|
||||
## [4.3.4](https://github.com/angular/angular/compare/4.3.3...4.3.4) (2017-08-10)
|
||||
<a name="5.0.0-beta.3"></a>
|
||||
# [5.0.0-beta.3](https://github.com/angular/angular/compare/5.0.0-beta.2...5.0.0-beta.3) (2017-08-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** revert container/queried animations accordingly during cancel ([#18516](https://github.com/angular/angular/issues/18516)) ([5a165eb](https://github.com/angular/angular/commit/5a165eb))
|
||||
* **animations:** support persisting dynamic styles within animation states ([#18468](https://github.com/angular/angular/issues/18468)) ([e0660b1](https://github.com/angular/angular/commit/e0660b1)), closes [#18423](https://github.com/angular/angular/issues/18423) [#17505](https://github.com/angular/angular/issues/17505)
|
||||
* **benchpress:** compile cleanly with TS 2.4 ([#18455](https://github.com/angular/angular/issues/18455)) ([5afc7ab](https://github.com/angular/angular/commit/5afc7ab))
|
||||
* **compiler:** cleanly compile with TypeScript 2.4 ([#18456](https://github.com/angular/angular/issues/18456)) ([5e4054b](https://github.com/angular/angular/commit/5e4054b))
|
||||
* **compiler:** ignore [@import](https://github.com/import) in multi-line css ([#18452](https://github.com/angular/angular/issues/18452)) ([e7e7622](https://github.com/angular/angular/commit/e7e7622)), closes [#18038](https://github.com/angular/angular/issues/18038)
|
||||
* **animations:** revert container/queried animations accordingly during cancel ([#18516](https://github.com/angular/angular/issues/18516)) ([c0c03dc](https://github.com/angular/angular/commit/c0c03dc))
|
||||
* **animations:** support persisting dynamic styles within animation states ([#18468](https://github.com/angular/angular/issues/18468)) ([05472cb](https://github.com/angular/angular/commit/05472cb)), closes [#18423](https://github.com/angular/angular/issues/18423) [#17505](https://github.com/angular/angular/issues/17505)
|
||||
* **benchpress:** compile cleanly with TS 2.4 ([#18455](https://github.com/angular/angular/issues/18455)) ([e25b3dd](https://github.com/angular/angular/commit/e25b3dd))
|
||||
* **common:** don't recreate view when context shape doesn't change ([#18277](https://github.com/angular/angular/issues/18277)) ([685cc26](https://github.com/angular/angular/commit/685cc26)), closes [#13407](https://github.com/angular/angular/issues/13407)
|
||||
* **compiler:** cleanly compile with TypeScript 2.4 ([#18456](https://github.com/angular/angular/issues/18456)) ([7c47b62](https://github.com/angular/angular/commit/7c47b62))
|
||||
* **compiler:** ignore [@import](https://github.com/import) in multi-line css ([#18452](https://github.com/angular/angular/issues/18452)) ([1dca575](https://github.com/angular/angular/commit/1dca575)), closes [#18038](https://github.com/angular/angular/issues/18038)
|
||||
* **compiler-cli:** disable buggy expression lowering ([#18513](https://github.com/angular/angular/issues/18513)) ([ca695e0](https://github.com/angular/angular/commit/ca695e0))
|
||||
* **compiler-cli:** fix and re-enable expression lowering ([#18570](https://github.com/angular/angular/issues/18570)) ([6f2038c](https://github.com/angular/angular/commit/6f2038c)), closes [#18388](https://github.com/angular/angular/issues/18388)
|
||||
* **compiler-cli:** modified ngc to throw all errors, not just syntax ([#18388](https://github.com/angular/angular/issues/18388)) ([5651e4a](https://github.com/angular/angular/commit/5651e4a))
|
||||
* **compiler-cli:** remove minimist dependency of compiler-cli/index ([#18532](https://github.com/angular/angular/issues/18532)) ([5b7432b](https://github.com/angular/angular/commit/5b7432b))
|
||||
* **core:** fix platform-browser-dynamic ([#18576](https://github.com/angular/angular/issues/18576)) ([f0a5501](https://github.com/angular/angular/commit/f0a5501))
|
||||
* **core:** forbid destroyed views to be inserted or moved in VC ([#18568](https://github.com/angular/angular/issues/18568)) ([e54bd59](https://github.com/angular/angular/commit/e54bd59)), closes [#17780](https://github.com/angular/angular/issues/17780)
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** Create StaticInjector which does not depend on Reflect polyfill. ([d9d00bd](https://github.com/angular/angular/commit/d9d00bd))
|
||||
* **forms:** add default updateOn values for groups and arrays ([#18536](https://github.com/angular/angular/issues/18536)) ([ff5c58b](https://github.com/angular/angular/commit/ff5c58b))
|
||||
* **forms:** add updateOn blur option to FormControls ([#18408](https://github.com/angular/angular/issues/18408)) ([333a708](https://github.com/angular/angular/commit/333a708)), closes [#7113](https://github.com/angular/angular/issues/7113)
|
||||
* **forms:** add updateOn submit option to FormControls ([#18514](https://github.com/angular/angular/issues/18514)) ([f69561b](https://github.com/angular/angular/commit/f69561b))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* switch angular to use StaticInjector instead of ReflectiveInjector ([fcadbf4](https://github.com/angular/angular/commit/fcadbf4)), closes [#18496](https://github.com/angular/angular/issues/18496)
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* `platformXXXX()` no longer accepts providers which depend on reflection.
|
||||
Specifically the method signature when from `Provider[]` to
|
||||
`StaticProvider[]`.
|
||||
|
||||
Example:
|
||||
Before:
|
||||
```
|
||||
[
|
||||
MyClass,
|
||||
{provide: ClassA, useClass: SubClassA}
|
||||
]
|
||||
|
||||
```
|
||||
|
||||
After:
|
||||
```
|
||||
[
|
||||
{provide: MyClass, deps: [Dep1,...]},
|
||||
{provide: ClassA, useClass: SubClassA, deps: [Dep1,...]}
|
||||
]
|
||||
```
|
||||
|
||||
NOTE: This only applies to platform creation and providers for the JIT
|
||||
compiler. It does not apply to `@Component` or `@NgModule` provides
|
||||
declarations.
|
||||
|
||||
Benchpress note: Previously Benchpress also supported reflective
|
||||
provides, which now require static providers.
|
||||
|
||||
DEPRECATION:
|
||||
|
||||
- `ReflectiveInjector` is now deprecated as it will be remove. Use
|
||||
`Injector.create` as a replacement.
|
||||
|
||||
<a name="5.0.0-beta.2"></a>
|
||||
# [5.0.0-beta.2](https://github.com/angular/angular/compare/5.0.0-beta.1...5.0.0-beta.2) (2017-08-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** do not consider arguments when determining recursion ([e64b54b](https://github.com/angular/angular/commit/e64b54b))
|
||||
* **compiler:** fix for element needing implicit parent placed in top-level ng-container ([381471d](https://github.com/angular/angular/commit/381471d)), closes [#18314](https://github.com/angular/angular/issues/18314)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **forms:** add options arg to abstract controls ([ebef5e6](https://github.com/angular/angular/commit/ebef5e6))
|
||||
* **router:** add events tracking activation of individual routes ([49cd851](https://github.com/angular/angular/commit/49cd851))
|
||||
|
||||
|
||||
|
||||
<a name="4.3.3"></a>
|
||||
## [4.3.3](https://github.com/angular/angular/compare/4.3.2...4.3.3) (2017-08-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** fix for element needing implicit parent placed in top-level ng-container ([f5cbc2e](https://github.com/angular/angular/commit/f5cbc2e)), closes [#18314](https://github.com/angular/angular/issues/18314)
|
||||
|
||||
|
||||
|
||||
<a name="5.0.0-beta.1"></a>
|
||||
# [5.0.0-beta.1](https://github.com/angular/angular/compare/5.0.0-beta.0...5.0.0-beta.1) (2017-07-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** export BrowserModule as apart of BrowserAnimationsModule ([#18263](https://github.com/angular/angular/issues/18263)) ([fd0cc01](https://github.com/angular/angular/commit/fd0cc01))
|
||||
* **compiler:** add equiv & disp attributes to Xliff2 ICU placeholders ([#18283](https://github.com/angular/angular/issues/18283)) ([38ec05f](https://github.com/angular/angular/commit/38ec05f)), closes [#17344](https://github.com/angular/angular/issues/17344)
|
||||
* **compiler:** allow numbers for ICU message cases in lexer ([#18095](https://github.com/angular/angular/issues/18095)) ([a3a5429](https://github.com/angular/angular/commit/a3a5429)), closes [#17799](https://github.com/angular/angular/issues/17799)
|
||||
* **core:** invoke error handler outside of the Angular Zone ([#18269](https://github.com/angular/angular/issues/18269)) ([7ae7573](https://github.com/angular/angular/commit/7ae7573)), closes [#17073](https://github.com/angular/angular/issues/17073) [#7774](https://github.com/angular/angular/issues/7774)
|
||||
* **platform-server:** don't clobber parse5 properties when setting ([#18237](https://github.com/angular/angular/issues/18237)) ([a094769](https://github.com/angular/angular/commit/a094769)), closes [#17050](https://github.com/angular/angular/issues/17050)
|
||||
* **router:** child CanActivate guard should wait for parent to complete ([#18110](https://github.com/angular/angular/issues/18110)) ([086f4aa](https://github.com/angular/angular/commit/086f4aa)), closes [#15670](https://github.com/angular/angular/issues/15670)
|
||||
* **router:** should throw when lazy loaded module doesn't define any routes ([#15001](https://github.com/angular/angular/issues/15001)) ([82923a3](https://github.com/angular/angular/commit/82923a3)), closes [#14596](https://github.com/angular/angular/issues/14596)
|
||||
* **upgrade:** ensure downgraded components are created in the Angular zone ([#18209](https://github.com/angular/angular/issues/18209)) ([43c33d5](https://github.com/angular/angular/commit/43c33d5))
|
||||
* **upgrade:** throw error if trying to get injector before setting ([#18209](https://github.com/angular/angular/issues/18209)) ([d31dc7b](https://github.com/angular/angular/commit/d31dc7b))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **compiler:** add representation of placeholders to xliff & xmb ([b3085e9](https://github.com/angular/angular/commit/b3085e9)), closes [#17345](https://github.com/angular/angular/issues/17345)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* latest tsickle to tree shake: abstract class methods & interfaces ([#18236](https://github.com/angular/angular/issues/18236)) ([b7a6f52](https://github.com/angular/angular/commit/b7a6f52))
|
||||
* **core:** use native addEventListener for faster rendering. ([#18107](https://github.com/angular/angular/issues/18107)) ([6279e50](https://github.com/angular/angular/commit/6279e50))
|
||||
|
||||
|
||||
|
||||
<a name="4.3.2"></a>
|
||||
## [4.3.2](https://github.com/angular/angular/compare/4.3.1...4.3.2) (2017-07-26)
|
||||
|
||||
@ -33,6 +138,35 @@
|
||||
|
||||
|
||||
|
||||
<a name="5.0.0-beta.0"></a>
|
||||
# [5.0.0-beta.0](https://github.com/angular/angular/compare/4.3.0...5.0.0-beta.0) (2017-07-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** always camelcase style property names that contain auto styles ([d22f8f5](https://github.com/angular/angular/commit/d22f8f5)), closes [#17938](https://github.com/angular/angular/issues/17938)
|
||||
* **animations:** capture cancelled animation styles within grouped animations ([23146c9](https://github.com/angular/angular/commit/23146c9)), closes [#17170](https://github.com/angular/angular/issues/17170)
|
||||
* **animations:** do not crash animations if a nested component fires CD during CD ([5db6f38](https://github.com/angular/angular/commit/5db6f38)), closes [#18193](https://github.com/angular/angular/issues/18193)
|
||||
* **animations:** make sure @.disabled works in non-animation components ([5344be5](https://github.com/angular/angular/commit/5344be5))
|
||||
* **common:** send flushed body as error instead of null ([5c62e30](https://github.com/angular/angular/commit/5c62e30)), closes [#18181](https://github.com/angular/angular/issues/18181)
|
||||
* **compiler:** ensure jit external id arguments names are unique ([95635c1](https://github.com/angular/angular/commit/95635c1))
|
||||
* **compiler-cli:** don't generate empty <target/> when extracting xliff ([65c9e13](https://github.com/angular/angular/commit/65c9e13)), closes [#15754](https://github.com/angular/angular/issues/15754)
|
||||
* **platform-server:** provide XhrFactory for HttpClient ([8076482](https://github.com/angular/angular/commit/8076482))
|
||||
* **router:** canDeactivate guards should run from bottom to top ([e20cfe1](https://github.com/angular/angular/commit/e20cfe1)), closes [#15657](https://github.com/angular/angular/issues/15657)
|
||||
* **router:** should navigate to the same url when config changes ([eb6fb5f](https://github.com/angular/angular/commit/eb6fb5f)), closes [#15535](https://github.com/angular/angular/issues/15535)
|
||||
* **router:** should run resolvers for the same route concurrently ([ad3029e](https://github.com/angular/angular/commit/ad3029e)), closes [#14279](https://github.com/angular/angular/issues/14279)
|
||||
* **router:** terminal route in custom matcher ([b399cb2](https://github.com/angular/angular/commit/b399cb2))
|
||||
* **upgrade:** allow accessing AngularJS injector from downgraded module ([a5205c6](https://github.com/angular/angular/commit/a5205c6))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **animations:** support :increment and :decrement transition aliases ([6f45519](https://github.com/angular/angular/commit/6f45519))
|
||||
* **upgrade:** propagate touched state of NgModelController ([59c23c7](https://github.com/angular/angular/commit/59c23c7))
|
||||
* **upgrade:** support lazy-loading Angular module into AngularJS app ([30e76fc](https://github.com/angular/angular/commit/30e76fc))
|
||||
|
||||
|
||||
|
||||
<a name="4.3.1"></a>
|
||||
## [4.3.1](https://github.com/angular/angular/compare/4.3.0...4.3.1) (2017-07-19)
|
||||
|
||||
|
176
aio/content/guide/language-service.md
Normal file
176
aio/content/guide/language-service.md
Normal file
@ -0,0 +1,176 @@
|
||||
# Angular Language Service
|
||||
|
||||
The Angular Language Service is a way to get completions, errors,
|
||||
hints, and navigation inside your Angular templates whether they
|
||||
are external in an HTML file or embedded in annotations/decorators
|
||||
in a string. The Angular Language Service autodetects that you are
|
||||
opening an Angular file, reads your `tsconfig.json` file, finds all the
|
||||
templates you have in your application, and then provides language
|
||||
services for any templates that you open.
|
||||
|
||||
|
||||
## Autocompletion
|
||||
|
||||
Autocompletion can speed up your development time by providing you with
|
||||
contextual possibilities and hints as you type. This example shows
|
||||
autocomplete in an interpolation. As you type it out,
|
||||
you can hit tab to complete.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/language-service/language-completion.gif" alt="autocompletion">
|
||||
</figure>
|
||||
|
||||
There are also completions within
|
||||
elements. Any elements you have as a component selector will
|
||||
show up in the completion list.
|
||||
|
||||
## Error checking
|
||||
|
||||
The Angular Language Service can also forewarn you of mistakes in your code.
|
||||
In this example, Angular doesn't know what `orders` is or where it comes from.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/language-service/language-error.gif" alt="error checking">
|
||||
</figure>
|
||||
|
||||
## Navigation
|
||||
|
||||
Navigation allows you to hover to
|
||||
see where a component, directive, module, etc. is from and then
|
||||
click and press F12 to go directly to its definition.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/language-service/language-navigation.gif" alt="navigation">
|
||||
</figure>
|
||||
|
||||
|
||||
## Angular Language Service in your editor
|
||||
|
||||
Angular Language Service is currently available for [Visual Studio Code](https://code.visualstudio.com/) and
|
||||
[WebStorm](https://www.jetbrains.com/webstorm).
|
||||
|
||||
### Visual Studio Code
|
||||
|
||||
In Visual Studio Code, install Angular Language Service from the store,
|
||||
which is accessible from the bottom icon on the left menu pane.
|
||||
You can also use the VS Quick Open (⌘+P) to search for the extension. When you've opened it,
|
||||
enter the following command:
|
||||
|
||||
```sh
|
||||
ext install ng-template
|
||||
```
|
||||
|
||||
Then click the install button to install the Angular Language Service.
|
||||
|
||||
|
||||
### WebStorm
|
||||
|
||||
In webstorm, you have to install the language service as a dev dependency.
|
||||
When Angular sees this dev dependency, it provides the
|
||||
language service inside of WebStorm. Webstorm then gives you
|
||||
colorization inside the template and autocomplete in addition to the Angular Language Service.
|
||||
|
||||
Here's the dev dependency
|
||||
you need to have in `package.json`:
|
||||
|
||||
```json
|
||||
|
||||
devDependencies {
|
||||
"@angular/language-service": "^4.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
Then in the terminal window at the root of your project,
|
||||
install the `devDependencies` with `npm` or `yarn`:
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
*OR*
|
||||
|
||||
```sh
|
||||
yarn
|
||||
```
|
||||
|
||||
*OR*
|
||||
|
||||
```sh
|
||||
yarn install
|
||||
```
|
||||
|
||||
|
||||
### Sublime Text
|
||||
|
||||
In [Sublime Text](https://www.sublimetext.com/), you first need an extension to allow Typescript.
|
||||
Install the latest version of typescript in a local `node_modules` directory:
|
||||
|
||||
```sh
|
||||
npm install --save-dev typescript
|
||||
```
|
||||
|
||||
Then install the Angular Language Service in the same location:
|
||||
```sh
|
||||
npm install --save-dev @angular/language-service
|
||||
```
|
||||
|
||||
Starting with TypeScript 2.3, TypeScript has a language service plugin model that the language service can use.
|
||||
|
||||
Next, in your user preferences (`Cmd+,` or `Ctrl+,`), add:
|
||||
|
||||
```json
|
||||
"typescript-tsdk": "<path to your folder>/node_modules/typescript/lib"
|
||||
```
|
||||
|
||||
|
||||
## Installing in your project
|
||||
|
||||
You can also install Angular Language Service in your project with the
|
||||
following `npm` command:
|
||||
|
||||
```sh
|
||||
npm install --save-dev @angular/language-service
|
||||
```
|
||||
Additionally, add the following to the `"compilerOptions"` section of
|
||||
your project's `tsconfig.json`.
|
||||
|
||||
```json
|
||||
"plugins": [
|
||||
{"name": "@angular/language-service"}
|
||||
]
|
||||
```
|
||||
Note that this only provides diagnostics and completions in `.ts`
|
||||
files. You need a custom sublime plugin (or modifications to the current plugin)
|
||||
for completions in HTML files.
|
||||
|
||||
|
||||
## How the Language Service works
|
||||
|
||||
When you use an editor with a language service, there's an
|
||||
editor process which starts a separate language process/service
|
||||
to which it speaks through an [RPC](https://en.wikipedia.org/wiki/Remote_procedure_call).
|
||||
Any time you type inside of the editor, it sends information to the other process to
|
||||
track the state of your project. When you trigger a completion list within a template, the editor process first parses the template into an HTML AST, or [abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree). Then the Angular compiler interprets
|
||||
what module the template is part of, the scope you're in, and the component selector. Then it figures out where in the template AST your cursor is. When it determines the
|
||||
context, it can then determine what the children can be.
|
||||
|
||||
It's a little more involved if you are in an interpolation. If you have an interpolation of `{{data.---}}` inside a `div` and need the completion list after `data.---`, the compiler can't use the HTML AST to find the answer. The HTML AST can only tell the compiler that there is some text with the characters "`{{data.---}}`". That's when the template parser produces an expression AST, which resides within the template AST. The Angular Language Services then looks at `data.---` within its context and asks the TypeScript Language Service what the members of data are. TypeScript then returns the list of possibilities.
|
||||
|
||||
|
||||
For more in-depth information, see the
|
||||
[Angular Language Service API](https://github.com/angular/angular/blob/master/packages/language-service/src/types.ts)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
## More on Information
|
||||
|
||||
For more information, see [Chuck Jazdzewski's presentation](https://www.youtube.com/watch?v=ez3R0Gi4z5A&t=368s) on the Angular Language
|
||||
Service from [ng-conf](https://www.ng-conf.org/) 2017.
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 8.6 KiB |
Binary file not shown.
After Width: | Height: | Size: 132 KiB |
BIN
aio/content/images/guide/language-service/language-error.gif
Normal file
BIN
aio/content/images/guide/language-service/language-error.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 274 KiB |
Binary file not shown.
After Width: | Height: | Size: 857 KiB |
@ -1,10 +1,10 @@
|
||||
<!--FULL HEADER BLOCK-->
|
||||
<!-- FULL HEADER BLOCK -->
|
||||
<header>
|
||||
|
||||
<!--BACKGROUND IMAGE-->
|
||||
<!-- BACKGROUND IMAGE -->
|
||||
<div class="background-sky hero"></div>
|
||||
|
||||
<!--INTRO SECTION -->
|
||||
<!-- INTRO SECTION -->
|
||||
<section id="intro">
|
||||
|
||||
<!-- LOGO -->
|
||||
@ -12,31 +12,33 @@
|
||||
<img src="assets/images/logos/angular/angular.svg"/>
|
||||
</div>
|
||||
|
||||
<!-- CONTAINER -->
|
||||
<!-- CONTAINER -->
|
||||
<div class="homepage-container">
|
||||
<!-- container content starts -->
|
||||
|
||||
<div class="hero-headline no-toc">One framework.<br>Mobile & desktop.</div>
|
||||
<a class="button hero-cta" href="guide/quickstart">Get Started</a>
|
||||
</div><!-- CONTAINER END -->
|
||||
</section>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
</header>
|
||||
|
||||
<!-- MAIN CONTENT -->
|
||||
<article>
|
||||
|
||||
<h1 class="no-toc" style="display: none"></h1>
|
||||
|
||||
<div class="home-rows">
|
||||
|
||||
<!--Announcement Bar-->
|
||||
<!-- Announcement Bar -->
|
||||
<div class="homepage-container">
|
||||
<div class="announcement-bar">
|
||||
<img src="generated/images/marketing/home/angular-mix.png" height="40" width="151">
|
||||
<p>Join us at our newest event, October 2017</p>
|
||||
<a class="button" href="https://angularmix.com/">Learn More</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Group 1-->
|
||||
<!-- Group 1 -->
|
||||
<div layout="row" layout-xs="column" class="home-row homepage-container">
|
||||
<div class="promo-img-container promo-1">
|
||||
<div>
|
||||
@ -53,7 +55,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<!-- Group 2-->
|
||||
|
||||
<!-- Group 2 -->
|
||||
<div layout="row" layout-xs="column" class="home-row">
|
||||
<div class="text-container">
|
||||
<div class="text-block">
|
||||
@ -71,7 +74,7 @@
|
||||
</div>
|
||||
<hr>
|
||||
|
||||
<!-- Group 3-->
|
||||
<!-- Group 3 -->
|
||||
<div layout="row" layout-xs="column" class="home-row">
|
||||
<div class="promo-img-container promo-3">
|
||||
<div><img src="generated/images/marketing/home/joyful-development.svg" alt="IDE example"></div>
|
||||
@ -88,9 +91,8 @@
|
||||
</div>
|
||||
<hr>
|
||||
|
||||
<!-- Group 4-->
|
||||
<!-- Group 4 -->
|
||||
<div layout="row" layout-xs="column" class="home-row">
|
||||
|
||||
<div class="text-container">
|
||||
<div class="text-block l-pad-top-2">
|
||||
<div class="text-headline">Loved by Millions</div>
|
||||
@ -105,20 +107,19 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CTA CARDS -->
|
||||
<div layout="row" layout-xs="column" class="home-row">
|
||||
|
||||
<a href="guide/quickstart">
|
||||
<div class="card">
|
||||
<!-- CTA CARDS -->
|
||||
<div layout="row" layout-xs="column" class="home-row">
|
||||
<a href="guide/quickstart">
|
||||
<div class="card">
|
||||
<img src="generated/images/marketing/home/code-icon.svg" height="70px">
|
||||
<div class="card-text-container">
|
||||
<div class="text-headline">Get Started</div>
|
||||
<p>Start building your Angular application.</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div> <!-- end of home rows -->
|
||||
</div><!-- end of home rows -->
|
||||
|
||||
</article>
|
||||
|
@ -288,6 +288,11 @@
|
||||
"title": "Internationalization (i18n)",
|
||||
"tooltip": "Translate the app's template text into multiple languages."
|
||||
},
|
||||
{
|
||||
"url": "guide/language-service",
|
||||
"title": "Language Service",
|
||||
"tooltip": "Use Angular Language Service to speed up dev time."
|
||||
},
|
||||
{
|
||||
"url": "guide/security",
|
||||
"title": "Security",
|
||||
|
@ -41,12 +41,9 @@ const ANGULAR_PACKAGES = [
|
||||
'platform-browser-dynamic',
|
||||
'platform-server',
|
||||
'router',
|
||||
'tsc-wrapped',
|
||||
'upgrade',
|
||||
];
|
||||
const ANGULAR_TOOLS_PACKAGES_PATH = path.resolve(ANGULAR_DIST_PATH, 'tools', '@angular');
|
||||
const ANGULAR_TOOLS_PACKAGES = [
|
||||
'tsc-wrapped'
|
||||
];
|
||||
|
||||
const EXAMPLE_CONFIG_FILENAME = 'example-config.json';
|
||||
|
||||
@ -63,7 +60,6 @@ class ExampleBoilerPlate {
|
||||
// Replace the Angular packages with those from the dist folder, if necessary
|
||||
if (useLocal) {
|
||||
ANGULAR_PACKAGES.forEach(packageName => this.overridePackage(ANGULAR_PACKAGES_PATH, packageName));
|
||||
ANGULAR_TOOLS_PACKAGES.forEach(packageName => this.overridePackage(ANGULAR_TOOLS_PACKAGES_PATH, packageName));
|
||||
}
|
||||
|
||||
// Get all the examples folders, indicated by those that contain a `example-config.json` file
|
||||
@ -137,4 +133,4 @@ module.exports = new ExampleBoilerPlate();
|
||||
// If this file was run directly then run the main function,
|
||||
if (require.main === module) {
|
||||
module.exports.main();
|
||||
}
|
||||
}
|
@ -32,7 +32,7 @@ describe('example-boilerplate tool', () => {
|
||||
// for example
|
||||
expect(exampleBoilerPlate.overridePackage).toHaveBeenCalledWith(path.resolve(__dirname, '../../../dist/packages-dist'), 'common');
|
||||
expect(exampleBoilerPlate.overridePackage).toHaveBeenCalledWith(path.resolve(__dirname, '../../../dist/packages-dist'), 'core');
|
||||
expect(exampleBoilerPlate.overridePackage).toHaveBeenCalledWith(path.resolve(__dirname, '../../../dist/tools/@angular'), 'tsc-wrapped');
|
||||
expect(exampleBoilerPlate.overridePackage).toHaveBeenCalledWith(path.resolve(__dirname, '../../../dist/packages-dist'), 'tsc-wrapped');
|
||||
});
|
||||
|
||||
it('should process all the example folders', () => {
|
||||
|
@ -34,5 +34,5 @@ function getText(h1) {
|
||||
(node.properties.ariaHidden === 'true' || node.properties['aria-hidden'] === 'true')
|
||||
));
|
||||
|
||||
return toString(cleaned);
|
||||
}
|
||||
return cleaned ? toString(cleaned) : '';
|
||||
}
|
||||
|
@ -69,4 +69,14 @@ describe('h1Checker postprocessor', () => {
|
||||
processor.$process([doc]);
|
||||
expect(doc.vFile.title).toEqual('What is Angular?');
|
||||
});
|
||||
});
|
||||
|
||||
it('should not break if the h1 is empty (except for an aria-hidden anchor)', () => {
|
||||
const doc = {
|
||||
docType: 'a',
|
||||
renderedContent: `
|
||||
<h1><a aria-hidden="true"></a></h1>
|
||||
`
|
||||
};
|
||||
expect(() => processor.$process([doc])).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
22
build.sh
22
build.sh
@ -333,7 +333,7 @@ echo "====== BUILDING: Version ${VERSION}"
|
||||
N="
|
||||
"
|
||||
TSC=`pwd`/node_modules/.bin/tsc
|
||||
NGC="node --max-old-space-size=3000 dist/tools/@angular/tsc-wrapped/src/main"
|
||||
NGC="node --max-old-space-size=3000 dist/packages-dist/tsc-wrapped/src/main"
|
||||
MAP_SOURCES="node `pwd`/scripts/build/map_sources.js "
|
||||
UGLIFYJS=`pwd`/node_modules/.bin/uglifyjs
|
||||
TSCONFIG=./tools/tsconfig.json
|
||||
@ -345,8 +345,6 @@ if [[ ${BUILD_TOOLS} == true ]]; then
|
||||
rm -rf ./dist/tools/
|
||||
mkdir -p ./dist/tools/
|
||||
$(npm bin)/tsc -p ${TSCONFIG}
|
||||
|
||||
cp ./tools/@angular/tsc-wrapped/package.json ./dist/tools/@angular/tsc-wrapped
|
||||
travisFoldEnd "build tools"
|
||||
fi
|
||||
|
||||
@ -398,11 +396,11 @@ if [[ ${BUILD_ALL} == true && ${TYPECHECK_ALL} == true ]]; then
|
||||
|
||||
TSCONFIG="packages/tsconfig.json"
|
||||
travisFoldStart "tsc -p ${TSCONFIG}" "no-xtrace"
|
||||
$NGC -p ${TSCONFIG}
|
||||
$TSC -p ${TSCONFIG}
|
||||
travisFoldEnd "tsc -p ${TSCONFIG}"
|
||||
TSCONFIG="modules/tsconfig.json"
|
||||
travisFoldStart "tsc -p ${TSCONFIG}" "no-xtrace"
|
||||
$NGC -p ${TSCONFIG}
|
||||
$TSC -p ${TSCONFIG}
|
||||
travisFoldEnd "tsc -p ${TSCONFIG}"
|
||||
|
||||
fi
|
||||
@ -414,6 +412,20 @@ if [[ ${BUILD_ALL} == true ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ${BUILD_TOOLS} == true || ${BUILD_ALL} == true ]]; then
|
||||
echo "====== (tsc-wrapped)COMPILING: \$(npm bin)/tsc -p packages/tsc-wrapped/tsconfig.json ====="
|
||||
$(npm bin)/tsc -p packages/tsc-wrapped/tsconfig.json
|
||||
echo "====== (tsc-wrapped)COMPILING: \$(npm bin)/tsc -p packages/tsc-wrapped/tsconfig-build.json ====="
|
||||
$(npm bin)/tsc -p packages/tsc-wrapped/tsconfig-build.json
|
||||
cp ./packages/tsc-wrapped/package.json ./dist/packages-dist/tsc-wrapped
|
||||
cp ./packages/tsc-wrapped/README.md ./dist/packages-dist/tsc-wrapped
|
||||
(
|
||||
cd dist/packages-dist/tsc-wrapped
|
||||
echo "====== EXECUTE: perl -p -i -e \"s/0\.0\.0\-PLACEHOLDER/${VERSION}/g\" $""(grep -ril 0\.0\.0\-PLACEHOLDER .)"
|
||||
perl -p -i -e "s/0\.0\.0\-PLACEHOLDER/${VERSION}/g" $(grep -ril 0\.0\.0\-PLACEHOLDER .) < /dev/null 2> /dev/null
|
||||
)
|
||||
fi
|
||||
|
||||
for PACKAGE in ${PACKAGES[@]}
|
||||
do
|
||||
travisFoldStart "build package: ${PACKAGE}" "no-xtrace"
|
||||
|
@ -10,7 +10,7 @@
|
||||
"@angular/core": "file:../../dist/packages-dist/core",
|
||||
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
|
||||
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
|
||||
"@angular/tsc-wrapped": "file:../../dist/tools/@angular/tsc-wrapped",
|
||||
"@angular/tsc-wrapped": "file:../../dist/packages-dist/tsc-wrapped",
|
||||
"google-closure-compiler": "20170409.0.0",
|
||||
"rxjs": "5.3.1",
|
||||
"typescript": "2.1.6",
|
||||
|
@ -12,7 +12,7 @@
|
||||
"@angular/language-service": "file:../../dist/packages-dist/language-service",
|
||||
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
|
||||
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
|
||||
"@angular/tsc-wrapped": "file:../../dist/tools/@angular/tsc-wrapped",
|
||||
"@angular/tsc-wrapped": "file:../../dist/packages-dist/tsc-wrapped",
|
||||
"@types/minimist": "^1.2.0",
|
||||
"@types/node": "^7.0.5",
|
||||
"minimist": "^1.2.0",
|
||||
|
@ -15,7 +15,7 @@
|
||||
"@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic",
|
||||
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
|
||||
"@angular/router": "file:../../dist/packages-dist/router",
|
||||
"@angular/tsc-wrapped": "file:../../dist/tools/@angular/tsc-wrapped",
|
||||
"@angular/tsc-wrapped": "file:../../dist/packages-dist/tsc-wrapped",
|
||||
"@angular/upgrade": "file:../../dist/packages-dist/upgrade",
|
||||
"@types/jasmine": "2.5.41",
|
||||
"rxjs": "file:../../node_modules/rxjs",
|
||||
|
@ -15,7 +15,7 @@
|
||||
"@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic",
|
||||
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
|
||||
"@angular/router": "file:../../dist/packages-dist/router",
|
||||
"@angular/tsc-wrapped": "file:../../dist/tools/@angular/tsc-wrapped",
|
||||
"@angular/tsc-wrapped": "file:../../dist/packages-dist/tsc-wrapped",
|
||||
"@angular/upgrade": "file:../../dist/packages-dist/upgrade",
|
||||
"@types/jasmine": "2.5.41",
|
||||
"rxjs": "file:../../node_modules/rxjs",
|
||||
|
@ -15,7 +15,7 @@
|
||||
"@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic",
|
||||
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
|
||||
"@angular/router": "file:../../dist/packages-dist/router",
|
||||
"@angular/tsc-wrapped": "file:../../dist/tools/@angular/tsc-wrapped",
|
||||
"@angular/tsc-wrapped": "file:../../dist/packages-dist/tsc-wrapped",
|
||||
"@angular/upgrade": "file:../../dist/packages-dist/upgrade",
|
||||
"@types/jasmine": "2.5.41",
|
||||
"rxjs": "file:../../node_modules/rxjs",
|
||||
|
@ -59,6 +59,7 @@ module.exports = function(config) {
|
||||
'dist/all/@angular/examples/**/e2e_test/*',
|
||||
'dist/all/@angular/language-service/**',
|
||||
'dist/all/@angular/router/test/**',
|
||||
'dist/all/@angular/tsc-wrapped/**',
|
||||
'dist/all/@angular/platform-browser/testing/e2e_util.js',
|
||||
'dist/all/angular1_router.js',
|
||||
'dist/examples/**/e2e_test/**',
|
||||
|
@ -11,7 +11,7 @@ const yargs = require('yargs');
|
||||
const nodeUuid = require('node-uuid');
|
||||
import * as fs from 'fs-extra';
|
||||
|
||||
import {SeleniumWebDriverAdapter, Options, JsonFileReporter, Validator, RegressionSlopeValidator, ConsoleReporter, SizeValidator, MultiReporter, MultiMetric, Runner, Provider} from '@angular/benchpress';
|
||||
import {SeleniumWebDriverAdapter, Options, JsonFileReporter, Validator, RegressionSlopeValidator, ConsoleReporter, SizeValidator, MultiReporter, MultiMetric, Runner, StaticProvider} from '@angular/benchpress';
|
||||
import {readCommandLine as readE2eCommandLine, openBrowser} from './e2e_util';
|
||||
|
||||
let cmdArgs: {'sample-size': number, 'force-gc': boolean, 'dryrun': boolean, 'bundles': boolean};
|
||||
@ -59,7 +59,7 @@ function createBenchpressRunner(): Runner {
|
||||
}
|
||||
const resultsFolder = './dist/benchmark_results';
|
||||
fs.ensureDirSync(resultsFolder);
|
||||
const providers: Provider[] = [
|
||||
const providers: StaticProvider[] = [
|
||||
SeleniumWebDriverAdapter.PROTRACTOR_PROVIDERS,
|
||||
{provide: Options.FORCE_GC, useValue: cmdArgs['force-gc']},
|
||||
{provide: Options.DEFAULT_DESCRIPTION, useValue: {'runId': runId}}, JsonFileReporter.PROVIDERS,
|
||||
|
@ -1,6 +1,8 @@
|
||||
# How to run the examples locally
|
||||
|
||||
```
|
||||
$ cp -r ./modules/playground ./dist/all/
|
||||
$ ./node_modules/.bin/tsc -p modules --emitDecoratorMetadata -w
|
||||
$ gulp serve
|
||||
$ open http://localhost:8000/all/playground/src/hello_world/index.html?bundles=false
|
||||
```
|
@ -13,8 +13,8 @@
|
||||
"selenium-webdriver": ["../node_modules/@types/selenium-webdriver/index.d.ts"],
|
||||
"rxjs/*": ["../node_modules/rxjs/*"],
|
||||
"@angular/*": ["../dist/all/@angular/*"],
|
||||
"@angular/tsc-wrapped": ["../dist/tools/@angular/tsc-wrapped"],
|
||||
"@angular/tsc-wrapped/*": ["../dist/tools/@angular/tsc-wrapped/*"]
|
||||
"@angular/tsc-wrapped": ["../dist/packages-dist/tsc-wrapped"],
|
||||
"@angular/tsc-wrapped/*": ["../dist/packages-dist/tsc-wrapped/*"]
|
||||
},
|
||||
"rootDir": ".",
|
||||
"inlineSourceMap": true,
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "4.3.0-beta.1",
|
||||
"version": "5.0.0-beta.0",
|
||||
"dependencies": {
|
||||
"@types/angularjs": {
|
||||
"version": "1.5.13-alpha"
|
||||
@ -4841,7 +4841,7 @@
|
||||
}
|
||||
},
|
||||
"tsickle": {
|
||||
"version": "0.21.6"
|
||||
"version": "0.23.4"
|
||||
},
|
||||
"tslib": {
|
||||
"version": "1.7.1"
|
||||
@ -4901,7 +4901,7 @@
|
||||
"version": "0.0.6"
|
||||
},
|
||||
"typescript": {
|
||||
"version": "2.3.2"
|
||||
"version": "2.3.4"
|
||||
},
|
||||
"ua-parser-js": {
|
||||
"version": "0.7.10"
|
||||
|
8
npm-shrinkwrap.json
generated
8
npm-shrinkwrap.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "4.3.0-beta.1",
|
||||
"version": "5.0.0-beta.0",
|
||||
"dependencies": {
|
||||
"@types/angularjs": {
|
||||
"version": "1.5.13-alpha",
|
||||
@ -7733,9 +7733,9 @@
|
||||
}
|
||||
},
|
||||
"tsickle": {
|
||||
"version": "0.21.6",
|
||||
"from": "tsickle@0.21.6",
|
||||
"resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.21.6.tgz"
|
||||
"version": "0.23.4",
|
||||
"from": "tsickle@0.23.4",
|
||||
"resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.23.4.tgz"
|
||||
},
|
||||
"tslib": {
|
||||
"version": "1.7.1",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "4.3.4",
|
||||
"version": "5.0.0-beta.3",
|
||||
"private": true,
|
||||
"branchPattern": "2.0.*",
|
||||
"description": "Angular - a web framework for modern web apps",
|
||||
@ -93,10 +93,10 @@
|
||||
"source-map-support": "^0.4.2",
|
||||
"systemjs": "0.18.10",
|
||||
"ts-api-guardian": "^0.2.2",
|
||||
"tsickle": "^0.21.1",
|
||||
"tsickle": "^0.23.4",
|
||||
"tslint": "^4.1.1",
|
||||
"tslint-eslint-rules": "^3.1.0",
|
||||
"typescript": "^2.3.2",
|
||||
"typescript": "^2.3.4",
|
||||
"universal-analytics": "^0.3.9",
|
||||
"vrsource-tslint-rules": "^4.0.0",
|
||||
"webpack": "^1.12.6",
|
||||
|
@ -24,8 +24,14 @@ export function parseTransitionExpr(
|
||||
function parseInnerTransitionStr(
|
||||
eventStr: string, expressions: TransitionMatcherFn[], errors: string[]) {
|
||||
if (eventStr[0] == ':') {
|
||||
eventStr = parseAnimationAlias(eventStr, errors);
|
||||
const result = parseAnimationAlias(eventStr, errors);
|
||||
if (typeof result == 'function') {
|
||||
expressions.push(result);
|
||||
return;
|
||||
}
|
||||
eventStr = result as string;
|
||||
}
|
||||
|
||||
const match = eventStr.match(/^(\*|[-\w]+)\s*(<?[=-]>)\s*(\*|[-\w]+)$/);
|
||||
if (match == null || match.length < 4) {
|
||||
errors.push(`The provided transition expression "${eventStr}" is not supported`);
|
||||
@ -43,12 +49,16 @@ function parseInnerTransitionStr(
|
||||
}
|
||||
}
|
||||
|
||||
function parseAnimationAlias(alias: string, errors: string[]): string {
|
||||
function parseAnimationAlias(alias: string, errors: string[]): string|TransitionMatcherFn {
|
||||
switch (alias) {
|
||||
case ':enter':
|
||||
return 'void => *';
|
||||
case ':leave':
|
||||
return '* => void';
|
||||
case ':increment':
|
||||
return (fromState: any, toState: any): boolean => parseFloat(toState) > parseFloat(fromState);
|
||||
case ':decrement':
|
||||
return (fromState: any, toState: any): boolean => parseFloat(toState) < parseFloat(fromState);
|
||||
default:
|
||||
errors.push(`The transition alias value "${alias}" is not supported`);
|
||||
return '* => *';
|
||||
|
@ -509,7 +509,7 @@ export class TransitionAnimationEngine {
|
||||
// this method is designed to be overridden by the code that uses this engine
|
||||
public onRemovalComplete = (element: any, context: any) => {};
|
||||
|
||||
// tslint:disable-next-line
|
||||
/** @internal */
|
||||
_onRemovalComplete(element: any, context: any) { this.onRemovalComplete(element, context); }
|
||||
|
||||
constructor(public driver: AnimationDriver, private _normalizer: AnimationStyleNormalizer) {}
|
||||
|
@ -710,7 +710,7 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe
|
||||
* ])
|
||||
* ```
|
||||
*
|
||||
* ### Transition Aliases (`:enter` and `:leave`)
|
||||
* ### Using :enter and :leave
|
||||
*
|
||||
* Given that enter (insertion) and leave (removal) animations are so common, the `transition`
|
||||
* function accepts both `:enter` and `:leave` values which are aliases for the `void => *` and `*
|
||||
@ -720,12 +720,88 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe
|
||||
* transition(":enter", [
|
||||
* style({ opacity: 0 }),
|
||||
* animate(500, style({ opacity: 1 }))
|
||||
* ])
|
||||
* ]),
|
||||
* transition(":leave", [
|
||||
* animate(500, style({ opacity: 0 }))
|
||||
* ])
|
||||
* ```
|
||||
*
|
||||
* ### Using :increment and :decrement
|
||||
* In addition to the :enter and :leave transition aliases, the :increment and :decrement aliases
|
||||
* can be used to kick off a transition when a numeric value has increased or decreased in value.
|
||||
*
|
||||
* ```
|
||||
* import {group, animate, query, transition, style, trigger} from '@angular/animations';
|
||||
* import {Component} from '@angular/core';
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'banner-carousel-component',
|
||||
* styles: [`
|
||||
* .banner-container {
|
||||
* position:relative;
|
||||
* height:500px;
|
||||
* overflow:hidden;
|
||||
* }
|
||||
* .banner-container > .banner {
|
||||
* position:absolute;
|
||||
* left:0;
|
||||
* top:0;
|
||||
* font-size:200px;
|
||||
* line-height:500px;
|
||||
* font-weight:bold;
|
||||
* text-align:center;
|
||||
* width:100%;
|
||||
* }
|
||||
* `],
|
||||
* template: `
|
||||
* <button (click)="previous()">Previous</button>
|
||||
* <button (click)="next()">Next</button>
|
||||
* <hr>
|
||||
* <div [@bannerAnimation]="selectedIndex" class="banner-container">
|
||||
* <div class="banner"> {{ banner }} </div>
|
||||
* </div>
|
||||
* `
|
||||
* animations: [
|
||||
* trigger('bannerAnimation', [
|
||||
* transition(":increment", group([
|
||||
* query(':enter', [
|
||||
* style({ left: '100%' }),
|
||||
* animate('0.5s ease-out', style('*'))
|
||||
* ]),
|
||||
* query(':leave', [
|
||||
* animate('0.5s ease-out', style({ left: '-100%' }))
|
||||
* ])
|
||||
* ])),
|
||||
* transition(":decrement", group([
|
||||
* query(':enter', [
|
||||
* style({ left: '-100%' }),
|
||||
* animate('0.5s ease-out', style('*'))
|
||||
* ]),
|
||||
* query(':leave', [
|
||||
* animate('0.5s ease-out', style({ left: '100%' }))
|
||||
* ])
|
||||
* ])),
|
||||
* ])
|
||||
* ]
|
||||
* })
|
||||
* class BannerCarouselComponent {
|
||||
* allBanners: string[] = ['1', '2', '3', '4'];
|
||||
* selectedIndex: number = 0;
|
||||
*
|
||||
* get banners() {
|
||||
* return [this.allBanners[this.selectedIndex]];
|
||||
* }
|
||||
*
|
||||
* previous() {
|
||||
* this.selectedIndex = Math.max(this.selectedIndex - 1, 0);
|
||||
* }
|
||||
*
|
||||
* next() {
|
||||
* this.selectedIndex = Math.min(this.selectedIndex + 1, this.allBanners.length - 1);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* {@example core/animation/ts/dsl/animation_example.ts region='Component'}
|
||||
*
|
||||
* @experimental Animation support is experimental.
|
||||
|
@ -9,7 +9,7 @@
|
||||
// Must be imported first, because Angular decorators throw on load.
|
||||
import 'reflect-metadata';
|
||||
|
||||
export {InjectionToken, Injector, Provider, ReflectiveInjector} from '@angular/core';
|
||||
export {InjectionToken, Injector, Provider, ReflectiveInjector, StaticProvider} from '@angular/core';
|
||||
export {Options} from './src/common_options';
|
||||
export {MeasureValues} from './src/measure_values';
|
||||
export {Metric} from './src/metric';
|
||||
|
@ -20,7 +20,14 @@ import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_e
|
||||
export class PerflogMetric extends Metric {
|
||||
static SET_TIMEOUT = new InjectionToken('PerflogMetric.setTimeout');
|
||||
static PROVIDERS = [
|
||||
PerflogMetric, {
|
||||
{
|
||||
provide: PerflogMetric,
|
||||
deps: [
|
||||
WebDriverExtension, PerflogMetric.SET_TIMEOUT, Options.MICRO_METRICS, Options.FORCE_GC,
|
||||
Options.CAPTURE_FRAMES, Options.RECEIVED_DATA, Options.REQUEST_COUNT
|
||||
]
|
||||
},
|
||||
{
|
||||
provide: PerflogMetric.SET_TIMEOUT,
|
||||
useValue: (fn: Function, millis: number) => <any>setTimeout(fn, millis)
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Inject, Injectable} from '@angular/core';
|
||||
import {Inject, Injectable, StaticProvider} from '@angular/core';
|
||||
|
||||
import {Options} from '../common_options';
|
||||
import {Metric} from '../metric';
|
||||
@ -14,7 +14,8 @@ import {WebDriverAdapter} from '../web_driver_adapter';
|
||||
|
||||
@Injectable()
|
||||
export class UserMetric extends Metric {
|
||||
static PROVIDERS = [UserMetric];
|
||||
static PROVIDERS =
|
||||
<StaticProvider[]>[{provide: UserMetric, deps: [Options.USER_METRICS, WebDriverAdapter]}];
|
||||
|
||||
constructor(
|
||||
@Inject(Options.USER_METRICS) private _userMetrics: {[key: string]: string},
|
||||
|
@ -22,7 +22,11 @@ export class ConsoleReporter extends Reporter {
|
||||
static PRINT = new InjectionToken('ConsoleReporter.print');
|
||||
static COLUMN_WIDTH = new InjectionToken('ConsoleReporter.columnWidth');
|
||||
static PROVIDERS = [
|
||||
ConsoleReporter, {provide: ConsoleReporter.COLUMN_WIDTH, useValue: 18}, {
|
||||
{
|
||||
provide: ConsoleReporter,
|
||||
deps: [ConsoleReporter.COLUMN_WIDTH, SampleDescription, ConsoleReporter.PRINT]
|
||||
},
|
||||
{provide: ConsoleReporter.COLUMN_WIDTH, useValue: 18}, {
|
||||
provide: ConsoleReporter.PRINT,
|
||||
useValue: function(v: any) {
|
||||
// tslint:disable-next-line:no-console
|
||||
|
@ -22,7 +22,13 @@ import {formatStats, sortedProps} from './util';
|
||||
@Injectable()
|
||||
export class JsonFileReporter extends Reporter {
|
||||
static PATH = new InjectionToken('JsonFileReporter.path');
|
||||
static PROVIDERS = [JsonFileReporter, {provide: JsonFileReporter.PATH, useValue: '.'}];
|
||||
static PROVIDERS = [
|
||||
{
|
||||
provide: JsonFileReporter,
|
||||
deps: [SampleDescription, JsonFileReporter.PATH, Options.WRITE_FILE, Options.NOW]
|
||||
},
|
||||
{provide: JsonFileReporter.PATH, useValue: '.'}
|
||||
];
|
||||
|
||||
constructor(
|
||||
private _description: SampleDescription, @Inject(JsonFileReporter.PATH) private _path: string,
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Provider, ReflectiveInjector} from '@angular/core';
|
||||
import {Injector, StaticProvider} from '@angular/core';
|
||||
|
||||
import {Options} from './common_options';
|
||||
import {Metric} from './metric';
|
||||
@ -34,17 +34,17 @@ import {IOsDriverExtension} from './webdriver/ios_driver_extension';
|
||||
* It provides defaults, creates the injector and calls the sampler.
|
||||
*/
|
||||
export class Runner {
|
||||
constructor(private _defaultProviders: Provider[] = []) {}
|
||||
constructor(private _defaultProviders: StaticProvider[] = []) {}
|
||||
|
||||
sample({id, execute, prepare, microMetrics, providers, userMetrics}: {
|
||||
id: string,
|
||||
execute?: Function,
|
||||
prepare?: Function,
|
||||
microMetrics?: {[key: string]: string},
|
||||
providers?: Provider[],
|
||||
providers?: StaticProvider[],
|
||||
userMetrics?: {[key: string]: string}
|
||||
}): Promise<SampleState> {
|
||||
const sampleProviders: Provider[] = [
|
||||
const sampleProviders: StaticProvider[] = [
|
||||
_DEFAULT_PROVIDERS, this._defaultProviders, {provide: Options.SAMPLE_ID, useValue: id},
|
||||
{provide: Options.EXECUTE, useValue: execute}
|
||||
];
|
||||
@ -61,7 +61,7 @@ export class Runner {
|
||||
sampleProviders.push(providers);
|
||||
}
|
||||
|
||||
const inj = ReflectiveInjector.resolveAndCreate(sampleProviders);
|
||||
const inj = Injector.create(sampleProviders);
|
||||
const adapter: WebDriverAdapter = inj.get(WebDriverAdapter);
|
||||
|
||||
return Promise
|
||||
@ -75,7 +75,7 @@ export class Runner {
|
||||
// Only WebDriverAdapter is reused.
|
||||
// TODO vsavkin consider changing it when toAsyncFactory is added back or when child
|
||||
// injectors are handled better.
|
||||
const injector = ReflectiveInjector.resolveAndCreate([
|
||||
const injector = Injector.create([
|
||||
sampleProviders, {provide: Options.CAPABILITIES, useValue: capabilities},
|
||||
{provide: Options.USER_AGENT, useValue: userAgent},
|
||||
{provide: WebDriverAdapter, useValue: adapter}
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Inject, Injectable} from '@angular/core';
|
||||
import {Inject, Injectable, StaticProvider} from '@angular/core';
|
||||
|
||||
import {Options} from './common_options';
|
||||
import {MeasureValues} from './measure_values';
|
||||
@ -26,8 +26,12 @@ import {WebDriverAdapter} from './web_driver_adapter';
|
||||
*/
|
||||
@Injectable()
|
||||
export class Sampler {
|
||||
static PROVIDERS = [Sampler];
|
||||
|
||||
static PROVIDERS = <StaticProvider[]>[{
|
||||
provide: Sampler,
|
||||
deps: [
|
||||
WebDriverAdapter, Metric, Reporter, Validator, Options.PREPARE, Options.EXECUTE, Options.NOW
|
||||
]
|
||||
}];
|
||||
constructor(
|
||||
private _driver: WebDriverAdapter, private _metric: Metric, private _reporter: Reporter,
|
||||
private _validator: Validator, @Inject(Options.PREPARE) private _prepare: Function,
|
||||
|
@ -21,7 +21,11 @@ export class RegressionSlopeValidator extends Validator {
|
||||
static SAMPLE_SIZE = new InjectionToken('RegressionSlopeValidator.sampleSize');
|
||||
static METRIC = new InjectionToken('RegressionSlopeValidator.metric');
|
||||
static PROVIDERS = [
|
||||
RegressionSlopeValidator, {provide: RegressionSlopeValidator.SAMPLE_SIZE, useValue: 10},
|
||||
{
|
||||
provide: RegressionSlopeValidator,
|
||||
deps: [RegressionSlopeValidator.SAMPLE_SIZE, RegressionSlopeValidator.METRIC]
|
||||
},
|
||||
{provide: RegressionSlopeValidator.SAMPLE_SIZE, useValue: 10},
|
||||
{provide: RegressionSlopeValidator.METRIC, useValue: 'scriptTime'}
|
||||
];
|
||||
|
||||
|
@ -17,7 +17,10 @@ import {Validator} from '../validator';
|
||||
@Injectable()
|
||||
export class SizeValidator extends Validator {
|
||||
static SAMPLE_SIZE = new InjectionToken('SizeValidator.sampleSize');
|
||||
static PROVIDERS = [SizeValidator, {provide: SizeValidator.SAMPLE_SIZE, useValue: 10}];
|
||||
static PROVIDERS = [
|
||||
{provide: SizeValidator, deps: [SizeValidator.SAMPLE_SIZE]},
|
||||
{provide: SizeValidator.SAMPLE_SIZE, useValue: 10}
|
||||
];
|
||||
|
||||
constructor(@Inject(SizeValidator.SAMPLE_SIZE) private _sampleSize: number) { super(); }
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Inject, Injectable} from '@angular/core';
|
||||
import {Inject, Injectable, StaticProvider} from '@angular/core';
|
||||
|
||||
import {Options} from '../common_options';
|
||||
import {WebDriverAdapter} from '../web_driver_adapter';
|
||||
@ -21,7 +21,10 @@ import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_e
|
||||
*/
|
||||
@Injectable()
|
||||
export class ChromeDriverExtension extends WebDriverExtension {
|
||||
static PROVIDERS = [ChromeDriverExtension];
|
||||
static PROVIDERS = <StaticProvider>[{
|
||||
provide: ChromeDriverExtension,
|
||||
deps: [WebDriverAdapter, Options.USER_AGENT]
|
||||
}];
|
||||
|
||||
private _majorChromeVersion: number;
|
||||
private _firstRun = true;
|
||||
|
@ -13,7 +13,7 @@ import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_e
|
||||
|
||||
@Injectable()
|
||||
export class FirefoxDriverExtension extends WebDriverExtension {
|
||||
static PROVIDERS = [FirefoxDriverExtension];
|
||||
static PROVIDERS = [{provide: FirefoxDriverExtension, deps: [WebDriverAdapter]}];
|
||||
|
||||
private _profilerStarted: boolean;
|
||||
|
||||
|
@ -13,7 +13,7 @@ import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_e
|
||||
|
||||
@Injectable()
|
||||
export class IOsDriverExtension extends WebDriverExtension {
|
||||
static PROVIDERS = [IOsDriverExtension];
|
||||
static PROVIDERS = [{provide: IOsDriverExtension, deps: [WebDriverAdapter]}];
|
||||
|
||||
constructor(private _driver: WebDriverAdapter) { super(); }
|
||||
|
||||
|
@ -6,15 +6,19 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {StaticProvider} from '@angular/core';
|
||||
|
||||
import {WebDriverAdapter} from '../web_driver_adapter';
|
||||
|
||||
|
||||
/**
|
||||
* Adapter for the selenium-webdriver.
|
||||
*/
|
||||
export class SeleniumWebDriverAdapter extends WebDriverAdapter {
|
||||
static PROTRACTOR_PROVIDERS = [{
|
||||
static PROTRACTOR_PROVIDERS = <StaticProvider[]>[{
|
||||
provide: WebDriverAdapter,
|
||||
useFactory: () => new SeleniumWebDriverAdapter((<any>global).browser)
|
||||
useFactory: () => new SeleniumWebDriverAdapter((<any>global).browser),
|
||||
deps: []
|
||||
}];
|
||||
|
||||
constructor(private _driver: any) { super(); }
|
||||
|
@ -7,12 +7,13 @@
|
||||
*/
|
||||
|
||||
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal';
|
||||
import {Metric, MultiMetric, ReflectiveInjector} from '../../index';
|
||||
|
||||
import {Injector, Metric, MultiMetric} from '../../index';
|
||||
|
||||
export function main() {
|
||||
function createMetric(ids: any[]) {
|
||||
const m = ReflectiveInjector
|
||||
.resolveAndCreate([
|
||||
const m = Injector
|
||||
.create([
|
||||
ids.map(id => ({provide: id, useValue: new MockMetric(id)})),
|
||||
MultiMetric.provideWith(ids)
|
||||
])
|
||||
|
@ -6,10 +6,10 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Provider} from '@angular/core';
|
||||
import {StaticProvider} from '@angular/core';
|
||||
import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal';
|
||||
|
||||
import {Metric, Options, PerfLogEvent, PerfLogFeatures, PerflogMetric, ReflectiveInjector, WebDriverExtension} from '../../index';
|
||||
import {Injector, Metric, Options, PerfLogEvent, PerfLogFeatures, PerflogMetric, WebDriverExtension} from '../../index';
|
||||
import {TraceEventFactory} from '../trace_event_factory';
|
||||
|
||||
export function main() {
|
||||
@ -33,7 +33,7 @@ export function main() {
|
||||
if (!microMetrics) {
|
||||
microMetrics = {};
|
||||
}
|
||||
const providers: Provider[] = [
|
||||
const providers: StaticProvider[] = [
|
||||
Options.DEFAULT_PROVIDERS, PerflogMetric.PROVIDERS,
|
||||
{provide: Options.MICRO_METRICS, useValue: microMetrics}, {
|
||||
provide: PerflogMetric.SET_TIMEOUT,
|
||||
@ -59,7 +59,7 @@ export function main() {
|
||||
if (requestCount != null) {
|
||||
providers.push({provide: Options.REQUEST_COUNT, useValue: requestCount});
|
||||
}
|
||||
return ReflectiveInjector.resolveAndCreate(providers).get(PerflogMetric);
|
||||
return Injector.create(providers).get(PerflogMetric);
|
||||
}
|
||||
|
||||
describe('perflog metric', () => {
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Provider, ReflectiveInjector} from '@angular/core';
|
||||
import {Injector, StaticProvider} from '@angular/core';
|
||||
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal';
|
||||
|
||||
import {Options, PerfLogEvent, PerfLogFeatures, UserMetric, WebDriverAdapter} from '../../index';
|
||||
@ -25,12 +25,12 @@ export function main() {
|
||||
userMetrics = {};
|
||||
}
|
||||
wdAdapter = new MockDriverAdapter();
|
||||
const providers: Provider[] = [
|
||||
const providers: StaticProvider[] = [
|
||||
Options.DEFAULT_PROVIDERS, UserMetric.PROVIDERS,
|
||||
{provide: Options.USER_METRICS, useValue: userMetrics},
|
||||
{provide: WebDriverAdapter, useValue: wdAdapter}
|
||||
];
|
||||
return ReflectiveInjector.resolveAndCreate(providers).get(UserMetric);
|
||||
return Injector.create(providers).get(UserMetric);
|
||||
}
|
||||
|
||||
describe('user metric', () => {
|
||||
|
@ -6,10 +6,10 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Provider} from '@angular/core';
|
||||
import {StaticProvider} from '@angular/core';
|
||||
import {describe, expect, it} from '@angular/core/testing/src/testing_internal';
|
||||
|
||||
import {ConsoleReporter, MeasureValues, ReflectiveInjector, SampleDescription} from '../../index';
|
||||
import {ConsoleReporter, Injector, MeasureValues, SampleDescription} from '../../index';
|
||||
|
||||
export function main() {
|
||||
describe('console reporter', () => {
|
||||
@ -30,7 +30,7 @@ export function main() {
|
||||
if (sampleId == null) {
|
||||
sampleId = 'null';
|
||||
}
|
||||
const providers: Provider[] = [
|
||||
const providers: StaticProvider[] = [
|
||||
ConsoleReporter.PROVIDERS, {
|
||||
provide: SampleDescription,
|
||||
useValue: new SampleDescription(sampleId, descriptions, metrics !)
|
||||
@ -40,7 +40,7 @@ export function main() {
|
||||
if (columnWidth != null) {
|
||||
providers.push({provide: ConsoleReporter.COLUMN_WIDTH, useValue: columnWidth});
|
||||
}
|
||||
reporter = ReflectiveInjector.resolveAndCreate(providers).get(ConsoleReporter);
|
||||
reporter = Injector.create(providers).get(ConsoleReporter);
|
||||
}
|
||||
|
||||
it('should print the sample id, description and table header', () => {
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal';
|
||||
|
||||
import {JsonFileReporter, MeasureValues, Options, ReflectiveInjector, SampleDescription} from '../../index';
|
||||
import {Injector, JsonFileReporter, MeasureValues, Options, SampleDescription} from '../../index';
|
||||
|
||||
export function main() {
|
||||
describe('file reporter', () => {
|
||||
@ -34,7 +34,7 @@ export function main() {
|
||||
}
|
||||
}
|
||||
];
|
||||
return ReflectiveInjector.resolveAndCreate(providers).get(JsonFileReporter);
|
||||
return Injector.create(providers).get(JsonFileReporter);
|
||||
}
|
||||
|
||||
it('should write all data into a file',
|
||||
|
@ -8,12 +8,12 @@
|
||||
|
||||
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal';
|
||||
|
||||
import {MeasureValues, MultiReporter, ReflectiveInjector, Reporter} from '../../index';
|
||||
import {Injector, MeasureValues, MultiReporter, Reporter} from '../../index';
|
||||
|
||||
export function main() {
|
||||
function createReporters(ids: any[]) {
|
||||
const r = ReflectiveInjector
|
||||
.resolveAndCreate([
|
||||
const r = Injector
|
||||
.create([
|
||||
ids.map(id => ({provide: id, useValue: new MockReporter(id)})),
|
||||
MultiReporter.provideWith(ids)
|
||||
])
|
||||
|
@ -8,11 +8,11 @@
|
||||
|
||||
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal';
|
||||
|
||||
import {Injector, Metric, Options, ReflectiveInjector, Runner, SampleDescription, SampleState, Sampler, Validator, WebDriverAdapter} from '../index';
|
||||
import {Injector, Metric, Options, Runner, SampleDescription, SampleState, Sampler, Validator, WebDriverAdapter} from '../index';
|
||||
|
||||
export function main() {
|
||||
describe('runner', () => {
|
||||
let injector: ReflectiveInjector;
|
||||
let injector: Injector;
|
||||
let runner: Runner;
|
||||
|
||||
function createRunner(defaultProviders?: any[]): Runner {
|
||||
@ -22,7 +22,7 @@ export function main() {
|
||||
runner = new Runner([
|
||||
defaultProviders, {
|
||||
provide: Sampler,
|
||||
useFactory: (_injector: ReflectiveInjector) => {
|
||||
useFactory: (_injector: Injector) => {
|
||||
injector = _injector;
|
||||
return new MockSampler();
|
||||
},
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal';
|
||||
|
||||
import {MeasureValues, Metric, Options, ReflectiveInjector, Reporter, Sampler, Validator, WebDriverAdapter} from '../index';
|
||||
import {Injector, MeasureValues, Metric, Options, Reporter, Sampler, Validator, WebDriverAdapter} from '../index';
|
||||
|
||||
export function main() {
|
||||
const EMPTY_EXECUTE = () => {};
|
||||
@ -44,7 +44,7 @@ export function main() {
|
||||
providers.push({provide: Options.PREPARE, useValue: prepare});
|
||||
}
|
||||
|
||||
sampler = ReflectiveInjector.resolveAndCreate(providers).get(Sampler);
|
||||
sampler = Injector.create(providers).get(Sampler);
|
||||
}
|
||||
|
||||
it('should call the prepare and execute callbacks using WebDriverAdapter.waitFor',
|
||||
|
@ -8,15 +8,15 @@
|
||||
|
||||
import {describe, expect, it} from '@angular/core/testing/src/testing_internal';
|
||||
|
||||
import {MeasureValues, ReflectiveInjector, RegressionSlopeValidator} from '../../index';
|
||||
import {Injector, MeasureValues, RegressionSlopeValidator} from '../../index';
|
||||
|
||||
export function main() {
|
||||
describe('regression slope validator', () => {
|
||||
let validator: RegressionSlopeValidator;
|
||||
|
||||
function createValidator({size, metric}: {size: number, metric: string}) {
|
||||
validator = ReflectiveInjector
|
||||
.resolveAndCreate([
|
||||
validator = Injector
|
||||
.create([
|
||||
RegressionSlopeValidator.PROVIDERS,
|
||||
{provide: RegressionSlopeValidator.METRIC, useValue: metric},
|
||||
{provide: RegressionSlopeValidator.SAMPLE_SIZE, useValue: size}
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {describe, expect, it} from '@angular/core/testing/src/testing_internal';
|
||||
|
||||
import {MeasureValues, ReflectiveInjector, SizeValidator} from '../../index';
|
||||
import {Injector, MeasureValues, SizeValidator} from '../../index';
|
||||
|
||||
export function main() {
|
||||
describe('size validator', () => {
|
||||
@ -16,8 +16,8 @@ export function main() {
|
||||
|
||||
function createValidator(size: number) {
|
||||
validator =
|
||||
ReflectiveInjector
|
||||
.resolveAndCreate(
|
||||
Injector
|
||||
.create(
|
||||
[SizeValidator.PROVIDERS, {provide: SizeValidator.SAMPLE_SIZE, useValue: size}])
|
||||
.get(SizeValidator);
|
||||
}
|
||||
|
@ -8,14 +8,14 @@
|
||||
|
||||
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal';
|
||||
|
||||
import {Options, ReflectiveInjector, WebDriverExtension} from '../index';
|
||||
import {Injector, Options, WebDriverExtension} from '../index';
|
||||
|
||||
export function main() {
|
||||
function createExtension(ids: any[], caps: any) {
|
||||
return new Promise<any>((res, rej) => {
|
||||
try {
|
||||
res(ReflectiveInjector
|
||||
.resolveAndCreate([
|
||||
res(Injector
|
||||
.create([
|
||||
ids.map((id) => ({provide: id, useValue: new MockExtension(id)})),
|
||||
{provide: Options.CAPABILITIES, useValue: caps},
|
||||
WebDriverExtension.provideFirstSupported(ids)
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {AsyncTestCompleter, describe, expect, iit, inject, it} from '@angular/core/testing/src/testing_internal';
|
||||
|
||||
import {ChromeDriverExtension, Options, ReflectiveInjector, WebDriverAdapter, WebDriverExtension} from '../../index';
|
||||
import {ChromeDriverExtension, Injector, Options, WebDriverAdapter, WebDriverExtension} from '../../index';
|
||||
import {TraceEventFactory} from '../trace_event_factory';
|
||||
|
||||
export function main() {
|
||||
@ -41,8 +41,8 @@ export function main() {
|
||||
userAgent = CHROME45_USER_AGENT;
|
||||
}
|
||||
log = [];
|
||||
extension = ReflectiveInjector
|
||||
.resolveAndCreate([
|
||||
extension = Injector
|
||||
.create([
|
||||
ChromeDriverExtension.PROVIDERS, {
|
||||
provide: WebDriverAdapter,
|
||||
useValue: new MockDriverAdapter(log, perfRecords, messageMethod)
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal';
|
||||
|
||||
import {IOsDriverExtension, ReflectiveInjector, WebDriverAdapter, WebDriverExtension} from '../../index';
|
||||
import {IOsDriverExtension, Injector, WebDriverAdapter, WebDriverExtension} from '../../index';
|
||||
import {TraceEventFactory} from '../trace_event_factory';
|
||||
|
||||
export function main() {
|
||||
@ -24,8 +24,8 @@ export function main() {
|
||||
}
|
||||
log = [];
|
||||
extension =
|
||||
ReflectiveInjector
|
||||
.resolveAndCreate([
|
||||
Injector
|
||||
.create([
|
||||
IOsDriverExtension.PROVIDERS,
|
||||
{provide: WebDriverAdapter, useValue: new MockDriverAdapter(log, perfRecords)}
|
||||
])
|
||||
|
@ -6,7 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ComponentFactoryResolver, ComponentRef, Directive, Injector, Input, NgModuleFactory, NgModuleRef, OnChanges, OnDestroy, Provider, SimpleChanges, Type, ViewContainerRef} from '@angular/core';
|
||||
import {ComponentFactoryResolver, ComponentRef, Directive, Injector, Input, NgModuleFactory, NgModuleRef, OnChanges, OnDestroy, SimpleChanges, StaticProvider, Type, ViewContainerRef} from '@angular/core';
|
||||
|
||||
|
||||
/**
|
||||
* Instantiates a single {@link Component} type and inserts its Host View into current View.
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, EmbeddedViewRef, Input, OnChanges, SimpleChanges, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
import {Directive, EmbeddedViewRef, Input, OnChanges, SimpleChange, SimpleChanges, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
/**
|
||||
* @ngModule CommonModule
|
||||
@ -49,13 +49,58 @@ export class NgTemplateOutlet implements OnChanges {
|
||||
set ngOutletContext(context: Object) { this.ngTemplateOutletContext = context; }
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (this._viewRef) {
|
||||
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._viewRef));
|
||||
}
|
||||
const recreateView = this._shouldRecreateView(changes);
|
||||
|
||||
if (this.ngTemplateOutlet) {
|
||||
this._viewRef = this._viewContainerRef.createEmbeddedView(
|
||||
this.ngTemplateOutlet, this.ngTemplateOutletContext);
|
||||
if (recreateView) {
|
||||
if (this._viewRef) {
|
||||
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._viewRef));
|
||||
}
|
||||
|
||||
if (this.ngTemplateOutlet) {
|
||||
this._viewRef = this._viewContainerRef.createEmbeddedView(
|
||||
this.ngTemplateOutlet, this.ngTemplateOutletContext);
|
||||
}
|
||||
} else {
|
||||
if (this._viewRef && this.ngTemplateOutletContext) {
|
||||
this._updateExistingContext(this.ngTemplateOutletContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We need to re-create existing embedded view if:
|
||||
* - templateRef has changed
|
||||
* - context has changes
|
||||
*
|
||||
* To mark context object as changed when the corresponding object
|
||||
* shape changes (new properties are added or existing properties are removed).
|
||||
* In other words we consider context with the same properties as "the same" even
|
||||
* if object reference changes (see https://github.com/angular/angular/issues/13407).
|
||||
*/
|
||||
private _shouldRecreateView(changes: SimpleChanges): boolean {
|
||||
const ctxChange = changes['ngTemplateOutletContext'];
|
||||
return !!changes['ngTemplateOutlet'] || (ctxChange && this._hasContextShapeChanged(ctxChange));
|
||||
}
|
||||
|
||||
private _hasContextShapeChanged(ctxChange: SimpleChange): boolean {
|
||||
const prevCtxKeys = Object.keys(ctxChange.previousValue || {});
|
||||
const currCtxKeys = Object.keys(ctxChange.currentValue || {});
|
||||
|
||||
if (prevCtxKeys.length === currCtxKeys.length) {
|
||||
for (let propName of currCtxKeys) {
|
||||
if (prevCtxKeys.indexOf(propName) === -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private _updateExistingContext(ctx: Object): void {
|
||||
for (let propName of Object.keys(ctx)) {
|
||||
(<any>this._viewRef.context)[propName] = (<any>this.ngTemplateOutletContext)[propName];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {NgComponentOutlet} from '@angular/common/src/directives/ng_component_outlet';
|
||||
import {Compiler, Component, ComponentRef, Inject, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, NgModuleFactory, Optional, Provider, QueryList, ReflectiveInjector, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core';
|
||||
import {Compiler, Component, ComponentRef, Inject, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, NgModuleFactory, Optional, QueryList, StaticProvider, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core';
|
||||
import {TestBed, async, fakeAsync} from '@angular/core/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
|
||||
@ -96,7 +96,7 @@ export function main() {
|
||||
|
||||
const uniqueValue = {};
|
||||
fixture.componentInstance.currentComponent = InjectedComponent;
|
||||
fixture.componentInstance.injector = ReflectiveInjector.resolveAndCreate(
|
||||
fixture.componentInstance.injector = Injector.create(
|
||||
[{provide: TEST_TOKEN, useValue: uniqueValue}], fixture.componentRef.injector);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {Component, ContentChildren, Directive, NO_ERRORS_SCHEMA, QueryList, TemplateRef} from '@angular/core';
|
||||
import {Component, ContentChildren, Directive, Injectable, NO_ERRORS_SCHEMA, OnDestroy, QueryList, TemplateRef} from '@angular/core';
|
||||
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
|
||||
@ -26,11 +26,9 @@ export function main() {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
TestComponent,
|
||||
CaptureTplRefs,
|
||||
],
|
||||
declarations: [TestComponent, CaptureTplRefs, DestroyableCmpt],
|
||||
imports: [CommonModule],
|
||||
providers: [DestroyedSpyService]
|
||||
});
|
||||
});
|
||||
|
||||
@ -125,9 +123,105 @@ export function main() {
|
||||
fixture.componentInstance.context = {shawshank: 'was here'};
|
||||
detectChangesAndExpectText('was here');
|
||||
}));
|
||||
|
||||
it('should update but not destroy embedded view when context values change', () => {
|
||||
const template =
|
||||
`<ng-template let-foo="foo" #tpl><destroyable-cmpt></destroyable-cmpt>:{{foo}}</ng-template>` +
|
||||
`<ng-template [ngTemplateOutlet]="tpl" [ngTemplateOutletContext]="{foo: value}"></ng-template>`;
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
const spyService = fixture.debugElement.injector.get(DestroyedSpyService);
|
||||
|
||||
detectChangesAndExpectText('Content to destroy:bar');
|
||||
expect(spyService.destroyed).toBeFalsy();
|
||||
|
||||
fixture.componentInstance.value = 'baz';
|
||||
detectChangesAndExpectText('Content to destroy:baz');
|
||||
expect(spyService.destroyed).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should recreate embedded view when context shape changes', () => {
|
||||
const template =
|
||||
`<ng-template let-foo="foo" #tpl><destroyable-cmpt></destroyable-cmpt>:{{foo}}</ng-template>` +
|
||||
`<ng-template [ngTemplateOutlet]="tpl" [ngTemplateOutletContext]="context"></ng-template>`;
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
const spyService = fixture.debugElement.injector.get(DestroyedSpyService);
|
||||
|
||||
detectChangesAndExpectText('Content to destroy:bar');
|
||||
expect(spyService.destroyed).toBeFalsy();
|
||||
|
||||
fixture.componentInstance.context = {foo: 'baz', other: true};
|
||||
detectChangesAndExpectText('Content to destroy:baz');
|
||||
expect(spyService.destroyed).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should destroy embedded view when context value changes and templateRef becomes undefined',
|
||||
() => {
|
||||
const template =
|
||||
`<ng-template let-foo="foo" #tpl><destroyable-cmpt></destroyable-cmpt>:{{foo}}</ng-template>` +
|
||||
`<ng-template [ngTemplateOutlet]="value === 'bar' ? tpl : undefined" [ngTemplateOutletContext]="{foo: value}"></ng-template>`;
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
const spyService = fixture.debugElement.injector.get(DestroyedSpyService);
|
||||
|
||||
detectChangesAndExpectText('Content to destroy:bar');
|
||||
expect(spyService.destroyed).toBeFalsy();
|
||||
|
||||
fixture.componentInstance.value = 'baz';
|
||||
detectChangesAndExpectText('');
|
||||
expect(spyService.destroyed).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not try to update null / undefined context when context changes but template stays the same',
|
||||
() => {
|
||||
const template = `<ng-template let-foo="foo" #tpl>{{foo}}</ng-template>` +
|
||||
`<ng-template [ngTemplateOutlet]="tpl" [ngTemplateOutletContext]="value === 'bar' ? null : undefined"></ng-template>`;
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('');
|
||||
|
||||
fixture.componentInstance.value = 'baz';
|
||||
detectChangesAndExpectText('');
|
||||
});
|
||||
|
||||
it('should not try to update null / undefined context when template changes', () => {
|
||||
const template = `<ng-template let-foo="foo" #tpl1>{{foo}}</ng-template>` +
|
||||
`<ng-template let-foo="foo" #tpl2>{{foo}}</ng-template>` +
|
||||
`<ng-template [ngTemplateOutlet]="value === 'bar' ? tpl1 : tpl2" [ngTemplateOutletContext]="value === 'bar' ? null : undefined"></ng-template>`;
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('');
|
||||
|
||||
fixture.componentInstance.value = 'baz';
|
||||
detectChangesAndExpectText('');
|
||||
});
|
||||
|
||||
it('should not try to update context on undefined view', () => {
|
||||
const template = `<ng-template let-foo="foo" #tpl>{{foo}}</ng-template>` +
|
||||
`<ng-template [ngTemplateOutlet]="value === 'bar' ? null : undefined" [ngTemplateOutletContext]="{foo: value}"></ng-template>`;
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('');
|
||||
|
||||
fixture.componentInstance.value = 'baz';
|
||||
detectChangesAndExpectText('');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class DestroyedSpyService {
|
||||
destroyed = false;
|
||||
}
|
||||
|
||||
@Component({selector: 'destroyable-cmpt', template: 'Content to destroy'})
|
||||
class DestroyableCmpt implements OnDestroy {
|
||||
constructor(private _spyService: DestroyedSpyService) {}
|
||||
|
||||
ngOnDestroy(): void { this._spyService.destroyed = true; }
|
||||
}
|
||||
|
||||
@Directive({selector: 'tpl-refs', exportAs: 'tplRefs'})
|
||||
class CaptureTplRefs {
|
||||
@ContentChildren(TemplateRef) tplRefs: QueryList<TemplateRef<any>>;
|
||||
@ -137,6 +231,7 @@ class CaptureTplRefs {
|
||||
class TestComponent {
|
||||
currentTplRef: TemplateRef<any>;
|
||||
context: any = {foo: 'bar'};
|
||||
value = 'bar';
|
||||
}
|
||||
|
||||
function createTestComponent(template: string): ComponentFixture<TestComponent> {
|
||||
|
17
packages/compiler-cli/BUILD.bazel
Normal file
17
packages/compiler-cli/BUILD.bazel
Normal file
@ -0,0 +1,17 @@
|
||||
package(default_visibility=["//visibility:public"])
|
||||
load("@build_bazel_rules_typescript//:defs.bzl", "ts_library")
|
||||
|
||||
ts_library(
|
||||
name = "compiler-cli",
|
||||
srcs = glob(["**/*.ts"], exclude=[
|
||||
"integrationtest/**",
|
||||
"test/**",
|
||||
]),
|
||||
module_name = "@angular/compiler-cli",
|
||||
deps = [
|
||||
"//packages/compiler",
|
||||
"//packages/core",
|
||||
"//packages/tsc-wrapped",
|
||||
],
|
||||
tsconfig = ":tsconfig-build.json",
|
||||
)
|
@ -121,9 +121,6 @@ At a high level, this program
|
||||
# Build Angular and the compiler
|
||||
./build.sh
|
||||
|
||||
# Copy over the package so we can test the compiler tests
|
||||
$ cp tools/@angular/tsc-wrapped/package.json dist/tools/@angular/tsc-wrapped
|
||||
|
||||
# Run the test once
|
||||
# (First edit the LINKABLE_PKGS to use npm link instead of npm install)
|
||||
$ ./scripts/ci/offline_compiler_test.sh
|
||||
|
@ -20,7 +20,7 @@ export {BuiltinType, DeclarationKind, Definition, PipeInfo, Pipes, Signature, Sp
|
||||
export * from './src/transformers/api';
|
||||
export * from './src/transformers/entry_points';
|
||||
|
||||
export {main as ngc} from './src/ngc';
|
||||
export {performCompilation} from './src/perform-compile';
|
||||
|
||||
// TODO(hansl): moving to Angular 4 need to update this API.
|
||||
export {NgTools_InternalApi_NG_2 as __NGTOOLS_PRIVATE_API_2} from './src/ngtools_api';
|
||||
|
@ -9,7 +9,7 @@
|
||||
"ng-xi18n": "./src/extract_i18n.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/tsc-wrapped": "4.3.4",
|
||||
"@angular/tsc-wrapped": "5.0.0-beta.3",
|
||||
"reflect-metadata": "^0.1.2",
|
||||
"minimist": "^1.2.0"
|
||||
},
|
||||
|
@ -100,8 +100,8 @@ export class CodeGenerator {
|
||||
}
|
||||
const {compiler: aotCompiler} = compiler.createAotCompiler(ngCompilerHost, {
|
||||
translations: transContent,
|
||||
i18nFormat: cliOptions.i18nFormat,
|
||||
locale: cliOptions.locale, missingTranslation,
|
||||
i18nFormat: cliOptions.i18nFormat || undefined,
|
||||
locale: cliOptions.locale || undefined, missingTranslation,
|
||||
enableLegacyTemplate: options.enableLegacyTemplate !== false,
|
||||
enableSummariesForJit: options.enableSummariesForJit !== false,
|
||||
});
|
||||
|
@ -25,8 +25,9 @@ export interface CompilerHostContext extends ts.ModuleResolutionHost {
|
||||
assumeFileExists(fileName: string): void;
|
||||
}
|
||||
|
||||
export interface MetadataProvider { getMetadata(source: ts.SourceFile): ModuleMetadata|undefined; }
|
||||
|
||||
export class CompilerHost implements AotCompilerHost {
|
||||
protected metadataCollector = new MetadataCollector();
|
||||
private isGenDirChildOfRootDir: boolean;
|
||||
protected basePath: string;
|
||||
private genDir: string;
|
||||
@ -39,7 +40,8 @@ export class CompilerHost implements AotCompilerHost {
|
||||
|
||||
constructor(
|
||||
protected program: ts.Program, protected options: AngularCompilerOptions,
|
||||
protected context: CompilerHostContext, collectorOptions?: CollectorOptions) {
|
||||
protected context: CompilerHostContext, collectorOptions?: CollectorOptions,
|
||||
protected metadataProvider: MetadataProvider = new MetadataCollector()) {
|
||||
// normalize the path so that it never ends with '/'.
|
||||
this.basePath = path.normalize(path.join(this.options.basePath !, '.')).replace(/\\/g, '/');
|
||||
this.genDir = path.normalize(path.join(this.options.genDir !, '.')).replace(/\\/g, '/');
|
||||
@ -206,7 +208,7 @@ export class CompilerHost implements AotCompilerHost {
|
||||
}
|
||||
|
||||
const sf = this.getSourceFile(filePath);
|
||||
const metadata = this.metadataCollector.getMetadata(sf);
|
||||
const metadata = this.metadataProvider.getMetadata(sf);
|
||||
return metadata ? [metadata] : [];
|
||||
}
|
||||
|
||||
@ -245,7 +247,7 @@ export class CompilerHost implements AotCompilerHost {
|
||||
v3Metadata.metadata[prop] = v1Metadata.metadata[prop];
|
||||
}
|
||||
|
||||
const exports = this.metadataCollector.getMetadata(this.getSourceFile(dtsFilePath));
|
||||
const exports = this.metadataProvider.getMetadata(this.getSourceFile(dtsFilePath));
|
||||
if (exports) {
|
||||
for (let prop in exports.metadata) {
|
||||
if (!v3Metadata.metadata[prop]) {
|
||||
|
@ -10,179 +10,14 @@
|
||||
import 'reflect-metadata';
|
||||
|
||||
import {isSyntaxError, syntaxError} from '@angular/compiler';
|
||||
import {MetadataBundler, createBundleIndexHost} from '@angular/tsc-wrapped';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
import * as api from './transformers/api';
|
||||
import * as ng from './transformers/entry_points';
|
||||
|
||||
const TS_EXT = /\.ts$/;
|
||||
import {performCompilation, readConfiguration, throwOnDiagnostics} from './perform-compile';
|
||||
|
||||
type Diagnostics = ts.Diagnostic[] | api.Diagnostic[];
|
||||
|
||||
function isTsDiagnostics(diagnostics: any): diagnostics is ts.Diagnostic[] {
|
||||
return diagnostics && diagnostics[0] && (diagnostics[0].file || diagnostics[0].messageText);
|
||||
}
|
||||
|
||||
function formatDiagnostics(cwd: string, diags: Diagnostics): string {
|
||||
if (diags && diags.length) {
|
||||
if (isTsDiagnostics(diags)) {
|
||||
return ts.formatDiagnostics(diags, {
|
||||
getCurrentDirectory: () => cwd,
|
||||
getCanonicalFileName: fileName => fileName,
|
||||
getNewLine: () => ts.sys.newLine
|
||||
});
|
||||
} else {
|
||||
return diags
|
||||
.map(d => {
|
||||
let res = api.DiagnosticCategory[d.category];
|
||||
if (d.span) {
|
||||
res +=
|
||||
` at ${d.span.start.file.url}(${d.span.start.line + 1},${d.span.start.col + 1})`;
|
||||
}
|
||||
if (d.span && d.span.details) {
|
||||
res += `: ${d.span.details}, ${d.message}\n`;
|
||||
} else {
|
||||
res += `: ${d.message}\n`;
|
||||
}
|
||||
return res;
|
||||
})
|
||||
.join();
|
||||
}
|
||||
} else
|
||||
return '';
|
||||
}
|
||||
|
||||
function check(cwd: string, ...args: Diagnostics[]) {
|
||||
if (args.some(diags => !!(diags && diags[0]))) {
|
||||
throw syntaxError(args.map(diags => {
|
||||
if (diags && diags[0]) {
|
||||
return formatDiagnostics(cwd, diags);
|
||||
}
|
||||
})
|
||||
.filter(message => !!message)
|
||||
.join(''));
|
||||
}
|
||||
}
|
||||
|
||||
function syntheticError(message: string): ts.Diagnostic {
|
||||
return {
|
||||
file: null as any as ts.SourceFile,
|
||||
start: 0,
|
||||
length: 0,
|
||||
messageText: message,
|
||||
category: ts.DiagnosticCategory.Error,
|
||||
code: 0
|
||||
};
|
||||
}
|
||||
|
||||
export function readConfiguration(
|
||||
project: string, basePath: string, existingOptions?: ts.CompilerOptions) {
|
||||
// Allow a directory containing tsconfig.json as the project value
|
||||
// Note, TS@next returns an empty array, while earlier versions throw
|
||||
const projectFile =
|
||||
fs.lstatSync(project).isDirectory() ? path.join(project, 'tsconfig.json') : project;
|
||||
let {config, error} = ts.readConfigFile(projectFile, ts.sys.readFile);
|
||||
|
||||
if (error) check(basePath, [error]);
|
||||
const parseConfigHost = {
|
||||
useCaseSensitiveFileNames: true,
|
||||
fileExists: fs.existsSync,
|
||||
readDirectory: ts.sys.readDirectory,
|
||||
readFile: ts.sys.readFile
|
||||
};
|
||||
const parsed = ts.parseJsonConfigFileContent(config, parseConfigHost, basePath, existingOptions);
|
||||
|
||||
check(basePath, parsed.errors);
|
||||
|
||||
// Default codegen goes to the current directory
|
||||
// Parsed options are already converted to absolute paths
|
||||
const ngOptions = config.angularCompilerOptions || {};
|
||||
// Ignore the genDir option
|
||||
ngOptions.genDir = basePath;
|
||||
|
||||
return {parsed, ngOptions};
|
||||
}
|
||||
|
||||
function getProjectDirectory(project: string): string {
|
||||
let isFile: boolean;
|
||||
try {
|
||||
isFile = fs.lstatSync(project).isFile();
|
||||
} catch (e) {
|
||||
// Project doesn't exist. Assume it is a file has an extension. This case happens
|
||||
// when the project file is passed to set basePath but no tsconfig.json file exists.
|
||||
// It is used in tests to ensure that the options can be passed in without there being
|
||||
// an actual config file.
|
||||
isFile = path.extname(project) !== '';
|
||||
}
|
||||
|
||||
// If project refers to a file, the project directory is the file's parent directory
|
||||
// otherwise project is the project directory.
|
||||
return isFile ? path.dirname(project) : project;
|
||||
}
|
||||
|
||||
export function performCompilation(
|
||||
basePath: string, files: string[], options: ts.CompilerOptions, ngOptions: any,
|
||||
consoleError: (s: string) => void = console.error, tsCompilerHost?: ts.CompilerHost) {
|
||||
try {
|
||||
ngOptions.basePath = basePath;
|
||||
ngOptions.genDir = basePath;
|
||||
|
||||
let host = tsCompilerHost || ts.createCompilerHost(options, true);
|
||||
host.realpath = p => p;
|
||||
|
||||
const rootFileNames = files.map(f => path.normalize(f));
|
||||
|
||||
const addGeneratedFileName = (fileName: string) => {
|
||||
if (fileName.startsWith(basePath) && TS_EXT.exec(fileName)) {
|
||||
rootFileNames.push(fileName);
|
||||
}
|
||||
};
|
||||
|
||||
if (ngOptions.flatModuleOutFile && !ngOptions.skipMetadataEmit) {
|
||||
const {host: bundleHost, indexName, errors} =
|
||||
createBundleIndexHost(ngOptions, rootFileNames, host);
|
||||
if (errors) check(basePath, errors);
|
||||
if (indexName) addGeneratedFileName(indexName);
|
||||
host = bundleHost;
|
||||
}
|
||||
|
||||
const ngHostOptions = {...options, ...ngOptions};
|
||||
const ngHost = ng.createHost({tsHost: host, options: ngHostOptions});
|
||||
|
||||
const ngProgram =
|
||||
ng.createProgram({rootNames: rootFileNames, host: ngHost, options: ngHostOptions});
|
||||
|
||||
// Check parameter diagnostics
|
||||
check(basePath, ngProgram.getTsOptionDiagnostics(), ngProgram.getNgOptionDiagnostics());
|
||||
|
||||
// Check syntactic diagnostics
|
||||
check(basePath, ngProgram.getTsSyntacticDiagnostics());
|
||||
|
||||
// Check TypeScript semantic and Angular structure diagnostics
|
||||
check(basePath, ngProgram.getTsSemanticDiagnostics(), ngProgram.getNgStructuralDiagnostics());
|
||||
|
||||
// Check Angular semantic diagnostics
|
||||
check(basePath, ngProgram.getNgSemanticDiagnostics());
|
||||
|
||||
ngProgram.emit({
|
||||
emitFlags: api.EmitFlags.Default |
|
||||
((ngOptions.skipMetadataEmit || ngOptions.flatModuleOutFile) ? 0 : api.EmitFlags.Metadata)
|
||||
});
|
||||
} catch (e) {
|
||||
if (isSyntaxError(e)) {
|
||||
console.error(e.message);
|
||||
consoleError(e.message);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
export function main(args: string[], consoleError: (s: string) => void = console.error): number {
|
||||
export function main(
|
||||
args: string[], consoleError: (s: string) => void = console.error,
|
||||
checkFunc: (cwd: string, ...args: any[]) => void = throwOnDiagnostics): number {
|
||||
try {
|
||||
const parsedArgs = require('minimist')(args);
|
||||
const project = parsedArgs.p || parsedArgs.project || '.';
|
||||
@ -191,8 +26,9 @@ export function main(args: string[], consoleError: (s: string) => void = console
|
||||
|
||||
// file names in tsconfig are resolved relative to this absolute path
|
||||
const basePath = path.resolve(process.cwd(), projectDir);
|
||||
const {parsed, ngOptions} = readConfiguration(project, basePath);
|
||||
return performCompilation(basePath, parsed.fileNames, parsed.options, ngOptions, consoleError);
|
||||
const {parsed, ngOptions} = readConfiguration(project, basePath, checkFunc);
|
||||
return performCompilation(
|
||||
basePath, parsed.fileNames, parsed.options, ngOptions, consoleError, checkFunc);
|
||||
} catch (e) {
|
||||
consoleError(e.stack);
|
||||
consoleError('Compilation failed');
|
||||
|
@ -81,12 +81,10 @@ class CustomLoaderModuleResolutionHostAdapter extends ModuleResolutionHostAdapte
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @private
|
||||
*/
|
||||
export class NgTools_InternalApi_NG_2 {
|
||||
/**
|
||||
* @internal
|
||||
* @private
|
||||
*/
|
||||
static codeGen(options: NgTools_InternalApi_NG2_CodeGen_Options): Promise<any> {
|
||||
const hostContext: CompilerHostContext =
|
||||
@ -114,7 +112,6 @@ export class NgTools_InternalApi_NG_2 {
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @private
|
||||
*/
|
||||
static listLazyRoutes(options: NgTools_InternalApi_NG2_ListLazyRoutes_Options):
|
||||
NgTools_InternalApi_NG_2_LazyRouteMap {
|
||||
@ -144,7 +141,6 @@ export class NgTools_InternalApi_NG_2 {
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @private
|
||||
*/
|
||||
static extractI18n(options: NgTools_InternalApi_NG2_ExtractI18n_Options): Promise<any> {
|
||||
const hostContext: CompilerHostContext =
|
||||
|
@ -49,11 +49,6 @@ export class RouteDef {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {LazyRouteMap}
|
||||
* @private
|
||||
*/
|
||||
export function listLazyRoutesOfModule(
|
||||
entryModule: string, host: AotCompilerHost, reflector: StaticReflector): LazyRouteMap {
|
||||
const entryRouteDef = RouteDef.fromString(entryModule);
|
||||
@ -111,7 +106,6 @@ function _resolveModule(modulePath: string, containingFile: string, host: AotCom
|
||||
|
||||
/**
|
||||
* Throw an exception if a route is in a route map, but does not point to the same module.
|
||||
* @private
|
||||
*/
|
||||
function _assertRoute(map: LazyRouteMap, route: LazyRoute) {
|
||||
const r = route.routeDef.toString();
|
||||
@ -173,7 +167,6 @@ function _extractLazyRoutesFromStaticModule(
|
||||
|
||||
/**
|
||||
* Get the NgModule Metadata of a symbol.
|
||||
* @private
|
||||
*/
|
||||
function _getNgModuleMetadata(staticSymbol: StaticSymbol, reflector: StaticReflector): NgModule {
|
||||
const ngModules = reflector.annotations(staticSymbol).filter((s: any) => s instanceof NgModule);
|
||||
@ -204,7 +197,6 @@ function _collectRoutes(
|
||||
|
||||
/**
|
||||
* Return the loadChildren values of a list of Route.
|
||||
* @private
|
||||
*/
|
||||
function _collectLoadChildren(routes: Route[]): string[] {
|
||||
return routes.reduce((m, r) => {
|
||||
|
@ -132,7 +132,7 @@ export class PathMappedCompilerHost extends CompilerHost {
|
||||
} else {
|
||||
const sf = this.getSourceFile(rootedPath);
|
||||
sf.fileName = sf.fileName;
|
||||
const metadata = this.metadataCollector.getMetadata(sf);
|
||||
const metadata = this.metadataProvider.getMetadata(sf);
|
||||
return metadata ? [metadata] : [];
|
||||
}
|
||||
}
|
||||
|
192
packages/compiler-cli/src/perform-compile.ts
Normal file
192
packages/compiler-cli/src/perform-compile.ts
Normal file
@ -0,0 +1,192 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
import {isSyntaxError, syntaxError} from '@angular/compiler';
|
||||
import {MetadataBundler, createBundleIndexHost} from '@angular/tsc-wrapped';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
import * as api from './transformers/api';
|
||||
import * as ng from './transformers/entry_points';
|
||||
|
||||
const TS_EXT = /\.ts$/;
|
||||
|
||||
export type Diagnostics = ts.Diagnostic[] | api.Diagnostic[];
|
||||
|
||||
function isTsDiagnostics(diagnostics: any): diagnostics is ts.Diagnostic[] {
|
||||
return diagnostics && diagnostics[0] && (diagnostics[0].file || diagnostics[0].messageText);
|
||||
}
|
||||
|
||||
function formatDiagnostics(cwd: string, diags: Diagnostics): string {
|
||||
if (diags && diags.length) {
|
||||
if (isTsDiagnostics(diags)) {
|
||||
return ts.formatDiagnostics(diags, {
|
||||
getCurrentDirectory: () => cwd,
|
||||
getCanonicalFileName: fileName => fileName,
|
||||
getNewLine: () => ts.sys.newLine
|
||||
});
|
||||
} else {
|
||||
return diags
|
||||
.map(d => {
|
||||
let res = api.DiagnosticCategory[d.category];
|
||||
if (d.span) {
|
||||
res +=
|
||||
` at ${d.span.start.file.url}(${d.span.start.line + 1},${d.span.start.col + 1})`;
|
||||
}
|
||||
if (d.span && d.span.details) {
|
||||
res += `: ${d.span.details}, ${d.message}\n`;
|
||||
} else {
|
||||
res += `: ${d.message}\n`;
|
||||
}
|
||||
return res;
|
||||
})
|
||||
.join();
|
||||
}
|
||||
} else
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw a syntax error exception with a message formatted for output
|
||||
* if the args parameter contains diagnostics errors.
|
||||
*
|
||||
* @param cwd The directory to report error as relative to.
|
||||
* @param args A list of potentially empty diagnostic errors.
|
||||
*/
|
||||
export function throwOnDiagnostics(cwd: string, ...args: Diagnostics[]) {
|
||||
if (args.some(diags => !!(diags && diags[0]))) {
|
||||
throw syntaxError(args.map(diags => {
|
||||
if (diags && diags[0]) {
|
||||
return formatDiagnostics(cwd, diags);
|
||||
}
|
||||
})
|
||||
.filter(message => !!message)
|
||||
.join(''));
|
||||
}
|
||||
}
|
||||
|
||||
function syntheticError(message: string): ts.Diagnostic {
|
||||
return {
|
||||
file: null as any as ts.SourceFile,
|
||||
start: 0,
|
||||
length: 0,
|
||||
messageText: message,
|
||||
category: ts.DiagnosticCategory.Error,
|
||||
code: 0
|
||||
};
|
||||
}
|
||||
|
||||
export function readConfiguration(
|
||||
project: string, basePath: string,
|
||||
checkFunc: (cwd: string, ...args: any[]) => void = throwOnDiagnostics,
|
||||
existingOptions?: ts.CompilerOptions) {
|
||||
// Allow a directory containing tsconfig.json as the project value
|
||||
// Note, TS@next returns an empty array, while earlier versions throw
|
||||
const projectFile =
|
||||
fs.lstatSync(project).isDirectory() ? path.join(project, 'tsconfig.json') : project;
|
||||
let {config, error} = ts.readConfigFile(projectFile, ts.sys.readFile);
|
||||
|
||||
if (error) checkFunc(basePath, [error]);
|
||||
const parseConfigHost = {
|
||||
useCaseSensitiveFileNames: true,
|
||||
fileExists: fs.existsSync,
|
||||
readDirectory: ts.sys.readDirectory,
|
||||
readFile: ts.sys.readFile
|
||||
};
|
||||
const parsed = ts.parseJsonConfigFileContent(config, parseConfigHost, basePath, existingOptions);
|
||||
|
||||
checkFunc(basePath, parsed.errors);
|
||||
|
||||
// Default codegen goes to the current directory
|
||||
// Parsed options are already converted to absolute paths
|
||||
const ngOptions = config.angularCompilerOptions || {};
|
||||
// Ignore the genDir option
|
||||
ngOptions.genDir = basePath;
|
||||
|
||||
return {parsed, ngOptions};
|
||||
}
|
||||
|
||||
function getProjectDirectory(project: string): string {
|
||||
let isFile: boolean;
|
||||
try {
|
||||
isFile = fs.lstatSync(project).isFile();
|
||||
} catch (e) {
|
||||
// Project doesn't exist. Assume it is a file has an extension. This case happens
|
||||
// when the project file is passed to set basePath but no tsconfig.json file exists.
|
||||
// It is used in tests to ensure that the options can be passed in without there being
|
||||
// an actual config file.
|
||||
isFile = path.extname(project) !== '';
|
||||
}
|
||||
|
||||
// If project refers to a file, the project directory is the file's parent directory
|
||||
// otherwise project is the project directory.
|
||||
return isFile ? path.dirname(project) : project;
|
||||
}
|
||||
|
||||
export function performCompilation(
|
||||
basePath: string, files: string[], options: ts.CompilerOptions, ngOptions: any,
|
||||
consoleError: (s: string) => void = console.error,
|
||||
checkFunc: (cwd: string, ...args: any[]) => void = throwOnDiagnostics,
|
||||
tsCompilerHost?: ts.CompilerHost) {
|
||||
try {
|
||||
ngOptions.basePath = basePath;
|
||||
ngOptions.genDir = basePath;
|
||||
|
||||
let host = tsCompilerHost || ts.createCompilerHost(options, true);
|
||||
host.realpath = p => p;
|
||||
|
||||
const rootFileNames = files.map(f => path.normalize(f));
|
||||
|
||||
const addGeneratedFileName = (fileName: string) => {
|
||||
if (fileName.startsWith(basePath) && TS_EXT.exec(fileName)) {
|
||||
rootFileNames.push(fileName);
|
||||
}
|
||||
};
|
||||
|
||||
if (ngOptions.flatModuleOutFile && !ngOptions.skipMetadataEmit) {
|
||||
const {host: bundleHost, indexName, errors} =
|
||||
createBundleIndexHost(ngOptions, rootFileNames, host);
|
||||
if (errors) checkFunc(basePath, errors);
|
||||
if (indexName) addGeneratedFileName(indexName);
|
||||
host = bundleHost;
|
||||
}
|
||||
|
||||
const ngHostOptions = {...options, ...ngOptions};
|
||||
const ngHost = ng.createHost({tsHost: host, options: ngHostOptions});
|
||||
|
||||
const ngProgram =
|
||||
ng.createProgram({rootNames: rootFileNames, host: ngHost, options: ngHostOptions});
|
||||
|
||||
// Check parameter diagnostics
|
||||
checkFunc(basePath, ngProgram.getTsOptionDiagnostics(), ngProgram.getNgOptionDiagnostics());
|
||||
|
||||
// Check syntactic diagnostics
|
||||
checkFunc(basePath, ngProgram.getTsSyntacticDiagnostics());
|
||||
|
||||
// Check TypeScript semantic and Angular structure diagnostics
|
||||
checkFunc(
|
||||
basePath, ngProgram.getTsSemanticDiagnostics(), ngProgram.getNgStructuralDiagnostics());
|
||||
|
||||
// Check Angular semantic diagnostics
|
||||
checkFunc(basePath, ngProgram.getNgSemanticDiagnostics());
|
||||
|
||||
ngProgram.emit({
|
||||
emitFlags: api.EmitFlags.Default |
|
||||
((ngOptions.skipMetadataEmit || ngOptions.flatModuleOutFile) ? 0 : api.EmitFlags.Metadata)
|
||||
});
|
||||
} catch (e) {
|
||||
if (isSyntaxError(e)) {
|
||||
console.error(e.message);
|
||||
consoleError(e.message);
|
||||
return 1;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -91,6 +91,10 @@ export interface CompilerOptions extends ts.CompilerOptions {
|
||||
|
||||
// Whether to enable support for <template> and the template attribute (true by default)
|
||||
enableLegacyTemplate?: boolean;
|
||||
|
||||
// Whether to enable lowering expressions lambdas and expressions in a reference value
|
||||
// position.
|
||||
disableExpressionLowering?: boolean;
|
||||
}
|
||||
|
||||
export interface ModuleFilenameResolver {
|
||||
|
222
packages/compiler-cli/src/transformers/lower_expressions.ts
Normal file
222
packages/compiler-cli/src/transformers/lower_expressions.ts
Normal file
@ -0,0 +1,222 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
import {CollectorOptions, MetadataCollector, MetadataValue, ModuleMetadata} from '@angular/tsc-wrapped';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
export interface LoweringRequest {
|
||||
kind: ts.SyntaxKind;
|
||||
location: number;
|
||||
end: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export type RequestLocationMap = Map<number, LoweringRequest>;
|
||||
|
||||
interface Declaration {
|
||||
name: string;
|
||||
node: ts.Node;
|
||||
}
|
||||
|
||||
interface DeclarationInsert {
|
||||
declarations: Declaration[];
|
||||
priorTo: ts.Node;
|
||||
}
|
||||
|
||||
function toMap<T, K>(items: T[], select: (item: T) => K): Map<K, T> {
|
||||
return new Map(items.map<[K, T]>(i => [select(i), i]));
|
||||
}
|
||||
|
||||
// We will never lower expressions in a nested lexical scope so avoid entering them.
|
||||
// This also avoids a bug in TypeScript 2.3 where the lexical scopes get out of sync
|
||||
// when using visitEachChild.
|
||||
function isLexicalScope(node: ts.Node): boolean {
|
||||
switch (node.kind) {
|
||||
case ts.SyntaxKind.ArrowFunction:
|
||||
case ts.SyntaxKind.FunctionExpression:
|
||||
case ts.SyntaxKind.FunctionDeclaration:
|
||||
case ts.SyntaxKind.ClassExpression:
|
||||
case ts.SyntaxKind.ClassDeclaration:
|
||||
case ts.SyntaxKind.FunctionType:
|
||||
case ts.SyntaxKind.TypeLiteral:
|
||||
case ts.SyntaxKind.ArrayType:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function transformSourceFile(
|
||||
sourceFile: ts.SourceFile, requests: RequestLocationMap,
|
||||
context: ts.TransformationContext): ts.SourceFile {
|
||||
const inserts: DeclarationInsert[] = [];
|
||||
|
||||
// Calculate the range of intersting locations. The transform will only visit nodes in this
|
||||
// range to improve the performance on large files.
|
||||
const locations = Array.from(requests.keys());
|
||||
const min = Math.min(...locations);
|
||||
const max = Math.max(...locations);
|
||||
|
||||
function visitSourceFile(sourceFile: ts.SourceFile): ts.SourceFile {
|
||||
function topLevelStatement(node: ts.Node): ts.Node {
|
||||
const declarations: Declaration[] = [];
|
||||
|
||||
function visitNode(node: ts.Node): ts.Node {
|
||||
const nodeRequest = requests.get(node.pos);
|
||||
if (nodeRequest && nodeRequest.kind == node.kind && nodeRequest.end == node.end) {
|
||||
// This node is requested to be rewritten as a reference to the exported name.
|
||||
// Record that the node needs to be moved to an exported variable with the given name
|
||||
const name = nodeRequest.name;
|
||||
declarations.push({name, node});
|
||||
return ts.createIdentifier(name);
|
||||
}
|
||||
let result = node;
|
||||
if (node.pos <= max && node.end >= min && !isLexicalScope(node)) {
|
||||
result = ts.visitEachChild(node, visitNode, context);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const result =
|
||||
(node.pos <= max && node.end >= min) ? ts.visitEachChild(node, visitNode, context) : node;
|
||||
|
||||
if (declarations.length) {
|
||||
inserts.push({priorTo: result, declarations});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const traversedSource = ts.visitEachChild(sourceFile, topLevelStatement, context);
|
||||
if (inserts.length) {
|
||||
// Insert the declarations before the rewritten statement that references them.
|
||||
const insertMap = toMap(inserts, i => i.priorTo);
|
||||
const newStatements: ts.Statement[] = [...traversedSource.statements];
|
||||
for (let i = newStatements.length; i >= 0; i--) {
|
||||
const statement = newStatements[i];
|
||||
const insert = insertMap.get(statement);
|
||||
if (insert) {
|
||||
const declarations = insert.declarations.map(
|
||||
i => ts.createVariableDeclaration(
|
||||
i.name, /* type */ undefined, i.node as ts.Expression));
|
||||
const statement = ts.createVariableStatement(
|
||||
/* modifiers */ undefined,
|
||||
ts.createVariableDeclarationList(declarations, ts.NodeFlags.Const));
|
||||
newStatements.splice(i, 0, statement);
|
||||
}
|
||||
}
|
||||
|
||||
// Insert an exports clause to export the declarations
|
||||
newStatements.push(ts.createExportDeclaration(
|
||||
/* decorators */ undefined,
|
||||
/* modifiers */ undefined,
|
||||
ts.createNamedExports(
|
||||
inserts
|
||||
.reduce(
|
||||
(accumulator, insert) => [...accumulator, ...insert.declarations],
|
||||
[] as Declaration[])
|
||||
.map(
|
||||
declaration => ts.createExportSpecifier(
|
||||
/* propertyName */ undefined, declaration.name)))));
|
||||
return ts.updateSourceFileNode(traversedSource, newStatements);
|
||||
}
|
||||
return traversedSource;
|
||||
}
|
||||
|
||||
return visitSourceFile(sourceFile);
|
||||
}
|
||||
|
||||
export function getExpressionLoweringTransformFactory(requestsMap: RequestsMap):
|
||||
(context: ts.TransformationContext) => (sourceFile: ts.SourceFile) => ts.SourceFile {
|
||||
// Return the factory
|
||||
return (context: ts.TransformationContext) => (sourceFile: ts.SourceFile): ts.SourceFile => {
|
||||
const requests = requestsMap.getRequests(sourceFile);
|
||||
if (requests && requests.size) {
|
||||
return transformSourceFile(sourceFile, requests, context);
|
||||
}
|
||||
return sourceFile;
|
||||
};
|
||||
}
|
||||
|
||||
export interface RequestsMap { getRequests(sourceFile: ts.SourceFile): RequestLocationMap; }
|
||||
|
||||
interface MetadataAndLoweringRequests {
|
||||
metadata: ModuleMetadata|undefined;
|
||||
requests: RequestLocationMap;
|
||||
}
|
||||
|
||||
function shouldLower(node: ts.Node | undefined): boolean {
|
||||
if (node) {
|
||||
switch (node.kind) {
|
||||
case ts.SyntaxKind.SourceFile:
|
||||
case ts.SyntaxKind.Decorator:
|
||||
// Lower expressions that are local to the module scope or
|
||||
// in a decorator.
|
||||
return true;
|
||||
case ts.SyntaxKind.ClassDeclaration:
|
||||
case ts.SyntaxKind.InterfaceDeclaration:
|
||||
case ts.SyntaxKind.EnumDeclaration:
|
||||
case ts.SyntaxKind.FunctionDeclaration:
|
||||
// Don't lower expressions in a declaration.
|
||||
return false;
|
||||
case ts.SyntaxKind.VariableDeclaration:
|
||||
// Avoid lowering expressions already in an exported variable declaration
|
||||
return (ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Export) == 0;
|
||||
}
|
||||
return shouldLower(node.parent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export class LowerMetadataCache implements RequestsMap {
|
||||
private collector: MetadataCollector;
|
||||
private metadataCache = new Map<string, MetadataAndLoweringRequests>();
|
||||
|
||||
constructor(options: CollectorOptions, private strict?: boolean) {
|
||||
this.collector = new MetadataCollector(options);
|
||||
}
|
||||
|
||||
getMetadata(sourceFile: ts.SourceFile): ModuleMetadata|undefined {
|
||||
return this.ensureMetadataAndRequests(sourceFile).metadata;
|
||||
}
|
||||
|
||||
getRequests(sourceFile: ts.SourceFile): RequestLocationMap {
|
||||
return this.ensureMetadataAndRequests(sourceFile).requests;
|
||||
}
|
||||
|
||||
private ensureMetadataAndRequests(sourceFile: ts.SourceFile): MetadataAndLoweringRequests {
|
||||
let result = this.metadataCache.get(sourceFile.fileName);
|
||||
if (!result) {
|
||||
result = this.getMetadataAndRequests(sourceFile);
|
||||
this.metadataCache.set(sourceFile.fileName, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private getMetadataAndRequests(sourceFile: ts.SourceFile): MetadataAndLoweringRequests {
|
||||
let identNumber = 0;
|
||||
const freshIdent = () => '\u0275' + identNumber++;
|
||||
const requests = new Map<number, LoweringRequest>();
|
||||
const replaceNode = (node: ts.Node) => {
|
||||
const name = freshIdent();
|
||||
requests.set(node.pos, {name, kind: node.kind, location: node.pos, end: node.end});
|
||||
return {__symbolic: 'reference', name};
|
||||
};
|
||||
|
||||
const substituteExpression = (value: MetadataValue, node: ts.Node): MetadataValue => {
|
||||
if ((node.kind === ts.SyntaxKind.ArrowFunction ||
|
||||
node.kind === ts.SyntaxKind.FunctionExpression) &&
|
||||
shouldLower(node)) {
|
||||
return replaceNode(node);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
const metadata = this.collector.getMetadata(sourceFile, this.strict, substituteExpression);
|
||||
|
||||
return {metadata, requests};
|
||||
}
|
||||
}
|
@ -289,4 +289,4 @@ function createModuleFilenameResolverHost(host: ts.ModuleResolutionHost):
|
||||
resolveModuleNameHost.realpath = (fileName: string) => fileName;
|
||||
|
||||
return resolveModuleNameHost;
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@ import {CompilerHost as AotCompilerHost, CompilerHostContext} from '../compiler_
|
||||
import {TypeChecker} from '../diagnostics/check_types';
|
||||
|
||||
import {CompilerHost, CompilerOptions, Diagnostic, DiagnosticCategory, EmitFlags, Program} from './api';
|
||||
import {LowerMetadataCache, getExpressionLoweringTransformFactory} from './lower_expressions';
|
||||
import {getAngularEmitterTransformFactory} from './node_emitter_transform';
|
||||
|
||||
const GENERATED_FILES = /\.ngfactory\.js$|\.ngstyle\.js$|\.ngsummary\.js$/;
|
||||
@ -34,7 +35,7 @@ class AngularCompilerProgram implements Program {
|
||||
private aotCompilerHost: AotCompilerHost;
|
||||
private compiler: AotCompiler;
|
||||
private srcNames: string[];
|
||||
private collector: MetadataCollector;
|
||||
private metadataCache: LowerMetadataCache;
|
||||
// Lazily initialized fields
|
||||
private _analyzedModules: NgAnalyzedModules|undefined;
|
||||
private _structuralDiagnostics: Diagnostic[] = [];
|
||||
@ -54,13 +55,14 @@ class AngularCompilerProgram implements Program {
|
||||
|
||||
this.tsProgram = ts.createProgram(rootNames, options, host, this.oldTsProgram);
|
||||
this.srcNames = this.tsProgram.getSourceFiles().map(sf => sf.fileName);
|
||||
this.aotCompilerHost = new AotCompilerHost(this.tsProgram, options, host);
|
||||
this.metadataCache = new LowerMetadataCache({quotedNames: true}, !!options.strictMetadataEmit);
|
||||
this.aotCompilerHost = new AotCompilerHost(
|
||||
this.tsProgram, options, host, /* collectorOptions */ undefined, this.metadataCache);
|
||||
if (host.readResource) {
|
||||
this.aotCompilerHost.loadResource = host.readResource.bind(host);
|
||||
}
|
||||
const {compiler} = createAotCompiler(this.aotCompilerHost, options);
|
||||
this.compiler = compiler;
|
||||
this.collector = new MetadataCollector({quotedNames: true});
|
||||
}
|
||||
|
||||
// Program implementation
|
||||
@ -117,11 +119,9 @@ class AngularCompilerProgram implements Program {
|
||||
const emitMap = new Map<string, string>();
|
||||
const result = this.programWithStubs.emit(
|
||||
/* targetSourceFile */ undefined,
|
||||
createWriteFileCallback(emitFlags, this.host, this.collector, this.options, emitMap),
|
||||
cancellationToken, (emitFlags & (EmitFlags.DTS | EmitFlags.JS)) == EmitFlags.DTS, {
|
||||
after: this.options.skipTemplateCodegen ? [] : [getAngularEmitterTransformFactory(
|
||||
this.generatedFiles)]
|
||||
});
|
||||
createWriteFileCallback(emitFlags, this.host, this.metadataCache, emitMap),
|
||||
cancellationToken, (emitFlags & (EmitFlags.DTS | EmitFlags.JS)) == EmitFlags.DTS,
|
||||
this.calculateTransforms());
|
||||
|
||||
this.generatedFiles.forEach(file => {
|
||||
if (file.source && file.source.length && SUMMARY_JSON_FILES.test(file.genFileUrl)) {
|
||||
@ -183,6 +183,21 @@ class AngularCompilerProgram implements Program {
|
||||
return this.generatedFiles && this._generatedFileDiagnostics !;
|
||||
}
|
||||
|
||||
private calculateTransforms(): ts.CustomTransformers {
|
||||
const before: ts.TransformerFactory<ts.SourceFile>[] = [];
|
||||
const after: ts.TransformerFactory<ts.SourceFile>[] = [];
|
||||
if (!this.options.disableExpressionLowering) {
|
||||
before.push(getExpressionLoweringTransformFactory(this.metadataCache));
|
||||
}
|
||||
if (!this.options.skipTemplateCodegen) {
|
||||
after.push(getAngularEmitterTransformFactory(this.generatedFiles));
|
||||
}
|
||||
const result: ts.CustomTransformers = {};
|
||||
if (before.length) result.before = before;
|
||||
if (after.length) result.after = after;
|
||||
return result;
|
||||
}
|
||||
|
||||
private catchAnalysisError(e: any): NgAnalyzedModules {
|
||||
if (isSyntaxError(e)) {
|
||||
const parserErrors = getParseErrors(e);
|
||||
@ -256,8 +271,7 @@ export function createProgram(
|
||||
}
|
||||
|
||||
function writeMetadata(
|
||||
emitFilePath: string, sourceFile: ts.SourceFile, collector: MetadataCollector,
|
||||
ngOptions: CompilerOptions) {
|
||||
emitFilePath: string, sourceFile: ts.SourceFile, metadataCache: LowerMetadataCache) {
|
||||
if (/\.js$/.test(emitFilePath)) {
|
||||
const path = emitFilePath.replace(/\.js$/, '.metadata.json');
|
||||
|
||||
@ -270,7 +284,7 @@ function writeMetadata(
|
||||
collectableFile = (collectableFile as any).original;
|
||||
}
|
||||
|
||||
const metadata = collector.getMetadata(collectableFile, !!ngOptions.strictMetadataEmit);
|
||||
const metadata = metadataCache.getMetadata(collectableFile);
|
||||
if (metadata) {
|
||||
const metadataText = JSON.stringify([metadata]);
|
||||
writeFileSync(path, metadataText, {encoding: 'utf-8'});
|
||||
@ -279,8 +293,8 @@ function writeMetadata(
|
||||
}
|
||||
|
||||
function createWriteFileCallback(
|
||||
emitFlags: EmitFlags, host: ts.CompilerHost, collector: MetadataCollector,
|
||||
ngOptions: CompilerOptions, emitMap: Map<string, string>) {
|
||||
emitFlags: EmitFlags, host: ts.CompilerHost, metadataCache: LowerMetadataCache,
|
||||
emitMap: Map<string, string>) {
|
||||
const withMetadata =
|
||||
(fileName: string, data: string, writeByteOrderMark: boolean,
|
||||
onError?: (message: string) => void, sourceFiles?: ts.SourceFile[]) => {
|
||||
@ -290,7 +304,7 @@ function createWriteFileCallback(
|
||||
}
|
||||
if (!generatedFile && sourceFiles && sourceFiles.length == 1) {
|
||||
emitMap.set(sourceFiles[0].fileName, fileName);
|
||||
writeMetadata(fileName, sourceFiles[0], collector, ngOptions);
|
||||
writeMetadata(fileName, sourceFiles[0], metadataCache);
|
||||
}
|
||||
};
|
||||
const withoutMetadata =
|
||||
|
@ -11,7 +11,8 @@ import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {main, performCompilation} from '../src/ngc';
|
||||
import {main} from '../src/ngc';
|
||||
import {performCompilation, readConfiguration} from '../src/perform-compile';
|
||||
|
||||
function getNgRootDir() {
|
||||
const moduleFilename = module.filename.replace(/\\/g, '/');
|
||||
@ -308,7 +309,6 @@ describe('ngc command-line', () => {
|
||||
|
||||
const exitCode = main(['-p', path.join(basePath, 'tsconfig.json')]);
|
||||
expect(exitCode).toEqual(0);
|
||||
|
||||
expect(fs.existsSync(path.resolve(outDir, 'mymodule.ngfactory.js'))).toBe(true);
|
||||
expect(fs.existsSync(path.resolve(
|
||||
outDir, 'node_modules', '@angular', 'core', 'src',
|
||||
@ -316,6 +316,145 @@ describe('ngc command-line', () => {
|
||||
.toBe(true);
|
||||
});
|
||||
|
||||
describe('expression lowering', () => {
|
||||
beforeEach(() => {
|
||||
writeConfig(`{
|
||||
"extends": "./tsconfig-base.json",
|
||||
"files": ["mymodule.ts"]
|
||||
}`);
|
||||
});
|
||||
|
||||
function compile(): number {
|
||||
const errors: string[] = [];
|
||||
const result = main(['-p', path.join(basePath, 'tsconfig.json')], s => errors.push(s));
|
||||
expect(errors).toEqual([]);
|
||||
return result;
|
||||
}
|
||||
|
||||
it('should be able to lower a lambda expression in a provider', () => {
|
||||
write('mymodule.ts', `
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
class Foo {}
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule],
|
||||
providers: [{provide: 'someToken', useFactory: () => new Foo()}]
|
||||
})
|
||||
export class MyModule {}
|
||||
`);
|
||||
expect(compile()).toEqual(0);
|
||||
|
||||
const mymodulejs = path.resolve(outDir, 'mymodule.js');
|
||||
const mymoduleSource = fs.readFileSync(mymodulejs, 'utf8');
|
||||
expect(mymoduleSource).toContain('var ɵ0 = function () { return new Foo(); }');
|
||||
expect(mymoduleSource).toContain('export { ɵ0');
|
||||
|
||||
const mymodulefactory = path.resolve(outDir, 'mymodule.ngfactory.js');
|
||||
const mymodulefactorySource = fs.readFileSync(mymodulefactory, 'utf8');
|
||||
expect(mymodulefactorySource).toContain('"someToken", i1.ɵ0');
|
||||
});
|
||||
|
||||
it('should be able to lower a function expression in a provider', () => {
|
||||
write('mymodule.ts', `
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
class Foo {}
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule],
|
||||
providers: [{provide: 'someToken', useFactory: function() {return new Foo();}}]
|
||||
})
|
||||
export class MyModule {}
|
||||
`);
|
||||
expect(compile()).toEqual(0);
|
||||
|
||||
const mymodulejs = path.resolve(outDir, 'mymodule.js');
|
||||
const mymoduleSource = fs.readFileSync(mymodulejs, 'utf8');
|
||||
expect(mymoduleSource).toContain('var ɵ0 = function () { return new Foo(); }');
|
||||
expect(mymoduleSource).toContain('export { ɵ0');
|
||||
|
||||
const mymodulefactory = path.resolve(outDir, 'mymodule.ngfactory.js');
|
||||
const mymodulefactorySource = fs.readFileSync(mymodulefactory, 'utf8');
|
||||
expect(mymodulefactorySource).toContain('"someToken", i1.ɵ0');
|
||||
});
|
||||
|
||||
it('should able to lower multiple expressions', () => {
|
||||
write('mymodule.ts', `
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
class Foo {}
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule],
|
||||
providers: [
|
||||
{provide: 'someToken', useFactory: () => new Foo()},
|
||||
{provide: 'someToken', useFactory: () => new Foo()},
|
||||
{provide: 'someToken', useFactory: () => new Foo()},
|
||||
{provide: 'someToken', useFactory: () => new Foo()}
|
||||
]
|
||||
})
|
||||
export class MyModule {}
|
||||
`);
|
||||
expect(compile()).toEqual(0);
|
||||
const mymodulejs = path.resolve(outDir, 'mymodule.js');
|
||||
const mymoduleSource = fs.readFileSync(mymodulejs, 'utf8');
|
||||
expect(mymoduleSource).toContain('ɵ0 = function () { return new Foo(); }');
|
||||
expect(mymoduleSource).toContain('ɵ1 = function () { return new Foo(); }');
|
||||
expect(mymoduleSource).toContain('ɵ2 = function () { return new Foo(); }');
|
||||
expect(mymoduleSource).toContain('ɵ3 = function () { return new Foo(); }');
|
||||
expect(mymoduleSource).toContain('export { ɵ0, ɵ1, ɵ2, ɵ3');
|
||||
});
|
||||
|
||||
it('should be able to lower an indirect expression', () => {
|
||||
write('mymodule.ts', `
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
class Foo {}
|
||||
|
||||
const factory = () => new Foo();
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule],
|
||||
providers: [{provide: 'someToken', useFactory: factory}]
|
||||
})
|
||||
export class MyModule {}
|
||||
`);
|
||||
expect(compile()).toEqual(0, 'Compile failed');
|
||||
|
||||
const mymodulejs = path.resolve(outDir, 'mymodule.js');
|
||||
const mymoduleSource = fs.readFileSync(mymodulejs, 'utf8');
|
||||
expect(mymoduleSource).toContain('var ɵ0 = function () { return new Foo(); }');
|
||||
expect(mymoduleSource).toContain('export { ɵ0');
|
||||
});
|
||||
|
||||
it('should not lower a lambda that is already exported', () => {
|
||||
write('mymodule.ts', `
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
class Foo {}
|
||||
|
||||
export const factory = () => new Foo();
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule],
|
||||
providers: [{provide: 'someToken', useFactory: factory}]
|
||||
})
|
||||
export class MyModule {}
|
||||
`);
|
||||
expect(compile()).toEqual(0);
|
||||
|
||||
const mymodulejs = path.resolve(outDir, 'mymodule.js');
|
||||
const mymoduleSource = fs.readFileSync(mymodulejs, 'utf8');
|
||||
expect(mymoduleSource).not.toContain('ɵ0');
|
||||
});
|
||||
});
|
||||
|
||||
const shouldExist = (fileName: string) => {
|
||||
if (!fs.existsSync(path.resolve(outDir, fileName))) {
|
||||
throw new Error(`Expected ${fileName} to be emitted (outDir: ${outDir})`);
|
||||
|
@ -0,0 +1,105 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {LoweringRequest, RequestLocationMap, getExpressionLoweringTransformFactory} from '../../src/transformers/lower_expressions';
|
||||
import {Directory, MockAotContext, MockCompilerHost} from '../mocks';
|
||||
|
||||
describe('Expression lowering', () => {
|
||||
it('should be able to lower a simple expression', () => {
|
||||
expect(convert('const a = 1 +◊b: 2◊;')).toBe('const b = 2; const a = 1 + b; export { b };');
|
||||
});
|
||||
|
||||
it('should be able to lower an expression in a decorator', () => {
|
||||
expect(convert(`
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
provider: [{provide: 'someToken', useFactory:◊l: () => null◊}]
|
||||
})
|
||||
class MyClass {}
|
||||
`)).toContain('const l = () => null; exports.l = l;');
|
||||
});
|
||||
});
|
||||
|
||||
function convert(annotatedSource: string) {
|
||||
const annotations: {start: number, length: number, name: string}[] = [];
|
||||
let adjustment = 0;
|
||||
const unannotatedSource = annotatedSource.replace(
|
||||
/◊([a-zA-Z]+):(.*)◊/g,
|
||||
(text: string, name: string, source: string, index: number): string => {
|
||||
annotations.push({start: index + adjustment, length: source.length, name});
|
||||
adjustment -= text.length - source.length;
|
||||
return source;
|
||||
});
|
||||
|
||||
const baseFileName = 'someFile';
|
||||
const moduleName = '/' + baseFileName;
|
||||
const fileName = moduleName + '.ts';
|
||||
const context = new MockAotContext('/', {[baseFileName + '.ts']: unannotatedSource});
|
||||
const host = new MockCompilerHost(context);
|
||||
|
||||
const sourceFile = ts.createSourceFile(
|
||||
fileName, unannotatedSource, ts.ScriptTarget.Latest, /* setParentNodes */ true);
|
||||
const requests = new Map<number, LoweringRequest>();
|
||||
|
||||
for (const annotation of annotations) {
|
||||
const node = findNode(sourceFile, annotation.start, annotation.length);
|
||||
if (!node) throw new Error('Invalid test specification. Could not find the node to substitute');
|
||||
const location = node.pos;
|
||||
requests.set(location, {name: annotation.name, kind: node.kind, location, end: node.end});
|
||||
}
|
||||
|
||||
const program = ts.createProgram(
|
||||
[fileName], {module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2017}, host);
|
||||
const moduleSourceFile = program.getSourceFile(fileName);
|
||||
const transformers: ts.CustomTransformers = {
|
||||
before: [getExpressionLoweringTransformFactory({
|
||||
getRequests(sourceFile: ts.SourceFile): RequestLocationMap{
|
||||
if (sourceFile.fileName == moduleSourceFile.fileName) {
|
||||
return requests;
|
||||
} else {return new Map();}
|
||||
}
|
||||
})]
|
||||
};
|
||||
let result: string = '';
|
||||
const emitResult = program.emit(
|
||||
moduleSourceFile, (emittedFileName, data, writeByteOrderMark, onError, sourceFiles) => {
|
||||
if (fileName.startsWith(moduleName)) {
|
||||
result = data;
|
||||
}
|
||||
}, undefined, undefined, transformers);
|
||||
return normalizeResult(result);
|
||||
};
|
||||
|
||||
function findNode(node: ts.Node, start: number, length: number): ts.Node|undefined {
|
||||
function find(node: ts.Node): ts.Node|undefined {
|
||||
if (node.getFullStart() == start && node.getEnd() == start + length) {
|
||||
return node;
|
||||
}
|
||||
if (node.getFullStart() <= start && node.getEnd() >= start + length) {
|
||||
return ts.forEachChild(node, find);
|
||||
}
|
||||
}
|
||||
return ts.forEachChild(node, find);
|
||||
}
|
||||
|
||||
function normalizeResult(result: string): string {
|
||||
// Remove TypeScript prefixes
|
||||
// Remove new lines
|
||||
// Squish adjacent spaces
|
||||
// Remove prefix and postfix spaces
|
||||
return result.replace('"use strict";', ' ')
|
||||
.replace('exports.__esModule = true;', ' ')
|
||||
.replace('Object.defineProperty(exports, "__esModule", { value: true });', ' ')
|
||||
.replace(/\n/g, ' ')
|
||||
.replace(/ +/g, ' ')
|
||||
.replace(/^ /g, '')
|
||||
.replace(/ $/g, '');
|
||||
}
|
@ -14,7 +14,7 @@
|
||||
"@angular/http": ["../../dist/packages/http"],
|
||||
"@angular/platform-server": ["../../dist/packages/platform-server"],
|
||||
"@angular/platform-browser": ["../../dist/packages/platform-browser"],
|
||||
"@angular/tsc-wrapped": ["../../dist/tools/@angular/tsc-wrapped"]
|
||||
"@angular/tsc-wrapped": ["../../dist/packages-dist/tsc-wrapped"]
|
||||
},
|
||||
"rootDir": ".",
|
||||
"sourceMap": true,
|
||||
|
13
packages/compiler/BUILD.bazel
Normal file
13
packages/compiler/BUILD.bazel
Normal file
@ -0,0 +1,13 @@
|
||||
package(default_visibility=["//visibility:public"])
|
||||
load("@build_bazel_rules_typescript//:defs.bzl", "ts_library")
|
||||
|
||||
ts_library(
|
||||
name = "compiler",
|
||||
srcs = glob(["**/*.ts"], exclude=[
|
||||
"test/**",
|
||||
"testing/**",
|
||||
]),
|
||||
module_name = "@angular/compiler",
|
||||
deps = ["//packages/core"],
|
||||
tsconfig = ":tsconfig-build.json",
|
||||
)
|
@ -376,7 +376,6 @@ export class StaticReflector implements CompileReflector {
|
||||
if (calling.get(functionSymbol)) {
|
||||
throw new Error('Recursion not supported');
|
||||
}
|
||||
calling.set(functionSymbol, true);
|
||||
try {
|
||||
const value = targetFunction['value'];
|
||||
if (value && (depth != 0 || value.__symbolic != 'error')) {
|
||||
@ -387,6 +386,7 @@ export class StaticReflector implements CompileReflector {
|
||||
if (defaults && defaults.length > args.length) {
|
||||
args.push(...defaults.slice(args.length).map((value: any) => simplify(value)));
|
||||
}
|
||||
calling.set(functionSymbol, true);
|
||||
const functionScope = BindingScope.build();
|
||||
for (let i = 0; i < parameters.length; i++) {
|
||||
functionScope.define(parameters[i], args[i]);
|
||||
|
@ -345,4 +345,4 @@ class FromJsonDeserializer extends ValueTransformer {
|
||||
return super.visitStringMap(map, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -231,7 +231,6 @@ class _Scanner {
|
||||
* @param two second symbol (part of the operator when the second code point matches)
|
||||
* @param threeCode code point for the third symbol
|
||||
* @param three third symbol (part of the operator when provided and matches source expression)
|
||||
* @returns {Token}
|
||||
*/
|
||||
scanComplexOperator(
|
||||
start: number, one: string, twoCode: number, two: string, threeCode?: number,
|
||||
|
@ -139,23 +139,28 @@ class _WriteVisitor implements i18n.Visitor {
|
||||
visitTagPlaceholder(ph: i18n.TagPlaceholder, context?: any): xml.Node[] {
|
||||
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];
|
||||
return [new xml.Tag(
|
||||
_PLACEHOLDER_TAG, {id: ph.startName, ctype, 'equiv-text': `<${ph.tag}/>`})];
|
||||
}
|
||||
|
||||
const closeTagPh = new xml.Tag(_PLACEHOLDER_TAG, {id: ph.closeName, ctype});
|
||||
const startTagPh =
|
||||
new xml.Tag(_PLACEHOLDER_TAG, {id: ph.startName, ctype, 'equiv-text': `<${ph.tag}>`});
|
||||
const closeTagPh =
|
||||
new xml.Tag(_PLACEHOLDER_TAG, {id: ph.closeName, ctype, 'equiv-text': `</${ph.tag}>`});
|
||||
|
||||
return [startTagPh, ...this.serialize(ph.children), closeTagPh];
|
||||
}
|
||||
|
||||
visitPlaceholder(ph: i18n.Placeholder, context?: any): xml.Node[] {
|
||||
return [new xml.Tag(_PLACEHOLDER_TAG, {id: ph.name})];
|
||||
return [new xml.Tag(_PLACEHOLDER_TAG, {id: ph.name, 'equiv-text': `{{${ph.value}}}`})];
|
||||
}
|
||||
|
||||
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): xml.Node[] {
|
||||
return [new xml.Tag(_PLACEHOLDER_TAG, {id: ph.name})];
|
||||
const equivText =
|
||||
`{${ph.value.expression}, ${ph.value.type}, ${Object.keys(ph.value.cases).map((value: string) => value + ' {...}').join(' ')}}`;
|
||||
return [new xml.Tag(_PLACEHOLDER_TAG, {id: ph.name, 'equiv-text': equivText})];
|
||||
}
|
||||
|
||||
serialize(nodes: i18n.Node[]): xml.Node[] {
|
||||
|
@ -129,11 +129,16 @@ class _Visitor implements i18n.Visitor {
|
||||
}
|
||||
|
||||
visitPlaceholder(ph: i18n.Placeholder, context?: any): xml.Node[] {
|
||||
return [new xml.Tag(_PLACEHOLDER_TAG, {name: ph.name})];
|
||||
const exTag = new xml.Tag(_EXEMPLE_TAG, {}, [new xml.Text(`{{${ph.value}}}`)]);
|
||||
return [new xml.Tag(_PLACEHOLDER_TAG, {name: ph.name}, [exTag])];
|
||||
}
|
||||
|
||||
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): xml.Node[] {
|
||||
return [new xml.Tag(_PLACEHOLDER_TAG, {name: ph.name})];
|
||||
const exTag = new xml.Tag(_EXEMPLE_TAG, {}, [
|
||||
new xml.Text(
|
||||
`{${ph.value.expression}, ${ph.value.type}, ${Object.keys(ph.value.cases).map((value: string) => value + ' {...}').join(' ')}}`)
|
||||
]);
|
||||
return [new xml.Tag(_PLACEHOLDER_TAG, {name: ph.name}, [exTag])];
|
||||
}
|
||||
|
||||
serialize(nodes: i18n.Node[]): xml.Node[] {
|
||||
|
@ -6,8 +6,9 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, InjectionToken, MissingTranslationStrategy, Optional, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore, ɵConsole as Console} from '@angular/core';
|
||||
import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, InjectionToken, Injector, MissingTranslationStrategy, Optional, PACKAGE_ROOT_URL, PlatformRef, StaticProvider, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore, ɵConsole as Console} from '@angular/core';
|
||||
|
||||
import {StaticSymbolCache} from '../aot/static_symbol';
|
||||
import {CompileReflector} from '../compile_reflector';
|
||||
import {CompilerConfig} from '../config';
|
||||
import {DirectiveNormalizer} from '../directive_normalizer';
|
||||
@ -16,7 +17,7 @@ import {Lexer} from '../expression_parser/lexer';
|
||||
import {Parser} from '../expression_parser/parser';
|
||||
import * as i18n from '../i18n/index';
|
||||
import {CompilerInjectable} from '../injectable';
|
||||
import {CompileMetadataResolver} from '../metadata_resolver';
|
||||
import {CompileMetadataResolver, ERROR_COLLECTOR_TOKEN} from '../metadata_resolver';
|
||||
import {HtmlParser} from '../ml_parser/html_parser';
|
||||
import {NgModuleCompiler} from '../ng_module_compiler';
|
||||
import {NgModuleResolver} from '../ng_module_resolver';
|
||||
@ -26,7 +27,7 @@ import {DomElementSchemaRegistry} from '../schema/dom_element_schema_registry';
|
||||
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
||||
import {StyleCompiler} from '../style_compiler';
|
||||
import {JitSummaryResolver, SummaryResolver} from '../summary_resolver';
|
||||
import {TemplateParser} from '../template_parser/template_parser';
|
||||
import {TEMPLATE_TRANSFORMS, TemplateParser} from '../template_parser/template_parser';
|
||||
import {DEFAULT_PACKAGE_URL_PROVIDER, UrlResolver} from '../url_resolver';
|
||||
import {ViewCompiler} from '../view_compiler/view_compiler';
|
||||
|
||||
@ -45,17 +46,18 @@ const baseHtmlParser = new InjectionToken('HtmlParser');
|
||||
* A set of providers that provide `JitCompiler` and its dependencies to use for
|
||||
* template compilation.
|
||||
*/
|
||||
export const COMPILER_PROVIDERS: Array<any|Type<any>|{[k: string]: any}|any[]> = [
|
||||
export const COMPILER_PROVIDERS = <StaticProvider[]>[
|
||||
{provide: CompileReflector, useValue: new JitReflector()},
|
||||
{provide: ResourceLoader, useValue: _NO_RESOURCE_LOADER},
|
||||
JitSummaryResolver,
|
||||
{provide: JitSummaryResolver, deps: []},
|
||||
{provide: SummaryResolver, useExisting: JitSummaryResolver},
|
||||
Console,
|
||||
Lexer,
|
||||
Parser,
|
||||
{provide: Console, deps: []},
|
||||
{provide: Lexer, deps: []},
|
||||
{provide: Parser, deps: [Lexer]},
|
||||
{
|
||||
provide: baseHtmlParser,
|
||||
useClass: HtmlParser,
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
provide: i18n.I18NHtmlParser,
|
||||
@ -78,22 +80,37 @@ export const COMPILER_PROVIDERS: Array<any|Type<any>|{[k: string]: any}|any[]> =
|
||||
provide: HtmlParser,
|
||||
useExisting: i18n.I18NHtmlParser,
|
||||
},
|
||||
TemplateParser,
|
||||
DirectiveNormalizer,
|
||||
CompileMetadataResolver,
|
||||
{
|
||||
provide: TemplateParser, deps: [CompilerConfig, CompileReflector,
|
||||
Parser, ElementSchemaRegistry,
|
||||
i18n.I18NHtmlParser, Console, [Optional, TEMPLATE_TRANSFORMS]]
|
||||
},
|
||||
{ provide: DirectiveNormalizer, deps: [ResourceLoader, UrlResolver, HtmlParser, CompilerConfig]},
|
||||
{ provide: CompileMetadataResolver, deps: [CompilerConfig, NgModuleResolver,
|
||||
DirectiveResolver, PipeResolver,
|
||||
SummaryResolver,
|
||||
ElementSchemaRegistry,
|
||||
DirectiveNormalizer, Console,
|
||||
[Optional, StaticSymbolCache],
|
||||
CompileReflector,
|
||||
[Optional, ERROR_COLLECTOR_TOKEN]]},
|
||||
DEFAULT_PACKAGE_URL_PROVIDER,
|
||||
StyleCompiler,
|
||||
ViewCompiler,
|
||||
NgModuleCompiler,
|
||||
{provide: CompilerConfig, useValue: new CompilerConfig()},
|
||||
JitCompiler,
|
||||
{provide: Compiler, useExisting: JitCompiler},
|
||||
DomElementSchemaRegistry,
|
||||
{provide: ElementSchemaRegistry, useExisting: DomElementSchemaRegistry},
|
||||
UrlResolver,
|
||||
DirectiveResolver,
|
||||
PipeResolver,
|
||||
NgModuleResolver,
|
||||
{ provide: StyleCompiler, deps: [UrlResolver]},
|
||||
{ provide: ViewCompiler, deps: [CompilerConfig, CompileReflector, ElementSchemaRegistry]},
|
||||
{ provide: NgModuleCompiler, deps: [CompileReflector] },
|
||||
{ provide: CompilerConfig, useValue: new CompilerConfig()},
|
||||
{ provide: JitCompiler, deps: [Injector, CompileMetadataResolver,
|
||||
TemplateParser, StyleCompiler,
|
||||
ViewCompiler, NgModuleCompiler,
|
||||
SummaryResolver, CompilerConfig,
|
||||
Console]},
|
||||
{ provide: Compiler, useExisting: JitCompiler},
|
||||
{ provide: DomElementSchemaRegistry, deps: []},
|
||||
{ provide: ElementSchemaRegistry, useExisting: DomElementSchemaRegistry},
|
||||
{ provide: UrlResolver, deps: [PACKAGE_ROOT_URL]},
|
||||
{ provide: DirectiveResolver, deps: [CompileReflector]},
|
||||
{ provide: PipeResolver, deps: [CompileReflector]},
|
||||
{ provide: NgModuleResolver, deps: [CompileReflector]},
|
||||
];
|
||||
|
||||
@CompilerInjectable()
|
||||
@ -112,7 +129,7 @@ export class JitCompilerFactory implements CompilerFactory {
|
||||
}
|
||||
createCompiler(options: CompilerOptions[] = []): Compiler {
|
||||
const opts = _mergeOptions(this._defaultOptions.concat(options));
|
||||
const injector = ReflectiveInjector.resolveAndCreate([
|
||||
const injector = Injector.create([
|
||||
COMPILER_PROVIDERS, {
|
||||
provide: CompilerConfig,
|
||||
useFactory: () => {
|
||||
@ -142,7 +159,7 @@ export class JitCompilerFactory implements CompilerFactory {
|
||||
*/
|
||||
export const platformCoreDynamic = createPlatformFactory(platformCore, 'coreDynamic', [
|
||||
{provide: COMPILER_OPTIONS, useValue: {}, multi: true},
|
||||
{provide: CompilerFactory, useClass: JitCompilerFactory},
|
||||
{provide: CompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS]},
|
||||
]);
|
||||
|
||||
function _mergeOptions(optionsArr: CompilerOptions[]): CompilerOptions {
|
||||
|
@ -150,7 +150,7 @@ class _Tokenizer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean} whether an ICU token has been created
|
||||
* @returns whether an ICU token has been created
|
||||
* @internal
|
||||
*/
|
||||
private _tokenizeExpansionForm(): boolean {
|
||||
|
@ -401,7 +401,6 @@ export class BindingParser {
|
||||
* @param propName the name of the property / attribute
|
||||
* @param sourceSpan
|
||||
* @param isAttr true when binding to an attribute
|
||||
* @private
|
||||
*/
|
||||
private _validatePropertyOrAttributeName(
|
||||
propName: string, sourceSpan: ParseSourceSpan, isAttr: boolean): void {
|
||||
|
@ -202,7 +202,6 @@ function _buildFromEncodedParts(
|
||||
* $6 = <undefined> query without ?
|
||||
* $7 = Related fragment without #
|
||||
* </pre>
|
||||
* @type {!RegExp}
|
||||
* @internal
|
||||
*/
|
||||
const _splitRe = new RegExp(
|
||||
|
@ -1071,4 +1071,4 @@ function calcStaticDynamicQueryFlags(
|
||||
flags |= NodeFlags.DynamicQuery;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
}
|
||||
|
@ -462,6 +462,20 @@ describe('StaticReflector', () => {
|
||||
expect(annotations[0].providers[0].useValue.members[0]).toEqual('staticMethod');
|
||||
});
|
||||
|
||||
it('should be able to get metadata for a class calling a macro function', () => {
|
||||
const annotations = reflector.annotations(
|
||||
reflector.getStaticSymbol('/tmp/src/call-macro-function.ts', 'MyComponent'));
|
||||
expect(annotations.length).toBe(1);
|
||||
expect(annotations[0].providers.useValue).toBe(100);
|
||||
});
|
||||
|
||||
it('should be able to get metadata for a class calling a nested macro function', () => {
|
||||
const annotations = reflector.annotations(
|
||||
reflector.getStaticSymbol('/tmp/src/call-macro-function.ts', 'MyComponentNested'));
|
||||
expect(annotations.length).toBe(1);
|
||||
expect(annotations[0].providers.useValue.useValue).toBe(100);
|
||||
});
|
||||
|
||||
// #13605
|
||||
it('should not throw on unknown decorators', () => {
|
||||
const data = Object.create(DEFAULT_TEST_DATA);
|
||||
@ -1392,6 +1406,25 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
|
||||
static VALUE = 'Some string';
|
||||
}
|
||||
`,
|
||||
'/tmp/src/macro-function.ts': `
|
||||
export function v(value: any) {
|
||||
return { provide: 'a', useValue: value };
|
||||
}
|
||||
`,
|
||||
'/tmp/src/call-macro-function.ts': `
|
||||
import {Component} from '@angular/core';
|
||||
import {v} from './macro-function';
|
||||
|
||||
@Component({
|
||||
providers: v(100)
|
||||
})
|
||||
export class MyComponent { }
|
||||
|
||||
@Component({
|
||||
providers: v(v(100))
|
||||
})
|
||||
export class MyComponentNested { }
|
||||
`,
|
||||
'/tmp/src/static-field-reference.ts': `
|
||||
import {Component} from '@angular/core';
|
||||
import {MyModule} from './static-field';
|
||||
|
@ -405,7 +405,7 @@ export class MockMetadataBundlerHost implements MetadataBundlerHost {
|
||||
|
||||
constructor(private host: ts.CompilerHost) {}
|
||||
|
||||
getMetadataFor(moduleName: string): ModuleMetadata {
|
||||
getMetadataFor(moduleName: string): ModuleMetadata|undefined {
|
||||
const source = this.host.getSourceFile(moduleName + '.ts', ts.ScriptTarget.Latest);
|
||||
return this.collector.getMetadata(source);
|
||||
}
|
||||
|
@ -328,10 +328,7 @@ export function main() {
|
||||
|
||||
describe('normalizeExternalStylesheets', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureCompiler(
|
||||
{providers: [{provide: ResourceLoader, useClass: SpyResourceLoader}]});
|
||||
});
|
||||
beforeEach(() => { TestBed.configureCompiler({providers: [SpyResourceLoader.PROVIDE]}); });
|
||||
|
||||
it('should load an external stylesheet',
|
||||
inject(
|
||||
|
@ -26,6 +26,7 @@ export class I18nComponent {
|
||||
}
|
||||
|
||||
export class FrLocalization extends NgLocalization {
|
||||
public static PROVIDE = {provide: NgLocalization, useClass: FrLocalization, deps: []};
|
||||
getPluralCategory(value: number): string {
|
||||
switch (value) {
|
||||
case 0:
|
||||
@ -113,6 +114,7 @@ export const HTML = `
|
||||
|
||||
<div id="i18n-3"><p i18n><i>with placeholders</i></p></div>
|
||||
<div id="i18n-3b"><p i18n><i class="preserved-on-placeholders">with placeholders</i></p></div>
|
||||
<div id="i18n-3c"><div i18n><div>with <div>nested</div> placeholders</div></div></div>
|
||||
|
||||
<div>
|
||||
<p id="i18n-4" i18n-title title="on not translatable node"></p>
|
||||
|
@ -26,8 +26,8 @@ export function main() {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureCompiler({
|
||||
providers: [
|
||||
{provide: ResourceLoader, useClass: SpyResourceLoader},
|
||||
{provide: NgLocalization, useClass: FrLocalization},
|
||||
SpyResourceLoader.PROVIDE,
|
||||
FrLocalization.PROVIDE,
|
||||
{provide: TRANSLATIONS, useValue: XLIFF2_TOMERGE},
|
||||
{provide: TRANSLATIONS_FORMAT, useValue: 'xlf2'},
|
||||
]
|
||||
@ -80,6 +80,15 @@ const XLIFF2_TOMERGE = `
|
||||
<target><pc id="0" equivStart="START_ITALIC_TEXT" equivEnd="CLOSE_ITALIC_TEXT" type="fmt" dispStart="<i>" dispEnd="</i>">avec des espaces réservés</pc></target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="5415448997399451992">
|
||||
<notes>
|
||||
<note category="location">file.ts:11</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source><pc id="0" equivStart="START_TAG_DIV" equivEnd="CLOSE_TAG_DIV" type="other" dispStart="<div>" dispEnd="</div>">with <pc id="1" equivStart="START_TAG_DIV" equivEnd="CLOSE_TAG_DIV" type="other" dispStart="<div>" dispEnd="</div>">nested</pc> placeholders</pc></source>
|
||||
<target><pc id="0" equivStart="START_TAG_DIV" equivEnd="CLOSE_TAG_DIV" type="other" dispStart="<div>" dispEnd="</div>">avec <pc id="1" equivStart="START_TAG_DIV" equivEnd="CLOSE_TAG_DIV" type="other" dispStart="<div>" dispEnd="</div>">espaces réservés</pc> imbriqués</pc></target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="5525133077318024839">
|
||||
<segment>
|
||||
<source>on not translatable node</source>
|
||||
@ -242,9 +251,17 @@ const XLIFF2_EXTRACTED = `
|
||||
<source><pc id="0" equivStart="START_ITALIC_TEXT" equivEnd="CLOSE_ITALIC_TEXT" type="fmt" dispStart="<i>" dispEnd="</i>">with placeholders</pc></source>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="5415448997399451992">
|
||||
<notes>
|
||||
<note category="location">file.ts:11</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source><pc id="0" equivStart="START_TAG_DIV" equivEnd="CLOSE_TAG_DIV" type="other" dispStart="<div>" dispEnd="</div>">with <pc id="1" equivStart="START_TAG_DIV" equivEnd="CLOSE_TAG_DIV" type="other" dispStart="<div>" dispEnd="</div>">nested</pc> placeholders</pc></source>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="5525133077318024839">
|
||||
<notes>
|
||||
<note category="location">file.ts:13</note>
|
||||
<note category="location">file.ts:14</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source>on not translatable node</source>
|
||||
@ -252,7 +269,7 @@ const XLIFF2_EXTRACTED = `
|
||||
</unit>
|
||||
<unit id="8670732454866344690">
|
||||
<notes>
|
||||
<note category="location">file.ts:14</note>
|
||||
<note category="location">file.ts:15</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source>on translatable node</source>
|
||||
@ -260,8 +277,8 @@ const XLIFF2_EXTRACTED = `
|
||||
</unit>
|
||||
<unit id="4593805537723189714">
|
||||
<notes>
|
||||
<note category="location">file.ts:19</note>
|
||||
<note category="location">file.ts:36</note>
|
||||
<note category="location">file.ts:20</note>
|
||||
<note category="location">file.ts:37</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<pc id="0" equivStart="START_BOLD_TEXT" equivEnd="CLOSE_BOLD_TEXT" type="fmt" dispStart="<b>" dispEnd="</b>">many</pc>} }</source>
|
||||
@ -269,7 +286,7 @@ const XLIFF2_EXTRACTED = `
|
||||
</unit>
|
||||
<unit id="4360321700965841752">
|
||||
<notes>
|
||||
<note category="location">file.ts:21,23</note>
|
||||
<note category="location">file.ts:22,24</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source>
|
||||
@ -279,7 +296,7 @@ const XLIFF2_EXTRACTED = `
|
||||
</unit>
|
||||
<unit id="5460933846928880074">
|
||||
<notes>
|
||||
<note category="location">file.ts:22</note>
|
||||
<note category="location">file.ts:23</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source>{VAR_SELECT, select, 0 {other} m {male} f {female} }</source>
|
||||
@ -287,7 +304,7 @@ const XLIFF2_EXTRACTED = `
|
||||
</unit>
|
||||
<unit id="1746565782635215">
|
||||
<notes>
|
||||
<note category="location">file.ts:24,26</note>
|
||||
<note category="location">file.ts:25,27</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source>
|
||||
@ -297,7 +314,7 @@ const XLIFF2_EXTRACTED = `
|
||||
</unit>
|
||||
<unit id="5868084092545682515">
|
||||
<notes>
|
||||
<note category="location">file.ts:25</note>
|
||||
<note category="location">file.ts:26</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source>{VAR_SELECT, select, m {male} f {female} }</source>
|
||||
@ -305,7 +322,7 @@ const XLIFF2_EXTRACTED = `
|
||||
</unit>
|
||||
<unit id="4851788426695310455">
|
||||
<notes>
|
||||
<note category="location">file.ts:28</note>
|
||||
<note category="location">file.ts:29</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source><ph id="0" equiv="INTERPOLATION" disp="{{ "count = " + count }}"/></source>
|
||||
@ -313,7 +330,7 @@ const XLIFF2_EXTRACTED = `
|
||||
</unit>
|
||||
<unit id="9013357158046221374">
|
||||
<notes>
|
||||
<note category="location">file.ts:29</note>
|
||||
<note category="location">file.ts:30</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source>sex = <ph id="0" equiv="INTERPOLATION" disp="{{ sex }}"/></source>
|
||||
@ -321,7 +338,7 @@ const XLIFF2_EXTRACTED = `
|
||||
</unit>
|
||||
<unit id="8324617391167353662">
|
||||
<notes>
|
||||
<note category="location">file.ts:30</note>
|
||||
<note category="location">file.ts:31</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source><ph id="0" equiv="CUSTOM_NAME" disp="{{ "custom name" //i18n(ph="CUSTOM_NAME") }}"/></source>
|
||||
@ -329,8 +346,8 @@ const XLIFF2_EXTRACTED = `
|
||||
</unit>
|
||||
<unit id="7685649297917455806">
|
||||
<notes>
|
||||
<note category="location">file.ts:35</note>
|
||||
<note category="location">file.ts:53</note>
|
||||
<note category="location">file.ts:36</note>
|
||||
<note category="location">file.ts:54</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source>in a translatable section</source>
|
||||
@ -338,7 +355,7 @@ const XLIFF2_EXTRACTED = `
|
||||
</unit>
|
||||
<unit id="2387287228265107305">
|
||||
<notes>
|
||||
<note category="location">file.ts:33,37</note>
|
||||
<note category="location">file.ts:34,38</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source>
|
||||
@ -350,7 +367,7 @@ const XLIFF2_EXTRACTED = `
|
||||
</unit>
|
||||
<unit id="1491627405349178954">
|
||||
<notes>
|
||||
<note category="location">file.ts:39</note>
|
||||
<note category="location">file.ts:40</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source>it <pc id="0" equivStart="START_BOLD_TEXT" equivEnd="CLOSE_BOLD_TEXT" type="fmt" dispStart="<b>" dispEnd="</b>">should</pc> work</source>
|
||||
@ -358,7 +375,7 @@ const XLIFF2_EXTRACTED = `
|
||||
</unit>
|
||||
<unit id="i18n16">
|
||||
<notes>
|
||||
<note category="location">file.ts:41</note>
|
||||
<note category="location">file.ts:42</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source>with an explicit ID</source>
|
||||
@ -366,7 +383,7 @@ const XLIFF2_EXTRACTED = `
|
||||
</unit>
|
||||
<unit id="i18n17">
|
||||
<notes>
|
||||
<note category="location">file.ts:42</note>
|
||||
<note category="location">file.ts:43</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<pc id="0" equivStart="START_BOLD_TEXT" equivEnd="CLOSE_BOLD_TEXT" type="fmt" dispStart="<b>" dispEnd="</b>">many</pc>} }</source>
|
||||
@ -375,7 +392,7 @@ const XLIFF2_EXTRACTED = `
|
||||
<unit id="4085484936881858615">
|
||||
<notes>
|
||||
<note category="description">desc</note>
|
||||
<note category="location">file.ts:45,51</note>
|
||||
<note category="location">file.ts:46,52</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source>{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <ph id="0" equiv="INTERPOLATION" disp="{{response.getItemsList().length}}"/> results} }</source>
|
||||
@ -383,7 +400,7 @@ const XLIFF2_EXTRACTED = `
|
||||
</unit>
|
||||
<unit id="4035252431381981115">
|
||||
<notes>
|
||||
<note category="location">file.ts:53</note>
|
||||
<note category="location">file.ts:54</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source>foo<pc id="0" equivStart="START_LINK" equivEnd="CLOSE_LINK" type="link" dispStart="<a>" dispEnd="</a>">bar</pc></source>
|
||||
@ -391,7 +408,7 @@ const XLIFF2_EXTRACTED = `
|
||||
</unit>
|
||||
<unit id="5339604010413301604">
|
||||
<notes>
|
||||
<note category="location">file.ts:55</note>
|
||||
<note category="location">file.ts:56</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source><ph id="0" equiv="MAP NAME" disp="{{ 'test' //i18n(ph="map name") }}"/></source>
|
||||
|
@ -26,8 +26,8 @@ export function main() {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureCompiler({
|
||||
providers: [
|
||||
{provide: ResourceLoader, useClass: SpyResourceLoader},
|
||||
{provide: NgLocalization, useClass: FrLocalization},
|
||||
SpyResourceLoader.PROVIDE,
|
||||
FrLocalization.PROVIDE,
|
||||
{provide: TRANSLATIONS, useValue: XLIFF_TOMERGE},
|
||||
{provide: TRANSLATIONS_FORMAT, useValue: 'xliff'},
|
||||
]
|
||||
@ -70,9 +70,17 @@ const XLIFF_TOMERGE = `
|
||||
<note priority="1" from="meaning">different meaning</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="34fec9cc62e28e8aa6ffb306fa8569ef0a8087fe" datatype="html">
|
||||
<source><x id="START_ITALIC_TEXT" ctype="x-i"/>with placeholders<x id="CLOSE_ITALIC_TEXT" ctype="x-i"/></source>
|
||||
<source><x id="START_ITALIC_TEXT" ctype="x-i" equiv-text="<i>"/>with placeholders<x id="CLOSE_ITALIC_TEXT" ctype="x-i" equiv-text="</i>"/></source>
|
||||
<target><x id="START_ITALIC_TEXT" ctype="x-i"/>avec des espaces réservés<x id="CLOSE_ITALIC_TEXT" ctype="x-i"/></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="651d7249d3a225037eb66f3433d98ad4a86f0a22" datatype="html">
|
||||
<source><x id="START_TAG_DIV" ctype="x-div"/>with <x id="START_TAG_DIV" ctype="x-div"/>nested<x id="CLOSE_TAG_DIV" ctype="x-div"/> placeholders<x id="CLOSE_TAG_DIV" ctype="x-div"/></source>
|
||||
<target><x id="START_TAG_DIV" ctype="x-div"/>with <x id="START_TAG_DIV" ctype="x-div"/>nested<x id="CLOSE_TAG_DIV" ctype="x-div"/> placeholders<x id="CLOSE_TAG_DIV" ctype="x-div"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">11</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1fe4616cce80a57c7707bac1c97054aa8e244a67" datatype="html">
|
||||
<source>on not translatable node</source>
|
||||
<target>sur des balises non traductibles</target>
|
||||
@ -82,12 +90,12 @@ const XLIFF_TOMERGE = `
|
||||
<target>sur des balises traductibles</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="dc5536bb9e0e07291c185a0d306601a2ecd4813f" datatype="html">
|
||||
<source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<x id="START_BOLD_TEXT" ctype="x-b"/>many<x id="CLOSE_BOLD_TEXT" ctype="x-b"/>} }</source>
|
||||
<source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<x id="START_BOLD_TEXT" ctype="x-b" equiv-text="<b>"/>many<x id="CLOSE_BOLD_TEXT" ctype="x-b" equiv-text="</b>"/>} }</source>
|
||||
<target>{VAR_PLURAL, plural, =0 {zero} =1 {un} =2 {deux} other {<x id="START_BOLD_TEXT" ctype="x-b"/>beaucoup<x id="CLOSE_BOLD_TEXT" ctype="x-b"/>} }</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ba9106fca6e4e33a9b703a8c1927e2d1794dd9ff" datatype="html">
|
||||
<source>
|
||||
<x id="ICU"/>
|
||||
<x id="ICU" equiv-text="{sex, select, m {...} f {...}}"/>
|
||||
</source>
|
||||
<target><x id="ICU"/></target>
|
||||
</trans-unit>
|
||||
@ -97,7 +105,7 @@ const XLIFF_TOMERGE = `
|
||||
</trans-unit>
|
||||
<trans-unit id="078b7089573c5f66a2f78dce0adaa55e6715beb1" datatype="html">
|
||||
<source>
|
||||
<x id="ICU"/>
|
||||
<x id="ICU" equiv-text="{sexB, select, m {...} f {...}}"/>
|
||||
</source>
|
||||
<target><x id="ICU"/></target>
|
||||
</trans-unit>
|
||||
@ -106,15 +114,15 @@ const XLIFF_TOMERGE = `
|
||||
<target>{VAR_SELECT, select, m {homme} f {femme} }</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="d9879678f727b244bc7c7e20f22b63d98cb14890" datatype="html">
|
||||
<source><x id="INTERPOLATION"/></source>
|
||||
<source><x id="INTERPOLATION" equiv-text="{{ "count = " + count }}"/></source>
|
||||
<target><x id="INTERPOLATION"/></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="50dac33dc6fc0578884baac79d875785ed77c928" datatype="html">
|
||||
<source>sex = <x id="INTERPOLATION"/></source>
|
||||
<source>sex = <x id="INTERPOLATION" equiv-text="{{ sex }}"/></source>
|
||||
<target>sexe = <x id="INTERPOLATION"/></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="a46f833b1fe6ca49e8b97c18f4b7ea0b930c9383" datatype="html">
|
||||
<source><x id="CUSTOM_NAME"/></source>
|
||||
<source><x id="CUSTOM_NAME" equiv-text="{{ "custom name" //i18n(ph="CUSTOM_NAME") }}"/></source>
|
||||
<target><x id="CUSTOM_NAME"/></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="2ec983b4893bcd5b24af33bebe3ecba63868453c" datatype="html">
|
||||
@ -123,9 +131,9 @@ const XLIFF_TOMERGE = `
|
||||
</trans-unit>
|
||||
<trans-unit id="eee74a5be8a75881a4785905bd8302a71f7d9f75" datatype="html">
|
||||
<source>
|
||||
<x id="START_HEADING_LEVEL1" ctype="x-h1"/>Markers in html comments<x id="CLOSE_HEADING_LEVEL1" ctype="x-h1"/>
|
||||
<x id="START_TAG_DIV" ctype="x-div"/><x id="CLOSE_TAG_DIV" ctype="x-div"/>
|
||||
<x id="START_TAG_DIV_1" ctype="x-div"/><x id="ICU"/><x id="CLOSE_TAG_DIV" ctype="x-div"/>
|
||||
<x id="START_HEADING_LEVEL1" ctype="x-h1" equiv-text="<h1>"/>Markers in html comments<x id="CLOSE_HEADING_LEVEL1" ctype="x-h1" equiv-text="</h1>"/>
|
||||
<x id="START_TAG_DIV" ctype="x-div" equiv-text="<div>"/><x id="CLOSE_TAG_DIV" ctype="x-div" equiv-text="</div>"/>
|
||||
<x id="START_TAG_DIV_1" ctype="x-div" equiv-text="<div>"/><x id="ICU" equiv-text="{count, plural, =0 {...} =1 {...} =2 {...} other {...}}"/><x id="CLOSE_TAG_DIV" ctype="x-div" equiv-text="</div>"/>
|
||||
</source>
|
||||
<target>
|
||||
<x id="START_HEADING_LEVEL1" ctype="x-h1"/>Balises dans les commentaires html<x id="CLOSE_HEADING_LEVEL1" ctype="x-h1"/>
|
||||
@ -134,7 +142,7 @@ const XLIFF_TOMERGE = `
|
||||
</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="93a30c67d4e6c9b37aecfe2ac0f2b5d366d7b520" datatype="html">
|
||||
<source>it <x id="START_BOLD_TEXT" ctype="x-b"/>should<x id="CLOSE_BOLD_TEXT" ctype="x-b"/> work</source>
|
||||
<source>it <x id="START_BOLD_TEXT" ctype="x-b" equiv-text="<b>"/>should<x id="CLOSE_BOLD_TEXT" ctype="x-b" equiv-text="</b>"/> work</source>
|
||||
<target>ca <x id="START_BOLD_TEXT" ctype="x-b"/>devrait<x id="CLOSE_BOLD_TEXT" ctype="x-b"/> marcher</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="i18n16" datatype="html">
|
||||
@ -142,20 +150,20 @@ const XLIFF_TOMERGE = `
|
||||
<target>avec un ID explicite</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="i18n17" datatype="html">
|
||||
<source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<x id="START_BOLD_TEXT" ctype="x-b"/>many<x id="CLOSE_BOLD_TEXT" ctype="x-b"/>} }</source>
|
||||
<source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<x id="START_BOLD_TEXT" ctype="x-b" equiv-text="<b>"/>many<x id="CLOSE_BOLD_TEXT" ctype="x-b" equiv-text="</b>"/>} }</source>
|
||||
<target>{VAR_PLURAL, plural, =0 {zero} =1 {un} =2 {deux} other {<x id="START_BOLD_TEXT" ctype="x-b"/>beaucoup<x id="CLOSE_BOLD_TEXT" ctype="x-b"/>} }</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="2370d995bdcc1e7496baa32df20654aff65c2d10" datatype="html">
|
||||
<source>{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <x id="INTERPOLATION"/> results} }</source>
|
||||
<source>{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <x id="INTERPOLATION" equiv-text="{{response.getItemsList().length}}"/> results} }</source>
|
||||
<target>{VAR_PLURAL, plural, =0 {Pas de réponse} =1 {une réponse} other {Found <x id="INTERPOLATION"/> réponse} }</target>
|
||||
<note priority="1" from="description">desc</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="296ab5eab8d370822488c152586db3a5875ee1a2" datatype="html">
|
||||
<source>foo<x id="START_LINK" ctype="x-a"/>bar<x id="CLOSE_LINK" ctype="x-a"/></source>
|
||||
<source>foo<x id="START_LINK" ctype="x-a" equiv-text="<a>"/>bar<x id="CLOSE_LINK" ctype="x-a" equiv-text="</a>"/></source>
|
||||
<target>FOO<x id="START_LINK" ctype="x-a"/>BAR<x id="CLOSE_LINK" ctype="x-a"/></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="2e013b311caa0916478941a985887e091d8288b6" datatype="html">
|
||||
<source><x id="MAP NAME"/></source>
|
||||
<source><x id="MAP NAME" equiv-text="{{ 'test' //i18n(ph="map name") }}"/></source>
|
||||
<target><x id="MAP NAME"/></target>
|
||||
</trans-unit>`;
|
||||
|
||||
@ -183,7 +191,7 @@ const XLIFF_EXTRACTED = `
|
||||
<note priority="1" from="meaning">different meaning</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="34fec9cc62e28e8aa6ffb306fa8569ef0a8087fe" datatype="html">
|
||||
<source><x id="START_ITALIC_TEXT" ctype="x-i"/>with placeholders<x id="CLOSE_ITALIC_TEXT" ctype="x-i"/></source>
|
||||
<source><x id="START_ITALIC_TEXT" ctype="x-i" equiv-text="<i>"/>with placeholders<x id="CLOSE_ITALIC_TEXT" ctype="x-i" equiv-text="</i>"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">9</context>
|
||||
@ -193,146 +201,153 @@ const XLIFF_EXTRACTED = `
|
||||
<context context-type="linenumber">10</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="651d7249d3a225037eb66f3433d98ad4a86f0a22" datatype="html">
|
||||
<source><x id="START_TAG_DIV" ctype="x-div" equiv-text="<div>"/>with <x id="START_TAG_DIV" ctype="x-div" equiv-text="<div>"/>nested<x id="CLOSE_TAG_DIV" ctype="x-div" equiv-text="</div>"/> placeholders<x id="CLOSE_TAG_DIV" ctype="x-div" equiv-text="</div>"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">11</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1fe4616cce80a57c7707bac1c97054aa8e244a67" datatype="html">
|
||||
<source>on not translatable node</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
<context context-type="linenumber">14</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="67162b5af5f15fd0eb6480c88688dafdf952b93a" datatype="html">
|
||||
<source>on translatable node</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">14</context>
|
||||
<context context-type="linenumber">15</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="dc5536bb9e0e07291c185a0d306601a2ecd4813f" datatype="html">
|
||||
<source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<x id="START_BOLD_TEXT" ctype="x-b"/>many<x id="CLOSE_BOLD_TEXT" ctype="x-b"/>} }</source>
|
||||
<source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<x id="START_BOLD_TEXT" ctype="x-b" equiv-text="<b>"/>many<x id="CLOSE_BOLD_TEXT" ctype="x-b" equiv-text="</b>"/>} }</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">19</context>
|
||||
<context context-type="linenumber">20</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">36</context>
|
||||
<context context-type="linenumber">37</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ba9106fca6e4e33a9b703a8c1927e2d1794dd9ff" datatype="html">
|
||||
<source>
|
||||
<x id="ICU"/>
|
||||
<x id="ICU" equiv-text="{sex, select, 0 {...} m {...} f {...}}"/>
|
||||
</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">21</context>
|
||||
<context context-type="linenumber">22</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="7b7916d063ebaafcd2e9dcdf697b5e184fcb8099" datatype="html">
|
||||
<source>{VAR_SELECT, select, 0 {other} m {male} f {female} }</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">22</context>
|
||||
<context context-type="linenumber">23</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="078b7089573c5f66a2f78dce0adaa55e6715beb1" datatype="html">
|
||||
<source>
|
||||
<x id="ICU"/>
|
||||
<x id="ICU" equiv-text="{sexB, select, m {...} f {...}}"/>
|
||||
</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">24</context>
|
||||
<context context-type="linenumber">25</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="a25cf2e21a299f30be1392e731163825233edc61" datatype="html">
|
||||
<source>{VAR_SELECT, select, m {male} f {female} }</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">25</context>
|
||||
<context context-type="linenumber">26</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="d9879678f727b244bc7c7e20f22b63d98cb14890" datatype="html">
|
||||
<source><x id="INTERPOLATION"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">28</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="50dac33dc6fc0578884baac79d875785ed77c928" datatype="html">
|
||||
<source>sex = <x id="INTERPOLATION"/></source>
|
||||
<source><x id="INTERPOLATION" equiv-text="{{ "count = " + count }}"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">29</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="a46f833b1fe6ca49e8b97c18f4b7ea0b930c9383" datatype="html">
|
||||
<source><x id="CUSTOM_NAME"/></source>
|
||||
<trans-unit id="50dac33dc6fc0578884baac79d875785ed77c928" datatype="html">
|
||||
<source>sex = <x id="INTERPOLATION" equiv-text="{{ sex }}"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">30</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="a46f833b1fe6ca49e8b97c18f4b7ea0b930c9383" datatype="html">
|
||||
<source><x id="CUSTOM_NAME" equiv-text="{{ "custom name" //i18n(ph="CUSTOM_NAME") }}"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">31</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2ec983b4893bcd5b24af33bebe3ecba63868453c" datatype="html">
|
||||
<source>in a translatable section</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">35</context>
|
||||
<context context-type="linenumber">36</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">53</context>
|
||||
<context context-type="linenumber">54</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="eee74a5be8a75881a4785905bd8302a71f7d9f75" datatype="html">
|
||||
<source>
|
||||
<x id="START_HEADING_LEVEL1" ctype="x-h1"/>Markers in html comments<x id="CLOSE_HEADING_LEVEL1" ctype="x-h1"/>
|
||||
<x id="START_TAG_DIV" ctype="x-div"/><x id="CLOSE_TAG_DIV" ctype="x-div"/>
|
||||
<x id="START_TAG_DIV_1" ctype="x-div"/><x id="ICU"/><x id="CLOSE_TAG_DIV" ctype="x-div"/>
|
||||
<x id="START_HEADING_LEVEL1" ctype="x-h1" equiv-text="<h1>"/>Markers in html comments<x id="CLOSE_HEADING_LEVEL1" ctype="x-h1" equiv-text="</h1>"/>
|
||||
<x id="START_TAG_DIV" ctype="x-div" equiv-text="<div>"/><x id="CLOSE_TAG_DIV" ctype="x-div" equiv-text="</div>"/>
|
||||
<x id="START_TAG_DIV_1" ctype="x-div" equiv-text="<div>"/><x id="ICU" equiv-text="{count, plural, =0 {...} =1 {...} =2 {...} other {...}}"/><x id="CLOSE_TAG_DIV" ctype="x-div" equiv-text="</div>"/>
|
||||
</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">33</context>
|
||||
<context context-type="linenumber">34</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="93a30c67d4e6c9b37aecfe2ac0f2b5d366d7b520" datatype="html">
|
||||
<source>it <x id="START_BOLD_TEXT" ctype="x-b"/>should<x id="CLOSE_BOLD_TEXT" ctype="x-b"/> work</source>
|
||||
<source>it <x id="START_BOLD_TEXT" ctype="x-b" equiv-text="<b>"/>should<x id="CLOSE_BOLD_TEXT" ctype="x-b" equiv-text="</b>"/> work</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">39</context>
|
||||
<context context-type="linenumber">40</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="i18n16" datatype="html">
|
||||
<source>with an explicit ID</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">41</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="i18n17" datatype="html">
|
||||
<source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<x id="START_BOLD_TEXT" ctype="x-b"/>many<x id="CLOSE_BOLD_TEXT" ctype="x-b"/>} }</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">42</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2370d995bdcc1e7496baa32df20654aff65c2d10" datatype="html">
|
||||
<source>{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <x id="INTERPOLATION"/> results} }</source>
|
||||
<trans-unit id="i18n17" datatype="html">
|
||||
<source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<x id="START_BOLD_TEXT" ctype="x-b" equiv-text="<b>"/>many<x id="CLOSE_BOLD_TEXT" ctype="x-b" equiv-text="</b>"/>} }</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">45</context>
|
||||
<context context-type="linenumber">43</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2370d995bdcc1e7496baa32df20654aff65c2d10" datatype="html">
|
||||
<source>{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <x id="INTERPOLATION" equiv-text="{{response.getItemsList().length}}"/> results} }</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">46</context>
|
||||
</context-group>
|
||||
<note priority="1" from="description">desc</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="296ab5eab8d370822488c152586db3a5875ee1a2" datatype="html">
|
||||
<source>foo<x id="START_LINK" ctype="x-a"/>bar<x id="CLOSE_LINK" ctype="x-a"/></source>
|
||||
<source>foo<x id="START_LINK" ctype="x-a" equiv-text="<a>"/>bar<x id="CLOSE_LINK" ctype="x-a" equiv-text="</a>"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">53</context>
|
||||
<context context-type="linenumber">54</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2e013b311caa0916478941a985887e091d8288b6" datatype="html">
|
||||
<source><x id="MAP NAME"/></source>
|
||||
<source><x id="MAP NAME" equiv-text="{{ 'test' //i18n(ph="map name") }}"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">55</context>
|
||||
<context context-type="linenumber">56</context>
|
||||
</context-group>
|
||||
</trans-unit>`;
|
||||
|
@ -26,8 +26,8 @@ export function main() {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureCompiler({
|
||||
providers: [
|
||||
{provide: ResourceLoader, useClass: SpyResourceLoader},
|
||||
{provide: NgLocalization, useClass: FrLocalization},
|
||||
SpyResourceLoader.PROVIDE,
|
||||
FrLocalization.PROVIDE,
|
||||
{provide: TRANSLATIONS, useValue: XTB},
|
||||
{provide: TRANSLATIONS_FORMAT, useValue: 'xtb'},
|
||||
]
|
||||
@ -61,6 +61,7 @@ const XTB = `
|
||||
<translation id="3707494640264351337">imbriqué</translation>
|
||||
<translation id="5539162898278769904">imbriqué</translation>
|
||||
<translation id="3780349238193953556"><ph name="START_ITALIC_TEXT"/>avec des espaces réservés<ph name="CLOSE_ITALIC_TEXT"/></translation>
|
||||
<translation id="5415448997399451992"><ph name="START_TAG_DIV"><ex><div></ex></ph>avec <ph name="START_TAG_DIV"><ex><div></ex></ph>des espaces réservés<ph name="CLOSE_TAG_DIV"><ex></div></ex></ph> imbriqués<ph name="CLOSE_TAG_DIV"><ex></div></ex></ph></translation>
|
||||
<translation id="5525133077318024839">sur des balises non traductibles</translation>
|
||||
<translation id="8670732454866344690">sur des balises traductibles</translation>
|
||||
<translation id="4593805537723189714">{VAR_PLURAL, plural, =0 {zero} =1 {un} =2 {deux} other {<ph name="START_BOLD_TEXT"/>beaucoup<ph name="CLOSE_BOLD_TEXT"/>}}</translation>
|
||||
@ -90,29 +91,30 @@ const XMB = `<msg id="615790887472569365"><source>file.ts:3</source>i18n attribu
|
||||
<msg id="3707494640264351337"><source>file.ts:5</source>nested</msg>
|
||||
<msg id="5539162898278769904" meaning="different meaning"><source>file.ts:7</source>nested</msg>
|
||||
<msg id="3780349238193953556"><source>file.ts:9</source><source>file.ts:10</source><ph name="START_ITALIC_TEXT"><ex><i></ex></ph>with placeholders<ph name="CLOSE_ITALIC_TEXT"><ex></i></ex></ph></msg>
|
||||
<msg id="5525133077318024839"><source>file.ts:13</source>on not translatable node</msg>
|
||||
<msg id="8670732454866344690"><source>file.ts:14</source>on translatable node</msg>
|
||||
<msg id="4593805537723189714"><source>file.ts:19</source><source>file.ts:36</source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<ph name="START_BOLD_TEXT"><ex><b></ex></ph>many<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph>} }</msg>
|
||||
<msg id="4360321700965841752"><source>file.ts:21,23</source>
|
||||
<ph name="ICU"><ex>ICU</ex></ph>
|
||||
<msg id="5415448997399451992"><source>file.ts:11</source><ph name="START_TAG_DIV"><ex><div></ex></ph>with <ph name="START_TAG_DIV"><ex><div></ex></ph>nested<ph name="CLOSE_TAG_DIV"><ex></div></ex></ph> placeholders<ph name="CLOSE_TAG_DIV"><ex></div></ex></ph></msg>
|
||||
<msg id="5525133077318024839"><source>file.ts:14</source>on not translatable node</msg>
|
||||
<msg id="8670732454866344690"><source>file.ts:15</source>on translatable node</msg>
|
||||
<msg id="4593805537723189714"><source>file.ts:20</source><source>file.ts:37</source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<ph name="START_BOLD_TEXT"><ex><b></ex></ph>many<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph>} }</msg>
|
||||
<msg id="4360321700965841752"><source>file.ts:22,24</source>
|
||||
<ph name="ICU"><ex>{sex, select, 0 {...} m {...} f {...}}</ex></ph>
|
||||
</msg>
|
||||
<msg id="5460933846928880074"><source>file.ts:22</source>{VAR_SELECT, select, 0 {other} m {male} f {female} }</msg>
|
||||
<msg id="1746565782635215"><source>file.ts:24,26</source>
|
||||
<ph name="ICU"><ex>ICU</ex></ph>
|
||||
<msg id="5460933846928880074"><source>file.ts:23</source>{VAR_SELECT, select, 0 {other} m {male} f {female} }</msg>
|
||||
<msg id="1746565782635215"><source>file.ts:25,27</source>
|
||||
<ph name="ICU"><ex>{sexB, select, m {...} f {...}}</ex></ph>
|
||||
</msg>
|
||||
<msg id="5868084092545682515"><source>file.ts:25</source>{VAR_SELECT, select, m {male} f {female} }</msg>
|
||||
<msg id="4851788426695310455"><source>file.ts:28</source><ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph></msg>
|
||||
<msg id="9013357158046221374"><source>file.ts:29</source>sex = <ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph></msg>
|
||||
<msg id="8324617391167353662"><source>file.ts:30</source><ph name="CUSTOM_NAME"><ex>CUSTOM_NAME</ex></ph></msg>
|
||||
<msg id="7685649297917455806"><source>file.ts:35</source><source>file.ts:53</source>in a translatable section</msg>
|
||||
<msg id="2387287228265107305"><source>file.ts:33,37</source>
|
||||
<msg id="5868084092545682515"><source>file.ts:26</source>{VAR_SELECT, select, m {male} f {female} }</msg>
|
||||
<msg id="4851788426695310455"><source>file.ts:29</source><ph name="INTERPOLATION"><ex>{{ "count = " + count }}</ex></ph></msg>
|
||||
<msg id="9013357158046221374"><source>file.ts:30</source>sex = <ph name="INTERPOLATION"><ex>{{ sex }}</ex></ph></msg>
|
||||
<msg id="8324617391167353662"><source>file.ts:31</source><ph name="CUSTOM_NAME"><ex>{{ "custom name" //i18n(ph="CUSTOM_NAME") }}</ex></ph></msg>
|
||||
<msg id="7685649297917455806"><source>file.ts:36</source><source>file.ts:54</source>in a translatable section</msg>
|
||||
<msg id="2387287228265107305"><source>file.ts:34,38</source>
|
||||
<ph name="START_HEADING_LEVEL1"><ex><h1></ex></ph>Markers in html comments<ph name="CLOSE_HEADING_LEVEL1"><ex></h1></ex></ph>
|
||||
<ph name="START_TAG_DIV"><ex><div></ex></ph><ph name="CLOSE_TAG_DIV"><ex></div></ex></ph>
|
||||
<ph name="START_TAG_DIV_1"><ex><div></ex></ph><ph name="ICU"><ex>ICU</ex></ph><ph name="CLOSE_TAG_DIV"><ex></div></ex></ph>
|
||||
<ph name="START_TAG_DIV_1"><ex><div></ex></ph><ph name="ICU"><ex>{count, plural, =0 {...} =1 {...} =2 {...} other {...}}</ex></ph><ph name="CLOSE_TAG_DIV"><ex></div></ex></ph>
|
||||
</msg>
|
||||
<msg id="1491627405349178954"><source>file.ts:39</source>it <ph name="START_BOLD_TEXT"><ex><b></ex></ph>should<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph> work</msg>
|
||||
<msg id="i18n16"><source>file.ts:41</source>with an explicit ID</msg>
|
||||
<msg id="i18n17"><source>file.ts:42</source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<ph name="START_BOLD_TEXT"><ex><b></ex></ph>many<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph>} }</msg>
|
||||
<msg id="4085484936881858615" desc="desc"><source>file.ts:45,51</source>{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph> results} }</msg>
|
||||
<msg id="4035252431381981115"><source>file.ts:53</source>foo<ph name="START_LINK"><ex><a></ex></ph>bar<ph name="CLOSE_LINK"><ex></a></ex></ph></msg>
|
||||
<msg id="5339604010413301604"><source>file.ts:55</source><ph name="MAP_NAME"><ex>MAP_NAME</ex></ph></msg>`;
|
||||
<msg id="1491627405349178954"><source>file.ts:40</source>it <ph name="START_BOLD_TEXT"><ex><b></ex></ph>should<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph> work</msg>
|
||||
<msg id="i18n16"><source>file.ts:42</source>with an explicit ID</msg>
|
||||
<msg id="i18n17"><source>file.ts:43</source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<ph name="START_BOLD_TEXT"><ex><b></ex></ph>many<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph>} }</msg>
|
||||
<msg id="4085484936881858615" desc="desc"><source>file.ts:46,52</source>{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <ph name="INTERPOLATION"><ex>{{response.getItemsList().length}}</ex></ph> results} }</msg>
|
||||
<msg id="4035252431381981115"><source>file.ts:54</source>foo<ph name="START_LINK"><ex><a></ex></ph>bar<ph name="CLOSE_LINK"><ex></a></ex></ph></msg>
|
||||
<msg id="5339604010413301604"><source>file.ts:56</source><ph name="MAP_NAME"><ex>{{ 'test' //i18n(ph="map name") }}</ex></ph></msg>`;
|
||||
|
@ -24,7 +24,7 @@ const HTML = `
|
||||
<p i18n="@@bar">foo</p>
|
||||
<p i18n="ph names"><br><img><div></div></p>
|
||||
<p i18n="@@baz">{ count, plural, =0 { { sex, select, other {<p>deeply nested</p>}} }}</p>
|
||||
<p i18n>{ count, plural, =0 { { sex, select, other {<p>deeply nested</p>}} }}</p>
|
||||
<p i18n>Test: { count, plural, =0 { { sex, select, other {<p>deeply nested</p>}} } =other {a lot}}</p>
|
||||
<p i18n>multi
|
||||
lines</p>
|
||||
`;
|
||||
@ -41,14 +41,14 @@ const WRITE_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ec1d033f2436133c14ab038286c4f5df4697484a" datatype="html">
|
||||
<source>translatable element <x id="START_BOLD_TEXT" ctype="x-b"/>with placeholders<x id="CLOSE_BOLD_TEXT" ctype="x-b"/> <x id="INTERPOLATION"/></source>
|
||||
<source>translatable element <x id="START_BOLD_TEXT" ctype="x-b" equiv-text="<b>"/>with placeholders<x id="CLOSE_BOLD_TEXT" ctype="x-b" equiv-text="</b>"/> <x id="INTERPOLATION" equiv-text="{{ interpolation}}"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">3</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="e2ccf3d131b15f54aa1fcf1314b1ca77c14bfcc2" datatype="html">
|
||||
<source>{VAR_PLURAL, plural, =0 {<x id="START_PARAGRAPH" ctype="x-p"/>test<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} }</source>
|
||||
<source>{VAR_PLURAL, plural, =0 {<x id="START_PARAGRAPH" ctype="x-p" equiv-text="<p>"/>test<x id="CLOSE_PARAGRAPH" ctype="x-p" equiv-text="</p>"/>} }</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">4</context>
|
||||
@ -84,7 +84,7 @@ const WRITE_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="d7fa2d59aaedcaa5309f13028c59af8c85b8c49d" datatype="html">
|
||||
<source><x id="LINE_BREAK" ctype="lb"/><x id="TAG_IMG" ctype="image"/><x id="START_TAG_DIV" ctype="x-div"/><x id="CLOSE_TAG_DIV" ctype="x-div"/></source>
|
||||
<source><x id="LINE_BREAK" ctype="lb" equiv-text="<br/>"/><x id="TAG_IMG" ctype="image" equiv-text="<img/>"/><x id="START_TAG_DIV" ctype="x-div" equiv-text="<div>"/><x id="CLOSE_TAG_DIV" ctype="x-div" equiv-text="</div>"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">9</context>
|
||||
@ -92,14 +92,21 @@ const WRITE_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<note priority="1" from="description">ph names</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="baz" datatype="html">
|
||||
<source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p"/>deeply nested<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} } } }</source>
|
||||
<source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p" equiv-text="<p>"/>deeply nested<x id="CLOSE_PARAGRAPH" ctype="x-p" equiv-text="</p>"/>} } } }</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">10</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="0e16a673a5a7a135c9f7b957ec2c5c6f6ee6e2c4" datatype="html">
|
||||
<source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p"/>deeply nested<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} } } }</source>
|
||||
<trans-unit id="52ffa620dcd76247a56d5331f34e73f340a43cdb" datatype="html">
|
||||
<source>Test: <x id="ICU" equiv-text="{ count, plural, =0 {...} =other {...}}"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">11</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1503afd0ccc20ff01d5e2266a9157b7b342ba494" datatype="html">
|
||||
<source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p" equiv-text="<p>"/>deeply nested<x id="CLOSE_PARAGRAPH" ctype="x-p" equiv-text="</p>"/>} } } =other {a lot} }</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">11</context>
|
||||
@ -192,9 +199,17 @@ const LOAD_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p"/>deeply nested<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} } } }</source>
|
||||
<target>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p"/>profondément imbriqué<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} } } }</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="0e16a673a5a7a135c9f7b957ec2c5c6f6ee6e2c4" datatype="html">
|
||||
<source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p"/>deeply nested<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} } } }</source>
|
||||
<target>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p"/>profondément imbriqué<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} } } }</target>
|
||||
<trans-unit id="52ffa620dcd76247a56d5331f34e73f340a43cdb" datatype="html">
|
||||
<source>Test: <x id="ICU" equiv-text="{ count, plural, =0 {...} =other {...}}"/></source>
|
||||
<target>Test: <x id="ICU" equiv-text="{ count, plural, =0 {...} =other {...}}"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">11</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1503afd0ccc20ff01d5e2266a9157b7b342ba494" datatype="html">
|
||||
<source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p"/>deeply nested<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} } } =other {a lot} }</source>
|
||||
<target>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p"/>profondément imbriqué<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} } } =other {beaucoup} }</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="fcfa109b0e152d4c217dbc02530be0bcb8123ad1" datatype="html">
|
||||
<source>multi
|
||||
@ -253,9 +268,10 @@ export function main(): void {
|
||||
'empty target': '',
|
||||
'baz':
|
||||
'{VAR_PLURAL, plural, =0 {[{VAR_SELECT, select, other {[<ph name="START_PARAGRAPH"/>, profondément imbriqué, <ph name="CLOSE_PARAGRAPH"/>]}}, ]}}',
|
||||
'0e16a673a5a7a135c9f7b957ec2c5c6f6ee6e2c4':
|
||||
'52ffa620dcd76247a56d5331f34e73f340a43cdb': 'Test: <ph name="ICU"/>',
|
||||
'1503afd0ccc20ff01d5e2266a9157b7b342ba494':
|
||||
'{VAR_PLURAL, plural, =0 {[{VAR_SELECT, select, other {[<ph' +
|
||||
' name="START_PARAGRAPH"/>, profondément imbriqué, <ph name="CLOSE_PARAGRAPH"/>]}}, ]}}',
|
||||
' name="START_PARAGRAPH"/>, profondément imbriqué, <ph name="CLOSE_PARAGRAPH"/>]}}, ]}, =other {[beaucoup]}}',
|
||||
'fcfa109b0e152d4c217dbc02530be0bcb8123ad1': `multi
|
||||
lignes`
|
||||
});
|
||||
|
@ -21,7 +21,7 @@ export function main(): void {
|
||||
<p i18n="m|d@@i">foo</p>
|
||||
<p i18n="@@bar">foo</p>
|
||||
<p i18n="@@baz">{ count, plural, =0 { { sex, select, other {<p>deeply nested</p>}} }}</p>
|
||||
<p i18n>{ count, plural, =0 { { sex, select, other {<p>deeply nested</p>}} }}</p>
|
||||
<p i18n>Test: { count, plural, =0 { { sex, select, other {<p>deeply nested</p>}} } =other {a lot}}</p>
|
||||
<p i18n>multi
|
||||
lines</p>`;
|
||||
|
||||
@ -48,13 +48,14 @@ lines</p>`;
|
||||
<!ELEMENT ex (#PCDATA)>
|
||||
]>
|
||||
<messagebundle>
|
||||
<msg id="7056919470098446707"><source>file.ts:3</source>translatable element <ph name="START_BOLD_TEXT"><ex><b></ex></ph>with placeholders<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph> <ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph></msg>
|
||||
<msg id="7056919470098446707"><source>file.ts:3</source>translatable element <ph name="START_BOLD_TEXT"><ex><b></ex></ph>with placeholders<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph> <ph name="INTERPOLATION"><ex>{{ interpolation}}</ex></ph></msg>
|
||||
<msg id="2981514368455622387"><source>file.ts:4</source>{VAR_PLURAL, plural, =0 {<ph name="START_PARAGRAPH"><ex><p></ex></ph>test<ph name="CLOSE_PARAGRAPH"><ex></p></ex></ph>} }</msg>
|
||||
<msg id="7999024498831672133" desc="d" meaning="m"><source>file.ts:5</source>foo</msg>
|
||||
<msg id="i" desc="d" meaning="m"><source>file.ts:6</source>foo</msg>
|
||||
<msg id="bar"><source>file.ts:7</source>foo</msg>
|
||||
<msg id="baz"><source>file.ts:8</source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<ph name="START_PARAGRAPH"><ex><p></ex></ph>deeply nested<ph name="CLOSE_PARAGRAPH"><ex></p></ex></ph>} } } }</msg>
|
||||
<msg id="2015957479576096115"><source>file.ts:9</source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<ph name="START_PARAGRAPH"><ex><p></ex></ph>deeply nested<ph name="CLOSE_PARAGRAPH"><ex></p></ex></ph>} } } }</msg>
|
||||
<msg id="6997386649824869937"><source>file.ts:9</source>Test: <ph name="ICU"><ex>{ count, plural, =0 {...} =other {...}}</ex></ph></msg>
|
||||
<msg id="5229984852258993423"><source>file.ts:9</source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<ph name="START_PARAGRAPH"><ex><p></ex></ph>deeply nested<ph name="CLOSE_PARAGRAPH"><ex></p></ex></ph>} } } =other {a lot} }</msg>
|
||||
<msg id="2340165783990709777"><source>file.ts:10,11</source>multi
|
||||
lines</msg>
|
||||
</messagebundle>
|
||||
|
@ -36,7 +36,7 @@ export function main() {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureCompiler(
|
||||
{providers: [{provide: ResourceLoader, useClass: StubResourceLoader}]});
|
||||
{providers: [{provide: ResourceLoader, useClass: StubResourceLoader, deps: []}]});
|
||||
});
|
||||
|
||||
it('should throw when using a templateUrl that has not been compiled before', async(() => {
|
||||
@ -68,7 +68,7 @@ export function main() {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureCompiler(
|
||||
{providers: [{provide: ResourceLoader, useClass: StubResourceLoader}]});
|
||||
{providers: [{provide: ResourceLoader, useClass: StubResourceLoader, deps: []}]});
|
||||
});
|
||||
|
||||
it('should allow to use templateUrl components that have been loaded before', async(() => {
|
||||
@ -88,10 +88,7 @@ export function main() {
|
||||
let dirResolver: MockDirectiveResolver;
|
||||
let injector: Injector;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureCompiler(
|
||||
{providers: [{provide: ResourceLoader, useClass: SpyResourceLoader}]});
|
||||
});
|
||||
beforeEach(() => { TestBed.configureCompiler({providers: [SpyResourceLoader.PROVIDE]}); });
|
||||
|
||||
beforeEach(fakeAsync(inject(
|
||||
[Compiler, ResourceLoader, DirectiveResolver, Injector],
|
||||
|
@ -11,5 +11,6 @@ import {ResourceLoader} from '@angular/compiler/src/resource_loader';
|
||||
import {SpyObject} from '@angular/core/testing/src/testing_internal';
|
||||
|
||||
export class SpyResourceLoader extends SpyObject {
|
||||
public static PROVIDE = {provide: ResourceLoader, useClass: SpyResourceLoader, deps: []};
|
||||
constructor() { super(ResourceLoader); }
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user