PR feedback

This commit is contained in:
Sheetal Nandi
2017-09-26 16:21:15 -07:00
parent 38f3a2b700
commit 68d360585a
20 changed files with 472 additions and 431 deletions
+31 -18
View File
@@ -43,10 +43,23 @@ namespace ts {
}
interface EmitHandler {
addScriptInfo(program: Program, sourceFile: SourceFile): void;
removeScriptInfo(path: Path): void;
updateScriptInfo(program: Program, sourceFile: SourceFile): void;
updaterScriptInfoWithSameVersion(program: Program, sourceFile: SourceFile): boolean;
/**
* Called when sourceFile is added to the program
*/
onAddSourceFile(program: Program, sourceFile: SourceFile): void;
/**
* Called when sourceFile is removed from the program
*/
onRemoveSourceFile(path: Path): void;
/**
* Called when sourceFile is changed
*/
onUpdateSourceFile(program: Program, sourceFile: SourceFile): void;
/**
* Called when source file has not changed but has some of the resolutions invalidated
* If returned true, builder will mark the file as changed (noting that something associated with file has changed)
*/
onUpdateSourceFileWithSameVersion(program: Program, sourceFile: SourceFile): boolean;
/**
* Gets the files affected by the script info which has updated shape from the known one
*/
@@ -135,23 +148,23 @@ namespace ts {
function addNewFileInfo(program: Program, sourceFile: SourceFile): FileInfo {
registerChangedFile(sourceFile.path, sourceFile.fileName);
emitHandler.addScriptInfo(program, sourceFile);
emitHandler.onAddSourceFile(program, sourceFile);
return { fileName: sourceFile.fileName, version: sourceFile.version, signature: undefined };
}
function removeExistingFileInfo(existingFileInfo: FileInfo, path: Path) {
registerChangedFile(path, existingFileInfo.fileName);
emitHandler.removeScriptInfo(path);
emitHandler.onRemoveSourceFile(path);
}
function updateExistingFileInfo(program: Program, existingInfo: FileInfo, sourceFile: SourceFile, hasInvalidatedResolution: HasInvalidatedResolution) {
if (existingInfo.version !== sourceFile.version) {
registerChangedFile(sourceFile.path, sourceFile.fileName);
existingInfo.version = sourceFile.version;
emitHandler.updateScriptInfo(program, sourceFile);
emitHandler.onUpdateSourceFile(program, sourceFile);
}
else if (hasInvalidatedResolution(sourceFile.path) &&
emitHandler.updaterScriptInfoWithSameVersion(program, sourceFile)) {
emitHandler.onUpdateSourceFileWithSameVersion(program, sourceFile)) {
registerChangedFile(sourceFile.path, sourceFile.fileName);
}
}
@@ -388,10 +401,10 @@ namespace ts {
function getNonModuleEmitHandler(): EmitHandler {
return {
addScriptInfo: noop,
removeScriptInfo: noop,
updateScriptInfo: noop,
updaterScriptInfoWithSameVersion: returnFalse,
onAddSourceFile: noop,
onRemoveSourceFile: noop,
onUpdateSourceFile: noop,
onUpdateSourceFileWithSameVersion: returnFalse,
getFilesAffectedByUpdatedShape
};
@@ -409,17 +422,17 @@ namespace ts {
function getModuleEmitHandler(): EmitHandler {
const references = createMap<Map<true>>();
return {
addScriptInfo: setReferences,
removeScriptInfo,
updateScriptInfo: updateReferences,
updaterScriptInfoWithSameVersion: updateReferencesTrackingChangedReferences,
onAddSourceFile: setReferences,
onRemoveSourceFile,
onUpdateSourceFile: updateReferences,
onUpdateSourceFileWithSameVersion: updateReferencesTrackingChangedReferences,
getFilesAffectedByUpdatedShape
};
function setReferences(program: Program, sourceFile: SourceFile) {
const newReferences = getReferencedFiles(program, sourceFile);
if (newReferences) {
references.set(sourceFile.path, getReferencedFiles(program, sourceFile));
references.set(sourceFile.path, newReferences);
}
}
@@ -452,7 +465,7 @@ namespace ts {
!!oldReferences.size;
}
function removeScriptInfo(removedFilePath: Path) {
function onRemoveSourceFile(removedFilePath: Path) {
// Remove existing references
references.forEach((referencesInFile, filePath) => {
if (referencesInFile.has(removedFilePath)) {
+1 -1
View File
@@ -1683,7 +1683,7 @@ namespace ts {
return undefined;
}
let extendedConfigPath = toPath(extendedConfig, basePath, getCanonicalFileName);
if (!host.fileExists(extendedConfigPath) && !endsWith(extendedConfigPath, ".json")) {
if (!host.fileExists(extendedConfigPath) && !endsWith(extendedConfigPath, Extension.Json)) {
extendedConfigPath = `${extendedConfigPath}.json` as Path;
if (!host.fileExists(extendedConfigPath)) {
errors.push(createDiagnostic(Diagnostics.File_0_does_not_exist, extendedConfig));
+11 -11
View File
@@ -2262,7 +2262,7 @@ namespace ts {
return ScriptKind.TS;
case Extension.Tsx:
return ScriptKind.TSX;
case ".json":
case Extension.Json:
return ScriptKind.JSON;
default:
return ScriptKind.Unknown;
@@ -2669,7 +2669,7 @@ namespace ts {
export function assertTypeIsNever(_: never): void { }
export interface CachedDirectoryStructureHost extends DirectoryStructureHost {
addOrDeleteFileOrFolder(fileOrFolder: string, fileOrFolderPath: Path): void;
addOrDeleteFileOrDirectory(fileOrDirectory: string, fileOrDirectoryPath: Path): void;
addOrDeleteFile(fileName: string, filePath: Path, eventKind: FileWatcherEventKind): void;
clearCache(): void;
}
@@ -2695,7 +2695,7 @@ namespace ts {
getCurrentDirectory,
getDirectories,
readDirectory,
addOrDeleteFileOrFolder,
addOrDeleteFileOrDirectory,
addOrDeleteFile,
clearCache,
exit: code => host.exit(code)
@@ -2824,23 +2824,23 @@ namespace ts {
}
}
function addOrDeleteFileOrFolder(fileOrFolder: string, fileOrFolderPath: Path) {
const existingResult = getCachedFileSystemEntries(fileOrFolderPath);
function addOrDeleteFileOrDirectory(fileOrDirectory: string, fileOrDirectoryPath: Path) {
const existingResult = getCachedFileSystemEntries(fileOrDirectoryPath);
if (existingResult) {
// This was a folder already present, remove it if this doesnt exist any more
if (!host.directoryExists(fileOrFolder)) {
cachedReadDirectoryResult.delete(fileOrFolderPath);
if (!host.directoryExists(fileOrDirectory)) {
cachedReadDirectoryResult.delete(fileOrDirectoryPath);
}
}
else {
// This was earlier a file (hence not in cached directory contents)
// or we never cached the directory containing it
const parentResult = getCachedFileSystemEntriesForBaseDir(fileOrFolderPath);
const parentResult = getCachedFileSystemEntriesForBaseDir(fileOrDirectoryPath);
if (parentResult) {
const baseName = getBaseNameOfFileName(fileOrFolder);
const baseName = getBaseNameOfFileName(fileOrDirectory);
if (parentResult) {
updateFilesOfFileSystemEntry(parentResult, baseName, host.fileExists(fileOrFolderPath));
updateFileSystemEntry(parentResult.directories, baseName, host.directoryExists(fileOrFolderPath));
updateFilesOfFileSystemEntry(parentResult, baseName, host.fileExists(fileOrDirectoryPath));
updateFileSystemEntry(parentResult.directories, baseName, host.directoryExists(fileOrDirectoryPath));
}
}
}
+7 -77
View File
@@ -395,6 +395,9 @@ namespace ts {
allDiagnostics?: Diagnostic[];
}
/**
* Determines if program structure is upto date or needs to be recreated
*/
export function isProgramUptoDate(
program: Program | undefined,
rootFileNames: string[],
@@ -402,10 +405,10 @@ namespace ts {
getSourceVersion: (path: Path) => string,
fileExists: (fileName: string) => boolean,
hasInvalidatedResolution: HasInvalidatedResolution,
hasChangedAutomaticTypeDirectiveNames: () => boolean,
hasChangedAutomaticTypeDirectiveNames: boolean,
): boolean {
// If we haven't create a program yet or has changed automatic type directives, then it is not up-to-date
if (!program || hasChangedAutomaticTypeDirectiveNames()) {
if (!program || hasChangedAutomaticTypeDirectiveNames) {
return false;
}
@@ -416,7 +419,7 @@ namespace ts {
// If any file is not up-to-date, then the whole program is not up-to-date
if (program.getSourceFiles().some(sourceFileNotUptoDate)) {
return false;
return false;
}
// If any of the missing file paths are now created
@@ -464,78 +467,6 @@ namespace ts {
);
}
/**
* Updates the existing missing file watches with the new set of missing files after new program is created
*/
export function updateMissingFilePathsWatch(
program: Program,
missingFileWatches: Map<FileWatcher>,
createMissingFileWatch: (missingFilePath: Path) => FileWatcher,
) {
const missingFilePaths = program.getMissingFilePaths();
const newMissingFilePathMap = arrayToSet(missingFilePaths);
// Update the missing file paths watcher
mutateMap(
missingFileWatches,
newMissingFilePathMap,
{
// Watch the missing files
createNewValue: createMissingFileWatch,
// Files that are no longer missing (e.g. because they are no longer required)
// should no longer be watched.
onDeleteValue: closeFileWatcher
}
);
}
export interface WildcardDirectoryWatcher {
watcher: FileWatcher;
flags: WatchDirectoryFlags;
}
/**
* Updates the existing wild card directory watches with the new set of wild card directories from the config file
* after new program is created because the config file was reloaded or program was created first time from the config file
* Note that there is no need to call this function when the program is updated with additional files without reloading config files,
* as wildcard directories wont change unless reloading config file
*/
export function updateWatchingWildcardDirectories(
existingWatchedForWildcards: Map<WildcardDirectoryWatcher>,
wildcardDirectories: Map<WatchDirectoryFlags>,
watchDirectory: (directory: string, flags: WatchDirectoryFlags) => FileWatcher
) {
mutateMap(
existingWatchedForWildcards,
wildcardDirectories,
{
// Create new watch and recursive info
createNewValue: createWildcardDirectoryWatcher,
// Close existing watch thats not needed any more
onDeleteValue: closeFileWatcherOf,
// Close existing watch that doesnt match in the flags
onExistingValue: updateWildcardDirectoryWatcher
}
);
function createWildcardDirectoryWatcher(directory: string, flags: WatchDirectoryFlags): WildcardDirectoryWatcher {
// Create new watch and recursive info
return {
watcher: watchDirectory(directory, flags),
flags
};
}
function updateWildcardDirectoryWatcher(existingWatcher: WildcardDirectoryWatcher, flags: WatchDirectoryFlags, directory: string) {
// Watcher needs to be updated if the recursive flags dont match
if (existingWatcher.flags === flags) {
return;
}
existingWatcher.watcher.close();
existingWatchedForWildcards.set(directory, createWildcardDirectoryWatcher(directory, flags));
}
}
/**
* Create a new 'Program' instance. A Program is an immutable collection of 'SourceFile's and a 'CompilerOptions'
* that represent a compilation unit.
@@ -599,7 +530,6 @@ namespace ts {
let moduleResolutionCache: ModuleResolutionCache;
let resolveModuleNamesWorker: (moduleNames: string[], containingFile: string, reusedNames?: string[]) => ResolvedModuleFull[];
const hasInvalidatedResolution = host.hasInvalidatedResolution || returnFalse;
const hasChangedAutomaticTypeDirectiveNames = host.hasChangedAutomaticTypeDirectiveNames && host.hasChangedAutomaticTypeDirectiveNames.bind(host) || returnFalse;
if (host.resolveModuleNames) {
resolveModuleNamesWorker = (moduleNames, containingFile, reusedNames) => host.resolveModuleNames(checkAllDefined(moduleNames), containingFile, reusedNames).map(resolved => {
// An older host may have omitted extension, in which case we should infer it from the file extension of resolvedFileName.
@@ -1105,7 +1035,7 @@ namespace ts {
return oldProgram.structureIsReused;
}
if (hasChangedAutomaticTypeDirectiveNames()) {
if (host.hasChangedAutomaticTypeDirectiveNames) {
return oldProgram.structureIsReused = StructureIsReused.SafeModules;
}
+101 -66
View File
@@ -1,5 +1,6 @@
/// <reference path="types.ts"/>
/// <reference path="core.ts"/>
/// <reference path="watchUtilities.ts"/>
/*@internal*/
namespace ts {
@@ -18,8 +19,6 @@ namespace ts {
startCachingPerDirectoryResolution(): void;
finishCachingPerDirectoryResolution(): void;
setRootDirectory(dir: string): void;
updateTypeRootsWatch(): void;
closeTypeRootsWatch(): void;
@@ -35,6 +34,12 @@ namespace ts {
resolvedFileName: string | undefined;
}
interface ResolvedModuleWithFailedLookupLocations extends ts.ResolvedModuleWithFailedLookupLocations, ResolutionWithFailedLookupLocations {
}
interface ResolvedTypeReferenceDirectiveWithFailedLookupLocations extends ts.ResolvedTypeReferenceDirectiveWithFailedLookupLocations, ResolutionWithFailedLookupLocations {
}
export interface ResolutionCacheHost extends ModuleResolutionHost {
toPath(fileName: string): Path;
getCompilationSettings(): CompilerOptions;
@@ -67,7 +72,7 @@ namespace ts {
(resolution: T): R;
}
export function createResolutionCache(resolutionHost: ResolutionCacheHost): ResolutionCache {
export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootDirForResolution: string): ResolutionCache {
let filesWithChangedSetOfUnresolvedImports: Path[] | undefined;
let filesWithInvalidatedResolutions: Map<true> | undefined;
let allFilesHaveInvalidatedResolution = false;
@@ -83,12 +88,18 @@ namespace ts {
const getCurrentDirectory = memoize(() => resolutionHost.getCurrentDirectory());
/**
* These are the extensions that failed lookup files will have by default,
* any other extension of failed lookup will be store that path in custom failed lookup path
* This helps in not having to comb through all resolutions when files are added/removed
* Note that .d.ts file also has .d.ts extension hence will be part of default extensions
*/
const failedLookupDefaultExtensions = [Extension.Ts, Extension.Tsx, Extension.Js, Extension.Jsx, Extension.Json];
const customFailedLookupPaths = createMap<number>();
const directoryWatchesOfFailedLookups = createMap<DirectoryWatchesOfFailedLookup>();
let rootDir: string;
let rootPath: Path;
const rootDir = rootDirForResolution && removeTrailingDirectorySeparator(getNormalizedAbsolutePath(rootDirForResolution, getCurrentDirectory()));
const rootPath = rootDir && resolutionHost.toPath(rootDir);
// TypeRoot watches for the types that get added as part of getAutomaticTypeDirectiveNames
const typeRootsWatches = createMap<FileWatcher>();
@@ -103,7 +114,6 @@ namespace ts {
removeResolutionsOfFile,
invalidateResolutionOfFile,
createHasInvalidatedResolution,
setRootDirectory,
updateTypeRootsWatch,
closeTypeRootsWatch,
clear
@@ -117,12 +127,6 @@ namespace ts {
return resolution.resolvedTypeReferenceDirective;
}
function setRootDirectory(dir: string) {
Debug.assert(!resolvedModuleNames.size && !resolvedTypeReferenceDirectives.size && !directoryWatchesOfFailedLookups.size);
rootDir = removeTrailingDirectorySeparator(getNormalizedAbsolutePath(dir, getCurrentDirectory()));
rootPath = resolutionHost.toPath(rootDir);
}
function isInDirectoryPath(dir: Path, file: Path) {
if (dir === undefined || file.length <= dir.length) {
return false;
@@ -238,9 +242,17 @@ namespace ts {
perDirectoryResolution.set(name, resolution);
}
resolutionsInFile.set(name, resolution);
const diffIndex = existingResolution && existingResolution.failedLookupLocations && resolution.failedLookupLocations && findDiffIndex(resolution.failedLookupLocations, existingResolution.failedLookupLocations);
watchFailedLookupLocationOfResolution(resolution, diffIndex || 0);
stopWatchFailedLookupLocationOfResolutionFrom(existingResolution, diffIndex || 0);
if (resolution.failedLookupLocations) {
if (existingResolution && existingResolution.failedLookupLocations) {
watchAndStopWatchDiffFailedLookupLocations(resolution, existingResolution);
}
else {
watchFailedLookupLocationOfResolution(resolution, 0);
}
}
else if (existingResolution) {
stopWatchFailedLookupLocationOfResolution(existingResolution);
}
if (logChanges && filesWithChangedSetOfUnresolvedImports && !resolutionIsEqualTo(existingResolution, resolution)) {
filesWithChangedSetOfUnresolvedImports.push(path);
// reset log changes to avoid recording the same file multiple times
@@ -344,68 +356,91 @@ namespace ts {
return fileExtensionIsOneOf(path, failedLookupDefaultExtensions);
}
function watchFailedLookupLocationOfResolution(resolution: ResolutionWithFailedLookupLocations, startIndex?: number) {
if (resolution && resolution.failedLookupLocations) {
for (let i = startIndex || 0; i < resolution.failedLookupLocations.length; i++) {
const failedLookupLocation = resolution.failedLookupLocations[i];
const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation);
// If the failed lookup location path is not one of the supported extensions,
// store it in the custom path
if (!isPathWithDefaultFailedLookupExtension(failedLookupLocationPath)) {
const refCount = customFailedLookupPaths.get(failedLookupLocationPath) || 0;
customFailedLookupPaths.set(failedLookupLocationPath, refCount + 1);
}
const { dir, dirPath } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
if (dirWatcher) {
dirWatcher.refCount++;
}
else {
directoryWatchesOfFailedLookups.set(dirPath, { watcher: createDirectoryWatcher(dir, dirPath), refCount: 1 });
}
function watchAndStopWatchDiffFailedLookupLocations(resolution: ResolutionWithFailedLookupLocations, existingResolution: ResolutionWithFailedLookupLocations) {
const failedLookupLocations = resolution.failedLookupLocations;
const existingFailedLookupLocations = existingResolution.failedLookupLocations;
for (let index = 0; index < failedLookupLocations.length; index++) {
if (index === existingFailedLookupLocations.length) {
// Additional failed lookup locations, watch from this index
watchFailedLookupLocationOfResolution(resolution, index);
return;
}
else if (failedLookupLocations[index] !== existingFailedLookupLocations[index]) {
// Different failed lookup locations,
// Watch new resolution failed lookup locations from this index and
// stop watching existing resolutions from this index
watchFailedLookupLocationOfResolution(resolution, index);
stopWatchFailedLookupLocationOfResolutionFrom(existingResolution, index);
return;
}
}
// All new failed lookup locations are already watched (and are same),
// Stop watching failed lookup locations of existing resolution after failed lookup locations length
stopWatchFailedLookupLocationOfResolutionFrom(existingResolution, failedLookupLocations.length);
}
function watchFailedLookupLocationOfResolution({ failedLookupLocations }: ResolutionWithFailedLookupLocations, startIndex: number) {
for (let i = startIndex; i < failedLookupLocations.length; i++) {
const failedLookupLocation = failedLookupLocations[i];
const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation);
// If the failed lookup location path is not one of the supported extensions,
// store it in the custom path
if (!isPathWithDefaultFailedLookupExtension(failedLookupLocationPath)) {
const refCount = customFailedLookupPaths.get(failedLookupLocationPath) || 0;
customFailedLookupPaths.set(failedLookupLocationPath, refCount + 1);
}
const { dir, dirPath } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
if (dirWatcher) {
dirWatcher.refCount++;
}
else {
directoryWatchesOfFailedLookups.set(dirPath, { watcher: createDirectoryWatcher(dir, dirPath), refCount: 1 });
}
}
}
function stopWatchFailedLookupLocationOfResolution(resolution: ResolutionWithFailedLookupLocations) {
stopWatchFailedLookupLocationOfResolutionFrom(resolution, 0);
if (resolution.failedLookupLocations) {
stopWatchFailedLookupLocationOfResolutionFrom(resolution, 0);
}
}
function stopWatchFailedLookupLocationOfResolutionFrom(resolution: ResolutionWithFailedLookupLocations, startIndex: number) {
if (resolution && resolution.failedLookupLocations) {
for (let i = startIndex; i < resolution.failedLookupLocations.length; i++) {
const failedLookupLocation = resolution.failedLookupLocations[i];
const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation);
const refCount = customFailedLookupPaths.get(failedLookupLocationPath);
if (refCount) {
if (refCount === 1) {
customFailedLookupPaths.delete(failedLookupLocationPath);
}
else {
customFailedLookupPaths.set(failedLookupLocationPath, refCount - 1);
}
function stopWatchFailedLookupLocationOfResolutionFrom({ failedLookupLocations }: ResolutionWithFailedLookupLocations, startIndex: number) {
for (let i = startIndex; i < failedLookupLocations.length; i++) {
const failedLookupLocation = failedLookupLocations[i];
const failedLookupLocationPath = resolutionHost.toPath(failedLookupLocation);
const refCount = customFailedLookupPaths.get(failedLookupLocationPath);
if (refCount) {
if (refCount === 1) {
customFailedLookupPaths.delete(failedLookupLocationPath);
}
else {
Debug.assert(refCount > 1);
customFailedLookupPaths.set(failedLookupLocationPath, refCount - 1);
}
const { dirPath } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
// Do not close the watcher yet since it might be needed by other failed lookup locations.
dirWatcher.refCount--;
}
const { dirPath } = getDirectoryToWatchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath);
const dirWatcher = directoryWatchesOfFailedLookups.get(dirPath);
// Do not close the watcher yet since it might be needed by other failed lookup locations.
dirWatcher.refCount--;
}
}
function createDirectoryWatcher(directory: string, dirPath: Path) {
return resolutionHost.watchDirectoryOfFailedLookupLocation(directory, fileOrFolder => {
const fileOrFolderPath = resolutionHost.toPath(fileOrFolder);
return resolutionHost.watchDirectoryOfFailedLookupLocation(directory, fileOrDirectory => {
const fileOrDirectoryPath = resolutionHost.toPath(fileOrDirectory);
if (resolutionHost.getCachedDirectoryStructureHost) {
// Since the file existance changed, update the sourceFiles cache
resolutionHost.getCachedDirectoryStructureHost().addOrDeleteFileOrFolder(fileOrFolder, fileOrFolderPath);
resolutionHost.getCachedDirectoryStructureHost().addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
}
// If the files are added to project root or node_modules directory, always run through the invalidation process
// Otherwise run through invalidation only if adding to the immediate directory
if (!allFilesHaveInvalidatedResolution &&
dirPath === rootPath || isNodeModulesDirectory(dirPath) || getDirectoryPath(fileOrFolderPath) === dirPath) {
if (invalidateResolutionOfFailedLookupLocation(fileOrFolderPath, dirPath === fileOrFolderPath)) {
dirPath === rootPath || isNodeModulesDirectory(dirPath) || getDirectoryPath(fileOrDirectoryPath) === dirPath) {
if (invalidateResolutionOfFailedLookupLocation(fileOrDirectoryPath, dirPath === fileOrDirectoryPath)) {
resolutionHost.onInvalidatedResolution();
}
}
@@ -482,21 +517,21 @@ namespace ts {
);
}
function invalidateResolutionOfFailedLookupLocation(fileOrFolderPath: Path, isCreatingWatchedDirectory: boolean) {
function invalidateResolutionOfFailedLookupLocation(fileOrDirectoryPath: Path, isCreatingWatchedDirectory: boolean) {
let isChangedFailedLookupLocation: (location: string) => boolean;
if (isCreatingWatchedDirectory) {
// Watching directory is created
// Invalidate any resolution has failed lookup in this directory
isChangedFailedLookupLocation = location => isInDirectoryPath(fileOrFolderPath, resolutionHost.toPath(location));
isChangedFailedLookupLocation = location => isInDirectoryPath(fileOrDirectoryPath, resolutionHost.toPath(location));
}
else {
// Some file or folder in the watching directory is created
// Some file or directory in the watching directory is created
// Return early if it does not have any of the watching extension or not the custom failed lookup path
if (!isPathWithDefaultFailedLookupExtension(fileOrFolderPath) && !customFailedLookupPaths.has(fileOrFolderPath)) {
if (!isPathWithDefaultFailedLookupExtension(fileOrDirectoryPath) && !customFailedLookupPaths.has(fileOrDirectoryPath)) {
return false;
}
// Resolution need to be invalidated if failed lookup location is same as the file or folder getting created
isChangedFailedLookupLocation = location => resolutionHost.toPath(location) === fileOrFolderPath;
// Resolution need to be invalidated if failed lookup location is same as the file or directory getting created
isChangedFailedLookupLocation = location => resolutionHost.toPath(location) === fileOrDirectoryPath;
}
const hasChangedFailedLookupLocation = (resolution: ResolutionWithFailedLookupLocations) => some(resolution.failedLookupLocations, isChangedFailedLookupLocation);
const invalidatedFilesCount = filesWithInvalidatedResolutions && filesWithInvalidatedResolutions.size;
@@ -513,11 +548,11 @@ namespace ts {
function createTypeRootsWatch(_typeRootPath: string, typeRoot: string): FileWatcher {
// Create new watch and recursive info
return resolutionHost.watchTypeRootsDirectory(typeRoot, fileOrFolder => {
const fileOrFolderPath = resolutionHost.toPath(fileOrFolder);
return resolutionHost.watchTypeRootsDirectory(typeRoot, fileOrDirectory => {
const fileOrDirectoryPath = resolutionHost.toPath(fileOrDirectory);
if (resolutionHost.getCachedDirectoryStructureHost) {
// Since the file existance changed, update the sourceFiles cache
resolutionHost.getCachedDirectoryStructureHost().addOrDeleteFileOrFolder(fileOrFolder, fileOrFolderPath);
resolutionHost.getCachedDirectoryStructureHost().addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
}
// For now just recompile
+20 -9
View File
@@ -78,8 +78,7 @@ namespace ts {
close(): void;
}
export interface DirectoryWatcher extends FileWatcher {
directoryName: string;
interface DirectoryWatcher extends FileWatcher {
referenceCount: number;
}
@@ -187,7 +186,7 @@ namespace ts {
fileWatcherCallbacks.remove(filePath, callback);
}
function fileEventHandler(eventName: string, relativeFileName: string, baseDirPath: string) {
function fileEventHandler(eventName: string, relativeFileName: string | undefined, baseDirPath: string) {
// When files are deleted from disk, the triggered "rename" event would have a relativefileName of "undefined"
const fileName = !isString(relativeFileName)
? undefined
@@ -255,19 +254,25 @@ namespace ts {
}
function fsWatchDirectory(directoryName: string, callback: (eventName: string, relativeFileName: string) => void, recursive?: boolean): FileWatcher {
// Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows
// (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643)
let options: any;
/** Watcher for the directory depending on whether it is missing or present */
let watcher = !directoryExists(directoryName) ?
watchMissingDirectory() :
watchPresentDirectory();
return {
close: () => {
// Close the watcher (either existing directory watcher or missing directory watcher)
watcher.close();
}
};
/**
* Watch the directory that is currently present
* and when the watched directory is deleted, switch to missing directory watcher
*/
function watchPresentDirectory(): FileWatcher {
// Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows
// (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643)
if (options === undefined) {
if (isNode4OrLater && (process.platform === "win32" || process.platform === "darwin")) {
options = { persistent: true, recursive: !!recursive };
@@ -283,14 +288,20 @@ namespace ts {
callback
);
dirWatcher.on("error", () => {
// Deleting file
watcher = watchMissingDirectory();
// Call the callback for current directory
callback("rename", "");
if (!directoryExists(directoryName)) {
// Deleting directory
watcher = watchMissingDirectory();
// Call the callback for current directory
callback("rename", "");
}
});
return dirWatcher;
}
/**
* Watch the directory that is missing
* and switch to existing directory when the directory is created
*/
function watchMissingDirectory(): FileWatcher {
return fsWatchFile(directoryName, (_fileName, eventKind) => {
if (eventKind === FileWatcherEventKind.Created && directoryExists(directoryName)) {
+1
View File
@@ -36,6 +36,7 @@
"sourcemap.ts",
"declarationEmitter.ts",
"emitter.ts",
"watchUtilities.ts",
"program.ts",
"builder.ts",
"resolutionCache.ts",
+1 -5
View File
@@ -4099,8 +4099,6 @@ namespace ts {
readonly resolvedModule: ResolvedModuleFull | undefined;
/* @internal */
readonly failedLookupLocations: ReadonlyArray<string>;
/*@internal*/
isInvalidated?: boolean;
}
export interface ResolvedTypeReferenceDirective {
@@ -4114,8 +4112,6 @@ namespace ts {
export interface ResolvedTypeReferenceDirectiveWithFailedLookupLocations {
readonly resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective;
readonly failedLookupLocations: ReadonlyArray<string>;
/*@internal*/
isInvalidated?: boolean;
}
export interface HasInvalidatedResolution {
@@ -4150,7 +4146,7 @@ namespace ts {
getEnvironmentVariable?(name: string): string;
onReleaseOldSourceFile?(oldSourceFile: SourceFile, oldOptions: CompilerOptions): void;
hasInvalidatedResolution?: HasInvalidatedResolution;
hasChangedAutomaticTypeDirectiveNames?(): boolean;
hasChangedAutomaticTypeDirectiveNames?: boolean;
}
/* @internal */
+1 -71
View File
@@ -3521,7 +3521,7 @@ namespace ts {
/**
* clears already present map by calling onDeleteExistingValue callback before deleting that key/value
*/
export function clearMap<T>(map: Map<T>, onDeleteValue: (existingValue: T, key: string) => void) {
export function clearMap<T>(map: Map<T>, onDeleteValue: (valueInMap: T, key: string) => void) {
// Remove all
map.forEach(onDeleteValue);
map.clear();
@@ -3568,76 +3568,6 @@ namespace ts {
});
}
/**
* Find the index where arrayA and arrayB differ
*/
export function findDiffIndex<T>(arrayA: ReadonlyArray<T>, arrayB: ReadonlyArray<T>) {
for (let i = 0; i < arrayA.length; i++) {
if (i === arrayB.length || arrayA[i] !== arrayB[i]) {
return i;
}
}
return arrayA.length;
}
export function addFileWatcher(host: System, file: string, cb: FileWatcherCallback): FileWatcher {
return host.watchFile(file, cb);
}
export function addFileWatcherWithLogging(host: System, file: string, cb: FileWatcherCallback, log: (s: string) => void): FileWatcher {
const watcherCaption = `FileWatcher:: `;
return createWatcherWithLogging(addFileWatcher, watcherCaption, log, host, file, cb);
}
export type FilePathWatcherCallback = (fileName: string, eventKind: FileWatcherEventKind, filePath: Path) => void;
export function addFilePathWatcher(host: System, file: string, cb: FilePathWatcherCallback, path: Path): FileWatcher {
return host.watchFile(file, (fileName, eventKind) => cb(fileName, eventKind, path));
}
export function addFilePathWatcherWithLogging(host: System, file: string, cb: FilePathWatcherCallback, path: Path, log: (s: string) => void): FileWatcher {
const watcherCaption = `FileWatcher:: `;
return createWatcherWithLogging(addFileWatcher, watcherCaption, log, host, file, cb, path);
}
export function addDirectoryWatcher(host: System, directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags): FileWatcher {
const recursive = (flags & WatchDirectoryFlags.Recursive) !== 0;
return host.watchDirectory(directory, cb, recursive);
}
export function addDirectoryWatcherWithLogging(host: System, directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags, log: (s: string) => void): FileWatcher {
const watcherCaption = `DirectoryWatcher ${(flags & WatchDirectoryFlags.Recursive) !== 0 ? "recursive" : ""}:: `;
return createWatcherWithLogging(addDirectoryWatcher, watcherCaption, log, host, directory, cb, flags);
}
type WatchCallback<T, U> = (fileName: string, cbOptional1?: T, optional?: U) => void;
type AddWatch<T, U> = (host: System, file: string, cb: WatchCallback<T, U>, optional?: U) => FileWatcher;
function createWatcherWithLogging<T, U>(addWatch: AddWatch<T, U>, watcherCaption: string, log: (s: string) => void, host: System, file: string, cb: WatchCallback<T, U>, optional?: U): FileWatcher {
const info = `PathInfo: ${file}`;
log(`${watcherCaption}Added: ${info}`);
const watcher = addWatch(host, file, (fileName, cbOptional1?) => {
const optionalInfo = cbOptional1 !== undefined ? ` ${cbOptional1}` : "";
log(`${watcherCaption}Trigger: ${fileName}${optionalInfo} ${info}`);
const start = timestamp();
cb(fileName, cbOptional1, optional);
const elapsed = timestamp() - start;
log(`${watcherCaption}Elapsed: ${elapsed}ms Trigger: ${fileName}${optionalInfo} ${info}`);
}, optional);
return {
close: () => {
log(`${watcherCaption}Close: ${info}`);
watcher.close();
}
};
}
export function closeFileWatcher(watcher: FileWatcher) {
watcher.close();
}
export function closeFileWatcherOf<T extends { watcher: FileWatcher; }>(objWithWatcher: T) {
objWithWatcher.watcher.close();
}
/** Calls `callback` on `directory` and every ancestor directory it has, returning the first defined result. */
export function forEachAncestorDirectory<T>(directory: string, callback: (directory: string) => T): T {
while (true) {
+22 -57
View File
@@ -245,7 +245,7 @@ namespace ts {
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 hasChangedCompilerOptions = false; // True if the compiler options have changed between compilations
let changedAutomaticTypeDirectiveNames = false; // True if the automatic type directives have changed
let hasChangedAutomaticTypeDirectiveNames = false; // True if the automatic type directives have changed
const loggingEnabled = compilerOptions.diagnostics || compilerOptions.extendedDiagnostics;
const writeLog: (s: string) => void = loggingEnabled ? s => system.write(s) : noop;
@@ -269,26 +269,25 @@ namespace ts {
const compilerHost: CompilerHost & ResolutionCacheHost = {
// Members for CompilerHost
getSourceFile: getVersionedSourceFile,
getSourceFile: (fileName, languageVersion, onError?, shouldCreateNewSourceFile?) => getVersionedSourceFileByPath(fileName, toPath(fileName), languageVersion, onError, shouldCreateNewSourceFile),
getSourceFileByPath: getVersionedSourceFileByPath,
getDefaultLibLocation,
getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)),
writeFile: (_fileName, _data, _writeByteOrderMark, _onError?, _sourceFiles?) => { },
writeFile: notImplemented,
getCurrentDirectory,
useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames,
getCanonicalFileName,
getNewLine: () => newLine,
fileExists,
readFile,
trace,
directoryExists,
readFile: fileName => system.readFile(fileName),
trace: s => system.write(s + newLine),
directoryExists: directoryName => directoryStructureHost.directoryExists(directoryName),
getEnvironmentVariable: name => system.getEnvironmentVariable ? system.getEnvironmentVariable(name) : "",
getDirectories,
getDirectories: path => directoryStructureHost.getDirectories(path),
realpath,
resolveTypeReferenceDirectives,
resolveModuleNames,
resolveTypeReferenceDirectives: (typeDirectiveNames, containingFile) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile),
resolveModuleNames: (moduleNames, containingFile, reusedNames?) => resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames, /*logChanges*/ false),
onReleaseOldSourceFile,
hasChangedAutomaticTypeDirectiveNames,
// Members for ResolutionCacheHost
toPath,
getCompilationSettings: () => compilerOptions,
@@ -296,12 +295,14 @@ namespace ts {
watchTypeRootsDirectory: watchDirectory,
getCachedDirectoryStructureHost,
onInvalidatedResolution: scheduleProgramUpdate,
onChangedAutomaticTypeDirectiveNames,
onChangedAutomaticTypeDirectiveNames: () => {
hasChangedAutomaticTypeDirectiveNames = true;
scheduleProgramUpdate();
},
writeLog
};
// Cache for the module resolution
const resolutionCache = createResolutionCache(compilerHost);
resolutionCache.setRootDirectory(configFileName ?
const resolutionCache = createResolutionCache(compilerHost, configFileName ?
getDirectoryPath(getNormalizedAbsolutePath(configFileName, getCurrentDirectory())) :
getCurrentDirectory()
);
@@ -337,6 +338,7 @@ namespace ts {
// Compile the program
resolutionCache.startCachingPerDirectoryResolution();
compilerHost.hasInvalidatedResolution = hasInvalidatedResolution;
compilerHost.hasChangedAutomaticTypeDirectiveNames = hasChangedAutomaticTypeDirectiveNames;
program = createProgram(rootFileNames, compilerOptions, compilerHost, program);
resolutionCache.finishCachingPerDirectoryResolution();
builder.onProgramUpdateGraph(program, hasInvalidatedResolution);
@@ -379,38 +381,10 @@ namespace ts {
return directoryStructureHost.fileExists(fileName);
}
function directoryExists(directoryName: string) {
return directoryStructureHost.directoryExists(directoryName);
}
function readFile(fileName: string) {
return system.readFile(fileName);
}
function trace(s: string) {
return system.write(s + newLine);
}
function getDirectories(path: string) {
return directoryStructureHost.getDirectories(path);
}
function resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames?: string[]) {
return resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames, /*logChanges*/ false);
}
function resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string) {
return resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile);
}
function getDefaultLibLocation(): string {
return getDirectoryPath(normalizePath(system.getExecutingFilePath()));
}
function getVersionedSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile {
return getVersionedSourceFileByPath(fileName, toPath(fileName), languageVersion, onError, shouldCreateNewSourceFile);
}
function getVersionedSourceFileByPath(fileName: string, path: Path, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile {
const hostSourceFile = sourceFilesCache.get(path);
// No source file on the host
@@ -594,15 +568,6 @@ namespace ts {
return watchDirectoryWorker(system, directory, cb, flags, writeLog);
}
function onChangedAutomaticTypeDirectiveNames() {
changedAutomaticTypeDirectiveNames = true;
scheduleProgramUpdate();
}
function hasChangedAutomaticTypeDirectiveNames() {
return changedAutomaticTypeDirectiveNames;
}
function watchMissingFilePath(missingFilePath: Path) {
return watchFilePath(system, missingFilePath, onMissingFileChange, missingFilePath, writeLog);
}
@@ -633,19 +598,19 @@ namespace ts {
function watchWildcardDirectory(directory: string, flags: WatchDirectoryFlags) {
return watchDirectory(
directory,
fileOrFolder => {
fileOrDirectory => {
Debug.assert(!!configFileName);
const fileOrFolderPath = toPath(fileOrFolder);
const fileOrDirectoryPath = toPath(fileOrDirectory);
// Since the file existance changed, update the sourceFiles cache
(directoryStructureHost as CachedDirectoryStructureHost).addOrDeleteFileOrFolder(fileOrFolder, fileOrFolderPath);
removeSourceFile(fileOrFolderPath);
(directoryStructureHost as CachedDirectoryStructureHost).addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
removeSourceFile(fileOrDirectoryPath);
// If the the added or created file or folder is not supported file name, ignore the file
// If the the added or created file or directory is not supported file name, ignore the file
// But when watched directory is added/removed, we need to reload the file list
if (fileOrFolderPath !== directory && !isSupportedSourceFileName(fileOrFolder, compilerOptions)) {
writeLog(`Project: ${configFileName} Detected file add/remove of non supported extension: ${fileOrFolder}`);
if (fileOrDirectoryPath !== directory && !isSupportedSourceFileName(fileOrDirectory, compilerOptions)) {
writeLog(`Project: ${configFileName} Detected file add/remove of non supported extension: ${fileOrDirectory}`);
return;
}
+136
View File
@@ -0,0 +1,136 @@
/// <reference path="core.ts" />
namespace ts {
/**
* Updates the existing missing file watches with the new set of missing files after new program is created
*/
export function updateMissingFilePathsWatch(
program: Program,
missingFileWatches: Map<FileWatcher>,
createMissingFileWatch: (missingFilePath: Path) => FileWatcher,
) {
const missingFilePaths = program.getMissingFilePaths();
const newMissingFilePathMap = arrayToSet(missingFilePaths);
// Update the missing file paths watcher
mutateMap(
missingFileWatches,
newMissingFilePathMap,
{
// Watch the missing files
createNewValue: createMissingFileWatch,
// Files that are no longer missing (e.g. because they are no longer required)
// should no longer be watched.
onDeleteValue: closeFileWatcher
}
);
}
export interface WildcardDirectoryWatcher {
watcher: FileWatcher;
flags: WatchDirectoryFlags;
}
/**
* Updates the existing wild card directory watches with the new set of wild card directories from the config file
* after new program is created because the config file was reloaded or program was created first time from the config file
* Note that there is no need to call this function when the program is updated with additional files without reloading config files,
* as wildcard directories wont change unless reloading config file
*/
export function updateWatchingWildcardDirectories(
existingWatchedForWildcards: Map<WildcardDirectoryWatcher>,
wildcardDirectories: Map<WatchDirectoryFlags>,
watchDirectory: (directory: string, flags: WatchDirectoryFlags) => FileWatcher
) {
mutateMap(
existingWatchedForWildcards,
wildcardDirectories,
{
// Create new watch and recursive info
createNewValue: createWildcardDirectoryWatcher,
// Close existing watch thats not needed any more
onDeleteValue: closeFileWatcherOf,
// Close existing watch that doesnt match in the flags
onExistingValue: updateWildcardDirectoryWatcher
}
);
function createWildcardDirectoryWatcher(directory: string, flags: WatchDirectoryFlags): WildcardDirectoryWatcher {
// Create new watch and recursive info
return {
watcher: watchDirectory(directory, flags),
flags
};
}
function updateWildcardDirectoryWatcher(existingWatcher: WildcardDirectoryWatcher, flags: WatchDirectoryFlags, directory: string) {
// Watcher needs to be updated if the recursive flags dont match
if (existingWatcher.flags === flags) {
return;
}
existingWatcher.watcher.close();
existingWatchedForWildcards.set(directory, createWildcardDirectoryWatcher(directory, flags));
}
}
}
/* @internal */
namespace ts {
export function addFileWatcher(host: System, file: string, cb: FileWatcherCallback): FileWatcher {
return host.watchFile(file, cb);
}
export function addFileWatcherWithLogging(host: System, file: string, cb: FileWatcherCallback, log: (s: string) => void): FileWatcher {
const watcherCaption = `FileWatcher:: `;
return createWatcherWithLogging(addFileWatcher, watcherCaption, log, host, file, cb);
}
export type FilePathWatcherCallback = (fileName: string, eventKind: FileWatcherEventKind, filePath: Path) => void;
export function addFilePathWatcher(host: System, file: string, cb: FilePathWatcherCallback, path: Path): FileWatcher {
return host.watchFile(file, (fileName, eventKind) => cb(fileName, eventKind, path));
}
export function addFilePathWatcherWithLogging(host: System, file: string, cb: FilePathWatcherCallback, path: Path, log: (s: string) => void): FileWatcher {
const watcherCaption = `FileWatcher:: `;
return createWatcherWithLogging(addFileWatcher, watcherCaption, log, host, file, cb, path);
}
export function addDirectoryWatcher(host: System, directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags): FileWatcher {
const recursive = (flags & WatchDirectoryFlags.Recursive) !== 0;
return host.watchDirectory(directory, cb, recursive);
}
export function addDirectoryWatcherWithLogging(host: System, directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags, log: (s: string) => void): FileWatcher {
const watcherCaption = `DirectoryWatcher ${(flags & WatchDirectoryFlags.Recursive) !== 0 ? "recursive" : ""}:: `;
return createWatcherWithLogging(addDirectoryWatcher, watcherCaption, log, host, directory, cb, flags);
}
type WatchCallback<T, U> = (fileName: string, cbOptional1?: T, optional?: U) => void;
type AddWatch<T, U> = (host: System, file: string, cb: WatchCallback<T, U>, optional?: U) => FileWatcher;
function createWatcherWithLogging<T, U>(addWatch: AddWatch<T, U>, watcherCaption: string, log: (s: string) => void, host: System, file: string, cb: WatchCallback<T, U>, optional?: U): FileWatcher {
const info = `PathInfo: ${file}`;
log(`${watcherCaption}Added: ${info}`);
const watcher = addWatch(host, file, (fileName, cbOptional1?) => {
const optionalInfo = cbOptional1 !== undefined ? ` ${cbOptional1}` : "";
log(`${watcherCaption}Trigger: ${fileName}${optionalInfo} ${info}`);
const start = timestamp();
cb(fileName, cbOptional1, optional);
const elapsed = timestamp() - start;
log(`${watcherCaption}Elapsed: ${elapsed}ms Trigger: ${fileName}${optionalInfo} ${info}`);
}, optional);
return {
close: () => {
log(`${watcherCaption}Close: ${info}`);
watcher.close();
}
};
}
export function closeFileWatcher(watcher: FileWatcher) {
watcher.close();
}
export function closeFileWatcherOf<T extends { watcher: FileWatcher; }>(objWithWatcher: T) {
objWithWatcher.watcher.close();
}
}