Skip to content

Commit 8d5d900

Browse files
committed
Factor out caching logic so tsc (without watch can use it and --watch has its own cache logic).
1 parent c7f8959 commit 8d5d900

File tree

3 files changed

+117
-88
lines changed

3 files changed

+117
-88
lines changed

src/compiler/program.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,114 @@ namespace ts {
203203
return compilerHost;
204204
}
205205

206+
/*@internal*/
207+
export function changeCompilerHostToUseCache(
208+
host: CompilerHost,
209+
toPath: (fileName: string) => Path,
210+
useCacheForSourceFile: boolean
211+
) {
212+
const originalReadFile = host.readFile;
213+
const originalFileExists = host.fileExists;
214+
const originalDirectoryExists = host.directoryExists;
215+
const originalCreateDirectory = host.createDirectory;
216+
const originalWriteFile = host.writeFile;
217+
const originalGetSourceFile = host.getSourceFile;
218+
const readFileCache = createMap<string | false>();
219+
const fileExistsCache = createMap<boolean>();
220+
const directoryExistsCache = createMap<boolean>();
221+
const sourceFileCache = createMap<SourceFile>();
222+
223+
const readFileWithCache = (fileName: string): string | undefined => {
224+
const key = toPath(fileName);
225+
const value = readFileCache.get(key);
226+
if (value !== undefined) return value || undefined;
227+
return setReadFileCache(key, fileName);
228+
};
229+
const setReadFileCache = (key: Path, fileName: string) => {
230+
const newValue = originalReadFile.call(host, fileName);
231+
readFileCache.set(key, newValue || false);
232+
return newValue;
233+
};
234+
host.readFile = fileName => {
235+
const key = toPath(fileName);
236+
const value = readFileCache.get(key);
237+
if (value !== undefined) return value; // could be .d.ts from output
238+
if (!fileExtensionIs(fileName, Extension.Json)) {
239+
return originalReadFile.call(host, fileName);
240+
}
241+
242+
return setReadFileCache(key, fileName);
243+
};
244+
245+
if (useCacheForSourceFile) {
246+
host.getSourceFile = (fileName, languageVersion, onError, shouldCreateNewSourceFile) => {
247+
const key = toPath(fileName);
248+
const value = sourceFileCache.get(key);
249+
if (value) return value;
250+
251+
const sourceFile = originalGetSourceFile.call(host, fileName, languageVersion, onError, shouldCreateNewSourceFile);
252+
if (sourceFile && (isDeclarationFileName(fileName) || fileExtensionIs(fileName, Extension.Json))) {
253+
sourceFileCache.set(key, sourceFile);
254+
}
255+
return sourceFile;
256+
};
257+
}
258+
259+
// fileExists for any kind of extension
260+
host.fileExists = fileName => {
261+
const key = toPath(fileName);
262+
const value = fileExistsCache.get(key);
263+
if (value !== undefined) return value;
264+
const newValue = originalFileExists.call(host, fileName);
265+
fileExistsCache.set(key, !!newValue);
266+
return newValue;
267+
};
268+
host.writeFile = (fileName, data, writeByteOrderMark, onError, sourceFiles) => {
269+
const key = toPath(fileName);
270+
fileExistsCache.delete(key);
271+
272+
const value = readFileCache.get(key);
273+
if (value && value !== data) {
274+
readFileCache.delete(key);
275+
sourceFileCache.delete(key);
276+
}
277+
else if (useCacheForSourceFile) {
278+
const sourceFile = sourceFileCache.get(key);
279+
if (sourceFile && sourceFile.text !== data) {
280+
sourceFileCache.delete(key);
281+
}
282+
}
283+
originalWriteFile.call(host, fileName, data, writeByteOrderMark, onError, sourceFiles);
284+
};
285+
286+
// directoryExists
287+
if (originalDirectoryExists && originalCreateDirectory) {
288+
host.directoryExists = directory => {
289+
const key = toPath(directory);
290+
const value = directoryExistsCache.get(key);
291+
if (value !== undefined) return value;
292+
const newValue = originalDirectoryExists.call(host, directory);
293+
directoryExistsCache.set(key, !!newValue);
294+
return newValue;
295+
};
296+
host.createDirectory = directory => {
297+
const key = toPath(directory);
298+
directoryExistsCache.delete(key);
299+
originalCreateDirectory.call(host, directory);
300+
};
301+
}
302+
303+
return {
304+
originalReadFile,
305+
originalFileExists,
306+
originalDirectoryExists,
307+
originalCreateDirectory,
308+
originalWriteFile,
309+
originalGetSourceFile,
310+
readFileWithCache
311+
};
312+
}
313+
206314
export function getPreEmitDiagnostics(program: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic> {
207315
const diagnostics = [
208316
...program.getConfigFileParsingDiagnostics(),

src/compiler/tsbuild.ts

Lines changed: 6 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,96 +1183,14 @@ namespace ts {
11831183

11841184
function buildAllProjects(): ExitStatus {
11851185
if (options.watch) { reportWatchStatus(Diagnostics.Starting_compilation_in_watch_mode); }
1186-
const originalReadFile = host.readFile;
1187-
const originalFileExists = host.fileExists;
1188-
const originalDirectoryExists = host.directoryExists;
1189-
const originalCreateDirectory = host.createDirectory;
1190-
const originalWriteFile = host.writeFile;
1191-
const originalGetSourceFile = host.getSourceFile;
1192-
const readFileCache = createMap<string | false>();
1193-
const fileExistsCache = createMap<boolean>();
1194-
const directoryExistsCache = createMap<boolean>();
1195-
const sourceFileCache = createMap<SourceFile>();
1196-
const savedReadFileWithCache = readFileWithCache;
1197-
11981186
// TODO:: In watch mode as well to use caches for incremental build once we can invalidate caches correctly and have right api
11991187
// Override readFile for json files and output .d.ts to cache the text
1200-
readFileWithCache = fileName => {
1201-
const key = toPath(fileName);
1202-
const value = readFileCache.get(key);
1203-
if (value !== undefined) return value || undefined;
1204-
return setReadFileCache(key, fileName);
1205-
};
1206-
const setReadFileCache = (key: Path, fileName: string) => {
1207-
const newValue = originalReadFile.call(host, fileName);
1208-
readFileCache.set(key, newValue || false);
1209-
return newValue;
1210-
};
1211-
host.readFile = fileName => {
1212-
const key = toPath(fileName);
1213-
const value = readFileCache.get(key);
1214-
if (value !== undefined) return value; // could be .d.ts from output
1215-
if (!fileExtensionIs(fileName, Extension.Json)) {
1216-
return originalReadFile.call(host, fileName);
1217-
}
1218-
1219-
return setReadFileCache(key, fileName);
1220-
};
1221-
host.getSourceFile = (fileName, languageVersion, onError, shouldCreateNewSourceFile) => {
1222-
const key = toPath(fileName);
1223-
const value = sourceFileCache.get(key);
1224-
if (value) return value;
1225-
1226-
const sourceFile = originalGetSourceFile.call(host, fileName, languageVersion, onError, shouldCreateNewSourceFile);
1227-
if (sourceFile && (isDeclarationFileName(fileName) || fileExtensionIs(fileName, Extension.Json))) {
1228-
sourceFileCache.set(key, sourceFile);
1229-
}
1230-
return sourceFile;
1231-
};
1232-
1233-
// fileExists for any kind of extension
1234-
host.fileExists = fileName => {
1235-
const key = toPath(fileName);
1236-
const value = fileExistsCache.get(key);
1237-
if (value !== undefined) return value;
1238-
const newValue = originalFileExists.call(host, fileName);
1239-
fileExistsCache.set(key, !!newValue);
1240-
return newValue;
1241-
};
1242-
host.writeFile = (fileName, data, writeByteOrderMark, onError, sourceFiles) => {
1243-
const key = toPath(fileName);
1244-
fileExistsCache.delete(key);
1245-
1246-
const value = readFileCache.get(key);
1247-
if (value && value !== data) {
1248-
readFileCache.delete(key);
1249-
sourceFileCache.delete(key);
1250-
}
1251-
else {
1252-
const sourceFile = sourceFileCache.get(key);
1253-
if (sourceFile && sourceFile.text !== data) {
1254-
sourceFileCache.delete(key);
1255-
}
1256-
}
1257-
originalWriteFile.call(host, fileName, data, writeByteOrderMark, onError, sourceFiles);
1258-
};
1259-
1260-
// directoryExists
1261-
if (originalDirectoryExists && originalCreateDirectory) {
1262-
host.directoryExists = directory => {
1263-
const key = toPath(directory);
1264-
const value = directoryExistsCache.get(key);
1265-
if (value !== undefined) return value;
1266-
const newValue = originalDirectoryExists.call(host, directory);
1267-
directoryExistsCache.set(key, !!newValue);
1268-
return newValue;
1269-
};
1270-
host.createDirectory = directory => {
1271-
const key = toPath(directory);
1272-
directoryExistsCache.delete(key);
1273-
originalCreateDirectory.call(host, directory);
1274-
};
1275-
}
1188+
const { originalReadFile, originalFileExists, originalDirectoryExists,
1189+
originalCreateDirectory, originalWriteFile, originalGetSourceFile,
1190+
readFileWithCache: newReadFileWithCache
1191+
} = changeCompilerHostToUseCache(host, toPath, /*useCacheForSourceFile*/ true);
1192+
const savedReadFileWithCache = readFileWithCache;
1193+
readFileWithCache = newReadFileWithCache;
12761194

12771195
const graph = getGlobalDependencyGraph();
12781196
reportBuildQueue(graph);

src/tsc/tsc.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,9 @@ namespace ts {
227227

228228
function performCompilation(rootNames: string[], projectReferences: ReadonlyArray<ProjectReference> | undefined, options: CompilerOptions, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) {
229229
const host = createCompilerHost(options);
230+
const currentDirectory = host.getCurrentDirectory();
231+
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames());
232+
changeCompilerHostToUseCache(host, fileName => toPath(fileName, currentDirectory, getCanonicalFileName), /*useCacheForSourceFile*/ false);
230233
enableStatistics(options);
231234

232235
const programOptions: CreateProgramOptions = {

0 commit comments

Comments
 (0)