Compare commits

...

29 Commits
4.0.2 ... 4.0.3

Author SHA1 Message Date
6ccb93728e docs: add changelog for 4.0.3 2017-04-20 23:41:41 -05:00
b54a957ef4 release: cut the 4.0.3 release 2017-04-20 23:39:36 -05:00
21c14a79fa fix(compiler): suppress another closure warning (#16137)
closure compiler warns in generated .ngfactory.ts files:
```
WARNING - property createInternal already defined on superclass module$contents$..$core$src$linker$ng_module_factory_NgModuleInjector; use @override to override it
```

PR Close #16137
2017-04-20 23:39:36 -05:00
eed2d456d7 refactor(tsc-wrapped): cleanup (#16178)
PR Close #16178
2017-04-20 23:39:36 -05:00
719c232321 fix(core): distribute externs for testability API (#16179)
Workaround for #11119

PR Close #16179
2017-04-20 23:39:36 -05:00
bd033b69c1 feat: add support for TS 2.3 2017-04-20 23:39:36 -05:00
5b92f7ee6d fix(core): benchmarks - enable ng1 benchmark again
Also make it match the ng2 benchmark.
2017-04-20 23:39:36 -05:00
20ffb4196d fix(benchpress): chrome - prevent trace buffer overflow 2017-04-20 23:39:36 -05:00
11e6f45387 build: bump protractor to 4.0.14 2017-04-20 23:39:36 -05:00
a30a73c2aa fix(compiler): ignore calls to unresolved symbols in metadata
This only shows up in the language service. Calls to symbols
that are not resolve resulted in null instead of being resolved
causing the language service to see exceptions when the null
was not expected such as in the animations array.

Fixes #15969
2017-04-20 23:39:36 -05:00
fb94c9937c feat(upgrade): fixes for allow setting the angularjs lib at runtime
- always have a value for `angular`, even if no angular is on the page
- use `const` instead of `function` to allow to export a variable `module`
  without breaking tsickle / closure.
2017-04-20 23:39:36 -05:00
b3259e26fe feat(upgrade): allow setting the angularjs lib at runtime (#15168)
Readds 8ad464d90e.
2017-04-20 23:39:36 -05:00
be406f8464 fix(compiler): Inform user where Quoted error was thrown 2017-04-20 23:39:36 -05:00
5cab3b146e fix(router): prevent RouterLinkActive from causing an infinite CD loop
fixes #15825
2017-04-20 23:39:36 -05:00
eff6216319 fix(language-service): only use canonical symbols
Language service was treating some alias TypeScript symbols as if
they where the canonical symbol. If the symbol in scope is an alias
of another symbol the symbol should be converted to the canonical
symbol.
2017-04-20 23:39:35 -05:00
88b2ab09ea Revert "fix(compiler): ignore calls to unresolved symbols in metadata (#15970)"
This reverts commit ce47d33cd9.
2017-04-20 23:39:35 -05:00
5ad6d55a45 Revert "feat(upgrade): allow setting the angularjs lib at runtime (#15168)"
This reverts commit 8ad464d90e.

Breaks G3.
2017-04-20 23:39:35 -05:00
c81c7c0920 fix(compiler): fix build error in xliff2 2017-04-20 23:39:35 -05:00
6f8e23b061 refactor(router): drop the InternalRoute interface 2017-04-20 23:39:35 -05:00
dc9711107d refactor(router): misc refactoring 2017-04-20 23:39:35 -05:00
4c0c1e5fa0 feat(compiler): Implement i18n XLIFF 2.0 serializer (#14185)
- Ensure that the result passes OASIS XLIFF 2.0 schema validation
- Use <ph/> for self-closing placeholder tags
- Use <pc></pc> for other placeholder tags
- Check for the correct XLIFF file version
- Add ICU support

fixes #11735
2017-04-20 23:39:35 -05:00
06ef0908c7 feat(upgrade): allow setting the angularjs lib at runtime (#15168)
This PR adds an ability to reset the angularjs library, which is often needed when Angular
is loaded lazily using RequireJS.
2017-04-20 23:39:35 -05:00
a893d5e0a9 fix(packaging): increased buffer size (#15840) 2017-04-20 23:39:35 -05:00
44afa5a03a fix(core): key-value differ changes iteration (#15968)
fixes #14997
2017-04-20 23:39:35 -05:00
0733f8d3e1 fix(compiler): ignore calls to unresolved symbols in metadata (#15970)
This only shows up in the language service. Calls to symbols
that are not resolve resulted in null instead of being resolved
causing the language service to see exceptions when the null
was not expected such as in the animations array.

Fixes #15969
2017-04-20 23:39:35 -05:00
6604a4dc40 refactor(compiler): cleanup (#15960) 2017-04-20 23:39:35 -05:00
96c63a92e2 fix(platform-server): handle innerText (#15818) 2017-04-20 23:39:35 -05:00
c444b1575f fix(tsc-wrapped): collect new expressions with no arguments (#15908)
Fixes #15906
2017-04-20 23:39:35 -05:00
2b676192f6 docs: add changelog for 4.1.0-beta.1 2017-04-20 23:39:34 -05:00
64 changed files with 1724 additions and 485 deletions

View File

@ -1,3 +1,63 @@
<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)

View File

@ -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

View File

@ -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

View File

@ -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) {};

View File

@ -0,0 +1,81 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {$} 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,
}
];

View File

@ -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
});
}

View File

@ -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('');
}
});

View 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>

View 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();

View 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); }
]);
}

View File

@ -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
View File

@ -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",

View File

@ -1,6 +1,6 @@
{
"name": "angular-srcs",
"version": "4.0.2",
"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",

View File

@ -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}');`);
}

View File

@ -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((_) => {

View File

@ -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();

View File

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

View File

@ -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 */

View File

@ -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(

View File

@ -584,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']) {

View File

@ -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 {

View File

@ -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:

View File

@ -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';

View 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';
}
}

View File

@ -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[] = [];

View File

@ -546,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) {}

View File

@ -436,6 +436,9 @@ export class MockStaticSymbolResolverHost implements StaticSymbolResolverHost {
}
return baseName + '.d.ts';
}
if (modulePath == 'unresolved') {
return undefined;
}
return '/tmp/' + modulePath + '.d.ts';
}

View 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="&lt;b&gt;" dispEnd="&lt;/b&gt;">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="&lt;p&gt;" dispEnd="&lt;/p&gt;">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="&lt;b&gt;" dispEnd="&lt;/b&gt;"><pc id="1" equivStart="START_UNDERLINED_TEXT" equivEnd="CLOSE_UNDERLINED_TEXT" type="fmt" dispStart="&lt;u&gt;" dispEnd="&lt;/u&gt;"><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="&lt;br/&gt;"/><ph id="1" equiv="TAG_IMG" type="image" disp="&lt;img/&gt;"/><ph id="2" equiv="TAG_IMG_1" type="image" disp="&lt;img/&gt;"/></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="&lt;span&gt;" dispEnd="&lt;/span&gt;"></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="&lt;p&gt;" dispEnd="&lt;/p&gt;">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="&lt;p&gt;" dispEnd="&lt;/p&gt;">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="&lt;b&gt;" dispEnd="&lt;/b&gt;">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="&lt;b&gt;" dispEnd="&lt;/b&gt;">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="&lt;p&gt;" dispEnd="&lt;/p&gt;">test</pc>} }</source>
<target>{VAR_PLURAL, plural, =0 {<pc id="0" equivStart="START_PARAGRAPH" equivEnd="CLOSE_PARAGRAPH" type="other" dispStart="&lt;p&gt;" dispEnd="&lt;/p&gt;">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="&lt;b&gt;" dispEnd="&lt;/b&gt;"><pc id="1" equivStart="START_UNDERLINED_TEXT" equivEnd="CLOSE_UNDERLINED_TEXT" type="fmt" dispStart="&lt;u&gt;" dispEnd="&lt;/u&gt;"><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="&lt;b&gt;" dispEnd="&lt;/b&gt;"><pc id="1" equivStart="START_UNDERLINED_TEXT" equivEnd="CLOSE_UNDERLINED_TEXT" type="fmt" dispStart="&lt;u&gt;" dispEnd="&lt;/u&gt;">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="&lt;br/&gt;"/><ph id="1" equiv="TAG_IMG" type="image" disp="&lt;img/&gt;"/><ph id="2" equiv="TAG_IMG_1" type="image" disp="&lt;img/&gt;"/></source>
<target><ph id="2" equiv="TAG_IMG_1" type="image" disp="&lt;img/&gt;"/><ph id="1" equiv="TAG_IMG" type="image" disp="&lt;img/&gt;"/><ph id="0" equiv="LINE_BREAK" type="fmt" disp="&lt;br/&gt;"/></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="&lt;span&gt;" dispEnd="&lt;/span&gt;"></pc></source>
<target><pc id="0" equivStart="START_TAG_SPAN" equivEnd="CLOSE_TAG_SPAN" type="other" dispStart="&lt;span&gt;" dispEnd="&lt;/span&gt;"></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="&lt;p&gt;" dispEnd="&lt;/p&gt;">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="&lt;p&gt;" dispEnd="&lt;/p&gt;">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="&lt;p&gt;" dispEnd="&lt;/p&gt;">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="&lt;p&gt;" dispEnd="&lt;/p&gt;">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`)));
});
});
});
}

View File

@ -120,7 +120,6 @@ export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyVal
}
this._removalsHead = insertBefore;
this._removalsTail = insertBefore;
for (let record = insertBefore; record !== null; record = record._nextRemoved) {
if (record === this._mapHead) {
@ -135,6 +134,10 @@ export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyVal
}
}
// 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;
}
@ -222,7 +225,7 @@ export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyVal
this._changesHead = this._changesTail = null;
this._additionsHead = this._additionsTail = null;
this._removalsHead = this._removalsTail = null;
this._removalsHead = null;
}
}
@ -254,28 +257,17 @@ export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyVal
}
toString(): string {
const items: any[] = [];
const previous: any[] = [];
const changes: any[] = [];
const additions: any[] = [];
const removals: any[] = [];
let record: KeyValueChangeRecord_<K, V>|null;
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' +

View 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) {};

View File

@ -194,6 +194,19 @@ export function main() {
}));
});
// https://github.com/angular/angular/issues/14997
it('should work regardless key order', () => {
differ.check({a: 1, b: 2});
differ.check({b: 3, a: 2});
differ.check({a: 1, b: 2});
expect(differ.toString()).toEqual(kvChangesAsString({
map: ['a[2->1]', 'b[3->2]'],
previous: ['b[3->2]', 'a[2->1]'],
changes: ['a[2->1]', 'b[3->2]']
}));
});
it('should when the first item is moved', () => {
differ.check({a: 'a', b: 'b'});
differ.check({c: 'c', a: 'a'});

View File

@ -8,12 +8,12 @@
import {CompileMetadataResolver, CompileNgModuleMetadata, CompilerConfig, DomElementSchemaRegistry, HtmlParser, I18NHtmlParser, Lexer, NgAnalyzedModules, Parser, TemplateParser} from '@angular/compiler';
import {AstResult, AttrInfo, TemplateInfo} from './common';
import {AstResult, TemplateInfo} from './common';
import {getTemplateCompletions} from './completions';
import {getDefinition} from './definitions';
import {getDeclarationDiagnostics, getTemplateDiagnostics} from './diagnostics';
import {getHover} from './hover';
import {Completion, CompletionKind, Completions, Declaration, Declarations, Definition, Diagnostic, DiagnosticKind, Diagnostics, Hover, LanguageService, LanguageServiceHost, Location, PipeInfo, Pipes, Signature, Span, Symbol, SymbolDeclaration, SymbolQuery, SymbolTable, TemplateSource, TemplateSources} from './types';
import {Completions, Definition, Diagnostic, DiagnosticKind, Diagnostics, Hover, LanguageService, LanguageServiceHost, Pipes, Span, TemplateSource} from './types';
/**
@ -126,8 +126,7 @@ class LanguageServiceImpl implements LanguageService {
const pipes = ngModule.transitiveModule.pipes.map(
p => this.host.resolver.getOrLoadPipeMetadata(p.reference).toSummary());
const schemas = ngModule.schemas;
const parseResult = parser.tryParseHtml(
htmlResult, metadata, template.source, directives, pipes, schemas, '');
const parseResult = parser.tryParseHtml(htmlResult, metadata, directives, pipes, schemas);
result = {
htmlAst: htmlResult.rootNodes,
templateAst: parseResult.templateAst,

View File

@ -191,7 +191,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
let sourceFile = this.getSourceFile(fileName);
if (sourceFile) {
this.context = sourceFile.path;
this.context = (sourceFile as any).path || sourceFile.fileName;
ts.forEachChild(sourceFile, visit);
}
return result.length ? result : undefined;
@ -807,10 +807,15 @@ class TypeWrapper implements Symbol {
}
class SymbolWrapper implements Symbol {
private symbol: ts.Symbol;
private _tsType: ts.Type;
private _members: SymbolTable;
constructor(private symbol: ts.Symbol, private context: TypeContext) {}
constructor(symbol: ts.Symbol, private context: TypeContext) {
this.symbol = symbol && context && (symbol.flags & ts.SymbolFlags.Alias) ?
context.checker.getAliasedSymbol(symbol) :
symbol;
}
get name(): string { return this.symbol.name; }

View File

@ -80,6 +80,8 @@ export class Parse5DomAdapter extends DomAdapter {
setProperty(el: any, name: string, value: any) {
if (name === 'innerHTML') {
this.setInnerHTML(el, value);
} else if (name === 'innerText') {
this.setText(el, value);
} else if (name === 'className') {
el.attribs['class'] = el.className = value;
} else {

View File

@ -54,15 +54,19 @@ class TitleApp {
class TitleAppModule {
}
@Component({selector: 'app', template: '{{text}}'})
@Component({selector: 'app', template: '{{text}}<h1 [innerText]="h1"></h1>'})
class MyAsyncServerApp {
text = '';
h1 = '';
@HostListener('window:scroll')
track() { console.error('scroll'); }
ngOnInit() {
Promise.resolve(null).then(() => setTimeout(() => { this.text = 'Works!'; }, 10));
Promise.resolve(null).then(() => setTimeout(() => {
this.text = 'Works!';
this.h1 = 'fine';
}, 10));
}
}
@ -353,7 +357,7 @@ export function main() {
let doc: string;
let called: boolean;
let expectedOutput =
'<html><head></head><body><app ng-version="0.0.0-PLACEHOLDER">Works!</app></body></html>';
'<html><head></head><body><app ng-version="0.0.0-PLACEHOLDER">Works!<h1 innerText="fine">fine</h1></app></body></html>';
beforeEach(() => {
// PlatformConfig takes in a parsed document so that it can be cached across requests.

View File

@ -10,6 +10,18 @@ import {Injectable, RenderComponentType, RendererType2, Type, ɵstringify as str
import {RenderStore} from './render_store';
/**
* @experimental WebWorker support in Angular is currently experimental.
*/
export const enum SerializerTypes {
// RendererType2
RENDERER_TYPE_2,
// Primitive types
PRIMITIVE,
// An object stored in a RenderStore
RENDER_STORE_OBJECT,
}
/**
* Any type that does not need to be serialized (string, number, boolean)
*
@ -25,18 +37,6 @@ export class LocationType {
public origin: string) {}
}
/**
* @experimental WebWorker support in Angular is currently experimental.
*/
export const enum SerializerTypes {
// RendererType2
RENDERER_TYPE_2,
// Primitive types
PRIMITIVE,
// An object stored in a RenderStore
RENDER_STORE_OBJECT,
}
@Injectable()
export class Serializer {
constructor(private _renderStore: RenderStore) {}

View File

@ -18,8 +18,8 @@ import {map} from 'rxjs/operator/map';
import {mergeMap} from 'rxjs/operator/mergeMap';
import {EmptyError} from 'rxjs/util/EmptyError';
import {InternalRoute, Route, Routes} from './config';
import {LoadedRouterConfig, RouterConfigLoader} from './router_config_loader';
import {LoadedRouterConfig, Route, Routes} from './config';
import {RouterConfigLoader} from './router_config_loader';
import {PRIMARY_OUTLET, Params, defaultUrlMatcher, navigationCancelingError} from './shared';
import {UrlSegment, UrlSegmentGroup, UrlSerializer, UrlTree} from './url_tree';
import {andObservables, forEach, waitForMap, wrapIntoObservable} from './utils/collection';
@ -247,7 +247,7 @@ class ApplyRedirects {
}
private matchSegmentAgainstRoute(
ngModule: NgModuleRef<any>, rawSegmentGroup: UrlSegmentGroup, route: InternalRoute,
ngModule: NgModuleRef<any>, rawSegmentGroup: UrlSegmentGroup, route: Route,
segments: UrlSegment[]): Observable<UrlSegmentGroup> {
if (route.path === '**') {
if (route.loadChildren) {
@ -292,8 +292,7 @@ class ApplyRedirects {
});
}
private getChildConfig(ngModule: NgModuleRef<any>, route: InternalRoute):
Observable<LoadedRouterConfig> {
private getChildConfig(ngModule: NgModuleRef<any>, route: Route): Observable<LoadedRouterConfig> {
if (route.children) {
// The children belong to the same module
return of (new LoadedRouterConfig(route.children, ngModule));

View File

@ -6,13 +6,11 @@
* found in the LICENSE file at https://angular.io/license
*/
import {NgModuleFactory, Type} from '@angular/core';
import {NgModuleFactory, NgModuleRef, Type} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import {PRIMARY_OUTLET} from './shared';
import {UrlSegment, UrlSegmentGroup} from './url_tree';
/**
* @whatItDoes Represents router configuration.
*
@ -359,11 +357,15 @@ export interface Route {
children?: Routes;
loadChildren?: LoadChildren;
runGuardsAndResolvers?: RunGuardsAndResolvers;
/**
* Filled for routes with `loadChildren` once the module has been loaded
* @internal
*/
_loadedConfig?: LoadedRouterConfig;
}
export interface InternalRoute extends Route {
// `LoadedRouterConfig` loaded via a Route `loadChildren`
_loadedConfig?: any;
export class LoadedRouterConfig {
constructor(public routes: Route[], public module: NgModuleRef<any>) {}
}
export function validateConfig(config: Routes, parentPath: string = ''): void {

View File

@ -123,10 +123,9 @@ export class RouterLinkActive implements OnChanges,
// react only when status has changed to prevent unnecessary dom updates
if (this.active !== hasActiveLinks) {
this.active = hasActiveLinks;
this.classes.forEach(
c => this.renderer.setElementClass(this.element.nativeElement, c, hasActiveLinks));
this.cdr.detectChanges();
Promise.resolve(hasActiveLinks).then(active => this.active = active);
}
}

View File

@ -11,7 +11,7 @@ import {Observable} from 'rxjs/Observable';
import {Observer} from 'rxjs/Observer';
import {of } from 'rxjs/observable/of';
import {Data, InternalRoute, ResolveData, Route, Routes} from './config';
import {Data, ResolveData, Route, Routes} from './config';
import {ActivatedRouteSnapshot, RouterStateSnapshot, inheritedParamsDataResolve} from './router_state';
import {PRIMARY_OUTLET, defaultUrlMatcher} from './shared';
import {UrlSegment, UrlSegmentGroup, UrlTree, mapChildrenIntoArray} from './url_tree';
@ -154,7 +154,7 @@ function sortActivatedRouteSnapshots(nodes: TreeNode<ActivatedRouteSnapshot>[]):
});
}
function getChildConfig(route: InternalRoute): Route[] {
function getChildConfig(route: Route): Route[] {
if (route.children) {
return route.children;
}

View File

@ -22,14 +22,14 @@ import {mergeMap} from 'rxjs/operator/mergeMap';
import {reduce} from 'rxjs/operator/reduce';
import {applyRedirects} from './apply_redirects';
import {InternalRoute, QueryParamsHandling, ResolveData, Route, Routes, RunGuardsAndResolvers, validateConfig} from './config';
import {LoadedRouterConfig, QueryParamsHandling, ResolveData, Route, Routes, RunGuardsAndResolvers, validateConfig} from './config';
import {createRouterState} from './create_router_state';
import {createUrlTree} from './create_url_tree';
import {RouterOutlet} from './directives/router_outlet';
import {Event, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, RouteConfigLoadEnd, RouteConfigLoadStart, RoutesRecognized} from './events';
import {recognize} from './recognize';
import {DetachedRouteHandle, DetachedRouteHandleInternal, RouteReuseStrategy} from './route_reuse_strategy';
import {LoadedRouterConfig, RouterConfigLoader} from './router_config_loader';
import {RouterConfigLoader} from './router_config_loader';
import {RouterOutletMap} from './router_outlet_map';
import {ActivatedRoute, ActivatedRouteSnapshot, RouterState, RouterStateSnapshot, advanceActivatedRoute, createEmptyState, equalParamsAndUrlSegments, inheritedParamsDataResolve} from './router_state';
import {PRIMARY_OUTLET, Params, isNavigationCancelingError} from './shared';
@ -1168,7 +1168,7 @@ function advanceActivatedRouteNodeAndItsChildren(node: TreeNode<ActivatedRoute>)
function parentLoadedConfig(snapshot: ActivatedRouteSnapshot): LoadedRouterConfig {
for (let s = snapshot.parent; s; s = s.parent) {
const route: InternalRoute = s._routeConfig;
const route = s._routeConfig;
if (route && route._loadedConfig) return route._loadedConfig;
if (route && route.component) return null;
}
@ -1180,7 +1180,7 @@ function closestLoadedConfig(snapshot: ActivatedRouteSnapshot): LoadedRouterConf
if (!snapshot) return null;
for (let s = snapshot.parent; s; s = s.parent) {
const route: InternalRoute = s._routeConfig;
const route = s._routeConfig;
if (route && route._loadedConfig) return route._loadedConfig;
}

View File

@ -12,7 +12,7 @@ import {fromPromise} from 'rxjs/observable/fromPromise';
import {of } from 'rxjs/observable/of';
import {map} from 'rxjs/operator/map';
import {mergeMap} from 'rxjs/operator/mergeMap';
import {LoadChildren, Route} from './config';
import {LoadChildren, LoadedRouterConfig, Route} from './config';
import {flatten, wrapIntoObservable} from './utils/collection';
/**
@ -21,10 +21,6 @@ import {flatten, wrapIntoObservable} from './utils/collection';
*/
export const ROUTES = new InjectionToken<Route[][]>('ROUTES');
export class LoadedRouterConfig {
constructor(public routes: Route[], public module: NgModuleRef<any>) {}
}
export class RouterConfigLoader {
constructor(
private loader: NgModuleFactoryLoader, private compiler: Compiler,

View File

@ -6,7 +6,7 @@
*found in the LICENSE file at https://angular.io/license
*/
import {Compiler, Injectable, Injector, NgModuleFactoryLoader, NgModuleRef} from '@angular/core';
import {Compiler, Injectable, Injector, NgModuleFactoryLoader, NgModuleRef, OnDestroy} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import {Subscription} from 'rxjs/Subscription';
import {from} from 'rxjs/observable/from';
@ -16,8 +16,8 @@ import {concatMap} from 'rxjs/operator/concatMap';
import {filter} from 'rxjs/operator/filter';
import {mergeAll} from 'rxjs/operator/mergeAll';
import {mergeMap} from 'rxjs/operator/mergeMap';
import {InternalRoute, Route, Routes} from './config';
import {NavigationEnd, RouteConfigLoadEnd, RouteConfigLoadStart} from './events';
import {LoadedRouterConfig, Route, Routes} from './config';
import {Event, NavigationEnd, RouteConfigLoadEnd, RouteConfigLoadStart} from './events';
import {Router} from './router';
import {RouterConfigLoader} from './router_config_loader';
@ -73,7 +73,7 @@ export class NoPreloading implements PreloadingStrategy {
* @stable
*/
@Injectable()
export class RouterPreloader {
export class RouterPreloader implements OnDestroy {
private loader: RouterConfigLoader;
private subscription: Subscription;
@ -87,8 +87,8 @@ export class RouterPreloader {
};
setUpPreloading(): void {
const navigations = filter.call(this.router.events, (e: any) => e instanceof NavigationEnd);
this.subscription = concatMap.call(navigations, () => this.preload()).subscribe(() => {});
const navigations$ = filter.call(this.router.events, (e: Event) => e instanceof NavigationEnd);
this.subscription = concatMap.call(navigations$, () => this.preload()).subscribe(() => {});
}
preload(): Observable<any> {
@ -100,8 +100,7 @@ export class RouterPreloader {
private processRoutes(ngModule: NgModuleRef<any>, routes: Routes): Observable<void> {
const res: Observable<any>[] = [];
for (const r of routes) {
const route: InternalRoute = r;
for (const route of routes) {
// we already have the config loaded, just recurse
if (route.loadChildren && !route.canLoad && route._loadedConfig) {
const childConfig = route._loadedConfig;
@ -119,10 +118,10 @@ export class RouterPreloader {
return mergeAll.call(from(res));
}
private preloadConfig(ngModule: NgModuleRef<any>, route: InternalRoute): Observable<void> {
private preloadConfig(ngModule: NgModuleRef<any>, route: Route): Observable<void> {
return this.preloadingStrategy.preload(route, () => {
const loaded = this.loader.load(ngModule.injector, route);
return mergeMap.call(loaded, (config: any): any => {
const loaded$ = this.loader.load(ngModule.injector, route);
return mergeMap.call(loaded$, (config: LoadedRouterConfig) => {
route._loadedConfig = config;
return this.processRoutes(config.module, config.routes);
});

View File

@ -12,8 +12,7 @@ import {Observable} from 'rxjs/Observable';
import {of } from 'rxjs/observable/of';
import {applyRedirects} from '../src/apply_redirects';
import {Routes} from '../src/config';
import {LoadedRouterConfig} from '../src/router_config_loader';
import {LoadedRouterConfig, Routes} from '../src/config';
import {DefaultUrlSerializer, UrlSegmentGroup, UrlTree, equalSegments} from '../src/url_tree';
describe('applyRedirects', () => {
@ -163,12 +162,12 @@ describe('applyRedirects', () => {
return of (loadedConfig);
}
};
const config = [{path: 'a', component: ComponentA, loadChildren: 'children'}];
const config: Routes = [{path: 'a', component: ComponentA, loadChildren: 'children'}];
applyRedirects(testModule.injector, <any>loader, serializer, tree('a/b'), config)
.forEach(r => {
compareTrees(r, tree('/a/b'));
expect((<any>config[0])._loadedConfig).toBe(loadedConfig);
expect(config[0]._loadedConfig).toBe(loadedConfig);
});
});
@ -290,12 +289,12 @@ describe('applyRedirects', () => {
const loader = {load: (injector: any, p: any) => of (loadedConfig)};
const config =
const config: Routes =
[{path: '', pathMatch: 'full', redirectTo: '/a'}, {path: 'a', loadChildren: 'children'}];
applyRedirects(testModule.injector, <any>loader, serializer, tree(''), config).forEach(r => {
compareTrees(r, tree('a'));
expect((<any>config[1])._loadedConfig).toBe(loadedConfig);
expect(config[1]._loadedConfig).toBe(loadedConfig);
});
});
@ -311,7 +310,7 @@ describe('applyRedirects', () => {
}
};
const config = [{path: 'a', loadChildren: 'children'}];
const config: Routes = [{path: 'a', loadChildren: 'children'}];
applyRedirects(testModule.injector, <any>loader, serializer, tree('a?k1'), config)
.subscribe(r => {});
@ -320,7 +319,7 @@ describe('applyRedirects', () => {
.subscribe(
r => {
compareTrees(r, tree('a?k2'));
expect((<any>config[0])._loadedConfig).toBe(loadedConfig);
expect(config[0]._loadedConfig).toBe(loadedConfig);
},
(e) => { throw 'Should not reach'; });
});
@ -330,10 +329,10 @@ describe('applyRedirects', () => {
const loader = {load: (injector: any, p: any) => of (loadedConfig)};
const config = [{path: '**', loadChildren: 'children'}];
const config: Routes = [{path: '**', loadChildren: 'children'}];
applyRedirects(testModule.injector, <any>loader, serializer, tree('xyz'), config)
.forEach(r => { expect((<any>config[0])._loadedConfig).toBe(loadedConfig); });
.forEach(r => { expect(config[0]._loadedConfig).toBe(loadedConfig); });
});
it('should load the configuration after a local redirect from a wildcard route', () => {
@ -341,11 +340,11 @@ describe('applyRedirects', () => {
const loader = {load: (injector: any, p: any) => of (loadedConfig)};
const config =
const config: Routes =
[{path: 'not-found', loadChildren: 'children'}, {path: '**', redirectTo: 'not-found'}];
applyRedirects(testModule.injector, <any>loader, serializer, tree('xyz'), config)
.forEach(r => { expect((<any>config[0])._loadedConfig).toBe(loadedConfig); });
.forEach(r => { expect(config[0]._loadedConfig).toBe(loadedConfig); });
});
it('should load the configuration after an absolute redirect from a wildcard route', () => {
@ -353,11 +352,11 @@ describe('applyRedirects', () => {
const loader = {load: (injector: any, p: any) => of (loadedConfig)};
const config =
const config: Routes =
[{path: 'not-found', loadChildren: 'children'}, {path: '**', redirectTo: '/not-found'}];
applyRedirects(testModule.injector, <any>loader, serializer, tree('xyz'), config)
.forEach(r => { expect((<any>config[0])._loadedConfig).toBe(loadedConfig); });
.forEach(r => { expect(config[0]._loadedConfig).toBe(loadedConfig); });
});
});

View File

@ -11,11 +11,10 @@ import {Component, Injectable, NgModule, NgModuleFactoryLoader, NgModuleRef} fro
import {ComponentFixture, TestBed, fakeAsync, inject, tick} from '@angular/core/testing';
import {By} from '@angular/platform-browser/src/dom/debug/by';
import {expect} from '@angular/platform-browser/testing/src/matchers';
import {ActivatedRoute, ActivatedRouteSnapshot, CanActivate, CanDeactivate, DetachedRouteHandle, Event, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, PRIMARY_OUTLET, ParamMap, Params, PreloadAllModules, PreloadingStrategy, Resolve, RouteConfigLoadEnd, RouteConfigLoadStart, RouteReuseStrategy, Router, RouterModule, RouterPreloader, RouterStateSnapshot, RoutesRecognized, RunGuardsAndResolvers, UrlHandlingStrategy, UrlSegmentGroup, UrlTree} from '@angular/router';
import {Observable} from 'rxjs/Observable';
import {map} from 'rxjs/operator/map';
import {ActivatedRoute, ActivatedRouteSnapshot, CanActivate, CanDeactivate, DetachedRouteHandle, Event, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, PRIMARY_OUTLET, ParamMap, Params, PreloadAllModules, PreloadingStrategy, Resolve, RouteConfigLoadEnd, RouteConfigLoadStart, RouteReuseStrategy, Router, RouterModule, RouterStateSnapshot, RoutesRecognized, RunGuardsAndResolvers, UrlHandlingStrategy, UrlSegmentGroup, UrlTree} from '../index';
import {RouterPreloader} from '../src/router_preloader';
import {forEach} from '../src/utils/collection';
import {RouterTestingModule, SpyNgModuleFactoryLoader} from '../testing';
@ -2470,13 +2469,14 @@ describe('Integration', () => {
const fixture = TestBed.createComponent(ComponentWithRouterLink);
router.navigateByUrl('/team');
expect(() => advance(fixture)).not.toThrow();
advance(fixture);
const paragraph = fixture.nativeElement.querySelector('p');
expect(paragraph.textContent).toEqual('true');
router.navigateByUrl('/otherteam');
advance(fixture);
advance(fixture);
expect(paragraph.textContent).toEqual('false');
}));
@ -3049,7 +3049,7 @@ describe('Integration', () => {
router.navigateByUrl('/blank');
advance(fixture);
const config: any = router.config;
const config = router.config;
const firstConfig = config[1]._loadedConfig;
expect(firstConfig).toBeDefined();

View File

@ -0,0 +1,74 @@
/**
* @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 {CommonModule} from '@angular/common';
import {Component, NgModule, Type} from '@angular/core';
import {ComponentFixture, TestBed, fakeAsync, tick} from '@angular/core/testing';
import {Router} from '@angular/router';
import {RouterTestingModule} from '@angular/router/testing';
describe('Integration', () => {
describe('routerLinkActive', () => {
it('should not cause infinite loops in the change detection - #15825', fakeAsync(() => {
@Component({selector: 'simple', template: 'simple'})
class SimpleCmp {
}
@Component({
selector: 'some-root',
template: `
<div *ngIf="show">
<ng-container *ngTemplateOutlet="tpl"></ng-container>
</div>
<router-outlet></router-outlet>
<ng-template #tpl>
<a routerLink="/simple" routerLinkActive="active"></a>
</ng-template>`
})
class MyCmp {
show: boolean = false;
}
@NgModule({
imports: [CommonModule, RouterTestingModule],
declarations: [MyCmp, SimpleCmp],
entryComponents: [SimpleCmp],
})
class MyModule {
}
TestBed.configureTestingModule({imports: [MyModule]});
const router: Router = TestBed.get(Router);
const fixture = createRoot(router, MyCmp);
router.resetConfig([{path: 'simple', component: SimpleCmp}]);
router.navigateByUrl('/simple');
advance(fixture);
const instance = fixture.componentInstance;
instance.show = true;
expect(() => advance(fixture)).not.toThrow();
}));
});
});
function advance<T>(fixture: ComponentFixture<T>): void {
tick();
fixture.detectChanges();
}
function createRoot<T>(router: Router, type: Type<T>): ComponentFixture<T> {
const f = TestBed.createComponent(type);
advance(f);
router.initialNavigation();
advance(f);
return f;
}

View File

@ -10,7 +10,7 @@ import {Compiler, Component, NgModule, NgModuleFactoryLoader, NgModuleRef} from
import {TestBed, fakeAsync, inject, tick} from '@angular/core/testing';
import {Route, RouteConfigLoadEnd, RouteConfigLoadStart, Router, RouterModule} from '../index';
import {LoadedRouterConfig} from '../src/router_config_loader';
import {LoadedRouterConfig} from '../src/config';
import {PreloadAllModules, PreloadingStrategy, RouterPreloader} from '../src/router_preloader';
import {RouterTestingModule, SpyNgModuleFactoryLoader} from '../testing';
@ -46,7 +46,7 @@ describe('RouterPreloader', () => {
tick();
const c = router.config;
expect(!!((<any>c[0])._loadedConfig)).toBe(false);
expect(c[0]._loadedConfig).not.toBeDefined();
})));
});
@ -97,12 +97,12 @@ describe('RouterPreloader', () => {
const c = router.config;
expect(c[0].loadChildren).toEqual('expected');
const loadedConfig: LoadedRouterConfig = (<any>c[0])._loadedConfig;
const loadedConfig: LoadedRouterConfig = c[0]._loadedConfig;
const module: any = loadedConfig.module;
expect(loadedConfig.routes[0].path).toEqual('LoadedModule1');
expect(module.parent).toBe(testModule);
const loadedConfig2: LoadedRouterConfig = (<any>loadedConfig.routes[0])._loadedConfig;
const loadedConfig2: LoadedRouterConfig = loadedConfig.routes[0]._loadedConfig;
const module2: any = loadedConfig2.module;
expect(loadedConfig2.routes[0].path).toEqual('LoadedModule2');
expect(module2.parent).toBe(module);
@ -165,12 +165,12 @@ describe('RouterPreloader', () => {
const c = router.config;
const loadedConfig: LoadedRouterConfig = (<any>c[0])._loadedConfig;
const loadedConfig: LoadedRouterConfig = c[0]._loadedConfig;
const module: any = loadedConfig.module;
expect(module.parent).toBe(testModule);
const loadedConfig2: LoadedRouterConfig = (<any>loadedConfig.routes[0])._loadedConfig;
const loadedConfig3: LoadedRouterConfig = (<any>loadedConfig2.routes[0])._loadedConfig;
const loadedConfig2: LoadedRouterConfig = loadedConfig.routes[0]._loadedConfig;
const loadedConfig3: LoadedRouterConfig = loadedConfig2.routes[0]._loadedConfig;
const module3: any = loadedConfig3.module;
expect(module3.parent).toBe(module2);
})));
@ -204,8 +204,8 @@ describe('RouterPreloader', () => {
tick();
const c = router.config;
expect(!!((<any>c[0])._loadedConfig)).toBe(false);
expect(!!((<any>c[1])._loadedConfig)).toBe(true);
expect(c[0]._loadedConfig).not.toBeDefined();
expect(c[1]._loadedConfig).toBeDefined();
})));
});
});

View File

@ -201,6 +201,7 @@ function noNg() {
throw new Error('AngularJS v1.x is not loaded!');
}
let angular: {
bootstrap: (e: Element, modules: (string | IInjectable)[], config: IAngularBootstrapConfig) =>
void,
@ -217,7 +218,6 @@ let angular: {
getTestability: noNg
};
try {
if (window.hasOwnProperty('angular')) {
angular = (<any>window).angular;
@ -226,9 +226,37 @@ try {
// ignore in CJS mode.
}
export const bootstrap = angular.bootstrap;
export const module = angular.module;
export const element = angular.element;
/**
* Resets the AngularJS library.
*
* Used when angularjs is loaded lazily, and not available on `window`.
*
* @stable
*/
export function setAngularLib(ng: any): void {
angular = ng;
}
/**
* Returns the current version of the AngularJS library.
*
* @stable
*/
export function getAngularLib(): any {
return angular;
}
export const bootstrap =
(e: Element, modules: (string | IInjectable)[], config: IAngularBootstrapConfig): void =>
angular.bootstrap(e, modules, config);
export const module = (prefix: string, dependencies?: string[]): IModule =>
angular.module(prefix, dependencies);
export const element = (e: Element | string): IAugmentedJQuery => angular.element(e);
export const resumeBootstrap = (): void => angular.resumeBootstrap();
export const getTestability = (e: Element): ITestabilityService => angular.getTestability(e);
export const version = angular.version;
export const resumeBootstrap = angular.resumeBootstrap;
export const getTestability = angular.getTestability;

View File

@ -12,6 +12,7 @@
* Entry point for all public APIs of the upgrade/static package, allowing
* Angular 1 and Angular 2+ to run side by side in the same application.
*/
export {getAngularLib, setAngularLib} from './src/common/angular1';
export {downgradeComponent} from './src/common/downgrade_component';
export {downgradeInjectable} from './src/common/downgrade_injectable';
export {VERSION} from './src/common/version';

View File

@ -12,7 +12,7 @@ import {BrowserModule} from '@angular/platform-browser';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import * as angular from '@angular/upgrade/src/common/angular1';
import {$INJECTOR, INJECTOR_KEY} from '@angular/upgrade/src/common/constants';
import {UpgradeModule, downgradeInjectable} from '@angular/upgrade/static';
import {UpgradeModule, downgradeInjectable, getAngularLib, setAngularLib} from '@angular/upgrade/static';
import {bootstrap, html} from '../test_helpers';
@ -99,5 +99,33 @@ export function main() {
expect(runBlockTriggered).toBeTruthy();
});
}));
it('should allow resetting angular at runtime', async(() => {
let wrappedBootstrapepedCalled = false;
const n: any = getAngularLib();
setAngularLib({
bootstrap: (...args: any[]) => {
wrappedBootstrapepedCalled = true;
n.bootstrap(...args);
},
module: n.module,
element: n.element,
version: n.version,
resumeBootstrap: n.resumeBootstrap,
getTestability: n.getTestability
});
@NgModule({imports: [BrowserModule, UpgradeModule]})
class Ng2Module {
ngDoBootstrap() {}
}
const ng1Module = angular.module('ng1Module', []);
bootstrap(platformBrowserDynamic(), Ng2Module, html('<div>'), ng1Module)
.then((upgrade) => { expect(wrappedBootstrapepedCalled).toEqual(true); });
}));
});
}

View File

@ -58,6 +58,7 @@ cp -v package.json $TMP
./node_modules/.bin/ngc -p tsconfig-build.json --i18nFile=src/messages.fi.xlf --locale=fi --i18nFormat=xlf
./node_modules/.bin/ng-xi18n -p tsconfig-xi18n.json --i18nFormat=xlf --locale=fr
./node_modules/.bin/ng-xi18n -p tsconfig-xi18n.json --i18nFormat=xlf2 --outFile=messages.xliff2.xlf
./node_modules/.bin/ng-xi18n -p tsconfig-xi18n.json --i18nFormat=xmb --outFile=custom_file.xmb
# Removed until #15219 is fixed

View File

@ -1,6 +1,6 @@
{
"name": "@angular/tsc-wrapped",
"version": "4.0.2",
"version": "4.0.3",
"description": "Wraps the tsc CLI, allowing extensions.",
"homepage": "https://github.com/angular/angular/tree/master/tools/tsc-wrapped",
"bugs": "https://github.com/angular/angular/issues",

View File

@ -9,14 +9,11 @@ import * as path from 'path';
import * as ts from 'typescript';
import {MetadataCollector} from './collector';
import {ClassMetadata, ConstructorMetadata, FunctionMetadata, MemberMetadata, MetadataArray, MetadataEntry, MetadataError, MetadataImportedSymbolReferenceExpression, MetadataMap, MetadataObject, MetadataSymbolicBinaryExpression, MetadataSymbolicCallExpression, MetadataSymbolicExpression, MetadataSymbolicIfExpression, MetadataSymbolicIndexExpression, MetadataSymbolicPrefixExpression, MetadataSymbolicReferenceExpression, MetadataSymbolicSelectExpression, MetadataSymbolicSpreadExpression, MetadataValue, MethodMetadata, ModuleMetadata, VERSION, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isInterfaceMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicExpression, isMetadataSymbolicReferenceExpression, isMethodMetadata} from './schema';
import {ClassMetadata, ConstructorMetadata, FunctionMetadata, MemberMetadata, MetadataEntry, MetadataError, MetadataImportedSymbolReferenceExpression, MetadataMap, MetadataObject, MetadataSymbolicExpression, MetadataSymbolicReferenceExpression, MetadataValue, MethodMetadata, ModuleMetadata, VERSION, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isInterfaceMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicExpression, isMethodMetadata} from './schema';
// The character set used to produce private names.
const PRIVATE_NAME_CHARS = [
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
];
const PRIVATE_NAME_CHARS = 'abcdefghijklmnopqrstuvwxyz';
interface Symbol {
module: string;
@ -199,7 +196,6 @@ export class MetadataBundler {
private canonicalizeSymbols(exportedSymbols: Symbol[]) {
const symbols = Array.from(this.symbolMap.values());
this.exported = new Set(exportedSymbols);
;
symbols.forEach(this.canonicalizeSymbol, this);
}
@ -433,18 +429,15 @@ export class MetadataBundler {
if (isMetadataImportedSymbolReferenceExpression(value)) {
// References to imported symbols are separated into two, references to bundled modules and
// references to modules
// external to the bundle. If the module reference is relative it is assuemd to be in the
// bundle. If it is Global
// it is assumed to be outside the bundle. References to symbols outside the bundle are left
// unmodified. Refernces
// to symbol inside the bundle need to be converted to a bundle import reference reachable
// from the bundle index.
// references to modules external to the bundle. If the module reference is relative it is
// assumed to be in the bundle. If it is Global it is assumed to be outside the bundle.
// References to symbols outside the bundle are left unmodified. References to symbol inside
// the bundle need to be converted to a bundle import reference reachable from the bundle
// index.
if (value.module.startsWith('.')) {
// Reference is to a symbol defined inside the module. Convert the reference to a reference
// to the canonical
// symbol.
// to the canonical symbol.
const referencedModule = resolveModule(value.module, moduleName);
const referencedName = value.name;
return createReference(this.canonicalSymbolOf(referencedModule, referencedName));
@ -453,7 +446,7 @@ export class MetadataBundler {
// Value is a reference to a symbol defined outside the module.
if (value.arguments) {
// If a reference has arguments the arguments need to be converted.
const result: MetadataImportedSymbolReferenceExpression = {
return {
__symbolic: 'reference',
name: value.name,
module: value.module,
@ -538,10 +531,6 @@ function isPrimitive(o: any): o is boolean|string|number {
return o === null || (typeof o !== 'function' && typeof o !== 'object');
}
function isMetadataArray(o: MetadataValue): o is MetadataArray {
return Array.isArray(o);
}
function getRootExport(symbol: Symbol): Symbol {
return symbol.reexportedAs ? getRootExport(symbol.reexportedAs) : symbol;
}

View File

@ -94,7 +94,6 @@ export class MetadataCollector {
value: evaluator.evaluateNode(returnStatement.expression)
};
if (functionDeclaration.parameters.some(p => p.initializer != null)) {
const defaults: MetadataValue[] = [];
func.defaults = functionDeclaration.parameters.map(
p => p.initializer && evaluator.evaluateNode(p.initializer));
}
@ -358,7 +357,6 @@ export class MetadataCollector {
case ts.SyntaxKind.ClassDeclaration:
const classDeclaration = <ts.ClassDeclaration>node;
if (classDeclaration.name) {
const className = classDeclaration.name.text;
if (isExported(classDeclaration)) {
if (!metadata) metadata = {};
metadata[exportedName(classDeclaration)] = classMetadataOf(classDeclaration);

View File

@ -8,7 +8,6 @@
import {writeFileSync} from 'fs';
import {normalize} from 'path';
import * as tsickle from 'tsickle';
import * as ts from 'typescript';
import NgOptions from './options';
@ -98,8 +97,7 @@ export class MetadataWriterHost extends DelegatingHost {
}
if (isDts) {
// TODO: remove this early return after https://github.com/Microsoft/TypeScript/pull/8412
// is
// released
// is released
return;
}

View File

@ -9,7 +9,7 @@
import * as ts from 'typescript';
import {CollectorOptions} from './collector';
import {MetadataEntry, MetadataError, MetadataGlobalReferenceExpression, MetadataImportedSymbolReferenceExpression, MetadataSymbolicCallExpression, MetadataSymbolicReferenceExpression, MetadataValue, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSpreadExpression} from './schema';
import {MetadataEntry, MetadataError, MetadataImportedSymbolReferenceExpression, MetadataSymbolicCallExpression, MetadataValue, isMetadataError, isMetadataModuleReferenceExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSpreadExpression} from './schema';
import {Symbols} from './symbols';
// In TypeScript 2.1 the spread element kind was renamed.
@ -86,7 +86,7 @@ export function errorSymbol(
const {line, character} =
ts.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile));
result = {__symbolic: 'error', message, line, character};
};
}
}
if (!result) {
result = {__symbolic: 'error', message};
@ -154,7 +154,8 @@ export class Evaluator {
case ts.SyntaxKind.CallExpression:
const callExpression = <ts.CallExpression>node;
// We can fold a <array>.concat(<v>).
if (isMethodCallOf(callExpression, 'concat') && callExpression.arguments.length === 1) {
if (isMethodCallOf(callExpression, 'concat') &&
arrayOrEmpty(callExpression.arguments).length === 1) {
const arrayNode = (<ts.PropertyAccessExpression>callExpression.expression).expression;
if (this.isFoldableWorker(arrayNode, folding) &&
this.isFoldableWorker(callExpression.arguments[0], folding)) {
@ -167,7 +168,8 @@ export class Evaluator {
}
// We can fold a call to CONST_EXPR
if (isCallOf(callExpression, 'CONST_EXPR') && callExpression.arguments.length === 1)
if (isCallOf(callExpression, 'CONST_EXPR') &&
arrayOrEmpty(callExpression.arguments).length === 1)
return this.isFoldableWorker(callExpression.arguments[0], folding);
return false;
case ts.SyntaxKind.NoSubstitutionTemplateLiteral:
@ -295,14 +297,15 @@ export class Evaluator {
return recordEntry({__symbolic: 'spread', expression: spreadExpression}, node);
case ts.SyntaxKind.CallExpression:
const callExpression = <ts.CallExpression>node;
if (isCallOf(callExpression, 'forwardRef') && callExpression.arguments.length === 1) {
if (isCallOf(callExpression, 'forwardRef') &&
arrayOrEmpty(callExpression.arguments).length === 1) {
const firstArgument = callExpression.arguments[0];
if (firstArgument.kind == ts.SyntaxKind.ArrowFunction) {
const arrowFunction = <ts.ArrowFunction>firstArgument;
return recordEntry(this.evaluateNode(arrowFunction.body), node);
}
}
const args = callExpression.arguments.map(arg => this.evaluateNode(arg));
const args = arrayOrEmpty(callExpression.arguments).map(arg => this.evaluateNode(arg));
if (!this.options.verboseInvalidExpression && args.some(isMetadataError)) {
return args.find(isMetadataError);
}
@ -315,7 +318,8 @@ export class Evaluator {
}
}
// Always fold a CONST_EXPR even if the argument is not foldable.
if (isCallOf(callExpression, 'CONST_EXPR') && callExpression.arguments.length === 1) {
if (isCallOf(callExpression, 'CONST_EXPR') &&
arrayOrEmpty(callExpression.arguments).length === 1) {
return recordEntry(args[0], node);
}
const expression = this.evaluateNode(callExpression.expression);
@ -329,7 +333,7 @@ export class Evaluator {
return recordEntry(result, node);
case ts.SyntaxKind.NewExpression:
const newExpression = <ts.NewExpression>node;
const newArgs = newExpression.arguments.map(arg => this.evaluateNode(arg));
const newArgs = arrayOrEmpty(newExpression.arguments).map(arg => this.evaluateNode(arg));
if (!this.options.verboseInvalidExpression && newArgs.some(isMetadataError)) {
return recordEntry(newArgs.find(isMetadataError), node);
}
@ -575,3 +579,9 @@ export class Evaluator {
function isPropertyAssignment(node: ts.Node): node is ts.PropertyAssignment {
return node.kind == ts.SyntaxKind.PropertyAssignment;
}
const empty = [] as ts.NodeArray<any>;
function arrayOrEmpty<T extends ts.Node>(v: ts.NodeArray<T>): ts.NodeArray<T> {
return v || empty;
}

View File

@ -55,7 +55,7 @@ export function main(
let host = ts.createCompilerHost(parsed.options, true);
// If the comilation is a flat module index then produce the flat module index
// If the compilation is a flat module index then produce the flat module index
// metadata and the synthetic flat module index.
if (ngOptions.flatModuleOutFile && !ngOptions.skipMetadataEmit) {
const files = parsed.fileNames.filter(f => !DTS.test(f));
@ -170,7 +170,7 @@ export function main(
// CLI entry point
if (require.main === module) {
const args = process.argv.slice(2);
let {options, fileNames, errors} = (ts as any).parseCommandLine(args);
let {options, errors} = (ts as any).parseCommandLine(args);
check(errors);
const project = options.project || '.';
// TODO(alexeagle): command line should be TSC-compatible, remove "CliOptions" here

View File

@ -51,7 +51,7 @@ export class Symbols {
// in the parent chain). This doesn't damage the node as the binder unconditionally
// sets the parent.
externalReference.expression.parent = externalReference;
externalReference.parent = this.sourceFile;
externalReference.parent = this.sourceFile as any;
}
const from = stripQuotes(externalReference.expression.getText());
symbols.set(importEqualsDeclaration.name.text, {__symbolic: 'reference', module: from});

View File

@ -12,8 +12,7 @@ export interface VinylFile extends Object {
// Content of the virtual file
contents: Buffer;
}
;
export function isVinylFile(obj: any): obj is VinylFile {
return (typeof obj === 'object') && ('path' in obj) && ('contents' in obj);
};
}

View File

@ -215,8 +215,21 @@ describe('Evaluator', () => {
0, {__symbolic: 'spread', expression: {__symbolic: 'reference', name: 'arrImport'}}, 5
]);
});
it('should be able to handle a new expression with no arguments', () => {
const source = sourceFileOf(`
export var a = new f;
`);
const expr = findVar(source, 'a');
expect(evaluator.evaluateNode(expr.initializer))
.toEqual({__symbolic: 'new', expression: {__symbolic: 'reference', name: 'f'}});
});
});
function sourceFileOf(text: string): ts.SourceFile {
return ts.createSourceFile('test.ts', text, ts.ScriptTarget.Latest, true);
}
const FILES: Directory = {
'directives.ts': `
export function Pipe(options: { name?: string, pure?: boolean}) {

View File

@ -84,6 +84,9 @@ export class MockNode implements ts.Node {
getText(sourceFile?: ts.SourceFile): string { return ''; }
getFirstToken(sourceFile?: ts.SourceFile): ts.Node { return null; }
getLastToken(sourceFile?: ts.SourceFile): ts.Node { return null; }
forEachChild<T>(cbNode: (node: ts.Node) => T, cbNodeArray?: (nodes: ts.Node[]) => T): T {
return null;
}
}
export class MockIdentifier extends MockNode implements ts.Identifier {

View File

@ -2,5 +2,6 @@
module.exports = (gulp) => (done) => {
const path = require('path');
const childProcess = require('child_process');
childProcess.exec(path.join(__dirname, '../../build.sh'), done);
// increase maxbuffer to address out of memory exception when running certain tasks
childProcess.exec(path.join(__dirname, '../../build.sh'), {maxBuffer: 300 * 1024}, done);
};

View File

@ -354,7 +354,7 @@ export declare class RouterOutletMap {
}
/** @stable */
export declare class RouterPreloader {
export declare class RouterPreloader implements OnDestroy {
constructor(router: Router, moduleLoader: NgModuleFactoryLoader, compiler: Compiler, injector: Injector, preloadingStrategy: PreloadingStrategy);
ngOnDestroy(): void;
preload(): Observable<any>;

View File

@ -9,6 +9,12 @@ export declare function downgradeComponent(info: {
/** @experimental */
export declare function downgradeInjectable(token: any): Function;
/** @stable */
export declare function getAngularLib(): any;
/** @stable */
export declare function setAngularLib(ng: any): void;
/** @experimental */
export declare class UpgradeComponent implements OnInit, OnChanges, DoCheck, OnDestroy {
constructor(name: string, elementRef: ElementRef, injector: Injector);