MDX: json-schema support inserting $schema to JSON files

This commit is contained in:
Fuma Nama
2025-10-25 02:01:39 +08:00
parent 56332df0d2
commit 91add4fb95
6 changed files with 94 additions and 2 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'fumadocs-mdx': patch
---
Plugin `json-schema`: support inserting `$schema` to JSON files
+6
View File
@@ -17,6 +17,7 @@ import {
} from 'fumadocs-core/mdx-plugins';
import { remarkAutoTypeTable } from 'fumadocs-typescript';
import type { ElementContent } from 'hast';
import jsonSchema from 'fumadocs-mdx/plugins/json-schema';
export const docs = defineDocs({
docs: {
@@ -50,6 +51,11 @@ export const blog = defineCollections({
export default defineConfig({
lastModifiedTime: 'git',
plugins: [
jsonSchema({
insert: true,
}),
],
mdxOptions: {
rehypeCodeOptions: {
lazy: true,
+2
View File
@@ -90,6 +90,7 @@
"lru-cache": "^11.2.2",
"mdast-util-to-markdown": "^2.1.2",
"picocolors": "^1.1.1",
"picomatch": "^4.0.3",
"remark-mdx": "^3.1.1",
"remark-parse": "^11.0.0",
"tinyexec": "^1.0.1",
@@ -106,6 +107,7 @@
"@types/mdast": "^4.0.4",
"@types/mdx": "^2.0.13",
"@types/node": "^24.9.1",
"@types/picomatch": "^4.0.2",
"@types/react": "^19.2.2",
"eslint-config-custom": "workspace:*",
"fumadocs-core": "workspace:*",
+55 -2
View File
@@ -1,25 +1,78 @@
import type { EmitEntry, Plugin } from '@/plugins';
import type { LoadedConfig } from '@/loaders/config';
import { z } from 'zod';
import { isFileInCollection } from '@/utils/collections';
import fs from 'node:fs/promises';
import path from 'node:path';
export interface JSONSchemaOptions {
/**
* insert `$schema` field to JSON files on creation.
*
* @defaultValue false
*/
insert?: boolean;
}
/**
* Generate JSON schemas locally for collection schemas
*
* note: **it only works with Zod**
*/
export default function jsonSchema(): Plugin {
export default function jsonSchema({
insert = false,
}: JSONSchemaOptions = {}): Plugin {
let config: LoadedConfig;
function getSchemaPath(name: string) {
return `json-schema/${name}.json`;
}
return {
config(v) {
config = v;
},
configureServer(server) {
if (!server.watcher || !insert) return;
server.watcher.on('add', async (file) => {
for (const [name, collection] of config.collections) {
const single =
collection.type === 'docs' ? collection.meta : collection;
if (single.type !== 'meta' || !isFileInCollection(file, single))
continue;
let obj: object;
try {
const content = (await fs.readFile(file)).toString();
obj = content.length > 0 ? JSON.parse(content) : {};
} catch {
return;
}
if ('$schema' in obj) return;
const schemaPath = path.join(
this.outDir,
getSchemaPath(collection.type === 'docs' ? `${name}.meta` : name),
);
const updated = {
$schema: path.relative(path.dirname(file), schemaPath),
...obj,
};
// TODO: try persist formatting?
await fs.writeFile(file, JSON.stringify(updated, null, 2));
return;
}
});
},
emit() {
const files: EmitEntry[] = [];
function onSchema(name: string, schema: z.ZodSchema) {
files.push({
path: `json-schema/${name}.json`,
path: getSchemaPath(name),
content: JSON.stringify(
z.toJSONSchema(schema, {
io: 'input',
+15
View File
@@ -1,4 +1,5 @@
import type { DocCollection, MetaCollection } from '@/config/define';
import picomatch from 'picomatch';
export function getSupportedFormats(
collection: DocCollection | MetaCollection,
@@ -26,3 +27,17 @@ export function isFileSupported(
return false;
}
export function isFileInCollection(
file: string,
collection: DocCollection | MetaCollection,
): boolean {
if (!isFileSupported(file, collection)) return false;
const patterns = getGlobPatterns(collection);
const isMatch = picomatch(patterns, {
cwd: Array.isArray(collection.dir) ? collection.dir[0] : collection.dir,
});
return isMatch(file);
}
+11
View File
@@ -1817,6 +1817,9 @@ importers:
picocolors:
specifier: ^1.1.1
version: 1.1.1
picomatch:
specifier: ^4.0.3
version: 4.0.3
remark-mdx:
specifier: ^3.1.1
version: 3.1.1
@@ -1860,6 +1863,9 @@ importers:
'@types/node':
specifier: ^24.9.1
version: 24.9.1
'@types/picomatch':
specifier: ^4.0.2
version: 4.0.2
'@types/react':
specifier: ^19.2.2
version: 19.2.2
@@ -5640,6 +5646,9 @@ packages:
'@types/[email protected]':
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
'@types/[email protected]':
resolution: {integrity: sha512-qHHxQ+P9PysNEGbALT8f8YOSHW0KJu6l2xU8DYY0fu/EmGxXdVnuTLvFUvBgPJMSqXq29SYHveejeAha+4AYgA==}
'@types/[email protected]':
resolution: {integrity: sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==}
peerDependencies:
@@ -15185,6 +15194,8 @@ snapshots:
'@types/[email protected]': {}
'@types/[email protected]': {}
'@types/[email protected](@types/[email protected])':
dependencies:
'@types/react': 19.2.2