UNPKG

7 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 * 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};