@jsdocs-io/extractor
Version:
Analyze and extract the API from npm packages
2,099 lines (1,907 loc) • 60 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var perf_hooks = require('perf_hooks');
var queryRegistry = require('query-registry');
var debug = require('debug');
var tsm = require('ts-morph');
var prettier = require('prettier');
var tsdoc = require('@microsoft/tsdoc');
var HostedGitInfo = require('hosted-git-info');
var path = require('path');
var got = require('got');
var gunzipMaybe = require('gunzip-maybe');
var stream = require('stream');
var util = require('util');
var concat = require('concat-stream');
var tar = require('tar-stream');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n["default"] = e;
return n;
}
var debug__default = /*#__PURE__*/_interopDefaultLegacy(debug);
var tsm__namespace = /*#__PURE__*/_interopNamespace(tsm);
var prettier__namespace = /*#__PURE__*/_interopNamespace(prettier);
var tsdoc__namespace = /*#__PURE__*/_interopNamespace(tsdoc);
var HostedGitInfo__default = /*#__PURE__*/_interopDefaultLegacy(HostedGitInfo);
var path__namespace = /*#__PURE__*/_interopNamespace(path);
var got__default = /*#__PURE__*/_interopDefaultLegacy(got);
var gunzipMaybe__default = /*#__PURE__*/_interopDefaultLegacy(gunzipMaybe);
var stream__default = /*#__PURE__*/_interopDefaultLegacy(stream);
var concat__default = /*#__PURE__*/_interopDefaultLegacy(concat);
var tar__default = /*#__PURE__*/_interopDefaultLegacy(tar);
const log = /*#__PURE__*/debug__default["default"]('@jsdocs-io/extractor');
function getOverview({
indexFile
}) {
var _indexFile$getDescend, _indexFile$getDescend2;
return (_indexFile$getDescend = indexFile.getDescendantsOfKind(tsm__namespace.SyntaxKind.JSDocTag).find(tag => tag.getTagName() === 'packageDocumentation')) == null ? void 0 : (_indexFile$getDescend2 = _indexFile$getDescend.getParentIfKind(tsm__namespace.SyntaxKind.JSDocComment)) == null ? void 0 : _indexFile$getDescend2.getText();
}
/**
* `DeclarationKinds` lists the possible kinds of declarations
*/
exports.DeclarationKinds = void 0;
(function (DeclarationKinds) {
DeclarationKinds["VariableDeclaration"] = "VariableDeclaration";
DeclarationKinds["FunctionDeclaration"] = "FunctionDeclaration";
DeclarationKinds["ClassDeclaration"] = "ClassDeclaration";
DeclarationKinds["ClassConstructorDeclaration"] = "ClassConstructorDeclaration";
DeclarationKinds["ClassPropertyDeclaration"] = "ClassPropertyDeclaration";
DeclarationKinds["ClassMethodDeclaration"] = "ClassMethodDeclaration";
DeclarationKinds["InterfaceDeclaration"] = "InterfaceDeclaration";
DeclarationKinds["InterfacePropertyDeclaration"] = "InterfacePropertyDeclaration";
DeclarationKinds["InterfaceMethodDeclaration"] = "InterfaceMethodDeclaration";
DeclarationKinds["InterfaceConstructSignatureDeclaration"] = "InterfaceConstructSignatureDeclaration";
DeclarationKinds["InterfaceCallSignatureDeclaration"] = "InterfaceCallSignatureDeclaration";
DeclarationKinds["InterfaceIndexSignatureDeclaration"] = "InterfaceIndexSignatureDeclaration";
DeclarationKinds["EnumDeclaration"] = "EnumDeclaration";
DeclarationKinds["EnumMemberDeclaration"] = "EnumMemberDeclaration";
DeclarationKinds["TypeAliasDeclaration"] = "TypeAliasDeclaration";
DeclarationKinds["NamespaceDeclaration"] = "NamespaceDeclaration";
})(exports.DeclarationKinds || (exports.DeclarationKinds = {}));
function isVariableDeclaration(declaration) {
return declaration.kind === exports.DeclarationKinds.VariableDeclaration;
}
function isFunctionDeclaration(declaration) {
return declaration.kind === exports.DeclarationKinds.FunctionDeclaration;
}
function isClassDeclaration(declaration) {
return declaration.kind === exports.DeclarationKinds.ClassDeclaration;
}
function isClassConstructorDeclaration(declaration) {
return declaration.kind === exports.DeclarationKinds.ClassConstructorDeclaration;
}
function isClassPropertyDeclaration(declaration) {
return declaration.kind === exports.DeclarationKinds.ClassPropertyDeclaration;
}
function isClassMethodDeclaration(declaration) {
return declaration.kind === exports.DeclarationKinds.ClassMethodDeclaration;
}
function isInterfaceDeclaration(declaration) {
return declaration.kind === exports.DeclarationKinds.InterfaceDeclaration;
}
function isInterfacePropertyDeclaration(declaration) {
return declaration.kind === exports.DeclarationKinds.InterfacePropertyDeclaration;
}
function isInterfaceMethodDeclaration(declaration) {
return declaration.kind === exports.DeclarationKinds.InterfaceMethodDeclaration;
}
function isInterfaceConstructSignatureDeclaration(declaration) {
return declaration.kind === exports.DeclarationKinds.InterfaceConstructSignatureDeclaration;
}
function isInterfaceCallSignatureDeclaration(declaration) {
return declaration.kind === exports.DeclarationKinds.InterfaceCallSignatureDeclaration;
}
function isInterfaceIndexSignatureDeclaration(declaration) {
return declaration.kind === exports.DeclarationKinds.InterfaceIndexSignatureDeclaration;
}
function isEnumDeclaration(declaration) {
return declaration.kind === exports.DeclarationKinds.EnumDeclaration;
}
function isEnumMemberDeclaration(declaration) {
return declaration.kind === exports.DeclarationKinds.EnumMemberDeclaration;
}
function isTypeAliasDeclaration(declaration) {
return declaration.kind === exports.DeclarationKinds.TypeAliasDeclaration;
}
function isNamespaceDeclaration(declaration) {
return declaration.kind === exports.DeclarationKinds.NamespaceDeclaration;
}
const formatOptions = {
semi: true,
singleQuote: true,
trailingComma: 'es5',
tabWidth: 4,
printWidth: 85,
endOfLine: 'lf',
arrowParens: 'always',
parser: 'typescript'
};
function formatFunctionSignature(text) {
const varLike = `let ${text}`;
return formatVariableSignature(varLike).replace(/^let\s/, '');
}
function formatVariableSignature(text) {
// Temporarily replace the invalid variable name `default`
const escapedText = text.replace('default:', '_______:');
return formatText(escapedText).replace('_______:', 'default:');
}
function formatClassMember(text) {
const wrapped = `class C { ${text} }`;
return formatWrappedMember(wrapped);
}
function formatInterfaceMember(text) {
const wrapped = `interface I { ${text} }`;
return formatWrappedMember(wrapped);
}
function formatEnumMember(text) {
const wrapped = `enum E { ${text} }`;
return formatWrappedMember(wrapped).replace(/,$/, '');
}
function formatWrappedMember(text) {
const formatted = formatText(text);
const lines = formatted.split('\n'); // Remove wrapper and member indentation
const member = lines.slice(1, lines.length - 1).map(line => line.replace(/^\s{4}/, '')).join('\n');
return member;
}
function formatText(text) {
let formatted = text.trim().replace(/^export\s+/, '').replace(/^default\s+/, '').replace(/^declare\s+/, '');
try {
formatted = prettier__namespace.format(formatted, formatOptions);
} catch (err) {
// istanbul ignore next
log('formatText: formatting error: %O', {
err
});
}
return formatted.trim();
}
function getApparentType({
declaration
}) {
// See https://github.com/dsherret/ts-morph/issues/453#issuecomment-427405736
// and https://github.com/dsherret/ts-morph/issues/453#issuecomment-667578386
return declaration.getType().getApparentType().getText(declaration, tsm__namespace.ts.TypeFormatFlags.NoTruncation | tsm__namespace.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope).replace(/^Number$/, 'number').replace(/^Boolean$/, 'boolean').replace(/^String$/, 'string');
}
function getJSDocs({
declaration
}) {
const declarations = getDeclarationsWithDocs({
declaration
});
const allDocs = declarations.flatMap(declaration => {
const doc = getLastJSDoc({
declaration
});
return doc ? doc : [];
});
return Array.from(new Set(allDocs));
}
function getDeclarationsWithDocs({
declaration
}) {
if (tsm__namespace.Node.isVariableDeclaration(declaration)) {
return [declaration.getVariableStatementOrThrow()];
}
if (tsm__namespace.Node.isExpression(declaration)) {
return [declaration.getParent()];
} // Make functions and class methods share the same docs,
// that is one declaration with multiple (overload) docs,
// since they have their signature built from the typechecker.
// Exclude constructors since their signatures are built manually and
// thus each constructor needs its own doc.
if (tsm__namespace.Node.isOverloadable(declaration) && !tsm__namespace.Node.isConstructorDeclaration(declaration)) {
const overloads = declaration.getOverloads();
const implementation = declaration.getImplementation();
return [...overloads, ...(implementation ? [implementation] : [])];
} // Make interface methods share the same docs as for overloadable nodes
if (tsm__namespace.Node.isMethodSignature(declaration) && declaration.getParent().getKind() === tsm__namespace.SyntaxKind.InterfaceDeclaration) {
const methodName = declaration.getName();
const overloads = declaration.getParentIfKindOrThrow(tsm__namespace.SyntaxKind.InterfaceDeclaration).getMethods().filter(method => method.getName() === methodName);
return overloads;
}
return [declaration];
}
function getLastJSDoc({
declaration
}) {
var _declaration$getLastC;
// Get the doc closest to the declaration signature
const doc = (_declaration$getLastC = declaration.getLastChildByKind(tsm__namespace.SyntaxKind.JSDocComment)) == null ? void 0 : _declaration$getLastC.getText();
if (!doc) {
return undefined;
} // The first declaration after package documentation
// should not inherit that jsdoc if it has none.
// See `export-named-declaration-without-jsdoc.test.ts`.
const isPackageDocumentation = new tsdoc__namespace.TSDocParser().parseString(doc).docComment.modifierTagSet.isPackageDocumentation();
if (isPackageDocumentation) {
return undefined;
}
return doc;
}
function getModifiersText({
declaration
}) {
return declaration.getModifiers().flatMap(modifier => {
// Ignore `public` modifier
if (modifier.getKind() === tsm__namespace.SyntaxKind.PublicKeyword) {
return [];
}
return modifier.getText();
}).join(' ');
}
function getWrapperSignature({
declaration
}) {
const parts = [];
for (const child of declaration.getChildren()) {
// Ignore documentation comments
if (isJSDocComment(child)) {
continue;
} // Stop at body start (e.g., the first opening bracket of a class).
if (isOpenBraceToken(child)) {
break;
}
parts.push(child.getText());
}
parts.push('{}');
const signature = parts.join(' ');
return formatText(signature);
}
function isJSDocComment(node) {
return node.getKind() === tsm__namespace.SyntaxKind.JSDocComment;
}
function isOpenBraceToken(node) {
return node.getKind() === tsm__namespace.SyntaxKind.OpenBraceToken;
}
function isInternalDeclaration({
declaration,
name = ''
}) {
return name.startsWith('_') || name.startsWith('#') || hasPrivateModifier({
declaration
}) || hasInternalTagDoc({
declaration
});
}
function hasPrivateModifier({
declaration
}) {
return tsm__namespace.Node.isModifierable(declaration) && declaration.hasModifier(tsm__namespace.SyntaxKind.PrivateKeyword);
}
function hasInternalTagDoc({
declaration
}) {
const firstDoc = getJSDocs({
declaration
})[0];
if (!firstDoc) {
return false;
}
const parser = new tsdoc__namespace.TSDocParser();
return parser.parseString(firstDoc).docComment.modifierTagSet.isInternal();
}
function sortByID(a, b) {
return a.id.localeCompare(b.id);
}
function toID(...elements) {
return elements.filter(Boolean).join('.');
}
function isClass(declaration) {
return tsm__namespace.Node.isClassDeclaration(declaration);
}
function newClass({
id,
name,
declaration,
getSource,
getType
}) {
const kind = exports.DeclarationKinds.ClassDeclaration;
const docs = getJSDocs({
declaration
});
const source = getSource({
declaration
});
const isAbstract = declaration.isAbstract();
const signature = getWrapperSignature({
declaration
});
const constructors = getClassConstructors({
classID: id,
classDeclaration: declaration,
getSource
});
const members = getClassMembers({
classID: id,
classDeclaration: declaration,
getSource,
getType
});
return {
kind,
id,
name,
docs,
source,
signature,
isAbstract,
constructors,
members
};
}
function getClassConstructors({
classID,
classDeclaration,
getSource
}) {
// `getConstructors()` returns all constructors for ambient modules
// but only the implementation constructor in normal modules
const declaration = classDeclaration.getConstructors()[0];
if (!declaration) {
return [];
} // Manually retrieve all constructors from the first constructor
const overloads = declaration.getOverloads();
const implementation = declaration.getImplementation();
const constructors = [...overloads, ...(implementation ? [implementation] : [])];
return constructors.flatMap((declaration, index) => {
if (isInternalDeclaration({
declaration
})) {
return [];
}
const kind = exports.DeclarationKinds.ClassConstructorDeclaration;
const name = 'constructor';
const id = toID(classID, `${index}-${name}`);
const docs = getJSDocs({
declaration
});
const source = getSource({
declaration
});
const signature = getClassConstructorSignature({
declaration
});
return {
kind,
id,
name,
docs,
source,
signature
};
});
}
function getClassConstructorSignature({
declaration
}) {
const modifiers = declaration.getModifiers().map(modifier => modifier.getText()).join(' ');
const params = declaration.getParameters().map(param => {
const name = param.getName();
const type = getApparentType({
declaration: 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 formatClassMember(signature);
}
function getClassMembers({
classID,
classDeclaration,
getSource,
getType
}) {
const seenMethods = new Set();
const members = [...classDeclaration.getStaticMembers(), ...classDeclaration.getInstanceMembers()].flatMap(declaration => {
const name = declaration.getName();
const id = toID(classID, name);
if (isInternalDeclaration({
declaration,
name
})) {
return [];
}
if (tsm__namespace.Node.isPropertyDeclaration(declaration) || tsm__namespace.Node.isParameterDeclaration(declaration)) {
return newProperty$1({
id,
name,
declaration,
getSource
});
}
if (tsm__namespace.Node.isGetAccessorDeclaration(declaration)) {
return newGetAccessor({
id,
name,
declaration,
getSource
});
}
if (tsm__namespace.Node.isMethodDeclaration(declaration)) {
// Skip overloaded methods
if (seenMethods.has(id)) {
return [];
}
seenMethods.add(id);
return newMethod$1({
id,
name,
declaration,
getSource,
getType
});
}
return [];
}).sort(sortByID);
return {
properties: members.filter(isClassPropertyDeclaration),
methods: members.filter(isClassMethodDeclaration)
};
}
function newProperty$1({
id,
name,
declaration,
getSource
}) {
const kind = exports.DeclarationKinds.ClassPropertyDeclaration;
const docs = getJSDocs({
declaration
});
const source = getSource({
declaration
});
const modifiersText = getModifiersText({
declaration
});
const isStatic = tsm__namespace.Node.isPropertyDeclaration(declaration) && declaration.isStatic();
const isOptional = declaration.hasQuestionToken();
const optionalText = isOptional ? '?' : '';
const type = getApparentType({
declaration
});
const signature = formatClassMember(`${modifiersText} ${name} ${optionalText}: ${type}`);
return {
kind,
id,
name,
docs,
source,
signature,
isStatic,
type
};
}
function newGetAccessor({
id,
name,
declaration,
getSource
}) {
const kind = exports.DeclarationKinds.ClassPropertyDeclaration;
const docs = getJSDocs({
declaration
});
const source = getSource({
declaration
});
const isStatic = declaration.isStatic();
const isReadonly = declaration.getSetAccessor() === undefined;
const type = getApparentType({
declaration: declaration
});
const staticText = isStatic ? 'static' : '';
const readonlyText = isReadonly ? 'readonly' : '';
const signature = formatClassMember(`${staticText} ${readonlyText} ${name}: ${type}`);
return {
kind,
id,
name,
docs,
source,
signature,
isStatic,
type
};
}
function newMethod$1({
id,
name,
declaration,
getSource,
getType
}) {
const kind = exports.DeclarationKinds.ClassMethodDeclaration;
const docs = getJSDocs({
declaration
});
const source = getSource({
declaration
});
const isStatic = declaration.isStatic();
const modifiersText = getModifiersText({
declaration
});
const type = getType({
declaration
});
const signature = formatClassMember(`${modifiersText} ${name}: ${type}`);
return {
kind,
id,
name,
docs,
source,
signature,
isStatic,
type
};
}
function isEnum(declaration) {
return tsm__namespace.Node.isEnumDeclaration(declaration);
}
function newEnum({
id,
name,
declaration,
getSource
}) {
const kind = exports.DeclarationKinds.EnumDeclaration;
const docs = getJSDocs({
declaration
});
const source = getSource({
declaration
});
const isConst = declaration.isConstEnum(); // Keep members in original order for signature text and sort them later
const members = getEnumMembers({
enumID: id,
enumDeclaration: declaration,
getSource
});
const signature = getEnumSignature({
isConst,
name,
members
});
members.sort(sortByID);
return {
kind,
id,
name,
docs,
source,
signature,
isConst,
members
};
}
function getEnumMembers({
enumID,
enumDeclaration,
getSource
}) {
return enumDeclaration.getMembers().flatMap(declaration => {
const name = declaration.getName();
if (isInternalDeclaration({
declaration,
name
})) {
return [];
}
const kind = exports.DeclarationKinds.EnumMemberDeclaration;
const id = toID(enumID, name);
const docs = getJSDocs({
declaration
});
const source = getSource({
declaration
});
const signature = formatEnumMember(declaration.getText());
const value = declaration.getValue();
return {
kind,
id,
name,
docs,
source,
signature,
value
};
});
}
function getEnumSignature({
isConst,
name,
members
}) {
const kind = isConst ? 'const enum' : 'enum';
const membersText = members.map(({
signature
}) => signature).join();
const signature = `${kind} ${name} { ${membersText} }`;
return formatText(signature);
}
function hasFunctionLikeType(node) {
var _node$getTypeNode;
const typeKind = (_node$getTypeNode = node.getTypeNode()) == null ? void 0 : _node$getTypeNode.getKind();
const hasFunctionType = typeKind === tsm__namespace.SyntaxKind.FunctionType;
if (hasFunctionType) {
return true;
}
const initializer = node.getInitializer();
if (!initializer) {
return false;
}
const hasFunctionInitializer = tsm__namespace.Node.isArrowFunction(initializer) || tsm__namespace.Node.isFunctionExpression(initializer);
return hasFunctionInitializer;
}
function hasVarLikeType(node) {
return !hasFunctionLikeType(node);
}
function isVariable(declaration) {
return tsm__namespace.Node.isVariableDeclaration(declaration) && hasVarLikeType(declaration);
}
function newVariable({
id,
name,
declaration,
getSource,
suggestedType
}) {
const kind = exports.DeclarationKinds.VariableDeclaration;
const docs = getJSDocs({
declaration
});
const source = getSource({
declaration
});
const variableKind = getVariableKind({
declaration
});
const type = getVariableType({
declaration,
suggestedType
});
const signature = getVariableSignature({
variableKind,
name,
type
});
return {
kind,
id,
name,
docs,
source,
signature,
variableKind,
type
};
}
function getVariableKind({
declaration
}) {
return declaration.getVariableStatementOrThrow().getDeclarationKind().toString();
}
function getVariableType({
declaration,
suggestedType = 'any'
}) {
const apparentType = getApparentType({
declaration
});
return apparentType !== 'any' ? apparentType : suggestedType;
}
function getVariableSignature({
variableKind,
name,
type
}) {
const signature = `${variableKind} ${name}: ${type}`;
return formatVariableSignature(signature);
}
function isVariableAssignmentExpression(declaration) {
return tsm__namespace.Node.isBinaryExpression(declaration) && tsm__namespace.Node.isIdentifier(declaration.getLeft());
}
function newVariableAssignmentExpression({
id,
name,
declaration,
getSource
}) {
const variableDeclaration = getVariableDeclaration({
declaration
});
const suggestedType = getApparentType({
declaration: declaration.getRight()
});
return newVariable({
id,
name,
declaration: variableDeclaration,
getSource,
suggestedType
});
}
function getVariableDeclaration({
declaration
}) {
return declaration.getLeft().getSymbol().getDeclarations()[0];
}
function isExpression(declaration) {
return tsm__namespace.Node.isExpression(declaration);
}
function newExpression({
id,
name,
declaration,
getSource
}) {
const kind = exports.DeclarationKinds.VariableDeclaration;
const docs = getJSDocs({
declaration
});
const source = getSource({
declaration
});
const variableKind = 'const';
const type = getApparentType({
declaration
});
const signature = getVariableSignature({
variableKind,
name,
type
});
return {
kind,
id,
name,
docs,
source,
signature,
variableKind,
type
};
}
function getFilename({
declaration
}) {
// Remove leading `/` from filepath
return declaration.getSourceFile().getFilePath().replace(/^\//, '');
}
function isFileModule(declaration) {
return tsm__namespace.Node.isSourceFile(declaration);
}
function newFileModule({
id,
name,
declaration,
declarations,
getSource
}) {
const kind = exports.DeclarationKinds.NamespaceDeclaration;
const docs = getFileModuleDocs({
declaration
});
const source = getSource({
declaration
});
const signature = getFileModuleSignature({
declaration
});
return {
kind,
id,
name,
docs,
source,
signature,
declarations
};
}
function getFileModuleDocs({
declaration
}) {
var _declaration$getDesce;
const doc = (_declaration$getDesce = declaration.getDescendantsOfKind(tsm__namespace.SyntaxKind.JSDocComment)[0]) == null ? void 0 : _declaration$getDesce.getText();
return doc ? [doc] : [];
}
function getFileModuleSignature({
declaration
}) {
const filename = getFilename({
declaration
});
const signature = `module '${filename}' {}`;
return formatText(signature);
}
function isFunction(declaration) {
return tsm__namespace.Node.isFunctionDeclaration(declaration) && (declaration.isAmbient() || declaration.isImplementation());
}
function newFunction({
id,
name,
declaration,
getSource,
getType
}) {
const kind = exports.DeclarationKinds.FunctionDeclaration;
const docs = getJSDocs({
declaration
});
const source = getSource({
declaration
});
const type = getType({
declaration
});
const signature = getFunctionSignature({
name,
type
});
return {
kind,
id,
name,
docs,
source,
signature,
type
};
}
function isFunctionExpression(declaration) {
return tsm__namespace.Node.isVariableDeclaration(declaration) && hasFunctionLikeType(declaration);
}
function newFunctionExpression({
id,
name,
declaration,
getSource,
getType
}) {
const kind = exports.DeclarationKinds.FunctionDeclaration;
const docs = getJSDocs({
declaration
});
const source = getSource({
declaration
});
const type = getType({
declaration
});
const signature = getFunctionSignature({
name,
type
});
return {
kind,
id,
name,
docs,
source,
signature,
type
};
}
function getFunctionSignature({
name,
type
}) {
const signature = `${name}: ${type}`;
return formatFunctionSignature(signature);
}
function getDeclarationName({
exportName,
declaration
}) {
var _declaration$getName;
if (exportName !== 'default' || tsm__namespace.Node.isExpression(declaration) || tsm__namespace.Node.isSourceFile(declaration)) {
return exportName;
}
return (_declaration$getName = declaration.getName()) != null ? _declaration$getName : exportName;
}
function isInterface(declaration) {
return tsm__namespace.Node.isInterfaceDeclaration(declaration);
}
function newInterface({
id,
name,
declaration,
getSource,
getType
}) {
const kind = exports.DeclarationKinds.InterfaceDeclaration;
const docs = getJSDocs({
declaration
});
const source = getSource({
declaration
});
const signature = getWrapperSignature({
declaration
});
const members = getInterfaceMembers({
interfaceID: id,
interfaceDeclaration: declaration,
getSource,
getType
});
return {
kind,
id,
name,
docs,
source,
signature,
members
};
}
function getInterfaceMembers({
interfaceID,
interfaceDeclaration,
getSource,
getType
}) {
const seenMethods = new Set();
const members = interfaceDeclaration.getMembers().flatMap((declaration, index) => {
const name = getMemberName({
declaration
});
const id = toID(interfaceID, getMemberID({
declaration,
index
}));
if (isInternalDeclaration({
declaration,
name
})) {
return [];
}
if (tsm__namespace.Node.isPropertySignature(declaration)) {
return newProperty({
id,
name,
declaration,
getSource
});
}
if (tsm__namespace.Node.isMethodSignature(declaration)) {
// Skip overloaded methods
if (seenMethods.has(id)) {
return [];
}
seenMethods.add(id);
return newMethod({
id,
name,
declaration,
getSource,
getType
});
}
if (tsm__namespace.Node.isConstructSignatureDeclaration(declaration)) {
return newConstructSignature({
id,
name,
declaration,
getSource
});
}
if (tsm__namespace.Node.isCallSignatureDeclaration(declaration)) {
return newCallSignature({
id,
name,
declaration,
getSource
});
}
return newIndexSignature({
id,
name,
declaration,
getSource
});
}).sort(sortByID);
return {
properties: members.filter(isInterfacePropertyDeclaration),
methods: members.filter(isInterfaceMethodDeclaration),
constructSignatures: members.filter(isInterfaceConstructSignatureDeclaration),
callSignatures: members.filter(isInterfaceCallSignatureDeclaration),
indexSignatures: members.filter(isInterfaceIndexSignatureDeclaration)
};
}
function getMemberName({
declaration
}) {
if (tsm__namespace.Node.isPropertySignature(declaration) || tsm__namespace.Node.isMethodSignature(declaration)) {
return declaration.getName();
}
if (tsm__namespace.Node.isConstructSignatureDeclaration(declaration)) {
return 'construct signature';
}
if (tsm__namespace.Node.isCallSignatureDeclaration(declaration)) {
return 'call signature';
}
return 'index signature';
}
function getMemberID({
declaration,
index
}) {
if (tsm__namespace.Node.isPropertySignature(declaration) || tsm__namespace.Node.isMethodSignature(declaration)) {
return declaration.getName();
}
if (tsm__namespace.Node.isConstructSignatureDeclaration(declaration)) {
return `${index}-construct-signature`;
}
if (tsm__namespace.Node.isCallSignatureDeclaration(declaration)) {
return `${index}-call-signature`;
}
return `${index}-index-signature`;
}
function newProperty({
id,
name,
declaration,
getSource
}) {
const kind = exports.DeclarationKinds.InterfacePropertyDeclaration;
const docs = getJSDocs({
declaration
});
const source = getSource({
declaration
});
const isReadonly = declaration.isReadonly();
const isOptional = declaration.hasQuestionToken();
const type = getApparentType({
declaration
});
const signature = formatInterfaceMember(declaration.getText());
return {
kind,
id,
name,
docs,
source,
signature,
isReadonly,
isOptional,
type
};
}
function newMethod({
id,
name,
declaration,
getSource,
getType
}) {
const kind = exports.DeclarationKinds.InterfaceMethodDeclaration;
const docs = getJSDocs({
declaration
});
const source = getSource({
declaration
});
const type = getType({
declaration
});
const signature = formatInterfaceMember(`${name}: ${type}`);
return {
kind,
id,
name,
docs,
source,
signature,
type
};
}
function newConstructSignature({
id,
name,
declaration,
getSource
}) {
const kind = exports.DeclarationKinds.InterfaceConstructSignatureDeclaration;
const docs = getJSDocs({
declaration
});
const source = getSource({
declaration
});
const signature = formatInterfaceMember(declaration.getText());
return {
kind,
id,
name,
docs,
source,
signature
};
}
function newCallSignature({
id,
name,
declaration,
getSource
}) {
const kind = exports.DeclarationKinds.InterfaceCallSignatureDeclaration;
const docs = getJSDocs({
declaration
});
const source = getSource({
declaration
});
const signature = formatInterfaceMember(declaration.getText());
return {
kind,
id,
name,
docs,
source,
signature
};
}
function newIndexSignature({
id,
name,
declaration,
getSource
}) {
const kind = exports.DeclarationKinds.InterfaceIndexSignatureDeclaration;
const docs = getJSDocs({
declaration
});
const source = getSource({
declaration
});
const signature = formatInterfaceMember(declaration.getText());
return {
kind,
id,
name,
docs,
source,
signature
};
}
function isExportedDeclarations(declaration) {
return tsm__namespace.Node.isVariableDeclaration(declaration) || tsm__namespace.Node.isClassDeclaration(declaration) || tsm__namespace.Node.isInterfaceDeclaration(declaration) || tsm__namespace.Node.isEnumDeclaration(declaration) || tsm__namespace.Node.isTypeAliasDeclaration(declaration) || tsm__namespace.Node.isModuleDeclaration(declaration) || tsm__namespace.Node.isExpression(declaration) || tsm__namespace.Node.isFunctionDeclaration(declaration);
}
function isGlobalDeclaration({
declaration
}) {
const isGlobalVariable = tsm__namespace.Node.isVariableDeclaration(declaration) && declaration.getVariableStatementOrThrow().isAmbient() && !declaration.isExported();
const isGlobalFunction = tsm__namespace.Node.isFunctionDeclaration(declaration) && declaration.isAmbient() && declaration.getName() !== undefined && !declaration.isExported();
const isGlobalNamespace = tsm__namespace.Node.isModuleDeclaration(declaration) && declaration.isAmbient() && !declaration.isExported() && !declaration.hasModuleKeyword();
return isGlobalVariable || isGlobalFunction || isGlobalNamespace;
}
function isNamespace(declaration) {
return tsm__namespace.Node.isModuleDeclaration(declaration);
}
function newNamespace({
id,
name,
declaration,
declarations,
getSource
}) {
const kind = exports.DeclarationKinds.NamespaceDeclaration;
const docs = getJSDocs({
declaration
});
const source = getSource({
declaration
});
const signature = getNamespaceSignature({
id,
name
});
return {
kind,
id,
name: id,
docs,
source,
signature,
declarations
};
}
function getNamespaceSignature({
id,
name
}) {
const signature = isAmbientModule({
name
}) ? `module ${name} {}` : `namespace ${id} {}`;
return formatText(signature);
}
function isAmbientModule({
name
}) {
return name.startsWith("'") || name.startsWith('"');
}
function isTypeAlias(declaration) {
return tsm__namespace.Node.isTypeAliasDeclaration(declaration);
}
function newTypeAlias({
id,
name,
declaration,
getSource
}) {
const kind = exports.DeclarationKinds.TypeAliasDeclaration;
const docs = getJSDocs({
declaration
});
const source = getSource({
declaration
});
const signature = getTypeAliasSignature({
declaration
});
return {
kind,
id,
name,
docs,
source,
signature
};
}
function getTypeAliasSignature({
declaration
}) {
const signature = declaration.getText();
return formatText(signature);
}
function getPackageDeclarations({
project,
indexFile,
getSource,
getType,
maxDepth = 5
}) {
return getModuleDeclarations({
module: indexFile,
moduleName: '',
maxDepth,
getSource,
getType,
project
});
}
/**
* `getModuleDeclarations` extracts the public declarations from the given module.
*
* @param module - module (for example, a source file, node or namespace)
* @param maxDepth - maximum extraction depth for inner modules
* @param getSource - source provider
* @param getType - type checker
* @param moduleName - module's name, used to define IDs for declarations (optional)
*/
function getModuleDeclarations({
module,
moduleName,
maxDepth,
getSource,
getType,
project
}) {
log('getModuleDeclarations: extracting declarations: %O', {
moduleName,
maxDepth,
module
});
const normalExportDeclarations = getNormalExportDeclarations({
module,
moduleName
});
log('getModuleDeclarations: got normal export declarations: %O', {
moduleName,
total: normalExportDeclarations.length,
normalExportDeclarations
});
const exportEqualsDeclarations = getExportEqualsDeclarations({
module,
moduleName
});
log('getModuleDeclarations: got export equals declarations: %O', {
moduleName,
total: exportEqualsDeclarations.length,
exportEqualsDeclarations
});
const ambientModulesDeclarations = getAmbientModulesDeclarations({
project
});
log('getModuleDeclarations: got ambient modules declarations: %O', {
moduleName,
total: ambientModulesDeclarations.length,
ambientModulesDeclarations
});
const globalAmbientDeclarations = getGlobalAmbientDeclarations({
module,
moduleName
});
log('getModuleDeclarations: got global ambient declarations: %O', {
moduleName,
total: globalAmbientDeclarations.length,
globalAmbientDeclarations
});
return extractModuleDeclarations({
exportedDeclarations: [...normalExportDeclarations, ...exportEqualsDeclarations, ...ambientModulesDeclarations, ...globalAmbientDeclarations],
maxDepth,
getSource,
getType
});
}
function getNormalExportDeclarations({
module,
moduleName
}) {
const namedExports = new Set();
return Array.from(module.getExportedDeclarations()).flatMap(([exportName, declarations]) => {
return declarations.flatMap(declaration => {
// Skip internal/private declarations
if (isInternalDeclaration({
declaration,
name: exportName
})) {
return [];
}
const exportID = toID(moduleName, exportName);
const declarationName = getDeclarationName({
exportName,
declaration
});
const declarationID = toID(moduleName, declarationName); // Keep track of named exports for the filter step
if (declarationID === exportID) {
namedExports.add(declarationID);
}
return {
exportID,
exportName,
declarationID,
declarationName,
declaration
};
});
}).filter(({
exportID,
declarationID
}) => {
// Keep only named exports or default exports
// with no corresponding named export
return declarationID === exportID || !namedExports.has(declarationID);
});
}
function getExportEqualsDeclarations({
module,
moduleName
}) {
var _module$getExportAssi;
// Skip shorthand ambient modules without body
// (for example, `declare module 'foo';`)
if (tsm__namespace.Node.isModuleDeclaration(module) && !module.hasBody()) {
return [];
}
const exportIdentifier = (_module$getExportAssi = module.getExportAssignment(ea => ea.isExportEquals())) == null ? void 0 : _module$getExportAssi.getLastChildByKind(tsm__namespace.SyntaxKind.Identifier);
if (!exportIdentifier) {
return [];
}
const exportName = exportIdentifier.getText();
return exportIdentifier.getDefinitionNodes().flatMap(declaration => {
// Skip internal or unsupported declarations
if (isInternalDeclaration({
declaration,
name: exportName
}) || !isExportedDeclarations(declaration)) {
return [];
} // Skip namespaces since `getNormalExportDeclarations` already extracts
// the inner declarations of an export equals namespace
// as non-namespaced declarations belonging to the parent module.
// See snapshot for `export-equals-function-and-namespace.test.ts`.
if (isNamespace(declaration)) {
return [];
}
const exportID = toID(moduleName, exportName);
const declarationName = getDeclarationName({
exportName,
declaration
});
const declarationID = toID(moduleName, declarationName);
return {
exportID,
exportName,
declarationID,
declarationName,
declaration
};
});
}
function getAmbientModulesDeclarations({
project
}) {
if (!project) {
return [];
}
return project.getAmbientModules().flatMap(symbol => {
return symbol.getDeclarations().flatMap(declaration => {
const filepath = declaration.getSourceFile().getFilePath();
if (!tsm__namespace.Node.isModuleDeclaration(declaration) || filepath.startsWith('/node_modules')) {
return [];
}
const exportName = declaration.getName();
const declarationName = exportName; // Remove surrounding quotes and eventual spaces
const exportID = exportName.replace(/"|'/g, '').replace(/\s/g, '_').trim();
const declarationID = exportID;
return {
exportID,
exportName,
declarationID,
declarationName,
declaration
};
});
});
}
function getGlobalAmbientDeclarations({
module,
moduleName
}) {
if (!tsm__namespace.Node.isSourceFile(module)) {
return [];
} // See https://www.typescriptlang.org/docs/handbook/declaration-files/by-example.html#global-variables
const globalCandidates = [...module.getVariableDeclarations(), ...module.getFunctions(), ...module.getModules()];
return globalCandidates.flatMap(declaration => {
// Global ambient functions must have a name
const exportName = declaration.getName();
if (!isGlobalDeclaration({
declaration
}) || isInternalDeclaration({
declaration,
name: exportName
})) {
return [];
}
const exportID = toID(moduleName, exportName);
const declarationName = getDeclarationName({
exportName,
declaration
});
const declarationID = toID(moduleName, declarationName);
return {
exportID,
exportName,
declarationID,
declarationName,
declaration
};
});
}
function extractModuleDeclarations({
exportedDeclarations,
maxDepth,
getSource,
getType
}) {
const exportedFunctions = new Set();
const exportedNamespaces = new Set();
const declarations = exportedDeclarations.flatMap(({
exportID,
declarationID: id,
declarationName: name,
declaration
}) => {
if (isVariable(declaration)) {
return newVariable({
id,
name,
declaration,
getSource
});
}
if (isVariableAssignmentExpression(declaration)) {
return newVariableAssignmentExpression({
id,
name,
declaration,
getSource
});
}
if (isExpression(declaration)) {
return newExpression({
id,
name,
declaration,
getSource
});
}
if (isFunction(declaration)) {
// Skip ambient function overloads
if (exportedFunctions.has(exportID)) {
return [];
}
exportedFunctions.add(exportID);
return newFunction({
id,
name,
declaration,
getSource,
getType
});
}
if (isFunctionExpression(declaration)) {
return newFunctionExpression({
id,
name,
declaration,
getSource,
getType
});
}
if (isClass(declaration)) {
return newClass({
id,
name,
declaration,
getSource,
getType
});
}
if (isInterface(declaration)) {
return newInterface({
id,
name,
declaration,
getSource,
getType
});
}
if (isEnum(declaration)) {
return newEnum({
id,
name,
declaration,
getSource
});
}
if (isTypeAlias(declaration)) {
return newTypeAlias({
id,
name,
declaration,
getSource
});
}
if (isNamespace(declaration) && maxDepth > 0) {
// Skip merged or nested namespace declarations
if (exportedNamespaces.has(exportID)) {
return [];
}
const declarations = getModuleDeclarations({
module: declaration,
moduleName: id,
maxDepth: maxDepth - 1,
getSource,
getType
});
exportedNamespaces.add(exportID);
return newNamespace({
id,
name,
declaration,
declarations,
getSource
});
} // From `import * as ns from module; export { ns };`
// or from `export * as ns from module`.
if (isFileModule(declaration) && maxDepth > 0) {
const declarations = getModuleDeclarations({
module: declaration,
moduleName: id,
maxDepth: maxDepth - 1,
getSource,
getType
});
return newFileModule({
id,
name,
declaration,
declarations,
getSource
});
}
return [];
}).sort(sortByID);
return {
variables: declarations.filter(isVariableDeclaration),
functions: declarations.filter(isFunctionDeclaration),
classes: declarations.filter(isClassDeclaration),
interfaces: declarations.filter(isInterfaceDeclaration),
enums: declarations.filter(isEnumDeclaration),
typeAliases: declarations.filter(isTypeAliasDeclaration),
namespaces: declarations.filter(isNamespaceDeclaration)
};
}
function getPackageFiles({
indexFile,
declarations,
getRepositoryFileURL,
getUnpkgFileURL
}) {
const indexFilename = getFilename({
declaration: indexFile
});
const declarationFilenames = getDeclarationFilenames({
declarations
});
return Array.from(new Set([indexFilename, ...declarationFilenames])).sort().map(filename => {
const url = getRepositoryFileURL({
filename
});
const unpkgURL = getUnpkgFileURL({
filename
});
if (filename === indexFilename) {
return {
isIndexFile: true,
filename,
url,
unpkgURL
};
}
return {
filename,
url,
unpkgURL
};
});
}
function getDeclarationFilenames({
declarations
}) {
return Object.values(declarations).flat().flatMap(declaration => {
const {
source: {
filename
}
} = declaration;
if (isNamespaceDeclaration(declaration)) {
const {
declarations
} = declaration;
return [filename, ...getDeclarationFilenames({
declarations
})];
}
return filename;
});
}
function getProject({
fileSystem,
pattern = '**/*.ts'
}) {
const project = new tsm__namespace.Project({
fileSystem,
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']
}
});
project.addSourceFilesAtPaths(pattern);
return project;
}
function toForwardSlashes(s) {
return s.replace(/\\/g, '/');
}
function getRepositoryFileURLProvider({
repository
}) {
if (!repository) {
return () => undefined;
}
const {
url,
tag = '',
dir = ''
} = repository;
const info = HostedGitInfo__default["default"].fromUrl(url, {
noGitPlus: true
});
if (!info) {
return () => undefined;
}
const linePrefix = getLinePrefix({
info
});
return ({
filename,
line
}) => {
const filepath = toForwardSlashes(path__namespace.join(dir, filename));
const fileURL = info.browse(filepath, {
committish: tag
});
const lineFragment = line ? `${linePrefix}${line}` : '';
return `${fileURL}${lineFragment}`;
};
}
function getLinePrefix({
info
}) {
const {
type: provider
} = info;
switch (provider) {
case 'bitbucket':
return '#lines-';
default:
return '#L';
}
}
function getStartLineNumber({
declaration
}) {
if (tsm__namespace.Node.isSourceFile(declaration)) {
return 1;
}
return declaration.getStartLineNumber();
}
function getSourceProvider({
getRepositoryFileURL,
getUnpkgFileURL
}) {
return ({
declaration
}) => {
const filename = getFilename({
declaration
});
const line = getStartLineNumber({
declaration
});
const url = getRepositoryFileURL({
filename,
line
});
const unpkgURL = getUnpkgFileURL({
filename,
line
});
return {
filename,
line,
url,
unpkgURL
};
};
}
function getTypeChecker({
project
}) {
const projectTypeChecker = project.getTypeChecker();
return ({
declaration
}) => {
let type = 'any';
try {
const typeChecker = projectTypeChecker.compilerObject;
const {
compilerNode
} = declaration;
const nodeType = typeChecker.getTypeAtLocation(compilerNode);
type = typeChecker.typeToString(nodeType, compilerNode, tsm__namespace.ts.TypeFormatFlags.NoTruncation | tsm__namespace.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope);
} catch (err) {
// istanbul ignore next
log('type checker: error: %O', {
err
});
}
return type;
};
}
function getUnpkgFileURLProvider({
id
}) {
if (!id) {
return () => undefined;
}
return ({
filename,
line
}) => {
const fileURL = `https://unpkg.com/browse/${id}/${filename}`;
if (!line) {
return fileURL;
}
return `${fileURL}#L${line}`;
};
}
/**
* `extractPackageAPI` extracts the public API from a package.
*
* @param fileSystem - filesystem containing the package's source code
* @param entryPoint - absolute path of the file acting as the package's entry point
* @param maxDepth - maximum depth for analyzing nested namespaces (default: `5`)
* @param pattern - file pattern including files to be analyzed
* @param repository - a tagged git repository to enable linking to source
* @param id - npm-style package ID used for logging (for example, `foo@1.0.0`)
*
* @see {@link PackageAPI}
*/
function extractPackageAPI({
fileSystem,
entryPoint,
maxDepth,
pattern,
repository,
id
}) {
const start = perf_hooks.performance.now();
log('extractPackageAPI: extracting API: %O', {
id,
entryPoint,
maxDepth,
pattern,
repository,
fileSystem
});
const project = getProject({
fileSystem,
pattern
});
log('extractPackageAPI: created project: %O', {
id,
files: project.getSourceFiles().map(file => file.getFilePath())
});
const indexFile = project.getSourceFileOrThrow(entryPoint);
log('extractPackageAPI: found index file: %O', {
id,
indexFile: indexFile.getFilePath()
});
const getRepositoryFileURL = getRepositoryFileURLProvider({
repository
});
log('extractPackageAPI: got repository file URL provider');
const getUnpkgFileURL = getUnpkgFileURLProvider({
id
});
log('extractPackageAPI: got unpkg file URL provider');
const getSource = getSourceProvider({
getRepositoryFileURL,
getUnpkgFileURL
});
log('extractPackageAPI: got source provider');
const getType = getTypeChecker({
project
});
log('extractPackageAPI: got type checker');
const overview = getOverview({
indexFile
});
log('extractPackageAPI: