marked-react
Version:
Render Markdown as React components
330 lines (323 loc) • 10.2 kB
JavaScript
Object.defineProperty(exports, '__esModule', { value: true });
//#region rolldown:runtime
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
key = keys[i];
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
get: ((k) => from[k]).bind(null, key),
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
}
return to;
};
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));
//#endregion
const react = __toESM(require("react"));
const marked = __toESM(require("marked"));
//#region src/helpers.ts
const htmlUnescapes = {
"&": "&",
"<": "<",
">": ">",
""": "\"",
"'": "'"
};
/** Used to match HTML entities and HTML characters. */
const reEscapedHtml = /&(?:amp|lt|gt|quot|#(?:0+)?39);/g;
const reHasEscapedHtml = RegExp(reEscapedHtml.source);
const unescape = (str = "") => {
return reHasEscapedHtml.test(str) ? str.replace(reEscapedHtml, (entity) => htmlUnescapes[entity] || "'") : str;
};
const joinBase = (path, base) => {
if (!base) return path;
try {
return new URL(path, base).href;
} catch {
return path;
}
};
//#endregion
//#region src/ReactParser.ts
var ReactParser = class {
renderer;
constructor(options) {
this.renderer = options.renderer;
}
parse(tokens) {
this.renderer.elIdList.push(0);
const result = tokens.map((token) => {
switch (token.type) {
case "space": return null;
case "heading": {
const level = token.depth;
return this.renderer.heading(this.parseInline(token.tokens), level);
}
case "paragraph": return this.renderer.paragraph(this.parseInline(token.tokens));
case "text": {
const textToken = token;
return textToken.tokens ? this.parseInline(textToken.tokens) : token.text;
}
case "blockquote": {
const blockquoteToken = token;
const quote = this.parse(blockquoteToken.tokens);
return this.renderer.blockquote(quote);
}
case "list": {
const listToken = token;
this.renderer.elIdList.push(0);
const children = listToken.items.map((item) => {
const listItemChildren = [];
if (item.task) listItemChildren.push(this.renderer.checkbox(item.checked ?? false));
listItemChildren.push(this.parse(item.tokens));
return this.renderer.listItem(listItemChildren);
});
this.renderer.elIdList.pop();
return this.renderer.list(children, token.ordered, token.ordered ? token.start : void 0);
}
case "code": return this.renderer.code(token.text, token.lang);
case "html": return this.renderer.html(token.text);
case "table": {
const tableToken = token;
this.renderer.elIdList.push(0);
const headerCells = tableToken.header.map((cell, index) => {
return this.renderer.tableCell(this.parseInline(cell.tokens), {
header: true,
align: token.align[index]
});
});
this.renderer.elIdList.pop();
const headerRow = this.renderer.tableRow(headerCells);
const header = this.renderer.tableHeader(headerRow);
this.renderer.elIdList.push(0);
const bodyChilren = tableToken.rows.map((row) => {
this.renderer.elIdList.push(0);
const rowChildren = row.map((cell, index) => {
return this.renderer.tableCell(this.parseInline(cell.tokens), {
header: false,
align: token.align[index]
});
});
this.renderer.elIdList.pop();
return this.renderer.tableRow(rowChildren);
});
this.renderer.elIdList.pop();
const body = this.renderer.tableBody(bodyChilren);
return this.renderer.table([header, body]);
}
case "hr": return this.renderer.hr();
default: {
console.warn(`Token with "${token.type}" type was not found`);
return null;
}
}
});
this.renderer.elIdList.pop();
return result;
}
parseInline(tokens = []) {
this.renderer.elIdList.push(0);
const result = tokens.map((token) => {
switch (token.type) {
case "text": return this.renderer.text(unescape(token.text));
case "strong": return this.renderer.strong(this.parseInline(token.tokens));
case "em": return this.renderer.em(this.parseInline(token.tokens));
case "del": return this.renderer.del(this.parseInline(token.tokens));
case "codespan": return this.renderer.codespan(unescape(token.text));
case "link": return this.renderer.link(token.href, this.parseInline(token.tokens));
case "image": return this.renderer.image(token.href, token.text, token.title);
case "html": return this.renderer.html(token.text);
case "br": return this.renderer.br();
case "escape": return this.renderer.text(token.text);
default: {
console.warn(`Token with "${token.type}" type was not found`);
return null;
}
}
});
this.renderer.elIdList.pop();
return result;
}
};
var ReactParser_default = ReactParser;
//#endregion
//#region src/ReactRenderer.ts
var ReactRenderer = class {
elIdList = [];
#options;
constructor(options = {}) {
const { renderer } = options;
this.#options = options;
if (renderer && typeof renderer === "object") Object.entries(renderer).forEach(([key, value]) => {
const rendererName = key;
const rendererFunction = value;
if (!this[rendererName] || rendererName === "elementId" || rendererName === "elIdList" || typeof rendererFunction !== "function") return;
Object.defineProperty(this, rendererName, {
value(...args) {
this.#incrementElId();
return rendererFunction.apply(this, args);
},
writable: true,
enumerable: true,
configurable: true
});
});
}
#h(el, children = null, props = {}) {
const elProps = {
key: `marked-react-${this.elementId}`,
suppressHydrationWarning: true
};
this.#incrementElId();
return (0, react.createElement)(el, {
...props,
...elProps
}, children);
}
#incrementElId() {
this.elIdList[this.elIdList.length - 1] += 1;
}
get elementId() {
return this.elIdList.join("-");
}
heading(children, level) {
return this.#h(`h${level}`, children);
}
paragraph(children) {
return this.#h("p", children);
}
link(href, text) {
const url = joinBase(href, this.#options.baseURL);
const target = this.#options.openLinksInNewTab ? "_blank" : null;
return this.#h("a", text, {
href: url,
target
});
}
image(src, alt, title = null) {
const url = joinBase(src, this.#options.baseURL);
return this.#h("img", null, {
src: url,
alt,
title
});
}
codespan(code, lang = null) {
const className = lang ? `${this.#options.langPrefix}${lang}` : null;
return this.#h("code", code, { className });
}
code(code, lang) {
return this.#h("pre", this.codespan(code, lang));
}
blockquote(children) {
return this.#h("blockquote", children);
}
list(children, ordered, start) {
return this.#h(ordered ? "ol" : "ul", children, ordered && start !== 1 ? { start } : {});
}
listItem(children) {
return this.#h("li", children);
}
checkbox(checked) {
return this.#h("input", null, {
type: "checkbox",
disabled: true,
checked
});
}
table(children) {
return this.#h("table", children);
}
tableHeader(children) {
return this.#h("thead", children);
}
tableBody(children) {
return this.#h("tbody", children);
}
tableRow(children) {
return this.#h("tr", children);
}
tableCell(children, flags) {
const tag = flags.header ? "th" : "td";
return this.#h(tag, children, { align: flags.align });
}
strong(children) {
return this.#h("strong", children);
}
em(children) {
return this.#h("em", children);
}
del(children) {
return this.#h("del", children);
}
text(text) {
return text;
}
html(html) {
return html;
}
hr() {
return this.#h("hr");
}
br() {
return this.#h("br");
}
};
var ReactRenderer_default = ReactRenderer;
//#endregion
//#region src/Markdown.ts
const validateComponentProps = (props) => {
if (props.value && typeof props.value !== "string") throw new TypeError(`[marked-react]: Expected value to be of type string but got ${typeof props.value}`);
if (props.children && typeof props.children !== "string") throw new TypeError(`[marked-react]: Expected children to be of type string but got ${typeof props.children}`);
};
const defaultProps = {
isInline: false,
breaks: false,
gfm: true,
baseURL: void 0,
openLinksInNewTab: true,
langPrefix: "language-",
renderer: void 0
};
const markedInstance = new marked.Marked();
const Markdown = (props) => {
validateComponentProps(props);
const options = {
...defaultProps,
...props
};
const marked$1 = options.instance ?? markedInstance;
const lexerOptions = {
breaks: options.breaks,
gfm: options.gfm,
tokenizer: marked$1.defaults.tokenizer
};
const markdownString = options.value ?? options.children ?? "";
const tokens = options.isInline ? marked$1.Lexer.lexInline(markdownString, lexerOptions) : marked$1.lexer(markdownString, lexerOptions);
const parserOptions = { renderer: new ReactRenderer_default({
renderer: options.renderer,
baseURL: options.baseURL,
openLinksInNewTab: options.openLinksInNewTab,
langPrefix: options.langPrefix
}) };
const parser = new ReactParser_default(parserOptions);
const children = options.isInline ? parser.parseInline(tokens) : parser.parse(tokens);
return (0, react.createElement)(react.Fragment, null, children);
};
var Markdown_default = Markdown;
//#endregion
//#region src/index.ts
var src_default = Markdown_default;
//#endregion
exports.ReactParser = ReactParser_default;
exports.ReactRenderer = ReactRenderer_default;
exports.default = src_default;
//# sourceMappingURL=index.cjs.map