diff --git a/src/server/project.ts b/src/server/project.ts index 889e97b24c5..e35183c411a 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -527,6 +527,17 @@ namespace ts.server { return this.projectService.getSourceFileLike(fileName, this); } + /*@internal*/ + getUnresolvedImports(): SortedReadonlyArray | undefined { + return this.typingsCache.getUnresolvedImports(this); + } + + /*@internal*/ + hasTypeAcquisitionAcquiredNodeTypings?(): boolean { + const globalCache = this.getGlobalCache(); + return !!globalCache && some(this.typingFiles, fileName => startsWithDirectory(fileName, combinePaths(globalCache, "node_modules", "@types", "node"), this.getCanonicalFileName)); + } + private shouldEmitFile(scriptInfo: ScriptInfo) { return scriptInfo && !scriptInfo.isDynamicOrHasMixedContent(); } diff --git a/src/server/typingsCache.ts b/src/server/typingsCache.ts index 8f3cb7607b0..88208f97c17 100644 --- a/src/server/typingsCache.ts +++ b/src/server/typingsCache.ts @@ -138,5 +138,12 @@ namespace ts.server { this.perProjectCache.delete(project.getProjectName()); this.installer.onProjectClosed(project); } + + getUnresolvedImports(project: Project): SortedReadonlyArray | undefined { + const entry = this.perProjectCache.get(project.getProjectName()); + if (entry) { + return entry.unresolvedImports; + } + } } } diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 87b61efff8b..0f4f602fc21 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -570,25 +570,40 @@ namespace ts.codefix { export function forEachExternalModuleToImportFrom(checker: TypeChecker, host: LanguageServiceHost, from: SourceFile, allSourceFiles: ReadonlyArray, cb: (module: Symbol) => void) { const packageJsons = findPackageJsons(getDirectoryPath(from.fileName), host); const dependencyIterator = readPackageJsonDependencies(); + const isJS = isSourceFileJS(from); forEachExternalModule(checker, allSourceFiles, (module, sourceFile) => { - if (sourceFile === undefined) { - const maybeSourceFile = module.valueDeclaration.getSourceFile(); - if (!maybeSourceFile || packageJsonAllowsImporting(maybeSourceFile.fileName)) { + if (sourceFile === undefined && packageJsonAllowsImporting(stripQuotes(module.getName()))) { + cb(module); + } + else if (sourceFile && sourceFile !== from && isImportablePath(from.fileName, sourceFile.fileName)) { + const moduleSpecifier = getModuleSpecifierFromFileName(sourceFile.fileName); + if (!moduleSpecifier || packageJsonAllowsImporting(moduleSpecifier)) { cb(module); } } - else if (sourceFile && sourceFile !== from && isImportablePath(from.fileName, sourceFile.fileName) && packageJsonAllowsImporting(sourceFile.fileName)) { - cb(module); - } }); let seenDeps: Map | undefined; - function packageJsonAllowsImporting(fileName: string): boolean { - const moduleSpecifier = getModuleSpecifierFromFileName(fileName); - if (!moduleSpecifier || !packageJsons.length || seenDeps && seenDeps.has(moduleSpecifier)) { + let hasNodeTypingsViaTypeAcquisition: boolean | undefined; + function packageJsonAllowsImporting(moduleSpecifier: string): boolean { + if (!packageJsons.length || seenDeps && seenDeps.has(moduleSpecifier)) { return true; } + // If we’re in JavaScript, it can be difficult to tell whether the user wants to import + // from Node core modules or not. We can start by seeing if type acquisition has already + // downloaded @types/node, because if it has, that indicates that the user has already + // been using some Node core modules directly, as opposed to simply having @types/node + // accidentally as a dependency of a dependency. + if (isJS && JsTyping.nodeCoreModules.has(moduleSpecifier)) { + if (hasNodeTypingsViaTypeAcquisition === undefined) { + hasNodeTypingsViaTypeAcquisition = host.hasTypeAcquisitionAcquiredNodeTypings && host.hasTypeAcquisitionAcquiredNodeTypings(); + } + if (hasNodeTypingsViaTypeAcquisition) { + return true; + } + } + let packageName: string; while (packageName = dependencyIterator.next().value) { const nameWithoutTypes = getPackageNameFromTypesPackageName(packageName); diff --git a/src/services/types.ts b/src/services/types.ts index 099263063a8..2f67f6152c7 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -236,6 +236,8 @@ namespace ts { getDocumentPositionMapper?(generatedFileName: string, sourceFileName?: string): DocumentPositionMapper | undefined; /* @internal */ getSourceFileLike?(fileName: string): SourceFileLike | undefined; + /* @internal */ + hasTypeAcquisitionAcquiredNodeTypings?(): boolean; } /* @internal */