decap-cms-widget-markdown 2.16.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/CHANGELOG.md +820 -0
  2. package/LICENSE +22 -0
  3. package/README.md +9 -0
  4. package/dist/decap-cms-widget-markdown.js +9 -0
  5. package/dist/decap-cms-widget-markdown.js.LICENSE.txt +37 -0
  6. package/dist/decap-cms-widget-markdown.js.map +1 -0
  7. package/dist/esm/MarkdownControl/RawEditor.js +106 -0
  8. package/dist/esm/MarkdownControl/Toolbar.js +216 -0
  9. package/dist/esm/MarkdownControl/ToolbarButton.js +43 -0
  10. package/dist/esm/MarkdownControl/VisualEditor.js +268 -0
  11. package/dist/esm/MarkdownControl/components/Shortcode.js +78 -0
  12. package/dist/esm/MarkdownControl/components/VoidBlock.js +55 -0
  13. package/dist/esm/MarkdownControl/index.js +129 -0
  14. package/dist/esm/MarkdownControl/plugins/BreakToDefaultBlock.js +34 -0
  15. package/dist/esm/MarkdownControl/plugins/CloseBlock.js +36 -0
  16. package/dist/esm/MarkdownControl/plugins/CommandsAndQueries.js +188 -0
  17. package/dist/esm/MarkdownControl/plugins/ForceInsert.js +49 -0
  18. package/dist/esm/MarkdownControl/plugins/Hotkey.js +35 -0
  19. package/dist/esm/MarkdownControl/plugins/LineBreak.js +21 -0
  20. package/dist/esm/MarkdownControl/plugins/Link.js +56 -0
  21. package/dist/esm/MarkdownControl/plugins/blocks/defaultEmptyBlock.js +16 -0
  22. package/dist/esm/MarkdownControl/plugins/blocks/events/keyDown.js +47 -0
  23. package/dist/esm/MarkdownControl/plugins/blocks/events/keyDownBackspace.js +26 -0
  24. package/dist/esm/MarkdownControl/plugins/blocks/events/keyDownEnter.js +28 -0
  25. package/dist/esm/MarkdownControl/plugins/blocks/events/toggleBlock.js +46 -0
  26. package/dist/esm/MarkdownControl/plugins/blocks/locations/areCurrentAndPreviousBlocksOfType.js +20 -0
  27. package/dist/esm/MarkdownControl/plugins/blocks/locations/getListTypeAtCursor.js +16 -0
  28. package/dist/esm/MarkdownControl/plugins/blocks/locations/isCursorAtEndOfParagraph.js +19 -0
  29. package/dist/esm/MarkdownControl/plugins/blocks/locations/isCursorAtStartOfBlockType.js +19 -0
  30. package/dist/esm/MarkdownControl/plugins/blocks/locations/isCursorAtStartOfNonEmptyHeading.js +20 -0
  31. package/dist/esm/MarkdownControl/plugins/blocks/locations/isCursorInBlockType.js +20 -0
  32. package/dist/esm/MarkdownControl/plugins/blocks/locations/isCursorInNonDefaultBlock.js +20 -0
  33. package/dist/esm/MarkdownControl/plugins/blocks/transforms/splitIntoParagraph.js +23 -0
  34. package/dist/esm/MarkdownControl/plugins/blocks/transforms/unwrapIfCursorAtStart.js +36 -0
  35. package/dist/esm/MarkdownControl/plugins/blocks/transforms/wrapListItemsInBlock.js +27 -0
  36. package/dist/esm/MarkdownControl/plugins/blocks/withBlocks.js +19 -0
  37. package/dist/esm/MarkdownControl/plugins/inlines/events/keyDown.js +39 -0
  38. package/dist/esm/MarkdownControl/plugins/inlines/events/keyDownShiftEnter.js +27 -0
  39. package/dist/esm/MarkdownControl/plugins/inlines/events/toggleLink.js +24 -0
  40. package/dist/esm/MarkdownControl/plugins/inlines/events/toggleMark.js +18 -0
  41. package/dist/esm/MarkdownControl/plugins/inlines/locations/isMarkActive.js +17 -0
  42. package/dist/esm/MarkdownControl/plugins/inlines/selectors/getActiveLink.js +15 -0
  43. package/dist/esm/MarkdownControl/plugins/inlines/transforms/unwrapLink.js +14 -0
  44. package/dist/esm/MarkdownControl/plugins/inlines/transforms/wrapLink.js +45 -0
  45. package/dist/esm/MarkdownControl/plugins/inlines/withInlines.js +23 -0
  46. package/dist/esm/MarkdownControl/plugins/lists/events/keyDown.js +38 -0
  47. package/dist/esm/MarkdownControl/plugins/lists/events/keyDownBackspace.js +29 -0
  48. package/dist/esm/MarkdownControl/plugins/lists/events/keyDownEnter.js +44 -0
  49. package/dist/esm/MarkdownControl/plugins/lists/events/keyDownShiftTab.js +32 -0
  50. package/dist/esm/MarkdownControl/plugins/lists/events/keyDownTab.js +57 -0
  51. package/dist/esm/MarkdownControl/plugins/lists/events/toggleListType.js +29 -0
  52. package/dist/esm/MarkdownControl/plugins/lists/locations/isCursorAtListItemStart.js +18 -0
  53. package/dist/esm/MarkdownControl/plugins/lists/locations/isCursorAtNoninitialParagraphStart.js +17 -0
  54. package/dist/esm/MarkdownControl/plugins/lists/locations/isCursorInItemContainingNestedList.js +14 -0
  55. package/dist/esm/MarkdownControl/plugins/lists/locations/isCursorInListItem.js +20 -0
  56. package/dist/esm/MarkdownControl/plugins/lists/locations/isSelectionWithinNoninitialListItem.js +18 -0
  57. package/dist/esm/MarkdownControl/plugins/lists/selectors/getListContainedInListItem.js +18 -0
  58. package/dist/esm/MarkdownControl/plugins/lists/selectors/getLowestAncestorList.js +15 -0
  59. package/dist/esm/MarkdownControl/plugins/lists/selectors/getLowestAncestorQuote.js +15 -0
  60. package/dist/esm/MarkdownControl/plugins/lists/transforms/changeListType.js +24 -0
  61. package/dist/esm/MarkdownControl/plugins/lists/transforms/convertParagraphToListItem.js +27 -0
  62. package/dist/esm/MarkdownControl/plugins/lists/transforms/liftFirstMatchedParent.js +20 -0
  63. package/dist/esm/MarkdownControl/plugins/lists/transforms/liftListItem.js +37 -0
  64. package/dist/esm/MarkdownControl/plugins/lists/transforms/mergeWithPreviousListItem.js +19 -0
  65. package/dist/esm/MarkdownControl/plugins/lists/transforms/moveListToListItem.js +18 -0
  66. package/dist/esm/MarkdownControl/plugins/lists/transforms/splitListItem.js +37 -0
  67. package/dist/esm/MarkdownControl/plugins/lists/transforms/splitToNestedList.js +37 -0
  68. package/dist/esm/MarkdownControl/plugins/lists/transforms/unwrapFirstMatchedParent.js +14 -0
  69. package/dist/esm/MarkdownControl/plugins/lists/transforms/unwrapSelectionFromList.js +25 -0
  70. package/dist/esm/MarkdownControl/plugins/lists/transforms/wrapFirstMatchedParent.js +14 -0
  71. package/dist/esm/MarkdownControl/plugins/lists/transforms/wrapSelectionInList.js +27 -0
  72. package/dist/esm/MarkdownControl/plugins/lists/withLists.js +67 -0
  73. package/dist/esm/MarkdownControl/plugins/matchers/lowestMatchedAncestor.js +13 -0
  74. package/dist/esm/MarkdownControl/plugins/matchers/matchLink.js +14 -0
  75. package/dist/esm/MarkdownControl/plugins/matchers/matchedAncestors.js +15 -0
  76. package/dist/esm/MarkdownControl/plugins/shortcodes/insertShortcode.js +35 -0
  77. package/dist/esm/MarkdownControl/plugins/shortcodes/locations/isCursorInEmptyParagraph.js +20 -0
  78. package/dist/esm/MarkdownControl/plugins/shortcodes/withShortcodes.js +30 -0
  79. package/dist/esm/MarkdownControl/plugins/util.js +18 -0
  80. package/dist/esm/MarkdownControl/renderers.js +326 -0
  81. package/dist/esm/MarkdownPreview.js +47 -0
  82. package/dist/esm/index.js +31 -0
  83. package/dist/esm/regexHelper.js +151 -0
  84. package/dist/esm/schema.js +35 -0
  85. package/dist/esm/serializers/index.js +233 -0
  86. package/dist/esm/serializers/rehypePaperEmoji.js +24 -0
  87. package/dist/esm/serializers/remarkAllowHtmlEntities.js +55 -0
  88. package/dist/esm/serializers/remarkAssertParents.js +89 -0
  89. package/dist/esm/serializers/remarkEscapeMarkdownEntities.js +271 -0
  90. package/dist/esm/serializers/remarkImagesToText.js +41 -0
  91. package/dist/esm/serializers/remarkPaddedLinks.js +127 -0
  92. package/dist/esm/serializers/remarkRehypeShortcodes.js +93 -0
  93. package/dist/esm/serializers/remarkShortcodes.js +123 -0
  94. package/dist/esm/serializers/remarkSlate.js +518 -0
  95. package/dist/esm/serializers/remarkSquashReferences.js +88 -0
  96. package/dist/esm/serializers/remarkStripTrailingBreaks.js +61 -0
  97. package/dist/esm/serializers/remarkWrapHtml.js +24 -0
  98. package/dist/esm/serializers/slateRemark.js +512 -0
  99. package/dist/esm/styles.js +18 -0
  100. package/dist/esm/types.js +10 -0
  101. package/package.json +64 -0
  102. package/src/MarkdownControl/RawEditor.js +100 -0
  103. package/src/MarkdownControl/Toolbar.js +278 -0
  104. package/src/MarkdownControl/ToolbarButton.js +48 -0
  105. package/src/MarkdownControl/VisualEditor.js +293 -0
  106. package/src/MarkdownControl/__tests__/VisualEditor.spec.js +56 -0
  107. package/src/MarkdownControl/__tests__/__snapshots__/parser.spec.js.snap +543 -0
  108. package/src/MarkdownControl/__tests__/parser.spec.js +668 -0
  109. package/src/MarkdownControl/components/Shortcode.js +74 -0
  110. package/src/MarkdownControl/components/VoidBlock.js +58 -0
  111. package/src/MarkdownControl/index.js +125 -0
  112. package/src/MarkdownControl/plugins/BreakToDefaultBlock.js +23 -0
  113. package/src/MarkdownControl/plugins/CloseBlock.js +25 -0
  114. package/src/MarkdownControl/plugins/CommandsAndQueries.js +156 -0
  115. package/src/MarkdownControl/plugins/ForceInsert.js +38 -0
  116. package/src/MarkdownControl/plugins/Hotkey.js +29 -0
  117. package/src/MarkdownControl/plugins/LineBreak.js +15 -0
  118. package/src/MarkdownControl/plugins/Link.js +40 -0
  119. package/src/MarkdownControl/plugins/blocks/defaultEmptyBlock.js +8 -0
  120. package/src/MarkdownControl/plugins/blocks/events/keyDown.js +47 -0
  121. package/src/MarkdownControl/plugins/blocks/events/keyDownBackspace.js +27 -0
  122. package/src/MarkdownControl/plugins/blocks/events/keyDownEnter.js +26 -0
  123. package/src/MarkdownControl/plugins/blocks/events/toggleBlock.js +39 -0
  124. package/src/MarkdownControl/plugins/blocks/locations/areCurrentAndPreviousBlocksOfType.js +15 -0
  125. package/src/MarkdownControl/plugins/blocks/locations/getListTypeAtCursor.js +11 -0
  126. package/src/MarkdownControl/plugins/blocks/locations/isCursorAtEndOfParagraph.js +14 -0
  127. package/src/MarkdownControl/plugins/blocks/locations/isCursorAtStartOfBlockType.js +14 -0
  128. package/src/MarkdownControl/plugins/blocks/locations/isCursorAtStartOfNonEmptyHeading.js +22 -0
  129. package/src/MarkdownControl/plugins/blocks/locations/isCursorInBlockType.js +27 -0
  130. package/src/MarkdownControl/plugins/blocks/locations/isCursorInNonDefaultBlock.js +17 -0
  131. package/src/MarkdownControl/plugins/blocks/transforms/splitIntoParagraph.js +13 -0
  132. package/src/MarkdownControl/plugins/blocks/transforms/unwrapIfCursorAtStart.js +30 -0
  133. package/src/MarkdownControl/plugins/blocks/transforms/wrapListItemsInBlock.js +11 -0
  134. package/src/MarkdownControl/plugins/blocks/withBlocks.js +15 -0
  135. package/src/MarkdownControl/plugins/inlines/events/keyDown.js +38 -0
  136. package/src/MarkdownControl/plugins/inlines/events/keyDownShiftEnter.js +28 -0
  137. package/src/MarkdownControl/plugins/inlines/events/toggleLink.js +17 -0
  138. package/src/MarkdownControl/plugins/inlines/events/toggleMark.js +13 -0
  139. package/src/MarkdownControl/plugins/inlines/locations/isMarkActive.js +11 -0
  140. package/src/MarkdownControl/plugins/inlines/selectors/getActiveLink.js +10 -0
  141. package/src/MarkdownControl/plugins/inlines/transforms/unwrapLink.js +9 -0
  142. package/src/MarkdownControl/plugins/inlines/transforms/wrapLink.js +30 -0
  143. package/src/MarkdownControl/plugins/inlines/withInlines.js +20 -0
  144. package/src/MarkdownControl/plugins/lists/events/keyDown.js +34 -0
  145. package/src/MarkdownControl/plugins/lists/events/keyDownBackspace.js +31 -0
  146. package/src/MarkdownControl/plugins/lists/events/keyDownEnter.js +39 -0
  147. package/src/MarkdownControl/plugins/lists/events/keyDownShiftTab.js +27 -0
  148. package/src/MarkdownControl/plugins/lists/events/keyDownTab.js +55 -0
  149. package/src/MarkdownControl/plugins/lists/events/toggleListType.js +23 -0
  150. package/src/MarkdownControl/plugins/lists/locations/isCursorAtListItemStart.js +13 -0
  151. package/src/MarkdownControl/plugins/lists/locations/isCursorAtNoninitialParagraphStart.js +11 -0
  152. package/src/MarkdownControl/plugins/lists/locations/isCursorInItemContainingNestedList.js +9 -0
  153. package/src/MarkdownControl/plugins/lists/locations/isCursorInListItem.js +21 -0
  154. package/src/MarkdownControl/plugins/lists/locations/isSelectionWithinNoninitialListItem.js +14 -0
  155. package/src/MarkdownControl/plugins/lists/selectors/getListContainedInListItem.js +12 -0
  156. package/src/MarkdownControl/plugins/lists/selectors/getLowestAncestorList.js +11 -0
  157. package/src/MarkdownControl/plugins/lists/selectors/getLowestAncestorQuote.js +11 -0
  158. package/src/MarkdownControl/plugins/lists/transforms/changeListType.js +16 -0
  159. package/src/MarkdownControl/plugins/lists/transforms/convertParagraphToListItem.js +19 -0
  160. package/src/MarkdownControl/plugins/lists/transforms/liftFirstMatchedParent.js +11 -0
  161. package/src/MarkdownControl/plugins/lists/transforms/liftListItem.js +30 -0
  162. package/src/MarkdownControl/plugins/lists/transforms/mergeWithPreviousListItem.js +13 -0
  163. package/src/MarkdownControl/plugins/lists/transforms/moveListToListItem.js +13 -0
  164. package/src/MarkdownControl/plugins/lists/transforms/splitListItem.js +33 -0
  165. package/src/MarkdownControl/plugins/lists/transforms/splitToNestedList.js +33 -0
  166. package/src/MarkdownControl/plugins/lists/transforms/unwrapFirstMatchedParent.js +9 -0
  167. package/src/MarkdownControl/plugins/lists/transforms/unwrapSelectionFromList.js +14 -0
  168. package/src/MarkdownControl/plugins/lists/transforms/wrapFirstMatchedParent.js +9 -0
  169. package/src/MarkdownControl/plugins/lists/transforms/wrapSelectionInList.js +17 -0
  170. package/src/MarkdownControl/plugins/lists/withLists.js +66 -0
  171. package/src/MarkdownControl/plugins/matchers/lowestMatchedAncestor.js +6 -0
  172. package/src/MarkdownControl/plugins/matchers/matchLink.js +9 -0
  173. package/src/MarkdownControl/plugins/matchers/matchedAncestors.js +18 -0
  174. package/src/MarkdownControl/plugins/shortcodes/insertShortcode.js +34 -0
  175. package/src/MarkdownControl/plugins/shortcodes/locations/isCursorInEmptyParagraph.js +17 -0
  176. package/src/MarkdownControl/plugins/shortcodes/withShortcodes.js +26 -0
  177. package/src/MarkdownControl/plugins/util.js +11 -0
  178. package/src/MarkdownControl/renderers.js +352 -0
  179. package/src/MarkdownPreview.js +27 -0
  180. package/src/__tests__/__snapshots__/renderer.spec.js.snap +239 -0
  181. package/src/__tests__/renderer.spec.js +261 -0
  182. package/src/index.js +16 -0
  183. package/src/regexHelper.js +137 -0
  184. package/src/schema.js +35 -0
  185. package/src/serializers/__tests__/__fixtures__/commonmarkExpected.json +625 -0
  186. package/src/serializers/__tests__/__fixtures__/duplicate_marks_github_issue_3280.md +1 -0
  187. package/src/serializers/__tests__/commonmark.spec.js +110 -0
  188. package/src/serializers/__tests__/index.spec.js +52 -0
  189. package/src/serializers/__tests__/remarkAllowHtmlEntities.spec.js +25 -0
  190. package/src/serializers/__tests__/remarkAssertParents.spec.js +171 -0
  191. package/src/serializers/__tests__/remarkEscapeMarkdownEntities.spec.js +84 -0
  192. package/src/serializers/__tests__/remarkPaddedLinks.spec.js +43 -0
  193. package/src/serializers/__tests__/remarkPlugins.spec.js +299 -0
  194. package/src/serializers/__tests__/remarkShortcodes.spec.js +106 -0
  195. package/src/serializers/__tests__/remarkSlate.spec.js +67 -0
  196. package/src/serializers/__tests__/remarkStripTrailingBreaks.spec.js +23 -0
  197. package/src/serializers/__tests__/slate.spec.js +300 -0
  198. package/src/serializers/index.js +226 -0
  199. package/src/serializers/rehypePaperEmoji.js +16 -0
  200. package/src/serializers/remarkAllowHtmlEntities.js +58 -0
  201. package/src/serializers/remarkAssertParents.js +83 -0
  202. package/src/serializers/remarkEscapeMarkdownEntities.js +269 -0
  203. package/src/serializers/remarkImagesToText.js +26 -0
  204. package/src/serializers/remarkPaddedLinks.js +120 -0
  205. package/src/serializers/remarkRehypeShortcodes.js +67 -0
  206. package/src/serializers/remarkShortcodes.js +106 -0
  207. package/src/serializers/remarkSlate.js +425 -0
  208. package/src/serializers/remarkSquashReferences.js +73 -0
  209. package/src/serializers/remarkStripTrailingBreaks.js +56 -0
  210. package/src/serializers/remarkWrapHtml.js +20 -0
  211. package/src/serializers/slateRemark.js +468 -0
  212. package/src/styles.js +13 -0
  213. package/src/types.js +3 -0
  214. package/test-helpers/h.js +32 -0
  215. package/webpack.config.js +3 -0
@@ -0,0 +1,425 @@
1
+ import { isEmpty, isArray, flatMap, map, flatten, isEqual } from 'lodash';
2
+
3
+ /**
4
+ * Map of MDAST node types to Slate node types.
5
+ */
6
+ const typeMap = {
7
+ root: 'root',
8
+ paragraph: 'paragraph',
9
+ blockquote: 'quote',
10
+ code: 'code-block',
11
+ listItem: 'list-item',
12
+ table: 'table',
13
+ tableRow: 'table-row',
14
+ tableCell: 'table-cell',
15
+ thematicBreak: 'thematic-break',
16
+ link: 'link',
17
+ image: 'image',
18
+ shortcode: 'shortcode',
19
+ };
20
+
21
+ /**
22
+ * Map of MDAST node types to Slate mark types.
23
+ */
24
+ const markMap = {
25
+ strong: 'bold',
26
+ emphasis: 'italic',
27
+ delete: 'delete',
28
+ inlineCode: 'code',
29
+ };
30
+
31
+ function isText(node) {
32
+ return !!node.text;
33
+ }
34
+
35
+ function isMarksEqual(node1, node2) {
36
+ return isEqual(node1.marks, node2.marks);
37
+ }
38
+
39
+ export function mergeAdjacentTexts(children) {
40
+ if (children.length <= 0) {
41
+ return children;
42
+ }
43
+
44
+ const mergedChildren = [];
45
+
46
+ let isMerging = false;
47
+ let current;
48
+
49
+ for (let i = 0; i < children.length - 1; i++) {
50
+ if (!isMerging) {
51
+ current = children[i];
52
+ }
53
+ const next = children[i + 1];
54
+ if (isText(current) && isText(next) && isMarksEqual(current, next)) {
55
+ isMerging = true;
56
+ current = { ...current, text: `${current.text}${next.text}` };
57
+ } else {
58
+ mergedChildren.push(current);
59
+ isMerging = false;
60
+ }
61
+ }
62
+
63
+ if (isMerging) {
64
+ mergedChildren.push(current);
65
+ } else {
66
+ mergedChildren.push(children[children.length - 1]);
67
+ }
68
+
69
+ return mergedChildren;
70
+ }
71
+
72
+ /**
73
+ * A Remark plugin for converting an MDAST to Slate Raw AST. Remark plugins
74
+ * return a `transformNode` function that receives the MDAST as it's first argument.
75
+ */
76
+ export default function remarkToSlate({ voidCodeBlock } = {}) {
77
+ return transformNode;
78
+
79
+ function transformNode(node) {
80
+ /**
81
+ * Call `transformNode` recursively on child nodes.
82
+ *
83
+ * If a node returns a falsey value, filter it out. Some nodes do not
84
+ * translate from MDAST to Slate, such as definitions for link/image
85
+ * references or footnotes.
86
+ */
87
+ let children =
88
+ !['strong', 'emphasis', 'delete'].includes(node.type) &&
89
+ !isEmpty(node.children) &&
90
+ flatMap(node.children, transformNode).filter(val => val);
91
+
92
+ if (Array.isArray(children)) {
93
+ // Merge adjacent text nodes with the same marks to conform to slate schema
94
+ children = mergeAdjacentTexts(children);
95
+ }
96
+
97
+ /**
98
+ * Run individual nodes through the conversion factory.
99
+ */
100
+ const output = convertNode(node, children || undefined);
101
+ return output;
102
+ }
103
+
104
+ /**
105
+ * Add nodes to a parent node only if `nodes` is truthy.
106
+ */
107
+ function addNodes(parent, nodes) {
108
+ return nodes ? { ...parent, children: nodes } : parent;
109
+ }
110
+
111
+ /**
112
+ * Create a Slate Block node.
113
+ */
114
+ function createBlock(type, nodes, props = {}) {
115
+ if (!isArray(nodes)) {
116
+ props = nodes;
117
+ nodes = undefined;
118
+ }
119
+
120
+ // Ensure block nodes have at least one text child to conform to slate schema
121
+ const children = isEmpty(nodes) ? [createText('')] : nodes;
122
+ const node = { type, ...props };
123
+ return addNodes(node, children);
124
+ }
125
+
126
+ /**
127
+ * Create a Slate Inline node.
128
+ */
129
+ function createInline(type, props = {}, nodes) {
130
+ const node = { type, ...props };
131
+
132
+ // Ensure inline nodes have at least one text child to conform to slate schema
133
+ const children = isEmpty(nodes) ? [createText('')] : nodes;
134
+ return addNodes(node, children);
135
+ }
136
+
137
+ /**
138
+ * Create a Slate Raw text node.
139
+ */
140
+ function createText(node) {
141
+ const newNode = {};
142
+ if (typeof node === 'string') {
143
+ return { ...newNode, text: node };
144
+ }
145
+ const { text, marks } = node;
146
+ return normalizeMarks({ ...newNode, text, marks });
147
+ }
148
+
149
+ function processMarkChild(childNode, marks) {
150
+ switch (childNode.type) {
151
+ /**
152
+ * If a text node is a direct child of the current node, it should be
153
+ * set aside as a text, and all marks that have been collected in the
154
+ * `marks` array should apply to that specific text.
155
+ */
156
+ case 'html':
157
+ case 'text':
158
+ return { ...convertNode(childNode), marks };
159
+
160
+ /**
161
+ * MDAST inline code nodes don't have children, just a text value, similar
162
+ * to a text node, so it receives the same treatment as a text node, but we
163
+ * first add the inline code mark to the marks array.
164
+ */
165
+ case 'inlineCode': {
166
+ return { ...convertNode(childNode), marks: [...marks, { type: 'code' }] };
167
+ }
168
+
169
+ /**
170
+ * Process nested style nodes. The recursive results should be pushed into
171
+ * the texts array. This way, every MDAST nested text structure becomes a
172
+ * flat array of texts that can serve as the value of a single Slate Raw
173
+ * text node.
174
+ */
175
+ case 'strong':
176
+ case 'emphasis':
177
+ case 'delete':
178
+ return processMarkNode(childNode, marks);
179
+
180
+ case 'link': {
181
+ const nodes = map(childNode.children, child =>
182
+ normalizeMarks(processMarkChild(child, marks)),
183
+ );
184
+ const result = convertNode(childNode, flatten(nodes));
185
+ return result;
186
+ }
187
+
188
+ /**
189
+ * Remaining nodes simply need mark data added to them, and to then be
190
+ * added into the cumulative children array.
191
+ */
192
+ default:
193
+ return transformNode({ ...childNode, data: { ...childNode.data, marks } });
194
+ }
195
+ }
196
+
197
+ function processMarkNode(node, parentMarks = []) {
198
+ /**
199
+ * Add the current node's mark type to the marks collected from parent
200
+ * mark nodes, if any.
201
+ */
202
+ const markType = markMap[node.type];
203
+ const marks = markType
204
+ ? [...parentMarks.filter(({ type }) => type !== markType), { type: markType }]
205
+ : parentMarks;
206
+
207
+ const children = flatMap(node.children, child =>
208
+ normalizeMarks(processMarkChild(child, marks)),
209
+ );
210
+
211
+ return children;
212
+ }
213
+
214
+ function normalizeMarks(node) {
215
+ if (node.marks) {
216
+ node.marks.forEach(mark => {
217
+ node[mark.type] = true;
218
+ });
219
+ }
220
+
221
+ return node;
222
+ }
223
+
224
+ /**
225
+ * Convert a single MDAST node to a Slate Raw node. Uses local node factories
226
+ * that mimic the unist-builder function utilized in the slateRemark
227
+ * transformer.
228
+ */
229
+ function convertNode(node, nodes) {
230
+ switch (node.type) {
231
+ /**
232
+ * General
233
+ *
234
+ * Convert simple cases that only require a type and children, with no
235
+ * additional properties.
236
+ */
237
+ case 'root':
238
+ case 'paragraph':
239
+ case 'blockquote':
240
+ case 'tableRow':
241
+ case 'tableCell': {
242
+ return createBlock(typeMap[node.type], nodes);
243
+ }
244
+
245
+ /**
246
+ * List Items
247
+ *
248
+ * Markdown list items can be empty, but a list item in the Slate schema
249
+ * should at least have an empty paragraph node.
250
+ */
251
+ case 'listItem': {
252
+ const children = isEmpty(nodes) ? [createBlock('paragraph')] : nodes;
253
+ return createBlock(typeMap[node.type], children);
254
+ }
255
+
256
+ /**
257
+ * Shortcodes
258
+ *
259
+ * Shortcode nodes are represented as "void" blocks in the Slate AST. They
260
+ * maintain the same data as MDAST shortcode nodes. Slate void blocks must
261
+ * contain a blank text node.
262
+ */
263
+ case 'shortcode': {
264
+ const nodes = [createText('')];
265
+ const data = { ...node.data, id: node.data.shortcode, shortcodeNew: true };
266
+ return createBlock(typeMap[node.type], nodes, { data });
267
+ }
268
+
269
+ case 'text': {
270
+ const text = node.value;
271
+ return createText(text);
272
+ }
273
+
274
+ /**
275
+ * HTML
276
+ *
277
+ * HTML nodes contain plain text like text nodes, except they only contain
278
+ * HTML. Our serialization results in non-HTML being placed in HTML nodes
279
+ * sometimes to ensure that we're never escaping HTML from the rich text
280
+ * editor. We do not replace line feeds in HTML because the HTML is raw
281
+ * in the rich text editor, so the writer knows they're writing HTML, and
282
+ * should expect soft breaks to be visually absent in the rendered HTML.
283
+ */
284
+ case 'html': {
285
+ return createText(node.value);
286
+ }
287
+
288
+ /**
289
+ * Inline Code
290
+ *
291
+ * Inline code nodes from an MDAST are represented in our Slate schema as
292
+ * text nodes with a "code" mark. We manually create the text containing
293
+ * the inline code value and a "code" mark, and place it in an array for use
294
+ * as a Slate text node's children array.
295
+ */
296
+ case 'inlineCode': {
297
+ return createText({ text: node.value, code: true, marks: [{ type: 'code' }] });
298
+ }
299
+
300
+ /**
301
+ * Marks
302
+ *
303
+ * Marks are typically decorative sub-types that apply to text nodes. In an
304
+ * MDAST, marks are nodes that can contain other nodes. This nested
305
+ * hierarchy has to be flattened and split into distinct text nodes with
306
+ * their own set of marks.
307
+ */
308
+ case 'strong':
309
+ case 'emphasis':
310
+ case 'delete': {
311
+ return processMarkNode(node);
312
+ }
313
+
314
+ /**
315
+ * Headings
316
+ *
317
+ * MDAST headings use a single type with a separate "depth" property to
318
+ * indicate the heading level, while the Slate schema uses a separate node
319
+ * type for each heading level. Here we get the proper Slate node name based
320
+ * on the MDAST node depth.
321
+ */
322
+ case 'heading': {
323
+ const depthMap = { 1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five', 6: 'six' };
324
+ const slateType = `heading-${depthMap[node.depth]}`;
325
+ return createBlock(slateType, nodes);
326
+ }
327
+
328
+ /**
329
+ * Code Blocks
330
+ *
331
+ * MDAST code blocks are a distinct node type with a simple text value. We
332
+ * convert that value into a nested child text node for Slate. If a void
333
+ * node is required due to a custom code block handler, the value is
334
+ * stored in the "code" data property instead. We also carry over the "lang"
335
+ * data property if it's defined.
336
+ */
337
+ case 'code': {
338
+ const data = {
339
+ lang: node.lang,
340
+ ...(voidCodeBlock ? { code: node.value } : {}),
341
+ shortcode: 'code-block',
342
+ shortcodeData: {
343
+ code: node.value,
344
+ lang: node.lang,
345
+ },
346
+ };
347
+ const text = createText(voidCodeBlock ? '' : node.value);
348
+ const nodes = [text];
349
+ const block = createBlock('shortcode', nodes, { data });
350
+ return block;
351
+ }
352
+
353
+ /**
354
+ * Lists
355
+ *
356
+ * MDAST has a single list type and an "ordered" property. We derive that
357
+ * information into the Slate schema's distinct list node types. We also
358
+ * include the "start" property, which indicates the number an ordered list
359
+ * starts at, if defined.
360
+ */
361
+ case 'list': {
362
+ const slateType = node.ordered ? 'numbered-list' : 'bulleted-list';
363
+ const data = { start: node.start };
364
+ return createBlock(slateType, nodes, { data });
365
+ }
366
+
367
+ /**
368
+ * Breaks
369
+ *
370
+ * MDAST soft break nodes represent a trailing double space or trailing
371
+ * slash from a Markdown document. In Slate, these are simply transformed to
372
+ * line breaks within a text node.
373
+ */
374
+ case 'break': {
375
+ const { data } = node;
376
+ return createInline('break', { data });
377
+ }
378
+
379
+ /**
380
+ * Thematic Breaks
381
+ *
382
+ * Thematic breaks are void nodes in the Slate schema.
383
+ */
384
+ case 'thematicBreak': {
385
+ return createBlock(typeMap[node.type]);
386
+ }
387
+
388
+ /**
389
+ * Links
390
+ *
391
+ * MDAST stores the link attributes directly on the node, while our Slate
392
+ * schema references them in the data object.
393
+ */
394
+ case 'link': {
395
+ const { title, url, data } = node;
396
+ const newData = { ...data, title, url };
397
+ return createInline(typeMap[node.type], { data: newData }, nodes);
398
+ }
399
+
400
+ /**
401
+ * Images
402
+ *
403
+ * Identical to link nodes except for the lack of child nodes and addition
404
+ * of alt attribute data MDAST stores the link attributes directly on the
405
+ * node, while our Slate schema references them in the data object.
406
+ */
407
+ case 'image': {
408
+ const { title, url, alt, data } = node;
409
+ const newData = { ...data, title, alt, url };
410
+ return createInline(typeMap[node.type], { data: newData });
411
+ }
412
+
413
+ /**
414
+ * Tables
415
+ *
416
+ * Tables are parsed separately because they may include an "align"
417
+ * property, which should be passed to the Slate node.
418
+ */
419
+ case 'table': {
420
+ const data = { align: node.align };
421
+ return createBlock(typeMap[node.type], nodes, { data });
422
+ }
423
+ }
424
+ }
425
+ }
@@ -0,0 +1,73 @@
1
+ import { without, flatten } from 'lodash';
2
+ import u from 'unist-builder';
3
+ import mdastDefinitions from 'mdast-util-definitions';
4
+
5
+ /**
6
+ * Raw markdown may contain image references or link references. Because there
7
+ * is no way to maintain these references within the Slate AST, we convert image
8
+ * and link references to standard images and links by putting their url's
9
+ * inline. The definitions are then removed from the document.
10
+ *
11
+ * For example, the following markdown:
12
+ *
13
+ * ```
14
+ * ![alpha][bravo]
15
+ *
16
+ * [bravo]: http://example.com/example.jpg
17
+ * ```
18
+ *
19
+ * Yields:
20
+ *
21
+ * ```
22
+ * ![alpha](http://example.com/example.jpg)
23
+ * ```
24
+ *
25
+ */
26
+ export default function remarkSquashReferences() {
27
+ return getTransform;
28
+
29
+ function getTransform(node) {
30
+ const getDefinition = mdastDefinitions(node);
31
+ return transform.call(null, getDefinition, node);
32
+ }
33
+
34
+ function transform(getDefinition, node) {
35
+ /**
36
+ * Bind the `getDefinition` function to `transform` and recursively map all
37
+ * nodes.
38
+ */
39
+ const boundTransform = transform.bind(null, getDefinition);
40
+ const children = node.children ? node.children.map(boundTransform) : node.children;
41
+
42
+ /**
43
+ * Combine reference and definition nodes into standard image and link
44
+ * nodes.
45
+ */
46
+ if (['imageReference', 'linkReference'].includes(node.type)) {
47
+ const type = node.type === 'imageReference' ? 'image' : 'link';
48
+ const definition = getDefinition(node.identifier);
49
+
50
+ if (definition) {
51
+ const { title, url } = definition;
52
+ return u(type, { title, url, alt: node.alt }, children);
53
+ }
54
+
55
+ const pre = u('text', node.type === 'imageReference' ? '![' : '[');
56
+ const post = u('text', ']');
57
+ const nodes = children || [u('text', node.alt)];
58
+ return [pre, ...nodes, post];
59
+ }
60
+
61
+ /**
62
+ * Remove definition nodes and filter the resulting null values from the
63
+ * filtered children array.
64
+ */
65
+ if (node.type === 'definition') {
66
+ return null;
67
+ }
68
+
69
+ const filteredChildren = without(children, null);
70
+
71
+ return { ...node, children: flatten(filteredChildren) };
72
+ }
73
+ }
@@ -0,0 +1,56 @@
1
+ import mdastToString from 'mdast-util-to-string';
2
+
3
+ /**
4
+ * Removes break nodes that are at the end of a block.
5
+ *
6
+ * When a trailing double space or backslash is encountered at the end of a
7
+ * markdown block, Remark will interpret the character(s) literally, as only
8
+ * break entities followed by text qualify as breaks. A manually created MDAST,
9
+ * however, may have such entities, and users of visual editors shouldn't see
10
+ * these artifacts in resulting markdown.
11
+ */
12
+ export default function remarkStripTrailingBreaks() {
13
+ function transform(node) {
14
+ if (node.children) {
15
+ node.children = node.children
16
+ .map((child, idx, children) => {
17
+ /**
18
+ * Only touch break nodes. Convert all subsequent nodes to their text
19
+ * value and exclude the break node if no non-whitespace characters
20
+ * are found.
21
+ */
22
+ if (child.type === 'break') {
23
+ const subsequentNodes = children.slice(idx + 1);
24
+
25
+ /**
26
+ * Create a small MDAST so that mdastToString can process all
27
+ * siblings as children of one node rather than making multiple
28
+ * calls.
29
+ */
30
+ const fragment = { type: 'root', children: subsequentNodes };
31
+ const subsequentText = mdastToString(fragment);
32
+ return subsequentText.trim() ? child : null;
33
+ }
34
+
35
+ /**
36
+ * Always return the child if not a break.
37
+ */
38
+ return child;
39
+ })
40
+
41
+ /**
42
+ * Because some break nodes may be excluded, we filter out the resulting
43
+ * null values.
44
+ */
45
+ .filter(child => child)
46
+
47
+ /**
48
+ * Recurse through the MDAST by transforming each individual child node.
49
+ */
50
+ .map(transform);
51
+ }
52
+ return node;
53
+ }
54
+
55
+ return transform;
56
+ }
@@ -0,0 +1,20 @@
1
+ import u from 'unist-builder';
2
+
3
+ /**
4
+ * Ensure that top level 'html' type nodes are wrapped in paragraphs. Html nodes
5
+ * are used for text nodes that we don't want Remark or Rehype to parse.
6
+ */
7
+ export default function remarkWrapHtml() {
8
+ function transform(tree) {
9
+ tree.children = tree.children.map(node => {
10
+ if (node.type === 'html') {
11
+ return u('paragraph', [node]);
12
+ }
13
+ return node;
14
+ });
15
+
16
+ return tree;
17
+ }
18
+
19
+ return transform;
20
+ }