Core: Support remarkDirectiveAdmonition

This commit is contained in:
Fuma Nama
2025-11-11 21:26:51 +08:00
parent 112e8d9878
commit 733b01edf1
8 changed files with 417 additions and 298 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'fumadocs-core': patch
---
Support `remarkDirectiveAdmonition`, deprecate `remarkAdmonition` in favor of it.
+1
View File
@@ -150,6 +150,7 @@
"mdast-util-mdxjs-esm": "^2.0.1",
"next": "16.0.1",
"react-router": "^7.9.5",
"remark-directive": "^4.0.0",
"remark-mdx": "^3.1.1",
"remove-markdown": "^0.6.2",
"tsconfig": "workspace:*",
+1
View File
@@ -4,6 +4,7 @@ export * from './remark-image';
export * from './remark-structure';
export * from './remark-heading';
export * from './remark-admonition';
export * from './remark-directive-admonition';
export * from './rehype-toc';
export * from './remark-code-tab';
export * from './remark-steps';
@@ -16,6 +16,8 @@ export interface RemarkAdmonitionOptions {
* Remark Plugin to support Admonition syntax
*
* Useful when Migrating from Docusaurus
*
* @deprecated Use `remarkDirectiveAdmonition` with `remark-directive` configured instead.
*/
export function remarkAdmonition(
options: RemarkAdmonitionOptions = {},
@@ -0,0 +1,100 @@
import { BlockContent, DefinitionContent, Root } from 'mdast';
import type { MdxJsxAttribute, MdxJsxFlowElement } from 'mdast-util-mdx-jsx';
import { Transformer } from 'unified';
import { visit } from 'unist-util-visit';
export interface RemarkDirectiveAdmonitionOptions {
/**
* the tag names of Callout component.
*/
tags?: {
CalloutContainer?: string;
CalloutTitle?: string;
CalloutDescription?: string;
};
/**
* All supported admonition types and their linked Callout type.
*
* When specified, all defaults will be cleared.
*/
types?: Record<string, string>;
}
/**
* Remark Plugin to support Admonition syntax in Docusaurus, useful for migrating from Docusaurus.
*
* Requires [`remark-directive`](https://github.com/remarkjs/remark-directive) to be configured.
*/
export function remarkDirectiveAdmonition({
tags: {
CalloutContainer = 'CalloutContainer',
CalloutTitle = 'CalloutTitle',
CalloutDescription = 'CalloutDescription',
} = {},
types = {
note: 'info',
tip: 'info',
info: 'info',
warning: 'warning',
danger: 'error',
success: 'success',
},
}: RemarkDirectiveAdmonitionOptions = {}): Transformer<Root, Root> {
return (tree) => {
visit(tree, 'containerDirective', (node) => {
if (!(node.name in types)) return;
const attributes: MdxJsxAttribute[] = [
{
type: 'mdxJsxAttribute',
name: 'type',
value: types[node.name],
},
];
for (const [k, v] of Object.entries(node.attributes ?? {})) {
attributes.push({
type: 'mdxJsxAttribute',
name: k,
value: v,
});
}
const titleNodes: (BlockContent | DefinitionContent)[] = [];
const descriptionNodes: (BlockContent | DefinitionContent)[] = [];
for (const item of node.children) {
if (
item.data &&
'directiveLabel' in item.data &&
item.data.directiveLabel
) {
titleNodes.push(item);
} else {
descriptionNodes.push(item);
}
}
Object.assign({
type: 'mdxJsxFlowElement',
attributes,
name: CalloutContainer,
children: [
titleNodes.length > 0 && {
type: 'mdxJsxFlowElement' as const,
name: CalloutTitle,
attributes: [],
children: titleNodes,
},
descriptionNodes.length > 0 && {
type: 'mdxJsxFlowElement' as const,
name: CalloutDescription,
attributes: [],
children: descriptionNodes,
},
].filter((v) => v !== false),
} satisfies MdxJsxFlowElement);
});
};
}
@@ -2,10 +2,8 @@ import * as path from 'node:path';
import type { Image, Root } from 'mdast';
import type { Transformer } from 'unified';
import { visit } from 'unist-util-visit';
import { imageSize } from 'image-size';
import type { MdxjsEsm } from 'mdast-util-mdxjs-esm';
import type { ISizeCalculationResult } from 'image-size/types/interface';
import { imageSizeFromFile } from 'image-size/fromFile';
import type { MdxJsxFlowElement } from 'mdast-util-mdx-jsx';
import { fileURLToPath } from 'node:url';
@@ -319,7 +317,10 @@ async function getImageSize(
src: Source,
onExternal: ExternalImageOptions,
): Promise<ISizeCalculationResult | undefined> {
if (src.type === 'file') return imageSizeFromFile(src.file);
if (src.type === 'file') {
const { imageSizeFromFile } = await import('image-size/fromFile');
return imageSizeFromFile(src.file);
}
if (onExternal === false) return;
const { timeout } = typeof onExternal === 'object' ? onExternal : {};
@@ -333,5 +334,6 @@ async function getImageSize(
);
}
const { imageSize } = await import('image-size');
return imageSize(new Uint8Array(await res.arrayBuffer()));
}
+2 -1
View File
@@ -3,7 +3,8 @@
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
},
"types": ["remark-directive"]
},
"exclude": ["node_modules", "dist", "eslint.config.mjs"]
}
+301 -294
View File
File diff suppressed because it is too large Load Diff