Skip to content

Commit 79bcb3d

Browse files
authored
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
1 parent 5ea4257 commit 79bcb3d

File tree

46 files changed

+1571
-676
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1571
-676
lines changed

src/compiler/builder.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ namespace ts {
137137
*/
138138
emittedBuildInfo?: boolean;
139139
/**
140-
* Already seen affected files
140+
* Already seen emitted files
141141
*/
142142
seenEmittedFiles: Map<true> | undefined;
143143
/**
@@ -329,7 +329,6 @@ namespace ts {
329329
handleDtsMayChangeOfAffectedFile(state, affectedFile, cancellationToken, computeHash);
330330
return affectedFile;
331331
}
332-
seenAffectedFiles.set(affectedFile.path, true);
333332
affectedFilesIndex++;
334333
}
335334

@@ -549,7 +548,7 @@ namespace ts {
549548
* This is called after completing operation on the next affected file.
550549
* The operations here are postponed to ensure that cancellation during the iteration is handled correctly
551550
*/
552-
function doneWithAffectedFile(state: BuilderProgramState, affected: SourceFile | Program, isPendingEmit?: boolean, isBuildInfoEmit?: boolean) {
551+
function doneWithAffectedFile(state: BuilderProgramState, affected: SourceFile | Program, isPendingEmit?: boolean, isBuildInfoEmit?: boolean, isEmitResult?: boolean) {
553552
if (isBuildInfoEmit) {
554553
state.emittedBuildInfo = true;
555554
}
@@ -559,6 +558,9 @@ namespace ts {
559558
}
560559
else {
561560
state.seenAffectedFiles!.set((affected as SourceFile).path, true);
561+
if (isEmitResult) {
562+
(state.seenEmittedFiles || (state.seenEmittedFiles = createMap())).set((affected as SourceFile).path, true);
563+
}
562564
if (isPendingEmit) {
563565
state.affectedFilesPendingEmitIndex!++;
564566
}
@@ -576,6 +578,14 @@ namespace ts {
576578
return { result, affected };
577579
}
578580

581+
/**
582+
* Returns the result with affected file
583+
*/
584+
function toAffectedFileEmitResult(state: BuilderProgramState, result: EmitResult, affected: SourceFile | Program, isPendingEmit?: boolean, isBuildInfoEmit?: boolean): AffectedFileResult<EmitResult> {
585+
doneWithAffectedFile(state, affected, isPendingEmit, isBuildInfoEmit, /*isEmitResult*/ true);
586+
return { result, affected };
587+
}
588+
579589
/**
580590
* Gets the semantic diagnostics either from cache if present, or otherwise from program and caches it
581591
* Note that it is assumed that the when asked about semantic diagnostics, the file has been taken out of affected files/changed file set
@@ -849,7 +859,7 @@ namespace ts {
849859
}
850860

851861
const affected = Debug.assertDefined(state.program);
852-
return toAffectedFileResult(
862+
return toAffectedFileEmitResult(
853863
state,
854864
// When whole program is affected, do emit only once (eg when --out or --outFile is specified)
855865
// Otherwise just affected file
@@ -872,14 +882,14 @@ namespace ts {
872882
}
873883
}
874884

875-
return toAffectedFileResult(
885+
return toAffectedFileEmitResult(
876886
state,
877887
// When whole program is affected, do emit only once (eg when --out or --outFile is specified)
878888
// Otherwise just affected file
879889
Debug.assertDefined(state.program).emit(affected === state.program ? undefined : affected as SourceFile, writeFile || maybeBind(host, host.writeFile), cancellationToken, emitOnlyDtsFiles, customTransformers),
880890
affected,
881-
isPendingEmitFile
882-
);
891+
isPendingEmitFile,
892+
);
883893
}
884894

885895
/**

src/compiler/builderState.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,8 +345,13 @@ namespace ts.BuilderState {
345345
}
346346
else {
347347
const emitOutput = getFileEmitOutput(programOfThisState, sourceFile, /*emitOnlyDtsFiles*/ true, cancellationToken);
348-
if (emitOutput.outputFiles && emitOutput.outputFiles.length > 0) {
349-
latestSignature = computeHash(emitOutput.outputFiles[0].text);
348+
const firstDts = emitOutput.outputFiles &&
349+
programOfThisState.getCompilerOptions().declarationMap ?
350+
emitOutput.outputFiles.length > 1 ? emitOutput.outputFiles[1] : undefined :
351+
emitOutput.outputFiles.length > 0 ? emitOutput.outputFiles[0] : undefined;
352+
if (firstDts) {
353+
Debug.assert(fileExtensionIs(firstDts.name, Extension.Dts), "File extension for signature expected to be dts", () => `Found: ${getAnyExtensionFromPath(firstDts.name)} for ${firstDts.name}:: All output files: ${JSON.stringify(emitOutput.outputFiles.map(f => f.name))}`);
354+
latestSignature = computeHash(firstDts.text);
350355
if (exportedModulesMapCache && latestSignature !== prevSignature) {
351356
updateExportedModules(sourceFile, emitOutput.exportedModulesFromDeclarationEmit, exportedModulesMapCache);
352357
}

src/compiler/emitter.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ namespace ts {
155155
}
156156

157157
function getOutputJSFileName(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean) {
158+
if (configFile.options.emitDeclarationOnly) return undefined;
158159
const isJsonFile = fileExtensionIs(inputFileName, Extension.Json);
159160
const outputFileName = changeExtension(
160161
getOutputPathWithoutChangingExt(inputFileName, configFile, ignoreCase, configFile.options.outDir),
@@ -187,7 +188,7 @@ namespace ts {
187188
const js = getOutputJSFileName(inputFileName, configFile, ignoreCase);
188189
addOutput(js);
189190
if (fileExtensionIs(inputFileName, Extension.Json)) continue;
190-
if (configFile.options.sourceMap) {
191+
if (js && configFile.options.sourceMap) {
191192
addOutput(`${js}.map`);
192193
}
193194
if (getEmitDeclarations(configFile.options) && hasTSFileExtension(inputFileName)) {
@@ -214,6 +215,10 @@ namespace ts {
214215
if (fileExtensionIs(inputFileName, Extension.Dts)) continue;
215216
const jsFilePath = getOutputJSFileName(inputFileName, configFile, ignoreCase);
216217
if (jsFilePath) return jsFilePath;
218+
if (fileExtensionIs(inputFileName, Extension.Json)) continue;
219+
if (getEmitDeclarations(configFile.options) && hasTSFileExtension(inputFileName)) {
220+
return getOutputDeclarationFileName(inputFileName, configFile, ignoreCase);
221+
}
217222
}
218223
const buildInfoPath = getOutputPathForBuildInfo(configFile.options);
219224
if (buildInfoPath) return buildInfoPath;

src/harness/fakes.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,10 @@ ${indentText}${text}`;
545545
super.writeFile(fileName, ts.getBuildInfoText(buildInfo), writeByteOrderMark);
546546
}
547547

548+
createHash(data: string) {
549+
return `${ts.generateDjb2Hash(data)}-${data}`;
550+
}
551+
548552
now() {
549553
return new Date(this.sys.vfs.time());
550554
}
@@ -571,6 +575,15 @@ Actual: ${JSON.stringify(actual, /*replacer*/ undefined, " ")}
571575
Expected: ${JSON.stringify(expected, /*replacer*/ undefined, " ")}`);
572576
}
573577

578+
assertErrors(...expectedDiagnostics: ExpectedErrorDiagnostic[]) {
579+
const actual = this.diagnostics.filter(d => d.kind === DiagnosticKind.Error).map(diagnosticToText);
580+
const expected = expectedDiagnostics.map(expectedDiagnosticToText);
581+
assert.deepEqual(actual, expected, `Diagnostics arrays did not match:
582+
Actual: ${JSON.stringify(actual, /*replacer*/ undefined, " ")}
583+
Expected: ${JSON.stringify(expected, /*replacer*/ undefined, " ")}
584+
Actual All:: ${JSON.stringify(this.diagnostics.slice().map(diagnosticToText), /*replacer*/ undefined, " ")}`);
585+
}
586+
574587
printDiagnostics(header = "== Diagnostics ==") {
575588
const out = ts.createDiagnosticReporter(ts.sys);
576589
ts.sys.write(header + "\r\n");

src/testRunner/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
"unittests/tsbuild/amdModulesWithOut.ts",
9595
"unittests/tsbuild/containerOnlyReferenced.ts",
9696
"unittests/tsbuild/demo.ts",
97+
"unittests/tsbuild/emitDeclarationOnly.ts",
9798
"unittests/tsbuild/emptyFiles.ts",
9899
"unittests/tsbuild/graphOrdering.ts",
99100
"unittests/tsbuild/inferredTypeFromTransitiveModule.ts",

src/testRunner/unittests/tsbuild/amdModulesWithOut.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ namespace ts {
7777
[outputFiles[project.lib][ext.buildinfo], outputFiles[project.lib][ext.js], outputFiles[project.lib][ext.dts]],
7878
[outputFiles[project.app][ext.buildinfo], outputFiles[project.app][ext.js], outputFiles[project.app][ext.dts]]
7979
],
80-
lastProjectOutputJs: outputFiles[project.app][ext.js],
80+
lastProjectOutput: outputFiles[project.app][ext.js],
8181
initialBuild: {
8282
modifyFs
8383
},
@@ -231,7 +231,7 @@ ${internal} export enum internalEnum { a, b, c }`);
231231
[libOutputFile[ext.buildinfo], libOutputFile[ext.js], libOutputFile[ext.dts]],
232232
[outputFiles[project.app][ext.buildinfo], outputFiles[project.app][ext.js], outputFiles[project.app][ext.dts]]
233233
],
234-
lastProjectOutputJs: outputFiles[project.app][ext.js],
234+
lastProjectOutput: outputFiles[project.app][ext.js],
235235
initialBuild: {
236236
modifyFs,
237237
expectedDiagnostics: [
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
namespace ts {
2+
describe("unittests:: tsbuild:: on project with emitDeclarationOnly set to true", () => {
3+
let projFs: vfs.FileSystem;
4+
const { time, tick } = getTime();
5+
before(() => {
6+
projFs = loadProjectFromDisk("tests/projects/emitDeclarationOnly", time);
7+
});
8+
after(() => {
9+
projFs = undefined!;
10+
});
11+
12+
function verifyEmitDeclarationOnly(disableMap?: true) {
13+
verifyTsbuildOutput({
14+
scenario: `only dts output in circular import project with emitDeclarationOnly${disableMap ? "" : " and declarationMap"}`,
15+
projFs: () => projFs,
16+
time,
17+
tick,
18+
proj: "emitDeclarationOnly",
19+
rootNames: ["/src"],
20+
lastProjectOutput: `/src/lib/index.d.ts`,
21+
outputFiles: [
22+
"/src/lib/a.d.ts",
23+
"/src/lib/b.d.ts",
24+
"/src/lib/c.d.ts",
25+
"/src/lib/index.d.ts",
26+
"/src/tsconfig.tsbuildinfo",
27+
...(disableMap ? emptyArray : [
28+
"/src/lib/a.d.ts.map",
29+
"/src/lib/b.d.ts.map",
30+
"/src/lib/c.d.ts.map",
31+
"/src/lib/index.d.ts.map"
32+
])
33+
],
34+
initialBuild: {
35+
modifyFs: disableMap ?
36+
(fs => replaceText(fs, "/src/tsconfig.json", `"declarationMap": true,`, "")) :
37+
noop,
38+
expectedDiagnostics: [
39+
getExpectedDiagnosticForProjectsInBuild("src/tsconfig.json"),
40+
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/tsconfig.json", "src/lib/a.d.ts"],
41+
[Diagnostics.Building_project_0, "/src/tsconfig.json"]
42+
]
43+
},
44+
incrementalDtsChangedBuild: {
45+
modifyFs: fs => replaceText(fs, "/src/src/a.ts", "b: B;", "b: B; foo: any;"),
46+
expectedDiagnostics: [
47+
getExpectedDiagnosticForProjectsInBuild("src/tsconfig.json"),
48+
[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"],
49+
[Diagnostics.Building_project_0, "/src/tsconfig.json"]
50+
]
51+
},
52+
baselineOnly: true,
53+
verifyDiagnostics: true
54+
});
55+
}
56+
verifyEmitDeclarationOnly();
57+
verifyEmitDeclarationOnly(/*disableMap*/ true);
58+
59+
verifyTsbuildOutput({
60+
scenario: `only dts output in non circular imports project with emitDeclarationOnly`,
61+
projFs: () => projFs,
62+
time,
63+
tick,
64+
proj: "emitDeclarationOnly",
65+
rootNames: ["/src"],
66+
lastProjectOutput: `/src/lib/a.d.ts`,
67+
outputFiles: [
68+
"/src/lib/a.d.ts",
69+
"/src/lib/b.d.ts",
70+
"/src/lib/c.d.ts",
71+
"/src/tsconfig.tsbuildinfo",
72+
"/src/lib/a.d.ts.map",
73+
"/src/lib/b.d.ts.map",
74+
"/src/lib/c.d.ts.map",
75+
],
76+
initialBuild: {
77+
modifyFs: fs => {
78+
fs.rimrafSync("/src/src/index.ts");
79+
replaceText(fs, "/src/src/a.ts", `import { B } from "./b";`, `export class B { prop = "hello"; }`);
80+
},
81+
expectedDiagnostics: [
82+
getExpectedDiagnosticForProjectsInBuild("src/tsconfig.json"),
83+
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/tsconfig.json", "src/lib/a.d.ts"],
84+
[Diagnostics.Building_project_0, "/src/tsconfig.json"]
85+
]
86+
},
87+
incrementalDtsChangedBuild: {
88+
modifyFs: fs => replaceText(fs, "/src/src/a.ts", "b: B;", "b: B; foo: any;"),
89+
expectedDiagnostics: [
90+
getExpectedDiagnosticForProjectsInBuild("src/tsconfig.json"),
91+
[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"],
92+
[Diagnostics.Building_project_0, "/src/tsconfig.json"]
93+
]
94+
},
95+
incrementalDtsUnchangedBuild: {
96+
modifyFs: fs => replaceText(fs, "/src/src/a.ts", "export interface A {", `class C { }
97+
export interface A {`),
98+
expectedDiagnostics: [
99+
getExpectedDiagnosticForProjectsInBuild("src/tsconfig.json"),
100+
[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"],
101+
[Diagnostics.Building_project_0, "/src/tsconfig.json"],
102+
[Diagnostics.Updating_unchanged_output_timestamps_of_project_0, "/src/tsconfig.json"]
103+
]
104+
},
105+
baselineOnly: true,
106+
verifyDiagnostics: true
107+
});
108+
});
109+
}

0 commit comments

Comments
 (0)