@lofcz/platejs-markdown 52.3.9 → 53.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +137 -71
  2. package/package.json +8 -5
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { ElementApi, KEYS, TextApi, bindFirst, createTSlatePlugin, getPluginKey, getPluginType, isUrl } from "platejs";
2
2
  import "mdast-util-mdx";
3
+ import { normalizeDateValue } from "@platejs/date";
3
4
  import kebabCase from "lodash/kebabCase.js";
4
5
  import { defaultHandlers, toMarkdown } from "mdast-util-to-markdown";
5
6
  import { unified } from "unified";
@@ -131,7 +132,7 @@ function propsToAttributes(props) {
131
132
  return Object.entries(props).map(([name, value]) => ({
132
133
  name,
133
134
  type: "mdxJsxAttribute",
134
- value: typeof value === "string" ? value : JSON.stringify(value)
135
+ value: typeof value === "string" || value?.type === "mdxJsxAttributeValueExpression" ? value : JSON.stringify(value)
135
136
  }));
136
137
  }
137
138
 
@@ -239,7 +240,7 @@ function createMediaRule() {
239
240
  };
240
241
  },
241
242
  serialize: (node, options) => {
242
- const { id, children, type, url, ...rest } = node;
243
+ const { children, type, url, ...rest } = node;
243
244
  return {
244
245
  attributes: propsToAttributes({
245
246
  ...rest,
@@ -255,6 +256,7 @@ function createMediaRule() {
255
256
  const mediaRules = {
256
257
  audio: createMediaRule(),
257
258
  file: createMediaRule(),
259
+ media_embed: createMediaRule(),
258
260
  video: createMediaRule()
259
261
  };
260
262
 
@@ -1296,6 +1298,7 @@ const serializeMd = (editor, options) => {
1296
1298
  const { remarkPlugins, value } = mergedOptions;
1297
1299
  const toRemarkProcessor = unified().use(remarkPlugins ?? []).use(remarkStringify, {
1298
1300
  emphasis: "_",
1301
+ resourceLink: false,
1299
1302
  ...mergedOptions?.remarkStringifyOptions
1300
1303
  });
1301
1304
  const mdast = slateToMdast({
@@ -1313,6 +1316,7 @@ const slateToMdast = ({ children, options }) => {
1313
1316
 
1314
1317
  //#endregion
1315
1318
  //#region src/lib/rules/defaultRules.ts
1319
+ const BARE_AUTOLINK_PROTOCOL_REGEX = /^https?:\/\//i;
1316
1320
  const LEADING_NEWLINE_REGEX = /^\n/;
1317
1321
  function isBoolean(value) {
1318
1322
  return value === true || value === false || !!value && typeof value === "object" && Object.prototype.toString.call(value) === "[object Boolean]";
@@ -1330,6 +1334,33 @@ const deserializeClassicListItemChildren = (mdastChildren, deco, options) => {
1330
1334
  if (!children.some((child) => child.type === licType)) children.unshift(createClassicListItemContent(options.editor));
1331
1335
  return children;
1332
1336
  };
1337
+ const groupInlineChildrenIntoParagraphs = (editor, children = []) => {
1338
+ const paragraphType = getPluginType(editor, KEYS.p);
1339
+ const elements = [];
1340
+ let inlineNodes = [];
1341
+ const flushInlineNodes = () => {
1342
+ if (inlineNodes.length === 0) return;
1343
+ elements.push({
1344
+ children: inlineNodes,
1345
+ type: paragraphType
1346
+ });
1347
+ inlineNodes = [];
1348
+ };
1349
+ children.forEach((child) => {
1350
+ if (ElementApi.isElement(child) && !editor.api.isInline(child) && editor.api.isBlock(child)) {
1351
+ flushInlineNodes();
1352
+ elements.push(child);
1353
+ return;
1354
+ }
1355
+ inlineNodes.push(child);
1356
+ });
1357
+ flushInlineNodes();
1358
+ if (elements.length > 0) return elements;
1359
+ return [{
1360
+ children: [{ text: "" }],
1361
+ type: paragraphType
1362
+ }];
1363
+ };
1333
1364
  const defaultRules = {
1334
1365
  a: {
1335
1366
  deserialize: (mdastNode, deco, options) => ({
@@ -1337,48 +1368,29 @@ const defaultRules = {
1337
1368
  type: getPluginType(options.editor, KEYS.a),
1338
1369
  url: mdastNode.url
1339
1370
  }),
1340
- serialize: (node, options) => ({
1341
- children: convertNodesSerialize(node.children, options),
1342
- type: "link",
1343
- url: node.url
1344
- })
1345
- },
1346
- blockquote: {
1347
- deserialize: (mdastNode, deco, options) => {
1348
- return {
1349
- children: (mdastNode.children.length > 0 ? mdastNode.children.flatMap((paragraph, index, children) => {
1350
- if (paragraph.type === "paragraph") {
1351
- if (children.length > 1 && children.length - 1 !== index) {
1352
- const paragraphChildren = convertChildrenDeserialize(paragraph.children, deco, options);
1353
- paragraphChildren.push({ text: "\n" }, { text: "\n" });
1354
- return paragraphChildren;
1355
- }
1356
- return convertChildrenDeserialize(paragraph.children, deco, options);
1357
- }
1358
- if ("children" in paragraph) return convertChildrenDeserialize(paragraph.children, deco, options);
1359
- return [{ text: "" }];
1360
- }) : [{ text: "" }]).flatMap((child) => child.type === "blockquote" ? child.children : [child]),
1361
- type: getPluginType(options.editor, KEYS.blockquote)
1362
- };
1363
- },
1364
1371
  serialize: (node, options) => {
1365
- const nodes = [];
1366
- for (const child of node.children) if (child.text === "\n") nodes.push({ type: "break" });
1367
- else nodes.push(child);
1368
- const paragraphChildren = convertNodesSerialize(nodes, options);
1369
- if (paragraphChildren.length > 0 && paragraphChildren.at(-1).type === "break") {
1370
- paragraphChildren.at(-1).type = "html";
1371
- paragraphChildren.at(-1).value = "\n<br />";
1372
- }
1372
+ const children = convertNodesSerialize(node.children, options);
1373
+ if (children.length === 1 && children[0]?.type === "text" && children[0].value === node.url && BARE_AUTOLINK_PROTOCOL_REGEX.test(node.url ?? "")) return {
1374
+ type: "html",
1375
+ value: node.url
1376
+ };
1373
1377
  return {
1374
- children: [{
1375
- children: paragraphChildren,
1376
- type: "paragraph"
1377
- }],
1378
- type: "blockquote"
1378
+ children,
1379
+ type: "link",
1380
+ url: node.url
1379
1381
  };
1380
1382
  }
1381
1383
  },
1384
+ blockquote: {
1385
+ deserialize: (mdastNode, deco, options) => ({
1386
+ children: groupInlineChildrenIntoParagraphs(options.editor, convertNodesDeserialize(mdastNode.children, deco, options)),
1387
+ type: getPluginType(options.editor, KEYS.blockquote)
1388
+ }),
1389
+ serialize: (node, options) => ({
1390
+ children: convertNodesSerialize(groupInlineChildrenIntoParagraphs(options.editor, node.children), options),
1391
+ type: "blockquote"
1392
+ })
1393
+ },
1382
1394
  bold: {
1383
1395
  mark: true,
1384
1396
  deserialize: (mdastNode, deco, options) => convertTextsDeserialize(mdastNode, deco, options)
@@ -1454,18 +1466,25 @@ const defaultRules = {
1454
1466
  },
1455
1467
  date: {
1456
1468
  deserialize(mdastNode, _deco, options) {
1469
+ const props = parseAttributes(mdastNode.attributes);
1457
1470
  return {
1458
1471
  children: [{ text: "" }],
1459
- date: (mdastNode.children?.[0])?.value || "",
1472
+ ...normalizeDateValue(typeof props.value === "string" ? props.value : (mdastNode.children?.[0])?.value || ""),
1460
1473
  type: getPluginType(options.editor, KEYS.date)
1461
1474
  };
1462
1475
  },
1463
- serialize({ date }) {
1476
+ serialize({ date, rawDate }) {
1477
+ if (date && !rawDate) return {
1478
+ attributes: propsToAttributes({ value: date }),
1479
+ children: [],
1480
+ name: "date",
1481
+ type: "mdxJsxTextElement"
1482
+ };
1464
1483
  return {
1465
1484
  attributes: [],
1466
1485
  children: [{
1467
1486
  type: "text",
1468
- value: date ?? ""
1487
+ value: rawDate ?? date ?? ""
1469
1488
  }],
1470
1489
  name: "date",
1471
1490
  type: "mdxJsxTextElement"
@@ -1490,13 +1509,47 @@ const defaultRules = {
1490
1509
  value: node.texExpression
1491
1510
  })
1492
1511
  },
1493
- footnoteDefinition: { deserialize: (mdastNode, deco, options) => {
1494
- return {
1495
- children: convertChildrenDeserialize(mdastNode.children, deco, options).flatMap((child) => child.type === "p" ? child.children : [child]),
1496
- type: getPluginType(options.editor, KEYS.p)
1497
- };
1498
- } },
1499
- footnoteReference: {},
1512
+ footnoteDefinition: {
1513
+ deserialize: (mdastNode, deco, options) => {
1514
+ const paragraphType = getPluginType(options.editor, KEYS.p);
1515
+ const blocks = convertNodesDeserialize(mdastNode.children, deco, options).map((child) => child.type === paragraphType ? child : {
1516
+ children: [child],
1517
+ type: paragraphType
1518
+ });
1519
+ const identifier = mdastNode.identifier;
1520
+ if (blocks.length === 0) return {
1521
+ children: [{
1522
+ children: [{ text: "" }],
1523
+ type: paragraphType
1524
+ }],
1525
+ identifier,
1526
+ type: getPluginType(options.editor, "footnoteDefinition")
1527
+ };
1528
+ return {
1529
+ children: blocks,
1530
+ identifier,
1531
+ type: getPluginType(options.editor, "footnoteDefinition")
1532
+ };
1533
+ },
1534
+ serialize: (node, options) => ({
1535
+ children: convertNodesSerialize(node.children, options),
1536
+ identifier: node.identifier,
1537
+ type: "footnoteDefinition"
1538
+ })
1539
+ },
1540
+ footnoteReference: {
1541
+ deserialize: (mdastNode, _deco, options) => {
1542
+ return {
1543
+ children: [{ text: "" }],
1544
+ identifier: mdastNode.identifier ?? "",
1545
+ type: getPluginType(options.editor, "footnoteReference")
1546
+ };
1547
+ },
1548
+ serialize: (node) => ({
1549
+ identifier: node.identifier ?? node.children?.map((child) => child.text ?? "").join("") ?? "",
1550
+ type: "footnoteReference"
1551
+ })
1552
+ },
1500
1553
  heading: {
1501
1554
  deserialize: (mdastNode, deco, options) => {
1502
1555
  const defaultType = {
@@ -1568,11 +1621,11 @@ const defaultRules = {
1568
1621
  ...rest
1569
1622
  };
1570
1623
  },
1571
- serialize: ({ caption, url }) => {
1624
+ serialize: ({ caption, title, url }) => {
1572
1625
  return {
1573
1626
  children: [{
1574
1627
  alt: caption ? caption.map((c) => c.text).join("") : void 0,
1575
- title: caption ? caption.map((c) => c.text).join("") : void 0,
1628
+ title: typeof title === "string" ? title : void 0,
1576
1629
  type: "image",
1577
1630
  url
1578
1631
  }],
@@ -2018,6 +2071,34 @@ const getDeserializerByKey = (key, options) => {
2018
2071
 
2019
2072
  //#endregion
2020
2073
  //#region src/lib/deserializer/utils/customMdxDeserialize.ts
2074
+ const MDX_ATTR_NAME_TO_HTML_ATTR = {
2075
+ className: "class",
2076
+ htmlFor: "for"
2077
+ };
2078
+ const serializeUnknownMdxChild = (child) => {
2079
+ if (child?.type === "mdxJsxTextElement" || child?.type === "mdxJsxFlowElement") return serializeUnknownMdxNode(child);
2080
+ if ("value" in (child ?? {})) return child.value ?? "";
2081
+ if (Array.isArray(child?.children)) return child.children.map(serializeUnknownMdxChild).join("");
2082
+ return "";
2083
+ };
2084
+ const serializeUnknownMdxAttributes = (attributes) => {
2085
+ if (!attributes?.length) return "";
2086
+ const serialized = attributes.map((attribute) => {
2087
+ const name = MDX_ATTR_NAME_TO_HTML_ATTR[attribute.name] ?? attribute.name;
2088
+ if (attribute.value === void 0 || attribute.value === null) return name;
2089
+ if (typeof attribute.value === "object" && attribute.value?.type === "mdxJsxAttributeValueExpression") return `${name}={${attribute.value.value}}`;
2090
+ return `${name}="${String(attribute.value)}"`;
2091
+ });
2092
+ return serialized.length > 0 ? ` ${serialized.join(" ")}` : "";
2093
+ };
2094
+ const serializeUnknownMdxNode = (mdastNode) => {
2095
+ const attrs = serializeUnknownMdxAttributes(mdastNode.attributes);
2096
+ const openTag = `<${mdastNode.name}${attrs}`;
2097
+ if (!mdastNode.children?.length) return `${openTag} />`;
2098
+ const inner = mdastNode.children.map(serializeUnknownMdxChild).join(mdastNode.type === "mdxJsxFlowElement" ? "\n" : "");
2099
+ if (mdastNode.type === "mdxJsxFlowElement") return `${openTag}>\n${inner}\n</${mdastNode.name}>`;
2100
+ return `${openTag}>${inner}</${mdastNode.name}>`;
2101
+ };
2021
2102
  const customMdxDeserialize = (mdastNode, deco, options) => {
2022
2103
  const customJsxElementKey = mdastNode.name;
2023
2104
  const key = getPluginKey(options.editor, customJsxElementKey) ?? mdastNode.name;
@@ -2025,26 +2106,11 @@ const customMdxDeserialize = (mdastNode, deco, options) => {
2025
2106
  const nodeParserDeserialize = getDeserializerByKey(mdastToPlate(options.editor, key), options);
2026
2107
  if (nodeParserDeserialize) return nodeParserDeserialize(mdastNode, deco, options);
2027
2108
  } else console.warn("This MDX node does not have a parser for deserialization", mdastNode);
2028
- if (mdastNode.type === "mdxJsxTextElement") {
2029
- const tagName = mdastNode.name;
2030
- let textContent = "";
2031
- if (mdastNode.children) textContent = mdastNode.children.map((child) => {
2032
- if ("value" in child) return child.value;
2033
- return "";
2034
- }).join("");
2035
- return [{ text: `<${tagName}>${textContent}</${tagName}>` }];
2036
- }
2037
- if (mdastNode.type === "mdxJsxFlowElement") {
2038
- const tagName = mdastNode.name;
2039
- return [{
2040
- children: [
2041
- { text: `<${tagName}>\n` },
2042
- ...convertChildrenDeserialize(mdastNode.children, deco, options),
2043
- { text: `\n</${tagName}>` }
2044
- ],
2045
- type: getPluginType(options.editor, KEYS.p)
2046
- }];
2047
- }
2109
+ if (mdastNode.type === "mdxJsxTextElement") return [{ text: serializeUnknownMdxNode(mdastNode) }];
2110
+ if (mdastNode.type === "mdxJsxFlowElement") return [{
2111
+ children: [{ text: serializeUnknownMdxNode(mdastNode) }],
2112
+ type: getPluginType(options.editor, KEYS.p)
2113
+ }];
2048
2114
  };
2049
2115
 
2050
2116
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lofcz/platejs-markdown",
3
- "version": "52.3.9",
3
+ "version": "53.0.0",
4
4
  "description": "Markdown serializer plugin for Plate",
5
5
  "keywords": [
6
6
  "markdown",
@@ -42,18 +42,21 @@
42
42
  "remark-stringify": "^11.0.0",
43
43
  "ts-essentials": "10.1.0",
44
44
  "unified": "^11.0.5",
45
- "unist-util-visit": "5.0.0"
45
+ "unist-util-visit": "5.0.0",
46
+ "@platejs/date": "npm:@lofcz/platejs-date@53.0.0"
46
47
  },
47
48
  "devDependencies": {
48
49
  "@types/mdast": "^4.0.4",
49
50
  "@types/unist": "^3.0.3",
51
+ "remark-emoji": "5.0.1",
50
52
  "remark-gfm": "4.0.1",
51
53
  "remark-math": "6.0.0",
52
- "platejs": "npm:@lofcz/platejs@52.3.6",
53
- "@plate/scripts": "1.0.0"
54
+ "@plate/scripts": "1.0.0",
55
+ "@platejs/footnote": "npm:@lofcz/platejs-footnote@53.0.0",
56
+ "platejs": "npm:@lofcz/platejs@53.0.0"
54
57
  },
55
58
  "peerDependencies": {
56
- "platejs": ">=52.0.11",
59
+ "platejs": ">=53.0.0",
57
60
  "react": ">=18.0.0",
58
61
  "react-dom": ">=18.0.0"
59
62
  },