Update based on feedback

This commit is contained in:
Sheetal Nandi 2017-08-14 15:52:20 -07:00
parent f1b1b12604
commit 136b091a4a
7 changed files with 69 additions and 51 deletions

View File

@ -393,7 +393,7 @@ namespace ts {
allDiagnostics?: Diagnostic[];
}
export function isProgramUptoDate(program: Program, rootFileNames: string[], newOptions: CompilerOptions,
export function isProgramUptoDate(program: Program | undefined, rootFileNames: string[], newOptions: CompilerOptions,
getSourceVersion: (path: Path) => string, fileExists: (fileName: string) => boolean, hasInvalidatedResolution: HasInvalidatedResolution): boolean {
// If we haven't create a program yet, then it is not up-to-date
if (!program) {
@ -406,14 +406,12 @@ namespace ts {
}
// If any file is not up-to-date, then the whole program is not up-to-date
for (const file of program.getSourceFiles()) {
if (!sourceFileUpToDate(program.getSourceFile(file.fileName))) {
if (program.getSourceFiles().some(sourceFileNotUptoDate)) {
return false;
}
}
// If any of the missing file paths are now created
if (program.getMissingFilePaths().some(missingFilePath => fileExists(missingFilePath))) {
if (program.getMissingFilePaths().some(fileExists)) {
return false;
}
@ -431,15 +429,18 @@ namespace ts {
return true;
function sourceFileUpToDate(sourceFile: SourceFile): boolean {
return sourceFile &&
sourceFile.version === getSourceVersion(sourceFile.path) &&
!hasInvalidatedResolution(sourceFile.path);
function sourceFileNotUptoDate(sourceFile: SourceFile): boolean {
return sourceFile.version !== getSourceVersion(sourceFile.path) ||
hasInvalidatedResolution(sourceFile.path);
}
}
/**
* Determined if source file needs to be re-created even if its text hasnt changed
*/
function shouldProgramCreateNewSourceFiles(program: Program, newOptions: CompilerOptions) {
// If any of these options change, we cant reuse old source file even if version match
// The change in options like these could result in change in syntax tree change
const oldOptions = program && program.getCompilerOptions();
return oldOptions &&
(oldOptions.target !== newOptions.target ||
@ -478,19 +479,19 @@ namespace ts {
);
}
export interface WildcardDirectoryWatchers {
export interface WildcardDirectoryWatcher {
watcher: FileWatcher;
flags: WatchDirectoryFlags;
}
/**
* Updates the existing wild card directory watcyhes with the new set of wild card directories from the config file after new program is created
* Updates the existing wild card directory watches with the new set of wild card directories from the config file after new program is created
*/
export function updateWatchingWildcardDirectories(
existingWatchedForWildcards: Map<WildcardDirectoryWatchers>,
existingWatchedForWildcards: Map<WildcardDirectoryWatcher>,
wildcardDirectories: Map<WatchDirectoryFlags>,
watchDirectory: (directory: string, flags: WatchDirectoryFlags) => FileWatcher,
closeDirectoryWatcher: (directory: string, wildcardDirectoryWatcher: WildcardDirectoryWatchers, flagsChanged: boolean) => void
closeDirectoryWatcher: (directory: string, wildcardDirectoryWatcher: WildcardDirectoryWatcher, flagsChanged: boolean) => void
) {
mutateMap(
existingWatchedForWildcards,
@ -506,7 +507,7 @@ namespace ts {
}
);
function createWildcardDirectoryWatcher(directory: string, flags: WatchDirectoryFlags): WildcardDirectoryWatchers {
function createWildcardDirectoryWatcher(directory: string, flags: WatchDirectoryFlags): WildcardDirectoryWatcher {
// Create new watch and recursive info
return {
watcher: watchDirectory(directory, flags),
@ -514,7 +515,7 @@ namespace ts {
};
}
function updateWildcardDirectoryWatcher(directory: string, wildcardDirectoryWatcher: WildcardDirectoryWatchers, flags: WatchDirectoryFlags) {
function updateWildcardDirectoryWatcher(directory: string, wildcardDirectoryWatcher: WildcardDirectoryWatcher, flags: WatchDirectoryFlags) {
// Watcher needs to be updated if the recursive flags dont match
if (wildcardDirectoryWatcher.flags === flags) {
return;
@ -622,7 +623,7 @@ namespace ts {
let redirectTargetsSet = createMap<true>();
const filesByName = createMap<SourceFile | undefined>();
let missingFilePaths: Path[];
let missingFilePaths: ReadonlyArray<Path>;
// stores 'filename -> file association' ignoring case
// used to track cases when two file names differ only in casing
const filesByNameIgnoreCase = host.useCaseSensitiveFileNames() ? createMap<SourceFile>() : undefined;
@ -666,6 +667,8 @@ namespace ts {
missingFilePaths = arrayFrom(filesByName.keys(), p => <Path>p).filter(p => !filesByName.get(p));
}
Debug.assert(!!missingFilePaths);
// unconditionally set moduleResolutionCache to undefined to avoid unnecessary leaks
moduleResolutionCache = undefined;

View File

@ -1,7 +1,9 @@
/// <reference path="types.ts"/>
/// <reference path="core.ts"/>
/*@internal*/
namespace ts {
/** This is the cache of module/typedirectives resolution that can be retained across program */
export interface ResolutionCache {
setModuleResolutionHost(host: ModuleResolutionHost): void;
@ -19,10 +21,15 @@ namespace ts {
clear(): void;
}
type NameResolutionWithFailedLookupLocations = { failedLookupLocations: string[], isInvalidated?: boolean };
type ResolverWithGlobalCache = (primaryResult: ResolvedModuleWithFailedLookupLocations, moduleName: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost) => ResolvedModuleWithFailedLookupLocations | undefined;
interface NameResolutionWithFailedLookupLocations {
readonly failedLookupLocations: string[];
isInvalidated?: boolean;
}
interface ResolverWithGlobalCache {
(primaryResult: ResolvedModuleWithFailedLookupLocations, moduleName: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations | undefined;
}
/*@internal*/
export function resolveWithGlobalCache(primaryResult: ResolvedModuleWithFailedLookupLocations, moduleName: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, globalCache: string | undefined, projectName: string): ResolvedModuleWithFailedLookupLocations | undefined {
if (!isExternalModuleNameRelative(moduleName) && !(primaryResult.resolvedModule && extensionIsTypeScript(primaryResult.resolvedModule.extension)) && globalCache !== undefined) {
// otherwise try to load typings from @types
@ -36,7 +43,11 @@ namespace ts {
}
}
/*@internal*/
interface FailedLookupLocationsWatcher {
fileWatcher: FileWatcher;
refCount: number;
}
export function createResolutionCache(
toPath: (fileName: string) => Path,
getCompilerOptions: () => CompilerOptions,
@ -51,7 +62,6 @@ namespace ts {
const resolvedModuleNames = createMap<Map<ResolvedModuleWithFailedLookupLocations>>();
const resolvedTypeReferenceDirectives = createMap<Map<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
type FailedLookupLocationsWatcher = { fileWatcher: FileWatcher; refCount: number };
const failedLookupLocationsWatches = createMap<FailedLookupLocationsWatcher>();
return {
@ -71,11 +81,10 @@ namespace ts {
}
function clear() {
failedLookupLocationsWatches.forEach((failedLookupLocationWatcher, failedLookupLocationPath: Path) => {
clearMap(failedLookupLocationsWatches, (failedLookupLocationPath, failedLookupLocationWatcher) => {
log(`Watcher: FailedLookupLocations: Status: ForceClose: LocationPath: ${failedLookupLocationPath}, refCount: ${failedLookupLocationWatcher.refCount}`);
failedLookupLocationWatcher.fileWatcher.close();
});
failedLookupLocationsWatches.clear();
resolvedModuleNames.clear();
resolvedTypeReferenceDirectives.clear();
}

View File

@ -2411,7 +2411,7 @@ namespace ts {
* program source file but could not be located.
*/
/* @internal */
getMissingFilePaths(): Path[];
getMissingFilePaths(): ReadonlyArray<Path>;
/**
* Emits the JavaScript and declaration files. If targetSourceFile is not specified, then
@ -3561,6 +3561,7 @@ namespace ts {
charset?: string;
checkJs?: boolean;
/* @internal */ configFilePath?: string;
/** configFile is set as non enumerable property so as to avoid checking of json source files */
/* @internal */ readonly configFile?: JsonSourceFile;
declaration?: boolean;
declarationDir?: string;

View File

@ -403,7 +403,10 @@ namespace ts {
((<ModuleDeclaration>node).name.kind === SyntaxKind.StringLiteral || isGlobalScopeAugmentation(<ModuleDeclaration>node));
}
/* @internal */
export function isModuleWithStringLiteralName(node: Node): node is ModuleDeclaration {
return isModuleDeclaration(node) && node.name.kind === SyntaxKind.StringLiteral;
}
export function isNonGlobalAmbientModule(node: Node): node is ModuleDeclaration & { name: StringLiteral } {
return isModuleDeclaration(node) && isStringLiteral(node.name);
}
@ -1403,10 +1406,6 @@ namespace ts {
return SpecialPropertyAssignmentKind.None;
}
export function isModuleWithStringLiteralName(node: Node): node is ModuleDeclaration {
return isModuleDeclaration(node) && node.name.kind === SyntaxKind.StringLiteral;
}
export function getExternalModuleName(node: Node): Expression {
if (node.kind === SyntaxKind.ImportDeclaration) {
return (<ImportDeclaration>node).moduleSpecifier;

View File

@ -241,12 +241,13 @@ namespace ts {
let needsReload: boolean; // true if the config file changed and needs to reload it from the disk
let missingFilesMap: Map<FileWatcher>; // Map of file watchers for the missing files
let configFileWatcher: FileWatcher; // watcher for the config file
let watchedWildcardDirectories: Map<WildcardDirectoryWatchers>; // map of watchers for the wild card directories in the config file
let watchedWildcardDirectories: Map<WildcardDirectoryWatcher>; // map of watchers for the wild card directories in the config file
let timerToUpdateProgram: any; // timer callback to recompile the program
const sourceFilesCache = createMap<HostFileInfo | string>(); // Cache that stores the source file and version info
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
let hasInvalidatedResolution: HasInvalidatedResolution; // Passed along to see if source file has invalidated resolutions
let hasChangedCompilerOptions = false; // True if the compiler options have changed between compilations
watchingHost = watchingHost || createWatchingSystemHost(compilerOptions.pretty);
const { system, parseConfigFile, reportDiagnostic, reportWatchDiagnostic, beforeCompile, afterCompile } = watchingHost;
@ -291,9 +292,10 @@ namespace ts {
// Create the compiler host
const compilerHost = createWatchedCompilerHost(compilerOptions);
resolutionCache.setModuleResolutionHost(compilerHost);
if (changesAffectModuleResolution(program && program.getCompilerOptions(), compilerOptions)) {
if (hasChangedCompilerOptions && changesAffectModuleResolution(program && program.getCompilerOptions(), compilerOptions)) {
resolutionCache.clear();
}
hasChangedCompilerOptions = false;
beforeCompile(compilerOptions);
// Compile the program
@ -504,6 +506,7 @@ namespace ts {
const configParseResult = parseConfigFile(configFileName, optionsToExtendForConfigFile, cachedHost, reportDiagnostic, reportWatchDiagnostic);
rootFileNames = configParseResult.fileNames;
compilerOptions = configParseResult.options;
hasChangedCompilerOptions = true;
configFileSpecs = configParseResult.configFileSpecs;
configFileWildCardDirectories = configParseResult.wildcardDirectories;
@ -603,7 +606,7 @@ namespace ts {
(flags & WatchDirectoryFlags.Recursive) !== 0);
}
function stopWatchingWildCardDirectory(_directory: string, { watcher }: WildcardDirectoryWatchers, _recursiveChanged: boolean) {
function stopWatchingWildCardDirectory(_directory: string, { watcher }: WildcardDirectoryWatcher, _recursiveChanged: boolean) {
watcher.close();
}

View File

@ -1,6 +1,18 @@
/// <reference path="..\harness.ts" />
namespace ts {
function verifyMissingFilePaths(missingPaths: ReadonlyArray<Path>, expected: ReadonlyArray<string>) {
assert.isDefined(missingPaths);
const map = arrayToMap(expected, k => k, _v => true);
for (const missing of missingPaths) {
const value = map.get(missing);
assert.isTrue(value, `${missing} to be ${value === undefined ? "not present" : "present only once"}, in actual: ${missingPaths} expected: ${expected}`);
map.set(missing, false);
}
const notFound = mapDefinedIter(map.keys(), k => map.get(k) === true ? k : undefined);
assert.equal(notFound.length, 0, `Not found ${notFound} in actual: ${missingPaths} expected: ${expected}`);
}
describe("Program.getMissingFilePaths", () => {
const options: CompilerOptions = {
@ -40,34 +52,31 @@ namespace ts {
it("handles no missing root files", () => {
const program = createProgram([emptyFileRelativePath], options, testCompilerHost);
const missing = program.getMissingFilePaths();
assert.isDefined(missing);
assert.deepEqual(missing, []);
verifyMissingFilePaths(missing, []);
});
it("handles missing root file", () => {
const program = createProgram(["./nonexistent.ts"], options, testCompilerHost);
const missing = program.getMissingFilePaths();
assert.isDefined(missing);
assert.deepEqual(missing, ["d:/pretend/nonexistent.ts"]); // Absolute path
verifyMissingFilePaths(missing, ["d:/pretend/nonexistent.ts"]); // Absolute path
});
it("handles multiple missing root files", () => {
const program = createProgram(["./nonexistent0.ts", "./nonexistent1.ts"], options, testCompilerHost);
const missing = program.getMissingFilePaths().sort();
assert.deepEqual(missing, ["d:/pretend/nonexistent0.ts", "d:/pretend/nonexistent1.ts"]);
const missing = program.getMissingFilePaths();
verifyMissingFilePaths(missing, ["d:/pretend/nonexistent0.ts", "d:/pretend/nonexistent1.ts"]);
});
it("handles a mix of present and missing root files", () => {
const program = createProgram(["./nonexistent0.ts", emptyFileRelativePath, "./nonexistent1.ts"], options, testCompilerHost);
const missing = program.getMissingFilePaths().sort();
assert.deepEqual(missing, ["d:/pretend/nonexistent0.ts", "d:/pretend/nonexistent1.ts"]);
const missing = program.getMissingFilePaths();
verifyMissingFilePaths(missing, ["d:/pretend/nonexistent0.ts", "d:/pretend/nonexistent1.ts"]);
});
it("handles repeatedly specified root files", () => {
const program = createProgram(["./nonexistent.ts", "./nonexistent.ts"], options, testCompilerHost);
const missing = program.getMissingFilePaths();
assert.isDefined(missing);
assert.deepEqual(missing, ["d:/pretend/nonexistent.ts"]);
verifyMissingFilePaths(missing, ["d:/pretend/nonexistent.ts"]);
});
it("normalizes file paths", () => {
@ -81,9 +90,8 @@ namespace ts {
it("handles missing triple slash references", () => {
const program = createProgram([referenceFileRelativePath], options, testCompilerHost);
const missing = program.getMissingFilePaths().sort();
assert.isDefined(missing);
assert.deepEqual(missing, [
const missing = program.getMissingFilePaths();
verifyMissingFilePaths(missing, [
// From absolute reference
"d:/imaginary/nonexistent1.ts",
@ -100,4 +108,4 @@ namespace ts {
]);
});
});
}
}

View File

@ -1020,11 +1020,6 @@ namespace ts.server {
}
}
interface WildcardDirectoryWatcher {
watcher: FileWatcher;
flags: WatchDirectoryFlags;
}
/**
* If a file is opened, the server will look for a tsconfig (or jsconfig)
* and if successfull create a ConfiguredProject for it.