Skip to content

Commit 4aad78b

Browse files
authored
implement create Hash to be default hashing plus data so we can verify it easily in baseline
1 parent 775a4dd commit 4aad78b

File tree

46 files changed

+1556
-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

+1556
-676
lines changed

src/compiler/builder.ts

+17-7
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

+7-2
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

+6-1
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

+4
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,10 @@ namespace fakes {
442442
super.writeFile(fileName, ts.getBuildInfoText(buildInfo), writeByteOrderMark);
443443
}
444444

445+
createHash(data: string) {
446+
return `${ts.generateDjb2Hash(data)}-${data}`;
447+
}
448+
445449
now() {
446450
return new Date(this.sys.vfs.time());
447451
}

src/testRunner/tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
"unittests/services/transpile.ts",
9494
"unittests/tsbuild/amdModulesWithOut.ts",
9595
"unittests/tsbuild/containerOnlyReferenced.ts",
96+
"unittests/tsbuild/emitDeclarationOnly.ts",
9697
"unittests/tsbuild/emptyFiles.ts",
9798
"unittests/tsbuild/graphOrdering.ts",
9899
"unittests/tsbuild/inferredTypeFromTransitiveModule.ts",

src/testRunner/unittests/tsbuild/amdModulesWithOut.ts

+2-2
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: [
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+
}

src/testRunner/unittests/tsbuild/helpers.ts

+45-11
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,22 @@ namespace ts {
6666
interface ReadonlyArray<T> {}
6767
declare const console: { log(msg: any): void; };`;
6868

69-
export function loadProjectFromDisk(root: string, time?: vfs.FileSystemOptions["time"]): vfs.FileSystem {
69+
export const symbolLibContent = `
70+
interface SymbolConstructor {
71+
readonly species: symbol;
72+
readonly toStringTag: symbol;
73+
}
74+
declare var Symbol: SymbolConstructor;
75+
interface Symbol {
76+
readonly [Symbol.toStringTag]: string;
77+
}
78+
`;
79+
80+
export function loadProjectFromDisk(
81+
root: string,
82+
time?: vfs.FileSystemOptions["time"],
83+
libContentToAppend?: string
84+
): vfs.FileSystem {
7085
const resolver = vfs.createResolver(Harness.IO);
7186
const fs = new vfs.FileSystem(/*ignoreCase*/ true, {
7287
files: {
@@ -76,10 +91,29 @@ declare const console: { log(msg: any): void; };`;
7691
meta: { defaultLibLocation: "/lib" },
7792
time
7893
});
94+
addLibAndMakeReadonly(fs, libContentToAppend);
95+
return fs;
96+
}
97+
98+
export function loadProjectFromFiles(
99+
files: vfs.FileSet,
100+
time?: vfs.FileSystemOptions["time"],
101+
libContentToAppend?: string
102+
): vfs.FileSystem {
103+
const fs = new vfs.FileSystem(/*ignoreCase*/ true, {
104+
files,
105+
cwd: "/",
106+
meta: { defaultLibLocation: "/lib" },
107+
time
108+
});
109+
addLibAndMakeReadonly(fs, libContentToAppend);
110+
return fs;
111+
}
112+
113+
function addLibAndMakeReadonly(fs: vfs.FileSystem, libContentToAppend?: string) {
79114
fs.mkdirSync("/lib");
80-
fs.writeFileSync("/lib/lib.d.ts", libContent);
115+
fs.writeFileSync("/lib/lib.d.ts", libContentToAppend ? `${libContent}${libContentToAppend}` : libContent);
81116
fs.makeReadonly();
82-
return fs;
83117
}
84118

85119
export function verifyOutputsPresent(fs: vfs.FileSystem, outputs: readonly string[]) {
@@ -163,7 +197,7 @@ declare const console: { log(msg: any): void; };`;
163197
fs: vfs.FileSystem;
164198
tick: () => void;
165199
rootNames: ReadonlyArray<string>;
166-
expectedMapFileNames: ReadonlyArray<string>;
200+
expectedMapFileNames?: ReadonlyArray<string>;
167201
expectedBuildInfoFilesForSectionBaselines?: ReadonlyArray<BuildInfoSectionBaselineFiles>;
168202
modifyFs: (fs: vfs.FileSystem) => void;
169203
}
@@ -185,7 +219,7 @@ declare const console: { log(msg: any): void; };`;
185219
return originalReadFile.call(host, path);
186220
};
187221
builder.build();
188-
generateSourceMapBaselineFiles(fs, expectedMapFileNames);
222+
if (expectedMapFileNames) generateSourceMapBaselineFiles(fs, expectedMapFileNames);
189223
generateBuildInfoSectionBaselineFiles(fs, expectedBuildInfoFilesForSectionBaselines || emptyArray);
190224
fs.makeReadonly();
191225
return { fs, actualReadFileMap, host, builder };
@@ -232,9 +266,10 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
232266
tick: () => void;
233267
proj: string;
234268
rootNames: ReadonlyArray<string>;
235-
expectedMapFileNames: ReadonlyArray<string>;
269+
/** map file names to generate baseline of */
270+
expectedMapFileNames?: ReadonlyArray<string>;
236271
expectedBuildInfoFilesForSectionBaselines?: ReadonlyArray<BuildInfoSectionBaselineFiles>;
237-
lastProjectOutputJs: string;
272+
lastProjectOutput: string;
238273
initialBuild: BuildState;
239274
outputFiles?: ReadonlyArray<string>;
240275
incrementalDtsChangedBuild?: BuildState;
@@ -246,7 +281,7 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
246281

247282
export function verifyTsbuildOutput({
248283
scenario, projFs, time, tick, proj, rootNames, outputFiles, baselineOnly, verifyDiagnostics,
249-
expectedMapFileNames, expectedBuildInfoFilesForSectionBaselines, lastProjectOutputJs,
284+
expectedMapFileNames, expectedBuildInfoFilesForSectionBaselines, lastProjectOutput,
250285
initialBuild, incrementalDtsChangedBuild, incrementalDtsUnchangedBuild, incrementalHeaderChangedBuild
251286
}: VerifyTsBuildInput) {
252287
describe(`tsc --b ${proj}:: ${scenario}`, () => {
@@ -295,7 +330,7 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
295330
let beforeBuildTime: number;
296331
let afterBuildTime: number;
297332
before(() => {
298-
beforeBuildTime = fs.statSync(lastProjectOutputJs).mtimeMs;
333+
beforeBuildTime = fs.statSync(lastProjectOutput).mtimeMs;
299334
tick();
300335
newFs = fs.shadow();
301336
tick();
@@ -307,7 +342,7 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
307342
expectedBuildInfoFilesForSectionBaselines,
308343
modifyFs: incrementalModifyFs,
309344
}));
310-
afterBuildTime = newFs.statSync(lastProjectOutputJs).mtimeMs;
345+
afterBuildTime = newFs.statSync(lastProjectOutput).mtimeMs;
311346
});
312347
after(() => {
313348
newFs = undefined!;
@@ -337,7 +372,6 @@ Mismatch Actual(path, actual, expected): ${JSON.stringify(arrayFrom(mapDefinedIt
337372
fs: newFs.shadow(),
338373
tick,
339374
rootNames,
340-
expectedMapFileNames: emptyArray,
341375
modifyFs: fs => {
342376
// Delete output files
343377
for (const outputFile of expectedOutputFiles) {

0 commit comments

Comments
 (0)