背景(と先行事例)
ruby.wasmでrequire_relativeを使えるようにしたい
https://speakerdeck.com/ledsun/load-gem-from-browsr
RubyKaigiのレポート読んでたら見つけて、幾つか気になった点があったので(もうできてるみたいだったけど)ちょっと試してみました
requrie_relativeを改変すると標準gemの読込ができなくなる
できるよ
require_relative を上書きした時点で呼び出し元のパスが変わるためエラーになるとのこと
るりまにも
現在のファイルからの相対パスで require します。
って書いてあるし一見できなさそうではあるけれども、これ絶対パス渡してもちゃんと動くので
module Kernel
alias origin_require_relative require_relative
def require_relative(path)
caller_path = caller_locations(1, 1).first.absolute_path || ''
dir = File.dirname(caller_path)
file = File.absolute_path(path, dir)
origin_require_relative(file)
rescue LoadError
require_relative_url(path)
end
end
みたいにして、上書き時点で caller_locations でも使って呼び出し元パスを用意しておけば上書きしても多分ヨシ!
スクリプトのrequire_relative
全部置き換えるよりかは素直なんじゃないかな
fetchが非同期関数
fetch?知らない子ですね
…… いやjavascriptはあまり詳しくないというか大分昔に触ったっきりだったのでこう、ね?
XMLHttpRequest とかいう、もはやあまり使われない気がする関数使えば同期/非同期の指定ができるから、こっち使えば行けなくもないでしょというか
結果
require 'js'
module JSRequireRelative
module_function
def import(file)
@pathlist ||= ['']
file = "#{file}.rb" unless file.end_with?('.rb')
path = File.absolute_path(file, @pathlist.last)
@pathlist << File.dirname(path)
path = ".#{path}" if path.start_with?('/')
evalfile(path)
@pathlist.pop
end
def evalfile(path)
jsscript = <<~JAVASCRIPT
const xhr = new XMLHttpRequest()
xhr.open('GET', '#{path}', false)
xhr.send()
return xhr.response
JAVASCRIPT
script = JS.eval(jsscript).to_s
::TOPLEVEL_BINDING.eval script
end
end
module Kernel
alias origin_require_relative require_relative
def require_relative(path)
caller_path = caller_locations(1, 1).first.absolute_path || ''
dir = File.dirname(caller_path)
file = File.absolute_path(path, dir)
origin_require_relative(file)
rescue LoadError
JSRequireRelative.import(path)
end
end
実際使うならもうちょっと(特にXHRのとこ)ちゃんと書いた方がいいと思うけど
だいたいこんな感じのをスクリプトの頭に突っ込んでおけば require_relative を(ちょっとお試し程度に)使えるようにできそう
まあWASIにソケットが搭載されれば必要なくなる気もする
require は vender/bundle あたりを対象に同じことする感じになるかな …… ならないかも ……