Skip to content

Type reference directive improvements #39708

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

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -4473,6 +4473,10 @@
"category": "Message",
"code": 6235
},
"Resolving type reference directive for program with typeRoots, skipping lookup in 'node_modules' folder.": {
"category": "Message",
"code": 6236
},

"Projects to reference": {
"category": "Message",
Expand Down
61 changes: 30 additions & 31 deletions src/compiler/moduleNameResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,27 +250,19 @@ namespace ts {
}

if (currentDirectory !== undefined) {
return getDefaultTypeRoots(currentDirectory, host);
return getDefaultTypeRoots(currentDirectory);
}
}

/**
* Returns the path to every node_modules/@types directory from some ancestor directory.
* Returns undefined if there are none.
*/
function getDefaultTypeRoots(currentDirectory: string, host: { directoryExists?: (directoryName: string) => boolean }): string[] | undefined {
if (!host.directoryExists) {
return [combinePaths(currentDirectory, nodeModulesAtTypes)];
// And if it doesn't exist, tough.
}

function getDefaultTypeRoots(currentDirectory: string): string[] | undefined {
let typeRoots: string[] | undefined;
forEachAncestorDirectory(normalizePath(currentDirectory), directory => {
const atTypes = combinePaths(directory, nodeModulesAtTypes);
if (host.directoryExists!(atTypes)) {
(typeRoots || (typeRoots = [])).push(atTypes);
}
return undefined;
(typeRoots || (typeRoots = [])).push(atTypes);
});
return typeRoots;
}
Expand Down Expand Up @@ -343,15 +335,15 @@ namespace ts {
trace(host, Diagnostics.Resolving_with_primary_search_path_0, typeRoots.join(", "));
}
return firstDefined(typeRoots, typeRoot => {
const candidate = combinePaths(typeRoot, typeReferenceDirectiveName);
const candidateDirectory = getDirectoryPath(candidate);
const directoryExists = directoryProbablyExists(candidateDirectory, host);
if (!directoryExists && traceEnabled) {
trace(host, Diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it, candidateDirectory);
}
return resolvedTypeScriptOnly(
loadNodeModuleFromDirectory(Extensions.DtsOnly, candidate,
!directoryExists, moduleResolutionState));
nodeLoadModuleByRelativeName(
Extensions.DtsOnly,
combinePaths(typeRoot, typeReferenceDirectiveName),
/*onlyRecordFailures*/ false,
moduleResolutionState,
/*considerPackageJson*/ true
)
);
});
}
else {
Expand All @@ -363,20 +355,25 @@ namespace ts {

function secondaryLookup(): PathAndPackageId | undefined {
const initialLocationForSecondaryLookup = containingFile && getDirectoryPath(containingFile);

if (initialLocationForSecondaryLookup !== undefined) {
// check secondary locations
if (traceEnabled) {
trace(host, Diagnostics.Looking_up_in_node_modules_folder_initial_location_0, initialLocationForSecondaryLookup);
}
// Do not resolve auto types from secondary location
let result: Resolved | undefined;
if (!isExternalModuleNameRelative(typeReferenceDirectiveName)) {
const searchResult = loadModuleFromNearestNodeModulesDirectory(Extensions.DtsOnly, typeReferenceDirectiveName, initialLocationForSecondaryLookup, moduleResolutionState, /*cache*/ undefined, /*redirectedReference*/ undefined);
result = searchResult && searchResult.value;
if (!options.typeRoots || !endsWith(containingFile!, inferredTypesContainingFile)) {
// check secondary locations
if (traceEnabled) {
trace(host, Diagnostics.Looking_up_in_node_modules_folder_initial_location_0, initialLocationForSecondaryLookup);
}
if (!isExternalModuleNameRelative(typeReferenceDirectiveName)) {
const searchResult = loadModuleFromNearestNodeModulesDirectory(Extensions.DtsOnly, typeReferenceDirectiveName, initialLocationForSecondaryLookup, moduleResolutionState, /*cache*/ undefined, /*redirectedReference*/ undefined);
result = searchResult && searchResult.value;
}
else {
const { path: candidate } = normalizePathAndParts(combinePaths(initialLocationForSecondaryLookup, typeReferenceDirectiveName));
result = nodeLoadModuleByRelativeName(Extensions.DtsOnly, candidate, /*onlyRecordFailures*/ false, moduleResolutionState, /*considerPackageJson*/ true);
}
}
else {
const { path: candidate } = normalizePathAndParts(combinePaths(initialLocationForSecondaryLookup, typeReferenceDirectiveName));
result = nodeLoadModuleByRelativeName(Extensions.DtsOnly, candidate, /*onlyRecordFailures*/ false, moduleResolutionState, /*considerPackageJson*/ true);
else if (traceEnabled) {
trace(host, Diagnostics.Resolving_type_reference_directive_for_program_with_typeRoots_skipping_lookup_in_node_modules_folder);
}
const resolvedFile = resolvedTypeScriptOnly(result);
if (!resolvedFile && traceEnabled) {
Expand Down Expand Up @@ -944,6 +941,7 @@ namespace ts {
if (traceEnabled) {
trace(host, Diagnostics.Loading_module_0_from_node_modules_folder_target_file_type_1, moduleName, Extensions[extensions]);
}
// TODO:: resolve type ference directive
const resolved = loadModuleFromNearestNodeModulesDirectory(extensions, moduleName, containingDirectory, state, cache, redirectedReference);
if (!resolved) return undefined;

Expand Down Expand Up @@ -1473,7 +1471,8 @@ namespace ts {
}
if (extensions === Extensions.TypeScript) {
// If we didn't find the file normally, look it up in @types.
return loadModuleFromNearestNodeModulesDirectoryTypesScope(moduleName, containingDirectory, state);
// TODO type referecnce like resolution
return resolveTypeReferenceDirective(moduleName, containingFile, containingDirectory, state);
}
}
else {
Expand Down
53 changes: 27 additions & 26 deletions src/compiler/resolutionCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -869,26 +869,28 @@ namespace ts {

function createTypeRootsWatch(typeRootPath: Path, typeRoot: string): FileWatcher {
// Create new watch and recursive info
return resolutionHost.watchTypeRootsDirectory(typeRoot, fileOrDirectory => {
const fileOrDirectoryPath = resolutionHost.toPath(fileOrDirectory);
if (cachedDirectoryStructureHost) {
// Since the file existence changed, update the sourceFiles cache
cachedDirectoryStructureHost.addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
}

// For now just recompile
// We could potentially store more data here about whether it was/would be really be used or not
// and with that determine to trigger compilation but for now this is enough
hasChangedAutomaticTypeDirectiveNames = true;
resolutionHost.onChangedAutomaticTypeDirectiveNames();
return canWatchTypeRootPath(typeRootPath) ?
resolutionHost.watchTypeRootsDirectory(typeRoot, fileOrDirectory => {
const fileOrDirectoryPath = resolutionHost.toPath(fileOrDirectory);
if (cachedDirectoryStructureHost) {
// Since the file existence changed, update the sourceFiles cache
cachedDirectoryStructureHost.addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
}

// Since directory watchers invoked are flaky, the failed lookup location events might not be triggered
// So handle to failed lookup locations here as well to ensure we are invalidating resolutions
const dirPath = getDirectoryToWatchFailedLookupLocationFromTypeRoot(typeRoot, typeRootPath);
if (dirPath) {
scheduleInvalidateResolutionOfFailedLookupLocation(fileOrDirectoryPath, dirPath === fileOrDirectoryPath);
}
}, WatchDirectoryFlags.Recursive);
// For now just recompile
// We could potentially store more data here about whether it was/would be really be used or not
// and with that determine to trigger compilation but for now this is enough
hasChangedAutomaticTypeDirectiveNames = true;
resolutionHost.onChangedAutomaticTypeDirectiveNames();

// Since directory watchers invoked are flaky, the failed lookup location events might not be triggered
// So handle to failed lookup locations here as well to ensure we are invalidating resolutions
const dirPath = getDirectoryToWatchFailedLookupLocationFromTypeRoot(typeRoot, typeRootPath);
if (dirPath) {
scheduleInvalidateResolutionOfFailedLookupLocation(fileOrDirectoryPath, dirPath === fileOrDirectoryPath);
}
}, WatchDirectoryFlags.Recursive) :
noopFileWatcher;
}

/**
Expand All @@ -906,7 +908,7 @@ namespace ts {

// we need to assume the directories exist to ensure that we can get all the type root directories that get included
// But filter directories that are at root level to say directory doesnt exist, so that we arent watching them
const typeRoots = getEffectiveTypeRoots(options, { directoryExists: directoryExistsForTypeRootWatch, getCurrentDirectory });
const typeRoots = getEffectiveTypeRoots(options, { getCurrentDirectory });
if (typeRoots) {
mutateMap(
typeRootsWatches,
Expand All @@ -922,12 +924,11 @@ namespace ts {
}
}

/**
* Use this function to return if directory exists to get type roots to watch
* If we return directory exists then only the paths will be added to type roots
* Hence return true for all directories except root directories which are filtered from watching
*/
function directoryExistsForTypeRootWatch(nodeTypesDirectory: string) {
function canWatchTypeRootPath(nodeTypesDirectory: string) {
// If type roots is specified, watch that path
if (resolutionHost.getCompilationSettings().typeRoots) return true;

// Otherwise can watch directory only if we can watch the parent directory of node_modules/@types
const dir = getDirectoryPath(getDirectoryPath(nodeTypesDirectory));
const dirPath = resolutionHost.toPath(dir);
return dirPath === rootPath || canWatchDirectory(dirPath);
Expand Down
1 change: 0 additions & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7748,7 +7748,6 @@ namespace ts {
}

export interface GetEffectiveTypeRootsHost {
directoryExists?(directoryName: string): boolean;
getCurrentDirectory?(): string;
}

Expand Down
4 changes: 0 additions & 4 deletions src/server/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2278,10 +2278,6 @@ namespace ts.server {
return !!this.externalProjectRefCount;
}

getEffectiveTypeRoots() {
return getEffectiveTypeRoots(this.getCompilationSettings(), this.directoryStructureHost) || [];
}

/*@internal*/
updateErrorOnNoInputFiles(fileNameResult: ExpandResult) {
updateErrorForNoInputFiles(fileNameResult, this.getConfigFilePath(), this.configFileSpecs!, this.projectErrors!, this.canConfigFileJsonReportNoInputFiles);
Expand Down
1 change: 1 addition & 0 deletions src/services/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ namespace ts {
readFile?(path: string, encoding?: string): string | undefined;
realpath?(path: string): string;
fileExists?(path: string): boolean;
directoryExists?(directoryName: string): boolean;

/*
* LS host can optionally implement these methods to support automatic updating when new type libraries are installed
Expand Down
Loading