UNPKG

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