style(compiler-cli): reformat of codebase with new clang-format version (#36520)

This commit reformats the packages/compiler-cli tree using the new version
of clang-format.

PR Close #36520
This commit is contained in:
Alex Rickabaugh 2020-04-07 12:43:43 -07:00 committed by atscott
parent 717df13207
commit 0a69a2832b
205 changed files with 2949 additions and 2122 deletions

View File

@ -20,7 +20,9 @@ export class NormalService {
}) })
export class AppComponent { export class AppComponent {
found: boolean; found: boolean;
constructor(service: ShakeableService) { this.found = !!service.normal; } constructor(service: ShakeableService) {
this.found = !!service.normal;
}
} }
@NgModule({ @NgModule({

View File

@ -29,7 +29,9 @@ export class AppComponent {
export class ChildComponent { export class ChildComponent {
found: boolean; found: boolean;
constructor(@Optional() @Self() service: Service|null) { this.found = !!service; } constructor(@Optional() @Self() service: Service|null) {
this.found = !!service;
}
} }
@NgModule({ @NgModule({

View File

@ -21,7 +21,9 @@ export class NormalService {
}) })
export class AppComponent { export class AppComponent {
found: boolean; found: boolean;
constructor(service: NormalService) { this.found = !!service.shakeable; } constructor(service: NormalService) {
this.found = !!service.shakeable;
}
} }
@NgModule({ @NgModule({

View File

@ -17,7 +17,9 @@ import {ServerModule} from '@angular/platform-server';
}) })
export class AppComponent { export class AppComponent {
data: string; data: string;
constructor(service: Service) { this.data = service.data; } constructor(service: Service) {
this.data = service.data;
}
} }
@NgModule({ @NgModule({

View File

@ -6,11 +6,13 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Component, Inject, Injectable, InjectionToken, NgModule, forwardRef, inject} from '@angular/core'; import {Component, forwardRef, Inject, inject, Injectable, InjectionToken, NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser'; import {BrowserModule} from '@angular/platform-browser';
import {ServerModule} from '@angular/platform-server'; import {ServerModule} from '@angular/platform-server';
export interface IService { readonly dep: {readonly data: string;}; } export interface IService {
readonly dep: {readonly data: string;};
}
@NgModule({}) @NgModule({})
export class TokenModule { export class TokenModule {
@ -28,7 +30,9 @@ export const TOKEN = new InjectionToken('test', {
}) })
export class AppComponent { export class AppComponent {
data: string; data: string;
constructor(@Inject(TOKEN) service: IService) { this.data = service.dep.data; } constructor(@Inject(TOKEN) service: IService) {
this.data = service.dep.data;
}
} }
@NgModule({ @NgModule({

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Component, INJECTOR, Injectable, NgModule} from '@angular/core'; import {Component, Injectable, INJECTOR, NgModule} from '@angular/core';
import {TestBed} from '@angular/core/testing'; import {TestBed} from '@angular/core/testing';
import {renderModuleFactory} from '@angular/platform-server'; import {renderModuleFactory} from '@angular/platform-server';
import {BasicAppModuleNgFactory} from 'app_built/src/basic.ngfactory'; import {BasicAppModuleNgFactory} from 'app_built/src/basic.ngfactory';
@ -104,7 +104,6 @@ describe('ngInjectableDef Bazel Integration', () => {
}); });
it('allows provider override in JIT for module-scoped @Injectables', () => { it('allows provider override in JIT for module-scoped @Injectables', () => {
@NgModule() @NgModule()
class Module { class Module {
} }
@ -172,7 +171,9 @@ describe('ngInjectableDef Bazel Integration', () => {
// ChildServices exteds ParentService but does not have @Injectable // ChildServices exteds ParentService but does not have @Injectable
class ChildService extends ParentService { class ChildService extends ParentService {
constructor(value: string) { super(value); } constructor(value: string) {
super(value);
}
static ngInjectableDef = { static ngInjectableDef = {
providedIn: 'root', providedIn: 'root',
factory: () => new ChildService('child'), factory: () => new ChildService('child'),

View File

@ -6,17 +6,23 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Injectable, InjectionToken, Injector, NgModule, forwardRef, ɵcreateInjector as createInjector} from '@angular/core'; import {forwardRef, Injectable, InjectionToken, Injector, NgModule, ɵcreateInjector as createInjector} from '@angular/core';
import {AOT_TOKEN, AotModule, AotService} from 'app_built/src/module'; import {AOT_TOKEN, AotModule, AotService} from 'app_built/src/module';
describe('Ivy NgModule', () => { describe('Ivy NgModule', () => {
describe('AOT', () => { describe('AOT', () => {
let injector: Injector; let injector: Injector;
beforeEach(() => { injector = createInjector(AotModule); }); beforeEach(() => {
it('works', () => { expect(injector.get(AotService) instanceof AotService).toBeTruthy(); }); injector = createInjector(AotModule);
});
it('works', () => {
expect(injector.get(AotService) instanceof AotService).toBeTruthy();
});
it('merges imports and exports', () => { expect(injector.get(AOT_TOKEN)).toEqual('exports'); }); it('merges imports and exports', () => {
expect(injector.get(AOT_TOKEN)).toEqual('exports');
});
}); });
@ -38,7 +44,9 @@ describe('Ivy NgModule', () => {
class JitAppModule { class JitAppModule {
} }
it('works', () => { createInjector(JitAppModule); }); it('works', () => {
createInjector(JitAppModule);
});
it('throws an error on circular module dependencies', () => { it('throws an error on circular module dependencies', () => {
@NgModule({ @NgModule({

View File

@ -16,10 +16,8 @@ export class LazyFeatureComponent {
@NgModule({ @NgModule({
imports: [RouterModule.forChild([ imports: [RouterModule.forChild([
{path: '', component: LazyFeatureComponent, pathMatch: 'full'}, {path: '', component: LazyFeatureComponent, pathMatch: 'full'},
{path: 'feature', loadChildren: './feature.module#FeatureModule'}, { {path: 'feature', loadChildren: './feature.module#FeatureModule'},
path: 'nested-feature', {path: 'nested-feature', loadChildren: './lazy-feature-nested.module#LazyFeatureNestedModule'}
loadChildren: './lazy-feature-nested.module#LazyFeatureNestedModule'
}
])], ])],
declarations: [LazyFeatureComponent] declarations: [LazyFeatureComponent]
}) })

View File

@ -5,7 +5,7 @@
* Use of this source code is governed by an MIT-style license that can be * 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 * found in the LICENSE file at https://angular.io/license
*/ */
import {AUTO_STYLE, animate, state, style, transition, trigger} from '@angular/animations'; import {animate, AUTO_STYLE, state, style, transition, trigger} from '@angular/animations';
import {Component} from '@angular/core'; import {Component} from '@angular/core';
@Component({ @Component({
@ -30,8 +30,16 @@ import {Component} from '@angular/core';
}) })
export class AnimateCmp { export class AnimateCmp {
stateExpression: string; stateExpression: string;
constructor() { this.setAsClosed(); } constructor() {
setAsSomethingElse() { this.stateExpression = 'something'; } this.setAsClosed();
setAsOpen() { this.stateExpression = 'open'; } }
setAsClosed() { this.stateExpression = 'closed'; } setAsSomethingElse() {
this.stateExpression = 'something';
}
setAsOpen() {
this.stateExpression = 'open';
}
setAsClosed() {
this.stateExpression = 'closed';
}
} }

View File

@ -8,6 +8,8 @@
import {InjectionToken} from '@angular/core'; import {InjectionToken} from '@angular/core';
export interface Named { name: string; } export interface Named {
name: string;
}
export const CUSTOM = new InjectionToken<Named>('CUSTOM'); export const CUSTOM = new InjectionToken<Named>('CUSTOM');

View File

@ -10,5 +10,7 @@ import {Component} from '@angular/core';
@Component({selector: 'comp-with-error', templateUrl: 'errors.html'}) @Component({selector: 'comp-with-error', templateUrl: 'errors.html'})
export class BindingErrorComp { export class BindingErrorComp {
createError() { throw new Error('Test'); } createError() {
throw new Error('Test');
}
} }

View File

@ -7,7 +7,7 @@
*/ */
import * as common from '@angular/common'; import * as common from '@angular/common';
import {CUSTOM_ELEMENTS_SCHEMA, Component, Directive, EventEmitter, Inject, InjectionToken, NgModule, Output, forwardRef} from '@angular/core'; import {Component, CUSTOM_ELEMENTS_SCHEMA, Directive, EventEmitter, forwardRef, Inject, InjectionToken, NgModule, Output} from '@angular/core';
import {Observable} from 'rxjs'; import {Observable} from 'rxjs';
import {wrapInArray} from './funcs'; import {wrapInArray} from './funcs';
@ -62,7 +62,9 @@ export class CompUsingCustomElements {
}) })
export class CompConsumingEvents { export class CompConsumingEvents {
handleDomEventVoid(e: any): void {} handleDomEventVoid(e: any): void {}
handleDomEventPreventDefault(e: any): boolean { return false; } handleDomEventPreventDefault(e: any): boolean {
return false;
}
handleDirEvent(e: any): void {} handleDirEvent(e: any): void {}
} }
@ -70,8 +72,7 @@ export class CompConsumingEvents {
selector: '[dirEvent]', selector: '[dirEvent]',
}) })
export class DirPublishingEvents { export class DirPublishingEvents {
@Output('dirEvent') @Output('dirEvent') dirEvent: Observable<string> = new EventEmitter();
dirEvent: Observable<string> = new EventEmitter();
} }
@NgModule({schemas: [CUSTOM_ELEMENTS_SCHEMA], declarations: wrapInArray(CompUsingCustomElements)}) @NgModule({schemas: [CUSTOM_ELEMENTS_SCHEMA], declarations: wrapInArray(CompUsingCustomElements)})

View File

@ -11,7 +11,7 @@ import {Component, Directive, Injectable, NgModule, Pipe} from '@angular/core';
const instances = new Map<any, Base>(); const instances = new Map<any, Base>();
export function expectInstanceCreated(type: any) { export function expectInstanceCreated(type: any) {
const instance = instances.get(type) !; const instance = instances.get(type)!;
expect(instance).toBeDefined(); expect(instance).toBeDefined();
expect(instance.dep instanceof SomeDep).toBe(true); expect(instance.dep instanceof SomeDep).toBe(true);
} }
@ -19,7 +19,9 @@ export function expectInstanceCreated(type: any) {
export class SomeDep {} export class SomeDep {}
export class Base { export class Base {
constructor(public dep: SomeDep) { instances.set(Object.getPrototypeOf(this).constructor, this); } constructor(public dep: SomeDep) {
instances.set(Object.getPrototypeOf(this).constructor, this);
}
} }
@Component({templateUrl: './jit_summaries.html'}) @Component({templateUrl: './jit_summaries.html'})
@ -36,7 +38,9 @@ export class SomeDirective extends Base {
@Pipe({name: 'somePipe'}) @Pipe({name: 'somePipe'})
export class SomePipe extends Base { export class SomePipe extends Base {
transform(value: any) { return value; } transform(value: any) {
return value;
}
} }
@Injectable() @Injectable()

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {ApplicationRef, NgModule, forwardRef} from '@angular/core'; import {ApplicationRef, forwardRef, NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms'; import {FormsModule} from '@angular/forms';
import {ServerModule} from '@angular/platform-server'; import {ServerModule} from '@angular/platform-server';
import {FlatModule} from 'flat_module'; import {FlatModule} from 'flat_module';

View File

@ -19,24 +19,26 @@ export class ServiceUsingLibModule {
@Directive({selector: '[someDir]', host: {'[title]': 'someDir'}}) @Directive({selector: '[someDir]', host: {'[title]': 'someDir'}})
export class SomeDirectiveInRootModule { export class SomeDirectiveInRootModule {
@Input() @Input() someDir: string;
someDir: string;
} }
@Directive({selector: '[someDir]', host: {'[title]': 'someDir'}}) @Directive({selector: '[someDir]', host: {'[title]': 'someDir'}})
export class SomeDirectiveInLibModule { export class SomeDirectiveInLibModule {
@Input() @Input() someDir: string;
someDir: string;
} }
@Pipe({name: 'somePipe'}) @Pipe({name: 'somePipe'})
export class SomePipeInRootModule { export class SomePipeInRootModule {
transform(value: string): any { return `transformed ${value}`; } transform(value: string): any {
return `transformed ${value}`;
}
} }
@Pipe({name: 'somePipe'}) @Pipe({name: 'somePipe'})
export class SomePipeInLibModule { export class SomePipeInLibModule {
transform(value: string): any { return `transformed ${value}`; } transform(value: string): any {
return `transformed ${value}`;
}
} }
@Component({selector: 'comp', template: `<div [someDir]="'someValue' | somePipe"></div>`}) @Component({selector: 'comp', template: `<div [someDir]="'someValue' | somePipe"></div>`})
@ -66,8 +68,8 @@ export class SomeLibModule {
return { return {
ngModule: SomeLibModule, ngModule: SomeLibModule,
providers: [ providers: [
ServiceUsingLibModule, provideValueWithEntryComponents( ServiceUsingLibModule,
[{a: 'b', component: CompUsingLibModuleDirectiveAndPipe}]) provideValueWithEntryComponents([{a: 'b', component: CompUsingLibModuleDirectiveAndPipe}])
] ]
}; };
} }

View File

@ -8,9 +8,9 @@
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {TestBed} from '@angular/core/testing'; import {TestBed} from '@angular/core/testing';
import {ServerTestingModule, platformServerTesting} from '@angular/platform-server/testing'; import {platformServerTesting, ServerTestingModule} from '@angular/platform-server/testing';
import {SomeDep, SomeDirective, SomeModule, SomePipe, SomePrivateComponent, SomeService, expectInstanceCreated} from '../src/jit_summaries'; import {expectInstanceCreated, SomeDep, SomeDirective, SomeModule, SomePipe, SomePrivateComponent, SomeService} from '../src/jit_summaries';
import {SomeModuleNgSummary} from '../src/jit_summaries.ngsummary'; import {SomeModuleNgSummary} from '../src/jit_summaries.ngsummary';
describe('Jit Summaries', () => { describe('Jit Summaries', () => {
@ -18,7 +18,9 @@ describe('Jit Summaries', () => {
TestBed.initTestEnvironment(ServerTestingModule, platformServerTesting(), SomeModuleNgSummary); TestBed.initTestEnvironment(ServerTestingModule, platformServerTesting(), SomeModuleNgSummary);
}); });
afterEach(() => { TestBed.resetTestEnvironment(); }); afterEach(() => {
TestBed.resetTestEnvironment();
});
it('should use directive metadata from summaries', () => { it('should use directive metadata from summaries', () => {
@Component({template: '<div someDir></div>'}) @Component({template: '<div someDir></div>'})

View File

@ -10,7 +10,7 @@ import './init';
import {ComponentUsingThirdParty} from '../src/comp_using_3rdp'; import {ComponentUsingThirdParty} from '../src/comp_using_3rdp';
import {ComponentUsingFlatModule} from '../src/comp_using_flat_module'; import {ComponentUsingFlatModule} from '../src/comp_using_flat_module';
import {MainModule} from '../src/module'; import {MainModule} from '../src/module';
import {CompUsingLibModuleDirectiveAndPipe, CompUsingRootModuleDirectiveAndPipe, SOME_TOKEN, ServiceUsingLibModule, SomeLibModule, SomeService} from '../src/module_fixtures'; import {CompUsingLibModuleDirectiveAndPipe, CompUsingRootModuleDirectiveAndPipe, ServiceUsingLibModule, SOME_TOKEN, SomeLibModule, SomeService} from '../src/module_fixtures';
import {createComponent, createModule} from './util'; import {createComponent, createModule} from './util';

View File

@ -19,7 +19,6 @@ describe('child queries', () => {
debugElement.query(By.directive(CompWithChildQuery)); debugElement.query(By.directive(CompWithChildQuery));
expect(childQueryCompFixture.componentInstance.child).toBeDefined(); expect(childQueryCompFixture.componentInstance.child).toBeDefined();
expect(childQueryCompFixture.componentInstance.child instanceof CompForChildQuery).toBe(true); expect(childQueryCompFixture.componentInstance.child instanceof CompForChildQuery).toBe(true);
}); });
it('should support compiling children queries', () => { it('should support compiling children queries', () => {

View File

@ -31,7 +31,7 @@ function getSourcePositionForStack(stack: string): {source: string, line: number
const htmlLocations = stack const htmlLocations = stack
.split('\n') .split('\n')
// e.g. at View_MyComp_0 (...html:153:40) // e.g. at View_MyComp_0 (...html:153:40)
.map(line => /\((.*\.html):(\d+):(\d+)/.exec(line) !) .map(line => /\((.*\.html):(\d+):(\d+)/.exec(line)!)
.filter(match => !!match) .filter(match => !!match)
.map(match => ({ .map(match => ({
source: match[1], source: match[1],

View File

@ -22,10 +22,15 @@ import {createProgram, readConfiguration} from '@angular/compiler-cli';
* properly read and wrote. * properly read and wrote.
*/ */
function main() { function main() {
Promise.resolve().then(() => lazyRoutesTest()).then(() => { process.exit(0); }).catch((err) => { Promise.resolve()
console.error(err.stack); .then(() => lazyRoutesTest())
process.exit(1); .then(() => {
}); process.exit(0);
})
.catch((err) => {
console.error(err.stack);
process.exit(1);
});
} }
function lazyRoutesTest() { function lazyRoutesTest() {
@ -36,7 +41,8 @@ function lazyRoutesTest() {
const host = ts.createCompilerHost(config.options, true); const host = ts.createCompilerHost(config.options, true);
const program = createProgram({ const program = createProgram({
rootNames: config.rootNames, rootNames: config.rootNames,
options: config.options, host, options: config.options,
host,
}); });
config.options.basePath = basePath; config.options.basePath = basePath;

View File

@ -13,7 +13,7 @@ import {platformServerTesting} from '@angular/platform-server/testing';
import {MainModule} from '../src/module'; import {MainModule} from '../src/module';
import {MainModuleNgFactory} from '../src/module.ngfactory'; import {MainModuleNgFactory} from '../src/module.ngfactory';
let mainModuleRef: NgModuleRef<MainModule> = null !; let mainModuleRef: NgModuleRef<MainModule> = null!;
beforeEach((done) => { beforeEach((done) => {
platformServerTesting().bootstrapModuleFactory(MainModuleNgFactory).then((moduleRef: any) => { platformServerTesting().bootstrapModuleFactory(MainModuleNgFactory).then((moduleRef: any) => {
mainModuleRef = moduleRef; mainModuleRef = moduleRef;

View File

@ -35,7 +35,8 @@ export function translateDiagnostics(
const fileName = span.start.file.url; const fileName = span.start.file.url;
ng.push({ ng.push({
messageText: diagnosticMessageToString(diagnostic.messageText), messageText: diagnosticMessageToString(diagnostic.messageText),
category: diagnostic.category, span, category: diagnostic.category,
span,
source: SOURCE, source: SOURCE,
code: DEFAULT_ERROR_CODE code: DEFAULT_ERROR_CODE
}); });
@ -53,6 +54,6 @@ function sourceSpanOf(host: TypeCheckHost, source: ts.SourceFile, start: number)
return host.parseSourceSpanOf(source.fileName, line, character); return host.parseSourceSpanOf(source.fileName, line, character);
} }
function diagnosticMessageToString(message: ts.DiagnosticMessageChain | string): string { function diagnosticMessageToString(message: ts.DiagnosticMessageChain|string): string {
return ts.flattenDiagnosticMessageText(message, '\n'); return ts.flattenDiagnosticMessageText(message, '\n');
} }

View File

@ -16,4 +16,4 @@ to the language service.
*/ */
export {MetadataCollector, ModuleMetadata} from './metadata'; export {MetadataCollector, ModuleMetadata} from './metadata';
export {CompilerOptions} from './transformers/api'; export {CompilerOptions} from './transformers/api';
export {MetadataReaderCache, MetadataReaderHost, createMetadataReaderCache, readMetadata} from './transformers/metadata_reader'; export {createMetadataReaderCache, MetadataReaderCache, MetadataReaderHost, readMetadata} from './transformers/metadata_reader';

View File

@ -24,9 +24,9 @@ import {NodeJSFileSystem, setFileSystem} from './ngtsc/file_system';
export function main( export function main(
args: string[], consoleError: (s: string) => void = console.error, args: string[], consoleError: (s: string) => void = console.error,
config?: NgcParsedConfiguration, customTransformers?: api.CustomTransformers, programReuse?: { config?: NgcParsedConfiguration, customTransformers?: api.CustomTransformers, programReuse?: {
program: api.Program | undefined, program: api.Program|undefined,
}, },
modifiedResourceFiles?: Set<string>| null): number { modifiedResourceFiles?: Set<string>|null): number {
let {project, rootNames, options, errors: configErrors, watch, emitFlags} = let {project, rootNames, options, errors: configErrors, watch, emitFlags} =
config || readNgcCommandLineAndConfiguration(args); config || readNgcCommandLineAndConfiguration(args);
if (configErrors.length) { if (configErrors.length) {
@ -47,7 +47,9 @@ export function main(
options, options,
emitFlags, emitFlags,
oldProgram, oldProgram,
emitCallback: createEmitCallback(options), customTransformers, modifiedResourceFiles emitCallback: createEmitCallback(options),
customTransformers,
modifiedResourceFiles
}); });
if (programReuse !== undefined) { if (programReuse !== undefined) {
programReuse.program = program; programReuse.program = program;
@ -57,8 +59,8 @@ export function main(
export function mainDiagnosticsForTest( export function mainDiagnosticsForTest(
args: string[], config?: NgcParsedConfiguration, args: string[], config?: NgcParsedConfiguration,
programReuse?: {program: api.Program | undefined}, programReuse?: {program: api.Program|undefined},
modifiedResourceFiles?: Set<string>| null): ReadonlyArray<ts.Diagnostic|api.Diagnostic> { modifiedResourceFiles?: Set<string>|null): ReadonlyArray<ts.Diagnostic|api.Diagnostic> {
let {project, rootNames, options, errors: configErrors, watch, emitFlags} = let {project, rootNames, options, errors: configErrors, watch, emitFlags} =
config || readNgcCommandLineAndConfiguration(args); config || readNgcCommandLineAndConfiguration(args);
if (configErrors.length) { if (configErrors.length) {
@ -100,9 +102,10 @@ function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|un
options.emitDecoratorMetadata = true; options.emitDecoratorMetadata = true;
} }
const tsickleHost: Pick< const tsickleHost: Pick<
tsickle.TsickleHost, 'shouldSkipTsickleProcessing'|'pathToModuleName'| tsickle.TsickleHost,
'shouldIgnoreWarningsForPath'|'fileNameToModuleId'|'googmodule'|'untyped'| 'shouldSkipTsickleProcessing'|'pathToModuleName'|'shouldIgnoreWarningsForPath'|
'convertIndexImportShorthand'|'transformDecorators'|'transformTypesToClosure'> = { 'fileNameToModuleId'|'googmodule'|'untyped'|'convertIndexImportShorthand'|
'transformDecorators'|'transformTypesToClosure'> = {
shouldSkipTsickleProcessing: (fileName) => /\.d\.ts$/.test(fileName) || shouldSkipTsickleProcessing: (fileName) => /\.d\.ts$/.test(fileName) ||
// View Engine's generated files were never intended to be processed with tsickle. // View Engine's generated files were never intended to be processed with tsickle.
(!options.enableIvy && GENERATED_FILES.test(fileName)), (!options.enableIvy && GENERATED_FILES.test(fileName)),
@ -111,7 +114,9 @@ function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|un
fileNameToModuleId: (fileName) => fileName, fileNameToModuleId: (fileName) => fileName,
googmodule: false, googmodule: false,
untyped: true, untyped: true,
convertIndexImportShorthand: false, transformDecorators, transformTypesToClosure, convertIndexImportShorthand: false,
transformDecorators,
transformTypesToClosure,
}; };
if (options.annotateForClosureCompiler || options.annotationsAs === 'static fields') { if (options.annotateForClosureCompiler || options.annotationsAs === 'static fields') {
@ -147,7 +152,9 @@ function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|un
} }
} }
export interface NgcParsedConfiguration extends ParsedConfiguration { watch?: boolean; } export interface NgcParsedConfiguration extends ParsedConfiguration {
watch?: boolean;
}
export function readNgcCommandLineAndConfiguration(args: string[]): NgcParsedConfiguration { export function readNgcCommandLineAndConfiguration(args: string[]): NgcParsedConfiguration {
const options: api.CompilerOptions = {}; const options: api.CompilerOptions = {};
@ -194,7 +201,8 @@ export function readCommandLineAndConfiguration(
} }
return { return {
project, project,
rootNames: config.rootNames, options, rootNames: config.rootNames,
options,
errors: config.errors, errors: config.errors,
emitFlags: config.emitFlags emitFlags: config.emitFlags
}; };
@ -237,7 +245,7 @@ export function watchMode(
function printDiagnostics( function printDiagnostics(
diagnostics: ReadonlyArray<ts.Diagnostic|api.Diagnostic>, diagnostics: ReadonlyArray<ts.Diagnostic|api.Diagnostic>,
options: api.CompilerOptions | undefined, consoleError: (s: string) => void): void { options: api.CompilerOptions|undefined, consoleError: (s: string) => void): void {
if (diagnostics.length === 0) { if (diagnostics.length === 0) {
return; return;
} }

View File

@ -47,8 +47,7 @@ function createSyntheticIndexHost<H extends ts.CompilerHost>(
newHost.writeFile = newHost.writeFile =
(fileName: string, data: string, writeByteOrderMark: boolean, (fileName: string, data: string, writeByteOrderMark: boolean,
onError: ((message: string) => void) | undefined, onError: ((message: string) => void)|undefined, sourceFiles: Readonly<ts.SourceFile>[]) => {
sourceFiles: Readonly<ts.SourceFile>[]) => {
delegate.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles); delegate.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles);
if (fileName.match(DTS) && sourceFiles && sourceFiles.length == 1 && if (fileName.match(DTS) && sourceFiles && sourceFiles.length == 1 &&
path.normalize(sourceFiles[0].fileName) === normalSyntheticIndexName) { path.normalize(sourceFiles[0].fileName) === normalSyntheticIndexName) {
@ -103,7 +102,7 @@ export function createBundleIndexHost<H extends ts.CompilerHost>(
// contents of the flat module index. The bundle produced during emit does use the metadata cache // contents of the flat module index. The bundle produced during emit does use the metadata cache
// with associated transforms, so the metadata will have lowered expressions, resource inlining, // with associated transforms, so the metadata will have lowered expressions, resource inlining,
// etc. // etc.
const getMetadataBundle = (cache: MetadataCache | null) => { const getMetadataBundle = (cache: MetadataCache|null) => {
const bundler = new MetadataBundler( const bundler = new MetadataBundler(
indexModule, ngOptions.flatModuleId, new CompilerHostAdapter(host, cache, ngOptions), indexModule, ngOptions.flatModuleId, new CompilerHostAdapter(host, cache, ngOptions),
ngOptions.flatModulePrivateSymbolPrefix); ngOptions.flatModulePrivateSymbolPrefix);
@ -113,7 +112,7 @@ export function createBundleIndexHost<H extends ts.CompilerHost>(
// First, produce the bundle with no MetadataCache. // First, produce the bundle with no MetadataCache.
const metadataBundle = getMetadataBundle(/* MetadataCache */ null); const metadataBundle = getMetadataBundle(/* MetadataCache */ null);
const name = const name =
path.join(path.dirname(indexModule), ngOptions.flatModuleOutFile !.replace(JS_EXT, '.ts')); path.join(path.dirname(indexModule), ngOptions.flatModuleOutFile!.replace(JS_EXT, '.ts'));
const libraryIndex = `./${path.basename(indexModule)}`; const libraryIndex = `./${path.basename(indexModule)}`;
const content = privateEntriesToIndex(libraryIndex, metadataBundle.privates); const content = privateEntriesToIndex(libraryIndex, metadataBundle.privates);

View File

@ -11,7 +11,7 @@ import * as ts from 'typescript';
import {MetadataCache} from '../transformers/metadata_cache'; import {MetadataCache} from '../transformers/metadata_cache';
import {MetadataCollector} from './collector'; import {MetadataCollector} from './collector';
import {ClassMetadata, ConstructorMetadata, FunctionMetadata, METADATA_VERSION, MemberMetadata, MetadataEntry, MetadataError, MetadataMap, MetadataObject, MetadataSymbolicExpression, MetadataSymbolicReferenceExpression, MetadataValue, MethodMetadata, ModuleExportMetadata, ModuleMetadata, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isInterfaceMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicCallExpression, isMetadataSymbolicExpression, isMethodMetadata} from './schema'; import {ClassMetadata, ConstructorMetadata, FunctionMetadata, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isInterfaceMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicCallExpression, isMetadataSymbolicExpression, isMethodMetadata, MemberMetadata, METADATA_VERSION, MetadataEntry, MetadataError, MetadataMap, MetadataObject, MetadataSymbolicExpression, MetadataSymbolicReferenceExpression, MetadataValue, MethodMetadata, ModuleExportMetadata, ModuleMetadata} from './schema';
@ -59,7 +59,9 @@ interface Symbol {
privateName?: string; privateName?: string;
} }
export interface BundleEntries { [name: string]: MetadataEntry; } export interface BundleEntries {
[name: string]: MetadataEntry;
}
export interface BundlePrivateEntry { export interface BundlePrivateEntry {
privateName: string; privateName: string;
@ -77,7 +79,7 @@ export interface MetadataBundlerHost {
} }
type StaticsMetadata = { type StaticsMetadata = {
[name: string]: MetadataValue | FunctionMetadata; [name: string]: MetadataValue|FunctionMetadata;
}; };
export class MetadataBundler { export class MetadataBundler {
@ -87,7 +89,7 @@ export class MetadataBundler {
private rootModule: string; private rootModule: string;
private privateSymbolPrefix: string; private privateSymbolPrefix: string;
// TODO(issue/24571): remove '!'. // TODO(issue/24571): remove '!'.
private exported !: Set<Symbol>; private exported!: Set<Symbol>;
constructor( constructor(
private root: string, private importAs: string|undefined, private host: MetadataBundlerHost, private root: string, private importAs: string|undefined, private host: MetadataBundlerHost,
@ -106,14 +108,14 @@ export class MetadataBundler {
const privates = Array.from(this.symbolMap.values()) const privates = Array.from(this.symbolMap.values())
.filter(s => s.referenced && s.isPrivate) .filter(s => s.referenced && s.isPrivate)
.map(s => ({ .map(s => ({
privateName: s.privateName !, privateName: s.privateName!,
name: s.declaration !.name, name: s.declaration!.name,
module: s.declaration !.module module: s.declaration!.module
})); }));
const origins = Array.from(this.symbolMap.values()) const origins = Array.from(this.symbolMap.values())
.filter(s => s.referenced && !s.reexport) .filter(s => s.referenced && !s.reexport)
.reduce<{[name: string]: string}>((p, s) => { .reduce<{[name: string]: string}>((p, s) => {
p[s.isPrivate ? s.privateName ! : s.name] = s.declaration !.module; p[s.isPrivate ? s.privateName! : s.name] = s.declaration!.module;
return p; return p;
}, {}); }, {});
const exports = this.getReExports(exportedSymbols); const exports = this.getReExports(exportedSymbols);
@ -121,8 +123,10 @@ export class MetadataBundler {
metadata: { metadata: {
__symbolic: 'module', __symbolic: 'module',
version: METADATA_VERSION, version: METADATA_VERSION,
exports: exports.length ? exports : undefined, metadata, origins, exports: exports.length ? exports : undefined,
importAs: this.importAs ! metadata,
origins,
importAs: this.importAs!
}, },
privates privates
}; };
@ -156,7 +160,7 @@ export class MetadataBundler {
const exportSymbol = (exportedSymbol: Symbol, exportAs: string) => { const exportSymbol = (exportedSymbol: Symbol, exportAs: string) => {
const symbol = this.symbolOf(moduleName, exportAs); const symbol = this.symbolOf(moduleName, exportAs);
result !.push(symbol); result!.push(symbol);
exportedSymbol.reexportedAs = symbol; exportedSymbol.reexportedAs = symbol;
symbol.exports = exportedSymbol; symbol.exports = exportedSymbol;
}; };
@ -276,18 +280,18 @@ export class MetadataBundler {
Array.from(this.symbolMap.values()).forEach(symbol => { Array.from(this.symbolMap.values()).forEach(symbol => {
if (symbol.referenced && !symbol.reexport) { if (symbol.referenced && !symbol.reexport) {
let name = symbol.name; let name = symbol.name;
const identifier = `${symbol.declaration!.module}:${symbol.declaration !.name}`; const identifier = `${symbol.declaration!.module}:${symbol.declaration!.name}`;
if (symbol.isPrivate && !symbol.privateName) { if (symbol.isPrivate && !symbol.privateName) {
name = newPrivateName(this.privateSymbolPrefix); name = newPrivateName(this.privateSymbolPrefix);
symbol.privateName = name; symbol.privateName = name;
} }
if (symbolsMap.has(identifier)) { if (symbolsMap.has(identifier)) {
const names = symbolsMap.get(identifier); const names = symbolsMap.get(identifier);
names !.push(name); names!.push(name);
} else { } else {
symbolsMap.set(identifier, [name]); symbolsMap.set(identifier, [name]);
} }
result[name] = symbol.value !; result[name] = symbol.value!;
} }
}); });
@ -320,9 +324,9 @@ export class MetadataBundler {
for (const symbol of exportedSymbols) { for (const symbol of exportedSymbols) {
if (symbol.reexport) { if (symbol.reexport) {
// symbol.declaration is guaranteed to be defined during the phase this method is called. // symbol.declaration is guaranteed to be defined during the phase this method is called.
const declaration = symbol.declaration !; const declaration = symbol.declaration!;
const module = declaration.module; const module = declaration.module;
if (declaration !.name == '*') { if (declaration!.name == '*') {
// Reexport all the symbols. // Reexport all the symbols.
exportAlls.add(declaration.module); exportAlls.add(declaration.module);
} else { } else {
@ -346,12 +350,12 @@ export class MetadataBundler {
private convertSymbol(symbol: Symbol) { private convertSymbol(symbol: Symbol) {
// canonicalSymbol is ensured to be defined before this is called. // canonicalSymbol is ensured to be defined before this is called.
const canonicalSymbol = symbol.canonicalSymbol !; const canonicalSymbol = symbol.canonicalSymbol!;
if (!canonicalSymbol.referenced) { if (!canonicalSymbol.referenced) {
canonicalSymbol.referenced = true; canonicalSymbol.referenced = true;
// declaration is ensured to be definded before this method is called. // declaration is ensured to be definded before this method is called.
const declaration = canonicalSymbol.declaration !; const declaration = canonicalSymbol.declaration!;
const module = this.getMetadata(declaration.module); const module = this.getMetadata(declaration.module);
if (module) { if (module) {
const value = module.metadata[declaration.name]; const value = module.metadata[declaration.name];
@ -399,11 +403,11 @@ export class MetadataBundler {
private convertMember(moduleName: string, member: MemberMetadata) { private convertMember(moduleName: string, member: MemberMetadata) {
const result: MemberMetadata = {__symbolic: member.__symbolic}; const result: MemberMetadata = {__symbolic: member.__symbolic};
result.decorators = result.decorators =
member.decorators && member.decorators.map(d => this.convertExpression(moduleName, d) !); member.decorators && member.decorators.map(d => this.convertExpression(moduleName, d)!);
if (isMethodMetadata(member)) { if (isMethodMetadata(member)) {
(result as MethodMetadata).parameterDecorators = member.parameterDecorators && (result as MethodMetadata).parameterDecorators = member.parameterDecorators &&
member.parameterDecorators.map( member.parameterDecorators.map(
d => d && d.map(p => this.convertExpression(moduleName, p) !)); d => d && d.map(p => this.convertExpression(moduleName, p)!));
if (isConstructorMetadata(member)) { if (isConstructorMetadata(member)) {
if (member.parameters) { if (member.parameters) {
(result as ConstructorMetadata).parameters = (result as ConstructorMetadata).parameters =
@ -450,7 +454,7 @@ export class MetadataBundler {
return this.convertError(moduleName, value); return this.convertError(moduleName, value);
} }
if (isMetadataSymbolicExpression(value)) { if (isMetadataSymbolicExpression(value)) {
return this.convertExpression(moduleName, value) !; return this.convertExpression(moduleName, value)!;
} }
if (Array.isArray(value)) { if (Array.isArray(value)) {
return value.map(v => this.convertValue(moduleName, v)); return value.map(v => this.convertValue(moduleName, v));
@ -466,8 +470,8 @@ export class MetadataBundler {
} }
private convertExpression( private convertExpression(
moduleName: string, value: MetadataSymbolicExpression|MetadataError|null| moduleName: string, value: MetadataSymbolicExpression|MetadataError|null|undefined):
undefined): MetadataSymbolicExpression|MetadataError|undefined|null { MetadataSymbolicExpression|MetadataError|undefined|null {
if (value) { if (value) {
switch (value.__symbolic) { switch (value.__symbolic) {
case 'error': case 'error':
@ -487,14 +491,15 @@ export class MetadataBundler {
message: value.message, message: value.message,
line: value.line, line: value.line,
character: value.character, character: value.character,
context: value.context, module context: value.context,
module
}; };
} }
private convertReference(moduleName: string, value: MetadataSymbolicReferenceExpression): private convertReference(moduleName: string, value: MetadataSymbolicReferenceExpression):
MetadataSymbolicReferenceExpression|MetadataError|undefined { MetadataSymbolicReferenceExpression|MetadataError|undefined {
const createReference = (symbol: Symbol): MetadataSymbolicReferenceExpression => { const createReference = (symbol: Symbol): MetadataSymbolicReferenceExpression => {
const declaration = symbol.declaration !; const declaration = symbol.declaration!;
if (declaration.module.startsWith('.')) { if (declaration.module.startsWith('.')) {
// Reference to a symbol defined in the module. Ensure it is converted then return a // Reference to a symbol defined in the module. Ensure it is converted then return a
// references to the final symbol. // references to the final symbol.
@ -503,11 +508,11 @@ export class MetadataBundler {
__symbolic: 'reference', __symbolic: 'reference',
get name() { get name() {
// Resolved lazily because private names are assigned late. // Resolved lazily because private names are assigned late.
const canonicalSymbol = symbol.canonicalSymbol !; const canonicalSymbol = symbol.canonicalSymbol!;
if (canonicalSymbol.isPrivate == null) { if (canonicalSymbol.isPrivate == null) {
throw Error('Invalid state: isPrivate was not initialized'); throw Error('Invalid state: isPrivate was not initialized');
} }
return canonicalSymbol.isPrivate ? canonicalSymbol.privateName ! : canonicalSymbol.name; return canonicalSymbol.isPrivate ? canonicalSymbol.privateName! : canonicalSymbol.name;
} }
}; };
} else { } else {
@ -584,7 +589,7 @@ export class MetadataBundler {
private convertExpressionNode(moduleName: string, value: MetadataSymbolicExpression): private convertExpressionNode(moduleName: string, value: MetadataSymbolicExpression):
MetadataSymbolicExpression { MetadataSymbolicExpression {
const result: MetadataSymbolicExpression = { __symbolic: value.__symbolic } as any; const result: MetadataSymbolicExpression = {__symbolic: value.__symbolic} as any;
for (const key in value) { for (const key in value) {
(result as any)[key] = this.convertValue(moduleName, (value as any)[key]); (result as any)[key] = this.convertValue(moduleName, (value as any)[key]);
} }

View File

@ -8,8 +8,8 @@
import * as ts from 'typescript'; import * as ts from 'typescript';
import {Evaluator, errorSymbol, recordMapEntry} from './evaluator'; import {errorSymbol, Evaluator, recordMapEntry} from './evaluator';
import {ClassMetadata, ConstructorMetadata, FunctionMetadata, InterfaceMetadata, METADATA_VERSION, MemberMetadata, MetadataEntry, MetadataError, MetadataMap, MetadataSymbolicBinaryExpression, MetadataSymbolicCallExpression, MetadataSymbolicExpression, MetadataSymbolicIfExpression, MetadataSymbolicIndexExpression, MetadataSymbolicPrefixExpression, MetadataSymbolicReferenceExpression, MetadataSymbolicSelectExpression, MetadataSymbolicSpreadExpression, MetadataValue, MethodMetadata, ModuleExportMetadata, ModuleMetadata, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportDefaultReference, isMetadataImportedSymbolReferenceExpression, isMetadataSymbolicExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSelectExpression, isMethodMetadata} from './schema'; import {ClassMetadata, ConstructorMetadata, FunctionMetadata, InterfaceMetadata, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportDefaultReference, isMetadataImportedSymbolReferenceExpression, isMetadataSymbolicExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSelectExpression, isMethodMetadata, MemberMetadata, METADATA_VERSION, MetadataEntry, MetadataError, MetadataMap, MetadataSymbolicBinaryExpression, MetadataSymbolicCallExpression, MetadataSymbolicExpression, MetadataSymbolicIfExpression, MetadataSymbolicIndexExpression, MetadataSymbolicPrefixExpression, MetadataSymbolicReferenceExpression, MetadataSymbolicSelectExpression, MetadataSymbolicSpreadExpression, MetadataValue, MethodMetadata, ModuleExportMetadata, ModuleMetadata} from './schema';
import {Symbols} from './symbols'; import {Symbols} from './symbols';
const isStatic = (node: ts.Declaration) => const isStatic = (node: ts.Declaration) =>
@ -60,12 +60,12 @@ export class MetadataCollector {
new Map<MetadataValue|ClassMetadata|InterfaceMetadata|FunctionMetadata, ts.Node>(); new Map<MetadataValue|ClassMetadata|InterfaceMetadata|FunctionMetadata, ts.Node>();
const composedSubstituter = substituteExpression && this.options.substituteExpression ? const composedSubstituter = substituteExpression && this.options.substituteExpression ?
(value: MetadataValue, node: ts.Node) => (value: MetadataValue, node: ts.Node) =>
this.options.substituteExpression !(substituteExpression(value, node), node) : this.options.substituteExpression!(substituteExpression(value, node), node) :
substituteExpression; substituteExpression;
const evaluatorOptions = substituteExpression ? const evaluatorOptions = substituteExpression ?
{...this.options, substituteExpression: composedSubstituter} : {...this.options, substituteExpression: composedSubstituter} :
this.options; this.options;
let metadata: {[name: string]: MetadataValue | ClassMetadata | FunctionMetadata}|undefined; let metadata: {[name: string]: MetadataValue|ClassMetadata|FunctionMetadata}|undefined;
const evaluator = new Evaluator(locals, nodeMap, evaluatorOptions, (name, value) => { const evaluator = new Evaluator(locals, nodeMap, evaluatorOptions, (name, value) => {
if (!metadata) metadata = {}; if (!metadata) metadata = {};
metadata[name] = value; metadata[name] = value;
@ -88,9 +88,9 @@ export class MetadataCollector {
return errorSymbol(message, node, context, sourceFile); return errorSymbol(message, node, context, sourceFile);
} }
function maybeGetSimpleFunction( function maybeGetSimpleFunction(functionDeclaration: ts.FunctionDeclaration|
functionDeclaration: ts.FunctionDeclaration | ts.MethodDeclaration): {func: FunctionMetadata, name: string}|
ts.MethodDeclaration): {func: FunctionMetadata, name: string}|undefined { undefined {
if (functionDeclaration.name && functionDeclaration.name.kind == ts.SyntaxKind.Identifier) { if (functionDeclaration.name && functionDeclaration.name.kind == ts.SyntaxKind.Identifier) {
const nameNode = <ts.Identifier>functionDeclaration.name; const nameNode = <ts.Identifier>functionDeclaration.name;
const functionName = nameNode.text; const functionName = nameNode.text;
@ -119,8 +119,8 @@ export class MetadataCollector {
function classMetadataOf(classDeclaration: ts.ClassDeclaration): ClassMetadata { function classMetadataOf(classDeclaration: ts.ClassDeclaration): ClassMetadata {
const result: ClassMetadata = {__symbolic: 'class'}; const result: ClassMetadata = {__symbolic: 'class'};
function getDecorators(decorators: ReadonlyArray<ts.Decorator>| undefined): function getDecorators(decorators: ReadonlyArray<ts.Decorator>|
MetadataSymbolicExpression[]|undefined { undefined): MetadataSymbolicExpression[]|undefined {
if (decorators && decorators.length) if (decorators && decorators.length)
return decorators.map(decorator => objFromDecorator(decorator)); return decorators.map(decorator => objFromDecorator(decorator));
return undefined; return undefined;
@ -167,8 +167,8 @@ export class MetadataCollector {
} }
// static member // static member
let statics: {[name: string]: MetadataValue | FunctionMetadata}|null = null; let statics: {[name: string]: MetadataValue|FunctionMetadata}|null = null;
function recordStaticMember(name: string, value: MetadataValue | FunctionMetadata) { function recordStaticMember(name: string, value: MetadataValue|FunctionMetadata) {
if (!statics) statics = {}; if (!statics) statics = {};
statics[name] = value; statics[name] = value;
} }
@ -189,11 +189,10 @@ export class MetadataCollector {
} }
const methodDecorators = getDecorators(method.decorators); const methodDecorators = getDecorators(method.decorators);
const parameters = method.parameters; const parameters = method.parameters;
const parameterDecoratorData: const parameterDecoratorData: ((MetadataSymbolicExpression | MetadataError)[]|
((MetadataSymbolicExpression | MetadataError)[] | undefined)[] = []; undefined)[] = [];
const parametersData: const parametersData: (MetadataSymbolicReferenceExpression|MetadataError|
(MetadataSymbolicReferenceExpression | MetadataError | MetadataSymbolicSelectExpression|null)[] = [];
MetadataSymbolicSelectExpression | null)[] = [];
let hasDecoratorData: boolean = false; let hasDecoratorData: boolean = false;
let hasParameterData: boolean = false; let hasParameterData: boolean = false;
for (const parameter of parameters) { for (const parameter of parameters) {
@ -282,15 +281,14 @@ export class MetadataCollector {
ts.getCombinedModifierFlags(node as ts.Declaration) & ts.ModifierFlags.Export; ts.getCombinedModifierFlags(node as ts.Declaration) & ts.ModifierFlags.Export;
const isExportedIdentifier = (identifier?: ts.Identifier) => const isExportedIdentifier = (identifier?: ts.Identifier) =>
identifier && exportMap.has(identifier.text); identifier && exportMap.has(identifier.text);
const isExported = const isExported = (node: ts.FunctionDeclaration|ts.ClassDeclaration|ts.TypeAliasDeclaration|
(node: ts.FunctionDeclaration | ts.ClassDeclaration | ts.TypeAliasDeclaration | ts.InterfaceDeclaration|ts.EnumDeclaration) =>
ts.InterfaceDeclaration | ts.EnumDeclaration) => isExport(node) || isExportedIdentifier(node.name);
isExport(node) || isExportedIdentifier(node.name);
const exportedIdentifierName = (identifier?: ts.Identifier) => const exportedIdentifierName = (identifier?: ts.Identifier) =>
identifier && (exportMap.get(identifier.text) || identifier.text); identifier && (exportMap.get(identifier.text) || identifier.text);
const exportedName = const exportedName = (node: ts.FunctionDeclaration|ts.ClassDeclaration|
(node: ts.FunctionDeclaration | ts.ClassDeclaration | ts.InterfaceDeclaration | ts.InterfaceDeclaration|ts.TypeAliasDeclaration|ts.EnumDeclaration) =>
ts.TypeAliasDeclaration | ts.EnumDeclaration) => exportedIdentifierName(node.name); exportedIdentifierName(node.name);
// Pre-declare classes and functions // Pre-declare classes and functions
@ -419,8 +417,8 @@ export class MetadataCollector {
if (name) { if (name) {
if (!metadata) metadata = {}; if (!metadata) metadata = {};
// TODO(alxhub): The literal here is not valid FunctionMetadata. // TODO(alxhub): The literal here is not valid FunctionMetadata.
metadata[name] = maybeFunc ? recordEntry(maybeFunc.func, node) : metadata[name] =
({ __symbolic: 'function' } as any); maybeFunc ? recordEntry(maybeFunc.func, node) : ({__symbolic: 'function'} as any);
} }
} }
break; break;
@ -456,7 +454,8 @@ export class MetadataCollector {
operator: '+', operator: '+',
left: { left: {
__symbolic: 'select', __symbolic: 'select',
expression: recordEntry({__symbolic: 'reference', name: enumName}, node), name expression: recordEntry({__symbolic: 'reference', name: enumName}, node),
name
}, },
} as any; } as any;
} else { } else {
@ -555,7 +554,8 @@ export class MetadataCollector {
} }
const result: ModuleMetadata = { const result: ModuleMetadata = {
__symbolic: 'module', __symbolic: 'module',
version: this.options.version || METADATA_VERSION, metadata version: this.options.version || METADATA_VERSION,
metadata
}; };
if (sourceFile.moduleName) result.importAs = sourceFile.moduleName; if (sourceFile.moduleName) result.importAs = sourceFile.moduleName;
if (exports) result.exports = exports; if (exports) result.exports = exports;
@ -570,8 +570,7 @@ function validateMetadata(
metadata: {[name: string]: MetadataEntry}) { metadata: {[name: string]: MetadataEntry}) {
let locals: Set<string> = new Set(['Array', 'Object', 'Set', 'Map', 'string', 'number', 'any']); let locals: Set<string> = new Set(['Array', 'Object', 'Set', 'Map', 'string', 'number', 'any']);
function validateExpression( function validateExpression(expression: MetadataValue|MetadataSymbolicExpression|MetadataError) {
expression: MetadataValue | MetadataSymbolicExpression | MetadataError) {
if (!expression) { if (!expression) {
return; return;
} else if (Array.isArray(expression)) { } else if (Array.isArray(expression)) {
@ -648,11 +647,11 @@ function validateMetadata(
} }
if (classData.members) { if (classData.members) {
Object.getOwnPropertyNames(classData.members) Object.getOwnPropertyNames(classData.members)
.forEach(name => classData.members ![name].forEach((m) => validateMember(classData, m))); .forEach(name => classData.members![name].forEach((m) => validateMember(classData, m)));
} }
if (classData.statics) { if (classData.statics) {
Object.getOwnPropertyNames(classData.statics).forEach(name => { Object.getOwnPropertyNames(classData.statics).forEach(name => {
const staticMember = classData.statics ![name]; const staticMember = classData.statics![name];
if (isFunctionMetadata(staticMember)) { if (isFunctionMetadata(staticMember)) {
validateExpression(staticMember.value); validateExpression(staticMember.value);
} else { } else {
@ -675,7 +674,7 @@ function validateMetadata(
} }
} }
function shouldReportNode(node: ts.Node | undefined) { function shouldReportNode(node: ts.Node|undefined) {
if (node) { if (node) {
const nodeStart = node.getStart(); const nodeStart = node.getStart();
return !( return !(
@ -688,12 +687,13 @@ function validateMetadata(
function reportError(error: MetadataError) { function reportError(error: MetadataError) {
const node = nodeMap.get(error); const node = nodeMap.get(error);
if (shouldReportNode(node)) { if (shouldReportNode(node)) {
const lineInfo = error.line != undefined ? const lineInfo = error.line != undefined ? error.character != undefined ?
error.character != undefined ? `:${error.line + 1}:${error.character + 1}` : `:${error.line + 1}:${error.character + 1}` :
`:${error.line + 1}` : `:${error.line + 1}` :
''; '';
throw new Error( throw new Error(`${sourceFile.fileName}${
`${sourceFile.fileName}${lineInfo}: Metadata collected contains an error that will be reported at runtime: ${expandedMessage(error)}.\n ${JSON.stringify(error)}`); lineInfo}: Metadata collected contains an error that will be reported at runtime: ${
expandedMessage(error)}.\n ${JSON.stringify(error)}`);
} }
} }
@ -708,8 +708,9 @@ function validateMetadata(
if (shouldReportNode(node)) { if (shouldReportNode(node)) {
if (node) { if (node) {
const {line, character} = sourceFile.getLineAndCharacterOfPosition(node.getStart()); const {line, character} = sourceFile.getLineAndCharacterOfPosition(node.getStart());
throw new Error( throw new Error(`${sourceFile.fileName}:${line + 1}:${
`${sourceFile.fileName}:${line + 1}:${character + 1}: Error encountered in metadata generated for exported symbol '${name}': \n ${e.message}`); character + 1}: Error encountered in metadata generated for exported symbol '${
name}': \n ${e.message}`);
} }
throw new Error( throw new Error(
`Error encountered in metadata generated for exported symbol ${name}: \n ${e.message}`); `Error encountered in metadata generated for exported symbol ${name}: \n ${e.message}`);
@ -722,7 +723,7 @@ function validateMetadata(
function namesOf(parameters: ts.NodeArray<ts.ParameterDeclaration>): string[] { function namesOf(parameters: ts.NodeArray<ts.ParameterDeclaration>): string[] {
const result: string[] = []; const result: string[] = [];
function addNamesOf(name: ts.Identifier | ts.BindingPattern) { function addNamesOf(name: ts.Identifier|ts.BindingPattern) {
if (name.kind == ts.SyntaxKind.Identifier) { if (name.kind == ts.SyntaxKind.Identifier) {
const identifier = <ts.Identifier>name; const identifier = <ts.Identifier>name;
result.push(identifier.text); result.push(identifier.text);
@ -752,7 +753,8 @@ function expandedMessage(error: any): string {
switch (error.message) { switch (error.message) {
case 'Reference to non-exported class': case 'Reference to non-exported class':
if (error.context && error.context.className) { if (error.context && error.context.className) {
return `Reference to a non-exported class ${error.context.className}. Consider exporting the class`; return `Reference to a non-exported class ${
error.context.className}. Consider exporting the class`;
} }
break; break;
case 'Variable not initialized': case 'Variable not initialized':
@ -771,7 +773,8 @@ function expandedMessage(error: any): string {
'unction calls are not supported. Consider replacing the function or lambda with a reference to an exported function'; 'unction calls are not supported. Consider replacing the function or lambda with a reference to an exported function';
case 'Reference to a local symbol': case 'Reference to a local symbol':
if (error.context && error.context.name) { if (error.context && error.context.name) {
return `Reference to a local (non-exported) symbol '${error.context.name}'. Consider exporting the symbol`; return `Reference to a local (non-exported) symbol '${
error.context.name}'. Consider exporting the symbol`;
} }
} }
return error.message; return error.message;

View File

@ -9,7 +9,7 @@
import * as ts from 'typescript'; import * as ts from 'typescript';
import {CollectorOptions} from './collector'; import {CollectorOptions} from './collector';
import {ClassMetadata, FunctionMetadata, InterfaceMetadata, MetadataEntry, MetadataError, MetadataImportedSymbolReferenceExpression, MetadataSourceLocationInfo, MetadataSymbolicCallExpression, MetadataValue, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportDefaultReference, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSpreadExpression} from './schema'; import {ClassMetadata, FunctionMetadata, InterfaceMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportDefaultReference, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSpreadExpression, MetadataEntry, MetadataError, MetadataImportedSymbolReferenceExpression, MetadataSourceLocationInfo, MetadataSymbolicCallExpression, MetadataValue} from './schema';
import {Symbols} from './symbols'; import {Symbols} from './symbols';
@ -46,8 +46,9 @@ export function recordMapEntry<T extends MetadataEntry>(
sourceFile?: ts.SourceFile) { sourceFile?: ts.SourceFile) {
if (!nodeMap.has(entry)) { if (!nodeMap.has(entry)) {
nodeMap.set(entry, node); nodeMap.set(entry, node);
if (node && (isMetadataImportedSymbolReferenceExpression(entry) || if (node &&
isMetadataImportDefaultReference(entry)) && (isMetadataImportedSymbolReferenceExpression(entry) ||
isMetadataImportDefaultReference(entry)) &&
entry.line == null) { entry.line == null) {
const info = sourceInfo(node, sourceFile); const info = sourceInfo(node, sourceFile);
if (info.line != null) entry.line = info.line; if (info.line != null) entry.line = info.line;
@ -88,7 +89,7 @@ export interface ImportMetadata {
} }
function getSourceFileOfNode(node: ts.Node | undefined): ts.SourceFile { function getSourceFileOfNode(node: ts.Node|undefined): ts.SourceFile {
while (node && node.kind != ts.SyntaxKind.SourceFile) { while (node && node.kind != ts.SyntaxKind.SourceFile) {
node = node.parent; node = node.parent;
} }
@ -97,7 +98,7 @@ function getSourceFileOfNode(node: ts.Node | undefined): ts.SourceFile {
/* @internal */ /* @internal */
export function sourceInfo( export function sourceInfo(
node: ts.Node | undefined, sourceFile: ts.SourceFile | undefined): MetadataSourceLocationInfo { node: ts.Node|undefined, sourceFile: ts.SourceFile|undefined): MetadataSourceLocationInfo {
if (node) { if (node) {
sourceFile = sourceFile || getSourceFileOfNode(node); sourceFile = sourceFile || getSourceFileOfNode(node);
if (sourceFile) { if (sourceFile) {
@ -435,7 +436,7 @@ export class Evaluator {
case ts.SyntaxKind.TypeReference: case ts.SyntaxKind.TypeReference:
const typeReferenceNode = <ts.TypeReferenceNode>node; const typeReferenceNode = <ts.TypeReferenceNode>node;
const typeNameNode = typeReferenceNode.typeName; const typeNameNode = typeReferenceNode.typeName;
const getReference: (typeNameNode: ts.Identifier | ts.QualifiedName) => MetadataValue = const getReference: (typeNameNode: ts.Identifier|ts.QualifiedName) => MetadataValue =
node => { node => {
if (typeNameNode.kind === ts.SyntaxKind.QualifiedName) { if (typeNameNode.kind === ts.SyntaxKind.QualifiedName) {
const qualifiedName = <ts.QualifiedName>node; const qualifiedName = <ts.QualifiedName>node;
@ -691,6 +692,6 @@ function isPropertyAssignment(node: ts.Node): node is ts.PropertyAssignment {
const empty = ts.createNodeArray<any>(); const empty = ts.createNodeArray<any>();
function arrayOrEmpty<T extends ts.Node>(v: ts.NodeArray<T>| undefined): ts.NodeArray<T> { function arrayOrEmpty<T extends ts.Node>(v: ts.NodeArray<T>|undefined): ts.NodeArray<T> {
return v || empty; return v || empty;
} }

View File

@ -18,7 +18,7 @@
export const METADATA_VERSION = 4; export const METADATA_VERSION = 4;
export type MetadataEntry = ClassMetadata | InterfaceMetadata | FunctionMetadata | MetadataValue; export type MetadataEntry = ClassMetadata|InterfaceMetadata|FunctionMetadata|MetadataValue;
export interface ModuleMetadata { export interface ModuleMetadata {
__symbolic: 'module'; __symbolic: 'module';
@ -43,18 +43,22 @@ export interface ClassMetadata {
arity?: number; arity?: number;
decorators?: (MetadataSymbolicExpression|MetadataError)[]; decorators?: (MetadataSymbolicExpression|MetadataError)[];
members?: MetadataMap; members?: MetadataMap;
statics?: {[name: string]: MetadataValue | FunctionMetadata}; statics?: {[name: string]: MetadataValue|FunctionMetadata};
} }
export function isClassMetadata(value: any): value is ClassMetadata { export function isClassMetadata(value: any): value is ClassMetadata {
return value && value.__symbolic === 'class'; return value && value.__symbolic === 'class';
} }
export interface InterfaceMetadata { __symbolic: 'interface'; } export interface InterfaceMetadata {
__symbolic: 'interface';
}
export function isInterfaceMetadata(value: any): value is InterfaceMetadata { export function isInterfaceMetadata(value: any): value is InterfaceMetadata {
return value && value.__symbolic === 'interface'; return value && value.__symbolic === 'interface';
} }
export interface MetadataMap { [name: string]: MemberMetadata[]; } export interface MetadataMap {
[name: string]: MemberMetadata[];
}
export interface MemberMetadata { export interface MemberMetadata {
__symbolic: 'constructor'|'method'|'property'; __symbolic: 'constructor'|'method'|'property';
@ -99,24 +103,26 @@ export function isFunctionMetadata(value: any): value is FunctionMetadata {
return value && value.__symbolic === 'function'; return value && value.__symbolic === 'function';
} }
export type MetadataValue = string | number | boolean | undefined | null | MetadataObject | export type MetadataValue = string|number|boolean|undefined|null|MetadataObject|MetadataArray|
MetadataArray | MetadataSymbolicExpression | MetadataSymbolicReferenceExpression | MetadataSymbolicExpression|MetadataSymbolicReferenceExpression|MetadataSymbolicBinaryExpression|
MetadataSymbolicBinaryExpression | MetadataSymbolicIndexExpression | MetadataSymbolicIndexExpression|MetadataSymbolicCallExpression|MetadataSymbolicPrefixExpression|
MetadataSymbolicCallExpression | MetadataSymbolicPrefixExpression | MetadataSymbolicIfExpression|MetadataSymbolicSpreadExpression|MetadataSymbolicSelectExpression|
MetadataSymbolicIfExpression | MetadataSymbolicSpreadExpression | MetadataError;
MetadataSymbolicSelectExpression | MetadataError;
export interface MetadataObject { [name: string]: MetadataValue; } export interface MetadataObject {
[name: string]: MetadataValue;
}
export interface MetadataArray { [name: number]: MetadataValue; } export interface MetadataArray {
[name: number]: MetadataValue;
}
export type MetadataSymbolicExpression = MetadataSymbolicBinaryExpression | export type MetadataSymbolicExpression = MetadataSymbolicBinaryExpression|
MetadataSymbolicIndexExpression | MetadataSymbolicIndexExpression | MetadataSymbolicIndexExpression|MetadataSymbolicIndexExpression|MetadataSymbolicCallExpression|
MetadataSymbolicCallExpression | MetadataSymbolicCallExpression | MetadataSymbolicCallExpression|MetadataSymbolicPrefixExpression|MetadataSymbolicIfExpression|
MetadataSymbolicPrefixExpression | MetadataSymbolicIfExpression | MetadataGlobalReferenceExpression|MetadataModuleReferenceExpression|
MetadataGlobalReferenceExpression | MetadataModuleReferenceExpression | MetadataImportedSymbolReferenceExpression|MetadataImportedDefaultReferenceExpression|
MetadataImportedSymbolReferenceExpression | MetadataImportedDefaultReferenceExpression | MetadataSymbolicSelectExpression|MetadataSymbolicSpreadExpression;
MetadataSymbolicSelectExpression | MetadataSymbolicSpreadExpression;
export function isMetadataSymbolicExpression(value: any): value is MetadataSymbolicExpression { export function isMetadataSymbolicExpression(value: any): value is MetadataSymbolicExpression {
if (value) { if (value) {
@ -234,18 +240,17 @@ export function isMetadataImportedSymbolReferenceExpression(value: any):
export interface MetadataImportedDefaultReferenceExpression extends MetadataSourceLocationInfo { export interface MetadataImportedDefaultReferenceExpression extends MetadataSourceLocationInfo {
__symbolic: 'reference'; __symbolic: 'reference';
module: string; module: string;
default: default: boolean;
boolean; arguments?: MetadataValue[];
arguments?: MetadataValue[];
} }
export function isMetadataImportDefaultReference(value: any): export function isMetadataImportDefaultReference(value: any):
value is MetadataImportedDefaultReferenceExpression { value is MetadataImportedDefaultReferenceExpression {
return value && value.module && value.default && isMetadataSymbolicReferenceExpression(value); return value && value.module && value.default && isMetadataSymbolicReferenceExpression(value);
} }
export type MetadataSymbolicReferenceExpression = MetadataGlobalReferenceExpression | export type MetadataSymbolicReferenceExpression =
MetadataModuleReferenceExpression | MetadataImportedSymbolReferenceExpression | MetadataGlobalReferenceExpression|MetadataModuleReferenceExpression|
MetadataImportedDefaultReferenceExpression; MetadataImportedSymbolReferenceExpression|MetadataImportedDefaultReferenceExpression;
export function isMetadataSymbolicReferenceExpression(value: any): export function isMetadataSymbolicReferenceExpression(value: any):
value is MetadataSymbolicReferenceExpression { value is MetadataSymbolicReferenceExpression {
return value && value.__symbolic === 'reference'; return value && value.__symbolic === 'reference';

View File

@ -12,7 +12,7 @@ import {MetadataSymbolicReferenceExpression, MetadataValue} from './schema';
export class Symbols { export class Symbols {
// TODO(issue/24571): remove '!'. // TODO(issue/24571): remove '!'.
private _symbols !: Map<string, MetadataValue>; private _symbols!: Map<string, MetadataValue>;
private references = new Map<string, MetadataSymbolicReferenceExpression>(); private references = new Map<string, MetadataSymbolicReferenceExpression>();
constructor(private sourceFile: ts.SourceFile) {} constructor(private sourceFile: ts.SourceFile) {}
@ -21,12 +21,16 @@ export class Symbols {
return (preferReference && this.references.get(name)) || this.symbols.get(name); return (preferReference && this.references.get(name)) || this.symbols.get(name);
} }
define(name: string, value: MetadataValue) { this.symbols.set(name, value); } define(name: string, value: MetadataValue) {
this.symbols.set(name, value);
}
defineReference(name: string, value: MetadataSymbolicReferenceExpression) { defineReference(name: string, value: MetadataSymbolicReferenceExpression) {
this.references.set(name, value); this.references.set(name, value);
} }
has(name: string): boolean { return this.symbols.has(name); } has(name: string): boolean {
return this.symbols.has(name);
}
private get symbols(): Map<string, MetadataValue> { private get symbols(): Map<string, MetadataValue> {
let result = this._symbols; let result = this._symbols;

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DomElementSchemaRegistry, Expression, ExternalExpr, Identifiers, InterpolationConfig, LexerRange, ParseError, ParseSourceFile, ParseTemplateOptions, R3ComponentMetadata, R3FactoryTarget, R3TargetBinder, SchemaMetadata, SelectorMatcher, Statement, TmplAstNode, WrappedNodeExpr, compileComponentFromMetadata, makeBindingParser, parseTemplate} from '@angular/compiler'; import {compileComponentFromMetadata, ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DomElementSchemaRegistry, Expression, ExternalExpr, Identifiers, InterpolationConfig, LexerRange, makeBindingParser, ParseError, ParseSourceFile, parseTemplate, ParseTemplateOptions, R3ComponentMetadata, R3FactoryTarget, R3TargetBinder, SchemaMetadata, SelectorMatcher, Statement, TmplAstNode, WrappedNodeExpr} from '@angular/compiler';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {CycleAnalyzer} from '../../cycles'; import {CycleAnalyzer} from '../../cycles';
@ -15,7 +15,7 @@ import {absoluteFrom, relative} from '../../file_system';
import {DefaultImportRecorder, ModuleResolver, Reference, ReferenceEmitter} from '../../imports'; import {DefaultImportRecorder, ModuleResolver, Reference, ReferenceEmitter} from '../../imports';
import {DependencyTracker} from '../../incremental/api'; import {DependencyTracker} from '../../incremental/api';
import {IndexingContext} from '../../indexer'; import {IndexingContext} from '../../indexer';
import {DirectiveMeta, InjectableClassRegistry, MetadataReader, MetadataRegistry, extractDirectiveGuards} from '../../metadata'; import {DirectiveMeta, extractDirectiveGuards, InjectableClassRegistry, MetadataReader, MetadataRegistry} from '../../metadata';
import {flattenInheritedDirectiveMetadata} from '../../metadata/src/inheritance'; import {flattenInheritedDirectiveMetadata} from '../../metadata/src/inheritance';
import {EnumValue, PartialEvaluator} from '../../partial_evaluator'; import {EnumValue, PartialEvaluator} from '../../partial_evaluator';
import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection'; import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection';
@ -200,7 +200,7 @@ export class ComponentDecoratorHandler implements
} else { } else {
return previous; return previous;
} }
}, undefined) !; }, undefined)!;
// Note that we could technically combine the `viewProvidersRequiringFactory` and // Note that we could technically combine the `viewProvidersRequiringFactory` and
@ -211,7 +211,7 @@ export class ComponentDecoratorHandler implements
let wrappedViewProviders: Expression|null = null; let wrappedViewProviders: Expression|null = null;
if (component.has('viewProviders')) { if (component.has('viewProviders')) {
const viewProviders = component.get('viewProviders') !; const viewProviders = component.get('viewProviders')!;
viewProvidersRequiringFactory = viewProvidersRequiringFactory =
resolveProvidersRequiringFactory(viewProviders, this.reflector, this.evaluator); resolveProvidersRequiringFactory(viewProviders, this.reflector, this.evaluator);
wrappedViewProviders = new WrappedNodeExpr( wrappedViewProviders = new WrappedNodeExpr(
@ -221,7 +221,7 @@ export class ComponentDecoratorHandler implements
if (component.has('providers')) { if (component.has('providers')) {
providersRequiringFactory = resolveProvidersRequiringFactory( providersRequiringFactory = resolveProvidersRequiringFactory(
component.get('providers') !, this.reflector, this.evaluator); component.get('providers')!, this.reflector, this.evaluator);
} }
// Parse the template. // Parse the template.
@ -232,14 +232,14 @@ export class ComponentDecoratorHandler implements
let template: ParsedTemplateWithSource; let template: ParsedTemplateWithSource;
if (this.preanalyzeTemplateCache.has(node)) { if (this.preanalyzeTemplateCache.has(node)) {
// The template was parsed in preanalyze. Use it and delete it to save memory. // The template was parsed in preanalyze. Use it and delete it to save memory.
const preanalyzed = this.preanalyzeTemplateCache.get(node) !; const preanalyzed = this.preanalyzeTemplateCache.get(node)!;
this.preanalyzeTemplateCache.delete(node); this.preanalyzeTemplateCache.delete(node);
template = preanalyzed; template = preanalyzed;
} else { } else {
// The template was not already parsed. Either there's a templateUrl, or an inline template. // The template was not already parsed. Either there's a templateUrl, or an inline template.
if (component.has('templateUrl')) { if (component.has('templateUrl')) {
const templateUrlExpr = component.get('templateUrl') !; const templateUrlExpr = component.get('templateUrl')!;
const templateUrl = this.evaluator.evaluate(templateUrlExpr); const templateUrl = this.evaluator.evaluate(templateUrlExpr);
if (typeof templateUrl !== 'string') { if (typeof templateUrl !== 'string') {
throw new FatalDiagnosticError( throw new FatalDiagnosticError(
@ -303,7 +303,7 @@ export class ComponentDecoratorHandler implements
let animations: Expression|null = null; let animations: Expression|null = null;
if (component.has('animations')) { if (component.has('animations')) {
animations = new WrappedNodeExpr(component.get('animations') !); animations = new WrappedNodeExpr(component.get('animations')!);
} }
const output: AnalysisOutput<ComponentAnalysisData> = { const output: AnalysisOutput<ComponentAnalysisData> = {
@ -323,7 +323,8 @@ export class ComponentDecoratorHandler implements
// analyzed and the full compilation scope for the component can be realized. // analyzed and the full compilation scope for the component can be realized.
animations, animations,
viewProviders: wrappedViewProviders, viewProviders: wrappedViewProviders,
i18nUseExternalIds: this.i18nUseExternalIds, relativeContextFilePath, i18nUseExternalIds: this.i18nUseExternalIds,
relativeContextFilePath,
}, },
guards: extractDirectiveGuards(node, this.reflector), guards: extractDirectiveGuards(node, this.reflector),
metadataStmt: generateSetClassMetadataCall( metadataStmt: generateSetClassMetadataCall(
@ -335,7 +336,7 @@ export class ComponentDecoratorHandler implements
}, },
}; };
if (changeDetection !== null) { if (changeDetection !== null) {
output.analysis !.meta.changeDetection = changeDetection; output.analysis!.meta.changeDetection = changeDetection;
} }
return output; return output;
} }
@ -353,7 +354,8 @@ export class ComponentDecoratorHandler implements
outputs: analysis.meta.outputs, outputs: analysis.meta.outputs,
queries: analysis.meta.queries.map(query => query.propertyName), queries: analysis.meta.queries.map(query => query.propertyName),
isComponent: true, isComponent: true,
baseClass: analysis.baseClass, ...analysis.guards, baseClass: analysis.baseClass,
...analysis.guards,
}); });
this.injectableRegistry.registerInjectable(node); this.injectableRegistry.registerInjectable(node);
@ -415,8 +417,8 @@ export class ComponentDecoratorHandler implements
} }
for (const {name, ref} of scope.compilation.pipes) { for (const {name, ref} of scope.compilation.pipes) {
if (!ts.isClassDeclaration(ref.node)) { if (!ts.isClassDeclaration(ref.node)) {
throw new Error( throw new Error(`Unexpected non-class declaration ${
`Unexpected non-class declaration ${ts.SyntaxKind[ref.node.kind]} for pipe ${ref.debugName}`); ts.SyntaxKind[ref.node.kind]} for pipe ${ref.debugName}`);
} }
pipes.set(name, ref as Reference<ClassDeclaration<ts.ClassDeclaration>>); pipes.set(name, ref as Reference<ClassDeclaration<ts.ClassDeclaration>>);
} }
@ -491,7 +493,7 @@ export class ComponentDecoratorHandler implements
// The BoundTarget knows which directives and pipes matched the template. // The BoundTarget knows which directives and pipes matched the template.
const usedDirectives = bound.getUsedDirectives(); const usedDirectives = bound.getUsedDirectives();
const usedPipes = bound.getUsedPipes().map(name => pipes.get(name) !); const usedPipes = bound.getUsedPipes().map(name => pipes.get(name)!);
// Scan through the directives/pipes actually used in the template and check whether any // Scan through the directives/pipes actually used in the template and check whether any
// import which needs to be generated would create a cycle. // import which needs to be generated would create a cycle.
@ -539,7 +541,7 @@ export class ComponentDecoratorHandler implements
if (analysis.providersRequiringFactory !== null && if (analysis.providersRequiringFactory !== null &&
analysis.meta.providers instanceof WrappedNodeExpr) { analysis.meta.providers instanceof WrappedNodeExpr) {
const providerDiagnostics = getProviderDiagnostics( const providerDiagnostics = getProviderDiagnostics(
analysis.providersRequiringFactory, analysis.meta.providers !.node, analysis.providersRequiringFactory, analysis.meta.providers!.node,
this.injectableRegistry); this.injectableRegistry);
diagnostics.push(...providerDiagnostics); diagnostics.push(...providerDiagnostics);
} }
@ -547,7 +549,7 @@ export class ComponentDecoratorHandler implements
if (analysis.viewProvidersRequiringFactory !== null && if (analysis.viewProvidersRequiringFactory !== null &&
analysis.meta.viewProviders instanceof WrappedNodeExpr) { analysis.meta.viewProviders instanceof WrappedNodeExpr) {
const viewProviderDiagnostics = getProviderDiagnostics( const viewProviderDiagnostics = getProviderDiagnostics(
analysis.viewProvidersRequiringFactory, analysis.meta.viewProviders !.node, analysis.viewProvidersRequiringFactory, analysis.meta.viewProviders!.node,
this.injectableRegistry); this.injectableRegistry);
diagnostics.push(...viewProviderDiagnostics); diagnostics.push(...viewProviderDiagnostics);
} }
@ -587,7 +589,7 @@ export class ComponentDecoratorHandler implements
private _resolveLiteral(decorator: Decorator): ts.ObjectLiteralExpression { private _resolveLiteral(decorator: Decorator): ts.ObjectLiteralExpression {
if (this.literalCache.has(decorator)) { if (this.literalCache.has(decorator)) {
return this.literalCache.get(decorator) !; return this.literalCache.get(decorator)!;
} }
if (decorator.args === null || decorator.args.length !== 1) { if (decorator.args === null || decorator.args.length !== 1) {
throw new FatalDiagnosticError( throw new FatalDiagnosticError(
@ -609,7 +611,7 @@ export class ComponentDecoratorHandler implements
component: Map<string, ts.Expression>, field: string, enumSymbolName: string): number|null { component: Map<string, ts.Expression>, field: string, enumSymbolName: string): number|null {
let resolved: number|null = null; let resolved: number|null = null;
if (component.has(field)) { if (component.has(field)) {
const expr = component.get(field) !; const expr = component.get(field)!;
const value = this.evaluator.evaluate(expr) as any; const value = this.evaluator.evaluate(expr) as any;
if (value instanceof EnumValue && isAngularCoreReference(value.enumRef, enumSymbolName)) { if (value instanceof EnumValue && isAngularCoreReference(value.enumRef, enumSymbolName)) {
resolved = value.resolved as number; resolved = value.resolved as number;
@ -628,7 +630,7 @@ export class ComponentDecoratorHandler implements
return extraUrls.length > 0 ? extraUrls : null; return extraUrls.length > 0 ? extraUrls : null;
} }
const styleUrlsExpr = component.get('styleUrls') !; const styleUrlsExpr = component.get('styleUrls')!;
const styleUrls = this.evaluator.evaluate(styleUrlsExpr); const styleUrls = this.evaluator.evaluate(styleUrlsExpr);
if (!Array.isArray(styleUrls) || !styleUrls.every(url => typeof url === 'string')) { if (!Array.isArray(styleUrls) || !styleUrls.every(url => typeof url === 'string')) {
throw new FatalDiagnosticError( throw new FatalDiagnosticError(
@ -643,7 +645,7 @@ export class ComponentDecoratorHandler implements
containingFile: string): Promise<ParsedTemplate|null> { containingFile: string): Promise<ParsedTemplate|null> {
if (component.has('templateUrl')) { if (component.has('templateUrl')) {
// Extract the templateUrl and preload it. // Extract the templateUrl and preload it.
const templateUrlExpr = component.get('templateUrl') !; const templateUrlExpr = component.get('templateUrl')!;
const templateUrl = this.evaluator.evaluate(templateUrlExpr); const templateUrl = this.evaluator.evaluate(templateUrlExpr);
if (typeof templateUrl !== 'string') { if (typeof templateUrl !== 'string') {
throw new FatalDiagnosticError( throw new FatalDiagnosticError(
@ -703,7 +705,7 @@ export class ComponentDecoratorHandler implements
ErrorCode.COMPONENT_MISSING_TEMPLATE, Decorator.nodeForError(decorator), ErrorCode.COMPONENT_MISSING_TEMPLATE, Decorator.nodeForError(decorator),
'component is missing a template'); 'component is missing a template');
} }
const templateExpr = component.get('template') !; const templateExpr = component.get('template')!;
let templateStr: string; let templateStr: string;
let templateUrl: string = ''; let templateUrl: string = '';
@ -721,7 +723,7 @@ export class ComponentDecoratorHandler implements
escapedString = true; escapedString = true;
sourceMapping = { sourceMapping = {
type: 'direct', type: 'direct',
node: templateExpr as(ts.StringLiteral | ts.NoSubstitutionTemplateLiteral), node: templateExpr as (ts.StringLiteral | ts.NoSubstitutionTemplateLiteral),
}; };
} else { } else {
const resolvedTemplate = this.evaluator.evaluate(templateExpr); const resolvedTemplate = this.evaluator.evaluate(templateExpr);
@ -749,7 +751,7 @@ export class ComponentDecoratorHandler implements
templateRange: LexerRange|undefined, escapedString: boolean): ParsedTemplate { templateRange: LexerRange|undefined, escapedString: boolean): ParsedTemplate {
let preserveWhitespaces: boolean = this.defaultPreserveWhitespaces; let preserveWhitespaces: boolean = this.defaultPreserveWhitespaces;
if (component.has('preserveWhitespaces')) { if (component.has('preserveWhitespaces')) {
const expr = component.get('preserveWhitespaces') !; const expr = component.get('preserveWhitespaces')!;
const value = this.evaluator.evaluate(expr); const value = this.evaluator.evaluate(expr);
if (typeof value !== 'boolean') { if (typeof value !== 'boolean') {
throw new FatalDiagnosticError( throw new FatalDiagnosticError(
@ -760,7 +762,7 @@ export class ComponentDecoratorHandler implements
let interpolation: InterpolationConfig = DEFAULT_INTERPOLATION_CONFIG; let interpolation: InterpolationConfig = DEFAULT_INTERPOLATION_CONFIG;
if (component.has('interpolation')) { if (component.has('interpolation')) {
const expr = component.get('interpolation') !; const expr = component.get('interpolation')!;
const value = this.evaluator.evaluate(expr); const value = this.evaluator.evaluate(expr);
if (!Array.isArray(value) || value.length !== 2 || if (!Array.isArray(value) || value.length !== 2 ||
!value.every(element => typeof element === 'string')) { !value.every(element => typeof element === 'string')) {
@ -768,14 +770,15 @@ export class ComponentDecoratorHandler implements
ErrorCode.VALUE_HAS_WRONG_TYPE, expr, ErrorCode.VALUE_HAS_WRONG_TYPE, expr,
'interpolation must be an array with 2 elements of string type'); 'interpolation must be an array with 2 elements of string type');
} }
interpolation = InterpolationConfig.fromArray(value as[string, string]); interpolation = InterpolationConfig.fromArray(value as [string, string]);
} }
const {errors, nodes: emitNodes, styleUrls, styles, ngContentSelectors} = const {errors, nodes: emitNodes, styleUrls, styles, ngContentSelectors} =
parseTemplate(templateStr, templateUrl, { parseTemplate(templateStr, templateUrl, {
preserveWhitespaces, preserveWhitespaces,
interpolationConfig: interpolation, interpolationConfig: interpolation,
range: templateRange, escapedString, range: templateRange,
escapedString,
enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat, enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
}); });
@ -795,7 +798,8 @@ export class ComponentDecoratorHandler implements
const {nodes: diagNodes} = parseTemplate(templateStr, templateUrl, { const {nodes: diagNodes} = parseTemplate(templateStr, templateUrl, {
preserveWhitespaces: true, preserveWhitespaces: true,
interpolationConfig: interpolation, interpolationConfig: interpolation,
range: templateRange, escapedString, range: templateRange,
escapedString,
enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat, enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
leadingTriviaChars: [], leadingTriviaChars: [],
}); });
@ -808,7 +812,8 @@ export class ComponentDecoratorHandler implements
styles, styles,
ngContentSelectors, ngContentSelectors,
errors, errors,
template: templateStr, templateUrl, template: templateStr,
templateUrl,
isInline: component.has('template'), isInline: component.has('template'),
file: new ParseSourceFile(templateStr, templateUrl), file: new ParseSourceFile(templateStr, templateUrl),
}; };
@ -820,7 +825,7 @@ export class ComponentDecoratorHandler implements
} }
// Figure out what file is being imported. // Figure out what file is being imported.
return this.moduleResolver.resolveModule(expr.value.moduleName !, origin.fileName); return this.moduleResolver.resolveModule(expr.value.moduleName!, origin.fileName);
} }
private _isCyclicImport(expr: Expression, origin: ts.SourceFile): boolean { private _isCyclicImport(expr: Expression, origin: ts.SourceFile): boolean {

View File

@ -36,9 +36,13 @@ export function getProviderDiagnostics(
const contextNode = provider.getOriginForDiagnostics(providersDeclaration); const contextNode = provider.getOriginForDiagnostics(providersDeclaration);
diagnostics.push(makeDiagnostic( diagnostics.push(makeDiagnostic(
ErrorCode.UNDECORATED_PROVIDER, contextNode, ErrorCode.UNDECORATED_PROVIDER, contextNode,
`The class '${provider.node.name.text}' cannot be created via dependency injection, as it does not have an Angular decorator. This will result in an error at runtime. `The class '${
provider.node.name
.text}' cannot be created via dependency injection, as it does not have an Angular decorator. This will result in an error at runtime.
Either add the @Injectable() decorator to '${provider.node.name.text}', or configure a different provider (such as a provider with 'useFactory'). Either add the @Injectable() decorator to '${
provider.node.name
.text}', or configure a different provider (such as a provider with 'useFactory').
`, `,
[{node: provider.node, messageText: `'${provider.node.name.text}' is declared here.`}])); [{node: provider.node, messageText: `'${provider.node.name.text}' is declared here.`}]));
} }
@ -52,7 +56,7 @@ export function getDirectiveDiagnostics(
kind: string): ts.Diagnostic[]|null { kind: string): ts.Diagnostic[]|null {
let diagnostics: ts.Diagnostic[]|null = []; let diagnostics: ts.Diagnostic[]|null = [];
const addDiagnostics = (more: ts.Diagnostic | ts.Diagnostic[] | null) => { const addDiagnostics = (more: ts.Diagnostic|ts.Diagnostic[]|null) => {
if (more === null) { if (more === null) {
return; return;
} else if (diagnostics === null) { } else if (diagnostics === null) {
@ -121,14 +125,16 @@ export function checkInheritanceOfDirective(
function getInheritedUndecoratedCtorDiagnostic( function getInheritedUndecoratedCtorDiagnostic(
node: ClassDeclaration, baseClass: Reference, reader: MetadataReader) { node: ClassDeclaration, baseClass: Reference, reader: MetadataReader) {
const subclassMeta = reader.getDirectiveMetadata(new Reference(node)) !; const subclassMeta = reader.getDirectiveMetadata(new Reference(node))!;
const dirOrComp = subclassMeta.isComponent ? 'Component' : 'Directive'; const dirOrComp = subclassMeta.isComponent ? 'Component' : 'Directive';
const baseClassName = baseClass.debugName; const baseClassName = baseClass.debugName;
return makeDiagnostic( return makeDiagnostic(
ErrorCode.DIRECTIVE_INHERITS_UNDECORATED_CTOR, node.name, ErrorCode.DIRECTIVE_INHERITS_UNDECORATED_CTOR, node.name,
`The ${dirOrComp.toLowerCase()} ${node.name.text} inherits its constructor from ${baseClassName}, ` + `The ${dirOrComp.toLowerCase()} ${node.name.text} inherits its constructor from ${
baseClassName}, ` +
`but the latter does not have an Angular decorator of its own. Dependency injection will not be able to ` + `but the latter does not have an Angular decorator of its own. Dependency injection will not be able to ` +
`resolve the parameters of ${baseClassName}'s constructor. Either add a @Directive decorator ` + `resolve the parameters of ${
baseClassName}'s constructor. Either add a @Directive decorator ` +
`to ${baseClassName}, or add an explicit constructor to ${node.name.text}.`); `to ${baseClassName}, or add an explicit constructor to ${node.name.text}.`);
} }

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {ConstantPool, Expression, Identifiers, ParseError, ParsedHostBindings, R3DependencyMetadata, R3DirectiveMetadata, R3FactoryTarget, R3QueryMetadata, Statement, WrappedNodeExpr, compileDirectiveFromMetadata, makeBindingParser, parseHostBindings, verifyHostBindings} from '@angular/compiler'; import {compileDirectiveFromMetadata, ConstantPool, Expression, Identifiers, makeBindingParser, ParsedHostBindings, ParseError, parseHostBindings, R3DependencyMetadata, R3DirectiveMetadata, R3FactoryTarget, R3QueryMetadata, Statement, verifyHostBindings, WrappedNodeExpr} from '@angular/compiler';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
@ -14,7 +14,7 @@ import {DefaultImportRecorder, Reference} from '../../imports';
import {InjectableClassRegistry, MetadataReader, MetadataRegistry} from '../../metadata'; import {InjectableClassRegistry, MetadataReader, MetadataRegistry} from '../../metadata';
import {extractDirectiveGuards} from '../../metadata/src/util'; import {extractDirectiveGuards} from '../../metadata/src/util';
import {DynamicValue, EnumValue, PartialEvaluator} from '../../partial_evaluator'; import {DynamicValue, EnumValue, PartialEvaluator} from '../../partial_evaluator';
import {ClassDeclaration, ClassMember, ClassMemberKind, Decorator, ReflectionHost, filterToMembersWithDecorator, reflectObjectLiteral} from '../../reflection'; import {ClassDeclaration, ClassMember, ClassMemberKind, Decorator, filterToMembersWithDecorator, ReflectionHost, reflectObjectLiteral} from '../../reflection';
import {LocalModuleScopeRegistry} from '../../scope'; import {LocalModuleScopeRegistry} from '../../scope';
import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerFlags, HandlerPrecedence, ResolveResult} from '../../transform'; import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerFlags, HandlerPrecedence, ResolveResult} from '../../transform';
@ -92,7 +92,7 @@ export class DirectiveDecoratorHandler implements
let providersRequiringFactory: Set<Reference<ClassDeclaration>>|null = null; let providersRequiringFactory: Set<Reference<ClassDeclaration>>|null = null;
if (directiveResult !== undefined && directiveResult.decorator.has('providers')) { if (directiveResult !== undefined && directiveResult.decorator.has('providers')) {
providersRequiringFactory = resolveProvidersRequiringFactory( providersRequiringFactory = resolveProvidersRequiringFactory(
directiveResult.decorator.get('providers') !, this.reflector, this.evaluator); directiveResult.decorator.get('providers')!, this.reflector, this.evaluator);
} }
return { return {
@ -102,7 +102,8 @@ export class DirectiveDecoratorHandler implements
node, this.reflector, this.defaultImportRecorder, this.isCore, node, this.reflector, this.defaultImportRecorder, this.isCore,
this.annotateForClosureCompiler), this.annotateForClosureCompiler),
baseClass: readBaseClass(node, this.reflector, this.evaluator), baseClass: readBaseClass(node, this.reflector, this.evaluator),
guards: extractDirectiveGuards(node, this.reflector), providersRequiringFactory guards: extractDirectiveGuards(node, this.reflector),
providersRequiringFactory
} }
}; };
} }
@ -120,7 +121,8 @@ export class DirectiveDecoratorHandler implements
outputs: analysis.meta.outputs, outputs: analysis.meta.outputs,
queries: analysis.meta.queries.map(query => query.propertyName), queries: analysis.meta.queries.map(query => query.propertyName),
isComponent: false, isComponent: false,
baseClass: analysis.baseClass, ...analysis.guards, baseClass: analysis.baseClass,
...analysis.guards,
}); });
this.injectableRegistry.registerInjectable(node); this.injectableRegistry.registerInjectable(node);
@ -132,7 +134,7 @@ export class DirectiveDecoratorHandler implements
if (analysis.providersRequiringFactory !== null && if (analysis.providersRequiringFactory !== null &&
analysis.meta.providers instanceof WrappedNodeExpr) { analysis.meta.providers instanceof WrappedNodeExpr) {
const providerDiagnostics = getProviderDiagnostics( const providerDiagnostics = getProviderDiagnostics(
analysis.providersRequiringFactory, analysis.meta.providers !.node, analysis.providersRequiringFactory, analysis.meta.providers!.node,
this.injectableRegistry); this.injectableRegistry);
diagnostics.push(...providerDiagnostics); diagnostics.push(...providerDiagnostics);
} }
@ -176,9 +178,8 @@ export class DirectiveDecoratorHandler implements
export function extractDirectiveMetadata( export function extractDirectiveMetadata(
clazz: ClassDeclaration, decorator: Readonly<Decorator|null>, reflector: ReflectionHost, clazz: ClassDeclaration, decorator: Readonly<Decorator|null>, reflector: ReflectionHost,
evaluator: PartialEvaluator, defaultImportRecorder: DefaultImportRecorder, isCore: boolean, evaluator: PartialEvaluator, defaultImportRecorder: DefaultImportRecorder, isCore: boolean,
flags: HandlerFlags, annotateForClosureCompiler: boolean, flags: HandlerFlags, annotateForClosureCompiler: boolean, defaultSelector: string|null = null):
defaultSelector: string | null = {decorator: Map<string, ts.Expression>, metadata: R3DirectiveMetadata}|undefined {
null): {decorator: Map<string, ts.Expression>, metadata: R3DirectiveMetadata}|undefined {
let directive: Map<string, ts.Expression>; let directive: Map<string, ts.Expression>;
if (decorator === null || decorator.args === null || decorator.args.length === 0) { if (decorator === null || decorator.args === null || decorator.args.length === 0) {
directive = new Map<string, ts.Expression>(); directive = new Map<string, ts.Expression>();
@ -220,9 +221,10 @@ export function extractDirectiveMetadata(
// And outputs. // And outputs.
const outputsFromMeta = parseFieldToPropertyMapping(directive, 'outputs', evaluator); const outputsFromMeta = parseFieldToPropertyMapping(directive, 'outputs', evaluator);
const outputsFromFields = parseDecoratedFields( const outputsFromFields =
filterToMembersWithDecorator(decoratedElements, 'Output', coreModule), evaluator, parseDecoratedFields(
resolveOutput) as{[field: string]: string}; filterToMembersWithDecorator(decoratedElements, 'Output', coreModule), evaluator,
resolveOutput) as {[field: string]: string};
// Construct the list of queries. // Construct the list of queries.
const contentChildFromFields = queriesFromFields( const contentChildFromFields = queriesFromFields(
filterToMembersWithDecorator(decoratedElements, 'ContentChild', coreModule), reflector, filterToMembersWithDecorator(decoratedElements, 'ContentChild', coreModule), reflector,
@ -244,7 +246,7 @@ export function extractDirectiveMetadata(
if (directive.has('queries')) { if (directive.has('queries')) {
const queriesFromDecorator = const queriesFromDecorator =
extractQueriesFromDecorator(directive.get('queries') !, reflector, evaluator, isCore); extractQueriesFromDecorator(directive.get('queries')!, reflector, evaluator, isCore);
queries.push(...queriesFromDecorator.content); queries.push(...queriesFromDecorator.content);
viewQueries.push(...queriesFromDecorator.view); viewQueries.push(...queriesFromDecorator.view);
} }
@ -252,7 +254,7 @@ export function extractDirectiveMetadata(
// Parse the selector. // Parse the selector.
let selector = defaultSelector; let selector = defaultSelector;
if (directive.has('selector')) { if (directive.has('selector')) {
const expr = directive.get('selector') !; const expr = directive.get('selector')!;
const resolved = evaluator.evaluate(expr); const resolved = evaluator.evaluate(expr);
if (typeof resolved !== 'string') { if (typeof resolved !== 'string') {
throw new FatalDiagnosticError( throw new FatalDiagnosticError(
@ -272,8 +274,8 @@ export function extractDirectiveMetadata(
const providers: Expression|null = directive.has('providers') ? const providers: Expression|null = directive.has('providers') ?
new WrappedNodeExpr( new WrappedNodeExpr(
annotateForClosureCompiler ? annotateForClosureCompiler ?
wrapFunctionExpressionsInParens(directive.get('providers') !) : wrapFunctionExpressionsInParens(directive.get('providers')!) :
directive.get('providers') !) : directive.get('providers')!) :
null; null;
// Determine if `ngOnChanges` is a lifecycle hook defined on the component. // Determine if `ngOnChanges` is a lifecycle hook defined on the component.
@ -284,7 +286,7 @@ export function extractDirectiveMetadata(
// Parse exportAs. // Parse exportAs.
let exportAs: string[]|null = null; let exportAs: string[]|null = null;
if (directive.has('exportAs')) { if (directive.has('exportAs')) {
const expr = directive.get('exportAs') !; const expr = directive.get('exportAs')!;
const resolved = evaluator.evaluate(expr); const resolved = evaluator.evaluate(expr);
if (typeof resolved !== 'string') { if (typeof resolved !== 'string') {
throw new FatalDiagnosticError( throw new FatalDiagnosticError(
@ -312,15 +314,24 @@ export function extractDirectiveMetadata(
const metadata: R3DirectiveMetadata = { const metadata: R3DirectiveMetadata = {
name: clazz.name.text, name: clazz.name.text,
deps: ctorDeps, host, deps: ctorDeps,
host,
lifecycle: { lifecycle: {
usesOnChanges, usesOnChanges,
}, },
inputs: {...inputsFromMeta, ...inputsFromFields}, inputs: {...inputsFromMeta, ...inputsFromFields},
outputs: {...outputsFromMeta, ...outputsFromFields}, queries, viewQueries, selector, outputs: {...outputsFromMeta, ...outputsFromFields},
fullInheritance: !!(flags & HandlerFlags.FULL_INHERITANCE), type, internalType, queries,
viewQueries,
selector,
fullInheritance: !!(flags & HandlerFlags.FULL_INHERITANCE),
type,
internalType,
typeArgumentCount: reflector.getGenericArityOfClass(clazz) || 0, typeArgumentCount: reflector.getGenericArityOfClass(clazz) || 0,
typeSourceSpan: createSourceSpan(clazz.name), usesInheritance, exportAs, providers typeSourceSpan: createSourceSpan(clazz.name),
usesInheritance,
exportAs,
providers
}; };
return {decorator: directive, metadata}; return {decorator: directive, metadata};
} }
@ -366,11 +377,11 @@ export function extractQueryMetadata(
} }
const options = reflectObjectLiteral(optionsExpr); const options = reflectObjectLiteral(optionsExpr);
if (options.has('read')) { if (options.has('read')) {
read = new WrappedNodeExpr(options.get('read') !); read = new WrappedNodeExpr(options.get('read')!);
} }
if (options.has('descendants')) { if (options.has('descendants')) {
const descendantsExpr = options.get('descendants') !; const descendantsExpr = options.get('descendants')!;
const descendantsValue = evaluator.evaluate(descendantsExpr); const descendantsValue = evaluator.evaluate(descendantsExpr);
if (typeof descendantsValue !== 'boolean') { if (typeof descendantsValue !== 'boolean') {
throw new FatalDiagnosticError( throw new FatalDiagnosticError(
@ -381,7 +392,7 @@ export function extractQueryMetadata(
} }
if (options.has('static')) { if (options.has('static')) {
const staticValue = evaluator.evaluate(options.get('static') !); const staticValue = evaluator.evaluate(options.get('static')!);
if (typeof staticValue !== 'boolean') { if (typeof staticValue !== 'boolean') {
throw new FatalDiagnosticError( throw new FatalDiagnosticError(
ErrorCode.VALUE_HAS_WRONG_TYPE, node, `@${name} options.static must be a boolean`); ErrorCode.VALUE_HAS_WRONG_TYPE, node, `@${name} options.static must be a boolean`);
@ -466,7 +477,7 @@ export function parseFieldArrayValue(
} }
// Resolve the field of interest from the directive metadata to a string[]. // Resolve the field of interest from the directive metadata to a string[].
const expression = directive.get(field) !; const expression = directive.get(field)!;
const value = evaluator.evaluate(expression); const value = evaluator.evaluate(expression);
if (!isStringArrayOrDie(value, field, expression)) { if (!isStringArrayOrDie(value, field, expression)) {
throw new FatalDiagnosticError( throw new FatalDiagnosticError(
@ -489,15 +500,13 @@ function parseFieldToPropertyMapping(
return EMPTY_OBJECT; return EMPTY_OBJECT;
} }
return metaValues.reduce( return metaValues.reduce((results, value) => {
(results, value) => { // Either the value is 'field' or 'field: property'. In the first case, `property` will
// Either the value is 'field' or 'field: property'. In the first case, `property` will // be undefined, in which case the field name should also be used as the property name.
// be undefined, in which case the field name should also be used as the property name. const [field, property] = value.split(':', 2).map(str => str.trim());
const [field, property] = value.split(':', 2).map(str => str.trim()); results[field] = property || field;
results[field] = property || field; return results;
return results; }, {} as {[field: string]: string});
},
{} as{[field: string]: string});
} }
/** /**
@ -507,33 +516,32 @@ function parseFieldToPropertyMapping(
function parseDecoratedFields( function parseDecoratedFields(
fields: {member: ClassMember, decorators: Decorator[]}[], evaluator: PartialEvaluator, fields: {member: ClassMember, decorators: Decorator[]}[], evaluator: PartialEvaluator,
mapValueResolver: (publicName: string, internalName: string) => mapValueResolver: (publicName: string, internalName: string) =>
string | [string, string]): {[field: string]: string | [string, string]} { string | [string, string]): {[field: string]: string|[string, string]} {
return fields.reduce( return fields.reduce((results, field) => {
(results, field) => { const fieldName = field.member.name;
const fieldName = field.member.name; field.decorators.forEach(decorator => {
field.decorators.forEach(decorator => { // The decorator either doesn't have an argument (@Input()) in which case the property
// The decorator either doesn't have an argument (@Input()) in which case the property // name is used, or it has one argument (@Output('named')).
// name is used, or it has one argument (@Output('named')). if (decorator.args == null || decorator.args.length === 0) {
if (decorator.args == null || decorator.args.length === 0) { results[fieldName] = fieldName;
results[fieldName] = fieldName; } else if (decorator.args.length === 1) {
} else if (decorator.args.length === 1) { const property = evaluator.evaluate(decorator.args[0]);
const property = evaluator.evaluate(decorator.args[0]); if (typeof property !== 'string') {
if (typeof property !== 'string') { throw new FatalDiagnosticError(
throw new FatalDiagnosticError( ErrorCode.VALUE_HAS_WRONG_TYPE, Decorator.nodeForError(decorator),
ErrorCode.VALUE_HAS_WRONG_TYPE, Decorator.nodeForError(decorator), `@${decorator.name} decorator argument must resolve to a string`);
`@${decorator.name} decorator argument must resolve to a string`); }
} results[fieldName] = mapValueResolver(property, fieldName);
results[fieldName] = mapValueResolver(property, fieldName); } else {
} else { // Too many arguments.
// Too many arguments. throw new FatalDiagnosticError(
throw new FatalDiagnosticError( ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator),
ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), `@${decorator.name} can have at most one argument, got ${
`@${decorator.name} can have at most one argument, got ${decorator.args.length} argument(s)`); decorator.args.length} argument(s)`);
} }
}); });
return results; return results;
}, }, {} as {[field: string]: string | [string, string]});
{} as{[field: string]: string | [string, string]});
} }
function resolveInput(publicName: string, internalName: string): [string, string] { function resolveInput(publicName: string, internalName: string): [string, string] {
@ -552,7 +560,7 @@ export function queriesFromFields(
const node = member.node || Decorator.nodeForError(decorator); const node = member.node || Decorator.nodeForError(decorator);
// Throw in case of `@Input() @ContentChild('foo') foo: any`, which is not supported in Ivy // Throw in case of `@Input() @ContentChild('foo') foo: any`, which is not supported in Ivy
if (member.decorators !.some(v => v.name === 'Input')) { if (member.decorators!.some(v => v.name === 'Input')) {
throw new FatalDiagnosticError( throw new FatalDiagnosticError(
ErrorCode.DECORATOR_COLLISION, node, ErrorCode.DECORATOR_COLLISION, node,
'Cannot combine @Input decorators with query decorators'); 'Cannot combine @Input decorators with query decorators');
@ -626,38 +634,40 @@ function evaluateHostExpressionBindings(
} }
export function extractHostBindings( export function extractHostBindings(
members: ClassMember[], evaluator: PartialEvaluator, coreModule: string | undefined, members: ClassMember[], evaluator: PartialEvaluator, coreModule: string|undefined,
metadata?: Map<string, ts.Expression>): ParsedHostBindings { metadata?: Map<string, ts.Expression>): ParsedHostBindings {
let bindings: ParsedHostBindings; let bindings: ParsedHostBindings;
if (metadata && metadata.has('host')) { if (metadata && metadata.has('host')) {
bindings = evaluateHostExpressionBindings(metadata.get('host') !, evaluator); bindings = evaluateHostExpressionBindings(metadata.get('host')!, evaluator);
} else { } else {
bindings = parseHostBindings({}); bindings = parseHostBindings({});
} }
filterToMembersWithDecorator(members, 'HostBinding', coreModule).forEach(({member, decorators}) => { filterToMembersWithDecorator(members, 'HostBinding', coreModule)
decorators.forEach(decorator => { .forEach(({member, decorators}) => {
let hostPropertyName: string = member.name; decorators.forEach(decorator => {
if (decorator.args !== null && decorator.args.length > 0) { let hostPropertyName: string = member.name;
if (decorator.args.length !== 1) { if (decorator.args !== null && decorator.args.length > 0) {
throw new FatalDiagnosticError( if (decorator.args.length !== 1) {
ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), throw new FatalDiagnosticError(
`@HostBinding can have at most one argument, got ${decorator.args.length} argument(s)`); ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator),
} `@HostBinding can have at most one argument, got ${
decorator.args.length} argument(s)`);
}
const resolved = evaluator.evaluate(decorator.args[0]); const resolved = evaluator.evaluate(decorator.args[0]);
if (typeof resolved !== 'string') { if (typeof resolved !== 'string') {
throw new FatalDiagnosticError( throw new FatalDiagnosticError(
ErrorCode.VALUE_HAS_WRONG_TYPE, Decorator.nodeForError(decorator), ErrorCode.VALUE_HAS_WRONG_TYPE, Decorator.nodeForError(decorator),
`@HostBinding's argument must be a string`); `@HostBinding's argument must be a string`);
} }
hostPropertyName = resolved; hostPropertyName = resolved;
} }
bindings.properties[hostPropertyName] = member.name; bindings.properties[hostPropertyName] = member.name;
}); });
}); });
filterToMembersWithDecorator(members, 'HostListener', coreModule) filterToMembersWithDecorator(members, 'HostListener', coreModule)
.forEach(({member, decorators}) => { .forEach(({member, decorators}) => {

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {R3FactoryMetadata, compileFactoryFunction} from '@angular/compiler'; import {compileFactoryFunction, R3FactoryMetadata} from '@angular/compiler';
import {CompileResult} from '../../transform'; import {CompileResult} from '../../transform';

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Expression, Identifiers, LiteralExpr, R3DependencyMetadata, R3FactoryTarget, R3InjectableMetadata, R3ResolvedDependencyType, Statement, WrappedNodeExpr, compileInjectable as compileIvyInjectable} from '@angular/compiler'; import {compileInjectable as compileIvyInjectable, Expression, Identifiers, LiteralExpr, R3DependencyMetadata, R3FactoryTarget, R3InjectableMetadata, R3ResolvedDependencyType, Statement, WrappedNodeExpr} from '@angular/compiler';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
@ -83,7 +83,9 @@ export class InjectableDecoratorHandler implements
}; };
} }
register(node: ClassDeclaration): void { this.injectableRegistry.registerInjectable(node); } register(node: ClassDeclaration): void {
this.injectableRegistry.registerInjectable(node);
}
compile(node: ClassDeclaration, analysis: Readonly<InjectableHandlerData>): CompileResult[] { compile(node: ClassDeclaration, analysis: Readonly<InjectableHandlerData>): CompileResult[] {
const res = compileIvyInjectable(analysis.meta); const res = compileIvyInjectable(analysis.meta);
@ -165,12 +167,12 @@ function extractInjectableMetadata(
const meta = reflectObjectLiteral(metaNode); const meta = reflectObjectLiteral(metaNode);
let providedIn: Expression = new LiteralExpr(null); let providedIn: Expression = new LiteralExpr(null);
if (meta.has('providedIn')) { if (meta.has('providedIn')) {
providedIn = new WrappedNodeExpr(meta.get('providedIn') !); providedIn = new WrappedNodeExpr(meta.get('providedIn')!);
} }
let userDeps: R3DependencyMetadata[]|undefined = undefined; let userDeps: R3DependencyMetadata[]|undefined = undefined;
if ((meta.has('useClass') || meta.has('useFactory')) && meta.has('deps')) { if ((meta.has('useClass') || meta.has('useFactory')) && meta.has('deps')) {
const depsExpr = meta.get('deps') !; const depsExpr = meta.get('deps')!;
if (!ts.isArrayLiteralExpression(depsExpr)) { if (!ts.isArrayLiteralExpression(depsExpr)) {
throw new FatalDiagnosticError( throw new FatalDiagnosticError(
ErrorCode.VALUE_NOT_LITERAL, depsExpr, ErrorCode.VALUE_NOT_LITERAL, depsExpr,
@ -186,7 +188,7 @@ function extractInjectableMetadata(
typeArgumentCount, typeArgumentCount,
internalType, internalType,
providedIn, providedIn,
useValue: new WrappedNodeExpr(unwrapForwardRef(meta.get('useValue') !, reflector)), useValue: new WrappedNodeExpr(unwrapForwardRef(meta.get('useValue')!, reflector)),
}; };
} else if (meta.has('useExisting')) { } else if (meta.has('useExisting')) {
return { return {
@ -195,7 +197,7 @@ function extractInjectableMetadata(
typeArgumentCount, typeArgumentCount,
internalType, internalType,
providedIn, providedIn,
useExisting: new WrappedNodeExpr(unwrapForwardRef(meta.get('useExisting') !, reflector)), useExisting: new WrappedNodeExpr(unwrapForwardRef(meta.get('useExisting')!, reflector)),
}; };
} else if (meta.has('useClass')) { } else if (meta.has('useClass')) {
return { return {
@ -204,19 +206,20 @@ function extractInjectableMetadata(
typeArgumentCount, typeArgumentCount,
internalType, internalType,
providedIn, providedIn,
useClass: new WrappedNodeExpr(unwrapForwardRef(meta.get('useClass') !, reflector)), useClass: new WrappedNodeExpr(unwrapForwardRef(meta.get('useClass')!, reflector)),
userDeps, userDeps,
}; };
} else if (meta.has('useFactory')) { } else if (meta.has('useFactory')) {
// useFactory is special - the 'deps' property must be analyzed. // useFactory is special - the 'deps' property must be analyzed.
const factory = new WrappedNodeExpr(meta.get('useFactory') !); const factory = new WrappedNodeExpr(meta.get('useFactory')!);
return { return {
name, name,
type, type,
typeArgumentCount, typeArgumentCount,
internalType, internalType,
providedIn, providedIn,
useFactory: factory, userDeps, useFactory: factory,
userDeps,
}; };
} else { } else {
return {name, type, typeArgumentCount, internalType, providedIn}; return {name, type, typeArgumentCount, internalType, providedIn};

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Expression, ExternalExpr, FunctionExpr, Identifiers, InvokeFunctionExpr, LiteralArrayExpr, LiteralExpr, NONE_TYPE, ReturnStatement, Statement, WrappedNodeExpr, literalMap} from '@angular/compiler'; import {Expression, ExternalExpr, FunctionExpr, Identifiers, InvokeFunctionExpr, LiteralArrayExpr, LiteralExpr, literalMap, NONE_TYPE, ReturnStatement, Statement, WrappedNodeExpr} from '@angular/compiler';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {DefaultImportRecorder} from '../../imports'; import {DefaultImportRecorder} from '../../imports';
@ -71,7 +71,7 @@ export function generateSetClassMetadataCall(
duplicateDecoratedMemberNames.join(', ')); duplicateDecoratedMemberNames.join(', '));
} }
const decoratedMembers = const decoratedMembers =
classMembers.map(member => classMemberToMetadata(member.name, member.decorators !, isCore)); classMembers.map(member => classMemberToMetadata(member.name, member.decorators!, isCore));
if (decoratedMembers.length > 0) { if (decoratedMembers.length > 0) {
metaPropDecorators = ts.createObjectLiteral(decoratedMembers); metaPropDecorators = ts.createObjectLiteral(decoratedMembers);
} }

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {CUSTOM_ELEMENTS_SCHEMA, Expression, ExternalExpr, InvokeFunctionExpr, LiteralArrayExpr, LiteralExpr, NO_ERRORS_SCHEMA, R3Identifiers, R3InjectorMetadata, R3NgModuleMetadata, R3Reference, STRING_TYPE, SchemaMetadata, Statement, WrappedNodeExpr, compileInjector, compileNgModule} from '@angular/compiler'; import {compileInjector, compileNgModule, CUSTOM_ELEMENTS_SCHEMA, Expression, ExternalExpr, InvokeFunctionExpr, LiteralArrayExpr, LiteralExpr, NO_ERRORS_SCHEMA, R3Identifiers, R3InjectorMetadata, R3NgModuleMetadata, R3Reference, SchemaMetadata, Statement, STRING_TYPE, WrappedNodeExpr} from '@angular/compiler';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {ErrorCode, FatalDiagnosticError, makeDiagnostic} from '../../diagnostics'; import {ErrorCode, FatalDiagnosticError, makeDiagnostic} from '../../diagnostics';
@ -40,7 +40,9 @@ export interface NgModuleAnalysis {
providers: ts.Expression|null; providers: ts.Expression|null;
} }
export interface NgModuleResolution { injectorImports: Expression[]; } export interface NgModuleResolution {
injectorImports: Expression[];
}
/** /**
* Compiles @NgModule annotations to ngModuleDef fields. * Compiles @NgModule annotations to ngModuleDef fields.
@ -116,7 +118,7 @@ export class NgModuleDecoratorHandler implements
let declarationRefs: Reference<ClassDeclaration>[] = []; let declarationRefs: Reference<ClassDeclaration>[] = [];
let rawDeclarations: ts.Expression|null = null; let rawDeclarations: ts.Expression|null = null;
if (ngModule.has('declarations')) { if (ngModule.has('declarations')) {
rawDeclarations = ngModule.get('declarations') !; rawDeclarations = ngModule.get('declarations')!;
const declarationMeta = this.evaluator.evaluate(rawDeclarations, forwardRefResolver); const declarationMeta = this.evaluator.evaluate(rawDeclarations, forwardRefResolver);
declarationRefs = declarationRefs =
this.resolveTypeList(rawDeclarations, declarationMeta, name, 'declarations'); this.resolveTypeList(rawDeclarations, declarationMeta, name, 'declarations');
@ -128,7 +130,9 @@ export class NgModuleDecoratorHandler implements
diagnostics.push(makeDiagnostic( diagnostics.push(makeDiagnostic(
ErrorCode.NGMODULE_INVALID_DECLARATION, errorNode, ErrorCode.NGMODULE_INVALID_DECLARATION, errorNode,
`Cannot declare '${ref.node.name.text}' in an NgModule as it's not a part of the current compilation.`, `Cannot declare '${
ref.node.name
.text}' in an NgModule as it's not a part of the current compilation.`,
[{ [{
node: ref.node.name, node: ref.node.name,
messageText: `'${ref.node.name.text}' is declared here.`, messageText: `'${ref.node.name.text}' is declared here.`,
@ -144,28 +148,28 @@ export class NgModuleDecoratorHandler implements
let importRefs: Reference<ClassDeclaration>[] = []; let importRefs: Reference<ClassDeclaration>[] = [];
let rawImports: ts.Expression|null = null; let rawImports: ts.Expression|null = null;
if (ngModule.has('imports')) { if (ngModule.has('imports')) {
rawImports = ngModule.get('imports') !; rawImports = ngModule.get('imports')!;
const importsMeta = this.evaluator.evaluate(rawImports, moduleResolvers); const importsMeta = this.evaluator.evaluate(rawImports, moduleResolvers);
importRefs = this.resolveTypeList(rawImports, importsMeta, name, 'imports'); importRefs = this.resolveTypeList(rawImports, importsMeta, name, 'imports');
} }
let exportRefs: Reference<ClassDeclaration>[] = []; let exportRefs: Reference<ClassDeclaration>[] = [];
let rawExports: ts.Expression|null = null; let rawExports: ts.Expression|null = null;
if (ngModule.has('exports')) { if (ngModule.has('exports')) {
rawExports = ngModule.get('exports') !; rawExports = ngModule.get('exports')!;
const exportsMeta = this.evaluator.evaluate(rawExports, moduleResolvers); const exportsMeta = this.evaluator.evaluate(rawExports, moduleResolvers);
exportRefs = this.resolveTypeList(rawExports, exportsMeta, name, 'exports'); exportRefs = this.resolveTypeList(rawExports, exportsMeta, name, 'exports');
this.referencesRegistry.add(node, ...exportRefs); this.referencesRegistry.add(node, ...exportRefs);
} }
let bootstrapRefs: Reference<ClassDeclaration>[] = []; let bootstrapRefs: Reference<ClassDeclaration>[] = [];
if (ngModule.has('bootstrap')) { if (ngModule.has('bootstrap')) {
const expr = ngModule.get('bootstrap') !; const expr = ngModule.get('bootstrap')!;
const bootstrapMeta = this.evaluator.evaluate(expr, forwardRefResolver); const bootstrapMeta = this.evaluator.evaluate(expr, forwardRefResolver);
bootstrapRefs = this.resolveTypeList(expr, bootstrapMeta, name, 'bootstrap'); bootstrapRefs = this.resolveTypeList(expr, bootstrapMeta, name, 'bootstrap');
} }
const schemas: SchemaMetadata[] = []; const schemas: SchemaMetadata[] = [];
if (ngModule.has('schemas')) { if (ngModule.has('schemas')) {
const rawExpr = ngModule.get('schemas') !; const rawExpr = ngModule.get('schemas')!;
const result = this.evaluator.evaluate(rawExpr); const result = this.evaluator.evaluate(rawExpr);
if (!Array.isArray(result)) { if (!Array.isArray(result)) {
throw new FatalDiagnosticError( throw new FatalDiagnosticError(
@ -203,7 +207,7 @@ export class NgModuleDecoratorHandler implements
} }
const id: Expression|null = const id: Expression|null =
ngModule.has('id') ? new WrappedNodeExpr(ngModule.get('id') !) : null; ngModule.has('id') ? new WrappedNodeExpr(ngModule.get('id')!) : null;
const valueContext = node.getSourceFile(); const valueContext = node.getSourceFile();
let typeContext = valueContext; let typeContext = valueContext;
@ -220,7 +224,7 @@ export class NgModuleDecoratorHandler implements
const exports = exportRefs.map(exp => this._toR3Reference(exp, valueContext, typeContext)); const exports = exportRefs.map(exp => this._toR3Reference(exp, valueContext, typeContext));
const isForwardReference = (ref: R3Reference) => const isForwardReference = (ref: R3Reference) =>
isExpressionForwardReference(ref.value, node.name !, valueContext); isExpressionForwardReference(ref.value, node.name!, valueContext);
const containsForwardDecls = bootstrap.some(isForwardReference) || const containsForwardDecls = bootstrap.some(isForwardReference) ||
declarations.some(isForwardReference) || imports.some(isForwardReference) || declarations.some(isForwardReference) || imports.some(isForwardReference) ||
exports.some(isForwardReference); exports.some(isForwardReference);
@ -244,7 +248,7 @@ export class NgModuleDecoratorHandler implements
schemas: [], schemas: [],
}; };
const rawProviders = ngModule.has('providers') ? ngModule.get('providers') ! : null; const rawProviders = ngModule.has('providers') ? ngModule.get('providers')! : null;
const wrapperProviders = rawProviders !== null ? const wrapperProviders = rawProviders !== null ?
new WrappedNodeExpr( new WrappedNodeExpr(
this.annotateForClosureCompiler ? wrapFunctionExpressionsInParens(rawProviders) : this.annotateForClosureCompiler ? wrapFunctionExpressionsInParens(rawProviders) :
@ -256,7 +260,7 @@ export class NgModuleDecoratorHandler implements
// and pipes from the module exports. // and pipes from the module exports.
const injectorImports: WrappedNodeExpr<ts.Expression>[] = []; const injectorImports: WrappedNodeExpr<ts.Expression>[] = [];
if (ngModule.has('imports')) { if (ngModule.has('imports')) {
injectorImports.push(new WrappedNodeExpr(ngModule.get('imports') !)); injectorImports.push(new WrappedNodeExpr(ngModule.get('imports')!));
} }
if (this.routeAnalyzer !== null) { if (this.routeAnalyzer !== null) {
@ -279,7 +283,8 @@ export class NgModuleDecoratorHandler implements
schemas: schemas, schemas: schemas,
mod: ngModuleDef, mod: ngModuleDef,
inj: ngInjectorDef, inj: ngInjectorDef,
declarations: declarationRefs, rawDeclarations, declarations: declarationRefs,
rawDeclarations,
imports: importRefs, imports: importRefs,
exports: exportRefs, exports: exportRefs,
providers: rawProviders, providers: rawProviders,
@ -326,7 +331,7 @@ export class NgModuleDecoratorHandler implements
if (analysis.providersRequiringFactory !== null) { if (analysis.providersRequiringFactory !== null) {
const providerDiagnostics = getProviderDiagnostics( const providerDiagnostics = getProviderDiagnostics(
analysis.providersRequiringFactory, analysis.providers !, this.injectableRegistry); analysis.providersRequiringFactory, analysis.providers!, this.injectableRegistry);
diagnostics.push(...providerDiagnostics); diagnostics.push(...providerDiagnostics);
} }
@ -396,7 +401,7 @@ export class NgModuleDecoratorHandler implements
const pipes = scope.compilation.pipes.map(pipe => this.refEmitter.emit(pipe.ref, context)); const pipes = scope.compilation.pipes.map(pipe => this.refEmitter.emit(pipe.ref, context));
const directiveArray = new LiteralArrayExpr(directives); const directiveArray = new LiteralArrayExpr(directives);
const pipesArray = new LiteralArrayExpr(pipes); const pipesArray = new LiteralArrayExpr(pipes);
const declExpr = this.refEmitter.emit(decl, context) !; const declExpr = this.refEmitter.emit(decl, context)!;
const setComponentScope = new ExternalExpr(R3Identifiers.setComponentScope); const setComponentScope = new ExternalExpr(R3Identifiers.setComponentScope);
const callExpr = const callExpr =
new InvokeFunctionExpr(setComponentScope, [declExpr, directiveArray, pipesArray]); new InvokeFunctionExpr(setComponentScope, [declExpr, directiveArray, pipesArray]);
@ -472,8 +477,9 @@ export class NgModuleDecoratorHandler implements
return null; return null;
} }
const typeName = type && (ts.isIdentifier(type.typeName) && type.typeName || const typeName = type &&
ts.isQualifiedName(type.typeName) && type.typeName.right) || (ts.isIdentifier(type.typeName) && type.typeName ||
ts.isQualifiedName(type.typeName) && type.typeName.right) ||
null; null;
if (typeName === null) { if (typeName === null) {
return null; return null;
@ -559,7 +565,7 @@ export class NgModuleDecoratorHandler implements
// Unwrap ModuleWithProviders for modules that are locally declared (and thus static // Unwrap ModuleWithProviders for modules that are locally declared (and thus static
// resolution was able to descend into the function and return an object literal, a Map). // resolution was able to descend into the function and return an object literal, a Map).
if (entry instanceof Map && entry.has('ngModule')) { if (entry instanceof Map && entry.has('ngModule')) {
entry = entry.get('ngModule') !; entry = entry.get('ngModule')!;
} }
if (Array.isArray(entry)) { if (Array.isArray(entry)) {
@ -569,14 +575,16 @@ export class NgModuleDecoratorHandler implements
if (!this.isClassDeclarationReference(entry)) { if (!this.isClassDeclarationReference(entry)) {
throw new FatalDiagnosticError( throw new FatalDiagnosticError(
ErrorCode.VALUE_HAS_WRONG_TYPE, entry.node, ErrorCode.VALUE_HAS_WRONG_TYPE, entry.node,
`Value at position ${idx} in the NgModule.${arrayName} of ${className} is not a class`); `Value at position ${idx} in the NgModule.${arrayName} of ${
className} is not a class`);
} }
refList.push(entry); refList.push(entry);
} else { } else {
// TODO(alxhub): Produce a better diagnostic here - the array index may be an inner array. // TODO(alxhub): Produce a better diagnostic here - the array index may be an inner array.
throw new FatalDiagnosticError( throw new FatalDiagnosticError(
ErrorCode.VALUE_HAS_WRONG_TYPE, expr, ErrorCode.VALUE_HAS_WRONG_TYPE, expr,
`Value at position ${idx} in the NgModule.${arrayName} of ${className} is not a reference: ${entry}`); `Value at position ${idx} in the NgModule.${arrayName} of ${
className} is not a reference: ${entry}`);
} }
}); });

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Identifiers, R3FactoryTarget, R3PipeMetadata, Statement, WrappedNodeExpr, compilePipeFromMetadata} from '@angular/compiler'; import {compilePipeFromMetadata, Identifiers, R3FactoryTarget, R3PipeMetadata, Statement, WrappedNodeExpr} from '@angular/compiler';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
@ -79,7 +79,7 @@ export class PipeDecoratorHandler implements DecoratorHandler<Decorator, PipeHan
throw new FatalDiagnosticError( throw new FatalDiagnosticError(
ErrorCode.PIPE_MISSING_NAME, meta, `@Pipe decorator is missing name field`); ErrorCode.PIPE_MISSING_NAME, meta, `@Pipe decorator is missing name field`);
} }
const pipeNameExpr = pipe.get('name') !; const pipeNameExpr = pipe.get('name')!;
const pipeName = this.evaluator.evaluate(pipeNameExpr); const pipeName = this.evaluator.evaluate(pipeNameExpr);
if (typeof pipeName !== 'string') { if (typeof pipeName !== 'string') {
throw new FatalDiagnosticError( throw new FatalDiagnosticError(
@ -88,7 +88,7 @@ export class PipeDecoratorHandler implements DecoratorHandler<Decorator, PipeHan
let pure = true; let pure = true;
if (pipe.has('pure')) { if (pipe.has('pure')) {
const expr = pipe.get('pure') !; const expr = pipe.get('pure')!;
const pureValue = this.evaluator.evaluate(expr); const pureValue = this.evaluator.evaluate(expr);
if (typeof pureValue !== 'boolean') { if (typeof pureValue !== 'boolean') {
throw new FatalDiagnosticError( throw new FatalDiagnosticError(
@ -103,7 +103,8 @@ export class PipeDecoratorHandler implements DecoratorHandler<Decorator, PipeHan
name, name,
type, type,
internalType, internalType,
typeArgumentCount: this.reflector.getGenericArityOfClass(clazz) || 0, pipeName, typeArgumentCount: this.reflector.getGenericArityOfClass(clazz) || 0,
pipeName,
deps: getValidConstructorDependencies( deps: getValidConstructorDependencies(
clazz, this.reflector, this.defaultImportRecorder, this.isCore), clazz, this.reflector, this.defaultImportRecorder, this.isCore),
pure, pure,

View File

@ -12,7 +12,7 @@ import * as ts from 'typescript';
import {ErrorCode, FatalDiagnosticError, makeDiagnostic} from '../../diagnostics'; import {ErrorCode, FatalDiagnosticError, makeDiagnostic} from '../../diagnostics';
import {DefaultImportRecorder, ImportFlags, Reference, ReferenceEmitter} from '../../imports'; import {DefaultImportRecorder, ImportFlags, Reference, ReferenceEmitter} from '../../imports';
import {ForeignFunctionResolver, PartialEvaluator} from '../../partial_evaluator'; import {ForeignFunctionResolver, PartialEvaluator} from '../../partial_evaluator';
import {ClassDeclaration, CtorParameter, Decorator, Import, ReflectionHost, TypeValueReference, isNamedClassDeclaration} from '../../reflection'; import {ClassDeclaration, CtorParameter, Decorator, Import, isNamedClassDeclaration, ReflectionHost, TypeValueReference} from '../../reflection';
import {DeclarationData} from '../../scope'; import {DeclarationData} from '../../scope';
export enum ConstructorDepErrorKind { export enum ConstructorDepErrorKind {
@ -21,8 +21,7 @@ export enum ConstructorDepErrorKind {
export type ConstructorDeps = { export type ConstructorDeps = {
deps: R3DependencyMetadata[]; deps: R3DependencyMetadata[];
} | }|{
{
deps: null; deps: null;
errors: ConstructorDepError[]; errors: ConstructorDepError[];
}; };
@ -53,7 +52,7 @@ export function getConstructorDependencies(
let resolved = R3ResolvedDependencyType.Token; let resolved = R3ResolvedDependencyType.Token;
(param.decorators || []).filter(dec => isCore || isAngularCore(dec)).forEach(dec => { (param.decorators || []).filter(dec => isCore || isAngularCore(dec)).forEach(dec => {
const name = isCore || dec.import === null ? dec.name : dec.import !.name; const name = isCore || dec.import === null ? dec.name : dec.import!.name;
if (name === 'Inject') { if (name === 'Inject') {
if (dec.args === null || dec.args.length !== 1) { if (dec.args === null || dec.args.length !== 1) {
throw new FatalDiagnosticError( throw new FatalDiagnosticError(
@ -97,7 +96,8 @@ export function getConstructorDependencies(
if (token === null) { if (token === null) {
errors.push({ errors.push({
index: idx, index: idx,
kind: ConstructorDepErrorKind.NO_SUITABLE_TOKEN, param, kind: ConstructorDepErrorKind.NO_SUITABLE_TOKEN,
param,
}); });
} else { } else {
deps.push({token, attribute, optional, self, skipSelf, host, resolved}); deps.push({token, attribute, optional, self, skipSelf, host, resolved});
@ -122,10 +122,10 @@ export function valueReferenceToExpression(
export function valueReferenceToExpression( export function valueReferenceToExpression(
valueRef: null, defaultImportRecorder: DefaultImportRecorder): null; valueRef: null, defaultImportRecorder: DefaultImportRecorder): null;
export function valueReferenceToExpression( export function valueReferenceToExpression(
valueRef: TypeValueReference | null, defaultImportRecorder: DefaultImportRecorder): Expression| valueRef: TypeValueReference|null, defaultImportRecorder: DefaultImportRecorder): Expression|
null; null;
export function valueReferenceToExpression( export function valueReferenceToExpression(
valueRef: TypeValueReference | null, defaultImportRecorder: DefaultImportRecorder): Expression| valueRef: TypeValueReference|null, defaultImportRecorder: DefaultImportRecorder): Expression|
null { null {
if (valueRef === null) { if (valueRef === null) {
return null; return null;
@ -138,7 +138,7 @@ export function valueReferenceToExpression(
return new WrappedNodeExpr(valueRef.expression); return new WrappedNodeExpr(valueRef.expression);
} else { } else {
// TODO(alxhub): this cast is necessary because the g3 typescript version doesn't narrow here. // TODO(alxhub): this cast is necessary because the g3 typescript version doesn't narrow here.
return new ExternalExpr(valueRef as{moduleName: string, name: string}); return new ExternalExpr(valueRef as {moduleName: string, name: string});
} }
} }
@ -148,7 +148,7 @@ export function valueReferenceToExpression(
* *
* This is a companion function to `validateConstructorDependencies` which accepts invalid deps. * This is a companion function to `validateConstructorDependencies` which accepts invalid deps.
*/ */
export function unwrapConstructorDependencies(deps: ConstructorDeps | null): R3DependencyMetadata[]| export function unwrapConstructorDependencies(deps: ConstructorDeps|null): R3DependencyMetadata[]|
'invalid'|null { 'invalid'|null {
if (deps === null) { if (deps === null) {
return null; return null;
@ -176,18 +176,19 @@ export function getValidConstructorDependencies(
* deps. * deps.
*/ */
export function validateConstructorDependencies( export function validateConstructorDependencies(
clazz: ClassDeclaration, deps: ConstructorDeps | null): R3DependencyMetadata[]|null { clazz: ClassDeclaration, deps: ConstructorDeps|null): R3DependencyMetadata[]|null {
if (deps === null) { if (deps === null) {
return null; return null;
} else if (deps.deps !== null) { } else if (deps.deps !== null) {
return deps.deps; return deps.deps;
} else { } else {
// TODO(alxhub): this cast is necessary because the g3 typescript version doesn't narrow here. // TODO(alxhub): this cast is necessary because the g3 typescript version doesn't narrow here.
const {param, index} = (deps as{errors: ConstructorDepError[]}).errors[0]; const {param, index} = (deps as {errors: ConstructorDepError[]}).errors[0];
// There is at least one error. // There is at least one error.
throw new FatalDiagnosticError( throw new FatalDiagnosticError(
ErrorCode.PARAM_MISSING_TOKEN, param.nameNode, ErrorCode.PARAM_MISSING_TOKEN, param.nameNode,
`No suitable injection token for parameter '${param.name || index}' of class '${clazz.name.text}'.\n` + `No suitable injection token for parameter '${param.name || index}' of class '${
clazz.name.text}'.\n` +
(param.typeNode !== null ? `Found ${param.typeNode.getText()}` : (param.typeNode !== null ? `Found ${param.typeNode.getText()}` :
'no type or decorator')); 'no type or decorator'));
} }
@ -319,8 +320,7 @@ export function forwardRefResolver(
*/ */
export function combineResolvers(resolvers: ForeignFunctionResolver[]): ForeignFunctionResolver { export function combineResolvers(resolvers: ForeignFunctionResolver[]): ForeignFunctionResolver {
return (ref: Reference<ts.FunctionDeclaration|ts.MethodDeclaration|ts.FunctionExpression>, return (ref: Reference<ts.FunctionDeclaration|ts.MethodDeclaration|ts.FunctionExpression>,
args: ReadonlyArray<ts.Expression>): ts.Expression | args: ReadonlyArray<ts.Expression>): ts.Expression|null => {
null => {
for (const resolver of resolvers) { for (const resolver of resolvers) {
const resolved = resolver(ref, args); const resolved = resolver(ref, args);
if (resolved !== null) { if (resolved !== null) {
@ -406,8 +406,8 @@ export function makeDuplicateDeclarationError(
const contextNode = decl.ref.getOriginForDiagnostics(decl.rawDeclarations, decl.ngModule.name); const contextNode = decl.ref.getOriginForDiagnostics(decl.rawDeclarations, decl.ngModule.name);
context.push({ context.push({
node: contextNode, node: contextNode,
messageText: messageText: `'${node.name.text}' is listed in the declarations of the NgModule '${
`'${node.name.text}' is listed in the declarations of the NgModule '${decl.ngModule.name.text}'.`, decl.ngModule.name.text}'.`,
}); });
} }
@ -441,7 +441,7 @@ export function resolveProvidersRequiringFactory(
} else if (provider instanceof Reference) { } else if (provider instanceof Reference) {
tokenClass = provider; tokenClass = provider;
} else if (provider instanceof Map && provider.has('useClass') && !provider.has('deps')) { } else if (provider instanceof Map && provider.has('useClass') && !provider.has('deps')) {
const useExisting = provider.get('useClass') !; const useExisting = provider.get('useClass')!;
if (useExisting instanceof Reference) { if (useExisting instanceof Reference) {
tokenClass = useExisting; tokenClass = useExisting;
} }

View File

@ -12,17 +12,23 @@ import {runInEachFileSystem} from '../../file_system/testing';
import {ModuleResolver, NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../imports'; import {ModuleResolver, NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../imports';
import {CompoundMetadataReader, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry} from '../../metadata'; import {CompoundMetadataReader, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry} from '../../metadata';
import {PartialEvaluator} from '../../partial_evaluator'; import {PartialEvaluator} from '../../partial_evaluator';
import {TypeScriptReflectionHost, isNamedClassDeclaration} from '../../reflection'; import {isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection';
import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope'; import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope';
import {getDeclaration, makeProgram} from '../../testing'; import {getDeclaration, makeProgram} from '../../testing';
import {ResourceLoader} from '../src/api'; import {ResourceLoader} from '../src/api';
import {ComponentDecoratorHandler} from '../src/component'; import {ComponentDecoratorHandler} from '../src/component';
export class NoopResourceLoader implements ResourceLoader { export class NoopResourceLoader implements ResourceLoader {
resolve(): string { throw new Error('Not implemented.'); } resolve(): string {
throw new Error('Not implemented.');
}
canPreload = false; canPreload = false;
load(): string { throw new Error('Not implemented'); } load(): string {
preload(): Promise<void>|undefined { throw new Error('Not implemented'); } throw new Error('Not implemented');
}
preload(): Promise<void>|undefined {
throw new Error('Not implemented');
}
} }
runInEachFileSystem(() => { runInEachFileSystem(() => {
describe('ComponentDecoratorHandler', () => { describe('ComponentDecoratorHandler', () => {
@ -83,10 +89,12 @@ runInEachFileSystem(() => {
const diag = err.toDiagnostic(); const diag = err.toDiagnostic();
expect(diag.code).toEqual(ivyCode(ErrorCode.DECORATOR_ARG_NOT_LITERAL)); expect(diag.code).toEqual(ivyCode(ErrorCode.DECORATOR_ARG_NOT_LITERAL));
expect(diag.file.fileName.endsWith('entry.ts')).toBe(true); expect(diag.file.fileName.endsWith('entry.ts')).toBe(true);
expect(diag.start).toBe(detected.metadata.args ![0].getStart()); expect(diag.start).toBe(detected.metadata.args![0].getStart());
} }
}); });
}); });
function ivyCode(code: ErrorCode): number { return Number('-99' + code.valueOf()); } function ivyCode(code: ErrorCode): number {
return Number('-99' + code.valueOf());
}
}); });

View File

@ -6,12 +6,13 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import * as ts from 'typescript'; import * as ts from 'typescript';
import {absoluteFrom} from '../../file_system'; import {absoluteFrom} from '../../file_system';
import {runInEachFileSystem} from '../../file_system/testing'; import {runInEachFileSystem} from '../../file_system/testing';
import {NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../imports'; import {NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../imports';
import {DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry} from '../../metadata'; import {DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry} from '../../metadata';
import {PartialEvaluator} from '../../partial_evaluator'; import {PartialEvaluator} from '../../partial_evaluator';
import {ClassDeclaration, TypeScriptReflectionHost, isNamedClassDeclaration} from '../../reflection'; import {ClassDeclaration, isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection';
import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope'; import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope';
import {getDeclaration, makeProgram} from '../../testing'; import {getDeclaration, makeProgram} from '../../testing';
import {DirectiveDecoratorHandler} from '../src/directive'; import {DirectiveDecoratorHandler} from '../src/directive';
@ -77,9 +78,13 @@ runInEachFileSystem(() => {
// Helpers // Helpers
function analyzeDirective(program: ts.Program, dirName: string, hasBaseClass: boolean = false) { function analyzeDirective(program: ts.Program, dirName: string, hasBaseClass: boolean = false) {
class TestReflectionHost extends TypeScriptReflectionHost { class TestReflectionHost extends TypeScriptReflectionHost {
constructor(checker: ts.TypeChecker) { super(checker); } constructor(checker: ts.TypeChecker) {
super(checker);
}
hasBaseClass(_class: ClassDeclaration): boolean { return hasBaseClass; } hasBaseClass(_class: ClassDeclaration): boolean {
return hasBaseClass;
}
} }
const checker = program.getTypeChecker(); const checker = program.getTypeChecker();

View File

@ -10,7 +10,7 @@ import {absoluteFrom} from '../../file_system';
import {runInEachFileSystem} from '../../file_system/testing'; import {runInEachFileSystem} from '../../file_system/testing';
import {NOOP_DEFAULT_IMPORT_RECORDER} from '../../imports'; import {NOOP_DEFAULT_IMPORT_RECORDER} from '../../imports';
import {InjectableClassRegistry} from '../../metadata'; import {InjectableClassRegistry} from '../../metadata';
import {TypeScriptReflectionHost, isNamedClassDeclaration} from '../../reflection'; import {isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection';
import {getDeclaration, makeProgram} from '../../testing'; import {getDeclaration, makeProgram} from '../../testing';
import {InjectableDecoratorHandler} from '../src/injectable'; import {InjectableDecoratorHandler} from '../src/injectable';
@ -31,7 +31,7 @@ runInEachFileSystem(() => {
const diag = err.toDiagnostic(); const diag = err.toDiagnostic();
expect(diag.code).toEqual(ngErrorCode(ErrorCode.INJECTABLE_DUPLICATE_PROV)); expect(diag.code).toEqual(ngErrorCode(ErrorCode.INJECTABLE_DUPLICATE_PROV));
expect(diag.file.fileName.endsWith('entry.ts')).toBe(true); expect(diag.file.fileName.endsWith('entry.ts')).toBe(true);
expect(diag.start).toBe(ɵprov.nameNode !.getStart()); expect(diag.start).toBe(ɵprov.nameNode!.getStart());
} }
}); });
@ -43,7 +43,6 @@ runInEachFileSystem(() => {
expect(res).not.toContain(jasmine.objectContaining({name: 'ɵprov'})); expect(res).not.toContain(jasmine.objectContaining({name: 'ɵprov'}));
}); });
}); });
}); });
}); });

View File

@ -6,8 +6,9 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import * as ts from 'typescript'; import * as ts from 'typescript';
import {absoluteFrom, getSourceFileOrError} from '../../file_system'; import {absoluteFrom, getSourceFileOrError} from '../../file_system';
import {TestFile, runInEachFileSystem} from '../../file_system/testing'; import {runInEachFileSystem, TestFile} from '../../file_system/testing';
import {NOOP_DEFAULT_IMPORT_RECORDER, NoopImportRewriter} from '../../imports'; import {NOOP_DEFAULT_IMPORT_RECORDER, NoopImportRewriter} from '../../imports';
import {TypeScriptReflectionHost} from '../../reflection'; import {TypeScriptReflectionHost} from '../../reflection';
import {getDeclaration, makeProgram} from '../../testing'; import {getDeclaration, makeProgram} from '../../testing';

View File

@ -14,7 +14,7 @@ import {runInEachFileSystem} from '../../file_system/testing';
import {LocalIdentifierStrategy, NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../imports'; import {LocalIdentifierStrategy, NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../imports';
import {CompoundMetadataReader, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry} from '../../metadata'; import {CompoundMetadataReader, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry} from '../../metadata';
import {PartialEvaluator} from '../../partial_evaluator'; import {PartialEvaluator} from '../../partial_evaluator';
import {TypeScriptReflectionHost, isNamedClassDeclaration} from '../../reflection'; import {isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection';
import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope'; import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope';
import {getDeclaration, makeProgram} from '../../testing'; import {getDeclaration, makeProgram} from '../../testing';
import {NgModuleDecoratorHandler} from '../src/ng_module'; import {NgModuleDecoratorHandler} from '../src/ng_module';
@ -79,7 +79,7 @@ runInEachFileSystem(() => {
if (detected === undefined) { if (detected === undefined) {
return fail('Failed to recognize @NgModule'); return fail('Failed to recognize @NgModule');
} }
const moduleDef = handler.analyze(TestModule, detected.metadata).analysis !.mod; const moduleDef = handler.analyze(TestModule, detected.metadata).analysis!.mod;
expect(getReferenceIdentifierTexts(moduleDef.declarations)).toEqual(['TestComp']); expect(getReferenceIdentifierTexts(moduleDef.declarations)).toEqual(['TestComp']);
expect(getReferenceIdentifierTexts(moduleDef.exports)).toEqual(['TestComp']); expect(getReferenceIdentifierTexts(moduleDef.exports)).toEqual(['TestComp']);

View File

@ -13,8 +13,9 @@ import {unwrapExpression} from '../src/util';
describe('ngtsc annotation utilities', () => { describe('ngtsc annotation utilities', () => {
describe('unwrapExpression', () => { describe('unwrapExpression', () => {
const obj = ts.createObjectLiteral(); const obj = ts.createObjectLiteral();
it('should pass through an ObjectLiteralExpression', it('should pass through an ObjectLiteralExpression', () => {
() => { expect(unwrapExpression(obj)).toBe(obj); }); expect(unwrapExpression(obj)).toBe(obj);
});
it('should unwrap an ObjectLiteralExpression in parentheses', () => { it('should unwrap an ObjectLiteralExpression in parentheses', () => {
const wrapped = ts.createParen(obj); const wrapped = ts.createParen(obj);

View File

@ -56,7 +56,7 @@ export interface ResourceHost {
* core interface. * core interface.
*/ */
export interface ExtendedTsCompilerHost extends ts.CompilerHost, Partial<ResourceHost>, export interface ExtendedTsCompilerHost extends ts.CompilerHost, Partial<ResourceHost>,
Partial<UnifiedModulesHost> {} Partial<UnifiedModulesHost> {}
export interface LazyRoute { export interface LazyRoute {
route: string; route: string;

View File

@ -36,7 +36,8 @@ export interface TestOnlyOptions {
*/ */
ivyTemplateTypeCheck?: boolean; ivyTemplateTypeCheck?: boolean;
/** An option to enable ngtsc's internal performance tracing. /**
* An option to enable ngtsc's internal performance tracing.
* *
* This should be a path to a JSON file where trace information will be written. An optional 'ts:' * This should be a path to a JSON file where trace information will be written. An optional 'ts:'
* prefix will cause the trace to be written via the TS host instead of directly to the filesystem * prefix will cause the trace to be written via the TS host instead of directly to the filesystem
@ -54,4 +55,5 @@ export interface TestOnlyOptions {
* Also includes a few miscellaneous options. * Also includes a few miscellaneous options.
*/ */
export interface NgCompilerOptions extends ts.CompilerOptions, LegacyNgcOptions, BazelAndG3Options, export interface NgCompilerOptions extends ts.CompilerOptions, LegacyNgcOptions, BazelAndG3Options,
NgcCompatibilityOptions, StrictTemplateOptions, TestOnlyOptions, I18nOptions, MiscOptions {} NgcCompatibilityOptions, StrictTemplateOptions,
TestOnlyOptions, I18nOptions, MiscOptions {}

View File

@ -12,23 +12,23 @@ import * as ts from 'typescript';
import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, NoopReferencesRegistry, PipeDecoratorHandler, ReferencesRegistry} from '../../annotations'; import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, NoopReferencesRegistry, PipeDecoratorHandler, ReferencesRegistry} from '../../annotations';
import {CycleAnalyzer, ImportGraph} from '../../cycles'; import {CycleAnalyzer, ImportGraph} from '../../cycles';
import {ErrorCode, ngErrorCode} from '../../diagnostics'; import {ErrorCode, ngErrorCode} from '../../diagnostics';
import {ReferenceGraph, checkForPrivateExports} from '../../entry_point'; import {checkForPrivateExports, ReferenceGraph} from '../../entry_point';
import {LogicalFileSystem, getSourceFileOrError} from '../../file_system'; import {getSourceFileOrError, LogicalFileSystem} from '../../file_system';
import {AbsoluteModuleStrategy, AliasStrategy, AliasingHost, DefaultImportTracker, ImportRewriter, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NoopImportRewriter, PrivateExportAliasingHost, R3SymbolsImportRewriter, Reference, ReferenceEmitStrategy, ReferenceEmitter, RelativePathStrategy, UnifiedModulesAliasingHost, UnifiedModulesStrategy} from '../../imports'; import {AbsoluteModuleStrategy, AliasingHost, AliasStrategy, DefaultImportTracker, ImportRewriter, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NoopImportRewriter, PrivateExportAliasingHost, R3SymbolsImportRewriter, Reference, ReferenceEmitStrategy, ReferenceEmitter, RelativePathStrategy, UnifiedModulesAliasingHost, UnifiedModulesStrategy} from '../../imports';
import {IncrementalDriver} from '../../incremental'; import {IncrementalDriver} from '../../incremental';
import {IndexedComponent, IndexingContext, generateAnalysis} from '../../indexer'; import {generateAnalysis, IndexedComponent, IndexingContext} from '../../indexer';
import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry, MetadataReader} from '../../metadata'; import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry, MetadataReader} from '../../metadata';
import {ModuleWithProvidersScanner} from '../../modulewithproviders'; import {ModuleWithProvidersScanner} from '../../modulewithproviders';
import {PartialEvaluator} from '../../partial_evaluator'; import {PartialEvaluator} from '../../partial_evaluator';
import {NOOP_PERF_RECORDER, PerfRecorder} from '../../perf'; import {NOOP_PERF_RECORDER, PerfRecorder} from '../../perf';
import {TypeScriptReflectionHost} from '../../reflection'; import {TypeScriptReflectionHost} from '../../reflection';
import {HostResourceLoader} from '../../resource'; import {HostResourceLoader} from '../../resource';
import {NgModuleRouteAnalyzer, entryPointKeyFor} from '../../routing'; import {entryPointKeyFor, NgModuleRouteAnalyzer} from '../../routing';
import {ComponentScopeReader, LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope'; import {ComponentScopeReader, LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope';
import {generatedFactoryTransform} from '../../shims'; import {generatedFactoryTransform} from '../../shims';
import {ivySwitchTransform} from '../../switch'; import {ivySwitchTransform} from '../../switch';
import {DecoratorHandler, DtsTransformRegistry, TraitCompiler, aliasTransformFactory, declarationTransformFactory, ivyTransformFactory} from '../../transform'; import {aliasTransformFactory, declarationTransformFactory, DecoratorHandler, DtsTransformRegistry, ivyTransformFactory, TraitCompiler} from '../../transform';
import {TypeCheckContext, TypeCheckingConfig, isTemplateDiagnostic} from '../../typecheck'; import {isTemplateDiagnostic, TypeCheckContext, TypeCheckingConfig} from '../../typecheck';
import {getSourceFileOrNull, isDtsPath, resolveModuleName} from '../../util/src/typescript'; import {getSourceFileOrNull, isDtsPath, resolveModuleName} from '../../util/src/typescript';
import {LazyRoute, NgCompilerOptions} from '../api'; import {LazyRoute, NgCompilerOptions} from '../api';
@ -191,7 +191,9 @@ export class NgCompiler {
/** /**
* Get all setup-related diagnostics for this compilation. * Get all setup-related diagnostics for this compilation.
*/ */
getOptionDiagnostics(): ts.Diagnostic[] { return this.constructionDiagnostics; } getOptionDiagnostics(): ts.Diagnostic[] {
return this.constructionDiagnostics;
}
/** /**
* Get the `ts.Program` to use as a starting point when spawning a subsequent incremental * Get the `ts.Program` to use as a starting point when spawning a subsequent incremental
@ -202,7 +204,9 @@ export class NgCompiler {
* operation, the consumer's `ts.Program` is no longer usable for starting a new incremental * operation, the consumer's `ts.Program` is no longer usable for starting a new incremental
* compilation. `getNextProgram` retrieves the `ts.Program` which can be used instead. * compilation. `getNextProgram` retrieves the `ts.Program` which can be used instead.
*/ */
getNextProgram(): ts.Program { return this.nextProgram; } getNextProgram(): ts.Program {
return this.nextProgram;
}
/** /**
* Perform Angular's analysis step (as a precursor to `getDiagnostics` or `prepareEmit`) * Perform Angular's analysis step (as a precursor to `getDiagnostics` or `prepareEmit`)
@ -262,8 +266,8 @@ export class NgCompiler {
// Relative entry paths are disallowed. // Relative entry paths are disallowed.
if (entryRoute.startsWith('.')) { if (entryRoute.startsWith('.')) {
throw new Error( throw new Error(`Failed to list lazy routes: Resolution of relative paths (${
`Failed to list lazy routes: Resolution of relative paths (${entryRoute}) is not supported.`); entryRoute}) is not supported.`);
} }
// Non-relative entry paths fall into one of the following categories: // Non-relative entry paths fall into one of the following categories:
@ -349,7 +353,7 @@ export class NgCompiler {
if (this.compilation === null) { if (this.compilation === null) {
this.analyzeSync(); this.analyzeSync();
} }
return this.compilation !; return this.compilation!;
} }
private analyzeSync(): void { private analyzeSync(): void {
@ -482,7 +486,7 @@ export class NgCompiler {
// Execute the typeCheck phase of each decorator in the program. // Execute the typeCheck phase of each decorator in the program.
const prepSpan = this.perfRecorder.start('typeCheckPrep'); const prepSpan = this.perfRecorder.start('typeCheckPrep');
const ctx = new TypeCheckContext( const ctx = new TypeCheckContext(
typeCheckingConfig, compilation.refEmitter !, compilation.reflector, host.typeCheckFile); typeCheckingConfig, compilation.refEmitter!, compilation.reflector, host.typeCheckFile);
compilation.traitCompiler.typeCheck(ctx); compilation.traitCompiler.typeCheck(ctx);
this.perfRecorder.stop(prepSpan); this.perfRecorder.stop(prepSpan);
@ -505,7 +509,7 @@ export class NgCompiler {
const recordSpan = this.perfRecorder.start('recordDependencies'); const recordSpan = this.perfRecorder.start('recordDependencies');
const depGraph = this.incrementalDriver.depGraph; const depGraph = this.incrementalDriver.depGraph;
for (const scope of this.compilation !.scopeRegistry !.getCompilationScopes()) { for (const scope of this.compilation!.scopeRegistry!.getCompilationScopes()) {
const file = scope.declaration.getSourceFile(); const file = scope.declaration.getSourceFile();
const ngModuleFile = scope.ngModule.getSourceFile(); const ngModuleFile = scope.ngModule.getSourceFile();
@ -517,7 +521,7 @@ export class NgCompiler {
depGraph.addDependency(file, ngModuleFile); depGraph.addDependency(file, ngModuleFile);
const meta = const meta =
this.compilation !.metaReader.getDirectiveMetadata(new Reference(scope.declaration)); this.compilation!.metaReader.getDirectiveMetadata(new Reference(scope.declaration));
if (meta !== null && meta.isComponent) { if (meta !== null && meta.isComponent) {
// If a component's template changes, it might have affected the import graph, and thus the // If a component's template changes, it might have affected the import graph, and thus the
// remote scoping feature which is activated in the event of potential import cycles. Thus, // remote scoping feature which is activated in the event of potential import cycles. Thus,
@ -543,12 +547,11 @@ export class NgCompiler {
} }
private scanForMwp(sf: ts.SourceFile): void { private scanForMwp(sf: ts.SourceFile): void {
this.compilation !.mwpScanner.scan(sf, { this.compilation!.mwpScanner.scan(sf, {
addTypeReplacement: (node: ts.Declaration, type: Type): void => { addTypeReplacement: (node: ts.Declaration, type: Type): void => {
// Only obtain the return type transform for the source file once there's a type to replace, // Only obtain the return type transform for the source file once there's a type to replace,
// so that no transform is allocated when there's nothing to do. // so that no transform is allocated when there's nothing to do.
this.compilation !.dtsTransforms !.getReturnTypeTransform(sf).addTypeReplacement( this.compilation!.dtsTransforms!.getReturnTypeTransform(sf).addTypeReplacement(node, type);
node, type);
} }
}); });
} }
@ -686,9 +689,18 @@ export class NgCompiler {
this.options.compileNonExportedClasses !== false, dtsTransforms); this.options.compileNonExportedClasses !== false, dtsTransforms);
return { return {
isCore, traitCompiler, reflector, scopeRegistry, isCore,
dtsTransforms, exportReferenceGraph, routeAnalyzer, mwpScanner, traitCompiler,
metaReader, defaultImportTracker, aliasingHost, refEmitter, reflector,
scopeRegistry,
dtsTransforms,
exportReferenceGraph,
routeAnalyzer,
mwpScanner,
metaReader,
defaultImportTracker,
aliasingHost,
refEmitter,
}; };
} }
} }

View File

@ -9,7 +9,7 @@
import * as ts from 'typescript'; import * as ts from 'typescript';
import {ErrorCode, ngErrorCode} from '../../diagnostics'; import {ErrorCode, ngErrorCode} from '../../diagnostics';
import {FlatIndexGenerator, findFlatIndexEntryPoint} from '../../entry_point'; import {findFlatIndexEntryPoint, FlatIndexGenerator} from '../../entry_point';
import {AbsoluteFsPath, resolve} from '../../file_system'; import {AbsoluteFsPath, resolve} from '../../file_system';
import {FactoryGenerator, FactoryTracker, ShimGenerator, SummaryGenerator, TypeCheckShimGenerator} from '../../shims'; import {FactoryGenerator, FactoryTracker, ShimGenerator, SummaryGenerator, TypeCheckShimGenerator} from '../../shims';
import {typeCheckFilePath} from '../../typecheck'; import {typeCheckFilePath} from '../../typecheck';
@ -88,8 +88,7 @@ export class DelegatingCompilerHost implements
* `ExtendedTsCompilerHost` methods whenever present. * `ExtendedTsCompilerHost` methods whenever present.
*/ */
export class NgCompilerHost extends DelegatingCompilerHost implements export class NgCompilerHost extends DelegatingCompilerHost implements
RequiredCompilerHostDelegations, RequiredCompilerHostDelegations, ExtendedTsCompilerHost {
ExtendedTsCompilerHost {
readonly factoryTracker: FactoryTracker|null = null; readonly factoryTracker: FactoryTracker|null = null;
readonly entryPoint: AbsoluteFsPath|null = null; readonly entryPoint: AbsoluteFsPath|null = null;
readonly diagnostics: ts.Diagnostic[]; readonly diagnostics: ts.Diagnostic[];

View File

@ -8,7 +8,7 @@
import * as ts from 'typescript'; import * as ts from 'typescript';
import {FileSystem, NgtscCompilerHost, absoluteFrom as _, getFileSystem, getSourceFileOrError, setFileSystem} from '../../file_system'; import {absoluteFrom as _, FileSystem, getFileSystem, getSourceFileOrError, NgtscCompilerHost, setFileSystem} from '../../file_system';
import {runInEachFileSystem} from '../../file_system/testing'; import {runInEachFileSystem} from '../../file_system/testing';
import {NgCompilerOptions} from '../api'; import {NgCompilerOptions} from '../api';
import {NgCompiler} from '../src/compiler'; import {NgCompiler} from '../src/compiler';
@ -16,7 +16,6 @@ import {NgCompilerHost} from '../src/host';
runInEachFileSystem(() => { runInEachFileSystem(() => {
describe('NgCompiler', () => { describe('NgCompiler', () => {
let fs: FileSystem; let fs: FileSystem;

View File

@ -30,7 +30,7 @@ export class ImportGraph {
if (!this.map.has(sf)) { if (!this.map.has(sf)) {
this.map.set(sf, this.scanImports(sf)); this.map.set(sf, this.scanImports(sf));
} }
return this.map.get(sf) !; return this.map.get(sf)!;
} }
/** /**
@ -47,7 +47,9 @@ export class ImportGraph {
return; return;
} }
results.add(sf); results.add(sf);
this.importsOf(sf).forEach(imported => { this.transitiveImportsOfHelper(imported, results); }); this.importsOf(sf).forEach(imported => {
this.transitiveImportsOfHelper(imported, results);
});
} }
/** /**

View File

@ -33,7 +33,8 @@ export function makeDiagnostic(code: ErrorCode, node: ts.Node, messageText: stri
code: Number('-99' + code.valueOf()), code: Number('-99' + code.valueOf()),
file: ts.getOriginalNode(node).getSourceFile(), file: ts.getOriginalNode(node).getSourceFile(),
start: node.getStart(undefined, false), start: node.getStart(undefined, false),
length: node.getWidth(), messageText, length: node.getWidth(),
messageText,
}; };
if (relatedInfo !== undefined) { if (relatedInfo !== undefined) {
diag.relatedInformation = relatedInfo.map(info => { diag.relatedInformation = relatedInfo.map(info => {

View File

@ -24,7 +24,9 @@ export class FlatIndexGenerator implements ShimGenerator {
join(dirname(entryPoint), relativeFlatIndexPath).replace(/\.js$/, '') + '.ts'; join(dirname(entryPoint), relativeFlatIndexPath).replace(/\.js$/, '') + '.ts';
} }
recognize(fileName: string): boolean { return fileName === this.flatIndexPath; } recognize(fileName: string): boolean {
return fileName === this.flatIndexPath;
}
generate(): ts.SourceFile { generate(): ts.SourceFile {
const relativeEntryPoint = relativePathBetween(this.flatIndexPath, this.entryPoint); const relativeEntryPoint = relativePathBetween(this.flatIndexPath, this.entryPoint);

View File

@ -95,9 +95,11 @@ export function checkForPrivateExports(
const diagnostic: ts.Diagnostic = { const diagnostic: ts.Diagnostic = {
category: ts.DiagnosticCategory.Error, category: ts.DiagnosticCategory.Error,
code: ngErrorCode(ErrorCode.SYMBOL_NOT_EXPORTED), code: ngErrorCode(ErrorCode.SYMBOL_NOT_EXPORTED),
file: transitiveReference.getSourceFile(), ...getPosOfDeclaration(transitiveReference), file: transitiveReference.getSourceFile(),
messageText: ...getPosOfDeclaration(transitiveReference),
`Unsupported private ${descriptor} ${name}. This ${descriptor} is visible to consumers via ${visibleVia}, but is not exported from the top-level library entrypoint.`, messageText: `Unsupported private ${descriptor} ${name}. This ${
descriptor} is visible to consumers via ${
visibleVia}, but is not exported from the top-level library entrypoint.`,
}; };
diagnostics.push(diagnostic); diagnostics.push(diagnostic);

View File

@ -15,7 +15,7 @@ export class ReferenceGraph<T = ts.Declaration> {
if (!this.references.has(from)) { if (!this.references.has(from)) {
this.references.set(from, new Set()); this.references.set(from, new Set());
} }
this.references.get(from) !.add(to); this.references.get(from)!.add(to);
} }
transitiveReferencesOf(target: T): Set<T> { transitiveReferencesOf(target: T): Set<T> {
@ -47,7 +47,7 @@ export class ReferenceGraph<T = ts.Declaration> {
// Look through the outgoing edges of `source`. // Look through the outgoing edges of `source`.
// TODO(alxhub): use proper iteration when the legacy build is removed. (#27762) // TODO(alxhub): use proper iteration when the legacy build is removed. (#27762)
let candidatePath: T[]|null = null; let candidatePath: T[]|null = null;
this.references.get(source) !.forEach(edge => { this.references.get(source)!.forEach(edge => {
// Early exit if a path has already been found. // Early exit if a path has already been found.
if (candidatePath !== null) { if (candidatePath !== null) {
return; return;
@ -67,7 +67,7 @@ export class ReferenceGraph<T = ts.Declaration> {
private collectTransitiveReferences(set: Set<T>, decl: T): void { private collectTransitiveReferences(set: Set<T>, decl: T): void {
if (this.references.has(decl)) { if (this.references.has(decl)) {
// TODO(alxhub): use proper iteration when the legacy build is removed. (#27762) // TODO(alxhub): use proper iteration when the legacy build is removed. (#27762)
this.references.get(decl) !.forEach(ref => { this.references.get(decl)!.forEach(ref => {
if (!set.has(ref)) { if (!set.has(ref)) {
set.add(ref); set.add(ref);
this.collectTransitiveReferences(set, ref); this.collectTransitiveReferences(set, ref);

View File

@ -16,9 +16,9 @@ runInEachFileSystem(() => {
beforeEach(() => _ = absoluteFrom); beforeEach(() => _ = absoluteFrom);
describe('findFlatIndexEntryPoint', () => { describe('findFlatIndexEntryPoint', () => {
it('should use the only source file if only a single one is specified', () => {
it('should use the only source file if only a single one is specified', expect(findFlatIndexEntryPoint([_('/src/index.ts')])).toBe(_('/src/index.ts'));
() => { expect(findFlatIndexEntryPoint([_('/src/index.ts')])).toBe(_('/src/index.ts')); }); });
it('should use the shortest source file ending with "index.ts" for multiple files', () => { it('should use the shortest source file ending with "index.ts" for multiple files', () => {
expect(findFlatIndexEntryPoint([ expect(findFlatIndexEntryPoint([

View File

@ -12,8 +12,9 @@ import {ReferenceGraph} from '../src/reference_graph';
describe('entry_point reference graph', () => { describe('entry_point reference graph', () => {
let graph: ReferenceGraph<string>; let graph: ReferenceGraph<string>;
const refs = const refs = (target: string) => {
(target: string) => { return Array.from(graph.transitiveReferencesOf(target)).sort(); }; return Array.from(graph.transitiveReferencesOf(target)).sort();
};
beforeEach(() => { beforeEach(() => {
graph = new ReferenceGraph(); graph = new ReferenceGraph();
@ -45,6 +46,7 @@ describe('entry_point reference graph', () => {
expect(graph.pathFrom('beta', 'alpha')).toEqual(['beta', 'delta', 'alpha']); expect(graph.pathFrom('beta', 'alpha')).toEqual(['beta', 'delta', 'alpha']);
}); });
it('should not report a path that doesn\'t exist', it('should not report a path that doesn\'t exist', () => {
() => { expect(graph.pathFrom('gamma', 'beta')).toBeNull(); }); expect(graph.pathFrom('gamma', 'beta')).toBeNull();
});
}); });

View File

@ -25,7 +25,7 @@ export class CachedFileSystem implements FileSystem {
if (!this.existsCache.has(path)) { if (!this.existsCache.has(path)) {
this.existsCache.set(path, this.delegate.exists(path)); this.existsCache.set(path, this.delegate.exists(path));
} }
return this.existsCache.get(path) !; return this.existsCache.get(path)!;
} }
invalidateCaches(path: AbsoluteFsPath) { invalidateCaches(path: AbsoluteFsPath) {
@ -131,15 +131,33 @@ export class CachedFileSystem implements FileSystem {
} }
// The following methods simply call through to the delegate. // The following methods simply call through to the delegate.
readdir(path: AbsoluteFsPath): PathSegment[] { return this.delegate.readdir(path); } readdir(path: AbsoluteFsPath): PathSegment[] {
pwd(): AbsoluteFsPath { return this.delegate.pwd(); } return this.delegate.readdir(path);
chdir(path: AbsoluteFsPath): void { this.delegate.chdir(path); } }
extname(path: AbsoluteFsPath|PathSegment): string { return this.delegate.extname(path); } pwd(): AbsoluteFsPath {
isCaseSensitive(): boolean { return this.delegate.isCaseSensitive(); } return this.delegate.pwd();
isRoot(path: AbsoluteFsPath): boolean { return this.delegate.isRoot(path); } }
isRooted(path: string): boolean { return this.delegate.isRooted(path); } chdir(path: AbsoluteFsPath): void {
resolve(...paths: string[]): AbsoluteFsPath { return this.delegate.resolve(...paths); } this.delegate.chdir(path);
dirname<T extends PathString>(file: T): T { return this.delegate.dirname(file); } }
extname(path: AbsoluteFsPath|PathSegment): string {
return this.delegate.extname(path);
}
isCaseSensitive(): boolean {
return this.delegate.isCaseSensitive();
}
isRoot(path: AbsoluteFsPath): boolean {
return this.delegate.isRoot(path);
}
isRooted(path: string): boolean {
return this.delegate.isRooted(path);
}
resolve(...paths: string[]): AbsoluteFsPath {
return this.delegate.resolve(...paths);
}
dirname<T extends PathString>(file: T): T {
return this.delegate.dirname(file);
}
join<T extends PathString>(basePath: T, ...paths: string[]): T { join<T extends PathString>(basePath: T, ...paths: string[]): T {
return this.delegate.join(basePath, ...paths); return this.delegate.join(basePath, ...paths);
} }
@ -149,7 +167,13 @@ export class CachedFileSystem implements FileSystem {
basename(filePath: string, extension?: string|undefined): PathSegment { basename(filePath: string, extension?: string|undefined): PathSegment {
return this.delegate.basename(filePath, extension); return this.delegate.basename(filePath, extension);
} }
realpath(filePath: AbsoluteFsPath): AbsoluteFsPath { return this.delegate.realpath(filePath); } realpath(filePath: AbsoluteFsPath): AbsoluteFsPath {
getDefaultLibLocation(): AbsoluteFsPath { return this.delegate.getDefaultLibLocation(); } return this.delegate.realpath(filePath);
normalize<T extends PathString>(path: T): T { return this.delegate.normalize(path); } }
getDefaultLibLocation(): AbsoluteFsPath {
return this.delegate.getDefaultLibLocation();
}
normalize<T extends PathString>(path: T): T {
return this.delegate.normalize(path);
}
} }

View File

@ -26,7 +26,9 @@ export class NgtscCompilerHost implements ts.CompilerHost {
return this.fs.join(this.getDefaultLibLocation(), ts.getDefaultLibFileName(options)); return this.fs.join(this.getDefaultLibLocation(), ts.getDefaultLibFileName(options));
} }
getDefaultLibLocation(): string { return this.fs.getDefaultLibLocation(); } getDefaultLibLocation(): string {
return this.fs.getDefaultLibLocation();
}
writeFile( writeFile(
fileName: string, data: string, writeByteOrderMark: boolean, fileName: string, data: string, writeByteOrderMark: boolean,
@ -37,13 +39,17 @@ export class NgtscCompilerHost implements ts.CompilerHost {
this.fs.writeFile(path, data); this.fs.writeFile(path, data);
} }
getCurrentDirectory(): string { return this.fs.pwd(); } getCurrentDirectory(): string {
return this.fs.pwd();
}
getCanonicalFileName(fileName: string): string { getCanonicalFileName(fileName: string): string {
return this.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase(); return this.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
} }
useCaseSensitiveFileNames(): boolean { return this.fs.isCaseSensitive(); } useCaseSensitiveFileNames(): boolean {
return this.fs.isCaseSensitive();
}
getNewLine(): string { getNewLine(): string {
switch (this.options.newLine) { switch (this.options.newLine) {

View File

@ -16,32 +16,84 @@ import {AbsoluteFsPath, FileStats, FileSystem, PathSegment, PathString} from './
* the `FileSystem` under the hood. * the `FileSystem` under the hood.
*/ */
export class InvalidFileSystem implements FileSystem { export class InvalidFileSystem implements FileSystem {
exists(path: AbsoluteFsPath): boolean { throw makeError(); } exists(path: AbsoluteFsPath): boolean {
readFile(path: AbsoluteFsPath): string { throw makeError(); } throw makeError();
writeFile(path: AbsoluteFsPath, data: string, exclusive?: boolean): void { throw makeError(); } }
removeFile(path: AbsoluteFsPath): void { throw makeError(); } readFile(path: AbsoluteFsPath): string {
symlink(target: AbsoluteFsPath, path: AbsoluteFsPath): void { throw makeError(); } throw makeError();
readdir(path: AbsoluteFsPath): PathSegment[] { throw makeError(); } }
lstat(path: AbsoluteFsPath): FileStats { throw makeError(); } writeFile(path: AbsoluteFsPath, data: string, exclusive?: boolean): void {
stat(path: AbsoluteFsPath): FileStats { throw makeError(); } throw makeError();
pwd(): AbsoluteFsPath { throw makeError(); } }
chdir(path: AbsoluteFsPath): void { throw makeError(); } removeFile(path: AbsoluteFsPath): void {
extname(path: AbsoluteFsPath|PathSegment): string { throw makeError(); } throw makeError();
copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { throw makeError(); } }
moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { throw makeError(); } symlink(target: AbsoluteFsPath, path: AbsoluteFsPath): void {
ensureDir(path: AbsoluteFsPath): void { throw makeError(); } throw makeError();
removeDeep(path: AbsoluteFsPath): void { throw makeError(); } }
isCaseSensitive(): boolean { throw makeError(); } readdir(path: AbsoluteFsPath): PathSegment[] {
resolve(...paths: string[]): AbsoluteFsPath { throw makeError(); } throw makeError();
dirname<T extends PathString>(file: T): T { throw makeError(); } }
join<T extends PathString>(basePath: T, ...paths: string[]): T { throw makeError(); } lstat(path: AbsoluteFsPath): FileStats {
isRoot(path: AbsoluteFsPath): boolean { throw makeError(); } throw makeError();
isRooted(path: string): boolean { throw makeError(); } }
relative<T extends PathString>(from: T, to: T): PathSegment { throw makeError(); } stat(path: AbsoluteFsPath): FileStats {
basename(filePath: string, extension?: string): PathSegment { throw makeError(); } throw makeError();
realpath(filePath: AbsoluteFsPath): AbsoluteFsPath { throw makeError(); } }
getDefaultLibLocation(): AbsoluteFsPath { throw makeError(); } pwd(): AbsoluteFsPath {
normalize<T extends PathString>(path: T): T { throw makeError(); } throw makeError();
}
chdir(path: AbsoluteFsPath): void {
throw makeError();
}
extname(path: AbsoluteFsPath|PathSegment): string {
throw makeError();
}
copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void {
throw makeError();
}
moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void {
throw makeError();
}
ensureDir(path: AbsoluteFsPath): void {
throw makeError();
}
removeDeep(path: AbsoluteFsPath): void {
throw makeError();
}
isCaseSensitive(): boolean {
throw makeError();
}
resolve(...paths: string[]): AbsoluteFsPath {
throw makeError();
}
dirname<T extends PathString>(file: T): T {
throw makeError();
}
join<T extends PathString>(basePath: T, ...paths: string[]): T {
throw makeError();
}
isRoot(path: AbsoluteFsPath): boolean {
throw makeError();
}
isRooted(path: string): boolean {
throw makeError();
}
relative<T extends PathString>(from: T, to: T): PathSegment {
throw makeError();
}
basename(filePath: string, extension?: string): PathSegment {
throw makeError();
}
realpath(filePath: AbsoluteFsPath): AbsoluteFsPath {
throw makeError();
}
getDefaultLibLocation(): AbsoluteFsPath {
throw makeError();
}
normalize<T extends PathString>(path: T): T {
throw makeError();
}
} }
function makeError() { function makeError() {

View File

@ -91,7 +91,7 @@ export class LogicalFileSystem {
} }
this.cache.set(physicalFile, logicalFile); this.cache.set(physicalFile, logicalFile);
} }
return this.cache.get(physicalFile) !; return this.cache.get(physicalFile)!;
} }
private createLogicalProjectPath(file: AbsoluteFsPath, rootDir: AbsoluteFsPath): private createLogicalProjectPath(file: AbsoluteFsPath, rootDir: AbsoluteFsPath):

View File

@ -17,20 +17,42 @@ import {AbsoluteFsPath, FileStats, FileSystem, PathSegment, PathString} from './
*/ */
export class NodeJSFileSystem implements FileSystem { export class NodeJSFileSystem implements FileSystem {
private _caseSensitive: boolean|undefined = undefined; private _caseSensitive: boolean|undefined = undefined;
exists(path: AbsoluteFsPath): boolean { return fs.existsSync(path); } exists(path: AbsoluteFsPath): boolean {
readFile(path: AbsoluteFsPath): string { return fs.readFileSync(path, 'utf8'); } return fs.existsSync(path);
}
readFile(path: AbsoluteFsPath): string {
return fs.readFileSync(path, 'utf8');
}
writeFile(path: AbsoluteFsPath, data: string, exclusive: boolean = false): void { writeFile(path: AbsoluteFsPath, data: string, exclusive: boolean = false): void {
fs.writeFileSync(path, data, exclusive ? {flag: 'wx'} : undefined); fs.writeFileSync(path, data, exclusive ? {flag: 'wx'} : undefined);
} }
removeFile(path: AbsoluteFsPath): void { fs.unlinkSync(path); } removeFile(path: AbsoluteFsPath): void {
symlink(target: AbsoluteFsPath, path: AbsoluteFsPath): void { fs.symlinkSync(target, path); } fs.unlinkSync(path);
readdir(path: AbsoluteFsPath): PathSegment[] { return fs.readdirSync(path) as PathSegment[]; } }
lstat(path: AbsoluteFsPath): FileStats { return fs.lstatSync(path); } symlink(target: AbsoluteFsPath, path: AbsoluteFsPath): void {
stat(path: AbsoluteFsPath): FileStats { return fs.statSync(path); } fs.symlinkSync(target, path);
pwd(): AbsoluteFsPath { return this.normalize(process.cwd()) as AbsoluteFsPath; } }
chdir(dir: AbsoluteFsPath): void { process.chdir(dir); } readdir(path: AbsoluteFsPath): PathSegment[] {
copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { fs.copyFileSync(from, to); } return fs.readdirSync(path) as PathSegment[];
moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { fs.renameSync(from, to); } }
lstat(path: AbsoluteFsPath): FileStats {
return fs.lstatSync(path);
}
stat(path: AbsoluteFsPath): FileStats {
return fs.statSync(path);
}
pwd(): AbsoluteFsPath {
return this.normalize(process.cwd()) as AbsoluteFsPath;
}
chdir(dir: AbsoluteFsPath): void {
process.chdir(dir);
}
copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void {
fs.copyFileSync(from, to);
}
moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void {
fs.renameSync(from, to);
}
ensureDir(path: AbsoluteFsPath): void { ensureDir(path: AbsoluteFsPath): void {
const parents: AbsoluteFsPath[] = []; const parents: AbsoluteFsPath[] = [];
while (!this.isRoot(path) && !this.exists(path)) { while (!this.isRoot(path) && !this.exists(path)) {
@ -38,10 +60,12 @@ export class NodeJSFileSystem implements FileSystem {
path = this.dirname(path); path = this.dirname(path);
} }
while (parents.length) { while (parents.length) {
this.safeMkdir(parents.pop() !); this.safeMkdir(parents.pop()!);
} }
} }
removeDeep(path: AbsoluteFsPath): void { fsExtra.removeSync(path); } removeDeep(path: AbsoluteFsPath): void {
fsExtra.removeSync(path);
}
isCaseSensitive(): boolean { isCaseSensitive(): boolean {
if (this._caseSensitive === undefined) { if (this._caseSensitive === undefined) {
this._caseSensitive = this.exists(togglePathCase(__filename)); this._caseSensitive = this.exists(togglePathCase(__filename));
@ -52,20 +76,30 @@ export class NodeJSFileSystem implements FileSystem {
return this.normalize(p.resolve(...paths)) as AbsoluteFsPath; return this.normalize(p.resolve(...paths)) as AbsoluteFsPath;
} }
dirname<T extends string>(file: T): T { return this.normalize(p.dirname(file)) as T; } dirname<T extends string>(file: T): T {
return this.normalize(p.dirname(file)) as T;
}
join<T extends string>(basePath: T, ...paths: string[]): T { join<T extends string>(basePath: T, ...paths: string[]): T {
return this.normalize(p.join(basePath, ...paths)) as T; return this.normalize(p.join(basePath, ...paths)) as T;
} }
isRoot(path: AbsoluteFsPath): boolean { return this.dirname(path) === this.normalize(path); } isRoot(path: AbsoluteFsPath): boolean {
isRooted(path: string): boolean { return p.isAbsolute(path); } return this.dirname(path) === this.normalize(path);
}
isRooted(path: string): boolean {
return p.isAbsolute(path);
}
relative<T extends PathString>(from: T, to: T): PathSegment { relative<T extends PathString>(from: T, to: T): PathSegment {
return relativeFrom(this.normalize(p.relative(from, to))); return relativeFrom(this.normalize(p.relative(from, to)));
} }
basename(filePath: string, extension?: string): PathSegment { basename(filePath: string, extension?: string): PathSegment {
return p.basename(filePath, extension) as PathSegment; return p.basename(filePath, extension) as PathSegment;
} }
extname(path: AbsoluteFsPath|PathSegment): string { return p.extname(path); } extname(path: AbsoluteFsPath|PathSegment): string {
realpath(path: AbsoluteFsPath): AbsoluteFsPath { return this.resolve(fs.realpathSync(path)); } return p.extname(path);
}
realpath(path: AbsoluteFsPath): AbsoluteFsPath {
return this.resolve(fs.realpathSync(path));
}
getDefaultLibLocation(): AbsoluteFsPath { getDefaultLibLocation(): AbsoluteFsPath {
return this.resolve(require.resolve('typescript'), '..'); return this.resolve(require.resolve('typescript'), '..');
} }

View File

@ -12,7 +12,7 @@
* A `string` is not assignable to a `BrandedPath`, but a `BrandedPath` is assignable to a `string`. * A `string` is not assignable to a `BrandedPath`, but a `BrandedPath` is assignable to a `string`.
* Two `BrandedPath`s with different brands are not mutually assignable. * Two `BrandedPath`s with different brands are not mutually assignable.
*/ */
export type BrandedPath<B extends string> = string & { export type BrandedPath<B extends string> = string&{
_brand: B; _brand: B;
}; };
@ -63,7 +63,7 @@ export interface FileSystem {
normalize<T extends PathString>(path: T): T; normalize<T extends PathString>(path: T): T;
} }
export type PathString = string | AbsoluteFsPath | PathSegment; export type PathString = string|AbsoluteFsPath|PathSegment;
/** /**
* Information about an object in the FileSystem. * Information about an object in the FileSystem.

View File

@ -28,8 +28,8 @@ export function stripExtension(path: string): string {
export function getSourceFileOrError(program: ts.Program, fileName: AbsoluteFsPath): ts.SourceFile { export function getSourceFileOrError(program: ts.Program, fileName: AbsoluteFsPath): ts.SourceFile {
const sf = program.getSourceFile(fileName); const sf = program.getSourceFile(fileName);
if (sf === undefined) { if (sf === undefined) {
throw new Error( throw new Error(`Program does not contain "${fileName}" - available files are ${
`Program does not contain "${fileName}" - available files are ${program.getSourceFiles().map(sf => sf.fileName).join(', ')}`); program.getSourceFiles().map(sf => sf.fileName).join(', ')}`);
} }
return sf; return sf;
} }

View File

@ -11,37 +11,49 @@ import {absoluteFrom, relativeFrom, setFileSystem} from '../src/helpers';
import {NodeJSFileSystem} from '../src/node_js_file_system'; import {NodeJSFileSystem} from '../src/node_js_file_system';
describe('path types', () => { describe('path types', () => {
beforeEach(() => { setFileSystem(new NodeJSFileSystem()); }); beforeEach(() => {
setFileSystem(new NodeJSFileSystem());
});
describe('absoluteFrom', () => { describe('absoluteFrom', () => {
it('should not throw when creating one from an absolute path', it('should not throw when creating one from an absolute path', () => {
() => { expect(() => absoluteFrom('/test.txt')).not.toThrow(); }); expect(() => absoluteFrom('/test.txt')).not.toThrow();
});
if (os.platform() === 'win32') { if (os.platform() === 'win32') {
it('should not throw when creating one from a windows absolute path', it('should not throw when creating one from a windows absolute path', () => {
() => { expect(absoluteFrom('C:\\test.txt')).toEqual('C:/test.txt'); }); expect(absoluteFrom('C:\\test.txt')).toEqual('C:/test.txt');
});
it('should not throw when creating one from a windows absolute path with POSIX separators', it('should not throw when creating one from a windows absolute path with POSIX separators',
() => { expect(absoluteFrom('C:/test.txt')).toEqual('C:/test.txt'); }); () => {
it('should support windows drive letters', expect(absoluteFrom('C:/test.txt')).toEqual('C:/test.txt');
() => { expect(absoluteFrom('D:\\foo\\test.txt')).toEqual('D:/foo/test.txt'); }); });
it('should convert Windows path separators to POSIX separators', it('should support windows drive letters', () => {
() => { expect(absoluteFrom('C:\\foo\\test.txt')).toEqual('C:/foo/test.txt'); }); expect(absoluteFrom('D:\\foo\\test.txt')).toEqual('D:/foo/test.txt');
});
it('should convert Windows path separators to POSIX separators', () => {
expect(absoluteFrom('C:\\foo\\test.txt')).toEqual('C:/foo/test.txt');
});
} }
it('should throw when creating one from a non-absolute path', it('should throw when creating one from a non-absolute path', () => {
() => { expect(() => absoluteFrom('test.txt')).toThrow(); }); expect(() => absoluteFrom('test.txt')).toThrow();
});
}); });
describe('relativeFrom', () => { describe('relativeFrom', () => {
it('should not throw when creating one from a relative path', it('should not throw when creating one from a relative path', () => {
() => { expect(() => relativeFrom('a/b/c.txt')).not.toThrow(); }); expect(() => relativeFrom('a/b/c.txt')).not.toThrow();
});
it('should throw when creating one from an absolute path', it('should throw when creating one from an absolute path', () => {
() => { expect(() => relativeFrom('/a/b/c.txt')).toThrow(); }); expect(() => relativeFrom('/a/b/c.txt')).toThrow();
});
if (os.platform() === 'win32') { if (os.platform() === 'win32') {
it('should throw when creating one from a Windows absolute path', it('should throw when creating one from a Windows absolute path', () => {
() => { expect(() => relativeFrom('C:/a/b/c.txt')).toThrow(); }); expect(() => relativeFrom('C:/a/b/c.txt')).toThrow();
});
} }
}); });
}); });

View File

@ -10,4 +10,4 @@ export {Folder, MockFileSystem} from './src/mock_file_system';
export {MockFileSystemNative} from './src/mock_file_system_native'; export {MockFileSystemNative} from './src/mock_file_system_native';
export {MockFileSystemPosix} from './src/mock_file_system_posix'; export {MockFileSystemPosix} from './src/mock_file_system_posix';
export {MockFileSystemWindows} from './src/mock_file_system_windows'; export {MockFileSystemWindows} from './src/mock_file_system_windows';
export {TestFile, initMockFileSystem, runInEachFileSystem} from './src/test_helper'; export {initMockFileSystem, runInEachFileSystem, TestFile} from './src/test_helper';

View File

@ -21,9 +21,13 @@ export abstract class MockFileSystem implements FileSystem {
this._cwd = this.normalize(cwd); this._cwd = this.normalize(cwd);
} }
isCaseSensitive() { return this._isCaseSensitive; } isCaseSensitive() {
return this._isCaseSensitive;
}
exists(path: AbsoluteFsPath): boolean { return this.findFromPath(path).entity !== null; } exists(path: AbsoluteFsPath): boolean {
return this.findFromPath(path).entity !== null;
}
readFile(path: AbsoluteFsPath): string { readFile(path: AbsoluteFsPath): string {
const {entity} = this.findFromPath(path); const {entity} = this.findFromPath(path);
@ -142,7 +146,9 @@ export abstract class MockFileSystem implements FileSystem {
delete entity[basename]; delete entity[basename];
} }
isRoot(path: AbsoluteFsPath): boolean { return this.dirname(path) === path; } isRoot(path: AbsoluteFsPath): boolean {
return this.dirname(path) === path;
}
extname(path: AbsoluteFsPath|PathSegment): string { extname(path: AbsoluteFsPath|PathSegment): string {
const match = /.+(\.[^.]*)$/.exec(path); const match = /.+(\.[^.]*)$/.exec(path);
@ -159,9 +165,13 @@ export abstract class MockFileSystem implements FileSystem {
} }
} }
pwd(): AbsoluteFsPath { return this._cwd; } pwd(): AbsoluteFsPath {
return this._cwd;
}
chdir(path: AbsoluteFsPath): void { this._cwd = this.normalize(path); } chdir(path: AbsoluteFsPath): void {
this._cwd = this.normalize(path);
}
getDefaultLibLocation(): AbsoluteFsPath { getDefaultLibLocation(): AbsoluteFsPath {
// Mimic the node module resolution algorithm and start in the current directory, then look // Mimic the node module resolution algorithm and start in the current directory, then look
@ -201,8 +211,12 @@ export abstract class MockFileSystem implements FileSystem {
abstract normalize<T extends PathString>(path: T): T; abstract normalize<T extends PathString>(path: T): T;
protected abstract splitPath<T extends PathString>(path: T): string[]; protected abstract splitPath<T extends PathString>(path: T): string[];
dump(): Folder { return cloneFolder(this._fileTree); } dump(): Folder {
init(folder: Folder): void { this._fileTree = cloneFolder(folder); } return cloneFolder(this._fileTree);
}
init(folder: Folder): void {
this._fileTree = cloneFolder(folder);
}
protected findFromPath(path: AbsoluteFsPath, options?: {followSymLinks: boolean}): FindResult { protected findFromPath(path: AbsoluteFsPath, options?: {followSymLinks: boolean}): FindResult {
const followSymLinks = !!options && options.followSymLinks; const followSymLinks = !!options && options.followSymLinks;
@ -215,7 +229,7 @@ export abstract class MockFileSystem implements FileSystem {
segments[0] = ''; segments[0] = '';
let current: Entity|null = this._fileTree; let current: Entity|null = this._fileTree;
while (segments.length) { while (segments.length) {
current = current[segments.shift() !]; current = current[segments.shift()!];
if (current === undefined) { if (current === undefined) {
return {path, entity: null}; return {path, entity: null};
} }
@ -239,7 +253,7 @@ export abstract class MockFileSystem implements FileSystem {
protected splitIntoFolderAndFile(path: AbsoluteFsPath): [AbsoluteFsPath, string] { protected splitIntoFolderAndFile(path: AbsoluteFsPath): [AbsoluteFsPath, string] {
const segments = this.splitPath(path); const segments = this.splitPath(path);
const file = segments.pop() !; const file = segments.pop()!;
return [path.substring(0, path.length - file.length - 1) as AbsoluteFsPath, file]; return [path.substring(0, path.length - file.length - 1) as AbsoluteFsPath, file];
} }
} }
@ -247,8 +261,10 @@ export interface FindResult {
path: AbsoluteFsPath; path: AbsoluteFsPath;
entity: Entity|null; entity: Entity|null;
} }
export type Entity = Folder | File | SymLink; export type Entity = Folder|File|SymLink;
export interface Folder { [pathSegments: string]: Entity; } export interface Folder {
[pathSegments: string]: Entity;
}
export type File = string; export type File = string;
export class SymLink { export class SymLink {
constructor(public path: AbsoluteFsPath) {} constructor(public path: AbsoluteFsPath) {}
@ -256,24 +272,32 @@ export class SymLink {
class MockFileStats implements FileStats { class MockFileStats implements FileStats {
constructor(private entity: Entity) {} constructor(private entity: Entity) {}
isFile(): boolean { return isFile(this.entity); } isFile(): boolean {
isDirectory(): boolean { return isFolder(this.entity); } return isFile(this.entity);
isSymbolicLink(): boolean { return isSymLink(this.entity); } }
isDirectory(): boolean {
return isFolder(this.entity);
}
isSymbolicLink(): boolean {
return isSymLink(this.entity);
}
} }
class MockFileSystemError extends Error { class MockFileSystemError extends Error {
constructor(public code: string, public path: string, message: string) { super(message); } constructor(public code: string, public path: string, message: string) {
super(message);
}
} }
export function isFile(item: Entity | null): item is File { export function isFile(item: Entity|null): item is File {
return typeof item === 'string'; return typeof item === 'string';
} }
export function isSymLink(item: Entity | null): item is SymLink { export function isSymLink(item: Entity|null): item is SymLink {
return item instanceof SymLink; return item instanceof SymLink;
} }
export function isFolder(item: Entity | null): item is Folder { export function isFolder(item: Entity|null): item is Folder {
return item !== null && !isFile(item) && !isSymLink(item); return item !== null && !isFile(item) && !isSymLink(item);
} }

View File

@ -15,7 +15,9 @@ import {MockFileSystem} from './mock_file_system';
const isWindows = os.platform() === 'win32'; const isWindows = os.platform() === 'win32';
export class MockFileSystemNative extends MockFileSystem { export class MockFileSystemNative extends MockFileSystem {
constructor(cwd: AbsoluteFsPath = '/' as AbsoluteFsPath) { super(undefined, cwd); } constructor(cwd: AbsoluteFsPath = '/' as AbsoluteFsPath) {
super(undefined, cwd);
}
// Delegate to the real NodeJSFileSystem for these path related methods // Delegate to the real NodeJSFileSystem for these path related methods
@ -36,9 +38,13 @@ export class MockFileSystemNative extends MockFileSystem {
return NodeJSFileSystem.prototype.basename.call(this, filePath, extension); return NodeJSFileSystem.prototype.basename.call(this, filePath, extension);
} }
isCaseSensitive() { return NodeJSFileSystem.prototype.isCaseSensitive.call(this); } isCaseSensitive() {
return NodeJSFileSystem.prototype.isCaseSensitive.call(this);
}
isRooted(path: string): boolean { return NodeJSFileSystem.prototype.isRooted.call(this, path); } isRooted(path: string): boolean {
return NodeJSFileSystem.prototype.isRooted.call(this, path);
}
isRoot(path: AbsoluteFsPath): boolean { isRoot(path: AbsoluteFsPath): boolean {
return NodeJSFileSystem.prototype.isRoot.call(this, path); return NodeJSFileSystem.prototype.isRoot.call(this, path);
@ -57,5 +63,7 @@ export class MockFileSystemNative extends MockFileSystem {
return NodeJSFileSystem.prototype.normalize.call(this, path) as T; return NodeJSFileSystem.prototype.normalize.call(this, path) as T;
} }
protected splitPath<T>(path: string): string[] { return path.split(/[\\\/]/); } protected splitPath<T>(path: string): string[] {
return path.split(/[\\\/]/);
}
} }

View File

@ -17,7 +17,9 @@ export class MockFileSystemPosix extends MockFileSystem {
return this.normalize(resolved) as AbsoluteFsPath; return this.normalize(resolved) as AbsoluteFsPath;
} }
dirname<T extends string>(file: T): T { return this.normalize(p.posix.dirname(file)) as T; } dirname<T extends string>(file: T): T {
return this.normalize(p.posix.dirname(file)) as T;
}
join<T extends string>(basePath: T, ...paths: string[]): T { join<T extends string>(basePath: T, ...paths: string[]): T {
return this.normalize(p.posix.join(basePath, ...paths)) as T; return this.normalize(p.posix.join(basePath, ...paths)) as T;
@ -31,9 +33,13 @@ export class MockFileSystemPosix extends MockFileSystem {
return p.posix.basename(filePath, extension) as PathSegment; return p.posix.basename(filePath, extension) as PathSegment;
} }
isRooted(path: string): boolean { return path.startsWith('/'); } isRooted(path: string): boolean {
return path.startsWith('/');
}
protected splitPath<T extends PathString>(path: T): string[] { return path.split('/'); } protected splitPath<T extends PathString>(path: T): string[] {
return path.split('/');
}
normalize<T extends PathString>(path: T): T { normalize<T extends PathString>(path: T): T {
return path.replace(/^[a-z]:\//i, '/').replace(/\\/g, '/') as T; return path.replace(/^[a-z]:\//i, '/').replace(/\\/g, '/') as T;

View File

@ -17,7 +17,9 @@ export class MockFileSystemWindows extends MockFileSystem {
return this.normalize(resolved as AbsoluteFsPath); return this.normalize(resolved as AbsoluteFsPath);
} }
dirname<T extends string>(path: T): T { return this.normalize(p.win32.dirname(path) as T); } dirname<T extends string>(path: T): T {
return this.normalize(p.win32.dirname(path) as T);
}
join<T extends string>(basePath: T, ...paths: string[]): T { join<T extends string>(basePath: T, ...paths: string[]): T {
return this.normalize(p.win32.join(basePath, ...paths)) as T; return this.normalize(p.win32.join(basePath, ...paths)) as T;
@ -31,9 +33,13 @@ export class MockFileSystemWindows extends MockFileSystem {
return p.win32.basename(filePath, extension) as PathSegment; return p.win32.basename(filePath, extension) as PathSegment;
} }
isRooted(path: string): boolean { return /^([A-Z]:)?([\\\/]|$)/i.test(path); } isRooted(path: string): boolean {
return /^([A-Z]:)?([\\\/]|$)/i.test(path);
}
protected splitPath<T extends PathString>(path: T): string[] { return path.split(/[\\\/]/); } protected splitPath<T extends PathString>(path: T): string[] {
return path.split(/[\\\/]/);
}
normalize<T extends PathString>(path: T): T { normalize<T extends PathString>(path: T): T {
return path.replace(/^[\/\\]/i, 'C:/').replace(/\\/g, '/') as T; return path.replace(/^[\/\\]/i, 'C:/').replace(/\\/g, '/') as T;

View File

@ -47,7 +47,9 @@ function runInFileSystem(os: string, callback: (os: string) => void, error: bool
afterEach(() => setFileSystem(new InvalidFileSystem())); afterEach(() => setFileSystem(new InvalidFileSystem()));
callback(os); callback(os);
if (error) { if (error) {
afterAll(() => { throw new Error(`runInFileSystem limited to ${os}, cannot pass`); }); afterAll(() => {
throw new Error(`runInFileSystem limited to ${os}, cannot pass`);
});
} }
}); });
} }
@ -125,14 +127,16 @@ function monkeyPatchTypeScript(os: string, fs: MockFileSystem) {
return {files, directories}; return {files, directories};
} }
function realPath(path: string): string { return fs.realpath(fs.resolve(path)); } function realPath(path: string): string {
return fs.realpath(fs.resolve(path));
}
// Rather than completely re-implementing we are using the `ts.matchFiles` function, // Rather than completely re-implementing we are using the `ts.matchFiles` function,
// which is internal to the `ts` namespace. // which is internal to the `ts` namespace.
const tsMatchFiles: ( const tsMatchFiles: (
path: string, extensions: ReadonlyArray<string>| undefined, path: string, extensions: ReadonlyArray<string>|undefined,
excludes: ReadonlyArray<string>| undefined, includes: ReadonlyArray<string>| undefined, excludes: ReadonlyArray<string>|undefined, includes: ReadonlyArray<string>|undefined,
useCaseSensitiveFileNames: boolean, currentDirectory: string, depth: number | undefined, useCaseSensitiveFileNames: boolean, currentDirectory: string, depth: number|undefined,
getFileSystemEntries: (path: string) => FileSystemEntries, getFileSystemEntries: (path: string) => FileSystemEntries,
realpath: (path: string) => string) => string[] = (ts as any).matchFiles; realpath: (path: string) => string) => string[] = (ts as any).matchFiles;

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
export {AliasStrategy, AliasingHost, PrivateExportAliasingHost, UnifiedModulesAliasingHost} from './src/alias'; export {AliasingHost, AliasStrategy, PrivateExportAliasingHost, UnifiedModulesAliasingHost} from './src/alias';
export {ImportRewriter, NoopImportRewriter, R3SymbolsImportRewriter, validateAndRewriteCoreSymbol} from './src/core'; export {ImportRewriter, NoopImportRewriter, R3SymbolsImportRewriter, validateAndRewriteCoreSymbol} from './src/core';
export {DefaultImportRecorder, DefaultImportTracker, NOOP_DEFAULT_IMPORT_RECORDER} from './src/default'; export {DefaultImportRecorder, DefaultImportTracker, NOOP_DEFAULT_IMPORT_RECORDER} from './src/default';
export {AbsoluteModuleStrategy, ImportFlags, LocalIdentifierStrategy, LogicalProjectStrategy, ReferenceEmitStrategy, ReferenceEmitter, RelativePathStrategy, UnifiedModulesStrategy} from './src/emitter'; export {AbsoluteModuleStrategy, ImportFlags, LocalIdentifierStrategy, LogicalProjectStrategy, ReferenceEmitStrategy, ReferenceEmitter, RelativePathStrategy, UnifiedModulesStrategy} from './src/emitter';

View File

@ -10,7 +10,7 @@ import {Expression, ExternalExpr} from '@angular/compiler';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {UnifiedModulesHost} from '../../core/api'; import {UnifiedModulesHost} from '../../core/api';
import {ClassDeclaration, ReflectionHost, isNamedClassDeclaration} from '../../reflection'; import {ClassDeclaration, isNamedClassDeclaration, ReflectionHost} from '../../reflection';
import {ImportFlags, ReferenceEmitStrategy} from './emitter'; import {ImportFlags, ReferenceEmitStrategy} from './emitter';
import {Reference} from './references'; import {Reference} from './references';
@ -203,7 +203,9 @@ export class PrivateExportAliasingHost implements AliasingHost {
* *
* Thus, `getAliasIn` always returns `null`. * Thus, `getAliasIn` always returns `null`.
*/ */
getAliasIn(): null { return null; } getAliasIn(): null {
return null;
}
} }
/** /**

View File

@ -36,11 +36,17 @@ export interface ImportRewriter {
* `ImportRewriter` that does no rewriting. * `ImportRewriter` that does no rewriting.
*/ */
export class NoopImportRewriter implements ImportRewriter { export class NoopImportRewriter implements ImportRewriter {
shouldImportSymbol(symbol: string, specifier: string): boolean { return true; } shouldImportSymbol(symbol: string, specifier: string): boolean {
return true;
}
rewriteSymbol(symbol: string, specifier: string): string { return symbol; } rewriteSymbol(symbol: string, specifier: string): string {
return symbol;
}
rewriteSpecifier(specifier: string, inContextOfFile: string): string { return specifier; } rewriteSpecifier(specifier: string, inContextOfFile: string): string {
return specifier;
}
} }
/** /**
@ -70,7 +76,9 @@ const CORE_MODULE = '@angular/core';
export class R3SymbolsImportRewriter implements ImportRewriter { export class R3SymbolsImportRewriter implements ImportRewriter {
constructor(private r3SymbolsPath: string) {} constructor(private r3SymbolsPath: string) {}
shouldImportSymbol(symbol: string, specifier: string): boolean { return true; } shouldImportSymbol(symbol: string, specifier: string): boolean {
return true;
}
rewriteSymbol(symbol: string, specifier: string): string { rewriteSymbol(symbol: string, specifier: string): string {
if (specifier !== CORE_MODULE) { if (specifier !== CORE_MODULE) {
@ -89,8 +97,8 @@ export class R3SymbolsImportRewriter implements ImportRewriter {
const relativePathToR3Symbols = relativePathBetween(inContextOfFile, this.r3SymbolsPath); const relativePathToR3Symbols = relativePathBetween(inContextOfFile, this.r3SymbolsPath);
if (relativePathToR3Symbols === null) { if (relativePathToR3Symbols === null) {
throw new Error( throw new Error(`Failed to rewrite import inside ${CORE_MODULE}: ${inContextOfFile} -> ${
`Failed to rewrite import inside ${CORE_MODULE}: ${inContextOfFile} -> ${this.r3SymbolsPath}`); this.r3SymbolsPath}`);
} }
return relativePathToR3Symbols; return relativePathToR3Symbols;
@ -101,5 +109,5 @@ export function validateAndRewriteCoreSymbol(name: string): string {
if (!CORE_SUPPORTED_SYMBOLS.has(name)) { if (!CORE_SUPPORTED_SYMBOLS.has(name)) {
throw new Error(`Importing unexpected symbol ${name} while compiling ${CORE_MODULE}`); throw new Error(`Importing unexpected symbol ${name} while compiling ${CORE_MODULE}`);
} }
return CORE_SUPPORTED_SYMBOLS.get(name) !; return CORE_SUPPORTED_SYMBOLS.get(name)!;
} }

View File

@ -1,10 +1,10 @@
/** /**
* @license * @license
* Copyright Google Inc. All Rights Reserved. * Copyright Google Inc. All Rights Reserved.
* *
* Use of this source code is governed by an MIT-style license that can be * 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 * found in the LICENSE file at https://angular.io/license
*/ */
import * as ts from 'typescript'; import * as ts from 'typescript';
@ -96,7 +96,7 @@ export class DefaultImportTracker implements DefaultImportRecorder {
if (!this.sourceFileToImportMap.has(sf)) { if (!this.sourceFileToImportMap.has(sf)) {
this.sourceFileToImportMap.set(sf, new Map<ts.Identifier, ts.ImportDeclaration>()); this.sourceFileToImportMap.set(sf, new Map<ts.Identifier, ts.ImportDeclaration>());
} }
this.sourceFileToImportMap.get(sf) !.set(id, decl); this.sourceFileToImportMap.get(sf)!.set(id, decl);
} }
recordUsedIdentifier(id: ts.Identifier): void { recordUsedIdentifier(id: ts.Identifier): void {
@ -105,18 +105,18 @@ export class DefaultImportTracker implements DefaultImportRecorder {
// The identifier's source file has no registered default imports at all. // The identifier's source file has no registered default imports at all.
return; return;
} }
const identiferToDeclaration = this.sourceFileToImportMap.get(sf) !; const identiferToDeclaration = this.sourceFileToImportMap.get(sf)!;
if (!identiferToDeclaration.has(id)) { if (!identiferToDeclaration.has(id)) {
// The identifier isn't from a registered default import. // The identifier isn't from a registered default import.
return; return;
} }
const decl = identiferToDeclaration.get(id) !; const decl = identiferToDeclaration.get(id)!;
// Add the default import declaration to the set of used import declarations for the file. // Add the default import declaration to the set of used import declarations for the file.
if (!this.sourceFileToUsedImports.has(sf)) { if (!this.sourceFileToUsedImports.has(sf)) {
this.sourceFileToUsedImports.set(sf, new Set<ts.ImportDeclaration>()); this.sourceFileToUsedImports.set(sf, new Set<ts.ImportDeclaration>());
} }
this.sourceFileToUsedImports.get(sf) !.add(decl); this.sourceFileToUsedImports.get(sf)!.add(decl);
} }
/** /**
@ -127,7 +127,9 @@ export class DefaultImportTracker implements DefaultImportRecorder {
*/ */
importPreservingTransformer(): ts.TransformerFactory<ts.SourceFile> { importPreservingTransformer(): ts.TransformerFactory<ts.SourceFile> {
return (context: ts.TransformationContext) => { return (context: ts.TransformationContext) => {
return (sf: ts.SourceFile) => { return this.transformSourceFile(sf); }; return (sf: ts.SourceFile) => {
return this.transformSourceFile(sf);
};
}; };
} }
@ -142,7 +144,7 @@ export class DefaultImportTracker implements DefaultImportRecorder {
} }
// There are declarations that need to be preserved. // There are declarations that need to be preserved.
const importsToPreserve = this.sourceFileToUsedImports.get(originalSf) !; const importsToPreserve = this.sourceFileToUsedImports.get(originalSf)!;
// Generate a new statement list which preserves any imports present in `importsToPreserve`. // Generate a new statement list which preserves any imports present in `importsToPreserve`.
const statements = sf.statements.map(stmt => { const statements = sf.statements.map(stmt => {

View File

@ -9,7 +9,7 @@ import {Expression, ExternalExpr, ExternalReference, WrappedNodeExpr} from '@ang
import * as ts from 'typescript'; import * as ts from 'typescript';
import {UnifiedModulesHost} from '../../core/api'; import {UnifiedModulesHost} from '../../core/api';
import {LogicalFileSystem, LogicalProjectPath, PathSegment, absoluteFromSourceFile, dirname, relative} from '../../file_system'; import {absoluteFromSourceFile, dirname, LogicalFileSystem, LogicalProjectPath, PathSegment, relative} from '../../file_system';
import {stripExtension} from '../../file_system/src/util'; import {stripExtension} from '../../file_system/src/util';
import {ReflectionHost} from '../../reflection'; import {ReflectionHost} from '../../reflection';
import {getSourceFile, isDeclaration, isTypeDeclaration, nodeNameForError} from '../../util/src/typescript'; import {getSourceFile, isDeclaration, isTypeDeclaration, nodeNameForError} from '../../util/src/typescript';
@ -93,8 +93,8 @@ export class ReferenceEmitter {
return emitted; return emitted;
} }
} }
throw new Error( throw new Error(`Unable to write a reference to ${nodeNameForError(ref.node)} in ${
`Unable to write a reference to ${nodeNameForError(ref.node)} in ${ref.node.getSourceFile().fileName} from ${context.fileName}`); ref.node.getSourceFile().fileName} from ${context.fileName}`);
} }
} }
@ -149,11 +149,11 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy {
return null; return null;
} else if (!isDeclaration(ref.node)) { } else if (!isDeclaration(ref.node)) {
// It's not possible to import something which isn't a declaration. // It's not possible to import something which isn't a declaration.
throw new Error( throw new Error(`Debug assert: unable to import a Reference to non-declaration of type ${
`Debug assert: unable to import a Reference to non-declaration of type ${ts.SyntaxKind[ref.node.kind]}.`); ts.SyntaxKind[ref.node.kind]}.`);
} else if ((importFlags & ImportFlags.AllowTypeImports) === 0 && isTypeDeclaration(ref.node)) { } else if ((importFlags & ImportFlags.AllowTypeImports) === 0 && isTypeDeclaration(ref.node)) {
throw new Error( throw new Error(`Importing a type-only declaration of type ${
`Importing a type-only declaration of type ${ts.SyntaxKind[ref.node.kind]} in a value position is not allowed.`); ts.SyntaxKind[ref.node.kind]} in a value position is not allowed.`);
} }
// Try to find the exported name of the declaration, if one is available. // Try to find the exported name of the declaration, if one is available.
@ -162,8 +162,9 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy {
if (symbolName === null) { if (symbolName === null) {
// TODO(alxhub): make this error a ts.Diagnostic pointing at whatever caused this import to be // TODO(alxhub): make this error a ts.Diagnostic pointing at whatever caused this import to be
// triggered. // triggered.
throw new Error( throw new Error(`Symbol ${ref.debugName} declared in ${
`Symbol ${ref.debugName} declared in ${getSourceFile(ref.node).fileName} is not exported from ${specifier} (import into ${context.fileName})`); getSourceFile(ref.node).fileName} is not exported from ${specifier} (import into ${
context.fileName})`);
} }
return new ExternalExpr(new ExternalReference(specifier, symbolName)); return new ExternalExpr(new ExternalReference(specifier, symbolName));
@ -173,7 +174,7 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy {
|null { |null {
const exports = this.getExportsOfModule(moduleName, fromFile); const exports = this.getExportsOfModule(moduleName, fromFile);
if (exports !== null && exports.has(target)) { if (exports !== null && exports.has(target)) {
return exports.get(target) !; return exports.get(target)!;
} else { } else {
return null; return null;
} }
@ -184,7 +185,7 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy {
if (!this.moduleExportsCache.has(moduleName)) { if (!this.moduleExportsCache.has(moduleName)) {
this.moduleExportsCache.set(moduleName, this.enumerateExportsOfModule(moduleName, fromFile)); this.moduleExportsCache.set(moduleName, this.enumerateExportsOfModule(moduleName, fromFile));
} }
return this.moduleExportsCache.get(moduleName) !; return this.moduleExportsCache.get(moduleName)!;
} }
protected enumerateExportsOfModule(specifier: string, fromFile: string): protected enumerateExportsOfModule(specifier: string, fromFile: string):

View File

@ -80,7 +80,9 @@ export class Reference<T extends ts.Node = ts.Node> {
* *
* See `bestGuessOwningModule`. * See `bestGuessOwningModule`.
*/ */
get hasOwningModuleGuess(): boolean { return this.bestGuessOwningModule !== null; } get hasOwningModuleGuess(): boolean {
return this.bestGuessOwningModule !== null;
}
/** /**
* A name for the node, if one is available. * A name for the node, if one is available.
@ -93,14 +95,18 @@ export class Reference<T extends ts.Node = ts.Node> {
return id !== null ? id.text : null; return id !== null ? id.text : null;
} }
get alias(): Expression|null { return this._alias; } get alias(): Expression|null {
return this._alias;
}
/** /**
* Record a `ts.Identifier` by which it's valid to refer to this node, within the context of this * Record a `ts.Identifier` by which it's valid to refer to this node, within the context of this
* `Reference`. * `Reference`.
*/ */
addIdentifier(identifier: ts.Identifier): void { this.identifiers.push(identifier); } addIdentifier(identifier: ts.Identifier): void {
this.identifiers.push(identifier);
}
/** /**
* Get a `ts.Identifier` within this `Reference` that can be used to refer within the context of a * Get a `ts.Identifier` within this `Reference` that can be used to refer within the context of a

View File

@ -39,7 +39,7 @@ runInEachFileSystem(() => {
module: ts.ModuleKind.ES2015, module: ts.ModuleKind.ES2015,
}); });
const fooClause = getDeclaration(program, _('/test.ts'), 'Foo', ts.isImportClause); const fooClause = getDeclaration(program, _('/test.ts'), 'Foo', ts.isImportClause);
const fooId = fooClause.name !; const fooId = fooClause.name!;
const fooDecl = fooClause.parent; const fooDecl = fooClause.parent;
const tracker = new DefaultImportTracker(); const tracker = new DefaultImportTracker();
@ -48,7 +48,7 @@ runInEachFileSystem(() => {
program.emit(undefined, undefined, undefined, undefined, { program.emit(undefined, undefined, undefined, undefined, {
before: [tracker.importPreservingTransformer()], before: [tracker.importPreservingTransformer()],
}); });
const testContents = host.readFile('/test.js') !; const testContents = host.readFile('/test.js')!;
expect(testContents).toContain(`import Foo from './dep';`); expect(testContents).toContain(`import Foo from './dep';`);
// The control should have the import elided. // The control should have the import elided.
@ -69,7 +69,7 @@ runInEachFileSystem(() => {
module: ts.ModuleKind.CommonJS, module: ts.ModuleKind.CommonJS,
}); });
const fooClause = getDeclaration(program, _('/test.ts'), 'Foo', ts.isImportClause); const fooClause = getDeclaration(program, _('/test.ts'), 'Foo', ts.isImportClause);
const fooId = ts.updateIdentifier(fooClause.name !); const fooId = ts.updateIdentifier(fooClause.name!);
const fooDecl = fooClause.parent; const fooDecl = fooClause.parent;
const tracker = new DefaultImportTracker(); const tracker = new DefaultImportTracker();
@ -81,7 +81,7 @@ runInEachFileSystem(() => {
tracker.importPreservingTransformer(), tracker.importPreservingTransformer(),
], ],
}); });
const testContents = host.readFile('/test.js') !; const testContents = host.readFile('/test.js')!;
expect(testContents).toContain(`var dep_1 = require("./dep");`); expect(testContents).toContain(`var dep_1 = require("./dep");`);
expect(testContents).toContain(`var ref = dep_1["default"];`); expect(testContents).toContain(`var ref = dep_1["default"];`);
}); });

View File

@ -8,8 +8,8 @@
import {ExternalExpr} from '@angular/compiler'; import {ExternalExpr} from '@angular/compiler';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {LogicalFileSystem, absoluteFrom as _} from '../../file_system'; import {absoluteFrom as _, LogicalFileSystem} from '../../file_system';
import {TestFile, runInEachFileSystem} from '../../file_system/testing'; import {runInEachFileSystem, TestFile} from '../../file_system/testing';
import {Declaration, TypeScriptReflectionHost} from '../../reflection'; import {Declaration, TypeScriptReflectionHost} from '../../reflection';
import {getDeclaration, makeProgram} from '../../testing'; import {getDeclaration, makeProgram} from '../../testing';
import {AbsoluteModuleStrategy, ImportFlags, LogicalProjectStrategy} from '../src/emitter'; import {AbsoluteModuleStrategy, ImportFlags, LogicalProjectStrategy} from '../src/emitter';
@ -42,7 +42,7 @@ runInEachFileSystem(() => {
]); ]);
const decl = const decl =
getDeclaration(program, _('/node_modules/external.d.ts'), 'Foo', ts.isClassDeclaration); getDeclaration(program, _('/node_modules/external.d.ts'), 'Foo', ts.isClassDeclaration);
const context = program.getSourceFile(_('/context.ts')) !; const context = program.getSourceFile(_('/context.ts'))!;
const reference = new Reference(decl); const reference = new Reference(decl);
const emitted = strategy.emit(reference, context, ImportFlags.None); const emitted = strategy.emit(reference, context, ImportFlags.None);
@ -65,7 +65,7 @@ runInEachFileSystem(() => {
]); ]);
const decl = const decl =
getDeclaration(program, _('/node_modules/external.d.ts'), 'Foo', ts.isClassDeclaration); getDeclaration(program, _('/node_modules/external.d.ts'), 'Foo', ts.isClassDeclaration);
const context = program.getSourceFile(_('/context.ts')) !; const context = program.getSourceFile(_('/context.ts'))!;
const reference = new Reference(decl, { const reference = new Reference(decl, {
specifier: 'external', specifier: 'external',
@ -92,7 +92,7 @@ runInEachFileSystem(() => {
]); ]);
const decl = getDeclaration( const decl = getDeclaration(
program, _('/node_modules/external.d.ts'), 'Foo', ts.isInterfaceDeclaration); program, _('/node_modules/external.d.ts'), 'Foo', ts.isInterfaceDeclaration);
const context = program.getSourceFile(_('/context.ts')) !; const context = program.getSourceFile(_('/context.ts'))!;
const reference = new Reference(decl, { const reference = new Reference(decl, {
specifier: 'external', specifier: 'external',
@ -116,7 +116,7 @@ runInEachFileSystem(() => {
]); ]);
const decl = getDeclaration( const decl = getDeclaration(
program, _('/node_modules/external.d.ts'), 'Foo', ts.isInterfaceDeclaration); program, _('/node_modules/external.d.ts'), 'Foo', ts.isInterfaceDeclaration);
const context = program.getSourceFile(_('/context.ts')) !; const context = program.getSourceFile(_('/context.ts'))!;
const reference = const reference =
new Reference(decl, {specifier: 'external', resolutionContext: context.fileName}); new Reference(decl, {specifier: 'external', resolutionContext: context.fileName});
@ -139,7 +139,9 @@ runInEachFileSystem(() => {
return null; return null;
} }
const fakeExports = new Map<string, Declaration>(); const fakeExports = new Map<string, Declaration>();
realExports.forEach((decl, name) => { fakeExports.set(`test${name}`, decl); }); realExports.forEach((decl, name) => {
fakeExports.set(`test${name}`, decl);
});
return fakeExports; return fakeExports;
} }
} }
@ -158,12 +160,12 @@ runInEachFileSystem(() => {
const logicalFs = new LogicalFileSystem([_('/')]); const logicalFs = new LogicalFileSystem([_('/')]);
const strategy = new LogicalProjectStrategy(new TestHost(checker), logicalFs); const strategy = new LogicalProjectStrategy(new TestHost(checker), logicalFs);
const decl = getDeclaration(program, _('/index.ts'), 'Foo', ts.isClassDeclaration); const decl = getDeclaration(program, _('/index.ts'), 'Foo', ts.isClassDeclaration);
const context = program.getSourceFile(_('/context.ts')) !; const context = program.getSourceFile(_('/context.ts'))!;
const ref = strategy.emit(new Reference(decl), context); const ref = strategy.emit(new Reference(decl), context);
expect(ref).not.toBeNull(); expect(ref).not.toBeNull();
// Expect the prefixed name from the TestHost. // Expect the prefixed name from the TestHost.
expect((ref !as ExternalExpr).value.name).toEqual('testFoo'); expect((ref! as ExternalExpr).value.name).toEqual('testFoo');
}); });
}); });
}); });

View File

@ -25,7 +25,7 @@ export interface IncrementalBuild<W> {
/** /**
* Tracks dependencies between source files or resources in the application. * Tracks dependencies between source files or resources in the application.
*/ */
export interface DependencyTracker<T extends{fileName: string} = ts.SourceFile> { export interface DependencyTracker<T extends {fileName: string} = ts.SourceFile> {
/** /**
* Record that the file `from` depends on the file `on`. * Record that the file `from` depends on the file `on`.
*/ */

View File

@ -23,11 +23,13 @@ import {DependencyTracker} from '../api';
* 2. One of its dependencies has physically changed. * 2. One of its dependencies has physically changed.
* 3. One of its resource dependencies has physically changed. * 3. One of its resource dependencies has physically changed.
*/ */
export class FileDependencyGraph<T extends{fileName: string} = ts.SourceFile> implements export class FileDependencyGraph<T extends {fileName: string} = ts.SourceFile> implements
DependencyTracker<T> { DependencyTracker<T> {
private nodes = new Map<T, FileNode>(); private nodes = new Map<T, FileNode>();
addDependency(from: T, on: T): void { this.nodeFor(from).dependsOn.add(on.fileName); } addDependency(from: T, on: T): void {
this.nodeFor(from).dependsOn.add(on.fileName);
}
addResourceDependency(from: T, resource: AbsoluteFsPath): void { addResourceDependency(from: T, resource: AbsoluteFsPath): void {
this.nodeFor(from).usesResources.add(resource); this.nodeFor(from).usesResources.add(resource);
@ -103,7 +105,7 @@ export class FileDependencyGraph<T extends{fileName: string} = ts.SourceFile> im
usesResources: new Set<AbsoluteFsPath>(), usesResources: new Set<AbsoluteFsPath>(),
}); });
} }
return this.nodes.get(sf) !; return this.nodes.get(sf)!;
} }
} }
@ -111,7 +113,7 @@ export class FileDependencyGraph<T extends{fileName: string} = ts.SourceFile> im
* Determine whether `sf` has logically changed, given its dependencies and the set of physically * Determine whether `sf` has logically changed, given its dependencies and the set of physically
* changed files and resources. * changed files and resources.
*/ */
function isLogicallyChanged<T extends{fileName: string}>( function isLogicallyChanged<T extends {fileName: string}>(
sf: T, node: FileNode, changedTsPaths: ReadonlySet<string>, deletedTsPaths: ReadonlySet<string>, sf: T, node: FileNode, changedTsPaths: ReadonlySet<string>, deletedTsPaths: ReadonlySet<string>,
changedResources: ReadonlySet<AbsoluteFsPath>): boolean { changedResources: ReadonlySet<AbsoluteFsPath>): boolean {
// A file is logically changed if it has physically changed itself (including being deleted). // A file is logically changed if it has physically changed itself (including being deleted).

View File

@ -8,7 +8,7 @@
import * as ts from 'typescript'; import * as ts from 'typescript';
import {AbsoluteFsPath, absoluteFrom} from '../../file_system'; import {absoluteFrom, AbsoluteFsPath} from '../../file_system';
import {ClassRecord, TraitCompiler} from '../../transform'; import {ClassRecord, TraitCompiler} from '../../transform';
import {IncrementalBuild} from '../api'; import {IncrementalBuild} from '../api';
@ -194,9 +194,13 @@ export class IncrementalDriver implements IncrementalBuild<ClassRecord> {
}; };
} }
recordSuccessfulEmit(sf: ts.SourceFile): void { this.state.pendingEmit.delete(sf.fileName); } recordSuccessfulEmit(sf: ts.SourceFile): void {
this.state.pendingEmit.delete(sf.fileName);
}
safeToSkipEmit(sf: ts.SourceFile): boolean { return !this.state.pendingEmit.has(sf.fileName); } safeToSkipEmit(sf: ts.SourceFile): boolean {
return !this.state.pendingEmit.has(sf.fileName);
}
priorWorkFor(sf: ts.SourceFile): ClassRecord[]|null { priorWorkFor(sf: ts.SourceFile): ClassRecord[]|null {
if (this.state.lastGood === null || this.logicalChanges === null) { if (this.state.lastGood === null || this.logicalChanges === null) {
@ -212,7 +216,7 @@ export class IncrementalDriver implements IncrementalBuild<ClassRecord> {
} }
} }
type BuildState = PendingBuildState | AnalyzedBuildState; type BuildState = PendingBuildState|AnalyzedBuildState;
enum BuildStateKind { enum BuildStateKind {
Pending, Pending,

View File

@ -43,13 +43,19 @@ interface ExpressionIdentifier extends TemplateIdentifier {
} }
/** Describes a property accessed in a template. */ /** Describes a property accessed in a template. */
export interface PropertyIdentifier extends ExpressionIdentifier { kind: IdentifierKind.Property; } export interface PropertyIdentifier extends ExpressionIdentifier {
kind: IdentifierKind.Property;
}
/** Describes a method accessed in a template. */ /** Describes a method accessed in a template. */
export interface MethodIdentifier extends ExpressionIdentifier { kind: IdentifierKind.Method; } export interface MethodIdentifier extends ExpressionIdentifier {
kind: IdentifierKind.Method;
}
/** Describes an element attribute in a template. */ /** Describes an element attribute in a template. */
export interface AttributeIdentifier extends TemplateIdentifier { kind: IdentifierKind.Attribute; } export interface AttributeIdentifier extends TemplateIdentifier {
kind: IdentifierKind.Attribute;
}
/** A reference to a directive node and its selector. */ /** A reference to a directive node and its selector. */
interface DirectiveReference { interface DirectiveReference {
@ -85,7 +91,7 @@ export interface ReferenceIdentifier extends TemplateIdentifier {
/** The target of this reference. If the target is not known, this is `null`. */ /** The target of this reference. If the target is not known, this is `null`. */
target: { target: {
/** The template AST node that the reference targets. */ /** The template AST node that the reference targets. */
node: ElementIdentifier | TemplateIdentifier; node: ElementIdentifier|TemplateIdentifier;
/** /**
* The directive on `node` that the reference targets. If no directive is targeted, this is * The directive on `node` that the reference targets. If no directive is targeted, this is
@ -96,14 +102,16 @@ export interface ReferenceIdentifier extends TemplateIdentifier {
} }
/** Describes a template variable like "foo" in `<div *ngFor="let foo of foos"></div>`. */ /** Describes a template variable like "foo" in `<div *ngFor="let foo of foos"></div>`. */
export interface VariableIdentifier extends TemplateIdentifier { kind: IdentifierKind.Variable; } export interface VariableIdentifier extends TemplateIdentifier {
kind: IdentifierKind.Variable;
}
/** /**
* Identifiers recorded at the top level of the template, without any context about the HTML nodes * Identifiers recorded at the top level of the template, without any context about the HTML nodes
* they were discovered in. * they were discovered in.
*/ */
export type TopLevelIdentifier = PropertyIdentifier | MethodIdentifier | ElementIdentifier | export type TopLevelIdentifier = PropertyIdentifier|MethodIdentifier|ElementIdentifier|
TemplateNodeIdentifier | ReferenceIdentifier | VariableIdentifier; TemplateNodeIdentifier|ReferenceIdentifier|VariableIdentifier;
/** /**
* Describes the absolute byte offsets of a text anchor in a source code. * Describes the absolute byte offsets of a text anchor in a source code.

View File

@ -56,5 +56,7 @@ export class IndexingContext {
/** /**
* Adds a component to the context. * Adds a component to the context.
*/ */
addComponent(info: ComponentInfo) { this.components.add(info); } addComponent(info: ComponentInfo) {
this.components.add(info);
}
} }

View File

@ -18,9 +18,9 @@ interface HTMLNode extends TmplAstNode {
name?: string; name?: string;
} }
type ExpressionIdentifier = PropertyIdentifier | MethodIdentifier; type ExpressionIdentifier = PropertyIdentifier|MethodIdentifier;
type TmplTarget = TmplAstReference | TmplAstVariable; type TmplTarget = TmplAstReference|TmplAstVariable;
type TargetIdentifier = ReferenceIdentifier | VariableIdentifier; type TargetIdentifier = ReferenceIdentifier|VariableIdentifier;
type TargetIdentifierMap = Map<TmplTarget, TargetIdentifier>; type TargetIdentifierMap = Map<TmplTarget, TargetIdentifier>;
/** /**
@ -62,7 +62,9 @@ class ExpressionVisitor extends RecursiveAstVisitor {
return visitor.identifiers; return visitor.identifiers;
} }
visit(ast: AST) { ast.visit(this); } visit(ast: AST) {
ast.visit(this);
}
visitMethodCall(ast: MethodCall, context: {}) { visitMethodCall(ast: MethodCall, context: {}) {
this.visitIdentifier(ast, IdentifierKind.Method); this.visitIdentifier(ast, IdentifierKind.Method);
@ -144,16 +146,22 @@ class TemplateVisitor extends TmplAstRecursiveVisitor {
* *
* @param boundTemplate bound template target * @param boundTemplate bound template target
*/ */
constructor(private boundTemplate: BoundTarget<ComponentMeta>) { super(); } constructor(private boundTemplate: BoundTarget<ComponentMeta>) {
super();
}
/** /**
* Visits a node in the template. * Visits a node in the template.
* *
* @param node node to visit * @param node node to visit
*/ */
visit(node: HTMLNode) { node.visit(this); } visit(node: HTMLNode) {
node.visit(this);
}
visitAll(nodes: TmplAstNode[]) { nodes.forEach(node => this.visit(node)); } visitAll(nodes: TmplAstNode[]) {
nodes.forEach(node => this.visit(node));
}
/** /**
* Add an identifier for an HTML element and visit its children recursively. * Add an identifier for an HTML element and visit its children recursively.
@ -204,8 +212,12 @@ class TemplateVisitor extends TmplAstRecursiveVisitor {
this.targetToIdentifier.bind(this)); this.targetToIdentifier.bind(this));
identifiers.forEach(id => this.identifiers.add(id)); identifiers.forEach(id => this.identifiers.add(id));
} }
visitBoundEvent(attribute: TmplAstBoundEvent) { this.visitExpression(attribute.handler); } visitBoundEvent(attribute: TmplAstBoundEvent) {
visitBoundText(text: TmplAstBoundText) { this.visitExpression(text.value); } this.visitExpression(attribute.handler);
}
visitBoundText(text: TmplAstBoundText) {
this.visitExpression(text.value);
}
visitReference(reference: TmplAstReference) { visitReference(reference: TmplAstReference) {
const referenceIdentifer = this.targetToIdentifier(reference); const referenceIdentifer = this.targetToIdentifier(reference);
@ -222,7 +234,7 @@ class TemplateVisitor extends TmplAstRecursiveVisitor {
|TemplateNodeIdentifier { |TemplateNodeIdentifier {
// If this node has already been seen, return the cached result. // If this node has already been seen, return the cached result.
if (this.elementAndTemplateIdentifierCache.has(node)) { if (this.elementAndTemplateIdentifierCache.has(node)) {
return this.elementAndTemplateIdentifierCache.get(node) !; return this.elementAndTemplateIdentifierCache.get(node)!;
} }
let name: string; let name: string;
@ -254,7 +266,8 @@ class TemplateVisitor extends TmplAstRecursiveVisitor {
const identifier = { const identifier = {
name, name,
span: absoluteSpan, kind, span: absoluteSpan,
kind,
attributes: new Set(attributes), attributes: new Set(attributes),
usedDirectives: new Set(usedDirectives.map(dir => { usedDirectives: new Set(usedDirectives.map(dir => {
return { return {
@ -274,7 +287,7 @@ class TemplateVisitor extends TmplAstRecursiveVisitor {
private targetToIdentifier(node: TmplAstReference|TmplAstVariable): TargetIdentifier { private targetToIdentifier(node: TmplAstReference|TmplAstVariable): TargetIdentifier {
// If this node has already been seen, return the cached result. // If this node has already been seen, return the cached result.
if (this.targetIdentifierCache.has(node)) { if (this.targetIdentifierCache.has(node)) {
return this.targetIdentifierCache.get(node) !; return this.targetIdentifierCache.get(node)!;
} }
const {name, sourceSpan} = node; const {name, sourceSpan} = node;
@ -304,7 +317,8 @@ class TemplateVisitor extends TmplAstRecursiveVisitor {
identifier = { identifier = {
name, name,
span, span,
kind: IdentifierKind.Reference, target, kind: IdentifierKind.Reference,
target,
}; };
} else { } else {
identifier = { identifier = {

View File

@ -19,7 +19,8 @@ runInEachFileSystem(() => {
context.addComponent({ context.addComponent({
declaration, declaration,
selector: 'c-selector', boundTemplate, selector: 'c-selector',
boundTemplate,
templateMeta: { templateMeta: {
isInline: false, isInline: false,
file: new ParseSourceFile('<div></div>', util.getTestFilePath()), file: new ParseSourceFile('<div></div>', util.getTestFilePath()),
@ -29,7 +30,8 @@ runInEachFileSystem(() => {
expect(context.components).toEqual(new Set([ expect(context.components).toEqual(new Set([
{ {
declaration, declaration,
selector: 'c-selector', boundTemplate, selector: 'c-selector',
boundTemplate,
templateMeta: { templateMeta: {
isInline: false, isInline: false,
file: new ParseSourceFile('<div></div>', util.getTestFilePath()), file: new ParseSourceFile('<div></div>', util.getTestFilePath()),

View File

@ -219,11 +219,11 @@ runInEachFileSystem(() => {
const refArr = Array.from(refs); const refArr = Array.from(refs);
expect(refArr).toEqual(jasmine.arrayContaining([{ expect(refArr).toEqual(jasmine.arrayContaining([{
name: 'foo', name: 'foo',
kind: IdentifierKind.Property, kind: IdentifierKind.Property,
span: new AbsoluteSourceSpan(20, 23), span: new AbsoluteSourceSpan(20, 23),
target: null, target: null,
}] as TopLevelIdentifier[])); }] as TopLevelIdentifier[]));
}); });
it('should ignore property writes that are not implicitly received by the template', () => { it('should ignore property writes that are not implicitly received by the template', () => {
@ -314,12 +314,13 @@ runInEachFileSystem(() => {
}; };
const refArray = Array.from(refs); const refArray = Array.from(refs);
expect(refArray).toEqual(jasmine.arrayContaining([{ expect(refArray).toEqual(
name: 'foo', jasmine.arrayContaining([{
kind: IdentifierKind.Reference, name: 'foo',
span: new AbsoluteSourceSpan(6, 9), kind: IdentifierKind.Reference,
target: {node: elementReference, directive: null}, span: new AbsoluteSourceSpan(6, 9),
}] as TopLevelIdentifier[])); target: {node: elementReference, directive: null},
}] as TopLevelIdentifier[]));
}); });
it('should discover nested references', () => { it('should discover nested references', () => {
@ -334,12 +335,13 @@ runInEachFileSystem(() => {
}; };
const refArray = Array.from(refs); const refArray = Array.from(refs);
expect(refArray).toEqual(jasmine.arrayContaining([{ expect(refArray).toEqual(
name: 'foo', jasmine.arrayContaining([{
kind: IdentifierKind.Reference, name: 'foo',
span: new AbsoluteSourceSpan(12, 15), kind: IdentifierKind.Reference,
target: {node: elementReference, directive: null}, span: new AbsoluteSourceSpan(12, 15),
}] as TopLevelIdentifier[])); target: {node: elementReference, directive: null},
}] as TopLevelIdentifier[]));
}); });
it('should discover references to references', () => { it('should discover references to references', () => {
@ -409,14 +411,14 @@ runInEachFileSystem(() => {
const refArr = Array.from(refs); const refArr = Array.from(refs);
let fooRef = refArr.find(id => id.name === 'foo'); let fooRef = refArr.find(id => id.name === 'foo');
expect(fooRef).toBeDefined(); expect(fooRef).toBeDefined();
expect(fooRef !.kind).toBe(IdentifierKind.Reference); expect(fooRef!.kind).toBe(IdentifierKind.Reference);
fooRef = fooRef as ReferenceIdentifier; fooRef = fooRef as ReferenceIdentifier;
expect(fooRef.target).toBeDefined(); expect(fooRef.target).toBeDefined();
expect(fooRef.target !.node.kind).toBe(IdentifierKind.Element); expect(fooRef.target!.node.kind).toBe(IdentifierKind.Element);
expect(fooRef.target !.node.name).toBe('div'); expect(fooRef.target!.node.name).toBe('div');
expect(fooRef.target !.node.span).toEqual(new AbsoluteSourceSpan(1, 4)); expect(fooRef.target!.node.span).toEqual(new AbsoluteSourceSpan(1, 4));
expect(fooRef.target !.directive).toEqual(declB); expect(fooRef.target!.directive).toEqual(declB);
}); });
it('should discover references to references', () => { it('should discover references to references', () => {
@ -455,10 +457,10 @@ runInEachFileSystem(() => {
const refArray = Array.from(refs); const refArray = Array.from(refs);
expect(refArray).toEqual(jasmine.arrayContaining([{ expect(refArray).toEqual(jasmine.arrayContaining([{
name: 'foo', name: 'foo',
kind: IdentifierKind.Variable, kind: IdentifierKind.Variable,
span: new AbsoluteSourceSpan(17, 20), span: new AbsoluteSourceSpan(17, 20),
}] as TopLevelIdentifier[])); }] as TopLevelIdentifier[]));
}); });
it('should discover variables with let- syntax', () => { it('should discover variables with let- syntax', () => {
@ -467,10 +469,10 @@ runInEachFileSystem(() => {
const refArray = Array.from(refs); const refArray = Array.from(refs);
expect(refArray).toEqual(jasmine.arrayContaining([{ expect(refArray).toEqual(jasmine.arrayContaining([{
name: 'var', name: 'var',
kind: IdentifierKind.Variable, kind: IdentifierKind.Variable,
span: new AbsoluteSourceSpan(17, 20), span: new AbsoluteSourceSpan(17, 20),
}] as TopLevelIdentifier[])); }] as TopLevelIdentifier[]));
}); });
it('should discover nested variables', () => { it('should discover nested variables', () => {
@ -479,10 +481,10 @@ runInEachFileSystem(() => {
const refArray = Array.from(refs); const refArray = Array.from(refs);
expect(refArray).toEqual(jasmine.arrayContaining([{ expect(refArray).toEqual(jasmine.arrayContaining([{
name: 'foo', name: 'foo',
kind: IdentifierKind.Variable, kind: IdentifierKind.Variable,
span: new AbsoluteSourceSpan(23, 26), span: new AbsoluteSourceSpan(23, 26),
}] as TopLevelIdentifier[])); }] as TopLevelIdentifier[]));
}); });
it('should discover references to variables', () => { it('should discover references to variables', () => {

View File

@ -68,7 +68,7 @@ runInEachFileSystem(() => {
const info = analysis.get(decl); const info = analysis.get(decl);
expect(info).toBeDefined(); expect(info).toBeDefined();
expect(info !.template.file) expect(info!.template.file)
.toEqual(new ParseSourceFile('class C {}', util.getTestFilePath())); .toEqual(new ParseSourceFile('class C {}', util.getTestFilePath()));
}); });
@ -83,7 +83,7 @@ runInEachFileSystem(() => {
const info = analysis.get(decl); const info = analysis.get(decl);
expect(info).toBeDefined(); expect(info).toBeDefined();
expect(info !.template.file) expect(info!.template.file)
.toEqual(new ParseSourceFile('<div>{{foo}}</div>', util.getTestFilePath())); .toEqual(new ParseSourceFile('<div>{{foo}}</div>', util.getTestFilePath()));
}); });
@ -110,11 +110,11 @@ runInEachFileSystem(() => {
const infoA = analysis.get(declA); const infoA = analysis.get(declA);
expect(infoA).toBeDefined(); expect(infoA).toBeDefined();
expect(infoA !.template.usedComponents).toEqual(new Set([declB])); expect(infoA!.template.usedComponents).toEqual(new Set([declB]));
const infoB = analysis.get(declB); const infoB = analysis.get(declB);
expect(infoB).toBeDefined(); expect(infoB).toBeDefined();
expect(infoB !.template.usedComponents).toEqual(new Set([declA])); expect(infoB!.template.usedComponents).toEqual(new Set([declA]));
}); });
}); });
}); });

View File

@ -6,9 +6,10 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {BoundTarget, CssSelector, ParseTemplateOptions, R3TargetBinder, SelectorMatcher, parseTemplate} from '@angular/compiler'; import {BoundTarget, CssSelector, parseTemplate, ParseTemplateOptions, R3TargetBinder, SelectorMatcher} from '@angular/compiler';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {AbsoluteFsPath, absoluteFrom} from '../../file_system';
import {absoluteFrom, AbsoluteFsPath} from '../../file_system';
import {Reference} from '../../imports'; import {Reference} from '../../imports';
import {ClassDeclaration} from '../../reflection'; import {ClassDeclaration} from '../../reflection';
import {getDeclaration, makeProgram} from '../../testing'; import {getDeclaration, makeProgram} from '../../testing';

View File

@ -9,7 +9,7 @@
import * as ts from 'typescript'; import * as ts from 'typescript';
import {Reference} from '../../imports'; import {Reference} from '../../imports';
import {ClassDeclaration, ReflectionHost, isNamedClassDeclaration} from '../../reflection'; import {ClassDeclaration, isNamedClassDeclaration, ReflectionHost} from '../../reflection';
import {DirectiveMeta, MetadataReader, NgModuleMeta, PipeMeta} from './api'; import {DirectiveMeta, MetadataReader, NgModuleMeta, PipeMeta} from './api';
import {extractDirectiveGuards, extractReferencesFromType, readStringArrayType, readStringMapType, readStringType} from './util'; import {extractDirectiveGuards, extractReferencesFromType, readStringArrayType, readStringMapType, readStringType} from './util';

View File

@ -25,7 +25,7 @@ export function flattenInheritedDirectiveMetadata(
throw new Error(`Metadata not found for directive: ${dir.debugName}`); throw new Error(`Metadata not found for directive: ${dir.debugName}`);
} }
let inputs: {[key: string]: string | [string, string]} = {}; let inputs: {[key: string]: string|[string, string]} = {};
let outputs: {[key: string]: string} = {}; let outputs: {[key: string]: string} = {};
let coercedInputFields = new Set<string>(); let coercedInputFields = new Set<string>();
let isDynamic = false; let isDynamic = false;

View File

@ -22,18 +22,24 @@ export class LocalMetadataRegistry implements MetadataRegistry, MetadataReader {
private pipes = new Map<ClassDeclaration, PipeMeta>(); private pipes = new Map<ClassDeclaration, PipeMeta>();
getDirectiveMetadata(ref: Reference<ClassDeclaration>): DirectiveMeta|null { getDirectiveMetadata(ref: Reference<ClassDeclaration>): DirectiveMeta|null {
return this.directives.has(ref.node) ? this.directives.get(ref.node) ! : null; return this.directives.has(ref.node) ? this.directives.get(ref.node)! : null;
} }
getNgModuleMetadata(ref: Reference<ClassDeclaration>): NgModuleMeta|null { getNgModuleMetadata(ref: Reference<ClassDeclaration>): NgModuleMeta|null {
return this.ngModules.has(ref.node) ? this.ngModules.get(ref.node) ! : null; return this.ngModules.has(ref.node) ? this.ngModules.get(ref.node)! : null;
} }
getPipeMetadata(ref: Reference<ClassDeclaration>): PipeMeta|null { getPipeMetadata(ref: Reference<ClassDeclaration>): PipeMeta|null {
return this.pipes.has(ref.node) ? this.pipes.get(ref.node) ! : null; return this.pipes.has(ref.node) ? this.pipes.get(ref.node)! : null;
} }
registerDirectiveMetadata(meta: DirectiveMeta): void { this.directives.set(meta.ref.node, meta); } registerDirectiveMetadata(meta: DirectiveMeta): void {
registerNgModuleMetadata(meta: NgModuleMeta): void { this.ngModules.set(meta.ref.node, meta); } this.directives.set(meta.ref.node, meta);
registerPipeMetadata(meta: PipeMeta): void { this.pipes.set(meta.ref.node, meta); } }
registerNgModuleMetadata(meta: NgModuleMeta): void {
this.ngModules.set(meta.ref.node, meta);
}
registerPipeMetadata(meta: PipeMeta): void {
this.pipes.set(meta.ref.node, meta);
}
} }
/** /**
@ -70,7 +76,9 @@ export class InjectableClassRegistry {
constructor(private host: ReflectionHost) {} constructor(private host: ReflectionHost) {}
registerInjectable(declaration: ClassDeclaration): void { this.classes.add(declaration); } registerInjectable(declaration: ClassDeclaration): void {
this.classes.add(declaration);
}
isInjectable(declaration: ClassDeclaration): boolean { isInjectable(declaration: ClassDeclaration): boolean {
// Figure out whether the class is injectable based on the registered classes, otherwise // Figure out whether the class is injectable based on the registered classes, otherwise

View File

@ -9,13 +9,13 @@
import * as ts from 'typescript'; import * as ts from 'typescript';
import {Reference} from '../../imports'; import {Reference} from '../../imports';
import {ClassDeclaration, ClassMember, ClassMemberKind, ReflectionHost, isNamedClassDeclaration, reflectTypeEntityToDeclaration} from '../../reflection'; import {ClassDeclaration, ClassMember, ClassMemberKind, isNamedClassDeclaration, ReflectionHost, reflectTypeEntityToDeclaration} from '../../reflection';
import {nodeDebugInfo} from '../../util/src/typescript'; import {nodeDebugInfo} from '../../util/src/typescript';
import {DirectiveMeta, MetadataReader, NgModuleMeta, PipeMeta, TemplateGuardMeta} from './api'; import {DirectiveMeta, MetadataReader, NgModuleMeta, PipeMeta, TemplateGuardMeta} from './api';
export function extractReferencesFromType( export function extractReferencesFromType(
checker: ts.TypeChecker, def: ts.TypeNode, ngModuleImportedFrom: string | null, checker: ts.TypeChecker, def: ts.TypeNode, ngModuleImportedFrom: string|null,
resolutionContext: string): Reference<ClassDeclaration>[] { resolutionContext: string): Reference<ClassDeclaration>[] {
if (!ts.isTupleTypeNode(def)) { if (!ts.isTupleTypeNode(def)) {
return []; return [];
@ -122,7 +122,7 @@ function extractTemplateGuard(member: ClassMember): TemplateGuardMeta|null {
function extractCoercedInput(member: ClassMember): string|null { function extractCoercedInput(member: ClassMember): string|null {
if (member.kind !== ClassMemberKind.Property || !member.name.startsWith('ngAcceptInputType_')) { if (member.kind !== ClassMemberKind.Property || !member.name.startsWith('ngAcceptInputType_')) {
return null !; return null!;
} }
return afterUnderscore(member.name); return afterUnderscore(member.name);
} }

View File

@ -13,7 +13,9 @@ import {ImportFlags, Reference, ReferenceEmitter} from '../../imports';
import {PartialEvaluator, ResolvedValueMap} from '../../partial_evaluator'; import {PartialEvaluator, ResolvedValueMap} from '../../partial_evaluator';
import {ReflectionHost} from '../../reflection'; import {ReflectionHost} from '../../reflection';
export interface DtsHandler { addTypeReplacement(node: ts.Declaration, type: Type): void; } export interface DtsHandler {
addTypeReplacement(node: ts.Declaration, type: Type): void;
}
export class ModuleWithProvidersScanner { export class ModuleWithProvidersScanner {
constructor( constructor(

View File

@ -12,9 +12,11 @@ import {PerfRecorder} from './api';
export const NOOP_PERF_RECORDER: PerfRecorder = { export const NOOP_PERF_RECORDER: PerfRecorder = {
enabled: false, enabled: false,
mark: (name: string, node: ts.SourceFile | ts.Declaration, category?: string, detail?: string): mark: (name: string, node: ts.SourceFile|ts.Declaration, category?: string, detail?: string):
void => {}, void => {},
start: (name: string, node: ts.SourceFile | ts.Declaration, category?: string, detail?: string): start: (name: string, node: ts.SourceFile|ts.Declaration, category?: string, detail?: string):
number => { return 0;}, number => {
stop: (span: number | false): void => {}, return 0;
},
stop: (span: number|false): void => {},
}; };

View File

@ -20,7 +20,9 @@ export class PerfTracker implements PerfRecorder {
private constructor(private zeroTime: HrTime) {} private constructor(private zeroTime: HrTime) {}
static zeroedToNow(): PerfTracker { return new PerfTracker(mark()); } static zeroedToNow(): PerfTracker {
return new PerfTracker(mark());
}
mark(name: string, node?: ts.SourceFile|ts.Declaration, category?: string, detail?: string): mark(name: string, node?: ts.SourceFile|ts.Declaration, category?: string, detail?: string):
void { void {
@ -73,7 +75,9 @@ export class PerfTracker implements PerfRecorder {
return msg; return msg;
} }
asJson(): unknown { return this.log; } asJson(): unknown {
return this.log;
}
serializeToFile(target: string, host: ts.CompilerHost): void { serializeToFile(target: string, host: ts.CompilerHost): void {
const json = JSON.stringify(this.log, null, 2); const json = JSON.stringify(this.log, null, 2);

View File

@ -77,7 +77,9 @@ export class NgtscProgram implements api.Program {
new NgCompiler(this.host, options, this.tsProgram, reuseProgram, this.perfRecorder); new NgCompiler(this.host, options, this.tsProgram, reuseProgram, this.perfRecorder);
} }
getTsProgram(): ts.Program { return this.tsProgram; } getTsProgram(): ts.Program {
return this.tsProgram;
}
getTsOptionDiagnostics(cancellationToken?: ts.CancellationToken| getTsOptionDiagnostics(cancellationToken?: ts.CancellationToken|
undefined): readonly ts.Diagnostic[] { undefined): readonly ts.Diagnostic[] {
@ -137,8 +139,8 @@ export class NgtscProgram implements api.Program {
} }
getNgSemanticDiagnostics( getNgSemanticDiagnostics(
fileName?: string|undefined, cancellationToken?: ts.CancellationToken| fileName?: string|undefined, cancellationToken?: ts.CancellationToken|undefined):
undefined): readonly(ts.Diagnostic|api.Diagnostic)[] { readonly(ts.Diagnostic|api.Diagnostic)[] {
let sf: ts.SourceFile|undefined = undefined; let sf: ts.SourceFile|undefined = undefined;
if (fileName !== undefined) { if (fileName !== undefined) {
sf = this.tsProgram.getSourceFile(fileName); sf = this.tsProgram.getSourceFile(fileName);
@ -161,14 +163,17 @@ export class NgtscProgram implements api.Program {
* This is used by the Angular CLI to allow for spawning (async) child compilations for things * This is used by the Angular CLI to allow for spawning (async) child compilations for things
* like SASS files used in `styleUrls`. * like SASS files used in `styleUrls`.
*/ */
loadNgStructureAsync(): Promise<void> { return this.compiler.analyzeAsync(); } loadNgStructureAsync(): Promise<void> {
return this.compiler.analyzeAsync();
}
listLazyRoutes(entryRoute?: string|undefined): api.LazyRoute[] { listLazyRoutes(entryRoute?: string|undefined): api.LazyRoute[] {
return this.compiler.listLazyRoutes(entryRoute); return this.compiler.listLazyRoutes(entryRoute);
} }
emit(opts?: { emit(opts?: {
emitFlags?: api.EmitFlags | undefined; cancellationToken?: ts.CancellationToken | undefined; emitFlags?: api.EmitFlags|undefined;
cancellationToken?: ts.CancellationToken | undefined;
customTransformers?: api.CustomTransformers | undefined; customTransformers?: api.CustomTransformers | undefined;
emitCallback?: api.TsEmitCallback | undefined; emitCallback?: api.TsEmitCallback | undefined;
mergeEmitResultsCallback?: api.TsMergeEmitResultsCallback | undefined; mergeEmitResultsCallback?: api.TsMergeEmitResultsCallback | undefined;
@ -179,8 +184,8 @@ export class NgtscProgram implements api.Program {
const writeFile: ts.WriteFileCallback = const writeFile: ts.WriteFileCallback =
(fileName: string, data: string, writeByteOrderMark: boolean, (fileName: string, data: string, writeByteOrderMark: boolean,
onError: ((message: string) => void) | undefined, onError: ((message: string) => void)|undefined,
sourceFiles: ReadonlyArray<ts.SourceFile>| undefined) => { sourceFiles: ReadonlyArray<ts.SourceFile>|undefined) => {
if (sourceFiles !== undefined) { if (sourceFiles !== undefined) {
// Record successful writes for any `ts.SourceFile` (that's not a declaration file) // Record successful writes for any `ts.SourceFile` (that's not a declaration file)
// that's an input to this write. // that's an input to this write.
@ -221,7 +226,8 @@ export class NgtscProgram implements api.Program {
program: this.tsProgram, program: this.tsProgram,
host: this.host, host: this.host,
options: this.options, options: this.options,
emitOnlyDtsFiles: false, writeFile, emitOnlyDtsFiles: false,
writeFile,
customTransformers: { customTransformers: {
before: beforeTransforms, before: beforeTransforms,
after: customTransforms && customTransforms.afterTs, after: customTransforms && customTransforms.afterTs,
@ -257,11 +263,16 @@ export class NgtscProgram implements api.Program {
} }
} }
const defaultEmitCallback: api.TsEmitCallback = const defaultEmitCallback: api.TsEmitCallback = ({
({program, targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, program,
customTransformers}) => targetSourceFile,
program.emit( writeFile,
targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers); cancellationToken,
emitOnlyDtsFiles,
customTransformers
}) =>
program.emit(
targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
function mergeEmitResults(emitResults: ts.EmitResult[]): ts.EmitResult { function mergeEmitResults(emitResults: ts.EmitResult[]): ts.EmitResult {
const diagnostics: ts.Diagnostic[] = []; const diagnostics: ts.Diagnostic[] = [];

View File

@ -12,7 +12,7 @@ import * as ts from 'typescript';
* Metadata extracted from an instance of a decorator on another declaration, or synthesized from * Metadata extracted from an instance of a decorator on another declaration, or synthesized from
* other information about a class. * other information about a class.
*/ */
export type Decorator = ConcreteDecorator | SyntheticDecorator; export type Decorator = ConcreteDecorator|SyntheticDecorator;
export interface BaseDecorator { export interface BaseDecorator {
/** /**
@ -28,11 +28,11 @@ export interface BaseDecorator {
*/ */
identifier: DecoratorIdentifier|null; identifier: DecoratorIdentifier|null;
/** /**
* `Import` by which the decorator was brought into the module in which it was invoked, or `null` * `Import` by which the decorator was brought into the module in which it was invoked, or `null`
* if the decorator was declared in the same module and not imported. * if the decorator was declared in the same module and not imported.
*/ */
import : Import | null; import: Import|null;
/** /**
* TypeScript reference to the decorator itself, or `null` if the decorator is synthesized (e.g. * TypeScript reference to the decorator itself, or `null` if the decorator is synthesized (e.g.
@ -87,8 +87,8 @@ export const Decorator = {
* A decorator is identified by either a simple identifier (e.g. `Decorator`) or, in some cases, * A decorator is identified by either a simple identifier (e.g. `Decorator`) or, in some cases,
* a namespaced property access (e.g. `core.Decorator`). * a namespaced property access (e.g. `core.Decorator`).
*/ */
export type DecoratorIdentifier = ts.Identifier | NamespacedIdentifier; export type DecoratorIdentifier = ts.Identifier|NamespacedIdentifier;
export type NamespacedIdentifier = ts.PropertyAccessExpression & { export type NamespacedIdentifier = ts.PropertyAccessExpression&{
expression: ts.Identifier; expression: ts.Identifier;
name: ts.Identifier name: ts.Identifier
}; };
@ -113,7 +113,7 @@ export function isDecoratorIdentifier(exp: ts.Expression): exp is DecoratorIdent
* For `ReflectionHost` purposes, a class declaration should always have a `name` identifier, * For `ReflectionHost` purposes, a class declaration should always have a `name` identifier,
* because we need to be able to reference it in other parts of the program. * because we need to be able to reference it in other parts of the program.
*/ */
export type ClassDeclaration<T extends ts.Declaration = ts.Declaration> = T & {name: ts.Identifier}; export type ClassDeclaration<T extends ts.Declaration = ts.Declaration> = T&{name: ts.Identifier};
/** /**
* An enumeration of possible kinds of class members. * An enumeration of possible kinds of class members.
@ -241,8 +241,7 @@ export interface ClassMember {
*/ */
export type TypeValueReference = { export type TypeValueReference = {
local: true; expression: ts.Expression; defaultImportStatement: ts.ImportDeclaration | null; local: true; expression: ts.Expression; defaultImportStatement: ts.ImportDeclaration | null;
} | }|{
{
local: false; local: false;
name: string; name: string;
moduleName: string; moduleName: string;
@ -447,7 +446,7 @@ export interface InlineDeclaration extends BaseDeclaration {
* downlevelings to a `ts.Expression` instead. * downlevelings to a `ts.Expression` instead.
*/ */
export type Declaration<T extends ts.Declaration = ts.Declaration> = export type Declaration<T extends ts.Declaration = ts.Declaration> =
ConcreteDeclaration<T>| InlineDeclaration; ConcreteDeclaration<T>|InlineDeclaration;
/** /**
* Abstracts reflection operations on a TypeScript AST. * Abstracts reflection operations on a TypeScript AST.

View File

@ -18,7 +18,7 @@ import {TypeValueReference} from './host';
* declaration, or if it is not possible to statically understand. * declaration, or if it is not possible to statically understand.
*/ */
export function typeToValue( export function typeToValue(
typeNode: ts.TypeNode | null, checker: ts.TypeChecker): TypeValueReference|null { typeNode: ts.TypeNode|null, checker: ts.TypeChecker): TypeValueReference|null {
// It's not possible to get a value expression if the parameter doesn't even have a type. // It's not possible to get a value expression if the parameter doesn't even have a type.
if (typeNode === null || !ts.isTypeReferenceNode(typeNode)) { if (typeNode === null || !ts.isTypeReferenceNode(typeNode)) {
return null; return null;
@ -95,7 +95,7 @@ export function typeNodeToValueExpr(node: ts.TypeNode): ts.Expression|null {
* give the identifier name within the current file by which the import is known. * give the identifier name within the current file by which the import is known.
*/ */
function resolveTypeSymbols(typeRef: ts.TypeReferenceNode, checker: ts.TypeChecker): function resolveTypeSymbols(typeRef: ts.TypeReferenceNode, checker: ts.TypeChecker):
{local: ts.Symbol, decl: ts.Symbol, importName: string | null}|null { {local: ts.Symbol, decl: ts.Symbol, importName: string|null}|null {
const typeName = typeRef.typeName; const typeName = typeRef.typeName;
// typeRefSymbol is the ts.Symbol of the entire type reference. // typeRefSymbol is the ts.Symbol of the entire type reference.
const typeRefSymbol: ts.Symbol|undefined = checker.getSymbolAtLocation(typeName); const typeRefSymbol: ts.Symbol|undefined = checker.getSymbolAtLocation(typeName);
@ -156,8 +156,8 @@ function isImportSource(node: ts.Declaration): node is(ts.ImportSpecifier | ts.N
} }
function extractModuleAndNameFromImport( function extractModuleAndNameFromImport(
node: ts.ImportSpecifier | ts.NamespaceImport | ts.ImportClause, node: ts.ImportSpecifier|ts.NamespaceImport|ts.ImportClause,
localName: string | null): {name: string, moduleName: string} { localName: string|null): {name: string, moduleName: string} {
let name: string; let name: string;
let moduleSpecifier: ts.Expression; let moduleSpecifier: ts.Expression;
switch (node.kind) { switch (node.kind) {

View File

@ -8,7 +8,7 @@
import * as ts from 'typescript'; import * as ts from 'typescript';
import {ClassDeclaration, ClassMember, ClassMemberKind, CtorParameter, Declaration, Decorator, FunctionDefinition, Import, ReflectionHost, isDecoratorIdentifier} from './host'; import {ClassDeclaration, ClassMember, ClassMemberKind, CtorParameter, Declaration, Decorator, FunctionDefinition, Import, isDecoratorIdentifier, ReflectionHost} from './host';
import {typeToValue} from './type_to_value'; import {typeToValue} from './type_to_value';
/** /**
@ -76,8 +76,10 @@ export class TypeScriptReflectionHost implements ReflectionHost {
return { return {
name, name,
nameNode: node.name, typeValueReference, nameNode: node.name,
typeNode: originalTypeNode, decorators, typeValueReference,
typeNode: originalTypeNode,
decorators,
}; };
}); });
} }
@ -183,11 +185,17 @@ export class TypeScriptReflectionHost implements ReflectionHost {
return declaration.initializer || null; return declaration.initializer || null;
} }
getDtsDeclaration(_: ts.Declaration): ts.Declaration|null { return null; } getDtsDeclaration(_: ts.Declaration): ts.Declaration|null {
return null;
}
getInternalNameOfClass(clazz: ClassDeclaration): ts.Identifier { return clazz.name; } getInternalNameOfClass(clazz: ClassDeclaration): ts.Identifier {
return clazz.name;
}
getAdjacentNameOfClass(clazz: ClassDeclaration): ts.Identifier { return clazz.name; } getAdjacentNameOfClass(clazz: ClassDeclaration): ts.Identifier {
return clazz.name;
}
protected getDirectImportOfIdentifier(id: ts.Identifier): Import|null { protected getDirectImportOfIdentifier(id: ts.Identifier): Import|null {
const symbol = this.checker.getSymbolAtLocation(id); const symbol = this.checker.getSymbolAtLocation(id);
@ -305,12 +313,14 @@ export class TypeScriptReflectionHost implements ReflectionHost {
if (symbol.valueDeclaration !== undefined) { if (symbol.valueDeclaration !== undefined) {
return { return {
node: symbol.valueDeclaration, node: symbol.valueDeclaration,
known: null, viaModule, known: null,
viaModule,
}; };
} else if (symbol.declarations !== undefined && symbol.declarations.length > 0) { } else if (symbol.declarations !== undefined && symbol.declarations.length > 0) {
return { return {
node: symbol.declarations[0], node: symbol.declarations[0],
known: null, viaModule, known: null,
viaModule,
}; };
} else { } else {
return null; return null;
@ -342,7 +352,9 @@ export class TypeScriptReflectionHost implements ReflectionHost {
return { return {
name: decoratorIdentifier.text, name: decoratorIdentifier.text,
identifier: decoratorExpr, identifier: decoratorExpr,
import: importDecl, node, args, import: importDecl,
node,
args,
}; };
} }
@ -382,8 +394,14 @@ export class TypeScriptReflectionHost implements ReflectionHost {
return { return {
node, node,
implementation: node, kind, implementation: node,
type: node.type || null, name, nameNode, decorators, value, isStatic, kind,
type: node.type || null,
name,
nameNode,
decorators,
value,
isStatic,
}; };
} }
} }
@ -405,7 +423,7 @@ export function reflectIdentifierOfDeclaration(decl: ts.Declaration): ts.Identif
} }
export function reflectTypeEntityToDeclaration( export function reflectTypeEntityToDeclaration(
type: ts.EntityName, checker: ts.TypeChecker): {node: ts.Declaration, from: string | null} { type: ts.EntityName, checker: ts.TypeChecker): {node: ts.Declaration, from: string|null} {
let realSymbol = checker.getSymbolAtLocation(type); let realSymbol = checker.getSymbolAtLocation(type);
if (realSymbol === undefined) { if (realSymbol === undefined) {
throw new Error(`Cannot resolve type entity ${type.getText()} to symbol`); throw new Error(`Cannot resolve type entity ${type.getText()} to symbol`);
@ -434,8 +452,8 @@ export function reflectTypeEntityToDeclaration(
} }
const decl = symbol.declarations[0]; const decl = symbol.declarations[0];
if (ts.isNamespaceImport(decl)) { if (ts.isNamespaceImport(decl)) {
const clause = decl.parent !; const clause = decl.parent!;
const importDecl = clause.parent !; const importDecl = clause.parent!;
if (!ts.isStringLiteral(importDecl.moduleSpecifier)) { if (!ts.isStringLiteral(importDecl.moduleSpecifier)) {
throw new Error(`Module specifier is not a string`); throw new Error(`Module specifier is not a string`);
} }
@ -552,7 +570,7 @@ function getFarLeftIdentifier(propertyAccess: ts.PropertyAccessExpression): ts.I
* `NamespaceImport`. If not return `null`. * `NamespaceImport`. If not return `null`.
*/ */
function getContainingImportDeclaration(node: ts.Node): ts.ImportDeclaration|null { function getContainingImportDeclaration(node: ts.Node): ts.ImportDeclaration|null {
return ts.isImportSpecifier(node) ? node.parent !.parent !.parent ! : return ts.isImportSpecifier(node) ? node.parent!.parent!.parent! :
ts.isNamespaceImport(node) ? node.parent.parent : null; ts.isNamespaceImport(node) ? node.parent.parent : null;
} }

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