UNPKG

@jsdocs-io/extractor

Version:

The API extractor for npm packages powering jsdocs.io

149 lines (148 loc) 6.09 kB
import { orderBy } from "natural-orderby"; import { Node, } from "ts-morph"; import { apparentType } from "./apparent-type.js"; import { docs } from "./docs.js"; import { formatSignature } from "./format-signature.js"; import { headText } from "./head-text.js"; import { id } from "./id.js"; import { isHidden } from "./is-hidden.js"; import { modifiersText } from "./modifiers-text.js"; import { sourceFilePath } from "./source-file-path.js"; import { typeCheckerType } from "./type-checker-type.js"; export async function extractClass(containerName, exportName, declaration) { const classId = id(containerName, "+class", exportName); return { kind: "class", id: classId, name: exportName, docs: docs(declaration), file: sourceFilePath(declaration), line: declaration.getStartLineNumber(), signature: await classSignature(declaration), constructors: await extractClassConstructors(classId, declaration), properties: await extractClassProperties(classId, declaration), methods: await extractClassMethods(classId, declaration), }; } async function classSignature(declaration) { const signature = headText(declaration); return await formatSignature("class", signature); } async function extractClassConstructors(classId, classDeclaration) { // Calling `getConstructors()` returns all constructors in ambient modules // but only the implementation constructor in normal modules. // We get the first constructor and then find all others starting from it. const declaration = classDeclaration.getConstructors().at(0); if (!declaration) return []; const implementation = declaration.getImplementation(); const constructorDeclarations = [ ...(implementation ? [implementation] : []), ...declaration.getOverloads(), ]; const constructors = []; for (const [index, declaration] of constructorDeclarations.entries()) { if (isHidden(declaration)) continue; constructors.push({ kind: "class-constructor", id: id(classId, "constructor", index > 0 ? `${index}` : ""), name: "constructor", docs: docs(declaration), file: sourceFilePath(declaration), line: declaration.getStartLineNumber(), signature: await classConstructorSignature(declaration), }); } return constructors; } async function classConstructorSignature(declaration) { const modifiers = modifiersText(declaration); const params = declaration .getParameters() .map((param) => { const name = param.getName(); const type = apparentType(param); const isRest = param.isRestParameter(); const dotsToken = isRest ? "..." : ""; const isOptional = param.isOptional(); const questionToken = !isRest && isOptional ? "?" : ""; return `${dotsToken}${name}${questionToken}: ${type}`; }) .join(","); const signature = `${modifiers} constructor(${params});`; return await formatSignature("class-constructor", signature); } async function extractClassProperties(classId, classDeclaration) { const propertiesDeclarations = [ ...classDeclaration.getInstanceProperties(), ...classDeclaration.getStaticProperties(), ]; const properties = []; for (const declaration of propertiesDeclarations) { if (!(Node.isParameterDeclaration(declaration) || Node.isPropertyDeclaration(declaration) || Node.isGetAccessorDeclaration(declaration))) { continue; } if (isHidden(declaration)) continue; const name = declaration.getName(); properties.push({ kind: "class-property", id: id(classId, "+property", name), name, docs: docs(declaration), file: sourceFilePath(declaration), line: declaration.getStartLineNumber(), signature: await classPropertySignature(name, declaration), }); } return orderBy(properties, "id"); } async function classPropertySignature(name, declaration) { const type = apparentType(declaration); if (Node.isParameterDeclaration(declaration) || Node.isPropertyDeclaration(declaration)) { const modifiers = modifiersText(declaration); const optional = declaration.hasQuestionToken() ? "?" : ""; const signature = `${modifiers} ${name}${optional}: ${type}`; return formatSignature("class-property", signature); } // GetAccessorDeclaration. const staticKeyword = declaration.isStatic() ? "static" : ""; const readonlyKeyword = declaration.getSetAccessor() === undefined ? "readonly" : ""; const signature = `${staticKeyword} ${readonlyKeyword} ${name}: ${type}`; return await formatSignature("class-property", signature); } async function extractClassMethods(classId, classDeclaration) { const methodsDeclarations = [ ...classDeclaration.getInstanceMethods(), ...classDeclaration.getStaticMethods(), ]; const methods = []; const seenMethods = new Set(); for (const declaration of methodsDeclarations) { const name = declaration.getName(); // Skip overloaded methods. if (seenMethods.has(name)) continue; if (isHidden(declaration)) continue; seenMethods.add(name); methods.push({ kind: "class-method", id: id(classId, "+method", name), name, docs: docs(declaration), file: sourceFilePath(declaration), line: declaration.getStartLineNumber(), signature: await classMethodSignature(name, declaration), }); } return orderBy(methods, "id"); } async function classMethodSignature(name, declaration) { const modifiers = modifiersText(declaration); const type = typeCheckerType(declaration); return await formatSignature("class-method", `${modifiers} ${name}: ${type}`); }