Compare commits

..

104 Commits

Author SHA1 Message Date
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
242 changed files with 13393 additions and 7603 deletions

View File

@ -1,21 +1,65 @@
<a name="2.2.2"></a> <a name="2.3.0-rc.0"></a>
## [2.2.2](https://github.com/angular/angular/compare/2.2.1...2.2.2) (2016-11-22) # [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 ### 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) * **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)) ([33a7902](https://github.com/angular/angular/commit/33a7902)) * **benchmarks:** use sanitized style values ([#12943](https://github.com/angular/angular/issues/12943)) ([fc5ac1e](https://github.com/angular/angular/commit/fc5ac1e))
* **closure:** quote date pattern aliases ([#13012](https://github.com/angular/angular/issues/13012)) ([0956ace](https://github.com/angular/angular/commit/0956ace)) * **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)
* **compiler:** fix versions of `@angular/tsc-wrapped` ([2fe6fb1](https://github.com/angular/angular/commit/2fe6fb1)) * **changelog:** replace beta.1 with beta.0 ([#12961](https://github.com/angular/angular/issues/12961)) ([07a986d](https://github.com/angular/angular/commit/07a986d))
* **router:** add a banner file for the router ([#12919](https://github.com/angular/angular/issues/12919)) ([8df328b](https://github.com/angular/angular/commit/8df328b)) * **ci:** pin version of npm on CircleCI ([#12954](https://github.com/angular/angular/issues/12954)) ([a3884db](https://github.com/angular/angular/commit/a3884db))
* **router:** add a banner file for the router ([#12919](https://github.com/angular/angular/issues/12919)) ([511cd4d](https://github.com/angular/angular/commit/511cd4d)) * **closure:** quote date pattern aliases ([#13012](https://github.com/angular/angular/issues/13012)) ([7dcca30](https://github.com/angular/angular/commit/7dcca30))
* **router:** removes a peer dependency from router to upgrade ([115f18f](https://github.com/angular/angular/commit/115f18f)) * **common:** update DatePipe to allow closure compilation ([b2b7219](https://github.com/angular/angular/commit/b2b7219))
* **router:** removes a peer dependency from router to upgrade ([87d5d49](https://github.com/angular/angular/commit/87d5d49)) * **compiler:** correctly evaluate references to static functions ([#13133](https://github.com/angular/angular/issues/13133)) ([627282d](https://github.com/angular/angular/commit/627282d))
* **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) * **compiler:** fix performance regression caused by 5b0f9e2 ([43c0e9a](https://github.com/angular/angular/commit/43c0e9a)), closes [#13146](https://github.com/angular/angular/issues/13146)
* **upgrade:** call ng1 lifecycle hooks ([#12875](https://github.com/angular/angular/issues/12875)) ([462316b](https://github.com/angular/angular/commit/462316b)) * **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 ([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.3.0-beta.0"></a> <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) # [2.3.0-beta.0](https://github.com/angular/angular/compare/2.2.0...2.3.0-beta.0) (2016-11-17)

View File

@ -17,6 +17,7 @@ PACKAGES=(core
upgrade upgrade
router router
compiler-cli compiler-cli
language-service
benchpress) benchpress)
BUILD_ALL=true BUILD_ALL=true
BUNDLE=true BUNDLE=true
@ -175,7 +176,6 @@ do
mv ${UMD_ES5_PATH}.tmp ${UMD_ES5_PATH} mv ${UMD_ES5_PATH}.tmp ${UMD_ES5_PATH}
$UGLIFYJS -c --screw-ie8 --comments -o ${UMD_ES5_MIN_PATH} ${UMD_ES5_PATH} $UGLIFYJS -c --screw-ie8 --comments -o ${UMD_ES5_MIN_PATH} ${UMD_ES5_PATH}
if [[ -e rollup-testing.config.js ]]; then if [[ -e rollup-testing.config.js ]]; then
echo "====== Rollup ${PACKAGE} testing" echo "====== Rollup ${PACKAGE} testing"
../../../node_modules/.bin/rollup -c rollup-testing.config.js ../../../node_modules/.bin/rollup -c rollup-testing.config.js

View File

@ -52,6 +52,7 @@ module.exports = function(config) {
'dist/all/@angular/compiler-cli/**', 'dist/all/@angular/compiler-cli/**',
'dist/all/@angular/compiler/test/aot/**', 'dist/all/@angular/compiler/test/aot/**',
'dist/all/@angular/benchpress/**', 'dist/all/@angular/benchpress/**',
'dist/all/@angular/language-service/**',
'dist/all/angular1_router.js', 'dist/all/angular1_router.js',
'dist/all/@angular/platform-browser/testing/e2e_util.js', 'dist/all/@angular/platform-browser/testing/e2e_util.js',
'dist/examples/**/e2e_test/**', 'dist/examples/**/e2e_test/**',

View File

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

View File

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

View File

@ -14,6 +14,10 @@
export * from './src/location'; export * from './src/location';
export {NgLocalization} from './src/localization'; export {NgLocalization} from './src/localization';
export {CommonModule} from './src/common_module'; export {CommonModule} from './src/common_module';
export {NgClass, NgFor, NgIf, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet} from './src/directives/index'; 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'; export {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe} from './src/pipes/index';
import {Version} from '@angular/core';
/**
* @stable
*/
export const VERSION = new Version('0.0.0-PLACEHOLDER');

View File

@ -59,7 +59,7 @@ const _observableStrategy = new ObservableStrategy();
* {@example common/pipes/ts/async_pipe.ts region='AsyncPipePromise'} * {@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 * 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'} * {@example common/pipes/ts/async_pipe.ts region='AsyncPipeObservable'}
* *

View File

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

View File

@ -5,10 +5,14 @@
* Use of this source code is governed by an MIT-style license that can be * 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 * found in the LICENSE file at https://angular.io/license
*/ */
export {AotCompilerHost, AotCompilerHost as StaticReflectorHost, StaticReflector, StaticSymbol} from '@angular/compiler'; export {AotCompilerHost, AotCompilerHost as StaticReflectorHost, StaticReflector, StaticSymbol} from '@angular/compiler';
export {CodeGenerator} from './src/codegen'; export {CodeGenerator} from './src/codegen';
export {CompilerHost, CompilerHostContext, NodeCompilerHostContext} from './src/compiler_host'; export {CompilerHost, CompilerHostContext, NodeCompilerHostContext} from './src/compiler_host';
export {Extractor} from './src/extractor'; export {Extractor} from './src/extractor';
export * from '@angular/tsc-wrapped'; export * from '@angular/tsc-wrapped';
import {Version} from '@angular/core';
/**
* @stable
*/
export const VERSION = new Version('0.0.0-PLACEHOLDER');

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

@ -19,7 +19,7 @@ import {BasicComp} from './basic';
import {ComponentUsingThirdParty} from './comp_using_3rdp'; import {ComponentUsingThirdParty} from './comp_using_3rdp';
import {CompWithAnalyzeEntryComponentsProvider, CompWithEntryComponents} from './entry_components'; import {CompWithAnalyzeEntryComponentsProvider, CompWithEntryComponents} from './entry_components';
import {CompConsumingEvents, CompUsingPipes, CompWithProviders, CompWithReferences, DirPublishingEvents, ModuleUsingCustomElements} from './features'; 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 {CompWithNgContent, ProjectingComp} from './projection';
import {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, DirectiveForQuery} from './queries'; import {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, DirectiveForQuery} from './queries';
@ -52,7 +52,7 @@ import {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, Directive
FormsModule, FormsModule,
MdButtonModule, MdButtonModule,
ModuleUsingCustomElements, ModuleUsingCustomElements,
someLibModuleWithProviders(), SomeLibModule.withProviders(),
ThirdpartyModule, ThirdpartyModule,
], ],
providers: [SomeService], providers: [SomeService],

View File

@ -63,17 +63,13 @@ export function provideValueWithEntryComponents(value: any) {
entryComponents: [CompUsingLibModuleDirectiveAndPipe], entryComponents: [CompUsingLibModuleDirectiveAndPipe],
}) })
export class SomeLibModule { export class SomeLibModule {
} static withProviders() {
// 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 {
return { return {
ngModule: SomeLibModule, ngModule: SomeLibModule,
providers: [ providers: [
ServiceUsingLibModule, ServiceUsingLibModule, provideValueWithEntryComponents(
provideValueWithEntryComponents([{a: 'b', component: CompUsingLibModuleDirectiveAndPipe}]) [{a: 'b', component: CompUsingLibModuleDirectiveAndPipe}])
] ]
}; };
} }
}

View File

@ -34,9 +34,9 @@ const EXPECTED_XMB = `<?xml version="1.0" encoding="UTF-8" ?>
<!ELEMENT ex (#PCDATA)> <!ELEMENT ex (#PCDATA)>
]> ]>
<messagebundle> <messagebundle>
<msg id="63a85808f03b8181e36a952e0fa38202c2304862">other-3rdP-component</msg> <msg id="3772663375917578720">other-3rdP-component</msg>
<msg id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" desc="desc" meaning="meaning">translate me</msg> <msg id="8136548302122759730" desc="desc" meaning="meaning">translate me</msg>
<msg id="65cc4ab3b4c438e07c89be2b677d08369fb62da2">Welcome</msg> <msg id="3492007542396725315">Welcome</msg>
</messagebundle> </messagebundle>
`; `;
@ -79,5 +79,4 @@ describe('template i18n extraction output', () => {
const xlf = fs.readFileSync(xlfOutput, {encoding: 'utf-8'}); const xlf = fs.readFileSync(xlfOutput, {encoding: 'utf-8'});
expect(xlf).toEqual(EXPECTED_XLIFF); expect(xlf).toEqual(EXPECTED_XLIFF);
}); });
}); });

View File

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

View File

@ -44,7 +44,7 @@ export class CodeGenerator {
let root = this.options.basePath; let root = this.options.basePath;
for (const eachRootDir of this.options.rootDirs || []) { for (const eachRootDir of this.options.rootDirs || []) {
if (this.options.trace) { if (this.options.trace) {
console.log(`Check if ${filePath} is under rootDirs element ${eachRootDir}`); console.error(`Check if ${filePath} is under rootDirs element ${eachRootDir}`);
} }
if (path.relative(eachRootDir, filePath).indexOf('.') !== 0) { if (path.relative(eachRootDir, filePath).indexOf('.') !== 0) {
root = eachRootDir; root = eachRootDir;

View File

@ -186,7 +186,7 @@ export class CompilerHost implements AotCompilerHost {
if (!v2Metadata && v1Metadata) { if (!v2Metadata && v1Metadata) {
// patch up v1 to v2 by merging the metadata with metadata collected from the d.ts file // patch up v1 to v2 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 // as the only difference between the versions is whether all exports are contained in
// the metadata // the metadata and the `extends` clause.
v2Metadata = {'__symbolic': 'module', 'version': 2, 'metadata': {}}; v2Metadata = {'__symbolic': 'module', 'version': 2, 'metadata': {}};
if (v1Metadata.exports) { if (v1Metadata.exports) {
v2Metadata.exports = v1Metadata.exports; v2Metadata.exports = v1Metadata.exports;

View File

@ -41,9 +41,8 @@ function extract(
case 'xliff': case 'xliff':
case 'xlf': case 'xlf':
default: default:
const htmlParser = new compiler.I18NHtmlParser(new compiler.HtmlParser());
ext = 'xlf'; ext = 'xlf';
serializer = new compiler.Xliff(htmlParser, compiler.DEFAULT_INTERPOLATION_CONFIG); serializer = new compiler.Xliff();
break; break;
} }

View File

@ -56,7 +56,7 @@ export class PathMappedCompilerHost extends CompilerHost {
ts.resolveModuleName(m, rootedContainingFile, this.options, this.context).resolvedModule; ts.resolveModuleName(m, rootedContainingFile, this.options, this.context).resolvedModule;
if (resolved) { if (resolved) {
if (this.options.traceResolution) { if (this.options.traceResolution) {
console.log('resolve', m, containingFile, '=>', resolved.resolvedFileName); console.error('resolve', m, containingFile, '=>', resolved.resolvedFileName);
} }
return this.getCanonicalFileName(resolved.resolvedFileName); return this.getCanonicalFileName(resolved.resolvedFileName);
} }
@ -71,7 +71,7 @@ export class PathMappedCompilerHost extends CompilerHost {
*/ */
fileNameToModuleName(importedFile: string, containingFile: string): string { fileNameToModuleName(importedFile: string, containingFile: string): string {
if (this.options.traceResolution) { if (this.options.traceResolution) {
console.log( console.error(
'getImportPath from containingFile', containingFile, 'to importedFile', importedFile); 'getImportPath from containingFile', containingFile, 'to importedFile', importedFile);
} }

View File

@ -163,7 +163,11 @@ describe('CompilerHost', () => {
{__symbolic: 'module', version: 1, metadata: {foo: {__symbolic: 'class'}}}, { {__symbolic: 'module', version: 1, metadata: {foo: {__symbolic: 'class'}}}, {
__symbolic: 'module', __symbolic: 'module',
version: 2, version: 2,
metadata: {foo: {__symbolic: 'class'}, bar: {__symbolic: 'class'}} metadata: {
foo: {__symbolic: 'class'},
Bar: {__symbolic: 'class', members: {ngOnInit: [{__symbolic: 'method'}]}},
BarChild: {__symbolic: 'class', extends: {__symbolic: 'reference', name: 'Bar'}}
}
} }
]); ]);
}); });
@ -198,7 +202,12 @@ const FILES: Entry = {
} }
}, },
'metadata_versions': { 'metadata_versions': {
'v1.d.ts': 'export declare class bar {}', 'v1.d.ts': `
export declare class Bar {
ngOnInit() {}
}
export declare class BarChild extends Bar {}
`,
'v1.metadata.json': 'v1.metadata.json':
`{"__symbolic":"module", "version": 1, "metadata": {"foo": {"__symbolic": "class"}}}`, `{"__symbolic":"module", "version": 1, "metadata": {"foo": {"__symbolic": "class"}}}`,
} }

View File

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

View File

@ -21,6 +21,11 @@
* </p> * </p>
* </div> * </div>
*/ */
import {Version} from '@angular/core';
/**
* @stable
*/
export const VERSION = new Version('0.0.0-PLACEHOLDER');
export * from './src/template_parser/template_ast'; export * from './src/template_parser/template_ast';
export {TEMPLATE_TRANSFORMS} from './src/template_parser/template_parser'; export {TEMPLATE_TRANSFORMS} from './src/template_parser/template_parser';
export {CompilerConfig, RenderTypes} from './src/config'; export {CompilerConfig, RenderTypes} from './src/config';
@ -58,5 +63,4 @@ export * from './src/style_compiler';
export * from './src/template_parser/template_parser'; export * from './src/template_parser/template_parser';
export {ViewCompiler} from './src/view_compiler/view_compiler'; export {ViewCompiler} from './src/view_compiler/view_compiler';
export {AnimationParser} from './src/animation/animation_parser'; export {AnimationParser} from './src/animation/animation_parser';
// This file only reexports content of the `src` folder. Keep it that way. // 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 {isPresent} from '../facade/lang';
import {Identifiers, resolveIdentifier} from '../identifiers'; import {Identifiers, createIdentifier} from '../identifiers';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';
import {ANY_STATE, DEFAULT_STATE, EMPTY_STATE} from '../private_import_core'; import {ANY_STATE, DEFAULT_STATE, EMPTY_STATE} from '../private_import_core';
@ -69,8 +69,8 @@ class _AnimationBuilder implements AnimationAstVisitor {
stylesArr.push(o.literalMap(entries)); stylesArr.push(o.literalMap(entries));
}); });
return o.importExpr(resolveIdentifier(Identifiers.AnimationStyles)).instantiate([ return o.importExpr(createIdentifier(Identifiers.AnimationStyles)).instantiate([
o.importExpr(resolveIdentifier(Identifiers.collectAndResolveStyles)).callFn([ o.importExpr(createIdentifier(Identifiers.collectAndResolveStyles)).callFn([
_ANIMATION_COLLECTED_STYLES, o.literalArr(stylesArr) _ANIMATION_COLLECTED_STYLES, o.literalArr(stylesArr)
]) ])
]); ]);
@ -78,7 +78,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
visitAnimationKeyframe(ast: AnimationKeyframeAst, context: _AnimationBuilderContext): visitAnimationKeyframe(ast: AnimationKeyframeAst, context: _AnimationBuilderContext):
o.Expression { 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) o.literal(ast.offset), ast.styles.visit(this, context)
]); ]);
} }
@ -100,7 +100,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
const startingStylesExpr = ast.startingStyles.visit(this, context); const startingStylesExpr = ast.startingStyles.visit(this, context);
const keyframeExpressions = ast.keyframes.map(keyframe => keyframe.visit(this, context)); const keyframeExpressions = ast.keyframes.map(keyframe => keyframe.visit(this, context));
const keyframesExpr = const keyframesExpr =
o.importExpr(resolveIdentifier(Identifiers.balanceAnimationKeyframes)).callFn([ o.importExpr(createIdentifier(Identifiers.balanceAnimationKeyframes)).callFn([
_ANIMATION_COLLECTED_STYLES, _ANIMATION_END_STATE_STYLES_VAR, _ANIMATION_COLLECTED_STYLES, _ANIMATION_END_STATE_STYLES_VAR,
o.literalArr(keyframeExpressions) o.literalArr(keyframeExpressions)
]); ]);
@ -127,14 +127,14 @@ class _AnimationBuilder implements AnimationAstVisitor {
visitAnimationSequence(ast: AnimationSequenceAst, context: _AnimationBuilderContext): visitAnimationSequence(ast: AnimationSequenceAst, context: _AnimationBuilderContext):
o.Expression { o.Expression {
const playerExprs = ast.steps.map(step => step.visit(this, context)); 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) o.literalArr(playerExprs)
]); ]);
} }
visitAnimationGroup(ast: AnimationGroupAst, context: _AnimationBuilderContext): o.Expression { visitAnimationGroup(ast: AnimationGroupAst, context: _AnimationBuilderContext): o.Expression {
const playerExprs = ast.steps.map(step => step.visit(this, context)); 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) o.literalArr(playerExprs)
]); ]);
} }
@ -228,7 +228,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
_ANIMATION_END_STATE_STYLES_VAR.equals(o.NULL_EXPR), _ANIMATION_END_STATE_STYLES_VAR.equals(o.NULL_EXPR),
[_ANIMATION_END_STATE_STYLES_VAR.set(_ANIMATION_DEFAULT_STATE_VAR).toStmt()])); [_ANIMATION_END_STATE_STYLES_VAR.set(_ANIMATION_DEFAULT_STATE_VAR).toStmt()]));
const RENDER_STYLES_FN = o.importExpr(resolveIdentifier(Identifiers.renderStyles)); const RENDER_STYLES_FN = o.importExpr(createIdentifier(Identifiers.renderStyles));
ast.stateTransitions.forEach(transAst => statements.push(transAst.visit(this, context))); ast.stateTransitions.forEach(transAst => statements.push(transAst.visit(this, context)));
@ -237,7 +237,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
statements.push(new o.IfStmt( statements.push(new o.IfStmt(
_ANIMATION_PLAYER_VAR.equals(o.NULL_EXPR), _ANIMATION_PLAYER_VAR.equals(o.NULL_EXPR),
[_ANIMATION_PLAYER_VAR [_ANIMATION_PLAYER_VAR
.set(o.importExpr(resolveIdentifier(Identifiers.NoOpAnimationPlayer)).instantiate([])) .set(o.importExpr(createIdentifier(Identifiers.NoOpAnimationPlayer)).instantiate([]))
.toStmt()])); .toStmt()]));
// once complete we want to apply the styles on the element // once complete we want to apply the styles on the element
@ -255,8 +255,9 @@ class _AnimationBuilder implements AnimationAstVisitor {
.callFn([ .callFn([
_ANIMATION_FACTORY_ELEMENT_VAR, _ANIMATION_FACTORY_RENDERER_VAR, _ANIMATION_FACTORY_ELEMENT_VAR, _ANIMATION_FACTORY_RENDERER_VAR,
o.importExpr( o.importExpr(
resolveIdentifier(Identifiers.prepareFinalAnimationStyles)) createIdentifier(Identifiers.prepareFinalAnimationStyles))
.callFn([ .callFn(
[
_ANIMATION_START_STATE_STYLES_VAR, _ANIMATION_START_STATE_STYLES_VAR,
_ANIMATION_END_STATE_STYLES_VAR _ANIMATION_END_STATE_STYLES_VAR
]) ])
@ -265,7 +266,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
])]) ])])
.toStmt()); .toStmt());
statements.push(o.importExpr(resolveIdentifier(Identifiers.AnimationSequencePlayer)) statements.push(o.importExpr(createIdentifier(Identifiers.AnimationSequencePlayer))
.instantiate([_PREVIOUS_ANIMATION_PLAYERS]) .instantiate([_PREVIOUS_ANIMATION_PLAYERS])
.callMethod('destroy', []) .callMethod('destroy', [])
.toStmt()); .toStmt());
@ -276,7 +277,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
statements.push(RENDER_STYLES_FN statements.push(RENDER_STYLES_FN
.callFn([ .callFn([
_ANIMATION_FACTORY_ELEMENT_VAR, _ANIMATION_FACTORY_RENDERER_VAR, _ANIMATION_FACTORY_ELEMENT_VAR, _ANIMATION_FACTORY_RENDERER_VAR,
o.importExpr(resolveIdentifier(Identifiers.clearStyles)) o.importExpr(createIdentifier(Identifiers.clearStyles))
.callFn([_ANIMATION_START_STATE_STYLES_VAR]) .callFn([_ANIMATION_START_STATE_STYLES_VAR])
]) ])
.toStmt()); .toStmt());
@ -291,7 +292,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
.toStmt()); .toStmt());
statements.push(new o.ReturnStatement( 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_PLAYER_VAR, _ANIMATION_CURRENT_STATE_VAR, _ANIMATION_NEXT_STATE_VAR,
_ANIMATION_TIME_VAR _ANIMATION_TIME_VAR
]))); ])));
@ -300,12 +301,12 @@ class _AnimationBuilder implements AnimationAstVisitor {
[ [
new o.FnParam( new o.FnParam(
_ANIMATION_FACTORY_VIEW_VAR.name, _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_FACTORY_ELEMENT_VAR.name, o.DYNAMIC_TYPE),
new o.FnParam(_ANIMATION_CURRENT_STATE_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) 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 { build(ast: AnimationAst): AnimationEntryCompileResult {

View File

@ -8,7 +8,7 @@
import {Injectable} from '@angular/core'; 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 {StringMapWrapper} from '../facade/collection';
import {isBlank, isPresent} from '../facade/lang'; import {isBlank, isPresent} from '../facade/lang';
import {ParseError} from '../parse_util'; import {ParseError} from '../parse_util';
@ -41,7 +41,7 @@ export class AnimationParser {
parseComponent(component: CompileDirectiveMetadata): AnimationEntryAst[] { parseComponent(component: CompileDirectiveMetadata): AnimationEntryAst[] {
const errors: string[] = []; const errors: string[] = [];
const componentName = component.type.name; const componentName = identifierName(component.type);
const animationTriggerNames = new Set<string>(); const animationTriggerNames = new Set<string>();
const asts = component.template.animations.map(entry => { const asts = component.template.animations.map(entry => {
const result = this.parseEntry(entry); const result = this.parseEntry(entry);

View File

@ -10,11 +10,11 @@ import {SchemaMetadata} from '@angular/core';
import {AnimationCompiler} from '../animation/animation_compiler'; import {AnimationCompiler} from '../animation/animation_compiler';
import {AnimationParser} from '../animation/animation_parser'; import {AnimationParser} from '../animation/animation_parser';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, createHostComponentMeta} from '../compile_metadata'; import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, createHostComponentMeta, identifierModuleUrl, identifierName} from '../compile_metadata';
import {DirectiveNormalizer} from '../directive_normalizer'; import {DirectiveNormalizer} from '../directive_normalizer';
import {DirectiveWrapperCompileResult, DirectiveWrapperCompiler} from '../directive_wrapper_compiler'; import {DirectiveWrapperCompileResult, DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
import {ListWrapper} from '../facade/collection'; import {ListWrapper} from '../facade/collection';
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from '../identifiers'; import {Identifiers, createIdentifier, createIdentifierToken} from '../identifiers';
import {CompileMetadataResolver} from '../metadata_resolver'; import {CompileMetadataResolver} from '../metadata_resolver';
import {NgModuleCompiler} from '../ng_module_compiler'; import {NgModuleCompiler} from '../ng_module_compiler';
import {OutputEmitter} from '../output/abstract_emitter'; import {OutputEmitter} from '../output/abstract_emitter';
@ -82,7 +82,7 @@ export class AotCompiler {
const ngModule = ngModuleByPipeOrDirective.get(dirType); const ngModule = ngModuleByPipeOrDirective.get(dirType);
if (!ngModule) { if (!ngModule) {
throw new Error( 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); _assertComponent(compMeta);
@ -113,24 +113,24 @@ export class AotCompiler {
const providers: CompileProviderMetadata[] = []; const providers: CompileProviderMetadata[] = [];
if (this._localeId) { if (this._localeId) {
providers.push(new CompileProviderMetadata({ providers.push({
token: resolveIdentifierToken(Identifiers.LOCALE_ID), token: createIdentifierToken(Identifiers.LOCALE_ID),
useValue: this._localeId, useValue: this._localeId,
})); });
} }
if (this._translationFormat) { if (this._translationFormat) {
providers.push(new CompileProviderMetadata({ providers.push({
token: resolveIdentifierToken(Identifiers.TRANSLATIONS_FORMAT), token: createIdentifierToken(Identifiers.TRANSLATIONS_FORMAT),
useValue: this._translationFormat useValue: this._translationFormat
})); });
} }
const appCompileResult = this._ngModuleCompiler.compile(ngModule, providers); const appCompileResult = this._ngModuleCompiler.compile(ngModule, providers);
appCompileResult.dependencies.forEach((dep) => { appCompileResult.dependencies.forEach((dep) => {
dep.placeholder.name = _componentFactoryName(dep.comp); dep.placeholder.reference = this._staticReflector.getStaticSymbol(
dep.placeholder.moduleUrl = _ngfactoryModuleUrl(dep.comp.moduleUrl); _ngfactoryModuleUrl(identifierModuleUrl(dep.comp)), _componentFactoryName(dep.comp));
}); });
targetStatements.push(...appCompileResult.statements); targetStatements.push(...appCompileResult.statements);
@ -149,14 +149,17 @@ export class AotCompiler {
private _compileComponentFactory( private _compileComponentFactory(
compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata, fileSuffix: string, compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata, fileSuffix: string,
targetStatements: o.Statement[]): string { targetStatements: o.Statement[]): string {
const hostMeta = createHostComponentMeta(compMeta); const hostMeta = createHostComponentMeta(
this._staticReflector.getStaticSymbol(
identifierModuleUrl(compMeta.type), `${identifierName(compMeta.type)}_Host`),
compMeta);
const hostViewFactoryVar = this._compileComponent( const hostViewFactoryVar = this._compileComponent(
hostMeta, ngModule, [compMeta.type], null, fileSuffix, targetStatements); hostMeta, ngModule, [compMeta.type], null, fileSuffix, targetStatements);
const compFactoryVar = _componentFactoryName(compMeta.type); const compFactoryVar = _componentFactoryName(compMeta.type);
targetStatements.push( targetStatements.push(
o.variable(compFactoryVar) o.variable(compFactoryVar)
.set(o.importExpr(resolveIdentifier(Identifiers.ComponentFactory), [o.importType( .set(o.importExpr(
compMeta.type)]) createIdentifier(Identifiers.ComponentFactory), [o.importType(compMeta.type)])
.instantiate( .instantiate(
[ [
o.literal(compMeta.selector), o.literal(compMeta.selector),
@ -164,7 +167,7 @@ export class AotCompiler {
o.importExpr(compMeta.type), o.importExpr(compMeta.type),
], ],
o.importType( o.importType(
resolveIdentifier(Identifiers.ComponentFactory), createIdentifier(Identifiers.ComponentFactory),
[o.importType(compMeta.type)], [o.TypeModifier.Const]))) [o.importType(compMeta.type)], [o.TypeModifier.Const])))
.toDeclStmt(null, [o.StmtModifier.Final])); .toDeclStmt(null, [o.StmtModifier.Final]));
return compFactoryVar; return compFactoryVar;
@ -182,23 +185,24 @@ export class AotCompiler {
const parsedTemplate = this._templateParser.parse( const parsedTemplate = this._templateParser.parse(
compMeta, compMeta.template.template, directives, pipes, ngModule.schemas, compMeta, compMeta.template.template, directives, pipes, ngModule.schemas,
compMeta.type.name); identifierName(compMeta.type));
const stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]); const stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]);
const compiledAnimations = const compiledAnimations =
this._animationCompiler.compile(compMeta.type.name, parsedAnimations); this._animationCompiler.compile(identifierName(compMeta.type), parsedAnimations);
const viewResult = this._viewCompiler.compileComponent( const viewResult = this._viewCompiler.compileComponent(
compMeta, parsedTemplate, stylesExpr, pipes, compiledAnimations); compMeta, parsedTemplate, stylesExpr, pipes, compiledAnimations);
if (componentStyles) { if (componentStyles) {
targetStatements.push(..._resolveStyleStatements(componentStyles, fileSuffix)); targetStatements.push(
..._resolveStyleStatements(this._staticReflector, componentStyles, fileSuffix));
} }
compiledAnimations.forEach(entry => targetStatements.push(...entry.statements)); compiledAnimations.forEach(entry => targetStatements.push(...entry.statements));
targetStatements.push(..._resolveViewStatements(viewResult)); targetStatements.push(..._resolveViewStatements(this._staticReflector, viewResult));
return viewResult.viewClassVar; return viewResult.viewClassVar;
} }
private _codgenStyles( private _codgenStyles(
fileUrl: string, stylesCompileResult: CompiledStylesheet, fileSuffix: string): SourceModule { fileUrl: string, stylesCompileResult: CompiledStylesheet, fileSuffix: string): SourceModule {
_resolveStyleStatements(stylesCompileResult, fileSuffix); _resolveStyleStatements(this._staticReflector, stylesCompileResult, fileSuffix);
return this._codegenSourceModule( return this._codegenSourceModule(
fileUrl, _stylesModuleUrl( fileUrl, _stylesModuleUrl(
stylesCompileResult.meta.moduleUrl, stylesCompileResult.isShimmed, fileSuffix), stylesCompileResult.meta.moduleUrl, stylesCompileResult.isShimmed, fileSuffix),
@ -214,18 +218,21 @@ export class AotCompiler {
} }
} }
function _resolveViewStatements(compileResult: ViewCompileResult): o.Statement[] { function _resolveViewStatements(
reflector: StaticReflector, compileResult: ViewCompileResult): o.Statement[] {
compileResult.dependencies.forEach((dep) => { compileResult.dependencies.forEach((dep) => {
if (dep instanceof ViewClassDependency) { if (dep instanceof ViewClassDependency) {
const vfd = <ViewClassDependency>dep; 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) { } else if (dep instanceof ComponentFactoryDependency) {
const cfd = <ComponentFactoryDependency>dep; const cfd = <ComponentFactoryDependency>dep;
cfd.placeholder.name = _componentFactoryName(cfd.comp); cfd.placeholder.reference = reflector.getStaticSymbol(
cfd.placeholder.moduleUrl = _ngfactoryModuleUrl(cfd.comp.moduleUrl); _ngfactoryModuleUrl(identifierModuleUrl(cfd.comp)), _componentFactoryName(cfd.comp));
} else if (dep instanceof DirectiveWrapperDependency) { } else if (dep instanceof DirectiveWrapperDependency) {
const dwd = <DirectiveWrapperDependency>dep; 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; return compileResult.statements;
@ -233,9 +240,11 @@ function _resolveViewStatements(compileResult: ViewCompileResult): o.Statement[]
function _resolveStyleStatements( function _resolveStyleStatements(
compileResult: CompiledStylesheet, fileSuffix: string): o.Statement[] { reflector: StaticReflector, compileResult: CompiledStylesheet,
fileSuffix: string): o.Statement[] {
compileResult.dependencies.forEach((dep) => { 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; return compileResult.statements;
} }
@ -246,7 +255,7 @@ function _ngfactoryModuleUrl(dirUrl: string): string {
} }
function _componentFactoryName(comp: CompileIdentifierMetadata): string { function _componentFactoryName(comp: CompileIdentifierMetadata): string {
return `${comp.name}NgFactory`; return `${identifierName(comp)}NgFactory`;
} }
function _stylesModuleUrl(stylesheetUrl: string, shim: boolean, suffix: string): string { function _stylesModuleUrl(stylesheetUrl: string, shim: boolean, suffix: string): string {
@ -255,7 +264,8 @@ function _stylesModuleUrl(stylesheetUrl: string, shim: boolean, suffix: string):
function _assertComponent(meta: CompileDirectiveMetadata) { function _assertComponent(meta: CompileDirectiveMetadata) {
if (!meta.isComponent) { 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.`);
} }
} }
@ -369,7 +379,7 @@ export function extractProgramSymbols(
files.filter(fileName => _filterFileByPatterns(fileName, options)).forEach(sourceFile => { files.filter(fileName => _filterFileByPatterns(fileName, options)).forEach(sourceFile => {
const moduleMetadata = staticReflector.getModuleMetadata(sourceFile); const moduleMetadata = staticReflector.getModuleMetadata(sourceFile);
if (!moduleMetadata) { if (!moduleMetadata) {
console.log(`WARNING: no metadata found for ${sourceFile}`); console.error(`WARNING: no metadata found for ${sourceFile}`);
return; return;
} }

View File

@ -70,16 +70,24 @@ export class StaticSymbolCache {
export class StaticReflector implements ReflectorReader { export class StaticReflector implements ReflectorReader {
private declarationCache = new Map<string, StaticSymbol>(); private declarationCache = new Map<string, StaticSymbol>();
private annotationCache = new Map<StaticSymbol, any[]>(); private annotationCache = new Map<StaticSymbol, any[]>();
private propertyCache = new Map<StaticSymbol, {[key: string]: any}>(); private propertyCache = new Map<StaticSymbol, {[key: string]: any[]}>();
private parameterCache = new Map<StaticSymbol, any[]>(); private parameterCache = new Map<StaticSymbol, any[]>();
private methodCache = new Map<StaticSymbol, {[key: string]: boolean}>();
private metadataCache = new Map<string, {[key: string]: any}>(); private metadataCache = new Map<string, {[key: string]: any}>();
private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>(); private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>();
private opaqueToken: StaticSymbol; private opaqueToken: StaticSymbol;
constructor( constructor(
private host: StaticReflectorHost, private host: StaticReflectorHost,
private staticSymbolCache: StaticSymbolCache = new StaticSymbolCache()) { private staticSymbolCache: StaticSymbolCache = new StaticSymbolCache(),
knownMetadataClasses: {name: string, filePath: string, ctor: any}[] = [],
knownMetadataFunctions: {name: string, filePath: string, fn: any}[] = []) {
this.initializeConversionMap(); 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 { importUri(typeOrFunc: StaticSymbol): string {
@ -99,29 +107,45 @@ export class StaticReflector implements ReflectorReader {
public annotations(type: StaticSymbol): any[] { public annotations(type: StaticSymbol): any[] {
let annotations = this.annotationCache.get(type); let annotations = this.annotationCache.get(type);
if (!annotations) { if (!annotations) {
const classMetadata = this.getTypeMetadata(type);
if (classMetadata['decorators']) {
annotations = this.simplify(type, classMetadata['decorators']);
} else {
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)); this.annotationCache.set(type, annotations.filter(ann => !!ann));
} }
return annotations; return annotations;
} }
public propMetadata(type: StaticSymbol): {[key: string]: any} { public propMetadata(type: StaticSymbol): {[key: string]: any[]} {
let propMetadata = this.propertyCache.get(type); let propMetadata = this.propertyCache.get(type);
if (!propMetadata) { if (!propMetadata) {
const classMetadata = this.getTypeMetadata(type); const classMetadata = this.getTypeMetadata(type) || {};
const members = classMetadata ? classMetadata['members'] : {}; propMetadata = {};
propMetadata = mapStringMap(members, (propData, propName) => { 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) const prop = (<any[]>propData)
.find(a => a['__symbolic'] == 'property' || a['__symbolic'] == 'method'); .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']) { if (prop && prop['decorators']) {
return this.simplify(type, prop['decorators']); decorators.push(...this.simplify(type, prop['decorators']));
} else {
return [];
} }
}); });
this.propertyCache.set(type, propMetadata); this.propertyCache.set(type, propMetadata);
@ -155,6 +179,8 @@ export class StaticReflector implements ReflectorReader {
} }
parameters.push(nestedResult); parameters.push(nestedResult);
}); });
} else if (classMetadata['extends']) {
parameters = this.parameters(this.simplify(type, classMetadata['extends']));
} }
if (!parameters) { if (!parameters) {
parameters = []; parameters = [];
@ -163,28 +189,52 @@ export class StaticReflector implements ReflectorReader {
} }
return parameters; return parameters;
} catch (e) { } catch (e) {
console.log(`Failed on type ${JSON.stringify(type)} with error ${e}`); console.error(`Failed on type ${JSON.stringify(type)} with error ${e}`);
throw 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 { hasLifecycleHook(type: any, lcProperty: string): boolean {
if (!(type instanceof StaticSymbol)) { if (!(type instanceof StaticSymbol)) {
throw new Error( throw new Error(
`hasLifecycleHook received ${JSON.stringify(type)} which is not a StaticSymbol`); `hasLifecycleHook received ${JSON.stringify(type)} which is not a StaticSymbol`);
} }
const classMetadata = this.getTypeMetadata(type); try {
const members = classMetadata ? classMetadata['members'] : null; return !!this._methodNames(type)[lcProperty];
const member: any[] = } catch (e) {
members && members.hasOwnProperty(lcProperty) ? members[lcProperty] : null; console.error(`Failed on type ${JSON.stringify(type)} with error ${e}`);
return member ? member.some(a => a['__symbolic'] == 'method') : false; throw e;
}
} }
private registerDecoratorOrConstructor(type: StaticSymbol, ctor: any): void { private _registerDecoratorOrConstructor(type: StaticSymbol, ctor: any): void {
this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => new ctor(...args)); this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => new ctor(...args));
} }
private registerFunction(type: StaticSymbol, fn: any): void { private _registerFunction(type: StaticSymbol, fn: any): void {
this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => fn.apply(undefined, args)); this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => fn.apply(undefined, args));
} }
@ -193,50 +243,51 @@ export class StaticReflector implements ReflectorReader {
ANGULAR_IMPORT_LOCATIONS; ANGULAR_IMPORT_LOCATIONS;
this.opaqueToken = this.findDeclaration(diOpaqueToken, 'OpaqueToken'); this.opaqueToken = this.findDeclaration(diOpaqueToken, 'OpaqueToken');
this.registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Host'), Host); this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Host'), Host);
this.registerDecoratorOrConstructor( this._registerDecoratorOrConstructor(
this.findDeclaration(diDecorators, 'Injectable'), Injectable); this.findDeclaration(diDecorators, 'Injectable'), Injectable);
this.registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Self'), Self); this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Self'), Self);
this.registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'SkipSelf'), SkipSelf); this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'SkipSelf'), SkipSelf);
this.registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Inject'), Inject); this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Inject'), Inject);
this.registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Optional'), Optional); this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Optional'), Optional);
this.registerDecoratorOrConstructor( this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'Attribute'), Attribute); this.findDeclaration(coreDecorators, 'Attribute'), Attribute);
this.registerDecoratorOrConstructor( this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'ContentChild'), ContentChild); this.findDeclaration(coreDecorators, 'ContentChild'), ContentChild);
this.registerDecoratorOrConstructor( this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'ContentChildren'), ContentChildren); this.findDeclaration(coreDecorators, 'ContentChildren'), ContentChildren);
this.registerDecoratorOrConstructor( this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'ViewChild'), ViewChild); this.findDeclaration(coreDecorators, 'ViewChild'), ViewChild);
this.registerDecoratorOrConstructor( this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'ViewChildren'), ViewChildren); this.findDeclaration(coreDecorators, 'ViewChildren'), ViewChildren);
this.registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Input'), Input); this._registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Input'), Input);
this.registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Output'), Output); this._registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Output'), Output);
this.registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Pipe'), Pipe); this._registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Pipe'), Pipe);
this.registerDecoratorOrConstructor( this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'HostBinding'), HostBinding); this.findDeclaration(coreDecorators, 'HostBinding'), HostBinding);
this.registerDecoratorOrConstructor( this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'HostListener'), HostListener); this.findDeclaration(coreDecorators, 'HostListener'), HostListener);
this.registerDecoratorOrConstructor( this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'Directive'), Directive); this.findDeclaration(coreDecorators, 'Directive'), Directive);
this.registerDecoratorOrConstructor( this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'Component'), Component); this.findDeclaration(coreDecorators, 'Component'), Component);
this.registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'NgModule'), NgModule); this._registerDecoratorOrConstructor(
this.findDeclaration(coreDecorators, 'NgModule'), NgModule);
// Note: Some metadata classes can be used directly with Provider.deps. // Note: Some metadata classes can be used directly with Provider.deps.
this.registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Host'), Host); this._registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Host'), Host);
this.registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Self'), Self); this._registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Self'), Self);
this.registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'SkipSelf'), SkipSelf); this._registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'SkipSelf'), SkipSelf);
this.registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Optional'), Optional); this._registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Optional'), Optional);
this.registerFunction(this.findDeclaration(animationMetadata, 'trigger'), trigger); this._registerFunction(this.findDeclaration(animationMetadata, 'trigger'), trigger);
this.registerFunction(this.findDeclaration(animationMetadata, 'state'), state); this._registerFunction(this.findDeclaration(animationMetadata, 'state'), state);
this.registerFunction(this.findDeclaration(animationMetadata, 'transition'), transition); this._registerFunction(this.findDeclaration(animationMetadata, 'transition'), transition);
this.registerFunction(this.findDeclaration(animationMetadata, 'style'), style); this._registerFunction(this.findDeclaration(animationMetadata, 'style'), style);
this.registerFunction(this.findDeclaration(animationMetadata, 'animate'), animate); this._registerFunction(this.findDeclaration(animationMetadata, 'animate'), animate);
this.registerFunction(this.findDeclaration(animationMetadata, 'keyframes'), keyframes); this._registerFunction(this.findDeclaration(animationMetadata, 'keyframes'), keyframes);
this.registerFunction(this.findDeclaration(animationMetadata, 'sequence'), sequence); this._registerFunction(this.findDeclaration(animationMetadata, 'sequence'), sequence);
this.registerFunction(this.findDeclaration(animationMetadata, 'group'), group); this._registerFunction(this.findDeclaration(animationMetadata, 'group'), group);
} }
/** /**
@ -333,7 +384,7 @@ export class StaticReflector implements ReflectorReader {
/** @internal */ /** @internal */
public simplify(context: StaticSymbol, value: any): any { public simplify(context: StaticSymbol, value: any): any {
const _this = this; const self = this;
let scope = BindingScope.empty; let scope = BindingScope.empty;
const calling = new Map<StaticSymbol, boolean>(); const calling = new Map<StaticSymbol, boolean>();
@ -342,15 +393,15 @@ export class StaticReflector implements ReflectorReader {
let staticSymbol: StaticSymbol; let staticSymbol: StaticSymbol;
if (expression['module']) { if (expression['module']) {
staticSymbol = staticSymbol =
_this.findDeclaration(expression['module'], expression['name'], context.filePath); self.findDeclaration(expression['module'], expression['name'], context.filePath);
} else { } else {
staticSymbol = _this.getStaticSymbol(context.filePath, expression['name']); staticSymbol = self.getStaticSymbol(context.filePath, expression['name']);
} }
return staticSymbol; return staticSymbol;
} }
function resolveReferenceValue(staticSymbol: StaticSymbol): any { function resolveReferenceValue(staticSymbol: StaticSymbol): any {
const moduleMetadata = _this.getModuleMetadata(staticSymbol.filePath); const moduleMetadata = self.getModuleMetadata(staticSymbol.filePath);
const declarationValue = const declarationValue =
moduleMetadata ? moduleMetadata['metadata'][staticSymbol.name] : null; moduleMetadata ? moduleMetadata['metadata'][staticSymbol.name] : null;
return declarationValue; return declarationValue;
@ -360,7 +411,7 @@ export class StaticReflector implements ReflectorReader {
if (value && value.__symbolic === 'new' && value.expression) { if (value && value.__symbolic === 'new' && value.expression) {
const target = value.expression; const target = value.expression;
if (target.__symbolic == 'reference') { if (target.__symbolic == 'reference') {
return sameSymbol(resolveReference(context, target), _this.opaqueToken); return sameSymbol(resolveReference(context, target), self.opaqueToken);
} }
} }
return false; return false;
@ -542,22 +593,26 @@ export class StaticReflector implements ReflectorReader {
if (indexTarget && isPrimitive(index)) return indexTarget[index]; if (indexTarget && isPrimitive(index)) return indexTarget[index];
return null; return null;
case 'select': case 'select':
let selectContext = context;
let selectTarget = simplify(expression['expression']); let selectTarget = simplify(expression['expression']);
if (selectTarget instanceof StaticSymbol) { if (selectTarget instanceof StaticSymbol) {
// Access to a static instance variable // Access to a static instance variable
const declarationValue = resolveReferenceValue(selectTarget);
if (declarationValue && declarationValue.statics) {
selectTarget = declarationValue.statics;
} else {
const member: string = expression['member']; const member: string = expression['member'];
const members = selectTarget.members ? const members = selectTarget.members ?
(selectTarget.members as string[]).concat(member) : (selectTarget.members as string[]).concat(member) :
[member]; [member];
return _this.getStaticSymbol(selectTarget.filePath, selectTarget.name, members); const declarationValue = resolveReferenceValue(selectTarget);
selectContext =
self.getStaticSymbol(selectTarget.filePath, selectTarget.name, members);
if (declarationValue && declarationValue.statics) {
selectTarget = declarationValue.statics;
} else {
return selectContext;
} }
} }
const member = simplify(expression['member']); const member = simplifyInContext(selectContext, expression['member'], depth + 1);
if (selectTarget && isPrimitive(member)) return simplify(selectTarget[member]); if (selectTarget && isPrimitive(member))
return simplifyInContext(selectContext, selectTarget[member], depth + 1);
return null; return null;
case 'reference': case 'reference':
if (!expression.module) { if (!expression.module) {
@ -589,11 +644,11 @@ export class StaticReflector implements ReflectorReader {
let target = expression['expression']; let target = expression['expression'];
if (target['module']) { if (target['module']) {
staticSymbol = staticSymbol =
_this.findDeclaration(target['module'], target['name'], context.filePath); self.findDeclaration(target['module'], target['name'], context.filePath);
} else { } else {
staticSymbol = _this.getStaticSymbol(context.filePath, target['name']); staticSymbol = self.getStaticSymbol(context.filePath, target['name']);
} }
let converter = _this.conversionMap.get(staticSymbol); let converter = self.conversionMap.get(staticSymbol);
if (converter) { if (converter) {
let args: any[] = expression['arguments']; let args: any[] = expression['arguments'];
if (!args) { if (!args) {

View File

@ -8,11 +8,12 @@
import {ChangeDetectionStrategy, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core'; import {ChangeDetectionStrategy, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core';
import {StaticSymbol, isStaticSymbol} from './aot/static_symbol';
import {ListWrapper} from './facade/collection'; import {ListWrapper} from './facade/collection';
import {isPresent} from './facade/lang'; import {isPresent, stringify} from './facade/lang';
import {LifecycleHooks} from './private_import_core'; import {LifecycleHooks, reflector} from './private_import_core';
import {CssSelector} from './selector'; import {CssSelector} from './selector';
import {sanitizeIdentifier, splitAtColon} from './util'; import {splitAtColon} from './util';
function unimplemented(): any { function unimplemented(): any {
throw new Error('unimplemented'); throw new Error('unimplemented');
@ -24,10 +25,6 @@ function unimplemented(): any {
// group 3: "@trigger" from "@trigger" // group 3: "@trigger" from "@trigger"
const HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))|(\@[-\w]+)$/; const HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))|(\@[-\w]+)$/;
export abstract class CompileMetadataWithIdentifier {
get identifier(): CompileIdentifierMetadata { return <CompileIdentifierMetadata>unimplemented(); }
}
export class CompileAnimationEntryMetadata { export class CompileAnimationEntryMetadata {
constructor( constructor(
public name: string = null, public definitions: CompileAnimationStateMetadata[] = null) {} public name: string = null, public definitions: CompileAnimationStateMetadata[] = null) {}
@ -78,25 +75,44 @@ export class CompileAnimationGroupMetadata extends CompileAnimationWithStepsMeta
constructor(steps: CompileAnimationMetadata[] = null) { super(steps); } constructor(steps: CompileAnimationMetadata[] = null) { super(steps); }
} }
export class CompileIdentifierMetadata implements CompileMetadataWithIdentifier {
reference: any;
name: string;
prefix: string;
moduleUrl: string;
value: any;
constructor( function _sanitizeIdentifier(name: string): string {
{reference, name, moduleUrl, prefix, value}: return name.replace(/\W/g, '_');
{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;
} }
get identifier(): CompileIdentifierMetadata { return this; } let _anonymousTypeIndex = 0;
export function identifierName(compileIdentifier: CompileIdentifierMetadata): string {
if (!compileIdentifier || !compileIdentifier.reference) {
return null;
} }
const ref = compileIdentifier.reference;
if (isStaticSymbol(ref)) {
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;
}
export function identifierModuleUrl(compileIdentifier: CompileIdentifierMetadata): string {
const ref = compileIdentifier.reference;
if (isStaticSymbol(ref)) {
return ref.filePath;
}
return reflector.importUri(ref);
}
export interface CompileIdentifierMetadata { reference: any; }
/** /**
* A CompileSummary is the data needed to use a directive / pipe / module * A CompileSummary is the data needed to use a directive / pipe / module
@ -107,154 +123,65 @@ export interface CompileSummary {
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */; isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
} }
export class CompileDiDependencyMetadata { export interface CompileDiDependencyMetadata {
isAttribute: boolean; isAttribute?: boolean;
isSelf: boolean; isSelf?: boolean;
isHost: boolean; isHost?: boolean;
isSkipSelf: boolean; isSkipSelf?: boolean;
isOptional: boolean; isOptional?: boolean;
isValue: boolean; isValue?: boolean;
token?: CompileTokenMetadata;
value?: any;
}
export interface CompileProviderMetadata {
token: CompileTokenMetadata; token: CompileTokenMetadata;
value: any; useClass?: CompileTypeMetadata;
useValue?: any;
constructor({isAttribute, isSelf, isHost, isSkipSelf, isOptional, isValue, token, value}: { useExisting?: CompileTokenMetadata;
isAttribute?: boolean, useFactory?: CompileFactoryMetadata;
isSelf?: boolean, deps?: CompileDiDependencyMetadata[];
isHost?: boolean, multi?: 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;
}
} }
export class CompileProviderMetadata { export interface CompileFactoryMetadata extends CompileIdentifierMetadata {
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 {
diDeps: CompileDiDependencyMetadata[]; 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 class CompileTokenMetadata implements CompileMetadataWithIdentifier { export function tokenName(token: CompileTokenMetadata) {
value: any; return isPresent(token.value) ? _sanitizeIdentifier(token.value) :
identifier: CompileIdentifierMetadata; identifierName(token.identifier);
identifierIsInstance: boolean;
constructor(
{value, identifier, identifierIsInstance}:
{value?: any, identifier?: CompileIdentifierMetadata, identifierIsInstance?: boolean}) {
this.value = value;
this.identifier = identifier;
this.identifierIsInstance = !!identifierIsInstance;
} }
get reference(): any { export function tokenReference(token: CompileTokenMetadata) {
if (isPresent(this.identifier)) { if (isPresent(token.identifier)) {
return this.identifier.reference; return token.identifier.reference;
} else { } else {
return this.value; return token.value;
} }
} }
get name(): string { export interface CompileTokenMetadata {
return isPresent(this.value) ? sanitizeIdentifier(this.value) : this.identifier.name; value?: any;
} identifier?: CompileIdentifierMetadata|CompileTypeMetadata;
} }
/** /**
* Metadata regarding compilation of a type. * Metadata regarding compilation of a type.
*/ */
export class CompileTypeMetadata extends CompileIdentifierMetadata { export interface CompileTypeMetadata extends CompileIdentifierMetadata {
isHost: boolean;
diDeps: CompileDiDependencyMetadata[]; diDeps: CompileDiDependencyMetadata[];
lifecycleHooks: LifecycleHooks[]; lifecycleHooks: LifecycleHooks[];
reference: any;
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);
}
} }
export class CompileQueryMetadata { export interface CompileQueryMetadata {
selectors: Array<CompileTokenMetadata>; selectors: Array<CompileTokenMetadata>;
descendants: boolean; descendants: boolean;
first: boolean; first: boolean;
propertyName: string; propertyName: string;
read: CompileTokenMetadata; 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;
}
} }
/** /**
@ -333,6 +260,8 @@ export class CompileTemplateMetadata {
} }
} }
// 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 CompileSummary { export interface CompileDirectiveSummary extends CompileSummary {
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */; isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
type: CompileTypeMetadata; type: CompileTypeMetadata;
@ -355,10 +284,11 @@ export interface CompileDirectiveSummary extends CompileSummary {
/** /**
* Metadata regarding compilation of a directive. * Metadata regarding compilation of a directive.
*/ */
export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier { export class CompileDirectiveMetadata {
static create( static create(
{type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host, providers, {isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host,
viewProviders, queries, viewQueries, entryComponents, template}: { providers, viewProviders, queries, viewQueries, entryComponents, template}: {
isHost?: boolean,
type?: CompileTypeMetadata, type?: CompileTypeMetadata,
isComponent?: boolean, isComponent?: boolean,
selector?: string, selector?: string,
@ -367,10 +297,8 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
inputs?: string[], inputs?: string[],
outputs?: string[], outputs?: string[],
host?: {[key: string]: string}, host?: {[key: string]: string},
providers?: providers?: CompileProviderMetadata[],
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>, viewProviders?: CompileProviderMetadata[],
viewProviders?:
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
queries?: CompileQueryMetadata[], queries?: CompileQueryMetadata[],
viewQueries?: CompileQueryMetadata[], viewQueries?: CompileQueryMetadata[],
entryComponents?: CompileIdentifierMetadata[], entryComponents?: CompileIdentifierMetadata[],
@ -412,6 +340,7 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
} }
return new CompileDirectiveMetadata({ return new CompileDirectiveMetadata({
isHost,
type, type,
isComponent: !!isComponent, selector, exportAs, changeDetection, isComponent: !!isComponent, selector, exportAs, changeDetection,
inputs: inputsMap, inputs: inputsMap,
@ -427,6 +356,7 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
template, template,
}); });
} }
isHost: boolean;
type: CompileTypeMetadata; type: CompileTypeMetadata;
isComponent: boolean; isComponent: boolean;
selector: string; selector: string;
@ -446,9 +376,10 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
template: CompileTemplateMetadata; template: CompileTemplateMetadata;
constructor( constructor(
{type, isComponent, selector, exportAs, changeDetection, inputs, outputs, hostListeners, {isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs,
hostProperties, hostAttributes, providers, viewProviders, queries, viewQueries, hostListeners, hostProperties, hostAttributes, providers, viewProviders, queries,
entryComponents, template}: { viewQueries, entryComponents, template}: {
isHost?: boolean,
type?: CompileTypeMetadata, type?: CompileTypeMetadata,
isComponent?: boolean, isComponent?: boolean,
selector?: string, selector?: string,
@ -459,15 +390,14 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
hostListeners?: {[key: string]: string}, hostListeners?: {[key: string]: string},
hostProperties?: {[key: string]: string}, hostProperties?: {[key: string]: string},
hostAttributes?: {[key: string]: string}, hostAttributes?: {[key: string]: string},
providers?: providers?: CompileProviderMetadata[],
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>, viewProviders?: CompileProviderMetadata[],
viewProviders?:
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
queries?: CompileQueryMetadata[], queries?: CompileQueryMetadata[],
viewQueries?: CompileQueryMetadata[], viewQueries?: CompileQueryMetadata[],
entryComponents?: CompileIdentifierMetadata[], entryComponents?: CompileIdentifierMetadata[],
template?: CompileTemplateMetadata, template?: CompileTemplateMetadata,
} = {}) { } = {}) {
this.isHost = !!isHost;
this.type = type; this.type = type;
this.isComponent = isComponent; this.isComponent = isComponent;
this.selector = selector; this.selector = selector;
@ -487,8 +417,6 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
this.template = template; this.template = template;
} }
get identifier(): CompileIdentifierMetadata { return this.type; }
toSummary(): CompileDirectiveSummary { toSummary(): CompileDirectiveSummary {
return { return {
isSummary: true, isSummary: true,
@ -514,16 +442,12 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
/** /**
* Construct {@link CompileDirectiveMetadata} from {@link ComponentTypeMetadata} and a selector. * Construct {@link CompileDirectiveMetadata} from {@link ComponentTypeMetadata} and a selector.
*/ */
export function createHostComponentMeta(compMeta: CompileDirectiveMetadata): export function createHostComponentMeta(
CompileDirectiveMetadata { typeReference: any, compMeta: CompileDirectiveMetadata): CompileDirectiveMetadata {
const template = CssSelector.parse(compMeta.selector)[0].getMatchingElementTemplate(); const template = CssSelector.parse(compMeta.selector)[0].getMatchingElementTemplate();
return CompileDirectiveMetadata.create({ return CompileDirectiveMetadata.create({
type: new CompileTypeMetadata({ isHost: true,
reference: Object, type: {reference: typeReference, diDeps: [], lifecycleHooks: []},
name: `${compMeta.type.name}_Host`,
moduleUrl: compMeta.type.moduleUrl,
isHost: true
}),
template: new CompileTemplateMetadata({ template: new CompileTemplateMetadata({
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
template: template, template: template,
@ -553,7 +477,7 @@ export interface CompilePipeSummary extends CompileSummary {
pure: boolean; pure: boolean;
} }
export class CompilePipeMetadata implements CompileMetadataWithIdentifier { export class CompilePipeMetadata {
type: CompileTypeMetadata; type: CompileTypeMetadata;
name: string; name: string;
pure: boolean; pure: boolean;
@ -567,13 +491,14 @@ export class CompilePipeMetadata implements CompileMetadataWithIdentifier {
this.name = name; this.name = name;
this.pure = !!pure; this.pure = !!pure;
} }
get identifier(): CompileIdentifierMetadata { return this.type; }
toSummary(): CompilePipeSummary { toSummary(): CompilePipeSummary {
return {isSummary: true, type: this.type, name: this.name, pure: this.pure}; return {isSummary: true, type: this.type, name: this.name, pure: this.pure};
} }
} }
// Note: This should only use interfaces as nested data types
// as we need to be able to serialize this from/to JSON!
export interface CompileNgModuleInjectorSummary extends CompileSummary { export interface CompileNgModuleInjectorSummary extends CompileSummary {
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */; isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
type: CompileTypeMetadata; type: CompileTypeMetadata;
@ -583,6 +508,8 @@ export interface CompileNgModuleInjectorSummary extends CompileSummary {
exportedModules: CompileNgModuleInjectorSummary[]; exportedModules: CompileNgModuleInjectorSummary[];
} }
// Note: This should only use interfaces as nested data types
// as we need to be able to serialize this from/to JSON!
export interface CompileNgModuleDirectiveSummary extends CompileSummary { export interface CompileNgModuleDirectiveSummary extends CompileSummary {
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */; isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
type: CompileTypeMetadata; type: CompileTypeMetadata;
@ -592,13 +519,15 @@ export interface CompileNgModuleDirectiveSummary extends CompileSummary {
directiveLoaders: (() => Promise<void>)[]; directiveLoaders: (() => Promise<void>)[];
} }
// Note: This should only use interfaces as nested data types
// as we need to be able to serialize this from/to JSON!
export type CompileNgModuleSummary = export type CompileNgModuleSummary =
CompileNgModuleInjectorSummary & CompileNgModuleDirectiveSummary; CompileNgModuleInjectorSummary & CompileNgModuleDirectiveSummary;
/** /**
* Metadata regarding compilation of a module. * Metadata regarding compilation of a module.
*/ */
export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier { export class CompileNgModuleMetadata {
type: CompileTypeMetadata; type: CompileTypeMetadata;
declaredDirectives: CompileIdentifierMetadata[]; declaredDirectives: CompileIdentifierMetadata[];
exportedDirectives: CompileIdentifierMetadata[]; exportedDirectives: CompileIdentifierMetadata[];
@ -620,8 +549,7 @@ export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
entryComponents, bootstrapComponents, importedModules, exportedModules, schemas, entryComponents, bootstrapComponents, importedModules, exportedModules, schemas,
transitiveModule, id}: { transitiveModule, id}: {
type?: CompileTypeMetadata, type?: CompileTypeMetadata,
providers?: providers?: CompileProviderMetadata[],
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
declaredDirectives?: CompileIdentifierMetadata[], declaredDirectives?: CompileIdentifierMetadata[],
exportedDirectives?: CompileIdentifierMetadata[], exportedDirectives?: CompileIdentifierMetadata[],
declaredPipes?: CompileIdentifierMetadata[], declaredPipes?: CompileIdentifierMetadata[],
@ -649,8 +577,6 @@ export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
this.transitiveModule = transitiveModule; this.transitiveModule = transitiveModule;
} }
get identifier(): CompileIdentifierMetadata { return this.type; }
toSummary(): CompileNgModuleSummary { toSummary(): CompileNgModuleSummary {
return { return {
isSummary: true, isSummary: true,
@ -701,19 +627,6 @@ export class TransitiveCompileNgModuleMetadata {
} }
} }
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);
}
});
return Array.from(map.values());
}
function _normalizeArray(obj: any[]): any[] { function _normalizeArray(obj: any[]): any[] {
return obj || []; return obj || [];
} }

View File

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

View File

@ -9,7 +9,7 @@
import * as cdAst from '../expression_parser/ast'; import * as cdAst from '../expression_parser/ast';
import {isBlank, isPresent} from '../facade/lang'; import {isBlank, isPresent} from '../facade/lang';
import {Identifiers, resolveIdentifier} from '../identifiers'; import {Identifiers, createIdentifier} from '../identifiers';
import {ClassBuilder} from '../output/class_builder'; import {ClassBuilder} from '../output/class_builder';
import * as o from '../output/output_ast'; 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)) { if (readVars.has(VAL_UNWRAPPER_VAR.name)) {
unwrapperStmts.push( unwrapperStmts.push(
VAL_UNWRAPPER_VAR VAL_UNWRAPPER_VAR
.set(o.importExpr(resolveIdentifier(Identifiers.ValueUnwrapper)).instantiate([])) .set(o.importExpr(createIdentifier(Identifiers.ValueUnwrapper)).instantiate([]))
.toDeclStmt(null, [o.StmtModifier.Final])); .toDeclStmt(null, [o.StmtModifier.Final]));
} }
return unwrapperStmts; return unwrapperStmts;
@ -277,8 +277,8 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
args.push(o.literal(ast.strings[ast.strings.length - 1])); args.push(o.literal(ast.strings[ast.strings.length - 1]));
return ast.expressions.length <= 9 ? return ast.expressions.length <= 9 ?
o.importExpr(resolveIdentifier(Identifiers.inlineInterpolate)).callFn(args) : o.importExpr(createIdentifier(Identifiers.inlineInterpolate)).callFn(args) :
o.importExpr(resolveIdentifier(Identifiers.interpolate)).callFn([ o.importExpr(createIdentifier(Identifiers.interpolate)).callFn([
args[0], o.literalArr(args.slice(1)) args[0], o.literalArr(args.slice(1))
]); ]);
} }
@ -578,7 +578,7 @@ function flattenStatements(arg: any, output: o.Statement[]) {
function createCachedLiteralArray(builder: ClassBuilder, values: o.Expression[]): o.Expression { function createCachedLiteralArray(builder: ClassBuilder, values: o.Expression[]): o.Expression {
if (values.length === 0) { 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 proxyExpr = o.THIS_EXPR.prop(`_arr_${builder.fields.length}`);
const proxyParams: o.FnParam[] = []; const proxyParams: o.FnParam[] = [];
@ -599,7 +599,7 @@ function createCachedLiteralArray(builder: ClassBuilder, values: o.Expression[])
function createCachedLiteralMap( function createCachedLiteralMap(
builder: ClassBuilder, entries: [string, o.Expression][]): o.Expression { builder: ClassBuilder, entries: [string, o.Expression][]): o.Expression {
if (entries.length === 0) { 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 proxyExpr = o.THIS_EXPR.prop(`_map_${builder.fields.length}`);
const proxyParams: o.FnParam[] = []; const proxyParams: o.FnParam[] = [];

View File

@ -8,15 +8,12 @@
import {CompileTokenMetadata} from '../compile_metadata'; import {CompileTokenMetadata} from '../compile_metadata';
import {isPresent} from '../facade/lang'; 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'; import * as o from '../output/output_ast';
export function createDiTokenExpression(token: CompileTokenMetadata): o.Expression { export function createDiTokenExpression(token: CompileTokenMetadata): o.Expression {
if (isPresent(token.value)) { if (isPresent(token.value)) {
return o.literal(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 { } else {
return o.importExpr(token.identifier); return o.importExpr(token.identifier);
} }
@ -24,13 +21,13 @@ export function createDiTokenExpression(token: CompileTokenMetadata): o.Expressi
export function createInlineArray(values: o.Expression[]): o.Expression { export function createInlineArray(values: o.Expression[]): o.Expression {
if (values.length === 0) { 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 log2 = Math.log(values.length) / Math.log(2);
const index = Math.ceil(log2); const index = Math.ceil(log2);
const identifierSpec = index < Identifiers.inlineArrays.length ? Identifiers.inlineArrays[index] : const identifierSpec = index < Identifiers.inlineArrays.length ? Identifiers.inlineArrays[index] :
Identifiers.InlineArrayDynamic; Identifiers.InlineArrayDynamic;
const identifier = resolveIdentifier(identifierSpec); const identifier = createIdentifier(identifierSpec);
return o.importExpr(identifier).instantiate([ return o.importExpr(identifier).instantiate([
<o.Expression>o.literal(values.length) <o.Expression>o.literal(values.length)
].concat(values)); ].concat(values));
@ -46,7 +43,7 @@ export function createPureProxy(
throw new Error(`Unsupported number of argument for pure functions: ${argCount}`); throw new Error(`Unsupported number of argument for pure functions: ${argCount}`);
} }
builder.ctorStmts.push(o.THIS_EXPR.prop(pureProxyProp.name) builder.ctorStmts.push(o.THIS_EXPR.prop(pureProxyProp.name)
.set(o.importExpr(resolveIdentifier(pureProxyId)).callFn([fn])) .set(o.importExpr(createIdentifier(pureProxyId)).callFn([fn]))
.toStmt()); .toStmt());
} }
@ -56,5 +53,5 @@ export function createEnumExpression(enumType: IdentifierSpec, enumValue: any):
if (!enumName) { if (!enumName) {
throw new Error(`Unknown enum value ${enumValue} in ${enumType.name}`); 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 {SecurityContext} from '@angular/core';
import {isPresent} from '../facade/lang'; import {isPresent} from '../facade/lang';
import {Identifiers, resolveIdentifier} from '../identifiers'; import {Identifiers, createIdentifier} from '../identifiers';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';
import {EMPTY_STATE as EMPTY_ANIMATION_STATE} from '../private_import_core'; import {EMPTY_STATE as EMPTY_ANIMATION_STATE} from '../private_import_core';
import {BoundElementPropertyAst, BoundEventAst, PropertyBindingType} from '../template_parser/template_ast'; import {BoundElementPropertyAst, BoundEventAst, PropertyBindingType} from '../template_parser/template_ast';
@ -26,7 +26,7 @@ export function writeToRenderer(
case PropertyBindingType.Property: case PropertyBindingType.Property:
if (logBindingUpdate) { if (logBindingUpdate) {
updateStmts.push( updateStmts.push(
o.importExpr(resolveIdentifier(Identifiers.setBindingDebugInfo)) o.importExpr(createIdentifier(Identifiers.setBindingDebugInfo))
.callFn([renderer, renderElement, o.literal(boundProp.name), renderValue]) .callFn([renderer, renderElement, o.literal(boundProp.name), renderValue])
.toStmt()); .toStmt());
} }
@ -104,7 +104,7 @@ export function triggerAnimation(
// it's important to normalize the void value as `void` explicitly // it's important to normalize the void value as `void` explicitly
// so that the styles data can be obtained from the stringmap // so that the styles data can be obtained from the stringmap
const emptyStateValue = o.literal(EMPTY_ANIMATION_STATE); 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); const animationTransitionVar = o.variable('animationTransition_' + animationName);
updateStmts.push( updateStmts.push(

View File

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

View File

@ -8,11 +8,12 @@
import {Component, Directive, HostBinding, HostListener, Injectable, Input, Output, Query, Type, resolveForwardRef} from '@angular/core'; import {Component, Directive, HostBinding, HostListener, Injectable, 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 {stringify} from './facade/lang';
import {ReflectorReader, reflector} from './private_import_core'; import {ReflectorReader, reflector} from './private_import_core';
import {splitAtColon} from './util'; import {splitAtColon} from './util';
/* /*
* Resolve a `Type` for {@link Directive}. * Resolve a `Type` for {@link Directive}.
* *
@ -35,7 +36,7 @@ export class DirectiveResolver {
resolve(type: Type<any>, throwIfNotFound = true): Directive { resolve(type: Type<any>, throwIfNotFound = true): Directive {
const typeMetadata = this._reflector.annotations(resolveForwardRef(type)); const typeMetadata = this._reflector.annotations(resolveForwardRef(type));
if (typeMetadata) { if (typeMetadata) {
const metadata = typeMetadata.find(isDirectiveMetadata); const metadata = ListWrapper.findLast(typeMetadata, isDirectiveMetadata);
if (metadata) { if (metadata) {
const propertyMetadata = this._reflector.propMetadata(type); const propertyMetadata = this._reflector.propMetadata(type);
return this._mergeWithPropertyMetadata(metadata, propertyMetadata, type); return this._mergeWithPropertyMetadata(metadata, propertyMetadata, type);
@ -58,23 +59,25 @@ export class DirectiveResolver {
const queries: {[key: string]: any} = {}; const queries: {[key: string]: any} = {};
Object.keys(propertyMetadata).forEach((propName: string) => { Object.keys(propertyMetadata).forEach((propName: string) => {
const input = ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof Input);
propertyMetadata[propName].forEach(a => { if (input) {
if (a instanceof Input) { if (input.bindingPropertyName) {
if (a.bindingPropertyName) { inputs.push(`${propName}: ${input.bindingPropertyName}`);
inputs.push(`${propName}: ${a.bindingPropertyName}`);
} else { } else {
inputs.push(propName); 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) { if (output.bindingPropertyName) {
outputs.push(`${propName}: ${output.bindingPropertyName}`); outputs.push(`${propName}: ${output.bindingPropertyName}`);
} else { } else {
outputs.push(propName); outputs.push(propName);
} }
} else if (a instanceof HostBinding) { }
const hostBinding: HostBinding = a; const hostBinding =
ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof HostBinding);
if (hostBinding) {
if (hostBinding.hostPropertyName) { if (hostBinding.hostPropertyName) {
const startWith = hostBinding.hostPropertyName[0]; const startWith = hostBinding.hostPropertyName[0];
if (startWith === '(') { if (startWith === '(') {
@ -87,56 +90,45 @@ export class DirectiveResolver {
} else { } else {
host[`[${propName}]`] = propName; host[`[${propName}]`] = propName;
} }
} else if (a instanceof HostListener) { }
const hostListener: HostListener = a; const hostListener =
ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof HostListener);
if (hostListener) {
const args = hostListener.args || []; const args = hostListener.args || [];
host[`(${hostListener.eventName})`] = `${propName}(${args.join(',')})`; 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); return this._merge(dm, inputs, outputs, host, queries, directiveType);
} }
private _extractPublicName(def: string) { return splitAtColon(def, [null, def])[1].trim(); } 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( private _merge(
directive: Directive, inputs: string[], outputs: string[], host: {[key: string]: string}, directive: Directive, inputs: string[], outputs: string[], host: {[key: string]: string},
queries: {[key: string]: any}, directiveType: Type<any>): Directive { queries: {[key: string]: any}, directiveType: Type<any>): Directive {
const mergedInputs: string[] = inputs; const mergedInputs =
this._dedupeBindings(directive.inputs ? directive.inputs.concat(inputs) : inputs);
if (directive.inputs) { const mergedOutputs =
const inputNames: string[] = this._dedupeBindings(directive.outputs ? directive.outputs.concat(outputs) : outputs);
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 mergedHost = directive.host ? StringMapWrapper.merge(directive.host, host) : host; const mergedHost = directive.host ? StringMapWrapper.merge(directive.host, host) : host;
const mergedQueries = const mergedQueries =
directive.queries ? StringMapWrapper.merge(directive.queries, queries) : queries; directive.queries ? StringMapWrapper.merge(directive.queries, queries) : queries;

View File

@ -8,13 +8,13 @@
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata} from './compile_metadata'; import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, identifierModuleUrl, identifierName} from './compile_metadata';
import {createCheckBindingField, createCheckBindingStmt} from './compiler_util/binding_util'; import {createCheckBindingField, createCheckBindingStmt} from './compiler_util/binding_util';
import {EventHandlerVars, convertActionBinding, convertPropertyBinding} from './compiler_util/expression_converter'; import {EventHandlerVars, convertActionBinding, convertPropertyBinding} from './compiler_util/expression_converter';
import {triggerAnimation, writeToRenderer} from './compiler_util/render_util'; import {triggerAnimation, writeToRenderer} from './compiler_util/render_util';
import {CompilerConfig} from './config'; import {CompilerConfig} from './config';
import {Parser} from './expression_parser/parser'; import {Parser} from './expression_parser/parser';
import {Identifiers, resolveIdentifier} from './identifiers'; import {Identifiers, createIdentifier} from './identifiers';
import {DEFAULT_INTERPOLATION_CONFIG} from './ml_parser/interpolation_config'; import {DEFAULT_INTERPOLATION_CONFIG} from './ml_parser/interpolation_config';
import {ClassBuilder, createClassStmt} from './output/class_builder'; import {ClassBuilder, createClassStmt} from './output/class_builder';
import * as o from './output/output_ast'; import * as o from './output/output_ast';
@ -53,7 +53,9 @@ const RESET_CHANGES_STMT = o.THIS_EXPR.prop(CHANGES_FIELD_NAME).set(o.literalMap
*/ */
@Injectable() @Injectable()
export class DirectiveWrapperCompiler { export class DirectiveWrapperCompiler {
static dirWrapperClassName(id: CompileIdentifierMetadata) { return `Wrapper_${id.name}`; } static dirWrapperClassName(id: CompileIdentifierMetadata) {
return `Wrapper_${identifierName(id)}`;
}
constructor( constructor(
private compilerConfig: CompilerConfig, private _exprParser: Parser, private compilerConfig: CompilerConfig, private _exprParser: Parser,
@ -117,10 +119,10 @@ class DirectiveWrapperBuilder implements ClassBuilder {
[ [
new o.FnParam( new o.FnParam(
VIEW_VAR.name, VIEW_VAR.name,
o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])), o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
new o.FnParam( new o.FnParam(
COMPONENT_VIEW_VAR.name, COMPONENT_VIEW_VAR.name,
o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])), o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
new o.FnParam(RENDER_EL_VAR.name, o.DYNAMIC_TYPE), new o.FnParam(RENDER_EL_VAR.name, o.DYNAMIC_TYPE),
], ],
this.detachStmts), this.detachStmts),
@ -172,7 +174,7 @@ function addNgDoCheckMethod(builder: DirectiveWrapperBuilder) {
} }
if (builder.compilerConfig.logBindingUpdate) { if (builder.compilerConfig.logBindingUpdate) {
onChangesStmts.push( onChangesStmts.push(
o.importExpr(resolveIdentifier(Identifiers.setBindingDebugInfoForChanges)) o.importExpr(createIdentifier(Identifiers.setBindingDebugInfoForChanges))
.callFn( .callFn(
[VIEW_VAR.prop('renderer'), RENDER_EL_VAR, o.THIS_EXPR.prop(CHANGES_FIELD_NAME)]) [VIEW_VAR.prop('renderer'), RENDER_EL_VAR, o.THIS_EXPR.prop(CHANGES_FIELD_NAME)])
.toStmt()); .toStmt());
@ -198,7 +200,7 @@ function addNgDoCheckMethod(builder: DirectiveWrapperBuilder) {
'ngDoCheck', 'ngDoCheck',
[ [
new o.FnParam( new o.FnParam(
VIEW_VAR.name, o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])), VIEW_VAR.name, o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
new o.FnParam(RENDER_EL_VAR.name, o.DYNAMIC_TYPE), new o.FnParam(RENDER_EL_VAR.name, o.DYNAMIC_TYPE),
new o.FnParam(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE), new o.FnParam(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE),
], ],
@ -214,7 +216,7 @@ function addCheckInputMethod(input: string, builder: DirectiveWrapperBuilder) {
if (builder.genChanges) { if (builder.genChanges) {
onChangeStatements.push(o.THIS_EXPR.prop(CHANGES_FIELD_NAME) onChangeStatements.push(o.THIS_EXPR.prop(CHANGES_FIELD_NAME)
.key(o.literal(input)) .key(o.literal(input))
.set(o.importExpr(resolveIdentifier(Identifiers.SimpleChange)) .set(o.importExpr(createIdentifier(Identifiers.SimpleChange))
.instantiate([field.expression, CURR_VALUE_VAR])) .instantiate([field.expression, CURR_VALUE_VAR]))
.toStmt()); .toStmt());
} }
@ -237,10 +239,10 @@ function addCheckHostMethod(
const stmts: o.Statement[] = []; const stmts: o.Statement[] = [];
const methodParams: o.FnParam[] = [ const methodParams: o.FnParam[] = [
new o.FnParam( new o.FnParam(
VIEW_VAR.name, o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])), VIEW_VAR.name, o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
new o.FnParam( new o.FnParam(
COMPONENT_VIEW_VAR.name, COMPONENT_VIEW_VAR.name,
o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])), o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
new o.FnParam(RENDER_EL_VAR.name, o.DYNAMIC_TYPE), new o.FnParam(RENDER_EL_VAR.name, o.DYNAMIC_TYPE),
new o.FnParam(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE), new o.FnParam(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE),
]; ];
@ -255,14 +257,14 @@ function addCheckHostMethod(
if (hostProp.needsRuntimeSecurityContext) { if (hostProp.needsRuntimeSecurityContext) {
securityContextExpr = o.variable(`secCtx_${methodParams.length}`); securityContextExpr = o.variable(`secCtx_${methodParams.length}`);
methodParams.push(new o.FnParam( methodParams.push(new o.FnParam(
securityContextExpr.name, o.importType(resolveIdentifier(Identifiers.SecurityContext)))); securityContextExpr.name, o.importType(createIdentifier(Identifiers.SecurityContext))));
} }
let checkBindingStmts: o.Statement[]; let checkBindingStmts: o.Statement[];
if (hostProp.isAnimation) { if (hostProp.isAnimation) {
const {updateStmts, detachStmts} = triggerAnimation( const {updateStmts, detachStmts} = triggerAnimation(
VIEW_VAR, COMPONENT_VIEW_VAR, hostProp, VIEW_VAR, COMPONENT_VIEW_VAR, hostProp,
o.THIS_EXPR.prop(EVENT_HANDLER_FIELD_NAME) o.THIS_EXPR.prop(EVENT_HANDLER_FIELD_NAME)
.or(o.importExpr(resolveIdentifier(Identifiers.noop))), .or(o.importExpr(createIdentifier(Identifiers.noop))),
RENDER_EL_VAR, evalResult.currValExpr, field.expression); RENDER_EL_VAR, evalResult.currValExpr, field.expression);
checkBindingStmts = updateStmts; checkBindingStmts = updateStmts;
builder.detachStmts.push(...detachStmts); builder.detachStmts.push(...detachStmts);
@ -306,7 +308,7 @@ function addHandleEventMethod(hostListeners: BoundEventAst[], builder: Directive
function addSubscribeMethod(dirMeta: CompileDirectiveMetadata, builder: DirectiveWrapperBuilder) { function addSubscribeMethod(dirMeta: CompileDirectiveMetadata, builder: DirectiveWrapperBuilder) {
const methodParams: o.FnParam[] = [ const methodParams: o.FnParam[] = [
new o.FnParam( new o.FnParam(
VIEW_VAR.name, o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])), VIEW_VAR.name, o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
new o.FnParam(EVENT_HANDLER_FIELD_NAME, o.DYNAMIC_TYPE) new o.FnParam(EVENT_HANDLER_FIELD_NAME, o.DYNAMIC_TYPE)
]; ];
const stmts: o.Statement[] = [ const stmts: o.Statement[] = [
@ -348,9 +350,10 @@ function parseHostBindings(
const errors: ParseError[] = []; const errors: ParseError[] = [];
const parser = const parser =
new BindingParser(exprParser, DEFAULT_INTERPOLATION_CONFIG, schemaRegistry, [], errors); new BindingParser(exprParser, DEFAULT_INTERPOLATION_CONFIG, schemaRegistry, [], errors);
const sourceFileName = dirMeta.type.moduleUrl ? const moduleUrl = identifierModuleUrl(dirMeta.type);
`in Directive ${dirMeta.type.name} in ${dirMeta.type.moduleUrl}` : const sourceFileName = moduleUrl ?
`in Directive ${dirMeta.type.name}`; `in Directive ${identifierName(dirMeta.type)} in ${moduleUrl}` :
`in Directive ${identifierName(dirMeta.type)}`;
const sourceFile = new ParseSourceFile('', sourceFileName); const sourceFile = new ParseSourceFile('', sourceFileName);
const sourceSpan = new ParseSourceSpan( const sourceSpan = new ParseSourceSpan(
new ParseLocation(sourceFile, null, null, null), new ParseLocation(sourceFile, null, null, null),

View File

@ -8,10 +8,16 @@
import * as i18n from './i18n_ast'; import * as i18n from './i18n_ast';
export function digestMessage(message: i18n.Message): string { export function digest(message: i18n.Message): string {
return sha1(serializeNodes(message.nodes).join('') + `[${message.meaning}]`); return sha1(serializeNodes(message.nodes).join('') + `[${message.meaning}]`);
} }
export function decimalDigest(message: i18n.Message): string {
const visitor = new _SerializerIgnoreIcuExpVisitor();
const parts = message.nodes.map(a => a.visit(visitor, null));
return computeMsgId(parts.join(''), message.meaning);
}
/** /**
* Serialize the i18n ast to something xml-like in order to generate an UID. * Serialize the i18n ast to something xml-like in order to generate an UID.
* *
@ -39,7 +45,7 @@ class _SerializerVisitor implements i18n.Visitor {
} }
visitPlaceholder(ph: i18n.Placeholder, context: any): any { visitPlaceholder(ph: i18n.Placeholder, context: any): any {
return `<ph name="${ph.name}">${ph.value}</ph>`; return ph.value ? `<ph name="${ph.name}">${ph.value}</ph>` : `<ph name="${ph.name}"/>`;
} }
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): any { visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): any {
@ -53,6 +59,21 @@ export function serializeNodes(nodes: i18n.Node[]): string[] {
return nodes.map(a => a.visit(serializerVisitor, null)); return nodes.map(a => a.visit(serializerVisitor, null));
} }
/**
* Serialize the i18n ast to something xml-like in order to generate an UID.
*
* Ignore the ICU expressions so that message IDs stays identical if only the expression changes.
*
* @internal
*/
class _SerializerIgnoreIcuExpVisitor extends _SerializerVisitor {
visitIcu(icu: i18n.Icu, context: any): any {
let strCases = Object.keys(icu.cases).map((k: string) => `${k} {${icu.cases[k].visit(this)}}`);
// Do not take the expression into account
return `{${icu.type}, ${strCases.join(', ')}}`;
}
}
/** /**
* Compute the SHA1 of the given string * Compute the SHA1 of the given string
* *
@ -63,7 +84,7 @@ export function serializeNodes(nodes: i18n.Node[]): string[] {
*/ */
export function sha1(str: string): string { export function sha1(str: string): string {
const utf8 = utf8Encode(str); const utf8 = utf8Encode(str);
const words32 = stringToWords32(utf8); const words32 = stringToWords32(utf8, Endian.Big);
const len = utf8.length * 8; const len = utf8.length * 8;
const w = new Array(80); const w = new Array(80);
@ -90,15 +111,99 @@ export function sha1(str: string): string {
[a, b, c, d, e] = [add32(a, h0), add32(b, h1), add32(c, h2), add32(d, h3), add32(e, h4)]; [a, b, c, d, e] = [add32(a, h0), add32(b, h1), add32(c, h2), add32(d, h3), add32(e, h4)];
} }
const sha1 = words32ToString([a, b, c, d, e]); return byteStringToHexString(words32ToByteString([a, b, c, d, e]));
let hex: string = '';
for (let i = 0; i < sha1.length; i++) {
const b = sha1.charCodeAt(i);
hex += (b >>> 4 & 0x0f).toString(16) + (b & 0x0f).toString(16);
} }
return hex.toLowerCase(); function fk(index: number, b: number, c: number, d: number): [number, number] {
if (index < 20) {
return [(b & c) | (~b & d), 0x5a827999];
}
if (index < 40) {
return [b ^ c ^ d, 0x6ed9eba1];
}
if (index < 60) {
return [(b & c) | (b & d) | (c & d), 0x8f1bbcdc];
}
return [b ^ c ^ d, 0xca62c1d6];
}
/**
* Compute the fingerprint of the given string
*
* The output is 64 bit number encoded as a decimal string
*
* based on:
* https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/GoogleJsMessageIdGenerator.java
*/
export function fingerprint(str: string): [number, number] {
const utf8 = utf8Encode(str);
let [hi, lo] = [hash32(utf8, 0), hash32(utf8, 102072)];
if (hi == 0 && (lo == 0 || lo == 1)) {
hi = hi ^ 0x130f9bef;
lo = lo ^ -0x6b5f56d8;
}
return [hi, lo];
}
export function computeMsgId(msg: string, meaning: string): string {
let [hi, lo] = fingerprint(msg);
if (meaning) {
const [him, lom] = fingerprint(meaning);
[hi, lo] = add64(rol64([hi, lo], 1), [him, lom]);
}
return byteStringToDecString(words32ToByteString([hi & 0x7fffffff, lo]));
}
function hash32(str: string, c: number): number {
let [a, b] = [0x9e3779b9, 0x9e3779b9];
let i: number;
const len = str.length;
for (i = 0; i + 12 <= len; i += 12) {
a = add32(a, wordAt(str, i, Endian.Little));
b = add32(b, wordAt(str, i + 4, Endian.Little));
c = add32(c, wordAt(str, i + 8, Endian.Little));
[a, b, c] = mix([a, b, c]);
}
a = add32(a, wordAt(str, i, Endian.Little));
b = add32(b, wordAt(str, i + 4, Endian.Little));
// the first byte of c is reserved for the length
c = add32(c, len);
c = add32(c, wordAt(str, i + 8, Endian.Little) << 8);
return mix([a, b, c])[2];
}
// clang-format off
function mix([a, b, c]: [number, number, number]): [number, number, number] {
a = sub32(a, b); a = sub32(a, c); a ^= c >>> 13;
b = sub32(b, c); b = sub32(b, a); b ^= a << 8;
c = sub32(c, a); c = sub32(c, b); c ^= b >>> 13;
a = sub32(a, b); a = sub32(a, c); a ^= c >>> 12;
b = sub32(b, c); b = sub32(b, a); b ^= a << 16;
c = sub32(c, a); c = sub32(c, b); c ^= b >>> 5;
a = sub32(a, b); a = sub32(a, c); a ^= c >>> 3;
b = sub32(b, c); b = sub32(b, a); b ^= a << 10;
c = sub32(c, a); c = sub32(c, b); c ^= b >>> 15;
return [a, b, c];
}
// clang-format on
// Utils
enum Endian {
Little,
Big,
} }
function utf8Encode(str: string): string { function utf8Encode(str: string): string {
@ -131,10 +236,9 @@ function decodeSurrogatePairs(str: string, index: number): number {
} }
const high = str.charCodeAt(index); const high = str.charCodeAt(index);
let low: number;
if (high >= 0xd800 && high <= 0xdfff && str.length > index + 1) { if (high >= 0xd800 && high <= 0xdfff && str.length > index + 1) {
low = str.charCodeAt(index + 1); const low = byteAt(str, index + 1);
if (low >= 0xdc00 && low <= 0xdfff) { if (low >= 0xdc00 && low <= 0xdfff) {
return (high - 0xd800) * 0x400 + low - 0xdc00 + 0x10000; return (high - 0xd800) * 0x400 + low - 0xdc00 + 0x10000;
} }
@ -143,50 +247,126 @@ function decodeSurrogatePairs(str: string, index: number): number {
return high; return high;
} }
function stringToWords32(str: string): number[] { function add32(a: number, b: number): number {
const words32 = Array(str.length >>> 2); return add32to64(a, b)[1];
for (let i = 0; i < words32.length; i++) {
words32[i] = 0;
} }
for (let i = 0; i < str.length; i++) { function add32to64(a: number, b: number): [number, number] {
words32[i >>> 2] |= (str.charCodeAt(i) & 0xff) << 8 * (3 - i & 0x3); const low = (a & 0xffff) + (b & 0xffff);
const high = (a >>> 16) + (b >>> 16) + (low >>> 16);
return [high >>> 16, (high << 16) | (low & 0xffff)];
}
function add64([ah, al]: [number, number], [bh, bl]: [number, number]): [number, number] {
const [carry, l] = add32to64(al, bl);
const h = add32(add32(ah, bh), carry);
return [h, l];
}
function sub32(a: number, b: number): number {
const low = (a & 0xffff) - (b & 0xffff);
const high = (a >> 16) - (b >> 16) + (low >> 16);
return (high << 16) | (low & 0xffff);
}
// Rotate a 32b number left `count` position
function rol32(a: number, count: number): number {
return (a << count) | (a >>> (32 - count));
}
// Rotate a 64b number left `count` position
function rol64([hi, lo]: [number, number], count: number): [number, number] {
const h = (hi << count) | (lo >>> (32 - count));
const l = (lo << count) | (hi >>> (32 - count));
return [h, l];
}
function stringToWords32(str: string, endian: Endian): number[] {
const words32 = Array((str.length + 3) >>> 2);
for (let i = 0; i < words32.length; i++) {
words32[i] = wordAt(str, i * 4, endian);
} }
return words32; return words32;
} }
function words32ToString(words32: number[]): string { function byteAt(str: string, index: number): number {
return index >= str.length ? 0 : str.charCodeAt(index) & 0xff;
}
function wordAt(str: string, index: number, endian: Endian): number {
let word = 0;
if (endian === Endian.Big) {
for (let i = 0; i < 4; i++) {
word += byteAt(str, index + i) << (24 - 8 * i);
}
} else {
for (let i = 0; i < 4; i++) {
word += byteAt(str, index + i) << 8 * i;
}
}
return word;
}
function words32ToByteString(words32: number[]): string {
return words32.reduce((str, word) => str + word32ToByteString(word), '');
}
function word32ToByteString(word: number): string {
let str = ''; let str = '';
for (let i = 0; i < words32.length * 4; i++) { for (let i = 0; i < 4; i++) {
str += String.fromCharCode((words32[i >>> 2] >>> 8 * (3 - i & 0x3)) & 0xff); str += String.fromCharCode((word >>> 8 * (3 - i)) & 0xff);
} }
return str; return str;
} }
function fk(index: number, b: number, c: number, d: number): [number, number] { function byteStringToHexString(str: string): string {
if (index < 20) { let hex: string = '';
return [(b & c) | (~b & d), 0x5a827999]; for (let i = 0; i < str.length; i++) {
const b = byteAt(str, i);
hex += (b >>> 4).toString(16) + (b & 0x0f).toString(16);
}
return hex.toLowerCase();
} }
if (index < 40) { // based on http://www.danvk.org/hex2dec.html (JS can not handle more than 56b)
return [b ^ c ^ d, 0x6ed9eba1]; function byteStringToDecString(str: string): string {
let decimal = '';
let toThePower = '1';
for (let i = str.length - 1; i >= 0; i--) {
decimal = addBigInt(decimal, numberTimesBigInt(byteAt(str, i), toThePower));
toThePower = numberTimesBigInt(256, toThePower);
} }
if (index < 60) { return decimal.split('').reverse().join('');
return [(b & c) | (b & d) | (c & d), 0x8f1bbcdc];
} }
return [b ^ c ^ d, 0xca62c1d6]; // x and y decimal, lowest significant digit first
function addBigInt(x: string, y: string): string {
let sum = '';
const len = Math.max(x.length, y.length);
for (let i = 0, carry = 0; i < len || carry; i++) {
const tmpSum = carry + +(x[i] || 0) + +(y[i] || 0);
if (tmpSum >= 10) {
carry = 1;
sum += tmpSum - 10;
} else {
carry = 0;
sum += tmpSum;
}
} }
function add32(a: number, b: number): number { return sum;
const low = (a & 0xffff) + (b & 0xffff);
const high = (a >> 16) + (b >> 16) + (low >> 16);
return (high << 16) | (low & 0xffff);
} }
function rol32(a: number, count: number): number { function numberTimesBigInt(num: number, b: string): string {
return (a << count) | (a >>> (32 - count)); let product = '';
let bToThePower = b;
for (; num !== 0; num = num >>> 1) {
if (num & 1) product = addBigInt(product, bToThePower);
bToThePower = addBigInt(bToThePower, bToThePower);
}
return product;
} }

View File

@ -10,7 +10,6 @@ import * as html from '../ml_parser/ast';
import {InterpolationConfig} from '../ml_parser/interpolation_config'; import {InterpolationConfig} from '../ml_parser/interpolation_config';
import {ParseTreeResult} from '../ml_parser/parser'; import {ParseTreeResult} from '../ml_parser/parser';
import {digestMessage} from './digest';
import * as i18n from './i18n_ast'; import * as i18n from './i18n_ast';
import {createI18nMessageFactory} from './i18n_parser'; import {createI18nMessageFactory} from './i18n_parser';
import {I18nError} from './parse_util'; import {I18nError} from './parse_util';
@ -214,8 +213,8 @@ class _Visitor implements html.Visitor {
// Extract only top level nodes with the (implicit) "i18n" attribute if not in a block or an ICU // Extract only top level nodes with the (implicit) "i18n" attribute if not in a block or an ICU
// message // message
const i18nAttr = _getI18nAttr(el); const i18nAttr = _getI18nAttr(el);
const isImplicit = this._implicitTags.some((tag: string): boolean => el.name === tag) && const isImplicit = this._implicitTags.some(tag => el.name === tag) && !this._inIcu &&
!this._inIcu && !this._isInTranslatableSection; !this._isInTranslatableSection;
const isTopLevelImplicit = !wasInImplicitNode && isImplicit; const isTopLevelImplicit = !wasInImplicitNode && isImplicit;
this._inImplicitNode = this._inImplicitNode || isImplicit; this._inImplicitNode = this._inImplicitNode || isImplicit;
@ -348,14 +347,14 @@ class _Visitor implements html.Visitor {
// no-op when called in extraction mode (returns []) // no-op when called in extraction mode (returns [])
private _translateMessage(el: html.Node, message: i18n.Message): html.Node[] { private _translateMessage(el: html.Node, message: i18n.Message): html.Node[] {
if (message && this._mode === _VisitorMode.Merge) { if (message && this._mode === _VisitorMode.Merge) {
const id = digestMessage(message); const nodes = this._translations.get(message);
const nodes = this._translations.get(id);
if (nodes) { if (nodes) {
return nodes; return nodes;
} }
this._reportError(el, `Translation unavailable for message id="${id}"`); this._reportError(
el, `Translation unavailable for message id="${this._translations.digest(message)}"`);
} }
return []; return [];
@ -384,19 +383,20 @@ class _Visitor implements html.Visitor {
if (attr.value && attr.value != '' && i18nAttributeMeanings.hasOwnProperty(attr.name)) { if (attr.value && attr.value != '' && i18nAttributeMeanings.hasOwnProperty(attr.name)) {
const meaning = i18nAttributeMeanings[attr.name]; const meaning = i18nAttributeMeanings[attr.name];
const message: i18n.Message = this._createI18nMessage([attr], meaning, ''); const message: i18n.Message = this._createI18nMessage([attr], meaning, '');
const id = digestMessage(message); const nodes = this._translations.get(message);
const nodes = this._translations.get(id);
if (nodes) { if (nodes) {
if (nodes[0] instanceof html.Text) { if (nodes[0] instanceof html.Text) {
const value = (nodes[0] as html.Text).value; const value = (nodes[0] as html.Text).value;
translatedAttributes.push(new html.Attribute(attr.name, value, attr.sourceSpan)); translatedAttributes.push(new html.Attribute(attr.name, value, attr.sourceSpan));
} else { } else {
this._reportError( this._reportError(
el, `Unexpected translation for attribute "${attr.name}" (id="${id}")`); el,
`Unexpected translation for attribute "${attr.name}" (id="${this._translations.digest(message)}")`);
} }
} else { } else {
this._reportError( this._reportError(
el, `Translation unavailable for attribute "${attr.name}" (id="${id}")`); el,
`Translation unavailable for attribute "${attr.name}" (id="${this._translations.digest(message)}")`);
} }
} else { } else {
translatedAttributes.push(attr); translatedAttributes.push(attr);

View File

@ -12,18 +12,20 @@ export class Message {
/** /**
* @param nodes message AST * @param nodes message AST
* @param placeholders maps placeholder names to static content * @param placeholders maps placeholder names to static content
* @param placeholderToMsgIds maps placeholder names to translatable message IDs (used for ICU * @param placeholderToMessage maps placeholder names to messages (used for nested ICU messages)
* messages)
* @param meaning * @param meaning
* @param description * @param description
*/ */
constructor( constructor(
public nodes: Node[], public placeholders: {[name: string]: string}, public nodes: Node[], public placeholders: {[phName: string]: string},
public placeholderToMsgIds: {[name: string]: string}, public meaning: string, public placeholderToMessage: {[phName: string]: Message}, public meaning: string,
public description: string) {} public description: string) {}
} }
export interface Node { visit(visitor: Visitor, context?: any): any; } export interface Node {
sourceSpan: ParseSourceSpan;
visit(visitor: Visitor, context?: any): any;
}
export class Text implements Node { export class Text implements Node {
constructor(public value: string, public sourceSpan: ParseSourceSpan) {} constructor(public value: string, public sourceSpan: ParseSourceSpan) {}
@ -31,6 +33,7 @@ export class Text implements Node {
visit(visitor: Visitor, context?: any): any { return visitor.visitText(this, context); } visit(visitor: Visitor, context?: any): any { return visitor.visitText(this, context); }
} }
// TODO(vicb): do we really need this node (vs an array) ?
export class Container implements Node { export class Container implements Node {
constructor(public children: Node[], public sourceSpan: ParseSourceSpan) {} constructor(public children: Node[], public sourceSpan: ParseSourceSpan) {}
@ -38,6 +41,7 @@ export class Container implements Node {
} }
export class Icu implements Node { export class Icu implements Node {
public expressionPlaceholder: string;
constructor( constructor(
public expression: string, public type: string, public cases: {[k: string]: Node}, public expression: string, public type: string, public cases: {[k: string]: Node},
public sourceSpan: ParseSourceSpan) {} public sourceSpan: ParseSourceSpan) {}
@ -55,13 +59,13 @@ export class TagPlaceholder implements Node {
} }
export class Placeholder implements Node { export class Placeholder implements Node {
constructor(public value: string, public name: string = '', public sourceSpan: ParseSourceSpan) {} constructor(public value: string, public name: string, public sourceSpan: ParseSourceSpan) {}
visit(visitor: Visitor, context?: any): any { return visitor.visitPlaceholder(this, context); } visit(visitor: Visitor, context?: any): any { return visitor.visitPlaceholder(this, context); }
} }
export class IcuPlaceholder implements Node { export class IcuPlaceholder implements Node {
constructor(public value: Icu, public name: string = '', public sourceSpan: ParseSourceSpan) {} constructor(public value: Icu, public name: string, public sourceSpan: ParseSourceSpan) {}
visit(visitor: Visitor, context?: any): any { return visitor.visitIcuPlaceholder(this, context); } visit(visitor: Visitor, context?: any): any { return visitor.visitIcuPlaceholder(this, context); }
} }

View File

@ -11,7 +11,6 @@ import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../ml_parser/in
import {ParseTreeResult} from '../ml_parser/parser'; import {ParseTreeResult} from '../ml_parser/parser';
import {mergeTranslations} from './extractor_merger'; import {mergeTranslations} from './extractor_merger';
import {MessageBundle} from './message_bundle';
import {Serializer} from './serializers/serializer'; import {Serializer} from './serializers/serializer';
import {Xliff} from './serializers/xliff'; import {Xliff} from './serializers/xliff';
import {Xmb} from './serializers/xmb'; import {Xmb} from './serializers/xmb';
@ -41,32 +40,29 @@ export class I18NHtmlParser implements HtmlParser {
} }
// TODO(vicb): add support for implicit tags / attributes // TODO(vicb): add support for implicit tags / attributes
const messageBundle = new MessageBundle(this._htmlParser, [], {});
const errors = messageBundle.updateFromTemplate(source, url, interpolationConfig);
if (errors && errors.length) { if (parseResult.errors.length) {
return new ParseTreeResult(parseResult.rootNodes, parseResult.errors.concat(errors)); return new ParseTreeResult(parseResult.rootNodes, parseResult.errors);
} }
const serializer = this._createSerializer(interpolationConfig); const serializer = this._createSerializer();
const translationBundle = const translationBundle = TranslationBundle.load(this._translations, url, serializer);
TranslationBundle.load(this._translations, url, messageBundle, serializer);
return mergeTranslations(parseResult.rootNodes, translationBundle, interpolationConfig, [], {}); return mergeTranslations(parseResult.rootNodes, translationBundle, interpolationConfig, [], {});
} }
private _createSerializer(interpolationConfig: InterpolationConfig): Serializer { private _createSerializer(): Serializer {
const format = (this._translationsFormat || 'xlf').toLowerCase(); const format = (this._translationsFormat || 'xlf').toLowerCase();
switch (format) { switch (format) {
case 'xmb': case 'xmb':
return new Xmb(); return new Xmb();
case 'xtb': case 'xtb':
return new Xtb(this._htmlParser, interpolationConfig); return new Xtb();
case 'xliff': case 'xliff':
case 'xlf': case 'xlf':
default: default:
return new Xliff(this._htmlParser, interpolationConfig); return new Xliff();
} }
} }
} }

View File

@ -12,7 +12,6 @@ import * as html from '../ml_parser/ast';
import {getHtmlTagDefinition} from '../ml_parser/html_tags'; import {getHtmlTagDefinition} from '../ml_parser/html_tags';
import {InterpolationConfig} from '../ml_parser/interpolation_config'; import {InterpolationConfig} from '../ml_parser/interpolation_config';
import {ParseSourceSpan} from '../parse_util'; import {ParseSourceSpan} from '../parse_util';
import {digestMessage} from './digest';
import * as i18n from './i18n_ast'; import * as i18n from './i18n_ast';
import {PlaceholderRegistry} from './serializers/placeholder'; import {PlaceholderRegistry} from './serializers/placeholder';
@ -34,8 +33,8 @@ class _I18nVisitor implements html.Visitor {
private _isIcu: boolean; private _isIcu: boolean;
private _icuDepth: number; private _icuDepth: number;
private _placeholderRegistry: PlaceholderRegistry; private _placeholderRegistry: PlaceholderRegistry;
private _placeholderToContent: {[name: string]: string}; private _placeholderToContent: {[phName: string]: string};
private _placeholderToIds: {[name: string]: string}; private _placeholderToMessage: {[phName: string]: i18n.Message};
constructor( constructor(
private _expressionParser: ExpressionParser, private _expressionParser: ExpressionParser,
@ -46,12 +45,12 @@ class _I18nVisitor implements html.Visitor {
this._icuDepth = 0; this._icuDepth = 0;
this._placeholderRegistry = new PlaceholderRegistry(); this._placeholderRegistry = new PlaceholderRegistry();
this._placeholderToContent = {}; this._placeholderToContent = {};
this._placeholderToIds = {}; this._placeholderToMessage = {};
const i18nodes: i18n.Node[] = html.visitAll(this, nodes, {}); const i18nodes: i18n.Node[] = html.visitAll(this, nodes, {});
return new i18n.Message( return new i18n.Message(
i18nodes, this._placeholderToContent, this._placeholderToIds, meaning, description); i18nodes, this._placeholderToContent, this._placeholderToMessage, meaning, description);
} }
visitElement(el: html.Element, context: any): i18n.Node { visitElement(el: html.Element, context: any): i18n.Node {
@ -99,7 +98,13 @@ class _I18nVisitor implements html.Visitor {
this._icuDepth--; this._icuDepth--;
if (this._isIcu || this._icuDepth > 0) { if (this._isIcu || this._icuDepth > 0) {
// If the message (vs a part of the message) is an ICU message returns it // Returns an ICU node when:
// - the message (vs a part of the message) is an ICU message, or
// - the ICU message is nested.
const expPh = this._placeholderRegistry.getUniquePlaceholder(`VAR_${icu.type}`);
i18nIcu.expressionPlaceholder = expPh;
this._placeholderToContent[expPh] = icu.switchValue;
return i18nIcu; return i18nIcu;
} }
@ -110,7 +115,7 @@ class _I18nVisitor implements html.Visitor {
// TODO(vicb): add a html.Node -> i18n.Message cache to avoid having to re-create the msg // TODO(vicb): add a html.Node -> i18n.Message cache to avoid having to re-create the msg
const phName = this._placeholderRegistry.getPlaceholderName('ICU', icu.sourceSpan.toString()); const phName = this._placeholderRegistry.getPlaceholderName('ICU', icu.sourceSpan.toString());
const visitor = new _I18nVisitor(this._expressionParser, this._interpolationConfig); const visitor = new _I18nVisitor(this._expressionParser, this._interpolationConfig);
this._placeholderToIds[phName] = digestMessage(visitor.toI18nMessage([icu], '', '')); this._placeholderToMessage[phName] = visitor.toI18nMessage([icu], '', '');
return new i18n.IcuPlaceholder(i18nIcu, phName, icu.sourceSpan); return new i18n.IcuPlaceholder(i18nIcu, phName, icu.sourceSpan);
} }

View File

@ -10,7 +10,6 @@ import {HtmlParser} from '../ml_parser/html_parser';
import {InterpolationConfig} from '../ml_parser/interpolation_config'; import {InterpolationConfig} from '../ml_parser/interpolation_config';
import {ParseError} from '../parse_util'; import {ParseError} from '../parse_util';
import {digestMessage} from './digest';
import {extractMessages} from './extractor_merger'; import {extractMessages} from './extractor_merger';
import {Message} from './i18n_ast'; import {Message} from './i18n_ast';
import {Serializer} from './serializers/serializer'; import {Serializer} from './serializers/serializer';
@ -19,7 +18,7 @@ import {Serializer} from './serializers/serializer';
* A container for message extracted from the templates. * A container for message extracted from the templates.
*/ */
export class MessageBundle { export class MessageBundle {
private _messageMap: {[id: string]: Message} = {}; private _messages: Message[] = [];
constructor( constructor(
private _htmlParser: HtmlParser, private _implicitTags: string[], private _htmlParser: HtmlParser, private _implicitTags: string[],
@ -40,11 +39,10 @@ export class MessageBundle {
return i18nParserResult.errors; return i18nParserResult.errors;
} }
i18nParserResult.messages.forEach( this._messages.push(...i18nParserResult.messages);
(message) => { this._messageMap[digestMessage(message)] = message; });
} }
getMessageMap(): {[id: string]: Message} { return this._messageMap; } getMessages(): Message[] { return this._messages; }
write(serializer: Serializer): string { return serializer.write(this._messageMap); } write(serializer: Serializer): string { return serializer.write(this._messages); }
} }

View File

@ -40,7 +40,9 @@ const TAG_TO_PLACEHOLDER_NAMES: {[k: string]: string} = {
}; };
/** /**
* Creates unique names for placeholder with different content * Creates unique names for placeholder with different content.
*
* Returns the same placeholder name when the content is identical.
* *
* @internal * @internal
*/ */
@ -93,6 +95,10 @@ export class PlaceholderRegistry {
return uniqueName; return uniqueName;
} }
getUniquePlaceholder(name: string): string {
return this._generateUniqueName(name.toUpperCase());
}
// Generate a hash for a tag - does not take attribute order into account // Generate a hash for a tag - does not take attribute order into account
private _hashTag(tag: string, attrs: {[k: string]: string}, isVoid: boolean): string { private _hashTag(tag: string, attrs: {[k: string]: string}, isVoid: boolean): string {
const start = `<${tag}`; const start = `<${tag}`;
@ -105,18 +111,8 @@ export class PlaceholderRegistry {
private _hashClosingTag(tag: string): string { return this._hashTag(`/${tag}`, {}, false); } private _hashClosingTag(tag: string): string { return this._hashTag(`/${tag}`, {}, false); }
private _generateUniqueName(base: string): string { private _generateUniqueName(base: string): string {
let name = base; const next = this._placeHolderNameCounts[base];
let next = this._placeHolderNameCounts[name]; this._placeHolderNameCounts[base] = next ? next + 1 : 1;
return next ? `${base}_${next}` : base;
if (!next) {
next = 1;
} else {
name += `_${next}`;
next++;
}
this._placeHolderNameCounts[base] = next;
return name;
} }
} }

View File

@ -6,36 +6,12 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import * as html from '../../ml_parser/ast';
import * as i18n from '../i18n_ast'; import * as i18n from '../i18n_ast';
import {MessageBundle} from '../message_bundle';
export interface Serializer { export interface Serializer {
write(messageMap: {[id: string]: i18n.Message}): string; write(messages: i18n.Message[]): string;
load(content: string, url: string, messageBundle: MessageBundle): {[id: string]: html.Node[]}; load(content: string, url: string): {[msgId: string]: i18n.Node[]};
}
digest(message: i18n.Message): string;
// Generate a map of placeholder to content indexed by message ids
export function extractPlaceholders(messageBundle: MessageBundle) {
const messageMap = messageBundle.getMessageMap();
const placeholders: {[id: string]: {[name: string]: string}} = {};
Object.keys(messageMap).forEach(msgId => {
placeholders[msgId] = messageMap[msgId].placeholders;
});
return placeholders;
}
// Generate a map of placeholder to message ids indexed by message ids
export function extractPlaceholderToIds(messageBundle: MessageBundle) {
const messageMap = messageBundle.getMessageMap();
const placeholderToIds: {[id: string]: {[name: string]: string}} = {};
Object.keys(messageMap).forEach(msgId => {
placeholderToIds[msgId] = messageMap[msgId].placeholderToMsgIds;
});
return placeholderToIds;
} }

View File

@ -7,15 +7,12 @@
*/ */
import * as ml from '../../ml_parser/ast'; import * as ml from '../../ml_parser/ast';
import {HtmlParser} from '../../ml_parser/html_parser';
import {InterpolationConfig} from '../../ml_parser/interpolation_config';
import {XmlParser} from '../../ml_parser/xml_parser'; import {XmlParser} from '../../ml_parser/xml_parser';
import {ParseError} from '../../parse_util'; import {digest} from '../digest';
import * as i18n from '../i18n_ast'; import * as i18n from '../i18n_ast';
import {MessageBundle} from '../message_bundle';
import {I18nError} from '../parse_util'; import {I18nError} from '../parse_util';
import {Serializer, extractPlaceholderToIds, extractPlaceholders} from './serializer'; import {Serializer} from './serializer';
import * as xml from './xml_helper'; import * as xml from './xml_helper';
const _VERSION = '1.2'; const _VERSION = '1.2';
@ -23,6 +20,7 @@ const _XMLNS = 'urn:oasis:names:tc:xliff:document:1.2';
// TODO(vicb): make this a param (s/_/-/) // TODO(vicb): make this a param (s/_/-/)
const _SOURCE_LANG = 'en'; const _SOURCE_LANG = 'en';
const _PLACEHOLDER_TAG = 'x'; const _PLACEHOLDER_TAG = 'x';
const _SOURCE_TAG = 'source'; const _SOURCE_TAG = 'source';
const _TARGET_TAG = 'target'; const _TARGET_TAG = 'target';
const _UNIT_TAG = 'trans-unit'; const _UNIT_TAG = 'trans-unit';
@ -30,17 +28,19 @@ const _UNIT_TAG = 'trans-unit';
// http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html // http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html
// http://docs.oasis-open.org/xliff/v1.2/xliff-profile-html/xliff-profile-html-1.2.html // http://docs.oasis-open.org/xliff/v1.2/xliff-profile-html/xliff-profile-html-1.2.html
export class Xliff implements Serializer { export class Xliff implements Serializer {
constructor(private _htmlParser: HtmlParser, private _interpolationConfig: InterpolationConfig) {} write(messages: i18n.Message[]): string {
write(messageMap: {[id: string]: i18n.Message}): string {
const visitor = new _WriteVisitor(); const visitor = new _WriteVisitor();
const visited: {[id: string]: boolean} = {};
const transUnits: xml.Node[] = []; const transUnits: xml.Node[] = [];
Object.keys(messageMap).forEach((id) => { messages.forEach(message => {
const message = messageMap[id]; const id = this.digest(message);
const transUnit = new xml.Tag(_UNIT_TAG, {id: id, datatype: 'html'}); // deduplicate messages
if (visited[id]) return;
visited[id] = true;
const transUnit = new xml.Tag(_UNIT_TAG, {id, datatype: 'html'});
transUnit.children.push( transUnit.children.push(
new xml.CR(8), new xml.Tag(_SOURCE_TAG, {}, visitor.serialize(message.nodes)), new xml.CR(8), new xml.Tag(_SOURCE_TAG, {}, visitor.serialize(message.nodes)),
new xml.CR(8), new xml.Tag(_TARGET_TAG)); new xml.CR(8), new xml.Tag(_TARGET_TAG));
@ -75,38 +75,28 @@ export class Xliff implements Serializer {
]); ]);
} }
load(content: string, url: string, messageBundle: MessageBundle): {[id: string]: ml.Node[]} { load(content: string, url: string): {[msgId: string]: i18n.Node[]} {
// Parse the xtb file into xml nodes // xliff to xml nodes
const result = new XmlParser().parse(content, url); const xliffParser = new XliffParser();
const {mlNodesByMsgId, errors} = xliffParser.parse(content, url);
if (result.errors.length) { // xml nodes to i18n nodes
throw new Error(`xtb parse errors:\n${result.errors.join('\n')}`); const i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {};
} const converter = new XmlToI18n();
Object.keys(mlNodesByMsgId).forEach(msgId => {
// Replace the placeholders, messages are now string const {i18nNodes, errors: e} = converter.convert(mlNodesByMsgId[msgId]);
const {messages, errors} = new _LoadVisitor().parse(result.rootNodes, messageBundle); errors.push(...e);
i18nNodesByMsgId[msgId] = i18nNodes;
if (errors.length) {
throw new Error(`xtb parse errors:\n${errors.join('\n')}`);
}
// Convert the string messages to html ast
// TODO(vicb): map error message back to the original message in xtb
const messageMap: {[id: string]: ml.Node[]} = {};
const parseErrors: ParseError[] = [];
Object.keys(messages).forEach((id) => {
const res = this._htmlParser.parse(messages[id], url, true, this._interpolationConfig);
parseErrors.push(...res.errors);
messageMap[id] = res.rootNodes;
}); });
if (parseErrors.length) { if (errors.length) {
throw new Error(`xtb parse errors:\n${parseErrors.join('\n')}`); throw new Error(`xliff parse errors:\n${errors.join('\n')}`);
} }
return messageMap; return i18nNodesByMsgId;
} }
digest(message: i18n.Message): string { return digest(message); }
} }
class _WriteVisitor implements i18n.Visitor { class _WriteVisitor implements i18n.Visitor {
@ -166,75 +156,46 @@ class _WriteVisitor implements i18n.Visitor {
} }
// TODO(vicb): add error management (structure) // TODO(vicb): add error management (structure)
// TODO(vicb): factorize (xtb) ? // Extract messages as xml nodes from the xliff file
class _LoadVisitor implements ml.Visitor { class XliffParser implements ml.Visitor {
private _messageNodes: [string, ml.Node[]][]; private _unitMlNodes: ml.Node[];
private _translatedMessages: {[id: string]: string};
private _msgId: string;
private _target: ml.Node[];
private _errors: I18nError[]; private _errors: I18nError[];
private _placeholders: {[name: string]: string}; private _mlNodesByMsgId: {[msgId: string]: ml.Node[]};
private _placeholderToIds: {[name: string]: string};
parse(nodes: ml.Node[], messageBundle: MessageBundle): parse(xliff: string, url: string) {
{messages: {[k: string]: string}, errors: I18nError[]} { this._unitMlNodes = [];
this._messageNodes = []; this._mlNodesByMsgId = {};
this._translatedMessages = {};
this._msgId = '';
this._target = [];
this._errors = [];
// Find all messages const xml = new XmlParser().parse(xliff, url, false);
ml.visitAll(this, nodes, null);
const messageMap = messageBundle.getMessageMap(); this._errors = xml.errors;
const placeholders = extractPlaceholders(messageBundle); ml.visitAll(this, xml.rootNodes, null);
const placeholderToIds = extractPlaceholderToIds(messageBundle);
this._messageNodes return {
.filter(message => { mlNodesByMsgId: this._mlNodesByMsgId,
// Remove any messages that is not present in the source message bundle. errors: this._errors,
return messageMap.hasOwnProperty(message[0]); };
})
.sort((a, b) => {
// Because there could be no ICU placeholders inside an ICU message,
// we do not need to take into account the `placeholderToMsgIds` of the referenced
// messages, those would always be empty
// TODO(vicb): overkill - create 2 buckets and [...woDeps, ...wDeps].process()
if (Object.keys(messageMap[a[0]].placeholderToMsgIds).length == 0) {
return -1;
}
if (Object.keys(messageMap[b[0]].placeholderToMsgIds).length == 0) {
return 1;
}
return 0;
})
.forEach(message => {
const id = message[0];
this._placeholders = placeholders[id] || {};
this._placeholderToIds = placeholderToIds[id] || {};
// TODO(vicb): make sure there is no `_TRANSLATIONS_TAG` nor `_TRANSLATION_TAG`
this._translatedMessages[id] = ml.visitAll(this, message[1]).join('');
});
return {messages: this._translatedMessages, errors: this._errors};
} }
visitElement(element: ml.Element, context: any): any { visitElement(element: ml.Element, context: any): any {
switch (element.name) { switch (element.name) {
case _UNIT_TAG: case _UNIT_TAG:
this._target = null; this._unitMlNodes = null;
const msgId = element.attrs.find((attr) => attr.name === 'id'); const idAttr = element.attrs.find((attr) => attr.name === 'id');
if (!msgId) { if (!idAttr) {
this._addError(element, `<${_UNIT_TAG}> misses the "id" attribute`); this._addError(element, `<${_UNIT_TAG}> misses the "id" attribute`);
} else { } else {
this._msgId = msgId.value; const id = idAttr.value;
} if (this._mlNodesByMsgId.hasOwnProperty(id)) {
this._addError(element, `Duplicated translations for msg ${id}`);
} else {
ml.visitAll(this, element.children, null); ml.visitAll(this, element.children, null);
if (this._msgId !== null) { if (this._unitMlNodes) {
this._messageNodes.push([this._msgId, this._target]); this._mlNodesByMsgId[id] = this._unitMlNodes;
} else {
this._addError(element, `Message ${id} misses a translation`);
}
}
} }
break; break;
@ -243,49 +204,66 @@ class _LoadVisitor implements ml.Visitor {
break; break;
case _TARGET_TAG: case _TARGET_TAG:
this._target = element.children; this._unitMlNodes = element.children;
break;
case _PLACEHOLDER_TAG:
const idAttr = element.attrs.find((attr) => attr.name === 'id');
if (!idAttr) {
this._addError(element, `<${_PLACEHOLDER_TAG}> misses the "id" attribute`);
} else {
const id = idAttr.value;
if (this._placeholders.hasOwnProperty(id)) {
return this._placeholders[id];
}
if (this._placeholderToIds.hasOwnProperty(id) &&
this._translatedMessages.hasOwnProperty(this._placeholderToIds[id])) {
return this._translatedMessages[this._placeholderToIds[id]];
}
// TODO(vicb): better error message for when
// !this._translatedMessages.hasOwnProperty(this._placeholderToIds[id])
this._addError(element, `The placeholder "${id}" does not exists in the source message`);
}
break; break;
default: default:
// TODO(vicb): assert file structure, xliff version
// For now only recurse on unhandled nodes
ml.visitAll(this, element.children, null); ml.visitAll(this, element.children, null);
} }
} }
visitAttribute(attribute: ml.Attribute, context: any): any { visitAttribute(attribute: ml.Attribute, context: any): any {}
throw new Error('unreachable code');
visitText(text: ml.Text, context: any): any {}
visitComment(comment: ml.Comment, context: any): any {}
visitExpansion(expansion: ml.Expansion, context: any): any {}
visitExpansionCase(expansionCase: ml.ExpansionCase, context: any): any {}
private _addError(node: ml.Node, message: string): void {
this._errors.push(new I18nError(node.sourceSpan, message));
}
} }
visitText(text: ml.Text, context: any): any { return text.value; } // Convert ml nodes (xliff syntax) to i18n nodes
class XmlToI18n implements ml.Visitor {
private _errors: I18nError[];
visitComment(comment: ml.Comment, context: any): any { return ''; } convert(nodes: ml.Node[]) {
this._errors = [];
visitExpansion(expansion: ml.Expansion, context: any): any { return {
throw new Error('unreachable code'); i18nNodes: ml.visitAll(this, nodes),
errors: this._errors,
};
} }
visitExpansionCase(expansionCase: ml.ExpansionCase, context: any): any { visitText(text: ml.Text, context: any) { return new i18n.Text(text.value, text.sourceSpan); }
throw new Error('unreachable code');
visitElement(el: ml.Element, context: any): i18n.Placeholder {
if (el.name === _PLACEHOLDER_TAG) {
const nameAttr = el.attrs.find((attr) => attr.name === 'id');
if (nameAttr) {
return new i18n.Placeholder('', nameAttr.value, el.sourceSpan);
} }
this._addError(el, `<${_PLACEHOLDER_TAG}> misses the "id" attribute`);
} else {
this._addError(el, `Unexpected tag`);
}
}
visitExpansion(icu: ml.Expansion, context: any) {}
visitExpansionCase(icuCase: ml.ExpansionCase, context: any): any {}
visitComment(comment: ml.Comment, context: any) {}
visitAttribute(attribute: ml.Attribute, context: any) {}
private _addError(node: ml.Node, message: string): void { private _addError(node: ml.Node, message: string): void {
this._errors.push(new I18nError(node.sourceSpan, message)); this._errors.push(new I18nError(node.sourceSpan, message));
} }

View File

@ -6,9 +6,8 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import * as html from '../../ml_parser/ast'; import {decimalDigest} from '../digest';
import * as i18n from '../i18n_ast'; import * as i18n from '../i18n_ast';
import {MessageBundle} from '../message_bundle';
import {Serializer} from './serializer'; import {Serializer} from './serializer';
import * as xml from './xml_helper'; import * as xml from './xml_helper';
@ -39,12 +38,18 @@ const _DOCTYPE = `<!ELEMENT messagebundle (msg)*>
<!ELEMENT ex (#PCDATA)>`; <!ELEMENT ex (#PCDATA)>`;
export class Xmb implements Serializer { export class Xmb implements Serializer {
write(messageMap: {[k: string]: i18n.Message}): string { write(messages: i18n.Message[]): string {
const visitor = new _Visitor(); const visitor = new _Visitor();
const rootNode = new xml.Tag(_MESSAGES_TAG); const visited: {[id: string]: boolean} = {};
let rootNode = new xml.Tag(_MESSAGES_TAG);
messages.forEach(message => {
const id = this.digest(message);
// deduplicate messages
if (visited[id]) return;
visited[id] = true;
Object.keys(messageMap).forEach((id) => {
const message = messageMap[id];
const attrs: {[k: string]: string} = {id}; const attrs: {[k: string]: string} = {id};
if (message.description) { if (message.description) {
@ -71,9 +76,11 @@ export class Xmb implements Serializer {
]); ]);
} }
load(content: string, url: string, messageBundle: MessageBundle): {[id: string]: html.Node[]} { load(content: string, url: string): {[msgId: string]: i18n.Node[]} {
throw new Error('Unsupported'); throw new Error('Unsupported');
} }
digest(message: i18n.Message): string { return digest(message); }
} }
class _Visitor implements i18n.Visitor { class _Visitor implements i18n.Visitor {
@ -86,7 +93,7 @@ class _Visitor implements i18n.Visitor {
} }
visitIcu(icu: i18n.Icu, context?: any): xml.Node[] { visitIcu(icu: i18n.Icu, context?: any): xml.Node[] {
const nodes = [new xml.Text(`{${icu.expression}, ${icu.type}, `)]; const nodes = [new xml.Text(`{${icu.expressionPlaceholder}, ${icu.type}, `)];
Object.keys(icu.cases).forEach((c: string) => { Object.keys(icu.cases).forEach((c: string) => {
nodes.push(new xml.Text(`${c} {`), ...icu.cases[c].visit(this), new xml.Text(`} `)); nodes.push(new xml.Text(`${c} {`), ...icu.cases[c].visit(this), new xml.Text(`} `));
@ -123,3 +130,7 @@ class _Visitor implements i18n.Visitor {
return [].concat(...nodes.map(node => node.visit(this))); return [].concat(...nodes.map(node => node.visit(this)));
} }
} }
export function digest(message: i18n.Message): string {
return decimalDigest(message);
}

View File

@ -7,112 +7,63 @@
*/ */
import * as ml from '../../ml_parser/ast'; import * as ml from '../../ml_parser/ast';
import {HtmlParser} from '../../ml_parser/html_parser';
import {InterpolationConfig} from '../../ml_parser/interpolation_config';
import {XmlParser} from '../../ml_parser/xml_parser'; import {XmlParser} from '../../ml_parser/xml_parser';
import {ParseError} from '../../parse_util';
import * as i18n from '../i18n_ast'; import * as i18n from '../i18n_ast';
import {MessageBundle} from '../message_bundle';
import {I18nError} from '../parse_util'; import {I18nError} from '../parse_util';
import {Serializer, extractPlaceholderToIds, extractPlaceholders} from './serializer'; import {Serializer} from './serializer';
import {digest} from './xmb';
const _TRANSLATIONS_TAG = 'translationbundle'; const _TRANSLATIONS_TAG = 'translationbundle';
const _TRANSLATION_TAG = 'translation'; const _TRANSLATION_TAG = 'translation';
const _PLACEHOLDER_TAG = 'ph'; const _PLACEHOLDER_TAG = 'ph';
export class Xtb implements Serializer { export class Xtb implements Serializer {
constructor(private _htmlParser: HtmlParser, private _interpolationConfig: InterpolationConfig) {} write(messages: i18n.Message[]): string { throw new Error('Unsupported'); }
write(messageMap: {[id: string]: i18n.Message}): string { throw new Error('Unsupported'); } load(content: string, url: string): {[msgId: string]: i18n.Node[]} {
// xtb to xml nodes
const xtbParser = new XtbParser();
const {mlNodesByMsgId, errors} = xtbParser.parse(content, url);
load(content: string, url: string, messageBundle: MessageBundle): {[id: string]: ml.Node[]} { // xml nodes to i18n nodes
// Parse the xtb file into xml nodes const i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {};
const result = new XmlParser().parse(content, url); const converter = new XmlToI18n();
Object.keys(mlNodesByMsgId).forEach(msgId => {
if (result.errors.length) { const {i18nNodes, errors: e} = converter.convert(mlNodesByMsgId[msgId]);
throw new Error(`xtb parse errors:\n${result.errors.join('\n')}`); errors.push(...e);
} i18nNodesByMsgId[msgId] = i18nNodes;
});
// Replace the placeholders, messages are now string
const {messages, errors} = new _Visitor().parse(result.rootNodes, messageBundle);
if (errors.length) { if (errors.length) {
throw new Error(`xtb parse errors:\n${errors.join('\n')}`); throw new Error(`xtb parse errors:\n${errors.join('\n')}`);
} }
// Convert the string messages to html ast return i18nNodesByMsgId;
// TODO(vicb): map error message back to the original message in xtb
const messageMap: {[id: string]: ml.Node[]} = {};
const parseErrors: ParseError[] = [];
Object.keys(messages).forEach((id) => {
const res = this._htmlParser.parse(messages[id], url, true, this._interpolationConfig);
parseErrors.push(...res.errors);
messageMap[id] = res.rootNodes;
});
if (parseErrors.length) {
throw new Error(`xtb parse errors:\n${parseErrors.join('\n')}`);
} }
return messageMap; digest(message: i18n.Message): string { return digest(message); }
}
} }
class _Visitor implements ml.Visitor { // Extract messages as xml nodes from the xtb file
private _messageNodes: [string, ml.Node[]][]; class XtbParser implements ml.Visitor {
private _translatedMessages: {[id: string]: string};
private _bundleDepth: number; private _bundleDepth: number;
private _translationDepth: number;
private _errors: I18nError[]; private _errors: I18nError[];
private _placeholders: {[name: string]: string}; private _mlNodesByMsgId: {[msgId: string]: ml.Node[]};
private _placeholderToIds: {[name: string]: string};
parse(nodes: ml.Node[], messageBundle: MessageBundle): parse(xtb: string, url: string) {
{messages: {[k: string]: string}, errors: I18nError[]} {
this._messageNodes = [];
this._translatedMessages = {};
this._bundleDepth = 0; this._bundleDepth = 0;
this._translationDepth = 0; this._mlNodesByMsgId = {};
this._errors = [];
// Find all messages const xml = new XmlParser().parse(xtb, url, true);
ml.visitAll(this, nodes, null);
const messageMap = messageBundle.getMessageMap(); this._errors = xml.errors;
const placeholders = extractPlaceholders(messageBundle); ml.visitAll(this, xml.rootNodes);
const placeholderToIds = extractPlaceholderToIds(messageBundle);
this._messageNodes return {
.filter(message => { mlNodesByMsgId: this._mlNodesByMsgId,
// Remove any messages that is not present in the source message bundle. errors: this._errors,
return messageMap.hasOwnProperty(message[0]); };
})
.sort((a, b) => {
// Because there could be no ICU placeholders inside an ICU message,
// we do not need to take into account the `placeholderToMsgIds` of the referenced
// messages, those would always be empty
// TODO(vicb): overkill - create 2 buckets and [...woDeps, ...wDeps].process()
if (Object.keys(messageMap[a[0]].placeholderToMsgIds).length == 0) {
return -1;
}
if (Object.keys(messageMap[b[0]].placeholderToMsgIds).length == 0) {
return 1;
}
return 0;
})
.forEach(message => {
const id = message[0];
this._placeholders = placeholders[id] || {};
this._placeholderToIds = placeholderToIds[id] || {};
// TODO(vicb): make sure there is no `_TRANSLATIONS_TAG` nor `_TRANSLATION_TAG`
this._translatedMessages[id] = ml.visitAll(this, message[1]).join('');
});
return {messages: this._translatedMessages, errors: this._errors};
} }
visitElement(element: ml.Element, context: any): any { visitElement(element: ml.Element, context: any): any {
@ -127,40 +78,16 @@ class _Visitor implements ml.Visitor {
break; break;
case _TRANSLATION_TAG: case _TRANSLATION_TAG:
this._translationDepth++;
if (this._translationDepth > 1) {
this._addError(element, `<${_TRANSLATION_TAG}> elements can not be nested`);
}
const idAttr = element.attrs.find((attr) => attr.name === 'id'); const idAttr = element.attrs.find((attr) => attr.name === 'id');
if (!idAttr) { if (!idAttr) {
this._addError(element, `<${_TRANSLATION_TAG}> misses the "id" attribute`); this._addError(element, `<${_TRANSLATION_TAG}> misses the "id" attribute`);
} else { } else {
// ICU placeholders are reference to other messages. const id = idAttr.value;
// The referenced message might not have been decoded yet. if (this._mlNodesByMsgId.hasOwnProperty(id)) {
// We need to have all messages available to make sure deps are decoded first. this._addError(element, `Duplicated translations for msg ${id}`);
// TODO(vicb): report an error on duplicate id
this._messageNodes.push([idAttr.value, element.children]);
}
this._translationDepth--;
break;
case _PLACEHOLDER_TAG:
const nameAttr = element.attrs.find((attr) => attr.name === 'name');
if (!nameAttr) {
this._addError(element, `<${_PLACEHOLDER_TAG}> misses the "name" attribute`);
} else { } else {
const name = nameAttr.value; this._mlNodesByMsgId[id] = element.children;
if (this._placeholders.hasOwnProperty(name)) {
return this._placeholders[name];
} }
if (this._placeholderToIds.hasOwnProperty(name) &&
this._translatedMessages.hasOwnProperty(this._placeholderToIds[name])) {
return this._translatedMessages[this._placeholderToIds[name]];
}
// TODO(vicb): better error message for when
// !this._translatedMessages.hasOwnProperty(this._placeholderToIds[name])
this._addError(
element, `The placeholder "${name}" does not exists in the source message`);
} }
break; break;
@ -169,23 +96,68 @@ class _Visitor implements ml.Visitor {
} }
} }
visitAttribute(attribute: ml.Attribute, context: any): any { visitAttribute(attribute: ml.Attribute, context: any): any {}
throw new Error('unreachable code');
}
visitText(text: ml.Text, context: any): any { return text.value; } visitText(text: ml.Text, context: any): any {}
visitComment(comment: ml.Comment, context: any): any { return ''; } visitComment(comment: ml.Comment, context: any): any {}
visitExpansion(expansion: ml.Expansion, context: any): any { visitExpansion(expansion: ml.Expansion, context: any): any {}
const strCases = expansion.cases.map(c => c.visit(this, null));
return `{${expansion.switchValue}, ${expansion.type}, strCases.join(' ')}`; visitExpansionCase(expansionCase: ml.ExpansionCase, context: any): any {}
}
private _addError(node: ml.Node, message: string): void {
visitExpansionCase(expansionCase: ml.ExpansionCase, context: any): any { this._errors.push(new I18nError(node.sourceSpan, message));
return `${expansionCase.value} {${ml.visitAll(this, expansionCase.expression, null)}}`; }
} }
// Convert ml nodes (xtb syntax) to i18n nodes
class XmlToI18n implements ml.Visitor {
private _errors: I18nError[];
convert(nodes: ml.Node[]) {
this._errors = [];
return {
i18nNodes: ml.visitAll(this, nodes),
errors: this._errors,
};
}
visitText(text: ml.Text, context: any) { return new i18n.Text(text.value, text.sourceSpan); }
visitExpansion(icu: ml.Expansion, context: any) {
const caseMap: {[value: string]: i18n.Node} = {};
ml.visitAll(this, icu.cases).forEach(c => {
caseMap[c.value] = new i18n.Container(c.nodes, icu.sourceSpan);
});
return new i18n.Icu(icu.switchValue, icu.type, caseMap, icu.sourceSpan);
}
visitExpansionCase(icuCase: ml.ExpansionCase, context: any): any {
return {
value: icuCase.value,
nodes: ml.visitAll(this, icuCase.expression),
};
}
visitElement(el: ml.Element, context: any): i18n.Placeholder {
if (el.name === _PLACEHOLDER_TAG) {
const nameAttr = el.attrs.find((attr) => attr.name === 'name');
if (nameAttr) {
return new i18n.Placeholder('', nameAttr.value, el.sourceSpan);
}
this._addError(el, `<${_PLACEHOLDER_TAG}> misses the "name" attribute`);
} else {
this._addError(el, `Unexpected tag`);
}
}
visitComment(comment: ml.Comment, context: any) {}
visitAttribute(attribute: ml.Attribute, context: any) {}
private _addError(node: ml.Node, message: string): void { private _addError(node: ml.Node, message: string): void {
this._errors.push(new I18nError(node.sourceSpan, message)); this._errors.push(new I18nError(node.sourceSpan, message));

View File

@ -7,22 +7,120 @@
*/ */
import * as html from '../ml_parser/ast'; import * as html from '../ml_parser/ast';
import {HtmlParser} from '../ml_parser/html_parser';
import {MessageBundle} from './message_bundle'; import * as i18n from './i18n_ast';
import {I18nError} from './parse_util';
import {Serializer} from './serializers/serializer'; import {Serializer} from './serializers/serializer';
/** /**
* A container for translated messages * A container for translated messages
*/ */
export class TranslationBundle { export class TranslationBundle {
constructor(private _messageMap: {[id: string]: html.Node[]} = {}) {} private _i18nToHtml: I18nToHtmlVisitor;
static load(content: string, url: string, messageBundle: MessageBundle, serializer: Serializer): constructor(
TranslationBundle { private _i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {},
return new TranslationBundle(serializer.load(content, url, messageBundle)); public digest: (m: i18n.Message) => string) {
this._i18nToHtml = new I18nToHtmlVisitor(_i18nNodesByMsgId, digest);
} }
get(id: string): html.Node[] { return this._messageMap[id]; } static load(content: string, url: string, serializer: Serializer): TranslationBundle {
const i18nNodesByMsgId = serializer.load(content, url);
has(id: string): boolean { return id in this._messageMap; } const digestFn = (m: i18n.Message) => serializer.digest(m);
return new TranslationBundle(i18nNodesByMsgId, digestFn);
}
get(srcMsg: i18n.Message): html.Node[] {
const html = this._i18nToHtml.convert(srcMsg);
if (html.errors.length) {
throw new Error(html.errors.join('\n'));
}
return html.nodes;
}
has(srcMsg: i18n.Message): boolean { return this.digest(srcMsg) in this._i18nNodesByMsgId; }
}
class I18nToHtmlVisitor implements i18n.Visitor {
private _srcMsg: i18n.Message;
private _srcMsgStack: i18n.Message[] = [];
private _errors: I18nError[] = [];
constructor(
private _i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {},
private _digest: (m: i18n.Message) => string) {}
convert(srcMsg: i18n.Message): {nodes: html.Node[], errors: I18nError[]} {
this._srcMsgStack.length = 0;
this._errors.length = 0;
// i18n to text
const text = this._convertToText(srcMsg);
// text to html
const url = srcMsg.nodes[0].sourceSpan.start.file.url;
const html = new HtmlParser().parse(text, url, true);
return {
nodes: html.rootNodes,
errors: [...this._errors, ...html.errors],
};
}
visitText(text: i18n.Text, context?: any): string { return text.value; }
visitContainer(container: i18n.Container, context?: any): any {
return container.children.map(n => n.visit(this)).join('');
}
visitIcu(icu: i18n.Icu, context?: any): any {
const cases = Object.keys(icu.cases).map(k => `${k} {${icu.cases[k].visit(this)}}`);
// TODO(vicb): Once all format switch to using expression placeholders
// we should throw when the placeholder is not in the source message
const exp = this._srcMsg.placeholders.hasOwnProperty(icu.expression) ?
this._srcMsg.placeholders[icu.expression] :
icu.expression;
return `{${exp}, ${icu.type}, ${cases.join(' ')}}`;
}
visitPlaceholder(ph: i18n.Placeholder, context?: any): string {
const phName = ph.name;
if (this._srcMsg.placeholders.hasOwnProperty(phName)) {
return this._srcMsg.placeholders[phName];
}
if (this._srcMsg.placeholderToMessage.hasOwnProperty(phName)) {
return this._convertToText(this._srcMsg.placeholderToMessage[phName]);
}
this._addError(ph, `Unknown placeholder`);
return '';
}
visitTagPlaceholder(ph: i18n.TagPlaceholder, context?: any): any { throw 'unreachable code'; }
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): any { throw 'unreachable code'; }
private _convertToText(srcMsg: i18n.Message): string {
const digest = this._digest(srcMsg);
if (this._i18nNodesByMsgId.hasOwnProperty(digest)) {
this._srcMsgStack.push(this._srcMsg);
this._srcMsg = srcMsg;
const nodes = this._i18nNodesByMsgId[digest];
const text = nodes.map(node => node.visit(this)).join('');
this._srcMsg = this._srcMsgStack.pop();
return text;
}
this._addError(srcMsg.nodes[0], `Missing translation for message ${digest}`);
return '';
}
private _addError(el: i18n.Node, msg: string) {
this._errors.push(new I18nError(el.sourceSpan, msg));
}
} }

View File

@ -9,7 +9,7 @@
import {ANALYZE_FOR_ENTRY_COMPONENTS, AnimationTransitionEvent, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, LOCALE_ID, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core'; import {ANALYZE_FOR_ENTRY_COMPONENTS, AnimationTransitionEvent, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, LOCALE_ID, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
import {StaticSymbol, isStaticSymbol} from './aot/static_symbol'; import {StaticSymbol, isStaticSymbol} from './aot/static_symbol';
import {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata'; import {CompileIdentifierMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName} from './compile_metadata';
import {AnimationGroupPlayer, AnimationKeyframe, AnimationSequencePlayer, AnimationStyles, AnimationTransition, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, ComponentRef_, DebugAppView, DebugContext, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, UNINITIALIZED, ValueUnwrapper, ViewContainer, ViewType, balanceAnimationKeyframes, clearStyles, collectAndResolveStyles, devModeEqual, prepareFinalAnimationStyles, reflector, registerModuleFactory, renderStyles, view_utils} from './private_import_core'; import {AnimationGroupPlayer, AnimationKeyframe, AnimationSequencePlayer, AnimationStyles, AnimationTransition, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, ComponentRef_, DebugAppView, DebugContext, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, UNINITIALIZED, ValueUnwrapper, ViewContainer, ViewType, balanceAnimationKeyframes, clearStyles, collectAndResolveStyles, devModeEqual, prepareFinalAnimationStyles, reflector, registerModuleFactory, renderStyles, view_utils} from './private_import_core';
const APP_VIEW_MODULE_URL = assetUrl('core', 'linker/view'); const APP_VIEW_MODULE_URL = assetUrl('core', 'linker/view');
@ -347,27 +347,25 @@ export function assetUrl(pkg: string, path: string = null, type: string = 'src')
} }
export function resolveIdentifier(identifier: IdentifierSpec) { export function resolveIdentifier(identifier: IdentifierSpec) {
let moduleUrl = identifier.moduleUrl; return reflector.resolveIdentifier(identifier.name, identifier.moduleUrl, identifier.runtime);
}
export function createIdentifier(identifier: IdentifierSpec): CompileIdentifierMetadata {
const reference = const reference =
reflector.resolveIdentifier(identifier.name, identifier.moduleUrl, identifier.runtime); reflector.resolveIdentifier(identifier.name, identifier.moduleUrl, identifier.runtime);
if (isStaticSymbol(reference)) { return {reference: reference};
moduleUrl = reference.filePath;
}
return new CompileIdentifierMetadata(
{name: identifier.name, moduleUrl: moduleUrl, reference: reference});
} }
export function identifierToken(identifier: CompileIdentifierMetadata): CompileTokenMetadata { export function identifierToken(identifier: CompileIdentifierMetadata): CompileTokenMetadata {
return new CompileTokenMetadata({identifier: identifier}); return {identifier: identifier};
} }
export function resolveIdentifierToken(identifier: IdentifierSpec): CompileTokenMetadata { export function createIdentifierToken(identifier: IdentifierSpec): CompileTokenMetadata {
return identifierToken(resolveIdentifier(identifier)); return identifierToken(createIdentifier(identifier));
} }
export function resolveEnumIdentifier( export function createEnumIdentifier(
enumType: CompileIdentifierMetadata, name: string): CompileIdentifierMetadata { enumType: IdentifierSpec, name: string): CompileIdentifierMetadata {
const resolvedEnum = reflector.resolveEnum(enumType.reference, name); const resolvedEnum = reflector.resolveEnum(resolveIdentifier(enumType), name);
return new CompileIdentifierMetadata( return {reference: resolvedEnum};
{name: `${enumType.name}.${name}`, moduleUrl: enumType.moduleUrl, reference: resolvedEnum});
} }

View File

@ -10,7 +10,7 @@ import {Compiler, ComponentFactory, Injectable, Injector, ModuleWithComponentFac
import {AnimationCompiler} from '../animation/animation_compiler'; import {AnimationCompiler} from '../animation/animation_compiler';
import {AnimationParser} from '../animation/animation_parser'; import {AnimationParser} from '../animation/animation_parser';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, ProviderMeta, createHostComponentMeta} from '../compile_metadata'; import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, ProviderMeta, createHostComponentMeta, identifierName} from '../compile_metadata';
import {CompilerConfig} from '../config'; import {CompilerConfig} from '../config';
import {DirectiveNormalizer} from '../directive_normalizer'; import {DirectiveNormalizer} from '../directive_normalizer';
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler'; import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
@ -126,14 +126,13 @@ export class JitCompiler implements Compiler {
compileResult.dependencies.forEach((dep) => { compileResult.dependencies.forEach((dep) => {
dep.placeholder.reference = dep.placeholder.reference =
this._assertComponentKnown(dep.comp.reference, true).proxyComponentFactory; this._assertComponentKnown(dep.comp.reference, true).proxyComponentFactory;
dep.placeholder.name = `compFactory_${dep.comp.name}`;
}); });
if (!this._compilerConfig.useJit) { if (!this._compilerConfig.useJit) {
ngModuleFactory = ngModuleFactory =
interpretStatements(compileResult.statements, compileResult.ngModuleFactoryVar); interpretStatements(compileResult.statements, compileResult.ngModuleFactoryVar);
} else { } else {
ngModuleFactory = jitStatements( ngModuleFactory = jitStatements(
`/${moduleMeta.type.name}/module.ngfactory.js`, compileResult.statements, `/${identifierName(moduleMeta.type)}/module.ngfactory.js`, compileResult.statements,
compileResult.ngModuleFactoryVar); compileResult.ngModuleFactoryVar);
} }
this._compiledNgModuleCache.set(moduleMeta.type.reference, ngModuleFactory); this._compiledNgModuleCache.set(moduleMeta.type.reference, ngModuleFactory);
@ -215,7 +214,12 @@ export class JitCompiler implements Compiler {
if (!compiledTemplate) { if (!compiledTemplate) {
const compMeta = this._metadataResolver.getDirectiveMetadata(compType); const compMeta = this._metadataResolver.getDirectiveMetadata(compType);
assertComponent(compMeta); assertComponent(compMeta);
const hostMeta = createHostComponentMeta(compMeta);
class HostClass {
static overriddenName = `${identifierName(compMeta.type)}_Host`;
}
const hostMeta = createHostComponentMeta(HostClass, compMeta);
compiledTemplate = new CompiledTemplate( compiledTemplate = new CompiledTemplate(
true, compMeta.selector, compMeta.type, hostMeta, ngModule, [compMeta.type]); true, compMeta.selector, compMeta.type, hostMeta, ngModule, [compMeta.type]);
this._compiledHostTemplateCache.set(compType, compiledTemplate); this._compiledHostTemplateCache.set(compType, compiledTemplate);
@ -264,8 +268,8 @@ export class JitCompiler implements Compiler {
directiveWrapperClass = interpretStatements(statements, compileResult.dirWrapperClassVar); directiveWrapperClass = interpretStatements(statements, compileResult.dirWrapperClassVar);
} else { } else {
directiveWrapperClass = jitStatements( directiveWrapperClass = jitStatements(
`/${moduleMeta.type.name}/${dirMeta.type.name}/wrapper.ngfactory.js`, statements, `/${identifierName(moduleMeta.type)}/${identifierName(dirMeta.type)}/wrapper.ngfactory.js`,
compileResult.dirWrapperClassVar); statements, compileResult.dirWrapperClassVar);
} }
this._compiledDirectiveWrapperCache.set(dirMeta.type.reference, directiveWrapperClass); this._compiledDirectiveWrapperCache.set(dirMeta.type.reference, directiveWrapperClass);
} }
@ -288,9 +292,9 @@ export class JitCompiler implements Compiler {
pipe => this._metadataResolver.getPipeSummary(pipe.reference)); pipe => this._metadataResolver.getPipeSummary(pipe.reference));
const parsedTemplate = this._templateParser.parse( const parsedTemplate = this._templateParser.parse(
compMeta, compMeta.template.template, directives, pipes, template.ngModule.schemas, compMeta, compMeta.template.template, directives, pipes, template.ngModule.schemas,
compMeta.type.name); identifierName(compMeta.type));
const compiledAnimations = const compiledAnimations =
this._animationCompiler.compile(compMeta.type.name, parsedAnimations); this._animationCompiler.compile(identifierName(compMeta.type), parsedAnimations);
const compileResult = this._viewCompiler.compileComponent( const compileResult = this._viewCompiler.compileComponent(
compMeta, parsedTemplate, ir.variable(stylesCompileResult.componentStylesheet.stylesVar), compMeta, parsedTemplate, ir.variable(stylesCompileResult.componentStylesheet.stylesVar),
pipes, compiledAnimations); pipes, compiledAnimations);
@ -300,12 +304,10 @@ export class JitCompiler implements Compiler {
const vfd = <ViewClassDependency>dep; const vfd = <ViewClassDependency>dep;
depTemplate = this._assertComponentKnown(vfd.comp.reference, false); depTemplate = this._assertComponentKnown(vfd.comp.reference, false);
vfd.placeholder.reference = depTemplate.proxyViewClass; vfd.placeholder.reference = depTemplate.proxyViewClass;
vfd.placeholder.name = `View_${vfd.comp.name}`;
} else if (dep instanceof ComponentFactoryDependency) { } else if (dep instanceof ComponentFactoryDependency) {
const cfd = <ComponentFactoryDependency>dep; const cfd = <ComponentFactoryDependency>dep;
depTemplate = this._assertComponentKnown(cfd.comp.reference, true); depTemplate = this._assertComponentKnown(cfd.comp.reference, true);
cfd.placeholder.reference = depTemplate.proxyComponentFactory; cfd.placeholder.reference = depTemplate.proxyComponentFactory;
cfd.placeholder.name = `compFactory_${cfd.comp.name}`;
} else if (dep instanceof DirectiveWrapperDependency) { } else if (dep instanceof DirectiveWrapperDependency) {
const dwd = <DirectiveWrapperDependency>dep; const dwd = <DirectiveWrapperDependency>dep;
dwd.placeholder.reference = this._assertDirectiveWrapper(dwd.dir.reference); dwd.placeholder.reference = this._assertDirectiveWrapper(dwd.dir.reference);
@ -319,7 +321,7 @@ export class JitCompiler implements Compiler {
viewClass = interpretStatements(statements, compileResult.viewClassVar); viewClass = interpretStatements(statements, compileResult.viewClassVar);
} else { } else {
viewClass = jitStatements( viewClass = jitStatements(
`/${template.ngModule.type.name}/${template.compType.name}/${template.isHost?'host':'component'}.ngfactory.js`, `/${identifierName(template.ngModule.type)}/${identifierName(template.compType)}/${template.isHost?'host':'component'}.ngfactory.js`,
statements, compileResult.viewClassVar); statements, compileResult.viewClassVar);
} }
template.compiled(viewClass); template.compiled(viewClass);
@ -332,7 +334,6 @@ export class JitCompiler implements Compiler {
const nestedStylesArr = this._resolveAndEvalStylesCompileResult( const nestedStylesArr = this._resolveAndEvalStylesCompileResult(
nestedCompileResult, externalStylesheetsByModuleUrl); nestedCompileResult, externalStylesheetsByModuleUrl);
dep.valuePlaceholder.reference = nestedStylesArr; dep.valuePlaceholder.reference = nestedStylesArr;
dep.valuePlaceholder.name = `importedStyles${i}`;
}); });
} }
@ -380,7 +381,8 @@ class CompiledTemplate {
function assertComponent(meta: CompileDirectiveMetadata) { function assertComponent(meta: CompileDirectiveMetadata) {
if (!meta.isComponent) { 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.`);
} }
} }

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, Attribute, ChangeDetectionStrategy, Component, Host, Inject, Injectable, ModuleWithProviders, Optional, Provider, Query, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef} from '@angular/core'; import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, Attribute, ChangeDetectionStrategy, Component, Directive, Host, Inject, Injectable, ModuleWithProviders, Optional, Provider, Query, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef} from '@angular/core';
import {isStaticSymbol} from './aot/static_symbol'; import {isStaticSymbol} from './aot/static_symbol';
import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions'; import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
@ -15,14 +15,14 @@ import {DirectiveNormalizer} from './directive_normalizer';
import {DirectiveResolver} from './directive_resolver'; import {DirectiveResolver} from './directive_resolver';
import {ListWrapper, StringMapWrapper} from './facade/collection'; import {ListWrapper, StringMapWrapper} from './facade/collection';
import {isBlank, isPresent, stringify} from './facade/lang'; import {isBlank, isPresent, stringify} from './facade/lang';
import {Identifiers, resolveIdentifierToken} from './identifiers'; import {Identifiers, createIdentifierToken, resolveIdentifier} from './identifiers';
import {hasLifecycleHook} from './lifecycle_reflector'; import {hasLifecycleHook} from './lifecycle_reflector';
import {NgModuleResolver} from './ng_module_resolver'; import {NgModuleResolver} from './ng_module_resolver';
import {PipeResolver} from './pipe_resolver'; import {PipeResolver} from './pipe_resolver';
import {ComponentStillLoadingError, LIFECYCLE_HOOKS_VALUES, ReflectorReader, reflector} from './private_import_core'; import {ComponentStillLoadingError, LIFECYCLE_HOOKS_VALUES, ReflectorReader, reflector} from './private_import_core';
import {ElementSchemaRegistry} from './schema/element_schema_registry'; import {ElementSchemaRegistry} from './schema/element_schema_registry';
import {getUrlScheme} from './url_resolver'; import {getUrlScheme} from './url_resolver';
import {MODULE_SUFFIX, SyncAsyncResult, ValueTransformer, sanitizeIdentifier, visitValue} from './util'; import {MODULE_SUFFIX, SyncAsyncResult, ValueTransformer, visitValue} from './util';
@ -41,8 +41,6 @@ export class CompileMetadataResolver {
private _pipeSummaryCache = new Map<Type<any>, cpl.CompilePipeSummary>(); private _pipeSummaryCache = new Map<Type<any>, cpl.CompilePipeSummary>();
private _ngModuleCache = new Map<Type<any>, cpl.CompileNgModuleMetadata>(); private _ngModuleCache = new Map<Type<any>, cpl.CompileNgModuleMetadata>();
private _ngModuleOfTypes = new Map<Type<any>, Type<any>>(); private _ngModuleOfTypes = new Map<Type<any>, Type<any>>();
private _anonymousTypes = new Map<Object, number>();
private _anonymousTypeIndex = 0;
constructor( constructor(
private _ngModuleResolver: NgModuleResolver, private _directiveResolver: DirectiveResolver, private _ngModuleResolver: NgModuleResolver, private _directiveResolver: DirectiveResolver,
@ -50,20 +48,6 @@ export class CompileMetadataResolver {
private _directiveNormalizer: DirectiveNormalizer, private _directiveNormalizer: DirectiveNormalizer,
private _reflector: ReflectorReader = reflector) {} private _reflector: ReflectorReader = reflector) {}
private sanitizeTokenName(token: any): string {
let identifier = stringify(token);
if (identifier.indexOf('(') >= 0) {
// case: anonymous functions!
let found = this._anonymousTypes.get(token);
if (!found) {
this._anonymousTypes.set(token, this._anonymousTypeIndex++);
found = this._anonymousTypes.get(token);
}
identifier = `anonymous_token_${found}_`;
}
return sanitizeIdentifier(identifier);
}
clearCacheFor(type: Type<any>) { clearCacheFor(type: Type<any>) {
const dirMeta = this._directiveCache.get(type); const dirMeta = this._directiveCache.get(type);
this._directiveCache.delete(type); this._directiveCache.delete(type);
@ -142,30 +126,30 @@ export class CompileMetadataResolver {
return null; return null;
} }
private _loadDirectiveMetadata(directiveType: any, isSync: boolean): Promise<any> { private _loadDirectiveMetadata(directiveType: any, isSync: boolean): () => Promise<any> {
if (this._directiveCache.has(directiveType)) { if (this._directiveCache.has(directiveType)) {
return; return;
} }
directiveType = resolveForwardRef(directiveType); directiveType = resolveForwardRef(directiveType);
const nonNormalizedMetadata = this.getNonNormalizedDirectiveMetadata(directiveType); const {annotation, metadata} = this.getNonNormalizedDirectiveMetadata(directiveType);
const createDirectiveMetadata = (templateMetadata: cpl.CompileTemplateMetadata) => { const createDirectiveMetadata = (templateMetadata: cpl.CompileTemplateMetadata) => {
const normalizedDirMeta = new cpl.CompileDirectiveMetadata({ const normalizedDirMeta = new cpl.CompileDirectiveMetadata({
type: nonNormalizedMetadata.type, type: metadata.type,
isComponent: nonNormalizedMetadata.isComponent, isComponent: metadata.isComponent,
selector: nonNormalizedMetadata.selector, selector: metadata.selector,
exportAs: nonNormalizedMetadata.exportAs, exportAs: metadata.exportAs,
changeDetection: nonNormalizedMetadata.changeDetection, changeDetection: metadata.changeDetection,
inputs: nonNormalizedMetadata.inputs, inputs: metadata.inputs,
outputs: nonNormalizedMetadata.outputs, outputs: metadata.outputs,
hostListeners: nonNormalizedMetadata.hostListeners, hostListeners: metadata.hostListeners,
hostProperties: nonNormalizedMetadata.hostProperties, hostProperties: metadata.hostProperties,
hostAttributes: nonNormalizedMetadata.hostAttributes, hostAttributes: metadata.hostAttributes,
providers: nonNormalizedMetadata.providers, providers: metadata.providers,
viewProviders: nonNormalizedMetadata.viewProviders, viewProviders: metadata.viewProviders,
queries: nonNormalizedMetadata.queries, queries: metadata.queries,
viewQueries: nonNormalizedMetadata.viewQueries, viewQueries: metadata.viewQueries,
entryComponents: nonNormalizedMetadata.entryComponents, entryComponents: metadata.entryComponents,
template: templateMetadata template: templateMetadata
}); });
this._directiveCache.set(directiveType, normalizedDirMeta); this._directiveCache.set(directiveType, normalizedDirMeta);
@ -173,17 +157,17 @@ export class CompileMetadataResolver {
return normalizedDirMeta; return normalizedDirMeta;
}; };
if (nonNormalizedMetadata.isComponent) { if (metadata.isComponent) {
const templateMeta = this._directiveNormalizer.normalizeTemplate({ const templateMeta = this._directiveNormalizer.normalizeTemplate({
componentType: directiveType, componentType: directiveType,
moduleUrl: nonNormalizedMetadata.type.moduleUrl, moduleUrl: componentModuleUrl(this._reflector, directiveType, annotation),
encapsulation: nonNormalizedMetadata.template.encapsulation, encapsulation: metadata.template.encapsulation,
template: nonNormalizedMetadata.template.template, template: metadata.template.template,
templateUrl: nonNormalizedMetadata.template.templateUrl, templateUrl: metadata.template.templateUrl,
styles: nonNormalizedMetadata.template.styles, styles: metadata.template.styles,
styleUrls: nonNormalizedMetadata.template.styleUrls, styleUrls: metadata.template.styleUrls,
animations: nonNormalizedMetadata.template.animations, animations: metadata.template.animations,
interpolation: nonNormalizedMetadata.template.interpolation interpolation: metadata.template.interpolation
}); });
if (templateMeta.syncResult) { if (templateMeta.syncResult) {
createDirectiveMetadata(templateMeta.syncResult); createDirectiveMetadata(templateMeta.syncResult);
@ -192,7 +176,7 @@ export class CompileMetadataResolver {
if (isSync) { if (isSync) {
throw new ComponentStillLoadingError(directiveType); throw new ComponentStillLoadingError(directiveType);
} }
return templateMeta.asyncResult.then(createDirectiveMetadata); return () => templateMeta.asyncResult.then(createDirectiveMetadata);
} }
} else { } else {
// directive // directive
@ -201,18 +185,17 @@ export class CompileMetadataResolver {
} }
} }
getNonNormalizedDirectiveMetadata(directiveType: any): cpl.CompileDirectiveMetadata { getNonNormalizedDirectiveMetadata(directiveType: any):
{annotation: Directive, metadata: cpl.CompileDirectiveMetadata} {
directiveType = resolveForwardRef(directiveType); directiveType = resolveForwardRef(directiveType);
const dirMeta = this._directiveResolver.resolve(directiveType); const dirMeta = this._directiveResolver.resolve(directiveType);
if (!dirMeta) { if (!dirMeta) {
return null; return null;
} }
let moduleUrl = staticTypeModuleUrl(directiveType);
let nonNormalizedTemplateMetadata: cpl.CompileTemplateMetadata; let nonNormalizedTemplateMetadata: cpl.CompileTemplateMetadata;
if (dirMeta instanceof Component) { if (dirMeta instanceof Component) {
// component // component
moduleUrl = componentModuleUrl(this._reflector, directiveType, dirMeta);
assertArrayOfStrings('styles', dirMeta.styles); assertArrayOfStrings('styles', dirMeta.styles);
assertArrayOfStrings('styleUrls', dirMeta.styleUrls); assertArrayOfStrings('styleUrls', dirMeta.styleUrls);
assertInterpolationSymbols('interpolation', dirMeta.interpolation); assertInterpolationSymbols('interpolation', dirMeta.interpolation);
@ -233,7 +216,7 @@ export class CompileMetadataResolver {
} }
let changeDetectionStrategy: ChangeDetectionStrategy = null; let changeDetectionStrategy: ChangeDetectionStrategy = null;
let viewProviders: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = []; let viewProviders: cpl.CompileProviderMetadata[] = [];
let entryComponentMetadata: cpl.CompileIdentifierMetadata[] = []; let entryComponentMetadata: cpl.CompileIdentifierMetadata[] = [];
let selector = dirMeta.selector; let selector = dirMeta.selector;
@ -246,9 +229,8 @@ export class CompileMetadataResolver {
`viewProviders for "${stringify(directiveType)}"`); `viewProviders for "${stringify(directiveType)}"`);
} }
if (dirMeta.entryComponents) { if (dirMeta.entryComponents) {
entryComponentMetadata = entryComponentMetadata = flattenAndDedupeArray(dirMeta.entryComponents)
flattenAndDedupeArray(dirMeta.entryComponents) .map((type) => this._getIdentifierMetadata(type))
.map((type) => this._getIdentifierMetadata(type, staticTypeModuleUrl(type)))
.concat(entryComponentMetadata); .concat(entryComponentMetadata);
} }
if (!selector) { if (!selector) {
@ -261,7 +243,7 @@ export class CompileMetadataResolver {
} }
} }
let providers: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = []; let providers: cpl.CompileProviderMetadata[] = [];
if (isPresent(dirMeta.providers)) { if (isPresent(dirMeta.providers)) {
providers = this._getProvidersMetadata( providers = this._getProvidersMetadata(
dirMeta.providers, entryComponentMetadata, `providers for "${stringify(directiveType)}"`); dirMeta.providers, entryComponentMetadata, `providers for "${stringify(directiveType)}"`);
@ -273,11 +255,11 @@ export class CompileMetadataResolver {
viewQueries = this._getQueriesMetadata(dirMeta.queries, true, directiveType); viewQueries = this._getQueriesMetadata(dirMeta.queries, true, directiveType);
} }
return cpl.CompileDirectiveMetadata.create({ const metadata = cpl.CompileDirectiveMetadata.create({
selector: selector, selector: selector,
exportAs: dirMeta.exportAs, exportAs: dirMeta.exportAs,
isComponent: !!nonNormalizedTemplateMetadata, isComponent: !!nonNormalizedTemplateMetadata,
type: this._getTypeMetadata(directiveType, moduleUrl), type: this._getTypeMetadata(directiveType),
template: nonNormalizedTemplateMetadata, template: nonNormalizedTemplateMetadata,
changeDetection: changeDetectionStrategy, changeDetection: changeDetectionStrategy,
inputs: dirMeta.inputs, inputs: dirMeta.inputs,
@ -289,6 +271,7 @@ export class CompileMetadataResolver {
viewQueries: viewQueries, viewQueries: viewQueries,
entryComponents: entryComponentMetadata entryComponents: entryComponentMetadata
}); });
return {metadata, annotation: dirMeta};
} }
/** /**
@ -420,8 +403,7 @@ export class CompileMetadataResolver {
if (exportedModuleSummary) { if (exportedModuleSummary) {
exportedModules.push(exportedModuleSummary); exportedModules.push(exportedModuleSummary);
} else { } else {
exportedNonModuleIdentifiers.push( exportedNonModuleIdentifiers.push(this._getIdentifierMetadata(exportedType));
this._getIdentifierMetadata(exportedType, staticTypeModuleUrl(exportedType)));
} }
}); });
} }
@ -435,15 +417,16 @@ export class CompileMetadataResolver {
throw new Error( throw new Error(
`Unexpected value '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`); `Unexpected value '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`);
} }
const declaredIdentifier = const declaredIdentifier = this._getIdentifierMetadata(declaredType);
this._getIdentifierMetadata(declaredType, staticTypeModuleUrl(declaredType));
if (this._directiveResolver.isDirective(declaredType)) { if (this._directiveResolver.isDirective(declaredType)) {
transitiveModule.directivesSet.add(declaredType); transitiveModule.directivesSet.add(declaredType);
transitiveModule.directives.push(declaredIdentifier); transitiveModule.directives.push(declaredIdentifier);
declaredDirectives.push(declaredIdentifier); declaredDirectives.push(declaredIdentifier);
this._addTypeToModule(declaredType, moduleType); this._addTypeToModule(declaredType, moduleType);
transitiveModule.directiveLoaders.push( const loader = this._loadDirectiveMetadata(declaredType, isSync);
() => this._loadDirectiveMetadata(declaredType, isSync)); if (loader) {
transitiveModule.directiveLoaders.push(loader);
}
} else if (this._pipeResolver.isPipe(declaredType)) { } else if (this._pipeResolver.isPipe(declaredType)) {
transitiveModule.pipesSet.add(declaredType); transitiveModule.pipesSet.add(declaredType);
transitiveModule.pipes.push(declaredIdentifier); transitiveModule.pipes.push(declaredIdentifier);
@ -479,8 +462,7 @@ export class CompileMetadataResolver {
if (meta.entryComponents) { if (meta.entryComponents) {
entryComponents.push( entryComponents.push(
...flattenAndDedupeArray(meta.entryComponents) ...flattenAndDedupeArray(meta.entryComponents).map(type => this._getTypeMetadata(type)));
.map(type => this._getTypeMetadata(type, staticTypeModuleUrl(type))));
} }
if (meta.bootstrap) { if (meta.bootstrap) {
@ -489,7 +471,7 @@ export class CompileMetadataResolver {
throw new Error( throw new Error(
`Unexpected value '${stringify(type)}' used in the bootstrap property of module '${stringify(moduleType)}'`); `Unexpected value '${stringify(type)}' used in the bootstrap property of module '${stringify(moduleType)}'`);
} }
return this._getTypeMetadata(type, staticTypeModuleUrl(type)); return this._getTypeMetadata(type);
}); });
bootstrapComponents.push(...typeMetadata); bootstrapComponents.push(...typeMetadata);
} }
@ -504,7 +486,7 @@ export class CompileMetadataResolver {
transitiveModule.providers.push(...providers); transitiveModule.providers.push(...providers);
compileMeta = new cpl.CompileNgModuleMetadata({ compileMeta = new cpl.CompileNgModuleMetadata({
type: this._getTypeMetadata(moduleType, staticTypeModuleUrl(moduleType)), type: this._getTypeMetadata(moduleType),
providers, providers,
entryComponents, entryComponents,
bootstrapComponents, bootstrapComponents,
@ -575,35 +557,25 @@ export class CompileMetadataResolver {
transitiveModules, providers, entryComponents, directives, pipes, directiveLoaders); transitiveModules, providers, entryComponents, directives, pipes, directiveLoaders);
} }
private _getIdentifierMetadata(type: Type<any>, moduleUrl: string): private _getIdentifierMetadata(type: Type<any>): cpl.CompileIdentifierMetadata {
cpl.CompileIdentifierMetadata {
type = resolveForwardRef(type); type = resolveForwardRef(type);
return new cpl.CompileIdentifierMetadata( return {reference: type};
{name: this.sanitizeTokenName(type), moduleUrl, reference: type});
} }
private _getTypeMetadata(type: Type<any>, moduleUrl: string, dependencies: any[] = null): private _getTypeMetadata(type: Type<any>, dependencies: any[] = null): cpl.CompileTypeMetadata {
cpl.CompileTypeMetadata { const identifier = this._getIdentifierMetadata(type);
const identifier = this._getIdentifierMetadata(type, moduleUrl); return {
return new cpl.CompileTypeMetadata({
name: identifier.name,
moduleUrl: identifier.moduleUrl,
reference: identifier.reference, reference: identifier.reference,
diDeps: this._getDependenciesMetadata(identifier.reference, dependencies), diDeps: this._getDependenciesMetadata(identifier.reference, dependencies),
lifecycleHooks: lifecycleHooks:
LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, identifier.reference)), LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, identifier.reference)),
}); };
} }
private _getFactoryMetadata(factory: Function, moduleUrl: string, dependencies: any[] = null): private _getFactoryMetadata(factory: Function, dependencies: any[] = null):
cpl.CompileFactoryMetadata { cpl.CompileFactoryMetadata {
factory = resolveForwardRef(factory); factory = resolveForwardRef(factory);
return new cpl.CompileFactoryMetadata({ return {reference: factory, diDeps: this._getDependenciesMetadata(factory, dependencies)};
name: this.sanitizeTokenName(factory),
moduleUrl,
reference: factory,
diDeps: this._getDependenciesMetadata(factory, dependencies)
});
} }
/** /**
@ -641,7 +613,7 @@ export class CompileMetadataResolver {
const pipeAnnotation = this._pipeResolver.resolve(pipeType); const pipeAnnotation = this._pipeResolver.resolve(pipeType);
const pipeMeta = new cpl.CompilePipeMetadata({ const pipeMeta = new cpl.CompilePipeMetadata({
type: this._getTypeMetadata(pipeType, staticTypeModuleUrl(pipeType)), type: this._getTypeMetadata(pipeType),
name: pipeAnnotation.name, name: pipeAnnotation.name,
pure: pipeAnnotation.pure pure: pipeAnnotation.pure
}); });
@ -689,14 +661,14 @@ export class CompileMetadataResolver {
return null; return null;
} }
return new cpl.CompileDiDependencyMetadata({ return {
isAttribute, isAttribute,
isHost, isHost,
isSelf, isSelf,
isSkipSelf, isSkipSelf,
isOptional, isOptional,
token: this._getTokenMetadata(token) token: this._getTokenMetadata(token)
}); };
}); });
@ -714,41 +686,27 @@ export class CompileMetadataResolver {
token = resolveForwardRef(token); token = resolveForwardRef(token);
let compileToken: cpl.CompileTokenMetadata; let compileToken: cpl.CompileTokenMetadata;
if (typeof token === 'string') { if (typeof token === 'string') {
compileToken = new cpl.CompileTokenMetadata({value: token}); compileToken = {value: token};
} else { } else {
compileToken = new cpl.CompileTokenMetadata({ compileToken = {identifier: {reference: token}};
identifier: new cpl.CompileIdentifierMetadata({
reference: token,
name: this.sanitizeTokenName(token),
moduleUrl: staticTypeModuleUrl(token)
})
});
} }
return compileToken; return compileToken;
} }
private _getProvidersMetadata( private _getProvidersMetadata(
providers: Provider[], targetEntryComponents: cpl.CompileIdentifierMetadata[], providers: Provider[], targetEntryComponents: cpl.CompileIdentifierMetadata[],
debugInfo?: string): Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> { debugInfo?: string,
const compileProviders: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = []; compileProviders: cpl.CompileProviderMetadata[] = []): cpl.CompileProviderMetadata[] {
providers.forEach((provider: any, providerIdx: number) => { providers.forEach((provider: any, providerIdx: number) => {
provider = resolveForwardRef(provider);
if (provider && typeof provider == 'object' && provider.hasOwnProperty('provide')) {
provider = new cpl.ProviderMeta(provider.provide, provider);
}
let compileProvider: cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[];
if (Array.isArray(provider)) { if (Array.isArray(provider)) {
compileProvider = this._getProvidersMetadata(provider, targetEntryComponents, debugInfo); this._getProvidersMetadata(provider, targetEntryComponents, debugInfo, compileProviders);
} else if (provider instanceof cpl.ProviderMeta) {
const tokenMeta = this._getTokenMetadata(provider.token);
if (tokenMeta.reference ===
resolveIdentifierToken(Identifiers.ANALYZE_FOR_ENTRY_COMPONENTS).reference) {
targetEntryComponents.push(...this._getEntryComponentsFromProvider(provider));
} else { } else {
compileProvider = this.getProviderMetadata(provider); provider = resolveForwardRef(provider);
} let providerMeta: cpl.ProviderMeta;
if (provider && typeof provider == 'object' && provider.hasOwnProperty('provide')) {
providerMeta = new cpl.ProviderMeta(provider.provide, provider);
} else if (isValidType(provider)) { } else if (isValidType(provider)) {
compileProvider = this._getTypeMetadata(provider, staticTypeModuleUrl(provider)); providerMeta = new cpl.ProviderMeta(provider, {useClass: provider});
} else { } else {
const providersInfo = const providersInfo =
(<string[]>providers.reduce( (<string[]>providers.reduce(
@ -764,12 +722,14 @@ export class CompileMetadataResolver {
}, },
[])) []))
.join(', '); .join(', ');
throw new Error( throw new Error(
`Invalid ${debugInfo ? debugInfo : 'provider'} - only instances of Provider and Type are allowed, got: [${providersInfo}]`); `Invalid ${debugInfo ? debugInfo : 'provider'} - only instances of Provider and Type are allowed, got: [${providersInfo}]`);
} }
if (compileProvider) { if (providerMeta.token === resolveIdentifier(Identifiers.ANALYZE_FOR_ENTRY_COMPONENTS)) {
compileProviders.push(compileProvider); targetEntryComponents.push(...this._getEntryComponentsFromProvider(providerMeta));
} else {
compileProviders.push(this.getProviderMetadata(providerMeta));
}
} }
}); });
return compileProviders; return compileProviders;
@ -788,7 +748,7 @@ export class CompileMetadataResolver {
throw new Error(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports 'multi = true'!`); throw new Error(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports 'multi = true'!`);
} }
convertToCompileValue(provider.useValue, collectedIdentifiers); extractIdentifiers(provider.useValue, collectedIdentifiers);
collectedIdentifiers.forEach((identifier) => { collectedIdentifiers.forEach((identifier) => {
if (this._directiveResolver.isDirective(identifier.reference)) { if (this._directiveResolver.isDirective(identifier.reference)) {
components.push(identifier); components.push(identifier);
@ -801,26 +761,29 @@ export class CompileMetadataResolver {
let compileDeps: cpl.CompileDiDependencyMetadata[]; let compileDeps: cpl.CompileDiDependencyMetadata[];
let compileTypeMetadata: cpl.CompileTypeMetadata = null; let compileTypeMetadata: cpl.CompileTypeMetadata = null;
let compileFactoryMetadata: cpl.CompileFactoryMetadata = null; let compileFactoryMetadata: cpl.CompileFactoryMetadata = null;
let token: cpl.CompileTokenMetadata = this._getTokenMetadata(provider.token);
if (provider.useClass) { if (provider.useClass) {
compileTypeMetadata = this._getTypeMetadata( compileTypeMetadata = this._getTypeMetadata(provider.useClass, provider.dependencies);
provider.useClass, staticTypeModuleUrl(provider.useClass), provider.dependencies);
compileDeps = compileTypeMetadata.diDeps; compileDeps = compileTypeMetadata.diDeps;
if (provider.token === provider.useClass) {
// use the compileTypeMetadata as it contains information about lifecycleHooks...
token = {identifier: compileTypeMetadata};
}
} else if (provider.useFactory) { } else if (provider.useFactory) {
compileFactoryMetadata = this._getFactoryMetadata( compileFactoryMetadata = this._getFactoryMetadata(provider.useFactory, provider.dependencies);
provider.useFactory, staticTypeModuleUrl(provider.useFactory), provider.dependencies);
compileDeps = compileFactoryMetadata.diDeps; compileDeps = compileFactoryMetadata.diDeps;
} }
return new cpl.CompileProviderMetadata({ return {
token: this._getTokenMetadata(provider.token), token: token,
useClass: compileTypeMetadata, useClass: compileTypeMetadata,
useValue: convertToCompileValue(provider.useValue, []), useValue: provider.useValue,
useFactory: compileFactoryMetadata, useFactory: compileFactoryMetadata,
useExisting: provider.useExisting ? this._getTokenMetadata(provider.useExisting) : null, useExisting: provider.useExisting ? this._getTokenMetadata(provider.useExisting) : null,
deps: compileDeps, deps: compileDeps,
multi: provider.multi multi: provider.multi
}); };
} }
private _getQueriesMetadata( private _getQueriesMetadata(
@ -854,12 +817,12 @@ export class CompileMetadataResolver {
selectors = [this._getTokenMetadata(q.selector)]; selectors = [this._getTokenMetadata(q.selector)];
} }
return new cpl.CompileQueryMetadata({ return {
selectors, selectors,
first: q.first, first: q.first,
descendants: q.descendants, propertyName, descendants: q.descendants, propertyName,
read: q.read ? this._getTokenMetadata(q.read) : null read: q.read ? this._getTokenMetadata(q.read) : null
}); };
} }
} }
@ -925,14 +888,10 @@ function isValidType(value: any): boolean {
return isStaticSymbol(value) || (value instanceof Type); return isStaticSymbol(value) || (value instanceof Type);
} }
function staticTypeModuleUrl(value: any): string { export function componentModuleUrl(
return isStaticSymbol(value) ? value.filePath : null;
}
function componentModuleUrl(
reflector: ReflectorReader, type: Type<any>, cmpMetadata: Component): string { reflector: ReflectorReader, type: Type<any>, cmpMetadata: Component): string {
if (isStaticSymbol(type)) { if (isStaticSymbol(type)) {
return staticTypeModuleUrl(type); return type.filePath;
} }
const moduleId = cmpMetadata.moduleId; const moduleId = cmpMetadata.moduleId;
@ -949,21 +908,12 @@ function componentModuleUrl(
return reflector.importUri(type); return reflector.importUri(type);
} }
function convertToCompileValue( function extractIdentifiers(value: any, targetIdentifiers: cpl.CompileIdentifierMetadata[]) {
value: any, targetIdentifiers: cpl.CompileIdentifierMetadata[]): any { visitValue(value, new _CompileValueConverter(), targetIdentifiers);
return visitValue(value, new _CompileValueConverter(), targetIdentifiers);
} }
class _CompileValueConverter extends ValueTransformer { class _CompileValueConverter extends ValueTransformer {
visitOther(value: any, targetIdentifiers: cpl.CompileIdentifierMetadata[]): any { visitOther(value: any, targetIdentifiers: cpl.CompileIdentifierMetadata[]): any {
let identifier: cpl.CompileIdentifierMetadata; targetIdentifiers.push({reference: value});
if (isStaticSymbol(value)) {
identifier = new cpl.CompileIdentifierMetadata(
{name: value.name, moduleUrl: value.filePath, reference: value});
} else {
identifier = new cpl.CompileIdentifierMetadata({reference: value});
}
targetIdentifiers.push(identifier);
return identifier;
} }
} }

View File

@ -8,10 +8,10 @@
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {CompileDiDependencyMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTokenMetadata} from './compile_metadata'; import {CompileDiDependencyMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName, tokenName, tokenReference} from './compile_metadata';
import {createDiTokenExpression} from './compiler_util/identifier_util'; import {createDiTokenExpression} from './compiler_util/identifier_util';
import {isPresent} from './facade/lang'; import {isPresent} from './facade/lang';
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from './identifiers'; import {Identifiers, createIdentifier, createIdentifierToken, resolveIdentifier} from './identifiers';
import {ClassBuilder, createClassStmt} from './output/class_builder'; import {ClassBuilder, createClassStmt} from './output/class_builder';
import * as o from './output/output_ast'; import * as o from './output/output_ast';
import {convertValueToOutputAst} from './output/value_util'; import {convertValueToOutputAst} from './output/value_util';
@ -35,9 +35,10 @@ export class NgModuleCompileResult {
export class NgModuleCompiler { export class NgModuleCompiler {
compile(ngModuleMeta: CompileNgModuleMetadata, extraProviders: CompileProviderMetadata[]): compile(ngModuleMeta: CompileNgModuleMetadata, extraProviders: CompileProviderMetadata[]):
NgModuleCompileResult { NgModuleCompileResult {
const sourceFileName = isPresent(ngModuleMeta.type.moduleUrl) ? const moduleUrl = identifierModuleUrl(ngModuleMeta.type);
`in NgModule ${ngModuleMeta.type.name} in ${ngModuleMeta.type.moduleUrl}` : const sourceFileName = isPresent(moduleUrl) ?
`in NgModule ${ngModuleMeta.type.name}`; `in NgModule ${identifierName(ngModuleMeta.type)} in ${moduleUrl}` :
`in NgModule ${identifierName(ngModuleMeta.type)}`;
const sourceFile = new ParseSourceFile('', sourceFileName); const sourceFile = new ParseSourceFile('', sourceFileName);
const sourceSpan = new ParseSourceSpan( const sourceSpan = new ParseSourceSpan(
new ParseLocation(sourceFile, null, null, null), new ParseLocation(sourceFile, null, null, null),
@ -46,7 +47,7 @@ export class NgModuleCompiler {
const bootstrapComponentFactories: CompileIdentifierMetadata[] = []; const bootstrapComponentFactories: CompileIdentifierMetadata[] = [];
const entryComponentFactories = const entryComponentFactories =
ngModuleMeta.transitiveModule.entryComponents.map((entryComponent) => { ngModuleMeta.transitiveModule.entryComponents.map((entryComponent) => {
const id = new CompileIdentifierMetadata({name: entryComponent.name}); const id: CompileIdentifierMetadata = {reference: null};
if (ngModuleMeta.bootstrapComponents.indexOf(entryComponent) > -1) { if (ngModuleMeta.bootstrapComponents.indexOf(entryComponent) > -1) {
bootstrapComponentFactories.push(id); bootstrapComponentFactories.push(id);
} }
@ -59,21 +60,21 @@ export class NgModuleCompiler {
const providerParser = new NgModuleProviderAnalyzer(ngModuleMeta, extraProviders, sourceSpan); const providerParser = new NgModuleProviderAnalyzer(ngModuleMeta, extraProviders, sourceSpan);
providerParser.parse().forEach((provider) => builder.addProvider(provider)); providerParser.parse().forEach((provider) => builder.addProvider(provider));
const injectorClass = builder.build(); const injectorClass = builder.build();
const ngModuleFactoryVar = `${ngModuleMeta.type.name}NgFactory`; const ngModuleFactoryVar = `${identifierName(ngModuleMeta.type)}NgFactory`;
const ngModuleFactoryStmt = const ngModuleFactoryStmt =
o.variable(ngModuleFactoryVar) o.variable(ngModuleFactoryVar)
.set(o.importExpr(resolveIdentifier(Identifiers.NgModuleFactory)) .set(o.importExpr(createIdentifier(Identifiers.NgModuleFactory))
.instantiate( .instantiate(
[o.variable(injectorClass.name), o.importExpr(ngModuleMeta.type)], [o.variable(injectorClass.name), o.importExpr(ngModuleMeta.type)],
o.importType( o.importType(
resolveIdentifier(Identifiers.NgModuleFactory), createIdentifier(Identifiers.NgModuleFactory),
[o.importType(ngModuleMeta.type)], [o.TypeModifier.Const]))) [o.importType(ngModuleMeta.type)], [o.TypeModifier.Const])))
.toDeclStmt(null, [o.StmtModifier.Final]); .toDeclStmt(null, [o.StmtModifier.Final]);
const stmts: o.Statement[] = [injectorClass, ngModuleFactoryStmt]; const stmts: o.Statement[] = [injectorClass, ngModuleFactoryStmt];
if (ngModuleMeta.id) { if (ngModuleMeta.id) {
const registerFactoryStmt = const registerFactoryStmt =
o.importExpr(resolveIdentifier(Identifiers.RegisterModuleFactoryFn)) o.importExpr(createIdentifier(Identifiers.RegisterModuleFactoryFn))
.callFn([o.literal(ngModuleMeta.id), o.variable(ngModuleFactoryVar)]) .callFn([o.literal(ngModuleMeta.id), o.variable(ngModuleFactoryVar)])
.toStmt(); .toStmt();
stmts.push(registerFactoryStmt); stmts.push(registerFactoryStmt);
@ -102,7 +103,7 @@ class _InjectorBuilder implements ClassBuilder {
addProvider(resolvedProvider: ProviderAst) { addProvider(resolvedProvider: ProviderAst) {
const providerValueExpressions = const providerValueExpressions =
resolvedProvider.providers.map((provider) => this._getProviderValue(provider)); resolvedProvider.providers.map((provider) => this._getProviderValue(provider));
const propName = `_${resolvedProvider.token.name}_${this._instances.size}`; const propName = `_${tokenName(resolvedProvider.token)}_${this._instances.size}`;
const instance = this._createProviderProperty( const instance = this._createProviderProperty(
propName, resolvedProvider, providerValueExpressions, resolvedProvider.multiProvider, propName, resolvedProvider, providerValueExpressions, resolvedProvider.multiProvider,
resolvedProvider.eager); resolvedProvider.eager);
@ -110,12 +111,12 @@ class _InjectorBuilder implements ClassBuilder {
this._destroyStmts.push(instance.callMethod('ngOnDestroy', []).toStmt()); this._destroyStmts.push(instance.callMethod('ngOnDestroy', []).toStmt());
} }
this._tokens.push(resolvedProvider.token); this._tokens.push(resolvedProvider.token);
this._instances.set(resolvedProvider.token.reference, instance); this._instances.set(tokenReference(resolvedProvider.token), instance);
} }
build(): o.ClassStmt { build(): o.ClassStmt {
const getMethodStmts: o.Statement[] = this._tokens.map((token) => { const getMethodStmts: o.Statement[] = this._tokens.map((token) => {
const providerExpr = this._instances.get(token.reference); const providerExpr = this._instances.get(tokenReference(token));
return new o.IfStmt( return new o.IfStmt(
InjectMethodVars.token.identical(createDiTokenExpression(token)), InjectMethodVars.token.identical(createDiTokenExpression(token)),
[new o.ReturnStatement(providerExpr)]); [new o.ReturnStatement(providerExpr)]);
@ -143,13 +144,13 @@ class _InjectorBuilder implements ClassBuilder {
o.literalArr(this._bootstrapComponentFactories.map( o.literalArr(this._bootstrapComponentFactories.map(
(componentFactory) => o.importExpr(componentFactory))) (componentFactory) => o.importExpr(componentFactory)))
]; ];
const injClassName = `${this._ngModuleMeta.type.name}Injector`; const injClassName = `${identifierName(this._ngModuleMeta.type)}Injector`;
return createClassStmt({ return createClassStmt({
name: injClassName, name: injClassName,
ctorParams: [new o.FnParam( ctorParams: [new o.FnParam(
InjectorProps.parent.name, o.importType(resolveIdentifier(Identifiers.Injector)))], InjectorProps.parent.name, o.importType(createIdentifier(Identifiers.Injector)))],
parent: o.importExpr( parent: o.importExpr(
resolveIdentifier(Identifiers.NgModuleInjector), [o.importType(this._ngModuleMeta.type)]), createIdentifier(Identifiers.NgModuleInjector), [o.importType(this._ngModuleMeta.type)]),
parentArgs: parentArgs, parentArgs: parentArgs,
builders: [{methods}, this] builders: [{methods}, this]
}); });
@ -158,7 +159,7 @@ class _InjectorBuilder implements ClassBuilder {
private _getProviderValue(provider: CompileProviderMetadata): o.Expression { private _getProviderValue(provider: CompileProviderMetadata): o.Expression {
let result: o.Expression; let result: o.Expression;
if (isPresent(provider.useExisting)) { if (isPresent(provider.useExisting)) {
result = this._getDependency(new CompileDiDependencyMetadata({token: provider.useExisting})); result = this._getDependency({token: provider.useExisting});
} else if (isPresent(provider.useFactory)) { } else if (isPresent(provider.useFactory)) {
const deps = provider.deps || provider.useFactory.diDeps; const deps = provider.deps || provider.useFactory.diDeps;
const depsExpr = deps.map((dep) => this._getDependency(dep)); const depsExpr = deps.map((dep) => this._getDependency(dep));
@ -215,13 +216,12 @@ class _InjectorBuilder implements ClassBuilder {
} }
if (!dep.isSkipSelf) { if (!dep.isSkipSelf) {
if (dep.token && if (dep.token &&
(dep.token.reference === resolveIdentifierToken(Identifiers.Injector).reference || (tokenReference(dep.token) === resolveIdentifier(Identifiers.Injector) ||
dep.token.reference === tokenReference(dep.token) === resolveIdentifier(Identifiers.ComponentFactoryResolver))) {
resolveIdentifierToken(Identifiers.ComponentFactoryResolver).reference)) {
result = o.THIS_EXPR; result = o.THIS_EXPR;
} }
if (!result) { if (!result) {
result = this._instances.get(dep.token.reference); result = this._instances.get(tokenReference(dep.token));
} }
} }
if (!result) { if (!result) {

View File

@ -8,6 +8,7 @@
import {Injectable, NgModule, Type} from '@angular/core'; import {Injectable, NgModule, Type} from '@angular/core';
import {ListWrapper} from './facade/collection';
import {isPresent, stringify} from './facade/lang'; import {isPresent, stringify} from './facade/lang';
import {ReflectorReader, reflector} from './private_import_core'; import {ReflectorReader, reflector} from './private_import_core';
@ -25,7 +26,8 @@ export class NgModuleResolver {
isNgModule(type: any) { return this._reflector.annotations(type).some(_isNgModuleMetadata); } isNgModule(type: any) { return this._reflector.annotations(type).some(_isNgModuleMetadata); }
resolve(type: Type<any>, throwIfNotFound = true): NgModule { resolve(type: Type<any>, throwIfNotFound = true): NgModule {
const ngModuleMeta: NgModule = this._reflector.annotations(type).find(_isNgModuleMetadata); const ngModuleMeta: NgModule =
ListWrapper.findLast(this._reflector.annotations(type), _isNgModuleMetadata);
if (isPresent(ngModuleMeta)) { if (isPresent(ngModuleMeta)) {
return ngModuleMeta; return ngModuleMeta;

View File

@ -7,6 +7,7 @@
*/ */
import {identifierModuleUrl, identifierName} from '../compile_metadata';
import {isBlank, isPresent} from '../facade/lang'; import {isBlank, isPresent} from '../facade/lang';
import {EmitterVisitorContext, OutputEmitter} from './abstract_emitter'; import {EmitterVisitorContext, OutputEmitter} from './abstract_emitter';
@ -38,18 +39,21 @@ class JsEmitterVisitor extends AbstractJsEmitterVisitor {
constructor(private _moduleUrl: string) { super(); } constructor(private _moduleUrl: string) { super(); }
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any { visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
if (isBlank(ast.value.name)) { const name = identifierName(ast.value);
const moduleUrl = identifierModuleUrl(ast.value);
if (isBlank(name)) {
console.error('>>>', ast.value);
throw new Error(`Internal error: unknown identifier ${ast.value}`); throw new Error(`Internal error: unknown identifier ${ast.value}`);
} }
if (isPresent(ast.value.moduleUrl) && ast.value.moduleUrl != this._moduleUrl) { if (isPresent(moduleUrl) && moduleUrl != this._moduleUrl) {
let prefix = this.importsWithPrefixes.get(ast.value.moduleUrl); let prefix = this.importsWithPrefixes.get(moduleUrl);
if (isBlank(prefix)) { if (isBlank(prefix)) {
prefix = `import${this.importsWithPrefixes.size}`; prefix = `import${this.importsWithPrefixes.size}`;
this.importsWithPrefixes.set(ast.value.moduleUrl, prefix); this.importsWithPrefixes.set(moduleUrl, prefix);
} }
ctx.print(`${prefix}.`); ctx.print(`${prefix}.`);
} }
ctx.print(ast.value.name); ctx.print(name);
return null; return null;
} }
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any { visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any {

View File

@ -43,14 +43,14 @@ export class BuiltinType extends Type {
} }
} }
export class ExternalType extends Type { export class ExpressionType extends Type {
constructor( constructor(
public value: CompileIdentifierMetadata, public typeParams: Type[] = null, public value: Expression, public typeParams: Type[] = null,
modifiers: TypeModifier[] = null) { modifiers: TypeModifier[] = null) {
super(modifiers); super(modifiers);
} }
visitType(visitor: TypeVisitor, context: any): any { visitType(visitor: TypeVisitor, context: any): any {
return visitor.visitExternalType(this, context); return visitor.visitExpressionType(this, context);
} }
} }
@ -78,7 +78,7 @@ export var NULL_TYPE = new BuiltinType(BuiltinTypeName.Null);
export interface TypeVisitor { export interface TypeVisitor {
visitBuiltintType(type: BuiltinType, context: any): any; visitBuiltintType(type: BuiltinType, context: any): any;
visitExternalType(type: ExternalType, context: any): any; visitExpressionType(type: ExpressionType, context: any): any;
visitArrayType(type: ArrayType, context: any): any; visitArrayType(type: ArrayType, context: any): any;
visitMapType(type: MapType, context: any): any; visitMapType(type: MapType, context: any): any;
} }
@ -876,8 +876,14 @@ export function importExpr(id: CompileIdentifierMetadata, typeParams: Type[] = n
export function importType( export function importType(
id: CompileIdentifierMetadata, typeParams: Type[] = null, id: CompileIdentifierMetadata, typeParams: Type[] = null,
typeModifiers: TypeModifier[] = null): ExternalType { typeModifiers: TypeModifier[] = null): ExpressionType {
return isPresent(id) ? new ExternalType(id, typeParams, typeModifiers) : null; return isPresent(id) ? expressionType(importExpr(id), typeParams, typeModifiers) : null;
}
export function expressionType(
expr: Expression, typeParams: Type[] = null,
typeModifiers: TypeModifier[] = null): ExpressionType {
return isPresent(expr) ? new ExpressionType(expr, typeParams, typeModifiers) : null;
} }
export function literalArr(values: Expression[], type: Type = null): LiteralArrayExpr { export function literalArr(values: Expression[], type: Type = null): LiteralArrayExpr {

View File

@ -6,8 +6,8 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {identifierName} from '../compile_metadata';
import {isPresent} from '../facade/lang'; import {isPresent} from '../facade/lang';
import {sanitizeIdentifier} from '../util';
import {EmitterVisitorContext} from './abstract_emitter'; import {EmitterVisitorContext} from './abstract_emitter';
import {AbstractJsEmitterVisitor} from './abstract_js_emitter'; import {AbstractJsEmitterVisitor} from './abstract_js_emitter';
@ -52,8 +52,8 @@ class JitEmitterVisitor extends AbstractJsEmitterVisitor {
if (id === -1) { if (id === -1) {
id = this._evalArgValues.length; id = this._evalArgValues.length;
this._evalArgValues.push(value); this._evalArgValues.push(value);
const name = isPresent(ast.value.name) ? sanitizeIdentifier(ast.value.name) : 'val'; const name = identifierName(ast.value) || 'val';
this._evalArgNames.push(sanitizeIdentifier(`jit_${name}${id}`)); this._evalArgNames.push(`jit_${name}${id}`);
} }
ctx.print(this._evalArgNames[id]); ctx.print(this._evalArgNames[id]);
return null; return null;

View File

@ -7,7 +7,7 @@
*/ */
import {CompileIdentifierMetadata} from '../compile_metadata'; import {CompileIdentifierMetadata, identifierModuleUrl, identifierName} from '../compile_metadata';
import {isBlank, isPresent} from '../facade/lang'; import {isBlank, isPresent} from '../facade/lang';
import {AbstractEmitterVisitor, CATCH_ERROR_VAR, CATCH_STACK_VAR, EmitterVisitorContext, OutputEmitter} from './abstract_emitter'; import {AbstractEmitterVisitor, CATCH_ERROR_VAR, CATCH_STACK_VAR, EmitterVisitorContext, OutputEmitter} from './abstract_emitter';
@ -271,8 +271,13 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
return null; return null;
} }
visitExternalType(ast: o.ExternalType, ctx: EmitterVisitorContext): any { visitExpressionType(ast: o.ExpressionType, ctx: EmitterVisitorContext): any {
this._visitIdentifier(ast.value, ast.typeParams, ctx); ast.value.visitExpression(this, ctx);
if (isPresent(ast.typeParams) && ast.typeParams.length > 0) {
ctx.print(`<`);
this.visitAllObjects(type => type.visitType(this, ctx), ast.typeParams, ctx, ',');
ctx.print(`>`);
}
return null; return null;
} }
@ -317,14 +322,16 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
private _visitIdentifier( private _visitIdentifier(
value: CompileIdentifierMetadata, typeParams: o.Type[], ctx: EmitterVisitorContext): void { value: CompileIdentifierMetadata, typeParams: o.Type[], ctx: EmitterVisitorContext): void {
if (isBlank(value.name)) { const name = identifierName(value);
const moduleUrl = identifierModuleUrl(value);
if (isBlank(name)) {
throw new Error(`Internal error: unknown identifier ${value}`); throw new Error(`Internal error: unknown identifier ${value}`);
} }
if (isPresent(value.moduleUrl) && value.moduleUrl != this._moduleUrl) { if (isPresent(moduleUrl) && moduleUrl != this._moduleUrl) {
let prefix = this.importsWithPrefixes.get(value.moduleUrl); let prefix = this.importsWithPrefixes.get(moduleUrl);
if (isBlank(prefix)) { if (isBlank(prefix)) {
prefix = `import${this.importsWithPrefixes.size}`; prefix = `import${this.importsWithPrefixes.size}`;
this.importsWithPrefixes.set(value.moduleUrl, prefix); this.importsWithPrefixes.set(moduleUrl, prefix);
} }
ctx.print(`${prefix}.`); ctx.print(`${prefix}.`);
} }
@ -333,7 +340,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
ctx.print('.'); ctx.print('.');
ctx.print(value.reference.members.join('.')); ctx.print(value.reference.members.join('.'));
} else { } else {
ctx.print(value.name); ctx.print(name);
} }
if (isPresent(typeParams) && typeParams.length > 0) { if (isPresent(typeParams) && typeParams.length > 0) {
ctx.print(`<`); ctx.print(`<`);

View File

@ -30,12 +30,10 @@ class _ValueOutputAstTransformer implements ValueTransformer {
visitPrimitive(value: any, type: o.Type): o.Expression { return o.literal(value, type); } visitPrimitive(value: any, type: o.Type): o.Expression { return o.literal(value, type); }
visitOther(value: any, type: o.Type): o.Expression { visitOther(value: any, type: o.Type): o.Expression {
if (value instanceof CompileIdentifierMetadata) { if (value instanceof o.Expression) {
return o.importExpr(value);
} else if (value instanceof o.Expression) {
return value; return value;
} else { } else {
throw new Error(`Illegal state: Don't now how to compile value ${value}`); return o.importExpr({reference: value});
} }
} }
} }

View File

@ -8,6 +8,7 @@
import {Injectable, Pipe, Type, resolveForwardRef} from '@angular/core'; import {Injectable, Pipe, Type, resolveForwardRef} from '@angular/core';
import {ListWrapper} from './facade/collection';
import {isPresent, stringify} from './facade/lang'; import {isPresent, stringify} from './facade/lang';
import {ReflectorReader, reflector} from './private_import_core'; import {ReflectorReader, reflector} from './private_import_core';
@ -37,7 +38,7 @@ export class PipeResolver {
resolve(type: Type<any>, throwIfNotFound = true): Pipe { resolve(type: Type<any>, throwIfNotFound = true): Pipe {
const metas = this._reflector.annotations(resolveForwardRef(type)); const metas = this._reflector.annotations(resolveForwardRef(type));
if (isPresent(metas)) { if (isPresent(metas)) {
const annotation = metas.find(_isPipeMetadata); const annotation = ListWrapper.findLast(metas, _isPipeMetadata);
if (isPresent(annotation)) { if (isPresent(annotation)) {
return annotation; return annotation;
} }

View File

@ -7,9 +7,9 @@
*/ */
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileDirectiveSummary, CompileNgModuleMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata, CompileTypeMetadata} from './compile_metadata'; import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileDirectiveSummary, CompileNgModuleMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata, CompileTypeMetadata, tokenName, tokenReference} from './compile_metadata';
import {isBlank, isPresent} from './facade/lang'; import {isBlank, isPresent} from './facade/lang';
import {Identifiers, resolveIdentifierToken} from './identifiers'; import {Identifiers, createIdentifierToken, resolveIdentifier} from './identifiers';
import {ParseError, ParseSourceSpan} from './parse_util'; import {ParseError, ParseSourceSpan} from './parse_util';
import {AttrAst, DirectiveAst, ProviderAst, ProviderAstType, ReferenceAst} from './template_parser/template_ast'; import {AttrAst, DirectiveAst, ProviderAst, ProviderAstType, ReferenceAst} from './template_parser/template_ast';
@ -31,9 +31,9 @@ export class ProviderViewContext {
constructor(public component: CompileDirectiveMetadata, public sourceSpan: ParseSourceSpan) { constructor(public component: CompileDirectiveMetadata, public sourceSpan: ParseSourceSpan) {
this.viewQueries = _getViewQueries(component); this.viewQueries = _getViewQueries(component);
this.viewProviders = new Map<any, boolean>(); this.viewProviders = new Map<any, boolean>();
_normalizeProviders(component.viewProviders, sourceSpan, this.errors).forEach((provider) => { component.viewProviders.forEach((provider) => {
if (isBlank(this.viewProviders.get(provider.token.reference))) { if (isBlank(this.viewProviders.get(tokenReference(provider.token)))) {
this.viewProviders.set(provider.token.reference, true); this.viewProviders.set(tokenReference(provider.token), true);
} }
}); });
} }
@ -62,17 +62,14 @@ export class ProviderElementContext {
Array.from(this._allProviders.values()).forEach((provider) => { Array.from(this._allProviders.values()).forEach((provider) => {
this._addQueryReadsTo(provider.token, queriedTokens); this._addQueryReadsTo(provider.token, queriedTokens);
}); });
refs.forEach((refAst) => { refs.forEach((refAst) => { this._addQueryReadsTo({value: refAst.name}, queriedTokens); });
this._addQueryReadsTo(new CompileTokenMetadata({value: refAst.name}), queriedTokens); if (isPresent(queriedTokens.get(resolveIdentifier(Identifiers.ViewContainerRef)))) {
});
if (isPresent(
queriedTokens.get(resolveIdentifierToken(Identifiers.ViewContainerRef).reference))) {
this._hasViewContainer = true; this._hasViewContainer = true;
} }
// create the providers that we know are eager first // create the providers that we know are eager first
Array.from(this._allProviders.values()).forEach((provider) => { Array.from(this._allProviders.values()).forEach((provider) => {
const eager = provider.eager || isPresent(queriedTokens.get(provider.token.reference)); const eager = provider.eager || isPresent(queriedTokens.get(tokenReference(provider.token)));
if (eager) { if (eager) {
this._getOrCreateLocalProvider(provider.providerType, provider.token, true); this._getOrCreateLocalProvider(provider.providerType, provider.token, true);
} }
@ -104,8 +101,8 @@ export class ProviderElementContext {
private _addQueryReadsTo(token: CompileTokenMetadata, queryReadTokens: Map<any, boolean>) { private _addQueryReadsTo(token: CompileTokenMetadata, queryReadTokens: Map<any, boolean>) {
this._getQueriesFor(token).forEach((query) => { this._getQueriesFor(token).forEach((query) => {
const queryReadToken = query.read || token; const queryReadToken = query.read || token;
if (isBlank(queryReadTokens.get(queryReadToken.reference))) { if (isBlank(queryReadTokens.get(tokenReference(queryReadToken)))) {
queryReadTokens.set(queryReadToken.reference, true); queryReadTokens.set(tokenReference(queryReadToken), true);
} }
}); });
} }
@ -116,7 +113,7 @@ export class ProviderElementContext {
let distance = 0; let distance = 0;
let queries: CompileQueryMetadata[]; let queries: CompileQueryMetadata[];
while (currentEl !== null) { while (currentEl !== null) {
queries = currentEl._contentQueries.get(token.reference); queries = currentEl._contentQueries.get(tokenReference(token));
if (isPresent(queries)) { if (isPresent(queries)) {
result.push(...queries.filter((query) => query.descendants || distance <= 1)); result.push(...queries.filter((query) => query.descendants || distance <= 1));
} }
@ -125,7 +122,7 @@ export class ProviderElementContext {
} }
currentEl = currentEl._parent; currentEl = currentEl._parent;
} }
queries = this.viewContext.viewQueries.get(token.reference); queries = this.viewContext.viewQueries.get(tokenReference(token));
if (isPresent(queries)) { if (isPresent(queries)) {
result.push(...queries); result.push(...queries);
} }
@ -136,7 +133,7 @@ export class ProviderElementContext {
private _getOrCreateLocalProvider( private _getOrCreateLocalProvider(
requestingProviderType: ProviderAstType, token: CompileTokenMetadata, requestingProviderType: ProviderAstType, token: CompileTokenMetadata,
eager: boolean): ProviderAst { eager: boolean): ProviderAst {
const resolvedProvider = this._allProviders.get(token.reference); const resolvedProvider = this._allProviders.get(tokenReference(token));
if (!resolvedProvider || ((requestingProviderType === ProviderAstType.Directive || if (!resolvedProvider || ((requestingProviderType === ProviderAstType.Directive ||
requestingProviderType === ProviderAstType.PublicService) && requestingProviderType === ProviderAstType.PublicService) &&
resolvedProvider.providerType === ProviderAstType.PrivateService) || resolvedProvider.providerType === ProviderAstType.PrivateService) ||
@ -145,24 +142,23 @@ export class ProviderElementContext {
resolvedProvider.providerType === ProviderAstType.Builtin)) { resolvedProvider.providerType === ProviderAstType.Builtin)) {
return null; return null;
} }
let transformedProviderAst = this._transformedProviders.get(token.reference); let transformedProviderAst = this._transformedProviders.get(tokenReference(token));
if (isPresent(transformedProviderAst)) { if (isPresent(transformedProviderAst)) {
return transformedProviderAst; return transformedProviderAst;
} }
if (isPresent(this._seenProviders.get(token.reference))) { if (isPresent(this._seenProviders.get(tokenReference(token)))) {
this.viewContext.errors.push(new ProviderError( this.viewContext.errors.push(new ProviderError(
`Cannot instantiate cyclic dependency! ${token.name}`, this._sourceSpan)); `Cannot instantiate cyclic dependency! ${tokenName(token)}`, this._sourceSpan));
return null; return null;
} }
this._seenProviders.set(token.reference, true); this._seenProviders.set(tokenReference(token), true);
const transformedProviders = resolvedProvider.providers.map((provider) => { const transformedProviders = resolvedProvider.providers.map((provider) => {
let transformedUseValue = provider.useValue; let transformedUseValue = provider.useValue;
let transformedUseExisting = provider.useExisting; let transformedUseExisting = provider.useExisting;
let transformedDeps: CompileDiDependencyMetadata[]; let transformedDeps: CompileDiDependencyMetadata[];
if (isPresent(provider.useExisting)) { if (isPresent(provider.useExisting)) {
const existingDiDep = this._getDependency( const existingDiDep = this._getDependency(
resolvedProvider.providerType, resolvedProvider.providerType, {token: provider.useExisting}, eager);
new CompileDiDependencyMetadata({token: provider.useExisting}), eager);
if (isPresent(existingDiDep.token)) { if (isPresent(existingDiDep.token)) {
transformedUseExisting = existingDiDep.token; transformedUseExisting = existingDiDep.token;
} else { } else {
@ -186,7 +182,7 @@ export class ProviderElementContext {
}); });
transformedProviderAst = transformedProviderAst =
_transformProviderAst(resolvedProvider, {eager: eager, providers: transformedProviders}); _transformProviderAst(resolvedProvider, {eager: eager, providers: transformedProviders});
this._transformedProviders.set(token.reference, transformedProviderAst); this._transformedProviders.set(tokenReference(token), transformedProviderAst);
return transformedProviderAst; return transformedProviderAst;
} }
@ -195,28 +191,25 @@ export class ProviderElementContext {
eager: boolean = null): CompileDiDependencyMetadata { eager: boolean = null): CompileDiDependencyMetadata {
if (dep.isAttribute) { if (dep.isAttribute) {
const attrValue = this._attrs[dep.token.value]; const attrValue = this._attrs[dep.token.value];
return new CompileDiDependencyMetadata( return {isValue: true, value: attrValue == null ? null : attrValue};
{isValue: true, value: attrValue == null ? null : attrValue});
} }
if (isPresent(dep.token)) { if (isPresent(dep.token)) {
// access builtints // access builtints
if ((requestingProviderType === ProviderAstType.Directive || if ((requestingProviderType === ProviderAstType.Directive ||
requestingProviderType === ProviderAstType.Component)) { requestingProviderType === ProviderAstType.Component)) {
if (dep.token.reference === resolveIdentifierToken(Identifiers.Renderer).reference || if (tokenReference(dep.token) === resolveIdentifier(Identifiers.Renderer) ||
dep.token.reference === resolveIdentifierToken(Identifiers.ElementRef).reference || tokenReference(dep.token) === resolveIdentifier(Identifiers.ElementRef) ||
dep.token.reference === tokenReference(dep.token) === resolveIdentifier(Identifiers.ChangeDetectorRef) ||
resolveIdentifierToken(Identifiers.ChangeDetectorRef).reference || tokenReference(dep.token) === resolveIdentifier(Identifiers.TemplateRef)) {
dep.token.reference === resolveIdentifierToken(Identifiers.TemplateRef).reference) {
return dep; return dep;
} }
if (dep.token.reference === if (tokenReference(dep.token) === resolveIdentifier(Identifiers.ViewContainerRef)) {
resolveIdentifierToken(Identifiers.ViewContainerRef).reference) {
this._hasViewContainer = true; this._hasViewContainer = true;
} }
} }
// access the injector // access the injector
if (dep.token.reference === resolveIdentifierToken(Identifiers.Injector).reference) { if (tokenReference(dep.token) === resolveIdentifier(Identifiers.Injector)) {
return dep; return dep;
} }
// access providers // access providers
@ -238,7 +231,7 @@ export class ProviderElementContext {
} }
if (dep.isSelf) { if (dep.isSelf) {
if (!result && dep.isOptional) { if (!result && dep.isOptional) {
result = new CompileDiDependencyMetadata({isValue: true, value: null}); result = {isValue: true, value: null};
} }
} else { } else {
// check parent elements // check parent elements
@ -252,20 +245,18 @@ export class ProviderElementContext {
} }
// check @Host restriction // check @Host restriction
if (!result) { if (!result) {
if (!dep.isHost || this.viewContext.component.type.isHost || if (!dep.isHost || this.viewContext.component.isHost ||
this.viewContext.component.type.reference === dep.token.reference || this.viewContext.component.type.reference === tokenReference(dep.token) ||
isPresent(this.viewContext.viewProviders.get(dep.token.reference))) { isPresent(this.viewContext.viewProviders.get(tokenReference(dep.token)))) {
result = dep; result = dep;
} else { } else {
result = dep.isOptional ? result = dep.isOptional ? result = {isValue: true, value: null} : null;
result = new CompileDiDependencyMetadata({isValue: true, value: null}) :
null;
} }
} }
} }
if (!result) { if (!result) {
this.viewContext.errors.push( this.viewContext.errors.push(
new ProviderError(`No provider for ${dep.token.name}`, this._sourceSpan)); new ProviderError(`No provider for ${tokenName(dep.token)}`, this._sourceSpan));
} }
return result; return result;
} }
@ -284,16 +275,14 @@ export class NgModuleProviderAnalyzer {
this._allProviders = new Map<any, ProviderAst>(); this._allProviders = new Map<any, ProviderAst>();
const ngModuleTypes = ngModule.transitiveModule.modules.map((moduleMeta) => moduleMeta.type); const ngModuleTypes = ngModule.transitiveModule.modules.map((moduleMeta) => moduleMeta.type);
ngModuleTypes.forEach((ngModuleType: CompileTypeMetadata) => { ngModuleTypes.forEach((ngModuleType: CompileTypeMetadata) => {
const ngModuleProvider = new CompileProviderMetadata( const ngModuleProvider = {token: {identifier: ngModuleType}, useClass: ngModuleType};
{token: new CompileTokenMetadata({identifier: ngModuleType}), useClass: ngModuleType});
_resolveProviders( _resolveProviders(
[ngModuleProvider], ProviderAstType.PublicService, true, sourceSpan, this._errors, [ngModuleProvider], ProviderAstType.PublicService, true, sourceSpan, this._errors,
this._allProviders); this._allProviders);
}); });
_resolveProviders( _resolveProviders(
_normalizeProviders( ngModule.transitiveModule.providers.concat(extraProviders), ProviderAstType.PublicService,
ngModule.transitiveModule.providers.concat(extraProviders), sourceSpan, this._errors), false, sourceSpan, this._errors, this._allProviders);
ProviderAstType.PublicService, false, sourceSpan, this._errors, this._allProviders);
} }
parse(): ProviderAst[] { parse(): ProviderAst[] {
@ -308,28 +297,28 @@ export class NgModuleProviderAnalyzer {
} }
private _getOrCreateLocalProvider(token: CompileTokenMetadata, eager: boolean): ProviderAst { private _getOrCreateLocalProvider(token: CompileTokenMetadata, eager: boolean): ProviderAst {
const resolvedProvider = this._allProviders.get(token.reference); const resolvedProvider = this._allProviders.get(tokenReference(token));
if (!resolvedProvider) { if (!resolvedProvider) {
return null; return null;
} }
let transformedProviderAst = this._transformedProviders.get(token.reference); let transformedProviderAst = this._transformedProviders.get(tokenReference(token));
if (isPresent(transformedProviderAst)) { if (isPresent(transformedProviderAst)) {
return transformedProviderAst; return transformedProviderAst;
} }
if (isPresent(this._seenProviders.get(token.reference))) { if (isPresent(this._seenProviders.get(tokenReference(token)))) {
this._errors.push(new ProviderError( this._errors.push(new ProviderError(
`Cannot instantiate cyclic dependency! ${token.name}`, resolvedProvider.sourceSpan)); `Cannot instantiate cyclic dependency! ${tokenName(token)}`,
resolvedProvider.sourceSpan));
return null; return null;
} }
this._seenProviders.set(token.reference, true); this._seenProviders.set(tokenReference(token), true);
const transformedProviders = resolvedProvider.providers.map((provider) => { const transformedProviders = resolvedProvider.providers.map((provider) => {
let transformedUseValue = provider.useValue; let transformedUseValue = provider.useValue;
let transformedUseExisting = provider.useExisting; let transformedUseExisting = provider.useExisting;
let transformedDeps: CompileDiDependencyMetadata[]; let transformedDeps: CompileDiDependencyMetadata[];
if (isPresent(provider.useExisting)) { if (isPresent(provider.useExisting)) {
const existingDiDep = this._getDependency( const existingDiDep =
new CompileDiDependencyMetadata({token: provider.useExisting}), eager, this._getDependency({token: provider.useExisting}, eager, resolvedProvider.sourceSpan);
resolvedProvider.sourceSpan);
if (isPresent(existingDiDep.token)) { if (isPresent(existingDiDep.token)) {
transformedUseExisting = existingDiDep.token; transformedUseExisting = existingDiDep.token;
} else { } else {
@ -353,7 +342,7 @@ export class NgModuleProviderAnalyzer {
}); });
transformedProviderAst = transformedProviderAst =
_transformProviderAst(resolvedProvider, {eager: eager, providers: transformedProviders}); _transformProviderAst(resolvedProvider, {eager: eager, providers: transformedProviders});
this._transformedProviders.set(token.reference, transformedProviderAst); this._transformedProviders.set(tokenReference(token), transformedProviderAst);
return transformedProviderAst; return transformedProviderAst;
} }
@ -363,9 +352,8 @@ export class NgModuleProviderAnalyzer {
let foundLocal = false; let foundLocal = false;
if (!dep.isSkipSelf && isPresent(dep.token)) { if (!dep.isSkipSelf && isPresent(dep.token)) {
// access the injector // access the injector
if (dep.token.reference === resolveIdentifierToken(Identifiers.Injector).reference || if (tokenReference(dep.token) === resolveIdentifier(Identifiers.Injector) ||
dep.token.reference === tokenReference(dep.token) === resolveIdentifier(Identifiers.ComponentFactoryResolver)) {
resolveIdentifierToken(Identifiers.ComponentFactoryResolver).reference) {
foundLocal = true; foundLocal = true;
// access providers // access providers
} else if (isPresent(this._getOrCreateLocalProvider(dep.token, eager))) { } else if (isPresent(this._getOrCreateLocalProvider(dep.token, eager))) {
@ -375,10 +363,10 @@ export class NgModuleProviderAnalyzer {
let result: CompileDiDependencyMetadata = dep; let result: CompileDiDependencyMetadata = dep;
if (dep.isSelf && !foundLocal) { if (dep.isSelf && !foundLocal) {
if (dep.isOptional) { if (dep.isOptional) {
result = new CompileDiDependencyMetadata({isValue: true, value: null}); result = {isValue: true, value: null};
} else { } else {
this._errors.push( this._errors.push(
new ProviderError(`No provider for ${dep.token.name}`, requestorSourceSpan)); new ProviderError(`No provider for ${tokenName(dep.token)}`, requestorSourceSpan));
} }
} }
return result; return result;
@ -389,7 +377,7 @@ function _transformProvider(
provider: CompileProviderMetadata, provider: CompileProviderMetadata,
{useExisting, useValue, deps}: {useExisting, useValue, deps}:
{useExisting: CompileTokenMetadata, useValue: any, deps: CompileDiDependencyMetadata[]}) { {useExisting: CompileTokenMetadata, useValue: any, deps: CompileDiDependencyMetadata[]}) {
return new CompileProviderMetadata({ return {
token: provider.token, token: provider.token,
useClass: provider.useClass, useClass: provider.useClass,
useExisting: useExisting, useExisting: useExisting,
@ -397,7 +385,7 @@ function _transformProvider(
useValue: useValue, useValue: useValue,
deps: deps, deps: deps,
multi: provider.multi multi: provider.multi
}); };
} }
function _transformProviderAst( function _transformProviderAst(
@ -408,44 +396,13 @@ function _transformProviderAst(
provider.providerType, provider.lifecycleHooks, provider.sourceSpan); provider.providerType, provider.lifecycleHooks, provider.sourceSpan);
} }
function _normalizeProviders(
providers: Array<CompileProviderMetadata|CompileTypeMetadata|any[]>,
sourceSpan: ParseSourceSpan, targetErrors: ParseError[],
targetProviders: CompileProviderMetadata[] = null): CompileProviderMetadata[] {
if (!targetProviders) {
targetProviders = [];
}
if (isPresent(providers)) {
providers.forEach((provider) => {
if (Array.isArray(provider)) {
_normalizeProviders(<any[]>provider, sourceSpan, targetErrors, targetProviders);
} else {
let normalizeProvider: CompileProviderMetadata;
if (provider instanceof CompileProviderMetadata) {
normalizeProvider = provider;
} else if (provider instanceof CompileTypeMetadata) {
normalizeProvider = new CompileProviderMetadata(
{token: new CompileTokenMetadata({identifier: provider}), useClass: provider});
} else {
targetErrors.push(new ProviderError(`Unknown provider type ${provider}`, sourceSpan));
}
if (isPresent(normalizeProvider)) {
targetProviders.push(normalizeProvider);
}
}
});
}
return targetProviders;
}
function _resolveProvidersFromDirectives( function _resolveProvidersFromDirectives(
directives: CompileDirectiveSummary[], sourceSpan: ParseSourceSpan, directives: CompileDirectiveSummary[], sourceSpan: ParseSourceSpan,
targetErrors: ParseError[]): Map<any, ProviderAst> { targetErrors: ParseError[]): Map<any, ProviderAst> {
const providersByToken = new Map<any, ProviderAst>(); const providersByToken = new Map<any, ProviderAst>();
directives.forEach((directive) => { directives.forEach((directive) => {
const dirProvider = new CompileProviderMetadata( const dirProvider:
{token: new CompileTokenMetadata({identifier: directive.type}), useClass: directive.type}); CompileProviderMetadata = {token: {identifier: directive.type}, useClass: directive.type};
_resolveProviders( _resolveProviders(
[dirProvider], [dirProvider],
directive.isComponent ? ProviderAstType.Component : ProviderAstType.Directive, true, directive.isComponent ? ProviderAstType.Component : ProviderAstType.Directive, true,
@ -457,11 +414,11 @@ function _resolveProvidersFromDirectives(
directives.filter(dir => dir.isComponent).concat(directives.filter(dir => !dir.isComponent)); directives.filter(dir => dir.isComponent).concat(directives.filter(dir => !dir.isComponent));
directivesWithComponentFirst.forEach((directive) => { directivesWithComponentFirst.forEach((directive) => {
_resolveProviders( _resolveProviders(
_normalizeProviders(directive.providers, sourceSpan, targetErrors), directive.providers, ProviderAstType.PublicService, false, sourceSpan, targetErrors,
ProviderAstType.PublicService, false, sourceSpan, targetErrors, providersByToken); providersByToken);
_resolveProviders( _resolveProviders(
_normalizeProviders(directive.viewProviders, sourceSpan, targetErrors), directive.viewProviders, ProviderAstType.PrivateService, false, sourceSpan, targetErrors,
ProviderAstType.PrivateService, false, sourceSpan, targetErrors, providersByToken); providersByToken);
}); });
return providersByToken; return providersByToken;
} }
@ -471,21 +428,21 @@ function _resolveProviders(
sourceSpan: ParseSourceSpan, targetErrors: ParseError[], sourceSpan: ParseSourceSpan, targetErrors: ParseError[],
targetProvidersByToken: Map<any, ProviderAst>) { targetProvidersByToken: Map<any, ProviderAst>) {
providers.forEach((provider) => { providers.forEach((provider) => {
let resolvedProvider = targetProvidersByToken.get(provider.token.reference); let resolvedProvider = targetProvidersByToken.get(tokenReference(provider.token));
if (isPresent(resolvedProvider) && resolvedProvider.multiProvider !== provider.multi) { if (isPresent(resolvedProvider) && !!resolvedProvider.multiProvider !== !!provider.multi) {
targetErrors.push(new ProviderError( targetErrors.push(new ProviderError(
`Mixing multi and non multi provider is not possible for token ${resolvedProvider.token.name}`, `Mixing multi and non multi provider is not possible for token ${tokenName(resolvedProvider.token)}`,
sourceSpan)); sourceSpan));
} }
if (!resolvedProvider) { if (!resolvedProvider) {
const lifecycleHooks = const lifecycleHooks = provider.token.identifier &&
provider.token.identifier && provider.token.identifier instanceof CompileTypeMetadata ? (<CompileTypeMetadata>provider.token.identifier).lifecycleHooks ?
provider.token.identifier.lifecycleHooks : (<CompileTypeMetadata>provider.token.identifier).lifecycleHooks :
[]; [];
resolvedProvider = new ProviderAst( resolvedProvider = new ProviderAst(
provider.token, provider.multi, eager || lifecycleHooks.length > 0, [provider], provider.token, provider.multi, eager || lifecycleHooks.length > 0, [provider],
providerType, lifecycleHooks, sourceSpan); providerType, lifecycleHooks, sourceSpan);
targetProvidersByToken.set(provider.token.reference, resolvedProvider); targetProvidersByToken.set(tokenReference(provider.token), resolvedProvider);
} else { } else {
if (!provider.multi) { if (!provider.multi) {
resolvedProvider.providers.length = 0; resolvedProvider.providers.length = 0;
@ -517,10 +474,10 @@ function _getContentQueries(directives: CompileDirectiveSummary[]):
function _addQueryToTokenMap(map: Map<any, CompileQueryMetadata[]>, query: CompileQueryMetadata) { function _addQueryToTokenMap(map: Map<any, CompileQueryMetadata[]>, query: CompileQueryMetadata) {
query.selectors.forEach((token: CompileTokenMetadata) => { query.selectors.forEach((token: CompileTokenMetadata) => {
let entry = map.get(token.reference); let entry = map.get(tokenReference(token));
if (!entry) { if (!entry) {
entry = []; entry = [];
map.set(token.reference, entry); map.set(tokenReference(token), entry);
} }
entry.push(query); entry.push(query);
}); });

View File

@ -7,7 +7,8 @@
*/ */
import {Injectable, ViewEncapsulation} from '@angular/core'; import {Injectable, ViewEncapsulation} from '@angular/core';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileStylesheetMetadata} from './compile_metadata';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileStylesheetMetadata, identifierModuleUrl, identifierName} from './compile_metadata';
import * as o from './output/output_ast'; import * as o from './output/output_ast';
import {ShadowCss} from './shadow_css'; import {ShadowCss} from './shadow_css';
import {UrlResolver} from './url_resolver'; import {UrlResolver} from './url_resolver';
@ -18,7 +19,7 @@ const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
export class StylesCompileDependency { export class StylesCompileDependency {
constructor( constructor(
public moduleUrl: string, public isShimmed: boolean, public name: string, public moduleUrl: string, public isShimmed: boolean,
public valuePlaceholder: CompileIdentifierMetadata) {} public valuePlaceholder: CompileIdentifierMetadata) {}
} }
@ -47,7 +48,7 @@ export class StyleCompiler {
comp, new CompileStylesheetMetadata({ comp, new CompileStylesheetMetadata({
styles: comp.template.styles, styles: comp.template.styles,
styleUrls: comp.template.styleUrls, styleUrls: comp.template.styleUrls,
moduleUrl: comp.type.moduleUrl moduleUrl: identifierModuleUrl(comp.type)
}), }),
true); true);
comp.template.externalStylesheets.forEach((stylesheetMeta) => { comp.template.externalStylesheets.forEach((stylesheetMeta) => {
@ -65,8 +66,9 @@ export class StyleCompiler {
stylesheet.styles.map(plainStyle => o.literal(this._shimIfNeeded(plainStyle, shim))); stylesheet.styles.map(plainStyle => o.literal(this._shimIfNeeded(plainStyle, shim)));
const dependencies: StylesCompileDependency[] = []; const dependencies: StylesCompileDependency[] = [];
for (let i = 0; i < stylesheet.styleUrls.length; i++) { for (let i = 0; i < stylesheet.styleUrls.length; i++) {
const identifier = new CompileIdentifierMetadata({name: getStylesVarName(null)}); const identifier: CompileIdentifierMetadata = {reference: null};
dependencies.push(new StylesCompileDependency(stylesheet.styleUrls[i], shim, identifier)); dependencies.push(new StylesCompileDependency(
getStylesVarName(null), stylesheet.styleUrls[i], shim, identifier));
styleExpressions.push(new o.ExternalExpr(identifier)); styleExpressions.push(new o.ExternalExpr(identifier));
} }
// styles variable contains plain strings and arrays of other styles arrays (recursive), // styles variable contains plain strings and arrays of other styles arrays (recursive),
@ -87,7 +89,7 @@ export class StyleCompiler {
function getStylesVarName(component: CompileDirectiveMetadata): string { function getStylesVarName(component: CompileDirectiveMetadata): string {
let result = `styles`; let result = `styles`;
if (component) { if (component) {
result += `_${component.type.name}`; result += `_${identifierName(component.type)}`;
} }
return result; return result;
} }

View File

@ -8,12 +8,12 @@
import {Inject, Injectable, OpaqueToken, Optional, SchemaMetadata, SecurityContext} from '@angular/core'; import {Inject, Injectable, OpaqueToken, Optional, SchemaMetadata, SecurityContext} from '@angular/core';
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeSummary, CompileTemplateMetadata, CompileTemplateSummary, CompileTokenMetadata, CompileTypeMetadata, removeIdentifierDuplicates} from '../compile_metadata'; import {CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeSummary, CompileTemplateMetadata, CompileTemplateSummary, CompileTokenMetadata, CompileTypeMetadata, identifierName} from '../compile_metadata';
import {AST, ASTWithSource, BindingPipe, EmptyExpr, Interpolation, ParserError, RecursiveAstVisitor, TemplateBinding} from '../expression_parser/ast'; import {AST, ASTWithSource, BindingPipe, EmptyExpr, Interpolation, ParserError, RecursiveAstVisitor, TemplateBinding} from '../expression_parser/ast';
import {Parser} from '../expression_parser/parser'; import {Parser} from '../expression_parser/parser';
import {isPresent} from '../facade/lang'; import {isPresent} from '../facade/lang';
import {I18NHtmlParser} from '../i18n/i18n_html_parser'; import {I18NHtmlParser} from '../i18n/i18n_html_parser';
import {Identifiers, identifierToken, resolveIdentifierToken} from '../identifiers'; import {Identifiers, createIdentifierToken, identifierToken} from '../identifiers';
import * as html from '../ml_parser/ast'; import * as html from '../ml_parser/ast';
import {ParseTreeResult} from '../ml_parser/html_parser'; import {ParseTreeResult} from '../ml_parser/html_parser';
import {expandNodes} from '../ml_parser/icu_ast_expander'; import {expandNodes} from '../ml_parser/icu_ast_expander';
@ -546,7 +546,8 @@ class TemplateParseVisitor implements html.Visitor {
let component: CompileDirectiveSummary = null; let component: CompileDirectiveSummary = null;
const directiveAsts = directives.map((directive) => { const directiveAsts = directives.map((directive) => {
const sourceSpan = new ParseSourceSpan( const sourceSpan = new ParseSourceSpan(
elementSourceSpan.start, elementSourceSpan.end, `Directive ${directive.type.name}`); elementSourceSpan.start, elementSourceSpan.end,
`Directive ${identifierName(directive.type)}`);
if (directive.isComponent) { if (directive.isComponent) {
component = directive; component = directive;
} }
@ -579,7 +580,7 @@ class TemplateParseVisitor implements html.Visitor {
} else if (!component) { } else if (!component) {
let refToken: CompileTokenMetadata = null; let refToken: CompileTokenMetadata = null;
if (isTemplateElement) { if (isTemplateElement) {
refToken = resolveIdentifierToken(Identifiers.TemplateRef); refToken = createIdentifierToken(Identifiers.TemplateRef);
} }
targetReferences.push(new ReferenceAst(elOrDirRef.name, refToken, elOrDirRef.sourceSpan)); targetReferences.push(new ReferenceAst(elOrDirRef.name, refToken, elOrDirRef.sourceSpan));
} }
@ -640,7 +641,7 @@ class TemplateParseVisitor implements html.Visitor {
private _findComponentDirectiveNames(directives: DirectiveAst[]): string[] { private _findComponentDirectiveNames(directives: DirectiveAst[]): string[] {
return this._findComponentDirectives(directives) return this._findComponentDirectives(directives)
.map(directive => directive.directive.type.name); .map(directive => identifierName(directive.directive.type));
} }
private _assertOnlyOneComponent(directives: DirectiveAst[], sourceSpan: ParseSourceSpan) { private _assertOnlyOneComponent(directives: DirectiveAst[], sourceSpan: ParseSourceSpan) {

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {isBlank, isPrimitive, isStrictStringMap} from './facade/lang'; import {isPrimitive, isStrictStringMap} from './facade/lang';
export const MODULE_SUFFIX = ''; export const MODULE_SUFFIX = '';
@ -35,10 +35,6 @@ function _splitAt(input: string, character: string, defaultValues: string[]): st
return [input.slice(0, characterIndex).trim(), input.slice(characterIndex + 1).trim()]; return [input.slice(0, characterIndex).trim(), input.slice(characterIndex + 1).trim()];
} }
export function sanitizeIdentifier(name: string): string {
return name.replace(/\W/g, '_');
}
export function visitValue(value: any, visitor: ValueVisitor, context: any): any { export function visitValue(value: any, visitor: ValueVisitor, context: any): any {
if (Array.isArray(value)) { if (Array.isArray(value)) {
return visitor.visitArray(<any[]>value, context); return visitor.visitArray(<any[]>value, context);
@ -48,7 +44,7 @@ export function visitValue(value: any, visitor: ValueVisitor, context: any): any
return visitor.visitStringMap(<{[key: string]: any}>value, context); return visitor.visitStringMap(<{[key: string]: any}>value, context);
} }
if (isBlank(value) || isPrimitive(value)) { if (value == null || isPrimitive(value)) {
return visitor.visitPrimitive(value, context); return visitor.visitPrimitive(value, context);
} }

View File

@ -7,11 +7,11 @@
*/ */
import {CompileDiDependencyMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata} from '../compile_metadata'; import {CompileDiDependencyMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata, identifierName, tokenName, tokenReference} from '../compile_metadata';
import {createDiTokenExpression} from '../compiler_util/identifier_util'; import {createDiTokenExpression} from '../compiler_util/identifier_util';
import {DirectiveWrapperCompiler, DirectiveWrapperExpressions} from '../directive_wrapper_compiler'; import {DirectiveWrapperCompiler, DirectiveWrapperExpressions} from '../directive_wrapper_compiler';
import {isPresent} from '../facade/lang'; import {isPresent} from '../facade/lang';
import {Identifiers, identifierToken, resolveIdentifier, resolveIdentifierToken} from '../identifiers'; import {Identifiers, createIdentifier, createIdentifierToken, identifierToken, resolveIdentifier} from '../identifiers';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';
import {convertValueToOutputAst} from '../output/value_util'; import {convertValueToOutputAst} from '../output/value_util';
import {ProviderAst, ProviderAstType, ReferenceAst, TemplateAst} from '../template_parser/template_ast'; import {ProviderAst, ProviderAstType, ReferenceAst, TemplateAst} from '../template_parser/template_ast';
@ -20,7 +20,7 @@ import {CompileMethod} from './compile_method';
import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query'; import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query';
import {CompileView, CompileViewRootNode} from './compile_view'; import {CompileView, CompileViewRootNode} from './compile_view';
import {InjectMethodVars, ViewProperties} from './constants'; import {InjectMethodVars, ViewProperties} from './constants';
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency} from './deps'; import {ComponentFactoryDependency, DirectiveWrapperDependency} from './deps';
import {getPropertyInView, injectFromViewParentInjector} from './util'; import {getPropertyInView, injectFromViewParentInjector} from './util';
export class CompileNode { export class CompileNode {
@ -35,7 +35,7 @@ export class CompileNode {
export class CompileElement extends CompileNode { export class CompileElement extends CompileNode {
static createNull(): CompileElement { static createNull(): CompileElement {
return new CompileElement(null, null, null, null, null, null, [], [], false, false, [], []); return new CompileElement(null, null, null, null, null, null, [], [], false, false, []);
} }
public compViewExpr: o.Expression = null; public compViewExpr: o.Expression = null;
@ -57,21 +57,18 @@ export class CompileElement extends CompileNode {
sourceAst: TemplateAst, public component: CompileDirectiveSummary, sourceAst: TemplateAst, public component: CompileDirectiveSummary,
private _directives: CompileDirectiveSummary[], private _directives: CompileDirectiveSummary[],
private _resolvedProvidersArray: ProviderAst[], public hasViewContainer: boolean, private _resolvedProvidersArray: ProviderAst[], public hasViewContainer: boolean,
public hasEmbeddedView: boolean, references: ReferenceAst[], public hasEmbeddedView: boolean, references: ReferenceAst[]) {
private _targetDependencies:
Array<ViewClassDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {
super(parent, view, nodeIndex, renderNode, sourceAst); super(parent, view, nodeIndex, renderNode, sourceAst);
this.referenceTokens = {}; this.referenceTokens = {};
references.forEach(ref => this.referenceTokens[ref.name] = ref.value); references.forEach(ref => this.referenceTokens[ref.name] = ref.value);
this.elementRef = this.elementRef =
o.importExpr(resolveIdentifier(Identifiers.ElementRef)).instantiate([this.renderNode]); o.importExpr(createIdentifier(Identifiers.ElementRef)).instantiate([this.renderNode]);
this.instances.set(resolveIdentifierToken(Identifiers.ElementRef).reference, this.elementRef); this.instances.set(resolveIdentifier(Identifiers.ElementRef), this.elementRef);
this.instances.set( this.instances.set(
resolveIdentifierToken(Identifiers.Injector).reference, resolveIdentifier(Identifiers.Injector),
o.THIS_EXPR.callMethod('injector', [o.literal(this.nodeIndex)])); o.THIS_EXPR.callMethod('injector', [o.literal(this.nodeIndex)]));
this.instances.set( this.instances.set(resolveIdentifier(Identifiers.Renderer), o.THIS_EXPR.prop('renderer'));
resolveIdentifierToken(Identifiers.Renderer).reference, o.THIS_EXPR.prop('renderer'));
if (this.hasViewContainer || this.hasEmbeddedView) { if (this.hasViewContainer || this.hasEmbeddedView) {
this._createViewContainer(); this._createViewContainer();
} }
@ -85,41 +82,40 @@ export class CompileElement extends CompileNode {
const parentNodeIndex = this.isRootElement() ? null : this.parent.nodeIndex; const parentNodeIndex = this.isRootElement() ? null : this.parent.nodeIndex;
// private is fine here as no child view will reference a ViewContainer // private is fine here as no child view will reference a ViewContainer
this.view.fields.push(new o.ClassField( this.view.fields.push(new o.ClassField(
fieldName, o.importType(resolveIdentifier(Identifiers.ViewContainer)), fieldName, o.importType(createIdentifier(Identifiers.ViewContainer)),
[o.StmtModifier.Private])); [o.StmtModifier.Private]));
const statement = const statement =
o.THIS_EXPR.prop(fieldName) o.THIS_EXPR.prop(fieldName)
.set(o.importExpr(resolveIdentifier(Identifiers.ViewContainer)).instantiate([ .set(o.importExpr(createIdentifier(Identifiers.ViewContainer)).instantiate([
o.literal(this.nodeIndex), o.literal(parentNodeIndex), o.THIS_EXPR, this.renderNode o.literal(this.nodeIndex), o.literal(parentNodeIndex), o.THIS_EXPR, this.renderNode
])) ]))
.toStmt(); .toStmt();
this.view.createMethod.addStmt(statement); this.view.createMethod.addStmt(statement);
this.viewContainer = o.THIS_EXPR.prop(fieldName); this.viewContainer = o.THIS_EXPR.prop(fieldName);
this.instances.set( this.instances.set(resolveIdentifier(Identifiers.ViewContainer), this.viewContainer);
resolveIdentifierToken(Identifiers.ViewContainer).reference, this.viewContainer);
this.view.viewContainers.push(this.viewContainer); this.view.viewContainers.push(this.viewContainer);
} }
private _createComponentFactoryResolver() { private _createComponentFactoryResolver() {
const entryComponents = const entryComponents =
this.component.entryComponents.map((entryComponent: CompileIdentifierMetadata) => { this.component.entryComponents.map((entryComponent: CompileIdentifierMetadata) => {
const id = new CompileIdentifierMetadata({name: entryComponent.name}); const id: CompileIdentifierMetadata = {reference: null};
this._targetDependencies.push(new ComponentFactoryDependency(entryComponent, id)); this.view.targetDependencies.push(new ComponentFactoryDependency(entryComponent, id));
return id; return id;
}); });
if (!entryComponents || entryComponents.length === 0) { if (!entryComponents || entryComponents.length === 0) {
return; return;
} }
const createComponentFactoryResolverExpr = const createComponentFactoryResolverExpr =
o.importExpr(resolveIdentifier(Identifiers.CodegenComponentFactoryResolver)).instantiate([ o.importExpr(createIdentifier(Identifiers.CodegenComponentFactoryResolver)).instantiate([
o.literalArr(entryComponents.map((entryComponent) => o.importExpr(entryComponent))), o.literalArr(entryComponents.map((entryComponent) => o.importExpr(entryComponent))),
injectFromViewParentInjector( injectFromViewParentInjector(
this.view, resolveIdentifierToken(Identifiers.ComponentFactoryResolver), false) this.view, createIdentifierToken(Identifiers.ComponentFactoryResolver), false)
]); ]);
const provider = new CompileProviderMetadata({ const provider: CompileProviderMetadata = {
token: resolveIdentifierToken(Identifiers.ComponentFactoryResolver), token: createIdentifierToken(Identifiers.ComponentFactoryResolver),
useValue: createComponentFactoryResolverExpr useValue: createComponentFactoryResolverExpr
}); };
// Add ComponentFactoryResolver as first provider as it does not have deps on other providers // Add ComponentFactoryResolver as first provider as it does not have deps on other providers
// ProviderAstType.PrivateService as only the component and its view can see it, // ProviderAstType.PrivateService as only the component and its view can see it,
// but nobody else // but nobody else
@ -141,13 +137,13 @@ export class CompileElement extends CompileNode {
this.embeddedView = embeddedView; this.embeddedView = embeddedView;
if (isPresent(embeddedView)) { if (isPresent(embeddedView)) {
const createTemplateRefExpr = const createTemplateRefExpr =
o.importExpr(resolveIdentifier(Identifiers.TemplateRef_)).instantiate([ o.importExpr(createIdentifier(Identifiers.TemplateRef_)).instantiate([
o.THIS_EXPR, o.literal(this.nodeIndex), this.renderNode o.THIS_EXPR, o.literal(this.nodeIndex), this.renderNode
]); ]);
const provider = new CompileProviderMetadata({ const provider: CompileProviderMetadata = {
token: resolveIdentifierToken(Identifiers.TemplateRef), token: createIdentifierToken(Identifiers.TemplateRef),
useValue: createTemplateRefExpr useValue: createTemplateRefExpr
}); };
// Add TemplateRef as first provider as it does not have deps on other providers // Add TemplateRef as first provider as it does not have deps on other providers
this._resolvedProvidersArray.unshift(new ProviderAst( this._resolvedProvidersArray.unshift(new ProviderAst(
provider.token, false, true, [provider], ProviderAstType.Builtin, [], provider.token, false, true, [provider], ProviderAstType.Builtin, [],
@ -158,13 +154,12 @@ export class CompileElement extends CompileNode {
beforeChildren(): void { beforeChildren(): void {
if (this.hasViewContainer) { if (this.hasViewContainer) {
this.instances.set( this.instances.set(
resolveIdentifierToken(Identifiers.ViewContainerRef).reference, resolveIdentifier(Identifiers.ViewContainerRef), this.viewContainer.prop('vcRef'));
this.viewContainer.prop('vcRef'));
} }
this._resolvedProviders = new Map<any, ProviderAst>(); this._resolvedProviders = new Map<any, ProviderAst>();
this._resolvedProvidersArray.forEach( this._resolvedProvidersArray.forEach(
provider => this._resolvedProviders.set(provider.token.reference, provider)); provider => this._resolvedProviders.set(tokenReference(provider.token), provider));
// create all the provider instances, some in the view constructor, // create all the provider instances, some in the view constructor,
// some as getters. We rely on the fact that they are already sorted topologically. // some as getters. We rely on the fact that they are already sorted topologically.
@ -173,9 +168,7 @@ export class CompileElement extends CompileNode {
resolvedProvider.providerType === ProviderAstType.Directive; resolvedProvider.providerType === ProviderAstType.Directive;
const providerValueExpressions = resolvedProvider.providers.map((provider) => { const providerValueExpressions = resolvedProvider.providers.map((provider) => {
if (provider.useExisting) { if (provider.useExisting) {
return this._getDependency( return this._getDependency(resolvedProvider.providerType, {token: provider.useExisting});
resolvedProvider.providerType,
new CompileDiDependencyMetadata({token: provider.useExisting}));
} else if (provider.useFactory) { } else if (provider.useFactory) {
const deps = provider.deps || provider.useFactory.diDeps; const deps = provider.deps || provider.useFactory.diDeps;
const depsExpr = const depsExpr =
@ -186,10 +179,10 @@ export class CompileElement extends CompileNode {
const depsExpr = const depsExpr =
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep)); deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep));
if (isDirectiveWrapper) { if (isDirectiveWrapper) {
const directiveWrapperIdentifier = new CompileIdentifierMetadata( const directiveWrapperIdentifier: CompileIdentifierMetadata = {reference: null};
{name: DirectiveWrapperCompiler.dirWrapperClassName(provider.useClass)}); this.view.targetDependencies.push(new DirectiveWrapperDependency(
this._targetDependencies.push( provider.useClass, DirectiveWrapperCompiler.dirWrapperClassName(provider.useClass),
new DirectiveWrapperDependency(provider.useClass, directiveWrapperIdentifier)); directiveWrapperIdentifier));
return DirectiveWrapperExpressions.create(directiveWrapperIdentifier, depsExpr); return DirectiveWrapperExpressions.create(directiveWrapperIdentifier, depsExpr);
} else { } else {
return o.importExpr(provider.useClass) return o.importExpr(provider.useClass)
@ -199,22 +192,23 @@ export class CompileElement extends CompileNode {
return convertValueToOutputAst(provider.useValue); return convertValueToOutputAst(provider.useValue);
} }
}); });
const propName = `_${resolvedProvider.token.name}_${this.nodeIndex}_${this.instances.size}`; const propName =
`_${tokenName(resolvedProvider.token)}_${this.nodeIndex}_${this.instances.size}`;
const instance = createProviderProperty( const instance = createProviderProperty(
propName, resolvedProvider, providerValueExpressions, resolvedProvider.multiProvider, propName, resolvedProvider, providerValueExpressions, resolvedProvider.multiProvider,
resolvedProvider.eager, this); resolvedProvider.eager, this);
if (isDirectiveWrapper) { if (isDirectiveWrapper) {
this.directiveWrapperInstance.set(resolvedProvider.token.reference, instance); this.directiveWrapperInstance.set(tokenReference(resolvedProvider.token), instance);
this.instances.set( this.instances.set(
resolvedProvider.token.reference, DirectiveWrapperExpressions.context(instance)); tokenReference(resolvedProvider.token), DirectiveWrapperExpressions.context(instance));
} else { } else {
this.instances.set(resolvedProvider.token.reference, instance); this.instances.set(tokenReference(resolvedProvider.token), instance);
} }
}); });
for (let i = 0; i < this._directives.length; i++) { for (let i = 0; i < this._directives.length; i++) {
const directive = this._directives[i]; const directive = this._directives[i];
const directiveInstance = this.instances.get(identifierToken(directive.type).reference); const directiveInstance = this.instances.get(tokenReference(identifierToken(directive.type)));
directive.queries.forEach((queryMeta) => { this._addQuery(queryMeta, directiveInstance); }); directive.queries.forEach((queryMeta) => { this._addQuery(queryMeta, directiveInstance); });
} }
const queriesWithReads: _QueryWithRead[] = []; const queriesWithReads: _QueryWithRead[] = [];
@ -227,12 +221,12 @@ export class CompileElement extends CompileNode {
const token = this.referenceTokens[varName]; const token = this.referenceTokens[varName];
let varValue: o.Expression; let varValue: o.Expression;
if (token) { if (token) {
varValue = this.instances.get(token.reference); varValue = this.instances.get(tokenReference(token));
} else { } else {
varValue = this.renderNode; varValue = this.renderNode;
} }
this.view.locals.set(varName, varValue); this.view.locals.set(varName, varValue);
const varToken = new CompileTokenMetadata({value: varName}); const varToken = {value: varName};
queriesWithReads.push( queriesWithReads.push(
...this._getQueriesFor(varToken).map(query => new _QueryWithRead(query, varToken))); ...this._getQueriesFor(varToken).map(query => new _QueryWithRead(query, varToken)));
}); });
@ -240,12 +234,12 @@ export class CompileElement extends CompileNode {
let value: o.Expression; let value: o.Expression;
if (isPresent(queryWithRead.read.identifier)) { if (isPresent(queryWithRead.read.identifier)) {
// query for an identifier // query for an identifier
value = this.instances.get(queryWithRead.read.reference); value = this.instances.get(tokenReference(queryWithRead.read));
} else { } else {
// query for a reference // query for a reference
const token = this.referenceTokens[queryWithRead.read.value]; const token = this.referenceTokens[queryWithRead.read.value];
if (isPresent(token)) { if (isPresent(token)) {
value = this.instances.get(token.reference); value = this.instances.get(tokenReference(token));
} else { } else {
value = this.elementRef; value = this.elementRef;
} }
@ -261,7 +255,7 @@ export class CompileElement extends CompileNode {
// Note: afterChildren is called after recursing into children. // Note: afterChildren is called after recursing into children.
// This is good so that an injector match in an element that is closer to a requesting element // This is good so that an injector match in an element that is closer to a requesting element
// matches first. // matches first.
const providerExpr = this.instances.get(resolvedProvider.token.reference); const providerExpr = this.instances.get(tokenReference(resolvedProvider.token));
// Note: view providers are only visible on the injector of that element. // Note: view providers are only visible on the injector of that element.
// This is not fully correct as the rules during codegen don't allow a directive // This is not fully correct as the rules during codegen don't allow a directive
// to get hold of a view provdier on the same element. We still do this semantic // to get hold of a view provdier on the same element. We still do this semantic
@ -285,7 +279,7 @@ export class CompileElement extends CompileNode {
getComponent(): o.Expression { getComponent(): o.Expression {
return isPresent(this.component) ? return isPresent(this.component) ?
this.instances.get(identifierToken(this.component.type).reference) : this.instances.get(tokenReference(identifierToken(this.component.type))) :
null; null;
} }
@ -300,7 +294,7 @@ export class CompileElement extends CompileNode {
let distance = 0; let distance = 0;
let queries: CompileQuery[]; let queries: CompileQuery[];
while (!currentEl.isNull()) { while (!currentEl.isNull()) {
queries = currentEl._queries.get(token.reference); queries = currentEl._queries.get(tokenReference(token));
if (isPresent(queries)) { if (isPresent(queries)) {
result.push(...queries.filter((query) => query.meta.descendants || distance <= 1)); result.push(...queries.filter((query) => query.meta.descendants || distance <= 1));
} }
@ -309,7 +303,7 @@ export class CompileElement extends CompileNode {
} }
currentEl = currentEl.parent; currentEl = currentEl.parent;
} }
queries = this.view.componentView.viewQueries.get(token.reference); queries = this.view.componentView.viewQueries.get(tokenReference(token));
if (isPresent(queries)) { if (isPresent(queries)) {
result.push(...queries); result.push(...queries);
} }
@ -319,7 +313,7 @@ export class CompileElement extends CompileNode {
private _addQuery(queryMeta: CompileQueryMetadata, directiveInstance: o.Expression): private _addQuery(queryMeta: CompileQueryMetadata, directiveInstance: o.Expression):
CompileQuery { CompileQuery {
const propName = const propName =
`_query_${queryMeta.selectors[0].name}_${this.nodeIndex}_${this._queryCount++}`; `_query_${tokenName(queryMeta.selectors[0])}_${this.nodeIndex}_${this._queryCount++}`;
const queryList = createQueryList(queryMeta, directiveInstance, propName, this.view); const queryList = createQueryList(queryMeta, directiveInstance, propName, this.view);
const query = new CompileQuery(queryMeta, queryList, directiveInstance, this.view); const query = new CompileQuery(queryMeta, queryList, directiveInstance, this.view);
addQueryToTokenMap(this._queries, query); addQueryToTokenMap(this._queries, query);
@ -332,8 +326,7 @@ export class CompileElement extends CompileNode {
if (isPresent(dep.token)) { if (isPresent(dep.token)) {
// access builtins with special visibility // access builtins with special visibility
if (!result) { if (!result) {
if (dep.token.reference === if (tokenReference(dep.token) === resolveIdentifier(Identifiers.ChangeDetectorRef)) {
resolveIdentifierToken(Identifiers.ChangeDetectorRef).reference) {
if (requestingProviderType === ProviderAstType.Component) { if (requestingProviderType === ProviderAstType.Component) {
return this.compViewExpr.prop('ref'); return this.compViewExpr.prop('ref');
} else { } else {
@ -343,7 +336,7 @@ export class CompileElement extends CompileNode {
} }
// access regular providers on the element // access regular providers on the element
if (!result) { if (!result) {
const resolvedProvider = this._resolvedProviders.get(dep.token.reference); const resolvedProvider = this._resolvedProviders.get(tokenReference(dep.token));
// don't allow directives / public services to access private services. // don't allow directives / public services to access private services.
// only components and private services can access private services. // only components and private services can access private services.
if (resolvedProvider && (requestingProviderType === ProviderAstType.Directive || if (resolvedProvider && (requestingProviderType === ProviderAstType.Directive ||
@ -351,7 +344,7 @@ export class CompileElement extends CompileNode {
resolvedProvider.providerType === ProviderAstType.PrivateService) { resolvedProvider.providerType === ProviderAstType.PrivateService) {
return null; return null;
} }
result = this.instances.get(dep.token.reference); result = this.instances.get(tokenReference(dep.token));
} }
} }
return result; return result;
@ -370,8 +363,7 @@ export class CompileElement extends CompileNode {
// check parent elements // check parent elements
while (!result && !currElement.parent.isNull()) { while (!result && !currElement.parent.isNull()) {
currElement = currElement.parent; currElement = currElement.parent;
result = currElement._getLocalDependency( result = currElement._getLocalDependency(ProviderAstType.PublicService, {token: dep.token});
ProviderAstType.PublicService, new CompileDiDependencyMetadata({token: dep.token}));
} }
if (!result) { if (!result) {

View File

@ -7,9 +7,9 @@
*/ */
import {CompilePipeSummary} from '../compile_metadata'; import {CompilePipeSummary, tokenReference} from '../compile_metadata';
import {createPureProxy} from '../compiler_util/identifier_util'; import {createPureProxy} from '../compiler_util/identifier_util';
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from '../identifiers'; import {Identifiers, createIdentifier, resolveIdentifier} from '../identifiers';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';
import {CompileView} from './compile_view'; import {CompileView} from './compile_view';
@ -42,8 +42,7 @@ export class CompilePipe {
constructor(public view: CompileView, public meta: CompilePipeSummary) { constructor(public view: CompileView, public meta: CompilePipeSummary) {
this.instance = o.THIS_EXPR.prop(`_pipe_${meta.name}_${view.pipeCount++}`); this.instance = o.THIS_EXPR.prop(`_pipe_${meta.name}_${view.pipeCount++}`);
const deps = this.meta.type.diDeps.map((diDep) => { const deps = this.meta.type.diDeps.map((diDep) => {
if (diDep.token.reference === if (tokenReference(diDep.token) === resolveIdentifier(Identifiers.ChangeDetectorRef)) {
resolveIdentifierToken(Identifiers.ChangeDetectorRef).reference) {
return getPropertyInView(o.THIS_EXPR.prop('ref'), this.view, this.view.componentView); return getPropertyInView(o.THIS_EXPR.prop('ref'), this.view, this.view.componentView);
} }
return injectFromViewParentInjector(view, diDep.token, false); return injectFromViewParentInjector(view, diDep.token, false);
@ -69,7 +68,7 @@ export class CompilePipe {
.callMethod(o.BuiltinMethod.Bind, [pipeInstanceSeenFromPureProxy]), .callMethod(o.BuiltinMethod.Bind, [pipeInstanceSeenFromPureProxy]),
args.length, purePipeProxyInstance, args.length, purePipeProxyInstance,
{fields: callingView.fields, ctorStmts: callingView.createMethod}); {fields: callingView.fields, ctorStmts: callingView.createMethod});
return o.importExpr(resolveIdentifier(Identifiers.castByValue)) return o.importExpr(createIdentifier(Identifiers.castByValue))
.callFn([purePipeProxyInstance, pipeInstanceSeenFromPureProxy.prop('transform')]) .callFn([purePipeProxyInstance, pipeInstanceSeenFromPureProxy.prop('transform')])
.callFn(args); .callFn(args);
} else { } else {

View File

@ -6,10 +6,10 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {CompileQueryMetadata} from '../compile_metadata'; import {CompileQueryMetadata, tokenReference} from '../compile_metadata';
import {ListWrapper} from '../facade/collection'; import {ListWrapper} from '../facade/collection';
import {isPresent} from '../facade/lang'; import {isPresent} from '../facade/lang';
import {Identifiers, resolveIdentifier} from '../identifiers'; import {Identifiers, createIdentifier} from '../identifiers';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';
import {CompileElement} from './compile_element'; import {CompileElement} from './compile_element';
@ -114,22 +114,22 @@ export function createQueryList(
query: CompileQueryMetadata, directiveInstance: o.Expression, propertyName: string, query: CompileQueryMetadata, directiveInstance: o.Expression, propertyName: string,
compileView: CompileView): o.Expression { compileView: CompileView): o.Expression {
compileView.fields.push(new o.ClassField( compileView.fields.push(new o.ClassField(
propertyName, o.importType(resolveIdentifier(Identifiers.QueryList), [o.DYNAMIC_TYPE]))); propertyName, o.importType(createIdentifier(Identifiers.QueryList), [o.DYNAMIC_TYPE])));
const expr = o.THIS_EXPR.prop(propertyName); const expr = o.THIS_EXPR.prop(propertyName);
compileView.createMethod.addStmt( compileView.createMethod.addStmt(
o.THIS_EXPR.prop(propertyName) o.THIS_EXPR.prop(propertyName)
.set(o.importExpr(resolveIdentifier(Identifiers.QueryList), [o.DYNAMIC_TYPE]) .set(o.importExpr(createIdentifier(Identifiers.QueryList), [o.DYNAMIC_TYPE]).instantiate([
.instantiate([])) ]))
.toStmt()); .toStmt());
return expr; return expr;
} }
export function addQueryToTokenMap(map: Map<any, CompileQuery[]>, query: CompileQuery) { export function addQueryToTokenMap(map: Map<any, CompileQuery[]>, query: CompileQuery) {
query.meta.selectors.forEach((selector) => { query.meta.selectors.forEach((selector) => {
let entry = map.get(selector.reference); let entry = map.get(tokenReference(selector));
if (!entry) { if (!entry) {
entry = []; entry = [];
map.set(selector.reference, entry); map.set(tokenReference(selector), entry);
} }
entry.push(query); entry.push(query);
}); });

View File

@ -7,12 +7,12 @@
*/ */
import {AnimationEntryCompileResult} from '../animation/animation_compiler'; import {AnimationEntryCompileResult} from '../animation/animation_compiler';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeSummary} from '../compile_metadata'; import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeSummary, tokenName} from '../compile_metadata';
import {EventHandlerVars, NameResolver} from '../compiler_util/expression_converter'; import {EventHandlerVars, NameResolver} from '../compiler_util/expression_converter';
import {createPureProxy} from '../compiler_util/identifier_util'; import {createPureProxy} from '../compiler_util/identifier_util';
import {CompilerConfig} from '../config'; import {CompilerConfig} from '../config';
import {isPresent} from '../facade/lang'; import {isPresent} from '../facade/lang';
import {Identifiers, resolveIdentifier} from '../identifiers'; import {Identifiers, createIdentifier} from '../identifiers';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';
import {ViewType} from '../private_import_core'; import {ViewType} from '../private_import_core';
@ -20,6 +20,7 @@ import {CompileElement, CompileNode} from './compile_element';
import {CompileMethod} from './compile_method'; import {CompileMethod} from './compile_method';
import {CompilePipe} from './compile_pipe'; import {CompilePipe} from './compile_pipe';
import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query'; import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query';
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency} from './deps';
import {getPropertyInView, getViewClassName} from './util'; import {getPropertyInView, getViewClassName} from './util';
export enum CompileViewRootNodeType { export enum CompileViewRootNodeType {
@ -84,7 +85,9 @@ export class CompileView implements NameResolver {
public component: CompileDirectiveMetadata, public genConfig: CompilerConfig, public component: CompileDirectiveMetadata, public genConfig: CompilerConfig,
public pipeMetas: CompilePipeSummary[], public styles: o.Expression, public pipeMetas: CompilePipeSummary[], public styles: o.Expression,
public animations: AnimationEntryCompileResult[], public viewIndex: number, public animations: AnimationEntryCompileResult[], public viewIndex: number,
public declarationElement: CompileElement, public templateVariableBindings: string[][]) { public declarationElement: CompileElement, public templateVariableBindings: string[][],
public targetDependencies:
Array<ViewClassDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {
this.createMethod = new CompileMethod(this); this.createMethod = new CompileMethod(this);
this.animationBindingsMethod = new CompileMethod(this); this.animationBindingsMethod = new CompileMethod(this);
this.injectorGetMethod = new CompileMethod(this); this.injectorGetMethod = new CompileMethod(this);
@ -101,7 +104,7 @@ export class CompileView implements NameResolver {
this.viewType = getViewType(component, viewIndex); this.viewType = getViewType(component, viewIndex);
this.className = getViewClassName(component, viewIndex); this.className = getViewClassName(component, viewIndex);
this.classType = o.importType(new CompileIdentifierMetadata({name: this.className})); this.classType = o.expressionType(o.variable(this.className));
this.classExpr = o.variable(this.className); this.classExpr = o.variable(this.className);
if (this.viewType === ViewType.COMPONENT || this.viewType === ViewType.HOST) { if (this.viewType === ViewType.COMPONENT || this.viewType === ViewType.HOST) {
this.componentView = this; this.componentView = this;
@ -115,7 +118,7 @@ export class CompileView implements NameResolver {
if (this.viewType === ViewType.COMPONENT) { if (this.viewType === ViewType.COMPONENT) {
const directiveInstance = o.THIS_EXPR.prop('context'); const directiveInstance = o.THIS_EXPR.prop('context');
this.component.viewQueries.forEach((queryMeta, queryIndex) => { this.component.viewQueries.forEach((queryMeta, queryIndex) => {
const propName = `_viewQuery_${queryMeta.selectors[0].name}_${queryIndex}`; const propName = `_viewQuery_${tokenName(queryMeta.selectors[0])}_${queryIndex}`;
const queryList = createQueryList(queryMeta, directiveInstance, propName, this); const queryList = createQueryList(queryMeta, directiveInstance, propName, this);
const query = new CompileQuery(queryMeta, queryList, directiveInstance, this); const query = new CompileQuery(queryMeta, queryList, directiveInstance, this);
addQueryToTokenMap(viewQueries, query); addQueryToTokenMap(viewQueries, query);
@ -164,7 +167,7 @@ function getViewType(component: CompileDirectiveMetadata, embeddedTemplateIndex:
return ViewType.EMBEDDED; return ViewType.EMBEDDED;
} }
if (component.type.isHost) { if (component.isHost) {
return ViewType.HOST; return ViewType.HOST;
} }

View File

@ -10,7 +10,8 @@ import {CompileIdentifierMetadata} from '../compile_metadata';
export class ViewClassDependency { export class ViewClassDependency {
constructor( constructor(
public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {} public comp: CompileIdentifierMetadata, public name: string,
public placeholder: CompileIdentifierMetadata) {}
} }
export class ComponentFactoryDependency { export class ComponentFactoryDependency {
@ -20,5 +21,6 @@ export class ComponentFactoryDependency {
export class DirectiveWrapperDependency { export class DirectiveWrapperDependency {
constructor( constructor(
public dir: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {} public dir: CompileIdentifierMetadata, public name: string,
public placeholder: CompileIdentifierMetadata) {}
} }

View File

@ -9,7 +9,7 @@
import {EventHandlerVars, convertActionBinding} from '../compiler_util/expression_converter'; import {EventHandlerVars, convertActionBinding} from '../compiler_util/expression_converter';
import {createInlineArray} from '../compiler_util/identifier_util'; import {createInlineArray} from '../compiler_util/identifier_util';
import {DirectiveWrapperExpressions} from '../directive_wrapper_compiler'; import {DirectiveWrapperExpressions} from '../directive_wrapper_compiler';
import {Identifiers, resolveIdentifier} from '../identifiers'; import {Identifiers, createIdentifier} from '../identifiers';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';
import {BoundEventAst, DirectiveAst} from '../template_parser/template_ast'; import {BoundEventAst, DirectiveAst} from '../template_parser/template_ast';
@ -56,7 +56,7 @@ function subscribeToRenderEvents(
compileElement.view.disposables.push(disposableVar); compileElement.view.disposables.push(disposableVar);
compileElement.view.createMethod.addStmt( compileElement.view.createMethod.addStmt(
disposableVar disposableVar
.set(o.importExpr(resolveIdentifier(Identifiers.subscribeToRenderElement)).callFn([ .set(o.importExpr(createIdentifier(Identifiers.subscribeToRenderElement)).callFn([
o.THIS_EXPR, compileElement.renderNode, createInlineArray(eventAndTargetExprs), o.THIS_EXPR, compileElement.renderNode, createInlineArray(eventAndTargetExprs),
handleEventExpr(compileElement) handleEventExpr(compileElement)
])) ]))

View File

@ -13,7 +13,7 @@ import {ConvertPropertyBindingResult, convertPropertyBinding} from '../compiler_
import {createEnumExpression} from '../compiler_util/identifier_util'; import {createEnumExpression} from '../compiler_util/identifier_util';
import {triggerAnimation, writeToRenderer} from '../compiler_util/render_util'; import {triggerAnimation, writeToRenderer} from '../compiler_util/render_util';
import {DirectiveWrapperExpressions} from '../directive_wrapper_compiler'; import {DirectiveWrapperExpressions} from '../directive_wrapper_compiler';
import {Identifiers, resolveIdentifier} from '../identifiers'; import {Identifiers, createIdentifier} from '../identifiers';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';
import {isDefaultChangeDetectionStrategy} from '../private_import_core'; import {isDefaultChangeDetectionStrategy} from '../private_import_core';
import {ElementSchemaRegistry} from '../schema/element_schema_registry'; import {ElementSchemaRegistry} from '../schema/element_schema_registry';
@ -69,7 +69,7 @@ export function bindRenderInputs(
const {updateStmts, detachStmts} = triggerAnimation( const {updateStmts, detachStmts} = triggerAnimation(
o.THIS_EXPR, o.THIS_EXPR, boundProp, o.THIS_EXPR, o.THIS_EXPR, boundProp,
(hasEvents ? o.THIS_EXPR.prop(getHandleEventMethodName(compileElement.nodeIndex)) : (hasEvents ? o.THIS_EXPR.prop(getHandleEventMethodName(compileElement.nodeIndex)) :
o.importExpr(resolveIdentifier(Identifiers.noop))) o.importExpr(createIdentifier(Identifiers.noop)))
.callMethod(o.BuiltinMethod.Bind, [o.THIS_EXPR]), .callMethod(o.BuiltinMethod.Bind, [o.THIS_EXPR]),
compileElement.renderNode, evalResult.currValExpr, bindingField.expression); compileElement.renderNode, evalResult.currValExpr, bindingField.expression);
checkBindingStmts.push(...updateStmts); checkBindingStmts.push(...updateStmts);

View File

@ -7,10 +7,10 @@
*/ */
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, CompileTokenMetadata} from '../compile_metadata'; import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, CompileTokenMetadata, identifierName} from '../compile_metadata';
import {createDiTokenExpression} from '../compiler_util/identifier_util'; import {createDiTokenExpression} from '../compiler_util/identifier_util';
import {isPresent} from '../facade/lang'; import {isPresent} from '../facade/lang';
import {Identifiers, resolveIdentifier} from '../identifiers'; import {Identifiers, createIdentifier} from '../identifiers';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';
import {ViewType} from '../private_import_core'; import {ViewType} from '../private_import_core';
@ -74,7 +74,7 @@ export function injectFromViewParentInjector(
export function getViewClassName( export function getViewClassName(
component: CompileDirectiveSummary | CompileDirectiveMetadata, component: CompileDirectiveSummary | CompileDirectiveMetadata,
embeddedTemplateIndex: number): string { embeddedTemplateIndex: number): string {
return `View_${component.type.name}${embeddedTemplateIndex}`; return `View_${identifierName(component.type)}${embeddedTemplateIndex}`;
} }
export function getHandleEventMethodName(elementIndex: number): string { export function getHandleEventMethodName(elementIndex: number): string {

View File

@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {tokenReference} from '../compile_metadata';
import {ElementSchemaRegistry} from '../schema/element_schema_registry'; import {ElementSchemaRegistry} from '../schema/element_schema_registry';
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast'; import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast';
@ -66,7 +67,7 @@ class ViewBinderVisitor implements TemplateAstVisitor {
directiveAst, directiveWrapperInstance, compileElement); directiveAst, directiveWrapperInstance, compileElement);
}); });
ast.providers.forEach((providerAst) => { ast.providers.forEach((providerAst) => {
const providerInstance = compileElement.instances.get(providerAst.token.reference); const providerInstance = compileElement.instances.get(tokenReference(providerAst.token));
bindInjectableDestroyLifecycleCallbacks(providerAst, providerInstance, compileElement); bindInjectableDestroyLifecycleCallbacks(providerAst, providerInstance, compileElement);
}); });
return null; return null;
@ -89,7 +90,7 @@ class ViewBinderVisitor implements TemplateAstVisitor {
directiveAst, directiveWrapperInstance, compileElement); directiveAst, directiveWrapperInstance, compileElement);
}); });
ast.providers.forEach((providerAst) => { ast.providers.forEach((providerAst) => {
const providerInstance = compileElement.instances.get(providerAst.token.reference); const providerInstance = compileElement.instances.get(tokenReference(providerAst.token));
bindInjectableDestroyLifecycleCallbacks(providerAst, providerInstance, compileElement); bindInjectableDestroyLifecycleCallbacks(providerAst, providerInstance, compileElement);
}); });
bindView(compileElement.embeddedView, ast.children, this._schemaRegistry); bindView(compileElement.embeddedView, ast.children, this._schemaRegistry);

View File

@ -8,11 +8,11 @@
import {ViewEncapsulation} from '@angular/core'; import {ViewEncapsulation} from '@angular/core';
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, CompileTokenMetadata} from '../compile_metadata'; import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName} from '../compile_metadata';
import {createSharedBindingVariablesIfNeeded} from '../compiler_util/expression_converter'; import {createSharedBindingVariablesIfNeeded} from '../compiler_util/expression_converter';
import {createDiTokenExpression, createInlineArray} from '../compiler_util/identifier_util'; import {createDiTokenExpression, createInlineArray} from '../compiler_util/identifier_util';
import {isPresent} from '../facade/lang'; import {isPresent} from '../facade/lang';
import {Identifiers, identifierToken, resolveIdentifier} from '../identifiers'; import {Identifiers, createIdentifier, identifierToken} from '../identifiers';
import {createClassStmt} from '../output/class_builder'; import {createClassStmt} from '../output/class_builder';
import * as o from '../output/output_ast'; import * as o from '../output/output_ast';
import {ParseSourceSpan} from '../parse_util'; import {ParseSourceSpan} from '../parse_util';
@ -189,13 +189,13 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
_mergeHtmlAndDirectiveAttrs(htmlAttrs, directives).map(v => o.literal(v))); _mergeHtmlAndDirectiveAttrs(htmlAttrs, directives).map(v => o.literal(v)));
if (nodeIndex === 0 && this.view.viewType === ViewType.HOST) { if (nodeIndex === 0 && this.view.viewType === ViewType.HOST) {
createRenderNodeExpr = createRenderNodeExpr =
o.importExpr(resolveIdentifier(Identifiers.selectOrCreateRenderHostElement)).callFn([ o.importExpr(createIdentifier(Identifiers.selectOrCreateRenderHostElement)).callFn([
ViewProperties.renderer, o.literal(ast.name), attrNameAndValues, rootSelectorVar, ViewProperties.renderer, o.literal(ast.name), attrNameAndValues, rootSelectorVar,
debugContextExpr debugContextExpr
]); ]);
} else { } else {
createRenderNodeExpr = createRenderNodeExpr =
o.importExpr(resolveIdentifier(Identifiers.createRenderElement)).callFn([ o.importExpr(createIdentifier(Identifiers.createRenderElement)).callFn([
ViewProperties.renderer, this._getParentRenderNode(parent), o.literal(ast.name), ViewProperties.renderer, this._getParentRenderNode(parent), o.literal(ast.name),
attrNameAndValues, debugContextExpr attrNameAndValues, debugContextExpr
]); ]);
@ -210,19 +210,18 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
const compileElement = new CompileElement( const compileElement = new CompileElement(
parent, this.view, nodeIndex, renderNode, ast, component, directives, ast.providers, parent, this.view, nodeIndex, renderNode, ast, component, directives, ast.providers,
ast.hasViewContainer, false, ast.references, this.targetDependencies); ast.hasViewContainer, false, ast.references);
this.view.nodes.push(compileElement); this.view.nodes.push(compileElement);
let compViewExpr: o.ReadPropExpr = null; let compViewExpr: o.ReadPropExpr = null;
if (isPresent(component)) { if (isPresent(component)) {
const nestedComponentIdentifier = const nestedComponentIdentifier: CompileIdentifierMetadata = {reference: null};
new CompileIdentifierMetadata({name: getViewClassName(component, 0)}); this.targetDependencies.push(new ViewClassDependency(
this.targetDependencies.push( component.type, getViewClassName(component, 0), nestedComponentIdentifier));
new ViewClassDependency(component.type, nestedComponentIdentifier));
compViewExpr = o.THIS_EXPR.prop(`compView_${nodeIndex}`); // fix highlighting: ` compViewExpr = o.THIS_EXPR.prop(`compView_${nodeIndex}`); // fix highlighting: `
this.view.fields.push(new o.ClassField( this.view.fields.push(new o.ClassField(
compViewExpr.name, compViewExpr.name,
o.importType(resolveIdentifier(Identifiers.AppView), [o.importType(component.type)]))); o.importType(createIdentifier(Identifiers.AppView), [o.importType(component.type)])));
this.view.viewChildren.push(compViewExpr); this.view.viewChildren.push(compViewExpr);
compileElement.setComponentView(compViewExpr); compileElement.setComponentView(compViewExpr);
this.view.createMethod.addStmt( this.view.createMethod.addStmt(
@ -266,14 +265,14 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
const directives = ast.directives.map(directiveAst => directiveAst.directive); const directives = ast.directives.map(directiveAst => directiveAst.directive);
const compileElement = new CompileElement( const compileElement = new CompileElement(
parent, this.view, nodeIndex, renderNode, ast, null, directives, ast.providers, parent, this.view, nodeIndex, renderNode, ast, null, directives, ast.providers,
ast.hasViewContainer, true, ast.references, this.targetDependencies); ast.hasViewContainer, true, ast.references);
this.view.nodes.push(compileElement); this.view.nodes.push(compileElement);
this.nestedViewCount++; this.nestedViewCount++;
const embeddedView = new CompileView( const embeddedView = new CompileView(
this.view.component, this.view.genConfig, this.view.pipeMetas, o.NULL_EXPR, this.view.component, this.view.genConfig, this.view.pipeMetas, o.NULL_EXPR,
this.view.animations, this.view.viewIndex + this.nestedViewCount, compileElement, this.view.animations, this.view.viewIndex + this.nestedViewCount, compileElement,
templateVariableBindings); templateVariableBindings, this.targetDependencies);
this.nestedViewCount += buildView(embeddedView, ast.children, this.targetDependencies); this.nestedViewCount += buildView(embeddedView, ast.children, this.targetDependencies);
compileElement.beforeChildren(); compileElement.beforeChildren();
@ -372,31 +371,33 @@ function createViewTopLevelStmts(view: CompileView, targetStatements: o.Statemen
let nodeDebugInfosVar: o.Expression = o.NULL_EXPR; let nodeDebugInfosVar: o.Expression = o.NULL_EXPR;
if (view.genConfig.genDebugInfo) { if (view.genConfig.genDebugInfo) {
nodeDebugInfosVar = o.variable( nodeDebugInfosVar = o.variable(
`nodeDebugInfos_${view.component.type.name}${view.viewIndex}`); // fix highlighting: ` `nodeDebugInfos_${identifierName(view.component.type)}${view.viewIndex}`); // fix
// highlighting:
// `
targetStatements.push( targetStatements.push(
(<o.ReadVarExpr>nodeDebugInfosVar) (<o.ReadVarExpr>nodeDebugInfosVar)
.set(o.literalArr( .set(o.literalArr(
view.nodes.map(createStaticNodeDebugInfo), view.nodes.map(createStaticNodeDebugInfo),
new o.ArrayType( new o.ArrayType(
new o.ExternalType(resolveIdentifier(Identifiers.StaticNodeDebugInfo)), o.importType(createIdentifier(Identifiers.StaticNodeDebugInfo)),
[o.TypeModifier.Const]))) [o.TypeModifier.Const])))
.toDeclStmt(null, [o.StmtModifier.Final])); .toDeclStmt(null, [o.StmtModifier.Final]));
} }
const renderCompTypeVar: o.ReadVarExpr = const renderCompTypeVar: o.ReadVarExpr =
o.variable(`renderType_${view.component.type.name}`); // fix highlighting: ` o.variable(`renderType_${identifierName(view.component.type)}`); // fix highlighting: `
if (view.viewIndex === 0) { if (view.viewIndex === 0) {
let templateUrlInfo: string; let templateUrlInfo: string;
if (view.component.template.templateUrl == view.component.type.moduleUrl) { if (view.component.template.templateUrl == identifierModuleUrl(view.component.type)) {
templateUrlInfo = templateUrlInfo =
`${view.component.type.moduleUrl} class ${view.component.type.name} - inline template`; `${identifierModuleUrl(view.component.type)} class ${identifierName(view.component.type)} - inline template`;
} else { } else {
templateUrlInfo = view.component.template.templateUrl; templateUrlInfo = view.component.template.templateUrl;
} }
targetStatements.push( targetStatements.push(
renderCompTypeVar renderCompTypeVar
.set(o.importExpr(resolveIdentifier(Identifiers.createRenderComponentType)).callFn([ .set(o.importExpr(createIdentifier(Identifiers.createRenderComponentType)).callFn([
view.genConfig.genDebugInfo ? o.literal(templateUrlInfo) : o.literal(''), view.genConfig.genDebugInfo ? o.literal(templateUrlInfo) : o.literal(''),
o.literal(view.component.template.ngContentSelectors.length), o.literal(view.component.template.ngContentSelectors.length),
ViewEncapsulationEnum.fromValue(view.component.template.encapsulation), ViewEncapsulationEnum.fromValue(view.component.template.encapsulation),
@ -404,7 +405,7 @@ function createViewTopLevelStmts(view: CompileView, targetStatements: o.Statemen
o.literalMap(view.animations.map( o.literalMap(view.animations.map(
(entry): [string, o.Expression] => [entry.name, entry.fnExp])), (entry): [string, o.Expression] => [entry.name, entry.fnExp])),
])) ]))
.toDeclStmt(o.importType(resolveIdentifier(Identifiers.RenderComponentType)))); .toDeclStmt(o.importType(createIdentifier(Identifiers.RenderComponentType))));
} }
const viewClass = createViewClass(view, renderCompTypeVar, nodeDebugInfosVar); const viewClass = createViewClass(view, renderCompTypeVar, nodeDebugInfosVar);
@ -427,7 +428,7 @@ function createStaticNodeDebugInfo(node: CompileNode): o.Expression {
[varName, isPresent(token) ? createDiTokenExpression(token) : o.NULL_EXPR]); [varName, isPresent(token) ? createDiTokenExpression(token) : o.NULL_EXPR]);
}); });
} }
return o.importExpr(resolveIdentifier(Identifiers.StaticNodeDebugInfo)) return o.importExpr(createIdentifier(Identifiers.StaticNodeDebugInfo))
.instantiate( .instantiate(
[ [
o.literalArr(providerTokens, new o.ArrayType(o.DYNAMIC_TYPE, [o.TypeModifier.Const])), o.literalArr(providerTokens, new o.ArrayType(o.DYNAMIC_TYPE, [o.TypeModifier.Const])),
@ -435,7 +436,7 @@ function createStaticNodeDebugInfo(node: CompileNode): o.Expression {
o.literalMap(varTokenEntries, new o.MapType(o.DYNAMIC_TYPE, [o.TypeModifier.Const])) o.literalMap(varTokenEntries, new o.MapType(o.DYNAMIC_TYPE, [o.TypeModifier.Const]))
], ],
o.importType( o.importType(
resolveIdentifier(Identifiers.StaticNodeDebugInfo), null, [o.TypeModifier.Const])); createIdentifier(Identifiers.StaticNodeDebugInfo), null, [o.TypeModifier.Const]));
} }
function createViewClass( function createViewClass(
@ -443,10 +444,10 @@ function createViewClass(
nodeDebugInfosVar: o.Expression): o.ClassStmt { nodeDebugInfosVar: o.Expression): o.ClassStmt {
const viewConstructorArgs = [ const viewConstructorArgs = [
new o.FnParam( new o.FnParam(
ViewConstructorVars.viewUtils.name, o.importType(resolveIdentifier(Identifiers.ViewUtils))), ViewConstructorVars.viewUtils.name, o.importType(createIdentifier(Identifiers.ViewUtils))),
new o.FnParam( new o.FnParam(
ViewConstructorVars.parentView.name, ViewConstructorVars.parentView.name,
o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])), o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
new o.FnParam(ViewConstructorVars.parentIndex.name, o.NUMBER_TYPE), new o.FnParam(ViewConstructorVars.parentIndex.name, o.NUMBER_TYPE),
new o.FnParam(ViewConstructorVars.parentElement.name, o.DYNAMIC_TYPE) new o.FnParam(ViewConstructorVars.parentElement.name, o.DYNAMIC_TYPE)
]; ];
@ -461,14 +462,14 @@ function createViewClass(
} }
if (view.viewType === ViewType.EMBEDDED) { if (view.viewType === ViewType.EMBEDDED) {
viewConstructorArgs.push(new o.FnParam( viewConstructorArgs.push(new o.FnParam(
'declaredViewContainer', o.importType(resolveIdentifier(Identifiers.ViewContainer)))); 'declaredViewContainer', o.importType(createIdentifier(Identifiers.ViewContainer))));
superConstructorArgs.push(o.variable('declaredViewContainer')); superConstructorArgs.push(o.variable('declaredViewContainer'));
} }
const viewMethods = [ const viewMethods = [
new o.ClassMethod( new o.ClassMethod(
'createInternal', [new o.FnParam(rootSelectorVar.name, o.STRING_TYPE)], 'createInternal', [new o.FnParam(rootSelectorVar.name, o.STRING_TYPE)],
generateCreateMethod(view), generateCreateMethod(view),
o.importType(resolveIdentifier(Identifiers.ComponentRef), [o.DYNAMIC_TYPE])), o.importType(createIdentifier(Identifiers.ComponentRef), [o.DYNAMIC_TYPE])),
new o.ClassMethod( new o.ClassMethod(
'injectorGetInternal', 'injectorGetInternal',
[ [
@ -492,7 +493,7 @@ function createViewClass(
const viewClass = createClassStmt({ const viewClass = createClassStmt({
name: view.className, name: view.className,
parent: o.importExpr(resolveIdentifier(superClass), [getContextType(view)]), parent: o.importExpr(createIdentifier(superClass), [getContextType(view)]),
parentArgs: superConstructorArgs, parentArgs: superConstructorArgs,
ctorParams: viewConstructorArgs, ctorParams: viewConstructorArgs,
builders: [{methods: viewMethods}, view] builders: [{methods: viewMethods}, view]
@ -526,7 +527,7 @@ function generateCreateMethod(view: CompileView): o.Statement[] {
if (view.viewType === ViewType.HOST) { if (view.viewType === ViewType.HOST) {
const hostEl = <CompileElement>view.nodes[0]; const hostEl = <CompileElement>view.nodes[0];
resultExpr = resultExpr =
o.importExpr(resolveIdentifier(Identifiers.ComponentRef_), [o.DYNAMIC_TYPE]).instantiate([ o.importExpr(createIdentifier(Identifiers.ComponentRef_), [o.DYNAMIC_TYPE]).instantiate([
o.literal(hostEl.nodeIndex), o.THIS_EXPR, hostEl.renderNode, hostEl.getComponent() o.literal(hostEl.nodeIndex), o.THIS_EXPR, hostEl.renderNode, hostEl.getComponent()
]); ]);
} else { } else {
@ -591,7 +592,7 @@ function generateDetectChangesMethod(view: CompileView): o.Statement[] {
if (readVars.has(DetectChangesVars.changes.name)) { if (readVars.has(DetectChangesVars.changes.name)) {
varStmts.push( varStmts.push(
DetectChangesVars.changes.set(o.NULL_EXPR) DetectChangesVars.changes.set(o.NULL_EXPR)
.toDeclStmt(new o.MapType(o.importType(resolveIdentifier(Identifiers.SimpleChange))))); .toDeclStmt(new o.MapType(o.importType(createIdentifier(Identifiers.SimpleChange)))));
} }
varStmts.push(...createSharedBindingVariablesIfNeeded(stmts)); varStmts.push(...createSharedBindingVariablesIfNeeded(stmts));
return varStmts.concat(stmts); return varStmts.concat(stmts);
@ -703,5 +704,5 @@ function generateCreateEmbeddedViewsMethod(view: CompileView): o.ClassMethod {
} }
return new o.ClassMethod( return new o.ClassMethod(
'createEmbeddedViewInternal', [new o.FnParam(nodeIndexVar.name, o.NUMBER_TYPE)], stmts, 'createEmbeddedViewInternal', [new o.FnParam(nodeIndexVar.name, o.NUMBER_TYPE)], stmts,
o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])); o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE]));
} }

View File

@ -42,7 +42,7 @@ export class ViewCompiler {
Array<ViewClassDependency|ComponentFactoryDependency|DirectiveWrapperDependency> = []; Array<ViewClassDependency|ComponentFactoryDependency|DirectiveWrapperDependency> = [];
const view = new CompileView( const view = new CompileView(
component, this._genConfig, pipes, styles, compiledAnimations, 0, component, this._genConfig, pipes, styles, compiledAnimations, 0,
CompileElement.createNull(), []); CompileElement.createNull(), [], dependencies);
const statements: o.Statement[] = []; const statements: o.Statement[] = [];
buildView(view, template, dependencies); buildView(view, template, dependencies);

View File

@ -10,7 +10,7 @@ import {AnimationMetadata, animate, sequence, style, transition, trigger} from '
import {beforeEach, describe, expect, inject, it} from '@angular/core/testing/testing_internal'; import {beforeEach, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
import {AnimationCompiler, AnimationEntryCompileResult} from '../../src/animation/animation_compiler'; import {AnimationCompiler, AnimationEntryCompileResult} from '../../src/animation/animation_compiler';
import {AnimationParser} from '../../src/animation/animation_parser'; import {AnimationParser} from '../../src/animation/animation_parser';
import {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileTemplateMetadata, CompileTypeMetadata} from '../../src/compile_metadata'; import {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileTemplateMetadata, CompileTypeMetadata, identifierName} from '../../src/compile_metadata';
import {CompileMetadataResolver} from '../../src/metadata_resolver'; import {CompileMetadataResolver} from '../../src/metadata_resolver';
import {ElementSchemaRegistry} from '../../src/schema/element_schema_registry'; import {ElementSchemaRegistry} from '../../src/schema/element_schema_registry';
@ -30,7 +30,7 @@ export function main() {
const compileAnimations = const compileAnimations =
(component: CompileDirectiveMetadata): AnimationEntryCompileResult[] => { (component: CompileDirectiveMetadata): AnimationEntryCompileResult[] => {
const parsedAnimations = parser.parseComponent(component); const parsedAnimations = parser.parseComponent(component);
return compiler.compile(component.type.name, parsedAnimations); return compiler.compile(identifierName(component.type), parsedAnimations);
}; };
const compileTriggers = (input: any[]) => { const compileTriggers = (input: any[]) => {
@ -40,7 +40,7 @@ export function main() {
}); });
const component = CompileDirectiveMetadata.create({ const component = CompileDirectiveMetadata.create({
type: new CompileTypeMetadata({name: 'myCmp'}), type: {reference: {name: 'myCmp', filePath: ''}, diDeps: [], lifecycleHooks: []},
template: new CompileTemplateMetadata({animations: entries}) template: new CompileTemplateMetadata({animations: entries})
}); });

View File

@ -21,10 +21,14 @@ describe('StaticReflector', () => {
let host: StaticReflectorHost; let host: StaticReflectorHost;
let reflector: StaticReflector; let reflector: StaticReflector;
beforeEach(() => { function init(
host = new MockStaticReflectorHost(); testData: {[key: string]: any} = DEFAULT_TEST_DATA,
reflector = new StaticReflector(host); decorators: {name: string, filePath: string, ctor: any}[] = []) {
}); host = new MockStaticReflectorHost(testData);
reflector = new StaticReflector(host, undefined, decorators);
}
beforeEach(() => init());
function simplify(context: StaticSymbol, value: any) { function simplify(context: StaticSymbol, value: any) {
return reflector.simplify(context, value); return reflector.simplify(context, value);
@ -446,6 +450,17 @@ describe('StaticReflector', () => {
]); ]);
}); });
it('should be able to get metadata for a class with nested method calls', () => {
const annotations = reflector.annotations(
reflector.getStaticSymbol('/tmp/src/static-method-call.ts', 'MyFactoryComponent'));
expect(annotations.length).toBe(1);
expect(annotations[0].providers).toEqual({
provide: 'c',
useFactory:
reflector.getStaticSymbol('/tmp/src/static-method.ts', 'AnotherModule', ['someFactory'])
});
});
it('should be able to get the metadata for a class calling a method with default parameters', it('should be able to get the metadata for a class calling a method with default parameters',
() => { () => {
const annotations = reflector.annotations( const annotations = reflector.annotations(
@ -517,11 +532,173 @@ describe('StaticReflector', () => {
expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts'); expect(symbol.filePath).toEqual('/tmp/src/reexport/src/origin1.d.ts');
}); });
describe('inheritance', () => {
class ClassDecorator {
constructor(public value: any) {}
}
class ParamDecorator {
constructor(public value: any) {}
}
class PropDecorator {
constructor(public value: any) {}
}
function initWithDecorator(testData: {[key: string]: any}) {
testData['/tmp/src/decorator.ts'] = `
export function ClassDecorator(): any {}
export function ParamDecorator(): any {}
export function PropDecorator(): any {}
`;
init(testData, [
{filePath: '/tmp/src/decorator.ts', name: 'ClassDecorator', ctor: ClassDecorator},
{filePath: '/tmp/src/decorator.ts', name: 'ParamDecorator', ctor: ParamDecorator},
{filePath: '/tmp/src/decorator.ts', name: 'PropDecorator', ctor: PropDecorator}
]);
}
it('should inherit annotations', () => {
initWithDecorator({
'/tmp/src/main.ts': `
import {ClassDecorator} from './decorator';
@ClassDecorator('parent')
export class Parent {}
@ClassDecorator('child')
export class Child extends Parent {}
export class ChildNoDecorators extends Parent {}
`
});
// Check that metadata for Parent was not changed!
expect(reflector.annotations(reflector.getStaticSymbol('/tmp/src/main.ts', 'Parent')))
.toEqual([new ClassDecorator('parent')]);
expect(reflector.annotations(reflector.getStaticSymbol('/tmp/src/main.ts', 'Child')))
.toEqual([new ClassDecorator('parent'), new ClassDecorator('child')]);
expect(
reflector.annotations(reflector.getStaticSymbol('/tmp/src/main.ts', 'ChildNoDecorators')))
.toEqual([new ClassDecorator('parent')]);
});
it('should inherit parameters', () => {
initWithDecorator({
'/tmp/src/main.ts': `
import {ParamDecorator} from './decorator';
export class A {}
export class B {}
export class C {}
export class Parent {
constructor(@ParamDecorator('a') a: A, @ParamDecorator('b') b: B) {}
}
export class Child extends Parent {}
export class ChildWithCtor extends Parent {
constructor(@ParamDecorator('c') c: C) {}
}
`
});
// Check that metadata for Parent was not changed!
expect(reflector.parameters(reflector.getStaticSymbol('/tmp/src/main.ts', 'Parent')))
.toEqual([
[reflector.getStaticSymbol('/tmp/src/main.ts', 'A'), new ParamDecorator('a')],
[reflector.getStaticSymbol('/tmp/src/main.ts', 'B'), new ParamDecorator('b')]
]);
expect(reflector.parameters(reflector.getStaticSymbol('/tmp/src/main.ts', 'Child'))).toEqual([
[reflector.getStaticSymbol('/tmp/src/main.ts', 'A'), new ParamDecorator('a')],
[reflector.getStaticSymbol('/tmp/src/main.ts', 'B'), new ParamDecorator('b')]
]);
expect(reflector.parameters(reflector.getStaticSymbol('/tmp/src/main.ts', 'ChildWithCtor')))
.toEqual([[reflector.getStaticSymbol('/tmp/src/main.ts', 'C'), new ParamDecorator('c')]]);
});
it('should inherit property metadata', () => {
initWithDecorator({
'/tmp/src/main.ts': `
import {PropDecorator} from './decorator';
export class A {}
export class B {}
export class C {}
export class Parent {
@PropDecorator('a')
a: A;
@PropDecorator('b1')
b: B;
}
export class Child extends Parent {
@PropDecorator('b2')
b: B;
@PropDecorator('c')
c: C;
}
`
});
// Check that metadata for Parent was not changed!
expect(reflector.propMetadata(reflector.getStaticSymbol('/tmp/src/main.ts', 'Parent')))
.toEqual({
'a': [new PropDecorator('a')],
'b': [new PropDecorator('b1')],
});
expect(reflector.propMetadata(reflector.getStaticSymbol('/tmp/src/main.ts', 'Child')))
.toEqual({
'a': [new PropDecorator('a')],
'b': [new PropDecorator('b1'), new PropDecorator('b2')],
'c': [new PropDecorator('c')]
});
});
it('should inherit lifecycle hooks', () => {
initWithDecorator({
'/tmp/src/main.ts': `
export class Parent {
hook1() {}
hook2() {}
}
export class Child extends Parent {
hook2() {}
hook3() {}
}
`
});
function hooks(symbol: StaticSymbol, names: string[]): boolean[] {
return names.map(name => reflector.hasLifecycleHook(symbol, name));
}
// Check that metadata for Parent was not changed!
expect(hooks(reflector.getStaticSymbol('/tmp/src/main.ts', 'Parent'), [
'hook1', 'hook2', 'hook3'
])).toEqual([true, true, false]);
expect(hooks(reflector.getStaticSymbol('/tmp/src/main.ts', 'Child'), [
'hook1', 'hook2', 'hook3'
])).toEqual([true, true, true]);
});
});
}); });
class MockStaticReflectorHost implements StaticReflectorHost { class MockStaticReflectorHost implements StaticReflectorHost {
private collector = new MetadataCollector(); private collector = new MetadataCollector();
constructor(private data: {[key: string]: any}) {}
// In tests, assume that symbols are not re-exported // In tests, assume that symbols are not re-exported
moduleNameToFileName(modulePath: string, containingFile?: string): string { moduleNameToFileName(modulePath: string, containingFile?: string): string {
function splitPath(path: string): string[] { return path.split(/\/|\\/g); } function splitPath(path: string): string[] { return path.split(/\/|\\/g); }
@ -568,7 +745,28 @@ class MockStaticReflectorHost implements StaticReflectorHost {
getMetadataFor(moduleId: string): any { return this._getMetadataFor(moduleId); } getMetadataFor(moduleId: string): any { return this._getMetadataFor(moduleId); }
private _getMetadataFor(moduleId: string): any { private _getMetadataFor(moduleId: string): any {
const data: {[key: string]: any} = { if (this.data[moduleId] && moduleId.match(TS_EXT)) {
const text = this.data[moduleId];
if (typeof text === 'string') {
const sf = ts.createSourceFile(
moduleId, this.data[moduleId], ts.ScriptTarget.ES5, /* setParentNodes */ true);
const diagnostics: ts.Diagnostic[] = (<any>sf).parseDiagnostics;
if (diagnostics && diagnostics.length) {
throw Error(`Error encountered during parse of file ${moduleId}`);
}
return [this.collector.getMetadata(sf)];
}
}
const result = this.data[moduleId];
if (result) {
return Array.isArray(result) ? result : [result];
} else {
return null;
}
}
}
const DEFAULT_TEST_DATA: {[key: string]: any} = {
'/tmp/@angular/common/src/forms-deprecated/directives.d.ts': [{ '/tmp/@angular/common/src/forms-deprecated/directives.d.ts': [{
'__symbolic': 'module', '__symbolic': 'module',
'version': 2, 'version': 2,
@ -1044,6 +1242,15 @@ class MockStaticReflectorHost implements StaticReflectorHost {
static defaultsMethod(a, b = true, c = false) { static defaultsMethod(a, b = true, c = false) {
return [a, b, c]; return [a, b, c];
} }
static withFactory() {
return { provide: 'c', useFactory: AnotherModule.someFactory };
}
}
export class AnotherModule {
static someFactory() {
return 'e';
}
} }
`, `,
'/tmp/src/static-method-call.ts': ` '/tmp/src/static-method-call.ts': `
@ -1064,6 +1271,11 @@ class MockStaticReflectorHost implements StaticReflectorHost {
providers: [MyModule.defaultsMethod('a')] providers: [MyModule.defaultsMethod('a')]
}) })
export class MyDefaultsComponent { } export class MyDefaultsComponent { }
@Component({
providers: MyModule.withFactory()
})
export class MyFactoryComponent { }
`, `,
'/tmp/src/static-field.ts': ` '/tmp/src/static-field.ts': `
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
@ -1162,25 +1374,3 @@ class MockStaticReflectorHost implements StaticReflectorHost {
exports: [{from: './originNone'}, {from: './origin30'}] exports: [{from: './originNone'}, {from: './origin30'}]
} }
}; };
if (data[moduleId] && moduleId.match(TS_EXT)) {
const text = data[moduleId];
if (typeof text === 'string') {
const sf = ts.createSourceFile(
moduleId, data[moduleId], ts.ScriptTarget.ES5, /* setParentNodes */ true);
const diagnostics: ts.Diagnostic[] = (<any>sf).parseDiagnostics;
if (diagnostics && diagnostics.length) {
throw Error(`Error encountered during parse of file ${moduleId}`);
}
return [this.collector.getMetadata(sf)];
}
}
const result = data[moduleId];
if (result) {
return Array.isArray(result) ? result : [result];
} else {
return null;
}
}
}

View File

@ -7,7 +7,7 @@
*/ */
import {createInlineArray} from '../../src/compiler_util/identifier_util'; import {createInlineArray} from '../../src/compiler_util/identifier_util';
import {Identifiers, resolveIdentifier} from '../../src/identifiers'; import {Identifiers, createIdentifier} from '../../src/identifiers';
import * as o from '../../src/output/output_ast'; import * as o from '../../src/output/output_ast';
export function main() { export function main() {
@ -16,7 +16,7 @@ export function main() {
function check(argCount: number, expectedIdentifier: any) { function check(argCount: number, expectedIdentifier: any) {
const args = createArgs(argCount); const args = createArgs(argCount);
expect(createInlineArray(args)) expect(createInlineArray(args))
.toEqual(o.importExpr(resolveIdentifier(expectedIdentifier)).instantiate([ .toEqual(o.importExpr(createIdentifier(expectedIdentifier)).instantiate([
<o.Expression>o.literal(argCount) <o.Expression>o.literal(argCount)
].concat(args))); ].concat(args)));
} }
@ -31,7 +31,7 @@ export function main() {
it('should work for arrays of length 0', () => { it('should work for arrays of length 0', () => {
expect(createInlineArray([ expect(createInlineArray([
])).toEqual(o.importExpr(resolveIdentifier(Identifiers.EMPTY_INLINE_ARRAY))); ])).toEqual(o.importExpr(createIdentifier(Identifiers.EMPTY_INLINE_ARRAY)));
}); });
it('should work for arrays of length 1 - 2', () => { it('should work for arrays of length 1 - 2', () => {

View File

@ -8,15 +8,12 @@
import {DirectiveResolver} from '@angular/compiler/src/directive_resolver'; import {DirectiveResolver} from '@angular/compiler/src/directive_resolver';
import {Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Input, Output, ViewChild, ViewChildren} from '@angular/core/src/metadata'; import {Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Input, Output, ViewChild, ViewChildren} from '@angular/core/src/metadata';
import {reflector} from '@angular/core/src/reflection/reflection';
@Directive({selector: 'someDirective'}) @Directive({selector: 'someDirective'})
class SomeDirective { class SomeDirective {
} }
@Directive({selector: 'someChildDirective'})
class SomeChildDirective extends SomeDirective {
}
@Directive({selector: 'someDirective', inputs: ['c']}) @Directive({selector: 'someDirective', inputs: ['c']})
class SomeDirectiveWithInputs { class SomeDirectiveWithInputs {
@Input() a: any; @Input() a: any;
@ -31,28 +28,6 @@ class SomeDirectiveWithOutputs {
c: any; c: any;
} }
@Directive({selector: 'someDirective', outputs: ['a']})
class SomeDirectiveWithDuplicateOutputs {
@Output() a: any;
}
@Directive({selector: 'someDirective', outputs: ['localA: a']})
class SomeDirectiveWithDuplicateRenamedOutputs {
@Output() a: any;
localA: any;
}
@Directive({selector: 'someDirective', inputs: ['a']})
class SomeDirectiveWithDuplicateInputs {
@Input() a: any;
}
@Directive({selector: 'someDirective', inputs: ['localA: a']})
class SomeDirectiveWithDuplicateRenamedInputs {
@Input() a: any;
localA: any;
}
@Directive({selector: 'someDirective'}) @Directive({selector: 'someDirective'})
class SomeDirectiveWithSetterProps { class SomeDirectiveWithSetterProps {
@Input('renamed') @Input('renamed')
@ -150,11 +125,22 @@ export function main() {
}).toThrowError('No Directive annotation found on SomeDirectiveWithoutMetadata'); }).toThrowError('No Directive annotation found on SomeDirectiveWithoutMetadata');
}); });
it('should not read parent class Directive metadata', function() { it('should support inheriting the Directive metadata', function() {
const directiveMetadata = resolver.resolve(SomeChildDirective); @Directive({selector: 'p'})
expect(directiveMetadata) class Parent {
.toEqual(new Directive( }
{selector: 'someChildDirective', inputs: [], outputs: [], host: {}, queries: {}}));
class ChildNoDecorator extends Parent {}
@Directive({selector: 'c'})
class ChildWithDecorator extends Parent {
}
expect(resolver.resolve(ChildNoDecorator))
.toEqual(new Directive({selector: 'p', inputs: [], outputs: [], host: {}, queries: {}}));
expect(resolver.resolve(ChildWithDecorator))
.toEqual(new Directive({selector: 'c', inputs: [], outputs: [], host: {}, queries: {}}));
}); });
describe('inputs', () => { describe('inputs', () => {
@ -168,16 +154,52 @@ export function main() {
expect(directiveMetadata.inputs).toEqual(['a: renamed']); expect(directiveMetadata.inputs).toEqual(['a: renamed']);
}); });
it('should throw if duplicate inputs', () => { it('should remove duplicate inputs', () => {
expect(() => { @Directive({selector: 'someDirective', inputs: ['a', 'a']})
resolver.resolve(SomeDirectiveWithDuplicateInputs); class SomeDirectiveWithDuplicateInputs {
}).toThrowError(`Input 'a' defined multiple times in 'SomeDirectiveWithDuplicateInputs'`); }
const directiveMetadata = resolver.resolve(SomeDirectiveWithDuplicateInputs);
expect(directiveMetadata.inputs).toEqual(['a']);
}); });
it('should throw if duplicate inputs (with rename)', () => { it('should use the last input if duplicate inputs (with rename)', () => {
expect(() => { resolver.resolve(SomeDirectiveWithDuplicateRenamedInputs); }) @Directive({selector: 'someDirective', inputs: ['a', 'localA: a']})
.toThrowError( class SomeDirectiveWithDuplicateInputs {
`Input 'a' defined multiple times in 'SomeDirectiveWithDuplicateRenamedInputs'`); }
const directiveMetadata = resolver.resolve(SomeDirectiveWithDuplicateInputs);
expect(directiveMetadata.inputs).toEqual(['localA: a']);
});
it('should prefer @Input over @Directive.inputs', () => {
@Directive({selector: 'someDirective', inputs: ['a']})
class SomeDirectiveWithDuplicateInputs {
@Input('a')
propA: any;
}
const directiveMetadata = resolver.resolve(SomeDirectiveWithDuplicateInputs);
expect(directiveMetadata.inputs).toEqual(['propA: a']);
});
it('should support inheriting inputs', () => {
@Directive({selector: 'p'})
class Parent {
@Input()
p1: any;
@Input('p21')
p2: any;
}
class Child extends Parent {
@Input('p22')
p2: any;
@Input()
p3: any;
}
const directiveMetadata = resolver.resolve(Child);
expect(directiveMetadata.inputs).toEqual(['p1', 'p2: p22', 'p3']);
}); });
}); });
@ -192,16 +214,52 @@ export function main() {
expect(directiveMetadata.outputs).toEqual(['a: renamed']); expect(directiveMetadata.outputs).toEqual(['a: renamed']);
}); });
it('should throw if duplicate outputs', () => { it('should remove duplicate outputs', () => {
expect(() => { resolver.resolve(SomeDirectiveWithDuplicateOutputs); }) @Directive({selector: 'someDirective', outputs: ['a', 'a']})
.toThrowError( class SomeDirectiveWithDuplicateOutputs {
`Output event 'a' defined multiple times in 'SomeDirectiveWithDuplicateOutputs'`); }
const directiveMetadata = resolver.resolve(SomeDirectiveWithDuplicateOutputs);
expect(directiveMetadata.outputs).toEqual(['a']);
}); });
it('should throw if duplicate outputs (with rename)', () => { it('should use the last output if duplicate outputs (with rename)', () => {
expect(() => { resolver.resolve(SomeDirectiveWithDuplicateRenamedOutputs); }) @Directive({selector: 'someDirective', outputs: ['a', 'localA: a']})
.toThrowError( class SomeDirectiveWithDuplicateOutputs {
`Output event 'a' defined multiple times in 'SomeDirectiveWithDuplicateRenamedOutputs'`); }
const directiveMetadata = resolver.resolve(SomeDirectiveWithDuplicateOutputs);
expect(directiveMetadata.outputs).toEqual(['localA: a']);
});
it('should prefer @Output over @Directive.outputs', () => {
@Directive({selector: 'someDirective', outputs: ['a']})
class SomeDirectiveWithDuplicateOutputs {
@Output('a')
propA: any;
}
const directiveMetadata = resolver.resolve(SomeDirectiveWithDuplicateOutputs);
expect(directiveMetadata.outputs).toEqual(['propA: a']);
});
it('should support inheriting outputs', () => {
@Directive({selector: 'p'})
class Parent {
@Output()
p1: any;
@Output('p21')
p2: any;
}
class Child extends Parent {
@Output('p22')
p2: any;
@Output()
p3: any;
}
const directiveMetadata = resolver.resolve(Child);
expect(directiveMetadata.outputs).toEqual(['p1', 'p2: p22', 'p3']);
}); });
}); });
@ -233,6 +291,46 @@ export function main() {
.toThrowError( .toThrowError(
`@HostBinding parameter should be a property name, 'class.<name>', or 'attr.<name>'.`); `@HostBinding parameter should be a property name, 'class.<name>', or 'attr.<name>'.`);
}); });
it('should support inheriting host bindings', () => {
@Directive({selector: 'p'})
class Parent {
@HostBinding()
p1: any;
@HostBinding('p21')
p2: any;
}
class Child extends Parent {
@HostBinding('p22')
p2: any;
@HostBinding()
p3: any;
}
const directiveMetadata = resolver.resolve(Child);
expect(directiveMetadata.host).toEqual({'[p1]': 'p1', '[p22]': 'p2', '[p3]': 'p3'});
});
it('should support inheriting host listeners', () => {
@Directive({selector: 'p'})
class Parent {
@HostListener('p1')
p1() {}
@HostListener('p21')
p2() {}
}
class Child extends Parent {
@HostListener('p22')
p2() {}
@HostListener('p3')
p3() {}
}
const directiveMetadata = resolver.resolve(Child);
expect(directiveMetadata.host).toEqual({'(p1)': 'p1()', '(p22)': 'p2()', '(p3)': 'p3()'});
});
}); });
describe('queries', () => { describe('queries', () => {
@ -259,6 +357,30 @@ export function main() {
expect(directiveMetadata.queries) expect(directiveMetadata.queries)
.toEqual({'c': new ViewChild('c'), 'a': new ViewChild('a')}); .toEqual({'c': new ViewChild('c'), 'a': new ViewChild('a')});
}); });
it('should support inheriting queries', () => {
@Directive({selector: 'p'})
class Parent {
@ContentChild('p1')
p1: any;
@ContentChild('p21')
p2: any;
}
class Child extends Parent {
@ContentChild('p22')
p2: any;
@ContentChild('p3')
p3: any;
}
const directiveMetadata = resolver.resolve(Child);
expect(directiveMetadata.queries).toEqual({
'p1': new ContentChild('p1'),
'p2': new ContentChild('p22'),
'p3': new ContentChild('p3')
});
});
}); });
describe('Component', () => { describe('Component', () => {

View File

@ -6,13 +6,12 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {describe, expect, it} from '@angular/core/testing/testing_internal'; import {computeMsgId, sha1} from '../../src/i18n/digest';
import {sha1} from '../../src/i18n/digest';
export function main(): void { export function main(): void {
describe('digest', () => {
describe('sha1', () => { describe('sha1', () => {
it('should work on emnpty strings', it('should work on empty strings',
() => { expect(sha1('')).toEqual('da39a3ee5e6b4b0d3255bfef95601890afd80709'); }); () => { expect(sha1('')).toEqual('da39a3ee5e6b4b0d3255bfef95601890afd80709'); });
it('should returns the sha1 of "hello world"', it('should returns the sha1 of "hello world"',
@ -55,4 +54,53 @@ export function main(): void {
expect(sha1(result)).toEqual('24c2dae5c1ac6f604dbe670a60290d7ce6320b45'); expect(sha1(result)).toEqual('24c2dae5c1ac6f604dbe670a60290d7ce6320b45');
}); });
}); });
describe('decimal fingerprint', () => {
it('should work on well known inputs w/o meaning', () => {
const fixtures: {[msg: string]: string} = {
' Spaced Out ': '3976450302996657536',
'Last Name': '4407559560004943843',
'First Name': '6028371114637047813',
'View': '2509141182388535183',
'START_BOLDNUMEND_BOLD of START_BOLDmillionsEND_BOLD': '29997634073898638',
'The customer\'s credit card was authorized for AMOUNT and passed all risk checks.':
'6836487644149622036',
'Hello world!': '3022994926184248873',
'Jalape\u00f1o': '8054366208386598941',
'The set of SET_NAME is {XXX, ...}.': '135956960462609535',
'NAME took a trip to DESTINATION.': '768490705511913603',
'by AUTHOR (YEAR)': '7036633296476174078',
'': '4416290763660062288',
};
Object.keys(fixtures).forEach(
msg => { expect(computeMsgId(msg, '')).toEqual(fixtures[msg]); });
});
it('should work on well known inputs with meaning', () => {
const fixtures: {[msg: string]: [string, string]} = {
'7790835225175622807': ['Last Name', 'Gmail UI'],
'1809086297585054940': ['First Name', 'Gmail UI'],
'3993998469942805487': ['View', 'Gmail UI'],
};
Object.keys(fixtures).forEach(
id => { expect(computeMsgId(fixtures[id][0], fixtures[id][1])).toEqual(id); });
});
it('should support arbitrary string size', () => {
const prefix = `你好,世界`;
let result = computeMsgId(prefix, '');
for (let size = prefix.length; size < 5000; size += 101) {
result = prefix + computeMsgId(result, '');
while (result.length < size) {
result += result;
}
result = result.slice(-size);
}
expect(computeMsgId(result, '')).toEqual('2122606631351252558');
});
});
});
} }

View File

@ -6,15 +6,13 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {describe, expect, it} from '@angular/core/testing/testing_internal'; import {DEFAULT_INTERPOLATION_CONFIG, HtmlParser} from '@angular/compiler';
import {digestMessage, serializeNodes as serializeI18nNodes} from '../../src/i18n/digest'; import {digest, serializeNodes as serializeI18nNodes} from '../../src/i18n/digest';
import {extractMessages, mergeTranslations} from '../../src/i18n/extractor_merger'; import {extractMessages, mergeTranslations} from '../../src/i18n/extractor_merger';
import * as i18n from '../../src/i18n/i18n_ast'; import * as i18n from '../../src/i18n/i18n_ast';
import {TranslationBundle} from '../../src/i18n/translation_bundle'; import {TranslationBundle} from '../../src/i18n/translation_bundle';
import * as html from '../../src/ml_parser/ast'; import * as html from '../../src/ml_parser/ast';
import {HtmlParser} from '../../src/ml_parser/html_parser';
import {DEFAULT_INTERPOLATION_CONFIG} from '../../src/ml_parser/interpolation_config';
import {serializeNodes as serializeHtmlNodes} from '../ml_parser/ast_serializer_spec'; import {serializeNodes as serializeHtmlNodes} from '../ml_parser/ast_serializer_spec';
export function main() { export function main() {
@ -94,9 +92,10 @@ export function main() {
], ],
[ [
[ [
'text', 'text', '<ph tag name="START_PARAGRAPH">html, <ph tag' +
'<ph tag name="START_PARAGRAPH">html, <ph tag name="START_BOLD_TEXT">nested</ph name="CLOSE_BOLD_TEXT"></ph name="CLOSE_PARAGRAPH">', ' name="START_BOLD_TEXT">nested</ph name="CLOSE_BOLD_TEXT"></ph name="CLOSE_PARAGRAPH">',
'<ph icu name="ICU">{count, plural, =0 {[<ph tag name="START_TAG_SPAN">html</ph name="CLOSE_TAG_SPAN">]}}</ph>', '<ph icu name="ICU">{count, plural, =0 {[<ph tag' +
' name="START_TAG_SPAN">html</ph name="CLOSE_TAG_SPAN">]}}</ph>',
'[<ph name="INTERPOLATION">interp</ph>]' '[<ph name="INTERPOLATION">interp</ph>]'
], ],
'', '' '', ''
@ -190,9 +189,8 @@ export function main() {
it('should extract from attributes in translatable elements', () => { it('should extract from attributes in translatable elements', () => {
expect(extract('<div i18n><p><b i18n-title="m|d" title="msg"></b></p></div>')).toEqual([ expect(extract('<div i18n><p><b i18n-title="m|d" title="msg"></b></p></div>')).toEqual([
[ [
[ ['<ph tag name="START_PARAGRAPH"><ph tag name="START_BOLD_TEXT"></ph' +
'<ph tag name="START_PARAGRAPH"><ph tag name="START_BOLD_TEXT"></ph name="CLOSE_BOLD_TEXT"></ph name="CLOSE_PARAGRAPH">' ' name="CLOSE_BOLD_TEXT"></ph name="CLOSE_PARAGRAPH">'],
],
'', '' '', ''
], ],
[['msg'], 'm', 'd'], [['msg'], 'm', 'd'],
@ -204,9 +202,8 @@ export function main() {
.toEqual([ .toEqual([
[['msg'], 'm', 'd'], [['msg'], 'm', 'd'],
[ [
[ ['<ph tag name="START_PARAGRAPH"><ph tag name="START_BOLD_TEXT"></ph' +
'<ph tag name="START_PARAGRAPH"><ph tag name="START_BOLD_TEXT"></ph name="CLOSE_BOLD_TEXT"></ph name="CLOSE_PARAGRAPH">' ' name="CLOSE_BOLD_TEXT"></ph name="CLOSE_PARAGRAPH">'],
],
'', '' '', ''
], ],
]); ]);
@ -220,7 +217,8 @@ export function main() {
[['msg'], 'm', 'd'], [['msg'], 'm', 'd'],
[ [
[ [
'{count, plural, =0 {[<ph tag name="START_PARAGRAPH"><ph tag name="START_BOLD_TEXT"></ph name="CLOSE_BOLD_TEXT"></ph name="CLOSE_PARAGRAPH">]}}' '{count, plural, =0 {[<ph tag name="START_PARAGRAPH"><ph tag' +
' name="START_BOLD_TEXT"></ph name="CLOSE_BOLD_TEXT"></ph name="CLOSE_PARAGRAPH">]}}'
], ],
'', '' '', ''
], ],
@ -351,7 +349,9 @@ export function main() {
const HTML = `before<!-- i18n --><p>foo</p><span><i>bar</i></span><!-- /i18n -->after`; const HTML = `before<!-- i18n --><p>foo</p><span><i>bar</i></span><!-- /i18n -->after`;
expect(fakeTranslate(HTML)) expect(fakeTranslate(HTML))
.toEqual( .toEqual(
'before**<ph tag name="START_PARAGRAPH">foo</ph name="CLOSE_PARAGRAPH"><ph tag name="START_TAG_SPAN"><ph tag name="START_ITALIC_TEXT">bar</ph name="CLOSE_ITALIC_TEXT"></ph name="CLOSE_TAG_SPAN">**after'); 'before**[ph tag name="START_PARAGRAPH">foo[/ph name="CLOSE_PARAGRAPH">[ph tag' +
' name="START_TAG_SPAN">[ph tag name="START_ITALIC_TEXT">bar[/ph' +
' name="CLOSE_ITALIC_TEXT">[/ph name="CLOSE_TAG_SPAN">**after');
}); });
it('should merge nested blocks', () => { it('should merge nested blocks', () => {
@ -359,7 +359,9 @@ export function main() {
`<div>before<!-- i18n --><p>foo</p><span><i>bar</i></span><!-- /i18n -->after</div>`; `<div>before<!-- i18n --><p>foo</p><span><i>bar</i></span><!-- /i18n -->after</div>`;
expect(fakeTranslate(HTML)) expect(fakeTranslate(HTML))
.toEqual( .toEqual(
'<div>before**<ph tag name="START_PARAGRAPH">foo</ph name="CLOSE_PARAGRAPH"><ph tag name="START_TAG_SPAN"><ph tag name="START_ITALIC_TEXT">bar</ph name="CLOSE_ITALIC_TEXT"></ph name="CLOSE_TAG_SPAN">**after</div>'); '<div>before**[ph tag name="START_PARAGRAPH">foo[/ph name="CLOSE_PARAGRAPH">[ph' +
' tag name="START_TAG_SPAN">[ph tag name="START_ITALIC_TEXT">bar[/ph' +
' name="CLOSE_ITALIC_TEXT">[/ph name="CLOSE_TAG_SPAN">**after</div>');
}); });
}); });
@ -400,15 +402,15 @@ function fakeTranslate(
extractMessages(htmlNodes, DEFAULT_INTERPOLATION_CONFIG, implicitTags, implicitAttrs) extractMessages(htmlNodes, DEFAULT_INTERPOLATION_CONFIG, implicitTags, implicitAttrs)
.messages; .messages;
const i18nMsgMap: {[id: string]: html.Node[]} = {}; const i18nMsgMap: {[id: string]: i18n.Node[]} = {};
messages.forEach(message => { messages.forEach(message => {
const id = digestMessage(message); const id = digest(message);
const text = serializeI18nNodes(message.nodes).join(''); const text = serializeI18nNodes(message.nodes).join('').replace(/</g, '[');
i18nMsgMap[id] = [new html.Text(`**${text}**`, null)]; i18nMsgMap[id] = [new i18n.Text(`**${text}**`, null)];
}); });
const translations = new TranslationBundle(i18nMsgMap); const translations = new TranslationBundle(i18nMsgMap, digest);
const translatedNodes = const translatedNodes =
mergeTranslations( mergeTranslations(

View File

@ -6,9 +6,9 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {digest} from '@angular/compiler/src/i18n/digest';
import {extractMessages} from '@angular/compiler/src/i18n/extractor_merger'; import {extractMessages} from '@angular/compiler/src/i18n/extractor_merger';
import {Message} from '@angular/compiler/src/i18n/i18n_ast'; import {Message} from '@angular/compiler/src/i18n/i18n_ast';
import {describe, expect, it} from '@angular/core/testing/testing_internal';
import {serializeNodes} from '../../src/i18n/digest'; import {serializeNodes} from '../../src/i18n/digest';
import {HtmlParser} from '../../src/ml_parser/html_parser'; import {HtmlParser} from '../../src/ml_parser/html_parser';
@ -272,11 +272,14 @@ export function main() {
[['{count, plural, =1 {[1]}}'], '', ''], [['{count, plural, =1 {[1]}}'], '', ''],
]); ]);
// ICU message placeholders are reference to translations. expect(_humanizePlaceholders(html)).toEqual([
// As such they have no static content but refs to message ids. '',
expect(_humanizePlaceholders(html)).toEqual(['', '', '', '']); 'VAR_PLURAL=count',
'VAR_PLURAL=count',
'VAR_PLURAL=count',
]);
expect(_humanizePlaceholdersToIds(html)).toEqual([ expect(_humanizePlaceholdersToMessage(html)).toEqual([
'ICU=f0f76923009914f1b05f41042a5c7231b9496504, ICU_1=73693d1f78d0fc882f0bcbce4cb31a0aa1995cfe', 'ICU=f0f76923009914f1b05f41042a5c7231b9496504, ICU_1=73693d1f78d0fc882f0bcbce4cb31a0aa1995cfe',
'', '',
'', '',
@ -308,13 +311,13 @@ function _humanizePlaceholders(
// clang-format on // clang-format on
} }
function _humanizePlaceholdersToIds( function _humanizePlaceholdersToMessage(
html: string, implicitTags: string[] = [], html: string, implicitTags: string[] = [],
implicitAttrs: {[k: string]: string[]} = {}): string[] { implicitAttrs: {[k: string]: string[]} = {}): string[] {
// clang-format off // clang-format off
// https://github.com/angular/clang-format/issues/35 // https://github.com/angular/clang-format/issues/35
return _extractMessages(html, implicitTags, implicitAttrs).map( return _extractMessages(html, implicitTags, implicitAttrs).map(
msg => Object.keys(msg.placeholderToMsgIds).map(k => `${k}=${msg.placeholderToMsgIds[k]}`).join(', ')); msg => Object.keys(msg.placeholderToMessage).map(k => `${k}=${digest(msg.placeholderToMessage[k])}`).join(', '));
// clang-format on // clang-format on
} }

View File

@ -43,6 +43,9 @@ export function main() {
expectHtml(el, '#i18n-2').toBe('<div id="i18n-2"><p>imbriqué</p></div>'); expectHtml(el, '#i18n-2').toBe('<div id="i18n-2"><p>imbriqué</p></div>');
expectHtml(el, '#i18n-3') expectHtml(el, '#i18n-3')
.toBe('<div id="i18n-3"><p><i>avec des espaces réservés</i></p></div>'); .toBe('<div id="i18n-3"><p><i>avec des espaces réservés</i></p></div>');
expectHtml(el, '#i18n-3b')
.toBe(
'<div id="i18n-3b"><p><i class="preserved-on-placeholders">avec des espaces réservés</i></p></div>');
expectHtml(el, '#i18n-4') expectHtml(el, '#i18n-4')
.toBe('<p id="i18n-4" title="sur des balises non traductibles"></p>'); .toBe('<p id="i18n-4" title="sur des balises non traductibles"></p>');
expectHtml(el, '#i18n-5').toBe('<p id="i18n-5" title="sur des balises traductibles"></p>'); expectHtml(el, '#i18n-5').toBe('<p id="i18n-5" title="sur des balises traductibles"></p>');
@ -66,8 +69,10 @@ export function main() {
expect(el.query(By.css('#i18n-14')).nativeElement).toHaveText('beaucoup'); expect(el.query(By.css('#i18n-14')).nativeElement).toHaveText('beaucoup');
cmp.sex = 'm'; cmp.sex = 'm';
cmp.sexB = 'f';
tb.detectChanges(); tb.detectChanges();
expect(el.query(By.css('#i18n-8')).nativeElement).toHaveText('homme'); expect(el.query(By.css('#i18n-8')).nativeElement).toHaveText('homme');
expect(el.query(By.css('#i18n-8b')).nativeElement).toHaveText('femme');
cmp.sex = 'f'; cmp.sex = 'f';
tb.detectChanges(); tb.detectChanges();
expect(el.query(By.css('#i18n-8')).nativeElement).toHaveText('femme'); expect(el.query(By.css('#i18n-8')).nativeElement).toHaveText('femme');
@ -106,6 +111,7 @@ function expectHtml(el: DebugElement, cssSelector: string): any {
<div id="i18n-2"><p i18n="different meaning|">nested</p></div> <div id="i18n-2"><p i18n="different meaning|">nested</p></div>
<div id="i18n-3"><p i18n><i>with placeholders</i></p></div> <div id="i18n-3"><p i18n><i>with placeholders</i></p></div>
<div id="i18n-3b"><p i18n><i class="preserved-on-placeholders">with placeholders</i></p></div>
<div> <div>
<p id="i18n-4" i18n-title title="on not translatable node"></p> <p id="i18n-4" i18n-title title="on not translatable node"></p>
@ -117,7 +123,10 @@ function expectHtml(el: DebugElement, cssSelector: string): any {
<div i18n id="i18n-7">{count, plural, =0 {zero} =1 {one} =2 {two} other {<b>many</b>}}</div> <div i18n id="i18n-7">{count, plural, =0 {zero} =1 {one} =2 {two} other {<b>many</b>}}</div>
<div i18n id="i18n-8"> <div i18n id="i18n-8">
{sex, sex, m {male} f {female}} {sex, select, m {male} f {female}}
</div>
<div i18n id="i18n-8b">
{sexB, select, m {male} f {female}}
</div> </div>
<div i18n id="i18n-9">{{ "count = " + count }}</div> <div i18n id="i18n-9">{{ "count = " + count }}</div>
@ -135,8 +144,9 @@ function expectHtml(el: DebugElement, cssSelector: string): any {
` `
}) })
class I18nComponent { class I18nComponent {
count: number = 0; count: number;
sex: string = 'm'; sex: string;
sexB: string;
} }
class FrLocalization extends NgLocalization { class FrLocalization extends NgLocalization {
@ -153,51 +163,52 @@ class FrLocalization extends NgLocalization {
const XTB = ` const XTB = `
<translationbundle> <translationbundle>
<translation id="3cb04208df1c2f62553ed48e75939cf7107f9dad">attributs i18n sur les balises</translation> <translation id="615790887472569365">attributs i18n sur les balises</translation>
<translation id="52895b1221effb3f3585b689f049d2784d714952">imbriqué</translation> <translation id="3707494640264351337">imbriqué</translation>
<translation id="88d5f22050a9df477ee5646153558b3a4862d47e">imbriqué</translation> <translation id="5539162898278769904">imbriqué</translation>
<translation id="34fec9cc62e28e8aa6ffb306fa8569ef0a8087fe"><ph name="START_ITALIC_TEXT"/>avec des espaces réservés<ph name="CLOSE_ITALIC_TEXT"/></translation> <translation id="3780349238193953556"><ph name="START_ITALIC_TEXT"/>avec des espaces réservés<ph name="CLOSE_ITALIC_TEXT"/></translation>
<translation id="1fe4616cce80a57c7707bac1c97054aa8e244a67">sur des balises non traductibles</translation> <translation id="5525133077318024839">sur des balises non traductibles</translation>
<translation id="67162b5af5f15fd0eb6480c88688dafdf952b93a">sur des balises traductibles</translation> <translation id="8670732454866344690">sur des balises traductibles</translation>
<translation id="dc5536bb9e0e07291c185a0d306601a2ecd4813f">{count, plural, =0 {zero} =1 {un} =2 {deux} other {<ph name="START_BOLD_TEXT"/>beaucoup<ph name="CLOSE_BOLD_TEXT"/>}}</translation> <translation id="4593805537723189714">{VAR_PLURAL, plural, =0 {zero} =1 {un} =2 {deux} other {<ph name="START_BOLD_TEXT"/>beaucoup<ph name="CLOSE_BOLD_TEXT"/>}}</translation>
<translation id="018efa03821ca41e27611e4a584736810d56ed8a"><ph name="ICU"/></translation> <translation id="1746565782635215"><ph name="ICU"/></translation>
<translation id="fd3186ad2a9aa801fe072ddb16ca34cd98ae93da">{sex, sex, m {homme} f {femme}}</translation> <translation id="5868084092545682515">{VAR_SELECT, select, m {homme} f {femme}}</translation>
<translation id="d9879678f727b244bc7c7e20f22b63d98cb14890"><ph name="INTERPOLATION"/></translation> <translation id="4851788426695310455"><ph name="INTERPOLATION"/></translation>
<translation id="50dac33dc6fc0578884baac79d875785ed77c928">sexe = <ph name="INTERPOLATION"/></translation> <translation id="9013357158046221374">sexe = <ph name="INTERPOLATION"/></translation>
<translation id="a46f833b1fe6ca49e8b97c18f4b7ea0b930c9383"><ph name="CUSTOM_NAME"/></translation> <translation id="8324617391167353662"><ph name="CUSTOM_NAME"/></translation>
<translation id="2ec983b4893bcd5b24af33bebe3ecba63868453c">dans une section traductible</translation> <translation id="7685649297917455806">dans une section traductible</translation>
<translation id="eee74a5be8a75881a4785905bd8302a71f7d9f75"> <translation id="2387287228265107305">
<ph name="START_HEADING_LEVEL1"/>Balises dans les commentaires html<ph name="CLOSE_HEADING_LEVEL1"/> <ph name="START_HEADING_LEVEL1"/>Balises dans les commentaires html<ph name="CLOSE_HEADING_LEVEL1"/>
<ph name="START_TAG_DIV"/><ph name="CLOSE_TAG_DIV"/> <ph name="START_TAG_DIV"/><ph name="CLOSE_TAG_DIV"/>
<ph name="START_TAG_DIV_1"/><ph name="ICU"/><ph name="CLOSE_TAG_DIV"></ph> <ph name="START_TAG_DIV_1"/><ph name="ICU"/><ph name="CLOSE_TAG_DIV"></ph>
</translation> </translation>
<translation id="93a30c67d4e6c9b37aecfe2ac0f2b5d366d7b520">ca <ph name="START_BOLD_TEXT"/>devrait<ph name="CLOSE_BOLD_TEXT"/> marcher</translation> <translation id="1491627405349178954">ca <ph name="START_BOLD_TEXT"/>devrait<ph name="CLOSE_BOLD_TEXT"/> marcher</translation>
</translationbundle>`; </translationbundle>`;
// unused, for reference only // unused, for reference only
// can be generated from xmb_spec as follow: // can be generated from xmb_spec as follow:
// `iit('extract xmb', () => { console.log(toXmb(HTML)); });` // `fit('extract xmb', () => { console.log(toXmb(HTML)); });`
const XMB = ` const XMB = `
<messagebundle> <messagebundle>
<msg id="3cb04208df1c2f62553ed48e75939cf7107f9dad">i18n attribute on tags</msg> <msg id="615790887472569365">i18n attribute on tags</msg>
<msg id="52895b1221effb3f3585b689f049d2784d714952">nested</msg> <msg id="3707494640264351337">nested</msg>
<msg id="88d5f22050a9df477ee5646153558b3a4862d47e" meaning="different meaning">nested</msg> <msg id="5539162898278769904" meaning="different meaning">nested</msg>
<msg id="34fec9cc62e28e8aa6ffb306fa8569ef0a8087fe"><ph name="START_ITALIC_TEXT"><ex>&lt;i&gt;</ex></ph>with placeholders<ph name="CLOSE_ITALIC_TEXT"><ex>&lt;/i&gt;</ex></ph></msg> <msg id="3780349238193953556"><ph name="START_ITALIC_TEXT"><ex>&lt;i&gt;</ex></ph>with placeholders<ph name="CLOSE_ITALIC_TEXT"><ex>&lt;/i&gt;</ex></ph></msg>
<msg id="1fe4616cce80a57c7707bac1c97054aa8e244a67">on not translatable node</msg> <msg id="5525133077318024839">on not translatable node</msg>
<msg id="67162b5af5f15fd0eb6480c88688dafdf952b93a">on translatable node</msg> <msg id="8670732454866344690">on translatable node</msg>
<msg id="dc5536bb9e0e07291c185a0d306601a2ecd4813f">{count, plural, =0 {zero}=1 {one}=2 {two}other {<ph name="START_BOLD_TEXT"><ex>&lt;b&gt;</ex></ph>many<ph name="CLOSE_BOLD_TEXT"><ex>&lt;/b&gt;</ex></ph>}}</msg> <msg id="4593805537723189714">{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<ph name="START_BOLD_TEXT"><ex>&lt;b&gt;</ex></ph>many<ph name="CLOSE_BOLD_TEXT"><ex>&lt;/b&gt;</ex></ph>} }</msg>
<msg id="018efa03821ca41e27611e4a584736810d56ed8a"> <msg id="1746565782635215">
<ph name="ICU"/> <ph name="ICU"/>
</msg> </msg>
<msg id="fd3186ad2a9aa801fe072ddb16ca34cd98ae93da">{sex, sex, m {male}f {female}}</msg> <msg id="5868084092545682515">{VAR_SELECT, select, m {male} f {female} }</msg>
<msg id="d9879678f727b244bc7c7e20f22b63d98cb14890"><ph name="INTERPOLATION"/></msg> <msg id="4851788426695310455"><ph name="INTERPOLATION"/></msg>
<msg id="50dac33dc6fc0578884baac79d875785ed77c928">sex = <ph name="INTERPOLATION"/></msg> <msg id="9013357158046221374">sex = <ph name="INTERPOLATION"/></msg>
<msg id="a46f833b1fe6ca49e8b97c18f4b7ea0b930c9383"><ph name="CUSTOM_NAME"/></msg> <msg id="8324617391167353662"><ph name="CUSTOM_NAME"/></msg>
<msg id="2ec983b4893bcd5b24af33bebe3ecba63868453c">in a translatable section</msg> <msg id="7685649297917455806">in a translatable section</msg>
<msg id="eee74a5be8a75881a4785905bd8302a71f7d9f75"> <msg id="2387287228265107305">
<ph name="START_HEADING_LEVEL1"><ex>&lt;h1&gt;</ex></ph>Markers in html comments<ph name="CLOSE_HEADING_LEVEL1"><ex>&lt;/h1&gt;</ex></ph> <ph name="START_HEADING_LEVEL1"><ex>&lt;h1&gt;</ex></ph>Markers in html comments<ph name="CLOSE_HEADING_LEVEL1"><ex>&lt;/h1&gt;</ex></ph>
<ph name="START_TAG_DIV"><ex>&lt;div&gt;</ex></ph><ph name="CLOSE_TAG_DIV"><ex>&lt;/div&gt;</ex></ph> <ph name="START_TAG_DIV"><ex>&lt;div&gt;</ex></ph><ph name="CLOSE_TAG_DIV"><ex>&lt;/div&gt;</ex></ph>
<ph name="START_TAG_DIV_1"><ex>&lt;div&gt;</ex></ph><ph name="ICU"/><ph name="CLOSE_TAG_DIV"><ex>&lt;/div&gt;</ex></ph> <ph name="START_TAG_DIV_1"><ex>&lt;div&gt;</ex></ph><ph name="ICU"/><ph name="CLOSE_TAG_DIV"><ex>&lt;/div&gt;</ex></ph>
</msg> </msg>
<msg id="93a30c67d4e6c9b37aecfe2ac0f2b5d366d7b520">it <ph name="START_BOLD_TEXT"><ex>&lt;b&gt;</ex></ph>should<ph name="CLOSE_BOLD_TEXT"><ex>&lt;/b&gt;</ex></ph> work</msg> <msg id="1491627405349178954">it <ph name="START_BOLD_TEXT"><ex>&lt;b&gt;</ex></ph>should<ph name="CLOSE_BOLD_TEXT"><ex>&lt;/b&gt;</ex></ph> work</msg>
</messagebundle>`; </messagebundle>
`;

View File

@ -6,12 +6,10 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import * as i18n from '@angular/compiler/src/i18n/i18n_ast';
import {Serializer} from '@angular/compiler/src/i18n/serializers/serializer';
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
import {serializeNodes} from '../../src/i18n/digest'; import {serializeNodes} from '../../src/i18n/digest';
import * as i18n from '../../src/i18n/i18n_ast';
import {MessageBundle} from '../../src/i18n/message_bundle'; import {MessageBundle} from '../../src/i18n/message_bundle';
import {Serializer} from '../../src/i18n/serializers/serializer';
import {HtmlParser} from '../../src/ml_parser/html_parser'; import {HtmlParser} from '../../src/ml_parser/html_parser';
import {DEFAULT_INTERPOLATION_CONFIG} from '../../src/ml_parser/interpolation_config'; import {DEFAULT_INTERPOLATION_CONFIG} from '../../src/ml_parser/interpolation_config';
@ -26,17 +24,18 @@ export function main(): void {
messages.updateFromTemplate( messages.updateFromTemplate(
'<p i18n="m|d">Translate Me</p>', 'url', DEFAULT_INTERPOLATION_CONFIG); '<p i18n="m|d">Translate Me</p>', 'url', DEFAULT_INTERPOLATION_CONFIG);
expect(humanizeMessages(messages)).toEqual([ expect(humanizeMessages(messages)).toEqual([
'2e791a68a3324ecdd29e252198638dafacec46e9=Translate Me', 'Translate Me (m|d)',
]); ]);
}); });
it('should extract the same message with different meaning in different entries', () => { it('should extract the all messages and duplicates', () => {
messages.updateFromTemplate( messages.updateFromTemplate(
'<p i18n="m|d">Translate Me</p><p i18n>Translate Me</p>', 'url', '<p i18n="m|d">Translate Me</p><p i18n>Translate Me</p><p i18n>Translate Me</p>', 'url',
DEFAULT_INTERPOLATION_CONFIG); DEFAULT_INTERPOLATION_CONFIG);
expect(humanizeMessages(messages)).toEqual([ expect(humanizeMessages(messages)).toEqual([
'2e791a68a3324ecdd29e252198638dafacec46e9=Translate Me', 'Translate Me (m|d)',
'8ca133f957845af1b1868da1b339180d1f519644=Translate Me', 'Translate Me (|)',
'Translate Me (|)',
]); ]);
}); });
}); });
@ -44,13 +43,14 @@ export function main(): void {
} }
class _TestSerializer implements Serializer { class _TestSerializer implements Serializer {
write(messageMap: {[id: string]: i18n.Message}): string { write(messages: i18n.Message[]): string {
return Object.keys(messageMap) return messages.map(msg => `${serializeNodes(msg.nodes)} (${msg.meaning}|${msg.description})`)
.map(id => `${id}=${serializeNodes(messageMap[id].nodes)}`)
.join('//'); .join('//');
} }
load(content: string, url: string, placeholders: {}): {} { return null; } load(content: string, url: string): {} { return null; }
digest(msg: i18n.Message): string { return 'unused'; }
} }
function humanizeMessages(catalog: MessageBundle): string[] { function humanizeMessages(catalog: MessageBundle): string[] {

View File

@ -6,8 +6,6 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
import {PlaceholderRegistry} from '../../../src/i18n/serializers/placeholder'; import {PlaceholderRegistry} from '../../../src/i18n/serializers/placeholder';
export function main(): void { export function main(): void {

View File

@ -6,12 +6,13 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Xliff} from '@angular/compiler/src/i18n/serializers/xliff'; import {escapeRegExp} from '@angular/core/src/facade/lang';
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
import {serializeNodes} from '../../../src/i18n/digest';
import {MessageBundle} from '../../../src/i18n/message_bundle'; import {MessageBundle} from '../../../src/i18n/message_bundle';
import {Xliff} from '../../../src/i18n/serializers/xliff';
import {HtmlParser} from '../../../src/ml_parser/html_parser'; import {HtmlParser} from '../../../src/ml_parser/html_parser';
import {DEFAULT_INTERPOLATION_CONFIG} from '../../../src/ml_parser/interpolation_config'; import {DEFAULT_INTERPOLATION_CONFIG} from '../../../src/ml_parser/interpolation_config';
import {serializeNodes} from '../../ml_parser/ast_serializer_spec';
const HTML = ` const HTML = `
<p i18n-title title="translatable attribute">not translatable</p> <p i18n-title title="translatable attribute">not translatable</p>
@ -77,8 +78,7 @@ const LOAD_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
`; `;
export function main(): void { export function main(): void {
let serializer: Xliff; const serializer = new Xliff();
let htmlParser: HtmlParser;
function toXliff(html: string): string { function toXliff(html: string): string {
const catalog = new MessageBundle(new HtmlParser, [], {}); const catalog = new MessageBundle(new HtmlParser, [], {});
@ -86,39 +86,130 @@ export function main(): void {
return catalog.write(serializer); return catalog.write(serializer);
} }
function loadAsText(template: string, xliff: string): {[id: string]: string} { function loadAsMap(xliff: string): {[id: string]: string} {
const messageBundle = new MessageBundle(htmlParser, [], {}); const i18nNodesByMsgId = serializer.load(xliff, 'url');
messageBundle.updateFromTemplate(template, 'url', DEFAULT_INTERPOLATION_CONFIG); const msgMap: {[id: string]: string} = {};
Object.keys(i18nNodesByMsgId)
.forEach(id => msgMap[id] = serializeNodes(i18nNodesByMsgId[id]).join(''));
const asAst = serializer.load(xliff, 'url', messageBundle); return msgMap;
const asText: {[id: string]: string} = {};
Object.keys(asAst).forEach(id => { asText[id] = serializeNodes(asAst[id]).join(''); });
return asText;
} }
describe('XLIFF serializer', () => { describe('XLIFF serializer', () => {
beforeEach(() => {
htmlParser = new HtmlParser();
serializer = new Xliff(htmlParser, DEFAULT_INTERPOLATION_CONFIG);
});
describe('write', () => { describe('write', () => {
it('should write a valid xliff file', () => { expect(toXliff(HTML)).toEqual(WRITE_XLIFF); }); it('should write a valid xliff file', () => { expect(toXliff(HTML)).toEqual(WRITE_XLIFF); });
}); });
describe('load', () => { describe('load', () => {
it('should load XLIFF files', () => { it('should load XLIFF files', () => {
expect(loadAsText(HTML, LOAD_XLIFF)).toEqual({ expect(loadAsMap(LOAD_XLIFF)).toEqual({
'983775b9a51ce14b036be72d4cfd65d68d64e231': 'etubirtta elbatalsnart', '983775b9a51ce14b036be72d4cfd65d68d64e231': 'etubirtta elbatalsnart',
'ec1d033f2436133c14ab038286c4f5df4697484a': 'ec1d033f2436133c14ab038286c4f5df4697484a':
'{{ interpolation}} footnemele elbatalsnart <b>sredlohecalp htiw</b>', '<ph name="INTERPOLATION"/> footnemele elbatalsnart <ph name="START_BOLD_TEXT"/>sredlohecalp htiw<ph name="CLOSE_BOLD_TEXT"/>',
'db3e0a6a5a96481f60aec61d98c3eecddef5ac23': 'oof', 'db3e0a6a5a96481f60aec61d98c3eecddef5ac23': 'oof',
'd7fa2d59aaedcaa5309f13028c59af8c85b8c49d': '<div></div><img/><br/>', 'd7fa2d59aaedcaa5309f13028c59af8c85b8c49d':
'<ph name="START_TAG_DIV"/><ph name="CLOSE_TAG_DIV"/><ph name="TAG_IMG"/><ph name="LINE_BREAK"/>',
}); });
}); });
describe('structure errors', () => {
it('should throw when a trans-unit has no translation', () => {
const 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="missingtarget">
<source/>
</trans-unit>
</body>
</file>
</xliff>`;
expect(() => {
loadAsMap(XLIFF);
}).toThrowError(/Message missingtarget misses a translation/);
});
it('should throw when a trans-unit has no id attribute', () => {
const 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 datatype="html">
<source/>
<target/>
</trans-unit>
</body>
</file>
</xliff>`;
expect(() => {
loadAsMap(XLIFF);
}).toThrowError(/<trans-unit> misses the "id" attribute/);
});
it('should throw on duplicate trans-unit id', () => {
const 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="deadbeef">
<source/>
<target/>
</trans-unit>
<trans-unit id="deadbeef">
<source/>
<target/>
</trans-unit>
</body>
</file>
</xliff>`;
expect(() => {
loadAsMap(XLIFF);
}).toThrowError(/Duplicated translations for msg deadbeef/);
});
});
describe('message errors', () => {
it('should throw on unknown message tags', () => {
const 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="deadbeef" datatype="html">
<source/>
<target><b>msg should contain only ph tags</b></target>
</trans-unit>
</body>
</file>
</xliff>`;
expect(() => { loadAsMap(XLIFF); })
.toThrowError(
new RegExp(escapeRegExp(`[ERROR ->]<b>msg should contain only ph tags</b>`)));
});
it('should throw when a placeholder misses an id attribute', () => {
const 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="deadbeef" datatype="html">
<source/>
<target><x/></target>
</trans-unit>
</body>
</file>
</xliff>`;
expect(() => {
loadAsMap(XLIFF);
}).toThrowError(new RegExp(escapeRegExp(`<x> misses the "id" attribute`)));
});
});
}); });
}); });
} }

View File

@ -43,10 +43,10 @@ export function main(): void {
<!ELEMENT ex (#PCDATA)> <!ELEMENT ex (#PCDATA)>
]> ]>
<messagebundle> <messagebundle>
<msg id="ec1d033f2436133c14ab038286c4f5df4697484a">translatable element <ph name="START_BOLD_TEXT"><ex>&lt;b&gt;</ex></ph>with placeholders<ph name="CLOSE_BOLD_TEXT"><ex>&lt;/b&gt;</ex></ph> <ph name="INTERPOLATION"/></msg> <msg id="7056919470098446707">translatable element <ph name="START_BOLD_TEXT"><ex>&lt;b&gt;</ex></ph>with placeholders<ph name="CLOSE_BOLD_TEXT"><ex>&lt;/b&gt;</ex></ph> <ph name="INTERPOLATION"/></msg>
<msg id="e2ccf3d131b15f54aa1fcf1314b1ca77c14bfcc2">{ count, plural, =0 {<ph name="START_PARAGRAPH"><ex>&lt;p&gt;</ex></ph>test<ph name="CLOSE_PARAGRAPH"><ex>&lt;/p&gt;</ex></ph>} }</msg> <msg id="2981514368455622387">{VAR_PLURAL, plural, =0 {<ph name="START_PARAGRAPH"><ex>&lt;p&gt;</ex></ph>test<ph name="CLOSE_PARAGRAPH"><ex>&lt;/p&gt;</ex></ph>} }</msg>
<msg id="db3e0a6a5a96481f60aec61d98c3eecddef5ac23" desc="d" meaning="m">foo</msg> <msg id="7999024498831672133" desc="d" meaning="m">foo</msg>
<msg id="0e16a673a5a7a135c9f7b957ec2c5c6f6ee6e2c4">{ count, plural, =0 {{ sex, select, other {<ph name="START_PARAGRAPH"><ex>&lt;p&gt;</ex></ph>deeply nested<ph name="CLOSE_PARAGRAPH"><ex>&lt;/p&gt;</ex></ph>} } } }</msg> <msg id="2015957479576096115">{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<ph name="START_PARAGRAPH"><ex>&lt;p&gt;</ex></ph>deeply nested<ph name="CLOSE_PARAGRAPH"><ex>&lt;/p&gt;</ex></ph>} } } }</msg>
</messagebundle> </messagebundle>
`; `;
@ -55,7 +55,7 @@ export function main(): void {
it('should throw when trying to load an xmb file', () => { it('should throw when trying to load an xmb file', () => {
expect(() => { expect(() => {
const serializer = new Xmb(); const serializer = new Xmb();
serializer.load(XMB, 'url', null); serializer.load(XMB, 'url');
}).toThrowError(/Unsupported/); }).toThrowError(/Unsupported/);
}); });
}); });

View File

@ -6,8 +6,6 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {describe, expect, it} from '@angular/core/testing/testing_internal';
import * as xml from '../../../src/i18n/serializers/xml_helper'; import * as xml from '../../../src/i18n/serializers/xml_helper';
export function main(): void { export function main(): void {

View File

@ -8,37 +8,24 @@
import {escapeRegExp} from '@angular/core/src/facade/lang'; import {escapeRegExp} from '@angular/core/src/facade/lang';
import {MessageBundle} from '../../../src/i18n/message_bundle'; import {serializeNodes} from '../../../src/i18n/digest';
import {Xtb} from '../../../src/i18n/serializers/xtb'; import {Xtb} from '../../../src/i18n/serializers/xtb';
import {HtmlParser} from '../../../src/ml_parser/html_parser';
import {DEFAULT_INTERPOLATION_CONFIG} from '../../../src/ml_parser/interpolation_config';
import {serializeNodes} from '../../ml_parser/ast_serializer_spec';
export function main(): void { export function main(): void {
describe('XTB serializer', () => { describe('XTB serializer', () => {
let serializer: Xtb; const serializer = new Xtb();
let htmlParser: HtmlParser;
function loadAsText(template: string, xtb: string): {[id: string]: string} { function loadAsMap(xtb: string): {[id: string]: string} {
const messageBundle = new MessageBundle(htmlParser, [], {}); const i18nNodesByMsgId = serializer.load(xtb, 'url');
messageBundle.updateFromTemplate(template, 'url', DEFAULT_INTERPOLATION_CONFIG); const msgMap: {[id: string]: string} = {};
Object.keys(i18nNodesByMsgId).forEach(id => {
const asAst = serializer.load(xtb, 'url', messageBundle); msgMap[id] = serializeNodes(i18nNodesByMsgId[id]).join('');
const asText: {[id: string]: string} = {};
Object.keys(asAst).forEach(id => { asText[id] = serializeNodes(asAst[id]).join(''); });
return asText;
}
beforeEach(() => {
htmlParser = new HtmlParser();
serializer = new Xtb(htmlParser, DEFAULT_INTERPOLATION_CONFIG);
}); });
return msgMap;
}
describe('load', () => { describe('load', () => {
it('should load XTB files with a doctype', () => { it('should load XTB files with a doctype', () => {
const HTML = `<div i18n>bar</div>`;
const XTB = `<?xml version="1.0" encoding="UTF-8"?> const XTB = `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE translationbundle [<!ELEMENT translationbundle (translation)*> <!DOCTYPE translationbundle [<!ELEMENT translationbundle (translation)*>
<!ATTLIST translationbundle lang CDATA #REQUIRED> <!ATTLIST translationbundle lang CDATA #REQUIRED>
@ -50,75 +37,66 @@ export function main(): void {
<!ATTLIST ph name CDATA #REQUIRED> <!ATTLIST ph name CDATA #REQUIRED>
]> ]>
<translationbundle> <translationbundle>
<translation id="28a86c8a00ae573b2bac698d6609316dc7b4a226">rab</translation> <translation id="8841459487341224498">rab</translation>
</translationbundle>`; </translationbundle>`;
expect(loadAsText(HTML, XTB)).toEqual({'28a86c8a00ae573b2bac698d6609316dc7b4a226': 'rab'}); expect(loadAsMap(XTB)).toEqual({'8841459487341224498': 'rab'});
}); });
it('should load XTB files without placeholders', () => { it('should load XTB files without placeholders', () => {
const HTML = `<div i18n>bar</div>`;
const XTB = `<?xml version="1.0" encoding="UTF-8"?> const XTB = `<?xml version="1.0" encoding="UTF-8"?>
<translationbundle> <translationbundle>
<translation id="28a86c8a00ae573b2bac698d6609316dc7b4a226">rab</translation> <translation id="8841459487341224498">rab</translation>
</translationbundle>`; </translationbundle>`;
expect(loadAsText(HTML, XTB)).toEqual({'28a86c8a00ae573b2bac698d6609316dc7b4a226': 'rab'}); expect(loadAsMap(XTB)).toEqual({'8841459487341224498': 'rab'});
}); });
it('should load XTB files with placeholders', () => {
const HTML = `<div i18n><p>bar</p></div>`;
it('should load XTB files with placeholders', () => {
const XTB = `<?xml version="1.0" encoding="UTF-8"?> const XTB = `<?xml version="1.0" encoding="UTF-8"?>
<translationbundle> <translationbundle>
<translation id="7de4d8ff1e42b7b31da6204074818236a9a5317f"><ph name="START_PARAGRAPH"/>rab<ph name="CLOSE_PARAGRAPH"/></translation> <translation id="8877975308926375834"><ph name="START_PARAGRAPH"/>rab<ph name="CLOSE_PARAGRAPH"/></translation>
</translationbundle>`; </translationbundle>`;
expect(loadAsText(HTML, XTB)).toEqual({ expect(loadAsMap(XTB)).toEqual({
'7de4d8ff1e42b7b31da6204074818236a9a5317f': '<p>rab</p>' '8877975308926375834': '<ph name="START_PARAGRAPH"/>rab<ph name="CLOSE_PARAGRAPH"/>'
}); });
}); });
it('should replace ICU placeholders with their translations', () => { it('should replace ICU placeholders with their translations', () => {
const HTML = `<div i18n>-{ count, plural, =0 {<p>bar</p>}}-</div>`;
const XTB = `<?xml version="1.0" encoding="UTF-8" ?> const XTB = `<?xml version="1.0" encoding="UTF-8" ?>
<translationbundle> <translationbundle>
<translation id="eb404e202fed4846e25e7d9ac1fcb719fe4da257">*<ph name="ICU"/>*</translation> <translation id="7717087045075616176">*<ph name="ICU"/>*</translation>
<translation id="fc92b9b781194a02ab773129c8c5a7fc0735efd7">{ count, plural, =1 {<ph name="START_PARAGRAPH"/>rab<ph name="CLOSE_PARAGRAPH"/>}}</translation> <translation id="5115002811911870583">{VAR_PLURAL, plural, =1 {<ph name="START_PARAGRAPH"/>rab<ph name="CLOSE_PARAGRAPH"/>}}</translation>
</translationbundle>`; </translationbundle>`;
expect(loadAsText(HTML, XTB)).toEqual({ expect(loadAsMap(XTB)).toEqual({
'eb404e202fed4846e25e7d9ac1fcb719fe4da257': `*{ count, plural, =1 {<p>rab</p>}}*`, '7717087045075616176': `*<ph name="ICU"/>*`,
'fc92b9b781194a02ab773129c8c5a7fc0735efd7': `{ count, plural, =1 {<p>rab</p>}}`, '5115002811911870583':
`{VAR_PLURAL, plural, =1 {[<ph name="START_PARAGRAPH"/>, rab, <ph name="CLOSE_PARAGRAPH"/>]}}`,
}); });
}); });
it('should load complex XTB files', () => { it('should load complex XTB files', () => {
const HTML = `
<div i18n>foo <b>bar</b> {{ a + b }}</div>
<div i18n>{ count, plural, =0 {<p>bar</p>}}</div>
<div i18n="m|d">foo</div>
<div i18n>{ count, plural, =0 {{ sex, select, other {<p>bar</p>}} }}</div>`;
const XTB = `<?xml version="1.0" encoding="UTF-8" ?> const XTB = `<?xml version="1.0" encoding="UTF-8" ?>
<translationbundle> <translationbundle>
<translation id="7103b4b13b616270a0044efade97d8b4f96f2ca6"><ph name="INTERPOLATION"/><ph name="START_BOLD_TEXT"/>rab<ph name="CLOSE_BOLD_TEXT"/> oof</translation> <translation id="8281795707202401639"><ph name="INTERPOLATION"/><ph name="START_BOLD_TEXT"/>rab<ph name="CLOSE_BOLD_TEXT"/> oof</translation>
<translation id="fc92b9b781194a02ab773129c8c5a7fc0735efd7">{ count, plural, =1 {<ph name="START_PARAGRAPH"/>rab<ph name="CLOSE_PARAGRAPH"/>}}</translation> <translation id="5115002811911870583">{VAR_PLURAL, plural, =1 {<ph name="START_PARAGRAPH"/>rab<ph name="CLOSE_PARAGRAPH"/>}}</translation>
<translation id="db3e0a6a5a96481f60aec61d98c3eecddef5ac23">oof</translation> <translation id="130772889486467622">oof</translation>
<translation id="8fb569d3dd83e92eff2551b24f5290d3035ce61b">{ count, plural, =1 {{ sex, select, other {<ph name="START_PARAGRAPH"/>rab<ph name="CLOSE_PARAGRAPH"/>}} }}</translation> <translation id="4739316421648347533">{VAR_PLURAL, plural, =1 {{VAR_GENDER, gender, male {<ph name="START_PARAGRAPH"/>rab<ph name="CLOSE_PARAGRAPH"/>}} }}</translation>
</translationbundle>`; </translationbundle>`;
expect(loadAsText(HTML, XTB)).toEqual({ expect(loadAsMap(XTB)).toEqual({
'7103b4b13b616270a0044efade97d8b4f96f2ca6': `{{ a + b }}<b>rab</b> oof`, '8281795707202401639':
'fc92b9b781194a02ab773129c8c5a7fc0735efd7': `{ count, plural, =1 {<p>rab</p>}}`, `<ph name="INTERPOLATION"/><ph name="START_BOLD_TEXT"/>rab<ph name="CLOSE_BOLD_TEXT"/> oof`,
'db3e0a6a5a96481f60aec61d98c3eecddef5ac23': `oof`, '5115002811911870583':
'8fb569d3dd83e92eff2551b24f5290d3035ce61b': `{VAR_PLURAL, plural, =1 {[<ph name="START_PARAGRAPH"/>, rab, <ph name="CLOSE_PARAGRAPH"/>]}}`,
`{ count, plural, =1 {{ sex, select, other {<p>rab</p>}} }}`, '130772889486467622': `oof`,
'4739316421648347533':
`{VAR_PLURAL, plural, =1 {[{VAR_GENDER, gender, male {[<ph name="START_PARAGRAPH"/>, rab, <ph name="CLOSE_PARAGRAPH"/>]}}, ]}}`,
}); });
}); });
}); });
describe('errors', () => { describe('errors', () => {
@ -127,7 +105,7 @@ export function main(): void {
'<translationbundle><translationbundle></translationbundle></translationbundle>'; '<translationbundle><translationbundle></translationbundle></translationbundle>';
expect(() => { expect(() => {
loadAsText('', XTB); loadAsMap(XTB);
}).toThrowError(/<translationbundle> elements can not be nested/); }).toThrowError(/<translationbundle> elements can not be nested/);
}); });
@ -136,58 +114,49 @@ export function main(): void {
<translation></translation> <translation></translation>
</translationbundle>`; </translationbundle>`;
expect(() => { expect(() => { loadAsMap(XTB); }).toThrowError(/<translation> misses the "id" attribute/);
loadAsText('', XTB);
}).toThrowError(/<translation> misses the "id" attribute/);
}); });
it('should throw when a placeholder has no name attribute', () => { it('should throw when a placeholder has no name attribute', () => {
const HTML = '<div i18n>give me a message</div>';
const XTB = `<translationbundle> const XTB = `<translationbundle>
<translation id="8de97c6a35252d9409dcaca0b8171c952740b28c"><ph /></translation> <translation id="1186013544048295927"><ph /></translation>
</translationbundle>`; </translationbundle>`;
expect(() => { loadAsText(HTML, XTB); }).toThrowError(/<ph> misses the "name" attribute/); expect(() => { loadAsMap(XTB); }).toThrowError(/<ph> misses the "name" attribute/);
}); });
it('should throw when a placeholder is not present in the source message', () => { it('should throw on unknown xtb tags', () => {
const HTML = `<div i18n>bar</div>`;
const XTB = `<?xml version="1.0" encoding="UTF-8"?>
<translationbundle>
<translation id="28a86c8a00ae573b2bac698d6609316dc7b4a226"><ph name="UNKNOWN"/></translation>
</translationbundle>`;
expect(() => {
loadAsText(HTML, XTB);
}).toThrowError(/The placeholder "UNKNOWN" does not exists in the source message/);
});
});
it('should throw when the translation results in invalid html', () => {
const HTML = `<div i18n><p>bar</p></div>`;
const XTB = `<?xml version="1.0" encoding="UTF-8"?>
<translationbundle>
<translation id="7de4d8ff1e42b7b31da6204074818236a9a5317f">rab<ph name="CLOSE_PARAGRAPH"/></translation>
</translationbundle>`;
expect(() => {
loadAsText(HTML, XTB);
}).toThrowError(/xtb parse errors:\nUnexpected closing tag "p"/);
});
it('should throw on unknown tags', () => {
const XTB = `<what></what>`; const XTB = `<what></what>`;
expect(() => { expect(() => {
loadAsText('', XTB); loadAsMap(XTB);
}).toThrowError(new RegExp(escapeRegExp(`Unexpected tag ("[ERROR ->]<what></what>")`))); }).toThrowError(new RegExp(escapeRegExp(`Unexpected tag ("[ERROR ->]<what></what>")`)));
}); });
it('should throw on unknown message tags', () => {
const XTB = `<translationbundle>
<translation id="1186013544048295927"><b>msg should contain only ph tags</b></translation>
</translationbundle>`;
expect(() => { loadAsMap(XTB); })
.toThrowError(
new RegExp(escapeRegExp(`[ERROR ->]<b>msg should contain only ph tags</b>`)));
});
it('should throw on duplicate message id', () => {
const XTB = `<translationbundle>
<translation id="1186013544048295927">msg1</translation>
<translation id="1186013544048295927">msg2</translation>
</translationbundle>`;
expect(() => {
loadAsMap(XTB);
}).toThrowError(/Duplicated translations for msg 1186013544048295927/);
});
it('should throw when trying to save an xtb file', it('should throw when trying to save an xtb file',
() => { expect(() => { serializer.write({}); }).toThrowError(/Unsupported/); }); () => { expect(() => { serializer.write([]); }).toThrowError(/Unsupported/); });
});
}); });
} }

View File

@ -0,0 +1,110 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as i18n from '../../src/i18n/i18n_ast';
import {TranslationBundle} from '../../src/i18n/translation_bundle';
import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_util';
import {serializeNodes} from '../ml_parser/ast_serializer_spec';
export function main(): void {
describe('TranslationBundle', () => {
const file = new ParseSourceFile('content', 'url');
const location = new ParseLocation(file, 0, 0, 0);
const span = new ParseSourceSpan(location, null);
const srcNode = new i18n.Text('src', span);
it('should translate a plain message', () => {
const msgMap = {foo: [new i18n.Text('bar', null)]};
const tb = new TranslationBundle(msgMap, (_) => 'foo');
const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd');
expect(serializeNodes(tb.get(msg))).toEqual(['bar']);
});
it('should translate a message with placeholder', () => {
const msgMap = {
foo: [
new i18n.Text('bar', null),
new i18n.Placeholder('', 'ph1', null),
]
};
const phMap = {
ph1: '*phContent*',
};
const tb = new TranslationBundle(msgMap, (_) => 'foo');
const msg = new i18n.Message([srcNode], phMap, {}, 'm', 'd');
expect(serializeNodes(tb.get(msg))).toEqual(['bar*phContent*']);
});
it('should translate a message with placeholder referencing messages', () => {
const msgMap = {
foo: [
new i18n.Text('--', null),
new i18n.Placeholder('', 'ph1', null),
new i18n.Text('++', null),
],
ref: [
new i18n.Text('*refMsg*', null),
],
};
const refMsg = new i18n.Message([srcNode], {}, {}, 'm', 'd');
const msg = new i18n.Message([srcNode], {}, {ph1: refMsg}, 'm', 'd');
let count = 0;
const digest = (_: any) => count++ ? 'ref' : 'foo';
const tb = new TranslationBundle(msgMap, digest);
expect(serializeNodes(tb.get(msg))).toEqual(['--*refMsg*++']);
});
describe('errors', () => {
it('should report unknown placeholders', () => {
const msgMap = {
foo: [
new i18n.Text('bar', null),
new i18n.Placeholder('', 'ph1', span),
]
};
const tb = new TranslationBundle(msgMap, (_) => 'foo');
const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd');
expect(() => tb.get(msg)).toThrowError(/Unknown placeholder/);
});
it('should report missing translation', () => {
const tb = new TranslationBundle({}, (_) => 'foo');
const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd');
expect(() => tb.get(msg)).toThrowError(/Missing translation for message foo/);
});
it('should report missing referenced message', () => {
const msgMap = {
foo: [new i18n.Placeholder('', 'ph1', span)],
};
const refMsg = new i18n.Message([srcNode], {}, {}, 'm', 'd');
const msg = new i18n.Message([srcNode], {}, {ph1: refMsg}, 'm', 'd');
let count = 0;
const digest = (_: any) => count++ ? 'ref' : 'foo';
const tb = new TranslationBundle(msgMap, digest);
expect(() => tb.get(msg)).toThrowError(/Missing translation for message ref/);
});
it('should report invalid translated html', () => {
const msgMap = {
foo: [
new i18n.Text('text', null),
new i18n.Placeholder('', 'ph1', null),
]
};
const phMap = {
ph1: '</b>',
};
const tb = new TranslationBundle(msgMap, (_) => 'foo');
const msg = new i18n.Message([srcNode], phMap, {}, 'm', 'd');
expect(() => tb.get(msg)).toThrowError(/Unexpected closing tag "b"/);
});
});
});
}

View File

@ -11,6 +11,7 @@ import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit,
import {LIFECYCLE_HOOKS_VALUES} from '@angular/core/src/metadata/lifecycle_hooks'; import {LIFECYCLE_HOOKS_VALUES} from '@angular/core/src/metadata/lifecycle_hooks';
import {TestBed, async, inject} from '@angular/core/testing'; import {TestBed, async, inject} from '@angular/core/testing';
import {identifierName} from '../src/compile_metadata';
import {stringify} from '../src/facade/lang'; import {stringify} from '../src/facade/lang';
import {CompileMetadataResolver} from '../src/metadata_resolver'; import {CompileMetadataResolver} from '../src/metadata_resolver';
import {ResourceLoader} from '../src/resource_loader'; import {ResourceLoader} from '../src/resource_loader';
@ -50,7 +51,7 @@ export function main() {
expect(meta.exportAs).toEqual('someExportAs'); expect(meta.exportAs).toEqual('someExportAs');
expect(meta.isComponent).toBe(true); expect(meta.isComponent).toBe(true);
expect(meta.type.reference).toBe(ComponentWithEverythingInline); expect(meta.type.reference).toBe(ComponentWithEverythingInline);
expect(meta.type.name).toEqual(stringify(ComponentWithEverythingInline)); expect(identifierName(meta.type)).toEqual(stringify(ComponentWithEverythingInline));
expect(meta.type.lifecycleHooks).toEqual(LIFECYCLE_HOOKS_VALUES); expect(meta.type.lifecycleHooks).toEqual(LIFECYCLE_HOOKS_VALUES);
expect(meta.changeDetection).toBe(ChangeDetectionStrategy.Default); expect(meta.changeDetection).toBe(ChangeDetectionStrategy.Default);
expect(meta.inputs).toEqual({'someProp': 'someProp'}); expect(meta.inputs).toEqual({'someProp': 'someProp'});
@ -114,18 +115,24 @@ export function main() {
resourceLoader.flush(); resourceLoader.flush();
}))); })));
it('should use the moduleUrl from the reflector if none is given', it('should use `./` as base url for templates during runtime compilation if no moduleId is given',
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { async(inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
@Component({selector: 'someComponent', templateUrl: 'someUrl'})
class ComponentWithoutModuleId {
}
@NgModule({declarations: [ComponentWithoutModuleId]}) @NgModule({declarations: [ComponentWithoutModuleId]})
class SomeModule { class SomeModule {
} }
resolver.loadNgModuleMetadata(SomeModule, true);
resolver.loadNgModuleMetadata(SomeModule, false).loading.then(() => {
const value: string = const value: string =
resolver.getDirectiveMetadata(ComponentWithoutModuleId).type.moduleUrl; resolver.getDirectiveMetadata(ComponentWithoutModuleId).template.templateUrl;
const expectedEndValue = './ComponentWithoutModuleId'; const expectedEndValue = './someUrl';
expect(value.endsWith(expectedEndValue)).toBe(true); expect(value.endsWith(expectedEndValue)).toBe(true);
})); });
})));
it('should throw when the moduleId is not a string', it('should throw when the moduleId is not a string',
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {

View File

@ -45,9 +45,26 @@ export function main() {
})); }));
}); });
it('should throw when simple class has no component decorator', () => { it('should throw when simple class has no NgModule decorator', () => {
expect(() => resolver.resolve(SimpleClass)) expect(() => resolver.resolve(SimpleClass))
.toThrowError(`No NgModule metadata found for '${stringify(SimpleClass)}'.`); .toThrowError(`No NgModule metadata found for '${stringify(SimpleClass)}'.`);
}); });
it('should support inheriting the metadata', function() {
@NgModule({id: 'p'})
class Parent {
}
class ChildNoDecorator extends Parent {}
@NgModule({id: 'c'})
class ChildWithDecorator extends Parent {
}
expect(resolver.resolve(ChildNoDecorator)).toEqual(new NgModule({id: 'p'}));
expect(resolver.resolve(ChildWithDecorator)).toEqual(new NgModule({id: 'c'}));
});
}); });
} }

View File

@ -9,18 +9,23 @@
import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata'; import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata';
import {JavaScriptEmitter} from '@angular/compiler/src/output/js_emitter'; import {JavaScriptEmitter} from '@angular/compiler/src/output/js_emitter';
import * as o from '@angular/compiler/src/output/output_ast'; import * as o from '@angular/compiler/src/output/output_ast';
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal'; import {ImportResolver} from '@angular/compiler/src/output/path_util';
import {SimpleJsImportGenerator} from './output_emitter_util';
const someModuleUrl = 'somePackage/somePath'; const someModuleUrl = 'somePackage/somePath';
const anotherModuleUrl = 'somePackage/someOtherPath'; const anotherModuleUrl = 'somePackage/someOtherPath';
const sameModuleIdentifier = const sameModuleIdentifier: CompileIdentifierMetadata = {
new CompileIdentifierMetadata({name: 'someLocalId', moduleUrl: someModuleUrl}); reference: {name: 'someLocalId', filePath: someModuleUrl}
};
const externalModuleIdentifier: CompileIdentifierMetadata = {
reference: {name: 'someExternalId', filePath: anotherModuleUrl}
};
const externalModuleIdentifier = class SimpleJsImportGenerator implements ImportResolver {
new CompileIdentifierMetadata({name: 'someExternalId', moduleUrl: anotherModuleUrl}); fileNameToModuleName(importedUrlStr: string, moduleUrlStr: string): string {
return importedUrlStr;
}
}
export function main() { export function main() {
// Note supported features of our OutputAstin JavaScript / ES5: // Note supported features of our OutputAstin JavaScript / ES5:

View File

@ -1,38 +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
*/
// ATTENTION: This file will be overwritten with generated code by main()
import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter';
import {print} from '../../src/facade/lang';
import {assetUrl} from '../../src/identifiers';
function unimplemented(): any {
throw new Error('unimplemented');
}
import {SimpleJsImportGenerator, codegenExportsVars, codegenStmts} from './output_emitter_util';
export function getExpressions(): any {
return unimplemented();
}
// Generator
export function emit() {
const emitter = new TypeScriptEmitter(new SimpleJsImportGenerator());
const emittedCode = emitter.emitStatements(
assetUrl('compiler', 'output/output_emitter_codegen_typed', 'test'), codegenStmts,
codegenExportsVars);
return emittedCode;
}
export function main(args: string[]) {
const emittedCode = emit();
print(emittedCode);
}

View File

@ -1,34 +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
*/
// ATTENTION: This file will be overwritten with generated code by main()
import {JavaScriptEmitter} from '@angular/compiler/src/output/js_emitter';
import {print} from '../../src/facade/lang';
import {assetUrl} from '../../src/identifiers';
import {SimpleJsImportGenerator, codegenExportsVars, codegenStmts} from './output_emitter_util';
export function getExpressions(): any {
throw new Error('unimplemented');
}
// Generator
export function emit() {
const emitter = new JavaScriptEmitter(new SimpleJsImportGenerator());
const emittedCode = emitter.emitStatements(
assetUrl('compiler', 'output/output_emitter_codegen_untyped', 'test'), codegenStmts,
codegenExportsVars);
return emittedCode;
}
export function main(args: string[]) {
const emittedCode = emit();
// debug: console.error(emittedCode);
print(emittedCode);
}

View File

@ -1,197 +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 {interpretStatements} from '@angular/compiler/src/output/output_interpreter';
import {jitStatements} from '@angular/compiler/src/output/output_jit';
import {EventEmitter} from '@angular/core';
import {ViewType} from '@angular/core/src/linker/view_type';
import {beforeEach, describe, it} from '@angular/core/testing/testing_internal';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
import {expect} from '@angular/platform-browser/testing/matchers';
import * as typed from './output_emitter_codegen_typed';
import * as untyped from './output_emitter_codegen_untyped';
import {ExternalClass, codegenStmts} from './output_emitter_util';
export function main() {
const outputDefs: any[] /** TODO #9100 */ = [];
outputDefs.push({
'getExpressions': () => interpretStatements(codegenStmts, 'getExpressions'),
'name': 'interpreted'
});
if (!getDOM().supportsDOMEvents()) {
// Our generator only works on node.js
outputDefs.push({'getExpressions': () => typed.getExpressions, 'name': 'typed'});
} else {
// Our generator only works on node.js
if (!getDOM().supportsDOMEvents()) {
outputDefs.push({'getExpressions': () => untyped.getExpressions, 'name': 'untyped'});
}
outputDefs.push({
'getExpressions': () => jitStatements('output_emitter_spec', codegenStmts, 'getExpressions'),
'name': 'jit'
});
}
describe('output emitter', () => {
outputDefs.forEach((outputDef) => {
describe(`${outputDef['name']}`, () => {
let expressions: {[k: string]: any};
beforeEach(() => { expressions = outputDef['getExpressions']()(); });
it('should support literals', () => {
expect(expressions['stringLiteral']).toEqual('Hello World!');
expect(expressions['intLiteral']).toEqual(42);
expect(expressions['boolLiteral']).toEqual(true);
expect(expressions['arrayLiteral']).toEqual([0]);
expect(expressions['mapLiteral']).toEqual({'key0': 0});
});
it('should support reading vars/keys/props', () => {
expect(expressions['readVar']).toEqual('someValue');
expect(expressions['readKey']).toEqual('someValue');
expect(expressions['readPropExternalInstance']).toEqual('someValue');
expect(expressions['readPropDynamicInstance']).toEqual('dynamicValue');
expect(expressions['readGetterDynamicInstance'])
.toEqual({'data': 'someValue', 'dynamicProp': 'dynamicValue'});
});
it('should support writing to vars / keys / props', () => {
expect(expressions['changedVar']).toEqual('changedValue');
expect(expressions['changedKey']).toEqual('changedValue');
expect(expressions['changedPropExternalInstance']).toEqual('changedValue');
expect(expressions['changedPropDynamicInstance']).toEqual('changedValue');
});
it('should support declaring functions with parameters and return', () => {
expect(expressions['fn']('someParam')).toEqual({'param': 'someParam'});
expect(expressions['closureInDynamicInstance']('someParam'))
.toEqual({'param': 'someParam', 'data': 'someValue', 'dynamicProp': 'dynamicValue'});
});
it('should support invoking functions and methods', () => {
expect(expressions['invokeFn']).toEqual({'param': 'someParam'});
expect(expressions['concatedArray']).toEqual([0, 1]);
expect(expressions['invokeMethodExternalInstance'])
.toEqual({'data': 'someValue', 'param': 'someParam'});
expect(expressions['invokeMethodExternalInstanceViaBind'])
.toEqual({'data': 'someValue', 'param': 'someParam'});
expect(expressions['invokeMethodDynamicInstance'])
.toEqual({'data': 'someValue', 'dynamicProp': 'dynamicValue', 'param': 'someParam'});
expect(expressions['invokeMethodDynamicInstanceViaBind'])
.toEqual({'data': 'someValue', 'dynamicProp': 'dynamicValue', 'param': 'someParam'});
});
it('should support conditionals', () => {
expect(expressions['conditionalTrue']).toEqual('true');
expect(expressions['conditionalFalse']).toEqual('false');
});
it('should support not', () => { expect(expressions['not']).toEqual(true); });
it('should support reading external identifiers', () => {
expect(expressions['externalTestIdentifier']).toBe(ExternalClass);
expect(expressions['externalSrcIdentifier']).toBe(EventEmitter);
expect(expressions['externalEnumIdentifier']).toBe(ViewType.HOST);
});
it('should support instantiating classes', () => {
expect(expressions['externalInstance']).toBeAnInstanceOf(ExternalClass);
// Note: toBeAnInstanceOf does not check super classes in Dart...
expect(expressions['dynamicInstance'] instanceof ExternalClass).toBe(true);
});
describe('operators', () => {
let ops: {[k: string]: Function};
let aObj: any;
let bObj: any;
beforeEach(() => {
ops = expressions['operators'];
aObj = {};
bObj = {};
});
it('should support ==', () => {
expect(ops['=='](aObj, aObj)).toBe(true);
expect(ops['=='](aObj, bObj)).toBe(false);
expect(ops['=='](1, 1)).toBe(true);
expect(ops['=='](0, 1)).toBe(false);
expect(ops['==']('a', 'a')).toBe(true);
expect(ops['==']('a', 'b')).toBe(false);
});
it('should support !=', () => {
expect(ops['!='](aObj, aObj)).toBe(false);
expect(ops['!='](aObj, bObj)).toBe(true);
expect(ops['!='](1, 1)).toBe(false);
expect(ops['!='](0, 1)).toBe(true);
expect(ops['!=']('a', 'a')).toBe(false);
expect(ops['!=']('a', 'b')).toBe(true);
});
it('should support ===', () => {
expect(ops['==='](aObj, aObj)).toBe(true);
expect(ops['==='](aObj, bObj)).toBe(false);
expect(ops['==='](1, 1)).toBe(true);
expect(ops['==='](0, 1)).toBe(false);
});
it('should support !==', () => {
expect(ops['!=='](aObj, aObj)).toBe(false);
expect(ops['!=='](aObj, bObj)).toBe(true);
expect(ops['!=='](1, 1)).toBe(false);
expect(ops['!=='](0, 1)).toBe(true);
});
it('should support -', () => { expect(ops['-'](3, 2)).toEqual(1); });
it('should support +', () => { expect(ops['+'](1, 2)).toEqual(3); });
it('should support /', () => { expect(ops['/'](6, 2)).toEqual(3); });
it('should support *', () => { expect(ops['*'](2, 3)).toEqual(6); });
it('should support %', () => { expect(ops['%'](3, 2)).toEqual(1); });
it('should support &&', () => {
expect(ops['&&'](true, true)).toBe(true);
expect(ops['&&'](true, false)).toBe(false);
});
it('should support ||', () => {
expect(ops['||'](true, false)).toBe(true);
expect(ops['||'](false, false)).toBe(false);
});
it('should support <', () => {
expect(ops['<'](1, 2)).toBe(true);
expect(ops['<'](1, 1)).toBe(false);
});
it('should support <=', () => {
expect(ops['<='](1, 2)).toBe(true);
expect(ops['<='](1, 1)).toBe(true);
});
it('should support >', () => {
expect(ops['>'](2, 1)).toBe(true);
expect(ops['>'](1, 1)).toBe(false);
});
it('should support >=', () => {
expect(ops['>='](2, 1)).toBe(true);
expect(ops['>='](1, 1)).toBe(true);
});
});
it('should support throwing errors',
() => { expect(expressions['throwError']).toThrowError('someError'); });
it('should support catching errors', () => {
function someOperation() { throw new Error('Boom!'); }
const errorAndStack = expressions['catchError'](someOperation);
expect(errorAndStack[0].message).toEqual('Boom!');
// Somehow we don't get stacktraces on ios7...
if (!browserDetection.isIOS7 && !browserDetection.isIE) {
expect(errorAndStack[1].toString()).toContain('someOperation');
}
});
});
});
});
}

View File

@ -7,7 +7,7 @@
*/ */
import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata'; import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata';
import {assetUrl} from '@angular/compiler/src/identifiers'; import {assetUrl, createIdentifier} from '@angular/compiler/src/identifiers';
import * as o from '@angular/compiler/src/output/output_ast'; import * as o from '@angular/compiler/src/output/output_ast';
import {ImportResolver} from '@angular/compiler/src/output/path_util'; import {ImportResolver} from '@angular/compiler/src/output/path_util';
import {EventEmitter} from '@angular/core'; import {EventEmitter} from '@angular/core';
@ -20,23 +20,29 @@ export class ExternalClass {
someMethod(a: any /** TODO #9100 */) { return {'param': a, 'data': this.data}; } someMethod(a: any /** TODO #9100 */) { return {'param': a, 'data': this.data}; }
} }
const testDataIdentifier = new CompileIdentifierMetadata({ const testDataIdentifier = {
name: 'ExternalClass', name: 'ExternalClass',
moduleUrl: `@angular/compiler/test/output/output_emitter_util`, moduleUrl: `@angular/compiler/test/output/output_emitter_util`,
reference: ExternalClass runtime: ExternalClass
}); };
const eventEmitterIdentifier = new CompileIdentifierMetadata( const eventEmitterIdentifier = {
{name: 'EventEmitter', moduleUrl: assetUrl('core'), reference: EventEmitter}); name: 'EventEmitter',
moduleUrl: assetUrl('core'),
runtime: EventEmitter
};
const enumIdentifier = new CompileIdentifierMetadata({ const enumIdentifier = {
name: 'ViewType.HOST', name: 'ViewType.HOST',
moduleUrl: assetUrl('core', 'linker/view_type'), moduleUrl: assetUrl('core', 'linker/view_type'),
reference: ViewType.HOST runtime: ViewType.HOST
}); };
const baseErrorIdentifier = new CompileIdentifierMetadata( const baseErrorIdentifier = {
{name: 'BaseError', moduleUrl: assetUrl('core', 'facade/errors'), reference: BaseError}); name: 'BaseError',
moduleUrl: assetUrl('core', 'facade/errors'),
runtime: BaseError
};
export var codegenExportsVars = [ export var codegenExportsVars = [
'getExpressions', 'getExpressions',
@ -58,7 +64,7 @@ const _getExpressionsStmts: o.Statement[] = [
o.variable('map').key(o.literal('changeable')).set(o.literal('changedValue')).toStmt(), o.variable('map').key(o.literal('changeable')).set(o.literal('changedValue')).toStmt(),
o.variable('externalInstance') o.variable('externalInstance')
.set(o.importExpr(testDataIdentifier).instantiate([o.literal('someValue')])) .set(o.importExpr(createIdentifier(testDataIdentifier)).instantiate([o.literal('someValue')]))
.toDeclStmt(), .toDeclStmt(),
o.variable('externalInstance').prop('changeable').set(o.literal('changedValue')).toStmt(), o.variable('externalInstance').prop('changeable').set(o.literal('changedValue')).toStmt(),
@ -69,8 +75,8 @@ const _getExpressionsStmts: o.Statement[] = [
.toDeclStmt(), .toDeclStmt(),
o.variable('throwError') o.variable('throwError')
.set(o.fn([], [new o.ThrowStmt( .set(o.fn([], [new o.ThrowStmt(o.importExpr(createIdentifier(baseErrorIdentifier))
o.importExpr(baseErrorIdentifier).instantiate([o.literal('someError')]))])) .instantiate([o.literal('someError')]))]))
.toDeclStmt(), .toDeclStmt(),
o.variable('catchError') o.variable('catchError')
@ -152,9 +158,9 @@ const _getExpressionsStmts: o.Statement[] = [
['not', o.not(o.literal(false))], ['not', o.not(o.literal(false))],
['externalTestIdentifier', o.importExpr(testDataIdentifier)], ['externalTestIdentifier', o.importExpr(createIdentifier(testDataIdentifier))],
['externalSrcIdentifier', o.importExpr(eventEmitterIdentifier)], ['externalSrcIdentifier', o.importExpr(createIdentifier(eventEmitterIdentifier))],
['externalEnumIdentifier', o.importExpr(enumIdentifier)], ['externalEnumIdentifier', o.importExpr(createIdentifier(enumIdentifier))],
['externalInstance', o.variable('externalInstance')], ['externalInstance', o.variable('externalInstance')],
['dynamicInstance', o.variable('dynamicInstance')], ['dynamicInstance', o.variable('dynamicInstance')],
@ -188,7 +194,7 @@ export var codegenStmts: o.Statement[] = [
new o.CommentStmt('This is a comment'), new o.CommentStmt('This is a comment'),
new o.ClassStmt( new o.ClassStmt(
'DynamicClass', o.importExpr(testDataIdentifier), 'DynamicClass', o.importExpr(createIdentifier(testDataIdentifier)),
[ [
new o.ClassField('dynamicProp', o.DYNAMIC_TYPE), new o.ClassField('dynamicProp', o.DYNAMIC_TYPE),
new o.ClassField('dynamicChangeable', o.DYNAMIC_TYPE), new o.ClassField('dynamicChangeable', o.DYNAMIC_TYPE),

View File

@ -8,19 +8,25 @@
import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata'; import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata';
import * as o from '@angular/compiler/src/output/output_ast'; import * as o from '@angular/compiler/src/output/output_ast';
import {ImportResolver} from '@angular/compiler/src/output/path_util';
import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter'; import {TypeScriptEmitter} from '@angular/compiler/src/output/ts_emitter';
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
import {SimpleJsImportGenerator} from './output_emitter_util';
const someModuleUrl = 'somePackage/somePath'; const someModuleUrl = 'somePackage/somePath';
const anotherModuleUrl = 'somePackage/someOtherPath'; const anotherModuleUrl = 'somePackage/someOtherPath';
const sameModuleIdentifier = const sameModuleIdentifier: CompileIdentifierMetadata = {
new CompileIdentifierMetadata({name: 'someLocalId', moduleUrl: someModuleUrl}); reference: {name: 'someLocalId', filePath: someModuleUrl}
};
const externalModuleIdentifier = const externalModuleIdentifier: CompileIdentifierMetadata = {
new CompileIdentifierMetadata({name: 'someExternalId', moduleUrl: anotherModuleUrl}); reference: {name: 'someExternalId', filePath: anotherModuleUrl}
};
class SimpleJsImportGenerator implements ImportResolver {
fileNameToModuleName(importedUrlStr: string, moduleUrlStr: string): string {
return importedUrlStr;
}
}
export function main() { export function main() {
// Note supported features of our OutputAsti n TS: // Note supported features of our OutputAsti n TS:
@ -318,6 +324,14 @@ export function main() {
].join('\n')); ].join('\n'));
}); });
it('should support expression types', () => {
expect(emitStmt(o.variable('a')
.set(o.NULL_EXPR)
.toDeclStmt(o.expressionType(
o.variable('b'), [o.expressionType(o.variable('c'))]))))
.toEqual('var a:b<c> = (null as any);');
});
it('should support combined types', () => { it('should support combined types', () => {
const writeVarExpr = o.variable('a').set(o.NULL_EXPR); const writeVarExpr = o.variable('a').set(o.NULL_EXPR);
expect(emitStmt(writeVarExpr.toDeclStmt(new o.ArrayType(null)))) expect(emitStmt(writeVarExpr.toDeclStmt(new o.ArrayType(null))))

View File

@ -0,0 +1,52 @@
/**
* @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 {PipeResolver} from '@angular/compiler/src/pipe_resolver';
import {Pipe} from '@angular/core/src/metadata';
import {stringify} from '../src/facade/lang';
@Pipe({name: 'somePipe', pure: true})
class SomePipe {
}
class SimpleClass {}
export function main() {
describe('PipeResolver', () => {
let resolver: PipeResolver;
beforeEach(() => { resolver = new PipeResolver(); });
it('should read out the metadata from the class', () => {
const moduleMetadata = resolver.resolve(SomePipe);
expect(moduleMetadata).toEqual(new Pipe({name: 'somePipe', pure: true}));
});
it('should throw when simple class has no pipe decorator', () => {
expect(() => resolver.resolve(SimpleClass))
.toThrowError(`No Pipe decorator found on ${stringify(SimpleClass)}`);
});
it('should support inheriting the metadata', function() {
@Pipe({name: 'p'})
class Parent {
}
class ChildNoDecorator extends Parent {}
@Pipe({name: 'c'})
class ChildWithDecorator extends Parent {
}
expect(resolver.resolve(ChildNoDecorator)).toEqual(new Pipe({name: 'p'}));
expect(resolver.resolve(ChildWithDecorator)).toEqual(new Pipe({name: 'c'}));
});
});
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {CompileAnimationEntryMetadata, CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeMetadata, CompilePipeSummary, CompileProviderMetadata, CompileQueryMetadata, CompileTemplateMetadata, CompileTokenMetadata, CompileTypeMetadata} from '@angular/compiler/src/compile_metadata'; import {CompileAnimationEntryMetadata, CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeMetadata, CompilePipeSummary, CompileProviderMetadata, CompileQueryMetadata, CompileTemplateMetadata, CompileTokenMetadata, CompileTypeMetadata, tokenReference} from '@angular/compiler/src/compile_metadata';
import {DomElementSchemaRegistry} from '@angular/compiler/src/schema/dom_element_schema_registry'; import {DomElementSchemaRegistry} from '@angular/compiler/src/schema/dom_element_schema_registry';
import {ElementSchemaRegistry} from '@angular/compiler/src/schema/element_schema_registry'; import {ElementSchemaRegistry} from '@angular/compiler/src/schema/element_schema_registry';
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ProviderAstType, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '@angular/compiler/src/template_parser/template_ast'; import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ProviderAstType, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '@angular/compiler/src/template_parser/template_ast';
@ -14,10 +14,9 @@ import {TEMPLATE_TRANSFORMS, TemplateParser, splitClasses} from '@angular/compil
import {TEST_COMPILER_PROVIDERS} from '@angular/compiler/testing/test_bindings'; import {TEST_COMPILER_PROVIDERS} from '@angular/compiler/testing/test_bindings';
import {SchemaMetadata, SecurityContext, Type} from '@angular/core'; import {SchemaMetadata, SecurityContext, Type} from '@angular/core';
import {Console} from '@angular/core/src/console'; import {Console} from '@angular/core/src/console';
import {TestBed} from '@angular/core/testing'; import {TestBed, inject} from '@angular/core/testing';
import {beforeEach, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
import {Identifiers, identifierToken, resolveIdentifierToken} from '../../src/identifiers'; import {Identifiers, createIdentifierToken, identifierToken} from '../../src/identifiers';
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../../src/ml_parser/interpolation_config'; import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../../src/ml_parser/interpolation_config';
import {MockSchemaRegistry} from '../../testing/index'; import {MockSchemaRegistry} from '../../testing/index';
import {unparse} from '../expression_parser/unparser'; import {unparse} from '../expression_parser/unparser';
@ -31,6 +30,11 @@ const MOCK_SCHEMA_REGISTRY = [{
['onEvent'], ['onEvent']), ['onEvent'], ['onEvent']),
}]; }];
function createTypeMeta({reference, diDeps}: {reference: any, diDeps?: any[]}):
CompileTypeMetadata {
return {reference: reference, diDeps: diDeps || [], lifecycleHooks: []};
}
export function main() { export function main() {
let ngIf: CompileDirectiveSummary; let ngIf: CompileDirectiveSummary;
let parse: ( let parse: (
@ -49,16 +53,14 @@ export function main() {
const component = CompileDirectiveMetadata.create({ const component = CompileDirectiveMetadata.create({
selector: 'root', selector: 'root',
template: someTemplate, template: someTemplate,
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'Root'}}),
{moduleUrl: someModuleUrl, name: 'Root', reference: {} as Type<any>}),
isComponent: true isComponent: true
}); });
ngIf = CompileDirectiveMetadata ngIf = CompileDirectiveMetadata
.create({ .create({
selector: '[ngIf]', selector: '[ngIf]',
template: someTemplate, template: someTemplate,
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'NgIf'}}),
{moduleUrl: someModuleUrl, name: 'NgIf', reference: {} as Type<any>}),
inputs: ['ngIf'] inputs: ['ngIf']
}) })
.toSummary(); .toSummary();
@ -302,8 +304,7 @@ export function main() {
inject([TemplateParser], (parser: TemplateParser) => { inject([TemplateParser], (parser: TemplateParser) => {
const component = CompileDirectiveMetadata.create({ const component = CompileDirectiveMetadata.create({
selector: 'test', selector: 'test',
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'Test'}}),
{moduleUrl: someModuleUrl, name: 'Test', reference: {} as Type<any>}),
isComponent: true, isComponent: true,
template: new CompileTemplateMetadata({interpolation: ['{%', '%}']}) template: new CompileTemplateMetadata({interpolation: ['{%', '%}']})
}); });
@ -470,10 +471,8 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
.create({ .create({
selector: 'div', selector: 'div',
template: new CompileTemplateMetadata({animations: animationEntries}), template: new CompileTemplateMetadata({animations: animationEntries}),
type: new CompileTypeMetadata({ type: createTypeMeta({
moduleUrl: someModuleUrl, reference: {filePath: someModuleUrl, name: 'DirA'},
name: 'DirA',
reference: {} as Type<any>
}), }),
host: {'[@prop]': 'expr'} host: {'[@prop]': 'expr'}
}) })
@ -488,8 +487,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
CompileDirectiveMetadata CompileDirectiveMetadata
.create({ .create({
selector: 'broken', selector: 'broken',
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
host: {'[class.foo]': null} host: {'[class.foo]': null}
}) })
.toSummary(); .toSummary();
@ -504,8 +502,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
CompileDirectiveMetadata CompileDirectiveMetadata
.create({ .create({
selector: 'broken', selector: 'broken',
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
host: {'(click)': null} host: {'(click)': null}
}) })
.toSummary(); .toSummary();
@ -572,11 +569,8 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
.create({ .create({
selector: 'template', selector: 'template',
outputs: ['e'], outputs: ['e'],
type: new CompileTypeMetadata({ type: createTypeMeta(
moduleUrl: someModuleUrl, {reference: {filePath: someModuleUrl, name: 'DirA'}})
name: 'DirA',
reference: {} as Type<any>
})
}) })
.toSummary(); .toSummary();
expect(humanizeTplAst(parse('<template (e)="f"></template>', [dirA]))).toEqual([ expect(humanizeTplAst(parse('<template (e)="f"></template>', [dirA]))).toEqual([
@ -614,31 +608,22 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
const dirA = CompileDirectiveMetadata const dirA = CompileDirectiveMetadata
.create({ .create({
selector: '[a]', selector: '[a]',
type: new CompileTypeMetadata({ type: createTypeMeta(
moduleUrl: someModuleUrl, {reference: {filePath: someModuleUrl, name: 'DirA'}})
name: 'DirA',
reference: {} as Type<any>
})
}) })
.toSummary(); .toSummary();
const dirB = CompileDirectiveMetadata const dirB = CompileDirectiveMetadata
.create({ .create({
selector: '[b]', selector: '[b]',
type: new CompileTypeMetadata({ type: createTypeMeta(
moduleUrl: someModuleUrl, {reference: {filePath: someModuleUrl, name: 'DirB'}})
name: 'DirB',
reference: {} as Type<any>
})
}) })
.toSummary(); .toSummary();
const dirC = CompileDirectiveMetadata const dirC = CompileDirectiveMetadata
.create({ .create({
selector: '[c]', selector: '[c]',
type: new CompileTypeMetadata({ type: createTypeMeta(
moduleUrl: someModuleUrl, {reference: {filePath: someModuleUrl, name: 'DirC'}})
name: 'DirC',
reference: {} as Type<any>
})
}) })
.toSummary(); .toSummary();
expect(humanizeTplAst(parse('<div a c b a b>', [dirA, dirB, dirC]))).toEqual([ expect(humanizeTplAst(parse('<div a c b a b>', [dirA, dirB, dirC]))).toEqual([
@ -653,16 +638,14 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
CompileDirectiveMetadata CompileDirectiveMetadata
.create({ .create({
selector: '[a=b]', selector: '[a=b]',
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}})
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>})
}) })
.toSummary(); .toSummary();
const dirB = const dirB =
CompileDirectiveMetadata CompileDirectiveMetadata
.create({ .create({
selector: '[b]', selector: '[b]',
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirB'}})
{moduleUrl: someModuleUrl, name: 'DirB', reference: {} as Type<any>})
}) })
.toSummary(); .toSummary();
expect(humanizeTplAst(parse('<div [a]="b">', [dirA, dirB]))).toEqual([ expect(humanizeTplAst(parse('<div [a]="b">', [dirA, dirB]))).toEqual([
@ -677,8 +660,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
CompileDirectiveMetadata CompileDirectiveMetadata
.create({ .create({
selector: '[a]', selector: '[a]',
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirB'}})
{moduleUrl: someModuleUrl, name: 'DirB', reference: {} as Type<any>})
}) })
.toSummary(); .toSummary();
@ -692,8 +674,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
CompileDirectiveMetadata CompileDirectiveMetadata
.create({ .create({
selector: 'div', selector: 'div',
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
host: {'[a]': 'expr'} host: {'[a]': 'expr'}
}) })
.toSummary(); .toSummary();
@ -708,8 +689,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
CompileDirectiveMetadata CompileDirectiveMetadata
.create({ .create({
selector: 'div', selector: 'div',
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
host: {'(a)': 'expr'} host: {'(a)': 'expr'}
}) })
.toSummary(); .toSummary();
@ -723,8 +703,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
CompileDirectiveMetadata CompileDirectiveMetadata
.create({ .create({
selector: 'div', selector: 'div',
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
inputs: ['aProp'] inputs: ['aProp']
}) })
.toSummary(); .toSummary();
@ -739,8 +718,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
CompileDirectiveMetadata CompileDirectiveMetadata
.create({ .create({
selector: 'div', selector: 'div',
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
inputs: ['b:a'] inputs: ['b:a']
}) })
.toSummary(); .toSummary();
@ -755,8 +733,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
CompileDirectiveMetadata CompileDirectiveMetadata
.create({ .create({
selector: 'div', selector: 'div',
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
inputs: ['a'] inputs: ['a']
}) })
.toSummary(); .toSummary();
@ -771,8 +748,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
CompileDirectiveMetadata CompileDirectiveMetadata
.create({ .create({
selector: 'div', selector: 'div',
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
inputs: ['a'] inputs: ['a']
}) })
.toSummary(); .toSummary();
@ -788,8 +764,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
CompileDirectiveMetadata CompileDirectiveMetadata
.create({ .create({
selector: 'div', selector: 'div',
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
inputs: ['a'] inputs: ['a']
}) })
.toSummary(); .toSummary();
@ -807,12 +782,9 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
let token: CompileTokenMetadata; let token: CompileTokenMetadata;
if (value.startsWith('type:')) { if (value.startsWith('type:')) {
const name = value.substring(5); const name = value.substring(5);
token = new CompileTokenMetadata({ token = {identifier: createTypeMeta({reference: <any>name})};
identifier: new CompileTypeMetadata(
{moduleUrl: someModuleUrl, name, reference: name as any as Type<any>})
});
} else { } else {
token = new CompileTokenMetadata({value: value}); token = {value: value};
} }
return token; return token;
} }
@ -833,24 +805,28 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
isHost = true; isHost = true;
value = value.substring(5); value = value.substring(5);
} }
return new CompileDiDependencyMetadata({ return {
token: createToken(value), token: createToken(value),
isOptional: isOptional, isOptional: isOptional,
isSelf: isSelf, isSelf: isSelf,
isHost: isHost isHost: isHost
}); };
} }
function createProvider( function createProvider(
token: string, {multi = false, deps = []}: {multi?: boolean, deps?: string[]} = {}): token: string, {multi = false, deps = []}: {multi?: boolean, deps?: string[]} = {}):
CompileProviderMetadata { CompileProviderMetadata {
const name = `provider${nextProviderId++}`; const name = `provider${nextProviderId++}`;
return new CompileProviderMetadata({ const compileToken = createToken(token);
token: createToken(token), return {
token: compileToken,
multi: multi, multi: multi,
useClass: new CompileTypeMetadata({name, reference: name as any as Type<any>}), useClass: createTypeMeta({reference: tokenReference(compileToken)}),
deps: deps.map(createDep) deps: deps.map(createDep),
}); useExisting: undefined,
useFactory: undefined,
useValue: undefined
};
} }
function createDir( function createDir(
@ -865,18 +841,23 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
return CompileDirectiveMetadata return CompileDirectiveMetadata
.create({ .create({
selector: selector, selector: selector,
type: new CompileTypeMetadata({ type: createTypeMeta({
moduleUrl: someModuleUrl, reference: <any>selector,
name: selector,
diDeps: deps.map(createDep), diDeps: deps.map(createDep),
reference: selector as any as Type<any>
}), }),
isComponent: isComponent, isComponent: isComponent,
template: new CompileTemplateMetadata({ngContentSelectors: []}), template: new CompileTemplateMetadata({ngContentSelectors: []}),
providers: providers, providers: providers,
viewProviders: viewProviders, viewProviders: viewProviders,
queries: queries.map( queries: queries.map((value) => {
(value) => new CompileQueryMetadata({selectors: [createToken(value)]})) return {
selectors: [createToken(value)],
descendants: false,
first: false,
propertyName: 'test',
read: undefined
};
})
}) })
.toSummary(); .toSummary();
} }
@ -1122,8 +1103,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
CompileDirectiveMetadata CompileDirectiveMetadata
.create({ .create({
selector: '[a]', selector: '[a]',
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
exportAs: 'dirA' exportAs: 'dirA'
}) })
.toSummary(); .toSummary();
@ -1170,8 +1150,7 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
.create({ .create({
selector: '[a]', selector: '[a]',
isComponent: true, isComponent: true,
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
exportAs: 'dirA', exportAs: 'dirA',
template: new CompileTemplateMetadata({ngContentSelectors: []}) template: new CompileTemplateMetadata({ngContentSelectors: []})
}) })
@ -1189,8 +1168,7 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
CompileDirectiveMetadata CompileDirectiveMetadata
.create({ .create({
selector: '[a]', selector: '[a]',
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}})
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>})
}) })
.toSummary(); .toSummary();
expect(humanizeTplAst(parse('<div ref-a>', [dirA]))).toEqual([ expect(humanizeTplAst(parse('<div ref-a>', [dirA]))).toEqual([
@ -1218,14 +1196,14 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
it('should support references via #...', () => { it('should support references via #...', () => {
expect(humanizeTplAst(parse('<template #a>', []))).toEqual([ expect(humanizeTplAst(parse('<template #a>', []))).toEqual([
[EmbeddedTemplateAst], [EmbeddedTemplateAst],
[ReferenceAst, 'a', resolveIdentifierToken(Identifiers.TemplateRef)] [ReferenceAst, 'a', createIdentifierToken(Identifiers.TemplateRef)]
]); ]);
}); });
it('should support references via ref-...', () => { it('should support references via ref-...', () => {
expect(humanizeTplAst(parse('<template ref-a>', []))).toEqual([ expect(humanizeTplAst(parse('<template ref-a>', []))).toEqual([
[EmbeddedTemplateAst], [EmbeddedTemplateAst],
[ReferenceAst, 'a', resolveIdentifierToken(Identifiers.TemplateRef)] [ReferenceAst, 'a', createIdentifierToken(Identifiers.TemplateRef)]
]); ]);
}); });
@ -1239,8 +1217,7 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
CompileDirectiveMetadata CompileDirectiveMetadata
.create({ .create({
selector: '[a]', selector: '[a]',
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}})
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>})
}) })
.toSummary(); .toSummary();
expect(humanizeTplAst(parse('<template let-a="b"></template>', [dirA]))).toEqual([ expect(humanizeTplAst(parse('<template let-a="b"></template>', [dirA]))).toEqual([
@ -1281,23 +1258,19 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
describe('directives', () => { describe('directives', () => {
it('should locate directives in property bindings', () => { it('should locate directives in property bindings', () => {
const dirA = const dirA = CompileDirectiveMetadata
CompileDirectiveMetadata
.create({ .create({
selector: '[a=b]', selector: '[a=b]',
type: new CompileTypeMetadata( type: createTypeMeta(
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}), {reference: {filePath: someModuleUrl, name: 'DirA'}}),
inputs: ['a'] inputs: ['a']
}) })
.toSummary(); .toSummary();
const dirB = CompileDirectiveMetadata const dirB = CompileDirectiveMetadata
.create({ .create({
selector: '[b]', selector: '[b]',
type: new CompileTypeMetadata({ type: createTypeMeta(
moduleUrl: someModuleUrl, {reference: {filePath: someModuleUrl, name: 'DirB'}})
name: 'DirB',
reference: {} as Type<any>
})
}) })
.toSummary(); .toSummary();
expect(humanizeTplAst(parse('<div template="a b" b>', [dirA, dirB]))).toEqual([ expect(humanizeTplAst(parse('<div template="a b" b>', [dirA, dirB]))).toEqual([
@ -1311,11 +1284,8 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
const dirA = CompileDirectiveMetadata const dirA = CompileDirectiveMetadata
.create({ .create({
selector: '[a]', selector: '[a]',
type: new CompileTypeMetadata({ type: createTypeMeta(
moduleUrl: someModuleUrl, {reference: {filePath: someModuleUrl, name: 'DirA'}})
name: 'DirA',
reference: {} as Type<any>
})
}) })
.toSummary(); .toSummary();
expect(humanizeTplAst(parse('<div template="let a=b">', [dirA]))).toEqual([ expect(humanizeTplAst(parse('<div template="let a=b">', [dirA]))).toEqual([
@ -1327,11 +1297,8 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
const dirA = CompileDirectiveMetadata const dirA = CompileDirectiveMetadata
.create({ .create({
selector: '[a]', selector: '[a]',
type: new CompileTypeMetadata({ type: createTypeMeta(
moduleUrl: someModuleUrl, {reference: {filePath: someModuleUrl, name: 'DirA'}})
name: 'DirA',
reference: {} as Type<any>
})
}) })
.toSummary(); .toSummary();
expect(humanizeTplAst(parse('<div ref-a>', [dirA]))).toEqual([ expect(humanizeTplAst(parse('<div ref-a>', [dirA]))).toEqual([
@ -1367,11 +1334,8 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
.create({ .create({
selector: selector, selector: selector,
isComponent: true, isComponent: true,
type: new CompileTypeMetadata({ type: createTypeMeta(
moduleUrl: someModuleUrl, {reference: {filePath: someModuleUrl, name: `SomeComp${compCounter++}`}}),
name: `SomeComp${compCounter++}`,
reference: {} as Type<any>
}),
template: new CompileTemplateMetadata({ngContentSelectors: ngContentSelectors}) template: new CompileTemplateMetadata({ngContentSelectors: ngContentSelectors})
}) })
.toSummary(); .toSummary();
@ -1381,11 +1345,8 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
return CompileDirectiveMetadata return CompileDirectiveMetadata
.create({ .create({
selector: selector, selector: selector,
type: new CompileTypeMetadata({ type: createTypeMeta(
moduleUrl: someModuleUrl, {reference: {filePath: someModuleUrl, name: `SomeDir${compCounter++}`}})
name: `SomeDir${compCounter++}`,
reference: {} as Type<any>
})
}) })
.toSummary(); .toSummary();
} }
@ -1559,8 +1520,7 @@ Can't bind to 'invalidProp' since it isn't a known property of 'div'. ("<div [ER
CompileDirectiveMetadata CompileDirectiveMetadata
.create({ .create({
selector: 'div', selector: 'div',
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
host: {'[invalidProp]': 'someProp'} host: {'[invalidProp]': 'someProp'}
}) })
.toSummary(); .toSummary();
@ -1579,8 +1539,7 @@ Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:5 ("<div [
CompileDirectiveMetadata CompileDirectiveMetadata
.create({ .create({
selector: 'div', selector: 'div',
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
inputs: ['invalidProp'] inputs: ['invalidProp']
}) })
.toSummary(); .toSummary();
@ -1593,8 +1552,7 @@ Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:5 ("<div [
.create({ .create({
selector: 'div', selector: 'div',
isComponent: true, isComponent: true,
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
template: new CompileTemplateMetadata({ngContentSelectors: []}) template: new CompileTemplateMetadata({ngContentSelectors: []})
}) })
.toSummary(); .toSummary();
@ -1603,8 +1561,7 @@ Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:5 ("<div [
.create({ .create({
selector: 'div', selector: 'div',
isComponent: true, isComponent: true,
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirB'}}),
{moduleUrl: someModuleUrl, name: 'DirB', reference: {} as Type<any>}),
template: new CompileTemplateMetadata({ngContentSelectors: []}) template: new CompileTemplateMetadata({ngContentSelectors: []})
}) })
.toSummary(); .toSummary();
@ -1623,8 +1580,7 @@ Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:5 ("<div [
.create({ .create({
selector: '[a]', selector: '[a]',
isComponent: true, isComponent: true,
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
template: new CompileTemplateMetadata({ngContentSelectors: []}) template: new CompileTemplateMetadata({ngContentSelectors: []})
}) })
.toSummary(); .toSummary();
@ -1641,8 +1597,7 @@ Property binding a not used by any directive on an embedded template. Make sure
.create({ .create({
selector: '[a]', selector: '[a]',
isComponent: true, isComponent: true,
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
template: new CompileTemplateMetadata({ngContentSelectors: []}) template: new CompileTemplateMetadata({ngContentSelectors: []})
}) })
.toSummary(); .toSummary();
@ -1801,8 +1756,7 @@ Property binding a not used by any directive on an embedded template. Make sure
CompileDirectiveMetadata CompileDirectiveMetadata
.create({ .create({
selector: '[a]', selector: '[a]',
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}})
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>})
}) })
.toSummary(); .toSummary();
const comp = const comp =
@ -1810,8 +1764,7 @@ Property binding a not used by any directive on an embedded template. Make sure
.create({ .create({
selector: 'div', selector: 'div',
isComponent: true, isComponent: true,
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'ZComp'}}),
{moduleUrl: someModuleUrl, name: 'ZComp', reference: {} as Type<any>}),
template: new CompileTemplateMetadata({ngContentSelectors: []}) template: new CompileTemplateMetadata({ngContentSelectors: []})
}) })
.toSummary(); .toSummary();
@ -1826,16 +1779,14 @@ Property binding a not used by any directive on an embedded template. Make sure
CompileDirectiveMetadata CompileDirectiveMetadata
.create({ .create({
selector: 'circle', selector: 'circle',
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'elDir'}})
{moduleUrl: someModuleUrl, name: 'elDir', reference: {} as Type<any>})
}) })
.toSummary(); .toSummary();
const attrSel = const attrSel = CompileDirectiveMetadata
CompileDirectiveMetadata
.create({ .create({
selector: '[href]', selector: '[href]',
type: new CompileTypeMetadata( type: createTypeMeta(
{moduleUrl: someModuleUrl, name: 'attrDir', reference: {} as Type<any>}) {reference: {filePath: someModuleUrl, name: 'attrDir'}})
}) })
.toSummary(); .toSummary();
@ -1856,8 +1807,7 @@ Property binding a not used by any directive on an embedded template. Make sure
CompileDirectiveMetadata CompileDirectiveMetadata
.create({ .create({
selector: 'div', selector: 'div',
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
inputs: ['aProp'] inputs: ['aProp']
}) })
.toSummary(); .toSummary();
@ -1873,8 +1823,7 @@ Property binding a not used by any directive on an embedded template. Make sure
CompileDirectiveMetadata CompileDirectiveMetadata
.create({ .create({
selector: 'circle', selector: 'circle',
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'elDir'}})
{moduleUrl: someModuleUrl, name: 'elDir', reference: {} as Type<any>})
}) })
.toSummary(); .toSummary();
const result = parse('<circle></circle>', [tagSel]); const result = parse('<circle></circle>', [tagSel]);
@ -1889,16 +1838,14 @@ Property binding a not used by any directive on an embedded template. Make sure
CompileDirectiveMetadata CompileDirectiveMetadata
.create({ .create({
selector: 'ul', selector: 'ul',
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'ulDir'}})
{moduleUrl: someModuleUrl, name: 'ulDir', reference: {} as Type<any>})
}) })
.toSummary(); .toSummary();
const liSel = const liSel =
CompileDirectiveMetadata CompileDirectiveMetadata
.create({ .create({
selector: 'li', selector: 'li',
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'liDir'}})
{moduleUrl: someModuleUrl, name: 'liDir', reference: {} as Type<any>})
}) })
.toSummary(); .toSummary();
const result = parse('<ul><li><li></ul>', [ulSel, liSel]); const result = parse('<ul><li><li></ul>', [ulSel, liSel]);
@ -1913,8 +1860,7 @@ Property binding a not used by any directive on an embedded template. Make sure
const testPipe = const testPipe =
new CompilePipeMetadata({ new CompilePipeMetadata({
name: 'test', name: 'test',
type: new CompileTypeMetadata( type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}})
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>})
}).toSummary(); }).toSummary();
expect(() => parse('{{a | test}}', [], [testPipe])).not.toThrow(); expect(() => parse('{{a | test}}', [], [testPipe])).not.toThrow();
}); });

View File

@ -57,7 +57,7 @@ function createCompileView(config: {className: string, parent?: CompileView, fie
config.fields.forEach((fieldName) => { fields.push(new o.ClassField(fieldName)); }); config.fields.forEach((fieldName) => { fields.push(new o.ClassField(fieldName)); });
} }
return <any>{ return <any>{
classType: o.importType(new CompileIdentifierMetadata({name: config.className})), classType: o.importType({reference: null}),
fields: fields, fields: fields,
getters: [], getters: [],
declarationElement: declarationElement declarationElement: declarationElement

View File

@ -22,5 +22,8 @@
"files": [ "files": [
"index.ts", "index.ts",
"../../../node_modules/zone.js/dist/zone.js.d.ts" "../../../node_modules/zone.js/dist/zone.js.d.ts"
] ],
"angularCompilerOptions": {
"annotateForClosureCompiler": true
}
} }

View File

@ -12,5 +12,10 @@
* Entry point for all public APIs of the core package. * Entry point for all public APIs of the core package.
*/ */
export * from './src/core'; export * from './src/core';
import {Version} from './src/core';
/**
* @stable
*/
export const VERSION = new Version('0.0.0-PLACEHOLDER');
// This file only reexports content of the `src` folder. Keep it that way. // This file only reexports content of the `src` folder. Keep it that way.

View File

@ -8,8 +8,8 @@
"author": "angular", "author": "angular",
"license": "MIT", "license": "MIT",
"peerDependencies": { "peerDependencies": {
"rxjs": "5.0.0-beta.12", "rxjs": "5.0.0-rc.4",
"zone.js": "^0.6.21" "zone.js": "^0.7.2"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

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