@lexical/markdown
Version:
This package contains Markdown helpers and functionality for Lexical.
10 lines (8 loc) • 17.1 kB
JavaScript
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
;var e=require("lexical"),t=require("@lexical/list"),n=require("@lexical/rich-text"),o=require("@lexical/utils"),r=require("@lexical/code"),i=require("@lexical/link");function s(e,t){const n={};for(const o of e){const e=t(o);e&&(n[e]?n[e].push(o):n[e]=[o])}return n}function c(e){const t=s(e,e=>e.type);return{element:t.element||[],multilineElement:t["multiline-element"]||[],textFormat:t["text-format"]||[],textMatch:t["text-match"]||[]}}const l=/[!-/:-@[-`{-~\s]/,a=/^\s{0,3}$/;function f(t){if(!e.$isParagraphNode(t))return!1;const n=t.getFirstChild();return null==n||1===t.getChildrenSize()&&e.$isTextNode(n)&&a.test(n.getTextContent())}function d(t,n,o,r){for(const e of n){if(!e.export)continue;const n=e.export(t,e=>u(e,o,r));if(null!=n)return n}return e.$isElementNode(t)?u(t,o,r):e.$isDecoratorNode(t)?t.getTextContent():null}function u(t,n,o,r,i){const s=[],c=t.getChildren();r||(r=[]),i||(i=[]);e:for(const t of c){for(const e of o){if(!e.export)continue;const c=e.export(t,e=>u(e,n,o,r,[...i,...r]),(e,t)=>g(e,t,n,r,i));if(null!=c){s.push(c);continue e}}e.$isLineBreakNode(t)?s.push("\n"):e.$isTextNode(t)?s.push(g(t,t.getTextContent(),n,r,i)):e.$isElementNode(t)?s.push(u(t,n,o,r,i)):e.$isDecoratorNode(t)&&s.push(t.getTextContent())}return s.join("")}function g(e,t,n,o,r){let i=0===e.getFormat()?t:function(e){return e.replace(/^\s+|\s+$/g,e=>[...e].map(e=>"&#"+e.codePointAt(0)+";").join(""))}(t);e.hasFormat("code")||(i=i.replace(/([*_`~\\])/g,"\\$1"));let s="",c="",l="";const a=p(e,!0),f=p(e,!1),d=new Set;for(const t of n){const n=t.format[0],r=t.tag;x(e,n)&&!d.has(n)&&(d.add(n),x(a,n)&&o.find(e=>e.tag===r)||(o.push({format:n,tag:r}),s+=r))}for(let t=0;t<o.length;t++){const n=x(e,o[t].format),i=x(f,o[t].format);if(n&&i)continue;const s=[...o];for(;s.length>t;){const e=s.pop();r&&e&&r.find(t=>t.tag===e.tag)||(e&&"string"==typeof e.tag&&(n?i||(l+=e.tag):c+=e.tag),o.pop())}break}return i=s+i+l,c+i}function p(t,n){let o=n?t.getPreviousSibling():t.getNextSibling();if(!o){const e=t.getParentOrThrow();e.isInline()&&(o=n?e.getPreviousSibling():e.getNextSibling())}for(;o;){if(e.$isElementNode(o)){if(!o.isInline())break;const t=n?o.getLastDescendant():o.getFirstDescendant();if(e.$isTextNode(t))return t;o=n?o.getPreviousSibling():o.getNextSibling()}if(e.$isTextNode(o))return o;if(!e.$isElementNode(o))return null}return null}function x(t,n){return e.$isTextNode(t)&&t.hasFormat(n)}function h(e,t){const n=function(e,t){const n=e.match(t.openTagsRegExp);if(null==n)return null;for(const o of n){const n=o.replace(/^\s/,""),r=t.fullMatchRegExpByTag[n];if(null==r)continue;const i=e.match(r),s=t.transformersByTag[n];if(null!=i&&null!=s){if(!1!==s.intraword)return i;const{index:t=0}=i,n=e[t-1],o=e[t+i[0].length];if((!n||l.test(n))&&(!o||l.test(o)))return i}}return null}(e.getTextContent(),t);if(!n)return null;const o=n.index||0;return{endIndex:o+n[0].length,match:n,startIndex:o,transformer:t.transformersByTag[n[1]]}}function m(t){return e.$isTextNode(t)&&!t.hasFormat("code")}function $(e,t,n){let o=h(e,t),r=function(e,t){const n=e;let o,r,i,s;for(const e of t){if(!e.replace||!e.importRegExp)continue;const t=n.getTextContent().match(e.importRegExp);if(!t)continue;const c=t.index||0,l=e.getEndIndex?e.getEndIndex(n,t):c+t[0].length;!1!==l&&(void 0===o||void 0===r||c<o&&(l>r||l<=o))&&(o=c,r=l,i=e,s=t)}return void 0===o||void 0===r||void 0===i||void 0===s?null:{endIndex:r,match:s,startIndex:o,transformer:i}}(e,n);if(o&&r&&(o.startIndex<=r.startIndex&&o.endIndex>=r.endIndex||r.startIndex>o.endIndex?r=null:o=null),o){const r=function(e,t,n,o,r){const i=e.getTextContent();let s,c,l;if(r[0]===i?s=e:0===t?[s,c]=e.splitText(n):[l,s,c]=e.splitText(t,n),s.setTextContent(r[2]),o)for(const e of o.format)s.hasFormat(e)||s.toggleFormat(e);return{nodeAfter:c,nodeBefore:l,transformedNode:s}}(e,o.startIndex,o.endIndex,o.transformer,o.match);m(r.nodeAfter)&&$(r.nodeAfter,t,n),m(r.nodeBefore)&&$(r.nodeBefore,t,n),m(r.transformedNode)&&$(r.transformedNode,t,n)}else if(r){const o=function(e,t,n,o,r){let i,s,c;return 0===t?[i,s]=e.splitText(n):[c,i,s]=e.splitText(t,n),o.replace?{nodeAfter:s,nodeBefore:c,transformedNode:o.replace(i,r)||void 0}:null}(e,r.startIndex,r.endIndex,r.transformer,r.match);if(!o)return;m(o.nodeAfter)&&$(o.nodeAfter,t,n),m(o.nodeBefore)&&$(o.nodeBefore,t,n),m(o.transformedNode)&&$(o.transformedNode,t,n)}const i=e.getTextContent().replace(/\\([*_`~\\])/g,"$1").replace(/&#(\d+);/g,(e,t)=>String.fromCodePoint(t));e.setTextContent(i)}function N(t,n=!1){const o=c(t),r=function(e){const t={},n={},o=[],r="(?<![\\\\])";for(const r of e){const{tag:e}=r;t[e]=r;const i=e.replace(/(\*|\^|\+)/g,"\\$1");o.push(i),1===e.length?n[e]="`"===e?new RegExp("(?<![\\\\`])(`)((?:\\\\`|[^`])+?)(`)(?!`)"):new RegExp(`(?<![\\\\${i}])(${i})((\\\\${i})?.*?[^${i}\\s](\\\\${i})?)((?<!\\\\)|(?<=\\\\\\\\))(${i})(?![\\\\${i}])`):n[e]=new RegExp(`(?<!\\\\)(${i})((\\\\${i})?.*?[^\\s](\\\\${i})?)((?<!\\\\)|(?<=\\\\\\\\))(${i})(?!\\\\)`)}return{fullMatchRegExpByTag:n,openTagsRegExp:new RegExp(`${r}(${o.join("|")})`,"g"),transformersByTag:t}}(o.textFormat);return(t,i)=>{const s=t.split("\n"),c=s.length,l=i||e.$getRoot();l.clear();for(let e=0;e<c;e++){const t=s[e],[i,c]=T(s,e,o.multilineElement,l);i?e=c:E(t,l,o.element,r,o.textMatch,n)}const a=l.getChildren();for(const e of a)!n&&f(e)&&l.getChildrenSize()>1&&e.remove();null!==e.$getSelection()&&l.selectStart()}}function T(e,t,n,o){for(const r of n){const{handleImportAfterStartMatch:n,regExpEnd:i,regExpStart:s,replace:c}=r,l=e[t].match(s);if(!l)continue;if(n){const i=n({lines:e,rootNode:o,startLineIndex:t,startMatch:l,transformer:r});if(null===i)continue;if(i)return i}const a="object"==typeof i&&"regExp"in i?i.regExp:i,f=i&&"object"==typeof i&&"optional"in i?i.optional:!i;let d=t;const u=e.length;for(;d<u;){const n=a?e[d].match(a):null;if(!n&&(!f||f&&d<u-1)){d++;continue}if(n&&t===d&&n.index===l.index){d++;continue}const r=[];if(n&&t===d)r.push(e[t].slice(l[0].length,-n[0].length));else for(let o=t;o<=d;o++)if(o===t){const t=e[o].slice(l[0].length);r.push(t)}else if(o===d&&n){const t=e[o].slice(0,-n[0].length);r.push(t)}else r.push(e[o]);if(!1!==c(o,null,l,n,r,!0))return[!0,d];break}}return[!1,t]}function E(r,i,s,c,l,a){const f=e.$createTextNode(r),d=e.$createParagraphNode();d.append(f),i.append(d);for(const{regExp:e,replace:t}of s){const n=r.match(e);if(n&&(f.setTextContent(r.slice(n[0].length)),!1!==t(d,[f],n,!0)))break}if($(f,c,l),d.isAttached()&&r.length>0){const r=d.getPreviousSibling();if(!a&&(e.$isParagraphNode(r)||n.$isQuoteNode(r)||t.$isListNode(r))){let n=r;if(t.$isListNode(r)){const e=r.getLastDescendant();n=null==e?null:o.$findMatchingParent(e,t.$isListItemNode)}null!=n&&n.getTextContentSize()>0&&(n.splice(n.getChildrenSize(),0,[e.$createLineBreakNode(),...d.getChildren()]),d.remove())}}}function S(e,...t){const n=new URL("https://lexical.dev/docs/error"),o=new URLSearchParams;o.append("code",e);for(const e of t)o.append("v",e);throw n.search=o.toString(),Error(`Minified Lexical error #${e}; visit ${n.toString()} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`)}const C=/^(\s*)(\d{1,})\.\s/,L=/^(\s*)[-*+]\s/,R=/^(\s*)(?:[-*+]\s)?\s?(\[(\s|x)?\])\s/i,I=/^(#{1,6})\s/,y=/^>\s/,v=/^[ \t]*```([\w-]+)?/,_=/[ \t]*```$/,b=/^[ \t]*```[^`]+(?:(?:`{1,2}|`{4,})[^`]+)*```(?:[^`]|$)/,A=/^(?:\|)(.+)(?:\|)\s?$/,F=/^(\| ?:?-*:? ?)+\|\s?$/,k=/^<[a-z_][\w-]*(?:\s[^<>]*)?\/?>/i,M=/^<\/[a-z_][\w-]*\s*>/i,O=e=>new RegExp(`(?:${e.source})$`,e.flags),w=e.createState("mdListMarker",{parse:e=>"string"==typeof e&&/^[-*+]$/.test(e)?e:"-"}),B=e=>(t,n,o,r)=>{const i=e(o);i.append(...n),t.replace(i),r||i.select(0,0)};const D=n=>(o,r,i,s)=>{const c=o.getPreviousSibling(),l=o.getNextSibling(),a=t.$createListItemNode("check"===n?"x"===i[3]:void 0),f=i[0].trim()[0],d="bullet"!==n&&"check"!==n||f!==w.parse(f)?void 0:f;if(t.$isListNode(l)&&l.getListType()===n){d&&e.$setState(l,w,d);const t=l.getFirstChild();null!==t?t.insertBefore(a):l.append(a),o.remove()}else if(t.$isListNode(c)&&c.getListType()===n)d&&e.$setState(c,w,d),c.append(a),o.remove();else{const r=t.$createListNode(n,"number"===n?Number(i[2]):void 0);d&&e.$setState(r,w,d),r.append(a),o.replace(r)}a.append(...r),s||a.select(0,0);const u=function(e){const t=e.match(/\t/g),n=e.match(/ /g);let o=0;return t&&(o+=t.length),n&&(o+=Math.floor(n.length/4)),o}(i[1]);u&&a.setIndent(u)},P=(n,o,r)=>{const i=[],s=n.getChildren();let c=0;for(const l of s)if(t.$isListItemNode(l)){if(1===l.getChildrenSize()){const e=l.getFirstChild();if(t.$isListNode(e)){i.push(P(e,o,r+1));continue}}const s=" ".repeat(4*r),a=n.getListType(),f=e.$getState(n,w),d="number"===a?`${n.getStart()+c}. `:"check"===a?`${f} [${l.getChecked()?"x":" "}] `:f+" ";i.push(s+d+o(l)),c++}return i.join("\n")},H={dependencies:[n.HeadingNode],export:(e,t)=>{if(!n.$isHeadingNode(e))return null;const o=Number(e.getTag().slice(1));return"#".repeat(o)+" "+t(e)},regExp:I,replace:B(e=>{const t="h"+e[1].length;return n.$createHeadingNode(t)}),type:"element"},U={dependencies:[n.QuoteNode],export:(e,t)=>{if(!n.$isQuoteNode(e))return null;const o=t(e).split("\n"),r=[];for(const e of o)r.push("> "+e);return r.join("\n")},regExp:y,replace:(t,o,r,i)=>{if(i){const r=t.getPreviousSibling();if(n.$isQuoteNode(r))return r.splice(r.getChildrenSize(),0,[e.$createLineBreakNode(),...o]),void t.remove()}const s=n.$createQuoteNode();s.append(...o),t.replace(s),i||s.select(0,0)},type:"element"},j={dependencies:[r.CodeNode],export:e=>{if(!r.$isCodeNode(e))return null;const t=e.getTextContent();return"```"+(e.getLanguage()||"")+(t?"\n"+t:"")+"\n```"},regExpEnd:{optional:!0,regExp:_},regExpStart:v,replace:(t,n,o,i,s,c)=>{let l,a;if(!n&&s){if(1===s.length)i?(l=r.$createCodeNode(),a=o[1]+s[0]):(l=r.$createCodeNode(o[1]),a=s[0].startsWith(" ")?s[0].slice(1):s[0]);else{if(l=r.$createCodeNode(o[1]),0===s[0].trim().length)for(;s.length>0&&!s[0].length;)s.shift();else s[0]=s[0].startsWith(" ")?s[0].slice(1):s[0];for(;s.length>0&&!s[s.length-1].length;)s.pop();a=s.join("\n")}const n=e.$createTextNode(a);l.append(n),t.append(l)}else n&&B(e=>r.$createCodeNode(e?e[1]:void 0))(t,n,o,c)},type:"multiline-element"},z={dependencies:[t.ListNode,t.ListItemNode],export:(e,n)=>t.$isListNode(e)?P(e,n,0):null,regExp:L,replace:D("bullet"),type:"element"},q={dependencies:[t.ListNode,t.ListItemNode],export:(e,n)=>t.$isListNode(e)?P(e,n,0):null,regExp:R,replace:D("check"),type:"element"},G={dependencies:[t.ListNode,t.ListItemNode],export:(e,n)=>t.$isListNode(e)?P(e,n,0):null,regExp:C,replace:D("number"),type:"element"},Q={format:["code"],tag:"`",type:"text-format"},W={format:["highlight"],tag:"==",type:"text-format"},K={format:["bold","italic"],tag:"***",type:"text-format"},X={format:["bold","italic"],intraword:!1,tag:"___",type:"text-format"},J={format:["bold"],tag:"**",type:"text-format"},V={format:["bold"],intraword:!1,tag:"__",type:"text-format"},Y={format:["strikethrough"],tag:"~~",type:"text-format"},Z={format:["italic"],tag:"*",type:"text-format"},ee={format:["italic"],intraword:!1,tag:"_",type:"text-format"},te={dependencies:[i.LinkNode],export:(e,t,n)=>{if(!i.$isLinkNode(e)||i.$isAutoLinkNode(e))return null;const o=e.getTitle(),r=t(e);return o?`[${r}](${e.getURL()} "${o}")`:`[${r}](${e.getURL()})`},importRegExp:/(?:\[(.+?)\])(?:\((?:([^()\s]+)(?:\s"((?:[^"]*\\")*[^"]*)"\s*)?)\))/,regExp:/(?:\[(.+?)\])(?:\((?:([^()\s]+)(?:\s"((?:[^"]*\\")*[^"]*)"\s*)?)\))$/,replace:(t,n)=>{const[,o,r,s]=n,c=i.$createLinkNode(r,{title:s}),l=o.split("[").length-1,a=o.split("]").length-1;let f=o,d="";if(l<a)return;if(l>a){const e=o.split("[");d="["+e[0],f=e.slice(1).join("[")}const u=e.$createTextNode(f);return u.setFormat(t.getFormat()),c.append(u),t.replace(c),d&&c.insertBefore(e.$createTextNode(d)),u},trigger:")",type:"text-match"},ne=[H,U,z,G],oe=[j],re=[Q,K,X,J,V,W,Z,ee,Y],ie=[te],se=[...ne,...oe,...re,...ie];function ce(e,t,n){const o=n.length;for(let r=t;r>=o;r--){const t=r-o;if(le(e,t,n,0,o)&&" "!==e[t+o])return t}return-1}function le(e,t,n,o,r){for(let i=0;i<r;i++)if(e[t+i]!==n[o+i])return!1;return!0}exports.$convertFromMarkdownString=function(e,t=se,n,o=!1,r=!1){const i=o?e:function(e,t=!1){const n=e.split("\n");let o=!1;const r=[];for(let e=0;e<n.length;e++){const i=n[e].trimEnd(),s=r[r.length-1];b.test(i)?r.push(i):v.test(i)||_.test(i)?(o=!o,r.push(i)):o||""===i||""===s||!s||I.test(s)||I.test(i)||y.test(i)||C.test(i)||L.test(i)||R.test(i)||A.test(i)||F.test(i)||!t||k.test(i)||M.test(i)||O(M).test(s)||O(k).test(s)||_.test(s)?r.push(i):r[r.length-1]=s+" "+i.trimStart()}return r.join("\n")}(e,r);return N(t,o)(i,n)},exports.$convertToMarkdownString=function(t=se,n,o=!1){return function(t,n=!1){const o=c(t),r=[...o.multilineElement,...o.element],i=!n,s=o.textFormat.filter(e=>1===e.format.length).sort((e,t)=>Number(e.format.includes("code"))-Number(t.format.includes("code")));return t=>{const n=[],c=(t||e.$getRoot()).getChildren();for(let e=0;e<c.length;e++){const t=c[e],l=d(t,r,s,o.textMatch);null!=l&&n.push(i&&e>0&&!f(t)&&!f(c[e-1])?"\n".concat(l):l)}return n.join("\n")}}(t,o)(n)},exports.BOLD_ITALIC_STAR=K,exports.BOLD_ITALIC_UNDERSCORE=X,exports.BOLD_STAR=J,exports.BOLD_UNDERSCORE=V,exports.CHECK_LIST=q,exports.CODE=j,exports.ELEMENT_TRANSFORMERS=ne,exports.HEADING=H,exports.HIGHLIGHT=W,exports.INLINE_CODE=Q,exports.ITALIC_STAR=Z,exports.ITALIC_UNDERSCORE=ee,exports.LINK=te,exports.MULTILINE_ELEMENT_TRANSFORMERS=oe,exports.ORDERED_LIST=G,exports.QUOTE=U,exports.STRIKETHROUGH=Y,exports.TEXT_FORMAT_TRANSFORMERS=re,exports.TEXT_MATCH_TRANSFORMERS=ie,exports.TRANSFORMERS=se,exports.UNORDERED_LIST=z,exports.registerMarkdownShortcuts=function(t,n=se){const o=c(n),i=s(o.textFormat,({tag:e})=>e[e.length-1]),a=s(o.textMatch,({trigger:e})=>e);for(const e of n){const n=e.type;if("element"===n||"text-match"===n||"multiline-element"===n){const n=e.dependencies;for(const e of n)t.hasNode(e)||S(173,e.getType())}}const f=(t,n,r)=>{(function(t,n,o,r){const i=t.getParent();if(!e.$isRootOrShadowRoot(i)||t.getFirstChild()!==n)return!1;const s=n.getTextContent();if(" "!==s[o-1])return!1;for(const{regExp:e,replace:i}of r){const r=s.match(e);if(r&&r[0].length===(r[0].endsWith(" ")?o:o-1)){const e=n.getNextSiblings(),[s,c]=n.splitText(o);if(!1!==i(t,c?[c,...e]:e,r,!1))return s.remove(),!0}}return!1})(t,n,r,o.element)||function(t,n,o,r){const i=t.getParent();if(!e.$isRootOrShadowRoot(i)||t.getFirstChild()!==n)return!1;const s=n.getTextContent();if(" "!==s[o-1])return!1;for(const{regExpStart:e,replace:i,regExpEnd:c}of r){if(c&&!("optional"in c)||c&&"optional"in c&&!c.optional)continue;const r=s.match(e);if(r&&r[0].length===(r[0].endsWith(" ")?o:o-1)){const e=n.getNextSiblings(),[s,c]=n.splitText(o);if(!1!==i(t,c?[c,...e]:e,r,null,null,!1))return s.remove(),!0}}return!1}(t,n,r,o.multilineElement)||function(e,t,n){let o=e.getTextContent();const r=n[o[t-1]];if(null==r)return!1;t<o.length&&(o=o.slice(0,t));for(const t of r){if(!t.replace||!t.regExp)continue;const n=o.match(t.regExp);if(null===n)continue;const r=n.index||0,i=r+n[0].length;let s;return 0===r?[s]=e.splitText(i):[,s]=e.splitText(r,i),s.selectNext(0,0),t.replace(s,n),!0}return!1}(n,r,a)||function(t,n,o){const r=t.getTextContent(),i=n-1,s=r[i],c=o[s];if(!c)return!1;for(const n of c){const{tag:o}=n,c=o.length,a=i-c+1;if(c>1&&!le(r,a,o,0,c))continue;if(" "===r[a-1])continue;const f=r[i+1];if(!1===n.intraword&&f&&!l.test(f))continue;const d=t;let u=d,g=ce(r,a,o),p=u;for(;g<0&&(p=p.getPreviousSibling())&&!e.$isLineBreakNode(p);)if(e.$isTextNode(p)){if(p.hasFormat("code"))continue;const e=p.getTextContent();u=p,g=ce(e,e.length,o)}if(g<0)continue;if(u===d&&g+c===a)continue;const x=u.getTextContent();if(g>0&&x[g-1]===s)continue;const h=x[g-1];if(!1===n.intraword&&h&&!l.test(h))continue;const m=d.getTextContent(),$=m.slice(0,a)+m.slice(i+1);d.setTextContent($);const N=u===d?$:x;u.setTextContent(N.slice(0,g)+N.slice(g+c));const T=e.$getSelection(),E=e.$createRangeSelection();e.$setSelection(E);const S=i-c*(u===d?2:1)+1;E.anchor.set(u.__key,g,"text"),E.focus.set(d.__key,S,"text");for(const e of n.format)E.hasFormat(e)||E.formatText(e);E.anchor.set(E.focus.key,E.focus.offset,E.focus.type);for(const e of n.format)E.hasFormat(e)&&E.toggleFormat(e);return e.$isRangeSelection(T)&&(E.format=T.format),!0}}(n,r,i)};return t.registerUpdateListener(({tags:n,dirtyLeaves:o,editorState:i,prevEditorState:s})=>{if(n.has(e.COLLABORATION_TAG)||n.has(e.HISTORIC_TAG))return;if(t.isComposing())return;const c=i.read(e.$getSelection),l=s.read(e.$getSelection);if(!e.$isRangeSelection(l)||!e.$isRangeSelection(c)||!c.isCollapsed()||c.is(l))return;const a=c.anchor.key,d=c.anchor.offset,u=i._nodeMap.get(a);!e.$isTextNode(u)||!o.has(a)||1!==d&&d>l.anchor.offset+1||t.update(()=>{if(!m(u))return;const e=u.getParent();null===e||r.$isCodeNode(e)||f(e,u,c.anchor.offset)})})};