From df140457c86e200d1a8f97d2f15ec8116c2fe914 Mon Sep 17 00:00:00 2001 From: Jonian Guveli Date: Tue, 26 Dec 2023 18:27:04 +0200 Subject: [PATCH] add support for components in markdown --- package.json | 2 + src/builder/parsers/markdown/index.js | 106 ++++++++++++++++++++++++- src/runtime/components/NuxtContent.vue | 35 +++++++- yarn.lock | 10 +++ 4 files changed, 145 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index e727d00..565c489 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/builder/parsers/markdown/index.js b/src/builder/parsers/markdown/index.js index cb0fe0b..270bdcf 100644 --- a/src/builder/parsers/markdown/index.js +++ b/src/builder/parsers/markdown/index.js @@ -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(`${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(`${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() @@ -25,12 +122,13 @@ export default class Markdown { excerpt_separator: '' }) - 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 } } diff --git a/src/runtime/components/NuxtContent.vue b/src/runtime/components/NuxtContent.vue index db0e9f0..de8f6ee 100644 --- a/src/runtime/components/NuxtContent.vue +++ b/src/runtime/components/NuxtContent.vue @@ -1,15 +1,42 @@ - - diff --git a/yarn.lock b/yarn.lock index e59b1b6..e45c40b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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" @@ -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== + http-errors@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"