mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-26 10:55:57 +00:00
Local MD: implement content source
This commit is contained in:
@@ -14,6 +14,11 @@
|
||||
"dist"
|
||||
],
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./dist/index.js",
|
||||
"./dev": "./dist/dev/index.js",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
@@ -27,14 +32,21 @@
|
||||
"dependencies": {
|
||||
"chokidar": "^5.0.0",
|
||||
"eval-estree-expression": "^3.0.1",
|
||||
"ignore": "^7.0.5",
|
||||
"tinyglobby": "^0.2.16"
|
||||
"hast-util-to-jsx-runtime": "^2.3.6",
|
||||
"remark": "^15.0.1",
|
||||
"remark-rehype": "^11.1.2",
|
||||
"tinyglobby": "^0.2.16",
|
||||
"unified": "^11.0.5",
|
||||
"vfile": "^6.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/hast": "^3.0.4",
|
||||
"@types/mdx": "^2.0.13",
|
||||
"@types/node": "25.6.0",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/unist": "^3.0.3",
|
||||
"fumadocs-core": "workspace:*",
|
||||
"ignore": "^7.0.5",
|
||||
"react": "^19.2.5",
|
||||
"tsconfig": "workspace:*",
|
||||
"tsdown": "0.21.8"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './watcher';
|
||||
@@ -2,7 +2,7 @@ import { type ChokidarOptions, type Matcher, watch } from 'chokidar';
|
||||
import ignore, { type Ignore } from 'ignore';
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { LocalMarkdownConfig } from '.';
|
||||
import type { LocalMarkdownConfig } from '..';
|
||||
|
||||
export async function startWatcher(config: LocalMarkdownConfig) {
|
||||
// init .gitignore
|
||||
@@ -24,3 +24,6 @@ export interface LocalMarkdownConfig {
|
||||
export async function localMd(config: LocalMarkdownConfig) {
|
||||
return source(await getPages(config));
|
||||
}
|
||||
|
||||
export type { Meta, Page } from './storage';
|
||||
export * from './md';
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import { remark } from 'remark';
|
||||
import remarkRehype, { type Options as RemarkRehypeOptions } from 'remark-rehype';
|
||||
import { type Compatible, VFile } from 'vfile';
|
||||
import {
|
||||
type Components,
|
||||
type Options as RenderOptions,
|
||||
toJsxRuntime,
|
||||
} from 'hast-util-to-jsx-runtime';
|
||||
import type { Root } from 'hast';
|
||||
import * as JsxRuntime from 'react/jsx-runtime';
|
||||
import type { Pluggable, PluggableList, Plugin } from 'unified';
|
||||
import type { ReactNode } from 'react';
|
||||
// @ts-expect-error -- untyped
|
||||
import { evaluate } from 'eval-estree-expression';
|
||||
|
||||
export interface MarkdownCompilerOptions {
|
||||
remarkPlugins?: PluggableList;
|
||||
rehypePlugins?: PluggableList;
|
||||
remarkRehypeOptions?: RemarkRehypeOptions;
|
||||
}
|
||||
|
||||
export interface CompileResult {
|
||||
tree: Root;
|
||||
file: VFile;
|
||||
render: (components?: Components) => ReactNode;
|
||||
}
|
||||
|
||||
export interface MarkdownCompiler {
|
||||
compile: (input: Compatible) => Promise<CompileResult>;
|
||||
|
||||
render: (tree: Root, file: VFile, components?: Components) => ReactNode;
|
||||
}
|
||||
|
||||
export function createMarkdownCompiler({
|
||||
rehypePlugins = [],
|
||||
remarkPlugins = [],
|
||||
remarkRehypeOptions,
|
||||
}: MarkdownCompilerOptions = {}): MarkdownCompiler {
|
||||
const processor = remark()
|
||||
.use(remarkPlugins)
|
||||
.use(remarkRehype, remarkRehypeOptions)
|
||||
.use(rehypePlugins);
|
||||
|
||||
function renderOptions(override?: Partial<RenderOptions>): RenderOptions {
|
||||
return {
|
||||
development: false,
|
||||
createEvaluater() {
|
||||
return {
|
||||
evaluateProgram(program) {
|
||||
return evaluate.sync(program, { ...override?.components }, { functions: true });
|
||||
},
|
||||
evaluateExpression(node) {
|
||||
return evaluate.sync(node, { ...override?.components }, { functions: true });
|
||||
},
|
||||
};
|
||||
},
|
||||
...JsxRuntime,
|
||||
...override,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
async compile(input) {
|
||||
const file = new VFile(input);
|
||||
const tree = await processor.run(processor.parse(file), file);
|
||||
return {
|
||||
tree,
|
||||
file,
|
||||
render(components) {
|
||||
return toJsxRuntime(
|
||||
tree,
|
||||
renderOptions({
|
||||
filePath: file.path,
|
||||
components,
|
||||
}),
|
||||
);
|
||||
},
|
||||
};
|
||||
},
|
||||
render(tree, file, components) {
|
||||
return toJsxRuntime(
|
||||
tree,
|
||||
renderOptions({
|
||||
filePath: file.path,
|
||||
components,
|
||||
}),
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function plugin<
|
||||
PluginParameters extends unknown[],
|
||||
Input extends string | import('unist').Node | undefined,
|
||||
Output,
|
||||
>(plugin: Plugin<PluginParameters, Input, Output>, ...params: NoInfer<PluginParameters>) {
|
||||
return [plugin, ...params] as Pluggable;
|
||||
}
|
||||
@@ -5,20 +5,15 @@ import { metaSchema, pageSchema } from 'fumadocs-core/source/schema';
|
||||
import { LocalMarkdownConfig } from '.';
|
||||
import { frontmatter as parseFrontmatter } from 'fumadocs-core/content/md/frontmatter';
|
||||
|
||||
export interface RawPage {
|
||||
export interface Page {
|
||||
type: 'page';
|
||||
path: string;
|
||||
absolutePath: string;
|
||||
|
||||
data: {
|
||||
title: string;
|
||||
description?: string;
|
||||
content: string;
|
||||
frontmatter: Record<string, unknown>;
|
||||
};
|
||||
data: PageData;
|
||||
}
|
||||
|
||||
export interface RawMeta {
|
||||
export interface Meta {
|
||||
type: 'meta';
|
||||
path: string;
|
||||
absolutePath: string;
|
||||
@@ -29,10 +24,17 @@ export interface RawMeta {
|
||||
} & Record<string, unknown>;
|
||||
}
|
||||
|
||||
type BuildFileOutput = RawPage | RawMeta | undefined;
|
||||
export interface PageData {
|
||||
title: string;
|
||||
description?: string;
|
||||
content: string;
|
||||
frontmatter: Record<string, unknown>;
|
||||
}
|
||||
|
||||
type BuildFileOutput = Page | Meta | undefined;
|
||||
|
||||
const CHUNK_SIZE = 100;
|
||||
export const filesCache = new Map<string, RawPage | RawMeta>();
|
||||
export const filesCache = new Map<string, Page | Meta>();
|
||||
|
||||
async function buildFile(config: LocalMarkdownConfig, file: string): Promise<BuildFileOutput> {
|
||||
const absolutePath = path.resolve(config.dir, file);
|
||||
@@ -80,8 +82,8 @@ export async function getPages(config: LocalMarkdownConfig) {
|
||||
chunks.push(Promise.all(promises));
|
||||
}
|
||||
|
||||
const pages: RawPage[] = [];
|
||||
const metas: RawMeta[] = [];
|
||||
const pages: Page[] = [];
|
||||
const metas: Meta[] = [];
|
||||
for await (const chunk of chunks) {
|
||||
for (const item of chunk) {
|
||||
if (!item) continue;
|
||||
@@ -92,7 +94,7 @@ export async function getPages(config: LocalMarkdownConfig) {
|
||||
return { pages, metas };
|
||||
}
|
||||
|
||||
async function md(absolutePath: string, file: string): Promise<RawPage> {
|
||||
async function md(absolutePath: string, file: string): Promise<Page> {
|
||||
const content = await fs.readFile(absolutePath, 'utf-8');
|
||||
const parsed = parseFrontmatter(content);
|
||||
|
||||
@@ -112,7 +114,7 @@ async function md(absolutePath: string, file: string): Promise<RawPage> {
|
||||
};
|
||||
}
|
||||
|
||||
async function json(absolutePath: string, file: string): Promise<RawMeta | undefined> {
|
||||
async function json(absolutePath: string, file: string): Promise<Meta | undefined> {
|
||||
const content = await fs.readFile(absolutePath, 'utf-8');
|
||||
const parsed = JSON.parse(content);
|
||||
const result = metaSchema.loose().safeParse(parsed);
|
||||
|
||||
@@ -4,10 +4,11 @@ export default defineConfig({
|
||||
dts: true,
|
||||
fixedExtension: false,
|
||||
target: 'es2023',
|
||||
entry: ['./src/index.ts', './src/client/index.ts'],
|
||||
entry: ['./src/index.ts', './src/dev/index.ts'],
|
||||
format: 'esm',
|
||||
exports: true,
|
||||
deps: {
|
||||
onlyBundle: [],
|
||||
onlyBundle: ['ignore'],
|
||||
neverBundle: ['mdx/types'],
|
||||
},
|
||||
});
|
||||
|
||||
Generated
+24
-3
@@ -2186,13 +2186,28 @@ importers:
|
||||
eval-estree-expression:
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.1
|
||||
ignore:
|
||||
specifier: ^7.0.5
|
||||
version: 7.0.5
|
||||
hast-util-to-jsx-runtime:
|
||||
specifier: ^2.3.6
|
||||
version: 2.3.6
|
||||
remark:
|
||||
specifier: ^15.0.1
|
||||
version: 15.0.1
|
||||
remark-rehype:
|
||||
specifier: ^11.1.2
|
||||
version: 11.1.2
|
||||
tinyglobby:
|
||||
specifier: ^0.2.16
|
||||
version: 0.2.16
|
||||
unified:
|
||||
specifier: ^11.0.5
|
||||
version: 11.0.5
|
||||
vfile:
|
||||
specifier: ^6.0.3
|
||||
version: 6.0.3
|
||||
devDependencies:
|
||||
'@types/hast':
|
||||
specifier: ^3.0.4
|
||||
version: 3.0.4
|
||||
'@types/mdx':
|
||||
specifier: ^2.0.13
|
||||
version: 2.0.13
|
||||
@@ -2202,9 +2217,15 @@ importers:
|
||||
'@types/react':
|
||||
specifier: ^19.2.14
|
||||
version: 19.2.14
|
||||
'@types/unist':
|
||||
specifier: ^3.0.3
|
||||
version: 3.0.3
|
||||
fumadocs-core:
|
||||
specifier: workspace:*
|
||||
version: link:../core
|
||||
ignore:
|
||||
specifier: ^7.0.5
|
||||
version: 7.0.5
|
||||
react:
|
||||
specifier: ^19.2.5
|
||||
version: 19.2.5
|
||||
|
||||
Reference in New Issue
Block a user