Skip to content

Resolve only relative references in open files on syntax server #39476

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 16, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,8 @@ namespace ts {
let mapFromFileToProjectReferenceRedirects: ESMap<Path, Path> | undefined;
let mapFromToProjectReferenceRedirectSource: ESMap<Path, SourceOfProjectReferenceRedirect> | undefined;

let skippedTrippleSlashReferences: Set<Path> | undefined;

const useSourceOfProjectReferenceRedirect = !!host.useSourceOfProjectReferenceRedirect?.() &&
!options.disableSourceOfProjectReferenceRedirect;
const { onProgramCreateComplete, fileExists } = updateHostForUseSourceOfProjectReferenceRedirect({
Expand Down Expand Up @@ -928,6 +930,7 @@ namespace ts {
getSourceFiles: () => files,
getMissingFilePaths: () => missingFilePaths!, // TODO: GH#18217
getRefFileMap: () => refFileMap,
getSkippedTrippleSlashReferences: () => skippedTrippleSlashReferences,
getFilesByNameMap: () => filesByName,
getCompilerOptions: () => options,
getSyntacticDiagnostics,
Expand Down Expand Up @@ -1269,6 +1272,7 @@ namespace ts {
const oldSourceFiles = oldProgram.getSourceFiles();
const enum SeenPackageName { Exists, Modified }
const seenPackageNames = new Map<string, SeenPackageName>();
const oldSkippedTrippleSlashReferences = oldProgram.getSkippedTrippleSlashReferences();

for (const oldSourceFile of oldSourceFiles) {
let newSourceFile = host.getSourceFileByPath
Expand Down Expand Up @@ -1341,6 +1345,11 @@ namespace ts {
oldProgram.structureIsReused = StructureIsReused.SafeModules;
}

if (oldSkippedTrippleSlashReferences?.has(oldSourceFile.path) && includeTripleslashReferencesFrom(newSourceFile)) {
// tripleslash reference resolution is now allowed
oldProgram.structureIsReused = StructureIsReused.SafeModules;
}

// check imports and module augmentations
collectExternalModuleReferences(newSourceFile);
if (!arrayIsEqualTo(oldSourceFile.imports, newSourceFile.imports, moduleNameIsEqualTo)) {
Expand Down Expand Up @@ -1428,6 +1437,7 @@ namespace ts {

missingFilePaths = oldProgram.getMissingFilePaths();
refFileMap = oldProgram.getRefFileMap();
skippedTrippleSlashReferences = oldSkippedTrippleSlashReferences;

// update fileName -> file mapping
Debug.assert(newSourceFiles.length === oldProgram.getSourceFiles().length);
Expand Down Expand Up @@ -2647,7 +2657,15 @@ namespace ts {
return projectReferenceRedirects.get(projectReferencePath) || undefined;
}

function includeTripleslashReferencesFrom(file: SourceFile) {
return !host.includeTripleslashReferencesFrom || host.includeTripleslashReferencesFrom(file.originalFileName);
}

function processReferencedFiles(file: SourceFile, isDefaultLib: boolean) {
if (!includeTripleslashReferencesFrom(file)) {
(skippedTrippleSlashReferences || (skippedTrippleSlashReferences = new Set())).add(file.path);
return;
}
forEach(file.referencedFiles, (ref, index) => {
const referencedFileName = resolveTripleslashReference(ref.fileName, file.originalFileName);
processSourceFile(
Expand Down
56 changes: 52 additions & 4 deletions src/compiler/resolutionCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace ts {

invalidateResolutionsOfFailedLookupLocations(): boolean;
invalidateResolutionOfFile(filePath: Path): void;
removeRelativeNoResolveResolutionsOfFile(filePath: Path): boolean;
removeResolutionsOfFile(filePath: Path): void;
removeResolutionsFromProjectReferenceRedirects(filePath: Path): void;
setFilesWithInvalidatedNonRelativeUnresolvedImports(filesWithUnresolvedImports: ESMap<Path, readonly string[]>): void;
Expand Down Expand Up @@ -141,7 +142,21 @@ namespace ts {
type GetResolutionWithResolvedFileName<T extends ResolutionWithFailedLookupLocations = ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName = ResolutionWithResolvedFileName> =
(resolution: T) => R | undefined;

export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootDirForResolution: string | undefined, logChangesWhenResolvingModule: boolean): ResolutionCache {
export enum ResolutionKind {
All,
RelativeReferencesInOpenFileOnly
}

const noResolveResolvedModule: ResolvedModuleWithFailedLookupLocations = {
resolvedModule: undefined,
failedLookupLocations: []
};
const noResolveResolvedTypeReferenceDirective: ResolvedTypeReferenceDirectiveWithFailedLookupLocations = {
resolvedTypeReferenceDirective: undefined,
failedLookupLocations: []
};

export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootDirForResolution: string | undefined, resolutionKind: ResolutionKind, logChangesWhenResolvingModule: boolean): ResolutionCache {
let filesWithChangedSetOfUnresolvedImports: Path[] | undefined;
let filesWithInvalidatedResolutions: Set<Path> | undefined;
let filesWithInvalidatedNonRelativeUnresolvedImports: ReadonlyESMap<Path, readonly string[]> | undefined;
Expand Down Expand Up @@ -206,6 +221,7 @@ namespace ts {
hasChangedAutomaticTypeDirectiveNames: () => hasChangedAutomaticTypeDirectiveNames,
invalidateResolutionOfFile,
invalidateResolutionsOfFailedLookupLocations,
removeRelativeNoResolveResolutionsOfFile,
setFilesWithInvalidatedNonRelativeUnresolvedImports,
createHasInvalidatedResolution,
updateTypeRootsWatch,
Expand Down Expand Up @@ -341,11 +357,12 @@ namespace ts {
shouldRetryResolution: (t: T) => boolean;
reusedNames?: readonly string[];
logChanges?: boolean;
noResolveResolution: T;
}
function resolveNamesWithLocalCache<T extends ResolutionWithFailedLookupLocations, R extends ResolutionWithResolvedFileName>({
names, containingFile, redirectedReference,
cache, perDirectoryCacheWithRedirects,
loader, getResolutionWithResolvedFileName,
loader, getResolutionWithResolvedFileName, noResolveResolution,
shouldRetryResolution, reusedNames, logChanges
}: ResolveNamesWithLocalCacheInput<T, R>): (R | undefined)[] {
const path = resolutionHost.toPath(containingFile);
Expand Down Expand Up @@ -382,7 +399,10 @@ namespace ts {
resolution = resolutionInDirectory;
}
else {
resolution = loader(name, containingFile, compilerOptions, resolutionHost.getCompilerHost?.() || resolutionHost, redirectedReference);
resolution = resolutionKind === ResolutionKind.All ||
(isExternalModuleNameRelative(name) && resolutionHost.fileIsOpen(path)) ?
loader(name, containingFile, compilerOptions, resolutionHost.getCompilerHost?.() || resolutionHost, redirectedReference) :
noResolveResolution;
perDirectoryResolution.set(name, resolution);
}
resolutionsInFile.set(name, resolution);
Expand Down Expand Up @@ -441,6 +461,7 @@ namespace ts {
loader: resolveTypeReferenceDirective,
getResolutionWithResolvedFileName: getResolvedTypeReferenceDirective,
shouldRetryResolution: resolution => resolution.resolvedTypeReferenceDirective === undefined,
noResolveResolution: noResolveResolvedTypeReferenceDirective,
});
}

Expand All @@ -455,7 +476,8 @@ namespace ts {
getResolutionWithResolvedFileName: getResolvedModule,
shouldRetryResolution: resolution => !resolution.resolvedModule || !resolutionExtensionIsTSOrJson(resolution.resolvedModule.extension),
reusedNames,
logChanges: logChangesWhenResolvingModule
logChanges: logChangesWhenResolvingModule,
noResolveResolution: noResolveResolvedModule,
});
}

Expand Down Expand Up @@ -741,6 +763,32 @@ namespace ts {
}
}

function removeRelativeNoResolveResolutionsOfFileFromCache<T extends ResolutionWithFailedLookupLocations>(
cache: ESMap<Path, ESMap<string, T>>,
filePath: Path,
noResolveResolution: T,
) {
Debug.assert(resolutionKind === ResolutionKind.RelativeReferencesInOpenFileOnly);
// Deleted file, stop watching failed lookups for all the resolutions in the file
const resolutions = cache.get(filePath);
if (!resolutions) return false;
let invalidated = false;
resolutions.forEach((resolution, name) => {
if (resolution === noResolveResolution && isExternalModuleNameRelative(name)) {
resolutions.delete(name);
invalidated = true;
}
});
return invalidated;
}

function removeRelativeNoResolveResolutionsOfFile(filePath: Path) {
let invalidated = removeRelativeNoResolveResolutionsOfFileFromCache(resolvedModuleNames, filePath, noResolveResolvedModule);
invalidated = removeRelativeNoResolveResolutionsOfFileFromCache(resolvedTypeReferenceDirectives, filePath, noResolveResolvedTypeReferenceDirective) || invalidated;
return invalidated;

}

function setFilesWithInvalidatedNonRelativeUnresolvedImports(filesMap: ReadonlyESMap<Path, readonly string[]>) {
Debug.assert(filesWithInvalidatedNonRelativeUnresolvedImports === filesMap || filesWithInvalidatedNonRelativeUnresolvedImports === undefined);
filesWithInvalidatedNonRelativeUnresolvedImports = filesMap;
Expand Down
3 changes: 3 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3687,6 +3687,8 @@ namespace ts {
/* @internal */
getRefFileMap(): MultiMap<Path, RefFile> | undefined;
/* @internal */
getSkippedTrippleSlashReferences(): Set<Path> | undefined;
/* @internal */
getFilesByNameMap(): ESMap<string, SourceFile | false | undefined>;

/**
Expand Down Expand Up @@ -6226,6 +6228,7 @@ namespace ts {
* This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files
*/
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions): (ResolvedTypeReferenceDirective | undefined)[];
/* @internal */ includeTripleslashReferencesFrom?(containingFile: string): boolean;
getEnvironmentVariable?(name: string): string | undefined;
/* @internal */ onReleaseOldSourceFile?(oldSourceFile: SourceFile, oldOptions: CompilerOptions, hasSourceFileByPath: boolean): void;
/* @internal */ hasInvalidatedResolution?: HasInvalidatedResolution;
Expand Down
1 change: 1 addition & 0 deletions src/compiler/watchPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ namespace ts {
configFileName ?
getDirectoryPath(getNormalizedAbsolutePath(configFileName, currentDirectory)) :
currentDirectory,
ResolutionKind.All,
/*logChangesWhenResolvingModule*/ false
);
// Resolve module using host module resolution strategy if provided otherwise use resolution cache to resolve module names
Expand Down
14 changes: 13 additions & 1 deletion src/server/editorServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2967,7 +2967,15 @@ namespace ts.server {
let project: ConfiguredProject | ExternalProject | undefined = this.findExternalProjectContainingOpenScriptInfo(info);
let defaultConfigProject: ConfiguredProject | undefined;
let retainProjects: ConfiguredProject[] | ConfiguredProject | undefined;
if (!project && !this.syntaxOnly) { // Checking syntaxOnly is an optimization
if (this.syntaxOnly) {
// Invalidate resolutions in the file since this file is now open
info.containingProjects.forEach(project => {
if (project.resolutionCache.removeRelativeNoResolveResolutionsOfFile(info.path)) {
project.markAsDirty();
}
});
}
else if (!project) { // Checking syntaxOnly is an optimization
configFileName = this.getConfigFileNameForFile(info);
if (configFileName) {
project = this.findConfiguredProjectByProjectName(configFileName);
Expand Down Expand Up @@ -3047,6 +3055,10 @@ namespace ts.server {
Debug.assert(this.openFiles.has(info.path));
this.assignOrphanScriptInfoToInferredProject(info, this.openFiles.get(info.path));
}
else if (this.syntaxOnly && info.cacheSourceFile?.sourceFile.referencedFiles.length) {
// This file was just opened and references in this file will previously not been resolved so schedule update
info.containingProjects.forEach(project => project.markAsDirty());
}
Debug.assert(!info.isOrphan());
return { configFileName, configFileErrors, retainProjects };
}
Expand Down
13 changes: 11 additions & 2 deletions src/server/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,6 @@ namespace ts.server {

this.languageServiceEnabled = true;
if (projectService.syntaxOnly) {
this.compilerOptions.noResolve = true;
this.compilerOptions.types = [];
}

Expand All @@ -296,7 +295,12 @@ namespace ts.server {
this.realpath = maybeBind(host, host.realpath);

// Use the current directory as resolution root only if the project created using current directory string
this.resolutionCache = createResolutionCache(this, currentDirectory && this.currentDirectory, /*logChangesWhenResolvingModule*/ true);
this.resolutionCache = createResolutionCache(
this,
currentDirectory && this.currentDirectory,
projectService.syntaxOnly ? ResolutionKind.RelativeReferencesInOpenFileOnly : ResolutionKind.All,
/*logChangesWhenResolvingModule*/ true
);
this.languageService = createLanguageService(this, this.documentRegistry, this.projectService.syntaxOnly);
if (lastFileExceededProgramSize) {
this.disableLanguageService(lastFileExceededProgramSize);
Expand Down Expand Up @@ -450,6 +454,11 @@ namespace ts.server {
return this.resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile, redirectedReference);
}

/*@internal*/
includeTripleslashReferencesFrom(containingFile: string) {
return !this.projectService.syntaxOnly || this.fileIsOpen(this.toPath(containingFile));
}

directoryExists(path: string): boolean {
return this.directoryStructureHost.directoryExists!(path); // TODO: GH#18217
}
Expand Down
22 changes: 7 additions & 15 deletions src/services/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1296,7 +1296,7 @@ namespace ts {
getCurrentDirectory: () => currentDirectory,
fileExists,
readFile,
realpath: host.realpath && (path => host.realpath!(path)),
realpath: maybeBind(host, host.realpath),
directoryExists: directoryName => {
return directoryProbablyExists(directoryName, host);
},
Expand All @@ -1309,21 +1309,13 @@ namespace ts {
},
onReleaseOldSourceFile,
hasInvalidatedResolution,
hasChangedAutomaticTypeDirectiveNames
hasChangedAutomaticTypeDirectiveNames,
includeTripleslashReferencesFrom: maybeBind(host, host.includeTripleslashReferencesFrom),
trace: maybeBind(host, host.trace),
resolveModuleNames: maybeBind(host, host.resolveModuleNames),
resolveTypeReferenceDirectives: maybeBind(host, host.resolveTypeReferenceDirectives),
useSourceOfProjectReferenceRedirect: maybeBind(host, host.useSourceOfProjectReferenceRedirect),
};
if (host.trace) {
compilerHost.trace = message => host.trace!(message);
}

if (host.resolveModuleNames) {
compilerHost.resolveModuleNames = (...args) => host.resolveModuleNames!(...args);
}
if (host.resolveTypeReferenceDirectives) {
compilerHost.resolveTypeReferenceDirectives = (...args) => host.resolveTypeReferenceDirectives!(...args);
}
if (host.useSourceOfProjectReferenceRedirect) {
compilerHost.useSourceOfProjectReferenceRedirect = () => host.useSourceOfProjectReferenceRedirect!();
}
Comment on lines +1312 to -1326
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️

host.setCompilerHost?.(compilerHost);

const documentRegistryBucketKey = documentRegistry.getKeyForCompilationSettings(newSettings);
Expand Down
1 change: 1 addition & 0 deletions src/services/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ namespace ts {
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions): (ResolvedModule | undefined)[];
getResolvedModuleWithFailedLookupLocationsFromCache?(modulename: string, containingFile: string): ResolvedModuleWithFailedLookupLocations | undefined;
resolveTypeReferenceDirectives?(typeDirectiveNames: string[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions): (ResolvedTypeReferenceDirective | undefined)[];
/* @internal */ includeTripleslashReferencesFrom?(containingFile: string): boolean;
/* @internal */ hasInvalidatedResolution?: HasInvalidatedResolution;
/* @internal */ hasChangedAutomaticTypeDirectiveNames?: HasChangedAutomaticTypeDirectiveNames;
/* @internal */
Expand Down
Loading