Skip to content

VFS Support #3

@MarketingPip

Description

@MarketingPip

Is there a way we can hard monkey patch os.FileRead?

Options to do this could be:

// Manually force-register a fake package
packages["os"] = &compiler.Archive{
    ImportPath: "os",
    // You would need to populate this with the compiled 
    // declarations of your custom ReadFile
}

or update main in complie.go:

// 1. Define your mock OS archive globally or fetch it once
var mockOSArchive *compiler.Archive 

// ... inside main ...
importContext := &compiler.ImportContext{
    Packages: make(map[string]*types.Package),
    Import: func(path string) (*compiler.Archive, error) {
        // HIJACK: If anything asks for "os", give them our fake one
        if path == "os" && mockOSArchive != nil {
            return mockOSArchive, nil
        }

        if pkg, found := packages[path]; found {
            return pkg, nil
        }
        pkgsToLoad[path] = struct{}{}
        return &compiler.Archive{}, nil
    },
}

Dropping this here since @hatemhosny - will think this is useful.

import { compile, format } from 'https://cdn.jsdelivr.net/npm/@live-codes/go2js';
  import { vol } from "https://cdn.jsdelivr.net/npm/memfs@4.6.0/+esm";

  // ---------------------------
  // 1. Load JSON into memfs
  // ---------------------------
  vol.fromJSON({
    "/etc/hello.txt": "Hello from the JSON VFS!",
    "/data/config.json": JSON.stringify({ version: "1.0", status: "ready" })
  });

  // ---------------------------
  // 2. Expose VFS to Go
  // ---------------------------
  window.VFS = {
    readFile: (path) => vol.readFileSync(path, "utf8"),
    writeFile: (path, data) => vol.writeFileSync(path, data, "utf8"),
    stat: (path) => vol.statSync(path),
    readDir: (path) => vol.readdirSync(path),
    mkdirAll: (path) => vol.mkdirSync(path, { recursive: true }),
    remove: (path) => vol.unlinkSync(path),

    // Simple open/read/close
    open: (path) => vol.openSync(path, "r"),
    read: (fd, len) => {
    const buf = new Uint8Array(len);
    const bytes = vol.readSync(fd, buf, 0, len, null);
    return new TextDecoder().decode(buf.slice(0, bytes));
    }
    ,
    close: (fd) => vol.closeSync(fd)
  };

  // ---------------------------
  // 3. Go code (os-compatible API)
  // ---------------------------
  const goCode = `
package main

import (
  "fmt"
  "io"
  "os"
  "syscall/js"
  "time"
)

// ---- vfs/os-compatible types ----
type File struct{ fd int }

type fileInfo struct {
  name  string
  size  int64
  mode  os.FileMode
  isDir bool
  mtime time.Time
}

func (fi fileInfo) Name() string       { return fi.name }
func (fi fileInfo) Size() int64        { return fi.size }
func (fi fileInfo) Mode() os.FileMode  { return fi.mode }
func (fi fileInfo) ModTime() time.Time { return fi.mtime }
func (fi fileInfo) IsDir() bool        { return fi.isDir }
func (fi fileInfo) Sys() interface{}   { return nil }

// ---- VFS API ----
func ReadFile(path string) ([]byte, error) {
  val := js.Global().Get("VFS").Call("readFile", path)
  return []byte(val.String()), nil
}

func WriteFile(path string, data string) error {
  js.Global().Get("VFS").Call("writeFile", path, data)
  return nil
}

func Stat(path string) (os.FileInfo, error) {
  st := js.Global().Get("VFS").Call("stat", path)
  return fileInfo{
    name:  st.Get("name").String(),
    size:  int64(st.Get("size").Int()),
    mode:  os.FileMode(st.Get("mode").Int()),
    isDir: st.Get("isDirectory").Bool(),
    mtime: time.Unix(0, int64(st.Get("mtimeMs").Int())*int64(time.Millisecond)),
  }, nil
}

func ReadDir(path string) ([]os.DirEntry, error) {
  arr := js.Global().Get("VFS").Call("readDir", path)
  out := make([]os.DirEntry, 0, arr.Length())
  for i := 0; i < arr.Length(); i++ {
    name := arr.Index(i).String()
    out = append(out, dirEntry{name: name})
  }
  return out, nil
}

type dirEntry struct{ name string }

func (d dirEntry) Name() string               { return d.name }
func (d dirEntry) IsDir() bool                { return false }
func (d dirEntry) Type() os.FileMode          { return 0 }
func (d dirEntry) Info() (os.FileInfo, error) { return nil, nil }

func Open(path string) (*File, error) {
  fd := js.Global().Get("VFS").Call("open", path).Int()
  return &File{fd: fd}, nil
}

func (f *File) Read(p []byte) (int, error) {
  s := js.Global().Get("VFS").Call("read", f.fd, len(p)).String()
  n := copy(p, []byte(s))
  if n == 0 {
    return 0, io.EOF
  }
  return n, nil
}


func (f *File) Close() error {
  js.Global().Get("VFS").Call("close", f.fd)
  return nil
}

// ---- main ----
func main() {
  data, _ := ReadFile("/etc/hello.txt")
  fmt.Println("ReadFile:", string(data))

  f, _ := Open("/etc/hello.txt")
  buf := make([]byte, 128)
  n, _ := f.Read(buf)
  fmt.Println("Open+Read:", string(buf[:n]))
  f.Close()
}
`;

  // ---------------------------
  // 4. Compile & run
  // ---------------------------
  format(goCode)
    .then((formatted) => compile(formatted))
    .then((js) => eval(js))
    .catch(console.error);

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions