Zeta: Support generating package install commands

This commit is contained in:
SonMooSans
2023-10-24 21:58:15 +08:00
parent c36c395f90
commit 2da93d8839
17 changed files with 142 additions and 32 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'next-docs-zeta': patch
---
Support generating package install codeblocks automatically
+2 -2
View File
@@ -20,8 +20,8 @@ It's recommended to use Next Docs with [Tailwind CSS](https://tailwindcss.com) +
For normal usages, you can use [Next Docs UI](/docs/ui) instead.
</Callout>
```bash
npm install next-docs-zeta shiki
```package-install
next-docs-zeta shiki
```
### Demo
@@ -22,8 +22,8 @@ paragraphs are all indexed as a record, and the attribute for distinct is
1. Install `algoliasearch`
```bash
npm install algoliasearch
```package-install
algoliasearch
```
2. Sign up and obtain the required API keys for your search. Store these
+2 -2
View File
@@ -17,8 +17,8 @@ plugins for building a good documentation site.
Notice that the package doesn't include loader by default.
```bash
npm install next-docs-mdx @next/mdx @mdx-js/loader @mdx-js/react @types/mdx
```package-install
next-docs-mdx @next/mdx @mdx-js/loader @mdx-js/react @types/mdx
```
### Configuration
+2 -2
View File
@@ -10,8 +10,8 @@ detected, it will throw an error immediately.
To customise the schemas, you have to install `zod`.
```bash
npm install zod
```package-install
zod
```
Now you can override or extend the default zod schemas by passing a `validate`
@@ -7,7 +7,7 @@ description: Generate documentation sites with a command
No arguments needed.
<Tabs items={["npm", "pnpm", "yarn"]} defaultValue="npm">
<InstallTabs>
<Tab value="npm">
```bash
@@ -29,7 +29,7 @@ yarn create next-docs-app
```
</Tab>
</Tabs>
</InstallTabs>
### Options
+4 -4
View File
@@ -42,8 +42,8 @@ yarn create next-docs-app
Install required dependencies
```bash
npm install next-docs-ui next-docs-zeta
```package-install
next-docs-ui next-docs-zeta
```
<h3>Configuration</h3>
@@ -52,8 +52,8 @@ Next Docs UI doesn't provide static generation directly. You can use a source
you prefer, such as [Contentlayer](https://www.contentlayer.dev) which is
supported by default. **This guide is written for Contentlayer.**
```bash
npm install contentlayer next-contentlayer
```package-install
contentlayer next-contentlayer
```
1. **Configure Contentlayer**
+2 -2
View File
@@ -10,8 +10,8 @@ import 'katex/dist/katex.min.css'
Next Docs UI doesn't bundle with other plugins, you have to manually install
those plugins.
```bash
npm install remark-math rehype-katex katex
```package-install
remark-math rehype-katex katex
```
### Add Plugins
+14
View File
@@ -5,6 +5,7 @@ import { Callout } from 'next-docs-ui/components/callout'
import { Tab, Tabs } from 'next-docs-ui/components/tabs'
import { TypeTable } from 'next-docs-ui/components/type-table'
import defaultComponents from 'next-docs-ui/mdx-server'
import type { ReactNode } from 'react'
export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
@@ -20,6 +21,19 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
Accordion: (p => <Accordion {...p} />) as typeof Accordion,
Accordions: (p => <Accordions {...p} />) as typeof Accordions,
Wrapper,
InstallTabs: ({
items,
children
}: {
items: string[]
children: ReactNode
}) => {
return (
<Tabs items={items} id="package-manager" persist>
{children}
</Tabs>
)
},
blockquote: props => <Callout className="my-4">{props.children}</Callout>,
...components
}
+6 -2
View File
@@ -1,6 +1,6 @@
import createBundleAnalyzer from '@next/bundle-analyzer'
import createNextDocs from 'next-docs-mdx/config'
import { remarkDynamicContent } from 'next-docs-zeta/mdx-plugins'
import { remarkDynamicContent, remarkInstall } from 'next-docs-zeta/mdx-plugins'
import rehypeKatex from 'rehype-katex'
import remarkMath from 'remark-math'
@@ -20,7 +20,11 @@ const config = {
const withNextDocs = createNextDocs({
mdxOptions: {
remarkPlugins: [remarkMath, remarkDynamicContent],
remarkPlugins: [
remarkMath,
remarkDynamicContent,
[remarkInstall, { Tabs: 'InstallTabs' }]
],
rehypePlugins: [rehypeKatex]
}
})
+1
View File
@@ -13,3 +13,4 @@ export {
} from './mdx-plugins/remark-dynamic-content'
export * from './mdx-plugins/remark-structure'
export { remarkToc } from './mdx-plugins/remark-toc'
export * from './mdx-plugins/remark-install'
@@ -1,8 +1,8 @@
import fs from 'fs'
import path from 'path'
import type { Root } from 'mdast'
import type { Content, Root } from 'mdast'
import type { Transformer } from 'unified'
import { visit } from 'unist-util-visit'
import { visit } from './unist-visit'
const regex = /^\|reference:(.+)\|/
@@ -35,7 +35,7 @@ export function remarkDynamicContent(
} = options
return tree => {
visit(tree, filter, node => {
visit(tree, filter, (node: Content) => {
if (!('value' in node) || typeof node.value !== 'string') return
const result = regex.exec(node.value)
@@ -0,0 +1,78 @@
import type { Code, Root } from 'mdast'
import type { Transformer } from 'unified'
import { visit } from './unist-visit'
type PackageManager = (name: string) => {
packageManager: string
command: string
}
export type RemarkInstallOptions = {
Tabs: string
Tab: string
packageManagers: PackageManager[]
}
export function remarkInstall({
Tab = 'Tab',
Tabs = 'Tabs',
packageManagers = [
name => ({ command: `npm install ${name}`, packageManager: 'npm' }),
name => ({ command: `pnpm add ${name}`, packageManager: 'pnpm' }),
name => ({ command: `yarn add ${name}`, packageManager: 'yarn' })
]
}: Partial<RemarkInstallOptions> = {}): Transformer<Root, Root> {
return tree => {
visit(tree, ['code'], (node: Code) => {
if (node.lang !== 'package-install') return 'skip'
const managers = packageManagers.map(manager => manager(node.value))
const insert = {
type: 'mdxJsxFlowElement',
name: Tabs,
attributes: [
{
type: 'mdxJsxAttribute',
name: 'items',
value: {
type: 'mdxJsxAttributeValueExpression',
data: {
estree: {
body: [
{
type: 'ExpressionStatement',
expression: {
type: 'ArrayExpression',
elements: managers.map(({ packageManager }) => ({
type: 'Literal',
value: packageManager
}))
}
}
]
}
}
}
}
],
children: managers.map(({ command, packageManager }) => ({
type: 'mdxJsxFlowElement',
name: Tab,
attributes: [
{ type: 'mdxJsxAttribute', name: 'value', value: packageManager }
],
children: [
{
type: 'code',
lang: 'bash',
value: command
} satisfies Code
]
}))
}
Object.assign(node, insert)
})
}
}
@@ -1,11 +1,11 @@
import Slugger from 'github-slugger'
import type { Root } from 'mdast'
import type { Content as MdastContent, Root } from 'mdast'
import { remark } from 'remark'
import remarkGfm from 'remark-gfm'
import remarkMdx from 'remark-mdx'
import type { PluggableList, Transformer } from 'unified'
import { visit } from 'unist-util-visit'
import { flattenNode } from './remark-utils'
import { visit } from './unist-visit'
type Heading = {
id: string
@@ -47,9 +47,7 @@ export function remarkStructure({
const data: StructuredData = { contents: [], headings: [] }
let lastHeading: string | undefined = ''
visit(node, types, element => {
if (element.type === 'root') return
visit(node, types, (element: MdastContent) => {
if (element.type === 'heading') {
const heading = flattenNode(element)
const slug = slugger.slug(heading)
@@ -2,8 +2,8 @@ import type { TOCItemType } from '@/server/types'
import Slugger from 'github-slugger'
import type { Heading, Root } from 'mdast'
import type { Transformer } from 'unified'
import { visit } from 'unist-util-visit'
import { flattenNode } from './remark-utils'
import { visit } from './unist-visit'
const slugger = new Slugger()
@@ -15,8 +15,7 @@ export function remarkToc(): Transformer<Root, Root> {
const toc: TOCItemType[] = []
slugger.reset()
visit(node, ['heading'], child => {
const heading = child as Heading
visit(node, ['heading'], (heading: Heading) => {
const text = flattenNode(heading)
toc.push({
@@ -1,13 +1,13 @@
import type { Content, Literal } from 'mdast'
import { visit } from 'unist-util-visit'
import { visit } from './unist-visit'
const textTypes = ['text', 'inlineCode', 'code']
export function flattenNode(node: Content): string {
const p: string[] = []
visit(node, textTypes, child => {
p.push((child as Literal).value)
visit(node, textTypes, (child: Literal) => {
p.push(child.value)
})
return p.join('')
@@ -0,0 +1,11 @@
import { visit as original } from 'unist-util-visit'
type SimpleVisit = {
<R, T>(node: R, callback: (v: T) => void): void | 'skip'
<R, T>(node: R, check: string[], callback: (v: T) => void): void | 'skip'
}
/**
* The default type binding is too complicated, it slows the whole Typescript language server
*/
export const visit: SimpleVisit = original as unknown as SimpleVisit