typedoc
Version:
Create api documentation for TypeScript projects.
73 lines (72 loc) • 3.26 kB
JavaScript
import path from "path";
import fs from "fs";
import { ConverterComponent } from "../components.js";
import { ConverterEvents } from "../converter-events.js";
import { MinimalSourceFile } from "../../utils/minimalSourceFile.js";
import { isFile } from "../../utils/fs.js";
/**
* Handles `@include` and `@includeCode` within comments/documents.
*/
export class IncludePlugin extends ConverterComponent {
get logger() {
return this.application.logger;
}
constructor(owner) {
super(owner);
const onCreate = this.onCreate.bind(this);
owner.on(ConverterEvents.CREATE_DOCUMENT, onCreate);
owner.on(ConverterEvents.CREATE_DECLARATION, onCreate);
owner.on(ConverterEvents.CREATE_PARAMETER, onCreate);
owner.on(ConverterEvents.CREATE_SIGNATURE, onCreate);
owner.on(ConverterEvents.CREATE_TYPE_PARAMETER, onCreate);
}
onCreate(_context, refl) {
if (refl.isDocument()) {
// We know it must be present as documents are always associated with a file.
const relative = this.application.files.getReflectionPath(refl);
this.checkIncludeTagsParts(refl, path.dirname(relative), refl.content);
}
if (!refl.comment?.sourcePath)
return;
const relative = path.dirname(refl.comment.sourcePath);
this.checkIncludeTagsParts(refl, relative, refl.comment.summary);
for (const tag of refl.comment.blockTags) {
this.checkIncludeTagsParts(refl, relative, tag.content);
}
}
checkIncludeTagsParts(refl, relative, parts, included = []) {
for (let i = 0; i < parts.length; ++i) {
const part = parts[i];
if (part.kind !== "inline-tag" ||
!["@include", "@includeCode"].includes(part.tag)) {
continue;
}
const file = path.resolve(relative, part.text.trim());
if (included.includes(file) && part.tag === "@include") {
this.logger.error(this.logger.i18n.include_0_in_1_specified_2_circular_include_3(part.tag, refl.getFriendlyFullName(), part.text, included.join("\n\t")));
}
else if (isFile(file)) {
const text = fs.readFileSync(file, "utf-8");
if (part.tag === "@include") {
const sf = new MinimalSourceFile(text, file);
const { content } = this.owner.parseRawComment(sf, refl.project.files);
this.checkIncludeTagsParts(refl, path.dirname(file), content, [...included, file]);
parts.splice(i, 1, ...content);
}
else {
parts[i] = {
kind: "code",
text: makeCodeBlock(path.extname(file).substring(1), text),
};
}
}
else {
this.logger.warn(this.logger.i18n.include_0_in_1_specified_2_resolved_to_3_does_not_exist(part.tag, refl.getFriendlyFullName(), part.text, file));
}
}
}
}
function makeCodeBlock(lang, code) {
const escaped = code.replace(/`(?=`)/g, "`\u200B");
return "\n\n```" + lang + "\n" + escaped.trimEnd() + "\n```";
}