Skip to content

Commit 521edc1

Browse files
committed
Refactoring to handle case sensitivity of the host when caching
1 parent 50bcfb6 commit 521edc1

File tree

1 file changed

+60
-66
lines changed

1 file changed

+60
-66
lines changed

src/compiler/tsbuild.ts

Lines changed: 60 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ namespace ts {
3434
/**
3535
* Map from config file name to up-to-date status
3636
*/
37-
projectStatus: FileMap<UpToDateStatus>;
38-
diagnostics?: FileMap<number>; // TODO(shkamat): this should be really be diagnostics but thats for later time
37+
projectStatus: ConfigFileMap<UpToDateStatus>;
38+
diagnostics?: ConfigFileMap<number>; // TODO(shkamat): this should be really be diagnostics but thats for later time
3939

4040
invalidateProject(project: ResolvedConfigFileName, dependencyGraph: DependencyGraph | undefined): void;
4141
getNextInvalidatedProject(): ResolvedConfigFileName | undefined;
@@ -189,110 +189,96 @@ namespace ts {
189189
}
190190
}
191191

192-
interface FileMap<T> {
193-
setValue(fileName: string, value: T): void;
194-
getValue(fileName: string): T | never;
195-
getValueOrUndefined(fileName: string): T | undefined;
196-
hasKey(fileName: string): boolean;
197-
removeKey(fileName: string): void;
198-
getKeys(): string[];
192+
interface FileMap<T, U extends string = string, V extends Path = Path> {
193+
setValue(fileName: U, value: T): void;
194+
getValue(fileName: U): T | undefined;
195+
hasKey(fileName: U): boolean;
196+
removeKey(fileName: U): void;
197+
forEach(action: (value: T, key: V) => void): void;
199198
getSize(): number;
200199
}
201200

201+
type ResolvedConfigFilePath = ResolvedConfigFileName & Path;
202+
type ConfigFileMap<T> = FileMap<T, ResolvedConfigFileName, ResolvedConfigFilePath>;
203+
type ToResolvedConfigFilePath = (fileName: ResolvedConfigFileName) => ResolvedConfigFilePath;
204+
type ToPath = (fileName: string) => Path;
205+
202206
/**
203207
* A FileMap maintains a normalized-key to value relationship
204208
*/
205-
function createFileMap<T>(): FileMap<T> {
209+
function createFileMap<T>(toPath: ToResolvedConfigFilePath): ConfigFileMap<T>;
210+
function createFileMap<T, U extends string = string, V extends Path = Path>(toPath: ToPath): FileMap<T, U, V>;
211+
function createFileMap<T, U extends string = string, V extends Path = Path>(toPath: (fileName: U) => V): FileMap<T, U, V> {
206212
// tslint:disable-next-line:no-null-keyword
207213
const lookup = createMap<T>();
208214

209215
return {
210216
setValue,
211217
getValue,
212-
getValueOrUndefined,
213218
removeKey,
214-
getKeys,
219+
forEach,
215220
hasKey,
216221
getSize
217222
};
218223

219-
function getKeys(): string[] {
220-
return Object.keys(lookup);
221-
}
222-
223-
function hasKey(fileName: string) {
224-
return lookup.has(normalizePath(fileName));
224+
function forEach(action: (value: T, key: V) => void) {
225+
lookup.forEach(action);
225226
}
226227

227-
function removeKey(fileName: string) {
228-
lookup.delete(normalizePath(fileName));
228+
function hasKey(fileName: U) {
229+
return lookup.has(toPath(fileName));
229230
}
230231

231-
function setValue(fileName: string, value: T) {
232-
lookup.set(normalizePath(fileName), value);
232+
function removeKey(fileName: U) {
233+
lookup.delete(toPath(fileName));
233234
}
234235

235-
function getValue(fileName: string): T | never {
236-
const f = normalizePath(fileName);
237-
if (lookup.has(f)) {
238-
return lookup.get(f)!;
239-
}
240-
else {
241-
throw new Error(`No value corresponding to ${fileName} exists in this map`);
242-
}
236+
function setValue(fileName: U, value: T) {
237+
lookup.set(toPath(fileName), value);
243238
}
244239

245-
function getValueOrUndefined(fileName: string): T | undefined {
246-
const f = normalizePath(fileName);
247-
return lookup.get(f);
240+
function getValue(fileName: U): T | undefined {
241+
return lookup.get(toPath(fileName));
248242
}
249243

250244
function getSize() {
251245
return lookup.size;
252246
}
253247
}
254248

255-
function createDependencyMapper() {
256-
const childToParents = createFileMap<ResolvedConfigFileName[]>();
257-
const parentToChildren = createFileMap<ResolvedConfigFileName[]>();
258-
const allKeys = createFileMap<true>();
249+
function createDependencyMapper(toPath: ToResolvedConfigFilePath) {
250+
const childToParents = createFileMap<ResolvedConfigFileName[]>(toPath);
251+
const parentToChildren = createFileMap<ResolvedConfigFileName[]>(toPath);
259252

260253
function addReference(childConfigFileName: ResolvedConfigFileName, parentConfigFileName: ResolvedConfigFileName): void {
261254
addEntry(childToParents, childConfigFileName, parentConfigFileName);
262255
addEntry(parentToChildren, parentConfigFileName, childConfigFileName);
263256
}
264257

265258
function getReferencesTo(parentConfigFileName: ResolvedConfigFileName): ResolvedConfigFileName[] {
266-
return parentToChildren.getValueOrUndefined(parentConfigFileName) || [];
259+
return parentToChildren.getValue(parentConfigFileName) || [];
267260
}
268261

269262
function getReferencesOf(childConfigFileName: ResolvedConfigFileName): ResolvedConfigFileName[] {
270-
return childToParents.getValueOrUndefined(childConfigFileName) || [];
271-
}
272-
273-
function getKeys(): ReadonlyArray<ResolvedConfigFileName> {
274-
return allKeys.getKeys() as ResolvedConfigFileName[];
263+
return childToParents.getValue(childConfigFileName) || [];
275264
}
276265

277266
function addEntry(mapToAddTo: typeof childToParents | typeof parentToChildren, key: ResolvedConfigFileName, element: ResolvedConfigFileName) {
278267
key = normalizePath(key) as ResolvedConfigFileName;
279268
element = normalizePath(element) as ResolvedConfigFileName;
280-
let arr = mapToAddTo.getValueOrUndefined(key);
269+
let arr = mapToAddTo.getValue(key);
281270
if (arr === undefined) {
282271
mapToAddTo.setValue(key, arr = []);
283272
}
284273
if (arr.indexOf(element) < 0) {
285274
arr.push(element);
286275
}
287-
allKeys.setValue(key, true);
288-
allKeys.setValue(element, true);
289276
}
290277

291278
return {
292279
addReference,
293280
getReferencesTo,
294281
getReferencesOf,
295-
getKeys
296282
};
297283
}
298284

@@ -355,16 +341,16 @@ namespace ts {
355341
return opts.rootDir || getDirectoryPath(configFileName);
356342
}
357343

358-
function createConfigFileCache(host: CompilerHost) {
359-
const cache = createFileMap<ParsedCommandLine | "error">();
344+
function createConfigFileCache(host: CompilerHost, toPath: ToResolvedConfigFilePath) {
345+
const cache = createFileMap<ParsedCommandLine | "error">(toPath);
360346
const configParseHost = parseConfigHostFromCompilerHost(host);
361347

362348
function isParsedCommandLine(value: ParsedCommandLine | "error"): value is ParsedCommandLine {
363349
return !(value as "error").length;
364350
}
365351

366352
function parseConfigFile(configFilePath: ResolvedConfigFileName) {
367-
const value = cache.getValueOrUndefined(configFilePath);
353+
const value = cache.getValue(configFilePath);
368354
if (value) {
369355
return isParsedCommandLine(value) ? value : undefined;
370356
}
@@ -398,18 +384,18 @@ namespace ts {
398384
return fileExtensionIs(fileName, Extension.Dts);
399385
}
400386

401-
export function createBuildContext(options: BuildOptions): BuildContext {
387+
export function createBuildContext(options: BuildOptions, toPath: ToResolvedConfigFilePath): BuildContext {
402388
const invalidatedProjectQueue = [] as ResolvedConfigFileName[];
403389
let nextIndex = 0;
404-
const projectPendingBuild = createFileMap<true>();
390+
const projectPendingBuild = createFileMap<true>(toPath);
405391
const missingRoots = createMap<true>();
406-
const diagnostics = options.watch ? createFileMap<number>() : undefined;
392+
const diagnostics = options.watch ? createFileMap<number>(toPath) : undefined;
407393

408394
return {
409395
options,
410-
projectStatus: createFileMap(),
396+
projectStatus: createFileMap(toPath),
411397
diagnostics,
412-
unchangedOutputs: createFileMap(),
398+
unchangedOutputs: createFileMap(toPath as ToPath),
413399
invalidateProject,
414400
getNextInvalidatedProject,
415401
hasPendingInvalidatedProjects,
@@ -513,8 +499,10 @@ namespace ts {
513499
*/
514500
export function createSolutionBuilder(host: SolutionBuilderHost, rootNames: ReadonlyArray<string>, defaultOptions: BuildOptions) {
515501
const hostWithWatch = host as SolutionBuilderWithWatchHost;
516-
const configFileCache = createConfigFileCache(host);
517-
let context = createBuildContext(defaultOptions);
502+
const currentDirectory = host.getCurrentDirectory();
503+
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames());
504+
const configFileCache = createConfigFileCache(host, toPath);
505+
let context = createBuildContext(defaultOptions, toPath);
518506
let timerToBuildInvalidatedProject: any;
519507
let reportFileChangeDetected = false;
520508

@@ -535,6 +523,12 @@ namespace ts {
535523
startWatching
536524
};
537525

526+
function toPath(fileName: ResolvedConfigFileName): ResolvedConfigFilePath;
527+
function toPath(fileName: string): Path;
528+
function toPath(fileName: string) {
529+
return ts.toPath(fileName, currentDirectory, getCanonicalFileName);
530+
}
531+
538532
function reportStatus(message: DiagnosticMessage, ...args: string[]) {
539533
host.reportSolutionBuilderStatus(createCompilerDiagnostic(message, ...args));
540534
}
@@ -600,7 +594,7 @@ namespace ts {
600594
}
601595

602596
function resetBuildContext(opts = defaultOptions) {
603-
context = createBuildContext(opts);
597+
context = createBuildContext(opts, toPath);
604598
}
605599

606600
function getUpToDateStatusOfFile(configFileName: ResolvedConfigFileName): UpToDateStatus {
@@ -623,13 +617,13 @@ namespace ts {
623617
return { type: UpToDateStatusType.Unbuildable, reason: "File deleted mid-build" };
624618
}
625619

626-
const prior = context.projectStatus.getValueOrUndefined(project.options.configFilePath!);
620+
const prior = context.projectStatus.getValue(project.options.configFilePath as ResolvedConfigFilePath);
627621
if (prior !== undefined) {
628622
return prior;
629623
}
630624

631625
const actual = getUpToDateStatusWorker(project);
632-
context.projectStatus.setValue(project.options.configFilePath!, actual);
626+
context.projectStatus.setValue(project.options.configFilePath as ResolvedConfigFilePath, actual);
633627
return actual;
634628
}
635629

@@ -700,7 +694,7 @@ namespace ts {
700694
// had its file touched but not had its contents changed - this allows us
701695
// to skip a downstream typecheck
702696
if (isDeclarationFile(output)) {
703-
const unchangedTime = context.unchangedOutputs.getValueOrUndefined(output);
697+
const unchangedTime = context.unchangedOutputs.getValue(output);
704698
if (unchangedTime !== undefined) {
705699
newestDeclarationFileContentChangedTime = newer(unchangedTime, newestDeclarationFileContentChangedTime);
706700
}
@@ -845,9 +839,9 @@ namespace ts {
845839

846840
function reportErrorSummary() {
847841
if (context.options.watch) {
848-
let errorCount = 0;
849-
context.diagnostics!.getKeys().forEach(resolved => errorCount += context.diagnostics!.getValue(resolved));
850-
reportWatchStatus(errorCount === 1 ? Diagnostics.Found_1_error_Watching_for_file_changes : Diagnostics.Found_0_errors_Watching_for_file_changes, errorCount);
842+
let totalErrors = 0;
843+
context.diagnostics!.forEach(singleProjectErrors => totalErrors += singleProjectErrors);
844+
reportWatchStatus(totalErrors === 1 ? Diagnostics.Found_1_error_Watching_for_file_changes : Diagnostics.Found_0_errors_Watching_for_file_changes, totalErrors);
851845
}
852846
}
853847

@@ -881,7 +875,7 @@ namespace ts {
881875
const permanentMarks: { [path: string]: true } = {};
882876
const circularityReportStack: string[] = [];
883877
const buildOrder: ResolvedConfigFileName[] = [];
884-
const graph = createDependencyMapper();
878+
const graph = createDependencyMapper(toPath);
885879

886880
let hadError = false;
887881

@@ -1061,7 +1055,7 @@ namespace ts {
10611055
host.setModifiedTime(file, now);
10621056
}
10631057

1064-
context.projectStatus.setValue(proj.options.configFilePath!, { type: UpToDateStatusType.UpToDate, newestDeclarationFileContentChangedTime: priorNewestUpdateTime } as UpToDateStatus);
1058+
context.projectStatus.setValue(proj.options.configFilePath as ResolvedConfigFilePath, { type: UpToDateStatusType.UpToDate, newestDeclarationFileContentChangedTime: priorNewestUpdateTime } as UpToDateStatus);
10651059
}
10661060

10671061
function getFilesToClean(configFileNames: ReadonlyArray<ResolvedConfigFileName>): string[] | undefined {

0 commit comments

Comments
 (0)