typedoc
Version:
Create api documentation for TypeScript projects.
92 lines (91 loc) • 4.58 kB
JavaScript
import { DeclarationReflection, ReflectionKind, ReflectionType, } from "../models/index.js";
import { i18n, removeFlag } from "#utils";
export function validateDocumentation(project, logger, requiredToBeDocumented, intentionallyNotDocumented, packagesRequiringDocumentation) {
let kinds = requiredToBeDocumented.reduce((prev, cur) => prev | ReflectionKind[cur], 0);
// Functions, Constructors, and Accessors never have comments directly on them.
// If they are required to be documented, what's really required is that their
// contained signatures have a comment.
if (kinds & ReflectionKind.FunctionOrMethod) {
kinds |= ReflectionKind.CallSignature;
kinds = removeFlag(kinds, ReflectionKind.FunctionOrMethod);
}
if (kinds & ReflectionKind.Constructor) {
kinds |= ReflectionKind.ConstructorSignature;
kinds = removeFlag(kinds, ReflectionKind.Constructor);
}
if (kinds & ReflectionKind.Accessor) {
kinds |= ReflectionKind.GetSignature | ReflectionKind.SetSignature;
kinds = removeFlag(kinds, ReflectionKind.Accessor);
}
const toProcess = project.getReflectionsByKind(kinds);
const seen = new Set();
const intentionalUsage = new Set();
outer: while (toProcess.length) {
const ref = toProcess.shift();
if (seen.has(ref))
continue;
seen.add(ref);
// If inside a parameter, we shouldn't care. Callback parameter's values don't get deeply documented.
let r = ref.parent;
while (r) {
if (r.kindOf(ReflectionKind.Parameter)) {
continue outer;
}
r = r.parent;
}
// Type aliases own their comments, even if they're function-likes.
// So if we're a type literal owned by a type alias, don't do anything.
if (ref.kindOf(ReflectionKind.TypeLiteral) &&
ref.parent?.kindOf(ReflectionKind.TypeAlias)) {
toProcess.push(ref.parent);
continue;
}
// Call signatures are considered documented if they have a comment directly, or their
// container has a comment and they are directly within a type literal belonging to that container.
if (ref.kindOf(ReflectionKind.CallSignature) &&
ref.parent?.kindOf(ReflectionKind.TypeLiteral)) {
toProcess.push(ref.parent.parent);
continue;
}
// Construct signatures are considered documented if they are directly within a documented type alias.
if (ref.kindOf(ReflectionKind.ConstructorSignature) &&
ref.parent?.parent?.kindOf(ReflectionKind.TypeAlias)) {
toProcess.push(ref.parent.parent);
continue;
}
if (ref instanceof DeclarationReflection) {
const signatures = ref.type instanceof ReflectionType
? ref.type.declaration.getNonIndexSignatures()
: ref.getNonIndexSignatures();
if (signatures.length) {
// We've been asked to validate this reflection, so we should validate that
// signatures all have comments
toProcess.push(...signatures);
if (ref.kindOf(ReflectionKind.SignatureContainer)) {
// Comments belong to each signature, and will not be included on this object.
continue;
}
}
}
const symbolId = project.getSymbolIdFromReflection(ref);
// #2644, signatures may be documented by their parent reflection.
const hasComment = ref.hasComment() ||
(ref.kindOf(ReflectionKind.SomeSignature) &&
ref.parent?.hasComment());
if (!hasComment && symbolId) {
if (!packagesRequiringDocumentation.includes(symbolId.packageName)) {
continue;
}
const intentionalIndex = intentionallyNotDocumented.indexOf(ref.getFriendlyFullName());
if (intentionalIndex !== -1) {
intentionalUsage.add(intentionalIndex);
continue;
}
logger.warn(i18n.reflection_0_kind_1_defined_in_2_does_not_have_any_documentation(ref.getFriendlyFullName(), ReflectionKind[ref.kind], `${symbolId.packageName}/${symbolId.packagePath}`));
}
}
const unusedIntentional = intentionallyNotDocumented.filter((_, i) => !intentionalUsage.has(i));
if (unusedIntentional.length) {
logger.warn(i18n.invalid_intentionally_not_documented_names_0(unusedIntentional.join("\n\t")));
}
}