uiwjs / react-md-editor

A simple markdown editor with preview, implemented with React.js and TypeScript.
https://uiwjs.github.io/react-md-editor
MIT License
2.17k stars 156 forks source link

Breaks the application when try to edit the code snippet extension beside #406

Open wajeshubham opened 2 years ago

wajeshubham commented 2 years ago

While using code snippet syntax in editor and trying to edit extension text beside "```" I'm getting following error:

Cannot read properties of null (reading 'children')

./node_modules/rehype-prism-plus/dist/rehype-prism-plus.es.js:1

> 1 | import{visit as e}from"unist-util-visit";import{toString as t}from"hast-util-to-string";import{filter as r}from"unist-util-filter";import n from"parse-numeric-range";import{refractor as i}from"refractor/lib/common.js";import{refractor as o}from"refractor/lib/all.js";function a(){a=function(e,t){return new r(e,void 0,t)};var e=RegExp.prototype,t=new WeakMap;function r(e,n,i){var o=new RegExp(e,n);return t.set(o,i||t.get(e)),l(o,r.prototype)}function n(e,r){var n=t.get(r);return Object.keys(n).reduce(function(t,r){return t[r]=e[n[r]],t},Object.create(null))}return s(r,RegExp),r.prototype.exec=function(t){var r=e.exec.call(this,t);return r&&(r.groups=n(r,this)),r},r.prototype[Symbol.replace]=function(r,i){if("string"==typeof i){var o=t.get(this);return e[Symbol.replace].call(this,r,i.replace(/\$<([^>]+)>/g,function(e,t){return"$"+o[t]}))}if("function"==typeof i){var a=this;return e[Symbol.replace].call(this,r,function(){var e=arguments;return"object"!=typeof e[e.length-1]&&(e=[].slice.call(e)).push(n(e,a)),i.apply(this,e)})}return e[Symbol.replace].call(this,r,i)},a.apply(this,arguments)}function s(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),Object.defineProperty(e,"prototype",{writable:!1}),t&&l(e,t)}function l(e,t){return l=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},l(e,t)}function u(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r<t;r++)n[r]=e[r];return n}function c(e,t){var r="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(r)return(r=r.call(e)).next.bind(r);if(Array.isArray(e)||(r=function(e,t){if(e){if("string"==typeof e)return u(e,t);var r=Object.prototype.toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?u(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0;return function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var p=function e(t){return t.reduce(function(t,r){if("text"===r.type){if(-1===r.value.indexOf("\n"))return t.push(r),t;for(var n,i=r.value.split("\n"),o=c(i.entries());!(n=o()).done;){var a=n.value,s=a[0],l=a[1];t.push({type:"text",value:s===i.length-1?l:l+"\n",position:{start:{line:r.position.start.line+s},end:{line:r.position.start.line+s}}})}return t}return Object.prototype.hasOwnProperty.call(r,"children")?(r.children=e(r.children),t.push(r),t):(t.push(r),t)},[])},f=function(i){return function(o){return void 0===o&&(o={}),function(t){e(t,"element",s)};function s(e,s,l){if(l&&"pre"===l.tagName&&"code"===e.tagName){var u=e.data&&e.data.meta?e.data.meta:"";e.properties.className?"boolean"==typeof e.properties.className?e.properties.className=[]:Array.isArray(e.properties.className)||(e.properties.className=[e.properties.className]):e.properties.className=[],e.properties.className.push("code-highlight");var f,h=function(e){for(var t,r=c(e.properties.className);!(t=r()).done;){var n=t.value;if("language-"===n.slice(0,9))return n.slice(9).toLowerCase()}return null}(e);if(h)try{f=i.highlight(t(e),h),l.properties.className=(l.properties.className||[]).concat("language-"+h)}catch(t){if(!o.ignoreMissing||!/Unknown language/.test(t.message))throw t;f=e}else f=e;var m,d=(m=1,function e(t){return t.reduce(function(t,r){if("text"===r.type){var n=(r.value.match(/\n/g)||"").length;return r.position={start:{line:m,column:0},end:{line:m+n,column:0}},m+=n,t.push(r),t}if(Object.prototype.hasOwnProperty.call(r,"children")){var i=m;return r.children=e(r.children),t.push(r),r.position={start:{line:i,column:0},end:{line:m,column:0}},t}return t.push(r),t},[])})(f.children);f.children=p(d),f.children.length>0&&(f.position={start:{line:f.children[0].position.start.line,column:0},end:{line:f.children[f.children.length-1].position.end.line,column:0}});for(var g,y,v=function(e){var t=/{([\d,-]+)}/,r=e.split(",").map(function(e){return e.trim()}).join();if(t.test(r)){var i=t.exec(r)[1],o=n(i);return function(e){return o.includes(e+1)}}return function(){return!1}}(u),b=function(e){var t=/*#__PURE__*/a(/showLineNumbers=([0-9]+)/i,{lines:1});if(t.test(e)){var r=t.exec(e);return Number(r.groups.lines)}return 1}(u),N=(""===(g=t(e).split(/\n/))[g.length-1].trim()&&g.pop(),g.map(function(e){return{type:"element",tagName:"span",properties:{className:["code-line"]},children:[{type:"text",value:e}]}})),w=function(){var e=y.value,n=e[0],i=e[1];(u.toLowerCase().includes("showLineNumbers".toLowerCase())||o.showLineNumbers)&&(i.properties.line=[(n+b).toString()],i.properties.className.push("line-number")),v(n)&&i.properties.className.push("highlight-line"),"diff"===h&&"-"===t(i).substring(0,1)?i.properties.className.push("deleted"):"diff"===h&&"+"===t(i).substring(0,1)&&i.properties.className.push("inserted");var a=r(f,function(e){return e.position.start.line<=n+1&&e.position.end.line>=n+1});i.children=a.children},x=c(N.entries());!(y=x()).done;)w();e.children=N}}}},h=f(i),m=f(o);export{m as default,h as rehypePrismCommon,f as rehypePrismGenerator};
    |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                ^
  2 | //# sourceMappingURL=rehype-prism-plus.es.js.map
  3 |

This happens when user try to edit "```jsx" part inside the editor:

// (Sometimes when I try to edit/remove `"```jsx"` in the following line  it breaks the application and gives the above error)
```jsx
import React from "react";
import MDEditor from '@uiw/react-md-editor';

export default function App() {
  const [value, setValue] = React.useState("**Hello world!!!**");
  return (
    <div className="container">
      <MDEditor
        value={value}
        onChange={setValue}
      />
      <MDEditor.Markdown source={value} style={{ whiteSpace: 'pre-wrap' }} />
    </div>
  );
}

Also the code snippets are not getting highlighted in my markdown preview.

NOTE: Your own website is also crashing due to the same issue.Kindly open https://uiwjs.github.io/react-md-editor/ and try to edit the following part:

https://user-images.githubusercontent.com/62889697/173334753-23d79d78-cdca-4d23-9c8b-eb2abe9220fe.mp4

jaywcjlove commented 2 years ago

@wajeshubham Fixed this, but not perfect.

wajeshubham commented 2 years ago

Ok thanks @jaywcjlove

wajeshubham commented 2 years ago

@jaywcjlove I'm still facing a highlighting issue for code

Screenshot 2022-06-13 at 8 58 01 PM

Please let me know if I'm missing something.

export const MdComp: React.FC<Props> = ({ source, className }) => {
  return (
    <MDEditor.Markdown
      className={className}
      source={source}
      rehypePlugins={[[rehypeSanitize]]}
      linkTarget="_blank"
    />
  );
};
jaywcjlove commented 2 years ago

@wajeshubham https://codesandbox.io/embed/markdown-editor-for-react-uiwjs-react-md-editor-issues-406-44qb3y?fontsize=14&hidenavigation=1&theme=dark

import React from "react";
import ReactDOM from "react-dom";
import MDEditor from "@uiw/react-md-editor";
// No import is required in the WebPack.
// import "@uiw/react-md-editor/dist/markdown-editor.css";

const mkdStr = `# Markdown Editor for [React](https://facebook.github.io/react/)

**Hello world!!!**

[![](https://avatars.githubusercontent.com/u/1680273?s=80&v=4)](https://avatars.githubusercontent.com/u/1680273?v=4)

\`\`\`javascript
import React from "react";
import ReactDOM from "react-dom";
import MEDitor from '@uiw/react-md-editor';

\`\`\`

\`\`\`jsx
export const MdComp: React.FC<Props> = ({ source, className }) => {
  return (
    <MDEditor.Markdown
      className={className}
      source={source}
      rehypePlugins={[[rehypeSanitize]]}
      linkTarget="_blank"
    />
  );
};
\`\`\`
`;

function App() {
  return (
    <div className="container">
      <MDEditor.Markdown
        style={{ padding: 15 }}
        source={mkdStr}
        linkTarget="_blank"
        // previewOptions={{
        //   linkTarget: "_blank"
        // }}
      />
    </div>
  );
}
wajeshubham commented 2 years ago

Removing rehypePlugins prop resolves the issue. But isn't it insecure? do you have any other secure way for the same?

jaywcjlove commented 2 years ago

https://codesandbox.io/embed/markdown-editor-for-react-uiwjs-react-md-editor-issues-406-44qb3y?fontsize=14&hidenavigation=1&theme=dark @wajeshubham

import React from "react";
import ReactDOM from "react-dom";
import MDEditor from "@uiw/react-md-editor";
import rehypeSanitize, { defaultSchema } from "rehype-sanitize";
// No import is required in the WebPack.
// import "@uiw/react-md-editor/dist/markdown-editor.css";

const mkdStr = `# Markdown Editor for [React](https://facebook.github.io/react/)

**Hello world!!!**

[![](https://avatars.githubusercontent.com/u/1680273?s=80&v=4)](https://avatars.githubusercontent.com/u/1680273?v=4)

\`\`\`javascript
import React from "react";
import ReactDOM from "react-dom";
import MEDitor from '@uiw/react-md-editor';

\`\`\`

\`\`\`jsx
export const MdComp: React.FC<Props> = ({ source, className }) => {
  return (
    <MDEditor.Markdown
      className={className}
      source={source}
      rehypePlugins={[[rehypeSanitize]]}
      linkTarget="_blank"
    />
  );
};
\`\`\`
`;

function App() {
  return (
    <div className="container">
      <MDEditor.Markdown
        style={{ padding: 15 }}
        source={mkdStr}
        linkTarget="_blank"
        // rehypePlugins={[[rehypeSanitize]]}
        pluginsFilter={(type, plugin) => {
          if (type === "rehype") {
            // plugin.push(rehypeSanitize);
            plugin.unshift([
              rehypeSanitize,
              {
                ...defaultSchema,
                attributes: {
                  ...defaultSchema.attributes,
                  code: [
                    ...(defaultSchema.attributes.code || []),
                    // List of all allowed languages:
                    [
                      "className",
                      "language-jsx",
                      "language-javascript",
                      "language-jsx",
                      "language-js",
                      "language-css",
                      "language-md"
                    ]
                  ]
                }
              }
            ]);
            console.log(plugin);
          }
          return plugin;
        }}
        // previewOptions={{
        //   linkTarget: "_blank"
        // }}
      />
    </div>
  );
}
wajeshubham commented 2 years ago

Thanks @jaywcjlove. Last question, how much secure is the rehypen plugin? Do we need to use something like dompurify to stay away from the XSS attacks? or rehypen plugin is alone secure to do so?

jaywcjlove commented 2 years ago

I'm not sure how safe it is, you can ask here: https://www.npmjs.com/package/rehype-sanitize

@wajeshubham