refactor: extract shared parse markdown
- Added `@microflash/remark-callout-directives` to `package.json` and `pnpm-lock.yaml`. - Updated `MarkdownWeb` component to utilize a new markdown parsing utility. - Removed unused imports related to `remark-gh-alerts` and refactored markdown parsing logic. - Enhanced `parse-markdown` function to integrate with the new utility and support custom components. This update improves markdown rendering capabilities and streamlines the codebase. Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
parent
93b38a7c70
commit
8007924a7f
|
|
@ -1,61 +1,12 @@
|
|||
"use dom"
|
||||
import "@/src/global.css"
|
||||
import "remark-gh-alerts/styles/github-base.css"
|
||||
import "remark-gh-alerts/styles/github-colors-dark-media.css"
|
||||
import "remark-gh-alerts/styles/github-colors-light.css"
|
||||
|
||||
import { parseMarkdown } from "@follow/components/src/utils/parse-markdown"
|
||||
import { cn } from "@follow/utils"
|
||||
import type { Components } from "hast-util-to-jsx-runtime"
|
||||
import { toJsxRuntime } from "hast-util-to-jsx-runtime"
|
||||
import { Fragment, jsx, jsxs } from "react/jsx-runtime"
|
||||
import rehypeStringify from "rehype-stringify"
|
||||
import remarkDirective from "remark-directive"
|
||||
import remarkGfm from "remark-gfm"
|
||||
import remarkGithubAlerts from "remark-gh-alerts"
|
||||
import remarkParse from "remark-parse"
|
||||
import remarkRehype from "remark-rehype"
|
||||
import { unified } from "unified"
|
||||
import { useDarkMode } from "usehooks-ts"
|
||||
import { VFile } from "vfile"
|
||||
|
||||
import { useCSSInjection } from "@/src/theme/web"
|
||||
|
||||
export interface RemarkOptions {
|
||||
components: Partial<Components>
|
||||
}
|
||||
const parseMarkdown = (content: string, options?: Partial<RemarkOptions>) => {
|
||||
const file = new VFile(content)
|
||||
const { components } = options || {}
|
||||
|
||||
const pipeline = unified()
|
||||
.use(remarkParse)
|
||||
|
||||
.use(remarkGfm)
|
||||
.use(remarkGithubAlerts)
|
||||
|
||||
.use(remarkDirective)
|
||||
|
||||
.use(remarkRehype, { allowDangerousHtml: true })
|
||||
.use(rehypeStringify, { allowDangerousHtml: true })
|
||||
|
||||
const tree = pipeline.parse(content)
|
||||
|
||||
const hastTree = pipeline.runSync(tree, file)
|
||||
|
||||
return {
|
||||
content: toJsxRuntime(hastTree, {
|
||||
Fragment,
|
||||
ignoreInvalidStyle: true,
|
||||
jsx: (type, props, key) => jsx(type as any, props, key),
|
||||
jsxs: (type, props, key) => jsxs(type as any, props, key),
|
||||
passNode: true,
|
||||
components: {
|
||||
...components,
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
const MarkdownWeb: WebComponent<{ value: string }> = ({ value }) => {
|
||||
useCSSInjection()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,96 +1,28 @@
|
|||
import "@microflash/remark-callout-directives/theme/github"
|
||||
import "remark-gh-alerts/styles/github-colors-light.css"
|
||||
import "remark-gh-alerts/styles/github-colors-dark-media.css"
|
||||
import "remark-gh-alerts/styles/github-base.css"
|
||||
|
||||
import remarkCalloutDirectives from "@microflash/remark-callout-directives"
|
||||
import type { Components } from "hast-util-to-jsx-runtime"
|
||||
import { toJsxRuntime } from "hast-util-to-jsx-runtime"
|
||||
import type { RemarkOptions } from "@follow/components/utils/parse-markdown.js"
|
||||
import { parseMarkdown as parseMarkdownImpl } from "@follow/components/utils/parse-markdown.js"
|
||||
import { createElement } from "react"
|
||||
import { Fragment, jsx, jsxs } from "react/jsx-runtime"
|
||||
import rehypeStringify from "rehype-stringify"
|
||||
import remarkDirective from "remark-directive"
|
||||
import remarkGfm from "remark-gfm"
|
||||
import remarkGithubAlerts from "remark-gh-alerts"
|
||||
import remarkParse from "remark-parse"
|
||||
import remarkRehype from "remark-rehype"
|
||||
import { unified } from "unified"
|
||||
import { VFile } from "vfile"
|
||||
|
||||
import { MarkdownLink } from "~/components/ui/markdown/renderers/MarkdownLink"
|
||||
import { VideoPlayer } from "~/components/ui/media/VideoPlayer"
|
||||
|
||||
export interface RemarkOptions {
|
||||
components: Partial<Components>
|
||||
}
|
||||
export const parseMarkdown = (content: string, options?: Partial<RemarkOptions>) => {
|
||||
const file = new VFile(content)
|
||||
const { components } = options || {}
|
||||
|
||||
const pipeline = unified()
|
||||
.use(remarkParse)
|
||||
|
||||
.use(remarkGfm)
|
||||
.use(remarkGithubAlerts)
|
||||
|
||||
.use(remarkDirective)
|
||||
.use(remarkCalloutDirectives, {
|
||||
aliases: {
|
||||
danger: "deter",
|
||||
tip: "note",
|
||||
return parseMarkdownImpl(content, {
|
||||
...options,
|
||||
components: {
|
||||
a: ({ node, ...props }) => createElement(MarkdownLink, { ...props } as any),
|
||||
img: ({ node, ...props }) => {
|
||||
const { src } = props
|
||||
const isVideo = src?.endsWith(".mp4")
|
||||
if (isVideo) {
|
||||
return createElement(VideoPlayer, {
|
||||
src: src as string,
|
||||
})
|
||||
}
|
||||
return createElement("img", { ...props } as any)
|
||||
},
|
||||
callouts: {
|
||||
note: {
|
||||
title: "Note",
|
||||
hint: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M12 8h.01M12 12v4"/><circle cx="12" cy="12" r="10"/></svg>`,
|
||||
},
|
||||
commend: {
|
||||
title: "Success",
|
||||
hint: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="m8 12 2.7 2.7L16 9.3"/><circle cx="12" cy="12" r="10"/></svg>`,
|
||||
},
|
||||
warn: {
|
||||
title: "Warning",
|
||||
hint: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M12 9v4m0 4h.01M8.681 4.082C9.351 2.797 10.621 2 12 2s2.649.797 3.319 2.082l6.203 11.904a4.28 4.28 0 0 1-.046 4.019C20.793 21.241 19.549 22 18.203 22H5.797c-1.346 0-2.59-.759-3.273-1.995a4.28 4.28 0 0 1-.046-4.019L8.681 4.082Z"/></svg>`,
|
||||
},
|
||||
deter: {
|
||||
title: "Danger",
|
||||
hint: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M12 12s-5.6 4.6-3.6 8c1.6 2.6 5.7 2.7 7.2 0 2-3.7-3.6-8-3.6-8Z"/><path d="M13.004 2 8.5 9 6.001 6s-4.268 7.206-1.629 11.8c3.016 5.5 11.964 5.7 15.08 0C23.876 10 13.004 2 13.004 2Z"/></svg>`,
|
||||
},
|
||||
assert: {
|
||||
title: "Info",
|
||||
hint: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M12.5 7.5h.01m-.01 4v4m-7.926.685L2 21l6.136-1.949c1.307.606 2.791.949 4.364.949 5.243 0 9.5-3.809 9.5-8.5S17.743 3 12.5 3 3 6.809 3 11.5c0 1.731.579 3.341 1.574 4.685"/></svg>`,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
.use(remarkRehype, { allowDangerousHtml: true })
|
||||
.use(rehypeStringify, { allowDangerousHtml: true })
|
||||
|
||||
const tree = pipeline.parse(content)
|
||||
|
||||
const hastTree = pipeline.runSync(tree, file)
|
||||
|
||||
return {
|
||||
content: toJsxRuntime(hastTree, {
|
||||
Fragment,
|
||||
ignoreInvalidStyle: true,
|
||||
jsx: (type, props, key) => jsx(type as any, props, key),
|
||||
jsxs: (type, props, key) => jsxs(type as any, props, key),
|
||||
passNode: true,
|
||||
components: {
|
||||
a: ({ node, ...props }) => createElement(MarkdownLink, { ...props } as any),
|
||||
img: ({ node, ...props }) => {
|
||||
const { src } = props
|
||||
const isVideo = src?.endsWith(".mp4")
|
||||
if (isVideo) {
|
||||
return createElement(VideoPlayer, {
|
||||
src: src as string,
|
||||
})
|
||||
}
|
||||
return createElement("img", { ...props } as any)
|
||||
},
|
||||
...components,
|
||||
},
|
||||
}),
|
||||
}
|
||||
...options?.components,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export { type RemarkOptions } from "@follow/components/utils/parse-markdown.js"
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@
|
|||
"update:main-hash": "tsx plugins/vite/generate-main-hash.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/generator": "7.26.3",
|
||||
"@clack/prompts": "0.8.2",
|
||||
"@egoist/tailwindcss-icons": "1.8.1",
|
||||
"@electron-forge/cli": "7.6.0",
|
||||
|
|
@ -57,14 +56,11 @@
|
|||
"@electron-forge/publisher-github": "7.6.0",
|
||||
"@electron-toolkit/tsconfig": "^1.0.1",
|
||||
"@eslint/compat": "^1.2.4",
|
||||
"@fontsource/sn-pro": "5.1.0",
|
||||
"@hono/node-server": "1.13.7",
|
||||
"@hookform/resolvers": "3.9.1",
|
||||
"@iconify-json/logos": "1.2.3",
|
||||
"@iconify-json/mingcute": "1.2.1",
|
||||
"@iconify-json/simple-icons": "^1.2.15",
|
||||
"@iconify/tools": "4.0.7",
|
||||
"@microflash/remark-callout-directives": "4.3.2",
|
||||
"@mozilla/readability": "^0.5.0",
|
||||
"@pengx17/electron-forge-maker-appimage": "1.2.1",
|
||||
"@sentry/vite-plugin": "2.22.7",
|
||||
|
|
@ -141,7 +137,8 @@
|
|||
"workbox-precaching": "patches/workbox-precaching.patch",
|
||||
"@tanstack/react-virtual": "patches/@tanstack__react-virtual.patch",
|
||||
"@pengx17/electron-forge-maker-appimage": "patches/@pengx17__electron-forge-maker-appimage.patch",
|
||||
"devlop": "patches/devlop.patch"
|
||||
"devlop": "patches/devlop.patch",
|
||||
"@microflash/remark-callout-directives": "patches/@microflash__remark-callout-directives.patch"
|
||||
},
|
||||
"overrides": {
|
||||
"@types/react": "npm:@types/react@^18.3.12",
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
"@follow/types": "workspace:*",
|
||||
"@follow/utils": "workspace:*",
|
||||
"@headlessui/react": "2.2.0",
|
||||
"@microflash/remark-callout-directives": "4.3.2",
|
||||
"@radix-ui/react-avatar": "1.1.1",
|
||||
"@radix-ui/react-checkbox": "1.1.2",
|
||||
"@radix-ui/react-context-menu": "2.2.2",
|
||||
|
|
@ -65,6 +66,10 @@
|
|||
"rehype-parse": "9.0.1",
|
||||
"rehype-sanitize": "6.0.0",
|
||||
"rehype-stringify": "10.0.1",
|
||||
"remark-directive": "3.0.0",
|
||||
"remark-gfm": "4.0.0",
|
||||
"remark-gh-alerts": "0.0.3",
|
||||
"remark-rehype": "11.1.1",
|
||||
"sonner": "1.7.1",
|
||||
"unified": "11.0.5",
|
||||
"unist-util-visit": "5.0.0",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
import "@microflash/remark-callout-directives/themes/github/index.css"
|
||||
import "remark-gh-alerts/styles/github-colors-light.css"
|
||||
import "remark-gh-alerts/styles/github-colors-dark-media.css"
|
||||
import "remark-gh-alerts/styles/github-base.css"
|
||||
|
||||
import remarkCalloutDirectives from "@microflash/remark-callout-directives"
|
||||
import type { Components } from "hast-util-to-jsx-runtime"
|
||||
import { toJsxRuntime } from "hast-util-to-jsx-runtime"
|
||||
import { Fragment, jsx, jsxs } from "react/jsx-runtime"
|
||||
import rehypeStringify from "rehype-stringify"
|
||||
import remarkDirective from "remark-directive"
|
||||
import remarkGfm from "remark-gfm"
|
||||
import remarkGithubAlerts from "remark-gh-alerts"
|
||||
import remarkParse from "remark-parse"
|
||||
import remarkRehype from "remark-rehype"
|
||||
import { unified } from "unified"
|
||||
import { VFile } from "vfile"
|
||||
|
||||
export interface RemarkOptions {
|
||||
components: Partial<Components>
|
||||
}
|
||||
export const parseMarkdown = (content: string, options?: Partial<RemarkOptions>) => {
|
||||
const file = new VFile(content)
|
||||
const { components } = options || {}
|
||||
|
||||
const pipeline = unified()
|
||||
.use(remarkParse)
|
||||
|
||||
.use(remarkGfm)
|
||||
.use(remarkGithubAlerts)
|
||||
|
||||
.use(remarkDirective)
|
||||
.use(remarkCalloutDirectives, {
|
||||
aliases: {
|
||||
danger: "deter",
|
||||
tip: "note",
|
||||
},
|
||||
callouts: {
|
||||
note: {
|
||||
title: "Note",
|
||||
hint: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M12 8h.01M12 12v4"/><circle cx="12" cy="12" r="10"/></svg>`,
|
||||
},
|
||||
commend: {
|
||||
title: "Success",
|
||||
hint: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="m8 12 2.7 2.7L16 9.3"/><circle cx="12" cy="12" r="10"/></svg>`,
|
||||
},
|
||||
warn: {
|
||||
title: "Warning",
|
||||
hint: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M12 9v4m0 4h.01M8.681 4.082C9.351 2.797 10.621 2 12 2s2.649.797 3.319 2.082l6.203 11.904a4.28 4.28 0 0 1-.046 4.019C20.793 21.241 19.549 22 18.203 22H5.797c-1.346 0-2.59-.759-3.273-1.995a4.28 4.28 0 0 1-.046-4.019L8.681 4.082Z"/></svg>`,
|
||||
},
|
||||
deter: {
|
||||
title: "Danger",
|
||||
hint: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M12 12s-5.6 4.6-3.6 8c1.6 2.6 5.7 2.7 7.2 0 2-3.7-3.6-8-3.6-8Z"/><path d="M13.004 2 8.5 9 6.001 6s-4.268 7.206-1.629 11.8c3.016 5.5 11.964 5.7 15.08 0C23.876 10 13.004 2 13.004 2Z"/></svg>`,
|
||||
},
|
||||
assert: {
|
||||
title: "Info",
|
||||
hint: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M12.5 7.5h.01m-.01 4v4m-7.926.685L2 21l6.136-1.949c1.307.606 2.791.949 4.364.949 5.243 0 9.5-3.809 9.5-8.5S17.743 3 12.5 3 3 6.809 3 11.5c0 1.731.579 3.341 1.574 4.685"/></svg>`,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
.use(remarkRehype, { allowDangerousHtml: true })
|
||||
.use(rehypeStringify, { allowDangerousHtml: true })
|
||||
|
||||
const tree = pipeline.parse(content)
|
||||
|
||||
const hastTree = pipeline.runSync(tree, file)
|
||||
|
||||
return {
|
||||
content: toJsxRuntime(hastTree, {
|
||||
Fragment,
|
||||
ignoreInvalidStyle: true,
|
||||
jsx: (type, props, key) => jsx(type as any, props, key),
|
||||
jsxs: (type, props, key) => jsxs(type as any, props, key),
|
||||
passNode: true,
|
||||
components,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
diff --git a/package.json b/package.json
|
||||
index de27f20188873e2f432fff3aba04bbe5a0a33389..d4926c3f2eb58a65d04354665db3b30c940dd3e5 100644
|
||||
--- a/package.json
|
||||
+++ b/package.json
|
||||
@@ -28,6 +28,7 @@
|
||||
"./config/github": "./themes/github/index.js",
|
||||
"./config/vitepress": "./themes/vitepress/index.js",
|
||||
"./theme/github": "./themes/github/index.css",
|
||||
+ "./themes/*": "./themes/*",
|
||||
"./theme/microflash": "./themes/microflash/index.css",
|
||||
"./theme/vitepress": "./themes/vitepress/index.css"
|
||||
},
|
||||
|
|
@ -13,6 +13,9 @@ overrides:
|
|||
vfile: 5.3.7
|
||||
|
||||
patchedDependencies:
|
||||
'@microflash/remark-callout-directives':
|
||||
hash: bl6uhe4vs4xm3d2jmecdpzbh2m
|
||||
path: patches/@microflash__remark-callout-directives.patch
|
||||
'@mozilla/readability':
|
||||
hash: 43niildbdafdxi7qfcwhpkkxwa
|
||||
path: patches/@mozilla__readability.patch
|
||||
|
|
@ -107,7 +110,7 @@ importers:
|
|||
version: 4.0.7
|
||||
'@microflash/remark-callout-directives':
|
||||
specifier: 4.3.2
|
||||
version: 4.3.2
|
||||
version: 4.3.2(patch_hash=bl6uhe4vs4xm3d2jmecdpzbh2m)
|
||||
'@mozilla/readability':
|
||||
specifier: ^0.5.0
|
||||
version: 0.5.0(patch_hash=43niildbdafdxi7qfcwhpkkxwa)
|
||||
|
|
@ -658,7 +661,7 @@ importers:
|
|||
version: 0.11.0(react@18.3.1)
|
||||
'@microflash/remark-callout-directives':
|
||||
specifier: 4.3.2
|
||||
version: 4.3.2
|
||||
version: 4.3.2(patch_hash=bl6uhe4vs4xm3d2jmecdpzbh2m)
|
||||
'@openpanel/web':
|
||||
specifier: 1.0.1
|
||||
version: 1.0.1
|
||||
|
|
@ -1195,6 +1198,9 @@ importers:
|
|||
'@headlessui/react':
|
||||
specifier: 2.2.0
|
||||
version: 2.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@microflash/remark-callout-directives':
|
||||
specifier: 4.3.2
|
||||
version: 4.3.2(patch_hash=bl6uhe4vs4xm3d2jmecdpzbh2m)
|
||||
'@radix-ui/react-avatar':
|
||||
specifier: 1.1.1
|
||||
version: 1.1.1(@types/react-dom@18.3.2)(@types/react@18.3.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
|
|
@ -1312,6 +1318,18 @@ importers:
|
|||
rehype-stringify:
|
||||
specifier: 10.0.1
|
||||
version: 10.0.1
|
||||
remark-directive:
|
||||
specifier: 3.0.0
|
||||
version: 3.0.0
|
||||
remark-gfm:
|
||||
specifier: 4.0.0
|
||||
version: 4.0.0
|
||||
remark-gh-alerts:
|
||||
specifier: 0.0.3
|
||||
version: 0.0.3(@types/mdast@4.0.4)(unified@11.0.5)
|
||||
remark-rehype:
|
||||
specifier: 11.1.1
|
||||
version: 11.1.1
|
||||
sonner:
|
||||
specifier: 1.7.1
|
||||
version: 1.7.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
|
|
@ -18150,7 +18168,7 @@ snapshots:
|
|||
- encoding
|
||||
- supports-color
|
||||
|
||||
'@microflash/remark-callout-directives@4.3.2':
|
||||
'@microflash/remark-callout-directives@4.3.2(patch_hash=bl6uhe4vs4xm3d2jmecdpzbh2m)':
|
||||
dependencies:
|
||||
defu: 6.1.4
|
||||
hastscript: 9.0.0
|
||||
|
|
|
|||
Loading…
Reference in New Issue