@launchpad-ui/markdown
Advanced tools
+2
-1
@@ -47,2 +47,3 @@ // ../../scripts/react-shim.js | ||
| // src/Markdown.tsx | ||
| import { jsx } from "react/jsx-runtime"; | ||
| DOMPurify2.addHook("afterSanitizeAttributes", (node) => { | ||
@@ -65,3 +66,3 @@ if (isAnchorNode(node) && node.target.toLowerCase() === "_blank") { | ||
| const classes = cx("Markdown", className); | ||
| return /* @__PURE__ */ React.createElement(Container, { | ||
| return /* @__PURE__ */ jsx(Container, { | ||
| className: classes, | ||
@@ -68,0 +69,0 @@ dangerouslySetInnerHTML: { |
@@ -5,4 +5,4 @@ { | ||
| "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import type { RefObject } from 'react';\n\nimport cx from 'clsx';\nimport DOMPurify from 'isomorphic-dompurify';\n\nimport './styles/Markdown.css';\nimport { isAnchorNode, renderMarkdown } from './utils';\n\nDOMPurify.addHook('afterSanitizeAttributes', (node) => {\n // Ensure we add the required rel attribute.\n if (isAnchorNode(node) && node.target.toLowerCase() === '_blank') {\n node.setAttribute('rel', 'noopener noreferrer');\n } else {\n node.removeAttribute('target');\n }\n});\n\ntype MarkdownProps = {\n source: string;\n className?: string;\n baseUri?: string;\n allowedTags?: string[];\n container?: React.ElementType;\n textRef?: RefObject<HTMLElement>;\n};\n\nconst Markdown = ({\n source,\n className,\n baseUri,\n allowedTags,\n container = 'div',\n textRef,\n}: MarkdownProps) => {\n const Container = container;\n const classes = cx('Markdown', className);\n return (\n <Container\n className={classes}\n // We sanitize \"source\" (via DOMPurify) before inserting it into the DOM, to protect against XSS attacks.\n // Using dangerouslySetInnerHTML is safe.\n dangerouslySetInnerHTML={{\n __html: renderMarkdown(source, { baseUri, allowedTags }),\n }}\n ref={textRef}\n />\n );\n};\n\nexport { Markdown };\nexport type { MarkdownProps };\n", "import DOMPurify from 'isomorphic-dompurify';\nimport { marked } from 'marked';\n\nfunction isAnchorNode(node: Element): node is HTMLAnchorElement {\n return node.tagName.toLowerCase() === 'a';\n}\n\nfunction renderMarkdown(\n source: string,\n {\n baseUri,\n allowedTags,\n }: {\n baseUri?: string;\n allowedTags?: string[];\n } = {}\n) {\n const renderer = new marked.Renderer();\n marked.setOptions({\n gfm: true,\n breaks: true,\n });\n renderer.link = function (href: string, title: string, text: string) {\n const link = marked.Renderer.prototype.link.call(this, href, title, text);\n if (!(href.startsWith('https://') || href.startsWith('http://'))) {\n return link;\n }\n if (baseUri && href.startsWith(baseUri)) {\n return link;\n }\n return link.replace('<a', \"<a target='_blank' rel='noopener noreferrer'\");\n };\n\n const html = marked(source, { renderer });\n\n const sanitizationConfig: DOMPurify.Config = {\n KEEP_CONTENT: false,\n ADD_ATTR: ['target'],\n FORBID_ATTR: ['style', 'class'],\n };\n\n if (allowedTags) {\n sanitizationConfig.ALLOWED_TAGS = allowedTags;\n }\n\n return DOMPurify.sanitize(html, sanitizationConfig);\n}\n\nexport { isAnchorNode, renderMarkdown };\n"], | ||
| "mappings": ";AAAA;;;ACEA;AACA;AAEA;;;ACLA;AACA;AAEA,sBAAsB,MAA0C;AAC9D,SAAO,KAAK,QAAQ,YAAY,MAAM;AACxC;AAEA,wBACE,QACA;AAAA,EACE;AAAA,EACA;AAAA,IAIE,CAAC,GACL;AACA,QAAM,WAAW,IAAI,OAAO,SAAS;AACrC,SAAO,WAAW;AAAA,IAChB,KAAK;AAAA,IACL,QAAQ;AAAA,EACV,CAAC;AACD,WAAS,OAAO,SAAU,MAAc,OAAe,MAAc;AACnE,UAAM,OAAO,OAAO,SAAS,UAAU,KAAK,KAAK,MAAM,MAAM,OAAO,IAAI;AACxE,QAAI,CAAE,MAAK,WAAW,UAAU,KAAK,KAAK,WAAW,SAAS,IAAI;AAChE,aAAO;AAAA,IACT;AACA,QAAI,WAAW,KAAK,WAAW,OAAO,GAAG;AACvC,aAAO;AAAA,IACT;AACA,WAAO,KAAK,QAAQ,MAAM,8CAA8C;AAAA,EAC1E;AAEA,QAAM,OAAO,OAAO,QAAQ,EAAE,SAAS,CAAC;AAExC,QAAM,qBAAuC;AAAA,IAC3C,cAAc;AAAA,IACd,UAAU,CAAC,QAAQ;AAAA,IACnB,aAAa,CAAC,SAAS,OAAO;AAAA,EAChC;AAEA,MAAI,aAAa;AACf,uBAAmB,eAAe;AAAA,EACpC;AAEA,SAAO,UAAU,SAAS,MAAM,kBAAkB;AACpD;;;ADtCA,WAAU,QAAQ,2BAA2B,CAAC,SAAS;AAErD,MAAI,aAAa,IAAI,KAAK,KAAK,OAAO,YAAY,MAAM,UAAU;AAChE,SAAK,aAAa,OAAO,qBAAqB;AAAA,EAChD,OAAO;AACL,SAAK,gBAAgB,QAAQ;AAAA,EAC/B;AACF,CAAC;AAWD,IAAM,WAAW,CAAC;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,MACmB;AACnB,QAAM,YAAY;AAClB,QAAM,UAAU,GAAG,YAAY,SAAS;AACxC,SACE,oCAAC;AAAA,IACC,WAAW;AAAA,IAGX,yBAAyB;AAAA,MACvB,QAAQ,eAAe,QAAQ,EAAE,SAAS,YAAY,CAAC;AAAA,IACzD;AAAA,IACA,KAAK;AAAA,GACP;AAEJ;", | ||
| "names": [] | ||
| "mappings": ";AAAA,YAAY,WAAW;;;ACEvB,OAAO,QAAQ;AACf,OAAOA,gBAAe;AAEtB,OAAO;;;ACLP,OAAO,eAAe;AACtB,SAAS,cAAc;AAEvB,SAAS,aAAa,MAA0C;AAC9D,SAAO,KAAK,QAAQ,YAAY,MAAM;AACxC;AAEA,SAAS,eACP,QACA;AAAA,EACE;AAAA,EACA;AACF,IAGI,CAAC,GACL;AACA,QAAM,WAAW,IAAI,OAAO,SAAS;AACrC,SAAO,WAAW;AAAA,IAChB,KAAK;AAAA,IACL,QAAQ;AAAA,EACV,CAAC;AACD,WAAS,OAAO,SAAU,MAAc,OAAe,MAAc;AACnE,UAAM,OAAO,OAAO,SAAS,UAAU,KAAK,KAAK,MAAM,MAAM,OAAO,IAAI;AACxE,QAAI,EAAE,KAAK,WAAW,UAAU,KAAK,KAAK,WAAW,SAAS,IAAI;AAChE,aAAO;AAAA,IACT;AACA,QAAI,WAAW,KAAK,WAAW,OAAO,GAAG;AACvC,aAAO;AAAA,IACT;AACA,WAAO,KAAK,QAAQ,MAAM,8CAA8C;AAAA,EAC1E;AAEA,QAAM,OAAO,OAAO,QAAQ,EAAE,SAAS,CAAC;AAExC,QAAM,qBAAuC;AAAA,IAC3C,cAAc;AAAA,IACd,UAAU,CAAC,QAAQ;AAAA,IACnB,aAAa,CAAC,SAAS,OAAO;AAAA,EAChC;AAEA,MAAI,aAAa;AACf,uBAAmB,eAAe;AAAA,EACpC;AAEA,SAAO,UAAU,SAAS,MAAM,kBAAkB;AACpD;;;ADTI;AA7BJC,WAAU,QAAQ,2BAA2B,CAAC,SAAS;AAErD,MAAI,aAAa,IAAI,KAAK,KAAK,OAAO,YAAY,MAAM,UAAU;AAChE,SAAK,aAAa,OAAO,qBAAqB;AAAA,EAChD,OAAO;AACL,SAAK,gBAAgB,QAAQ;AAAA,EAC/B;AACF,CAAC;AAWD,IAAM,WAAW,CAAC;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AACF,MAAqB;AACnB,QAAM,YAAY;AAClB,QAAM,UAAU,GAAG,YAAY,SAAS;AACxC,SACE,oBAAC;AAAA,IACC,WAAW;AAAA,IAGX,yBAAyB;AAAA,MACvB,QAAQ,eAAe,QAAQ,EAAE,SAAS,YAAY,CAAC;AAAA,IACzD;AAAA,IACA,KAAK;AAAA,GACP;AAEJ;", | ||
| "names": ["DOMPurify", "DOMPurify"] | ||
| } |
+6
-2
@@ -20,3 +20,6 @@ "use strict"; | ||
| }; | ||
| var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); | ||
| var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||
| isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | ||
| mod | ||
| )); | ||
| var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
@@ -77,2 +80,3 @@ | ||
| // src/Markdown.tsx | ||
| var import_jsx_runtime = require("react/jsx-runtime"); | ||
| import_isomorphic_dompurify2.default.addHook("afterSanitizeAttributes", (node) => { | ||
@@ -95,3 +99,3 @@ if (isAnchorNode(node) && node.target.toLowerCase() === "_blank") { | ||
| const classes = (0, import_clsx.default)("Markdown", className); | ||
| return /* @__PURE__ */ React.createElement(Container, { | ||
| return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Container, { | ||
| className: classes, | ||
@@ -98,0 +102,0 @@ dangerouslySetInnerHTML: { |
@@ -5,4 +5,4 @@ { | ||
| "sourcesContent": ["export type { MarkdownProps } from './Markdown';\nexport { Markdown } from './Markdown';\n", "import * as React from 'react';\nexport { React };\n", "import type { RefObject } from 'react';\n\nimport cx from 'clsx';\nimport DOMPurify from 'isomorphic-dompurify';\n\nimport './styles/Markdown.css';\nimport { isAnchorNode, renderMarkdown } from './utils';\n\nDOMPurify.addHook('afterSanitizeAttributes', (node) => {\n // Ensure we add the required rel attribute.\n if (isAnchorNode(node) && node.target.toLowerCase() === '_blank') {\n node.setAttribute('rel', 'noopener noreferrer');\n } else {\n node.removeAttribute('target');\n }\n});\n\ntype MarkdownProps = {\n source: string;\n className?: string;\n baseUri?: string;\n allowedTags?: string[];\n container?: React.ElementType;\n textRef?: RefObject<HTMLElement>;\n};\n\nconst Markdown = ({\n source,\n className,\n baseUri,\n allowedTags,\n container = 'div',\n textRef,\n}: MarkdownProps) => {\n const Container = container;\n const classes = cx('Markdown', className);\n return (\n <Container\n className={classes}\n // We sanitize \"source\" (via DOMPurify) before inserting it into the DOM, to protect against XSS attacks.\n // Using dangerouslySetInnerHTML is safe.\n dangerouslySetInnerHTML={{\n __html: renderMarkdown(source, { baseUri, allowedTags }),\n }}\n ref={textRef}\n />\n );\n};\n\nexport { Markdown };\nexport type { MarkdownProps };\n", "import DOMPurify from 'isomorphic-dompurify';\nimport { marked } from 'marked';\n\nfunction isAnchorNode(node: Element): node is HTMLAnchorElement {\n return node.tagName.toLowerCase() === 'a';\n}\n\nfunction renderMarkdown(\n source: string,\n {\n baseUri,\n allowedTags,\n }: {\n baseUri?: string;\n allowedTags?: string[];\n } = {}\n) {\n const renderer = new marked.Renderer();\n marked.setOptions({\n gfm: true,\n breaks: true,\n });\n renderer.link = function (href: string, title: string, text: string) {\n const link = marked.Renderer.prototype.link.call(this, href, title, text);\n if (!(href.startsWith('https://') || href.startsWith('http://'))) {\n return link;\n }\n if (baseUri && href.startsWith(baseUri)) {\n return link;\n }\n return link.replace('<a', \"<a target='_blank' rel='noopener noreferrer'\");\n };\n\n const html = marked(source, { renderer });\n\n const sanitizationConfig: DOMPurify.Config = {\n KEEP_CONTENT: false,\n ADD_ATTR: ['target'],\n FORBID_ATTR: ['style', 'class'],\n };\n\n if (allowedTags) {\n sanitizationConfig.ALLOWED_TAGS = allowedTags;\n }\n\n return DOMPurify.sanitize(html, sanitizationConfig);\n}\n\nexport { isAnchorNode, renderMarkdown };\n"], | ||
| "mappings": ";;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,YAAuB;;;ACEvB,kBAAe;AACf,mCAAsB;AAEtB,sBAAO;;;ACLP,kCAAsB;AACtB,oBAAuB;AAEvB,sBAAsB,MAA0C;AAC9D,SAAO,KAAK,QAAQ,YAAY,MAAM;AACxC;AAEA,wBACE,QACA;AAAA,EACE;AAAA,EACA;AAAA,IAIE,CAAC,GACL;AACA,QAAM,WAAW,IAAI,qBAAO,SAAS;AACrC,uBAAO,WAAW;AAAA,IAChB,KAAK;AAAA,IACL,QAAQ;AAAA,EACV,CAAC;AACD,WAAS,OAAO,SAAU,MAAc,OAAe,MAAc;AACnE,UAAM,OAAO,qBAAO,SAAS,UAAU,KAAK,KAAK,MAAM,MAAM,OAAO,IAAI;AACxE,QAAI,CAAE,MAAK,WAAW,UAAU,KAAK,KAAK,WAAW,SAAS,IAAI;AAChE,aAAO;AAAA,IACT;AACA,QAAI,WAAW,KAAK,WAAW,OAAO,GAAG;AACvC,aAAO;AAAA,IACT;AACA,WAAO,KAAK,QAAQ,MAAM,8CAA8C;AAAA,EAC1E;AAEA,QAAM,OAAO,0BAAO,QAAQ,EAAE,SAAS,CAAC;AAExC,QAAM,qBAAuC;AAAA,IAC3C,cAAc;AAAA,IACd,UAAU,CAAC,QAAQ;AAAA,IACnB,aAAa,CAAC,SAAS,OAAO;AAAA,EAChC;AAEA,MAAI,aAAa;AACf,uBAAmB,eAAe;AAAA,EACpC;AAEA,SAAO,oCAAU,SAAS,MAAM,kBAAkB;AACpD;;;ADtCA,qCAAU,QAAQ,2BAA2B,CAAC,SAAS;AAErD,MAAI,aAAa,IAAI,KAAK,KAAK,OAAO,YAAY,MAAM,UAAU;AAChE,SAAK,aAAa,OAAO,qBAAqB;AAAA,EAChD,OAAO;AACL,SAAK,gBAAgB,QAAQ;AAAA,EAC/B;AACF,CAAC;AAWD,IAAM,WAAW,CAAC;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,MACmB;AACnB,QAAM,YAAY;AAClB,QAAM,UAAU,yBAAG,YAAY,SAAS;AACxC,SACE,oCAAC;AAAA,IACC,WAAW;AAAA,IAGX,yBAAyB;AAAA,MACvB,QAAQ,eAAe,QAAQ,EAAE,SAAS,YAAY,CAAC;AAAA,IACzD;AAAA,IACA,KAAK;AAAA,GACP;AAEJ;", | ||
| "names": [] | ||
| "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,YAAuB;;;ACEvB,kBAAe;AACf,IAAAA,+BAAsB;AAEtB,sBAAO;;;ACLP,kCAAsB;AACtB,oBAAuB;AAEvB,SAAS,aAAa,MAA0C;AAC9D,SAAO,KAAK,QAAQ,YAAY,MAAM;AACxC;AAEA,SAAS,eACP,QACA;AAAA,EACE;AAAA,EACA;AACF,IAGI,CAAC,GACL;AACA,QAAM,WAAW,IAAI,qBAAO,SAAS;AACrC,uBAAO,WAAW;AAAA,IAChB,KAAK;AAAA,IACL,QAAQ;AAAA,EACV,CAAC;AACD,WAAS,OAAO,SAAU,MAAc,OAAe,MAAc;AACnE,UAAM,OAAO,qBAAO,SAAS,UAAU,KAAK,KAAK,MAAM,MAAM,OAAO,IAAI;AACxE,QAAI,EAAE,KAAK,WAAW,UAAU,KAAK,KAAK,WAAW,SAAS,IAAI;AAChE,aAAO;AAAA,IACT;AACA,QAAI,WAAW,KAAK,WAAW,OAAO,GAAG;AACvC,aAAO;AAAA,IACT;AACA,WAAO,KAAK,QAAQ,MAAM,8CAA8C;AAAA,EAC1E;AAEA,QAAM,WAAO,sBAAO,QAAQ,EAAE,SAAS,CAAC;AAExC,QAAM,qBAAuC;AAAA,IAC3C,cAAc;AAAA,IACd,UAAU,CAAC,QAAQ;AAAA,IACnB,aAAa,CAAC,SAAS,OAAO;AAAA,EAChC;AAEA,MAAI,aAAa;AACf,uBAAmB,eAAe;AAAA,EACpC;AAEA,SAAO,4BAAAC,QAAU,SAAS,MAAM,kBAAkB;AACpD;;;ADTI;AA7BJ,6BAAAC,QAAU,QAAQ,2BAA2B,CAAC,SAAS;AAErD,MAAI,aAAa,IAAI,KAAK,KAAK,OAAO,YAAY,MAAM,UAAU;AAChE,SAAK,aAAa,OAAO,qBAAqB;AAAA,EAChD,OAAO;AACL,SAAK,gBAAgB,QAAQ;AAAA,EAC/B;AACF,CAAC;AAWD,IAAM,WAAW,CAAC;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AACF,MAAqB;AACnB,QAAM,YAAY;AAClB,QAAM,cAAU,YAAAC,SAAG,YAAY,SAAS;AACxC,SACE,4CAAC;AAAA,IACC,WAAW;AAAA,IAGX,yBAAyB;AAAA,MACvB,QAAQ,eAAe,QAAQ,EAAE,SAAS,YAAY,CAAC;AAAA,IACzD;AAAA,IACA,KAAK;AAAA,GACP;AAEJ;", | ||
| "names": ["import_isomorphic_dompurify", "DOMPurify", "DOMPurify", "cx"] | ||
| } |
+3
-4
| { | ||
| "name": "@launchpad-ui/markdown", | ||
| "version": "0.1.1", | ||
| "version": "0.2.0", | ||
| "status": "beta", | ||
@@ -35,7 +35,6 @@ "publishConfig": { | ||
| "peerDependencies": { | ||
| "react": "^16.8.0 || ^17.0.0 || ^18.0.0", | ||
| "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" | ||
| "react": "^18.0.0", | ||
| "react-dom": "^18.0.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/dompurify": "^2.2.9", | ||
| "@types/marked": "^4.0.3", | ||
@@ -42,0 +41,0 @@ "react": "^18.2.0", |
18470
1.17%3
-25%200
2.56%