import { Marked } from '../../lib/marked.esm.js'; import { timeout } from './utils.js'; import { describe, it, beforeEach } from 'node:test'; import assert from 'node:assert'; function createHeadingToken(text) { return { type: 'heading', raw: `# ${text}`, depth: 1, text, tokens: [ { type: 'text', raw: text, text } ] }; } describe('Hooks', () => { let marked; beforeEach(() => { marked = new Marked(); }); it('should preprocess markdown', () => { marked.use({ hooks: { preprocess(markdown) { return `# preprocess\n\n${markdown}`; } } }); const html = marked.parse('*text*'); assert.strictEqual(html.trim(), '

preprocess

\n

text

'); }); it('should preprocess async', async() => { marked.use({ async: true, hooks: { async preprocess(markdown) { await timeout(); return `# preprocess async\n\n${markdown}`; } } }); const promise = marked.parse('*text*'); assert.ok(promise instanceof Promise); const html = await promise; assert.strictEqual(html.trim(), '

preprocess async

\n

text

'); }); it('should preprocess options', () => { marked.use({ hooks: { preprocess(markdown) { this.options.breaks = true; return markdown; } } }); const html = marked.parse('line1\nline2'); assert.strictEqual(html.trim(), '

line1
line2

'); }); it('should preprocess options async', async() => { marked.use({ async: true, hooks: { async preprocess(markdown) { await timeout(); this.options.breaks = true; return markdown; } } }); const html = await marked.parse('line1\nline2'); assert.strictEqual(html.trim(), '

line1
line2

'); }); it('should postprocess html', () => { marked.use({ hooks: { postprocess(html) { return html + '

postprocess

'; } } }); const html = marked.parse('*text*'); assert.strictEqual(html.trim(), '

text

\n

postprocess

'); }); it('should postprocess async', async() => { marked.use({ async: true, hooks: { async postprocess(html) { await timeout(); return html + '

postprocess async

\n'; } } }); const promise = marked.parse('*text*'); assert.ok(promise instanceof Promise); const html = await promise; assert.strictEqual(html.trim(), '

text

\n

postprocess async

'); }); it('should process tokens before walkTokens', () => { marked.use({ hooks: { processAllTokens(tokens) { tokens.push(createHeadingToken('processAllTokens')); return tokens; } }, walkTokens(token) { if (token.type === 'heading') { token.tokens[0].text += ' walked'; } return token; } }); const html = marked.parse('*text*'); assert.strictEqual(html.trim(), '

text

\n

processAllTokens walked

'); }); it('should process tokens async before walkTokens', async() => { marked.use({ async: true, hooks: { async processAllTokens(tokens) { await timeout(); tokens.push(createHeadingToken('processAllTokens async')); return tokens; } }, walkTokens(token) { if (token.type === 'heading') { token.tokens[0].text += ' walked'; } return token; } }); const promise = marked.parse('*text*'); assert.ok(promise instanceof Promise); const html = await promise; assert.strictEqual(html.trim(), '

text

\n

processAllTokens async walked

'); }); it('should process all hooks in reverse', async() => { marked.use({ hooks: { preprocess(markdown) { return `# preprocess1\n\n${markdown}`; }, postprocess(html) { return html + '

postprocess1

\n'; }, processAllTokens(tokens) { tokens.push(createHeadingToken('processAllTokens1')); return tokens; } } }); marked.use({ async: true, hooks: { preprocess(markdown) { return `# preprocess2\n\n${markdown}`; }, async postprocess(html) { await timeout(); return html + '

postprocess2 async

\n'; }, processAllTokens(tokens) { tokens.push(createHeadingToken('processAllTokens2')); return tokens; } } }); const promise = marked.parse('*text*'); assert.ok(promise instanceof Promise); const html = await promise; assert.strictEqual(html.trim(), `\

preprocess1

preprocess2

text

processAllTokens2

processAllTokens1

postprocess2 async

postprocess1

`); }); });