diff --git a/src/harness/unittests/typingsInstaller.ts b/src/harness/unittests/typingsInstaller.ts index 2f83fe90c8a17..0cb021b5d8371 100644 --- a/src/harness/unittests/typingsInstaller.ts +++ b/src/harness/unittests/typingsInstaller.ts @@ -1340,6 +1340,27 @@ namespace ts.projectSystem { assert.deepEqual(result.newTypingNames, ["bar"]); }); + it("should gracefully handle packages that have been removed from the types-registry", () => { + const f = { + path: "/a/b/app.js", + content: "" + }; + const node = { + path: "/a/b/node.d.ts", + content: "" + }; + const host = createServerHost([f, node]); + const cache = createMapFromTemplate({ node: { typingLocation: node.path, version: Semver.parse("1.3.0") } }); + const logger = trackingLogger(); + const result = JsTyping.discoverTypings(host, logger.log, [f.path], getDirectoryPath(f.path), emptySafeList, cache, { enable: true }, ["fs", "bar"], emptyMap); + assert.deepEqual(logger.finish(), [ + 'Inferred typings from unresolved imports: ["node","bar"]', + 'Result: {"cachedTypingPaths":[],"newTypingNames":["node","bar"],"filesToWatch":["/a/b/bower_components","/a/b/node_modules"]}', + ]); + assert.deepEqual(result.cachedTypingPaths, []); + assert.deepEqual(result.newTypingNames, ["node", "bar"]); + }); + it("should search only 2 levels deep", () => { const app = { path: "/app.js", diff --git a/src/server/typingsInstaller/nodeTypingsInstaller.ts b/src/server/typingsInstaller/nodeTypingsInstaller.ts index 35f926f1c1407..13886c43798f4 100644 --- a/src/server/typingsInstaller/nodeTypingsInstaller.ts +++ b/src/server/typingsInstaller/nodeTypingsInstaller.ts @@ -106,7 +106,7 @@ namespace ts.server.typingsInstaller { if (this.log.isEnabled()) { this.log.writeLine(`Updating ${typesRegistryPackageName} npm package...`); } - this.execSyncAndLog(`${this.npmPath} install --ignore-scripts ${typesRegistryPackageName}`, { cwd: globalTypingsCacheLocation }); + this.execSyncAndLog(`${this.npmPath} install --ignore-scripts ${typesRegistryPackageName}@${this.latestDistTag}`, { cwd: globalTypingsCacheLocation }); if (this.log.isEnabled()) { this.log.writeLine(`Updated ${typesRegistryPackageName} npm package`); } diff --git a/src/server/typingsInstaller/typingsInstaller.ts b/src/server/typingsInstaller/typingsInstaller.ts index 7d8cf02fab08a..59ea6aa5033b4 100644 --- a/src/server/typingsInstaller/typingsInstaller.ts +++ b/src/server/typingsInstaller/typingsInstaller.ts @@ -349,8 +349,8 @@ namespace ts.server.typingsInstaller { } // packageName is guaranteed to exist in typesRegistry by filterTypings - const distTags = this.typesRegistry.get(packageName); - const newVersion = Semver.parse(distTags[`ts${versionMajorMinor}`] || distTags[latestDistTag]); + const distTags = this.typesRegistry.get(packageName)!; + const newVersion = Semver.parse(distTags[`ts${versionMajorMinor}`] || distTags[this.latestDistTag]); const newTyping: JsTyping.CachedTyping = { typingLocation: typingFile, version: newVersion }; this.packageNameToTypingLocation.set(packageName, newTyping); installedTypingFiles.push(typingFile); @@ -523,12 +523,12 @@ namespace ts.server.typingsInstaller { protected abstract installWorker(requestId: number, packageNames: string[], cwd: string, onRequestCompleted: RequestCompletedAction): void; protected abstract sendResponse(response: SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes): void; + + protected readonly latestDistTag = "latest"; } /* @internal */ export function typingsName(packageName: string): string { return `@types/${packageName}@ts${versionMajorMinor}`; } - - const latestDistTag = "latest"; } diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index c6f97ec2a1acb..0f69146ae9424 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -160,7 +160,8 @@ namespace ts.JsTyping { } // Add the cached typing locations for inferred typings that are already installed packageNameToTypingLocation.forEach((typing, name) => { - if (inferredTypings.has(name) && inferredTypings.get(name) === undefined && isTypingUpToDate(typing, typesRegistry.get(name))) { + const registryEntry = typesRegistry.get(name); + if (inferredTypings.has(name) && inferredTypings.get(name) === undefined && registryEntry !== undefined && isTypingUpToDate(typing, registryEntry)) { inferredTypings.set(name, typing.typingLocation); } });