marked 0.8.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/Parser.js CHANGED
@@ -1,10 +1,8 @@
1
1
  const Renderer = require('./Renderer.js');
2
- const Slugger = require('./Slugger.js');
3
- const InlineLexer = require('./InlineLexer.js');
4
2
  const TextRenderer = require('./TextRenderer.js');
3
+ const Slugger = require('./Slugger.js');
5
4
  const { defaults } = require('./defaults.js');
6
5
  const {
7
- merge,
8
6
  unescape
9
7
  } = require('./helpers.js');
10
8
 
@@ -13,12 +11,11 @@ const {
13
11
  */
14
12
  module.exports = class Parser {
15
13
  constructor(options) {
16
- this.tokens = [];
17
- this.token = null;
18
14
  this.options = options || defaults;
19
15
  this.options.renderer = this.options.renderer || new Renderer();
20
16
  this.renderer = this.options.renderer;
21
17
  this.renderer.options = this.options;
18
+ this.textRenderer = new TextRenderer();
22
19
  this.slugger = new Slugger();
23
20
  }
24
21
 
@@ -28,179 +25,231 @@ module.exports = class Parser {
28
25
  static parse(tokens, options) {
29
26
  const parser = new Parser(options);
30
27
  return parser.parse(tokens);
31
- };
28
+ }
32
29
 
33
30
  /**
34
31
  * Parse Loop
35
32
  */
36
- parse(tokens) {
37
- this.inline = new InlineLexer(tokens.links, this.options);
38
- // use an InlineLexer with a TextRenderer to extract pure text
39
- this.inlineText = new InlineLexer(
40
- tokens.links,
41
- merge({}, this.options, { renderer: new TextRenderer() })
42
- );
43
- this.tokens = tokens.reverse();
44
-
45
- let out = '';
46
- while (this.next()) {
47
- out += this.tok();
48
- }
49
-
50
- return out;
51
- };
52
-
53
- /**
54
- * Next Token
55
- */
56
- next() {
57
- this.token = this.tokens.pop();
58
- return this.token;
59
- };
60
-
61
- /**
62
- * Preview Next Token
63
- */
64
- peek() {
65
- return this.tokens[this.tokens.length - 1] || 0;
66
- };
67
-
68
- /**
69
- * Parse Text Tokens
70
- */
71
- parseText() {
72
- let body = this.token.text;
73
-
74
- while (this.peek().type === 'text') {
75
- body += '\n' + this.next().text;
76
- }
77
-
78
- return this.inline.output(body);
79
- };
80
-
81
- /**
82
- * Parse Current Token
83
- */
84
- tok() {
85
- let body = '';
86
- switch (this.token.type) {
87
- case 'space': {
88
- return '';
89
- }
90
- case 'hr': {
91
- return this.renderer.hr();
92
- }
93
- case 'heading': {
94
- return this.renderer.heading(
95
- this.inline.output(this.token.text),
96
- this.token.depth,
97
- unescape(this.inlineText.output(this.token.text)),
98
- this.slugger);
99
- }
100
- case 'code': {
101
- return this.renderer.code(this.token.text,
102
- this.token.lang,
103
- this.token.escaped);
104
- }
105
- case 'table': {
106
- let header = '',
107
- i,
108
- row,
109
- cell,
110
- j;
111
-
112
- // header
113
- cell = '';
114
- for (i = 0; i < this.token.header.length; i++) {
115
- cell += this.renderer.tablecell(
116
- this.inline.output(this.token.header[i]),
117
- { header: true, align: this.token.align[i] }
118
- );
119
- }
120
- header += this.renderer.tablerow(cell);
121
-
122
- for (i = 0; i < this.token.cells.length; i++) {
123
- row = this.token.cells[i];
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 = '';
124
81
 
82
+ // header
125
83
  cell = '';
126
- for (j = 0; j < row.length; j++) {
84
+ l2 = token.header.length;
85
+ for (j = 0; j < l2; j++) {
127
86
  cell += this.renderer.tablecell(
128
- this.inline.output(row[j]),
129
- { header: false, align: this.token.align[j] }
87
+ this.parseInline(token.tokens.header[j]),
88
+ { header: true, align: token.align[j] }
130
89
  );
131
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
+ }
132
106
 
133
- body += this.renderer.tablerow(cell);
107
+ body += this.renderer.tablerow(cell);
108
+ }
109
+ out += this.renderer.table(header, body);
110
+ continue;
134
111
  }
135
- return this.renderer.table(header, body);
136
- }
137
- case 'blockquote_start': {
138
- body = '';
139
-
140
- while (this.next().type !== 'blockquote_end') {
141
- body += this.tok();
112
+ case 'blockquote': {
113
+ body = this.parse(token.tokens);
114
+ out += this.renderer.blockquote(body);
115
+ continue;
142
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
+ }
143
148
 
144
- return this.renderer.blockquote(body);
145
- }
146
- case 'list_start': {
147
- body = '';
148
- const ordered = this.token.ordered,
149
- start = this.token.start;
149
+ itemBody += this.parse(item.tokens, loose);
150
+ body += this.renderer.listitem(itemBody, task, checked);
151
+ }
150
152
 
151
- while (this.next().type !== 'list_end') {
152
- body += this.tok();
153
+ out += this.renderer.list(body, ordered, start);
154
+ continue;
153
155
  }
154
-
155
- return this.renderer.list(body, ordered, start);
156
- }
157
- case 'list_item_start': {
158
- body = '';
159
- const loose = this.token.loose;
160
- const checked = this.token.checked;
161
- const task = this.token.task;
162
-
163
- if (this.token.task) {
164
- if (loose) {
165
- if (this.peek().type === 'text') {
166
- const nextToken = this.peek();
167
- nextToken.text = this.renderer.checkbox(checked) + ' ' + nextToken.text;
168
- } else {
169
- this.tokens.push({
170
- type: 'text',
171
- text: this.renderer.checkbox(checked)
172
- });
173
- }
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;
174
179
  } else {
175
- body += this.renderer.checkbox(checked);
180
+ throw new Error(errMsg);
176
181
  }
177
182
  }
183
+ }
184
+ }
178
185
 
179
- while (this.next().type !== 'list_item_end') {
180
- body += !loose && this.token.type === 'text'
181
- ? this.parseText()
182
- : this.tok();
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;
183
205
  }
184
- return this.renderer.listitem(body, task, checked);
185
- }
186
- case 'html': {
187
- // TODO parse inline content if parameter markdown=1
188
- return this.renderer.html(this.token.text);
189
- }
190
- case 'paragraph': {
191
- return this.renderer.paragraph(this.inline.output(this.token.text));
192
- }
193
- case 'text': {
194
- return this.renderer.paragraph(this.parseText());
195
- }
196
- default: {
197
- const errMsg = 'Token with "' + this.token.type + '" type was not found.';
198
- if (this.options.silent) {
199
- console.log(errMsg);
200
- } else {
201
- throw new Error(errMsg);
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
+ }
202
250
  }
203
251
  }
204
252
  }
205
- };
253
+ return out;
254
+ }
206
255
  };
package/src/Renderer.js CHANGED
@@ -34,15 +34,15 @@ module.exports = class Renderer {
34
34
  + '">'
35
35
  + (escaped ? code : escape(code, true))
36
36
  + '</code></pre>\n';
37
- };
37
+ }
38
38
 
39
39
  blockquote(quote) {
40
40
  return '<blockquote>\n' + quote + '</blockquote>\n';
41
- };
41
+ }
42
42
 
43
43
  html(html) {
44
44
  return html;
45
- };
45
+ }
46
46
 
47
47
  heading(text, level, raw, slugger) {
48
48
  if (this.options.headerIds) {
@@ -59,21 +59,21 @@ module.exports = class Renderer {
59
59
  }
60
60
  // ignore IDs
61
61
  return '<h' + level + '>' + text + '</h' + level + '>\n';
62
- };
62
+ }
63
63
 
64
64
  hr() {
65
65
  return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
66
- };
66
+ }
67
67
 
68
68
  list(body, ordered, start) {
69
69
  const type = ordered ? 'ol' : 'ul',
70
70
  startatt = (ordered && start !== 1) ? (' start="' + start + '"') : '';
71
71
  return '<' + type + startatt + '>\n' + body + '</' + type + '>\n';
72
- };
72
+ }
73
73
 
74
74
  listitem(text) {
75
75
  return '<li>' + text + '</li>\n';
76
- };
76
+ }
77
77
 
78
78
  checkbox(checked) {
79
79
  return '<input '
@@ -81,11 +81,11 @@ module.exports = class Renderer {
81
81
  + 'disabled="" type="checkbox"'
82
82
  + (this.options.xhtml ? ' /' : '')
83
83
  + '> ';
84
- };
84
+ }
85
85
 
86
86
  paragraph(text) {
87
87
  return '<p>' + text + '</p>\n';
88
- };
88
+ }
89
89
 
90
90
  table(header, body) {
91
91
  if (body) body = '<tbody>' + body + '</tbody>';
@@ -96,11 +96,11 @@ module.exports = class Renderer {
96
96
  + '</thead>\n'
97
97
  + body
98
98
  + '</table>\n';
99
- };
99
+ }
100
100
 
101
101
  tablerow(content) {
102
102
  return '<tr>\n' + content + '</tr>\n';
103
- };
103
+ }
104
104
 
105
105
  tablecell(content, flags) {
106
106
  const type = flags.header ? 'th' : 'td';
@@ -108,28 +108,28 @@ module.exports = class Renderer {
108
108
  ? '<' + type + ' align="' + flags.align + '">'
109
109
  : '<' + type + '>';
110
110
  return tag + content + '</' + type + '>\n';
111
- };
111
+ }
112
112
 
113
113
  // span level renderer
114
114
  strong(text) {
115
115
  return '<strong>' + text + '</strong>';
116
- };
116
+ }
117
117
 
118
118
  em(text) {
119
119
  return '<em>' + text + '</em>';
120
- };
120
+ }
121
121
 
122
122
  codespan(text) {
123
123
  return '<code>' + text + '</code>';
124
- };
124
+ }
125
125
 
126
126
  br() {
127
127
  return this.options.xhtml ? '<br/>' : '<br>';
128
- };
128
+ }
129
129
 
130
130
  del(text) {
131
131
  return '<del>' + text + '</del>';
132
- };
132
+ }
133
133
 
134
134
  link(href, title, text) {
135
135
  href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
@@ -142,7 +142,7 @@ module.exports = class Renderer {
142
142
  }
143
143
  out += '>' + text + '</a>';
144
144
  return out;
145
- };
145
+ }
146
146
 
147
147
  image(href, title, text) {
148
148
  href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
@@ -156,9 +156,9 @@ module.exports = class Renderer {
156
156
  }
157
157
  out += this.options.xhtml ? '/>' : '>';
158
158
  return out;
159
- };
159
+ }
160
160
 
161
161
  text(text) {
162
162
  return text;
163
- };
163
+ }
164
164
  };
package/src/Slugger.js CHANGED
@@ -29,5 +29,5 @@ module.exports = class Slugger {
29
29
  this.seen[slug] = 0;
30
30
 
31
31
  return slug;
32
- };
32
+ }
33
33
  };