@sullux/markdown-html 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,113 +1,45 @@
1
+ <p align="center">
2
+ <img src="docs/logo.svg" alt="Markdown HTML" width="160" height="160" />
3
+ </p>
4
+
1
5
  # @sullux/markdown-html
2
6
 
3
- A high-performance, bidirectional Markdown $\leftrightarrow$ HTML compiler supporting GitHub-Flavored Markdown (GFM), GitBook hints, pluggable code block renderers, built-in syntax highlighting, and custom image dimensions.
7
+ A high-performance, zero-dependency, bidirectional Markdown HTML compiler supporting GitHub-Flavored Markdown (GFM), GitBook hints, pluggable code block renderers, built-in syntax highlighting, and custom image dimensions.
4
8
 
5
- Part of the Sullux markdown suite, `@sullux/markdown-html` relies on `@sullux/markdown-compiler` for AST parsing and stringifying, ensuring zero dependencies and fully auditable code.
9
+ 📖 **Official Documentation:** [https://sullux.com/projects/markdown/markdown-html/](https://sullux.com/projects/markdown/markdown-html/)
6
10
 
7
- ## Core Features
11
+ ## Overview
8
12
 
9
- * **Bidirectional Transformations:**
10
- * `markdownToHtml(markdown, options)`: Renders AST-driven semantic HTML.
11
- * `htmlToMarkdown(html)`: Converts HTML documents/emails back to clean GFM Markdown.
12
- * **Pluggable Code Block Renderers:** Register custom handlers for languages like `mermaid`, `dot`, or bespoke blocks via `options.codeRenderers`.
13
- * **Built-in Zero-Dependency Syntax Highlighting:**
14
- * Out-of-the-box tokenizers for `js`, `json`, `yaml`, `bash`, `html`, and `sql`.
15
- * Customizable and overridable via `options.tokenizers`.
16
- * **Image Dimension Styling:** Multi-syntax dimension support (Obsidian `|400x200`, Pandoc/GitLab `{width=50%}`, GitHub `{:width="400px"}`, VS Code `=300x200`) rendered as inline CSS (`style="width: 400px; height: 200px;"`).
17
- * **Rich Component Support:**
18
- * Auto-slugified heading IDs with deduplication (`<h2 id="section-one-1">`).
19
- * Callouts (`> [!NOTE]`, `{% hint %}`).
20
- * Task list checkboxes (`- [ ]`, `- [x]`).
21
- * GFM tables with column alignment attributes (`<th align="center">`).
22
- * YAML frontmatter stripping.
13
+ Part of the Sullux markdown suite, `@sullux/markdown-html` relies on `@sullux/markdown-compiler` to deliver fast, bidirectional conversion between Markdown and clean, semantic HTML. It transforms Markdown ASTs into HTML with auto-slugified heading IDs, callout boxes, task lists, GFM tables, and multi-syntax image dimensions styled as inline CSS. It also parses arbitrary HTML documents, emails, or CMS content back into canonical GFM Markdown with style normalization and layout table flattening.
23
14
 
24
- ## Folder Topography
15
+ Key capabilities include pluggable code block renderers for diagrams (such as Mermaid or Graphviz) and zero-dependency compile-time syntax highlighting for common languages (`js`, `json`, `yaml`, `bash`, `html`, `sql`).
25
16
 
26
- ```
27
- packages/markdown-html/
28
- ├── lib/
29
- │ ├── markdown-to-html/ # Markdown -> AST -> HTML renderer (<100 lines each)
30
- │ │ ├── index.js
31
- │ │ ├── render-block.js
32
- │ │ ├── render-inline.js
33
- │ │ ├── slugify.js
34
- │ │ ├── escape.js
35
- │ │ └── highlight/ # Zero-dependency language tokenizers
36
- │ │ ├── index.js
37
- │ │ ├── js.js
38
- │ │ ├── json.js
39
- │ │ ├── yaml.js
40
- │ │ ├── bash.js
41
- │ │ ├── html.js
42
- │ │ └── sql.js
43
- │ └── html-to-markdown/ # HTML -> AST -> Markdown compiler (<100 lines each)
44
- │ ├── index.js
45
- │ ├── parse-html.js
46
- │ ├── ast-builder.js
47
- │ ├── block-builder.js
48
- │ ├── block-table.js
49
- │ ├── inline-builder.js
50
- │ └── utils.js
51
- ├── index.js # Package entrypoint
52
- └── package.json # Package manifest
53
- ```
17
+ ## Quick Start
54
18
 
55
- ## Programmatic Usage
19
+ ### Installation
56
20
 
57
- ### 1. Converting Markdown to HTML with Options
58
-
59
- ```javascript
60
- const { markdownToHtml } = require('@sullux/markdown-html')
61
-
62
- const md = `
63
- # System Spec
64
-
65
- ![Diagram|400x200](schema.png)
21
+ ```bash
22
+ yarn add @sullux/markdown-html
23
+ ```
66
24
 
67
- \`\`\`js
68
- const name = "Sullux";
69
- \`\`\`
25
+ ### Usage
70
26
 
71
- \`\`\`mermaid
72
- graph TD;
73
- A-->B;
74
- \`\`\`
75
- `
27
+ ```javascript
28
+ const { markdownToHtml, htmlToMarkdown } = require('@sullux/markdown-html')
76
29
 
77
- const html = markdownToHtml(md, {
30
+ // Convert Markdown to semantic HTML with custom code renderers
31
+ const html = markdownToHtml('# Hello\n\n![Diagram|400x200](arch.png)', {
78
32
  codeRenderers: {
79
33
  mermaid: (node) => `<div class="mermaid">${node.value}</div>\n`,
80
34
  },
81
35
  })
82
36
 
83
- console.log(html)
84
- /* Output includes:
85
- <h1 id="system-spec">System Spec</h1>
86
- <p><img src="schema.png" alt="Diagram" style="width: 400px; height: 200px;" /></p>
87
- <pre><code class="language-js"><span class="hl-kw">const</span> <span class="hl-id">name</span> <span class="hl-punc">=</span> <span class="hl-str">&quot;Sullux&quot;</span><span class="hl-punc">;</span></code></pre>
88
- <div class="mermaid">graph TD;
89
- A-->B;</div>
90
- */
37
+ // Convert HTML back to clean GFM Markdown
38
+ const markdown = htmlToMarkdown('<h1>Hello</h1><p>This is <strong>bold</strong> text.</p>')
91
39
  ```
92
40
 
93
- ### 2. Converting HTML back to Markdown
94
-
95
- ```javascript
96
- const { htmlToMarkdown } = require('@sullux/markdown-html')
41
+ For complete guides, syntax highlighting details, and full API specifications, see the [official documentation](https://sullux.com/projects/markdown/markdown-html/).
97
42
 
98
- const htmlInput = '<h1>Title</h1><p>This is <strong>bold</strong> text with <a href="https://sullux.com">a link</a>.</p>'
99
- const markdown = htmlToMarkdown(htmlInput)
43
+ ## Contributing & License
100
44
 
101
- console.log(markdown)
102
- /* Output:
103
- # Title
104
-
105
- This is **bold** text with [a link](https://sullux.com).
106
- */
107
- ```
108
-
109
- ## Running Unit Tests
110
-
111
- ```bash
112
- yarn test
113
- ```
45
+ Please see the [Monorepo README](../../README.md) for contribution guidelines, testing instructions, and license details.
@@ -21,6 +21,13 @@ const processBlockNode = (node, style, blocks, currentInline, traverse, toAst) =
21
21
  return true
22
22
  }
23
23
 
24
+ if ((attrs.class || '').includes('math-display') || (tagName !== 'span' && attrs['data-latex'])) {
25
+ flushInline()
26
+ const latex = attrs['data-latex'] || (node.children ? node.children.map((c) => c.value || '').join('').replace(/^\$\$|\$\$$/g, '').trim() : '')
27
+ blocks.push({ type: 'mathBlock', value: latex })
28
+ return true
29
+ }
30
+
24
31
  const headerMatch = tagName.match(/^h([1-6])$/)
25
32
  if (headerMatch) {
26
33
  flushInline()
@@ -47,6 +47,11 @@ const processInlineNode = (node, style, currentInline, traverse) => {
47
47
  if (attrs.src) currentInline.push({ type: 'image', url: attrs.src, alt: attrs.alt || '' })
48
48
  return true
49
49
  }
50
+ if ((attrs.class || '').includes('math-inline') || (tagName === 'span' && attrs['data-latex'])) {
51
+ const latex = attrs['data-latex'] || (node.children ? node.children.map((c) => c.value || '').join('').replace(/^\$|\$$/g, '').trim() : '')
52
+ currentInline.push({ type: 'inlineMath', value: latex })
53
+ return true
54
+ }
50
55
  if (tagName === 'a') {
51
56
  const childTokens = []
52
57
  for (const child of node.children) traverse(child, nextStyle, childTokens)
@@ -8,13 +8,13 @@ const renderBlock = (node, options = {}) => {
8
8
 
9
9
  switch (node.type) {
10
10
  case 'header': {
11
- const text = renderInline(node.children)
11
+ const text = renderInline(node.children, options)
12
12
  const rawText = node.children ? node.children.map((c) => (c.type === 'text' ? c.value : '')).join('') : ''
13
13
  const slug = (options.slugify || slugify)(rawText, options.usedSlugs)
14
14
  return `<h${node.level} id="${slug}">${text}</h${node.level}>\n`
15
15
  }
16
16
  case 'paragraph': {
17
- return `<p>${renderInline(node.children)}</p>\n`
17
+ return `<p>${renderInline(node.children, options)}</p>\n`
18
18
  }
19
19
  case 'codeBlock': {
20
20
  const lang = node.language || ''
@@ -36,11 +36,11 @@ const renderBlock = (node, options = {}) => {
36
36
  return `<div class="callout callout-${style}">\n<div class="callout-title">${title}</div>\n${innerHtml}</div>\n`
37
37
  }
38
38
  case 'bulletList': {
39
- const itemsHtml = node.items ? node.items.map((item) => `<li>${renderInline(item)}</li>\n`).join('') : ''
39
+ const itemsHtml = node.items ? node.items.map((item) => `<li>${renderInline(item, options)}</li>\n`).join('') : ''
40
40
  return `<ul>\n${itemsHtml}</ul>\n`
41
41
  }
42
42
  case 'orderedList': {
43
- const itemsHtml = node.items ? node.items.map((item) => `<li>${renderInline(item)}</li>\n`).join('') : ''
43
+ const itemsHtml = node.items ? node.items.map((item) => `<li>${renderInline(item, options)}</li>\n`).join('') : ''
44
44
  return `<ol>\n${itemsHtml}</ol>\n`
45
45
  }
46
46
  case 'table': {
@@ -57,7 +57,7 @@ const renderBlock = (node, options = {}) => {
57
57
  .map((cell, colIdx) => {
58
58
  const align = alignments[colIdx] || 'default'
59
59
  const alignAttr = align !== 'default' ? ` align="${align}"` : ''
60
- return `<${cellTag}${alignAttr}>${renderInline(cell)}</${cellTag}>`
60
+ return `<${cellTag}${alignAttr}>${renderInline(cell, options)}</${cellTag}>`
61
61
  })
62
62
  .join('')
63
63
  return `<tr>${cells}</tr>\n`
@@ -73,6 +73,14 @@ const renderBlock = (node, options = {}) => {
73
73
  case 'hr': {
74
74
  return '<hr />\n'
75
75
  }
76
+ case 'html': {
77
+ return `${node.value}\n`
78
+ }
79
+ case 'mathBlock': {
80
+ const mathRenderer = options.mathBlockRenderer || options.codeRenderers?.math
81
+ if (mathRenderer) return mathRenderer(node, options)
82
+ return `<div class="math-display" data-latex="${escapeHtml(node.value)}">$$\n${escapeHtml(node.value)}\n$$</div>\n`
83
+ }
76
84
  default: {
77
85
  return ''
78
86
  }
@@ -1,6 +1,6 @@
1
1
  const { escapeHtml } = require('./escape')
2
2
 
3
- const renderInline = (tokens) => {
3
+ const renderInline = (tokens, options = {}) => {
4
4
  if (!tokens) return ''
5
5
  if (!Array.isArray(tokens)) return escapeHtml(tokens)
6
6
 
@@ -13,15 +13,15 @@ const renderInline = (tokens) => {
13
13
  case 'text':
14
14
  return escapeHtml(token.value)
15
15
  case 'bold':
16
- return `<strong>${renderInline(token.children)}</strong>`
16
+ return `<strong>${renderInline(token.children, options)}</strong>`
17
17
  case 'italic':
18
- return `<em>${renderInline(token.children)}</em>`
18
+ return `<em>${renderInline(token.children, options)}</em>`
19
19
  case 'strikethrough':
20
- return `<del>${renderInline(token.children)}</del>`
20
+ return `<del>${renderInline(token.children, options)}</del>`
21
21
  case 'code':
22
22
  return `<code>${escapeHtml(token.value)}</code>`
23
23
  case 'link':
24
- return `<a href="${escapeHtml(token.url)}">${renderInline(token.children)}</a>`
24
+ return `<a href="${escapeHtml(token.url)}">${renderInline(token.children, options)}</a>`
25
25
  case 'wikilink':
26
26
  return `<a href="${escapeHtml(token.target)}">${escapeHtml(token.display)}</a>`
27
27
  case 'image': {
@@ -36,6 +36,10 @@ const renderInline = (tokens) => {
36
36
  }
37
37
  case 'checkbox':
38
38
  return `<input type="checkbox"${token.checked ? ' checked' : ''} disabled /> `
39
+ case 'inlineMath': {
40
+ if (options.inlineMathRenderer) return options.inlineMathRenderer(token, options)
41
+ return `<span class="math-inline" data-latex="${escapeHtml(token.value)}">$${escapeHtml(token.value)}$</span>`
42
+ }
39
43
  case 'br':
40
44
  return '<br />'
41
45
  default:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sullux/markdown-html",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "A high-performance, bidirectional Markdown <-> HTML compiler supporting GFM, GitBook hints, syntax highlighting, and custom image dimensions.",
5
5
  "main": "./index.js",
6
6
  "author": "Charles Sullivan <charles@sullux.com>",