Skip to content

Commit 24a4bd8

Browse files
authored
Merge pull request #10 from palkan/bakaface/cache-wasm-module-mount
feat: cache rails.wasm
2 parents 31d9144 + b36684c commit 24a4bd8

File tree

4 files changed

+134
-2
lines changed

4 files changed

+134
-2
lines changed

src/components/FileManager.tsx

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1-
import { useStore } from '@nanostores/react'
1+
import { useStore } from '@nanostores/react';
22
import tutorialStore from 'tutorialkit:store';
33
import { useRef, useEffect } from 'react';
44
import { webcontainer } from 'tutorialkit:core';
55
import type { WebContainer } from '@webcontainer/api';
6+
import { RAILS_WASM_PACKAGE_VERSION } from '../templates/default/lib/constants';
67

78
export 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;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const RAILS_WASM_PACKAGE_VERSION = '8.0.2-rc.1';

src/templates/default/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"version": "0.0.0",
55
"type": "module",
66
"scripts": {
7+
"preinstall": "node scripts/preinstall.js",
78
"dev": "vite",
89
"test": "node --disable-warning=ExperimentalWarning ./scripts/test.js",
910
"test:watch": "node --disable-warning=ExperimentalWarning --watch --watch-path=./workspace ./scripts/test.js",
@@ -12,7 +13,6 @@
1213
"dependencies": {
1314
"@electric-sql/pglite": "^0.3.1",
1415
"@ruby/wasm-wasi": "^2.7.1",
15-
"@rails-tutorial/wasm": "8.0.2-rc.1",
1616
"express": "^5.1.0",
1717
"multer": "^2.0.1",
1818
"set-cookie-parser": "^2.7.1"
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#!/usr/bin/env node
2+
3+
import fs from 'fs/promises';
4+
import path from 'path';
5+
import { execSync } from 'child_process';
6+
import { RAILS_WASM_PACKAGE_VERSION } from '../lib/constants.js';
7+
const VERSIONED_RAILS_WASM_PATH = `./rails-${RAILS_WASM_PACKAGE_VERSION}.wasm`;
8+
const TARGET_DIR = 'node_modules/@rails-tutorial/wasm/dist';
9+
const TARGET_FILE = path.join(TARGET_DIR, 'rails.wasm');
10+
11+
async function checkIfFileExists(filePath) {
12+
try {
13+
await fs.access(filePath);
14+
return true;
15+
} catch {
16+
return false;
17+
}
18+
}
19+
20+
async function moveFile(src, dest) {
21+
try {
22+
await fs.mkdir(path.dirname(dest), { recursive: true });
23+
await fs.rename(src, dest);
24+
console.log(`✓ Moved ${src} to ${dest}`);
25+
return true;
26+
} catch (error) {
27+
console.error(`✗ Failed to move ${src} to ${dest}:`, error.message);
28+
return false;
29+
}
30+
}
31+
32+
async function installPackage(packageName, version) {
33+
try {
34+
console.log(`Installing ${packageName}@${version}...`);
35+
execSync(`npm install ${packageName}@${version}`, { stdio: 'inherit' });
36+
console.log(`✓ Installed ${packageName}@${version}`);
37+
return true;
38+
} catch (error) {
39+
console.error(`✗ Failed to install ${packageName}@${version}:`, error.message);
40+
return false;
41+
}
42+
}
43+
44+
async function main() {
45+
const versionedWasmExists = await checkIfFileExists(VERSIONED_RAILS_WASM_PATH);
46+
47+
if (versionedWasmExists) {
48+
const success = await moveFile(VERSIONED_RAILS_WASM_PATH, TARGET_FILE);
49+
if (!success) {
50+
process.exit(1);
51+
}
52+
} else {
53+
const success = await installPackage('@rails-tutorial/wasm', RAILS_WASM_PACKAGE_VERSION);
54+
if (!success) {
55+
process.exit(1);
56+
}
57+
}
58+
}
59+
60+
main().catch(error => {
61+
console.error('Preinstall script failed:', error);
62+
process.exit(1);
63+
});

0 commit comments

Comments
 (0)