248 lines
6.0 KiB
TypeScript
248 lines
6.0 KiB
TypeScript
/**
|
|
* @license
|
|
*
|
|
* Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
|
|
* https://github.com/chjj/marked
|
|
*
|
|
* Copyright (c) 2018, Костя Третяк. (MIT Licensed)
|
|
* https://github.com/ts-stack/markdown
|
|
*/
|
|
|
|
import { InlineLexer } from "./inline-lexer.ts";
|
|
import {
|
|
Links,
|
|
MarkedOptions,
|
|
SimpleRenderer,
|
|
Token,
|
|
TokenType,
|
|
} from "./interfaces.ts";
|
|
import { Marked } from "./marked.ts";
|
|
import { Renderer } from "./renderer.ts";
|
|
|
|
/**
|
|
* Parsing & Compiling.
|
|
*/
|
|
export class Parser {
|
|
simpleRenderers: SimpleRenderer[] = [];
|
|
protected tokens: Token[];
|
|
protected token: Token | undefined;
|
|
protected inlineLexer!: InlineLexer;
|
|
protected options: MarkedOptions;
|
|
protected renderer: Renderer;
|
|
protected line: number = 0;
|
|
|
|
constructor(options?: MarkedOptions) {
|
|
this.tokens = [];
|
|
this.token = undefined;
|
|
this.options = options || Marked.options;
|
|
this.renderer = this.options.renderer || new Renderer(this.options);
|
|
}
|
|
|
|
static parse(tokens: Token[], links: Links, options?: MarkedOptions): string {
|
|
const parser = new this(options);
|
|
return parser.parse(links, tokens);
|
|
}
|
|
|
|
parse(links: Links, tokens: Token[]) {
|
|
this.inlineLexer = new InlineLexer(
|
|
InlineLexer,
|
|
links,
|
|
this.options,
|
|
this.renderer,
|
|
);
|
|
this.tokens = tokens.reverse();
|
|
|
|
let out = "";
|
|
|
|
while (this.next()) {
|
|
out += this.tok();
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
debug(links: Links, tokens: Token[]) {
|
|
this.inlineLexer = new InlineLexer(
|
|
InlineLexer,
|
|
links,
|
|
this.options,
|
|
this.renderer,
|
|
);
|
|
this.tokens = tokens.reverse();
|
|
|
|
let out = "";
|
|
|
|
while (this.next()) {
|
|
const outToken: string = this.tok() || "";
|
|
if (!this.token) throw ReferenceError;
|
|
this.token.line = this.line += outToken.split("\n").length - 1;
|
|
out += outToken;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
protected next() {
|
|
return (this.token = this.tokens.pop());
|
|
}
|
|
|
|
protected getNextElement() {
|
|
return this.tokens[this.tokens.length - 1];
|
|
}
|
|
|
|
protected parseText() {
|
|
if (!this.token) throw ReferenceError;
|
|
let body = this.token.text;
|
|
let nextElement: Token;
|
|
|
|
while (
|
|
(nextElement = this.getNextElement()) &&
|
|
nextElement.type == TokenType.text
|
|
) {
|
|
body += "\n" + this.next()?.text;
|
|
}
|
|
|
|
return this.inlineLexer.output(body || "");
|
|
}
|
|
|
|
protected tok() {
|
|
if (!this.token) throw ReferenceError;
|
|
switch (this.token.type) {
|
|
case TokenType.space: {
|
|
return "";
|
|
}
|
|
case TokenType.paragraph: {
|
|
return this.renderer.paragraph(
|
|
this.inlineLexer.output(this.token.text || ""),
|
|
);
|
|
}
|
|
case TokenType.text: {
|
|
if (this.options.isNoP) {
|
|
return this.parseText();
|
|
} else {
|
|
return this.renderer.paragraph(this.parseText());
|
|
}
|
|
}
|
|
case TokenType.heading: {
|
|
return this.renderer.heading(
|
|
this.inlineLexer.output(this.token.text || ""),
|
|
this.token.depth || 0,
|
|
this.token.text || "",
|
|
);
|
|
}
|
|
case TokenType.listStart: {
|
|
let body = "";
|
|
const ordered = this.token.ordered;
|
|
|
|
while (this.next()?.type != TokenType.listEnd) {
|
|
body += this.tok();
|
|
}
|
|
|
|
return this.renderer.list(body, ordered);
|
|
}
|
|
case TokenType.listItemStart: {
|
|
let body = "";
|
|
|
|
while (this.next()?.type != TokenType.listItemEnd) {
|
|
body += this.token.type == (TokenType.text as any)
|
|
? this.parseText()
|
|
: this.tok();
|
|
}
|
|
|
|
return this.renderer.listitem(body);
|
|
}
|
|
case TokenType.looseItemStart: {
|
|
let body = "";
|
|
|
|
while (this.next()?.type != TokenType.listItemEnd) {
|
|
body += this.tok();
|
|
}
|
|
|
|
return this.renderer.listitem(body);
|
|
}
|
|
case TokenType.code: {
|
|
return this.renderer.code(
|
|
this.token.text || "",
|
|
this.token.lang,
|
|
this.token.escaped,
|
|
);
|
|
}
|
|
case TokenType.table: {
|
|
let header = "";
|
|
let body = "";
|
|
let cell;
|
|
|
|
if (
|
|
!this.token || !this.token.header || !this.token.align ||
|
|
!this.token.cells
|
|
) {
|
|
throw ReferenceError;
|
|
}
|
|
// header
|
|
cell = "";
|
|
for (let i = 0; i < this.token.header.length; i++) {
|
|
const flags = { header: true, align: this.token.align[i] };
|
|
const out = this.inlineLexer.output(this.token.header[i]);
|
|
|
|
cell += this.renderer.tablecell(out, flags);
|
|
}
|
|
|
|
header += this.renderer.tablerow(cell);
|
|
|
|
for (const row of this.token.cells) {
|
|
cell = "";
|
|
|
|
for (let j = 0; j < row.length; j++) {
|
|
cell += this.renderer.tablecell(this.inlineLexer.output(row[j]), {
|
|
header: false,
|
|
align: this.token.align[j],
|
|
});
|
|
}
|
|
|
|
body += this.renderer.tablerow(cell);
|
|
}
|
|
|
|
return this.renderer.table(header, body);
|
|
}
|
|
case TokenType.blockquoteStart: {
|
|
let body = "";
|
|
|
|
while (this.next()?.type != TokenType.blockquoteEnd) {
|
|
body += this.tok();
|
|
}
|
|
|
|
return this.renderer.blockquote(body);
|
|
}
|
|
case TokenType.hr: {
|
|
return this.renderer.hr();
|
|
}
|
|
case TokenType.html: {
|
|
const html = !this.token.pre && !this.options.pedantic
|
|
? this.inlineLexer.output(this.token.text || "")
|
|
: this.token.text;
|
|
return this.renderer.html(html || "");
|
|
}
|
|
default: {
|
|
if (this.simpleRenderers.length) {
|
|
for (let i = 0; i < this.simpleRenderers.length; i++) {
|
|
if (this.token.type == "simpleRule" + (i + 1)) {
|
|
return this.simpleRenderers[i].call(
|
|
this.renderer,
|
|
this.token.execArr,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
const errMsg = `Token with "${this.token.type}" type was not found.`;
|
|
|
|
if (this.options.silent) {
|
|
console.log(errMsg);
|
|
} else {
|
|
throw new Error(errMsg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|