Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/index.full.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Defuddle as DefuddleBase } from './defuddle';
import { DefuddleOptions, DefuddleResponse } from './types';
import { toMarkdown, createMarkdownContent } from './markdown';
import { toMarkdown, createMarkdownContent, addDefuddleRules } from './markdown';

// Export types
export type { DefuddleOptions, DefuddleResponse };

// Export standalone markdown conversion
export { createMarkdownContent };
export { createMarkdownContent, addDefuddleRules };

class Defuddle {
private defuddle: DefuddleBase;
Expand All @@ -32,6 +32,7 @@ class Defuddle {

// Attach named exports as static properties for UMD/CJS consumers
(Defuddle as any).createMarkdownContent = createMarkdownContent;
(Defuddle as any).addDefuddleRules = addDefuddleRules;

// Export Defuddle as default
export default Defuddle;
Expand Down
33 changes: 14 additions & 19 deletions src/markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,17 +76,7 @@ function getBestImageSrc(node: GenericElement): string {
return node.getAttribute('src') || '';
}

export function createMarkdownContent(content: string, url: string) {
const footnotes: { [key: string]: string } = {};
const turndownService = new TurndownService({
headingStyle: 'atx',
hr: '---',
bulletListMarker: '-',
codeBlockStyle: 'fenced',
emDelimiter: '*',
preformattedCode: true,
});

export function addDefuddleRules(turndownService: TurndownService): void {
turndownService.addRule('table', {
filter: 'table',
replacement: function(content, node) {
Expand Down Expand Up @@ -726,6 +716,19 @@ export function createMarkdownContent(content: string, url: string) {
}
return '';
}
}

export function createMarkdownContent(content: string, url: string) {
const turndownService = new TurndownService({
headingStyle: 'atx',
hr: '---',
bulletListMarker: '-',
codeBlockStyle: 'fenced',
emDelimiter: '*',
preformattedCode: true,
});

addDefuddleRules(turndownService);

try {
// Strip <wbr> tags — word break opportunity hints that are invisible in
Expand All @@ -752,14 +755,6 @@ export function createMarkdownContent(content: string, url: string) {

// Remove any consecutive newlines more than two
markdown = markdown.replace(/\n{3,}/g, '\n\n');

// Append footnotes at the end of the document
if (Object.keys(footnotes).length > 0) {
markdown += '\n\n---\n\n';
for (const [id, content] of Object.entries(footnotes)) {
markdown += `[^${id}]: ${content}\n\n`;
}
}

return markdown.trim();
} catch (error) {
Expand Down
36 changes: 36 additions & 0 deletions tests/markdown.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { describe, test, expect } from 'vitest';
import TurndownService from 'turndown';
import { Defuddle } from '../src/node';
import { parseDocument } from './helpers';
import { addDefuddleRules } from '../src/markdown';

describe('Markdown conversion', () => {
describe('exclamation mark before image', () => {
Expand Down Expand Up @@ -68,4 +70,38 @@ describe('Markdown conversion', () => {
expect(result.contentMarkdown).toContain('longword');
});
});

describe('addDefuddleRules', () => {
test('should register rules on a custom TurndownService instance', () => {
const td = new TurndownService({ headingStyle: 'setext', bulletListMarker: '*' });
addDefuddleRules(td);

const html = '<h2>Title</h2><ul><li>one</li><li>two</li></ul>';
const md = td.turndown(html);

// setext heading style (user option respected)
expect(md).toContain('Title\n-----');
// bullet marker from user option
expect(md).toContain('* one');
});

test('should apply table rule from defuddle', () => {
const td = new TurndownService();
addDefuddleRules(td);

const html = '<table><tr><th>A</th><th>B</th></tr><tr><td>1</td><td>2</td></tr></table>';
const md = td.turndown(html);

expect(md).toContain('| A | B |');
expect(md).toContain('| 1 | 2 |');
});

test('should apply highlight rule from defuddle', () => {
const td = new TurndownService();
addDefuddleRules(td);

const md = td.turndown('<p>This is <mark>highlighted</mark> text</p>');
expect(md).toContain('==highlighted==');
});
});
});