marked/src/Parser.ts
import { _Renderer } from './Renderer.ts';
import { _TextRenderer } from './TextRenderer.ts';
import { _defaults } from './defaults.ts';
import type { MarkedToken, Token, Tokens } from './Tokens.ts';
import type { MarkedOptions } from './MarkedOptions.ts';
/**
* Parsing & Compiling
*/
export class _Parser<ParserOutput = string, RendererOutput = string> {
options: MarkedOptions<ParserOutput, RendererOutput>;
renderer: _Renderer<ParserOutput, RendererOutput>;
textRenderer: _TextRenderer<RendererOutput>;
constructor(options?: MarkedOptions<ParserOutput, RendererOutput>) {
this.options = options || _defaults;
this.options.renderer = this.options.renderer || new _Renderer<ParserOutput, RendererOutput>();
this.renderer = this.options.renderer;
this.renderer.options = this.options;
this.renderer.parser = this;
this.textRenderer = new _TextRenderer<RendererOutput>();
}
/**
* Static Parse Method
*/
static parse<ParserOutput = string, RendererOutput = string>(tokens: Token[], options?: MarkedOptions<ParserOutput, RendererOutput>) {
const parser = new _Parser<ParserOutput, RendererOutput>(options);
return parser.parse(tokens);
}
/**
* Static Parse Inline Method
*/
static parseInline<ParserOutput = string, RendererOutput = string>(tokens: Token[], options?: MarkedOptions<ParserOutput, RendererOutput>) {
const parser = new _Parser<ParserOutput, RendererOutput>(options);
return parser.parseInline(tokens);
}
/**
* Parse Loop
*/
parse(tokens: Token[]): ParserOutput {
this.renderer.parser = this;
let out = '';
for (let i = 0; i < tokens.length; i++) {
const anyToken = tokens[i];
// Run any renderer extensions
if (this.options.extensions?.renderers?.[anyToken.type]) {
const genericToken = anyToken as Tokens.Generic;
const ret = this.options.extensions.renderers[genericToken.type].call({ parser: this }, genericToken);
if (ret !== false || !['space', 'hr', 'heading', 'code', 'table', 'blockquote', 'list', 'checkbox', 'html', 'def', 'paragraph', 'text'].includes(genericToken.type)) {
out += ret || '';
continue;
}
}
const token = anyToken as MarkedToken;
switch (token.type) {
case 'space': {
out += this.renderer.space(token);
break;
}
case 'hr': {
out += this.renderer.hr(token);
break;
}
case 'heading': {
out += this.renderer.heading(token);
break;
}
case 'code': {
out += this.renderer.code(token);
break;
}
case 'table': {
out += this.renderer.table(token);
break;
}
case 'blockquote': {
out += this.renderer.blockquote(token);
break;
}
case 'list': {
out += this.renderer.list(token);
break;
}
case 'checkbox': {
out += this.renderer.checkbox(token);
break;
}
case 'html': {
out += this.renderer.html(token);
break;
}
case 'def': {
out += this.renderer.def(token);
break;
}
case 'paragraph': {
out += this.renderer.paragraph(token);
break;
}
case 'text': {
out += this.renderer.text(token);
break;
}
default: {
const errMsg = 'Token with "' + token.type + '" type was not found.';
if (this.options.silent) {
console.error(errMsg);
return '' as ParserOutput;
} else {
throw new Error(errMsg);
}
}
}
}
return out as ParserOutput;
}
/**
* Parse Inline Tokens
*/
parseInline(tokens: Token[], renderer: _Renderer<ParserOutput, RendererOutput> | _TextRenderer<RendererOutput> = this.renderer): ParserOutput {
this.renderer.parser = this;
let out = '';
for (let i = 0; i < tokens.length; i++) {
const anyToken = tokens[i];
// Run any renderer extensions
if (this.options.extensions?.renderers?.[anyToken.type]) {
const ret = this.options.extensions.renderers[anyToken.type].call({ parser: this }, anyToken);
if (ret !== false || !['escape', 'html', 'link', 'image', 'checkbox', 'strong', 'em', 'codespan', 'br', 'del', 'text'].includes(anyToken.type)) {
out += ret || '';
continue;
}
}
const token = anyToken as MarkedToken;
switch (token.type) {
case 'escape': {
out += renderer.text(token);
break;
}
case 'html': {
out += renderer.html(token);
break;
}
case 'link': {
out += renderer.link(token);
break;
}
case 'image': {
out += renderer.image(token);
break;
}
case 'checkbox': {
out += renderer.checkbox(token);
break;
}
case 'strong': {
out += renderer.strong(token);
break;
}
case 'em': {
out += renderer.em(token);
break;
}
case 'codespan': {
out += renderer.codespan(token);
break;
}
case 'br': {
out += renderer.br(token);
break;
}
case 'del': {
out += renderer.del(token);
break;
}
case 'text': {
out += renderer.text(token);
break;
}
default: {
const errMsg = 'Token with "' + token.type + '" type was not found.';
if (this.options.silent) {
console.error(errMsg);
return '' as ParserOutput;
} else {
throw new Error(errMsg);
}
}
}
}
return out as ParserOutput;
}
}