@connectv/marked
Version:
component-based markdown renderer
94 lines • 4.43 kB
JavaScript
import { InlineLexer, TextRenderer, Slugger } from 'marked';
import { InlineProcessor } from './inline';
import { fill } from './defaults';
import { unescape } from './util/unescape';
export class Parser {
constructor(options) {
this.tokens = [];
this.slugger = new Slugger();
this.options = fill(options || {});
}
parse(tokens, renderer) {
this.renderer = renderer;
this.inline = new InlineProcessor(tokens, renderer, this.options);
this.inlineText = new InlineLexer(tokens.links, {
renderer: new TextRenderer()
});
this.tokens = tokens.reverse();
const res = renderer.create("fragment", null);
while (this.next())
renderer.render(this.tok()).on(res);
return res;
}
next() {
const _next = this.tokens.pop();
if (_next)
this.token = _next;
return _next;
}
safeNext() {
const _next = this.next();
if (!_next) /* istanbul ignore next */
throw new Error('Unexpected end of stream');
return _next;
}
peek() {
return this.tokens[this.tokens.length - 1];
}
parseText() {
let body = this.token.text || '';
while (this.peek().type === 'text') /* istanbul ignore next */
body += '\n' + this.next().text;
return this.inline.process(body);
}
tok() {
const renderer = this.renderer;
switch (this.token.type) {
case 'space': return this.options.Space ? renderer.create(this.options.Space, null) : renderer.create("fragment", null);
case 'hr': return renderer.create(this.options.Hr, null);
case 'heading':
const slug = this.slugger.slug(unescape(this.inlineText.output(this.token.text)));
return renderer.create(this.options.Heading, { depth: this.token.depth, slug: slug }, this.inline.process(this.token.text));
case 'code':
return renderer.create(this.options.Code, { lang: this.token.lang || '' }, this.token.text);
case 'table': {
const header = this.token.header.map((cell, i) => renderer.create(this.options.TableHeaderCell, { align: this.token.align[i] }, this.inline.process(cell)));
const body = this.token.cells.map(row => renderer.create(this.options.TableRow, null, row.map((cell, j) => renderer.create(this.options.TableCell, { align: this.token.align[j] }, this.inline.process(cell)))));
return renderer.create(this.options.Table, { header: header, body: body });
}
case 'blockquote_start': {
const body = renderer.create("fragment", null);
while (this.safeNext().type !== 'blockquote_end')
renderer.render(this.tok()).on(body);
return renderer.create(this.options.BlockQuote, null, body);
}
case 'list_start': {
const body = renderer.create("fragment", null);
const ordered = this.token.ordered;
while (this.safeNext().type !== 'list_end')
renderer.render(this.tok()).on(body);
return renderer.create(this.options.List, { ordered: ordered }, body);
}
case 'list_item_start': {
const body = renderer.create("fragment", null);
const loose = this.token.loose;
while (this.safeNext().type !== 'list_item_end') {
if (!loose && this.token.type === 'text')
renderer.render(renderer.create("fragment", null, this.parseText())).on(body);
else
renderer.render(this.tok()).on(body);
}
return renderer.create(this.options.ListItem, null, body);
}
case 'html':
return renderer.create(this.options.Html, { content: this.token.text });
case 'paragraph':
return renderer.create(this.options.Paragraph, null, this.inline.process(this.token.text));
case 'text':
return renderer.create(this.options.Paragraph, null, this.parseText());
default: /* istanbul ignore next */
throw new Error('Unrecognized Token:: ' + this.token.type);
}
}
}
//# sourceMappingURL=parser.js.map