diff --git a/app/assets/javascripts/content_editor/services/markdown_serializer.js b/app/assets/javascripts/content_editor/services/markdown_serializer.js index d2810049e6cb9e5c639f13d07ef7b33ec15cb286..e5deab196954c224cb81501a37863e4c524134e1 100644 --- a/app/assets/javascripts/content_editor/services/markdown_serializer.js +++ b/app/assets/javascripts/content_editor/services/markdown_serializer.js @@ -28,14 +28,26 @@ import { renderTableCell, renderTableRow, renderOrderedList, - renderHeading, renderHTMLNode, - renderContent, renderBulletList, renderReference, renderReferenceLabel, preserveUnchanged, } from './serialization_helpers'; +import descriptionList from './serializer/description_list'; +import descriptionItem from './serializer/description_item'; +import details from './serializer/details'; +import detailsContent from './serializer/details_content'; +import emoji from './serializer/emoji'; +import footnoteDefinition from './serializer/footnote_definition'; +import footnoteReference from './serializer/footnote_reference'; +import frontmatter from './serializer/frontmatter'; +import figure from './serializer/figure'; +import figureCaption from './serializer/figure_caption'; +import heading from './serializer/heading'; +import horizontalRule from './serializer/horizontal_rule'; +import listItem from './serializer/list_item'; +import loading from './serializer/loading'; const defaultSerializerConfig = { marks: { @@ -66,61 +78,22 @@ const defaultSerializerConfig = { [extensions.Diagram.name]: diagram, [extensions.CodeSuggestion.name]: codeSuggestion, [extensions.DrawioDiagram.name]: drawioDiagram, - [extensions.DescriptionList.name]: renderHTMLNode('dl', true), - [extensions.DescriptionItem.name]: (state, node, parent, index) => { - if (index === 1) state.ensureNewLine(); - renderHTMLNode(node.attrs.isTerm ? 'dt' : 'dd')(state, node); - if (index === parent.childCount - 1) state.ensureNewLine(); - }, - [extensions.Details.name]: renderHTMLNode('details', true), - [extensions.DetailsContent.name]: (state, node, parent, index) => { - if (!index) renderHTMLNode('summary')(state, node); - else { - if (index === 1) state.ensureNewLine(); - renderContent(state, node); - if (index === parent.childCount - 1) state.ensureNewLine(); - } - }, - [extensions.Emoji.name]: (state, node) => { - const { name } = node.attrs; - - state.write(`:${name}:`); - }, - [extensions.FootnoteDefinition.name]: preserveUnchanged((state, node) => { - state.write(`[^${node.attrs.identifier}]: `); - state.renderInline(node); - state.ensureNewLine(); - }), - [extensions.FootnoteReference.name]: preserveUnchanged({ - render: (state, node) => { - state.write(`[^${node.attrs.identifier}]`); - }, - inline: true, - }), - [extensions.Frontmatter.name]: preserveUnchanged((state, node) => { - const { language } = node.attrs; - const syntax = { - toml: '+++', - json: ';;;', - yaml: '---', - }[language]; - - state.write(`${syntax}\n`); - state.text(node.textContent, false); - state.ensureNewLine(); - state.write(syntax); - state.closeBlock(node); - }), - [extensions.Figure.name]: renderHTMLNode('figure'), - [extensions.FigureCaption.name]: renderHTMLNode('figcaption'), + [extensions.DescriptionList.name]: descriptionList, + [extensions.DescriptionItem.name]: descriptionItem, + [extensions.Details.name]: details, + [extensions.DetailsContent.name]: detailsContent, + [extensions.Emoji.name]: emoji, + [extensions.FootnoteDefinition.name]: footnoteDefinition, + [extensions.FootnoteReference.name]: footnoteReference, + [extensions.Frontmatter.name]: frontmatter, + [extensions.Figure.name]: figure, + [extensions.FigureCaption.name]: figureCaption, [extensions.HardBreak.name]: preserveUnchanged(renderHardBreak), - [extensions.Heading.name]: preserveUnchanged(renderHeading), - [extensions.HorizontalRule.name]: preserveUnchanged( - defaultMarkdownSerializer.nodes.horizontal_rule, - ), + [extensions.Heading.name]: heading, + [extensions.HorizontalRule.name]: horizontalRule, [extensions.Image.name]: image, - [extensions.ListItem.name]: preserveUnchanged(defaultMarkdownSerializer.nodes.list_item), - [extensions.Loading.name]: () => {}, + [extensions.ListItem.name]: listItem, + [extensions.Loading.name]: loading, [extensions.OrderedList.name]: preserveUnchanged(renderOrderedList), [extensions.Paragraph.name]: preserveUnchanged(defaultMarkdownSerializer.nodes.paragraph), [extensions.HTMLComment.name]: (state, node) => { diff --git a/app/assets/javascripts/content_editor/services/serialization_helpers.js b/app/assets/javascripts/content_editor/services/serialization_helpers.js index 194ecc79a2012860370111d0d14002b137abf825..b44e600b2f9359133061127e8dfe5300506c04dd 100644 --- a/app/assets/javascripts/content_editor/services/serialization_helpers.js +++ b/app/assets/javascripts/content_editor/services/serialization_helpers.js @@ -1,5 +1,4 @@ import { uniq, omit, isFunction } from 'lodash'; -import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer'; const defaultAttrs = { td: { colspan: 1, rowspan: 1, colwidth: null, align: 'left' }, @@ -337,12 +336,6 @@ export function renderHardBreak(state, node, parent, index) { } } -export function renderHeading(state, node) { - if (state.options.skipEmptyNodes && !node.childCount) return; - - defaultMarkdownSerializer.nodes.heading(state, node); -} - const expandPreserveUnchangedConfig = (configOrRender) => isFunction(configOrRender) ? { render: configOrRender, overwriteSourcePreservationStrategy: false, inline: false } diff --git a/app/assets/javascripts/content_editor/services/serializer/description_item.js b/app/assets/javascripts/content_editor/services/serializer/description_item.js new file mode 100644 index 0000000000000000000000000000000000000000..842c84f910e79d5394ba209c1efbb1b930d09169 --- /dev/null +++ b/app/assets/javascripts/content_editor/services/serializer/description_item.js @@ -0,0 +1,9 @@ +import { renderHTMLNode } from '../serialization_helpers'; + +const descriptionItem = (state, node, parent, index) => { + if (index === 1) state.ensureNewLine(); + renderHTMLNode(node.attrs.isTerm ? 'dt' : 'dd')(state, node); + if (index === parent.childCount - 1) state.ensureNewLine(); +}; + +export default descriptionItem; diff --git a/app/assets/javascripts/content_editor/services/serializer/description_list.js b/app/assets/javascripts/content_editor/services/serializer/description_list.js new file mode 100644 index 0000000000000000000000000000000000000000..a8262930c68b610fe8d3efb452a7cec86c25486d --- /dev/null +++ b/app/assets/javascripts/content_editor/services/serializer/description_list.js @@ -0,0 +1,5 @@ +import { renderHTMLNode } from '../serialization_helpers'; + +const descriptionList = renderHTMLNode('dl', true); + +export default descriptionList; diff --git a/app/assets/javascripts/content_editor/services/serializer/details.js b/app/assets/javascripts/content_editor/services/serializer/details.js new file mode 100644 index 0000000000000000000000000000000000000000..af3c9499b805a513f7ad14257fdf5f853a238b7b --- /dev/null +++ b/app/assets/javascripts/content_editor/services/serializer/details.js @@ -0,0 +1,5 @@ +import { renderHTMLNode } from '../serialization_helpers'; + +const details = renderHTMLNode('details', true); + +export default details; diff --git a/app/assets/javascripts/content_editor/services/serializer/details_content.js b/app/assets/javascripts/content_editor/services/serializer/details_content.js new file mode 100644 index 0000000000000000000000000000000000000000..f799ec34966c4f91f1ca538e8960b3a44e004bdb --- /dev/null +++ b/app/assets/javascripts/content_editor/services/serializer/details_content.js @@ -0,0 +1,12 @@ +import { renderContent, renderHTMLNode } from '../serialization_helpers'; + +const detailsContent = (state, node, parent, index) => { + if (!index) renderHTMLNode('summary')(state, node); + else { + if (index === 1) state.ensureNewLine(); + renderContent(state, node); + if (index === parent.childCount - 1) state.ensureNewLine(); + } +}; + +export default detailsContent; diff --git a/app/assets/javascripts/content_editor/services/serializer/emoji.js b/app/assets/javascripts/content_editor/services/serializer/emoji.js new file mode 100644 index 0000000000000000000000000000000000000000..6983411ebdc38ee04ce015965012d86660bcb51a --- /dev/null +++ b/app/assets/javascripts/content_editor/services/serializer/emoji.js @@ -0,0 +1,7 @@ +const emoji = (state, node) => { + const { name } = node.attrs; + + state.write(`:${name}:`); +}; + +export default emoji; diff --git a/app/assets/javascripts/content_editor/services/serializer/figure.js b/app/assets/javascripts/content_editor/services/serializer/figure.js new file mode 100644 index 0000000000000000000000000000000000000000..8a2e08f190207a175e8ca2a9b6cf93bd38b171d6 --- /dev/null +++ b/app/assets/javascripts/content_editor/services/serializer/figure.js @@ -0,0 +1,5 @@ +import { renderHTMLNode } from '../serialization_helpers'; + +const figure = renderHTMLNode('figure'); + +export default figure; diff --git a/app/assets/javascripts/content_editor/services/serializer/figure_caption.js b/app/assets/javascripts/content_editor/services/serializer/figure_caption.js new file mode 100644 index 0000000000000000000000000000000000000000..d55b6eab54255eb12d0dd80defdf9fc23dc80822 --- /dev/null +++ b/app/assets/javascripts/content_editor/services/serializer/figure_caption.js @@ -0,0 +1,5 @@ +import { renderHTMLNode } from '../serialization_helpers'; + +const figureCaption = renderHTMLNode('figcaption'); + +export default figureCaption; diff --git a/app/assets/javascripts/content_editor/services/serializer/footnote_definition.js b/app/assets/javascripts/content_editor/services/serializer/footnote_definition.js new file mode 100644 index 0000000000000000000000000000000000000000..28719ee808ca4ee1fb8f1362b6ba9ed634678c12 --- /dev/null +++ b/app/assets/javascripts/content_editor/services/serializer/footnote_definition.js @@ -0,0 +1,9 @@ +import { preserveUnchanged } from '../serialization_helpers'; + +const footnoteDefinition = preserveUnchanged((state, node) => { + state.write(`[^${node.attrs.identifier}]: `); + state.renderInline(node); + state.ensureNewLine(); +}); + +export default footnoteDefinition; diff --git a/app/assets/javascripts/content_editor/services/serializer/footnote_reference.js b/app/assets/javascripts/content_editor/services/serializer/footnote_reference.js new file mode 100644 index 0000000000000000000000000000000000000000..199c49a45ab02867b9adea1656095c74fcef7b1a --- /dev/null +++ b/app/assets/javascripts/content_editor/services/serializer/footnote_reference.js @@ -0,0 +1,10 @@ +import { preserveUnchanged } from '../serialization_helpers'; + +const footnoteReference = preserveUnchanged({ + render: (state, node) => { + state.write(`[^${node.attrs.identifier}]`); + }, + inline: true, +}); + +export default footnoteReference; diff --git a/app/assets/javascripts/content_editor/services/serializer/frontmatter.js b/app/assets/javascripts/content_editor/services/serializer/frontmatter.js new file mode 100644 index 0000000000000000000000000000000000000000..83cf0ebb56b221b32a1690ca897f441e11312801 --- /dev/null +++ b/app/assets/javascripts/content_editor/services/serializer/frontmatter.js @@ -0,0 +1,18 @@ +import { preserveUnchanged } from '../serialization_helpers'; + +const frontmatter = preserveUnchanged((state, node) => { + const { language } = node.attrs; + const syntax = { + toml: '+++', + json: ';;;', + yaml: '---', + }[language]; + + state.write(`${syntax}\n`); + state.text(node.textContent, false); + state.ensureNewLine(); + state.write(syntax); + state.closeBlock(node); +}); + +export default frontmatter; diff --git a/app/assets/javascripts/content_editor/services/serializer/heading.js b/app/assets/javascripts/content_editor/services/serializer/heading.js new file mode 100644 index 0000000000000000000000000000000000000000..fa710bb0747124efc958997299b8f991a236e4eb --- /dev/null +++ b/app/assets/javascripts/content_editor/services/serializer/heading.js @@ -0,0 +1,10 @@ +import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer'; +import { preserveUnchanged } from '../serialization_helpers'; + +const heading = preserveUnchanged((state, node) => { + if (state.options.skipEmptyNodes && !node.childCount) return; + + defaultMarkdownSerializer.nodes.heading(state, node); +}); + +export default heading; diff --git a/app/assets/javascripts/content_editor/services/serializer/horizontal_rule.js b/app/assets/javascripts/content_editor/services/serializer/horizontal_rule.js new file mode 100644 index 0000000000000000000000000000000000000000..560dc3e124b73f0f513bb4c491faee9878255632 --- /dev/null +++ b/app/assets/javascripts/content_editor/services/serializer/horizontal_rule.js @@ -0,0 +1,6 @@ +import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer'; +import { preserveUnchanged } from '../serialization_helpers'; + +const horizontalRule = preserveUnchanged(defaultMarkdownSerializer.nodes.horizontal_rule); + +export default horizontalRule; diff --git a/app/assets/javascripts/content_editor/services/serializer/list_item.js b/app/assets/javascripts/content_editor/services/serializer/list_item.js new file mode 100644 index 0000000000000000000000000000000000000000..cac00838b08653f701dc6650a4df4ad47f3b3a1c --- /dev/null +++ b/app/assets/javascripts/content_editor/services/serializer/list_item.js @@ -0,0 +1,6 @@ +import { defaultMarkdownSerializer } from '~/lib/prosemirror_markdown_serializer'; +import { preserveUnchanged } from '../serialization_helpers'; + +const listItem = preserveUnchanged(defaultMarkdownSerializer.nodes.list_item); + +export default listItem; diff --git a/app/assets/javascripts/content_editor/services/serializer/loading.js b/app/assets/javascripts/content_editor/services/serializer/loading.js new file mode 100644 index 0000000000000000000000000000000000000000..6df7fdd31fb52323959fea2019fa942176553c88 --- /dev/null +++ b/app/assets/javascripts/content_editor/services/serializer/loading.js @@ -0,0 +1,3 @@ +const loading = () => {}; + +export default loading;