From 6f7a37c99aa43267f2aae4ade4db840fc344035d Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 20 Aug 2018 17:44:34 -0700 Subject: [PATCH] Added fourslash test --- src/compiler/moduleNameResolver.ts | 60 ++++++++++--------- src/services/pathCompletions.ts | 28 ++++++++- ...tionForStringLiteralNonrelativeImport13.ts | 36 +++++++++++ 3 files changed, 95 insertions(+), 29 deletions(-) create mode 100644 tests/cases/fourslash/completionForStringLiteralNonrelativeImport13.ts diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index d1054579e84..c474226217e 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -125,6 +125,34 @@ namespace ts { } } + /* @internal */ + export function getPackageJsonTypesVersionsOverride(typesVersions: MapLike) { + const typeScriptVersion = Version.parse(version); + let bestVersion: Version | undefined; + let bestVersionKey: string | undefined; + for (const key in typesVersions) { + if (!hasProperty(typesVersions, key)) continue; + + const keyVersion = Version.tryParse(key); + if (keyVersion === undefined) { + continue; + } + + // match the greatest version less than the current TypeScript version + if (keyVersion.compareTo(typeScriptVersion) <= 0 + && (bestVersion === undefined || keyVersion.compareTo(bestVersion) > 0)) { + bestVersion = keyVersion; + bestVersionKey = key; + } + } + + if (!bestVersionKey) { + return; + } + + return { version: bestVersionKey, directory: typesVersions[bestVersionKey] }; + } + function tryReadPackageJsonTypesVersion(jsonContent: PackageJson, baseDirectory: string, state: ModuleResolutionState): string | undefined { if (!hasProperty(jsonContent, "typesVersions")) { if (state.traceEnabled) { @@ -141,36 +169,12 @@ namespace ts { return; } - const typeScriptVersion = Version.parse(version); - let bestVersion: Version | undefined; - let bestVersionKey: string | undefined; - for (const key in typesVersions) { - if (!hasProperty(typesVersions, key)) continue; - - const keyVersion = Version.tryParse(key); - if (keyVersion === undefined) { - if (state.traceEnabled) { - // TODO(rbuckton): log - } - continue; - } - - // match the greatest version less than the current TypeScript version - if (keyVersion.compareTo(typeScriptVersion) <= 0 - && (bestVersion === undefined || keyVersion.compareTo(bestVersion) > 0)) { - bestVersion = keyVersion; - bestVersionKey = key; - } + const result = getPackageJsonTypesVersionsOverride(typesVersions); + if (!result) { + return undefined; } - if (!bestVersionKey) { - if (state.traceEnabled) { - // TODO(rbuckton): log - } - return; - } - - const bestVersionPath = typesVersions[bestVersionKey]; + const { version: bestVersionKey, directory: bestVersionPath } = result; if (!isString(bestVersionPath)) { if (state.traceEnabled) { trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_1_got_2, `typesVersion['${bestVersionKey}']`, "string", typeof bestVersionPath); diff --git a/src/services/pathCompletions.ts b/src/services/pathCompletions.ts index 3a546573db7..f958163bb14 100644 --- a/src/services/pathCompletions.ts +++ b/src/services/pathCompletions.ts @@ -107,10 +107,24 @@ namespace ts.Completions.PathCompletions { // const absolutePath = normalizeAndPreserveTrailingSlash(isRootedDiskPath(fragment) ? fragment : combinePaths(scriptPath, fragment)); // TODO(rbuckton): should use resolvePaths const absolutePath = resolvePath(scriptPath, fragment); - const baseDirectory = hasTrailingDirectorySeparator(absolutePath) ? absolutePath : getDirectoryPath(absolutePath); + let baseDirectory = hasTrailingDirectorySeparator(absolutePath) ? absolutePath : getDirectoryPath(absolutePath); const ignoreCase = !(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames()); if (tryDirectoryExists(host, baseDirectory)) { + // check for a version redirect + const packageJsonPath = findPackageJson(baseDirectory, host); + if (packageJsonPath) { + const packageJson = readJson(packageJsonPath, host as { readFile: (filename: string) => string | undefined }); + const typesVersions = (packageJson as any).typesVersions; + if (typeof typesVersions === "object") { + const result = getPackageJsonTypesVersionsOverride(typesVersions); + const versionPath = result && result.directory; + if (versionPath) { + baseDirectory = resolvePath(baseDirectory, versionPath); + } + } + } + // Enumerate the available files if possible const files = tryReadDirectory(host, baseDirectory, extensions, /*exclude*/ undefined, /*include*/ ["./*"]); @@ -390,6 +404,18 @@ namespace ts.Completions.PathCompletions { return paths; } + function findPackageJson(directory: string, host: LanguageServiceHost): string | undefined { + let packageJson: string | undefined; + forEachAncestorDirectory(directory, ancestor => { + if (ancestor === "node_modules") return true; + packageJson = findConfigFile(ancestor, (f) => tryFileExists(host, f), "package.json"); + if (packageJson) { + return true; // break out + } + }); + return packageJson; + } + function enumerateNodeModulesVisibleToScript(host: LanguageServiceHost, scriptPath: string): ReadonlyArray { if (!host.readFile || !host.fileExists) return emptyArray; diff --git a/tests/cases/fourslash/completionForStringLiteralNonrelativeImport13.ts b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport13.ts new file mode 100644 index 00000000000..db88a272ad5 --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteralNonrelativeImport13.ts @@ -0,0 +1,36 @@ +/// + +// Should give completions based on typesVersions + +// @Filename: node_modules/ext/package.json +//// { +//// "name": "ext", +//// "version": "1.0.0", +//// "types": "index", +//// "typesVersions": { +//// "3.0": "ts3.0" +//// } +//// } + +// @Filename: node_modules/ext/index.d.ts +//// export {}; + +// @Filename: node_modules/ext/aaa.d.ts +//// export {}; + +// @Filename: node_modules/ext/ts3.0/index.d.ts +//// export {}; + +// @Filename: node_modules/ext/ts3.0/zzz.d.ts +//// export {}; + +// @Filename: main.ts +//// import * as ext1 from "ext//*import_as0*/ +//// import ext2 = require("ext//*import_equals0*/ +//// var ext2 = require("ext//*require0*/ + +verify.completions({ + marker: test.markerNames(), + exact: ["index", "zzz"], + isNewIdentifierLocation: true, +});