11"use client" ;
22
3- import ts , { CompilerOptions } from "typescript" ;
4- import {
5- createSystem ,
6- createVirtualTypeScriptEnvironment ,
7- knownLibFilesForCompilerOptions ,
8- VirtualTypeScriptEnvironment ,
9- } from "@typescript/vfs" ;
3+ import type { CompilerOptions } from "typescript" ;
4+ import type { VirtualTypeScriptEnvironment } from "@typescript/vfs" ;
105import {
116 createContext ,
127 ReactNode ,
138 useCallback ,
149 useContext ,
1510 useEffect ,
16- useRef ,
1711 useState ,
1812} from "react" ;
1913import { useEmbedContext } from "../embedContext" ;
2014import { ReplOutput } from "../repl" ;
2115import { RuntimeContext } from "../runtime" ;
16+ import dynamic from "next/dynamic" ;
2217
2318export const compilerOptions : CompilerOptions = { } ;
2419
25- const TypeScriptContext = createContext < VirtualTypeScriptEnvironment | null > (
26- null
20+ type TSModules = {
21+ ts : typeof import ( "typescript" ) ;
22+ vfs : typeof import ( "@typescript/vfs" ) ;
23+ } ;
24+ interface ITypeScriptContext {
25+ modules : TSModules | null ;
26+ setModules : ( modules : TSModules ) => void ;
27+ tsEnv : VirtualTypeScriptEnvironment | null ;
28+ }
29+ const TypeScriptContext = createContext < ITypeScriptContext > ( {
30+ modules : null ,
31+ setModules : ( ) => { } ,
32+ tsEnv : null ,
33+ } ) ;
34+ const LazyInitTypeScript = dynamic (
35+ async ( ) => {
36+ const ts = await import ( "typescript" ) ;
37+ const vfs = await import ( "@typescript/vfs" ) ;
38+ return function LazyInitTypeScript ( ) {
39+ const { setModules } = useContext ( TypeScriptContext ) ;
40+ useEffect ( ( ) => {
41+ setModules ( { ts, vfs } ) ;
42+ } , [ setModules ] ) ;
43+ return null ;
44+ } ;
45+ } ,
46+ { ssr : false }
2747) ;
2848export function TypeScriptProvider ( { children } : { children : ReactNode } ) {
2949 const [ tsEnv , setTSEnv ] = useState < VirtualTypeScriptEnvironment | null > ( null ) ;
50+ const [ modules , setModules ] = useState < TSModules | null > ( null ) ;
51+
3052 useEffect ( ( ) => {
31- if ( tsEnv === null ) {
53+ if ( modules !== null && tsEnv === null ) {
54+ const { ts, vfs } = modules ;
3255 const abortController = new AbortController ( ) ;
3356 ( async ( ) => {
34- const system = createSystem ( new Map ( ) ) ;
35- const libFiles = knownLibFilesForCompilerOptions ( compilerOptions , ts ) ;
57+ const system = vfs . createSystem ( new Map ( ) ) ;
58+ const libFiles = vfs . knownLibFilesForCompilerOptions (
59+ compilerOptions ,
60+ ts
61+ ) ;
3662 const libFileContents = await Promise . all (
3763 libFiles . map ( async ( libFile ) => {
3864 const response = await fetch (
@@ -52,7 +78,7 @@ export function TypeScriptProvider({ children }: { children: ReactNode }) {
5278 system . writeFile ( `/${ libFile } ` , content ) ;
5379 }
5480 } ) ;
55- const env = createVirtualTypeScriptEnvironment (
81+ const env = vfs . createVirtualTypeScriptEnvironment (
5682 system ,
5783 [ ] ,
5884 ts ,
@@ -64,21 +90,22 @@ export function TypeScriptProvider({ children }: { children: ReactNode }) {
6490 abortController . abort ( ) ;
6591 } ;
6692 }
67- } , [ tsEnv ] ) ;
93+ } , [ tsEnv , setTSEnv , modules ] ) ;
6894 return (
69- < TypeScriptContext . Provider value = { tsEnv } >
95+ < TypeScriptContext . Provider value = { { tsEnv, modules, setModules } } >
96+ < LazyInitTypeScript />
7097 { children }
7198 </ TypeScriptContext . Provider >
7299 ) ;
73100}
74101
75102export function useTypeScript ( jsEval : RuntimeContext ) : RuntimeContext {
76- const tsEnv = useContext ( TypeScriptContext ) ;
103+ const { modules , tsEnv } = useContext ( TypeScriptContext ) ;
77104
78105 const { writeFile } = useEmbedContext ( ) ;
79106 const runFiles = useCallback (
80107 async ( filenames : string [ ] , files : Record < string , string > ) => {
81- if ( tsEnv === null ) {
108+ if ( tsEnv === null || modules === null ) {
82109 return [
83110 { type : "error" as const , message : "TypeScript is not ready yet." } ,
84111 ] ;
@@ -95,11 +122,14 @@ export function useTypeScript(jsEval: RuntimeContext): RuntimeContext {
95122 ) ) {
96123 outputs . push ( {
97124 type : "error" ,
98- message : ts . formatDiagnosticsWithColorAndContext ( [ diagnostic ] , {
99- getCurrentDirectory : ( ) => "" ,
100- getCanonicalFileName : ( f ) => f ,
101- getNewLine : ( ) => "\n" ,
102- } ) ,
125+ message : modules . ts . formatDiagnosticsWithColorAndContext (
126+ [ diagnostic ] ,
127+ {
128+ getCurrentDirectory : ( ) => "" ,
129+ getCanonicalFileName : ( f ) => f ,
130+ getNewLine : ( ) => "\n" ,
131+ }
132+ ) ,
103133 } ) ;
104134 }
105135
@@ -108,23 +138,33 @@ export function useTypeScript(jsEval: RuntimeContext): RuntimeContext {
108138 ) ) {
109139 outputs . push ( {
110140 type : "error" ,
111- message : ts . formatDiagnosticsWithColorAndContext ( [ diagnostic ] , {
112- getCurrentDirectory : ( ) => "" ,
113- getCanonicalFileName : ( f ) => f ,
114- getNewLine : ( ) => "\n" ,
115- } ) ,
141+ message : modules . ts . formatDiagnosticsWithColorAndContext (
142+ [ diagnostic ] ,
143+ {
144+ getCurrentDirectory : ( ) => "" ,
145+ getCanonicalFileName : ( f ) => f ,
146+ getNewLine : ( ) => "\n" ,
147+ }
148+ ) ,
116149 } ) ;
117150 }
118151
119152 const emitOutput = tsEnv . languageService . getEmitOutput ( filenames [ 0 ] ) ;
120- files = await writeFile ( Object . fromEntries ( emitOutput . outputFiles . map ( ( of ) => [ of . name , of . text ] ) ) ) ;
153+ files = await writeFile (
154+ Object . fromEntries (
155+ emitOutput . outputFiles . map ( ( of ) => [ of . name , of . text ] )
156+ )
157+ ) ;
121158
122- console . log ( emitOutput )
123- const jsOutputs = jsEval . runFiles ( [ emitOutput . outputFiles [ 0 ] . name ] , files ) ;
159+ console . log ( emitOutput ) ;
160+ const jsOutputs = jsEval . runFiles (
161+ [ emitOutput . outputFiles [ 0 ] . name ] ,
162+ files
163+ ) ;
124164
125165 return outputs . concat ( await jsOutputs ) ;
126166 } ,
127- [ tsEnv , writeFile , jsEval ]
167+ [ modules , tsEnv , writeFile , jsEval ]
128168 ) ;
129169 return {
130170 ready : tsEnv !== null ,
0 commit comments