Compare commits
77 Commits
Author | SHA1 | Date | |
---|---|---|---|
6ccb93728e | |||
b54a957ef4 | |||
21c14a79fa | |||
eed2d456d7 | |||
719c232321 | |||
bd033b69c1 | |||
5b92f7ee6d | |||
20ffb4196d | |||
11e6f45387 | |||
a30a73c2aa | |||
fb94c9937c | |||
b3259e26fe | |||
be406f8464 | |||
5cab3b146e | |||
eff6216319 | |||
88b2ab09ea | |||
5ad6d55a45 | |||
c81c7c0920 | |||
6f8e23b061 | |||
dc9711107d | |||
4c0c1e5fa0 | |||
06ef0908c7 | |||
a893d5e0a9 | |||
44afa5a03a | |||
0733f8d3e1 | |||
6604a4dc40 | |||
96c63a92e2 | |||
c444b1575f | |||
2b676192f6 | |||
14a2d1a6f7 | |||
61e089931f | |||
cbf30cb101 | |||
2f41b52e78 | |||
dcf60da16d | |||
9c9f409364 | |||
b44b983c1f | |||
978f80985c | |||
c10e50cf38 | |||
168a2eb5bf | |||
c9c7acd484 | |||
5b99533315 | |||
037805b741 | |||
0861fda69c | |||
feae7b6059 | |||
e4277a0869 | |||
1864ccb3dd | |||
6b79ab5abe | |||
53c12a84dc | |||
ca665303f4 | |||
0fe4985756 | |||
74087cb39d | |||
902bb2f026 | |||
23bf34853c | |||
8c4b963927 | |||
bfa4f70204 | |||
d481f6d150 | |||
fd6114561b | |||
c82851172e | |||
75478b2078 | |||
cdbb3dbd2a | |||
e72124c888 | |||
b8c0a97e35 | |||
5597fd3180 | |||
a88413f871 | |||
aa116524e6 | |||
4a5ad7ba30 | |||
d74e4d0633 | |||
a2c2b87aa3 | |||
8f4ea3e4b8 | |||
bf25e94f19 | |||
7983414e6a | |||
8248eba3e2 | |||
a65487528f | |||
426b3a19b7 | |||
2360676a7b | |||
ce3e03ff1a | |||
858c11cf7b |
@ -176,7 +176,8 @@ groups:
|
||||
- "packages/language-service/*"
|
||||
users:
|
||||
- chuckjaz #primary
|
||||
# needs secondary
|
||||
- tbosch #secondary
|
||||
- vicb
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
|
||||
@ -185,8 +186,8 @@ groups:
|
||||
files:
|
||||
- "packages/router/*"
|
||||
users:
|
||||
- vicb #primary
|
||||
# needs secondary
|
||||
- jasonaden
|
||||
- vicb
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
|
||||
|
107
CHANGELOG.md
107
CHANGELOG.md
@ -1,3 +1,110 @@
|
||||
<a name="4.0.3"></a>
|
||||
## [4.0.3](https://github.com/angular/angular/compare/4.0.2...4.0.3) (2017-04-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **benchpress:** chrome - prevent trace buffer overflow ([d216f94](https://github.com/angular/angular/commit/d216f94))
|
||||
* **compiler:** fix build error in xliff2 ([1870347](https://github.com/angular/angular/commit/1870347))
|
||||
* **compiler:** ignore calls to unresolved symbols in metadata ([d4038ab](https://github.com/angular/angular/commit/d4038ab)), closes [#15969](https://github.com/angular/angular/issues/15969)
|
||||
* **compiler:** ignore calls to unresolved symbols in metadata ([#15970](https://github.com/angular/angular/issues/15970)) ([db25f08](https://github.com/angular/angular/commit/db25f08)), closes [#15969](https://github.com/angular/angular/issues/15969)
|
||||
* **compiler:** Inform user where Quoted error was thrown ([3184cc5](https://github.com/angular/angular/commit/3184cc5))
|
||||
* **compiler:** suppress another closure warning ([#16137](https://github.com/angular/angular/issues/16137)) ([72e240a](https://github.com/angular/angular/commit/72e240a))
|
||||
* **core:** benchmarks - enable ng1 benchmark again ([ccac4c6](https://github.com/angular/angular/commit/ccac4c6))
|
||||
* **core:** distribute externs for testability API ([#16179](https://github.com/angular/angular/issues/16179)) ([e377d9d](https://github.com/angular/angular/commit/e377d9d))
|
||||
* **core:** key-value differ changes iteration ([#15968](https://github.com/angular/angular/issues/15968)) ([a8600dc](https://github.com/angular/angular/commit/a8600dc)), closes [#14997](https://github.com/angular/angular/issues/14997)
|
||||
* **language-service:** only use canonical symbols ([786093a](https://github.com/angular/angular/commit/786093a))
|
||||
* **packaging:** increased buffer size ([#15840](https://github.com/angular/angular/issues/15840)) ([88ad490](https://github.com/angular/angular/commit/88ad490))
|
||||
* **platform-server:** handle innerText ([#15818](https://github.com/angular/angular/issues/15818)) ([7de340d](https://github.com/angular/angular/commit/7de340d))
|
||||
* **router:** prevent `RouterLinkActive` from causing an infinite CD loop ([4479c42](https://github.com/angular/angular/commit/4479c42)), closes [#15825](https://github.com/angular/angular/issues/15825)
|
||||
* **tsc-wrapped:** collect new expressions with no arguments ([#15908](https://github.com/angular/angular/issues/15908)) ([41cac9e](https://github.com/angular/angular/commit/41cac9e)), closes [#15906](https://github.com/angular/angular/issues/15906)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **compiler:** Implement i18n XLIFF 2.0 serializer ([#14185](https://github.com/angular/angular/issues/14185)) ([a7d8edd](https://github.com/angular/angular/commit/a7d8edd)), closes [#11735](https://github.com/angular/angular/issues/11735)
|
||||
* **upgrade:** allow setting the angularjs lib at runtime ([#15168](https://github.com/angular/angular/issues/15168)) ([a75d056](https://github.com/angular/angular/commit/a75d056))
|
||||
* **upgrade:** allow setting the angularjs lib at runtime ([#15168](https://github.com/angular/angular/issues/15168)) ([4f172b0](https://github.com/angular/angular/commit/4f172b0))
|
||||
* **upgrade:** fixes for allow setting the angularjs lib at runtime ([bb6932d](https://github.com/angular/angular/commit/bb6932d))
|
||||
* add support for TS 2.3 ([5cf101f](https://github.com/angular/angular/commit/5cf101f))
|
||||
|
||||
|
||||
|
||||
<a name="4.1.0-beta.1"></a>
|
||||
# [4.1.0-beta.1](https://github.com/angular/angular/compare/4.1.0-beta.0...4.1.0-beta.1) (2017-04-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** fix inheritance for AOT with summaries ([#15583](https://github.com/angular/angular/issues/15583)) ([8ef621a](https://github.com/angular/angular/commit/8ef621a))
|
||||
* **language-service:** avoid throwing exceptions when reporting metadata errors ([7764c5c](https://github.com/angular/angular/commit/7764c5c))
|
||||
* **language-service:** detect when there isn't a tsconfig.json ([258d539](https://github.com/angular/angular/commit/258d539)), closes [#15874](https://github.com/angular/angular/issues/15874)
|
||||
* **language-service:** improve resilience to incomplete information ([71a8627](https://github.com/angular/angular/commit/71a8627))
|
||||
* **language-service:** initialize static reflector correctly ([fe0d02f](https://github.com/angular/angular/commit/fe0d02f)), closes [#15768](https://github.com/angular/angular/issues/15768)
|
||||
* **language-service:** parse extended i18n forms ([bde9771](https://github.com/angular/angular/commit/bde9771))
|
||||
* **language-service:** resolve any parameter types to any result ([5fbb0d0](https://github.com/angular/angular/commit/5fbb0d0))
|
||||
* **router:** fix query param parsing ([a487563](https://github.com/angular/angular/commit/a487563))
|
||||
* **router:** the preloader use the module from the loaded config ([6d12aa9](https://github.com/angular/angular/commit/6d12aa9))
|
||||
* **tsc-wrapped:** ensure valid path separators in metadata ([96aa236](https://github.com/angular/angular/commit/96aa236))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **animations:** Update types for TypeScript nullability support ([38d75d4](https://github.com/angular/angular/commit/38d75d4)), closes [#15870](https://github.com/angular/angular/issues/15870)
|
||||
* **benchpress:** Update types for TypeScript nullability support ([14669f2](https://github.com/angular/angular/commit/14669f2))
|
||||
* **common:** Update types for TypeScript nullability support ([d8b73e4](https://github.com/angular/angular/commit/d8b73e4))
|
||||
* **compiler:** Update types for TypeScript nullability support ([09d9f5f](https://github.com/angular/angular/commit/09d9f5f))
|
||||
* **language-service:** Update types for TypeScript nullability support ([540581d](https://github.com/angular/angular/commit/540581d))
|
||||
|
||||
|
||||
|
||||
<a name="4.0.2"></a>
|
||||
## [4.0.2](https://github.com/angular/angular/compare/4.0.1...4.0.2) (2017-04-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** fix inheritance for AOT with summaries ([#15583](https://github.com/angular/angular/issues/15583)) ([1864ccb](https://github.com/angular/angular/commit/1864ccb))
|
||||
* **language-service:** avoid throwing exceptions when reporting metadata errors ([0861fda](https://github.com/angular/angular/commit/0861fda))
|
||||
* **language-service:** detect when there isn't a tsconfig.json ([168a2eb](https://github.com/angular/angular/commit/168a2eb)), closes [#15874](https://github.com/angular/angular/issues/15874)
|
||||
* **language-service:** improve resilience to incomplete information ([e4277a0](https://github.com/angular/angular/commit/e4277a0))
|
||||
* **language-service:** initialize static reflector correctly ([5b99533](https://github.com/angular/angular/commit/5b99533)), closes [#15768](https://github.com/angular/angular/issues/15768)
|
||||
* **language-service:** parse extended i18n forms ([c9c7acd](https://github.com/angular/angular/commit/c9c7acd))
|
||||
* **language-service:** resolve any parameter types to any result ([feae7b6](https://github.com/angular/angular/commit/feae7b6))
|
||||
* **router:** fix query param parsing ([2f41b52](https://github.com/angular/angular/commit/2f41b52))
|
||||
* **router:** the preloader use the module from the loaded config ([978f809](https://github.com/angular/angular/commit/978f809))
|
||||
* **tsc-wrapped:** ensure valid path separators in metadata ([c10e50c](https://github.com/angular/angular/commit/c10e50c))
|
||||
|
||||
|
||||
|
||||
<a name="4.0.1"></a>
|
||||
## [4.0.1](https://github.com/angular/angular/compare/4.0.0...4.0.1) (2017-03-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** make sure style calculations are not computed too early ([#15540](https://github.com/angular/angular/issues/15540)) ([c828511](https://github.com/angular/angular/commit/c828511)), closes [#15507](https://github.com/angular/angular/issues/15507)
|
||||
* **compiler:** allow single quotes into named interpolations ([#15461](https://github.com/angular/angular/issues/15461)) ([a654875](https://github.com/angular/angular/commit/a654875)), closes [#15318](https://github.com/angular/angular/issues/15318)
|
||||
* **compiler:** ignore errors when evaluating base classes ([#15560](https://github.com/angular/angular/issues/15560)) ([a88413f](https://github.com/angular/angular/commit/a88413f)), closes [#15536](https://github.com/angular/angular/issues/15536)
|
||||
* **compiler:** throw when a component defines both template and templateUrl ([#15572](https://github.com/angular/angular/issues/15572)) ([902bb2f](https://github.com/angular/angular/commit/902bb2f)), closes [#15566](https://github.com/angular/angular/issues/15566)
|
||||
* **core:** check for undefined on normalizeDebugBindingValue ([#15503](https://github.com/angular/angular/issues/15503)) ([b8c0a97](https://github.com/angular/angular/commit/b8c0a97)), closes [#15494](https://github.com/angular/angular/issues/15494)
|
||||
* **core:** fix inheritance in JIT mode for TS 2.1 ([#15599](https://github.com/angular/angular/issues/15599)) ([ca66530](https://github.com/angular/angular/commit/ca66530)), closes [#15502](https://github.com/angular/angular/issues/15502)
|
||||
* **core:** fix the key/value differ ([#15539](https://github.com/angular/angular/issues/15539)) ([e72124c](https://github.com/angular/angular/commit/e72124c)), closes [#15457](https://github.com/angular/angular/issues/15457)
|
||||
* **core:** improve error msg for invalid KeyValueDiffer.diff arg ([#15489](https://github.com/angular/angular/issues/15489)) ([d74e4d0](https://github.com/angular/angular/commit/d74e4d0)), closes [#15402](https://github.com/angular/angular/issues/15402)
|
||||
* **core:** Update types for TypeScript nullability support ([#15472](https://github.com/angular/angular/issues/15472)) ([8c4b963](https://github.com/angular/angular/commit/8c4b963))
|
||||
* **language-service:** be resilient to invalidate ordering ([#15470](https://github.com/angular/angular/issues/15470)) ([a2c2b87](https://github.com/angular/angular/commit/a2c2b87)), closes [#15466](https://github.com/angular/angular/issues/15466)
|
||||
* **language-service:** correctly determine base members of types ([#15600](https://github.com/angular/angular/issues/15600)) ([0fe4985](https://github.com/angular/angular/commit/0fe4985)), closes [#15460](https://github.com/angular/angular/issues/15460)
|
||||
* **language-service:** don't require `reflect-metadata` module to be provided ([#15569](https://github.com/angular/angular/issues/15569)) ([bfa4f70](https://github.com/angular/angular/commit/bfa4f70)), closes [#15568](https://github.com/angular/angular/issues/15568)
|
||||
* **language-service:** guard access to `Symbol.members` ([#15529](https://github.com/angular/angular/issues/15529)) ([bf25e94](https://github.com/angular/angular/commit/bf25e94)), closes [#15528](https://github.com/angular/angular/issues/15528)
|
||||
* **language-service:** improve performance of `updateModuleAnalysis()` ([#15543](https://github.com/angular/angular/issues/15543)) ([5597fd3](https://github.com/angular/angular/commit/5597fd3))
|
||||
* **router:** should run CanActivate after CanDeactivate guards ([75478b2](https://github.com/angular/angular/commit/75478b2)), closes [#14059](https://github.com/angular/angular/issues/14059) [#15467](https://github.com/angular/angular/issues/15467)
|
||||
* **router:** shouldn't execute CanLoad when a route has been loaded ([2360676](https://github.com/angular/angular/commit/2360676)), closes [#14475](https://github.com/angular/angular/issues/14475) [#15438](https://github.com/angular/angular/issues/15438)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **router:** don't create new serializer every time UrlTree.toString is called ([#15565](https://github.com/angular/angular/issues/15565)) ([fd61145](https://github.com/angular/angular/commit/fd61145))
|
||||
|
||||
<a name="4.0.0"></a>
|
||||
# [4.0.0](https://github.com/angular/angular/compare/4.0.0-rc.6...4.0.0) invisible-makeover (2017-03-23)
|
||||
|
||||
|
3
build.sh
3
build.sh
@ -472,8 +472,9 @@ do
|
||||
rsync -a ${OUT_DIR}/ ${NPM_DIR}
|
||||
fi
|
||||
|
||||
echo "====== Copy ${PACKAGE} package.json files"
|
||||
echo "====== Copy ${PACKAGE} package.json and .externs.js files"
|
||||
rsync -am --include="package.json" --include="*/" --exclude=* ${SRC_DIR}/ ${NPM_DIR}/
|
||||
rsync -am --include="*.externs.js" --include="*/" --exclude=* ${SRC_DIR}/ ${NPM_DIR}/
|
||||
|
||||
cp ${ROOT_DIR}/README.md ${NPM_DIR}/
|
||||
fi
|
||||
|
@ -34,7 +34,7 @@ CLOSURE_ARGS=(
|
||||
# Uncomment for easier debugging
|
||||
# "--formatting=PRETTY_PRINT"
|
||||
|
||||
e2e/testability.externs.js
|
||||
node_modules/@angular/core/src/testability/testability.externs.js
|
||||
node_modules/zone.js/dist/zone.js
|
||||
$(find -L vendor/rxjs -name *.js)
|
||||
node_modules/@angular/core/@angular/core.js
|
||||
|
@ -1,47 +0,0 @@
|
||||
/** @externs */
|
||||
// Workaround for #11119
|
||||
// TODO(alexeagle): these externs ought to be distributed with Angular.
|
||||
/**
|
||||
* @externs
|
||||
* @suppress {duplicate}
|
||||
*/
|
||||
// NOTE: generated by tsickle, do not edit.
|
||||
|
||||
/** @record @struct */
|
||||
function BrowserNodeGlobal() {}
|
||||
/** @type {?} */
|
||||
BrowserNodeGlobal.prototype.getAngularTestability;
|
||||
/** @type {?} */
|
||||
BrowserNodeGlobal.prototype.getAllAngularTestabilities;
|
||||
/** @type {?} */
|
||||
BrowserNodeGlobal.prototype.getAllAngularRootElements;
|
||||
/** @type {?} */
|
||||
BrowserNodeGlobal.prototype.frameworkStabilizers;
|
||||
|
||||
/**
|
||||
* @param {?} condition
|
||||
* @return {?}
|
||||
*/
|
||||
BrowserNodeGlobal.prototype.assert = function(condition) {};
|
||||
|
||||
/** @record @struct */
|
||||
function PublicTestability() {}
|
||||
|
||||
/**
|
||||
* @return {?}
|
||||
*/
|
||||
PublicTestability.prototype.isStable = function() {};
|
||||
|
||||
/**
|
||||
* @param {?} callback
|
||||
* @return {?}
|
||||
*/
|
||||
PublicTestability.prototype.whenStable = function(callback) {};
|
||||
|
||||
/**
|
||||
* @param {?} using
|
||||
* @param {?} provider
|
||||
* @param {?} exactMatch
|
||||
* @return {?}
|
||||
*/
|
||||
PublicTestability.prototype.findProviders = function(using, provider, exactMatch) {};
|
81
modules/benchmarks/e2e_test/tree_data.ts
Normal file
81
modules/benchmarks/e2e_test/tree_data.ts
Normal file
@ -0,0 +1,81 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {$} from 'protractor';
|
||||
|
||||
export const CreateBtn = '#createDom';
|
||||
export const DestroyBtn = '#destroyDom';
|
||||
export const DetectChangesBtn = '#detectChanges';
|
||||
export const RootEl = '#root';
|
||||
export const NumberOfChecksEl = '#numberOfChecks';
|
||||
|
||||
export interface Benchmark {
|
||||
id: string;
|
||||
url: string;
|
||||
buttons: string[];
|
||||
ignoreBrowserSynchronization?: boolean;
|
||||
extraParams?: {name: string, value: any}[];
|
||||
}
|
||||
|
||||
const CreateDestroyButtons: string[] = [CreateBtn, DestroyBtn];
|
||||
const CreateDestroyDetectChangesButtons: string[] = [...CreateDestroyButtons, DetectChangesBtn];
|
||||
|
||||
export const Benchmarks: Benchmark[] = [
|
||||
{
|
||||
id: `deepTree.ng2`,
|
||||
url: 'all/benchmarks/src/tree/ng2/index.html',
|
||||
buttons: CreateDestroyDetectChangesButtons,
|
||||
},
|
||||
{
|
||||
id: `deepTree.ng2.next`,
|
||||
url: 'all/benchmarks/src/tree/ng2_next/index.html',
|
||||
buttons: CreateDestroyDetectChangesButtons,
|
||||
ignoreBrowserSynchronization: true,
|
||||
// Can't use bundles as we use non exported code
|
||||
extraParams: [{name: 'bundles', value: false}]
|
||||
},
|
||||
{
|
||||
id: `deepTree.ng2.static`,
|
||||
url: 'all/benchmarks/src/tree/ng2_static/index.html',
|
||||
buttons: CreateDestroyButtons,
|
||||
},
|
||||
{
|
||||
id: `deepTree.ng2_switch`,
|
||||
url: 'all/benchmarks/src/tree/ng2_switch/index.html',
|
||||
buttons: CreateDestroyButtons,
|
||||
},
|
||||
{
|
||||
id: `deepTree.baseline`,
|
||||
url: 'all/benchmarks/src/tree/baseline/index.html',
|
||||
buttons: CreateDestroyButtons,
|
||||
ignoreBrowserSynchronization: true,
|
||||
},
|
||||
{
|
||||
id: `deepTree.incremental_dom`,
|
||||
url: 'all/benchmarks/src/tree/incremental_dom/index.html',
|
||||
buttons: CreateDestroyButtons,
|
||||
ignoreBrowserSynchronization: true,
|
||||
},
|
||||
{
|
||||
id: `deepTree.polymer`,
|
||||
url: 'all/benchmarks/src/tree/polymer/index.html',
|
||||
buttons: CreateDestroyButtons,
|
||||
ignoreBrowserSynchronization: true,
|
||||
},
|
||||
{
|
||||
id: `deepTree.polymer_leaves`,
|
||||
url: 'all/benchmarks/src/tree/polymer_leaves/index.html',
|
||||
buttons: CreateDestroyButtons,
|
||||
ignoreBrowserSynchronization: true,
|
||||
},
|
||||
{
|
||||
id: `deepTree.ng1`,
|
||||
url: 'all/benchmarks/src/tree/ng1/index.html',
|
||||
buttons: CreateDestroyDetectChangesButtons,
|
||||
}
|
||||
];
|
@ -7,162 +7,77 @@
|
||||
*/
|
||||
|
||||
import {runBenchmark, verifyNoBrowserErrors} from 'e2e_util/perf_util';
|
||||
import {$} from 'protractor';
|
||||
import {$, browser} from 'protractor';
|
||||
|
||||
interface Worker {
|
||||
id: string;
|
||||
prepare?(): void;
|
||||
work(): void;
|
||||
}
|
||||
|
||||
const CreateOnlyWorker: Worker = {
|
||||
id: 'createOnly',
|
||||
prepare: () => $('#destroyDom').click(),
|
||||
work: () => $('#createDom').click()
|
||||
};
|
||||
|
||||
const CreateAndDestroyWorker: Worker = {
|
||||
id: 'createDestroy',
|
||||
work: () => {
|
||||
$('#createDom').click();
|
||||
$('#destroyDom').click();
|
||||
}
|
||||
};
|
||||
|
||||
const UpdateWorker: Worker = {
|
||||
id: 'update',
|
||||
work: () => $('#createDom').click()
|
||||
};
|
||||
import {Benchmark, Benchmarks, CreateBtn, DestroyBtn, DetectChangesBtn, RootEl} from './tree_data';
|
||||
|
||||
describe('tree benchmark perf', () => {
|
||||
|
||||
afterEach(verifyNoBrowserErrors);
|
||||
let _oldRootEl: any;
|
||||
beforeEach(() => _oldRootEl = browser.rootEl);
|
||||
|
||||
[CreateOnlyWorker, CreateAndDestroyWorker, UpdateWorker].forEach((worker) => {
|
||||
describe(worker.id, () => {
|
||||
afterEach(() => {
|
||||
browser.rootEl = _oldRootEl;
|
||||
verifyNoBrowserErrors();
|
||||
});
|
||||
|
||||
it('should run for ng2', (done) => {
|
||||
Benchmarks.forEach(benchmark => {
|
||||
describe(benchmark.id, () => {
|
||||
it('should work for createOnly', (done) => {
|
||||
runTreeBenchmark({
|
||||
id: `deepTree.ng2.${worker.id}`,
|
||||
url: 'all/benchmarks/src/tree/ng2/index.html',
|
||||
work: worker.work,
|
||||
prepare: worker.prepare,
|
||||
id: 'createOnly',
|
||||
benchmark,
|
||||
prepare: () => $(CreateBtn).click(),
|
||||
work: () => $(DestroyBtn).click()
|
||||
}).then(done, done.fail);
|
||||
});
|
||||
|
||||
it('should run for ng2 next', (done) => {
|
||||
it('should work for createDestroy', (done) => {
|
||||
runTreeBenchmark({
|
||||
id: `deepTree.ng2.next.${worker.id}`,
|
||||
url: 'all/benchmarks/src/tree/ng2_next/index.html',
|
||||
ignoreBrowserSynchronization: true,
|
||||
work: worker.work,
|
||||
prepare: worker.prepare,
|
||||
// Can't use bundles as we use non exported code
|
||||
extraParams: [{name: 'bundles', value: false}]
|
||||
id: 'createDestroy',
|
||||
benchmark,
|
||||
work: () => {
|
||||
$(DestroyBtn).click();
|
||||
$(CreateBtn).click();
|
||||
}
|
||||
}).then(done, done.fail);
|
||||
});
|
||||
|
||||
it('should run for ng2 static', (done) => {
|
||||
runTreeBenchmark({
|
||||
id: `deepTree.ng2.static.${worker.id}`,
|
||||
url: 'all/benchmarks/src/tree/ng2_static/index.html',
|
||||
work: worker.work,
|
||||
prepare: worker.prepare,
|
||||
}).then(done, done.fail);
|
||||
it('should work for update', (done) => {
|
||||
runTreeBenchmark({id: 'update', benchmark, work: () => $(CreateBtn).click()})
|
||||
.then(done, done.fail);
|
||||
});
|
||||
|
||||
it('should run for ng2 switch', (done) => {
|
||||
runTreeBenchmark({
|
||||
id: `deepTree.ng2_switch.${worker.id}`,
|
||||
url: 'all/benchmarks/src/tree/ng2_switch/index.html',
|
||||
work: worker.work,
|
||||
prepare: worker.prepare,
|
||||
}).then(done, done.fail);
|
||||
});
|
||||
if (benchmark.buttons.indexOf(DetectChangesBtn) !== -1) {
|
||||
it('should work for detectChanges', (done) => {
|
||||
runTreeBenchmark({
|
||||
id: 'detectChanges',
|
||||
benchmark,
|
||||
work: () => $(DetectChangesBtn).click(),
|
||||
setup: () => $(DestroyBtn).click()
|
||||
}).then(done, done.fail);
|
||||
});
|
||||
}
|
||||
|
||||
it('should run for the baseline', (done) => {
|
||||
runTreeBenchmark({
|
||||
id: `deepTree.baseline.${worker.id}`,
|
||||
url: 'all/benchmarks/src/tree/baseline/index.html',
|
||||
ignoreBrowserSynchronization: true,
|
||||
work: worker.work,
|
||||
prepare: worker.prepare,
|
||||
}).then(done, done.fail);
|
||||
});
|
||||
|
||||
it('should run for incremental-dom', (done) => {
|
||||
runTreeBenchmark({
|
||||
id: `deepTree.incremental_dom.${worker.id}`,
|
||||
url: 'all/benchmarks/src/tree/incremental_dom/index.html',
|
||||
ignoreBrowserSynchronization: true,
|
||||
work: worker.work,
|
||||
prepare: worker.prepare,
|
||||
}).then(done, done.fail);
|
||||
});
|
||||
|
||||
it('should run for polymer binary tree', (done) => {
|
||||
runTreeBenchmark({
|
||||
id: `deepTree.polymer.${worker.id}`,
|
||||
url: 'all/benchmarks/src/tree/polymer/index.html',
|
||||
ignoreBrowserSynchronization: true,
|
||||
work: worker.work,
|
||||
prepare: worker.prepare,
|
||||
}).then(done, done.fail);
|
||||
});
|
||||
|
||||
it('should run for polymer leaves', (done) => {
|
||||
runTreeBenchmark({
|
||||
id: `deepTree.polymer_leaves.${worker.id}`,
|
||||
url: 'all/benchmarks/src/tree/polymer_leaves/index.html',
|
||||
ignoreBrowserSynchronization: true,
|
||||
work: worker.work,
|
||||
prepare: worker.prepare,
|
||||
}).then(done, done.fail);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should run ng2 changedetection', (done) => {
|
||||
runTreeBenchmark({
|
||||
id: `deepTree.ng2.changedetection`,
|
||||
url: 'all/benchmarks/src/tree/ng2/index.html',
|
||||
work: () => $('#detectChanges').click(),
|
||||
setup: () => $('#createDom').click(),
|
||||
}).then(done, done.fail);
|
||||
});
|
||||
|
||||
it('should run ng2 next changedetection', (done) => {
|
||||
runTreeBenchmark({
|
||||
id: `deepTree.ng2.next.changedetection`,
|
||||
url: 'all/benchmarks/src/tree/ng2_next/index.html',
|
||||
work: () => $('#detectChanges').click(),
|
||||
setup: () => $('#createDom').click(),
|
||||
ignoreBrowserSynchronization: true,
|
||||
// Can't use bundles as we use non exported code
|
||||
extraParams: [{name: 'bundles', value: false}]
|
||||
}).then(done, done.fail);
|
||||
});
|
||||
|
||||
function runTreeBenchmark(config: {
|
||||
id: string,
|
||||
url: string, ignoreBrowserSynchronization?: boolean,
|
||||
work: () => any,
|
||||
prepare?: () => any,
|
||||
extraParams?: {name: string, value: any}[],
|
||||
setup?: () => any
|
||||
}) {
|
||||
let params = [{name: 'depth', value: 11}];
|
||||
if (config.extraParams) {
|
||||
params = params.concat(config.extraParams);
|
||||
}
|
||||
return runBenchmark({
|
||||
id: config.id,
|
||||
url: config.url,
|
||||
ignoreBrowserSynchronization: config.ignoreBrowserSynchronization,
|
||||
params: params,
|
||||
work: config.work,
|
||||
prepare: config.prepare,
|
||||
setup: config.setup
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function runTreeBenchmark({id, benchmark, prepare, setup, work}: {
|
||||
id: string; benchmark: Benchmark, prepare ? () : void; setup ? () : void; work(): void;
|
||||
}) {
|
||||
let params = [{name: 'depth', value: 11}];
|
||||
if (benchmark.extraParams) {
|
||||
params = params.concat(benchmark.extraParams);
|
||||
}
|
||||
browser.rootEl = RootEl;
|
||||
return runBenchmark({
|
||||
id: `${benchmark.id}.${id}`,
|
||||
url: benchmark.url,
|
||||
ignoreBrowserSynchronization: benchmark.ignoreBrowserSynchronization,
|
||||
params: params,
|
||||
work: work,
|
||||
prepare: prepare,
|
||||
setup: setup
|
||||
});
|
||||
}
|
||||
|
@ -7,107 +7,57 @@
|
||||
*/
|
||||
|
||||
import {openBrowser, verifyNoBrowserErrors} from 'e2e_util/e2e_util';
|
||||
import {$} from 'protractor';
|
||||
import {$, browser} from 'protractor';
|
||||
|
||||
import {Benchmark, Benchmarks, CreateBtn, DestroyBtn, DetectChangesBtn, NumberOfChecksEl, RootEl} from './tree_data';
|
||||
|
||||
describe('tree benchmark spec', () => {
|
||||
|
||||
afterEach(verifyNoBrowserErrors);
|
||||
let _oldRootEl: any;
|
||||
beforeEach(() => _oldRootEl = browser.rootEl);
|
||||
|
||||
it('should work for ng2', () => {
|
||||
testTreeBenchmark({
|
||||
url: 'all/benchmarks/src/tree/ng2/index.html',
|
||||
afterEach(() => {
|
||||
browser.rootEl = _oldRootEl;
|
||||
verifyNoBrowserErrors();
|
||||
});
|
||||
|
||||
Benchmarks.forEach(benchmark => {
|
||||
describe(benchmark.id, () => {
|
||||
it('should work for createDestroy', () => {
|
||||
openTreeBenchmark(benchmark);
|
||||
$(CreateBtn).click();
|
||||
expect($(RootEl).getText()).toContain('0');
|
||||
$(DestroyBtn).click();
|
||||
expect($(RootEl).getText()).toEqual('');
|
||||
});
|
||||
|
||||
it('should work for update', () => {
|
||||
openTreeBenchmark(benchmark);
|
||||
$(CreateBtn).click();
|
||||
$(CreateBtn).click();
|
||||
expect($(RootEl).getText()).toContain('A');
|
||||
});
|
||||
|
||||
if (benchmark.buttons.indexOf(DetectChangesBtn) !== -1) {
|
||||
it('should work for detectChanges', () => {
|
||||
openTreeBenchmark(benchmark);
|
||||
$(DetectChangesBtn).click();
|
||||
expect($(NumberOfChecksEl).getText()).toContain('10');
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should work for ng2 detect changes', () => {
|
||||
function openTreeBenchmark(benchmark: Benchmark) {
|
||||
let params = [{name: 'depth', value: 4}];
|
||||
openBrowser({url: 'all/benchmarks/src/tree/ng2/index.html', params});
|
||||
$('#detectChanges').click();
|
||||
expect($('#numberOfChecks').getText()).toContain('10');
|
||||
});
|
||||
|
||||
it('should work for ng2 next', () => {
|
||||
testTreeBenchmark({
|
||||
url: 'all/benchmarks/src/tree/ng2_next/index.html',
|
||||
ignoreBrowserSynchronization: true,
|
||||
// Can't use bundles as we use non exported code
|
||||
extraParams: [{name: 'bundles', value: false}]
|
||||
});
|
||||
});
|
||||
|
||||
it('should work for ng2 next detect changes', () => {
|
||||
let params = [
|
||||
{name: 'depth', value: 4},
|
||||
// Can't use bundles as we use non exported code
|
||||
{name: 'bundles', value: false}
|
||||
];
|
||||
openBrowser({
|
||||
url: 'all/benchmarks/src/tree/ng2_next/index.html',
|
||||
ignoreBrowserSynchronization: true, params
|
||||
});
|
||||
$('#detectChanges').click();
|
||||
expect($('#numberOfChecks').getText()).toContain('10');
|
||||
});
|
||||
|
||||
it('should work for ng2 static', () => {
|
||||
testTreeBenchmark({
|
||||
url: 'all/benchmarks/src/tree/ng2_static/index.html',
|
||||
});
|
||||
});
|
||||
|
||||
it('should work for ng2 switch', () => {
|
||||
testTreeBenchmark({
|
||||
url: 'all/benchmarks/src/tree/ng2_switch/index.html',
|
||||
});
|
||||
});
|
||||
|
||||
it('should work for the baseline', () => {
|
||||
testTreeBenchmark({
|
||||
url: 'all/benchmarks/src/tree/baseline/index.html',
|
||||
ignoreBrowserSynchronization: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should work for incremental dom', () => {
|
||||
testTreeBenchmark({
|
||||
url: 'all/benchmarks/src/tree/incremental_dom/index.html',
|
||||
ignoreBrowserSynchronization: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should work for polymer binary tree', () => {
|
||||
testTreeBenchmark({
|
||||
url: 'all/benchmarks/src/tree/polymer/index.html',
|
||||
ignoreBrowserSynchronization: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should work for polymer leaves', () => {
|
||||
testTreeBenchmark({
|
||||
url: 'all/benchmarks/src/tree/polymer_leaves/index.html',
|
||||
ignoreBrowserSynchronization: true,
|
||||
});
|
||||
});
|
||||
|
||||
function testTreeBenchmark(openConfig: {
|
||||
url: string,
|
||||
ignoreBrowserSynchronization?: boolean,
|
||||
extraParams?: {name: string, value: any}[]
|
||||
}) {
|
||||
let params = [{name: 'depth', value: 4}];
|
||||
if (openConfig.extraParams) {
|
||||
params = params.concat(openConfig.extraParams);
|
||||
if (benchmark.extraParams) {
|
||||
params = params.concat(benchmark.extraParams);
|
||||
}
|
||||
browser.rootEl = RootEl;
|
||||
openBrowser({
|
||||
url: openConfig.url,
|
||||
ignoreBrowserSynchronization: openConfig.ignoreBrowserSynchronization,
|
||||
url: benchmark.url,
|
||||
ignoreBrowserSynchronization: benchmark.ignoreBrowserSynchronization,
|
||||
params: params,
|
||||
});
|
||||
$('#createDom').click();
|
||||
expect($('#root').getText()).toContain('0');
|
||||
$('#createDom').click();
|
||||
expect($('#root').getText()).toContain('A');
|
||||
$('#destroyDom').click();
|
||||
expect($('#root').getText()).toEqual('');
|
||||
}
|
||||
});
|
||||
|
44
modules/benchmarks/src/tree/ng1/index.html
Normal file
44
modules/benchmarks/src/tree/ng1/index.html
Normal file
@ -0,0 +1,44 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<h2>Params</h2>
|
||||
<form>
|
||||
Depth:
|
||||
<input type="number" name="depth" placeholder="depth" value="9">
|
||||
<br>
|
||||
<button>Apply</button>
|
||||
</form>
|
||||
|
||||
<h2>Ng1 Tree Benchmark</h2>
|
||||
<p>
|
||||
<button id="destroyDom">destroyDom</button>
|
||||
<button id="createDom">createDom</button>
|
||||
<button id="detectChanges">detectChanges</button>
|
||||
<button id="updateDomProfile">profile updateDom</button>
|
||||
<button id="createDomProfile">profile createDom</button>
|
||||
<button id="detectChangesProfile">profile detectChanges</button>
|
||||
</p>
|
||||
|
||||
<div>
|
||||
Change detection runs:<span id="numberOfChecks"></span>
|
||||
</div>
|
||||
<div>
|
||||
<tree id="root" data="initData">Loading...</tree>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var mainUrls = [
|
||||
'/all/benchmarks/vendor/angular.js',
|
||||
'../../bootstrap_plain.js'
|
||||
];
|
||||
var mainUrl = window.location.search.split(/[?&]main=([^&]+)/)[1];
|
||||
if (mainUrl) {
|
||||
mainUrls = [mainUrl];
|
||||
}
|
||||
mainUrls.forEach(function(mainUrl) {
|
||||
document.write('<script src="' + mainUrl + '">\u003c/script>');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
53
modules/benchmarks/src/tree/ng1/index.ts
Normal file
53
modules/benchmarks/src/tree/ng1/index.ts
Normal file
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* @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 {bindAction, profile} from '../../util';
|
||||
import {buildTree, emptyTree} from '../util';
|
||||
|
||||
import {addTreeToModule} from './tree';
|
||||
|
||||
declare var angular: any;
|
||||
|
||||
function init() {
|
||||
let detectChangesRuns = 0;
|
||||
const numberOfChecksEl = document.getElementById('numberOfChecks') !;
|
||||
|
||||
addTreeToModule(angular.module('app', [])).run([
|
||||
'$rootScope',
|
||||
($rootScope: any) => {
|
||||
function detectChanges() {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
$rootScope.$digest();
|
||||
}
|
||||
detectChangesRuns += 10;
|
||||
numberOfChecksEl.textContent = `${detectChangesRuns}`;
|
||||
}
|
||||
|
||||
function noop() {}
|
||||
|
||||
function destroyDom() {
|
||||
$rootScope.$apply(() => { $rootScope.initData = emptyTree; });
|
||||
}
|
||||
|
||||
function createDom() {
|
||||
$rootScope.$apply(() => { $rootScope.initData = buildTree(); });
|
||||
}
|
||||
|
||||
bindAction('#destroyDom', destroyDom);
|
||||
bindAction('#createDom', createDom);
|
||||
bindAction('#detectChanges', detectChanges);
|
||||
bindAction('#detectChangesProfile', profile(detectChanges, noop, 'detectChanges'));
|
||||
bindAction('#updateDomProfile', profile(createDom, noop, 'update'));
|
||||
bindAction('#createDomProfile', profile(createDom, destroyDom, 'create'));
|
||||
}
|
||||
]);
|
||||
|
||||
angular.bootstrap(document.querySelector('tree'), ['app']);
|
||||
}
|
||||
|
||||
init();
|
72
modules/benchmarks/src/tree/ng1/tree.ts
Normal file
72
modules/benchmarks/src/tree/ng1/tree.ts
Normal file
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* @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 {TreeNode} from '../util';
|
||||
|
||||
declare var angular: any;
|
||||
|
||||
export function addTreeToModule(mod: any): any {
|
||||
return mod
|
||||
.directive(
|
||||
'tree',
|
||||
function() {
|
||||
return {
|
||||
scope: {data: '='},
|
||||
template:
|
||||
`<span ng-style="{'background-color': data.depth % 2 ? '' : 'grey'}"> {{data.value}} </span><tree-if data='data.right'></tree-if><tree-if data='data.left'></tree-if>`
|
||||
};
|
||||
})
|
||||
// special directive for "if" as angular 1.3 does not support
|
||||
// recursive components.
|
||||
// Cloned from real ngIf directive, but using a lazily created transclude function.
|
||||
.directive(
|
||||
'treeIf',
|
||||
[
|
||||
'$compile', '$animate',
|
||||
function($compile: any, $animate: any) {
|
||||
let transcludeFn: any;
|
||||
return {
|
||||
transclude: 'element',
|
||||
priority: 600,
|
||||
terminal: true,
|
||||
$$tlb: true,
|
||||
link: function($scope: any, $element: any, $attr: any, ctrl: any) {
|
||||
if (!transcludeFn) {
|
||||
const template = '<tree data="' + $attr.data + '"></tree>';
|
||||
transcludeFn = $compile(template);
|
||||
}
|
||||
let childElement: any, childScope: any;
|
||||
$scope.$watch($attr.data, function ngIfWatchAction(value: any) {
|
||||
|
||||
if (value) {
|
||||
if (!childScope) {
|
||||
childScope = $scope.$new();
|
||||
transcludeFn(childScope, function(clone: any) {
|
||||
childElement = clone;
|
||||
$animate.enter(clone, $element.parent(), $element);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (childScope) {
|
||||
childScope.$destroy();
|
||||
childScope = null;
|
||||
}
|
||||
if (childElement) {
|
||||
$animate.leave(childElement);
|
||||
childElement = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
])
|
||||
.config([
|
||||
'$compileProvider',
|
||||
function($compileProvider: any) { $compileProvider.debugInfoEnabled(false); }
|
||||
]);
|
||||
}
|
@ -40,7 +40,7 @@ export function init(moduleRef: NgModuleRef<AppModule>) {
|
||||
|
||||
const injector = moduleRef.injector;
|
||||
appRef = injector.get(ApplicationRef);
|
||||
const numberOfChecksEl = document.getElementById('numberOfChecks');
|
||||
const numberOfChecksEl = document.getElementById('numberOfChecks') !;
|
||||
|
||||
tree = appRef.components[0].instance;
|
||||
|
||||
|
@ -13,10 +13,10 @@ export class TreeNode {
|
||||
children: TreeNode[];
|
||||
|
||||
constructor(
|
||||
public value: string, public depth: number, public maxDepth: number, public left: TreeNode,
|
||||
public right: TreeNode) {
|
||||
public value: string, public depth: number, public maxDepth: number,
|
||||
public left: TreeNode|null, public right: TreeNode|null) {
|
||||
this.transitiveChildCount = Math.pow(2, (this.maxDepth - this.depth + 1)) - 1;
|
||||
this.children = this.left ? [this.left, this.right] : [];
|
||||
this.children = this.left ? [this.left, this.right !] : [];
|
||||
}
|
||||
|
||||
// Needed for Polymer as it does not support ternary nor modulo operator
|
||||
|
@ -35,7 +35,7 @@ export function getStringParameter(name: string) {
|
||||
}
|
||||
|
||||
export function bindAction(selector: string, callback: () => void) {
|
||||
document.querySelector(selector).addEventListener('click', callback);
|
||||
document.querySelector(selector) !.addEventListener('click', callback);
|
||||
}
|
||||
|
||||
|
||||
@ -60,7 +60,7 @@ export function profile(create: () => void, destroy: () => void, name: string) {
|
||||
function urlParamsToForm() {
|
||||
const regex = /(\w+)=(\w+)/g;
|
||||
const search = decodeURIComponent(location.search);
|
||||
let match: any[];
|
||||
let match: any[]|null;
|
||||
while (match = regex.exec(search)) {
|
||||
const name = match[1];
|
||||
const value = match[2];
|
||||
@ -75,4 +75,4 @@ function urlParamsToForm() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "4.0.0-rc.5",
|
||||
"version": "4.1.0-beta.1",
|
||||
"dependencies": {
|
||||
"@types/angularjs": {
|
||||
"version": "1.5.13-alpha",
|
||||
@ -86,6 +86,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ajv": {
|
||||
"version": "4.11.7",
|
||||
"dev": true
|
||||
},
|
||||
"align-text": {
|
||||
"version": "0.1.3",
|
||||
"dev": true,
|
||||
@ -2025,6 +2029,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"co": {
|
||||
"version": "4.6.0",
|
||||
"dev": true
|
||||
},
|
||||
"code-point-at": {
|
||||
"version": "1.0.0",
|
||||
"dev": true
|
||||
@ -3793,6 +3801,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"har-schema": {
|
||||
"version": "1.0.5",
|
||||
"dev": true
|
||||
},
|
||||
"har-validator": {
|
||||
"version": "2.0.6",
|
||||
"dev": true,
|
||||
@ -4219,6 +4231,10 @@
|
||||
"version": "0.2.3",
|
||||
"dev": true
|
||||
},
|
||||
"json-stable-stringify": {
|
||||
"version": "1.0.1",
|
||||
"dev": true
|
||||
},
|
||||
"json-stringify-safe": {
|
||||
"version": "5.0.1",
|
||||
"dev": true
|
||||
@ -4235,6 +4251,10 @@
|
||||
"version": "2.2.3",
|
||||
"dev": true
|
||||
},
|
||||
"jsonify": {
|
||||
"version": "0.0.0",
|
||||
"dev": true
|
||||
},
|
||||
"jsonparse": {
|
||||
"version": "1.2.0",
|
||||
"dev": true
|
||||
@ -5170,6 +5190,10 @@
|
||||
"version": "0.9.0",
|
||||
"dev": true
|
||||
},
|
||||
"performance-now": {
|
||||
"version": "0.2.0",
|
||||
"dev": true
|
||||
},
|
||||
"pify": {
|
||||
"version": "2.3.0",
|
||||
"dev": true
|
||||
@ -5225,39 +5249,51 @@
|
||||
}
|
||||
},
|
||||
"protractor": {
|
||||
"version": "4.0.11",
|
||||
"version": "4.0.14",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/jasmine": {
|
||||
"version": "2.5.37",
|
||||
"version": "2.5.47",
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "6.0.46",
|
||||
"version": "6.0.68",
|
||||
"dev": true
|
||||
},
|
||||
"@types/selenium-webdriver": {
|
||||
"version": "2.53.37",
|
||||
"dev": true
|
||||
},
|
||||
"caseless": {
|
||||
"version": "0.12.0",
|
||||
"dev": true
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.1",
|
||||
"dev": true
|
||||
},
|
||||
"har-validator": {
|
||||
"version": "4.2.1",
|
||||
"dev": true
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.24.0",
|
||||
"version": "1.27.0",
|
||||
"dev": true
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.12",
|
||||
"version": "2.1.15",
|
||||
"dev": true
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.3.0",
|
||||
"version": "6.4.0",
|
||||
"dev": true
|
||||
},
|
||||
"request": {
|
||||
"version": "2.78.0",
|
||||
"version": "2.81.0",
|
||||
"dev": true
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.5.4",
|
||||
"version": "2.6.1",
|
||||
"dev": true
|
||||
},
|
||||
"saucelabs": {
|
||||
@ -5272,8 +5308,16 @@
|
||||
"version": "2.3.2",
|
||||
"dev": true
|
||||
},
|
||||
"tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"dev": true
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.0.1",
|
||||
"dev": true
|
||||
},
|
||||
"webdriver-manager": {
|
||||
"version": "10.2.8",
|
||||
"version": "10.3.0",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@ -5604,6 +5648,10 @@
|
||||
"rxjs": {
|
||||
"version": "5.0.1"
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.0.1",
|
||||
"dev": true
|
||||
},
|
||||
"sander": {
|
||||
"version": "0.5.1",
|
||||
"dev": true,
|
||||
|
118
npm-shrinkwrap.json
generated
118
npm-shrinkwrap.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "4.0.0-rc.5",
|
||||
"version": "4.1.0-beta.1",
|
||||
"dependencies": {
|
||||
"@types/angularjs": {
|
||||
"version": "1.5.13-alpha",
|
||||
@ -126,6 +126,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ajv": {
|
||||
"version": "4.11.7",
|
||||
"from": "ajv@>=4.9.1 <5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.7.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"align-text": {
|
||||
"version": "0.1.3",
|
||||
"from": "align-text@>=0.1.0 <0.2.0",
|
||||
@ -2911,6 +2917,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"co": {
|
||||
"version": "4.6.0",
|
||||
"from": "co@>=4.6.0 <5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"code-point-at": {
|
||||
"version": "1.0.0",
|
||||
"from": "code-point-at@>=1.0.0 <2.0.0",
|
||||
@ -5529,6 +5541,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"har-schema": {
|
||||
"version": "1.0.5",
|
||||
"from": "har-schema@>=1.0.5 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"har-validator": {
|
||||
"version": "2.0.6",
|
||||
"from": "har-validator@>=2.0.6 <2.1.0",
|
||||
@ -6159,6 +6177,12 @@
|
||||
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"json-stable-stringify": {
|
||||
"version": "1.0.1",
|
||||
"from": "json-stable-stringify@>=1.0.1 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"json-stringify-safe": {
|
||||
"version": "5.0.1",
|
||||
"from": "json-stringify-safe@>=5.0.1 <5.1.0",
|
||||
@ -6183,6 +6207,12 @@
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.2.3.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"jsonify": {
|
||||
"version": "0.0.0",
|
||||
"from": "jsonify@>=0.0.0 <0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"jsonparse": {
|
||||
"version": "1.2.0",
|
||||
"from": "jsonparse@>=1.1.0 <2.0.0",
|
||||
@ -7554,6 +7584,12 @@
|
||||
"resolved": "https://registry.npmjs.org/pegjs/-/pegjs-0.9.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"performance-now": {
|
||||
"version": "0.2.0",
|
||||
"from": "performance-now@>=0.2.0 <0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"pify": {
|
||||
"version": "2.3.0",
|
||||
"from": "pify@>=2.0.0 <3.0.0",
|
||||
@ -7635,21 +7671,33 @@
|
||||
}
|
||||
},
|
||||
"protractor": {
|
||||
"version": "4.0.11",
|
||||
"from": "protractor@4.0.11",
|
||||
"resolved": "https://registry.npmjs.org/protractor/-/protractor-4.0.11.tgz",
|
||||
"version": "4.0.14",
|
||||
"from": "protractor@4.0.14",
|
||||
"resolved": "https://registry.npmjs.org/protractor/-/protractor-4.0.14.tgz",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/jasmine": {
|
||||
"version": "2.5.37",
|
||||
"version": "2.5.47",
|
||||
"from": "@types/jasmine@>=2.5.36 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.5.37.tgz",
|
||||
"resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.5.47.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "6.0.46",
|
||||
"version": "6.0.68",
|
||||
"from": "@types/node@>=6.0.46 <7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.46.tgz",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.68.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"@types/selenium-webdriver": {
|
||||
"version": "2.53.37",
|
||||
"from": "@types/selenium-webdriver@2.53.37",
|
||||
"resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-2.53.37.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"caseless": {
|
||||
"version": "0.12.0",
|
||||
"from": "caseless@>=0.12.0 <0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"glob": {
|
||||
@ -7658,34 +7706,40 @@
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"har-validator": {
|
||||
"version": "4.2.1",
|
||||
"from": "har-validator@>=4.2.1 <4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.24.0",
|
||||
"from": "mime-db@~1.24.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.24.0.tgz",
|
||||
"version": "1.27.0",
|
||||
"from": "mime-db@>=1.27.0 <1.28.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.12",
|
||||
"version": "2.1.15",
|
||||
"from": "mime-types@>=2.1.7 <2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.12.tgz",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.3.0",
|
||||
"from": "qs@>=6.3.0 <6.4.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.3.0.tgz",
|
||||
"version": "6.4.0",
|
||||
"from": "qs@>=6.4.0 <6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"request": {
|
||||
"version": "2.78.0",
|
||||
"version": "2.81.0",
|
||||
"from": "request@>=2.78.0 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/request/-/request-2.78.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.5.4",
|
||||
"version": "2.6.1",
|
||||
"from": "rimraf@>=2.5.2 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"saucelabs": {
|
||||
@ -7706,10 +7760,22 @@
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"from": "tunnel-agent@>=0.6.0 <0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.0.1",
|
||||
"from": "uuid@>=3.0.0 <4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"webdriver-manager": {
|
||||
"version": "10.2.8",
|
||||
"from": "webdriver-manager@>=10.2.8 <11.0.0",
|
||||
"resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-10.2.8.tgz",
|
||||
"version": "10.3.0",
|
||||
"from": "webdriver-manager@>=10.3.0 <11.0.0",
|
||||
"resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-10.3.0.tgz",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@ -8196,6 +8262,12 @@
|
||||
"from": "rxjs@5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.0.1.tgz"
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.0.1",
|
||||
"from": "safe-buffer@>=5.0.1 <6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"sander": {
|
||||
"version": "0.5.1",
|
||||
"from": "sander@>=0.5.0 <0.6.0",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "4.0.0",
|
||||
"version": "4.0.3",
|
||||
"private": true,
|
||||
"branchPattern": "2.0.*",
|
||||
"description": "Angular - a web framework for modern web apps",
|
||||
@ -77,7 +77,7 @@
|
||||
"nan": "^2.4.0",
|
||||
"node-uuid": "1.4.x",
|
||||
"parse5": "^3.0.1",
|
||||
"protractor": "^4.0.11",
|
||||
"protractor": "^4.0.14",
|
||||
"react": "^0.14.0",
|
||||
"rewire": "^2.3.3",
|
||||
"rho": "^0.3.0",
|
||||
|
@ -13,3 +13,4 @@ export {NoopAnimationDriver as ɵNoopAnimationDriver} from './render/animation_d
|
||||
export {DomAnimationEngine as ɵDomAnimationEngine} from './render/dom_animation_engine';
|
||||
export {NoopAnimationEngine as ɵNoopAnimationEngine} from './render/noop_animation_engine';
|
||||
export {WebAnimationsDriver as ɵWebAnimationsDriver, supportsWebAnimations as ɵsupportsWebAnimations} from './render/web_animations/web_animations_driver';
|
||||
export {WebAnimationsPlayer as ɵWebAnimationsPlayer} from './render/web_animations/web_animations_player';
|
||||
|
@ -259,8 +259,6 @@ export class DomAnimationEngine {
|
||||
const player = this._buildPlayer(element, instruction, previousPlayers, i);
|
||||
player.onDestroy(
|
||||
() => { deleteFromArrayMap(this._activeElementAnimations, element, player); });
|
||||
player.init();
|
||||
|
||||
this._markPlayerAsActive(element, player);
|
||||
return player;
|
||||
});
|
||||
@ -354,6 +352,7 @@ export class DomAnimationEngine {
|
||||
// in the event that an animation throws an error then we do
|
||||
// not want to re-run animations on any previous animations
|
||||
// if they have already been kicked off beforehand
|
||||
player.init();
|
||||
if (!player.hasStarted()) {
|
||||
player.play();
|
||||
}
|
||||
|
@ -87,6 +87,22 @@ export function main() {
|
||||
expect(engine.queuedPlayers.pop() instanceof NoopAnimationPlayer).toBe(true);
|
||||
});
|
||||
|
||||
it('should not initialize the animation until the engine has been flushed', () => {
|
||||
const engine = makeEngine();
|
||||
engine.registerTrigger(trigger(
|
||||
'trig', [transition('* => something', [animate(1000, style({color: 'gold'}))])]));
|
||||
|
||||
engine.setProperty(element, 'trig', 'something');
|
||||
const player = engine.queuedPlayers.pop() as MockAnimationPlayer;
|
||||
|
||||
let initialized = false;
|
||||
player.onInit(() => initialized = true);
|
||||
|
||||
expect(initialized).toBe(false);
|
||||
engine.flush();
|
||||
expect(initialized).toBe(true);
|
||||
});
|
||||
|
||||
it('should not queue an animation if the property value has not changed at all', () => {
|
||||
const engine = makeEngine();
|
||||
|
||||
|
@ -31,6 +31,7 @@ export class MockAnimationDriver implements AnimationDriver {
|
||||
export class MockAnimationPlayer extends NoopAnimationPlayer {
|
||||
private __finished = false;
|
||||
public previousStyles: {[key: string]: string | number} = {};
|
||||
private _onInitFns: (() => any)[] = [];
|
||||
|
||||
constructor(
|
||||
public element: any, public keyframes: {[key: string]: string | number}[],
|
||||
@ -45,6 +46,16 @@ export class MockAnimationPlayer extends NoopAnimationPlayer {
|
||||
});
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
onInit(fn: () => any) { this._onInitFns.push(fn); }
|
||||
|
||||
/* @internal */
|
||||
init() {
|
||||
super.init();
|
||||
this._onInitFns.forEach(fn => fn());
|
||||
this._onInitFns = [];
|
||||
}
|
||||
|
||||
finish(): void {
|
||||
super.finish();
|
||||
this.__finished = true;
|
||||
|
@ -98,7 +98,7 @@ export interface AnimationStyleMetadata extends AnimationMetadata {
|
||||
*/
|
||||
export interface AnimationAnimateMetadata extends AnimationMetadata {
|
||||
timings: string|number|AnimateTimings;
|
||||
styles: AnimationStyleMetadata|AnimationKeyframesSequenceMetadata;
|
||||
styles: AnimationStyleMetadata|AnimationKeyframesSequenceMetadata|null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -118,10 +118,10 @@ export interface AnimationSequenceMetadata extends AnimationMetadata { steps: An
|
||||
export interface AnimationGroupMetadata extends AnimationMetadata { steps: AnimationMetadata[]; }
|
||||
|
||||
/**
|
||||
* `trigger` is an animation-specific function that is designed to be used inside of Angular2's
|
||||
* `trigger` is an animation-specific function that is designed to be used inside of Angular's
|
||||
animation DSL language. If this information is new, please navigate to the {@link
|
||||
Component#animations-anchor component animations metadata page} to gain a better understanding of
|
||||
how animations in Angular2 are used.
|
||||
how animations in Angular are used.
|
||||
*
|
||||
* `trigger` Creates an animation trigger which will a list of {@link state state} and {@link
|
||||
transition transition} entries that will be evaluated when the expression bound to the trigger
|
||||
@ -173,10 +173,10 @@ export function trigger(name: string, definitions: AnimationMetadata[]): Animati
|
||||
}
|
||||
|
||||
/**
|
||||
* `animate` is an animation-specific function that is designed to be used inside of Angular2's
|
||||
* `animate` is an animation-specific function that is designed to be used inside of Angular's
|
||||
* animation DSL language. If this information is new, please navigate to the {@link
|
||||
* Component#animations-anchor component animations metadata page} to gain a better understanding of
|
||||
* how animations in Angular2 are used.
|
||||
* how animations in Angular are used.
|
||||
*
|
||||
* `animate` specifies an animation step that will apply the provided `styles` data for a given
|
||||
* amount of time based on the provided `timing` expression value. Calls to `animate` are expected
|
||||
@ -218,16 +218,16 @@ export function trigger(name: string, definitions: AnimationMetadata[]): Animati
|
||||
* @experimental Animation support is experimental.
|
||||
*/
|
||||
export function animate(
|
||||
timings: string | number, styles: AnimationStyleMetadata | AnimationKeyframesSequenceMetadata =
|
||||
null): AnimationAnimateMetadata {
|
||||
timings: string | number, styles: AnimationStyleMetadata | AnimationKeyframesSequenceMetadata |
|
||||
null = null): AnimationAnimateMetadata {
|
||||
return {type: AnimationMetadataType.Animate, styles: styles, timings: timings};
|
||||
}
|
||||
|
||||
/**
|
||||
* `group` is an animation-specific function that is designed to be used inside of Angular2's
|
||||
* `group` is an animation-specific function that is designed to be used inside of Angular's
|
||||
* animation DSL language. If this information is new, please navigate to the {@link
|
||||
* Component#animations-anchor component animations metadata page} to gain a better understanding of
|
||||
* how animations in Angular2 are used.
|
||||
* how animations in Angular are used.
|
||||
*
|
||||
* `group` specifies a list of animation steps that are all run in parallel. Grouped animations are
|
||||
* useful when a series of styles must be animated/closed off at different statrting/ending times.
|
||||
@ -259,10 +259,10 @@ export function group(steps: AnimationMetadata[]): AnimationGroupMetadata {
|
||||
}
|
||||
|
||||
/**
|
||||
* `sequence` is an animation-specific function that is designed to be used inside of Angular2's
|
||||
* `sequence` is an animation-specific function that is designed to be used inside of Angular's
|
||||
* animation DSL language. If this information is new, please navigate to the {@link
|
||||
* Component#animations-anchor component animations metadata page} to gain a better understanding of
|
||||
* how animations in Angular2 are used.
|
||||
* how animations in Angular are used.
|
||||
*
|
||||
* `sequence` Specifies a list of animation steps that are run one by one. (`sequence` is used by
|
||||
* default when an array is passed as animation data into {@link transition transition}.)
|
||||
@ -297,10 +297,10 @@ export function sequence(steps: AnimationMetadata[]): AnimationSequenceMetadata
|
||||
}
|
||||
|
||||
/**
|
||||
* `style` is an animation-specific function that is designed to be used inside of Angular2's
|
||||
* `style` is an animation-specific function that is designed to be used inside of Angular's
|
||||
* animation DSL language. If this information is new, please navigate to the {@link
|
||||
* Component#animations-anchor component animations metadata page} to gain a better understanding of
|
||||
* how animations in Angular2 are used.
|
||||
* how animations in Angular are used.
|
||||
*
|
||||
* `style` declares a key/value object containing CSS properties/styles that can then be used for
|
||||
* {@link state animation states}, within an {@link sequence animation sequence}, or as styling data
|
||||
@ -345,10 +345,10 @@ export function style(
|
||||
}
|
||||
|
||||
/**
|
||||
* `state` is an animation-specific function that is designed to be used inside of Angular2's
|
||||
* `state` is an animation-specific function that is designed to be used inside of Angular's
|
||||
* animation DSL language. If this information is new, please navigate to the {@link
|
||||
* Component#animations-anchor component animations metadata page} to gain a better understanding of
|
||||
* how animations in Angular2 are used.
|
||||
* how animations in Angular are used.
|
||||
*
|
||||
* `state` declares an animation state within the given trigger. When a state is active within a
|
||||
* component then its associated styles will persist on the element that the trigger is attached to
|
||||
@ -397,10 +397,10 @@ export function state(name: string, styles: AnimationStyleMetadata): AnimationSt
|
||||
}
|
||||
|
||||
/**
|
||||
* `keyframes` is an animation-specific function that is designed to be used inside of Angular2's
|
||||
* `keyframes` is an animation-specific function that is designed to be used inside of Angular's
|
||||
* animation DSL language. If this information is new, please navigate to the {@link
|
||||
* Component#animations-anchor component animations metadata page} to gain a better understanding of
|
||||
* how animations in Angular2 are used.
|
||||
* how animations in Angular are used.
|
||||
*
|
||||
* `keyframes` specifies a collection of {@link style style} entries each optionally characterized
|
||||
* by an `offset` value.
|
||||
@ -446,10 +446,10 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe
|
||||
}
|
||||
|
||||
/**
|
||||
* `transition` is an animation-specific function that is designed to be used inside of Angular2's
|
||||
* `transition` is an animation-specific function that is designed to be used inside of Angular's
|
||||
* animation DSL language. If this information is new, please navigate to the {@link
|
||||
* Component#animations-anchor component animations metadata page} to gain a better understanding of
|
||||
* how animations in Angular2 are used.
|
||||
* how animations in Angular are used.
|
||||
*
|
||||
* `transition` declares the {@link sequence sequence of animation steps} that will be run when the
|
||||
* provided `stateChangeExpr` value is satisfied. The `stateChangeExpr` consists of a `state1 =>
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
// Must be imported first, because angular2 decorators throws on load.
|
||||
// Must be imported first, because Angular decorators throw on load.
|
||||
import 'reflect-metadata';
|
||||
|
||||
export {InjectionToken, Injector, Provider, ReflectiveInjector} from '@angular/core';
|
||||
|
@ -24,6 +24,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
static PROVIDERS = [ChromeDriverExtension];
|
||||
|
||||
private _majorChromeVersion: number;
|
||||
private _firstRun = true;
|
||||
|
||||
constructor(private _driver: WebDriverAdapter, @Inject(Options.USER_AGENT) userAgent: string) {
|
||||
super();
|
||||
@ -48,6 +49,12 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
gc() { return this._driver.executeScript('window.gc()'); }
|
||||
|
||||
timeBegin(name: string): Promise<any> {
|
||||
if (this._firstRun) {
|
||||
this._firstRun = false;
|
||||
// Before the first run, read out the existing performance logs
|
||||
// so that the chrome buffer does not fill up.
|
||||
this._driver.logs('performance');
|
||||
}
|
||||
return this._driver.executeScript(`console.time('${name}');`);
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal';
|
||||
import {AsyncTestCompleter, describe, expect, iit, inject, it} from '@angular/core/testing/src/testing_internal';
|
||||
|
||||
import {ChromeDriverExtension, Options, ReflectiveInjector, WebDriverAdapter, WebDriverExtension} from '../../index';
|
||||
import {TraceEventFactory} from '../trace_event_factory';
|
||||
@ -61,14 +61,29 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should mark the timeline via console.time()',
|
||||
it('should clear the perf logs and mark the timeline via console.time() on the first call',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension().timeBegin('someName').then((_) => {
|
||||
expect(log).toEqual([['executeScript', `console.time('someName');`]]);
|
||||
createExtension().timeBegin('someName').then(() => {
|
||||
expect(log).toEqual(
|
||||
[['logs', 'performance'], ['executeScript', `console.time('someName');`]]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should mark the timeline via console.time() on the second call',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
const ext = createExtension();
|
||||
ext.timeBegin('someName')
|
||||
.then((_) => {
|
||||
log.splice(0, log.length);
|
||||
ext.timeBegin('someName');
|
||||
})
|
||||
.then(() => {
|
||||
expect(log).toEqual([['executeScript', `console.time('someName');`]]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should mark the timeline via console.timeEnd()',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
createExtension().timeEnd('someName', null).then((_) => {
|
||||
|
@ -72,7 +72,7 @@ export class NgLocaleLocalization extends NgLocalization {
|
||||
}
|
||||
|
||||
// This is generated code DO NOT MODIFY
|
||||
// see angular2/script/cldr/gen_plural_rules.js
|
||||
// see angular/script/cldr/gen_plural_rules.js
|
||||
|
||||
/** @experimental */
|
||||
export enum Plural {
|
||||
|
@ -63,6 +63,33 @@ const EXPECTED_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
</xliff>
|
||||
`;
|
||||
|
||||
const EXPECTED_XLIFF2 = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<xliff version="2.0" xmlns="urn:oasis:names:tc:xliff:document:2.0" srcLang="en">
|
||||
<file original="ng.template" id="ngi18n">
|
||||
<unit id="8136548302122759730">
|
||||
<notes>
|
||||
<note category="description">desc</note>
|
||||
<note category="meaning">meaning</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source>translate me</source>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="3492007542396725315">
|
||||
<segment>
|
||||
<source>Welcome</source>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="126808141597411718">
|
||||
<segment>
|
||||
<source>other-3rdP-component
|
||||
multi-lines</source>
|
||||
</segment>
|
||||
</unit>
|
||||
</file>
|
||||
</xliff>
|
||||
`;
|
||||
|
||||
describe('template i18n extraction output', () => {
|
||||
const outDir = '';
|
||||
const genDir = 'out';
|
||||
@ -81,6 +108,13 @@ describe('template i18n extraction output', () => {
|
||||
expect(xlf).toEqual(EXPECTED_XLIFF);
|
||||
});
|
||||
|
||||
it('should extract i18n messages as xliff version 2.0', () => {
|
||||
const xlfOutput = path.join(outDir, 'messages.xliff2.xlf');
|
||||
expect(fs.existsSync(xlfOutput)).toBeTruthy();
|
||||
const xlf = fs.readFileSync(xlfOutput, {encoding: 'utf-8'});
|
||||
expect(xlf).toEqual(EXPECTED_XLIFF2);
|
||||
});
|
||||
|
||||
it('should not emit js', () => {
|
||||
const genOutput = path.join(genDir, '');
|
||||
expect(fs.existsSync(genOutput)).toBeFalsy();
|
||||
|
@ -30,7 +30,7 @@ function getSourcePositionForStack(stack: string): {source: string, line: number
|
||||
const htmlLocations = stack
|
||||
.split('\n')
|
||||
// e.g. at View_MyComp_0 (...html:153:40)
|
||||
.map(line => /\((.*\.html):(\d+):(\d+)/.exec(line))
|
||||
.map(line => /\((.*\.html):(\d+):(\d+)/.exec(line) !)
|
||||
.filter(match => !!match)
|
||||
.map(match => ({
|
||||
source: match[1],
|
||||
|
@ -8,7 +8,7 @@
|
||||
*/
|
||||
/* tslint:disable:no-console */
|
||||
|
||||
// Must be imported first, because angular2 decorators throws on load.
|
||||
// Must be imported first, because Angular decorators throw on load.
|
||||
import 'reflect-metadata';
|
||||
|
||||
import * as path from 'path';
|
||||
@ -67,9 +67,9 @@ function codeGenTest() {
|
||||
angularCompilerOptions: config.ngOptions,
|
||||
|
||||
// i18n options.
|
||||
i18nFormat: null,
|
||||
i18nFile: null,
|
||||
locale: null,
|
||||
i18nFormat: undefined,
|
||||
i18nFile: undefined,
|
||||
locale: undefined,
|
||||
|
||||
readResource: (fileName: string) => {
|
||||
readResources.push(fileName);
|
||||
@ -131,8 +131,8 @@ function i18nTest() {
|
||||
compilerOptions: config.parsed.options, program, host,
|
||||
angularCompilerOptions: config.ngOptions,
|
||||
i18nFormat: 'xlf',
|
||||
locale: null,
|
||||
outFile: null,
|
||||
locale: undefined,
|
||||
outFile: undefined,
|
||||
readResource: (fileName: string) => {
|
||||
readResources.push(fileName);
|
||||
return hostContext.readResource(fileName);
|
||||
|
@ -8,7 +8,7 @@
|
||||
*/
|
||||
/* tslint:disable:no-console */
|
||||
|
||||
// Must be imported first, because angular2 decorators throws on load.
|
||||
// Must be imported first, because Angular decorators throw on load.
|
||||
import 'reflect-metadata';
|
||||
|
||||
import * as path from 'path';
|
||||
@ -34,7 +34,8 @@ function main() {
|
||||
if (/.*\/node_modules\/.*/.test(fileName) && !/.*ngsummary\.json$/.test(fileName) &&
|
||||
!/package\.json$/.test(fileName)) {
|
||||
// Only allow to read summaries and package.json files from node_modules
|
||||
return null;
|
||||
// TODO (mhevery): Fix this. TypeScript.d.ts does not allow returning null.
|
||||
return null !;
|
||||
}
|
||||
readFiles.push(path.relative(basePath, fileName));
|
||||
return super.readFile(fileName);
|
||||
|
@ -13,7 +13,7 @@ import {platformServer} from '@angular/platform-server';
|
||||
import {MainModule} from '../src/module';
|
||||
import {MainModuleNgFactory} from '../src/module.ngfactory';
|
||||
|
||||
let mainModuleRef: NgModuleRef<MainModule> = null;
|
||||
let mainModuleRef: NgModuleRef<MainModule> = null !;
|
||||
beforeEach((done) => {
|
||||
platformServer().bootstrapModuleFactory(MainModuleNgFactory).then((moduleRef: any) => {
|
||||
mainModuleRef = moduleRef;
|
||||
@ -29,5 +29,5 @@ export function createComponent<C>(comp: {new (...args: any[]): C}): ComponentFi
|
||||
const moduleRef = createModule();
|
||||
const compRef =
|
||||
moduleRef.componentFactoryResolver.resolveComponentFactory(comp).create(moduleRef.injector);
|
||||
return new ComponentFixture(compRef, null, null);
|
||||
return new ComponentFixture(compRef, null, false);
|
||||
}
|
||||
|
@ -10,6 +10,8 @@
|
||||
"target": "es5",
|
||||
"experimentalDecorators": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "node",
|
||||
"rootDir": "",
|
||||
"declaration": true,
|
||||
@ -32,4 +34,4 @@
|
||||
"benchmarks/src/largetable/ng2/index_aot.ts",
|
||||
"benchmarks/src/largetable/ng2_switch/index_aot.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
"ng-xi18n": "./src/extract_i18n.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/tsc-wrapped": "4.0.0",
|
||||
"@angular/tsc-wrapped": "4.0.3",
|
||||
"reflect-metadata": "^0.1.2",
|
||||
"minimist": "^1.2.0"
|
||||
},
|
||||
|
@ -24,7 +24,7 @@ const GENERATED_META_FILES = /\.json$/;
|
||||
const PREAMBLE = `/**
|
||||
* @fileoverview This file is generated by the Angular template compiler.
|
||||
* Do not edit.
|
||||
* @suppress {suspiciousCode,uselessCode,missingProperties}
|
||||
* @suppress {suspiciousCode,uselessCode,missingProperties,missingOverride}
|
||||
*/
|
||||
/* tslint:disable */
|
||||
|
||||
|
@ -33,6 +33,7 @@ export class CompilerHost implements AotCompilerHost {
|
||||
private resolverCache = new Map<string, ModuleMetadata[]>();
|
||||
private bundleIndexCache = new Map<string, boolean>();
|
||||
private bundleIndexNames = new Set<string>();
|
||||
private moduleFileNames = new Map<string, string>();
|
||||
protected resolveModuleNameHost: CompilerHostContext;
|
||||
|
||||
constructor(
|
||||
@ -69,19 +70,25 @@ export class CompilerHost implements AotCompilerHost {
|
||||
getCanonicalFileName(fileName: string): string { return fileName; }
|
||||
|
||||
moduleNameToFileName(m: string, containingFile: string): string|null {
|
||||
if (!containingFile || !containingFile.length) {
|
||||
if (m.indexOf('.') === 0) {
|
||||
throw new Error('Resolution of relative paths requires a containing file.');
|
||||
const key = m + ':' + (containingFile || '');
|
||||
let result = this.moduleFileNames.get(key);
|
||||
if (!result) {
|
||||
if (!containingFile || !containingFile.length) {
|
||||
if (m.indexOf('.') === 0) {
|
||||
throw new Error('Resolution of relative paths requires a containing file.');
|
||||
}
|
||||
// Any containing file gives the same result for absolute imports
|
||||
containingFile = this.getCanonicalFileName(path.join(this.basePath, 'index.ts'));
|
||||
}
|
||||
// Any containing file gives the same result for absolute imports
|
||||
containingFile = this.getCanonicalFileName(path.join(this.basePath, 'index.ts'));
|
||||
m = m.replace(EXT, '');
|
||||
const resolved =
|
||||
ts.resolveModuleName(
|
||||
m, containingFile.replace(/\\/g, '/'), this.options, this.resolveModuleNameHost)
|
||||
.resolvedModule;
|
||||
result = resolved ? this.getCanonicalFileName(resolved.resolvedFileName) : null;
|
||||
this.moduleFileNames.set(key, result);
|
||||
}
|
||||
m = m.replace(EXT, '');
|
||||
const resolved =
|
||||
ts.resolveModuleName(
|
||||
m, containingFile.replace(/\\/g, '/'), this.options, this.resolveModuleNameHost)
|
||||
.resolvedModule;
|
||||
return resolved ? this.getCanonicalFileName(resolved.resolvedFileName) : null;
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -11,7 +11,7 @@
|
||||
/**
|
||||
* Extract i18n messages from source code
|
||||
*/
|
||||
// Must be imported first, because angular2 decorators throws on load.
|
||||
// Must be imported first, because Angular decorators throw on load.
|
||||
import 'reflect-metadata';
|
||||
|
||||
import * as tsc from '@angular/tsc-wrapped';
|
||||
|
@ -10,7 +10,7 @@
|
||||
/**
|
||||
* Extract i18n messages from source code
|
||||
*/
|
||||
// Must be imported first, because angular2 decorators throws on load.
|
||||
// Must be imported first, because Angular decorators throw on load.
|
||||
import 'reflect-metadata';
|
||||
|
||||
import * as compiler from '@angular/compiler';
|
||||
@ -34,7 +34,7 @@ export class Extractor {
|
||||
const promiseBundle = this.extractBundle();
|
||||
|
||||
return promiseBundle.then(bundle => {
|
||||
const content = this.serialize(bundle, ext);
|
||||
const content = this.serialize(bundle, formatName);
|
||||
const dstFile = outFile || `messages.${ext}`;
|
||||
const dstPath = path.join(this.options.genDir, dstFile);
|
||||
this.host.writeFile(dstPath, content, false);
|
||||
@ -48,14 +48,20 @@ export class Extractor {
|
||||
return this.ngExtractor.extract(files);
|
||||
}
|
||||
|
||||
serialize(bundle: compiler.MessageBundle, ext: string): string {
|
||||
serialize(bundle: compiler.MessageBundle, formatName: string): string {
|
||||
const format = formatName.toLowerCase();
|
||||
let serializer: compiler.Serializer;
|
||||
|
||||
switch (ext) {
|
||||
switch (format) {
|
||||
case 'xmb':
|
||||
serializer = new compiler.Xmb();
|
||||
break;
|
||||
case 'xliff2':
|
||||
case 'xlf2':
|
||||
serializer = new compiler.Xliff2();
|
||||
break;
|
||||
case 'xlf':
|
||||
case 'xliff':
|
||||
default:
|
||||
serializer = new compiler.Xliff();
|
||||
}
|
||||
@ -66,10 +72,18 @@ export class Extractor {
|
||||
getExtension(formatName: string): string {
|
||||
const format = (formatName || 'xlf').toLowerCase();
|
||||
|
||||
if (format === 'xmb') return 'xmb';
|
||||
if (format === 'xlf' || format === 'xlif' || format === 'xliff') return 'xlf';
|
||||
switch (format) {
|
||||
case 'xmb':
|
||||
return 'xmb';
|
||||
case 'xlf':
|
||||
case 'xlif':
|
||||
case 'xliff':
|
||||
case 'xlf2':
|
||||
case 'xliff2':
|
||||
return 'xlf';
|
||||
}
|
||||
|
||||
throw new Error('Unsupported format "${formatName}"');
|
||||
throw new Error(`Unsupported format "${formatName}"`);
|
||||
}
|
||||
|
||||
static create(
|
||||
|
@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
|
||||
// Must be imported first, because angular2 decorators throws on load.
|
||||
// Must be imported first, because Angular decorators throw on load.
|
||||
import 'reflect-metadata';
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
@ -32,9 +32,9 @@ export interface NgTools_InternalApi_NG2_CodeGen_Options {
|
||||
angularCompilerOptions: AngularCompilerOptions;
|
||||
|
||||
// i18n options.
|
||||
i18nFormat: string;
|
||||
i18nFile: string;
|
||||
locale: string;
|
||||
i18nFormat?: string;
|
||||
i18nFile?: string;
|
||||
locale?: string;
|
||||
|
||||
readResource: (fileName: string) => Promise<string>;
|
||||
|
||||
@ -58,7 +58,7 @@ export interface NgTools_InternalApi_NG2_ExtractI18n_Options {
|
||||
program: ts.Program;
|
||||
host: ts.CompilerHost;
|
||||
angularCompilerOptions: AngularCompilerOptions;
|
||||
i18nFormat: string;
|
||||
i18nFormat?: string;
|
||||
readResource: (fileName: string) => Promise<string>;
|
||||
// Every new property under this line should be optional.
|
||||
locale?: string;
|
||||
@ -124,7 +124,7 @@ export class NgTools_InternalApi_NG_2 {
|
||||
const symbolCache = new StaticSymbolCache();
|
||||
const summaryResolver = new AotSummaryResolver(ngCompilerHost, symbolCache);
|
||||
const symbolResolver = new StaticSymbolResolver(ngCompilerHost, symbolCache, summaryResolver);
|
||||
const staticReflector = new StaticReflector(symbolResolver);
|
||||
const staticReflector = new StaticReflector(summaryResolver, symbolResolver);
|
||||
const routeMap = listLazyRoutesOfModule(options.entryModule, ngCompilerHost, staticReflector);
|
||||
|
||||
return Object.keys(routeMap).reduce(
|
||||
|
@ -47,7 +47,7 @@ export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCom
|
||||
const symbolCache = new StaticSymbolCache();
|
||||
const summaryResolver = new AotSummaryResolver(compilerHost, symbolCache);
|
||||
const symbolResolver = new StaticSymbolResolver(compilerHost, symbolCache, summaryResolver);
|
||||
const staticReflector = new StaticReflector(symbolResolver);
|
||||
const staticReflector = new StaticReflector(summaryResolver, symbolResolver);
|
||||
StaticAndDynamicReflectionCapabilities.install(staticReflector);
|
||||
const console = new Console();
|
||||
const htmlParser = new I18NHtmlParser(
|
||||
|
@ -7,7 +7,11 @@
|
||||
*/
|
||||
|
||||
import {Attribute, Component, ContentChild, ContentChildren, Directive, Host, HostBinding, HostListener, Inject, Injectable, Input, NgModule, Optional, Output, Pipe, Self, SkipSelf, ViewChild, ViewChildren, animate, group, keyframes, sequence, state, style, transition, trigger, ɵReflectorReader} from '@angular/core';
|
||||
|
||||
import {CompileSummaryKind} from '../compile_metadata';
|
||||
import {SummaryResolver} from '../summary_resolver';
|
||||
import {syntaxError} from '../util';
|
||||
|
||||
import {StaticSymbol} from './static_symbol';
|
||||
import {StaticSymbolResolver} from './static_symbol_resolver';
|
||||
|
||||
@ -35,8 +39,11 @@ export class StaticReflector implements ɵReflectorReader {
|
||||
private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>();
|
||||
private injectionToken: StaticSymbol;
|
||||
private opaqueToken: StaticSymbol;
|
||||
private annotationForParentClassWithSummaryKind = new Map<CompileSummaryKind, any[]>();
|
||||
private annotationNames = new Map<any, string>();
|
||||
|
||||
constructor(
|
||||
private summaryResolver: SummaryResolver<StaticSymbol>,
|
||||
private symbolResolver: StaticSymbolResolver,
|
||||
knownMetadataClasses: {name: string, filePath: string, ctor: any}[] = [],
|
||||
knownMetadataFunctions: {name: string, filePath: string, fn: any}[] = [],
|
||||
@ -47,6 +54,17 @@ export class StaticReflector implements ɵReflectorReader {
|
||||
this.getStaticSymbol(kc.filePath, kc.name), kc.ctor));
|
||||
knownMetadataFunctions.forEach(
|
||||
(kf) => this._registerFunction(this.getStaticSymbol(kf.filePath, kf.name), kf.fn));
|
||||
this.annotationForParentClassWithSummaryKind.set(
|
||||
CompileSummaryKind.Directive, [Directive, Component]);
|
||||
this.annotationForParentClassWithSummaryKind.set(CompileSummaryKind.Pipe, [Pipe]);
|
||||
this.annotationForParentClassWithSummaryKind.set(CompileSummaryKind.NgModule, [NgModule]);
|
||||
this.annotationForParentClassWithSummaryKind.set(
|
||||
CompileSummaryKind.Injectable, [Injectable, Pipe, Directive, Component, NgModule]);
|
||||
this.annotationNames.set(Directive, 'Directive');
|
||||
this.annotationNames.set(Component, 'Component');
|
||||
this.annotationNames.set(Pipe, 'Pipe');
|
||||
this.annotationNames.set(NgModule, 'NgModule');
|
||||
this.annotationNames.set(Injectable, 'Injectable');
|
||||
}
|
||||
|
||||
importUri(typeOrFunc: StaticSymbol): string {
|
||||
@ -96,17 +114,33 @@ export class StaticReflector implements ɵReflectorReader {
|
||||
if (!annotations) {
|
||||
annotations = [];
|
||||
const classMetadata = this.getTypeMetadata(type);
|
||||
if (classMetadata['extends']) {
|
||||
const parentType = this.trySimplify(type, classMetadata['extends']);
|
||||
if (parentType && (parentType instanceof StaticSymbol)) {
|
||||
const parentAnnotations = this.annotations(parentType);
|
||||
annotations.push(...parentAnnotations);
|
||||
}
|
||||
const parentType = this.findParentType(type, classMetadata);
|
||||
if (parentType) {
|
||||
const parentAnnotations = this.annotations(parentType);
|
||||
annotations.push(...parentAnnotations);
|
||||
}
|
||||
let ownAnnotations: any[] = [];
|
||||
if (classMetadata['decorators']) {
|
||||
const ownAnnotations: any[] = this.simplify(type, classMetadata['decorators']);
|
||||
ownAnnotations = this.simplify(type, classMetadata['decorators']);
|
||||
annotations.push(...ownAnnotations);
|
||||
}
|
||||
if (parentType && !this.summaryResolver.isLibraryFile(type.filePath) &&
|
||||
this.summaryResolver.isLibraryFile(parentType.filePath)) {
|
||||
const summary = this.summaryResolver.resolveSummary(parentType);
|
||||
if (summary && summary.type) {
|
||||
const requiredAnnotationTypes =
|
||||
this.annotationForParentClassWithSummaryKind.get(summary.type.summaryKind);
|
||||
const typeHasRequiredAnnotation = requiredAnnotationTypes.some(
|
||||
requiredType => ownAnnotations.some(ann => ann instanceof requiredType));
|
||||
if (!typeHasRequiredAnnotation) {
|
||||
this.reportError(
|
||||
syntaxError(
|
||||
`Class ${type.name} in ${type.filePath} extends from a ${CompileSummaryKind[summary.type.summaryKind]} in another compilation unit without duplicating the decorator. ` +
|
||||
`Please add a ${requiredAnnotationTypes.map(type => this.annotationNames.get(type)).join(' or ')} decorator to the class.`),
|
||||
type);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.annotationCache.set(type, annotations.filter(ann => !!ann));
|
||||
}
|
||||
return annotations;
|
||||
@ -117,14 +151,12 @@ export class StaticReflector implements ɵReflectorReader {
|
||||
if (!propMetadata) {
|
||||
const classMetadata = this.getTypeMetadata(type);
|
||||
propMetadata = {};
|
||||
if (classMetadata['extends']) {
|
||||
const parentType = this.simplify(type, classMetadata['extends']);
|
||||
if (parentType instanceof StaticSymbol) {
|
||||
const parentPropMetadata = this.propMetadata(parentType);
|
||||
Object.keys(parentPropMetadata).forEach((parentProp) => {
|
||||
propMetadata[parentProp] = parentPropMetadata[parentProp];
|
||||
});
|
||||
}
|
||||
const parentType = this.findParentType(type, classMetadata);
|
||||
if (parentType) {
|
||||
const parentPropMetadata = this.propMetadata(parentType);
|
||||
Object.keys(parentPropMetadata).forEach((parentProp) => {
|
||||
propMetadata[parentProp] = parentPropMetadata[parentProp];
|
||||
});
|
||||
}
|
||||
|
||||
const members = classMetadata['members'] || {};
|
||||
@ -157,6 +189,7 @@ export class StaticReflector implements ɵReflectorReader {
|
||||
let parameters = this.parameterCache.get(type);
|
||||
if (!parameters) {
|
||||
const classMetadata = this.getTypeMetadata(type);
|
||||
const parentType = this.findParentType(type, classMetadata);
|
||||
const members = classMetadata ? classMetadata['members'] : null;
|
||||
const ctorData = members ? members['__ctor__'] : null;
|
||||
if (ctorData) {
|
||||
@ -175,11 +208,8 @@ export class StaticReflector implements ɵReflectorReader {
|
||||
}
|
||||
parameters.push(nestedResult);
|
||||
});
|
||||
} else if (classMetadata['extends']) {
|
||||
const parentType = this.simplify(type, classMetadata['extends']);
|
||||
if (parentType instanceof StaticSymbol) {
|
||||
parameters = this.parameters(parentType);
|
||||
}
|
||||
} else if (parentType) {
|
||||
parameters = this.parameters(parentType);
|
||||
}
|
||||
if (!parameters) {
|
||||
parameters = [];
|
||||
@ -198,14 +228,12 @@ export class StaticReflector implements ɵReflectorReader {
|
||||
if (!methodNames) {
|
||||
const classMetadata = this.getTypeMetadata(type);
|
||||
methodNames = {};
|
||||
if (classMetadata['extends']) {
|
||||
const parentType = this.simplify(type, classMetadata['extends']);
|
||||
if (parentType instanceof StaticSymbol) {
|
||||
const parentMethodNames = this._methodNames(parentType);
|
||||
Object.keys(parentMethodNames).forEach((parentProp) => {
|
||||
methodNames[parentProp] = parentMethodNames[parentProp];
|
||||
});
|
||||
}
|
||||
const parentType = this.findParentType(type, classMetadata);
|
||||
if (parentType) {
|
||||
const parentMethodNames = this._methodNames(parentType);
|
||||
Object.keys(parentMethodNames).forEach((parentProp) => {
|
||||
methodNames[parentProp] = parentMethodNames[parentProp];
|
||||
});
|
||||
}
|
||||
|
||||
const members = classMetadata['members'] || {};
|
||||
@ -219,6 +247,13 @@ export class StaticReflector implements ɵReflectorReader {
|
||||
return methodNames;
|
||||
}
|
||||
|
||||
private findParentType(type: StaticSymbol, classMetadata: any): StaticSymbol|null {
|
||||
const parentType = this.trySimplify(type, classMetadata['extends']);
|
||||
if (parentType instanceof StaticSymbol) {
|
||||
return parentType;
|
||||
}
|
||||
}
|
||||
|
||||
hasLifecycleHook(type: any, lcProperty: string): boolean {
|
||||
if (!(type instanceof StaticSymbol)) {
|
||||
this.reportError(
|
||||
@ -549,7 +584,7 @@ export class StaticReflector implements ɵReflectorReader {
|
||||
return simplifyCall(staticSymbol, targetFunction, argExpressions);
|
||||
}
|
||||
}
|
||||
break;
|
||||
return IGNORE;
|
||||
case 'error':
|
||||
let message = produceErrorMessage(expression);
|
||||
if (expression['line']) {
|
||||
|
@ -59,6 +59,7 @@ export class StaticSymbolResolver {
|
||||
// Note: this will only contain StaticSymbols without members!
|
||||
private importAs = new Map<StaticSymbol, StaticSymbol>();
|
||||
private symbolResourcePaths = new Map<StaticSymbol, string>();
|
||||
private symbolFromFile = new Map<string, StaticSymbol[]>();
|
||||
|
||||
constructor(
|
||||
private host: StaticSymbolResolverHost, private staticSymbolCache: StaticSymbolCache,
|
||||
@ -143,6 +144,25 @@ export class StaticSymbolResolver {
|
||||
this.importAs.set(sourceSymbol, targetSymbol);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate all information derived from the given file.
|
||||
*
|
||||
* @param fileName the file to invalidate
|
||||
*/
|
||||
invalidateFile(fileName: string) {
|
||||
this.metadataCache.delete(fileName);
|
||||
this.resolvedFilePaths.delete(fileName);
|
||||
const symbols = this.symbolFromFile.get(fileName);
|
||||
if (symbols) {
|
||||
this.symbolFromFile.delete(fileName);
|
||||
for (const symbol of symbols) {
|
||||
this.resolvedSymbols.delete(symbol);
|
||||
this.importAs.delete(symbol);
|
||||
this.symbolResourcePaths.delete(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _resolveSymbolMembers(staticSymbol: StaticSymbol): ResolvedStaticSymbol {
|
||||
const members = staticSymbol.members;
|
||||
const baseResolvedSymbol =
|
||||
@ -281,11 +301,23 @@ export class StaticSymbolResolver {
|
||||
}
|
||||
resolvedSymbols.forEach(
|
||||
(resolvedSymbol) => this.resolvedSymbols.set(resolvedSymbol.symbol, resolvedSymbol));
|
||||
this.symbolFromFile.set(filePath, resolvedSymbols.map(resolvedSymbol => resolvedSymbol.symbol));
|
||||
}
|
||||
|
||||
private createResolvedSymbol(
|
||||
sourceSymbol: StaticSymbol, topLevelPath: string, topLevelSymbolNames: Set<string>,
|
||||
metadata: any): ResolvedStaticSymbol {
|
||||
// For classes that don't have Angular summaries / metadata,
|
||||
// we only keep their arity, but nothing else
|
||||
// (e.g. their constructor parameters).
|
||||
// We do this to prevent introducing deep imports
|
||||
// as we didn't generate .ngfactory.ts files with proper reexports.
|
||||
if (this.summaryResolver.isLibraryFile(sourceSymbol.filePath) && metadata &&
|
||||
metadata['__symbolic'] === 'class') {
|
||||
const transformedMeta = {__symbolic: 'class', arity: metadata.arity};
|
||||
return new ResolvedStaticSymbol(sourceSymbol, transformedMeta);
|
||||
}
|
||||
|
||||
const self = this;
|
||||
|
||||
class ReferenceTransformer extends ValueTransformer {
|
||||
|
@ -53,7 +53,7 @@ export function serializeSummaries(
|
||||
// (in a minimal way).
|
||||
types.forEach((typeSummary) => {
|
||||
serializer.addOrMergeSummary(
|
||||
{symbol: typeSummary.type.reference, metadata: {__symbolic: 'class'}, type: typeSummary});
|
||||
{symbol: typeSummary.type.reference, metadata: null, type: typeSummary});
|
||||
if (typeSummary.summaryKind === CompileSummaryKind.NgModule) {
|
||||
const ngModuleSummary = <CompileNgModuleSummary>typeSummary;
|
||||
ngModuleSummary.exportedDirectives.concat(ngModuleSummary.exportedPipes).forEach((id) => {
|
||||
@ -94,10 +94,21 @@ class Serializer extends ValueTransformer {
|
||||
addOrMergeSummary(summary: Summary<StaticSymbol>) {
|
||||
let symbolMeta = summary.metadata;
|
||||
if (symbolMeta && symbolMeta.__symbolic === 'class') {
|
||||
// For classes, we only keep their statics and arity, but not the metadata
|
||||
// of the class itself as that has been captured already via other summaries
|
||||
// (e.g. DirectiveSummary, ...).
|
||||
symbolMeta = {__symbolic: 'class', statics: symbolMeta.statics, arity: symbolMeta.arity};
|
||||
// For classes, we keep everything except their class decorators.
|
||||
// We need to keep e.g. the ctor args, method names, method decorators
|
||||
// so that the class can be extended in another compilation unit.
|
||||
// We don't keep the class decorators as
|
||||
// 1) they refer to data
|
||||
// that should not cause a rebuild of downstream compilation units
|
||||
// (e.g. inline templates of @Component, or @NgModule.declarations)
|
||||
// 2) their data is already captured in TypeSummaries, e.g. DirectiveSummary.
|
||||
const clone: {[key: string]: any} = {};
|
||||
Object.keys(symbolMeta).forEach((propName) => {
|
||||
if (propName !== 'decorators') {
|
||||
clone[propName] = symbolMeta[propName];
|
||||
}
|
||||
});
|
||||
symbolMeta = clone;
|
||||
}
|
||||
|
||||
let processedSummary = this.processedSummaryBySymbol.get(summary.symbol);
|
||||
|
@ -388,7 +388,8 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
||||
visitAll(asts: cdAst.AST[], mode: _Mode): any { return asts.map(ast => this.visit(ast, mode)); }
|
||||
|
||||
visitQuote(ast: cdAst.Quote, mode: _Mode): any {
|
||||
throw new Error('Quotes are not supported for evaluation!');
|
||||
throw new Error(`Quotes are not supported for evaluation!
|
||||
Statement: ${ast.uninterpretedExpression} located at ${ast.location}`);
|
||||
}
|
||||
|
||||
private visit(ast: cdAst.AST, mode: _Mode): any {
|
||||
|
@ -65,6 +65,10 @@ export class DirectiveNormalizer {
|
||||
let normalizedTemplateSync: CompileTemplateMetadata = null;
|
||||
let normalizedTemplateAsync: Promise<CompileTemplateMetadata>;
|
||||
if (prenormData.template != null) {
|
||||
if (prenormData.templateUrl != null) {
|
||||
throw syntaxError(
|
||||
`'${stringify(prenormData.componentType)}' component cannot define both template and templateUrl`);
|
||||
}
|
||||
if (typeof prenormData.template !== 'string') {
|
||||
throw syntaxError(
|
||||
`The template specified for component ${stringify(prenormData.componentType)} is not a string`);
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Component, Directive, HostBinding, HostListener, Input, Output, Query, Type, resolveForwardRef, ɵReflectorReader, ɵmerge as merge, ɵreflector, ɵstringify as stringify} from '@angular/core';
|
||||
import {Component, Directive, HostBinding, HostListener, Input, Output, Query, Type, resolveForwardRef, ɵReflectorReader, ɵreflector, ɵstringify as stringify} from '@angular/core';
|
||||
import {CompilerInjectable} from './injectable';
|
||||
import {splitAtColon} from './util';
|
||||
|
||||
@ -124,8 +124,8 @@ export class DirectiveResolver {
|
||||
this._dedupeBindings(directive.inputs ? directive.inputs.concat(inputs) : inputs);
|
||||
const mergedOutputs =
|
||||
this._dedupeBindings(directive.outputs ? directive.outputs.concat(outputs) : outputs);
|
||||
const mergedHost = directive.host ? merge(directive.host, host) : host;
|
||||
const mergedQueries = directive.queries ? merge(directive.queries, queries) : queries;
|
||||
const mergedHost = directive.host ? {...directive.host, ...host} : host;
|
||||
const mergedQueries = directive.queries ? {...directive.queries, ...queries} : queries;
|
||||
|
||||
if (directive instanceof Component) {
|
||||
return new Component({
|
||||
|
@ -94,7 +94,7 @@ export class Extractor {
|
||||
const symbolCache = new StaticSymbolCache();
|
||||
const summaryResolver = new AotSummaryResolver(host, symbolCache);
|
||||
const staticSymbolResolver = new StaticSymbolResolver(host, symbolCache, summaryResolver);
|
||||
const staticReflector = new StaticReflector(staticSymbolResolver);
|
||||
const staticReflector = new StaticReflector(summaryResolver, staticSymbolResolver);
|
||||
StaticAndDynamicReflectionCapabilities.install(staticReflector);
|
||||
|
||||
const config =
|
||||
|
@ -13,6 +13,7 @@ import {ParseTreeResult} from '../ml_parser/parser';
|
||||
import {mergeTranslations} from './extractor_merger';
|
||||
import {Serializer} from './serializers/serializer';
|
||||
import {Xliff} from './serializers/xliff';
|
||||
import {Xliff2} from './serializers/xliff2';
|
||||
import {Xmb} from './serializers/xmb';
|
||||
import {Xtb} from './serializers/xtb';
|
||||
import {TranslationBundle} from './translation_bundle';
|
||||
@ -62,6 +63,9 @@ function createSerializer(format?: string): Serializer {
|
||||
return new Xmb();
|
||||
case 'xtb':
|
||||
return new Xtb();
|
||||
case 'xliff2':
|
||||
case 'xlf2':
|
||||
return new Xliff2();
|
||||
case 'xliff':
|
||||
case 'xlf':
|
||||
default:
|
||||
|
@ -161,8 +161,9 @@ class _I18nVisitor implements html.Visitor {
|
||||
}
|
||||
}
|
||||
|
||||
const _CUSTOM_PH_EXP = /\/\/[\s\S]*i18n[\s\S]*\([\s\S]*ph[\s\S]*=[\s\S]*"([\s\S]*?)"[\s\S]*\)/g;
|
||||
const _CUSTOM_PH_EXP =
|
||||
/\/\/[\s\S]*i18n[\s\S]*\([\s\S]*ph[\s\S]*=[\s\S]*("|')([\s\S]*?)\1[\s\S]*\)/g;
|
||||
|
||||
function _extractPlaceholderName(input: string): string {
|
||||
return input.split(_CUSTOM_PH_EXP)[1];
|
||||
return input.split(_CUSTOM_PH_EXP)[2];
|
||||
}
|
||||
|
@ -11,5 +11,6 @@ export {I18NHtmlParser} from './i18n_html_parser';
|
||||
export {MessageBundle} from './message_bundle';
|
||||
export {Serializer} from './serializers/serializer';
|
||||
export {Xliff} from './serializers/xliff';
|
||||
export {Xliff2} from './serializers/xliff2';
|
||||
export {Xmb} from './serializers/xmb';
|
||||
export {Xtb} from './serializers/xtb';
|
||||
|
370
packages/compiler/src/i18n/serializers/xliff2.ts
Normal file
370
packages/compiler/src/i18n/serializers/xliff2.ts
Normal file
@ -0,0 +1,370 @@
|
||||
/**
|
||||
* @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 ml from '../../ml_parser/ast';
|
||||
import {XmlParser} from '../../ml_parser/xml_parser';
|
||||
import {decimalDigest} from '../digest';
|
||||
import * as i18n from '../i18n_ast';
|
||||
import {I18nError} from '../parse_util';
|
||||
|
||||
import {Serializer} from './serializer';
|
||||
import * as xml from './xml_helper';
|
||||
|
||||
const _VERSION = '2.0';
|
||||
const _XMLNS = 'urn:oasis:names:tc:xliff:document:2.0';
|
||||
// TODO(vicb): make this a param (s/_/-/)
|
||||
const _DEFAULT_SOURCE_LANG = 'en';
|
||||
const _PLACEHOLDER_TAG = 'ph';
|
||||
const _PLACEHOLDER_SPANNING_TAG = 'pc';
|
||||
|
||||
const _XLIFF_TAG = 'xliff';
|
||||
const _SOURCE_TAG = 'source';
|
||||
const _TARGET_TAG = 'target';
|
||||
const _UNIT_TAG = 'unit';
|
||||
|
||||
// http://docs.oasis-open.org/xliff/xliff-core/v2.0/os/xliff-core-v2.0-os.html
|
||||
export class Xliff2 extends Serializer {
|
||||
write(messages: i18n.Message[], locale: string|null): string {
|
||||
const visitor = new _WriteVisitor();
|
||||
const units: xml.Node[] = [];
|
||||
|
||||
messages.forEach(message => {
|
||||
const unit = new xml.Tag(_UNIT_TAG, {id: message.id});
|
||||
|
||||
if (message.description || message.meaning) {
|
||||
const notes = new xml.Tag('notes');
|
||||
if (message.description) {
|
||||
notes.children.push(
|
||||
new xml.CR(8),
|
||||
new xml.Tag('note', {category: 'description'}, [new xml.Text(message.description)]));
|
||||
}
|
||||
|
||||
if (message.meaning) {
|
||||
notes.children.push(
|
||||
new xml.CR(8),
|
||||
new xml.Tag('note', {category: 'meaning'}, [new xml.Text(message.meaning)]));
|
||||
}
|
||||
|
||||
notes.children.push(new xml.CR(6));
|
||||
unit.children.push(new xml.CR(6), notes);
|
||||
}
|
||||
|
||||
const segment = new xml.Tag('segment');
|
||||
|
||||
segment.children.push(
|
||||
new xml.CR(8), new xml.Tag(_SOURCE_TAG, {}, visitor.serialize(message.nodes)),
|
||||
new xml.CR(6));
|
||||
|
||||
unit.children.push(new xml.CR(6), segment, new xml.CR(4));
|
||||
|
||||
units.push(new xml.CR(4), unit);
|
||||
});
|
||||
|
||||
const file =
|
||||
new xml.Tag('file', {'original': 'ng.template', id: 'ngi18n'}, [...units, new xml.CR(2)]);
|
||||
|
||||
const xliff = new xml.Tag(
|
||||
_XLIFF_TAG, {version: _VERSION, xmlns: _XMLNS, srcLang: locale || _DEFAULT_SOURCE_LANG},
|
||||
[new xml.CR(2), file, new xml.CR()]);
|
||||
|
||||
return xml.serialize([
|
||||
new xml.Declaration({version: '1.0', encoding: 'UTF-8'}), new xml.CR(), xliff, new xml.CR()
|
||||
]);
|
||||
}
|
||||
|
||||
load(content: string, url: string):
|
||||
{locale: string, i18nNodesByMsgId: {[msgId: string]: i18n.Node[]}} {
|
||||
// xliff to xml nodes
|
||||
const xliff2Parser = new Xliff2Parser();
|
||||
const {locale, msgIdToHtml, errors} = xliff2Parser.parse(content, url);
|
||||
|
||||
// xml nodes to i18n nodes
|
||||
const i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {};
|
||||
const converter = new XmlToI18n();
|
||||
|
||||
Object.keys(msgIdToHtml).forEach(msgId => {
|
||||
const {i18nNodes, errors: e} = converter.convert(msgIdToHtml[msgId], url);
|
||||
errors.push(...e);
|
||||
i18nNodesByMsgId[msgId] = i18nNodes;
|
||||
});
|
||||
|
||||
if (errors.length) {
|
||||
throw new Error(`xliff2 parse errors:\n${errors.join('\n')}`);
|
||||
}
|
||||
|
||||
return {locale: locale !, i18nNodesByMsgId};
|
||||
}
|
||||
|
||||
digest(message: i18n.Message): string { return decimalDigest(message); }
|
||||
}
|
||||
|
||||
class _WriteVisitor implements i18n.Visitor {
|
||||
private _nextPlaceholderId: number;
|
||||
|
||||
visitText(text: i18n.Text, context?: any): xml.Node[] { return [new xml.Text(text.value)]; }
|
||||
|
||||
visitContainer(container: i18n.Container, context?: any): xml.Node[] {
|
||||
const nodes: xml.Node[] = [];
|
||||
container.children.forEach((node: i18n.Node) => nodes.push(...node.visit(this)));
|
||||
return nodes;
|
||||
}
|
||||
|
||||
visitIcu(icu: i18n.Icu, context?: any): xml.Node[] {
|
||||
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(`} `));
|
||||
});
|
||||
|
||||
nodes.push(new xml.Text(`}`));
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
visitTagPlaceholder(ph: i18n.TagPlaceholder, context?: any): xml.Node[] {
|
||||
const type = getTypeForTag(ph.tag);
|
||||
|
||||
if (ph.isVoid) {
|
||||
const tagPh = new xml.Tag(_PLACEHOLDER_TAG, {
|
||||
id: (this._nextPlaceholderId++).toString(),
|
||||
equiv: ph.startName,
|
||||
type: type,
|
||||
disp: `<${ph.tag}/>`,
|
||||
});
|
||||
return [tagPh];
|
||||
}
|
||||
|
||||
const tagPc = new xml.Tag(_PLACEHOLDER_SPANNING_TAG, {
|
||||
id: (this._nextPlaceholderId++).toString(),
|
||||
equivStart: ph.startName,
|
||||
equivEnd: ph.closeName,
|
||||
type: type,
|
||||
dispStart: `<${ph.tag}>`,
|
||||
dispEnd: `</${ph.tag}>`,
|
||||
});
|
||||
const nodes: xml.Node[] = [].concat(...ph.children.map(node => node.visit(this)));
|
||||
if (nodes.length) {
|
||||
nodes.forEach((node: xml.Node) => tagPc.children.push(node));
|
||||
} else {
|
||||
tagPc.children.push(new xml.Text(''));
|
||||
}
|
||||
|
||||
return [tagPc];
|
||||
}
|
||||
|
||||
visitPlaceholder(ph: i18n.Placeholder, context?: any): xml.Node[] {
|
||||
return [new xml.Tag(_PLACEHOLDER_TAG, {
|
||||
id: (this._nextPlaceholderId++).toString(),
|
||||
equiv: ph.name,
|
||||
disp: `{{${ph.value}}}`,
|
||||
})];
|
||||
}
|
||||
|
||||
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): xml.Node[] {
|
||||
return [new xml.Tag(_PLACEHOLDER_TAG, {id: (this._nextPlaceholderId++).toString()})];
|
||||
}
|
||||
|
||||
serialize(nodes: i18n.Node[]): xml.Node[] {
|
||||
this._nextPlaceholderId = 0;
|
||||
return [].concat(...nodes.map(node => node.visit(this)));
|
||||
}
|
||||
}
|
||||
|
||||
// Extract messages as xml nodes from the xliff file
|
||||
class Xliff2Parser implements ml.Visitor {
|
||||
private _unitMlString: string|null;
|
||||
private _errors: I18nError[];
|
||||
private _msgIdToHtml: {[msgId: string]: string};
|
||||
private _locale: string|null = null;
|
||||
|
||||
parse(xliff: string, url: string) {
|
||||
this._unitMlString = null;
|
||||
this._msgIdToHtml = {};
|
||||
|
||||
const xml = new XmlParser().parse(xliff, url, false);
|
||||
|
||||
this._errors = xml.errors;
|
||||
ml.visitAll(this, xml.rootNodes, null);
|
||||
|
||||
return {
|
||||
msgIdToHtml: this._msgIdToHtml,
|
||||
errors: this._errors,
|
||||
locale: this._locale,
|
||||
};
|
||||
}
|
||||
|
||||
visitElement(element: ml.Element, context: any): any {
|
||||
switch (element.name) {
|
||||
case _UNIT_TAG:
|
||||
this._unitMlString = null;
|
||||
const idAttr = element.attrs.find((attr) => attr.name === 'id');
|
||||
if (!idAttr) {
|
||||
this._addError(element, `<${_UNIT_TAG}> misses the "id" attribute`);
|
||||
} else {
|
||||
const id = idAttr.value;
|
||||
if (this._msgIdToHtml.hasOwnProperty(id)) {
|
||||
this._addError(element, `Duplicated translations for msg ${id}`);
|
||||
} else {
|
||||
ml.visitAll(this, element.children, null);
|
||||
if (typeof this._unitMlString === 'string') {
|
||||
this._msgIdToHtml[id] = this._unitMlString;
|
||||
} else {
|
||||
this._addError(element, `Message ${id} misses a translation`);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case _SOURCE_TAG:
|
||||
// ignore source message
|
||||
break;
|
||||
|
||||
case _TARGET_TAG:
|
||||
const innerTextStart = element.startSourceSpan !.end.offset;
|
||||
const innerTextEnd = element.endSourceSpan !.start.offset;
|
||||
const content = element.startSourceSpan !.start.file.content;
|
||||
const innerText = content.slice(innerTextStart, innerTextEnd);
|
||||
this._unitMlString = innerText;
|
||||
break;
|
||||
|
||||
case _XLIFF_TAG:
|
||||
const localeAttr = element.attrs.find((attr) => attr.name === 'trgLang');
|
||||
if (localeAttr) {
|
||||
this._locale = localeAttr.value;
|
||||
}
|
||||
|
||||
const versionAttr = element.attrs.find((attr) => attr.name === 'version');
|
||||
if (versionAttr) {
|
||||
const version = versionAttr.value;
|
||||
if (version !== '2.0') {
|
||||
this._addError(
|
||||
element,
|
||||
`The XLIFF file version ${version} is not compatible with XLIFF 2.0 serializer`);
|
||||
} else {
|
||||
ml.visitAll(this, element.children, null);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ml.visitAll(this, element.children, null);
|
||||
}
|
||||
}
|
||||
|
||||
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(message: string, url: string) {
|
||||
const xmlIcu = new XmlParser().parse(message, url, true);
|
||||
this._errors = xmlIcu.errors;
|
||||
|
||||
const i18nNodes = this._errors.length > 0 || xmlIcu.rootNodes.length == 0 ?
|
||||
[] :
|
||||
[].concat(...ml.visitAll(this, xmlIcu.rootNodes));
|
||||
|
||||
return {
|
||||
i18nNodes,
|
||||
errors: this._errors,
|
||||
};
|
||||
}
|
||||
|
||||
visitText(text: ml.Text, context: any) { return new i18n.Text(text.value, text.sourceSpan); }
|
||||
|
||||
visitElement(el: ml.Element, context: any): i18n.Node[]|null {
|
||||
switch (el.name) {
|
||||
case _PLACEHOLDER_TAG:
|
||||
const nameAttr = el.attrs.find((attr) => attr.name === 'equiv');
|
||||
if (nameAttr) {
|
||||
return [new i18n.Placeholder('', nameAttr.value, el.sourceSpan)];
|
||||
}
|
||||
|
||||
this._addError(el, `<${_PLACEHOLDER_TAG}> misses the "equiv" attribute`);
|
||||
break;
|
||||
case _PLACEHOLDER_SPANNING_TAG:
|
||||
const startAttr = el.attrs.find((attr) => attr.name === 'equivStart');
|
||||
const endAttr = el.attrs.find((attr) => attr.name === 'equivEnd');
|
||||
|
||||
if (!startAttr) {
|
||||
this._addError(el, `<${_PLACEHOLDER_TAG}> misses the "equivStart" attribute`);
|
||||
} else if (!endAttr) {
|
||||
this._addError(el, `<${_PLACEHOLDER_TAG}> misses the "equivEnd" attribute`);
|
||||
} else {
|
||||
const startId = startAttr.value;
|
||||
const endId = endAttr.value;
|
||||
|
||||
const nodes: i18n.Node[] = [];
|
||||
|
||||
return nodes.concat(
|
||||
new i18n.Placeholder('', startId, el.sourceSpan),
|
||||
...el.children.map(node => node.visit(this, null)),
|
||||
new i18n.Placeholder('', endId, el.sourceSpan));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
this._addError(el, `Unexpected tag`);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
visitExpansion(icu: ml.Expansion, context: any) {
|
||||
const caseMap: {[value: string]: i18n.Node} = {};
|
||||
|
||||
ml.visitAll(this, icu.cases).forEach((c: any) => {
|
||||
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: [].concat(...ml.visitAll(this, icuCase.expression)),
|
||||
};
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
function getTypeForTag(tag: string): string {
|
||||
switch (tag.toLowerCase()) {
|
||||
case 'br':
|
||||
case 'b':
|
||||
case 'i':
|
||||
case 'u':
|
||||
return 'fmt';
|
||||
case 'img':
|
||||
return 'image';
|
||||
case 'a':
|
||||
return 'link';
|
||||
default:
|
||||
return 'other';
|
||||
}
|
||||
}
|
@ -529,6 +529,7 @@ export class CompileMetadataResolver {
|
||||
syntaxError(
|
||||
`Can't export ${this._getTypeDescriptor(exportedId.reference)} ${stringifyType(exportedId.reference)} from ${stringifyType(moduleType)} as it was neither declared nor imported!`),
|
||||
moduleType);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
@ -627,6 +628,7 @@ export class CompileMetadataResolver {
|
||||
`Please consider moving ${stringifyType(type)} to a higher module that imports ${stringifyType(oldModule)} and ${stringifyType(moduleType)}. ` +
|
||||
`You can also create a new NgModule that exports and includes ${stringifyType(type)} then import that NgModule in ${stringifyType(oldModule)} and ${stringifyType(moduleType)}.`),
|
||||
moduleType);
|
||||
return;
|
||||
}
|
||||
this._ngModuleOfTypes.set(type, moduleType);
|
||||
}
|
||||
@ -860,6 +862,7 @@ export class CompileMetadataResolver {
|
||||
} else if (provider === void 0) {
|
||||
this._reportError(syntaxError(
|
||||
`Encountered undefined provider! Usually this means you have a circular dependencies (might be caused by using 'barrel' index.ts files.`));
|
||||
return;
|
||||
} else {
|
||||
const providersInfo =
|
||||
(<string[]>providers.reduce(
|
||||
@ -879,6 +882,7 @@ export class CompileMetadataResolver {
|
||||
syntaxError(
|
||||
`Invalid ${debugInfo ? debugInfo : 'provider'} - only instances of Provider and Type are allowed, got: [${providersInfo}]`),
|
||||
type);
|
||||
return;
|
||||
}
|
||||
if (providerMeta.token === resolveIdentifier(Identifiers.ANALYZE_FOR_ENTRY_COMPONENTS)) {
|
||||
targetEntryComponents.push(...this._getEntryComponentsFromProvider(providerMeta, type));
|
||||
@ -933,14 +937,12 @@ export class CompileMetadataResolver {
|
||||
const dirMeta = this.getNonNormalizedDirectiveMetadata(dirType);
|
||||
if (dirMeta && dirMeta.metadata.isComponent) {
|
||||
return {componentType: dirType, componentFactory: dirMeta.metadata.componentFactory};
|
||||
} else {
|
||||
const dirSummary =
|
||||
<cpl.CompileDirectiveSummary>this._loadSummary(dirType, cpl.CompileSummaryKind.Directive);
|
||||
if (dirSummary && dirSummary.isComponent) {
|
||||
return {componentType: dirType, componentFactory: dirSummary.componentFactory};
|
||||
}
|
||||
}
|
||||
|
||||
const dirSummary =
|
||||
<cpl.CompileDirectiveSummary>this._loadSummary(dirType, cpl.CompileSummaryKind.Directive);
|
||||
if (dirSummary && dirSummary.isComponent) {
|
||||
return {componentType: dirType, componentFactory: dirSummary.componentFactory};
|
||||
}
|
||||
if (throwIfNotFound) {
|
||||
throw syntaxError(`${dirType.name} cannot be used as an entry component.`);
|
||||
}
|
||||
@ -1004,8 +1006,10 @@ export class CompileMetadataResolver {
|
||||
syntaxError(
|
||||
`Can't construct a query for the property "${propertyName}" of "${stringifyType(typeOrFunc)}" since the query selector wasn't defined.`),
|
||||
typeOrFunc);
|
||||
selectors = [];
|
||||
} else {
|
||||
selectors = [this._getTokenMetadata(q.selector)];
|
||||
}
|
||||
selectors = [this._getTokenMetadata(q.selector)];
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -140,13 +140,13 @@ export class TemplateParser {
|
||||
return this.tryParseHtml(
|
||||
this.expandHtml(this._htmlParser.parse(
|
||||
template, templateUrl, true, this.getInterpolationConfig(component))),
|
||||
component, template, directives, pipes, schemas, templateUrl);
|
||||
component, directives, pipes, schemas);
|
||||
}
|
||||
|
||||
tryParseHtml(
|
||||
htmlAstWithErrors: ParseTreeResult, component: CompileDirectiveMetadata, template: string,
|
||||
directives: CompileDirectiveSummary[], pipes: CompilePipeSummary[], schemas: SchemaMetadata[],
|
||||
templateUrl: string): TemplateParseResult {
|
||||
htmlAstWithErrors: ParseTreeResult, component: CompileDirectiveMetadata,
|
||||
directives: CompileDirectiveSummary[], pipes: CompilePipeSummary[],
|
||||
schemas: SchemaMetadata[]): TemplateParseResult {
|
||||
let result: TemplateAst[];
|
||||
const errors = htmlAstWithErrors.errors;
|
||||
const usedPipes: CompilePipeSummary[] = [];
|
||||
|
@ -644,8 +644,8 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver {
|
||||
return EventHandlerVars.event;
|
||||
}
|
||||
let currViewExpr: o.Expression = VIEW_VAR;
|
||||
for (let currBuilder: ViewBuilder = this; currBuilder;
|
||||
currBuilder = currBuilder.parent, currViewExpr = currViewExpr.prop('parent')) {
|
||||
for (let currBuilder: ViewBuilder = this; currBuilder; currBuilder = currBuilder.parent,
|
||||
currViewExpr = currViewExpr.prop('parent').cast(o.DYNAMIC_TYPE)) {
|
||||
// check references
|
||||
const refNodeIndex = currBuilder.refNodeIndices[name];
|
||||
if (refNodeIndex != null) {
|
||||
@ -717,7 +717,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver {
|
||||
let compBuilder: ViewBuilder = this;
|
||||
while (compBuilder.parent) {
|
||||
compBuilder = compBuilder.parent;
|
||||
compViewExpr = compViewExpr.prop('parent');
|
||||
compViewExpr = compViewExpr.prop('parent').cast(o.DYNAMIC_TYPE);
|
||||
}
|
||||
const pipeNodeIndex = compBuilder.purePipeNodeIndices[name];
|
||||
const pipeValueExpr: o.Expression =
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
import {AotCompiler, AotCompilerHost, AotCompilerOptions, GeneratedFile, createAotCompiler} from '@angular/compiler';
|
||||
import {RenderComponentType, ɵReflectionCapabilities as ReflectionCapabilities, ɵreflector as reflector} from '@angular/core';
|
||||
import {NodeFlags} from '@angular/core/src/view/index';
|
||||
import {async} from '@angular/core/testing';
|
||||
import {MetadataBundler, MetadataCollector, ModuleMetadata, privateEntriesToIndex} from '@angular/tsc-wrapped';
|
||||
import * as ts from 'typescript';
|
||||
@ -368,6 +369,293 @@ describe('compiler (unbundled Angular)', () => {
|
||||
|
||||
}));
|
||||
});
|
||||
|
||||
describe('inheritance with summaries', () => {
|
||||
function compileWithSummaries(
|
||||
libInput: MockData, appInput: MockData): Promise<GeneratedFile[]> {
|
||||
const libHost = new MockCompilerHost(['/lib/base.ts'], libInput, angularFiles);
|
||||
const libAotHost = new MockAotCompilerHost(libHost);
|
||||
libAotHost.tsFilesOnly();
|
||||
const appHost = new MockCompilerHost(['/app/main.ts'], appInput, angularFiles);
|
||||
const appAotHost = new MockAotCompilerHost(appHost);
|
||||
appAotHost.tsFilesOnly();
|
||||
return compile(libHost, libAotHost, expectNoDiagnostics, expectNoDiagnosticsAndEmit)
|
||||
.then(() => {
|
||||
libHost.writtenFiles.forEach((value, key) => appHost.writeFile(key, value, false));
|
||||
libHost.overrides.forEach((value, key) => appHost.override(key, value));
|
||||
|
||||
return compile(appHost, appAotHost, expectNoDiagnostics, expectNoDiagnosticsAndEmit);
|
||||
});
|
||||
}
|
||||
|
||||
function compileParentAndChild(
|
||||
{parentClassDecorator, parentModuleDecorator, childClassDecorator, childModuleDecorator}: {
|
||||
parentClassDecorator: string,
|
||||
parentModuleDecorator: string,
|
||||
childClassDecorator: string,
|
||||
childModuleDecorator: string
|
||||
}) {
|
||||
const libInput: MockData = {
|
||||
'lib': {
|
||||
'base.ts': `
|
||||
import {Injectable, Pipe, Directive, Component, NgModule} from '@angular/core';
|
||||
|
||||
${parentClassDecorator}
|
||||
export class Base {}
|
||||
|
||||
${parentModuleDecorator}
|
||||
export class BaseModule {}
|
||||
`
|
||||
}
|
||||
};
|
||||
const appInput: MockData = {
|
||||
'app': {
|
||||
'main.ts': `
|
||||
import {Injectable, Pipe, Directive, Component, NgModule} from '@angular/core';
|
||||
import {Base} from '../lib/base';
|
||||
|
||||
${childClassDecorator}
|
||||
export class Extends extends Base {}
|
||||
|
||||
${childModuleDecorator}
|
||||
export class MyModule {}
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
return compileWithSummaries(libInput, appInput)
|
||||
.then((generatedFiles) => generatedFiles.find(gf => gf.srcFileUrl === '/app/main.ts'));
|
||||
}
|
||||
|
||||
it('should inherit ctor and lifecycle hooks from classes in other compilation units',
|
||||
async(() => {
|
||||
const libInput: MockData = {
|
||||
'lib': {
|
||||
'base.ts': `
|
||||
export class AParam {}
|
||||
|
||||
export class Base {
|
||||
constructor(a: AParam) {}
|
||||
ngOnDestroy() {}
|
||||
}
|
||||
`
|
||||
}
|
||||
};
|
||||
const appInput: MockData = {
|
||||
'app': {
|
||||
'main.ts': `
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
import {Base} from '../lib/base';
|
||||
|
||||
@Component({template: ''})
|
||||
export class Extends extends Base {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [Extends]
|
||||
})
|
||||
export class MyModule {}
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
compileWithSummaries(libInput, appInput).then((generatedFiles) => {
|
||||
const mainNgFactory = generatedFiles.find(gf => gf.srcFileUrl === '/app/main.ts');
|
||||
const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy;
|
||||
expect(mainNgFactory.source)
|
||||
.toContain(`${flags},(null as any),0,import1.Extends,[import2.AParam]`);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should inherit ctor and lifecycle hooks from classes in other compilation units over 2 levels',
|
||||
async(() => {
|
||||
const lib1Input: MockData = {
|
||||
'lib1': {
|
||||
'base.ts': `
|
||||
export class AParam {}
|
||||
|
||||
export class Base {
|
||||
constructor(a: AParam) {}
|
||||
ngOnDestroy() {}
|
||||
}
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
const lib2Input: MockData = {
|
||||
'lib2': {
|
||||
'middle.ts': `
|
||||
import {Base} from '../lib1/base';
|
||||
export class Middle extends Base {}
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const appInput: MockData = {
|
||||
'app': {
|
||||
'main.ts': `
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
import {Middle} from '../lib2/middle';
|
||||
|
||||
@Component({template: ''})
|
||||
export class Extends extends Middle {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [Extends]
|
||||
})
|
||||
export class MyModule {}
|
||||
`
|
||||
}
|
||||
};
|
||||
const lib1Host = new MockCompilerHost(['/lib1/base.ts'], lib1Input, angularFiles);
|
||||
const lib1AotHost = new MockAotCompilerHost(lib1Host);
|
||||
lib1AotHost.tsFilesOnly();
|
||||
const lib2Host = new MockCompilerHost(['/lib2/middle.ts'], lib2Input, angularFiles);
|
||||
const lib2AotHost = new MockAotCompilerHost(lib2Host);
|
||||
lib2AotHost.tsFilesOnly();
|
||||
const appHost = new MockCompilerHost(['/app/main.ts'], appInput, angularFiles);
|
||||
const appAotHost = new MockAotCompilerHost(appHost);
|
||||
appAotHost.tsFilesOnly();
|
||||
compile(lib1Host, lib1AotHost, expectNoDiagnostics, expectNoDiagnosticsAndEmit)
|
||||
.then(() => {
|
||||
lib1Host.writtenFiles.forEach((value, key) => lib2Host.writeFile(key, value, false));
|
||||
lib1Host.overrides.forEach((value, key) => lib2Host.override(key, value));
|
||||
return compile(
|
||||
lib2Host, lib2AotHost, expectNoDiagnostics, expectNoDiagnosticsAndEmit);
|
||||
})
|
||||
.then(() => {
|
||||
lib2Host.writtenFiles.forEach((value, key) => appHost.writeFile(key, value, false));
|
||||
lib2Host.overrides.forEach((value, key) => appHost.override(key, value));
|
||||
return compile(appHost, appAotHost, expectNoDiagnostics, expectNoDiagnosticsAndEmit);
|
||||
})
|
||||
.then((generatedFiles) => {
|
||||
const mainNgFactory = generatedFiles.find(gf => gf.srcFileUrl === '/app/main.ts');
|
||||
const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy;
|
||||
expect(mainNgFactory.source)
|
||||
.toContain(`${flags},(null as any),0,import1.Extends,[import2.AParam_2]`);
|
||||
});
|
||||
}));
|
||||
|
||||
describe('Injectable', () => {
|
||||
it('should allow to inherit', async(() => {
|
||||
compileParentAndChild({
|
||||
parentClassDecorator: '@Injectable()',
|
||||
parentModuleDecorator: '@NgModule({providers: [Base]})',
|
||||
childClassDecorator: '@Injectable()',
|
||||
childModuleDecorator: '@NgModule({providers: [Extends]})',
|
||||
}).then((mainNgFactory) => { expect(mainNgFactory).toBeTruthy(); });
|
||||
}));
|
||||
|
||||
it('should error if the child class has no matching decorator', async(() => {
|
||||
compileParentAndChild({
|
||||
parentClassDecorator: '@Injectable()',
|
||||
parentModuleDecorator: '@NgModule({providers: [Base]})',
|
||||
childClassDecorator: '',
|
||||
childModuleDecorator: '@NgModule({providers: [Extends]})',
|
||||
}).then(fail, (e) => {
|
||||
expect(e.message).toContain(
|
||||
'Class Extends in /app/main.ts extends from a Injectable in another compilation unit without duplicating the decorator. ' +
|
||||
'Please add a Injectable or Pipe or Directive or Component or NgModule decorator to the class.');
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Component', () => {
|
||||
it('should allow to inherit', async(() => {
|
||||
compileParentAndChild({
|
||||
parentClassDecorator: `@Component({template: ''})`,
|
||||
parentModuleDecorator: '@NgModule({declarations: [Base]})',
|
||||
childClassDecorator: `@Component({template: ''})`,
|
||||
childModuleDecorator: '@NgModule({declarations: [Extends]})',
|
||||
}).then((mainNgFactory) => { expect(mainNgFactory).toBeTruthy(); });
|
||||
}));
|
||||
|
||||
it('should error if the child class has no matching decorator', async(() => {
|
||||
compileParentAndChild({
|
||||
parentClassDecorator: `@Component({template: ''})`,
|
||||
parentModuleDecorator: '@NgModule({declarations: [Base]})',
|
||||
childClassDecorator: '',
|
||||
childModuleDecorator: '@NgModule({declarations: [Extends]})',
|
||||
}).then(fail, (e) => {
|
||||
expect(e.message).toContain(
|
||||
'Class Extends in /app/main.ts extends from a Directive in another compilation unit without duplicating the decorator. ' +
|
||||
'Please add a Directive or Component decorator to the class.');
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Directive', () => {
|
||||
it('should allow to inherit', async(() => {
|
||||
compileParentAndChild({
|
||||
parentClassDecorator: `@Directive({selector: '[someDir]'})`,
|
||||
parentModuleDecorator: '@NgModule({declarations: [Base]})',
|
||||
childClassDecorator: `@Directive({selector: '[someDir]'})`,
|
||||
childModuleDecorator: '@NgModule({declarations: [Extends]})',
|
||||
}).then((mainNgFactory) => { expect(mainNgFactory).toBeTruthy(); });
|
||||
}));
|
||||
|
||||
it('should error if the child class has no matching decorator', async(() => {
|
||||
compileParentAndChild({
|
||||
parentClassDecorator: `@Directive({selector: '[someDir]'})`,
|
||||
parentModuleDecorator: '@NgModule({declarations: [Base]})',
|
||||
childClassDecorator: '',
|
||||
childModuleDecorator: '@NgModule({declarations: [Extends]})',
|
||||
}).then(fail, (e) => {
|
||||
expect(e.message).toContain(
|
||||
'Class Extends in /app/main.ts extends from a Directive in another compilation unit without duplicating the decorator. ' +
|
||||
'Please add a Directive or Component decorator to the class.');
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Pipe', () => {
|
||||
it('should allow to inherit', async(() => {
|
||||
compileParentAndChild({
|
||||
parentClassDecorator: `@Pipe({name: 'somePipe'})`,
|
||||
parentModuleDecorator: '@NgModule({declarations: [Base]})',
|
||||
childClassDecorator: `@Pipe({name: 'somePipe'})`,
|
||||
childModuleDecorator: '@NgModule({declarations: [Extends]})',
|
||||
}).then((mainNgFactory) => { expect(mainNgFactory).toBeTruthy(); });
|
||||
}));
|
||||
|
||||
it('should error if the child class has no matching decorator', async(() => {
|
||||
compileParentAndChild({
|
||||
parentClassDecorator: `@Pipe({name: 'somePipe'})`,
|
||||
parentModuleDecorator: '@NgModule({declarations: [Base]})',
|
||||
childClassDecorator: '',
|
||||
childModuleDecorator: '@NgModule({declarations: [Extends]})',
|
||||
}).then(fail, (e) => {
|
||||
expect(e.message).toContain(
|
||||
'Class Extends in /app/main.ts extends from a Pipe in another compilation unit without duplicating the decorator. ' +
|
||||
'Please add a Pipe decorator to the class.');
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('NgModule', () => {
|
||||
it('should allow to inherit', async(() => {
|
||||
compileParentAndChild({
|
||||
parentClassDecorator: `@NgModule()`,
|
||||
parentModuleDecorator: '',
|
||||
childClassDecorator: `@NgModule()`,
|
||||
childModuleDecorator: '',
|
||||
}).then((mainNgFactory) => { expect(mainNgFactory).toBeTruthy(); });
|
||||
}));
|
||||
|
||||
it('should error if the child class has no matching decorator', async(() => {
|
||||
compileParentAndChild({
|
||||
parentClassDecorator: `@NgModule()`,
|
||||
parentModuleDecorator: '',
|
||||
childClassDecorator: '',
|
||||
childModuleDecorator: '',
|
||||
}).then(fail, (e) => {
|
||||
expect(e.message).toContain(
|
||||
'Class Extends in /app/main.ts extends from a NgModule in another compilation unit without duplicating the decorator. ' +
|
||||
'Please add a NgModule decorator to the class.');
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('compiler (bundled Angular)', () => {
|
||||
@ -513,6 +801,11 @@ function expectNoDiagnostics(program: ts.Program) {
|
||||
expectNoDiagnostics(program.getSemanticDiagnostics());
|
||||
}
|
||||
|
||||
function expectNoDiagnosticsAndEmit(program: ts.Program) {
|
||||
expectNoDiagnostics(program);
|
||||
program.emit();
|
||||
}
|
||||
|
||||
function isDTS(fileName: string): boolean {
|
||||
return /\.d\.ts$/.test(fileName);
|
||||
}
|
||||
@ -544,7 +837,7 @@ function summaryCompile(
|
||||
function compile(
|
||||
host: MockCompilerHost, aotHost: AotCompilerHost, preCompile?: (program: ts.Program) => void,
|
||||
postCompile: (program: ts.Program) => void = expectNoDiagnostics,
|
||||
options: AotCompilerOptions = {}) {
|
||||
options: AotCompilerOptions = {}): Promise<GeneratedFile[]> {
|
||||
const scripts = host.scriptNames.slice(0);
|
||||
const program = ts.createProgram(scripts, settings, host);
|
||||
if (preCompile) preCompile(program);
|
||||
|
@ -24,9 +24,10 @@ describe('StaticReflector', () => {
|
||||
errorRecorder?: (error: any, fileName: string) => void, collectorOptions?: CollectorOptions) {
|
||||
const symbolCache = new StaticSymbolCache();
|
||||
host = new MockStaticSymbolResolverHost(testData, collectorOptions);
|
||||
symbolResolver =
|
||||
new StaticSymbolResolver(host, symbolCache, new MockSummaryResolver([]), errorRecorder);
|
||||
reflector = new StaticReflector(symbolResolver, decorators, [], errorRecorder);
|
||||
const summaryResolver = new MockSummaryResolver([]);
|
||||
spyOn(summaryResolver, 'isLibraryFile').and.returnValue(false);
|
||||
symbolResolver = new StaticSymbolResolver(host, symbolCache, summaryResolver, errorRecorder);
|
||||
reflector = new StaticReflector(summaryResolver, symbolResolver, decorators, [], errorRecorder);
|
||||
noContext = reflector.getStaticSymbol('', '');
|
||||
}
|
||||
|
||||
@ -495,6 +496,31 @@ describe('StaticReflector', () => {
|
||||
expect(() => reflector.propMetadata(appComponent)).not.toThrow();
|
||||
});
|
||||
|
||||
it('should not throw with an invalid extends', () => {
|
||||
const data = Object.create(DEFAULT_TEST_DATA);
|
||||
const file = '/tmp/src/invalid-component.ts';
|
||||
data[file] = `
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
function InvalidParent() {
|
||||
return InvalidParent;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'tmp',
|
||||
template: '',
|
||||
})
|
||||
export class BadComponent extends InvalidParent() {
|
||||
|
||||
}
|
||||
`;
|
||||
init(data);
|
||||
const badComponent = reflector.getStaticSymbol(file, 'BadComponent');
|
||||
expect(reflector.propMetadata(badComponent)).toEqual({});
|
||||
expect(reflector.parameters(badComponent)).toEqual([]);
|
||||
expect(reflector.hasLifecycleHook(badComponent, 'onDestroy')).toEqual(false);
|
||||
});
|
||||
|
||||
it('should produce a annotation even if it contains errors', () => {
|
||||
const data = Object.create(DEFAULT_TEST_DATA);
|
||||
const file = '/tmp/src/invalid-component.ts';
|
||||
@ -520,6 +546,30 @@ describe('StaticReflector', () => {
|
||||
expect(annotation.providers).toEqual([1, 2, 3, 4, 5, 6, 7]);
|
||||
});
|
||||
|
||||
it('should ignore unresolved calls', () => {
|
||||
const data = Object.create(DEFAULT_TEST_DATA);
|
||||
const file = '/tmp/src/invalid-component.ts';
|
||||
data[file] = `
|
||||
import {Component} from '@angular/core';
|
||||
import {unknown} from 'unresolved';
|
||||
|
||||
@Component({
|
||||
selector: 'tmp',
|
||||
template: () => {},
|
||||
providers: [triggers()]
|
||||
})
|
||||
export class BadComponent {
|
||||
|
||||
}
|
||||
`;
|
||||
init(data, [], () => {}, {verboseInvalidExpression: true});
|
||||
|
||||
const badComponent = reflector.getStaticSymbol(file, 'BadComponent');
|
||||
const annotations = reflector.annotations(badComponent);
|
||||
const annotation = annotations[0];
|
||||
expect(annotation.providers).toEqual([]);
|
||||
});
|
||||
|
||||
describe('inheritance', () => {
|
||||
class ClassDecorator {
|
||||
constructor(public value: any) {}
|
||||
|
@ -283,6 +283,31 @@ describe('StaticSymbolResolver', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('should only use the arity for classes from libraries without summaries', () => {
|
||||
init({
|
||||
'/test.d.ts': [{
|
||||
'__symbolic': 'module',
|
||||
'version': 3,
|
||||
'metadata': {
|
||||
'AParam': {__symbolic: 'class'},
|
||||
'AClass': {
|
||||
__symbolic: 'class',
|
||||
arity: 1,
|
||||
members: {
|
||||
__ctor__: [{
|
||||
__symbolic: 'constructor',
|
||||
parameters: [symbolCache.get('/test.d.ts', 'AParam')]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
expect(symbolResolver.resolveSymbol(symbolCache.get('/test.d.ts', 'AClass')).metadata)
|
||||
.toEqual({__symbolic: 'class', arity: 1});
|
||||
});
|
||||
|
||||
it('should be able to trace a named export', () => {
|
||||
const symbol = symbolResolver
|
||||
.resolveSymbol(symbolResolver.getSymbolByModule(
|
||||
@ -411,6 +436,9 @@ export class MockStaticSymbolResolverHost implements StaticSymbolResolverHost {
|
||||
}
|
||||
return baseName + '.d.ts';
|
||||
}
|
||||
if (modulePath == 'unresolved') {
|
||||
return undefined;
|
||||
}
|
||||
return '/tmp/' + modulePath + '.d.ts';
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,8 @@ export function main() {
|
||||
metadata: {
|
||||
__symbolic: 'class',
|
||||
members: {'aMethod': {__symbolic: 'function'}},
|
||||
statics: {aStatic: true}
|
||||
statics: {aStatic: true},
|
||||
decorators: ['aDecoratorData']
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -88,8 +89,12 @@ export function main() {
|
||||
});
|
||||
|
||||
expect(summaries[1].symbol).toBe(symbolCache.get('/tmp/some_service.d.ts', 'SomeService'));
|
||||
// serialization should only keep the statics...
|
||||
expect(summaries[1].metadata).toEqual({__symbolic: 'class', statics: {aStatic: true}});
|
||||
// serialization should drop class decorators
|
||||
expect(summaries[1].metadata).toEqual({
|
||||
__symbolic: 'class',
|
||||
members: {aMethod: {__symbolic: 'function'}},
|
||||
statics: {aStatic: true}
|
||||
});
|
||||
expect(summaries[1].type.type.reference)
|
||||
.toBe(symbolCache.get('/tmp/some_service.d.ts', 'SomeService'));
|
||||
});
|
||||
|
@ -177,8 +177,8 @@ export class MockCompilerHost implements ts.CompilerHost {
|
||||
|
||||
private angularSourcePath: string|undefined;
|
||||
private nodeModulesPath: string|undefined;
|
||||
private overrides = new Map<string, string>();
|
||||
private writtenFiles = new Map<string, string>();
|
||||
public overrides = new Map<string, string>();
|
||||
public writtenFiles = new Map<string, string>();
|
||||
private sourceFiles = new Map<string, ts.SourceFile>();
|
||||
private assumeExists = new Set<string>();
|
||||
private traces: string[] = [];
|
||||
|
@ -51,6 +51,16 @@ export function main() {
|
||||
templateUrl: <any>{}
|
||||
})).toThrowError('The templateUrl specified for component SomeComp is not a string');
|
||||
}));
|
||||
it('should throw if both template and templateUrl are defined',
|
||||
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
|
||||
expect(() => normalizer.normalizeTemplate({
|
||||
ngModuleType: null,
|
||||
componentType: SomeComp,
|
||||
moduleUrl: SOME_MODULE_URL,
|
||||
template: '',
|
||||
templateUrl: '',
|
||||
})).toThrowError(`'SomeComp' component cannot define both template and templateUrl`);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('normalizeTemplateSync', () => {
|
||||
|
@ -125,6 +125,12 @@ export function main() {
|
||||
.toEqual([
|
||||
[['[before, <ph name="TEST"> exp //i18n(ph="teSt") </ph>, after]'], 'm', 'd'],
|
||||
]);
|
||||
|
||||
expect(
|
||||
_humanizeMessages('<div i18n=\'m|d\'>before{{ exp //i18n(ph=\'teSt\') }}after</div>'))
|
||||
.toEqual([
|
||||
[[`[before, <ph name="TEST"> exp //i18n(ph='teSt') </ph>, after]`], 'm', 'd'],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
|
336
packages/compiler/test/i18n/serializers/xliff2_spec.ts
Normal file
336
packages/compiler/test/i18n/serializers/xliff2_spec.ts
Normal file
@ -0,0 +1,336 @@
|
||||
/**
|
||||
* @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 {escapeRegExp} from '@angular/compiler/src/util';
|
||||
|
||||
import {serializeNodes} from '../../../src/i18n/digest';
|
||||
import {MessageBundle} from '../../../src/i18n/message_bundle';
|
||||
import {Xliff2} from '../../../src/i18n/serializers/xliff2';
|
||||
import {HtmlParser} from '../../../src/ml_parser/html_parser';
|
||||
import {DEFAULT_INTERPOLATION_CONFIG} from '../../../src/ml_parser/interpolation_config';
|
||||
|
||||
const HTML = `
|
||||
<p i18n-title title="translatable attribute">not translatable</p>
|
||||
<p i18n>translatable element <b>with placeholders</b> {{ interpolation}}</p>
|
||||
<!-- i18n -->{ count, plural, =0 {<p>test</p>}}<!-- /i18n -->
|
||||
<p i18n="m|d@@i">foo</p>
|
||||
<p i18n="nested"><b><u>{{interpolation}} Text</u></b></p>
|
||||
<p i18n="ph names"><br><img src="1.jpg"><img src="2.jpg"></p>
|
||||
<p i18n="empty element">hello <span></span></p>
|
||||
<p i18n="@@baz">{ count, plural, =0 { { sex, select, other {<p>deeply nested</p>}} }}</p>
|
||||
<p i18n>{ count, plural, =0 { { sex, select, other {<p>deeply nested</p>}} }}</p>
|
||||
`;
|
||||
|
||||
const WRITE_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<xliff version="2.0" xmlns="urn:oasis:names:tc:xliff:document:2.0" srcLang="en">
|
||||
<file original="ng.template" id="ngi18n">
|
||||
<unit id="1933478729560469763">
|
||||
<segment>
|
||||
<source>translatable attribute</source>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="7056919470098446707">
|
||||
<segment>
|
||||
<source>translatable element <pc id="0" equivStart="START_BOLD_TEXT" equivEnd="CLOSE_BOLD_TEXT" type="fmt" dispStart="<b>" dispEnd="</b>">with placeholders</pc> <ph id="1" equiv="INTERPOLATION" disp="{{ interpolation}}"/></source>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="2981514368455622387">
|
||||
<segment>
|
||||
<source>{VAR_PLURAL, plural, =0 {<pc id="0" equivStart="START_PARAGRAPH" equivEnd="CLOSE_PARAGRAPH" type="other" dispStart="<p>" dispEnd="</p>">test</pc>} }</source>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="i">
|
||||
<notes>
|
||||
<note category="description">d</note>
|
||||
<note category="meaning">m</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source>foo</source>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="6440235004920703622">
|
||||
<notes>
|
||||
<note category="description">nested</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source><pc id="0" equivStart="START_BOLD_TEXT" equivEnd="CLOSE_BOLD_TEXT" type="fmt" dispStart="<b>" dispEnd="</b>"><pc id="1" equivStart="START_UNDERLINED_TEXT" equivEnd="CLOSE_UNDERLINED_TEXT" type="fmt" dispStart="<u>" dispEnd="</u>"><ph id="2" equiv="INTERPOLATION" disp="{{interpolation}}"/> Text</pc></pc></source>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="8779402634269838862">
|
||||
<notes>
|
||||
<note category="description">ph names</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source><ph id="0" equiv="LINE_BREAK" type="fmt" disp="<br/>"/><ph id="1" equiv="TAG_IMG" type="image" disp="<img/>"/><ph id="2" equiv="TAG_IMG_1" type="image" disp="<img/>"/></source>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="6536355551500405293">
|
||||
<notes>
|
||||
<note category="description">empty element</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source>hello <pc id="0" equivStart="START_TAG_SPAN" equivEnd="CLOSE_TAG_SPAN" type="other" dispStart="<span>" dispEnd="</span>"></pc></source>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="baz">
|
||||
<segment>
|
||||
<source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<pc id="0" equivStart="START_PARAGRAPH" equivEnd="CLOSE_PARAGRAPH" type="other" dispStart="<p>" dispEnd="</p>">deeply nested</pc>} } } }</source>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="2015957479576096115">
|
||||
<segment>
|
||||
<source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<pc id="0" equivStart="START_PARAGRAPH" equivEnd="CLOSE_PARAGRAPH" type="other" dispStart="<p>" dispEnd="</p>">deeply nested</pc>} } } }</source>
|
||||
</segment>
|
||||
</unit>
|
||||
</file>
|
||||
</xliff>
|
||||
`;
|
||||
|
||||
const LOAD_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<xliff version="2.0" xmlns="urn:oasis:names:tc:xliff:document:2.0" srcLang="en" trgLang="fr">
|
||||
<file original="ng.template" id="ngi18n">
|
||||
<unit id="1933478729560469763">
|
||||
<segment>
|
||||
<source>translatable attribute</source>
|
||||
<target>etubirtta elbatalsnart</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="7056919470098446707">
|
||||
<segment>
|
||||
<source>translatable element <pc id="0" equivStart="START_BOLD_TEXT" equivEnd="CLOSE_BOLD_TEXT" type="fmt" dispStart="<b>" dispEnd="</b>">with placeholders</pc> <ph id="1" equiv="INTERPOLATION" disp="{{ interpolation}}"/></source>
|
||||
<target><ph id="1" equiv="INTERPOLATION" disp="{{ interpolation}}"/> <pc id="0" equivStart="START_BOLD_TEXT" equivEnd="CLOSE_BOLD_TEXT" type="fmt" dispStart="<b>" dispEnd="</b>">sredlohecalp htiw</pc> tnemele elbatalsnart</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="2981514368455622387">
|
||||
<segment>
|
||||
<source>{VAR_PLURAL, plural, =0 {<pc id="0" equivStart="START_PARAGRAPH" equivEnd="CLOSE_PARAGRAPH" type="other" dispStart="<p>" dispEnd="</p>">test</pc>} }</source>
|
||||
<target>{VAR_PLURAL, plural, =0 {<pc id="0" equivStart="START_PARAGRAPH" equivEnd="CLOSE_PARAGRAPH" type="other" dispStart="<p>" dispEnd="</p>">TEST</pc>} }</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="i">
|
||||
<notes>
|
||||
<note category="description">d</note>
|
||||
<note category="meaning">m</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source>foo</source>
|
||||
<target>oof</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="6440235004920703622">
|
||||
<notes>
|
||||
<note category="description">nested</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source><pc id="0" equivStart="START_BOLD_TEXT" equivEnd="CLOSE_BOLD_TEXT" type="fmt" dispStart="<b>" dispEnd="</b>"><pc id="1" equivStart="START_UNDERLINED_TEXT" equivEnd="CLOSE_UNDERLINED_TEXT" type="fmt" dispStart="<u>" dispEnd="</u>"><ph id="2" equiv="INTERPOLATION" disp="{{interpolation}}"/> Text</pc></pc></source>
|
||||
<target><pc id="0" equivStart="START_BOLD_TEXT" equivEnd="CLOSE_BOLD_TEXT" type="fmt" dispStart="<b>" dispEnd="</b>"><pc id="1" equivStart="START_UNDERLINED_TEXT" equivEnd="CLOSE_UNDERLINED_TEXT" type="fmt" dispStart="<u>" dispEnd="</u>">txeT <ph id="2" equiv="INTERPOLATION" disp="{{interpolation}}"/></pc></pc></target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="8779402634269838862">
|
||||
<notes>
|
||||
<note category="description">ph names</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source><ph id="0" equiv="LINE_BREAK" type="fmt" disp="<br/>"/><ph id="1" equiv="TAG_IMG" type="image" disp="<img/>"/><ph id="2" equiv="TAG_IMG_1" type="image" disp="<img/>"/></source>
|
||||
<target><ph id="2" equiv="TAG_IMG_1" type="image" disp="<img/>"/><ph id="1" equiv="TAG_IMG" type="image" disp="<img/>"/><ph id="0" equiv="LINE_BREAK" type="fmt" disp="<br/>"/></target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="6536355551500405293">
|
||||
<notes>
|
||||
<note category="description">empty element</note>
|
||||
</notes>
|
||||
<segment>
|
||||
<source>hello <pc id="0" equivStart="START_TAG_SPAN" equivEnd="CLOSE_TAG_SPAN" type="other" dispStart="<span>" dispEnd="</span>"></pc></source>
|
||||
<target><pc id="0" equivStart="START_TAG_SPAN" equivEnd="CLOSE_TAG_SPAN" type="other" dispStart="<span>" dispEnd="</span>"></pc> olleh</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="baz">
|
||||
<segment>
|
||||
<source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<pc id="0" equivStart="START_PARAGRAPH" equivEnd="CLOSE_PARAGRAPH" type="other" dispStart="<p>" dispEnd="</p>">deeply nested</pc>} } } }</source>
|
||||
<target>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<pc id="0" equivStart="START_PARAGRAPH" equivEnd="CLOSE_PARAGRAPH" type="other" dispStart="<p>" dispEnd="</p>">profondément imbriqué</pc>} } } }</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="2015957479576096115">
|
||||
<segment>
|
||||
<source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<pc id="0" equivStart="START_PARAGRAPH" equivEnd="CLOSE_PARAGRAPH" type="other" dispStart="<p>" dispEnd="</p>">deeply nested</pc>} } } }</source>
|
||||
<target>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<pc id="0" equivStart="START_PARAGRAPH" equivEnd="CLOSE_PARAGRAPH" type="other" dispStart="<p>" dispEnd="</p>">profondément imbriqué</pc>} } } }</target>
|
||||
</segment>
|
||||
</unit>
|
||||
</file>
|
||||
</xliff>
|
||||
`;
|
||||
|
||||
export function main(): void {
|
||||
const serializer = new Xliff2();
|
||||
|
||||
function toXliff(html: string, locale: string | null = null): string {
|
||||
const catalog = new MessageBundle(new HtmlParser, [], {}, locale);
|
||||
catalog.updateFromTemplate(html, '', DEFAULT_INTERPOLATION_CONFIG);
|
||||
return catalog.write(serializer);
|
||||
}
|
||||
|
||||
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(''));
|
||||
|
||||
return msgMap;
|
||||
}
|
||||
|
||||
describe('XLIFF 2.0 serializer', () => {
|
||||
describe('write', () => {
|
||||
it('should write a valid xliff 2.0 file',
|
||||
() => { expect(toXliff(HTML)).toEqual(WRITE_XLIFF); });
|
||||
it('should write a valid xliff 2.0 file with a source language',
|
||||
() => { expect(toXliff(HTML, 'fr')).toContain('srcLang="fr"'); });
|
||||
});
|
||||
|
||||
describe('load', () => {
|
||||
it('should load XLIFF files', () => {
|
||||
expect(loadAsMap(LOAD_XLIFF)).toEqual({
|
||||
'1933478729560469763': 'etubirtta elbatalsnart',
|
||||
'7056919470098446707':
|
||||
'<ph name="INTERPOLATION"/> <ph name="START_BOLD_TEXT"/>sredlohecalp htiw<ph name="CLOSE_BOLD_TEXT"/> tnemele elbatalsnart',
|
||||
'2981514368455622387':
|
||||
'{VAR_PLURAL, plural, =0 {[<ph name="START_PARAGRAPH"/>, TEST, <ph name="CLOSE_PARAGRAPH"/>]}}',
|
||||
'i': 'oof',
|
||||
'6440235004920703622':
|
||||
'<ph name="START_BOLD_TEXT"/><ph name="START_UNDERLINED_TEXT"/>txeT <ph name="INTERPOLATION"/><ph name="CLOSE_UNDERLINED_TEXT"/><ph name="CLOSE_BOLD_TEXT"/>',
|
||||
'8779402634269838862':
|
||||
'<ph name="TAG_IMG_1"/><ph name="TAG_IMG"/><ph name="LINE_BREAK"/>',
|
||||
'6536355551500405293': '<ph name="START_TAG_SPAN"/><ph name="CLOSE_TAG_SPAN"/> olleh',
|
||||
'baz':
|
||||
'{VAR_PLURAL, plural, =0 {[{VAR_SELECT, select, other {[<ph name="START_PARAGRAPH"/>, profondément imbriqué, <ph name="CLOSE_PARAGRAPH"/>]}}, ]}}',
|
||||
'2015957479576096115':
|
||||
'{VAR_PLURAL, plural, =0 {[{VAR_SELECT, select, other {[<ph name="START_PARAGRAPH"/>, profondément imbriqué, <ph name="CLOSE_PARAGRAPH"/>]}}, ]}}'
|
||||
});
|
||||
});
|
||||
|
||||
it('should return the target locale',
|
||||
() => { expect(serializer.load(LOAD_XLIFF, 'url').locale).toEqual('fr'); });
|
||||
});
|
||||
|
||||
describe('structure errors', () => {
|
||||
it('should throw when a wrong xliff version is used', () => {
|
||||
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>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>`;
|
||||
|
||||
expect(() => {
|
||||
loadAsMap(XLIFF);
|
||||
}).toThrowError(/The XLIFF file version 1.2 is not compatible with XLIFF 2.0 serializer/);
|
||||
});
|
||||
|
||||
it('should throw when an unit has no translation', () => {
|
||||
const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<xliff version="2.0" xmlns="urn:oasis:names:tc:xliff:document:2.0" srcLang="en">
|
||||
<file original="ng.template" id="ngi18n">
|
||||
<unit id="missingtarget">
|
||||
<segment>
|
||||
<source/>
|
||||
</segment>
|
||||
</unit>
|
||||
</file>
|
||||
</xliff>`;
|
||||
|
||||
expect(() => {
|
||||
loadAsMap(XLIFF);
|
||||
}).toThrowError(/Message missingtarget misses a translation/);
|
||||
});
|
||||
|
||||
|
||||
it('should throw when an unit has no id attribute', () => {
|
||||
const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<xliff version="2.0" xmlns="urn:oasis:names:tc:xliff:document:2.0" srcLang="en">
|
||||
<file original="ng.template" id="ngi18n">
|
||||
<unit>
|
||||
<segment>
|
||||
<source/>
|
||||
<target/>
|
||||
</segment>
|
||||
</unit>
|
||||
</file>
|
||||
</xliff>`;
|
||||
|
||||
expect(() => { loadAsMap(XLIFF); }).toThrowError(/<unit> misses the "id" attribute/);
|
||||
});
|
||||
|
||||
it('should throw on duplicate unit id', () => {
|
||||
const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<xliff version="2.0" xmlns="urn:oasis:names:tc:xliff:document:2.0" srcLang="en">
|
||||
<file original="ng.template" id="ngi18n">
|
||||
<unit id="deadbeef">
|
||||
<segment>
|
||||
<source/>
|
||||
<target/>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="deadbeef">
|
||||
<segment>
|
||||
<source/>
|
||||
<target/>
|
||||
</segment>
|
||||
</unit>
|
||||
</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="2.0" xmlns="urn:oasis:names:tc:xliff:document:2.0" srcLang="en">
|
||||
<file original="ng.template" id="ngi18n">
|
||||
<unit id="deadbeef">
|
||||
<segment>
|
||||
<source/>
|
||||
<target><b>msg should contain only ph and pc tags</b></target>
|
||||
</segment>
|
||||
</unit>
|
||||
</file>
|
||||
</xliff>`;
|
||||
|
||||
expect(() => { loadAsMap(XLIFF); })
|
||||
.toThrowError(new RegExp(
|
||||
escapeRegExp(`[ERROR ->]<b>msg should contain only ph and pc tags</b>`)));
|
||||
});
|
||||
|
||||
it('should throw when a placeholder misses an id attribute', () => {
|
||||
const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<xliff version="2.0" xmlns="urn:oasis:names:tc:xliff:document:2.0" srcLang="en">
|
||||
<file original="ng.template" id="ngi18n">
|
||||
<unit id="deadbeef">
|
||||
<segment>
|
||||
<source/>
|
||||
<target><ph/></target>
|
||||
</segment>
|
||||
</unit>
|
||||
</file>
|
||||
</xliff>`;
|
||||
|
||||
expect(() => {
|
||||
loadAsMap(XLIFF);
|
||||
}).toThrowError(new RegExp(escapeRegExp(`<ph> misses the "equiv" attribute`)));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
@ -62,7 +62,7 @@ export interface AnimationStyleMetadata extends AnimationMetadata {
|
||||
*/
|
||||
export interface AnimationAnimateMetadata extends AnimationMetadata {
|
||||
timings: string|number|AnimateTimings;
|
||||
styles: AnimationStyleMetadata|AnimationKeyframesSequenceMetadata;
|
||||
styles: AnimationStyleMetadata|AnimationKeyframesSequenceMetadata|null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -86,8 +86,8 @@ export function trigger(name: string, definitions: AnimationMetadata[]): Animati
|
||||
* @deprecated This symbol has moved. Please Import from @angular/animations instead!
|
||||
*/
|
||||
export function animate(
|
||||
timings: string | number, styles: AnimationStyleMetadata | AnimationKeyframesSequenceMetadata =
|
||||
null): AnimationAnimateMetadata {
|
||||
timings: string | number, styles?: AnimationStyleMetadata |
|
||||
AnimationKeyframesSequenceMetadata): AnimationAnimateMetadata {
|
||||
return _animate(timings, styles);
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,7 @@ export function createPlatform(injector: Injector): PlatformRef {
|
||||
* @experimental APIs related to application bootstrap are currently under review.
|
||||
*/
|
||||
export function createPlatformFactory(
|
||||
parentPlatformFactory: (extraProviders?: Provider[]) => PlatformRef, name: string,
|
||||
parentPlatformFactory: ((extraProviders?: Provider[]) => PlatformRef) | null, name: string,
|
||||
providers: Provider[] = []): (extraProviders?: Provider[]) => PlatformRef {
|
||||
const marker = new InjectionToken(`Platform: ${name}`);
|
||||
return (extraProviders: Provider[] = []) => {
|
||||
@ -153,7 +153,7 @@ export function destroyPlatform(): void {
|
||||
*
|
||||
* @experimental APIs related to application bootstrap are currently under review.
|
||||
*/
|
||||
export function getPlatform(): PlatformRef {
|
||||
export function getPlatform(): PlatformRef|null {
|
||||
return _platform && !_platform.destroyed ? _platform : null;
|
||||
}
|
||||
|
||||
@ -278,10 +278,10 @@ export class PlatformRef_ extends PlatformRef {
|
||||
}
|
||||
|
||||
bootstrapModuleFactory<M>(moduleFactory: NgModuleFactory<M>): Promise<NgModuleRef<M>> {
|
||||
return this._bootstrapModuleFactoryWithZone(moduleFactory, null);
|
||||
return this._bootstrapModuleFactoryWithZone(moduleFactory);
|
||||
}
|
||||
|
||||
private _bootstrapModuleFactoryWithZone<M>(moduleFactory: NgModuleFactory<M>, ngZone: NgZone):
|
||||
private _bootstrapModuleFactoryWithZone<M>(moduleFactory: NgModuleFactory<M>, ngZone?: NgZone):
|
||||
Promise<NgModuleRef<M>> {
|
||||
// Note: We need to create the NgZone _before_ we instantiate the module,
|
||||
// as instantiating the module creates some providers eagerly.
|
||||
@ -299,7 +299,7 @@ export class PlatformRef_ extends PlatformRef {
|
||||
throw new Error('No ErrorHandler. Is platform module (BrowserModule) included?');
|
||||
}
|
||||
moduleRef.onDestroy(() => remove(this._modules, moduleRef));
|
||||
ngZone.onError.subscribe({next: (error: any) => { exceptionHandler.handleError(error); }});
|
||||
ngZone !.onError.subscribe({next: (error: any) => { exceptionHandler.handleError(error); }});
|
||||
return _callAndReportToErrorHandler(exceptionHandler, () => {
|
||||
const initStatus: ApplicationInitStatus = moduleRef.injector.get(ApplicationInitStatus);
|
||||
return initStatus.donePromise.then(() => {
|
||||
@ -312,12 +312,12 @@ export class PlatformRef_ extends PlatformRef {
|
||||
|
||||
bootstrapModule<M>(moduleType: Type<M>, compilerOptions: CompilerOptions|CompilerOptions[] = []):
|
||||
Promise<NgModuleRef<M>> {
|
||||
return this._bootstrapModuleWithZone(moduleType, compilerOptions, null);
|
||||
return this._bootstrapModuleWithZone(moduleType, compilerOptions);
|
||||
}
|
||||
|
||||
private _bootstrapModuleWithZone<M>(
|
||||
moduleType: Type<M>, compilerOptions: CompilerOptions|CompilerOptions[] = [],
|
||||
ngZone: NgZone = null): Promise<NgModuleRef<M>> {
|
||||
ngZone?: NgZone): Promise<NgModuleRef<M>> {
|
||||
const compilerFactory: CompilerFactory = this.injector.get(CompilerFactory);
|
||||
const compiler = compilerFactory.createCompiler(
|
||||
Array.isArray(compilerOptions) ? compilerOptions : [compilerOptions]);
|
||||
@ -500,7 +500,8 @@ export class ApplicationRef_ extends ApplicationRef {
|
||||
if (componentOrFactory instanceof ComponentFactory) {
|
||||
componentFactory = componentOrFactory;
|
||||
} else {
|
||||
componentFactory = this._componentFactoryResolver.resolveComponentFactory(componentOrFactory);
|
||||
componentFactory =
|
||||
this._componentFactoryResolver.resolveComponentFactory(componentOrFactory) !;
|
||||
}
|
||||
this._rootComponentTypes.push(componentFactory.componentType);
|
||||
|
||||
|
@ -15,16 +15,15 @@ import {IterableChangeRecord, IterableChanges, IterableDiffer, IterableDifferFac
|
||||
|
||||
export class DefaultIterableDifferFactory implements IterableDifferFactory {
|
||||
constructor() {}
|
||||
supports(obj: Object): boolean { return isListLikeIterable(obj); }
|
||||
supports(obj: Object|null|undefined): boolean { return isListLikeIterable(obj); }
|
||||
|
||||
create<V>(trackByFn?: TrackByFunction<any>): DefaultIterableDiffer<V>;
|
||||
create<V>(trackByFn?: TrackByFunction<V>): DefaultIterableDiffer<V>;
|
||||
|
||||
/**
|
||||
* @deprecated v4.0.0 - ChangeDetectorRef is not used and is no longer a parameter
|
||||
*/
|
||||
create<V>(
|
||||
cdRefOrTrackBy?: ChangeDetectorRef|TrackByFunction<any>,
|
||||
trackByFn?: TrackByFunction<any>): DefaultIterableDiffer<V> {
|
||||
create<V>(cdRefOrTrackBy?: ChangeDetectorRef|TrackByFunction<V>, trackByFn?: TrackByFunction<V>):
|
||||
DefaultIterableDiffer<V> {
|
||||
return new DefaultIterableDiffer<V>(trackByFn || <TrackByFunction<any>>cdRefOrTrackBy);
|
||||
}
|
||||
}
|
||||
@ -35,27 +34,28 @@ const trackByIdentity = (index: number, item: any) => item;
|
||||
* @deprecated v4.0.0 - Should not be part of public API.
|
||||
*/
|
||||
export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChanges<V> {
|
||||
private _length: number = null;
|
||||
private _collection: NgIterable<V> = null;
|
||||
private _length: number = 0;
|
||||
private _collection: NgIterable<V>|null = null;
|
||||
// Keeps track of the used records at any point in time (during & across `_check()` calls)
|
||||
private _linkedRecords: _DuplicateMap<V> = null;
|
||||
private _linkedRecords: _DuplicateMap<V>|null = null;
|
||||
// Keeps track of the removed records at any point in time during `_check()` calls.
|
||||
private _unlinkedRecords: _DuplicateMap<V> = null;
|
||||
private _previousItHead: IterableChangeRecord_<V> = null;
|
||||
private _itHead: IterableChangeRecord_<V> = null;
|
||||
private _itTail: IterableChangeRecord_<V> = null;
|
||||
private _additionsHead: IterableChangeRecord_<V> = null;
|
||||
private _additionsTail: IterableChangeRecord_<V> = null;
|
||||
private _movesHead: IterableChangeRecord_<V> = null;
|
||||
private _movesTail: IterableChangeRecord_<V> = null;
|
||||
private _removalsHead: IterableChangeRecord_<V> = null;
|
||||
private _removalsTail: IterableChangeRecord_<V> = null;
|
||||
private _unlinkedRecords: _DuplicateMap<V>|null = null;
|
||||
private _previousItHead: IterableChangeRecord_<V>|null = null;
|
||||
private _itHead: IterableChangeRecord_<V>|null = null;
|
||||
private _itTail: IterableChangeRecord_<V>|null = null;
|
||||
private _additionsHead: IterableChangeRecord_<V>|null = null;
|
||||
private _additionsTail: IterableChangeRecord_<V>|null = null;
|
||||
private _movesHead: IterableChangeRecord_<V>|null = null;
|
||||
private _movesTail: IterableChangeRecord_<V>|null = null;
|
||||
private _removalsHead: IterableChangeRecord_<V>|null = null;
|
||||
private _removalsTail: IterableChangeRecord_<V>|null = null;
|
||||
// Keeps track of records where custom track by is the same, but item identity has changed
|
||||
private _identityChangesHead: IterableChangeRecord_<V> = null;
|
||||
private _identityChangesTail: IterableChangeRecord_<V> = null;
|
||||
private _identityChangesHead: IterableChangeRecord_<V>|null = null;
|
||||
private _identityChangesTail: IterableChangeRecord_<V>|null = null;
|
||||
private _trackByFn: TrackByFunction<V>
|
||||
|
||||
constructor(private _trackByFn?: TrackByFunction<V>) {
|
||||
this._trackByFn = this._trackByFn || trackByIdentity;
|
||||
constructor(trackByFn?: TrackByFunction<V>) {
|
||||
this._trackByFn = trackByFn || trackByIdentity;
|
||||
}
|
||||
|
||||
get collection() { return this._collection; }
|
||||
@ -63,25 +63,27 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
|
||||
get length(): number { return this._length; }
|
||||
|
||||
forEachItem(fn: (record: IterableChangeRecord_<V>) => void) {
|
||||
let record: IterableChangeRecord_<V>;
|
||||
let record: IterableChangeRecord_<V>|null;
|
||||
for (record = this._itHead; record !== null; record = record._next) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachOperation(
|
||||
fn: (item: IterableChangeRecord_<V>, previousIndex: number, currentIndex: number) => void) {
|
||||
fn: (item: IterableChangeRecord<V>, previousIndex: number|null, currentIndex: number|null) =>
|
||||
void) {
|
||||
let nextIt = this._itHead;
|
||||
let nextRemove = this._removalsHead;
|
||||
let addRemoveOffset = 0;
|
||||
let moveOffsets: number[] = null;
|
||||
let moveOffsets: number[]|null = null;
|
||||
while (nextIt || nextRemove) {
|
||||
// Figure out which is the next record to process
|
||||
// Order: remove, add, move
|
||||
const record = !nextRemove ||
|
||||
const record: IterableChangeRecord<V> = !nextRemove ||
|
||||
nextIt &&
|
||||
nextIt.currentIndex < getPreviousIndex(nextRemove, addRemoveOffset, moveOffsets) ?
|
||||
nextIt :
|
||||
nextIt.currentIndex ! <
|
||||
getPreviousIndex(nextRemove, addRemoveOffset, moveOffsets) ?
|
||||
nextIt ! :
|
||||
nextRemove;
|
||||
const adjPreviousIndex = getPreviousIndex(record, addRemoveOffset, moveOffsets);
|
||||
const currentIndex = record.currentIndex;
|
||||
@ -91,14 +93,14 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
|
||||
addRemoveOffset--;
|
||||
nextRemove = nextRemove._nextRemoved;
|
||||
} else {
|
||||
nextIt = nextIt._next;
|
||||
nextIt = nextIt !._next;
|
||||
if (record.previousIndex == null) {
|
||||
addRemoveOffset++;
|
||||
} else {
|
||||
// INVARIANT: currentIndex < previousIndex
|
||||
if (!moveOffsets) moveOffsets = [];
|
||||
const localMovePreviousIndex = adjPreviousIndex - addRemoveOffset;
|
||||
const localCurrentIndex = currentIndex - addRemoveOffset;
|
||||
const localCurrentIndex = currentIndex ! - addRemoveOffset;
|
||||
if (localMovePreviousIndex != localCurrentIndex) {
|
||||
for (let i = 0; i < localMovePreviousIndex; i++) {
|
||||
const offset = i < moveOffsets.length ? moveOffsets[i] : (moveOffsets[i] = 0);
|
||||
@ -120,44 +122,45 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
|
||||
}
|
||||
|
||||
forEachPreviousItem(fn: (record: IterableChangeRecord_<V>) => void) {
|
||||
let record: IterableChangeRecord_<V>;
|
||||
let record: IterableChangeRecord_<V>|null;
|
||||
for (record = this._previousItHead; record !== null; record = record._nextPrevious) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachAddedItem(fn: (record: IterableChangeRecord_<V>) => void) {
|
||||
let record: IterableChangeRecord_<V>;
|
||||
let record: IterableChangeRecord_<V>|null;
|
||||
for (record = this._additionsHead; record !== null; record = record._nextAdded) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachMovedItem(fn: (record: IterableChangeRecord_<V>) => void) {
|
||||
let record: IterableChangeRecord_<V>;
|
||||
let record: IterableChangeRecord_<V>|null;
|
||||
for (record = this._movesHead; record !== null; record = record._nextMoved) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachRemovedItem(fn: (record: IterableChangeRecord_<V>) => void) {
|
||||
let record: IterableChangeRecord_<V>;
|
||||
let record: IterableChangeRecord_<V>|null;
|
||||
for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachIdentityChange(fn: (record: IterableChangeRecord_<V>) => void) {
|
||||
let record: IterableChangeRecord_<V>;
|
||||
let record: IterableChangeRecord_<V>|null;
|
||||
for (record = this._identityChangesHead; record !== null; record = record._nextIdentityChange) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
diff(collection: NgIterable<V>): DefaultIterableDiffer<V> {
|
||||
diff(collection: NgIterable<V>): DefaultIterableDiffer<V>|null {
|
||||
if (collection == null) collection = [];
|
||||
if (!isListLikeIterable(collection)) {
|
||||
throw new Error(`Error trying to diff '${collection}'`);
|
||||
throw new Error(
|
||||
`Error trying to diff '${stringify(collection)}'. Only arrays and iterables are allowed`);
|
||||
}
|
||||
|
||||
if (this.check(collection)) {
|
||||
@ -173,7 +176,7 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
|
||||
check(collection: NgIterable<V>): boolean {
|
||||
this._reset();
|
||||
|
||||
let record: IterableChangeRecord_<V> = this._itHead;
|
||||
let record: IterableChangeRecord_<V>|null = this._itHead;
|
||||
let mayBeDirty: boolean = false;
|
||||
let index: number;
|
||||
let item: V;
|
||||
@ -240,8 +243,8 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
|
||||
*/
|
||||
_reset() {
|
||||
if (this.isDirty) {
|
||||
let record: IterableChangeRecord_<V>;
|
||||
let nextRecord: IterableChangeRecord_<V>;
|
||||
let record: IterableChangeRecord_<V>|null;
|
||||
let nextRecord: IterableChangeRecord_<V>|null;
|
||||
|
||||
for (record = this._previousItHead = this._itHead; record !== null; record = record._next) {
|
||||
record._nextPrevious = record._next;
|
||||
@ -275,15 +278,15 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
_mismatch(record: IterableChangeRecord_<V>, item: V, itemTrackBy: any, index: number):
|
||||
_mismatch(record: IterableChangeRecord_<V>|null, item: V, itemTrackBy: any, index: number):
|
||||
IterableChangeRecord_<V> {
|
||||
// The previous record after which we will append the current one.
|
||||
let previousRecord: IterableChangeRecord_<V>;
|
||||
|
||||
if (record === null) {
|
||||
previousRecord = this._itTail;
|
||||
previousRecord = this._itTail !;
|
||||
} else {
|
||||
previousRecord = record._prev;
|
||||
previousRecord = record._prev !;
|
||||
// Remove the record from the collection since we know it does not match the item.
|
||||
this._remove(record);
|
||||
}
|
||||
@ -298,7 +301,7 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
|
||||
this._moveAfter(record, previousRecord, index);
|
||||
} else {
|
||||
// Never seen it, check evicted list.
|
||||
record = this._unlinkedRecords === null ? null : this._unlinkedRecords.get(itemTrackBy);
|
||||
record = this._unlinkedRecords === null ? null : this._unlinkedRecords.get(itemTrackBy, null);
|
||||
if (record !== null) {
|
||||
// It is an item which we have evicted earlier: reinsert it back into the list.
|
||||
// But first we need to check if identity changed, so we can update in view if necessary
|
||||
@ -343,10 +346,10 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
|
||||
*/
|
||||
_verifyReinsertion(record: IterableChangeRecord_<V>, item: V, itemTrackBy: any, index: number):
|
||||
IterableChangeRecord_<V> {
|
||||
let reinsertRecord: IterableChangeRecord_<V> =
|
||||
this._unlinkedRecords === null ? null : this._unlinkedRecords.get(itemTrackBy);
|
||||
let reinsertRecord: IterableChangeRecord_<V>|null =
|
||||
this._unlinkedRecords === null ? null : this._unlinkedRecords.get(itemTrackBy, null);
|
||||
if (reinsertRecord !== null) {
|
||||
record = this._reinsertAfter(reinsertRecord, record._prev, index);
|
||||
record = this._reinsertAfter(reinsertRecord, record._prev !, index);
|
||||
} else if (record.currentIndex != index) {
|
||||
record.currentIndex = index;
|
||||
this._addToMoves(record, index);
|
||||
@ -361,10 +364,10 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
_truncate(record: IterableChangeRecord_<V>) {
|
||||
_truncate(record: IterableChangeRecord_<V>|null) {
|
||||
// Anything after that needs to be removed;
|
||||
while (record !== null) {
|
||||
const nextRecord: IterableChangeRecord_<V> = record._next;
|
||||
const nextRecord: IterableChangeRecord_<V>|null = record._next;
|
||||
this._addToRemovals(this._unlink(record));
|
||||
record = nextRecord;
|
||||
}
|
||||
@ -451,7 +454,8 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
|
||||
// assert(record._next === null);
|
||||
// assert(record._prev === null);
|
||||
|
||||
const next: IterableChangeRecord_<V> = prevRecord === null ? this._itHead : prevRecord._next;
|
||||
const next: IterableChangeRecord_<V>|null =
|
||||
prevRecord === null ? this._itHead : prevRecord._next;
|
||||
// todo(vicb)
|
||||
// assert(next != record);
|
||||
// assert(prevRecord != record);
|
||||
@ -598,29 +602,29 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
|
||||
* @stable
|
||||
*/
|
||||
export class IterableChangeRecord_<V> implements IterableChangeRecord<V> {
|
||||
currentIndex: number = null;
|
||||
previousIndex: number = null;
|
||||
currentIndex: number|null = null;
|
||||
previousIndex: number|null = null;
|
||||
|
||||
/** @internal */
|
||||
_nextPrevious: IterableChangeRecord_<V> = null;
|
||||
_nextPrevious: IterableChangeRecord_<V>|null = null;
|
||||
/** @internal */
|
||||
_prev: IterableChangeRecord_<V> = null;
|
||||
_prev: IterableChangeRecord_<V>|null = null;
|
||||
/** @internal */
|
||||
_next: IterableChangeRecord_<V> = null;
|
||||
_next: IterableChangeRecord_<V>|null = null;
|
||||
/** @internal */
|
||||
_prevDup: IterableChangeRecord_<V> = null;
|
||||
_prevDup: IterableChangeRecord_<V>|null = null;
|
||||
/** @internal */
|
||||
_nextDup: IterableChangeRecord_<V> = null;
|
||||
_nextDup: IterableChangeRecord_<V>|null = null;
|
||||
/** @internal */
|
||||
_prevRemoved: IterableChangeRecord_<V> = null;
|
||||
_prevRemoved: IterableChangeRecord_<V>|null = null;
|
||||
/** @internal */
|
||||
_nextRemoved: IterableChangeRecord_<V> = null;
|
||||
_nextRemoved: IterableChangeRecord_<V>|null = null;
|
||||
/** @internal */
|
||||
_nextAdded: IterableChangeRecord_<V> = null;
|
||||
_nextAdded: IterableChangeRecord_<V>|null = null;
|
||||
/** @internal */
|
||||
_nextMoved: IterableChangeRecord_<V> = null;
|
||||
_nextMoved: IterableChangeRecord_<V>|null = null;
|
||||
/** @internal */
|
||||
_nextIdentityChange: IterableChangeRecord_<V> = null;
|
||||
_nextIdentityChange: IterableChangeRecord_<V>|null = null;
|
||||
|
||||
|
||||
constructor(public item: V, public trackById: any) {}
|
||||
@ -635,9 +639,9 @@ export class IterableChangeRecord_<V> implements IterableChangeRecord<V> {
|
||||
// A linked list of CollectionChangeRecords with the same IterableChangeRecord_.item
|
||||
class _DuplicateItemRecordList<V> {
|
||||
/** @internal */
|
||||
_head: IterableChangeRecord_<V> = null;
|
||||
_head: IterableChangeRecord_<V>|null = null;
|
||||
/** @internal */
|
||||
_tail: IterableChangeRecord_<V> = null;
|
||||
_tail: IterableChangeRecord_<V>|null = null;
|
||||
|
||||
/**
|
||||
* Append the record to the list of duplicates.
|
||||
@ -653,7 +657,7 @@ class _DuplicateItemRecordList<V> {
|
||||
// todo(vicb)
|
||||
// assert(record.item == _head.item ||
|
||||
// record.item is num && record.item.isNaN && _head.item is num && _head.item.isNaN);
|
||||
this._tail._nextDup = record;
|
||||
this._tail !._nextDup = record;
|
||||
record._prevDup = this._tail;
|
||||
record._nextDup = null;
|
||||
this._tail = record;
|
||||
@ -662,8 +666,8 @@ class _DuplicateItemRecordList<V> {
|
||||
|
||||
// Returns a IterableChangeRecord_ having IterableChangeRecord_.trackById == trackById and
|
||||
// IterableChangeRecord_.currentIndex >= afterIndex
|
||||
get(trackById: any, afterIndex: number): IterableChangeRecord_<V> {
|
||||
let record: IterableChangeRecord_<V>;
|
||||
get(trackById: any, afterIndex: number|null): IterableChangeRecord_<V>|null {
|
||||
let record: IterableChangeRecord_<V>|null;
|
||||
for (record = this._head; record !== null; record = record._nextDup) {
|
||||
if ((afterIndex === null || afterIndex < record.currentIndex) &&
|
||||
looseIdentical(record.trackById, trackById)) {
|
||||
@ -688,8 +692,8 @@ class _DuplicateItemRecordList<V> {
|
||||
// return false;
|
||||
//});
|
||||
|
||||
const prev: IterableChangeRecord_<V> = record._prevDup;
|
||||
const next: IterableChangeRecord_<V> = record._nextDup;
|
||||
const prev: IterableChangeRecord_<V>|null = record._prevDup;
|
||||
const next: IterableChangeRecord_<V>|null = record._nextDup;
|
||||
if (prev === null) {
|
||||
this._head = next;
|
||||
} else {
|
||||
@ -725,7 +729,7 @@ class _DuplicateMap<V> {
|
||||
* Use case: `[a, b, c, a, a]` if we are at index `3` which is the second `a` then asking if we
|
||||
* have any more `a`s needs to return the last `a` not the first or second.
|
||||
*/
|
||||
get(trackById: any, afterIndex: number = null): IterableChangeRecord_<V> {
|
||||
get(trackById: any, afterIndex: number|null): IterableChangeRecord_<V>|null {
|
||||
const key = trackById;
|
||||
const recordList = this.map.get(key);
|
||||
return recordList ? recordList.get(trackById, afterIndex) : null;
|
||||
@ -738,7 +742,7 @@ class _DuplicateMap<V> {
|
||||
*/
|
||||
remove(record: IterableChangeRecord_<V>): IterableChangeRecord_<V> {
|
||||
const key = record.trackById;
|
||||
const recordList: _DuplicateItemRecordList<V> = this.map.get(key);
|
||||
const recordList: _DuplicateItemRecordList<V> = this.map.get(key) !;
|
||||
// Remove the list of duplicates when it gets empty
|
||||
if (recordList.remove(record)) {
|
||||
this.map.delete(key);
|
||||
@ -753,7 +757,8 @@ class _DuplicateMap<V> {
|
||||
toString(): string { return '_DuplicateMap(' + stringify(this.map) + ')'; }
|
||||
}
|
||||
|
||||
function getPreviousIndex(item: any, addRemoveOffset: number, moveOffsets: number[]): number {
|
||||
function getPreviousIndex(
|
||||
item: any, addRemoveOffset: number, moveOffsets: number[] | null): number {
|
||||
const previousIndex = item.previousIndex;
|
||||
if (previousIndex === null) return previousIndex;
|
||||
let moveOffset = 0;
|
||||
|
@ -9,7 +9,6 @@
|
||||
import {looseIdentical, stringify} from '../../util';
|
||||
import {isJsObject} from '../change_detection_util';
|
||||
import {ChangeDetectorRef} from '../change_detector_ref';
|
||||
|
||||
import {KeyValueChangeRecord, KeyValueChanges, KeyValueDiffer, KeyValueDifferFactory} from './keyvalue_differs';
|
||||
|
||||
|
||||
@ -28,15 +27,17 @@ export class DefaultKeyValueDifferFactory<K, V> implements KeyValueDifferFactory
|
||||
}
|
||||
|
||||
export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyValueChanges<K, V> {
|
||||
private _records: Map<K, V> = new Map<K, V>();
|
||||
private _mapHead: KeyValueChangeRecord_<K, V> = null;
|
||||
private _previousMapHead: KeyValueChangeRecord_<K, V> = null;
|
||||
private _changesHead: KeyValueChangeRecord_<K, V> = null;
|
||||
private _changesTail: KeyValueChangeRecord_<K, V> = null;
|
||||
private _additionsHead: KeyValueChangeRecord_<K, V> = null;
|
||||
private _additionsTail: KeyValueChangeRecord_<K, V> = null;
|
||||
private _removalsHead: KeyValueChangeRecord_<K, V> = null;
|
||||
private _removalsTail: KeyValueChangeRecord_<K, V> = null;
|
||||
private _records = new Map<K, KeyValueChangeRecord_<K, V>>();
|
||||
private _mapHead: KeyValueChangeRecord_<K, V>|null = null;
|
||||
// _appendAfter is used in the check loop
|
||||
private _appendAfter: KeyValueChangeRecord_<K, V>|null = null;
|
||||
private _previousMapHead: KeyValueChangeRecord_<K, V>|null = null;
|
||||
private _changesHead: KeyValueChangeRecord_<K, V>|null = null;
|
||||
private _changesTail: KeyValueChangeRecord_<K, V>|null = null;
|
||||
private _additionsHead: KeyValueChangeRecord_<K, V>|null = null;
|
||||
private _additionsTail: KeyValueChangeRecord_<K, V>|null = null;
|
||||
private _removalsHead: KeyValueChangeRecord_<K, V>|null = null;
|
||||
private _removalsTail: KeyValueChangeRecord_<K, V>|null = null;
|
||||
|
||||
get isDirty(): boolean {
|
||||
return this._additionsHead !== null || this._changesHead !== null ||
|
||||
@ -44,45 +45,46 @@ export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyVal
|
||||
}
|
||||
|
||||
forEachItem(fn: (r: KeyValueChangeRecord<K, V>) => void) {
|
||||
let record: KeyValueChangeRecord_<K, V>;
|
||||
let record: KeyValueChangeRecord_<K, V>|null;
|
||||
for (record = this._mapHead; record !== null; record = record._next) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachPreviousItem(fn: (r: KeyValueChangeRecord<K, V>) => void) {
|
||||
let record: KeyValueChangeRecord_<K, V>;
|
||||
let record: KeyValueChangeRecord_<K, V>|null;
|
||||
for (record = this._previousMapHead; record !== null; record = record._nextPrevious) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachChangedItem(fn: (r: KeyValueChangeRecord<K, V>) => void) {
|
||||
let record: KeyValueChangeRecord_<K, V>;
|
||||
let record: KeyValueChangeRecord_<K, V>|null;
|
||||
for (record = this._changesHead; record !== null; record = record._nextChanged) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachAddedItem(fn: (r: KeyValueChangeRecord<K, V>) => void) {
|
||||
let record: KeyValueChangeRecord_<K, V>;
|
||||
let record: KeyValueChangeRecord_<K, V>|null;
|
||||
for (record = this._additionsHead; record !== null; record = record._nextAdded) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachRemovedItem(fn: (r: KeyValueChangeRecord<K, V>) => void) {
|
||||
let record: KeyValueChangeRecord_<K, V>;
|
||||
let record: KeyValueChangeRecord_<K, V>|null;
|
||||
for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
diff(map: Map<any, any>|{[k: string]: any}): any {
|
||||
diff(map?: Map<any, any>|{[k: string]: any}|null): any {
|
||||
if (!map) {
|
||||
map = new Map();
|
||||
} else if (!(map instanceof Map || isJsObject(map))) {
|
||||
throw new Error(`Error trying to diff '${map}'`);
|
||||
throw new Error(
|
||||
`Error trying to diff '${stringify(map)}'. Only maps and objects are allowed`);
|
||||
}
|
||||
|
||||
return this.check(map) ? this : null;
|
||||
@ -90,98 +92,144 @@ export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyVal
|
||||
|
||||
onDestroy() {}
|
||||
|
||||
/**
|
||||
* Check the current state of the map vs the previous.
|
||||
* The algorithm is optimised for when the keys do no change.
|
||||
*/
|
||||
check(map: Map<any, any>|{[k: string]: any}): boolean {
|
||||
this._reset();
|
||||
const records = this._records;
|
||||
let oldSeqRecord: KeyValueChangeRecord_<K, V> = this._mapHead;
|
||||
let lastOldSeqRecord: KeyValueChangeRecord_<K, V> = null;
|
||||
let lastNewSeqRecord: KeyValueChangeRecord_<K, V> = null;
|
||||
let seqChanged: boolean = false;
|
||||
|
||||
let insertBefore = this._mapHead;
|
||||
this._appendAfter = null;
|
||||
|
||||
this._forEach(map, (value: any, key: any) => {
|
||||
let newSeqRecord: any;
|
||||
if (oldSeqRecord && key === oldSeqRecord.key) {
|
||||
newSeqRecord = oldSeqRecord;
|
||||
this._maybeAddToChanges(newSeqRecord, value);
|
||||
if (insertBefore && insertBefore.key === key) {
|
||||
this._maybeAddToChanges(insertBefore, value);
|
||||
this._appendAfter = insertBefore;
|
||||
insertBefore = insertBefore._next;
|
||||
} else {
|
||||
seqChanged = true;
|
||||
if (oldSeqRecord !== null) {
|
||||
this._removeFromSeq(lastOldSeqRecord, oldSeqRecord);
|
||||
this._addToRemovals(oldSeqRecord);
|
||||
}
|
||||
if (records.has(key)) {
|
||||
newSeqRecord = records.get(key);
|
||||
this._maybeAddToChanges(newSeqRecord, value);
|
||||
} else {
|
||||
newSeqRecord = new KeyValueChangeRecord_<K, V>(key);
|
||||
records.set(key, newSeqRecord);
|
||||
newSeqRecord.currentValue = value;
|
||||
this._addToAdditions(newSeqRecord);
|
||||
}
|
||||
const record = this._getOrCreateRecordForKey(key, value);
|
||||
insertBefore = this._insertBeforeOrAppend(insertBefore, record);
|
||||
}
|
||||
});
|
||||
|
||||
// Items remaining at the end of the list have been deleted
|
||||
if (insertBefore) {
|
||||
if (insertBefore._prev) {
|
||||
insertBefore._prev._next = null;
|
||||
}
|
||||
|
||||
if (seqChanged) {
|
||||
if (this._isInRemovals(newSeqRecord)) {
|
||||
this._removeFromRemovals(newSeqRecord);
|
||||
}
|
||||
if (lastNewSeqRecord == null) {
|
||||
this._mapHead = newSeqRecord;
|
||||
} else {
|
||||
lastNewSeqRecord._next = newSeqRecord;
|
||||
this._removalsHead = insertBefore;
|
||||
|
||||
for (let record = insertBefore; record !== null; record = record._nextRemoved) {
|
||||
if (record === this._mapHead) {
|
||||
this._mapHead = null;
|
||||
}
|
||||
this._records.delete(record.key);
|
||||
record._nextRemoved = record._next;
|
||||
record.previousValue = record.currentValue;
|
||||
record.currentValue = null;
|
||||
record._prev = null;
|
||||
record._next = null;
|
||||
}
|
||||
lastOldSeqRecord = oldSeqRecord;
|
||||
lastNewSeqRecord = newSeqRecord;
|
||||
oldSeqRecord = oldSeqRecord && oldSeqRecord._next;
|
||||
});
|
||||
this._truncate(lastOldSeqRecord, oldSeqRecord);
|
||||
}
|
||||
|
||||
// Make sure tails have no next records from previous runs
|
||||
if (this._changesTail) this._changesTail._nextChanged = null;
|
||||
if (this._additionsTail) this._additionsTail._nextAdded = null;
|
||||
|
||||
return this.isDirty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a record before `before` or append at the end of the list when `before` is null.
|
||||
*
|
||||
* Notes:
|
||||
* - This method appends at `this._appendAfter`,
|
||||
* - This method updates `this._appendAfter`,
|
||||
* - The return value is the new value for the insertion pointer.
|
||||
*/
|
||||
private _insertBeforeOrAppend(
|
||||
before: KeyValueChangeRecord_<K, V>,
|
||||
record: KeyValueChangeRecord_<K, V>): KeyValueChangeRecord_<K, V> {
|
||||
if (before) {
|
||||
const prev = before._prev;
|
||||
record._next = before;
|
||||
record._prev = prev;
|
||||
before._prev = record;
|
||||
if (prev) {
|
||||
prev._next = record;
|
||||
}
|
||||
if (before === this._mapHead) {
|
||||
this._mapHead = record;
|
||||
}
|
||||
|
||||
this._appendAfter = before;
|
||||
return before;
|
||||
}
|
||||
|
||||
if (this._appendAfter) {
|
||||
this._appendAfter._next = record;
|
||||
record._prev = this._appendAfter;
|
||||
} else {
|
||||
this._mapHead = record;
|
||||
}
|
||||
|
||||
this._appendAfter = record;
|
||||
return null;
|
||||
}
|
||||
|
||||
private _getOrCreateRecordForKey(key: K, value: V): KeyValueChangeRecord_<K, V> {
|
||||
if (this._records.has(key)) {
|
||||
const record = this._records.get(key);
|
||||
this._maybeAddToChanges(record, value);
|
||||
const prev = record._prev;
|
||||
const next = record._next;
|
||||
if (prev) {
|
||||
prev._next = next;
|
||||
}
|
||||
if (next) {
|
||||
next._prev = prev;
|
||||
}
|
||||
record._next = null;
|
||||
record._prev = null;
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
const record = new KeyValueChangeRecord_<K, V>(key);
|
||||
this._records.set(key, record);
|
||||
record.currentValue = value;
|
||||
this._addToAdditions(record);
|
||||
return record;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_reset() {
|
||||
if (this.isDirty) {
|
||||
let record: KeyValueChangeRecord_<K, V>;
|
||||
// Record the state of the mapping
|
||||
for (record = this._previousMapHead = this._mapHead; record !== null; record = record._next) {
|
||||
let record: KeyValueChangeRecord_<K, V>|null;
|
||||
// let `_previousMapHead` contain the state of the map before the changes
|
||||
this._previousMapHead = this._mapHead;
|
||||
for (record = this._previousMapHead; record !== null; record = record._next) {
|
||||
record._nextPrevious = record._next;
|
||||
}
|
||||
|
||||
// Update `record.previousValue` with the value of the item before the changes
|
||||
// We need to update all changed items (that's those which have been added and changed)
|
||||
for (record = this._changesHead; record !== null; record = record._nextChanged) {
|
||||
record.previousValue = record.currentValue;
|
||||
}
|
||||
|
||||
for (record = this._additionsHead; record != null; record = record._nextAdded) {
|
||||
record.previousValue = record.currentValue;
|
||||
}
|
||||
|
||||
this._changesHead = this._changesTail = null;
|
||||
this._additionsHead = this._additionsTail = null;
|
||||
this._removalsHead = this._removalsTail = null;
|
||||
}
|
||||
}
|
||||
|
||||
private _truncate(lastRecord: KeyValueChangeRecord_<K, V>, record: KeyValueChangeRecord_<K, V>) {
|
||||
while (record !== null) {
|
||||
if (lastRecord === null) {
|
||||
this._mapHead = null;
|
||||
} else {
|
||||
lastRecord._next = null;
|
||||
}
|
||||
const nextRecord = record._next;
|
||||
this._addToRemovals(record);
|
||||
lastRecord = record;
|
||||
record = nextRecord;
|
||||
}
|
||||
|
||||
for (let rec: KeyValueChangeRecord_<K, V> = this._removalsHead; rec !== null;
|
||||
rec = rec._nextRemoved) {
|
||||
rec.previousValue = rec.currentValue;
|
||||
rec.currentValue = null;
|
||||
this._records.delete(rec.key);
|
||||
this._removalsHead = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the record or a given key to the list of changes only when the value has actually changed
|
||||
private _maybeAddToChanges(record: KeyValueChangeRecord_<K, V>, newValue: any): void {
|
||||
if (!looseIdentical(newValue, record.currentValue)) {
|
||||
record.previousValue = record.currentValue;
|
||||
@ -190,52 +238,11 @@ export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyVal
|
||||
}
|
||||
}
|
||||
|
||||
private _isInRemovals(record: KeyValueChangeRecord_<K, V>) {
|
||||
return record === this._removalsHead || record._nextRemoved !== null ||
|
||||
record._prevRemoved !== null;
|
||||
}
|
||||
|
||||
private _addToRemovals(record: KeyValueChangeRecord_<K, V>) {
|
||||
if (this._removalsHead === null) {
|
||||
this._removalsHead = this._removalsTail = record;
|
||||
} else {
|
||||
this._removalsTail._nextRemoved = record;
|
||||
record._prevRemoved = this._removalsTail;
|
||||
this._removalsTail = record;
|
||||
}
|
||||
}
|
||||
|
||||
private _removeFromSeq(prev: KeyValueChangeRecord_<K, V>, record: KeyValueChangeRecord_<K, V>) {
|
||||
const next = record._next;
|
||||
if (prev === null) {
|
||||
this._mapHead = next;
|
||||
} else {
|
||||
prev._next = next;
|
||||
}
|
||||
record._next = null;
|
||||
}
|
||||
|
||||
private _removeFromRemovals(record: KeyValueChangeRecord_<K, V>) {
|
||||
const prev = record._prevRemoved;
|
||||
const next = record._nextRemoved;
|
||||
if (prev === null) {
|
||||
this._removalsHead = next;
|
||||
} else {
|
||||
prev._nextRemoved = next;
|
||||
}
|
||||
if (next === null) {
|
||||
this._removalsTail = prev;
|
||||
} else {
|
||||
next._prevRemoved = prev;
|
||||
}
|
||||
record._prevRemoved = record._nextRemoved = null;
|
||||
}
|
||||
|
||||
private _addToAdditions(record: KeyValueChangeRecord_<K, V>) {
|
||||
if (this._additionsHead === null) {
|
||||
this._additionsHead = this._additionsTail = record;
|
||||
} else {
|
||||
this._additionsTail._nextAdded = record;
|
||||
this._additionsTail !._nextAdded = record;
|
||||
this._additionsTail = record;
|
||||
}
|
||||
}
|
||||
@ -244,34 +251,23 @@ export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyVal
|
||||
if (this._changesHead === null) {
|
||||
this._changesHead = this._changesTail = record;
|
||||
} else {
|
||||
this._changesTail._nextChanged = record;
|
||||
this._changesTail !._nextChanged = record;
|
||||
this._changesTail = record;
|
||||
}
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
const items: any[] = [];
|
||||
const previous: any[] = [];
|
||||
const changes: any[] = [];
|
||||
const additions: any[] = [];
|
||||
const removals: any[] = [];
|
||||
let record: KeyValueChangeRecord_<K, V>;
|
||||
const items: string[] = [];
|
||||
const previous: string[] = [];
|
||||
const changes: string[] = [];
|
||||
const additions: string[] = [];
|
||||
const removals: string[] = [];
|
||||
|
||||
for (record = this._mapHead; record !== null; record = record._next) {
|
||||
items.push(stringify(record));
|
||||
}
|
||||
for (record = this._previousMapHead; record !== null; record = record._nextPrevious) {
|
||||
previous.push(stringify(record));
|
||||
}
|
||||
for (record = this._changesHead; record !== null; record = record._nextChanged) {
|
||||
changes.push(stringify(record));
|
||||
}
|
||||
for (record = this._additionsHead; record !== null; record = record._nextAdded) {
|
||||
additions.push(stringify(record));
|
||||
}
|
||||
for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
|
||||
removals.push(stringify(record));
|
||||
}
|
||||
this.forEachItem(r => items.push(stringify(r)));
|
||||
this.forEachPreviousItem(r => previous.push(stringify(r)));
|
||||
this.forEachChangedItem(r => changes.push(stringify(r)));
|
||||
this.forEachAddedItem(r => additions.push(stringify(r)));
|
||||
this.forEachRemovedItem(r => removals.push(stringify(r)));
|
||||
|
||||
return 'map: ' + items.join(', ') + '\n' +
|
||||
'previous: ' + previous.join(', ') + '\n' +
|
||||
@ -295,21 +291,21 @@ export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyVal
|
||||
* @stable
|
||||
*/
|
||||
class KeyValueChangeRecord_<K, V> implements KeyValueChangeRecord<K, V> {
|
||||
previousValue: V = null;
|
||||
currentValue: V = null;
|
||||
previousValue: V|null = null;
|
||||
currentValue: V|null = null;
|
||||
|
||||
/** @internal */
|
||||
_nextPrevious: KeyValueChangeRecord_<K, V> = null;
|
||||
_nextPrevious: KeyValueChangeRecord_<K, V>|null = null;
|
||||
/** @internal */
|
||||
_next: KeyValueChangeRecord_<K, V> = null;
|
||||
_next: KeyValueChangeRecord_<K, V>|null = null;
|
||||
/** @internal */
|
||||
_nextAdded: KeyValueChangeRecord_<K, V> = null;
|
||||
_prev: KeyValueChangeRecord_<K, V>|null = null;
|
||||
/** @internal */
|
||||
_nextRemoved: KeyValueChangeRecord_<K, V> = null;
|
||||
_nextAdded: KeyValueChangeRecord_<K, V>|null = null;
|
||||
/** @internal */
|
||||
_prevRemoved: KeyValueChangeRecord_<K, V> = null;
|
||||
_nextRemoved: KeyValueChangeRecord_<K, V>|null = null;
|
||||
/** @internal */
|
||||
_nextChanged: KeyValueChangeRecord_<K, V> = null;
|
||||
_nextChanged: KeyValueChangeRecord_<K, V>|null = null;
|
||||
|
||||
constructor(public key: K) {}
|
||||
|
||||
|
@ -30,7 +30,7 @@ export interface IterableDiffer<V> {
|
||||
* @returns an object describing the difference. The return value is only valid until the next
|
||||
* `diff()` invocation.
|
||||
*/
|
||||
diff(object: NgIterable<V>): IterableChanges<V>;
|
||||
diff(object: NgIterable<V>): IterableChanges<V>|null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -92,20 +92,16 @@ export interface IterableChanges<V> {
|
||||
*/
|
||||
export interface IterableChangeRecord<V> {
|
||||
/** Current index of the item in `Iterable` or null if removed. */
|
||||
// TODO(TS2.1): make readonly once we move to TS v2.1
|
||||
/* readonly */ currentIndex: number;
|
||||
readonly currentIndex: number|null;
|
||||
|
||||
/** Previous index of the item in `Iterable` or null if added. */
|
||||
// TODO(TS2.1): make readonly once we move to TS v2.1
|
||||
/* readonly */ previousIndex: number;
|
||||
readonly previousIndex: number|null;
|
||||
|
||||
/** The item. */
|
||||
// TODO(TS2.1): make readonly once we move to TS v2.1
|
||||
/* readonly */ item: V;
|
||||
readonly item: V;
|
||||
|
||||
/** Track by identity as computed by the `trackByFn`. */
|
||||
// TODO(TS2.1): make readonly once we move to TS v2.1
|
||||
/* readonly */ trackById: any;
|
||||
readonly trackById: any;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -82,17 +82,17 @@ export interface KeyValueChangeRecord<K, V> {
|
||||
/**
|
||||
* Current key in the Map.
|
||||
*/
|
||||
/* readonly */ key: K;
|
||||
readonly key: K;
|
||||
|
||||
/**
|
||||
* Current value for the key or `undefined` if removed.
|
||||
* Current value for the key or `null` if removed.
|
||||
*/
|
||||
/* readonly */ currentValue: V;
|
||||
readonly currentValue: V|null;
|
||||
|
||||
/**
|
||||
* Previous value for the key or `undefined` if added.
|
||||
* Previous value for the key or `null` if added.
|
||||
*/
|
||||
/* readonly */ previousValue: V;
|
||||
readonly previousValue: V|null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,5 +25,5 @@ export {GetterFn as ɵGetterFn, MethodFn as ɵMethodFn, SetterFn as ɵSetterFn}
|
||||
export {DirectRenderer as ɵDirectRenderer, RenderDebugInfo as ɵRenderDebugInfo} from './render/api';
|
||||
export {global as ɵglobal, looseIdentical as ɵlooseIdentical, stringify as ɵstringify} from './util';
|
||||
export {makeDecorator as ɵmakeDecorator} from './util/decorators';
|
||||
export {isObservable as ɵisObservable, isPromise as ɵisPromise, merge as ɵmerge} from './util/lang';
|
||||
export {isObservable as ɵisObservable, isPromise as ɵisPromise} from './util/lang';
|
||||
export {NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR as ɵNOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from './view/provider';
|
||||
|
@ -17,9 +17,9 @@ export class EventListener { constructor(public name: string, public callback: F
|
||||
export class DebugNode {
|
||||
nativeNode: any;
|
||||
listeners: EventListener[];
|
||||
parent: DebugElement;
|
||||
parent: DebugElement|null;
|
||||
|
||||
constructor(nativeNode: any, parent: DebugNode, private _debugContext: DebugContext) {
|
||||
constructor(nativeNode: any, parent: DebugNode|null, private _debugContext: DebugContext) {
|
||||
this.nativeNode = nativeNode;
|
||||
if (parent && parent instanceof DebugElement) {
|
||||
parent.addChild(this);
|
||||
@ -29,19 +29,15 @@ export class DebugNode {
|
||||
this.listeners = [];
|
||||
}
|
||||
|
||||
get injector(): Injector { return this._debugContext ? this._debugContext.injector : null; }
|
||||
get injector(): Injector { return this._debugContext.injector; }
|
||||
|
||||
get componentInstance(): any { return this._debugContext ? this._debugContext.component : null; }
|
||||
get componentInstance(): any { return this._debugContext.component; }
|
||||
|
||||
get context(): any { return this._debugContext ? this._debugContext.context : null; }
|
||||
get context(): any { return this._debugContext.context; }
|
||||
|
||||
get references(): {[key: string]: any} {
|
||||
return this._debugContext ? this._debugContext.references : null;
|
||||
}
|
||||
get references(): {[key: string]: any} { return this._debugContext.references; }
|
||||
|
||||
get providerTokens(): any[] {
|
||||
return this._debugContext ? this._debugContext.providerTokens : null;
|
||||
}
|
||||
get providerTokens(): any[] { return this._debugContext.providerTokens; }
|
||||
|
||||
/**
|
||||
* @deprecated since v4
|
||||
@ -55,9 +51,9 @@ export class DebugNode {
|
||||
export class DebugElement extends DebugNode {
|
||||
name: string;
|
||||
properties: {[key: string]: any};
|
||||
attributes: {[key: string]: string};
|
||||
attributes: {[key: string]: string | null};
|
||||
classes: {[key: string]: boolean};
|
||||
styles: {[key: string]: string};
|
||||
styles: {[key: string]: string | null};
|
||||
childNodes: DebugNode[];
|
||||
nativeElement: any;
|
||||
|
||||
@ -181,8 +177,8 @@ const _nativeNodeToDebugNode = new Map<any, DebugNode>();
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
export function getDebugNode(nativeNode: any): DebugNode {
|
||||
return _nativeNodeToDebugNode.get(nativeNode);
|
||||
export function getDebugNode(nativeNode: any): DebugNode|null {
|
||||
return _nativeNodeToDebugNode.get(nativeNode) || null;
|
||||
}
|
||||
|
||||
export function getAllDebugNodes(): DebugNode[] {
|
||||
@ -203,4 +199,4 @@ export function removeDebugNodeFromIndex(node: DebugNode) {
|
||||
*
|
||||
* @experimental All debugging apis are currently experimental.
|
||||
*/
|
||||
export interface Predicate<T> { (value: T, index?: number, array?: T[]): boolean; }
|
||||
export interface Predicate<T> { (value: T): boolean; }
|
||||
|
@ -42,8 +42,8 @@ export class OpaqueToken {
|
||||
* runtime representation) such as when injecting an interface, callable type, array or
|
||||
* parametrized type.
|
||||
*
|
||||
* `InjectionToken` is parametrize on `T` which is the type of object which will be returned by the
|
||||
* `Injector`. This provides additional level of type safety.
|
||||
* `InjectionToken` is parameterized on `T` which is the type of object which will be returned by
|
||||
* the `Injector`. This provides additional level of type safety.
|
||||
*
|
||||
* ```
|
||||
* interface MyInterface {...}
|
||||
@ -53,7 +53,7 @@ export class OpaqueToken {
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/injector_spec.ts region='Injector'}
|
||||
* {@example core/di/ts/injector_spec.ts region='InjectionToken'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
|
@ -56,7 +56,7 @@ export abstract class Injector {
|
||||
*/
|
||||
abstract get<T>(token: Type<T>|InjectionToken<T>, notFoundValue?: T): T;
|
||||
/**
|
||||
* @deprecated from v4.0.0 use Type<T> or InjectToken<T>
|
||||
* @deprecated from v4.0.0 use Type<T> or InjectionToken<T>
|
||||
* @suppress {duplicate}
|
||||
*/
|
||||
abstract get(token: any, notFoundValue?: any): any;
|
||||
|
@ -115,7 +115,7 @@ export abstract class ReflectiveInjector implements Injector {
|
||||
* because it needs to resolve the passed-in providers first.
|
||||
* See {@link Injector#resolve} and {@link Injector#fromResolvedProviders}.
|
||||
*/
|
||||
static resolveAndCreate(providers: Provider[], parent: Injector = null): ReflectiveInjector {
|
||||
static resolveAndCreate(providers: Provider[], parent?: Injector): ReflectiveInjector {
|
||||
const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
|
||||
return ReflectiveInjector.fromResolvedProviders(ResolvedReflectiveProviders, parent);
|
||||
}
|
||||
@ -143,7 +143,7 @@ export abstract class ReflectiveInjector implements Injector {
|
||||
* ```
|
||||
* @experimental
|
||||
*/
|
||||
static fromResolvedProviders(providers: ResolvedReflectiveProvider[], parent: Injector = null):
|
||||
static fromResolvedProviders(providers: ResolvedReflectiveProvider[], parent?: Injector):
|
||||
ReflectiveInjector {
|
||||
return new ReflectiveInjector_(providers, parent);
|
||||
}
|
||||
@ -163,7 +163,7 @@ export abstract class ReflectiveInjector implements Injector {
|
||||
* expect(child.parent).toBe(parent);
|
||||
* ```
|
||||
*/
|
||||
abstract get parent(): Injector;
|
||||
abstract get parent(): Injector|null;
|
||||
|
||||
/**
|
||||
* Resolves an array of providers and creates a child injector from those providers.
|
||||
@ -282,16 +282,16 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
|
||||
/** @internal */
|
||||
public _providers: ResolvedReflectiveProvider[];
|
||||
/** @internal */
|
||||
public _parent: Injector;
|
||||
public _parent: Injector|null;
|
||||
|
||||
keyIds: number[];
|
||||
objs: any[];
|
||||
/**
|
||||
* Private
|
||||
*/
|
||||
constructor(_providers: ResolvedReflectiveProvider[], _parent: Injector = null) {
|
||||
constructor(_providers: ResolvedReflectiveProvider[], _parent?: Injector) {
|
||||
this._providers = _providers;
|
||||
this._parent = _parent;
|
||||
this._parent = _parent || null;
|
||||
|
||||
const len = _providers.length;
|
||||
|
||||
@ -308,7 +308,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
|
||||
return this._getByKey(ReflectiveKey.get(token), null, notFoundValue);
|
||||
}
|
||||
|
||||
get parent(): Injector { return this._parent; }
|
||||
get parent(): Injector|null { return this._parent; }
|
||||
|
||||
resolveAndCreateChild(providers: Provider[]): ReflectiveInjector {
|
||||
const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
|
||||
@ -388,7 +388,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
|
||||
return this._getByKey(dep.key, dep.visibility, dep.optional ? null : THROW_IF_NOT_FOUND);
|
||||
}
|
||||
|
||||
private _getByKey(key: ReflectiveKey, visibility: Self|SkipSelf, notFoundValue: any): any {
|
||||
private _getByKey(key: ReflectiveKey, visibility: Self|SkipSelf|null, notFoundValue: any): any {
|
||||
if (key === INJECTOR_KEY) {
|
||||
return this;
|
||||
}
|
||||
@ -431,8 +431,8 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_getByKeyDefault(key: ReflectiveKey, notFoundValue: any, visibility: Self|SkipSelf): any {
|
||||
let inj: Injector;
|
||||
_getByKeyDefault(key: ReflectiveKey, notFoundValue: any, visibility: Self|SkipSelf|null): any {
|
||||
let inj: Injector|null;
|
||||
|
||||
if (visibility instanceof SkipSelf) {
|
||||
inj = this._parent;
|
||||
|
@ -64,7 +64,7 @@ export class KeyRegistry {
|
||||
if (token instanceof ReflectiveKey) return token;
|
||||
|
||||
if (this._allKeys.has(token)) {
|
||||
return this._allKeys.get(token);
|
||||
return this._allKeys.get(token) !;
|
||||
}
|
||||
|
||||
const newKey = new ReflectiveKey(token, ReflectiveKey.numberOfKeys);
|
||||
|
@ -26,7 +26,7 @@ interface NormalizedProvider extends TypeProvider, ValueProvider, ClassProvider,
|
||||
*/
|
||||
export class ReflectiveDependency {
|
||||
constructor(
|
||||
public key: ReflectiveKey, public optional: boolean, public visibility: Self|SkipSelf) {}
|
||||
public key: ReflectiveKey, public optional: boolean, public visibility: Self|SkipSelf|null) {}
|
||||
|
||||
static fromKey(key: ReflectiveKey): ReflectiveDependency {
|
||||
return new ReflectiveDependency(key, false, null);
|
||||
@ -128,7 +128,8 @@ function resolveReflectiveFactory(provider: NormalizedProvider): ResolvedReflect
|
||||
*/
|
||||
function resolveReflectiveProvider(provider: NormalizedProvider): ResolvedReflectiveProvider {
|
||||
return new ResolvedReflectiveProvider_(
|
||||
ReflectiveKey.get(provider.provide), [resolveReflectiveFactory(provider)], provider.multi);
|
||||
ReflectiveKey.get(provider.provide), [resolveReflectiveFactory(provider)],
|
||||
provider.multi || false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -198,7 +199,7 @@ function _normalizeProviders(providers: Provider[], res: Provider[]): Provider[]
|
||||
}
|
||||
|
||||
export function constructDependencies(
|
||||
typeOrFunc: any, dependencies: any[]): ReflectiveDependency[] {
|
||||
typeOrFunc: any, dependencies?: any[]): ReflectiveDependency[] {
|
||||
if (!dependencies) {
|
||||
return _dependenciesFor(typeOrFunc);
|
||||
} else {
|
||||
@ -230,7 +231,7 @@ function _extractToken(
|
||||
}
|
||||
}
|
||||
|
||||
let visibility: Self|SkipSelf = null;
|
||||
let visibility: Self|SkipSelf|null = null;
|
||||
|
||||
for (let i = 0; i < metadata.length; ++i) {
|
||||
const paramMetadata = metadata[i];
|
||||
@ -261,6 +262,6 @@ function _extractToken(
|
||||
}
|
||||
|
||||
function _createDependency(
|
||||
token: any, optional: boolean, visibility: Self | SkipSelf): ReflectiveDependency {
|
||||
token: any, optional: boolean, visibility: Self | SkipSelf | null): ReflectiveDependency {
|
||||
return new ReflectiveDependency(ReflectiveKey.get(token), optional, visibility);
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ export class CodegenComponentFactoryResolver implements ComponentFactoryResolver
|
||||
resolveComponentFactory<T>(component: {new (...args: any[]): T}): ComponentFactory<T> {
|
||||
let factory = this._factories.get(component) || this._parent.resolveComponentFactory(component);
|
||||
|
||||
return factory ? new ComponentFactoryBoundToModule(factory, this._ngModule) : null;
|
||||
return new ComponentFactoryBoundToModule(factory, this._ngModule);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ export class NgModuleFactory<T> {
|
||||
|
||||
get moduleType(): Type<T> { return this._moduleType; }
|
||||
|
||||
create(parentInjector: Injector): NgModuleRef<T> {
|
||||
create(parentInjector: Injector|null): NgModuleRef<T> {
|
||||
const instance = new this._injectorClass(parentInjector || Injector.NULL);
|
||||
instance.create();
|
||||
return instance;
|
||||
|
@ -63,7 +63,9 @@ export class QueryList<T>/* implements Iterable<T> */ {
|
||||
* See
|
||||
* [Array.find](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find)
|
||||
*/
|
||||
find(fn: (item: T, index: number, array: T[]) => boolean): T { return this._results.find(fn); }
|
||||
find(fn: (item: T, index: number, array: T[]) => boolean): T|undefined {
|
||||
return this._results.find(fn);
|
||||
}
|
||||
|
||||
/**
|
||||
* See
|
||||
|
@ -51,7 +51,7 @@ export abstract class ViewContainerRef {
|
||||
/**
|
||||
* Returns the {@link ViewRef} for the View located in this container at the specified index.
|
||||
*/
|
||||
abstract get(index: number): ViewRef;
|
||||
abstract get(index: number): ViewRef|null;
|
||||
|
||||
/**
|
||||
* Returns the number of Views currently attached to this container.
|
||||
@ -120,5 +120,5 @@ export abstract class ViewContainerRef {
|
||||
*
|
||||
* If the `index` param is omitted, the last {@link ViewRef} is detached.
|
||||
*/
|
||||
abstract detach(index?: number): ViewRef;
|
||||
abstract detach(index?: number): ViewRef|null;
|
||||
}
|
||||
|
@ -639,7 +639,7 @@ export interface Component extends Directive {
|
||||
* ### DSL Animation Functions
|
||||
*
|
||||
* Please visit each of the animation DSL functions listed below to gain a better understanding
|
||||
* of how and why they are used for crafting animations in Angular2:
|
||||
* of how and why they are used for crafting animations in Angular:
|
||||
*
|
||||
* - {@link trigger trigger()}
|
||||
* - {@link state state()}
|
||||
|
@ -62,19 +62,19 @@ export enum ViewEncapsulation {
|
||||
*/
|
||||
export class ViewMetadata {
|
||||
/** {@link Component.templateUrl} */
|
||||
templateUrl: string;
|
||||
templateUrl: string|undefined;
|
||||
/** {@link Component.template} */
|
||||
template: string;
|
||||
template: string|undefined;
|
||||
/** {@link Component.stylesUrl} */
|
||||
styleUrls: string[];
|
||||
styleUrls: string[]|undefined;
|
||||
/** {@link Component.styles} */
|
||||
styles: string[];
|
||||
styles: string[]|undefined;
|
||||
/** {@link Component.encapsulation} */
|
||||
encapsulation: ViewEncapsulation;
|
||||
encapsulation: ViewEncapsulation|undefined;
|
||||
/** {@link Component.animation} */
|
||||
animations: any[];
|
||||
animations: any[]|undefined;
|
||||
/** {@link Component.interpolation} */
|
||||
interpolation: [string, string];
|
||||
interpolation: [string, string]|undefined;
|
||||
|
||||
constructor(
|
||||
{templateUrl, template, encapsulation, styles, styleUrls, animations, interpolation}: {
|
||||
|
@ -53,7 +53,9 @@ export function createScope(signature: string, flags: any = null): any {
|
||||
return events.createScope(signature, flags);
|
||||
}
|
||||
|
||||
export function leave<T>(scope: Scope, returnValue?: T): T {
|
||||
export function leave<T>(scope: Scope): void;
|
||||
export function leave<T>(scope: Scope, returnValue?: T): T;
|
||||
export function leave<T>(scope: Scope, returnValue?: any): any {
|
||||
trace.leaveScope(scope, returnValue);
|
||||
return returnValue;
|
||||
}
|
||||
|
@ -21,6 +21,6 @@ export interface PlatformReflectionCapabilities {
|
||||
method(name: string): MethodFn;
|
||||
importUri(type: Type<any>): string;
|
||||
resourceUri(type: Type<any>): string;
|
||||
resolveIdentifier(name: string, moduleUrl: string, members: string[], runtime: any): any;
|
||||
resolveIdentifier(name: string, moduleUrl: string, members: string[]|null, runtime: any): any;
|
||||
resolveEnum(enumIdentifier: any, name: string): any;
|
||||
}
|
||||
|
@ -14,8 +14,7 @@ import {GetterFn, MethodFn, SetterFn} from './types';
|
||||
/**
|
||||
* Attention: This regex has to hold even if the code is minified!
|
||||
*/
|
||||
export const DELEGATE_CTOR =
|
||||
/^function\s+\S+\(\)\s*{\s*("use strict";)?\s*(return\s+)?(\S+\s+!==\s+null\s+&&\s+)?\S+\.apply\(this,\s*arguments\)/;
|
||||
export const DELEGATE_CTOR = /^function\s+\S+\(\)\s*{[\s\S]+\.apply\(this,\s*arguments\)/;
|
||||
|
||||
export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||
private _reflect: any;
|
||||
@ -54,7 +53,7 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||
return result;
|
||||
}
|
||||
|
||||
private _ownParameters(type: Type<any>, parentCtor: any): any[][] {
|
||||
private _ownParameters(type: Type<any>, parentCtor: any): any[][]|null {
|
||||
// If we have no decorators, we only have function.length as metadata.
|
||||
// In that case, to detect whether a child class declared an own constructor or not,
|
||||
// we need to look inside of that constructor to check whether it is
|
||||
@ -115,7 +114,7 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||
return parameters || [];
|
||||
}
|
||||
|
||||
private _ownAnnotations(typeOrFunc: Type<any>, parentCtor: any): any[] {
|
||||
private _ownAnnotations(typeOrFunc: Type<any>, parentCtor: any): any[]|null {
|
||||
// Prefer the direct API.
|
||||
if ((<any>typeOrFunc).annotations && (<any>typeOrFunc).annotations !== parentCtor.annotations) {
|
||||
let annotations = (<any>typeOrFunc).annotations;
|
||||
@ -134,6 +133,7 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||
if (this._reflect && this._reflect.getOwnMetadata) {
|
||||
return this._reflect.getOwnMetadata('annotations', typeOrFunc);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
annotations(typeOrFunc: Type<any>): any[] {
|
||||
@ -146,7 +146,7 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||
return parentAnnotations.concat(ownAnnotations);
|
||||
}
|
||||
|
||||
private _ownPropMetadata(typeOrFunc: any, parentCtor: any): {[key: string]: any[]} {
|
||||
private _ownPropMetadata(typeOrFunc: any, parentCtor: any): {[key: string]: any[]}|null {
|
||||
// Prefer the direct API.
|
||||
if ((<any>typeOrFunc).propMetadata &&
|
||||
(<any>typeOrFunc).propMetadata !== parentCtor.propMetadata) {
|
||||
@ -172,6 +172,7 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||
if (this._reflect && this._reflect.getOwnMetadata) {
|
||||
return this._reflect.getOwnMetadata('propMetadata', typeOrFunc);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
propMetadata(typeOrFunc: any): {[key: string]: any[]} {
|
||||
|
@ -51,7 +51,7 @@ export class Reflector extends ReflectorReader {
|
||||
|
||||
resourceUri(type: any): string { return this.reflectionCapabilities.resourceUri(type); }
|
||||
|
||||
resolveIdentifier(name: string, moduleUrl: string, members: string[], runtime: any): any {
|
||||
resolveIdentifier(name: string, moduleUrl: string, members: string[]|null, runtime: any): any {
|
||||
return this.reflectionCapabilities.resolveIdentifier(name, moduleUrl, members, runtime);
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ export abstract class ReflectorReader {
|
||||
abstract parameters(typeOrFunc: /*Type*/ any): any[][];
|
||||
abstract annotations(typeOrFunc: /*Type*/ any): any[];
|
||||
abstract propMetadata(typeOrFunc: /*Type*/ any): {[key: string]: any[]};
|
||||
abstract importUri(typeOrFunc: /*Type*/ any): string;
|
||||
abstract importUri(typeOrFunc: /*Type*/ any): string|null;
|
||||
abstract resourceUri(typeOrFunc: /*Type*/ any): string;
|
||||
abstract resolveIdentifier(name: string, moduleUrl: string, members: string[], runtime: any): any;
|
||||
abstract resolveEnum(identifier: any, name: string): any;
|
||||
|
@ -126,7 +126,7 @@ export interface RendererType2 {
|
||||
* @experimental
|
||||
*/
|
||||
export abstract class RendererFactory2 {
|
||||
abstract createRenderer(hostElement: any, type: RendererType2): Renderer2;
|
||||
abstract createRenderer(hostElement: any, type: RendererType2|null): Renderer2;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -148,7 +148,7 @@ export abstract class Renderer2 {
|
||||
abstract get data(): {[key: string]: any};
|
||||
|
||||
abstract destroy(): void;
|
||||
abstract createElement(name: string, namespace?: string): any;
|
||||
abstract createElement(name: string, namespace?: string|null): any;
|
||||
abstract createComment(value: string): any;
|
||||
abstract createText(value: string): any;
|
||||
/**
|
||||
@ -156,7 +156,7 @@ export abstract class Renderer2 {
|
||||
* in which case the view engine won't call it.
|
||||
* This is used as a performance optimization for production mode.
|
||||
*/
|
||||
destroyNode: (node: any) => void | null;
|
||||
destroyNode: ((node: any) => void)|null;
|
||||
abstract appendChild(parent: any, newChild: any): void;
|
||||
abstract insertBefore(parent: any, newChild: any, refChild: any): void;
|
||||
abstract removeChild(parent: any, oldChild: any): void;
|
||||
@ -173,8 +173,8 @@ export abstract class Renderer2 {
|
||||
* the caller can't rely on checking whether this is null or not.
|
||||
*/
|
||||
abstract nextSibling(node: any): any;
|
||||
abstract setAttribute(el: any, name: string, value: string, namespace?: string): void;
|
||||
abstract removeAttribute(el: any, name: string, namespace?: string): void;
|
||||
abstract setAttribute(el: any, name: string, value: string, namespace?: string|null): void;
|
||||
abstract removeAttribute(el: any, name: string, namespace?: string|null): void;
|
||||
abstract addClass(el: any, name: string): void;
|
||||
abstract removeClass(el: any, name: string): void;
|
||||
abstract setStyle(el: any, style: string, value: any, flags?: RendererStyleFlags2): void;
|
||||
|
23
packages/core/src/testability/testability.externs.js
Normal file
23
packages/core/src/testability/testability.externs.js
Normal file
@ -0,0 +1,23 @@
|
||||
/** @externs */
|
||||
|
||||
/** @record @struct */
|
||||
function PublicTestability() {}
|
||||
|
||||
/**
|
||||
* @return {?}
|
||||
*/
|
||||
PublicTestability.prototype.isStable = function() {};
|
||||
|
||||
/**
|
||||
* @param {?} callback
|
||||
* @return {?}
|
||||
*/
|
||||
PublicTestability.prototype.whenStable = function(callback) {};
|
||||
|
||||
/**
|
||||
* @param {?} using
|
||||
* @param {?} provider
|
||||
* @param {?} exactMatch
|
||||
* @return {?}
|
||||
*/
|
||||
PublicTestability.prototype.findProviders = function(using, provider, exactMatch) {};
|
@ -92,7 +92,7 @@ export class Testability implements PublicTestability {
|
||||
// Schedules the call backs in a new frame so that it is always async.
|
||||
scheduleMicroTask(() => {
|
||||
while (this._callbacks.length !== 0) {
|
||||
(this._callbacks.pop())(this._didWork);
|
||||
(this._callbacks.pop() !)(this._didWork);
|
||||
}
|
||||
this._didWork = false;
|
||||
});
|
||||
@ -136,13 +136,13 @@ export class TestabilityRegistry {
|
||||
this._applications.set(token, testability);
|
||||
}
|
||||
|
||||
getTestability(elem: any): Testability { return this._applications.get(elem); }
|
||||
getTestability(elem: any): Testability|null { return this._applications.get(elem) || null; }
|
||||
|
||||
getAllTestabilities(): Testability[] { return Array.from(this._applications.values()); }
|
||||
|
||||
getAllRootElements(): any[] { return Array.from(this._applications.keys()); }
|
||||
|
||||
findTestabilityInTree(elem: Node, findInAncestors: boolean = true): Testability {
|
||||
findTestabilityInTree(elem: Node, findInAncestors: boolean = true): Testability|null {
|
||||
return _testabilityGetter.findTestabilityInTree(this, elem, findInAncestors);
|
||||
}
|
||||
}
|
||||
@ -157,13 +157,13 @@ export class TestabilityRegistry {
|
||||
export interface GetTestability {
|
||||
addToWindow(registry: TestabilityRegistry): void;
|
||||
findTestabilityInTree(registry: TestabilityRegistry, elem: any, findInAncestors: boolean):
|
||||
Testability;
|
||||
Testability|null;
|
||||
}
|
||||
|
||||
class _NoopGetTestability implements GetTestability {
|
||||
addToWindow(registry: TestabilityRegistry): void {}
|
||||
findTestabilityInTree(registry: TestabilityRegistry, elem: any, findInAncestors: boolean):
|
||||
Testability {
|
||||
Testability|null {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ function extractAnnotation(annotation: any): any {
|
||||
return annotation;
|
||||
}
|
||||
|
||||
function applyParams(fnOrArray: (Function | any[]), key: string): Function {
|
||||
function applyParams(fnOrArray: Function | any[] | undefined, key: string): Function {
|
||||
if (fnOrArray === Object || fnOrArray === String || fnOrArray === Function ||
|
||||
fnOrArray === Number || fnOrArray === Array) {
|
||||
throw new Error(`Can not use native ${stringify(fnOrArray)} as constructor`);
|
||||
@ -107,7 +107,7 @@ function applyParams(fnOrArray: (Function | any[]), key: string): Function {
|
||||
}
|
||||
|
||||
if (Array.isArray(fnOrArray)) {
|
||||
const annotations: any[] = fnOrArray;
|
||||
const annotations: any[] = fnOrArray as any[];
|
||||
const annoLength = annotations.length - 1;
|
||||
const fn: Function = fnOrArray[annoLength];
|
||||
if (typeof fn !== 'function') {
|
||||
@ -263,7 +263,7 @@ export function Class(clsDef: ClassDefinition): Type<any> {
|
||||
*/
|
||||
export function makeDecorator(
|
||||
name: string, props: {[name: string]: any}, parentClass?: any,
|
||||
chainFn: (fn: Function) => void = null): (...args: any[]) => (cls: any) => any {
|
||||
chainFn?: (fn: Function) => void): (...args: any[]) => (cls: any) => any {
|
||||
const metaCtor = makeMetadataCtor([props]);
|
||||
|
||||
function DecoratorFactory(objOrType: any): (cls: any) => any {
|
||||
@ -332,7 +332,7 @@ export function makeParamDecorator(
|
||||
return ParamDecorator;
|
||||
|
||||
function ParamDecorator(cls: any, unusedKey: any, index: number): any {
|
||||
const parameters: any[][] = Reflect.getOwnMetadata('parameters', cls) || [];
|
||||
const parameters: (any[] | null)[] = Reflect.getOwnMetadata('parameters', cls) || [];
|
||||
|
||||
// there might be gaps if some in between parameters do not have annotations.
|
||||
// we pad with nulls.
|
||||
@ -341,7 +341,7 @@ export function makeParamDecorator(
|
||||
}
|
||||
|
||||
parameters[index] = parameters[index] || [];
|
||||
parameters[index].push(annotationInstance);
|
||||
parameters[index] !.push(annotationInstance);
|
||||
|
||||
Reflect.defineMetadata('parameters', parameters, cls);
|
||||
return cls;
|
||||
|
@ -24,18 +24,3 @@ export function isObservable(obj: any | Observable<any>): obj is Observable<any>
|
||||
// TODO use Symbol.observable when https://github.com/ReactiveX/rxjs/issues/2415 will be resolved
|
||||
return !!obj && typeof obj.subscribe === 'function';
|
||||
}
|
||||
|
||||
// TODO(misko): replace with Object.assign once we require ES6.
|
||||
export function merge<V>(m1: {[key: string]: V}, m2: {[key: string]: V}): {[key: string]: V} {
|
||||
const m: {[key: string]: V} = {};
|
||||
|
||||
for (const k of Object.keys(m1)) {
|
||||
m[k] = m1[k];
|
||||
}
|
||||
|
||||
for (const k of Object.keys(m2)) {
|
||||
m[k] = m2[k];
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
@ -16,20 +16,17 @@ export function anchorDef(
|
||||
flags: NodeFlags, matchedQueriesDsl: [string | number, QueryValueType][],
|
||||
ngContentIndex: number, childCount: number, handleEvent?: ElementHandleEventFn,
|
||||
templateFactory?: ViewDefinitionFactory): NodeDef {
|
||||
if (!handleEvent) {
|
||||
handleEvent = NOOP;
|
||||
}
|
||||
flags |= NodeFlags.TypeElement;
|
||||
const {matchedQueries, references, matchedQueryIds} = splitMatchedQueriesDsl(matchedQueriesDsl);
|
||||
const template = templateFactory ? resolveViewDefinition(templateFactory) : null;
|
||||
|
||||
return {
|
||||
// will bet set by the view definition
|
||||
index: undefined,
|
||||
parent: undefined,
|
||||
renderParent: undefined,
|
||||
bindingIndex: undefined,
|
||||
outputIndex: undefined,
|
||||
index: -1,
|
||||
parent: null,
|
||||
renderParent: null,
|
||||
bindingIndex: -1,
|
||||
outputIndex: -1,
|
||||
// regular values
|
||||
flags,
|
||||
childFlags: 0,
|
||||
@ -39,19 +36,20 @@ export function anchorDef(
|
||||
bindingFlags: 0,
|
||||
outputs: [],
|
||||
element: {
|
||||
ns: undefined,
|
||||
name: undefined,
|
||||
attrs: undefined, template,
|
||||
componentProvider: undefined,
|
||||
componentView: undefined,
|
||||
componentRendererType: undefined,
|
||||
publicProviders: undefined,
|
||||
allProviders: undefined, handleEvent
|
||||
ns: null,
|
||||
name: null,
|
||||
attrs: null, template,
|
||||
componentProvider: null,
|
||||
componentView: null,
|
||||
componentRendererType: null,
|
||||
publicProviders: null,
|
||||
allProviders: null,
|
||||
handleEvent: handleEvent || NOOP
|
||||
},
|
||||
provider: undefined,
|
||||
text: undefined,
|
||||
query: undefined,
|
||||
ngContent: undefined
|
||||
provider: null,
|
||||
text: null,
|
||||
query: null,
|
||||
ngContent: null
|
||||
};
|
||||
}
|
||||
|
||||
@ -61,13 +59,13 @@ export function elementDef(
|
||||
fixedAttrs: [string, string][] = [],
|
||||
bindings?: [BindingFlags, string, string | SecurityContext][], outputs?: ([string, string])[],
|
||||
handleEvent?: ElementHandleEventFn, componentView?: ViewDefinitionFactory,
|
||||
componentRendererType?: RendererType2): NodeDef {
|
||||
componentRendererType?: RendererType2 | null): NodeDef {
|
||||
if (!handleEvent) {
|
||||
handleEvent = NOOP;
|
||||
}
|
||||
const {matchedQueries, references, matchedQueryIds} = splitMatchedQueriesDsl(matchedQueriesDsl);
|
||||
let ns: string;
|
||||
let name: string;
|
||||
let ns: string = null !;
|
||||
let name: string = null !;
|
||||
if (namespaceAndName) {
|
||||
[ns, name] = splitNamespace(namespaceAndName);
|
||||
}
|
||||
@ -77,8 +75,8 @@ export function elementDef(
|
||||
const [bindingFlags, namespaceAndName, suffixOrSecurityContext] = bindings[i];
|
||||
|
||||
const [ns, name] = splitNamespace(namespaceAndName);
|
||||
let securityContext: SecurityContext;
|
||||
let suffix: string;
|
||||
let securityContext: SecurityContext = undefined !;
|
||||
let suffix: string = undefined !;
|
||||
switch (bindingFlags & BindingFlags.Types) {
|
||||
case BindingFlags.TypeElementStyle:
|
||||
suffix = <string>suffixOrSecurityContext;
|
||||
@ -98,7 +96,7 @@ export function elementDef(
|
||||
outputDefs[i] = {
|
||||
type: OutputType.ElementOutput,
|
||||
target: <any>target, eventName,
|
||||
propName: undefined
|
||||
propName: null
|
||||
};
|
||||
}
|
||||
fixedAttrs = fixedAttrs || [];
|
||||
@ -113,11 +111,11 @@ export function elementDef(
|
||||
flags |= NodeFlags.TypeElement;
|
||||
return {
|
||||
// will bet set by the view definition
|
||||
index: undefined,
|
||||
parent: undefined,
|
||||
renderParent: undefined,
|
||||
bindingIndex: undefined,
|
||||
outputIndex: undefined,
|
||||
index: -1,
|
||||
parent: null,
|
||||
renderParent: null,
|
||||
bindingIndex: -1,
|
||||
outputIndex: -1,
|
||||
// regular values
|
||||
flags,
|
||||
childFlags: 0,
|
||||
@ -130,21 +128,24 @@ export function elementDef(
|
||||
ns,
|
||||
name,
|
||||
attrs,
|
||||
template: undefined,
|
||||
template: null,
|
||||
// will bet set by the view definition
|
||||
componentProvider: undefined, componentView, componentRendererType,
|
||||
publicProviders: undefined,
|
||||
allProviders: undefined, handleEvent,
|
||||
componentProvider: null,
|
||||
componentView: componentView || null,
|
||||
componentRendererType: componentRendererType,
|
||||
publicProviders: null,
|
||||
allProviders: null,
|
||||
handleEvent: handleEvent || NOOP,
|
||||
},
|
||||
provider: undefined,
|
||||
text: undefined,
|
||||
query: undefined,
|
||||
ngContent: undefined
|
||||
provider: null,
|
||||
text: null,
|
||||
query: null,
|
||||
ngContent: null
|
||||
};
|
||||
}
|
||||
|
||||
export function createElement(view: ViewData, renderHost: any, def: NodeDef): ElementData {
|
||||
const elDef = def.element;
|
||||
const elDef = def.element !;
|
||||
const rootSelectorOrNode = view.root.selectorOrNode;
|
||||
const renderer = view.renderer;
|
||||
let el: any;
|
||||
@ -175,7 +176,7 @@ export function listenToElementOutputs(view: ViewData, compView: ViewData, def:
|
||||
const output = def.outputs[i];
|
||||
const handleEventClosure = renderEventHandlerClosure(
|
||||
view, def.index, elementEventFullName(output.target, output.eventName));
|
||||
let listenTarget = output.target;
|
||||
let listenTarget: 'window'|'document'|'body'|'component'|null = output.target;
|
||||
let listenerView = view;
|
||||
if (output.target === 'component') {
|
||||
listenTarget = null;
|
||||
@ -183,7 +184,7 @@ export function listenToElementOutputs(view: ViewData, compView: ViewData, def:
|
||||
}
|
||||
const disposable =
|
||||
<any>listenerView.renderer.listen(listenTarget || el, output.eventName, handleEventClosure);
|
||||
view.disposables[def.outputIndex + i] = disposable;
|
||||
view.disposables ![def.outputIndex + i] = disposable;
|
||||
}
|
||||
}
|
||||
|
||||
@ -225,7 +226,7 @@ function checkAndUpdateElementValue(view: ViewData, def: NodeDef, bindingIdx: nu
|
||||
const binding = def.bindings[bindingIdx];
|
||||
const elData = asElementData(view, def.index);
|
||||
const renderNode = elData.renderElement;
|
||||
const name = binding.name;
|
||||
const name = binding.name !;
|
||||
switch (binding.flags & BindingFlags.Types) {
|
||||
case BindingFlags.TypeElementAttribute:
|
||||
setElementAttribute(view, binding, renderNode, binding.ns, name, value);
|
||||
@ -248,7 +249,8 @@ function checkAndUpdateElementValue(view: ViewData, def: NodeDef, bindingIdx: nu
|
||||
}
|
||||
|
||||
function setElementAttribute(
|
||||
view: ViewData, binding: BindingDef, renderNode: any, ns: string, name: string, value: any) {
|
||||
view: ViewData, binding: BindingDef, renderNode: any, ns: string | null, name: string,
|
||||
value: any) {
|
||||
const securityContext = binding.securityContext;
|
||||
let renderValue = securityContext ? view.root.sanitizer.sanitize(securityContext, value) : value;
|
||||
renderValue = renderValue != null ? renderValue.toString() : null;
|
||||
@ -271,7 +273,7 @@ function setElementClass(view: ViewData, renderNode: any, name: string, value: b
|
||||
|
||||
function setElementStyle(
|
||||
view: ViewData, binding: BindingDef, renderNode: any, name: string, value: any) {
|
||||
let renderValue = view.root.sanitizer.sanitize(SecurityContext.STYLE, value);
|
||||
let renderValue: string|null = view.root.sanitizer.sanitize(SecurityContext.STYLE, value);
|
||||
if (renderValue != null) {
|
||||
renderValue = renderValue.toString();
|
||||
const unit = binding.suffix;
|
||||
|
@ -12,11 +12,11 @@ import {RenderNodeAction, getParentRenderElement, visitProjectedRenderNodes} fro
|
||||
export function ngContentDef(ngContentIndex: number, index: number): NodeDef {
|
||||
return {
|
||||
// will bet set by the view definition
|
||||
index: undefined,
|
||||
parent: undefined,
|
||||
renderParent: undefined,
|
||||
bindingIndex: undefined,
|
||||
outputIndex: undefined,
|
||||
index: -1,
|
||||
parent: null,
|
||||
renderParent: null,
|
||||
bindingIndex: -1,
|
||||
outputIndex: -1,
|
||||
// regular values
|
||||
flags: NodeFlags.TypeNgContent,
|
||||
childFlags: 0,
|
||||
@ -29,10 +29,10 @@ export function ngContentDef(ngContentIndex: number, index: number): NodeDef {
|
||||
bindings: [],
|
||||
bindingFlags: 0,
|
||||
outputs: [],
|
||||
element: undefined,
|
||||
provider: undefined,
|
||||
text: undefined,
|
||||
query: undefined,
|
||||
element: null,
|
||||
provider: null,
|
||||
text: null,
|
||||
query: null,
|
||||
ngContent: {index}
|
||||
};
|
||||
}
|
||||
@ -43,7 +43,7 @@ export function appendNgContent(view: ViewData, renderHost: any, def: NodeDef) {
|
||||
// Nothing to do if there is no parent element.
|
||||
return;
|
||||
}
|
||||
const ngContentIndex = def.ngContent.index;
|
||||
const ngContentIndex = def.ngContent !.index;
|
||||
visitProjectedRenderNodes(
|
||||
view, ngContentIndex, RenderNodeAction.AppendChild, parentEl, undefined, undefined);
|
||||
view, ngContentIndex, RenderNodeAction.AppendChild, parentEl, null, undefined);
|
||||
}
|
||||
|
@ -38,9 +38,9 @@ export function directiveDef(
|
||||
bindings[bindingIndex] = {
|
||||
flags: BindingFlags.TypeProperty,
|
||||
name: prop, nonMinifiedName,
|
||||
ns: undefined,
|
||||
securityContext: undefined,
|
||||
suffix: undefined
|
||||
ns: null,
|
||||
securityContext: null,
|
||||
suffix: null
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -67,9 +67,9 @@ export function providerDef(
|
||||
}
|
||||
|
||||
export function _def(
|
||||
flags: NodeFlags, matchedQueriesDsl: [string | number, QueryValueType][], childCount: number,
|
||||
token: any, value: any, deps: ([DepFlags, any] | any)[], bindings?: BindingDef[],
|
||||
outputs?: OutputDef[]): NodeDef {
|
||||
flags: NodeFlags, matchedQueriesDsl: [string | number, QueryValueType][] | null,
|
||||
childCount: number, token: any, value: any, deps: ([DepFlags, any] | any)[],
|
||||
bindings?: BindingDef[], outputs?: OutputDef[]): NodeDef {
|
||||
const {matchedQueries, references, matchedQueryIds} = splitMatchedQueriesDsl(matchedQueriesDsl);
|
||||
if (!outputs) {
|
||||
outputs = [];
|
||||
@ -92,23 +92,23 @@ export function _def(
|
||||
|
||||
return {
|
||||
// will bet set by the view definition
|
||||
index: undefined,
|
||||
parent: undefined,
|
||||
renderParent: undefined,
|
||||
bindingIndex: undefined,
|
||||
outputIndex: undefined,
|
||||
index: -1,
|
||||
parent: null,
|
||||
renderParent: null,
|
||||
bindingIndex: -1,
|
||||
outputIndex: -1,
|
||||
// regular values
|
||||
flags,
|
||||
childFlags: 0,
|
||||
directChildFlags: 0,
|
||||
childMatchedQueries: 0, matchedQueries, matchedQueryIds, references,
|
||||
ngContentIndex: undefined, childCount, bindings,
|
||||
ngContentIndex: -1, childCount, bindings,
|
||||
bindingFlags: calcBindingFlags(bindings), outputs,
|
||||
element: undefined,
|
||||
element: null,
|
||||
provider: {token, tokenKey: tokenKey(token), value, deps: depDefs},
|
||||
text: undefined,
|
||||
query: undefined,
|
||||
ngContent: undefined
|
||||
text: null,
|
||||
query: null,
|
||||
ngContent: null
|
||||
};
|
||||
}
|
||||
|
||||
@ -126,22 +126,22 @@ export function createPipeInstance(view: ViewData, def: NodeDef): any {
|
||||
const allowPrivateServices = true;
|
||||
// pipes are always eager and classes!
|
||||
return createClass(
|
||||
compView.parent, viewParentEl(compView), allowPrivateServices, def.provider.value,
|
||||
def.provider.deps);
|
||||
compView.parent !, viewParentEl(compView) !, allowPrivateServices, def.provider !.value,
|
||||
def.provider !.deps);
|
||||
}
|
||||
|
||||
export function createDirectiveInstance(view: ViewData, def: NodeDef): any {
|
||||
// components can see other private services, other directives can't.
|
||||
const allowPrivateServices = (def.flags & NodeFlags.Component) > 0;
|
||||
// directives are always eager and classes!
|
||||
const instance =
|
||||
createClass(view, def.parent, allowPrivateServices, def.provider.value, def.provider.deps);
|
||||
const instance = createClass(
|
||||
view, def.parent !, allowPrivateServices, def.provider !.value, def.provider !.deps);
|
||||
if (def.outputs.length) {
|
||||
for (let i = 0; i < def.outputs.length; i++) {
|
||||
const output = def.outputs[i];
|
||||
const subscription = instance[output.propName].subscribe(
|
||||
eventHandlerClosure(view, def.parent.index, output.eventName));
|
||||
view.disposables[def.outputIndex + i] = subscription.unsubscribe.bind(subscription);
|
||||
const subscription = instance[output.propName !].subscribe(
|
||||
eventHandlerClosure(view, def.parent !.index, output.eventName));
|
||||
view.disposables ![def.outputIndex + i] = subscription.unsubscribe.bind(subscription);
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
@ -157,48 +157,48 @@ export function checkAndUpdateDirectiveInline(
|
||||
const providerData = asProviderData(view, def.index);
|
||||
const directive = providerData.instance;
|
||||
let changed = false;
|
||||
let changes: SimpleChanges;
|
||||
let changes: SimpleChanges = undefined !;
|
||||
const bindLen = def.bindings.length;
|
||||
if (bindLen > 0 && checkBinding(view, def, 0, v0)) {
|
||||
changed = true;
|
||||
changes = updateProp(view, providerData, def, 0, v0, changes);
|
||||
};
|
||||
}
|
||||
if (bindLen > 1 && checkBinding(view, def, 1, v1)) {
|
||||
changed = true;
|
||||
changes = updateProp(view, providerData, def, 1, v1, changes);
|
||||
};
|
||||
}
|
||||
if (bindLen > 2 && checkBinding(view, def, 2, v2)) {
|
||||
changed = true;
|
||||
changes = updateProp(view, providerData, def, 2, v2, changes);
|
||||
};
|
||||
}
|
||||
if (bindLen > 3 && checkBinding(view, def, 3, v3)) {
|
||||
changed = true;
|
||||
changes = updateProp(view, providerData, def, 3, v3, changes);
|
||||
};
|
||||
}
|
||||
if (bindLen > 4 && checkBinding(view, def, 4, v4)) {
|
||||
changed = true;
|
||||
changes = updateProp(view, providerData, def, 4, v4, changes);
|
||||
};
|
||||
}
|
||||
if (bindLen > 5 && checkBinding(view, def, 5, v5)) {
|
||||
changed = true;
|
||||
changes = updateProp(view, providerData, def, 5, v5, changes);
|
||||
};
|
||||
}
|
||||
if (bindLen > 6 && checkBinding(view, def, 6, v6)) {
|
||||
changed = true;
|
||||
changes = updateProp(view, providerData, def, 6, v6, changes);
|
||||
};
|
||||
}
|
||||
if (bindLen > 7 && checkBinding(view, def, 7, v7)) {
|
||||
changed = true;
|
||||
changes = updateProp(view, providerData, def, 7, v7, changes);
|
||||
};
|
||||
}
|
||||
if (bindLen > 8 && checkBinding(view, def, 8, v8)) {
|
||||
changed = true;
|
||||
changes = updateProp(view, providerData, def, 8, v8, changes);
|
||||
};
|
||||
}
|
||||
if (bindLen > 9 && checkBinding(view, def, 9, v9)) {
|
||||
changed = true;
|
||||
changes = updateProp(view, providerData, def, 9, v9, changes);
|
||||
};
|
||||
}
|
||||
if (changes) {
|
||||
directive.ngOnChanges(changes);
|
||||
}
|
||||
@ -216,7 +216,7 @@ export function checkAndUpdateDirectiveDynamic(
|
||||
const providerData = asProviderData(view, def.index);
|
||||
const directive = providerData.instance;
|
||||
let changed = false;
|
||||
let changes: SimpleChanges;
|
||||
let changes: SimpleChanges = undefined !;
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
if (checkBinding(view, def, i, values[i])) {
|
||||
changed = true;
|
||||
@ -242,18 +242,18 @@ function _createProviderInstance(view: ViewData, def: NodeDef): any {
|
||||
let injectable: any;
|
||||
switch (def.flags & NodeFlags.Types) {
|
||||
case NodeFlags.TypeClassProvider:
|
||||
injectable =
|
||||
createClass(view, def.parent, allowPrivateServices, providerDef.value, providerDef.deps);
|
||||
injectable = createClass(
|
||||
view, def.parent !, allowPrivateServices, providerDef !.value, providerDef !.deps);
|
||||
break;
|
||||
case NodeFlags.TypeFactoryProvider:
|
||||
injectable =
|
||||
callFactory(view, def.parent, allowPrivateServices, providerDef.value, providerDef.deps);
|
||||
injectable = callFactory(
|
||||
view, def.parent !, allowPrivateServices, providerDef !.value, providerDef !.deps);
|
||||
break;
|
||||
case NodeFlags.TypeUseExistingProvider:
|
||||
injectable = resolveDep(view, def.parent, allowPrivateServices, providerDef.deps[0]);
|
||||
injectable = resolveDep(view, def.parent !, allowPrivateServices, providerDef !.deps[0]);
|
||||
break;
|
||||
case NodeFlags.TypeValueProvider:
|
||||
injectable = providerDef.value;
|
||||
injectable = providerDef !.value;
|
||||
break;
|
||||
}
|
||||
return injectable;
|
||||
@ -345,7 +345,7 @@ export const NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR = {};
|
||||
|
||||
export function resolveDep(
|
||||
view: ViewData, elDef: NodeDef, allowPrivateServices: boolean, depDef: DepDef,
|
||||
notFoundValue = Injector.THROW_IF_NOT_FOUND): any {
|
||||
notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any {
|
||||
if (depDef.flags & DepFlags.Value) {
|
||||
return depDef.token;
|
||||
}
|
||||
@ -357,7 +357,7 @@ export function resolveDep(
|
||||
|
||||
if (elDef && (depDef.flags & DepFlags.SkipSelf)) {
|
||||
allowPrivateServices = false;
|
||||
elDef = elDef.parent;
|
||||
elDef = elDef.parent !;
|
||||
}
|
||||
|
||||
while (view) {
|
||||
@ -376,7 +376,7 @@ export function resolveDep(
|
||||
case ViewContainerRefTokenKey:
|
||||
return asElementData(view, elDef.index).viewContainer;
|
||||
case TemplateRefTokenKey: {
|
||||
if (elDef.element.template) {
|
||||
if (elDef.element !.template) {
|
||||
return asElementData(view, elDef.index).template;
|
||||
}
|
||||
break;
|
||||
@ -389,8 +389,8 @@ export function resolveDep(
|
||||
return createInjector(view, elDef);
|
||||
default:
|
||||
const providerDef =
|
||||
(allowPrivateServices ? elDef.element.allProviders :
|
||||
elDef.element.publicProviders)[tokenKey];
|
||||
(allowPrivateServices ? elDef.element !.allProviders :
|
||||
elDef.element !.publicProviders) ![tokenKey];
|
||||
if (providerDef) {
|
||||
const providerData = asProviderData(view, providerDef.index);
|
||||
if (providerData.instance === NOT_CREATED) {
|
||||
@ -401,8 +401,8 @@ export function resolveDep(
|
||||
}
|
||||
}
|
||||
allowPrivateServices = isComponentView(view);
|
||||
elDef = viewParentEl(view);
|
||||
view = view.parent;
|
||||
elDef = viewParentEl(view) !;
|
||||
view = view.parent !;
|
||||
}
|
||||
|
||||
const value = startView.root.injector.get(depDef.token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR);
|
||||
@ -437,13 +437,13 @@ function updateProp(
|
||||
view: ViewData, providerData: ProviderData, def: NodeDef, bindingIdx: number, value: any,
|
||||
changes: SimpleChanges): SimpleChanges {
|
||||
if (def.flags & NodeFlags.Component) {
|
||||
const compView = asElementData(view, def.parent.index).componentView;
|
||||
const compView = asElementData(view, def.parent !.index).componentView;
|
||||
if (compView.def.flags & ViewFlags.OnPush) {
|
||||
compView.state |= ViewState.ChecksEnabled;
|
||||
}
|
||||
}
|
||||
const binding = def.bindings[bindingIdx];
|
||||
const propName = binding.name;
|
||||
const propName = binding.name !;
|
||||
// Note: This is still safe with Closure Compiler as
|
||||
// the user passed in the property name as an object has to `providerDef`,
|
||||
// so Closure Compiler will have renamed the property correctly already.
|
||||
@ -455,7 +455,7 @@ function updateProp(
|
||||
oldValue = oldValue.wrapped;
|
||||
}
|
||||
const binding = def.bindings[bindingIdx];
|
||||
changes[binding.nonMinifiedName] =
|
||||
changes[binding.nonMinifiedName !] =
|
||||
new SimpleChange(oldValue, value, (view.state & ViewState.FirstCheck) !== 0);
|
||||
}
|
||||
view.oldValues[def.bindingIndex + bindingIdx] = value;
|
||||
|
@ -29,19 +29,19 @@ function _pureExpressionDef(flags: NodeFlags, propertyNames: string[]): NodeDef
|
||||
bindings[i] = {
|
||||
flags: BindingFlags.TypeProperty,
|
||||
name: prop,
|
||||
ns: undefined,
|
||||
ns: null,
|
||||
nonMinifiedName: prop,
|
||||
securityContext: undefined,
|
||||
suffix: undefined
|
||||
securityContext: null,
|
||||
suffix: null
|
||||
};
|
||||
}
|
||||
return {
|
||||
// will bet set by the view definition
|
||||
index: undefined,
|
||||
parent: undefined,
|
||||
renderParent: undefined,
|
||||
bindingIndex: undefined,
|
||||
outputIndex: undefined,
|
||||
index: -1,
|
||||
parent: null,
|
||||
renderParent: null,
|
||||
bindingIndex: -1,
|
||||
outputIndex: -1,
|
||||
// regular values
|
||||
flags,
|
||||
childFlags: 0,
|
||||
@ -50,15 +50,15 @@ function _pureExpressionDef(flags: NodeFlags, propertyNames: string[]): NodeDef
|
||||
matchedQueries: {},
|
||||
matchedQueryIds: 0,
|
||||
references: {},
|
||||
ngContentIndex: undefined,
|
||||
ngContentIndex: -1,
|
||||
childCount: 0, bindings,
|
||||
bindingFlags: calcBindingFlags(bindings),
|
||||
outputs: [],
|
||||
element: undefined,
|
||||
provider: undefined,
|
||||
text: undefined,
|
||||
query: undefined,
|
||||
ngContent: undefined
|
||||
element: null,
|
||||
provider: null,
|
||||
text: null,
|
||||
query: null,
|
||||
ngContent: null
|
||||
};
|
||||
}
|
||||
|
||||
@ -102,16 +102,16 @@ export function checkAndUpdatePureExpressionInline(
|
||||
break;
|
||||
case NodeFlags.TypePureObject:
|
||||
value = {};
|
||||
if (bindLen > 0) value[bindings[0].name] = v0;
|
||||
if (bindLen > 1) value[bindings[1].name] = v1;
|
||||
if (bindLen > 2) value[bindings[2].name] = v2;
|
||||
if (bindLen > 3) value[bindings[3].name] = v3;
|
||||
if (bindLen > 4) value[bindings[4].name] = v4;
|
||||
if (bindLen > 5) value[bindings[5].name] = v5;
|
||||
if (bindLen > 6) value[bindings[6].name] = v6;
|
||||
if (bindLen > 7) value[bindings[7].name] = v7;
|
||||
if (bindLen > 8) value[bindings[8].name] = v8;
|
||||
if (bindLen > 9) value[bindings[9].name] = v9;
|
||||
if (bindLen > 0) value[bindings[0].name !] = v0;
|
||||
if (bindLen > 1) value[bindings[1].name !] = v1;
|
||||
if (bindLen > 2) value[bindings[2].name !] = v2;
|
||||
if (bindLen > 3) value[bindings[3].name !] = v3;
|
||||
if (bindLen > 4) value[bindings[4].name !] = v4;
|
||||
if (bindLen > 5) value[bindings[5].name !] = v5;
|
||||
if (bindLen > 6) value[bindings[6].name !] = v6;
|
||||
if (bindLen > 7) value[bindings[7].name !] = v7;
|
||||
if (bindLen > 8) value[bindings[8].name !] = v8;
|
||||
if (bindLen > 9) value[bindings[9].name !] = v9;
|
||||
break;
|
||||
case NodeFlags.TypePurePipe:
|
||||
const pipe = v0;
|
||||
@ -175,7 +175,7 @@ export function checkAndUpdatePureExpressionDynamic(
|
||||
case NodeFlags.TypePureObject:
|
||||
value = {};
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
value[bindings[i].name] = values[i];
|
||||
value[bindings[i].name !] = values[i];
|
||||
}
|
||||
break;
|
||||
case NodeFlags.TypePurePipe:
|
||||
|
@ -22,17 +22,17 @@ export function queryDef(
|
||||
|
||||
return {
|
||||
// will bet set by the view definition
|
||||
index: undefined,
|
||||
parent: undefined,
|
||||
renderParent: undefined,
|
||||
bindingIndex: undefined,
|
||||
outputIndex: undefined,
|
||||
index: -1,
|
||||
parent: null,
|
||||
renderParent: null,
|
||||
bindingIndex: -1,
|
||||
outputIndex: -1,
|
||||
// regular values
|
||||
flags,
|
||||
childFlags: 0,
|
||||
directChildFlags: 0,
|
||||
childMatchedQueries: 0,
|
||||
ngContentIndex: undefined,
|
||||
ngContentIndex: -1,
|
||||
matchedQueries: {},
|
||||
matchedQueryIds: 0,
|
||||
references: {},
|
||||
@ -40,11 +40,11 @@ export function queryDef(
|
||||
bindings: [],
|
||||
bindingFlags: 0,
|
||||
outputs: [],
|
||||
element: undefined,
|
||||
provider: undefined,
|
||||
text: undefined,
|
||||
element: null,
|
||||
provider: null,
|
||||
text: null,
|
||||
query: {id, filterId: filterQueryId(id), bindings: bindingDefs},
|
||||
ngContent: undefined
|
||||
ngContent: null
|
||||
};
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ export function createQuery(): QueryList<any> {
|
||||
export function dirtyParentQueries(view: ViewData) {
|
||||
const queryIds = view.def.nodeMatchedQueries;
|
||||
while (view.parent && isEmbeddedView(view)) {
|
||||
let tplDef = view.parentNodeDef;
|
||||
let tplDef = view.parentNodeDef !;
|
||||
view = view.parent;
|
||||
// content queries
|
||||
const end = tplDef.index + tplDef.childCount;
|
||||
@ -63,7 +63,7 @@ export function dirtyParentQueries(view: ViewData) {
|
||||
const nodeDef = view.def.nodes[i];
|
||||
if ((nodeDef.flags & NodeFlags.TypeContentQuery) &&
|
||||
(nodeDef.flags & NodeFlags.DynamicQuery) &&
|
||||
(nodeDef.query.filterId & queryIds) === nodeDef.query.filterId) {
|
||||
(nodeDef.query !.filterId & queryIds) === nodeDef.query !.filterId) {
|
||||
asQueryList(view, i).setDirty();
|
||||
}
|
||||
if ((nodeDef.flags & NodeFlags.TypeElement && i + nodeDef.childCount < tplDef.index) ||
|
||||
@ -94,18 +94,18 @@ export function checkAndUpdateQuery(view: ViewData, nodeDef: NodeDef) {
|
||||
return;
|
||||
}
|
||||
let directiveInstance: any;
|
||||
let newValues: any[];
|
||||
let newValues: any[] = undefined !;
|
||||
if (nodeDef.flags & NodeFlags.TypeContentQuery) {
|
||||
const elementDef = nodeDef.parent.parent;
|
||||
const elementDef = nodeDef.parent !.parent !;
|
||||
newValues = calcQueryValues(
|
||||
view, elementDef.index, elementDef.index + elementDef.childCount, nodeDef.query, []);
|
||||
directiveInstance = asProviderData(view, nodeDef.parent.index).instance;
|
||||
view, elementDef.index, elementDef.index + elementDef.childCount, nodeDef.query !, []);
|
||||
directiveInstance = asProviderData(view, nodeDef.parent !.index).instance;
|
||||
} else if (nodeDef.flags & NodeFlags.TypeViewQuery) {
|
||||
newValues = calcQueryValues(view, 0, view.def.nodes.length - 1, nodeDef.query, []);
|
||||
newValues = calcQueryValues(view, 0, view.def.nodes.length - 1, nodeDef.query !, []);
|
||||
directiveInstance = view.component;
|
||||
}
|
||||
queryList.reset(newValues);
|
||||
const bindings = nodeDef.query.bindings;
|
||||
const bindings = nodeDef.query !.bindings;
|
||||
let notify = false;
|
||||
for (let i = 0; i < bindings.length; i++) {
|
||||
const binding = bindings[i];
|
||||
@ -135,12 +135,13 @@ function calcQueryValues(
|
||||
if (valueType != null) {
|
||||
values.push(getQueryValue(view, nodeDef, valueType));
|
||||
}
|
||||
if (nodeDef.flags & NodeFlags.TypeElement && nodeDef.element.template &&
|
||||
(nodeDef.element.template.nodeMatchedQueries & queryDef.filterId) === queryDef.filterId) {
|
||||
if (nodeDef.flags & NodeFlags.TypeElement && nodeDef.element !.template &&
|
||||
(nodeDef.element !.template !.nodeMatchedQueries & queryDef.filterId) ===
|
||||
queryDef.filterId) {
|
||||
// check embedded views that were attached at the place of their template.
|
||||
const elementData = asElementData(view, i);
|
||||
if (nodeDef.flags & NodeFlags.EmbeddedViews) {
|
||||
const embeddedViews = elementData.viewContainer._embeddedViews;
|
||||
const embeddedViews = elementData.viewContainer !._embeddedViews;
|
||||
for (let k = 0; k < embeddedViews.length; k++) {
|
||||
const embeddedView = embeddedViews[k];
|
||||
const dvc = declaredViewContainer(embeddedView);
|
||||
|
@ -85,7 +85,7 @@ class ComponentFactory_ extends ComponentFactory<any> {
|
||||
throw new Error('ngModule should be provided');
|
||||
}
|
||||
const viewDef = resolveViewDefinition(this.viewDefFactory);
|
||||
const componentNodeIndex = viewDef.nodes[0].element.componentProvider.index;
|
||||
const componentNodeIndex = viewDef.nodes[0].element !.componentProvider !.index;
|
||||
const view = Services.createRootView(
|
||||
injector, projectableNodes || [], rootSelectorOrNode, viewDef, ngModule, EMPTY_CONTEXT);
|
||||
const component = asProviderData(view, componentNodeIndex).instance;
|
||||
@ -135,7 +135,7 @@ class ViewContainerRef_ implements ViewContainerData {
|
||||
let elDef = this._elDef.parent;
|
||||
while (!elDef && view) {
|
||||
elDef = viewParentEl(view);
|
||||
view = view.parent;
|
||||
view = view.parent !;
|
||||
}
|
||||
|
||||
return view ? new Injector_(view, elDef) : new Injector_(this._view, null);
|
||||
@ -144,12 +144,12 @@ class ViewContainerRef_ implements ViewContainerData {
|
||||
clear(): void {
|
||||
const len = this._embeddedViews.length;
|
||||
for (let i = len - 1; i >= 0; i--) {
|
||||
const view = detachEmbeddedView(this._data, i);
|
||||
const view = detachEmbeddedView(this._data, i) !;
|
||||
Services.destroyView(view);
|
||||
}
|
||||
}
|
||||
|
||||
get(index: number): ViewRef {
|
||||
get(index: number): ViewRef|null {
|
||||
const view = this._embeddedViews[index];
|
||||
if (view) {
|
||||
const ref = new ViewRef_(view);
|
||||
@ -206,7 +206,7 @@ class ViewContainerRef_ implements ViewContainerData {
|
||||
}
|
||||
}
|
||||
|
||||
detach(index?: number): ViewRef {
|
||||
detach(index?: number): ViewRef|null {
|
||||
const view = detachEmbeddedView(this._data, index);
|
||||
return view ? new ViewRef_(view) : null;
|
||||
}
|
||||
@ -219,8 +219,8 @@ export function createChangeDetectorRef(view: ViewData): ChangeDetectorRef {
|
||||
export class ViewRef_ implements EmbeddedViewRef<any>, InternalViewRef {
|
||||
/** @internal */
|
||||
_view: ViewData;
|
||||
private _viewContainerRef: ViewContainerRef;
|
||||
private _appRef: ApplicationRef;
|
||||
private _viewContainerRef: ViewContainerRef|null;
|
||||
private _appRef: ApplicationRef|null;
|
||||
|
||||
constructor(_view: ViewData) {
|
||||
this._view = _view;
|
||||
@ -317,7 +317,7 @@ export function nodeValue(view: ViewData, index: number): any {
|
||||
const def = view.def.nodes[index];
|
||||
if (def.flags & NodeFlags.TypeElement) {
|
||||
const elData = asElementData(view, def.index);
|
||||
return def.element.template ? elData.template : elData.renderElement;
|
||||
return def.element !.template ? elData.template : elData.renderElement;
|
||||
} else if (def.flags & NodeFlags.TypeText) {
|
||||
return asTextData(view, def.index).renderText;
|
||||
} else if (def.flags & (NodeFlags.CatProvider | NodeFlags.TypePipe)) {
|
||||
@ -387,7 +387,7 @@ class RendererAdapter implements RendererV1 {
|
||||
|
||||
destroyView(hostElement: Element|DocumentFragment, viewAllNodes: Node[]) {
|
||||
for (let i = 0; i < viewAllNodes.length; i++) {
|
||||
this.delegate.destroyNode(viewAllNodes[i]);
|
||||
this.delegate.destroyNode !(viewAllNodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -439,4 +439,4 @@ class RendererAdapter implements RendererV1 {
|
||||
setText(renderNode: Text, text: string): void { this.delegate.setValue(renderNode, text); }
|
||||
|
||||
animate(): any { throw new Error('Renderer.animate is no longer supported!'); }
|
||||
}
|
||||
}
|
||||
|
@ -159,9 +159,9 @@ enum DebugAction {
|
||||
|
||||
let _currentAction: DebugAction;
|
||||
let _currentView: ViewData;
|
||||
let _currentNodeIndex: number;
|
||||
let _currentNodeIndex: number|null;
|
||||
|
||||
function debugSetCurrentNode(view: ViewData, nodeIndex: number) {
|
||||
function debugSetCurrentNode(view: ViewData, nodeIndex: number | null) {
|
||||
_currentView = view;
|
||||
_currentNodeIndex = nodeIndex;
|
||||
}
|
||||
@ -231,13 +231,13 @@ function debugCheckAndUpdateNode(
|
||||
const binding = nodeDef.bindings[i];
|
||||
const value = values[i];
|
||||
if (binding.flags & BindingFlags.TypeProperty) {
|
||||
bindingValues[normalizeDebugBindingName(binding.nonMinifiedName)] =
|
||||
bindingValues[normalizeDebugBindingName(binding.nonMinifiedName !)] =
|
||||
normalizeDebugBindingValue(value);
|
||||
}
|
||||
}
|
||||
const elDef = nodeDef.parent;
|
||||
const elDef = nodeDef.parent !;
|
||||
const el = asElementData(view, elDef.index).renderElement;
|
||||
if (!elDef.element.name) {
|
||||
if (!elDef.element !.name) {
|
||||
// a comment.
|
||||
view.renderer.setValue(el, `bindings=${JSON.stringify(bindingValues, null, 2)}`);
|
||||
} else {
|
||||
@ -275,37 +275,37 @@ function camelCaseToDashCase(input: string): string {
|
||||
function normalizeDebugBindingValue(value: any): string {
|
||||
try {
|
||||
// Limit the size of the value as otherwise the DOM just gets polluted.
|
||||
return value ? value.toString().slice(0, 30) : value;
|
||||
return value != null ? value.toString().slice(0, 30) : value;
|
||||
} catch (e) {
|
||||
return '[ERROR] Exception while trying to serialize the value';
|
||||
}
|
||||
}
|
||||
|
||||
function nextDirectiveWithBinding(view: ViewData, nodeIndex: number): number {
|
||||
function nextDirectiveWithBinding(view: ViewData, nodeIndex: number): number|null {
|
||||
for (let i = nodeIndex; i < view.def.nodes.length; i++) {
|
||||
const nodeDef = view.def.nodes[i];
|
||||
if (nodeDef.flags & NodeFlags.TypeDirective && nodeDef.bindings && nodeDef.bindings.length) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
return null;
|
||||
}
|
||||
|
||||
function nextRenderNodeWithBinding(view: ViewData, nodeIndex: number): number {
|
||||
function nextRenderNodeWithBinding(view: ViewData, nodeIndex: number): number|null {
|
||||
for (let i = nodeIndex; i < view.def.nodes.length; i++) {
|
||||
const nodeDef = view.def.nodes[i];
|
||||
if ((nodeDef.flags & NodeFlags.CatRenderNode) && nodeDef.bindings && nodeDef.bindings.length) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
return null;
|
||||
}
|
||||
|
||||
class DebugContext_ implements DebugContext {
|
||||
private nodeDef: NodeDef;
|
||||
private elView: ViewData;
|
||||
private elDef: NodeDef;
|
||||
constructor(public view: ViewData, public nodeIndex: number) {
|
||||
constructor(public view: ViewData, public nodeIndex: number|null) {
|
||||
if (nodeIndex == null) {
|
||||
this.nodeIndex = nodeIndex = 0;
|
||||
}
|
||||
@ -313,12 +313,12 @@ class DebugContext_ implements DebugContext {
|
||||
let elDef = this.nodeDef;
|
||||
let elView = view;
|
||||
while (elDef && (elDef.flags & NodeFlags.TypeElement) === 0) {
|
||||
elDef = elDef.parent;
|
||||
elDef = elDef.parent !;
|
||||
}
|
||||
if (!elDef) {
|
||||
while (!elDef && elView) {
|
||||
elDef = viewParentEl(elView);
|
||||
elView = elView.parent;
|
||||
elDef = viewParentEl(elView) !;
|
||||
elView = elView.parent !;
|
||||
}
|
||||
}
|
||||
this.elDef = elDef;
|
||||
@ -337,7 +337,7 @@ class DebugContext_ implements DebugContext {
|
||||
for (let i = this.elDef.index + 1; i <= this.elDef.index + this.elDef.childCount; i++) {
|
||||
const childDef = this.elView.def.nodes[i];
|
||||
if (childDef.flags & NodeFlags.CatProvider) {
|
||||
tokens.push(childDef.provider.token);
|
||||
tokens.push(childDef.provider !.token);
|
||||
}
|
||||
i += childDef.childCount;
|
||||
}
|
||||
@ -389,7 +389,7 @@ class DebugContext_ implements DebugContext {
|
||||
return NOOP;
|
||||
}
|
||||
};
|
||||
logViewDef.factory(nodeLogger);
|
||||
logViewDef.factory !(nodeLogger);
|
||||
if (currRenderNodeIndex < renderNodeIndex) {
|
||||
console.error('Illegal state: the ViewDefinitionFactory did not call the logger!');
|
||||
(<any>console.error)(...values);
|
||||
@ -408,14 +408,14 @@ function getRenderNodeIndex(viewDef: ViewDefinition, nodeIndex: number): number
|
||||
return renderNodeIndex;
|
||||
}
|
||||
|
||||
function findHostElement(view: ViewData): ElementData {
|
||||
function findHostElement(view: ViewData): ElementData|null {
|
||||
while (view && !isComponentView(view)) {
|
||||
view = view.parent;
|
||||
view = view.parent !;
|
||||
}
|
||||
if (view.parent) {
|
||||
return asElementData(view.parent, viewParentEl(view).index);
|
||||
return asElementData(view.parent, viewParentEl(view) !.index);
|
||||
}
|
||||
return undefined;
|
||||
return null;
|
||||
}
|
||||
|
||||
function collectReferences(view: ViewData, nodeDef: NodeDef, references: {[key: string]: any}) {
|
||||
@ -440,11 +440,11 @@ function callWithDebugContext(action: DebugAction, fn: any, self: any, args: any
|
||||
throw e;
|
||||
}
|
||||
_currentView.state |= ViewState.Errored;
|
||||
throw viewWrappedDebugError(e, getCurrentDebugContext());
|
||||
throw viewWrappedDebugError(e, getCurrentDebugContext() !);
|
||||
}
|
||||
}
|
||||
|
||||
export function getCurrentDebugContext(): DebugContext {
|
||||
export function getCurrentDebugContext(): DebugContext|null {
|
||||
return _currentView ? new DebugContext_(_currentView, _currentNodeIndex) : null;
|
||||
}
|
||||
|
||||
@ -452,7 +452,7 @@ export function getCurrentDebugContext(): DebugContext {
|
||||
class DebugRendererFactory2 implements RendererFactory2 {
|
||||
constructor(private delegate: RendererFactory2) {}
|
||||
|
||||
createRenderer(element: any, renderData: RendererType2): Renderer2 {
|
||||
createRenderer(element: any, renderData: RendererType2|null): Renderer2 {
|
||||
return new DebugRenderer2(this.delegate.createRenderer(element, renderData));
|
||||
}
|
||||
}
|
||||
@ -464,7 +464,7 @@ class DebugRenderer2 implements Renderer2 {
|
||||
get data() { return this.delegate.data; }
|
||||
|
||||
destroyNode(node: any) {
|
||||
removeDebugNodeFromIndex(getDebugNode(node));
|
||||
removeDebugNodeFromIndex(getDebugNode(node) !);
|
||||
if (this.delegate.destroyNode) {
|
||||
this.delegate.destroyNode(node);
|
||||
}
|
||||
@ -513,7 +513,7 @@ class DebugRenderer2 implements Renderer2 {
|
||||
insertBefore(parent: any, newChild: any, refChild: any): void {
|
||||
const debugEl = getDebugNode(parent);
|
||||
const debugChildEl = getDebugNode(newChild);
|
||||
const debugRefEl = getDebugNode(refChild);
|
||||
const debugRefEl = getDebugNode(refChild) !;
|
||||
if (debugEl && debugChildEl && debugEl instanceof DebugElement) {
|
||||
debugEl.insertBefore(debugRefEl, debugChildEl);
|
||||
}
|
||||
|
@ -14,21 +14,21 @@ export function textDef(ngContentIndex: number, constants: string[]): NodeDef {
|
||||
for (let i = 1; i < constants.length; i++) {
|
||||
bindings[i - 1] = {
|
||||
flags: BindingFlags.TypeProperty,
|
||||
name: undefined,
|
||||
ns: undefined,
|
||||
nonMinifiedName: undefined,
|
||||
securityContext: undefined,
|
||||
name: null,
|
||||
ns: null,
|
||||
nonMinifiedName: null,
|
||||
securityContext: null,
|
||||
suffix: constants[i]
|
||||
};
|
||||
}
|
||||
const flags = NodeFlags.TypeText;
|
||||
return {
|
||||
// will bet set by the view definition
|
||||
index: undefined,
|
||||
parent: undefined,
|
||||
renderParent: undefined,
|
||||
bindingIndex: undefined,
|
||||
outputIndex: undefined,
|
||||
index: -1,
|
||||
parent: null,
|
||||
renderParent: null,
|
||||
bindingIndex: -1,
|
||||
outputIndex: -1,
|
||||
// regular values
|
||||
flags,
|
||||
childFlags: 0,
|
||||
@ -40,18 +40,18 @@ export function textDef(ngContentIndex: number, constants: string[]): NodeDef {
|
||||
childCount: 0, bindings,
|
||||
bindingFlags: calcBindingFlags(bindings),
|
||||
outputs: [],
|
||||
element: undefined,
|
||||
provider: undefined,
|
||||
element: null,
|
||||
provider: null,
|
||||
text: {prefix: constants[0]},
|
||||
query: undefined,
|
||||
ngContent: undefined
|
||||
query: null,
|
||||
ngContent: null
|
||||
};
|
||||
}
|
||||
|
||||
export function createText(view: ViewData, renderHost: any, def: NodeDef): TextData {
|
||||
let renderNode: any;
|
||||
const renderer = view.renderer;
|
||||
renderNode = renderer.createText(def.text.prefix);
|
||||
renderNode = renderer.createText(def.text !.prefix);
|
||||
const parentEl = getParentRenderElement(view, renderHost, def);
|
||||
if (parentEl) {
|
||||
renderer.appendChild(parentEl, renderNode);
|
||||
@ -77,7 +77,7 @@ export function checkAndUpdateTextInline(
|
||||
if (bindLen > 9 && checkAndUpdateBinding(view, def, 9, v9)) changed = true;
|
||||
|
||||
if (changed) {
|
||||
let value = def.text.prefix;
|
||||
let value = def.text !.prefix;
|
||||
if (bindLen > 0) value += _addInterpolationPart(v0, bindings[0]);
|
||||
if (bindLen > 1) value += _addInterpolationPart(v1, bindings[1]);
|
||||
if (bindLen > 2) value += _addInterpolationPart(v2, bindings[2]);
|
||||
@ -109,7 +109,7 @@ export function checkAndUpdateTextDynamic(view: ViewData, def: NodeDef, values:
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
value = value + _addInterpolationPart(values[i], bindings[i]);
|
||||
}
|
||||
value = def.text.prefix + value;
|
||||
value = def.text !.prefix + value;
|
||||
const renderNode = asTextData(view, def.index).renderText;
|
||||
view.renderer.setValue(renderNode, value);
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ import {Sanitizer, SecurityContext} from '../security';
|
||||
// -------------------------------------
|
||||
|
||||
export interface ViewDefinition {
|
||||
factory: ViewDefinitionFactory;
|
||||
factory: ViewDefinitionFactory|null;
|
||||
flags: ViewFlags;
|
||||
updateDirectives: ViewUpdateFn;
|
||||
updateRenderer: ViewUpdateFn;
|
||||
@ -32,7 +32,7 @@ export interface ViewDefinition {
|
||||
/** aggregated NodeFlags for all nodes **/
|
||||
nodeFlags: NodeFlags;
|
||||
rootNodeFlags: NodeFlags;
|
||||
lastRenderRootNode: NodeDef;
|
||||
lastRenderRootNode: NodeDef|null;
|
||||
bindingCount: number;
|
||||
outputCount: number;
|
||||
/**
|
||||
@ -91,8 +91,8 @@ export const enum ViewFlags {
|
||||
export interface NodeDef {
|
||||
flags: NodeFlags;
|
||||
index: number;
|
||||
parent: NodeDef;
|
||||
renderParent: NodeDef;
|
||||
parent: NodeDef|null;
|
||||
renderParent: NodeDef|null;
|
||||
/** this is checked against NgContentDef.index to find matched nodes */
|
||||
ngContentIndex: number;
|
||||
/** number of transitive children */
|
||||
@ -123,11 +123,11 @@ export interface NodeDef {
|
||||
* Used as a bloom filter.
|
||||
*/
|
||||
childMatchedQueries: number;
|
||||
element: ElementDef;
|
||||
provider: ProviderDef;
|
||||
text: TextDef;
|
||||
query: QueryDef;
|
||||
ngContent: NgContentDef;
|
||||
element: ElementDef|null;
|
||||
provider: ProviderDef|null;
|
||||
text: TextDef|null;
|
||||
query: QueryDef|null;
|
||||
ngContent: NgContentDef|null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -179,11 +179,11 @@ export const enum NodeFlags {
|
||||
|
||||
export interface BindingDef {
|
||||
flags: BindingFlags;
|
||||
ns: string;
|
||||
name: string;
|
||||
nonMinifiedName: string;
|
||||
securityContext: SecurityContext;
|
||||
suffix: string;
|
||||
ns: string|null;
|
||||
name: string|null;
|
||||
nonMinifiedName: string|null;
|
||||
securityContext: SecurityContext|null;
|
||||
suffix: string|null;
|
||||
}
|
||||
|
||||
export const enum BindingFlags {
|
||||
@ -201,9 +201,9 @@ export const enum BindingFlags {
|
||||
|
||||
export interface OutputDef {
|
||||
type: OutputType;
|
||||
target: 'window'|'document'|'body'|'component';
|
||||
target: 'window'|'document'|'body'|'component'|null;
|
||||
eventName: string;
|
||||
propName: string;
|
||||
propName: string|null;
|
||||
}
|
||||
|
||||
export const enum OutputType {ElementOutput, DirectiveOutput}
|
||||
@ -217,26 +217,26 @@ export const enum QueryValueType {
|
||||
}
|
||||
|
||||
export interface ElementDef {
|
||||
name: string;
|
||||
ns: string;
|
||||
name: string|null;
|
||||
ns: string|null;
|
||||
/** ns, name, value */
|
||||
attrs: [string, string, string][];
|
||||
template: ViewDefinition;
|
||||
componentProvider: NodeDef;
|
||||
componentRendererType: RendererType2;
|
||||
attrs: [string, string, string][]|null;
|
||||
template: ViewDefinition|null;
|
||||
componentProvider: NodeDef|null;
|
||||
componentRendererType: RendererType2|null;
|
||||
// closure to allow recursive components
|
||||
componentView: ViewDefinitionFactory;
|
||||
componentView: ViewDefinitionFactory|null;
|
||||
/**
|
||||
* visible public providers for DI in the view,
|
||||
* as see from this element. This does not include private providers.
|
||||
*/
|
||||
publicProviders: {[tokenKey: string]: NodeDef};
|
||||
publicProviders: {[tokenKey: string]: NodeDef}|null;
|
||||
/**
|
||||
* same as visiblePublicProviders, but also includes private providers
|
||||
* that are located on this element.
|
||||
*/
|
||||
allProviders: {[tokenKey: string]: NodeDef};
|
||||
handleEvent: ElementHandleEventFn;
|
||||
allProviders: {[tokenKey: string]: NodeDef}|null;
|
||||
handleEvent: ElementHandleEventFn|null;
|
||||
}
|
||||
|
||||
export interface ElementHandleEventFn { (view: ViewData, eventName: string, event: any): boolean; }
|
||||
@ -303,9 +303,9 @@ export interface ViewData {
|
||||
root: RootData;
|
||||
renderer: Renderer2;
|
||||
// index of component provider / anchor.
|
||||
parentNodeDef: NodeDef;
|
||||
parent: ViewData;
|
||||
viewContainerParent: ViewData;
|
||||
parentNodeDef: NodeDef|null;
|
||||
parent: ViewData|null;
|
||||
viewContainerParent: ViewData|null;
|
||||
component: any;
|
||||
context: any;
|
||||
// Attention: Never loop over this, as this will
|
||||
@ -316,7 +316,7 @@ export interface ViewData {
|
||||
nodes: {[key: number]: NodeData};
|
||||
state: ViewState;
|
||||
oldValues: any[];
|
||||
disposables: DisposableFn[];
|
||||
disposables: DisposableFn[]|null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -366,7 +366,7 @@ export function asTextData(view: ViewData, index: number): TextData {
|
||||
export interface ElementData {
|
||||
renderElement: any;
|
||||
componentView: ViewData;
|
||||
viewContainer: ViewContainerData;
|
||||
viewContainer: ViewContainerData|null;
|
||||
template: TemplateData;
|
||||
}
|
||||
|
||||
@ -434,7 +434,7 @@ export interface RootData {
|
||||
|
||||
export abstract class DebugContext {
|
||||
abstract get view(): ViewData;
|
||||
abstract get nodeIndex(): number;
|
||||
abstract get nodeIndex(): number|null;
|
||||
abstract get injector(): Injector;
|
||||
abstract get component(): any;
|
||||
abstract get providerTokens(): any[];
|
||||
@ -461,7 +461,7 @@ export interface Services {
|
||||
checkNoChangesView(view: ViewData): void;
|
||||
destroyView(view: ViewData): void;
|
||||
resolveDep(
|
||||
view: ViewData, elDef: NodeDef, allowPrivateServices: boolean, depDef: DepDef,
|
||||
view: ViewData, elDef: NodeDef|null, allowPrivateServices: boolean, depDef: DepDef,
|
||||
notFoundValue?: any): any;
|
||||
createDebugContext(view: ViewData, nodeIndex: number): DebugContext;
|
||||
handleEvent: ViewHandleEventFn;
|
||||
@ -475,16 +475,16 @@ export interface Services {
|
||||
* debug mode can hook it. It is lazily filled when `isDevMode` is known.
|
||||
*/
|
||||
export const Services: Services = {
|
||||
setCurrentNode: undefined,
|
||||
createRootView: undefined,
|
||||
createEmbeddedView: undefined,
|
||||
checkAndUpdateView: undefined,
|
||||
checkNoChangesView: undefined,
|
||||
destroyView: undefined,
|
||||
resolveDep: undefined,
|
||||
createDebugContext: undefined,
|
||||
handleEvent: undefined,
|
||||
updateDirectives: undefined,
|
||||
updateRenderer: undefined,
|
||||
dirtyParentQueries: undefined,
|
||||
setCurrentNode: undefined !,
|
||||
createRootView: undefined !,
|
||||
createEmbeddedView: undefined !,
|
||||
checkAndUpdateView: undefined !,
|
||||
checkNoChangesView: undefined !,
|
||||
destroyView: undefined !,
|
||||
resolveDep: undefined !,
|
||||
createDebugContext: undefined !,
|
||||
handleEvent: undefined !,
|
||||
updateDirectives: undefined !,
|
||||
updateRenderer: undefined !,
|
||||
dirtyParentQueries: undefined !,
|
||||
};
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user