Skip to content

Commit

Permalink
add support for components in markdown
Browse files Browse the repository at this point in the history
  • Loading branch information
jonian committed Dec 26, 2023
1 parent cd0daec commit df14045
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 8 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@
},
"dependencies": {
"@nuxt/kit": "^3.0.0",
"@xmldom/xmldom": "^0.8.7",
"csvtojson": "^2.0.10",
"destr": "^1.2.2",
"fuzzysort": "^2.0.4",
"gray-matter": "^4.0.3",
"hookable": "^5.5.3",
"html-tags": "^3.3.1",
"js-yaml": "^4.1.0",
"json5": "^2.2.3",
"markdown-it": "^13.0.1",
Expand Down
106 changes: 102 additions & 4 deletions src/builder/parsers/markdown/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,102 @@
import MarkdownIt from 'markdown-it'
import matter from 'gray-matter'
import { DOMParser } from '@xmldom/xmldom'

import JSON5 from 'json5'
import htmlTags from 'html-tags'
import voidHtmlTags from 'html-tags/void.js'

const isHtmlTag = tag => htmlTags.includes(tag) || voidHtmlTags.includes(tag)

const htmlToText = html => {
const dom = new DOMParser()
const doc = dom.parseFromString(`<!DOCTYPE html><html><body>${html}</body></html>`)

return nodeToText(doc.documentElement)
}

const nodeToText = (node) => {
if (node.nodeType === 3 || node.nodeType === 4) {
return node.nodeValue
}

let result = ''

if (node.childNodes && node.childNodes.length > 0) {
for (let i = 0; i < node.childNodes.length; i++) {
const child = node.childNodes[i]
result += nodeToText(child)
}
}

return result
}

const htmlToVnode = html => {
const dom = new DOMParser()
const doc = dom.parseFromString(`<!DOCTYPE html><html><body>${html}</body></html>`)

const env = { tags: [] }
const arr = nodeToVnode(doc.documentElement, env)

if (env.tags.every(isHtmlTag)) {
return html
} else {
return arr
}
}

const nodeToVnode = (node, env) => {
if (node.nodeType === 3 || node.nodeType === 4) {
return node.nodeValue
}

const children = []

if (node.childNodes && node.childNodes.length > 0) {
for (let i = 0; i < node.childNodes.length; i++) {
const child = node.childNodes[i]
children.push(nodeToVnode(child, env))
}
}

if (node.nodeName == 'html') return children[0]
if (node.nodeName == 'body') return children

const attributes = {}

if (node.attributes && node.attributes.length > 0) {
for (let i = 0; i < node.attributes.length; i++) {
const attribute = node.attributes[i]

try {
attributes[attribute.name] = JSON5.parse(attribute.value)
} catch (e) {
attributes[attribute.name] = attribute.value
}
}
}

const result = [node.nodeName]

if (Object.keys(attributes).length) {
result.push(attributes)
}

if (children.length) {
if (children.length == 1 && typeof children[0] == 'string') {
result.push(children[0])
} else {
result.push(children)
}
}

if (!env.tags.includes(node.nodeName)) {
env.tags.push(node.nodeName)
}

return result
}

const cleanHTML = html => {
return html.trim()
Expand All @@ -25,12 +122,13 @@ export default class Markdown {
excerpt_separator: '<!--more-->'
})

const body = cleanHTML(markdown.render(content || ''))
const excerpt = cleanHTML(markdown.render(meta.excerpt || ''))
const excerptHTML = cleanHTML(markdown.render(meta.excerpt || ''))
const contentHTML = cleanHTML(markdown.render(content || ''))

return {
excerpt,
body,
description: htmlToText(excerptHTML),
excerpt: htmlToVnode(excerptHTML),
body: htmlToVnode(contentHTML),
...data
}
}
Expand Down
35 changes: 31 additions & 4 deletions src/runtime/components/NuxtContent.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,42 @@
<template>
<div v-html="document.body" />
</template>

<script>
import { h, resolveDynamicComponent } from 'vue'
import { NuxtLink } from '#components'
const isArr = Array.isArray
const isObj = item => typeof item == 'object'
const isPobj = item => isObj(item) && !isArr(item)
const toNode = node => isArr(node) ? toComp(node) : node
const toArgs = item => isArr(item) ? item.map(toNode) : item
const toSlot = item => isPobj(item) ? item : (() => item)
const toComp = ([tag, ...props]) => {
const comp = /nuxt-?link/i.test(tag) ? NuxtLink : resolveDynamicComponent(tag)
const args = isObj(comp) ? props.map(toArgs).map(toSlot) : props.map(toArgs)
return h(comp, ...args)
}
export default {
name: 'NuxtContent',
props: {
tag: {
type: String,
default: 'div'
},
document: {
type: Object,
required: true
}
},
render() {
const body = this.document.body
if (isArr(body)) {
return h(this.tag, body.map(toNode))
} else {
return h(this.tag, { innerHTML: body })
}
}
}
</script>
10 changes: 10 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,11 @@
"@unhead/ssr" "^1.0.9"
"@unhead/vue" "^1.0.9"

"@xmldom/xmldom@^0.8.7":
version "0.8.7"
resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.7.tgz#8b1e39c547013941974d83ad5e9cf5042071a9a0"
integrity sha512-sI1Ly2cODlWStkINzqGrZ8K6n+MTSbAeQnAipGyL+KZCXuHaRlj2gyyy8B/9MvsFFqN7XHryQnB2QwhzvJXovg==

"@zhead/schema@^1.0.7":
version "1.0.7"
resolved "https://registry.yarnpkg.com/@zhead/schema/-/schema-1.0.7.tgz#229d03af31025a02b2de02a7b065542d7121e0df"
Expand Down Expand Up @@ -2810,6 +2815,11 @@ html-tags@^3.1.0:
resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.2.0.tgz#dbb3518d20b726524e4dd43de397eb0a95726961"
integrity sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==

html-tags@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.3.1.tgz#a04026a18c882e4bba8a01a3d39cfe465d40b5ce"
integrity sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==

[email protected]:
version "2.0.0"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
Expand Down

0 comments on commit df14045

Please sign in to comment.