mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-12 21:37:41 -06:00
Handle watches of missing directories and make project the module resolution host
This commit is contained in:
parent
5aafd3f06c
commit
17565d8407
@ -149,7 +149,6 @@ var harnessSources = harnessCoreSources.concat([
|
||||
"utilities.ts",
|
||||
"scriptVersionCache.ts",
|
||||
"scriptInfo.ts",
|
||||
"lsHost.ts",
|
||||
"project.ts",
|
||||
"typingsCache.ts",
|
||||
"editorServices.ts",
|
||||
|
||||
@ -5,8 +5,6 @@
|
||||
namespace ts {
|
||||
/** This is the cache of module/typedirectives resolution that can be retained across program */
|
||||
export interface ResolutionCache {
|
||||
setModuleResolutionHost(host: ModuleResolutionHost): void;
|
||||
|
||||
startRecordingFilesWithChangedResolutions(): void;
|
||||
finishRecordingFilesWithChangedResolutions(): Path[];
|
||||
|
||||
@ -14,8 +12,6 @@ namespace ts {
|
||||
resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
|
||||
|
||||
invalidateResolutionOfFile(filePath: Path): void;
|
||||
onFileAddOrRemoveInDirectoryOfFailedLookup(fileOrFolder: Path): boolean;
|
||||
|
||||
createHasInvalidatedResolution(): HasInvalidatedResolution;
|
||||
|
||||
clear(): void;
|
||||
@ -33,15 +29,17 @@ namespace ts {
|
||||
mapLocations: MultiMap<string>;
|
||||
}
|
||||
|
||||
export function createResolutionCache(
|
||||
toPath: (fileName: string) => Path,
|
||||
getCompilerOptions: () => CompilerOptions,
|
||||
watchDirectoryOfFailedLookupLocation: (directory: string) => FileWatcher,
|
||||
log: (s: string) => void,
|
||||
projectName?: string,
|
||||
getGlobalCache?: () => string | undefined): ResolutionCache {
|
||||
export interface ResolutionCacheHost extends ModuleResolutionHost {
|
||||
toPath(fileName: string): Path;
|
||||
getCompilationSettings(): CompilerOptions;
|
||||
watchDirectoryOfFailedLookupLocation(directory: string, cb: DirectoryWatcherCallback): FileWatcher;
|
||||
onInvalidatedResolution(): void;
|
||||
getCachedPartialSystem?(): CachedPartialSystem;
|
||||
projectName?: string;
|
||||
getGlobalCache?(): string | undefined;
|
||||
}
|
||||
|
||||
let host: ModuleResolutionHost;
|
||||
export function createResolutionCache(resolutionHost: ResolutionCacheHost): ResolutionCache {
|
||||
let filesWithChangedSetOfUnresolvedImports: Path[] | undefined;
|
||||
let filesWithInvalidatedResolutions: Map<true> | undefined;
|
||||
|
||||
@ -52,23 +50,16 @@ namespace ts {
|
||||
const resolvedTypeReferenceDirectives = createMap<Map<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
|
||||
|
||||
const directoryWatchesOfFailedLookups = createMap<DirectoryWatchesOfFailedLookup>();
|
||||
|
||||
return {
|
||||
setModuleResolutionHost,
|
||||
startRecordingFilesWithChangedResolutions,
|
||||
finishRecordingFilesWithChangedResolutions,
|
||||
resolveModuleNames,
|
||||
resolveTypeReferenceDirectives,
|
||||
invalidateResolutionOfFile,
|
||||
onFileAddOrRemoveInDirectoryOfFailedLookup,
|
||||
createHasInvalidatedResolution,
|
||||
clear
|
||||
};
|
||||
|
||||
function setModuleResolutionHost(updatedHost: ModuleResolutionHost) {
|
||||
host = updatedHost;
|
||||
}
|
||||
|
||||
function clear() {
|
||||
// Close all the watches for failed lookup locations, irrespective of refcounts for them since this is to clear the cache
|
||||
clearMap(directoryWatchesOfFailedLookups, closeFileWatcherOf);
|
||||
@ -95,16 +86,16 @@ namespace ts {
|
||||
function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
|
||||
const primaryResult = ts.resolveModuleName(moduleName, containingFile, compilerOptions, host);
|
||||
// return result immediately only if global cache support is not enabled or if it is .ts, .tsx or .d.ts
|
||||
if (!getGlobalCache) {
|
||||
if (!resolutionHost.getGlobalCache) {
|
||||
return primaryResult;
|
||||
}
|
||||
|
||||
// otherwise try to load typings from @types
|
||||
const globalCache = getGlobalCache();
|
||||
const globalCache = resolutionHost.getGlobalCache();
|
||||
if (globalCache !== undefined && !isExternalModuleNameRelative(moduleName) && !(primaryResult.resolvedModule && extensionIsTypeScript(primaryResult.resolvedModule.extension))) {
|
||||
// create different collection of failed lookup locations for second pass
|
||||
// if it will fail and we've already found something during the first pass - we don't want to pollute its results
|
||||
const { resolvedModule, failedLookupLocations } = loadModuleFromGlobalCache(moduleName, projectName, compilerOptions, host, globalCache);
|
||||
const { resolvedModule, failedLookupLocations } = loadModuleFromGlobalCache(moduleName, resolutionHost.projectName, compilerOptions, host, globalCache);
|
||||
if (resolvedModule) {
|
||||
return { resolvedModule, failedLookupLocations: addRange(primaryResult.failedLookupLocations as Array<string>, failedLookupLocations) };
|
||||
}
|
||||
@ -123,12 +114,12 @@ namespace ts {
|
||||
getResultFileName: (result: R) => string | undefined,
|
||||
logChanges: boolean): R[] {
|
||||
|
||||
const path = toPath(containingFile);
|
||||
const path = resolutionHost.toPath(containingFile);
|
||||
const currentResolutionsInFile = cache.get(path);
|
||||
|
||||
const newResolutions: Map<T> = createMap<T>();
|
||||
const resolvedModules: R[] = [];
|
||||
const compilerOptions = getCompilerOptions();
|
||||
const compilerOptions = resolutionHost.getCompilationSettings();
|
||||
|
||||
for (const name of names) {
|
||||
// check if this is a duplicate entry in the list
|
||||
@ -140,7 +131,7 @@ namespace ts {
|
||||
resolution = existingResolution;
|
||||
}
|
||||
else {
|
||||
resolution = loader(name, containingFile, compilerOptions, host);
|
||||
resolution = loader(name, containingFile, compilerOptions, resolutionHost);
|
||||
updateFailedLookupLocationWatches(resolution.failedLookupLocations, existingResolution && existingResolution.failedLookupLocations);
|
||||
}
|
||||
newResolutions.set(name, resolution);
|
||||
@ -214,12 +205,31 @@ namespace ts {
|
||||
const mapLocations = createMultiMap<string>();
|
||||
mapLocations.add(failedLookupLocationPath, failedLookupLocation);
|
||||
directoryWatchesOfFailedLookups.set(dirPath, {
|
||||
watcher: watchDirectoryOfFailedLookupLocation(getDirectoryPath(failedLookupLocation)),
|
||||
watcher: createDirectoryWatcher(getDirectoryPath(failedLookupLocation), dirPath),
|
||||
mapLocations
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function createDirectoryWatcher(directory: string, dirPath: Path) {
|
||||
return resolutionHost.watchDirectoryOfFailedLookupLocation(directory, fileOrFolder => {
|
||||
const fileOrFolderPath = resolutionHost.toPath(fileOrFolder);
|
||||
if (resolutionHost.getCachedPartialSystem) {
|
||||
// Since the file existance changed, update the sourceFiles cache
|
||||
resolutionHost.getCachedPartialSystem().addOrDeleteFileOrFolder(fileOrFolder, fileOrFolderPath);
|
||||
}
|
||||
|
||||
// If the location results in update to failed lookup, schedule program update
|
||||
if (dirPath === fileOrFolderPath) {
|
||||
onAddOrRemoveDirectoryOfFailedLookup(dirPath);
|
||||
resolutionHost.onInvalidatedResolution();
|
||||
}
|
||||
else if (onFileAddOrRemoveInDirectoryOfFailedLookup(fileOrFolderPath)) {
|
||||
resolutionHost.onInvalidatedResolution();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function closeFailedLookupLocationWatcher(failedLookupLocation: string, failedLookupLocationPath: Path) {
|
||||
const dirPath = getDirectoryPath(failedLookupLocationPath);
|
||||
const watches = directoryWatchesOfFailedLookups.get(dirPath);
|
||||
@ -234,13 +244,12 @@ namespace ts {
|
||||
function withFailedLookupLocations(failedLookupLocations: ReadonlyArray<string> | undefined, fn: FailedLookupLocationAction, startIndex?: number) {
|
||||
if (failedLookupLocations) {
|
||||
for (let i = startIndex || 0; i < failedLookupLocations.length; i++) {
|
||||
fn(failedLookupLocations[i], toPath(failedLookupLocations[i]));
|
||||
fn(failedLookupLocations[i], resolutionHost.toPath(failedLookupLocations[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateFailedLookupLocationWatches(failedLookupLocations: ReadonlyArray<string> | undefined, existingFailedLookupLocations: ReadonlyArray<string> | undefined) {
|
||||
log(`Resolution cache: Updating...`);
|
||||
const index = existingFailedLookupLocations && failedLookupLocations ?
|
||||
findDiffIndex(failedLookupLocations, existingFailedLookupLocations) :
|
||||
0;
|
||||
@ -269,7 +278,7 @@ namespace ts {
|
||||
if (resolution && !resolution.isInvalidated) {
|
||||
const result = getResult(resolution);
|
||||
if (result) {
|
||||
if (toPath(getResultFileName(result)) === deletedFilePath) {
|
||||
if (resolutionHost.toPath(getResultFileName(result)) === deletedFilePath) {
|
||||
resolution.isInvalidated = true;
|
||||
(filesWithInvalidatedResolutions || (filesWithInvalidatedResolutions = createMap<true>())).set(path, true);
|
||||
}
|
||||
@ -281,12 +290,13 @@ namespace ts {
|
||||
}
|
||||
|
||||
function invalidateResolutionCacheOfChangedFailedLookupLocation<T extends NameResolutionWithFailedLookupLocations>(
|
||||
failedLookupLocationPath: Path,
|
||||
cache: Map<Map<T>>) {
|
||||
cache: Map<Map<T>>,
|
||||
isChangedFailedLookupLocation: (location: string) => boolean
|
||||
) {
|
||||
cache.forEach((value, containingFile) => {
|
||||
if (value) {
|
||||
value.forEach(resolution => {
|
||||
if (resolution && !resolution.isInvalidated && some(resolution.failedLookupLocations, location => toPath(location) === failedLookupLocationPath)) {
|
||||
if (resolution && !resolution.isInvalidated && some(resolution.failedLookupLocations, isChangedFailedLookupLocation)) {
|
||||
// Mark the file as needing re-evaluation of module resolution instead of using it blindly.
|
||||
resolution.isInvalidated = true;
|
||||
(filesWithInvalidatedResolutions || (filesWithInvalidatedResolutions = createMap<true>())).set(containingFile, true);
|
||||
@ -306,10 +316,17 @@ namespace ts {
|
||||
const watches = directoryWatchesOfFailedLookups.get(dirPath);
|
||||
const isFailedLookupFile = watches.mapLocations.has(fileOrFolder);
|
||||
if (isFailedLookupFile) {
|
||||
invalidateResolutionCacheOfChangedFailedLookupLocation(fileOrFolder, resolvedModuleNames);
|
||||
invalidateResolutionCacheOfChangedFailedLookupLocation(fileOrFolder, resolvedTypeReferenceDirectives);
|
||||
const isFileOrFolder: (location: string) => boolean = location => resolutionHost.toPath(location) === fileOrFolder;
|
||||
invalidateResolutionCacheOfChangedFailedLookupLocation(resolvedModuleNames, isFileOrFolder);
|
||||
invalidateResolutionCacheOfChangedFailedLookupLocation(resolvedTypeReferenceDirectives, isFileOrFolder);
|
||||
}
|
||||
return isFailedLookupFile;
|
||||
}
|
||||
|
||||
function onAddOrRemoveDirectoryOfFailedLookup(dirPath: Path) {
|
||||
const isInDirPath: (location: string) => boolean = location => getDirectoryPath(resolutionHost.toPath(location)) === dirPath;
|
||||
invalidateResolutionCacheOfChangedFailedLookupLocation(resolvedModuleNames, isInDirPath);
|
||||
invalidateResolutionCacheOfChangedFailedLookupLocation(resolvedTypeReferenceDirectives, isInDirPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,11 +161,10 @@ namespace ts {
|
||||
watcher.referenceCount += 1;
|
||||
return;
|
||||
}
|
||||
watcher = _fs.watch(
|
||||
watcher = fsWatchDirectory(
|
||||
dirPath || ".",
|
||||
{ persistent: true },
|
||||
(eventName: string, relativeFileName: string) => fileEventHandler(eventName, relativeFileName, dirPath)
|
||||
);
|
||||
) as DirectoryWatcher;
|
||||
watcher.referenceCount = 1;
|
||||
dirWatchers.set(dirPath, watcher);
|
||||
return;
|
||||
@ -232,6 +231,83 @@ namespace ts {
|
||||
const platform: string = _os.platform();
|
||||
const useCaseSensitiveFileNames = isFileSystemCaseSensitive();
|
||||
|
||||
function fsWatchFile(fileName: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher {
|
||||
_fs.watchFile(fileName, { persistent: true, interval: pollingInterval || 250 }, fileChanged);
|
||||
return {
|
||||
close: () => _fs.unwatchFile(fileName, fileChanged)
|
||||
};
|
||||
|
||||
function fileChanged(curr: any, prev: any) {
|
||||
const isCurrZero = +curr.mtime === 0;
|
||||
const isPrevZero = +prev.mtime === 0;
|
||||
const created = !isCurrZero && isPrevZero;
|
||||
const deleted = isCurrZero && !isPrevZero;
|
||||
|
||||
const eventKind = created
|
||||
? FileWatcherEventKind.Created
|
||||
: deleted
|
||||
? FileWatcherEventKind.Deleted
|
||||
: FileWatcherEventKind.Changed;
|
||||
|
||||
if (eventKind === FileWatcherEventKind.Changed && +curr.mtime <= +prev.mtime) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(fileName, eventKind);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
let watcher = !directoryExists(directoryName) ?
|
||||
watchMissingDirectory() :
|
||||
watchPresentDirectory();
|
||||
return {
|
||||
close: () => {
|
||||
watcher.close();
|
||||
}
|
||||
};
|
||||
|
||||
function watchPresentDirectory(): FileWatcher {
|
||||
if (options === undefined) {
|
||||
if (isNode4OrLater && (process.platform === "win32" || process.platform === "darwin")) {
|
||||
options = { persistent: true, recursive: !!recursive };
|
||||
}
|
||||
else {
|
||||
options = { persistent: true };
|
||||
}
|
||||
}
|
||||
|
||||
const dirWatcher = _fs.watch(
|
||||
directoryName,
|
||||
options,
|
||||
callback
|
||||
);
|
||||
dirWatcher.on("error", () => {
|
||||
// Deleting file
|
||||
watcher = watchMissingDirectory();
|
||||
// Call the callback for current directory
|
||||
callback("rename", "");
|
||||
});
|
||||
return dirWatcher;
|
||||
}
|
||||
|
||||
function watchMissingDirectory(): FileWatcher {
|
||||
return fsWatchFile(directoryName, (_fileName, eventKind) => {
|
||||
if (eventKind === FileWatcherEventKind.Created && directoryExists(directoryName)) {
|
||||
watcher.close();
|
||||
watcher = watchPresentDirectory();
|
||||
// Call the callback for current directory
|
||||
// For now it could be callback for the inner directory creation,
|
||||
// but just return current directory, better than current no-op
|
||||
callback("rename", "");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function readFile(fileName: string, _encoding?: string): string | undefined {
|
||||
if (!fileExists(fileName)) {
|
||||
return undefined;
|
||||
@ -349,7 +425,6 @@ namespace ts {
|
||||
return filter<string>(_fs.readdirSync(path), dir => fileSystemEntryExists(combinePaths(path, dir), FileSystemEntryKind.Directory));
|
||||
}
|
||||
|
||||
const noOpFileWatcher: FileWatcher = { close: noop };
|
||||
const nodeSystem: System = {
|
||||
args: process.argv.slice(2),
|
||||
newLine: _os.EOL,
|
||||
@ -367,60 +442,21 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
else {
|
||||
_fs.watchFile(fileName, { persistent: true, interval: pollingInterval || 250 }, fileChanged);
|
||||
return {
|
||||
close: () => _fs.unwatchFile(fileName, fileChanged)
|
||||
};
|
||||
}
|
||||
|
||||
function fileChanged(curr: any, prev: any) {
|
||||
const isCurrZero = +curr.mtime === 0;
|
||||
const isPrevZero = +prev.mtime === 0;
|
||||
const created = !isCurrZero && isPrevZero;
|
||||
const deleted = isCurrZero && !isPrevZero;
|
||||
|
||||
const eventKind = created
|
||||
? FileWatcherEventKind.Created
|
||||
: deleted
|
||||
? FileWatcherEventKind.Deleted
|
||||
: FileWatcherEventKind.Changed;
|
||||
|
||||
if (eventKind === FileWatcherEventKind.Changed && +curr.mtime <= +prev.mtime) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(fileName, eventKind);
|
||||
return fsWatchFile(fileName, callback, pollingInterval);
|
||||
}
|
||||
},
|
||||
watchDirectory: (directoryName, callback, recursive) => {
|
||||
// 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;
|
||||
if (!directoryExists(directoryName)) {
|
||||
// do nothing if target folder does not exist
|
||||
return noOpFileWatcher;
|
||||
}
|
||||
|
||||
if (isNode4OrLater && (process.platform === "win32" || process.platform === "darwin")) {
|
||||
options = { persistent: true, recursive: !!recursive };
|
||||
}
|
||||
else {
|
||||
options = { persistent: true };
|
||||
}
|
||||
|
||||
return _fs.watch(
|
||||
directoryName,
|
||||
options,
|
||||
(eventName: string, relativeFileName: string) => {
|
||||
// In watchDirectory we only care about adding and removing files (when event name is
|
||||
// "rename"); changes made within files are handled by corresponding fileWatchers (when
|
||||
// event name is "change")
|
||||
if (eventName === "rename") {
|
||||
// When deleting a file, the passed baseFileName is null
|
||||
callback(!relativeFileName ? relativeFileName : normalizePath(combinePaths(directoryName, relativeFileName)));
|
||||
}
|
||||
return fsWatchDirectory(directoryName, (eventName, relativeFileName) => {
|
||||
// In watchDirectory we only care about adding and removing files (when event name is
|
||||
// "rename"); changes made within files are handled by corresponding fileWatchers (when
|
||||
// event name is "change")
|
||||
if (eventName === "rename") {
|
||||
// When deleting a file, the passed baseFileName is null
|
||||
callback(!relativeFileName ? relativeFileName : normalizePath(combinePaths(directoryName, relativeFileName)));
|
||||
}
|
||||
);
|
||||
}, recursive);
|
||||
},
|
||||
resolvePath: path => _path.resolve(path),
|
||||
fileExists,
|
||||
|
||||
@ -3576,9 +3576,7 @@ namespace ts {
|
||||
|
||||
export function addDirectoryWatcher(host: System, directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags): FileWatcher {
|
||||
const recursive = (flags & WatchDirectoryFlags.Recursive) !== 0;
|
||||
return host.watchDirectory(directory, fileName => {
|
||||
cb(getNormalizedAbsolutePath(fileName, directory));
|
||||
}, recursive);
|
||||
return host.watchDirectory(directory, cb, recursive);
|
||||
}
|
||||
|
||||
export function addDirectoryWatcherWithLogging(host: System, directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags, log: (s: string) => void): FileWatcher {
|
||||
|
||||
@ -245,7 +245,6 @@ 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 hasInvalidatedResolution: HasInvalidatedResolution; // Passed along to see if source file has invalidated resolutions
|
||||
let hasChangedCompilerOptions = false; // True if the compiler options have changed between compilations
|
||||
|
||||
const loggingEnabled = compilerOptions.diagnostics || compilerOptions.extendedDiagnostics;
|
||||
@ -257,20 +256,47 @@ namespace ts {
|
||||
watchingHost = watchingHost || createWatchingSystemHost(compilerOptions.pretty);
|
||||
const { system, parseConfigFile, reportDiagnostic, reportWatchDiagnostic, beforeCompile, afterCompile } = watchingHost;
|
||||
|
||||
const host = configFileName ? createCachedPartialSystem(system) : system;
|
||||
const partialSystem = configFileName ? createCachedPartialSystem(system) : system;
|
||||
if (configFileName) {
|
||||
configFileWatcher = watchFile(system, configFileName, scheduleProgramReload, writeLog);
|
||||
}
|
||||
const currentDirectory = host.getCurrentDirectory();
|
||||
const getCanonicalFileName = createGetCanonicalFileName(system.useCaseSensitiveFileNames);
|
||||
|
||||
// Cache for the module resolution
|
||||
const resolutionCache = createResolutionCache(
|
||||
fileName => toPath(fileName),
|
||||
() => compilerOptions,
|
||||
const getCurrentDirectory = memoize(() => partialSystem.getCurrentDirectory());
|
||||
const realpath = system.realpath && ((path: string) => system.realpath(path));
|
||||
const getCachedPartialSystem = configFileName && (() => partialSystem as CachedPartialSystem);
|
||||
const getCanonicalFileName = createGetCanonicalFileName(system.useCaseSensitiveFileNames);
|
||||
let newLine = getNewLineCharacter(compilerOptions, system);
|
||||
|
||||
const compilerHost: CompilerHost & ResolutionCacheHost = {
|
||||
// Members for CompilerHost
|
||||
getSourceFile: getVersionedSourceFile,
|
||||
getSourceFileByPath: getVersionedSourceFileByPath,
|
||||
getDefaultLibLocation,
|
||||
getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)),
|
||||
writeFile: (_fileName, _data, _writeByteOrderMark, _onError?, _sourceFiles?) => { },
|
||||
getCurrentDirectory,
|
||||
useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames,
|
||||
getCanonicalFileName,
|
||||
getNewLine: () => newLine,
|
||||
fileExists,
|
||||
readFile,
|
||||
trace,
|
||||
directoryExists,
|
||||
getEnvironmentVariable: name => system.getEnvironmentVariable ? system.getEnvironmentVariable(name) : "",
|
||||
getDirectories,
|
||||
realpath,
|
||||
resolveTypeReferenceDirectives,
|
||||
resolveModuleNames,
|
||||
onReleaseOldSourceFile,
|
||||
// Members for ResolutionCacheHost
|
||||
toPath,
|
||||
getCompilationSettings: () => compilerOptions,
|
||||
watchDirectoryOfFailedLookupLocation,
|
||||
writeLog
|
||||
);
|
||||
getCachedPartialSystem,
|
||||
onInvalidatedResolution: scheduleProgramUpdate
|
||||
};
|
||||
// Cache for the module resolution
|
||||
const resolutionCache = createResolutionCache(compilerHost);
|
||||
|
||||
// There is no extra check needed since we can just rely on the program to decide emit
|
||||
const builder = createBuilder(getCanonicalFileName, getFileEmitOutput, computeHash, _sourceFile => true);
|
||||
@ -285,14 +311,15 @@ namespace ts {
|
||||
function synchronizeProgram() {
|
||||
writeLog(`Synchronizing program`);
|
||||
|
||||
hasInvalidatedResolution = resolutionCache.createHasInvalidatedResolution();
|
||||
if (hasChangedCompilerOptions) {
|
||||
newLine = getNewLineCharacter(compilerOptions, system);
|
||||
}
|
||||
|
||||
const hasInvalidatedResolution = resolutionCache.createHasInvalidatedResolution();
|
||||
if (isProgramUptoDate(program, rootFileNames, compilerOptions, getSourceVersion, fileExists, hasInvalidatedResolution)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the compiler host
|
||||
const compilerHost = createWatchedCompilerHost(compilerOptions);
|
||||
resolutionCache.setModuleResolutionHost(compilerHost);
|
||||
if (hasChangedCompilerOptions && changesAffectModuleResolution(program && program.getCompilerOptions(), compilerOptions)) {
|
||||
resolutionCache.clear();
|
||||
}
|
||||
@ -300,6 +327,7 @@ namespace ts {
|
||||
beforeCompile(compilerOptions);
|
||||
|
||||
// Compile the program
|
||||
compilerHost.hasInvalidatedResolution = hasInvalidatedResolution;
|
||||
program = createProgram(rootFileNames, compilerOptions, compilerHost, program);
|
||||
builder.onProgramUpdateGraph(program, hasInvalidatedResolution);
|
||||
|
||||
@ -309,7 +337,7 @@ namespace ts {
|
||||
// These are the paths that program creater told us as not in use any more but were missing on the disk.
|
||||
// We didnt remove the entry for them from sourceFiles cache so that we dont have to do File IO,
|
||||
// if there is already watcher for it (for missing files)
|
||||
// At that point our watches were updated, hence now we know that these paths are not tracked and need to be removed
|
||||
// At this point our watches were updated, hence now we know that these paths are not tracked and need to be removed
|
||||
// so that at later time we have correct result of their presence
|
||||
for (const missingFilePath of missingFilePathsRequestedForRelease) {
|
||||
if (!missingFilesMap.has(missingFilePath)) {
|
||||
@ -319,40 +347,12 @@ namespace ts {
|
||||
missingFilePathsRequestedForRelease = undefined;
|
||||
}
|
||||
|
||||
afterCompile(host, program, builder);
|
||||
afterCompile(partialSystem, program, builder);
|
||||
reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.Compilation_complete_Watching_for_file_changes));
|
||||
}
|
||||
|
||||
function createWatchedCompilerHost(options: CompilerOptions): CompilerHost {
|
||||
const newLine = getNewLineCharacter(options, system);
|
||||
const realpath = system.realpath && ((path: string) => system.realpath(path));
|
||||
|
||||
return {
|
||||
getSourceFile: getVersionedSourceFile,
|
||||
getSourceFileByPath: getVersionedSourceFileByPath,
|
||||
getDefaultLibLocation,
|
||||
getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)),
|
||||
writeFile: (_fileName, _data, _writeByteOrderMark, _onError?, _sourceFiles?) => { },
|
||||
getCurrentDirectory: memoize(() => host.getCurrentDirectory()),
|
||||
useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames,
|
||||
getCanonicalFileName,
|
||||
getNewLine: () => newLine,
|
||||
fileExists,
|
||||
readFile: fileName => system.readFile(fileName),
|
||||
trace: (s: string) => system.write(s + newLine),
|
||||
directoryExists: directoryName => host.directoryExists(directoryName),
|
||||
getEnvironmentVariable: name => system.getEnvironmentVariable ? system.getEnvironmentVariable(name) : "",
|
||||
getDirectories: (path: string) => host.getDirectories(path),
|
||||
realpath,
|
||||
resolveTypeReferenceDirectives: (typeDirectiveNames, containingFile) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile),
|
||||
resolveModuleNames: (moduleNames, containingFile) => resolutionCache.resolveModuleNames(moduleNames, containingFile, /*logChanges*/ false),
|
||||
onReleaseOldSourceFile,
|
||||
hasInvalidatedResolution
|
||||
};
|
||||
}
|
||||
|
||||
function toPath(fileName: string) {
|
||||
return ts.toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
return ts.toPath(fileName, getCurrentDirectory(), getCanonicalFileName);
|
||||
}
|
||||
|
||||
function fileExists(fileName: string) {
|
||||
@ -362,7 +362,31 @@ namespace ts {
|
||||
return !isString(hostSourceFileInfo);
|
||||
}
|
||||
|
||||
return host.fileExists(fileName);
|
||||
return partialSystem.fileExists(fileName);
|
||||
}
|
||||
|
||||
function directoryExists(directoryName: string) {
|
||||
return partialSystem.directoryExists(directoryName);
|
||||
}
|
||||
|
||||
function readFile(fileName: string) {
|
||||
return system.readFile(fileName);
|
||||
}
|
||||
|
||||
function trace(s: string) {
|
||||
return system.write(s + newLine);
|
||||
}
|
||||
|
||||
function getDirectories(path: string) {
|
||||
return partialSystem.getDirectories(path);
|
||||
}
|
||||
|
||||
function resolveModuleNames(moduleNames: string[], containingFile: string) {
|
||||
return resolutionCache.resolveModuleNames(moduleNames, containingFile, /*logChanges*/ false);
|
||||
}
|
||||
|
||||
function resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string) {
|
||||
return resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile);
|
||||
}
|
||||
|
||||
function getDefaultLibLocation(): string {
|
||||
@ -503,7 +527,7 @@ namespace ts {
|
||||
writeLog(`Reloading config file: ${configFileName}`);
|
||||
needsReload = false;
|
||||
|
||||
const cachedHost = host as CachedPartialSystem;
|
||||
const cachedHost = partialSystem as CachedPartialSystem;
|
||||
cachedHost.clearCache();
|
||||
const configParseResult = parseConfigFile(configFileName, optionsToExtendForConfigFile, cachedHost, reportDiagnostic, reportWatchDiagnostic);
|
||||
rootFileNames = configParseResult.fileNames;
|
||||
@ -548,26 +572,12 @@ namespace ts {
|
||||
|
||||
function updateCachedSystemWithFile(fileName: string, path: Path, eventKind: FileWatcherEventKind) {
|
||||
if (configFileName) {
|
||||
(host as CachedPartialSystem).addOrDeleteFile(fileName, path, eventKind);
|
||||
(partialSystem as CachedPartialSystem).addOrDeleteFile(fileName, path, eventKind);
|
||||
}
|
||||
}
|
||||
|
||||
function watchDirectoryOfFailedLookupLocation(directory: string) {
|
||||
return watchDirectory(system, directory, onFileAddOrRemoveInDirectoryOfFailedLookup, WatchDirectoryFlags.None, writeLog);
|
||||
}
|
||||
|
||||
function onFileAddOrRemoveInDirectoryOfFailedLookup(fileOrFolder: string) {
|
||||
const fileOrFolderPath = toPath(fileOrFolder);
|
||||
|
||||
if (configFileName) {
|
||||
// Since the file existance changed, update the sourceFiles cache
|
||||
(host as CachedPartialSystem).addOrDeleteFileOrFolder(fileOrFolder, fileOrFolderPath);
|
||||
}
|
||||
|
||||
// If the location results in update to failed lookup, schedule program update
|
||||
if (resolutionCache.onFileAddOrRemoveInDirectoryOfFailedLookup(fileOrFolderPath)) {
|
||||
scheduleProgramUpdate();
|
||||
}
|
||||
function watchDirectoryOfFailedLookupLocation(directory: string, cb: DirectoryWatcherCallback) {
|
||||
return watchDirectory(system, directory, cb, WatchDirectoryFlags.None, writeLog);
|
||||
}
|
||||
|
||||
function watchMissingFilePath(missingFilePath: Path) {
|
||||
@ -593,42 +603,45 @@ namespace ts {
|
||||
updateWatchingWildcardDirectories(
|
||||
watchedWildcardDirectories || (watchedWildcardDirectories = createMap()),
|
||||
createMapFromTemplate(configFileWildCardDirectories),
|
||||
watchWildCardDirectory
|
||||
watchWildcardDirectory
|
||||
);
|
||||
}
|
||||
|
||||
function watchWildCardDirectory(directory: string, flags: WatchDirectoryFlags) {
|
||||
return watchDirectory(system, directory, onFileAddOrRemoveInWatchedDirectory, flags, writeLog);
|
||||
}
|
||||
function watchWildcardDirectory(directory: string, flags: WatchDirectoryFlags) {
|
||||
return watchDirectory(
|
||||
system,
|
||||
directory,
|
||||
fileOrFolder => {
|
||||
Debug.assert(!!configFileName);
|
||||
|
||||
function onFileAddOrRemoveInWatchedDirectory(fileOrFolder: string) {
|
||||
Debug.assert(!!configFileName);
|
||||
const fileOrFolderPath = toPath(fileOrFolder);
|
||||
|
||||
const fileOrFolderPath = toPath(fileOrFolder);
|
||||
// Since the file existance changed, update the sourceFiles cache
|
||||
(partialSystem as CachedPartialSystem).addOrDeleteFileOrFolder(fileOrFolder, fileOrFolderPath);
|
||||
removeSourceFile(fileOrFolderPath);
|
||||
|
||||
// Since the file existance changed, update the sourceFiles cache
|
||||
(host as CachedPartialSystem).addOrDeleteFileOrFolder(fileOrFolder, fileOrFolderPath);
|
||||
removeSourceFile(fileOrFolderPath);
|
||||
// If the the added or created file or folder 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}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// If a change was made inside "folder/file", node will trigger the callback twice:
|
||||
// one with the fileName being "folder/file", and the other one with "folder".
|
||||
// We don't respond to the second one.
|
||||
if (fileOrFolder && !isSupportedSourceFileName(fileOrFolder, compilerOptions)) {
|
||||
writeLog(`Project: ${configFileName} Detected file add/remove of non supported extension: ${fileOrFolder}`);
|
||||
return;
|
||||
}
|
||||
// Reload is pending, do the reload
|
||||
if (!needsReload) {
|
||||
const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), compilerOptions, partialSystem);
|
||||
if (!configFileSpecs.filesSpecs && result.fileNames.length === 0) {
|
||||
reportDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName));
|
||||
}
|
||||
rootFileNames = result.fileNames;
|
||||
|
||||
// Reload is pending, do the reload
|
||||
if (!needsReload) {
|
||||
const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), compilerOptions, host);
|
||||
if (!configFileSpecs.filesSpecs && result.fileNames.length === 0) {
|
||||
reportDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName));
|
||||
}
|
||||
rootFileNames = result.fileNames;
|
||||
|
||||
// Schedule Update the program
|
||||
scheduleProgramUpdate();
|
||||
}
|
||||
// Schedule Update the program
|
||||
scheduleProgramUpdate();
|
||||
}
|
||||
},
|
||||
flags,
|
||||
writeLog
|
||||
);
|
||||
}
|
||||
|
||||
function computeHash(data: string) {
|
||||
|
||||
@ -147,7 +147,7 @@ namespace ts {
|
||||
// setting compiler options discards module resolution cache
|
||||
fileExistsCalled = false;
|
||||
|
||||
const compilerOptions = ts.cloneCompilerOptions(project.getCompilerOptions());
|
||||
const compilerOptions = ts.cloneCompilerOptions(project.getCompilationSettings());
|
||||
compilerOptions.target = ts.ScriptTarget.ES5;
|
||||
project.setCompilerOptions(compilerOptions);
|
||||
|
||||
|
||||
@ -321,6 +321,34 @@ namespace ts.projectSystem {
|
||||
verifyDiagnostics(actual, []);
|
||||
}
|
||||
|
||||
function getPathsForTypesOrModules(base: string, rootPaths: string[], typesOrModules: string[], map: Map<true>, rootDir?: string) {
|
||||
while (1) {
|
||||
forEach(rootPaths, r => {
|
||||
const rp = combinePaths(base, r);
|
||||
forEach(typesOrModules, tm => {
|
||||
map.set(tm === "" ? rp : combinePaths(rp, tm), true);
|
||||
});
|
||||
});
|
||||
const parentDir = getDirectoryPath(base);
|
||||
if (base === rootDir || parentDir === base) {
|
||||
break;
|
||||
}
|
||||
base = parentDir;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
function getNodeModulesWatchedDirectories(path: string, modules: string[], map = createMap<true>()) {
|
||||
forEach(modules, module => {
|
||||
getPathsForTypesOrModules(path, ["node_modules"], ["", module, "@types", `@types/${module}`], map);
|
||||
});
|
||||
return map;
|
||||
}
|
||||
|
||||
function getTypesWatchedDirectories(path: string, typeRoots: string[], types: string[], map = createMap<true>()) {
|
||||
return getPathsForTypesOrModules(path, typeRoots, types.concat(""), map, path);
|
||||
}
|
||||
|
||||
describe("tsserverProjectSystem", () => {
|
||||
const commonFile1: FileOrFolder = {
|
||||
path: "/a/b/commonFile1.ts",
|
||||
@ -357,8 +385,9 @@ namespace ts.projectSystem {
|
||||
checkFileNames("inferred project", project.getFileNames(), [appFile.path, libFile.path, moduleFile.path]);
|
||||
const configFileLocations = ["/a/b/c/", "/a/b/", "/a/", "/"];
|
||||
const configFiles = flatMap(configFileLocations, location => [location + "tsconfig.json", location + "jsconfig.json"]);
|
||||
const moduleLookupLocations = ["/a/b/c/module.ts", "/a/b/c/module.tsx"];
|
||||
checkWatchedFiles(host, configFiles.concat(libFile.path, moduleFile.path, ...moduleLookupLocations));
|
||||
checkWatchedFiles(host, configFiles.concat(libFile.path, moduleFile.path));
|
||||
checkWatchedDirectories(host, ["/a/b/c"], /*recursive*/ false);
|
||||
checkWatchedDirectories(host, [], /*recursive*/ true);
|
||||
});
|
||||
|
||||
it("can handle tsconfig file name with difference casing", () => {
|
||||
@ -3403,9 +3432,9 @@ namespace ts.projectSystem {
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [file4.path]);
|
||||
checkProjectActualFiles(projectService.inferredProjects[1], [file1.path, file2.path]);
|
||||
checkProjectActualFiles(projectService.inferredProjects[2], [file3.path]);
|
||||
assert.equal(projectService.inferredProjects[0].getCompilerOptions().target, ScriptTarget.ESNext);
|
||||
assert.equal(projectService.inferredProjects[1].getCompilerOptions().target, ScriptTarget.ESNext);
|
||||
assert.equal(projectService.inferredProjects[2].getCompilerOptions().target, ScriptTarget.ES2015);
|
||||
assert.equal(projectService.inferredProjects[0].getCompilationSettings().target, ScriptTarget.ESNext);
|
||||
assert.equal(projectService.inferredProjects[1].getCompilationSettings().target, ScriptTarget.ESNext);
|
||||
assert.equal(projectService.inferredProjects[2].getCompilationSettings().target, ScriptTarget.ES2015);
|
||||
});
|
||||
});
|
||||
|
||||
@ -3895,13 +3924,13 @@ namespace ts.projectSystem {
|
||||
projectService.openClientFile(file1.path);
|
||||
|
||||
let project = projectService.inferredProjects[0];
|
||||
let options = project.getCompilerOptions();
|
||||
let options = project.getCompilationSettings();
|
||||
assert.isTrue(options.maxNodeModuleJsDepth === 2);
|
||||
|
||||
// Assert the option sticks
|
||||
projectService.setCompilerOptionsForInferredProjects({ target: ScriptTarget.ES2016 });
|
||||
project = projectService.inferredProjects[0];
|
||||
options = project.getCompilerOptions();
|
||||
options = project.getCompilationSettings();
|
||||
assert.isTrue(options.maxNodeModuleJsDepth === 2);
|
||||
});
|
||||
|
||||
@ -3921,15 +3950,15 @@ namespace ts.projectSystem {
|
||||
projectService.openClientFile(file1.path);
|
||||
checkNumberOfInferredProjects(projectService, 1);
|
||||
let project = projectService.inferredProjects[0];
|
||||
assert.isUndefined(project.getCompilerOptions().maxNodeModuleJsDepth);
|
||||
assert.isUndefined(project.getCompilationSettings().maxNodeModuleJsDepth);
|
||||
|
||||
projectService.openClientFile(file2.path);
|
||||
project = projectService.inferredProjects[0];
|
||||
assert.isTrue(project.getCompilerOptions().maxNodeModuleJsDepth === 2);
|
||||
assert.isTrue(project.getCompilationSettings().maxNodeModuleJsDepth === 2);
|
||||
|
||||
projectService.closeClientFile(file2.path);
|
||||
project = projectService.inferredProjects[0];
|
||||
assert.isUndefined(project.getCompilerOptions().maxNodeModuleJsDepth);
|
||||
assert.isUndefined(project.getCompilationSettings().maxNodeModuleJsDepth);
|
||||
});
|
||||
});
|
||||
|
||||
@ -4164,7 +4193,8 @@ namespace ts.projectSystem {
|
||||
describe("WatchDirectories for config file with", () => {
|
||||
function verifyWatchDirectoriesCaseSensitivity(useCaseSensitiveFileNames: boolean) {
|
||||
const frontendDir = "/Users/someuser/work/applications/frontend";
|
||||
const canonicalFrontendDir = useCaseSensitiveFileNames ? frontendDir : frontendDir.toLowerCase();
|
||||
const toCanonical: (s: string) => Path = useCaseSensitiveFileNames ? s => s as Path : s => s.toLowerCase() as Path;
|
||||
const canonicalFrontendDir = toCanonical(frontendDir);
|
||||
const file1: FileOrFolder = {
|
||||
path: `${frontendDir}/src/app/utils/Analytic.ts`,
|
||||
content: "export class SomeClass { };"
|
||||
@ -4181,6 +4211,8 @@ namespace ts.projectSystem {
|
||||
path: "/a/lib/lib.es2016.full.d.ts",
|
||||
content: libFile.content
|
||||
};
|
||||
const typeRoots = ["types", "node_modules/@types"];
|
||||
const types = ["node", "jest"];
|
||||
const tsconfigFile: FileOrFolder = {
|
||||
path: `${frontendDir}/tsconfig.json`,
|
||||
content: JSON.stringify({
|
||||
@ -4194,16 +4226,10 @@ namespace ts.projectSystem {
|
||||
"noEmitOnError": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"types": [
|
||||
"node",
|
||||
"jest"
|
||||
],
|
||||
types,
|
||||
"noUnusedLocals": true,
|
||||
"outDir": "./compiled",
|
||||
"typeRoots": [
|
||||
"types",
|
||||
"node_modules/@types"
|
||||
],
|
||||
typeRoots,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"*": [
|
||||
@ -4223,10 +4249,21 @@ namespace ts.projectSystem {
|
||||
const projectFiles = [file1, file2, es2016LibFile, tsconfigFile];
|
||||
const host = createServerHost(projectFiles, { useCaseSensitiveFileNames });
|
||||
const projectService = createProjectService(host);
|
||||
const canonicalConfigPath = useCaseSensitiveFileNames ? tsconfigFile.path : tsconfigFile.path.toLowerCase();
|
||||
const canonicalConfigPath = toCanonical(tsconfigFile.path);
|
||||
const { configFileName } = projectService.openClientFile(file1.path);
|
||||
assert.equal(configFileName, tsconfigFile.path, `should find config`);
|
||||
checkNumberOfConfiguredProjects(projectService, 1);
|
||||
const watchedModuleDirectories = arrayFrom(
|
||||
getNodeModulesWatchedDirectories(
|
||||
canonicalFrontendDir,
|
||||
types,
|
||||
getTypesWatchedDirectories(
|
||||
canonicalFrontendDir,
|
||||
typeRoots,
|
||||
types
|
||||
)
|
||||
).keys()
|
||||
);
|
||||
|
||||
const project = projectService.configuredProjects.get(canonicalConfigPath);
|
||||
verifyProjectAndWatchedDirectories();
|
||||
@ -4270,10 +4307,17 @@ namespace ts.projectSystem {
|
||||
verifyProjectAndWatchedDirectories();
|
||||
callsTrackingHost.verifyNoHostCalls();
|
||||
|
||||
function getFilePathIfOpen(f: FileOrFolder) {
|
||||
const path = toCanonical(f.path);
|
||||
const info = projectService.getScriptInfoForPath(toCanonical(f.path));
|
||||
return info && info.isScriptOpen() ? undefined : path;
|
||||
}
|
||||
|
||||
function verifyProjectAndWatchedDirectories() {
|
||||
checkProjectActualFiles(project, map(projectFiles, f => f.path));
|
||||
checkWatchedFiles(host, mapDefined(projectFiles, getFilePathIfOpen));
|
||||
checkWatchedDirectories(host, [`${canonicalFrontendDir}/src`], /*recursive*/ true);
|
||||
checkWatchedDirectories(host, [`${canonicalFrontendDir}/types`, `${canonicalFrontendDir}/node_modules/@types`], /*recursive*/ false);
|
||||
checkWatchedDirectories(host, watchedModuleDirectories, /*recursive*/ false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4328,7 +4372,7 @@ namespace ts.projectSystem {
|
||||
const projectService = createProjectService(host);
|
||||
const { configFileName } = projectService.openClientFile(app.path);
|
||||
assert.equal(configFileName, tsconfigJson.path, `should find config`);
|
||||
const watchedModuleLocations = getNodeModulesWatchedDirectories(appFolder, "lodash");
|
||||
const watchedModuleLocations = arrayFrom(getNodeModulesWatchedDirectories(appFolder, ["lodash"]).keys());
|
||||
verifyProject();
|
||||
|
||||
let timeoutAfterReloadFs = timeoutDuringPartialInstallation;
|
||||
@ -4406,7 +4450,7 @@ namespace ts.projectSystem {
|
||||
|
||||
const lodashIndexPath = "/a/b/node_modules/@types/lodash/index.d.ts";
|
||||
projectFiles.push(find(filesAndFoldersToAdd, f => f.path === lodashIndexPath));
|
||||
watchedModuleLocations.length = watchedModuleLocations.indexOf(lodashIndexPath);
|
||||
watchedModuleLocations.length = indexOf(watchedModuleLocations, getDirectoryPath(lodashIndexPath));
|
||||
// npm installation complete, timeout after reload fs
|
||||
timeoutAfterReloadFs = true;
|
||||
verifyAfterPartialOrCompleteNpmInstall(2);
|
||||
@ -4429,32 +4473,10 @@ namespace ts.projectSystem {
|
||||
const projectFilePaths = map(projectFiles, f => f.path);
|
||||
checkProjectActualFiles(project, projectFilePaths);
|
||||
|
||||
const filesWatched = filter(projectFilePaths, p => p !== app.path).concat(watchedModuleLocations);
|
||||
const filesWatched = filter(projectFilePaths, p => p !== app.path);
|
||||
checkWatchedFiles(host, filesWatched);
|
||||
checkWatchedDirectories(host, [appFolder], /*recursive*/ true);
|
||||
checkWatchedDirectories(host, [], /*recursive*/ false);
|
||||
}
|
||||
|
||||
function getNodeModulesWatchedDirectories(path: string, module: string): string[] {
|
||||
const nodeModulesDir = combinePaths(path, "node_modules/");
|
||||
const parentDir = getDirectoryPath(path);
|
||||
const parentNodeModules = parentDir !== path ? getNodeModulesWatchedDirectories(parentDir, module) : [];
|
||||
return [
|
||||
`${nodeModulesDir}${module}.ts`,
|
||||
`${nodeModulesDir}${module}.tsx`,
|
||||
`${nodeModulesDir}${module}.d.ts`,
|
||||
`${nodeModulesDir}${module}/index.ts`,
|
||||
`${nodeModulesDir}${module}/index.tsx`,
|
||||
`${nodeModulesDir}${module}/index.d.ts`,
|
||||
`${nodeModulesDir}@types/${module}.d.ts`,
|
||||
`${nodeModulesDir}@types/${module}/index.d.ts`,
|
||||
`${nodeModulesDir}@types/${module}/package.json`,
|
||||
`${nodeModulesDir}${module}.js`,
|
||||
`${nodeModulesDir}${module}.jsx`,
|
||||
`${nodeModulesDir}${module}/package.json`,
|
||||
`${nodeModulesDir}${module}/index.js`,
|
||||
`${nodeModulesDir}${module}/index.jsx`,
|
||||
].concat(parentNodeModules);
|
||||
checkWatchedDirectories(host, watchedModuleLocations, /*recursive*/ false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -95,8 +95,35 @@ namespace ts.TestFSWithWatch {
|
||||
}
|
||||
}
|
||||
|
||||
function getDiffInKeys(map: Map<any>, expectedKeys: string[]) {
|
||||
if (map.size === expectedKeys.length) {
|
||||
return "";
|
||||
}
|
||||
const notInActual: string[] = [];
|
||||
const duplicates: string[] = [];
|
||||
const seen = createMap<true>();
|
||||
forEach(expectedKeys, expectedKey => {
|
||||
if (seen.has(expectedKey)) {
|
||||
duplicates.push(expectedKey);
|
||||
return;
|
||||
}
|
||||
seen.set(expectedKey, true);
|
||||
if (!map.has(expectedKey)) {
|
||||
notInActual.push(expectedKey);
|
||||
}
|
||||
});
|
||||
const inActualNotExpected: string[] = [];
|
||||
map.forEach((_value, key) => {
|
||||
if (!seen.has(key)) {
|
||||
inActualNotExpected.push(key);
|
||||
}
|
||||
seen.set(key, true);
|
||||
});
|
||||
return `\n\nNotInActual: ${notInActual}\nDuplicates: ${duplicates}\nInActualButNotInExpected: ${inActualNotExpected}`;
|
||||
}
|
||||
|
||||
function checkMapKeys(caption: string, map: Map<any>, expectedKeys: string[]) {
|
||||
assert.equal(map.size, expectedKeys.length, `${caption}: incorrect size of map: Actual keys: ${arrayFrom(map.keys())} Expected: ${expectedKeys}`);
|
||||
assert.equal(map.size, expectedKeys.length, `${caption}: incorrect size of map: Actual keys: ${arrayFrom(map.keys())} Expected: ${expectedKeys}${getDiffInKeys(map, expectedKeys)}`);
|
||||
for (const name of expectedKeys) {
|
||||
assert.isTrue(map.has(name), `${caption} is expected to contain ${name}, actual keys: ${arrayFrom(map.keys())}`);
|
||||
}
|
||||
@ -175,6 +202,8 @@ namespace ts.TestFSWithWatch {
|
||||
|
||||
type TimeOutCallback = () => any;
|
||||
|
||||
export type TestFileWatcher = { cb: FileWatcherCallback; fileName: string; };
|
||||
export type TestDirectoryWatcher = { cb: DirectoryWatcherCallback; directoryName: string; };
|
||||
export class TestServerHost implements server.ServerHost {
|
||||
args: string[] = [];
|
||||
|
||||
@ -186,9 +215,9 @@ namespace ts.TestFSWithWatch {
|
||||
private timeoutCallbacks = new Callbacks();
|
||||
private immediateCallbacks = new Callbacks();
|
||||
|
||||
readonly watchedDirectories = createMultiMap<DirectoryWatcherCallback>();
|
||||
readonly watchedDirectoriesRecursive = createMultiMap<DirectoryWatcherCallback>();
|
||||
readonly watchedFiles = createMultiMap<FileWatcherCallback>();
|
||||
readonly watchedDirectories = createMultiMap<TestDirectoryWatcher>();
|
||||
readonly watchedDirectoriesRecursive = createMultiMap<TestDirectoryWatcher>();
|
||||
readonly watchedFiles = createMultiMap<TestFileWatcher>();
|
||||
|
||||
constructor(public withSafeList: boolean, public useCaseSensitiveFileNames: boolean, private executingFilePath: string, private currentDirectory: string, fileOrFolderList: FileOrFolder[], public readonly newLine = "\n") {
|
||||
this.getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
|
||||
@ -317,8 +346,8 @@ namespace ts.TestFSWithWatch {
|
||||
// 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 => cb(relativePath));
|
||||
invokeWatcherCallbacks(this.watchedDirectoriesRecursive.get(fileOrFolder.path), cb => cb(relativePath));
|
||||
invokeWatcherCallbacks(this.watchedDirectories.get(fileOrFolder.path), cb => this.directoryCallback(cb, relativePath));
|
||||
invokeWatcherCallbacks(this.watchedDirectoriesRecursive.get(fileOrFolder.path), cb => this.directoryCallback(cb, relativePath));
|
||||
}
|
||||
|
||||
if (basePath !== fileOrFolder.path) {
|
||||
@ -333,8 +362,7 @@ namespace ts.TestFSWithWatch {
|
||||
|
||||
private invokeFileWatcher(fileFullPath: string, eventKind: FileWatcherEventKind) {
|
||||
const callbacks = this.watchedFiles.get(this.toPath(fileFullPath));
|
||||
const fileName = getBaseFileName(fileFullPath);
|
||||
invokeWatcherCallbacks(callbacks, cb => cb(fileName, eventKind));
|
||||
invokeWatcherCallbacks(callbacks, ({ cb, fileName }) => cb(fileName, eventKind));
|
||||
}
|
||||
|
||||
private getRelativePathToDirectory(directoryFullPath: string, fileFullPath: string) {
|
||||
@ -346,16 +374,20 @@ namespace ts.TestFSWithWatch {
|
||||
*/
|
||||
private invokeDirectoryWatcher(folderFullPath: string, fileName: string) {
|
||||
const relativePath = this.getRelativePathToDirectory(folderFullPath, fileName);
|
||||
invokeWatcherCallbacks(this.watchedDirectories.get(this.toPath(folderFullPath)), cb => cb(relativePath));
|
||||
invokeWatcherCallbacks(this.watchedDirectories.get(this.toPath(folderFullPath)), cb => this.directoryCallback(cb, relativePath));
|
||||
this.invokeRecursiveDirectoryWatcher(folderFullPath, fileName);
|
||||
}
|
||||
|
||||
private directoryCallback({ cb, directoryName }: TestDirectoryWatcher, relativePath: string) {
|
||||
cb(combinePaths(directoryName, relativePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* This will call the recursive directory watcher for this directory as well as all the base directories
|
||||
*/
|
||||
private invokeRecursiveDirectoryWatcher(fullPath: string, fileName: string) {
|
||||
const relativePath = this.getRelativePathToDirectory(fullPath, fileName);
|
||||
invokeWatcherCallbacks(this.watchedDirectoriesRecursive.get(this.toPath(fullPath)), cb => cb(relativePath));
|
||||
invokeWatcherCallbacks(this.watchedDirectoriesRecursive.get(this.toPath(fullPath)), cb => this.directoryCallback(cb, relativePath));
|
||||
const basePath = getDirectoryPath(fullPath);
|
||||
if (this.getCanonicalFileName(fullPath) !== this.getCanonicalFileName(basePath)) {
|
||||
this.invokeRecursiveDirectoryWatcher(basePath, fileName);
|
||||
@ -437,9 +469,13 @@ namespace ts.TestFSWithWatch {
|
||||
});
|
||||
}
|
||||
|
||||
watchDirectory(directoryName: string, callback: DirectoryWatcherCallback, recursive: boolean): DirectoryWatcher {
|
||||
watchDirectory(directoryName: string, cb: DirectoryWatcherCallback, recursive: boolean): DirectoryWatcher {
|
||||
const path = this.toFullPath(directoryName);
|
||||
const map = recursive ? this.watchedDirectoriesRecursive : this.watchedDirectories;
|
||||
const callback: TestDirectoryWatcher = {
|
||||
cb,
|
||||
directoryName
|
||||
};
|
||||
map.add(path, callback);
|
||||
return {
|
||||
referenceCount: 0,
|
||||
@ -452,8 +488,9 @@ namespace ts.TestFSWithWatch {
|
||||
return Harness.mockHash(s);
|
||||
}
|
||||
|
||||
watchFile(fileName: string, callback: FileWatcherCallback) {
|
||||
watchFile(fileName: string, cb: FileWatcherCallback) {
|
||||
const path = this.toFullPath(fileName);
|
||||
const callback: TestFileWatcher = { fileName, cb };
|
||||
this.watchedFiles.add(path, callback);
|
||||
return { close: () => this.watchedFiles.remove(path, callback) };
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
/// <reference path="utilities.ts" />
|
||||
/// <reference path="session.ts" />
|
||||
/// <reference path="scriptVersionCache.ts"/>
|
||||
/// <reference path="lsHost.ts"/>
|
||||
/// <reference path="project.ts"/>
|
||||
/// <reference path="typingsCache.ts"/>
|
||||
|
||||
@ -476,7 +475,6 @@ namespace ts.server {
|
||||
this.typingsCache.deleteTypingsForProject(response.projectName);
|
||||
break;
|
||||
}
|
||||
project.markAsDirty();
|
||||
this.delayUpdateProjectGraphAndInferredProjectsRefresh(project);
|
||||
}
|
||||
|
||||
@ -528,6 +526,7 @@ namespace ts.server {
|
||||
|
||||
/* @internal */
|
||||
delayUpdateProjectGraphAndInferredProjectsRefresh(project: Project) {
|
||||
project.markAsDirty();
|
||||
this.delayUpdateProjectGraph(project);
|
||||
this.delayInferredProjectsRefresh();
|
||||
}
|
||||
@ -712,36 +711,56 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
onTypeRootFileChanged(project: ConfiguredProject, fileOrFolder: string) {
|
||||
project.getCachedPartialSystem().addOrDeleteFileOrFolder(fileOrFolder, this.toPath(fileOrFolder));
|
||||
project.updateTypes();
|
||||
this.delayUpdateProjectGraphAndInferredProjectsRefresh(project);
|
||||
/*@internal*/
|
||||
watchTypeRootDirectory(root: Path, project: ConfiguredProject) {
|
||||
// TODO: This is not needed anymore with watches for failed lookup locations?
|
||||
return this.watchDirectory(
|
||||
this.host,
|
||||
root,
|
||||
fileOrFolder => {
|
||||
project.getCachedPartialSystem().addOrDeleteFileOrFolder(fileOrFolder, this.toPath(fileOrFolder));
|
||||
project.updateTypes();
|
||||
this.delayUpdateProjectGraphAndInferredProjectsRefresh(project);
|
||||
},
|
||||
WatchDirectoryFlags.None,
|
||||
WatchType.TypeRoot,
|
||||
project
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the callback function when a watched directory has added or removed source code files.
|
||||
* @param project the project that associates with this directory watcher
|
||||
* @param fileName the absolute file name that changed in watched directory
|
||||
* This is to watch whenever files are added or removed to the wildcard directories
|
||||
*/
|
||||
/* @internal */
|
||||
onFileAddOrRemoveInWatchedDirectoryOfProject(project: ConfiguredProject, fileOrFolder: string) {
|
||||
project.getCachedPartialSystem().addOrDeleteFileOrFolder(fileOrFolder, this.toPath(fileOrFolder));
|
||||
const configFilename = project.getConfigFilePath();
|
||||
/*@internal*/
|
||||
watchWildcardDirectory(directory: Path, flags: WatchDirectoryFlags, project: ConfiguredProject) {
|
||||
return this.watchDirectory(
|
||||
this.host,
|
||||
directory,
|
||||
fileOrFolder => {
|
||||
const fileOrFolderPath = this.toPath(fileOrFolder);
|
||||
project.getCachedPartialSystem().addOrDeleteFileOrFolder(fileOrFolder, fileOrFolderPath);
|
||||
const configFilename = project.getConfigFilePath();
|
||||
|
||||
// If a change was made inside "folder/file", node will trigger the callback twice:
|
||||
// one with the fileName being "folder/file", and the other one with "folder".
|
||||
// We don't respond to the second one.
|
||||
if (fileOrFolder && !isSupportedSourceFileName(fileOrFolder, project.getCompilerOptions(), this.hostConfiguration.extraFileExtensions)) {
|
||||
this.logger.info(`Project: ${configFilename} Detected file add/remove of non supported extension: ${fileOrFolder}`);
|
||||
return;
|
||||
}
|
||||
// If the the added or created file or folder 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}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const configFileSpecs = project.configFileSpecs;
|
||||
const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFilename), project.getCompilerOptions(), project.getCachedPartialSystem(), this.hostConfiguration.extraFileExtensions);
|
||||
project.updateErrorOnNoInputFiles(result.fileNames.length !== 0);
|
||||
this.updateNonInferredProjectFiles(project, result.fileNames, fileNamePropertyReader, /*clientFileName*/ undefined);
|
||||
this.delayUpdateProjectGraphAndInferredProjectsRefresh(project);
|
||||
// Reload is pending, do the reload
|
||||
if (!project.pendingReload) {
|
||||
const configFileSpecs = project.configFileSpecs;
|
||||
const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFilename), project.getCompilationSettings(), project.getCachedPartialSystem(), this.hostConfiguration.extraFileExtensions);
|
||||
project.updateErrorOnNoInputFiles(result.fileNames.length !== 0);
|
||||
this.updateNonInferredProjectFiles(project, result.fileNames, fileNamePropertyReader, /*clientFileName*/ undefined);
|
||||
this.delayUpdateProjectGraphAndInferredProjectsRefresh(project);
|
||||
}
|
||||
},
|
||||
flags,
|
||||
WatchType.WildcardDirectories,
|
||||
project
|
||||
);
|
||||
}
|
||||
|
||||
private onConfigChangedForConfiguredProject(project: ConfiguredProject, eventKind: FileWatcherEventKind) {
|
||||
@ -1253,7 +1272,7 @@ namespace ts.server {
|
||||
return findProjectByName(projectFileName, this.externalProjects);
|
||||
}
|
||||
|
||||
private convertConfigFileContentToProjectOptions(configFilename: string, cachedServerHost: PartialSystem) {
|
||||
private convertConfigFileContentToProjectOptions(configFilename: string, cachedPartialSystem: CachedPartialSystem) {
|
||||
configFilename = normalizePath(configFilename);
|
||||
|
||||
const configFileContent = this.host.readFile(configFilename);
|
||||
@ -1265,7 +1284,7 @@ namespace ts.server {
|
||||
const errors = result.parseDiagnostics;
|
||||
const parsedCommandLine = parseJsonSourceFileConfigFileContent(
|
||||
result,
|
||||
cachedServerHost,
|
||||
cachedPartialSystem,
|
||||
getDirectoryPath(configFilename),
|
||||
/*existingOptions*/ {},
|
||||
configFilename,
|
||||
@ -1350,7 +1369,7 @@ namespace ts.server {
|
||||
const data: ProjectInfoTelemetryEventData = {
|
||||
projectId: this.host.createHash(projectKey),
|
||||
fileStats: countEachFileTypes(project.getScriptInfos()),
|
||||
compilerOptions: convertCompilerOptionsForTelemetry(project.getCompilerOptions()),
|
||||
compilerOptions: convertCompilerOptionsForTelemetry(project.getCompilationSettings()),
|
||||
typeAcquisition: convertTypeAcquisition(project.getTypeAcquisition()),
|
||||
extends: projectOptions && projectOptions.configHasExtendsProperty,
|
||||
files: projectOptions && projectOptions.configHasFilesProperty,
|
||||
@ -1435,8 +1454,8 @@ namespace ts.server {
|
||||
const normalizedPath = toNormalizedPath(newRootFile);
|
||||
let scriptInfo: ScriptInfo | NormalizedPath;
|
||||
let path: Path;
|
||||
// Use the project's lsHost so that it can use caching instead of reaching to disk for the query
|
||||
if (!project.lsHost.fileExists(newRootFile)) {
|
||||
// Use the project's fileExists so that it can use caching instead of reaching to disk for the query
|
||||
if (!project.fileExists(newRootFile)) {
|
||||
path = normalizedPathToPath(normalizedPath, this.currentDirectory, this.toCanonicalFileName);
|
||||
const existingValue = projectRootFilesMap.get(path);
|
||||
if (isScriptInfo(existingValue)) {
|
||||
@ -1448,7 +1467,7 @@ namespace ts.server {
|
||||
else {
|
||||
const scriptKind = propertyReader.getScriptKind(f);
|
||||
const hasMixedContent = propertyReader.hasMixedContent(f, this.hostConfiguration.extraFileExtensions);
|
||||
scriptInfo = this.getOrCreateScriptInfoForNormalizedPath(normalizedPath, /*openedByClient*/ clientFileName === newRootFile, /*fileContent*/ undefined, scriptKind, hasMixedContent, project.lsHost.host);
|
||||
scriptInfo = this.getOrCreateScriptInfoForNormalizedPath(normalizedPath, /*openedByClient*/ clientFileName === newRootFile, /*fileContent*/ undefined, scriptKind, hasMixedContent, project.partialSystem);
|
||||
path = scriptInfo.path;
|
||||
// If this script info is not already a root add it
|
||||
if (!project.isRoot(scriptInfo)) {
|
||||
|
||||
@ -1,137 +0,0 @@
|
||||
/// <reference path="..\services\services.ts" />
|
||||
/// <reference path="utilities.ts" />
|
||||
/// <reference path="scriptInfo.ts" />
|
||||
/// <reference path="..\compiler\resolutionCache.ts" />
|
||||
|
||||
namespace ts.server {
|
||||
export class LSHost implements LanguageServiceHost, ModuleResolutionHost {
|
||||
/*@internal*/
|
||||
compilationSettings: CompilerOptions;
|
||||
|
||||
readonly trace: (s: string) => void;
|
||||
readonly realpath?: (path: string) => string;
|
||||
|
||||
/*@internal*/
|
||||
hasInvalidatedResolution: HasInvalidatedResolution;
|
||||
|
||||
/**
|
||||
* This is the host that is associated with the project. This is normally same as projectService's host
|
||||
* except in Configured projects where it is CachedServerHost so that we can cache the results of the
|
||||
* file system entries as we would anyways be watching files in the project (so safe to cache)
|
||||
*/
|
||||
/*@internal*/
|
||||
host: PartialSystem;
|
||||
|
||||
constructor(host: PartialSystem, private project: Project, private readonly cancellationToken: HostCancellationToken) {
|
||||
this.host = host;
|
||||
this.cancellationToken = new ThrottledCancellationToken(cancellationToken, project.projectService.throttleWaitMilliseconds);
|
||||
|
||||
const serverHost = this.getServerHost();
|
||||
if (serverHost.trace) {
|
||||
this.trace = s => serverHost.trace(s);
|
||||
}
|
||||
|
||||
if (serverHost.realpath) {
|
||||
this.realpath = path => serverHost.realpath(path);
|
||||
}
|
||||
}
|
||||
|
||||
private getServerHost() {
|
||||
return this.project.projectService.host;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.project = undefined;
|
||||
this.host = undefined;
|
||||
}
|
||||
|
||||
getNewLine() {
|
||||
return this.host.newLine;
|
||||
}
|
||||
|
||||
getProjectVersion() {
|
||||
return this.project.getProjectVersion();
|
||||
}
|
||||
|
||||
getCompilationSettings() {
|
||||
return this.compilationSettings;
|
||||
}
|
||||
|
||||
useCaseSensitiveFileNames() {
|
||||
return this.host.useCaseSensitiveFileNames;
|
||||
}
|
||||
|
||||
getCancellationToken() {
|
||||
return this.cancellationToken;
|
||||
}
|
||||
|
||||
resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[] {
|
||||
return this.project.resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile);
|
||||
}
|
||||
|
||||
resolveModuleNames(moduleNames: string[], containingFile: string): ResolvedModuleFull[] {
|
||||
return this.project.resolutionCache.resolveModuleNames(moduleNames, containingFile, /*logChanges*/ true);
|
||||
}
|
||||
|
||||
getDefaultLibFileName() {
|
||||
const nodeModuleBinDir = getDirectoryPath(normalizePath(this.getServerHost().getExecutingFilePath()));
|
||||
return combinePaths(nodeModuleBinDir, getDefaultLibFileName(this.compilationSettings));
|
||||
}
|
||||
|
||||
getScriptSnapshot(filename: string): IScriptSnapshot {
|
||||
const scriptInfo = this.project.getScriptInfoLSHost(filename);
|
||||
if (scriptInfo) {
|
||||
return scriptInfo.getSnapshot();
|
||||
}
|
||||
}
|
||||
|
||||
getScriptFileNames() {
|
||||
return this.project.getRootFilesLSHost();
|
||||
}
|
||||
|
||||
getTypeRootsVersion() {
|
||||
return this.project.typesVersion;
|
||||
}
|
||||
|
||||
getScriptKind(fileName: string) {
|
||||
const info = this.project.getScriptInfoLSHost(fileName);
|
||||
return info && info.scriptKind;
|
||||
}
|
||||
|
||||
getScriptVersion(filename: string) {
|
||||
const info = this.project.getScriptInfoLSHost(filename);
|
||||
return info && info.getLatestVersion();
|
||||
}
|
||||
|
||||
getCurrentDirectory(): string {
|
||||
return this.host.getCurrentDirectory();
|
||||
}
|
||||
|
||||
resolvePath(path: string): string {
|
||||
return this.getServerHost().resolvePath(path);
|
||||
}
|
||||
|
||||
fileExists(file: string): boolean {
|
||||
// As an optimization, don't hit the disks for files we already know don't exist
|
||||
// (because we're watching for their creation).
|
||||
const path = this.project.projectService.toPath(file);
|
||||
return !this.project.isWatchedMissingFile(path) && this.host.fileExists(file);
|
||||
}
|
||||
|
||||
readFile(fileName: string): string | undefined {
|
||||
return this.host.readFile(fileName);
|
||||
}
|
||||
|
||||
directoryExists(path: string): boolean {
|
||||
return this.host.directoryExists(path);
|
||||
}
|
||||
|
||||
readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[] {
|
||||
return this.host.readDirectory(path, extensions, exclude, include, depth);
|
||||
}
|
||||
|
||||
getDirectories(path: string): string[] {
|
||||
return this.host.getDirectories(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
/// <reference path="..\services\services.ts" />
|
||||
/// <reference path="utilities.ts"/>
|
||||
/// <reference path="scriptInfo.ts"/>
|
||||
/// <reference path="lsHost.ts"/>
|
||||
/// <reference path="..\compiler\resolutionCache.ts"/>
|
||||
/// <reference path="typingsCache.ts"/>
|
||||
/// <reference path="..\compiler\builder.ts"/>
|
||||
|
||||
@ -112,7 +112,7 @@ namespace ts.server {
|
||||
return value instanceof ScriptInfo;
|
||||
}
|
||||
|
||||
export abstract class Project {
|
||||
export abstract class Project implements LanguageServiceHost, ModuleResolutionHost {
|
||||
private rootFiles: ScriptInfo[] = [];
|
||||
private rootFilesMap: Map<ProjectRoot> = createMap<ProjectRoot>();
|
||||
private program: Program;
|
||||
@ -127,11 +127,14 @@ namespace ts.server {
|
||||
|
||||
public languageServiceEnabled = true;
|
||||
|
||||
/*@internal*/
|
||||
resolutionCache: ResolutionCache;
|
||||
readonly trace?: (s: string) => void;
|
||||
readonly realpath?: (path: string) => string;
|
||||
|
||||
/*@internal*/
|
||||
lsHost: LSHost;
|
||||
hasInvalidatedResolution: HasInvalidatedResolution;
|
||||
|
||||
/*@internal*/
|
||||
resolutionCache: ResolutionCache;
|
||||
|
||||
private builder: Builder;
|
||||
/**
|
||||
@ -161,7 +164,7 @@ namespace ts.server {
|
||||
|
||||
private typingFiles: SortedReadonlyArray<string>;
|
||||
|
||||
public typesVersion = 0;
|
||||
private typesVersion = 0;
|
||||
|
||||
public isNonTsProject() {
|
||||
this.updateGraph();
|
||||
@ -190,7 +193,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly projectName: string,
|
||||
/*@internal*/readonly projectName: string,
|
||||
readonly projectKind: ProjectKind,
|
||||
readonly projectService: ProjectService,
|
||||
private documentRegistry: DocumentRegistry,
|
||||
@ -198,7 +201,7 @@ namespace ts.server {
|
||||
languageServiceEnabled: boolean,
|
||||
private compilerOptions: CompilerOptions,
|
||||
public compileOnSaveEnabled: boolean,
|
||||
host: PartialSystem) {
|
||||
/*@internal*/public partialSystem: PartialSystem) {
|
||||
|
||||
if (!this.compilerOptions) {
|
||||
this.compilerOptions = getDefaultCompilerOptions();
|
||||
@ -211,52 +214,162 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
this.setInternalCompilerOptionsForEmittingJsFiles();
|
||||
const host = this.projectService.host;
|
||||
if (host.trace) {
|
||||
this.trace = s => host.trace(s);
|
||||
}
|
||||
|
||||
this.lsHost = new LSHost(host, this, this.projectService.cancellationToken);
|
||||
this.resolutionCache = createResolutionCache(
|
||||
fileName => this.projectService.toPath(fileName),
|
||||
() => this.compilerOptions,
|
||||
directory => this.watchDirectoryOfFailedLookup(directory),
|
||||
s => this.projectService.logger.info(s),
|
||||
this.getProjectName(),
|
||||
() => this.getTypeAcquisition().enable ? this.projectService.typingsInstaller.globalTypingsCacheLocation : undefined
|
||||
);
|
||||
this.lsHost.compilationSettings = this.compilerOptions;
|
||||
this.resolutionCache.setModuleResolutionHost(this.lsHost);
|
||||
|
||||
this.languageService = createLanguageService(this.lsHost, this.documentRegistry);
|
||||
if (host.realpath) {
|
||||
this.realpath = path => host.realpath(path);
|
||||
}
|
||||
|
||||
this.languageService = createLanguageService(this, this.documentRegistry);
|
||||
if (!languageServiceEnabled) {
|
||||
this.disableLanguageService();
|
||||
}
|
||||
|
||||
this.resolutionCache = createResolutionCache(this);
|
||||
this.markAsDirty();
|
||||
}
|
||||
|
||||
private watchDirectoryOfFailedLookup(directory: string) {
|
||||
getCompilationSettings() {
|
||||
return this.compilerOptions;
|
||||
}
|
||||
|
||||
getNewLine() {
|
||||
return this.partialSystem.newLine;
|
||||
}
|
||||
|
||||
getProjectVersion() {
|
||||
return this.projectStateVersion.toString();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private getScriptInfoLSHost(fileName: string) {
|
||||
const scriptInfo = this.projectService.getOrCreateScriptInfo(fileName, /*openedByClient*/ false, this.partialSystem);
|
||||
if (scriptInfo) {
|
||||
const existingValue = this.rootFilesMap.get(scriptInfo.path);
|
||||
if (existingValue !== undefined && existingValue !== scriptInfo) {
|
||||
// This was missing path earlier but now the file exists. Update the root
|
||||
this.rootFiles.push(scriptInfo);
|
||||
this.rootFilesMap.set(scriptInfo.path, scriptInfo);
|
||||
}
|
||||
scriptInfo.attachToProject(this);
|
||||
}
|
||||
return scriptInfo;
|
||||
}
|
||||
|
||||
getScriptKind(fileName: string) {
|
||||
const info = this.getScriptInfoLSHost(fileName);
|
||||
return info && info.scriptKind;
|
||||
}
|
||||
|
||||
getScriptVersion(filename: string) {
|
||||
const info = this.getScriptInfoLSHost(filename);
|
||||
return info && info.getLatestVersion();
|
||||
}
|
||||
|
||||
getScriptSnapshot(filename: string): IScriptSnapshot {
|
||||
const scriptInfo = this.getScriptInfoLSHost(filename);
|
||||
if (scriptInfo) {
|
||||
return scriptInfo.getSnapshot();
|
||||
}
|
||||
}
|
||||
|
||||
getCancellationToken() {
|
||||
return this.projectService.cancellationToken;
|
||||
}
|
||||
|
||||
getCurrentDirectory(): string {
|
||||
return this.partialSystem.getCurrentDirectory();
|
||||
}
|
||||
|
||||
getDefaultLibFileName() {
|
||||
const nodeModuleBinDir = getDirectoryPath(normalizePath(this.projectService.host.getExecutingFilePath()));
|
||||
return combinePaths(nodeModuleBinDir, getDefaultLibFileName(this.compilerOptions));
|
||||
}
|
||||
|
||||
useCaseSensitiveFileNames() {
|
||||
return this.partialSystem.useCaseSensitiveFileNames;
|
||||
}
|
||||
|
||||
readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[] {
|
||||
return this.partialSystem.readDirectory(path, extensions, exclude, include, depth);
|
||||
}
|
||||
|
||||
readFile(fileName: string): string | undefined {
|
||||
return this.partialSystem.readFile(fileName);
|
||||
}
|
||||
|
||||
fileExists(file: string): boolean {
|
||||
// As an optimization, don't hit the disks for files we already know don't exist
|
||||
// (because we're watching for their creation).
|
||||
const path = this.toPath(file);
|
||||
return !this.isWatchedMissingFile(path) && this.partialSystem.fileExists(file);
|
||||
}
|
||||
|
||||
getTypeRootsVersion() {
|
||||
return this.typesVersion;
|
||||
}
|
||||
|
||||
resolveModuleNames(moduleNames: string[], containingFile: string): ResolvedModuleFull[] {
|
||||
return this.resolutionCache.resolveModuleNames(moduleNames, containingFile, /*logChanges*/ true);
|
||||
}
|
||||
|
||||
resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[] {
|
||||
return this.resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile);
|
||||
}
|
||||
|
||||
directoryExists(path: string): boolean {
|
||||
return this.partialSystem.directoryExists(path);
|
||||
}
|
||||
|
||||
getDirectories(path: string): string[] {
|
||||
return this.partialSystem.getDirectories(path);
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
toPath(fileName: string) {
|
||||
return this.projectService.toPath(fileName);
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
watchDirectoryOfFailedLookupLocation(directory: string, cb: DirectoryWatcherCallback) {
|
||||
return this.projectService.watchDirectory(
|
||||
this.projectService.host,
|
||||
directory,
|
||||
fileOrFolder => this.onFileAddOrRemoveInDirectoryOfFailedLookup(fileOrFolder),
|
||||
cb,
|
||||
WatchDirectoryFlags.None,
|
||||
WatchType.FailedLookupLocation,
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
private onFileAddOrRemoveInDirectoryOfFailedLookup(fileOrFolder: string) {
|
||||
const fileOrFolderPath = this.projectService.toPath(fileOrFolder);
|
||||
/*@internal*/
|
||||
onInvalidatedResolution() {
|
||||
this.projectService.delayUpdateProjectGraphAndInferredProjectsRefresh(this);
|
||||
}
|
||||
|
||||
// There is some kind of change in the failed lookup location, update the program
|
||||
if (this.projectKind === ProjectKind.Configured) {
|
||||
(this.lsHost.host as CachedPartialSystem).addOrDeleteFileOrFolder(fileOrFolder, fileOrFolderPath);
|
||||
}
|
||||
|
||||
// If the location results in update to failed lookup, schedule program update
|
||||
if (this.resolutionCache.onFileAddOrRemoveInDirectoryOfFailedLookup(fileOrFolderPath)) {
|
||||
this.markAsDirty();
|
||||
this.projectService.delayUpdateProjectGraphAndInferredProjectsRefresh(this);
|
||||
}
|
||||
/*@internal*/
|
||||
getGlobalCache() {
|
||||
return this.getTypeAcquisition().enable ? this.projectService.typingsInstaller.globalTypingsCacheLocation : undefined;
|
||||
}
|
||||
|
||||
private setInternalCompilerOptionsForEmittingJsFiles() {
|
||||
@ -326,10 +439,6 @@ namespace ts.server {
|
||||
return this.builder.getChangedProgramFiles(this.program);
|
||||
}
|
||||
|
||||
getProjectVersion() {
|
||||
return this.projectStateVersion.toString();
|
||||
}
|
||||
|
||||
enableLanguageService() {
|
||||
if (this.languageServiceEnabled) {
|
||||
return;
|
||||
@ -366,7 +475,6 @@ namespace ts.server {
|
||||
|
||||
updateTypes() {
|
||||
this.typesVersion++;
|
||||
this.markAsDirty();
|
||||
}
|
||||
|
||||
close() {
|
||||
@ -395,8 +503,7 @@ namespace ts.server {
|
||||
this.resolutionCache.clear();
|
||||
this.resolutionCache = undefined;
|
||||
this.cachedUnresolvedImportsPerFile = undefined;
|
||||
this.lsHost.dispose();
|
||||
this.lsHost = undefined;
|
||||
this.partialSystem = undefined;
|
||||
|
||||
// Clean up file watchers waiting for missing files
|
||||
if (this.missingFilesMap) {
|
||||
@ -410,11 +517,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
isClosed() {
|
||||
return this.lsHost === undefined;
|
||||
}
|
||||
|
||||
getCompilerOptions() {
|
||||
return this.compilerOptions;
|
||||
return this.rootFiles === undefined;
|
||||
}
|
||||
|
||||
hasRoots() {
|
||||
@ -425,25 +528,6 @@ namespace ts.server {
|
||||
return this.rootFiles && this.rootFiles.map(info => info.fileName);
|
||||
}
|
||||
|
||||
getRootFilesLSHost() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
getRootFilesMap() {
|
||||
return this.rootFilesMap;
|
||||
@ -622,7 +706,7 @@ namespace ts.server {
|
||||
*/
|
||||
updateGraph(): boolean {
|
||||
this.resolutionCache.startRecordingFilesWithChangedResolutions();
|
||||
this.lsHost.hasInvalidatedResolution = this.resolutionCache.createHasInvalidatedResolution();
|
||||
this.hasInvalidatedResolution = this.resolutionCache.createHasInvalidatedResolution();
|
||||
|
||||
let hasChanges = this.updateGraphWorker();
|
||||
|
||||
@ -659,7 +743,7 @@ namespace ts.server {
|
||||
// Note we are retaining builder so we can send events for project change
|
||||
if (this.builder) {
|
||||
if (this.languageServiceEnabled) {
|
||||
this.builder.onProgramUpdateGraph(this.program, this.lsHost.hasInvalidatedResolution);
|
||||
this.builder.onProgramUpdateGraph(this.program, this.hasInvalidatedResolution);
|
||||
}
|
||||
else {
|
||||
this.builder.clear();
|
||||
@ -720,7 +804,7 @@ namespace ts.server {
|
||||
// by the LSHost for files in the program when the program is retrieved above but
|
||||
// the program doesn't contain external files so this must be done explicitly.
|
||||
inserted => {
|
||||
const scriptInfo = this.projectService.getOrCreateScriptInfo(inserted, /*openedByClient*/ false, this.lsHost.host);
|
||||
const scriptInfo = this.projectService.getOrCreateScriptInfo(inserted, /*openedByClient*/ false, this.partialSystem);
|
||||
scriptInfo.attachToProject(this);
|
||||
},
|
||||
removed => {
|
||||
@ -739,7 +823,7 @@ namespace ts.server {
|
||||
missingFilePath,
|
||||
(fileName, eventKind) => {
|
||||
if (this.projectKind === ProjectKind.Configured) {
|
||||
(this.lsHost.host as CachedPartialSystem).addOrDeleteFile(fileName, missingFilePath, eventKind);
|
||||
(this.partialSystem as CachedPartialSystem).addOrDeleteFile(fileName, missingFilePath, eventKind);
|
||||
}
|
||||
|
||||
if (eventKind === FileWatcherEventKind.Created && this.missingFilesMap.has(missingFilePath)) {
|
||||
@ -747,7 +831,6 @@ namespace ts.server {
|
||||
fileWatcher.close();
|
||||
|
||||
// When a missing file is created, we should update the graph.
|
||||
this.markAsDirty();
|
||||
this.projectService.delayUpdateProjectGraphAndInferredProjectsRefresh(this);
|
||||
}
|
||||
},
|
||||
@ -757,28 +840,14 @@ namespace ts.server {
|
||||
return fileWatcher;
|
||||
}
|
||||
|
||||
isWatchedMissingFile(path: Path) {
|
||||
private isWatchedMissingFile(path: Path) {
|
||||
return this.missingFilesMap && this.missingFilesMap.has(path);
|
||||
}
|
||||
|
||||
getScriptInfoLSHost(fileName: string) {
|
||||
const scriptInfo = this.projectService.getOrCreateScriptInfo(fileName, /*openedByClient*/ false, this.lsHost.host);
|
||||
if (scriptInfo) {
|
||||
const existingValue = this.rootFilesMap.get(scriptInfo.path);
|
||||
if (existingValue !== undefined && existingValue !== scriptInfo) {
|
||||
// This was missing path earlier but now the file exists. Update the root
|
||||
this.rootFiles.push(scriptInfo);
|
||||
this.rootFilesMap.set(scriptInfo.path, scriptInfo);
|
||||
}
|
||||
scriptInfo.attachToProject(this);
|
||||
}
|
||||
return scriptInfo;
|
||||
}
|
||||
|
||||
getScriptInfoForNormalizedPath(fileName: NormalizedPath) {
|
||||
const scriptInfo = this.projectService.getOrCreateScriptInfoForNormalizedPath(
|
||||
fileName, /*openedByClient*/ false, /*fileContent*/ undefined,
|
||||
/*scriptKind*/ undefined, /*hasMixedContent*/ undefined, this.lsHost.host
|
||||
/*scriptKind*/ undefined, /*hasMixedContent*/ undefined, this.partialSystem
|
||||
);
|
||||
if (scriptInfo && !scriptInfo.isAttached(this)) {
|
||||
return Errors.ThrowProjectDoesNotContainDocument(fileName, this);
|
||||
@ -815,8 +884,6 @@ namespace ts.server {
|
||||
if (changesAffectModuleResolution(oldOptions, compilerOptions)) {
|
||||
this.resolutionCache.clear();
|
||||
}
|
||||
this.lsHost.compilationSettings = this.compilerOptions;
|
||||
|
||||
this.markAsDirty();
|
||||
}
|
||||
}
|
||||
@ -839,7 +906,7 @@ namespace ts.server {
|
||||
projectName: this.getProjectName(),
|
||||
version: this.projectStructureVersion,
|
||||
isInferred: this.projectKind === ProjectKind.Inferred,
|
||||
options: this.getCompilerOptions(),
|
||||
options: this.getCompilationSettings(),
|
||||
languageServiceDisabled: !this.languageServiceEnabled
|
||||
};
|
||||
const updatedFileNames = this.updatedFileNames;
|
||||
@ -913,7 +980,7 @@ namespace ts.server {
|
||||
|
||||
setCompilerOptions(options?: CompilerOptions) {
|
||||
// Avoid manipulating the given options directly
|
||||
const newOptions = options ? cloneCompilerOptions(options) : this.getCompilerOptions();
|
||||
const newOptions = options ? cloneCompilerOptions(options) : this.getCompilationSettings();
|
||||
if (!newOptions) {
|
||||
return;
|
||||
}
|
||||
@ -1042,7 +1109,7 @@ namespace ts.server {
|
||||
|
||||
/*@internal*/
|
||||
getCachedPartialSystem() {
|
||||
return this.lsHost.host as CachedPartialSystem;
|
||||
return this.partialSystem as CachedPartialSystem;
|
||||
}
|
||||
|
||||
getConfigFilePath() {
|
||||
@ -1051,7 +1118,7 @@ namespace ts.server {
|
||||
|
||||
enablePlugins() {
|
||||
const host = this.projectService.host;
|
||||
const options = this.getCompilerOptions();
|
||||
const options = this.getCompilationSettings();
|
||||
|
||||
if (!host.require) {
|
||||
this.projectService.logger.info("Plugins were requested but not running in environment that supports 'require'. Nothing will be loaded");
|
||||
@ -1113,7 +1180,7 @@ namespace ts.server {
|
||||
config: configEntry,
|
||||
project: this,
|
||||
languageService: this.languageService,
|
||||
languageServiceHost: this.lsHost,
|
||||
languageServiceHost: this,
|
||||
serverHost: this.projectService.host
|
||||
};
|
||||
|
||||
@ -1174,14 +1241,7 @@ namespace ts.server {
|
||||
this.directoriesWatchedForWildcards || (this.directoriesWatchedForWildcards = createMap()),
|
||||
wildcardDirectories,
|
||||
// Create new directory watcher
|
||||
(directory, flags) => this.projectService.watchDirectory(
|
||||
this.projectService.host,
|
||||
directory,
|
||||
path => this.projectService.onFileAddOrRemoveInWatchedDirectoryOfProject(this, path),
|
||||
flags,
|
||||
WatchType.WildcardDirectories,
|
||||
this
|
||||
)
|
||||
(directory, flags) => this.projectService.watchWildcardDirectory(directory as Path, flags, this),
|
||||
);
|
||||
}
|
||||
|
||||
@ -1201,14 +1261,7 @@ namespace ts.server {
|
||||
newTypeRoots,
|
||||
{
|
||||
// Create new watch
|
||||
createNewValue: root => this.projectService.watchDirectory(
|
||||
this.projectService.host,
|
||||
root,
|
||||
path => this.projectService.onTypeRootFileChanged(this, path),
|
||||
WatchDirectoryFlags.None,
|
||||
WatchType.TypeRoot,
|
||||
this
|
||||
),
|
||||
createNewValue: root => this.projectService.watchTypeRootDirectory(root as Path, this),
|
||||
// Close existing watch thats not needed any more
|
||||
onDeleteValue: closeFileWatcher
|
||||
}
|
||||
@ -1245,7 +1298,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getEffectiveTypeRoots() {
|
||||
return getEffectiveTypeRoots(this.getCompilerOptions(), this.lsHost.host) || [];
|
||||
return getEffectiveTypeRoots(this.getCompilationSettings(), this.partialSystem) || [];
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
|
||||
@ -237,7 +237,7 @@ namespace ts.server {
|
||||
detachAllProjects() {
|
||||
for (const p of this.containingProjects) {
|
||||
if (p.projectKind === ProjectKind.Configured) {
|
||||
(p.lsHost.host as CachedPartialSystem).addOrDeleteFile(this.fileName, this.path, FileWatcherEventKind.Deleted);
|
||||
(p.partialSystem as CachedPartialSystem).addOrDeleteFile(this.fileName, this.path, FileWatcherEventKind.Deleted);
|
||||
}
|
||||
const isInfoRoot = p.isRoot(this);
|
||||
// detach is unnecessary since we'll clean the list of containing projects anyways
|
||||
|
||||
@ -1218,7 +1218,7 @@ namespace ts.server {
|
||||
result.push({
|
||||
projectFileName: project.getProjectName(),
|
||||
fileNames: project.getCompileOnSaveAffectedFileList(info),
|
||||
projectUsesOutFile: !!project.getCompilerOptions().outFile || !!project.getCompilerOptions().out
|
||||
projectUsesOutFile: !!project.getCompilationSettings().outFile || !!project.getCompilationSettings().out
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,7 +15,6 @@
|
||||
"utilities.ts",
|
||||
"scriptVersionCache.ts",
|
||||
"scriptInfo.ts",
|
||||
"lsHost.ts",
|
||||
"typingsCache.ts",
|
||||
"project.ts",
|
||||
"editorServices.ts",
|
||||
|
||||
@ -16,7 +16,6 @@
|
||||
},
|
||||
"files": [
|
||||
"editorServices.ts",
|
||||
"lsHost.ts",
|
||||
"project.ts",
|
||||
"protocol.ts",
|
||||
"scriptInfo.ts",
|
||||
|
||||
@ -89,12 +89,12 @@ namespace ts.server {
|
||||
if (forceRefresh ||
|
||||
!entry ||
|
||||
typeAcquisitionChanged(typeAcquisition, entry.typeAcquisition) ||
|
||||
compilerOptionsChanged(project.getCompilerOptions(), entry.compilerOptions) ||
|
||||
compilerOptionsChanged(project.getCompilationSettings(), entry.compilerOptions) ||
|
||||
unresolvedImportsChanged(unresolvedImports, entry.unresolvedImports)) {
|
||||
// Note: entry is now poisoned since it does not really contain typings for a given combination of compiler options\typings options.
|
||||
// instead it acts as a placeholder to prevent issuing multiple requests
|
||||
this.perProjectCache.set(project.getProjectName(), {
|
||||
compilerOptions: project.getCompilerOptions(),
|
||||
compilerOptions: project.getCompilationSettings(),
|
||||
typeAcquisition,
|
||||
typings: result,
|
||||
unresolvedImports,
|
||||
@ -125,4 +125,4 @@ namespace ts.server {
|
||||
this.installer.onProjectClosed(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,7 +50,7 @@ namespace ts.server {
|
||||
return {
|
||||
projectName: project.getProjectName(),
|
||||
fileNames: project.getFileNames(/*excludeFilesFromExternalLibraries*/ true, /*excludeConfigFiles*/ true),
|
||||
compilerOptions: project.getCompilerOptions(),
|
||||
compilerOptions: project.getCompilationSettings(),
|
||||
typeAcquisition,
|
||||
unresolvedImports,
|
||||
projectRootPath: getProjectRootPath(project),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user