mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-26 23:49:51 +00:00
Support include in lint
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'fumadocs-core': patch
|
||||
---
|
||||
|
||||
Support remark plugins & vfile input on `getTableOfContents`
|
||||
+37
-15
@@ -5,6 +5,7 @@ import { getTableOfContents } from 'fumadocs-core/server';
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import matter from 'gray-matter';
|
||||
import { remarkInclude } from 'fumadocs-mdx/config';
|
||||
|
||||
async function readFromPath(file: string) {
|
||||
const content = await fs
|
||||
@@ -28,23 +29,44 @@ async function checkLinks() {
|
||||
await fg('content/blog/**/*.mdx').then((files) => files.map(readFromPath)),
|
||||
);
|
||||
|
||||
const docs = docsFiles.map(async (file) => {
|
||||
const info = parseFilePath(path.relative('content/docs', file.path));
|
||||
|
||||
return {
|
||||
value: getSlugs(info),
|
||||
hashes: (
|
||||
await getTableOfContents(
|
||||
{
|
||||
path: file.path,
|
||||
value: file.content,
|
||||
},
|
||||
[remarkInclude],
|
||||
)
|
||||
).map((item) => item.url.slice(1)),
|
||||
};
|
||||
});
|
||||
|
||||
const blogs = blogFiles.map(async (file) => {
|
||||
const info = parseFilePath(path.relative('content/blog', file.path));
|
||||
|
||||
return {
|
||||
value: getSlugs(info)[0],
|
||||
hashes: (
|
||||
await getTableOfContents(
|
||||
{
|
||||
path: file.path,
|
||||
value: file.content,
|
||||
},
|
||||
[remarkInclude],
|
||||
)
|
||||
).map((item) => item.url.slice(1)),
|
||||
};
|
||||
});
|
||||
|
||||
const scanned = await scanURLs({
|
||||
populate: {
|
||||
'(home)/blog/[slug]': blogFiles.map((file) => {
|
||||
const info = parseFilePath(path.relative('content/blog', file.path));
|
||||
|
||||
return { value: getSlugs(info)[0] };
|
||||
}),
|
||||
'docs/[...slug]': docsFiles.map((file) => {
|
||||
const info = parseFilePath(path.relative('content/docs', file.path));
|
||||
|
||||
return {
|
||||
value: getSlugs(info),
|
||||
hashes: file.data?._mdx?.mirror
|
||||
? undefined
|
||||
: getTableOfContents(file.content).map((item) => item.url.slice(1)),
|
||||
};
|
||||
}),
|
||||
'(home)/blog/[slug]': await Promise.all(blogs),
|
||||
'docs/[...slug]': await Promise.all(docs),
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -116,7 +116,8 @@
|
||||
"shiki-transformers": "^1.0.1",
|
||||
"tsconfig": "workspace:*",
|
||||
"typescript": "^5.7.2",
|
||||
"unified": "^11.0.5"
|
||||
"unified": "^11.0.5",
|
||||
"vfile": "^6.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@oramacloud/client": "1.x.x || 2.x.x",
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { remark } from 'remark';
|
||||
import type { ReactNode } from 'react';
|
||||
import { remarkHeading } from '@/mdx-plugins/remark-heading';
|
||||
import type { PluggableList } from 'unified';
|
||||
import type { Compatible } from 'vfile';
|
||||
|
||||
export interface TOCItemType {
|
||||
title: ReactNode;
|
||||
@@ -13,9 +15,38 @@ export type TableOfContents = TOCItemType[];
|
||||
/**
|
||||
* Get Table of Contents from markdown/mdx document (using remark)
|
||||
*
|
||||
* @param content - Markdown content
|
||||
* @param content - Markdown content or file
|
||||
*/
|
||||
export function getTableOfContents(content: string): TableOfContents {
|
||||
export function getTableOfContents(content: Compatible): TableOfContents;
|
||||
|
||||
/**
|
||||
* Get Table of Contents from markdown/mdx document (using remark)
|
||||
*
|
||||
* @param content - Markdown content or file
|
||||
* @param remarkPlugins - remark plugins to be applied first
|
||||
*/
|
||||
export function getTableOfContents(
|
||||
content: Compatible,
|
||||
remarkPlugins: PluggableList,
|
||||
): Promise<TableOfContents>;
|
||||
|
||||
export function getTableOfContents(
|
||||
content: Compatible,
|
||||
remarkPlugins?: PluggableList,
|
||||
): TableOfContents | Promise<TableOfContents> {
|
||||
if (remarkPlugins) {
|
||||
return remark()
|
||||
.use(remarkPlugins)
|
||||
.use(remarkHeading)
|
||||
.process(content)
|
||||
.then((result) => {
|
||||
if ('toc' in result.data) return result.data.toc as TableOfContents;
|
||||
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
// compatible with previous versions
|
||||
const result = remark().use(remarkHeading).processSync(content);
|
||||
|
||||
if ('toc' in result.data) return result.data.toc as TableOfContents;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createHash } from 'node:crypto';
|
||||
import fs from 'node:fs';
|
||||
import * as fs from 'node:fs';
|
||||
import { loadConfig, type LoadedConfig } from '@/config/load';
|
||||
|
||||
const cache = new Map<
|
||||
|
||||
@@ -2,3 +2,4 @@ export * from './types';
|
||||
export * from './define';
|
||||
export * from '../utils/mdx-options';
|
||||
export { frontmatterSchema, metaSchema } from '../utils/schema';
|
||||
export * from '../mdx-plugins/remark-include';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import path from 'node:path';
|
||||
import fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import * as fs from 'node:fs';
|
||||
import { readFile, writeFile } from 'node:fs/promises';
|
||||
import grayMatter from 'gray-matter';
|
||||
import { getConfigHash, loadConfigCached } from '@/config/cached';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import { type LoadedConfig } from '@/config/load';
|
||||
import type { MetaFile } from '@/loader-mdx';
|
||||
import { getTypeFromPath } from '@/utils/get-type-from-path';
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import type { Processor, Transformer } from 'unified';
|
||||
import { visit } from 'unist-util-visit';
|
||||
import type { Literal, Root } from 'mdast';
|
||||
import * as path from 'node:path';
|
||||
import * as fs from 'node:fs/promises';
|
||||
import matter from 'gray-matter';
|
||||
|
||||
export function remarkInclude(this: Processor): Transformer<Root, Root> {
|
||||
return async (tree, file) => {
|
||||
const queue: Promise<void>[] = [];
|
||||
|
||||
visit(tree, ['mdxJsxFlowElement', 'paragraph'] as const, (node) => {
|
||||
let specifier: string | undefined;
|
||||
|
||||
// without MDX, parse from paragraph nodes
|
||||
if (node.type === 'paragraph' && node.children.length === 3) {
|
||||
const [open, content, closure] = node.children;
|
||||
|
||||
if (
|
||||
open.type === 'html' &&
|
||||
open.value === '<include>' &&
|
||||
content.type === 'text' &&
|
||||
closure.type === 'html' &&
|
||||
closure.value === '</include>'
|
||||
) {
|
||||
specifier = content.value.trim();
|
||||
}
|
||||
}
|
||||
|
||||
if (node.type === 'mdxJsxFlowElement' && node.name === 'include') {
|
||||
const child = node.children.at(0) as Literal | undefined;
|
||||
|
||||
if (!child || child.type !== 'text') return;
|
||||
specifier = child.value;
|
||||
}
|
||||
|
||||
if (!specifier) return 'skip';
|
||||
const targetPath = path.resolve(path.dirname(file.path), specifier);
|
||||
|
||||
queue.push(
|
||||
fs.readFile(targetPath).then((content) => {
|
||||
const parsed = this.parse(matter(content).content);
|
||||
|
||||
if (file.data._compiler) {
|
||||
file.data._compiler.addDependency(targetPath);
|
||||
}
|
||||
Object.assign(node, parsed);
|
||||
}),
|
||||
);
|
||||
|
||||
return 'skip';
|
||||
});
|
||||
|
||||
await Promise.all(queue);
|
||||
};
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import path from 'node:path';
|
||||
import fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import * as fs from 'node:fs';
|
||||
import { findConfigFile, loadConfig } from '@/config/load';
|
||||
import { generateTypes } from '@/map/generate';
|
||||
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
import { createProcessor, type ProcessorOptions } from '@mdx-js/mdx';
|
||||
import type { VFile } from 'vfile';
|
||||
import type { Transformer } from 'unified';
|
||||
import { visit } from 'unist-util-visit';
|
||||
import type { MdxJsxFlowElement } from 'mdast-util-mdx-jsx';
|
||||
import type { Literal } from 'mdast';
|
||||
import * as fs from 'node:fs/promises';
|
||||
import * as path from 'node:path';
|
||||
import matter from 'gray-matter';
|
||||
import { remarkInclude } from '@/mdx-plugins/remark-include';
|
||||
|
||||
type Processor = ReturnType<typeof createProcessor>;
|
||||
|
||||
@@ -50,38 +44,6 @@ declare module 'vfile' {
|
||||
}
|
||||
}
|
||||
|
||||
function remarkInclude(this: Processor): Transformer {
|
||||
return async (tree, file) => {
|
||||
const queue: Promise<void>[] = [];
|
||||
|
||||
visit(tree, 'mdxJsxFlowElement', (node: MdxJsxFlowElement) => {
|
||||
if (node.name === 'include') {
|
||||
const child = node.children.at(0) as Literal | undefined;
|
||||
|
||||
if (!child || child.type !== 'text') return;
|
||||
const specifier = child.value;
|
||||
|
||||
const targetPath = path.resolve(path.dirname(file.path), specifier);
|
||||
|
||||
queue.push(
|
||||
fs.readFile(targetPath).then((content) => {
|
||||
const parsed = this.parse(matter(content).content);
|
||||
|
||||
if (file.data._compiler) {
|
||||
file.data._compiler.addDependency(targetPath);
|
||||
}
|
||||
Object.assign(node, parsed);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return 'skip';
|
||||
});
|
||||
|
||||
await Promise.all(queue);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param group - The cache group of MDX content, usually the collection name
|
||||
* @param configHash - config hash
|
||||
@@ -111,11 +73,7 @@ export function buildMDX(
|
||||
outputFormat: 'program',
|
||||
development: process.env.NODE_ENV === 'development',
|
||||
...rest,
|
||||
remarkPlugins: [
|
||||
// @ts-expect-error -- processor
|
||||
remarkInclude,
|
||||
...(rest.remarkPlugins ?? []),
|
||||
],
|
||||
remarkPlugins: [remarkInclude, ...(rest.remarkPlugins ?? [])],
|
||||
format,
|
||||
}),
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ const neutral: Preset = {
|
||||
foreground: '0 0% 3.9%',
|
||||
muted: '0 0% 96.1%',
|
||||
'muted-foreground': '0 0% 45.1%',
|
||||
popover: '0 0% 100%',
|
||||
popover: '0 0% 98%',
|
||||
'popover-foreground': '0 0% 15.1%',
|
||||
card: '0 0% 94.7%',
|
||||
'card-foreground': '0 0% 3.9%',
|
||||
@@ -123,11 +123,6 @@ const neutral: Preset = {
|
||||
ring: '0 0% 14.9%',
|
||||
},
|
||||
css: {
|
||||
'#nd-sidebar': {
|
||||
'--muted': '0deg 0% 89%',
|
||||
'--secondary': '0deg 0% 99%',
|
||||
'--muted-foreground': '0 0% 30%',
|
||||
},
|
||||
'.dark #nd-sidebar': {
|
||||
'--muted': '0deg 0% 16%',
|
||||
'--secondary': '0deg 0% 18%',
|
||||
|
||||
Generated
+3
@@ -622,6 +622,9 @@ importers:
|
||||
unified:
|
||||
specifier: ^11.0.5
|
||||
version: 11.0.5
|
||||
vfile:
|
||||
specifier: ^6.0.3
|
||||
version: 6.0.3
|
||||
|
||||
packages/create-app:
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user