diff --git a/packages/npm-packages/ruby-wasm-wasi/example/README.md b/packages/npm-packages/ruby-wasm-wasi/example/README.md index ece5617c2..299491fdb 100644 --- a/packages/npm-packages/ruby-wasm-wasi/example/README.md +++ b/packages/npm-packages/ruby-wasm-wasi/example/README.md @@ -17,4 +17,5 @@ $ # Open http://localhost:8000/script-src ```console $ npm install $ node index.node.js +$ node preopens.node.js ``` diff --git a/packages/npm-packages/ruby-wasm-wasi/example/preopens.node.js b/packages/npm-packages/ruby-wasm-wasi/example/preopens.node.js new file mode 100644 index 000000000..1a16f6b77 --- /dev/null +++ b/packages/npm-packages/ruby-wasm-wasi/example/preopens.node.js @@ -0,0 +1,17 @@ +import fs from "fs/promises"; +import { DefaultRubyVM } from "@ruby/wasm-wasi/dist/node"; + +// $ node preopens.node.js + +const binary = await fs.readFile( + "./node_modules/@ruby/head-wasm-wasi/dist/ruby.wasm", +); +const module = await WebAssembly.compile(binary); +const { vm } = await DefaultRubyVM(module, { + preopens: { + "/app": "./require_relative", + }, +}); + +// /app/main.rb uses require_relative to load /app/greeting.rb. +vm.eval('require "/app/main"'); diff --git a/packages/npm-packages/ruby-wasm-wasi/src/node.ts b/packages/npm-packages/ruby-wasm-wasi/src/node.ts index 2348510b3..f901a1840 100644 --- a/packages/npm-packages/ruby-wasm-wasi/src/node.ts +++ b/packages/npm-packages/ruby-wasm-wasi/src/node.ts @@ -3,9 +3,17 @@ import { RubyVM } from "./vm.js"; export const DefaultRubyVM = async ( rubyModule: WebAssembly.Module, - options: { env?: Record | undefined } = {}, + options: { + env?: Record | undefined; + preopens?: Record; + } = {}, ) => { - const wasi = new WASI({ env: options.env, version: "preview1", returnOnExit: true }); + const wasi = new WASI({ + env: options.env, + preopens: options.preopens, + version: "preview1", + returnOnExit: true, + }); const { vm, instance } = await RubyVM.instantiateModule({ module: rubyModule, wasip1: wasi }); return { diff --git a/packages/npm-packages/ruby-wasm-wasi/test/package.test.js b/packages/npm-packages/ruby-wasm-wasi/test/package.test.js index d3924092d..a0352ca67 100644 --- a/packages/npm-packages/ruby-wasm-wasi/test/package.test.js +++ b/packages/npm-packages/ruby-wasm-wasi/test/package.test.js @@ -1,5 +1,6 @@ import * as path from "path"; import * as fs from "fs/promises"; +import * as os from "os"; import { WASI } from "wasi"; import { RubyVM } from "../src/index"; import { DefaultRubyVM } from "../src/node"; @@ -59,6 +60,52 @@ describe("Packaging validation", () => { vm.eval(`require "stringio"`); }); + test("DefaultRubyVM WASI preopens with require_relative", async () => { + const mod = await loadWasmModule(`ruby+stdlib.wasm`); + const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "ruby-wasm-preopen-")); + const nestedDir = path.join(tempDir, "nested"); + const entryFile = path.join(tempDir, "entry.rb"); + const helperFile = path.join(nestedDir, "helper.rb"); + + try { + await fs.mkdir(nestedDir); + await fs.writeFile(entryFile, [ + "PREOPEN_ENTRY_FILE = __FILE__", + "require_relative './nested/helper'", + ].join("\n")); + await fs.writeFile(helperFile, "PREOPEN_HELPER_FILE = __FILE__\n"); + + const { vm } = await DefaultRubyVM(mod, { + preopens: { "/app": tempDir }, + }); + expect(vm.eval(`require "/app/entry"`).toString()).toBe("true"); + expect(vm.eval(`require "/app/entry"`).toString()).toBe("false"); + + expect(vm.eval("PREOPEN_ENTRY_FILE").toString()).toBe("/app/entry.rb"); + expect(vm.eval("PREOPEN_HELPER_FILE").toString()).toBe( + "/app/nested/helper.rb", + ); + expect( + vm.eval('$LOADED_FEATURES.include?("/app/entry.rb")').toString(), + ).toBe("true"); + expect( + vm + .eval('$LOADED_FEATURES.include?("/app/nested/helper.rb")') + .toString(), + ).toBe("true"); + expect( + vm + .eval(`$LOADED_FEATURES.include?(${JSON.stringify(entryFile)})`) + .toString(), + ).toBe("false"); + expect(vm.eval(`File.file?(${JSON.stringify(entryFile)})`).toString()).toBe( + "false", + ); + } finally { + await fs.rm(tempDir, { recursive: true, force: true }); + } + }); + test.each([ { file: "ruby+stdlib.wasm", stdlib: true }, { file: "ruby.debug+stdlib.wasm", stdlib: true },