RubyでChrome Extensionを作れるフレームワーク、"unloosen"を使ってContent Sctiptを作成した

## これは何

RubyKaigi　2023で[unloosen](https://github.com/aaaa777/unloosen/)(Ruby + wasmでChrome Extentionを作るためのフレームワーク)についての発表がありました。

RubyKaigi 2023での発表の詳細は下記です。

https://rubykaigi.org/2023/presentations/3yUma%E2%82%8Brb.html#day1

こちらの内容が面白く、実際に触ってみたという内容になります。
作ったサンプルは↓になります。

unloosenについては既に紹介記事が書かれている為、詳しい内容はそちらをご覧いただければと思います。

https://qiita.com/tomoasleep/items/3feef6898e849af3600a

## 何を作ったか

マークダウンにURLなどを貼るとき、`[Title](URL)` <- こちらのフォーマットで貼りたい時が多々あります。
h1タグ(ページのタイトル)と URLを取得して、`[Title](URL)`のフォーマットでクリップボードにコピーするボタンをChrome Extentionで追加するChrome Extentionを作成しました。

https://twitter.com/fussy113/status/1657631028931080194

作成したサンプルのコードはこちらになります。

https://github.com/fussy113/unloosen-sample

```json:manifest.json
{
  "manifest_version": 3,
  "name": "unloosen-sample",
  "description": "unloosen-sample",
  "version": "0.0.1",
  "content_scripts": [
    {
      "matches": [
        "http://www.example.com/"
      ],
      "js": [
        "node_modules/unloosen-ruby-loader/dist/entry/loader-content-script.esm.js"
      ]
    }
  ],
  "content_security_policy": {
    "extention_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'"
  },
  "web_accessible_resources": [
    {
      "resources": [
        "*",
        "node_modules/unloosen-ruby-loader/dist/*"
      ],
      "matches": [
        "<all_urls>"
      ]
    }
  ]
}
```

```json:unloosen.config.json
{
  "application": "app.rb",
  "ruby.wasm": "./node_modules/unloosen-ruby-loader/dist/ruby.wasm",
  "content-script-entry": "./node_modules/unloosen-ruby-loader/dist/entry/module-content-script.esm.js",
  "remote-require": false
}
```

```ruby:app.rb
require 'unloosen'

content_script site: 'www.example.com' do
  h1_content = document.querySelector("h1")
  url = window.location.href.inspect[1...-1]
  clip_text = "[#{h1_content.textContent}](#{url})"

  # Create input element
  input = document.createElement("input")
  input.type = "text"
  input.style = "position: absolute; left: -1000px; top: -1000px;"
  input.value = clip_text

  # Create button element
  button = document.createElement("button")
  button.textContent = "Copy [title](url)"

  button.addEventListener("click") do
    input.select()
    # 非推奨の書き方だが、HTTPではnavigator.clipboardが使えない、試しで書いているので、これで進めている...
    document.execCommand("copy")
    puts "Copied!"
  end

  flagment = document.createDocumentFragment()
  flagment.appendChild(input)
  flagment.appendChild(button)

  h1_content.appendChild(flagment)
end
```

長々となってしまっていますが、やっていることは、下記です。

- h1要素と、URLを取得
- 追加するinput要素、button要素の作成してDOMを追加する

実装をしていて気づいたことをあげます。

- unloosenがRuby wasmでjsを取り扱う部分をラップしてくれている

`document.querySelector("h1")` と記載している箇所、普通にRuby + wasmで書く場合は、`JS.global[:document].querySelector("h1")`と書く必要があります。
unloosenが扱いやすいよう、aliasを用意しているため、コードのようにシンプルに書くことができています。([Ref](https://github.com/aaaa777/unloosen/blob/main/lib/unloosen/toplevel_alias.rb))

## 使ってみて

- unloosenがRuby + wasm、Chrome Extentionの気になるところをサポートしてくれていると感じた
  - シンプルに記述ができる
  - Chrome Extentionを作る際にJavaScriptをほぼ触らず、Rubyで作っていくことができるのは、とても面白いなと感じます。
- 開発の体験の良さ
  - Extentionを読み込んだ後、コードの変更した際Extentionのリロードの必要がなく、ブラウザを読み込み直すだけで反映がされるので、シンプルにtry & errorを試せるのが良いなと思いました。
    - コンパイル、bundleを必要としない今のシンプルな構成、Rubyだからこその利点を感じました。

ruby.wasm、unloosen共にドキュメントがまだまだ少ないところではありますが、これからruby.wasmを触ってみたい、RubyでChrome拡張作るのに興味がある方に、ぜひ一度触ってみることをお勧めしたいです。