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>
|
||||
## [2.2.2](https://github.com/angular/angular/compare/2.2.1...2.2.2) (2016-11-22)
|
||||
<a name="2.3.0-rc.0"></a>
|
||||
# [2.3.0-rc.0](https://github.com/angular/angular/compare/2.3.0-beta.0...2.3.0-rc.0) (2016-11-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** blend in all previously transitioned styles into next animation if interrupted ([#13014](https://github.com/angular/angular/issues/13014)) ([ea4fc9b](https://github.com/angular/angular/commit/ea4fc9b)), closes [#13013](https://github.com/angular/angular/issues/13013)
|
||||
* **benchmarks:** use sanitized style values ([#12943](https://github.com/angular/angular/issues/12943)) ([33a7902](https://github.com/angular/angular/commit/33a7902))
|
||||
* **closure:** quote date pattern aliases ([#13012](https://github.com/angular/angular/issues/13012)) ([0956ace](https://github.com/angular/angular/commit/0956ace))
|
||||
* **compiler:** fix versions of `@angular/tsc-wrapped` ([2fe6fb1](https://github.com/angular/angular/commit/2fe6fb1))
|
||||
* **router:** add a banner file for the router ([#12919](https://github.com/angular/angular/issues/12919)) ([8df328b](https://github.com/angular/angular/commit/8df328b))
|
||||
* **router:** add a banner file for the router ([#12919](https://github.com/angular/angular/issues/12919)) ([511cd4d](https://github.com/angular/angular/commit/511cd4d))
|
||||
* **router:** removes a peer dependency from router to upgrade ([115f18f](https://github.com/angular/angular/commit/115f18f))
|
||||
* **router:** removes a peer dependency from router to upgrade ([87d5d49](https://github.com/angular/angular/commit/87d5d49))
|
||||
* **router:** support redirects to named outlets ([09226d9](https://github.com/angular/angular/commit/09226d9)), closes [#12740](https://github.com/angular/angular/issues/12740) [#9921](https://github.com/angular/angular/issues/9921)
|
||||
* **upgrade:** call ng1 lifecycle hooks ([#12875](https://github.com/angular/angular/issues/12875)) ([462316b](https://github.com/angular/angular/commit/462316b))
|
||||
* **animations:** blend in all previously transitioned styles into next animation if interrupted ([#13014](https://github.com/angular/angular/issues/13014)) ([ef96763](https://github.com/angular/angular/commit/ef96763)), closes [#13013](https://github.com/angular/angular/issues/13013)
|
||||
* **benchmarks:** use sanitized style values ([#12943](https://github.com/angular/angular/issues/12943)) ([fc5ac1e](https://github.com/angular/angular/commit/fc5ac1e))
|
||||
* **build:** update versions of umd bundles ([#13038](https://github.com/angular/angular/issues/13038)) ([86ffa88](https://github.com/angular/angular/commit/86ffa88)), closes [#13037](https://github.com/angular/angular/issues/13037)
|
||||
* **changelog:** replace beta.1 with beta.0 ([#12961](https://github.com/angular/angular/issues/12961)) ([07a986d](https://github.com/angular/angular/commit/07a986d))
|
||||
* **ci:** pin version of npm on CircleCI ([#12954](https://github.com/angular/angular/issues/12954)) ([a3884db](https://github.com/angular/angular/commit/a3884db))
|
||||
* **closure:** quote date pattern aliases ([#13012](https://github.com/angular/angular/issues/13012)) ([7dcca30](https://github.com/angular/angular/commit/7dcca30))
|
||||
* **common:** update DatePipe to allow closure compilation ([b2b7219](https://github.com/angular/angular/commit/b2b7219))
|
||||
* **compiler:** correctly evaluate references to static functions ([#13133](https://github.com/angular/angular/issues/13133)) ([627282d](https://github.com/angular/angular/commit/627282d))
|
||||
* **compiler:** fix performance regression caused by 5b0f9e2 ([43c0e9a](https://github.com/angular/angular/commit/43c0e9a)), closes [#13146](https://github.com/angular/angular/issues/13146)
|
||||
* **compiler:** fix versions of `@angular/tsc-wrapped` ([bccf0e6](https://github.com/angular/angular/commit/bccf0e6))
|
||||
* **compiler-cli:** fix paths in source maps to be relative ([2a3ca7b](https://github.com/angular/angular/commit/2a3ca7b)), closes [#13040](https://github.com/angular/angular/issues/13040)
|
||||
* **compiler-cli:** pin the version of `tsc-wrapped` ([966bcba](https://github.com/angular/angular/commit/966bcba))
|
||||
* **language-service:** harden against partial normalization of directives ([2975d89](https://github.com/angular/angular/commit/2975d89))
|
||||
* **core:** shrinkwrap was out of date with packages. ([e45b7ff](https://github.com/angular/angular/commit/e45b7ff))
|
||||
* **language-service:** make link check pass ([7194fc2](https://github.com/angular/angular/commit/7194fc2))
|
||||
* **router:** guards restore an incorrect url when used with skipLocationChange ([ad20d7d](https://github.com/angular/angular/commit/ad20d7d)), closes [#12825](https://github.com/angular/angular/issues/12825)
|
||||
* **router:** support redirects to named outlets ([602522b](https://github.com/angular/angular/commit/602522b)), closes [#12740](https://github.com/angular/angular/issues/12740) [#9921](https://github.com/angular/angular/issues/9921)
|
||||
* **tsc-wrapped:** set correct version number ([897555c](https://github.com/angular/angular/commit/897555c))
|
||||
* **tsc-wrapped:** still emit version 1 metadata to allow use of new components in old setups ([bc69c74](https://github.com/angular/angular/commit/bc69c74))
|
||||
* **upgrade:** call ng1 lifecycle hooks ([#12875](https://github.com/angular/angular/issues/12875)) ([1ef4696](https://github.com/angular/angular/commit/1ef4696))
|
||||
* **version:** take all of version string after patch version ([f275f36](https://github.com/angular/angular/commit/f275f36))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** update RxJS peer dependency to 5.0.0-rc.4 ([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>
|
||||
# [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
|
||||
router
|
||||
compiler-cli
|
||||
language-service
|
||||
benchpress)
|
||||
BUILD_ALL=true
|
||||
BUNDLE=true
|
||||
@ -175,7 +176,6 @@ do
|
||||
mv ${UMD_ES5_PATH}.tmp ${UMD_ES5_PATH}
|
||||
$UGLIFYJS -c --screw-ie8 --comments -o ${UMD_ES5_MIN_PATH} ${UMD_ES5_PATH}
|
||||
|
||||
|
||||
if [[ -e rollup-testing.config.js ]]; then
|
||||
echo "====== Rollup ${PACKAGE} testing"
|
||||
../../../node_modules/.bin/rollup -c rollup-testing.config.js
|
||||
|
@ -52,6 +52,7 @@ module.exports = function(config) {
|
||||
'dist/all/@angular/compiler-cli/**',
|
||||
'dist/all/@angular/compiler/test/aot/**',
|
||||
'dist/all/@angular/benchpress/**',
|
||||
'dist/all/@angular/language-service/**',
|
||||
'dist/all/angular1_router.js',
|
||||
'dist/all/@angular/platform-browser/testing/e2e_util.js',
|
||||
'dist/examples/**/e2e_test/**',
|
||||
|
@ -7,7 +7,7 @@
|
||||
"dependencies": {
|
||||
"@angular/core": "^2.0.0-rc.7",
|
||||
"reflect-metadata": "^0.1.2",
|
||||
"rxjs": "5.0.0-beta.12",
|
||||
"rxjs": "5.0.0-rc.4",
|
||||
"jpm": "1.1.4",
|
||||
"firefox-profile": "0.4.0",
|
||||
"selenium-webdriver": "^2.53.3"
|
||||
|
@ -6,6 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
/* tslint:disable:no-console */
|
||||
import {browser} from 'protractor';
|
||||
|
||||
const assertEventsContainsName = function(events: any[], eventName: string) {
|
||||
|
@ -14,6 +14,10 @@
|
||||
export * from './src/location';
|
||||
export {NgLocalization} from './src/localization';
|
||||
export {CommonModule} from './src/common_module';
|
||||
|
||||
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'}
|
||||
*
|
||||
* 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'}
|
||||
*
|
||||
|
@ -22,6 +22,7 @@
|
||||
"../../../node_modules/zone.js/dist/zone.js.d.ts"
|
||||
],
|
||||
"angularCompilerOptions": {
|
||||
"annotateForClosureCompiler": true,
|
||||
"strictMetadataEmit": true
|
||||
}
|
||||
}
|
||||
|
@ -5,10 +5,14 @@
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
export {AotCompilerHost, AotCompilerHost as StaticReflectorHost, StaticReflector, StaticSymbol} from '@angular/compiler';
|
||||
export {CodeGenerator} from './src/codegen';
|
||||
export {CompilerHost, CompilerHostContext, NodeCompilerHostContext} from './src/compiler_host';
|
||||
export {Extractor} from './src/extractor';
|
||||
|
||||
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 {CompWithAnalyzeEntryComponentsProvider, CompWithEntryComponents} from './entry_components';
|
||||
import {CompConsumingEvents, CompUsingPipes, CompWithProviders, CompWithReferences, DirPublishingEvents, ModuleUsingCustomElements} from './features';
|
||||
import {CompUsingRootModuleDirectiveAndPipe, SomeDirectiveInRootModule, SomePipeInRootModule, SomeService, someLibModuleWithProviders} from './module_fixtures';
|
||||
import {CompUsingRootModuleDirectiveAndPipe, SomeDirectiveInRootModule, SomeLibModule, SomePipeInRootModule, SomeService} from './module_fixtures';
|
||||
import {CompWithNgContent, ProjectingComp} from './projection';
|
||||
import {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, DirectiveForQuery} from './queries';
|
||||
|
||||
@ -52,7 +52,7 @@ import {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, Directive
|
||||
FormsModule,
|
||||
MdButtonModule,
|
||||
ModuleUsingCustomElements,
|
||||
someLibModuleWithProviders(),
|
||||
SomeLibModule.withProviders(),
|
||||
ThirdpartyModule,
|
||||
],
|
||||
providers: [SomeService],
|
||||
|
@ -63,17 +63,13 @@ export function provideValueWithEntryComponents(value: any) {
|
||||
entryComponents: [CompUsingLibModuleDirectiveAndPipe],
|
||||
})
|
||||
export class SomeLibModule {
|
||||
}
|
||||
|
||||
// TODO(tbosch): Make this a static method in `SomeLibModule` once
|
||||
// our static reflector supports it.
|
||||
// See https://github.com/angular/angular/issues/10266.
|
||||
export function someLibModuleWithProviders(): ModuleWithProviders {
|
||||
return {
|
||||
ngModule: SomeLibModule,
|
||||
providers: [
|
||||
ServiceUsingLibModule,
|
||||
provideValueWithEntryComponents([{a: 'b', component: CompUsingLibModuleDirectiveAndPipe}])
|
||||
]
|
||||
};
|
||||
static withProviders() {
|
||||
return {
|
||||
ngModule: SomeLibModule,
|
||||
providers: [
|
||||
ServiceUsingLibModule, provideValueWithEntryComponents(
|
||||
[{a: 'b', component: CompUsingLibModuleDirectiveAndPipe}])
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -34,9 +34,9 @@ const EXPECTED_XMB = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!ELEMENT ex (#PCDATA)>
|
||||
]>
|
||||
<messagebundle>
|
||||
<msg id="63a85808f03b8181e36a952e0fa38202c2304862">other-3rdP-component</msg>
|
||||
<msg id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" desc="desc" meaning="meaning">translate me</msg>
|
||||
<msg id="65cc4ab3b4c438e07c89be2b677d08369fb62da2">Welcome</msg>
|
||||
<msg id="3772663375917578720">other-3rdP-component</msg>
|
||||
<msg id="8136548302122759730" desc="desc" meaning="meaning">translate me</msg>
|
||||
<msg id="3492007542396725315">Welcome</msg>
|
||||
</messagebundle>
|
||||
`;
|
||||
|
||||
@ -79,5 +79,4 @@ describe('template i18n extraction output', () => {
|
||||
const xlf = fs.readFileSync(xlfOutput, {encoding: 'utf-8'});
|
||||
expect(xlf).toEqual(EXPECTED_XLIFF);
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -9,7 +9,7 @@
|
||||
"ng-xi18n": "./src/extract_i18n.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/tsc-wrapped": "^0.4.0",
|
||||
"@angular/tsc-wrapped": "0.4.1",
|
||||
"reflect-metadata": "^0.1.2",
|
||||
"minimist": "^1.2.0"
|
||||
},
|
||||
|
@ -44,7 +44,7 @@ export class CodeGenerator {
|
||||
let root = this.options.basePath;
|
||||
for (const eachRootDir of this.options.rootDirs || []) {
|
||||
if (this.options.trace) {
|
||||
console.log(`Check if ${filePath} is under rootDirs element ${eachRootDir}`);
|
||||
console.error(`Check if ${filePath} is under rootDirs element ${eachRootDir}`);
|
||||
}
|
||||
if (path.relative(eachRootDir, filePath).indexOf('.') !== 0) {
|
||||
root = eachRootDir;
|
||||
|
@ -186,7 +186,7 @@ export class CompilerHost implements AotCompilerHost {
|
||||
if (!v2Metadata && v1Metadata) {
|
||||
// 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
|
||||
// the metadata
|
||||
// the metadata and the `extends` clause.
|
||||
v2Metadata = {'__symbolic': 'module', 'version': 2, 'metadata': {}};
|
||||
if (v1Metadata.exports) {
|
||||
v2Metadata.exports = v1Metadata.exports;
|
||||
|
@ -41,9 +41,8 @@ function extract(
|
||||
case 'xliff':
|
||||
case 'xlf':
|
||||
default:
|
||||
const htmlParser = new compiler.I18NHtmlParser(new compiler.HtmlParser());
|
||||
ext = 'xlf';
|
||||
serializer = new compiler.Xliff(htmlParser, compiler.DEFAULT_INTERPOLATION_CONFIG);
|
||||
serializer = new compiler.Xliff();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ export class PathMappedCompilerHost extends CompilerHost {
|
||||
ts.resolveModuleName(m, rootedContainingFile, this.options, this.context).resolvedModule;
|
||||
if (resolved) {
|
||||
if (this.options.traceResolution) {
|
||||
console.log('resolve', m, containingFile, '=>', resolved.resolvedFileName);
|
||||
console.error('resolve', m, containingFile, '=>', resolved.resolvedFileName);
|
||||
}
|
||||
return this.getCanonicalFileName(resolved.resolvedFileName);
|
||||
}
|
||||
@ -71,7 +71,7 @@ export class PathMappedCompilerHost extends CompilerHost {
|
||||
*/
|
||||
fileNameToModuleName(importedFile: string, containingFile: string): string {
|
||||
if (this.options.traceResolution) {
|
||||
console.log(
|
||||
console.error(
|
||||
'getImportPath from containingFile', containingFile, 'to importedFile', importedFile);
|
||||
}
|
||||
|
||||
|
@ -163,7 +163,11 @@ describe('CompilerHost', () => {
|
||||
{__symbolic: 'module', version: 1, metadata: {foo: {__symbolic: 'class'}}}, {
|
||||
__symbolic: 'module',
|
||||
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': {
|
||||
'v1.d.ts': 'export declare class bar {}',
|
||||
'v1.d.ts': `
|
||||
export declare class Bar {
|
||||
ngOnInit() {}
|
||||
}
|
||||
export declare class BarChild extends Bar {}
|
||||
`,
|
||||
'v1.metadata.json':
|
||||
`{"__symbolic":"module", "version": 1, "metadata": {"foo": {"__symbolic": "class"}}}`,
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"lib": ["es6", "dom"],
|
||||
"noImplicitAny": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"declaration": true,
|
||||
"experimentalDecorators": true,
|
||||
"noImplicitAny": true,
|
||||
"module": "commonjs",
|
||||
"outDir": "../../../dist/packages-dist/compiler-cli",
|
||||
"paths": {
|
||||
"@angular/core": ["../../../dist/packages-dist/core"],
|
||||
"@angular/common": ["../../../dist/packages-dist/common"],
|
||||
@ -14,11 +14,11 @@
|
||||
"@angular/platform-browser": ["../../../dist/packages-dist/platform-browser"],
|
||||
"@angular/tsc-wrapped": ["../../../dist/tools/@angular/tsc-wrapped"]
|
||||
},
|
||||
"experimentalDecorators": true,
|
||||
"rootDir": ".",
|
||||
"sourceRoot": ".",
|
||||
"outDir": "../../../dist/packages-dist/compiler-cli",
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"inlineSources": true,
|
||||
"target": "es5",
|
||||
"lib": ["es6", "dom"],
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"exclude": ["integrationtest"],
|
||||
|
@ -21,6 +21,11 @@
|
||||
* </p>
|
||||
* </div>
|
||||
*/
|
||||
import {Version} from '@angular/core';
|
||||
/**
|
||||
* @stable
|
||||
*/
|
||||
export const VERSION = new Version('0.0.0-PLACEHOLDER');
|
||||
export * from './src/template_parser/template_ast';
|
||||
export {TEMPLATE_TRANSFORMS} from './src/template_parser/template_parser';
|
||||
export {CompilerConfig, RenderTypes} from './src/config';
|
||||
@ -58,5 +63,4 @@ export * from './src/style_compiler';
|
||||
export * from './src/template_parser/template_parser';
|
||||
export {ViewCompiler} from './src/view_compiler/view_compiler';
|
||||
export {AnimationParser} from './src/animation/animation_parser';
|
||||
|
||||
// This file only reexports content of the `src` folder. Keep it that way.
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {Identifiers, resolveIdentifier} from '../identifiers';
|
||||
import {Identifiers, createIdentifier} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
import {ANY_STATE, DEFAULT_STATE, EMPTY_STATE} from '../private_import_core';
|
||||
|
||||
@ -69,8 +69,8 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
stylesArr.push(o.literalMap(entries));
|
||||
});
|
||||
|
||||
return o.importExpr(resolveIdentifier(Identifiers.AnimationStyles)).instantiate([
|
||||
o.importExpr(resolveIdentifier(Identifiers.collectAndResolveStyles)).callFn([
|
||||
return o.importExpr(createIdentifier(Identifiers.AnimationStyles)).instantiate([
|
||||
o.importExpr(createIdentifier(Identifiers.collectAndResolveStyles)).callFn([
|
||||
_ANIMATION_COLLECTED_STYLES, o.literalArr(stylesArr)
|
||||
])
|
||||
]);
|
||||
@ -78,7 +78,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
|
||||
visitAnimationKeyframe(ast: AnimationKeyframeAst, context: _AnimationBuilderContext):
|
||||
o.Expression {
|
||||
return o.importExpr(resolveIdentifier(Identifiers.AnimationKeyframe)).instantiate([
|
||||
return o.importExpr(createIdentifier(Identifiers.AnimationKeyframe)).instantiate([
|
||||
o.literal(ast.offset), ast.styles.visit(this, context)
|
||||
]);
|
||||
}
|
||||
@ -100,7 +100,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
const startingStylesExpr = ast.startingStyles.visit(this, context);
|
||||
const keyframeExpressions = ast.keyframes.map(keyframe => keyframe.visit(this, context));
|
||||
const keyframesExpr =
|
||||
o.importExpr(resolveIdentifier(Identifiers.balanceAnimationKeyframes)).callFn([
|
||||
o.importExpr(createIdentifier(Identifiers.balanceAnimationKeyframes)).callFn([
|
||||
_ANIMATION_COLLECTED_STYLES, _ANIMATION_END_STATE_STYLES_VAR,
|
||||
o.literalArr(keyframeExpressions)
|
||||
]);
|
||||
@ -127,14 +127,14 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
visitAnimationSequence(ast: AnimationSequenceAst, context: _AnimationBuilderContext):
|
||||
o.Expression {
|
||||
const playerExprs = ast.steps.map(step => step.visit(this, context));
|
||||
return o.importExpr(resolveIdentifier(Identifiers.AnimationSequencePlayer)).instantiate([
|
||||
return o.importExpr(createIdentifier(Identifiers.AnimationSequencePlayer)).instantiate([
|
||||
o.literalArr(playerExprs)
|
||||
]);
|
||||
}
|
||||
|
||||
visitAnimationGroup(ast: AnimationGroupAst, context: _AnimationBuilderContext): o.Expression {
|
||||
const playerExprs = ast.steps.map(step => step.visit(this, context));
|
||||
return o.importExpr(resolveIdentifier(Identifiers.AnimationGroupPlayer)).instantiate([
|
||||
return o.importExpr(createIdentifier(Identifiers.AnimationGroupPlayer)).instantiate([
|
||||
o.literalArr(playerExprs)
|
||||
]);
|
||||
}
|
||||
@ -228,7 +228,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
_ANIMATION_END_STATE_STYLES_VAR.equals(o.NULL_EXPR),
|
||||
[_ANIMATION_END_STATE_STYLES_VAR.set(_ANIMATION_DEFAULT_STATE_VAR).toStmt()]));
|
||||
|
||||
const RENDER_STYLES_FN = o.importExpr(resolveIdentifier(Identifiers.renderStyles));
|
||||
const RENDER_STYLES_FN = o.importExpr(createIdentifier(Identifiers.renderStyles));
|
||||
|
||||
ast.stateTransitions.forEach(transAst => statements.push(transAst.visit(this, context)));
|
||||
|
||||
@ -237,7 +237,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
statements.push(new o.IfStmt(
|
||||
_ANIMATION_PLAYER_VAR.equals(o.NULL_EXPR),
|
||||
[_ANIMATION_PLAYER_VAR
|
||||
.set(o.importExpr(resolveIdentifier(Identifiers.NoOpAnimationPlayer)).instantiate([]))
|
||||
.set(o.importExpr(createIdentifier(Identifiers.NoOpAnimationPlayer)).instantiate([]))
|
||||
.toStmt()]));
|
||||
|
||||
// once complete we want to apply the styles on the element
|
||||
@ -255,17 +255,18 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
.callFn([
|
||||
_ANIMATION_FACTORY_ELEMENT_VAR, _ANIMATION_FACTORY_RENDERER_VAR,
|
||||
o.importExpr(
|
||||
resolveIdentifier(Identifiers.prepareFinalAnimationStyles))
|
||||
.callFn([
|
||||
_ANIMATION_START_STATE_STYLES_VAR,
|
||||
_ANIMATION_END_STATE_STYLES_VAR
|
||||
])
|
||||
createIdentifier(Identifiers.prepareFinalAnimationStyles))
|
||||
.callFn(
|
||||
[
|
||||
_ANIMATION_START_STATE_STYLES_VAR,
|
||||
_ANIMATION_END_STATE_STYLES_VAR
|
||||
])
|
||||
])
|
||||
.toStmt()
|
||||
])])
|
||||
.toStmt());
|
||||
|
||||
statements.push(o.importExpr(resolveIdentifier(Identifiers.AnimationSequencePlayer))
|
||||
statements.push(o.importExpr(createIdentifier(Identifiers.AnimationSequencePlayer))
|
||||
.instantiate([_PREVIOUS_ANIMATION_PLAYERS])
|
||||
.callMethod('destroy', [])
|
||||
.toStmt());
|
||||
@ -276,7 +277,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
statements.push(RENDER_STYLES_FN
|
||||
.callFn([
|
||||
_ANIMATION_FACTORY_ELEMENT_VAR, _ANIMATION_FACTORY_RENDERER_VAR,
|
||||
o.importExpr(resolveIdentifier(Identifiers.clearStyles))
|
||||
o.importExpr(createIdentifier(Identifiers.clearStyles))
|
||||
.callFn([_ANIMATION_START_STATE_STYLES_VAR])
|
||||
])
|
||||
.toStmt());
|
||||
@ -291,7 +292,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
.toStmt());
|
||||
|
||||
statements.push(new o.ReturnStatement(
|
||||
o.importExpr(resolveIdentifier(Identifiers.AnimationTransition)).instantiate([
|
||||
o.importExpr(createIdentifier(Identifiers.AnimationTransition)).instantiate([
|
||||
_ANIMATION_PLAYER_VAR, _ANIMATION_CURRENT_STATE_VAR, _ANIMATION_NEXT_STATE_VAR,
|
||||
_ANIMATION_TIME_VAR
|
||||
])));
|
||||
@ -300,12 +301,12 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
||||
[
|
||||
new o.FnParam(
|
||||
_ANIMATION_FACTORY_VIEW_VAR.name,
|
||||
o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
||||
o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
||||
new o.FnParam(_ANIMATION_FACTORY_ELEMENT_VAR.name, o.DYNAMIC_TYPE),
|
||||
new o.FnParam(_ANIMATION_CURRENT_STATE_VAR.name, o.DYNAMIC_TYPE),
|
||||
new o.FnParam(_ANIMATION_NEXT_STATE_VAR.name, o.DYNAMIC_TYPE)
|
||||
],
|
||||
statements, o.importType(resolveIdentifier(Identifiers.AnimationTransition)));
|
||||
statements, o.importType(createIdentifier(Identifiers.AnimationTransition)));
|
||||
}
|
||||
|
||||
build(ast: AnimationAst): AnimationEntryCompileResult {
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata, CompileDirectiveMetadata} from '../compile_metadata';
|
||||
import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata, CompileDirectiveMetadata, identifierName} from '../compile_metadata';
|
||||
import {StringMapWrapper} from '../facade/collection';
|
||||
import {isBlank, isPresent} from '../facade/lang';
|
||||
import {ParseError} from '../parse_util';
|
||||
@ -41,7 +41,7 @@ export class AnimationParser {
|
||||
|
||||
parseComponent(component: CompileDirectiveMetadata): AnimationEntryAst[] {
|
||||
const errors: string[] = [];
|
||||
const componentName = component.type.name;
|
||||
const componentName = identifierName(component.type);
|
||||
const animationTriggerNames = new Set<string>();
|
||||
const asts = component.template.animations.map(entry => {
|
||||
const result = this.parseEntry(entry);
|
||||
|
@ -10,11 +10,11 @@ import {SchemaMetadata} from '@angular/core';
|
||||
|
||||
import {AnimationCompiler} from '../animation/animation_compiler';
|
||||
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 {DirectiveWrapperCompileResult, DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
|
||||
import {ListWrapper} from '../facade/collection';
|
||||
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from '../identifiers';
|
||||
import {Identifiers, createIdentifier, createIdentifierToken} from '../identifiers';
|
||||
import {CompileMetadataResolver} from '../metadata_resolver';
|
||||
import {NgModuleCompiler} from '../ng_module_compiler';
|
||||
import {OutputEmitter} from '../output/abstract_emitter';
|
||||
@ -82,7 +82,7 @@ export class AotCompiler {
|
||||
const ngModule = ngModuleByPipeOrDirective.get(dirType);
|
||||
if (!ngModule) {
|
||||
throw new Error(
|
||||
`Internal Error: cannot determine the module for component ${compMeta.type.name}!`);
|
||||
`Internal Error: cannot determine the module for component ${identifierName(compMeta.type)}!`);
|
||||
}
|
||||
|
||||
_assertComponent(compMeta);
|
||||
@ -113,24 +113,24 @@ export class AotCompiler {
|
||||
const providers: CompileProviderMetadata[] = [];
|
||||
|
||||
if (this._localeId) {
|
||||
providers.push(new CompileProviderMetadata({
|
||||
token: resolveIdentifierToken(Identifiers.LOCALE_ID),
|
||||
providers.push({
|
||||
token: createIdentifierToken(Identifiers.LOCALE_ID),
|
||||
useValue: this._localeId,
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
if (this._translationFormat) {
|
||||
providers.push(new CompileProviderMetadata({
|
||||
token: resolveIdentifierToken(Identifiers.TRANSLATIONS_FORMAT),
|
||||
providers.push({
|
||||
token: createIdentifierToken(Identifiers.TRANSLATIONS_FORMAT),
|
||||
useValue: this._translationFormat
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
const appCompileResult = this._ngModuleCompiler.compile(ngModule, providers);
|
||||
|
||||
appCompileResult.dependencies.forEach((dep) => {
|
||||
dep.placeholder.name = _componentFactoryName(dep.comp);
|
||||
dep.placeholder.moduleUrl = _ngfactoryModuleUrl(dep.comp.moduleUrl);
|
||||
dep.placeholder.reference = this._staticReflector.getStaticSymbol(
|
||||
_ngfactoryModuleUrl(identifierModuleUrl(dep.comp)), _componentFactoryName(dep.comp));
|
||||
});
|
||||
|
||||
targetStatements.push(...appCompileResult.statements);
|
||||
@ -149,14 +149,17 @@ export class AotCompiler {
|
||||
private _compileComponentFactory(
|
||||
compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata, fileSuffix: 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(
|
||||
hostMeta, ngModule, [compMeta.type], null, fileSuffix, targetStatements);
|
||||
const compFactoryVar = _componentFactoryName(compMeta.type);
|
||||
targetStatements.push(
|
||||
o.variable(compFactoryVar)
|
||||
.set(o.importExpr(resolveIdentifier(Identifiers.ComponentFactory), [o.importType(
|
||||
compMeta.type)])
|
||||
.set(o.importExpr(
|
||||
createIdentifier(Identifiers.ComponentFactory), [o.importType(compMeta.type)])
|
||||
.instantiate(
|
||||
[
|
||||
o.literal(compMeta.selector),
|
||||
@ -164,7 +167,7 @@ export class AotCompiler {
|
||||
o.importExpr(compMeta.type),
|
||||
],
|
||||
o.importType(
|
||||
resolveIdentifier(Identifiers.ComponentFactory),
|
||||
createIdentifier(Identifiers.ComponentFactory),
|
||||
[o.importType(compMeta.type)], [o.TypeModifier.Const])))
|
||||
.toDeclStmt(null, [o.StmtModifier.Final]));
|
||||
return compFactoryVar;
|
||||
@ -182,23 +185,24 @@ export class AotCompiler {
|
||||
|
||||
const parsedTemplate = this._templateParser.parse(
|
||||
compMeta, compMeta.template.template, directives, pipes, ngModule.schemas,
|
||||
compMeta.type.name);
|
||||
identifierName(compMeta.type));
|
||||
const stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]);
|
||||
const compiledAnimations =
|
||||
this._animationCompiler.compile(compMeta.type.name, parsedAnimations);
|
||||
this._animationCompiler.compile(identifierName(compMeta.type), parsedAnimations);
|
||||
const viewResult = this._viewCompiler.compileComponent(
|
||||
compMeta, parsedTemplate, stylesExpr, pipes, compiledAnimations);
|
||||
if (componentStyles) {
|
||||
targetStatements.push(..._resolveStyleStatements(componentStyles, fileSuffix));
|
||||
targetStatements.push(
|
||||
..._resolveStyleStatements(this._staticReflector, componentStyles, fileSuffix));
|
||||
}
|
||||
compiledAnimations.forEach(entry => targetStatements.push(...entry.statements));
|
||||
targetStatements.push(..._resolveViewStatements(viewResult));
|
||||
targetStatements.push(..._resolveViewStatements(this._staticReflector, viewResult));
|
||||
return viewResult.viewClassVar;
|
||||
}
|
||||
|
||||
private _codgenStyles(
|
||||
fileUrl: string, stylesCompileResult: CompiledStylesheet, fileSuffix: string): SourceModule {
|
||||
_resolveStyleStatements(stylesCompileResult, fileSuffix);
|
||||
_resolveStyleStatements(this._staticReflector, stylesCompileResult, fileSuffix);
|
||||
return this._codegenSourceModule(
|
||||
fileUrl, _stylesModuleUrl(
|
||||
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) => {
|
||||
if (dep instanceof ViewClassDependency) {
|
||||
const vfd = <ViewClassDependency>dep;
|
||||
vfd.placeholder.moduleUrl = _ngfactoryModuleUrl(vfd.comp.moduleUrl);
|
||||
vfd.placeholder.reference =
|
||||
reflector.getStaticSymbol(_ngfactoryModuleUrl(identifierModuleUrl(vfd.comp)), dep.name);
|
||||
} else if (dep instanceof ComponentFactoryDependency) {
|
||||
const cfd = <ComponentFactoryDependency>dep;
|
||||
cfd.placeholder.name = _componentFactoryName(cfd.comp);
|
||||
cfd.placeholder.moduleUrl = _ngfactoryModuleUrl(cfd.comp.moduleUrl);
|
||||
cfd.placeholder.reference = reflector.getStaticSymbol(
|
||||
_ngfactoryModuleUrl(identifierModuleUrl(cfd.comp)), _componentFactoryName(cfd.comp));
|
||||
} else if (dep instanceof DirectiveWrapperDependency) {
|
||||
const dwd = <DirectiveWrapperDependency>dep;
|
||||
dwd.placeholder.moduleUrl = _ngfactoryModuleUrl(dwd.dir.moduleUrl);
|
||||
dwd.placeholder.reference =
|
||||
reflector.getStaticSymbol(_ngfactoryModuleUrl(identifierModuleUrl(dwd.dir)), dwd.name);
|
||||
}
|
||||
});
|
||||
return compileResult.statements;
|
||||
@ -233,9 +240,11 @@ function _resolveViewStatements(compileResult: ViewCompileResult): o.Statement[]
|
||||
|
||||
|
||||
function _resolveStyleStatements(
|
||||
compileResult: CompiledStylesheet, fileSuffix: string): o.Statement[] {
|
||||
reflector: StaticReflector, compileResult: CompiledStylesheet,
|
||||
fileSuffix: string): o.Statement[] {
|
||||
compileResult.dependencies.forEach((dep) => {
|
||||
dep.valuePlaceholder.moduleUrl = _stylesModuleUrl(dep.moduleUrl, dep.isShimmed, fileSuffix);
|
||||
dep.valuePlaceholder.reference = reflector.getStaticSymbol(
|
||||
_stylesModuleUrl(dep.moduleUrl, dep.isShimmed, fileSuffix), dep.name);
|
||||
});
|
||||
return compileResult.statements;
|
||||
}
|
||||
@ -246,7 +255,7 @@ function _ngfactoryModuleUrl(dirUrl: string): string {
|
||||
}
|
||||
|
||||
function _componentFactoryName(comp: CompileIdentifierMetadata): string {
|
||||
return `${comp.name}NgFactory`;
|
||||
return `${identifierName(comp)}NgFactory`;
|
||||
}
|
||||
|
||||
function _stylesModuleUrl(stylesheetUrl: string, shim: boolean, suffix: string): string {
|
||||
@ -255,7 +264,8 @@ function _stylesModuleUrl(stylesheetUrl: string, shim: boolean, suffix: string):
|
||||
|
||||
function _assertComponent(meta: CompileDirectiveMetadata) {
|
||||
if (!meta.isComponent) {
|
||||
throw new Error(`Could not compile '${meta.type.name}' because it is not a component.`);
|
||||
throw new Error(
|
||||
`Could not compile '${identifierName(meta.type)}' because it is not a component.`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -369,7 +379,7 @@ export function extractProgramSymbols(
|
||||
files.filter(fileName => _filterFileByPatterns(fileName, options)).forEach(sourceFile => {
|
||||
const moduleMetadata = staticReflector.getModuleMetadata(sourceFile);
|
||||
if (!moduleMetadata) {
|
||||
console.log(`WARNING: no metadata found for ${sourceFile}`);
|
||||
console.error(`WARNING: no metadata found for ${sourceFile}`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -70,16 +70,24 @@ export class StaticSymbolCache {
|
||||
export class StaticReflector implements ReflectorReader {
|
||||
private declarationCache = new Map<string, StaticSymbol>();
|
||||
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 methodCache = new Map<StaticSymbol, {[key: string]: boolean}>();
|
||||
private metadataCache = new Map<string, {[key: string]: any}>();
|
||||
private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>();
|
||||
private opaqueToken: StaticSymbol;
|
||||
|
||||
constructor(
|
||||
private host: StaticReflectorHost,
|
||||
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();
|
||||
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 {
|
||||
@ -99,29 +107,45 @@ export class StaticReflector implements ReflectorReader {
|
||||
public annotations(type: StaticSymbol): any[] {
|
||||
let annotations = this.annotationCache.get(type);
|
||||
if (!annotations) {
|
||||
annotations = [];
|
||||
const classMetadata = this.getTypeMetadata(type);
|
||||
if (classMetadata['extends']) {
|
||||
const parentAnnotations = this.annotations(this.simplify(type, classMetadata['extends']));
|
||||
annotations.push(...parentAnnotations);
|
||||
}
|
||||
if (classMetadata['decorators']) {
|
||||
annotations = this.simplify(type, classMetadata['decorators']);
|
||||
} else {
|
||||
annotations = [];
|
||||
const ownAnnotations: any[] = this.simplify(type, classMetadata['decorators']);
|
||||
annotations.push(...ownAnnotations);
|
||||
}
|
||||
this.annotationCache.set(type, annotations.filter(ann => !!ann));
|
||||
}
|
||||
return annotations;
|
||||
}
|
||||
|
||||
public propMetadata(type: StaticSymbol): {[key: string]: any} {
|
||||
public propMetadata(type: StaticSymbol): {[key: string]: any[]} {
|
||||
let propMetadata = this.propertyCache.get(type);
|
||||
if (!propMetadata) {
|
||||
const classMetadata = this.getTypeMetadata(type);
|
||||
const members = classMetadata ? classMetadata['members'] : {};
|
||||
propMetadata = mapStringMap(members, (propData, propName) => {
|
||||
const classMetadata = this.getTypeMetadata(type) || {};
|
||||
propMetadata = {};
|
||||
if (classMetadata['extends']) {
|
||||
const parentPropMetadata = this.propMetadata(this.simplify(type, classMetadata['extends']));
|
||||
Object.keys(parentPropMetadata).forEach((parentProp) => {
|
||||
propMetadata[parentProp] = parentPropMetadata[parentProp];
|
||||
});
|
||||
}
|
||||
|
||||
const members = classMetadata['members'] || {};
|
||||
Object.keys(members).forEach((propName) => {
|
||||
const propData = members[propName];
|
||||
const prop = (<any[]>propData)
|
||||
.find(a => a['__symbolic'] == 'property' || a['__symbolic'] == 'method');
|
||||
const decorators: any[] = [];
|
||||
if (propMetadata[propName]) {
|
||||
decorators.push(...propMetadata[propName]);
|
||||
}
|
||||
propMetadata[propName] = decorators;
|
||||
if (prop && prop['decorators']) {
|
||||
return this.simplify(type, prop['decorators']);
|
||||
} else {
|
||||
return [];
|
||||
decorators.push(...this.simplify(type, prop['decorators']));
|
||||
}
|
||||
});
|
||||
this.propertyCache.set(type, propMetadata);
|
||||
@ -155,6 +179,8 @@ export class StaticReflector implements ReflectorReader {
|
||||
}
|
||||
parameters.push(nestedResult);
|
||||
});
|
||||
} else if (classMetadata['extends']) {
|
||||
parameters = this.parameters(this.simplify(type, classMetadata['extends']));
|
||||
}
|
||||
if (!parameters) {
|
||||
parameters = [];
|
||||
@ -163,28 +189,52 @@ export class StaticReflector implements ReflectorReader {
|
||||
}
|
||||
return parameters;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
private _methodNames(type: any): {[key: string]: boolean} {
|
||||
let methodNames = this.methodCache.get(type);
|
||||
if (!methodNames) {
|
||||
const classMetadata = this.getTypeMetadata(type) || {};
|
||||
methodNames = {};
|
||||
if (classMetadata['extends']) {
|
||||
const parentMethodNames = this._methodNames(this.simplify(type, classMetadata['extends']));
|
||||
Object.keys(parentMethodNames).forEach((parentProp) => {
|
||||
methodNames[parentProp] = parentMethodNames[parentProp];
|
||||
});
|
||||
}
|
||||
|
||||
const members = classMetadata['members'] || {};
|
||||
Object.keys(members).forEach((propName) => {
|
||||
const propData = members[propName];
|
||||
const isMethod = (<any[]>propData).some(a => a['__symbolic'] == 'method');
|
||||
methodNames[propName] = methodNames[propName] || isMethod;
|
||||
});
|
||||
this.methodCache.set(type, methodNames);
|
||||
}
|
||||
return methodNames;
|
||||
}
|
||||
|
||||
hasLifecycleHook(type: any, lcProperty: string): boolean {
|
||||
if (!(type instanceof StaticSymbol)) {
|
||||
throw new Error(
|
||||
`hasLifecycleHook received ${JSON.stringify(type)} which is not a StaticSymbol`);
|
||||
}
|
||||
const classMetadata = this.getTypeMetadata(type);
|
||||
const members = classMetadata ? classMetadata['members'] : null;
|
||||
const member: any[] =
|
||||
members && members.hasOwnProperty(lcProperty) ? members[lcProperty] : null;
|
||||
return member ? member.some(a => a['__symbolic'] == 'method') : false;
|
||||
try {
|
||||
return !!this._methodNames(type)[lcProperty];
|
||||
} catch (e) {
|
||||
console.error(`Failed on type ${JSON.stringify(type)} with error ${e}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private registerDecoratorOrConstructor(type: StaticSymbol, ctor: any): void {
|
||||
private _registerDecoratorOrConstructor(type: StaticSymbol, ctor: any): void {
|
||||
this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => new ctor(...args));
|
||||
}
|
||||
|
||||
private registerFunction(type: StaticSymbol, fn: any): void {
|
||||
private _registerFunction(type: StaticSymbol, fn: any): void {
|
||||
this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => fn.apply(undefined, args));
|
||||
}
|
||||
|
||||
@ -193,50 +243,51 @@ export class StaticReflector implements ReflectorReader {
|
||||
ANGULAR_IMPORT_LOCATIONS;
|
||||
this.opaqueToken = this.findDeclaration(diOpaqueToken, 'OpaqueToken');
|
||||
|
||||
this.registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Host'), Host);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Host'), Host);
|
||||
this._registerDecoratorOrConstructor(
|
||||
this.findDeclaration(diDecorators, 'Injectable'), Injectable);
|
||||
this.registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Self'), Self);
|
||||
this.registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'SkipSelf'), SkipSelf);
|
||||
this.registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Inject'), Inject);
|
||||
this.registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Optional'), Optional);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Self'), Self);
|
||||
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'SkipSelf'), SkipSelf);
|
||||
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Inject'), Inject);
|
||||
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Optional'), Optional);
|
||||
this._registerDecoratorOrConstructor(
|
||||
this.findDeclaration(coreDecorators, 'Attribute'), Attribute);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this._registerDecoratorOrConstructor(
|
||||
this.findDeclaration(coreDecorators, 'ContentChild'), ContentChild);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this._registerDecoratorOrConstructor(
|
||||
this.findDeclaration(coreDecorators, 'ContentChildren'), ContentChildren);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this._registerDecoratorOrConstructor(
|
||||
this.findDeclaration(coreDecorators, 'ViewChild'), ViewChild);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this._registerDecoratorOrConstructor(
|
||||
this.findDeclaration(coreDecorators, 'ViewChildren'), ViewChildren);
|
||||
this.registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Input'), Input);
|
||||
this.registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Output'), Output);
|
||||
this.registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Pipe'), Pipe);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this._registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Input'), Input);
|
||||
this._registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Output'), Output);
|
||||
this._registerDecoratorOrConstructor(this.findDeclaration(coreDecorators, 'Pipe'), Pipe);
|
||||
this._registerDecoratorOrConstructor(
|
||||
this.findDeclaration(coreDecorators, 'HostBinding'), HostBinding);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this._registerDecoratorOrConstructor(
|
||||
this.findDeclaration(coreDecorators, 'HostListener'), HostListener);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this._registerDecoratorOrConstructor(
|
||||
this.findDeclaration(coreDecorators, 'Directive'), Directive);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this._registerDecoratorOrConstructor(
|
||||
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.
|
||||
this.registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Host'), Host);
|
||||
this.registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Self'), Self);
|
||||
this.registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'SkipSelf'), SkipSelf);
|
||||
this.registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Optional'), Optional);
|
||||
this._registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Host'), Host);
|
||||
this._registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Self'), Self);
|
||||
this._registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'SkipSelf'), SkipSelf);
|
||||
this._registerDecoratorOrConstructor(this.findDeclaration(diMetadata, 'Optional'), Optional);
|
||||
|
||||
this.registerFunction(this.findDeclaration(animationMetadata, 'trigger'), trigger);
|
||||
this.registerFunction(this.findDeclaration(animationMetadata, 'state'), state);
|
||||
this.registerFunction(this.findDeclaration(animationMetadata, 'transition'), transition);
|
||||
this.registerFunction(this.findDeclaration(animationMetadata, 'style'), style);
|
||||
this.registerFunction(this.findDeclaration(animationMetadata, 'animate'), animate);
|
||||
this.registerFunction(this.findDeclaration(animationMetadata, 'keyframes'), keyframes);
|
||||
this.registerFunction(this.findDeclaration(animationMetadata, 'sequence'), sequence);
|
||||
this.registerFunction(this.findDeclaration(animationMetadata, 'group'), group);
|
||||
this._registerFunction(this.findDeclaration(animationMetadata, 'trigger'), trigger);
|
||||
this._registerFunction(this.findDeclaration(animationMetadata, 'state'), state);
|
||||
this._registerFunction(this.findDeclaration(animationMetadata, 'transition'), transition);
|
||||
this._registerFunction(this.findDeclaration(animationMetadata, 'style'), style);
|
||||
this._registerFunction(this.findDeclaration(animationMetadata, 'animate'), animate);
|
||||
this._registerFunction(this.findDeclaration(animationMetadata, 'keyframes'), keyframes);
|
||||
this._registerFunction(this.findDeclaration(animationMetadata, 'sequence'), sequence);
|
||||
this._registerFunction(this.findDeclaration(animationMetadata, 'group'), group);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -333,7 +384,7 @@ export class StaticReflector implements ReflectorReader {
|
||||
|
||||
/** @internal */
|
||||
public simplify(context: StaticSymbol, value: any): any {
|
||||
const _this = this;
|
||||
const self = this;
|
||||
let scope = BindingScope.empty;
|
||||
const calling = new Map<StaticSymbol, boolean>();
|
||||
|
||||
@ -342,15 +393,15 @@ export class StaticReflector implements ReflectorReader {
|
||||
let staticSymbol: StaticSymbol;
|
||||
if (expression['module']) {
|
||||
staticSymbol =
|
||||
_this.findDeclaration(expression['module'], expression['name'], context.filePath);
|
||||
self.findDeclaration(expression['module'], expression['name'], context.filePath);
|
||||
} else {
|
||||
staticSymbol = _this.getStaticSymbol(context.filePath, expression['name']);
|
||||
staticSymbol = self.getStaticSymbol(context.filePath, expression['name']);
|
||||
}
|
||||
return staticSymbol;
|
||||
}
|
||||
|
||||
function resolveReferenceValue(staticSymbol: StaticSymbol): any {
|
||||
const moduleMetadata = _this.getModuleMetadata(staticSymbol.filePath);
|
||||
const moduleMetadata = self.getModuleMetadata(staticSymbol.filePath);
|
||||
const declarationValue =
|
||||
moduleMetadata ? moduleMetadata['metadata'][staticSymbol.name] : null;
|
||||
return declarationValue;
|
||||
@ -360,7 +411,7 @@ export class StaticReflector implements ReflectorReader {
|
||||
if (value && value.__symbolic === 'new' && value.expression) {
|
||||
const target = value.expression;
|
||||
if (target.__symbolic == 'reference') {
|
||||
return sameSymbol(resolveReference(context, target), _this.opaqueToken);
|
||||
return sameSymbol(resolveReference(context, target), self.opaqueToken);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@ -542,22 +593,26 @@ export class StaticReflector implements ReflectorReader {
|
||||
if (indexTarget && isPrimitive(index)) return indexTarget[index];
|
||||
return null;
|
||||
case 'select':
|
||||
let selectContext = context;
|
||||
let selectTarget = simplify(expression['expression']);
|
||||
if (selectTarget instanceof StaticSymbol) {
|
||||
// Access to a static instance variable
|
||||
const member: string = expression['member'];
|
||||
const members = selectTarget.members ?
|
||||
(selectTarget.members as string[]).concat(member) :
|
||||
[member];
|
||||
const declarationValue = resolveReferenceValue(selectTarget);
|
||||
selectContext =
|
||||
self.getStaticSymbol(selectTarget.filePath, selectTarget.name, members);
|
||||
if (declarationValue && declarationValue.statics) {
|
||||
selectTarget = declarationValue.statics;
|
||||
} else {
|
||||
const member: string = expression['member'];
|
||||
const members = selectTarget.members ?
|
||||
(selectTarget.members as string[]).concat(member) :
|
||||
[member];
|
||||
return _this.getStaticSymbol(selectTarget.filePath, selectTarget.name, members);
|
||||
return selectContext;
|
||||
}
|
||||
}
|
||||
const member = simplify(expression['member']);
|
||||
if (selectTarget && isPrimitive(member)) return simplify(selectTarget[member]);
|
||||
const member = simplifyInContext(selectContext, expression['member'], depth + 1);
|
||||
if (selectTarget && isPrimitive(member))
|
||||
return simplifyInContext(selectContext, selectTarget[member], depth + 1);
|
||||
return null;
|
||||
case 'reference':
|
||||
if (!expression.module) {
|
||||
@ -589,11 +644,11 @@ export class StaticReflector implements ReflectorReader {
|
||||
let target = expression['expression'];
|
||||
if (target['module']) {
|
||||
staticSymbol =
|
||||
_this.findDeclaration(target['module'], target['name'], context.filePath);
|
||||
self.findDeclaration(target['module'], target['name'], context.filePath);
|
||||
} 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) {
|
||||
let args: any[] = expression['arguments'];
|
||||
if (!args) {
|
||||
|
@ -8,11 +8,12 @@
|
||||
|
||||
import {ChangeDetectionStrategy, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core';
|
||||
|
||||
import {StaticSymbol, isStaticSymbol} from './aot/static_symbol';
|
||||
import {ListWrapper} from './facade/collection';
|
||||
import {isPresent} from './facade/lang';
|
||||
import {LifecycleHooks} from './private_import_core';
|
||||
import {isPresent, stringify} from './facade/lang';
|
||||
import {LifecycleHooks, reflector} from './private_import_core';
|
||||
import {CssSelector} from './selector';
|
||||
import {sanitizeIdentifier, splitAtColon} from './util';
|
||||
import {splitAtColon} from './util';
|
||||
|
||||
function unimplemented(): any {
|
||||
throw new Error('unimplemented');
|
||||
@ -24,10 +25,6 @@ function unimplemented(): any {
|
||||
// group 3: "@trigger" from "@trigger"
|
||||
const HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))|(\@[-\w]+)$/;
|
||||
|
||||
export abstract class CompileMetadataWithIdentifier {
|
||||
get identifier(): CompileIdentifierMetadata { return <CompileIdentifierMetadata>unimplemented(); }
|
||||
}
|
||||
|
||||
export class CompileAnimationEntryMetadata {
|
||||
constructor(
|
||||
public name: string = null, public definitions: CompileAnimationStateMetadata[] = null) {}
|
||||
@ -78,26 +75,45 @@ export class CompileAnimationGroupMetadata extends CompileAnimationWithStepsMeta
|
||||
constructor(steps: CompileAnimationMetadata[] = null) { super(steps); }
|
||||
}
|
||||
|
||||
export class CompileIdentifierMetadata implements CompileMetadataWithIdentifier {
|
||||
reference: any;
|
||||
name: string;
|
||||
prefix: string;
|
||||
moduleUrl: string;
|
||||
value: any;
|
||||
|
||||
constructor(
|
||||
{reference, name, moduleUrl, prefix, value}:
|
||||
{reference?: any, name?: string, moduleUrl?: string, prefix?: string, value?: any} = {}) {
|
||||
this.reference = reference;
|
||||
this.name = name;
|
||||
this.prefix = prefix;
|
||||
this.moduleUrl = moduleUrl;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
get identifier(): CompileIdentifierMetadata { return this; }
|
||||
function _sanitizeIdentifier(name: string): string {
|
||||
return name.replace(/\W/g, '_');
|
||||
}
|
||||
|
||||
let _anonymousTypeIndex = 0;
|
||||
|
||||
export function identifierName(compileIdentifier: CompileIdentifierMetadata): string {
|
||||
if (!compileIdentifier || !compileIdentifier.reference) {
|
||||
return null;
|
||||
}
|
||||
const ref = compileIdentifier.reference;
|
||||
if (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
|
||||
* in other modules / components. However, this data is not enough to compile
|
||||
@ -107,154 +123,65 @@ export interface CompileSummary {
|
||||
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
||||
}
|
||||
|
||||
export class CompileDiDependencyMetadata {
|
||||
isAttribute: boolean;
|
||||
isSelf: boolean;
|
||||
isHost: boolean;
|
||||
isSkipSelf: boolean;
|
||||
isOptional: boolean;
|
||||
isValue: boolean;
|
||||
token: CompileTokenMetadata;
|
||||
value: any;
|
||||
|
||||
constructor({isAttribute, isSelf, isHost, isSkipSelf, isOptional, isValue, token, value}: {
|
||||
isAttribute?: boolean,
|
||||
isSelf?: boolean,
|
||||
isHost?: boolean,
|
||||
isSkipSelf?: boolean,
|
||||
isOptional?: boolean,
|
||||
isValue?: boolean,
|
||||
query?: CompileQueryMetadata,
|
||||
viewQuery?: CompileQueryMetadata,
|
||||
token?: CompileTokenMetadata,
|
||||
value?: any
|
||||
} = {}) {
|
||||
this.isAttribute = !!isAttribute;
|
||||
this.isSelf = !!isSelf;
|
||||
this.isHost = !!isHost;
|
||||
this.isSkipSelf = !!isSkipSelf;
|
||||
this.isOptional = !!isOptional;
|
||||
this.isValue = !!isValue;
|
||||
this.token = token;
|
||||
this.value = value;
|
||||
}
|
||||
export interface CompileDiDependencyMetadata {
|
||||
isAttribute?: boolean;
|
||||
isSelf?: boolean;
|
||||
isHost?: boolean;
|
||||
isSkipSelf?: boolean;
|
||||
isOptional?: boolean;
|
||||
isValue?: boolean;
|
||||
token?: CompileTokenMetadata;
|
||||
value?: any;
|
||||
}
|
||||
|
||||
export class CompileProviderMetadata {
|
||||
export interface CompileProviderMetadata {
|
||||
token: CompileTokenMetadata;
|
||||
useClass: CompileTypeMetadata;
|
||||
useValue: any;
|
||||
useExisting: CompileTokenMetadata;
|
||||
useFactory: CompileFactoryMetadata;
|
||||
deps: CompileDiDependencyMetadata[];
|
||||
multi: boolean;
|
||||
|
||||
constructor({token, useClass, useValue, useExisting, useFactory, deps, multi}: {
|
||||
token?: CompileTokenMetadata,
|
||||
useClass?: CompileTypeMetadata,
|
||||
useValue?: any,
|
||||
useExisting?: CompileTokenMetadata,
|
||||
useFactory?: CompileFactoryMetadata,
|
||||
deps?: CompileDiDependencyMetadata[],
|
||||
multi?: boolean
|
||||
}) {
|
||||
this.token = token;
|
||||
this.useClass = useClass;
|
||||
this.useValue = useValue;
|
||||
this.useExisting = useExisting;
|
||||
this.useFactory = useFactory;
|
||||
this.deps = deps || null;
|
||||
this.multi = !!multi;
|
||||
}
|
||||
useClass?: CompileTypeMetadata;
|
||||
useValue?: any;
|
||||
useExisting?: CompileTokenMetadata;
|
||||
useFactory?: CompileFactoryMetadata;
|
||||
deps?: CompileDiDependencyMetadata[];
|
||||
multi?: boolean;
|
||||
}
|
||||
|
||||
export class CompileFactoryMetadata extends CompileIdentifierMetadata {
|
||||
export interface CompileFactoryMetadata extends CompileIdentifierMetadata {
|
||||
diDeps: CompileDiDependencyMetadata[];
|
||||
reference: any;
|
||||
}
|
||||
|
||||
constructor({reference, name, moduleUrl, prefix, diDeps, value}: {
|
||||
reference?: Function,
|
||||
name?: string,
|
||||
prefix?: string,
|
||||
moduleUrl?: string,
|
||||
value?: boolean,
|
||||
diDeps?: CompileDiDependencyMetadata[]
|
||||
}) {
|
||||
super({reference: reference, name: name, prefix: prefix, moduleUrl: moduleUrl, value: value});
|
||||
this.diDeps = _normalizeArray(diDeps);
|
||||
export function tokenName(token: CompileTokenMetadata) {
|
||||
return isPresent(token.value) ? _sanitizeIdentifier(token.value) :
|
||||
identifierName(token.identifier);
|
||||
}
|
||||
|
||||
export function tokenReference(token: CompileTokenMetadata) {
|
||||
if (isPresent(token.identifier)) {
|
||||
return token.identifier.reference;
|
||||
} else {
|
||||
return token.value;
|
||||
}
|
||||
}
|
||||
|
||||
export class CompileTokenMetadata implements CompileMetadataWithIdentifier {
|
||||
value: any;
|
||||
identifier: CompileIdentifierMetadata;
|
||||
identifierIsInstance: boolean;
|
||||
|
||||
constructor(
|
||||
{value, identifier, identifierIsInstance}:
|
||||
{value?: any, identifier?: CompileIdentifierMetadata, identifierIsInstance?: boolean}) {
|
||||
this.value = value;
|
||||
this.identifier = identifier;
|
||||
this.identifierIsInstance = !!identifierIsInstance;
|
||||
}
|
||||
|
||||
get reference(): any {
|
||||
if (isPresent(this.identifier)) {
|
||||
return this.identifier.reference;
|
||||
} else {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return isPresent(this.value) ? sanitizeIdentifier(this.value) : this.identifier.name;
|
||||
}
|
||||
export interface CompileTokenMetadata {
|
||||
value?: any;
|
||||
identifier?: CompileIdentifierMetadata|CompileTypeMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Metadata regarding compilation of a type.
|
||||
*/
|
||||
export class CompileTypeMetadata extends CompileIdentifierMetadata {
|
||||
isHost: boolean;
|
||||
export interface CompileTypeMetadata extends CompileIdentifierMetadata {
|
||||
diDeps: CompileDiDependencyMetadata[];
|
||||
lifecycleHooks: LifecycleHooks[];
|
||||
|
||||
constructor({reference, name, moduleUrl, prefix, isHost, value, diDeps, lifecycleHooks}: {
|
||||
reference?: Type<any>,
|
||||
name?: string,
|
||||
moduleUrl?: string,
|
||||
prefix?: string,
|
||||
isHost?: boolean,
|
||||
value?: any,
|
||||
diDeps?: CompileDiDependencyMetadata[],
|
||||
lifecycleHooks?: LifecycleHooks[];
|
||||
} = {}) {
|
||||
super({reference: reference, name: name, moduleUrl: moduleUrl, prefix: prefix, value: value});
|
||||
this.isHost = !!isHost;
|
||||
this.diDeps = _normalizeArray(diDeps);
|
||||
this.lifecycleHooks = _normalizeArray(lifecycleHooks);
|
||||
}
|
||||
reference: any;
|
||||
}
|
||||
|
||||
export class CompileQueryMetadata {
|
||||
export interface CompileQueryMetadata {
|
||||
selectors: Array<CompileTokenMetadata>;
|
||||
descendants: boolean;
|
||||
first: boolean;
|
||||
propertyName: string;
|
||||
read: CompileTokenMetadata;
|
||||
|
||||
constructor({selectors, descendants, first, propertyName, read}: {
|
||||
selectors?: Array<CompileTokenMetadata>,
|
||||
descendants?: boolean,
|
||||
first?: boolean,
|
||||
propertyName?: string,
|
||||
read?: CompileTokenMetadata
|
||||
} = {}) {
|
||||
this.selectors = selectors;
|
||||
this.descendants = !!descendants;
|
||||
this.first = !!first;
|
||||
this.propertyName = propertyName;
|
||||
this.read = read;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -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 {
|
||||
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
||||
type: CompileTypeMetadata;
|
||||
@ -355,10 +284,11 @@ export interface CompileDirectiveSummary extends CompileSummary {
|
||||
/**
|
||||
* Metadata regarding compilation of a directive.
|
||||
*/
|
||||
export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||
export class CompileDirectiveMetadata {
|
||||
static create(
|
||||
{type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host, providers,
|
||||
viewProviders, queries, viewQueries, entryComponents, template}: {
|
||||
{isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host,
|
||||
providers, viewProviders, queries, viewQueries, entryComponents, template}: {
|
||||
isHost?: boolean,
|
||||
type?: CompileTypeMetadata,
|
||||
isComponent?: boolean,
|
||||
selector?: string,
|
||||
@ -367,10 +297,8 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||
inputs?: string[],
|
||||
outputs?: string[],
|
||||
host?: {[key: string]: string},
|
||||
providers?:
|
||||
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
||||
viewProviders?:
|
||||
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
||||
providers?: CompileProviderMetadata[],
|
||||
viewProviders?: CompileProviderMetadata[],
|
||||
queries?: CompileQueryMetadata[],
|
||||
viewQueries?: CompileQueryMetadata[],
|
||||
entryComponents?: CompileIdentifierMetadata[],
|
||||
@ -412,6 +340,7 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||
}
|
||||
|
||||
return new CompileDirectiveMetadata({
|
||||
isHost,
|
||||
type,
|
||||
isComponent: !!isComponent, selector, exportAs, changeDetection,
|
||||
inputs: inputsMap,
|
||||
@ -427,6 +356,7 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||
template,
|
||||
});
|
||||
}
|
||||
isHost: boolean;
|
||||
type: CompileTypeMetadata;
|
||||
isComponent: boolean;
|
||||
selector: string;
|
||||
@ -446,9 +376,10 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||
template: CompileTemplateMetadata;
|
||||
|
||||
constructor(
|
||||
{type, isComponent, selector, exportAs, changeDetection, inputs, outputs, hostListeners,
|
||||
hostProperties, hostAttributes, providers, viewProviders, queries, viewQueries,
|
||||
entryComponents, template}: {
|
||||
{isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs,
|
||||
hostListeners, hostProperties, hostAttributes, providers, viewProviders, queries,
|
||||
viewQueries, entryComponents, template}: {
|
||||
isHost?: boolean,
|
||||
type?: CompileTypeMetadata,
|
||||
isComponent?: boolean,
|
||||
selector?: string,
|
||||
@ -459,15 +390,14 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||
hostListeners?: {[key: string]: string},
|
||||
hostProperties?: {[key: string]: string},
|
||||
hostAttributes?: {[key: string]: string},
|
||||
providers?:
|
||||
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
||||
viewProviders?:
|
||||
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
||||
providers?: CompileProviderMetadata[],
|
||||
viewProviders?: CompileProviderMetadata[],
|
||||
queries?: CompileQueryMetadata[],
|
||||
viewQueries?: CompileQueryMetadata[],
|
||||
entryComponents?: CompileIdentifierMetadata[],
|
||||
template?: CompileTemplateMetadata,
|
||||
} = {}) {
|
||||
this.isHost = !!isHost;
|
||||
this.type = type;
|
||||
this.isComponent = isComponent;
|
||||
this.selector = selector;
|
||||
@ -487,8 +417,6 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
get identifier(): CompileIdentifierMetadata { return this.type; }
|
||||
|
||||
toSummary(): CompileDirectiveSummary {
|
||||
return {
|
||||
isSummary: true,
|
||||
@ -514,16 +442,12 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
|
||||
/**
|
||||
* Construct {@link CompileDirectiveMetadata} from {@link ComponentTypeMetadata} and a selector.
|
||||
*/
|
||||
export function createHostComponentMeta(compMeta: CompileDirectiveMetadata):
|
||||
CompileDirectiveMetadata {
|
||||
export function createHostComponentMeta(
|
||||
typeReference: any, compMeta: CompileDirectiveMetadata): CompileDirectiveMetadata {
|
||||
const template = CssSelector.parse(compMeta.selector)[0].getMatchingElementTemplate();
|
||||
return CompileDirectiveMetadata.create({
|
||||
type: new CompileTypeMetadata({
|
||||
reference: Object,
|
||||
name: `${compMeta.type.name}_Host`,
|
||||
moduleUrl: compMeta.type.moduleUrl,
|
||||
isHost: true
|
||||
}),
|
||||
isHost: true,
|
||||
type: {reference: typeReference, diDeps: [], lifecycleHooks: []},
|
||||
template: new CompileTemplateMetadata({
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
template: template,
|
||||
@ -553,7 +477,7 @@ export interface CompilePipeSummary extends CompileSummary {
|
||||
pure: boolean;
|
||||
}
|
||||
|
||||
export class CompilePipeMetadata implements CompileMetadataWithIdentifier {
|
||||
export class CompilePipeMetadata {
|
||||
type: CompileTypeMetadata;
|
||||
name: string;
|
||||
pure: boolean;
|
||||
@ -567,13 +491,14 @@ export class CompilePipeMetadata implements CompileMetadataWithIdentifier {
|
||||
this.name = name;
|
||||
this.pure = !!pure;
|
||||
}
|
||||
get identifier(): CompileIdentifierMetadata { return this.type; }
|
||||
|
||||
toSummary(): CompilePipeSummary {
|
||||
return {isSummary: true, type: this.type, name: this.name, pure: this.pure};
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
||||
type: CompileTypeMetadata;
|
||||
@ -583,6 +508,8 @@ export interface CompileNgModuleInjectorSummary extends CompileSummary {
|
||||
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 {
|
||||
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
|
||||
type: CompileTypeMetadata;
|
||||
@ -592,13 +519,15 @@ export interface CompileNgModuleDirectiveSummary extends CompileSummary {
|
||||
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 =
|
||||
CompileNgModuleInjectorSummary & CompileNgModuleDirectiveSummary;
|
||||
|
||||
/**
|
||||
* Metadata regarding compilation of a module.
|
||||
*/
|
||||
export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
||||
export class CompileNgModuleMetadata {
|
||||
type: CompileTypeMetadata;
|
||||
declaredDirectives: CompileIdentifierMetadata[];
|
||||
exportedDirectives: CompileIdentifierMetadata[];
|
||||
@ -620,8 +549,7 @@ export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
||||
entryComponents, bootstrapComponents, importedModules, exportedModules, schemas,
|
||||
transitiveModule, id}: {
|
||||
type?: CompileTypeMetadata,
|
||||
providers?:
|
||||
Array<CompileProviderMetadata|CompileTypeMetadata|CompileIdentifierMetadata|any[]>,
|
||||
providers?: CompileProviderMetadata[],
|
||||
declaredDirectives?: CompileIdentifierMetadata[],
|
||||
exportedDirectives?: CompileIdentifierMetadata[],
|
||||
declaredPipes?: CompileIdentifierMetadata[],
|
||||
@ -649,8 +577,6 @@ export class CompileNgModuleMetadata implements CompileMetadataWithIdentifier {
|
||||
this.transitiveModule = transitiveModule;
|
||||
}
|
||||
|
||||
get identifier(): CompileIdentifierMetadata { return this.type; }
|
||||
|
||||
toSummary(): CompileNgModuleSummary {
|
||||
return {
|
||||
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[] {
|
||||
return obj || [];
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Identifiers, resolveIdentifier} from '../identifiers';
|
||||
import {Identifiers, createIdentifier} from '../identifiers';
|
||||
import {ClassBuilder} from '../output/class_builder';
|
||||
import * as o from '../output/output_ast';
|
||||
|
||||
@ -22,7 +22,7 @@ export function createCheckBindingField(builder: ClassBuilder): CheckBindingFiel
|
||||
// private is fine here as no child view will reference the cached value...
|
||||
builder.fields.push(new o.ClassField(fieldExpr.name, null, [o.StmtModifier.Private]));
|
||||
builder.ctorStmts.push(o.THIS_EXPR.prop(fieldExpr.name)
|
||||
.set(o.importExpr(resolveIdentifier(Identifiers.UNINITIALIZED)))
|
||||
.set(o.importExpr(createIdentifier(Identifiers.UNINITIALIZED)))
|
||||
.toStmt());
|
||||
return new CheckBindingField(fieldExpr, bindingId);
|
||||
}
|
||||
@ -30,7 +30,7 @@ export function createCheckBindingField(builder: ClassBuilder): CheckBindingFiel
|
||||
export function createCheckBindingStmt(
|
||||
evalResult: ConvertPropertyBindingResult, fieldExpr: o.ReadPropExpr,
|
||||
throwOnChangeVar: o.Expression, actions: o.Statement[]): o.Statement[] {
|
||||
let condition: o.Expression = o.importExpr(resolveIdentifier(Identifiers.checkBinding)).callFn([
|
||||
let condition: o.Expression = o.importExpr(createIdentifier(Identifiers.checkBinding)).callFn([
|
||||
throwOnChangeVar, fieldExpr, evalResult.currValExpr
|
||||
]);
|
||||
if (evalResult.forceUpdate) {
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
import * as cdAst from '../expression_parser/ast';
|
||||
import {isBlank, isPresent} from '../facade/lang';
|
||||
import {Identifiers, resolveIdentifier} from '../identifiers';
|
||||
import {Identifiers, createIdentifier} from '../identifiers';
|
||||
import {ClassBuilder} from '../output/class_builder';
|
||||
import * as o from '../output/output_ast';
|
||||
|
||||
@ -116,7 +116,7 @@ export function createSharedBindingVariablesIfNeeded(stmts: o.Statement[]): o.St
|
||||
if (readVars.has(VAL_UNWRAPPER_VAR.name)) {
|
||||
unwrapperStmts.push(
|
||||
VAL_UNWRAPPER_VAR
|
||||
.set(o.importExpr(resolveIdentifier(Identifiers.ValueUnwrapper)).instantiate([]))
|
||||
.set(o.importExpr(createIdentifier(Identifiers.ValueUnwrapper)).instantiate([]))
|
||||
.toDeclStmt(null, [o.StmtModifier.Final]));
|
||||
}
|
||||
return unwrapperStmts;
|
||||
@ -277,8 +277,8 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
||||
args.push(o.literal(ast.strings[ast.strings.length - 1]));
|
||||
|
||||
return ast.expressions.length <= 9 ?
|
||||
o.importExpr(resolveIdentifier(Identifiers.inlineInterpolate)).callFn(args) :
|
||||
o.importExpr(resolveIdentifier(Identifiers.interpolate)).callFn([
|
||||
o.importExpr(createIdentifier(Identifiers.inlineInterpolate)).callFn(args) :
|
||||
o.importExpr(createIdentifier(Identifiers.interpolate)).callFn([
|
||||
args[0], o.literalArr(args.slice(1))
|
||||
]);
|
||||
}
|
||||
@ -578,7 +578,7 @@ function flattenStatements(arg: any, output: o.Statement[]) {
|
||||
|
||||
function createCachedLiteralArray(builder: ClassBuilder, values: o.Expression[]): o.Expression {
|
||||
if (values.length === 0) {
|
||||
return o.importExpr(resolveIdentifier(Identifiers.EMPTY_ARRAY));
|
||||
return o.importExpr(createIdentifier(Identifiers.EMPTY_ARRAY));
|
||||
}
|
||||
const proxyExpr = o.THIS_EXPR.prop(`_arr_${builder.fields.length}`);
|
||||
const proxyParams: o.FnParam[] = [];
|
||||
@ -599,7 +599,7 @@ function createCachedLiteralArray(builder: ClassBuilder, values: o.Expression[])
|
||||
function createCachedLiteralMap(
|
||||
builder: ClassBuilder, entries: [string, o.Expression][]): o.Expression {
|
||||
if (entries.length === 0) {
|
||||
return o.importExpr(resolveIdentifier(Identifiers.EMPTY_MAP));
|
||||
return o.importExpr(createIdentifier(Identifiers.EMPTY_MAP));
|
||||
}
|
||||
const proxyExpr = o.THIS_EXPR.prop(`_map_${builder.fields.length}`);
|
||||
const proxyParams: o.FnParam[] = [];
|
||||
|
@ -8,15 +8,12 @@
|
||||
|
||||
import {CompileTokenMetadata} from '../compile_metadata';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {IdentifierSpec, Identifiers, resolveEnumIdentifier, resolveIdentifier} from '../identifiers';
|
||||
import {IdentifierSpec, Identifiers, createEnumIdentifier, createIdentifier} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
|
||||
export function createDiTokenExpression(token: CompileTokenMetadata): o.Expression {
|
||||
if (isPresent(token.value)) {
|
||||
return o.literal(token.value);
|
||||
} else if (token.identifierIsInstance) {
|
||||
return o.importExpr(token.identifier)
|
||||
.instantiate([], o.importType(token.identifier, [], [o.TypeModifier.Const]));
|
||||
} else {
|
||||
return o.importExpr(token.identifier);
|
||||
}
|
||||
@ -24,13 +21,13 @@ export function createDiTokenExpression(token: CompileTokenMetadata): o.Expressi
|
||||
|
||||
export function createInlineArray(values: o.Expression[]): o.Expression {
|
||||
if (values.length === 0) {
|
||||
return o.importExpr(resolveIdentifier(Identifiers.EMPTY_INLINE_ARRAY));
|
||||
return o.importExpr(createIdentifier(Identifiers.EMPTY_INLINE_ARRAY));
|
||||
}
|
||||
const log2 = Math.log(values.length) / Math.log(2);
|
||||
const index = Math.ceil(log2);
|
||||
const identifierSpec = index < Identifiers.inlineArrays.length ? Identifiers.inlineArrays[index] :
|
||||
Identifiers.InlineArrayDynamic;
|
||||
const identifier = resolveIdentifier(identifierSpec);
|
||||
const identifier = createIdentifier(identifierSpec);
|
||||
return o.importExpr(identifier).instantiate([
|
||||
<o.Expression>o.literal(values.length)
|
||||
].concat(values));
|
||||
@ -46,7 +43,7 @@ export function createPureProxy(
|
||||
throw new Error(`Unsupported number of argument for pure functions: ${argCount}`);
|
||||
}
|
||||
builder.ctorStmts.push(o.THIS_EXPR.prop(pureProxyProp.name)
|
||||
.set(o.importExpr(resolveIdentifier(pureProxyId)).callFn([fn]))
|
||||
.set(o.importExpr(createIdentifier(pureProxyId)).callFn([fn]))
|
||||
.toStmt());
|
||||
}
|
||||
|
||||
@ -56,5 +53,5 @@ export function createEnumExpression(enumType: IdentifierSpec, enumValue: any):
|
||||
if (!enumName) {
|
||||
throw new Error(`Unknown enum value ${enumValue} in ${enumType.name}`);
|
||||
}
|
||||
return o.importExpr(resolveEnumIdentifier(resolveIdentifier(enumType), enumName));
|
||||
return o.importExpr(createEnumIdentifier(enumType, enumName));
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
import {SecurityContext} from '@angular/core';
|
||||
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {Identifiers, resolveIdentifier} from '../identifiers';
|
||||
import {Identifiers, createIdentifier} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
import {EMPTY_STATE as EMPTY_ANIMATION_STATE} from '../private_import_core';
|
||||
import {BoundElementPropertyAst, BoundEventAst, PropertyBindingType} from '../template_parser/template_ast';
|
||||
@ -26,7 +26,7 @@ export function writeToRenderer(
|
||||
case PropertyBindingType.Property:
|
||||
if (logBindingUpdate) {
|
||||
updateStmts.push(
|
||||
o.importExpr(resolveIdentifier(Identifiers.setBindingDebugInfo))
|
||||
o.importExpr(createIdentifier(Identifiers.setBindingDebugInfo))
|
||||
.callFn([renderer, renderElement, o.literal(boundProp.name), renderValue])
|
||||
.toStmt());
|
||||
}
|
||||
@ -104,7 +104,7 @@ export function triggerAnimation(
|
||||
// it's important to normalize the void value as `void` explicitly
|
||||
// so that the styles data can be obtained from the stringmap
|
||||
const emptyStateValue = o.literal(EMPTY_ANIMATION_STATE);
|
||||
const unitializedValue = o.importExpr(resolveIdentifier(Identifiers.UNINITIALIZED));
|
||||
const unitializedValue = o.importExpr(createIdentifier(Identifiers.UNINITIALIZED));
|
||||
const animationTransitionVar = o.variable('animationTransition_' + animationName);
|
||||
|
||||
updateStmts.push(
|
||||
|
@ -9,7 +9,7 @@
|
||||
import {ViewEncapsulation, isDevMode} from '@angular/core';
|
||||
|
||||
import {CompileIdentifierMetadata} from './compile_metadata';
|
||||
import {Identifiers, resolveIdentifier} from './identifiers';
|
||||
import {Identifiers, createIdentifier} from './identifiers';
|
||||
|
||||
function unimplemented(): any {
|
||||
throw new Error('unimplemented');
|
||||
@ -61,7 +61,7 @@ export abstract class RenderTypes {
|
||||
}
|
||||
|
||||
export class DefaultRenderTypes implements RenderTypes {
|
||||
get renderer() { return resolveIdentifier(Identifiers.Renderer); };
|
||||
get renderer() { return createIdentifier(Identifiers.Renderer); };
|
||||
renderText: any = null;
|
||||
renderElement: any = null;
|
||||
renderComment: any = null;
|
||||
|
@ -8,11 +8,12 @@
|
||||
|
||||
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 {ReflectorReader, reflector} from './private_import_core';
|
||||
import {splitAtColon} from './util';
|
||||
|
||||
|
||||
/*
|
||||
* Resolve a `Type` for {@link Directive}.
|
||||
*
|
||||
@ -35,7 +36,7 @@ export class DirectiveResolver {
|
||||
resolve(type: Type<any>, throwIfNotFound = true): Directive {
|
||||
const typeMetadata = this._reflector.annotations(resolveForwardRef(type));
|
||||
if (typeMetadata) {
|
||||
const metadata = typeMetadata.find(isDirectiveMetadata);
|
||||
const metadata = ListWrapper.findLast(typeMetadata, isDirectiveMetadata);
|
||||
if (metadata) {
|
||||
const propertyMetadata = this._reflector.propMetadata(type);
|
||||
return this._mergeWithPropertyMetadata(metadata, propertyMetadata, type);
|
||||
@ -58,85 +59,76 @@ export class DirectiveResolver {
|
||||
const queries: {[key: string]: any} = {};
|
||||
|
||||
Object.keys(propertyMetadata).forEach((propName: string) => {
|
||||
|
||||
propertyMetadata[propName].forEach(a => {
|
||||
if (a instanceof Input) {
|
||||
if (a.bindingPropertyName) {
|
||||
inputs.push(`${propName}: ${a.bindingPropertyName}`);
|
||||
} else {
|
||||
inputs.push(propName);
|
||||
}
|
||||
} else if (a instanceof Output) {
|
||||
const output: Output = a;
|
||||
if (output.bindingPropertyName) {
|
||||
outputs.push(`${propName}: ${output.bindingPropertyName}`);
|
||||
} else {
|
||||
outputs.push(propName);
|
||||
}
|
||||
} else if (a instanceof HostBinding) {
|
||||
const hostBinding: HostBinding = a;
|
||||
if (hostBinding.hostPropertyName) {
|
||||
const startWith = hostBinding.hostPropertyName[0];
|
||||
if (startWith === '(') {
|
||||
throw new Error(`@HostBinding can not bind to events. Use @HostListener instead.`);
|
||||
} else if (startWith === '[') {
|
||||
throw new Error(
|
||||
`@HostBinding parameter should be a property name, 'class.<name>', or 'attr.<name>'.`);
|
||||
}
|
||||
host[`[${hostBinding.hostPropertyName}]`] = propName;
|
||||
} else {
|
||||
host[`[${propName}]`] = propName;
|
||||
}
|
||||
} else if (a instanceof HostListener) {
|
||||
const hostListener: HostListener = a;
|
||||
const args = hostListener.args || [];
|
||||
host[`(${hostListener.eventName})`] = `${propName}(${args.join(',')})`;
|
||||
} else if (a instanceof Query) {
|
||||
queries[propName] = a;
|
||||
const input = ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof Input);
|
||||
if (input) {
|
||||
if (input.bindingPropertyName) {
|
||||
inputs.push(`${propName}: ${input.bindingPropertyName}`);
|
||||
} else {
|
||||
inputs.push(propName);
|
||||
}
|
||||
});
|
||||
}
|
||||
const output = ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof Output);
|
||||
if (output) {
|
||||
if (output.bindingPropertyName) {
|
||||
outputs.push(`${propName}: ${output.bindingPropertyName}`);
|
||||
} else {
|
||||
outputs.push(propName);
|
||||
}
|
||||
}
|
||||
const hostBinding =
|
||||
ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof HostBinding);
|
||||
if (hostBinding) {
|
||||
if (hostBinding.hostPropertyName) {
|
||||
const startWith = hostBinding.hostPropertyName[0];
|
||||
if (startWith === '(') {
|
||||
throw new Error(`@HostBinding can not bind to events. Use @HostListener instead.`);
|
||||
} else if (startWith === '[') {
|
||||
throw new Error(
|
||||
`@HostBinding parameter should be a property name, 'class.<name>', or 'attr.<name>'.`);
|
||||
}
|
||||
host[`[${hostBinding.hostPropertyName}]`] = propName;
|
||||
} else {
|
||||
host[`[${propName}]`] = propName;
|
||||
}
|
||||
}
|
||||
const hostListener =
|
||||
ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof HostListener);
|
||||
if (hostListener) {
|
||||
const args = hostListener.args || [];
|
||||
host[`(${hostListener.eventName})`] = `${propName}(${args.join(',')})`;
|
||||
}
|
||||
const query = ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof Query);
|
||||
if (query) {
|
||||
queries[propName] = query;
|
||||
}
|
||||
});
|
||||
return this._merge(dm, inputs, outputs, host, queries, directiveType);
|
||||
}
|
||||
|
||||
private _extractPublicName(def: string) { return splitAtColon(def, [null, def])[1].trim(); }
|
||||
|
||||
private _dedupeBindings(bindings: string[]): string[] {
|
||||
const names = new Set<string>();
|
||||
const reversedResult: string[] = [];
|
||||
// go last to first to allow later entries to overwrite previous entries
|
||||
for (let i = bindings.length - 1; i >= 0; i--) {
|
||||
const binding = bindings[i];
|
||||
const name = this._extractPublicName(binding);
|
||||
if (!names.has(name)) {
|
||||
names.add(name);
|
||||
reversedResult.push(binding);
|
||||
}
|
||||
}
|
||||
return reversedResult.reverse();
|
||||
}
|
||||
|
||||
private _merge(
|
||||
directive: Directive, inputs: string[], outputs: string[], host: {[key: string]: string},
|
||||
queries: {[key: string]: any}, directiveType: Type<any>): Directive {
|
||||
const mergedInputs: string[] = inputs;
|
||||
|
||||
if (directive.inputs) {
|
||||
const inputNames: string[] =
|
||||
directive.inputs.map((def: string): string => this._extractPublicName(def));
|
||||
|
||||
inputs.forEach((inputDef: string) => {
|
||||
const publicName = this._extractPublicName(inputDef);
|
||||
if (inputNames.indexOf(publicName) > -1) {
|
||||
throw new Error(
|
||||
`Input '${publicName}' defined multiple times in '${stringify(directiveType)}'`);
|
||||
}
|
||||
});
|
||||
|
||||
mergedInputs.unshift(...directive.inputs);
|
||||
}
|
||||
|
||||
const mergedOutputs: string[] = outputs;
|
||||
|
||||
if (directive.outputs) {
|
||||
const outputNames: string[] =
|
||||
directive.outputs.map((def: string): string => this._extractPublicName(def));
|
||||
|
||||
outputs.forEach((outputDef: string) => {
|
||||
const publicName = this._extractPublicName(outputDef);
|
||||
if (outputNames.indexOf(publicName) > -1) {
|
||||
throw new Error(
|
||||
`Output event '${publicName}' defined multiple times in '${stringify(directiveType)}'`);
|
||||
}
|
||||
});
|
||||
mergedOutputs.unshift(...directive.outputs);
|
||||
}
|
||||
|
||||
const mergedInputs =
|
||||
this._dedupeBindings(directive.inputs ? directive.inputs.concat(inputs) : inputs);
|
||||
const mergedOutputs =
|
||||
this._dedupeBindings(directive.outputs ? directive.outputs.concat(outputs) : outputs);
|
||||
const mergedHost = directive.host ? StringMapWrapper.merge(directive.host, host) : host;
|
||||
const mergedQueries =
|
||||
directive.queries ? StringMapWrapper.merge(directive.queries, queries) : queries;
|
||||
|
@ -8,13 +8,13 @@
|
||||
|
||||
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 {EventHandlerVars, convertActionBinding, convertPropertyBinding} from './compiler_util/expression_converter';
|
||||
import {triggerAnimation, writeToRenderer} from './compiler_util/render_util';
|
||||
import {CompilerConfig} from './config';
|
||||
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 {ClassBuilder, createClassStmt} from './output/class_builder';
|
||||
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()
|
||||
export class DirectiveWrapperCompiler {
|
||||
static dirWrapperClassName(id: CompileIdentifierMetadata) { return `Wrapper_${id.name}`; }
|
||||
static dirWrapperClassName(id: CompileIdentifierMetadata) {
|
||||
return `Wrapper_${identifierName(id)}`;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private compilerConfig: CompilerConfig, private _exprParser: Parser,
|
||||
@ -117,10 +119,10 @@ class DirectiveWrapperBuilder implements ClassBuilder {
|
||||
[
|
||||
new o.FnParam(
|
||||
VIEW_VAR.name,
|
||||
o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
||||
o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
||||
new o.FnParam(
|
||||
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),
|
||||
],
|
||||
this.detachStmts),
|
||||
@ -172,7 +174,7 @@ function addNgDoCheckMethod(builder: DirectiveWrapperBuilder) {
|
||||
}
|
||||
if (builder.compilerConfig.logBindingUpdate) {
|
||||
onChangesStmts.push(
|
||||
o.importExpr(resolveIdentifier(Identifiers.setBindingDebugInfoForChanges))
|
||||
o.importExpr(createIdentifier(Identifiers.setBindingDebugInfoForChanges))
|
||||
.callFn(
|
||||
[VIEW_VAR.prop('renderer'), RENDER_EL_VAR, o.THIS_EXPR.prop(CHANGES_FIELD_NAME)])
|
||||
.toStmt());
|
||||
@ -198,7 +200,7 @@ function addNgDoCheckMethod(builder: DirectiveWrapperBuilder) {
|
||||
'ngDoCheck',
|
||||
[
|
||||
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(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE),
|
||||
],
|
||||
@ -214,7 +216,7 @@ function addCheckInputMethod(input: string, builder: DirectiveWrapperBuilder) {
|
||||
if (builder.genChanges) {
|
||||
onChangeStatements.push(o.THIS_EXPR.prop(CHANGES_FIELD_NAME)
|
||||
.key(o.literal(input))
|
||||
.set(o.importExpr(resolveIdentifier(Identifiers.SimpleChange))
|
||||
.set(o.importExpr(createIdentifier(Identifiers.SimpleChange))
|
||||
.instantiate([field.expression, CURR_VALUE_VAR]))
|
||||
.toStmt());
|
||||
}
|
||||
@ -237,10 +239,10 @@ function addCheckHostMethod(
|
||||
const stmts: o.Statement[] = [];
|
||||
const methodParams: 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(
|
||||
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(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE),
|
||||
];
|
||||
@ -255,14 +257,14 @@ function addCheckHostMethod(
|
||||
if (hostProp.needsRuntimeSecurityContext) {
|
||||
securityContextExpr = o.variable(`secCtx_${methodParams.length}`);
|
||||
methodParams.push(new o.FnParam(
|
||||
securityContextExpr.name, o.importType(resolveIdentifier(Identifiers.SecurityContext))));
|
||||
securityContextExpr.name, o.importType(createIdentifier(Identifiers.SecurityContext))));
|
||||
}
|
||||
let checkBindingStmts: o.Statement[];
|
||||
if (hostProp.isAnimation) {
|
||||
const {updateStmts, detachStmts} = triggerAnimation(
|
||||
VIEW_VAR, COMPONENT_VIEW_VAR, hostProp,
|
||||
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);
|
||||
checkBindingStmts = updateStmts;
|
||||
builder.detachStmts.push(...detachStmts);
|
||||
@ -306,7 +308,7 @@ function addHandleEventMethod(hostListeners: BoundEventAst[], builder: Directive
|
||||
function addSubscribeMethod(dirMeta: CompileDirectiveMetadata, builder: DirectiveWrapperBuilder) {
|
||||
const methodParams: 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)
|
||||
];
|
||||
const stmts: o.Statement[] = [
|
||||
@ -348,9 +350,10 @@ function parseHostBindings(
|
||||
const errors: ParseError[] = [];
|
||||
const parser =
|
||||
new BindingParser(exprParser, DEFAULT_INTERPOLATION_CONFIG, schemaRegistry, [], errors);
|
||||
const sourceFileName = dirMeta.type.moduleUrl ?
|
||||
`in Directive ${dirMeta.type.name} in ${dirMeta.type.moduleUrl}` :
|
||||
`in Directive ${dirMeta.type.name}`;
|
||||
const moduleUrl = identifierModuleUrl(dirMeta.type);
|
||||
const sourceFileName = moduleUrl ?
|
||||
`in Directive ${identifierName(dirMeta.type)} in ${moduleUrl}` :
|
||||
`in Directive ${identifierName(dirMeta.type)}`;
|
||||
const sourceFile = new ParseSourceFile('', sourceFileName);
|
||||
const sourceSpan = new ParseSourceSpan(
|
||||
new ParseLocation(sourceFile, null, null, null),
|
||||
|
@ -8,10 +8,16 @@
|
||||
|
||||
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}]`);
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
@ -39,7 +45,7 @@ class _SerializerVisitor implements i18n.Visitor {
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -53,6 +59,21 @@ export function serializeNodes(nodes: i18n.Node[]): string[] {
|
||||
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
|
||||
*
|
||||
@ -63,7 +84,7 @@ export function serializeNodes(nodes: i18n.Node[]): string[] {
|
||||
*/
|
||||
export function sha1(str: string): string {
|
||||
const utf8 = utf8Encode(str);
|
||||
const words32 = stringToWords32(utf8);
|
||||
const words32 = stringToWords32(utf8, Endian.Big);
|
||||
const len = utf8.length * 8;
|
||||
|
||||
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)];
|
||||
}
|
||||
|
||||
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);
|
||||
function fk(index: number, b: number, c: number, d: number): [number, number] {
|
||||
if (index < 20) {
|
||||
return [(b & c) | (~b & d), 0x5a827999];
|
||||
}
|
||||
|
||||
return hex.toLowerCase();
|
||||
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 {
|
||||
@ -131,10 +236,9 @@ function decodeSurrogatePairs(str: string, index: number): number {
|
||||
}
|
||||
|
||||
const high = str.charCodeAt(index);
|
||||
let low: number;
|
||||
|
||||
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) {
|
||||
return (high - 0xd800) * 0x400 + low - 0xdc00 + 0x10000;
|
||||
}
|
||||
@ -143,50 +247,126 @@ function decodeSurrogatePairs(str: string, index: number): number {
|
||||
return high;
|
||||
}
|
||||
|
||||
function stringToWords32(str: string): number[] {
|
||||
const words32 = Array(str.length >>> 2);
|
||||
function add32(a: number, b: number): number {
|
||||
return add32to64(a, b)[1];
|
||||
}
|
||||
|
||||
function add32to64(a: number, b: number): [number, number] {
|
||||
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] = 0;
|
||||
}
|
||||
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
words32[i >>> 2] |= (str.charCodeAt(i) & 0xff) << 8 * (3 - i & 0x3);
|
||||
words32[i] = wordAt(str, i * 4, endian);
|
||||
}
|
||||
|
||||
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 = '';
|
||||
for (let i = 0; i < words32.length * 4; i++) {
|
||||
str += String.fromCharCode((words32[i >>> 2] >>> 8 * (3 - i & 0x3)) & 0xff);
|
||||
for (let i = 0; i < 4; i++) {
|
||||
str += String.fromCharCode((word >>> 8 * (3 - i)) & 0xff);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function fk(index: number, b: number, c: number, d: number): [number, number] {
|
||||
if (index < 20) {
|
||||
return [(b & c) | (~b & d), 0x5a827999];
|
||||
function byteStringToHexString(str: string): string {
|
||||
let hex: string = '';
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const b = byteAt(str, i);
|
||||
hex += (b >>> 4).toString(16) + (b & 0x0f).toString(16);
|
||||
}
|
||||
|
||||
if (index < 40) {
|
||||
return [b ^ c ^ d, 0x6ed9eba1];
|
||||
}
|
||||
|
||||
if (index < 60) {
|
||||
return [(b & c) | (b & d) | (c & d), 0x8f1bbcdc];
|
||||
}
|
||||
|
||||
return [b ^ c ^ d, 0xca62c1d6];
|
||||
return hex.toLowerCase();
|
||||
}
|
||||
|
||||
function add32(a: number, b: number): number {
|
||||
const low = (a & 0xffff) + (b & 0xffff);
|
||||
const high = (a >> 16) + (b >> 16) + (low >> 16);
|
||||
return (high << 16) | (low & 0xffff);
|
||||
// based on http://www.danvk.org/hex2dec.html (JS can not handle more than 56b)
|
||||
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);
|
||||
}
|
||||
|
||||
return decimal.split('').reverse().join('');
|
||||
}
|
||||
|
||||
function rol32(a: number, count: number): number {
|
||||
return (a << count) | (a >>> (32 - count));
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
function numberTimesBigInt(num: number, b: string): string {
|
||||
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 {ParseTreeResult} from '../ml_parser/parser';
|
||||
|
||||
import {digestMessage} from './digest';
|
||||
import * as i18n from './i18n_ast';
|
||||
import {createI18nMessageFactory} from './i18n_parser';
|
||||
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
|
||||
// message
|
||||
const i18nAttr = _getI18nAttr(el);
|
||||
const isImplicit = this._implicitTags.some((tag: string): boolean => el.name === tag) &&
|
||||
!this._inIcu && !this._isInTranslatableSection;
|
||||
const isImplicit = this._implicitTags.some(tag => el.name === tag) && !this._inIcu &&
|
||||
!this._isInTranslatableSection;
|
||||
const isTopLevelImplicit = !wasInImplicitNode && isImplicit;
|
||||
this._inImplicitNode = this._inImplicitNode || isImplicit;
|
||||
|
||||
@ -348,14 +347,14 @@ class _Visitor implements html.Visitor {
|
||||
// no-op when called in extraction mode (returns [])
|
||||
private _translateMessage(el: html.Node, message: i18n.Message): html.Node[] {
|
||||
if (message && this._mode === _VisitorMode.Merge) {
|
||||
const id = digestMessage(message);
|
||||
const nodes = this._translations.get(id);
|
||||
const nodes = this._translations.get(message);
|
||||
|
||||
if (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 [];
|
||||
@ -384,19 +383,20 @@ class _Visitor implements html.Visitor {
|
||||
if (attr.value && attr.value != '' && i18nAttributeMeanings.hasOwnProperty(attr.name)) {
|
||||
const meaning = i18nAttributeMeanings[attr.name];
|
||||
const message: i18n.Message = this._createI18nMessage([attr], meaning, '');
|
||||
const id = digestMessage(message);
|
||||
const nodes = this._translations.get(id);
|
||||
const nodes = this._translations.get(message);
|
||||
if (nodes) {
|
||||
if (nodes[0] instanceof html.Text) {
|
||||
const value = (nodes[0] as html.Text).value;
|
||||
translatedAttributes.push(new html.Attribute(attr.name, value, attr.sourceSpan));
|
||||
} else {
|
||||
this._reportError(
|
||||
el, `Unexpected translation for attribute "${attr.name}" (id="${id}")`);
|
||||
el,
|
||||
`Unexpected translation for attribute "${attr.name}" (id="${this._translations.digest(message)}")`);
|
||||
}
|
||||
} else {
|
||||
this._reportError(
|
||||
el, `Translation unavailable for attribute "${attr.name}" (id="${id}")`);
|
||||
el,
|
||||
`Translation unavailable for attribute "${attr.name}" (id="${this._translations.digest(message)}")`);
|
||||
}
|
||||
} else {
|
||||
translatedAttributes.push(attr);
|
||||
|
@ -12,18 +12,20 @@ export class Message {
|
||||
/**
|
||||
* @param nodes message AST
|
||||
* @param placeholders maps placeholder names to static content
|
||||
* @param placeholderToMsgIds maps placeholder names to translatable message IDs (used for ICU
|
||||
* messages)
|
||||
* @param placeholderToMessage maps placeholder names to messages (used for nested ICU messages)
|
||||
* @param meaning
|
||||
* @param description
|
||||
*/
|
||||
constructor(
|
||||
public nodes: Node[], public placeholders: {[name: string]: string},
|
||||
public placeholderToMsgIds: {[name: string]: string}, public meaning: string,
|
||||
public nodes: Node[], public placeholders: {[phName: string]: string},
|
||||
public placeholderToMessage: {[phName: string]: Message}, public meaning: 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 {
|
||||
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); }
|
||||
}
|
||||
|
||||
// TODO(vicb): do we really need this node (vs an array) ?
|
||||
export class Container implements Node {
|
||||
constructor(public children: Node[], public sourceSpan: ParseSourceSpan) {}
|
||||
|
||||
@ -38,6 +41,7 @@ export class Container implements Node {
|
||||
}
|
||||
|
||||
export class Icu implements Node {
|
||||
public expressionPlaceholder: string;
|
||||
constructor(
|
||||
public expression: string, public type: string, public cases: {[k: string]: Node},
|
||||
public sourceSpan: ParseSourceSpan) {}
|
||||
@ -55,13 +59,13 @@ export class TagPlaceholder 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); }
|
||||
}
|
||||
|
||||
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); }
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../ml_parser/in
|
||||
import {ParseTreeResult} from '../ml_parser/parser';
|
||||
|
||||
import {mergeTranslations} from './extractor_merger';
|
||||
import {MessageBundle} from './message_bundle';
|
||||
import {Serializer} from './serializers/serializer';
|
||||
import {Xliff} from './serializers/xliff';
|
||||
import {Xmb} from './serializers/xmb';
|
||||
@ -41,32 +40,29 @@ export class I18NHtmlParser implements HtmlParser {
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return new ParseTreeResult(parseResult.rootNodes, parseResult.errors.concat(errors));
|
||||
if (parseResult.errors.length) {
|
||||
return new ParseTreeResult(parseResult.rootNodes, parseResult.errors);
|
||||
}
|
||||
|
||||
const serializer = this._createSerializer(interpolationConfig);
|
||||
const translationBundle =
|
||||
TranslationBundle.load(this._translations, url, messageBundle, serializer);
|
||||
const serializer = this._createSerializer();
|
||||
const translationBundle = TranslationBundle.load(this._translations, url, serializer);
|
||||
|
||||
return mergeTranslations(parseResult.rootNodes, translationBundle, interpolationConfig, [], {});
|
||||
}
|
||||
|
||||
private _createSerializer(interpolationConfig: InterpolationConfig): Serializer {
|
||||
private _createSerializer(): Serializer {
|
||||
const format = (this._translationsFormat || 'xlf').toLowerCase();
|
||||
|
||||
switch (format) {
|
||||
case 'xmb':
|
||||
return new Xmb();
|
||||
case 'xtb':
|
||||
return new Xtb(this._htmlParser, interpolationConfig);
|
||||
return new Xtb();
|
||||
case 'xliff':
|
||||
case 'xlf':
|
||||
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 {InterpolationConfig} from '../ml_parser/interpolation_config';
|
||||
import {ParseSourceSpan} from '../parse_util';
|
||||
import {digestMessage} from './digest';
|
||||
|
||||
import * as i18n from './i18n_ast';
|
||||
import {PlaceholderRegistry} from './serializers/placeholder';
|
||||
@ -34,8 +33,8 @@ class _I18nVisitor implements html.Visitor {
|
||||
private _isIcu: boolean;
|
||||
private _icuDepth: number;
|
||||
private _placeholderRegistry: PlaceholderRegistry;
|
||||
private _placeholderToContent: {[name: string]: string};
|
||||
private _placeholderToIds: {[name: string]: string};
|
||||
private _placeholderToContent: {[phName: string]: string};
|
||||
private _placeholderToMessage: {[phName: string]: i18n.Message};
|
||||
|
||||
constructor(
|
||||
private _expressionParser: ExpressionParser,
|
||||
@ -46,12 +45,12 @@ class _I18nVisitor implements html.Visitor {
|
||||
this._icuDepth = 0;
|
||||
this._placeholderRegistry = new PlaceholderRegistry();
|
||||
this._placeholderToContent = {};
|
||||
this._placeholderToIds = {};
|
||||
this._placeholderToMessage = {};
|
||||
|
||||
const i18nodes: i18n.Node[] = html.visitAll(this, nodes, {});
|
||||
|
||||
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 {
|
||||
@ -99,7 +98,13 @@ class _I18nVisitor implements html.Visitor {
|
||||
this._icuDepth--;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
const phName = this._placeholderRegistry.getPlaceholderName('ICU', icu.sourceSpan.toString());
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,6 @@ import {HtmlParser} from '../ml_parser/html_parser';
|
||||
import {InterpolationConfig} from '../ml_parser/interpolation_config';
|
||||
import {ParseError} from '../parse_util';
|
||||
|
||||
import {digestMessage} from './digest';
|
||||
import {extractMessages} from './extractor_merger';
|
||||
import {Message} from './i18n_ast';
|
||||
import {Serializer} from './serializers/serializer';
|
||||
@ -19,7 +18,7 @@ import {Serializer} from './serializers/serializer';
|
||||
* A container for message extracted from the templates.
|
||||
*/
|
||||
export class MessageBundle {
|
||||
private _messageMap: {[id: string]: Message} = {};
|
||||
private _messages: Message[] = [];
|
||||
|
||||
constructor(
|
||||
private _htmlParser: HtmlParser, private _implicitTags: string[],
|
||||
@ -40,11 +39,10 @@ export class MessageBundle {
|
||||
return i18nParserResult.errors;
|
||||
}
|
||||
|
||||
i18nParserResult.messages.forEach(
|
||||
(message) => { this._messageMap[digestMessage(message)] = message; });
|
||||
this._messages.push(...i18nParserResult.messages);
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
@ -93,6 +95,10 @@ export class PlaceholderRegistry {
|
||||
return uniqueName;
|
||||
}
|
||||
|
||||
getUniquePlaceholder(name: string): string {
|
||||
return this._generateUniqueName(name.toUpperCase());
|
||||
}
|
||||
|
||||
// Generate a hash for a tag - does not take attribute order into account
|
||||
private _hashTag(tag: string, attrs: {[k: string]: string}, isVoid: boolean): string {
|
||||
const start = `<${tag}`;
|
||||
@ -105,18 +111,8 @@ export class PlaceholderRegistry {
|
||||
private _hashClosingTag(tag: string): string { return this._hashTag(`/${tag}`, {}, false); }
|
||||
|
||||
private _generateUniqueName(base: string): string {
|
||||
let name = base;
|
||||
let next = this._placeHolderNameCounts[name];
|
||||
|
||||
if (!next) {
|
||||
next = 1;
|
||||
} else {
|
||||
name += `_${next}`;
|
||||
next++;
|
||||
}
|
||||
|
||||
this._placeHolderNameCounts[base] = next;
|
||||
|
||||
return name;
|
||||
const next = this._placeHolderNameCounts[base];
|
||||
this._placeHolderNameCounts[base] = next ? next + 1 : 1;
|
||||
return next ? `${base}_${next}` : base;
|
||||
}
|
||||
}
|
||||
|
@ -6,36 +6,12 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import * as html from '../../ml_parser/ast';
|
||||
import * as i18n from '../i18n_ast';
|
||||
import {MessageBundle} from '../message_bundle';
|
||||
|
||||
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[]};
|
||||
|
||||
// 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;
|
||||
digest(message: i18n.Message): string;
|
||||
}
|
@ -7,15 +7,12 @@
|
||||
*/
|
||||
|
||||
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 {ParseError} from '../../parse_util';
|
||||
import {digest} from '../digest';
|
||||
import * as i18n from '../i18n_ast';
|
||||
import {MessageBundle} from '../message_bundle';
|
||||
import {I18nError} from '../parse_util';
|
||||
|
||||
import {Serializer, extractPlaceholderToIds, extractPlaceholders} from './serializer';
|
||||
import {Serializer} from './serializer';
|
||||
import * as xml from './xml_helper';
|
||||
|
||||
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/_/-/)
|
||||
const _SOURCE_LANG = 'en';
|
||||
const _PLACEHOLDER_TAG = 'x';
|
||||
|
||||
const _SOURCE_TAG = 'source';
|
||||
const _TARGET_TAG = 'target';
|
||||
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/xliff-profile-html/xliff-profile-html-1.2.html
|
||||
export class Xliff implements Serializer {
|
||||
constructor(private _htmlParser: HtmlParser, private _interpolationConfig: InterpolationConfig) {}
|
||||
|
||||
write(messageMap: {[id: string]: i18n.Message}): string {
|
||||
write(messages: i18n.Message[]): string {
|
||||
const visitor = new _WriteVisitor();
|
||||
|
||||
const visited: {[id: string]: boolean} = {};
|
||||
const transUnits: xml.Node[] = [];
|
||||
|
||||
Object.keys(messageMap).forEach((id) => {
|
||||
const message = messageMap[id];
|
||||
messages.forEach(message => {
|
||||
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(
|
||||
new xml.CR(8), new xml.Tag(_SOURCE_TAG, {}, visitor.serialize(message.nodes)),
|
||||
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[]} {
|
||||
// Parse the xtb file into xml nodes
|
||||
const result = new XmlParser().parse(content, url);
|
||||
load(content: string, url: string): {[msgId: string]: i18n.Node[]} {
|
||||
// xliff to xml nodes
|
||||
const xliffParser = new XliffParser();
|
||||
const {mlNodesByMsgId, errors} = xliffParser.parse(content, url);
|
||||
|
||||
if (result.errors.length) {
|
||||
throw new Error(`xtb parse errors:\n${result.errors.join('\n')}`);
|
||||
}
|
||||
|
||||
// Replace the placeholders, messages are now string
|
||||
const {messages, errors} = new _LoadVisitor().parse(result.rootNodes, messageBundle);
|
||||
|
||||
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;
|
||||
// xml nodes to i18n nodes
|
||||
const i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {};
|
||||
const converter = new XmlToI18n();
|
||||
Object.keys(mlNodesByMsgId).forEach(msgId => {
|
||||
const {i18nNodes, errors: e} = converter.convert(mlNodesByMsgId[msgId]);
|
||||
errors.push(...e);
|
||||
i18nNodesByMsgId[msgId] = i18nNodes;
|
||||
});
|
||||
|
||||
if (parseErrors.length) {
|
||||
throw new Error(`xtb parse errors:\n${parseErrors.join('\n')}`);
|
||||
if (errors.length) {
|
||||
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 {
|
||||
@ -166,75 +156,46 @@ class _WriteVisitor implements i18n.Visitor {
|
||||
}
|
||||
|
||||
// TODO(vicb): add error management (structure)
|
||||
// TODO(vicb): factorize (xtb) ?
|
||||
class _LoadVisitor implements ml.Visitor {
|
||||
private _messageNodes: [string, ml.Node[]][];
|
||||
private _translatedMessages: {[id: string]: string};
|
||||
private _msgId: string;
|
||||
private _target: ml.Node[];
|
||||
// Extract messages as xml nodes from the xliff file
|
||||
class XliffParser implements ml.Visitor {
|
||||
private _unitMlNodes: ml.Node[];
|
||||
private _errors: I18nError[];
|
||||
private _placeholders: {[name: string]: string};
|
||||
private _placeholderToIds: {[name: string]: string};
|
||||
private _mlNodesByMsgId: {[msgId: string]: ml.Node[]};
|
||||
|
||||
parse(nodes: ml.Node[], messageBundle: MessageBundle):
|
||||
{messages: {[k: string]: string}, errors: I18nError[]} {
|
||||
this._messageNodes = [];
|
||||
this._translatedMessages = {};
|
||||
this._msgId = '';
|
||||
this._target = [];
|
||||
this._errors = [];
|
||||
parse(xliff: string, url: string) {
|
||||
this._unitMlNodes = [];
|
||||
this._mlNodesByMsgId = {};
|
||||
|
||||
// Find all messages
|
||||
ml.visitAll(this, nodes, null);
|
||||
const xml = new XmlParser().parse(xliff, url, false);
|
||||
|
||||
const messageMap = messageBundle.getMessageMap();
|
||||
const placeholders = extractPlaceholders(messageBundle);
|
||||
const placeholderToIds = extractPlaceholderToIds(messageBundle);
|
||||
this._errors = xml.errors;
|
||||
ml.visitAll(this, xml.rootNodes, null);
|
||||
|
||||
this._messageNodes
|
||||
.filter(message => {
|
||||
// Remove any messages that is not present in the source message bundle.
|
||||
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};
|
||||
return {
|
||||
mlNodesByMsgId: this._mlNodesByMsgId,
|
||||
errors: this._errors,
|
||||
};
|
||||
}
|
||||
|
||||
visitElement(element: ml.Element, context: any): any {
|
||||
switch (element.name) {
|
||||
case _UNIT_TAG:
|
||||
this._target = null;
|
||||
const msgId = element.attrs.find((attr) => attr.name === 'id');
|
||||
if (!msgId) {
|
||||
this._unitMlNodes = null;
|
||||
const idAttr = element.attrs.find((attr) => attr.name === 'id');
|
||||
if (!idAttr) {
|
||||
this._addError(element, `<${_UNIT_TAG}> misses the "id" attribute`);
|
||||
} else {
|
||||
this._msgId = msgId.value;
|
||||
}
|
||||
ml.visitAll(this, element.children, null);
|
||||
if (this._msgId !== null) {
|
||||
this._messageNodes.push([this._msgId, this._target]);
|
||||
const id = idAttr.value;
|
||||
if (this._mlNodesByMsgId.hasOwnProperty(id)) {
|
||||
this._addError(element, `Duplicated translations for msg ${id}`);
|
||||
} else {
|
||||
ml.visitAll(this, element.children, null);
|
||||
if (this._unitMlNodes) {
|
||||
this._mlNodesByMsgId[id] = this._unitMlNodes;
|
||||
} else {
|
||||
this._addError(element, `Message ${id} misses a translation`);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@ -243,48 +204,65 @@ class _LoadVisitor implements ml.Visitor {
|
||||
break;
|
||||
|
||||
case _TARGET_TAG:
|
||||
this._target = 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`);
|
||||
}
|
||||
this._unitMlNodes = element.children;
|
||||
break;
|
||||
|
||||
default:
|
||||
// TODO(vicb): assert file structure, xliff version
|
||||
// For now only recurse on unhandled nodes
|
||||
ml.visitAll(this, element.children, null);
|
||||
}
|
||||
}
|
||||
|
||||
visitAttribute(attribute: ml.Attribute, context: any): any {
|
||||
throw new Error('unreachable code');
|
||||
visitAttribute(attribute: ml.Attribute, context: any): any {}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
// Convert ml nodes (xliff 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): any { return text.value; }
|
||||
visitText(text: ml.Text, context: any) { return new i18n.Text(text.value, text.sourceSpan); }
|
||||
|
||||
visitComment(comment: ml.Comment, context: any): any { return ''; }
|
||||
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);
|
||||
}
|
||||
|
||||
visitExpansion(expansion: ml.Expansion, context: any): any {
|
||||
throw new Error('unreachable code');
|
||||
this._addError(el, `<${_PLACEHOLDER_TAG}> misses the "id" attribute`);
|
||||
} else {
|
||||
this._addError(el, `Unexpected tag`);
|
||||
}
|
||||
}
|
||||
|
||||
visitExpansionCase(expansionCase: ml.ExpansionCase, context: any): any {
|
||||
throw new Error('unreachable code');
|
||||
}
|
||||
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 {
|
||||
this._errors.push(new I18nError(node.sourceSpan, message));
|
||||
|
@ -6,9 +6,8 @@
|
||||
* 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 {MessageBundle} from '../message_bundle';
|
||||
|
||||
import {Serializer} from './serializer';
|
||||
import * as xml from './xml_helper';
|
||||
@ -39,12 +38,18 @@ const _DOCTYPE = `<!ELEMENT messagebundle (msg)*>
|
||||
<!ELEMENT ex (#PCDATA)>`;
|
||||
|
||||
export class Xmb implements Serializer {
|
||||
write(messageMap: {[k: string]: i18n.Message}): string {
|
||||
write(messages: i18n.Message[]): string {
|
||||
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};
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
digest(message: i18n.Message): string { return digest(message); }
|
||||
}
|
||||
|
||||
class _Visitor implements i18n.Visitor {
|
||||
@ -86,7 +93,7 @@ class _Visitor implements i18n.Visitor {
|
||||
}
|
||||
|
||||
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) => {
|
||||
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)));
|
||||
}
|
||||
}
|
||||
|
||||
export function digest(message: i18n.Message): string {
|
||||
return decimalDigest(message);
|
||||
}
|
@ -7,112 +7,63 @@
|
||||
*/
|
||||
|
||||
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 {ParseError} from '../../parse_util';
|
||||
import * as i18n from '../i18n_ast';
|
||||
import {MessageBundle} from '../message_bundle';
|
||||
import {I18nError} from '../parse_util';
|
||||
|
||||
import {Serializer, extractPlaceholderToIds, extractPlaceholders} from './serializer';
|
||||
import {Serializer} from './serializer';
|
||||
import {digest} from './xmb';
|
||||
|
||||
const _TRANSLATIONS_TAG = 'translationbundle';
|
||||
const _TRANSLATION_TAG = 'translation';
|
||||
const _PLACEHOLDER_TAG = 'ph';
|
||||
|
||||
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[]} {
|
||||
// Parse the xtb file into xml nodes
|
||||
const result = new XmlParser().parse(content, url);
|
||||
|
||||
if (result.errors.length) {
|
||||
throw new Error(`xtb parse errors:\n${result.errors.join('\n')}`);
|
||||
}
|
||||
|
||||
// Replace the placeholders, messages are now string
|
||||
const {messages, errors} = new _Visitor().parse(result.rootNodes, messageBundle);
|
||||
// xml nodes to i18n nodes
|
||||
const i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {};
|
||||
const converter = new XmlToI18n();
|
||||
Object.keys(mlNodesByMsgId).forEach(msgId => {
|
||||
const {i18nNodes, errors: e} = converter.convert(mlNodesByMsgId[msgId]);
|
||||
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) {
|
||||
throw new Error(`xtb parse errors:\n${parseErrors.join('\n')}`);
|
||||
}
|
||||
|
||||
return messageMap;
|
||||
return i18nNodesByMsgId;
|
||||
}
|
||||
|
||||
digest(message: i18n.Message): string { return digest(message); }
|
||||
}
|
||||
|
||||
class _Visitor implements ml.Visitor {
|
||||
private _messageNodes: [string, ml.Node[]][];
|
||||
private _translatedMessages: {[id: string]: string};
|
||||
// Extract messages as xml nodes from the xtb file
|
||||
class XtbParser implements ml.Visitor {
|
||||
private _bundleDepth: number;
|
||||
private _translationDepth: number;
|
||||
private _errors: I18nError[];
|
||||
private _placeholders: {[name: string]: string};
|
||||
private _placeholderToIds: {[name: string]: string};
|
||||
private _mlNodesByMsgId: {[msgId: string]: ml.Node[]};
|
||||
|
||||
parse(nodes: ml.Node[], messageBundle: MessageBundle):
|
||||
{messages: {[k: string]: string}, errors: I18nError[]} {
|
||||
this._messageNodes = [];
|
||||
this._translatedMessages = {};
|
||||
parse(xtb: string, url: string) {
|
||||
this._bundleDepth = 0;
|
||||
this._translationDepth = 0;
|
||||
this._errors = [];
|
||||
this._mlNodesByMsgId = {};
|
||||
|
||||
// Find all messages
|
||||
ml.visitAll(this, nodes, null);
|
||||
const xml = new XmlParser().parse(xtb, url, true);
|
||||
|
||||
const messageMap = messageBundle.getMessageMap();
|
||||
const placeholders = extractPlaceholders(messageBundle);
|
||||
const placeholderToIds = extractPlaceholderToIds(messageBundle);
|
||||
this._errors = xml.errors;
|
||||
ml.visitAll(this, xml.rootNodes);
|
||||
|
||||
this._messageNodes
|
||||
.filter(message => {
|
||||
// Remove any messages that is not present in the source message bundle.
|
||||
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};
|
||||
return {
|
||||
mlNodesByMsgId: this._mlNodesByMsgId,
|
||||
errors: this._errors,
|
||||
};
|
||||
}
|
||||
|
||||
visitElement(element: ml.Element, context: any): any {
|
||||
@ -127,40 +78,16 @@ class _Visitor implements ml.Visitor {
|
||||
break;
|
||||
|
||||
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');
|
||||
if (!idAttr) {
|
||||
this._addError(element, `<${_TRANSLATION_TAG}> misses the "id" attribute`);
|
||||
} else {
|
||||
// ICU placeholders are reference to other messages.
|
||||
// The referenced message might not have been decoded yet.
|
||||
// We need to have all messages available to make sure deps are decoded first.
|
||||
// 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 {
|
||||
const name = nameAttr.value;
|
||||
if (this._placeholders.hasOwnProperty(name)) {
|
||||
return this._placeholders[name];
|
||||
const id = idAttr.value;
|
||||
if (this._mlNodesByMsgId.hasOwnProperty(id)) {
|
||||
this._addError(element, `Duplicated translations for msg ${id}`);
|
||||
} else {
|
||||
this._mlNodesByMsgId[id] = element.children;
|
||||
}
|
||||
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;
|
||||
|
||||
@ -169,23 +96,68 @@ class _Visitor implements ml.Visitor {
|
||||
}
|
||||
}
|
||||
|
||||
visitAttribute(attribute: ml.Attribute, context: any): any {
|
||||
throw new Error('unreachable code');
|
||||
}
|
||||
visitAttribute(attribute: ml.Attribute, context: any): any {}
|
||||
|
||||
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 {
|
||||
const strCases = expansion.cases.map(c => c.visit(this, null));
|
||||
visitExpansion(expansion: ml.Expansion, context: any): any {}
|
||||
|
||||
return `{${expansion.switchValue}, ${expansion.type}, strCases.join(' ')}`;
|
||||
}
|
||||
|
||||
visitExpansionCase(expansionCase: ml.ExpansionCase, context: any): any {
|
||||
return `${expansionCase.value} {${ml.visitAll(this, expansionCase.expression, null)}}`;
|
||||
}
|
||||
visitExpansionCase(expansionCase: ml.ExpansionCase, context: any): any {}
|
||||
|
||||
private _addError(node: ml.Node, message: string): void {
|
||||
this._errors.push(new I18nError(node.sourceSpan, message));
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
this._errors.push(new I18nError(node.sourceSpan, message));
|
||||
|
@ -7,22 +7,120 @@
|
||||
*/
|
||||
|
||||
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';
|
||||
|
||||
/**
|
||||
* A container for translated messages
|
||||
*/
|
||||
export class TranslationBundle {
|
||||
constructor(private _messageMap: {[id: string]: html.Node[]} = {}) {}
|
||||
private _i18nToHtml: I18nToHtmlVisitor;
|
||||
|
||||
static load(content: string, url: string, messageBundle: MessageBundle, serializer: Serializer):
|
||||
TranslationBundle {
|
||||
return new TranslationBundle(serializer.load(content, url, messageBundle));
|
||||
constructor(
|
||||
private _i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {},
|
||||
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);
|
||||
const digestFn = (m: i18n.Message) => serializer.digest(m);
|
||||
return new TranslationBundle(i18nNodesByMsgId, digestFn);
|
||||
}
|
||||
|
||||
has(id: string): boolean { return id in this._messageMap; }
|
||||
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 {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';
|
||||
|
||||
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) {
|
||||
let moduleUrl = identifier.moduleUrl;
|
||||
return reflector.resolveIdentifier(identifier.name, identifier.moduleUrl, identifier.runtime);
|
||||
}
|
||||
|
||||
export function createIdentifier(identifier: IdentifierSpec): CompileIdentifierMetadata {
|
||||
const reference =
|
||||
reflector.resolveIdentifier(identifier.name, identifier.moduleUrl, identifier.runtime);
|
||||
if (isStaticSymbol(reference)) {
|
||||
moduleUrl = reference.filePath;
|
||||
}
|
||||
return new CompileIdentifierMetadata(
|
||||
{name: identifier.name, moduleUrl: moduleUrl, reference: reference});
|
||||
return {reference: reference};
|
||||
}
|
||||
|
||||
export function identifierToken(identifier: CompileIdentifierMetadata): CompileTokenMetadata {
|
||||
return new CompileTokenMetadata({identifier: identifier});
|
||||
return {identifier: identifier};
|
||||
}
|
||||
|
||||
export function resolveIdentifierToken(identifier: IdentifierSpec): CompileTokenMetadata {
|
||||
return identifierToken(resolveIdentifier(identifier));
|
||||
export function createIdentifierToken(identifier: IdentifierSpec): CompileTokenMetadata {
|
||||
return identifierToken(createIdentifier(identifier));
|
||||
}
|
||||
|
||||
export function resolveEnumIdentifier(
|
||||
enumType: CompileIdentifierMetadata, name: string): CompileIdentifierMetadata {
|
||||
const resolvedEnum = reflector.resolveEnum(enumType.reference, name);
|
||||
return new CompileIdentifierMetadata(
|
||||
{name: `${enumType.name}.${name}`, moduleUrl: enumType.moduleUrl, reference: resolvedEnum});
|
||||
export function createEnumIdentifier(
|
||||
enumType: IdentifierSpec, name: string): CompileIdentifierMetadata {
|
||||
const resolvedEnum = reflector.resolveEnum(resolveIdentifier(enumType), name);
|
||||
return {reference: resolvedEnum};
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import {Compiler, ComponentFactory, Injectable, Injector, ModuleWithComponentFac
|
||||
|
||||
import {AnimationCompiler} from '../animation/animation_compiler';
|
||||
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 {DirectiveNormalizer} from '../directive_normalizer';
|
||||
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
|
||||
@ -126,14 +126,13 @@ export class JitCompiler implements Compiler {
|
||||
compileResult.dependencies.forEach((dep) => {
|
||||
dep.placeholder.reference =
|
||||
this._assertComponentKnown(dep.comp.reference, true).proxyComponentFactory;
|
||||
dep.placeholder.name = `compFactory_${dep.comp.name}`;
|
||||
});
|
||||
if (!this._compilerConfig.useJit) {
|
||||
ngModuleFactory =
|
||||
interpretStatements(compileResult.statements, compileResult.ngModuleFactoryVar);
|
||||
} else {
|
||||
ngModuleFactory = jitStatements(
|
||||
`/${moduleMeta.type.name}/module.ngfactory.js`, compileResult.statements,
|
||||
`/${identifierName(moduleMeta.type)}/module.ngfactory.js`, compileResult.statements,
|
||||
compileResult.ngModuleFactoryVar);
|
||||
}
|
||||
this._compiledNgModuleCache.set(moduleMeta.type.reference, ngModuleFactory);
|
||||
@ -215,7 +214,12 @@ export class JitCompiler implements Compiler {
|
||||
if (!compiledTemplate) {
|
||||
const compMeta = this._metadataResolver.getDirectiveMetadata(compType);
|
||||
assertComponent(compMeta);
|
||||
const hostMeta = createHostComponentMeta(compMeta);
|
||||
|
||||
class HostClass {
|
||||
static overriddenName = `${identifierName(compMeta.type)}_Host`;
|
||||
}
|
||||
|
||||
const hostMeta = createHostComponentMeta(HostClass, compMeta);
|
||||
compiledTemplate = new CompiledTemplate(
|
||||
true, compMeta.selector, compMeta.type, hostMeta, ngModule, [compMeta.type]);
|
||||
this._compiledHostTemplateCache.set(compType, compiledTemplate);
|
||||
@ -264,8 +268,8 @@ export class JitCompiler implements Compiler {
|
||||
directiveWrapperClass = interpretStatements(statements, compileResult.dirWrapperClassVar);
|
||||
} else {
|
||||
directiveWrapperClass = jitStatements(
|
||||
`/${moduleMeta.type.name}/${dirMeta.type.name}/wrapper.ngfactory.js`, statements,
|
||||
compileResult.dirWrapperClassVar);
|
||||
`/${identifierName(moduleMeta.type)}/${identifierName(dirMeta.type)}/wrapper.ngfactory.js`,
|
||||
statements, compileResult.dirWrapperClassVar);
|
||||
}
|
||||
this._compiledDirectiveWrapperCache.set(dirMeta.type.reference, directiveWrapperClass);
|
||||
}
|
||||
@ -288,9 +292,9 @@ export class JitCompiler implements Compiler {
|
||||
pipe => this._metadataResolver.getPipeSummary(pipe.reference));
|
||||
const parsedTemplate = this._templateParser.parse(
|
||||
compMeta, compMeta.template.template, directives, pipes, template.ngModule.schemas,
|
||||
compMeta.type.name);
|
||||
identifierName(compMeta.type));
|
||||
const compiledAnimations =
|
||||
this._animationCompiler.compile(compMeta.type.name, parsedAnimations);
|
||||
this._animationCompiler.compile(identifierName(compMeta.type), parsedAnimations);
|
||||
const compileResult = this._viewCompiler.compileComponent(
|
||||
compMeta, parsedTemplate, ir.variable(stylesCompileResult.componentStylesheet.stylesVar),
|
||||
pipes, compiledAnimations);
|
||||
@ -300,12 +304,10 @@ export class JitCompiler implements Compiler {
|
||||
const vfd = <ViewClassDependency>dep;
|
||||
depTemplate = this._assertComponentKnown(vfd.comp.reference, false);
|
||||
vfd.placeholder.reference = depTemplate.proxyViewClass;
|
||||
vfd.placeholder.name = `View_${vfd.comp.name}`;
|
||||
} else if (dep instanceof ComponentFactoryDependency) {
|
||||
const cfd = <ComponentFactoryDependency>dep;
|
||||
depTemplate = this._assertComponentKnown(cfd.comp.reference, true);
|
||||
cfd.placeholder.reference = depTemplate.proxyComponentFactory;
|
||||
cfd.placeholder.name = `compFactory_${cfd.comp.name}`;
|
||||
} else if (dep instanceof DirectiveWrapperDependency) {
|
||||
const dwd = <DirectiveWrapperDependency>dep;
|
||||
dwd.placeholder.reference = this._assertDirectiveWrapper(dwd.dir.reference);
|
||||
@ -319,7 +321,7 @@ export class JitCompiler implements Compiler {
|
||||
viewClass = interpretStatements(statements, compileResult.viewClassVar);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
template.compiled(viewClass);
|
||||
@ -332,7 +334,6 @@ export class JitCompiler implements Compiler {
|
||||
const nestedStylesArr = this._resolveAndEvalStylesCompileResult(
|
||||
nestedCompileResult, externalStylesheetsByModuleUrl);
|
||||
dep.valuePlaceholder.reference = nestedStylesArr;
|
||||
dep.valuePlaceholder.name = `importedStyles${i}`;
|
||||
});
|
||||
}
|
||||
|
||||
@ -380,7 +381,8 @@ class CompiledTemplate {
|
||||
|
||||
function assertComponent(meta: CompileDirectiveMetadata) {
|
||||
if (!meta.isComponent) {
|
||||
throw new Error(`Could not compile '${meta.type.name}' because it is not a component.`);
|
||||
throw new Error(
|
||||
`Could not compile '${identifierName(meta.type)}' because it is not a component.`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
* 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 {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
|
||||
@ -15,14 +15,14 @@ import {DirectiveNormalizer} from './directive_normalizer';
|
||||
import {DirectiveResolver} from './directive_resolver';
|
||||
import {ListWrapper, StringMapWrapper} from './facade/collection';
|
||||
import {isBlank, isPresent, stringify} from './facade/lang';
|
||||
import {Identifiers, resolveIdentifierToken} from './identifiers';
|
||||
import {Identifiers, createIdentifierToken, resolveIdentifier} from './identifiers';
|
||||
import {hasLifecycleHook} from './lifecycle_reflector';
|
||||
import {NgModuleResolver} from './ng_module_resolver';
|
||||
import {PipeResolver} from './pipe_resolver';
|
||||
import {ComponentStillLoadingError, LIFECYCLE_HOOKS_VALUES, ReflectorReader, reflector} from './private_import_core';
|
||||
import {ElementSchemaRegistry} from './schema/element_schema_registry';
|
||||
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 _ngModuleCache = new Map<Type<any>, cpl.CompileNgModuleMetadata>();
|
||||
private _ngModuleOfTypes = new Map<Type<any>, Type<any>>();
|
||||
private _anonymousTypes = new Map<Object, number>();
|
||||
private _anonymousTypeIndex = 0;
|
||||
|
||||
constructor(
|
||||
private _ngModuleResolver: NgModuleResolver, private _directiveResolver: DirectiveResolver,
|
||||
@ -50,20 +48,6 @@ export class CompileMetadataResolver {
|
||||
private _directiveNormalizer: DirectiveNormalizer,
|
||||
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>) {
|
||||
const dirMeta = this._directiveCache.get(type);
|
||||
this._directiveCache.delete(type);
|
||||
@ -142,30 +126,30 @@ export class CompileMetadataResolver {
|
||||
return null;
|
||||
}
|
||||
|
||||
private _loadDirectiveMetadata(directiveType: any, isSync: boolean): Promise<any> {
|
||||
private _loadDirectiveMetadata(directiveType: any, isSync: boolean): () => Promise<any> {
|
||||
if (this._directiveCache.has(directiveType)) {
|
||||
return;
|
||||
}
|
||||
directiveType = resolveForwardRef(directiveType);
|
||||
const nonNormalizedMetadata = this.getNonNormalizedDirectiveMetadata(directiveType);
|
||||
const {annotation, metadata} = this.getNonNormalizedDirectiveMetadata(directiveType);
|
||||
|
||||
const createDirectiveMetadata = (templateMetadata: cpl.CompileTemplateMetadata) => {
|
||||
const normalizedDirMeta = new cpl.CompileDirectiveMetadata({
|
||||
type: nonNormalizedMetadata.type,
|
||||
isComponent: nonNormalizedMetadata.isComponent,
|
||||
selector: nonNormalizedMetadata.selector,
|
||||
exportAs: nonNormalizedMetadata.exportAs,
|
||||
changeDetection: nonNormalizedMetadata.changeDetection,
|
||||
inputs: nonNormalizedMetadata.inputs,
|
||||
outputs: nonNormalizedMetadata.outputs,
|
||||
hostListeners: nonNormalizedMetadata.hostListeners,
|
||||
hostProperties: nonNormalizedMetadata.hostProperties,
|
||||
hostAttributes: nonNormalizedMetadata.hostAttributes,
|
||||
providers: nonNormalizedMetadata.providers,
|
||||
viewProviders: nonNormalizedMetadata.viewProviders,
|
||||
queries: nonNormalizedMetadata.queries,
|
||||
viewQueries: nonNormalizedMetadata.viewQueries,
|
||||
entryComponents: nonNormalizedMetadata.entryComponents,
|
||||
type: metadata.type,
|
||||
isComponent: metadata.isComponent,
|
||||
selector: metadata.selector,
|
||||
exportAs: metadata.exportAs,
|
||||
changeDetection: metadata.changeDetection,
|
||||
inputs: metadata.inputs,
|
||||
outputs: metadata.outputs,
|
||||
hostListeners: metadata.hostListeners,
|
||||
hostProperties: metadata.hostProperties,
|
||||
hostAttributes: metadata.hostAttributes,
|
||||
providers: metadata.providers,
|
||||
viewProviders: metadata.viewProviders,
|
||||
queries: metadata.queries,
|
||||
viewQueries: metadata.viewQueries,
|
||||
entryComponents: metadata.entryComponents,
|
||||
template: templateMetadata
|
||||
});
|
||||
this._directiveCache.set(directiveType, normalizedDirMeta);
|
||||
@ -173,17 +157,17 @@ export class CompileMetadataResolver {
|
||||
return normalizedDirMeta;
|
||||
};
|
||||
|
||||
if (nonNormalizedMetadata.isComponent) {
|
||||
if (metadata.isComponent) {
|
||||
const templateMeta = this._directiveNormalizer.normalizeTemplate({
|
||||
componentType: directiveType,
|
||||
moduleUrl: nonNormalizedMetadata.type.moduleUrl,
|
||||
encapsulation: nonNormalizedMetadata.template.encapsulation,
|
||||
template: nonNormalizedMetadata.template.template,
|
||||
templateUrl: nonNormalizedMetadata.template.templateUrl,
|
||||
styles: nonNormalizedMetadata.template.styles,
|
||||
styleUrls: nonNormalizedMetadata.template.styleUrls,
|
||||
animations: nonNormalizedMetadata.template.animations,
|
||||
interpolation: nonNormalizedMetadata.template.interpolation
|
||||
moduleUrl: componentModuleUrl(this._reflector, directiveType, annotation),
|
||||
encapsulation: metadata.template.encapsulation,
|
||||
template: metadata.template.template,
|
||||
templateUrl: metadata.template.templateUrl,
|
||||
styles: metadata.template.styles,
|
||||
styleUrls: metadata.template.styleUrls,
|
||||
animations: metadata.template.animations,
|
||||
interpolation: metadata.template.interpolation
|
||||
});
|
||||
if (templateMeta.syncResult) {
|
||||
createDirectiveMetadata(templateMeta.syncResult);
|
||||
@ -192,7 +176,7 @@ export class CompileMetadataResolver {
|
||||
if (isSync) {
|
||||
throw new ComponentStillLoadingError(directiveType);
|
||||
}
|
||||
return templateMeta.asyncResult.then(createDirectiveMetadata);
|
||||
return () => templateMeta.asyncResult.then(createDirectiveMetadata);
|
||||
}
|
||||
} else {
|
||||
// directive
|
||||
@ -201,18 +185,17 @@ export class CompileMetadataResolver {
|
||||
}
|
||||
}
|
||||
|
||||
getNonNormalizedDirectiveMetadata(directiveType: any): cpl.CompileDirectiveMetadata {
|
||||
getNonNormalizedDirectiveMetadata(directiveType: any):
|
||||
{annotation: Directive, metadata: cpl.CompileDirectiveMetadata} {
|
||||
directiveType = resolveForwardRef(directiveType);
|
||||
const dirMeta = this._directiveResolver.resolve(directiveType);
|
||||
if (!dirMeta) {
|
||||
return null;
|
||||
}
|
||||
let moduleUrl = staticTypeModuleUrl(directiveType);
|
||||
let nonNormalizedTemplateMetadata: cpl.CompileTemplateMetadata;
|
||||
|
||||
if (dirMeta instanceof Component) {
|
||||
// component
|
||||
moduleUrl = componentModuleUrl(this._reflector, directiveType, dirMeta);
|
||||
assertArrayOfStrings('styles', dirMeta.styles);
|
||||
assertArrayOfStrings('styleUrls', dirMeta.styleUrls);
|
||||
assertInterpolationSymbols('interpolation', dirMeta.interpolation);
|
||||
@ -233,7 +216,7 @@ export class CompileMetadataResolver {
|
||||
}
|
||||
|
||||
let changeDetectionStrategy: ChangeDetectionStrategy = null;
|
||||
let viewProviders: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = [];
|
||||
let viewProviders: cpl.CompileProviderMetadata[] = [];
|
||||
let entryComponentMetadata: cpl.CompileIdentifierMetadata[] = [];
|
||||
let selector = dirMeta.selector;
|
||||
|
||||
@ -246,10 +229,9 @@ export class CompileMetadataResolver {
|
||||
`viewProviders for "${stringify(directiveType)}"`);
|
||||
}
|
||||
if (dirMeta.entryComponents) {
|
||||
entryComponentMetadata =
|
||||
flattenAndDedupeArray(dirMeta.entryComponents)
|
||||
.map((type) => this._getIdentifierMetadata(type, staticTypeModuleUrl(type)))
|
||||
.concat(entryComponentMetadata);
|
||||
entryComponentMetadata = flattenAndDedupeArray(dirMeta.entryComponents)
|
||||
.map((type) => this._getIdentifierMetadata(type))
|
||||
.concat(entryComponentMetadata);
|
||||
}
|
||||
if (!selector) {
|
||||
selector = this._schemaRegistry.getDefaultComponentElementName();
|
||||
@ -261,7 +243,7 @@ export class CompileMetadataResolver {
|
||||
}
|
||||
}
|
||||
|
||||
let providers: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = [];
|
||||
let providers: cpl.CompileProviderMetadata[] = [];
|
||||
if (isPresent(dirMeta.providers)) {
|
||||
providers = this._getProvidersMetadata(
|
||||
dirMeta.providers, entryComponentMetadata, `providers for "${stringify(directiveType)}"`);
|
||||
@ -273,11 +255,11 @@ export class CompileMetadataResolver {
|
||||
viewQueries = this._getQueriesMetadata(dirMeta.queries, true, directiveType);
|
||||
}
|
||||
|
||||
return cpl.CompileDirectiveMetadata.create({
|
||||
const metadata = cpl.CompileDirectiveMetadata.create({
|
||||
selector: selector,
|
||||
exportAs: dirMeta.exportAs,
|
||||
isComponent: !!nonNormalizedTemplateMetadata,
|
||||
type: this._getTypeMetadata(directiveType, moduleUrl),
|
||||
type: this._getTypeMetadata(directiveType),
|
||||
template: nonNormalizedTemplateMetadata,
|
||||
changeDetection: changeDetectionStrategy,
|
||||
inputs: dirMeta.inputs,
|
||||
@ -289,6 +271,7 @@ export class CompileMetadataResolver {
|
||||
viewQueries: viewQueries,
|
||||
entryComponents: entryComponentMetadata
|
||||
});
|
||||
return {metadata, annotation: dirMeta};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -420,8 +403,7 @@ export class CompileMetadataResolver {
|
||||
if (exportedModuleSummary) {
|
||||
exportedModules.push(exportedModuleSummary);
|
||||
} else {
|
||||
exportedNonModuleIdentifiers.push(
|
||||
this._getIdentifierMetadata(exportedType, staticTypeModuleUrl(exportedType)));
|
||||
exportedNonModuleIdentifiers.push(this._getIdentifierMetadata(exportedType));
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -435,15 +417,16 @@ export class CompileMetadataResolver {
|
||||
throw new Error(
|
||||
`Unexpected value '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`);
|
||||
}
|
||||
const declaredIdentifier =
|
||||
this._getIdentifierMetadata(declaredType, staticTypeModuleUrl(declaredType));
|
||||
const declaredIdentifier = this._getIdentifierMetadata(declaredType);
|
||||
if (this._directiveResolver.isDirective(declaredType)) {
|
||||
transitiveModule.directivesSet.add(declaredType);
|
||||
transitiveModule.directives.push(declaredIdentifier);
|
||||
declaredDirectives.push(declaredIdentifier);
|
||||
this._addTypeToModule(declaredType, moduleType);
|
||||
transitiveModule.directiveLoaders.push(
|
||||
() => this._loadDirectiveMetadata(declaredType, isSync));
|
||||
const loader = this._loadDirectiveMetadata(declaredType, isSync);
|
||||
if (loader) {
|
||||
transitiveModule.directiveLoaders.push(loader);
|
||||
}
|
||||
} else if (this._pipeResolver.isPipe(declaredType)) {
|
||||
transitiveModule.pipesSet.add(declaredType);
|
||||
transitiveModule.pipes.push(declaredIdentifier);
|
||||
@ -479,8 +462,7 @@ export class CompileMetadataResolver {
|
||||
|
||||
if (meta.entryComponents) {
|
||||
entryComponents.push(
|
||||
...flattenAndDedupeArray(meta.entryComponents)
|
||||
.map(type => this._getTypeMetadata(type, staticTypeModuleUrl(type))));
|
||||
...flattenAndDedupeArray(meta.entryComponents).map(type => this._getTypeMetadata(type)));
|
||||
}
|
||||
|
||||
if (meta.bootstrap) {
|
||||
@ -489,7 +471,7 @@ export class CompileMetadataResolver {
|
||||
throw new Error(
|
||||
`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);
|
||||
}
|
||||
@ -504,7 +486,7 @@ export class CompileMetadataResolver {
|
||||
transitiveModule.providers.push(...providers);
|
||||
|
||||
compileMeta = new cpl.CompileNgModuleMetadata({
|
||||
type: this._getTypeMetadata(moduleType, staticTypeModuleUrl(moduleType)),
|
||||
type: this._getTypeMetadata(moduleType),
|
||||
providers,
|
||||
entryComponents,
|
||||
bootstrapComponents,
|
||||
@ -575,35 +557,25 @@ export class CompileMetadataResolver {
|
||||
transitiveModules, providers, entryComponents, directives, pipes, directiveLoaders);
|
||||
}
|
||||
|
||||
private _getIdentifierMetadata(type: Type<any>, moduleUrl: string):
|
||||
cpl.CompileIdentifierMetadata {
|
||||
private _getIdentifierMetadata(type: Type<any>): cpl.CompileIdentifierMetadata {
|
||||
type = resolveForwardRef(type);
|
||||
return new cpl.CompileIdentifierMetadata(
|
||||
{name: this.sanitizeTokenName(type), moduleUrl, reference: type});
|
||||
return {reference: type};
|
||||
}
|
||||
|
||||
private _getTypeMetadata(type: Type<any>, moduleUrl: string, dependencies: any[] = null):
|
||||
cpl.CompileTypeMetadata {
|
||||
const identifier = this._getIdentifierMetadata(type, moduleUrl);
|
||||
return new cpl.CompileTypeMetadata({
|
||||
name: identifier.name,
|
||||
moduleUrl: identifier.moduleUrl,
|
||||
private _getTypeMetadata(type: Type<any>, dependencies: any[] = null): cpl.CompileTypeMetadata {
|
||||
const identifier = this._getIdentifierMetadata(type);
|
||||
return {
|
||||
reference: identifier.reference,
|
||||
diDeps: this._getDependenciesMetadata(identifier.reference, dependencies),
|
||||
lifecycleHooks:
|
||||
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 {
|
||||
factory = resolveForwardRef(factory);
|
||||
return new cpl.CompileFactoryMetadata({
|
||||
name: this.sanitizeTokenName(factory),
|
||||
moduleUrl,
|
||||
reference: factory,
|
||||
diDeps: this._getDependenciesMetadata(factory, dependencies)
|
||||
});
|
||||
return {reference: factory, diDeps: this._getDependenciesMetadata(factory, dependencies)};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -641,7 +613,7 @@ export class CompileMetadataResolver {
|
||||
const pipeAnnotation = this._pipeResolver.resolve(pipeType);
|
||||
|
||||
const pipeMeta = new cpl.CompilePipeMetadata({
|
||||
type: this._getTypeMetadata(pipeType, staticTypeModuleUrl(pipeType)),
|
||||
type: this._getTypeMetadata(pipeType),
|
||||
name: pipeAnnotation.name,
|
||||
pure: pipeAnnotation.pure
|
||||
});
|
||||
@ -689,14 +661,14 @@ export class CompileMetadataResolver {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new cpl.CompileDiDependencyMetadata({
|
||||
return {
|
||||
isAttribute,
|
||||
isHost,
|
||||
isSelf,
|
||||
isSkipSelf,
|
||||
isOptional,
|
||||
token: this._getTokenMetadata(token)
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
@ -714,62 +686,50 @@ export class CompileMetadataResolver {
|
||||
token = resolveForwardRef(token);
|
||||
let compileToken: cpl.CompileTokenMetadata;
|
||||
if (typeof token === 'string') {
|
||||
compileToken = new cpl.CompileTokenMetadata({value: token});
|
||||
compileToken = {value: token};
|
||||
} else {
|
||||
compileToken = new cpl.CompileTokenMetadata({
|
||||
identifier: new cpl.CompileIdentifierMetadata({
|
||||
reference: token,
|
||||
name: this.sanitizeTokenName(token),
|
||||
moduleUrl: staticTypeModuleUrl(token)
|
||||
})
|
||||
});
|
||||
compileToken = {identifier: {reference: token}};
|
||||
}
|
||||
return compileToken;
|
||||
}
|
||||
|
||||
private _getProvidersMetadata(
|
||||
providers: Provider[], targetEntryComponents: cpl.CompileIdentifierMetadata[],
|
||||
debugInfo?: string): Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> {
|
||||
const compileProviders: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = [];
|
||||
debugInfo?: string,
|
||||
compileProviders: cpl.CompileProviderMetadata[] = []): cpl.CompileProviderMetadata[] {
|
||||
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)) {
|
||||
compileProvider = this._getProvidersMetadata(provider, targetEntryComponents, debugInfo);
|
||||
} 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 {
|
||||
compileProvider = this.getProviderMetadata(provider);
|
||||
}
|
||||
} else if (isValidType(provider)) {
|
||||
compileProvider = this._getTypeMetadata(provider, staticTypeModuleUrl(provider));
|
||||
this._getProvidersMetadata(provider, targetEntryComponents, debugInfo, compileProviders);
|
||||
} else {
|
||||
const providersInfo =
|
||||
(<string[]>providers.reduce(
|
||||
(soFar: string[], seenProvider: any, seenProviderIdx: number) => {
|
||||
if (seenProviderIdx < providerIdx) {
|
||||
soFar.push(`${stringify(seenProvider)}`);
|
||||
} else if (seenProviderIdx == providerIdx) {
|
||||
soFar.push(`?${stringify(seenProvider)}?`);
|
||||
} else if (seenProviderIdx == providerIdx + 1) {
|
||||
soFar.push('...');
|
||||
}
|
||||
return soFar;
|
||||
},
|
||||
[]))
|
||||
.join(', ');
|
||||
|
||||
throw new Error(
|
||||
`Invalid ${debugInfo ? debugInfo : 'provider'} - only instances of Provider and Type are allowed, got: [${providersInfo}]`);
|
||||
}
|
||||
if (compileProvider) {
|
||||
compileProviders.push(compileProvider);
|
||||
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)) {
|
||||
providerMeta = new cpl.ProviderMeta(provider, {useClass: provider});
|
||||
} else {
|
||||
const providersInfo =
|
||||
(<string[]>providers.reduce(
|
||||
(soFar: string[], seenProvider: any, seenProviderIdx: number) => {
|
||||
if (seenProviderIdx < providerIdx) {
|
||||
soFar.push(`${stringify(seenProvider)}`);
|
||||
} else if (seenProviderIdx == providerIdx) {
|
||||
soFar.push(`?${stringify(seenProvider)}?`);
|
||||
} else if (seenProviderIdx == providerIdx + 1) {
|
||||
soFar.push('...');
|
||||
}
|
||||
return soFar;
|
||||
},
|
||||
[]))
|
||||
.join(', ');
|
||||
throw new Error(
|
||||
`Invalid ${debugInfo ? debugInfo : 'provider'} - only instances of Provider and Type are allowed, got: [${providersInfo}]`);
|
||||
}
|
||||
if (providerMeta.token === resolveIdentifier(Identifiers.ANALYZE_FOR_ENTRY_COMPONENTS)) {
|
||||
targetEntryComponents.push(...this._getEntryComponentsFromProvider(providerMeta));
|
||||
} else {
|
||||
compileProviders.push(this.getProviderMetadata(providerMeta));
|
||||
}
|
||||
}
|
||||
});
|
||||
return compileProviders;
|
||||
@ -788,7 +748,7 @@ export class CompileMetadataResolver {
|
||||
throw new Error(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports 'multi = true'!`);
|
||||
}
|
||||
|
||||
convertToCompileValue(provider.useValue, collectedIdentifiers);
|
||||
extractIdentifiers(provider.useValue, collectedIdentifiers);
|
||||
collectedIdentifiers.forEach((identifier) => {
|
||||
if (this._directiveResolver.isDirective(identifier.reference)) {
|
||||
components.push(identifier);
|
||||
@ -801,26 +761,29 @@ export class CompileMetadataResolver {
|
||||
let compileDeps: cpl.CompileDiDependencyMetadata[];
|
||||
let compileTypeMetadata: cpl.CompileTypeMetadata = null;
|
||||
let compileFactoryMetadata: cpl.CompileFactoryMetadata = null;
|
||||
let token: cpl.CompileTokenMetadata = this._getTokenMetadata(provider.token);
|
||||
|
||||
if (provider.useClass) {
|
||||
compileTypeMetadata = this._getTypeMetadata(
|
||||
provider.useClass, staticTypeModuleUrl(provider.useClass), provider.dependencies);
|
||||
compileTypeMetadata = this._getTypeMetadata(provider.useClass, provider.dependencies);
|
||||
compileDeps = compileTypeMetadata.diDeps;
|
||||
if (provider.token === provider.useClass) {
|
||||
// use the compileTypeMetadata as it contains information about lifecycleHooks...
|
||||
token = {identifier: compileTypeMetadata};
|
||||
}
|
||||
} else if (provider.useFactory) {
|
||||
compileFactoryMetadata = this._getFactoryMetadata(
|
||||
provider.useFactory, staticTypeModuleUrl(provider.useFactory), provider.dependencies);
|
||||
compileFactoryMetadata = this._getFactoryMetadata(provider.useFactory, provider.dependencies);
|
||||
compileDeps = compileFactoryMetadata.diDeps;
|
||||
}
|
||||
|
||||
return new cpl.CompileProviderMetadata({
|
||||
token: this._getTokenMetadata(provider.token),
|
||||
return {
|
||||
token: token,
|
||||
useClass: compileTypeMetadata,
|
||||
useValue: convertToCompileValue(provider.useValue, []),
|
||||
useValue: provider.useValue,
|
||||
useFactory: compileFactoryMetadata,
|
||||
useExisting: provider.useExisting ? this._getTokenMetadata(provider.useExisting) : null,
|
||||
deps: compileDeps,
|
||||
multi: provider.multi
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
private _getQueriesMetadata(
|
||||
@ -854,12 +817,12 @@ export class CompileMetadataResolver {
|
||||
selectors = [this._getTokenMetadata(q.selector)];
|
||||
}
|
||||
|
||||
return new cpl.CompileQueryMetadata({
|
||||
return {
|
||||
selectors,
|
||||
first: q.first,
|
||||
descendants: q.descendants, propertyName,
|
||||
read: q.read ? this._getTokenMetadata(q.read) : null
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -925,14 +888,10 @@ function isValidType(value: any): boolean {
|
||||
return isStaticSymbol(value) || (value instanceof Type);
|
||||
}
|
||||
|
||||
function staticTypeModuleUrl(value: any): string {
|
||||
return isStaticSymbol(value) ? value.filePath : null;
|
||||
}
|
||||
|
||||
function componentModuleUrl(
|
||||
export function componentModuleUrl(
|
||||
reflector: ReflectorReader, type: Type<any>, cmpMetadata: Component): string {
|
||||
if (isStaticSymbol(type)) {
|
||||
return staticTypeModuleUrl(type);
|
||||
return type.filePath;
|
||||
}
|
||||
|
||||
const moduleId = cmpMetadata.moduleId;
|
||||
@ -949,21 +908,12 @@ function componentModuleUrl(
|
||||
return reflector.importUri(type);
|
||||
}
|
||||
|
||||
function convertToCompileValue(
|
||||
value: any, targetIdentifiers: cpl.CompileIdentifierMetadata[]): any {
|
||||
return visitValue(value, new _CompileValueConverter(), targetIdentifiers);
|
||||
function extractIdentifiers(value: any, targetIdentifiers: cpl.CompileIdentifierMetadata[]) {
|
||||
visitValue(value, new _CompileValueConverter(), targetIdentifiers);
|
||||
}
|
||||
|
||||
class _CompileValueConverter extends ValueTransformer {
|
||||
visitOther(value: any, targetIdentifiers: cpl.CompileIdentifierMetadata[]): any {
|
||||
let identifier: cpl.CompileIdentifierMetadata;
|
||||
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;
|
||||
targetIdentifiers.push({reference: value});
|
||||
}
|
||||
}
|
||||
|
@ -8,10 +8,10 @@
|
||||
|
||||
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 {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 * as o from './output/output_ast';
|
||||
import {convertValueToOutputAst} from './output/value_util';
|
||||
@ -35,9 +35,10 @@ export class NgModuleCompileResult {
|
||||
export class NgModuleCompiler {
|
||||
compile(ngModuleMeta: CompileNgModuleMetadata, extraProviders: CompileProviderMetadata[]):
|
||||
NgModuleCompileResult {
|
||||
const sourceFileName = isPresent(ngModuleMeta.type.moduleUrl) ?
|
||||
`in NgModule ${ngModuleMeta.type.name} in ${ngModuleMeta.type.moduleUrl}` :
|
||||
`in NgModule ${ngModuleMeta.type.name}`;
|
||||
const moduleUrl = identifierModuleUrl(ngModuleMeta.type);
|
||||
const sourceFileName = isPresent(moduleUrl) ?
|
||||
`in NgModule ${identifierName(ngModuleMeta.type)} in ${moduleUrl}` :
|
||||
`in NgModule ${identifierName(ngModuleMeta.type)}`;
|
||||
const sourceFile = new ParseSourceFile('', sourceFileName);
|
||||
const sourceSpan = new ParseSourceSpan(
|
||||
new ParseLocation(sourceFile, null, null, null),
|
||||
@ -46,7 +47,7 @@ export class NgModuleCompiler {
|
||||
const bootstrapComponentFactories: CompileIdentifierMetadata[] = [];
|
||||
const entryComponentFactories =
|
||||
ngModuleMeta.transitiveModule.entryComponents.map((entryComponent) => {
|
||||
const id = new CompileIdentifierMetadata({name: entryComponent.name});
|
||||
const id: CompileIdentifierMetadata = {reference: null};
|
||||
if (ngModuleMeta.bootstrapComponents.indexOf(entryComponent) > -1) {
|
||||
bootstrapComponentFactories.push(id);
|
||||
}
|
||||
@ -59,21 +60,21 @@ export class NgModuleCompiler {
|
||||
const providerParser = new NgModuleProviderAnalyzer(ngModuleMeta, extraProviders, sourceSpan);
|
||||
providerParser.parse().forEach((provider) => builder.addProvider(provider));
|
||||
const injectorClass = builder.build();
|
||||
const ngModuleFactoryVar = `${ngModuleMeta.type.name}NgFactory`;
|
||||
const ngModuleFactoryVar = `${identifierName(ngModuleMeta.type)}NgFactory`;
|
||||
const ngModuleFactoryStmt =
|
||||
o.variable(ngModuleFactoryVar)
|
||||
.set(o.importExpr(resolveIdentifier(Identifiers.NgModuleFactory))
|
||||
.set(o.importExpr(createIdentifier(Identifiers.NgModuleFactory))
|
||||
.instantiate(
|
||||
[o.variable(injectorClass.name), o.importExpr(ngModuleMeta.type)],
|
||||
o.importType(
|
||||
resolveIdentifier(Identifiers.NgModuleFactory),
|
||||
createIdentifier(Identifiers.NgModuleFactory),
|
||||
[o.importType(ngModuleMeta.type)], [o.TypeModifier.Const])))
|
||||
.toDeclStmt(null, [o.StmtModifier.Final]);
|
||||
|
||||
const stmts: o.Statement[] = [injectorClass, ngModuleFactoryStmt];
|
||||
if (ngModuleMeta.id) {
|
||||
const registerFactoryStmt =
|
||||
o.importExpr(resolveIdentifier(Identifiers.RegisterModuleFactoryFn))
|
||||
o.importExpr(createIdentifier(Identifiers.RegisterModuleFactoryFn))
|
||||
.callFn([o.literal(ngModuleMeta.id), o.variable(ngModuleFactoryVar)])
|
||||
.toStmt();
|
||||
stmts.push(registerFactoryStmt);
|
||||
@ -102,7 +103,7 @@ class _InjectorBuilder implements ClassBuilder {
|
||||
addProvider(resolvedProvider: ProviderAst) {
|
||||
const providerValueExpressions =
|
||||
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(
|
||||
propName, resolvedProvider, providerValueExpressions, resolvedProvider.multiProvider,
|
||||
resolvedProvider.eager);
|
||||
@ -110,12 +111,12 @@ class _InjectorBuilder implements ClassBuilder {
|
||||
this._destroyStmts.push(instance.callMethod('ngOnDestroy', []).toStmt());
|
||||
}
|
||||
this._tokens.push(resolvedProvider.token);
|
||||
this._instances.set(resolvedProvider.token.reference, instance);
|
||||
this._instances.set(tokenReference(resolvedProvider.token), instance);
|
||||
}
|
||||
|
||||
build(): o.ClassStmt {
|
||||
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(
|
||||
InjectMethodVars.token.identical(createDiTokenExpression(token)),
|
||||
[new o.ReturnStatement(providerExpr)]);
|
||||
@ -143,13 +144,13 @@ class _InjectorBuilder implements ClassBuilder {
|
||||
o.literalArr(this._bootstrapComponentFactories.map(
|
||||
(componentFactory) => o.importExpr(componentFactory)))
|
||||
];
|
||||
const injClassName = `${this._ngModuleMeta.type.name}Injector`;
|
||||
const injClassName = `${identifierName(this._ngModuleMeta.type)}Injector`;
|
||||
return createClassStmt({
|
||||
name: injClassName,
|
||||
ctorParams: [new o.FnParam(
|
||||
InjectorProps.parent.name, o.importType(resolveIdentifier(Identifiers.Injector)))],
|
||||
InjectorProps.parent.name, o.importType(createIdentifier(Identifiers.Injector)))],
|
||||
parent: o.importExpr(
|
||||
resolveIdentifier(Identifiers.NgModuleInjector), [o.importType(this._ngModuleMeta.type)]),
|
||||
createIdentifier(Identifiers.NgModuleInjector), [o.importType(this._ngModuleMeta.type)]),
|
||||
parentArgs: parentArgs,
|
||||
builders: [{methods}, this]
|
||||
});
|
||||
@ -158,7 +159,7 @@ class _InjectorBuilder implements ClassBuilder {
|
||||
private _getProviderValue(provider: CompileProviderMetadata): o.Expression {
|
||||
let result: o.Expression;
|
||||
if (isPresent(provider.useExisting)) {
|
||||
result = this._getDependency(new CompileDiDependencyMetadata({token: provider.useExisting}));
|
||||
result = this._getDependency({token: provider.useExisting});
|
||||
} else if (isPresent(provider.useFactory)) {
|
||||
const deps = provider.deps || provider.useFactory.diDeps;
|
||||
const depsExpr = deps.map((dep) => this._getDependency(dep));
|
||||
@ -215,13 +216,12 @@ class _InjectorBuilder implements ClassBuilder {
|
||||
}
|
||||
if (!dep.isSkipSelf) {
|
||||
if (dep.token &&
|
||||
(dep.token.reference === resolveIdentifierToken(Identifiers.Injector).reference ||
|
||||
dep.token.reference ===
|
||||
resolveIdentifierToken(Identifiers.ComponentFactoryResolver).reference)) {
|
||||
(tokenReference(dep.token) === resolveIdentifier(Identifiers.Injector) ||
|
||||
tokenReference(dep.token) === resolveIdentifier(Identifiers.ComponentFactoryResolver))) {
|
||||
result = o.THIS_EXPR;
|
||||
}
|
||||
if (!result) {
|
||||
result = this._instances.get(dep.token.reference);
|
||||
result = this._instances.get(tokenReference(dep.token));
|
||||
}
|
||||
}
|
||||
if (!result) {
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
import {Injectable, NgModule, Type} from '@angular/core';
|
||||
|
||||
import {ListWrapper} from './facade/collection';
|
||||
import {isPresent, stringify} from './facade/lang';
|
||||
import {ReflectorReader, reflector} from './private_import_core';
|
||||
|
||||
@ -25,7 +26,8 @@ export class NgModuleResolver {
|
||||
isNgModule(type: any) { return this._reflector.annotations(type).some(_isNgModuleMetadata); }
|
||||
|
||||
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)) {
|
||||
return ngModuleMeta;
|
||||
|
@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
|
||||
import {identifierModuleUrl, identifierName} from '../compile_metadata';
|
||||
import {isBlank, isPresent} from '../facade/lang';
|
||||
|
||||
import {EmitterVisitorContext, OutputEmitter} from './abstract_emitter';
|
||||
@ -38,18 +39,21 @@ class JsEmitterVisitor extends AbstractJsEmitterVisitor {
|
||||
constructor(private _moduleUrl: string) { super(); }
|
||||
|
||||
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}`);
|
||||
}
|
||||
if (isPresent(ast.value.moduleUrl) && ast.value.moduleUrl != this._moduleUrl) {
|
||||
let prefix = this.importsWithPrefixes.get(ast.value.moduleUrl);
|
||||
if (isPresent(moduleUrl) && moduleUrl != this._moduleUrl) {
|
||||
let prefix = this.importsWithPrefixes.get(moduleUrl);
|
||||
if (isBlank(prefix)) {
|
||||
prefix = `import${this.importsWithPrefixes.size}`;
|
||||
this.importsWithPrefixes.set(ast.value.moduleUrl, prefix);
|
||||
this.importsWithPrefixes.set(moduleUrl, prefix);
|
||||
}
|
||||
ctx.print(`${prefix}.`);
|
||||
}
|
||||
ctx.print(ast.value.name);
|
||||
ctx.print(name);
|
||||
return null;
|
||||
}
|
||||
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(
|
||||
public value: CompileIdentifierMetadata, public typeParams: Type[] = null,
|
||||
public value: Expression, public typeParams: Type[] = null,
|
||||
modifiers: TypeModifier[] = null) {
|
||||
super(modifiers);
|
||||
}
|
||||
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 {
|
||||
visitBuiltintType(type: BuiltinType, context: any): any;
|
||||
visitExternalType(type: ExternalType, context: any): any;
|
||||
visitExpressionType(type: ExpressionType, context: any): any;
|
||||
visitArrayType(type: ArrayType, context: any): any;
|
||||
visitMapType(type: MapType, context: any): any;
|
||||
}
|
||||
@ -876,8 +876,14 @@ export function importExpr(id: CompileIdentifierMetadata, typeParams: Type[] = n
|
||||
|
||||
export function importType(
|
||||
id: CompileIdentifierMetadata, typeParams: Type[] = null,
|
||||
typeModifiers: TypeModifier[] = null): ExternalType {
|
||||
return isPresent(id) ? new ExternalType(id, typeParams, typeModifiers) : null;
|
||||
typeModifiers: TypeModifier[] = null): ExpressionType {
|
||||
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 {
|
||||
|
@ -6,8 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {identifierName} from '../compile_metadata';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {sanitizeIdentifier} from '../util';
|
||||
|
||||
import {EmitterVisitorContext} from './abstract_emitter';
|
||||
import {AbstractJsEmitterVisitor} from './abstract_js_emitter';
|
||||
@ -52,8 +52,8 @@ class JitEmitterVisitor extends AbstractJsEmitterVisitor {
|
||||
if (id === -1) {
|
||||
id = this._evalArgValues.length;
|
||||
this._evalArgValues.push(value);
|
||||
const name = isPresent(ast.value.name) ? sanitizeIdentifier(ast.value.name) : 'val';
|
||||
this._evalArgNames.push(sanitizeIdentifier(`jit_${name}${id}`));
|
||||
const name = identifierName(ast.value) || 'val';
|
||||
this._evalArgNames.push(`jit_${name}${id}`);
|
||||
}
|
||||
ctx.print(this._evalArgNames[id]);
|
||||
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 {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;
|
||||
}
|
||||
|
||||
visitExternalType(ast: o.ExternalType, ctx: EmitterVisitorContext): any {
|
||||
this._visitIdentifier(ast.value, ast.typeParams, ctx);
|
||||
visitExpressionType(ast: o.ExpressionType, ctx: EmitterVisitorContext): any {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -317,14 +322,16 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||
|
||||
private _visitIdentifier(
|
||||
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}`);
|
||||
}
|
||||
if (isPresent(value.moduleUrl) && value.moduleUrl != this._moduleUrl) {
|
||||
let prefix = this.importsWithPrefixes.get(value.moduleUrl);
|
||||
if (isPresent(moduleUrl) && moduleUrl != this._moduleUrl) {
|
||||
let prefix = this.importsWithPrefixes.get(moduleUrl);
|
||||
if (isBlank(prefix)) {
|
||||
prefix = `import${this.importsWithPrefixes.size}`;
|
||||
this.importsWithPrefixes.set(value.moduleUrl, prefix);
|
||||
this.importsWithPrefixes.set(moduleUrl, prefix);
|
||||
}
|
||||
ctx.print(`${prefix}.`);
|
||||
}
|
||||
@ -333,7 +340,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||
ctx.print('.');
|
||||
ctx.print(value.reference.members.join('.'));
|
||||
} else {
|
||||
ctx.print(value.name);
|
||||
ctx.print(name);
|
||||
}
|
||||
if (isPresent(typeParams) && typeParams.length > 0) {
|
||||
ctx.print(`<`);
|
||||
|
@ -30,12 +30,10 @@ class _ValueOutputAstTransformer implements ValueTransformer {
|
||||
visitPrimitive(value: any, type: o.Type): o.Expression { return o.literal(value, type); }
|
||||
|
||||
visitOther(value: any, type: o.Type): o.Expression {
|
||||
if (value instanceof CompileIdentifierMetadata) {
|
||||
return o.importExpr(value);
|
||||
} else if (value instanceof o.Expression) {
|
||||
if (value instanceof o.Expression) {
|
||||
return value;
|
||||
} 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 {ListWrapper} from './facade/collection';
|
||||
import {isPresent, stringify} from './facade/lang';
|
||||
import {ReflectorReader, reflector} from './private_import_core';
|
||||
|
||||
@ -37,7 +38,7 @@ export class PipeResolver {
|
||||
resolve(type: Type<any>, throwIfNotFound = true): Pipe {
|
||||
const metas = this._reflector.annotations(resolveForwardRef(type));
|
||||
if (isPresent(metas)) {
|
||||
const annotation = metas.find(_isPipeMetadata);
|
||||
const annotation = ListWrapper.findLast(metas, _isPipeMetadata);
|
||||
if (isPresent(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 {Identifiers, resolveIdentifierToken} from './identifiers';
|
||||
import {Identifiers, createIdentifierToken, resolveIdentifier} from './identifiers';
|
||||
import {ParseError, ParseSourceSpan} from './parse_util';
|
||||
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) {
|
||||
this.viewQueries = _getViewQueries(component);
|
||||
this.viewProviders = new Map<any, boolean>();
|
||||
_normalizeProviders(component.viewProviders, sourceSpan, this.errors).forEach((provider) => {
|
||||
if (isBlank(this.viewProviders.get(provider.token.reference))) {
|
||||
this.viewProviders.set(provider.token.reference, true);
|
||||
component.viewProviders.forEach((provider) => {
|
||||
if (isBlank(this.viewProviders.get(tokenReference(provider.token)))) {
|
||||
this.viewProviders.set(tokenReference(provider.token), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -62,17 +62,14 @@ export class ProviderElementContext {
|
||||
Array.from(this._allProviders.values()).forEach((provider) => {
|
||||
this._addQueryReadsTo(provider.token, queriedTokens);
|
||||
});
|
||||
refs.forEach((refAst) => {
|
||||
this._addQueryReadsTo(new CompileTokenMetadata({value: refAst.name}), queriedTokens);
|
||||
});
|
||||
if (isPresent(
|
||||
queriedTokens.get(resolveIdentifierToken(Identifiers.ViewContainerRef).reference))) {
|
||||
refs.forEach((refAst) => { this._addQueryReadsTo({value: refAst.name}, queriedTokens); });
|
||||
if (isPresent(queriedTokens.get(resolveIdentifier(Identifiers.ViewContainerRef)))) {
|
||||
this._hasViewContainer = true;
|
||||
}
|
||||
|
||||
// create the providers that we know are eager first
|
||||
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) {
|
||||
this._getOrCreateLocalProvider(provider.providerType, provider.token, true);
|
||||
}
|
||||
@ -104,8 +101,8 @@ export class ProviderElementContext {
|
||||
private _addQueryReadsTo(token: CompileTokenMetadata, queryReadTokens: Map<any, boolean>) {
|
||||
this._getQueriesFor(token).forEach((query) => {
|
||||
const queryReadToken = query.read || token;
|
||||
if (isBlank(queryReadTokens.get(queryReadToken.reference))) {
|
||||
queryReadTokens.set(queryReadToken.reference, true);
|
||||
if (isBlank(queryReadTokens.get(tokenReference(queryReadToken)))) {
|
||||
queryReadTokens.set(tokenReference(queryReadToken), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -116,7 +113,7 @@ export class ProviderElementContext {
|
||||
let distance = 0;
|
||||
let queries: CompileQueryMetadata[];
|
||||
while (currentEl !== null) {
|
||||
queries = currentEl._contentQueries.get(token.reference);
|
||||
queries = currentEl._contentQueries.get(tokenReference(token));
|
||||
if (isPresent(queries)) {
|
||||
result.push(...queries.filter((query) => query.descendants || distance <= 1));
|
||||
}
|
||||
@ -125,7 +122,7 @@ export class ProviderElementContext {
|
||||
}
|
||||
currentEl = currentEl._parent;
|
||||
}
|
||||
queries = this.viewContext.viewQueries.get(token.reference);
|
||||
queries = this.viewContext.viewQueries.get(tokenReference(token));
|
||||
if (isPresent(queries)) {
|
||||
result.push(...queries);
|
||||
}
|
||||
@ -136,7 +133,7 @@ export class ProviderElementContext {
|
||||
private _getOrCreateLocalProvider(
|
||||
requestingProviderType: ProviderAstType, token: CompileTokenMetadata,
|
||||
eager: boolean): ProviderAst {
|
||||
const resolvedProvider = this._allProviders.get(token.reference);
|
||||
const resolvedProvider = this._allProviders.get(tokenReference(token));
|
||||
if (!resolvedProvider || ((requestingProviderType === ProviderAstType.Directive ||
|
||||
requestingProviderType === ProviderAstType.PublicService) &&
|
||||
resolvedProvider.providerType === ProviderAstType.PrivateService) ||
|
||||
@ -145,24 +142,23 @@ export class ProviderElementContext {
|
||||
resolvedProvider.providerType === ProviderAstType.Builtin)) {
|
||||
return null;
|
||||
}
|
||||
let transformedProviderAst = this._transformedProviders.get(token.reference);
|
||||
let transformedProviderAst = this._transformedProviders.get(tokenReference(token));
|
||||
if (isPresent(transformedProviderAst)) {
|
||||
return transformedProviderAst;
|
||||
}
|
||||
if (isPresent(this._seenProviders.get(token.reference))) {
|
||||
if (isPresent(this._seenProviders.get(tokenReference(token)))) {
|
||||
this.viewContext.errors.push(new ProviderError(
|
||||
`Cannot instantiate cyclic dependency! ${token.name}`, this._sourceSpan));
|
||||
`Cannot instantiate cyclic dependency! ${tokenName(token)}`, this._sourceSpan));
|
||||
return null;
|
||||
}
|
||||
this._seenProviders.set(token.reference, true);
|
||||
this._seenProviders.set(tokenReference(token), true);
|
||||
const transformedProviders = resolvedProvider.providers.map((provider) => {
|
||||
let transformedUseValue = provider.useValue;
|
||||
let transformedUseExisting = provider.useExisting;
|
||||
let transformedDeps: CompileDiDependencyMetadata[];
|
||||
if (isPresent(provider.useExisting)) {
|
||||
const existingDiDep = this._getDependency(
|
||||
resolvedProvider.providerType,
|
||||
new CompileDiDependencyMetadata({token: provider.useExisting}), eager);
|
||||
resolvedProvider.providerType, {token: provider.useExisting}, eager);
|
||||
if (isPresent(existingDiDep.token)) {
|
||||
transformedUseExisting = existingDiDep.token;
|
||||
} else {
|
||||
@ -186,7 +182,7 @@ export class ProviderElementContext {
|
||||
});
|
||||
transformedProviderAst =
|
||||
_transformProviderAst(resolvedProvider, {eager: eager, providers: transformedProviders});
|
||||
this._transformedProviders.set(token.reference, transformedProviderAst);
|
||||
this._transformedProviders.set(tokenReference(token), transformedProviderAst);
|
||||
return transformedProviderAst;
|
||||
}
|
||||
|
||||
@ -195,28 +191,25 @@ export class ProviderElementContext {
|
||||
eager: boolean = null): CompileDiDependencyMetadata {
|
||||
if (dep.isAttribute) {
|
||||
const attrValue = this._attrs[dep.token.value];
|
||||
return new CompileDiDependencyMetadata(
|
||||
{isValue: true, value: attrValue == null ? null : attrValue});
|
||||
return {isValue: true, value: attrValue == null ? null : attrValue};
|
||||
}
|
||||
|
||||
if (isPresent(dep.token)) {
|
||||
// access builtints
|
||||
if ((requestingProviderType === ProviderAstType.Directive ||
|
||||
requestingProviderType === ProviderAstType.Component)) {
|
||||
if (dep.token.reference === resolveIdentifierToken(Identifiers.Renderer).reference ||
|
||||
dep.token.reference === resolveIdentifierToken(Identifiers.ElementRef).reference ||
|
||||
dep.token.reference ===
|
||||
resolveIdentifierToken(Identifiers.ChangeDetectorRef).reference ||
|
||||
dep.token.reference === resolveIdentifierToken(Identifiers.TemplateRef).reference) {
|
||||
if (tokenReference(dep.token) === resolveIdentifier(Identifiers.Renderer) ||
|
||||
tokenReference(dep.token) === resolveIdentifier(Identifiers.ElementRef) ||
|
||||
tokenReference(dep.token) === resolveIdentifier(Identifiers.ChangeDetectorRef) ||
|
||||
tokenReference(dep.token) === resolveIdentifier(Identifiers.TemplateRef)) {
|
||||
return dep;
|
||||
}
|
||||
if (dep.token.reference ===
|
||||
resolveIdentifierToken(Identifiers.ViewContainerRef).reference) {
|
||||
if (tokenReference(dep.token) === resolveIdentifier(Identifiers.ViewContainerRef)) {
|
||||
this._hasViewContainer = true;
|
||||
}
|
||||
}
|
||||
// access the injector
|
||||
if (dep.token.reference === resolveIdentifierToken(Identifiers.Injector).reference) {
|
||||
if (tokenReference(dep.token) === resolveIdentifier(Identifiers.Injector)) {
|
||||
return dep;
|
||||
}
|
||||
// access providers
|
||||
@ -238,7 +231,7 @@ export class ProviderElementContext {
|
||||
}
|
||||
if (dep.isSelf) {
|
||||
if (!result && dep.isOptional) {
|
||||
result = new CompileDiDependencyMetadata({isValue: true, value: null});
|
||||
result = {isValue: true, value: null};
|
||||
}
|
||||
} else {
|
||||
// check parent elements
|
||||
@ -252,20 +245,18 @@ export class ProviderElementContext {
|
||||
}
|
||||
// check @Host restriction
|
||||
if (!result) {
|
||||
if (!dep.isHost || this.viewContext.component.type.isHost ||
|
||||
this.viewContext.component.type.reference === dep.token.reference ||
|
||||
isPresent(this.viewContext.viewProviders.get(dep.token.reference))) {
|
||||
if (!dep.isHost || this.viewContext.component.isHost ||
|
||||
this.viewContext.component.type.reference === tokenReference(dep.token) ||
|
||||
isPresent(this.viewContext.viewProviders.get(tokenReference(dep.token)))) {
|
||||
result = dep;
|
||||
} else {
|
||||
result = dep.isOptional ?
|
||||
result = new CompileDiDependencyMetadata({isValue: true, value: null}) :
|
||||
null;
|
||||
result = dep.isOptional ? result = {isValue: true, value: null} : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!result) {
|
||||
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;
|
||||
}
|
||||
@ -284,16 +275,14 @@ export class NgModuleProviderAnalyzer {
|
||||
this._allProviders = new Map<any, ProviderAst>();
|
||||
const ngModuleTypes = ngModule.transitiveModule.modules.map((moduleMeta) => moduleMeta.type);
|
||||
ngModuleTypes.forEach((ngModuleType: CompileTypeMetadata) => {
|
||||
const ngModuleProvider = new CompileProviderMetadata(
|
||||
{token: new CompileTokenMetadata({identifier: ngModuleType}), useClass: ngModuleType});
|
||||
const ngModuleProvider = {token: {identifier: ngModuleType}, useClass: ngModuleType};
|
||||
_resolveProviders(
|
||||
[ngModuleProvider], ProviderAstType.PublicService, true, sourceSpan, this._errors,
|
||||
this._allProviders);
|
||||
});
|
||||
_resolveProviders(
|
||||
_normalizeProviders(
|
||||
ngModule.transitiveModule.providers.concat(extraProviders), sourceSpan, this._errors),
|
||||
ProviderAstType.PublicService, false, sourceSpan, this._errors, this._allProviders);
|
||||
ngModule.transitiveModule.providers.concat(extraProviders), ProviderAstType.PublicService,
|
||||
false, sourceSpan, this._errors, this._allProviders);
|
||||
}
|
||||
|
||||
parse(): ProviderAst[] {
|
||||
@ -308,28 +297,28 @@ export class NgModuleProviderAnalyzer {
|
||||
}
|
||||
|
||||
private _getOrCreateLocalProvider(token: CompileTokenMetadata, eager: boolean): ProviderAst {
|
||||
const resolvedProvider = this._allProviders.get(token.reference);
|
||||
const resolvedProvider = this._allProviders.get(tokenReference(token));
|
||||
if (!resolvedProvider) {
|
||||
return null;
|
||||
}
|
||||
let transformedProviderAst = this._transformedProviders.get(token.reference);
|
||||
let transformedProviderAst = this._transformedProviders.get(tokenReference(token));
|
||||
if (isPresent(transformedProviderAst)) {
|
||||
return transformedProviderAst;
|
||||
}
|
||||
if (isPresent(this._seenProviders.get(token.reference))) {
|
||||
if (isPresent(this._seenProviders.get(tokenReference(token)))) {
|
||||
this._errors.push(new ProviderError(
|
||||
`Cannot instantiate cyclic dependency! ${token.name}`, resolvedProvider.sourceSpan));
|
||||
`Cannot instantiate cyclic dependency! ${tokenName(token)}`,
|
||||
resolvedProvider.sourceSpan));
|
||||
return null;
|
||||
}
|
||||
this._seenProviders.set(token.reference, true);
|
||||
this._seenProviders.set(tokenReference(token), true);
|
||||
const transformedProviders = resolvedProvider.providers.map((provider) => {
|
||||
let transformedUseValue = provider.useValue;
|
||||
let transformedUseExisting = provider.useExisting;
|
||||
let transformedDeps: CompileDiDependencyMetadata[];
|
||||
if (isPresent(provider.useExisting)) {
|
||||
const existingDiDep = this._getDependency(
|
||||
new CompileDiDependencyMetadata({token: provider.useExisting}), eager,
|
||||
resolvedProvider.sourceSpan);
|
||||
const existingDiDep =
|
||||
this._getDependency({token: provider.useExisting}, eager, resolvedProvider.sourceSpan);
|
||||
if (isPresent(existingDiDep.token)) {
|
||||
transformedUseExisting = existingDiDep.token;
|
||||
} else {
|
||||
@ -353,7 +342,7 @@ export class NgModuleProviderAnalyzer {
|
||||
});
|
||||
transformedProviderAst =
|
||||
_transformProviderAst(resolvedProvider, {eager: eager, providers: transformedProviders});
|
||||
this._transformedProviders.set(token.reference, transformedProviderAst);
|
||||
this._transformedProviders.set(tokenReference(token), transformedProviderAst);
|
||||
return transformedProviderAst;
|
||||
}
|
||||
|
||||
@ -363,9 +352,8 @@ export class NgModuleProviderAnalyzer {
|
||||
let foundLocal = false;
|
||||
if (!dep.isSkipSelf && isPresent(dep.token)) {
|
||||
// access the injector
|
||||
if (dep.token.reference === resolveIdentifierToken(Identifiers.Injector).reference ||
|
||||
dep.token.reference ===
|
||||
resolveIdentifierToken(Identifiers.ComponentFactoryResolver).reference) {
|
||||
if (tokenReference(dep.token) === resolveIdentifier(Identifiers.Injector) ||
|
||||
tokenReference(dep.token) === resolveIdentifier(Identifiers.ComponentFactoryResolver)) {
|
||||
foundLocal = true;
|
||||
// access providers
|
||||
} else if (isPresent(this._getOrCreateLocalProvider(dep.token, eager))) {
|
||||
@ -375,10 +363,10 @@ export class NgModuleProviderAnalyzer {
|
||||
let result: CompileDiDependencyMetadata = dep;
|
||||
if (dep.isSelf && !foundLocal) {
|
||||
if (dep.isOptional) {
|
||||
result = new CompileDiDependencyMetadata({isValue: true, value: null});
|
||||
result = {isValue: true, value: null};
|
||||
} else {
|
||||
this._errors.push(
|
||||
new ProviderError(`No provider for ${dep.token.name}`, requestorSourceSpan));
|
||||
new ProviderError(`No provider for ${tokenName(dep.token)}`, requestorSourceSpan));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@ -389,7 +377,7 @@ function _transformProvider(
|
||||
provider: CompileProviderMetadata,
|
||||
{useExisting, useValue, deps}:
|
||||
{useExisting: CompileTokenMetadata, useValue: any, deps: CompileDiDependencyMetadata[]}) {
|
||||
return new CompileProviderMetadata({
|
||||
return {
|
||||
token: provider.token,
|
||||
useClass: provider.useClass,
|
||||
useExisting: useExisting,
|
||||
@ -397,7 +385,7 @@ function _transformProvider(
|
||||
useValue: useValue,
|
||||
deps: deps,
|
||||
multi: provider.multi
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function _transformProviderAst(
|
||||
@ -408,44 +396,13 @@ function _transformProviderAst(
|
||||
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(
|
||||
directives: CompileDirectiveSummary[], sourceSpan: ParseSourceSpan,
|
||||
targetErrors: ParseError[]): Map<any, ProviderAst> {
|
||||
const providersByToken = new Map<any, ProviderAst>();
|
||||
directives.forEach((directive) => {
|
||||
const dirProvider = new CompileProviderMetadata(
|
||||
{token: new CompileTokenMetadata({identifier: directive.type}), useClass: directive.type});
|
||||
const dirProvider:
|
||||
CompileProviderMetadata = {token: {identifier: directive.type}, useClass: directive.type};
|
||||
_resolveProviders(
|
||||
[dirProvider],
|
||||
directive.isComponent ? ProviderAstType.Component : ProviderAstType.Directive, true,
|
||||
@ -457,11 +414,11 @@ function _resolveProvidersFromDirectives(
|
||||
directives.filter(dir => dir.isComponent).concat(directives.filter(dir => !dir.isComponent));
|
||||
directivesWithComponentFirst.forEach((directive) => {
|
||||
_resolveProviders(
|
||||
_normalizeProviders(directive.providers, sourceSpan, targetErrors),
|
||||
ProviderAstType.PublicService, false, sourceSpan, targetErrors, providersByToken);
|
||||
directive.providers, ProviderAstType.PublicService, false, sourceSpan, targetErrors,
|
||||
providersByToken);
|
||||
_resolveProviders(
|
||||
_normalizeProviders(directive.viewProviders, sourceSpan, targetErrors),
|
||||
ProviderAstType.PrivateService, false, sourceSpan, targetErrors, providersByToken);
|
||||
directive.viewProviders, ProviderAstType.PrivateService, false, sourceSpan, targetErrors,
|
||||
providersByToken);
|
||||
});
|
||||
return providersByToken;
|
||||
}
|
||||
@ -471,21 +428,21 @@ function _resolveProviders(
|
||||
sourceSpan: ParseSourceSpan, targetErrors: ParseError[],
|
||||
targetProvidersByToken: Map<any, ProviderAst>) {
|
||||
providers.forEach((provider) => {
|
||||
let resolvedProvider = targetProvidersByToken.get(provider.token.reference);
|
||||
if (isPresent(resolvedProvider) && resolvedProvider.multiProvider !== provider.multi) {
|
||||
let resolvedProvider = targetProvidersByToken.get(tokenReference(provider.token));
|
||||
if (isPresent(resolvedProvider) && !!resolvedProvider.multiProvider !== !!provider.multi) {
|
||||
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));
|
||||
}
|
||||
if (!resolvedProvider) {
|
||||
const lifecycleHooks =
|
||||
provider.token.identifier && provider.token.identifier instanceof CompileTypeMetadata ?
|
||||
provider.token.identifier.lifecycleHooks :
|
||||
const lifecycleHooks = provider.token.identifier &&
|
||||
(<CompileTypeMetadata>provider.token.identifier).lifecycleHooks ?
|
||||
(<CompileTypeMetadata>provider.token.identifier).lifecycleHooks :
|
||||
[];
|
||||
resolvedProvider = new ProviderAst(
|
||||
provider.token, provider.multi, eager || lifecycleHooks.length > 0, [provider],
|
||||
providerType, lifecycleHooks, sourceSpan);
|
||||
targetProvidersByToken.set(provider.token.reference, resolvedProvider);
|
||||
targetProvidersByToken.set(tokenReference(provider.token), resolvedProvider);
|
||||
} else {
|
||||
if (!provider.multi) {
|
||||
resolvedProvider.providers.length = 0;
|
||||
@ -517,10 +474,10 @@ function _getContentQueries(directives: CompileDirectiveSummary[]):
|
||||
|
||||
function _addQueryToTokenMap(map: Map<any, CompileQueryMetadata[]>, query: CompileQueryMetadata) {
|
||||
query.selectors.forEach((token: CompileTokenMetadata) => {
|
||||
let entry = map.get(token.reference);
|
||||
let entry = map.get(tokenReference(token));
|
||||
if (!entry) {
|
||||
entry = [];
|
||||
map.set(token.reference, entry);
|
||||
map.set(tokenReference(token), entry);
|
||||
}
|
||||
entry.push(query);
|
||||
});
|
||||
|
@ -7,7 +7,8 @@
|
||||
*/
|
||||
|
||||
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 {ShadowCss} from './shadow_css';
|
||||
import {UrlResolver} from './url_resolver';
|
||||
@ -18,7 +19,7 @@ const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
|
||||
|
||||
export class StylesCompileDependency {
|
||||
constructor(
|
||||
public moduleUrl: string, public isShimmed: boolean,
|
||||
public name: string, public moduleUrl: string, public isShimmed: boolean,
|
||||
public valuePlaceholder: CompileIdentifierMetadata) {}
|
||||
}
|
||||
|
||||
@ -47,7 +48,7 @@ export class StyleCompiler {
|
||||
comp, new CompileStylesheetMetadata({
|
||||
styles: comp.template.styles,
|
||||
styleUrls: comp.template.styleUrls,
|
||||
moduleUrl: comp.type.moduleUrl
|
||||
moduleUrl: identifierModuleUrl(comp.type)
|
||||
}),
|
||||
true);
|
||||
comp.template.externalStylesheets.forEach((stylesheetMeta) => {
|
||||
@ -65,8 +66,9 @@ export class StyleCompiler {
|
||||
stylesheet.styles.map(plainStyle => o.literal(this._shimIfNeeded(plainStyle, shim)));
|
||||
const dependencies: StylesCompileDependency[] = [];
|
||||
for (let i = 0; i < stylesheet.styleUrls.length; i++) {
|
||||
const identifier = new CompileIdentifierMetadata({name: getStylesVarName(null)});
|
||||
dependencies.push(new StylesCompileDependency(stylesheet.styleUrls[i], shim, identifier));
|
||||
const identifier: CompileIdentifierMetadata = {reference: null};
|
||||
dependencies.push(new StylesCompileDependency(
|
||||
getStylesVarName(null), stylesheet.styleUrls[i], shim, identifier));
|
||||
styleExpressions.push(new o.ExternalExpr(identifier));
|
||||
}
|
||||
// styles variable contains plain strings and arrays of other styles arrays (recursive),
|
||||
@ -87,7 +89,7 @@ export class StyleCompiler {
|
||||
function getStylesVarName(component: CompileDirectiveMetadata): string {
|
||||
let result = `styles`;
|
||||
if (component) {
|
||||
result += `_${component.type.name}`;
|
||||
result += `_${identifierName(component.type)}`;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -8,12 +8,12 @@
|
||||
|
||||
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 {Parser} from '../expression_parser/parser';
|
||||
import {isPresent} from '../facade/lang';
|
||||
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 {ParseTreeResult} from '../ml_parser/html_parser';
|
||||
import {expandNodes} from '../ml_parser/icu_ast_expander';
|
||||
@ -546,7 +546,8 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
let component: CompileDirectiveSummary = null;
|
||||
const directiveAsts = directives.map((directive) => {
|
||||
const sourceSpan = new ParseSourceSpan(
|
||||
elementSourceSpan.start, elementSourceSpan.end, `Directive ${directive.type.name}`);
|
||||
elementSourceSpan.start, elementSourceSpan.end,
|
||||
`Directive ${identifierName(directive.type)}`);
|
||||
if (directive.isComponent) {
|
||||
component = directive;
|
||||
}
|
||||
@ -579,7 +580,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
} else if (!component) {
|
||||
let refToken: CompileTokenMetadata = null;
|
||||
if (isTemplateElement) {
|
||||
refToken = resolveIdentifierToken(Identifiers.TemplateRef);
|
||||
refToken = createIdentifierToken(Identifiers.TemplateRef);
|
||||
}
|
||||
targetReferences.push(new ReferenceAst(elOrDirRef.name, refToken, elOrDirRef.sourceSpan));
|
||||
}
|
||||
@ -640,7 +641,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||
|
||||
private _findComponentDirectiveNames(directives: DirectiveAst[]): string[] {
|
||||
return this._findComponentDirectives(directives)
|
||||
.map(directive => directive.directive.type.name);
|
||||
.map(directive => identifierName(directive.directive.type));
|
||||
}
|
||||
|
||||
private _assertOnlyOneComponent(directives: DirectiveAst[], sourceSpan: ParseSourceSpan) {
|
||||
|
@ -6,7 +6,7 @@
|
||||
* 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 = '';
|
||||
|
||||
@ -35,10 +35,6 @@ function _splitAt(input: string, character: string, defaultValues: string[]): st
|
||||
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 {
|
||||
if (Array.isArray(value)) {
|
||||
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);
|
||||
}
|
||||
|
||||
if (isBlank(value) || isPrimitive(value)) {
|
||||
if (value == null || isPrimitive(value)) {
|
||||
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 {DirectiveWrapperCompiler, DirectiveWrapperExpressions} from '../directive_wrapper_compiler';
|
||||
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 {convertValueToOutputAst} from '../output/value_util';
|
||||
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 {CompileView, CompileViewRootNode} from './compile_view';
|
||||
import {InjectMethodVars, ViewProperties} from './constants';
|
||||
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency} from './deps';
|
||||
import {ComponentFactoryDependency, DirectiveWrapperDependency} from './deps';
|
||||
import {getPropertyInView, injectFromViewParentInjector} from './util';
|
||||
|
||||
export class CompileNode {
|
||||
@ -35,7 +35,7 @@ export class CompileNode {
|
||||
|
||||
export class CompileElement extends CompileNode {
|
||||
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;
|
||||
@ -57,21 +57,18 @@ export class CompileElement extends CompileNode {
|
||||
sourceAst: TemplateAst, public component: CompileDirectiveSummary,
|
||||
private _directives: CompileDirectiveSummary[],
|
||||
private _resolvedProvidersArray: ProviderAst[], public hasViewContainer: boolean,
|
||||
public hasEmbeddedView: boolean, references: ReferenceAst[],
|
||||
private _targetDependencies:
|
||||
Array<ViewClassDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {
|
||||
public hasEmbeddedView: boolean, references: ReferenceAst[]) {
|
||||
super(parent, view, nodeIndex, renderNode, sourceAst);
|
||||
this.referenceTokens = {};
|
||||
references.forEach(ref => this.referenceTokens[ref.name] = ref.value);
|
||||
|
||||
this.elementRef =
|
||||
o.importExpr(resolveIdentifier(Identifiers.ElementRef)).instantiate([this.renderNode]);
|
||||
this.instances.set(resolveIdentifierToken(Identifiers.ElementRef).reference, this.elementRef);
|
||||
o.importExpr(createIdentifier(Identifiers.ElementRef)).instantiate([this.renderNode]);
|
||||
this.instances.set(resolveIdentifier(Identifiers.ElementRef), this.elementRef);
|
||||
this.instances.set(
|
||||
resolveIdentifierToken(Identifiers.Injector).reference,
|
||||
resolveIdentifier(Identifiers.Injector),
|
||||
o.THIS_EXPR.callMethod('injector', [o.literal(this.nodeIndex)]));
|
||||
this.instances.set(
|
||||
resolveIdentifierToken(Identifiers.Renderer).reference, o.THIS_EXPR.prop('renderer'));
|
||||
this.instances.set(resolveIdentifier(Identifiers.Renderer), o.THIS_EXPR.prop('renderer'));
|
||||
if (this.hasViewContainer || this.hasEmbeddedView) {
|
||||
this._createViewContainer();
|
||||
}
|
||||
@ -85,41 +82,40 @@ export class CompileElement extends CompileNode {
|
||||
const parentNodeIndex = this.isRootElement() ? null : this.parent.nodeIndex;
|
||||
// private is fine here as no child view will reference a ViewContainer
|
||||
this.view.fields.push(new o.ClassField(
|
||||
fieldName, o.importType(resolveIdentifier(Identifiers.ViewContainer)),
|
||||
fieldName, o.importType(createIdentifier(Identifiers.ViewContainer)),
|
||||
[o.StmtModifier.Private]));
|
||||
const statement =
|
||||
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
|
||||
]))
|
||||
.toStmt();
|
||||
this.view.createMethod.addStmt(statement);
|
||||
this.viewContainer = o.THIS_EXPR.prop(fieldName);
|
||||
this.instances.set(
|
||||
resolveIdentifierToken(Identifiers.ViewContainer).reference, this.viewContainer);
|
||||
this.instances.set(resolveIdentifier(Identifiers.ViewContainer), this.viewContainer);
|
||||
this.view.viewContainers.push(this.viewContainer);
|
||||
}
|
||||
|
||||
private _createComponentFactoryResolver() {
|
||||
const entryComponents =
|
||||
this.component.entryComponents.map((entryComponent: CompileIdentifierMetadata) => {
|
||||
const id = new CompileIdentifierMetadata({name: entryComponent.name});
|
||||
this._targetDependencies.push(new ComponentFactoryDependency(entryComponent, id));
|
||||
const id: CompileIdentifierMetadata = {reference: null};
|
||||
this.view.targetDependencies.push(new ComponentFactoryDependency(entryComponent, id));
|
||||
return id;
|
||||
});
|
||||
if (!entryComponents || entryComponents.length === 0) {
|
||||
return;
|
||||
}
|
||||
const createComponentFactoryResolverExpr =
|
||||
o.importExpr(resolveIdentifier(Identifiers.CodegenComponentFactoryResolver)).instantiate([
|
||||
o.importExpr(createIdentifier(Identifiers.CodegenComponentFactoryResolver)).instantiate([
|
||||
o.literalArr(entryComponents.map((entryComponent) => o.importExpr(entryComponent))),
|
||||
injectFromViewParentInjector(
|
||||
this.view, resolveIdentifierToken(Identifiers.ComponentFactoryResolver), false)
|
||||
this.view, createIdentifierToken(Identifiers.ComponentFactoryResolver), false)
|
||||
]);
|
||||
const provider = new CompileProviderMetadata({
|
||||
token: resolveIdentifierToken(Identifiers.ComponentFactoryResolver),
|
||||
const provider: CompileProviderMetadata = {
|
||||
token: createIdentifierToken(Identifiers.ComponentFactoryResolver),
|
||||
useValue: createComponentFactoryResolverExpr
|
||||
});
|
||||
};
|
||||
// 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,
|
||||
// but nobody else
|
||||
@ -141,13 +137,13 @@ export class CompileElement extends CompileNode {
|
||||
this.embeddedView = embeddedView;
|
||||
if (isPresent(embeddedView)) {
|
||||
const createTemplateRefExpr =
|
||||
o.importExpr(resolveIdentifier(Identifiers.TemplateRef_)).instantiate([
|
||||
o.importExpr(createIdentifier(Identifiers.TemplateRef_)).instantiate([
|
||||
o.THIS_EXPR, o.literal(this.nodeIndex), this.renderNode
|
||||
]);
|
||||
const provider = new CompileProviderMetadata({
|
||||
token: resolveIdentifierToken(Identifiers.TemplateRef),
|
||||
const provider: CompileProviderMetadata = {
|
||||
token: createIdentifierToken(Identifiers.TemplateRef),
|
||||
useValue: createTemplateRefExpr
|
||||
});
|
||||
};
|
||||
// Add TemplateRef as first provider as it does not have deps on other providers
|
||||
this._resolvedProvidersArray.unshift(new ProviderAst(
|
||||
provider.token, false, true, [provider], ProviderAstType.Builtin, [],
|
||||
@ -158,13 +154,12 @@ export class CompileElement extends CompileNode {
|
||||
beforeChildren(): void {
|
||||
if (this.hasViewContainer) {
|
||||
this.instances.set(
|
||||
resolveIdentifierToken(Identifiers.ViewContainerRef).reference,
|
||||
this.viewContainer.prop('vcRef'));
|
||||
resolveIdentifier(Identifiers.ViewContainerRef), this.viewContainer.prop('vcRef'));
|
||||
}
|
||||
|
||||
this._resolvedProviders = new Map<any, ProviderAst>();
|
||||
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,
|
||||
// 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;
|
||||
const providerValueExpressions = resolvedProvider.providers.map((provider) => {
|
||||
if (provider.useExisting) {
|
||||
return this._getDependency(
|
||||
resolvedProvider.providerType,
|
||||
new CompileDiDependencyMetadata({token: provider.useExisting}));
|
||||
return this._getDependency(resolvedProvider.providerType, {token: provider.useExisting});
|
||||
} else if (provider.useFactory) {
|
||||
const deps = provider.deps || provider.useFactory.diDeps;
|
||||
const depsExpr =
|
||||
@ -186,10 +179,10 @@ export class CompileElement extends CompileNode {
|
||||
const depsExpr =
|
||||
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep));
|
||||
if (isDirectiveWrapper) {
|
||||
const directiveWrapperIdentifier = new CompileIdentifierMetadata(
|
||||
{name: DirectiveWrapperCompiler.dirWrapperClassName(provider.useClass)});
|
||||
this._targetDependencies.push(
|
||||
new DirectiveWrapperDependency(provider.useClass, directiveWrapperIdentifier));
|
||||
const directiveWrapperIdentifier: CompileIdentifierMetadata = {reference: null};
|
||||
this.view.targetDependencies.push(new DirectiveWrapperDependency(
|
||||
provider.useClass, DirectiveWrapperCompiler.dirWrapperClassName(provider.useClass),
|
||||
directiveWrapperIdentifier));
|
||||
return DirectiveWrapperExpressions.create(directiveWrapperIdentifier, depsExpr);
|
||||
} else {
|
||||
return o.importExpr(provider.useClass)
|
||||
@ -199,22 +192,23 @@ export class CompileElement extends CompileNode {
|
||||
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(
|
||||
propName, resolvedProvider, providerValueExpressions, resolvedProvider.multiProvider,
|
||||
resolvedProvider.eager, this);
|
||||
if (isDirectiveWrapper) {
|
||||
this.directiveWrapperInstance.set(resolvedProvider.token.reference, instance);
|
||||
this.directiveWrapperInstance.set(tokenReference(resolvedProvider.token), instance);
|
||||
this.instances.set(
|
||||
resolvedProvider.token.reference, DirectiveWrapperExpressions.context(instance));
|
||||
tokenReference(resolvedProvider.token), DirectiveWrapperExpressions.context(instance));
|
||||
} else {
|
||||
this.instances.set(resolvedProvider.token.reference, instance);
|
||||
this.instances.set(tokenReference(resolvedProvider.token), instance);
|
||||
}
|
||||
});
|
||||
|
||||
for (let i = 0; i < this._directives.length; 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); });
|
||||
}
|
||||
const queriesWithReads: _QueryWithRead[] = [];
|
||||
@ -227,12 +221,12 @@ export class CompileElement extends CompileNode {
|
||||
const token = this.referenceTokens[varName];
|
||||
let varValue: o.Expression;
|
||||
if (token) {
|
||||
varValue = this.instances.get(token.reference);
|
||||
varValue = this.instances.get(tokenReference(token));
|
||||
} else {
|
||||
varValue = this.renderNode;
|
||||
}
|
||||
this.view.locals.set(varName, varValue);
|
||||
const varToken = new CompileTokenMetadata({value: varName});
|
||||
const varToken = {value: varName};
|
||||
queriesWithReads.push(
|
||||
...this._getQueriesFor(varToken).map(query => new _QueryWithRead(query, varToken)));
|
||||
});
|
||||
@ -240,12 +234,12 @@ export class CompileElement extends CompileNode {
|
||||
let value: o.Expression;
|
||||
if (isPresent(queryWithRead.read.identifier)) {
|
||||
// query for an identifier
|
||||
value = this.instances.get(queryWithRead.read.reference);
|
||||
value = this.instances.get(tokenReference(queryWithRead.read));
|
||||
} else {
|
||||
// query for a reference
|
||||
const token = this.referenceTokens[queryWithRead.read.value];
|
||||
if (isPresent(token)) {
|
||||
value = this.instances.get(token.reference);
|
||||
value = this.instances.get(tokenReference(token));
|
||||
} else {
|
||||
value = this.elementRef;
|
||||
}
|
||||
@ -261,7 +255,7 @@ export class CompileElement extends CompileNode {
|
||||
// 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
|
||||
// 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.
|
||||
// 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
|
||||
@ -285,7 +279,7 @@ export class CompileElement extends CompileNode {
|
||||
|
||||
getComponent(): o.Expression {
|
||||
return isPresent(this.component) ?
|
||||
this.instances.get(identifierToken(this.component.type).reference) :
|
||||
this.instances.get(tokenReference(identifierToken(this.component.type))) :
|
||||
null;
|
||||
}
|
||||
|
||||
@ -300,7 +294,7 @@ export class CompileElement extends CompileNode {
|
||||
let distance = 0;
|
||||
let queries: CompileQuery[];
|
||||
while (!currentEl.isNull()) {
|
||||
queries = currentEl._queries.get(token.reference);
|
||||
queries = currentEl._queries.get(tokenReference(token));
|
||||
if (isPresent(queries)) {
|
||||
result.push(...queries.filter((query) => query.meta.descendants || distance <= 1));
|
||||
}
|
||||
@ -309,7 +303,7 @@ export class CompileElement extends CompileNode {
|
||||
}
|
||||
currentEl = currentEl.parent;
|
||||
}
|
||||
queries = this.view.componentView.viewQueries.get(token.reference);
|
||||
queries = this.view.componentView.viewQueries.get(tokenReference(token));
|
||||
if (isPresent(queries)) {
|
||||
result.push(...queries);
|
||||
}
|
||||
@ -319,7 +313,7 @@ export class CompileElement extends CompileNode {
|
||||
private _addQuery(queryMeta: CompileQueryMetadata, directiveInstance: o.Expression):
|
||||
CompileQuery {
|
||||
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 query = new CompileQuery(queryMeta, queryList, directiveInstance, this.view);
|
||||
addQueryToTokenMap(this._queries, query);
|
||||
@ -332,8 +326,7 @@ export class CompileElement extends CompileNode {
|
||||
if (isPresent(dep.token)) {
|
||||
// access builtins with special visibility
|
||||
if (!result) {
|
||||
if (dep.token.reference ===
|
||||
resolveIdentifierToken(Identifiers.ChangeDetectorRef).reference) {
|
||||
if (tokenReference(dep.token) === resolveIdentifier(Identifiers.ChangeDetectorRef)) {
|
||||
if (requestingProviderType === ProviderAstType.Component) {
|
||||
return this.compViewExpr.prop('ref');
|
||||
} else {
|
||||
@ -343,7 +336,7 @@ export class CompileElement extends CompileNode {
|
||||
}
|
||||
// access regular providers on the element
|
||||
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.
|
||||
// only components and private services can access private services.
|
||||
if (resolvedProvider && (requestingProviderType === ProviderAstType.Directive ||
|
||||
@ -351,7 +344,7 @@ export class CompileElement extends CompileNode {
|
||||
resolvedProvider.providerType === ProviderAstType.PrivateService) {
|
||||
return null;
|
||||
}
|
||||
result = this.instances.get(dep.token.reference);
|
||||
result = this.instances.get(tokenReference(dep.token));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@ -370,8 +363,7 @@ export class CompileElement extends CompileNode {
|
||||
// check parent elements
|
||||
while (!result && !currElement.parent.isNull()) {
|
||||
currElement = currElement.parent;
|
||||
result = currElement._getLocalDependency(
|
||||
ProviderAstType.PublicService, new CompileDiDependencyMetadata({token: dep.token}));
|
||||
result = currElement._getLocalDependency(ProviderAstType.PublicService, {token: dep.token});
|
||||
}
|
||||
|
||||
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 {Identifiers, resolveIdentifier, resolveIdentifierToken} from '../identifiers';
|
||||
import {Identifiers, createIdentifier, resolveIdentifier} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
|
||||
import {CompileView} from './compile_view';
|
||||
@ -42,8 +42,7 @@ export class CompilePipe {
|
||||
constructor(public view: CompileView, public meta: CompilePipeSummary) {
|
||||
this.instance = o.THIS_EXPR.prop(`_pipe_${meta.name}_${view.pipeCount++}`);
|
||||
const deps = this.meta.type.diDeps.map((diDep) => {
|
||||
if (diDep.token.reference ===
|
||||
resolveIdentifierToken(Identifiers.ChangeDetectorRef).reference) {
|
||||
if (tokenReference(diDep.token) === resolveIdentifier(Identifiers.ChangeDetectorRef)) {
|
||||
return getPropertyInView(o.THIS_EXPR.prop('ref'), this.view, this.view.componentView);
|
||||
}
|
||||
return injectFromViewParentInjector(view, diDep.token, false);
|
||||
@ -69,7 +68,7 @@ export class CompilePipe {
|
||||
.callMethod(o.BuiltinMethod.Bind, [pipeInstanceSeenFromPureProxy]),
|
||||
args.length, purePipeProxyInstance,
|
||||
{fields: callingView.fields, ctorStmts: callingView.createMethod});
|
||||
return o.importExpr(resolveIdentifier(Identifiers.castByValue))
|
||||
return o.importExpr(createIdentifier(Identifiers.castByValue))
|
||||
.callFn([purePipeProxyInstance, pipeInstanceSeenFromPureProxy.prop('transform')])
|
||||
.callFn(args);
|
||||
} else {
|
||||
|
@ -6,10 +6,10 @@
|
||||
* 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 {isPresent} from '../facade/lang';
|
||||
import {Identifiers, resolveIdentifier} from '../identifiers';
|
||||
import {Identifiers, createIdentifier} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
|
||||
import {CompileElement} from './compile_element';
|
||||
@ -114,22 +114,22 @@ export function createQueryList(
|
||||
query: CompileQueryMetadata, directiveInstance: o.Expression, propertyName: string,
|
||||
compileView: CompileView): o.Expression {
|
||||
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);
|
||||
compileView.createMethod.addStmt(
|
||||
o.THIS_EXPR.prop(propertyName)
|
||||
.set(o.importExpr(resolveIdentifier(Identifiers.QueryList), [o.DYNAMIC_TYPE])
|
||||
.instantiate([]))
|
||||
.set(o.importExpr(createIdentifier(Identifiers.QueryList), [o.DYNAMIC_TYPE]).instantiate([
|
||||
]))
|
||||
.toStmt());
|
||||
return expr;
|
||||
}
|
||||
|
||||
export function addQueryToTokenMap(map: Map<any, CompileQuery[]>, query: CompileQuery) {
|
||||
query.meta.selectors.forEach((selector) => {
|
||||
let entry = map.get(selector.reference);
|
||||
let entry = map.get(tokenReference(selector));
|
||||
if (!entry) {
|
||||
entry = [];
|
||||
map.set(selector.reference, entry);
|
||||
map.set(tokenReference(selector), entry);
|
||||
}
|
||||
entry.push(query);
|
||||
});
|
||||
|
@ -7,12 +7,12 @@
|
||||
*/
|
||||
|
||||
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 {createPureProxy} from '../compiler_util/identifier_util';
|
||||
import {CompilerConfig} from '../config';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {Identifiers, resolveIdentifier} from '../identifiers';
|
||||
import {Identifiers, createIdentifier} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
import {ViewType} from '../private_import_core';
|
||||
|
||||
@ -20,6 +20,7 @@ import {CompileElement, CompileNode} from './compile_element';
|
||||
import {CompileMethod} from './compile_method';
|
||||
import {CompilePipe} from './compile_pipe';
|
||||
import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query';
|
||||
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency} from './deps';
|
||||
import {getPropertyInView, getViewClassName} from './util';
|
||||
|
||||
export enum CompileViewRootNodeType {
|
||||
@ -84,7 +85,9 @@ export class CompileView implements NameResolver {
|
||||
public component: CompileDirectiveMetadata, public genConfig: CompilerConfig,
|
||||
public pipeMetas: CompilePipeSummary[], public styles: o.Expression,
|
||||
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.animationBindingsMethod = new CompileMethod(this);
|
||||
this.injectorGetMethod = new CompileMethod(this);
|
||||
@ -101,7 +104,7 @@ export class CompileView implements NameResolver {
|
||||
|
||||
this.viewType = getViewType(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);
|
||||
if (this.viewType === ViewType.COMPONENT || this.viewType === ViewType.HOST) {
|
||||
this.componentView = this;
|
||||
@ -115,7 +118,7 @@ export class CompileView implements NameResolver {
|
||||
if (this.viewType === ViewType.COMPONENT) {
|
||||
const directiveInstance = o.THIS_EXPR.prop('context');
|
||||
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 query = new CompileQuery(queryMeta, queryList, directiveInstance, this);
|
||||
addQueryToTokenMap(viewQueries, query);
|
||||
@ -164,7 +167,7 @@ function getViewType(component: CompileDirectiveMetadata, embeddedTemplateIndex:
|
||||
return ViewType.EMBEDDED;
|
||||
}
|
||||
|
||||
if (component.type.isHost) {
|
||||
if (component.isHost) {
|
||||
return ViewType.HOST;
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,8 @@ import {CompileIdentifierMetadata} from '../compile_metadata';
|
||||
|
||||
export class ViewClassDependency {
|
||||
constructor(
|
||||
public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {}
|
||||
public comp: CompileIdentifierMetadata, public name: string,
|
||||
public placeholder: CompileIdentifierMetadata) {}
|
||||
}
|
||||
|
||||
export class ComponentFactoryDependency {
|
||||
@ -20,5 +21,6 @@ export class ComponentFactoryDependency {
|
||||
|
||||
export class DirectiveWrapperDependency {
|
||||
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 {createInlineArray} from '../compiler_util/identifier_util';
|
||||
import {DirectiveWrapperExpressions} from '../directive_wrapper_compiler';
|
||||
import {Identifiers, resolveIdentifier} from '../identifiers';
|
||||
import {Identifiers, createIdentifier} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
import {BoundEventAst, DirectiveAst} from '../template_parser/template_ast';
|
||||
|
||||
@ -56,7 +56,7 @@ function subscribeToRenderEvents(
|
||||
compileElement.view.disposables.push(disposableVar);
|
||||
compileElement.view.createMethod.addStmt(
|
||||
disposableVar
|
||||
.set(o.importExpr(resolveIdentifier(Identifiers.subscribeToRenderElement)).callFn([
|
||||
.set(o.importExpr(createIdentifier(Identifiers.subscribeToRenderElement)).callFn([
|
||||
o.THIS_EXPR, compileElement.renderNode, createInlineArray(eventAndTargetExprs),
|
||||
handleEventExpr(compileElement)
|
||||
]))
|
||||
|
@ -13,7 +13,7 @@ import {ConvertPropertyBindingResult, convertPropertyBinding} from '../compiler_
|
||||
import {createEnumExpression} from '../compiler_util/identifier_util';
|
||||
import {triggerAnimation, writeToRenderer} from '../compiler_util/render_util';
|
||||
import {DirectiveWrapperExpressions} from '../directive_wrapper_compiler';
|
||||
import {Identifiers, resolveIdentifier} from '../identifiers';
|
||||
import {Identifiers, createIdentifier} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
import {isDefaultChangeDetectionStrategy} from '../private_import_core';
|
||||
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
||||
@ -69,7 +69,7 @@ export function bindRenderInputs(
|
||||
const {updateStmts, detachStmts} = triggerAnimation(
|
||||
o.THIS_EXPR, o.THIS_EXPR, boundProp,
|
||||
(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]),
|
||||
compileElement.renderNode, evalResult.currValExpr, bindingField.expression);
|
||||
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 {isPresent} from '../facade/lang';
|
||||
import {Identifiers, resolveIdentifier} from '../identifiers';
|
||||
import {Identifiers, createIdentifier} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
import {ViewType} from '../private_import_core';
|
||||
|
||||
@ -74,7 +74,7 @@ export function injectFromViewParentInjector(
|
||||
export function getViewClassName(
|
||||
component: CompileDirectiveSummary | CompileDirectiveMetadata,
|
||||
embeddedTemplateIndex: number): string {
|
||||
return `View_${component.type.name}${embeddedTemplateIndex}`;
|
||||
return `View_${identifierName(component.type)}${embeddedTemplateIndex}`;
|
||||
}
|
||||
|
||||
export function getHandleEventMethodName(elementIndex: number): string {
|
||||
|
@ -6,6 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {tokenReference} from '../compile_metadata';
|
||||
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';
|
||||
|
||||
@ -66,7 +67,7 @@ class ViewBinderVisitor implements TemplateAstVisitor {
|
||||
directiveAst, directiveWrapperInstance, compileElement);
|
||||
});
|
||||
ast.providers.forEach((providerAst) => {
|
||||
const providerInstance = compileElement.instances.get(providerAst.token.reference);
|
||||
const providerInstance = compileElement.instances.get(tokenReference(providerAst.token));
|
||||
bindInjectableDestroyLifecycleCallbacks(providerAst, providerInstance, compileElement);
|
||||
});
|
||||
return null;
|
||||
@ -89,7 +90,7 @@ class ViewBinderVisitor implements TemplateAstVisitor {
|
||||
directiveAst, directiveWrapperInstance, compileElement);
|
||||
});
|
||||
ast.providers.forEach((providerAst) => {
|
||||
const providerInstance = compileElement.instances.get(providerAst.token.reference);
|
||||
const providerInstance = compileElement.instances.get(tokenReference(providerAst.token));
|
||||
bindInjectableDestroyLifecycleCallbacks(providerAst, providerInstance, compileElement);
|
||||
});
|
||||
bindView(compileElement.embeddedView, ast.children, this._schemaRegistry);
|
||||
|
@ -8,11 +8,11 @@
|
||||
|
||||
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 {createDiTokenExpression, createInlineArray} from '../compiler_util/identifier_util';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {Identifiers, identifierToken, resolveIdentifier} from '../identifiers';
|
||||
import {Identifiers, createIdentifier, identifierToken} from '../identifiers';
|
||||
import {createClassStmt} from '../output/class_builder';
|
||||
import * as o from '../output/output_ast';
|
||||
import {ParseSourceSpan} from '../parse_util';
|
||||
@ -189,13 +189,13 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
||||
_mergeHtmlAndDirectiveAttrs(htmlAttrs, directives).map(v => o.literal(v)));
|
||||
if (nodeIndex === 0 && this.view.viewType === ViewType.HOST) {
|
||||
createRenderNodeExpr =
|
||||
o.importExpr(resolveIdentifier(Identifiers.selectOrCreateRenderHostElement)).callFn([
|
||||
o.importExpr(createIdentifier(Identifiers.selectOrCreateRenderHostElement)).callFn([
|
||||
ViewProperties.renderer, o.literal(ast.name), attrNameAndValues, rootSelectorVar,
|
||||
debugContextExpr
|
||||
]);
|
||||
} else {
|
||||
createRenderNodeExpr =
|
||||
o.importExpr(resolveIdentifier(Identifiers.createRenderElement)).callFn([
|
||||
o.importExpr(createIdentifier(Identifiers.createRenderElement)).callFn([
|
||||
ViewProperties.renderer, this._getParentRenderNode(parent), o.literal(ast.name),
|
||||
attrNameAndValues, debugContextExpr
|
||||
]);
|
||||
@ -210,19 +210,18 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
||||
|
||||
const compileElement = new CompileElement(
|
||||
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);
|
||||
let compViewExpr: o.ReadPropExpr = null;
|
||||
if (isPresent(component)) {
|
||||
const nestedComponentIdentifier =
|
||||
new CompileIdentifierMetadata({name: getViewClassName(component, 0)});
|
||||
this.targetDependencies.push(
|
||||
new ViewClassDependency(component.type, nestedComponentIdentifier));
|
||||
const nestedComponentIdentifier: CompileIdentifierMetadata = {reference: null};
|
||||
this.targetDependencies.push(new ViewClassDependency(
|
||||
component.type, getViewClassName(component, 0), nestedComponentIdentifier));
|
||||
|
||||
compViewExpr = o.THIS_EXPR.prop(`compView_${nodeIndex}`); // fix highlighting: `
|
||||
this.view.fields.push(new o.ClassField(
|
||||
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);
|
||||
compileElement.setComponentView(compViewExpr);
|
||||
this.view.createMethod.addStmt(
|
||||
@ -266,14 +265,14 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
||||
const directives = ast.directives.map(directiveAst => directiveAst.directive);
|
||||
const compileElement = new CompileElement(
|
||||
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.nestedViewCount++;
|
||||
const embeddedView = new CompileView(
|
||||
this.view.component, this.view.genConfig, this.view.pipeMetas, o.NULL_EXPR,
|
||||
this.view.animations, this.view.viewIndex + this.nestedViewCount, compileElement,
|
||||
templateVariableBindings);
|
||||
templateVariableBindings, this.targetDependencies);
|
||||
this.nestedViewCount += buildView(embeddedView, ast.children, this.targetDependencies);
|
||||
|
||||
compileElement.beforeChildren();
|
||||
@ -372,31 +371,33 @@ function createViewTopLevelStmts(view: CompileView, targetStatements: o.Statemen
|
||||
let nodeDebugInfosVar: o.Expression = o.NULL_EXPR;
|
||||
if (view.genConfig.genDebugInfo) {
|
||||
nodeDebugInfosVar = o.variable(
|
||||
`nodeDebugInfos_${view.component.type.name}${view.viewIndex}`); // fix highlighting: `
|
||||
`nodeDebugInfos_${identifierName(view.component.type)}${view.viewIndex}`); // fix
|
||||
// highlighting:
|
||||
// `
|
||||
targetStatements.push(
|
||||
(<o.ReadVarExpr>nodeDebugInfosVar)
|
||||
.set(o.literalArr(
|
||||
view.nodes.map(createStaticNodeDebugInfo),
|
||||
new o.ArrayType(
|
||||
new o.ExternalType(resolveIdentifier(Identifiers.StaticNodeDebugInfo)),
|
||||
o.importType(createIdentifier(Identifiers.StaticNodeDebugInfo)),
|
||||
[o.TypeModifier.Const])))
|
||||
.toDeclStmt(null, [o.StmtModifier.Final]));
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
let templateUrlInfo: string;
|
||||
if (view.component.template.templateUrl == view.component.type.moduleUrl) {
|
||||
if (view.component.template.templateUrl == identifierModuleUrl(view.component.type)) {
|
||||
templateUrlInfo =
|
||||
`${view.component.type.moduleUrl} class ${view.component.type.name} - inline template`;
|
||||
`${identifierModuleUrl(view.component.type)} class ${identifierName(view.component.type)} - inline template`;
|
||||
} else {
|
||||
templateUrlInfo = view.component.template.templateUrl;
|
||||
}
|
||||
targetStatements.push(
|
||||
renderCompTypeVar
|
||||
.set(o.importExpr(resolveIdentifier(Identifiers.createRenderComponentType)).callFn([
|
||||
.set(o.importExpr(createIdentifier(Identifiers.createRenderComponentType)).callFn([
|
||||
view.genConfig.genDebugInfo ? o.literal(templateUrlInfo) : o.literal(''),
|
||||
o.literal(view.component.template.ngContentSelectors.length),
|
||||
ViewEncapsulationEnum.fromValue(view.component.template.encapsulation),
|
||||
@ -404,7 +405,7 @@ function createViewTopLevelStmts(view: CompileView, targetStatements: o.Statemen
|
||||
o.literalMap(view.animations.map(
|
||||
(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);
|
||||
@ -427,7 +428,7 @@ function createStaticNodeDebugInfo(node: CompileNode): o.Expression {
|
||||
[varName, isPresent(token) ? createDiTokenExpression(token) : o.NULL_EXPR]);
|
||||
});
|
||||
}
|
||||
return o.importExpr(resolveIdentifier(Identifiers.StaticNodeDebugInfo))
|
||||
return o.importExpr(createIdentifier(Identifiers.StaticNodeDebugInfo))
|
||||
.instantiate(
|
||||
[
|
||||
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.importType(
|
||||
resolveIdentifier(Identifiers.StaticNodeDebugInfo), null, [o.TypeModifier.Const]));
|
||||
createIdentifier(Identifiers.StaticNodeDebugInfo), null, [o.TypeModifier.Const]));
|
||||
}
|
||||
|
||||
function createViewClass(
|
||||
@ -443,10 +444,10 @@ function createViewClass(
|
||||
nodeDebugInfosVar: o.Expression): o.ClassStmt {
|
||||
const viewConstructorArgs = [
|
||||
new o.FnParam(
|
||||
ViewConstructorVars.viewUtils.name, o.importType(resolveIdentifier(Identifiers.ViewUtils))),
|
||||
ViewConstructorVars.viewUtils.name, o.importType(createIdentifier(Identifiers.ViewUtils))),
|
||||
new o.FnParam(
|
||||
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.parentElement.name, o.DYNAMIC_TYPE)
|
||||
];
|
||||
@ -461,14 +462,14 @@ function createViewClass(
|
||||
}
|
||||
if (view.viewType === ViewType.EMBEDDED) {
|
||||
viewConstructorArgs.push(new o.FnParam(
|
||||
'declaredViewContainer', o.importType(resolveIdentifier(Identifiers.ViewContainer))));
|
||||
'declaredViewContainer', o.importType(createIdentifier(Identifiers.ViewContainer))));
|
||||
superConstructorArgs.push(o.variable('declaredViewContainer'));
|
||||
}
|
||||
const viewMethods = [
|
||||
new o.ClassMethod(
|
||||
'createInternal', [new o.FnParam(rootSelectorVar.name, o.STRING_TYPE)],
|
||||
generateCreateMethod(view),
|
||||
o.importType(resolveIdentifier(Identifiers.ComponentRef), [o.DYNAMIC_TYPE])),
|
||||
o.importType(createIdentifier(Identifiers.ComponentRef), [o.DYNAMIC_TYPE])),
|
||||
new o.ClassMethod(
|
||||
'injectorGetInternal',
|
||||
[
|
||||
@ -492,7 +493,7 @@ function createViewClass(
|
||||
|
||||
const viewClass = createClassStmt({
|
||||
name: view.className,
|
||||
parent: o.importExpr(resolveIdentifier(superClass), [getContextType(view)]),
|
||||
parent: o.importExpr(createIdentifier(superClass), [getContextType(view)]),
|
||||
parentArgs: superConstructorArgs,
|
||||
ctorParams: viewConstructorArgs,
|
||||
builders: [{methods: viewMethods}, view]
|
||||
@ -526,7 +527,7 @@ function generateCreateMethod(view: CompileView): o.Statement[] {
|
||||
if (view.viewType === ViewType.HOST) {
|
||||
const hostEl = <CompileElement>view.nodes[0];
|
||||
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()
|
||||
]);
|
||||
} else {
|
||||
@ -591,7 +592,7 @@ function generateDetectChangesMethod(view: CompileView): o.Statement[] {
|
||||
if (readVars.has(DetectChangesVars.changes.name)) {
|
||||
varStmts.push(
|
||||
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));
|
||||
return varStmts.concat(stmts);
|
||||
@ -703,5 +704,5 @@ function generateCreateEmbeddedViewsMethod(view: CompileView): o.ClassMethod {
|
||||
}
|
||||
return new o.ClassMethod(
|
||||
'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> = [];
|
||||
const view = new CompileView(
|
||||
component, this._genConfig, pipes, styles, compiledAnimations, 0,
|
||||
CompileElement.createNull(), []);
|
||||
CompileElement.createNull(), [], dependencies);
|
||||
|
||||
const statements: o.Statement[] = [];
|
||||
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 {AnimationCompiler, AnimationEntryCompileResult} from '../../src/animation/animation_compiler';
|
||||
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 {ElementSchemaRegistry} from '../../src/schema/element_schema_registry';
|
||||
|
||||
@ -30,7 +30,7 @@ export function main() {
|
||||
const compileAnimations =
|
||||
(component: CompileDirectiveMetadata): AnimationEntryCompileResult[] => {
|
||||
const parsedAnimations = parser.parseComponent(component);
|
||||
return compiler.compile(component.type.name, parsedAnimations);
|
||||
return compiler.compile(identifierName(component.type), parsedAnimations);
|
||||
};
|
||||
|
||||
const compileTriggers = (input: any[]) => {
|
||||
@ -40,7 +40,7 @@ export function main() {
|
||||
});
|
||||
|
||||
const component = CompileDirectiveMetadata.create({
|
||||
type: new CompileTypeMetadata({name: 'myCmp'}),
|
||||
type: {reference: {name: 'myCmp', filePath: ''}, diDeps: [], lifecycleHooks: []},
|
||||
template: new CompileTemplateMetadata({animations: entries})
|
||||
});
|
||||
|
||||
|
@ -21,10 +21,14 @@ describe('StaticReflector', () => {
|
||||
let host: StaticReflectorHost;
|
||||
let reflector: StaticReflector;
|
||||
|
||||
beforeEach(() => {
|
||||
host = new MockStaticReflectorHost();
|
||||
reflector = new StaticReflector(host);
|
||||
});
|
||||
function init(
|
||||
testData: {[key: string]: any} = DEFAULT_TEST_DATA,
|
||||
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) {
|
||||
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',
|
||||
() => {
|
||||
const annotations = reflector.annotations(
|
||||
@ -517,11 +532,173 @@ describe('StaticReflector', () => {
|
||||
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 {
|
||||
private collector = new MetadataCollector();
|
||||
|
||||
constructor(private data: {[key: string]: any}) {}
|
||||
|
||||
// In tests, assume that symbols are not re-exported
|
||||
moduleNameToFileName(modulePath: string, containingFile?: string): string {
|
||||
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); }
|
||||
|
||||
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': [{
|
||||
'__symbolic': 'module',
|
||||
'version': 2,
|
||||
@ -1044,6 +1242,15 @@ class MockStaticReflectorHost implements StaticReflectorHost {
|
||||
static defaultsMethod(a, b = true, c = false) {
|
||||
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': `
|
||||
@ -1064,6 +1271,11 @@ class MockStaticReflectorHost implements StaticReflectorHost {
|
||||
providers: [MyModule.defaultsMethod('a')]
|
||||
})
|
||||
export class MyDefaultsComponent { }
|
||||
|
||||
@Component({
|
||||
providers: MyModule.withFactory()
|
||||
})
|
||||
export class MyFactoryComponent { }
|
||||
`,
|
||||
'/tmp/src/static-field.ts': `
|
||||
import {Injectable} from '@angular/core';
|
||||
@ -1162,25 +1374,3 @@ class MockStaticReflectorHost implements StaticReflectorHost {
|
||||
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 {Identifiers, resolveIdentifier} from '../../src/identifiers';
|
||||
import {Identifiers, createIdentifier} from '../../src/identifiers';
|
||||
import * as o from '../../src/output/output_ast';
|
||||
|
||||
export function main() {
|
||||
@ -16,7 +16,7 @@ export function main() {
|
||||
function check(argCount: number, expectedIdentifier: any) {
|
||||
const args = createArgs(argCount);
|
||||
expect(createInlineArray(args))
|
||||
.toEqual(o.importExpr(resolveIdentifier(expectedIdentifier)).instantiate([
|
||||
.toEqual(o.importExpr(createIdentifier(expectedIdentifier)).instantiate([
|
||||
<o.Expression>o.literal(argCount)
|
||||
].concat(args)));
|
||||
}
|
||||
@ -31,7 +31,7 @@ export function main() {
|
||||
|
||||
it('should work for arrays of length 0', () => {
|
||||
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', () => {
|
||||
|
@ -8,15 +8,12 @@
|
||||
|
||||
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 {reflector} from '@angular/core/src/reflection/reflection';
|
||||
|
||||
@Directive({selector: 'someDirective'})
|
||||
class SomeDirective {
|
||||
}
|
||||
|
||||
@Directive({selector: 'someChildDirective'})
|
||||
class SomeChildDirective extends SomeDirective {
|
||||
}
|
||||
|
||||
@Directive({selector: 'someDirective', inputs: ['c']})
|
||||
class SomeDirectiveWithInputs {
|
||||
@Input() a: any;
|
||||
@ -31,28 +28,6 @@ class SomeDirectiveWithOutputs {
|
||||
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'})
|
||||
class SomeDirectiveWithSetterProps {
|
||||
@Input('renamed')
|
||||
@ -150,11 +125,22 @@ export function main() {
|
||||
}).toThrowError('No Directive annotation found on SomeDirectiveWithoutMetadata');
|
||||
});
|
||||
|
||||
it('should not read parent class Directive metadata', function() {
|
||||
const directiveMetadata = resolver.resolve(SomeChildDirective);
|
||||
expect(directiveMetadata)
|
||||
.toEqual(new Directive(
|
||||
{selector: 'someChildDirective', inputs: [], outputs: [], host: {}, queries: {}}));
|
||||
it('should support inheriting the Directive metadata', function() {
|
||||
@Directive({selector: 'p'})
|
||||
class Parent {
|
||||
}
|
||||
|
||||
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', () => {
|
||||
@ -168,16 +154,52 @@ export function main() {
|
||||
expect(directiveMetadata.inputs).toEqual(['a: renamed']);
|
||||
});
|
||||
|
||||
it('should throw if duplicate inputs', () => {
|
||||
expect(() => {
|
||||
resolver.resolve(SomeDirectiveWithDuplicateInputs);
|
||||
}).toThrowError(`Input 'a' defined multiple times in 'SomeDirectiveWithDuplicateInputs'`);
|
||||
it('should remove duplicate inputs', () => {
|
||||
@Directive({selector: 'someDirective', inputs: ['a', 'a']})
|
||||
class SomeDirectiveWithDuplicateInputs {
|
||||
}
|
||||
|
||||
const directiveMetadata = resolver.resolve(SomeDirectiveWithDuplicateInputs);
|
||||
expect(directiveMetadata.inputs).toEqual(['a']);
|
||||
});
|
||||
|
||||
it('should throw if duplicate inputs (with rename)', () => {
|
||||
expect(() => { resolver.resolve(SomeDirectiveWithDuplicateRenamedInputs); })
|
||||
.toThrowError(
|
||||
`Input 'a' defined multiple times in 'SomeDirectiveWithDuplicateRenamedInputs'`);
|
||||
it('should use the last input if duplicate inputs (with rename)', () => {
|
||||
@Directive({selector: 'someDirective', inputs: ['a', 'localA: a']})
|
||||
class SomeDirectiveWithDuplicateInputs {
|
||||
}
|
||||
|
||||
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']);
|
||||
});
|
||||
|
||||
it('should throw if duplicate outputs', () => {
|
||||
expect(() => { resolver.resolve(SomeDirectiveWithDuplicateOutputs); })
|
||||
.toThrowError(
|
||||
`Output event 'a' defined multiple times in 'SomeDirectiveWithDuplicateOutputs'`);
|
||||
it('should remove duplicate outputs', () => {
|
||||
@Directive({selector: 'someDirective', outputs: ['a', 'a']})
|
||||
class SomeDirectiveWithDuplicateOutputs {
|
||||
}
|
||||
|
||||
const directiveMetadata = resolver.resolve(SomeDirectiveWithDuplicateOutputs);
|
||||
expect(directiveMetadata.outputs).toEqual(['a']);
|
||||
});
|
||||
|
||||
it('should throw if duplicate outputs (with rename)', () => {
|
||||
expect(() => { resolver.resolve(SomeDirectiveWithDuplicateRenamedOutputs); })
|
||||
.toThrowError(
|
||||
`Output event 'a' defined multiple times in 'SomeDirectiveWithDuplicateRenamedOutputs'`);
|
||||
it('should use the last output if duplicate outputs (with rename)', () => {
|
||||
@Directive({selector: 'someDirective', outputs: ['a', 'localA: a']})
|
||||
class SomeDirectiveWithDuplicateOutputs {
|
||||
}
|
||||
|
||||
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(
|
||||
`@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', () => {
|
||||
@ -259,6 +357,30 @@ export function main() {
|
||||
expect(directiveMetadata.queries)
|
||||
.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', () => {
|
||||
|
@ -6,53 +6,101 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {sha1} from '../../src/i18n/digest';
|
||||
import {computeMsgId, sha1} from '../../src/i18n/digest';
|
||||
|
||||
export function main(): void {
|
||||
describe('sha1', () => {
|
||||
it('should work on emnpty strings',
|
||||
() => { expect(sha1('')).toEqual('da39a3ee5e6b4b0d3255bfef95601890afd80709'); });
|
||||
describe('digest', () => {
|
||||
describe('sha1', () => {
|
||||
it('should work on empty strings',
|
||||
() => { expect(sha1('')).toEqual('da39a3ee5e6b4b0d3255bfef95601890afd80709'); });
|
||||
|
||||
it('should returns the sha1 of "hello world"',
|
||||
() => { expect(sha1('abc')).toEqual('a9993e364706816aba3e25717850c26c9cd0d89d'); });
|
||||
it('should returns the sha1 of "hello world"',
|
||||
() => { expect(sha1('abc')).toEqual('a9993e364706816aba3e25717850c26c9cd0d89d'); });
|
||||
|
||||
it('should returns the sha1 of unicode strings',
|
||||
() => { expect(sha1('你好,世界')).toEqual('3becb03b015ed48050611c8d7afe4b88f70d5a20'); });
|
||||
it('should returns the sha1 of unicode strings',
|
||||
() => { expect(sha1('你好,世界')).toEqual('3becb03b015ed48050611c8d7afe4b88f70d5a20'); });
|
||||
|
||||
it('should support arbitrary string size', () => {
|
||||
// node.js reference code:
|
||||
//
|
||||
// var crypto = require('crypto');
|
||||
//
|
||||
// function sha1(string) {
|
||||
// var shasum = crypto.createHash('sha1');
|
||||
// shasum.update(string, 'utf8');
|
||||
// return shasum.digest('hex', 'utf8');
|
||||
// }
|
||||
//
|
||||
// var prefix = `你好,世界`;
|
||||
// var result = sha1(prefix);
|
||||
// for (var size = prefix.length; size < 5000; size += 101) {
|
||||
// result = prefix + sha1(result);
|
||||
// while (result.length < size) {
|
||||
// result += result;
|
||||
// }
|
||||
// result = result.slice(-size);
|
||||
// }
|
||||
//
|
||||
// console.log(sha1(result));
|
||||
const prefix = `你好,世界`;
|
||||
let result = sha1(prefix);
|
||||
for (let size = prefix.length; size < 5000; size += 101) {
|
||||
result = prefix + sha1(result);
|
||||
while (result.length < size) {
|
||||
result += result;
|
||||
it('should support arbitrary string size', () => {
|
||||
// node.js reference code:
|
||||
//
|
||||
// var crypto = require('crypto');
|
||||
//
|
||||
// function sha1(string) {
|
||||
// var shasum = crypto.createHash('sha1');
|
||||
// shasum.update(string, 'utf8');
|
||||
// return shasum.digest('hex', 'utf8');
|
||||
// }
|
||||
//
|
||||
// var prefix = `你好,世界`;
|
||||
// var result = sha1(prefix);
|
||||
// for (var size = prefix.length; size < 5000; size += 101) {
|
||||
// result = prefix + sha1(result);
|
||||
// while (result.length < size) {
|
||||
// result += result;
|
||||
// }
|
||||
// result = result.slice(-size);
|
||||
// }
|
||||
//
|
||||
// console.log(sha1(result));
|
||||
const prefix = `你好,世界`;
|
||||
let result = sha1(prefix);
|
||||
for (let size = prefix.length; size < 5000; size += 101) {
|
||||
result = prefix + sha1(result);
|
||||
while (result.length < size) {
|
||||
result += result;
|
||||
}
|
||||
result = result.slice(-size);
|
||||
}
|
||||
result = result.slice(-size);
|
||||
}
|
||||
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
|
||||
*/
|
||||
|
||||
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 * as i18n from '../../src/i18n/i18n_ast';
|
||||
import {TranslationBundle} from '../../src/i18n/translation_bundle';
|
||||
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';
|
||||
|
||||
export function main() {
|
||||
@ -94,9 +92,10 @@ export function main() {
|
||||
],
|
||||
[
|
||||
[
|
||||
'text',
|
||||
'<ph tag name="START_PARAGRAPH">html, <ph tag 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>',
|
||||
'text', '<ph tag name="START_PARAGRAPH">html, <ph tag' +
|
||||
' 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 name="INTERPOLATION">interp</ph>]'
|
||||
],
|
||||
'', ''
|
||||
@ -190,9 +189,8 @@ export function main() {
|
||||
it('should extract from attributes in translatable elements', () => {
|
||||
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 name="CLOSE_BOLD_TEXT"></ph name="CLOSE_PARAGRAPH">'
|
||||
],
|
||||
['<ph tag name="START_PARAGRAPH"><ph tag name="START_BOLD_TEXT"></ph' +
|
||||
' name="CLOSE_BOLD_TEXT"></ph name="CLOSE_PARAGRAPH">'],
|
||||
'', ''
|
||||
],
|
||||
[['msg'], 'm', 'd'],
|
||||
@ -204,9 +202,8 @@ export function main() {
|
||||
.toEqual([
|
||||
[['msg'], 'm', 'd'],
|
||||
[
|
||||
[
|
||||
'<ph tag name="START_PARAGRAPH"><ph tag name="START_BOLD_TEXT"></ph name="CLOSE_BOLD_TEXT"></ph name="CLOSE_PARAGRAPH">'
|
||||
],
|
||||
['<ph tag name="START_PARAGRAPH"><ph tag name="START_BOLD_TEXT"></ph' +
|
||||
' name="CLOSE_BOLD_TEXT"></ph name="CLOSE_PARAGRAPH">'],
|
||||
'', ''
|
||||
],
|
||||
]);
|
||||
@ -220,7 +217,8 @@ export function main() {
|
||||
[['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`;
|
||||
expect(fakeTranslate(HTML))
|
||||
.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', () => {
|
||||
@ -359,7 +359,9 @@ export function main() {
|
||||
`<div>before<!-- i18n --><p>foo</p><span><i>bar</i></span><!-- /i18n -->after</div>`;
|
||||
expect(fakeTranslate(HTML))
|
||||
.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)
|
||||
.messages;
|
||||
|
||||
const i18nMsgMap: {[id: string]: html.Node[]} = {};
|
||||
const i18nMsgMap: {[id: string]: i18n.Node[]} = {};
|
||||
|
||||
messages.forEach(message => {
|
||||
const id = digestMessage(message);
|
||||
const text = serializeI18nNodes(message.nodes).join('');
|
||||
i18nMsgMap[id] = [new html.Text(`**${text}**`, null)];
|
||||
const id = digest(message);
|
||||
const text = serializeI18nNodes(message.nodes).join('').replace(/</g, '[');
|
||||
i18nMsgMap[id] = [new i18n.Text(`**${text}**`, null)];
|
||||
});
|
||||
|
||||
const translations = new TranslationBundle(i18nMsgMap);
|
||||
const translations = new TranslationBundle(i18nMsgMap, digest);
|
||||
|
||||
const translatedNodes =
|
||||
mergeTranslations(
|
||||
|
@ -6,9 +6,9 @@
|
||||
* 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 {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 {HtmlParser} from '../../src/ml_parser/html_parser';
|
||||
@ -272,11 +272,14 @@ export function main() {
|
||||
[['{count, plural, =1 {[1]}}'], '', ''],
|
||||
]);
|
||||
|
||||
// ICU message placeholders are reference to translations.
|
||||
// As such they have no static content but refs to message ids.
|
||||
expect(_humanizePlaceholders(html)).toEqual(['', '', '', '']);
|
||||
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',
|
||||
'',
|
||||
'',
|
||||
@ -308,13 +311,13 @@ function _humanizePlaceholders(
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
function _humanizePlaceholdersToIds(
|
||||
function _humanizePlaceholdersToMessage(
|
||||
html: string, implicitTags: string[] = [],
|
||||
implicitAttrs: {[k: string]: string[]} = {}): string[] {
|
||||
// clang-format off
|
||||
// https://github.com/angular/clang-format/issues/35
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,9 @@ export function main() {
|
||||
expectHtml(el, '#i18n-2').toBe('<div id="i18n-2"><p>imbriqué</p></div>');
|
||||
expectHtml(el, '#i18n-3')
|
||||
.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')
|
||||
.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>');
|
||||
@ -66,8 +69,10 @@ export function main() {
|
||||
expect(el.query(By.css('#i18n-14')).nativeElement).toHaveText('beaucoup');
|
||||
|
||||
cmp.sex = 'm';
|
||||
cmp.sexB = 'f';
|
||||
tb.detectChanges();
|
||||
expect(el.query(By.css('#i18n-8')).nativeElement).toHaveText('homme');
|
||||
expect(el.query(By.css('#i18n-8b')).nativeElement).toHaveText('femme');
|
||||
cmp.sex = 'f';
|
||||
tb.detectChanges();
|
||||
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-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>
|
||||
<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-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 i18n id="i18n-9">{{ "count = " + count }}</div>
|
||||
@ -135,8 +144,9 @@ function expectHtml(el: DebugElement, cssSelector: string): any {
|
||||
`
|
||||
})
|
||||
class I18nComponent {
|
||||
count: number = 0;
|
||||
sex: string = 'm';
|
||||
count: number;
|
||||
sex: string;
|
||||
sexB: string;
|
||||
}
|
||||
|
||||
class FrLocalization extends NgLocalization {
|
||||
@ -153,51 +163,52 @@ class FrLocalization extends NgLocalization {
|
||||
|
||||
const XTB = `
|
||||
<translationbundle>
|
||||
<translation id="3cb04208df1c2f62553ed48e75939cf7107f9dad">attributs i18n sur les balises</translation>
|
||||
<translation id="52895b1221effb3f3585b689f049d2784d714952">imbriqué</translation>
|
||||
<translation id="88d5f22050a9df477ee5646153558b3a4862d47e">imbriqué</translation>
|
||||
<translation id="34fec9cc62e28e8aa6ffb306fa8569ef0a8087fe"><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="67162b5af5f15fd0eb6480c88688dafdf952b93a">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="018efa03821ca41e27611e4a584736810d56ed8a"><ph name="ICU"/></translation>
|
||||
<translation id="fd3186ad2a9aa801fe072ddb16ca34cd98ae93da">{sex, sex, m {homme} f {femme}}</translation>
|
||||
<translation id="d9879678f727b244bc7c7e20f22b63d98cb14890"><ph name="INTERPOLATION"/></translation>
|
||||
<translation id="50dac33dc6fc0578884baac79d875785ed77c928">sexe = <ph name="INTERPOLATION"/></translation>
|
||||
<translation id="a46f833b1fe6ca49e8b97c18f4b7ea0b930c9383"><ph name="CUSTOM_NAME"/></translation>
|
||||
<translation id="2ec983b4893bcd5b24af33bebe3ecba63868453c">dans une section traductible</translation>
|
||||
<translation id="eee74a5be8a75881a4785905bd8302a71f7d9f75">
|
||||
<translation id="615790887472569365">attributs i18n sur les balises</translation>
|
||||
<translation id="3707494640264351337">imbriqué</translation>
|
||||
<translation id="5539162898278769904">imbriqué</translation>
|
||||
<translation id="3780349238193953556"><ph name="START_ITALIC_TEXT"/>avec des espaces réservés<ph name="CLOSE_ITALIC_TEXT"/></translation>
|
||||
<translation id="5525133077318024839">sur des balises non traductibles</translation>
|
||||
<translation id="8670732454866344690">sur des balises traductibles</translation>
|
||||
<translation id="4593805537723189714">{VAR_PLURAL, plural, =0 {zero} =1 {un} =2 {deux} other {<ph name="START_BOLD_TEXT"/>beaucoup<ph name="CLOSE_BOLD_TEXT"/>}}</translation>
|
||||
<translation id="1746565782635215"><ph name="ICU"/></translation>
|
||||
<translation id="5868084092545682515">{VAR_SELECT, select, m {homme} f {femme}}</translation>
|
||||
<translation id="4851788426695310455"><ph name="INTERPOLATION"/></translation>
|
||||
<translation id="9013357158046221374">sexe = <ph name="INTERPOLATION"/></translation>
|
||||
<translation id="8324617391167353662"><ph name="CUSTOM_NAME"/></translation>
|
||||
<translation id="7685649297917455806">dans une section traductible</translation>
|
||||
<translation id="2387287228265107305">
|
||||
<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_1"/><ph name="ICU"/><ph name="CLOSE_TAG_DIV"></ph>
|
||||
</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>`;
|
||||
|
||||
// unused, for reference only
|
||||
// can be generated from xmb_spec as follow:
|
||||
// `iit('extract xmb', () => { console.log(toXmb(HTML)); });`
|
||||
// `fit('extract xmb', () => { console.log(toXmb(HTML)); });`
|
||||
const XMB = `
|
||||
<messagebundle>
|
||||
<msg id="3cb04208df1c2f62553ed48e75939cf7107f9dad">i18n attribute on tags</msg>
|
||||
<msg id="52895b1221effb3f3585b689f049d2784d714952">nested</msg>
|
||||
<msg id="88d5f22050a9df477ee5646153558b3a4862d47e" 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="1fe4616cce80a57c7707bac1c97054aa8e244a67">on not translatable node</msg>
|
||||
<msg id="67162b5af5f15fd0eb6480c88688dafdf952b93a">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="018efa03821ca41e27611e4a584736810d56ed8a">
|
||||
<msg id="615790887472569365">i18n attribute on tags</msg>
|
||||
<msg id="3707494640264351337">nested</msg>
|
||||
<msg id="5539162898278769904" meaning="different meaning">nested</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="5525133077318024839">on not translatable node</msg>
|
||||
<msg id="8670732454866344690">on translatable node</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="1746565782635215">
|
||||
<ph name="ICU"/>
|
||||
</msg>
|
||||
<msg id="fd3186ad2a9aa801fe072ddb16ca34cd98ae93da">{sex, sex, m {male}f {female}}</msg>
|
||||
<msg id="d9879678f727b244bc7c7e20f22b63d98cb14890"><ph name="INTERPOLATION"/></msg>
|
||||
<msg id="50dac33dc6fc0578884baac79d875785ed77c928">sex = <ph name="INTERPOLATION"/></msg>
|
||||
<msg id="a46f833b1fe6ca49e8b97c18f4b7ea0b930c9383"><ph name="CUSTOM_NAME"/></msg>
|
||||
<msg id="2ec983b4893bcd5b24af33bebe3ecba63868453c">in a translatable section</msg>
|
||||
<msg id="eee74a5be8a75881a4785905bd8302a71f7d9f75">
|
||||
<msg id="5868084092545682515">{VAR_SELECT, select, m {male} f {female} }</msg>
|
||||
<msg id="4851788426695310455"><ph name="INTERPOLATION"/></msg>
|
||||
<msg id="9013357158046221374">sex = <ph name="INTERPOLATION"/></msg>
|
||||
<msg id="8324617391167353662"><ph name="CUSTOM_NAME"/></msg>
|
||||
<msg id="7685649297917455806">in a translatable section</msg>
|
||||
<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_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>
|
||||
</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>
|
||||
</messagebundle>`;
|
||||
<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>
|
||||
`;
|
||||
|
@ -6,12 +6,10 @@
|
||||
* 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 * as i18n from '../../src/i18n/i18n_ast';
|
||||
import {MessageBundle} from '../../src/i18n/message_bundle';
|
||||
import {Serializer} from '../../src/i18n/serializers/serializer';
|
||||
import {HtmlParser} from '../../src/ml_parser/html_parser';
|
||||
import {DEFAULT_INTERPOLATION_CONFIG} from '../../src/ml_parser/interpolation_config';
|
||||
|
||||
@ -26,17 +24,18 @@ export function main(): void {
|
||||
messages.updateFromTemplate(
|
||||
'<p i18n="m|d">Translate Me</p>', 'url', DEFAULT_INTERPOLATION_CONFIG);
|
||||
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(
|
||||
'<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);
|
||||
expect(humanizeMessages(messages)).toEqual([
|
||||
'2e791a68a3324ecdd29e252198638dafacec46e9=Translate Me',
|
||||
'8ca133f957845af1b1868da1b339180d1f519644=Translate Me',
|
||||
'Translate Me (m|d)',
|
||||
'Translate Me (|)',
|
||||
'Translate Me (|)',
|
||||
]);
|
||||
});
|
||||
});
|
||||
@ -44,13 +43,14 @@ export function main(): void {
|
||||
}
|
||||
|
||||
class _TestSerializer implements Serializer {
|
||||
write(messageMap: {[id: string]: i18n.Message}): string {
|
||||
return Object.keys(messageMap)
|
||||
.map(id => `${id}=${serializeNodes(messageMap[id].nodes)}`)
|
||||
write(messages: i18n.Message[]): string {
|
||||
return messages.map(msg => `${serializeNodes(msg.nodes)} (${msg.meaning}|${msg.description})`)
|
||||
.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[] {
|
||||
|
@ -6,8 +6,6 @@
|
||||
* 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';
|
||||
|
||||
export function main(): void {
|
||||
|
@ -6,12 +6,13 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Xliff} from '@angular/compiler/src/i18n/serializers/xliff';
|
||||
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||
import {escapeRegExp} from '@angular/core/src/facade/lang';
|
||||
|
||||
import {serializeNodes} from '../../../src/i18n/digest';
|
||||
import {MessageBundle} from '../../../src/i18n/message_bundle';
|
||||
import {Xliff} from '../../../src/i18n/serializers/xliff';
|
||||
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';
|
||||
|
||||
const HTML = `
|
||||
<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 {
|
||||
let serializer: Xliff;
|
||||
let htmlParser: HtmlParser;
|
||||
const serializer = new Xliff();
|
||||
|
||||
function toXliff(html: string): string {
|
||||
const catalog = new MessageBundle(new HtmlParser, [], {});
|
||||
@ -86,39 +86,130 @@ export function main(): void {
|
||||
return catalog.write(serializer);
|
||||
}
|
||||
|
||||
function loadAsText(template: string, xliff: string): {[id: string]: string} {
|
||||
const messageBundle = new MessageBundle(htmlParser, [], {});
|
||||
messageBundle.updateFromTemplate(template, 'url', DEFAULT_INTERPOLATION_CONFIG);
|
||||
function loadAsMap(xliff: string): {[id: string]: string} {
|
||||
const i18nNodesByMsgId = serializer.load(xliff, 'url');
|
||||
const msgMap: {[id: string]: string} = {};
|
||||
Object.keys(i18nNodesByMsgId)
|
||||
.forEach(id => msgMap[id] = serializeNodes(i18nNodesByMsgId[id]).join(''));
|
||||
|
||||
const asAst = serializer.load(xliff, 'url', messageBundle);
|
||||
const asText: {[id: string]: string} = {};
|
||||
Object.keys(asAst).forEach(id => { asText[id] = serializeNodes(asAst[id]).join(''); });
|
||||
|
||||
return asText;
|
||||
return msgMap;
|
||||
}
|
||||
|
||||
describe('XLIFF serializer', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
htmlParser = new HtmlParser();
|
||||
serializer = new Xliff(htmlParser, DEFAULT_INTERPOLATION_CONFIG);
|
||||
});
|
||||
|
||||
|
||||
describe('write', () => {
|
||||
it('should write a valid xliff file', () => { expect(toXliff(HTML)).toEqual(WRITE_XLIFF); });
|
||||
});
|
||||
|
||||
describe('load', () => {
|
||||
it('should load XLIFF files', () => {
|
||||
expect(loadAsText(HTML, LOAD_XLIFF)).toEqual({
|
||||
expect(loadAsMap(LOAD_XLIFF)).toEqual({
|
||||
'983775b9a51ce14b036be72d4cfd65d68d64e231': 'etubirtta elbatalsnart',
|
||||
'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',
|
||||
'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)>
|
||||
]>
|
||||
<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="e2ccf3d131b15f54aa1fcf1314b1ca77c14bfcc2">{ count, 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="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="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="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="7999024498831672133" desc="d" meaning="m">foo</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>
|
||||
`;
|
||||
|
||||
@ -55,7 +55,7 @@ export function main(): void {
|
||||
it('should throw when trying to load an xmb file', () => {
|
||||
expect(() => {
|
||||
const serializer = new Xmb();
|
||||
serializer.load(XMB, 'url', null);
|
||||
serializer.load(XMB, 'url');
|
||||
}).toThrowError(/Unsupported/);
|
||||
});
|
||||
});
|
||||
|
@ -6,8 +6,6 @@
|
||||
* 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';
|
||||
|
||||
export function main(): void {
|
||||
|
@ -8,37 +8,24 @@
|
||||
|
||||
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 {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 {
|
||||
describe('XTB serializer', () => {
|
||||
let serializer: Xtb;
|
||||
let htmlParser: HtmlParser;
|
||||
const serializer = new Xtb();
|
||||
|
||||
function loadAsText(template: string, xtb: string): {[id: string]: string} {
|
||||
const messageBundle = new MessageBundle(htmlParser, [], {});
|
||||
messageBundle.updateFromTemplate(template, 'url', DEFAULT_INTERPOLATION_CONFIG);
|
||||
|
||||
const asAst = serializer.load(xtb, 'url', messageBundle);
|
||||
const asText: {[id: string]: string} = {};
|
||||
Object.keys(asAst).forEach(id => { asText[id] = serializeNodes(asAst[id]).join(''); });
|
||||
|
||||
return asText;
|
||||
function loadAsMap(xtb: string): {[id: string]: string} {
|
||||
const i18nNodesByMsgId = serializer.load(xtb, 'url');
|
||||
const msgMap: {[id: string]: string} = {};
|
||||
Object.keys(i18nNodesByMsgId).forEach(id => {
|
||||
msgMap[id] = serializeNodes(i18nNodesByMsgId[id]).join('');
|
||||
});
|
||||
return msgMap;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
htmlParser = new HtmlParser();
|
||||
serializer = new Xtb(htmlParser, DEFAULT_INTERPOLATION_CONFIG);
|
||||
});
|
||||
|
||||
describe('load', () => {
|
||||
it('should load XTB files with a doctype', () => {
|
||||
const HTML = `<div i18n>bar</div>`;
|
||||
|
||||
const XTB = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE translationbundle [<!ELEMENT translationbundle (translation)*>
|
||||
<!ATTLIST translationbundle lang CDATA #REQUIRED>
|
||||
@ -50,75 +37,66 @@ export function main(): void {
|
||||
<!ATTLIST ph name CDATA #REQUIRED>
|
||||
]>
|
||||
<translationbundle>
|
||||
<translation id="28a86c8a00ae573b2bac698d6609316dc7b4a226">rab</translation>
|
||||
<translation id="8841459487341224498">rab</translation>
|
||||
</translationbundle>`;
|
||||
|
||||
expect(loadAsText(HTML, XTB)).toEqual({'28a86c8a00ae573b2bac698d6609316dc7b4a226': 'rab'});
|
||||
expect(loadAsMap(XTB)).toEqual({'8841459487341224498': 'rab'});
|
||||
});
|
||||
|
||||
it('should load XTB files without placeholders', () => {
|
||||
const HTML = `<div i18n>bar</div>`;
|
||||
|
||||
const XTB = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<translationbundle>
|
||||
<translation id="28a86c8a00ae573b2bac698d6609316dc7b4a226">rab</translation>
|
||||
<translation id="8841459487341224498">rab</translation>
|
||||
</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"?>
|
||||
<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>`;
|
||||
|
||||
expect(loadAsText(HTML, XTB)).toEqual({
|
||||
'7de4d8ff1e42b7b31da6204074818236a9a5317f': '<p>rab</p>'
|
||||
expect(loadAsMap(XTB)).toEqual({
|
||||
'8877975308926375834': '<ph name="START_PARAGRAPH"/>rab<ph name="CLOSE_PARAGRAPH"/>'
|
||||
});
|
||||
});
|
||||
|
||||
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" ?>
|
||||
<translationbundle>
|
||||
<translation id="eb404e202fed4846e25e7d9ac1fcb719fe4da257">*<ph name="ICU"/>*</translation>
|
||||
<translation id="fc92b9b781194a02ab773129c8c5a7fc0735efd7">{ count, plural, =1 {<ph name="START_PARAGRAPH"/>rab<ph name="CLOSE_PARAGRAPH"/>}}</translation>
|
||||
<translation id="7717087045075616176">*<ph name="ICU"/>*</translation>
|
||||
<translation id="5115002811911870583">{VAR_PLURAL, plural, =1 {<ph name="START_PARAGRAPH"/>rab<ph name="CLOSE_PARAGRAPH"/>}}</translation>
|
||||
</translationbundle>`;
|
||||
|
||||
expect(loadAsText(HTML, XTB)).toEqual({
|
||||
'eb404e202fed4846e25e7d9ac1fcb719fe4da257': `*{ count, plural, =1 {<p>rab</p>}}*`,
|
||||
'fc92b9b781194a02ab773129c8c5a7fc0735efd7': `{ count, plural, =1 {<p>rab</p>}}`,
|
||||
expect(loadAsMap(XTB)).toEqual({
|
||||
'7717087045075616176': `*<ph name="ICU"/>*`,
|
||||
'5115002811911870583':
|
||||
`{VAR_PLURAL, plural, =1 {[<ph name="START_PARAGRAPH"/>, rab, <ph name="CLOSE_PARAGRAPH"/>]}}`,
|
||||
});
|
||||
});
|
||||
|
||||
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" ?>
|
||||
<translationbundle>
|
||||
<translation id="7103b4b13b616270a0044efade97d8b4f96f2ca6"><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="db3e0a6a5a96481f60aec61d98c3eecddef5ac23">oof</translation>
|
||||
<translation id="8fb569d3dd83e92eff2551b24f5290d3035ce61b">{ count, plural, =1 {{ sex, select, other {<ph name="START_PARAGRAPH"/>rab<ph name="CLOSE_PARAGRAPH"/>}} }}</translation>
|
||||
<translation id="8281795707202401639"><ph name="INTERPOLATION"/><ph name="START_BOLD_TEXT"/>rab<ph name="CLOSE_BOLD_TEXT"/> oof</translation>
|
||||
<translation id="5115002811911870583">{VAR_PLURAL, plural, =1 {<ph name="START_PARAGRAPH"/>rab<ph name="CLOSE_PARAGRAPH"/>}}</translation>
|
||||
<translation id="130772889486467622">oof</translation>
|
||||
<translation id="4739316421648347533">{VAR_PLURAL, plural, =1 {{VAR_GENDER, gender, male {<ph name="START_PARAGRAPH"/>rab<ph name="CLOSE_PARAGRAPH"/>}} }}</translation>
|
||||
</translationbundle>`;
|
||||
|
||||
expect(loadAsText(HTML, XTB)).toEqual({
|
||||
'7103b4b13b616270a0044efade97d8b4f96f2ca6': `{{ a + b }}<b>rab</b> oof`,
|
||||
'fc92b9b781194a02ab773129c8c5a7fc0735efd7': `{ count, plural, =1 {<p>rab</p>}}`,
|
||||
'db3e0a6a5a96481f60aec61d98c3eecddef5ac23': `oof`,
|
||||
'8fb569d3dd83e92eff2551b24f5290d3035ce61b':
|
||||
`{ count, plural, =1 {{ sex, select, other {<p>rab</p>}} }}`,
|
||||
expect(loadAsMap(XTB)).toEqual({
|
||||
'8281795707202401639':
|
||||
`<ph name="INTERPOLATION"/><ph name="START_BOLD_TEXT"/>rab<ph name="CLOSE_BOLD_TEXT"/> oof`,
|
||||
'5115002811911870583':
|
||||
`{VAR_PLURAL, plural, =1 {[<ph name="START_PARAGRAPH"/>, rab, <ph name="CLOSE_PARAGRAPH"/>]}}`,
|
||||
'130772889486467622': `oof`,
|
||||
'4739316421648347533':
|
||||
`{VAR_PLURAL, plural, =1 {[{VAR_GENDER, gender, male {[<ph name="START_PARAGRAPH"/>, rab, <ph name="CLOSE_PARAGRAPH"/>]}}, ]}}`,
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('errors', () => {
|
||||
@ -127,7 +105,7 @@ export function main(): void {
|
||||
'<translationbundle><translationbundle></translationbundle></translationbundle>';
|
||||
|
||||
expect(() => {
|
||||
loadAsText('', XTB);
|
||||
loadAsMap(XTB);
|
||||
}).toThrowError(/<translationbundle> elements can not be nested/);
|
||||
});
|
||||
|
||||
@ -136,58 +114,49 @@ export function main(): void {
|
||||
<translation></translation>
|
||||
</translationbundle>`;
|
||||
|
||||
expect(() => {
|
||||
loadAsText('', XTB);
|
||||
}).toThrowError(/<translation> misses the "id" attribute/);
|
||||
expect(() => { loadAsMap(XTB); }).toThrowError(/<translation> misses the "id" attribute/);
|
||||
});
|
||||
|
||||
it('should throw when a placeholder has no name attribute', () => {
|
||||
const HTML = '<div i18n>give me a message</div>';
|
||||
|
||||
const XTB = `<translationbundle>
|
||||
<translation id="8de97c6a35252d9409dcaca0b8171c952740b28c"><ph /></translation>
|
||||
<translation id="1186013544048295927"><ph /></translation>
|
||||
</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', () => {
|
||||
const HTML = `<div i18n>bar</div>`;
|
||||
it('should throw on unknown xtb tags', () => {
|
||||
const XTB = `<what></what>`;
|
||||
|
||||
const XTB = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<translationbundle>
|
||||
<translation id="28a86c8a00ae573b2bac698d6609316dc7b4a226"><ph name="UNKNOWN"/></translation>
|
||||
expect(() => {
|
||||
loadAsMap(XTB);
|
||||
}).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(() => {
|
||||
loadAsText(HTML, XTB);
|
||||
}).toThrowError(/The placeholder "UNKNOWN" does not exists in the source message/);
|
||||
loadAsMap(XTB);
|
||||
}).toThrowError(/Duplicated translations for msg 1186013544048295927/);
|
||||
});
|
||||
});
|
||||
|
||||
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 when trying to save an xtb file',
|
||||
() => { expect(() => { serializer.write([]); }).toThrowError(/Unsupported/); });
|
||||
|
||||
});
|
||||
|
||||
it('should throw on unknown tags', () => {
|
||||
const XTB = `<what></what>`;
|
||||
|
||||
expect(() => {
|
||||
loadAsText('', XTB);
|
||||
}).toThrowError(new RegExp(escapeRegExp(`Unexpected tag ("[ERROR ->]<what></what>")`)));
|
||||
});
|
||||
|
||||
it('should throw when trying to save an xtb file',
|
||||
() => { 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 {TestBed, async, inject} from '@angular/core/testing';
|
||||
|
||||
import {identifierName} from '../src/compile_metadata';
|
||||
import {stringify} from '../src/facade/lang';
|
||||
import {CompileMetadataResolver} from '../src/metadata_resolver';
|
||||
import {ResourceLoader} from '../src/resource_loader';
|
||||
@ -50,7 +51,7 @@ export function main() {
|
||||
expect(meta.exportAs).toEqual('someExportAs');
|
||||
expect(meta.isComponent).toBe(true);
|
||||
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.changeDetection).toBe(ChangeDetectionStrategy.Default);
|
||||
expect(meta.inputs).toEqual({'someProp': 'someProp'});
|
||||
@ -114,18 +115,24 @@ export function main() {
|
||||
resourceLoader.flush();
|
||||
})));
|
||||
|
||||
it('should use the moduleUrl from the reflector if none is given',
|
||||
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
||||
it('should use `./` as base url for templates during runtime compilation if no moduleId is given',
|
||||
async(inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
||||
@Component({selector: 'someComponent', templateUrl: 'someUrl'})
|
||||
class ComponentWithoutModuleId {
|
||||
}
|
||||
|
||||
|
||||
@NgModule({declarations: [ComponentWithoutModuleId]})
|
||||
class SomeModule {
|
||||
}
|
||||
resolver.loadNgModuleMetadata(SomeModule, true);
|
||||
|
||||
const value: string =
|
||||
resolver.getDirectiveMetadata(ComponentWithoutModuleId).type.moduleUrl;
|
||||
const expectedEndValue = './ComponentWithoutModuleId';
|
||||
expect(value.endsWith(expectedEndValue)).toBe(true);
|
||||
}));
|
||||
resolver.loadNgModuleMetadata(SomeModule, false).loading.then(() => {
|
||||
const value: string =
|
||||
resolver.getDirectiveMetadata(ComponentWithoutModuleId).template.templateUrl;
|
||||
const expectedEndValue = './someUrl';
|
||||
expect(value.endsWith(expectedEndValue)).toBe(true);
|
||||
});
|
||||
})));
|
||||
|
||||
it('should throw when the moduleId is not a string',
|
||||
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
||||
|
@ -97,4 +97,4 @@ const serializerVisitor = new _SerializerVisitor();
|
||||
|
||||
export function serializeNodes(nodes: html.Node[]): string[] {
|
||||
return nodes.map(node => node.visit(serializerVisitor, null));
|
||||
}
|
||||
}
|
@ -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))
|
||||
.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 {JavaScriptEmitter} from '@angular/compiler/src/output/js_emitter';
|
||||
import * as o from '@angular/compiler/src/output/output_ast';
|
||||
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {SimpleJsImportGenerator} from './output_emitter_util';
|
||||
import {ImportResolver} from '@angular/compiler/src/output/path_util';
|
||||
|
||||
const someModuleUrl = 'somePackage/somePath';
|
||||
const anotherModuleUrl = 'somePackage/someOtherPath';
|
||||
|
||||
const sameModuleIdentifier =
|
||||
new CompileIdentifierMetadata({name: 'someLocalId', moduleUrl: someModuleUrl});
|
||||
const sameModuleIdentifier: CompileIdentifierMetadata = {
|
||||
reference: {name: 'someLocalId', filePath: someModuleUrl}
|
||||
};
|
||||
const externalModuleIdentifier: CompileIdentifierMetadata = {
|
||||
reference: {name: 'someExternalId', filePath: anotherModuleUrl}
|
||||
};
|
||||
|
||||
const externalModuleIdentifier =
|
||||
new CompileIdentifierMetadata({name: 'someExternalId', moduleUrl: anotherModuleUrl});
|
||||
class SimpleJsImportGenerator implements ImportResolver {
|
||||
fileNameToModuleName(importedUrlStr: string, moduleUrlStr: string): string {
|
||||
return importedUrlStr;
|
||||
}
|
||||
}
|
||||
|
||||
export function main() {
|
||||
// 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 {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 {ImportResolver} from '@angular/compiler/src/output/path_util';
|
||||
import {EventEmitter} from '@angular/core';
|
||||
@ -20,23 +20,29 @@ export class ExternalClass {
|
||||
someMethod(a: any /** TODO #9100 */) { return {'param': a, 'data': this.data}; }
|
||||
}
|
||||
|
||||
const testDataIdentifier = new CompileIdentifierMetadata({
|
||||
const testDataIdentifier = {
|
||||
name: 'ExternalClass',
|
||||
moduleUrl: `@angular/compiler/test/output/output_emitter_util`,
|
||||
reference: ExternalClass
|
||||
});
|
||||
runtime: ExternalClass
|
||||
};
|
||||
|
||||
const eventEmitterIdentifier = new CompileIdentifierMetadata(
|
||||
{name: 'EventEmitter', moduleUrl: assetUrl('core'), reference: EventEmitter});
|
||||
const eventEmitterIdentifier = {
|
||||
name: 'EventEmitter',
|
||||
moduleUrl: assetUrl('core'),
|
||||
runtime: EventEmitter
|
||||
};
|
||||
|
||||
const enumIdentifier = new CompileIdentifierMetadata({
|
||||
const enumIdentifier = {
|
||||
name: 'ViewType.HOST',
|
||||
moduleUrl: assetUrl('core', 'linker/view_type'),
|
||||
reference: ViewType.HOST
|
||||
});
|
||||
runtime: ViewType.HOST
|
||||
};
|
||||
|
||||
const baseErrorIdentifier = new CompileIdentifierMetadata(
|
||||
{name: 'BaseError', moduleUrl: assetUrl('core', 'facade/errors'), reference: BaseError});
|
||||
const baseErrorIdentifier = {
|
||||
name: 'BaseError',
|
||||
moduleUrl: assetUrl('core', 'facade/errors'),
|
||||
runtime: BaseError
|
||||
};
|
||||
|
||||
export var codegenExportsVars = [
|
||||
'getExpressions',
|
||||
@ -58,7 +64,7 @@ const _getExpressionsStmts: o.Statement[] = [
|
||||
o.variable('map').key(o.literal('changeable')).set(o.literal('changedValue')).toStmt(),
|
||||
|
||||
o.variable('externalInstance')
|
||||
.set(o.importExpr(testDataIdentifier).instantiate([o.literal('someValue')]))
|
||||
.set(o.importExpr(createIdentifier(testDataIdentifier)).instantiate([o.literal('someValue')]))
|
||||
.toDeclStmt(),
|
||||
o.variable('externalInstance').prop('changeable').set(o.literal('changedValue')).toStmt(),
|
||||
|
||||
@ -69,8 +75,8 @@ const _getExpressionsStmts: o.Statement[] = [
|
||||
.toDeclStmt(),
|
||||
|
||||
o.variable('throwError')
|
||||
.set(o.fn([], [new o.ThrowStmt(
|
||||
o.importExpr(baseErrorIdentifier).instantiate([o.literal('someError')]))]))
|
||||
.set(o.fn([], [new o.ThrowStmt(o.importExpr(createIdentifier(baseErrorIdentifier))
|
||||
.instantiate([o.literal('someError')]))]))
|
||||
.toDeclStmt(),
|
||||
|
||||
o.variable('catchError')
|
||||
@ -152,9 +158,9 @@ const _getExpressionsStmts: o.Statement[] = [
|
||||
|
||||
['not', o.not(o.literal(false))],
|
||||
|
||||
['externalTestIdentifier', o.importExpr(testDataIdentifier)],
|
||||
['externalSrcIdentifier', o.importExpr(eventEmitterIdentifier)],
|
||||
['externalEnumIdentifier', o.importExpr(enumIdentifier)],
|
||||
['externalTestIdentifier', o.importExpr(createIdentifier(testDataIdentifier))],
|
||||
['externalSrcIdentifier', o.importExpr(createIdentifier(eventEmitterIdentifier))],
|
||||
['externalEnumIdentifier', o.importExpr(createIdentifier(enumIdentifier))],
|
||||
|
||||
['externalInstance', o.variable('externalInstance')],
|
||||
['dynamicInstance', o.variable('dynamicInstance')],
|
||||
@ -188,7 +194,7 @@ export var codegenStmts: o.Statement[] = [
|
||||
new o.CommentStmt('This is a comment'),
|
||||
|
||||
new o.ClassStmt(
|
||||
'DynamicClass', o.importExpr(testDataIdentifier),
|
||||
'DynamicClass', o.importExpr(createIdentifier(testDataIdentifier)),
|
||||
[
|
||||
new o.ClassField('dynamicProp', o.DYNAMIC_TYPE),
|
||||
new o.ClassField('dynamicChangeable', o.DYNAMIC_TYPE),
|
||||
|
@ -8,19 +8,25 @@
|
||||
|
||||
import {CompileIdentifierMetadata} from '@angular/compiler/src/compile_metadata';
|
||||
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 {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {SimpleJsImportGenerator} from './output_emitter_util';
|
||||
|
||||
const someModuleUrl = 'somePackage/somePath';
|
||||
const anotherModuleUrl = 'somePackage/someOtherPath';
|
||||
|
||||
const sameModuleIdentifier =
|
||||
new CompileIdentifierMetadata({name: 'someLocalId', moduleUrl: someModuleUrl});
|
||||
const sameModuleIdentifier: CompileIdentifierMetadata = {
|
||||
reference: {name: 'someLocalId', filePath: someModuleUrl}
|
||||
};
|
||||
|
||||
const externalModuleIdentifier =
|
||||
new CompileIdentifierMetadata({name: 'someExternalId', moduleUrl: anotherModuleUrl});
|
||||
const externalModuleIdentifier: CompileIdentifierMetadata = {
|
||||
reference: {name: 'someExternalId', filePath: anotherModuleUrl}
|
||||
};
|
||||
|
||||
class SimpleJsImportGenerator implements ImportResolver {
|
||||
fileNameToModuleName(importedUrlStr: string, moduleUrlStr: string): string {
|
||||
return importedUrlStr;
|
||||
}
|
||||
}
|
||||
|
||||
export function main() {
|
||||
// Note supported features of our OutputAsti n TS:
|
||||
@ -318,6 +324,14 @@ export function main() {
|
||||
].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', () => {
|
||||
const writeVarExpr = o.variable('a').set(o.NULL_EXPR);
|
||||
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
|
||||
*/
|
||||
|
||||
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 {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';
|
||||
@ -14,10 +14,9 @@ import {TEMPLATE_TRANSFORMS, TemplateParser, splitClasses} from '@angular/compil
|
||||
import {TEST_COMPILER_PROVIDERS} from '@angular/compiler/testing/test_bindings';
|
||||
import {SchemaMetadata, SecurityContext, Type} from '@angular/core';
|
||||
import {Console} from '@angular/core/src/console';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {beforeEach, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||
import {TestBed, inject} from '@angular/core/testing';
|
||||
|
||||
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 {MockSchemaRegistry} from '../../testing/index';
|
||||
import {unparse} from '../expression_parser/unparser';
|
||||
@ -31,6 +30,11 @@ const MOCK_SCHEMA_REGISTRY = [{
|
||||
['onEvent'], ['onEvent']),
|
||||
}];
|
||||
|
||||
function createTypeMeta({reference, diDeps}: {reference: any, diDeps?: any[]}):
|
||||
CompileTypeMetadata {
|
||||
return {reference: reference, diDeps: diDeps || [], lifecycleHooks: []};
|
||||
}
|
||||
|
||||
export function main() {
|
||||
let ngIf: CompileDirectiveSummary;
|
||||
let parse: (
|
||||
@ -49,16 +53,14 @@ export function main() {
|
||||
const component = CompileDirectiveMetadata.create({
|
||||
selector: 'root',
|
||||
template: someTemplate,
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'Root', reference: {} as Type<any>}),
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'Root'}}),
|
||||
isComponent: true
|
||||
});
|
||||
ngIf = CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: '[ngIf]',
|
||||
template: someTemplate,
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'NgIf', reference: {} as Type<any>}),
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'NgIf'}}),
|
||||
inputs: ['ngIf']
|
||||
})
|
||||
.toSummary();
|
||||
@ -302,8 +304,7 @@ export function main() {
|
||||
inject([TemplateParser], (parser: TemplateParser) => {
|
||||
const component = CompileDirectiveMetadata.create({
|
||||
selector: 'test',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'Test', reference: {} as Type<any>}),
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'Test'}}),
|
||||
isComponent: true,
|
||||
template: new CompileTemplateMetadata({interpolation: ['{%', '%}']})
|
||||
});
|
||||
@ -470,10 +471,8 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||
.create({
|
||||
selector: 'div',
|
||||
template: new CompileTemplateMetadata({animations: animationEntries}),
|
||||
type: new CompileTypeMetadata({
|
||||
moduleUrl: someModuleUrl,
|
||||
name: 'DirA',
|
||||
reference: {} as Type<any>
|
||||
type: createTypeMeta({
|
||||
reference: {filePath: someModuleUrl, name: 'DirA'},
|
||||
}),
|
||||
host: {'[@prop]': 'expr'}
|
||||
})
|
||||
@ -488,8 +487,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||
CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: 'broken',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
|
||||
host: {'[class.foo]': null}
|
||||
})
|
||||
.toSummary();
|
||||
@ -504,8 +502,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||
CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: 'broken',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
|
||||
host: {'(click)': null}
|
||||
})
|
||||
.toSummary();
|
||||
@ -572,11 +569,8 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||
.create({
|
||||
selector: 'template',
|
||||
outputs: ['e'],
|
||||
type: new CompileTypeMetadata({
|
||||
moduleUrl: someModuleUrl,
|
||||
name: 'DirA',
|
||||
reference: {} as Type<any>
|
||||
})
|
||||
type: createTypeMeta(
|
||||
{reference: {filePath: someModuleUrl, name: 'DirA'}})
|
||||
})
|
||||
.toSummary();
|
||||
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
|
||||
.create({
|
||||
selector: '[a]',
|
||||
type: new CompileTypeMetadata({
|
||||
moduleUrl: someModuleUrl,
|
||||
name: 'DirA',
|
||||
reference: {} as Type<any>
|
||||
})
|
||||
type: createTypeMeta(
|
||||
{reference: {filePath: someModuleUrl, name: 'DirA'}})
|
||||
})
|
||||
.toSummary();
|
||||
const dirB = CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: '[b]',
|
||||
type: new CompileTypeMetadata({
|
||||
moduleUrl: someModuleUrl,
|
||||
name: 'DirB',
|
||||
reference: {} as Type<any>
|
||||
})
|
||||
type: createTypeMeta(
|
||||
{reference: {filePath: someModuleUrl, name: 'DirB'}})
|
||||
})
|
||||
.toSummary();
|
||||
const dirC = CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: '[c]',
|
||||
type: new CompileTypeMetadata({
|
||||
moduleUrl: someModuleUrl,
|
||||
name: 'DirC',
|
||||
reference: {} as Type<any>
|
||||
})
|
||||
type: createTypeMeta(
|
||||
{reference: {filePath: someModuleUrl, name: 'DirC'}})
|
||||
})
|
||||
.toSummary();
|
||||
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
|
||||
.create({
|
||||
selector: '[a=b]',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>})
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}})
|
||||
})
|
||||
.toSummary();
|
||||
const dirB =
|
||||
CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: '[b]',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirB', reference: {} as Type<any>})
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirB'}})
|
||||
})
|
||||
.toSummary();
|
||||
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
|
||||
.create({
|
||||
selector: '[a]',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirB', reference: {} as Type<any>})
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirB'}})
|
||||
})
|
||||
.toSummary();
|
||||
|
||||
@ -692,8 +674,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||
CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: 'div',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
|
||||
host: {'[a]': 'expr'}
|
||||
})
|
||||
.toSummary();
|
||||
@ -708,8 +689,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||
CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: 'div',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
|
||||
host: {'(a)': 'expr'}
|
||||
})
|
||||
.toSummary();
|
||||
@ -723,8 +703,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||
CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: 'div',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
|
||||
inputs: ['aProp']
|
||||
})
|
||||
.toSummary();
|
||||
@ -739,8 +718,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||
CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: 'div',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
|
||||
inputs: ['b:a']
|
||||
})
|
||||
.toSummary();
|
||||
@ -755,8 +733,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||
CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: 'div',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
|
||||
inputs: ['a']
|
||||
})
|
||||
.toSummary();
|
||||
@ -771,8 +748,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||
CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: 'div',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
|
||||
inputs: ['a']
|
||||
})
|
||||
.toSummary();
|
||||
@ -788,8 +764,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||
CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: 'div',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
|
||||
inputs: ['a']
|
||||
})
|
||||
.toSummary();
|
||||
@ -807,12 +782,9 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||
let token: CompileTokenMetadata;
|
||||
if (value.startsWith('type:')) {
|
||||
const name = value.substring(5);
|
||||
token = new CompileTokenMetadata({
|
||||
identifier: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name, reference: name as any as Type<any>})
|
||||
});
|
||||
token = {identifier: createTypeMeta({reference: <any>name})};
|
||||
} else {
|
||||
token = new CompileTokenMetadata({value: value});
|
||||
token = {value: value};
|
||||
}
|
||||
return token;
|
||||
}
|
||||
@ -833,24 +805,28 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||
isHost = true;
|
||||
value = value.substring(5);
|
||||
}
|
||||
return new CompileDiDependencyMetadata({
|
||||
return {
|
||||
token: createToken(value),
|
||||
isOptional: isOptional,
|
||||
isSelf: isSelf,
|
||||
isHost: isHost
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function createProvider(
|
||||
token: string, {multi = false, deps = []}: {multi?: boolean, deps?: string[]} = {}):
|
||||
CompileProviderMetadata {
|
||||
const name = `provider${nextProviderId++}`;
|
||||
return new CompileProviderMetadata({
|
||||
token: createToken(token),
|
||||
const compileToken = createToken(token);
|
||||
return {
|
||||
token: compileToken,
|
||||
multi: multi,
|
||||
useClass: new CompileTypeMetadata({name, reference: name as any as Type<any>}),
|
||||
deps: deps.map(createDep)
|
||||
});
|
||||
useClass: createTypeMeta({reference: tokenReference(compileToken)}),
|
||||
deps: deps.map(createDep),
|
||||
useExisting: undefined,
|
||||
useFactory: undefined,
|
||||
useValue: undefined
|
||||
};
|
||||
}
|
||||
|
||||
function createDir(
|
||||
@ -865,18 +841,23 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||
return CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: selector,
|
||||
type: new CompileTypeMetadata({
|
||||
moduleUrl: someModuleUrl,
|
||||
name: selector,
|
||||
type: createTypeMeta({
|
||||
reference: <any>selector,
|
||||
diDeps: deps.map(createDep),
|
||||
reference: selector as any as Type<any>
|
||||
}),
|
||||
isComponent: isComponent,
|
||||
template: new CompileTemplateMetadata({ngContentSelectors: []}),
|
||||
providers: providers,
|
||||
viewProviders: viewProviders,
|
||||
queries: queries.map(
|
||||
(value) => new CompileQueryMetadata({selectors: [createToken(value)]}))
|
||||
queries: queries.map((value) => {
|
||||
return {
|
||||
selectors: [createToken(value)],
|
||||
descendants: false,
|
||||
first: false,
|
||||
propertyName: 'test',
|
||||
read: undefined
|
||||
};
|
||||
})
|
||||
})
|
||||
.toSummary();
|
||||
}
|
||||
@ -1122,8 +1103,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
||||
CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: '[a]',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
|
||||
exportAs: 'dirA'
|
||||
})
|
||||
.toSummary();
|
||||
@ -1170,8 +1150,7 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
|
||||
.create({
|
||||
selector: '[a]',
|
||||
isComponent: true,
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
|
||||
exportAs: 'dirA',
|
||||
template: new CompileTemplateMetadata({ngContentSelectors: []})
|
||||
})
|
||||
@ -1189,8 +1168,7 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
|
||||
CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: '[a]',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>})
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}})
|
||||
})
|
||||
.toSummary();
|
||||
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 #...', () => {
|
||||
expect(humanizeTplAst(parse('<template #a>', []))).toEqual([
|
||||
[EmbeddedTemplateAst],
|
||||
[ReferenceAst, 'a', resolveIdentifierToken(Identifiers.TemplateRef)]
|
||||
[ReferenceAst, 'a', createIdentifierToken(Identifiers.TemplateRef)]
|
||||
]);
|
||||
});
|
||||
|
||||
it('should support references via ref-...', () => {
|
||||
expect(humanizeTplAst(parse('<template ref-a>', []))).toEqual([
|
||||
[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
|
||||
.create({
|
||||
selector: '[a]',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>})
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}})
|
||||
})
|
||||
.toSummary();
|
||||
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', () => {
|
||||
it('should locate directives in property bindings', () => {
|
||||
const dirA =
|
||||
CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: '[a=b]',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
|
||||
inputs: ['a']
|
||||
})
|
||||
.toSummary();
|
||||
const dirA = CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: '[a=b]',
|
||||
type: createTypeMeta(
|
||||
{reference: {filePath: someModuleUrl, name: 'DirA'}}),
|
||||
inputs: ['a']
|
||||
})
|
||||
.toSummary();
|
||||
const dirB = CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: '[b]',
|
||||
type: new CompileTypeMetadata({
|
||||
moduleUrl: someModuleUrl,
|
||||
name: 'DirB',
|
||||
reference: {} as Type<any>
|
||||
})
|
||||
type: createTypeMeta(
|
||||
{reference: {filePath: someModuleUrl, name: 'DirB'}})
|
||||
})
|
||||
.toSummary();
|
||||
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
|
||||
.create({
|
||||
selector: '[a]',
|
||||
type: new CompileTypeMetadata({
|
||||
moduleUrl: someModuleUrl,
|
||||
name: 'DirA',
|
||||
reference: {} as Type<any>
|
||||
})
|
||||
type: createTypeMeta(
|
||||
{reference: {filePath: someModuleUrl, name: 'DirA'}})
|
||||
})
|
||||
.toSummary();
|
||||
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
|
||||
.create({
|
||||
selector: '[a]',
|
||||
type: new CompileTypeMetadata({
|
||||
moduleUrl: someModuleUrl,
|
||||
name: 'DirA',
|
||||
reference: {} as Type<any>
|
||||
})
|
||||
type: createTypeMeta(
|
||||
{reference: {filePath: someModuleUrl, name: 'DirA'}})
|
||||
})
|
||||
.toSummary();
|
||||
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({
|
||||
selector: selector,
|
||||
isComponent: true,
|
||||
type: new CompileTypeMetadata({
|
||||
moduleUrl: someModuleUrl,
|
||||
name: `SomeComp${compCounter++}`,
|
||||
reference: {} as Type<any>
|
||||
}),
|
||||
type: createTypeMeta(
|
||||
{reference: {filePath: someModuleUrl, name: `SomeComp${compCounter++}`}}),
|
||||
template: new CompileTemplateMetadata({ngContentSelectors: ngContentSelectors})
|
||||
})
|
||||
.toSummary();
|
||||
@ -1381,11 +1345,8 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
|
||||
return CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: selector,
|
||||
type: new CompileTypeMetadata({
|
||||
moduleUrl: someModuleUrl,
|
||||
name: `SomeDir${compCounter++}`,
|
||||
reference: {} as Type<any>
|
||||
})
|
||||
type: createTypeMeta(
|
||||
{reference: {filePath: someModuleUrl, name: `SomeDir${compCounter++}`}})
|
||||
})
|
||||
.toSummary();
|
||||
}
|
||||
@ -1559,8 +1520,7 @@ Can't bind to 'invalidProp' since it isn't a known property of 'div'. ("<div [ER
|
||||
CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: 'div',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
|
||||
host: {'[invalidProp]': 'someProp'}
|
||||
})
|
||||
.toSummary();
|
||||
@ -1579,8 +1539,7 @@ Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:5 ("<div [
|
||||
CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: 'div',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
|
||||
inputs: ['invalidProp']
|
||||
})
|
||||
.toSummary();
|
||||
@ -1593,8 +1552,7 @@ Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:5 ("<div [
|
||||
.create({
|
||||
selector: 'div',
|
||||
isComponent: true,
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
|
||||
template: new CompileTemplateMetadata({ngContentSelectors: []})
|
||||
})
|
||||
.toSummary();
|
||||
@ -1603,8 +1561,7 @@ Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:5 ("<div [
|
||||
.create({
|
||||
selector: 'div',
|
||||
isComponent: true,
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirB', reference: {} as Type<any>}),
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirB'}}),
|
||||
template: new CompileTemplateMetadata({ngContentSelectors: []})
|
||||
})
|
||||
.toSummary();
|
||||
@ -1623,8 +1580,7 @@ Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:5 ("<div [
|
||||
.create({
|
||||
selector: '[a]',
|
||||
isComponent: true,
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
|
||||
template: new CompileTemplateMetadata({ngContentSelectors: []})
|
||||
})
|
||||
.toSummary();
|
||||
@ -1641,8 +1597,7 @@ Property binding a not used by any directive on an embedded template. Make sure
|
||||
.create({
|
||||
selector: '[a]',
|
||||
isComponent: true,
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
|
||||
template: new CompileTemplateMetadata({ngContentSelectors: []})
|
||||
})
|
||||
.toSummary();
|
||||
@ -1801,8 +1756,7 @@ Property binding a not used by any directive on an embedded template. Make sure
|
||||
CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: '[a]',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>})
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}})
|
||||
})
|
||||
.toSummary();
|
||||
const comp =
|
||||
@ -1810,8 +1764,7 @@ Property binding a not used by any directive on an embedded template. Make sure
|
||||
.create({
|
||||
selector: 'div',
|
||||
isComponent: true,
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'ZComp', reference: {} as Type<any>}),
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'ZComp'}}),
|
||||
template: new CompileTemplateMetadata({ngContentSelectors: []})
|
||||
})
|
||||
.toSummary();
|
||||
@ -1826,18 +1779,16 @@ Property binding a not used by any directive on an embedded template. Make sure
|
||||
CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: 'circle',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'elDir', reference: {} as Type<any>})
|
||||
})
|
||||
.toSummary();
|
||||
const attrSel =
|
||||
CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: '[href]',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'attrDir', reference: {} as Type<any>})
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'elDir'}})
|
||||
})
|
||||
.toSummary();
|
||||
const attrSel = CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: '[href]',
|
||||
type: createTypeMeta(
|
||||
{reference: {filePath: someModuleUrl, name: 'attrDir'}})
|
||||
})
|
||||
.toSummary();
|
||||
|
||||
expect(humanizeTplAstSourceSpans(
|
||||
parse('<svg><circle /><use xlink:href="Port" /></svg>', [tagSel, attrSel])))
|
||||
@ -1856,8 +1807,7 @@ Property binding a not used by any directive on an embedded template. Make sure
|
||||
CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: 'div',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>}),
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}}),
|
||||
inputs: ['aProp']
|
||||
})
|
||||
.toSummary();
|
||||
@ -1873,8 +1823,7 @@ Property binding a not used by any directive on an embedded template. Make sure
|
||||
CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: 'circle',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'elDir', reference: {} as Type<any>})
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'elDir'}})
|
||||
})
|
||||
.toSummary();
|
||||
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
|
||||
.create({
|
||||
selector: 'ul',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'ulDir', reference: {} as Type<any>})
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'ulDir'}})
|
||||
})
|
||||
.toSummary();
|
||||
const liSel =
|
||||
CompileDirectiveMetadata
|
||||
.create({
|
||||
selector: 'li',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'liDir', reference: {} as Type<any>})
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'liDir'}})
|
||||
})
|
||||
.toSummary();
|
||||
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 =
|
||||
new CompilePipeMetadata({
|
||||
name: 'test',
|
||||
type: new CompileTypeMetadata(
|
||||
{moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type<any>})
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'DirA'}})
|
||||
}).toSummary();
|
||||
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)); });
|
||||
}
|
||||
return <any>{
|
||||
classType: o.importType(new CompileIdentifierMetadata({name: config.className})),
|
||||
classType: o.importType({reference: null}),
|
||||
fields: fields,
|
||||
getters: [],
|
||||
declarationElement: declarationElement
|
||||
|
@ -22,5 +22,8 @@
|
||||
"files": [
|
||||
"index.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.
|
||||
*/
|
||||
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.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user