Use cache for module resolution even in watch mode
This commit is contained in:
parent
031a63762f
commit
0d5e6c9de5
198
src/compiler/resolutionCache.ts
Normal file
198
src/compiler/resolutionCache.ts
Normal file
|
@ -0,0 +1,198 @@
|
|||
/// <reference path="types.ts"/>
|
||||
/// <reference path="core.ts"/>
|
||||
|
||||
namespace ts {
|
||||
export interface ResolutionCache {
|
||||
setModuleResolutionHost(host: ModuleResolutionHost): void;
|
||||
startRecordingFilesWithChangedResolutions(): void;
|
||||
finishRecordingFilesWithChangedResolutions(): Path[];
|
||||
resolveModuleNames(moduleNames: string[], containingFile: string, logChanges: boolean): ResolvedModuleFull[];
|
||||
resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
|
||||
invalidateResolutionOfDeletedFile(filePath: Path): void;
|
||||
clear(): void;
|
||||
}
|
||||
|
||||
type NameResolutionWithFailedLookupLocations = { failedLookupLocations: string[], isInvalidated?: boolean };
|
||||
type ResolverWithGlobalCache = (primaryResult: ResolvedModuleWithFailedLookupLocations, moduleName: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost) => ResolvedModuleWithFailedLookupLocations | undefined;
|
||||
|
||||
/*@internal*/
|
||||
export function resolveWithGlobalCache(primaryResult: ResolvedModuleWithFailedLookupLocations, moduleName: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, globalCache: string | undefined, projectName: string): ResolvedModuleWithFailedLookupLocations | undefined {
|
||||
if (!isExternalModuleNameRelative(moduleName) && !(primaryResult.resolvedModule && extensionIsTypeScript(primaryResult.resolvedModule.extension)) && globalCache !== undefined) {
|
||||
// otherwise try to load typings from @types
|
||||
|
||||
// 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);
|
||||
if (resolvedModule) {
|
||||
return { resolvedModule, failedLookupLocations: primaryResult.failedLookupLocations.concat(failedLookupLocations) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
export function createResolutionCache(
|
||||
toPath: (fileName: string) => Path,
|
||||
getCompilerOptions: () => CompilerOptions,
|
||||
resolveWithGlobalCache?: ResolverWithGlobalCache): ResolutionCache {
|
||||
|
||||
let host: ModuleResolutionHost;
|
||||
let filesWithChangedSetOfUnresolvedImports: Path[];
|
||||
const resolvedModuleNames = createMap<Map<ResolvedModuleWithFailedLookupLocations>>();
|
||||
const resolvedTypeReferenceDirectives = createMap<Map<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
|
||||
|
||||
return {
|
||||
setModuleResolutionHost,
|
||||
startRecordingFilesWithChangedResolutions,
|
||||
finishRecordingFilesWithChangedResolutions,
|
||||
resolveModuleNames,
|
||||
resolveTypeReferenceDirectives,
|
||||
invalidateResolutionOfDeletedFile,
|
||||
clear
|
||||
};
|
||||
|
||||
function setModuleResolutionHost(updatedHost: ModuleResolutionHost) {
|
||||
host = updatedHost;
|
||||
}
|
||||
|
||||
function clear() {
|
||||
resolvedModuleNames.clear();
|
||||
resolvedTypeReferenceDirectives.clear();
|
||||
}
|
||||
|
||||
function startRecordingFilesWithChangedResolutions() {
|
||||
filesWithChangedSetOfUnresolvedImports = [];
|
||||
}
|
||||
|
||||
function finishRecordingFilesWithChangedResolutions() {
|
||||
const collected = filesWithChangedSetOfUnresolvedImports;
|
||||
filesWithChangedSetOfUnresolvedImports = undefined;
|
||||
return collected;
|
||||
}
|
||||
|
||||
function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
|
||||
const primaryResult = ts.resolveModuleName(moduleName, containingFile, compilerOptions, host);
|
||||
// return result immediately only if it is .ts, .tsx or .d.ts
|
||||
// otherwise try to load typings from @types
|
||||
return (resolveWithGlobalCache && resolveWithGlobalCache(primaryResult, moduleName, compilerOptions, host)) || primaryResult;
|
||||
}
|
||||
|
||||
function resolveNamesWithLocalCache<T extends NameResolutionWithFailedLookupLocations, R>(
|
||||
names: string[],
|
||||
containingFile: string,
|
||||
cache: Map<Map<T>>,
|
||||
loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost) => T,
|
||||
getResult: (s: T) => R,
|
||||
getResultFileName: (result: R) => string | undefined,
|
||||
logChanges: boolean): R[] {
|
||||
|
||||
const path = toPath(containingFile);
|
||||
const currentResolutionsInFile = cache.get(path);
|
||||
|
||||
const newResolutions: Map<T> = createMap<T>();
|
||||
const resolvedModules: R[] = [];
|
||||
const compilerOptions = getCompilerOptions();
|
||||
|
||||
for (const name of names) {
|
||||
// check if this is a duplicate entry in the list
|
||||
let resolution = newResolutions.get(name);
|
||||
if (!resolution) {
|
||||
const existingResolution = currentResolutionsInFile && currentResolutionsInFile.get(name);
|
||||
if (moduleResolutionIsValid(existingResolution)) {
|
||||
// ok, it is safe to use existing name resolution results
|
||||
resolution = existingResolution;
|
||||
}
|
||||
else {
|
||||
resolution = loader(name, containingFile, compilerOptions, host);
|
||||
newResolutions.set(name, resolution);
|
||||
}
|
||||
if (logChanges && filesWithChangedSetOfUnresolvedImports && !resolutionIsEqualTo(existingResolution, resolution)) {
|
||||
filesWithChangedSetOfUnresolvedImports.push(path);
|
||||
// reset log changes to avoid recording the same file multiple times
|
||||
logChanges = false;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.assert(resolution !== undefined);
|
||||
|
||||
resolvedModules.push(getResult(resolution));
|
||||
}
|
||||
|
||||
// replace old results with a new one
|
||||
cache.set(path, newResolutions);
|
||||
return resolvedModules;
|
||||
|
||||
function resolutionIsEqualTo(oldResolution: T, newResolution: T): boolean {
|
||||
if (oldResolution === newResolution) {
|
||||
return true;
|
||||
}
|
||||
if (!oldResolution || !newResolution || oldResolution.isInvalidated) {
|
||||
return false;
|
||||
}
|
||||
const oldResult = getResult(oldResolution);
|
||||
const newResult = getResult(newResolution);
|
||||
if (oldResult === newResult) {
|
||||
return true;
|
||||
}
|
||||
if (!oldResult || !newResult) {
|
||||
return false;
|
||||
}
|
||||
return getResultFileName(oldResult) === getResultFileName(newResult);
|
||||
}
|
||||
|
||||
function moduleResolutionIsValid(resolution: T): boolean {
|
||||
if (!resolution || resolution.isInvalidated) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const result = getResult(resolution);
|
||||
if (result) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// consider situation if we have no candidate locations as valid resolution.
|
||||
// after all there is no point to invalidate it if we have no idea where to look for the module.
|
||||
return resolution.failedLookupLocations.length === 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[] {
|
||||
return resolveNamesWithLocalCache(typeDirectiveNames, containingFile, resolvedTypeReferenceDirectives, resolveTypeReferenceDirective,
|
||||
m => m.resolvedTypeReferenceDirective, r => r.resolvedFileName, /*logChanges*/ false);
|
||||
}
|
||||
|
||||
function resolveModuleNames(moduleNames: string[], containingFile: string, logChanges: boolean): ResolvedModuleFull[] {
|
||||
return resolveNamesWithLocalCache(moduleNames, containingFile, resolvedModuleNames, resolveModuleName,
|
||||
m => m.resolvedModule, r => r.resolvedFileName, logChanges);
|
||||
}
|
||||
|
||||
function invalidateResolutionCacheOfDeletedFile<T extends NameResolutionWithFailedLookupLocations, R>(
|
||||
deletedFilePath: Path,
|
||||
cache: Map<Map<T>>,
|
||||
getResult: (s: T) => R,
|
||||
getResultFileName: (result: R) => string | undefined) {
|
||||
cache.forEach((value, path) => {
|
||||
if (path === deletedFilePath) {
|
||||
cache.delete(path);
|
||||
}
|
||||
else if (value) {
|
||||
value.forEach((resolution) => {
|
||||
if (resolution && !resolution.isInvalidated) {
|
||||
const result = getResult(resolution);
|
||||
if (result) {
|
||||
if (getResultFileName(result) === deletedFilePath) {
|
||||
resolution.isInvalidated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function invalidateResolutionOfDeletedFile(filePath: Path) {
|
||||
invalidateResolutionCacheOfDeletedFile(filePath, resolvedModuleNames, m => m.resolvedModule, r => r.resolvedFileName);
|
||||
invalidateResolutionCacheOfDeletedFile(filePath, resolvedTypeReferenceDirectives, m => m.resolvedTypeReferenceDirective, r => r.resolvedFileName);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -37,6 +37,7 @@
|
|||
"emitter.ts",
|
||||
"program.ts",
|
||||
"builder.ts",
|
||||
"resolutionCache.ts",
|
||||
"watchedProgram.ts",
|
||||
"commandLineParser.ts",
|
||||
"tsc.ts",
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/// <reference path="program.ts" />
|
||||
/// <reference path="builder.ts" />
|
||||
/// <reference path="resolutionCache.ts"/>
|
||||
|
||||
namespace ts {
|
||||
export type DiagnosticReporter = (diagnostic: Diagnostic) => void;
|
||||
|
@ -254,6 +255,7 @@ namespace ts {
|
|||
let timerToUpdateProgram: any; // timer callback to recompile the program
|
||||
|
||||
const sourceFilesCache = createMap<HostFileInfo | string>(); // Cache that stores the source file and version info
|
||||
|
||||
watchingHost = watchingHost || createWatchingSystemHost(compilerOptions.pretty);
|
||||
const { system, parseConfigFile, reportDiagnostic, reportWatchDiagnostic, beforeCompile, afterCompile } = watchingHost;
|
||||
|
||||
|
@ -268,6 +270,9 @@ namespace ts {
|
|||
const currentDirectory = host.getCurrentDirectory();
|
||||
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames);
|
||||
|
||||
// Cache for the module resolution
|
||||
const resolutionCache = createResolutionCache(fileName => toPath(fileName), () => compilerOptions);
|
||||
|
||||
// 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);
|
||||
|
||||
|
@ -287,6 +292,10 @@ namespace ts {
|
|||
|
||||
// Create the compiler host
|
||||
const compilerHost = createWatchedCompilerHost(compilerOptions);
|
||||
resolutionCache.setModuleResolutionHost(compilerHost);
|
||||
if (changesAffectModuleResolution(program && program.getCompilerOptions(), compilerOptions)) {
|
||||
resolutionCache.clear();
|
||||
}
|
||||
beforeCompile(compilerOptions);
|
||||
|
||||
// Compile the program
|
||||
|
@ -321,22 +330,18 @@ namespace ts {
|
|||
getEnvironmentVariable: name => host.getEnvironmentVariable ? host.getEnvironmentVariable(name) : "",
|
||||
getDirectories: (path: string) => host.getDirectories(path),
|
||||
realpath,
|
||||
onReleaseOldSourceFile,
|
||||
resolveTypeReferenceDirectives: (typeDirectiveNames, containingFile) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile),
|
||||
resolveModuleNames: (moduleNames, containingFile) => resolutionCache.resolveModuleNames(moduleNames, containingFile, /*logChanges*/ false),
|
||||
onReleaseOldSourceFile
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: cache module resolution
|
||||
// if (host.resolveModuleNames) {
|
||||
// compilerHost.resolveModuleNames = (moduleNames, containingFile) => host.resolveModuleNames(moduleNames, containingFile);
|
||||
// }
|
||||
// if (host.resolveTypeReferenceDirectives) {
|
||||
// compilerHost.resolveTypeReferenceDirectives = (typeReferenceDirectiveNames, containingFile) => {
|
||||
// return host.resolveTypeReferenceDirectives(typeReferenceDirectiveNames, containingFile);
|
||||
// };
|
||||
// }
|
||||
function toPath(fileName: string) {
|
||||
return ts.toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
}
|
||||
|
||||
function fileExists(fileName: string) {
|
||||
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
const path = toPath(fileName);
|
||||
const hostSourceFileInfo = sourceFilesCache.get(path);
|
||||
if (hostSourceFileInfo !== undefined) {
|
||||
return !isString(hostSourceFileInfo);
|
||||
|
@ -350,7 +355,7 @@ namespace ts {
|
|||
}
|
||||
|
||||
function getVersionedSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile {
|
||||
return getVersionedSourceFileByPath(fileName, toPath(fileName, currentDirectory, getCanonicalFileName), languageVersion, onError, shouldCreateNewSourceFile);
|
||||
return getVersionedSourceFileByPath(fileName, toPath(fileName), languageVersion, onError, shouldCreateNewSourceFile);
|
||||
}
|
||||
|
||||
function getVersionedSourceFileByPath(fileName: string, path: Path, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile {
|
||||
|
@ -418,6 +423,7 @@ namespace ts {
|
|||
if (hostSourceFile !== undefined) {
|
||||
if (!isString(hostSourceFile)) {
|
||||
hostSourceFile.fileWatcher.close();
|
||||
resolutionCache.invalidateResolutionOfDeletedFile(path);
|
||||
}
|
||||
sourceFilesCache.delete(path);
|
||||
}
|
||||
|
@ -501,6 +507,7 @@ namespace ts {
|
|||
if (hostSourceFile) {
|
||||
// Update the cache
|
||||
if (eventKind === FileWatcherEventKind.Deleted) {
|
||||
resolutionCache.invalidateResolutionOfDeletedFile(path);
|
||||
if (!isString(hostSourceFile)) {
|
||||
hostSourceFile.fileWatcher.close();
|
||||
sourceFilesCache.set(path, (hostSourceFile.version++).toString());
|
||||
|
@ -574,7 +581,7 @@ namespace ts {
|
|||
function onFileAddOrRemoveInWatchedDirectory(fileName: string) {
|
||||
Debug.assert(!!configFileName);
|
||||
|
||||
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
const path = toPath(fileName);
|
||||
|
||||
// Since the file existance changed, update the sourceFiles cache
|
||||
updateCachedSystem(fileName, path);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/// <reference path="..\services\services.ts" />
|
||||
/// <reference path="utilities.ts" />
|
||||
/// <reference path="scriptInfo.ts" />
|
||||
/// <reference path="..\compiler\resolutionCache.ts" />
|
||||
|
||||
namespace ts.server {
|
||||
export class CachedServerHost implements ServerHost {
|
||||
|
@ -103,15 +104,10 @@ namespace ts.server {
|
|||
|
||||
}
|
||||
|
||||
type NameResolutionWithFailedLookupLocations = { failedLookupLocations: string[], isInvalidated?: boolean };
|
||||
export class LSHost implements LanguageServiceHost, ModuleResolutionHost {
|
||||
private compilationSettings: CompilerOptions;
|
||||
private readonly resolvedModuleNames = createMap<Map<ResolvedModuleWithFailedLookupLocations>>();
|
||||
private readonly resolvedTypeReferenceDirectives = createMap<Map<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
|
||||
/*@internal*/
|
||||
compilationSettings: CompilerOptions;
|
||||
|
||||
private filesWithChangedSetOfUnresolvedImports: Path[];
|
||||
|
||||
private resolveModuleName: typeof resolveModuleName;
|
||||
readonly trace: (s: string) => void;
|
||||
readonly realpath?: (path: string) => string;
|
||||
/**
|
||||
|
@ -130,25 +126,6 @@ namespace ts.server {
|
|||
this.trace = s => host.trace(s);
|
||||
}
|
||||
|
||||
this.resolveModuleName = (moduleName, containingFile, compilerOptions, host) => {
|
||||
const globalCache = this.project.getTypeAcquisition().enable
|
||||
? this.project.projectService.typingsInstaller.globalTypingsCacheLocation
|
||||
: undefined;
|
||||
const primaryResult = resolveModuleName(moduleName, containingFile, compilerOptions, host);
|
||||
// return result immediately only if it is .ts, .tsx or .d.ts
|
||||
if (!isExternalModuleNameRelative(moduleName) && !(primaryResult.resolvedModule && extensionIsTypeScript(primaryResult.resolvedModule.extension)) && globalCache !== undefined) {
|
||||
// otherwise try to load typings from @types
|
||||
|
||||
// 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, this.project.getProjectName(), compilerOptions, host, globalCache);
|
||||
if (resolvedModule) {
|
||||
return { resolvedModule, failedLookupLocations: primaryResult.failedLookupLocations.concat(failedLookupLocations) };
|
||||
}
|
||||
}
|
||||
return primaryResult;
|
||||
};
|
||||
|
||||
if (this.host.realpath) {
|
||||
this.realpath = path => this.host.realpath(path);
|
||||
}
|
||||
|
@ -156,99 +133,9 @@ namespace ts.server {
|
|||
|
||||
dispose() {
|
||||
this.project = undefined;
|
||||
this.resolveModuleName = undefined;
|
||||
this.host = undefined;
|
||||
}
|
||||
|
||||
public startRecordingFilesWithChangedResolutions() {
|
||||
this.filesWithChangedSetOfUnresolvedImports = [];
|
||||
}
|
||||
|
||||
public finishRecordingFilesWithChangedResolutions() {
|
||||
const collected = this.filesWithChangedSetOfUnresolvedImports;
|
||||
this.filesWithChangedSetOfUnresolvedImports = undefined;
|
||||
return collected;
|
||||
}
|
||||
|
||||
private resolveNamesWithLocalCache<T extends NameResolutionWithFailedLookupLocations, R>(
|
||||
names: string[],
|
||||
containingFile: string,
|
||||
cache: Map<Map<T>>,
|
||||
loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost) => T,
|
||||
getResult: (s: T) => R,
|
||||
getResultFileName: (result: R) => string | undefined,
|
||||
logChanges: boolean): R[] {
|
||||
|
||||
const path = this.project.projectService.toPath(containingFile);
|
||||
const currentResolutionsInFile = cache.get(path);
|
||||
|
||||
const newResolutions: Map<T> = createMap<T>();
|
||||
const resolvedModules: R[] = [];
|
||||
const compilerOptions = this.getCompilationSettings();
|
||||
|
||||
for (const name of names) {
|
||||
// check if this is a duplicate entry in the list
|
||||
let resolution = newResolutions.get(name);
|
||||
if (!resolution) {
|
||||
const existingResolution = currentResolutionsInFile && currentResolutionsInFile.get(name);
|
||||
if (moduleResolutionIsValid(existingResolution)) {
|
||||
// ok, it is safe to use existing name resolution results
|
||||
resolution = existingResolution;
|
||||
}
|
||||
else {
|
||||
resolution = loader(name, containingFile, compilerOptions, this);
|
||||
newResolutions.set(name, resolution);
|
||||
}
|
||||
if (logChanges && this.filesWithChangedSetOfUnresolvedImports && !resolutionIsEqualTo(existingResolution, resolution)) {
|
||||
this.filesWithChangedSetOfUnresolvedImports.push(path);
|
||||
// reset log changes to avoid recording the same file multiple times
|
||||
logChanges = false;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.assert(resolution !== undefined);
|
||||
|
||||
resolvedModules.push(getResult(resolution));
|
||||
}
|
||||
|
||||
// replace old results with a new one
|
||||
cache.set(path, newResolutions);
|
||||
return resolvedModules;
|
||||
|
||||
function resolutionIsEqualTo(oldResolution: T, newResolution: T): boolean {
|
||||
if (oldResolution === newResolution) {
|
||||
return true;
|
||||
}
|
||||
if (!oldResolution || !newResolution || oldResolution.isInvalidated) {
|
||||
return false;
|
||||
}
|
||||
const oldResult = getResult(oldResolution);
|
||||
const newResult = getResult(newResolution);
|
||||
if (oldResult === newResult) {
|
||||
return true;
|
||||
}
|
||||
if (!oldResult || !newResult) {
|
||||
return false;
|
||||
}
|
||||
return getResultFileName(oldResult) === getResultFileName(newResult);
|
||||
}
|
||||
|
||||
function moduleResolutionIsValid(resolution: T): boolean {
|
||||
if (!resolution || resolution.isInvalidated) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const result = getResult(resolution);
|
||||
if (result) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// consider situation if we have no candidate locations as valid resolution.
|
||||
// after all there is no point to invalidate it if we have no idea where to look for the module.
|
||||
return resolution.failedLookupLocations.length === 0;
|
||||
}
|
||||
}
|
||||
|
||||
getNewLine() {
|
||||
return this.host.newLine;
|
||||
}
|
||||
|
@ -270,13 +157,11 @@ namespace ts.server {
|
|||
}
|
||||
|
||||
resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[] {
|
||||
return this.resolveNamesWithLocalCache(typeDirectiveNames, containingFile, this.resolvedTypeReferenceDirectives, resolveTypeReferenceDirective,
|
||||
m => m.resolvedTypeReferenceDirective, r => r.resolvedFileName, /*logChanges*/ false);
|
||||
return this.project.resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile);
|
||||
}
|
||||
|
||||
resolveModuleNames(moduleNames: string[], containingFile: string): ResolvedModuleFull[] {
|
||||
return this.resolveNamesWithLocalCache(moduleNames, containingFile, this.resolvedModuleNames, this.resolveModuleName,
|
||||
m => m.resolvedModule, r => r.resolvedFileName, /*logChanges*/ true);
|
||||
return this.project.resolutionCache.resolveModuleNames(moduleNames, containingFile, /*logChanges*/ true);
|
||||
}
|
||||
|
||||
getDefaultLibFileName() {
|
||||
|
@ -339,44 +224,5 @@ namespace ts.server {
|
|||
getDirectories(path: string): string[] {
|
||||
return this.host.getDirectories(path);
|
||||
}
|
||||
|
||||
notifyFileRemoved(info: ScriptInfo) {
|
||||
this.invalidateResolutionOfDeletedFile(info, this.resolvedModuleNames,
|
||||
m => m.resolvedModule, r => r.resolvedFileName);
|
||||
this.invalidateResolutionOfDeletedFile(info, this.resolvedTypeReferenceDirectives,
|
||||
m => m.resolvedTypeReferenceDirective, r => r.resolvedFileName);
|
||||
}
|
||||
|
||||
private invalidateResolutionOfDeletedFile<T extends NameResolutionWithFailedLookupLocations, R>(
|
||||
deletedInfo: ScriptInfo,
|
||||
cache: Map<Map<T>>,
|
||||
getResult: (s: T) => R,
|
||||
getResultFileName: (result: R) => string | undefined) {
|
||||
cache.forEach((value, path) => {
|
||||
if (path === deletedInfo.path) {
|
||||
cache.delete(path);
|
||||
}
|
||||
else if (value) {
|
||||
value.forEach((resolution) => {
|
||||
if (resolution && !resolution.isInvalidated) {
|
||||
const result = getResult(resolution);
|
||||
if (result) {
|
||||
if (getResultFileName(result) === deletedInfo.path) {
|
||||
resolution.isInvalidated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setCompilationSettings(opt: CompilerOptions) {
|
||||
if (changesAffectModuleResolution(this.compilationSettings, opt)) {
|
||||
this.resolvedModuleNames.clear();
|
||||
this.resolvedTypeReferenceDirectives.clear();
|
||||
}
|
||||
this.compilationSettings = opt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -127,6 +127,9 @@ namespace ts.server {
|
|||
|
||||
public languageServiceEnabled = true;
|
||||
|
||||
/*@internal*/
|
||||
resolutionCache: ResolutionCache;
|
||||
|
||||
/*@internal*/
|
||||
lsHost: LSHost;
|
||||
|
||||
|
@ -211,7 +214,14 @@ namespace ts.server {
|
|||
this.setInternalCompilerOptionsForEmittingJsFiles();
|
||||
|
||||
this.lsHost = new LSHost(host, this, this.projectService.cancellationToken);
|
||||
this.lsHost.setCompilationSettings(this.compilerOptions);
|
||||
this.resolutionCache = createResolutionCache(
|
||||
fileName => this.projectService.toPath(fileName),
|
||||
() => this.compilerOptions,
|
||||
(primaryResult, moduleName, compilerOptions, host) => resolveWithGlobalCache(primaryResult, moduleName, compilerOptions, host,
|
||||
this.getTypeAcquisition().enable ? this.projectService.typingsInstaller.globalTypingsCacheLocation : undefined, this.getProjectName())
|
||||
);
|
||||
this.lsHost.compilationSettings = this.compilerOptions;
|
||||
this.resolutionCache.setModuleResolutionHost(this.lsHost);
|
||||
|
||||
this.languageService = createLanguageService(this.lsHost, this.documentRegistry);
|
||||
|
||||
|
@ -349,6 +359,7 @@ namespace ts.server {
|
|||
this.rootFilesMap = undefined;
|
||||
this.program = undefined;
|
||||
this.builder = undefined;
|
||||
this.resolutionCache = undefined;
|
||||
this.cachedUnresolvedImportsPerFile = undefined;
|
||||
this.projectErrors = undefined;
|
||||
this.lsHost.dispose();
|
||||
|
@ -518,7 +529,7 @@ namespace ts.server {
|
|||
if (this.isRoot(info)) {
|
||||
this.removeRoot(info);
|
||||
}
|
||||
this.lsHost.notifyFileRemoved(info);
|
||||
this.resolutionCache.invalidateResolutionOfDeletedFile(info.path);
|
||||
this.cachedUnresolvedImportsPerFile.remove(info.path);
|
||||
|
||||
if (detachFromProject) {
|
||||
|
@ -573,11 +584,11 @@ namespace ts.server {
|
|||
* @returns: true if set of files in the project stays the same and false - otherwise.
|
||||
*/
|
||||
updateGraph(): boolean {
|
||||
this.lsHost.startRecordingFilesWithChangedResolutions();
|
||||
this.resolutionCache.startRecordingFilesWithChangedResolutions();
|
||||
|
||||
let hasChanges = this.updateGraphWorker();
|
||||
|
||||
const changedFiles: ReadonlyArray<Path> = this.lsHost.finishRecordingFilesWithChangedResolutions() || emptyArray;
|
||||
const changedFiles: ReadonlyArray<Path> = this.resolutionCache.finishRecordingFilesWithChangedResolutions() || emptyArray;
|
||||
|
||||
for (const file of changedFiles) {
|
||||
// delete cached information for changed files
|
||||
|
@ -759,9 +770,13 @@ namespace ts.server {
|
|||
this.cachedUnresolvedImportsPerFile.clear();
|
||||
this.lastCachedUnresolvedImportsList = undefined;
|
||||
}
|
||||
const oldOptions = this.compilerOptions;
|
||||
this.compilerOptions = compilerOptions;
|
||||
this.setInternalCompilerOptionsForEmittingJsFiles();
|
||||
this.lsHost.setCompilationSettings(compilerOptions);
|
||||
if (changesAffectModuleResolution(oldOptions, compilerOptions)) {
|
||||
this.resolutionCache.clear();
|
||||
}
|
||||
this.lsHost.compilationSettings = this.compilerOptions;
|
||||
|
||||
this.markAsDirty();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue