Compare commits
44 Commits
2.3.0-rc.0
...
2.3.0
Author | SHA1 | Date | |
---|---|---|---|
13b41bd631 | |||
f3524af68f | |||
0a56f4ea82 | |||
cf52284ac3 | |||
4a09c81724 | |||
16efb13dd1 | |||
986abbe0b2 | |||
25c2141991 | |||
2893c2c0a2 | |||
393c1007a8 | |||
66b6fc010d | |||
f31c9470fa | |||
4bd8f58552 | |||
93556a5720 | |||
5614c4ff0f | |||
c3065aac7a | |||
c767df0e4e | |||
25e5b2fdf0 | |||
307c4693dc | |||
349ad75de3 | |||
f562cbf86c | |||
804943c9b1 | |||
dea59165de | |||
a1322873c8 | |||
b8c839bd51 | |||
d2e5198b93 | |||
6cf7a1bf84 | |||
d46b8deeea | |||
bbb7a39414 | |||
d7d8fab211 | |||
51b06924bd | |||
aa4bd14b3f | |||
3ff6554cbc | |||
dfd8140084 | |||
6ea3ab7e14 | |||
9761db5ac2 | |||
75d1617b63 | |||
614a35d539 | |||
9ab401f4d3 | |||
82c81cd0d2 | |||
12959f444c | |||
25a6da244c | |||
5908b66ae9 | |||
480ef20eb1 |
59
CHANGELOG.md
59
CHANGELOG.md
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
5
build.sh
5
build.sh
@ -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
|
||||
|
@ -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.
|
||||
|
20
modules/@angular/common/src/common.ts
Normal file
20
modules/@angular/common/src/common.ts
Normal 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';
|
@ -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}"`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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';
|
13
modules/@angular/common/src/location/index.ts
Normal file
13
modules/@angular/common/src/location/index.ts
Normal 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';
|
@ -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$/, '');
|
||||
}
|
||||
|
19
modules/@angular/common/src/version.ts
Normal file
19
modules/@angular/common/src/version.ts
Normal 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');
|
@ -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"');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
@ -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';
|
||||
|
@ -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';
|
||||
|
@ -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();
|
@ -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",
|
||||
|
@ -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"
|
||||
},
|
||||
|
@ -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;
|
||||
|
@ -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; }
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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).
|
||||
|
19
modules/@angular/compiler-cli/src/version.ts
Normal file
19
modules/@angular/compiler-cli/src/version.ts
Normal 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');
|
@ -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,
|
||||
|
177
modules/@angular/compiler-cli/test/main_spec.ts
Normal file
177
modules/@angular/compiler-cli/test/main_spec.ts
Normal 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));
|
||||
});
|
||||
});
|
13
modules/@angular/compiler-cli/test/private_import_core.ts
Normal file
13
modules/@angular/compiler-cli/test/private_import_core.ts
Normal 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;
|
34
modules/@angular/compiler-cli/tsconfig-2015.json
Normal file
34
modules/@angular/compiler-cli/tsconfig-2015.json
Normal 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"
|
||||
]
|
||||
}
|
@ -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';
|
||||
|
@ -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;
|
||||
}
|
@ -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};
|
||||
}
|
||||
|
@ -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>;
|
||||
}
|
||||
}
|
||||
|
@ -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) {}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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.
|
||||
*
|
||||
|
124
modules/@angular/compiler/src/aot/summary_resolver.ts
Normal file
124
modules/@angular/compiler/src/aot/summary_resolver.ts
Normal 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`;
|
||||
}
|
19
modules/@angular/compiler/src/aot/utils.ts
Normal file
19
modules/@angular/compiler/src/aot/utils.ts
Normal 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;
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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, [], {});
|
||||
|
@ -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';
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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[] {
|
||||
|
14
modules/@angular/compiler/src/summary_resolver.ts
Normal file
14
modules/@angular/compiler/src/summary_resolver.ts
Normal 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; }
|
||||
}
|
19
modules/@angular/compiler/src/version.ts
Normal file
19
modules/@angular/compiler/src/version.ts
Normal 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');
|
@ -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}) {}
|
||||
|
138
modules/@angular/compiler/test/aot/summary_resolver_spec.ts
Normal file
138
modules/@angular/compiler/test/aot/summary_resolver_spec.ts
Normal 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';
|
||||
}
|
||||
}
|
@ -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(`['{', '}}'] 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);
|
||||
}));
|
||||
|
@ -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.
|
||||
|
@ -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';
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -1008,7 +1008,7 @@ export interface HostListener {
|
||||
}
|
||||
|
||||
/**
|
||||
* HostBinding decorator and metadata.
|
||||
* HostListener decorator and metadata.
|
||||
*
|
||||
* @stable
|
||||
* @Annotation
|
||||
|
@ -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');
|
||||
|
@ -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;
|
||||
|
@ -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`).
|
||||
|
||||
|
@ -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) => {
|
||||
|
@ -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.
|
||||
|
@ -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';
|
||||
|
19
modules/@angular/forms/src/version.ts
Normal file
19
modules/@angular/forms/src/version.ts
Normal 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');
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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';
|
||||
|
19
modules/@angular/http/src/version.ts
Normal file
19
modules/@angular/http/src/version.ts
Normal 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');
|
@ -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'};
|
||||
|
@ -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;
|
||||
|
@ -16,6 +16,7 @@ var esm = 'esm/';
|
||||
|
||||
var locations = {
|
||||
'tsc-wrapped': normalize('../../../dist/tools/@angular') + '/',
|
||||
'compiler-cli': normalize('../../../dist/esm') + '/'
|
||||
};
|
||||
|
||||
var esm_suffixes = {};
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
@ -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
|
||||
});
|
||||
}
|
||||
|
@ -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[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
19
modules/@angular/language-service/src/version.ts
Normal file
19
modules/@angular/language-service/src/version.ts
Normal 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');
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
});
|
||||
|
||||
});
|
@ -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,
|
||||
|
@ -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()) {
|
||||
|
@ -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"],
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
*/
|
||||
|
19
modules/@angular/platform-browser-dynamic/src/version.ts
Normal file
19
modules/@angular/platform-browser-dynamic/src/version.ts
Normal 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');
|
@ -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.
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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';
|
||||
|
19
modules/@angular/platform-browser/src/version.ts
Normal file
19
modules/@angular/platform-browser/src/version.ts
Normal 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');
|
@ -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();
|
||||
});
|
||||
}));
|
||||
|
@ -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.
|
||||
|
@ -8,3 +8,4 @@
|
||||
|
||||
export {ServerModule, platformDynamicServer, platformServer} from './server';
|
||||
export * from './private_export';
|
||||
export {VERSION} from './version';
|
||||
|
19
modules/@angular/platform-server/src/version.ts
Normal file
19
modules/@angular/platform-server/src/version.ts
Normal 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');
|
@ -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.
|
||||
|
@ -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.
|
||||
|
19
modules/@angular/platform-webworker-dynamic/src/version.ts
Normal file
19
modules/@angular/platform-webworker-dynamic/src/version.ts
Normal 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');
|
@ -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.
|
||||
|
@ -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';
|
||||
|
19
modules/@angular/platform-webworker/src/version.ts
Normal file
19
modules/@angular/platform-webworker/src/version.ts
Normal 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');
|
@ -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
Reference in New Issue
Block a user