Compare commits
104 Commits
2.2.2
...
2.3.0-rc.0
Author | SHA1 | Date | |
---|---|---|---|
c066281bad | |||
1b9493f725 | |||
ae26504e84 | |||
d420080b3b | |||
2975d8933c | |||
43c0e9a6bb | |||
f275f36081 | |||
e628b66cca | |||
3e73bea3e7 | |||
42cf06fa12 | |||
c4bbafc291 | |||
2d6a003dba | |||
e45b7ffcd9 | |||
627282d2c8 | |||
2f7492c986 | |||
2452cd14e0 | |||
bc69c74be0 | |||
897555ca78 | |||
966bcbad5a | |||
94b8612e4e | |||
b2b72190f8 | |||
f5c8e0989d | |||
4a09251921 | |||
36caaaa8e4 | |||
808275a9d5 | |||
be3784c957 | |||
555301ce3a | |||
7194fc2b9e | |||
2a3ca7bfcf | |||
4cbf8ccf05 | |||
a6c4490fce | |||
2c02d34c05 | |||
6c2d931744 | |||
86ffa884b7 | |||
3e548de99d | |||
909268036b | |||
519a324454 | |||
ef96763fa4 | |||
7dcca307d9 | |||
491d5a22a9 | |||
44572f114f | |||
1ef4696cb7 | |||
07a986d330 | |||
59d2b4c831 | |||
2a5bd2f345 | |||
3c06a5dc25 | |||
adeea5d86a | |||
dddbb1c1cb | |||
bccf0e69dc | |||
b15039d228 | |||
2235048432 | |||
484119e59f | |||
24099bdbd2 | |||
912ca44979 | |||
664a6273e1 | |||
c1a62e2154 | |||
aac37bedc0 | |||
a3884db87c | |||
fc5ac1ebc4 | |||
ad20d7d260 | |||
602522beb2 | |||
4e047302f2 | |||
419a812f04 | |||
f340e1a414 | |||
481c9b3258 | |||
8b2dfb2eca | |||
824ea8406c | |||
1f96a93f59 | |||
009d545787 | |||
53c25210a6 | |||
927aa69726 | |||
ce89039036 | |||
42198cd7d5 | |||
d6ba092a27 | |||
773b31de8f | |||
f79b320fc4 | |||
6a212fd561 | |||
be010a292a | |||
7c36e7f956 | |||
13ba2f90b9 | |||
75277cd94b | |||
46d150266b | |||
1b5384ee54 | |||
9f7d32a326 | |||
9de76ebfa5 | |||
46023e4792 | |||
b55aaf094f | |||
d90b622fa4 | |||
79e2bb9291 | |||
efbbefd353 | |||
c2fae72bc6 | |||
7908679c4b | |||
9ed9ff40b3 | |||
2f14415836 | |||
76e4911e8b | |||
ed5e98d0df | |||
146af1fed9 | |||
c60ba7a72f | |||
05beffe0d0 | |||
08c038ebd9 | |||
582550a90d | |||
1d53a870dd | |||
a0c58a6b5c | |||
d3eff6c483 |
68
CHANGELOG.md
68
CHANGELOG.md
@ -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)
|
||||||
|
2
build.sh
2
build.sh
@ -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
|
||||||
|
@ -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/**',
|
||||||
|
@ -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"
|
||||||
|
@ -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) {
|
||||||
|
@ -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');
|
@ -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'}
|
||||||
*
|
*
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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');
|
@ -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>
|
|
@ -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],
|
||||||
|
@ -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}])
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -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"
|
||||||
},
|
},
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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"}}}`,
|
||||||
}
|
}
|
||||||
|
@ -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"],
|
||||||
|
@ -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.
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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 || [];
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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[] = [];
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -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(
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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),
|
||||||
|
@ -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;
|
||||||
}
|
}
|
@ -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);
|
||||||
|
@ -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); }
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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); }
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
@ -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));
|
||||||
|
@ -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));
|
||||||
|
}
|
||||||
}
|
}
|
@ -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});
|
|
||||||
}
|
}
|
||||||
|
@ -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.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
||||||
|
@ -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(`<`);
|
||||||
|
@ -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});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
});
|
});
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
});
|
});
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
]))
|
]))
|
||||||
|
@ -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);
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
|
@ -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]));
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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', () => {
|
||||||
|
@ -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', () => {
|
||||||
|
@ -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');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -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(
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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><i></ex></ph>with placeholders<ph name="CLOSE_ITALIC_TEXT"><ex></i></ex></ph></msg>
|
<msg id="3780349238193953556"><ph name="START_ITALIC_TEXT"><ex><i></ex></ph>with placeholders<ph name="CLOSE_ITALIC_TEXT"><ex></i></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><b></ex></ph>many<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph>}}</msg>
|
<msg id="4593805537723189714">{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<ph name="START_BOLD_TEXT"><ex><b></ex></ph>many<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph>} }</msg>
|
||||||
<msg id="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><h1></ex></ph>Markers in html comments<ph name="CLOSE_HEADING_LEVEL1"><ex></h1></ex></ph>
|
<ph name="START_HEADING_LEVEL1"><ex><h1></ex></ph>Markers in html comments<ph name="CLOSE_HEADING_LEVEL1"><ex></h1></ex></ph>
|
||||||
<ph name="START_TAG_DIV"><ex><div></ex></ph><ph name="CLOSE_TAG_DIV"><ex></div></ex></ph>
|
<ph name="START_TAG_DIV"><ex><div></ex></ph><ph name="CLOSE_TAG_DIV"><ex></div></ex></ph>
|
||||||
<ph name="START_TAG_DIV_1"><ex><div></ex></ph><ph name="ICU"/><ph name="CLOSE_TAG_DIV"><ex></div></ex></ph>
|
<ph name="START_TAG_DIV_1"><ex><div></ex></ph><ph name="ICU"/><ph name="CLOSE_TAG_DIV"><ex></div></ex></ph>
|
||||||
</msg>
|
</msg>
|
||||||
<msg id="93a30c67d4e6c9b37aecfe2ac0f2b5d366d7b520">it <ph name="START_BOLD_TEXT"><ex><b></ex></ph>should<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph> work</msg>
|
<msg id="1491627405349178954">it <ph name="START_BOLD_TEXT"><ex><b></ex></ph>should<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph> work</msg>
|
||||||
</messagebundle>`;
|
</messagebundle>
|
||||||
|
`;
|
||||||
|
@ -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[] {
|
||||||
|
@ -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 {
|
||||||
|
@ -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`)));
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
@ -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><b></ex></ph>with placeholders<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph> <ph name="INTERPOLATION"/></msg>
|
<msg id="7056919470098446707">translatable element <ph name="START_BOLD_TEXT"><ex><b></ex></ph>with placeholders<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph> <ph name="INTERPOLATION"/></msg>
|
||||||
<msg id="e2ccf3d131b15f54aa1fcf1314b1ca77c14bfcc2">{ count, plural, =0 {<ph name="START_PARAGRAPH"><ex><p></ex></ph>test<ph name="CLOSE_PARAGRAPH"><ex></p></ex></ph>} }</msg>
|
<msg id="2981514368455622387">{VAR_PLURAL, plural, =0 {<ph name="START_PARAGRAPH"><ex><p></ex></ph>test<ph name="CLOSE_PARAGRAPH"><ex></p></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><p></ex></ph>deeply nested<ph name="CLOSE_PARAGRAPH"><ex></p></ex></ph>} } } }</msg>
|
<msg id="2015957479576096115">{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<ph name="START_PARAGRAPH"><ex><p></ex></ph>deeply nested<ph name="CLOSE_PARAGRAPH"><ex></p></ex></ph>} } } }</msg>
|
||||||
</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/);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -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 {
|
||||||
|
@ -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/); });
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
110
modules/@angular/compiler/test/i18n/translation_bundle_spec.ts
Normal file
110
modules/@angular/compiler/test/i18n/translation_bundle_spec.ts
Normal 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"/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -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) => {
|
||||||
|
@ -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'}));
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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:
|
||||||
|
@ -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);
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
@ -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');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -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),
|
||||||
|
@ -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))))
|
||||||
|
52
modules/@angular/compiler/test/pipe_resolver_spec.ts
Normal file
52
modules/@angular/compiler/test/pipe_resolver_spec.ts
Normal 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'}));
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
@ -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();
|
||||||
});
|
});
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
@ -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
Reference in New Issue
Block a user