Compare commits

...

310 Commits
2.2.3 ... 2.4.4

Author SHA1 Message Date
84542d8ae7 docs(changelog): add changelog for 2.4.4 2017-01-18 18:35:54 -06:00
17cb3ec565 chore(release): cut the 2.4.4 release 2017-01-18 18:32:57 -06:00
015878afe6 fix(http): don't create a blob out of ArrayBuffer when type is application/octet-stream (#13992)
Closes #13973
2017-01-18 18:28:37 -06:00
2af58622c1 fix(router): enable loadChildren with function in aot (#13909)
Closes #11075
2017-01-18 18:28:02 -06:00
7ffd10541d refactor(core): remove an unused import in application_ref (#13901) 2017-01-18 18:27:52 -06:00
481b099d82 docs(CHANGELOG): added reference to closed issue in CHANGELOG for informational purposes (#13985) 2017-01-18 18:27:25 -06:00
49c4b0fa92 fix(router): routerLinkActive should not throw when not initialized (#13273)
Fixes #13270

PR Close #13273
2017-01-18 18:27:14 -06:00
b8b6b1d27a refactor(router): clean up RouterLinkActive (#13273)
PR Close #13273
2017-01-18 18:27:03 -06:00
892b5ba950 chore(tsc-wrapped): update tsickle to latest (#13471) 2017-01-18 18:26:37 -06:00
bd15110c7d feat(security): allow calc and gradient functions. (#13943)
PR Close #13943

Also includes support for # color notation in function arguments (common
in gradient functions).
2017-01-18 18:25:45 -06:00
2250082fd7 fix(upgrade): detect async downgrade component changes (#13812)
This commit effectively reverts 7e0f02f96e
as it was an invalid fix for #6385, that created a more significant
bug, which was that changes were not always being detected.

Angular 1 digests should be run inside the ngZone to ensure
that async changes are detected.

We don't know how to fix #6385 without breaking change detection
at this stage. That issue is triggered by async operations, such as
`setTimeout`, being triggered inside scope watcher functions.

One could argue that watcher functions should be pure and not do
work such as triggering async operations. It is possible that the
original use case could be supported by moving the debounce
logic into the watch listener function, which is only called if the
watched value actually changes.

Closes #10660, #12318, #12034

PR Close #13812
2017-01-18 18:21:29 -06:00
87316c52db test(upgrade): reorganise test layout (#13812) 2017-01-18 18:21:24 -06:00
606b76d9bb chore(compiler-cli): Move calculateEmitPath into CompilerHost (#13904)
This is so that it can be overriden in an environment specific CompilerHost(like within Google) to customize the output paths.

PR Close #13904
2017-01-18 18:21:09 -06:00
3d0b1b8184 fix(common): support numeric value as discrete cases for NgPlural (#13876)
PR Close #13876
2017-01-18 18:20:56 -06:00
261fd16780 fix(animations): fix internal jscompiler issue and AOT quoting (#13798)
CL #143630929
PR Close #13798
2017-01-18 18:20:47 -06:00
104cc42f6d docs(http): Spelling Fix #13867 2017-01-18 18:20:30 -06:00
a7d28044c5 docs(changelog): add changelog for 2.4.3 2017-01-11 13:38:23 -08:00
055bea2969 chore(release): cut v2.4.3 2017-01-11 13:38:16 -08:00
dad0d21b89 chore(owners): configure pullapprove.com 2017-01-11 11:35:31 -08:00
313683f6f3 fix(compiler-cli): avoid handling functions in loadChildren as lazy load routes paths
The change avoids the compiler CLI internal API from mismatching the following case as lazy loading

```
import { NonLazyLoadedModule } from './non-lazy-loaded/non-lazy-loaded.module';

export function getNonLazyLoadedModule() { return NonLazyLoadedModule; }

export const routes = [
{ path: '/some-path', loadChildren: getNonLazyLoadedModule }
];
```

The output of the check is later passed to `RouteDef.fromString()`, so, it makes sense to be only a string.

Fixes angular/angular-cli#3204
2017-01-11 11:35:23 -08:00
338be6d6a5 refactor(common): remove some facade usages 2017-01-11 11:34:03 -08:00
4b56f79328 refactor(test): <template>/<ng-container>/*-directives
- remove outer `<div>` in tests,
- use `<ng-container>` instead of `<template>` where possible,
- use *... instead of template (tag or attr) where possible.

Fixes #13816
2017-01-11 11:33:30 -08:00
d7f2a3c71b fix(i18n): translate attributes inside elements marked for translation 2017-01-10 17:15:42 -08:00
1c929ae244 docs(NgPlural): fix API docs
Fixes #13786
2017-01-10 16:51:52 -08:00
83d0ff6d13 refactor(Compiler): misc cleanup 2017-01-10 16:50:20 -08:00
d43e5dd44d chore(benchmarks): change var to let 2017-01-09 16:08:33 -08:00
61ba223c1a fix(router): throw an error when navigate to null/undefined path
Closes #10560
Fixes #13384
2017-01-09 18:55:31 -05:00
6164eb25f3 fix(compiler-cli): add support for more than 2 levels of nested lazy routes
This change adds Compiler CLI support for any level of nesting for lazy routes.

For example `{app-root}/lazy-loaded-module-1/lazy-loaded-module-2/lazy-loaded-module-3`

Where `lazy-loaded-module-3` is lazy loaded from `lazy-loaded-module-2`,
and `lazy-loaded-module-2` is lazy loaded from module `lazy-loaded-module-1`,
and `lazy-loaded-module-1` is lazy loaded from `AppModule`

Fixes angular/angular-cli#3663
2017-01-09 18:06:26 -05:00
5e9d3dba3a fix(compiler): avoid evaluating arguments to unknown decorators
Fixes #13605
2017-01-09 18:06:11 -05:00
16922655ca fix(Router): fix checking for object intersection 2017-01-09 18:06:03 -05:00
7dc12b93fe fix(Compiler): fix template binding parsing (*directive="-...")
fixes #13800
2017-01-09 16:50:14 -05:00
1c82b58185 fix(router): RouterLink mirrors input target as attribute
Closes #13837
2017-01-09 16:50:06 -05:00
d6c414c08f fix: correctly show error when karma fails to load 2017-01-09 16:49:01 -05:00
d25d1730c7 chore(tsc-wrapped): bump version number to 0.5.1 2017-01-06 12:46:07 -08:00
03b35d2e8f docs(changelog): add release notes for 2.4.2 2017-01-06 12:40:35 -08:00
722543739e chore(release): cut the 2.4.2 release 2017-01-06 12:37:50 -08:00
56b4296a09 fix(language-service): support TypeScript 2.1 (#13655)
@angular/language-service now supports using TypeScript 2.1 as the
the TypeScript host. TypeScript 2.1 is now also partially supported
in `ngc` but is not recommended as Tsickle does not yet support 2.1.
2017-01-06 11:00:12 -08:00
f1cde4339b fix(core): animations no longer silently exits if the element is not apart of the DOM (#13763) 2017-01-06 11:00:12 -08:00
b245b920a6 fix(core): animations should blend in all previously transitioned styles into next animation if interrupted (#13148) 2017-01-06 11:00:12 -08:00
f47a71689c refactor: remove unused imports 2017-01-06 11:00:11 -08:00
6be55cc214 fix(Common): allow null/undefined values for NgForTrackBy
Reverts a breaking change introduced in 2.4.1 by #13420
fixes #13641
2017-01-06 11:00:11 -08:00
504199cf5a docs(common): add an example how to bind multiple classes based on a single parameter (#13779)
Closes #13778
2017-01-06 11:00:11 -08:00
17c5fa9293 fix(forms): Validators.required properly validate arrays (#13362)
Closes #12274
2017-01-06 11:00:11 -08:00
5f49c3ed23 fix(common): do not override locale provided on bootstrap (#13654)
Closes #13607
2017-01-06 11:00:11 -08:00
ebba63057f docs(developer): add linting section and correct command to verify API changes 2017-01-06 11:00:11 -08:00
5058461af7 feat(benchmarks): add detectChanges test for ng2 tree benchmark 2017-01-06 11:00:11 -08:00
21f5f05893 fix(core): Remove reference to "Angular 2" in dev mode warning (#13751) 2017-01-06 11:00:11 -08:00
f2ee81fa7a Typo (#13698) 2017-01-06 11:00:11 -08:00
ae1029da35 docs(Http): fix and extend samples for testing/MockBackend (#13689)
Fix samples for MockBackend and MockBackend.connections that were outdated. Also extend central sample for MockBackend to ease getting started.
2017-01-06 11:00:10 -08:00
230e33f3f1 fix(compiler): don’t throw when using ANALYZE_FOR_ENTRY_COMPONENTS with user classes (#13679)
Fixed #13565
2017-01-05 21:21:33 -08:00
ec0ca01224 docs(Core): fix API docs for ContentChild and ViewChildren (#13656)
Move the documentations of the ContentChild and ViewChildren decorators
so that they appear correctly on angular.io.

Closes #13625
2017-01-05 21:21:22 -08:00
1cd73c7a79 fix(compiler): query <template> elements before their children. (#13677)
Fixes #13118
Closes #13167
2017-01-05 21:20:56 -08:00
9f6a647908 fix(router): update route snapshot before emit new values (#13558)
Closes #12912
2017-01-05 21:20:45 -08:00
29ffdfdffe fix(Compiler): allow "." in attribute selectors (#13653)
fixes #13645
2017-01-05 21:14:54 -08:00
5754ecc3e1 fix(router): fix lazy loaded module with wildcard route (#13649)
Closes #12955
2017-01-05 21:14:42 -08:00
dab15c79dd chore(tslint): update tslint to 4.x (#13603) 2017-01-05 21:14:28 -08:00
21942a88f0 fix(upgrade): fix/improve support for lifecycle hooks (#13020)
With the exception of `$onChanges()`, all lifecycle hooks in ng1 are called on
the controller, regardless if it is the binding destination or not (i.e.
regardless of the value of `bindToController`).

This change makes `upgrade` mimic that behavior when calling lifecycle hooks.

Additionally, calling the `$onInit()` hook has been moved before calling the
linking functions, which also mimics the ng1 behavior.
2017-01-05 21:14:14 -08:00
018865ee6b fix(router): routerLink support of null/undefined (#13380)
Closes #6971
2017-01-05 21:11:56 -08:00
f7234378b6 fix(common): add link to trackBy docs (#13634) 2017-01-05 21:11:34 -08:00
5f47583c94 fixed minor typo (#13626) 2017-01-05 21:06:13 -08:00
0e7f9f0bff fix(testing): improve misleading error message when don't call compileComponents (#13543)
Closes #11301
2017-01-05 21:05:39 -08:00
28a92b2bcd docs(changelog): add changelog for 2.4.1 2016-12-21 14:26:13 -08:00
48be539824 chore(release): cut the 2.4.1 release 2016-12-21 14:22:42 -08:00
d788c679b6 fix(animations): always recover from a failed animation step (#13604) 2016-12-21 14:17:45 -08:00
a38f14b39c fix(router): should reset location if a navigation by location is successful (#13545)
Closes #13491
2016-12-21 14:17:25 -08:00
6a5e46cedd fix(animations): always quote string map key values in AOT code (#13602) 2016-12-21 09:49:03 -08:00
6316e5df71 fix(compiler): ignore @import in comments (#13368)
* refactor(compiler): clean up style url resolver
* fix(compiler): ignore @import in css comments

Closes #12196
2016-12-21 09:49:03 -08:00
90fca7c879 Include bower instructions in DEVELOPER.md (#13591) 2016-12-21 09:49:03 -08:00
d871ae2dc6 refactor(platform-browser): resolver merge conflict for tslint (#13601) 2016-12-21 09:49:03 -08:00
44e84d87f9 fix(common): throw an error if trackBy is not a function (#13420)
* fix(common): throw an error if trackBy is not a function

Closes #13388

* refactor(platform-browser): disable no-console rule in DomAdapter
2016-12-21 09:49:03 -08:00
b9e979e0a5 fix(core): improve error message when component factory cannot be found (#13541)
Closes #12678
2016-12-21 09:49:03 -08:00
cb2aa41782 build: fix publish-build-artifacts branch detection (#13599) 2016-12-21 09:49:03 -08:00
189a7e3750 build: publish build artifacts to branches (#13529)
Fix #13126
2016-12-21 09:49:03 -08:00
6efdf84d3e docs(changelog): add changelog for 2.4.0 2016-12-19 17:42:17 -08:00
e61bfc8b24 chore(release): cut the 2.4.0 release 2016-12-19 17:19:25 -08:00
070f9d0644 refactor: formatting fixes 2016-12-19 17:18:33 -08:00
8d5da1e57a feat: update to rxjs@5.0.1 and unpin the rxjs peerDeps via ^5.0.1 (#13572)
Now that rxjs is stable and the rxjs team follows semver, we can update and unpin the dependency safely.

From now on the Angular application/library developers are in charge of controlling the rxjs version as long as it's newer than 5.0.1.

closes #13561
closes #13478
closes #13572
2016-12-19 17:09:41 -08:00
b6406191c7 build(npm): update angular version in shrinkwrap files 2016-12-19 17:09:41 -08:00
124face441 refactor(compiler-cli): support extracting the mesage bundle without writing a file (#13580) 2016-12-19 17:09:41 -08:00
de4ace77fe feat(compiler-cli): private i18n API for the CLI (#13536)
Also change the Extractor API to align with the Codegen API (internal APIs)
2016-12-19 17:09:41 -08:00
debb0c9798 fix(compiler-cli): produce metadata for .d.ts files without metadata (#13526)
Fixes #13307
Fixes #13473
Fixes #13521
2016-12-19 17:09:40 -08:00
9b87bb6d7f fix(compiler): do not lex }} when interpolation is disabled (#13531)
* doc(compiler): fix the ICU expander API docs

* test(compiler): add lexer and parser specs

* fix(compiler): do not lex `}}` when interpolation is disabled

fix #13525
2016-12-19 17:09:40 -08:00
71e88a8c3c refactor(core): fix typo (#13515)
Closes #13512
2016-12-19 17:09:40 -08:00
c26c24c544 fix(upgrade): fix registerForNg1Tests (#13522)
Fix an issue in `registerForNg1Tests`, where it passes a `null` as
`ng1Injector` to `_bootstrapDone`. This causes a "TypeError: Cannot
read property 'get' of null" to be thrown from `_bootstrapDone`.
2016-12-19 17:09:40 -08:00
3f178410c3 fix(i18n): add a default example to xmb placeholders (#13507)
Otherwise the TC would not be able to load the message
2016-12-19 17:09:40 -08:00
b36f4bc00d fix(animations): allow players to be destroyed before initialized (#13346)
Closes #13293
Closes #13346
2016-12-19 17:09:40 -08:00
355c537883 refactor(compiler): format update (#13506) 2016-12-19 17:09:39 -08:00
f277303ca3 refactor(compiler): don't print stack trace on template parse errors (#13390) 2016-12-19 17:09:39 -08:00
50afbe094f fix(build): use bash string comparison operator (#13502) 2016-12-19 17:09:39 -08:00
15ea758d01 feature(DEVELOPER.md): add easy way to publish personal snapshot builds (#13469) 2016-12-19 17:09:39 -08:00
1f0f429f2a refactor(compiler): store metadata of top level symbols also in summaries (#13289)
This allows a build using summaries to not need .metadata.json files at all
any more.

Part of #12787
2016-12-19 17:09:39 -08:00
dbb364e23a docs(changelog): minor updates to 2.3.1 changelog 2016-12-14 21:57:56 -08:00
540b1197a6 fix(form): fix merge errors 2016-12-14 18:22:03 -08:00
d30cc8461b docs(changelog): add changelog for 2.3.1 2016-12-14 18:14:34 -08:00
f27954e62c build: bump angular to 2.3.1 & tsc-wrapped to 0.5.0 2016-12-14 18:11:35 -08:00
69b52eb2b3 fix(compiler): fix merge error in compiler_host 2016-12-14 18:08:54 -08:00
b9b557cdb0 fix(compiler): update to metadata version 3 (#13464)
This change retracts support for metadata version 2.

The collector used to produce version 2 metadata was incomplete
and can cause the AOT compiler to fail to resolve symbols or
produce other spurious errors.

All libraries compiled and published with 2.3.0 ngc will need
to be recompiled and updated with this change.
2016-12-14 18:08:48 -08:00
a72a002a8d refactor: format & lint 2016-12-14 18:08:43 -08:00
a0437f8c9d chore(animations/aot): always export NoOpAnimationDriver (#13480) 2016-12-14 18:08:36 -08:00
1c279b3264 fix(compiler): fix simplify a reference without a name
closes #13470
2016-12-14 18:08:32 -08:00
cd03c77364 fix(tsc-wrapped): generate metadata for exports without module specifier
fixes #13327
2016-12-14 18:08:29 -08:00
f6ef7d6e5a fix(compiler): propagate exports when upgrading metadata to v2 2016-12-14 18:08:25 -08:00
6aeaca3fb4 fix(compiler): resolver should merge host bindings and listeners (#13474)
fixes #13327
2016-12-14 18:07:41 -08:00
af62050729 docs(upgrade): fix UpgradeAdapter examples
closes #12675
2016-12-14 18:02:26 -08:00
cb69656b56 docs(upgrade/upgrade_adapter): fix up references to AngularJS and Angular 2 2016-12-14 18:02:14 -08:00
2fc0560988 feat(upgrade): enable Angular 1 unit testing of upgrade module
- New method `UpgradeAdapter.registerForNg1Tests(modules)` declares the
  Angular 1 upgrade module and provides it to the `angular.mock.module()`
  helper.
  This prevents the need to bootstrap the entire hybrid for every test.

Closes #5462, #12675
2016-12-14 18:02:05 -08:00
86c50983d7 fix(upgrade): fix downgrade content projection and injector inheritance
- Full support for content projection in downgraded Angular 2
  components. In particular, this enables multi-slot projection and
  other features on <ng-content>.
- Correctly wire up hierarchical injectors for downgraded Angular 2
  components: downgraded components inherit the injector of the first
  other downgraded Angular 2 component they find up the DOM tree.

Closes #6629, #7727, #8729, #9643, #9649, #12675
2016-12-14 17:56:20 -08:00
21976446e0 refactor(upgrade/upgrade_adapter): use Deferred helper
Making Angular 1's `$compile` asynchronous by chaining injector promises
in linking functions can cause flickering views in applications.
2016-12-14 17:56:16 -08:00
998ce9ad7e refactor(upgrade/util): remove unused stringify() method 2016-12-14 17:56:11 -08:00
111523677c refactor(compiler/template_parser): export createElementCssSelector
This is needed in `ngUpgrade`.
2016-12-14 17:56:06 -08:00
2d74a224d0 refactor(upgrade): add missing Angular 1 type info 2016-12-14 17:55:56 -08:00
4d6ac9d414 fix(core): detectChanges() doesn't work on detached instance
Closes #13426
Closes #13472
2016-12-14 17:55:38 -08:00
6557bc34f6 fix(animations): throw errors and normalize offset beyond the range of [0,1]
Closes #13348
Closes #13440
2016-12-14 17:55:34 -08:00
e2622add07 perf(animations): always run the animation queue outside of zones
Related #12732
Closes #13440
2016-12-14 17:55:27 -08:00
ecfad467a1 fix(compiler): emit quoted object literal keys if the source is quoted
feat(tsc-wrapped): recored when to quote a object literal key

Collecting quoted literals is off by default as it introduces
a breaking change in the .metadata.json file. A follow-up commit
will address this.

Fixes #13249
Closes #13356
2016-12-14 17:55:22 -08:00
5918133784 Revert "fix(compiler): xmb <ph> tags should not self close (#13413)"
This reverts commit 4b3d135193.
closes #13463
2016-12-14 17:55:18 -08:00
700bce9ec1 Revert "test(i18n): fix a typo in the reference xmb (#13441)"
This reverts commit a8d237581d.
2016-12-14 17:55:14 -08:00
a64a35a8c1 refactor(facade): don't expect super() to return a new Error object in BaseError (#12600)
Related to #12575
2016-12-14 17:55:10 -08:00
b3dcff0cc1 fix(forms): ensure select[multiple] retains selections
If you bound an array to select[multiple] via ngModel and subsequently
changed the options to select from, the UI would drop any selections
made since by the user. This was due to
SelectMultipleControlValueAccessor not keeping a reference to the new
model arrays it generated when users interacted with the select control.
Update code to keep the reference.

Closes #12527
Closes #12654
2016-12-14 17:55:02 -08:00
124267c87a fix(forms): introduce checkbox required validator
Closes #11459
Closes #13364
2016-12-14 17:52:53 -08:00
547bfa92ef fix (forms): clear selected options when model is not an array (#12519)
When an invalid model value (eg empty string) was preset ngModel on
select[multiple] would throw an error, which is inconsistent with how it
works on other user input elements. Setting the model value to null or
undefined would also have no effect on what was already selected in the
UI. Fix this by clearing selected options when model set to null,
undefined or a type other than Array.

Closes #11926
2016-12-14 17:52:02 -08:00
d40bbf4d5c fix(core): properly destroy embedded Views attatched to ApplicationRef (#13459)
Fixes #13062
2016-12-14 17:51:56 -08:00
94b7031fe9 refactor: format & lint 2016-12-14 17:51:48 -08:00
df0bf1dd74 chore(internal API): introduce an internal API for ngtools. (#13415) 2016-12-14 17:51:40 -08:00
c8a9b70890 fix(compiler): generated CSS files suffixed with ngstyle. (#13353)
Mirrors factories which ends in `ngfactory`.

Closes #13141.
2016-12-14 17:50:29 -08:00
efa2d80df8 fix(compiler): make sure provider values with name property don’t break.
Fixes #13394
Closes #13445
2016-12-14 17:50:22 -08:00
a58e5efd09 test(i18n): fix a typo in the reference xmb (#13441) 2016-12-14 17:50:12 -08:00
86cf0ef892 refactor: remove intl from facades (#13404)
The existing intl.ts file is not a facade but
rather a set of utils used by i18n-related pipes only.
As such moving it back to common module so those utils
are not used accidently from other places.
2016-12-14 17:50:02 -08:00
5c568fab86 test(upgrade): fix failing test in browsers which do not support RAF
closes #13399
2016-12-14 17:49:52 -08:00
566104504c ci(browser providers): update browsers in SL and BS (#13431) 2016-12-14 17:49:37 -08:00
307d305b2d fix(compiler): narrow the span reported for invalid pipes
fixes #13326
closes #13411
2016-12-14 17:49:05 -08:00
0a7364feea fix(language-service): correctly type undefined
fixes #13412
closes #13414
2016-12-14 17:40:58 -08:00
4544b1d7a6 fix(compiler): xmb <ph> tags should not self close (#13413) 2016-12-14 17:39:51 -08:00
9e0e6b59d1 docs(core): update OnDestroy description (#13369)
Closes #11228
2016-12-14 17:39:45 -08:00
14dd2b367a fix(language-service): treat string unions as strings (#13406)
Fixes #13403
2016-12-14 17:39:36 -08:00
91eb8914dd build: update the package list of the symlinks scripts for Windows (#13408) 2016-12-14 17:39:30 -08:00
77823d721f refactor: format and lint code 2016-12-14 17:38:31 -08:00
2afe2d107f docs(Location): updating Location docs and adding example
closes #11500
2016-12-14 17:38:22 -08:00
17f40fb75f chore: Add @types/systemjs 2016-12-14 17:37:43 -08:00
98936fdf16 chore: convert hash_location_strategy example to a tested spec 2016-12-14 17:37:33 -08:00
7383e4a801 fix(forms): fix Validators.min/maxLength with FormArray (#13095)
Fixes #13089
2016-12-14 17:37:18 -08:00
65c9b5b6aa fix(http): create a copy of headers when merge options (#13365)
Closes #11980
2016-12-14 17:36:48 -08:00
5fab8710cb fix(dom_adapter): remove logError from logGroup (#12925) 2016-12-14 17:35:12 -08:00
f106a18b96 fix(http): check response body text against undefined (#13017) 2016-12-14 17:35:05 -08:00
8db184d349 fix(compiler): support dotted property binding
fixes angular/flex-layout#34
2016-12-14 17:31:08 -08:00
c18eb298eb test(Selector): add a test for dotted attribute names 2016-12-14 17:31:00 -08:00
3f4aa59cfa refactor(Compiler): cleanup 2016-12-14 17:30:50 -08:00
79728b4c41 fix(compiler): fix PR 13322 (#13331) 2016-12-14 17:30:40 -08:00
413167ab1b style: clang-format the code 2016-12-14 17:26:52 -08:00
203cc7e1f1 fix: Better instructions on running examples and their tests 2016-12-14 17:23:11 -08:00
b0cd514709 fix: Better error when directive not listed in NgModule.declarations 2016-12-14 17:22:58 -08:00
392c9ac214 fix(selector): SelectorMatcher match elements with :not selector (#12977) 2016-12-14 17:21:34 -08:00
a26e054857 fix(animations): always cleanup players after they have finished internally (#13334)
Closes #13333
Closes #13334
2016-12-14 17:21:23 -08:00
c0b001a6af refactor(router): misc refactoring (#13330) 2016-12-14 17:21:09 -08:00
c8c1f22f9c refactor(router): simplify regexp
closes #11373
closes #13329
2016-12-14 17:20:47 -08:00
e4d5a5f003 fix(router): add support for query params with mulitple values
closes #11373
2016-12-14 17:20:22 -08:00
03d9de33a1 Revert "fix(compiler): fix transpiled ES5 code (#13322)"
This reverts commit 4398056146.
2016-12-14 17:20:14 -08:00
a8a80cf523 doc: update triage owners for language service and router (#13325) 2016-12-14 17:19:50 -08:00
6c1d7908d5 fix(compiler): fix transpiled ES5 code (#13322)
fixes #13301

The inner class would transpile to a nested function declaration which is not
allowed in ES5.

See http://eslint.org/docs/rules/no-inner-declarations
2016-12-14 17:18:12 -08:00
9aab6d24eb build(tslint): enable no-inner-declarations (#13316) 2016-12-14 17:18:02 -08:00
5ee8155e4e fix(router): Use T type in Resolve interface (#13242) 2016-12-14 17:17:45 -08:00
21de0f239d docs(changelog): fix a typo (#13298) 2016-12-14 17:16:49 -08:00
13b41bd631 chore(release): update version of tsc-wrapped to 0.4.2 2016-12-07 13:54:22 -08:00
f3524af68f docs(changelog): add changelog for 2.3.0 2016-12-07 11:21:13 -08:00
0a56f4ea82 chore(release): cut the 2.3.0 release 2016-12-07 10:58:29 -08:00
cf52284ac3 docs(changelog): fix typo AplicationRef => ApplicationRef (#13284)
docs(changelog): fix typo AplicationRef => ApplicationRef
2016-12-07 10:06:40 -08:00
4a09c81724 fix(language-service): do not throw for invalid metadata (#13261)
Fixes #13255
2016-12-06 17:11:09 -08:00
16efb13dd1 fix: display framework version on bootstrapped component (#13252) 2016-12-06 16:21:07 -08:00
986abbe0b2 fix(http): set the default Accept header (#12989)
Fixes #6354
2016-12-06 16:20:43 -08:00
25c2141991 fix(language-service): remove incompletely used parameter from createLanguageServiceFromTypescript() (#13278)
Fixes #13277
2016-12-06 16:19:39 -08:00
2893c2c0a2 fix(router): validate nested routes (#13224)
Fixes #12827
2016-12-06 10:41:01 -08:00
393c1007a8 fix(tsc-wrapped): have UserError display the actual error 2016-12-06 10:40:38 -08:00
66b6fc010d refactor(common): unify export structure 2016-12-06 10:40:38 -08:00
f31c9470fa fix(compiler): short-circut expressions with an index (#13263)
Fixes #13254
2016-12-06 10:40:15 -08:00
4bd8f58552 FIX typo (#13267) 2016-12-06 10:40:01 -08:00
93556a5720 fix(language-service): avoid throwing for invalid class declarations (#13257)
Fixes #13253
2016-12-06 09:56:30 -08:00
5614c4ff0f fix(compiler): serialize any StaticSymbol correctly, not matter in which context. 2016-12-05 14:20:01 -08:00
c3065aac7a refactor(compiler): replace isStaticSymbol with instanceof StaticSymbol 2016-12-05 14:20:01 -08:00
c767df0e4e fix(router): throw a better error message when angular 1 is not bootstraped 2016-12-05 10:57:24 -08:00
25e5b2fdf0 fix(router): make setUpLocationChangeListener idempotent 2016-12-05 10:57:24 -08:00
307c4693dc refactor(router): code cleanup 2016-12-05 10:54:10 -08:00
349ad75de3 fix(router): fix replaceUrl on RouterLink directives 2016-12-05 10:54:10 -08:00
f562cbf86c fix(router): fix skipLocationChanges on RouterLink directives
fixes #13156
2016-12-05 10:54:10 -08:00
804943c9b1 refactor(router): cleanup RouterLink 2016-12-05 10:54:10 -08:00
dea59165de docs(developer): add anchor to source code formatting (#13199)
In CONTRIBUTING.md#rules has reference to DEVELOPER.md#clang-format
but anchor to #clang-format is not present
2016-12-05 10:27:20 -08:00
a1322873c8 fix(facade): cache original format string (#12764) 2016-12-05 10:26:59 -08:00
b8c839bd51 docs(cheatsheet): correct HostBinding decorator example (#13206) 2016-12-05 10:25:40 -08:00
d2e5198b93 docs(contributing): fix styleguide link and typos (#13198) 2016-12-05 10:25:20 -08:00
6cf7a1bf84 refactor(build): fix build location of compiler-cli esm module (#13212) 2016-12-02 15:19:52 -08:00
d46b8deeea fix(router): runs guards every time when unsuccessfully navigating to the same url over and over again (#13209) 2016-12-02 15:19:00 -08:00
bbb7a39414 fix(router): allow specifying a matcher wihtout specifying a path
fixes #12972
2016-12-02 15:03:59 -08:00
d7d8fab211 refactor(router): cleanup config 2016-12-02 15:03:59 -08:00
51b06924bd docs(core): correct HostListener typo (#13203) 2016-12-02 14:45:47 -08:00
aa4bd14b3f docs(changelog): add instructions for people that depend on the cache operator (#13191) 2016-12-02 14:43:05 -08:00
3ff6554cbc fix(language-service): update to use CompilerHost from compiler-cli (#13189) 2016-12-02 14:34:16 -08:00
dfd8140084 Fix format 2016-12-02 14:31:55 -08:00
6ea3ab7e14 Fix exit code. Give a specific type. Add test cases. 2016-12-02 14:31:55 -08:00
9761db5ac2 refactor(compiler): change ngc error handling
Do not print stack trace for user errors
Print stack trace for compiler internal errors
2016-12-02 14:31:55 -08:00
75d1617b63 fix(compiler): include the summaries of reexported modules / directives / pipes (#13196)
Only if these are not part of the sources.
2016-12-02 10:08:46 -08:00
614a35d539 feat(compiler): read and write .ngsummary.json files
When compiling libraries, this feature extracts the minimal information
from the directives/pipes/modules of the library into `.ngsummary.json` files,
so that applications that use this library only need to be recompiled
if one of the summary files change, but not on every change
of the libraries (e.g. one of the templates).

Only works if individual codegen for libraries is enabled,
see the `generateCodeForLibraries: false` option.

Closes #12787
2016-12-01 14:49:52 -08:00
9ab401f4d3 refactor(compiler): simplify NgModuleSymmaryMetadata
- merge `NgModuleInjectorSummary` and `NgModuleDirectiveSummary`
- remove `directiveLoaders` from the summary
2016-12-01 14:49:52 -08:00
82c81cd0d2 fix(common): make sure the plural category exists (#13169)
fixes #12379
2016-12-01 13:26:24 -08:00
12959f444c refactor(router):remove unused parameter pathIndex (#13180) 2016-12-01 13:25:53 -08:00
25a6da244c refactor(compiler-cli): refactor compiler host parameters (#13147) 2016-12-01 13:24:51 -08:00
5908b66ae9 docs(changelog): move 2.2.2 to its place in the chronological order 2016-12-01 10:21:23 -08:00
480ef20eb1 docs(changelog): add changelog for 2.2.2 2016-12-01 09:59:23 -08:00
c066281bad docs(changelog): add changelog for 2.3.0-rc.0 2016-11-30 15:42:56 -08:00
1b9493f725 chore(release): cut the 2.3.0-rc.0 release 2016-11-30 15:42:56 -08:00
ae26504e84 fix(core): update peer dep on zone.js to ^0.7.2 2016-11-30 15:42:56 -08:00
d420080b3b docs(changelog): add changelog for 2.2.4 2016-11-30 15:19:09 -08:00
2975d8933c fix(language-service): harden against partial normalization of directives 2016-11-30 14:55:56 -08:00
43c0e9a6bb fix(compiler): fix performance regression caused by 5b0f9e2
Fixes #13146
2016-11-30 14:55:56 -08:00
f275f36081 fix(version): take all of version string after patch version 2016-11-30 14:25:11 -08:00
e628b66cca feat(build): record angular version in the dom (#13164) 2016-11-30 13:52:08 -08:00
3e73bea3e7 refactor(compiler): convert metadata classes used in summaries into interfaces (#13123)
Part of #12787
2016-11-30 10:52:51 -08:00
42cf06fa12 feat(router): add support for custom route reuse strategies 2016-11-29 23:21:41 -08:00
c4bbafc291 feat: upgrade zone.js to v0.7.1 2016-11-29 17:24:00 -08:00
2d6a003dba feat: update RxJS peer dependency to 5.0.0-rc.4
Closes #13125

RxJS from beta-12 to rc.4, has removed the `cache`
operator. (See https://github.com/ReactiveX/rxjs/blob/master/CHANGELOG.md#breaking-changes-1)
If your application relies on it, then we suggest 
that you use the one from this gist:
https://gist.github.com/robwormald/19dea0c70a6e01aadced6731aed4f9f7
2016-11-29 16:27:33 -08:00
e45b7ffcd9 fix: shrinkwrap was out of date with packages.
Rerun shrinkwrap on a clean workspace
2016-11-29 16:27:33 -08:00
627282d2c8 fix(compiler): correctly evaluate references to static functions (#13133) 2016-11-29 12:02:50 -08:00
2f7492c986 refactor(compiler): remove unneeded fields from metadata
Removes `CompileIdentifierMetadata.name` / `.moduleUrl`,
as well as `CompileTypeMetadata.name / moduleUrl` and
`CompileFactoryMetadata.name / moduleUrl`.
2016-11-28 15:19:23 -08:00
2452cd14e0 refactor(compiler): drop old codegen tests that run inside of test.sh
These tests were hard to maintain and only yielded little value,
now that we have the full integration with TypeScript.
2016-11-28 15:19:23 -08:00
bc69c74be0 fix(tsc-wrapped): still emit version 1 metadata to allow to use new components in old setups 2016-11-28 15:18:57 -08:00
897555ca78 fix(tsc-wrapped): set correct version number 2016-11-28 15:18:56 -08:00
966bcbad5a fix(compiler-cli): pin the version of tsc-wrapped 2016-11-28 15:18:56 -08:00
94b8612e4e Fix(http): invalidStateError response body
Check on null value failed with last version of mozilla.
Check on undefined type instead.
2016-11-28 14:36:32 -08:00
b2b72190f8 fix(common): update DatePipe to allow closure compilation
Quote the date formats to prevent closure renaming.  These are specified as strings in templates using DatePipes and also need to be quoted here.
2016-11-28 14:36:12 -08:00
f5c8e0989d feat(core): properly support inheritance
## Inheritance Semantics:

Decorators:
1) list the decorators of the class and its parents in the ancestor first order
2) only use the last decorator of each kind (e.g. @Component / ...)

Constructor parameters:
If a class inherits from a parent class and does not declare
a constructor, it inherits the parent class constructor,
and with it the parameter metadata of that parent class.

Lifecycle hooks:
Follow the normal class inheritance model,
i.e. lifecycle hooks of parent classes will be called
even if the method is not overwritten in the child class.

## Example

E.g. the following is a valid use of inheritance and it will
also inherit all metadata:

```
@Directive({selector: 'someDir'})
class ParentDirective {
  constructor(someDep: SomeDep) {}

  ngOnInit() {}
}

class ChildDirective extends ParentDirective {}
```

Closes #11606
Closes #12892
2016-11-28 14:12:12 -08:00
4a09251921 doc(common): fix a typo in async pipe 2016-11-28 14:11:45 -08:00
36caaaa8e4 refactor(core): remove unused import
APP_ID  was removed after 2.2.x
2016-11-28 14:11:25 -08:00
808275a9d5 feat(core): expose destroy() method on ViewRef 2016-11-28 14:10:42 -08:00
be3784c957 revert: fix(animations): blend in all previously transitioned styles into next animation if interrupted
This reverts commit ef96763fa4.
2016-11-28 13:23:52 -08:00
555301ce3a docs(changelog): add changelog for 2.2.3
original SHA: 380377139b
2016-11-27 22:11:04 -08:00
7194fc2b9e fix(language-service): make link check pass 2016-11-23 16:21:06 -08:00
2a3ca7bfcf fix(compiler-cli): fix paths in source maps to be relative
The change looks bigger than it really is because I reordered the properties to match other tsconfigs we have.

The only real change is removal of sourceRoot property.

Fixes #13040
2016-11-23 15:48:24 -08:00
4cbf8ccf05 Keep console.log that are not called during compilation. 2016-11-23 15:47:02 -08:00
a6c4490fce Check if console.error is defined 2016-11-23 15:47:02 -08:00
2c02d34c05 refactor(lint): Don't allow console.log
Enable tslint check for `console.log` as a follow-up to
https://github.com/angular/angular/issues/13018
2016-11-23 15:47:01 -08:00
6c2d931744 chore: make test.sh work again
Previously, `test.sh` relied on calling `build.sh` first
2016-11-23 14:23:05 -08:00
86ffa884b7 fix(build): update versions of umd bundles (#13038)
Fixes #13037
2016-11-22 13:39:41 -08:00
3e548de99d Revert "fix(router): guards restor an incorrect url when used with skipLocationChange"
This reverts commit ad20d7d260.
2016-11-22 13:31:33 -08:00
909268036b test(upgrade): remove setTimeout from lifecycle hook tests (#13027)
* test(upgrade): remove unnecessary NO_ERRORS_SCHEMA

* test(upgrade): remove `setTimeout` from lifecycle hook tests

Closes #13019
2016-11-22 09:21:03 -08:00
519a324454 feat(language-service): add services to support editors (#12987) 2016-11-22 09:10:23 -08:00
ef96763fa4 fix(animations): blend in all previously transitioned styles into next animation if interrupted (#13014)
Closes #13013
Closes #13014
2016-11-21 15:46:59 -08:00
7dcca307d9 fix(closure): quote date pattern aliases (#13012)
Quota the pattern aliases to prevent closure renaming. These are quoted in DatePipe and also need to be quoted here.
2016-11-21 11:45:48 -08:00
491d5a22a9 refactor(compiler): allow control of StaticSymbol lifetime (#12986) 2016-11-18 16:58:14 -08:00
44572f114f refactor(ngUpgrade): Small cleanup with Testability API and resumeBootstrap (#12926)
* With non-static ngUpgrade apps, callbacks to `whenStable` were being invoked with the wrong
  context
* With non-static ngUpgrade apps, `resumeBootstrap` was being run outside the NgZone
* Remove redundent `whenStableContext` variable

Neither of the first two problems were actually causing bugs (as far as I know), but they *might*
have caused problems in the future.

Inspired by https://github.com/angular/angular/pull/12910, but for non-static apps.
2016-11-18 14:30:47 -08:00
1ef4696cb7 fix(upgrade): call ng1 lifecycle hooks (#12875) 2016-11-18 13:46:49 -08:00
07a986d330 fix(changelog): replace beta.1 with beta.0 (#12961) 2016-11-18 12:37:26 -08:00
59d2b4c831 refactor(compiler): further minor fixes 2016-11-18 10:04:14 -08:00
2a5bd2f345 refactor(compiler): Reintroduce ReflectorHost and move Extractor into @angular/compiler 2016-11-18 10:04:14 -08:00
3c06a5dc25 refactor(comiler): various cleanups 2016-11-18 10:04:14 -08:00
adeea5d86a refactor(compiler): renames
- `NgHost` to `CompilerHost`
- `AotCompilerHost.resolveFileToImport` to `AotCompilerHost.fileNameToModuleName`
- `AotCompilerHoset.resolveImportToFile` to `AotCompilerHost.moduleNameToFileName`
2016-11-18 10:04:14 -08:00
dddbb1c1cb refactor(tsc-wrapped): collect all exported functions and classes and bump metadata version from 1 to 2
This is needed to resolve symbols without `.d.ts` files.
This bumps the version of the metadata from 1 to 2.
This adds logic into `ng_host.ts` to automatically upgrade
version 1 to version 2 metadata by adding the exported symbols
from the `.d.ts` file.
2016-11-18 10:04:14 -08:00
bccf0e69dc fix(compiler): fix versions of @angular/tsc-wrapped 2016-11-18 10:04:14 -08:00
b15039d228 refactor(compiler): move symbol extraction to AotCompiler 2016-11-18 10:04:14 -08:00
2235048432 refactor(compiler): add createAotCompiler factory
Also adds 2 more methods to the `AotCompilerHost`:
- `loadResource`
- `resolveFileToImport`
2016-11-18 10:04:14 -08:00
484119e59f refactor(compiler): remove asset: urls
These urls were just relicts from Dart.
2016-11-18 10:04:14 -08:00
24099bdbd2 refactor(compiler): move findDeclaration into the StaticReflector
Previously, this was part of the `AotCompilerHost`.
The `AotCompilerHost` is now also greatly simplified.
2016-11-18 10:04:14 -08:00
912ca44979 refactor(compiler): move static_reflector into @angular/compiler and rename files
- `src/runtime_compiler.ts` -> `src/jit/compiler.ts`
- `src/compiler.ts` -> `src/jit/compiler_factory.ts`
- `src/offline_compiler` -> `src/aot/compiler.ts`

Part of #12867
2016-11-18 10:04:14 -08:00
664a6273e1 feature(tsc-wrapped): add option for closure compiler JSDoc annotations 2016-11-18 09:37:40 -08:00
c1a62e2154 feat(tools): allow disabling annotation lowering 2016-11-18 09:37:40 -08:00
aac37bedc0 chore(build): update package.json versions during build (#12957) 2016-11-18 09:24:57 -08:00
a3884db87c fix(ci): pin version of npm on CircleCI (#12954) 2016-11-17 17:27:51 -08:00
fc5ac1ebc4 fix(benchmarks): use sanitized style values (#12943) 2016-11-17 15:18:10 -08:00
ad20d7d260 fix(router): guards restor an incorrect url when used with skipLocationChange
Closes #12825
2016-11-17 14:10:59 -08:00
602522beb2 fix(router): support redirects to named outlets
Closes #12740, #9921
2016-11-17 14:10:59 -08:00
4e047302f2 chore(release): cut the 2.3.0-beta.0 realse and add change log 2016-11-17 11:59:03 -08:00
419a812f04 chore(release): cut angular 2.2.1 2016-11-17 11:51:25 -08:00
f340e1a414 fix(tools): fix error when running test.sh (#12927) 2016-11-16 13:35:31 -08:00
481c9b3258 refactor(compiler): allows synchronous retrieving of metadata (#12908)
Allows non-normalized metadata to be retrieved synchronously.

Related to #7482
2016-11-16 10:22:11 -08:00
8b2dfb2eca fix(core): support ngTemplateOutlet in production mode (#12921)
Fixes #12911
2016-11-16 10:00:18 -08:00
824ea8406c docs(upgrade/static): improve API docs with examples
Closes #12717
2016-11-16 09:18:17 -08:00
1f96a93f59 chore(public_api): remove Angular 1 types from upgrade/static API 2016-11-16 09:18:10 -08:00
009d545787 chore(examples): add upgrade/static example 2016-11-16 09:18:10 -08:00
53c25210a6 chore(examples): support upgrade/static examples 2016-11-16 09:18:10 -08:00
927aa69726 fix(router): add a banner file for the router (#12919) 2016-11-16 09:17:19 -08:00
ce89039036 fix(platform_browser): fix disableDebugTools() (#12918) 2016-11-16 09:16:40 -08:00
42198cd7d5 fix(ngUpgrade): make AoT ngUpgrade work with the testability API and resumeBootstrap() (#12910) 2016-11-16 01:04:56 -08:00
d6ba092a27 build(build.sh): echo before building examples 2016-11-15 20:59:37 -08:00
773b31de8f fix(router): should not create a route state if navigation is canceled (#12868)
Closes #12776
2016-11-15 19:00:20 -08:00
f79b320fc4 refactor(forms): remove facade (#12558) 2016-11-15 18:48:34 -08:00
6a212fd561 fix(router): removes a peer dependency from router to upgrade 2016-11-15 18:37:08 -08:00
be010a292a fix(animations): only pass in same typed players as previous players into web-animations (#12907)
Closes #12907
2016-11-15 17:47:21 -08:00
7c36e7f956 chore(router): remove @angular/upgrade peer dep (#12896) 2016-11-15 14:00:11 -08:00
13ba2f90b9 refactor(http): remove all facade methods from http module (#12870) 2016-11-15 09:19:14 -08:00
75277cd94b fix(tsickle): support ctorParams in function closure (#12876)
See https://github.com/angular/tsickle/issues/261 for context.
2016-11-15 09:19:00 -08:00
46d150266b feat(router_link): add skipLocationChange and replaceUrl inputs (#12850) 2016-11-14 18:30:13 -08:00
1b5384ee54 feat(core): expose ViewRef as ChangeDetectorRef
closes #12722

This is helpful when manually dirty checking embedded views.
2016-11-14 17:01:41 -08:00
9f7d32a326 feat(core): add attachView / detachView to ApplicationRef
This feature is useful to allow components / embedded views
to be dirty checked if they are not placed in any `ViewContainer`.

Closes #9293
2016-11-14 17:01:35 -08:00
9de76ebfa5 fix(animations): retain styling when transition destinations are changed (#12208)
Closes #9661
Closes #12208
2016-11-14 16:59:06 -08:00
46023e4792 fix(select): allow for null values in HTML select options bound with ngValue
closes #12829
2016-11-14 16:47:14 -08:00
b55aaf094f fix: allow for null values in HTML select options bound with ngValue
This corrects the case of <option [ngValue]="null"> binding a string like "{0: null}" to the model instead of an actual null object.

Closes #10349
2016-11-14 16:47:09 -08:00
d90b622fa4 fix: allow for null values in HTML select options bound with ngValue
This corrects the case of <option [ngValue]="null"> binding a string like "{0: null}" to the model instead of an actual null object.

Closes #10349
2016-11-14 16:47:09 -08:00
79e2bb9291 refactor(core): remove dead code (#12871) 2016-11-14 16:44:25 -08:00
efbbefd353 fix(platform-browser): enable AOT
closes #12783
2016-11-14 12:57:11 -08:00
c2fae72bc6 feat(router): register router with ngprobe 2016-11-14 12:57:05 -08:00
7908679c4b fix(compiler): assert xliff messages have translations
fixes #12815
closes #12604
2016-11-14 12:55:56 -08:00
9ed9ff40b3 test(compiler): improve xliff tests 2016-11-14 12:55:48 -08:00
2f14415836 fix(compiler): updates hash algo for xmb/xtb files 2016-11-14 12:55:48 -08:00
76e4911e8b fix(core): fix placeholders handling in i18n.
Prior to this commit, translations were built in the serializers. This
could not work as a single translation can be used for different source
messages having different placeholder content.

Serializers do not try to replace the placeholders any more.
Placeholders are replaced by the translation bundle and the source
message is given as parameter so that the content of the placeholders is
taken into account.

Also XMB ids are now independent of the expression which is replaced by
a placeholder in the extracted file.
fixes #12512
2016-11-14 12:55:48 -08:00
ed5e98d0df fix(core): misc i18n fixes 2016-11-14 12:55:48 -08:00
146af1fed9 refactor(core): simplify i18n serializers code 2016-11-14 12:55:48 -08:00
c60ba7a72f refactor(core): remove ListWrapper from i18n 2016-11-14 12:55:48 -08:00
05beffe0d0 test(core): fix a typo in the i18n integration spec 2016-11-14 12:55:48 -08:00
08c038ebd9 fix(core): xmb serializer uses decimal messaged IDs
fixes #12511
2016-11-14 12:55:48 -08:00
582550a90d feat(core): implements a decimal fingerprint for i18n 2016-11-14 12:55:48 -08:00
1d53a870dd fix(http): return request url if it cannot be retrieved from response
closes #12837
2016-11-14 12:54:43 -08:00
a0c58a6b5c fix(http): correctly handle response body for 204 status code
closes #12830
fixes #12393
2016-11-14 12:36:22 -08:00
d3eff6c483 refactor(xhr_backend): remove facade 2016-11-14 12:36:16 -08:00
521 changed files with 25959 additions and 9310 deletions

32
.pullapprove.yml Normal file
View File

@ -0,0 +1,32 @@
# Configuration for pullapprove.com
# See ownership spreadsheet:
# https://docs.google.com/spreadsheets/d/1-HIlzfbPYGsPr9KuYMe6bLfc4LXzPjpoALqtYRYTZB0/edit?pli=1#gid=0&vpid=A5
version: 2
group_defaults:
required: 1
reset_on_reopened:
enabled: true
approve_by_comment:
enabled: true
approve_regex: '^(Approved|:\+1:|LGTM)'
groups:
config:
conditions:
files:
- "*.yml"
- "*.json"
teams:
- repoowners
compiler:
conditions:
files:
- "tools/@angular/tsc-wrapped/*"
- "modules/@angular/compiler/*"
- "modules/@angular/compiler-cli/*"
teams:
- compiler-owners
- repoowners

View File

@ -1,3 +1,309 @@
<a name="2.4.4"></a>
## [2.4.4](https://github.com/angular/angular/compare/2.4.3...2.4.4) (2017-01-19)
### Bug Fixes
* **animations:** fix internal jscompiler issue and AOT quoting ([#13798](https://github.com/angular/angular/issues/13798)) ([261fd16](https://github.com/angular/angular/commit/261fd16))
* **common:** support numeric value as discrete cases for NgPlural ([#13876](https://github.com/angular/angular/issues/13876)) ([3d0b1b8](https://github.com/angular/angular/commit/3d0b1b8))
* **http:** don't create a blob out of ArrayBuffer when type is application/octet-stream ([#13992](https://github.com/angular/angular/issues/13992)) ([015878a](https://github.com/angular/angular/commit/015878a)), closes [#13973](https://github.com/angular/angular/issues/13973)
* **router:** enable loadChildren with function in aot ([#13909](https://github.com/angular/angular/issues/13909)) ([2af5862](https://github.com/angular/angular/commit/2af5862)), closes [#11075](https://github.com/angular/angular/issues/11075)
* **router:** routerLinkActive should not throw when not initialized ([#13273](https://github.com/angular/angular/issues/13273)) ([49c4b0f](https://github.com/angular/angular/commit/49c4b0f)), closes [#13270](https://github.com/angular/angular/issues/13270)
* **security:** allow calc and gradient functions. ([#13943](https://github.com/angular/angular/issues/13943)) ([bd15110](https://github.com/angular/angular/commit/bd15110))
* **upgrade:** detect async downgrade component changes ([#13812](https://github.com/angular/angular/issues/13812)) ([2250082](https://github.com/angular/angular/commit/2250082)), closes [#6385](https://github.com/angular/angular/issues/6385) [#6385](https://github.com/angular/angular/issues/6385) [#10660](https://github.com/angular/angular/issues/10660) [#12318](https://github.com/angular/angular/issues/12318) [#12034](https://github.com/angular/angular/issues/12034)
<a name="2.4.3"></a>
## [2.4.3](https://github.com/angular/angular/compare/2.4.2...2.4.3) (2017-01-11)
### Bug Fixes
* **compiler:** avoid evaluating arguments to unknown decorators ([5e9d3db](https://github.com/angular/angular/commit/5e9d3db)), closes [#13605](https://github.com/angular/angular/issues/13605)
* **compiler:** fix template binding parsing (`*directive="-..."`) ([7dc12b9](https://github.com/angular/angular/commit/7dc12b9)), closes [#13800](https://github.com/angular/angular/issues/13800)
* **compiler-cli:** add support for more than 2 levels of nested lazy routes ([6164eb2](https://github.com/angular/angular/commit/6164eb2)), closes [angular/angular-cli#3663](https://github.com/angular/angular-cli/issues/3663)
* **compiler-cli:** avoid handling functions in loadChildren as lazy load routes paths ([313683f](https://github.com/angular/angular/commit/313683f)), closes [angular/angular-cli#3204](https://github.com/angular/angular-cli/issues/3204)
* **i18n:** translate attributes inside elements marked for translation ([d7f2a3c](https://github.com/angular/angular/commit/d7f2a3c))
* **router:** RouterLink mirrors input `target` as attribute ([1c82b58](https://github.com/angular/angular/commit/1c82b58)), closes [#13837](https://github.com/angular/angular/issues/13837)
* **router:** throw an error when navigate to null/undefined path ([61ba223](https://github.com/angular/angular/commit/61ba223)), closes [#10560](https://github.com/angular/angular/issues/10560) [#13384](https://github.com/angular/angular/issues/13384)
* **router:** fix checking for object intersection ([1692265](https://github.com/angular/angular/commit/1692265))
<a name="2.4.2"></a>
## [2.4.2](https://github.com/angular/angular/compare/2.4.1...2.4.2) (2017-01-06)
### Bug Fixes
* **common:** add link to trackBy docs from the error message ([#13634](https://github.com/angular/angular/issues/13634)) ([f723437](https://github.com/angular/angular/commit/f723437))
* **common:** do not override locale provided on bootstrap ([#13654](https://github.com/angular/angular/issues/13654)) ([5f49c3e](https://github.com/angular/angular/commit/5f49c3e)), closes [#13607](https://github.com/angular/angular/issues/13607)
* **common:** allow null/undefined values for `NgForTrackBy` ([6be55cc](https://github.com/angular/angular/commit/6be55cc)), closes [#13641](https://github.com/angular/angular/issues/13641)
* **compiler:** dont throw when using `ANALYZE_FOR_ENTRY_COMPONENTS` with user classes ([#13679](https://github.com/angular/angular/issues/13679)) ([230e33f](https://github.com/angular/angular/commit/230e33f)), closes [#13565](https://github.com/angular/angular/issues/13565)
* **compiler:** query `<template>` elements before their children. ([#13677](https://github.com/angular/angular/issues/13677)) ([1cd73c7](https://github.com/angular/angular/commit/1cd73c7)), closes [#13118](https://github.com/angular/angular/issues/13118) [#13167](https://github.com/angular/angular/issues/13167)
* **compiler:** allow "." in attribute selectors ([#13653](https://github.com/angular/angular/issues/13653)) ([29ffdfd](https://github.com/angular/angular/commit/29ffdfd)), closes [#13645](https://github.com/angular/angular/issues/13645) [#13982](https://github.com/angular/angular/issues/13982)
* **core:** animations no longer silently exits if the element is not apart of the DOM ([#13763](https://github.com/angular/angular/issues/13763)) ([f1cde43](https://github.com/angular/angular/commit/f1cde43))
* **core:** animations should blend in all previously transitioned styles into next animation if interrupted ([#13148](https://github.com/angular/angular/issues/13148)) ([b245b92](https://github.com/angular/angular/commit/b245b92))
* **core:** remove reference to "Angular 2" in dev mode warning ([#13751](https://github.com/angular/angular/issues/13751)) ([21f5f05](https://github.com/angular/angular/commit/21f5f05))
* **core/testing:** improve misleading error message when don't call compileComponents ([#13543](https://github.com/angular/angular/issues/13543)) ([0e7f9f0](https://github.com/angular/angular/commit/0e7f9f0)), closes [#11301](https://github.com/angular/angular/issues/11301)
* **forms:** Validators.required properly validate arrays ([#13362](https://github.com/angular/angular/issues/13362)) ([17c5fa9](https://github.com/angular/angular/commit/17c5fa9)), closes [#12274](https://github.com/angular/angular/issues/12274)
* **language-service:** support TypeScript 2.1 ([#13655](https://github.com/angular/angular/issues/13655)) ([56b4296](https://github.com/angular/angular/commit/56b4296))
* **router:** fix lazy loaded module with wildcard route ([#13649](https://github.com/angular/angular/issues/13649)) ([5754ecc](https://github.com/angular/angular/commit/5754ecc)), closes [#12955](https://github.com/angular/angular/issues/12955)
* **router:** routerLink support of null ([#13380](https://github.com/angular/angular/issues/13380)) ([018865e](https://github.com/angular/angular/commit/018865e)), closes [#6971](https://github.com/angular/angular/issues/6971)
* **router:** update route snapshot before emit new values ([#13558](https://github.com/angular/angular/issues/13558)) ([9f6a647](https://github.com/angular/angular/commit/9f6a647)), closes [#12912](https://github.com/angular/angular/issues/12912)
* **upgrade:** fix/improve support for lifecycle hooks ([#13020](https://github.com/angular/angular/issues/13020)) ([21942a8](https://github.com/angular/angular/commit/21942a8))
<a name="2.4.1"></a>
## [2.4.1](https://github.com/angular/angular/compare/2.4.0...2.4.1) (2016-12-21)
### Bug Fixes
* **animations:** always quote string map key values in AOT code ([#13602](https://github.com/angular/angular/issues/13602)) ([6a5e46c](https://github.com/angular/angular/commit/6a5e46c))
* **animations:** always recover from a failed animation step ([#13604](https://github.com/angular/angular/issues/13604)) ([d788c67](https://github.com/angular/angular/commit/d788c67))
* **compiler:** ignore `@import` in comments ([#13368](https://github.com/angular/angular/issues/13368)) ([6316e5d](https://github.com/angular/angular/commit/6316e5d)), closes [#12196](https://github.com/angular/angular/issues/12196)
* **core:** improve error message when component factory cannot be found ([#13541](https://github.com/angular/angular/issues/13541)) ([b9e979e](https://github.com/angular/angular/commit/b9e979e)), closes [#12678](https://github.com/angular/angular/issues/12678)
* **router:** should reset location if a navigation by location is successful ([#13545](https://github.com/angular/angular/issues/13545)) ([a38f14b](https://github.com/angular/angular/commit/a38f14b)), closes [#13491](https://github.com/angular/angular/issues/13491)
<a name="2.4.0"></a>
# [2.4.0 stability-interjection](https://github.com/angular/angular/compare/2.3.1...2.4.0) (2016-12-20)
### Bug Fixes
* **animations:** allow players to be destroyed before initialized ([#13346](https://github.com/angular/angular/issues/13346)) ([b36f4bc](https://github.com/angular/angular/commit/b36f4bc)), closes [#13293](https://github.com/angular/angular/issues/13293)
* **build:** use bash string comparison operator ([#13502](https://github.com/angular/angular/issues/13502)) ([50afbe0](https://github.com/angular/angular/commit/50afbe0))
* **compiler:** do not lex `}}` when interpolation is disabled ([#13531](https://github.com/angular/angular/issues/13531)) ([9b87bb6](https://github.com/angular/angular/commit/9b87bb6)), closes [#13525](https://github.com/angular/angular/issues/13525)
* **compiler-cli:** produce metadata for .d.ts files without metadata ([#13526](https://github.com/angular/angular/issues/13526)) ([debb0c9](https://github.com/angular/angular/commit/debb0c9)), closes [#13307](https://github.com/angular/angular/issues/13307) [#13473](https://github.com/angular/angular/issues/13473) [#13521](https://github.com/angular/angular/issues/13521)
* **i18n:** add a default example to xmb placeholders ([#13507](https://github.com/angular/angular/issues/13507)) ([3f17841](https://github.com/angular/angular/commit/3f17841))
* **upgrade:** fix `registerForNg1Tests` ([#13522](https://github.com/angular/angular/issues/13522)) ([c26c24c](https://github.com/angular/angular/commit/c26c24c))
### Features
* update to `rxjs@5.0.1` and unpin the rxjs peerDeps via `^5.0.1` ([#13572](https://github.com/angular/angular/issues/13572)) ([8d5da1e](https://github.com/angular/angular/commit/8d5da1e)), closes [#13561](https://github.com/angular/angular/issues/13561) [#13478](https://github.com/angular/angular/issues/13478)
<a name="2.3.1"></a>
## [2.3.1](https://github.com/angular/angular/compare/2.3.0...2.3.1) (2016-12-15)
### Bug Fixes
* **animations:** always cleanup players after they have finished internally ([#13334](https://github.com/angular/angular/issues/13334)) ([a26e054](https://github.com/angular/angular/commit/a26e054)), closes [#13333](https://github.com/angular/angular/issues/13333)
* **animations:** throw errors and normalize offset beyond the range of [0,1] ([6557bc3](https://github.com/angular/angular/commit/6557bc3)), closes [#13348](https://github.com/angular/angular/issues/13348) [#13440](https://github.com/angular/angular/issues/13440)
* **compiler:** emit quoted object literal keys if the source is quoted ([ecfad46](https://github.com/angular/angular/commit/ecfad46)), closes [#13249](https://github.com/angular/angular/issues/13249) [#13356](https://github.com/angular/angular/issues/13356)
* **compiler:** fix merge error in compiler_host ([69b52eb](https://github.com/angular/angular/commit/69b52eb))
* **compiler:** fix PR 13322 ([#13331](https://github.com/angular/angular/issues/13331)) ([79728b4](https://github.com/angular/angular/commit/79728b4))
* **compiler:** fix simplify a reference without a name ([1c279b3](https://github.com/angular/angular/commit/1c279b3)), closes [#13470](https://github.com/angular/angular/issues/13470)
* **compiler:** fix transpiled ES5 code ([#13322](https://github.com/angular/angular/issues/13322)) ([6c1d790](https://github.com/angular/angular/commit/6c1d790)), closes [#13301](https://github.com/angular/angular/issues/13301)
* **compiler:** generated CSS files suffixed with ngstyle. ([#13353](https://github.com/angular/angular/issues/13353)) ([c8a9b70](https://github.com/angular/angular/commit/c8a9b70)), closes [#13141](https://github.com/angular/angular/issues/13141)
* **compiler:** make sure provider values with `name` property dont break. ([efa2d80](https://github.com/angular/angular/commit/efa2d80)), closes [#13394](https://github.com/angular/angular/issues/13394) [#13445](https://github.com/angular/angular/issues/13445)
* **compiler:** narrow the span reported for invalid pipes ([307d305](https://github.com/angular/angular/commit/307d305)), closes [#13326](https://github.com/angular/angular/issues/13326) [#13411](https://github.com/angular/angular/issues/13411)
* **compiler:** propagate exports when upgrading metadata to v2 ([f6ef7d6](https://github.com/angular/angular/commit/f6ef7d6))
* **compiler:** resolver should merge host bindings and listeners ([#13474](https://github.com/angular/angular/issues/13474)) ([6aeaca3](https://github.com/angular/angular/commit/6aeaca3)), closes [#13327](https://github.com/angular/angular/issues/13327)
* **compiler:** support dotted property binding ([8db184d](https://github.com/angular/angular/commit/8db184d)), closes [angular/flex-layout#34](https://github.com/angular/flex-layout/issues/34)
* **compiler:** update to metadata version 3 ([#13464](https://github.com/angular/angular/issues/13464)) ([b9b557c](https://github.com/angular/angular/commit/b9b557c))
* **core:** detectChanges() doesn't work on detached instance ([4d6ac9d](https://github.com/angular/angular/commit/4d6ac9d)), closes [#13426](https://github.com/angular/angular/issues/13426) [#13472](https://github.com/angular/angular/issues/13472)
* **core:** properly destroy embedded Views attatched to ApplicationRef ([#13459](https://github.com/angular/angular/issues/13459)) ([d40bbf4](https://github.com/angular/angular/commit/d40bbf4)), closes [#13062](https://github.com/angular/angular/issues/13062)
* **core:** remove logError from logGroup ([#12925](https://github.com/angular/angular/issues/12925)) ([5fab871](https://github.com/angular/angular/commit/5fab871))
* **forms:** ensure `select[multiple]` retains selections ([b3dcff0](https://github.com/angular/angular/commit/b3dcff0)), closes [#12527](https://github.com/angular/angular/issues/12527) [#12654](https://github.com/angular/angular/issues/12654)
* **forms:** fix Validators.min/maxLength with FormArray ([#13095](https://github.com/angular/angular/issues/13095)) ([7383e4a](https://github.com/angular/angular/commit/7383e4a)), closes [#13089](https://github.com/angular/angular/issues/13089)
* **forms:** introduce checkbox required validator ([124267c](https://github.com/angular/angular/commit/124267c)), closes [#11459](https://github.com/angular/angular/issues/11459) [#13364](https://github.com/angular/angular/issues/13364)
* **http:** check response body text against undefined ([#13017](https://github.com/angular/angular/issues/13017)) ([f106a18](https://github.com/angular/angular/commit/f106a18))
* **http:** create a copy of headers when merge options ([#13365](https://github.com/angular/angular/issues/13365)) ([65c9b5b](https://github.com/angular/angular/commit/65c9b5b)), closes [#11980](https://github.com/angular/angular/issues/11980)
* **language-service:** correctly type `undefined` ([0a7364f](https://github.com/angular/angular/commit/0a7364f)), closes [#13412](https://github.com/angular/angular/issues/13412) [#13414](https://github.com/angular/angular/issues/13414)
* **compiler**: better error when directive not listed in NgModule.declarations ([b0cd514](https://github.com/angular/angular/commit/b0cd514))
* **language-service:** treat string unions as strings ([#13406](https://github.com/angular/angular/issues/13406)) ([14dd2b3](https://github.com/angular/angular/commit/14dd2b3)), closes [#13403](https://github.com/angular/angular/issues/13403)
* **router:** add support for query params with multiple values ([e4d5a5f](https://github.com/angular/angular/commit/e4d5a5f)), closes [#11373](https://github.com/angular/angular/issues/11373)
* **router:** Use T type in Resolve interface ([#13242](https://github.com/angular/angular/issues/13242)) ([5ee8155](https://github.com/angular/angular/commit/5ee8155))
* **selector:** SelectorMatcher match elements with :not selector ([#12977](https://github.com/angular/angular/issues/12977)) ([392c9ac](https://github.com/angular/angular/commit/392c9ac))
* **tsc-wrapped:** generate metadata for exports without module specifier ([cd03c77](https://github.com/angular/angular/commit/cd03c77)), closes [#13327](https://github.com/angular/angular/issues/13327)
* **upgrade:** fix downgrade content projection and injector inheritance ([86c5098](https://github.com/angular/angular/commit/86c5098)), closes [#6629](https://github.com/angular/angular/issues/6629) [#7727](https://github.com/angular/angular/issues/7727) [#8729](https://github.com/angular/angular/issues/8729) [#9643](https://github.com/angular/angular/issues/9643) [#9649](https://github.com/angular/angular/issues/9649) [#12675](https://github.com/angular/angular/issues/12675)
* **upgrade:** enable Angular 1 unit testing of upgrade module ([2fc0560](https://github.com/angular/angular/commit/2fc0560)), closes [#5462](https://github.com/angular/angular/issues/5462) [#12675](https://github.com/angular/angular/issues/12675)
### Performance Improvements
* **animations:** always run the animation queue outside of zones ([e2622ad](https://github.com/angular/angular/commit/e2622ad)), closes [#13440](https://github.com/angular/angular/issues/13440)
### Note ###
Due to regression in the 2.3.0 release that was fixed by [#13464](https://github.com/angular/angular/pull/13464),
components that have been compiled using 2.3.0 and published to npm will need to be recompiled and republished.
The >=2.3.1 compiler will issue is the following error if it encounters components compiled with 2.3.0:
`Unsupported metadata version 2 for module ${module}. This module should be compiled with a newer version of ngc`.
We are adding more tests to our test suite to catch these kinds of problems before we cut a release.
<a name="2.3.0"></a>
# [2.3.0](https://github.com/angular/angular/compare/2.3.0-rc.0...2.3.0) (2016-12-07)
### Bug Fixes
* **common:** make sure the plural category exists ([#13169](https://github.com/angular/angular/issues/13169)) ([82c81cd](https://github.com/angular/angular/commit/82c81cd)), closes [#12379](https://github.com/angular/angular/issues/12379)
* **compiler:** include the summaries of reexported modules / directives / pipes ([#13196](https://github.com/angular/angular/issues/13196)) ([75d1617](https://github.com/angular/angular/commit/75d1617))
* **compiler:** serialize any `StaticSymbol` correctly, not matter in which context ([5614c4f](https://github.com/angular/angular/commit/5614c4f))
* **compiler:** short-circut expressions with an index ([#13263](https://github.com/angular/angular/issues/13263)) ([f31c947](https://github.com/angular/angular/commit/f31c947)), closes [#13254](https://github.com/angular/angular/issues/13254)
* **core:** display framework version on bootstrapped component ([#13252](https://github.com/angular/angular/issues/13252)) ([16efb13](https://github.com/angular/angular/commit/16efb13))
* **facade:** cache original format string ([#12764](https://github.com/angular/angular/issues/12764)) ([a132287](https://github.com/angular/angular/commit/a132287))
* **http:** set the default Accept header ([#12989](https://github.com/angular/angular/issues/12989)) ([986abbe](https://github.com/angular/angular/commit/986abbe)), closes [#6354](https://github.com/angular/angular/issues/6354)
* **language-service:** avoid throwing for invalid class declarations ([#13257](https://github.com/angular/angular/issues/13257)) ([93556a5](https://github.com/angular/angular/commit/93556a5)), closes [#13253](https://github.com/angular/angular/issues/13253)
* **language-service:** do not throw for invalid metadata ([#13261](https://github.com/angular/angular/issues/13261)) ([4a09c81](https://github.com/angular/angular/commit/4a09c81)), closes [#13255](https://github.com/angular/angular/issues/13255)
* **language-service:** remove incompletely used parameter from `createLanguageServiceFromTypescript()` ([#13278](https://github.com/angular/angular/issues/13278)) ([25c2141](https://github.com/angular/angular/commit/25c2141)), closes [#13277](https://github.com/angular/angular/issues/13277)
* **language-service:** update to use `CompilerHost` from compiler-cli ([#13189](https://github.com/angular/angular/issues/13189)) ([3ff6554](https://github.com/angular/angular/commit/3ff6554))
* **router:** allow specifying a matcher without specifying a path ([bbb7a39](https://github.com/angular/angular/commit/bbb7a39)), closes [#12972](https://github.com/angular/angular/issues/12972)
* **router:** fix replaceUrl on RouterLink directives ([349ad75](https://github.com/angular/angular/commit/349ad75))
* **router:** fix skipLocationChanges on RouterLink directives ([f562cbf](https://github.com/angular/angular/commit/f562cbf)), closes [#13156](https://github.com/angular/angular/issues/13156)
* **router:** make setUpLocationChangeListener idempotent ([25e5b2f](https://github.com/angular/angular/commit/25e5b2f))
* **router:** runs guards every time when unsuccessfully navigating to the same url over and over again ([#13209](https://github.com/angular/angular/issues/13209)) ([d46b8de](https://github.com/angular/angular/commit/d46b8de))
* **router:** throw a better error message when angular 1 is not bootstraped ([c767df0](https://github.com/angular/angular/commit/c767df0))
* **router:** validate nested routes ([#13224](https://github.com/angular/angular/issues/13224)) ([2893c2c](https://github.com/angular/angular/commit/2893c2c)), closes [#12827](https://github.com/angular/angular/issues/12827)
* **tsc-wrapped:** have UserError display the actual error ([393c100](https://github.com/angular/angular/commit/393c100))
### Features
* **compiler:** read and write `.ngsummary.json` files ([614a35d](https://github.com/angular/angular/commit/614a35d)), closes [#12787](https://github.com/angular/angular/issues/12787)
<a name="2.3.0-rc.0"></a>
# [2.3.0-rc.0](https://github.com/angular/angular/compare/2.3.0-beta.0...2.3.0-rc.0) (2016-11-30)
### Bug Fixes
* **animations:** blend in all previously transitioned styles into next animation if interrupted ([#13014](https://github.com/angular/angular/issues/13014)) ([ef96763](https://github.com/angular/angular/commit/ef96763)), closes [#13013](https://github.com/angular/angular/issues/13013)
* **benchmarks:** use sanitized style values ([#12943](https://github.com/angular/angular/issues/12943)) ([fc5ac1e](https://github.com/angular/angular/commit/fc5ac1e))
* **build:** update versions of umd bundles ([#13038](https://github.com/angular/angular/issues/13038)) ([86ffa88](https://github.com/angular/angular/commit/86ffa88)), closes [#13037](https://github.com/angular/angular/issues/13037)
* **changelog:** replace beta.1 with beta.0 ([#12961](https://github.com/angular/angular/issues/12961)) ([07a986d](https://github.com/angular/angular/commit/07a986d))
* **ci:** pin version of npm on CircleCI ([#12954](https://github.com/angular/angular/issues/12954)) ([a3884db](https://github.com/angular/angular/commit/a3884db))
* **closure:** quote date pattern aliases ([#13012](https://github.com/angular/angular/issues/13012)) ([7dcca30](https://github.com/angular/angular/commit/7dcca30))
* **common:** update DatePipe to allow closure compilation ([b2b7219](https://github.com/angular/angular/commit/b2b7219))
* **compiler:** correctly evaluate references to static functions ([#13133](https://github.com/angular/angular/issues/13133)) ([627282d](https://github.com/angular/angular/commit/627282d))
* **compiler:** fix performance regression caused by 5b0f9e2 ([43c0e9a](https://github.com/angular/angular/commit/43c0e9a)), closes [#13146](https://github.com/angular/angular/issues/13146)
* **compiler:** fix versions of `@angular/tsc-wrapped` ([bccf0e6](https://github.com/angular/angular/commit/bccf0e6))
* **compiler-cli:** fix paths in source maps to be relative ([2a3ca7b](https://github.com/angular/angular/commit/2a3ca7b)), closes [#13040](https://github.com/angular/angular/issues/13040)
* **compiler-cli:** pin the version of `tsc-wrapped` ([966bcba](https://github.com/angular/angular/commit/966bcba))
* **language-service:** harden against partial normalization of directives ([2975d89](https://github.com/angular/angular/commit/2975d89))
* **core:** shrinkwrap was out of date with packages. ([e45b7ff](https://github.com/angular/angular/commit/e45b7ff))
* **language-service:** make link check pass ([7194fc2](https://github.com/angular/angular/commit/7194fc2))
* **router:** guards restore an incorrect url when used with skipLocationChange ([ad20d7d](https://github.com/angular/angular/commit/ad20d7d)), closes [#12825](https://github.com/angular/angular/issues/12825)
* **router:** support redirects to named outlets ([602522b](https://github.com/angular/angular/commit/602522b)), closes [#12740](https://github.com/angular/angular/issues/12740) [#9921](https://github.com/angular/angular/issues/9921)
* **tsc-wrapped:** set correct version number ([897555c](https://github.com/angular/angular/commit/897555c))
* **tsc-wrapped:** still emit version 1 metadata to allow use of new components in old setups ([bc69c74](https://github.com/angular/angular/commit/bc69c74))
* **upgrade:** call ng1 lifecycle hooks ([#12875](https://github.com/angular/angular/issues/12875)) ([1ef4696](https://github.com/angular/angular/commit/1ef4696))
* **version:** take all of version string after patch version ([f275f36](https://github.com/angular/angular/commit/f275f36))
### Features
* **core:** update RxJS peer dependency to 5.0.0-rc.4 Please see [this gist](https://gist.github.com/robwormald/19dea0c70a6e01aadced6731aed4f9f7) if you depend on the `cache` operator ([2d6a003](https://github.com/angular/angular/commit/2d6a003)), closes [#13125](https://github.com/angular/angular/issues/13125)
* **core:** upgrade zone.js to v0.7.1 ([c4bbafc](https://github.com/angular/angular/commit/c4bbafc))
* **build:** record angular version in the dom ([#13164](https://github.com/angular/angular/issues/13164)) ([e628b66](https://github.com/angular/angular/commit/e628b66))
* **core:** expose destroy() method on ViewRef ([808275a](https://github.com/angular/angular/commit/808275a))
* **core:** properly support inheritance ([f5c8e09](https://github.com/angular/angular/commit/f5c8e09)), closes [#11606](https://github.com/angular/angular/issues/11606) [#12892](https://github.com/angular/angular/issues/12892)
* **language-service:** add services to support editors ([#12987](https://github.com/angular/angular/issues/12987)) ([519a324](https://github.com/angular/angular/commit/519a324))
* **router:** add support for custom route reuse strategies ([42cf06f](https://github.com/angular/angular/commit/42cf06f))
* **tools:** allow disabling annotation lowering ([c1a62e2](https://github.com/angular/angular/commit/c1a62e2))
<a name="2.2.4"></a>
## [2.2.4](https://github.com/angular/angular/compare/2.2.3...2.2.4) (2016-11-30)
### Bug Fixes
* **common:** update DatePipe to allow closure compilation ([eba53fd](https://github.com/angular/angular/commit/eba53fd))
* **compiler:** fix performance regression caused by 5b0f9e2 ([ee2d6e5](https://github.com/angular/angular/commit/ee2d6e5)), closes [#13146](https://github.com/angular/angular/issues/13146)
* **compiler-cli:** fix paths in source maps to be relative ([eb173bc](https://github.com/angular/angular/commit/eb173bc)), closes [#13040](https://github.com/angular/angular/issues/13040)
<a name="2.2.3"></a>
## [2.2.3](https://github.com/angular/angular/compare/2.2.2...2.2.3) (2016-11-23)
### Bug Fixes
* **compiler:** Revert: fix versions of `@angular/tsc-wrapped` ([015ca47](https://github.com/angular/angular/commit/015ca47))
* **animations:** Revert: blend in all previously transitioned styles into next animation if interrupted ([c12e56e](https://github.com/angular/angular/commit/c12e56e))
<a name="2.2.2"></a>
## [2.2.2](https://github.com/angular/angular/compare/2.2.1...2.2.2) (2016-11-22)
### Bug Fixes
* **animations:** blend in all previously transitioned styles into next animation if interrupted ([#13014](https://github.com/angular/angular/issues/13014)) ([ea4fc9b](https://github.com/angular/angular/commit/ea4fc9b)), closes [#13013](https://github.com/angular/angular/issues/13013)
* **benchmarks:** use sanitized style values ([#12943](https://github.com/angular/angular/issues/12943)) ([33a7902](https://github.com/angular/angular/commit/33a7902))
* **closure:** quote date pattern aliases ([#13012](https://github.com/angular/angular/issues/13012)) ([0956ace](https://github.com/angular/angular/commit/0956ace))
* **compiler:** fix versions of `@angular/tsc-wrapped` ([2fe6fb1](https://github.com/angular/angular/commit/2fe6fb1))
* **router:** add a banner file for the router ([#12919](https://github.com/angular/angular/issues/12919)) ([8df328b](https://github.com/angular/angular/commit/8df328b))
* **router:** add a banner file for the router ([#12919](https://github.com/angular/angular/issues/12919)) ([511cd4d](https://github.com/angular/angular/commit/511cd4d))
* **router:** removes a peer dependency from router to upgrade ([115f18f](https://github.com/angular/angular/commit/115f18f))
* **router:** removes a peer dependency from router to upgrade ([87d5d49](https://github.com/angular/angular/commit/87d5d49))
* **router:** support redirects to named outlets ([09226d9](https://github.com/angular/angular/commit/09226d9)), closes [#12740](https://github.com/angular/angular/issues/12740) [#9921](https://github.com/angular/angular/issues/9921)
* **upgrade:** call ng1 lifecycle hooks ([#12875](https://github.com/angular/angular/issues/12875)) ([462316b](https://github.com/angular/angular/commit/462316b))
<a name="2.3.0-beta.0"></a>
# [2.3.0-beta.0](https://github.com/angular/angular/compare/2.2.0...2.3.0-beta.0) (2016-11-17)
### Bug Fixes
* **compiler:** assert xliff messages have translations ([7908679](https://github.com/angular/angular/commit/7908679)), closes [#12815](https://github.com/angular/angular/issues/12815) [#12604](https://github.com/angular/angular/issues/12604)
* **compiler:** updates hash algo for xmb/xtb files ([2f14415](https://github.com/angular/angular/commit/2f14415))
* **core:** fix placeholders handling in i18n. ([76e4911](https://github.com/angular/angular/commit/76e4911)), closes [#12512](https://github.com/angular/angular/issues/12512)
* **core:** misc i18n fixes ([ed5e98d](https://github.com/angular/angular/commit/ed5e98d))
* **core:** xmb serializer uses decimal messaged IDs ([08c038e](https://github.com/angular/angular/commit/08c038e)), closes [#12511](https://github.com/angular/angular/issues/12511)
* **platform-browser:** enable AOT ([efbbefd](https://github.com/angular/angular/commit/efbbefd)), closes [#12783](https://github.com/angular/angular/issues/12783)
### Features
* **core:** add `attachView` / `detachView` to ApplicationRef ([9f7d32a](https://github.com/angular/angular/commit/9f7d32a)), closes [#9293](https://github.com/angular/angular/issues/9293)
* **core:** expose `ViewRef` as `ChangeDetectorRef` ([1b5384e](https://github.com/angular/angular/commit/1b5384e)), closes [#12722](https://github.com/angular/angular/issues/12722)
* **core:** implements a decimal fingerprint for i18n ([582550a](https://github.com/angular/angular/commit/582550a))
* **router:** register router with ngprobe ([c2fae72](https://github.com/angular/angular/commit/c2fae72))
* **router_link:** add skipLocationChange and replaceUrl inputs ([#12850](https://github.com/angular/angular/issues/12850)) ([46d1502](https://github.com/angular/angular/commit/46d1502))
Note: The 2.3.0-beta.0 release also contains all the changes present in the 2.2.1 release.
<a name="2.2.1"></a>
## [2.2.1](https://github.com/angular/angular/compare/2.2.0...2.2.1) (2016-11-17)
### Bug Fixes
* **animations:** only pass in same typed players as previous players into web-animations ([#12907](https://github.com/angular/angular/issues/12907)) ([583d283](https://github.com/angular/angular/commit/583d283))
* **animations:** retain styling when transition destinations are changed ([#12208](https://github.com/angular/angular/issues/12208)) ([5c46c49](https://github.com/angular/angular/commit/5c46c49)), closes [#9661](https://github.com/angular/angular/issues/9661)
* **core:** support `ngTemplateOutlet` in production mode ([#12921](https://github.com/angular/angular/issues/12921)) ([4628798](https://github.com/angular/angular/commit/4628798)), closes [#12911](https://github.com/angular/angular/issues/12911)
* **http:** correctly handle response body for 204 status code ([21a4de9](https://github.com/angular/angular/commit/21a4de9)), closes [#12830](https://github.com/angular/angular/issues/12830) [#12393](https://github.com/angular/angular/issues/12393)
* **http:** return request url if it cannot be retrieved from response ([845ea23](https://github.com/angular/angular/commit/845ea23)), closes [#12837](https://github.com/angular/angular/issues/12837)
* **upgrade:** make AoT ngUpgrade work with the testability API and resumeBootstrap() ([#12910](https://github.com/angular/angular/issues/12910)) ([dc1662a](https://github.com/angular/angular/commit/dc1662a))
* **platform-browser:** fix disableDebugTools() ([#12918](https://github.com/angular/angular/issues/12918)) ([7b67bad](https://github.com/angular/angular/commit/7b67bad))
* **router:** add a banner file for the router ([#12919](https://github.com/angular/angular/issues/12919)) ([364642d](https://github.com/angular/angular/commit/364642d))
* **router:** removes a peer dependency from router to upgrade ([1dcf1f4](https://github.com/angular/angular/commit/1dcf1f4))
* **forms** allow for null values in HTML select options bound with ngValue ([e0ce545](https://github.com/angular/angular/commit/e0ce545)), closes [#10349](https://github.com/angular/angular/issues/10349)
* **router:** should not create a route state if navigation is canceled ([#12868](https://github.com/angular/angular/issues/12868)) ([dabaf85](https://github.com/angular/angular/commit/dabaf85)), closes [#12776](https://github.com/angular/angular/issues/12776)
* **common:** select should allow for null values in HTML select options bound with ngValue ([e02c180](https://github.com/angular/angular/commit/e02c180)), closes [#12829](https://github.com/angular/angular/issues/12829)
* **compiler-cli:** support ctorParams in function closure ([#12876](https://github.com/angular/angular/issues/12876)) ([6cdc3b5](https://github.com/angular/angular/commit/6cdc3b5))
<a name="2.2.0"></a>
# [2.2.0 upgrade-firebooster](https://github.com/angular/angular/compare/2.2.0-rc.0...2.2.0) (2016-11-14)
@ -798,7 +1104,7 @@ prefix using `animate-` must now be preixed using `bind-animate-`.
* core:
- `ApplicationRef.dispose` is deprecated. Destroy the module that was
created during bootstrap instead by calling `NgModuleRef.destroy`.
- `AplicationRef.registerDisposeListener` is deprecated.
- `ApplicationRef.registerDisposeListener` is deprecated.
Use the `ngOnDestroy` lifecycle hook for providers or
`NgModuleRef.onDestroy` instead.
- `disposePlatform` is deprecated. Use `destroyPlatform` instead.

View File

@ -57,7 +57,7 @@ We want to fix all the issues as soon as possible, but before fixing a bug we ne
- 3rd-party libraries and their versions
- and most importantly - a use-case that fails
A minimal reproduce scenario using http://plnkr.co/ allows us to quickly confirm a bug (or point out coding problem) as well as confirm that we are fixing the right problem. If plunker is not a suitable way to demostrate the problem (for example for issues related to our npm packaging), please create a standalone git repository demostrating the problem.
A minimal reproduce scenario using http://plnkr.co/ allows us to quickly confirm a bug (or point out coding problem) as well as confirm that we are fixing the right problem. If plunker is not a suitable way to demonstrate the problem (for example for issues related to our npm packaging), please create a standalone git repository demonstrating the problem.
We will be insisting on a minimal reproduce scenario in order to save maintainers time and ultimately be able to fix more bugs. Interestingly, from our experience users often find coding problems themselves while preparing a minimal plunk. We understand that sometimes it might be hard to extract essentials bits of code from a larger code-base but we really need to isolate the problem before we can fix it.
@ -244,7 +244,7 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
[github]: https://github.com/angular/angular
[gitter]: https://gitter.im/angular/angular
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
[js-style-guide]: https://google.github.io/styleguide/javascriptguide.xml
[js-style-guide]: https://google.github.io/styleguide/jsguide.html
[jsfiddle]: http://jsfiddle.net
[plunker]: http://plnkr.co/edit
[runnable]: http://runnable.com

View File

@ -1,6 +1,6 @@
# Building and Testing Angular 2 for JS
# Building and Testing Angular
This document describes how to set up your development environment to build and test Angular 2 JS version.
This document describes how to set up your development environment to build and test Angular.
It also explains the basic mechanics of using `git`, `node`, and `npm`.
* [Prerequisite Software](#prerequisite-software)
@ -74,6 +74,15 @@ use in these instructions.
*Option 2*: defining a bash alias like `alias nbin='PATH=$(npm bin):$PATH'` as detailed in this
[Stackoverflow answer](http://stackoverflow.com/questions/9679932/how-to-use-package-installed-locally-in-node-modules/15157360#15157360) and used like this: e.g., `nbin gulp build`.
## Installing Bower Modules
Now run `bower` to install additional dependencies:
```shell
# Install other Angular project dependencies (bower.json)
bower install
```
## Windows only
In order to create the right symlinks, run **as administrator**:
@ -124,9 +133,10 @@ If you happen to modify the public API of Angular, API golden files must be upda
$ gulp public-api:update
```
Note: The command `./test.sh tools` fails when the API doesn't match the golden files.
Note: The command `gulp public-api:enforce` fails when the API doesn't match the golden files. Make sure to rebuild
the project before trying to verify after an API change.
## Formatting your source code
## <a name="clang-format"></a> Formatting your source code
Angular uses [clang-format](http://clang.llvm.org/docs/ClangFormat.html) to format the source code. If the source code
is not properly formatted, the CI will fail and the PR can not be merged.
@ -137,4 +147,32 @@ You can automatically format your code by running:
$ gulp format
```
## Linting/verifying your source code
You can check that your code is properly formatted and adheres to coding style by running:
``` shell
$ gulp lint
```
## Publishing your own personal snapshot build
You may find that your un-merged change needs some validation from external participants.
Rather than requiring them to pull your Pull Request and build Angular locally, you can
publish the `*-builds` snapshots just like our Travis build does.
First time, you need to create the github repositories:
``` shell
$ export TOKEN=[get one from https://github.com/settings/tokens]
$ CREATE_REPOS=1 ./scripts/publish/publish-build-artifacts.sh [github username]
```
For subsequent snapshots, just run
``` shell
$ ./scripts/publish/publish-build-artifacts.sh [github username]
```
The script will publish the build snapshot to a branch with the same name as your current branch,
and create it if it doesn't exist.

View File

@ -24,8 +24,9 @@ with it.
* `comp: forms`: `@kara`
* `comp: http`: `@jeffbcross`
* `comp: i18n`: `@vicb`
* `comp: language service`: `@chuckjaz`
* `comp: metadata-extractor`: `@chuckjaz`
* `comp: router`: `@vsavkin`
* `comp: router`: `@vicb`
* `comp: testing`: `@juliemr`
* `comp: upgrade`: `@mhevery`
* `comp: web-worker`: `@vicb`

View File

@ -36,7 +36,7 @@ var CIconfiguration = {
'iOS7': {unitTest: {target: 'BS', required: true}, e2e: {target: null, required: true}},
'iOS8': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'iOS9': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'iOS10': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'iOS10': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
'WindowsPhone': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}
};
@ -44,10 +44,10 @@ var customLaunchers = {
'DartiumWithWebPlatform':
{base: 'Dartium', flags: ['--enable-experimental-web-platform-features']},
'ChromeNoSandbox': {base: 'Chrome', flags: ['--no-sandbox']},
'SL_CHROME': {base: 'SauceLabs', browserName: 'chrome', version: '52'},
'SL_CHROME': {base: 'SauceLabs', browserName: 'chrome', version: '54'},
'SL_CHROMEBETA': {base: 'SauceLabs', browserName: 'chrome', version: 'beta'},
'SL_CHROMEDEV': {base: 'SauceLabs', browserName: 'chrome', version: 'dev'},
'SL_FIREFOX': {base: 'SauceLabs', browserName: 'firefox', version: '46'},
'SL_FIREFOX': {base: 'SauceLabs', browserName: 'firefox', version: '50'},
'SL_FIREFOXBETA': {base: 'SauceLabs', browserName: 'firefox', version: 'beta'},
'SL_FIREFOXDEV': {base: 'SauceLabs', browserName: 'firefox', version: 'dev'},
'SL_SAFARI7': {base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.9', version: '7.0'},
@ -96,7 +96,7 @@ var customLaunchers = {
'BS_IE10': {
base: 'BrowserStack',
browser: 'ie',
browser_version: '10.0',
browser_version: '10.1',
os: 'Windows',
os_version: '8'
},

View File

@ -17,9 +17,14 @@ PACKAGES=(core
upgrade
router
compiler-cli
language-service
benchpress)
BUILD_ALL=true
BUNDLE=true
VERSION_PREFIX=$(node -p "require('./package.json').version")
VERSION_SUFFIX="-$(git log --oneline -1 | awk '{print $1}')"
ROUTER_VERSION_PREFIX=$(node -p "require('./package.json').version.replace(/^2/, '3')")
REMOVE_BENCHPRESS=false
for ARG in "$@"; do
case "$ARG" in
@ -31,6 +36,10 @@ for ARG in "$@"; do
--bundle=*)
BUNDLE=( "${ARG#--bundle=}" )
;;
--publish)
VERSION_SUFFIX=""
REMOVE_BENCHPRESS=true
;;
*)
echo "Unknown option $ARG."
exit 1
@ -38,6 +47,10 @@ for ARG in "$@"; do
esac
done
VERSION="${VERSION_PREFIX}${VERSION_SUFFIX}"
ROUTER_VERSION="${ROUTER_VERSION_PREFIX}${VERSION_SUFFIX}"
echo "====== BUILDING: Version ${VERSION} (Router ${ROUTER_VERSION})"
export NODE_PATH=${NODE_PATH}:$(pwd)/dist/all:$(pwd)/dist/tools
TSC="node --max-old-space-size=3000 dist/tools/@angular/tsc-wrapped/src/main"
UGLIFYJS=`pwd`/node_modules/.bin/uglifyjs
@ -106,7 +119,13 @@ do
UMD_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.min.js
UMD_STATIC_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}-static.umd.min.js
UMD_UPGRADE_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}-upgrade.umd.min.js
if [[ ${PACKAGE} != router ]]; then
LICENSE_BANNER=${PWD}/modules/@angular/license-banner.txt
fi
if [[ ${PACKAGE} == router ]]; then
LICENSE_BANNER=${PWD}/modules/@angular/router-license-banner.txt
fi
rm -rf ${DESTDIR}
@ -126,6 +145,11 @@ do
$TSC -p ${SRCDIR}/tsconfig-testing.json
fi
if [[ -e ${SRCDIR}/tsconfig-2015.json ]]; then
echo "====== COMPILING ESM: ${TSC} -p ${SRCDIR}/tsconfig-2015.json"
${TSC} -p ${SRCDIR}/tsconfig-2015.json
fi
echo "====== TSC 1.8 d.ts compat for ${DESTDIR} ====="
# safely strips 'readonly' specifier from d.ts files to make them compatible with tsc 1.8
if [ "$(uname)" == "Darwin" ]; then
@ -157,7 +181,6 @@ do
mv ${UMD_ES5_PATH}.tmp ${UMD_ES5_PATH}
$UGLIFYJS -c --screw-ie8 --comments -o ${UMD_ES5_MIN_PATH} ${UMD_ES5_PATH}
if [[ -e rollup-testing.config.js ]]; then
echo "====== Rollup ${PACKAGE} testing"
../../../node_modules/.bin/rollup -c rollup-testing.config.js
@ -191,8 +214,24 @@ do
$UGLIFYJS -c --screw-ie8 --comments -o ${UMD_UPGRADE_ES5_MIN_PATH} ${UMD_UPGRADE_ES5_PATH}
fi
) 2>&1 | grep -v "as external dependency"
fi
(
echo "====== VERSION: Updating version references"
cd ${DESTDIR}
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
echo "====== EXECUTE: perl -p -i -e \"s/0\.0\.0\-ROUTERPLACEHOLDER/${ROUTER_VERSION}/g\" $""(grep -ril 0\.0\.0\-ROUTERPLACEHOLDER .)"
perl -p -i -e "s/0\.0\.0\-ROUTERPLACEHOLDER/${ROUTER_VERSION}/g" $(grep -ril 0\.0\.0\-ROUTERPLACEHOLDER .) < /dev/null 2> /dev/null
)
done
echo ""
echo "====== Building examples: ./modules/@angular/examples/build.sh ====="
./modules/@angular/examples/build.sh
if [[ ${REMOVE_BENCHPRESS} == true ]]; then
echo ""
echo "==== Removing benchpress from publication"
rm -r dist/packages-dist/benchpress
fi

View File

@ -4,7 +4,7 @@ machine:
dependencies:
pre:
- npm install -g npm
- npm install -g npm@3.6.0
test:
override:

View File

@ -24,17 +24,25 @@ module.exports = function(config) {
'node_modules/core-js/client/core.js',
// include Angular v1 for upgrade module testing
'node_modules/angular/angular.js',
'node_modules/angular-mocks/angular-mocks.js',
'node_modules/zone.js/dist/zone.js', 'node_modules/zone.js/dist/long-stack-trace-zone.js',
'node_modules/zone.js/dist/proxy.js', 'node_modules/zone.js/dist/sync-test.js',
'node_modules/zone.js/dist/jasmine-patch.js', 'node_modules/zone.js/dist/async-test.js',
'node_modules/zone.js/dist/zone.js',
'node_modules/zone.js/dist/long-stack-trace-zone.js',
'node_modules/zone.js/dist/proxy.js',
'node_modules/zone.js/dist/sync-test.js',
'node_modules/zone.js/dist/jasmine-patch.js',
'node_modules/zone.js/dist/async-test.js',
'node_modules/zone.js/dist/fake-async-test.js',
// Including systemjs because it defines `__eval`, which produces correct stack traces.
'shims_for_IE.js', 'node_modules/systemjs/dist/system.src.js',
'shims_for_IE.js',
'node_modules/systemjs/dist/system.src.js',
{pattern: 'node_modules/rxjs/**', included: false, watched: false, served: true},
'node_modules/reflect-metadata/Reflect.js', 'tools/build/file2modulename.js', 'test-main.js',
{pattern: 'dist/all/empty.*', included: false, watched: false}, {
'node_modules/reflect-metadata/Reflect.js',
'tools/build/file2modulename.js',
'test-main.js',
{pattern: 'dist/all/empty.*', included: false, watched: false},
{
pattern: 'modules/@angular/platform-browser/test/static_assets/**',
included: false,
watched: false
@ -48,11 +56,15 @@ module.exports = function(config) {
exclude: [
'dist/all/@angular/**/e2e_test/**',
'dist/all/@angular/router/**',
'dist/all/@angular/compiler-cli/**',
'dist/all/@angular/**/*node_only_spec.js',
'dist/all/@angular/benchpress/**',
'dist/all/angular1_router.js',
'dist/all/@angular/compiler-cli/**',
'dist/all/@angular/compiler/test/aot/**',
'dist/all/@angular/examples/**/e2e_test/*',
'dist/all/@angular/language-service/**',
'dist/all/@angular/router/**',
'dist/all/@angular/platform-browser/testing/e2e_util.js',
'dist/all/angular1_router.js',
'dist/examples/**/e2e_test/**',
],

View File

@ -7,7 +7,7 @@
"dependencies": {
"@angular/core": "^2.0.0-rc.7",
"reflect-metadata": "^0.1.2",
"rxjs": "5.0.0-beta.12",
"rxjs": "^5.0.1",
"jpm": "1.1.4",
"firefox-profile": "0.4.0",
"selenium-webdriver": "^2.53.3"

View File

@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
/* tslint:disable:no-console */
import {browser} from 'protractor';
const assertEventsContainsName = function(events: any[], eventName: string) {

View File

@ -11,9 +11,6 @@
* @description
* Entry point for all public APIs of the common package.
*/
export * from './src/location';
export {NgLocalization} from './src/localization';
export {CommonModule} from './src/common_module';
export * from './src/common';
export {NgClass, NgFor, NgIf, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet} from './src/directives/index';
export {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe} from './src/pipes/index';
// This file only reexports content of the `src` folder. Keep it that way.

View File

@ -0,0 +1,20 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* @module
* @description
* Entry point for all public APIs of the common package.
*/
export * from './location/index';
export {NgLocalization} from './localization';
export {CommonModule} from './common_module';
export {NgClass, NgFor, NgIf, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet} from './directives/index';
export {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe} from './pipes/index';
export {VERSION} from './version';
export {Version} from '@angular/core';

View File

@ -9,7 +9,7 @@
import {CollectionChangeRecord, Directive, DoCheck, ElementRef, Input, IterableDiffer, IterableDiffers, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
import {isListLikeIterable} from '../facade/collection';
import {isPresent, stringify} from '../facade/lang';
import {stringify} from '../facade/lang';
/**
* @ngModule CommonModule
@ -25,6 +25,8 @@ import {isPresent, stringify} from '../facade/lang';
* <some-element [ngClass]="{'first': true, 'second': true, 'third': false}">...</some-element>
*
* <some-element [ngClass]="stringExp|arrayExp|objExp">...</some-element>
*
* <some-element [ngClass]="{'class1 class2 class3' : true}">...</some-element>
* ```
*
* @description
@ -132,7 +134,7 @@ export class NgClass implements DoCheck {
(<any>rawClassVal).forEach((klass: string) => this._toggleClass(klass, !isCleanup));
} else {
Object.keys(rawClassVal).forEach(klass => {
if (isPresent(rawClassVal[klass])) this._toggleClass(klass, !isCleanup);
if (rawClassVal[klass] != null) this._toggleClass(klass, !isCleanup);
});
}
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ChangeDetectorRef, CollectionChangeRecord, DefaultIterableDiffer, Directive, DoCheck, EmbeddedViewRef, Input, IterableDiffer, IterableDiffers, OnChanges, SimpleChanges, TemplateRef, TrackByFn, ViewContainerRef} from '@angular/core';
import {ChangeDetectorRef, CollectionChangeRecord, DefaultIterableDiffer, Directive, DoCheck, EmbeddedViewRef, Input, IterableDiffer, IterableDiffers, OnChanges, SimpleChanges, TemplateRef, TrackByFn, ViewContainerRef, isDevMode} from '@angular/core';
import {getTypeNameForDebugging} from '../facade/lang';
@ -89,9 +89,23 @@ export class NgForRow {
@Directive({selector: '[ngFor][ngForOf]'})
export class NgFor implements DoCheck, OnChanges {
@Input() ngForOf: any;
@Input() ngForTrackBy: TrackByFn;
@Input()
set ngForTrackBy(fn: TrackByFn) {
if (isDevMode() && fn != null && typeof fn !== 'function') {
// TODO(vicb): use a log service once there is a public one available
if (<any>console && <any>console.warn) {
console.warn(
`trackBy must be a function, but received ${JSON.stringify(fn)}. ` +
`See https://angular.io/docs/ts/latest/api/common/index/NgFor-directive.html#!#change-propagation for more information.`);
}
}
this._trackByFn = fn;
}
get ngForTrackBy(): TrackByFn { return this._trackByFn; }
private _differ: IterableDiffer = null;
private _trackByFn: TrackByFn;
constructor(
private _viewContainer: ViewContainerRef, private _template: TemplateRef<NgForRow>,
@ -119,7 +133,7 @@ export class NgFor implements DoCheck, OnChanges {
}
}
ngDoCheck() {
ngDoCheck(): void {
if (this._differ) {
const changes = this._differ.diff(this.ngForOf);
if (changes) this._applyChanges(changes);

View File

@ -21,10 +21,9 @@ import {SwitchView} from './ng_switch';
* @howToUse
* ```
* <some-element [ngPlural]="value">
* <ng-container *ngPluralCase="'=0'">there is nothing</ng-container>
* <ng-container *ngPluralCase="'=1'">there is one</ng-container>
* <ng-container *ngPluralCase="'few'">there are a few</ng-container>
* <ng-container *ngPluralCase="'other'">there are exactly #</ng-container>
* <template ngPluralCase="=0">there is nothing</template>
* <template ngPluralCase="=1">there is one</template>
* <template ngPluralCase="few">there are a few</template>
* </some-element>
* ```
*
@ -90,8 +89,8 @@ export class NgPlural {
* @howToUse
* ```
* <some-element [ngPlural]="value">
* <ng-container *ngPluralCase="'=0'">...</ng-container>
* <ng-container *ngPluralCase="'other'">...</ng-container>
* <template ngPluralCase="=0">...</template>
* <template ngPluralCase="other">...</template>
* </some-element>
*```
*
@ -104,6 +103,7 @@ export class NgPluralCase {
constructor(
@Attribute('ngPluralCase') public value: string, template: TemplateRef<Object>,
viewContainer: ViewContainerRef, @Host() ngPlural: NgPlural) {
ngPlural.addCase(value, new SwitchView(viewContainer, template));
const isANumber: boolean = !isNaN(Number(value));
ngPlural.addCase(isANumber ? `=${value}` : value, new SwitchView(viewContainer, template));
}
}

View File

@ -23,9 +23,23 @@ export abstract class NgLocalization { abstract getPluralCategory(value: any): s
*/
export function getPluralCategory(
value: number, cases: string[], ngLocalization: NgLocalization): string {
const nbCase = `=${value}`;
let key = `=${value}`;
return cases.indexOf(nbCase) > -1 ? nbCase : ngLocalization.getPluralCategory(value);
if (cases.indexOf(key) > -1) {
return key;
}
key = ngLocalization.getPluralCategory(value);
if (cases.indexOf(key) > -1) {
return key;
}
if (cases.indexOf('other') > -1) {
return 'other';
}
throw new Error(`No plural message found for value "${value}"`);
}
/**

View File

@ -1,13 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
export * from './location/platform_location';
export * from './location/location_strategy';
export * from './location/hash_location_strategy';
export * from './location/path_location_strategy';
export * from './location/location';

View File

@ -17,6 +17,8 @@ import {LocationChangeListener, PlatformLocation} from './platform_location';
/**
* @whatItDoes Use URL hash for storing application location data.
* @description
* `HashLocationStrategy` is a {@link LocationStrategy} used to configure the
* {@link Location} service to represent its state in the
* [hash fragment](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax)
@ -27,18 +29,7 @@ import {LocationChangeListener, PlatformLocation} from './platform_location';
*
* ### Example
*
* ```
* import {Component, NgModule} from '@angular/core';
* import {
* LocationStrategy,
* HashLocationStrategy
* } from '@angular/common';
*
* @NgModule({
* providers: [{provide: LocationStrategy, useClass: HashLocationStrategy}]
* })
* class AppModule {}
* ```
* {@example common/location/ts/hash_location_component.ts region='LocationComponent'}
*
* @stable
*/

View File

@ -0,0 +1,13 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
export * from './platform_location';
export * from './location_strategy';
export * from './hash_location_strategy';
export * from './path_location_strategy';
export * from './location';

View File

@ -12,7 +12,8 @@ import {LocationStrategy} from './location_strategy';
/**
* `Location` is a service that applications can use to interact with a browser's URL.
* @whatItDoes `Location` is a service that applications can use to interact with a browser's URL.
* @description
* Depending on which {@link LocationStrategy} is used, `Location` will either persist
* to the URL's path or the URL's hash segment.
*
@ -28,19 +29,7 @@ import {LocationStrategy} from './location_strategy';
* - `/my/app/user/123/` **is not** normalized
*
* ### Example
*
* ```
* import {Component} from '@angular/core';
* import {Location} from '@angular/common';
*
* @Component({selector: 'app-component'})
* class AppCmp {
* constructor(location: Location) {
* location.go('/foo');
* }
* }
* ```
*
* {@example common/location/ts/path_location_component.ts region='LocationComponent'}
* @stable
*/
@Injectable()
@ -96,7 +85,7 @@ export class Location {
* used, or the `APP_BASE_HREF` if the `PathLocationStrategy` is in use.
*/
prepareExternalUrl(url: string): string {
if (url.length > 0 && !url.startsWith('/')) {
if (url && url[0] !== '/') {
url = '/' + url;
}
return this._platformStrategy.prepareExternalUrl(url);
@ -143,7 +132,7 @@ export class Location {
* is.
*/
public static normalizeQueryParams(params: string): string {
return (params.length > 0 && params.substring(0, 1) != '?') ? ('?' + params) : params;
return params && params[0] !== '?' ? '?' + params : params;
}
/**
@ -175,25 +164,13 @@ export class Location {
/**
* If url has a trailing slash, remove it, otherwise return url as is.
*/
public static stripTrailingSlash(url: string): string {
if (/\/$/g.test(url)) {
url = url.substring(0, url.length - 1);
}
return url;
}
public static stripTrailingSlash(url: string): string { return url.replace(/\/$/, ''); }
}
function _stripBaseHref(baseHref: string, url: string): string {
if (baseHref.length > 0 && url.startsWith(baseHref)) {
return url.substring(baseHref.length);
}
return url;
return baseHref && url.startsWith(baseHref) ? url.substring(baseHref.length) : url;
}
function _stripIndexHtml(url: string): string {
if (/\/index.html$/g.test(url)) {
// '/index.html'.length == 11
return url.substring(0, url.length - 11);
}
return url;
return url.replace(/\/index.html$/, '');
}

View File

@ -12,7 +12,7 @@ import {LocationChangeListener} from './platform_location';
/**
* `LocationStrategy` is responsible for representing and reading route state
* from the browser's URL. Angular provides two strategies:
* {@link HashLocationStrategy} and {@link PathLocationStrategy} (default).
* {@link HashLocationStrategy} and {@link PathLocationStrategy}.
*
* This is used under the hood of the {@link Location} service.
*

View File

@ -17,14 +17,13 @@ import {LocationChangeListener, PlatformLocation} from './platform_location';
/**
* @whatItDoes Use URL for storing application location data.
* @description
* `PathLocationStrategy` is a {@link LocationStrategy} used to configure the
* {@link Location} service to represent its state in the
* [path](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) of the
* browser's URL.
*
* `PathLocationStrategy` is the default binding for {@link LocationStrategy}
* provided in {@link ROUTER_PROVIDERS}.
*
* If you're using `PathLocationStrategy`, you must provide a {@link APP_BASE_HREF}
* or add a base element to the document. This URL prefix that will be preserved
* when generating and recognizing URLs.
@ -37,6 +36,10 @@ import {LocationChangeListener, PlatformLocation} from './platform_location';
* `location.go('/foo')`, the browser's URL will become
* `example.com/my/app/foo`.
*
* ### Example
*
* {@example common/location/ts/path_location_component.ts region='LocationComponent'}
*
* @stable
*/
@Injectable()

View File

@ -59,7 +59,7 @@ const _observableStrategy = new ObservableStrategy();
* {@example common/pipes/ts/async_pipe.ts region='AsyncPipePromise'}
*
* It's also possible to use `async` with Observables. The example below binds the `time` Observable
* to the view. The Observable continuesly updates the view with the current time.
* to the view. The Observable continuously updates the view with the current time.
*
* {@example common/pipes/ts/async_pipe.ts region='AsyncPipeObservable'}
*

View File

@ -7,11 +7,14 @@
*/
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
import {DateFormatter} from '../facade/intl';
import {NumberWrapper, isDate} from '../facade/lang';
import {DateFormatter} from './intl';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
/**
* @ngModule CommonModule
* @whatItDoes Formats a date according to locale rules.

View File

@ -7,7 +7,6 @@
*/
import {Pipe, PipeTransform} from '@angular/core';
import {isBlank} from '../facade/lang';
import {NgLocalization, getPluralCategory} from '../localization';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
@ -35,7 +34,7 @@ export class I18nPluralPipe implements PipeTransform {
constructor(private _localization: NgLocalization) {}
transform(value: number, pluralMap: {[count: string]: string}): string {
if (isBlank(value)) return '';
if (value == null) return '';
if (typeof pluralMap !== 'object' || pluralMap === null) {
throw new InvalidPipeArgumentError(I18nPluralPipe, pluralMap);

View File

@ -44,7 +44,8 @@ const DATE_FORMATS_SPLIT =
/((?:[^yMLdHhmsazZEwGjJ']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|J+|j+|m+|s+|a|z|Z|G+|w+))(.*)/;
const PATTERN_ALIASES: {[format: string]: DateFormatterFn} = {
yMMMdjms: datePartGetterFactory(combine([
// Keys are quoted so they do not get renamed during closure compilation.
'yMMMdjms': datePartGetterFactory(combine([
digitCondition('year', 1),
nameCondition('month', 3),
digitCondition('day', 1),
@ -52,68 +53,70 @@ const PATTERN_ALIASES: {[format: string]: DateFormatterFn} = {
digitCondition('minute', 1),
digitCondition('second', 1),
])),
yMdjm: datePartGetterFactory(combine([
'yMdjm': datePartGetterFactory(combine([
digitCondition('year', 1), digitCondition('month', 1), digitCondition('day', 1),
digitCondition('hour', 1), digitCondition('minute', 1)
])),
yMMMMEEEEd: datePartGetterFactory(combine([
'yMMMMEEEEd': datePartGetterFactory(combine([
digitCondition('year', 1), nameCondition('month', 4), nameCondition('weekday', 4),
digitCondition('day', 1)
])),
yMMMMd: datePartGetterFactory(
'yMMMMd': datePartGetterFactory(
combine([digitCondition('year', 1), nameCondition('month', 4), digitCondition('day', 1)])),
yMMMd: datePartGetterFactory(
'yMMMd': datePartGetterFactory(
combine([digitCondition('year', 1), nameCondition('month', 3), digitCondition('day', 1)])),
yMd: datePartGetterFactory(
'yMd': datePartGetterFactory(
combine([digitCondition('year', 1), digitCondition('month', 1), digitCondition('day', 1)])),
jms: datePartGetterFactory(combine(
'jms': datePartGetterFactory(combine(
[digitCondition('hour', 1), digitCondition('second', 1), digitCondition('minute', 1)])),
jm: datePartGetterFactory(combine([digitCondition('hour', 1), digitCondition('minute', 1)]))
'jm': datePartGetterFactory(combine([digitCondition('hour', 1), digitCondition('minute', 1)]))
};
const DATE_FORMATS: {[format: string]: DateFormatterFn} = {
yyyy: datePartGetterFactory(digitCondition('year', 4)),
yy: datePartGetterFactory(digitCondition('year', 2)),
y: datePartGetterFactory(digitCondition('year', 1)),
MMMM: datePartGetterFactory(nameCondition('month', 4)),
MMM: datePartGetterFactory(nameCondition('month', 3)),
MM: datePartGetterFactory(digitCondition('month', 2)),
M: datePartGetterFactory(digitCondition('month', 1)),
LLLL: datePartGetterFactory(nameCondition('month', 4)),
L: datePartGetterFactory(nameCondition('month', 1)),
dd: datePartGetterFactory(digitCondition('day', 2)),
d: datePartGetterFactory(digitCondition('day', 1)),
HH: digitModifier(
// Keys are quoted so they do not get renamed.
'yyyy': datePartGetterFactory(digitCondition('year', 4)),
'yy': datePartGetterFactory(digitCondition('year', 2)),
'y': datePartGetterFactory(digitCondition('year', 1)),
'MMMM': datePartGetterFactory(nameCondition('month', 4)),
'MMM': datePartGetterFactory(nameCondition('month', 3)),
'MM': datePartGetterFactory(digitCondition('month', 2)),
'M': datePartGetterFactory(digitCondition('month', 1)),
'LLLL': datePartGetterFactory(nameCondition('month', 4)),
'L': datePartGetterFactory(nameCondition('month', 1)),
'dd': datePartGetterFactory(digitCondition('day', 2)),
'd': datePartGetterFactory(digitCondition('day', 1)),
'HH': digitModifier(
hourExtractor(datePartGetterFactory(hour12Modify(digitCondition('hour', 2), false)))),
H: hourExtractor(datePartGetterFactory(hour12Modify(digitCondition('hour', 1), false))),
hh: digitModifier(
'H': hourExtractor(datePartGetterFactory(hour12Modify(digitCondition('hour', 1), false))),
'hh': digitModifier(
hourExtractor(datePartGetterFactory(hour12Modify(digitCondition('hour', 2), true)))),
h: hourExtractor(datePartGetterFactory(hour12Modify(digitCondition('hour', 1), true))),
jj: datePartGetterFactory(digitCondition('hour', 2)),
j: datePartGetterFactory(digitCondition('hour', 1)),
mm: digitModifier(datePartGetterFactory(digitCondition('minute', 2))),
m: datePartGetterFactory(digitCondition('minute', 1)),
ss: digitModifier(datePartGetterFactory(digitCondition('second', 2))),
s: datePartGetterFactory(digitCondition('second', 1)),
'h': hourExtractor(datePartGetterFactory(hour12Modify(digitCondition('hour', 1), true))),
'jj': datePartGetterFactory(digitCondition('hour', 2)),
'j': datePartGetterFactory(digitCondition('hour', 1)),
'mm': digitModifier(datePartGetterFactory(digitCondition('minute', 2))),
'm': datePartGetterFactory(digitCondition('minute', 1)),
'ss': digitModifier(datePartGetterFactory(digitCondition('second', 2))),
's': datePartGetterFactory(digitCondition('second', 1)),
// while ISO 8601 requires fractions to be prefixed with `.` or `,`
// we can be just safely rely on using `sss` since we currently don't support single or two digit
// fractions
sss: datePartGetterFactory(digitCondition('second', 3)),
EEEE: datePartGetterFactory(nameCondition('weekday', 4)),
EEE: datePartGetterFactory(nameCondition('weekday', 3)),
EE: datePartGetterFactory(nameCondition('weekday', 2)),
E: datePartGetterFactory(nameCondition('weekday', 1)),
a: hourClockExtractor(datePartGetterFactory(hour12Modify(digitCondition('hour', 1), true))),
Z: timeZoneGetter('short'),
z: timeZoneGetter('long'),
ww: datePartGetterFactory({}), // Week of year, padded (00-53). Week 01 is the week with the
'sss': datePartGetterFactory(digitCondition('second', 3)),
'EEEE': datePartGetterFactory(nameCondition('weekday', 4)),
'EEE': datePartGetterFactory(nameCondition('weekday', 3)),
'EE': datePartGetterFactory(nameCondition('weekday', 2)),
'E': datePartGetterFactory(nameCondition('weekday', 1)),
'a': hourClockExtractor(datePartGetterFactory(hour12Modify(digitCondition('hour', 1), true))),
'Z': timeZoneGetter('short'),
'z': timeZoneGetter('long'),
'ww': datePartGetterFactory({}), // Week of year, padded (00-53). Week 01 is the week with the
// first Thursday of the year. not support ?
w: datePartGetterFactory({}), // Week of year (0-53). Week 1 is the week with the first Thursday
'w':
datePartGetterFactory({}), // Week of year (0-53). Week 1 is the week with the first Thursday
// of the year not support ?
G: datePartGetterFactory(nameCondition('era', 1)),
GG: datePartGetterFactory(nameCondition('era', 2)),
GGG: datePartGetterFactory(nameCondition('era', 3)),
GGGG: datePartGetterFactory(nameCondition('era', 4))
'G': datePartGetterFactory(nameCondition('era', 1)),
'GG': datePartGetterFactory(nameCondition('era', 2)),
'GGG': datePartGetterFactory(nameCondition('era', 3)),
'GGGG': datePartGetterFactory(nameCondition('era', 4))
};
@ -184,7 +187,8 @@ function dateFormatter(format: string, date: Date, locale: string): string {
if (fn) return fn(date, locale);
let parts = DATE_FORMATTER_CACHE.get(format);
const cacheKey = format;
let parts = DATE_FORMATTER_CACHE.get(cacheKey);
if (!parts) {
parts = [];
@ -202,7 +206,7 @@ function dateFormatter(format: string, date: Date, locale: string): string {
}
}
DATE_FORMATTER_CACHE.set(format, parts);
DATE_FORMATTER_CACHE.set(cacheKey, parts);
}
return parts.reduce((text, part) => {

View File

@ -8,9 +8,9 @@
import {Inject, LOCALE_ID, Pipe, PipeTransform, Type} from '@angular/core';
import {NumberFormatStyle, NumberFormatter} from '../facade/intl';
import {NumberWrapper, isBlank, isPresent} from '../facade/lang';
import {NumberWrapper} from '../facade/lang';
import {NumberFormatStyle, NumberFormatter} from './intl';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
const _NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(-(\d+))?)?$/;
@ -18,7 +18,7 @@ const _NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(-(\d+))?)?$/;
function formatNumber(
pipe: Type<any>, locale: string, value: number | string, style: NumberFormatStyle,
digits: string, currency: string = null, currencyAsSymbol: boolean = false): string {
if (isBlank(value)) return null;
if (value == null) return null;
// Convert strings to numbers
value = typeof value === 'string' && NumberWrapper.isNumeric(value) ? +value : value;
@ -41,13 +41,13 @@ function formatNumber(
if (parts === null) {
throw new Error(`${digits} is not a valid digit info for number pipes`);
}
if (isPresent(parts[1])) { // min integer digits
if (parts[1] != null) { // min integer digits
minInt = NumberWrapper.parseIntAutoRadix(parts[1]);
}
if (isPresent(parts[3])) { // min fraction digits
if (parts[3] != null) { // min fraction digits
minFraction = NumberWrapper.parseIntAutoRadix(parts[3]);
}
if (isPresent(parts[5])) { // max fraction digits
if (parts[5] != null) { // max fraction digits
maxFraction = NumberWrapper.parseIntAutoRadix(parts[5]);
}
}

View File

@ -7,7 +7,6 @@
*/
import {Pipe, PipeTransform} from '@angular/core';
import {isBlank} from '../facade/lang';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
/**
@ -58,7 +57,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
@Pipe({name: 'slice', pure: false})
export class SlicePipe implements PipeTransform {
transform(value: any, start: number, end?: number): any {
if (isBlank(value)) return value;
if (value == null) return value;
if (!this.supports(value)) {
throw new InvalidPipeArgumentError(SlicePipe, value);

View File

@ -0,0 +1,19 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* @module
* @description
* Entry point for all public APIs of the common package.
*/
import {Version} from '@angular/core';
/**
* @stable
*/
export const VERSION = new Version('0.0.0-PLACEHOLDER');

View File

@ -7,7 +7,7 @@
*/
import {CommonModule} from '@angular/common';
import {Component, ContentChild, TemplateRef} from '@angular/core';
import {Component} from '@angular/core';
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
import {By} from '@angular/platform-browser/src/dom/debug/by';
import {expect} from '@angular/platform-browser/testing/matchers';
@ -29,10 +29,7 @@ export function main() {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
TestComponent,
ComponentUsingTestComponent,
],
declarations: [TestComponent],
imports: [CommonModule],
});
});
@ -77,7 +74,7 @@ export function main() {
}));
it('should iterate over an array of objects', async(() => {
const template = '<ul><li template="ngFor let item of items">{{item["name"]}};</li></ul>';
const template = '<ul><li *ngFor="let item of items">{{item["name"]}};</li></ul>';
fixture = createTestComponent(template);
// INIT
@ -95,7 +92,7 @@ export function main() {
}));
it('should gracefully handle nulls', async(() => {
const template = '<ul><li template="ngFor let item of null">{{item}};</li></ul>';
const template = '<ul><li *ngFor="let item of null">{{item}};</li></ul>';
fixture = createTestComponent(template);
detectChangesAndExpectText('');
@ -140,12 +137,8 @@ export function main() {
}));
it('should repeat over nested arrays', async(() => {
const template = '<div>' +
'<div template="ngFor let item of items">' +
'<div template="ngFor let subitem of item">' +
'{{subitem}}-{{item.length}};' +
'</div>|' +
'</div>' +
const template = '<div *ngFor="let item of items">' +
'<div *ngFor="let subitem of item">{{subitem}}-{{item.length}};</div>|' +
'</div>';
fixture = createTestComponent(template);
@ -157,10 +150,9 @@ export function main() {
}));
it('should repeat over nested arrays with no intermediate element', async(() => {
const template = '<div><template ngFor let-item [ngForOf]="items">' +
'<div template="ngFor let subitem of item">' +
'{{subitem}}-{{item.length}};' +
'</div></template></div>';
const template = '<div *ngFor="let item of items">' +
'<div *ngFor="let subitem of item">{{subitem}}-{{item.length}};</div>' +
'</div>';
fixture = createTestComponent(template);
getComponent().items = [['a', 'b'], ['c']];
@ -170,10 +162,11 @@ export function main() {
detectChangesAndExpectText('e-1;f-2;g-2;');
}));
it('should repeat over nested ngIf that are the last node in the ngFor temlate', async(() => {
const template =
`<div><template ngFor let-item [ngForOf]="items" let-i="index"><div>{{i}}|</div>` +
`<div *ngIf="i % 2 == 0">even|</div></template></div>`;
it('should repeat over nested ngIf that are the last node in the ngFor template', async(() => {
const template = `<div *ngFor="let item of items; let i=index">` +
`<div>{{i}}|</div>` +
`<div *ngIf="i % 2 == 0">even|</div>` +
`</div>`;
fixture = createTestComponent(template);
@ -189,8 +182,7 @@ export function main() {
}));
it('should display indices correctly', async(() => {
const template =
'<div><span template="ngFor: let item of items; let i=index">{{i.toString()}}</span></div>';
const template = '<span *ngFor ="let item of items; let i=index">{{i.toString()}}</span>';
fixture = createTestComponent(template);
getComponent().items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
@ -202,7 +194,7 @@ export function main() {
it('should display first item correctly', async(() => {
const template =
'<div><span template="ngFor: let item of items; let isFirst=first">{{isFirst.toString()}}</span></div>';
'<span *ngFor="let item of items; let isFirst=first">{{isFirst.toString()}}</span>';
fixture = createTestComponent(template);
getComponent().items = [0, 1, 2];
@ -214,7 +206,7 @@ export function main() {
it('should display last item correctly', async(() => {
const template =
'<div><span template="ngFor: let item of items; let isLast=last">{{isLast.toString()}}</span></div>';
'<span *ngFor="let item of items; let isLast=last">{{isLast.toString()}}</span>';
fixture = createTestComponent(template);
getComponent().items = [0, 1, 2];
@ -226,7 +218,7 @@ export function main() {
it('should display even items correctly', async(() => {
const template =
'<div><span template="ngFor: let item of items; let isEven=even">{{isEven.toString()}}</span></div>';
'<span *ngFor="let item of items; let isEven=even">{{isEven.toString()}}</span>';
fixture = createTestComponent(template);
getComponent().items = [0, 1, 2];
@ -238,7 +230,7 @@ export function main() {
it('should display odd items correctly', async(() => {
const template =
'<div><span template="ngFor: let item of items; let isOdd=odd">{{isOdd.toString()}}</span></div>';
'<span *ngFor="let item of items; let isOdd=odd">{{isOdd.toString()}}</span>';
fixture = createTestComponent(template);
getComponent().items = [0, 1, 2, 3];
@ -249,54 +241,57 @@ export function main() {
}));
it('should allow to use a custom template', async(() => {
const tcTemplate =
'<ul><template ngFor [ngForOf]="items" [ngForTemplate]="contentTpl"></template></ul>';
TestBed.overrideComponent(TestComponent, {set: {template: tcTemplate}});
const cutTemplate =
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>';
TestBed.overrideComponent(ComponentUsingTestComponent, {set: {template: cutTemplate}});
fixture = TestBed.createComponent(ComponentUsingTestComponent);
const testComponent = fixture.debugElement.children[0];
testComponent.componentInstance.items = ['a', 'b', 'c'];
const template =
'<ng-container *ngFor="let item of items; template: tpl"></ng-container>' +
'<template let-item let-i="index" #tpl><p>{{i}}: {{item}};</p></template>';
fixture = createTestComponent(template);
getComponent().items = ['a', 'b', 'c'];
fixture.detectChanges();
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
detectChangesAndExpectText('0: a;1: b;2: c;');
}));
it('should use a default template if a custom one is null', async(() => {
const testTemplate = `<ul><template ngFor let-item [ngForOf]="items"
[ngForTemplate]="contentTpl" let-i="index">{{i}}: {{item}};</template></ul>`;
TestBed.overrideComponent(TestComponent, {set: {template: testTemplate}});
const cutTemplate =
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>';
TestBed.overrideComponent(ComponentUsingTestComponent, {set: {template: cutTemplate}});
fixture = TestBed.createComponent(ComponentUsingTestComponent);
const testComponent = fixture.debugElement.children[0];
testComponent.componentInstance.items = ['a', 'b', 'c'];
const template =
`<ul><ng-container *ngFor="let item of items; template: null; let i=index">{{i}}: {{item}};</ng-container></ul>`;
fixture = createTestComponent(template);
getComponent().items = ['a', 'b', 'c'];
fixture.detectChanges();
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
detectChangesAndExpectText('0: a;1: b;2: c;');
}));
it('should use a custom template when both default and a custom one are present', async(() => {
const testTemplate = `<ul><template ngFor let-item [ngForOf]="items"
[ngForTemplate]="contentTpl" let-i="index">{{i}}=> {{item}};</template></ul>`;
TestBed.overrideComponent(TestComponent, {set: {template: testTemplate}});
const cutTemplate =
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>';
TestBed.overrideComponent(ComponentUsingTestComponent, {set: {template: cutTemplate}});
fixture = TestBed.createComponent(ComponentUsingTestComponent);
const testComponent = fixture.debugElement.children[0];
testComponent.componentInstance.items = ['a', 'b', 'c'];
const template =
'<ng-container *ngFor="let item of items; template: tpl">{{i}};</ng-container>' +
'<template let-item let-i="index" #tpl>{{i}}: {{item}};</template>';
fixture = createTestComponent(template);
getComponent().items = ['a', 'b', 'c'];
fixture.detectChanges();
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
detectChangesAndExpectText('0: a;1: b;2: c;');
}));
describe('track by', () => {
it('should console.warn if trackBy is not a function', async(() => {
// TODO(vicb): expect a warning message when we have a proper log service
const template = `<p *ngFor="let item of items; trackBy: value"></p>`;
fixture = createTestComponent(template);
fixture.componentInstance.value = 0;
fixture.detectChanges();
}));
it('should track by identity when trackBy is to `null` or `undefined`', async(() => {
// TODO(vicb): expect no warning message when we have a proper log service
const template = `<p *ngFor="let item of items; trackBy: value">{{ item }}</p>`;
fixture = createTestComponent(template);
fixture.componentInstance.items = ['a', 'b', 'c'];
fixture.componentInstance.value = null;
detectChangesAndExpectText('abc');
fixture.componentInstance.value = undefined;
detectChangesAndExpectText('abc');
}));
it('should set the context to the component instance', async(() => {
const template =
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackByContext.bind(this)"></template>`;
`<p *ngFor="let item of items; trackBy: trackByContext.bind(this)"></p>`;
fixture = createTestComponent(template);
thisArg = null;
@ -306,9 +301,7 @@ export function main() {
it('should not replace tracked items', async(() => {
const template =
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById" let-i="index">
<p>{{items[i]}}</p>
</template>`;
`<p *ngFor="let item of items; trackBy: trackById; let i=index">{{items[i]}}</p>`;
fixture = createTestComponent(template);
const buildItemList = () => {
@ -324,7 +317,7 @@ export function main() {
it('should update implicit local variable on view', async(() => {
const template =
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
`<div *ngFor="let item of items; trackBy: trackById">{{item['color']}}</div>`;
fixture = createTestComponent(template);
getComponent().items = [{'id': 'a', 'color': 'blue'}];
@ -333,9 +326,10 @@ export function main() {
getComponent().items = [{'id': 'a', 'color': 'red'}];
detectChangesAndExpectText('red');
}));
it('should move items around and keep them updated ', async(() => {
const template =
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
`<div *ngFor="let item of items; trackBy: trackById">{{item['color']}}</div>`;
fixture = createTestComponent(template);
getComponent().items = [{'id': 'a', 'color': 'blue'}, {'id': 'b', 'color': 'yellow'}];
@ -346,8 +340,7 @@ export function main() {
}));
it('should handle added and removed items properly when tracking by index', async(() => {
const template =
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackByIndex">{{item}}</template></div>`;
const template = `<div *ngFor="let item of items; trackBy: trackByIndex">{{item}}</div>`;
fixture = createTestComponent(template);
getComponent().items = ['a', 'b', 'c', 'd'];
@ -367,19 +360,14 @@ class Foo {
@Component({selector: 'test-cmp', template: ''})
class TestComponent {
@ContentChild(TemplateRef) contentTpl: TemplateRef<Object>;
value: any;
items: any[] = [1, 2];
trackById(index: number, item: any): string { return item['id']; }
trackByIndex(index: number, item: any): number { return index; }
trackByContext(): void { thisArg = this; }
}
@Component({selector: 'outer-cmp', template: ''})
class ComponentUsingTestComponent {
items: any = [1, 2];
}
const TEMPLATE = '<div><span template="ngFor let item of items">{{item.toString()}};</span></div>';
const TEMPLATE = '<div><span *ngFor="let item of items">{{item.toString()}};</span></div>';
function createTestComponent(template: string = TEMPLATE): ComponentFixture<TestComponent> {
return TestBed.overrideComponent(TestComponent, {set: {template: template}})

View File

@ -9,6 +9,7 @@
import {CommonModule} from '@angular/common';
import {Component} from '@angular/core';
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
import {By} from '@angular/platform-browser/src/dom/debug/by';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {expect} from '@angular/platform-browser/testing/matchers';
@ -28,131 +29,114 @@ export function main() {
});
it('should work in a template attribute', async(() => {
const template = '<div><span template="ngIf booleanCondition">hello</span></div>';
const template = '<span *ngIf="booleanCondition">hello</span>';
fixture = createTestComponent(template);
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
expect(fixture.nativeElement).toHaveText('hello');
}));
it('should work in a template element', async(() => {
const template =
'<div><template [ngIf]="booleanCondition"><span>hello2</span></template></div>';
it('should work on a template element', async(() => {
const template = '<template [ngIf]="booleanCondition">hello2</template>';
fixture = createTestComponent(template);
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
expect(fixture.nativeElement).toHaveText('hello2');
}));
it('should toggle node when condition changes', async(() => {
const template = '<div><span template="ngIf booleanCondition">hello</span></div>';
const template = '<span *ngIf="booleanCondition">hello</span>';
fixture = createTestComponent(template);
getComponent().booleanCondition = false;
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(0);
expect(fixture.nativeElement).toHaveText('');
getComponent().booleanCondition = true;
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
expect(fixture.nativeElement).toHaveText('hello');
getComponent().booleanCondition = false;
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(0);
expect(fixture.nativeElement).toHaveText('');
}));
it('should handle nested if correctly', async(() => {
const template =
'<div><template [ngIf]="booleanCondition"><span *ngIf="nestedBooleanCondition">hello</span></template></div>';
'<div *ngIf="booleanCondition"><span *ngIf="nestedBooleanCondition">hello</span></div>';
fixture = createTestComponent(template);
getComponent().booleanCondition = false;
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(0);
expect(fixture.nativeElement).toHaveText('');
getComponent().booleanCondition = true;
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
expect(fixture.nativeElement).toHaveText('hello');
getComponent().nestedBooleanCondition = false;
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(0);
expect(fixture.nativeElement).toHaveText('');
getComponent().nestedBooleanCondition = true;
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
expect(fixture.nativeElement).toHaveText('hello');
getComponent().booleanCondition = false;
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(0);
expect(fixture.nativeElement).toHaveText('');
}));
it('should update several nodes with if', async(() => {
const template = '<div>' +
'<span template="ngIf numberCondition + 1 >= 2">helloNumber</span>' +
'<span template="ngIf stringCondition == \'foo\'">helloString</span>' +
'<span template="ngIf functionCondition(stringCondition, numberCondition)">helloFunction</span>' +
'</div>';
const template = '<span *ngIf="numberCondition + 1 >= 2">helloNumber</span>' +
'<span *ngIf="stringCondition == \'foo\'">helloString</span>' +
'<span *ngIf="functionCondition(stringCondition, numberCondition)">helloFunction</span>';
fixture = createTestComponent(template);
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(3);
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(3);
expect(getDOM().getText(fixture.nativeElement))
.toEqual('helloNumberhelloStringhelloFunction');
getComponent().numberCondition = 0;
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
expect(fixture.nativeElement).toHaveText('helloString');
getComponent().numberCondition = 1;
getComponent().stringCondition = 'bar';
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
expect(fixture.nativeElement).toHaveText('helloNumber');
}));
it('should not add the element twice if the condition goes from true to true (JS)',
async(() => {
const template = '<div><span template="ngIf numberCondition">hello</span></div>';
it('should not add the element twice if the condition goes from truthy to truthy', async(() => {
const template = '<span *ngIf="numberCondition">hello</span>';
fixture = createTestComponent(template);
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
let els = fixture.debugElement.queryAll(By.css('span'));
expect(els.length).toEqual(1);
getDOM().addClass(els[0].nativeElement, 'marker');
expect(fixture.nativeElement).toHaveText('hello');
getComponent().numberCondition = 2;
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
els = fixture.debugElement.queryAll(By.css('span'));
expect(els.length).toEqual(1);
expect(getDOM().hasClass(els[0].nativeElement, 'marker')).toBe(true);
expect(fixture.nativeElement).toHaveText('hello');
}));
it('should not recreate the element if the condition goes from true to true (JS)', async(() => {
const template = '<div><span template="ngIf numberCondition">hello</span></div>';
fixture = createTestComponent(template);
fixture.detectChanges();
getDOM().addClass(getDOM().querySelector(fixture.nativeElement, 'span'), 'foo');
getComponent().numberCondition = 2;
fixture.detectChanges();
expect(getDOM().hasClass(getDOM().querySelector(fixture.nativeElement, 'span'), 'foo'))
.toBe(true);
}));
});
}

View File

@ -12,7 +12,7 @@ import {ComponentFixture, TestBed, async} from '@angular/core/testing';
import {expect} from '@angular/platform-browser/testing/matchers';
export function main() {
describe('switch', () => {
describe('ngPlural', () => {
let fixture: ComponentFixture<any>;
function getComponent(): TestComponent { return fixture.componentInstance; }
@ -33,10 +33,25 @@ export function main() {
});
it('should display the template according to the exact value', async(() => {
const template = '<div>' +
'<ul [ngPlural]="switchValue">' +
const template = '<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="=0"><li>you have no messages.</li></template>' +
'<template ngPluralCase="=1"><li>you have one message.</li></template>' +
'</ul>';
fixture = createTestComponent(template);
getComponent().switchValue = 0;
detectChangesAndExpectText('you have no messages.');
getComponent().switchValue = 1;
detectChangesAndExpectText('you have one message.');
}));
it('should display the template according to the exact numeric value', async(() => {
const template = '<div>' +
'<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="0"><li>you have no messages.</li></template>' +
'<template ngPluralCase="1"><li>you have one message.</li></template>' +
'</ul></div>';
fixture = createTestComponent(template);
@ -51,10 +66,9 @@ export function main() {
// https://github.com/angular/angular/issues/9868
// https://github.com/angular/angular/issues/9882
it('should not throw when ngPluralCase contains expressions', async(() => {
const template = '<div>' +
'<ul [ngPlural]="switchValue">' +
const template = '<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="=0"><li>{{ switchValue }}</li></template>' +
'</ul></div>';
'</ul>';
fixture = createTestComponent(template);
@ -64,11 +78,10 @@ export function main() {
it('should be applicable to <ng-container> elements', async(() => {
const template = '<div>' +
'<ng-container [ngPlural]="switchValue">' +
const template = '<ng-container [ngPlural]="switchValue">' +
'<template ngPluralCase="=0">you have no messages.</template>' +
'<template ngPluralCase="=1">you have one message.</template>' +
'</ng-container></div>';
'</ng-container>';
fixture = createTestComponent(template);
@ -80,11 +93,10 @@ export function main() {
}));
it('should display the template according to the category', async(() => {
const template = '<div>' +
'<ul [ngPlural]="switchValue">' +
const template = '<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
'<template ngPluralCase="many"><li>you have many messages.</li></template>' +
'</ul></div>';
'</ul>';
fixture = createTestComponent(template);
@ -96,11 +108,10 @@ export function main() {
}));
it('should default to other when no matches are found', async(() => {
const template = '<div>' +
'<ul [ngPlural]="switchValue">' +
const template = '<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
'<template ngPluralCase="other"><li>default message.</li></template>' +
'</ul></div>';
'</ul>';
fixture = createTestComponent(template);
@ -109,11 +120,10 @@ export function main() {
}));
it('should prioritize value matches over category matches', async(() => {
const template = '<div>' +
'<ul [ngPlural]="switchValue">' +
const template = '<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
'<template ngPluralCase="=2">you have two messages.</template>' +
'</ul></div>';
'</ul>';
fixture = createTestComponent(template);

View File

@ -29,22 +29,19 @@ export function main() {
it('should add styles specified in an object literal', async(() => {
const template = `<div [ngStyle]="{'max-width': '40px'}"></div>`;
fixture = createTestComponent(template);
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
}));
it('should add and change styles specified in an object expression', async(() => {
const template = `<div [ngStyle]="expr"></div>`;
fixture = createTestComponent(template);
let expr: {[k: string]: string};
getComponent().expr = {'max-width': '40px'};
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
expr = getComponent().expr;
let expr = getComponent().expr;
expr['max-width'] = '30%';
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'max-width': '30%'});

View File

@ -33,11 +33,10 @@ export function main() {
describe('switch value changes', () => {
it('should switch amongst when values', () => {
const template = '<div>' +
'<ul [ngSwitch]="switchValue">' +
'<template ngSwitchCase="a"><li>when a</li></template>' +
'<template ngSwitchCase="b"><li>when b</li></template>' +
'</ul></div>';
const template = '<ul [ngSwitch]="switchValue">' +
'<li *ngSwitchCase="\'a\'">when a</li>' +
'<li *ngSwitchCase="\'b\'">when b</li>' +
'</ul>';
fixture = createTestComponent(template);
@ -51,11 +50,10 @@ export function main() {
});
it('should switch amongst when values with fallback to default', () => {
const template = '<div>' +
'<ul [ngSwitch]="switchValue">' +
'<li template="ngSwitchCase \'a\'">when a</li>' +
'<li template="ngSwitchDefault">when default</li>' +
'</ul></div>';
const template = '<ul [ngSwitch]="switchValue">' +
'<li *ngSwitchCase="\'a\'">when a</li>' +
'<li *ngSwitchDefault>when default</li>' +
'</ul>';
fixture = createTestComponent(template);
detectChangesAndExpectText('when default');
@ -71,15 +69,14 @@ export function main() {
});
it('should support multiple whens with the same value', () => {
const template = '<div>' +
'<ul [ngSwitch]="switchValue">' +
'<template ngSwitchCase="a"><li>when a1;</li></template>' +
'<template ngSwitchCase="b"><li>when b1;</li></template>' +
'<template ngSwitchCase="a"><li>when a2;</li></template>' +
'<template ngSwitchCase="b"><li>when b2;</li></template>' +
'<template ngSwitchDefault><li>when default1;</li></template>' +
'<template ngSwitchDefault><li>when default2;</li></template>' +
'</ul></div>';
const template = '<ul [ngSwitch]="switchValue">' +
'<li *ngSwitchCase="\'a\'">when a1;</li>' +
'<li *ngSwitchCase="\'b\'">when b1;</li>' +
'<li *ngSwitchCase="\'a\'">when a2;</li>' +
'<li *ngSwitchCase="\'b\'">when b2;</li>' +
'<li *ngSwitchDefault>when default1;</li>' +
'<li *ngSwitchDefault>when default2;</li>' +
'</ul>';
fixture = createTestComponent(template);
detectChangesAndExpectText('when default1;when default2;');
@ -94,12 +91,11 @@ export function main() {
describe('when values changes', () => {
it('should switch amongst when values', () => {
const template = '<div>' +
'<ul [ngSwitch]="switchValue">' +
'<template [ngSwitchCase]="when1"><li>when 1;</li></template>' +
'<template [ngSwitchCase]="when2"><li>when 2;</li></template>' +
'<template ngSwitchDefault><li>when default;</li></template>' +
'</ul></div>';
const template = '<ul [ngSwitch]="switchValue">' +
'<li *ngSwitchCase="when1">when 1;</li>' +
'<li *ngSwitchCase="when2">when 2;</li>' +
'<li *ngSwitchDefault>when default;</li>' +
'</ul>';
fixture = createTestComponent(template);
getComponent().when1 = 'a';
@ -148,11 +144,10 @@ export function main() {
});
it('should create the default case if there is no other case', () => {
const template = '<div>' +
'<ul [ngSwitch]="switchValue">' +
'<template ngSwitchDefault><li>when default1;</li></template>' +
'<template ngSwitchDefault><li>when default2;</li></template>' +
'</ul></div>';
const template = '<ul [ngSwitch]="switchValue">' +
'<li *ngSwitchDefault>when default1;</li>' +
'<li *ngSwitchDefault>when default2;</li>' +
'</ul>';
fixture = createTestComponent(template);
detectChangesAndExpectText('when default1;when default2;');
@ -160,15 +155,14 @@ export function main() {
});
it('should allow defaults before cases', () => {
const template = '<div>' +
'<ul [ngSwitch]="switchValue">' +
'<template ngSwitchDefault><li>when default1;</li></template>' +
'<template ngSwitchDefault><li>when default2;</li></template>' +
'<template ngSwitchCase="a"><li>when a1;</li></template>' +
'<template ngSwitchCase="b"><li>when b1;</li></template>' +
'<template ngSwitchCase="a"><li>when a2;</li></template>' +
'<template ngSwitchCase="b"><li>when b2;</li></template>' +
'</ul></div>';
const template = '<ul [ngSwitch]="switchValue">' +
'<li *ngSwitchDefault>when default1;</li>' +
'<li *ngSwitchDefault>when default2;</li>' +
'<li *ngSwitchCase="\'a\'">when a1;</li>' +
'<li *ngSwitchCase="\'b\'">when b1;</li>' +
'<li *ngSwitchCase="\'a\'">when a2;</li>' +
'<li *ngSwitchCase="\'b\'">when b2;</li>' +
'</ul>';
fixture = createTestComponent(template);
detectChangesAndExpectText('when default1;when default2;');

View File

@ -34,29 +34,22 @@ export function main() {
});
});
it('should do nothing if templateRef is null', async(() => {
const template = `<template [ngTemplateOutlet]="null"></template>`;
it('should do nothing if templateRef is `null`', async(() => {
const template = `<ng-container [ngTemplateOutlet]="null"></ng-container>`;
fixture = createTestComponent(template);
detectChangesAndExpectText('');
}));
it('should insert content specified by TemplateRef', async(() => {
const template =
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
const template = `<template #tpl>foo</template>` +
`<ng-container [ngTemplateOutlet]="tpl"></ng-container>`;
fixture = createTestComponent(template);
detectChangesAndExpectText('');
const refs = fixture.debugElement.children[0].references['refs'];
setTplRef(refs.tplRefs.first);
detectChangesAndExpectText('foo');
}));
it('should clear content if TemplateRef becomes null', async(() => {
const template =
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
it('should clear content if TemplateRef becomes `null`', async(() => {
const template = `<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs>` +
`<ng-container [ngTemplateOutlet]="currentTplRef"></ng-container>`;
fixture = createTestComponent(template);
fixture.detectChanges();
const refs = fixture.debugElement.children[0].references['refs'];
@ -70,7 +63,8 @@ export function main() {
it('should swap content if TemplateRef changes', async(() => {
const template =
`<tpl-refs #refs="tplRefs"><template>foo</template><template>bar</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
`<tpl-refs #refs="tplRefs"><template>foo</template><template>bar</template></tpl-refs>` +
`<ng-container [ngTemplateOutlet]="currentTplRef"></ng-container>`;
fixture = createTestComponent(template);
fixture.detectChanges();
@ -83,70 +77,47 @@ export function main() {
detectChangesAndExpectText('bar');
}));
it('should display template if context is null', async(() => {
const template =
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="null"></template>`;
it('should display template if context is `null`', async(() => {
const template = `<template #tpl>foo</template>` +
`<ng-container [ngTemplateOutlet]="tpl" [ngOutletContext]="null"></ng-container>`;
fixture = createTestComponent(template);
detectChangesAndExpectText('');
const refs = fixture.debugElement.children[0].references['refs'];
setTplRef(refs.tplRefs.first);
detectChangesAndExpectText('foo');
}));
it('should reflect initial context and changes', async(() => {
const template =
`<tpl-refs #refs="tplRefs"><template let-foo="foo"><span>{{foo}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
const template = `<template let-foo="foo" #tpl>{{foo}}</template>` +
`<ng-container [ngTemplateOutlet]="tpl" [ngOutletContext]="context"></ng-container>`;
fixture = createTestComponent(template);
fixture.detectChanges();
const refs = fixture.debugElement.children[0].references['refs'];
setTplRef(refs.tplRefs.first);
detectChangesAndExpectText('bar');
fixture.componentInstance.context.foo = 'alter-bar';
detectChangesAndExpectText('alter-bar');
}));
it('should reflect user defined $implicit property in the context', async(() => {
const template =
`<tpl-refs #refs="tplRefs"><template let-ctx><span>{{ctx.foo}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
it('should reflect user defined `$implicit` property in the context', async(() => {
const template = `<template let-ctx #tpl>{{ctx.foo}}</template>` +
`<ng-container [ngTemplateOutlet]="tpl" [ngOutletContext]="context"></ng-container>`;
fixture = createTestComponent(template);
fixture.detectChanges();
const refs = fixture.debugElement.children[0].references['refs'];
setTplRef(refs.tplRefs.first);
fixture.componentInstance.context = {$implicit: fixture.componentInstance.context};
detectChangesAndExpectText('bar');
fixture.componentInstance.context = {$implicit: {foo: 'bra'}};
detectChangesAndExpectText('bra');
}));
it('should reflect context re-binding', async(() => {
const template =
`<tpl-refs #refs="tplRefs"><template let-shawshank="shawshank"><span>{{shawshank}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
const template = `<template let-shawshank="shawshank" #tpl>{{shawshank}}</template>` +
`<ng-container [ngTemplateOutlet]="tpl" [ngOutletContext]="context"></ng-container>`;
fixture = createTestComponent(template);
fixture.detectChanges();
const refs = fixture.debugElement.children[0].references['refs'];
setTplRef(refs.tplRefs.first);
fixture.componentInstance.context = {shawshank: 'brooks'};
detectChangesAndExpectText('brooks');
fixture.componentInstance.context = {shawshank: 'was here'};
detectChangesAndExpectText('was here');
}));
});
}
@Directive({selector: 'tpl-refs', exportAs: 'tplRefs'})
class CaptureTplRefs {
@ContentChildren(TemplateRef) tplRefs: QueryList<TemplateRef<any>>;

View File

@ -7,13 +7,10 @@
*/
import {LOCALE_ID} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {beforeEach, describe, inject, it} from '@angular/core/testing/testing_internal';
import {expect} from '@angular/platform-browser/testing/matchers';
import {TestBed, inject} from '@angular/core/testing';
import {NgLocaleLocalization, NgLocalization, getPluralCategory} from '../src/localization';
export function main() {
describe('l10n', () => {
@ -141,6 +138,23 @@ export function main() {
expect(getPluralCategory(5, ['one', 'other', '=5'], l10n)).toEqual('=5');
expect(getPluralCategory(6, ['one', 'other', '=5'], l10n)).toEqual('other');
});
it('should fallback to other when the case is not present', () => {
const l10n = new NgLocaleLocalization('ro');
expect(getPluralCategory(1, ['one', 'other'], l10n)).toEqual('one');
// 2 -> 'few'
expect(getPluralCategory(2, ['one', 'other'], l10n)).toEqual('other');
});
describe('errors', () => {
it('should report an error when the "other" category is not present', () => {
expect(() => {
const l10n = new NgLocaleLocalization('ro');
// 2 -> 'few'
getPluralCategory(2, ['one'], l10n);
}).toThrowError('No plural message found for value "2"');
});
});
});
});
}

View File

@ -22,6 +22,7 @@
"../../../node_modules/zone.js/dist/zone.js.d.ts"
],
"angularCompilerOptions": {
"annotateForClosureCompiler": true,
"strictMetadataEmit": true
}
}

View File

@ -5,10 +5,13 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
export {AotCompilerHost, AotCompilerHost as StaticReflectorHost, StaticReflector, StaticSymbol} from '@angular/compiler';
export {CodeGenerator} from './src/codegen';
export {CompilerHost, CompilerHostContext, ModuleResolutionHostAdapter, NodeCompilerHostContext} from './src/compiler_host';
export {Extractor} from './src/extractor';
export {NodeReflectorHostContext, ReflectorHost, ReflectorHostContext} from './src/reflector_host';
export {StaticReflector, StaticReflectorHost, StaticSymbol} from './src/static_reflector';
export * from '@angular/tsc-wrapped';
export {VERSION} from './src/version';
// 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'

View File

@ -0,0 +1,3 @@
:host {
background-color: blue;
}

View File

@ -0,0 +1,5 @@
<div>
<h1 i18n>hello world</h1>
<a [routerLink]="['lazy']">lazy</a>
<router-outlet></router-outlet>
</div>

View File

@ -0,0 +1,19 @@
/**
* @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 {Component, ViewEncapsulation} from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.css'],
encapsulation: ViewEncapsulation.None
})
export class AppComponent {
}

View File

@ -0,0 +1,32 @@
/**
* @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 {Component, NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {RouterModule} from '@angular/router';
import {AppComponent} from './app.component';
@Component({selector: 'home-view', template: 'home!'})
export class HomeView {
}
@NgModule({
declarations: [AppComponent, HomeView],
imports: [
BrowserModule, RouterModule.forRoot([
{path: 'lazy', loadChildren: './lazy.module#LazyModule'},
{path: 'feature2', loadChildren: 'feature2/feature2.module#Feature2Module'},
{path: '', component: HomeView}
])
],
bootstrap: [AppComponent]
})
export class AppModule {
}

View File

@ -0,0 +1,21 @@
/**
* @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 {Component, NgModule} from '@angular/core';
import {RouterModule} from '@angular/router';
@Component({selector: 'feature-component', template: 'foo.html'})
export class FeatureComponent {
}
@NgModule({
declarations: [FeatureComponent],
imports: [RouterModule.forChild([{path: '', component: FeatureComponent}])]
})
export class FeatureModule {
}

View File

@ -0,0 +1,23 @@
/**
* @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 {Component, NgModule} from '@angular/core';
import {RouterModule} from '@angular/router';
@Component({selector: 'lazy-feature-comp', template: 'lazy feature, nested!'})
export class LazyFeatureNestedComponent {
}
@NgModule({
imports: [RouterModule.forChild([
{path: '', component: LazyFeatureNestedComponent, pathMatch: 'full'},
])],
declarations: [LazyFeatureNestedComponent]
})
export class LazyFeatureNestedModule {
}

View File

@ -0,0 +1,27 @@
/**
* @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 {Component, NgModule} from '@angular/core';
import {RouterModule} from '@angular/router';
@Component({selector: 'lazy-feature-comp', template: 'lazy feature!'})
export class LazyFeatureComponent {
}
@NgModule({
imports: [RouterModule.forChild([
{path: '', component: LazyFeatureComponent, pathMatch: 'full'},
{path: 'feature', loadChildren: './feature.module#FeatureModule'}, {
path: 'nested-feature',
loadChildren: './lazy-feature-nested.module#LazyFeatureNestedModule'
}
])],
declarations: [LazyFeatureComponent]
})
export class LazyFeatureModule {
}

View File

@ -0,0 +1,23 @@
/**
* @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 {Component, NgModule} from '@angular/core';
import {RouterModule} from '@angular/router';
@Component({selector: 'feature-component', template: 'foo.html'})
export class FeatureComponent {
}
@NgModule({
declarations: [FeatureComponent],
imports: [RouterModule.forChild([
{path: '', component: FeatureComponent},
])]
})
export default class DefaultModule {
}

View File

@ -0,0 +1,26 @@
/**
* @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 {Component, NgModule} from '@angular/core';
import {RouterModule} from '@angular/router';
@Component({selector: 'feature-component', template: 'foo.html'})
export class FeatureComponent {
}
@NgModule({
declarations: [FeatureComponent],
imports: [RouterModule.forChild([
{path: '', component: FeatureComponent}, {path: 'd', loadChildren: './default.module'} {
path: 'e',
loadChildren: 'feature/feature.module#FeatureModule'
}
])]
})
export class Feature2Module {
}

View File

@ -0,0 +1,27 @@
/**
* @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 {Component, NgModule} from '@angular/core';
import {RouterModule} from '@angular/router';
@Component({selector: 'lazy-comp', template: 'lazy!'})
export class LazyComponent {
}
@NgModule({
imports: [RouterModule.forChild([
{path: '', component: LazyComponent, pathMatch: 'full'},
{path: 'feature', loadChildren: './feature/feature.module#FeatureModule'},
{path: 'lazy-feature', loadChildren: './feature/lazy-feature.module#LazyFeatureModule'}
])],
declarations: [LazyComponent]
})
export class LazyModule {
}
export class SecondModule {}

View File

@ -0,0 +1,19 @@
{
"angularCompilerOptions": {
// For TypeScript 1.8, we have to lay out generated files
// in the same source directory with your code.
"genDir": ".",
"debug": true
},
"compilerOptions": {
"target": "es5",
"experimentalDecorators": true,
"noImplicitAny": true,
"moduleResolution": "node",
"rootDir": "",
"declaration": true,
"lib": ["es6", "dom"],
"baseUrl": "."
}
}

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE translationbundle [<!ELEMENT translationbundle (translation)*>
<!ATTLIST translationbundle lang CDATA #REQUIRED>
<!ELEMENT translation (#PCDATA|ph)*>
<!ATTLIST translation id CDATA #REQUIRED>
<!ELEMENT ph EMPTY>
<!ATTLIST ph name CDATA #REQUIRED>
]>
<translationbundle>
<translation id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4">käännä teksti</translation>
<translation id="65cc4ab3b4c438e07c89be2b677d08369fb62da2">tervetuloa</translation>
<translation id="63a85808f03b8181e36a952e0fa38202c2304862">other-3rdP-component</translation>
</translationbundle>

View File

@ -11,7 +11,9 @@ import {FormsModule} from '@angular/forms';
import {ServerModule} from '@angular/platform-server';
import {MdButtonModule} from '@angular2-material/button';
import {ThirdpartyModule} from '../third_party_src/module';
// Note: don't refer to third_party_src as we want to test that
// we can compile components from node_modules!
import {ThirdpartyModule} from 'third_party/module';
import {MultipleComponentsMyComp, NextComp} from './a/multiple_components';
import {AnimateCmp} from './animate';
@ -19,7 +21,7 @@ import {BasicComp} from './basic';
import {ComponentUsingThirdParty} from './comp_using_3rdp';
import {CompWithAnalyzeEntryComponentsProvider, CompWithEntryComponents} from './entry_components';
import {CompConsumingEvents, CompUsingPipes, CompWithProviders, CompWithReferences, DirPublishingEvents, ModuleUsingCustomElements} from './features';
import {CompUsingRootModuleDirectiveAndPipe, SomeDirectiveInRootModule, SomePipeInRootModule, SomeService, someLibModuleWithProviders} from './module_fixtures';
import {CompUsingRootModuleDirectiveAndPipe, SomeDirectiveInRootModule, SomeLibModule, SomePipeInRootModule, SomeService} from './module_fixtures';
import {CompWithNgContent, ProjectingComp} from './projection';
import {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, DirectiveForQuery} from './queries';
@ -52,7 +54,7 @@ import {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, Directive
FormsModule,
MdButtonModule,
ModuleUsingCustomElements,
someLibModuleWithProviders(),
SomeLibModule.withProviders(),
ThirdpartyModule,
],
providers: [SomeService],

View File

@ -63,17 +63,13 @@ export function provideValueWithEntryComponents(value: any) {
entryComponents: [CompUsingLibModuleDirectiveAndPipe],
})
export class SomeLibModule {
}
// TODO(tbosch): Make this a static method in `SomeLibModule` once
// our static reflector supports it.
// See https://github.com/angular/angular/issues/10266.
export function someLibModuleWithProviders(): ModuleWithProviders {
static withProviders() {
return {
ngModule: SomeLibModule,
providers: [
ServiceUsingLibModule,
provideValueWithEntryComponents([{a: 'b', component: CompUsingLibModuleDirectiveAndPipe}])
ServiceUsingLibModule, provideValueWithEntryComponents(
[{a: 'b', component: CompUsingLibModuleDirectiveAndPipe}])
]
};
}
}

View File

@ -34,9 +34,9 @@ const EXPECTED_XMB = `<?xml version="1.0" encoding="UTF-8" ?>
<!ELEMENT ex (#PCDATA)>
]>
<messagebundle>
<msg id="63a85808f03b8181e36a952e0fa38202c2304862">other-3rdP-component</msg>
<msg id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" desc="desc" meaning="meaning">translate me</msg>
<msg id="65cc4ab3b4c438e07c89be2b677d08369fb62da2">Welcome</msg>
<msg id="8136548302122759730" desc="desc" meaning="meaning">translate me</msg>
<msg id="3492007542396725315">Welcome</msg>
<msg id="3772663375917578720">other-3rdP-component</msg>
</messagebundle>
`;
@ -44,10 +44,6 @@ const EXPECTED_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="ng2.template">
<body>
<trans-unit id="63a85808f03b8181e36a952e0fa38202c2304862" datatype="html">
<source>other-3rdP-component</source>
<target/>
</trans-unit>
<trans-unit id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" datatype="html">
<source>translate me</source>
<target/>
@ -58,6 +54,10 @@ const EXPECTED_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
<source>Welcome</source>
<target/>
</trans-unit>
<trans-unit id="63a85808f03b8181e36a952e0fa38202c2304862" datatype="html">
<source>other-3rdP-component</source>
<target/>
</trans-unit>
</body>
</file>
</xliff>
@ -79,5 +79,4 @@ describe('template i18n extraction output', () => {
const xlf = fs.readFileSync(xlfOutput, {encoding: 'utf-8'});
expect(xlf).toEqual(EXPECTED_XLIFF);
});
});

View File

@ -0,0 +1,217 @@
#!/usr/bin/env node
/**
* @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
*/
/* tslint:disable:no-console */
// Must be imported first, because angular2 decorators throws on load.
import 'reflect-metadata';
import * as path from 'path';
import * as ts from 'typescript';
import * as assert from 'assert';
import {tsc} from '@angular/tsc-wrapped/src/tsc';
import {AngularCompilerOptions, CodeGenerator, CompilerHostContext, NodeCompilerHostContext, __NGTOOLS_PRIVATE_API_2} from '@angular/compiler-cli';
const glob = require('glob');
/**
* Main method.
* Standalone program that executes codegen using the ngtools API and tests that files were
* properly read and wrote.
*/
function main() {
console.log(`testing ngtools API...`);
Promise.resolve()
.then(() => codeGenTest())
.then(() => i18nTest())
.then(() => lazyRoutesTest())
.then(() => {
console.log('All done!');
process.exit(0);
})
.catch((err) => {
console.error(err.stack);
console.error('Test failed');
process.exit(1);
});
}
function codeGenTest() {
const basePath = path.join(__dirname, '../ngtools_src');
const project = path.join(basePath, 'tsconfig-build.json');
const readResources: string[] = [];
const wroteFiles: string[] = [];
const config = tsc.readConfiguration(project, basePath);
const hostContext = new NodeCompilerHostContext();
const delegateHost = ts.createCompilerHost(config.parsed.options, true);
const host: ts.CompilerHost = Object.assign(
{}, delegateHost,
{writeFile: (fileName: string, ...rest: any[]) => { wroteFiles.push(fileName); }});
const program = ts.createProgram(config.parsed.fileNames, config.parsed.options, host);
config.ngOptions.basePath = basePath;
console.log(`>>> running codegen for ${project}`);
return __NGTOOLS_PRIVATE_API_2
.codeGen({
basePath,
compilerOptions: config.parsed.options, program, host,
angularCompilerOptions: config.ngOptions,
// i18n options.
i18nFormat: null,
i18nFile: null,
locale: null,
readResource: (fileName: string) => {
readResources.push(fileName);
return hostContext.readResource(fileName);
}
})
.then(() => {
console.log(`>>> codegen done, asserting read and wrote files`);
// Assert for each file that it has been read and each `ts` has a written file associated.
const allFiles = glob.sync(path.join(basePath, '**/*'), {nodir: true});
allFiles.forEach((fileName: string) => {
// Skip tsconfig.
if (fileName.match(/tsconfig-build.json$/)) {
return;
}
// Assert that file was read.
if (fileName.match(/\.module\.ts$/)) {
const factory = fileName.replace(/\.module\.ts$/, '.module.ngfactory.ts');
assert(wroteFiles.indexOf(factory) != -1, `Expected file "${factory}" to be written.`);
} else if (fileName.match(/\.css$/) || fileName.match(/\.html$/)) {
assert(
readResources.indexOf(fileName) != -1,
`Expected resource "${fileName}" to be read.`);
}
});
console.log(`done, no errors.`);
})
.catch((e: any) => {
console.error(e.stack);
console.error('Compilation failed');
throw e;
});
}
function i18nTest() {
const basePath = path.join(__dirname, '../ngtools_src');
const project = path.join(basePath, 'tsconfig-build.json');
const readResources: string[] = [];
const wroteFiles: string[] = [];
const config = tsc.readConfiguration(project, basePath);
const hostContext = new NodeCompilerHostContext();
const delegateHost = ts.createCompilerHost(config.parsed.options, true);
const host: ts.CompilerHost = Object.assign(
{}, delegateHost,
{writeFile: (fileName: string, ...rest: any[]) => { wroteFiles.push(fileName); }});
const program = ts.createProgram(config.parsed.fileNames, config.parsed.options, host);
config.ngOptions.basePath = basePath;
console.log(`>>> running i18n extraction for ${project}`);
return __NGTOOLS_PRIVATE_API_2
.extractI18n({
basePath,
compilerOptions: config.parsed.options, program, host,
angularCompilerOptions: config.ngOptions,
i18nFormat: 'xlf',
readResource: (fileName: string) => {
readResources.push(fileName);
return hostContext.readResource(fileName);
},
})
.then(() => {
console.log(`>>> i18n extraction done, asserting read and wrote files`);
const allFiles = glob.sync(path.join(basePath, '**/*'), {nodir: true});
assert(wroteFiles.length == 1, `Expected a single message bundle file.`);
assert(
wroteFiles[0].endsWith('/ngtools_src/messages.xlf'),
`Expected the bundle file to be "message.xlf".`);
allFiles.forEach((fileName: string) => {
// Skip tsconfig.
if (fileName.match(/tsconfig-build.json$/)) {
return;
}
// Assert that file was read.
if (fileName.match(/\.css$/) || fileName.match(/\.html$/)) {
assert(
readResources.indexOf(fileName) != -1,
`Expected resource "${fileName}" to be read.`);
}
});
console.log(`done, no errors.`);
})
.catch((e: any) => {
console.error(e.stack);
console.error('Extraction failed');
throw e;
});
}
function lazyRoutesTest() {
const basePath = path.join(__dirname, '../ngtools_src');
const project = path.join(basePath, 'tsconfig-build.json');
const config = tsc.readConfiguration(project, basePath);
const host = ts.createCompilerHost(config.parsed.options, true);
const program = ts.createProgram(config.parsed.fileNames, config.parsed.options, host);
config.ngOptions.basePath = basePath;
const lazyRoutes = __NGTOOLS_PRIVATE_API_2.listLazyRoutes({
program,
host,
angularCompilerOptions: config.ngOptions,
entryModule: 'app.module#AppModule'
});
const expectations: {[route: string]: string} = {
'./lazy.module#LazyModule': 'lazy.module.ts',
'./feature/feature.module#FeatureModule': 'feature/feature.module.ts',
'./feature/lazy-feature.module#LazyFeatureModule': 'feature/lazy-feature.module.ts',
'./feature.module#FeatureModule': 'feature/feature.module.ts',
'./lazy-feature-nested.module#LazyFeatureNestedModule': 'feature/lazy-feature-nested.module.ts',
'feature2/feature2.module#Feature2Module': 'feature2/feature2.module.ts',
'./default.module': 'feature2/default.module.ts',
'feature/feature.module#FeatureModule': 'feature/feature.module.ts'
};
Object.keys(lazyRoutes).forEach((route: string) => {
assert(route in expectations, `Found a route that was not expected: "${route}".`);
assert(
lazyRoutes[route] == path.join(basePath, expectations[route]),
`Route "${route}" does not point to the expected absolute path ` +
`"${path.join(basePath, expectations[route])}". It points to "${lazyRoutes[route]}"`);
});
// Verify that all expectations were met.
assert.deepEqual(
Object.keys(lazyRoutes), Object.keys(expectations), `Expected routes listed to be: \n` +
` ${JSON.stringify(Object.keys(expectations))}\n` +
`Actual:\n` +
` ${JSON.stringify(Object.keys(lazyRoutes))}\n`);
}
main();

View File

@ -0,0 +1,125 @@
#!/usr/bin/env node
/**
* @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
*/
/* tslint:disable:no-console */
// Must be imported first, because angular2 decorators throws on load.
import 'reflect-metadata';
import * as path from 'path';
import * as ts from 'typescript';
import * as assert from 'assert';
import {tsc} from '@angular/tsc-wrapped/src/tsc';
import {AngularCompilerOptions, CodeGenerator, CompilerHostContext, NodeCompilerHostContext} from '@angular/compiler-cli';
/**
* Main method.
* Standalone program that executes the real codegen and tests that
* ngsummary.json files are used for libraries.
*/
function main() {
console.log(`testing usage of ngsummary.json files in libraries...`);
const basePath = path.resolve(__dirname, '..');
const project = path.resolve(basePath, 'tsconfig-build.json');
const readFiles: string[] = [];
const writtenFiles: {fileName: string, content: string}[] = [];
class AssertingHostContext extends NodeCompilerHostContext {
readFile(fileName: string): string {
if (/.*\/node_modules\/.*/.test(fileName) && !/.*ngsummary\.json$/.test(fileName)) {
// Only allow to read summaries from node_modules
return null;
}
readFiles.push(path.relative(basePath, fileName));
return super.readFile(fileName);
}
readResource(s: string): Promise<string> {
readFiles.push(path.relative(basePath, s));
return super.readResource(s);
}
}
const config = tsc.readConfiguration(project, basePath);
config.ngOptions.basePath = basePath;
// This flag tells ngc do not recompile libraries.
config.ngOptions.generateCodeForLibraries = false;
console.log(`>>> running codegen for ${project}`);
codegen(
config,
(host) => {
host.writeFile = (fileName: string, content: string) => {
fileName = path.relative(basePath, fileName);
writtenFiles.push({fileName, content});
};
return new AssertingHostContext();
})
.then((exitCode: any) => {
console.log(`>>> codegen done, asserting read files`);
assertSomeFileMatch(readFiles, /^node_modules\/.*\.ngsummary\.json$/);
assertNoFileMatch(readFiles, /^node_modules\/.*\.metadata.json$/);
assertNoFileMatch(readFiles, /^node_modules\/.*\.html$/);
assertNoFileMatch(readFiles, /^node_modules\/.*\.css$/);
assertNoFileMatch(readFiles, /^src\/.*\.ngsummary\.json$/);
assertSomeFileMatch(readFiles, /^src\/.*\.html$/);
assertSomeFileMatch(readFiles, /^src\/.*\.css$/);
console.log(`>>> asserting written files`);
assertWrittenFile(writtenFiles, /^src\/module\.ngfactory\.ts$/, /class MainModuleInjector/);
console.log(`done, no errors.`);
process.exit(exitCode);
})
.catch((e: any) => {
console.error(e.stack);
console.error('Compilation failed');
process.exit(1);
});
}
/**
* Simple adaption of tsc-wrapped main to just run codegen with a CompilerHostContext
*/
function codegen(
config: {parsed: ts.ParsedCommandLine, ngOptions: AngularCompilerOptions},
hostContextFactory: (host: ts.CompilerHost) => CompilerHostContext) {
const host = ts.createCompilerHost(config.parsed.options, true);
// HACK: patch the realpath to solve symlink issue here:
// https://github.com/Microsoft/TypeScript/issues/9552
// todo(misko): remove once facade symlinks are removed
host.realpath = (path) => path;
const program = ts.createProgram(config.parsed.fileNames, config.parsed.options, host);
return CodeGenerator.create(config.ngOptions, {
} as any, program, host, hostContextFactory(host)).codegen();
}
function assertSomeFileMatch(fileNames: string[], pattern: RegExp) {
assert(
fileNames.some(fileName => pattern.test(fileName)),
`Expected some read files match ${pattern}`);
}
function assertNoFileMatch(fileNames: string[], pattern: RegExp) {
const matches = fileNames.filter(fileName => pattern.test(fileName));
assert(
matches.length === 0,
`Expected no read files match ${pattern}, but found: \n${matches.join('\n')}`);
}
function assertWrittenFile(
files: {fileName: string, content: string}[], filePattern: RegExp, contentPattern: RegExp) {
assert(
files.some(file => filePattern.test(file.fileName) && contentPattern.test(file.content)),
`Expected some written files for ${filePattern} and content ${contentPattern}`);
}
main();

View File

@ -15,6 +15,8 @@
"declaration": true,
"lib": ["es6", "dom"],
"baseUrl": ".",
"outDir": "../node_modules/third_party"
"outDir": "../node_modules/third_party",
// Prevent scanning up the directory tree for types
"typeRoots": ["node_modules/@types"]
}
}

View File

@ -14,13 +14,17 @@
"rootDir": "",
"declaration": true,
"lib": ["es6", "dom"],
"baseUrl": "."
"baseUrl": ".",
// Prevent scanning up the directory tree for types
"typeRoots": ["node_modules/@types"]
},
"files": [
"src/module",
"src/bootstrap",
"test/all_spec",
"test/test_ngtools_api",
"test/test_summaries",
"benchmarks/src/tree/ng2/index_aot.ts",
"benchmarks/src/tree/ng2_switch/index_aot.ts",
"benchmarks/src/largetable/ng2/index_aot.ts",

View File

@ -9,7 +9,7 @@
"ng-xi18n": "./src/extract_i18n.js"
},
"dependencies": {
"@angular/tsc-wrapped": "^0.3.0",
"@angular/tsc-wrapped": "0.5.1",
"reflect-metadata": "^0.1.2",
"minimist": "^1.2.0"
},

View File

@ -11,20 +11,15 @@
* Intended to be used in a build step.
*/
import * as compiler from '@angular/compiler';
import {ViewEncapsulation} from '@angular/core';
import {AngularCompilerOptions, NgcCliOptions} from '@angular/tsc-wrapped';
import {readFileSync} from 'fs';
import * as path from 'path';
import * as ts from 'typescript';
import {PathMappedReflectorHost} from './path_mapped_reflector_host';
import {Console} from './private_import_core';
import {ReflectorHost, ReflectorHostContext} from './reflector_host';
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
import {StaticReflector, StaticReflectorHost, StaticSymbol} from './static_reflector';
import {CompilerHost, CompilerHostContext, ModuleResolutionHostAdapter} from './compiler_host';
import {PathMappedCompilerHost} from './path_mapped_compiler_host';
const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
const GENERATED_META_FILES = /\.json$/;
const PREAMBLE = `/**
* @fileoverview This file is generated by the Angular 2 template compiler.
@ -38,59 +33,34 @@ const PREAMBLE = `/**
export class CodeGenerator {
constructor(
private options: AngularCompilerOptions, private program: ts.Program,
public host: ts.CompilerHost, private staticReflector: StaticReflector,
private compiler: compiler.OfflineCompiler, private reflectorHost: StaticReflectorHost) {}
public host: ts.CompilerHost, private compiler: compiler.AotCompiler,
private ngCompilerHost: CompilerHost) {}
// Write codegen in a directory structure matching the sources.
private calculateEmitPath(filePath: string): string {
let root = this.options.basePath;
for (const eachRootDir of this.options.rootDirs || []) {
if (this.options.trace) {
console.log(`Check if ${filePath} is under rootDirs element ${eachRootDir}`);
}
if (path.relative(eachRootDir, filePath).indexOf('.') !== 0) {
root = eachRootDir;
}
}
// transplant the codegen path to be inside the `genDir`
let relativePath: string = path.relative(root, filePath);
while (relativePath.startsWith('..' + path.sep)) {
// Strip out any `..` path such as: `../node_modules/@foo` as we want to put everything
// into `genDir`.
relativePath = relativePath.substr(3);
}
return path.join(this.options.genDir, relativePath);
}
codegen(options: {transitiveModules: boolean}): Promise<any> {
const staticSymbols =
extractProgramSymbols(this.program, this.staticReflector, this.reflectorHost, this.options);
return this.compiler.compileModules(staticSymbols, options).then(generatedModules => {
codegen(): Promise<any> {
return this.compiler
.compileAll(this.program.getSourceFiles().map(
sf => this.ngCompilerHost.getCanonicalFileName(sf.fileName)))
.then(generatedModules => {
generatedModules.forEach(generatedModule => {
const sourceFile = this.program.getSourceFile(generatedModule.fileUrl);
const emitPath = this.calculateEmitPath(generatedModule.moduleUrl);
this.host.writeFile(
emitPath, PREAMBLE + generatedModule.source, false, () => {}, [sourceFile]);
const sourceFile = this.program.getSourceFile(generatedModule.srcFileUrl);
const emitPath = this.ngCompilerHost.calculateEmitPath(generatedModule.genFileUrl);
const source = GENERATED_META_FILES.test(emitPath) ? generatedModule.source :
PREAMBLE + generatedModule.source;
this.host.writeFile(emitPath, source, false, () => {}, [sourceFile]);
});
});
}
static create(
options: AngularCompilerOptions, cliOptions: NgcCliOptions, program: ts.Program,
compilerHost: ts.CompilerHost, reflectorHostContext?: ReflectorHostContext,
resourceLoader?: compiler.ResourceLoader, reflectorHost?: ReflectorHost): CodeGenerator {
resourceLoader = resourceLoader || {
get: (s: string) => {
if (!compilerHost.fileExists(s)) {
// TODO: We should really have a test for error cases like this!
throw new Error(`Compilation failed. Resource file not found: ${s}`);
tsCompilerHost: ts.CompilerHost, compilerHostContext?: CompilerHostContext,
ngCompilerHost?: CompilerHost): CodeGenerator {
if (!ngCompilerHost) {
const usePathMapping = !!options.rootDirs && options.rootDirs.length > 0;
const context = compilerHostContext || new ModuleResolutionHostAdapter(tsCompilerHost);
ngCompilerHost = usePathMapping ? new PathMappedCompilerHost(program, options, context) :
new CompilerHost(program, options, context);
}
return Promise.resolve(compilerHost.readFile(s));
}
};
const transFile = cliOptions.i18nFile;
const locale = cliOptions.locale;
let transContent: string = '';
@ -101,84 +71,12 @@ export class CodeGenerator {
}
transContent = readFileSync(transFile, 'utf8');
}
const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver();
if (!reflectorHost) {
const usePathMapping = !!options.rootDirs && options.rootDirs.length > 0;
reflectorHost = usePathMapping ?
new PathMappedReflectorHost(program, compilerHost, options, reflectorHostContext) :
new ReflectorHost(program, compilerHost, options, reflectorHostContext);
}
const staticReflector = new StaticReflector(reflectorHost);
StaticAndDynamicReflectionCapabilities.install(staticReflector);
const htmlParser =
new compiler.I18NHtmlParser(new compiler.HtmlParser(), transContent, cliOptions.i18nFormat);
const config = new compiler.CompilerConfig({
genDebugInfo: options.debug === true,
defaultEncapsulation: ViewEncapsulation.Emulated,
logBindingUpdate: false,
useJit: false
const {compiler: aotCompiler} = compiler.createAotCompiler(ngCompilerHost, {
debug: options.debug === true,
translations: transContent,
i18nFormat: cliOptions.i18nFormat,
locale: cliOptions.locale
});
const normalizer =
new compiler.DirectiveNormalizer(resourceLoader, urlResolver, htmlParser, config);
const expressionParser = new compiler.Parser(new compiler.Lexer());
const elementSchemaRegistry = new compiler.DomElementSchemaRegistry();
const console = new Console();
const tmplParser = new compiler.TemplateParser(
expressionParser, elementSchemaRegistry, htmlParser, console, []);
const resolver = new compiler.CompileMetadataResolver(
new compiler.NgModuleResolver(staticReflector),
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
elementSchemaRegistry, normalizer, staticReflector);
// TODO(vicb): do not pass cliOptions.i18nFormat here
const offlineCompiler = new compiler.OfflineCompiler(
resolver, tmplParser, new compiler.StyleCompiler(urlResolver),
new compiler.ViewCompiler(config, elementSchemaRegistry),
new compiler.DirectiveWrapperCompiler(
config, expressionParser, elementSchemaRegistry, console),
new compiler.NgModuleCompiler(), new compiler.TypeScriptEmitter(reflectorHost),
cliOptions.locale, cliOptions.i18nFormat,
new compiler.AnimationParser(elementSchemaRegistry));
return new CodeGenerator(
options, program, compilerHost, staticReflector, offlineCompiler, reflectorHost);
return new CodeGenerator(options, program, tsCompilerHost, aotCompiler, ngCompilerHost);
}
}
export function extractProgramSymbols(
program: ts.Program, staticReflector: StaticReflector, reflectorHost: StaticReflectorHost,
options: AngularCompilerOptions): StaticSymbol[] {
// Compare with false since the default should be true
const skipFileNames =
options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : GENERATED_FILES;
const staticSymbols: StaticSymbol[] = [];
program.getSourceFiles()
.filter(sourceFile => !skipFileNames.test(sourceFile.fileName))
.forEach(sourceFile => {
const absSrcPath = reflectorHost.getCanonicalFileName(sourceFile.fileName);
const moduleMetadata = staticReflector.getModuleMetadata(absSrcPath);
if (!moduleMetadata) {
console.log(`WARNING: no metadata found for ${absSrcPath}`);
return;
}
const metadata = moduleMetadata['metadata'];
if (!metadata) {
return;
}
for (const symbol of Object.keys(metadata)) {
if (metadata[symbol] && metadata[symbol].__symbolic == 'error') {
// Ignore symbols that are only included to record error information.
continue;
}
staticSymbols.push(reflectorHost.findDeclaration(absSrcPath, symbol, absSrcPath));
}
});
return staticSymbols;
}

View File

@ -0,0 +1,344 @@
/**
* @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 {AotCompilerHost, StaticSymbol} from '@angular/compiler';
import {AngularCompilerOptions, MetadataCollector, ModuleMetadata} from '@angular/tsc-wrapped';
import * as fs from 'fs';
import * as path from 'path';
import * as ts from 'typescript';
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
const DTS = /\.d\.ts$/;
const NODE_MODULES = '/node_modules/';
const IS_GENERATED = /\.(ngfactory|ngstyle)$/;
const GENERATED_FILES = /\.ngfactory\.ts$|\.ngstyle\.ts$/;
const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.ngstyle\.ts$/;
export interface CompilerHostContext extends ts.ModuleResolutionHost {
readResource(fileName: string): Promise<string>;
assumeFileExists(fileName: string): void;
}
export class CompilerHost implements AotCompilerHost {
protected metadataCollector = new MetadataCollector();
private isGenDirChildOfRootDir: boolean;
protected basePath: string;
private genDir: string;
private resolverCache = new Map<string, ModuleMetadata[]>();
protected resolveModuleNameHost: CompilerHostContext;
constructor(
protected program: ts.Program, protected options: AngularCompilerOptions,
protected context: CompilerHostContext) {
// 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, '/');
const genPath: string = path.relative(this.basePath, this.genDir);
this.isGenDirChildOfRootDir = genPath === '' || !genPath.startsWith('..');
this.resolveModuleNameHost = Object.create(this.context);
// When calling ts.resolveModuleName,
// additional allow checks for .d.ts files to be done based on
// checks for .ngsummary.json files,
// so that our codegen depends on fewer inputs and requires to be called
// less often.
// This is needed as we use ts.resolveModuleName in reflector_host
// and it should be able to resolve summary file names.
this.resolveModuleNameHost.fileExists = (fileName: string): boolean => {
if (this.context.fileExists(fileName)) {
return true;
}
if (DTS.test(fileName)) {
const base = fileName.substring(0, fileName.length - 5);
return this.context.fileExists(base + '.ngsummary.json');
}
return false;
};
}
// We use absolute paths on disk as canonical.
getCanonicalFileName(fileName: string): string { return fileName; }
moduleNameToFileName(m: string, containingFile: string): string|null {
if (!containingFile || !containingFile.length) {
if (m.indexOf('.') === 0) {
throw new Error('Resolution of relative paths requires a containing file.');
}
// Any containing file gives the same result for absolute imports
containingFile = this.getCanonicalFileName(path.join(this.basePath, 'index.ts'));
}
m = m.replace(EXT, '');
const resolved =
ts.resolveModuleName(
m, containingFile.replace(/\\/g, '/'), this.options, this.resolveModuleNameHost)
.resolvedModule;
return resolved ? this.getCanonicalFileName(resolved.resolvedFileName) : null;
};
/**
* We want a moduleId that will appear in import statements in the generated code.
* These need to be in a form that system.js can load, so absolute file paths don't work.
*
* The `containingFile` is always in the `genDir`, where as the `importedFile` can be in
* `genDir`, `node_module` or `basePath`. The `importedFile` is either a generated file or
* existing file.
*
* | genDir | node_module | rootDir
* --------------+----------+-------------+----------
* generated | relative | relative | n/a
* existing file | n/a | absolute | relative(*)
*
* NOTE: (*) the relative path is computed depending on `isGenDirChildOfRootDir`.
*/
fileNameToModuleName(importedFile: string, containingFile: string): string {
// If a file does not yet exist (because we compile it later), we still need to
// assume it exists it so that the `resolve` method works!
if (!this.context.fileExists(importedFile)) {
this.context.assumeFileExists(importedFile);
}
containingFile = this.rewriteGenDirPath(containingFile);
const containingDir = path.dirname(containingFile);
// drop extension
importedFile = importedFile.replace(EXT, '');
const nodeModulesIndex = importedFile.indexOf(NODE_MODULES);
const importModule = nodeModulesIndex === -1 ?
null :
importedFile.substring(nodeModulesIndex + NODE_MODULES.length);
const isGeneratedFile = IS_GENERATED.test(importedFile);
if (isGeneratedFile) {
// rewrite to genDir path
if (importModule) {
// it is generated, therefore we do a relative path to the factory
return this.dotRelative(containingDir, this.genDir + NODE_MODULES + importModule);
} else {
// assume that import is also in `genDir`
importedFile = this.rewriteGenDirPath(importedFile);
return this.dotRelative(containingDir, importedFile);
}
} else {
// user code import
if (importModule) {
return importModule;
} else {
if (!this.isGenDirChildOfRootDir) {
// assume that they are on top of each other.
importedFile = importedFile.replace(this.basePath, this.genDir);
}
return this.dotRelative(containingDir, importedFile);
}
}
}
private dotRelative(from: string, to: string): string {
const rPath: string = path.relative(from, to).replace(/\\/g, '/');
return rPath.startsWith('.') ? rPath : './' + rPath;
}
/**
* Moves the path into `genDir` folder while preserving the `node_modules` directory.
*/
private rewriteGenDirPath(filepath: string) {
const nodeModulesIndex = filepath.indexOf(NODE_MODULES);
if (nodeModulesIndex !== -1) {
// If we are in node_modulse, transplant them into `genDir`.
return path.join(this.genDir, filepath.substring(nodeModulesIndex));
} else {
// pretend that containing file is on top of the `genDir` to normalize the paths.
// we apply the `genDir` => `rootDir` delta through `rootDirPrefix` later.
return filepath.replace(this.basePath, this.genDir);
}
}
protected getSourceFile(filePath: string): ts.SourceFile {
const sf = this.program.getSourceFile(filePath);
if (!sf) {
if (this.context.fileExists(filePath)) {
const sourceText = this.context.readFile(filePath);
return ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true);
}
throw new Error(`Source file ${filePath} not present in program.`);
}
return sf;
}
getMetadataFor(filePath: string): ModuleMetadata[] {
if (!this.context.fileExists(filePath)) {
// If the file doesn't exists then we cannot return metadata for the file.
// This will occur if the user refernced a declared module for which no file
// exists for the module (i.e. jQuery or angularjs).
return;
}
if (DTS.test(filePath)) {
const metadataPath = filePath.replace(DTS, '.metadata.json');
if (this.context.fileExists(metadataPath)) {
return this.readMetadata(metadataPath, filePath);
} else {
// If there is a .d.ts file but no metadata file we need to produce a
// v3 metadata from the .d.ts file as v3 includes the exports we need
// to resolve symbols.
return [this.upgradeVersion1Metadata(
{'__symbolic': 'module', 'version': 1, 'metadata': {}}, filePath)];
}
} else {
const sf = this.getSourceFile(filePath);
const metadata = this.metadataCollector.getMetadata(sf);
return metadata ? [metadata] : [];
}
}
readMetadata(filePath: string, dtsFilePath: string): ModuleMetadata[] {
let metadatas = this.resolverCache.get(filePath);
if (metadatas) {
return metadatas;
}
try {
const metadataOrMetadatas = JSON.parse(this.context.readFile(filePath));
const metadatas: ModuleMetadata[] = metadataOrMetadatas ?
(Array.isArray(metadataOrMetadatas) ? metadataOrMetadatas : [metadataOrMetadatas]) :
[];
const v1Metadata = metadatas.find(m => m.version === 1);
let v3Metadata = metadatas.find(m => m.version === 3);
if (!v3Metadata && v1Metadata) {
metadatas.push(this.upgradeVersion1Metadata(v1Metadata, dtsFilePath));
}
this.resolverCache.set(filePath, metadatas);
return metadatas;
} catch (e) {
console.error(`Failed to read JSON file ${filePath}`);
throw e;
}
}
private upgradeVersion1Metadata(v1Metadata: ModuleMetadata, dtsFilePath: string): ModuleMetadata {
// patch up v1 to v3 by merging the metadata with metadata collected from the d.ts file
// as the only difference between the versions is whether all exports are contained in
// the metadata and the `extends` clause.
let v3Metadata: ModuleMetadata = {'__symbolic': 'module', 'version': 3, 'metadata': {}};
if (v1Metadata.exports) {
v3Metadata.exports = v1Metadata.exports;
}
for (let prop in v1Metadata.metadata) {
v3Metadata.metadata[prop] = v1Metadata.metadata[prop];
}
const exports = this.metadataCollector.getMetadata(this.getSourceFile(dtsFilePath));
if (exports) {
for (let prop in exports.metadata) {
if (!v3Metadata.metadata[prop]) {
v3Metadata.metadata[prop] = exports.metadata[prop];
}
}
if (exports.exports) {
v3Metadata.exports = exports.exports;
}
}
return v3Metadata;
}
loadResource(filePath: string): Promise<string> { return this.context.readResource(filePath); }
loadSummary(filePath: string): string|null {
if (this.context.fileExists(filePath)) {
return this.context.readFile(filePath);
}
}
getOutputFileName(sourceFilePath: string): string {
return sourceFilePath.replace(EXT, '') + '.d.ts';
}
isSourceFile(filePath: string): boolean {
const excludeRegex =
this.options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : GENERATED_FILES;
return !excludeRegex.test(filePath);
}
calculateEmitPath(filePath: string): string {
// Write codegen in a directory structure matching the sources.
let root = this.options.basePath;
for (const eachRootDir of this.options.rootDirs || []) {
if (this.options.trace) {
console.error(`Check if ${filePath} is under rootDirs element ${eachRootDir}`);
}
if (path.relative(eachRootDir, filePath).indexOf('.') !== 0) {
root = eachRootDir;
}
}
// transplant the codegen path to be inside the `genDir`
let relativePath: string = path.relative(root, filePath);
while (relativePath.startsWith('..' + path.sep)) {
// Strip out any `..` path such as: `../node_modules/@foo` as we want to put everything
// into `genDir`.
relativePath = relativePath.substr(3);
}
return path.join(this.options.genDir, relativePath);
}
}
export class CompilerHostContextAdapter {
protected assumedExists: {[fileName: string]: boolean} = {};
assumeFileExists(fileName: string): void { this.assumedExists[fileName] = true; }
}
export class ModuleResolutionHostAdapter extends CompilerHostContextAdapter implements
CompilerHostContext {
public directoryExists: ((directoryName: string) => boolean)|undefined;
constructor(private host: ts.ModuleResolutionHost) {
super();
if (host.directoryExists) {
this.directoryExists = (directoryName: string) => host.directoryExists(directoryName);
}
}
fileExists(fileName: string): boolean {
return this.assumedExists[fileName] || this.host.fileExists(fileName);
}
readFile(fileName: string): string { return this.host.readFile(fileName); }
readResource(s: string) {
if (!this.host.fileExists(s)) {
// TODO: We should really have a test for error cases like this!
throw new Error(`Compilation failed. Resource file not found: ${s}`);
}
return Promise.resolve(this.host.readFile(s));
}
}
export class NodeCompilerHostContext extends CompilerHostContextAdapter implements
CompilerHostContext {
fileExists(fileName: string): boolean {
return this.assumedExists[fileName] || fs.existsSync(fileName);
}
directoryExists(directoryName: string): boolean {
try {
return fs.statSync(directoryName).isDirectory();
} catch (e) {
return false;
}
}
readFile(fileName: string): string { return fs.readFileSync(fileName, 'utf8'); }
readResource(s: string) {
if (!this.fileExists(s)) {
// TODO: We should really have a test for error cases like this!
throw new Error(`Compilation failed. Resource file not found: ${s}`);
}
return Promise.resolve(this.readFile(s));
}
}

View File

@ -14,52 +14,15 @@
// Must be imported first, because angular2 decorators throws on load.
import 'reflect-metadata';
import * as compiler from '@angular/compiler';
import * as tsc from '@angular/tsc-wrapped';
import * as path from 'path';
import * as ts from 'typescript';
import {Extractor} from './extractor';
function extract(
ngOptions: tsc.AngularCompilerOptions, cliOptions: tsc.I18nExtractionCliOptions,
program: ts.Program, host: ts.CompilerHost) {
const resourceLoader: compiler.ResourceLoader = {
get: (s: string) => {
if (!host.fileExists(s)) {
// TODO: We should really have a test for error cases like this!
throw new Error(`Compilation failed. Resource file not found: ${s}`);
}
return Promise.resolve(host.readFile(s));
}
};
const extractor =
Extractor.create(ngOptions, cliOptions.i18nFormat, program, host, resourceLoader);
const bundlePromise: Promise<compiler.MessageBundle> = extractor.extract();
return (bundlePromise).then(messageBundle => {
let ext: string;
let serializer: compiler.Serializer;
const format = (cliOptions.i18nFormat || 'xlf').toLowerCase();
switch (format) {
case 'xmb':
ext = 'xmb';
serializer = new compiler.Xmb();
break;
case 'xliff':
case 'xlf':
default:
const htmlParser = new compiler.I18NHtmlParser(new compiler.HtmlParser());
ext = 'xlf';
serializer = new compiler.Xliff(htmlParser, compiler.DEFAULT_INTERPOLATION_CONFIG);
break;
}
const dstPath = path.join(ngOptions.genDir, `messages.${ext}`);
host.writeFile(dstPath, messageBundle.write(serializer), false);
});
program: ts.Program, host: ts.CompilerHost): Promise<void> {
return Extractor.create(ngOptions, program, host).extract(cliOptions.i18nFormat);
}
// Entry point

View File

@ -14,86 +14,75 @@
import 'reflect-metadata';
import * as compiler from '@angular/compiler';
import {ViewEncapsulation} from '@angular/core';
import * as tsc from '@angular/tsc-wrapped';
import * as path from 'path';
import * as ts from 'typescript';
import {extractProgramSymbols} from './codegen';
import {ReflectorHost} from './reflector_host';
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
import {StaticReflector, StaticSymbol} from './static_reflector';
import {CompilerHost, CompilerHostContext, ModuleResolutionHostAdapter} from './compiler_host';
import {PathMappedCompilerHost} from './path_mapped_compiler_host';
export class Extractor {
constructor(
private options: tsc.AngularCompilerOptions, private program: ts.Program,
public host: ts.CompilerHost, private staticReflector: StaticReflector,
private messageBundle: compiler.MessageBundle, private reflectorHost: ReflectorHost,
private metadataResolver: compiler.CompileMetadataResolver) {}
private options: tsc.AngularCompilerOptions, private ngExtractor: compiler.Extractor,
public host: ts.CompilerHost, private ngCompilerHost: CompilerHost,
private program: ts.Program) {}
extract(): Promise<compiler.MessageBundle> {
const programSymbols: StaticSymbol[] =
extractProgramSymbols(this.program, this.staticReflector, this.reflectorHost, this.options);
extract(formatName: string): Promise<void> {
// Checks the format and returns the extension
const ext = this.getExtension(formatName);
return compiler
.analyzeNgModules(programSymbols, {transitiveModules: true}, this.metadataResolver)
.then(({files}) => {
const errors: compiler.ParseError[] = [];
const promiseBundle = this.extractBundle();
files.forEach(file => {
const compMetas: compiler.CompileDirectiveMetadata[] = [];
file.directives.forEach(directiveType => {
const dirMeta = this.metadataResolver.getDirectiveMetadata(directiveType);
if (dirMeta && dirMeta.isComponent) {
compMetas.push(dirMeta);
}
return promiseBundle.then(bundle => {
const content = this.serialize(bundle, ext);
const dstPath = path.join(this.options.genDir, `messages.${ext}`);
this.host.writeFile(dstPath, content, false);
});
compMetas.forEach(compMeta => {
const html = compMeta.template.template;
const interpolationConfig =
compiler.InterpolationConfig.fromArray(compMeta.template.interpolation);
errors.push(
...this.messageBundle.updateFromTemplate(html, file.srcUrl, interpolationConfig));
});
});
if (errors.length) {
throw new Error(errors.map(e => e.toString()).join('\n'));
}
return this.messageBundle;
});
extractBundle(): Promise<compiler.MessageBundle> {
const files = this.program.getSourceFiles().map(
sf => this.ngCompilerHost.getCanonicalFileName(sf.fileName));
return this.ngExtractor.extract(files);
}
serialize(bundle: compiler.MessageBundle, ext: string): string {
let serializer: compiler.Serializer;
switch (ext) {
case 'xmb':
serializer = new compiler.Xmb();
break;
case 'xlf':
default:
serializer = new compiler.Xliff();
}
return bundle.write(serializer);
}
getExtension(formatName: string): string {
const format = (formatName || 'xlf').toLowerCase();
if (format === 'xmb') return 'xmb';
if (format === 'xlf' || format === 'xlif') return 'xlf';
throw new Error('Unsupported format "${formatName}"');
}
static create(
options: tsc.AngularCompilerOptions, translationsFormat: string, program: ts.Program,
compilerHost: ts.CompilerHost, resourceLoader: compiler.ResourceLoader,
reflectorHost?: ReflectorHost): Extractor {
const htmlParser = new compiler.I18NHtmlParser(new compiler.HtmlParser());
options: tsc.AngularCompilerOptions, program: ts.Program, tsCompilerHost: ts.CompilerHost,
compilerHostContext?: CompilerHostContext, ngCompilerHost?: CompilerHost): Extractor {
if (!ngCompilerHost) {
const usePathMapping = !!options.rootDirs && options.rootDirs.length > 0;
const context = compilerHostContext || new ModuleResolutionHostAdapter(tsCompilerHost);
ngCompilerHost = usePathMapping ? new PathMappedCompilerHost(program, options, context) :
new CompilerHost(program, options, context);
}
const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver();
if (!reflectorHost) reflectorHost = new ReflectorHost(program, compilerHost, options);
const staticReflector = new StaticReflector(reflectorHost);
StaticAndDynamicReflectionCapabilities.install(staticReflector);
const {extractor: ngExtractor} = compiler.Extractor.create(ngCompilerHost);
const config = new compiler.CompilerConfig({
genDebugInfo: options.debug === true,
defaultEncapsulation: ViewEncapsulation.Emulated,
logBindingUpdate: false,
useJit: false
});
const normalizer =
new compiler.DirectiveNormalizer(resourceLoader, urlResolver, htmlParser, config);
const elementSchemaRegistry = new compiler.DomElementSchemaRegistry();
const resolver = new compiler.CompileMetadataResolver(
new compiler.NgModuleResolver(staticReflector),
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
elementSchemaRegistry, normalizer, staticReflector);
// TODO(vicb): implicit tags & attributes
const messageBundle = new compiler.MessageBundle(htmlParser, [], {});
return new Extractor(
options, program, compilerHost, staticReflector, messageBundle, reflectorHost, resolver);
return new Extractor(options, ngExtractor, tsCompilerHost, ngCompilerHost, program);
}
}

View File

@ -14,24 +14,34 @@ import 'reflect-metadata';
import * as ts from 'typescript';
import * as tsc from '@angular/tsc-wrapped';
import {SyntaxError} from '@angular/compiler';
import {CodeGenerator} from './codegen';
function codegen(
ngOptions: tsc.AngularCompilerOptions, cliOptions: tsc.NgcCliOptions, program: ts.Program,
host: ts.CompilerHost) {
return CodeGenerator.create(ngOptions, cliOptions, program, host).codegen({
transitiveModules: true
return CodeGenerator.create(ngOptions, cliOptions, program, host).codegen();
}
export function main(
args: any, consoleError: (s: string) => void = console.error): Promise<number> {
const project = args.p || args.project || '.';
const cliOptions = new tsc.NgcCliOptions(args);
return tsc.main(project, cliOptions, codegen).then(() => 0).catch(e => {
if (e instanceof tsc.UserError || e instanceof SyntaxError) {
consoleError(e.message);
return Promise.resolve(1);
} else {
consoleError(e.stack);
consoleError('Compilation failed');
return Promise.resolve(1);
}
});
}
// CLI entry point
if (require.main === module) {
const args = require('minimist')(process.argv.slice(2));
const project = args.p || args.project || '.';
const cliOptions = new tsc.NgcCliOptions(args);
tsc.main(project, cliOptions, codegen).then(exitCode => process.exit(exitCode)).catch(e => {
console.error(e.stack);
console.error('Compilation failed');
process.exit(1);
});
main(args).then((exitCode: number) => process.exit(exitCode));
}

View File

@ -0,0 +1,150 @@
/**
* @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
*/
/**
* This is a private API for the ngtools toolkit.
*
* This API should be stable for NG 2. It can be removed in NG 4..., but should be replaced by
* something else.
*/
import {AotCompilerHost, AotSummaryResolver, StaticReflector, StaticSymbolCache, StaticSymbolResolver} from '@angular/compiler';
import {AngularCompilerOptions, NgcCliOptions} from '@angular/tsc-wrapped';
import * as ts from 'typescript';
import {CodeGenerator} from './codegen';
import {CompilerHost, CompilerHostContext, ModuleResolutionHostAdapter} from './compiler_host';
import {Extractor} from './extractor';
import {listLazyRoutesOfModule} from './ngtools_impl';
import {PathMappedCompilerHost} from './path_mapped_compiler_host';
export interface NgTools_InternalApi_NG2_CodeGen_Options {
basePath: string;
compilerOptions: ts.CompilerOptions;
program: ts.Program;
host: ts.CompilerHost;
angularCompilerOptions: AngularCompilerOptions;
// i18n options.
i18nFormat: string;
i18nFile: string;
locale: string;
readResource: (fileName: string) => Promise<string>;
// Every new property under this line should be optional.
}
export interface NgTools_InternalApi_NG2_ListLazyRoutes_Options {
program: ts.Program;
host: ts.CompilerHost;
angularCompilerOptions: AngularCompilerOptions;
entryModule: string;
// Every new property under this line should be optional.
}
export interface NgTools_InternalApi_NG_2_LazyRouteMap { [route: string]: string; }
export interface NgTools_InternalApi_NG2_ExtractI18n_Options {
basePath: string;
compilerOptions: ts.CompilerOptions;
program: ts.Program;
host: ts.CompilerHost;
angularCompilerOptions: AngularCompilerOptions;
i18nFormat: string;
readResource: (fileName: string) => Promise<string>;
// Every new property under this line should be optional.
}
/**
* A ModuleResolutionHostAdapter that overrides the readResource() method with the one
* passed in the interface.
*/
class CustomLoaderModuleResolutionHostAdapter extends ModuleResolutionHostAdapter {
constructor(
private _readResource: (path: string) => Promise<string>, host: ts.ModuleResolutionHost) {
super(host);
}
readResource(path: string) { return this._readResource(path); }
}
/**
* @internal
* @private
*/
export class NgTools_InternalApi_NG_2 {
/**
* @internal
* @private
*/
static codeGen(options: NgTools_InternalApi_NG2_CodeGen_Options): Promise<void> {
const hostContext: CompilerHostContext =
new CustomLoaderModuleResolutionHostAdapter(options.readResource, options.host);
const cliOptions: NgcCliOptions = {
i18nFormat: options.i18nFormat,
i18nFile: options.i18nFile,
locale: options.locale,
basePath: options.basePath
};
// Create the Code Generator.
const codeGenerator = CodeGenerator.create(
options.angularCompilerOptions, cliOptions, options.program, options.host, hostContext);
return codeGenerator.codegen();
}
/**
* @internal
* @private
*/
static listLazyRoutes(options: NgTools_InternalApi_NG2_ListLazyRoutes_Options):
NgTools_InternalApi_NG_2_LazyRouteMap {
const angularCompilerOptions = options.angularCompilerOptions;
const program = options.program;
const moduleResolutionHost = new ModuleResolutionHostAdapter(options.host);
const usePathMapping =
!!angularCompilerOptions.rootDirs && angularCompilerOptions.rootDirs.length > 0;
const ngCompilerHost: AotCompilerHost = usePathMapping ?
new PathMappedCompilerHost(program, angularCompilerOptions, moduleResolutionHost) :
new CompilerHost(program, angularCompilerOptions, moduleResolutionHost);
const symbolCache = new StaticSymbolCache();
const summaryResolver = new AotSummaryResolver(ngCompilerHost, symbolCache);
const symbolResolver = new StaticSymbolResolver(ngCompilerHost, symbolCache, summaryResolver);
const staticReflector = new StaticReflector(symbolResolver);
const routeMap = listLazyRoutesOfModule(options.entryModule, ngCompilerHost, staticReflector);
return Object.keys(routeMap).reduce(
(acc: NgTools_InternalApi_NG_2_LazyRouteMap, route: string) => {
acc[route] = routeMap[route].absoluteFilePath;
return acc;
},
{});
}
/**
* @internal
* @private
*/
static extractI18n(options: NgTools_InternalApi_NG2_ExtractI18n_Options): Promise<void> {
const hostContext: CompilerHostContext =
new CustomLoaderModuleResolutionHostAdapter(options.readResource, options.host);
// Create the i18n extractor.
const extractor = Extractor.create(
options.angularCompilerOptions, options.program, options.host, hostContext);
return extractor.extract(options.i18nFormat);
}
}

View File

@ -0,0 +1,211 @@
/**
* @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
*/
/**
* This is a private API for the ngtools toolkit.
*
* This API should be stable for NG 2. It can be removed in NG 4..., but should be replaced by
* something else.
*/
import {AotCompilerHost, StaticReflector, StaticSymbol} from '@angular/compiler';
import {NgModule} from '@angular/core';
// We cannot depend directly to @angular/router.
type Route = any;
const ROUTER_MODULE_PATH = '@angular/router/src/router_config_loader';
const ROUTER_ROUTES_SYMBOL_NAME = 'ROUTES';
// LazyRoute information between the extractors.
export interface LazyRoute {
routeDef: RouteDef;
absoluteFilePath: string;
}
export type LazyRouteMap = {
[route: string]: LazyRoute
};
// A route definition. Normally the short form 'path/to/module#ModuleClassName' is used by
// the user, and this is a helper class to extract information from it.
export class RouteDef {
private constructor(public readonly path: string, public readonly className: string = null) {}
toString() {
return (this.className === null || this.className == 'default') ?
this.path :
`${this.path}#${this.className}`;
}
static fromString(entry: string): RouteDef {
const split = entry.split('#');
return new RouteDef(split[0], split[1] || null);
}
}
/**
*
* @returns {LazyRouteMap}
* @private
*/
export function listLazyRoutesOfModule(
entryModule: string, host: AotCompilerHost, reflector: StaticReflector): LazyRouteMap {
const entryRouteDef = RouteDef.fromString(entryModule);
const containingFile = _resolveModule(entryRouteDef.path, entryRouteDef.path, host);
const modulePath = `./${containingFile.replace(/^(.*)\//, '')}`;
const className = entryRouteDef.className;
// List loadChildren of this single module.
const appStaticSymbol = reflector.findDeclaration(modulePath, className, containingFile);
const ROUTES = reflector.findDeclaration(ROUTER_MODULE_PATH, ROUTER_ROUTES_SYMBOL_NAME);
const lazyRoutes: LazyRoute[] =
_extractLazyRoutesFromStaticModule(appStaticSymbol, reflector, host, ROUTES);
const allLazyRoutes = lazyRoutes.reduce(
function includeLazyRouteAndSubRoutes(allRoutes: LazyRouteMap, lazyRoute: LazyRoute):
LazyRouteMap {
const route: string = lazyRoute.routeDef.toString();
_assertRoute(allRoutes, lazyRoute);
allRoutes[route] = lazyRoute;
// StaticReflector does not support discovering annotations like `NgModule` on default
// exports
// Which means: if a default export NgModule was lazy-loaded, we can discover it, but,
// we cannot parse its routes to see if they have loadChildren or not.
if (!lazyRoute.routeDef.className) {
return allRoutes;
}
const lazyModuleSymbol = reflector.findDeclaration(
lazyRoute.absoluteFilePath, lazyRoute.routeDef.className || 'default');
const subRoutes =
_extractLazyRoutesFromStaticModule(lazyModuleSymbol, reflector, host, ROUTES);
return subRoutes.reduce(includeLazyRouteAndSubRoutes, allRoutes);
},
{});
return allLazyRoutes;
}
/**
* Try to resolve a module, and returns its absolute path.
* @private
*/
function _resolveModule(modulePath: string, containingFile: string, host: AotCompilerHost) {
const result = host.moduleNameToFileName(modulePath, containingFile);
if (!result) {
throw new Error(`Could not resolve "${modulePath}" from "${containingFile}".`);
}
return result;
}
/**
* 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();
if (map[r] && map[r].absoluteFilePath != route.absoluteFilePath) {
throw new Error(
`Duplicated path in loadChildren detected: "${r}" is used in 2 loadChildren, ` +
`but they point to different modules "(${map[r].absoluteFilePath} and ` +
`"${route.absoluteFilePath}"). Webpack cannot distinguish on context and would fail to ` +
'load the proper one.');
}
}
/**
* Extract all the LazyRoutes from a module. This extracts all `loadChildren` keys from this
* module and all statically referred modules.
* @private
*/
function _extractLazyRoutesFromStaticModule(
staticSymbol: StaticSymbol, reflector: StaticReflector, host: AotCompilerHost,
ROUTES: StaticSymbol): LazyRoute[] {
const moduleMetadata = _getNgModuleMetadata(staticSymbol, reflector);
const allRoutes: any =
(moduleMetadata.imports || [])
.filter(i => 'providers' in i)
.reduce((mem: Route[], m: any) => {
return mem.concat(_collectRoutes(m.providers || [], reflector, ROUTES));
}, _collectRoutes(moduleMetadata.providers || [], reflector, ROUTES));
const lazyRoutes: LazyRoute[] =
_collectLoadChildren(allRoutes).reduce((acc: LazyRoute[], route: string) => {
const routeDef = RouteDef.fromString(route);
const absoluteFilePath = _resolveModule(routeDef.path, staticSymbol.filePath, host);
acc.push({routeDef, absoluteFilePath});
return acc;
}, []);
const importedSymbols = ((moduleMetadata.imports || []) as any[])
.filter(i => i instanceof StaticSymbol) as StaticSymbol[];
return importedSymbols
.reduce(
(acc: LazyRoute[], i: StaticSymbol) => {
return acc.concat(_extractLazyRoutesFromStaticModule(i, reflector, host, ROUTES));
},
[])
.concat(lazyRoutes);
}
/**
* 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);
if (ngModules.length === 0) {
throw new Error(`${staticSymbol.name} is not an NgModule`);
}
return ngModules[0];
}
/**
* Return the routes from the provider list.
* @private
*/
function _collectRoutes(
providers: any[], reflector: StaticReflector, ROUTES: StaticSymbol): Route[] {
return providers.reduce((routeList: Route[], p: any) => {
if (p.provide === ROUTES) {
return routeList.concat(p.useValue);
} else if (Array.isArray(p)) {
return routeList.concat(_collectRoutes(p, reflector, ROUTES));
} else {
return routeList;
}
}, []);
}
/**
* Return the loadChildren values of a list of Route.
* @private
*/
function _collectLoadChildren(routes: Route[]): string[] {
return routes.reduce((m, r) => {
if (r.loadChildren && typeof r.loadChildren === 'string') {
return m.concat(r.loadChildren);
} else if (Array.isArray(r)) {
return m.concat(_collectLoadChildren(r));
} else if (r.children) {
return m.concat(_collectLoadChildren(r.children));
} else {
return m;
}
}, []);
}

View File

@ -6,29 +6,27 @@
* found in the LICENSE file at https://angular.io/license
*/
import {StaticSymbol} from '@angular/compiler';
import {AngularCompilerOptions, ModuleMetadata} from '@angular/tsc-wrapped';
import * as fs from 'fs';
import * as path from 'path';
import * as ts from 'typescript';
import {ReflectorHost, ReflectorHostContext} from './reflector_host';
import {StaticSymbol} from './static_reflector';
import {CompilerHost, CompilerHostContext} from './compiler_host';
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
const DTS = /\.d\.ts$/;
/**
* This version of the reflector host expects that the program will be compiled
* This version of the AotCompilerHost expects that the program will be compiled
* and executed with a "path mapped" directory structure, where generated files
* are in a parallel tree with the sources, and imported using a `./` relative
* import. This requires using TS `rootDirs` option and also teaching the module
* loader what to do.
*/
export class PathMappedReflectorHost extends ReflectorHost {
constructor(
program: ts.Program, compilerHost: ts.CompilerHost, options: AngularCompilerOptions,
context?: ReflectorHostContext) {
super(program, compilerHost, options, context);
export class PathMappedCompilerHost extends CompilerHost {
constructor(program: ts.Program, options: AngularCompilerOptions, context: CompilerHostContext) {
super(program, options, context);
}
getCanonicalFileName(fileName: string): string {
@ -42,16 +40,23 @@ export class PathMappedReflectorHost extends ReflectorHost {
return fileName;
}
protected resolve(m: string, containingFile: string) {
moduleNameToFileName(m: string, containingFile: string) {
if (!containingFile || !containingFile.length) {
if (m.indexOf('.') === 0) {
throw new Error('Resolution of relative paths requires a containing file.');
}
// Any containing file gives the same result for absolute imports
containingFile = this.getCanonicalFileName(path.join(this.basePath, 'index.ts'));
}
for (const root of this.options.rootDirs || ['']) {
const rootedContainingFile = path.join(root, containingFile);
const resolved =
ts.resolveModuleName(m, rootedContainingFile, this.options, this.context).resolvedModule;
if (resolved) {
if (this.options.traceResolution) {
console.log('resolve', m, containingFile, '=>', resolved.resolvedFileName);
console.error('resolve', m, containingFile, '=>', resolved.resolvedFileName);
}
return resolved.resolvedFileName;
return this.getCanonicalFileName(resolved.resolvedFileName);
}
}
}
@ -62,12 +67,9 @@ export class PathMappedReflectorHost extends ReflectorHost {
* Relativize the paths by checking candidate prefixes of the absolute path, to see if
* they are resolvable by the moduleResolution strategy from the CompilerHost.
*/
getImportPath(containingFile: string, importedFile: string): string {
importedFile = this.resolveAssetUrl(importedFile, containingFile);
containingFile = this.resolveAssetUrl(containingFile, '');
fileNameToModuleName(importedFile: string, containingFile: string): string {
if (this.options.traceResolution) {
console.log(
console.error(
'getImportPath from containingFile', containingFile, 'to importedFile', importedFile);
}
@ -82,7 +84,7 @@ export class PathMappedReflectorHost extends ReflectorHost {
}
const resolvable = (candidate: string) => {
const resolved = this.getCanonicalFileName(this.resolve(candidate, importedFile));
const resolved = this.moduleNameToFileName(candidate, importedFile);
return resolved && resolved.replace(EXT, '') === importedFile.replace(EXT, '');
};
@ -112,10 +114,10 @@ export class PathMappedReflectorHost extends ReflectorHost {
`Unable to find any resolvable import for ${importedFile} relative to ${containingFile}`);
}
getMetadataFor(filePath: string): ModuleMetadata {
getMetadataFor(filePath: string): ModuleMetadata[] {
for (const root of this.options.rootDirs || []) {
const rootedPath = path.join(root, filePath);
if (!this.compilerHost.fileExists(rootedPath)) {
if (!this.context.fileExists(rootedPath)) {
// If the file doesn't exists then we cannot return metadata for the file.
// This will occur if the user refernced a declared module for which no file
// exists for the module (i.e. jQuery or angularjs).
@ -124,16 +126,13 @@ export class PathMappedReflectorHost extends ReflectorHost {
if (DTS.test(rootedPath)) {
const metadataPath = rootedPath.replace(DTS, '.metadata.json');
if (this.context.fileExists(metadataPath)) {
const metadata = this.readMetadata(metadataPath);
return (Array.isArray(metadata) && metadata.length == 0) ? undefined : metadata;
return this.readMetadata(metadataPath, rootedPath);
}
} else {
const sf = this.program.getSourceFile(rootedPath);
if (!sf) {
throw new Error(`Source file ${rootedPath} not present in program.`);
}
sf.fileName = this.getCanonicalFileName(sf.fileName);
return this.metadataCollector.getMetadata(sf);
const sf = this.getSourceFile(rootedPath);
sf.fileName = sf.fileName;
const metadata = this.metadataCollector.getMetadata(sf);
return metadata ? [metadata] : [];
}
}
}

View File

@ -9,16 +9,10 @@
import {__core_private__ as r} from '@angular/core';
export type ReflectorReader = typeof r._ReflectorReader;
export var ReflectorReader: typeof r.ReflectorReader = r.ReflectorReader;
export const ReflectorReader: typeof r.ReflectorReader = r.ReflectorReader;
export type ReflectionCapabilities = typeof r._ReflectionCapabilities;
export var ReflectionCapabilities: typeof r.ReflectionCapabilities = r.ReflectionCapabilities;
export const ReflectionCapabilities: typeof r.ReflectionCapabilities = r.ReflectionCapabilities;
export type Console = typeof r._Console;
export var Console: typeof r.Console = r.Console;
export var reflector: typeof r.reflector = r.reflector;
export type SetterFn = typeof r._SetterFn;
export type GetterFn = typeof r._GetterFn;
export type MethodFn = typeof r._MethodFn;
export const Console: typeof r.Console = r.Console;

View File

@ -1,354 +0,0 @@
/**
* @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 {AssetUrl, ImportGenerator} from '@angular/compiler';
import {AngularCompilerOptions, MetadataCollector, ModuleMetadata} from '@angular/tsc-wrapped';
import * as fs from 'fs';
import * as path from 'path';
import * as ts from 'typescript';
import {StaticReflectorHost, StaticSymbol} from './static_reflector';
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
const DTS = /\.d\.ts$/;
const NODE_MODULES = '/node_modules/';
const IS_GENERATED = /\.(ngfactory|css(\.shim)?)$/;
export interface ReflectorHostContext {
fileExists(fileName: string): boolean;
directoryExists(directoryName: string): boolean;
readFile(fileName: string): string;
assumeFileExists(fileName: string): void;
}
export class ReflectorHost implements StaticReflectorHost, ImportGenerator {
protected metadataCollector = new MetadataCollector();
protected context: ReflectorHostContext;
private isGenDirChildOfRootDir: boolean;
protected basePath: string;
private genDir: string;
constructor(
protected program: ts.Program, protected compilerHost: ts.CompilerHost,
protected options: AngularCompilerOptions, context?: ReflectorHostContext) {
// 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, '/');
this.context = context || new NodeReflectorHostContext(compilerHost);
const genPath: string = path.relative(this.basePath, this.genDir);
this.isGenDirChildOfRootDir = genPath === '' || !genPath.startsWith('..');
}
angularImportLocations() {
return {
coreDecorators: '@angular/core/src/metadata',
diDecorators: '@angular/core/src/di/metadata',
diMetadata: '@angular/core/src/di/metadata',
diOpaqueToken: '@angular/core/src/di/opaque_token',
animationMetadata: '@angular/core/src/animation/metadata',
provider: '@angular/core/src/di/provider'
};
}
// We use absolute paths on disk as canonical.
getCanonicalFileName(fileName: string): string { return fileName; }
protected resolve(m: string, containingFile: string) {
m = m.replace(EXT, '');
const resolved =
ts.resolveModuleName(m, containingFile.replace(/\\/g, '/'), this.options, this.context)
.resolvedModule;
return resolved ? resolved.resolvedFileName : null;
};
protected normalizeAssetUrl(url: string): string {
const assetUrl = AssetUrl.parse(url);
const path = assetUrl ? `${assetUrl.packageName}/${assetUrl.modulePath}` : null;
return this.getCanonicalFileName(path);
}
protected resolveAssetUrl(url: string, containingFile: string): string {
const assetUrl = this.normalizeAssetUrl(url);
if (assetUrl) {
return this.getCanonicalFileName(this.resolve(assetUrl, containingFile));
}
return url;
}
/**
* We want a moduleId that will appear in import statements in the generated code.
* These need to be in a form that system.js can load, so absolute file paths don't work.
*
* The `containingFile` is always in the `genDir`, where as the `importedFile` can be in
* `genDir`, `node_module` or `basePath`. The `importedFile` is either a generated file or
* existing file.
*
* | genDir | node_module | rootDir
* --------------+----------+-------------+----------
* generated | relative | relative | n/a
* existing file | n/a | absolute | relative(*)
*
* NOTE: (*) the relative path is computed depending on `isGenDirChildOfRootDir`.
*/
getImportPath(containingFile: string, importedFile: string): string {
importedFile = this.resolveAssetUrl(importedFile, containingFile);
containingFile = this.resolveAssetUrl(containingFile, '');
// If a file does not yet exist (because we compile it later), we still need to
// assume it exists it so that the `resolve` method works!
if (!this.compilerHost.fileExists(importedFile)) {
this.context.assumeFileExists(importedFile);
}
containingFile = this.rewriteGenDirPath(containingFile);
const containingDir = path.dirname(containingFile);
// drop extension
importedFile = importedFile.replace(EXT, '');
const nodeModulesIndex = importedFile.indexOf(NODE_MODULES);
const importModule = nodeModulesIndex === -1 ?
null :
importedFile.substring(nodeModulesIndex + NODE_MODULES.length);
const isGeneratedFile = IS_GENERATED.test(importedFile);
if (isGeneratedFile) {
// rewrite to genDir path
if (importModule) {
// it is generated, therefore we do a relative path to the factory
return this.dotRelative(containingDir, this.genDir + NODE_MODULES + importModule);
} else {
// assume that import is also in `genDir`
importedFile = this.rewriteGenDirPath(importedFile);
return this.dotRelative(containingDir, importedFile);
}
} else {
// user code import
if (importModule) {
return importModule;
} else {
if (!this.isGenDirChildOfRootDir) {
// assume that they are on top of each other.
importedFile = importedFile.replace(this.basePath, this.genDir);
}
return this.dotRelative(containingDir, importedFile);
}
}
}
private dotRelative(from: string, to: string): string {
const rPath: string = path.relative(from, to).replace(/\\/g, '/');
return rPath.startsWith('.') ? rPath : './' + rPath;
}
/**
* Moves the path into `genDir` folder while preserving the `node_modules` directory.
*/
private rewriteGenDirPath(filepath: string) {
const nodeModulesIndex = filepath.indexOf(NODE_MODULES);
if (nodeModulesIndex !== -1) {
// If we are in node_modulse, transplant them into `genDir`.
return path.join(this.genDir, filepath.substring(nodeModulesIndex));
} else {
// pretend that containing file is on top of the `genDir` to normalize the paths.
// we apply the `genDir` => `rootDir` delta through `rootDirPrefix` later.
return filepath.replace(this.basePath, this.genDir);
}
}
findDeclaration(
module: string, symbolName: string, containingFile: string,
containingModule?: string): StaticSymbol {
if (!containingFile || !containingFile.length) {
if (module.indexOf('.') === 0) {
throw new Error('Resolution of relative paths requires a containing file.');
}
// Any containing file gives the same result for absolute imports
containingFile = path.join(this.basePath, 'index.ts');
}
try {
const assetUrl = this.normalizeAssetUrl(module);
if (assetUrl) {
module = assetUrl;
}
const filePath = this.resolve(module, containingFile);
if (!filePath) {
// If the file cannot be found the module is probably referencing a declared module
// for which there is no disambiguating file and we also don't need to track
// re-exports. Just use the module name.
return this.getStaticSymbol(module, symbolName);
}
const tc = this.program.getTypeChecker();
const sf = this.program.getSourceFile(filePath);
if (!sf || !(<any>sf).symbol) {
// The source file was not needed in the compile but we do need the values from
// the corresponding .ts files stored in the .metadata.json file. Check the file
// for exports to see if the file is exported.
return this.resolveExportedSymbol(filePath, symbolName) ||
this.getStaticSymbol(filePath, symbolName);
}
let symbol = tc.getExportsOfModule((<any>sf).symbol).find(m => m.name === symbolName);
if (!symbol) {
throw new Error(`can't find symbol ${symbolName} exported from module ${filePath}`);
}
if (symbol &&
symbol.flags & ts.SymbolFlags.Alias) { // This is an alias, follow what it aliases
symbol = tc.getAliasedSymbol(symbol);
}
const declaration = symbol.getDeclarations()[0];
const declarationFile = this.getCanonicalFileName(declaration.getSourceFile().fileName);
return this.getStaticSymbol(declarationFile, symbol.getName());
} catch (e) {
console.error(`can't resolve module ${module} from ${containingFile}`);
throw e;
}
}
private typeCache = new Map<string, StaticSymbol>();
private resolverCache = new Map<string, ModuleMetadata>();
/**
* getStaticSymbol produces a Type whose metadata is known but whose implementation is not loaded.
* All types passed to the StaticResolver should be pseudo-types returned by this method.
*
* @param declarationFile the absolute path of the file where the symbol is declared
* @param name the name of the type.
*/
getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol {
const memberSuffix = members ? `.${ members.join('.')}` : '';
const key = `"${declarationFile}".${name}${memberSuffix}`;
let result = this.typeCache.get(key);
if (!result) {
result = new StaticSymbol(declarationFile, name, members);
this.typeCache.set(key, result);
}
return result;
}
getMetadataFor(filePath: string): ModuleMetadata {
if (!this.context.fileExists(filePath)) {
// If the file doesn't exists then we cannot return metadata for the file.
// This will occur if the user refernced a declared module for which no file
// exists for the module (i.e. jQuery or angularjs).
return;
}
if (DTS.test(filePath)) {
const metadataPath = filePath.replace(DTS, '.metadata.json');
if (this.context.fileExists(metadataPath)) {
const metadata = this.readMetadata(metadataPath);
return (Array.isArray(metadata) && metadata.length == 0) ? undefined : metadata;
}
} else {
const sf = this.program.getSourceFile(filePath);
if (!sf) {
if (this.context.fileExists(filePath)) {
const sourceText = this.context.readFile(filePath);
return this.metadataCollector.getMetadata(
ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true));
}
throw new Error(`Source file ${filePath} not present in program.`);
}
return this.metadataCollector.getMetadata(sf);
}
}
readMetadata(filePath: string) {
try {
return this.resolverCache.get(filePath) || JSON.parse(this.context.readFile(filePath));
} catch (e) {
console.error(`Failed to read JSON file ${filePath}`);
throw e;
}
}
private getResolverMetadata(filePath: string): ModuleMetadata {
let metadata = this.resolverCache.get(filePath);
if (!metadata) {
metadata = this.getMetadataFor(filePath);
this.resolverCache.set(filePath, metadata);
}
return metadata;
}
protected resolveExportedSymbol(filePath: string, symbolName: string): StaticSymbol {
const resolveModule = (moduleName: string): string => {
const resolvedModulePath = this.getCanonicalFileName(this.resolve(moduleName, filePath));
if (!resolvedModulePath) {
throw new Error(`Could not resolve module '${moduleName}' relative to file ${filePath}`);
}
return resolvedModulePath;
};
const metadata = this.getResolverMetadata(filePath);
if (metadata) {
// If we have metadata for the symbol, this is the original exporting location.
if (metadata.metadata[symbolName]) {
return this.getStaticSymbol(filePath, symbolName);
}
// If no, try to find the symbol in one of the re-export location
if (metadata.exports) {
// Try and find the symbol in the list of explicitly re-exported symbols.
for (const moduleExport of metadata.exports) {
if (moduleExport.export) {
const exportSymbol = moduleExport.export.find(symbol => {
if (typeof symbol === 'string') {
return symbol == symbolName;
} else {
return symbol.as == symbolName;
}
});
if (exportSymbol) {
let symName = symbolName;
if (typeof exportSymbol !== 'string') {
symName = exportSymbol.name;
}
return this.resolveExportedSymbol(resolveModule(moduleExport.from), symName);
}
}
}
// Try to find the symbol via export * directives.
for (const moduleExport of metadata.exports) {
if (!moduleExport.export) {
const resolvedModule = resolveModule(moduleExport.from);
const candidateSymbol = this.resolveExportedSymbol(resolvedModule, symbolName);
if (candidateSymbol) return candidateSymbol;
}
}
}
}
return null;
}
}
export class NodeReflectorHostContext implements ReflectorHostContext {
constructor(private host: ts.CompilerHost) {}
private assumedExists: {[fileName: string]: boolean} = {};
fileExists(fileName: string): boolean {
return this.assumedExists[fileName] || this.host.fileExists(fileName);
}
directoryExists(directoryName: string): boolean {
try {
return fs.statSync(directoryName).isDirectory();
} catch (e) {
return false;
}
}
readFile(fileName: string): string { return fs.readFileSync(fileName, 'utf8'); }
assumeFileExists(fileName: string): void { this.assumedExists[fileName] = true; }
}

View File

@ -1,675 +0,0 @@
/**
* @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 {Attribute, Component, ContentChild, ContentChildren, Directive, Host, HostBinding, HostListener, Inject, Injectable, Input, NgModule, Optional, Output, Pipe, Self, SkipSelf, ViewChild, ViewChildren, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
import {ReflectorReader} from './private_import_core';
const SUPPORTED_SCHEMA_VERSION = 1;
/**
* The host of the static resolver is expected to be able to provide module metadata in the form of
* ModuleMetadata. Angular 2 CLI will produce this metadata for a module whenever a .d.ts files is
* produced and the module has exported variables or classes with decorators. Module metadata can
* also be produced directly from TypeScript sources by using MetadataCollector in tools/metadata.
*/
export interface StaticReflectorHost {
/**
* Return a ModuleMetadata for the given module.
*
* @param modulePath is a string identifier for a module as an absolute path.
* @returns the metadata for the given module.
*/
getMetadataFor(modulePath: string): {[key: string]: any}|{[key: string]: any}[];
/**
* Resolve a symbol from an import statement form, to the file where it is declared.
* @param module the location imported from
* @param containingFile for relative imports, the path of the file containing the import
*/
findDeclaration(modulePath: string, symbolName: string, containingFile?: string): StaticSymbol;
getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol;
angularImportLocations(): {
coreDecorators: string,
diDecorators: string,
diMetadata: string,
diOpaqueToken: string,
animationMetadata: string,
provider: string
};
getCanonicalFileName(fileName: string): string;
}
/**
* A token representing the a reference to a static type.
*
* This token is unique for a filePath and name and can be used as a hash table key.
*/
export class StaticSymbol {
constructor(public filePath: string, public name: string, public members?: string[]) {}
}
/**
* A static reflector implements enough of the Reflector API that is necessary to compile
* templates statically.
*/
export class StaticReflector implements ReflectorReader {
private annotationCache = new Map<StaticSymbol, any[]>();
private propertyCache = new Map<StaticSymbol, {[key: string]: any}>();
private parameterCache = new Map<StaticSymbol, any[]>();
private metadataCache = new Map<string, {[key: string]: any}>();
private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>();
private opaqueToken: StaticSymbol;
constructor(private host: StaticReflectorHost) { this.initializeConversionMap(); }
importUri(typeOrFunc: StaticSymbol): string {
const staticSymbol = this.host.findDeclaration(typeOrFunc.filePath, typeOrFunc.name, '');
return staticSymbol ? staticSymbol.filePath : null;
}
resolveIdentifier(name: string, moduleUrl: string, runtime: any): any {
return this.host.findDeclaration(moduleUrl, name, '');
}
resolveEnum(enumIdentifier: any, name: string): any {
const staticSymbol: StaticSymbol = enumIdentifier;
return this.host.getStaticSymbol(staticSymbol.filePath, staticSymbol.name, [name]);
}
public annotations(type: StaticSymbol): any[] {
let annotations = this.annotationCache.get(type);
if (!annotations) {
const classMetadata = this.getTypeMetadata(type);
if (classMetadata['decorators']) {
annotations = this.simplify(type, classMetadata['decorators']);
} else {
annotations = [];
}
this.annotationCache.set(type, annotations.filter(ann => !!ann));
}
return annotations;
}
public propMetadata(type: StaticSymbol): {[key: string]: any} {
let propMetadata = this.propertyCache.get(type);
if (!propMetadata) {
const classMetadata = this.getTypeMetadata(type);
const members = classMetadata ? classMetadata['members'] : {};
propMetadata = mapStringMap(members, (propData, propName) => {
const prop = (<any[]>propData)
.find(a => a['__symbolic'] == 'property' || a['__symbolic'] == 'method');
if (prop && prop['decorators']) {
return this.simplify(type, prop['decorators']);
} else {
return [];
}
});
this.propertyCache.set(type, propMetadata);
}
return propMetadata;
}
public parameters(type: StaticSymbol): any[] {
if (!(type instanceof StaticSymbol)) {
throw new Error(`parameters received ${JSON.stringify(type)} which is not a StaticSymbol`);
}
try {
let parameters = this.parameterCache.get(type);
if (!parameters) {
const classMetadata = this.getTypeMetadata(type);
const members = classMetadata ? classMetadata['members'] : null;
const ctorData = members ? members['__ctor__'] : null;
if (ctorData) {
const ctor = (<any[]>ctorData).find(a => a['__symbolic'] == 'constructor');
const parameterTypes = <any[]>this.simplify(type, ctor['parameters'] || []);
const parameterDecorators = <any[]>this.simplify(type, ctor['parameterDecorators'] || []);
parameters = [];
parameterTypes.forEach((paramType, index) => {
const nestedResult: any[] = [];
if (paramType) {
nestedResult.push(paramType);
}
const decorators = parameterDecorators ? parameterDecorators[index] : null;
if (decorators) {
nestedResult.push(...decorators);
}
parameters.push(nestedResult);
});
}
if (!parameters) {
parameters = [];
}
this.parameterCache.set(type, parameters);
}
return parameters;
} catch (e) {
console.log(`Failed on type ${JSON.stringify(type)} with error ${e}`);
throw e;
}
}
hasLifecycleHook(type: any, lcProperty: string): boolean {
if (!(type instanceof StaticSymbol)) {
throw new Error(
`hasLifecycleHook received ${JSON.stringify(type)} which is not a StaticSymbol`);
}
const classMetadata = this.getTypeMetadata(type);
const members = classMetadata ? classMetadata['members'] : null;
const member: any[] =
members && members.hasOwnProperty(lcProperty) ? members[lcProperty] : null;
return member ? member.some(a => a['__symbolic'] == 'method') : false;
}
private registerDecoratorOrConstructor(type: StaticSymbol, ctor: any): void {
this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => new ctor(...args));
}
private registerFunction(type: StaticSymbol, fn: any): void {
this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => fn.apply(undefined, args));
}
private initializeConversionMap(): void {
const {coreDecorators, diDecorators, diMetadata, diOpaqueToken, animationMetadata, provider} =
this.host.angularImportLocations();
this.opaqueToken = this.host.findDeclaration(diOpaqueToken, 'OpaqueToken');
this.registerDecoratorOrConstructor(this.host.findDeclaration(diDecorators, 'Host'), Host);
this.registerDecoratorOrConstructor(
this.host.findDeclaration(diDecorators, 'Injectable'), Injectable);
this.registerDecoratorOrConstructor(this.host.findDeclaration(diDecorators, 'Self'), Self);
this.registerDecoratorOrConstructor(
this.host.findDeclaration(diDecorators, 'SkipSelf'), SkipSelf);
this.registerDecoratorOrConstructor(this.host.findDeclaration(diDecorators, 'Inject'), Inject);
this.registerDecoratorOrConstructor(
this.host.findDeclaration(diDecorators, 'Optional'), Optional);
this.registerDecoratorOrConstructor(
this.host.findDeclaration(coreDecorators, 'Attribute'), Attribute);
this.registerDecoratorOrConstructor(
this.host.findDeclaration(coreDecorators, 'ContentChild'), ContentChild);
this.registerDecoratorOrConstructor(
this.host.findDeclaration(coreDecorators, 'ContentChildren'), ContentChildren);
this.registerDecoratorOrConstructor(
this.host.findDeclaration(coreDecorators, 'ViewChild'), ViewChild);
this.registerDecoratorOrConstructor(
this.host.findDeclaration(coreDecorators, 'ViewChildren'), ViewChildren);
this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'Input'), Input);
this.registerDecoratorOrConstructor(
this.host.findDeclaration(coreDecorators, 'Output'), Output);
this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'Pipe'), Pipe);
this.registerDecoratorOrConstructor(
this.host.findDeclaration(coreDecorators, 'HostBinding'), HostBinding);
this.registerDecoratorOrConstructor(
this.host.findDeclaration(coreDecorators, 'HostListener'), HostListener);
this.registerDecoratorOrConstructor(
this.host.findDeclaration(coreDecorators, 'Directive'), Directive);
this.registerDecoratorOrConstructor(
this.host.findDeclaration(coreDecorators, 'Component'), Component);
this.registerDecoratorOrConstructor(
this.host.findDeclaration(coreDecorators, 'NgModule'), NgModule);
// Note: Some metadata classes can be used directly with Provider.deps.
this.registerDecoratorOrConstructor(this.host.findDeclaration(diMetadata, 'Host'), Host);
this.registerDecoratorOrConstructor(this.host.findDeclaration(diMetadata, 'Self'), Self);
this.registerDecoratorOrConstructor(
this.host.findDeclaration(diMetadata, 'SkipSelf'), SkipSelf);
this.registerDecoratorOrConstructor(
this.host.findDeclaration(diMetadata, 'Optional'), Optional);
this.registerFunction(this.host.findDeclaration(animationMetadata, 'trigger'), trigger);
this.registerFunction(this.host.findDeclaration(animationMetadata, 'state'), state);
this.registerFunction(this.host.findDeclaration(animationMetadata, 'transition'), transition);
this.registerFunction(this.host.findDeclaration(animationMetadata, 'style'), style);
this.registerFunction(this.host.findDeclaration(animationMetadata, 'animate'), animate);
this.registerFunction(this.host.findDeclaration(animationMetadata, 'keyframes'), keyframes);
this.registerFunction(this.host.findDeclaration(animationMetadata, 'sequence'), sequence);
this.registerFunction(this.host.findDeclaration(animationMetadata, 'group'), group);
}
/** @internal */
public simplify(context: StaticSymbol, value: any): any {
const _this = this;
let scope = BindingScope.empty;
const calling = new Map<StaticSymbol, boolean>();
function simplifyInContext(context: StaticSymbol, value: any, depth: number): any {
function resolveReference(context: StaticSymbol, expression: any): StaticSymbol {
let staticSymbol: StaticSymbol;
if (expression['module']) {
staticSymbol = _this.host.findDeclaration(
expression['module'], expression['name'], context.filePath);
} else {
staticSymbol = _this.host.getStaticSymbol(context.filePath, expression['name']);
}
return staticSymbol;
}
function resolveReferenceValue(staticSymbol: StaticSymbol): any {
const moduleMetadata = _this.getModuleMetadata(staticSymbol.filePath);
const declarationValue =
moduleMetadata ? moduleMetadata['metadata'][staticSymbol.name] : null;
return declarationValue;
}
function isOpaqueToken(context: StaticSymbol, value: any): boolean {
if (value && value.__symbolic === 'new' && value.expression) {
const target = value.expression;
if (target.__symbolic == 'reference') {
return sameSymbol(resolveReference(context, target), _this.opaqueToken);
}
}
return false;
}
function simplifyCall(expression: any) {
let callContext: {[name: string]: string}|undefined = undefined;
if (expression['__symbolic'] == 'call') {
const target = expression['expression'];
let functionSymbol: StaticSymbol;
let targetFunction: any;
if (target) {
switch (target.__symbolic) {
case 'reference':
// Find the function to call.
callContext = {name: target.name};
functionSymbol = resolveReference(context, target);
targetFunction = resolveReferenceValue(functionSymbol);
break;
case 'select':
// Find the static method to call
if (target.expression.__symbolic == 'reference') {
functionSymbol = resolveReference(context, target.expression);
const classData = resolveReferenceValue(functionSymbol);
if (classData && classData.statics) {
targetFunction = classData.statics[target.member];
}
}
break;
}
}
if (targetFunction && targetFunction['__symbolic'] == 'function') {
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')) {
// Determine the arguments
const args: any[] =
(expression['arguments'] || []).map((arg: any) => simplify(arg));
const parameters: string[] = targetFunction['parameters'];
const defaults: any[] = targetFunction.defaults;
if (defaults && defaults.length > args.length) {
args.push(...defaults.slice(args.length).map((value: any) => simplify(value)));
}
const functionScope = BindingScope.build();
for (let i = 0; i < parameters.length; i++) {
functionScope.define(parameters[i], args[i]);
}
const oldScope = scope;
let result: any;
try {
scope = functionScope.done();
result = simplifyInContext(functionSymbol, value, depth + 1);
} finally {
scope = oldScope;
}
return result;
}
} finally {
calling.delete(functionSymbol);
}
}
}
if (depth === 0) {
// If depth is 0 we are evaluating the top level expression that is describing element
// decorator. In this case, it is a decorator we don't understand, such as a custom
// non-angular decorator, and we should just ignore it.
return {__symbolic: 'ignore'};
}
return simplify(
{__symbolic: 'error', message: 'Function call not supported', context: callContext});
}
function simplify(expression: any): any {
if (isPrimitive(expression)) {
return expression;
}
if (expression instanceof Array) {
const result: any[] = [];
for (const item of (<any>expression)) {
// Check for a spread expression
if (item && item.__symbolic === 'spread') {
const spreadArray = simplify(item.expression);
if (Array.isArray(spreadArray)) {
for (const spreadItem of spreadArray) {
result.push(spreadItem);
}
continue;
}
}
const value = simplify(item);
if (shouldIgnore(value)) {
continue;
}
result.push(value);
}
return result;
}
if (expression instanceof StaticSymbol) {
return expression;
}
if (expression) {
if (expression['__symbolic']) {
let staticSymbol: StaticSymbol;
switch (expression['__symbolic']) {
case 'binop':
let left = simplify(expression['left']);
if (shouldIgnore(left)) return left;
let right = simplify(expression['right']);
if (shouldIgnore(right)) return right;
switch (expression['operator']) {
case '&&':
return left && right;
case '||':
return left || right;
case '|':
return left | right;
case '^':
return left ^ right;
case '&':
return left & right;
case '==':
return left == right;
case '!=':
return left != right;
case '===':
return left === right;
case '!==':
return left !== right;
case '<':
return left < right;
case '>':
return left > right;
case '<=':
return left <= right;
case '>=':
return left >= right;
case '<<':
return left << right;
case '>>':
return left >> right;
case '+':
return left + right;
case '-':
return left - right;
case '*':
return left * right;
case '/':
return left / right;
case '%':
return left % right;
}
return null;
case 'if':
let condition = simplify(expression['condition']);
return condition ? simplify(expression['thenExpression']) :
simplify(expression['elseExpression']);
case 'pre':
let operand = simplify(expression['operand']);
if (shouldIgnore(operand)) return operand;
switch (expression['operator']) {
case '+':
return operand;
case '-':
return -operand;
case '!':
return !operand;
case '~':
return ~operand;
}
return null;
case 'index':
let indexTarget = simplify(expression['expression']);
let index = simplify(expression['index']);
if (indexTarget && isPrimitive(index)) return indexTarget[index];
return null;
case 'select':
let selectTarget = simplify(expression['expression']);
if (selectTarget instanceof StaticSymbol) {
// Access to a static instance variable
const declarationValue = resolveReferenceValue(selectTarget);
if (declarationValue && declarationValue.statics) {
selectTarget = declarationValue.statics;
} else {
const member: string = expression['member'];
const members = selectTarget.members ?
(selectTarget.members as string[]).concat(member) :
[member];
return _this.host.getStaticSymbol(
selectTarget.filePath, selectTarget.name, members);
}
}
const member = simplify(expression['member']);
if (selectTarget && isPrimitive(member)) return simplify(selectTarget[member]);
return null;
case 'reference':
if (!expression.module) {
const name: string = expression['name'];
const localValue = scope.resolve(name);
if (localValue != BindingScope.missing) {
return localValue;
}
}
staticSymbol = resolveReference(context, expression);
let result: any = staticSymbol;
let declarationValue = resolveReferenceValue(result);
if (declarationValue) {
if (isOpaqueToken(staticSymbol, declarationValue)) {
// If the referenced symbol is initalized by a new OpaqueToken we can keep the
// reference to the symbol.
return staticSymbol;
}
result = simplifyInContext(staticSymbol, declarationValue, depth + 1);
}
return result;
case 'class':
return context;
case 'function':
return context;
case 'new':
case 'call':
// Determine if the function is a built-in conversion
let target = expression['expression'];
if (target['module']) {
staticSymbol = _this.host.findDeclaration(
target['module'], target['name'], context.filePath);
} else {
staticSymbol = _this.host.getStaticSymbol(context.filePath, target['name']);
}
let converter = _this.conversionMap.get(staticSymbol);
if (converter) {
let args: any[] = expression['arguments'];
if (!args) {
args = [];
}
return converter(
context, args.map(arg => simplifyInContext(context, arg, depth + 1)));
}
// Determine if the function is one we can simplify.
return simplifyCall(expression);
case 'error':
let message = produceErrorMessage(expression);
if (expression['line']) {
message =
`${message} (position ${expression['line']+1}:${expression['character']+1} in the original .ts file)`;
throw positionalError(
message, context.filePath, expression['line'], expression['character']);
}
throw new Error(message);
}
return null;
}
return mapStringMap(expression, (value, name) => simplify(value));
}
return null;
}
try {
return simplify(value);
} catch (e) {
const message = `${e.message}, resolving symbol ${context.name} in ${context.filePath}`;
if (e.fileName) {
throw positionalError(message, e.fileName, e.line, e.column);
}
throw new Error(message);
}
}
const result = simplifyInContext(context, value, 0);
if (shouldIgnore(result)) {
return undefined;
}
return result;
}
/**
* @param module an absolute path to a module file.
*/
public getModuleMetadata(module: string): {[key: string]: any} {
let moduleMetadata = this.metadataCache.get(module);
if (!moduleMetadata) {
moduleMetadata = this.host.getMetadataFor(module);
if (Array.isArray(moduleMetadata)) {
moduleMetadata = moduleMetadata.find(md => md['version'] === SUPPORTED_SCHEMA_VERSION) ||
moduleMetadata[0];
}
if (!moduleMetadata) {
moduleMetadata =
{__symbolic: 'module', version: SUPPORTED_SCHEMA_VERSION, module: module, metadata: {}};
}
if (moduleMetadata['version'] != SUPPORTED_SCHEMA_VERSION) {
throw new Error(
`Metadata version mismatch for module ${module}, found version ${moduleMetadata['version']}, expected ${SUPPORTED_SCHEMA_VERSION}`);
}
this.metadataCache.set(module, moduleMetadata);
}
return moduleMetadata;
}
private getTypeMetadata(type: StaticSymbol): {[key: string]: any} {
const moduleMetadata = this.getModuleMetadata(type.filePath);
return moduleMetadata['metadata'][type.name] || {__symbolic: 'class'};
}
}
function expandedMessage(error: any): string {
switch (error.message) {
case 'Reference to non-exported class':
if (error.context && error.context.className) {
return `Reference to a non-exported class ${error.context.className}. Consider exporting the class`;
}
break;
case 'Variable not initialized':
return 'Only initialized variables and constants can be referenced because the value of this variable is needed by the template compiler';
case 'Destructuring not supported':
return 'Referencing an exported destructured variable or constant is not supported by the template compiler. Consider simplifying this to avoid destructuring';
case 'Could not resolve type':
if (error.context && error.context.typeName) {
return `Could not resolve type ${error.context.typeName}`;
}
break;
case 'Function call not supported':
let prefix =
error.context && error.context.name ? `Calling function '${error.context.name}', f` : 'F';
return prefix +
'unction calls are not supported. Consider replacing the function or lambda with a reference to an exported function';
case 'Reference to a local symbol':
if (error.context && error.context.name) {
return `Reference to a local (non-exported) symbol '${error.context.name}'. Consider exporting the symbol`;
}
}
return error.message;
}
function produceErrorMessage(error: any): string {
return `Error encountered resolving symbol values statically. ${expandedMessage(error)}`;
}
function mapStringMap(input: {[key: string]: any}, transform: (value: any, key: string) => any):
{[key: string]: any} {
if (!input) return {};
const result: {[key: string]: any} = {};
Object.keys(input).forEach((key) => {
const value = transform(input[key], key);
if (!shouldIgnore(value)) {
result[key] = value;
}
});
return result;
}
function isPrimitive(o: any): boolean {
return o === null || (typeof o !== 'function' && typeof o !== 'object');
}
interface BindingScopeBuilder {
define(name: string, value: any): BindingScopeBuilder;
done(): BindingScope;
}
abstract class BindingScope {
abstract resolve(name: string): any;
public static missing = {};
public static empty: BindingScope = {resolve: name => BindingScope.missing};
public static build(): BindingScopeBuilder {
const current = new Map<string, any>();
return {
define: function(name, value) {
current.set(name, value);
return this;
},
done: function() {
return current.size > 0 ? new PopulatedScope(current) : BindingScope.empty;
}
};
}
}
class PopulatedScope extends BindingScope {
constructor(private bindings: Map<string, any>) { super(); }
resolve(name: string): any {
return this.bindings.has(name) ? this.bindings.get(name) : BindingScope.missing;
}
}
function sameSymbol(a: StaticSymbol, b: StaticSymbol): boolean {
return a === b || (a.name == b.name && a.filePath == b.filePath);
}
function shouldIgnore(value: any): boolean {
return value && value.__symbolic == 'ignore';
}
function positionalError(message: string, fileName: string, line: number, column: number): Error {
const result = new Error(message);
(result as any).fileName = fileName;
(result as any).line = line;
(result as any).column = column;
return result;
}

View File

@ -0,0 +1,19 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* @module
* @description
* Entry point for all public APIs of the common package.
*/
import {Version} from '@angular/core';
/**
* @stable
*/
export const VERSION = new Version('0.0.0-PLACEHOLDER');

View File

@ -0,0 +1,260 @@
/**
* @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 {ModuleMetadata} from '@angular/tsc-wrapped';
import * as ts from 'typescript';
import {CompilerHost} from '../src/compiler_host';
import {Directory, Entry, MockAotContext, MockCompilerHost} from './mocks';
describe('CompilerHost', () => {
let context: MockAotContext;
let program: ts.Program;
let hostNestedGenDir: CompilerHost;
let hostSiblingGenDir: CompilerHost;
beforeEach(() => {
context = new MockAotContext('/tmp/src', clone(FILES));
const host = new MockCompilerHost(context);
program = ts.createProgram(
['main.ts'], {
module: ts.ModuleKind.CommonJS,
},
host);
// Force a typecheck
const errors = program.getSemanticDiagnostics();
if (errors && errors.length) {
throw new Error('Expected no errors');
}
hostNestedGenDir = new CompilerHost(
program, {
genDir: '/tmp/project/src/gen/',
basePath: '/tmp/project/src',
skipMetadataEmit: false,
strictMetadataEmit: false,
skipTemplateCodegen: false,
trace: false
},
context);
hostSiblingGenDir = new CompilerHost(
program, {
genDir: '/tmp/project/gen',
basePath: '/tmp/project/src/',
skipMetadataEmit: false,
strictMetadataEmit: false,
skipTemplateCodegen: false,
trace: false
},
context);
});
describe('nestedGenDir', () => {
it('should import node_module from factory', () => {
expect(hostNestedGenDir.fileNameToModuleName(
'/tmp/project/node_modules/@angular/core.d.ts',
'/tmp/project/src/gen/my.ngfactory.ts', ))
.toEqual('@angular/core');
});
it('should import factory from factory', () => {
expect(hostNestedGenDir.fileNameToModuleName(
'/tmp/project/src/my.other.ngfactory.ts', '/tmp/project/src/my.ngfactory.ts'))
.toEqual('./my.other.ngfactory');
expect(hostNestedGenDir.fileNameToModuleName(
'/tmp/project/src/my.other.css.ngstyle.ts', '/tmp/project/src/a/my.ngfactory.ts'))
.toEqual('../my.other.css.ngstyle');
expect(hostNestedGenDir.fileNameToModuleName(
'/tmp/project/src/a/my.other.shim.ngstyle.ts', '/tmp/project/src/my.ngfactory.ts'))
.toEqual('./a/my.other.shim.ngstyle');
expect(hostNestedGenDir.fileNameToModuleName(
'/tmp/project/src/my.other.sass.ngstyle.ts', '/tmp/project/src/a/my.ngfactory.ts'))
.toEqual('../my.other.sass.ngstyle');
});
it('should import application from factory', () => {
expect(hostNestedGenDir.fileNameToModuleName(
'/tmp/project/src/my.other.ts', '/tmp/project/src/my.ngfactory.ts'))
.toEqual('../my.other');
expect(hostNestedGenDir.fileNameToModuleName(
'/tmp/project/src/my.other.ts', '/tmp/project/src/a/my.ngfactory.ts'))
.toEqual('../../my.other');
expect(hostNestedGenDir.fileNameToModuleName(
'/tmp/project/src/a/my.other.ts', '/tmp/project/src/my.ngfactory.ts'))
.toEqual('../a/my.other');
expect(hostNestedGenDir.fileNameToModuleName(
'/tmp/project/src/a/my.other.css.ts', '/tmp/project/src/my.ngfactory.ts'))
.toEqual('../a/my.other.css');
expect(hostNestedGenDir.fileNameToModuleName(
'/tmp/project/src/a/my.other.css.shim.ts', '/tmp/project/src/my.ngfactory.ts'))
.toEqual('../a/my.other.css.shim');
});
});
describe('siblingGenDir', () => {
it('should import node_module from factory', () => {
expect(hostSiblingGenDir.fileNameToModuleName(
'/tmp/project/node_modules/@angular/core.d.ts',
'/tmp/project/src/gen/my.ngfactory.ts'))
.toEqual('@angular/core');
});
it('should import factory from factory', () => {
expect(hostSiblingGenDir.fileNameToModuleName(
'/tmp/project/src/my.other.ngfactory.ts', '/tmp/project/src/my.ngfactory.ts'))
.toEqual('./my.other.ngfactory');
expect(hostSiblingGenDir.fileNameToModuleName(
'/tmp/project/src/my.other.css.ts', '/tmp/project/src/a/my.ngfactory.ts'))
.toEqual('../my.other.css');
expect(hostSiblingGenDir.fileNameToModuleName(
'/tmp/project/src/a/my.other.css.shim.ts', '/tmp/project/src/my.ngfactory.ts'))
.toEqual('./a/my.other.css.shim');
});
it('should import application from factory', () => {
expect(hostSiblingGenDir.fileNameToModuleName(
'/tmp/project/src/my.other.ts', '/tmp/project/src/my.ngfactory.ts'))
.toEqual('./my.other');
expect(hostSiblingGenDir.fileNameToModuleName(
'/tmp/project/src/my.other.ts', '/tmp/project/src/a/my.ngfactory.ts'))
.toEqual('../my.other');
expect(hostSiblingGenDir.fileNameToModuleName(
'/tmp/project/src/a/my.other.ts', '/tmp/project/src/my.ngfactory.ts'))
.toEqual('./a/my.other');
});
});
it('should be able to produce an import from main @angular/core', () => {
expect(hostNestedGenDir.fileNameToModuleName(
'/tmp/project/node_modules/@angular/core.d.ts', '/tmp/project/src/main.ts'))
.toEqual('@angular/core');
});
it('should be able to produce an import from main to a sub-directory', () => {
expect(hostNestedGenDir.fileNameToModuleName('lib/utils.ts', 'main.ts')).toEqual('./lib/utils');
});
it('should be able to produce an import from to a peer file', () => {
expect(hostNestedGenDir.fileNameToModuleName('lib/collections.ts', 'lib/utils.ts'))
.toEqual('./collections');
});
it('should be able to produce an import from to a sibling directory', () => {
expect(hostNestedGenDir.fileNameToModuleName('lib/utils.ts', 'lib2/utils2.ts'))
.toEqual('../lib/utils');
});
it('should be able to read a metadata file', () => {
expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/core.d.ts')).toEqual([
{__symbolic: 'module', version: 3, metadata: {foo: {__symbolic: 'class'}}}
]);
});
it('should be able to read metadata from an otherwise unused .d.ts file ', () => {
expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/unused.d.ts')).toEqual([
dummyMetadata
]);
});
it('should be able to read empty metadata ', () => {
expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/empty.d.ts')).toEqual([]);
});
it('should return undefined for missing modules', () => {
expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/missing.d.ts')).toBeUndefined();
});
it('should add missing v3 metadata from v1 metadata and .d.ts files', () => {
expect(hostNestedGenDir.getMetadataFor('metadata_versions/v1.d.ts')).toEqual([
{__symbolic: 'module', version: 1, metadata: {foo: {__symbolic: 'class'}}}, {
__symbolic: 'module',
version: 3,
metadata: {
foo: {__symbolic: 'class'},
Bar: {__symbolic: 'class', members: {ngOnInit: [{__symbolic: 'method'}]}},
BarChild: {__symbolic: 'class', extends: {__symbolic: 'reference', name: 'Bar'}},
ReExport: {__symbolic: 'reference', module: './lib/utils2', name: 'ReExport'},
},
exports: [{from: './lib/utils2', export: ['Export']}],
}
]);
});
it('should upgrade a missing metadata file into v3', () => {
expect(hostNestedGenDir.getMetadataFor('metadata_versions/v1_empty.d.ts')).toEqual([
{__symbolic: 'module', version: 3, metadata: {}, exports: [{from: './lib/utils'}]}
]);
});
});
const dummyModule = 'export let foo: any[];';
const dummyMetadata: ModuleMetadata = {
__symbolic: 'module',
version: 3,
metadata:
{foo: {__symbolic: 'error', message: 'Variable not initialized', line: 0, character: 11}}
};
const FILES: Entry = {
'tmp': {
'src': {
'main.ts': `
import * as c from '@angular/core';
import * as r from '@angular/router';
import * as u from './lib/utils';
import * as cs from './lib/collections';
import * as u2 from './lib2/utils2';
`,
'lib': {
'utils.ts': dummyModule,
'collections.ts': dummyModule,
},
'lib2': {'utils2.ts': dummyModule},
'node_modules': {
'@angular': {
'core.d.ts': dummyModule,
'core.metadata.json':
`{"__symbolic":"module", "version": 3, "metadata": {"foo": {"__symbolic": "class"}}}`,
'router': {'index.d.ts': dummyModule, 'src': {'providers.d.ts': dummyModule}},
'unused.d.ts': dummyModule,
'empty.d.ts': 'export declare var a: string;',
'empty.metadata.json': '[]',
}
},
'metadata_versions': {
'v1.d.ts': `
import {ReExport} from './lib/utils2';
export {ReExport};
export {Export} from './lib/utils2';
export declare class Bar {
ngOnInit() {}
}
export declare class BarChild extends Bar {}
`,
'v1.metadata.json':
`{"__symbolic":"module", "version": 1, "metadata": {"foo": {"__symbolic": "class"}}}`,
'v1_empty.d.ts': `
export * from './lib/utils';
`
}
}
}
};
function clone(entry: Entry): Entry {
if (typeof entry === 'string') {
return entry;
} else {
const result: Directory = {};
for (const name in entry) {
result[name] = clone(entry[name]);
}
return result;
}
}

View File

@ -0,0 +1,181 @@
/**
* @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 {makeTempDir} from '@angular/tsc-wrapped/test/test_support';
import * as fs from 'fs';
import * as path from 'path';
import {main} from '../src/main';
import {ReflectionCapabilities, reflector} from './private_import_core';
describe('compiler-cli', () => {
let basePath: string;
let write: (fileName: string, content: string) => void;
beforeEach(() => {
basePath = makeTempDir();
write = (fileName: string, content: string) => {
fs.writeFileSync(path.join(basePath, fileName), content, {encoding: 'utf-8'});
};
write('tsconfig.json', `{
"compilerOptions": {
"experimentalDecorators": true,
"types": [],
"outDir": "built",
"declaration": true,
"module": "es2015",
"moduleResolution": "node"
},
"angularCompilerOptions": {
"annotateForClosureCompiler": true
},
"files": ["test.ts"]
}`);
const nodeModulesPath = path.resolve(basePath, 'node_modules');
fs.mkdirSync(nodeModulesPath);
fs.symlinkSync(path.resolve(__dirname, '..', '..'), path.resolve(nodeModulesPath, '@angular'));
});
// Restore reflector since AoT compiler will update it with a new static reflector
afterEach(() => { reflector.updateCapabilities(new ReflectionCapabilities()); });
it('should compile without errors', (done) => {
write('test.ts', 'export const A = 1;');
const mockConsole = {error: (s: string) => {}};
spyOn(mockConsole, 'error');
main({p: basePath}, mockConsole.error)
.then((exitCode) => {
expect(mockConsole.error).not.toHaveBeenCalled();
expect(exitCode).toEqual(0);
done();
})
.catch(e => done.fail(e));
});
it('should not print the stack trace if user input file does not exist', (done) => {
const mockConsole = {error: (s: string) => {}};
spyOn(mockConsole, 'error');
main({p: basePath}, mockConsole.error)
.then((exitCode) => {
expect(mockConsole.error)
.toHaveBeenCalledWith(
`Error File '` + path.join(basePath, 'test.ts') + `' not found.`);
expect(mockConsole.error).not.toHaveBeenCalledWith('Compilation failed');
expect(exitCode).toEqual(1);
done();
})
.catch(e => done.fail(e));
});
it('should not print the stack trace if user input file is malformed', (done) => {
write('test.ts', 'foo;');
const mockConsole = {error: (s: string) => {}};
spyOn(mockConsole, 'error');
main({p: basePath}, mockConsole.error)
.then((exitCode) => {
expect(mockConsole.error)
.toHaveBeenCalledWith(
'Error at ' + path.join(basePath, 'test.ts') + `:1:1: Cannot find name 'foo'.`);
expect(mockConsole.error).not.toHaveBeenCalledWith('Compilation failed');
expect(exitCode).toEqual(1);
done();
})
.catch(e => done.fail(e));
});
it('should not print the stack trace if cannot find the imported module', (done) => {
write('test.ts', `import {MyClass} from './not-exist-deps';`);
const mockConsole = {error: (s: string) => {}};
spyOn(mockConsole, 'error');
main({p: basePath}, mockConsole.error)
.then((exitCode) => {
expect(mockConsole.error)
.toHaveBeenCalledWith(
'Error at ' + path.join(basePath, 'test.ts') +
`:1:23: Cannot find module './not-exist-deps'.`);
expect(mockConsole.error).not.toHaveBeenCalledWith('Compilation failed');
expect(exitCode).toEqual(1);
done();
})
.catch(e => done.fail(e));
});
it('should not print the stack trace if cannot import', (done) => {
write('empty-deps.ts', 'export const A = 1;');
write('test.ts', `import {MyClass} from './empty-deps';`);
const mockConsole = {error: (s: string) => {}};
spyOn(mockConsole, 'error');
main({p: basePath}, mockConsole.error)
.then((exitCode) => {
expect(mockConsole.error)
.toHaveBeenCalledWith(
'Error at ' + path.join(basePath, 'test.ts') + `:1:9: Module '"` +
path.join(basePath, 'empty-deps') + `"' has no exported member 'MyClass'.`);
expect(mockConsole.error).not.toHaveBeenCalledWith('Compilation failed');
expect(exitCode).toEqual(1);
done();
})
.catch(e => done.fail(e));
});
it('should not print the stack trace if type mismatches', (done) => {
write('empty-deps.ts', 'export const A = "abc";');
write('test.ts', `
import {A} from './empty-deps';
A();
`);
const mockConsole = {error: (s: string) => {}};
spyOn(mockConsole, 'error');
main({p: basePath}, mockConsole.error)
.then((exitCode) => {
expect(mockConsole.error)
.toHaveBeenCalledWith(
'Error at ' + path.join(basePath, 'test.ts') +
':3:7: Cannot invoke an expression whose type lacks a call signature.');
expect(mockConsole.error).not.toHaveBeenCalledWith('Compilation failed');
expect(exitCode).toEqual(1);
done();
})
.catch(e => done.fail(e));
});
it('should print the stack trace on compiler internal errors', (done) => {
write('test.ts', 'export const A = 1;');
const mockConsole = {error: (s: string) => {}};
spyOn(mockConsole, 'error');
main({p: 'not-exist'}, mockConsole.error)
.then((exitCode) => {
expect(mockConsole.error).toHaveBeenCalled();
expect(mockConsole.error).toHaveBeenCalledWith('Compilation failed');
expect(exitCode).toEqual(1);
done();
})
.catch(e => done.fail(e));
});
});

View File

@ -6,14 +6,14 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ReflectorHostContext} from '@angular/compiler-cli/src/reflector_host';
import {CompilerHostContext} from '@angular/compiler-cli/src/compiler_host';
import * as ts from 'typescript';
export type Entry = string | Directory;
export interface Directory { [name: string]: Entry; }
export class MockContext implements ReflectorHostContext {
export class MockAotContext implements CompilerHostContext {
constructor(public currentDirectory: string, private files: Entry) {}
fileExists(fileName: string): boolean { return typeof this.getEntry(fileName) === 'string'; }
@ -28,6 +28,14 @@ export class MockContext implements ReflectorHostContext {
return undefined;
}
readResource(fileName: string): Promise<string> {
const result = this.readFile(fileName);
if (result == null) {
return Promise.reject(new Error(`Resource not found: ${fileName}`));
}
return Promise.resolve(result);
}
writeFile(fileName: string, data: string): void {
const parts = fileName.split('/');
const name = parts.pop();
@ -89,7 +97,7 @@ function normalize(parts: string[]): string[] {
}
export class MockCompilerHost implements ts.CompilerHost {
constructor(private context: MockContext) {}
constructor(private context: MockAotContext) {}
fileExists(fileName: string): boolean { return this.context.fileExists(fileName); }
@ -114,11 +122,9 @@ export class MockCompilerHost implements ts.CompilerHost {
return ts.getDefaultLibFileName(options);
}
writeFile: ts.WriteFileCallback = (fileName, text) => { this.context.writeFile(fileName, text); }
writeFile: ts.WriteFileCallback = (fileName, text) => { this.context.writeFile(fileName, text); };
getCurrentDirectory(): string {
return this.context.currentDirectory;
}
getCurrentDirectory(): string { return this.context.currentDirectory; }
getCanonicalFileName(fileName: string): string { return fileName; }

View File

@ -0,0 +1,13 @@
/**
* @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 {__core_private__ as r} from '@angular/core';
export type ReflectionCapabilities = typeof r._ReflectionCapabilities;
export const ReflectionCapabilities: typeof r.ReflectionCapabilities = r.ReflectionCapabilities;
export const reflector: typeof r.reflector = r.reflector;

View File

@ -1,329 +0,0 @@
/**
* @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 {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
import * as ts from 'typescript';
import {ReflectorHost} from '../src/reflector_host';
import {Directory, Entry, MockCompilerHost, MockContext} from './mocks';
describe('reflector_host', () => {
let context: MockContext;
let host: ts.CompilerHost;
let program: ts.Program;
let reflectorNestedGenDir: ReflectorHost;
let reflectorSiblingGenDir: ReflectorHost;
beforeEach(() => {
context = new MockContext('/tmp/src', clone(FILES));
host = new MockCompilerHost(context);
program = ts.createProgram(
['main.ts'], {
module: ts.ModuleKind.CommonJS,
},
host);
// Force a typecheck
const errors = program.getSemanticDiagnostics();
if (errors && errors.length) {
throw new Error('Expected no errors');
}
reflectorNestedGenDir = new ReflectorHost(
program, host, {
genDir: '/tmp/project/src/gen/',
basePath: '/tmp/project/src',
skipMetadataEmit: false,
strictMetadataEmit: false,
skipTemplateCodegen: false,
trace: false
},
context);
reflectorSiblingGenDir = new ReflectorHost(
program, host, {
genDir: '/tmp/project/gen',
basePath: '/tmp/project/src/',
skipMetadataEmit: false,
strictMetadataEmit: false,
skipTemplateCodegen: false,
trace: false
},
context);
});
describe('nestedGenDir', () => {
it('should import node_module from factory', () => {
expect(reflectorNestedGenDir.getImportPath(
'/tmp/project/src/gen/my.ngfactory.ts',
'/tmp/project/node_modules/@angular/core.d.ts'))
.toEqual('@angular/core');
});
it('should import factory from factory', () => {
expect(reflectorNestedGenDir.getImportPath(
'/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/my.other.ngfactory.ts'))
.toEqual('./my.other.ngfactory');
expect(reflectorNestedGenDir.getImportPath(
'/tmp/project/src/a/my.ngfactory.ts', '/tmp/project/src/my.other.css.ts'))
.toEqual('../my.other.css');
expect(reflectorNestedGenDir.getImportPath(
'/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/a/my.other.css.shim.ts'))
.toEqual('./a/my.other.css.shim');
});
it('should import application from factory', () => {
expect(reflectorNestedGenDir.getImportPath(
'/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/my.other.ts'))
.toEqual('../my.other');
expect(reflectorNestedGenDir.getImportPath(
'/tmp/project/src/a/my.ngfactory.ts', '/tmp/project/src/my.other.ts'))
.toEqual('../../my.other');
expect(reflectorNestedGenDir.getImportPath(
'/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/a/my.other.ts'))
.toEqual('../a/my.other');
});
});
describe('nestedGenDir', () => {
it('should import node_module from factory', () => {
expect(reflectorSiblingGenDir.getImportPath(
'/tmp/project/src/gen/my.ngfactory.ts',
'/tmp/project/node_modules/@angular/core.d.ts'))
.toEqual('@angular/core');
});
it('should import factory from factory', () => {
expect(reflectorSiblingGenDir.getImportPath(
'/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/my.other.ngfactory.ts'))
.toEqual('./my.other.ngfactory');
expect(reflectorSiblingGenDir.getImportPath(
'/tmp/project/src/a/my.ngfactory.ts', '/tmp/project/src/my.other.css.ts'))
.toEqual('../my.other.css');
expect(reflectorSiblingGenDir.getImportPath(
'/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/a/my.other.css.shim.ts'))
.toEqual('./a/my.other.css.shim');
});
it('should import application from factory', () => {
expect(reflectorSiblingGenDir.getImportPath(
'/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/my.other.ts'))
.toEqual('./my.other');
expect(reflectorSiblingGenDir.getImportPath(
'/tmp/project/src/a/my.ngfactory.ts', '/tmp/project/src/my.other.ts'))
.toEqual('../my.other');
expect(reflectorSiblingGenDir.getImportPath(
'/tmp/project/src/my.ngfactory.ts', '/tmp/project/src/a/my.other.ts'))
.toEqual('./a/my.other');
});
});
it('should provide the import locations for angular', () => {
const {coreDecorators, diDecorators, diMetadata, animationMetadata, provider} =
reflectorNestedGenDir.angularImportLocations();
expect(coreDecorators).toEqual('@angular/core/src/metadata');
expect(diDecorators).toEqual('@angular/core/src/di/metadata');
expect(diMetadata).toEqual('@angular/core/src/di/metadata');
expect(animationMetadata).toEqual('@angular/core/src/animation/metadata');
expect(provider).toEqual('@angular/core/src/di/provider');
});
it('should be able to produce an import from main @angular/core', () => {
expect(reflectorNestedGenDir.getImportPath(
'/tmp/project/src/main.ts', '/tmp/project/node_modules/@angular/core.d.ts'))
.toEqual('@angular/core');
});
it('should be able to produce an import from main to a sub-directory', () => {
expect(reflectorNestedGenDir.getImportPath('main.ts', 'lib/utils.ts')).toEqual('./lib/utils');
});
it('should be able to produce an import from to a peer file', () => {
expect(reflectorNestedGenDir.getImportPath('lib/utils.ts', 'lib/collections.ts'))
.toEqual('./collections');
});
it('should be able to produce an import from to a sibling directory', () => {
expect(reflectorNestedGenDir.getImportPath('lib2/utils2.ts', 'lib/utils.ts'))
.toEqual('../lib/utils');
});
it('should be able to produce a symbol for an exported symbol', () => {
expect(reflectorNestedGenDir.findDeclaration('@angular/router', 'foo', 'main.ts'))
.toBeDefined();
});
it('should be able to produce a symbol for values space only reference', () => {
expect(reflectorNestedGenDir.findDeclaration('@angular/router/src/providers', 'foo', 'main.ts'))
.toBeDefined();
});
it('should be produce the same symbol if asked twice', () => {
const foo1 = reflectorNestedGenDir.getStaticSymbol('main.ts', 'foo');
const foo2 = reflectorNestedGenDir.getStaticSymbol('main.ts', 'foo');
expect(foo1).toBe(foo2);
});
it('should be able to produce a symbol for a module with no file', () => {
expect(reflectorNestedGenDir.getStaticSymbol('angularjs', 'SomeAngularSymbol')).toBeDefined();
});
it('should be able to read a metadata file', () => {
expect(reflectorNestedGenDir.getMetadataFor('node_modules/@angular/core.d.ts'))
.toEqual({__symbolic: 'module', version: 1, metadata: {foo: {__symbolic: 'class'}}});
});
it('should be able to read metadata from an otherwise unused .d.ts file ', () => {
expect(reflectorNestedGenDir.getMetadataFor('node_modules/@angular/unused.d.ts'))
.toBeUndefined();
});
it('should be able to read empty metadata ', () => {
expect(reflectorNestedGenDir.getMetadataFor('node_modules/@angular/empty.d.ts'))
.toBeUndefined();
});
it('should return undefined for missing modules', () => {
expect(reflectorNestedGenDir.getMetadataFor('node_modules/@angular/missing.d.ts'))
.toBeUndefined();
});
it('should be able to trace a named export', () => {
const symbol = reflectorNestedGenDir.findDeclaration(
'./reexport/reexport.d.ts', 'One', '/tmp/src/main.ts');
expect(symbol.name).toEqual('One');
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts');
});
it('should be able to trace a renamed export', () => {
const symbol = reflectorNestedGenDir.findDeclaration(
'./reexport/reexport.d.ts', 'Four', '/tmp/src/main.ts');
expect(symbol.name).toEqual('Three');
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts');
});
it('should be able to trace an export * export', () => {
const symbol = reflectorNestedGenDir.findDeclaration(
'./reexport/reexport.d.ts', 'Five', '/tmp/src/main.ts');
expect(symbol.name).toEqual('Five');
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin5.d.ts');
});
it('should be able to trace a multi-level re-export', () => {
const symbol = reflectorNestedGenDir.findDeclaration(
'./reexport/reexport.d.ts', 'Thirty', '/tmp/src/main.ts');
expect(symbol.name).toEqual('Thirty');
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin30.d.ts');
});
});
const dummyModule = 'export let foo: any[];';
const FILES: Entry = {
'tmp': {
'src': {
'main.ts': `
import * as c from '@angular/core';
import * as r from '@angular/router';
import * as u from './lib/utils';
import * as cs from './lib/collections';
import * as u2 from './lib2/utils2';
`,
'lib': {
'utils.ts': dummyModule,
'collections.ts': dummyModule,
},
'lib2': {'utils2.ts': dummyModule},
'reexport': {
'reexport.d.ts': `
import * as c from '@angular/core';
`,
'reexport.metadata.json': JSON.stringify({
__symbolic: 'module',
version: 1,
metadata: {},
exports: [
{from: './src/origin1', export: ['One', 'Two', {name: 'Three', as: 'Four'}]},
{from: './src/origin5'}, {from: './src/reexport2'}
]
}),
'src': {
'origin1.d.ts': `
export class One {}
export class Two {}
export class Three {}
`,
'origin1.metadata.json': JSON.stringify({
__symbolic: 'module',
version: 1,
metadata: {
One: {__symbolic: 'class'},
Two: {__symbolic: 'class'},
Three: {__symbolic: 'class'},
},
}),
'origin5.d.ts': `
export class Five {}
`,
'origin5.metadata.json': JSON.stringify({
__symbolic: 'module',
version: 1,
metadata: {
Five: {__symbolic: 'class'},
},
}),
'origin30.d.ts': `
export class Thirty {}
`,
'origin30.metadata.json': JSON.stringify({
__symbolic: 'module',
version: 1,
metadata: {
Thirty: {__symbolic: 'class'},
},
}),
'originNone.d.ts': dummyModule,
'originNone.metadata.json': JSON.stringify({
__symbolic: 'module',
version: 1,
metadata: {},
}),
'reexport2.d.ts': dummyModule,
'reexport2.metadata.json': JSON.stringify({
__symbolic: 'module',
version: 1,
metadata: {},
exports: [{from: './originNone'}, {from: './origin30'}]
})
}
},
'node_modules': {
'@angular': {
'core.d.ts': dummyModule,
'core.metadata.json':
`{"__symbolic":"module", "version": 1, "metadata": {"foo": {"__symbolic": "class"}}}`,
'router': {'index.d.ts': dummyModule, 'src': {'providers.d.ts': dummyModule}},
'unused.d.ts': dummyModule,
'empty.d.ts': 'export declare var a: string;',
'empty.metadata.json': '[]',
}
}
}
}
};
function clone(entry: Entry): Entry {
if (typeof entry === 'string') {
return entry;
} else {
const result: Directory = {};
for (const name in entry) {
result[name] = clone(entry[name]);
}
return result;
}
}

View File

@ -0,0 +1,34 @@
{
"compilerOptions": {
"baseUrl": ".",
"declaration": true,
"experimentalDecorators": true,
"noImplicitAny": true,
"module": "es2015",
"moduleResolution": "node",
"outDir": "../../../dist/esm/compiler-cli",
"paths": {
"@angular/core": ["../../../dist/packages-dist/core"],
"@angular/common": ["../../../dist/packages-dist/common"],
"@angular/compiler": ["../../../dist/packages-dist/compiler"],
"@angular/platform-server": ["../../../dist/packages-dist/platform-server"],
"@angular/platform-browser": ["../../../dist/packages-dist/platform-browser"],
"@angular/tsc-wrapped": ["../../../dist/tools/@angular/tsc-wrapped"]
},
"rootDir": ".",
"sourceMap": true,
"inlineSources": true,
"target": "es5",
"lib": ["es6", "dom"],
"skipLibCheck": true
},
"exclude": ["integrationtest"],
"files": [
"index.ts",
"src/main.ts",
"src/extract_i18n.ts",
"../../../node_modules/@types/node/index.d.ts",
"../../../node_modules/@types/jasmine/index.d.ts",
"../../../node_modules/zone.js/dist/zone.js.d.ts"
]
}

View File

@ -1,11 +1,11 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"lib": ["es6", "dom"],
"noImplicitAny": true,
"sourceMap": true,
"baseUrl": ".",
"declaration": true,
"experimentalDecorators": true,
"noImplicitAny": true,
"module": "commonjs",
"outDir": "../../../dist/packages-dist/compiler-cli",
"paths": {
"@angular/core": ["../../../dist/packages-dist/core"],
"@angular/common": ["../../../dist/packages-dist/common"],
@ -14,11 +14,11 @@
"@angular/platform-browser": ["../../../dist/packages-dist/platform-browser"],
"@angular/tsc-wrapped": ["../../../dist/tools/@angular/tsc-wrapped"]
},
"experimentalDecorators": true,
"rootDir": ".",
"sourceRoot": ".",
"outDir": "../../../dist/packages-dist/compiler-cli",
"declaration": true,
"sourceMap": true,
"inlineSources": true,
"target": "es5",
"lib": ["es6", "dom"],
"skipLibCheck": true
},
"exclude": ["integrationtest"],

View File

@ -21,15 +21,24 @@
* </p>
* </div>
*/
export {VERSION} from './src/version';
export * from './src/template_parser/template_ast';
export {TEMPLATE_TRANSFORMS} from './src/template_parser/template_parser';
export {CompilerConfig, RenderTypes} from './src/config';
export * from './src/compile_metadata';
export * from './src/offline_compiler';
export {RuntimeCompiler} from './src/runtime_compiler';
export * from './src/aot/compiler_factory';
export * from './src/aot/compiler';
export * from './src/aot/compiler_host';
export * from './src/aot/static_reflector';
export * from './src/aot/static_reflection_capabilities';
export * from './src/aot/static_symbol';
export * from './src/aot/static_symbol_resolver';
export * from './src/aot/summary_resolver';
export * from './src/summary_resolver';
export {JitCompiler} from './src/jit/compiler';
export * from './src/jit/compiler_factory';
export * from './src/url_resolver';
export * from './src/resource_loader';
export * from './src/compiler';
export {DirectiveResolver} from './src/directive_resolver';
export {PipeResolver} from './src/pipe_resolver';
export {NgModuleResolver} from './src/ng_module_resolver';
@ -53,5 +62,5 @@ export * from './src/style_compiler';
export * from './src/template_parser/template_parser';
export {ViewCompiler} from './src/view_compiler/view_compiler';
export {AnimationParser} from './src/animation/animation_parser';
export {SyntaxError} from './src/util';
// This file only reexports content of the `src` folder. Keep it that way.

View File

@ -8,7 +8,7 @@
import {isPresent} from '../facade/lang';
import {Identifiers, resolveIdentifier} from '../identifiers';
import {Identifiers, createIdentifier} from '../identifiers';
import * as o from '../output/output_ast';
import {ANY_STATE, DEFAULT_STATE, EMPTY_STATE} from '../private_import_core';
@ -41,7 +41,9 @@ const _ANIMATION_TIME_VAR = o.variable('totalTime');
const _ANIMATION_START_STATE_STYLES_VAR = o.variable('startStateStyles');
const _ANIMATION_END_STATE_STYLES_VAR = o.variable('endStateStyles');
const _ANIMATION_COLLECTED_STYLES = o.variable('collectedStyles');
const EMPTY_MAP = o.literalMap([]);
const _PREVIOUS_ANIMATION_PLAYERS = o.variable('previousPlayers');
const _EMPTY_MAP = o.literalMap([]);
const _EMPTY_ARRAY = o.literalArr([]);
class _AnimationBuilder implements AnimationAstVisitor {
private _fnVarName: string;
@ -64,11 +66,11 @@ class _AnimationBuilder implements AnimationAstVisitor {
ast.styles.forEach(entry => {
const entries =
Object.keys(entry).map((key): [string, o.Expression] => [key, o.literal(entry[key])]);
stylesArr.push(o.literalMap(entries));
stylesArr.push(o.literalMap(entries, null, true));
});
return o.importExpr(resolveIdentifier(Identifiers.AnimationStyles)).instantiate([
o.importExpr(resolveIdentifier(Identifiers.collectAndResolveStyles)).callFn([
return o.importExpr(createIdentifier(Identifiers.AnimationStyles)).instantiate([
o.importExpr(createIdentifier(Identifiers.collectAndResolveStyles)).callFn([
_ANIMATION_COLLECTED_STYLES, o.literalArr(stylesArr)
])
]);
@ -76,7 +78,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
visitAnimationKeyframe(ast: AnimationKeyframeAst, context: _AnimationBuilderContext):
o.Expression {
return o.importExpr(resolveIdentifier(Identifiers.AnimationKeyframe)).instantiate([
return o.importExpr(createIdentifier(Identifiers.AnimationKeyframe)).instantiate([
o.literal(ast.offset), ast.styles.visit(this, context)
]);
}
@ -98,7 +100,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
const startingStylesExpr = ast.startingStyles.visit(this, context);
const keyframeExpressions = ast.keyframes.map(keyframe => keyframe.visit(this, context));
const keyframesExpr =
o.importExpr(resolveIdentifier(Identifiers.balanceAnimationKeyframes)).callFn([
o.importExpr(createIdentifier(Identifiers.balanceAnimationKeyframes)).callFn([
_ANIMATION_COLLECTED_STYLES, _ANIMATION_END_STATE_STYLES_VAR,
o.literalArr(keyframeExpressions)
]);
@ -110,24 +112,29 @@ class _AnimationBuilder implements AnimationAstVisitor {
_callAnimateMethod(
ast: AnimationStepAst, startingStylesExpr: any, keyframesExpr: any,
context: _AnimationBuilderContext) {
let previousStylesValue: o.Expression = _EMPTY_ARRAY;
if (context.isExpectingFirstAnimateStep) {
previousStylesValue = _PREVIOUS_ANIMATION_PLAYERS;
context.isExpectingFirstAnimateStep = false;
}
context.totalTransitionTime += ast.duration + ast.delay;
return _ANIMATION_FACTORY_RENDERER_VAR.callMethod('animate', [
_ANIMATION_FACTORY_ELEMENT_VAR, startingStylesExpr, keyframesExpr, o.literal(ast.duration),
o.literal(ast.delay), o.literal(ast.easing)
o.literal(ast.delay), o.literal(ast.easing), previousStylesValue
]);
}
visitAnimationSequence(ast: AnimationSequenceAst, context: _AnimationBuilderContext):
o.Expression {
const playerExprs = ast.steps.map(step => step.visit(this, context));
return o.importExpr(resolveIdentifier(Identifiers.AnimationSequencePlayer)).instantiate([
return o.importExpr(createIdentifier(Identifiers.AnimationSequencePlayer)).instantiate([
o.literalArr(playerExprs)
]);
}
visitAnimationGroup(ast: AnimationGroupAst, context: _AnimationBuilderContext): o.Expression {
const playerExprs = ast.steps.map(step => step.visit(this, context));
return o.importExpr(resolveIdentifier(Identifiers.AnimationGroupPlayer)).instantiate([
return o.importExpr(createIdentifier(Identifiers.AnimationGroupPlayer)).instantiate([
o.literalArr(playerExprs)
]);
}
@ -150,6 +157,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
context.totalTransitionTime = 0;
context.isExpectingFirstStyleStep = true;
context.isExpectingFirstAnimateStep = true;
const stateChangePreconditions: o.Expression[] = [];
@ -187,17 +195,17 @@ class _AnimationBuilder implements AnimationAstVisitor {
context.stateMap.registerState(DEFAULT_STATE, {});
const statements: o.Statement[] = [];
statements.push(_ANIMATION_FACTORY_VIEW_CONTEXT
.callMethod(
'cancelActiveAnimation',
statements.push(_PREVIOUS_ANIMATION_PLAYERS
.set(_ANIMATION_FACTORY_VIEW_CONTEXT.callMethod(
'getAnimationPlayers',
[
_ANIMATION_FACTORY_ELEMENT_VAR, o.literal(this.animationName),
_ANIMATION_FACTORY_ELEMENT_VAR,
_ANIMATION_NEXT_STATE_VAR.equals(o.literal(EMPTY_STATE))
])
.toStmt());
.conditional(o.NULL_EXPR, o.literal(this.animationName))
]))
.toDeclStmt());
statements.push(_ANIMATION_COLLECTED_STYLES.set(EMPTY_MAP).toDeclStmt());
statements.push(_ANIMATION_COLLECTED_STYLES.set(_EMPTY_MAP).toDeclStmt());
statements.push(_ANIMATION_PLAYER_VAR.set(o.NULL_EXPR).toDeclStmt());
statements.push(_ANIMATION_TIME_VAR.set(o.literal(0)).toDeclStmt());
@ -221,18 +229,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
_ANIMATION_END_STATE_STYLES_VAR.equals(o.NULL_EXPR),
[_ANIMATION_END_STATE_STYLES_VAR.set(_ANIMATION_DEFAULT_STATE_VAR).toStmt()]));
const RENDER_STYLES_FN = o.importExpr(resolveIdentifier(Identifiers.renderStyles));
// before we start any animation we want to clear out the starting
// styles from the element's style property (since they were placed
// there at the end of the last animation
statements.push(RENDER_STYLES_FN
.callFn([
_ANIMATION_FACTORY_ELEMENT_VAR, _ANIMATION_FACTORY_RENDERER_VAR,
o.importExpr(resolveIdentifier(Identifiers.clearStyles))
.callFn([_ANIMATION_START_STATE_STYLES_VAR])
])
.toStmt());
const RENDER_STYLES_FN = o.importExpr(createIdentifier(Identifiers.renderStyles));
ast.stateTransitions.forEach(transAst => statements.push(transAst.visit(this, context)));
@ -241,7 +238,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
statements.push(new o.IfStmt(
_ANIMATION_PLAYER_VAR.equals(o.NULL_EXPR),
[_ANIMATION_PLAYER_VAR
.set(o.importExpr(resolveIdentifier(Identifiers.NoOpAnimationPlayer)).instantiate([]))
.set(o.importExpr(createIdentifier(Identifiers.NoOpAnimationPlayer)).instantiate([]))
.toStmt()]));
// once complete we want to apply the styles on the element
@ -259,8 +256,9 @@ class _AnimationBuilder implements AnimationAstVisitor {
.callFn([
_ANIMATION_FACTORY_ELEMENT_VAR, _ANIMATION_FACTORY_RENDERER_VAR,
o.importExpr(
resolveIdentifier(Identifiers.prepareFinalAnimationStyles))
.callFn([
createIdentifier(Identifiers.prepareFinalAnimationStyles))
.callFn(
[
_ANIMATION_START_STATE_STYLES_VAR,
_ANIMATION_END_STATE_STYLES_VAR
])
@ -269,6 +267,22 @@ class _AnimationBuilder implements AnimationAstVisitor {
])])
.toStmt());
statements.push(o.importExpr(createIdentifier(Identifiers.AnimationSequencePlayer))
.instantiate([_PREVIOUS_ANIMATION_PLAYERS])
.callMethod('destroy', [])
.toStmt());
// before we start any animation we want to clear out the starting
// styles from the element's style property (since they were placed
// there at the end of the last animation
statements.push(RENDER_STYLES_FN
.callFn([
_ANIMATION_FACTORY_ELEMENT_VAR, _ANIMATION_FACTORY_RENDERER_VAR,
o.importExpr(createIdentifier(Identifiers.clearStyles))
.callFn([_ANIMATION_START_STATE_STYLES_VAR])
])
.toStmt());
statements.push(_ANIMATION_FACTORY_VIEW_CONTEXT
.callMethod(
'queueAnimation',
@ -279,7 +293,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
.toStmt());
statements.push(new o.ReturnStatement(
o.importExpr(resolveIdentifier(Identifiers.AnimationTransition)).instantiate([
o.importExpr(createIdentifier(Identifiers.AnimationTransition)).instantiate([
_ANIMATION_PLAYER_VAR, _ANIMATION_CURRENT_STATE_VAR, _ANIMATION_NEXT_STATE_VAR,
_ANIMATION_TIME_VAR
])));
@ -288,12 +302,12 @@ class _AnimationBuilder implements AnimationAstVisitor {
[
new o.FnParam(
_ANIMATION_FACTORY_VIEW_VAR.name,
o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
new o.FnParam(_ANIMATION_FACTORY_ELEMENT_VAR.name, o.DYNAMIC_TYPE),
new o.FnParam(_ANIMATION_CURRENT_STATE_VAR.name, o.DYNAMIC_TYPE),
new o.FnParam(_ANIMATION_NEXT_STATE_VAR.name, o.DYNAMIC_TYPE)
],
statements, o.importType(resolveIdentifier(Identifiers.AnimationTransition)));
statements, o.importType(createIdentifier(Identifiers.AnimationTransition)));
}
build(ast: AnimationAst): AnimationEntryCompileResult {
@ -304,16 +318,17 @@ class _AnimationBuilder implements AnimationAstVisitor {
const lookupMap: any[] = [];
Object.keys(context.stateMap.states).forEach(stateName => {
const value = context.stateMap.states[stateName];
let variableValue = EMPTY_MAP;
let variableValue = _EMPTY_MAP;
if (isPresent(value)) {
const styleMap: any[] = [];
Object.keys(value).forEach(key => { styleMap.push([key, o.literal(value[key])]); });
variableValue = o.literalMap(styleMap);
variableValue = o.literalMap(styleMap, null, true);
}
lookupMap.push([stateName, variableValue]);
});
const compiledStatesMapStmt = this._statesMapVar.set(o.literalMap(lookupMap)).toDeclStmt();
const compiledStatesMapStmt =
this._statesMapVar.set(o.literalMap(lookupMap, null, true)).toDeclStmt();
const statements: o.Statement[] = [compiledStatesMapStmt, fnStatement];
return new AnimationEntryCompileResult(this.animationName, statements, fnVariable);
@ -324,6 +339,7 @@ class _AnimationBuilderContext {
stateMap = new _AnimationBuilderStateMap();
endStateAnimateStep: AnimationStepAst = null;
isExpectingFirstStyleStep = false;
isExpectingFirstAnimateStep = false;
totalTransitionTime = 0;
}

View File

@ -6,11 +6,10 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Injectable} from '@angular/core';
import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata, CompileDirectiveMetadata} from '../compile_metadata';
import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata, CompileDirectiveMetadata, identifierName} from '../compile_metadata';
import {StringMapWrapper} from '../facade/collection';
import {isBlank, isPresent} from '../facade/lang';
import {CompilerInjectable} from '../injectable';
import {ParseError} from '../parse_util';
import {ANY_STATE, FILL_STYLE_FLAG} from '../private_import_core';
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
@ -35,13 +34,13 @@ export class AnimationEntryParseResult {
constructor(public ast: AnimationEntryAst, public errors: AnimationParseError[]) {}
}
@Injectable()
@CompilerInjectable()
export class AnimationParser {
constructor(private _schema: ElementSchemaRegistry) {}
parseComponent(component: CompileDirectiveMetadata): AnimationEntryAst[] {
const errors: string[] = [];
const componentName = component.type.name;
const componentName = identifierName(component.type);
const animationTriggerNames = new Set<string>();
const asts = component.template.animations.map(entry => {
const result = this.parseEntry(entry);
@ -174,6 +173,11 @@ function _normalizeStyleMetadata(
entry: CompileAnimationStyleMetadata, stateStyles: {[key: string]: AnimationStylesAst},
schema: ElementSchemaRegistry, errors: AnimationParseError[],
permitStateReferences: boolean): {[key: string]: string | number}[] {
const offset = entry.offset;
if (offset > 1 || offset < 0) {
errors.push(new AnimationParseError(`Offset values for animations must be between 0 and 1`));
}
const normalizedStyles: {[key: string]: string | number}[] = [];
entry.styles.forEach(styleEntry => {
if (typeof styleEntry === 'string') {

View File

@ -6,116 +6,69 @@
* found in the LICENSE file at https://angular.io/license
*/
import {SchemaMetadata} from '@angular/core';
import {AnimationCompiler} from '../animation/animation_compiler';
import {AnimationParser} from '../animation/animation_parser';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, CompileTypeSummary, createHostComponentMeta, identifierModuleUrl, identifierName} from '../compile_metadata';
import {DirectiveNormalizer} from '../directive_normalizer';
import {DirectiveWrapperCompileResult, DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
import {ListWrapper} from '../facade/collection';
import {Identifiers, createIdentifier, createIdentifierToken} from '../identifiers';
import {CompileMetadataResolver} from '../metadata_resolver';
import {NgModuleCompiler} from '../ng_module_compiler';
import {OutputEmitter} from '../output/abstract_emitter';
import * as o from '../output/output_ast';
import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
import {SummaryResolver} from '../summary_resolver';
import {TemplateParser} from '../template_parser/template_parser';
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency, ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler';
import {AnimationCompiler} from './animation/animation_compiler';
import {AnimationParser} from './animation/animation_parser';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, StaticSymbol, createHostComponentMeta} from './compile_metadata';
import {DirectiveNormalizer} from './directive_normalizer';
import {DirectiveWrapperCompileResult, DirectiveWrapperCompiler} from './directive_wrapper_compiler';
import {ListWrapper} from './facade/collection';
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from './identifiers';
import {CompileMetadataResolver} from './metadata_resolver';
import {NgModuleCompiler} from './ng_module_compiler';
import {OutputEmitter} from './output/abstract_emitter';
import * as o from './output/output_ast';
import {CompiledStylesheet, StyleCompiler} from './style_compiler';
import {TemplateParser} from './template_parser/template_parser';
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency, ViewCompileResult, ViewCompiler} from './view_compiler/view_compiler';
import {AotCompilerHost} from './compiler_host';
import {GeneratedFile} from './generated_file';
import {StaticSymbol} from './static_symbol';
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
import {serializeSummaries, summaryFileName} from './summary_serializer';
export class SourceModule {
constructor(public fileUrl: string, public moduleUrl: string, public source: string) {}
}
// Returns all the source files and a mapping from modules to directives
export function analyzeNgModules(
programStaticSymbols: StaticSymbol[], options: {transitiveModules: boolean},
metadataResolver: CompileMetadataResolver): Promise<{
ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
files: Array<{srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}>
}> {
return _loadNgModules(programStaticSymbols, options, metadataResolver).then(_analyzeNgModules);
}
function _analyzeNgModules(ngModuleMetas: CompileNgModuleMetadata[]) {
const moduleMetasByRef = new Map<any, CompileNgModuleMetadata>();
ngModuleMetas.forEach((ngModule) => moduleMetasByRef.set(ngModule.type.reference, ngModule));
const ngModuleByPipeOrDirective = new Map<StaticSymbol, CompileNgModuleMetadata>();
const ngModulesByFile = new Map<string, StaticSymbol[]>();
const ngDirectivesByFile = new Map<string, StaticSymbol[]>();
const filePaths = new Set<string>();
// Looping over all modules to construct:
// - a map from file to modules `ngModulesByFile`,
// - a map from file to directives `ngDirectivesByFile`,
// - a map from directive/pipe to module `ngModuleByPipeOrDirective`.
ngModuleMetas.forEach((ngModuleMeta) => {
const srcFileUrl = ngModuleMeta.type.reference.filePath;
filePaths.add(srcFileUrl);
ngModulesByFile.set(
srcFileUrl, (ngModulesByFile.get(srcFileUrl) || []).concat(ngModuleMeta.type.reference));
ngModuleMeta.declaredDirectives.forEach((dirIdentifier) => {
const fileUrl = dirIdentifier.reference.filePath;
filePaths.add(fileUrl);
ngDirectivesByFile.set(
fileUrl, (ngDirectivesByFile.get(fileUrl) || []).concat(dirIdentifier.reference));
ngModuleByPipeOrDirective.set(dirIdentifier.reference, ngModuleMeta);
});
ngModuleMeta.declaredPipes.forEach((pipeIdentifier) => {
const fileUrl = pipeIdentifier.reference.filePath;
filePaths.add(fileUrl);
ngModuleByPipeOrDirective.set(pipeIdentifier.reference, ngModuleMeta);
});
});
const files: {srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}[] = [];
filePaths.forEach((srcUrl) => {
const directives = ngDirectivesByFile.get(srcUrl) || [];
const ngModules = ngModulesByFile.get(srcUrl) || [];
files.push({srcUrl, directives, ngModules});
});
return {
// map directive/pipe to module
ngModuleByPipeOrDirective,
// list modules and directives for every source file
files,
};
}
export class OfflineCompiler {
export class AotCompiler {
private _animationCompiler = new AnimationCompiler();
constructor(
private _metadataResolver: CompileMetadataResolver, private _templateParser: TemplateParser,
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
private _dirWrapperCompiler: DirectiveWrapperCompiler,
private _host: AotCompilerHost, private _metadataResolver: CompileMetadataResolver,
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
private _viewCompiler: ViewCompiler, private _dirWrapperCompiler: DirectiveWrapperCompiler,
private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter,
private _localeId: string, private _translationFormat: string,
private _animationParser: AnimationParser) {}
private _summaryResolver: SummaryResolver<StaticSymbol>, private _localeId: string,
private _translationFormat: string, private _animationParser: AnimationParser,
private _symbolResolver: StaticSymbolResolver) {}
clearCache() { this._metadataResolver.clearCache(); }
compileModules(staticSymbols: StaticSymbol[], options: {transitiveModules: boolean}):
Promise<SourceModule[]> {
return analyzeNgModules(staticSymbols, options, this._metadataResolver)
.then(({ngModuleByPipeOrDirective, files}) => {
compileAll(rootFiles: string[]): Promise<GeneratedFile[]> {
const programSymbols = extractProgramSymbols(this._symbolResolver, rootFiles, this._host);
const {ngModuleByPipeOrDirective, files, ngModules} =
analyzeAndValidateNgModules(programSymbols, this._host, this._metadataResolver);
return Promise
.all(ngModules.map(
ngModule => this._metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
ngModule.type.reference, false)))
.then(() => {
const sourceModules = files.map(
file => this._compileSrcFile(
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.ngModules));
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.pipes,
file.ngModules, file.injectables));
return ListWrapper.flatten(sourceModules);
});
}
private _compileSrcFile(
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
directives: StaticSymbol[], ngModules: StaticSymbol[]): SourceModule[] {
directives: StaticSymbol[], pipes: StaticSymbol[], ngModules: StaticSymbol[],
injectables: StaticSymbol[]): GeneratedFile[] {
const fileSuffix = _splitTypescriptSuffix(srcFileUrl)[1];
const statements: o.Statement[] = [];
const exportedVars: string[] = [];
const outputSourceModules: SourceModule[] = [];
const generatedFiles: GeneratedFile[] = [];
generatedFiles.push(this._createSummary(srcFileUrl, directives, pipes, ngModules, injectables));
// compile all ng modules
exportedVars.push(
@ -134,7 +87,7 @@ export class OfflineCompiler {
const ngModule = ngModuleByPipeOrDirective.get(dirType);
if (!ngModule) {
throw new Error(
`Internal Error: cannot determine the module for component ${compMeta.type.name}!`);
`Internal Error: cannot determine the module for component ${identifierName(compMeta.type)}!`);
}
_assertComponent(compMeta);
@ -142,7 +95,7 @@ export class OfflineCompiler {
// compile styles
const stylesCompileResults = this._styleCompiler.compileComponent(compMeta);
stylesCompileResults.externalStylesheets.forEach((compiledStyleSheet) => {
outputSourceModules.push(this._codgenStyles(srcFileUrl, compiledStyleSheet, fileSuffix));
generatedFiles.push(this._codgenStyles(srcFileUrl, compiledStyleSheet, fileSuffix));
});
// compile components
@ -155,9 +108,25 @@ export class OfflineCompiler {
if (statements.length > 0) {
const srcModule = this._codegenSourceModule(
srcFileUrl, _ngfactoryModuleUrl(srcFileUrl), statements, exportedVars);
outputSourceModules.unshift(srcModule);
generatedFiles.unshift(srcModule);
}
return outputSourceModules;
return generatedFiles;
}
private _createSummary(
srcFileUrl: string, directives: StaticSymbol[], pipes: StaticSymbol[],
ngModules: StaticSymbol[], injectables: StaticSymbol[]): GeneratedFile {
const symbolSummaries = this._symbolResolver.getSymbolsOf(srcFileUrl)
.map(symbol => this._symbolResolver.resolveSymbol(symbol));
const typeSummaries = [
...ngModules.map(ref => this._metadataResolver.getNgModuleSummary(ref)),
...directives.map(ref => this._metadataResolver.getDirectiveSummary(ref)),
...pipes.map(ref => this._metadataResolver.getPipeSummary(ref)),
...injectables.map(ref => this._metadataResolver.getInjectableSummary(ref))
];
const json = serializeSummaries(
this._host, this._summaryResolver, this._symbolResolver, symbolSummaries, typeSummaries);
return new GeneratedFile(srcFileUrl, summaryFileName(srcFileUrl), json);
}
private _compileModule(ngModuleType: StaticSymbol, targetStatements: o.Statement[]): string {
@ -165,24 +134,24 @@ export class OfflineCompiler {
const providers: CompileProviderMetadata[] = [];
if (this._localeId) {
providers.push(new CompileProviderMetadata({
token: resolveIdentifierToken(Identifiers.LOCALE_ID),
providers.push({
token: createIdentifierToken(Identifiers.LOCALE_ID),
useValue: this._localeId,
}));
});
}
if (this._translationFormat) {
providers.push(new CompileProviderMetadata({
token: resolveIdentifierToken(Identifiers.TRANSLATIONS_FORMAT),
providers.push({
token: createIdentifierToken(Identifiers.TRANSLATIONS_FORMAT),
useValue: this._translationFormat
}));
});
}
const appCompileResult = this._ngModuleCompiler.compile(ngModule, providers);
appCompileResult.dependencies.forEach((dep) => {
dep.placeholder.name = _componentFactoryName(dep.comp);
dep.placeholder.moduleUrl = _ngfactoryModuleUrl(dep.comp.moduleUrl);
dep.placeholder.reference = this._symbolResolver.getStaticSymbol(
_ngfactoryModuleUrl(identifierModuleUrl(dep.comp)), _componentFactoryName(dep.comp));
});
targetStatements.push(...appCompileResult.statements);
@ -201,14 +170,17 @@ export class OfflineCompiler {
private _compileComponentFactory(
compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata, fileSuffix: string,
targetStatements: o.Statement[]): string {
const hostMeta = createHostComponentMeta(compMeta);
const hostMeta = createHostComponentMeta(
this._symbolResolver.getStaticSymbol(
identifierModuleUrl(compMeta.type), `${identifierName(compMeta.type)}_Host`),
compMeta);
const hostViewFactoryVar = this._compileComponent(
hostMeta, ngModule, [compMeta.type], null, fileSuffix, targetStatements);
const compFactoryVar = _componentFactoryName(compMeta.type);
targetStatements.push(
o.variable(compFactoryVar)
.set(o.importExpr(resolveIdentifier(Identifiers.ComponentFactory), [o.importType(
compMeta.type)])
.set(o.importExpr(
createIdentifier(Identifiers.ComponentFactory), [o.importType(compMeta.type)])
.instantiate(
[
o.literal(compMeta.selector),
@ -216,7 +188,7 @@ export class OfflineCompiler {
o.importExpr(compMeta.type),
],
o.importType(
resolveIdentifier(Identifiers.ComponentFactory),
createIdentifier(Identifiers.ComponentFactory),
[o.importType(compMeta.type)], [o.TypeModifier.Const])))
.toDeclStmt(null, [o.StmtModifier.Final]));
return compFactoryVar;
@ -234,23 +206,24 @@ export class OfflineCompiler {
const parsedTemplate = this._templateParser.parse(
compMeta, compMeta.template.template, directives, pipes, ngModule.schemas,
compMeta.type.name);
identifierName(compMeta.type));
const stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]);
const compiledAnimations =
this._animationCompiler.compile(compMeta.type.name, parsedAnimations);
this._animationCompiler.compile(identifierName(compMeta.type), parsedAnimations);
const viewResult = this._viewCompiler.compileComponent(
compMeta, parsedTemplate, stylesExpr, pipes, compiledAnimations);
if (componentStyles) {
targetStatements.push(..._resolveStyleStatements(componentStyles, fileSuffix));
targetStatements.push(
..._resolveStyleStatements(this._symbolResolver, componentStyles, fileSuffix));
}
compiledAnimations.forEach(entry => targetStatements.push(...entry.statements));
targetStatements.push(..._resolveViewStatements(viewResult));
targetStatements.push(..._resolveViewStatements(this._symbolResolver, viewResult));
return viewResult.viewClassVar;
}
private _codgenStyles(
fileUrl: string, stylesCompileResult: CompiledStylesheet, fileSuffix: string): SourceModule {
_resolveStyleStatements(stylesCompileResult, fileSuffix);
fileUrl: string, stylesCompileResult: CompiledStylesheet, fileSuffix: string): GeneratedFile {
_resolveStyleStatements(this._symbolResolver, stylesCompileResult, fileSuffix);
return this._codegenSourceModule(
fileUrl, _stylesModuleUrl(
stylesCompileResult.meta.moduleUrl, stylesCompileResult.isShimmed, fileSuffix),
@ -258,26 +231,29 @@ export class OfflineCompiler {
}
private _codegenSourceModule(
fileUrl: string, moduleUrl: string, statements: o.Statement[],
exportedVars: string[]): SourceModule {
return new SourceModule(
fileUrl, moduleUrl,
this._outputEmitter.emitStatements(moduleUrl, statements, exportedVars));
srcFileUrl: string, genFileUrl: string, statements: o.Statement[],
exportedVars: string[]): GeneratedFile {
return new GeneratedFile(
srcFileUrl, genFileUrl,
this._outputEmitter.emitStatements(genFileUrl, statements, exportedVars));
}
}
function _resolveViewStatements(compileResult: ViewCompileResult): o.Statement[] {
function _resolveViewStatements(
reflector: StaticSymbolResolver, compileResult: ViewCompileResult): o.Statement[] {
compileResult.dependencies.forEach((dep) => {
if (dep instanceof ViewClassDependency) {
const vfd = <ViewClassDependency>dep;
vfd.placeholder.moduleUrl = _ngfactoryModuleUrl(vfd.comp.moduleUrl);
vfd.placeholder.reference =
reflector.getStaticSymbol(_ngfactoryModuleUrl(identifierModuleUrl(vfd.comp)), dep.name);
} else if (dep instanceof ComponentFactoryDependency) {
const cfd = <ComponentFactoryDependency>dep;
cfd.placeholder.name = _componentFactoryName(cfd.comp);
cfd.placeholder.moduleUrl = _ngfactoryModuleUrl(cfd.comp.moduleUrl);
cfd.placeholder.reference = reflector.getStaticSymbol(
_ngfactoryModuleUrl(identifierModuleUrl(cfd.comp)), _componentFactoryName(cfd.comp));
} else if (dep instanceof DirectiveWrapperDependency) {
const dwd = <DirectiveWrapperDependency>dep;
dwd.placeholder.moduleUrl = _ngfactoryModuleUrl(dwd.dir.moduleUrl);
dwd.placeholder.reference =
reflector.getStaticSymbol(_ngfactoryModuleUrl(identifierModuleUrl(dwd.dir)), dwd.name);
}
});
return compileResult.statements;
@ -285,9 +261,11 @@ function _resolveViewStatements(compileResult: ViewCompileResult): o.Statement[]
function _resolveStyleStatements(
compileResult: CompiledStylesheet, fileSuffix: string): o.Statement[] {
reflector: StaticSymbolResolver, compileResult: CompiledStylesheet,
fileSuffix: string): o.Statement[] {
compileResult.dependencies.forEach((dep) => {
dep.valuePlaceholder.moduleUrl = _stylesModuleUrl(dep.moduleUrl, dep.isShimmed, fileSuffix);
dep.valuePlaceholder.reference = reflector.getStaticSymbol(
_stylesModuleUrl(dep.moduleUrl, dep.isShimmed, fileSuffix), dep.name);
});
return compileResult.statements;
}
@ -298,16 +276,17 @@ function _ngfactoryModuleUrl(dirUrl: string): string {
}
function _componentFactoryName(comp: CompileIdentifierMetadata): string {
return `${comp.name}NgFactory`;
return `${identifierName(comp)}NgFactory`;
}
function _stylesModuleUrl(stylesheetUrl: string, shim: boolean, suffix: string): string {
return shim ? `${stylesheetUrl}.shim${suffix}` : `${stylesheetUrl}${suffix}`;
return `${stylesheetUrl}${shim ? '.shim' : ''}.ngstyle${suffix}`;
}
function _assertComponent(meta: CompileDirectiveMetadata) {
if (!meta.isComponent) {
throw new Error(`Could not compile '${meta.type.name}' because it is not a component.`);
throw new Error(
`Could not compile '${identifierName(meta.type)}' because it is not a component.`);
}
}
@ -325,31 +304,158 @@ function _splitTypescriptSuffix(path: string): string[] {
return [path, ''];
}
export interface NgAnalyzedModules {
ngModules: CompileNgModuleMetadata[];
ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>;
files: Array<{
srcUrl: string,
directives: StaticSymbol[],
pipes: StaticSymbol[],
ngModules: StaticSymbol[],
injectables: StaticSymbol[]
}>;
symbolsMissingModule?: StaticSymbol[];
}
export interface NgAnalyzeModulesHost { isSourceFile(filePath: string): boolean; }
// Returns all the source files and a mapping from modules to directives
export function analyzeNgModules(
programStaticSymbols: StaticSymbol[], host: NgAnalyzeModulesHost,
metadataResolver: CompileMetadataResolver): NgAnalyzedModules {
const {ngModules, symbolsMissingModule} =
_createNgModules(programStaticSymbols, host, metadataResolver);
return _analyzeNgModules(programStaticSymbols, ngModules, symbolsMissingModule, metadataResolver);
}
export function analyzeAndValidateNgModules(
programStaticSymbols: StaticSymbol[], host: NgAnalyzeModulesHost,
metadataResolver: CompileMetadataResolver): NgAnalyzedModules {
const result = analyzeNgModules(programStaticSymbols, host, metadataResolver);
if (result.symbolsMissingModule && result.symbolsMissingModule.length) {
const messages = result.symbolsMissingModule.map(
s => `Cannot determine the module for class ${s.name} in ${s.filePath}!`);
throw new Error(messages.join('\n'));
}
return result;
}
function _analyzeNgModules(
programSymbols: StaticSymbol[], ngModuleMetas: CompileNgModuleMetadata[],
symbolsMissingModule: StaticSymbol[],
metadataResolver: CompileMetadataResolver): NgAnalyzedModules {
const moduleMetasByRef = new Map<any, CompileNgModuleMetadata>();
ngModuleMetas.forEach((ngModule) => moduleMetasByRef.set(ngModule.type.reference, ngModule));
const ngModuleByPipeOrDirective = new Map<StaticSymbol, CompileNgModuleMetadata>();
const ngModulesByFile = new Map<string, StaticSymbol[]>();
const ngDirectivesByFile = new Map<string, StaticSymbol[]>();
const ngPipesByFile = new Map<string, StaticSymbol[]>();
const ngInjectablesByFile = new Map<string, StaticSymbol[]>();
const filePaths = new Set<string>();
// Make sure we produce an analyzed file for each input file
programSymbols.forEach((symbol) => {
const filePath = symbol.filePath;
filePaths.add(filePath);
if (metadataResolver.isInjectable(symbol)) {
ngInjectablesByFile.set(filePath, (ngInjectablesByFile.get(filePath) || []).concat(symbol));
}
});
// Looping over all modules to construct:
// - a map from file to modules `ngModulesByFile`,
// - a map from file to directives `ngDirectivesByFile`,
// - a map from file to pipes `ngPipesByFile`,
// - a map from directive/pipe to module `ngModuleByPipeOrDirective`.
ngModuleMetas.forEach((ngModuleMeta) => {
const srcFileUrl = ngModuleMeta.type.reference.filePath;
filePaths.add(srcFileUrl);
ngModulesByFile.set(
srcFileUrl, (ngModulesByFile.get(srcFileUrl) || []).concat(ngModuleMeta.type.reference));
ngModuleMeta.declaredDirectives.forEach((dirIdentifier) => {
const fileUrl = dirIdentifier.reference.filePath;
filePaths.add(fileUrl);
ngDirectivesByFile.set(
fileUrl, (ngDirectivesByFile.get(fileUrl) || []).concat(dirIdentifier.reference));
ngModuleByPipeOrDirective.set(dirIdentifier.reference, ngModuleMeta);
});
ngModuleMeta.declaredPipes.forEach((pipeIdentifier) => {
const fileUrl = pipeIdentifier.reference.filePath;
filePaths.add(fileUrl);
ngPipesByFile.set(
fileUrl, (ngPipesByFile.get(fileUrl) || []).concat(pipeIdentifier.reference));
ngModuleByPipeOrDirective.set(pipeIdentifier.reference, ngModuleMeta);
});
});
const files: {
srcUrl: string,
directives: StaticSymbol[],
pipes: StaticSymbol[],
ngModules: StaticSymbol[],
injectables: StaticSymbol[]
}[] = [];
filePaths.forEach((srcUrl) => {
const directives = ngDirectivesByFile.get(srcUrl) || [];
const pipes = ngPipesByFile.get(srcUrl) || [];
const ngModules = ngModulesByFile.get(srcUrl) || [];
const injectables = ngInjectablesByFile.get(srcUrl) || [];
files.push({srcUrl, directives, pipes, ngModules, injectables});
});
return {
// map directive/pipe to module
ngModuleByPipeOrDirective,
// list modules and directives for every source file
files,
ngModules: ngModuleMetas, symbolsMissingModule
};
}
export function extractProgramSymbols(
staticSymbolResolver: StaticSymbolResolver, files: string[],
host: NgAnalyzeModulesHost): StaticSymbol[] {
const staticSymbols: StaticSymbol[] = [];
files.filter(fileName => host.isSourceFile(fileName)).forEach(sourceFile => {
staticSymbolResolver.getSymbolsOf(sourceFile).forEach((symbol) => {
const resolvedSymbol = staticSymbolResolver.resolveSymbol(symbol);
const symbolMeta = resolvedSymbol.metadata;
if (symbolMeta) {
if (symbolMeta.__symbolic != 'error') {
// Ignore symbols that are only included to record error information.
staticSymbols.push(resolvedSymbol.symbol);
}
}
});
});
return staticSymbols;
}
// Load the NgModules and check
// that all directives / pipes that are present in the program
// are also declared by a module.
function _loadNgModules(
programStaticSymbols: StaticSymbol[], options: {transitiveModules: boolean},
metadataResolver: CompileMetadataResolver): Promise<CompileNgModuleMetadata[]> {
function _createNgModules(
programStaticSymbols: StaticSymbol[], host: NgAnalyzeModulesHost,
metadataResolver: CompileMetadataResolver):
{ngModules: CompileNgModuleMetadata[], symbolsMissingModule: StaticSymbol[]} {
const ngModules = new Map<any, CompileNgModuleMetadata>();
const programPipesAndDirectives: StaticSymbol[] = [];
const ngModulePipesAndDirective = new Set<StaticSymbol>();
const loadingPromises: Promise<any>[] = [];
const addNgModule = (staticSymbol: any) => {
if (ngModules.has(staticSymbol)) {
if (ngModules.has(staticSymbol) || !host.isSourceFile(staticSymbol.filePath)) {
return false;
}
const {ngModule, loading} = metadataResolver.loadNgModuleMetadata(staticSymbol, false, false);
const ngModule = metadataResolver.getNgModuleMetadata(staticSymbol, false);
if (ngModule) {
ngModules.set(ngModule.type.reference, ngModule);
loadingPromises.push(loading);
ngModule.declaredDirectives.forEach((dir) => ngModulePipesAndDirective.add(dir.reference));
ngModule.declaredPipes.forEach((pipe) => ngModulePipesAndDirective.add(pipe.reference));
if (options.transitiveModules) {
// For every input modules add the list of transitively included modules
ngModule.transitiveModule.modules.forEach(modMeta => addNgModule(modMeta.type.reference));
}
// For every input module add the list of transitively included modules
ngModule.transitiveModule.modules.forEach(modMeta => addNgModule(modMeta.reference));
}
return !!ngModule;
};
@ -364,11 +470,5 @@ function _loadNgModules(
const symbolsMissingModule =
programPipesAndDirectives.filter(s => !ngModulePipesAndDirective.has(s));
if (symbolsMissingModule.length) {
const messages = symbolsMissingModule.map(
s => `Cannot determine the module for class ${s.name} in ${s.filePath}!`);
throw new Error(messages.join('\n'));
}
return Promise.all(loadingPromises).then(() => Array.from(ngModules.values()));
return {ngModules: Array.from(ngModules.values()), symbolsMissingModule};
}

View File

@ -0,0 +1,81 @@
/**
* @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 {ViewEncapsulation} from '@angular/core';
import {AnimationParser} from '../animation/animation_parser';
import {CompilerConfig} from '../config';
import {DirectiveNormalizer} from '../directive_normalizer';
import {DirectiveResolver} from '../directive_resolver';
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
import {Lexer} from '../expression_parser/lexer';
import {Parser} from '../expression_parser/parser';
import {I18NHtmlParser} from '../i18n/i18n_html_parser';
import {CompileMetadataResolver} from '../metadata_resolver';
import {HtmlParser} from '../ml_parser/html_parser';
import {NgModuleCompiler} from '../ng_module_compiler';
import {NgModuleResolver} from '../ng_module_resolver';
import {TypeScriptEmitter} from '../output/ts_emitter';
import {PipeResolver} from '../pipe_resolver';
import {Console} from '../private_import_core';
import {DomElementSchemaRegistry} from '../schema/dom_element_schema_registry';
import {StyleCompiler} from '../style_compiler';
import {TemplateParser} from '../template_parser/template_parser';
import {createOfflineCompileUrlResolver} from '../url_resolver';
import {ViewCompiler} from '../view_compiler/view_compiler';
import {AotCompiler} from './compiler';
import {AotCompilerHost} from './compiler_host';
import {AotCompilerOptions} from './compiler_options';
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
import {StaticReflector} from './static_reflector';
import {StaticSymbolCache} from './static_symbol';
import {StaticSymbolResolver} from './static_symbol_resolver';
import {AotSummaryResolver} from './summary_resolver';
/**
* Creates a new AotCompiler based on options and a host.
*/
export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCompilerOptions):
{compiler: AotCompiler, reflector: StaticReflector} {
let translations: string = options.translations || '';
const urlResolver = createOfflineCompileUrlResolver();
const symbolCache = new StaticSymbolCache();
const summaryResolver = new AotSummaryResolver(compilerHost, symbolCache);
const symbolResolver = new StaticSymbolResolver(compilerHost, symbolCache, summaryResolver);
const staticReflector = new StaticReflector(symbolResolver);
StaticAndDynamicReflectionCapabilities.install(staticReflector);
const htmlParser = new I18NHtmlParser(new HtmlParser(), translations, options.i18nFormat);
const config = new CompilerConfig({
genDebugInfo: options.debug === true,
defaultEncapsulation: ViewEncapsulation.Emulated,
logBindingUpdate: false,
useJit: false
});
const normalizer = new DirectiveNormalizer(
{get: (url: string) => compilerHost.loadResource(url)}, urlResolver, htmlParser, config);
const expressionParser = new Parser(new Lexer());
const elementSchemaRegistry = new DomElementSchemaRegistry();
const console = new Console();
const tmplParser =
new TemplateParser(expressionParser, elementSchemaRegistry, htmlParser, console, []);
const resolver = new CompileMetadataResolver(
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
staticReflector);
// TODO(vicb): do not pass options.i18nFormat here
const compiler = new AotCompiler(
compilerHost, resolver, tmplParser, new StyleCompiler(urlResolver),
new ViewCompiler(config, elementSchemaRegistry),
new DirectiveWrapperCompiler(config, expressionParser, elementSchemaRegistry, console),
new NgModuleCompiler(), new TypeScriptEmitter(compilerHost), summaryResolver, options.locale,
options.i18nFormat, new AnimationParser(elementSchemaRegistry), symbolResolver);
return {compiler, reflector: staticReflector};
}

View File

@ -0,0 +1,26 @@
/**
* @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 {ImportResolver} from '../output/path_util';
import {StaticSymbol} from './static_symbol';
import {StaticSymbolResolverHost} from './static_symbol_resolver';
import {AotSummaryResolverHost} from './summary_resolver';
import {AotSummarySerializerHost} from './summary_serializer';
/**
* The host of the AotCompiler disconnects the implementation from TypeScript / other language
* services and from underlying file systems.
*/
export interface AotCompilerHost extends StaticSymbolResolverHost, ImportResolver,
AotSummaryResolverHost, AotSummarySerializerHost {
/**
* Loads a resource (e.g. html / css)
*/
loadResource(path: string): Promise<string>;
}

View File

@ -0,0 +1,14 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
export interface AotCompilerOptions {
debug?: boolean;
locale?: string;
i18nFormat?: string;
translations?: string;
}

View File

@ -6,8 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
/**
* @module
* @description
* This module provides a set of common Pipes.
*/
export class GeneratedFile {
constructor(public srcFileUrl: string, public genFileUrl: string, public source: string) {}
}

View File

@ -6,8 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/
import {GetterFn, MethodFn, ReflectionCapabilities, SetterFn, reflector} from './private_import_core';
import {GetterFn, MethodFn, ReflectionCapabilities, SetterFn, reflector} from '../private_import_core';
import {StaticReflector} from './static_reflector';
import {StaticSymbol} from './static_symbol';
export class StaticAndDynamicReflectionCapabilities {
static install(staticDelegate: StaticReflector) {
@ -42,7 +43,7 @@ export class StaticAndDynamicReflectionCapabilities {
method(name: string): MethodFn { return this.dynamicDelegate.method(name); }
importUri(type: any): string { return this.staticDelegate.importUri(type); }
resolveIdentifier(name: string, moduleUrl: string, runtime: any) {
return this.staticDelegate.resolveIdentifier(name, moduleUrl, runtime);
return this.staticDelegate.resolveIdentifier(name, moduleUrl);
}
resolveEnum(enumIdentifier: any, name: string): any {
if (isStaticType(enumIdentifier)) {

View File

@ -0,0 +1,665 @@
/**
* @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 {Attribute, Component, ContentChild, ContentChildren, Directive, Host, HostBinding, HostListener, Inject, Injectable, Input, NgModule, Optional, Output, Pipe, Self, SkipSelf, ViewChild, ViewChildren, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
import {ReflectorReader} from '../private_import_core';
import {StaticSymbol} from './static_symbol';
import {StaticSymbolResolver} from './static_symbol_resolver';
const ANGULAR_IMPORT_LOCATIONS = {
coreDecorators: '@angular/core/src/metadata',
diDecorators: '@angular/core/src/di/metadata',
diMetadata: '@angular/core/src/di/metadata',
diOpaqueToken: '@angular/core/src/di/opaque_token',
animationMetadata: '@angular/core/src/animation/metadata',
provider: '@angular/core/src/di/provider'
};
const HIDDEN_KEY = /^\$.*\$$/;
/**
* A static reflector implements enough of the Reflector API that is necessary to compile
* templates statically.
*/
export class StaticReflector implements ReflectorReader {
private annotationCache = new Map<StaticSymbol, any[]>();
private propertyCache = new Map<StaticSymbol, {[key: string]: any[]}>();
private parameterCache = new Map<StaticSymbol, any[]>();
private methodCache = new Map<StaticSymbol, {[key: string]: boolean}>();
private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>();
private opaqueToken: StaticSymbol;
constructor(
private symbolResolver: StaticSymbolResolver,
knownMetadataClasses: {name: string, filePath: string, ctor: any}[] = [],
knownMetadataFunctions: {name: string, filePath: string, fn: any}[] = [],
private errorRecorder?: (error: any, fileName: string) => void) {
this.initializeConversionMap();
knownMetadataClasses.forEach(
(kc) => this._registerDecoratorOrConstructor(
this.getStaticSymbol(kc.filePath, kc.name), kc.ctor));
knownMetadataFunctions.forEach(
(kf) => this._registerFunction(this.getStaticSymbol(kf.filePath, kf.name), kf.fn));
}
importUri(typeOrFunc: StaticSymbol): string {
const staticSymbol = this.findSymbolDeclaration(typeOrFunc);
return staticSymbol ? staticSymbol.filePath : null;
}
resolveIdentifier(name: string, moduleUrl: string): StaticSymbol {
return this.findDeclaration(moduleUrl, name);
}
findDeclaration(moduleUrl: string, name: string, containingFile?: string): StaticSymbol {
return this.findSymbolDeclaration(
this.symbolResolver.getSymbolByModule(moduleUrl, name, containingFile));
}
findSymbolDeclaration(symbol: StaticSymbol): StaticSymbol {
const resolvedSymbol = this.symbolResolver.resolveSymbol(symbol);
if (resolvedSymbol && resolvedSymbol.metadata instanceof StaticSymbol) {
return this.findSymbolDeclaration(resolvedSymbol.metadata);
} else {
return symbol;
}
}
resolveEnum(enumIdentifier: any, name: string): any {
const staticSymbol: StaticSymbol = enumIdentifier;
return this.getStaticSymbol(staticSymbol.filePath, staticSymbol.name, [name]);
}
public annotations(type: StaticSymbol): any[] {
let annotations = this.annotationCache.get(type);
if (!annotations) {
annotations = [];
const classMetadata = this.getTypeMetadata(type);
if (classMetadata['extends']) {
const parentAnnotations = this.annotations(this.simplify(type, classMetadata['extends']));
annotations.push(...parentAnnotations);
}
if (classMetadata['decorators']) {
const ownAnnotations: any[] = this.simplify(type, classMetadata['decorators']);
annotations.push(...ownAnnotations);
}
this.annotationCache.set(type, annotations.filter(ann => !!ann));
}
return annotations;
}
public propMetadata(type: StaticSymbol): {[key: string]: any[]} {
let propMetadata = this.propertyCache.get(type);
if (!propMetadata) {
const classMetadata = this.getTypeMetadata(type);
propMetadata = {};
if (classMetadata['extends']) {
const parentPropMetadata = this.propMetadata(this.simplify(type, classMetadata['extends']));
Object.keys(parentPropMetadata).forEach((parentProp) => {
propMetadata[parentProp] = parentPropMetadata[parentProp];
});
}
const members = classMetadata['members'] || {};
Object.keys(members).forEach((propName) => {
const propData = members[propName];
const prop = (<any[]>propData)
.find(a => a['__symbolic'] == 'property' || a['__symbolic'] == 'method');
const decorators: any[] = [];
if (propMetadata[propName]) {
decorators.push(...propMetadata[propName]);
}
propMetadata[propName] = decorators;
if (prop && prop['decorators']) {
decorators.push(...this.simplify(type, prop['decorators']));
}
});
this.propertyCache.set(type, propMetadata);
}
return propMetadata;
}
public parameters(type: StaticSymbol): any[] {
if (!(type instanceof StaticSymbol)) {
this.reportError(
new Error(`parameters received ${JSON.stringify(type)} which is not a StaticSymbol`),
type);
return [];
}
try {
let parameters = this.parameterCache.get(type);
if (!parameters) {
const classMetadata = this.getTypeMetadata(type);
const members = classMetadata ? classMetadata['members'] : null;
const ctorData = members ? members['__ctor__'] : null;
if (ctorData) {
const ctor = (<any[]>ctorData).find(a => a['__symbolic'] == 'constructor');
const parameterTypes = <any[]>this.simplify(type, ctor['parameters'] || []);
const parameterDecorators = <any[]>this.simplify(type, ctor['parameterDecorators'] || []);
parameters = [];
parameterTypes.forEach((paramType, index) => {
const nestedResult: any[] = [];
if (paramType) {
nestedResult.push(paramType);
}
const decorators = parameterDecorators ? parameterDecorators[index] : null;
if (decorators) {
nestedResult.push(...decorators);
}
parameters.push(nestedResult);
});
} else if (classMetadata['extends']) {
parameters = this.parameters(this.simplify(type, classMetadata['extends']));
}
if (!parameters) {
parameters = [];
}
this.parameterCache.set(type, parameters);
}
return parameters;
} catch (e) {
console.error(`Failed on type ${JSON.stringify(type)} with error ${e}`);
throw e;
}
}
private _methodNames(type: any): {[key: string]: boolean} {
let methodNames = this.methodCache.get(type);
if (!methodNames) {
const classMetadata = this.getTypeMetadata(type);
methodNames = {};
if (classMetadata['extends']) {
const parentMethodNames = this._methodNames(this.simplify(type, classMetadata['extends']));
Object.keys(parentMethodNames).forEach((parentProp) => {
methodNames[parentProp] = parentMethodNames[parentProp];
});
}
const members = classMetadata['members'] || {};
Object.keys(members).forEach((propName) => {
const propData = members[propName];
const isMethod = (<any[]>propData).some(a => a['__symbolic'] == 'method');
methodNames[propName] = methodNames[propName] || isMethod;
});
this.methodCache.set(type, methodNames);
}
return methodNames;
}
hasLifecycleHook(type: any, lcProperty: string): boolean {
if (!(type instanceof StaticSymbol)) {
this.reportError(
new Error(
`hasLifecycleHook received ${JSON.stringify(type)} which is not a StaticSymbol`),
type);
}
try {
return !!this._methodNames(type)[lcProperty];
} catch (e) {
console.error(`Failed on type ${JSON.stringify(type)} with error ${e}`);
throw e;
}
}
private _registerDecoratorOrConstructor(type: StaticSymbol, ctor: any): void {
this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => new ctor(...args));
}
private _registerFunction(type: StaticSymbol, fn: any): void {
this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => fn.apply(undefined, args));
}
private initializeConversionMap(): void {
const {coreDecorators, diDecorators, diMetadata, diOpaqueToken, animationMetadata, provider} =
ANGULAR_IMPORT_LOCATIONS;
this.opaqueToken = this.findDeclaration(diOpaqueToken, 'OpaqueToken');
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Host'), Host);
this._registerDecoratorOrConstructor(
this.findDeclaration(diDecorators, 'Injectable'), Injectable);
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Self'), Self);
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'SkipSelf'), SkipSelf);
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Inject'), Inject);
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Optional'), Optional);
this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'Attribute'), Attribute);
this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'ContentChild'), ContentChild);
this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'ContentChildren'), ContentChildren);
this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'ViewChild'), ViewChild);
this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'ViewChildren'), ViewChildren);
this._registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Input'), Input);
this._registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Output'), Output);
this._registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Pipe'), Pipe);
this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'HostBinding'), HostBinding);
this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'HostListener'), HostListener);
this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'Directive'), Directive);
this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'Component'), Component);
this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'NgModule'), NgModule);
// Note: Some metadata classes can be used directly with Provider.deps.
this._registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Host'), Host);
this._registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Self'), Self);
this._registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'SkipSelf'), SkipSelf);
this._registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Optional'), Optional);
this._registerFunction(this.findDeclaration(animationMetadata, 'trigger'), trigger);
this._registerFunction(this.findDeclaration(animationMetadata, 'state'), state);
this._registerFunction(this.findDeclaration(animationMetadata, 'transition'), transition);
this._registerFunction(this.findDeclaration(animationMetadata, 'style'), style);
this._registerFunction(this.findDeclaration(animationMetadata, 'animate'), animate);
this._registerFunction(this.findDeclaration(animationMetadata, 'keyframes'), keyframes);
this._registerFunction(this.findDeclaration(animationMetadata, 'sequence'), sequence);
this._registerFunction(this.findDeclaration(animationMetadata, 'group'), group);
}
/**
* getStaticSymbol produces a Type whose metadata is known but whose implementation is not loaded.
* All types passed to the StaticResolver should be pseudo-types returned by this method.
*
* @param declarationFile the absolute path of the file where the symbol is declared
* @param name the name of the type.
*/
getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol {
return this.symbolResolver.getStaticSymbol(declarationFile, name, members);
}
private reportError(error: Error, context: StaticSymbol, path?: string) {
if (this.errorRecorder) {
this.errorRecorder(error, (context && context.filePath) || path);
} else {
throw error;
}
}
/** @internal */
public simplify(context: StaticSymbol, value: any): any {
const self = this;
let scope = BindingScope.empty;
const calling = new Map<StaticSymbol, boolean>();
function simplifyInContext(context: StaticSymbol, value: any, depth: number): any {
function resolveReferenceValue(staticSymbol: StaticSymbol): any {
const resolvedSymbol = self.symbolResolver.resolveSymbol(staticSymbol);
return resolvedSymbol ? resolvedSymbol.metadata : null;
}
function simplifyCall(functionSymbol: StaticSymbol, targetFunction: any, args: any[]) {
if (targetFunction && targetFunction['__symbolic'] == 'function') {
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')) {
const parameters: string[] = targetFunction['parameters'];
const defaults: any[] = targetFunction.defaults;
args = args.map(arg => simplifyInContext(context, arg, depth + 1));
if (defaults && defaults.length > args.length) {
args.push(...defaults.slice(args.length).map((value: any) => simplify(value)));
}
const functionScope = BindingScope.build();
for (let i = 0; i < parameters.length; i++) {
functionScope.define(parameters[i], args[i]);
}
const oldScope = scope;
let result: any;
try {
scope = functionScope.done();
result = simplifyInContext(functionSymbol, value, depth + 1);
} finally {
scope = oldScope;
}
return result;
}
} finally {
calling.delete(functionSymbol);
}
}
if (depth === 0) {
// If depth is 0 we are evaluating the top level expression that is describing element
// decorator. In this case, it is a decorator we don't understand, such as a custom
// non-angular decorator, and we should just ignore it.
return {__symbolic: 'ignore'};
}
return simplify(
{__symbolic: 'error', message: 'Function call not supported', context: functionSymbol});
}
function simplify(expression: any): any {
if (isPrimitive(expression)) {
return expression;
}
if (expression instanceof Array) {
const result: any[] = [];
for (const item of (<any>expression)) {
// Check for a spread expression
if (item && item.__symbolic === 'spread') {
const spreadArray = simplify(item.expression);
if (Array.isArray(spreadArray)) {
for (const spreadItem of spreadArray) {
result.push(spreadItem);
}
continue;
}
}
const value = simplify(item);
if (shouldIgnore(value)) {
continue;
}
result.push(value);
}
return result;
}
if (expression instanceof StaticSymbol) {
// Stop simplification at builtin symbols
if (expression === self.opaqueToken || self.conversionMap.has(expression)) {
return expression;
} else {
const staticSymbol = expression;
const declarationValue = resolveReferenceValue(staticSymbol);
if (declarationValue) {
return simplifyInContext(staticSymbol, declarationValue, depth + 1);
} else {
return staticSymbol;
}
}
}
if (expression) {
if (expression['__symbolic']) {
let staticSymbol: StaticSymbol;
switch (expression['__symbolic']) {
case 'binop':
let left = simplify(expression['left']);
if (shouldIgnore(left)) return left;
let right = simplify(expression['right']);
if (shouldIgnore(right)) return right;
switch (expression['operator']) {
case '&&':
return left && right;
case '||':
return left || right;
case '|':
return left | right;
case '^':
return left ^ right;
case '&':
return left & right;
case '==':
return left == right;
case '!=':
return left != right;
case '===':
return left === right;
case '!==':
return left !== right;
case '<':
return left < right;
case '>':
return left > right;
case '<=':
return left <= right;
case '>=':
return left >= right;
case '<<':
return left << right;
case '>>':
return left >> right;
case '+':
return left + right;
case '-':
return left - right;
case '*':
return left * right;
case '/':
return left / right;
case '%':
return left % right;
}
return null;
case 'if':
let condition = simplify(expression['condition']);
return condition ? simplify(expression['thenExpression']) :
simplify(expression['elseExpression']);
case 'pre':
let operand = simplify(expression['operand']);
if (shouldIgnore(operand)) return operand;
switch (expression['operator']) {
case '+':
return operand;
case '-':
return -operand;
case '!':
return !operand;
case '~':
return ~operand;
}
return null;
case 'index':
let indexTarget = simplify(expression['expression']);
let index = simplify(expression['index']);
if (indexTarget && isPrimitive(index)) return indexTarget[index];
return null;
case 'select':
const member = expression['member'];
let selectContext = context;
let selectTarget = simplify(expression['expression']);
if (selectTarget instanceof StaticSymbol) {
const members = selectTarget.members.concat(member);
selectContext =
self.getStaticSymbol(selectTarget.filePath, selectTarget.name, members);
const declarationValue = resolveReferenceValue(selectContext);
if (declarationValue) {
return simplifyInContext(selectContext, declarationValue, depth + 1);
} else {
return selectContext;
}
}
if (selectTarget && isPrimitive(member))
return simplifyInContext(selectContext, selectTarget[member], depth + 1);
return null;
case 'reference':
// Note: This only has to deal with variable references,
// as symbol references have been converted into StaticSymbols already
// in the StaticSymbolResolver!
const name: string = expression['name'];
const localValue = scope.resolve(name);
if (localValue != BindingScope.missing) {
return localValue;
}
break;
case 'class':
return context;
case 'function':
return context;
case 'new':
case 'call':
// Determine if the function is a built-in conversion
staticSymbol = simplifyInContext(context, expression['expression'], depth + 1);
if (staticSymbol instanceof StaticSymbol) {
if (staticSymbol === self.opaqueToken) {
// if somebody calls new OpaqueToken, don't create an OpaqueToken,
// but rather return the symbol to which the OpaqueToken is assigned to.
return context;
}
const argExpressions: any[] = expression['arguments'] || [];
let converter = self.conversionMap.get(staticSymbol);
if (converter) {
const args =
argExpressions.map(arg => simplifyInContext(context, arg, depth + 1));
return converter(context, args);
} else {
// Determine if the function is one we can simplify.
const targetFunction = resolveReferenceValue(staticSymbol);
return simplifyCall(staticSymbol, targetFunction, argExpressions);
}
}
break;
case 'error':
let message = produceErrorMessage(expression);
if (expression['line']) {
message =
`${message} (position ${expression['line']+1}:${expression['character']+1} in the original .ts file)`;
throw positionalError(
message, context.filePath, expression['line'], expression['character']);
}
throw new Error(message);
}
return null;
}
return mapStringMap(expression, (value, name) => simplify(value));
}
return null;
}
try {
return simplify(value);
} catch (e) {
const members = context.members.length ? `.${context.members.join('.')}` : '';
const message =
`${e.message}, resolving symbol ${context.name}${members} in ${context.filePath}`;
if (e.fileName) {
throw positionalError(message, e.fileName, e.line, e.column);
}
throw new Error(message);
}
}
const recordedSimplifyInContext = (context: StaticSymbol, value: any, depth: number) => {
try {
return simplifyInContext(context, value, depth);
} catch (e) {
this.reportError(e, context);
}
};
const result = this.errorRecorder ? recordedSimplifyInContext(context, value, 0) :
simplifyInContext(context, value, 0);
if (shouldIgnore(result)) {
return undefined;
}
return result;
}
private getTypeMetadata(type: StaticSymbol): {[key: string]: any} {
const resolvedSymbol = this.symbolResolver.resolveSymbol(type);
return resolvedSymbol && resolvedSymbol.metadata ? resolvedSymbol.metadata :
{__symbolic: 'class'};
}
}
function expandedMessage(error: any): string {
switch (error.message) {
case 'Reference to non-exported class':
if (error.context && error.context.className) {
return `Reference to a non-exported class ${error.context.className}. Consider exporting the class`;
}
break;
case 'Variable not initialized':
return 'Only initialized variables and constants can be referenced because the value of this variable is needed by the template compiler';
case 'Destructuring not supported':
return 'Referencing an exported destructured variable or constant is not supported by the template compiler. Consider simplifying this to avoid destructuring';
case 'Could not resolve type':
if (error.context && error.context.typeName) {
return `Could not resolve type ${error.context.typeName}`;
}
break;
case 'Function call not supported':
let prefix =
error.context && error.context.name ? `Calling function '${error.context.name}', f` : 'F';
return prefix +
'unction calls are not supported. Consider replacing the function or lambda with a reference to an exported function';
case 'Reference to a local symbol':
if (error.context && error.context.name) {
return `Reference to a local (non-exported) symbol '${error.context.name}'. Consider exporting the symbol`;
}
break;
}
return error.message;
}
function produceErrorMessage(error: any): string {
return `Error encountered resolving symbol values statically. ${expandedMessage(error)}`;
}
function mapStringMap(input: {[key: string]: any}, transform: (value: any, key: string) => any):
{[key: string]: any} {
if (!input) return {};
const result: {[key: string]: any} = {};
Object.keys(input).forEach((key) => {
const value = transform(input[key], key);
if (!shouldIgnore(value)) {
if (HIDDEN_KEY.test(key)) {
Object.defineProperty(result, key, {enumerable: false, configurable: true, value: value});
} else {
result[key] = value;
}
}
});
return result;
}
function isPrimitive(o: any): boolean {
return o === null || (typeof o !== 'function' && typeof o !== 'object');
}
interface BindingScopeBuilder {
define(name: string, value: any): BindingScopeBuilder;
done(): BindingScope;
}
abstract class BindingScope {
abstract resolve(name: string): any;
public static missing = {};
public static empty: BindingScope = {resolve: name => BindingScope.missing};
public static build(): BindingScopeBuilder {
const current = new Map<string, any>();
return {
define: function(name, value) {
current.set(name, value);
return this;
},
done: function() {
return current.size > 0 ? new PopulatedScope(current) : BindingScope.empty;
}
};
}
}
class PopulatedScope extends BindingScope {
constructor(private bindings: Map<string, any>) { super(); }
resolve(name: string): any {
return this.bindings.has(name) ? this.bindings.get(name) : BindingScope.missing;
}
}
function shouldIgnore(value: any): boolean {
return value && value.__symbolic == 'ignore';
}
function positionalError(message: string, fileName: string, line: number, column: number): Error {
const result = new Error(message);
(result as any).fileName = fileName;
(result as any).line = line;
(result as any).column = column;
return result;
}

View File

@ -0,0 +1,36 @@
/**
* @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
*/
/**
* A token representing the a reference to a static type.
*
* This token is unique for a filePath and name and can be used as a hash table key.
*/
export class StaticSymbol {
constructor(public filePath: string, public name: string, public members?: string[]) {}
}
/**
* A cache of static symbol used by the StaticReflector to return the same symbol for the
* same symbol values.
*/
export class StaticSymbolCache {
private cache = new Map<string, StaticSymbol>();
get(declarationFile: string, name: string, members?: string[]): StaticSymbol {
members = members || [];
const memberSuffix = members.length ? `.${ members.join('.')}` : '';
const key = `"${declarationFile}".${name}${memberSuffix}`;
let result = this.cache.get(key);
if (!result) {
result = new StaticSymbol(declarationFile, name, members);
this.cache.set(key, result);
}
return result;
}
}

View File

@ -0,0 +1,289 @@
/**
* @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 {SummaryResolver} from '../summary_resolver';
import {ValueTransformer, visitValue} from '../util';
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
export class ResolvedStaticSymbol {
constructor(public symbol: StaticSymbol, public metadata: any) {}
}
/**
* The host of the SymbolResolverHost disconnects the implementation from TypeScript / other
* language
* services and from underlying file systems.
*/
export interface StaticSymbolResolverHost {
/**
* Return a ModuleMetadata for the given module.
* Angular 2 CLI will produce this metadata for a module whenever a .d.ts files is
* produced and the module has exported variables or classes with decorators. Module metadata can
* also be produced directly from TypeScript sources by using MetadataCollector in tools/metadata.
*
* @param modulePath is a string identifier for a module as an absolute path.
* @returns the metadata for the given module.
*/
getMetadataFor(modulePath: string): {[key: string]: any}[];
/**
* Converts a module name that is used in an `import` to a file path.
* I.e.
* `path/to/containingFile.ts` containing `import {...} from 'module-name'`.
*/
moduleNameToFileName(moduleName: string, containingFile: string): string /*|null*/;
}
const SUPPORTED_SCHEMA_VERSION = 3;
/**
* This class is responsible for loading metadata per symbol,
* and normalizing references between symbols.
*/
export class StaticSymbolResolver {
private metadataCache = new Map<string, {[key: string]: any}>();
private resolvedSymbols = new Map<StaticSymbol, ResolvedStaticSymbol>();
private resolvedFilePaths = new Set<string>();
constructor(
private host: StaticSymbolResolverHost, private staticSymbolCache: StaticSymbolCache,
private summaryResolver: SummaryResolver<StaticSymbol>,
private errorRecorder?: (error: any, fileName: string) => void) {}
resolveSymbol(staticSymbol: StaticSymbol): ResolvedStaticSymbol {
if (staticSymbol.members.length > 0) {
return this._resolveSymbolMembers(staticSymbol);
}
let result = this._resolveSymbolFromSummary(staticSymbol);
if (!result) {
// Note: Some users use libraries that were not compiled with ngc, i.e. they don't
// have summaries, only .d.ts files. So we always need to check both, the summary
// and metadata.
this._createSymbolsOf(staticSymbol.filePath);
result = this.resolvedSymbols.get(staticSymbol);
}
return result;
}
private _resolveSymbolMembers(staticSymbol: StaticSymbol): ResolvedStaticSymbol {
const members = staticSymbol.members;
const baseResolvedSymbol =
this.resolveSymbol(this.getStaticSymbol(staticSymbol.filePath, staticSymbol.name));
if (!baseResolvedSymbol) {
return null;
}
const baseMetadata = baseResolvedSymbol.metadata;
if (baseMetadata instanceof StaticSymbol) {
return new ResolvedStaticSymbol(
staticSymbol, this.getStaticSymbol(baseMetadata.filePath, baseMetadata.name, members));
} else if (baseMetadata && baseMetadata.__symbolic === 'class') {
if (baseMetadata.statics && members.length === 1) {
return new ResolvedStaticSymbol(staticSymbol, baseMetadata.statics[members[0]]);
}
} else {
let value = baseMetadata;
for (let i = 0; i < members.length && value; i++) {
value = value[members[i]];
}
return new ResolvedStaticSymbol(staticSymbol, value);
}
return null;
}
private _resolveSymbolFromSummary(staticSymbol: StaticSymbol): ResolvedStaticSymbol {
const summary = this.summaryResolver.resolveSummary(staticSymbol);
return summary ? new ResolvedStaticSymbol(staticSymbol, summary.metadata) : null;
}
/**
* getStaticSymbol produces a Type whose metadata is known but whose implementation is not loaded.
* All types passed to the StaticResolver should be pseudo-types returned by this method.
*
* @param declarationFile the absolute path of the file where the symbol is declared
* @param name the name of the type.
*/
getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol {
return this.staticSymbolCache.get(declarationFile, name, members);
}
getSymbolsOf(filePath: string): StaticSymbol[] {
// Note: Some users use libraries that were not compiled with ngc, i.e. they don't
// have summaries, only .d.ts files. So we always need to check both, the summary
// and metadata.
let symbols = new Set<StaticSymbol>(this.summaryResolver.getSymbolsOf(filePath));
this._createSymbolsOf(filePath);
this.resolvedSymbols.forEach((resolvedSymbol) => {
if (resolvedSymbol.symbol.filePath === filePath) {
symbols.add(resolvedSymbol.symbol);
}
});
return Array.from(symbols);
}
private _createSymbolsOf(filePath: string) {
if (this.resolvedFilePaths.has(filePath)) {
return;
}
this.resolvedFilePaths.add(filePath);
const resolvedSymbols: ResolvedStaticSymbol[] = [];
const metadata = this.getModuleMetadata(filePath);
if (metadata['metadata']) {
// handle direct declarations of the symbol
Object.keys(metadata['metadata']).forEach((symbolName) => {
const symbolMeta = metadata['metadata'][symbolName];
resolvedSymbols.push(
this.createResolvedSymbol(this.getStaticSymbol(filePath, symbolName), symbolMeta));
});
}
// handle the symbols in one of the re-export location
if (metadata['exports']) {
for (const moduleExport of metadata['exports']) {
// handle the symbols in the list of explicitly re-exported symbols.
if (moduleExport.export) {
moduleExport.export.forEach((exportSymbol: any) => {
let symbolName: string;
if (typeof exportSymbol === 'string') {
symbolName = exportSymbol;
} else {
symbolName = exportSymbol.as;
}
let symName = symbolName;
if (typeof exportSymbol !== 'string') {
symName = exportSymbol.name;
}
const resolvedModule = this.resolveModule(moduleExport.from, filePath);
if (resolvedModule) {
const targetSymbol = this.getStaticSymbol(resolvedModule, symName);
const sourceSymbol = this.getStaticSymbol(filePath, symbolName);
resolvedSymbols.push(new ResolvedStaticSymbol(sourceSymbol, targetSymbol));
}
});
} else {
// handle the symbols via export * directives.
const resolvedModule = this.resolveModule(moduleExport.from, filePath);
if (resolvedModule) {
const nestedExports = this.getSymbolsOf(resolvedModule);
nestedExports.forEach((targetSymbol) => {
const sourceSymbol = this.getStaticSymbol(filePath, targetSymbol.name);
resolvedSymbols.push(new ResolvedStaticSymbol(sourceSymbol, targetSymbol));
});
}
}
}
}
resolvedSymbols.forEach(
(resolvedSymbol) => this.resolvedSymbols.set(resolvedSymbol.symbol, resolvedSymbol));
}
private createResolvedSymbol(sourceSymbol: StaticSymbol, metadata: any): ResolvedStaticSymbol {
const self = this;
class ReferenceTransformer extends ValueTransformer {
visitStringMap(map: {[key: string]: any}, functionParams: string[]): any {
const symbolic = map['__symbolic'];
if (symbolic === 'function') {
const oldLen = functionParams.length;
functionParams.push(...(map['parameters'] || []));
const result = super.visitStringMap(map, functionParams);
functionParams.length = oldLen;
return result;
} else if (symbolic === 'reference') {
const module = map['module'];
const name = map['name'];
if (!name) {
return null;
}
let filePath: string;
if (module) {
filePath = self.resolveModule(module, sourceSymbol.filePath);
if (!filePath) {
return {
__symbolic: 'error',
message: `Could not resolve ${module} relative to ${sourceSymbol.filePath}.`
};
}
} else {
const isFunctionParam = functionParams.indexOf(name) >= 0;
if (!isFunctionParam) {
filePath = sourceSymbol.filePath;
}
}
if (filePath) {
return self.getStaticSymbol(filePath, name);
} else {
// reference to a function parameter
return {__symbolic: 'reference', name: name};
}
} else {
return super.visitStringMap(map, functionParams);
}
}
}
const transformedMeta = visitValue(metadata, new ReferenceTransformer(), []);
return new ResolvedStaticSymbol(sourceSymbol, transformedMeta);
}
private reportError(error: Error, context: StaticSymbol, path?: string) {
if (this.errorRecorder) {
this.errorRecorder(error, (context && context.filePath) || path);
} else {
throw error;
}
}
/**
* @param module an absolute path to a module file.
*/
private getModuleMetadata(module: string): {[key: string]: any} {
let moduleMetadata = this.metadataCache.get(module);
if (!moduleMetadata) {
const moduleMetadatas = this.host.getMetadataFor(module);
if (moduleMetadatas) {
let maxVersion = -1;
moduleMetadatas.forEach((md) => {
if (md['version'] > maxVersion) {
maxVersion = md['version'];
moduleMetadata = md;
}
});
}
if (!moduleMetadata) {
moduleMetadata =
{__symbolic: 'module', version: SUPPORTED_SCHEMA_VERSION, module: module, metadata: {}};
}
if (moduleMetadata['version'] != SUPPORTED_SCHEMA_VERSION) {
const errorMessage = moduleMetadata['version'] == 2 ?
`Unsupported metadata version ${moduleMetadata['version']} for module ${module}. This module should be compiled with a newer version of ngc` :
`Metadata version mismatch for module ${module}, found version ${moduleMetadata['version']}, expected ${SUPPORTED_SCHEMA_VERSION}`;
this.reportError(new Error(errorMessage), null);
}
this.metadataCache.set(module, moduleMetadata);
}
return moduleMetadata;
}
getSymbolByModule(module: string, symbolName: string, containingFile?: string): StaticSymbol {
const filePath = this.resolveModule(module, containingFile);
if (!filePath) {
throw new Error(`Could not resolve module ${module} relative to ${containingFile}`);
}
return this.getStaticSymbol(filePath, symbolName);
}
private resolveModule(module: string, containingFile: string): string {
try {
return this.host.moduleNameToFileName(module, containingFile);
} catch (e) {
console.error(`Could not resolve module '${module}' relative to file ${containingFile}`);
this.reportError(new e, null, containingFile);
}
}
}

View File

@ -0,0 +1,77 @@
/**
* @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 {Summary, SummaryResolver} from '../summary_resolver';
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
import {ResolvedStaticSymbol} from './static_symbol_resolver';
import {deserializeSummaries, summaryFileName} from './summary_serializer';
const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
export interface AotSummaryResolverHost {
/**
* Loads an NgModule/Directive/Pipe summary file
*/
loadSummary(filePath: string): string /*|null*/;
/**
* Returns whether a file is a source file or not.
*/
isSourceFile(sourceFilePath: string): boolean;
}
export class AotSummaryResolver implements SummaryResolver<StaticSymbol> {
private summaryCache = new Map<StaticSymbol, Summary<StaticSymbol>>();
private loadedFilePaths = new Set<string>();
constructor(private host: AotSummaryResolverHost, private staticSymbolCache: StaticSymbolCache) {}
private _assertNoMembers(symbol: StaticSymbol) {
if (symbol.members.length) {
throw new Error(
`Internal state: StaticSymbols in summaries can't have members! ${JSON.stringify(symbol)}`);
}
}
resolveSummary(staticSymbol: StaticSymbol): Summary<StaticSymbol> {
this._assertNoMembers(staticSymbol);
let summary = this.summaryCache.get(staticSymbol);
if (!summary) {
this._loadSummaryFile(staticSymbol.filePath);
summary = this.summaryCache.get(staticSymbol);
}
return summary;
}
getSymbolsOf(filePath: string): StaticSymbol[] {
this._loadSummaryFile(filePath);
return Array.from(this.summaryCache.keys()).filter((symbol) => symbol.filePath === filePath);
}
private _loadSummaryFile(filePath: string) {
if (this.loadedFilePaths.has(filePath)) {
return;
}
this.loadedFilePaths.add(filePath);
if (!this.host.isSourceFile(filePath)) {
const summaryFilePath = summaryFileName(filePath);
let json: string;
try {
json = this.host.loadSummary(summaryFilePath);
} catch (e) {
console.error(`Error loading summary file ${summaryFilePath}`);
throw e;
}
if (json) {
const readSummaries = deserializeSummaries(this.staticSymbolCache, json);
readSummaries.forEach((summary) => { this.summaryCache.set(summary.symbol, summary); });
}
}
}
}

View File

@ -0,0 +1,183 @@
/**
* @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 {CompileNgModuleSummary, CompileSummaryKind, CompileTypeSummary} from '../compile_metadata';
import {Summary, SummaryResolver} from '../summary_resolver';
import {ValueTransformer, visitValue} from '../util';
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
export interface AotSummarySerializerHost {
/**
* Returns the output file path of a source file.
* E.g.
* `some_file.ts` -> `some_file.d.ts`
*/
getOutputFileName(sourceFilePath: string): string;
/**
* Returns whether a file is a source file or not.
*/
isSourceFile(sourceFilePath: string): boolean;
}
export function serializeSummaries(
host: AotSummarySerializerHost, summaryResolver: SummaryResolver<StaticSymbol>,
symbolResolver: StaticSymbolResolver,
symbols: ResolvedStaticSymbol[], types: CompileTypeSummary[]): string {
const serializer = new Serializer(host);
// for symbols, we use everything except for the class metadata itself
// (we keep the statics though), as the class metadata is contained in the
// CompileTypeSummary.
symbols.forEach(
(resolvedSymbol) => serializer.addOrMergeSummary(
{symbol: resolvedSymbol.symbol, metadata: resolvedSymbol.metadata}));
// Add summaries that are referenced by the given symbols (transitively)
// Note: the serializer.symbols array might be growing while
// we execute the loop!
for (let processedIndex = 0; processedIndex < serializer.symbols.length; processedIndex++) {
const symbol = serializer.symbols[processedIndex];
if (!host.isSourceFile(symbol.filePath)) {
let summary = summaryResolver.resolveSummary(symbol);
if (!summary) {
// some symbols might originate from a plain typescript library
// that just exported .d.ts and .metadata.json files, i.e. where no summary
// files were created.
const resolvedSymbol = symbolResolver.resolveSymbol(symbol);
if (resolvedSymbol) {
summary = {symbol: resolvedSymbol.symbol, metadata: resolvedSymbol.metadata};
}
}
if (summary) {
serializer.addOrMergeSummary(summary);
}
}
}
// Add type summaries.
// Note: We don't add the summaries of all referenced symbols as for the ResolvedSymbols,
// as the type summaries already contain the transitive data that they require
// (in a minimal way).
types.forEach((typeSummary) => {
serializer.addOrMergeSummary(
{symbol: typeSummary.type.reference, metadata: {__symbolic: 'class'}, type: typeSummary});
if (typeSummary.summaryKind === CompileSummaryKind.NgModule) {
const ngModuleSummary = <CompileNgModuleSummary>typeSummary;
ngModuleSummary.exportedDirectives.concat(ngModuleSummary.exportedPipes).forEach((id) => {
const symbol: StaticSymbol = id.reference;
if (!host.isSourceFile(symbol.filePath)) {
serializer.addOrMergeSummary(summaryResolver.resolveSummary(symbol));
}
});
}
});
return serializer.serialize();
}
export function deserializeSummaries(
symbolCache: StaticSymbolCache, json: string): Summary<StaticSymbol>[] {
const deserializer = new Deserializer(symbolCache);
return deserializer.deserialize(json);
}
export function summaryFileName(fileName: string): string {
const fileNameWithoutSuffix = fileName.replace(STRIP_SRC_FILE_SUFFIXES, '');
return `${fileNameWithoutSuffix}.ngsummary.json`;
}
class Serializer extends ValueTransformer {
symbols: StaticSymbol[] = [];
private indexBySymbol = new Map<StaticSymbol, number>();
// This now contains a `__symbol: number` in the place of
// StaticSymbols, but otherwise has the same shape as the original objects.
private processedSummaryBySymbol = new Map<StaticSymbol, any>();
private processedSummaries: any[] = [];
constructor(private host: AotSummarySerializerHost) { super(); }
addOrMergeSummary(summary: Summary<StaticSymbol>) {
let symbolMeta = summary.metadata;
if (symbolMeta && symbolMeta.__symbolic === 'class') {
// For classes, we only keep their statics, but not the metadata
// of the class itself as that has been captured already via other summaries
// (e.g. DirectiveSummary, ...).
symbolMeta = {__symbolic: 'class', statics: symbolMeta.statics};
}
let processedSummary = this.processedSummaryBySymbol.get(summary.symbol);
if (!processedSummary) {
processedSummary = this.processValue({symbol: summary.symbol});
this.processedSummaries.push(processedSummary);
this.processedSummaryBySymbol.set(summary.symbol, processedSummary);
}
// Note: == by purpose to compare with undefined!
if (processedSummary.metadata == null && symbolMeta != null) {
processedSummary.metadata = this.processValue(symbolMeta);
}
// Note: == by purpose to compare with undefined!
if (processedSummary.type == null && summary.type != null) {
processedSummary.type = this.processValue(summary.type);
}
}
serialize(): string {
return JSON.stringify({
summaries: this.processedSummaries,
symbols: this.symbols.map((symbol, index) => {
return {
__symbol: index,
name: symbol.name,
// We convert the source filenames tinto output filenames,
// as the generated summary file will be used when teh current
// compilation unit is used as a library
filePath: this.host.getOutputFileName(symbol.filePath)
};
})
});
}
private processValue(value: any): any { return visitValue(value, this, null); }
visitOther(value: any, context: any): any {
if (value instanceof StaticSymbol) {
let index = this.indexBySymbol.get(value);
// Note: == by purpose to compare with undefined!
if (index == null) {
index = this.indexBySymbol.size;
this.indexBySymbol.set(value, index);
this.symbols.push(value);
}
return {__symbol: index};
}
}
}
class Deserializer extends ValueTransformer {
private symbols: StaticSymbol[];
constructor(private symbolCache: StaticSymbolCache) { super(); }
deserialize(json: string): Summary<StaticSymbol>[] {
const data: {summaries: any[], symbols: any[]} = JSON.parse(json);
this.symbols = data.symbols.map(
serializedSymbol => this.symbolCache.get(serializedSymbol.filePath, serializedSymbol.name));
return visitValue(data.summaries, this, null);
}
visitStringMap(map: {[key: string]: any}, context: any): any {
if ('__symbol' in map) {
return this.symbols[map['__symbol']];
} else {
return super.visitStringMap(map, context);
}
}
}

View File

@ -8,11 +8,12 @@
import {ChangeDetectionStrategy, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core';
import {StaticSymbol} from './aot/static_symbol';
import {ListWrapper} from './facade/collection';
import {isPresent} from './facade/lang';
import {LifecycleHooks} from './private_import_core';
import {isPresent, stringify} from './facade/lang';
import {LifecycleHooks, reflector} from './private_import_core';
import {CssSelector} from './selector';
import {sanitizeIdentifier, splitAtColon} from './util';
import {splitAtColon} from './util';
function unimplemented(): any {
throw new Error('unimplemented');
@ -24,10 +25,6 @@ function unimplemented(): any {
// group 3: "@trigger" from "@trigger"
const HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))|(\@[-\w]+)$/;
export abstract class CompileMetadataWithIdentifier {
get identifier(): CompileIdentifierMetadata { return <CompileIdentifierMetadata>unimplemented(); }
}
export class CompileAnimationEntryMetadata {
constructor(
public name: string = null, public definitions: CompileAnimationStateMetadata[] = null) {}
@ -78,24 +75,50 @@ export class CompileAnimationGroupMetadata extends CompileAnimationWithStepsMeta
constructor(steps: CompileAnimationMetadata[] = null) { super(steps); }
}
export class CompileIdentifierMetadata implements CompileMetadataWithIdentifier {
reference: any;
name: string;
prefix: string;
moduleUrl: string;
value: any;
constructor(
{reference, name, moduleUrl, prefix, value}:
{reference?: any, name?: string, moduleUrl?: string, prefix?: string, value?: any} = {}) {
this.reference = reference;
this.name = name;
this.prefix = prefix;
this.moduleUrl = moduleUrl;
this.value = value;
function _sanitizeIdentifier(name: string): string {
return name.replace(/\W/g, '_');
}
let _anonymousTypeIndex = 0;
export function identifierName(compileIdentifier: CompileIdentifierMetadata): string {
if (!compileIdentifier || !compileIdentifier.reference) {
return null;
}
const ref = compileIdentifier.reference;
if (ref instanceof StaticSymbol) {
return ref.name;
}
if (ref['__anonymousType']) {
return ref['__anonymousType'];
}
let identifier = stringify(ref);
if (identifier.indexOf('(') >= 0) {
// case: anonymous functions!
identifier = `anonymous_${_anonymousTypeIndex++}`;
ref['__anonymousType'] = identifier;
} else {
identifier = _sanitizeIdentifier(identifier);
}
return identifier;
}
get identifier(): CompileIdentifierMetadata { return this; }
export function identifierModuleUrl(compileIdentifier: CompileIdentifierMetadata): string {
const ref = compileIdentifier.reference;
if (ref instanceof StaticSymbol) {
return ref.filePath;
}
return reflector.importUri(ref);
}
export interface CompileIdentifierMetadata { reference: any; }
export enum CompileSummaryKind {
Pipe,
Directive,
NgModule,
Injectable
}
/**
@ -103,158 +126,70 @@ export class CompileIdentifierMetadata implements CompileMetadataWithIdentifier
* in other modules / components. However, this data is not enough to compile
* the directive / module itself.
*/
export interface CompileSummary {
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
export interface CompileTypeSummary {
summaryKind: CompileSummaryKind;
type: CompileTypeMetadata;
}
export class CompileDiDependencyMetadata {
isAttribute: boolean;
isSelf: boolean;
isHost: boolean;
isSkipSelf: boolean;
isOptional: boolean;
isValue: boolean;
export interface CompileDiDependencyMetadata {
isAttribute?: boolean;
isSelf?: boolean;
isHost?: boolean;
isSkipSelf?: boolean;
isOptional?: boolean;
isValue?: boolean;
token?: CompileTokenMetadata;
value?: any;
}
export interface CompileProviderMetadata {
token: CompileTokenMetadata;
value: any;
constructor({isAttribute, isSelf, isHost, isSkipSelf, isOptional, isValue, token, value}: {
isAttribute?: boolean,
isSelf?: boolean,
isHost?: boolean,
isSkipSelf?: boolean,
isOptional?: boolean,
isValue?: boolean,
query?: CompileQueryMetadata,
viewQuery?: CompileQueryMetadata,
token?: CompileTokenMetadata,
value?: any
} = {}) {
this.isAttribute = !!isAttribute;
this.isSelf = !!isSelf;
this.isHost = !!isHost;
this.isSkipSelf = !!isSkipSelf;
this.isOptional = !!isOptional;
this.isValue = !!isValue;
this.token = token;
this.value = value;
}
useClass?: CompileTypeMetadata;
useValue?: any;
useExisting?: CompileTokenMetadata;
useFactory?: CompileFactoryMetadata;
deps?: CompileDiDependencyMetadata[];
multi?: boolean;
}
export class CompileProviderMetadata {
token: CompileTokenMetadata;
useClass: CompileTypeMetadata;
useValue: any;
useExisting: CompileTokenMetadata;
useFactory: CompileFactoryMetadata;
deps: CompileDiDependencyMetadata[];
multi: boolean;
constructor({token, useClass, useValue, useExisting, useFactory, deps, multi}: {
token?: CompileTokenMetadata,
useClass?: CompileTypeMetadata,
useValue?: any,
useExisting?: CompileTokenMetadata,
useFactory?: CompileFactoryMetadata,
deps?: CompileDiDependencyMetadata[],
multi?: boolean
}) {
this.token = token;
this.useClass = useClass;
this.useValue = useValue;
this.useExisting = useExisting;
this.useFactory = useFactory;
this.deps = deps || null;
this.multi = !!multi;
}
}
export class CompileFactoryMetadata extends CompileIdentifierMetadata {
export interface CompileFactoryMetadata extends CompileIdentifierMetadata {
diDeps: CompileDiDependencyMetadata[];
reference: any;
}
constructor({reference, name, moduleUrl, prefix, diDeps, value}: {
reference?: Function,
name?: string,
prefix?: string,
moduleUrl?: string,
value?: boolean,
diDeps?: CompileDiDependencyMetadata[]
}) {
super({reference: reference, name: name, prefix: prefix, moduleUrl: moduleUrl, value: value});
this.diDeps = _normalizeArray(diDeps);
export function tokenName(token: CompileTokenMetadata) {
return isPresent(token.value) ? _sanitizeIdentifier(token.value) :
identifierName(token.identifier);
}
export function tokenReference(token: CompileTokenMetadata) {
if (isPresent(token.identifier)) {
return token.identifier.reference;
} else {
return token.value;
}
}
export class CompileTokenMetadata implements CompileMetadataWithIdentifier {
value: any;
identifier: CompileIdentifierMetadata;
identifierIsInstance: boolean;
constructor(
{value, identifier, identifierIsInstance}:
{value?: any, identifier?: CompileIdentifierMetadata, identifierIsInstance?: boolean}) {
this.value = value;
this.identifier = identifier;
this.identifierIsInstance = !!identifierIsInstance;
}
get reference(): any {
if (isPresent(this.identifier)) {
return this.identifier.reference;
} else {
return this.value;
}
}
get name(): string {
return isPresent(this.value) ? sanitizeIdentifier(this.value) : this.identifier.name;
}
export interface CompileTokenMetadata {
value?: any;
identifier?: CompileIdentifierMetadata|CompileTypeMetadata;
}
/**
* Metadata regarding compilation of a type.
*/
export class CompileTypeMetadata extends CompileIdentifierMetadata {
isHost: boolean;
export interface CompileTypeMetadata extends CompileIdentifierMetadata {
diDeps: CompileDiDependencyMetadata[];
lifecycleHooks: LifecycleHooks[];
constructor({reference, name, moduleUrl, prefix, isHost, value, diDeps, lifecycleHooks}: {
reference?: Type<any>,
name?: string,
moduleUrl?: string,
prefix?: string,
isHost?: boolean,
value?: any,
diDeps?: CompileDiDependencyMetadata[],
lifecycleHooks?: LifecycleHooks[];
} = {}) {
super({reference: reference, name: name, moduleUrl: moduleUrl, prefix: prefix, value: value});
this.isHost = !!isHost;
this.diDeps = _normalizeArray(diDeps);
this.lifecycleHooks = _normalizeArray(lifecycleHooks);
}
reference: any;
}
export class CompileQueryMetadata {
export interface CompileQueryMetadata {
selectors: Array<CompileTokenMetadata>;
descendants: boolean;
first: boolean;
propertyName: string;
read: CompileTokenMetadata;
constructor({selectors, descendants, first, propertyName, read}: {
selectors?: Array<CompileTokenMetadata>,
descendants?: boolean,
first?: boolean,
propertyName?: string,
read?: CompileTokenMetadata
} = {}) {
this.selectors = selectors;
this.descendants = !!descendants;
this.first = !!first;
this.propertyName = propertyName;
this.read = read;
}
}
/**
@ -276,8 +211,7 @@ export class CompileStylesheetMetadata {
/**
* Summary Metadata regarding compilation of a template.
*/
export interface CompileTemplateSummary extends CompileSummary {
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
export interface CompileTemplateSummary {
animations: string[];
ngContentSelectors: string[];
encapsulation: ViewEncapsulation;
@ -325,7 +259,6 @@ export class CompileTemplateMetadata {
toSummary(): CompileTemplateSummary {
return {
isSummary: true,
animations: this.animations.map(anim => anim.name),
ngContentSelectors: this.ngContentSelectors,
encapsulation: this.encapsulation
@ -333,8 +266,9 @@ export class CompileTemplateMetadata {
}
}
export interface CompileDirectiveSummary extends CompileSummary {
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
// Note: This should only use interfaces as nested data types
// as we need to be able to serialize this from/to JSON!
export interface CompileDirectiveSummary extends CompileTypeSummary {
type: CompileTypeMetadata;
isComponent: boolean;
selector: string;
@ -355,10 +289,11 @@ export interface CompileDirectiveSummary extends CompileSummary {
/**
* Metadata regarding compilation of a directive.
*/
export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
export class CompileDirectiveMetadata {
static create(
{type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host, providers,
viewProviders, queries, viewQueries, entryComponents, template}: {
{isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host,
providers, viewProviders, queries, viewQueries, entryComponents, template}: {
isHost?: boolean,
type?: CompileTypeMetadata,
isComponent?: boolean,
selector?: string,
@ -367,10 +302,8 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
inputs?: string[],
outputs?: string[],
host?: {[key: string]: string},
providers?:
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
viewProviders?:
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
providers?: CompileProviderMetadata[],
viewProviders?: CompileProviderMetadata[],
queries?: CompileQueryMetadata[],
viewQueries?: CompileQueryMetadata[],
entryComponents?: CompileIdentifierMetadata[],
@ -412,6 +345,7 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
}
return new CompileDirectiveMetadata({
isHost,
type,
isComponent: !!isComponent, selector, exportAs, changeDetection,
inputs: inputsMap,
@ -427,6 +361,7 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
template,
});
}
isHost: boolean;
type: CompileTypeMetadata;
isComponent: boolean;
selector: string;
@ -446,9 +381,10 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
template: CompileTemplateMetadata;
constructor(
{type, isComponent, selector, exportAs, changeDetection, inputs, outputs, hostListeners,
hostProperties, hostAttributes, providers, viewProviders, queries, viewQueries,
entryComponents, template}: {
{isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs,
hostListeners, hostProperties, hostAttributes, providers, viewProviders, queries,
viewQueries, entryComponents, template}: {
isHost?: boolean,
type?: CompileTypeMetadata,
isComponent?: boolean,
selector?: string,
@ -459,15 +395,14 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
hostListeners?: {[key: string]: string},
hostProperties?: {[key: string]: string},
hostAttributes?: {[key: string]: string},
providers?:
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
viewProviders?:
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
providers?: CompileProviderMetadata[],
viewProviders?: CompileProviderMetadata[],
queries?: CompileQueryMetadata[],
viewQueries?: CompileQueryMetadata[],
entryComponents?: CompileIdentifierMetadata[],
template?: CompileTemplateMetadata,
} = {}) {
this.isHost = !!isHost;
this.type = type;
this.isComponent = isComponent;
this.selector = selector;
@ -487,11 +422,9 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
this.template = template;
}
get identifier(): CompileIdentifierMetadata { return this.type; }
toSummary(): CompileDirectiveSummary {
return {
isSummary: true,
summaryKind: CompileSummaryKind.Directive,
type: this.type,
isComponent: this.isComponent,
selector: this.selector,
@ -514,16 +447,12 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
/**
* Construct {@link CompileDirectiveMetadata} from {@link ComponentTypeMetadata} and a selector.
*/
export function createHostComponentMeta(compMeta: CompileDirectiveMetadata):
CompileDirectiveMetadata {
export function createHostComponentMeta(
typeReference: any, compMeta: CompileDirectiveMetadata): CompileDirectiveMetadata {
const template = CssSelector.parse(compMeta.selector)[0].getMatchingElementTemplate();
return CompileDirectiveMetadata.create({
type: new CompileTypeMetadata({
reference: Object,
name: `${compMeta.type.name}_Host`,
moduleUrl: compMeta.type.moduleUrl,
isHost: true
}),
isHost: true,
type: {reference: typeReference, diDeps: [], lifecycleHooks: []},
template: new CompileTemplateMetadata({
encapsulation: ViewEncapsulation.None,
template: template,
@ -546,14 +475,13 @@ export function createHostComponentMeta(compMeta: CompileDirectiveMetadata):
});
}
export interface CompilePipeSummary extends CompileSummary {
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
export interface CompilePipeSummary extends CompileTypeSummary {
type: CompileTypeMetadata;
name: string;
pure: boolean;
}
export class CompilePipeMetadata implements CompileMetadataWithIdentifier {
export class CompilePipeMetadata {
type: CompileTypeMetadata;
name: string;
pure: boolean;
@ -567,42 +495,44 @@ export class CompilePipeMetadata implements CompileMetadataWithIdentifier {
this.name = name;
this.pure = !!pure;
}
get identifier(): CompileIdentifierMetadata { return this.type; }
toSummary(): CompilePipeSummary {
return {isSummary: true, type: this.type, name: this.name, pure: this.pure};
return {
summaryKind: CompileSummaryKind.Pipe,
type: this.type,
name: this.name,
pure: this.pure
};
}
}
export interface CompileNgModuleInjectorSummary extends CompileSummary {
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
// Note: This should only use interfaces as nested data types
// as we need to be able to serialize this from/to JSON!
export interface CompileNgModuleSummary extends CompileTypeSummary {
type: CompileTypeMetadata;
entryComponents: CompileIdentifierMetadata[];
providers: CompileProviderMetadata[];
importedModules: CompileNgModuleInjectorSummary[];
exportedModules: CompileNgModuleInjectorSummary[];
}
export interface CompileNgModuleDirectiveSummary extends CompileSummary {
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
type: CompileTypeMetadata;
// Note: This is transitive over the exported modules.
exportedDirectives: CompileIdentifierMetadata[];
// Note: This is transitive over the exported modules.
exportedPipes: CompileIdentifierMetadata[];
exportedModules: CompileNgModuleDirectiveSummary[];
loadingPromises: Promise<any>[];
}
export type CompileNgModuleSummary =
CompileNgModuleInjectorSummary & CompileNgModuleDirectiveSummary;
// Note: This is transitive.
entryComponents: CompileIdentifierMetadata[];
// Note: This is transitive.
providers: {provider: CompileProviderMetadata, module: CompileIdentifierMetadata}[];
// Note: This is transitive.
modules: CompileTypeMetadata[];
}
/**
* Metadata regarding compilation of a module.
*/
export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
export class CompileNgModuleMetadata {
type: CompileTypeMetadata;
declaredDirectives: CompileIdentifierMetadata[];
exportedDirectives: CompileIdentifierMetadata[];
declaredPipes: CompileIdentifierMetadata[];
exportedPipes: CompileIdentifierMetadata[];
entryComponents: CompileIdentifierMetadata[];
bootstrapComponents: CompileIdentifierMetadata[];
@ -620,8 +550,7 @@ export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
entryComponents, bootstrapComponents, importedModules, exportedModules, schemas,
transitiveModule, id}: {
type?: CompileTypeMetadata,
providers?:
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
providers?: CompileProviderMetadata[],
declaredDirectives?: CompileIdentifierMetadata[],
exportedDirectives?: CompileIdentifierMetadata[],
declaredPipes?: CompileIdentifierMetadata[],
@ -649,84 +578,81 @@ export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
this.transitiveModule = transitiveModule;
}
get identifier(): CompileIdentifierMetadata { return this.type; }
toSummary(): CompileNgModuleSummary {
return {
isSummary: true,
summaryKind: CompileSummaryKind.NgModule,
type: this.type,
entryComponents: this.entryComponents,
providers: this.providers,
importedModules: this.importedModules,
exportedModules: this.exportedModules,
exportedDirectives: this.exportedDirectives,
exportedPipes: this.exportedPipes,
loadingPromises: this.transitiveModule.loadingPromises
};
}
toInjectorSummary(): CompileNgModuleInjectorSummary {
return {
isSummary: true,
type: this.type,
entryComponents: this.entryComponents,
providers: this.providers,
importedModules: this.importedModules,
exportedModules: this.exportedModules
};
}
toDirectiveSummary(): CompileNgModuleDirectiveSummary {
return {
isSummary: true,
type: this.type,
exportedDirectives: this.exportedDirectives,
exportedPipes: this.exportedPipes,
exportedModules: this.exportedModules,
loadingPromises: this.transitiveModule.loadingPromises
entryComponents: this.transitiveModule.entryComponents,
providers: this.transitiveModule.providers,
modules: this.transitiveModule.modules,
exportedDirectives: this.transitiveModule.exportedDirectives,
exportedPipes: this.transitiveModule.exportedPipes
};
}
}
export class TransitiveCompileNgModuleMetadata {
directivesSet = new Set<any>();
directives: CompileIdentifierMetadata[] = [];
exportedDirectivesSet = new Set<any>();
exportedDirectives: CompileIdentifierMetadata[] = [];
pipesSet = new Set<any>();
pipes: CompileIdentifierMetadata[] = [];
exportedPipesSet = new Set<any>();
exportedPipes: CompileIdentifierMetadata[] = [];
modulesSet = new Set<any>();
modules: CompileTypeMetadata[] = [];
entryComponentsSet = new Set<any>();
entryComponents: CompileIdentifierMetadata[] = [];
constructor(
public modules: CompileNgModuleInjectorSummary[], public providers: CompileProviderMetadata[],
public entryComponents: CompileIdentifierMetadata[],
public directives: CompileIdentifierMetadata[], public pipes: CompileIdentifierMetadata[],
public loadingPromises: Promise<any>[]) {
directives.forEach(dir => this.directivesSet.add(dir.reference));
pipes.forEach(pipe => this.pipesSet.add(pipe.reference));
providers: {provider: CompileProviderMetadata, module: CompileIdentifierMetadata}[] = [];
addProvider(provider: CompileProviderMetadata, module: CompileIdentifierMetadata) {
this.providers.push({provider: provider, module: module});
}
}
export function removeIdentifierDuplicates<T extends CompileMetadataWithIdentifier>(items: T[]):
T[] {
const map = new Map<any, T>();
items.forEach((item) => {
if (!map.get(item.identifier.reference)) {
map.set(item.identifier.reference, item);
addDirective(id: CompileIdentifierMetadata) {
if (!this.directivesSet.has(id.reference)) {
this.directivesSet.add(id.reference);
this.directives.push(id);
}
}
addExportedDirective(id: CompileIdentifierMetadata) {
if (!this.exportedDirectivesSet.has(id.reference)) {
this.exportedDirectivesSet.add(id.reference);
this.exportedDirectives.push(id);
}
}
addPipe(id: CompileIdentifierMetadata) {
if (!this.pipesSet.has(id.reference)) {
this.pipesSet.add(id.reference);
this.pipes.push(id);
}
}
addExportedPipe(id: CompileIdentifierMetadata) {
if (!this.exportedPipesSet.has(id.reference)) {
this.exportedPipesSet.add(id.reference);
this.exportedPipes.push(id);
}
}
addModule(id: CompileTypeMetadata) {
if (!this.modulesSet.has(id.reference)) {
this.modulesSet.add(id.reference);
this.modules.push(id);
}
}
addEntryComponent(id: CompileIdentifierMetadata) {
if (!this.entryComponentsSet.has(id.reference)) {
this.entryComponentsSet.add(id.reference);
this.entryComponents.push(id);
}
}
});
return Array.from(map.values());
}
function _normalizeArray(obj: any[]): any[] {
return obj || [];
}
export function isStaticSymbol(value: any): value is StaticSymbol {
return typeof value === 'object' && value !== null && value['name'] && value['filePath'];
}
export interface StaticSymbol {
name: string;
filePath: string;
}
export class ProviderMeta {
token: any;
useClass: Type<any>;

View File

@ -5,8 +5,7 @@
* 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 {Identifiers, resolveIdentifier} from '../identifiers';
import {Identifiers, createIdentifier} from '../identifiers';
import {ClassBuilder} from '../output/class_builder';
import * as o from '../output/output_ast';
@ -22,7 +21,7 @@ export function createCheckBindingField(builder: ClassBuilder): CheckBindingFiel
// private is fine here as no child view will reference the cached value...
builder.fields.push(new o.ClassField(fieldExpr.name, null, [o.StmtModifier.Private]));
builder.ctorStmts.push(o.THIS_EXPR.prop(fieldExpr.name)
.set(o.importExpr(resolveIdentifier(Identifiers.UNINITIALIZED)))
.set(o.importExpr(createIdentifier(Identifiers.UNINITIALIZED)))
.toStmt());
return new CheckBindingField(fieldExpr, bindingId);
}
@ -30,7 +29,7 @@ export function createCheckBindingField(builder: ClassBuilder): CheckBindingFiel
export function createCheckBindingStmt(
evalResult: ConvertPropertyBindingResult, fieldExpr: o.ReadPropExpr,
throwOnChangeVar: o.Expression, actions: o.Statement[]): o.Statement[] {
let condition: o.Expression = o.importExpr(resolveIdentifier(Identifiers.checkBinding)).callFn([
let condition: o.Expression = o.importExpr(createIdentifier(Identifiers.checkBinding)).callFn([
throwOnChangeVar, fieldExpr, evalResult.currValExpr
]);
if (evalResult.forceUpdate) {

View File

@ -8,8 +8,8 @@
import * as cdAst from '../expression_parser/ast';
import {isBlank, isPresent} from '../facade/lang';
import {Identifiers, resolveIdentifier} from '../identifiers';
import {isBlank} from '../facade/lang';
import {Identifiers, createIdentifier} from '../identifiers';
import {ClassBuilder} from '../output/class_builder';
import * as o from '../output/output_ast';
@ -116,7 +116,7 @@ export function createSharedBindingVariablesIfNeeded(stmts: o.Statement[]): o.St
if (readVars.has(VAL_UNWRAPPER_VAR.name)) {
unwrapperStmts.push(
VAL_UNWRAPPER_VAR
.set(o.importExpr(resolveIdentifier(Identifiers.ValueUnwrapper)).instantiate([]))
.set(o.importExpr(createIdentifier(Identifiers.ValueUnwrapper)).instantiate([]))
.toDeclStmt(null, [o.StmtModifier.Final]));
}
return unwrapperStmts;
@ -277,16 +277,21 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
args.push(o.literal(ast.strings[ast.strings.length - 1]));
return ast.expressions.length <= 9 ?
o.importExpr(resolveIdentifier(Identifiers.inlineInterpolate)).callFn(args) :
o.importExpr(resolveIdentifier(Identifiers.interpolate)).callFn([
o.importExpr(createIdentifier(Identifiers.inlineInterpolate)).callFn(args) :
o.importExpr(createIdentifier(Identifiers.interpolate)).callFn([
args[0], o.literalArr(args.slice(1))
]);
}
visitKeyedRead(ast: cdAst.KeyedRead, mode: _Mode): any {
const leftMostSafe = this.leftMostSafeNode(ast);
if (leftMostSafe) {
return this.convertSafeAccess(ast, leftMostSafe, mode);
} else {
return convertToStatementIfNeeded(
mode, this.visit(ast.obj, _Mode.Expression).key(this.visit(ast.key, _Mode.Expression)));
}
}
visitKeyedWrite(ast: cdAst.KeyedWrite, mode: _Mode): any {
const obj: o.Expression = this.visit(ast.obj, _Mode.Expression);
@ -333,7 +338,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
const receiver = this.visit(ast.receiver, _Mode.Expression);
if (receiver === this._implicitReceiver) {
const varExpr = this._getLocal(ast.name);
if (isPresent(varExpr)) {
if (varExpr) {
result = varExpr.callFn(args);
}
}
@ -369,7 +374,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
const receiver: o.Expression = this.visit(ast.receiver, _Mode.Expression);
if (receiver === this._implicitReceiver) {
const varExpr = this._getLocal(ast.name);
if (isPresent(varExpr)) {
if (varExpr) {
throw new Error('Cannot assign to a reference or variable!');
}
}
@ -578,7 +583,7 @@ function flattenStatements(arg: any, output: o.Statement[]) {
function createCachedLiteralArray(builder: ClassBuilder, values: o.Expression[]): o.Expression {
if (values.length === 0) {
return o.importExpr(resolveIdentifier(Identifiers.EMPTY_ARRAY));
return o.importExpr(createIdentifier(Identifiers.EMPTY_ARRAY));
}
const proxyExpr = o.THIS_EXPR.prop(`_arr_${builder.fields.length}`);
const proxyParams: o.FnParam[] = [];
@ -599,7 +604,7 @@ function createCachedLiteralArray(builder: ClassBuilder, values: o.Expression[])
function createCachedLiteralMap(
builder: ClassBuilder, entries: [string, o.Expression][]): o.Expression {
if (entries.length === 0) {
return o.importExpr(resolveIdentifier(Identifiers.EMPTY_MAP));
return o.importExpr(createIdentifier(Identifiers.EMPTY_MAP));
}
const proxyExpr = o.THIS_EXPR.prop(`_map_${builder.fields.length}`);
const proxyParams: o.FnParam[] = [];

View File

@ -8,15 +8,12 @@
import {CompileTokenMetadata} from '../compile_metadata';
import {isPresent} from '../facade/lang';
import {IdentifierSpec, Identifiers, resolveEnumIdentifier, resolveIdentifier} from '../identifiers';
import {IdentifierSpec, Identifiers, createEnumIdentifier, createIdentifier} from '../identifiers';
import * as o from '../output/output_ast';
export function createDiTokenExpression(token: CompileTokenMetadata): o.Expression {
if (isPresent(token.value)) {
return o.literal(token.value);
} else if (token.identifierIsInstance) {
return o.importExpr(token.identifier)
.instantiate([], o.importType(token.identifier, [], [o.TypeModifier.Const]));
} else {
return o.importExpr(token.identifier);
}
@ -24,13 +21,13 @@ export function createDiTokenExpression(token: CompileTokenMetadata): o.Expressi
export function createInlineArray(values: o.Expression[]): o.Expression {
if (values.length === 0) {
return o.importExpr(resolveIdentifier(Identifiers.EMPTY_INLINE_ARRAY));
return o.importExpr(createIdentifier(Identifiers.EMPTY_INLINE_ARRAY));
}
const log2 = Math.log(values.length) / Math.log(2);
const index = Math.ceil(log2);
const identifierSpec = index < Identifiers.inlineArrays.length ? Identifiers.inlineArrays[index] :
Identifiers.InlineArrayDynamic;
const identifier = resolveIdentifier(identifierSpec);
const identifier = createIdentifier(identifierSpec);
return o.importExpr(identifier).instantiate([
<o.Expression>o.literal(values.length)
].concat(values));
@ -46,7 +43,7 @@ export function createPureProxy(
throw new Error(`Unsupported number of argument for pure functions: ${argCount}`);
}
builder.ctorStmts.push(o.THIS_EXPR.prop(pureProxyProp.name)
.set(o.importExpr(resolveIdentifier(pureProxyId)).callFn([fn]))
.set(o.importExpr(createIdentifier(pureProxyId)).callFn([fn]))
.toStmt());
}
@ -56,5 +53,5 @@ export function createEnumExpression(enumType: IdentifierSpec, enumValue: any):
if (!enumName) {
throw new Error(`Unknown enum value ${enumValue} in ${enumType.name}`);
}
return o.importExpr(resolveEnumIdentifier(resolveIdentifier(enumType), enumName));
return o.importExpr(createEnumIdentifier(enumType, enumName));
}

View File

@ -8,7 +8,7 @@
import {SecurityContext} from '@angular/core';
import {isPresent} from '../facade/lang';
import {Identifiers, resolveIdentifier} from '../identifiers';
import {Identifiers, createIdentifier} from '../identifiers';
import * as o from '../output/output_ast';
import {EMPTY_STATE as EMPTY_ANIMATION_STATE} from '../private_import_core';
import {BoundElementPropertyAst, BoundEventAst, PropertyBindingType} from '../template_parser/template_ast';
@ -26,7 +26,7 @@ export function writeToRenderer(
case PropertyBindingType.Property:
if (logBindingUpdate) {
updateStmts.push(
o.importExpr(resolveIdentifier(Identifiers.setBindingDebugInfo))
o.importExpr(createIdentifier(Identifiers.setBindingDebugInfo))
.callFn([renderer, renderElement, o.literal(boundProp.name), renderValue])
.toStmt());
}
@ -91,8 +91,8 @@ function sanitizedValue(
export function triggerAnimation(
view: o.Expression, componentView: o.Expression, boundProp: BoundElementPropertyAst,
eventListener: o.Expression, renderElement: o.Expression, renderValue: o.Expression,
lastRenderValue: o.Expression) {
boundOutputs: BoundEventAst[], eventListener: o.Expression, renderElement: o.Expression,
renderValue: o.Expression, lastRenderValue: o.Expression) {
const detachStmts: o.Statement[] = [];
const updateStmts: o.Statement[] = [];
@ -104,7 +104,7 @@ export function triggerAnimation(
// it's important to normalize the void value as `void` explicitly
// so that the styles data can be obtained from the stringmap
const emptyStateValue = o.literal(EMPTY_ANIMATION_STATE);
const unitializedValue = o.importExpr(resolveIdentifier(Identifiers.UNINITIALIZED));
const unitializedValue = o.importExpr(createIdentifier(Identifiers.UNINITIALIZED));
const animationTransitionVar = o.variable('animationTransition_' + animationName);
updateStmts.push(
@ -121,23 +121,32 @@ export function triggerAnimation(
.set(animationFnExpr.callFn([view, renderElement, lastRenderValue, emptyStateValue]))
.toDeclStmt());
const registerStmts = [
const registerStmts: o.Statement[] = [];
const animationStartMethodExists = boundOutputs.find(
event => event.isAnimation && event.name == animationName && event.phase == 'start');
if (animationStartMethodExists) {
registerStmts.push(
animationTransitionVar
.callMethod(
'onStart',
[eventListener.callMethod(
o.BuiltinMethod.Bind,
[view, o.literal(BoundEventAst.calcFullName(animationName, null, 'start'))])])
.toStmt(),
.toStmt());
}
const animationDoneMethodExists = boundOutputs.find(
event => event.isAnimation && event.name == animationName && event.phase == 'done');
if (animationDoneMethodExists) {
registerStmts.push(
animationTransitionVar
.callMethod(
'onDone',
[eventListener.callMethod(
o.BuiltinMethod.Bind,
[view, o.literal(BoundEventAst.calcFullName(animationName, null, 'done'))])])
.toStmt(),
];
.toStmt());
}
updateStmts.push(...registerStmts);
detachStmts.push(...registerStmts);

View File

@ -9,7 +9,7 @@
import {ViewEncapsulation, isDevMode} from '@angular/core';
import {CompileIdentifierMetadata} from './compile_metadata';
import {Identifiers, resolveIdentifier} from './identifiers';
import {Identifiers, createIdentifier} from './identifiers';
function unimplemented(): any {
throw new Error('unimplemented');
@ -61,7 +61,7 @@ export abstract class RenderTypes {
}
export class DefaultRenderTypes implements RenderTypes {
get renderer() { return resolveIdentifier(Identifiers.Renderer); };
get renderer() { return createIdentifier(Identifiers.Renderer); };
renderText: any = null;
renderElement: any = null;
renderComment: any = null;

View File

@ -6,11 +6,12 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Component, Injectable, ViewEncapsulation} from '@angular/core';
import {Component, ViewEncapsulation} from '@angular/core';
import {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata, CompileTypeMetadata} from './compile_metadata';
import {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata} from './compile_metadata';
import {CompilerConfig} from './config';
import {isBlank, isPresent, stringify} from './facade/lang';
import {CompilerInjectable} from './injectable';
import * as html from './ml_parser/ast';
import {HtmlParser} from './ml_parser/html_parser';
import {InterpolationConfig} from './ml_parser/interpolation_config';
@ -18,7 +19,7 @@ import {ResourceLoader} from './resource_loader';
import {extractStyleUrls, isStyleUrlResolvable} from './style_url_resolver';
import {PreparsedElementType, preparseElement} from './template_parser/template_preparser';
import {UrlResolver} from './url_resolver';
import {SyncAsyncResult} from './util';
import {SyncAsyncResult, SyntaxError} from './util';
export interface PrenormalizedTemplateMetadata {
componentType: any;
@ -32,7 +33,7 @@ export interface PrenormalizedTemplateMetadata {
animations?: CompileAnimationEntryMetadata[];
}
@Injectable()
@CompilerInjectable()
export class DirectiveNormalizer {
private _resourceLoaderCache = new Map<string, Promise<string>>();
@ -70,7 +71,7 @@ export class DirectiveNormalizer {
} else if (prenormData.templateUrl) {
normalizedTemplateAsync = this.normalizeTemplateAsync(prenormData);
} else {
throw new Error(
throw new SyntaxError(
`No template specified for component ${stringify(prenormData.componentType)}`);
}
@ -104,7 +105,7 @@ export class DirectiveNormalizer {
template, stringify(prenomData.componentType), false, interpolationConfig);
if (rootNodesAndErrors.errors.length > 0) {
const errorString = rootNodesAndErrors.errors.join('\n');
throw new Error(`Template parse errors:\n${errorString}`);
throw new SyntaxError(`Template parse errors:\n${errorString}`);
}
const templateMetadataStyles = this.normalizeStylesheet(new CompileStylesheetMetadata({
styles: prenomData.styles,

View File

@ -6,10 +6,11 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Component, Directive, HostBinding, HostListener, Injectable, Input, Output, Query, Type, resolveForwardRef} from '@angular/core';
import {Component, Directive, HostBinding, HostListener, Input, Output, Query, Type, resolveForwardRef} from '@angular/core';
import {StringMapWrapper} from './facade/collection';
import {ListWrapper, StringMapWrapper} from './facade/collection';
import {stringify} from './facade/lang';
import {CompilerInjectable} from './injectable';
import {ReflectorReader, reflector} from './private_import_core';
import {splitAtColon} from './util';
@ -20,7 +21,7 @@ import {splitAtColon} from './util';
*
* See {@link Compiler}
*/
@Injectable()
@CompilerInjectable()
export class DirectiveResolver {
constructor(private _reflector: ReflectorReader = reflector) {}
@ -35,7 +36,7 @@ export class DirectiveResolver {
resolve(type: Type<any>, throwIfNotFound = true): Directive {
const typeMetadata = this._reflector.annotations(resolveForwardRef(type));
if (typeMetadata) {
const metadata = typeMetadata.find(isDirectiveMetadata);
const metadata = ListWrapper.findLast(typeMetadata, isDirectiveMetadata);
if (metadata) {
const propertyMetadata = this._reflector.propMetadata(type);
return this._mergeWithPropertyMetadata(metadata, propertyMetadata, type);
@ -58,23 +59,24 @@ export class DirectiveResolver {
const queries: {[key: string]: any} = {};
Object.keys(propertyMetadata).forEach((propName: string) => {
propertyMetadata[propName].forEach(a => {
if (a instanceof Input) {
if (a.bindingPropertyName) {
inputs.push(`${propName}: ${a.bindingPropertyName}`);
const input = ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof Input);
if (input) {
if (input.bindingPropertyName) {
inputs.push(`${propName}: ${input.bindingPropertyName}`);
} else {
inputs.push(propName);
}
} else if (a instanceof Output) {
const output: Output = a;
}
const output = ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof Output);
if (output) {
if (output.bindingPropertyName) {
outputs.push(`${propName}: ${output.bindingPropertyName}`);
} else {
outputs.push(propName);
}
} else if (a instanceof HostBinding) {
const hostBinding: HostBinding = a;
}
const hostBindings = propertyMetadata[propName].filter(a => a && a instanceof HostBinding);
hostBindings.forEach(hostBinding => {
if (hostBinding.hostPropertyName) {
const startWith = hostBinding.hostPropertyName[0];
if (startWith === '(') {
@ -87,56 +89,44 @@ export class DirectiveResolver {
} else {
host[`[${propName}]`] = propName;
}
} else if (a instanceof HostListener) {
const hostListener: HostListener = a;
});
const hostListeners = propertyMetadata[propName].filter(a => a && a instanceof HostListener);
hostListeners.forEach(hostListener => {
const args = hostListener.args || [];
host[`(${hostListener.eventName})`] = `${propName}(${args.join(',')})`;
} else if (a instanceof Query) {
queries[propName] = a;
}
});
const query = ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof Query);
if (query) {
queries[propName] = query;
}
});
return this._merge(dm, inputs, outputs, host, queries, directiveType);
}
private _extractPublicName(def: string) { return splitAtColon(def, [null, def])[1].trim(); }
private _dedupeBindings(bindings: string[]): string[] {
const names = new Set<string>();
const reversedResult: string[] = [];
// go last to first to allow later entries to overwrite previous entries
for (let i = bindings.length - 1; i >= 0; i--) {
const binding = bindings[i];
const name = this._extractPublicName(binding);
if (!names.has(name)) {
names.add(name);
reversedResult.push(binding);
}
}
return reversedResult.reverse();
}
private _merge(
directive: Directive, inputs: string[], outputs: string[], host: {[key: string]: string},
queries: {[key: string]: any}, directiveType: Type<any>): Directive {
const mergedInputs: string[] = inputs;
if (directive.inputs) {
const inputNames: string[] =
directive.inputs.map((def: string): string => this._extractPublicName(def));
inputs.forEach((inputDef: string) => {
const publicName = this._extractPublicName(inputDef);
if (inputNames.indexOf(publicName) > -1) {
throw new Error(
`Input '${publicName}' defined multiple times in '${stringify(directiveType)}'`);
}
});
mergedInputs.unshift(...directive.inputs);
}
const mergedOutputs: string[] = outputs;
if (directive.outputs) {
const outputNames: string[] =
directive.outputs.map((def: string): string => this._extractPublicName(def));
outputs.forEach((outputDef: string) => {
const publicName = this._extractPublicName(outputDef);
if (outputNames.indexOf(publicName) > -1) {
throw new Error(
`Output event '${publicName}' defined multiple times in '${stringify(directiveType)}'`);
}
});
mergedOutputs.unshift(...directive.outputs);
}
const mergedInputs =
this._dedupeBindings(directive.inputs ? directive.inputs.concat(inputs) : inputs);
const mergedOutputs =
this._dedupeBindings(directive.outputs ? directive.outputs.concat(outputs) : outputs);
const mergedHost = directive.host ? StringMapWrapper.merge(directive.host, host) : host;
const mergedQueries =
directive.queries ? StringMapWrapper.merge(directive.queries, queries) : queries;

Some files were not shown because too many files have changed in this diff Show More