diff --git a/src/lib/models/types.ts b/src/lib/models/types.ts index ce4fc8db4..178e48e46 100644 --- a/src/lib/models/types.ts +++ b/src/lib/models/types.ts @@ -1,3 +1,6 @@ +import * as fs from "fs"; +import * as path from "path"; + import type * as ts from "typescript"; import type { Context } from "../converter"; import { Reflection } from "./reflections/abstract"; @@ -847,18 +850,22 @@ export class ReferenceType extends Type { .fileName.replace(/\\/g, "/"); if (!symbolPath) return ref; - let startIndex = symbolPath.indexOf("node_modules/"); - if (startIndex === -1) return ref; - startIndex += "node_modules/".length; - let stopIndex = symbolPath.indexOf("/", startIndex); - // Scoped package, e.g. `@types/node` - if (symbolPath[startIndex] === "@") { - stopIndex = symbolPath.indexOf("/", stopIndex + 1); + // Attempt to decide package name from path if it contains "node_modules" + let startIndex = symbolPath.lastIndexOf("node_modules/"); + if (startIndex !== -1) { + startIndex += "node_modules/".length; + let stopIndex = symbolPath.indexOf("/", startIndex); + // Scoped package, e.g. `@types/node` + if (symbolPath[startIndex] === "@") { + stopIndex = symbolPath.indexOf("/", stopIndex + 1); + } + const packageName = symbolPath.substring(startIndex, stopIndex); + ref.package = packageName; + return ref; } - const packageName = symbolPath.substring(startIndex, stopIndex); - ref.package = packageName; - + // Otherwise, look for a "package.json" file in a parent path + ref.package = findPackageForPath(symbolPath); return ref; } @@ -1287,3 +1294,32 @@ export class UnknownType extends Type { }; } } + +const packageJsonLookupCache: Record = {}; + +function findPackageForPath(sourcePath: string): string | undefined { + if (packageJsonLookupCache[sourcePath] !== undefined) { + return packageJsonLookupCache[sourcePath]; + } + let basePath = sourcePath; + for (;;) { + const nextPath = path.dirname(basePath); + if (nextPath === basePath) { + return; + } + basePath = nextPath; + const projectPath = path.join(basePath, "package.json"); + try { + const packageJsonData = fs.readFileSync(projectPath, { + encoding: "utf8", + }); + const packageJson = JSON.parse(packageJsonData); + if (packageJson.name !== undefined) { + packageJsonLookupCache[sourcePath] = packageJson.name; + } + return packageJson.name; + } catch (err) { + continue; + } + } +}