Skip to content

Commit 136b091

Browse files
committed
Update based on feedback
1 parent f1b1b12 commit 136b091

7 files changed

Lines changed: 69 additions & 51 deletions

File tree

src/compiler/program.ts

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ namespace ts {
393393
allDiagnostics?: Diagnostic[];
394394
}
395395

396-
export function isProgramUptoDate(program: Program, rootFileNames: string[], newOptions: CompilerOptions,
396+
export function isProgramUptoDate(program: Program | undefined, rootFileNames: string[], newOptions: CompilerOptions,
397397
getSourceVersion: (path: Path) => string, fileExists: (fileName: string) => boolean, hasInvalidatedResolution: HasInvalidatedResolution): boolean {
398398
// If we haven't create a program yet, then it is not up-to-date
399399
if (!program) {
@@ -406,14 +406,12 @@ namespace ts {
406406
}
407407

408408
// If any file is not up-to-date, then the whole program is not up-to-date
409-
for (const file of program.getSourceFiles()) {
410-
if (!sourceFileUpToDate(program.getSourceFile(file.fileName))) {
409+
if (program.getSourceFiles().some(sourceFileNotUptoDate)) {
411410
return false;
412-
}
413411
}
414412

415413
// If any of the missing file paths are now created
416-
if (program.getMissingFilePaths().some(missingFilePath => fileExists(missingFilePath))) {
414+
if (program.getMissingFilePaths().some(fileExists)) {
417415
return false;
418416
}
419417

@@ -431,15 +429,18 @@ namespace ts {
431429

432430
return true;
433431

434-
function sourceFileUpToDate(sourceFile: SourceFile): boolean {
435-
return sourceFile &&
436-
sourceFile.version === getSourceVersion(sourceFile.path) &&
437-
!hasInvalidatedResolution(sourceFile.path);
432+
function sourceFileNotUptoDate(sourceFile: SourceFile): boolean {
433+
return sourceFile.version !== getSourceVersion(sourceFile.path) ||
434+
hasInvalidatedResolution(sourceFile.path);
438435
}
439436
}
440437

438+
/**
439+
* Determined if source file needs to be re-created even if its text hasnt changed
440+
*/
441441
function shouldProgramCreateNewSourceFiles(program: Program, newOptions: CompilerOptions) {
442442
// If any of these options change, we cant reuse old source file even if version match
443+
// The change in options like these could result in change in syntax tree change
443444
const oldOptions = program && program.getCompilerOptions();
444445
return oldOptions &&
445446
(oldOptions.target !== newOptions.target ||
@@ -478,19 +479,19 @@ namespace ts {
478479
);
479480
}
480481

481-
export interface WildcardDirectoryWatchers {
482+
export interface WildcardDirectoryWatcher {
482483
watcher: FileWatcher;
483484
flags: WatchDirectoryFlags;
484485
}
485486

486487
/**
487-
* Updates the existing wild card directory watcyhes with the new set of wild card directories from the config file after new program is created
488+
* Updates the existing wild card directory watches with the new set of wild card directories from the config file after new program is created
488489
*/
489490
export function updateWatchingWildcardDirectories(
490-
existingWatchedForWildcards: Map<WildcardDirectoryWatchers>,
491+
existingWatchedForWildcards: Map<WildcardDirectoryWatcher>,
491492
wildcardDirectories: Map<WatchDirectoryFlags>,
492493
watchDirectory: (directory: string, flags: WatchDirectoryFlags) => FileWatcher,
493-
closeDirectoryWatcher: (directory: string, wildcardDirectoryWatcher: WildcardDirectoryWatchers, flagsChanged: boolean) => void
494+
closeDirectoryWatcher: (directory: string, wildcardDirectoryWatcher: WildcardDirectoryWatcher, flagsChanged: boolean) => void
494495
) {
495496
mutateMap(
496497
existingWatchedForWildcards,
@@ -506,15 +507,15 @@ namespace ts {
506507
}
507508
);
508509

509-
function createWildcardDirectoryWatcher(directory: string, flags: WatchDirectoryFlags): WildcardDirectoryWatchers {
510+
function createWildcardDirectoryWatcher(directory: string, flags: WatchDirectoryFlags): WildcardDirectoryWatcher {
510511
// Create new watch and recursive info
511512
return {
512513
watcher: watchDirectory(directory, flags),
513514
flags
514515
};
515516
}
516517

517-
function updateWildcardDirectoryWatcher(directory: string, wildcardDirectoryWatcher: WildcardDirectoryWatchers, flags: WatchDirectoryFlags) {
518+
function updateWildcardDirectoryWatcher(directory: string, wildcardDirectoryWatcher: WildcardDirectoryWatcher, flags: WatchDirectoryFlags) {
518519
// Watcher needs to be updated if the recursive flags dont match
519520
if (wildcardDirectoryWatcher.flags === flags) {
520521
return;
@@ -622,7 +623,7 @@ namespace ts {
622623
let redirectTargetsSet = createMap<true>();
623624

624625
const filesByName = createMap<SourceFile | undefined>();
625-
let missingFilePaths: Path[];
626+
let missingFilePaths: ReadonlyArray<Path>;
626627
// stores 'filename -> file association' ignoring case
627628
// used to track cases when two file names differ only in casing
628629
const filesByNameIgnoreCase = host.useCaseSensitiveFileNames() ? createMap<SourceFile>() : undefined;
@@ -666,6 +667,8 @@ namespace ts {
666667
missingFilePaths = arrayFrom(filesByName.keys(), p => <Path>p).filter(p => !filesByName.get(p));
667668
}
668669

670+
Debug.assert(!!missingFilePaths);
671+
669672
// unconditionally set moduleResolutionCache to undefined to avoid unnecessary leaks
670673
moduleResolutionCache = undefined;
671674

src/compiler/resolutionCache.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
/// <reference path="types.ts"/>
22
/// <reference path="core.ts"/>
33

4+
/*@internal*/
45
namespace ts {
6+
/** This is the cache of module/typedirectives resolution that can be retained across program */
57
export interface ResolutionCache {
68
setModuleResolutionHost(host: ModuleResolutionHost): void;
79

@@ -19,10 +21,15 @@ namespace ts {
1921
clear(): void;
2022
}
2123

22-
type NameResolutionWithFailedLookupLocations = { failedLookupLocations: string[], isInvalidated?: boolean };
23-
type ResolverWithGlobalCache = (primaryResult: ResolvedModuleWithFailedLookupLocations, moduleName: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost) => ResolvedModuleWithFailedLookupLocations | undefined;
24+
interface NameResolutionWithFailedLookupLocations {
25+
readonly failedLookupLocations: string[];
26+
isInvalidated?: boolean;
27+
}
28+
29+
interface ResolverWithGlobalCache {
30+
(primaryResult: ResolvedModuleWithFailedLookupLocations, moduleName: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations | undefined;
31+
}
2432

25-
/*@internal*/
2633
export function resolveWithGlobalCache(primaryResult: ResolvedModuleWithFailedLookupLocations, moduleName: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, globalCache: string | undefined, projectName: string): ResolvedModuleWithFailedLookupLocations | undefined {
2734
if (!isExternalModuleNameRelative(moduleName) && !(primaryResult.resolvedModule && extensionIsTypeScript(primaryResult.resolvedModule.extension)) && globalCache !== undefined) {
2835
// otherwise try to load typings from @types
@@ -36,7 +43,11 @@ namespace ts {
3643
}
3744
}
3845

39-
/*@internal*/
46+
interface FailedLookupLocationsWatcher {
47+
fileWatcher: FileWatcher;
48+
refCount: number;
49+
}
50+
4051
export function createResolutionCache(
4152
toPath: (fileName: string) => Path,
4253
getCompilerOptions: () => CompilerOptions,
@@ -51,7 +62,6 @@ namespace ts {
5162
const resolvedModuleNames = createMap<Map<ResolvedModuleWithFailedLookupLocations>>();
5263
const resolvedTypeReferenceDirectives = createMap<Map<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
5364

54-
type FailedLookupLocationsWatcher = { fileWatcher: FileWatcher; refCount: number };
5565
const failedLookupLocationsWatches = createMap<FailedLookupLocationsWatcher>();
5666

5767
return {
@@ -71,11 +81,10 @@ namespace ts {
7181
}
7282

7383
function clear() {
74-
failedLookupLocationsWatches.forEach((failedLookupLocationWatcher, failedLookupLocationPath: Path) => {
84+
clearMap(failedLookupLocationsWatches, (failedLookupLocationPath, failedLookupLocationWatcher) => {
7585
log(`Watcher: FailedLookupLocations: Status: ForceClose: LocationPath: ${failedLookupLocationPath}, refCount: ${failedLookupLocationWatcher.refCount}`);
7686
failedLookupLocationWatcher.fileWatcher.close();
7787
});
78-
failedLookupLocationsWatches.clear();
7988
resolvedModuleNames.clear();
8089
resolvedTypeReferenceDirectives.clear();
8190
}

src/compiler/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2411,7 +2411,7 @@ namespace ts {
24112411
* program source file but could not be located.
24122412
*/
24132413
/* @internal */
2414-
getMissingFilePaths(): Path[];
2414+
getMissingFilePaths(): ReadonlyArray<Path>;
24152415

24162416
/**
24172417
* Emits the JavaScript and declaration files. If targetSourceFile is not specified, then
@@ -3561,6 +3561,7 @@ namespace ts {
35613561
charset?: string;
35623562
checkJs?: boolean;
35633563
/* @internal */ configFilePath?: string;
3564+
/** configFile is set as non enumerable property so as to avoid checking of json source files */
35643565
/* @internal */ readonly configFile?: JsonSourceFile;
35653566
declaration?: boolean;
35663567
declarationDir?: string;

src/compiler/utilities.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,10 @@ namespace ts {
403403
((<ModuleDeclaration>node).name.kind === SyntaxKind.StringLiteral || isGlobalScopeAugmentation(<ModuleDeclaration>node));
404404
}
405405

406-
/* @internal */
406+
export function isModuleWithStringLiteralName(node: Node): node is ModuleDeclaration {
407+
return isModuleDeclaration(node) && node.name.kind === SyntaxKind.StringLiteral;
408+
}
409+
407410
export function isNonGlobalAmbientModule(node: Node): node is ModuleDeclaration & { name: StringLiteral } {
408411
return isModuleDeclaration(node) && isStringLiteral(node.name);
409412
}
@@ -1403,10 +1406,6 @@ namespace ts {
14031406
return SpecialPropertyAssignmentKind.None;
14041407
}
14051408

1406-
export function isModuleWithStringLiteralName(node: Node): node is ModuleDeclaration {
1407-
return isModuleDeclaration(node) && node.name.kind === SyntaxKind.StringLiteral;
1408-
}
1409-
14101409
export function getExternalModuleName(node: Node): Expression {
14111410
if (node.kind === SyntaxKind.ImportDeclaration) {
14121411
return (<ImportDeclaration>node).moduleSpecifier;

src/compiler/watchedProgram.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,12 +241,13 @@ namespace ts {
241241
let needsReload: boolean; // true if the config file changed and needs to reload it from the disk
242242
let missingFilesMap: Map<FileWatcher>; // Map of file watchers for the missing files
243243
let configFileWatcher: FileWatcher; // watcher for the config file
244-
let watchedWildcardDirectories: Map<WildcardDirectoryWatchers>; // map of watchers for the wild card directories in the config file
244+
let watchedWildcardDirectories: Map<WildcardDirectoryWatcher>; // map of watchers for the wild card directories in the config file
245245
let timerToUpdateProgram: any; // timer callback to recompile the program
246246

247247
const sourceFilesCache = createMap<HostFileInfo | string>(); // Cache that stores the source file and version info
248248
let missingFilePathsRequestedForRelease: Path[]; // These paths are held temparirly so that we can remove the entry from source file cache if the file is not tracked by missing files
249249
let hasInvalidatedResolution: HasInvalidatedResolution; // Passed along to see if source file has invalidated resolutions
250+
let hasChangedCompilerOptions = false; // True if the compiler options have changed between compilations
250251

251252
watchingHost = watchingHost || createWatchingSystemHost(compilerOptions.pretty);
252253
const { system, parseConfigFile, reportDiagnostic, reportWatchDiagnostic, beforeCompile, afterCompile } = watchingHost;
@@ -291,9 +292,10 @@ namespace ts {
291292
// Create the compiler host
292293
const compilerHost = createWatchedCompilerHost(compilerOptions);
293294
resolutionCache.setModuleResolutionHost(compilerHost);
294-
if (changesAffectModuleResolution(program && program.getCompilerOptions(), compilerOptions)) {
295+
if (hasChangedCompilerOptions && changesAffectModuleResolution(program && program.getCompilerOptions(), compilerOptions)) {
295296
resolutionCache.clear();
296297
}
298+
hasChangedCompilerOptions = false;
297299
beforeCompile(compilerOptions);
298300

299301
// Compile the program
@@ -504,6 +506,7 @@ namespace ts {
504506
const configParseResult = parseConfigFile(configFileName, optionsToExtendForConfigFile, cachedHost, reportDiagnostic, reportWatchDiagnostic);
505507
rootFileNames = configParseResult.fileNames;
506508
compilerOptions = configParseResult.options;
509+
hasChangedCompilerOptions = true;
507510
configFileSpecs = configParseResult.configFileSpecs;
508511
configFileWildCardDirectories = configParseResult.wildcardDirectories;
509512

@@ -603,7 +606,7 @@ namespace ts {
603606
(flags & WatchDirectoryFlags.Recursive) !== 0);
604607
}
605608

606-
function stopWatchingWildCardDirectory(_directory: string, { watcher }: WildcardDirectoryWatchers, _recursiveChanged: boolean) {
609+
function stopWatchingWildCardDirectory(_directory: string, { watcher }: WildcardDirectoryWatcher, _recursiveChanged: boolean) {
607610
watcher.close();
608611
}
609612

src/harness/unittests/programMissingFiles.ts

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
/// <reference path="..\harness.ts" />
22

33
namespace ts {
4+
function verifyMissingFilePaths(missingPaths: ReadonlyArray<Path>, expected: ReadonlyArray<string>) {
5+
assert.isDefined(missingPaths);
6+
const map = arrayToMap(expected, k => k, _v => true);
7+
for (const missing of missingPaths) {
8+
const value = map.get(missing);
9+
assert.isTrue(value, `${missing} to be ${value === undefined ? "not present" : "present only once"}, in actual: ${missingPaths} expected: ${expected}`);
10+
map.set(missing, false);
11+
}
12+
const notFound = mapDefinedIter(map.keys(), k => map.get(k) === true ? k : undefined);
13+
assert.equal(notFound.length, 0, `Not found ${notFound} in actual: ${missingPaths} expected: ${expected}`);
14+
}
15+
416
describe("Program.getMissingFilePaths", () => {
517

618
const options: CompilerOptions = {
@@ -40,34 +52,31 @@ namespace ts {
4052
it("handles no missing root files", () => {
4153
const program = createProgram([emptyFileRelativePath], options, testCompilerHost);
4254
const missing = program.getMissingFilePaths();
43-
assert.isDefined(missing);
44-
assert.deepEqual(missing, []);
55+
verifyMissingFilePaths(missing, []);
4556
});
4657

4758
it("handles missing root file", () => {
4859
const program = createProgram(["./nonexistent.ts"], options, testCompilerHost);
4960
const missing = program.getMissingFilePaths();
50-
assert.isDefined(missing);
51-
assert.deepEqual(missing, ["d:/pretend/nonexistent.ts"]); // Absolute path
61+
verifyMissingFilePaths(missing, ["d:/pretend/nonexistent.ts"]); // Absolute path
5262
});
5363

5464
it("handles multiple missing root files", () => {
5565
const program = createProgram(["./nonexistent0.ts", "./nonexistent1.ts"], options, testCompilerHost);
56-
const missing = program.getMissingFilePaths().sort();
57-
assert.deepEqual(missing, ["d:/pretend/nonexistent0.ts", "d:/pretend/nonexistent1.ts"]);
66+
const missing = program.getMissingFilePaths();
67+
verifyMissingFilePaths(missing, ["d:/pretend/nonexistent0.ts", "d:/pretend/nonexistent1.ts"]);
5868
});
5969

6070
it("handles a mix of present and missing root files", () => {
6171
const program = createProgram(["./nonexistent0.ts", emptyFileRelativePath, "./nonexistent1.ts"], options, testCompilerHost);
62-
const missing = program.getMissingFilePaths().sort();
63-
assert.deepEqual(missing, ["d:/pretend/nonexistent0.ts", "d:/pretend/nonexistent1.ts"]);
72+
const missing = program.getMissingFilePaths();
73+
verifyMissingFilePaths(missing, ["d:/pretend/nonexistent0.ts", "d:/pretend/nonexistent1.ts"]);
6474
});
6575

6676
it("handles repeatedly specified root files", () => {
6777
const program = createProgram(["./nonexistent.ts", "./nonexistent.ts"], options, testCompilerHost);
6878
const missing = program.getMissingFilePaths();
69-
assert.isDefined(missing);
70-
assert.deepEqual(missing, ["d:/pretend/nonexistent.ts"]);
79+
verifyMissingFilePaths(missing, ["d:/pretend/nonexistent.ts"]);
7180
});
7281

7382
it("normalizes file paths", () => {
@@ -81,9 +90,8 @@ namespace ts {
8190

8291
it("handles missing triple slash references", () => {
8392
const program = createProgram([referenceFileRelativePath], options, testCompilerHost);
84-
const missing = program.getMissingFilePaths().sort();
85-
assert.isDefined(missing);
86-
assert.deepEqual(missing, [
93+
const missing = program.getMissingFilePaths();
94+
verifyMissingFilePaths(missing, [
8795
// From absolute reference
8896
"d:/imaginary/nonexistent1.ts",
8997

@@ -100,4 +108,4 @@ namespace ts {
100108
]);
101109
});
102110
});
103-
}
111+
}

src/server/project.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,11 +1020,6 @@ namespace ts.server {
10201020
}
10211021
}
10221022

1023-
interface WildcardDirectoryWatcher {
1024-
watcher: FileWatcher;
1025-
flags: WatchDirectoryFlags;
1026-
}
1027-
10281023
/**
10291024
* If a file is opened, the server will look for a tsconfig (or jsconfig)
10301025
* and if successfull create a ConfiguredProject for it.

0 commit comments

Comments
 (0)