marked 5.0.4 → 5.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.
@@ -0,0 +1,374 @@
1
+ import { getDefaults } from './defaults.js';
2
+ import { Lexer } from './Lexer.js';
3
+ import { Parser } from './Parser.js';
4
+ import { Hooks } from './Hooks.js';
5
+ import { Renderer } from './Renderer.js';
6
+ import { Tokenizer } from './Tokenizer.js';
7
+ import { TextRenderer } from './TextRenderer.js';
8
+ import { Slugger } from './Slugger.js';
9
+ import {
10
+ checkDeprecations,
11
+ escape
12
+ } from './helpers.js';
13
+
14
+ export class Marked {
15
+ defaults = getDefaults();
16
+ options = this.setOptions;
17
+
18
+ parse = this.#parseMarkdown(Lexer.lex, Parser.parse);
19
+ parseInline = this.#parseMarkdown(Lexer.lexInline, Parser.parseInline);
20
+
21
+ Parser = Parser;
22
+ parser = Parser.parse;
23
+ Renderer = Renderer;
24
+ TextRenderer = TextRenderer;
25
+ Lexer = Lexer;
26
+ lexer = Lexer.lex;
27
+ Tokenizer = Tokenizer;
28
+ Slugger = Slugger;
29
+ Hooks = Hooks;
30
+
31
+ constructor(...args) {
32
+ this.use(...args);
33
+ }
34
+
35
+ walkTokens(tokens, callback) {
36
+ let values = [];
37
+ for (const token of tokens) {
38
+ values = values.concat(callback.call(this, token));
39
+ switch (token.type) {
40
+ case 'table': {
41
+ for (const cell of token.header) {
42
+ values = values.concat(this.walkTokens(cell.tokens, callback));
43
+ }
44
+ for (const row of token.rows) {
45
+ for (const cell of row) {
46
+ values = values.concat(this.walkTokens(cell.tokens, callback));
47
+ }
48
+ }
49
+ break;
50
+ }
51
+ case 'list': {
52
+ values = values.concat(this.walkTokens(token.items, callback));
53
+ break;
54
+ }
55
+ default: {
56
+ if (this.defaults.extensions && this.defaults.extensions.childTokens && this.defaults.extensions.childTokens[token.type]) { // Walk any extensions
57
+ this.defaults.extensions.childTokens[token.type].forEach((childTokens) => {
58
+ values = values.concat(this.walkTokens(token[childTokens], callback));
59
+ });
60
+ } else if (token.tokens) {
61
+ values = values.concat(this.walkTokens(token.tokens, callback));
62
+ }
63
+ }
64
+ }
65
+ }
66
+ return values;
67
+ }
68
+
69
+ use(...args) {
70
+ const extensions = this.defaults.extensions || { renderers: {}, childTokens: {} };
71
+
72
+ args.forEach((pack) => {
73
+ // copy options to new object
74
+ const opts = { ...pack };
75
+
76
+ // set async to true if it was set to true before
77
+ opts.async = this.defaults.async || opts.async || false;
78
+
79
+ // ==-- Parse "addon" extensions --== //
80
+ if (pack.extensions) {
81
+ pack.extensions.forEach((ext) => {
82
+ if (!ext.name) {
83
+ throw new Error('extension name required');
84
+ }
85
+ if (ext.renderer) { // Renderer extensions
86
+ const prevRenderer = extensions.renderers[ext.name];
87
+ if (prevRenderer) {
88
+ // Replace extension with func to run new extension but fall back if false
89
+ extensions.renderers[ext.name] = function(...args) {
90
+ let ret = ext.renderer.apply(this, args);
91
+ if (ret === false) {
92
+ ret = prevRenderer.apply(this, args);
93
+ }
94
+ return ret;
95
+ };
96
+ } else {
97
+ extensions.renderers[ext.name] = ext.renderer;
98
+ }
99
+ }
100
+ if (ext.tokenizer) { // Tokenizer Extensions
101
+ if (!ext.level || (ext.level !== 'block' && ext.level !== 'inline')) {
102
+ throw new Error("extension level must be 'block' or 'inline'");
103
+ }
104
+ if (extensions[ext.level]) {
105
+ extensions[ext.level].unshift(ext.tokenizer);
106
+ } else {
107
+ extensions[ext.level] = [ext.tokenizer];
108
+ }
109
+ if (ext.start) { // Function to check for start of token
110
+ if (ext.level === 'block') {
111
+ if (extensions.startBlock) {
112
+ extensions.startBlock.push(ext.start);
113
+ } else {
114
+ extensions.startBlock = [ext.start];
115
+ }
116
+ } else if (ext.level === 'inline') {
117
+ if (extensions.startInline) {
118
+ extensions.startInline.push(ext.start);
119
+ } else {
120
+ extensions.startInline = [ext.start];
121
+ }
122
+ }
123
+ }
124
+ }
125
+ if (ext.childTokens) { // Child tokens to be visited by walkTokens
126
+ extensions.childTokens[ext.name] = ext.childTokens;
127
+ }
128
+ });
129
+ opts.extensions = extensions;
130
+ }
131
+
132
+ // ==-- Parse "overwrite" extensions --== //
133
+ if (pack.renderer) {
134
+ const renderer = this.defaults.renderer || new Renderer(this.defaults);
135
+ for (const prop in pack.renderer) {
136
+ const prevRenderer = renderer[prop];
137
+ // Replace renderer with func to run extension, but fall back if false
138
+ renderer[prop] = (...args) => {
139
+ let ret = pack.renderer[prop].apply(renderer, args);
140
+ if (ret === false) {
141
+ ret = prevRenderer.apply(renderer, args);
142
+ }
143
+ return ret;
144
+ };
145
+ }
146
+ opts.renderer = renderer;
147
+ }
148
+ if (pack.tokenizer) {
149
+ const tokenizer = this.defaults.tokenizer || new Tokenizer(this.defaults);
150
+ for (const prop in pack.tokenizer) {
151
+ const prevTokenizer = tokenizer[prop];
152
+ // Replace tokenizer with func to run extension, but fall back if false
153
+ tokenizer[prop] = (...args) => {
154
+ let ret = pack.tokenizer[prop].apply(tokenizer, args);
155
+ if (ret === false) {
156
+ ret = prevTokenizer.apply(tokenizer, args);
157
+ }
158
+ return ret;
159
+ };
160
+ }
161
+ opts.tokenizer = tokenizer;
162
+ }
163
+
164
+ // ==-- Parse Hooks extensions --== //
165
+ if (pack.hooks) {
166
+ const hooks = this.defaults.hooks || new Hooks();
167
+ for (const prop in pack.hooks) {
168
+ const prevHook = hooks[prop];
169
+ if (Hooks.passThroughHooks.has(prop)) {
170
+ hooks[prop] = (arg) => {
171
+ if (this.defaults.async) {
172
+ return Promise.resolve(pack.hooks[prop].call(hooks, arg)).then(ret => {
173
+ return prevHook.call(hooks, ret);
174
+ });
175
+ }
176
+
177
+ const ret = pack.hooks[prop].call(hooks, arg);
178
+ return prevHook.call(hooks, ret);
179
+ };
180
+ } else {
181
+ hooks[prop] = (...args) => {
182
+ let ret = pack.hooks[prop].apply(hooks, args);
183
+ if (ret === false) {
184
+ ret = prevHook.apply(hooks, args);
185
+ }
186
+ return ret;
187
+ };
188
+ }
189
+ }
190
+ opts.hooks = hooks;
191
+ }
192
+
193
+ // ==-- Parse WalkTokens extensions --== //
194
+ if (pack.walkTokens) {
195
+ const walkTokens = this.defaults.walkTokens;
196
+ opts.walkTokens = function(token) {
197
+ let values = [];
198
+ values.push(pack.walkTokens.call(this, token));
199
+ if (walkTokens) {
200
+ values = values.concat(walkTokens.call(this, token));
201
+ }
202
+ return values;
203
+ };
204
+ }
205
+
206
+ this.defaults = { ...this.defaults, ...opts };
207
+ });
208
+
209
+ return this;
210
+ }
211
+
212
+ setOptions(opt) {
213
+ this.defaults = { ...this.defaults, ...opt };
214
+ return this;
215
+ }
216
+
217
+ #parseMarkdown(lexer, parser) {
218
+ return (src, opt, callback) => {
219
+ if (typeof opt === 'function') {
220
+ callback = opt;
221
+ opt = null;
222
+ }
223
+
224
+ const origOpt = { ...opt };
225
+ opt = { ...this.defaults, ...origOpt };
226
+ const throwError = this.#onError(opt.silent, opt.async, callback);
227
+
228
+ // throw error in case of non string input
229
+ if (typeof src === 'undefined' || src === null) {
230
+ return throwError(new Error('marked(): input parameter is undefined or null'));
231
+ }
232
+ if (typeof src !== 'string') {
233
+ return throwError(new Error('marked(): input parameter is of type '
234
+ + Object.prototype.toString.call(src) + ', string expected'));
235
+ }
236
+
237
+ checkDeprecations(opt, callback);
238
+
239
+ if (opt.hooks) {
240
+ opt.hooks.options = opt;
241
+ }
242
+
243
+ if (callback) {
244
+ const highlight = opt.highlight;
245
+ let tokens;
246
+
247
+ try {
248
+ if (opt.hooks) {
249
+ src = opt.hooks.preprocess(src);
250
+ }
251
+ tokens = lexer(src, opt);
252
+ } catch (e) {
253
+ return throwError(e);
254
+ }
255
+
256
+ const done = (err) => {
257
+ let out;
258
+
259
+ if (!err) {
260
+ try {
261
+ if (opt.walkTokens) {
262
+ this.walkTokens(tokens, opt.walkTokens);
263
+ }
264
+ out = parser(tokens, opt);
265
+ if (opt.hooks) {
266
+ out = opt.hooks.postprocess(out);
267
+ }
268
+ } catch (e) {
269
+ err = e;
270
+ }
271
+ }
272
+
273
+ opt.highlight = highlight;
274
+
275
+ return err
276
+ ? throwError(err)
277
+ : callback(null, out);
278
+ };
279
+
280
+ if (!highlight || highlight.length < 3) {
281
+ return done();
282
+ }
283
+
284
+ delete opt.highlight;
285
+
286
+ if (!tokens.length) return done();
287
+
288
+ let pending = 0;
289
+ this.walkTokens(tokens, (token) => {
290
+ if (token.type === 'code') {
291
+ pending++;
292
+ setTimeout(() => {
293
+ highlight(token.text, token.lang, (err, code) => {
294
+ if (err) {
295
+ return done(err);
296
+ }
297
+ if (code != null && code !== token.text) {
298
+ token.text = code;
299
+ token.escaped = true;
300
+ }
301
+
302
+ pending--;
303
+ if (pending === 0) {
304
+ done();
305
+ }
306
+ });
307
+ }, 0);
308
+ }
309
+ });
310
+
311
+ if (pending === 0) {
312
+ done();
313
+ }
314
+
315
+ return;
316
+ }
317
+
318
+ if (opt.async) {
319
+ return Promise.resolve(opt.hooks ? opt.hooks.preprocess(src) : src)
320
+ .then(src => lexer(src, opt))
321
+ .then(tokens => opt.walkTokens ? Promise.all(this.walkTokens(tokens, opt.walkTokens)).then(() => tokens) : tokens)
322
+ .then(tokens => parser(tokens, opt))
323
+ .then(html => opt.hooks ? opt.hooks.postprocess(html) : html)
324
+ .catch(throwError);
325
+ }
326
+
327
+ try {
328
+ if (opt.hooks) {
329
+ src = opt.hooks.preprocess(src);
330
+ }
331
+ const tokens = lexer(src, opt);
332
+ if (opt.walkTokens) {
333
+ this.walkTokens(tokens, opt.walkTokens);
334
+ }
335
+ let html = parser(tokens, opt);
336
+ if (opt.hooks) {
337
+ html = opt.hooks.postprocess(html);
338
+ }
339
+ return html;
340
+ } catch (e) {
341
+ return throwError(e);
342
+ }
343
+ };
344
+ }
345
+
346
+ #onError(silent, async, callback) {
347
+ return (e) => {
348
+ e.message += '\nPlease report this to https://github.com/markedjs/this.';
349
+
350
+ if (silent) {
351
+ const msg = '<p>An error occurred:</p><pre>'
352
+ + escape(e.message + '', true)
353
+ + '</pre>';
354
+ if (async) {
355
+ return Promise.resolve(msg);
356
+ }
357
+ if (callback) {
358
+ callback(null, msg);
359
+ return;
360
+ }
361
+ return msg;
362
+ }
363
+
364
+ if (async) {
365
+ return Promise.reject(e);
366
+ }
367
+ if (callback) {
368
+ callback(e);
369
+ return;
370
+ }
371
+ throw e;
372
+ };
373
+ }
374
+ }
package/src/Lexer.js CHANGED
@@ -347,10 +347,9 @@ export class Lexer {
347
347
  maskedSrc = maskedSrc.slice(0, match.index) + '[' + 'a'.repeat(match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);
348
348
  }
349
349
 
350
- // Mask out escaped em & strong delimiters
351
- while ((match = this.tokenizer.rules.inline.escapedEmSt.exec(maskedSrc)) != null) {
352
- maskedSrc = maskedSrc.slice(0, match.index + match[0].length - 2) + '++' + maskedSrc.slice(this.tokenizer.rules.inline.escapedEmSt.lastIndex);
353
- this.tokenizer.rules.inline.escapedEmSt.lastIndex--;
350
+ // Mask out escaped characters
351
+ while ((match = this.tokenizer.rules.inline.anyPunctuation.exec(maskedSrc)) != null) {
352
+ maskedSrc = maskedSrc.slice(0, match.index) + '++' + maskedSrc.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);
354
353
  }
355
354
 
356
355
  while (src) {
package/src/Tokenizer.js CHANGED
@@ -613,7 +613,7 @@ export class Tokenizer {
613
613
 
614
614
  const nextChar = match[1] || match[2] || '';
615
615
 
616
- if (!nextChar || (nextChar && (prevChar === '' || this.rules.inline.punctuation.exec(prevChar)))) {
616
+ if (!nextChar || !prevChar || this.rules.inline.punctuation.exec(prevChar)) {
617
617
  const lLength = match[0].length - 1;
618
618
  let rDelim, rLength, delimTotal = lLength, midDelimTotal = 0;
619
619
 
@@ -647,7 +647,7 @@ export class Tokenizer {
647
647
  // Remove extra characters. *a*** -> *a*
648
648
  rLength = Math.min(rLength, rLength + delimTotal + midDelimTotal);
649
649
 
650
- const raw = src.slice(0, lLength + match.index + (match[0].length - rDelim.length) + rLength);
650
+ const raw = src.slice(0, lLength + match.index + rLength + 1);
651
651
 
652
652
  // Create `em` if smallest delimiter has odd char count. *a***
653
653
  if (Math.min(lLength, rLength) % 2) {