@compodoc/compodoc
Version:
The missing documentation tool for your Angular application
188 lines (165 loc) • 6.55 kB
text/typescript
import * as fs from 'fs-extra';
import * as _ from '../../utils/collection.util';
import * as path from 'path';
import FileEngine from './file.engine';
import I18nEngine from './i18n.engine';
import { markedAcl } from '../../utils/marked.acl';
import { clearModuleCache } from '../../utils/module-cache.util';
export interface markdownReadedDatas {
markdown: string;
rawData: string;
}
export class MarkdownEngine {
/**
* List of markdown files without .md extension
*/
private readonly markdownFiles = ['README', 'CHANGELOG', 'LICENSE', 'CONTRIBUTING', 'TODO'];
private markedInstance;
private static instance: MarkdownEngine;
private constructor() {
clearModuleCache('marked');
this.markedInstance = markedAcl;
const self = this;
this.markedInstance.use({
gfm: true,
breaks: false,
renderer: {
code(token) {
const language = token.lang || 'none';
const highlighted = self.escape(token.text);
return `<b>${I18nEngine.translate(
'example'
)} :</b><div><pre class="line-numbers"><code class="language-${language}">${highlighted}</code></pre></div>`;
},
table(token) {
let header = '';
for (const cell of token.header) {
const align = cell.align ? ` style="text-align: ${cell.align}"` : '';
header += `<th${align}>${this.parser.parseInline(cell.tokens)}</th>`;
}
header = `<tr>${header}</tr>\n`;
let body = '';
for (const row of token.rows) {
let cells = '';
for (const cell of row) {
const align = cell.align ? ` style="text-align: ${cell.align}"` : '';
cells += `<td${align}>${this.parser.parseInline(cell.tokens)}</td>`;
}
body += `<tr>${cells}</tr>\n`;
}
return (
'<table class="table table-bordered compodoc-table">\n' +
'<thead>\n' +
header +
'</thead>\n' +
'<tbody>\n' +
body +
'</tbody>\n' +
'</table>\n'
);
},
image(token) {
let out =
'<img src="' +
token.href +
'" alt="' +
token.text +
'" class="img-responsive"';
if (token.title) {
out += ' title="' + token.title + '"';
}
out += '>';
return out;
}
}
});
}
public static getInstance() {
if (!MarkdownEngine.instance) {
MarkdownEngine.instance = new MarkdownEngine();
}
return MarkdownEngine.instance;
}
public getTraditionalMarkdown(filepath: string): Promise<markdownReadedDatas> {
return FileEngine.get(process.cwd() + path.sep + filepath + '.md')
.catch(err => FileEngine.get(process.cwd() + path.sep + filepath))
.then(data => {
const returnedData: markdownReadedDatas = {
markdown: this.renderMarkdown(data),
rawData: data
};
return returnedData;
});
}
public getTraditionalMarkdownSync(filepath: string): string {
return this.renderMarkdown(FileEngine.getSync(process.cwd() + path.sep + filepath));
}
private getReadmeFile(): Promise<string> {
return FileEngine.get(process.cwd() + path.sep + 'README.md').then(data =>
this.renderMarkdown(data)
);
}
public readNeighbourReadmeFile(file: string): string {
let dirname = path.dirname(file);
let readmeFile = dirname + path.sep + path.basename(file, '.ts') + '.md';
return fs.readFileSync(readmeFile, 'utf8');
}
public hasNeighbourReadmeFile(file: string): boolean {
let dirname = path.dirname(file);
let readmeFile = dirname + path.sep + path.basename(file, '.ts') + '.md';
return FileEngine.existsSync(readmeFile);
}
private componentReadmeFile(file: string): string {
let dirname = path.dirname(file);
let readmeFile = dirname + path.sep + 'README.md';
let readmeAlternativeFile = dirname + path.sep + path.basename(file, '.ts') + '.md';
let finalPath = '';
if (FileEngine.existsSync(readmeFile)) {
finalPath = readmeFile;
} else {
finalPath = readmeAlternativeFile;
}
return finalPath;
}
/**
* Checks if any of the markdown files is exists with or without endings
*/
public hasRootMarkdowns(): boolean {
return this.addEndings(this.markdownFiles).some(x =>
FileEngine.existsSync(process.cwd() + path.sep + x)
);
}
public listRootMarkdowns(): string[] {
let foundFiles = this.markdownFiles.filter(
x =>
FileEngine.existsSync(process.cwd() + path.sep + x + '.md') ||
FileEngine.existsSync(process.cwd() + path.sep + x)
);
return this.addEndings(foundFiles);
}
private escape(html: string): string {
return html
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/@/g, '@');
}
private renderMarkdown(markdown: string): string {
return this.markedInstance(this.normalizeBitbucketCommitLinks(markdown));
}
private normalizeBitbucketCommitLinks(markdown: string): string {
return markdown.replace(
/(https?:\/\/bitbucket\.org\/[^\s)\]>]+\/)commit\/([^\s)\]>]+)/g,
'$1commits/$2'
);
}
/**
* ['README'] => ['README', 'README.md']
*/
private addEndings(files: Array<string>): Array<string> {
return _.flatMap(files, x => [x, x + '.md']);
}
}
export default MarkdownEngine.getInstance();