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.
- package/CHANGELOG.md +820 -0
- package/LICENSE +22 -0
- package/README.md +9 -0
- package/dist/decap-cms-widget-markdown.js +9 -0
- package/dist/decap-cms-widget-markdown.js.LICENSE.txt +37 -0
- package/dist/decap-cms-widget-markdown.js.map +1 -0
- package/dist/esm/MarkdownControl/RawEditor.js +106 -0
- package/dist/esm/MarkdownControl/Toolbar.js +216 -0
- package/dist/esm/MarkdownControl/ToolbarButton.js +43 -0
- package/dist/esm/MarkdownControl/VisualEditor.js +268 -0
- package/dist/esm/MarkdownControl/components/Shortcode.js +78 -0
- package/dist/esm/MarkdownControl/components/VoidBlock.js +55 -0
- package/dist/esm/MarkdownControl/index.js +129 -0
- package/dist/esm/MarkdownControl/plugins/BreakToDefaultBlock.js +34 -0
- package/dist/esm/MarkdownControl/plugins/CloseBlock.js +36 -0
- package/dist/esm/MarkdownControl/plugins/CommandsAndQueries.js +188 -0
- package/dist/esm/MarkdownControl/plugins/ForceInsert.js +49 -0
- package/dist/esm/MarkdownControl/plugins/Hotkey.js +35 -0
- package/dist/esm/MarkdownControl/plugins/LineBreak.js +21 -0
- package/dist/esm/MarkdownControl/plugins/Link.js +56 -0
- package/dist/esm/MarkdownControl/plugins/blocks/defaultEmptyBlock.js +16 -0
- package/dist/esm/MarkdownControl/plugins/blocks/events/keyDown.js +47 -0
- package/dist/esm/MarkdownControl/plugins/blocks/events/keyDownBackspace.js +26 -0
- package/dist/esm/MarkdownControl/plugins/blocks/events/keyDownEnter.js +28 -0
- package/dist/esm/MarkdownControl/plugins/blocks/events/toggleBlock.js +46 -0
- package/dist/esm/MarkdownControl/plugins/blocks/locations/areCurrentAndPreviousBlocksOfType.js +20 -0
- package/dist/esm/MarkdownControl/plugins/blocks/locations/getListTypeAtCursor.js +16 -0
- package/dist/esm/MarkdownControl/plugins/blocks/locations/isCursorAtEndOfParagraph.js +19 -0
- package/dist/esm/MarkdownControl/plugins/blocks/locations/isCursorAtStartOfBlockType.js +19 -0
- package/dist/esm/MarkdownControl/plugins/blocks/locations/isCursorAtStartOfNonEmptyHeading.js +20 -0
- package/dist/esm/MarkdownControl/plugins/blocks/locations/isCursorInBlockType.js +20 -0
- package/dist/esm/MarkdownControl/plugins/blocks/locations/isCursorInNonDefaultBlock.js +20 -0
- package/dist/esm/MarkdownControl/plugins/blocks/transforms/splitIntoParagraph.js +23 -0
- package/dist/esm/MarkdownControl/plugins/blocks/transforms/unwrapIfCursorAtStart.js +36 -0
- package/dist/esm/MarkdownControl/plugins/blocks/transforms/wrapListItemsInBlock.js +27 -0
- package/dist/esm/MarkdownControl/plugins/blocks/withBlocks.js +19 -0
- package/dist/esm/MarkdownControl/plugins/inlines/events/keyDown.js +39 -0
- package/dist/esm/MarkdownControl/plugins/inlines/events/keyDownShiftEnter.js +27 -0
- package/dist/esm/MarkdownControl/plugins/inlines/events/toggleLink.js +24 -0
- package/dist/esm/MarkdownControl/plugins/inlines/events/toggleMark.js +18 -0
- package/dist/esm/MarkdownControl/plugins/inlines/locations/isMarkActive.js +17 -0
- package/dist/esm/MarkdownControl/plugins/inlines/selectors/getActiveLink.js +15 -0
- package/dist/esm/MarkdownControl/plugins/inlines/transforms/unwrapLink.js +14 -0
- package/dist/esm/MarkdownControl/plugins/inlines/transforms/wrapLink.js +45 -0
- package/dist/esm/MarkdownControl/plugins/inlines/withInlines.js +23 -0
- package/dist/esm/MarkdownControl/plugins/lists/events/keyDown.js +38 -0
- package/dist/esm/MarkdownControl/plugins/lists/events/keyDownBackspace.js +29 -0
- package/dist/esm/MarkdownControl/plugins/lists/events/keyDownEnter.js +44 -0
- package/dist/esm/MarkdownControl/plugins/lists/events/keyDownShiftTab.js +32 -0
- package/dist/esm/MarkdownControl/plugins/lists/events/keyDownTab.js +57 -0
- package/dist/esm/MarkdownControl/plugins/lists/events/toggleListType.js +29 -0
- package/dist/esm/MarkdownControl/plugins/lists/locations/isCursorAtListItemStart.js +18 -0
- package/dist/esm/MarkdownControl/plugins/lists/locations/isCursorAtNoninitialParagraphStart.js +17 -0
- package/dist/esm/MarkdownControl/plugins/lists/locations/isCursorInItemContainingNestedList.js +14 -0
- package/dist/esm/MarkdownControl/plugins/lists/locations/isCursorInListItem.js +20 -0
- package/dist/esm/MarkdownControl/plugins/lists/locations/isSelectionWithinNoninitialListItem.js +18 -0
- package/dist/esm/MarkdownControl/plugins/lists/selectors/getListContainedInListItem.js +18 -0
- package/dist/esm/MarkdownControl/plugins/lists/selectors/getLowestAncestorList.js +15 -0
- package/dist/esm/MarkdownControl/plugins/lists/selectors/getLowestAncestorQuote.js +15 -0
- package/dist/esm/MarkdownControl/plugins/lists/transforms/changeListType.js +24 -0
- package/dist/esm/MarkdownControl/plugins/lists/transforms/convertParagraphToListItem.js +27 -0
- package/dist/esm/MarkdownControl/plugins/lists/transforms/liftFirstMatchedParent.js +20 -0
- package/dist/esm/MarkdownControl/plugins/lists/transforms/liftListItem.js +37 -0
- package/dist/esm/MarkdownControl/plugins/lists/transforms/mergeWithPreviousListItem.js +19 -0
- package/dist/esm/MarkdownControl/plugins/lists/transforms/moveListToListItem.js +18 -0
- package/dist/esm/MarkdownControl/plugins/lists/transforms/splitListItem.js +37 -0
- package/dist/esm/MarkdownControl/plugins/lists/transforms/splitToNestedList.js +37 -0
- package/dist/esm/MarkdownControl/plugins/lists/transforms/unwrapFirstMatchedParent.js +14 -0
- package/dist/esm/MarkdownControl/plugins/lists/transforms/unwrapSelectionFromList.js +25 -0
- package/dist/esm/MarkdownControl/plugins/lists/transforms/wrapFirstMatchedParent.js +14 -0
- package/dist/esm/MarkdownControl/plugins/lists/transforms/wrapSelectionInList.js +27 -0
- package/dist/esm/MarkdownControl/plugins/lists/withLists.js +67 -0
- package/dist/esm/MarkdownControl/plugins/matchers/lowestMatchedAncestor.js +13 -0
- package/dist/esm/MarkdownControl/plugins/matchers/matchLink.js +14 -0
- package/dist/esm/MarkdownControl/plugins/matchers/matchedAncestors.js +15 -0
- package/dist/esm/MarkdownControl/plugins/shortcodes/insertShortcode.js +35 -0
- package/dist/esm/MarkdownControl/plugins/shortcodes/locations/isCursorInEmptyParagraph.js +20 -0
- package/dist/esm/MarkdownControl/plugins/shortcodes/withShortcodes.js +30 -0
- package/dist/esm/MarkdownControl/plugins/util.js +18 -0
- package/dist/esm/MarkdownControl/renderers.js +326 -0
- package/dist/esm/MarkdownPreview.js +47 -0
- package/dist/esm/index.js +31 -0
- package/dist/esm/regexHelper.js +151 -0
- package/dist/esm/schema.js +35 -0
- package/dist/esm/serializers/index.js +233 -0
- package/dist/esm/serializers/rehypePaperEmoji.js +24 -0
- package/dist/esm/serializers/remarkAllowHtmlEntities.js +55 -0
- package/dist/esm/serializers/remarkAssertParents.js +89 -0
- package/dist/esm/serializers/remarkEscapeMarkdownEntities.js +271 -0
- package/dist/esm/serializers/remarkImagesToText.js +41 -0
- package/dist/esm/serializers/remarkPaddedLinks.js +127 -0
- package/dist/esm/serializers/remarkRehypeShortcodes.js +93 -0
- package/dist/esm/serializers/remarkShortcodes.js +123 -0
- package/dist/esm/serializers/remarkSlate.js +518 -0
- package/dist/esm/serializers/remarkSquashReferences.js +88 -0
- package/dist/esm/serializers/remarkStripTrailingBreaks.js +61 -0
- package/dist/esm/serializers/remarkWrapHtml.js +24 -0
- package/dist/esm/serializers/slateRemark.js +512 -0
- package/dist/esm/styles.js +18 -0
- package/dist/esm/types.js +10 -0
- package/package.json +64 -0
- package/src/MarkdownControl/RawEditor.js +100 -0
- package/src/MarkdownControl/Toolbar.js +278 -0
- package/src/MarkdownControl/ToolbarButton.js +48 -0
- package/src/MarkdownControl/VisualEditor.js +293 -0
- package/src/MarkdownControl/__tests__/VisualEditor.spec.js +56 -0
- package/src/MarkdownControl/__tests__/__snapshots__/parser.spec.js.snap +543 -0
- package/src/MarkdownControl/__tests__/parser.spec.js +668 -0
- package/src/MarkdownControl/components/Shortcode.js +74 -0
- package/src/MarkdownControl/components/VoidBlock.js +58 -0
- package/src/MarkdownControl/index.js +125 -0
- package/src/MarkdownControl/plugins/BreakToDefaultBlock.js +23 -0
- package/src/MarkdownControl/plugins/CloseBlock.js +25 -0
- package/src/MarkdownControl/plugins/CommandsAndQueries.js +156 -0
- package/src/MarkdownControl/plugins/ForceInsert.js +38 -0
- package/src/MarkdownControl/plugins/Hotkey.js +29 -0
- package/src/MarkdownControl/plugins/LineBreak.js +15 -0
- package/src/MarkdownControl/plugins/Link.js +40 -0
- package/src/MarkdownControl/plugins/blocks/defaultEmptyBlock.js +8 -0
- package/src/MarkdownControl/plugins/blocks/events/keyDown.js +47 -0
- package/src/MarkdownControl/plugins/blocks/events/keyDownBackspace.js +27 -0
- package/src/MarkdownControl/plugins/blocks/events/keyDownEnter.js +26 -0
- package/src/MarkdownControl/plugins/blocks/events/toggleBlock.js +39 -0
- package/src/MarkdownControl/plugins/blocks/locations/areCurrentAndPreviousBlocksOfType.js +15 -0
- package/src/MarkdownControl/plugins/blocks/locations/getListTypeAtCursor.js +11 -0
- package/src/MarkdownControl/plugins/blocks/locations/isCursorAtEndOfParagraph.js +14 -0
- package/src/MarkdownControl/plugins/blocks/locations/isCursorAtStartOfBlockType.js +14 -0
- package/src/MarkdownControl/plugins/blocks/locations/isCursorAtStartOfNonEmptyHeading.js +22 -0
- package/src/MarkdownControl/plugins/blocks/locations/isCursorInBlockType.js +27 -0
- package/src/MarkdownControl/plugins/blocks/locations/isCursorInNonDefaultBlock.js +17 -0
- package/src/MarkdownControl/plugins/blocks/transforms/splitIntoParagraph.js +13 -0
- package/src/MarkdownControl/plugins/blocks/transforms/unwrapIfCursorAtStart.js +30 -0
- package/src/MarkdownControl/plugins/blocks/transforms/wrapListItemsInBlock.js +11 -0
- package/src/MarkdownControl/plugins/blocks/withBlocks.js +15 -0
- package/src/MarkdownControl/plugins/inlines/events/keyDown.js +38 -0
- package/src/MarkdownControl/plugins/inlines/events/keyDownShiftEnter.js +28 -0
- package/src/MarkdownControl/plugins/inlines/events/toggleLink.js +17 -0
- package/src/MarkdownControl/plugins/inlines/events/toggleMark.js +13 -0
- package/src/MarkdownControl/plugins/inlines/locations/isMarkActive.js +11 -0
- package/src/MarkdownControl/plugins/inlines/selectors/getActiveLink.js +10 -0
- package/src/MarkdownControl/plugins/inlines/transforms/unwrapLink.js +9 -0
- package/src/MarkdownControl/plugins/inlines/transforms/wrapLink.js +30 -0
- package/src/MarkdownControl/plugins/inlines/withInlines.js +20 -0
- package/src/MarkdownControl/plugins/lists/events/keyDown.js +34 -0
- package/src/MarkdownControl/plugins/lists/events/keyDownBackspace.js +31 -0
- package/src/MarkdownControl/plugins/lists/events/keyDownEnter.js +39 -0
- package/src/MarkdownControl/plugins/lists/events/keyDownShiftTab.js +27 -0
- package/src/MarkdownControl/plugins/lists/events/keyDownTab.js +55 -0
- package/src/MarkdownControl/plugins/lists/events/toggleListType.js +23 -0
- package/src/MarkdownControl/plugins/lists/locations/isCursorAtListItemStart.js +13 -0
- package/src/MarkdownControl/plugins/lists/locations/isCursorAtNoninitialParagraphStart.js +11 -0
- package/src/MarkdownControl/plugins/lists/locations/isCursorInItemContainingNestedList.js +9 -0
- package/src/MarkdownControl/plugins/lists/locations/isCursorInListItem.js +21 -0
- package/src/MarkdownControl/plugins/lists/locations/isSelectionWithinNoninitialListItem.js +14 -0
- package/src/MarkdownControl/plugins/lists/selectors/getListContainedInListItem.js +12 -0
- package/src/MarkdownControl/plugins/lists/selectors/getLowestAncestorList.js +11 -0
- package/src/MarkdownControl/plugins/lists/selectors/getLowestAncestorQuote.js +11 -0
- package/src/MarkdownControl/plugins/lists/transforms/changeListType.js +16 -0
- package/src/MarkdownControl/plugins/lists/transforms/convertParagraphToListItem.js +19 -0
- package/src/MarkdownControl/plugins/lists/transforms/liftFirstMatchedParent.js +11 -0
- package/src/MarkdownControl/plugins/lists/transforms/liftListItem.js +30 -0
- package/src/MarkdownControl/plugins/lists/transforms/mergeWithPreviousListItem.js +13 -0
- package/src/MarkdownControl/plugins/lists/transforms/moveListToListItem.js +13 -0
- package/src/MarkdownControl/plugins/lists/transforms/splitListItem.js +33 -0
- package/src/MarkdownControl/plugins/lists/transforms/splitToNestedList.js +33 -0
- package/src/MarkdownControl/plugins/lists/transforms/unwrapFirstMatchedParent.js +9 -0
- package/src/MarkdownControl/plugins/lists/transforms/unwrapSelectionFromList.js +14 -0
- package/src/MarkdownControl/plugins/lists/transforms/wrapFirstMatchedParent.js +9 -0
- package/src/MarkdownControl/plugins/lists/transforms/wrapSelectionInList.js +17 -0
- package/src/MarkdownControl/plugins/lists/withLists.js +66 -0
- package/src/MarkdownControl/plugins/matchers/lowestMatchedAncestor.js +6 -0
- package/src/MarkdownControl/plugins/matchers/matchLink.js +9 -0
- package/src/MarkdownControl/plugins/matchers/matchedAncestors.js +18 -0
- package/src/MarkdownControl/plugins/shortcodes/insertShortcode.js +34 -0
- package/src/MarkdownControl/plugins/shortcodes/locations/isCursorInEmptyParagraph.js +17 -0
- package/src/MarkdownControl/plugins/shortcodes/withShortcodes.js +26 -0
- package/src/MarkdownControl/plugins/util.js +11 -0
- package/src/MarkdownControl/renderers.js +352 -0
- package/src/MarkdownPreview.js +27 -0
- package/src/__tests__/__snapshots__/renderer.spec.js.snap +239 -0
- package/src/__tests__/renderer.spec.js +261 -0
- package/src/index.js +16 -0
- package/src/regexHelper.js +137 -0
- package/src/schema.js +35 -0
- package/src/serializers/__tests__/__fixtures__/commonmarkExpected.json +625 -0
- package/src/serializers/__tests__/__fixtures__/duplicate_marks_github_issue_3280.md +1 -0
- package/src/serializers/__tests__/commonmark.spec.js +110 -0
- package/src/serializers/__tests__/index.spec.js +52 -0
- package/src/serializers/__tests__/remarkAllowHtmlEntities.spec.js +25 -0
- package/src/serializers/__tests__/remarkAssertParents.spec.js +171 -0
- package/src/serializers/__tests__/remarkEscapeMarkdownEntities.spec.js +84 -0
- package/src/serializers/__tests__/remarkPaddedLinks.spec.js +43 -0
- package/src/serializers/__tests__/remarkPlugins.spec.js +299 -0
- package/src/serializers/__tests__/remarkShortcodes.spec.js +106 -0
- package/src/serializers/__tests__/remarkSlate.spec.js +67 -0
- package/src/serializers/__tests__/remarkStripTrailingBreaks.spec.js +23 -0
- package/src/serializers/__tests__/slate.spec.js +300 -0
- package/src/serializers/index.js +226 -0
- package/src/serializers/rehypePaperEmoji.js +16 -0
- package/src/serializers/remarkAllowHtmlEntities.js +58 -0
- package/src/serializers/remarkAssertParents.js +83 -0
- package/src/serializers/remarkEscapeMarkdownEntities.js +269 -0
- package/src/serializers/remarkImagesToText.js +26 -0
- package/src/serializers/remarkPaddedLinks.js +120 -0
- package/src/serializers/remarkRehypeShortcodes.js +67 -0
- package/src/serializers/remarkShortcodes.js +106 -0
- package/src/serializers/remarkSlate.js +425 -0
- package/src/serializers/remarkSquashReferences.js +73 -0
- package/src/serializers/remarkStripTrailingBreaks.js +56 -0
- package/src/serializers/remarkWrapHtml.js +20 -0
- package/src/serializers/slateRemark.js +468 -0
- package/src/styles.js +13 -0
- package/src/types.js +3 -0
- package/test-helpers/h.js +32 -0
- 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
|
+
* 
|
|
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
|
+
}
|