marked 1.0.0 → 1.1.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/package.json CHANGED
@@ -2,8 +2,9 @@
2
2
  "name": "marked",
3
3
  "description": "A markdown parser built for speed",
4
4
  "author": "Christopher Jeffrey",
5
- "version": "1.0.0",
5
+ "version": "1.1.0",
6
6
  "main": "./src/marked.js",
7
+ "browser": "./lib/marked.js",
7
8
  "bin": "./bin/marked",
8
9
  "man": "./man/marked.1",
9
10
  "files": [
@@ -30,27 +31,27 @@
30
31
  "html"
31
32
  ],
32
33
  "devDependencies": {
33
- "@babel/core": "^7.9.0",
34
- "@babel/preset-env": "^7.9.5",
35
- "@markedjs/html-differ": "^3.0.1",
34
+ "@babel/core": "^7.9.6",
35
+ "@babel/preset-env": "^7.9.6",
36
+ "@markedjs/html-differ": "^3.0.2",
36
37
  "cheerio": "^1.0.0-rc.3",
37
38
  "commonmark": "0.29.x",
38
- "eslint": "^6.8.0",
39
+ "eslint": "^7.0.0",
39
40
  "eslint-config-standard": "^14.1.1",
40
41
  "eslint-plugin-import": "^2.20.2",
41
42
  "eslint-plugin-node": "^11.1.0",
42
43
  "eslint-plugin-promise": "^4.2.1",
43
44
  "eslint-plugin-standard": "^4.0.1",
44
- "front-matter": "^3.1.0",
45
+ "front-matter": "^3.2.1",
45
46
  "jasmine": "^3.5.0",
46
47
  "markdown": "0.5.x",
47
48
  "markdown-it": "10.x",
48
49
  "node-fetch": "^2.6.0",
49
- "rollup": "^2.6.1",
50
+ "rollup": "^2.10.2",
50
51
  "rollup-plugin-babel": "^4.4.0",
51
52
  "rollup-plugin-commonjs": "^10.1.0",
52
- "rollup-plugin-license": "^2.0.0",
53
- "uglify-js": "^3.9.1",
53
+ "rollup-plugin-license": "^2.0.1",
54
+ "uglify-js": "^3.9.3",
54
55
  "vuln-regex-detector": "^1.3.0"
55
56
  },
56
57
  "scripts": {
package/src/Lexer.js CHANGED
@@ -112,7 +112,7 @@ module.exports = class Lexer {
112
112
  */
113
113
  blockTokens(src, tokens = [], top = true) {
114
114
  src = src.replace(/^ +$/gm, '');
115
- let token, i, l;
115
+ let token, i, l, lastToken;
116
116
 
117
117
  while (src) {
118
118
  // newline
@@ -127,7 +127,13 @@ module.exports = class Lexer {
127
127
  // code
128
128
  if (token = this.tokenizer.code(src, tokens)) {
129
129
  src = src.substring(token.raw.length);
130
- tokens.push(token);
130
+ if (token.type) {
131
+ tokens.push(token);
132
+ } else {
133
+ lastToken = tokens[tokens.length - 1];
134
+ lastToken.raw += '\n' + token.raw;
135
+ lastToken.text += '\n' + token.text;
136
+ }
131
137
  continue;
132
138
  }
133
139
 
@@ -219,9 +225,15 @@ module.exports = class Lexer {
219
225
  }
220
226
 
221
227
  // text
222
- if (token = this.tokenizer.text(src)) {
228
+ if (token = this.tokenizer.text(src, tokens)) {
223
229
  src = src.substring(token.raw.length);
224
- tokens.push(token);
230
+ if (token.type) {
231
+ tokens.push(token);
232
+ } else {
233
+ lastToken = tokens[tokens.length - 1];
234
+ lastToken.raw += '\n' + token.raw;
235
+ lastToken.text += '\n' + token.text;
236
+ }
225
237
  continue;
226
238
  }
227
239
 
package/src/Parser.js CHANGED
@@ -130,7 +130,7 @@ module.exports = class Parser {
130
130
  if (item.task) {
131
131
  checkbox = this.renderer.checkbox(checked);
132
132
  if (loose) {
133
- if (item.tokens[0].type === 'text') {
133
+ if (item.tokens.length > 0 && item.tokens[0].type === 'text') {
134
134
  item.tokens[0].text = checkbox + ' ' + item.tokens[0].text;
135
135
  if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') {
136
136
  item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text;
package/src/Renderer.js CHANGED
@@ -25,7 +25,7 @@ module.exports = class Renderer {
25
25
  if (!lang) {
26
26
  return '<pre><code>'
27
27
  + (escaped ? code : escape(code, true))
28
- + '</code></pre>';
28
+ + '</code></pre>\n';
29
29
  }
30
30
 
31
31
  return '<pre><code class="'
package/src/Tokenizer.js CHANGED
@@ -29,6 +29,34 @@ function outputLink(cap, link, raw) {
29
29
  }
30
30
  }
31
31
 
32
+ function indentCodeCompensation(raw, text) {
33
+ const matchIndentToCode = raw.match(/^(\s+)(?:```)/);
34
+
35
+ if (matchIndentToCode === null) {
36
+ return text;
37
+ }
38
+
39
+ const indentToCode = matchIndentToCode[1];
40
+
41
+ return text
42
+ .split('\n')
43
+ .map(node => {
44
+ const matchIndentInNode = node.match(/^\s+/);
45
+ if (matchIndentInNode === null) {
46
+ return node;
47
+ }
48
+
49
+ const [indentInNode] = matchIndentInNode;
50
+
51
+ if (indentInNode.length >= indentToCode.length) {
52
+ return node.slice(indentToCode.length);
53
+ }
54
+
55
+ return node;
56
+ })
57
+ .join('\n');
58
+ }
59
+
32
60
  /**
33
61
  * Tokenizer
34
62
  */
@@ -56,32 +84,35 @@ module.exports = class Tokenizer {
56
84
  const lastToken = tokens[tokens.length - 1];
57
85
  // An indented code block cannot interrupt a paragraph.
58
86
  if (lastToken && lastToken.type === 'paragraph') {
59
- tokens.pop();
60
- lastToken.text += '\n' + cap[0].trimRight();
61
- lastToken.raw += '\n' + cap[0];
62
- return lastToken;
63
- } else {
64
- const text = cap[0].replace(/^ {4}/gm, '');
65
87
  return {
66
- type: 'code',
67
88
  raw: cap[0],
68
- codeBlockStyle: 'indented',
69
- text: !this.options.pedantic
70
- ? rtrim(text, '\n')
71
- : text
89
+ text: cap[0].trimRight()
72
90
  };
73
91
  }
92
+
93
+ const text = cap[0].replace(/^ {4}/gm, '');
94
+ return {
95
+ type: 'code',
96
+ raw: cap[0],
97
+ codeBlockStyle: 'indented',
98
+ text: !this.options.pedantic
99
+ ? rtrim(text, '\n')
100
+ : text
101
+ };
74
102
  }
75
103
  }
76
104
 
77
105
  fences(src) {
78
106
  const cap = this.rules.block.fences.exec(src);
79
107
  if (cap) {
108
+ const raw = cap[0];
109
+ const text = indentCodeCompensation(raw, cap[3] || '');
110
+
80
111
  return {
81
112
  type: 'code',
82
- raw: cap[0],
113
+ raw,
83
114
  lang: cap[2] ? cap[2].trim() : cap[2],
84
- text: cap[3] || ''
115
+ text
85
116
  };
86
117
  }
87
118
  }
@@ -238,6 +269,7 @@ module.exports = class Tokenizer {
238
269
  }
239
270
 
240
271
  list.items.push({
272
+ type: 'list_item',
241
273
  raw,
242
274
  task: istask,
243
275
  checked: ischecked,
@@ -343,9 +375,17 @@ module.exports = class Tokenizer {
343
375
  }
344
376
  }
345
377
 
346
- text(src) {
378
+ text(src, tokens) {
347
379
  const cap = this.rules.block.text.exec(src);
348
380
  if (cap) {
381
+ const lastToken = tokens[tokens.length - 1];
382
+ if (lastToken && lastToken.type === 'text') {
383
+ return {
384
+ raw: cap[0],
385
+ text: cap[0]
386
+ };
387
+ }
388
+
349
389
  return {
350
390
  type: 'text',
351
391
  raw: cap[0],
@@ -473,10 +513,17 @@ module.exports = class Tokenizer {
473
513
  codespan(src) {
474
514
  const cap = this.rules.inline.code.exec(src);
475
515
  if (cap) {
516
+ let text = cap[2].replace(/\n/g, ' ');
517
+ const hasNonSpaceChars = /[^ ]/.test(text);
518
+ const hasSpaceCharsOnBothEnds = text.startsWith(' ') && text.endsWith(' ');
519
+ if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {
520
+ text = text.substring(1, text.length - 1);
521
+ }
522
+ text = escape(text, true);
476
523
  return {
477
524
  type: 'codespan',
478
525
  raw: cap[0],
479
- text: escape(cap[2].trim(), true)
526
+ text
480
527
  };
481
528
  }
482
529
  }
package/src/defaults.js CHANGED
@@ -16,6 +16,7 @@ function getDefaults() {
16
16
  smartLists: false,
17
17
  smartypants: false,
18
18
  tokenizer: null,
19
+ walkTokens: null,
19
20
  xhtml: false
20
21
  };
21
22
  }
package/src/marked.js CHANGED
@@ -28,18 +28,17 @@ function marked(src, opt, callback) {
28
28
  + Object.prototype.toString.call(src) + ', string expected');
29
29
  }
30
30
 
31
- if (callback || typeof opt === 'function') {
32
- if (!callback) {
33
- callback = opt;
34
- opt = null;
35
- }
31
+ if (typeof opt === 'function') {
32
+ callback = opt;
33
+ opt = null;
34
+ }
36
35
 
37
- opt = merge({}, marked.defaults, opt || {});
38
- checkSanitizeDeprecation(opt);
36
+ opt = merge({}, marked.defaults, opt || {});
37
+ checkSanitizeDeprecation(opt);
38
+
39
+ if (callback) {
39
40
  const highlight = opt.highlight;
40
- let tokens,
41
- pending,
42
- i = 0;
41
+ let tokens;
43
42
 
44
43
  try {
45
44
  tokens = Lexer.lex(src, opt);
@@ -47,20 +46,15 @@ function marked(src, opt, callback) {
47
46
  return callback(e);
48
47
  }
49
48
 
50
- pending = tokens.length;
51
-
52
49
  const done = function(err) {
53
- if (err) {
54
- opt.highlight = highlight;
55
- return callback(err);
56
- }
57
-
58
50
  let out;
59
51
 
60
- try {
61
- out = Parser.parse(tokens, opt);
62
- } catch (e) {
63
- err = e;
52
+ if (!err) {
53
+ try {
54
+ out = Parser.parse(tokens, opt);
55
+ } catch (e) {
56
+ err = e;
57
+ }
64
58
  }
65
59
 
66
60
  opt.highlight = highlight;
@@ -76,34 +70,45 @@ function marked(src, opt, callback) {
76
70
 
77
71
  delete opt.highlight;
78
72
 
79
- if (!pending) return done();
73
+ if (!tokens.length) return done();
80
74
 
81
- for (; i < tokens.length; i++) {
82
- (function(token) {
83
- if (token.type !== 'code') {
84
- return --pending || done();
85
- }
86
- return highlight(token.text, token.lang, function(err, code) {
87
- if (err) return done(err);
88
- if (code == null || code === token.text) {
89
- return --pending || done();
75
+ let pending = 0;
76
+ marked.walkTokens(tokens, function(token) {
77
+ if (token.type === 'code') {
78
+ pending++;
79
+ highlight(token.text, token.lang, function(err, code) {
80
+ if (err) {
81
+ return done(err);
82
+ }
83
+ if (code != null && code !== token.text) {
84
+ token.text = code;
85
+ token.escaped = true;
86
+ }
87
+
88
+ pending--;
89
+ if (pending === 0) {
90
+ done();
90
91
  }
91
- token.text = code;
92
- token.escaped = true;
93
- --pending || done();
94
92
  });
95
- })(tokens[i]);
93
+ }
94
+ });
95
+
96
+ if (pending === 0) {
97
+ done();
96
98
  }
97
99
 
98
100
  return;
99
101
  }
102
+
100
103
  try {
101
- opt = merge({}, marked.defaults, opt || {});
102
- checkSanitizeDeprecation(opt);
103
- return Parser.parse(Lexer.lex(src, opt), opt);
104
+ const tokens = Lexer.lex(src, opt);
105
+ if (opt.walkTokens) {
106
+ marked.walkTokens(tokens, opt.walkTokens);
107
+ }
108
+ return Parser.parse(tokens, opt);
104
109
  } catch (e) {
105
110
  e.message += '\nPlease report this to https://github.com/markedjs/marked.';
106
- if ((opt || marked.defaults).silent) {
111
+ if (opt.silent) {
107
112
  return '<p>An error occurred:</p><pre>'
108
113
  + escape(e.message + '', true)
109
114
  + '</pre>';
@@ -161,9 +166,50 @@ marked.use = function(extension) {
161
166
  }
162
167
  opts.tokenizer = tokenizer;
163
168
  }
169
+ if (extension.walkTokens) {
170
+ const walkTokens = marked.defaults.walkTokens;
171
+ opts.walkTokens = (token) => {
172
+ extension.walkTokens(token);
173
+ if (walkTokens) {
174
+ walkTokens(token);
175
+ }
176
+ };
177
+ }
164
178
  marked.setOptions(opts);
165
179
  };
166
180
 
181
+ /**
182
+ * Run callback for every token
183
+ */
184
+
185
+ marked.walkTokens = function(tokens, callback) {
186
+ for (const token of tokens) {
187
+ callback(token);
188
+ switch (token.type) {
189
+ case 'table': {
190
+ for (const cell of token.tokens.header) {
191
+ marked.walkTokens(cell, callback);
192
+ }
193
+ for (const row of token.tokens.cells) {
194
+ for (const cell of row) {
195
+ marked.walkTokens(cell, callback);
196
+ }
197
+ }
198
+ break;
199
+ }
200
+ case 'list': {
201
+ marked.walkTokens(token.items, callback);
202
+ break;
203
+ }
204
+ default: {
205
+ if (token.tokens) {
206
+ marked.walkTokens(token.tokens, callback);
207
+ }
208
+ }
209
+ }
210
+ }
211
+ };
212
+
167
213
  /**
168
214
  * Expose
169
215
  */
package/src/rules.js CHANGED
@@ -169,7 +169,7 @@ const inline = {
169
169
  reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,
170
170
  nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,
171
171
  strong: /^__([^\s_])__(?!_)|^\*\*([^\s*])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/,
172
- em: /^_([^\s_])_(?!_)|^_([^\s_<][\s\S]*?[^\s_])_(?!_|[^\spunctuation])|^_([^\s_<][\s\S]*?[^\s])_(?!_|[^\spunctuation])|^\*([^\s*<\[])\*(?!\*)|^\*([^\s<"][\s\S]*?[^\s\[\*])\*(?![\]`punctuation])|^\*([^\s*"<\[][\s\S]*[^\s])\*(?!\*)/,
172
+ em: /^_([^\s_])_(?!_)|^_([^\s_<][\s\S]*?[^\s_])_(?!_|[^\s,punctuation])|^_([^\s_<][\s\S]*?[^\s])_(?!_|[^\s,punctuation])|^\*([^\s*<\[])\*(?!\*)|^\*([^\s<"][\s\S]*?[^\s\[\*])\*(?![\]`punctuation])|^\*([^\s*"<\[][\s\S]*[^\s])\*(?!\*)/,
173
173
  code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,
174
174
  br: /^( {2,}|\\)\n(?!\s*$)/,
175
175
  del: noopTest,
@@ -178,6 +178,7 @@ const inline = {
178
178
 
179
179
  // list of punctuation marks from common mark spec
180
180
  // without ` and ] to workaround Rule 17 (inline code blocks/links)
181
+ // without , to work around example 393
181
182
  inline._punctuation = '!"#$%&\'()*+\\-./:;<=>?@\\[^_{|}~';
182
183
  inline.em = edit(inline.em).replace(/punctuation/g, inline._punctuation).getRegex();
183
184