l0bsterssg

node.js static responsive blog post generator
Log | Files | Refs | README

Parser.js (7000B)


      1 const Renderer = require('./Renderer.js');
      2 const TextRenderer = require('./TextRenderer.js');
      3 const Slugger = require('./Slugger.js');
      4 const { defaults } = require('./defaults.js');
      5 const {
      6   unescape
      7 } = require('./helpers.js');
      8 
      9 /**
     10  * Parsing & Compiling
     11  */
     12 module.exports = class Parser {
     13   constructor(options) {
     14     this.options = options || defaults;
     15     this.options.renderer = this.options.renderer || new Renderer();
     16     this.renderer = this.options.renderer;
     17     this.renderer.options = this.options;
     18     this.textRenderer = new TextRenderer();
     19     this.slugger = new Slugger();
     20   }
     21 
     22   /**
     23    * Static Parse Method
     24    */
     25   static parse(tokens, options) {
     26     const parser = new Parser(options);
     27     return parser.parse(tokens);
     28   }
     29 
     30   /**
     31    * Static Parse Inline Method
     32    */
     33   static parseInline(tokens, options) {
     34     const parser = new Parser(options);
     35     return parser.parseInline(tokens);
     36   }
     37 
     38   /**
     39    * Parse Loop
     40    */
     41   parse(tokens, top = true) {
     42     let out = '',
     43       i,
     44       j,
     45       k,
     46       l2,
     47       l3,
     48       row,
     49       cell,
     50       header,
     51       body,
     52       token,
     53       ordered,
     54       start,
     55       loose,
     56       itemBody,
     57       item,
     58       checked,
     59       task,
     60       checkbox;
     61 
     62     const l = tokens.length;
     63     for (i = 0; i < l; i++) {
     64       token = tokens[i];
     65       switch (token.type) {
     66         case 'space': {
     67           continue;
     68         }
     69         case 'hr': {
     70           out += this.renderer.hr();
     71           continue;
     72         }
     73         case 'heading': {
     74           out += this.renderer.heading(
     75             this.parseInline(token.tokens),
     76             token.depth,
     77             unescape(this.parseInline(token.tokens, this.textRenderer)),
     78             this.slugger);
     79           continue;
     80         }
     81         case 'code': {
     82           out += this.renderer.code(token.text,
     83             token.lang,
     84             token.escaped);
     85           continue;
     86         }
     87         case 'table': {
     88           header = '';
     89 
     90           // header
     91           cell = '';
     92           l2 = token.header.length;
     93           for (j = 0; j < l2; j++) {
     94             cell += this.renderer.tablecell(
     95               this.parseInline(token.tokens.header[j]),
     96               { header: true, align: token.align[j] }
     97             );
     98           }
     99           header += this.renderer.tablerow(cell);
    100 
    101           body = '';
    102           l2 = token.cells.length;
    103           for (j = 0; j < l2; j++) {
    104             row = token.tokens.cells[j];
    105 
    106             cell = '';
    107             l3 = row.length;
    108             for (k = 0; k < l3; k++) {
    109               cell += this.renderer.tablecell(
    110                 this.parseInline(row[k]),
    111                 { header: false, align: token.align[k] }
    112               );
    113             }
    114 
    115             body += this.renderer.tablerow(cell);
    116           }
    117           out += this.renderer.table(header, body);
    118           continue;
    119         }
    120         case 'blockquote': {
    121           body = this.parse(token.tokens);
    122           out += this.renderer.blockquote(body);
    123           continue;
    124         }
    125         case 'list': {
    126           ordered = token.ordered;
    127           start = token.start;
    128           loose = token.loose;
    129           l2 = token.items.length;
    130 
    131           body = '';
    132           for (j = 0; j < l2; j++) {
    133             item = token.items[j];
    134             checked = item.checked;
    135             task = item.task;
    136 
    137             itemBody = '';
    138             if (item.task) {
    139               checkbox = this.renderer.checkbox(checked);
    140               if (loose) {
    141                 if (item.tokens.length > 0 && item.tokens[0].type === 'text') {
    142                   item.tokens[0].text = checkbox + ' ' + item.tokens[0].text;
    143                   if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') {
    144                     item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text;
    145                   }
    146                 } else {
    147                   item.tokens.unshift({
    148                     type: 'text',
    149                     text: checkbox
    150                   });
    151                 }
    152               } else {
    153                 itemBody += checkbox;
    154               }
    155             }
    156 
    157             itemBody += this.parse(item.tokens, loose);
    158             body += this.renderer.listitem(itemBody, task, checked);
    159           }
    160 
    161           out += this.renderer.list(body, ordered, start);
    162           continue;
    163         }
    164         case 'html': {
    165           // TODO parse inline content if parameter markdown=1
    166           out += this.renderer.html(token.text);
    167           continue;
    168         }
    169         case 'paragraph': {
    170           out += this.renderer.paragraph(this.parseInline(token.tokens));
    171           continue;
    172         }
    173         case 'text': {
    174           body = token.tokens ? this.parseInline(token.tokens) : token.text;
    175           while (i + 1 < l && tokens[i + 1].type === 'text') {
    176             token = tokens[++i];
    177             body += '\n' + (token.tokens ? this.parseInline(token.tokens) : token.text);
    178           }
    179           out += top ? this.renderer.paragraph(body) : body;
    180           continue;
    181         }
    182         default: {
    183           const errMsg = 'Token with "' + token.type + '" type was not found.';
    184           if (this.options.silent) {
    185             console.error(errMsg);
    186             return;
    187           } else {
    188             throw new Error(errMsg);
    189           }
    190         }
    191       }
    192     }
    193 
    194     return out;
    195   }
    196 
    197   /**
    198    * Parse Inline Tokens
    199    */
    200   parseInline(tokens, renderer) {
    201     renderer = renderer || this.renderer;
    202     let out = '',
    203       i,
    204       token;
    205 
    206     const l = tokens.length;
    207     for (i = 0; i < l; i++) {
    208       token = tokens[i];
    209       switch (token.type) {
    210         case 'escape': {
    211           out += renderer.text(token.text);
    212           break;
    213         }
    214         case 'html': {
    215           out += renderer.html(token.text);
    216           break;
    217         }
    218         case 'link': {
    219           out += renderer.link(token.href, token.title, this.parseInline(token.tokens, renderer));
    220           break;
    221         }
    222         case 'image': {
    223           out += renderer.image(token.href, token.title, token.text);
    224           break;
    225         }
    226         case 'strong': {
    227           out += renderer.strong(this.parseInline(token.tokens, renderer));
    228           break;
    229         }
    230         case 'em': {
    231           out += renderer.em(this.parseInline(token.tokens, renderer));
    232           break;
    233         }
    234         case 'codespan': {
    235           out += renderer.codespan(token.text);
    236           break;
    237         }
    238         case 'br': {
    239           out += renderer.br();
    240           break;
    241         }
    242         case 'del': {
    243           out += renderer.del(this.parseInline(token.tokens, renderer));
    244           break;
    245         }
    246         case 'text': {
    247           out += renderer.text(token.text);
    248           break;
    249         }
    250         default: {
    251           const errMsg = 'Token with "' + token.type + '" type was not found.';
    252           if (this.options.silent) {
    253             console.error(errMsg);
    254             return;
    255           } else {
    256             throw new Error(errMsg);
    257           }
    258         }
    259       }
    260     }
    261     return out;
    262   }
    263 };