PR feedback

This commit is contained in:
Sheetal Nandi 2017-09-26 13:34:56 -07:00
parent 38f3a2b700
commit 68d360585a
20 changed files with 472 additions and 431 deletions

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)) {

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));

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));
}
}
}

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;
}

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

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)) {

View File

@ -36,6 +36,7 @@
"sourcemap.ts",
"declarationEmitter.ts",
"emitter.ts",
"watchUtilities.ts",
"program.ts",
"builder.ts",
"resolutionCache.ts",

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 */

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) {

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;
}

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();
}
}

View File

@ -885,7 +885,7 @@ namespace ts {
program, newRootFileNames, newOptions,
path => program.getSourceFileByPath(path).version, /*fileExists*/ returnFalse,
/*hasInvalidatedResolution*/ returnFalse,
/*hasChangedAutomaticTypeDirectiveNames*/ returnFalse
/*hasChangedAutomaticTypeDirectiveNames*/ false
);
assert.isTrue(actual);
}

View File

@ -967,7 +967,7 @@ namespace ts.projectSystem {
checkProjectActualFiles(projectService.inferredProjects[0], [file2.path, file3.path, libFile.path]);
});
it("should resuse same project if file is opened from the configured project that has no open files", () => {
it("should reuse same project if file is opened from the configured project that has no open files", () => {
const file1 = {
path: "/a/b/main.ts",
content: "let x =1;"
@ -4639,7 +4639,7 @@ namespace ts.projectSystem {
verifyProjectAndWatchedDirectories();
callsTrackingHost.verifyNoHostCalls();
function getFilePathIfOpen(f: FileOrFolder) {
function getFilePathIfNotOpen(f: FileOrFolder) {
const path = toCanonical(f.path);
const info = projectService.getScriptInfoForPath(toCanonical(f.path));
return info && info.isScriptOpen() ? undefined : path;
@ -4647,7 +4647,7 @@ namespace ts.projectSystem {
function verifyProjectAndWatchedDirectories() {
checkProjectActualFiles(project, map(projectFiles, f => f.path));
checkWatchedFiles(host, mapDefined(projectFiles, getFilePathIfOpen));
checkWatchedFiles(host, mapDefined(projectFiles, getFilePathIfNotOpen));
checkWatchedDirectories(host, watchingRecursiveDirectories, /*recursive*/ true);
checkWatchedDirectories(host, [], /*recursive*/ false);
}

View File

@ -246,17 +246,17 @@ namespace ts.TestFSWithWatch {
const mapNewLeaves = createMap<true>();
const isNewFs = this.fs.size === 0;
// always inject safelist file in the list of files
for (const fileOrFolder of fileOrFolderList.concat(this.withSafeList ? safeList : [])) {
const path = this.toFullPath(fileOrFolder.path);
for (const fileOrDirectory of fileOrFolderList.concat(this.withSafeList ? safeList : [])) {
const path = this.toFullPath(fileOrDirectory.path);
mapNewLeaves.set(path, true);
// If its a change
const currentEntry = this.fs.get(path);
if (currentEntry) {
if (isFile(currentEntry)) {
if (isString(fileOrFolder.content)) {
if (isString(fileOrDirectory.content)) {
// Update file
if (currentEntry.content !== fileOrFolder.content) {
currentEntry.content = fileOrFolder.content;
if (currentEntry.content !== fileOrDirectory.content) {
currentEntry.content = fileOrDirectory.content;
this.invokeFileWatcher(currentEntry.fullPath, FileWatcherEventKind.Changed);
}
}
@ -266,7 +266,7 @@ namespace ts.TestFSWithWatch {
}
else {
// Folder
if (isString(fileOrFolder.content)) {
if (isString(fileOrDirectory.content)) {
// TODO: Changing from folder => file
}
else {
@ -275,32 +275,32 @@ namespace ts.TestFSWithWatch {
}
}
else {
this.ensureFileOrFolder(fileOrFolder);
this.ensureFileOrFolder(fileOrDirectory);
}
}
if (!isNewFs) {
this.fs.forEach((fileOrFolder, path) => {
this.fs.forEach((fileOrDirectory, path) => {
// If this entry is not from the new file or folder
if (!mapNewLeaves.get(path)) {
// Leaf entries that arent in new list => remove these
if (isFile(fileOrFolder) || isFolder(fileOrFolder) && fileOrFolder.entries.length === 0) {
this.removeFileOrFolder(fileOrFolder, folder => !mapNewLeaves.get(folder.path));
if (isFile(fileOrDirectory) || isFolder(fileOrDirectory) && fileOrDirectory.entries.length === 0) {
this.removeFileOrFolder(fileOrDirectory, folder => !mapNewLeaves.get(folder.path));
}
}
});
}
}
ensureFileOrFolder(fileOrFolder: FileOrFolder) {
if (isString(fileOrFolder.content)) {
const file = this.toFile(fileOrFolder);
ensureFileOrFolder(fileOrDirectory: FileOrFolder) {
if (isString(fileOrDirectory.content)) {
const file = this.toFile(fileOrDirectory);
Debug.assert(!this.fs.get(file.path));
const baseFolder = this.ensureFolder(getDirectoryPath(file.fullPath));
this.addFileOrFolderInFolder(baseFolder, file);
}
else {
const fullPath = getNormalizedAbsolutePath(fileOrFolder.path, this.currentDirectory);
const fullPath = getNormalizedAbsolutePath(fileOrDirectory.path, this.currentDirectory);
this.ensureFolder(fullPath);
}
}
@ -326,44 +326,44 @@ namespace ts.TestFSWithWatch {
return folder;
}
private addFileOrFolderInFolder(folder: Folder, fileOrFolder: File | Folder) {
folder.entries.push(fileOrFolder);
this.fs.set(fileOrFolder.path, fileOrFolder);
private addFileOrFolderInFolder(folder: Folder, fileOrDirectory: File | Folder) {
folder.entries.push(fileOrDirectory);
this.fs.set(fileOrDirectory.path, fileOrDirectory);
if (isFile(fileOrFolder)) {
this.invokeFileWatcher(fileOrFolder.fullPath, FileWatcherEventKind.Created);
if (isFile(fileOrDirectory)) {
this.invokeFileWatcher(fileOrDirectory.fullPath, FileWatcherEventKind.Created);
}
this.invokeDirectoryWatcher(folder.fullPath, fileOrFolder.fullPath);
this.invokeDirectoryWatcher(folder.fullPath, fileOrDirectory.fullPath);
}
private removeFileOrFolder(fileOrFolder: File | Folder, isRemovableLeafFolder: (folder: Folder) => boolean) {
const basePath = getDirectoryPath(fileOrFolder.path);
private removeFileOrFolder(fileOrDirectory: File | Folder, isRemovableLeafFolder: (folder: Folder) => boolean) {
const basePath = getDirectoryPath(fileOrDirectory.path);
const baseFolder = this.fs.get(basePath) as Folder;
if (basePath !== fileOrFolder.path) {
if (basePath !== fileOrDirectory.path) {
Debug.assert(!!baseFolder);
filterMutate(baseFolder.entries, entry => entry !== fileOrFolder);
filterMutate(baseFolder.entries, entry => entry !== fileOrDirectory);
}
this.fs.delete(fileOrFolder.path);
this.fs.delete(fileOrDirectory.path);
if (isFile(fileOrFolder)) {
this.invokeFileWatcher(fileOrFolder.fullPath, FileWatcherEventKind.Deleted);
if (isFile(fileOrDirectory)) {
this.invokeFileWatcher(fileOrDirectory.fullPath, FileWatcherEventKind.Deleted);
}
else {
Debug.assert(fileOrFolder.entries.length === 0);
const relativePath = this.getRelativePathToDirectory(fileOrFolder.fullPath, fileOrFolder.fullPath);
Debug.assert(fileOrDirectory.entries.length === 0);
const relativePath = this.getRelativePathToDirectory(fileOrDirectory.fullPath, fileOrDirectory.fullPath);
// Invoke directory and recursive directory watcher for the folder
// Here we arent invoking recursive directory watchers for the base folders
// since that is something we would want to do for both file as well as folder we are deleting
invokeWatcherCallbacks(this.watchedDirectories.get(fileOrFolder.path), cb => this.directoryCallback(cb, relativePath));
invokeWatcherCallbacks(this.watchedDirectoriesRecursive.get(fileOrFolder.path), cb => this.directoryCallback(cb, relativePath));
invokeWatcherCallbacks(this.watchedDirectories.get(fileOrDirectory.path), cb => this.directoryCallback(cb, relativePath));
invokeWatcherCallbacks(this.watchedDirectoriesRecursive.get(fileOrDirectory.path), cb => this.directoryCallback(cb, relativePath));
}
if (basePath !== fileOrFolder.path) {
if (basePath !== fileOrDirectory.path) {
if (baseFolder.entries.length === 0 && isRemovableLeafFolder(baseFolder)) {
this.removeFileOrFolder(baseFolder, isRemovableLeafFolder);
}
else {
this.invokeRecursiveDirectoryWatcher(baseFolder.fullPath, fileOrFolder.fullPath);
this.invokeRecursiveDirectoryWatcher(baseFolder.fullPath, fileOrDirectory.fullPath);
}
}
}
@ -402,13 +402,13 @@ namespace ts.TestFSWithWatch {
}
}
private toFile(fileOrFolder: FileOrFolder): File {
const fullPath = getNormalizedAbsolutePath(fileOrFolder.path, this.currentDirectory);
private toFile(fileOrDirectory: FileOrFolder): File {
const fullPath = getNormalizedAbsolutePath(fileOrDirectory.path, this.currentDirectory);
return {
path: this.toPath(fullPath),
content: fileOrFolder.content,
content: fileOrDirectory.content,
fullPath,
fileSize: fileOrFolder.fileSize
fileSize: fileOrDirectory.fileSize
};
}
@ -477,7 +477,7 @@ namespace ts.TestFSWithWatch {
});
}
watchDirectory(directoryName: string, cb: DirectoryWatcherCallback, recursive: boolean): DirectoryWatcher {
watchDirectory(directoryName: string, cb: DirectoryWatcherCallback, recursive: boolean): FileWatcher {
const path = this.toFullPath(directoryName);
const map = recursive ? this.watchedDirectoriesRecursive : this.watchedDirectories;
const callback: TestDirectoryWatcher = {
@ -486,8 +486,6 @@ namespace ts.TestFSWithWatch {
};
map.add(path, callback);
return {
referenceCount: 0,
directoryName,
close: () => map.remove(path, callback)
};
}

View File

@ -349,7 +349,7 @@ namespace ts.server {
private readonly projectToSizeMap: Map<number> = createMap<number>();
/**
* This is a map of config file paths existance that doesnt need query to disk
* - The entry can be present because there is inferred project that needs to watch addition of config file to folder
* - The entry can be present because there is inferred project that needs to watch addition of config file to directory
* In this case the exists could be true/false based on config file is present or not
* - Or it is present if we have configured project open with config file at that location
* In this case the exists property is always true
@ -753,15 +753,15 @@ namespace ts.server {
return this.watchDirectory(
this.host,
directory,
fileOrFolder => {
const fileOrFolderPath = this.toPath(fileOrFolder);
project.getCachedDirectoryStructureHost().addOrDeleteFileOrFolder(fileOrFolder, fileOrFolderPath);
fileOrDirectory => {
const fileOrDirectoryPath = this.toPath(fileOrDirectory);
project.getCachedDirectoryStructureHost().addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
const configFilename = project.getConfigFilePath();
// 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, project.getCompilationSettings(), this.hostConfiguration.extraFileExtensions)) {
this.logger.info(`Project: ${configFilename} Detected file add/remove of non supported extension: ${fileOrFolder}`);
if (fileOrDirectoryPath !== directory && !isSupportedSourceFileName(fileOrDirectory, project.getCompilationSettings(), this.hostConfiguration.extraFileExtensions)) {
this.logger.info(`Project: ${configFilename} Detected file add/remove of non supported extension: ${fileOrDirectory}`);
return;
}
@ -833,7 +833,7 @@ namespace ts.server {
switch (project.projectKind) {
case ProjectKind.External:
unorderedRemoveItem(this.externalProjects, <ExternalProject>project);
this.projectToSizeMap.delete((project as ExternalProject).externalProjectName);
this.projectToSizeMap.delete(project.getProjectName());
break;
case ProjectKind.Configured:
this.configuredProjects.delete((<ConfiguredProject>project).canonicalConfigFilePath);
@ -852,7 +852,7 @@ namespace ts.server {
const project = this.getOrCreateInferredProjectForProjectRootPathIfEnabled(info, projectRootPath) ||
this.getOrCreateSingleInferredProjectIfEnabled() ||
this.createInferredProject();
this.createInferredProject(getDirectoryPath(info.path));
project.addRoot(info);
project.updateGraph();
@ -1374,7 +1374,7 @@ namespace ts.server {
this.addFilesToNonInferredProjectAndUpdateGraph(project, files, externalFilePropertyReader, typeAcquisition);
this.externalProjects.push(project);
this.sendProjectTelemetry(project.externalProjectName, project);
this.sendProjectTelemetry(projectFileName, project);
return project;
}
@ -1461,7 +1461,7 @@ namespace ts.server {
this.addFilesToNonInferredProjectAndUpdateGraph(project, filesToAdd, fileNamePropertyReader, projectOptions.typeAcquisition);
this.configuredProjects.set(project.canonicalConfigFilePath, project);
this.setConfigFileExistenceByNewConfiguredProject(project);
this.sendProjectTelemetry(project.getConfigFilePath(), project, projectOptions);
this.sendProjectTelemetry(configFileName, project, projectOptions);
return project;
}
@ -1584,7 +1584,7 @@ namespace ts.server {
return project;
}
}
return this.createInferredProject(/*isSingleInferredProject*/ false, projectRootPath);
return this.createInferredProject(projectRootPath, /*isSingleInferredProject*/ false, projectRootPath);
}
// we don't have an explicit root path, so we should try to find an inferred project
@ -1621,12 +1621,12 @@ namespace ts.server {
return this.inferredProjects[0];
}
return this.createInferredProject(/*isSingleInferredProject*/ true);
return this.createInferredProject(/*rootDirectoryForResolution*/ undefined, /*isSingleInferredProject*/ true);
}
private createInferredProject(isSingleInferredProject?: boolean, projectRootPath?: string): InferredProject {
private createInferredProject(rootDirectoryForResolution: string | undefined, isSingleInferredProject?: boolean, projectRootPath?: string): InferredProject {
const compilerOptions = projectRootPath && this.compilerOptionsForInferredProjectsPerProjectRoot.get(projectRootPath) || this.compilerOptionsForInferredProjects;
const project = new InferredProject(this, this.documentRegistry, compilerOptions, projectRootPath);
const project = new InferredProject(this, this.documentRegistry, compilerOptions, projectRootPath, rootDirectoryForResolution);
if (isSingleInferredProject) {
this.inferredProjects.unshift(project);
}
@ -1669,10 +1669,12 @@ namespace ts.server {
}
}
/*@internal*/
getOrCreateScriptInfoNotOpenedByClientForNormalizedPath(fileName: NormalizedPath, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: DirectoryStructureHost) {
return this.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ false, /*fileContent*/ undefined, scriptKind, hasMixedContent, hostToQueryFileExistsOn);
}
/*@internal*/
getOrCreateScriptInfoOpenedByClientForNormalizedPath(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: DirectoryStructureHost) {
return this.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ true, fileContent, scriptKind, hasMixedContent, hostToQueryFileExistsOn);
}

View File

@ -162,7 +162,8 @@ namespace ts.server {
*/
private projectStateVersion = 0;
private changedAutomaticTypeDirectiveNames = false;
/*@internal*/
hasChangedAutomaticTypeDirectiveNames = false;
private typingFiles: SortedReadonlyArray<string>;
@ -201,7 +202,8 @@ namespace ts.server {
languageServiceEnabled: boolean,
private compilerOptions: CompilerOptions,
public compileOnSaveEnabled: boolean,
/*@internal*/public directoryStructureHost: DirectoryStructureHost) {
/*@internal*/public directoryStructureHost: DirectoryStructureHost,
rootDirectoryForResolution: string | undefined) {
if (!this.compilerOptions) {
this.compilerOptions = getDefaultCompilerOptions();
@ -224,7 +226,7 @@ namespace ts.server {
}
this.languageService = createLanguageService(this, this.documentRegistry);
this.resolutionCache = createResolutionCache(this);
this.resolutionCache = createResolutionCache(this, rootDirectoryForResolution);
if (!languageServiceEnabled) {
this.disableLanguageService();
}
@ -244,29 +246,26 @@ namespace ts.server {
}
getScriptFileNames() {
const result: string[] = [];
if (this.rootFiles) {
this.rootFilesMap.forEach((value, _path) => {
const f: ScriptInfo = isScriptInfo(value) && value;
if (this.languageServiceEnabled || (f && f.isScriptOpen())) {
// if language service is disabled - process only files that are open
result.push(f ? f.fileName : value as NormalizedPath);
}
});
if (this.typingFiles) {
for (const f of this.typingFiles) {
result.push(f);
}
}
if (!this.rootFiles) {
return ts.emptyArray;
}
return result;
let result: string[] | undefined;
this.rootFilesMap.forEach(value => {
if (this.languageServiceEnabled || (isScriptInfo(value) && value.isScriptOpen())) {
// if language service is disabled - process only files that are open
(result || (result = [])).push(isScriptInfo(value) ? value.fileName : value);
}
});
return addRange(result, this.typingFiles) || ts.emptyArray;
}
private getScriptInfoLSHost(fileName: string) {
private getOrCreateScriptInfoAndAttachToProject(fileName: string) {
const scriptInfo = this.projectService.getOrCreateScriptInfoNotOpenedByClient(fileName, this.directoryStructureHost);
if (scriptInfo) {
const existingValue = this.rootFilesMap.get(scriptInfo.path);
if (existingValue !== undefined && existingValue !== scriptInfo) {
if (existingValue !== scriptInfo && existingValue !== undefined) {
// This was missing path earlier but now the file exists. Update the root
this.rootFiles.push(scriptInfo);
this.rootFilesMap.set(scriptInfo.path, scriptInfo);
@ -277,17 +276,17 @@ namespace ts.server {
}
getScriptKind(fileName: string) {
const info = this.getScriptInfoLSHost(fileName);
const info = this.getOrCreateScriptInfoAndAttachToProject(fileName);
return info && info.scriptKind;
}
getScriptVersion(filename: string) {
const info = this.getScriptInfoLSHost(filename);
const info = this.getOrCreateScriptInfoAndAttachToProject(filename);
return info && info.getLatestVersion();
}
getScriptSnapshot(filename: string): IScriptSnapshot {
const scriptInfo = this.getScriptInfoLSHost(filename);
const scriptInfo = this.getOrCreateScriptInfoAndAttachToProject(filename);
if (scriptInfo) {
return scriptInfo.getSnapshot();
}
@ -377,15 +376,10 @@ namespace ts.server {
/*@internal*/
onChangedAutomaticTypeDirectiveNames() {
this.changedAutomaticTypeDirectiveNames = true;
this.hasChangedAutomaticTypeDirectiveNames = true;
this.projectService.delayUpdateProjectGraphAndInferredProjectsRefresh(this);
}
/*@internal*/
hasChangedAutomaticTypeDirectiveNames() {
return this.changedAutomaticTypeDirectiveNames;
}
/*@internal*/
getGlobalCache() {
return this.getTypeAcquisition().enable ? this.projectService.typingsInstaller.globalTypingsCacheLocation : undefined;
@ -802,7 +796,7 @@ namespace ts.server {
// - oldProgram is not set - this is a first time updateGraph is called
// - newProgram is different from the old program and structure of the old program was not reused.
const hasChanges = !oldProgram || (this.program !== oldProgram && !(oldProgram.structureIsReused & StructureIsReused.Completely));
this.changedAutomaticTypeDirectiveNames = false;
this.hasChangedAutomaticTypeDirectiveNames = false;
if (hasChanges) {
if (oldProgram) {
for (const f of oldProgram.getSourceFiles()) {
@ -1035,7 +1029,12 @@ namespace ts.server {
super.setCompilerOptions(newOptions);
}
constructor(projectService: ProjectService, documentRegistry: DocumentRegistry, compilerOptions: CompilerOptions, public readonly projectRootPath?: string | undefined) {
constructor(
projectService: ProjectService,
documentRegistry: DocumentRegistry,
compilerOptions: CompilerOptions,
public readonly projectRootPath: string | undefined,
rootDirectoryForResolution: string | undefined) {
super(InferredProject.newName(),
ProjectKind.Inferred,
projectService,
@ -1044,7 +1043,8 @@ namespace ts.server {
/*languageServiceEnabled*/ true,
compilerOptions,
/*compileOnSaveEnabled*/ false,
projectService.host);
projectService.host,
rootDirectoryForResolution);
}
addRoot(info: ScriptInfo) {
@ -1053,13 +1053,6 @@ namespace ts.server {
this.toggleJsInferredProject(/*isJsInferredProject*/ true);
}
super.addRoot(info);
// For first root set the resolution cache root dir
if (this.getRootFiles.length === 1) {
const rootDirPath = this.getProjectRootPath();
if (rootDirPath) {
this.resolutionCache.setRootDirectory(rootDirPath);
}
}
}
removeRoot(info: ScriptInfo) {
@ -1081,11 +1074,9 @@ namespace ts.server {
}
getProjectRootPath() {
// Single inferred project does not have a project root.
if (this.projectService.useSingleInferredProject) {
return undefined;
}
return this.projectRootPath || getDirectoryPath(this.getRootFiles()[0]);
return this.projectRootPath ||
// Single inferred project does not have a project root.
!this.projectService.useSingleInferredProject && getDirectoryPath(this.getRootFiles()[0]);
}
close() {
@ -1135,10 +1126,18 @@ namespace ts.server {
languageServiceEnabled: boolean,
public compileOnSaveEnabled: boolean,
cachedDirectoryStructureHost: CachedDirectoryStructureHost) {
super(configFileName, ProjectKind.Configured, projectService, documentRegistry, hasExplicitListOfFiles, languageServiceEnabled, compilerOptions, compileOnSaveEnabled, cachedDirectoryStructureHost);
super(configFileName,
ProjectKind.Configured,
projectService,
documentRegistry,
hasExplicitListOfFiles,
languageServiceEnabled,
compilerOptions,
compileOnSaveEnabled,
cachedDirectoryStructureHost,
getDirectoryPath(configFileName));
this.canonicalConfigFilePath = asNormalizedPath(projectService.toCanonicalFileName(configFileName));
this.enablePlugins();
this.resolutionCache.setRootDirectory(getDirectoryPath(configFileName));
}
/**
@ -1370,8 +1369,15 @@ namespace ts.server {
languageServiceEnabled: boolean,
public compileOnSaveEnabled: boolean,
private readonly projectFilePath?: string) {
super(externalProjectName, ProjectKind.External, projectService, documentRegistry, /*hasExplicitListOfFiles*/ true, languageServiceEnabled, compilerOptions, compileOnSaveEnabled, projectService.host);
this.resolutionCache.setRootDirectory(this.getProjectRootPath());
super(externalProjectName,
ProjectKind.External,
projectService,
documentRegistry,
/*hasExplicitListOfFiles*/ true,
languageServiceEnabled, compilerOptions,
compileOnSaveEnabled,
projectService.host,
getDirectoryPath(projectFilePath || normalizeSlashes(externalProjectName)));
}
getExcludedFiles() {

View File

@ -4,17 +4,36 @@ namespace ts.server {
/* @internal */
export class TextStorage {
// Generated only on demand and removes the text if any edits
/**
* Generated only on demand (based on edits, or information requested)
* The property text is set to undefined when edits happen on the cache
*/
private svc: ScriptVersionCache | undefined;
private svcVersion = 0;
// Store text when there is no svc or svc has no change, on edit, remove the text
/**
* Stores the text when there are no changes to the script version cache
* The script version cache is generated on demand and text is still retained.
* Only on edits to the script version cache, the text will be set to undefined
*/
private text: string;
/**
* Line map for the text when there is no script version cache present
*/
private lineMap: number[];
private textVersion = 0;
/**
* True if the text is for the file thats open in the editor
*/
public isOpen: boolean;
/**
* True if the text present is the text from the file on the disk
*/
private ownFileText: boolean;
/**
* True when reloading contents of file from the disk is pending
*/
private pendingReloadFromDisk: boolean;
constructor(private readonly host: ServerHost, private readonly fileName: NormalizedPath) {

View File

@ -207,7 +207,7 @@ namespace ts.JsTyping {
}
// depth of 2, so we access `node_modules/foo` but not `node_modules/foo/bar`
const fileNames = host.readDirectory(packagesFolderPath, [".json"], /*excludes*/ undefined, /*includes*/ undefined, /*depth*/ 2);
const fileNames = host.readDirectory(packagesFolderPath, [Extension.Json], /*excludes*/ undefined, /*includes*/ undefined, /*depth*/ 2);
if (log) log(`Searching for typing names in ${packagesFolderPath}; all files: ${JSON.stringify(fileNames)}`);
const packageNames: string[] = [];
for (const fileName of fileNames) {

View File

@ -1101,13 +1101,11 @@ namespace ts {
}
function synchronizeHostData(): void {
const hasChangedAutomaticTypeDirectiveNames = host.hasChangedAutomaticTypeDirectiveNames && host.hasChangedAutomaticTypeDirectiveNames.bind(host) || returnFalse;
// perform fast check if host supports it
if (host.getProjectVersion) {
const hostProjectVersion = host.getProjectVersion();
if (hostProjectVersion) {
if (lastProjectVersion === hostProjectVersion && !hasChangedAutomaticTypeDirectiveNames()) {
if (lastProjectVersion === hostProjectVersion && !host.hasChangedAutomaticTypeDirectiveNames) {
return;
}
@ -1129,7 +1127,7 @@ namespace ts {
const hasInvalidatedResolution: HasInvalidatedResolution = host.hasInvalidatedResolution || returnFalse;
// If the program is already up-to-date, we can reuse it
if (isProgramUptoDate(program, rootFileNames, hostCache.compilationSettings(), path => hostCache.getVersion(path), fileExists, hasInvalidatedResolution, hasChangedAutomaticTypeDirectiveNames)) {
if (isProgramUptoDate(program, rootFileNames, hostCache.compilationSettings(), path => hostCache.getVersion(path), fileExists, hasInvalidatedResolution, host.hasChangedAutomaticTypeDirectiveNames)) {
return;
}
@ -1170,7 +1168,7 @@ namespace ts {
},
onReleaseOldSourceFile,
hasInvalidatedResolution,
hasChangedAutomaticTypeDirectiveNames
hasChangedAutomaticTypeDirectiveNames: host.hasChangedAutomaticTypeDirectiveNames
};
if (host.trace) {
compilerHost.trace = message => host.trace(message);

View File

@ -186,7 +186,7 @@ namespace ts {
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[];
resolveTypeReferenceDirectives?(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
hasInvalidatedResolution?: HasInvalidatedResolution;
hasChangedAutomaticTypeDirectiveNames?(): boolean;
hasChangedAutomaticTypeDirectiveNames?: boolean;
directoryExists?(directoryName: string): boolean;
/*
@ -278,6 +278,7 @@ namespace ts {
getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange): ApplicableRefactorInfo[];
getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string): RefactorEditInfo | undefined;
getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput;
getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean, isDetailed?: boolean): EmitOutput | EmitOutputDetailed;
getProgram(): Program;