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
\ntext
');
});
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
\ntext
');
});
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
\npostprocess
');
});
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
\npostprocess 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
\nprocessAllTokens 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
\nprocessAllTokens 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
`);
});
it('should provide lexer', () => {
marked.use({
hooks: {
provideLexer() {
return (src) => [createHeadingToken(src)];
},
},
});
const html = marked.parse('text');
assert.strictEqual(html.trim(), 'text
');
});
it('should provide lexer async', async() => {
marked.use({
async: true,
hooks: {
provideLexer() {
return async(src) => {
await timeout();
return [createHeadingToken(src)];
};
},
},
});
const html = await marked.parse('text');
assert.strictEqual(html.trim(), 'text
');
});
it('should provide parser return object', () => {
marked.use({
hooks: {
provideParser() {
return (tokens) => ({ text: 'test parser' });
},
},
});
const html = marked.parse('text');
assert.strictEqual(html.text, 'test parser');
});
it('should provide parser', () => {
marked.use({
hooks: {
provideParser() {
return (tokens) => 'test parser';
},
},
});
const html = marked.parse('text');
assert.strictEqual(html.trim(), 'test parser');
});
it('should provide parser async', async() => {
marked.use({
async: true,
hooks: {
provideParser() {
return async(tokens) => {
await timeout();
return 'test parser';
};
},
},
});
const html = await marked.parse('text');
assert.strictEqual(html.trim(), 'test parser');
});
});