diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts index 6c8c79adefb81..e242e7e0b1963 100644 --- a/src/compiler/resolutionCache.ts +++ b/src/compiler/resolutionCache.ts @@ -220,24 +220,17 @@ namespace ts { function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference): CachedResolvedModuleWithFailedLookupLocations { const primaryResult = ts.resolveModuleName(moduleName, containingFile, compilerOptions, host, moduleResolutionCache, redirectedReference); - // return result immediately only if global cache support is not enabled or if it is .ts, .tsx or .d.ts - if (!resolutionHost.getGlobalCache) { - return primaryResult; - } - // otherwise try to load typings from @types - const globalCache = resolutionHost.getGlobalCache(); + const globalCache = resolutionHost.getGlobalCache ? resolutionHost.getGlobalCache() : undefined; if (globalCache !== undefined && !isExternalModuleNameRelative(moduleName) && !(primaryResult.resolvedModule && extensionIsTS(primaryResult.resolvedModule.extension))) { // 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, resolutionHost.projectName, compilerOptions, host, globalCache); - if (resolvedModule) { - return { resolvedModule, failedLookupLocations: addRange(primaryResult.failedLookupLocations as string[], failedLookupLocations) }; - } + return { resolvedModule: resolvedModule || primaryResult.resolvedModule, failedLookupLocations: [...primaryResult.failedLookupLocations, ...failedLookupLocations] }; + } + else { + // Default return the result from the first pass + return primaryResult; } - - // Default return the result from the first pass - return primaryResult; } function resolveNamesWithLocalCache( @@ -390,10 +383,17 @@ namespace ts { return true; } + const globalCache = resolutionHost.getGlobalCache ? resolutionHost.getGlobalCache() : undefined; + // Using identity for getCanonicalFileName because assuming that if a path came from globalCache its case hasn't been changed. + if (globalCache !== undefined && dirPath === globalCache) { + return true; + } + + // Path must have at least 3 components for (let searchIndex = nextDirectorySeparator + 1, searchLevels = 2; searchLevels > 0; searchLevels--) { searchIndex = dirPath.indexOf(directorySeparator, searchIndex) + 1; if (searchIndex === 0) { - // Folder isnt at expected minimun levels + // Folder isnt at expected minimum levels return false; } } diff --git a/src/testRunner/unittests/typingsInstaller.ts b/src/testRunner/unittests/typingsInstaller.ts index b06c22d5d7912..ac78a2ece97fa 100644 --- a/src/testRunner/unittests/typingsInstaller.ts +++ b/src/testRunner/unittests/typingsInstaller.ts @@ -945,7 +945,7 @@ namespace ts.projectSystem { }); it("should install typings for unresolved imports", () => { - const file = { + const file: TestFSWithWatch.File = { path: "/a/b/app.js", content: ` import * as fs from "fs"; @@ -954,36 +954,42 @@ namespace ts.projectSystem { const cachePath = "/a/cache"; const node = { path: cachePath + "/node_modules/@types/node/index.d.ts", - content: "export let x: number" + content: "export let x: number", }; - const commander = { - path: cachePath + "/node_modules/@types/commander/index.d.ts", - content: "export let y: string" + const commanderJS: TestFSWithWatch.File = { + path: "/node_modules/commander/index.js", + content: "module.exports = 0", }; - const host = createServerHost([file]); + + const typeNames: ReadonlyArray = ["node", "commander"]; + const typePath = (name: string): string => `${cachePath}/node_modules/@types/${name}/index.d.ts`; + const host = createServerHost([file, node, commanderJS]); const installer = new (class extends Installer { constructor() { - super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("node", "commander") }); + super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry(...typeNames) }); } installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) { - const installedTypings = ["@types/node", "@types/commander"]; - const typingFiles = [node, commander]; + const installedTypings = typeNames.map(name => `@types/${name}`); + const typingFiles = typeNames.map((name): TestFSWithWatch.File => ({ path: typePath(name), content: "" })); executeCommand(this, host, installedTypings, typingFiles, cb); } })(); const service = createProjectService(host, { typingsInstaller: installer }); service.openClientFile(file.path); + checkWatchedFiles(host, [...flatMap(["/a/b", "/a", ""], x => [x + "/tsconfig.json", x + "/jsconfig.json"]), "/a/lib/lib.d.ts"]); + checkWatchedDirectories(host, [], /*recursive*/ false); + checkWatchedDirectories(host, ["/node_modules", "/a/b/node_modules", "/a/cache/node_modules", "/a/b/node_modules/@types", "/a/b/bower_components"], /*recursive*/ true); + service.checkNumberOfProjects({ inferredProjects: 1 }); - checkProjectActualFiles(service.inferredProjects[0], [file.path]); + checkProjectActualFiles(service.inferredProjects[0], [file.path, commanderJS.path]); installer.installAll(/*expectedCount*/1); - - assert.isTrue(host.fileExists(node.path), "typings for 'node' should be created"); - assert.isTrue(host.fileExists(commander.path), "typings for 'commander' should be created"); - + for (const name of typeNames) { + assert.isTrue(host.fileExists(typePath(name)), `typings for '${name}' should be created`); + } host.checkTimeoutQueueLengthAndRun(2); - checkProjectActualFiles(service.inferredProjects[0], [file.path, node.path, commander.path]); + checkProjectActualFiles(service.inferredProjects[0], [file.path, ...typeNames.map(typePath)]); }); it("should pick typing names from non-relative unresolved imports", () => {