Almostnode implementation cannot resolve dynamic file paths when rendering in iframe
03:57 17 Jun 2026

I am currently working on a project where I want to run GitHub-hosted projects directly in the browser. I was previously exploring nodepod and came across almostnode, so I wanted to give it a try.

I am facing an issue where custom paths do not seem to resolve correctly. After spending a few days investigating and reviewing the available documentation, I was not able to find anything related to this behavior, so I thought I would reach out for some guidance. I have attached the file I am trying to run. To reproduce the issue, please replace the following two variables with your own values:

Line 30: Replace the url variable with any simple Vite/React todo app that works with npm install followed by npm run dev.
Line 31: Replace the token variable with your own GitHub Personal Access Token.

The project itself is a simple Vite app created using:

npm create vite@latest

I then replaced the generated App.tsx file with the attached version.

I would appreciate any guidance on whether I am missing a configuration step or if there is a recommended way to handle custom path resolution with almostnode. Towards the end I have also placed all the logs on the Chrome console when i refresh the screen. I have also tried to unregister the service worker and doing hard refresh.

App.tsx

import { useEffect, useState, useRef } from "react";
import { cloneRepo } from "./components/CloneRepo";
import createContainer, { ViteDevServer } from "almostnode";
function App() {const viteServerRef = useRef(null);const [serverReady, setServerReady] = useState(false);const container = createContainer({onConsole(level, ...args) {console.log(`[${level}]`,...args);},
onServerReady(port) {  
  console.log(  
    \`Server started on ${port}\`  
  );  
  setServerReady(true);  
}  
});
const { vfs, npm, serverBridge } = container;
useEffect(() => {
    if (!container) return;
    async function runCode() {
      const files = await cloneRepo({
        url: "https://github.com/...", //public repo that you want to test
        token: "ghp_3sg...", // YOUR GITHUB TOKEN HERE
      });
      await mountFiles(files);
      await serverBridge.initServiceWorker();
      // Fix: Prefix all paths in index.html with /__virtual__/3000/
      // This ensures all requests go through the virtual server
      if (vfs.existsSync("/index.html")) {
        let htmlContent = vfs.readFileSync("/index.html", "utf8");
        console.log("[almost-node] Original index.html (first 400 chars):", htmlContent.substring(0, 400));
    const virtualPrefix = '/\__virtual_\_/3000';  

    // Prefix src attributes in script/link tags  
    htmlContent = htmlContent.replace(  
      /(src|href)=\["'\]\\/(\[^"'\]+)\["'\]/g,  
      \`$1="${virtualPrefix}/$2"\`  
    );  

    vfs.writeFileSync("/index.html", htmlContent);  
    console.log("\[almost-node\] Prefixed paths in index.html");  
    console.log("\[almost-node\] Final index.html (first 400 chars):", htmlContent.substring(0, 400));  
  }  

  console.log("Mounted files:",  
    vfs.readdirSync("/")  
  );  

  // Install dependencies from package.json  
  console.log("\[almost-node\] Installing dependencies...");  
  await npm.installFromPackageJson({  
    onProgress: (progress) =\> {  
      console.log("\[almost-node\] Progress:", progress);  
    },  
    includeDev: true,  
  });  
  console.log("\[almost-node\] Dependencies installed");  

  // Log mounted src files before starting server  
  const srcFiles = vfs.existsSync("/src") ? vfs.readdirSync("/src") : \[\];  
  console.log("\[almost-node\] Files in /src:", srcFiles);  

  console.log("\[almost-node\] Creating ViteDevServer...");  

  // Create and start ViteDevServer (the proper almostnode way)  
  const viteServer = new ViteDevServer(vfs, { port: 3000 });  
  viteServerRef.current = viteServer;  
  viteServer.start();  
  console.log("\[almost-node\] ViteDevServer started, isRunning:", viteServer.isRunning());  

  // Register server with serverBridge  
  serverBridge.registerServer(viteServer as any, 3000);  
  const url = serverBridge.getServerUrl(3000);  
  console.log("\[almost-node\] Virtual server URL:", url);  

  // Mark server as ready  
  setServerReady(true);  
  // Create ViteDevServer and register it  
  }
  runCode();
}, []);

// Guard until container is ready
async function mountFiles(files: Record) {
for (const [rawPath, content] of Object.entries(files)) {
// Files come from GitHub with /app prefix, we need them at root / for ViteDevServer// /app/package.json -> /package.json

const path = rawPath.replace(/^\/app/, '') || rawPath;

// Ensure parent directory exists  
    const dir = path.substring(0, path.lastIndexOf('/'));  
    if (dir && !vfs.existsSync(dir)) {  
        vfs.mkdirSync(dir, { recursive: true });  
    }  
    // Write file content  
    vfs.writeFileSync(path, content);  
}  

console.log(\`\[almost-node\] Mounted ${Object.keys(files).length} files to root /\`);  
console.log('\[almost-node\] Root contents:', vfs.readdirSync('/'));  
}
return (

Almostnode React Test

{!container ? ( \Loading container...\ ) : !serverReady ? ( \Starting virtual server...\ ) : ( \ \
\Virtual Server URL:\ \ {container.serverBridge.getServerUrl(3000)} \ \ \
\