/**
* External dependencies
*/
import TurndownService from 'turndown';
/**
* Types
*/
import type { Options, Rule, Filter } from 'turndown';
export type Fix = 'paragraph';
type Fixes = {
[ key in Fix ]: ( content: string ) => string;
};
const fixesList: Fixes = {
paragraph: ( content: string ) => {
// Keep
tags to prevent paragraphs from being split
return content.replaceAll( '\n', '
' );
},
};
const defaultTurndownOptions: Options = { emDelimiter: '_', headingStyle: 'atx' };
const defaultTurndownRules: { [ key: string ]: Rule } = {
strikethrough: {
filter: [ 'del', 's' ],
replacement: function ( content: string ) {
return '~~' + content + '~~';
},
},
};
export default class HTMLToMarkdown {
turndownService: TurndownService;
fixes: Fix[];
constructor( {
options = {},
rules = {},
keep = [],
remove = [],
fixes = [],
}: {
options?: Options;
rules?: { [ key: string ]: Rule };
keep?: Filter;
remove?: Filter;
fixes?: Fix[];
} = {} ) {
this.fixes = fixes;
this.turndownService = new TurndownService( { ...defaultTurndownOptions, ...options } );
this.turndownService.keep( keep );
this.turndownService.remove( remove );
const allRules = { ...defaultTurndownRules, ...rules };
for ( const rule in allRules ) {
this.turndownService.addRule( rule, allRules[ rule ] );
}
}
/**
* Renders HTML from Markdown content with specified processing rules.
* @param {object} options - The options to use when rendering the Markdown content
* @param {string} options.content - The HTML content to render
* @return {string} The rendered Markdown content
*/
render( { content }: { content: string } ): string {
const rendered = this.turndownService.turndown( content );
return this.fixes.reduce( ( renderedContent, fix ) => {
return fixesList[ fix ]( renderedContent );
}, rendered );
}
}