Files
coopgo/node_modules/decap-cms-widget-markdown/dist/esm/serializers/remarkSlate.js
sgauthier 6e64e138e2
All checks were successful
Publish To Prod / deploy_and_publish (push) Successful in 35s
planning
2024-10-14 09:15:30 +02:00

527 lines
16 KiB
JavaScript

"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
});
}
}
}
}