UNPKG

@mfyz/markdown-renderer-with-custom-directives

Version:
8 lines (7 loc) 10.3 kB
{ "version": 3, "sources": ["../src/index.js", "../src/index.esm.js"], "sourcesContent": ["/**\n * VanillaJS Markdown Renderer with Custom Directives\n * Zero dependencies, slim implementation\n */\n\nclass MarkdownRenderer {\n static PATTERNS = {\n // Basic markdown\n bold: /\\*\\*(.*?)\\*\\*/g,\n italic: /\\*(.*?)\\*/g,\n strike: /~~(.*?)~~/g,\n link: /\\[(.*?)\\]\\((.*?)\\)/g,\n // Headlines\n h6: /^#{6}\\s+(.+)$/gm,\n h5: /^#{5}\\s+(.+)$/gm,\n h4: /^#{4}\\s+(.+)$/gm,\n h3: /^#{3}\\s+(.+)$/gm,\n h2: /^#{2}\\s+(.+)$/gm,\n h1: /^#\\s+(.+)$/gm,\n // Lists\n ul: /^[\\s]*[-*]\\s+(.+)$/gm,\n // Line breaks\n lineBreak: /\\n/g,\n\n // Custom directives\n color: /:color\\[(.*?)\\]{(.*?)}/g,\n button: /:button\\[(.*?)\\]{(.*?)}/g\n }\n\n static BUTTON_STYLES = {\n shapes: {\n pill: 'border-radius: 9999px',\n rect: 'border-radius: 0',\n rounded: 'border-radius: 5px'\n },\n colors: {\n purple: 'background-color: #8a63d2',\n blue: 'background-color: #3b82f6',\n green: 'background-color: #22c55e',\n red: 'background-color: #ef4444',\n yellow: 'background-color: #eab308',\n gray: 'background-color: #6b7280'\n },\n base: [\n 'display: inline-block',\n 'text-decoration: none',\n 'padding: 2px 10px',\n 'cursor: pointer',\n 'color: white'\n ].join('; ')\n }\n\n /**\n * Parse button parameters from the parameter string\n * @param {string} params - Parameter string (e.g., \"url=https://example.com shape=pill color=blue\")\n * @returns {Object} Parsed parameters\n */\n static parseButtonParams(params) {\n const result = {\n url: '',\n shape: 'rounded',\n color: 'blue'\n }\n\n params.split(' ').forEach(param => {\n const [key, value] = param.split('=')\n if (key && value) {\n result[key] = value\n }\n })\n\n return result\n }\n\n /**\n * Process basic markdown syntax\n * @param {string} text\n * @returns {string}\n */\n static processBasicMarkdown(text) {\n return text\n .replace(this.PATTERNS.bold, '<strong>$1</strong>')\n .replace(this.PATTERNS.italic, '<em>$1</em>')\n .replace(this.PATTERNS.strike, '<del>$1</del>')\n .replace(this.PATTERNS.link, '<a href=\"$2\">$1</a>')\n }\n\n /**\n * Process color directive\n * @param {string} text\n * @returns {string}\n */\n static processColorDirective(text) {\n return text.replace(this.PATTERNS.color, (match, content, color) => {\n // Process markdown inside the color directive\n const processedContent = this.processBasicMarkdown(content)\n return `<span style=\"color:${color}\">${processedContent}</span>`\n })\n }\n\n /**\n * Process button directive\n * @param {string} text\n * @returns {string}\n */\n static processButtonDirective(text) {\n return text.replace(this.PATTERNS.button, (match, content, params) => {\n const { url, shape, color } = this.parseButtonParams(params)\n\n const buttonStyle = [\n this.BUTTON_STYLES.base,\n this.BUTTON_STYLES.shapes[shape] || this.BUTTON_STYLES.shapes.rounded,\n this.BUTTON_STYLES.colors[color] || this.BUTTON_STYLES.colors.blue\n ].join('; ')\n\n // Process markdown inside the button directive\n const processedContent = this.processBasicMarkdown(content)\n\n return `<a href=\"${url}\" style=\"${buttonStyle}\">${processedContent}</a>`\n })\n }\n\n /**\n * Process headlines\n * @param {string} text\n * @returns {string}\n */\n static processHeadlines(text) {\n // Process headlines and mark them for line break handling\n return text\n .replace(this.PATTERNS.h6, '<h6>$1</h6>__HEADLINE_MARKER__')\n .replace(this.PATTERNS.h5, '<h5>$1</h5>__HEADLINE_MARKER__')\n .replace(this.PATTERNS.h4, '<h4>$1</h4>__HEADLINE_MARKER__')\n .replace(this.PATTERNS.h3, '<h3>$1</h3>__HEADLINE_MARKER__')\n .replace(this.PATTERNS.h2, '<h2>$1</h2>__HEADLINE_MARKER__')\n .replace(this.PATTERNS.h1, '<h1>$1</h1>__HEADLINE_MARKER__')\n }\n\n /**\n * Process unordered lists\n * @param {string} text\n * @returns {string}\n */\n static processLists(text) {\n // First, identify list items and wrap them in <li> tags\n text = text.replace(this.PATTERNS.ul, '<li>$1</li>')\n\n // Then, group consecutive <li> elements into <ul> blocks\n const lines = text.split('\\n')\n let inList = false\n let result = []\n\n for (let line of lines) {\n if (line.startsWith('<li>')) {\n if (!inList) {\n result.push('<ul>')\n inList = true\n }\n result.push(line)\n } else {\n if (inList) {\n result.push('</ul>')\n inList = false\n }\n result.push(line)\n }\n }\n\n if (inList) {\n result.push('</ul>')\n }\n\n // Join with newlines and clean up extra line breaks around lists\n return result\n .join('\\n')\n .replace(/\\n<\\/ul>/g, '</ul>')\n .replace(/<ul>\\n/g, '<ul>')\n .replace(/(<li>.*?<\\/li>)\\n(?=<li>)/g, '$1')\n }\n\n /**\n * Process line breaks\n * @param {string} text\n * @returns {string}\n */\n static processLineBreaks(text) {\n return (\n text\n // Handle line breaks after headlines\n .replace(\n /(__HEADLINE_MARKER__)\\n+/g,\n (match, marker, offset, string) => {\n // Count the number of newlines\n const newlines = match.match(/\\n/g)?.length || 0\n // If more than 2 newlines, keep the extras as <br>\n return newlines > 2 ? '<br>'.repeat(newlines - 2) : ''\n }\n )\n // Remove any remaining markers\n .replace(/__HEADLINE_MARKER__/g, '')\n // Handle regular line breaks\n .replace(/\\n\\n+/g, '<br><br>')\n .replace(/\\n/g, '<br>')\n )\n }\n\n /**\n * Render markdown with custom directives\n * @param {string} markdown\n * @returns {string}\n */\n static render(markdown) {\n if (!markdown) return ''\n\n let html = markdown\n\n // Process headlines before other markdown\n html = this.processHeadlines(html)\n\n // Process lists\n html = this.processLists(html)\n\n // First process basic markdown that might appear outside directives\n html = this.processBasicMarkdown(html)\n\n // Then process custom directives (which handle their own internal markdown)\n html = this.processColorDirective(html)\n html = this.processButtonDirective(html)\n\n // Process line breaks last\n html = this.processLineBreaks(html)\n\n return html\n }\n}\n\n// Convenience function to render markdown\nfunction render(markdown) {\n return MarkdownRenderer.render(markdown)\n}\n\nmodule.exports = {\n render,\n MarkdownRenderer,\n default: MarkdownRenderer\n}\n", "import MarkdownRenderer from './index.js'\nconst { render } = MarkdownRenderer\nexport { render, MarkdownRenderer }\nexport default MarkdownRenderer\n"], "mappings": "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", "names": ["require_index", "__commonJSMin", "exports", "module", "MarkdownRenderer", "params", "result", "param", "key", "value", "text", "match", "content", "color", "processedContent", "url", "shape", "buttonStyle", "lines", "inList", "line", "marker", "offset", "string", "_a", "newlines", "markdown", "html", "__publicField", "render", "import_index", "render", "MarkdownRenderer", "index_esm_default", "MarkdownRenderer"] }