diff --git a/src/actions/editorialWorkflow.js b/src/actions/editorialWorkflow.js
index 7c1afdd3..98e48532 100644
--- a/src/actions/editorialWorkflow.js
+++ b/src/actions/editorialWorkflow.js
@@ -1,5 +1,6 @@
import uuid from 'uuid';
import { actions as notifActions } from 'redux-notifications';
+import { serializeValues } from '../lib/serializeEntryValues';
import { closeEntry } from './editor';
import { BEGIN, COMMIT, REVERT } from 'redux-optimist';
import { currentBackend } from '../backends/backend';
diff --git a/src/components/Widgets/Markdown/MarkdownControl/RawEditor/index.js b/src/components/Widgets/Markdown/MarkdownControl/RawEditor/index.js
index 1ef4ae0f..7983afb4 100644
--- a/src/components/Widgets/Markdown/MarkdownControl/RawEditor/index.js
+++ b/src/components/Widgets/Markdown/MarkdownControl/RawEditor/index.js
@@ -1,6 +1,5 @@
import React, { PropTypes } from 'react';
import { Editor as Slate, Plain } from 'slate';
-import { markdownToRemark, remarkToMarkdown } from '../../serializers';
import Toolbar from '../Toolbar/Toolbar';
import { Sticky } from '../../../../UI/Sticky/Sticky';
import styles from './index.css';
@@ -8,14 +7,8 @@ import styles from './index.css';
export default class RawEditor extends React.Component {
constructor(props) {
super(props);
- /**
- * The value received is a Remark AST (MDAST), and must be stringified
- * to plain text before Slate's Plain serializer can convert it to the
- * Slate AST.
- */
- const value = remarkToMarkdown(this.props.value);
this.state = {
- editorState: Plain.deserialize(value || ''),
+ editorState: Plain.deserialize(this.props.value || ''),
};
}
@@ -29,13 +22,11 @@ export default class RawEditor extends React.Component {
/**
* When the document value changes, serialize from Slate's AST back to plain
- * text (which is Markdown), and then deserialize from that to a Remark MDAST,
- * before passing up as the new value.
+ * text (which is Markdown) and pass that up as the new value.
*/
handleDocumentChange = (doc, editorState) => {
const value = Plain.serialize(editorState);
- const mdast = markdownToRemark(value);
- this.props.onChange(mdast);
+ this.props.onChange(value);
};
/**
@@ -79,5 +70,5 @@ export default class RawEditor extends React.Component {
RawEditor.propTypes = {
onChange: PropTypes.func.isRequired,
onMode: PropTypes.func.isRequired,
- value: PropTypes.object,
+ value: PropTypes.string,
};
diff --git a/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/__tests__/parser.spec.js b/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/__tests__/parser.spec.js
index 6700eacf..f549b366 100644
--- a/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/__tests__/parser.spec.js
+++ b/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/__tests__/parser.spec.js
@@ -1,8 +1,9 @@
import { fromJS } from 'immutable';
-import { markdownToRemark, remarkToSlate } from '../../../serializers';
+import { markdownToSlate } from '../../../serializers';
-// Temporary plugins test, uses preloaded plugins from ../parser
-// TODO: make the parser more testable
+const parser = markdownToSlate;
+
+// Temporary plugins test
const testPlugins = fromJS([
{
label: 'Image',
@@ -44,8 +45,6 @@ const testPlugins = fromJS([
},
]);
-const parser = markdown => remarkToSlate(markdownToRemark(markdown));
-
describe("Compile markdown to Slate Raw AST", () => {
it("should compile simple markdown", () => {
const value = `
diff --git a/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/index.js b/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/index.js
index 33fbcd26..3a67ad7a 100644
--- a/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/index.js
+++ b/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/index.js
@@ -1,7 +1,7 @@
import React, { Component, PropTypes } from 'react';
import { get, isEmpty } from 'lodash';
import { Editor as Slate, Raw, Block, Text } from 'slate';
-import { slateToRemark, remarkToSlate, htmlToSlate } from '../../serializers';
+import { slateToMarkdown, markdownToSlate, htmlToSlate } from '../../serializers';
import registry from '../../../../../lib/registry';
import Toolbar from '../Toolbar/Toolbar';
import { Sticky } from '../../../../UI/Sticky/Sticky';
@@ -15,10 +15,10 @@ export default class Editor extends Component {
constructor(props) {
super(props);
const emptyBlock = Block.create({ kind: 'block', type: 'paragraph'});
- const emptyRaw = { nodes: [emptyBlock] };
- const mdast = this.props.value && remarkToSlate(this.props.value);
- const mdastHasNodes = !isEmpty(get(mdast, 'nodes'))
- const editorState = Raw.deserialize(mdastHasNodes ? mdast : emptyRaw, { terse: true });
+ const emptyRawDoc = { nodes: [emptyBlock] };
+ const rawDoc = this.props.value && markdownToSlate(this.props.value);
+ const rawDocHasNodes = !isEmpty(get(rawDoc, 'nodes'))
+ const editorState = Raw.deserialize(rawDocHasNodes ? rawDoc : emptyRawDoc, { terse: true });
this.state = {
editorState,
schema: {
@@ -46,8 +46,8 @@ export default class Editor extends Component {
handleDocumentChange = (doc, editorState) => {
const raw = Raw.serialize(editorState, { terse: true });
const plugins = this.state.shortcodePlugins;
- const mdast = slateToRemark(raw, plugins);
- this.props.onChange(mdast);
+ const markdown = slateToMarkdown(raw, plugins);
+ this.props.onChange(markdown);
};
hasMark = type => this.state.editorState.marks.some(mark => mark.type === type);
@@ -211,5 +211,5 @@ Editor.propTypes = {
getAsset: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
onMode: PropTypes.func.isRequired,
- value: PropTypes.object,
+ value: PropTypes.string,
};
diff --git a/src/components/Widgets/Markdown/MarkdownControl/index.js b/src/components/Widgets/Markdown/MarkdownControl/index.js
index 423bafd8..79b7e866 100644
--- a/src/components/Widgets/Markdown/MarkdownControl/index.js
+++ b/src/components/Widgets/Markdown/MarkdownControl/index.js
@@ -7,24 +7,13 @@ import { StickyContainer } from '../../../UI/Sticky/Sticky';
const MODE_STORAGE_KEY = 'cms.md-mode';
-/**
- * The markdown field value is persisted as a markdown string, but stringifying
- * on every keystroke is a big perf hit, so we'll register functions to perform
- * those actions only when necessary, such as after loading and before
- * persisting.
- */
-registry.registerWidgetValueSerializer('markdown', {
- serialize: remarkToMarkdown,
- deserialize: markdownToRemark,
-});
-
export default class MarkdownControl extends React.Component {
static propTypes = {
onChange: PropTypes.func.isRequired,
onAddAsset: PropTypes.func.isRequired,
onRemoveAsset: PropTypes.func.isRequired,
getAsset: PropTypes.func.isRequired,
- value: PropTypes.object,
+ value: PropTypes.string,
};
constructor(props) {
diff --git a/src/components/Widgets/Markdown/MarkdownPreview/__tests__/renderer.spec.js b/src/components/Widgets/Markdown/MarkdownPreview/__tests__/renderer.spec.js
index 02bb94c2..b4d7c0bf 100644
--- a/src/components/Widgets/Markdown/MarkdownPreview/__tests__/renderer.spec.js
+++ b/src/components/Widgets/Markdown/MarkdownPreview/__tests__/renderer.spec.js
@@ -4,7 +4,9 @@ import React from 'react';
import { shallow } from 'enzyme';
import { padStart } from 'lodash';
import MarkdownPreview from '../index';
-import { markdownToRemark } from '../../serializers';
+import { markdownToHtml } from '../../serializers';
+
+const parser = markdownToHtml;
describe('Markdown Preview renderer', () => {
describe('Markdown rendering', () => {
@@ -36,7 +38,7 @@ Text with **bold** & _em_ elements
###### H6
`;
- const component = shallow();
+ const component = shallow();
expect(component.html()).toMatchSnapshot();
});
});
@@ -45,7 +47,7 @@ Text with **bold** & _em_ elements
for (const heading of [...Array(6).keys()]) {
it(`should render Heading ${ heading + 1 }`, () => {
const value = padStart(' Title', heading + 7, '#');
- const component = shallow();
+ const component = shallow();
expect(component.html()).toMatchSnapshot();
});
}
@@ -64,7 +66,7 @@ Text with **bold** & _em_ elements
1. Sub-Sublist 3
1. ol item 3
`;
- const component = shallow();
+ const component = shallow();
expect(component.html()).toMatchSnapshot();
});
});
@@ -78,7 +80,7 @@ I get 10 times more traffic from [Google] [1] than from [Yahoo] [2] or [MSN] [3]
[2]: http://search.yahoo.com/ "Yahoo Search"
[3]: http://search.msn.com/ "MSN Search"
`;
- const component = shallow();
+ const component = shallow();
expect(component.html()).toMatchSnapshot();
});
});
@@ -86,13 +88,13 @@ I get 10 times more traffic from [Google] [1] than from [Yahoo] [2] or [MSN] [3]
describe('Code', () => {
it('should render code', () => {
const value = 'Use the `printf()` function.';
- const component = shallow();
+ const component = shallow();
expect(component.html()).toMatchSnapshot();
});
it('should render code 2', () => {
const value = '``There is a literal backtick (`) here.``';
- const component = shallow();
+ const component = shallow();
expect(component.html()).toMatchSnapshot();
});
});
@@ -114,7 +116,7 @@ I get 10 times more traffic from [Google] [1] than from [Yahoo] [2] or [MSN] [3]
Test
`;
- const component = shallow();
+ const component = shallow();
expect(component.html()).toMatchSnapshot();
});
});
@@ -123,7 +125,7 @@ I get 10 times more traffic from [Google] [1] than from [Yahoo] [2] or [MSN] [3]
describe('HTML rendering', () => {
it('should render HTML', () => {
const value = 'Paragraph with inline element
';
- const component = shallow();
+ const component = shallow();
expect(component.html()).toMatchSnapshot();
});
});
diff --git a/src/components/Widgets/Markdown/MarkdownPreview/index.js b/src/components/Widgets/Markdown/MarkdownPreview/index.js
index 461b0b40..c6cbfcf9 100644
--- a/src/components/Widgets/Markdown/MarkdownPreview/index.js
+++ b/src/components/Widgets/Markdown/MarkdownPreview/index.js
@@ -1,18 +1,18 @@
import React, { PropTypes } from 'react';
-import { remarkToHtml } from '../serializers';
+import { markdownToHtml } from '../serializers';
import previewStyle from '../../defaultPreviewStyle';
const MarkdownPreview = ({ value, getAsset }) => {
if (value === null) {
return null;
}
- const html = remarkToHtml(value, getAsset);
+ const html = markdownToHtml(value, getAsset);
return ;
};
MarkdownPreview.propTypes = {
getAsset: PropTypes.func.isRequired,
- value: PropTypes.object,
+ value: PropTypes.string,
};
export default MarkdownPreview;
diff --git a/src/components/Widgets/Markdown/serializers/index.js b/src/components/Widgets/Markdown/serializers/index.js
index f0d4dbdb..f3c11a64 100644
--- a/src/components/Widgets/Markdown/serializers/index.js
+++ b/src/components/Widgets/Markdown/serializers/index.js
@@ -12,12 +12,12 @@ import rehypePaperEmoji from './rehypePaperEmoji';
import remarkAssertParents from './remarkAssertParents';
import remarkPaddedLinks from './remarkPaddedLinks';
import remarkWrapHtml from './remarkWrapHtml';
-import remarkToSlatePlugin from './remarkSlate';
+import remarkToSlate from './remarkSlate';
import remarkSquashReferences from './remarkSquashReferences';
import remarkImagesToText from './remarkImagesToText';
import remarkShortcodes from './remarkShortcodes';
import remarkEscapeMarkdownEntities from './remarkEscapeMarkdownEntities'
-import slateToRemarkParser from './slateRemark';
+import slateToRemark from './slateRemark';
import registry from '../../../../lib/registry';
/**
@@ -35,11 +35,9 @@ import registry from '../../../../lib/registry';
* - MDAST {object}
* Also loosely referred to as "Remark". MDAST stands for MarkDown AST
* (Abstract Syntax Tree), and is an object representation of a Markdown
- * document. Underneath, it's a Unist tree with a Markdown-specific schema. An
- * MDAST is used as the source of truth for any Markdown field within the CMS
- * once the Markdown string value is loaded. MDAST syntax is a part of the
- * Unified ecosystem, and powers the Remark processor, so Remark plugins may
- * be used.
+ * document. Underneath, it's a Unist tree with a Markdown-specific schema.
+ * MDAST syntax is a part of the Unified ecosystem, and powers the Remark
+ * processor, so Remark plugins may be used.
*
* - HAST {object}
* Also loosely referred to as "Rehype". HAST, similar to MDAST, is an object
@@ -54,55 +52,6 @@ import registry from '../../../../lib/registry';
* Slate's Raw AST is a very simple and unopinionated object representation of
* a document in a Slate editor. We define our own Markdown-specific schema
* for serialization to/from Slate's Raw AST and MDAST.
- *
- * Overview of the Markdown widget serialization life cycle:
- *
- * - Entry Load
- * When an entry is loaded, all Markdown widget values are serialized to
- * MDAST within the entry draft.
- *
- * - Visual Editor Render
- * When a Markdown widget using the visual editor renders, it converts the
- * MDAST value from the entry draft to Slate's Raw AST, and renders that.
- *
- * - Visual Editor Update
- * When the value of a Markdown field is changed in the visual editor, the
- * resulting Slate Raw AST is converted back to MDAST, and the MDAST value is
- * set as the new state of the field in the entry draft.
- *
- * - Visual Editor Paste
- * When a value is pasted to the visual editor, the pasted value is checked
- * for HTML data. If HTML is found, the value is deserialized to an HAST, then
- * to MDAST, and finally to Slate's Raw AST. If no HTML is found, the plain
- * text value of the paste is serialized to Slate's Raw AST via the Slate
- * Plain serializer. The deserialized fragment is then inserted to the Slate
- * document.
- *
- * - Raw Editor Render
- * When a Markdown widget using the raw editor (Markdown switch activated),
- * it stringifies the MDAST from the entry draft to Markdown, and runs the
- * stringified Markdown through Slate's Plain serializer, which outputs a
- * Slate Raw AST of the plain text, which is then rendered in the editor.
- *
- * - Raw Editor Update
- * When the value of a Markdown field is changed in the raw editor, the
- * resulting Slate Raw AST is stringified back to a string, and the string
- * value is then parsed as Markdown into an MDAST. The MDAST value is
- * set as the new state of the field in the entry draft.
- *
- * - Raw Editor Paste
- * When a value is pasted to the raw editor, the text value of the paste is
- * serialized to Slate's Raw AST via the Slate Plain serializer. The
- * deserialized fragment is then inserted to the Slate document.
- *
- * - Preview Pane Render
- * When the preview pane renders the value of a Markdown widget, it first
- * converts the MDAST value to HAST, stringifies the HAST to HTML, and
- * renders that.
- *
- * - Entry Persist (Save)
- * On persist, the MDAST value in the entry draft is stringified back to
- * a Markdown string for storage.
*/
@@ -180,9 +129,11 @@ export const remarkToMarkdown = obj => {
/**
- * Convert an MDAST to an HTML string.
+ * Convert Markdown to HTML.
*/
-export const remarkToHtml = (mdast, getAsset) => {
+export const markdownToHtml = (markdown, getAsset) => {
+ const mdast = markdownToRemark(markdown);
+
const hast = unified()
.use(remarkToRehypeShortcodes, { plugins: registry.getEditorComponents(), getAsset })
.use(remarkToRehype, { allowDangerousHTML: true })
@@ -216,7 +167,7 @@ export const htmlToSlate = html => {
.use(remarkImagesToText)
.use(remarkShortcodes, { plugins: registry.getEditorComponents() })
.use(remarkWrapHtml)
- .use(remarkToSlatePlugin)
+ .use(remarkToSlate)
.runSync(mdast);
return slateRaw;
@@ -224,19 +175,22 @@ export const htmlToSlate = html => {
/**
- * Convert an MDAST to Slate's Raw AST.
+ * Convert Markdown to Slate's Raw AST.
*/
-export const remarkToSlate = mdast => {
- const result = unified()
+export const markdownToSlate = markdown => {
+ const mdast = markdownToRemark(markdown);
+
+ const slateRaw = unified()
.use(remarkWrapHtml)
- .use(remarkToSlatePlugin)
+ .use(remarkToSlate)
.runSync(mdast);
- return result;
+
+ return slateRaw;
};
/**
- * Convert a Slate Raw AST to MDAST.
+ * Convert a Slate Raw AST to Markdown.
*
* Requires shortcode plugins to parse shortcode nodes back to text.
*
@@ -244,7 +198,8 @@ export const remarkToSlate = mdast => {
* MDAST. The conversion is manual because Unified can only operate on Unist
* trees.
*/
-export const slateToRemark = (raw) => {
- const mdast = slateToRemarkParser(raw, { shortcodePlugins: registry.getEditorComponents() });
- return mdast;
+export const slateToMarkdown = raw => {
+ const mdast = slateToRemark(raw, { shortcodePlugins: registry.getEditorComponents() });
+ const markdown = remarkToMarkdown(mdast);
+ return markdown;
};
diff --git a/src/components/Widgets/Markdown/serializers/remarkSlate.js b/src/components/Widgets/Markdown/serializers/remarkSlate.js
index c7673aa7..f8cc42f0 100644
--- a/src/components/Widgets/Markdown/serializers/remarkSlate.js
+++ b/src/components/Widgets/Markdown/serializers/remarkSlate.js
@@ -281,7 +281,7 @@ function convertNode(node, nodes) {
* A Remark plugin for converting an MDAST to Slate Raw AST. Remark plugins
* return a `transform` function that receives the MDAST as it's first argument.
*/
-export default function remarkToSlatePlugin() {
+export default function remarkToSlate() {
function transform(node) {
/**