Skip to content

Commit 7a59e45

Browse files
authored
During emit, if shape signature for the file is same as version, then update it with emitted d.ts file (#48616)
* If we are writing dts file and have used file text as version, we can update the signature when doing actual emit * Make WriteFileCallback Api ready for future * Assert that there is only single source file when emitting d.ts file
1 parent 45faac7 commit 7a59e45

File tree

183 files changed

+2312
-2784
lines changed

Some content is hidden

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

183 files changed

+2312
-2784
lines changed

src/compiler/builder.ts

+42-2
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ namespace ts {
161161
* true if program has been emitted
162162
*/
163163
programEmitComplete?: true;
164+
/** Stores list of files that change signature during emit - test only */
165+
filesChangingSignature?: Set<Path>;
164166
}
165167

166168
function hasSameKeys(map1: ReadonlyCollection<string> | undefined, map2: ReadonlyCollection<string> | undefined): boolean {
@@ -1150,7 +1152,9 @@ namespace ts {
11501152
// Otherwise just affected file
11511153
Debug.checkDefined(state.program).emit(
11521154
affected === state.program ? undefined : affected as SourceFile,
1153-
writeFile || maybeBind(host, host.writeFile),
1155+
affected !== state.program && getEmitDeclarations(state.compilerOptions) && !customTransformers ?
1156+
getWriteFileUpdatingSignatureCallback(writeFile) :
1157+
writeFile || maybeBind(host, host.writeFile),
11541158
cancellationToken,
11551159
emitOnlyDtsFiles || emitKind === BuilderFileEmit.DtsOnly,
11561160
customTransformers
@@ -1161,6 +1165,34 @@ namespace ts {
11611165
);
11621166
}
11631167

1168+
function getWriteFileUpdatingSignatureCallback(writeFile: WriteFileCallback | undefined): WriteFileCallback {
1169+
return (fileName, text, writeByteOrderMark, onError, sourceFiles, data) => {
1170+
if (isDeclarationFileName(fileName)) {
1171+
Debug.assert(sourceFiles?.length === 1);
1172+
const file = sourceFiles[0];
1173+
const info = state.fileInfos.get(file.resolvedPath)!;
1174+
const signature = state.currentAffectedFilesSignatures?.get(file.resolvedPath) || info.signature;
1175+
if (signature === file.version) {
1176+
const newSignature = (computeHash || generateDjb2Hash)(data?.sourceMapUrlPos !== undefined ? text.substring(0, data.sourceMapUrlPos) : text);
1177+
if (newSignature !== file.version) { // Update it
1178+
if (host.storeFilesChangingSignatureDuringEmit) (state.filesChangingSignature ||= new Set()).add(file.resolvedPath);
1179+
if (state.exportedModulesMap) BuilderState.updateExportedModules(file, file.exportedModulesFromDeclarationEmit, state.currentAffectedFilesExportedModulesMap ||= BuilderState.createManyToManyPathMap());
1180+
if (state.affectedFiles && state.affectedFilesIndex! < state.affectedFiles.length) {
1181+
state.currentAffectedFilesSignatures!.set(file.resolvedPath, newSignature);
1182+
}
1183+
else {
1184+
info.signature = newSignature;
1185+
if (state.exportedModulesMap) BuilderState.updateExportedFilesMapFromCache(state, state.currentAffectedFilesExportedModulesMap);
1186+
}
1187+
}
1188+
}
1189+
}
1190+
if (writeFile) writeFile(fileName, text, writeByteOrderMark, onError, sourceFiles, data);
1191+
else if (host.writeFile) host.writeFile(fileName, text, writeByteOrderMark, onError, sourceFiles, data);
1192+
else state.program!.writeFile(fileName, text, writeByteOrderMark, onError, sourceFiles, data);
1193+
};
1194+
}
1195+
11641196
/**
11651197
* Emits the JavaScript and declaration files.
11661198
* When targetSource file is specified, emits the files corresponding to that source file,
@@ -1215,7 +1247,15 @@ namespace ts {
12151247
}
12161248
}
12171249
}
1218-
return Debug.checkDefined(state.program).emit(targetSourceFile, writeFile || maybeBind(host, host.writeFile), cancellationToken, emitOnlyDtsFiles, customTransformers);
1250+
return Debug.checkDefined(state.program).emit(
1251+
targetSourceFile,
1252+
!outFile(state.compilerOptions) && getEmitDeclarations(state.compilerOptions) && !customTransformers ?
1253+
getWriteFileUpdatingSignatureCallback(writeFile) :
1254+
writeFile || maybeBind(host, host.writeFile),
1255+
cancellationToken,
1256+
emitOnlyDtsFiles,
1257+
customTransformers
1258+
);
12191259
}
12201260

12211261
/**

src/compiler/builderPublic.ts

+5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ namespace ts {
2020
*/
2121
/*@internal*/
2222
disableUseFileVersionAsSignature?: boolean;
23+
/**
24+
* Store the list of files that update signature during the emit
25+
*/
26+
/*@internal*/
27+
storeFilesChangingSignatureDuringEmit?: boolean;
2328
}
2429

2530
/**

src/compiler/builderState.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ namespace ts {
450450
/**
451451
* Coverts the declaration emit result into exported modules map
452452
*/
453-
function updateExportedModules(sourceFile: SourceFile, exportedModulesFromDeclarationEmit: ExportedModulesFromDeclarationEmit | undefined, exportedModulesMapCache: ManyToManyPathMap) {
453+
export function updateExportedModules(sourceFile: SourceFile, exportedModulesFromDeclarationEmit: ExportedModulesFromDeclarationEmit | undefined, exportedModulesMapCache: ManyToManyPathMap) {
454454
if (!exportedModulesFromDeclarationEmit) {
455455
exportedModulesMapCache.deleteKey(sourceFile.resolvedPath);
456456
return;

src/compiler/emitter.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,7 @@ namespace ts {
531531
printer.writeFile(sourceFile!, writer, sourceMapGenerator);
532532
}
533533

534+
let sourceMapUrlPos;
534535
if (sourceMapGenerator) {
535536
if (sourceMapDataList) {
536537
sourceMapDataList.push({
@@ -548,6 +549,7 @@ namespace ts {
548549

549550
if (sourceMappingURL) {
550551
if (!writer.isAtStartOfLine()) writer.rawWrite(newLine);
552+
sourceMapUrlPos = writer.getTextPos();
551553
writer.writeComment(`//# ${"sourceMappingURL"}=${sourceMappingURL}`); // Tools can sometimes see this line as a source mapping url comment
552554
}
553555

@@ -562,7 +564,7 @@ namespace ts {
562564
}
563565

564566
// Write the output file
565-
writeFile(host, emitterDiagnostics, jsFilePath, writer.getText(), !!compilerOptions.emitBOM, sourceFiles);
567+
writeFile(host, emitterDiagnostics, jsFilePath, writer.getText(), !!compilerOptions.emitBOM, sourceFiles, { sourceMapUrlPos });
566568

567569
// Reset state
568570
writer.clear();

src/compiler/program.ts

+15-4
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ namespace ts {
262262
return newValue;
263263
};
264264
if (originalWriteFile) {
265-
host.writeFile = (fileName, data, writeByteOrderMark, onError, sourceFiles) => {
265+
host.writeFile = (fileName, data, ...rest) => {
266266
const key = toPath(fileName);
267267
fileExistsCache.delete(key);
268268

@@ -277,7 +277,7 @@ namespace ts {
277277
sourceFileCache.delete(key);
278278
}
279279
}
280-
originalWriteFile.call(host, fileName, data, writeByteOrderMark, onError, sourceFiles);
280+
originalWriteFile.call(host, fileName, data, ...rest);
281281
};
282282
}
283283

@@ -1340,6 +1340,7 @@ namespace ts {
13401340
useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(),
13411341
getFileIncludeReasons: () => fileReasons,
13421342
structureIsReused,
1343+
writeFile,
13431344
};
13441345

13451346
onProgramCreateComplete();
@@ -1894,8 +1895,7 @@ namespace ts {
18941895
getProjectReferenceRedirect,
18951896
isSourceOfProjectReferenceRedirect,
18961897
getSymlinkCache,
1897-
writeFile: writeFileCallback || (
1898-
(fileName, data, writeByteOrderMark, onError, sourceFiles) => host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles)),
1898+
writeFile: writeFileCallback || writeFile,
18991899
isEmitBlocked,
19001900
readFile: f => host.readFile(f),
19011901
fileExists: f => {
@@ -1914,6 +1914,17 @@ namespace ts {
19141914
};
19151915
}
19161916

1917+
function writeFile(
1918+
fileName: string,
1919+
text: string,
1920+
writeByteOrderMark: boolean,
1921+
onError?: (message: string) => void,
1922+
sourceFiles?: readonly SourceFile[],
1923+
data?: WriteFileCallbackData
1924+
) {
1925+
host.writeFile(fileName, text, writeByteOrderMark, onError, sourceFiles, data);
1926+
}
1927+
19171928
function emitBuildInfo(writeFileCallback?: WriteFileCallback): EmitResult {
19181929
Debug.assert(!outFile(options));
19191930
tracing?.push(tracing.Phase.Emit, "emitBuildInfo", {}, /*separateBeginAndEnd*/ true);

src/compiler/sys.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -1210,11 +1210,13 @@ namespace ts {
12101210
base64decode?(input: string): string;
12111211
base64encode?(input: string): string;
12121212
/*@internal*/ bufferFrom?(input: string, encoding?: string): Buffer;
1213+
/*@internal*/ require?(baseDir: string, moduleName: string): RequireResult;
1214+
/*@internal*/ defaultWatchFileKind?(): WatchFileKind | undefined;
1215+
12131216
// For testing
12141217
/*@internal*/ now?(): Date;
12151218
/*@internal*/ disableUseFileVersionAsSignature?: boolean;
1216-
/*@internal*/ require?(baseDir: string, moduleName: string): RequireResult;
1217-
/*@internal*/ defaultWatchFileKind?(): WatchFileKind | undefined;
1219+
/*@internal*/ storeFilesChangingSignatureDuringEmit?: boolean;
12181220
}
12191221

12201222
export interface FileWatcher {

src/compiler/types.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -3856,12 +3856,16 @@ namespace ts {
38563856
*/
38573857
export type ResolvedConfigFileName = string & { _isResolvedConfigFileName: never };
38583858

3859+
export interface WriteFileCallbackData {
3860+
/*@internal*/ sourceMapUrlPos?: number;
3861+
}
38593862
export type WriteFileCallback = (
38603863
fileName: string,
3861-
data: string,
3864+
text: string,
38623865
writeByteOrderMark: boolean,
38633866
onError?: (message: string) => void,
38643867
sourceFiles?: readonly SourceFile[],
3868+
data?: WriteFileCallbackData,
38653869
) => void;
38663870

38673871
export class OperationCanceledException { }
@@ -4067,6 +4071,8 @@ namespace ts {
40674071
* This implementation handles file exists to be true if file is source of project reference redirect when program is created using useSourceOfProjectReferenceRedirect
40684072
*/
40694073
/*@internal*/ fileExists(fileName: string): boolean;
4074+
/** Call compilerHost.writeFile on host program was created with */
4075+
/*@internal*/ writeFile: WriteFileCallback;
40704076
}
40714077

40724078
/*@internal*/
@@ -6782,6 +6788,7 @@ namespace ts {
67826788

67836789
// For testing:
67846790
/*@internal*/ disableUseFileVersionAsSignature?: boolean;
6791+
/*@internal*/ storeFilesChangingSignatureDuringEmit?: boolean;
67856792
}
67866793

67876794
/** true if --out otherwise source file name */

src/compiler/utilities.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -4459,10 +4459,10 @@ namespace ts {
44594459
return combinePaths(newDirPath, sourceFilePath);
44604460
}
44614461

4462-
export function writeFile(host: { writeFile: WriteFileCallback; }, diagnostics: DiagnosticCollection, fileName: string, data: string, writeByteOrderMark: boolean, sourceFiles?: readonly SourceFile[]) {
4463-
host.writeFile(fileName, data, writeByteOrderMark, hostErrorMessage => {
4462+
export function writeFile(host: { writeFile: WriteFileCallback; }, diagnostics: DiagnosticCollection, fileName: string, text: string, writeByteOrderMark: boolean, sourceFiles?: readonly SourceFile[], data?: WriteFileCallbackData) {
4463+
host.writeFile(fileName, text, writeByteOrderMark, hostErrorMessage => {
44644464
diagnostics.add(createCompilerDiagnostic(Diagnostics.Could_not_write_file_0_Colon_1, fileName, hostErrorMessage));
4465-
}, sourceFiles);
4465+
}, sourceFiles, data);
44664466
}
44674467

44684468
function ensureDirectoriesExist(

src/compiler/watch.ts

+2
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,7 @@ namespace ts {
573573
createHash: maybeBind(host, host.createHash),
574574
readDirectory: maybeBind(host, host.readDirectory),
575575
disableUseFileVersionAsSignature: host.disableUseFileVersionAsSignature,
576+
storeFilesChangingSignatureDuringEmit: host.storeFilesChangingSignatureDuringEmit,
576577
};
577578

578579
function writeFile(fileName: string, text: string, writeByteOrderMark: boolean, onError: (message: string) => void) {
@@ -637,6 +638,7 @@ namespace ts {
637638
createHash: maybeBind(system, system.createHash),
638639
createProgram: createProgram || createEmitAndSemanticDiagnosticsBuilderProgram as any as CreateProgram<T>,
639640
disableUseFileVersionAsSignature: system.disableUseFileVersionAsSignature,
641+
storeFilesChangingSignatureDuringEmit: system.storeFilesChangingSignatureDuringEmit,
640642
};
641643
}
642644

src/compiler/watchPublic.ts

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ namespace ts {
2020
const host = createCompilerHostWorker(options, /*setParentNodes*/ undefined, system);
2121
host.createHash = maybeBind(system, system.createHash);
2222
host.disableUseFileVersionAsSignature = system.disableUseFileVersionAsSignature;
23+
host.storeFilesChangingSignatureDuringEmit = system.storeFilesChangingSignatureDuringEmit;
2324
setGetSourceFileAsHashVersioned(host, system);
2425
changeCompilerHostLikeToUseCache(host, fileName => toPath(fileName, host.getCurrentDirectory(), host.getCanonicalFileName));
2526
return host;
@@ -114,6 +115,7 @@ namespace ts {
114115
writeFile?(path: string, data: string, writeByteOrderMark?: boolean): void;
115116
// For testing
116117
disableUseFileVersionAsSignature?: boolean;
118+
storeFilesChangingSignatureDuringEmit?: boolean;
117119
}
118120

119121
export interface WatchCompilerHost<T extends BuilderProgram> extends ProgramHost<T>, WatchHost {

src/harness/virtualFileSystemWithWatch.ts

+1
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,7 @@ interface Array<T> { length: number; [n: number]: T; }`
396396
private readonly currentDirectory: string;
397397
public require: ((initialPath: string, moduleName: string) => RequireResult) | undefined;
398398
public defaultWatchFileKind?: () => WatchFileKind | undefined;
399+
public storeFilesChangingSignatureDuringEmit = true;
399400
watchFile: HostWatchFile;
400401
watchDirectory: HostWatchDirectory;
401402
constructor(

src/testRunner/unittests/tsbuild/outputPaths.ts

+1-10
Original file line numberDiff line numberDiff line change
@@ -55,16 +55,7 @@ namespace ts {
5555
}
5656
})
5757
}),
58-
edits: [
59-
noChangeRun,
60-
{
61-
...noChangeProject,
62-
cleanBuildDiscrepancies: () => new Map([
63-
["/src/dist/tsconfig.tsbuildinfo", CleanBuildDescrepancy.CleanFileTextDifferent], // tsbuildinfo will have -p setting when built using -p vs no build happens incrementally because of no change.
64-
["/src/dist/tsconfig.tsbuildinfo.readable.baseline.txt", CleanBuildDescrepancy.CleanFileTextDifferent] // tsbuildinfo will have -p setting when built using -p vs no build happens incrementally because of no change.
65-
]),
66-
}
67-
],
58+
edits,
6859
}, ["/src/dist/src/index.js", "/src/dist/src/index.d.ts"]);
6960

7061
verify({

src/testRunner/unittests/tsc/helpers.ts

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ namespace ts {
33
writtenFiles: Set<Path>;
44
baseLine(): { file: string; text: string; };
55
disableUseFileVersionAsSignature?: boolean;
6+
storeFilesChangingSignatureDuringEmit?: boolean;
67
};
78

89
export const noChangeRun: TestTscEdit = {
@@ -84,6 +85,7 @@ namespace ts {
8485
// Create system
8586
const sys = new fakes.System(fs, { executingFilePath: "/lib/tsc", env: environmentVariables }) as TscCompileSystem;
8687
if (input.disableUseFileVersionAsSignature) sys.disableUseFileVersionAsSignature = true;
88+
sys.storeFilesChangingSignatureDuringEmit = true;
8789
sys.write(`${sys.getExecutingFilePath()} ${commandLineArgs.join(" ")}\n`);
8890
sys.exit = exitCode => sys.exitCode = exitCode;
8991
worker(sys);

src/testRunner/unittests/tscWatch/helpers.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ namespace ts.tscWatch {
336336
if (!builderProgram) return;
337337
if (builderProgram !== oldProgram?.[1]) {
338338
const state = builderProgram.getState();
339-
const internalState = state as unknown as BuilderState;
339+
const internalState = state as unknown as BuilderProgramState;
340340
if (state.semanticDiagnosticsPerFile?.size) {
341341
baseline.push("Semantic diagnostics in builder refreshed for::");
342342
for (const file of program.getSourceFiles()) {
@@ -354,9 +354,12 @@ namespace ts.tscWatch {
354354
baseline.push("Shape signatures in builder refreshed for::");
355355
internalState.hasCalledUpdateShapeSignature.forEach((path: Path) => {
356356
const info = state.fileInfos.get(path);
357-
if(info?.version === info?.signature || !info?.signature) {
357+
if (info?.version === info?.signature || !info?.signature) {
358358
baseline.push(path + " (used version)");
359359
}
360+
else if (internalState.filesChangingSignature?.has(path)) {
361+
baseline.push(path + " (computed .d.ts during emit)");
362+
}
360363
else {
361364
baseline.push(path + " (computed .d.ts)");
362365
}

tests/baselines/reference/api/tsserverlibrary.d.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -2172,7 +2172,9 @@ declare namespace ts {
21722172
export type ResolvedConfigFileName = string & {
21732173
_isResolvedConfigFileName: never;
21742174
};
2175-
export type WriteFileCallback = (fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void, sourceFiles?: readonly SourceFile[]) => void;
2175+
export interface WriteFileCallbackData {
2176+
}
2177+
export type WriteFileCallback = (fileName: string, text: string, writeByteOrderMark: boolean, onError?: (message: string) => void, sourceFiles?: readonly SourceFile[], data?: WriteFileCallbackData) => void;
21762178
export class OperationCanceledException {
21772179
}
21782180
export interface CancellationToken {

tests/baselines/reference/api/typescript.d.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -2172,7 +2172,9 @@ declare namespace ts {
21722172
export type ResolvedConfigFileName = string & {
21732173
_isResolvedConfigFileName: never;
21742174
};
2175-
export type WriteFileCallback = (fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void, sourceFiles?: readonly SourceFile[]) => void;
2175+
export interface WriteFileCallbackData {
2176+
}
2177+
export type WriteFileCallback = (fileName: string, text: string, writeByteOrderMark: boolean, onError?: (message: string) => void, sourceFiles?: readonly SourceFile[], data?: WriteFileCallbackData) => void;
21762178
export class OperationCanceledException {
21772179
}
21782180
export interface CancellationToken {

0 commit comments

Comments
 (0)