@jsdocs-io/extractor
Version:
The API extractor for npm packages powering jsdocs.io
1,193 lines (1,132 loc) • 41.9 kB
JavaScript
// src/bun-package-manager.ts
import { Effect as Effect2 } from "effect";
import { execa } from "execa";
// src/package-manager.ts
import { Context, Data } from "effect";
var InstallPackageError = class extends Data.TaggedError("InstallPackageError") {
};
var PackageManager = class extends Context.Tag("PackageManager")() {
};
// src/bun-package-manager.ts
var bunPackageManager = (bunPath = "bun") => PackageManager.of({
installPackage: ({ pkg, cwd }) => Effect2.gen(function* () {
const { stdout } = yield* Effect2.tryPromise({
try: () => execa(bunPath, ["add", pkg, "--verbose"], { cwd }),
catch: (e) => new InstallPackageError({ cause: e })
});
const lines = stdout.split("\n");
const beginHash = lines.findIndex((line) => line.startsWith("-- BEGIN SHA512/256"));
const endHash = lines.findIndex((line) => line.startsWith("-- END HASH"));
const installedPackages = lines.slice(beginHash + 1, endHash);
return installedPackages;
})
});
// src/create-project.ts
import { Data as Data2, Effect as Effect3 } from "effect";
import { ModuleKind, ModuleResolutionKind, Project, ScriptTarget } from "ts-morph";
var ProjectError = class extends Data2.TaggedError("ProjectError") {
};
var createProject = ({ indexFilePath, cwd }) => Effect3.try({
try: () => {
const project = new Project({
compilerOptions: {
// See https://github.com/dsherret/ts-morph/issues/938
// and https://github.com/microsoft/TypeScript/blob/master/lib/lib.esnext.full.d.ts
lib: ["lib.esnext.full.d.ts"],
target: ScriptTarget.ESNext,
module: ModuleKind.ESNext,
moduleResolution: ModuleResolutionKind.Bundler,
// By default, ts-morph creates a project rooted in the current working directory.
// We must change the `typeRoots` directory to the temporary directory
// where the packages are installed, otherwise TypeScript will discover
// `@types` packages from our local `node_modules` directory.
// See https://www.typescriptlang.org/tsconfig#typeRoots.
typeRoots: [cwd]
}
});
const indexFile = project.addSourceFileAtPath(indexFilePath);
project.resolveSourceFileDependencies();
return { project, indexFile };
},
catch: (e) => new ProjectError({ cause: e })
});
// src/extract-declarations.ts
import { orderBy as orderBy3 } from "natural-orderby";
import {
Node as Node19
} from "ts-morph";
// src/ambient-modules-declarations.ts
import { Node as Node3 } from "ts-morph";
// src/is-hidden.ts
import { Node as Node2, SyntaxKind as SyntaxKind2 } from "ts-morph";
// src/docs.ts
import { Node, SyntaxKind } from "ts-morph";
// src/parse-doc-comment.ts
import { TSDocParser } from "@microsoft/tsdoc";
import memoize from "memoize";
var parseDocComment = memoize((s) => {
const parser = new TSDocParser();
return parser.parseString(s).docComment;
});
// src/docs.ts
var docs = (node) => [
// List of unique jsdoc comments that are closest to the node.
...new Set(nodesWithDocs(node).flatMap((node2) => lastDoc(node2) ?? []))
];
var nodesWithDocs = (node) => {
if (Node.isVariableDeclaration(node)) {
return [node.getVariableStatementOrThrow()];
}
if (Node.isExpression(node)) {
return [node.getParent()];
}
if (Node.isOverloadable(node) && !Node.isConstructorDeclaration(node)) {
const implementation = node.getImplementation();
return [...node.getOverloads(), ...implementation ? [implementation] : []];
}
if (Node.isMethodSignature(node) && node.getParent().getKind() === SyntaxKind.InterfaceDeclaration) {
const methodName = node.getName();
const overloads = node.getParentIfKindOrThrow(SyntaxKind.InterfaceDeclaration).getMethods().filter((method) => method.getName() === methodName);
return overloads;
}
return [node];
};
var lastDoc = (node) => {
const doc = node.getLastChildByKind(SyntaxKind.JSDoc)?.getText();
if (!doc) {
return void 0;
}
if (parseDocComment(doc).modifierTagSet.isPackageDocumentation()) {
return void 0;
}
return doc;
};
// src/is-hidden.ts
var isHidden = (node) => (
// Check if a declaration is part of a package's private API.
isPrivateProperty(node) || hasPrivateModifier(node) || hasInternalTag(node)
);
var isPrivateProperty = (node) => (
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_properties.
Node2.hasName(node) && Node2.isPrivateIdentifier(node.getNameNode())
);
var hasPrivateModifier = (node) => (
// See https://www.typescriptlang.org/docs/handbook/2/classes.html#private.
Node2.isModifierable(node) && node.hasModifier(SyntaxKind2.PrivateKeyword)
);
var hasInternalTag = (node) => (
// See https://tsdoc.org/pages/tags/internal.
docs(node).some((doc) => parseDocComment(doc).modifierTagSet.isInternal())
);
// src/source-file-path.ts
var sourceFilePath = (node) => node.getSourceFile().getFilePath().split("node_modules").pop();
// src/ambient-modules-declarations.ts
var ambientModulesDeclarations = (containerName, project, pkgName) => {
const ambientModulesDeclarations2 = [];
for (const symbol of project.getAmbientModules()) {
for (const declaration of symbol.getDeclarations()) {
if (isHidden(declaration) || !Node3.isModuleDeclaration(declaration)) {
continue;
}
if (pkgName && !sourceFilePath(declaration).startsWith(`/${pkgName}`)) {
continue;
}
const exportName = declaration.getName();
ambientModulesDeclarations2.push({
containerName,
exportName,
declaration
});
}
}
return ambientModulesDeclarations2;
};
// src/export-equals-declarations.ts
import { SyntaxKind as SyntaxKind3 } from "ts-morph";
// src/is-exported-declarations.ts
import { Node as Node4 } from "ts-morph";
var isExportedDeclarations = (node) => Node4.isVariableDeclaration(node) || Node4.isFunctionDeclaration(node) || Node4.isClassDeclaration(node) || Node4.isInterfaceDeclaration(node) || Node4.isEnumDeclaration(node) || Node4.isTypeAliasDeclaration(node) || Node4.isModuleDeclaration(node) || Node4.isExpression(node) || Node4.isSourceFile(node);
// src/is-namespace.ts
import { Node as Node5 } from "ts-morph";
var isNamespace = (node) => Node5.isModuleDeclaration(node);
// src/is-shorthand-ambient-module.ts
import { Node as Node6 } from "ts-morph";
var isShorthandAmbientModule = (node) => (
// Shorthand ambient modules have no body (e.g., `declare module 'foo';`)
// and their name includes the quotes (e.g., name === "'foo'").
Node6.isModuleDeclaration(node) && !node.hasBody()
);
// src/export-equals-declarations.ts
var exportEqualsDeclarations = (containerName, container) => {
if (isShorthandAmbientModule(container)) {
return [];
}
const exportIdentifier = container.getExportAssignment((assignment) => assignment.isExportEquals())?.getLastChildByKind(SyntaxKind3.Identifier);
if (!exportIdentifier) {
return [];
}
const exportName = exportIdentifier.getText();
const exportEqualsDeclarations2 = [];
for (const declaration of exportIdentifier.getDefinitionNodes()) {
if (isHidden(declaration) || !isExportedDeclarations(declaration)) {
continue;
}
if (isNamespace(declaration)) {
continue;
}
exportEqualsDeclarations2.push({ containerName, exportName, declaration });
}
return exportEqualsDeclarations2;
};
// src/exported-declarations.ts
var exportedDeclarations = (containerName, container) => {
const exportedDeclarations2 = [];
for (const [exportName, declarations] of container.getExportedDeclarations()) {
for (const declaration of declarations) {
if (isHidden(declaration) || !isExportedDeclarations(declaration)) {
continue;
}
exportedDeclarations2.push({ containerName, exportName, declaration });
}
}
return exportedDeclarations2;
};
// src/extract-class.ts
import { orderBy } from "natural-orderby";
import {
Node as Node7
} from "ts-morph";
// src/apparent-type.ts
import { TypeFormatFlags, ts } from "ts-morph";
var apparentType = (node) => (
// See:
// https://github.com/dsherret/ts-morph/issues/453#issuecomment-427405736
// https://github.com/dsherret/ts-morph/issues/453#issuecomment-667578386
node.getType().getApparentType().getText(
node,
ts.TypeFormatFlags.NoTruncation | TypeFormatFlags.UseAliasDefinedOutsideCurrentScope
).replace(/^Number$/, "number").replace(/^Boolean$/, "boolean").replace(/^String$/, "string")
);
// src/format-signature.ts
import { format } from "prettier";
var formatSignature = async (kind, signature) => {
const s = signature.trim().replace(/^export\s+/, "").replace(/^default\s+/, "").replace(/^declare\s+/, "");
switch (kind) {
case "variable": {
const raw = s.replace("default:", "_______:");
const formatted = await formatWithPrettier(raw);
return formatted.replace("_______:", "default:");
}
case "function": {
const raw = `let ${s.replace("default:", "_______:")}`;
const formatted = await formatWithPrettier(raw);
return formatted.replace("_______:", "default:").replace(/^let\s/, "");
}
case "class":
case "interface":
case "enum":
case "type":
case "namespace": {
const formatted = await formatWithPrettier(s);
return formatted;
}
case "class-constructor":
case "class-property":
case "class-method":
case "interface-property":
case "interface-method":
case "interface-construct-signature":
case "interface-call-signature":
case "interface-index-signature":
case "interface-get-accessor":
case "interface-set-accessor":
case "enum-member": {
const parentKind = kind.split("-")[0];
const raw = `${parentKind} P { ${s} }`;
const formattedParent = await formatWithPrettier(raw);
const formatted = formattedParent.split("\n").slice(1, -1).map((line) => line.replace(/^\s{2}/, "")).join("\n").replace(/,$/, "");
return formatted;
}
}
};
var formatWithPrettier = async (s) => {
try {
const formatted = (await format(s, { parser: "typescript" })).trim();
return formatted;
} catch {
return s;
}
};
// src/head-text.ts
import {
SyntaxKind as SyntaxKind4
} from "ts-morph";
var headText = (declaration) => {
const parts = [];
for (const node of declaration.getChildren()) {
if (node.getKind() === SyntaxKind4.JSDoc) {
continue;
}
if (node.getKind() === SyntaxKind4.OpenBraceToken) {
break;
}
parts.push(node.getText());
}
parts.push("{}");
return parts.join(" ");
};
// src/id.ts
var id = (...parts) => parts.filter(Boolean).join(".");
// src/modifiers-text.ts
import { SyntaxKind as SyntaxKind5 } from "ts-morph";
var modifiersText = (node) => node.getModifiers().filter((modifier) => modifier.getKind() !== SyntaxKind5.PublicKeyword).map((modifier) => modifier.getText()).join(" ");
// src/type-checker-type.ts
import { TypeFormatFlags as TypeFormatFlags2 } from "ts-morph";
var typeCheckerType = (node) => {
try {
const typeChecker = node.getProject().getTypeChecker().compilerObject;
const nodeType = typeChecker.getTypeAtLocation(node.compilerNode);
return typeChecker.typeToString(
nodeType,
node.compilerNode,
TypeFormatFlags2.NoTruncation | TypeFormatFlags2.UseAliasDefinedOutsideCurrentScope
);
} catch {
return "any";
}
};
// src/extract-class.ts
var extractClass = async (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)
};
};
var classSignature = async (declaration) => {
const signature = headText(declaration);
return await formatSignature("class", signature);
};
var extractClassConstructors = async (classId, classDeclaration) => {
const declaration = classDeclaration.getConstructors()[0];
if (!declaration) {
return [];
}
const implementation = declaration.getImplementation();
const constructorDeclarations = [
...implementation ? [implementation] : [],
...declaration.getOverloads()
];
const constructors = [];
for (const [index, declaration2] of constructorDeclarations.entries()) {
if (isHidden(declaration2)) {
continue;
}
constructors.push({
kind: "class-constructor",
id: id(classId, "constructor", index > 0 ? `${index}` : ""),
name: "constructor",
docs: docs(declaration2),
file: sourceFilePath(declaration2),
line: declaration2.getStartLineNumber(),
signature: await classConstructorSignature(declaration2)
});
}
return constructors;
};
var classConstructorSignature = async (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);
};
var extractClassProperties = async (classId, classDeclaration) => {
const propertiesDeclarations = [
...classDeclaration.getInstanceProperties(),
...classDeclaration.getStaticProperties()
];
const properties = [];
for (const declaration of propertiesDeclarations) {
if (isHidden(declaration) || !(Node7.isParameterDeclaration(declaration) || Node7.isPropertyDeclaration(declaration) || Node7.isGetAccessorDeclaration(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");
};
var classPropertySignature = async (name, declaration) => {
const type = apparentType(declaration);
if (Node7.isParameterDeclaration(declaration) || Node7.isPropertyDeclaration(declaration)) {
const modifiers = modifiersText(declaration);
const optional = declaration.hasQuestionToken() ? "?" : "";
const signature2 = `${modifiers} ${name}${optional}: ${type}`;
return formatSignature("class-property", signature2);
}
const staticKeyword = declaration.isStatic() ? "static" : "";
const readonlyKeyword = declaration.getSetAccessor() === void 0 ? "readonly" : "";
const signature = `${staticKeyword} ${readonlyKeyword} ${name}: ${type}`;
return await formatSignature("class-property", signature);
};
var extractClassMethods = async (classId, classDeclaration) => {
const methodsDeclarations = [
...classDeclaration.getInstanceMethods(),
...classDeclaration.getStaticMethods()
];
const methods = [];
const seenMethods = /* @__PURE__ */ new Set();
for (const declaration of methodsDeclarations) {
if (isHidden(declaration)) {
continue;
}
const name = declaration.getName();
if (seenMethods.has(name)) {
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");
};
var classMethodSignature = async (name, declaration) => {
const modifiers = modifiersText(declaration);
const type = typeCheckerType(declaration);
return await formatSignature("class-method", `${modifiers} ${name}: ${type}`);
};
// src/extract-enum.ts
import "ts-morph";
var extractEnum = async (containerName, exportName, declaration) => {
const enumId = id(containerName, "+enum", exportName);
return {
kind: "enum",
id: enumId,
name: exportName,
docs: docs(declaration),
file: sourceFilePath(declaration),
line: declaration.getStartLineNumber(),
signature: await enumSignature(declaration),
members: await extractEnumMembers(enumId, declaration)
};
};
var enumSignature = async (declaration) => {
const signature = headText(declaration);
return await formatSignature("enum", signature);
};
var extractEnumMembers = async (enumId, enumDeclaration) => {
const members = [];
for (const declaration of enumDeclaration.getMembers()) {
if (isHidden(declaration)) {
continue;
}
const name = declaration.getName();
members.push({
kind: "enum-member",
id: id(enumId, "+member", name),
name,
docs: docs(declaration),
file: sourceFilePath(declaration),
line: declaration.getStartLineNumber(),
signature: await enumMemberSignature(declaration)
});
}
return members;
};
var enumMemberSignature = async (declaration) => {
const signature = declaration.getText();
return await formatSignature("enum-member", signature);
};
// src/extract-expression.ts
var extractExpression = async (containerName, exportName, declaration) => ({
kind: "variable",
id: id(containerName, "+variable", exportName),
name: exportName,
docs: docs(declaration),
file: sourceFilePath(declaration),
line: declaration.getStartLineNumber(),
signature: await expressionSignature(exportName, declaration)
});
var expressionSignature = async (name, declaration) => {
const kind = "const";
const type = apparentType(declaration);
return await formatSignature("variable", `${kind} ${name}: ${type}`);
};
// src/extract-file-module.ts
import { SyntaxKind as SyntaxKind6 } from "ts-morph";
var extractFileModule = async (containerName, exportName, declaration, declarations) => ({
kind: "namespace",
id: id(containerName, "+namespace", exportName),
name: exportName,
docs: fileModuleDocs(declaration),
file: sourceFilePath(declaration),
line: declaration.getStartLineNumber(),
signature: await fileModuleSignature(declaration),
declarations
});
var fileModuleDocs = (declaration) => {
const firstDoc = declaration.getFirstDescendantByKind(SyntaxKind6.JSDoc)?.getText();
if (!firstDoc) {
return [];
}
return [firstDoc];
};
var fileModuleSignature = async (declaration) => {
const filename = declaration.getSourceFile().getBaseName();
return await formatSignature("namespace", `module "${filename}" {}`);
};
// src/extract-function.ts
var extractFunction = async (containerName, exportName, declaration) => ({
kind: "function",
id: id(containerName, "+function", exportName),
name: exportName,
docs: docs(declaration),
file: sourceFilePath(declaration),
line: declaration.getStartLineNumber(),
signature: await functionSignature(exportName, declaration)
});
var functionSignature = async (name, declaration) => {
const type = typeCheckerType(declaration);
return await formatSignature("function", `${name}: ${type}`);
};
// src/extract-function-expression.ts
var extractFunctionExpression = async (containerName, exportName, declaration) => ({
kind: "function",
id: id(containerName, "+function", exportName),
name: exportName,
docs: docs(declaration),
file: sourceFilePath(declaration),
line: declaration.getStartLineNumber(),
signature: await functionExpressionSignature(exportName, declaration)
});
var functionExpressionSignature = async (name, declaration) => {
const type = typeCheckerType(declaration);
return await formatSignature("function", `${name}: ${type}`);
};
// src/extract-interface.ts
import { orderBy as orderBy2 } from "natural-orderby";
import "ts-morph";
var extractInterface = async (containerName, exportName, declaration) => {
const interfaceId = id(containerName, "+interface", exportName);
return {
kind: "interface",
id: interfaceId,
name: exportName,
docs: docs(declaration),
file: sourceFilePath(declaration),
line: declaration.getStartLineNumber(),
signature: await interfaceSignature(declaration),
properties: await extractInterfaceProperties(interfaceId, declaration),
methods: await extractInterfaceMethods(interfaceId, declaration),
constructSignatures: await extractInterfaceConstructSignatures(interfaceId, declaration),
callSignatures: await extractInterfaceCallSignatures(interfaceId, declaration),
indexSignatures: await extractInterfaceIndexSignatures(interfaceId, declaration),
getAccessors: await extractInterfaceGetAccessors(interfaceId, declaration),
setAccessors: await extractInterfaceSetAccessors(interfaceId, declaration)
};
};
var interfaceSignature = async (declaration) => {
const signature = headText(declaration);
return await formatSignature("interface", signature);
};
var extractInterfaceProperties = async (interfaceId, interfaceDeclaration) => {
const properties = [];
for (const declaration of interfaceDeclaration.getProperties()) {
if (isHidden(declaration)) {
continue;
}
const name = declaration.getName();
properties.push({
kind: "interface-property",
id: id(interfaceId, "+property", name),
name,
docs: docs(declaration),
file: sourceFilePath(declaration),
line: declaration.getStartLineNumber(),
signature: await interfacePropertySignature(declaration)
});
}
return orderBy2(properties, "id");
};
var interfacePropertySignature = async (declaration) => {
const signature = declaration.getText();
return await formatSignature("interface-property", signature);
};
var extractInterfaceMethods = async (interfaceId, interfaceDeclaration) => {
const methods = [];
const seenMethods = /* @__PURE__ */ new Set();
for (const declaration of interfaceDeclaration.getMethods()) {
if (isHidden(declaration)) {
continue;
}
const name = declaration.getName();
if (seenMethods.has(name)) {
continue;
}
seenMethods.add(name);
methods.push({
kind: "interface-method",
id: id(interfaceId, "+method", name),
name,
docs: docs(declaration),
file: sourceFilePath(declaration),
line: declaration.getStartLineNumber(),
signature: await interfaceMethodSignature(name, declaration)
});
}
return orderBy2(methods, "id");
};
var interfaceMethodSignature = async (name, declaration) => {
const type = typeCheckerType(declaration);
return await formatSignature("interface-method", `${name}: ${type}`);
};
var extractInterfaceConstructSignatures = async (interfaceId, interfaceDeclaration) => {
const constructSignatures = [];
for (const [index, declaration] of interfaceDeclaration.getConstructSignatures().entries()) {
if (isHidden(declaration)) {
continue;
}
constructSignatures.push({
kind: "interface-construct-signature",
id: id(interfaceId, "construct-signature", index > 0 ? `${index}` : ""),
name: "construct-signature",
docs: docs(declaration),
file: sourceFilePath(declaration),
line: declaration.getStartLineNumber(),
signature: await interfaceConstructSignatureSignature(declaration)
});
}
return orderBy2(constructSignatures, "id");
};
var interfaceConstructSignatureSignature = async (declaration) => {
const signature = declaration.getText();
return await formatSignature("interface-construct-signature", signature);
};
var extractInterfaceCallSignatures = async (interfaceId, interfaceDeclaration) => {
const callSignatures = [];
for (const [index, declaration] of interfaceDeclaration.getCallSignatures().entries()) {
if (isHidden(declaration)) {
continue;
}
callSignatures.push({
kind: "interface-call-signature",
id: id(interfaceId, "call-signature", index > 0 ? `${index}` : ""),
name: "call-signature",
docs: docs(declaration),
file: sourceFilePath(declaration),
line: declaration.getStartLineNumber(),
signature: await interfaceCallSignatureSignature(declaration)
});
}
return orderBy2(callSignatures, "id");
};
var interfaceCallSignatureSignature = async (declaration) => {
const signature = declaration.getText();
return await formatSignature("interface-call-signature", signature);
};
var extractInterfaceIndexSignatures = async (interfaceId, interfaceDeclaration) => {
const indexSignatures = [];
for (const [index, declaration] of interfaceDeclaration.getIndexSignatures().entries()) {
if (isHidden(declaration)) {
continue;
}
indexSignatures.push({
kind: "interface-index-signature",
id: id(interfaceId, "index-signature", index > 0 ? `${index}` : ""),
name: "index-signature",
docs: docs(declaration),
file: sourceFilePath(declaration),
line: declaration.getStartLineNumber(),
signature: await interfaceIndexSignatureSignature(declaration)
});
}
return orderBy2(indexSignatures, "id");
};
var interfaceIndexSignatureSignature = async (declaration) => {
const signature = declaration.getText();
return await formatSignature("interface-index-signature", signature);
};
var extractInterfaceGetAccessors = async (interfaceId, interfaceDeclaration) => {
const getAccessors = [];
for (const declaration of interfaceDeclaration.getGetAccessors()) {
if (isHidden(declaration)) {
continue;
}
const name = declaration.getName();
getAccessors.push({
kind: "interface-get-accessor",
id: id(interfaceId, "+get-accessor", name),
name,
docs: docs(declaration),
file: sourceFilePath(declaration),
line: declaration.getStartLineNumber(),
signature: await interfaceGetAccessorSignature(declaration)
});
}
return orderBy2(getAccessors, "id");
};
var interfaceGetAccessorSignature = async (declaration) => {
const signature = declaration.getText();
return await formatSignature("interface-get-accessor", signature);
};
var extractInterfaceSetAccessors = async (interfaceId, interfaceDeclaration) => {
const setAccessors = [];
for (const declaration of interfaceDeclaration.getSetAccessors()) {
if (isHidden(declaration)) {
continue;
}
const name = declaration.getName();
setAccessors.push({
kind: "interface-set-accessor",
id: id(interfaceId, "+set-accessor", name),
name,
docs: docs(declaration),
file: sourceFilePath(declaration),
line: declaration.getStartLineNumber(),
signature: await interfaceSetAccessorSignature(declaration)
});
}
return orderBy2(setAccessors, "id");
};
var interfaceSetAccessorSignature = async (declaration) => {
const signature = declaration.getText();
return await formatSignature("interface-get-accessor", signature);
};
// src/extract-namespace.ts
import "ts-morph";
var extractNamespace = async (containerName, exportName, declaration, declarations) => ({
kind: "namespace",
id: id(containerName, "+namespace", exportName),
name: exportName,
docs: docs(declaration),
file: sourceFilePath(declaration),
line: declaration.getStartLineNumber(),
signature: await namespaceSignature(exportName),
declarations
});
var namespaceSignature = async (exportName) => {
const containerKeyword = exportName.startsWith('"') || exportName.startsWith("'") ? "module" : "namespace";
const signature = `${containerKeyword} ${exportName} {}`;
return await formatSignature("namespace", signature);
};
// src/extract-type-alias.ts
var extractTypeAlias = async (containerName, exportName, declaration) => ({
kind: "type",
id: id(containerName, "+type", exportName),
name: exportName,
docs: docs(declaration),
file: sourceFilePath(declaration),
line: declaration.getStartLineNumber(),
signature: await typeAliasSignature(declaration)
});
var typeAliasSignature = async (declaration) => {
const signature = declaration.getText();
return await formatSignature("type", signature);
};
// src/extract-variable.ts
var extractVariable = async (containerName, exportName, declaration) => ({
kind: "variable",
id: id(containerName, "+variable", exportName),
name: exportName,
docs: docs(declaration),
file: sourceFilePath(declaration),
line: declaration.getStartLineNumber(),
signature: await variableSignature(exportName, declaration)
});
var variableSignature = async (name, declaration) => {
const kind = declaration.getVariableStatementOrThrow().getDeclarationKind().toString();
const type = apparentType(declaration);
return await formatSignature("variable", `${kind} ${name}: ${type}`);
};
// src/extract-variable-assignment-expression.ts
var extractVariableAssignmentExpression = async (containerName, exportName, declaration) => {
const variableDeclaration = declaration.getLeft().getSymbol().getDeclarations()[0];
return {
kind: "variable",
id: id(containerName, "+variable", exportName),
name: exportName,
docs: docs(variableDeclaration),
file: sourceFilePath(variableDeclaration),
line: variableDeclaration.getStartLineNumber(),
signature: await variableAssignmentExpressionSignature(
exportName,
declaration,
variableDeclaration
)
};
};
var variableAssignmentExpressionSignature = async (name, declaration, variableDeclaration) => {
const kind = variableDeclaration.getVariableStatementOrThrow().getDeclarationKind().toString();
const variableType = apparentType(variableDeclaration);
const expressionType = apparentType(declaration);
const type = variableType !== "any" ? variableType : expressionType;
return await formatSignature("variable", `${kind} ${name}: ${type}`);
};
// src/global-ambient-declarations.ts
import "ts-morph";
// src/is-global.ts
import {
Node as Node8
} from "ts-morph";
var isGlobal = (node) => {
const isGlobalVariable = Node8.isVariableDeclaration(node) && node.getVariableStatementOrThrow().isAmbient() && !node.isExported();
const isGlobalFunction = Node8.isFunctionDeclaration(node) && node.isAmbient() && node.getName() !== void 0 && !node.isExported();
const isGlobalNamespace = Node8.isModuleDeclaration(node) && node.isAmbient() && !node.isExported() && !node.hasModuleKeyword();
return isGlobalVariable || isGlobalFunction || isGlobalNamespace;
};
// src/global-ambient-declarations.ts
var globalAmbientDeclarations = (containerName, container) => {
const globalCandidates = [
...container.getVariableDeclarations(),
...container.getFunctions(),
...container.getModules()
];
const globalAmbientDeclarations2 = [];
for (const declaration of globalCandidates) {
if (isHidden(declaration) || !isGlobal(declaration)) {
continue;
}
globalAmbientDeclarations2.push({
containerName,
// Global ambient functions must have a name.
exportName: declaration.getName(),
declaration
});
}
return globalAmbientDeclarations2;
};
// src/is-class.ts
import { Node as Node9 } from "ts-morph";
var isClass = (node) => Node9.isClassDeclaration(node);
// src/is-enum.ts
import { Node as Node10 } from "ts-morph";
var isEnum = (node) => Node10.isEnumDeclaration(node);
// src/is-expression.ts
import { Node as Node11 } from "ts-morph";
var isExpression = (node) => Node11.isExpression(node) && !Node11.isArrowFunction(node);
// src/is-file-module.ts
import { Node as Node12 } from "ts-morph";
var isFileModule = (node) => Node12.isSourceFile(node);
// src/is-function.ts
import { Node as Node13 } from "ts-morph";
var isFunction = (node) => Node13.isFunctionDeclaration(node) || Node13.isArrowFunction(node);
// src/is-function-expression.ts
import { Node as Node14, SyntaxKind as SyntaxKind7 } from "ts-morph";
var isFunctionExpression = (node) => Node14.isVariableDeclaration(node) && hasFunctionLikeType(node);
var hasFunctionLikeType = (declaration) => {
if (declaration.getTypeNode()?.getKind() === SyntaxKind7.FunctionType) {
return true;
}
const initializer = declaration.getInitializer();
if (!initializer) {
return false;
}
return Node14.isArrowFunction(initializer) || Node14.isFunctionExpression(initializer);
};
// src/is-interface.ts
import { Node as Node15 } from "ts-morph";
var isInterface = (node) => Node15.isInterfaceDeclaration(node);
// src/is-type-alias.ts
import { Node as Node16 } from "ts-morph";
var isTypeAlias = (node) => Node16.isTypeAliasDeclaration(node);
// src/is-variable.ts
import { Node as Node17 } from "ts-morph";
var isVariable = (node) => Node17.isVariableDeclaration(node) && !isFunctionExpression(node);
// src/is-variable-assignment-expression.ts
import { Node as Node18 } from "ts-morph";
var isVariableAssignmentExpression = (node) => Node18.isBinaryExpression(node) && Node18.isIdentifier(node.getLeft());
// src/extract-declarations.ts
var extractDeclarations = async ({
containerName,
container,
maxDepth,
project,
pkgName
}) => {
const foundDeclarations = [
...exportedDeclarations(containerName, container),
...exportEqualsDeclarations(containerName, container),
...project ? ambientModulesDeclarations(containerName, project, pkgName) : [],
...Node19.isSourceFile(container) ? globalAmbientDeclarations(containerName, container) : []
];
const seenFunctions = /* @__PURE__ */ new Set();
const seenNamespaces = /* @__PURE__ */ new Set();
const extractedDeclarations = [];
for (const { containerName: containerName2, exportName, declaration } of foundDeclarations) {
const extractedDeclaration = await extractDeclaration({
containerName: containerName2,
exportName,
declaration,
maxDepth,
seenFunctions,
seenNamespaces
});
if (!extractedDeclaration) {
continue;
}
extractedDeclarations.push(extractedDeclaration);
}
return orderBy3(extractedDeclarations, "id");
};
var extractDeclaration = async ({
containerName,
exportName,
declaration,
maxDepth,
seenFunctions,
seenNamespaces
}) => {
if (isVariable(declaration)) {
return await extractVariable(containerName, exportName, declaration);
}
if (isVariableAssignmentExpression(declaration)) {
return await extractVariableAssignmentExpression(containerName, exportName, declaration);
}
if (isExpression(declaration)) {
return await extractExpression(containerName, exportName, declaration);
}
if (isFunction(declaration)) {
if (seenFunctions.has(exportName)) {
return void 0;
}
seenFunctions.add(exportName);
return await extractFunction(containerName, exportName, declaration);
}
if (isFunctionExpression(declaration)) {
return await extractFunctionExpression(containerName, exportName, declaration);
}
if (isClass(declaration)) {
return await extractClass(containerName, exportName, declaration);
}
if (isInterface(declaration)) {
return await extractInterface(containerName, exportName, declaration);
}
if (isEnum(declaration)) {
return await extractEnum(containerName, exportName, declaration);
}
if (isTypeAlias(declaration)) {
return await extractTypeAlias(containerName, exportName, declaration);
}
if (isNamespace(declaration) && maxDepth > 0) {
if (seenNamespaces.has(exportName)) {
return void 0;
}
seenNamespaces.add(exportName);
const innerDeclarations = await extractDeclarations({
containerName: id(containerName, "+namespace", exportName),
container: declaration,
maxDepth: maxDepth - 1
});
return await extractNamespace(containerName, exportName, declaration, innerDeclarations);
}
if (isFileModule(declaration) && maxDepth > 0) {
const innerDeclarations = await extractDeclarations({
containerName: id(containerName, "+namespace", exportName),
container: declaration,
maxDepth: maxDepth - 1
});
return await extractFileModule(containerName, exportName, declaration, innerDeclarations);
}
return void 0;
};
// src/extract-package-api.ts
import { Effect as Effect9 } from "effect";
// src/extract-package-api-effect.ts
import { Effect as Effect8 } from "effect";
import { performance } from "perf_hooks";
import { join } from "pathe";
// src/package-declarations.ts
import { Data as Data3, Effect as Effect4 } from "effect";
var PackageDeclarationsError = class extends Data3.TaggedError("PackageDeclarationsError") {
};
var packageDeclarations = ({
pkgName,
project,
indexFile,
maxDepth
}) => Effect4.tryPromise({
try: () => extractDeclarations({
containerName: "",
container: indexFile,
maxDepth,
project,
pkgName
}),
catch: (e) => new PackageDeclarationsError({ cause: e })
});
// src/package-json.ts
import { Data as Data4, Effect as Effect5 } from "effect";
import { readPackage } from "read-pkg";
var PackageJsonError = class extends Data4.TaggedError("PackageJsonError") {
};
var packageJson = (pkgDir) => Effect5.tryPromise({
try: () => readPackage({ cwd: pkgDir }),
catch: (e) => new PackageJsonError({ cause: e })
});
// src/package-overview.ts
import { SyntaxKind as SyntaxKind8 } from "ts-morph";
var packageOverview = (indexFile) => {
return indexFile.getDescendantsOfKind(SyntaxKind8.JSDocTag).find((tag) => tag.getTagName() === "packageDocumentation")?.getParentIfKind(SyntaxKind8.JSDoc)?.getText();
};
// src/package-types.ts
import { Data as Data5, Effect as Effect6 } from "effect";
import { exports } from "resolve.exports";
var PackageTypesError = class extends Data5.TaggedError("PackageTypesError") {
};
var packageTypes = (pkgJson, subpath) => Effect6.gen(function* () {
const firstPath = yield* resolveExports(pkgJson, subpath);
if (firstPath && isTypesFile(firstPath)) {
return firstPath;
}
const isRootSubpath = [".", pkgJson.name].includes(subpath);
if (isRootSubpath && pkgJson.types && isTypesFile(pkgJson.types)) {
return pkgJson.types;
}
if (isRootSubpath && pkgJson.typings && isTypesFile(pkgJson.typings)) {
return pkgJson.typings;
}
return yield* new PackageTypesError();
});
var resolveExports = (pkgJson, subpath) => {
try {
const resolvedPaths = exports(pkgJson, subpath, {
conditions: ["types", "import", "node"],
unsafe: true
}) ?? [];
return Effect6.succeed(resolvedPaths[0]);
} catch {
return Effect6.succeed(void 0);
}
};
var isTypesFile = (filepath) => [".d.ts", ".d.mts", ".d.cts"].some((ext) => filepath.endsWith(ext));
// src/work-dir.ts
import { Data as Data6, Effect as Effect7 } from "effect";
import { rm } from "fs/promises";
import { temporaryDirectory } from "tempy";
var WorkDirError = class extends Data6.TaggedError("WorkDirError") {
};
var acquire = Effect7.try({
try: () => {
const path = temporaryDirectory();
return {
path,
close: async () => {
try {
await rm(path, { force: true, recursive: true, maxRetries: 3 });
} catch {
}
}
};
},
catch: (e) => new WorkDirError({ cause: e })
});
var release = (workDir2) => Effect7.promise(() => workDir2.close());
var workDir = Effect7.acquireRelease(acquire, release);
// src/extract-package-api-effect.ts
var extractPackageApiEffect = ({
pkg,
subpath = ".",
maxDepth = 5
}) => Effect8.gen(function* () {
const startTime = performance.now();
const { path: cwd } = yield* workDir;
const pm = yield* PackageManager;
const packages = yield* pm.installPackage({ pkg, cwd });
const workDirPkgJson = yield* packageJson(cwd);
const pkgName = Object.keys(workDirPkgJson.dependencies)[0];
const pkgDir = join(cwd, "node_modules", pkgName);
const pkgJson = yield* packageJson(pkgDir);
const types = yield* packageTypes(pkgJson, subpath);
const indexFilePath = join(pkgDir, types);
const { project, indexFile } = yield* createProject({ indexFilePath, cwd });
const overview = packageOverview(indexFile);
const declarations = yield* packageDeclarations({ pkgName, project, indexFile, maxDepth });
const pkgApi = {
name: pkgJson.name,
version: pkgJson.version,
subpath,
types,
overview,
declarations,
packages,
analyzedAt: (/* @__PURE__ */ new Date()).toISOString(),
analyzedIn: Math.round(performance.now() - startTime)
};
return pkgApi;
});
// src/extract-package-api.ts
var extractPackageApi = async ({
pkg,
subpath = ".",
maxDepth = 5,
bunPath = "bun"
}) => {
return await extractPackageApiEffect({ pkg, subpath, maxDepth }).pipe(
Effect9.scoped,
Effect9.provideService(PackageManager, bunPackageManager(bunPath)),
Effect9.runPromise
);
};
// src/package-name.ts
import { Data as Data7, Effect as Effect10 } from "effect";
import validate from "validate-npm-package-name";
var PackageNameError = class extends Data7.TaggedError("PackageNameError") {
};
var packageName = (pkg) => Effect10.gen(function* () {
const versionMarker = pkg.lastIndexOf("@");
const pkgName = pkg.slice(0, versionMarker > 0 ? versionMarker : void 0);
const { validForNewPackages, warnings, errors } = validate(pkgName);
if (!validForNewPackages) {
return yield* new PackageNameError({ warnings, errors });
}
return pkgName;
});
export {
InstallPackageError,
PackageDeclarationsError,
PackageJsonError,
PackageManager,
PackageNameError,
PackageTypesError,
ProjectError,
WorkDirError,
bunPackageManager,
extractDeclarations,
extractPackageApi,
extractPackageApiEffect,
packageJson,
packageName,
packageTypes,
parseDocComment,
workDir
};