JS::Object.await ふたたび - @ledsun blog で、JS::Object.awaitの使い方がわかりました。 そこでfetchを使ってrequire_relativeを実装します。
import { RubyScript } from "./RubyScript"; import { EvaluatedScriptStack } from "./EvaluatedScriptStack"; class RubyWasm { private _loadedPathes: Set<string> = new Set(); private _stack: EvaluatedScriptStack; constructor(stack: EvaluatedScriptStack) { this._stack = stack; } async requireRelativeURL(relative_feature): Promise<boolean> { const filename = relative_feature.endsWith(".rb") ? relative_feature : `${relative_feature}.rb`; const url = new URL(filename, this._stack.currentURL); // Prevents multiple loading. if (this._loadedPathes.has(url.pathname)) { return false; } const response = await fetch(url); if (!response.ok) { return false; } const text = await response.text(); await this._stack.eval(new RubyScript(url, text)); return true; } } export function defineRubyWasm(stack: EvaluatedScriptStack): void { const loadedPathes: Set<string> = new Set(); const global = window as any; global.rubyWasm = new RubyWasm(stack); }
fetchして結果を待ってawait this._stack.eval(new RubyScript(url, text))
します。
stackは次のようなクラスです。
import { RubyScript } from "./RubyScript"; // To achieve require_relative, we need to resolve relative paths in Ruby scripts. // Remember the URL of the running Ruby script. export class EvaluatedScriptStack { private _vm; // Stores the URL of the running Ruby script to get the relative path from within the Ruby script. private _stack: Array<RubyScript>; constructor(vm) { this._vm = vm; this._stack = []; } async eval(script: RubyScript): Promise<void> { this._stack.push(script); await this._vm.evalAsync(script.ScriptBody); this._stack.pop(); } get currentURL(): URL { return this._stack.at(-1).URL; } }
実行中のRubyスクリプトのURLを保持しつつawait this._vm.evalAsync(script.ScriptBody)
します。
そして、requireRelativeURL
はrequrie_relative
を置換したrequire_relative_url
メソッドで呼び出します。
import { RubyVM } from ".."; export function define_require_relative_url(vm: RubyVM) { const script = ` require "js" module Kernel def require_relative_url(relative_feature) JS.global[:rubyWasm].requireRelativeURL(relative_feature).await end end `; vm.eval(script); }
これを次のようなHTMLを実行します。
<html> <script src="../../ruby-head-wasm-wasi/dist/browser.script.iife.js"></script> <script type="text/ruby"> require_relative "a" puts "Hello, world!" </script> </html>
a.rbを読み込んだあとにHello, world!
が表示されます。
RubyでJavaScriptのPromiseを待てています。
つぎに読み込むファイルを増やします。
<html> <script src="../../ruby-head-wasm-wasi/dist/browser.script.iife.js"></script> <script type="text/ruby"> require_relative "a" require_relative "b" puts "Hello, world!" </script> </html>
つぎに、a.rbを経由してb.rbを読み込んでみます。
require_relative "b" p "a loaded!"
a.rbを読み込んだあとにHello, world!
が表示されてほしいです。
this._vm.evalAsync(script.ScriptBody)
の中でthis._vm.evalAsync(script.ScriptBody)
するのがよくないのでしょうか?