Files
coopgo/node_modules/decap-cms-widget-markdown/dist/esm/serializers/slateRemark.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

512 lines
19 KiB
JavaScript

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