planning
All checks were successful
Publish To Prod / deploy_and_publish (push) Successful in 35s

This commit is contained in:
2024-10-14 09:15:30 +02:00
parent bcba00a730
commit 6e64e138e2
21059 changed files with 2317811 additions and 1 deletions

View File

@@ -0,0 +1,233 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.htmlToSlate = htmlToSlate;
exports.markdownToHtml = markdownToHtml;
exports.markdownToRemark = markdownToRemark;
exports.markdownToSlate = markdownToSlate;
exports.remarkToMarkdown = remarkToMarkdown;
exports.slateToMarkdown = slateToMarkdown;
var _trimEnd2 = _interopRequireDefault(require("lodash/trimEnd"));
var _unified = _interopRequireDefault(require("unified"));
var _unistBuilder = _interopRequireDefault(require("unist-builder"));
var _remarkParse = _interopRequireDefault(require("remark-parse"));
var _remarkStringify = _interopRequireDefault(require("remark-stringify"));
var _remarkRehype = _interopRequireDefault(require("remark-rehype"));
var _rehypeStringify = _interopRequireDefault(require("rehype-stringify"));
var _rehypeParse = _interopRequireDefault(require("rehype-parse"));
var _rehypeRemark = _interopRequireDefault(require("rehype-remark"));
var _remarkRehypeShortcodes = _interopRequireDefault(require("./remarkRehypeShortcodes"));
var _rehypePaperEmoji = _interopRequireDefault(require("./rehypePaperEmoji"));
var _remarkAssertParents = _interopRequireDefault(require("./remarkAssertParents"));
var _remarkPaddedLinks = _interopRequireDefault(require("./remarkPaddedLinks"));
var _remarkWrapHtml = _interopRequireDefault(require("./remarkWrapHtml"));
var _remarkSlate = _interopRequireDefault(require("./remarkSlate"));
var _remarkSquashReferences = _interopRequireDefault(require("./remarkSquashReferences"));
var _remarkShortcodes = require("./remarkShortcodes");
var _remarkEscapeMarkdownEntities = _interopRequireDefault(require("./remarkEscapeMarkdownEntities"));
var _remarkStripTrailingBreaks = _interopRequireDefault(require("./remarkStripTrailingBreaks"));
var _remarkAllowHtmlEntities = _interopRequireDefault(require("./remarkAllowHtmlEntities"));
var _slateRemark = _interopRequireDefault(require("./slateRemark"));
var _MarkdownControl = require("../MarkdownControl");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* This module contains all serializers for the Markdown widget.
*
* The value of a Markdown widget is transformed to various formats during
* editing, and these formats are referenced throughout serializer source
* documentation. Below is brief glossary of the formats used.
*
* - Markdown {string}
* The stringified Markdown value. The value of the field is persisted
* (stored) in this format, and the stringified value is also used when the
* editor is in "raw" Markdown mode.
*
* - 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.
* 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
* representation of an HTML document. The field value takes this format
* temporarily before the document is stringified to HTML.
*
* - HTML {string}
* The field value is stringified to HTML for preview purposes - the HTML value
* is never parsed, it is output only.
*
* - Slate Raw AST {object}
* 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.
*/
/**
* Deserialize a Markdown string to an MDAST.
*/
function markdownToRemark(markdown, remarkPlugins) {
const processor = (0, _unified.default)().use(_remarkParse.default, {
fences: true,
commonmark: true
}).use(markdownToRemarkRemoveTokenizers, {
inlineTokenizers: ['url']
}).use(_remarkShortcodes.remarkParseShortcodes, {
plugins: (0, _MarkdownControl.getEditorComponents)()
}).use(_remarkAllowHtmlEntities.default).use(_remarkSquashReferences.default).use(remarkPlugins);
/**
* Parse the Markdown string input to an MDAST.
*/
const parsed = processor.parse(markdown);
/**
* Further transform the MDAST with plugins.
*/
const result = processor.runSync(parsed);
return result;
}
/**
* Remove named tokenizers from the parser, effectively deactivating them.
*/
function markdownToRemarkRemoveTokenizers({
inlineTokenizers
}) {
inlineTokenizers && inlineTokenizers.forEach(tokenizer => {
delete this.Parser.prototype.inlineTokenizers[tokenizer];
});
}
/**
* Serialize an MDAST to a Markdown string.
*/
function remarkToMarkdown(obj, remarkPlugins) {
/**
* Rewrite the remark-stringify text visitor to simply return the text value,
* without encoding or escaping any characters. This means we're completely
* trusting the markdown that we receive.
*/
function remarkAllowAllText() {
const Compiler = this.Compiler;
const visitors = Compiler.prototype.visitors;
visitors.text = node => node.value;
}
/**
* Provide an empty MDAST if no value is provided.
*/
const mdast = obj || (0, _unistBuilder.default)('root', [(0, _unistBuilder.default)('paragraph', [(0, _unistBuilder.default)('text', '')])]);
const remarkToMarkdownPluginOpts = {
commonmark: true,
fences: true,
listItemIndent: '1',
/**
* Use asterisk for everything, it's the most versatile. Eventually using
* other characters should be an option.
*/
bullet: '*',
emphasis: '*',
strong: '*',
rule: '-'
};
const processor = (0, _unified.default)().use({
settings: remarkToMarkdownPluginOpts
}).use(_remarkEscapeMarkdownEntities.default).use(_remarkStripTrailingBreaks.default).use(_remarkStringify.default).use(remarkAllowAllText).use((0, _remarkShortcodes.createRemarkShortcodeStringifier)({
plugins: (0, _MarkdownControl.getEditorComponents)()
})).use(remarkPlugins);
/**
* Transform the MDAST with plugins.
*/
const processedMdast = processor.runSync(mdast);
/**
* Serialize the MDAST to markdown.
*/
const markdown = processor.stringify(processedMdast).replace(/\r?/g, '');
/**
* Return markdown with trailing whitespace removed.
*/
return (0, _trimEnd2.default)(markdown);
}
/**
* Convert Markdown to HTML.
*/
function markdownToHtml(markdown, {
getAsset,
resolveWidget,
remarkPlugins = []
} = {}) {
const mdast = markdownToRemark(markdown, remarkPlugins);
const hast = (0, _unified.default)().use(_remarkRehypeShortcodes.default, {
plugins: (0, _MarkdownControl.getEditorComponents)(),
getAsset,
resolveWidget
}).use(_remarkRehype.default, {
allowDangerousHTML: true
}).runSync(mdast);
const html = (0, _unified.default)().use(_rehypeStringify.default, {
allowDangerousHtml: true,
allowDangerousCharacters: true,
closeSelfClosing: true,
entities: {
useNamedReferences: true
}
}).stringify(hast);
return html;
}
/**
* Deserialize an HTML string to Slate's Raw AST. Currently used for HTML
* pastes.
*/
function htmlToSlate(html) {
const hast = (0, _unified.default)().use(_rehypeParse.default, {
fragment: true
}).parse(html);
const mdast = (0, _unified.default)().use(_rehypePaperEmoji.default).use(_rehypeRemark.default, {
minify: false
}).runSync(hast);
const slateRaw = (0, _unified.default)().use(_remarkAssertParents.default).use(_remarkPaddedLinks.default).use(_remarkWrapHtml.default).use(_remarkSlate.default).runSync(mdast);
return slateRaw;
}
/**
* Convert Markdown to Slate's Raw AST.
*/
function markdownToSlate(markdown, {
voidCodeBlock,
remarkPlugins = []
} = {}) {
const mdast = markdownToRemark(markdown, remarkPlugins);
const slateRaw = (0, _unified.default)().use(_remarkWrapHtml.default).use(_remarkSlate.default, {
voidCodeBlock
}).runSync(mdast);
return slateRaw.children;
}
/**
* Convert a Slate Raw AST to Markdown.
*
* Requires shortcode plugins to parse shortcode nodes back to text.
*
* Note that Unified is not utilized for the conversion from Slate's Raw AST to
* MDAST. The conversion is manual because Unified can only operate on Unist
* trees.
*/
function slateToMarkdown(raw, {
voidCodeBlock,
remarkPlugins = []
} = {}) {
const mdast = (0, _slateRemark.default)(raw, {
voidCodeBlock
});
const markdown = remarkToMarkdown(mdast, remarkPlugins);
return markdown;
}

View File

@@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = rehypePaperEmoji;
/**
* Dropbox Paper outputs emoji characters as images, and stores the actual
* emoji character in a `data-emoji-ch` attribute on the image. This plugin
* replaces the images with the emoji characters.
*/
function rehypePaperEmoji() {
function transform(node) {
if (node.tagName === 'img' && node.properties.dataEmojiCh) {
return {
type: 'text',
value: node.properties.dataEmojiCh
};
}
node.children = node.children ? node.children.map(transform) : node.children;
return node;
}
return transform;
}

View File

@@ -0,0 +1,55 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = remarkAllowHtmlEntities;
function remarkAllowHtmlEntities() {
this.Parser.prototype.inlineTokenizers.text = text;
/**
* This is a port of the `remark-parse` text tokenizer, adapted to exclude
* HTML entity decoding.
*/
function text(eat, value, silent) {
var self = this;
var methods;
var tokenizers;
var index;
var length;
var subvalue;
var position;
var tokenizer;
var name;
var min;
/* istanbul ignore if - never used (yet) */
if (silent) {
return true;
}
methods = self.inlineMethods;
length = methods.length;
tokenizers = self.inlineTokenizers;
index = -1;
min = value.length;
while (++index < length) {
name = methods[index];
if (name === 'text' || !tokenizers[name]) {
continue;
}
tokenizer = tokenizers[name].locator;
if (!tokenizer) {
eat.file.fail('Missing locator: `' + name + '`');
}
position = tokenizer.call(self, value, 1);
if (position !== -1 && position < min) {
min = position;
}
}
subvalue = value.slice(0, min);
eat(subvalue)({
type: 'text',
value: subvalue
});
}
}

View File

@@ -0,0 +1,89 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = remarkUnwrapInvalidNest;
var _isEmpty2 = _interopRequireDefault(require("lodash/isEmpty"));
var _nth2 = _interopRequireDefault(require("lodash/nth"));
var _last2 = _interopRequireDefault(require("lodash/last"));
var _concat2 = _interopRequireDefault(require("lodash/concat"));
var _unistUtilVisitParents = _interopRequireDefault(require("unist-util-visit-parents"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
/**
* remarkUnwrapInvalidNest
*
* Some MDAST node types can only be nested within specific node types - for
* example, a paragraph can't be nested within another paragraph, and a heading
* can't be nested in a "strong" type node. This kind of invalid MDAST can be
* generated by rehype-remark from invalid HTML.
*
* This plugin finds instances of invalid nesting, and unwraps the invalidly
* nested nodes as far up the parental line as necessary, splitting parent nodes
* along the way. The resulting node has no invalidly nested nodes, and all
* validly nested nodes retain their ancestry. Nodes that are emptied as a
* result of unnesting nodes are removed from the tree.
*/
function remarkUnwrapInvalidNest() {
return transform;
function transform(tree) {
const invalidNest = findInvalidNest(tree);
if (!invalidNest) return tree;
splitTreeAtNest(tree, invalidNest);
return transform(tree);
}
/**
* visitParents uses unist-util-visit-parent to check every node in the
* tree while having access to every ancestor of the node. This is ideal
* for determining whether a block node has an ancestor that should not
* contain a block node. Note that it operates in a mutable fashion.
*/
function findInvalidNest(tree) {
/**
* Node types that are considered "blocks".
*/
const blocks = ['paragraph', 'heading', 'code', 'blockquote', 'list', 'table', 'thematicBreak'];
/**
* Node types that can contain "block" nodes as direct children. We check
*/
const canContainBlocks = ['root', 'blockquote', 'listItem', 'tableCell'];
let invalidNest;
(0, _unistUtilVisitParents.default)(tree, (node, parents) => {
const parentType = !(0, _isEmpty2.default)(parents) && (0, _last2.default)(parents).type;
const isInvalidNest = blocks.includes(node.type) && !canContainBlocks.includes(parentType);
if (isInvalidNest) {
invalidNest = (0, _concat2.default)(parents, node);
return false;
}
});
return invalidNest;
}
function splitTreeAtNest(tree, nest) {
const grandparent = (0, _nth2.default)(nest, -3) || tree;
const parent = (0, _nth2.default)(nest, -2);
const node = (0, _last2.default)(nest);
const splitIndex = grandparent.children.indexOf(parent);
const splitChildren = grandparent.children;
const splitChildIndex = parent.children.indexOf(node);
const childrenBefore = parent.children.slice(0, splitChildIndex);
const childrenAfter = parent.children.slice(splitChildIndex + 1);
const nodeBefore = !(0, _isEmpty2.default)(childrenBefore) && _objectSpread(_objectSpread({}, parent), {}, {
children: childrenBefore
});
const nodeAfter = !(0, _isEmpty2.default)(childrenAfter) && _objectSpread(_objectSpread({}, parent), {}, {
children: childrenAfter
});
const childrenToInsert = [nodeBefore, node, nodeAfter].filter(val => !(0, _isEmpty2.default)(val));
const beforeChildren = splitChildren.slice(0, splitIndex);
const afterChildren = splitChildren.slice(splitIndex + 1);
const newChildren = (0, _concat2.default)(beforeChildren, childrenToInsert, afterChildren);
grandparent.children = newChildren;
}
}

View File

@@ -0,0 +1,271 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = remarkEscapeMarkdownEntities;
var _map2 = _interopRequireDefault(require("lodash/map"));
var _partial2 = _interopRequireDefault(require("lodash/partial"));
var _flow2 = _interopRequireDefault(require("lodash/flow"));
var _has2 = _interopRequireDefault(require("lodash/has"));
var _regexHelper = require("../regexHelper");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
/**
* Reusable regular expressions segments.
*/
const patternSegments = {
/**
* Matches zero or more HTML attributes followed by the tag close bracket,
* which may be prepended by zero or more spaces. The attributes can use
* single or double quotes and may be prepended by zero or more spaces.
*/
htmlOpeningTagEnd: /(?: *\w+=(?:(?:"[^"]*")|(?:'[^']*')))* *>/
};
/**
* Patterns matching substrings that should not be escaped. Array values must be
* joined before use.
*/
const nonEscapePatterns = {
/**
* HTML Tags
*
* Matches HTML opening tags and any attributes. Does not check for contents
* between tags or closing tags.
*/
htmlTags: [
/**
* Matches the beginning of an HTML tag, excluding preformatted tag types.
*/
/<(?!pre|style|script)[\w]+/,
/**
* Matches attributes.
*/
patternSegments.htmlOpeningTagEnd],
/**
* Preformatted HTML Blocks
*
* Matches HTML blocks with preformatted content. The content of these blocks,
* including the tags and attributes, should not be escaped at all.
*/
preformattedHtmlBlocks: [
/**
* Matches the names of tags known to have preformatted content. The capture
* group is reused when matching the closing tag.
*
* NOTE: this pattern reuses a capture group, and could break if combined with
* other expressions using capture groups.
*/
/<(pre|style|script)/,
/**
* Matches attributes.
*/
patternSegments.htmlOpeningTagEnd,
/**
* Allow zero or more of any character (including line breaks) between the
* tags. Match lazily in case of subsequent blocks.
*/
/(.|[\n\r])*?/,
/**
* Match closing tag via first capture group.
*/
/<\/\1>/]
};
/**
* Escape patterns
*
* Each escape pattern matches a markdown entity and captures up to two
* groups. These patterns must use one of the following formulas:
*
* - Single capture group followed by match content - /(...).../
* The captured characters should be escaped and the remaining match should
* remain unchanged.
*
* - Two capture groups surrounding matched content - /(...)...(...)/
* The captured characters in both groups should be escaped and the matched
* characters in between should remain unchanged.
*/
const escapePatterns = [
/**
* Emphasis/Bold - Asterisk
*
* Match strings surrounded by one or more asterisks on both sides.
*/
/(\*+)[^*]*(\1)/g,
/**
* Emphasis - Underscore
*
* Match strings surrounded by a single underscore on both sides followed by
* a word boundary. Remark disregards whether a word boundary exists at the
* beginning of an emphasis node.
*/
/(_)[^_]+(_)\b/g,
/**
* Bold - Underscore
*
* Match strings surrounded by multiple underscores on both sides. Remark
* disregards the absence of word boundaries on either side of a bold node.
*/
/(_{2,})[^_]*(\1)/g,
/**
* Strikethrough
*
* Match strings surrounded by multiple tildes on both sides.
*/
/(~+)[^~]*(\1)/g,
/**
* Inline Code
*
* Match strings surrounded by backticks.
*/
/(`+)[^`]*(\1)/g,
/**
* Links and Images
*
* Match strings surrounded by square brackets, except when the opening
* bracket is followed by a caret. This could be improved to specifically
* match only the exact syntax of each covered entity, but doing so through
* current approach would incur a considerable performance penalty.
*/
/(\[(?!\^)+)[^\]]*]/g];
/**
* Generate new non-escape expression. The non-escape expression matches
* substrings whose contents should not be processed for escaping.
*/
const joinedNonEscapePatterns = (0, _map2.default)(nonEscapePatterns, pattern => {
return new RegExp((0, _regexHelper.joinPatternSegments)(pattern));
});
const nonEscapePattern = (0, _regexHelper.combinePatterns)(joinedNonEscapePatterns);
/**
* Create chain of successive escape functions for various markdown entities.
*/
const escapeFunctions = escapePatterns.map(pattern => (0, _partial2.default)(escapeDelimiters, pattern));
const escapeAll = (0, _flow2.default)(escapeFunctions);
/**
* Executes both the `escapeCommonChars` and `escapeLeadingChars` functions.
*/
function escapeAllChars(text) {
const partiallyEscapedMarkdown = escapeCommonChars(text);
return escapeLeadingChars(partiallyEscapedMarkdown);
}
/**
* escapeLeadingChars
*
* Handles escaping for characters that must be positioned at the beginning of
* the string, such as headers and list items.
*
* Escapes '#', '*', '-', '>', '=', '|', and sequences of 3+ backticks or 4+
* spaces when found at the beginning of a string, preceded by zero or more
* whitespace characters.
*/
function escapeLeadingChars(text) {
return text.replace(/^\s*([-#*>=|]| {4,}|`{3,})/, '$`\\$1');
}
/**
* escapeCommonChars
*
* Escapes active markdown entities. See escape pattern groups for details on
* which entities are replaced.
*/
function escapeCommonChars(text) {
/**
* Generate new non-escape expression (must happen at execution time because
* we use `RegExp.exec`, which tracks it's own state internally).
*/
const nonEscapeExpression = new RegExp(nonEscapePattern, 'gm');
/**
* Use `replaceWhen` to escape markdown entities only within substrings that
* are eligible for escaping.
*/
return (0, _regexHelper.replaceWhen)(nonEscapeExpression, escapeAll, text, true);
}
/**
* escapeDelimiters
*
* Executes `String.replace` for a given pattern, but only on the first two
* capture groups. Specifically intended for escaping opening (and optionally
* closing) markdown entities without escaping the content in between.
*/
function escapeDelimiters(pattern, text) {
return text.replace(pattern, (match, start, end) => {
const hasEnd = typeof end === 'string';
const matchSliceEnd = hasEnd ? match.length - end.length : match.length;
const content = match.slice(start.length, matchSliceEnd);
return `${escape(start)}${content}${hasEnd ? escape(end) : ''}`;
});
}
/**
* escape
*
* Simple replacement function for escaping markdown entities. Prepends every
* character in the received string with a backslash.
*/
function escape(delim) {
let result = '';
for (const char of delim) {
result += `\\${char}`;
}
return result;
}
/**
* A Remark plugin for escaping markdown entities.
*
* When markdown entities are entered in raw markdown, they don't appear as
* characters in the resulting AST; for example, dashes surrounding a piece of
* text cause the text to be inserted in a special node type, but the asterisks
* themselves aren't present as text. Therefore, we generally don't expect to
* encounter markdown characters in text nodes.
*
* However, the CMS visual editor does not interpret markdown characters, and
* users will expect these characters to be represented literally. In that case,
* we need to escape them, otherwise they'll be interpreted during
* stringification.
*/
function remarkEscapeMarkdownEntities() {
function transform(node, index) {
/**
* Shortcode nodes will intentionally inject markdown entities in text node
* children not be escaped.
*/
if ((0, _has2.default)(node.data, 'shortcode')) return node;
const children = node.children ? {
children: node.children.map(transform)
} : {};
/**
* Escape characters in text and html nodes only. We store a lot of normal
* text in html nodes to keep Remark from escaping html entities.
*/
if (['text', 'html'].includes(node.type) && node.value) {
/**
* Escape all characters if this is the first child node, otherwise only
* common characters.
*/
const value = index === 0 ? escapeAllChars(node.value) : escapeCommonChars(node.value);
return _objectSpread(_objectSpread({}, node), {}, {
value
}, children);
}
/**
* Always return nodes with recursively mapped children.
*/
return _objectSpread(_objectSpread({}, node), children);
}
return transform;
}

View File

@@ -0,0 +1,41 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = remarkImagesToText;
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
/**
* Images must be parsed as shortcodes for asset proxying. This plugin converts
* MDAST image nodes back to text to allow shortcode pattern matching. Note that
* this transformation only occurs for images that are the sole child of a top
* level paragraph - any other image is left alone and treated as an inline
* image.
*/
function remarkImagesToText() {
return transform;
function transform(node) {
const children = node.children.map(child => {
if (child.type === 'paragraph' && child.children.length === 1 && child.children[0].type === 'image') {
const {
alt,
url,
title
} = child.children[0];
const value = `![${alt || ''}](${url || ''}${title ? ` "${title}"` : ''})`;
child.children = [{
type: 'text',
value
}];
}
return child;
});
return _objectSpread(_objectSpread({}, node), {}, {
children
});
}
}

View File

@@ -0,0 +1,127 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = remarkPaddedLinks;
var _flatMap2 = _interopRequireDefault(require("lodash/flatMap"));
var _trimEnd2 = _interopRequireDefault(require("lodash/trimEnd"));
var _trimStart2 = _interopRequireDefault(require("lodash/trimStart"));
var _endsWith2 = _interopRequireDefault(require("lodash/endsWith"));
var _startsWith2 = _interopRequireDefault(require("lodash/startsWith"));
var _findLast2 = _interopRequireDefault(require("lodash/findLast"));
var _find2 = _interopRequireDefault(require("lodash/find"));
var _unistBuilder = _interopRequireDefault(require("unist-builder"));
var _mdastUtilToString = _interopRequireDefault(require("mdast-util-to-string"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
/**
* Convert leading and trailing spaces in a link to single spaces outside of the
* link. MDASTs derived from pasted Google Docs HTML require this treatment.
*
* Note that, because we're potentially replacing characters in a link node's
* children with character's in a link node's siblings, we have to operate on a
* parent (link) node and its children at once, rather than just processing
* children one at a time.
*/
function remarkPaddedLinks() {
function transform(node) {
/**
* Because we're operating on link nodes and their children at once, we can
* exit if the current node has no children.
*/
if (!node.children) return node;
/**
* Process a node's children if any of them are links. If a node is a link
* with leading or trailing spaces, we'll get back an array of nodes instead
* of a single node, so we use `flatMap` to keep those nodes as siblings
* with the other children.
*
* If performance improvements are found desirable, we could change this to
* only pass in the link nodes instead of the entire array of children, but
* this seems unlikely to produce a noticeable perf gain.
*/
const hasLinkChild = node.children.some(child => child.type === 'link');
const processedChildren = hasLinkChild ? (0, _flatMap2.default)(node.children, transformChildren) : node.children;
/**
* Run all children through the transform recursively.
*/
const children = processedChildren.map(transform);
return _objectSpread(_objectSpread({}, node), {}, {
children
});
}
function transformChildren(node) {
if (node.type !== 'link') return node;
/**
* Get the node's complete string value, check for leading and trailing
* whitespace, and get nodes from each edge where whitespace is found.
*/
const text = (0, _mdastUtilToString.default)(node);
const leadingWhitespaceNode = (0, _startsWith2.default)(text, ' ') && getEdgeTextChild(node);
const trailingWhitespaceNode = (0, _endsWith2.default)(text, ' ') && getEdgeTextChild(node, true);
if (!leadingWhitespaceNode && !trailingWhitespaceNode) return node;
/**
* Trim the edge nodes in place. Unified handles everything in a mutable
* fashion, so it's often simpler to do the same when working with Unified
* ASTs.
*/
if (leadingWhitespaceNode) {
leadingWhitespaceNode.value = (0, _trimStart2.default)(leadingWhitespaceNode.value);
}
if (trailingWhitespaceNode) {
trailingWhitespaceNode.value = (0, _trimEnd2.default)(trailingWhitespaceNode.value);
}
/**
* Create an array of nodes. The first and last child will either be `false`
* or a text node. We filter out the false values before returning.
*/
const nodes = [leadingWhitespaceNode && (0, _unistBuilder.default)('text', ' '), node, trailingWhitespaceNode && (0, _unistBuilder.default)('text', ' ')];
return nodes.filter(val => val);
}
/**
* Get the first or last non-blank text child of a node, regardless of
* nesting. If `end` is truthy, get the last node, otherwise first.
*/
function getEdgeTextChild(node, end) {
/**
* This was changed from a ternary to a long form if due to issues with istanbul's instrumentation and babel's code
* generation.
* TODO: watch https://github.com/istanbuljs/babel-plugin-istanbul/issues/95
* when it is resolved then revert to ```const findFn = end ? findLast : find;```
*/
let findFn;
if (end) {
findFn = _findLast2.default;
} else {
findFn = _find2.default;
}
let edgeChildWithValue;
setEdgeChildWithValue(node);
return edgeChildWithValue;
/**
* searchChildren checks a node and all of it's children deeply to find a
* non-blank text value. When the text node is found, we set it in an outside
* variable, as it may be deep in the tree and therefore wouldn't be returned
* by `find`/`findLast`.
*/
function setEdgeChildWithValue(child) {
if (!edgeChildWithValue && child.value) {
edgeChildWithValue = child;
}
findFn(child.children, setEdgeChildWithValue);
}
}
return transform;
}

View File

@@ -0,0 +1,93 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = remarkToRehypeShortcodes;
var _has2 = _interopRequireDefault(require("lodash/has"));
var _map2 = _interopRequireDefault(require("lodash/map"));
var _react = _interopRequireDefault(require("react"));
var _server = require("react-dom/server");
var _unistBuilder = _interopRequireDefault(require("unist-builder"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
/**
* This plugin doesn't actually transform Remark (MDAST) nodes to Rehype
* (HAST) nodes, but rather, it prepares an MDAST shortcode node for HAST
* conversion by replacing the shortcode text with stringified HTML for
* previewing the shortcode output.
*/
function remarkToRehypeShortcodes({
plugins,
getAsset,
resolveWidget
}) {
return transform;
function transform(root) {
const transformedChildren = (0, _map2.default)(root.children, processShortcodes);
return _objectSpread(_objectSpread({}, root), {}, {
children: transformedChildren
});
}
/**
* Mapping function to transform nodes that contain shortcodes.
*/
function processShortcodes(node) {
/**
* If the node doesn't contain shortcode data, return the original node.
*/
if (!(0, _has2.default)(node, ['data', 'shortcode'])) return node;
/**
* Get shortcode data from the node, and retrieve the matching plugin by
* key.
*/
const {
shortcode,
shortcodeData
} = node.data;
const plugin = plugins.get(shortcode);
/**
* Run the shortcode plugin's `toPreview` method, which will return either
* an HTML string or a React component. If a React component is returned,
* render it to an HTML string.
*/
const value = getPreview(plugin, shortcodeData);
const valueHtml = typeof value === 'string' ? value : (0, _server.renderToString)(value);
/**
* Return a new 'html' type node containing the shortcode preview markup.
*/
const textNode = (0, _unistBuilder.default)('html', valueHtml);
const children = [textNode];
return _objectSpread(_objectSpread({}, node), {}, {
children
});
}
/**
* Retrieve the shortcode preview component.
*/
function getPreview(plugin, shortcodeData) {
const {
toPreview,
widget,
fields
} = plugin;
if (toPreview) {
return toPreview(shortcodeData, getAsset, fields);
}
const preview = resolveWidget(widget);
return /*#__PURE__*/_react.default.createElement(preview.preview, {
value: shortcodeData,
field: plugin,
getAsset
});
}
}

View File

@@ -0,0 +1,123 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createRemarkShortcodeStringifier = createRemarkShortcodeStringifier;
exports.getLinesWithOffsets = getLinesWithOffsets;
exports.remarkParseShortcodes = remarkParseShortcodes;
function remarkParseShortcodes({
plugins
}) {
const Parser = this.Parser;
const tokenizers = Parser.prototype.blockTokenizers;
const methods = Parser.prototype.blockMethods;
tokenizers.shortcode = createShortcodeTokenizer({
plugins
});
methods.unshift('shortcode');
}
function getLinesWithOffsets(value) {
const SEPARATOR = '\n\n';
const splitted = value.split(SEPARATOR);
const trimmedLines = splitted.reduce((acc, line) => {
const {
start: previousLineStart,
originalLength: previousLineOriginalLength
} = acc[acc.length - 1];
return [...acc, {
line: line.trimEnd(),
start: previousLineStart + previousLineOriginalLength + SEPARATOR.length,
originalLength: line.length
}];
}, [{
start: -SEPARATOR.length,
originalLength: 0
}]).slice(1).map(({
line,
start
}) => ({
line,
start
}));
return trimmedLines;
}
function matchFromLines({
trimmedLines,
plugin
}) {
for (const {
line,
start
} of trimmedLines) {
const match = line.match(plugin.pattern);
if (match) {
match.index += start;
return match;
}
}
}
function createShortcodeTokenizer({
plugins
}) {
return function tokenizeShortcode(eat, value, silent) {
// Plugin patterns may rely on `^` and `$` tokens, even if they don't
// use the multiline flag. To support this, we fall back to searching
// through each line individually, trimming trailing whitespace and
// newlines, if we don't initially match on a pattern. We keep track of
// the starting position of each line so that we can sort correctly
// across the full multiline matches.
const trimmedLines = getLinesWithOffsets(value);
// Attempt to find a regex match for each plugin's pattern, and then
// select the first by its occurrence in `value`. This ensures we won't
// skip a plugin that occurs later in the plugin registry, but earlier
// in the `value`.
const [{
plugin,
match
} = {}] = plugins.toArray().map(plugin => ({
match: value.match(plugin.pattern) || matchFromLines({
trimmedLines,
plugin
}),
plugin
})).filter(({
match
}) => !!match).sort((a, b) => a.match.index - b.match.index);
if (match) {
if (silent) {
return true;
}
const shortcodeData = plugin.fromBlock(match);
try {
return eat(match[0])({
type: 'shortcode',
data: {
shortcode: plugin.id,
shortcodeData
}
});
} catch (e) {
console.warn(`Sent invalid data to remark. Plugin: ${plugin.id}. Value: ${match[0]}. Data: ${JSON.stringify(shortcodeData)}`);
return false;
}
}
};
}
function createRemarkShortcodeStringifier({
plugins
}) {
return function remarkStringifyShortcodes() {
const Compiler = this.Compiler;
const visitors = Compiler.prototype.visitors;
visitors.shortcode = shortcode;
function shortcode(node) {
const {
data
} = node;
const plugin = plugins.find(plugin => data.shortcode === plugin.id);
return plugin.toBlock(data.shortcodeData);
}
};
}

View File

@@ -0,0 +1,527 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = remarkToSlate;
exports.mergeAdjacentTexts = mergeAdjacentTexts;
var _isEqual2 = _interopRequireDefault(require("lodash/isEqual"));
var _flatten2 = _interopRequireDefault(require("lodash/flatten"));
var _map2 = _interopRequireDefault(require("lodash/map"));
var _flatMap2 = _interopRequireDefault(require("lodash/flatMap"));
var _isArray2 = _interopRequireDefault(require("lodash/isArray"));
var _isEmpty2 = _interopRequireDefault(require("lodash/isEmpty"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
/**
* Map of MDAST node types to Slate node types.
*/
const typeMap = {
root: 'root',
paragraph: 'paragraph',
blockquote: 'quote',
code: 'code-block',
listItem: 'list-item',
table: 'table',
tableRow: 'table-row',
tableCell: 'table-cell',
thematicBreak: 'thematic-break',
link: 'link',
image: 'image',
shortcode: 'shortcode'
};
/**
* Map of MDAST node types to Slate mark types.
*/
const markMap = {
strong: 'bold',
emphasis: 'italic',
delete: 'delete',
inlineCode: 'code'
};
function isText(node) {
return !!node.text;
}
function isMarksEqual(node1, node2) {
return (0, _isEqual2.default)(node1.marks, node2.marks);
}
function mergeAdjacentTexts(children) {
if (children.length <= 0) {
return children;
}
const mergedChildren = [];
let isMerging = false;
let current;
for (let i = 0; i < children.length - 1; i++) {
if (!isMerging) {
current = children[i];
}
const next = children[i + 1];
if (isText(current) && isText(next) && isMarksEqual(current, next)) {
isMerging = true;
current = _objectSpread(_objectSpread({}, current), {}, {
text: `${current.text}${next.text}`
});
} else {
mergedChildren.push(current);
isMerging = false;
}
}
if (isMerging) {
mergedChildren.push(current);
} else {
mergedChildren.push(children[children.length - 1]);
}
return mergedChildren;
}
/**
* A Remark plugin for converting an MDAST to Slate Raw AST. Remark plugins
* return a `transformNode` function that receives the MDAST as it's first argument.
*/
function remarkToSlate({
voidCodeBlock
} = {}) {
return transformNode;
function transformNode(node) {
/**
* Call `transformNode` recursively on child nodes.
*
* If a node returns a falsey value, filter it out. Some nodes do not
* translate from MDAST to Slate, such as definitions for link/image
* references or footnotes.
*/
let children = !['strong', 'emphasis', 'delete'].includes(node.type) && !(0, _isEmpty2.default)(node.children) && (0, _flatMap2.default)(node.children, transformNode).filter(val => val);
if (Array.isArray(children)) {
// Merge adjacent text nodes with the same marks to conform to slate schema
children = mergeAdjacentTexts(children);
}
/**
* Run individual nodes through the conversion factory.
*/
const output = convertNode(node, children || undefined);
return output;
}
/**
* Add nodes to a parent node only if `nodes` is truthy.
*/
function addNodes(parent, nodes) {
return nodes ? _objectSpread(_objectSpread({}, parent), {}, {
children: nodes
}) : parent;
}
/**
* Create a Slate Block node.
*/
function createBlock(type, nodes, props = {}) {
if (!(0, _isArray2.default)(nodes)) {
props = nodes;
nodes = undefined;
}
// Ensure block nodes have at least one text child to conform to slate schema
const children = (0, _isEmpty2.default)(nodes) ? [createText('')] : nodes;
const node = _objectSpread({
type
}, props);
return addNodes(node, children);
}
/**
* Create a Slate Inline node.
*/
function createInline(type, props = {}, nodes) {
const node = _objectSpread({
type
}, props);
// Ensure inline nodes have at least one text child to conform to slate schema
const children = (0, _isEmpty2.default)(nodes) ? [createText('')] : nodes;
return addNodes(node, children);
}
/**
* Create a Slate Raw text node.
*/
function createText(node) {
const newNode = {};
if (typeof node === 'string') {
return _objectSpread(_objectSpread({}, newNode), {}, {
text: node
});
}
const {
text,
marks
} = node;
return normalizeMarks(_objectSpread(_objectSpread({}, newNode), {}, {
text,
marks
}));
}
function processMarkChild(childNode, marks) {
switch (childNode.type) {
/**
* If a text node is a direct child of the current node, it should be
* set aside as a text, and all marks that have been collected in the
* `marks` array should apply to that specific text.
*/
case 'html':
case 'text':
return _objectSpread(_objectSpread({}, convertNode(childNode)), {}, {
marks
});
/**
* MDAST inline code nodes don't have children, just a text value, similar
* to a text node, so it receives the same treatment as a text node, but we
* first add the inline code mark to the marks array.
*/
case 'inlineCode':
{
return _objectSpread(_objectSpread({}, convertNode(childNode)), {}, {
marks: [...marks, {
type: 'code'
}]
});
}
/**
* Process nested style nodes. The recursive results should be pushed into
* the texts array. This way, every MDAST nested text structure becomes a
* flat array of texts that can serve as the value of a single Slate Raw
* text node.
*/
case 'strong':
case 'emphasis':
case 'delete':
return processMarkNode(childNode, marks);
case 'link':
{
const nodes = (0, _map2.default)(childNode.children, child => normalizeMarks(processMarkChild(child, marks)));
const result = convertNode(childNode, (0, _flatten2.default)(nodes));
return result;
}
/**
* Remaining nodes simply need mark data added to them, and to then be
* added into the cumulative children array.
*/
default:
return transformNode(_objectSpread(_objectSpread({}, childNode), {}, {
data: _objectSpread(_objectSpread({}, childNode.data), {}, {
marks
})
}));
}
}
function processMarkNode(node, parentMarks = []) {
/**
* Add the current node's mark type to the marks collected from parent
* mark nodes, if any.
*/
const markType = markMap[node.type];
const marks = markType ? [...parentMarks.filter(({
type
}) => type !== markType), {
type: markType
}] : parentMarks;
const children = (0, _flatMap2.default)(node.children, child => normalizeMarks(processMarkChild(child, marks)));
return children;
}
function normalizeMarks(node) {
if (node.marks) {
node.marks.forEach(mark => {
node[mark.type] = true;
});
}
return node;
}
/**
* Convert a single MDAST node to a Slate Raw node. Uses local node factories
* that mimic the unist-builder function utilized in the slateRemark
* transformer.
*/
function convertNode(node, nodes) {
switch (node.type) {
/**
* General
*
* Convert simple cases that only require a type and children, with no
* additional properties.
*/
case 'paragraph':
case 'blockquote':
case 'tableRow':
case 'tableCell':
{
return createBlock(typeMap[node.type], nodes);
}
/**
* Root element
* If the root node is empty, we need to add a paragraph node to it.
*/
case 'root':
{
const children = (0, _isEmpty2.default)(nodes) ? [createBlock('paragraph')] : nodes;
return createBlock(typeMap[node.type], children);
}
/**
* List Items
*
* Markdown list items can be empty, but a list item in the Slate schema
* should at least have an empty paragraph node.
*/
case 'listItem':
{
const children = (0, _isEmpty2.default)(nodes) ? [createBlock('paragraph')] : nodes;
return createBlock(typeMap[node.type], children);
}
/**
* Shortcodes
*
* Shortcode nodes are represented as "void" blocks in the Slate AST. They
* maintain the same data as MDAST shortcode nodes. Slate void blocks must
* contain a blank text node.
*/
case 'shortcode':
{
const nodes = [createText('')];
const data = _objectSpread(_objectSpread({}, node.data), {}, {
id: node.data.shortcode,
shortcodeNew: true
});
return createBlock(typeMap[node.type], nodes, {
data
});
}
case 'text':
{
const text = node.value;
return createText(text);
}
/**
* HTML
*
* HTML nodes contain plain text like text nodes, except they only contain
* HTML. Our serialization results in non-HTML being placed in HTML nodes
* sometimes to ensure that we're never escaping HTML from the rich text
* editor. We do not replace line feeds in HTML because the HTML is raw
* in the rich text editor, so the writer knows they're writing HTML, and
* should expect soft breaks to be visually absent in the rendered HTML.
*/
case 'html':
{
return createText(node.value);
}
/**
* Inline Code
*
* Inline code nodes from an MDAST are represented in our Slate schema as
* text nodes with a "code" mark. We manually create the text containing
* the inline code value and a "code" mark, and place it in an array for use
* as a Slate text node's children array.
*/
case 'inlineCode':
{
return createText({
text: node.value,
code: true,
marks: [{
type: 'code'
}]
});
}
/**
* Marks
*
* Marks are typically decorative sub-types that apply to text nodes. In an
* MDAST, marks are nodes that can contain other nodes. This nested
* hierarchy has to be flattened and split into distinct text nodes with
* their own set of marks.
*/
case 'strong':
case 'emphasis':
case 'delete':
{
return processMarkNode(node);
}
/**
* Headings
*
* MDAST headings use a single type with a separate "depth" property to
* indicate the heading level, while the Slate schema uses a separate node
* type for each heading level. Here we get the proper Slate node name based
* on the MDAST node depth.
*/
case 'heading':
{
const depthMap = {
1: 'one',
2: 'two',
3: 'three',
4: 'four',
5: 'five',
6: 'six'
};
const slateType = `heading-${depthMap[node.depth]}`;
return createBlock(slateType, nodes);
}
/**
* Code Blocks
*
* MDAST code blocks are a distinct node type with a simple text value. We
* convert that value into a nested child text node for Slate. If a void
* node is required due to a custom code block handler, the value is
* stored in the "code" data property instead. We also carry over the "lang"
* data property if it's defined.
*/
case 'code':
{
const data = _objectSpread(_objectSpread({
lang: node.lang
}, voidCodeBlock ? {
code: node.value
} : {}), {}, {
shortcode: 'code-block',
shortcodeData: {
code: node.value,
lang: node.lang
}
});
const text = createText(voidCodeBlock ? '' : node.value);
const nodes = [text];
const block = createBlock('shortcode', nodes, {
data
});
return block;
}
/**
* Lists
*
* MDAST has a single list type and an "ordered" property. We derive that
* information into the Slate schema's distinct list node types. We also
* include the "start" property, which indicates the number an ordered list
* starts at, if defined.
*/
case 'list':
{
const slateType = node.ordered ? 'numbered-list' : 'bulleted-list';
const data = {
start: node.start
};
return createBlock(slateType, nodes, {
data
});
}
/**
* Breaks
*
* MDAST soft break nodes represent a trailing double space or trailing
* slash from a Markdown document. In Slate, these are simply transformed to
* line breaks within a text node.
*/
case 'break':
{
const {
data
} = node;
return createInline('break', {
data
});
}
/**
* Thematic Breaks
*
* Thematic breaks are void nodes in the Slate schema.
*/
case 'thematicBreak':
{
return createBlock(typeMap[node.type]);
}
/**
* Links
*
* MDAST stores the link attributes directly on the node, while our Slate
* schema references them in the data object.
*/
case 'link':
{
const {
title,
url,
data
} = node;
const newData = _objectSpread(_objectSpread({}, data), {}, {
title,
url
});
return createInline(typeMap[node.type], {
data: newData
}, nodes);
}
/**
* Images
*
* Identical to link nodes except for the lack of child nodes and addition
* of alt attribute data MDAST stores the link attributes directly on the
* node, while our Slate schema references them in the data object.
*/
case 'image':
{
const {
title,
url,
alt,
data
} = node;
const newData = _objectSpread(_objectSpread({}, data), {}, {
title,
alt,
url
});
return createInline(typeMap[node.type], {
data: newData
});
}
/**
* Tables
*
* Tables are parsed separately because they may include an "align"
* property, which should be passed to the Slate node.
*/
case 'table':
{
const data = {
align: node.align
};
return createBlock(typeMap[node.type], nodes, {
data
});
}
}
}
}

View File

@@ -0,0 +1,88 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = remarkSquashReferences;
var _flatten2 = _interopRequireDefault(require("lodash/flatten"));
var _without2 = _interopRequireDefault(require("lodash/without"));
var _unistBuilder = _interopRequireDefault(require("unist-builder"));
var _mdastUtilDefinitions = _interopRequireDefault(require("mdast-util-definitions"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
/**
* Raw markdown may contain image references or link references. Because there
* is no way to maintain these references within the Slate AST, we convert image
* and link references to standard images and links by putting their url's
* inline. The definitions are then removed from the document.
*
* For example, the following markdown:
*
* ```
* ![alpha][bravo]
*
* [bravo]: http://example.com/example.jpg
* ```
*
* Yields:
*
* ```
* ![alpha](http://example.com/example.jpg)
* ```
*
*/
function remarkSquashReferences() {
return getTransform;
function getTransform(node) {
const getDefinition = (0, _mdastUtilDefinitions.default)(node);
return transform.call(null, getDefinition, node);
}
function transform(getDefinition, node) {
/**
* Bind the `getDefinition` function to `transform` and recursively map all
* nodes.
*/
const boundTransform = transform.bind(null, getDefinition);
const children = node.children ? node.children.map(boundTransform) : node.children;
/**
* Combine reference and definition nodes into standard image and link
* nodes.
*/
if (['imageReference', 'linkReference'].includes(node.type)) {
const type = node.type === 'imageReference' ? 'image' : 'link';
const definition = getDefinition(node.identifier);
if (definition) {
const {
title,
url
} = definition;
return (0, _unistBuilder.default)(type, {
title,
url,
alt: node.alt
}, children);
}
const pre = (0, _unistBuilder.default)('text', node.type === 'imageReference' ? '![' : '[');
const post = (0, _unistBuilder.default)('text', ']');
const nodes = children || [(0, _unistBuilder.default)('text', node.alt)];
return [pre, ...nodes, post];
}
/**
* Remove definition nodes and filter the resulting null values from the
* filtered children array.
*/
if (node.type === 'definition') {
return null;
}
const filteredChildren = (0, _without2.default)(children, null);
return _objectSpread(_objectSpread({}, node), {}, {
children: (0, _flatten2.default)(filteredChildren)
});
}
}

View File

@@ -0,0 +1,61 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = remarkStripTrailingBreaks;
var _mdastUtilToString = _interopRequireDefault(require("mdast-util-to-string"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Removes break nodes that are at the end of a block.
*
* When a trailing double space or backslash is encountered at the end of a
* markdown block, Remark will interpret the character(s) literally, as only
* break entities followed by text qualify as breaks. A manually created MDAST,
* however, may have such entities, and users of visual editors shouldn't see
* these artifacts in resulting markdown.
*/
function remarkStripTrailingBreaks() {
function transform(node) {
if (node.children) {
node.children = node.children.map((child, idx, children) => {
/**
* Only touch break nodes. Convert all subsequent nodes to their text
* value and exclude the break node if no non-whitespace characters
* are found.
*/
if (child.type === 'break') {
const subsequentNodes = children.slice(idx + 1);
/**
* Create a small MDAST so that mdastToString can process all
* siblings as children of one node rather than making multiple
* calls.
*/
const fragment = {
type: 'root',
children: subsequentNodes
};
const subsequentText = (0, _mdastUtilToString.default)(fragment);
return subsequentText.trim() ? child : null;
}
/**
* Always return the child if not a break.
*/
return child;
})
/**
* Because some break nodes may be excluded, we filter out the resulting
* null values.
*/.filter(child => child)
/**
* Recurse through the MDAST by transforming each individual child node.
*/.map(transform);
}
return node;
}
return transform;
}

View File

@@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = remarkWrapHtml;
var _unistBuilder = _interopRequireDefault(require("unist-builder"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Ensure that top level 'html' type nodes are wrapped in paragraphs. Html nodes
* are used for text nodes that we don't want Remark or Rehype to parse.
*/
function remarkWrapHtml() {
function transform(tree) {
tree.children = tree.children.map(node => {
if (node.type === 'html') {
return (0, _unistBuilder.default)('paragraph', [node]);
}
return node;
});
return tree;
}
return transform;
}

View File

@@ -0,0 +1,512 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = slateToRemark;
var _omit2 = _interopRequireDefault(require("lodash/omit"));
var _intersection2 = _interopRequireDefault(require("lodash/intersection"));
var _map2 = _interopRequireDefault(require("lodash/map"));
var _last2 = _interopRequireDefault(require("lodash/last"));
var _without2 = _interopRequireDefault(require("lodash/without"));
var _get4 = _interopRequireDefault(require("lodash/get"));
var _unistBuilder = _interopRequireDefault(require("unist-builder"));
var _mdastUtilToString = _interopRequireDefault(require("mdast-util-to-string"));
const _excluded = ["lang", "code"],
_excluded2 = ["url", "title", "alt"];
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
/**
* Map of Slate node types to MDAST/Remark node types.
*/
const typeMap = {
root: 'root',
paragraph: 'paragraph',
'heading-one': 'heading',
'heading-two': 'heading',
'heading-three': 'heading',
'heading-four': 'heading',
'heading-five': 'heading',
'heading-six': 'heading',
quote: 'blockquote',
'code-block': 'code',
'numbered-list': 'list',
'bulleted-list': 'list',
'list-item': 'listItem',
table: 'table',
'table-row': 'tableRow',
'table-cell': 'tableCell',
break: 'break',
'thematic-break': 'thematicBreak',
link: 'link',
image: 'image',
shortcode: 'shortcode'
};
/**
* Map of Slate mark types to MDAST/Remark node types.
*/
const markMap = {
bold: 'strong',
italic: 'emphasis',
delete: 'delete',
code: 'inlineCode'
};
const blockTypes = ['paragraph', 'quote', 'heading-one', 'heading-two', 'heading-three', 'heading-four', 'heading-five', 'heading-six', 'bulleted-list', 'numbered-list', 'list-item', 'shortcode', 'table', 'table-row', 'table-cell'];
const inlineTypes = ['link', 'image', 'break'];
const leadingWhitespaceExp = /^\s+\S/;
const trailingWhitespaceExp = /(?!\S)\s+$/;
function slateToRemark(value, {
voidCodeBlock
}) {
/**
* The Slate Raw AST generally won't have a top level type, so we set it to
* "root" for clarity.
*/
const root = {
type: 'root',
children: value
};
return transform(root);
/**
* The transform function mimics the approach of a Remark plugin for
* conformity with the other serialization functions. This function converts
* Slate nodes to MDAST nodes, and recursively calls itself to process child
* nodes to arbitrary depth.
*/
function transform(node) {
/**
* Combine adjacent text and inline nodes before processing so they can
* share marks.
*/
const hasBlockChildren = node.children && node.children[0] && blockTypes.includes(node.children[0].type);
const children = hasBlockChildren ? node.children.map(transform).filter(v => v) : convertInlineAndTextChildren(node.children);
const output = convertBlockNode(node, children);
//console.log(JSON.stringify(output, null, 2));
return output;
}
function removeMarkFromNodes(nodes, markType) {
return nodes.map(node => {
const newNode = _objectSpread({}, node);
switch (node.type) {
case 'link':
{
const updatedNodes = removeMarkFromNodes(node.children, markType);
return _objectSpread(_objectSpread({}, node), {}, {
children: updatedNodes
});
}
case 'image':
case 'break':
{
const data = (0, _omit2.default)(node.data, 'marks');
return _objectSpread(_objectSpread({}, node), {}, {
data
});
}
default:
delete newNode[markType];
newNode.marks = newNode.marks ? newNode.marks.filter(({
type
}) => type !== markType) : [];
if (newNode.marks.length === 0) {
delete newNode.marks;
}
return newNode;
}
});
}
function getNodeMarks(node) {
switch (node.type) {
case 'link':
{
// Code marks can't always be condensed together. If all text in a link
// is wrapped in a mark, this function returns that mark and the node
// ends up nested inside of that mark. Code marks sometimes can't do
// that, like when they wrap all of the text content of a link. Here we
// remove code marks before processing so that they stay put.
const nodesWithoutCode = node.children.map(n => {
const newNode = _objectSpread({}, n);
newNode.marks = n.marks ? n.marks.filter(({
type
}) => type !== 'code') : n.marks, delete newNode.code;
return newNode;
});
const childMarks = (0, _map2.default)(nodesWithoutCode, getNodeMarks);
return (0, _intersection2.default)(...childMarks);
}
case 'break':
case 'image':
return (0, _map2.default)((0, _get4.default)(node, ['data', 'marks']), mark => mark.type);
default:
return getNodeMarkArray(node);
}
}
function getNodeMarkArray(node) {
return Object.keys(markMap).filter(mark => !!node[mark]);
}
function getSharedMarks(marks, node) {
const nodeMarks = getNodeMarks(node);
const sharedMarks = (0, _intersection2.default)(marks, nodeMarks);
if (sharedMarks[0] === 'code') {
return nodeMarks.length === 1 ? marks : [];
}
return sharedMarks;
}
function extractFirstMark(nodes) {
let firstGroupMarks = getNodeMarks(nodes[0]) || [];
// If code mark is present, but there are other marks, process others first.
// If only the code mark is present, don't allow it to be shared with other
// nodes.
if (firstGroupMarks[0] === 'code' && firstGroupMarks.length > 1) {
firstGroupMarks = [...(0, _without2.default)('firstGroupMarks', 'code'), 'code'];
}
let splitIndex = 1;
if (firstGroupMarks.length > 0) {
while (splitIndex < nodes.length) {
if (nodes[splitIndex]) {
const sharedMarks = getSharedMarks(firstGroupMarks, nodes[splitIndex]);
if (sharedMarks.length > 0) {
firstGroupMarks = sharedMarks;
} else {
break;
}
}
splitIndex += 1;
}
}
const markType = firstGroupMarks[0];
const childNodes = nodes.slice(0, splitIndex);
const updatedChildNodes = markType ? removeMarkFromNodes(childNodes, markType) : childNodes;
const remainingNodes = nodes.slice(splitIndex);
return [markType, updatedChildNodes, remainingNodes];
}
/**
* Converts the strings returned from `splitToNamedParts` to Slate nodes.
*/
function splitWhitespace(node, {
trailing
} = {}) {
if (!node.text) {
return {
trimmedNode: node
};
}
const exp = trailing ? trailingWhitespaceExp : leadingWhitespaceExp;
const index = node.text.search(exp);
if (index > -1) {
const substringIndex = trailing ? index : index + 1;
const firstSplit = node.text.slice(0, substringIndex);
const secondSplit = node.text.slice(substringIndex);
const whitespace = trailing ? secondSplit : firstSplit;
const text = trailing ? firstSplit : secondSplit;
return {
whitespace,
trimmedNode: _objectSpread(_objectSpread({}, node), {}, {
text
})
};
}
return {
trimmedNode: node
};
}
function collectCenterNodes(nodes, leadingNode, trailingNode) {
switch (nodes.length) {
case 0:
return [];
case 1:
return [trailingNode];
case 2:
return [leadingNode, trailingNode];
default:
return [leadingNode, ...nodes.slice(1, -1), trailingNode];
}
}
function normalizeFlankingWhitespace(nodes) {
const {
whitespace: leadingWhitespace,
trimmedNode: leadingNode
} = splitWhitespace(nodes[0]);
const lastNode = nodes.length > 1 ? (0, _last2.default)(nodes) : leadingNode;
const trailingSplitResult = splitWhitespace(lastNode, {
trailing: true
});
const {
whitespace: trailingWhitespace,
trimmedNode: trailingNode
} = trailingSplitResult;
const centerNodes = collectCenterNodes(nodes, leadingNode, trailingNode).filter(val => val);
return {
leadingWhitespace,
centerNodes,
trailingWhitespace
};
}
function createText(text) {
return text && (0, _unistBuilder.default)('html', text);
}
function isNodeInline(node) {
return inlineTypes.includes(node.type);
}
function convertInlineAndTextChildren(nodes = []) {
const convertedNodes = [];
let remainingNodes = [...nodes];
while (remainingNodes.length > 0) {
const nextNode = remainingNodes[0];
if (isNodeInline(nextNode) || getNodeMarkArray(nextNode).length > 0) {
const [markType, markNodes, remainder] = extractFirstMark(remainingNodes);
/**
* A node with a code mark will be a text node, and will not be adjacent
* to a sibling code node as the Slate schema requires them to be
* merged. Markdown also requires at least a space between inline code
* nodes.
*/
if (markType === 'code') {
const node = markNodes[0];
convertedNodes.push((0, _unistBuilder.default)(markMap[markType], node.data, node.text));
} else if (!markType && markNodes.length === 1 && isNodeInline(nextNode)) {
const node = markNodes[0];
convertedNodes.push(convertInlineNode(node, convertInlineAndTextChildren(node.children)));
} else {
const {
leadingWhitespace,
trailingWhitespace,
centerNodes
} = normalizeFlankingWhitespace(markNodes);
const children = convertInlineAndTextChildren(centerNodes);
const markNode = (0, _unistBuilder.default)(markMap[markType], children);
// Filter out empty marks, otherwise their output literally by
// remark-stringify, eg. an empty bold node becomes "****"
if ((0, _mdastUtilToString.default)(markNode) === '') {
remainingNodes = remainder;
continue;
}
const normalizedNodes = [createText(leadingWhitespace), markNode, createText(trailingWhitespace)].filter(val => val);
convertedNodes.push(...normalizedNodes);
}
remainingNodes = remainder;
} else if (nextNode.type === 'break') {
remainingNodes = remainingNodes.slice(1);
convertedNodes.push(convertInlineNode(nextNode));
} else {
remainingNodes.shift();
convertedNodes.push((0, _unistBuilder.default)('html', nextNode.text));
}
}
return convertedNodes;
}
function convertCodeBlock(node) {
return _objectSpread(_objectSpread({}, node), {}, {
type: 'code-block',
data: _objectSpread(_objectSpread({}, node.data), node.data.shortcodeData)
});
}
function convertBlockNode(node, children) {
if (node.type == 'shortcode' && node.data.shortcode == 'code-block') {
node = convertCodeBlock(node);
}
switch (node.type) {
/**
* General
*
* Convert simple cases that only require a type and children, with no
* additional properties.
*/
case 'root':
case 'paragraph':
case 'quote':
case 'list-item':
case 'table':
case 'table-row':
case 'table-cell':
{
return (0, _unistBuilder.default)(typeMap[node.type], children);
}
/**
* Lists
*
* Enclose list items in paragraphs
*/
// case 'list-item':
// return u(typeMap[node.type], [{ type: 'paragraph', children }]);
/**
* Shortcodes
*
* Shortcode nodes only exist in Slate's Raw AST if they were inserted
* via the plugin toolbar in memory, so they should always have
* shortcode data attached. The "shortcode" data property contains the
* name of the registered shortcode plugin, and the "shortcodeData" data
* property contains the data received from the shortcode plugin's
* `fromBlock` method when the shortcode node was created.
*
* Here we create a `shortcode` MDAST node that contains only the shortcode
* data.
*/
case 'shortcode':
{
const {
data
} = node;
return (0, _unistBuilder.default)(typeMap[node.type], {
data
});
}
/**
* Headings
*
* Slate schemas don't usually infer basic type info from data, so each
* level of heading is a separately named type. The MDAST schema just
* has a single "heading" type with the depth stored in a "depth"
* property on the node. Here we derive the depth from the Slate node
* type - e.g., for "heading-two", we need a depth value of "2".
*/
case 'heading-one':
case 'heading-two':
case 'heading-three':
case 'heading-four':
case 'heading-five':
case 'heading-six':
{
const depthMap = {
one: 1,
two: 2,
three: 3,
four: 4,
five: 5,
six: 6
};
const depthText = node.type.split('-')[1];
const depth = depthMap[depthText];
const mdastNode = (0, _unistBuilder.default)(typeMap[node.type], {
depth
}, children);
if ((0, _mdastUtilToString.default)(mdastNode)) {
return mdastNode;
}
return;
}
/**
* Code Blocks
*
* Code block nodes may have a single text child, or instead be void and
* store their value in `data.code`. They also may have a code language
* stored in the "lang" data property. Here we transfer both the node value
* and the "lang" data property to the new MDAST node, and spread any
* remaining data as `data`.
*/
case 'code-block':
{
var _children$;
const _get2 = (0, _get4.default)(node, 'data', {}),
{
lang,
code
} = _get2,
data = _objectWithoutProperties(_get2, _excluded);
const value = voidCodeBlock ? code : (_children$ = children[0]) === null || _children$ === void 0 ? void 0 : _children$.value;
return (0, _unistBuilder.default)(typeMap[node.type], {
lang,
data
}, value || '');
}
/**
* Lists
*
* Our Slate schema has separate node types for ordered and unordered
* lists, but the MDAST spec uses a single type with a boolean "ordered"
* property to indicate whether the list is numbered. The MDAST spec also
* allows for a "start" property to indicate the first number used for an
* ordered list. Here we translate both values to our Slate schema.
*/
case 'numbered-list':
case 'bulleted-list':
{
const ordered = node.type === 'numbered-list';
const props = {
ordered,
start: (0, _get4.default)(node.data, 'start') || 1
};
return (0, _unistBuilder.default)(typeMap[node.type], props, children);
}
/**
* Thematic Break
*
* Thematic break is a block level break. They cannot have children.
*/
case 'thematic-break':
{
return (0, _unistBuilder.default)(typeMap[node.type]);
}
}
}
function convertInlineNode(node, children) {
switch (node.type) {
/**
* Break
*
* Breaks are phrasing level breaks. They cannot have children.
*/
case 'break':
{
return (0, _unistBuilder.default)(typeMap[node.type]);
}
/**
* Links
*
* Url is now stored in data for slate, so we need to pull it out.
*/
case 'link':
{
const {
title,
data
} = node;
return (0, _unistBuilder.default)(typeMap[node.type], _objectSpread({
url: data === null || data === void 0 ? void 0 : data.url,
title
}, data), children);
}
/**
* Images
*
* This transformation is almost identical to that of links, except for the
* lack of child nodes and addition of `alt` attribute data.
*/
case 'image':
{
const _get3 = (0, _get4.default)(node, 'data', {}),
{
url,
title,
alt
} = _get3,
data = _objectWithoutProperties(_get3, _excluded2);
return (0, _unistBuilder.default)(typeMap[node.type], {
url,
title,
alt,
data
});
}
}
}
}