1- import { useStore } from '@nanostores/react'
1+ import { useStore } from '@nanostores/react' ;
22import tutorialStore from 'tutorialkit:store' ;
33import { useRef , useEffect } from 'react' ;
44import { webcontainer } from 'tutorialkit:core' ;
55import type { WebContainer } from '@webcontainer/api' ;
6+ import { RAILS_WASM_PACKAGE_VERSION } from '../templates/default/lib/constants' ;
67
78export function FileManager ( ) {
89 const files = useStore ( tutorialStore . files ) ;
910 const processedFiles = useRef ( new Set < string > ( ) ) ;
11+ const wasmCached = useRef ( false ) ;
12+ const cachingInterval = useRef < number | null > ( null ) ;
13+ const VERSIONED_RAILS_WASM_FILE_NAME = `rails-${ RAILS_WASM_PACKAGE_VERSION } .wasm` ;
1014
1115 async function chmodx ( wc : WebContainer , path : string ) {
1216 const process = await wc . spawn ( 'chmod' , [ '+x' , path ] ) ;
@@ -20,12 +24,69 @@ export function FileManager() {
2024 }
2125 }
2226
27+ async function fetchCachedWasmFile ( ) : Promise < Uint8Array | null > {
28+ try {
29+ const opfsRoot = await navigator . storage . getDirectory ( ) ;
30+ const fileHandle = await opfsRoot . getFileHandle ( VERSIONED_RAILS_WASM_FILE_NAME ) ;
31+ const file = await fileHandle . getFile ( ) ;
32+ console . log ( `Found cached WASM version ${ RAILS_WASM_PACKAGE_VERSION } ` ) ;
33+ return new Uint8Array ( await file . arrayBuffer ( ) ) ;
34+ } catch {
35+ return null ;
36+ }
37+ }
38+
39+ async function persistWasmFile ( wasmData : Uint8Array ) : Promise < void > {
40+ try {
41+ const opfsRoot = await navigator . storage . getDirectory ( ) ;
42+ const fileHandle = await opfsRoot . getFileHandle ( VERSIONED_RAILS_WASM_FILE_NAME , { create : true } ) ;
43+ const writable = await fileHandle . createWritable ( ) ;
44+ await writable . write ( wasmData ) ;
45+ await writable . close ( ) ;
46+ console . log ( `Rails WASM v${ RAILS_WASM_PACKAGE_VERSION } cached` ) ;
47+ } catch ( error ) {
48+ console . error ( 'Failed to persist Rails WASM:' , error ) ;
49+ }
50+ }
51+
52+ async function cacheWasmFile ( wc : WebContainer ) : Promise < void > {
53+ if ( cachingInterval . current ) return ;
54+
55+ console . log ( `Caching WASM file v${ RAILS_WASM_PACKAGE_VERSION } ...` ) ;
56+
57+ cachingInterval . current = window . setInterval ( async ( ) => {
58+ try {
59+ const wasmData = await wc . fs . readFile ( '/node_modules/@rails-tutorial/wasm/dist/rails.wasm' ) ;
60+ if ( wasmData && wasmData . length > 0 ) {
61+ clearInterval ( cachingInterval . current ! ) ;
62+ cachingInterval . current = null ;
63+
64+ await persistWasmFile ( wasmData ) ;
65+ wasmCached . current = true ;
66+ }
67+ } catch ( error ) {
68+ // File not ready yet, continue checking
69+ }
70+ } , 1000 ) ;
71+ }
72+
2373 useEffect ( ( ) => {
2474 if ( ! files ) return ;
2575
2676 ( async ( ) => {
2777 const wc = await webcontainer ;
2878
79+ if ( ! wasmCached . current ) {
80+ const cachedWasm = await fetchCachedWasmFile ( ) ;
81+ if ( cachedWasm ) {
82+ await wc . fs . writeFile ( VERSIONED_RAILS_WASM_FILE_NAME , cachedWasm ) ;
83+ console . log ( `Rails WASM v${ RAILS_WASM_PACKAGE_VERSION } loaded from cache` ) ;
84+ wasmCached . current = true ;
85+ } else {
86+ await cacheWasmFile ( wc ) ;
87+ }
88+ }
89+
2990 Object . entries ( files ) . forEach ( ( [ _ , fd ] ) => {
3091 const dir = fd . path . split ( '/' ) . filter ( Boolean ) . slice ( - 2 , - 1 ) [ 0 ] ;
3192 if ( dir === "bin" && ! processedFiles . current . has ( fd . path ) ) {
@@ -34,6 +95,13 @@ export function FileManager() {
3495 }
3596 } ) ;
3697 } ) ( ) ;
98+
99+ return ( ) => {
100+ if ( cachingInterval . current ) {
101+ clearInterval ( cachingInterval . current ) ;
102+ cachingInterval . current = null ;
103+ }
104+ } ;
37105 } , [ files ] ) ;
38106
39107 return null ;
0 commit comments