Tauriのアプリ開発でドツボにハマッた部分or気になった部分と対策法

[この記事](https://qiita.com/Odashikonbu/items/3818384c7cdc19121f1a)でTauri V2をベタ褒めしましたが実際には開発でつまづく所もあったので解決方法含めて備忘録として残しておきます。

### img src="..."でローカルファイルが参照できない。
タイトルの通りです。
TauriではElectronと同じようにhtmlをアプリ内のローカルホストを使って表示しているのですが、このローカルホスト内からパス表記のローカルファイルは参照できません。
下記の記述ではTauriの画面上で画像がリンク切れになります。
```html
<img src="path/to/aaa.png" />
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3923046/5ee5fbb0-9e0f-17a4-e0b9-77ce0f642836.png)


以下のような感じでコンソールから怒られています。
```terminal
localhost/:1  Not allowed to load local resource: file:///Path/to/AAA.png
```
解決するにはTauriの標準機能「convertFileSrc」を使ってファイルパスをURIに変換してあげる必要があります。
```typescript
import { convertFileSrc } from '@tauri-apps/api/core';

ImageURL = await convertFileSrc('Path/to/AAA.png')
```

ただし、この処理だけではtauri devした時のデバッグ画面では正常に表示されるものの、リリースした.exe上では接続拒否のエラーが出てやはり表示されません。

```
Failed to load resource: net::ERR_CONNECTION_REFUSED
```
これはブラウザのCSP(コンテンツセキュリティポリシー)にローカルURIの画像が違反しているため、アクセスが拒否されているのが原因です。
解決するにはsrc-tauri/tauri.conf.jsonに以下の設定を追加します。

```json:tauri.conf.json
  "app": {
    "security": {
      "csp": {
        "img-src": "'self' asset: http://asset.localhost blob: data:"
      },
      "assetProtocol": {
        "enable": true,
        "scope": ["**"]
      }
    }
  }
```

画像以外のCSP設定も[公式ガイド](https://v2.tauri.app/security/csp/)に載っているので、動画や外部CSS等を読み込む際につまづいたら参考にしてみてください。

### fsプラグインでファイルやパスの読み書きができない
プラグインを導入しimportして問題なく処理を実装したのに実際の画面上ではエラーを吐くことがあります。

```typescript
import { readFile } from '@tauri-apps/plugin-fs';

const file = await readFile('path/to/aaa.txt')
```
エラーメッセージはこんな感じ
```terminal
Uncaught (in promise) forbidden path: path/to/aaa.txt
```

これはプラグインの機能に適切な権限のスコープ設定がされていないのが原因です。
解決するにはsrc-tauri/capabilities/default.jsonに下記のような権限スコープを追加します。

```json:default.json
  "permissions": [
    "core:default",
    "fs:default",
    {
      "identifier": "fs:allow-read-file",
      "allow": [
        {
          "path": "**"
        }
      ]
    },
 ]
```
"allow"の"path"には権限が適用されるディレクトリ階層を指定します。
私は作っているツールがそれほど重要なリソースに触れるわけでもない単純な物なので「"**"」でフル権限を付与しましたが、
作るツールの用途に合わせて都度微調整してください。

### 実行exeのパスが取得できない

TauriのresourceDirで取得できます。
```typescript
  import { resourceDir } from "@tauri-apps/api/path";

  await resourceDir()
```

### ビルド時にインストーラー無しのexeだけ出力したい。
-no-bundleオプションを付けるだけ
```terminal
bun tauri build --no-bundle
```

### (2024/11/7追記) invokeでrustの関数を呼び出す際に引数の指定が通らない。
Tauriのinvokeを使ってrust側の処理を呼び出す際、以下の関数に対してinvokeで引数を指定するとエラーになることがあります
```rust
#[tauri::command]
fn resize_image(mod_image: String, origin_image: String) -> Result<(), String> {
～省略～
}
```

```typescript
  import { invoke } from '@tauri-apps/api/core';

  await invoke("resize_image", {mod_image: modImageFile, origin_image: originImageFile})
```

rustのコードと同じ引数を指定しているはずなのに何故か以下のエラーが出てしまいます。
```
Uncaught (in promise) invalid args `modImage` for command `resize_image`: command resize_image missing required key modImage
```

よく見るとrustではmod_imageとなっている引数の指定がmodImageになっていますね。
実はこれ、Tauriが勝手にrustの引数を解釈し、スネークケース(_区切り)をキャメルケース(大文字区切り)に変換しています。
変換された引数に従ってmod_imageをmodImageとして渡してあげると無事通ります。
```typescript
await invoke("resize_image", {modImage: modImageFile, originImage: originImageFile})

```

invokeでrustの処理を呼び出す際はこの一点だけ注意しましょう。

##
### まとめ
結構触ってみたけど本当に困ったのはCSP周りの設定ぐらいですね。
ビルド時間も比較的早かったので不便さはありませんでした。