fix(language-service): do not throw for invalid metadata (#13261)

Fixes #13255
This commit is contained in:
Chuck Jazdzewski 2016-12-06 17:11:09 -08:00 committed by Alex Rickabaugh
parent 16efb13dd1
commit 4a09c81724
4 changed files with 147 additions and 57 deletions

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 {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, Attribute, ChangeDetectionStrategy, Component, Directive, Host, Inject, Injectable, ModuleWithProviders, Optional, Provider, Query, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef} from '@angular/core'; import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, Attribute, ChangeDetectionStrategy, Component, Directive, Host, Inject, Injectable, ModuleWithProviders, OpaqueToken, Optional, Provider, Query, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef} from '@angular/core';
import {StaticSymbol} from './aot/static_symbol'; import {StaticSymbol} from './aot/static_symbol';
import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions'; import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
@ -25,7 +25,8 @@ import {SummaryResolver} from './summary_resolver';
import {getUrlScheme} from './url_resolver'; import {getUrlScheme} from './url_resolver';
import {MODULE_SUFFIX, SyncAsyncResult, ValueTransformer, visitValue} from './util'; import {MODULE_SUFFIX, SyncAsyncResult, ValueTransformer, visitValue} from './util';
export type ErrorCollector = (error: any, type?: any) => void;
export const ERROR_COLLECTOR_TOKEN = new OpaqueToken('ErrorCollector');
// Design notes: // Design notes:
// - don't lazily create metadata: // - don't lazily create metadata:
@ -47,7 +48,8 @@ export class CompileMetadataResolver {
private _pipeResolver: PipeResolver, private _summaryResolver: SummaryResolver, private _pipeResolver: PipeResolver, private _summaryResolver: SummaryResolver,
private _schemaRegistry: ElementSchemaRegistry, private _schemaRegistry: ElementSchemaRegistry,
private _directiveNormalizer: DirectiveNormalizer, private _directiveNormalizer: DirectiveNormalizer,
private _reflector: ReflectorReader = reflector) {} private _reflector: ReflectorReader = reflector,
@Optional() @Inject(ERROR_COLLECTOR_TOKEN) private _errorCollector?: ErrorCollector) {}
clearCacheFor(type: Type<any>) { clearCacheFor(type: Type<any>) {
const dirMeta = this._directiveCache.get(type); const dirMeta = this._directiveCache.get(type);
@ -182,7 +184,8 @@ export class CompileMetadataResolver {
return null; return null;
} else { } else {
if (isSync) { if (isSync) {
throw new ComponentStillLoadingError(directiveType); this._reportError(new ComponentStillLoadingError(directiveType), directiveType);
return null;
} }
return templateMeta.asyncResult.then(createDirectiveMetadata); return templateMeta.asyncResult.then(createDirectiveMetadata);
} }
@ -234,7 +237,7 @@ export class CompileMetadataResolver {
if (dirMeta.viewProviders) { if (dirMeta.viewProviders) {
viewProviders = this._getProvidersMetadata( viewProviders = this._getProvidersMetadata(
dirMeta.viewProviders, entryComponentMetadata, dirMeta.viewProviders, entryComponentMetadata,
`viewProviders for "${stringify(directiveType)}"`); `viewProviders for "${stringify(directiveType)}"`, [], directiveType);
} }
if (dirMeta.entryComponents) { if (dirMeta.entryComponents) {
entryComponentMetadata = flattenAndDedupeArray(dirMeta.entryComponents) entryComponentMetadata = flattenAndDedupeArray(dirMeta.entryComponents)
@ -247,14 +250,18 @@ export class CompileMetadataResolver {
} else { } else {
// Directive // Directive
if (!selector) { if (!selector) {
throw new Error(`Directive ${stringify(directiveType)} has no selector, please add it!`); this._reportError(
new Error(`Directive ${stringify(directiveType)} has no selector, please add it!`),
directiveType);
selector = 'error';
} }
} }
let providers: cpl.CompileProviderMetadata[] = []; let providers: cpl.CompileProviderMetadata[] = [];
if (isPresent(dirMeta.providers)) { if (isPresent(dirMeta.providers)) {
providers = this._getProvidersMetadata( providers = this._getProvidersMetadata(
dirMeta.providers, entryComponentMetadata, `providers for "${stringify(directiveType)}"`); dirMeta.providers, entryComponentMetadata, `providers for "${stringify(directiveType)}"`,
[], directiveType);
} }
let queries: cpl.CompileQueryMetadata[] = []; let queries: cpl.CompileQueryMetadata[] = [];
let viewQueries: cpl.CompileQueryMetadata[] = []; let viewQueries: cpl.CompileQueryMetadata[] = [];
@ -289,8 +296,10 @@ export class CompileMetadataResolver {
getDirectiveMetadata(directiveType: any): cpl.CompileDirectiveMetadata { getDirectiveMetadata(directiveType: any): cpl.CompileDirectiveMetadata {
const dirMeta = this._directiveCache.get(directiveType); const dirMeta = this._directiveCache.get(directiveType);
if (!dirMeta) { if (!dirMeta) {
throw new Error( this._reportError(
`Illegal state: getDirectiveMetadata can only be called after loadNgModuleMetadata for a module that declares it. Directive ${stringify(directiveType)}.`); new Error(
`Illegal state: getDirectiveMetadata can only be called after loadNgModuleMetadata for a module that declares it. Directive ${stringify(directiveType)}.`),
directiveType);
} }
return dirMeta; return dirMeta;
} }
@ -299,8 +308,10 @@ export class CompileMetadataResolver {
const dirSummary = const dirSummary =
<cpl.CompileDirectiveSummary>this._loadSummary(dirType, cpl.CompileSummaryKind.Directive); <cpl.CompileDirectiveSummary>this._loadSummary(dirType, cpl.CompileSummaryKind.Directive);
if (!dirSummary) { if (!dirSummary) {
throw new Error( this._reportError(
`Illegal state: Could not load the summary for directive ${stringify(dirType)}.`); new Error(
`Illegal state: Could not load the summary for directive ${stringify(dirType)}.`),
dirType);
} }
return dirSummary; return dirSummary;
} }
@ -372,20 +383,26 @@ export class CompileMetadataResolver {
if (moduleWithProviders.providers) { if (moduleWithProviders.providers) {
providers.push(...this._getProvidersMetadata( providers.push(...this._getProvidersMetadata(
moduleWithProviders.providers, entryComponents, moduleWithProviders.providers, entryComponents,
`provider for the NgModule '${stringify(importedModuleType)}'`)); `provider for the NgModule '${stringify(importedModuleType)}'`, [], importedType));
} }
} }
if (importedModuleType) { if (importedModuleType) {
const importedModuleSummary = this.getNgModuleSummary(importedModuleType); const importedModuleSummary = this.getNgModuleSummary(importedModuleType);
if (!importedModuleSummary) { if (!importedModuleSummary) {
throw new Error( this._reportError(
`Unexpected ${this._getTypeDescriptor(importedType)} '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`); new Error(
`Unexpected ${this._getTypeDescriptor(importedType)} '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`),
moduleType);
return;
} }
importedModules.push(importedModuleSummary); importedModules.push(importedModuleSummary);
} else { } else {
throw new Error( this._reportError(
`Unexpected value '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`); new Error(
`Unexpected value '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`),
moduleType);
return;
} }
}); });
} }
@ -393,8 +410,11 @@ export class CompileMetadataResolver {
if (meta.exports) { if (meta.exports) {
flattenAndDedupeArray(meta.exports).forEach((exportedType) => { flattenAndDedupeArray(meta.exports).forEach((exportedType) => {
if (!isValidType(exportedType)) { if (!isValidType(exportedType)) {
throw new Error( this._reportError(
`Unexpected value '${stringify(exportedType)}' exported by the module '${stringify(moduleType)}'`); new Error(
`Unexpected value '${stringify(exportedType)}' exported by the module '${stringify(moduleType)}'`),
moduleType);
return;
} }
const exportedModuleSummary = this.getNgModuleSummary(exportedType); const exportedModuleSummary = this.getNgModuleSummary(exportedType);
if (exportedModuleSummary) { if (exportedModuleSummary) {
@ -411,8 +431,11 @@ export class CompileMetadataResolver {
if (meta.declarations) { if (meta.declarations) {
flattenAndDedupeArray(meta.declarations).forEach((declaredType) => { flattenAndDedupeArray(meta.declarations).forEach((declaredType) => {
if (!isValidType(declaredType)) { if (!isValidType(declaredType)) {
throw new Error( this._reportError(
`Unexpected value '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`); new Error(
`Unexpected value '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`),
moduleType);
return;
} }
const declaredIdentifier = this._getIdentifierMetadata(declaredType); const declaredIdentifier = this._getIdentifierMetadata(declaredType);
if (this._directiveResolver.isDirective(declaredType)) { if (this._directiveResolver.isDirective(declaredType)) {
@ -425,8 +448,11 @@ export class CompileMetadataResolver {
declaredPipes.push(declaredIdentifier); declaredPipes.push(declaredIdentifier);
this._addTypeToModule(declaredType, moduleType); this._addTypeToModule(declaredType, moduleType);
} else { } else {
throw new Error( this._reportError(
`Unexpected ${this._getTypeDescriptor(declaredType)} '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`); new Error(
`Unexpected ${this._getTypeDescriptor(declaredType)} '${stringify(declaredType)}' declared by the module '${stringify(moduleType)}'`),
moduleType);
return;
} }
}); });
} }
@ -441,8 +467,10 @@ export class CompileMetadataResolver {
exportedPipes.push(exportedId); exportedPipes.push(exportedId);
transitiveModule.addExportedPipe(exportedId); transitiveModule.addExportedPipe(exportedId);
} else { } else {
throw new Error( this._reportError(
`Can't export ${this._getTypeDescriptor(exportedId.reference)} ${stringify(exportedId.reference)} from ${stringify(moduleType)} as it was neither declared nor imported!`); new Error(
`Can't export ${this._getTypeDescriptor(exportedId.reference)} ${stringify(exportedId.reference)} from ${stringify(moduleType)} as it was neither declared nor imported!`),
moduleType);
} }
}); });
@ -450,7 +478,8 @@ export class CompileMetadataResolver {
// so that they overwrite any other provider we already added. // so that they overwrite any other provider we already added.
if (meta.providers) { if (meta.providers) {
providers.push(...this._getProvidersMetadata( providers.push(...this._getProvidersMetadata(
meta.providers, entryComponents, `provider for the NgModule '${stringify(moduleType)}'`)); meta.providers, entryComponents, `provider for the NgModule '${stringify(moduleType)}'`,
[], moduleType));
} }
if (meta.entryComponents) { if (meta.entryComponents) {
@ -459,14 +488,16 @@ export class CompileMetadataResolver {
} }
if (meta.bootstrap) { if (meta.bootstrap) {
const typeMetadata = flattenAndDedupeArray(meta.bootstrap).map(type => { flattenAndDedupeArray(meta.bootstrap).forEach(type => {
if (!isValidType(type)) { if (!isValidType(type)) {
throw new Error( this._reportError(
`Unexpected value '${stringify(type)}' used in the bootstrap property of module '${stringify(moduleType)}'`); new Error(
`Unexpected value '${stringify(type)}' used in the bootstrap property of module '${stringify(moduleType)}'`),
moduleType);
return;
} }
return this._getTypeMetadata(type); bootstrapComponents.push(this._getTypeMetadata(type));
}); });
bootstrapComponents.push(...typeMetadata);
} }
entryComponents.push(...bootstrapComponents); entryComponents.push(...bootstrapComponents);
@ -522,10 +553,12 @@ export class CompileMetadataResolver {
private _addTypeToModule(type: Type<any>, moduleType: Type<any>) { private _addTypeToModule(type: Type<any>, moduleType: Type<any>) {
const oldModule = this._ngModuleOfTypes.get(type); const oldModule = this._ngModuleOfTypes.get(type);
if (oldModule && oldModule !== moduleType) { if (oldModule && oldModule !== moduleType) {
throw new Error( this._reportError(
`Type ${stringify(type)} is part of the declarations of 2 modules: ${stringify(oldModule)} and ${stringify(moduleType)}! ` + new Error(
`Please consider moving ${stringify(type)} to a higher module that imports ${stringify(oldModule)} and ${stringify(moduleType)}. ` + `Type ${stringify(type)} is part of the declarations of 2 modules: ${stringify(oldModule)} and ${stringify(moduleType)}! ` +
`You can also create a new NgModule that exports and includes ${stringify(type)} then import that NgModule in ${stringify(oldModule)} and ${stringify(moduleType)}.`); `Please consider moving ${stringify(type)} to a higher module that imports ${stringify(oldModule)} and ${stringify(moduleType)}. ` +
`You can also create a new NgModule that exports and includes ${stringify(type)} then import that NgModule in ${stringify(oldModule)} and ${stringify(moduleType)}.`),
moduleType);
} }
this._ngModuleOfTypes.set(type, moduleType); this._ngModuleOfTypes.set(type, moduleType);
} }
@ -596,8 +629,10 @@ export class CompileMetadataResolver {
getPipeMetadata(pipeType: any): cpl.CompilePipeMetadata { getPipeMetadata(pipeType: any): cpl.CompilePipeMetadata {
const pipeMeta = this._pipeCache.get(pipeType); const pipeMeta = this._pipeCache.get(pipeType);
if (!pipeMeta) { if (!pipeMeta) {
throw new Error( this._reportError(
`Illegal state: getPipeMetadata can only be called after loadNgModuleMetadata for a module that declares it. Pipe ${stringify(pipeType)}.`); new Error(
`Illegal state: getPipeMetadata can only be called after loadNgModuleMetadata for a module that declares it. Pipe ${stringify(pipeType)}.`),
pipeType);
} }
return pipeMeta; return pipeMeta;
} }
@ -606,7 +641,9 @@ export class CompileMetadataResolver {
const pipeSummary = const pipeSummary =
<cpl.CompilePipeSummary>this._loadSummary(pipeType, cpl.CompileSummaryKind.Pipe); <cpl.CompilePipeSummary>this._loadSummary(pipeType, cpl.CompileSummaryKind.Pipe);
if (!pipeSummary) { if (!pipeSummary) {
throw new Error(`Illegal state: Could not load the summary for pipe ${stringify(pipeType)}.`); this._reportError(
new Error(`Illegal state: Could not load the summary for pipe ${stringify(pipeType)}.`),
pipeType);
} }
return pipeSummary; return pipeSummary;
} }
@ -686,8 +723,9 @@ export class CompileMetadataResolver {
if (hasUnknownDeps) { if (hasUnknownDeps) {
const depsTokens = const depsTokens =
dependenciesMetadata.map((dep) => dep ? stringify(dep.token) : '?').join(', '); dependenciesMetadata.map((dep) => dep ? stringify(dep.token) : '?').join(', ');
throw new Error( this._reportError(
`Can't resolve all parameters for ${stringify(typeOrFunc)}: (${depsTokens}).`); new Error(`Can't resolve all parameters for ${stringify(typeOrFunc)}: (${depsTokens}).`),
typeOrFunc);
} }
return dependenciesMetadata; return dependenciesMetadata;
@ -706,8 +744,8 @@ export class CompileMetadataResolver {
private _getProvidersMetadata( private _getProvidersMetadata(
providers: Provider[], targetEntryComponents: cpl.CompileIdentifierMetadata[], providers: Provider[], targetEntryComponents: cpl.CompileIdentifierMetadata[],
debugInfo?: string, debugInfo?: string, compileProviders: cpl.CompileProviderMetadata[] = [],
compileProviders: cpl.CompileProviderMetadata[] = []): cpl.CompileProviderMetadata[] { type?: any): cpl.CompileProviderMetadata[] {
providers.forEach((provider: any, providerIdx: number) => { providers.forEach((provider: any, providerIdx: number) => {
if (Array.isArray(provider)) { if (Array.isArray(provider)) {
this._getProvidersMetadata(provider, targetEntryComponents, debugInfo, compileProviders); this._getProvidersMetadata(provider, targetEntryComponents, debugInfo, compileProviders);
@ -733,11 +771,13 @@ export class CompileMetadataResolver {
}, },
[])) []))
.join(', '); .join(', ');
throw new Error( this._reportError(
`Invalid ${debugInfo ? debugInfo : 'provider'} - only instances of Provider and Type are allowed, got: [${providersInfo}]`); new Error(
`Invalid ${debugInfo ? debugInfo : 'provider'} - only instances of Provider and Type are allowed, got: [${providersInfo}]`),
type);
} }
if (providerMeta.token === resolveIdentifier(Identifiers.ANALYZE_FOR_ENTRY_COMPONENTS)) { if (providerMeta.token === resolveIdentifier(Identifiers.ANALYZE_FOR_ENTRY_COMPONENTS)) {
targetEntryComponents.push(...this._getEntryComponentsFromProvider(providerMeta)); targetEntryComponents.push(...this._getEntryComponentsFromProvider(providerMeta, type));
} else { } else {
compileProviders.push(this.getProviderMetadata(providerMeta)); compileProviders.push(this.getProviderMetadata(providerMeta));
} }
@ -746,17 +786,21 @@ export class CompileMetadataResolver {
return compileProviders; return compileProviders;
} }
private _getEntryComponentsFromProvider(provider: cpl.ProviderMeta): private _getEntryComponentsFromProvider(provider: cpl.ProviderMeta, type?: any):
cpl.CompileIdentifierMetadata[] { cpl.CompileIdentifierMetadata[] {
const components: cpl.CompileIdentifierMetadata[] = []; const components: cpl.CompileIdentifierMetadata[] = [];
const collectedIdentifiers: cpl.CompileIdentifierMetadata[] = []; const collectedIdentifiers: cpl.CompileIdentifierMetadata[] = [];
if (provider.useFactory || provider.useExisting || provider.useClass) { if (provider.useFactory || provider.useExisting || provider.useClass) {
throw new Error(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports useValue!`); this._reportError(
new Error(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports useValue!`), type);
return [];
} }
if (!provider.multi) { if (!provider.multi) {
throw new Error(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports 'multi = true'!`); this._reportError(
new Error(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports 'multi = true'!`), type);
return [];
} }
extractIdentifiers(provider.useValue, collectedIdentifiers); extractIdentifiers(provider.useValue, collectedIdentifiers);
@ -822,8 +866,10 @@ export class CompileMetadataResolver {
this._queryVarBindings(q.selector).map(varName => this._getTokenMetadata(varName)); this._queryVarBindings(q.selector).map(varName => this._getTokenMetadata(varName));
} else { } else {
if (!q.selector) { if (!q.selector) {
throw new Error( this._reportError(
`Can't construct a query for the property "${propertyName}" of "${stringify(typeOrFunc)}" since the query selector wasn't defined.`); new Error(
`Can't construct a query for the property "${propertyName}" of "${stringify(typeOrFunc)}" since the query selector wasn't defined.`),
typeOrFunc);
} }
selectors = [this._getTokenMetadata(q.selector)]; selectors = [this._getTokenMetadata(q.selector)];
} }
@ -835,6 +881,17 @@ export class CompileMetadataResolver {
read: q.read ? this._getTokenMetadata(q.read) : null read: q.read ? this._getTokenMetadata(q.read) : null
}; };
} }
private _reportError(error: any, type?: any, otherType?: any) {
if (this._errorCollector) {
this._errorCollector(error, type);
if (otherType) {
this._errorCollector(error, otherType);
}
} else {
throw error;
}
}
} }
function flattenArray(tree: any[], out: Array<any> = []): Array<any> { function flattenArray(tree: any[], out: Array<any> = []): Array<any> {

View File

@ -120,7 +120,8 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
result = this._resolver = new CompileMetadataResolver( result = this._resolver = new CompileMetadataResolver(
moduleResolver, directiveResolver, pipeResolver, new SummaryResolver(), moduleResolver, directiveResolver, pipeResolver, new SummaryResolver(),
elementSchemaRegistry, directiveNormalizer, this.reflector); elementSchemaRegistry, directiveNormalizer, this.reflector,
(error, type) => this.collectError(error, type && type.filePath));
} }
return result; return result;
} }

View File

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

View File

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