UNPKG

typedoc

Version:

Create api documentation for TypeScript projects.

94 lines (93 loc) 3.55 kB
import { ReflectionSymbolId } from "#models"; import { findPackageForPath, getCommonDirectory, getQualifiedName, normalizePath, readFile } from "#node-utils"; import { Validation } from "#utils"; import { existsSync } from "fs"; import { join, relative, resolve } from "node:path"; import ts from "typescript"; const declarationMapCache = new Map(); let transientCount = 0; const transientIds = new WeakMap(); export function createSymbolId(symbol, declaration) { declaration ??= symbol.declarations?.[0]; const tsSource = declaration?.getSourceFile().fileName ?? ""; const sourceFileName = resolveDeclarationMaps(tsSource); let packageName; let packagePath; const packageInfo = findPackageForPath(tsSource); if (packageInfo) { let packageDir; [packageName, packageDir] = packageInfo; packagePath = normalizePath(relative(packageDir, sourceFileName)); } else { packageName = ReflectionSymbolId.UNKNOWN_PACKAGE; packagePath = normalizePath(sourceFileName); } let qualifiedName; if (symbol.declarations?.some(ts.isSourceFile)) { qualifiedName = ""; } else { qualifiedName = getQualifiedName(symbol, symbol.name); } const pos = declaration?.getStart() ?? Infinity; let transientId = NaN; if (symbol.flags & ts.SymbolFlags.Transient) { transientId = transientIds.get(symbol) ?? ++transientCount; transientIds.set(symbol, transientId); } const id = new ReflectionSymbolId({ packageName, packagePath, qualifiedName, }); id.pos = pos; id.transientId = transientId; id.fileName = normalizePath(sourceFileName); return id; } function resolveDeclarationMaps(file) { if (!/\.d\.[cm]?ts$/.test(file)) return file; if (declarationMapCache.has(file)) return declarationMapCache.get(file); const mapFile = file + ".map"; if (!existsSync(mapFile)) return file; let sourceMap; try { sourceMap = JSON.parse(readFile(mapFile)); } catch { return file; } if (Validation.validate({ file: String, sourceRoot: Validation.optional(String), sources: [Array, String], }, sourceMap)) { // There's a pretty large assumption in here that we only have // 1 source file per js file. This is a pretty standard typescript approach, // but people might do interesting things with transpilation that could break this. let source = sourceMap.sources[0]; // If we have a sourceRoot, trim any leading slash from the source, and join them // Similar to how it's done at https://github.com/mozilla/source-map/blob/58819f09018d56ef84dc41ba9c93f554e0645169/lib/util.js#L412 if (sourceMap.sourceRoot !== undefined) { source = source.replace(/^\//, ""); source = join(sourceMap.sourceRoot, source); } const result = resolve(mapFile, "..", source); declarationMapCache.set(file, result); return result; } return file; } // See also: inferEntryPoints in entry-point.ts export function addInferredDeclarationMapPaths(opts, files) { const rootDir = opts.rootDir || getCommonDirectory(files); const declDir = opts.declarationDir || opts.outDir || rootDir; for (const file of files) { const mapFile = normalizePath(resolve(declDir, relative(rootDir, file)).replace(/\.([cm]?[tj]s)x?$/, ".d.$1")); declarationMapCache.set(mapFile, file); } }