mirror of
https://github.com/docmost/docmost.git
synced 2025-11-13 17:22:37 +10:00
* fix page import title bug * fix youtube embed in markdown export * add link to rendered file html * fix: markdown callout import * update local generateJSON * feat: switch spaces from sidebar * remove unused package * feat: editor date menu command * fix date description * update default locale code * feat: add more code highlight languages
136 lines
3.9 KiB
TypeScript
136 lines
3.9 KiB
TypeScript
import * as TurndownService from '@joplin/turndown';
|
|
import * as TurndownPluginGfm from '@joplin/turndown-plugin-gfm';
|
|
|
|
export function turndown(html: string): string {
|
|
const turndownService = new TurndownService({
|
|
headingStyle: 'atx',
|
|
codeBlockStyle: 'fenced',
|
|
hr: '---',
|
|
bulletListMarker: '-',
|
|
});
|
|
const tables = TurndownPluginGfm.tables;
|
|
const strikethrough = TurndownPluginGfm.strikethrough;
|
|
const highlightedCodeBlock = TurndownPluginGfm.highlightedCodeBlock;
|
|
|
|
turndownService.use([
|
|
tables,
|
|
strikethrough,
|
|
highlightedCodeBlock,
|
|
taskList,
|
|
callout,
|
|
preserveDetail,
|
|
listParagraph,
|
|
mathInline,
|
|
mathBlock,
|
|
iframeEmbed,
|
|
]);
|
|
return turndownService.turndown(html).replaceAll('<br>', ' ');
|
|
}
|
|
|
|
function listParagraph(turndownService: TurndownService) {
|
|
turndownService.addRule('paragraph', {
|
|
filter: ['p'],
|
|
replacement: (content: any, node: HTMLInputElement) => {
|
|
if (node.parentElement?.nodeName === 'LI') {
|
|
return content;
|
|
}
|
|
|
|
return `\n\n${content}\n\n`;
|
|
},
|
|
});
|
|
}
|
|
|
|
function callout(turndownService: TurndownService) {
|
|
turndownService.addRule('callout', {
|
|
filter: function (node: HTMLInputElement) {
|
|
return (
|
|
node.nodeName === 'DIV' && node.getAttribute('data-type') === 'callout'
|
|
);
|
|
},
|
|
replacement: function (content: any, node: HTMLInputElement) {
|
|
const calloutType = node.getAttribute('data-callout-type');
|
|
return `\n\n:::${calloutType}\n${content.trim()}\n:::\n\n`;
|
|
},
|
|
});
|
|
}
|
|
|
|
function taskList(turndownService: TurndownService) {
|
|
turndownService.addRule('taskListItem', {
|
|
filter: function (node: HTMLInputElement) {
|
|
return (
|
|
node.getAttribute('data-type') === 'taskItem' &&
|
|
node.parentNode.nodeName === 'UL'
|
|
);
|
|
},
|
|
replacement: function (content: any, node: HTMLInputElement) {
|
|
const checkbox = node.querySelector(
|
|
'input[type="checkbox"]',
|
|
) as HTMLInputElement;
|
|
const isChecked = checkbox.checked;
|
|
|
|
return `- ${isChecked ? '[x]' : '[ ]'} ${content.trim()} \n`;
|
|
},
|
|
});
|
|
}
|
|
|
|
function preserveDetail(turndownService: TurndownService) {
|
|
turndownService.addRule('preserveDetail', {
|
|
filter: function (node: HTMLInputElement) {
|
|
return node.nodeName === 'DETAILS';
|
|
},
|
|
replacement: function (content: any, node: HTMLInputElement) {
|
|
// TODO: preserve summary of nested details
|
|
const summary = node.querySelector(':scope > summary');
|
|
let detailSummary = '';
|
|
|
|
if (summary) {
|
|
detailSummary = `<summary>${turndownService.turndown(summary.innerHTML)}</summary>`;
|
|
summary.remove();
|
|
}
|
|
|
|
const detailsContent = turndownService.turndown(node.innerHTML);
|
|
return `\n<details>\n${detailSummary}\n\n${detailsContent}\n\n</details>\n`;
|
|
},
|
|
});
|
|
}
|
|
|
|
function mathInline(turndownService: TurndownService) {
|
|
turndownService.addRule('mathInline', {
|
|
filter: function (node: HTMLInputElement) {
|
|
return (
|
|
node.nodeName === 'SPAN' &&
|
|
node.getAttribute('data-type') === 'mathInline'
|
|
);
|
|
},
|
|
replacement: function (content: any, node: HTMLInputElement) {
|
|
return `$${content}$`;
|
|
},
|
|
});
|
|
}
|
|
|
|
function mathBlock(turndownService: TurndownService) {
|
|
turndownService.addRule('mathBlock', {
|
|
filter: function (node: HTMLInputElement) {
|
|
return (
|
|
node.nodeName === 'DIV' &&
|
|
node.getAttribute('data-type') === 'mathBlock'
|
|
);
|
|
},
|
|
replacement: function (content: any, node: HTMLInputElement) {
|
|
return `\n$$${content}$$\n`;
|
|
},
|
|
});
|
|
}
|
|
|
|
function iframeEmbed(turndownService: TurndownService) {
|
|
turndownService.addRule('iframeEmbed', {
|
|
filter: function (node: HTMLInputElement) {
|
|
return node.nodeName === 'IFRAME';
|
|
},
|
|
replacement: function (content: any, node: HTMLInputElement) {
|
|
const src = node.getAttribute('src');
|
|
return '[' + src + '](' + src + ')';
|
|
},
|
|
});
|
|
}
|