Use getDirectories and condition node modules resolution on moduleResolution flag

This commit is contained in:
Richard Knoll 2016-07-28 16:44:24 -07:00
parent 4ca7e95706
commit 9e797b4c78
2 changed files with 67 additions and 31 deletions

View File

@ -1171,6 +1171,11 @@ namespace ts {
resolveModuleNames?(moduleNames: string[], containingFile: string): ResolvedModule[];
resolveTypeReferenceDirectives?(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
directoryExists?(directoryName: string): boolean;
/**
* getDirectories is also required for full import and type reference completions. Without it defined, certain
* completions will not be provided
*/
getDirectories?(directoryName: string): string[];
}
@ -4652,7 +4657,7 @@ namespace ts {
getCompletionEntriesFromTypings(host, options, scriptPath, result);
forEach(enumeratePotentialNonRelativeModules(fragment, scriptPath), moduleName => {
forEach(enumeratePotentialNonRelativeModules(fragment, scriptPath, options), moduleName => {
result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName));
});
@ -4704,7 +4709,7 @@ namespace ts {
return undefined;
}
function enumeratePotentialNonRelativeModules(fragment: string, scriptPath: string): string[] {
function enumeratePotentialNonRelativeModules(fragment: string, scriptPath: string, options: CompilerOptions): string[] {
const trailingSeperator = hasTrailingDirectorySeparator(fragment);
fragment = normalizePath(fragment);
@ -4733,19 +4738,21 @@ namespace ts {
return moduleName;
});
forEach(enumerateNodeModulesVisibleToScript(host, scriptPath, moduleNameFragment), visibleModule => {
if (!isNestedModule) {
nonRelativeModules.push(visibleModule.canBeImported ? visibleModule.moduleName : ensureTrailingDirectorySeparator(visibleModule.moduleName));
}
else {
const nestedFiles = host.readDirectory(visibleModule.moduleDir, supportedTypeScriptExtensions, /*exclude*/undefined, /*include*/["./*"]);
if (!options.moduleResolution || options.moduleResolution === ModuleResolutionKind.NodeJs) {
forEach(enumerateNodeModulesVisibleToScript(host, scriptPath, moduleNameFragment), visibleModule => {
if (!isNestedModule) {
nonRelativeModules.push(visibleModule.canBeImported ? visibleModule.moduleName : ensureTrailingDirectorySeparator(visibleModule.moduleName));
}
else {
const nestedFiles = host.readDirectory(visibleModule.moduleDir, supportedTypeScriptExtensions, /*exclude*/undefined, /*include*/["./*"]);
forEach(nestedFiles, (f) => {
const nestedModule = removeFileExtension(getBaseFileName(f));
nonRelativeModules.push(nestedModule);
});
}
});
forEach(nestedFiles, (f) => {
const nestedModule = removeFileExtension(getBaseFileName(f));
nonRelativeModules.push(nestedModule);
});
}
});
}
return deduplicate(nonRelativeModules);
}
@ -4791,16 +4798,18 @@ namespace ts {
result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName));
});
}
else if (options.typeRoots) {
else if (host.getDirectories && options.typeRoots) {
const absoluteRoots = map(options.typeRoots, rootDirectory => getAbsoluteProjectPath(rootDirectory, host, options.project));
forEach(absoluteRoots, absoluteRoot => getCompletionEntriesFromDirectory(host, options, absoluteRoot, result));
forEach(absoluteRoots, absoluteRoot => getCompletionEntriesFromDirectories(host, options, absoluteRoot, result));
}
// Also get all @types typings installed in visible node_modules directories
forEach(findPackageJsons(scriptPath), package => {
const typesDir = combinePaths(getDirectoryPath(package), "node_modules/@types");
getCompletionEntriesFromDirectory(host, options, typesDir, result);
});
if (host.getDirectories) {
// Also get all @types typings installed in visible node_modules directories
forEach(findPackageJsons(scriptPath), package => {
const typesDir = combinePaths(getDirectoryPath(package), "node_modules/@types");
getCompletionEntriesFromDirectories(host, options, typesDir, result);
});
}
return result;
}
@ -4817,16 +4826,10 @@ namespace ts {
return normalizePath(host.resolvePath(path));
}
function getCompletionEntriesFromDirectory(host: LanguageServiceHost, options: CompilerOptions, directory: string, result: CompletionEntry[]) {
if (directoryProbablyExists(directory, host)) {
const typeDirectories = host.readDirectory(directory, getSupportedExtensions(options), /*exclude*/undefined, /*include*/["./*/*"]);
const seen: {[index: string]: boolean} = {};
forEach(typeDirectories, typeFile => {
const typeDirectory = getDirectoryPath(typeFile);
if (!hasProperty(seen, typeDirectory)) {
seen[typeDirectory] = true;
result.push(createCompletionEntryForModule(getBaseFileName(typeDirectory), ScriptElementKind.externalModuleName));
}
function getCompletionEntriesFromDirectories(host: LanguageServiceHost, options: CompilerOptions, directory: string, result: CompletionEntry[]) {
if (host.getDirectories && directoryProbablyExists(directory, host)) {
forEach(host.getDirectories(directory), typeDirectory => {
result.push(createCompletionEntryForModule(getBaseFileName(typeDirectory), ScriptElementKind.externalModuleName));
});
}
}

View File

@ -0,0 +1,33 @@
/// <reference path='fourslash.ts' />
// @moduleResolution: classic
// @Filename: dir1/dir2/dir3/dir4/test0.ts
//// import * as foo1 from "f/*import_as0*/
//// import * as foo3 from "fake-module/*import_as1*/
//// import foo4 = require("f/*import_equals0*/
//// import foo6 = require("fake-module/*import_equals1*/
//// var foo7 = require("f/*require0*/
//// var foo9 = require("fake-module/*require1*/
// @Filename: package.json
//// { "dependencies": { "fake-module": "latest" } }
// @Filename: node_modules/fake-module/ts.ts
//// /*module1*/
// @Filename: dir1/dir2/dir3/package.json
//// { "dependencies": { "fake-module3": "latest" } }
// @Filename: dir1/dir2/dir3/node_modules/fake-module3/ts.ts
//// /*module3*/
const kinds = ["import_as", "import_equals", "require"];
for (const kind of kinds) {
goTo.marker(kind + "0");
verify.completionListIsEmpty();
goTo.marker(kind + "1");
verify.completionListIsEmpty();
}