Handle seenEmittedFiles which was not being set when emit of a file was complete (#33145)

* Add test that fails because file is written multiple times
Reported from #33061

* Handle seenEmittedFiles which was not being set when emit of a file was complete.
It was issue only when errors are reported before emitting (which puts the files into pendingEmit that needs to check only in seenEmittedFiles)
If emit happens before semantic diagnostics query this issue is not repro, because the affected files come into play and those are being set correctly
Fixes #31398

* make baselining source map optional

* Handle emitDeclarationOnly in --build scenario

* Ensure we are using d.ts emit as signature even when --declarationMap is on (map files are emitted before d.ts)

* Move module specifiers to verifyTsBuildOutput

* implement create Hash to be default hashing plus data so we can verify it easily in baseline

* Remove failing baseline

* Accept correct baseline name
This commit is contained in:
Sheetal Nandi
2019-08-30 16:33:44 -07:00
committed by GitHub
parent 5ea4257e6e
commit 79bcb3d547
46 changed files with 1573 additions and 678 deletions

View File

@@ -94,6 +94,7 @@
"unittests/tsbuild/amdModulesWithOut.ts",
"unittests/tsbuild/containerOnlyReferenced.ts",
"unittests/tsbuild/demo.ts",
"unittests/tsbuild/emitDeclarationOnly.ts",
"unittests/tsbuild/emptyFiles.ts",
"unittests/tsbuild/graphOrdering.ts",
"unittests/tsbuild/inferredTypeFromTransitiveModule.ts",

View File

@@ -77,7 +77,7 @@ namespace ts {
[outputFiles[project.lib][ext.buildinfo], outputFiles[project.lib][ext.js], outputFiles[project.lib][ext.dts]],
[outputFiles[project.app][ext.buildinfo], outputFiles[project.app][ext.js], outputFiles[project.app][ext.dts]]
],
lastProjectOutputJs: outputFiles[project.app][ext.js],
lastProjectOutput: outputFiles[project.app][ext.js],
initialBuild: {
modifyFs
},
@@ -231,7 +231,7 @@ ${internal} export enum internalEnum { a, b, c }`);
[libOutputFile[ext.buildinfo], libOutputFile[ext.js], libOutputFile[ext.dts]],
[outputFiles[project.app][ext.buildinfo], outputFiles[project.app][ext.js], outputFiles[project.app][ext.dts]]
],
lastProjectOutputJs: outputFiles[project.app][ext.js],
lastProjectOutput: outputFiles[project.app][ext.js],
initialBuild: {
modifyFs,
expectedDiagnostics: [

View File

@@ -0,0 +1,109 @@
namespace ts {
describe("unittests:: tsbuild:: on project with emitDeclarationOnly set to true", () => {
let projFs: vfs.FileSystem;
const { time, tick } = getTime();
before(() => {
projFs = loadProjectFromDisk("tests/projects/emitDeclarationOnly", time);
});
after(() => {
projFs = undefined!;
});
function verifyEmitDeclarationOnly(disableMap?: true) {
verifyTsbuildOutput({
scenario: `only dts output in circular import project with emitDeclarationOnly${disableMap ? "" : " and declarationMap"}`,
projFs: () => projFs,
time,
tick,
proj: "emitDeclarationOnly",
rootNames: ["/src"],
lastProjectOutput: `/src/lib/index.d.ts`,
outputFiles: [
"/src/lib/a.d.ts",
"/src/lib/b.d.ts",
"/src/lib/c.d.ts",
"/src/lib/index.d.ts",
"/src/tsconfig.tsbuildinfo",
...(disableMap ? emptyArray : [
"/src/lib/a.d.ts.map",
"/src/lib/b.d.ts.map",
"/src/lib/c.d.ts.map",
"/src/lib/index.d.ts.map"
])
],
initialBuild: {
modifyFs: disableMap ?
(fs => replaceText(fs, "/src/tsconfig.json", `"declarationMap": true,`, "")) :
noop,
expectedDiagnostics: [
getExpectedDiagnosticForProjectsInBuild("src/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/tsconfig.json", "src/lib/a.d.ts"],
[Diagnostics.Building_project_0, "/src/tsconfig.json"]
]
},
incrementalDtsChangedBuild: {
modifyFs: fs => replaceText(fs, "/src/src/a.ts", "b: B;", "b: B; foo: any;"),
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/lib/a.d.ts", "src/src/a.ts"],
[Diagnostics.Building_project_0, "/src/tsconfig.json"]
]
},
baselineOnly: true,
verifyDiagnostics: true
});
}
verifyEmitDeclarationOnly();
verifyEmitDeclarationOnly(/*disableMap*/ true);
verifyTsbuildOutput({
scenario: `only dts output in non circular imports project with emitDeclarationOnly`,
projFs: () => projFs,
time,
tick,
proj: "emitDeclarationOnly",
rootNames: ["/src"],
lastProjectOutput: `/src/lib/a.d.ts`,
outputFiles: [
"/src/lib/a.d.ts",
"/src/lib/b.d.ts",
"/src/lib/c.d.ts",
"/src/tsconfig.tsbuildinfo",
"/src/lib/a.d.ts.map",
"/src/lib/b.d.ts.map",
"/src/lib/c.d.ts.map",
],
initialBuild: {
modifyFs: fs => {
fs.rimrafSync("/src/src/index.ts");
replaceText(fs, "/src/src/a.ts", `import { B } from "./b";`, `export class B { prop = "hello"; }`);
},
expectedDiagnostics: [
getExpectedDiagnosticForProjectsInBuild("src/tsconfig.json"),
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/tsconfig.json", "src/lib/a.d.ts"],
[Diagnostics.Building_project_0, "/src/tsconfig.json"]
]
},
incrementalDtsChangedBuild: {
modifyFs: fs => replaceText(fs, "/src/src/a.ts", "b: B;", "b: B; foo: any;"),
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/lib/a.d.ts", "src/src/a.ts"],
[Diagnostics.Building_project_0, "/src/tsconfig.json"]
]
},
incrementalDtsUnchangedBuild: {
modifyFs: fs => replaceText(fs, "/src/src/a.ts", "export interface A {", `class C { }
export interface A {`),
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/lib/a.d.ts", "src/src/a.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

@@ -102,7 +102,22 @@ namespace ts {
interface ReadonlyArray<T> {}
declare const console: { log(msg: any): void; };`;
export function loadProjectFromDisk(root: string, time?: vfs.FileSystemOptions["time"]): vfs.FileSystem {
export const symbolLibContent = `
interface SymbolConstructor {
readonly species: symbol;
readonly toStringTag: symbol;
}
declare var Symbol: SymbolConstructor;
interface Symbol {
readonly [Symbol.toStringTag]: string;
}
`;
export function loadProjectFromDisk(
root: string,
time?: vfs.FileSystemOptions["time"],
libContentToAppend?: string
): vfs.FileSystem {
const resolver = vfs.createResolver(Harness.IO);
const fs = new vfs.FileSystem(/*ignoreCase*/ true, {
files: {
@@ -112,12 +127,31 @@ declare const console: { log(msg: any): void; };`;
meta: { defaultLibLocation: "/lib" },
time
});
fs.mkdirSync("/lib");
fs.writeFileSync("/lib/lib.d.ts", libContent);
fs.makeReadonly();
addLibAndMakeReadonly(fs, libContentToAppend);
return fs;
}
export function loadProjectFromFiles(
files: vfs.FileSet,
time?: vfs.FileSystemOptions["time"],
libContentToAppend?: string
): vfs.FileSystem {
const fs = new vfs.FileSystem(/*ignoreCase*/ true, {
files,
cwd: "/",
meta: { defaultLibLocation: "/lib" },
time
});
addLibAndMakeReadonly(fs, libContentToAppend);
return fs;
}
function addLibAndMakeReadonly(fs: vfs.FileSystem, libContentToAppend?: string) {
fs.mkdirSync("/lib");
fs.writeFileSync("/lib/lib.d.ts", libContentToAppend ? `${libContent}${libContentToAppend}` : libContent);
fs.makeReadonly();
}
export function verifyOutputsPresent(fs: vfs.FileSystem, outputs: readonly string[]) {
for (const output of outputs) {
assert(fs.existsSync(output), `Expect file ${output} to exist`);
@@ -199,7 +233,7 @@ declare const console: { log(msg: any): void; };`;
fs: vfs.FileSystem;
tick: () => void;
rootNames: ReadonlyArray<string>;
expectedMapFileNames: ReadonlyArray<string>;
expectedMapFileNames?: ReadonlyArray<string>;
expectedBuildInfoFilesForSectionBaselines?: ReadonlyArray<BuildInfoSectionBaselineFiles>;
modifyFs: (fs: vfs.FileSystem) => void;
}
@@ -221,7 +255,7 @@ declare const console: { log(msg: any): void; };`;
return originalReadFile.call(host, path);
};
builder.build();
generateSourceMapBaselineFiles(fs, expectedMapFileNames);
if (expectedMapFileNames) generateSourceMapBaselineFiles(fs, expectedMapFileNames);
generateBuildInfoSectionBaselineFiles(fs, expectedBuildInfoFilesForSectionBaselines || emptyArray);
fs.makeReadonly();
return { fs, actualReadFileMap, host, builder };
@@ -268,9 +302,10 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
tick: () => void;
proj: string;
rootNames: ReadonlyArray<string>;
expectedMapFileNames: ReadonlyArray<string>;
/** map file names to generate baseline of */
expectedMapFileNames?: ReadonlyArray<string>;
expectedBuildInfoFilesForSectionBaselines?: ReadonlyArray<BuildInfoSectionBaselineFiles>;
lastProjectOutputJs: string;
lastProjectOutput: string;
initialBuild: BuildState;
outputFiles?: ReadonlyArray<string>;
incrementalDtsChangedBuild?: BuildState;
@@ -282,7 +317,7 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
export function verifyTsbuildOutput({
scenario, projFs, time, tick, proj, rootNames, outputFiles, baselineOnly, verifyDiagnostics,
expectedMapFileNames, expectedBuildInfoFilesForSectionBaselines, lastProjectOutputJs,
expectedMapFileNames, expectedBuildInfoFilesForSectionBaselines, lastProjectOutput,
initialBuild, incrementalDtsChangedBuild, incrementalDtsUnchangedBuild, incrementalHeaderChangedBuild
}: VerifyTsBuildInput) {
describe(`tsc --b ${proj}:: ${scenario}`, () => {
@@ -331,7 +366,7 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
let beforeBuildTime: number;
let afterBuildTime: number;
before(() => {
beforeBuildTime = fs.statSync(lastProjectOutputJs).mtimeMs;
beforeBuildTime = fs.statSync(lastProjectOutput).mtimeMs;
tick();
newFs = fs.shadow();
tick();
@@ -343,7 +378,7 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
expectedBuildInfoFilesForSectionBaselines,
modifyFs: incrementalModifyFs,
}));
afterBuildTime = newFs.statSync(lastProjectOutputJs).mtimeMs;
afterBuildTime = newFs.statSync(lastProjectOutput).mtimeMs;
});
after(() => {
newFs = undefined!;
@@ -359,6 +394,12 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
host.assertDiagnosticMessages(...(incrementalExpectedDiagnostics || emptyArray));
});
}
else {
// Build should pass without errors if not verifying diagnostics
it(`verify no errors`, () => {
host.assertErrors(/*empty*/);
});
}
it(`Generates files matching the baseline`, () => {
generateBaseline(newFs, proj, scenario, subScenario, fs);
});
@@ -373,7 +414,6 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
fs: newFs.shadow(),
tick,
rootNames,
expectedMapFileNames: emptyArray,
modifyFs: fs => {
// Delete output files
for (const outputFile of expectedOutputFiles) {

View File

@@ -16,8 +16,7 @@ namespace ts {
tick,
proj: "inferredTypeFromTransitiveModule",
rootNames: ["/src"],
expectedMapFileNames: emptyArray,
lastProjectOutputJs: `/src/obj/index.js`,
lastProjectOutput: `/src/obj/index.js`,
outputFiles: [
"/src/obj/bar.js", "/src/obj/bar.d.ts",
"/src/obj/bundling.js", "/src/obj/bundling.d.ts",

View File

@@ -16,8 +16,7 @@ namespace ts {
tick,
proj: "lateBoundSymbol",
rootNames: ["/src/tsconfig.json"],
expectedMapFileNames: emptyArray,
lastProjectOutputJs: "/src/src/main.js",
lastProjectOutput: "/src/src/main.js",
outputFiles: [
"/src/src/hkt.js",
"/src/src/main.js",

View File

@@ -1,8 +1,10 @@
namespace ts {
// https://github.com/microsoft/TypeScript/issues/31696
it("unittests:: tsbuild:: moduleSpecifiers:: synthesized module specifiers to referenced projects resolve correctly", () => {
const baseFs = vfs.createFromFileSystem(Harness.IO, /*ignoreCase*/ false, {
files: {
describe("unittests:: tsbuild:: moduleSpecifiers:: synthesized module specifiers to referenced projects resolve correctly", () => {
let projFs: vfs.FileSystem;
const { time, tick } = getTime();
before(() => {
projFs = loadProjectFromFiles({
"/src/common/nominal.ts": utils.dedent`
export declare type Nominal<T, Name extends string> = T & {
[Symbol.species]: Name;
@@ -71,7 +73,6 @@ namespace ts {
"skipLibCheck": true,
"rootDir": "./",
"outDir": "lib",
"lib": ["dom", "es2015", "es2015.symbol.wellknown"]
}
}`,
"/tsconfig.json": utils.dedent`{
@@ -83,16 +84,23 @@ namespace ts {
],
"include": []
}`
},
cwd: "/"
}, time, symbolLibContent);
});
after(() => {
projFs = undefined!;
});
verifyTsbuildOutput({
scenario: `synthesized module specifiers resolve correctly`,
projFs: () => projFs,
time,
tick,
proj: "moduleSpecifiers",
rootNames: ["/"],
lastProjectOutput: `/src/lib/index.d.ts`,
initialBuild: {
modifyFs: noop,
},
baselineOnly: true
});
const fs = baseFs.makeReadonly().shadow();
const sys = new fakes.System(fs, { executingFilePath: "/", newLine: "\n" });
const host = new fakes.SolutionBuilderHost(sys);
const builder = createSolutionBuilder(host, ["/tsconfig.json"], { dry: false, force: false, verbose: false });
builder.build();
// Prior to fixing GH31696 the import in `/lib/src/sub-project-2/index.d.ts` was `import("../../lib/src/common/nonterminal")`, which was invalid.
Harness.Baseline.runBaseline("tsbuild/moduleSpecifiers/initial-build/resolves-correctly.js", vfs.formatPatch(fs.diff(baseFs)));
});
}
}

View File

@@ -288,7 +288,7 @@ namespace ts {
rootNames: ["/src/third"],
expectedMapFileNames,
expectedBuildInfoFilesForSectionBaselines: expectedBuildInfoFilesForSectionBaselines || expectedTsbuildInfoFileNames,
lastProjectOutputJs: outputFiles[project.third][ext.js],
lastProjectOutput: outputFiles[project.third][ext.js],
initialBuild: {
modifyFs,
expectedDiagnostics: initialExpectedDiagnostics,

View File

@@ -617,7 +617,7 @@ export class cNew {}`);
"/src/core/index.d.ts.map",
"/src/logic/index.js.map"
],
lastProjectOutputJs: "/src/tests/index.js",
lastProjectOutput: "/src/tests/index.js",
initialBuild,
incrementalDtsChangedBuild: {
modifyFs: fs => appendText(fs, "/src/core/index.ts", `
@@ -727,7 +727,7 @@ class someClass { }`),
"/src/core/index.d.ts.map",
"/src/logic/index.js.map"
],
lastProjectOutputJs: "/src/tests/index.js",
lastProjectOutput: "/src/tests/index.js",
initialBuild,
incrementalDtsChangedBuild: {
modifyFs: fs => replaceText(fs, "/src/logic/tsconfig.json", `"declaration": true,`, `"declaration": true,
@@ -795,7 +795,7 @@ class someClass { }`),
"/src/core/index.d.ts.map",
"/src/logic/index.js.map"
],
lastProjectOutputJs: "/src/tests/index.js",
lastProjectOutput: "/src/tests/index.js",
initialBuild: {
modifyFs: fs => replaceText(fs, "/src/logic/tsconfig.json", `"composite": true,`, `"composite": true,
"tsBuildInfoFile": "ownFile.tsbuildinfo",`),
@@ -851,8 +851,7 @@ class someClass { }`),
tick,
proj: "sample1",
rootNames: ["/src/core"],
expectedMapFileNames: emptyArray,
lastProjectOutputJs: "/src/core/index.js",
lastProjectOutput: "/src/core/index.js",
initialBuild: {
modifyFs: fs => fs.writeFileSync("/src/core/tsconfig.json", `{
"compilerOptions": {
@@ -892,8 +891,7 @@ class someClass { }`),
tick,
proj: "sample1",
rootNames: ["/src/core"],
expectedMapFileNames: emptyArray,
lastProjectOutputJs: "/src/core/index.js",
lastProjectOutput: "/src/core/index.js",
initialBuild: {
modifyFs: fs => {
fs.writeFileSync("/lib/lib.esnext.full.d.ts", `/// <reference no-default-lib="true"/>
@@ -942,8 +940,7 @@ class someClass { }`),
tick,
proj: "sample1",
rootNames: ["/src/core"],
expectedMapFileNames: emptyArray,
lastProjectOutputJs: "/src/core/index.js",
lastProjectOutput: "/src/core/index.js",
initialBuild: {
modifyFs: fs => fs.writeFileSync("/src/core/tsconfig.json", `{
"compilerOptions": {
@@ -981,8 +978,7 @@ class someClass { }`),
tick,
proj: "sample1",
rootNames: ["/src/tests"],
expectedMapFileNames: emptyArray,
lastProjectOutputJs: "/src/tests/index.js",
lastProjectOutput: "/src/tests/index.js",
initialBuild: {
modifyFs: fs => fs.writeFileSync("/src/tests/tsconfig.json", `{
"references": [

View File

@@ -16,17 +16,17 @@ namespace ts.tscWatch {
expectedIncrementalEmit?: ReadonlyArray<File>;
expectedIncrementalErrors?: ReadonlyArray<string>;
}
function verifyIncrementalWatchEmit(input: VerifyIncrementalWatchEmitInput) {
function verifyIncrementalWatchEmit(input: () => VerifyIncrementalWatchEmitInput) {
it("with tsc --w", () => {
verifyIncrementalWatchEmitWorker({
input,
input: input(),
emitAndReportErrors: createWatchOfConfigFile,
verifyErrors: checkOutputErrorsInitial
});
});
it("with tsc", () => {
verifyIncrementalWatchEmitWorker({
input,
input: input(),
emitAndReportErrors: incrementalBuild,
verifyErrors: checkNormalBuildErrors
});
@@ -122,7 +122,7 @@ namespace ts.tscWatch {
function checkFileEmit(actual: Map<string>, expected: ReadonlyArray<File>) {
assert.equal(actual.size, expected.length, `Actual: ${JSON.stringify(arrayFrom(actual.entries()), /*replacer*/ undefined, " ")}\nExpected: ${JSON.stringify(expected, /*replacer*/ undefined, " ")}`);
expected.forEach(file => {
for (const file of expected) {
let expectedContent = file.content;
let actualContent = actual.get(file.path);
if (isBuildInfoFile(file.path)) {
@@ -130,7 +130,7 @@ namespace ts.tscWatch {
expectedContent = sanitizeBuildInfo(expectedContent);
}
assert.equal(actualContent, expectedContent, `Emit for ${file.path}`);
});
}
}
const libFileInfo: BuilderState.FileInfo = {
@@ -170,7 +170,7 @@ namespace ts.tscWatch {
describe("own file emit without errors", () => {
function verify(optionsToExtend?: CompilerOptions, expectedBuildinfoOptions?: CompilerOptions) {
const modifiedFile2Content = file2.content.replace("y", "z").replace("20", "10");
verifyIncrementalWatchEmit({
verifyIncrementalWatchEmit(() => ({
files: [libFile, file1, file2, configFile],
optionsToExtend,
expectedInitialEmit: [
@@ -226,7 +226,7 @@ namespace ts.tscWatch {
}
],
expectedIncrementalErrors: emptyArray,
});
}));
}
verify();
describe("with commandline parameters that are not relative", () => {
@@ -259,7 +259,7 @@ namespace ts.tscWatch {
"file2.ts(1,7): error TS2322: Type '20' is not assignable to type 'string'.\n"
];
const modifiedFile1Content = file1.content.replace("x", "z");
verifyIncrementalWatchEmit({
verifyIncrementalWatchEmit(() => ({
files: [libFile, file1, fileModified, configFile],
expectedInitialEmit: [
file1Js,
@@ -320,7 +320,7 @@ namespace ts.tscWatch {
}
],
expectedIncrementalErrors: file2Errors,
});
}));
});
describe("with --out", () => {
@@ -332,7 +332,7 @@ namespace ts.tscWatch {
path: `${project}/out.js`,
content: "var x = 10;\nvar y = 20;\n"
};
verifyIncrementalWatchEmit({
verifyIncrementalWatchEmit(() => ({
files: [libFile, file1, file2, config],
expectedInitialEmit: [
outFile,
@@ -353,7 +353,7 @@ namespace ts.tscWatch {
}
],
expectedInitialErrors: emptyArray
});
}));
});
});
@@ -397,7 +397,7 @@ namespace ts.tscWatch {
describe("own file emit without errors", () => {
const modifiedFile2Content = file2.content.replace("y", "z").replace("20", "10");
verifyIncrementalWatchEmit({
verifyIncrementalWatchEmit(() => ({
files: [libFile, file1, file2, config],
expectedInitialEmit: [
file1Js,
@@ -451,7 +451,7 @@ namespace ts.tscWatch {
}
],
expectedIncrementalErrors: emptyArray,
});
}));
});
describe("own file emit with errors", () => {
@@ -479,7 +479,7 @@ namespace ts.tscWatch {
"file2.ts(1,14): error TS2322: Type '20' is not assignable to type 'string'.\n"
];
const modifiedFile1Content = file1.content.replace("x = 10", "z = 10");
verifyIncrementalWatchEmit({
verifyIncrementalWatchEmit(() => ({
files: [libFile, file1, fileModified, config],
expectedInitialEmit: [
file1Js,
@@ -541,7 +541,7 @@ namespace ts.tscWatch {
}
],
expectedIncrementalErrors: file2Errors,
});
}));
it("verify that state is read correctly", () => {
const system = createWatchedSystem([libFile, file1, fileModified, config], { currentDirectory: project });
@@ -604,7 +604,7 @@ namespace ts.tscWatch {
});
`;
}
verifyIncrementalWatchEmit({
verifyIncrementalWatchEmit(() => ({
files: [libFile, file1, file2, config],
expectedInitialEmit: [
outFile,
@@ -625,7 +625,140 @@ namespace ts.tscWatch {
}
],
expectedInitialErrors: emptyArray
});
}));
});
});
describe("incremental with circular references", () => {
function getFileInfo(content: string): BuilderState.FileInfo {
const signature = Harness.mockHash(content);
return { version: signature, signature };
}
const config: File = {
path: configFile.path,
content: JSON.stringify({
compilerOptions: {
incremental: true,
target: "es5",
module: "commonjs",
declaration: true,
emitDeclarationOnly: true
}
})
};
const aTs: File = {
path: `${project}/a.ts`,
content: `import { B } from "./b";
export interface A {
b: B;
}
`
};
const bTs: File = {
path: `${project}/b.ts`,
content: `import { C } from "./c";
export interface B {
b: C;
}
`
};
const cTs: File = {
path: `${project}/c.ts`,
content: `import { A } from "./a";
export interface C {
a: A;
}
`
};
const indexTs: File = {
path: `${project}/index.ts`,
content: `export { A } from "./a";
export { B } from "./b";
export { C } from "./c";
`
};
verifyIncrementalWatchEmit(() => {
const referencedMap: MapLike<string[]> = {
"./a.ts": ["./b.ts"],
"./b.ts": ["./c.ts"],
"./c.ts": ["./a.ts"],
"./index.ts": ["./a.ts", "./b.ts", "./c.ts"],
};
const initialProgram: ProgramBuildInfo = {
fileInfos: {
[libFilePath]: libFileInfo,
"./c.ts": getFileInfo(cTs.content),
"./b.ts": getFileInfo(bTs.content),
"./a.ts": getFileInfo(aTs.content),
"./index.ts": getFileInfo(indexTs.content)
},
options: {
incremental: true,
target: ScriptTarget.ES5,
module: ModuleKind.CommonJS,
declaration: true,
emitDeclarationOnly: true,
configFilePath: "./tsconfig.json"
},
referencedMap,
exportedModulesMap: referencedMap,
semanticDiagnosticsPerFile: [
libFilePath,
"./a.ts",
"./b.ts",
"./c.ts",
"./index.ts",
]
};
const { fileInfos, ...rest } = initialProgram;
const expectedADts: File = { path: `${project}/a.d.ts`, content: aTs.content };
const expectedBDts: File = { path: `${project}/b.d.ts`, content: bTs.content };
const expectedCDts: File = { path: `${project}/c.d.ts`, content: cTs.content };
const expectedIndexDts: File = { path: `${project}/index.d.ts`, content: indexTs.content };
const modifiedATsContent = aTs.content.replace("b: B;", `b: B;
foo: any;`);
return {
files: [libFile, aTs, bTs, cTs, indexTs, config],
expectedInitialEmit: [
expectedADts,
expectedBDts,
expectedCDts,
expectedIndexDts,
{
path: `${project}/tsconfig.tsbuildinfo`,
content: getBuildInfoText({
program: initialProgram,
version
})
}
],
expectedInitialErrors: emptyArray,
modifyFs: host => host.writeFile(aTs.path, modifiedATsContent),
expectedIncrementalEmit: [
{ path: expectedADts.path, content: modifiedATsContent },
expectedBDts,
expectedCDts,
expectedIndexDts,
{
path: `${project}/tsconfig.tsbuildinfo`,
content: getBuildInfoText({
program: {
fileInfos: {
[libFilePath]: libFileInfo,
"./c.ts": getFileInfo(cTs.content),
"./b.ts": getFileInfo(bTs.content),
"./a.ts": getFileInfo(modifiedATsContent),
"./index.ts": getFileInfo(indexTs.content)
},
...rest
},
version
})
}
],
expectedIncrementalErrors: emptyArray
};
});
});
});