Skip to content

Instantly share code, notes, and snippets.

@Xheldon
Last active January 18, 2023 02:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Xheldon/036d9b187bd83303205001e8af97eda7 to your computer and use it in GitHub Desktop.
Save Xheldon/036d9b187bd83303205001e8af97eda7 to your computer and use it in GitHub Desktop.
/**
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