diff --git a/package.json b/package.json index cdf2a6231..749c439fb 100644 --- a/package.json +++ b/package.json @@ -51,8 +51,8 @@ "json5": "^2.2.3", "knitwork": "^1.0.0", "listhen": "^1.0.4", - "mdast-util-to-hast": "^12.3.0", - "mdurl": "^1.0.1", + "mdast-util-to-hast": "^13.0.0", + "micromark-util-sanitize-uri": "^2.0.0", "ohash": "^1.1.2", "pathe": "^1.1.1", "property-information": "^6.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7fd595561..1a870da9d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,11 +30,11 @@ dependencies: specifier: ^1.0.4 version: 1.0.4 mdast-util-to-hast: - specifier: ^12.3.0 - version: 12.3.0 - mdurl: - specifier: ^1.0.1 - version: 1.0.1 + specifier: ^13.0.0 + version: 13.0.0 + micromark-util-sanitize-uri: + specifier: ^2.0.0 + version: 2.0.0 ohash: specifier: ^1.1.2 version: 1.1.2 @@ -1657,6 +1657,12 @@ packages: dependencies: '@types/unist': 2.0.7 + /@types/hast@3.0.0: + resolution: {integrity: sha512-SoytUJRuf68HXYqcXicQIhCrLQjqeYU2anikr4G3p3Iz+OZO5QDQpDj++gv+RenHsnUBwNZ2dumBArF8VLSk2Q==} + dependencies: + '@types/unist': 3.0.0 + dev: false + /@types/http-cache-semantics@4.0.1: resolution: {integrity: sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==} dev: true @@ -1691,6 +1697,12 @@ packages: '@types/unist': 2.0.7 dev: false + /@types/mdast@4.0.0: + resolution: {integrity: sha512-YLeG8CujC9adtj/kuDzq1N4tCDYKoZ5l/bnjq8d74+t/3q/tHquJOJKUQXJrLCflOHpKjXgcI/a929gpmLOEng==} + dependencies: + '@types/unist': 3.0.0 + dev: false + /@types/mdurl@1.0.2: resolution: {integrity: sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==} dev: true @@ -1868,6 +1880,10 @@ packages: eslint-visitor-keys: 3.4.1 dev: true + /@ungap/structured-clone@1.2.0: + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + dev: false + /@unhead/dom@1.1.32: resolution: {integrity: sha512-AMpHlKEKcm1dxSAvm6GPXhjoZHzXh7ZeR8DAnXVH7Sd9a48xaJhQjmxETweFAcNBSnAn9e7TxTPZVrUcW0ej2w==} dependencies: @@ -3475,6 +3491,12 @@ packages: resolution: {integrity: sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==} dev: true + /devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + dependencies: + dequal: 2.0.3 + dev: false + /diff-sequences@29.4.3: resolution: {integrity: sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -6086,6 +6108,19 @@ packages: unist-util-visit: 4.1.2 dev: false + /mdast-util-to-hast@13.0.0: + resolution: {integrity: sha512-G6lqIDTZUze20I2HuqQ5Qe222DH/hzEv4lQ2I7XFj5hvhFNFMhgxDGIXrJp86Z+9rfzbs2al5RLjcgBcq3Ry4w==} + dependencies: + '@types/hast': 3.0.0 + '@types/mdast': 4.0.0 + '@ungap/structured-clone': 1.2.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.0 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + dev: false + /mdast-util-to-markdown@1.5.0: resolution: {integrity: sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==} dependencies: @@ -6113,10 +6148,6 @@ packages: resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} dev: true - /mdurl@1.0.1: - resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==} - dev: false - /memory-fs@0.5.0: resolution: {integrity: sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==} engines: {node: '>=4.3.0 <5.0.0 || >=5.10'} @@ -6273,6 +6304,13 @@ packages: micromark-util-types: 1.1.0 dev: false + /micromark-util-character@2.0.1: + resolution: {integrity: sha512-3wgnrmEAJ4T+mGXAUfMvMAbxU9RDG43XmGce4j6CwPtVxB3vfwXSZ6KhFwDzZ3mZHhmPimMAXg71veiBGzeAZw==} + dependencies: + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + /micromark-util-chunked@1.1.0: resolution: {integrity: sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==} dependencies: @@ -6313,6 +6351,10 @@ packages: resolution: {integrity: sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==} dev: false + /micromark-util-encode@2.0.0: + resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==} + dev: false + /micromark-util-html-tag-name@1.2.0: resolution: {integrity: sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==} dev: false @@ -6337,6 +6379,14 @@ packages: micromark-util-symbol: 1.1.0 dev: false + /micromark-util-sanitize-uri@2.0.0: + resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==} + dependencies: + micromark-util-character: 2.0.1 + micromark-util-encode: 2.0.0 + micromark-util-symbol: 2.0.0 + dev: false + /micromark-util-subtokenize@1.1.0: resolution: {integrity: sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==} dependencies: @@ -6350,10 +6400,18 @@ packages: resolution: {integrity: sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==} dev: false + /micromark-util-symbol@2.0.0: + resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==} + dev: false + /micromark-util-types@1.1.0: resolution: {integrity: sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==} dev: false + /micromark-util-types@2.0.0: + resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==} + dev: false + /micromark@3.2.0: resolution: {integrity: sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==} dependencies: diff --git a/src/module.ts b/src/module.ts index 9cbcb9d08..6d33a105a 100644 --- a/src/module.ts +++ b/src/module.ts @@ -271,7 +271,10 @@ export default defineNuxtModule({ defaultLocale: undefined, highlight: false, markdown: { - tags: Object.fromEntries(PROSE_TAGS.map(t => [t, `prose-${t}`])), + tags: { + ...Object.fromEntries(PROSE_TAGS.map(t => [t, `prose-${t}`])), + code: 'ProseCodeInline' + }, anchorLinks: { depth: 4, exclude: [1] diff --git a/src/runtime/components/Prose/ProsePre.vue b/src/runtime/components/Prose/ProsePre.vue new file mode 100644 index 000000000..53e20a03b --- /dev/null +++ b/src/runtime/components/Prose/ProsePre.vue @@ -0,0 +1,38 @@ + + + diff --git a/src/runtime/markdown-parser/handler/blockquote.ts b/src/runtime/markdown-parser/handler/blockquote.ts deleted file mode 100644 index 4e21d18bf..000000000 --- a/src/runtime/markdown-parser/handler/blockquote.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { H } from 'mdast-util-to-hast' -import { all } from 'mdast-util-to-hast' -import type { MdastContent } from 'mdast-util-to-hast/lib' -import { wrap } from './utils' - -export default function blockquote (h: H, node: MdastContent) { - return h(node, 'blockquote', wrap(all(h, node), true)) -} diff --git a/src/runtime/markdown-parser/handler/code.ts b/src/runtime/markdown-parser/handler/code.ts index d6390a2ad..f0881d6e3 100644 --- a/src/runtime/markdown-parser/handler/code.ts +++ b/src/runtime/markdown-parser/handler/code.ts @@ -1,31 +1,43 @@ -import type { H } from 'mdast-util-to-hast' +import { type State } from 'mdast-util-to-hast' +import { type Element, type Properties } from 'hast' +import { type Code } from 'mdast' import { detab } from 'detab' -import { u } from 'unist-builder' -import type { MdastContent } from 'mdast-util-to-hast/lib' import { parseThematicBlock } from './utils' -type Node = MdastContent & { - lang: string - meta: string - value: string -} - -export default (h: H, node: Node) => { +export default (state: State, node: Code) => { const lang = (node.lang || '') + ' ' + (node.meta || '') const { language, highlights, filename, meta } = parseThematicBlock(lang) - const code = node.value ? detab(node.value + '\n') : '' + const value = node.value ? detab(node.value + '\n') : '' + + // Create ``. + let result: Element = { + type: 'element', + tagName: 'code', + properties: { __ignoreMap: '' }, + children: [{ type: 'text', value }] + } + + if (node.meta) { + result.data = { meta: node.meta } + } + + state.patch(node, result) + result = state.applyData(node, result) + + const properties: Properties = { + language, + filename, + highlights, + meta, + code: value + } + + if (node.lang) { + properties.className = ['language-' + node.lang] + } - return h( - node.position, - 'code', - { - language, - filename, - highlights, - meta, - code, - className: [`language-${language}`] - }, - [h(node, 'pre', {}, [h(node, 'code', { __ignoreMap: '' }, [u('text', code)])])] - ) + // Create `
`.
+  result = { type: 'element', tagName: 'pre', properties, children: [result] }
+  state.patch(node, result)
+  return result
 }
diff --git a/src/runtime/markdown-parser/handler/containerComponent.ts b/src/runtime/markdown-parser/handler/containerComponent.ts
index 016355d3f..ec2b1cd22 100644
--- a/src/runtime/markdown-parser/handler/containerComponent.ts
+++ b/src/runtime/markdown-parser/handler/containerComponent.ts
@@ -1,21 +1,30 @@
-import type { H } from 'mdast-util-to-hast'
-import { all } from 'mdast-util-to-hast'
-import type { MdastContent } from 'mdast-util-to-hast/lib'
+import { type State } from 'mdast-util-to-hast'
+import { type Element, type Properties } from 'hast'
+import { type Nodes as MdastContent } from 'mdast'
 
 type Node = MdastContent & {
-  tagName: string
-  attributes?: any
-  fmAttributes?: any
+  name: string
+  attributes?: Properties
+  fmAttributes?: Properties
 }
 
-export default function containerComponent (h: H, node: Node) {
-  const hast: any = h(node, node.tagName, node.attributes, all(h, node))
+export default function containerComponent (state: State, node: Node) {
+  const result: Element = {
+    type: 'element',
+    tagName: node.name,
+    properties: {
+      ...node.attributes,
+      ...node.data?.hProperties
+    },
+    children: state.all(node)
+  }
+  state.patch(node, result)
 
-  // Inline attributes that passed in MDC sysntax `:component{...attributes}`
-  hast.attributes = node.attributes
+  // @ts-ignore Inline attributes that passed in MDC sysntax `:component{...attributes}`
+  result.attributes = node.attributes
 
-  // Attributes define using FrontMatter syntax and YAML format
-  hast.fmAttributes = node.fmAttributes
+  // @ts-ignore Attributes define using FrontMatter syntax and YAML format
+  result.fmAttributes = node.fmAttributes
 
-  return hast
+  return result
 }
diff --git a/src/runtime/markdown-parser/handler/emphasis.ts b/src/runtime/markdown-parser/handler/emphasis.ts
index 45b0b750c..7c5baae07 100644
--- a/src/runtime/markdown-parser/handler/emphasis.ts
+++ b/src/runtime/markdown-parser/handler/emphasis.ts
@@ -1,11 +1,14 @@
-import type { H } from 'mdast-util-to-hast'
-import { all } from 'mdast-util-to-hast'
-import type { MdastContent } from 'mdast-util-to-hast/lib'
+import { type State } from 'mdast-util-to-hast'
+import { type Element, type Properties } from 'hast'
+import { type Emphasis } from 'mdast'
 
-type Node = MdastContent & {
-  attributes?: any
-}
-
-export default function emphasis (h: H, node: Node) {
-  return h(node, 'em', node.attributes, all(h, node))
+export default function emphasis (state: State, node: Emphasis & { attributes?: Properties }) {
+  const result: Element = {
+    type: 'element',
+    tagName: 'em',
+    properties: node.attributes || {},
+    children: state.all(node)
+  }
+  state.patch(node, result)
+  return state.applyData(node, result)
 }
diff --git a/src/runtime/markdown-parser/handler/heading.ts b/src/runtime/markdown-parser/handler/heading.ts
index fe5fc393b..5bacbb534 100644
--- a/src/runtime/markdown-parser/handler/heading.ts
+++ b/src/runtime/markdown-parser/handler/heading.ts
@@ -1,7 +1,14 @@
-import type { H } from 'mdast-util-to-hast'
-import { all } from 'mdast-util-to-hast'
-import type { MdastContent } from 'mdast-util-to-hast/lib'
+import { type State } from 'mdast-util-to-hast'
+import { type Element, type Properties } from 'hast'
+import { type Heading } from 'mdast'
 
-export default function heading (h: H, node: MdastContent) {
-  return h(node, 'h' + (node as any).depth, all(h, node))
+export default function heading (state: State, node: Heading & { attributes?: Properties }) {
+  const result: Element = {
+    type: 'element',
+    tagName: 'h' + node.depth,
+    properties: node.attributes || {},
+    children: state.all(node)
+  }
+  state.patch(node, result)
+  return state.applyData(node, result)
 }
diff --git a/src/runtime/markdown-parser/handler/html.ts b/src/runtime/markdown-parser/handler/html.ts
index 83054f9bf..8d060b683 100644
--- a/src/runtime/markdown-parser/handler/html.ts
+++ b/src/runtime/markdown-parser/handler/html.ts
@@ -1,24 +1,21 @@
-import type { H } from 'mdast-util-to-hast'
+import { type State, type Raw } from 'mdast-util-to-hast'
+import { type Html } from 'mdast'
 import { kebabCase } from 'scule'
-import { u } from 'unist-builder'
-import type { MdastContent } from 'mdast-util-to-hast/lib'
 import { getTagName } from './utils'
 
-type Node = MdastContent & {
-  value: string
-}
-
-export default function html (h: H, node: Node) {
+export default function html (state: State, node: Html) {
   const tagName = getTagName(node.value)
 
   if (tagName && /[A-Z]/.test(tagName)) {
     node.value = node.value.replace(tagName, kebabCase(tagName))
   }
 
-  // Html `` tags should parse and render as inline code
-  if (tagName === 'code') {
-    node.value = node.value.replace(tagName, 'code-inline')
+  if ((state as any).dangerous || state.options?.allowDangerousHtml) {
+    /** @type {Raw} */
+    const result: Raw = { type: 'raw', value: node.value }
+    state.patch(node, result)
+    return state.applyData(node, result)
   }
 
-  return h.dangerous ? h.augment(node, u('raw', node.value)) : null
+  return undefined
 }
diff --git a/src/runtime/markdown-parser/handler/image.ts b/src/runtime/markdown-parser/handler/image.ts
index 93af023d5..47f35bdac 100644
--- a/src/runtime/markdown-parser/handler/image.ts
+++ b/src/runtime/markdown-parser/handler/image.ts
@@ -1,24 +1,20 @@
-import type { H } from 'mdast-util-to-hast'
-import { encode } from 'mdurl'
-import type { MdastContent } from 'mdast-util-to-hast/lib'
+import { type State } from 'mdast-util-to-hast'
+import { type Element, type Properties } from 'hast'
+import { type Image } from 'mdast'
+import { normalizeUri } from 'micromark-util-sanitize-uri'
 
-type Node = MdastContent & {
-  url: string
-  alt: string
-  title: string
-  attributes?: any
-}
+export default function image (state: State, node: Image & { attributes?: Properties }) {
+  const properties: Properties = { ...node.attributes, src: normalizeUri(node.url) }
 
-export default function image (h: H, node: Node) {
-  const props: any = {
-    ...node.attributes,
-    src: encode(node.url),
-    alt: node.alt
+  if (node.alt !== null && node.alt !== undefined) {
+    properties.alt = node.alt
   }
 
   if (node.title !== null && node.title !== undefined) {
-    props.title = node.title
+    properties.title = node.title
   }
 
-  return h(node, 'img', props)
+  const result: Element = { type: 'element', tagName: 'img', properties, children: [] }
+  state.patch(node, result)
+  return state.applyData(node, result)
 }
diff --git a/src/runtime/markdown-parser/handler/index.ts b/src/runtime/markdown-parser/handler/index.ts
index b481d5a2c..b64eab29f 100644
--- a/src/runtime/markdown-parser/handler/index.ts
+++ b/src/runtime/markdown-parser/handler/index.ts
@@ -4,30 +4,22 @@ import html from './html'
 import heading from './heading'
 import link from './link'
 import list from './list'
-import listItem from './listItem'
-import table from './table'
 import paragraph from './paragraph'
 import image from './image'
-import blockquote from './blockquote'
 import strong from './strong'
 import inlineCode from './inlineCode'
-import thematicBreak from './thematicBreak'
 import containerComponent from './containerComponent'
 
 export default {
   emphasis,
   code,
+  link,
   paragraph,
   html,
-  link,
   list,
-  listItem,
   heading,
-  table,
   image,
-  blockquote,
   strong,
   inlineCode,
-  thematicBreak,
   containerComponent
 }
diff --git a/src/runtime/markdown-parser/handler/inlineCode.ts b/src/runtime/markdown-parser/handler/inlineCode.ts
index 7f30a652e..6d1dc9c05 100644
--- a/src/runtime/markdown-parser/handler/inlineCode.ts
+++ b/src/runtime/markdown-parser/handler/inlineCode.ts
@@ -1,15 +1,17 @@
-import type { H } from 'mdast-util-to-hast'
-import { u } from 'unist-builder'
-import type { MdastContent } from 'mdast-util-to-hast/lib'
+import { type State } from 'mdast-util-to-hast'
+import { type Element, type Text } from 'hast'
+import { type InlineCode } from 'mdast'
 
-type Node = MdastContent & {
-  value: string
-  attributes?: any
-}
+export default function inlineCode (state: State, node: InlineCode & { attributes: any }) {
+  const text: Text = { type: 'text', value: node.value.replace(/\r?\n|\r/g, ' ') }
+  state.patch(node, text)
 
-export default function inlineCode (h: H, node: Node) {
-  return h(node, 'code-inline', node.attributes, [
-    // @ts-ignore
-    u('text', node.value.replace(/\r?\n|\r/g, ' '))
-  ])
+  const result: Element = {
+    type: 'element',
+    tagName: 'code',
+    properties: node.attributes,
+    children: [text]
+  }
+  state.patch(node, result)
+  return state.applyData(node, result)
 }
diff --git a/src/runtime/markdown-parser/handler/link.ts b/src/runtime/markdown-parser/handler/link.ts
index 98645aa19..f70b893ea 100644
--- a/src/runtime/markdown-parser/handler/link.ts
+++ b/src/runtime/markdown-parser/handler/link.ts
@@ -1,31 +1,28 @@
-// import { join } from 'path'
-// import fs from 'fs'
-import type { H } from 'mdast-util-to-hast'
-import { all } from 'mdast-util-to-hast'
-import { encode } from 'mdurl'
-import type { MdastContent } from 'mdast-util-to-hast/lib'
+import { type State } from 'mdast-util-to-hast'
+import { type Element, type Properties } from 'hast'
+import { type Link } from 'mdast'
 import { isRelative } from 'ufo'
+import { normalizeUri } from 'micromark-util-sanitize-uri'
 import { generatePath } from '../../transformers/path-meta'
 
-type Node = MdastContent & {
-  title: string
-  url: string
-  attributes?: any
-  tagName: string
-  children?: Node[]
-}
-
-export default function link (h: H, node: Node) {
-  const props: any = {
-    ...((node.attributes || {}) as object),
-    href: encode(normalizeLink(node.url))
+export default function link (state: State, node: Link & { attributes?: Properties}) {
+  const properties: Properties = {
+    ...((node.attributes || {})),
+    href: normalizeUri(normalizeLink(node.url))
   }
 
   if (node.title !== null && node.title !== undefined) {
-    props.title = node.title
+    properties.title = node.title
   }
 
-  return h(node, 'a', props, all(h, node))
+  const result: Element = {
+    type: 'element',
+    tagName: 'a',
+    properties,
+    children: state.all(node)
+  }
+  state.patch(node, result)
+  return state.applyData(node, result)
 }
 
 function normalizeLink (link: string) {
diff --git a/src/runtime/markdown-parser/handler/list.ts b/src/runtime/markdown-parser/handler/list.ts
index 30e74c549..71333f2ec 100644
--- a/src/runtime/markdown-parser/handler/list.ts
+++ b/src/runtime/markdown-parser/handler/list.ts
@@ -1,27 +1,43 @@
-import type { H } from 'mdast-util-to-hast'
-import { all } from 'mdast-util-to-hast'
-import type { MdastContent } from 'mdast-util-to-hast/lib'
-import { wrap } from './utils'
+import { type State } from 'mdast-util-to-hast'
+import { type Element, type Properties } from 'hast'
+import { type List } from 'mdast'
 
-type Node = MdastContent & {
-  ordered?: boolean
-  start?: number,
-  checked?: boolean
-  children: Node[]
-}
-
-export default function list (h: H, node: Node) {
-  const props: any = {}
-  const name = `${node.ordered ? 'ol' : 'ul'}`
+export default function list (state: State, node: List) {
+  const properties: Properties = {}
+  const results = state.all(node)
+  let index = -1
 
   if (typeof node.start === 'number' && node.start !== 1) {
-    props.start = node.start
+    properties.start = node.start
+  }
+
+  // Like GitHub, add a class for custom styling.
+  while (++index < results.length) {
+    const child = results[index]
+
+    if (
+      child.type === 'element' &&
+      child.tagName === 'li' &&
+      child.properties &&
+      Array.isArray(child.properties.className) &&
+      child.properties.className.includes('task-list-item')
+    ) {
+      properties.className = ['contains-task-list']
+      break
+    }
   }
 
   // Add class for task list. See: https://github.com/remarkjs/remark-gfm#use
   if ((node.children || []).some(child => typeof child.checked === 'boolean')) {
-    props.className = ['contains-task-list']
+    properties.className = ['contains-task-list']
   }
 
-  return h(node, name, props, wrap(all(h, node), true))
+  const result: Element = {
+    type: 'element',
+    tagName: node.ordered ? 'ol' : 'ul',
+    properties,
+    children: state.wrap(results, true)
+  }
+  state.patch(node, result)
+  return state.applyData(node, result)
 }
diff --git a/src/runtime/markdown-parser/handler/listItem.ts b/src/runtime/markdown-parser/handler/listItem.ts
deleted file mode 100644
index e60a1e8eb..000000000
--- a/src/runtime/markdown-parser/handler/listItem.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-// import { u } from 'unist-builder'
-import type { H } from 'mdast-util-to-hast'
-import { all } from 'mdast-util-to-hast'
-import type { MdastContent } from 'mdast-util-to-hast/lib'
-
-type Node = MdastContent & {
-  tagName: string
-  checked?: boolean
-  spread?: boolean
-  children?: Node[]
-}
-
-export default function listItem (h: H, node: Node, parent: Node) {
-  const result = all(h, node)
-  const loose = parent ? listLoose(parent) : listItemLoose(node)
-  const props: any = {}
-  let wrapped: any[] = []
-  let index
-  let child
-
-  if (typeof node.checked === 'boolean') {
-    result.unshift(
-      h({} as any, 'input', {
-        type: 'checkbox',
-        checked: node.checked,
-        disabled: true
-      })
-    )
-
-    // According to github-markdown-css, this class hides bullet.
-    // See: .
-    props.className = ['task-list-item']
-  }
-
-  const length = result.length
-  index = -1
-
-  while (++index < length) {
-    child = result[index] as Node
-
-    if (child.tagName === 'p' && !loose) {
-      wrapped = wrapped.concat(child.children || [])
-    } else {
-      wrapped.push(child)
-    }
-  }
-
-  return h(node, 'li', props, wrapped)
-}
-
-function listLoose (node: Node) {
-  let loose = node.spread
-  const children = node.children as Node[]
-  const length = children.length
-  let index = -1
-
-  while (!loose && ++index < length) {
-    loose = listItemLoose(children[index])
-  }
-
-  return loose
-}
-
-function listItemLoose (node: Node) {
-  const spread = node.spread
-  const children = (node.children || []) as Node[]
-  return spread === undefined || spread === null ? children.length > 1 : spread
-}
diff --git a/src/runtime/markdown-parser/handler/paragraph.ts b/src/runtime/markdown-parser/handler/paragraph.ts
index 67287ad3b..9588d835e 100644
--- a/src/runtime/markdown-parser/handler/paragraph.ts
+++ b/src/runtime/markdown-parser/handler/paragraph.ts
@@ -1,21 +1,25 @@
-import type { H } from 'mdast-util-to-hast'
-import { all } from 'mdast-util-to-hast'
-import { MdastContent } from 'mdast-util-to-hast/lib'
+import { type State } from 'mdast-util-to-hast'
+import { type Element } from 'hast'
+import { type Paragraph } from 'mdast'
 import { kebabCase } from 'scule'
 import htmlTags from '../../utils/html-tags'
 import { getTagName } from './utils'
 
-type Node = MdastContent & {
-  children: any[]
-}
-
-export default function paragraph (h: H, node: Node) {
+export default function paragraph (state: State, node: Paragraph) {
   if (node.children && node.children[0] && node.children[0].type === 'html') {
     const tagName = kebabCase(getTagName(node.children[0].value) || 'div')
     // Unwrap if component
     if (!htmlTags.includes(tagName)) {
-      return all(h, node)
+      return state.all(node)
     }
   }
-  return h(node, 'p', all(h, node))
+
+  const result: Element = {
+    type: 'element',
+    tagName: 'p',
+    properties: {},
+    children: state.all(node)
+  }
+  state.patch(node, result)
+  return state.applyData(node, result)
 }
diff --git a/src/runtime/markdown-parser/handler/strong.ts b/src/runtime/markdown-parser/handler/strong.ts
index f6d4d42a9..fc8f3ccad 100644
--- a/src/runtime/markdown-parser/handler/strong.ts
+++ b/src/runtime/markdown-parser/handler/strong.ts
@@ -1,11 +1,14 @@
-import type { H } from 'mdast-util-to-hast'
-import { all } from 'mdast-util-to-hast'
-import { MdastContent } from 'mdast-util-to-hast/lib'
+import { type State } from 'mdast-util-to-hast'
+import { type Element, type Properties } from 'hast'
+import { type Strong } from 'mdast'
 
-type Node = MdastContent & {
-  attributes?: any
-}
-
-export default function strong (h: H, node: Node) {
-  return h(node, 'strong', node.attributes, all(h, node))
+export default function strong (state: State, node: Strong & { attributes?: Properties }) {
+  const result: Element = {
+    type: 'element',
+    tagName: 'strong',
+    properties: node.attributes || {},
+    children: state.all(node)
+  }
+  state.patch(node, result)
+  return state.applyData(node, result)
 }
diff --git a/src/runtime/markdown-parser/handler/table.ts b/src/runtime/markdown-parser/handler/table.ts
deleted file mode 100644
index 74ce4a6c4..000000000
--- a/src/runtime/markdown-parser/handler/table.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import type { H } from 'mdast-util-to-hast'
-import { position } from 'unist-util-position'
-import { all } from 'mdast-util-to-hast'
-import type { MdastContent } from 'mdast-util-to-hast/lib'
-import { wrap } from './utils'
-
-type Node = MdastContent & {
-  align?: any
-  children: MdastContent[]
-}
-
-export default function table (h: H, node: Node) {
-  const rows = node.children
-  const align = node.align || []
-
-  const result = rows.map((row: any, index: number) => {
-    const childres = row.children
-    const name = index === 0 ? 'th' : 'td'
-    let pos = node.align ? align.length : childres.length
-    const out = []
-
-    while (pos--) {
-      const cell = childres[pos]
-      out[pos] = h(cell, name, { align: align[pos] }, cell ? all(h, cell) : [])
-    }
-
-    return h(row, 'tr', wrap(out, true))
-  })
-
-  const body =
-    result[1] &&
-    h(
-      {
-        start: position(result[1]).start,
-        end: position(result[result.length - 1]).end
-      } as any,
-      'tbody',
-      wrap(result.slice(1), true)
-    )
-  return h(node, 'table', wrap([h(result[0].position, 'thead', wrap([result[0]], true))].concat(body || []), true))
-}
diff --git a/src/runtime/markdown-parser/handler/thematicBreak.ts b/src/runtime/markdown-parser/handler/thematicBreak.ts
deleted file mode 100644
index 0ea621c08..000000000
--- a/src/runtime/markdown-parser/handler/thematicBreak.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import type { H } from 'mdast-util-to-hast'
-import type { MdastContent } from 'mdast-util-to-hast/lib'
-
-export default function thematicBreak (h: H, node: MdastContent) {
-  return h(node, 'hr')
-}
diff --git a/src/runtime/markdown-parser/handler/utils.ts b/src/runtime/markdown-parser/handler/utils.ts
index d08766fc8..9207cefa8 100644
--- a/src/runtime/markdown-parser/handler/utils.ts
+++ b/src/runtime/markdown-parser/handler/utils.ts
@@ -1,5 +1,3 @@
-import { u } from 'unist-builder'
-
 /**
  * Parses the value defined next to 3 back ticks
  * in a codeblock and set line-highlights or
@@ -48,28 +46,3 @@ export function getTagName (value: string) {
 
   return result && result[1]
 }
-
-// mdast-util-to-hast/lib/wrap.js
-/**
- * Wrap `nodes` with line feeds between each entry.
- * Optionally adds line feeds at the start and end.
- */
-export function wrap (nodes: any[], loose = false) {
-  const result = []
-  let index = -1
-
-  if (loose) {
-    result.push(u('text', '\n'))
-  }
-
-  while (++index < nodes.length) {
-    if (index) { result.push(u('text', '\n')) }
-    result.push(nodes[index])
-  }
-
-  if (loose && nodes.length > 0) {
-    result.push(u('text', '\n'))
-  }
-
-  return result
-}
diff --git a/src/runtime/transformers/shiki/shiki.ts b/src/runtime/transformers/shiki/shiki.ts
index b074ed9e7..b056f8dbb 100644
--- a/src/runtime/transformers/shiki/shiki.ts
+++ b/src/runtime/transformers/shiki/shiki.ts
@@ -30,11 +30,11 @@ export default defineTransformer({
       const inlineCodes: any = []
       visit(
         document,
-        (node: any) => (node?.tag === 'code' && node?.props.code) || (node?.tag === 'code-inline' && (node.props?.lang || node.props?.language)),
+        (node: any) => (node?.tag === 'pre' && node?.props.code) || (node?.tag === 'code' && (node.props?.lang || node.props?.language)),
         (node: MarkdownNode) => {
-          if (node?.tag === 'code') {
+          if (node?.tag === 'pre') {
             codeBlocks.push(node)
-          } else if (node?.tag === 'code-inline') {
+          } else if (node?.tag === 'code') {
             inlineCodes.push(node)
           }
         }
@@ -75,7 +75,7 @@ export default defineTransformer({
     async function highlightBlock (node: MarkdownNode, styleMap: TokenStyleMap) {
       const { code, language: lang, highlights = [] } = node.props!
 
-      const innerCodeNode = node.children![0].children![0]
+      const innerCodeNode = node.children![0]
       innerCodeNode.children = await shikiHighlighter.getHighlightedAST(code, lang, options.theme, { styleMap, highlights })
 
       return node
diff --git a/src/utils.ts b/src/utils.ts
index e6a28f838..066152402 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -27,7 +27,6 @@ export const PROSE_TAGS = [
   'p',
   'a',
   'blockquote',
-  'code-inline',
   'code',
   'em',
   'h1',
@@ -47,7 +46,8 @@ export const PROSE_TAGS = [
   'tbody',
   'td',
   'th',
-  'tr'
+  'tr',
+  'pre'
 ]
 
 const unstorageDrivers = {
diff --git a/test/features/highlighter.ts b/test/features/highlighter.ts
index a2da3c133..42552faee 100644
--- a/test/features/highlighter.ts
+++ b/test/features/highlighter.ts
@@ -23,7 +23,7 @@ export const testHighlighter = () => {
       expect(styleElement.tag).toBe('style')
       const style = styleElement.children[0].value
 
-      const code = parsed.body.children[0].children[0].children[0].children[0].children
+      const code = parsed.body.children[0].children[0].children[0].children
 
       expect(style).toContain(`.${code[0].props.class}{color:#D73A49;}`)
       expect(style).toContain(`.dark .${code[0].props.class}{color:#F97583;}`)
diff --git a/test/features/parser-markdown.ts b/test/features/parser-markdown.ts
index dd1248a75..c2d72cb77 100644
--- a/test/features/parser-markdown.ts
+++ b/test/features/parser-markdown.ts
@@ -37,7 +37,7 @@ export const testMarkdownParser = () => {
         expect(parsed).toHaveProperty('body')
         expect(parsed.body).toHaveProperty('type', 'root')
         expect(parsed.body).toHaveProperty('children[0].tag', 'p')
-        expect(parsed.body).toHaveProperty('children[0].children[0].tag', 'code-inline')
+        expect(parsed.body).toHaveProperty('children[0].children[0].tag', 'code')
       })
 
       test('Keep meta from fenced code block', async () => {
@@ -55,7 +55,7 @@ export const testMarkdownParser = () => {
         })
 
         expect(parsed).toHaveProperty('body')
-        expect(parsed.body).toHaveProperty('children[0].tag', 'code')
+        expect(parsed.body).toHaveProperty('children[0].tag', 'pre')
         expect(parsed.body).toHaveProperty('children[0].props')
         const props = parsed.body.children[0].props
         expect(props).toHaveProperty('meta')
@@ -80,7 +80,7 @@ export const testMarkdownParser = () => {
         })
 
         expect(parsed).toHaveProperty('body')
-        expect(parsed.body).toHaveProperty('children[0].tag', 'code')
+        expect(parsed.body).toHaveProperty('children[0].tag', 'pre')
         expect(parsed.body).toHaveProperty('children[0].props')
         const props = parsed.body.children[0].props
         expect(props).toHaveProperty('meta')
@@ -105,7 +105,7 @@ export const testMarkdownParser = () => {
         })
 
         expect(parsed).toHaveProperty('body')
-        expect(parsed.body).toHaveProperty('children[0].tag', 'code')
+        expect(parsed.body).toHaveProperty('children[0].tag', 'pre')
         expect(parsed.body).toHaveProperty('children[0].props')
         const props = parsed.body.children[0].props
         expect(props).toHaveProperty('meta')