Compare commits

...

44 Commits

Author SHA1 Message Date
13b41bd631 chore(release): update version of tsc-wrapped to 0.4.2 2016-12-07 13:54:22 -08:00
f3524af68f docs(changelog): add changelog for 2.3.0 2016-12-07 11:21:13 -08:00
0a56f4ea82 chore(release): cut the 2.3.0 release 2016-12-07 10:58:29 -08:00
cf52284ac3 docs(changelog): fix typo AplicationRef => ApplicationRef (#13284)
docs(changelog): fix typo AplicationRef => ApplicationRef
2016-12-07 10:06:40 -08:00
4a09c81724 fix(language-service): do not throw for invalid metadata (#13261)
Fixes #13255
2016-12-06 17:11:09 -08:00
16efb13dd1 fix: display framework version on bootstrapped component (#13252) 2016-12-06 16:21:07 -08:00
986abbe0b2 fix(http): set the default Accept header (#12989)
Fixes #6354
2016-12-06 16:20:43 -08:00
25c2141991 fix(language-service): remove incompletely used parameter from createLanguageServiceFromTypescript() (#13278)
Fixes #13277
2016-12-06 16:19:39 -08:00
2893c2c0a2 fix(router): validate nested routes (#13224)
Fixes #12827
2016-12-06 10:41:01 -08:00
393c1007a8 fix(tsc-wrapped): have UserError display the actual error 2016-12-06 10:40:38 -08:00
66b6fc010d refactor(common): unify export structure 2016-12-06 10:40:38 -08:00
f31c9470fa fix(compiler): short-circut expressions with an index (#13263)
Fixes #13254
2016-12-06 10:40:15 -08:00
4bd8f58552 FIX typo (#13267) 2016-12-06 10:40:01 -08:00
93556a5720 fix(language-service): avoid throwing for invalid class declarations (#13257)
Fixes #13253
2016-12-06 09:56:30 -08:00
5614c4ff0f fix(compiler): serialize any StaticSymbol correctly, not matter in which context. 2016-12-05 14:20:01 -08:00
c3065aac7a refactor(compiler): replace isStaticSymbol with instanceof StaticSymbol 2016-12-05 14:20:01 -08:00
c767df0e4e fix(router): throw a better error message when angular 1 is not bootstraped 2016-12-05 10:57:24 -08:00
25e5b2fdf0 fix(router): make setUpLocationChangeListener idempotent 2016-12-05 10:57:24 -08:00
307c4693dc refactor(router): code cleanup 2016-12-05 10:54:10 -08:00
349ad75de3 fix(router): fix replaceUrl on RouterLink directives 2016-12-05 10:54:10 -08:00
f562cbf86c fix(router): fix skipLocationChanges on RouterLink directives
fixes #13156
2016-12-05 10:54:10 -08:00
804943c9b1 refactor(router): cleanup RouterLink 2016-12-05 10:54:10 -08:00
dea59165de docs(developer): add anchor to source code formatting (#13199)
In CONTRIBUTING.md#rules has reference to DEVELOPER.md#clang-format
but anchor to #clang-format is not present
2016-12-05 10:27:20 -08:00
a1322873c8 fix(facade): cache original format string (#12764) 2016-12-05 10:26:59 -08:00
b8c839bd51 docs(cheatsheet): correct HostBinding decorator example (#13206) 2016-12-05 10:25:40 -08:00
d2e5198b93 docs(contributing): fix styleguide link and typos (#13198) 2016-12-05 10:25:20 -08:00
6cf7a1bf84 refactor(build): fix build location of compiler-cli esm module (#13212) 2016-12-02 15:19:52 -08:00
d46b8deeea fix(router): runs guards every time when unsuccessfully navigating to the same url over and over again (#13209) 2016-12-02 15:19:00 -08:00
bbb7a39414 fix(router): allow specifying a matcher wihtout specifying a path
fixes #12972
2016-12-02 15:03:59 -08:00
d7d8fab211 refactor(router): cleanup config 2016-12-02 15:03:59 -08:00
51b06924bd docs(core): correct HostListener typo (#13203) 2016-12-02 14:45:47 -08:00
aa4bd14b3f docs(changelog): add instructions for people that depend on the cache operator (#13191) 2016-12-02 14:43:05 -08:00
3ff6554cbc fix(language-service): update to use CompilerHost from compiler-cli (#13189) 2016-12-02 14:34:16 -08:00
dfd8140084 Fix format 2016-12-02 14:31:55 -08:00
6ea3ab7e14 Fix exit code. Give a specific type. Add test cases. 2016-12-02 14:31:55 -08:00
9761db5ac2 refactor(compiler): change ngc error handling
Do not print stack trace for user errors
Print stack trace for compiler internal errors
2016-12-02 14:31:55 -08:00
75d1617b63 fix(compiler): include the summaries of reexported modules / directives / pipes (#13196)
Only if these are not part of the sources.
2016-12-02 10:08:46 -08:00
614a35d539 feat(compiler): read and write .ngsummary.json files
When compiling libraries, this feature extracts the minimal information
from the directives/pipes/modules of the library into `.ngsummary.json` files,
so that applications that use this library only need to be recompiled
if one of the summary files change, but not on every change
of the libraries (e.g. one of the templates).

Only works if individual codegen for libraries is enabled,
see the `generateCodeForLibraries: false` option.

Closes #12787
2016-12-01 14:49:52 -08:00
9ab401f4d3 refactor(compiler): simplify NgModuleSymmaryMetadata
- merge `NgModuleInjectorSummary` and `NgModuleDirectiveSummary`
- remove `directiveLoaders` from the summary
2016-12-01 14:49:52 -08:00
82c81cd0d2 fix(common): make sure the plural category exists (#13169)
fixes #12379
2016-12-01 13:26:24 -08:00
12959f444c refactor(router):remove unused parameter pathIndex (#13180) 2016-12-01 13:25:53 -08:00
25a6da244c refactor(compiler-cli): refactor compiler host parameters (#13147) 2016-12-01 13:24:51 -08:00
5908b66ae9 docs(changelog): move 2.2.2 to its place in the chronological order 2016-12-01 10:21:23 -08:00
480ef20eb1 docs(changelog): add changelog for 2.2.2 2016-12-01 09:59:23 -08:00
121 changed files with 2273 additions and 1086 deletions

View File

@ -1,3 +1,36 @@
<a name="2.3.0"></a>
# [2.3.0](https://github.com/angular/angular/compare/2.3.0-rc.0...2.3.0) (2016-12-07)
### Bug Fixes
* **common:** make sure the plural category exists ([#13169](https://github.com/angular/angular/issues/13169)) ([82c81cd](https://github.com/angular/angular/commit/82c81cd)), closes [#12379](https://github.com/angular/angular/issues/12379)
* **compiler:** include the summaries of reexported modules / directives / pipes ([#13196](https://github.com/angular/angular/issues/13196)) ([75d1617](https://github.com/angular/angular/commit/75d1617))
* **compiler:** serialize any `StaticSymbol` correctly, not matter in which context ([5614c4f](https://github.com/angular/angular/commit/5614c4f))
* **compiler:** short-circut expressions with an index ([#13263](https://github.com/angular/angular/issues/13263)) ([f31c947](https://github.com/angular/angular/commit/f31c947)), closes [#13254](https://github.com/angular/angular/issues/13254)
* **core:** display framework version on bootstrapped component ([#13252](https://github.com/angular/angular/issues/13252)) ([16efb13](https://github.com/angular/angular/commit/16efb13))
* **facade:** cache original format string ([#12764](https://github.com/angular/angular/issues/12764)) ([a132287](https://github.com/angular/angular/commit/a132287))
* **http:** set the default Accept header ([#12989](https://github.com/angular/angular/issues/12989)) ([986abbe](https://github.com/angular/angular/commit/986abbe)), closes [#6354](https://github.com/angular/angular/issues/6354)
* **language-service:** avoid throwing for invalid class declarations ([#13257](https://github.com/angular/angular/issues/13257)) ([93556a5](https://github.com/angular/angular/commit/93556a5)), closes [#13253](https://github.com/angular/angular/issues/13253)
* **language-service:** do not throw for invalid metadata ([#13261](https://github.com/angular/angular/issues/13261)) ([4a09c81](https://github.com/angular/angular/commit/4a09c81)), closes [#13255](https://github.com/angular/angular/issues/13255)
* **language-service:** remove incompletely used parameter from `createLanguageServiceFromTypescript()` ([#13278](https://github.com/angular/angular/issues/13278)) ([25c2141](https://github.com/angular/angular/commit/25c2141)), closes [#13277](https://github.com/angular/angular/issues/13277)
* **language-service:** update to use `CompilerHost` from compiler-cli ([#13189](https://github.com/angular/angular/issues/13189)) ([3ff6554](https://github.com/angular/angular/commit/3ff6554))
* **router:** allow specifying a matcher wihtout specifying a path ([bbb7a39](https://github.com/angular/angular/commit/bbb7a39)), closes [#12972](https://github.com/angular/angular/issues/12972)
* **router:** fix replaceUrl on RouterLink directives ([349ad75](https://github.com/angular/angular/commit/349ad75))
* **router:** fix skipLocationChanges on RouterLink directives ([f562cbf](https://github.com/angular/angular/commit/f562cbf)), closes [#13156](https://github.com/angular/angular/issues/13156)
* **router:** make setUpLocationChangeListener idempotent ([25e5b2f](https://github.com/angular/angular/commit/25e5b2f))
* **router:** runs guards every time when unsuccessfully navigating to the same url over and over again ([#13209](https://github.com/angular/angular/issues/13209)) ([d46b8de](https://github.com/angular/angular/commit/d46b8de))
* **router:** throw a better error message when angular 1 is not bootstraped ([c767df0](https://github.com/angular/angular/commit/c767df0))
* **router:** validate nested routes ([#13224](https://github.com/angular/angular/issues/13224)) ([2893c2c](https://github.com/angular/angular/commit/2893c2c)), closes [#12827](https://github.com/angular/angular/issues/12827)
* **tsc-wrapped:** have UserError display the actual error ([393c100](https://github.com/angular/angular/commit/393c100))
### Features
* **compiler:** read and write `.ngsummary.json` files ([614a35d](https://github.com/angular/angular/commit/614a35d)), closes [#12787](https://github.com/angular/angular/issues/12787)
<a name="2.3.0-rc.0"></a>
# [2.3.0-rc.0](https://github.com/angular/angular/compare/2.3.0-beta.0...2.3.0-rc.0) (2016-11-30)
@ -29,7 +62,7 @@
### Features
* **core:** update RxJS peer dependency to 5.0.0-rc.4 ([2d6a003](https://github.com/angular/angular/commit/2d6a003)), closes [#13125](https://github.com/angular/angular/issues/13125)
* **core:** update RxJS peer dependency to 5.0.0-rc.4 Please see [this gist](https://gist.github.com/robwormald/19dea0c70a6e01aadced6731aed4f9f7) if you depend on the `cache` operator ([2d6a003](https://github.com/angular/angular/commit/2d6a003)), closes [#13125](https://github.com/angular/angular/issues/13125)
* **core:** upgrade zone.js to v0.7.1 ([c4bbafc](https://github.com/angular/angular/commit/c4bbafc))
* **build:** record angular version in the dom ([#13164](https://github.com/angular/angular/issues/13164)) ([e628b66](https://github.com/angular/angular/commit/e628b66))
* **core:** expose destroy() method on ViewRef ([808275a](https://github.com/angular/angular/commit/808275a))
@ -61,6 +94,26 @@
* **animations:** Revert: blend in all previously transitioned styles into next animation if interrupted ([c12e56e](https://github.com/angular/angular/commit/c12e56e))
<a name="2.2.2"></a>
## [2.2.2](https://github.com/angular/angular/compare/2.2.1...2.2.2) (2016-11-22)
### Bug Fixes
* **animations:** blend in all previously transitioned styles into next animation if interrupted ([#13014](https://github.com/angular/angular/issues/13014)) ([ea4fc9b](https://github.com/angular/angular/commit/ea4fc9b)), closes [#13013](https://github.com/angular/angular/issues/13013)
* **benchmarks:** use sanitized style values ([#12943](https://github.com/angular/angular/issues/12943)) ([33a7902](https://github.com/angular/angular/commit/33a7902))
* **closure:** quote date pattern aliases ([#13012](https://github.com/angular/angular/issues/13012)) ([0956ace](https://github.com/angular/angular/commit/0956ace))
* **compiler:** fix versions of `@angular/tsc-wrapped` ([2fe6fb1](https://github.com/angular/angular/commit/2fe6fb1))
* **router:** add a banner file for the router ([#12919](https://github.com/angular/angular/issues/12919)) ([8df328b](https://github.com/angular/angular/commit/8df328b))
* **router:** add a banner file for the router ([#12919](https://github.com/angular/angular/issues/12919)) ([511cd4d](https://github.com/angular/angular/commit/511cd4d))
* **router:** removes a peer dependency from router to upgrade ([115f18f](https://github.com/angular/angular/commit/115f18f))
* **router:** removes a peer dependency from router to upgrade ([87d5d49](https://github.com/angular/angular/commit/87d5d49))
* **router:** support redirects to named outlets ([09226d9](https://github.com/angular/angular/commit/09226d9)), closes [#12740](https://github.com/angular/angular/issues/12740) [#9921](https://github.com/angular/angular/issues/9921)
* **upgrade:** call ng1 lifecycle hooks ([#12875](https://github.com/angular/angular/issues/12875)) ([462316b](https://github.com/angular/angular/commit/462316b))
<a name="2.3.0-beta.0"></a>
# [2.3.0-beta.0](https://github.com/angular/angular/compare/2.2.0...2.3.0-beta.0) (2016-11-17)
@ -83,6 +136,8 @@
Note: The 2.3.0-beta.0 release also contains all the changes present in the 2.2.1 release.
<a name="2.2.1"></a>
## [2.2.1](https://github.com/angular/angular/compare/2.2.0...2.2.1) (2016-11-17)
@ -904,7 +959,7 @@ prefix using `animate-` must now be preixed using `bind-animate-`.
* core:
- `ApplicationRef.dispose` is deprecated. Destroy the module that was
created during bootstrap instead by calling `NgModuleRef.destroy`.
- `AplicationRef.registerDisposeListener` is deprecated.
- `ApplicationRef.registerDisposeListener` is deprecated.
Use the `ngOnDestroy` lifecycle hook for providers or
`NgModuleRef.onDestroy` instead.
- `disposePlatform` is deprecated. Use `destroyPlatform` instead.

View File

@ -57,7 +57,7 @@ We want to fix all the issues as soon as possible, but before fixing a bug we ne
- 3rd-party libraries and their versions
- and most importantly - a use-case that fails
A minimal reproduce scenario using http://plnkr.co/ allows us to quickly confirm a bug (or point out coding problem) as well as confirm that we are fixing the right problem. If plunker is not a suitable way to demostrate the problem (for example for issues related to our npm packaging), please create a standalone git repository demostrating the problem.
A minimal reproduce scenario using http://plnkr.co/ allows us to quickly confirm a bug (or point out coding problem) as well as confirm that we are fixing the right problem. If plunker is not a suitable way to demonstrate the problem (for example for issues related to our npm packaging), please create a standalone git repository demonstrating the problem.
We will be insisting on a minimal reproduce scenario in order to save maintainers time and ultimately be able to fix more bugs. Interestingly, from our experience users often find coding problems themselves while preparing a minimal plunk. We understand that sometimes it might be hard to extract essentials bits of code from a larger code-base but we really need to isolate the problem before we can fix it.
@ -244,7 +244,7 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
[github]: https://github.com/angular/angular
[gitter]: https://gitter.im/angular/angular
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
[js-style-guide]: https://google.github.io/styleguide/javascriptguide.xml
[js-style-guide]: https://google.github.io/styleguide/jsguide.html
[jsfiddle]: http://jsfiddle.net
[plunker]: http://plnkr.co/edit
[runnable]: http://runnable.com

View File

@ -126,7 +126,7 @@ $ gulp public-api:update
Note: The command `./test.sh tools` fails when the API doesn't match the golden files.
## Formatting your source code
## <a name="clang-format"></a> Formatting your source code
Angular uses [clang-format](http://clang.llvm.org/docs/ClangFormat.html) to format the source code. If the source code
is not properly formatted, the CI will fail and the PR can not be merged.

View File

@ -145,6 +145,11 @@ do
$TSC -p ${SRCDIR}/tsconfig-testing.json
fi
if [[ -e ${SRCDIR}/tsconfig-2015.json ]]; then
echo "====== COMPILING ESM: ${TSC} -p ${SRCDIR}/tsconfig-2015.json"
${TSC} -p ${SRCDIR}/tsconfig-2015.json
fi
echo "====== TSC 1.8 d.ts compat for ${DESTDIR} ====="
# safely strips 'readonly' specifier from d.ts files to make them compatible with tsc 1.8
if [ "$(uname)" == "Darwin" ]; then

View File

@ -11,13 +11,6 @@
* @description
* Entry point for all public APIs of the common package.
*/
export * from './src/location';
export {NgLocalization} from './src/localization';
export {CommonModule} from './src/common_module';
export {NgClass, NgFor, NgIf, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet} from './src/directives/index';
export {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe} from './src/pipes/index';
import {Version} from '@angular/core';
/**
* @stable
*/
export const VERSION = new Version('0.0.0-PLACEHOLDER');
export * from './src/common';
// This file only reexports content of the `src` folder. Keep it that way.

View File

@ -0,0 +1,20 @@
/**
* @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
*/
/**
* @module
* @description
* Entry point for all public APIs of the common package.
*/
export * from './location/index';
export {NgLocalization} from './localization';
export {CommonModule} from './common_module';
export {NgClass, NgFor, NgIf, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet} from './directives/index';
export {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe} from './pipes/index';
export {VERSION} from './version';
export {Version} from '@angular/core';

View File

@ -23,9 +23,23 @@ export abstract class NgLocalization { abstract getPluralCategory(value: any): s
*/
export function getPluralCategory(
value: number, cases: string[], ngLocalization: NgLocalization): string {
const nbCase = `=${value}`;
let key = `=${value}`;
return cases.indexOf(nbCase) > -1 ? nbCase : ngLocalization.getPluralCategory(value);
if (cases.indexOf(key) > -1) {
return key;
}
key = ngLocalization.getPluralCategory(value);
if (cases.indexOf(key) > -1) {
return key;
}
if (cases.indexOf('other') > -1) {
return 'other';
}
throw new Error(`No plural message found for value "${value}"`);
}
/**

View File

@ -1,13 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
export * from './location/platform_location';
export * from './location/location_strategy';
export * from './location/hash_location_strategy';
export * from './location/path_location_strategy';
export * from './location/location';

View File

@ -0,0 +1,13 @@
/**
* @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
*/
export * from './platform_location';
export * from './location_strategy';
export * from './hash_location_strategy';
export * from './path_location_strategy';
export * from './location';

View File

@ -96,7 +96,7 @@ export class Location {
* used, or the `APP_BASE_HREF` if the `PathLocationStrategy` is in use.
*/
prepareExternalUrl(url: string): string {
if (url.length > 0 && !url.startsWith('/')) {
if (url && url[0] !== '/') {
url = '/' + url;
}
return this._platformStrategy.prepareExternalUrl(url);
@ -143,7 +143,7 @@ export class Location {
* is.
*/
public static normalizeQueryParams(params: string): string {
return (params.length > 0 && params.substring(0, 1) != '?') ? ('?' + params) : params;
return params && params[0] !== '?' ? '?' + params : params;
}
/**
@ -175,25 +175,13 @@ export class Location {
/**
* If url has a trailing slash, remove it, otherwise return url as is.
*/
public static stripTrailingSlash(url: string): string {
if (/\/$/g.test(url)) {
url = url.substring(0, url.length - 1);
}
return url;
}
public static stripTrailingSlash(url: string): string { return url.replace(/\/$/, ''); }
}
function _stripBaseHref(baseHref: string, url: string): string {
if (baseHref.length > 0 && url.startsWith(baseHref)) {
return url.substring(baseHref.length);
}
return url;
return baseHref && url.startsWith(baseHref) ? url.substring(baseHref.length) : url;
}
function _stripIndexHtml(url: string): string {
if (/\/index.html$/g.test(url)) {
// '/index.html'.length == 11
return url.substring(0, url.length - 11);
}
return url;
return url.replace(/\/index.html$/, '');
}

View File

@ -0,0 +1,19 @@
/**
* @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
*/
/**
* @module
* @description
* Entry point for all public APIs of the common package.
*/
import {Version} from '@angular/core';
/**
* @stable
*/
export const VERSION = new Version('0.0.0-PLACEHOLDER');

View File

@ -7,13 +7,10 @@
*/
import {LOCALE_ID} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {beforeEach, describe, inject, it} from '@angular/core/testing/testing_internal';
import {expect} from '@angular/platform-browser/testing/matchers';
import {TestBed, inject} from '@angular/core/testing';
import {NgLocaleLocalization, NgLocalization, getPluralCategory} from '../src/localization';
export function main() {
describe('l10n', () => {
@ -141,6 +138,23 @@ export function main() {
expect(getPluralCategory(5, ['one', 'other', '=5'], l10n)).toEqual('=5');
expect(getPluralCategory(6, ['one', 'other', '=5'], l10n)).toEqual('other');
});
it('should fallback to other when the case is not present', () => {
const l10n = new NgLocaleLocalization('ro');
expect(getPluralCategory(1, ['one', 'other'], l10n)).toEqual('one');
// 2 -> 'few'
expect(getPluralCategory(2, ['one', 'other'], l10n)).toEqual('other');
});
describe('errors', () => {
it('should report an error when the "other" category is not present', () => {
expect(() => {
const l10n = new NgLocaleLocalization('ro');
// 2 -> 'few'
getPluralCategory(2, ['one'], l10n);
}).toThrowError('No plural message found for value "2"');
});
});
});
});
}

View File

@ -7,12 +7,7 @@
*/
export {AotCompilerHost, AotCompilerHost as StaticReflectorHost, StaticReflector, StaticSymbol} from '@angular/compiler';
export {CodeGenerator} from './src/codegen';
export {CompilerHost, CompilerHostContext, NodeCompilerHostContext} from './src/compiler_host';
export {CompilerHost, CompilerHostContext, ModuleResolutionHostAdapter, NodeCompilerHostContext} from './src/compiler_host';
export {Extractor} from './src/extractor';
export * from '@angular/tsc-wrapped';
import {Version} from '@angular/core';
/**
* @stable
*/
export const VERSION = new Version('0.0.0-PLACEHOLDER');
export {VERSION} from './src/version';

View File

@ -11,7 +11,9 @@ import {FormsModule} from '@angular/forms';
import {ServerModule} from '@angular/platform-server';
import {MdButtonModule} from '@angular2-material/button';
import {ThirdpartyModule} from '../third_party_src/module';
// Note: don't refer to third_party_src as we want to test that
// we can compile components from node_modules!
import {ThirdpartyModule} from 'third_party/module';
import {MultipleComponentsMyComp, NextComp} from './a/multiple_components';
import {AnimateCmp} from './animate';

View File

@ -0,0 +1,100 @@
#!/usr/bin/env node
/**
* @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
*/
/* tslint:disable:no-console */
// Must be imported first, because angular2 decorators throws on load.
import 'reflect-metadata';
import * as path from 'path';
import * as ts from 'typescript';
import * as assert from 'assert';
import {tsc} from '@angular/tsc-wrapped/src/tsc';
import {AngularCompilerOptions, CodeGenerator, CompilerHostContext, NodeCompilerHostContext} from '@angular/compiler-cli';
/**
* Main method.
* Standalone program that executes the real codegen and tests that
* ngsummary.json files are used for libraries.
*/
function main() {
console.log(`testing usage of ngsummary.json files in libraries...`);
const basePath = path.resolve(__dirname, '..');
const project = path.resolve(basePath, 'tsconfig-build.json');
const readFiles: string[] = [];
class AssertingHostContext extends NodeCompilerHostContext {
readFile(fileName: string): string {
readFiles.push(path.relative(basePath, fileName));
return super.readFile(fileName);
}
readResource(s: string): Promise<string> {
readFiles.push(path.relative(basePath, s));
return super.readResource(s);
}
}
const config = tsc.readConfiguration(project, basePath);
config.ngOptions.basePath = basePath;
// This flag tells ngc do not recompile libraries.
config.ngOptions.generateCodeForLibraries = false;
console.log(`>>> running codegen for ${project}`);
codegen(config, (host) => new AssertingHostContext())
.then((exitCode: any) => {
console.log(`>>> codegen done, asserting read files`);
assertSomeFileMatch(readFiles, /^node_modules\/.*\.ngsummary\.json$/);
assertNoFileMatch(readFiles, /^node_modules\/.*\.html$/);
assertNoFileMatch(readFiles, /^node_modules\/.*\.css$/);
assertNoFileMatch(readFiles, /^src\/.*\.ngsummary\.json$/);
assertSomeFileMatch(readFiles, /^src\/.*\.html$/);
assertSomeFileMatch(readFiles, /^src\/.*\.css$/);
console.log(`done, no errors.`);
process.exit(exitCode);
})
.catch((e: any) => {
console.error(e.stack);
console.error('Compilation failed');
process.exit(1);
});
}
/**
* Simple adaption of tsc-wrapped main to just run codegen with a CompilerHostContext
*/
function codegen(
config: {parsed: ts.ParsedCommandLine, ngOptions: AngularCompilerOptions},
hostContextFactory: (host: ts.CompilerHost) => CompilerHostContext) {
const host = ts.createCompilerHost(config.parsed.options, true);
// HACK: patch the realpath to solve symlink issue here:
// https://github.com/Microsoft/TypeScript/issues/9552
// todo(misko): remove once facade symlinks are removed
host.realpath = (path) => path;
const program = ts.createProgram(config.parsed.fileNames, config.parsed.options, host);
return CodeGenerator.create(config.ngOptions, {
} as any, program, host, hostContextFactory(host)).codegen();
}
function assertSomeFileMatch(fileNames: string[], pattern: RegExp) {
assert(
fileNames.some(fileName => pattern.test(fileName)),
`Expected some read files match ${pattern}`);
}
function assertNoFileMatch(fileNames: string[], pattern: RegExp) {
const matches = fileNames.filter(fileName => pattern.test(fileName));
assert(
matches.length === 0,
`Expected no read files match ${pattern}, but found: \n${matches.join('\n')}`);
}
main();

View File

@ -21,6 +21,7 @@
"src/module",
"src/bootstrap",
"test/all_spec",
"test/test_summaries",
"benchmarks/src/tree/ng2/index_aot.ts",
"benchmarks/src/tree/ng2_switch/index_aot.ts",
"benchmarks/src/largetable/ng2/index_aot.ts",

View File

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

View File

@ -17,11 +17,12 @@ import {readFileSync} from 'fs';
import * as path from 'path';
import * as ts from 'typescript';
import {CompilerHost, CompilerHostContext} from './compiler_host';
import {CompilerHost, CompilerHostContext, ModuleResolutionHostAdapter} from './compiler_host';
import {PathMappedCompilerHost} from './path_mapped_compiler_host';
import {Console} from './private_import_core';
const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
const GENERATED_META_FILES = /\.json$/;
const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
const PREAMBLE = `/**
@ -68,10 +69,11 @@ export class CodeGenerator {
sf => this.ngCompilerHost.getCanonicalFileName(sf.fileName)))
.then(generatedModules => {
generatedModules.forEach(generatedModule => {
const sourceFile = this.program.getSourceFile(generatedModule.fileUrl);
const emitPath = this.calculateEmitPath(generatedModule.moduleUrl);
this.host.writeFile(
emitPath, PREAMBLE + generatedModule.source, false, () => {}, [sourceFile]);
const sourceFile = this.program.getSourceFile(generatedModule.srcFileUrl);
const emitPath = this.calculateEmitPath(generatedModule.genFileUrl);
const source = GENERATED_META_FILES.test(emitPath) ? generatedModule.source :
PREAMBLE + generatedModule.source;
this.host.writeFile(emitPath, source, false, () => {}, [sourceFile]);
});
});
}
@ -82,9 +84,9 @@ export class CodeGenerator {
ngCompilerHost?: CompilerHost): CodeGenerator {
if (!ngCompilerHost) {
const usePathMapping = !!options.rootDirs && options.rootDirs.length > 0;
ngCompilerHost = usePathMapping ?
new PathMappedCompilerHost(program, tsCompilerHost, options, compilerHostContext) :
new CompilerHost(program, tsCompilerHost, options, compilerHostContext);
const context = compilerHostContext || new ModuleResolutionHostAdapter(tsCompilerHost);
ngCompilerHost = usePathMapping ? new PathMappedCompilerHost(program, options, context) :
new CompilerHost(program, options, context);
}
const transFile = cliOptions.i18nFile;
const locale = cliOptions.locale;

View File

@ -17,30 +17,25 @@ const DTS = /\.d\.ts$/;
const NODE_MODULES = '/node_modules/';
const IS_GENERATED = /\.(ngfactory|css(\.shim)?)$/;
export interface CompilerHostContext {
fileExists(fileName: string): boolean;
directoryExists(directoryName: string): boolean;
readFile(fileName: string): string;
export interface CompilerHostContext extends ts.ModuleResolutionHost {
readResource(fileName: string): Promise<string>;
assumeFileExists(fileName: string): void;
}
export class CompilerHost implements AotCompilerHost {
protected metadataCollector = new MetadataCollector();
protected context: CompilerHostContext;
private isGenDirChildOfRootDir: boolean;
protected basePath: string;
private genDir: string;
private resolverCache = new Map<string, ModuleMetadata[]>();
constructor(
protected program: ts.Program, protected compilerHost: ts.CompilerHost,
protected options: AngularCompilerOptions, context?: CompilerHostContext) {
protected program: ts.Program, protected options: AngularCompilerOptions,
protected context: CompilerHostContext) {
// normalize the path so that it never ends with '/'.
this.basePath = path.normalize(path.join(this.options.basePath, '.')).replace(/\\/g, '/');
this.genDir = path.normalize(path.join(this.options.genDir, '.')).replace(/\\/g, '/');
this.context = context || new NodeCompilerHostContext(compilerHost);
const genPath: string = path.relative(this.basePath, this.genDir);
this.isGenDirChildOfRootDir = genPath === '' || !genPath.startsWith('..');
}
@ -81,7 +76,7 @@ export class CompilerHost implements AotCompilerHost {
fileNameToModuleName(importedFile: string, containingFile: string): string {
// If a file does not yet exist (because we compile it later), we still need to
// assume it exists it so that the `resolve` method works!
if (!this.compilerHost.fileExists(importedFile)) {
if (!this.context.fileExists(importedFile)) {
this.context.assumeFileExists(importedFile);
}
@ -181,8 +176,8 @@ export class CompilerHost implements AotCompilerHost {
const metadatas = metadataOrMetadatas ?
(Array.isArray(metadataOrMetadatas) ? metadataOrMetadatas : [metadataOrMetadatas]) :
[];
const v1Metadata = metadatas.find(m => m['version'] === 1);
let v2Metadata = metadatas.find(m => m['version'] === 2);
const v1Metadata = metadatas.find((m: any) => m['version'] === 1);
let v2Metadata = metadatas.find((m: any) => m['version'] === 2);
if (!v2Metadata && v1Metadata) {
// patch up v1 to v2 by merging the metadata with metadata collected from the d.ts file
// as the only difference between the versions is whether all exports are contained in
@ -214,17 +209,52 @@ export class CompilerHost implements AotCompilerHost {
}
loadResource(filePath: string): Promise<string> { return this.context.readResource(filePath); }
loadSummary(filePath: string): string { return this.context.readFile(filePath); }
getOutputFileName(sourceFilePath: string): string {
return sourceFilePath.replace(EXT, '') + '.d.ts';
}
}
export class NodeCompilerHostContext implements CompilerHostContext {
constructor(private host: ts.CompilerHost) {}
export class CompilerHostContextAdapter {
protected assumedExists: {[fileName: string]: boolean} = {};
private assumedExists: {[fileName: string]: boolean} = {};
assumeFileExists(fileName: string): void { this.assumedExists[fileName] = true; }
}
export class ModuleResolutionHostAdapter extends CompilerHostContextAdapter implements
CompilerHostContext {
public directoryExists: ((directoryName: string) => boolean)|undefined;
constructor(private host: ts.ModuleResolutionHost) {
super();
if (host.directoryExists) {
this.directoryExists = (directoryName: string) => host.directoryExists(directoryName);
}
}
fileExists(fileName: string): boolean {
return this.assumedExists[fileName] || this.host.fileExists(fileName);
}
readFile(fileName: string): string { return this.host.readFile(fileName); }
readResource(s: string) {
if (!this.host.fileExists(s)) {
// TODO: We should really have a test for error cases like this!
throw new Error(`Compilation failed. Resource file not found: ${s}`);
}
return Promise.resolve(this.host.readFile(s));
}
}
export class NodeCompilerHostContext extends CompilerHostContextAdapter implements
CompilerHostContext {
fileExists(fileName: string): boolean {
return this.assumedExists[fileName] || fs.existsSync(fileName);
}
directoryExists(directoryName: string): boolean {
try {
return fs.statSync(directoryName).isDirectory();
@ -236,12 +266,10 @@ export class NodeCompilerHostContext implements CompilerHostContext {
readFile(fileName: string): string { return fs.readFileSync(fileName, 'utf8'); }
readResource(s: string) {
if (!this.host.fileExists(s)) {
if (!this.fileExists(s)) {
// TODO: We should really have a test for error cases like this!
throw new Error(`Compilation failed. Resource file not found: ${s}`);
}
return Promise.resolve(this.host.readFile(s));
return Promise.resolve(this.readFile(s));
}
assumeFileExists(fileName: string): void { this.assumedExists[fileName] = true; }
}

View File

@ -18,7 +18,7 @@ import * as tsc from '@angular/tsc-wrapped';
import * as ts from 'typescript';
import {excludeFilePattern} from './codegen';
import {CompilerHost} from './compiler_host';
import {CompilerHost, ModuleResolutionHostAdapter} from './compiler_host';
export class Extractor {
constructor(
@ -32,8 +32,10 @@ export class Extractor {
static create(
options: tsc.AngularCompilerOptions, translationsFormat: string, program: ts.Program,
tsCompilerHost: ts.CompilerHost, ngCompilerHost?: CompilerHost): Extractor {
if (!ngCompilerHost) ngCompilerHost = new CompilerHost(program, tsCompilerHost, options);
moduleResolverHost: ts.ModuleResolutionHost, ngCompilerHost?: CompilerHost): Extractor {
if (!ngCompilerHost)
ngCompilerHost =
new CompilerHost(program, options, new ModuleResolutionHostAdapter(moduleResolverHost));
const {extractor: ngExtractor} = compiler.Extractor.create(
ngCompilerHost, {excludeFilePattern: excludeFilePattern(options)});
return new Extractor(ngExtractor, ngCompilerHost, program);

View File

@ -22,14 +22,25 @@ function codegen(
return CodeGenerator.create(ngOptions, cliOptions, program, host).codegen();
}
export function main(
args: any, consoleError: (s: string) => void = console.error): Promise<number> {
const project = args.p || args.project || '.';
const cliOptions = new tsc.NgcCliOptions(args);
return tsc.main(project, cliOptions, codegen).then(() => 0).catch(e => {
if (e instanceof tsc.UserError) {
consoleError(e.message);
return Promise.resolve(1);
} else {
consoleError(e.stack);
consoleError('Compilation failed');
return Promise.resolve(1);
}
});
}
// CLI entry point
if (require.main === module) {
const args = require('minimist')(process.argv.slice(2));
const project = args.p || args.project || '.';
const cliOptions = new tsc.NgcCliOptions(args);
tsc.main(project, cliOptions, codegen).then(exitCode => process.exit(exitCode)).catch(e => {
console.error(e.stack);
console.error('Compilation failed');
process.exit(1);
});
main(args).then((exitCode: number) => process.exit(exitCode));
}

View File

@ -25,10 +25,8 @@ const DTS = /\.d\.ts$/;
* loader what to do.
*/
export class PathMappedCompilerHost extends CompilerHost {
constructor(
program: ts.Program, compilerHost: ts.CompilerHost, options: AngularCompilerOptions,
context?: CompilerHostContext) {
super(program, compilerHost, options, context);
constructor(program: ts.Program, options: AngularCompilerOptions, context: CompilerHostContext) {
super(program, options, context);
}
getCanonicalFileName(fileName: string): string {
@ -119,7 +117,7 @@ export class PathMappedCompilerHost extends CompilerHost {
getMetadataFor(filePath: string): ModuleMetadata[] {
for (const root of this.options.rootDirs || []) {
const rootedPath = path.join(root, filePath);
if (!this.compilerHost.fileExists(rootedPath)) {
if (!this.context.fileExists(rootedPath)) {
// If the file doesn't exists then we cannot return metadata for the file.
// This will occur if the user refernced a declared module for which no file
// exists for the module (i.e. jQuery or angularjs).

View File

@ -0,0 +1,19 @@
/**
* @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
*/
/**
* @module
* @description
* Entry point for all public APIs of the common package.
*/
import {Version} from '@angular/core';
/**
* @stable
*/
export const VERSION = new Version('0.0.0-PLACEHOLDER');

View File

@ -14,14 +14,13 @@ import {Directory, Entry, MockAotContext, MockCompilerHost} from './mocks';
describe('CompilerHost', () => {
let context: MockAotContext;
let host: ts.CompilerHost;
let program: ts.Program;
let hostNestedGenDir: CompilerHost;
let hostSiblingGenDir: CompilerHost;
beforeEach(() => {
context = new MockAotContext('/tmp/src', clone(FILES));
host = new MockCompilerHost(context);
const host = new MockCompilerHost(context);
program = ts.createProgram(
['main.ts'], {
module: ts.ModuleKind.CommonJS,
@ -33,7 +32,7 @@ describe('CompilerHost', () => {
throw new Error('Expected no errors');
}
hostNestedGenDir = new CompilerHost(
program, host, {
program, {
genDir: '/tmp/project/src/gen/',
basePath: '/tmp/project/src',
skipMetadataEmit: false,
@ -43,7 +42,7 @@ describe('CompilerHost', () => {
},
context);
hostSiblingGenDir = new CompilerHost(
program, host, {
program, {
genDir: '/tmp/project/gen',
basePath: '/tmp/project/src/',
skipMetadataEmit: false,

View File

@ -0,0 +1,177 @@
/**
* @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 {makeTempDir} from '@angular/tsc-wrapped/test/test_support';
import * as fs from 'fs';
import * as path from 'path';
import {main} from '../src/main';
import {ReflectionCapabilities, reflector} from './private_import_core';
describe('compiler-cli', () => {
let basePath: string;
let write: (fileName: string, content: string) => void;
beforeEach(() => {
basePath = makeTempDir();
write = (fileName: string, content: string) => {
fs.writeFileSync(path.join(basePath, fileName), content, {encoding: 'utf-8'});
};
write('tsconfig.json', `{
"compilerOptions": {
"experimentalDecorators": true,
"types": [],
"outDir": "built",
"declaration": true,
"module": "es2015"
},
"angularCompilerOptions": {
"annotateForClosureCompiler": true
},
"files": ["test.ts"]
}`);
});
// Restore reflector since AoT compiler will update it with a new static reflector
afterEach(() => { reflector.updateCapabilities(new ReflectionCapabilities()); });
it('should compile without errors', (done) => {
write('test.ts', 'export const A = 1;');
const mockConsole = {error: (s: string) => {}};
spyOn(mockConsole, 'error');
main({p: basePath}, mockConsole.error)
.then((exitCode) => {
expect(mockConsole.error).not.toHaveBeenCalled();
expect(exitCode).toEqual(0);
done();
})
.catch(e => done.fail(e));
});
it('should not print the stack trace if user input file does not exist', (done) => {
const mockConsole = {error: (s: string) => {}};
spyOn(mockConsole, 'error');
main({p: basePath}, mockConsole.error)
.then((exitCode) => {
expect(mockConsole.error)
.toHaveBeenCalledWith(
`Error File '` + path.join(basePath, 'test.ts') + `' not found.`);
expect(mockConsole.error).not.toHaveBeenCalledWith('Compilation failed');
expect(exitCode).toEqual(1);
done();
})
.catch(e => done.fail(e));
});
it('should not print the stack trace if user input file is malformed', (done) => {
write('test.ts', 'foo;');
const mockConsole = {error: (s: string) => {}};
spyOn(mockConsole, 'error');
main({p: basePath}, mockConsole.error)
.then((exitCode) => {
expect(mockConsole.error)
.toHaveBeenCalledWith(
'Error at ' + path.join(basePath, 'test.ts') + `:1:1: Cannot find name 'foo'.`);
expect(mockConsole.error).not.toHaveBeenCalledWith('Compilation failed');
expect(exitCode).toEqual(1);
done();
})
.catch(e => done.fail(e));
});
it('should not print the stack trace if cannot find the imported module', (done) => {
write('test.ts', `import {MyClass} from './not-exist-deps';`);
const mockConsole = {error: (s: string) => {}};
spyOn(mockConsole, 'error');
main({p: basePath}, mockConsole.error)
.then((exitCode) => {
expect(mockConsole.error)
.toHaveBeenCalledWith(
'Error at ' + path.join(basePath, 'test.ts') +
`:1:23: Cannot find module './not-exist-deps'.`);
expect(mockConsole.error).not.toHaveBeenCalledWith('Compilation failed');
expect(exitCode).toEqual(1);
done();
})
.catch(e => done.fail(e));
});
it('should not print the stack trace if cannot import', (done) => {
write('empty-deps.ts', 'export const A = 1;');
write('test.ts', `import {MyClass} from './empty-deps';`);
const mockConsole = {error: (s: string) => {}};
spyOn(mockConsole, 'error');
main({p: basePath}, mockConsole.error)
.then((exitCode) => {
expect(mockConsole.error)
.toHaveBeenCalledWith(
'Error at ' + path.join(basePath, 'test.ts') + `:1:9: Module '"` +
path.join(basePath, 'empty-deps') + `"' has no exported member 'MyClass'.`);
expect(mockConsole.error).not.toHaveBeenCalledWith('Compilation failed');
expect(exitCode).toEqual(1);
done();
})
.catch(e => done.fail(e));
});
it('should not print the stack trace if type mismatches', (done) => {
write('empty-deps.ts', 'export const A = "abc";');
write('test.ts', `
import {A} from './empty-deps';
A();
`);
const mockConsole = {error: (s: string) => {}};
spyOn(mockConsole, 'error');
main({p: basePath}, mockConsole.error)
.then((exitCode) => {
expect(mockConsole.error)
.toHaveBeenCalledWith(
'Error at ' + path.join(basePath, 'test.ts') +
':3:7: Cannot invoke an expression whose type lacks a call signature.');
expect(mockConsole.error).not.toHaveBeenCalledWith('Compilation failed');
expect(exitCode).toEqual(1);
done();
})
.catch(e => done.fail(e));
});
it('should print the stack trace on compiler internal errors', (done) => {
write('test.ts', 'export const A = 1;');
const mockConsole = {error: (s: string) => {}};
spyOn(mockConsole, 'error');
main({p: 'not-exist'}, mockConsole.error)
.then((exitCode) => {
expect(mockConsole.error).toHaveBeenCalled();
expect(mockConsole.error).toHaveBeenCalledWith('Compilation failed');
expect(exitCode).toEqual(1);
done();
})
.catch(e => done.fail(e));
});
});

View File

@ -0,0 +1,13 @@
/**
* @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 {__core_private__ as r} from '@angular/core';
export type ReflectionCapabilities = typeof r._ReflectionCapabilities;
export var ReflectionCapabilities: typeof r.ReflectionCapabilities = r.ReflectionCapabilities;
export var reflector: typeof r.reflector = r.reflector;

View File

@ -0,0 +1,34 @@
{
"compilerOptions": {
"baseUrl": ".",
"declaration": true,
"experimentalDecorators": true,
"noImplicitAny": true,
"module": "es2015",
"moduleResolution": "node",
"outDir": "../../../dist/esm/compiler-cli",
"paths": {
"@angular/core": ["../../../dist/packages-dist/core"],
"@angular/common": ["../../../dist/packages-dist/common"],
"@angular/compiler": ["../../../dist/packages-dist/compiler"],
"@angular/platform-server": ["../../../dist/packages-dist/platform-server"],
"@angular/platform-browser": ["../../../dist/packages-dist/platform-browser"],
"@angular/tsc-wrapped": ["../../../dist/tools/@angular/tsc-wrapped"]
},
"rootDir": ".",
"sourceMap": true,
"inlineSources": true,
"target": "es5",
"lib": ["es6", "dom"],
"skipLibCheck": true
},
"exclude": ["integrationtest"],
"files": [
"index.ts",
"src/main.ts",
"src/extract_i18n.ts",
"../../../node_modules/@types/node/index.d.ts",
"../../../node_modules/@types/jasmine/index.d.ts",
"../../../node_modules/zone.js/dist/zone.js.d.ts"
]
}

View File

@ -21,11 +21,7 @@
* </p>
* </div>
*/
import {Version} from '@angular/core';
/**
* @stable
*/
export const VERSION = new Version('0.0.0-PLACEHOLDER');
export {VERSION} from './src/version';
export * from './src/template_parser/template_ast';
export {TEMPLATE_TRANSFORMS} from './src/template_parser/template_parser';
export {CompilerConfig, RenderTypes} from './src/config';
@ -36,6 +32,7 @@ export * from './src/aot/compiler_host';
export * from './src/aot/static_reflector';
export * from './src/aot/static_reflection_capabilities';
export * from './src/aot/static_symbol';
export * from './src/aot/summary_resolver';
export {JitCompiler} from './src/jit/compiler';
export * from './src/jit/compiler_factory';
export * from './src/url_resolver';

View File

@ -10,7 +10,7 @@ import {SchemaMetadata} from '@angular/core';
import {AnimationCompiler} from '../animation/animation_compiler';
import {AnimationParser} from '../animation/animation_parser';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, createHostComponentMeta, identifierModuleUrl, identifierName} from '../compile_metadata';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, CompileTypeSummary, createHostComponentMeta, identifierModuleUrl, identifierName} from '../compile_metadata';
import {DirectiveNormalizer} from '../directive_normalizer';
import {DirectiveWrapperCompileResult, DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
import {ListWrapper} from '../facade/collection';
@ -24,12 +24,11 @@ import {TemplateParser} from '../template_parser/template_parser';
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency, ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler';
import {AotCompilerOptions} from './compiler_options';
import {GeneratedFile} from './generated_file';
import {StaticReflector} from './static_reflector';
import {StaticSymbol} from './static_symbol';
export class SourceModule {
constructor(public fileUrl: string, public moduleUrl: string, public source: string) {}
}
import {AotSummaryResolver} from './summary_resolver';
import {filterFileByPatterns} from './utils';
export class AotCompiler {
private _animationCompiler = new AnimationCompiler();
@ -39,31 +38,45 @@ export class AotCompiler {
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
private _dirWrapperCompiler: DirectiveWrapperCompiler,
private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter,
private _localeId: string, private _translationFormat: string,
private _animationParser: AnimationParser, private _staticReflector: StaticReflector,
private _options: AotCompilerOptions) {}
private _summaryResolver: AotSummaryResolver, private _localeId: string,
private _translationFormat: string, private _animationParser: AnimationParser,
private _staticReflector: StaticReflector, private _options: AotCompilerOptions) {}
clearCache() { this._metadataResolver.clearCache(); }
compileAll(rootFiles: string[]): Promise<SourceModule[]> {
compileAll(rootFiles: string[]): Promise<GeneratedFile[]> {
const programSymbols = extractProgramSymbols(this._staticReflector, rootFiles, this._options);
const {ngModuleByPipeOrDirective, files, ngModules} =
analyzeAndValidateNgModules(programSymbols, this._options, this._metadataResolver);
return loadNgModuleDirectives(ngModules).then(() => {
const sourceModules = files.map(
file => this._compileSrcFile(
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.ngModules));
return ListWrapper.flatten(sourceModules);
});
return Promise
.all(ngModules.map(
ngModule => this._metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
ngModule.type.reference, false)))
.then(() => {
const sourceModules = files.map(
file => this._compileSrcFile(
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.pipes,
file.ngModules));
return ListWrapper.flatten(sourceModules);
});
}
private _compileSrcFile(
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
directives: StaticSymbol[], ngModules: StaticSymbol[]): SourceModule[] {
directives: StaticSymbol[], pipes: StaticSymbol[],
ngModules: StaticSymbol[]): GeneratedFile[] {
const fileSuffix = _splitTypescriptSuffix(srcFileUrl)[1];
const statements: o.Statement[] = [];
const exportedVars: string[] = [];
const outputSourceModules: SourceModule[] = [];
const generatedFiles: GeneratedFile[] = [];
// write summary files
const summaries: CompileTypeSummary[] = [
...ngModules.map(ref => this._metadataResolver.getNgModuleSummary(ref)),
...directives.map(ref => this._metadataResolver.getDirectiveSummary(ref)),
...pipes.map(ref => this._metadataResolver.getPipeSummary(ref))
];
generatedFiles.push(this._summaryResolver.serializeSummaries(srcFileUrl, summaries));
// compile all ng modules
exportedVars.push(
@ -90,7 +103,7 @@ export class AotCompiler {
// compile styles
const stylesCompileResults = this._styleCompiler.compileComponent(compMeta);
stylesCompileResults.externalStylesheets.forEach((compiledStyleSheet) => {
outputSourceModules.push(this._codgenStyles(srcFileUrl, compiledStyleSheet, fileSuffix));
generatedFiles.push(this._codgenStyles(srcFileUrl, compiledStyleSheet, fileSuffix));
});
// compile components
@ -103,9 +116,9 @@ export class AotCompiler {
if (statements.length > 0) {
const srcModule = this._codegenSourceModule(
srcFileUrl, _ngfactoryModuleUrl(srcFileUrl), statements, exportedVars);
outputSourceModules.unshift(srcModule);
generatedFiles.unshift(srcModule);
}
return outputSourceModules;
return generatedFiles;
}
private _compileModule(ngModuleType: StaticSymbol, targetStatements: o.Statement[]): string {
@ -201,7 +214,7 @@ export class AotCompiler {
}
private _codgenStyles(
fileUrl: string, stylesCompileResult: CompiledStylesheet, fileSuffix: string): SourceModule {
fileUrl: string, stylesCompileResult: CompiledStylesheet, fileSuffix: string): GeneratedFile {
_resolveStyleStatements(this._staticReflector, stylesCompileResult, fileSuffix);
return this._codegenSourceModule(
fileUrl, _stylesModuleUrl(
@ -210,11 +223,11 @@ export class AotCompiler {
}
private _codegenSourceModule(
fileUrl: string, moduleUrl: string, statements: o.Statement[],
exportedVars: string[]): SourceModule {
return new SourceModule(
fileUrl, moduleUrl,
this._outputEmitter.emitStatements(moduleUrl, statements, exportedVars));
srcFileUrl: string, genFileUrl: string, statements: o.Statement[],
exportedVars: string[]): GeneratedFile {
return new GeneratedFile(
srcFileUrl, genFileUrl,
this._outputEmitter.emitStatements(genFileUrl, statements, exportedVars));
}
}
@ -286,7 +299,12 @@ function _splitTypescriptSuffix(path: string): string[] {
export interface NgAnalyzedModules {
ngModules: CompileNgModuleMetadata[];
ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>;
files: Array<{srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}>;
files: Array<{
srcUrl: string,
directives: StaticSymbol[],
pipes: StaticSymbol[],
ngModules: StaticSymbol[]
}>;
symbolsMissingModule?: StaticSymbol[];
}
@ -313,14 +331,6 @@ export function analyzeAndValidateNgModules(
return result;
}
// Wait for the directives in the given modules have been loaded
export function loadNgModuleDirectives(ngModules: CompileNgModuleMetadata[]) {
return Promise
.all(ListWrapper.flatten(ngModules.map(
(ngModule) => ngModule.transitiveModule.directiveLoaders.map(loader => loader()))))
.then(() => {});
}
function _analyzeNgModules(
ngModuleMetas: CompileNgModuleMetadata[],
symbolsMissingModule: StaticSymbol[]): NgAnalyzedModules {
@ -329,11 +339,13 @@ function _analyzeNgModules(
const ngModuleByPipeOrDirective = new Map<StaticSymbol, CompileNgModuleMetadata>();
const ngModulesByFile = new Map<string, StaticSymbol[]>();
const ngDirectivesByFile = new Map<string, StaticSymbol[]>();
const ngPipesByFile = new Map<string, StaticSymbol[]>();
const filePaths = new Set<string>();
// Looping over all modules to construct:
// - a map from file to modules `ngModulesByFile`,
// - a map from file to directives `ngDirectivesByFile`,
// - a map from file to pipes `ngPipesByFile`,
// - a map from directive/pipe to module `ngModuleByPipeOrDirective`.
ngModuleMetas.forEach((ngModuleMeta) => {
const srcFileUrl = ngModuleMeta.type.reference.filePath;
@ -351,16 +363,23 @@ function _analyzeNgModules(
ngModuleMeta.declaredPipes.forEach((pipeIdentifier) => {
const fileUrl = pipeIdentifier.reference.filePath;
filePaths.add(fileUrl);
ngPipesByFile.set(
fileUrl, (ngPipesByFile.get(fileUrl) || []).concat(pipeIdentifier.reference));
ngModuleByPipeOrDirective.set(pipeIdentifier.reference, ngModuleMeta);
});
});
const files: {srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}[] = [];
const files:
{srcUrl: string,
directives: StaticSymbol[],
pipes: StaticSymbol[],
ngModules: StaticSymbol[]}[] = [];
filePaths.forEach((srcUrl) => {
const directives = ngDirectivesByFile.get(srcUrl) || [];
const pipes = ngPipesByFile.get(srcUrl) || [];
const ngModules = ngModulesByFile.get(srcUrl) || [];
files.push({srcUrl, directives, ngModules});
files.push({srcUrl, directives, pipes, ngModules});
});
return {
@ -376,7 +395,7 @@ export function extractProgramSymbols(
staticReflector: StaticReflector, files: string[],
options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp} = {}): StaticSymbol[] {
const staticSymbols: StaticSymbol[] = [];
files.filter(fileName => _filterFileByPatterns(fileName, options)).forEach(sourceFile => {
files.filter(fileName => filterFileByPatterns(fileName, options)).forEach(sourceFile => {
const moduleMetadata = staticReflector.getModuleMetadata(sourceFile);
if (!moduleMetadata) {
console.error(`WARNING: no metadata found for ${sourceFile}`);
@ -414,16 +433,16 @@ function _createNgModules(
const ngModulePipesAndDirective = new Set<StaticSymbol>();
const addNgModule = (staticSymbol: any) => {
if (ngModules.has(staticSymbol) || !_filterFileByPatterns(staticSymbol.filePath, options)) {
if (ngModules.has(staticSymbol) || !filterFileByPatterns(staticSymbol.filePath, options)) {
return false;
}
const ngModule = metadataResolver.getUnloadedNgModuleMetadata(staticSymbol, false, false);
const ngModule = metadataResolver.getNgModuleMetadata(staticSymbol, false);
if (ngModule) {
ngModules.set(ngModule.type.reference, ngModule);
ngModule.declaredDirectives.forEach((dir) => ngModulePipesAndDirective.add(dir.reference));
ngModule.declaredPipes.forEach((pipe) => ngModulePipesAndDirective.add(pipe.reference));
// For every input module add the list of transitively included modules
ngModule.transitiveModule.modules.forEach(modMeta => addNgModule(modMeta.type.reference));
ngModule.transitiveModule.modules.forEach(modMeta => addNgModule(modMeta.reference));
}
return !!ngModule;
};
@ -440,15 +459,3 @@ function _createNgModules(
return {ngModules: Array.from(ngModules.values()), symbolsMissingModule};
}
function _filterFileByPatterns(
fileName: string, options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp} = {}) {
let match = true;
if (options.includeFilePattern) {
match = match && !!options.includeFilePattern.exec(fileName);
}
if (options.excludeFilePattern) {
match = match && !options.excludeFilePattern.exec(fileName);
}
return match;
}

View File

@ -34,8 +34,7 @@ import {AotCompilerHost} from './compiler_host';
import {AotCompilerOptions} from './compiler_options';
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
import {StaticReflector} from './static_reflector';
import {AotSummaryResolver} from './summary_resolver';
/**
* Creates a new AotCompiler based on options and a host.
@ -61,15 +60,17 @@ export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCom
const console = new Console();
const tmplParser =
new TemplateParser(expressionParser, elementSchemaRegistry, htmlParser, console, []);
const summaryResolver = new AotSummaryResolver(compilerHost, staticReflector, options);
const resolver = new CompileMetadataResolver(
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
new PipeResolver(staticReflector), elementSchemaRegistry, normalizer, staticReflector);
new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
staticReflector);
// TODO(vicb): do not pass options.i18nFormat here
const compiler = new AotCompiler(
resolver, tmplParser, new StyleCompiler(urlResolver),
new ViewCompiler(config, elementSchemaRegistry),
new DirectiveWrapperCompiler(config, expressionParser, elementSchemaRegistry, console),
new NgModuleCompiler(), new TypeScriptEmitter(compilerHost), options.locale,
new NgModuleCompiler(), new TypeScriptEmitter(compilerHost), summaryResolver, options.locale,
options.i18nFormat, new AnimationParser(elementSchemaRegistry), staticReflector, options);
return {compiler, reflector: staticReflector};
}

View File

@ -10,15 +10,16 @@ import {ImportResolver} from '../output/path_util';
import {StaticReflectorHost} from './static_reflector';
import {StaticSymbol} from './static_symbol';
import {AotSummaryResolverHost} from './summary_resolver';
/**
* The host of the AotCompiler disconnects the implementation from TypeScript / other language
* services and from underlying file systems.
*/
export interface AotCompilerHost extends StaticReflectorHost, ImportResolver {
export interface AotCompilerHost extends StaticReflectorHost, ImportResolver,
AotSummaryResolverHost {
/**
* Loads a resource (e.g. html / css)
*/
loadResource(path: string): Promise<string>;
}
}

View File

@ -6,8 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
/**
* @module
* @description
* This module provides a set of common Pipes.
*/
export class GeneratedFile {
constructor(public srcFileUrl: string, public genFileUrl: string, public source: string) {}
}

View File

@ -81,7 +81,8 @@ export class StaticReflector implements ReflectorReader {
private host: StaticReflectorHost,
private staticSymbolCache: StaticSymbolCache = new StaticSymbolCache(),
knownMetadataClasses: {name: string, filePath: string, ctor: any}[] = [],
knownMetadataFunctions: {name: string, filePath: string, fn: any}[] = []) {
knownMetadataFunctions: {name: string, filePath: string, fn: any}[] = [],
private errorRecorder?: (error: any, fileName: string) => void) {
this.initializeConversionMap();
knownMetadataClasses.forEach(
(kc) => this._registerDecoratorOrConstructor(
@ -155,7 +156,10 @@ export class StaticReflector implements ReflectorReader {
public parameters(type: StaticSymbol): any[] {
if (!(type instanceof StaticSymbol)) {
throw new Error(`parameters received ${JSON.stringify(type)} which is not a StaticSymbol`);
this.reportError(
new Error(`parameters received ${JSON.stringify(type)} which is not a StaticSymbol`),
type);
return [];
}
try {
let parameters = this.parameterCache.get(type);
@ -219,8 +223,10 @@ export class StaticReflector implements ReflectorReader {
hasLifecycleHook(type: any, lcProperty: string): boolean {
if (!(type instanceof StaticSymbol)) {
throw new Error(
`hasLifecycleHook received ${JSON.stringify(type)} which is not a StaticSymbol`);
this.reportError(
new Error(
`hasLifecycleHook received ${JSON.stringify(type)} which is not a StaticSymbol`),
type);
}
try {
return !!this._methodNames(type)[lcProperty];
@ -301,11 +307,21 @@ export class StaticReflector implements ReflectorReader {
return this.staticSymbolCache.get(declarationFile, name, members);
}
private reportError(error: Error, context: StaticSymbol, path?: string) {
if (this.errorRecorder) {
this.errorRecorder(error, (context && context.filePath) || path);
} else {
throw error;
}
}
private resolveExportedSymbol(filePath: string, symbolName: string): StaticSymbol {
const resolveModule = (moduleName: string): string => {
const resolvedModulePath = this.host.moduleNameToFileName(moduleName, filePath);
if (!resolvedModulePath) {
throw new Error(`Could not resolve module '${moduleName}' relative to file ${filePath}`);
this.reportError(
new Error(`Could not resolve module '${moduleName}' relative to file ${filePath}`),
null, filePath);
}
return resolvedModulePath;
};
@ -338,7 +354,12 @@ export class StaticReflector implements ReflectorReader {
if (typeof exportSymbol !== 'string') {
symName = exportSymbol.name;
}
staticSymbol = this.resolveExportedSymbol(resolveModule(moduleExport.from), symName);
const resolvedModule = resolveModule(moduleExport.from);
if (resolvedModule) {
staticSymbol =
this.resolveExportedSymbol(resolveModule(moduleExport.from), symName);
break;
}
}
}
}
@ -348,10 +369,12 @@ export class StaticReflector implements ReflectorReader {
for (const moduleExport of metadata['exports']) {
if (!moduleExport.export) {
const resolvedModule = resolveModule(moduleExport.from);
const candidateSymbol = this.resolveExportedSymbol(resolvedModule, symbolName);
if (candidateSymbol) {
staticSymbol = candidateSymbol;
break;
if (resolvedModule) {
const candidateSymbol = this.resolveExportedSymbol(resolvedModule, symbolName);
if (candidateSymbol) {
staticSymbol = candidateSymbol;
break;
}
}
}
}
@ -689,7 +712,16 @@ export class StaticReflector implements ReflectorReader {
}
}
const result = simplifyInContext(context, value, 0);
const recordedSimplifyInContext = (context: StaticSymbol, value: any, depth: number) => {
try {
return simplifyInContext(context, value, depth);
} catch (e) {
this.reportError(e, context);
}
};
const result = this.errorRecorder ? recordedSimplifyInContext(context, value, 0) :
simplifyInContext(context, value, 0);
if (shouldIgnore(result)) {
return undefined;
}
@ -717,8 +749,10 @@ export class StaticReflector implements ReflectorReader {
{__symbolic: 'module', version: SUPPORTED_SCHEMA_VERSION, module: module, metadata: {}};
}
if (moduleMetadata['version'] != SUPPORTED_SCHEMA_VERSION) {
throw new Error(
`Metadata version mismatch for module ${module}, found version ${moduleMetadata['version']}, expected ${SUPPORTED_SCHEMA_VERSION}`);
this.reportError(
new Error(
`Metadata version mismatch for module ${module}, found version ${moduleMetadata['version']}, expected ${SUPPORTED_SCHEMA_VERSION}`),
null);
}
this.metadataCache.set(module, moduleMetadata);
}

View File

@ -6,10 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
export function isStaticSymbol(value: any): value is StaticSymbol {
return typeof value === 'object' && value !== null && value['name'] && value['filePath'];
}
/**
* A token representing the a reference to a static type.
*

View File

@ -0,0 +1,124 @@
/**
* @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 {CompileDirectiveSummary, CompileIdentifierMetadata, CompileNgModuleSummary, CompilePipeSummary, CompileSummaryKind, CompileTypeMetadata, CompileTypeSummary, identifierModuleUrl, identifierName} from '../compile_metadata';
import {SummaryResolver} from '../summary_resolver';
import {GeneratedFile} from './generated_file';
import {StaticReflector} from './static_reflector';
import {StaticSymbol} from './static_symbol';
import {filterFileByPatterns} from './utils';
const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
export interface AotSummaryResolverHost {
/**
* Loads an NgModule/Directive/Pipe summary file
*/
loadSummary(filePath: string): string;
/**
* Returns the output file path of a source file.
* E.g.
* `some_file.ts` -> `some_file.d.ts`
*/
getOutputFileName(sourceFilePath: string): string;
}
export interface AotSummaryResolverOptions {
includeFilePattern?: RegExp;
excludeFilePattern?: RegExp;
}
export class AotSummaryResolver implements SummaryResolver {
private summaryCache: {[cacheKey: string]: CompileTypeSummary} = {};
constructor(
private host: AotSummaryResolverHost, private staticReflector: StaticReflector,
private options: AotSummaryResolverOptions) {}
serializeSummaries(srcFileUrl: string, summaries: CompileTypeSummary[]): GeneratedFile {
const jsonReplacer = (key: string, value: any) => {
if (value instanceof StaticSymbol) {
// We convert the source filenames into output filenames,
// as the generated summary file will be used when the current
// compilation unit is used as a library
return {
'__symbolic__': 'symbol',
'name': value.name,
'path': this.host.getOutputFileName(value.filePath),
'members': value.members
};
}
return value;
};
const allSummaries = summaries.slice();
summaries.forEach((summary) => {
if (summary.summaryKind === CompileSummaryKind.NgModule) {
const moduleMeta = <CompileNgModuleSummary>summary;
moduleMeta.exportedDirectives.concat(moduleMeta.exportedPipes).forEach((id) => {
if (!filterFileByPatterns(id.reference.filePath, this.options)) {
allSummaries.push(this.resolveSummary(id.reference));
}
});
}
});
return new GeneratedFile(
srcFileUrl, summaryFileName(srcFileUrl), JSON.stringify(allSummaries, jsonReplacer));
}
private _cacheKey(symbol: StaticSymbol) { return `${symbol.filePath}|${symbol.name}`; }
resolveSummary(staticSymbol: StaticSymbol): any {
const filePath = staticSymbol.filePath;
const name = staticSymbol.name;
const cacheKey = this._cacheKey(staticSymbol);
if (!filterFileByPatterns(filePath, this.options)) {
let summary = this.summaryCache[cacheKey];
const summaryFilePath = summaryFileName(filePath);
if (!summary) {
try {
const jsonReviver = (key: string, value: any) => {
if (value && value['__symbolic__'] === 'symbol') {
// Note: We can't use staticReflector.findDeclaration here:
// Summary files can contain symbols of transitive compilation units
// (via the providers), and findDeclaration needs .metadata.json / .d.ts files,
// but we don't want to depend on these for transitive dependencies.
return this.staticReflector.getStaticSymbol(
value['path'], value['name'], value['members']);
} else {
return value;
}
};
const readSummaries: CompileTypeSummary[] =
JSON.parse(this.host.loadSummary(summaryFilePath), jsonReviver);
readSummaries.forEach((summary) => {
const filePath = summary.type.reference.filePath;
this.summaryCache[this._cacheKey(summary.type.reference)] = summary;
});
summary = this.summaryCache[cacheKey];
} catch (e) {
console.error(`Error loading summary file ${summaryFilePath}`);
throw e;
}
}
if (!summary) {
throw new Error(
`Could not find the symbol ${name} in the summary file ${summaryFilePath}!`);
}
return summary;
} else {
return null;
}
}
}
function summaryFileName(fileName: string): string {
const fileNameWithoutSuffix = fileName.replace(STRIP_SRC_FILE_SUFFIXES, '');
return `${fileNameWithoutSuffix}.ngsummary.json`;
}

View File

@ -0,0 +1,19 @@
/**
* @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
*/
export function filterFileByPatterns(
fileName: string, options: {includeFilePattern?: RegExp, excludeFilePattern?: RegExp} = {}) {
let match = true;
if (options.includeFilePattern) {
match = match && !!options.includeFilePattern.exec(fileName);
}
if (options.excludeFilePattern) {
match = match && !options.excludeFilePattern.exec(fileName);
}
return match;
}

View File

@ -8,7 +8,7 @@
import {ChangeDetectionStrategy, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core';
import {StaticSymbol, isStaticSymbol} from './aot/static_symbol';
import {StaticSymbol} from './aot/static_symbol';
import {ListWrapper} from './facade/collection';
import {isPresent, stringify} from './facade/lang';
import {LifecycleHooks, reflector} from './private_import_core';
@ -87,7 +87,7 @@ export function identifierName(compileIdentifier: CompileIdentifierMetadata): st
return null;
}
const ref = compileIdentifier.reference;
if (isStaticSymbol(ref)) {
if (ref instanceof StaticSymbol) {
return ref.name;
}
if (ref['__anonymousType']) {
@ -106,7 +106,7 @@ export function identifierName(compileIdentifier: CompileIdentifierMetadata): st
export function identifierModuleUrl(compileIdentifier: CompileIdentifierMetadata): string {
const ref = compileIdentifier.reference;
if (isStaticSymbol(ref)) {
if (ref instanceof StaticSymbol) {
return ref.filePath;
}
return reflector.importUri(ref);
@ -114,14 +114,21 @@ export function identifierModuleUrl(compileIdentifier: CompileIdentifierMetadata
export interface CompileIdentifierMetadata { reference: any; }
export enum CompileSummaryKind {
Template,
Pipe,
Directive,
NgModule
}
/**
* A CompileSummary is the data needed to use a directive / pipe / module
* in other modules / components. However, this data is not enough to compile
* the directive / module itself.
*/
export interface CompileSummary {
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
}
export interface CompileSummary { summaryKind: CompileSummaryKind; }
export interface CompileTypeSummary extends CompileSummary { type: CompileTypeMetadata; }
export interface CompileDiDependencyMetadata {
isAttribute?: boolean;
@ -204,7 +211,6 @@ export class CompileStylesheetMetadata {
* Summary Metadata regarding compilation of a template.
*/
export interface CompileTemplateSummary extends CompileSummary {
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
animations: string[];
ngContentSelectors: string[];
encapsulation: ViewEncapsulation;
@ -252,7 +258,7 @@ export class CompileTemplateMetadata {
toSummary(): CompileTemplateSummary {
return {
isSummary: true,
summaryKind: CompileSummaryKind.Template,
animations: this.animations.map(anim => anim.name),
ngContentSelectors: this.ngContentSelectors,
encapsulation: this.encapsulation
@ -262,8 +268,7 @@ export class CompileTemplateMetadata {
// Note: This should only use interfaces as nested data types
// as we need to be able to serialize this from/to JSON!
export interface CompileDirectiveSummary extends CompileSummary {
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
export interface CompileDirectiveSummary extends CompileTypeSummary {
type: CompileTypeMetadata;
isComponent: boolean;
selector: string;
@ -419,7 +424,7 @@ export class CompileDirectiveMetadata {
toSummary(): CompileDirectiveSummary {
return {
isSummary: true,
summaryKind: CompileSummaryKind.Directive,
type: this.type,
isComponent: this.isComponent,
selector: this.selector,
@ -470,8 +475,7 @@ export function createHostComponentMeta(
});
}
export interface CompilePipeSummary extends CompileSummary {
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
export interface CompilePipeSummary extends CompileTypeSummary {
type: CompileTypeMetadata;
name: string;
pure: boolean;
@ -493,36 +497,32 @@ export class CompilePipeMetadata {
}
toSummary(): CompilePipeSummary {
return {isSummary: true, type: this.type, name: this.name, pure: this.pure};
return {
summaryKind: CompileSummaryKind.Pipe,
type: this.type,
name: this.name,
pure: this.pure
};
}
}
// Note: This should only use interfaces as nested data types
// as we need to be able to serialize this from/to JSON!
export interface CompileNgModuleInjectorSummary extends CompileSummary {
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
export interface CompileNgModuleSummary extends CompileTypeSummary {
type: CompileTypeMetadata;
entryComponents: CompileIdentifierMetadata[];
providers: CompileProviderMetadata[];
importedModules: CompileNgModuleInjectorSummary[];
exportedModules: CompileNgModuleInjectorSummary[];
}
// Note: This should only use interfaces as nested data types
// as we need to be able to serialize this from/to JSON!
export interface CompileNgModuleDirectiveSummary extends CompileSummary {
isSummary: boolean /* TODO: `true` when we drop TS 1.8 support */;
type: CompileTypeMetadata;
// Note: This is transitive over the exported modules.
exportedDirectives: CompileIdentifierMetadata[];
// Note: This is transitive over the exported modules.
exportedPipes: CompileIdentifierMetadata[];
exportedModules: CompileNgModuleDirectiveSummary[];
directiveLoaders: (() => Promise<void>)[];
}
// Note: This should only use interfaces as nested data types
// as we need to be able to serialize this from/to JSON!
export type CompileNgModuleSummary =
CompileNgModuleInjectorSummary & CompileNgModuleDirectiveSummary;
// Note: This is transitive.
entryComponents: CompileIdentifierMetadata[];
// Note: This is transitive.
providers: {provider: CompileProviderMetadata, module: CompileIdentifierMetadata}[];
// Note: This is transitive.
modules: CompileTypeMetadata[];
}
/**
* Metadata regarding compilation of a module.
@ -532,6 +532,7 @@ export class CompileNgModuleMetadata {
declaredDirectives: CompileIdentifierMetadata[];
exportedDirectives: CompileIdentifierMetadata[];
declaredPipes: CompileIdentifierMetadata[];
exportedPipes: CompileIdentifierMetadata[];
entryComponents: CompileIdentifierMetadata[];
bootstrapComponents: CompileIdentifierMetadata[];
@ -579,51 +580,72 @@ export class CompileNgModuleMetadata {
toSummary(): CompileNgModuleSummary {
return {
isSummary: true,
summaryKind: CompileSummaryKind.NgModule,
type: this.type,
entryComponents: this.entryComponents,
providers: this.providers,
importedModules: this.importedModules,
exportedModules: this.exportedModules,
exportedDirectives: this.exportedDirectives,
exportedPipes: this.exportedPipes,
directiveLoaders: this.transitiveModule.directiveLoaders
};
}
toInjectorSummary(): CompileNgModuleInjectorSummary {
return {
isSummary: true,
type: this.type,
entryComponents: this.entryComponents,
providers: this.providers,
importedModules: this.importedModules,
exportedModules: this.exportedModules
};
}
toDirectiveSummary(): CompileNgModuleDirectiveSummary {
return {
isSummary: true,
type: this.type,
exportedDirectives: this.exportedDirectives,
exportedPipes: this.exportedPipes,
exportedModules: this.exportedModules,
directiveLoaders: this.transitiveModule.directiveLoaders
entryComponents: this.transitiveModule.entryComponents,
providers: this.transitiveModule.providers,
modules: this.transitiveModule.modules,
exportedDirectives: this.transitiveModule.exportedDirectives,
exportedPipes: this.transitiveModule.exportedPipes
};
}
}
export class TransitiveCompileNgModuleMetadata {
directivesSet = new Set<any>();
directives: CompileIdentifierMetadata[] = [];
exportedDirectivesSet = new Set<any>();
exportedDirectives: CompileIdentifierMetadata[] = [];
pipesSet = new Set<any>();
pipes: CompileIdentifierMetadata[] = [];
exportedPipesSet = new Set<any>();
exportedPipes: CompileIdentifierMetadata[] = [];
modulesSet = new Set<any>();
modules: CompileTypeMetadata[] = [];
entryComponentsSet = new Set<any>();
entryComponents: CompileIdentifierMetadata[] = [];
constructor(
public modules: CompileNgModuleInjectorSummary[], public providers: CompileProviderMetadata[],
public entryComponents: CompileIdentifierMetadata[],
public directives: CompileIdentifierMetadata[], public pipes: CompileIdentifierMetadata[],
public directiveLoaders: (() => Promise<void>)[]) {
directives.forEach(dir => this.directivesSet.add(dir.reference));
pipes.forEach(pipe => this.pipesSet.add(pipe.reference));
providers: {provider: CompileProviderMetadata, module: CompileIdentifierMetadata}[] = [];
addProvider(provider: CompileProviderMetadata, module: CompileIdentifierMetadata) {
this.providers.push({provider: provider, module: module});
}
addDirective(id: CompileIdentifierMetadata) {
if (!this.directivesSet.has(id.reference)) {
this.directivesSet.add(id.reference);
this.directives.push(id);
}
}
addExportedDirective(id: CompileIdentifierMetadata) {
if (!this.exportedDirectivesSet.has(id.reference)) {
this.exportedDirectivesSet.add(id.reference);
this.exportedDirectives.push(id);
}
}
addPipe(id: CompileIdentifierMetadata) {
if (!this.pipesSet.has(id.reference)) {
this.pipesSet.add(id.reference);
this.pipes.push(id);
}
}
addExportedPipe(id: CompileIdentifierMetadata) {
if (!this.exportedPipesSet.has(id.reference)) {
this.exportedPipesSet.add(id.reference);
this.exportedPipes.push(id);
}
}
addModule(id: CompileTypeMetadata) {
if (!this.modulesSet.has(id.reference)) {
this.modulesSet.add(id.reference);
this.modules.push(id);
}
}
addEntryComponent(id: CompileIdentifierMetadata) {
if (!this.entryComponentsSet.has(id.reference)) {
this.entryComponentsSet.add(id.reference);
this.entryComponents.push(id);
}
}
}

View File

@ -284,8 +284,13 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
}
visitKeyedRead(ast: cdAst.KeyedRead, mode: _Mode): any {
return convertToStatementIfNeeded(
mode, this.visit(ast.obj, _Mode.Expression).key(this.visit(ast.key, _Mode.Expression)));
const leftMostSafe = this.leftMostSafeNode(ast);
if (leftMostSafe) {
return this.convertSafeAccess(ast, leftMostSafe, mode);
} else {
return convertToStatementIfNeeded(
mode, this.visit(ast.obj, _Mode.Expression).key(this.visit(ast.key, _Mode.Expression)));
}
}
visitKeyedWrite(ast: cdAst.KeyedWrite, mode: _Mode): any {

View File

@ -12,9 +12,10 @@
*/
import {ViewEncapsulation} from '@angular/core';
import {analyzeAndValidateNgModules, extractProgramSymbols, loadNgModuleDirectives} from '../aot/compiler';
import {analyzeAndValidateNgModules, extractProgramSymbols} from '../aot/compiler';
import {StaticAndDynamicReflectionCapabilities} from '../aot/static_reflection_capabilities';
import {StaticReflector, StaticReflectorHost} from '../aot/static_reflector';
import {AotSummaryResolver, AotSummaryResolverHost} from '../aot/summary_resolver';
import {CompileDirectiveMetadata} from '../compile_metadata';
import {CompilerConfig} from '../config';
import {DirectiveNormalizer} from '../directive_normalizer';
@ -41,7 +42,7 @@ export interface ExtractorOptions {
* The host of the Extractor disconnects the implementation from TypeScript / other language
* services and from underlying file systems.
*/
export interface ExtractorHost extends StaticReflectorHost {
export interface ExtractorHost extends StaticReflectorHost, AotSummaryResolverHost {
/**
* Loads a resource (e.g. html / css)
*/
@ -58,32 +59,36 @@ export class Extractor {
const programSymbols = extractProgramSymbols(this.staticReflector, rootFiles, this.options);
const {ngModuleByPipeOrDirective, files, ngModules} =
analyzeAndValidateNgModules(programSymbols, this.options, this.metadataResolver);
return loadNgModuleDirectives(ngModules).then(() => {
const errors: ParseError[] = [];
return Promise
.all(ngModules.map(
ngModule => this.metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
ngModule.type.reference, false)))
.then(() => {
const errors: ParseError[] = [];
files.forEach(file => {
const compMetas: CompileDirectiveMetadata[] = [];
file.directives.forEach(directiveType => {
const dirMeta = this.metadataResolver.getDirectiveMetadata(directiveType);
if (dirMeta && dirMeta.isComponent) {
compMetas.push(dirMeta);
files.forEach(file => {
const compMetas: CompileDirectiveMetadata[] = [];
file.directives.forEach(directiveType => {
const dirMeta = this.metadataResolver.getDirectiveMetadata(directiveType);
if (dirMeta && dirMeta.isComponent) {
compMetas.push(dirMeta);
}
});
compMetas.forEach(compMeta => {
const html = compMeta.template.template;
const interpolationConfig =
InterpolationConfig.fromArray(compMeta.template.interpolation);
errors.push(
...this.messageBundle.updateFromTemplate(html, file.srcUrl, interpolationConfig));
});
});
if (errors.length) {
throw new Error(errors.map(e => e.toString()).join('\n'));
}
});
compMetas.forEach(compMeta => {
const html = compMeta.template.template;
const interpolationConfig =
InterpolationConfig.fromArray(compMeta.template.interpolation);
errors.push(
...this.messageBundle.updateFromTemplate(html, file.srcUrl, interpolationConfig));
});
});
if (errors.length) {
throw new Error(errors.map(e => e.toString()).join('\n'));
}
return this.messageBundle;
});
return this.messageBundle;
});
}
static create(host: ExtractorHost, options: ExtractorOptions):
@ -106,7 +111,8 @@ export class Extractor {
const elementSchemaRegistry = new DomElementSchemaRegistry();
const resolver = new CompileMetadataResolver(
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
new PipeResolver(staticReflector), elementSchemaRegistry, normalizer, staticReflector);
new PipeResolver(staticReflector), new AotSummaryResolver(host, staticReflector, options),
elementSchemaRegistry, normalizer, staticReflector);
// TODO(vicb): implicit tags & attributes
const messageBundle = new MessageBundle(htmlParser, [], {});

View File

@ -8,7 +8,7 @@
import {ANALYZE_FOR_ENTRY_COMPONENTS, AnimationTransitionEvent, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, LOCALE_ID, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
import {StaticSymbol, isStaticSymbol} from './aot/static_symbol';
import {StaticSymbol} from './aot/static_symbol';
import {CompileIdentifierMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName} from './compile_metadata';
import {AnimationGroupPlayer, AnimationKeyframe, AnimationSequencePlayer, AnimationStyles, AnimationTransition, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, ComponentRef_, DebugAppView, DebugContext, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, UNINITIALIZED, ValueUnwrapper, ViewContainer, ViewType, balanceAnimationKeyframes, clearStyles, collectAndResolveStyles, devModeEqual, prepareFinalAnimationStyles, reflector, registerModuleFactory, renderStyles, view_utils} from './private_import_core';

View File

@ -101,16 +101,14 @@ export class JitCompiler implements Compiler {
private _loadModules(mainModule: any, isSync: boolean): Promise<any> {
const loadingPromises: Promise<any>[] = [];
const {ngModule, loading} = this._metadataResolver.loadNgModuleMetadata(mainModule, isSync);
loadingPromises.push(loading);
const ngModule = this._metadataResolver.getNgModuleMetadata(mainModule);
// Note: the loadingPromise for a module only includes the loading of the exported directives
// of imported modules.
// However, for runtime compilation, we want to transitively compile all modules,
// so we also need to call loadNgModuleMetadata for all nested modules.
ngModule.transitiveModule.modules.forEach((localModuleMeta) => {
loadingPromises.push(
this._metadataResolver.loadNgModuleMetadata(localModuleMeta.type.reference, isSync)
.loading);
loadingPromises.push(this._metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
localModuleMeta.reference, isSync));
});
return Promise.all(loadingPromises);
}
@ -150,7 +148,7 @@ export class JitCompiler implements Compiler {
ngModule.transitiveModule.modules.forEach((localModuleSummary) => {
const localModuleMeta =
this._metadataResolver.getNgModuleMetadata(localModuleSummary.type.reference);
this._metadataResolver.getNgModuleMetadata(localModuleSummary.reference);
localModuleMeta.declaredDirectives.forEach((dirIdentifier) => {
moduleByDirective.set(dirIdentifier.reference, localModuleMeta);
const dirMeta = this._metadataResolver.getDirectiveMetadata(dirIdentifier.reference);
@ -168,7 +166,7 @@ export class JitCompiler implements Compiler {
});
ngModule.transitiveModule.modules.forEach((localModuleSummary) => {
const localModuleMeta =
this._metadataResolver.getNgModuleMetadata(localModuleSummary.type.reference);
this._metadataResolver.getNgModuleMetadata(localModuleSummary.reference);
localModuleMeta.declaredDirectives.forEach((dirIdentifier) => {
const dirMeta = this._metadataResolver.getDirectiveMetadata(dirIdentifier.reference);
if (dirMeta.isComponent) {

View File

@ -26,6 +26,7 @@ import {ResourceLoader} from '../resource_loader';
import {DomElementSchemaRegistry} from '../schema/dom_element_schema_registry';
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
import {StyleCompiler} from '../style_compiler';
import {SummaryResolver} from '../summary_resolver';
import {TemplateParser} from '../template_parser/template_parser';
import {DEFAULT_PACKAGE_URL_PROVIDER, UrlResolver} from '../url_resolver';
import {ViewCompiler} from '../view_compiler/view_compiler';
@ -46,6 +47,7 @@ export const COMPILER_PROVIDERS: Array<any|Type<any>|{[k: string]: any}|any[]> =
{provide: Reflector, useValue: reflector},
{provide: ReflectorReader, useExisting: Reflector},
{provide: ResourceLoader, useValue: _NO_RESOURCE_LOADER},
SummaryResolver,
Console,
Lexer,
Parser,

View File

@ -6,9 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, Attribute, ChangeDetectionStrategy, Component, Directive, Host, Inject, Injectable, ModuleWithProviders, Optional, Provider, Query, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef} from '@angular/core';
import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, Attribute, ChangeDetectionStrategy, Component, Directive, Host, Inject, Injectable, ModuleWithProviders, OpaqueToken, Optional, Provider, Query, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef} from '@angular/core';
import {isStaticSymbol} from './aot/static_symbol';
import {StaticSymbol} from './aot/static_symbol';
import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
import * as cpl from './compile_metadata';
import {DirectiveNormalizer} from './directive_normalizer';
@ -21,10 +21,12 @@ import {NgModuleResolver} from './ng_module_resolver';
import {PipeResolver} from './pipe_resolver';
import {ComponentStillLoadingError, LIFECYCLE_HOOKS_VALUES, ReflectorReader, reflector} from './private_import_core';
import {ElementSchemaRegistry} from './schema/element_schema_registry';
import {SummaryResolver} from './summary_resolver';
import {getUrlScheme} from './url_resolver';
import {MODULE_SUFFIX, SyncAsyncResult, ValueTransformer, visitValue} from './util';
export type ErrorCollector = (error: any, type?: any) => void;
export const ERROR_COLLECTOR_TOKEN = new OpaqueToken('ErrorCollector');
// Design notes:
// - don't lazily create metadata:
@ -36,24 +38,24 @@ import {MODULE_SUFFIX, SyncAsyncResult, ValueTransformer, visitValue} from './ut
@Injectable()
export class CompileMetadataResolver {
private _directiveCache = new Map<Type<any>, cpl.CompileDirectiveMetadata>();
private _directiveSummaryCache = new Map<Type<any>, cpl.CompileDirectiveSummary>();
private _summaryCache = new Map<Type<any>, cpl.CompileTypeSummary>();
private _pipeCache = new Map<Type<any>, cpl.CompilePipeMetadata>();
private _pipeSummaryCache = new Map<Type<any>, cpl.CompilePipeSummary>();
private _ngModuleCache = new Map<Type<any>, cpl.CompileNgModuleMetadata>();
private _ngModuleOfTypes = new Map<Type<any>, Type<any>>();
constructor(
private _ngModuleResolver: NgModuleResolver, private _directiveResolver: DirectiveResolver,
private _pipeResolver: PipeResolver, private _schemaRegistry: ElementSchemaRegistry,
private _pipeResolver: PipeResolver, private _summaryResolver: SummaryResolver,
private _schemaRegistry: ElementSchemaRegistry,
private _directiveNormalizer: DirectiveNormalizer,
private _reflector: ReflectorReader = reflector) {}
private _reflector: ReflectorReader = reflector,
@Optional() @Inject(ERROR_COLLECTOR_TOKEN) private _errorCollector?: ErrorCollector) {}
clearCacheFor(type: Type<any>) {
const dirMeta = this._directiveCache.get(type);
this._directiveCache.delete(type);
this._directiveSummaryCache.delete(type);
this._summaryCache.delete(type);
this._pipeCache.delete(type);
this._pipeSummaryCache.delete(type);
this._ngModuleOfTypes.delete(type);
// Clear all of the NgModule as they contain transitive information!
this._ngModuleCache.clear();
@ -64,9 +66,8 @@ export class CompileMetadataResolver {
clearCache() {
this._directiveCache.clear();
this._directiveSummaryCache.clear();
this._summaryCache.clear();
this._pipeCache.clear();
this._pipeSummaryCache.clear();
this._ngModuleCache.clear();
this._ngModuleOfTypes.clear();
this._directiveNormalizer.clearCache();
@ -126,7 +127,16 @@ export class CompileMetadataResolver {
return null;
}
private _loadDirectiveMetadata(directiveType: any, isSync: boolean): () => Promise<any> {
private _loadSummary(type: any, kind: cpl.CompileSummaryKind): cpl.CompileTypeSummary {
let summary = this._summaryCache.get(type);
if (!summary) {
summary = this._summaryResolver.resolveSummary(type);
this._summaryCache.set(type, summary);
}
return summary && summary.summaryKind === kind ? summary : null;
}
private _loadDirectiveMetadata(directiveType: any, isSync: boolean): Promise<any> {
if (this._directiveCache.has(directiveType)) {
return;
}
@ -153,7 +163,7 @@ export class CompileMetadataResolver {
template: templateMetadata
});
this._directiveCache.set(directiveType, normalizedDirMeta);
this._directiveSummaryCache.set(directiveType, normalizedDirMeta.toSummary());
this._summaryCache.set(directiveType, normalizedDirMeta.toSummary());
return normalizedDirMeta;
};
@ -174,9 +184,10 @@ export class CompileMetadataResolver {
return null;
} else {
if (isSync) {
throw new ComponentStillLoadingError(directiveType);
this._reportError(new ComponentStillLoadingError(directiveType), directiveType);
return null;
}
return () => templateMeta.asyncResult.then(createDirectiveMetadata);
return templateMeta.asyncResult.then(createDirectiveMetadata);
}
} else {
// directive
@ -226,7 +237,7 @@ export class CompileMetadataResolver {
if (dirMeta.viewProviders) {
viewProviders = this._getProvidersMetadata(
dirMeta.viewProviders, entryComponentMetadata,
`viewProviders for "${stringify(directiveType)}"`);
`viewProviders for "${stringify(directiveType)}"`, [], directiveType);
}
if (dirMeta.entryComponents) {
entryComponentMetadata = flattenAndDedupeArray(dirMeta.entryComponents)
@ -239,14 +250,18 @@ export class CompileMetadataResolver {
} else {
// Directive
if (!selector) {
throw new Error(`Directive ${stringify(directiveType)} has no selector, please add it!`);
this._reportError(
new Error(`Directive ${stringify(directiveType)} has no selector, please add it!`),
directiveType);
selector = 'error';
}
}
let providers: cpl.CompileProviderMetadata[] = [];
if (isPresent(dirMeta.providers)) {
providers = this._getProvidersMetadata(
dirMeta.providers, entryComponentMetadata, `providers for "${stringify(directiveType)}"`);
dirMeta.providers, entryComponentMetadata, `providers for "${stringify(directiveType)}"`,
[], directiveType);
}
let queries: cpl.CompileQueryMetadata[] = [];
let viewQueries: cpl.CompileQueryMetadata[] = [];
@ -281,17 +296,22 @@ export class CompileMetadataResolver {
getDirectiveMetadata(directiveType: any): cpl.CompileDirectiveMetadata {
const dirMeta = this._directiveCache.get(directiveType);
if (!dirMeta) {
throw new Error(
`Illegal state: getDirectiveMetadata can only be called after loadNgModuleMetadata for a module that declares it. Directive ${stringify(directiveType)}.`);
this._reportError(
new Error(
`Illegal state: getDirectiveMetadata can only be called after loadNgModuleMetadata for a module that declares it. Directive ${stringify(directiveType)}.`),
directiveType);
}
return dirMeta;
}
getDirectiveSummary(dirType: any): cpl.CompileDirectiveSummary {
const dirSummary = this._directiveSummaryCache.get(dirType);
const dirSummary =
<cpl.CompileDirectiveSummary>this._loadSummary(dirType, cpl.CompileSummaryKind.Directive);
if (!dirSummary) {
throw new Error(
`Illegal state: getDirectiveSummary can only be called after loadNgModuleMetadata for a module that imports it. Directive ${stringify(dirType)}.`);
this._reportError(
new Error(
`Illegal state: Could not load the summary for directive ${stringify(dirType)}.`),
dirType);
}
return dirSummary;
}
@ -300,51 +320,39 @@ export class CompileMetadataResolver {
isPipe(type: any) { return this._pipeResolver.isPipe(type); }
/**
* Gets the metadata for the given module.
* This assumes `loadNgModuleMetadata` has been called first.
*/
getNgModuleMetadata(moduleType: any): cpl.CompileNgModuleMetadata {
const modMeta = this._ngModuleCache.get(moduleType);
if (!modMeta) {
throw new Error(
`Illegal state: getNgModuleMetadata can only be called after loadNgModuleMetadata. Module ${stringify(moduleType)}.`);
getNgModuleSummary(moduleType: any): cpl.CompileNgModuleSummary {
let moduleSummary =
<cpl.CompileNgModuleSummary>this._loadSummary(moduleType, cpl.CompileSummaryKind.NgModule);
if (!moduleSummary) {
const moduleMeta = this.getNgModuleMetadata(moduleType, false);
moduleSummary = moduleMeta ? moduleMeta.toSummary() : null;
if (moduleSummary) {
this._summaryCache.set(moduleType, moduleSummary);
}
}
return modMeta;
}
private _loadNgModuleSummary(moduleType: any, isSync: boolean): cpl.CompileNgModuleSummary {
// TODO(tbosch): add logic to read summary files!
// - needs to add directive / pipe summaries to this._directiveSummaryCache /
// this._pipeSummaryCache as well!
const moduleMeta = this._loadNgModuleMetadata(moduleType, isSync, false);
return moduleMeta ? moduleMeta.toSummary() : null;
return moduleSummary;
}
/**
* Loads an NgModule and all of its directives. This includes loading the exported directives of
* imported modules,
* but not private directives of imported modules.
* Loads the declared directives and pipes of an NgModule.
*/
loadNgModuleMetadata(moduleType: any, isSync: boolean, throwIfNotFound = true):
{ngModule: cpl.CompileNgModuleMetadata, loading: Promise<any>} {
const ngModule = this._loadNgModuleMetadata(moduleType, isSync, throwIfNotFound);
const loading = ngModule ?
Promise.all(ngModule.transitiveModule.directiveLoaders.map(loader => loader())) :
Promise.resolve(null);
return {ngModule, loading};
loadNgModuleDirectiveAndPipeMetadata(moduleType: any, isSync: boolean, throwIfNotFound = true):
Promise<any> {
const ngModule = this.getNgModuleMetadata(moduleType, throwIfNotFound);
const loading: Promise<any>[] = [];
if (ngModule) {
ngModule.declaredDirectives.forEach((id) => {
const promise = this._loadDirectiveMetadata(id.reference, isSync);
if (promise) {
loading.push(promise);
}
});
ngModule.declaredPipes.forEach((id) => this._loadPipeMetadata(id.reference));
}
return Promise.all(loading);
}
/**
* Get the NgModule metadata without loading the directives.
*/
getUnloadedNgModuleMetadata(moduleType: any, isSync: boolean, throwIfNotFound = true):
cpl.CompileNgModuleMetadata {
return this._loadNgModuleMetadata(moduleType, isSync, throwIfNotFound);
}
private _loadNgModuleMetadata(moduleType: any, isSync: boolean, throwIfNotFound = true):
cpl.CompileNgModuleMetadata {
getNgModuleMetadata(moduleType: any, throwIfNotFound = true): cpl.CompileNgModuleMetadata {
moduleType = resolveForwardRef(moduleType);
let compileMeta = this._ngModuleCache.get(moduleType);
if (compileMeta) {
@ -359,7 +367,7 @@ export class CompileMetadataResolver {
const declaredPipes: cpl.CompileIdentifierMetadata[] = [];
const importedModules: cpl.CompileNgModuleSummary[] = [];
const exportedModules: cpl.CompileNgModuleSummary[] = [];
const providers: any[] = [];
const providers: cpl.CompileProviderMetadata[] = [];
const entryComponents: cpl.CompileIdentifierMetadata[] = [];
const bootstrapComponents: cpl.CompileIdentifierMetadata[] = [];
const schemas: SchemaMetadata[] = [];
@ -375,20 +383,26 @@ export class CompileMetadataResolver {
if (moduleWithProviders.providers) {
providers.push(...this._getProvidersMetadata(
moduleWithProviders.providers, entryComponents,
`provider for the NgModule '${stringify(importedModuleType)}'`));
`provider for the NgModule '${stringify(importedModuleType)}'`, [], importedType));
}
}
if (importedModuleType) {
const importedModuleSummary = this._loadNgModuleSummary(importedModuleType, isSync);
const importedModuleSummary = this.getNgModuleSummary(importedModuleType);
if (!importedModuleSummary) {
throw new Error(
`Unexpected ${this._getTypeDescriptor(importedType)} '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`);
this._reportError(
new Error(
`Unexpected ${this._getTypeDescriptor(importedType)} '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`),
moduleType);
return;
}
importedModules.push(importedModuleSummary);
} else {
throw new Error(
`Unexpected value '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`);
this._reportError(
new Error(
`Unexpected value '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`),
moduleType);
return;
}
});
}
@ -396,10 +410,13 @@ export class CompileMetadataResolver {
if (meta.exports) {
flattenAndDedupeArray(meta.exports).forEach((exportedType) => {
if (!isValidType(exportedType)) {
throw new Error(
`Unexpected value '${stringify(exportedType)}' exported by the module '${stringify(moduleType)}'`);
this._reportError(
new Error(
`Unexpected value '${stringify(exportedType)}' exported by the module '${stringify(moduleType)}'`),
moduleType);
return;
}
const exportedModuleSummary = this._loadNgModuleSummary(exportedType, isSync);
const exportedModuleSummary = this.getNgModuleSummary(exportedType);
if (exportedModuleSummary) {
exportedModules.push(exportedModuleSummary);
} else {
@ -414,28 +431,28 @@ export class CompileMetadataResolver {
if (meta.declarations) {
flattenAndDedupeArray(meta.declarations).forEach((declaredType) => {
if (!isValidType(declaredType)) {
throw new Error(
`Unexpected value '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`);
this._reportError(
new Error(
`Unexpected value '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`),
moduleType);
return;
}
const declaredIdentifier = this._getIdentifierMetadata(declaredType);
if (this._directiveResolver.isDirective(declaredType)) {
transitiveModule.directivesSet.add(declaredType);
transitiveModule.directives.push(declaredIdentifier);
transitiveModule.addDirective(declaredIdentifier);
declaredDirectives.push(declaredIdentifier);
this._addTypeToModule(declaredType, moduleType);
const loader = this._loadDirectiveMetadata(declaredType, isSync);
if (loader) {
transitiveModule.directiveLoaders.push(loader);
}
} else if (this._pipeResolver.isPipe(declaredType)) {
transitiveModule.pipesSet.add(declaredType);
transitiveModule.addPipe(declaredIdentifier);
transitiveModule.pipes.push(declaredIdentifier);
declaredPipes.push(declaredIdentifier);
this._addTypeToModule(declaredType, moduleType);
this._loadPipeMetadata(declaredType);
} else {
throw new Error(
`Unexpected ${this._getTypeDescriptor(declaredType)} '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`);
this._reportError(
new Error(
`Unexpected ${this._getTypeDescriptor(declaredType)} '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`),
moduleType);
return;
}
});
}
@ -445,11 +462,15 @@ export class CompileMetadataResolver {
exportedNonModuleIdentifiers.forEach((exportedId) => {
if (transitiveModule.directivesSet.has(exportedId.reference)) {
exportedDirectives.push(exportedId);
transitiveModule.addExportedDirective(exportedId);
} else if (transitiveModule.pipesSet.has(exportedId.reference)) {
exportedPipes.push(exportedId);
transitiveModule.addExportedPipe(exportedId);
} else {
throw new Error(
`Can't export ${this._getTypeDescriptor(exportedId.reference)} ${stringify(exportedId.reference)} from ${stringify(moduleType)} as it was neither declared nor imported!`);
this._reportError(
new Error(
`Can't export ${this._getTypeDescriptor(exportedId.reference)} ${stringify(exportedId.reference)} from ${stringify(moduleType)} as it was neither declared nor imported!`),
moduleType);
}
});
@ -457,7 +478,8 @@ export class CompileMetadataResolver {
// so that they overwrite any other provider we already added.
if (meta.providers) {
providers.push(...this._getProvidersMetadata(
meta.providers, entryComponents, `provider for the NgModule '${stringify(moduleType)}'`));
meta.providers, entryComponents, `provider for the NgModule '${stringify(moduleType)}'`,
[], moduleType));
}
if (meta.entryComponents) {
@ -466,14 +488,16 @@ export class CompileMetadataResolver {
}
if (meta.bootstrap) {
const typeMetadata = flattenAndDedupeArray(meta.bootstrap).map(type => {
flattenAndDedupeArray(meta.bootstrap).forEach(type => {
if (!isValidType(type)) {
throw new Error(
`Unexpected value '${stringify(type)}' used in the bootstrap property of module '${stringify(moduleType)}'`);
this._reportError(
new Error(
`Unexpected value '${stringify(type)}' used in the bootstrap property of module '${stringify(moduleType)}'`),
moduleType);
return;
}
return this._getTypeMetadata(type);
bootstrapComponents.push(this._getTypeMetadata(type));
});
bootstrapComponents.push(...typeMetadata);
}
entryComponents.push(...bootstrapComponents);
@ -482,9 +506,6 @@ export class CompileMetadataResolver {
schemas.push(...flattenAndDedupeArray(meta.schemas));
}
transitiveModule.entryComponents.push(...entryComponents);
transitiveModule.providers.push(...providers);
compileMeta = new cpl.CompileNgModuleMetadata({
type: this._getTypeMetadata(moduleType),
providers,
@ -501,7 +522,9 @@ export class CompileMetadataResolver {
id: meta.id,
});
transitiveModule.modules.push(compileMeta.toInjectorSummary());
entryComponents.forEach((id) => transitiveModule.addEntryComponent(id));
providers.forEach((provider) => transitiveModule.addProvider(provider, compileMeta.type));
transitiveModule.addModule(compileMeta.type);
this._ngModuleCache.set(moduleType, compileMeta);
return compileMeta;
}
@ -530,10 +553,12 @@ export class CompileMetadataResolver {
private _addTypeToModule(type: Type<any>, moduleType: Type<any>) {
const oldModule = this._ngModuleOfTypes.get(type);
if (oldModule && oldModule !== moduleType) {
throw new Error(
`Type ${stringify(type)} is part of the declarations of 2 modules: ${stringify(oldModule)} and ${stringify(moduleType)}! ` +
`Please consider moving ${stringify(type)} to a higher module that imports ${stringify(oldModule)} and ${stringify(moduleType)}. ` +
`You can also create a new NgModule that exports and includes ${stringify(type)} then import that NgModule in ${stringify(oldModule)} and ${stringify(moduleType)}.`);
this._reportError(
new Error(
`Type ${stringify(type)} is part of the declarations of 2 modules: ${stringify(oldModule)} and ${stringify(moduleType)}! ` +
`Please consider moving ${stringify(type)} to a higher module that imports ${stringify(oldModule)} and ${stringify(moduleType)}. ` +
`You can also create a new NgModule that exports and includes ${stringify(type)} then import that NgModule in ${stringify(oldModule)} and ${stringify(moduleType)}.`),
moduleType);
}
this._ngModuleOfTypes.set(type, moduleType);
}
@ -542,19 +567,38 @@ export class CompileMetadataResolver {
importedModules: cpl.CompileNgModuleSummary[],
exportedModules: cpl.CompileNgModuleSummary[]): cpl.TransitiveCompileNgModuleMetadata {
// collect `providers` / `entryComponents` from all imported and all exported modules
const transitiveModules = getTransitiveImportedModules(importedModules.concat(exportedModules));
const providers = flattenArray(transitiveModules.map((ngModule) => ngModule.providers));
const entryComponents =
flattenArray(transitiveModules.map((ngModule) => ngModule.entryComponents));
const transitiveExportedModules = getTransitiveExportedModules(importedModules);
const directives =
flattenArray(transitiveExportedModules.map((ngModule) => ngModule.exportedDirectives));
const pipes = flattenArray(transitiveExportedModules.map((ngModule) => ngModule.exportedPipes));
const directiveLoaders =
ListWrapper.flatten(transitiveExportedModules.map(ngModule => ngModule.directiveLoaders));
return new cpl.TransitiveCompileNgModuleMetadata(
transitiveModules, providers, entryComponents, directives, pipes, directiveLoaders);
const result = new cpl.TransitiveCompileNgModuleMetadata();
const modulesByToken = new Map<any, Set<any>>();
importedModules.concat(exportedModules).forEach((modSummary) => {
modSummary.modules.forEach((mod) => result.addModule(mod));
modSummary.entryComponents.forEach((comp) => result.addEntryComponent(comp));
const addedTokens = new Set<any>();
modSummary.providers.forEach((entry) => {
const tokenRef = cpl.tokenReference(entry.provider.token);
let prevModules = modulesByToken.get(tokenRef);
if (!prevModules) {
prevModules = new Set<any>();
modulesByToken.set(tokenRef, prevModules);
}
const moduleRef = entry.module.reference;
// Note: the providers of one module may still contain multiple providers
// per token (e.g. for multi providers), and we need to preserve these.
if (addedTokens.has(tokenRef) || !prevModules.has(moduleRef)) {
prevModules.add(moduleRef);
addedTokens.add(tokenRef);
result.addProvider(entry.provider, entry.module);
}
});
});
exportedModules.forEach((modSummary) => {
modSummary.exportedDirectives.forEach((id) => result.addExportedDirective(id));
modSummary.exportedPipes.forEach((id) => result.addExportedPipe(id));
});
importedModules.forEach((modSummary) => {
modSummary.exportedDirectives.forEach((id) => result.addDirective(id));
modSummary.exportedPipes.forEach((id) => result.addPipe(id));
});
return result;
}
private _getIdentifierMetadata(type: Type<any>): cpl.CompileIdentifierMetadata {
@ -585,17 +629,21 @@ export class CompileMetadataResolver {
getPipeMetadata(pipeType: any): cpl.CompilePipeMetadata {
const pipeMeta = this._pipeCache.get(pipeType);
if (!pipeMeta) {
throw new Error(
`Illegal state: getPipeMetadata can only be called after loadNgModuleMetadata for a module that declares it. Pipe ${stringify(pipeType)}.`);
this._reportError(
new Error(
`Illegal state: getPipeMetadata can only be called after loadNgModuleMetadata for a module that declares it. Pipe ${stringify(pipeType)}.`),
pipeType);
}
return pipeMeta;
}
getPipeSummary(pipeType: any): cpl.CompilePipeSummary {
const pipeSummary = this._pipeSummaryCache.get(pipeType);
const pipeSummary =
<cpl.CompilePipeSummary>this._loadSummary(pipeType, cpl.CompileSummaryKind.Pipe);
if (!pipeSummary) {
throw new Error(
`Illegal state: getPipeSummary can only be called after loadNgModuleMetadata for a module that imports it. Pipe ${stringify(pipeType)}.`);
this._reportError(
new Error(`Illegal state: Could not load the summary for pipe ${stringify(pipeType)}.`),
pipeType);
}
return pipeSummary;
}
@ -618,7 +666,7 @@ export class CompileMetadataResolver {
pure: pipeAnnotation.pure
});
this._pipeCache.set(pipeType, pipeMeta);
this._pipeSummaryCache.set(pipeType, pipeMeta.toSummary());
this._summaryCache.set(pipeType, pipeMeta.toSummary());
return pipeMeta;
}
@ -675,8 +723,9 @@ export class CompileMetadataResolver {
if (hasUnknownDeps) {
const depsTokens =
dependenciesMetadata.map((dep) => dep ? stringify(dep.token) : '?').join(', ');
throw new Error(
`Can't resolve all parameters for ${stringify(typeOrFunc)}: (${depsTokens}).`);
this._reportError(
new Error(`Can't resolve all parameters for ${stringify(typeOrFunc)}: (${depsTokens}).`),
typeOrFunc);
}
return dependenciesMetadata;
@ -695,8 +744,8 @@ export class CompileMetadataResolver {
private _getProvidersMetadata(
providers: Provider[], targetEntryComponents: cpl.CompileIdentifierMetadata[],
debugInfo?: string,
compileProviders: cpl.CompileProviderMetadata[] = []): cpl.CompileProviderMetadata[] {
debugInfo?: string, compileProviders: cpl.CompileProviderMetadata[] = [],
type?: any): cpl.CompileProviderMetadata[] {
providers.forEach((provider: any, providerIdx: number) => {
if (Array.isArray(provider)) {
this._getProvidersMetadata(provider, targetEntryComponents, debugInfo, compileProviders);
@ -722,11 +771,13 @@ export class CompileMetadataResolver {
},
[]))
.join(', ');
throw new Error(
`Invalid ${debugInfo ? debugInfo : 'provider'} - only instances of Provider and Type are allowed, got: [${providersInfo}]`);
this._reportError(
new Error(
`Invalid ${debugInfo ? debugInfo : 'provider'} - only instances of Provider and Type are allowed, got: [${providersInfo}]`),
type);
}
if (providerMeta.token === resolveIdentifier(Identifiers.ANALYZE_FOR_ENTRY_COMPONENTS)) {
targetEntryComponents.push(...this._getEntryComponentsFromProvider(providerMeta));
targetEntryComponents.push(...this._getEntryComponentsFromProvider(providerMeta, type));
} else {
compileProviders.push(this.getProviderMetadata(providerMeta));
}
@ -735,17 +786,21 @@ export class CompileMetadataResolver {
return compileProviders;
}
private _getEntryComponentsFromProvider(provider: cpl.ProviderMeta):
private _getEntryComponentsFromProvider(provider: cpl.ProviderMeta, type?: any):
cpl.CompileIdentifierMetadata[] {
const components: cpl.CompileIdentifierMetadata[] = [];
const collectedIdentifiers: cpl.CompileIdentifierMetadata[] = [];
if (provider.useFactory || provider.useExisting || provider.useClass) {
throw new Error(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports useValue!`);
this._reportError(
new Error(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports useValue!`), type);
return [];
}
if (!provider.multi) {
throw new Error(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports 'multi = true'!`);
this._reportError(
new Error(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports 'multi = true'!`), type);
return [];
}
extractIdentifiers(provider.useValue, collectedIdentifiers);
@ -811,8 +866,10 @@ export class CompileMetadataResolver {
this._queryVarBindings(q.selector).map(varName => this._getTokenMetadata(varName));
} else {
if (!q.selector) {
throw new Error(
`Can't construct a query for the property "${propertyName}" of "${stringify(typeOrFunc)}" since the query selector wasn't defined.`);
this._reportError(
new Error(
`Can't construct a query for the property "${propertyName}" of "${stringify(typeOrFunc)}" since the query selector wasn't defined.`),
typeOrFunc);
}
selectors = [this._getTokenMetadata(q.selector)];
}
@ -824,39 +881,17 @@ export class CompileMetadataResolver {
read: q.read ? this._getTokenMetadata(q.read) : null
};
}
}
function getTransitiveExportedModules(
modules: cpl.CompileNgModuleDirectiveSummary[],
targetModules: cpl.CompileNgModuleDirectiveSummary[] = [],
visitedModules = new Set<Type<any>>()): cpl.CompileNgModuleDirectiveSummary[] {
modules.forEach((ngModule) => {
if (!visitedModules.has(ngModule.type.reference)) {
visitedModules.add(ngModule.type.reference);
getTransitiveExportedModules(ngModule.exportedModules, targetModules, visitedModules);
// Add after recursing so imported/exported modules are before the module itself.
// This is important for overwriting providers of imported modules!
targetModules.push(ngModule);
private _reportError(error: any, type?: any, otherType?: any) {
if (this._errorCollector) {
this._errorCollector(error, type);
if (otherType) {
this._errorCollector(error, otherType);
}
} else {
throw error;
}
});
return targetModules;
}
function getTransitiveImportedModules(
modules: cpl.CompileNgModuleInjectorSummary[],
targetModules: cpl.CompileNgModuleInjectorSummary[] = [],
visitedModules = new Set<Type<any>>()): cpl.CompileNgModuleInjectorSummary[] {
modules.forEach((ngModule) => {
if (!visitedModules.has(ngModule.type.reference)) {
visitedModules.add(ngModule.type.reference);
const nestedModules = ngModule.importedModules.concat(ngModule.exportedModules);
getTransitiveImportedModules(nestedModules, targetModules, visitedModules);
// Add after recursing so imported/exported modules are before the module itself.
// This is important for overwriting providers of imported modules!
targetModules.push(ngModule);
}
});
return targetModules;
}
}
function flattenArray(tree: any[], out: Array<any> = []): Array<any> {
@ -885,12 +920,12 @@ function flattenAndDedupeArray(tree: any[]): Array<any> {
}
function isValidType(value: any): boolean {
return isStaticSymbol(value) || (value instanceof Type);
return (value instanceof StaticSymbol) || (value instanceof Type);
}
export function componentModuleUrl(
reflector: ReflectorReader, type: Type<any>, cmpMetadata: Component): string {
if (isStaticSymbol(type)) {
if (type instanceof StaticSymbol) {
return type.filePath;
}

View File

@ -48,7 +48,8 @@ export class NgModuleCompiler {
const entryComponentFactories =
ngModuleMeta.transitiveModule.entryComponents.map((entryComponent) => {
const id: CompileIdentifierMetadata = {reference: null};
if (ngModuleMeta.bootstrapComponents.indexOf(entryComponent) > -1) {
if (ngModuleMeta.bootstrapComponents.some(
(id) => id.reference === entryComponent.reference)) {
bootstrapComponentFactories.push(id);
}
deps.push(new ComponentFactoryDependency(entryComponent, id));

View File

@ -273,16 +273,15 @@ export class NgModuleProviderAnalyzer {
ngModule: CompileNgModuleMetadata, extraProviders: CompileProviderMetadata[],
sourceSpan: ParseSourceSpan) {
this._allProviders = new Map<any, ProviderAst>();
const ngModuleTypes = ngModule.transitiveModule.modules.map((moduleMeta) => moduleMeta.type);
ngModuleTypes.forEach((ngModuleType: CompileTypeMetadata) => {
ngModule.transitiveModule.modules.forEach((ngModuleType: CompileTypeMetadata) => {
const ngModuleProvider = {token: {identifier: ngModuleType}, useClass: ngModuleType};
_resolveProviders(
[ngModuleProvider], ProviderAstType.PublicService, true, sourceSpan, this._errors,
this._allProviders);
});
_resolveProviders(
ngModule.transitiveModule.providers.concat(extraProviders), ProviderAstType.PublicService,
false, sourceSpan, this._errors, this._allProviders);
ngModule.transitiveModule.providers.map(entry => entry.provider).concat(extraProviders),
ProviderAstType.PublicService, false, sourceSpan, this._errors, this._allProviders);
}
parse(): ProviderAst[] {

View File

@ -0,0 +1,14 @@
/**
* @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 {Injectable} from '@angular/core';
import {CompileTypeSummary} from './compile_metadata';
@Injectable()
export class SummaryResolver {
resolveSummary(reference: any): CompileTypeSummary { return null; }
}

View File

@ -0,0 +1,19 @@
/**
* @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
*/
/**
* @module
* @description
* Entry point for all public APIs of the common package.
*/
import {Version} from '@angular/core';
/**
* @stable
*/
export const VERSION = new Version('0.0.0-PLACEHOLDER');

View File

@ -694,7 +694,7 @@ describe('StaticReflector', () => {
});
class MockStaticReflectorHost implements StaticReflectorHost {
export class MockStaticReflectorHost implements StaticReflectorHost {
private collector = new MetadataCollector();
constructor(private data: {[key: string]: any}) {}

View File

@ -0,0 +1,138 @@
/**
* @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 {AotSummaryResolver, AotSummaryResolverHost, CompileNgModuleSummary, CompileSummaryKind, CompileTypeSummary, StaticReflector, StaticReflectorHost, StaticSymbol} from '@angular/compiler';
import * as path from 'path';
import {MockStaticReflectorHost} from './static_reflector_spec';
const EXT = /\.ts$|.d.ts$/;
export function main() {
describe('AotSummaryResolver', () => {
let resolver: AotSummaryResolver;
let staticReflector: StaticReflector;
function init(summaries: {[filePath: string]: string} = {}) {
// Note: We don't give the static reflector metadata files,
// so that we can test that we can deserialize summary files
// without reading metadata files. This is important
// as summary files can contain references to files of transitive compilation
// dependencies, and we don't want to read their metadata files.
staticReflector = new StaticReflector(new MockStaticReflectorHost({}));
const host = new MockAotSummaryResolverHost(summaries);
resolver = new AotSummaryResolver(host, staticReflector, {excludeFilePattern: /\.d\.ts$/});
}
it('should add .ngsummary.json to the filename', () => {
init();
expect(resolver.serializeSummaries('a.ts', []).genFileUrl).toBe('a.ngsummary.json');
expect(resolver.serializeSummaries('a.d.ts', []).genFileUrl).toBe('a.ngsummary.json');
expect(resolver.serializeSummaries('a.js', []).genFileUrl).toBe('a.ngsummary.json');
});
it('should serialize various data correctly', () => {
init();
const serializedData = resolver.serializeSummaries(
'/tmp/some_pipe.ts', [<any>{
summaryKind: CompileSummaryKind.Pipe,
type: {
reference: staticReflector.getStaticSymbol('/tmp/some_pipe.ts', 'SomePipe'),
},
aNumber: 1,
aString: 'hello',
anArray: [1, 2],
aStaticSymbol:
staticReflector.getStaticSymbol('/tmp/some_symbol.ts', 'someName', ['someMember'])
}]);
// Note: this creates a new staticReflector!
init({[serializedData.genFileUrl]: serializedData.source});
const deserialized = resolver.resolveSummary(
staticReflector.getStaticSymbol('/tmp/some_pipe.d.ts', 'SomePipe'));
expect(deserialized.aNumber).toBe(1);
expect(deserialized.aString).toBe('hello');
expect(deserialized.anArray).toEqual([1, 2]);
expect(deserialized.aStaticSymbol instanceof StaticSymbol).toBe(true);
// Note: change from .ts to .d.ts is expected
expect(deserialized.aStaticSymbol)
.toEqual(
staticReflector.getStaticSymbol('/tmp/some_symbol.d.ts', 'someName', ['someMember']));
});
it('should store reexports in the same file', () => {
init();
const reexportedData = resolver.serializeSummaries(
'/tmp/some_pipe.ts', [{
summaryKind: CompileSummaryKind.Pipe,
type: {
reference: staticReflector.getStaticSymbol('/tmp/some_pipe.ts', 'SomeReexportedPipe'),
diDeps: [],
lifecycleHooks: []
},
}]);
init({[reexportedData.genFileUrl]: reexportedData.source});
const serializedData = resolver.serializeSummaries('/tmp/some_module.ts', [
<CompileNgModuleSummary>{
summaryKind: CompileSummaryKind.NgModule,
type: {
reference: staticReflector.getStaticSymbol('/tmp/some_module.ts', 'SomeModule'),
diDeps: [],
lifecycleHooks: []
},
exportedPipes: [{
reference: staticReflector.getStaticSymbol('/tmp/some_pipe.d.ts', 'SomeReexportedPipe')
}],
exportedDirectives: [],
providers: [],
entryComponents: [],
modules: []
}
]);
init({[serializedData.genFileUrl]: serializedData.source});
resolver.resolveSummary(
staticReflector.getStaticSymbol('/tmp/some_module.d.ts', 'SomeModule'));
expect(resolver.resolveSummary(
staticReflector.getStaticSymbol('/tmp/some_pipe.d.ts', 'SomeReexportedPipe')))
.toEqual({
summaryKind: CompileSummaryKind.Pipe,
type: {
reference:
staticReflector.getStaticSymbol('/tmp/some_pipe.d.ts', 'SomeReexportedPipe'),
diDeps: [],
lifecycleHooks: []
},
});
});
});
}
class MockAotSummaryResolverHost implements AotSummaryResolverHost {
constructor(private summaries: {[fileName: string]: string}) {}
loadSummary(filePath: string): string {
const result = this.summaries[filePath];
if (!result) {
throw new Error(`Could not find summary for ${filePath}`);
}
return result;
}
fileNameToModuleName(fileName: string): string {
return './' + path.basename(fileName).replace(EXT, '');
}
getOutputFileName(sourceFileName: string): string {
return sourceFileName.replace(EXT, '') + '.d.ts';
}
}

View File

@ -23,7 +23,7 @@ export function main() {
describe('CompileMetadataResolver', () => {
beforeEach(() => { TestBed.configureCompiler({providers: TEST_COMPILER_PROVIDERS}); });
it('should throw on the get... methods if the module has not been loaded yet',
it('should throw on the getDirectiveMetadata/getPipeMetadata methods if the module has not been loaded yet',
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
@NgModule({})
class SomeModule {
@ -33,7 +33,6 @@ export function main() {
class SomePipe {
}
expect(() => resolver.getNgModuleMetadata(SomeModule)).toThrowError(/Illegal state/);
expect(() => resolver.getDirectiveMetadata(ComponentWithEverythingInline))
.toThrowError(/Illegal state/);
expect(() => resolver.getPipeMetadata(SomePipe)).toThrowError(/Illegal state/);
@ -44,7 +43,7 @@ export function main() {
@NgModule({declarations: [ComponentWithEverythingInline]})
class SomeModule {
}
resolver.loadNgModuleMetadata(SomeModule, true);
resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true);
const meta = resolver.getDirectiveMetadata(ComponentWithEverythingInline);
expect(meta.selector).toEqual('someSelector');
@ -71,7 +70,7 @@ export function main() {
class SomeModule {
}
expect(() => resolver.loadNgModuleMetadata(SomeModule, true))
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError(
`Can't compile synchronously as ${stringify(ComponentWithExternalResources)} is still being loaded!`);
}));
@ -85,7 +84,7 @@ export function main() {
}
resourceLoader.when('someTemplateUrl', 'someTemplate');
resolver.loadNgModuleMetadata(SomeModule, false).loading.then(() => {
resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, false).then(() => {
const meta = resolver.getDirectiveMetadata(ComponentWithExternalResources);
expect(meta.selector).toEqual('someSelector');
expect(meta.template.styleUrls).toEqual(['someStyleUrl']);
@ -95,26 +94,6 @@ export function main() {
resourceLoader.flush();
})));
it('should wait for external resources of imported modules',
async(inject(
[CompileMetadataResolver, ResourceLoader],
(resolver: CompileMetadataResolver, resourceLoader: MockResourceLoader) => {
@NgModule({declarations: [ComponentWithExternalResources]})
class SomeImportedModule {
}
@NgModule({imports: [SomeImportedModule]})
class SomeModule {
}
resourceLoader.when('someTemplateUrl', 'someTemplate');
resolver.loadNgModuleMetadata(SomeModule, false).loading.then(() => {
const meta = resolver.getDirectiveMetadata(ComponentWithExternalResources);
expect(meta.selector).toEqual('someSelector');
});
resourceLoader.flush();
})));
it('should use `./` as base url for templates during runtime compilation if no moduleId is given',
async(inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
@Component({selector: 'someComponent', templateUrl: 'someUrl'})
@ -126,7 +105,7 @@ export function main() {
class SomeModule {
}
resolver.loadNgModuleMetadata(SomeModule, false).loading.then(() => {
resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, false).then(() => {
const value: string =
resolver.getDirectiveMetadata(ComponentWithoutModuleId).template.templateUrl;
const expectedEndValue = './someUrl';
@ -140,7 +119,7 @@ export function main() {
class SomeModule {
}
expect(() => resolver.loadNgModuleMetadata(SomeModule, true))
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError(
`moduleId should be a string in "ComponentWithInvalidModuleId". See` +
` https://goo.gl/wIDDiL for more information.\n` +
@ -155,7 +134,7 @@ export function main() {
class SomeModule {
}
expect(() => resolver.loadNgModuleMetadata(SomeModule, true))
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError(`Expected 'styles' to be an array of strings.`);
}));
@ -165,7 +144,7 @@ export function main() {
class SomeModule {
}
expect(() => resolver.loadNgModuleMetadata(SomeModule, true))
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError(`Can't resolve all parameters for MyBrokenComp1: (?).`);
}));
it('should throw with descriptive error message when a directive is passed to imports',
@ -173,7 +152,8 @@ export function main() {
@NgModule({imports: [ComponentWithoutModuleId]})
class ModuleWithImportedComponent {
}
expect(() => resolver.loadNgModuleMetadata(ModuleWithImportedComponent, true))
expect(
() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithImportedComponent, true))
.toThrowError(
`Unexpected directive 'ComponentWithoutModuleId' imported by the module 'ModuleWithImportedComponent'`);
}));
@ -186,7 +166,7 @@ export function main() {
@NgModule({imports: [SomePipe]})
class ModuleWithImportedPipe {
}
expect(() => resolver.loadNgModuleMetadata(ModuleWithImportedPipe, true))
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithImportedPipe, true))
.toThrowError(
`Unexpected pipe 'SomePipe' imported by the module 'ModuleWithImportedPipe'`);
}));
@ -199,7 +179,7 @@ export function main() {
@NgModule({declarations: [SomeModule]})
class ModuleWithDeclaredModule {
}
expect(() => resolver.loadNgModuleMetadata(ModuleWithDeclaredModule, true))
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithDeclaredModule, true))
.toThrowError(
`Unexpected module 'SomeModule' declared by the module 'ModuleWithDeclaredModule'`);
}));
@ -209,7 +189,7 @@ export function main() {
@NgModule({declarations: [null]})
class ModuleWithNullDeclared {
}
expect(() => resolver.loadNgModuleMetadata(ModuleWithNullDeclared, true))
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithNullDeclared, true))
.toThrowError(
`Unexpected value 'null' declared by the module 'ModuleWithNullDeclared'`);
}));
@ -219,7 +199,7 @@ export function main() {
@NgModule({imports: [null]})
class ModuleWithNullImported {
}
expect(() => resolver.loadNgModuleMetadata(ModuleWithNullImported, true))
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithNullImported, true))
.toThrowError(
`Unexpected value 'null' imported by the module 'ModuleWithNullImported'`);
}));
@ -231,7 +211,7 @@ export function main() {
class SomeModule {
}
expect(() => resolver.loadNgModuleMetadata(SomeModule, true))
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError(`Can't resolve all parameters for NonAnnotatedService: (?).`);
}));
@ -241,7 +221,7 @@ export function main() {
class SomeModule {
}
expect(() => resolver.loadNgModuleMetadata(SomeModule, true))
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError(
`Invalid providers for "MyBrokenComp3" - only instances of Provider and Type are allowed, got: [SimpleService, ?null?, ...]`);
}));
@ -252,7 +232,7 @@ export function main() {
class SomeModule {
}
expect(() => resolver.loadNgModuleMetadata(SomeModule, true))
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError(
`Invalid viewProviders for "MyBrokenComp4" - only instances of Provider and Type are allowed, got: [?null?, ...]`);
}));
@ -266,10 +246,12 @@ export function main() {
class ModuleWithUndefinedBootstrap {
}
expect(() => resolver.loadNgModuleMetadata(ModuleWithNullBootstrap, true))
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithNullBootstrap, true))
.toThrowError(
`Unexpected value 'null' used in the bootstrap property of module 'ModuleWithNullBootstrap'`);
expect(() => resolver.loadNgModuleMetadata(ModuleWithUndefinedBootstrap, true))
expect(
() =>
resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithUndefinedBootstrap, true))
.toThrowError(
`Unexpected value 'undefined' used in the bootstrap property of module 'ModuleWithUndefinedBootstrap'`);
}));
@ -280,35 +262,35 @@ export function main() {
class Module1 {
}
expect(() => resolver.loadNgModuleMetadata(Module1, true))
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(Module1, true))
.toThrowError(`[' ', ' '] contains unusable interpolation symbol.`);
@NgModule({declarations: [ComponentWithInvalidInterpolation2]})
class Module2 {
}
expect(() => resolver.loadNgModuleMetadata(Module2, true))
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(Module2, true))
.toThrowError(`['{', '}'] contains unusable interpolation symbol.`);
@NgModule({declarations: [ComponentWithInvalidInterpolation3]})
class Module3 {
}
expect(() => resolver.loadNgModuleMetadata(Module3, true))
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(Module3, true))
.toThrowError(`['<%', '%>'] contains unusable interpolation symbol.`);
@NgModule({declarations: [ComponentWithInvalidInterpolation4]})
class Module4 {
}
expect(() => resolver.loadNgModuleMetadata(Module4, true))
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(Module4, true))
.toThrowError(`['&#', '}}'] contains unusable interpolation symbol.`);
@NgModule({declarations: [ComponentWithInvalidInterpolation5]})
class Module5 {
}
expect(() => resolver.loadNgModuleMetadata(Module5, true))
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(Module5, true))
.toThrowError(`['&lbrace;', '}}'] contains unusable interpolation symbol.`);
}));
});
@ -324,7 +306,7 @@ export function main() {
class MyModule {
}
const modMeta = resolver.loadNgModuleMetadata(MyModule, true).ngModule;
const modMeta = resolver.getNgModuleMetadata(MyModule);
expect(modMeta.declaredDirectives.length).toBe(1);
expect(modMeta.declaredDirectives[0].reference).toBe(MyComp);
}));

View File

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

View File

@ -35,4 +35,4 @@ export * from './core_private_export';
export * from './animation/metadata';
export {AnimationTransitionEvent} from './animation/animation_transition_event';
export {AnimationPlayer} from './animation/animation_player';
export {Sanitizer, SecurityContext} from './security';
export {Sanitizer, SecurityContext} from './security';

View File

@ -13,6 +13,7 @@ import {isPresent, looseIdentical} from '../facade/lang';
import {ViewEncapsulation} from '../metadata/view';
import {RenderComponentType, RenderDebugInfo, Renderer, RootRenderer} from '../render/api';
import {Sanitizer} from '../security';
import {VERSION} from '../version';
import {ExpressionChangedAfterItHasBeenCheckedError} from './errors';
import {AppView} from './view';
@ -360,6 +361,7 @@ export function selectOrCreateRenderHostElement(
for (let i = 0; i < attrs.length; i += 2) {
renderer.setElementAttribute(hostElement, attrs.get(i), attrs.get(i + 1));
}
renderer.setElementAttribute(hostElement, 'ng-version', VERSION.full);
} else {
hostElement = createRenderElement(renderer, null, elementName, attrs, debugInfo);
}

View File

@ -1008,7 +1008,7 @@ export interface HostListener {
}
/**
* HostBinding decorator and metadata.
* HostListener decorator and metadata.
*
* @stable
* @Annotation

View File

@ -19,4 +19,9 @@ export class Version {
get minor(): string { return this.full.split('.')[1]; }
get patch(): string { return this.full.split('.').slice(2).join('.'); }
}
}
/**
* @stable
*/
export const VERSION = new Version('0.0.0-PLACEHOLDER');

View File

@ -305,6 +305,12 @@ export function main() {
expect(renderLog.log).toEqual(['someProp=null']);
}));
it('should support short-circuting array index operations', fakeAsync(() => {
const ctx = _bindSimpleValue('value?.phones[0]', PersonHolder);
ctx.detectChanges(false);
expect(renderLog.log).toEqual(['someProp=null']);
}));
it('should still throw if right-side would throw', fakeAsync(() => {
expect(() => {
const ctx = _bindSimpleValue('value?.address.city', PersonHolder);
@ -1515,6 +1521,7 @@ class Person {
age: number;
name: string;
address: Address = null;
phones: number[];
init(name: string, address: Address = null) {
this.name = name;

View File

@ -27,10 +27,10 @@ Declares an output property that fires events that you can subscribe to with an
@cheatsheetItem
syntax(ts):
`@HostBinding('[class.valid]') isValid;`|`@HostBinding('[class.valid]')`
`@HostBinding('class.valid') isValid;`|`@HostBinding('class.valid')`
syntax(js):
`ng.core.HostBinding('[class.valid]',
'isValid', myComponent);`|`ng.core.HostBinding('[class.valid]', 'isValid'`|`);`
`ng.core.HostBinding('class.valid',
'isValid', myComponent);`|`ng.core.HostBinding('class.valid', 'isValid'`|`);`
description:
Binds a host element property (here, the CSS class `valid`) to a directive/component property (`isValid`).

View File

@ -187,7 +187,8 @@ function dateFormatter(format: string, date: Date, locale: string): string {
if (fn) return fn(date, locale);
let parts = DATE_FORMATTER_CACHE.get(format);
const cacheKey = format;
let parts = DATE_FORMATTER_CACHE.get(cacheKey);
if (!parts) {
parts = [];
@ -205,7 +206,7 @@ function dateFormatter(format: string, date: Date, locale: string): string {
}
}
DATE_FORMATTER_CACHE.set(format, parts);
DATE_FORMATTER_CACHE.set(cacheKey, parts);
}
return parts.reduce((text, part) => {

View File

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

View File

@ -42,5 +42,5 @@ export {AsyncValidatorFn, MaxLengthValidator, MinLengthValidator, PatternValidat
export {FormBuilder} from './form_builder';
export {AbstractControl, FormArray, FormControl, FormGroup} from './model';
export {NG_ASYNC_VALIDATORS, NG_VALIDATORS, Validators} from './validators';
export {VERSION} from './version';
export * from './form_providers';

View File

@ -0,0 +1,19 @@
/**
* @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
*/
/**
* @module
* @description
* Entry point for all public APIs of the common package.
*/
import {Version} from '@angular/core';
/**
* @stable
*/
export const VERSION = new Version('0.0.0-PLACEHOLDER');

View File

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

View File

@ -109,9 +109,13 @@ export class XHRConnection implements Connection {
this.setDetectedContentType(req, _xhr);
if (req.headers != null) {
req.headers.forEach((values, name) => _xhr.setRequestHeader(name, values.join(',')));
if (req.headers == null) {
req.headers = new Headers();
}
if (!req.headers.has('Accept')) {
req.headers.append('Accept', 'application/json, text/plain, */*');
}
req.headers.forEach((values, name) => _xhr.setRequestHeader(name, values.join(',')));
// Select the correct buffer type to store the response
if (req.responseType != null && _xhr.responseType != null) {

View File

@ -19,3 +19,4 @@ export {Connection, ConnectionBackend, RequestOptionsArgs, ResponseOptionsArgs,
export {Request} from './static_request';
export {Response} from './static_response';
export {QueryEncoder, URLSearchParams} from './url_search_params';
export {VERSION} from './version';

View File

@ -0,0 +1,19 @@
/**
* @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
*/
/**
* @module
* @description
* Entry point for all public APIs of the common package.
*/
import {Version} from '@angular/core';
/**
* @stable
*/
export const VERSION = new Version('0.0.0-PLACEHOLDER');

View File

@ -237,6 +237,25 @@ export function main() {
expect(setRequestHeaderSpy).toHaveBeenCalledWith('X-Multi', 'a,b');
});
it('should attach default Accept header', () => {
const headers = new Headers();
const base = new BaseRequestOptions();
const connection = new XHRConnection(
new Request(base.merge(new RequestOptions({headers}))), new MockBrowserXHR());
connection.response.subscribe();
expect(setRequestHeaderSpy)
.toHaveBeenCalledWith('Accept', 'application/json, text/plain, */*');
});
it('should not override user provided Accept header', () => {
const headers = new Headers({'Accept': 'text/xml'});
const base = new BaseRequestOptions();
const connection = new XHRConnection(
new Request(base.merge(new RequestOptions({headers}))), new MockBrowserXHR());
connection.response.subscribe();
expect(setRequestHeaderSpy).toHaveBeenCalledWith('Accept', 'text/xml');
});
it('should skip content type detection if custom content type header is set', () => {
const headers = new Headers({'Content-Type': 'text/plain'});
const body = {test: 'val'};

View File

@ -11,17 +11,11 @@
* @description
* Entry point for all public APIs of the language service package.
*/
import {Version} from '@angular/core';
import * as ts from 'typescript';
import {LanguageServicePlugin} from './src/ts_plugin';
/**
* @stable
*/
export const VERSION = new Version('0.0.0-PLACEHOLDER');
export {createLanguageService} from './src/language_service';
export {Completion, Completions, Declaration, Declarations, Definition, Diagnostic, Diagnostics, Hover, HoverTextSection, LanguageService, LanguageServiceHost, Location, Span, TemplateSource, TemplateSources} from './src/types';
export {TypeScriptServiceHost, createLanguageServiceFromTypescript} from './src/typescript_host';
export {VERSION} from './src/version';
export default LanguageServicePlugin;

View File

@ -16,6 +16,7 @@ var esm = 'esm/';
var locations = {
'tsc-wrapped': normalize('../../../dist/tools/@angular') + '/',
'compiler-cli': normalize('../../../dist/esm') + '/'
};
var esm_suffixes = {};

View File

@ -55,12 +55,14 @@ export function getDeclarationDiagnostics(
let directives: Set<StaticSymbol>|undefined = undefined;
for (const declaration of declarations) {
let report = (message: string) => {
results.push(
<Diagnostic>{kind: DiagnosticKind.Error, span: declaration.declarationSpan, message});
const report = (message: string, span?: Span) => {
results.push(<Diagnostic>{
kind: DiagnosticKind.Error,
span: span || declaration.declarationSpan, message
});
};
if (declaration.error) {
report(declaration.error);
for (const error of declaration.errors) {
report(error.message, error.span);
}
if (declaration.metadata) {
if (declaration.metadata.isComponent) {

View File

@ -6,28 +6,16 @@
* found in the LICENSE file at https://angular.io/license
*/
import {StaticReflectorHost, StaticSymbol} from '@angular/compiler';
import {MetadataCollector} from '@angular/tsc-wrapped/src/collector';
import {ModuleMetadata} from '@angular/tsc-wrapped/src/schema';
import * as path from 'path';
import {AngularCompilerOptions, AotCompilerHost, CompilerHost, ModuleResolutionHostAdapter} from '@angular/compiler-cli';
import * as ts from 'typescript';
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
const DTS = /\.d\.ts$/;
let serialNumber = 0;
class ReflectorModuleModuleResolutionHost implements ts.ModuleResolutionHost {
private forceExists: string[] = [];
constructor(private host: ts.LanguageServiceHost) {
if (host.directoryExists)
this.directoryExists = directoryName => this.host.directoryExists(directoryName);
}
fileExists(fileName: string): boolean {
return !!this.host.getScriptSnapshot(fileName) || this.forceExists.indexOf(fileName) >= 0;
}
fileExists(fileName: string): boolean { return !!this.host.getScriptSnapshot(fileName); }
readFile(fileName: string): string {
let snapshot = this.host.getScriptSnapshot(fileName);
@ -37,122 +25,19 @@ class ReflectorModuleModuleResolutionHost implements ts.ModuleResolutionHost {
}
directoryExists: (directoryName: string) => boolean;
forceExist(fileName: string): void { this.forceExists.push(fileName); }
}
export class ReflectorHost implements StaticReflectorHost {
private metadataCollector: MetadataCollector;
private moduleResolverHost: ReflectorModuleModuleResolutionHost;
private _typeChecker: ts.TypeChecker;
private metadataCache = new Map<string, MetadataCacheEntry>();
export class ReflectorHost extends CompilerHost {
constructor(
private getProgram: () => ts.Program, private serviceHost: ts.LanguageServiceHost,
private options: ts.CompilerOptions, private basePath: string) {
this.moduleResolverHost = new ReflectorModuleModuleResolutionHost(serviceHost);
this.metadataCollector = new MetadataCollector();
private getProgram: () => ts.Program, serviceHost: ts.LanguageServiceHost,
options: AngularCompilerOptions) {
super(
null, options,
new ModuleResolutionHostAdapter(new ReflectorModuleModuleResolutionHost(serviceHost)));
}
getCanonicalFileName(fileName: string): string { return fileName; }
private get program() { return this.getProgram(); }
public moduleNameToFileName(moduleName: string, containingFile: string) {
if (!containingFile || !containingFile.length) {
if (moduleName.indexOf('.') === 0) {
throw new Error('Resolution of relative paths requires a containing file.');
}
// Any containing file gives the same result for absolute imports
containingFile = this.getCanonicalFileName(path.join(this.basePath, 'index.ts'));
}
moduleName = moduleName.replace(EXT, '');
const resolved =
ts.resolveModuleName(moduleName, containingFile, this.options, this.moduleResolverHost)
.resolvedModule;
return resolved ? resolved.resolvedFileName : null;
};
/**
* We want a moduleId that will appear in import statements in the generated code.
* These need to be in a form that system.js can load, so absolute file paths don't work.
* Relativize the paths by checking candidate prefixes of the absolute path, to see if
* they are resolvable by the moduleResolution strategy from the CompilerHost.
*/
fileNameToModuleName(importedFile: string, containingFile: string) {
// TODO(tbosch): if a file does not yet exist (because we compile it later),
// we still need to create it so that the `resolve` method works!
if (!this.moduleResolverHost.fileExists(importedFile)) {
this.moduleResolverHost.forceExist(importedFile);
}
const parts = importedFile.replace(EXT, '').split(path.sep).filter(p => !!p);
for (let index = parts.length - 1; index >= 0; index--) {
let candidate = parts.slice(index, parts.length).join(path.sep);
if (this.moduleNameToFileName('.' + path.sep + candidate, containingFile) === importedFile) {
return `./${candidate}`;
}
if (this.moduleNameToFileName(candidate, containingFile) === importedFile) {
return candidate;
}
}
throw new Error(
`Unable to find any resolvable import for ${importedFile} relative to ${containingFile}`);
}
private get typeChecker(): ts.TypeChecker {
let result = this._typeChecker;
if (!result) {
result = this._typeChecker = this.program.getTypeChecker();
}
return result;
}
private typeCache = new Map<string, StaticSymbol>();
// TODO(alexeagle): take a statictype
getMetadataFor(filePath: string): ModuleMetadata[] {
if (!this.moduleResolverHost.fileExists(filePath)) {
throw new Error(`No such file '${filePath}'`);
}
if (DTS.test(filePath)) {
const metadataPath = filePath.replace(DTS, '.metadata.json');
if (this.moduleResolverHost.fileExists(metadataPath)) {
return this.readMetadata(metadataPath);
}
}
let sf = this.program.getSourceFile(filePath);
if (!sf) {
throw new Error(`Source file ${filePath} not present in program.`);
}
const entry = this.metadataCache.get(sf.path);
const version = this.serviceHost.getScriptVersion(sf.path);
if (entry && entry.version == version) {
if (!entry.content) return undefined;
return [entry.content];
}
const metadata = this.metadataCollector.getMetadata(sf);
this.metadataCache.set(sf.path, {version, content: metadata});
if (metadata) return [metadata];
}
readMetadata(filePath: string) {
try {
const text = this.moduleResolverHost.readFile(filePath);
const result = JSON.parse(text);
if (!Array.isArray(result)) return [result];
return result;
} catch (e) {
console.error(`Failed to read JSON file ${filePath}`);
throw e;
}
protected get program() { return this.getProgram(); }
protected set program(value: ts.Program) {
// Discard the result set by ancestor constructor
}
}
interface MetadataCacheEntry {
version: string;
content: ModuleMetadata;
}

View File

@ -19,7 +19,6 @@ import {TypeScriptServiceHost} from './typescript_host';
* @experimental
*/
export class LanguageServicePlugin {
private ts: typeof ts;
private serviceHost: TypeScriptServiceHost;
private service: LanguageService;
private host: ts.LanguageServiceHost;
@ -27,12 +26,11 @@ export class LanguageServicePlugin {
static 'extension-kind' = 'language-service';
constructor(config: {
ts: typeof ts; host: ts.LanguageServiceHost; service: ts.LanguageService;
host: ts.LanguageServiceHost; service: ts.LanguageService;
registry?: ts.DocumentRegistry, args?: any
}) {
this.ts = config.ts;
this.host = config.host;
this.serviceHost = new TypeScriptServiceHost(this.ts, config.host, config.service);
this.serviceHost = new TypeScriptServiceHost(config.host, config.service);
this.service = createLanguageService(this.serviceHost);
this.serviceHost.setSite(this.service);
}
@ -51,7 +49,7 @@ export class LanguageServicePlugin {
start: error.span.start,
length: error.span.end - error.span.start,
messageText: error.message,
category: this.ts.DiagnosticCategory.Error,
category: ts.DiagnosticCategory.Error,
code: 0
});
}

View File

@ -87,6 +87,25 @@ export interface TemplateSource {
*/
export type TemplateSources = TemplateSource[] /* | undefined */;
/**
* Error information found getting declaration information
*
* A host type; see `LanagueServiceHost`.
*
* @experimental
*/
export interface DeclarationError {
/**
* The span of the error in the declaration's module.
*/
readonly span: Span;
/**
* The message to display describing the error.
*/
readonly message: string;
}
/**
* Information about the component declarations.
*
@ -117,11 +136,10 @@ export interface Declaration {
*/
readonly metadata?: CompileDirectiveMetadata;
/**
* Error reported trying to get the metadata.
*/
readonly error?: string;
readonly errors: DeclarationError[];
}
/**

View File

@ -18,6 +18,7 @@ import {NgModuleResolver} from '@angular/compiler/src/ng_module_resolver';
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
import {ResourceLoader} from '@angular/compiler/src/resource_loader';
import {DomElementSchemaRegistry} from '@angular/compiler/src/schema/dom_element_schema_registry';
import {SummaryResolver} from '@angular/compiler/src/summary_resolver';
import {UrlResolver} from '@angular/compiler/src/url_resolver';
import {Type, ViewEncapsulation} from '@angular/core';
import * as fs from 'fs';
@ -26,17 +27,15 @@ import * as ts from 'typescript';
import {createLanguageService} from './language_service';
import {ReflectorHost} from './reflector_host';
import {BuiltinType, CompletionKind, Declaration, Declarations, Definition, LanguageService, LanguageServiceHost, PipeInfo, Pipes, Signature, Span, Symbol, SymbolDeclaration, SymbolQuery, SymbolTable, TemplateSource, TemplateSources} from './types';
import {BuiltinType, CompletionKind, Declaration, DeclarationError, Declarations, Definition, LanguageService, LanguageServiceHost, PipeInfo, Pipes, Signature, Span, Symbol, SymbolDeclaration, SymbolQuery, SymbolTable, TemplateSource, TemplateSources} from './types';
/**
* Create a `LanguageServiceHost`
*/
export function createLanguageServiceFromTypescript(
typescript: typeof ts, host: ts.LanguageServiceHost,
service: ts.LanguageService): LanguageService {
const ngHost = new TypeScriptServiceHost(typescript, host, service);
host: ts.LanguageServiceHost, service: ts.LanguageService): LanguageService {
const ngHost = new TypeScriptServiceHost(host, service);
const ngServer = createLanguageService(ngHost);
ngHost.setSite(ngServer);
return ngServer;
@ -74,7 +73,6 @@ export class DummyResourceLoader extends ResourceLoader {
* @expermental
*/
export class TypeScriptServiceHost implements LanguageServiceHost {
private ts: typeof ts;
private _resolver: CompileMetadataResolver;
private _staticSymbolCache = new StaticSymbolCache();
private _reflector: StaticReflector;
@ -88,12 +86,9 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
private service: LanguageService;
private fileToComponent: Map<string, StaticSymbol>;
private templateReferences: string[];
private collectedErrors: Map<string, any[]>;
constructor(
typescript: typeof ts, private host: ts.LanguageServiceHost,
private tsService: ts.LanguageService) {
this.ts = typescript;
}
constructor(private host: ts.LanguageServiceHost, private tsService: ts.LanguageService) {}
setSite(service: LanguageService) { this.service = service; }
@ -124,8 +119,9 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
new DirectiveNormalizer(resourceLoader, urlResolver, htmlParser, config);
result = this._resolver = new CompileMetadataResolver(
moduleResolver, directiveResolver, pipeResolver, elementSchemaRegistry,
directiveNormalizer, this.reflector);
moduleResolver, directiveResolver, pipeResolver, new SummaryResolver(),
elementSchemaRegistry, directiveNormalizer, this.reflector,
(error, type) => this.collectError(error, type && type.filePath));
}
return result;
}
@ -157,6 +153,10 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
getAnalyzedModules(): NgAnalyzedModules {
this.validate();
return this.ensureAnalyzedModules();
}
private ensureAnalyzedModules(): NgAnalyzedModules {
let analyzedModules = this.analyzedModules;
if (!analyzedModules) {
const programSymbols = extractProgramSymbols(
@ -185,14 +185,14 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
if (templateSource) {
result.push(templateSource);
} else {
this.ts.forEachChild(child, visit);
ts.forEachChild(child, visit);
}
};
let sourceFile = this.getSourceFile(fileName);
if (sourceFile) {
this.context = sourceFile.path;
this.ts.forEachChild(sourceFile, visit);
ts.forEachChild(sourceFile, visit);
}
return result.length ? result : undefined;
}
@ -220,9 +220,14 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
}
updateAnalyzedModules() {
this.validate();
if (this.modulesOutOfDate) {
this.analyzedModules = null;
this.getAnalyzedModules();
this._reflector = null;
this.templateReferences = null;
this.fileToComponent = null;
this.ensureAnalyzedModules();
this.modulesOutOfDate = false;
}
}
@ -248,10 +253,8 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
this._checker = null;
this._typeCache = [];
this._resolver = null;
this._reflector = null;
this.collectedErrors = null;
this.modulesOutOfDate = true;
this.templateReferences = null;
this.fileToComponent = null;
}
private ensureTemplateMap() {
@ -296,7 +299,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
return new TypeWrapper(type, {node, program, checker}).members();},
get query(): SymbolQuery{
if (!queryCache) {
queryCache = new TypeScriptSymbolQuery(t.ts, t.program, t.checker, sourceFile, () => {
queryCache = new TypeScriptSymbolQuery(t.program, t.checker, sourceFile, () => {
const pipes = t.service.getPipesAt(fileName, node.getStart());
const checker = t.checker;
const program = t.program;
@ -313,11 +316,11 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
let result: TemplateSource|undefined = undefined;
const t = this;
switch (node.kind) {
case this.ts.SyntaxKind.NoSubstitutionTemplateLiteral:
case this.ts.SyntaxKind.StringLiteral:
case ts.SyntaxKind.NoSubstitutionTemplateLiteral:
case ts.SyntaxKind.StringLiteral:
let [declaration, decorator] = this.getTemplateClassDeclFromNode(node);
let queryCache: SymbolQuery|undefined = undefined;
if (declaration) {
if (declaration && declaration.name) {
const sourceFile = this.getSourceFile(fileName);
return this.getSourceFromDeclaration(
fileName, version, this.stringOf(node), shrink(spanOf(node)),
@ -360,19 +363,34 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
throw new Error('Internal error: no context could be determined');
}
const tsConfigPath = findTsConfig(source.path);
const tsConfigPath = findTsConfig(source.fileName);
const basePath = path.dirname(tsConfigPath || this.context);
result = this._reflectorHost = new ReflectorHost(
() => this.tsService.getProgram(), this.host, this.host.getCompilationSettings(),
basePath);
() => this.tsService.getProgram(), this.host, {basePath, genDir: basePath});
}
return result;
}
private collectError(error: any, filePath: string) {
let errorMap = this.collectedErrors;
if (!errorMap) {
errorMap = this.collectedErrors = new Map();
}
let errors = errorMap.get(filePath);
if (!errors) {
errors = [];
this.collectedErrors.set(filePath, errors);
}
errors.push(error);
}
private get reflector(): StaticReflector {
let result = this._reflector;
if (!result) {
result = this._reflector = new StaticReflector(this.reflectorHost, this._staticSymbolCache);
result = this._reflector = new StaticReflector(
this.reflectorHost, this._staticSymbolCache, [], [],
(e, filePath) => this.collectError(e, filePath));
}
return result;
}
@ -380,7 +398,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
private getTemplateClassFromStaticSymbol(type: StaticSymbol): ts.ClassDeclaration|undefined {
const source = this.getSourceFile(type.filePath);
if (source) {
const declarationNode = this.ts.forEachChild(source, child => {
const declarationNode = ts.forEachChild(source, child => {
if (child.kind === ts.SyntaxKind.ClassDeclaration) {
const classDeclaration = child as ts.ClassDeclaration;
if (classDeclaration.name.text === type.name) {
@ -408,7 +426,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
if (!parentNode) {
return TypeScriptServiceHost.missingTemplate;
}
if (parentNode.kind !== this.ts.SyntaxKind.PropertyAssignment) {
if (parentNode.kind !== ts.SyntaxKind.PropertyAssignment) {
return TypeScriptServiceHost.missingTemplate;
} else {
// TODO: Is this different for a literal, i.e. a quoted property name like "template"?
@ -417,28 +435,36 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
}
}
parentNode = parentNode.parent; // ObjectLiteralExpression
if (!parentNode || parentNode.kind !== this.ts.SyntaxKind.ObjectLiteralExpression) {
if (!parentNode || parentNode.kind !== ts.SyntaxKind.ObjectLiteralExpression) {
return TypeScriptServiceHost.missingTemplate;
}
parentNode = parentNode.parent; // CallExpression
if (!parentNode || parentNode.kind !== this.ts.SyntaxKind.CallExpression) {
if (!parentNode || parentNode.kind !== ts.SyntaxKind.CallExpression) {
return TypeScriptServiceHost.missingTemplate;
}
const callTarget = (<ts.CallExpression>parentNode).expression;
let decorator = parentNode.parent; // Decorator
if (!decorator || decorator.kind !== this.ts.SyntaxKind.Decorator) {
if (!decorator || decorator.kind !== ts.SyntaxKind.Decorator) {
return TypeScriptServiceHost.missingTemplate;
}
let declaration = <ts.ClassDeclaration>decorator.parent; // ClassDeclaration
if (!declaration || declaration.kind !== this.ts.SyntaxKind.ClassDeclaration) {
if (!declaration || declaration.kind !== ts.SyntaxKind.ClassDeclaration) {
return TypeScriptServiceHost.missingTemplate;
}
return [declaration, callTarget];
}
private getCollectedErrors(defaultSpan: Span, sourceFile: ts.SourceFile): DeclarationError[] {
const errors = (this.collectedErrors && this.collectedErrors.get(sourceFile.fileName));
return (errors && errors.map((e: any) => {
return {message: e.message, span: spanAt(sourceFile, e.line, e.column) || defaultSpan};
})) ||
[];
}
private getDeclarationFromNode(sourceFile: ts.SourceFile, node: ts.Node): Declaration|undefined {
if (node.kind == ts.SyntaxKind.ClassDeclaration && node.decorators &&
(node as ts.ClassDeclaration).name) {
@ -456,14 +482,22 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
if (this.resolver.isDirective(staticSymbol as any)) {
const {metadata} =
this.resolver.getNonNormalizedDirectiveMetadata(staticSymbol as any);
return {type: staticSymbol, declarationSpan: spanOf(target), metadata};
const declarationSpan = spanOf(target);
return {
type: staticSymbol,
declarationSpan,
metadata,
errors: this.getCollectedErrors(declarationSpan, sourceFile)
};
}
} catch (e) {
if (e.message) {
this.collectError(e, sourceFile.fileName);
const declarationSpan = spanOf(target);
return {
type: staticSymbol,
declarationSpan: spanAt(sourceFile, e.line, e.column) || spanOf(target),
error: e.message
declarationSpan,
errors: this.getCollectedErrors(declarationSpan, sourceFile)
};
}
}
@ -476,9 +510,9 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
private stringOf(node: ts.Node): string|undefined {
switch (node.kind) {
case this.ts.SyntaxKind.NoSubstitutionTemplateLiteral:
case ts.SyntaxKind.NoSubstitutionTemplateLiteral:
return (<ts.LiteralExpression>node).text;
case this.ts.SyntaxKind.StringLiteral:
case ts.SyntaxKind.StringLiteral:
return (<ts.StringLiteral>node).text;
}
}
@ -488,7 +522,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
function find(node: ts.Node): ts.Node|undefined {
if (position >= node.getStart() && position < node.getEnd()) {
return _this.ts.forEachChild(node, find) || node;
return ts.forEachChild(node, find) || node;
}
}
@ -501,26 +535,26 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
switch (kind) {
case BuiltinType.Any:
type = checker.getTypeAtLocation(<ts.Node><any>{
kind: this.ts.SyntaxKind.AsExpression,
expression: <ts.Node>{kind: this.ts.SyntaxKind.TrueKeyword},
type: <ts.Node>{kind: this.ts.SyntaxKind.AnyKeyword}
kind: ts.SyntaxKind.AsExpression,
expression: <ts.Node>{kind: ts.SyntaxKind.TrueKeyword},
type: <ts.Node>{kind: ts.SyntaxKind.AnyKeyword}
});
break;
case BuiltinType.Boolean:
type = checker.getTypeAtLocation(<ts.Node>{kind: this.ts.SyntaxKind.TrueKeyword});
type = checker.getTypeAtLocation(<ts.Node>{kind: ts.SyntaxKind.TrueKeyword});
break;
case BuiltinType.Null:
type = checker.getTypeAtLocation(<ts.Node>{kind: this.ts.SyntaxKind.NullKeyword});
type = checker.getTypeAtLocation(<ts.Node>{kind: ts.SyntaxKind.NullKeyword});
break;
case BuiltinType.Number:
type = checker.getTypeAtLocation(<ts.Node>{kind: this.ts.SyntaxKind.NumericLiteral});
type = checker.getTypeAtLocation(<ts.Node>{kind: ts.SyntaxKind.NumericLiteral});
break;
case BuiltinType.String:
type = checker.getTypeAtLocation(
<ts.Node>{kind: this.ts.SyntaxKind.NoSubstitutionTemplateLiteral});
type =
checker.getTypeAtLocation(<ts.Node>{kind: ts.SyntaxKind.NoSubstitutionTemplateLiteral});
break;
case BuiltinType.Undefined:
type = checker.getTypeAtLocation(<ts.Node>{kind: this.ts.SyntaxKind.VoidExpression});
type = checker.getTypeAtLocation(<ts.Node>{kind: ts.SyntaxKind.VoidExpression});
break;
default:
throw new Error(`Internal error, unhandled literal kind ${kind}:${BuiltinType[kind]}`);
@ -530,30 +564,27 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
}
class TypeScriptSymbolQuery implements SymbolQuery {
private ts: typeof ts;
private typeCache = new Map<BuiltinType, Symbol>();
private pipesCache: SymbolTable;
constructor(
typescript: typeof ts, private program: ts.Program, private checker: ts.TypeChecker,
private source: ts.SourceFile, private fetchPipes: () => SymbolTable) {
this.ts = typescript;
}
private program: ts.Program, private checker: ts.TypeChecker, private source: ts.SourceFile,
private fetchPipes: () => SymbolTable) {}
getTypeKind(symbol: Symbol): BuiltinType {
const type = this.getTsTypeOf(symbol);
if (type) {
if (type.flags & this.ts.TypeFlags.Any) {
if (type.flags & ts.TypeFlags.Any) {
return BuiltinType.Any;
} else if (
type.flags & (this.ts.TypeFlags.String | this.ts.TypeFlags.StringLike |
this.ts.TypeFlags.StringLiteral)) {
type.flags &
(ts.TypeFlags.String | ts.TypeFlags.StringLike | ts.TypeFlags.StringLiteral)) {
return BuiltinType.String;
} else if (type.flags & (this.ts.TypeFlags.Number | this.ts.TypeFlags.NumberLike)) {
} else if (type.flags & (ts.TypeFlags.Number | ts.TypeFlags.NumberLike)) {
return BuiltinType.Number;
} else if (type.flags & (this.ts.TypeFlags.Undefined)) {
} else if (type.flags & (ts.TypeFlags.Undefined)) {
return BuiltinType.Undefined;
} else if (type.flags & (this.ts.TypeFlags.Null)) {
} else if (type.flags & (ts.TypeFlags.Null)) {
return BuiltinType.Null;
}
}

View File

@ -0,0 +1,19 @@
/**
* @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
*/
/**
* @module
* @description
* Entry point for all public APIs of the common package.
*/
import {Version} from '@angular/core';
/**
* @stable
*/
export const VERSION = new Version('0.0.0-PLACEHOLDER');

View File

@ -21,7 +21,7 @@ describe('completions', () => {
let mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts'], toh);
let service = ts.createLanguageService(mockHost, documentRegistry);
let program = service.getProgram();
let ngHost = new TypeScriptServiceHost(ts, mockHost, service);
let ngHost = new TypeScriptServiceHost(mockHost, service);
let ngService = createLanguageService(ngHost);
ngHost.setSite(ngService);
@ -166,6 +166,7 @@ export class MyComponent {
const originalContent = mockHost.getFileContent(fileName);
const newContent = originalContent + code;
mockHost.override(fileName, originalContent + code);
ngHost.updateAnalyzedModules();
try {
cb(fileName, newContent);
} finally {

View File

@ -21,7 +21,7 @@ describe('definitions', () => {
let mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts'], toh);
let service = ts.createLanguageService(mockHost, documentRegistry);
let program = service.getProgram();
let ngHost = new TypeScriptServiceHost(ts, mockHost, service);
let ngHost = new TypeScriptServiceHost(mockHost, service);
let ngService = createLanguageService(ngHost);
ngHost.setSite(ngService);

View File

@ -20,7 +20,7 @@ describe('diagnostics', () => {
let mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts'], toh);
let service = ts.createLanguageService(mockHost, documentRegistry);
let program = service.getProgram();
let ngHost = new TypeScriptServiceHost(ts, mockHost, service);
let ngHost = new TypeScriptServiceHost(mockHost, service);
let ngService = createLanguageService(ngHost);
ngHost.setSite(ngService);
@ -115,11 +115,18 @@ describe('diagnostics', () => {
});
});
it('should not throw for an invalid class', () => {
const code = ` @Component({template: ''}) class`;
addCode(
code, fileName => { expect(() => ngService.getDiagnostics(fileName)).not.toThrow(); });
});
function addCode(code: string, cb: (fileName: string, content?: string) => void) {
const fileName = '/app/app.component.ts';
const originalContent = mockHost.getFileContent(fileName);
const newContent = originalContent + code;
mockHost.override(fileName, originalContent + code);
ngHost.updateAnalyzedModules();
try {
cb(fileName, newContent);
} finally {

View File

@ -22,7 +22,7 @@ describe('hover', () => {
let mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts'], toh);
let service = ts.createLanguageService(mockHost, documentRegistry);
let program = service.getProgram();
let ngHost = new TypeScriptServiceHost(ts, mockHost, service);
let ngHost = new TypeScriptServiceHost(mockHost, service);
let ngService = createLanguageService(ngHost);
ngHost.setSite(ngService);

View File

@ -9,7 +9,7 @@
import * as ts from 'typescript';
import {createLanguageService} from '../src/language_service';
import {Completions, Diagnostic, Diagnostics} from '../src/types';
import {Completions, Diagnostic, Diagnostics, LanguageService} from '../src/types';
import {TypeScriptServiceHost} from '../src/typescript_host';
import {toh} from './test_data';
@ -17,16 +17,42 @@ import {MockTypescriptHost} from './test_utils';
describe('references', () => {
let documentRegistry = ts.createDocumentRegistry();
let mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts'], toh);
let service = ts.createLanguageService(mockHost, documentRegistry);
let program = service.getProgram();
let ngHost = new TypeScriptServiceHost(ts, mockHost, service);
let ngService = createLanguageService(ngHost);
ngHost.setSite(ngService);
let mockHost: MockTypescriptHost;
let service: ts.LanguageService;
let program: ts.Program;
let ngHost: TypeScriptServiceHost;
let ngService: LanguageService = createLanguageService(ngHost);
beforeEach(() => {
mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts'], toh);
service = ts.createLanguageService(mockHost, documentRegistry);
program = service.getProgram();
ngHost = new TypeScriptServiceHost(mockHost, service);
ngService = createLanguageService(ngHost);
ngHost.setSite(ngService);
});
it('should be able to get template references',
() => { expect(() => ngService.getTemplateReferences()).not.toThrow(); });
it('should be able to determine that test.ng is a template reference',
() => { expect(ngService.getTemplateReferences()).toContain('/app/test.ng'); });
it('should be able to get template references for an invalid project', () => {
const moduleCode = `
import {NgModule} from '@angular/core';
import {NewClass} from './test.component';
@NgModule({declarations: [NewClass]}) export class TestModule {}`;
const classCode = `
export class NewClass {}
@Component({})
export class SomeComponent {}
`;
mockHost.addScript('/app/test.module.ts', moduleCode);
mockHost.addScript('/app/test.component.ts', classCode);
expect(() => { ngService.getTemplateReferences(); }).not.toThrow();
});
});

View File

@ -91,6 +91,12 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
}
}
addScript(fileName: string, content: string) {
this.projectVersion++;
this.overrides.set(fileName, content);
this.scriptNames.push(fileName);
}
getCompilationSettings(): ts.CompilerOptions {
return {
target: ts.ScriptTarget.ES5,

View File

@ -29,8 +29,7 @@ describe('plugin', () => {
}
});
let plugin =
new LanguageServicePlugin({ts: ts, host: mockHost, service, registry: documentRegistry});
let plugin = new LanguageServicePlugin({host: mockHost, service, registry: documentRegistry});
it('should not report template errors on tour of heroes', () => {
for (let source of program.getSourceFiles()) {

View File

@ -15,6 +15,7 @@
"@angular/common": ["../../../dist/packages-dist/common"],
"@angular/compiler": ["../../../dist/packages-dist/compiler"],
"@angular/compiler/*": ["../../../dist/packages-dist/compiler/*"],
"@angular/compiler-cli": ["../../../dist/packages-dist/compiler-cli"],
"@angular/platform-server": ["../../../dist/packages-dist/platform-server"],
"@angular/platform-browser": ["../../../dist/packages-dist/platform-browser"],
"@angular/tsc-wrapped": ["../../../dist/tools/@angular/tsc-wrapped"],

View File

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

View File

@ -13,7 +13,7 @@ import {INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS} from './platform_providers'
import {CachedResourceLoader} from './resource_loader/resource_loader_cache';
export * from './private_export';
export {VERSION} from './version';
/**
* @experimental
*/

View File

@ -0,0 +1,19 @@
/**
* @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
*/
/**
* @module
* @description
* Entry point for all public APIs of the common package.
*/
import {Version} from '@angular/core';
/**
* @stable
*/
export const VERSION = new Version('0.0.0-PLACEHOLDER');

View File

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

View File

@ -7,7 +7,7 @@
*/
import {CommonModule, PlatformLocation} from '@angular/common';
import {ApplicationModule, ErrorHandler, NgModule, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, RootRenderer, Sanitizer, SkipSelf, Testability, VERSION, createPlatformFactory, platformCore} from '@angular/core';
import {ApplicationModule, ErrorHandler, NgModule, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, RootRenderer, Sanitizer, SkipSelf, Testability, createPlatformFactory, platformCore} from '@angular/core';
import {AnimationDriver} from '../src/dom/animation_driver';
import {WebAnimationsDriver} from '../src/dom/web_animations_driver';
@ -29,7 +29,6 @@ import {DomSanitizer, DomSanitizerImpl} from './security/dom_sanitization_servic
export const INTERNAL_BROWSER_PLATFORM_PROVIDERS: Provider[] = [
{provide: PLATFORM_INITIALIZER, useValue: initDomAdapter, multi: true},
{provide: PLATFORM_INITIALIZER, useValue: recordAngularVersion, multi: true},
{provide: PlatformLocation, useClass: BrowserPlatformLocation}
];
@ -55,14 +54,6 @@ export function initDomAdapter() {
BrowserGetTestability.init();
}
export function recordAngularVersion(): void {
const domAdapter = getDOM();
const body = domAdapter.getElementsByTagName(domAdapter.defaultDoc(), 'body');
if (body.length > 0) {
domAdapter.setAttribute(body[0], 'ng-version', VERSION.full);
}
}
export function errorHandler(): ErrorHandler {
return new ErrorHandler();
}

View File

@ -16,4 +16,5 @@ export {DOCUMENT} from './dom/dom_tokens';
export {EVENT_MANAGER_PLUGINS, EventManager} from './dom/events/event_manager';
export {HAMMER_GESTURE_CONFIG, HammerGestureConfig} from './dom/events/hammer_gestures';
export {DomSanitizer, SafeHtml, SafeResourceUrl, SafeScript, SafeStyle, SafeUrl} from './security/dom_sanitization_service';
export * from './private_export';
export * from './private_export';
export {VERSION} from './version';

View File

@ -0,0 +1,19 @@
/**
* @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
*/
/**
* @module
* @description
* Entry point for all public APIs of the common package.
*/
import {Version} from '@angular/core';
/**
* @stable
*/
export const VERSION = new Version('0.0.0-PLACEHOLDER');

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {APP_INITIALIZER, CUSTOM_ELEMENTS_SCHEMA, Compiler, Component, Directive, ErrorHandler, Inject, Input, NgModule, OnDestroy, PLATFORM_INITIALIZER, Pipe, Provider, createPlatformFactory} from '@angular/core';
import {APP_INITIALIZER, CUSTOM_ELEMENTS_SCHEMA, Compiler, Component, Directive, ErrorHandler, Inject, Input, NgModule, OnDestroy, PLATFORM_INITIALIZER, Pipe, Provider, VERSION, createPlatformFactory} from '@angular/core';
import {ApplicationRef, destroyPlatform} from '@angular/core/src/application_ref';
import {Console} from '@angular/core/src/console';
import {ComponentRef} from '@angular/core/src/linker/component_factory';
@ -219,6 +219,7 @@ export function main() {
const refPromise = bootstrap(HelloRootCmp, testProviders);
refPromise.then((ref) => {
expect(el).toHaveText('hello world!');
expect(el.getAttribute('ng-version')).toEqual(VERSION.full);
async.done();
});
}));

View File

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

View File

@ -8,3 +8,4 @@
export {ServerModule, platformDynamicServer, platformServer} from './server';
export * from './private_export';
export {VERSION} from './version';

View File

@ -0,0 +1,19 @@
/**
* @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
*/
/**
* @module
* @description
* Entry point for all public APIs of the common package.
*/
import {Version} from '@angular/core';
/**
* @stable
*/
export const VERSION = new Version('0.0.0-PLACEHOLDER');

View File

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

View File

@ -9,7 +9,7 @@
import {ResourceLoader, platformCoreDynamic} from '@angular/compiler';
import {COMPILER_OPTIONS, PlatformRef, Provider, createPlatformFactory} from '@angular/core';
import {ResourceLoaderImpl} from './private_import_platform-browser-dynamic';
export {VERSION} from './version';
/**
* @experimental API related to bootstrapping are still under review.

View File

@ -0,0 +1,19 @@
/**
* @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
*/
/**
* @module
* @description
* Entry point for all public APIs of the common package.
*/
import {Version} from '@angular/core';
/**
* @stable
*/
export const VERSION = new Version('0.0.0-PLACEHOLDER');

View File

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

View File

@ -10,6 +10,7 @@ import {PlatformRef, Provider} from '@angular/core';
import {WORKER_SCRIPT, platformWorkerUi} from './worker_render';
export {VERSION} from './version';
export {ClientMessageBroker, ClientMessageBrokerFactory, FnArg, UiArguments} from './web_workers/shared/client_message_broker';
export {MessageBus, MessageBusSink, MessageBusSource} from './web_workers/shared/message_bus';
export {PRIMITIVE} from './web_workers/shared/serializer';

View File

@ -0,0 +1,19 @@
/**
* @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
*/
/**
* @module
* @description
* Entry point for all public APIs of the common package.
*/
import {Version} from '@angular/core';
/**
* @stable
*/
export const VERSION = new Version('0.0.0-PLACEHOLDER');

View File

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

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