Last active
January 18, 2023 02:28
-
-
Save Xheldon/036d9b187bd83303205001e8af97eda7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
Craft's markdown api has the following problems. | |
1. Nested lists are not supported. | |
2. The order of Nested blockquote and is incorrectly. | |
3. only the latter will be recognized as a reference block. | |
4. urlBlock (may called 'bookmark'?) will be recognized as a link(in this function, you can custom render tag of jekyll, you can modify this as you wish) | |
Note: | |
1. not support toggle/formula, because markdown not support those too, but you can make it. | |
2. I use this function for may jekyll blog workflow, you can modify it to fit your workflow. | |
3. caption(#####) is used for image caption if it is right next to the image(in my case, it not render as `###### xxx`). | |
Usage: use `craft.dataApi.getCurrentPage()` first, get the result, then invoke `result.subblocks.map(craftBlockToMarkdown).filter(Boolean).join('\n')` to get the markdown | |
*/ | |
const inlineText = (content = []) => { | |
return content.reduce((pre, curr) => { | |
let result = ''; | |
if (curr.link) { | |
result += '[' | |
} | |
if (curr.isCode) { | |
result += '`'; | |
} | |
if (curr.isBold) { | |
result += '**'; | |
} | |
if (curr.isStrikethrough) { | |
result += '~~'; | |
} | |
if (curr.isItalic) { | |
result += '*'; | |
} | |
result += curr.text; | |
if (curr.isItalic) { | |
result += '*'; | |
} | |
if (curr.isStrikethrough) { | |
result += '~~'; | |
} | |
if (curr.isBold) { | |
result += '**'; | |
} | |
if (curr.isCode) { | |
result += '`'; | |
} | |
if (curr.link) { | |
result += `](${curr.link.url})`; | |
} | |
return `${pre}${result}` | |
}, '') || '' | |
} | |
const craftBlockToMarkdown = (block, key, blocks) => { | |
let indent = ''; | |
for (let i = 0; i < block.indentationLevel; i++) { | |
indent += ` `; | |
} | |
switch (block.type) { | |
case 'textBlock': { | |
if (!block.content) { | |
console.log(`no content in ${key}`) | |
return; | |
} | |
let symbol = ''; | |
if (block.listStyle?.type && block.listStyle.type !== 'none') { | |
let blockquote = ''; | |
if (block.hasBlockDecoration || block.hasFocusDecoration) { | |
blockquote = '> ' | |
} | |
switch (block.listStyle?.type) { | |
case 'todo': { | |
symbol = block.listStyle.state === 'checked' ? '- [x] ' : '- [ ] '; | |
break; | |
} | |
case 'toggle': { | |
break; | |
} | |
case 'bullet': { | |
symbol = '* ' | |
break; | |
} | |
case 'numbered': { | |
symbol = '1. '; | |
} | |
} | |
return indent + blockquote + symbol + inlineText(block.content) + '\n'; | |
} | |
if (block.hasBlockDecoration || block.hasFocusDecoration) { | |
return indent + `> ${inlineText(block.content)}\n` | |
} | |
switch (block.style?.textStyle) { | |
case 'title': { | |
return `${indent}# ${inlineText(block.content)}\n`; | |
} | |
case 'subtitle': { | |
return `${indent}## ${inlineText(block.content)}\n`; | |
} | |
case 'heading': { | |
return `${indent}### ${inlineText(block.content)}\n`; | |
} | |
case 'strong': { | |
return `${indent}#### ${inlineText(block.content)}\n`; | |
} | |
case 'body': { | |
return `${indent}${inlineText(block.content)}\n`; | |
} | |
// used for image caption | |
case 'caption': { | |
return ''; | |
} | |
case 'page': | |
case 'card': { | |
return `${indent}${inlineText(block.content)}\n\n${block.subblocks?.map(craftBlockToMarkdown).filter(Boolean).join('\n')}`; | |
} | |
} | |
} | |
case 'horizontalLineBlock': { | |
return '---\n' | |
} | |
case 'tableBlock': { | |
return craft.markdown.craftBlockToMarkdown([block], 'common', { | |
tableSupported: true, | |
}) + '\n'; | |
} | |
case 'imageBlock': { | |
const next = blocks[key + 1]; | |
if (next?.style?.textStyle === 'caption') { | |
return `{% render_caption caption="${next.content[0]?.text}" %}\n![${block.filename || '__Unsplash__'}](${block.url}){% endrender_caption %}\n` | |
} | |
return `![${block.filename || 'Unsplash'}](${block.url})\n`; | |
} | |
case 'codeBlock': { | |
if (block.language === 'math_formula') { | |
// Note: markdown not support formula | |
} | |
return indent + `\`\`\`${block.language}\n${indent}${block.code}\n${indent}\`\`\`\n`; | |
} | |
case 'urlBlock': { | |
// render as jekyll custom tag, you can modify this as you wish | |
return `{% render_bookmark url="${block.url}" title="${block.title}" img="${block.imageUrl || '__no__img__'}" %}\n${block.pageDescription || ''}\n{% endrender_bookmark %}\n`; | |
} | |
case 'urlBlock': { | |
// Note: render as jekyll custom tag, you can modify this as you wish | |
// youtube and bilibili link support | |
// urlBlock has 'url' which the url may redirect and the 'originalUrl' has the original url. | |
let url = new URL(block.url || block.originalUrl); | |
let yid = ''; | |
let bid = ''; | |
if (url.hostname === 'www.youtube.com') { | |
for (let i of url.searchParams) { | |
if (i[0] === 'v') { | |
yid = i[1]; | |
} | |
} | |
} else if (url.hostname === 'www.bilibili.com') { | |
bid = url.pathname.split('/').filter(Boolean)[1]; | |
} | |
return `{% render_bookmark url="${block.url}" title="${block.title || ''}" img="${block.imageUrl || ''}" yid="${yid}" bid="${bid}" %}\n${block.pageDescription || ''}\n{% endrender_bookmark %}\n`; | |
} | |
case 'fileBlock': { | |
// file attachment not support in markdown | |
return '' | |
} | |
case 'videoBlock': { | |
if (block.filename) { | |
let filename = block.filename; | |
let suffix = ''; | |
let arr = filename.split('.'); | |
if (arr.length > 1) { | |
filename = arr.slice(0, arr.length - 1).join(''); | |
suffix = arr[arr.length - 1]; | |
} | |
return `{% render_video caption="${filename}" img="${block.url}" suffix="${suffix}" %}\n![${filename}](${block.url})\n{% endrender_video %}\n` | |
} | |
} | |
}; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment