typedoc
Version:
Create api documentation for TypeScript projects.
79 lines (78 loc) • 3.66 kB
JavaScript
import { i18n } from "#utils";
import { ReflectionKind, ReflectionSymbolId, } from "../models/index.js";
const linkTags = ["@link", "@linkcode", "@linkplain"];
function getBrokenPartLinks(parts) {
const links = [];
for (const part of parts) {
if (part.kind === "inline-tag" &&
linkTags.includes(part.tag) &&
(!part.target || part.target instanceof ReflectionSymbolId)) {
links.push(part);
}
}
return links;
}
function getBrokenLinks(comment) {
if (!comment)
return [];
const links = [...getBrokenPartLinks(comment.summary)];
for (const tag of comment.blockTags) {
links.push(...getBrokenPartLinks(tag.content));
}
return links;
}
export function validateLinks(project, logger) {
for (const id in project.reflections) {
checkReflection(project.reflections[id], logger);
}
}
function checkReflection(reflection, logger) {
if (reflection.isProject() || reflection.isDeclaration()) {
for (const broken of getBrokenPartLinks(reflection.readme || [])) {
const linkText = broken.text.trim();
// #2360, "@" is a future reserved character in TSDoc component paths
// If a link starts with it, and doesn't include a module source indicator "!"
// then the user probably is trying to link to a package containing "@" with an absolute link.
if (linkText.startsWith("@") && !linkText.includes("!")) {
logger.warn(i18n.failed_to_resolve_link_to_0_in_readme_for_1_may_have_meant_2(linkText, reflection.getFriendlyFullName(), linkText.replace(/[.#~]/, "!")));
}
else {
logger.warn(i18n.failed_to_resolve_link_to_0_in_readme_for_1(linkText, reflection.getFriendlyFullName()));
}
}
}
if (reflection.isDocument()) {
for (const broken of getBrokenPartLinks(reflection.content)) {
const linkText = broken.text.trim();
if (linkText.startsWith("@") && !linkText.includes("!")) {
logger.warn(i18n.failed_to_resolve_link_to_0_in_document_1_may_have_meant_2(linkText, reflection.getFriendlyFullName(), linkText.replace(/[.#~]/, "!")));
}
else {
logger.warn(i18n.failed_to_resolve_link_to_0_in_document_1(linkText, reflection.getFriendlyFullName()));
}
}
}
for (const broken of getBrokenLinks(reflection.comment)) {
reportBrokenCommentLink(broken, reflection, logger);
}
if (reflection.isDeclaration() &&
reflection.kindOf(ReflectionKind.TypeAlias) &&
reflection.type?.type === "union" &&
reflection.type.elementSummaries) {
for (const broken of reflection.type.elementSummaries.flatMap(getBrokenPartLinks)) {
reportBrokenCommentLink(broken, reflection, logger);
}
}
}
function reportBrokenCommentLink(broken, reflection, logger) {
const linkText = broken.text.trim();
if (broken.target instanceof ReflectionSymbolId) {
logger.warn(i18n.comment_for_0_links_to_1_not_included_in_docs_use_external_link_2(reflection.getFriendlyFullName(), linkText, `{ "${broken.target.packageName}": { "${broken.target.qualifiedName}": "#" }}`));
}
else if (linkText.startsWith("@") && !linkText.includes("!")) {
logger.warn(i18n.failed_to_resolve_link_to_0_in_comment_for_1_may_have_meant_2(linkText, reflection.getFriendlyFullName(), linkText.replace(/[.#~]/, "!")));
}
else {
logger.warn(i18n.failed_to_resolve_link_to_0_in_comment_for_1(linkText, reflection.getFriendlyFullName()));
}
}