@@ -9,9 +9,16 @@ import {
99 useContext ,
1010 useEffect ,
1111 useMemo ,
12+ useRef ,
1213 useState ,
1314} from "react" ;
14- import { ReplOutput , RuntimeContext , RuntimeInfo , UpdatedFile } from "../interface" ;
15+ import {
16+ ReplOutput ,
17+ RuntimeContext ,
18+ RuntimeErrorHandler ,
19+ RuntimeInfo ,
20+ UpdatedFile ,
21+ } from "../interface" ;
1522
1623export const compilerOptions : CompilerOptions = {
1724 lib : [ "ESNext" , "WebWorker" ] ,
@@ -20,22 +27,26 @@ export const compilerOptions: CompilerOptions = {
2027} ;
2128
2229const TypeScriptContext = createContext < {
23- init : ( ) => void ;
30+ init : ( onError ?: RuntimeErrorHandler ) => void ;
2431 tsEnv : VirtualTypeScriptEnvironment | null ;
2532 tsVersion ?: string ;
2633} > ( { init : ( ) => undefined , tsEnv : null } ) ;
2734export function TypeScriptProvider ( { children } : { children : ReactNode } ) {
2835 const [ tsEnv , setTSEnv ] = useState < VirtualTypeScriptEnvironment | null > ( null ) ;
2936 const [ tsVersion , setTSVersion ] = useState < string | undefined > ( undefined ) ;
3037 const [ doInit , setDoInit ] = useState ( false ) ;
31- const init = useCallback ( ( ) => setDoInit ( true ) , [ ] ) ;
38+ const onErrorRef = useRef < RuntimeErrorHandler | undefined > ( undefined ) ;
39+ const init = useCallback ( ( onError ?: RuntimeErrorHandler ) => {
40+ onErrorRef . current = onError ;
41+ setDoInit ( true ) ;
42+ } , [ ] ) ;
3243 useEffect ( ( ) => {
3344 // useEffectはサーバーサイドでは実行されないが、
3445 // typeof window !== "undefined" でガードしないとなぜかesbuildが"typescript"を
3546 // サーバーサイドでのインポート対象とみなしてしまう。
3647 if ( doInit && tsEnv === null && typeof window !== "undefined" ) {
3748 const abortController = new AbortController ( ) ;
38- ( async ( ) => {
49+ void ( async ( ) => {
3950 const ts = await import ( "typescript" ) ;
4051 const vfs = await import ( "@typescript/vfs" ) ;
4152 const system = vfs . createSystem ( new Map ( ) ) ;
@@ -70,7 +81,12 @@ export function TypeScriptProvider({ children }: { children: ReactNode }) {
7081 ) ;
7182 setTSEnv ( env ) ;
7283 setTSVersion ( ts . version ) ;
73- } ) ( ) ;
84+ } ) ( ) . catch ( ( error ) => {
85+ if ( error instanceof DOMException && error . name === "AbortError" ) {
86+ return ;
87+ }
88+ onErrorRef . current ?.( error ) ;
89+ } ) ;
7490 return ( ) => {
7591 abortController . abort ( ) ;
7692 } ;
@@ -86,9 +102,11 @@ export function TypeScriptProvider({ children }: { children: ReactNode }) {
86102export function useTypeScript ( jsEval : RuntimeContext ) : RuntimeContext {
87103 const { init : tsInit , tsEnv, tsVersion } = useContext ( TypeScriptContext ) ;
88104 const { init : jsInit } = jsEval ;
89- const init = useCallback ( ( ) => {
90- tsInit ( ) ;
91- jsInit ?.( ) ;
105+ const onErrorRef = useRef < RuntimeErrorHandler | undefined > ( undefined ) ;
106+ const init = useCallback ( ( onError ?: RuntimeErrorHandler ) => {
107+ onErrorRef . current = onError ;
108+ tsInit ( onError ) ;
109+ jsInit ?.( onError ) ;
92110 } , [ tsInit , jsInit ] ) ;
93111
94112 const runFiles = useCallback (
@@ -101,6 +119,7 @@ export function useTypeScript(jsEval: RuntimeContext): RuntimeContext {
101119 onOutput ( { type : "error" , message : "TypeScript is not ready yet." } ) ;
102120 return ;
103121 } else {
122+ try {
104123 for ( const [ filename , content ] of Object . entries ( files ) ) {
105124 tsEnv . createFile ( filename , content ) ;
106125 }
@@ -151,6 +170,13 @@ export function useTypeScript(jsEval: RuntimeContext): RuntimeContext {
151170 { ...files , ...emittedFiles } ,
152171 onOutput
153172 ) ;
173+ } catch ( error ) {
174+ onErrorRef . current ?.( error ) ;
175+ onOutput ( {
176+ type : "fatalError" ,
177+ message : error instanceof Error ? error . message : String ( error ) ,
178+ } ) ;
179+ }
154180 }
155181 } ,
156182 [ tsEnv , jsEval ]
0 commit comments