118 lines
3.0 KiB
TypeScript

import { DocumentData, DocumentMetadata } from '@nrwl/nx-dev/models-document';
import { readFileSync } from 'fs';
import matter from 'gray-matter';
import { join } from 'path';
import { extractTitle } from './documents.utils';
export interface StaticDocumentPaths {
params: { segments: string[] };
}
export class DocumentsApi {
private documents: DocumentMetadata;
constructor(
private readonly options: {
publicDocsRoot: string;
documentSources: DocumentMetadata[];
addAncestor: { id: string; name: string } | null;
}
) {
if (!options.publicDocsRoot) {
throw new Error('public docs root cannot be undefined');
}
if (!options.documentSources) {
throw new Error('public document sources cannot be undefined');
}
const itemList: DocumentMetadata[] = options.documentSources.flatMap(
(x) => x.itemList
) as DocumentMetadata[];
this.documents = {
id: 'documents',
name: 'documents',
itemList: !!this.options.addAncestor
? [
{
id: this.options.addAncestor.id,
name: this.options.addAncestor.name,
itemList,
},
]
: itemList,
};
}
getDocument(path: string[]): DocumentData {
const docPath = this.getFilePath(path);
const originalContent = readFileSync(docPath, 'utf8');
const file = matter(originalContent);
// Set default title if not provided in front-matter section.
if (!file.data.title) {
file.data.title = extractTitle(originalContent) ?? path[path.length - 1];
file.data.description = file.excerpt ?? path[path.length - 1];
}
return {
filePath: docPath,
data: file.data,
content: file.content,
excerpt: file.excerpt,
};
}
getDocuments(): DocumentMetadata {
const docs = this.documents;
if (docs) return docs;
throw new Error(`Cannot find any documents`);
}
getStaticDocumentPaths(): StaticDocumentPaths[] {
const paths: StaticDocumentPaths[] = [];
function recur(curr, acc) {
if (curr.itemList) {
curr.itemList.forEach((ii) => {
recur(ii, [...acc, curr.id]);
});
} else {
paths.push({
params: {
segments: [...acc, curr.id],
},
});
}
}
if (!this.documents || !this.documents.itemList)
throw new Error(`Can't find any items`);
this.documents.itemList.forEach((item) => {
recur(item, []);
});
return paths;
}
private getFilePath(path: string[]): string {
let items = this.documents?.itemList;
if (!items) {
throw new Error(`Document not found`);
}
let found;
for (const part of path) {
found = items?.find((item) => item.id === part);
if (found) {
items = found.itemList;
} else {
throw new Error(`Document not found`);
}
}
const file = found.file ?? ['generated', ...path].join('/');
return join(this.options.publicDocsRoot, `${file}.md`);
}
}