Merge pull request #30971 from Microsoft/dtsSignatureChange

Handle when change in type of dts may result in only dts emit but not js emit
This commit is contained in:
Sheetal Nandi 2019-04-24 11:16:22 -07:00 committed by GitHub
commit f27cf9b34e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 460 additions and 109 deletions

View file

@ -238,10 +238,7 @@ namespace ts {
if (oldCompilerOptions && compilerOptionsAffectEmit(compilerOptions, oldCompilerOptions)) {
// Add all files to affectedFilesPendingEmit since emit changed
state.affectedFilesPendingEmit = concatenate(state.affectedFilesPendingEmit, newProgram.getSourceFiles().map(f => f.path));
if (state.affectedFilesPendingEmitIndex === undefined) {
state.affectedFilesPendingEmitIndex = 0;
}
addToAffectedFilesPendingEmit(state, newProgram.getSourceFiles().map(f => f.path));
Debug.assert(state.seenAffectedFiles === undefined);
state.seenAffectedFiles = createMap<true>();
}
@ -339,7 +336,7 @@ namespace ts {
if (!seenAffectedFiles.has(affectedFile.path)) {
// Set the next affected file as seen and remove the cached semantic diagnostics
state.affectedFilesIndex = affectedFilesIndex;
cleanSemanticDiagnosticsOfAffectedFile(state, affectedFile);
handleDtsMayChangeOfAffectedFile(state, affectedFile, cancellationToken, computeHash);
return affectedFile;
}
seenAffectedFiles.set(affectedFile.path, true);
@ -406,104 +403,62 @@ namespace ts {
}
/**
* Remove the semantic diagnostics cached from old state for affected File and the files that are referencing modules that export entities from affected file
* Handles semantic diagnostics and dts emit for affectedFile and files, that are referencing modules that export entities from affected file
* This is because even though js emit doesnt change, dts emit / type used can change resulting in need for dts emit and js change
*/
function cleanSemanticDiagnosticsOfAffectedFile(state: BuilderProgramState, affectedFile: SourceFile) {
if (removeSemanticDiagnosticsOf(state, affectedFile.path)) {
// If there are no more diagnostics from old cache, done
function handleDtsMayChangeOfAffectedFile(state: BuilderProgramState, affectedFile: SourceFile, cancellationToken: CancellationToken | undefined, computeHash: BuilderState.ComputeHash) {
removeSemanticDiagnosticsOf(state, affectedFile.path);
// If affected files is everything except default library, then nothing more to do
if (state.allFilesExcludingDefaultLibraryFile === state.affectedFiles) {
if (!state.cleanedDiagnosticsOfLibFiles) {
state.cleanedDiagnosticsOfLibFiles = true;
const program = Debug.assertDefined(state.program);
const options = program.getCompilerOptions();
forEach(program.getSourceFiles(), f =>
program.isSourceFileDefaultLibrary(f) &&
!skipTypeChecking(f, options) &&
removeSemanticDiagnosticsOf(state, f.path)
);
}
return;
}
// Clean lib file diagnostics if its all files excluding default files to emit
if (state.allFilesExcludingDefaultLibraryFile === state.affectedFiles && !state.cleanedDiagnosticsOfLibFiles) {
state.cleanedDiagnosticsOfLibFiles = true;
forEachReferencingModulesOfExportOfAffectedFile(state, affectedFile, (state, path) => handleDtsMayChangeOf(state, path, cancellationToken, computeHash));
}
/**
* Handle the dts may change, so they need to be added to pending emit if dts emit is enabled,
* Also we need to make sure signature is updated for these files
*/
function handleDtsMayChangeOf(state: BuilderProgramState, path: Path, cancellationToken: CancellationToken | undefined, computeHash: BuilderState.ComputeHash) {
removeSemanticDiagnosticsOf(state, path);
if (!state.changedFilesSet.has(path)) {
const program = Debug.assertDefined(state.program);
const options = program.getCompilerOptions();
if (forEach(program.getSourceFiles(), f =>
program.isSourceFileDefaultLibrary(f) &&
!skipTypeChecking(f, options) &&
removeSemanticDiagnosticsOf(state, f.path)
)) {
return;
const sourceFile = program.getSourceFileByPath(path);
if (sourceFile) {
// Even though the js emit doesnt change and we are already handling dts emit and semantic diagnostics
// we need to update the signature to reflect correctness of the signature(which is output d.ts emit) of this file
// This ensures that we dont later during incremental builds considering wrong signature.
// Eg where this also is needed to ensure that .tsbuildinfo generated by incremental build should be same as if it was first fresh build
BuilderState.updateShapeSignature(
state,
program,
sourceFile,
Debug.assertDefined(state.currentAffectedFilesSignatures),
cancellationToken,
computeHash,
state.currentAffectedFilesExportedModulesMap
);
// If not dts emit, nothing more to do
if (getEmitDeclarations(state.compilerOptions)) {
addToAffectedFilesPendingEmit(state, [path]);
}
}
}
// If there was change in signature for the changed file,
// then delete the semantic diagnostics for files that are affected by using exports of this module
if (!state.exportedModulesMap || state.affectedFiles!.length === 1 || !state.changedFilesSet.has(affectedFile.path)) {
return;
}
Debug.assert(!!state.currentAffectedFilesExportedModulesMap);
const seenFileAndExportsOfFile = createMap<true>();
// Go through exported modules from cache first
// If exported modules has path, all files referencing file exported from are affected
if (forEachEntry(state.currentAffectedFilesExportedModulesMap!, (exportedModules, exportedFromPath) =>
exportedModules &&
exportedModules.has(affectedFile.path) &&
removeSemanticDiagnosticsOfFilesReferencingPath(state, exportedFromPath as Path, seenFileAndExportsOfFile)
)) {
return;
}
// If exported from path is not from cache and exported modules has path, all files referencing file exported from are affected
forEachEntry(state.exportedModulesMap, (exportedModules, exportedFromPath) =>
!state.currentAffectedFilesExportedModulesMap!.has(exportedFromPath) && // If we already iterated this through cache, ignore it
exportedModules.has(affectedFile.path) &&
removeSemanticDiagnosticsOfFilesReferencingPath(state, exportedFromPath as Path, seenFileAndExportsOfFile)
);
}
/**
* removes the semantic diagnostics of files referencing referencedPath and
* returns true if there are no more semantic diagnostics from old state
*/
function removeSemanticDiagnosticsOfFilesReferencingPath(state: BuilderProgramState, referencedPath: Path, seenFileAndExportsOfFile: Map<true>) {
return forEachEntry(state.referencedMap!, (referencesInFile, filePath) =>
referencesInFile.has(referencedPath) && removeSemanticDiagnosticsOfFileAndExportsOfFile(state, filePath as Path, seenFileAndExportsOfFile)
);
}
/**
* Removes semantic diagnostics of file and anything that exports this file
*/
function removeSemanticDiagnosticsOfFileAndExportsOfFile(state: BuilderProgramState, filePath: Path, seenFileAndExportsOfFile: Map<true>): boolean {
if (!addToSeen(seenFileAndExportsOfFile, filePath)) {
return false;
}
if (removeSemanticDiagnosticsOf(state, filePath)) {
// If there are no more diagnostics from old cache, done
return true;
}
Debug.assert(!!state.currentAffectedFilesExportedModulesMap);
// Go through exported modules from cache first
// If exported modules has path, all files referencing file exported from are affected
if (forEachEntry(state.currentAffectedFilesExportedModulesMap!, (exportedModules, exportedFromPath) =>
exportedModules &&
exportedModules.has(filePath) &&
removeSemanticDiagnosticsOfFileAndExportsOfFile(state, exportedFromPath as Path, seenFileAndExportsOfFile)
)) {
return true;
}
// If exported from path is not from cache and exported modules has path, all files referencing file exported from are affected
if (forEachEntry(state.exportedModulesMap!, (exportedModules, exportedFromPath) =>
!state.currentAffectedFilesExportedModulesMap!.has(exportedFromPath) && // If we already iterated this through cache, ignore it
exportedModules.has(filePath) &&
removeSemanticDiagnosticsOfFileAndExportsOfFile(state, exportedFromPath as Path, seenFileAndExportsOfFile)
)) {
return true;
}
// Remove diagnostics of files that import this file (without going to exports of referencing files)
return !!forEachEntry(state.referencedMap!, (referencesInFile, referencingFilePath) =>
referencesInFile.has(filePath) &&
!seenFileAndExportsOfFile.has(referencingFilePath) && // Not already removed diagnostic file
removeSemanticDiagnosticsOf(state, referencingFilePath as Path) // Dont add to seen since this is not yet done with the export removal
);
return false;
}
/**
@ -519,6 +474,87 @@ namespace ts {
return !state.semanticDiagnosticsFromOldState.size;
}
/**
* Iterate on referencing modules that export entities from affected file
*/
function forEachReferencingModulesOfExportOfAffectedFile(state: BuilderProgramState, affectedFile: SourceFile, fn: (state: BuilderProgramState, filePath: Path) => boolean) {
// If there was change in signature (dts output) for the changed file,
// then only we need to handle pending file emit
if (!state.exportedModulesMap || state.affectedFiles!.length === 1 || !state.changedFilesSet.has(affectedFile.path)) {
return;
}
Debug.assert(!!state.currentAffectedFilesExportedModulesMap);
const seenFileAndExportsOfFile = createMap<true>();
// Go through exported modules from cache first
// If exported modules has path, all files referencing file exported from are affected
if (forEachEntry(state.currentAffectedFilesExportedModulesMap!, (exportedModules, exportedFromPath) =>
exportedModules &&
exportedModules.has(affectedFile.path) &&
forEachFilesReferencingPath(state, exportedFromPath as Path, seenFileAndExportsOfFile, fn)
)) {
return;
}
// If exported from path is not from cache and exported modules has path, all files referencing file exported from are affected
forEachEntry(state.exportedModulesMap, (exportedModules, exportedFromPath) =>
!state.currentAffectedFilesExportedModulesMap!.has(exportedFromPath) && // If we already iterated this through cache, ignore it
exportedModules.has(affectedFile.path) &&
forEachFilesReferencingPath(state, exportedFromPath as Path, seenFileAndExportsOfFile, fn)
);
}
/**
* Iterate on files referencing referencedPath
*/
function forEachFilesReferencingPath(state: BuilderProgramState, referencedPath: Path, seenFileAndExportsOfFile: Map<true>, fn: (state: BuilderProgramState, filePath: Path) => boolean) {
return forEachEntry(state.referencedMap!, (referencesInFile, filePath) =>
referencesInFile.has(referencedPath) && forEachFileAndExportsOfFile(state, filePath as Path, seenFileAndExportsOfFile, fn)
);
}
/**
* fn on file and iterate on anything that exports this file
*/
function forEachFileAndExportsOfFile(state: BuilderProgramState, filePath: Path, seenFileAndExportsOfFile: Map<true>, fn: (state: BuilderProgramState, filePath: Path) => boolean): boolean {
if (!addToSeen(seenFileAndExportsOfFile, filePath)) {
return false;
}
if (fn(state, filePath)) {
// If there are no more diagnostics from old cache, done
return true;
}
Debug.assert(!!state.currentAffectedFilesExportedModulesMap);
// Go through exported modules from cache first
// If exported modules has path, all files referencing file exported from are affected
if (forEachEntry(state.currentAffectedFilesExportedModulesMap!, (exportedModules, exportedFromPath) =>
exportedModules &&
exportedModules.has(filePath) &&
forEachFileAndExportsOfFile(state, exportedFromPath as Path, seenFileAndExportsOfFile, fn)
)) {
return true;
}
// If exported from path is not from cache and exported modules has path, all files referencing file exported from are affected
if (forEachEntry(state.exportedModulesMap!, (exportedModules, exportedFromPath) =>
!state.currentAffectedFilesExportedModulesMap!.has(exportedFromPath) && // If we already iterated this through cache, ignore it
exportedModules.has(filePath) &&
forEachFileAndExportsOfFile(state, exportedFromPath as Path, seenFileAndExportsOfFile, fn)
)) {
return true;
}
// Remove diagnostics of files that import this file (without going to exports of referencing files)
return !!forEachEntry(state.referencedMap!, (referencesInFile, referencingFilePath) =>
referencesInFile.has(filePath) &&
!seenFileAndExportsOfFile.has(referencingFilePath) && // Not already removed diagnostic file
fn(state, referencingFilePath as Path) // Dont add to seen since this is not yet done with the export removal
);
}
/**
* This is called after completing operation on the next affected file.
* The operations here are postponed to ensure that cancellation during the iteration is handled correctly
@ -808,11 +844,6 @@ namespace ts {
}
}
// Mark seen emitted files if there are pending files to be emitted
if (state.affectedFilesPendingEmit && state.program !== affected) {
(state.seenEmittedFiles || (state.seenEmittedFiles = createMap())).set((affected as SourceFile).path, true);
}
return toAffectedFileResult(
state,
// When whole program is affected, do emit only once (eg when --out or --outFile is specified)
@ -931,14 +962,7 @@ namespace ts {
// In case of emit builder, cache the files to be emitted
if (affectedFilesPendingEmit) {
state.affectedFilesPendingEmit = concatenate(state.affectedFilesPendingEmit, affectedFilesPendingEmit);
// affectedFilesPendingEmitIndex === undefined
// - means the emit state.affectedFilesPendingEmit was undefined before adding current affected files
// so start from 0 as array would be affectedFilesPendingEmit
// else, continue to iterate from existing index, the current set is appended to existing files
if (state.affectedFilesPendingEmitIndex === undefined) {
state.affectedFilesPendingEmitIndex = 0;
}
addToAffectedFilesPendingEmit(state, affectedFilesPendingEmit);
}
let diagnostics: Diagnostic[] | undefined;
@ -949,6 +973,17 @@ namespace ts {
}
}
function addToAffectedFilesPendingEmit(state: BuilderProgramState, affectedFilesPendingEmit: readonly Path[]) {
state.affectedFilesPendingEmit = concatenate(state.affectedFilesPendingEmit, affectedFilesPendingEmit);
// affectedFilesPendingEmitIndex === undefined
// - means the emit state.affectedFilesPendingEmit was undefined before adding current affected files
// so start from 0 as array would be affectedFilesPendingEmit
// else, continue to iterate from existing index, the current set is appended to existing files
if (state.affectedFilesPendingEmitIndex === undefined) {
state.affectedFilesPendingEmitIndex = 0;
}
}
function getMapOfReferencedSet(mapLike: MapLike<ReadonlyArray<string>> | undefined): ReadonlyMap<BuilderState.ReferencedSet> | undefined {
if (!mapLike) return undefined;
const map = createMap<BuilderState.ReferencedSet>();

View file

@ -321,7 +321,7 @@ namespace ts.BuilderState {
/**
* Returns if the shape of the signature has changed since last emit
*/
function updateShapeSignature(state: Readonly<BuilderState>, programOfThisState: Program, sourceFile: SourceFile, cacheToUpdateSignature: Map<string>, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, exportedModulesMapCache?: ComputingExportedModulesMap) {
export function updateShapeSignature(state: Readonly<BuilderState>, programOfThisState: Program, sourceFile: SourceFile, cacheToUpdateSignature: Map<string>, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, exportedModulesMapCache?: ComputingExportedModulesMap) {
Debug.assert(!!sourceFile);
Debug.assert(!exportedModulesMapCache || !!state.exportedModulesMap, "Compute visible to outside map only if visibleToOutsideReferencedMap present in the state");

View file

@ -92,6 +92,7 @@
"unittests/tsbuild/amdModulesWithOut.ts",
"unittests/tsbuild/emptyFiles.ts",
"unittests/tsbuild/graphOrdering.ts",
"unittests/tsbuild/inferredTypeFromTransitiveModule.ts",
"unittests/tsbuild/lateBoundSymbol.ts",
"unittests/tsbuild/missingExtendedFile.ts",
"unittests/tsbuild/outFile.ts",

View file

@ -0,0 +1,49 @@
namespace ts {
describe("unittests:: tsbuild:: inferredTypeFromTransitiveModule::", () => {
let projFs: vfs.FileSystem;
const { time, tick } = getTime();
before(() => {
projFs = loadProjectFromDisk("tests/projects/inferredTypeFromTransitiveModule", time);
});
after(() => {
projFs = undefined!;
});
verifyTsbuildOutput({
scenario: "inferred type from transitive module",
projFs: () => projFs,
time,
tick,
proj: "inferredTypeFromTransitiveModule",
rootNames: ["/src"],
expectedMapFileNames: emptyArray,
lastProjectOutputJs: `/src/obj/index.js`,
outputFiles: [
"/src/obj/bar.js", "/src/obj/bar.d.ts",
"/src/obj/bundling.js", "/src/obj/bundling.d.ts",
"/src/obj/lazyIndex.js", "/src/obj/lazyIndex.d.ts",
"/src/obj/index.js", "/src/obj/index.d.ts",
"/src/obj/tsconfig.tsbuildinfo"
],
initialBuild: {
modifyFs: noop,
expectedDiagnostics: [
getExpectedDiagnosticForProjectsInBuild("src/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/tsconfig.json", "src/obj/bar.js"],
[Diagnostics.Building_project_0, "/src/tsconfig.json"]
]
},
incrementalDtsChangedBuild: {
modifyFs: fs => replaceText(fs, "/src/bar.ts", "param: string", ""),
expectedDiagnostics: [
getExpectedDiagnosticForProjectsInBuild("src/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, "src/tsconfig.json", "src/obj/bar.js", "src/bar.ts"],
[Diagnostics.Building_project_0, "/src/tsconfig.json"],
[Diagnostics.Updating_unchanged_output_timestamps_of_project_0, "/src/tsconfig.json"]
]
},
baselineOnly: true,
verifyDiagnostics: true
});
});
}

View file

@ -0,0 +1,94 @@
//// [/src/bar.ts]
interface RawAction {
(...args: any[]): Promise<any> | void;
}
interface ActionFactory {
<T extends RawAction>(target: T): T;
}
declare function foo<U extends any[] = any[]>(): ActionFactory;
export default foo()(function foobar(): void {
});
//// [/src/obj/bar.d.ts]
declare const _default: () => void;
export default _default;
//// [/src/obj/bar.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = foo()(function foobar() {
});
//// [/src/obj/index.d.ts]
import { LazyAction } from './bundling';
export declare const lazyBar: LazyAction<() => void, typeof import("./lazyIndex")>;
//// [/src/obj/tsconfig.tsbuildinfo]
{
"program": {
"fileInfos": {
"/lib/lib.d.ts": {
"version": "-15964756381",
"signature": "-15964756381"
},
"/src/bar.ts": {
"version": "747071916",
"signature": "-9232740537"
},
"/src/bundling.ts": {
"version": "-21659820217",
"signature": "-40032907372"
},
"/src/global.d.ts": {
"version": "-9780226215",
"signature": "-9780226215"
},
"/src/lazyindex.ts": {
"version": "-6956449754",
"signature": "-6224542381"
},
"/src/index.ts": {
"version": "-11602502901",
"signature": "6256067474"
}
},
"options": {
"target": 1,
"declaration": true,
"outDir": "/src/obj",
"incremental": true,
"configFilePath": "/src/tsconfig.json"
},
"referencedMap": {
"/src/index.ts": [
"/src/bundling.ts",
"/src/lazyindex.ts"
],
"/src/lazyindex.ts": [
"/src/bar.ts"
]
},
"exportedModulesMap": {
"/src/index.ts": [
"/src/bundling.ts",
"/src/lazyindex.ts"
],
"/src/lazyindex.ts": [
"/src/bar.ts"
]
},
"semanticDiagnosticsPerFile": [
"/lib/lib.d.ts",
"/src/bar.ts",
"/src/bundling.ts",
"/src/global.d.ts",
"/src/index.ts",
"/src/lazyindex.ts"
]
},
"version": "FakeTSVersion"
}

View file

@ -0,0 +1,132 @@
//// [/src/obj/bar.d.ts]
declare const _default: (param: string) => void;
export default _default;
//// [/src/obj/bar.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = foo()(function foobar(param) {
});
//// [/src/obj/bundling.d.ts]
export declare class LazyModule<TModule> {
private importCallback;
constructor(importCallback: () => Promise<TModule>);
}
export declare class LazyAction<TAction extends (...args: any[]) => any, TModule> {
constructor(_lazyModule: LazyModule<TModule>, _getter: (module: TModule) => TAction);
}
//// [/src/obj/bundling.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var LazyModule = /** @class */ (function () {
function LazyModule(importCallback) {
this.importCallback = importCallback;
}
return LazyModule;
}());
exports.LazyModule = LazyModule;
var LazyAction = /** @class */ (function () {
function LazyAction(_lazyModule, _getter) {
}
return LazyAction;
}());
exports.LazyAction = LazyAction;
//// [/src/obj/index.d.ts]
import { LazyAction } from './bundling';
export declare const lazyBar: LazyAction<(param: string) => void, typeof import("./lazyIndex")>;
//// [/src/obj/index.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var bundling_1 = require("./bundling");
var lazyModule = new bundling_1.LazyModule(function () {
return Promise.resolve().then(function () { return require('./lazyIndex'); });
});
exports.lazyBar = new bundling_1.LazyAction(lazyModule, function (m) { return m.bar; });
//// [/src/obj/lazyIndex.d.ts]
export { default as bar } from './bar';
//// [/src/obj/lazyIndex.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var bar_1 = require("./bar");
exports.bar = bar_1.default;
//// [/src/obj/tsconfig.tsbuildinfo]
{
"program": {
"fileInfos": {
"/lib/lib.d.ts": {
"version": "-15964756381",
"signature": "-15964756381"
},
"/src/bar.ts": {
"version": "5936740878",
"signature": "11191036521"
},
"/src/bundling.ts": {
"version": "-21659820217",
"signature": "-40032907372"
},
"/src/global.d.ts": {
"version": "-9780226215",
"signature": "-9780226215"
},
"/src/lazyindex.ts": {
"version": "-6956449754",
"signature": "-6224542381"
},
"/src/index.ts": {
"version": "-11602502901",
"signature": "18468008756"
}
},
"options": {
"target": 1,
"declaration": true,
"outDir": "/src/obj",
"incremental": true,
"configFilePath": "/src/tsconfig.json"
},
"referencedMap": {
"/src/index.ts": [
"/src/bundling.ts",
"/src/lazyindex.ts"
],
"/src/lazyindex.ts": [
"/src/bar.ts"
]
},
"exportedModulesMap": {
"/src/index.ts": [
"/src/bundling.ts",
"/src/lazyindex.ts"
],
"/src/lazyindex.ts": [
"/src/bar.ts"
]
},
"semanticDiagnosticsPerFile": [
"/lib/lib.d.ts",
"/src/bar.ts",
"/src/bundling.ts",
"/src/global.d.ts",
"/src/index.ts",
"/src/lazyindex.ts"
]
},
"version": "FakeTSVersion"
}

View file

@ -0,0 +1,9 @@
interface RawAction {
(...args: any[]): Promise<any> | void;
}
interface ActionFactory {
<T extends RawAction>(target: T): T;
}
declare function foo<U extends any[] = any[]>(): ActionFactory;
export default foo()(function foobar(param: string): void {
});

View file

@ -0,0 +1,11 @@
export class LazyModule<TModule> {
constructor(private importCallback: () => Promise<TModule>) {}
}
export class LazyAction<
TAction extends (...args: any[]) => any,
TModule
> {
constructor(_lazyModule: LazyModule<TModule>, _getter: (module: TModule) => TAction) {
}
}

View file

@ -0,0 +1,6 @@
interface PromiseConstructor {
new <T>(): Promise<T>;
}
declare var Promise: PromiseConstructor;
interface Promise<T> {
}

View file

@ -0,0 +1,5 @@
import { LazyAction, LazyModule } from './bundling';
const lazyModule = new LazyModule(() =>
import('./lazyIndex')
);
export const lazyBar = new LazyAction(lazyModule, m => m.bar);

View file

@ -0,0 +1 @@
export { default as bar } from './bar';

View file

@ -0,0 +1,8 @@
{
"compilerOptions": {
"target": "es5",
"declaration": true,
"outDir": "obj",
"incremental": true
}
}