Skip to content

Commit fb20c38

Browse files
committed
If we are writing dts file and have used file text as version, we can update the signature when doing actual emit
1 parent a92aaed commit fb20c38

File tree

182 files changed

+2284
-2765
lines changed

Some content is hidden

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

182 files changed

+2284
-2765
lines changed

src/compiler/builder.ts

+38-1
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 {
@@ -1083,7 +1085,9 @@ namespace ts {
10831085
// Otherwise just affected file
10841086
Debug.checkDefined(state.program).emit(
10851087
affected === state.program ? undefined : affected as SourceFile,
1086-
writeFile || maybeBind(host, host.writeFile),
1088+
affected !== state.program && getEmitDeclarations(state.compilerOptions) && !customTransformers ?
1089+
writeFileUpdatingSignature :
1090+
writeFile || maybeBind(host, host.writeFile),
10871091
cancellationToken,
10881092
emitOnlyDtsFiles || emitKind === BuilderFileEmit.DtsOnly,
10891093
customTransformers
@@ -1092,6 +1096,39 @@ namespace ts {
10921096
emitKind,
10931097
isPendingEmitFile,
10941098
);
1099+
1100+
function writeFileUpdatingSignature(
1101+
fileName: string,
1102+
data: string,
1103+
writeByteOrderMark: boolean,
1104+
onError?: (message: string) => void,
1105+
sourceFiles?: readonly SourceFile[],
1106+
sourceMapUrlPos?: number
1107+
) {
1108+
Debug.assert(sourceFiles?.length === 1);
1109+
if (isDeclarationFileName(fileName)) {
1110+
const file = sourceFiles[0];
1111+
const info = state.fileInfos.get(file.resolvedPath)!;
1112+
const signature = state.currentAffectedFilesSignatures?.get(file.resolvedPath) || info.signature;
1113+
if (signature === file.version) {
1114+
const newSignature = (computeHash || generateDjb2Hash)(sourceMapUrlPos !== undefined ? data.substring(0, sourceMapUrlPos) : data);
1115+
if (newSignature !== file.version) { // Update it
1116+
if (host.storeFilesChangingSignatureDuringEmit) (state.filesChangingSignature ||= new Set()).add(file.resolvedPath);
1117+
if (state.exportedModulesMap) BuilderState.updateExportedModules(file, file.exportedModulesFromDeclarationEmit, state.currentAffectedFilesExportedModulesMap ||= BuilderState.createManyToManyPathMap());
1118+
if (state.affectedFiles && state.affectedFilesIndex! < state.affectedFiles.length) {
1119+
state.currentAffectedFilesSignatures!.set(file.resolvedPath, newSignature);
1120+
}
1121+
else {
1122+
info.signature = newSignature;
1123+
if (state.exportedModulesMap) BuilderState.updateExportedFilesMapFromCache(state, state.currentAffectedFilesExportedModulesMap);
1124+
}
1125+
}
1126+
}
1127+
}
1128+
if (writeFile) writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles, sourceMapUrlPos);
1129+
else if (host.writeFile) host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles, sourceMapUrlPos);
1130+
else state.program!.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles, sourceMapUrlPos);
1131+
}
10951132
}
10961133

10971134
/**

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ namespace ts {
434434
);
435435
const firstDts = firstOrUndefined(emitOutput.outputFiles);
436436
if (firstDts) {
437-
Debug.assert(fileExtensionIsOneOf(firstDts.name, [Extension.Dts, Extension.Dmts, Extension.Dcts]), "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))}`);
437+
Debug.assert(isDeclarationFileName(firstDts.name), "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))}`);
438438
latestSignature = (computeHash || generateDjb2Hash)(firstDts.text);
439439
if (exportedModulesMapCache && latestSignature !== prevSignature) {
440440
updateExportedModules(sourceFile, emitOutput.exportedModulesFromDeclarationEmit, exportedModulesMapCache);
@@ -462,7 +462,7 @@ namespace ts {
462462
/**
463463
* Coverts the declaration emit result into exported modules map
464464
*/
465-
function updateExportedModules(sourceFile: SourceFile, exportedModulesFromDeclarationEmit: ExportedModulesFromDeclarationEmit | undefined, exportedModulesMapCache: ManyToManyPathMap) {
465+
export function updateExportedModules(sourceFile: SourceFile, exportedModulesFromDeclarationEmit: ExportedModulesFromDeclarationEmit | undefined, exportedModulesMapCache: ManyToManyPathMap) {
466466
if (!exportedModulesFromDeclarationEmit) {
467467
exportedModulesMapCache.deleteKey(sourceFile.resolvedPath);
468468
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/moduleNameResolver.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1498,7 +1498,7 @@ namespace ts {
14981498
}
14991499

15001500
function loadJSOrExactTSFileName(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined {
1501-
if ((extensions === Extensions.TypeScript || extensions === Extensions.DtsOnly) && fileExtensionIsOneOf(candidate, [Extension.Dts, Extension.Dcts, Extension.Dmts])) {
1501+
if ((extensions === Extensions.TypeScript || extensions === Extensions.DtsOnly) && isDeclarationFileName(candidate)) {
15021502
const result = tryFile(candidate, onlyRecordFailures, state);
15031503
return result !== undefined ? { path: candidate, ext: forEach([Extension.Dts, Extension.Dcts, Extension.Dmts], e => fileExtensionIs(candidate, e) ? e : undefined)! } : undefined;
15041504
}

src/compiler/parser.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -9411,9 +9411,10 @@ namespace ts {
94119411
}
94129412
}
94139413

9414+
const dtsExtensions = [Extension.Dts, Extension.Dmts, Extension.Dcts];
94149415
/** @internal */
94159416
export function isDeclarationFileName(fileName: string): boolean {
9416-
return fileExtensionIsOneOf(fileName, [Extension.Dts, Extension.Dmts, Extension.Dcts]);
9417+
return fileExtensionIsOneOf(fileName, dtsExtensions);
94179418
}
94189419

94199420
/*@internal*/

src/compiler/program.ts

+16-5
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();
@@ -1393,7 +1394,7 @@ namespace ts {
13931394

13941395
function getRedirectReferenceForResolution(file: SourceFile) {
13951396
const redirect = getResolvedProjectReferenceToRedirect(file.originalFileName);
1396-
if (redirect || !fileExtensionIsOneOf(file.originalFileName, [Extension.Dts, Extension.Dcts, Extension.Dmts])) return redirect;
1397+
if (redirect || !isDeclarationFileName(file.originalFileName)) return redirect;
13971398

13981399
// The originalFileName could not be actual source file name if file found was d.ts from referecned project
13991400
// So in this case try to look up if this is output from referenced project, if it is use the redirected project in that case
@@ -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+
data: string,
1920+
writeByteOrderMark: boolean,
1921+
onError?: (message: string) => void,
1922+
sourceFiles?: readonly SourceFile[],
1923+
sourceMapUrlPos?: number,
1924+
) {
1925+
host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles, sourceMapUrlPos);
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

+4
Original file line numberDiff line numberDiff line change
@@ -3862,6 +3862,7 @@ namespace ts {
38623862
writeByteOrderMark: boolean,
38633863
onError?: (message: string) => void,
38643864
sourceFiles?: readonly SourceFile[],
3865+
sourceMapUrlPos?: number,
38653866
) => void;
38663867

38673868
export class OperationCanceledException { }
@@ -4067,6 +4068,8 @@ namespace ts {
40674068
* This implementation handles file exists to be true if file is source of project reference redirect when program is created using useSourceOfProjectReferenceRedirect
40684069
*/
40694070
/*@internal*/ fileExists(fileName: string): boolean;
4071+
/** Call compilerHost.writeFile on host program was created with */
4072+
/*@internal*/ writeFile: WriteFileCallback;
40704073
}
40714074

40724075
/*@internal*/
@@ -6779,6 +6782,7 @@ namespace ts {
67796782

67806783
// For testing:
67816784
/*@internal*/ disableUseFileVersionAsSignature?: boolean;
6785+
/*@internal*/ storeFilesChangingSignatureDuringEmit?: boolean;
67826786
}
67836787

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

src/compiler/utilities.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -4446,10 +4446,10 @@ namespace ts {
44464446
return combinePaths(newDirPath, sourceFilePath);
44474447
}
44484448

4449-
export function writeFile(host: { writeFile: WriteFileCallback; }, diagnostics: DiagnosticCollection, fileName: string, data: string, writeByteOrderMark: boolean, sourceFiles?: readonly SourceFile[]) {
4449+
export function writeFile(host: { writeFile: WriteFileCallback; }, diagnostics: DiagnosticCollection, fileName: string, data: string, writeByteOrderMark: boolean, sourceFiles?: readonly SourceFile[], sourceMapUrlPos?: number) {
44504450
host.writeFile(fileName, data, writeByteOrderMark, hostErrorMessage => {
44514451
diagnostics.add(createCompilerDiagnostic(Diagnostics.Could_not_write_file_0_Colon_1, fileName, hostErrorMessage));
4452-
}, sourceFiles);
4452+
}, sourceFiles, sourceMapUrlPos);
44534453
}
44544454

44554455
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/harness/vpathUtil.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ namespace vpath {
108108
}
109109

110110
export function isDeclaration(path: string) {
111-
return ts.fileExtensionIsOneOf(path, [ts.Extension.Dmts, ts.Extension.Dcts, ts.Extension.Dts]);
111+
return ts.isDeclarationFileName(path);
112112
}
113113

114114
export function isSourceMap(path: string) {

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -2172,7 +2172,7 @@ 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 type WriteFileCallback = (fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void, sourceFiles?: readonly SourceFile[], sourceMapUrlPos?: number) => void;
21762176
export class OperationCanceledException {
21772177
}
21782178
export interface CancellationToken {

0 commit comments

Comments
 (0)