ds-algo-study
Version:
Just experimenting with publishing a package
207 lines (185 loc) • 5.12 kB
JavaScript
const Renderer = require('./Renderer.js');
const Slugger = require('./Slugger.js');
const InlineLexer = require('./InlineLexer.js');
const TextRenderer = require('./TextRenderer.js');
const { defaults } = require('./defaults.js');
const {
merge,
unescape
} = require('./helpers.js');
/**
* Parsing & Compiling
*/
module.exports = class Parser {
constructor(options) {
this.tokens = [];
this.token = null;
this.options = options || defaults;
this.options.renderer = this.options.renderer || new Renderer();
this.renderer = this.options.renderer;
this.renderer.options = this.options;
this.slugger = new Slugger();
}
/**
* Static Parse Method
*/
static parse(tokens, options) {
const parser = new Parser(options);
return parser.parse(tokens);
};
/**
* Parse Loop
*/
parse(tokens) {
this.inline = new InlineLexer(tokens.links, this.options);
// use an InlineLexer with a TextRenderer to extract pure text
this.inlineText = new InlineLexer(
tokens.links,
merge({}, this.options, { renderer: new TextRenderer() })
);
this.tokens = tokens.reverse();
let out = '';
while (this.next()) {
out += this.tok();
}
return out;
};
/**
* Next Token
*/
next() {
this.token = this.tokens.pop();
return this.token;
};
/**
* Preview Next Token
*/
peek() {
return this.tokens[this.tokens.length - 1] || 0;
};
/**
* Parse Text Tokens
*/
parseText() {
let body = this.token.text;
while (this.peek().type === 'text') {
body += '\n' + this.next().text;
}
return this.inline.output(body);
};
/**
* Parse Current Token
*/
tok() {
let body = '';
switch (this.token.type) {
case 'space': {
return '';
}
case 'hr': {
return this.renderer.hr();
}
case 'heading': {
return this.renderer.heading(
this.inline.output(this.token.text),
this.token.depth,
unescape(this.inlineText.output(this.token.text)),
this.slugger);
}
case 'code': {
return this.renderer.code(this.token.text,
this.token.lang,
this.token.escaped);
}
case 'table': {
let header = '',
i,
row,
cell,
j;
// header
cell = '';
for (i = 0; i < this.token.header.length; i++) {
cell += this.renderer.tablecell(
this.inline.output(this.token.header[i]),
{ header: true, align: this.token.align[i] }
);
}
header += this.renderer.tablerow(cell);
for (i = 0; i < this.token.cells.length; i++) {
row = this.token.cells[i];
cell = '';
for (j = 0; j < row.length; j++) {
cell += this.renderer.tablecell(
this.inline.output(row[j]),
{ header: false, align: this.token.align[j] }
);
}
body += this.renderer.tablerow(cell);
}
return this.renderer.table(header, body);
}
case 'blockquote_start': {
body = '';
while (this.next().type !== 'blockquote_end') {
body += this.tok();
}
return this.renderer.blockquote(body);
}
case 'list_start': {
body = '';
const ordered = this.token.ordered,
start = this.token.start;
while (this.next().type !== 'list_end') {
body += this.tok();
}
return this.renderer.list(body, ordered, start);
}
case 'list_item_start': {
body = '';
const loose = this.token.loose;
const checked = this.token.checked;
const task = this.token.task;
if (this.token.task) {
if (loose) {
if (this.peek().type === 'text') {
const nextToken = this.peek();
nextToken.text = this.renderer.checkbox(checked) + ' ' + nextToken.text;
} else {
this.tokens.push({
type: 'text',
text: this.renderer.checkbox(checked)
});
}
} else {
body += this.renderer.checkbox(checked);
}
}
while (this.next().type !== 'list_item_end') {
body += !loose && this.token.type === 'text'
? this.parseText()
: this.tok();
}
return this.renderer.listitem(body, task, checked);
}
case 'html': {
// TODO parse inline content if parameter markdown=1
return this.renderer.html(this.token.text);
}
case 'paragraph': {
return this.renderer.paragraph(this.inline.output(this.token.text));
}
case 'text': {
return this.renderer.paragraph(this.parseText());
}
default: {
const errMsg = 'Token with "' + this.token.type + '" type was not found.';
if (this.options.silent) {
console.log(errMsg);
} else {
throw new Error(errMsg);
}
}
}
};
};