-
-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathindex.ts
More file actions
99 lines (88 loc) · 2.67 KB
/
Copy pathindex.ts
File metadata and controls
99 lines (88 loc) · 2.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import {
FunctionComponent,
Fragment,
ReactElement,
createElement,
useState,
useEffect,
useCallback,
} from 'react';
import unified, { PluggableList } from 'unified';
import remarkParse, { RemarkParseOptions } from 'remark-parse';
import { Options as RemarkRehypeOptions } from 'mdast-util-to-hast';
import remarkToRehype from 'remark-rehype';
import rehypeReact, { Options as RehypeReactOptions } from 'rehype-react';
type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
export interface UseRemarkSyncOptions {
remarkParseOptions?: RemarkParseOptions;
remarkToRehypeOptions?: RemarkRehypeOptions;
rehypeReactOptions?: PartialBy<
RehypeReactOptions<typeof createElement>,
'createElement'
>;
remarkPlugins?: PluggableList;
rehypePlugins?: PluggableList;
}
export const useRemarkSync = (
source: string,
{
remarkParseOptions,
remarkToRehypeOptions,
rehypeReactOptions,
remarkPlugins = [],
rehypePlugins = [],
}: UseRemarkOptions = {}
): ReactElement =>
unified()
.use(remarkParse, remarkParseOptions)
.use(remarkPlugins)
.use(remarkToRehype, remarkToRehypeOptions)
.use(rehypePlugins)
.use(rehypeReact, {
createElement,
Fragment,
...rehypeReactOptions,
} as RehypeReactOptions<typeof createElement>)
.processSync(source).result as ReactElement;
export interface UseRemarkOptions extends UseRemarkSyncOptions {
onError?: (err: Error) => void;
}
export const useRemark = ({
remarkParseOptions,
remarkToRehypeOptions,
rehypeReactOptions,
remarkPlugins = [],
rehypePlugins = [],
onError = () => {},
}: UseRemarkOptions = {}): [ReactElement | null, (source: string) => void] => {
const [reactContent, setReactContent] = useState<ReactElement | null>(null);
const setMarkdownSource = useCallback((source: string) => {
unified()
.use(remarkParse, remarkParseOptions)
.use(remarkPlugins)
.use(remarkToRehype, remarkToRehypeOptions)
.use(rehypePlugins)
.use(rehypeReact, {
createElement,
Fragment,
...rehypeReactOptions,
} as RehypeReactOptions<typeof createElement>)
.process(source)
.then((vfile) => setReactContent(vfile.result as ReactElement))
.catch(onError);
}, []);
return [reactContent, setMarkdownSource];
};
export interface RemarkProps extends UseRemarkOptions {
children: string;
}
export const Remark: FunctionComponent<RemarkProps> = ({
children,
...useRemarkOptions
}: RemarkProps) => {
const [reactContent, setMarkdownSource] = useRemark(useRemarkOptions);
useEffect(() => {
setMarkdownSource(children);
}, [children, setMarkdownSource]);
return reactContent;
};