Skip to content

Commit 9bd2365

Browse files
authored
Merge pull request #29161 from Microsoft/incrementalBuild
Supports incremental build in tsc --b --w mode
2 parents 31e8f02 + 900d6f7 commit 9bd2365

File tree

20 files changed

+1016
-513
lines changed

20 files changed

+1016
-513
lines changed

src/compiler/builder.ts

Lines changed: 218 additions & 63 deletions
Large diffs are not rendered by default.

src/compiler/builderState.ts

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,15 @@ namespace ts {
5050
/**
5151
* Cache of all files excluding default library file for the current program
5252
*/
53-
allFilesExcludingDefaultLibraryFile: ReadonlyArray<SourceFile> | undefined;
53+
allFilesExcludingDefaultLibraryFile?: ReadonlyArray<SourceFile>;
5454
/**
5555
* Cache of all the file names
5656
*/
57-
allFileNames: ReadonlyArray<string> | undefined;
57+
allFileNames?: ReadonlyArray<string>;
58+
}
59+
60+
export function cloneMapOrUndefined<T>(map: ReadonlyMap<T> | undefined) {
61+
return map ? cloneMap(map) : undefined;
5862
}
5963
}
6064

@@ -230,9 +234,32 @@ namespace ts.BuilderState {
230234
fileInfos,
231235
referencedMap,
232236
exportedModulesMap,
233-
hasCalledUpdateShapeSignature,
234-
allFilesExcludingDefaultLibraryFile: undefined,
235-
allFileNames: undefined
237+
hasCalledUpdateShapeSignature
238+
};
239+
}
240+
241+
/**
242+
* Releases needed properties
243+
*/
244+
export function releaseCache(state: BuilderState) {
245+
state.allFilesExcludingDefaultLibraryFile = undefined;
246+
state.allFileNames = undefined;
247+
}
248+
249+
/**
250+
* Creates a clone of the state
251+
*/
252+
export function clone(state: Readonly<BuilderState>): BuilderState {
253+
const fileInfos = createMap<FileInfo>();
254+
state.fileInfos.forEach((value, key) => {
255+
fileInfos.set(key, { ...value });
256+
});
257+
// Dont need to backup allFiles info since its cache anyway
258+
return {
259+
fileInfos,
260+
referencedMap: cloneMapOrUndefined(state.referencedMap),
261+
exportedModulesMap: cloneMapOrUndefined(state.exportedModulesMap),
262+
hasCalledUpdateShapeSignature: cloneMap(state.hasCalledUpdateShapeSignature),
236263
};
237264
}
238265

@@ -505,14 +532,14 @@ namespace ts.BuilderState {
505532

506533
// Start with the paths this file was referenced by
507534
seenFileNamesMap.set(sourceFileWithUpdatedShape.path, sourceFileWithUpdatedShape);
508-
const queue = getReferencedByPaths(state, sourceFileWithUpdatedShape.path);
535+
const queue = getReferencedByPaths(state, sourceFileWithUpdatedShape.resolvedPath);
509536
while (queue.length > 0) {
510537
const currentPath = queue.pop()!;
511538
if (!seenFileNamesMap.has(currentPath)) {
512539
const currentSourceFile = programOfThisState.getSourceFileByPath(currentPath)!;
513540
seenFileNamesMap.set(currentPath, currentSourceFile);
514541
if (currentSourceFile && updateShapeSignature(state, programOfThisState, currentSourceFile, cacheToUpdateSignature, cancellationToken, computeHash!, exportedModulesMapCache)) { // TODO: GH#18217
515-
queue.push(...getReferencedByPaths(state, currentPath));
542+
queue.push(...getReferencedByPaths(state, currentSourceFile.resolvedPath));
516543
}
517544
}
518545
}

src/compiler/core.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1387,6 +1387,18 @@ namespace ts {
13871387
return result;
13881388
}
13891389

1390+
export function copyProperties<T1 extends T2, T2>(first: T1, second: T2) {
1391+
for (const id in second) {
1392+
if (hasOwnProperty.call(second, id)) {
1393+
(first as any)[id] = second[id];
1394+
}
1395+
}
1396+
}
1397+
1398+
export function maybeBind<T, A extends any[], R>(obj: T, fn: ((this: T, ...args: A) => R) | undefined): ((...args: A) => R) | undefined {
1399+
return fn ? fn.bind(obj) : undefined;
1400+
}
1401+
13901402
export interface MultiMap<T> extends Map<T[]> {
13911403
/**
13921404
* Adds the value to an array of values associated with the key, and returns the array.

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3957,6 +3957,10 @@
39573957
"category": "Error",
39583958
"code": 6370
39593959
},
3960+
"Updating unchanged output timestamps of project '{0}'...": {
3961+
"category": "Message",
3962+
"code": 6371
3963+
},
39603964

39613965
"The expected type comes from property '{0}' which is declared here on type '{1}'": {
39623966
"category": "Message",

src/compiler/program.ts

Lines changed: 56 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ namespace ts {
6969
export function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost {
7070
return createCompilerHostWorker(options, setParentNodes);
7171
}
72+
7273
/*@internal*/
7374
// TODO(shkamat): update this after reworking ts build API
7475
export function createCompilerHostWorker(options: CompilerOptions, setParentNodes?: boolean, system = sys): CompilerHost {
@@ -93,7 +94,6 @@ namespace ts {
9394
}
9495
text = "";
9596
}
96-
9797
return text !== undefined ? createSourceFile(fileName, text, languageVersion, setParentNodes) : undefined;
9898
}
9999

@@ -203,18 +203,25 @@ namespace ts {
203203
return compilerHost;
204204
}
205205

206+
interface CompilerHostLikeForCache {
207+
fileExists(fileName: string): boolean;
208+
readFile(fileName: string, encoding?: string): string | undefined;
209+
directoryExists?(directory: string): boolean;
210+
createDirectory?(directory: string): void;
211+
writeFile?: WriteFileCallback;
212+
}
213+
206214
/*@internal*/
207-
export function changeCompilerHostToUseCache(
208-
host: CompilerHost,
215+
export function changeCompilerHostLikeToUseCache(
216+
host: CompilerHostLikeForCache,
209217
toPath: (fileName: string) => Path,
210-
useCacheForSourceFile: boolean
218+
getSourceFile?: CompilerHost["getSourceFile"]
211219
) {
212220
const originalReadFile = host.readFile;
213221
const originalFileExists = host.fileExists;
214222
const originalDirectoryExists = host.directoryExists;
215223
const originalCreateDirectory = host.createDirectory;
216224
const originalWriteFile = host.writeFile;
217-
const originalGetSourceFile = host.getSourceFile;
218225
const readFileCache = createMap<string | false>();
219226
const fileExistsCache = createMap<boolean>();
220227
const directoryExistsCache = createMap<boolean>();
@@ -242,19 +249,17 @@ namespace ts {
242249
return setReadFileCache(key, fileName);
243250
};
244251

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;
252+
const getSourceFileWithCache: CompilerHost["getSourceFile"] | undefined = getSourceFile ? (fileName, languageVersion, onError, shouldCreateNewSourceFile) => {
253+
const key = toPath(fileName);
254+
const value = sourceFileCache.get(key);
255+
if (value) return value;
250256

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-
}
257+
const sourceFile = getSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile);
258+
if (sourceFile && (isDeclarationFileName(fileName) || fileExtensionIs(fileName, Extension.Json))) {
259+
sourceFileCache.set(key, sourceFile);
260+
}
261+
return sourceFile;
262+
} : undefined;
258263

259264
// fileExists for any kind of extension
260265
host.fileExists = fileName => {
@@ -265,23 +270,25 @@ namespace ts {
265270
fileExistsCache.set(key, !!newValue);
266271
return newValue;
267272
};
268-
host.writeFile = (fileName, data, writeByteOrderMark, onError, sourceFiles) => {
269-
const key = toPath(fileName);
270-
fileExistsCache.delete(key);
273+
if (originalWriteFile) {
274+
host.writeFile = (fileName, data, writeByteOrderMark, onError, sourceFiles) => {
275+
const key = toPath(fileName);
276+
fileExistsCache.delete(key);
271277

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) {
278+
const value = readFileCache.get(key);
279+
if (value && value !== data) {
280+
readFileCache.delete(key);
280281
sourceFileCache.delete(key);
281282
}
282-
}
283-
originalWriteFile.call(host, fileName, data, writeByteOrderMark, onError, sourceFiles);
284-
};
283+
else if (getSourceFileWithCache) {
284+
const sourceFile = sourceFileCache.get(key);
285+
if (sourceFile && sourceFile.text !== data) {
286+
sourceFileCache.delete(key);
287+
}
288+
}
289+
originalWriteFile.call(host, fileName, data, writeByteOrderMark, onError, sourceFiles);
290+
};
291+
}
285292

286293
// directoryExists
287294
if (originalDirectoryExists && originalCreateDirectory) {
@@ -306,7 +313,7 @@ namespace ts {
306313
originalDirectoryExists,
307314
originalCreateDirectory,
308315
originalWriteFile,
309-
originalGetSourceFile,
316+
getSourceFileWithCache,
310317
readFileWithCache
311318
};
312319
}
@@ -735,7 +742,7 @@ namespace ts {
735742
performance.mark("beforeProgram");
736743

737744
const host = createProgramOptions.host || createCompilerHost(options);
738-
const configParsingHost = parseConfigHostFromCompilerHost(host);
745+
const configParsingHost = parseConfigHostFromCompilerHostLike(host);
739746

740747
let skipDefaultLib = options.noLib;
741748
const getDefaultLibraryFileName = memoize(() => host.getDefaultLibFileName(options));
@@ -3104,18 +3111,28 @@ namespace ts {
31043111
}
31053112
}
31063113

3114+
interface CompilerHostLike {
3115+
useCaseSensitiveFileNames(): boolean;
3116+
getCurrentDirectory(): string;
3117+
fileExists(fileName: string): boolean;
3118+
readFile(fileName: string): string | undefined;
3119+
readDirectory?(rootDir: string, extensions: ReadonlyArray<string>, excludes: ReadonlyArray<string> | undefined, includes: ReadonlyArray<string>, depth?: number): string[];
3120+
trace?(s: string): void;
3121+
onUnRecoverableConfigFileDiagnostic?: DiagnosticReporter;
3122+
}
3123+
31073124
/* @internal */
3108-
export function parseConfigHostFromCompilerHost(host: CompilerHost): ParseConfigFileHost {
3125+
export function parseConfigHostFromCompilerHostLike(host: CompilerHostLike, directoryStructureHost: DirectoryStructureHost = host): ParseConfigFileHost {
31093126
return {
3110-
fileExists: f => host.fileExists(f),
3127+
fileExists: f => directoryStructureHost.fileExists(f),
31113128
readDirectory(root, extensions, excludes, includes, depth) {
3112-
Debug.assertDefined(host.readDirectory, "'CompilerHost.readDirectory' must be implemented to correctly process 'projectReferences'");
3113-
return host.readDirectory!(root, extensions, excludes, includes, depth);
3129+
Debug.assertDefined(directoryStructureHost.readDirectory, "'CompilerHost.readDirectory' must be implemented to correctly process 'projectReferences'");
3130+
return directoryStructureHost.readDirectory!(root, extensions, excludes, includes, depth);
31143131
},
3115-
readFile: f => host.readFile(f),
3132+
readFile: f => directoryStructureHost.readFile(f),
31163133
useCaseSensitiveFileNames: host.useCaseSensitiveFileNames(),
31173134
getCurrentDirectory: () => host.getCurrentDirectory(),
3118-
onUnRecoverableConfigFileDiagnostic: () => undefined,
3135+
onUnRecoverableConfigFileDiagnostic: host.onUnRecoverableConfigFileDiagnostic || (() => undefined),
31193136
trace: host.trace ? (s) => host.trace!(s) : undefined
31203137
};
31213138
}

src/compiler/sys.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@ declare function setTimeout(handler: (...args: any[]) => void, timeout: number):
22
declare function clearTimeout(handle: any): void;
33

44
namespace ts {
5+
/**
6+
* djb2 hashing algorithm
7+
* http://www.cse.yorku.ca/~oz/hash.html
8+
*/
9+
/* @internal */
10+
export function generateDjb2Hash(data: string): string {
11+
const chars = data.split("").map(str => str.charCodeAt(0));
12+
return `${chars.reduce((prev, curr) => ((prev << 5) + prev) + curr, 5381)}`;
13+
}
14+
515
/**
616
* Set a high stack trace limit to provide more information in case of an error.
717
* Called for command-line and server use cases.
@@ -1115,15 +1125,6 @@ namespace ts {
11151125
}
11161126
}
11171127

1118-
/**
1119-
* djb2 hashing algorithm
1120-
* http://www.cse.yorku.ca/~oz/hash.html
1121-
*/
1122-
function generateDjb2Hash(data: string): string {
1123-
const chars = data.split("").map(str => str.charCodeAt(0));
1124-
return `${chars.reduce((prev, curr) => ((prev << 5) + prev) + curr, 5381)}`;
1125-
}
1126-
11271128
function createMD5HashUsingNativeCrypto(data: string): string {
11281129
const hash = _crypto!.createHash("md5");
11291130
hash.update(data);

0 commit comments

Comments
 (0)