Skip to content

Commit

Permalink
feat: docs support mdx
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <[email protected]>
  • Loading branch information
Innei committed Mar 15, 2024
1 parent 8c0f6b3 commit 56b013b
Show file tree
Hide file tree
Showing 17 changed files with 638 additions and 90 deletions.
24 changes: 18 additions & 6 deletions demo/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,37 @@
"prettier",
"plugin:tailwindcss/recommended"
],
"plugins": ["tailwindcss"],
"plugins": [
"tailwindcss"
],
"rules": {
"@next/next/no-html-link-for-pages": "off",
"react/jsx-key": "off",
"tailwindcss/no-custom-classname": "off"
"tailwindcss/no-custom-classname": "off",
"@next/next/google-font-display": 0,
"@next/next/google-font-preconnect": 0,
"@next/next/no-page-custom-font": 0
},
"settings": {
"tailwindcss": {
"callees": ["cn"],
"callees": [
"cn"
],
"config": "tailwind.config.js"
},
"next": {
"rootDir": ["./"]
"rootDir": [
"./"
]
}
},
"overrides": [
{
"files": ["*.ts", "*.tsx"],
"files": [
"*.ts",
"*.tsx"
],
"parser": "@typescript-eslint/parser"
}
]
}
}
20 changes: 20 additions & 0 deletions demo/app/(mdx)/highlighter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"use client"

import { useEffect, useLayoutEffect } from "react"
import { Pirata_One } from "next/font/google"
import { usePathname } from "next/navigation"

export const Highlighter = () => {
const pathname = usePathname()
useEffect(() => {
;(window as any).Prism?.highlightAll()
}, [pathname])

useLayoutEffect(() => {
// alway run in development when every re-render
if (process.env.NODE_ENV === "development") {
;(window as any).Prism?.highlightAll()
}
})
return null
}
5 changes: 5 additions & 0 deletions demo/app/(mdx)/install/page.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
order: 1
---

# Quick Start
13 changes: 13 additions & 0 deletions demo/app/(mdx)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* eslint-disable react/display-name */
/* eslint-disable import/no-anonymous-default-export */

import type { PropsWithChildren } from "react"

import { Highlighter } from "./highlighter"

export default ({ children }: PropsWithChildren) => (
<>
<div className="prose dark:prose-invert">{children}</div>
<Highlighter />
</>
)
23 changes: 23 additions & 0 deletions demo/app/hero.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Button, buttonVariants } from "@/components/ui/button"

export const Hero = () => {
return (
<section className="container mt-32 flex flex-col items-center justify-center gap-6 pb-8 pt-6 md:py-10">
<div className="flex flex-col">
<div className="flex max-w-[980px] flex-col items-start gap-2">
<h1 className="text-3xl font-extrabold leading-tight tracking-tighter md:text-4xl">
Beautifully designed React Component{" "}
<br className="hidden sm:inline" />
built with Radix UI and Tailwind CSS.
</h1>
<p className="text-muted-foreground max-w-[700px] text-lg">
A demo page.
</p>
</div>
</div>
<div className="mt-12 flex gap-4">
<Button className={buttonVariants()}>A button</Button>
</div>
</section>
)
}
20 changes: 20 additions & 0 deletions demo/app/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.prose {
@apply !w-full !max-w-full;

h1 {
@apply text-2xl font-bold;
}

h2 {
@apply text-xl font-semibold;
}

h3,
h4 {
@apply text-lg font-medium;
}

code {
@apply !font-mono font-bold;
}
}
74 changes: 63 additions & 11 deletions demo/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import "@/styles/globals.css"
import { Metadata } from "next"
import { clsxm } from "~/lib/helper"

import "./index.css"
import Script from "next/script"

import { siteConfig } from "@/config/site"
import { fontSans } from "@/lib/fonts"
import { cn } from "@/lib/utils"
import { LeftAside } from "@/components/layout/sidebar"
import { SiteHeader } from "@/components/site-header"
import { ThemeProvider } from "@/components/theme-provider"

import { Hero } from "./hero"

export const metadata: Metadata = {
title: {
default: siteConfig.name,
Expand All @@ -32,18 +37,65 @@ export default function RootLayout({ children }: RootLayoutProps) {
return (
<>
<html lang="en" suppressHydrationWarning>
<head />
<body
className={cn(
"bg-background min-h-screen font-sans antialiased",
fontSans.variable
)}
>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
<head>
<meta charSet="UTF-8" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link
rel="preconnect"
href="https://fonts.gstatic.com"
crossOrigin=""
/>
<link
href="https://fonts.googleapis.com/css2?family=Manrope:[email protected]&display=swap"
rel="stylesheet"
/>
<link
rel="stylesheet"
href="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/prism-themes/1.9.0/prism-one-dark.css"
/>
<link
href="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/prism/1.23.0/plugins/line-numbers/prism-line-numbers.min.css"
rel="stylesheet"
/>
<Script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/prism/1.23.0/components/prism-core.min.js" />
<Script
src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/prism/1.23.0/plugins/autoloader/prism-autoloader.min.js"
async
/>
<Script
src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/prism/1.23.0/plugins/line-numbers/prism-line-numbers.min.js"
async
/>
</head>
<body className={"bg-background min-h-screen font-sans antialiased"}>
<ThemeProvider>
<div className="relative flex min-h-screen flex-col">
<SiteHeader />

<div className="flex-1">{children}</div>
<div className="flex-1">
<Hero />
<div className="m-auto mt-24">
<div
className={clsxm(
"relative mx-auto grid min-h-[calc(100vh-3rem-10rem)] max-w-full",
"gap-4 md:grid-cols-1 xl:max-w-[calc(60rem+400px)] xl:grid-cols-[1fr_minmax(auto,60rem)_1fr]",
"mt-12",
"md:mt-6 print:!block print:!max-w-full"
)}
>
<div
className="relative mr-4 hidden min-w-0 xl:block"
data-hide-print
>
<LeftAside />
</div>

<main className="min-w-0 px-4 py-14 lg:px-2">
{children}
</main>
</div>
</div>
</div>
</div>
</ThemeProvider>
</body>
Expand Down
24 changes: 4 additions & 20 deletions demo/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,7 @@
import { Button } from "rc-template"
import { redirect } from "next/navigation"

import { buildFsTree } from "@/lib/fs"

export default function IndexPage() {
return (
<section className="container mt-32 flex flex-col items-center justify-center gap-6 pb-8 pt-6 md:py-10">
<div className="flex flex-col">
<div className="flex max-w-[980px] flex-col items-start gap-2">
<h1 className="text-3xl font-extrabold leading-tight tracking-tighter md:text-4xl">
Beautifully designed React Component{" "}
<br className="hidden sm:inline" />
built with Radix UI and Tailwind CSS.
</h1>
<p className="text-muted-foreground max-w-[700px] text-lg">
A demo template website
</p>
</div>
</div>
<div className="mt-12 flex gap-4">
<Button>Button</Button>
</div>
</section>
)
redirect(buildFsTree().at(0)!.to)
}
27 changes: 27 additions & 0 deletions demo/components/layout/LeftAsideLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"use client"

import { memo } from "react"
import Link from "next/link"
import { usePathname } from "next/navigation"
import { clsxm } from "~/lib/helper"

export const LeftAsideLink = memo(
({ title, path }: { title: string; path: string }) => {
const pathname = usePathname()

return (
<span
className={clsxm(
"font-medium opacity-40 duration-200 hover:opacity-90 text-black dark:text-white",

pathname === path && "opacity-100"
)}
key={path}
>
<Link href={path}>{title}</Link>
</span>
)
}
)

LeftAsideLink.displayName = "LeftAsideLink"
21 changes: 21 additions & 0 deletions demo/components/layout/sidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { buildFsTree } from "@/lib/fs"

import { LeftAsideLink } from "./LeftAsideLink"

export const LeftAside = async ({ asWeight }: { asWeight?: boolean }) => {
const tree = buildFsTree()

return (
<aside className={asWeight ? "" : "sticky top-24 mt-24 min-h-[300px]"}>
<ul>
{tree.map((t) => {
return (
<li key={t.title} className="mt-3 font-semibold">
<LeftAsideLink path={t.to} title={t.title} key={t.title} />
</li>
)
})}
</ul>
</aside>
)
}
43 changes: 43 additions & 0 deletions demo/lib/fs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { readFileSync } from "fs"
import { resolve } from "path"

import { extractFirstHeadingText, parseYamlFrontMatterSync } from "@/lib/remark"

interface Tree {
to: string
title: string
order: number
}

export const buildFsTree = () => {
const filePaths = (
require
// @ts-expect-error
.context("../app/(mdx)/", true, /\.mdx$/)
.keys() as string[]
).filter((path) => path.startsWith("./"))

// [ './guide/install/page.mdx', 'app/(mdx)/guide/install/page.mdx' ]

const tree = [] as Tree[]
for (let filePath of filePaths) {
const path = filePath
.split("/")
.slice(1, -1)
.join("/")
.replace(/\.mdx$/, "")
// const depth = path.split("/").length

const fileContent = readFileSync(
resolve(process.cwd(), `app/(mdx)/${filePath}`),
"utf-8"
)
const yaml = parseYamlFrontMatterSync(fileContent)
const title = extractFirstHeadingText(fileContent)

// console.log({ path: `/${path}`, title, depth })
tree.push({ to: `/${path}`, title: title || "", order: yaml.order })
}

return tree.sort((a, b) => a.order - b.order)
}
48 changes: 48 additions & 0 deletions demo/lib/remark.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import yaml from "js-yaml"
import remarkParse from "remark-parse"
import { unified } from "unified"
import { EXIT, visit } from "unist-util-visit"

// 定义函数以提取第一个一级标题的纯文字内容
export function extractFirstHeadingText(markdown: string): string | null {
const processor = unified().use(remarkParse)
const file = processor.parse(markdown)

let headingText: string | null = null

// 遍历 AST,寻找第一个一级标题
visit(file, "heading", (node) => {
if (node.depth === 1 && headingText === null) {
// 提取纯文字内容
headingText = node.children
.map((child) => ("value" in child ? child.value : ""))
.join("")
return EXIT
}
})

return headingText
}

// 定义函数以同步解析 Markdown 文件顶部的 YAML Front Matter
const yamlFrontMatterRegex = /^---\s*\n([\s\S]+?)\n?---\s*\n?/m
export function parseYamlFrontMatterSync(
markdown: string
): Record<string, any> {
// 尝试查找 Markdown 中的第一个 YAML Front Matter 区块
const match = yamlFrontMatterRegex.exec(markdown)

if (match) {
// 尝试解析找到的 YAML 内容
try {
const yamlContent = yaml.load(match[1].replace(/---/g, "")) as object
// console.log('match', match[1].replace(/---/g, ''), yamlContent)
return yamlContent
} catch (error) {
console.error("YAML parsing error:", error, match)
return {}
}
}

return {}
}
Loading

0 comments on commit 56b013b

Please sign in to comment.