markdown-toc-gen
Version:
Generating and updating table of contents in Markdown files which conform with prettier.
138 lines (137 loc) • 5.33 kB
JavaScript
"use strict";
/* eslint-disable no-control-regex */
Object.defineProperty(exports, "__esModule", { value: true });
exports.Toc = void 0;
const markdown_1 = require("../markdown");
const toc_service_1 = require("./toc.service");
/**
* Toc
*
* class to parse and insert/update markdown files with
* a generated table of content
*
* To create a toc it is necessary that the markdown file
* has the following placeholder
*
* <!-- toc -->
* <!-- tocstop -->
*/
class Toc {
/** file path of markdown file */
get filePath() {
return this._mdPath;
}
/** set file path of markdown file */
set filePath(path) {
this._mdPath = path;
const mdString = this.mdService.parseMarkdown(path);
this._isWindows = mdString.includes(this._carriageReturn);
this._mdString = mdString.replace(new RegExp(this._carriageReturn, 'g'), this._lineFeed);
}
/**
* set max depth for parsing headings
*/
set maxDepth(value) {
this.mdService.setMaxDepth(value);
}
/**
* returns carriage return for windows os and linefeed for unix systems
*/
get newLineChar() {
return this._isWindows ? this._carriageReturn : this._lineFeed;
}
constructor(mdService = new markdown_1.MarkdownService(), tocService = new toc_service_1.TocService()) {
this.mdService = mdService;
this.tocService = tocService;
this._isWindows = false;
this._mdPath = '';
this._mdString = '';
this._tocPlaceholder = new RegExp('(?<toc>(<!--\\s?toc\\s?-->\n?(?<tocInline>(?:.*?\n)*?)<!--\\s?tocstop\\s?-->\n))');
this._tocStart = '<!-- toc -->\n';
this._tocStop = '<!-- tocstop -->\n';
this._indentation = ' ';
this._hyphen = '- ';
this._carriageReturn = '\r\n';
this._lineFeed = '\n';
}
/**
* create table of content
* @returns table of content
*/
createToc() {
let toc = '';
const cleandedContent = this.mdService.removeCodeBlocks(this._mdString);
this.mdService.parseHeadings(cleandedContent).forEach((heading) => {
toc +=
this._indentation.repeat(heading.level - 1) +
this._hyphen +
this.createTocEntry(heading.heading, heading.counter) +
this._lineFeed;
});
return toc;
}
/**
* insert/update toc in given markdown file
*/
insertToc() {
const tocMatch = this._tocPlaceholder.exec(this._mdString);
const placeholderMatch = new RegExp('<!--\\s?(toc|tocstop)\\s?-->').exec(this._mdString);
if (tocMatch && tocMatch.groups && tocMatch.groups.toc) {
const mdWithToc = this._mdString.replace(tocMatch.groups.toc, this._tocStart + '\n' + this.createToc() + '\n' + this._tocStop);
this.mdService.updateMarkdown(this.filePath, mdWithToc.replace(new RegExp(this._lineFeed, 'g'), this.newLineChar));
return;
}
else if (placeholderMatch) {
throw new Error([
'Could not find placeholder',
'<!-- toc -->',
'<!-- tocstop -->',
'A toc update or insertion was not possible. Please sure the placeholder are set.',
].join(this.newLineChar));
}
else {
const toc = this._tocStart + '\n' + this.createToc() + '\n' + this._tocStop + '\n';
const posFirstSecondHeading = this._mdString.search(new RegExp('\n##\\s'));
if (posFirstSecondHeading === -1) {
throw new Error([
'Could not find placeholder',
'<!-- toc -->',
'<!-- tocstop -->',
'or there is an semantic issue in your heading level.',
'A toc insertion was not possible. Please sure the placeholders are set or your semantic is correct',
].join(this.newLineChar));
}
const replacedContent = (this._mdString.slice(0, posFirstSecondHeading + 1) +
toc +
this._mdString.slice(posFirstSecondHeading + 1)).replace(new RegExp(this._lineFeed, 'g'), this.newLineChar);
this.mdService.updateMarkdown(this.filePath, replacedContent);
return;
}
}
/**
* evaluates if given toc is valid
* the given toc will be compared with the generated toc
* @returns is table of content valid
*/
isTocValid() {
const parsedToc = this.tocService.parseToc(this.mdService.removeCodeBlocks(this._mdString));
const expectedToc = this.createToc();
const tocDiff = this.tocService.validateToc(parsedToc, expectedToc);
if (tocDiff) {
console.log(`validation of ${this.filePath} failed:`);
console.log(tocDiff);
return false;
}
return true;
}
/**
* create table of content entry with creating unique id
* @param caption - headline which is a toc entry
* @param counter - counts the amount of a given headline to create an unique id
* @returns toc entry
*/
createTocEntry(caption, counter) {
return `[${caption}]${this.tocService.createLink(caption, counter)}`;
}
}
exports.Toc = Toc;