@@ -2,45 +2,28 @@ import * as React from "react";
22import * as runtime from "react/jsx-runtime" ;
33import * as R from "remeda" ;
44
5- import { evaluate , EvaluateOptions } from "@mdx-js/mdx" ;
5+ import { evaluate } from "@mdx-js/mdx" ;
6+ import * as provider from "@mdx-js/react" ;
67import { CircularProgress } from "@mui/material" ;
7- import { ErrorBoundary , Suspense } from "@suspensive/react" ;
8- import { useSuspenseQuery } from "@tanstack/react-query " ;
9- import components , { MuiMdxComponentsOptions } from 'mui-mdx-components' ;
8+ import { ErrorBoundary } from "@suspensive/react" ;
9+ import type { MDXComponents } from "mdx/types " ;
10+ import muiComponents from 'mui-mdx-components' ;
1011
11- import Components from "../components" ;
1212import Hooks from "../hooks" ;
13+ import { ErrorFallback } from "./error_handler" ;
1314
14- const MDXComponents : MuiMdxComponentsOptions = {
15- overrides : {
16- 'h1' : ( props ) => < h1 { ...props } /> ,
17- 'h2' : ( props ) => < h2 { ...props } /> ,
18- 'h3' : ( props ) => < h3 { ...props } /> ,
19- 'h4' : ( props ) => < h4 { ...props } /> ,
20- 'h5' : ( props ) => < h5 { ...props } /> ,
21- 'h6' : ( props ) => < h6 { ...props } /> ,
22- 'strong' : ( props ) => < strong { ...props } /> ,
23- 'em' : ( props ) => < em { ...props } /> ,
24- 'ul' : ( props ) => < ul { ...props } /> ,
25- 'ol' : ( props ) => < ol { ...props } /> ,
26- 'li' : ( props ) => < li { ...props } /> ,
27- }
28- }
29-
30- const InnerMDXRenderer : React . FC < { text : string , baseUrl : string } > = ( { text, baseUrl } ) => {
31- const options : EvaluateOptions = { ...runtime , baseUrl } ;
32-
33- const { data } = useSuspenseQuery ( {
34- queryKey : [ "mdx" , text ] ,
35- queryFn : async ( ) => {
36- const { default : RenderResult } = await evaluate ( text , options ) ;
37- return < div className = "markdown-body" >
38- < RenderResult components = { components ( MDXComponents ) } />
39- </ div >
40- } ,
41- } ) ;
42-
43- return < > { data } </ > ;
15+ const CustomMDXComponents : MDXComponents = {
16+ 'h1' : ( props ) => < h1 { ...props } /> ,
17+ 'h2' : ( props ) => < h2 { ...props } /> ,
18+ 'h3' : ( props ) => < h3 { ...props } /> ,
19+ 'h4' : ( props ) => < h4 { ...props } /> ,
20+ 'h5' : ( props ) => < h5 { ...props } /> ,
21+ 'h6' : ( props ) => < h6 { ...props } /> ,
22+ 'strong' : ( props ) => < strong { ...props } /> ,
23+ 'em' : ( props ) => < em { ...props } /> ,
24+ 'ul' : ( props ) => < ul { ...props } /> ,
25+ 'ol' : ( props ) => < ol { ...props } /> ,
26+ 'li' : ( props ) => < li { ...props } /> ,
4427}
4528
4629const lineFormatterForMDX = ( line : string ) => {
@@ -55,20 +38,31 @@ const lineFormatterForMDX = (line: string) => {
5538 return `${ trimmedLine } \n` ;
5639}
5740
58- export const MDXRenderer : React . FC < { text : string } > = ( { text } ) => {
59- // 원래 MDX는 각 줄의 마지막에 공백 2개가 있어야 줄바꿈이 되고, 또 연속 줄바꿈은 무시되지만,
60- // 편의성을 위해 렌더러 단에서 공백 2개를 추가하고 연속 줄바꿈을 <br />로 변환합니다.
41+ export const MDXRenderer : React . FC < { text : string ; components ?: MDXComponents , resetKey ?: string } > = ( { text, components, resetKey } ) => {
6142 const { baseUrl } = Hooks . Common . useCommonContext ( ) ;
43+ const [ state , setState ] = React . useState < { component : React . ReactNode , resetKey : string } > ( {
44+ component : < CircularProgress /> ,
45+ resetKey : window . crypto . randomUUID ( ) ,
46+ } )
47+
48+ const setRenderResult = ( component : React . ReactNode ) => setState ( ( prev ) => ( { ...prev , component : component } ) ) ;
49+ const setRandomResetKey = ( ) => setState ( ( prev ) => ( { ...prev , resetKey : window . crypto . randomUUID ( ) } ) )
6250
63- const processedText = text
64- . split ( "\n" )
65- . map ( lineFormatterForMDX )
66- . join ( "" )
67- . replaceAll ( "\n\n" , "\n<br />\n" ) ;
51+ React . useEffect ( ( ) => {
52+ (
53+ async ( ) => {
54+ try {
55+ // 원래 MDX는 각 줄의 마지막에 공백 2개가 있어야 줄바꿈이 되고, 또 연속 줄바꿈은 무시되지만,
56+ // 편의성을 위해 렌더러 단에서 공백 2개를 추가하고 연속 줄바꿈을 <br />로 변환합니다.
57+ const processedText = text . split ( "\n" ) . map ( lineFormatterForMDX ) . join ( "" ) . replaceAll ( "\n\n" , "\n<br />\n" ) ;
58+ const { default : RenderResult } = await evaluate ( processedText , { ...runtime , ...provider , baseUrl } ) ;
59+ setRenderResult ( < RenderResult components = { muiComponents ( { overrides : { ...CustomMDXComponents , ...components } } ) } /> ) ;
60+ } catch ( error ) {
61+ setRenderResult ( < ErrorFallback error = { error as Error } reset = { setRandomResetKey } /> ) ;
62+ }
63+ }
64+ ) ( ) ;
65+ } , [ text , resetKey , state . resetKey ] ) ;
6866
69- return < ErrorBoundary fallback = { Components . ErrorFallback } >
70- < Suspense fallback = { < CircularProgress /> } >
71- < InnerMDXRenderer text = { processedText } baseUrl = { baseUrl } />
72- </ Suspense >
73- </ ErrorBoundary >
67+ return < ErrorBoundary fallback = { ErrorFallback } resetKeys = { [ text , resetKey , state . resetKey ] } > { state . component } </ ErrorBoundary >
7468} ;
0 commit comments