improve Dropbox Paper paste handling

This commit is contained in:
Shawn Erquhart 2017-07-07 18:28:15 -04:00
parent 804ef3d4b3
commit b08a9fcaa8

View File

@ -13,6 +13,9 @@ const remarkParseConfig = { fences: true };
const remarkStringifyConfig = { listItemIndent: '1', fences: true };
const rehypeParseConfig = { fragment: true };
/**
* Remove empty nodes, including the top level parents of deeply nested empty nodes.
*/
const rehypeRemoveEmpty = () => {
const isVoidElement = node => ['img', 'hr'].includes(node.tagName);
const isNonEmptyText = node => node.type === 'text' && node.value;
@ -37,6 +40,44 @@ const rehypeRemoveEmpty = () => {
return transform;
};
/**
* If the first child of a list item is a list, include it in the previous list
* item. Otherwise it translates to markdown as having two bullets. When
* rehype-remark processes a list and finds children that are not list items, it
* wraps them in list items, which leads to the condition this plugin addresses.
* Dropbox Paper currently outputs this kind of HTML, which is invalid. We have
* a support issue open for it, and this plugin can potentially be removed when
* that's resolved.
*/
const remarkNestedList = () => {
const transform = node => {
if (node.type === 'list' && node.children && node.children.length > 1) {
node.children = node.children.reduce((acc, childNode, index) => {
if (index && childNode.children && childNode.children[0].type === 'list') {
acc[acc.length - 1].children.push(transform(childNode.children.shift()))
if (childNode.children.length) {
acc.push(transform(childNode));
}
} else {
acc.push(transform(childNode));
}
return acc;
}, []);
return node;
}
if (node.children) {
node.children = node.children.map(transform);
}
return node;
};
return transform;
};
/**
* Dropbox Paper outputs emoji characters as images, and stores the actual
* emoji character in a `data-emoji-ch` attribute on the image. This plugin
* replaces the images with the emoji characters.
*/
const rehypePaperEmoji = () => {
const transform = node => {
if (node.tagName === 'img' && node.properties.dataEmojiCh) {
@ -49,25 +90,29 @@ const rehypePaperEmoji = () => {
};
export const markdownToHtml = markdown => {
console.log('markdownToHtml input', markdown);
const result = unified()
.use(markdownToRemark, remarkParseConfig)
.use(remarkToRehype)
.use(rehypeRemoveEmpty)
.use(rehypeSanitize)
.use(rehypeMinifyWhitespace)
.use(rehypeToHtml)
.processSync(markdown)
.contents;
console.log('markdownToHtml output', result);
return result;
}
export const htmlToMarkdown = html => {
console.log('htmlToMarkdown input', html);
const result = unified()
.use(htmlToRehype, rehypeParseConfig)
.use(rehypePaperEmoji)
.use(rehypeSanitize)
.use(rehypeRemoveEmpty)
.use(rehypeMinifyWhitespace)
.use(rehypeToRemark)
.use(remarkNestedList)
.use(remarkToMarkdown, remarkStringifyConfig)
.processSync(html)
.contents;
console.log('htmlToMarkdown output', result);
return result;
};