diff --git a/src/marked.js b/src/marked.js
index f9f59adc..c5bbca4e 100644
--- a/src/marked.js
+++ b/src/marked.js
@@ -72,8 +72,10 @@ function marked(src, opt, callback) {
if (!tokens.length) return done();
+ let pending = 0;
marked.iterateTokens(tokens, function(token) {
if (token.type === 'code') {
+ pending++;
highlight(token.text, token.lang, function(err, code) {
if (err) {
return done(err);
@@ -82,11 +84,20 @@ function marked(src, opt, callback) {
token.text = code;
token.escaped = true;
}
+
+ pending--;
+ if (pending === 0) {
+ done();
+ }
});
}
});
- return done();
+ if (pending === 0) {
+ done();
+ }
+
+ return;
}
try {
@@ -167,16 +178,20 @@ marked.iterateTokens = function(tokens, callback) {
}
switch (token.type) {
case 'table': {
- ret = marked.iterateTokens(token.tokens.header, callback);
- if (ret === false) {
- return false;
- }
- for (const row of token.tokens.cell) {
- ret = marked.iterateTokens(row, callback);
+ for (const cell of token.tokens.header) {
+ ret = marked.iterateTokens(cell, callback);
if (ret === false) {
return false;
}
}
+ for (const row of token.tokens.cells) {
+ for (const cell of row) {
+ ret = marked.iterateTokens(cell, callback);
+ if (ret === false) {
+ return false;
+ }
+ }
+ }
break;
}
case 'list': {
diff --git a/test/unit/marked-spec.js b/test/unit/marked-spec.js
index 1b6beafd..3087f46f 100644
--- a/test/unit/marked-spec.js
+++ b/test/unit/marked-spec.js
@@ -229,3 +229,167 @@ paragraph
expect(html).toBe('arrow no options\nfunction options\nshorthand options\n');
});
});
+
+describe('async highlight', () => {
+ let highlight, markdown;
+ beforeEach(() => {
+ highlight = jasmine.createSpy('highlight', (text, lang, callback) => {
+ setImmediate(() => {
+ callback(null, `async ${text || ''}`);
+ });
+ });
+ markdown = `
+\`\`\`lang1
+text 1
+\`\`\`
+
+> \`\`\`lang2
+> text 2
+> \`\`\`
+
+- \`\`\`lang3
+ text 3
+ \`\`\`
+`;
+ });
+
+ it('should highlight codeblocks async', (done) => {
+ highlight.and.callThrough();
+
+ marked(markdown, { highlight }, (err, html) => {
+ if (err) {
+ fail(err);
+ }
+
+ expect(html).toBe(`
async text 1
+
+async text 2
+
+
+`);
+ done();
+ });
+ });
+
+ it('should call callback for each error in highlight', (done) => {
+ highlight.and.callFake((lang, text, callback) => {
+ callback(new Error('highlight error'));
+ });
+
+ let numErrors = 0;
+ marked(markdown, { highlight }, (err, html) => {
+ expect(err).toBeTruthy();
+ expect(html).toBeUndefined();
+
+ if (err) {
+ numErrors++;
+ }
+
+ if (numErrors === 3) {
+ done();
+ }
+ });
+ });
+});
+
+describe('iterateTokens', () => {
+ it('should iterate over every token', () => {
+ const markdown = `
+paragraph
+
+---
+
+# heading
+
+\`\`\`
+code
+\`\`\`
+
+| a | b |
+|---|---|
+| 1 | 2 |
+| 3 | 4 |
+
+> blockquote
+
+- list
+
+html
+
+[link](https://example.com)
+
+
+
+**strong**
+
+*em*
+
+\`codespan\`
+
+~~del~~
+
+br
+br
+`;
+ const tokens = marked.lexer(markdown, { ...marked.getDefaults(), breaks: true });
+ const tokensSeen = [];
+ marked.iterateTokens(tokens, (token) => {
+ tokensSeen.push([token.type, (token.raw || '').replace(/\n/g, '')]);
+ });
+
+ expect(tokensSeen).toEqual([
+ ['paragraph', 'paragraph'],
+ ['text', 'paragraph'],
+ ['space', ''],
+ ['hr', '---'],
+ ['heading', '# heading'],
+ ['text', 'heading'],
+ ['code', '```code```'],
+ ['table', '| a | b ||---|---|| 1 | 2 || 3 | 4 |'],
+ ['text', 'a'],
+ ['text', 'b'],
+ ['text', '1'],
+ ['text', '2'],
+ ['text', '3'],
+ ['text', '4'],
+ ['blockquote', '> blockquote'],
+ ['paragraph', 'blockquote'],
+ ['text', 'blockquote'],
+ ['list', '- list'],
+ ['list_item', '- list'],
+ ['text', 'list'],
+ ['text', 'list'],
+ ['space', ''],
+ ['html', 'html
'],
+ ['paragraph', '[link](https://example.com)'],
+ ['link', '[link](https://example.com)'],
+ ['text', 'link'],
+ ['space', ''],
+ ['paragraph', ''],
+ ['image', ''],
+ ['space', ''],
+ ['paragraph', '**strong**'],
+ ['strong', '**strong**'],
+ ['text', 'strong'],
+ ['space', ''],
+ ['paragraph', '*em*'],
+ ['em', '*em*'],
+ ['text', 'em'],
+ ['space', ''],
+ ['paragraph', '`codespan`'],
+ ['codespan', '`codespan`'],
+ ['space', ''],
+ ['paragraph', '~~del~~'],
+ ['del', '~~del~~'],
+ ['text', 'del'],
+ ['space', ''],
+ ['paragraph', 'brbr'],
+ ['text', 'br'],
+ ['br', ''],
+ ['text', 'br']
+ ]);
+ });
+});