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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions