Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails以外の開発一般

Electronでローカルファイルを扱うWindowsアプリは、v29にアップデートする前に気をつけよう

ElectronというかNode.jsの問題です。

リリースタイムラインによると2024/02/20にStableになる予定のElectron 29から、Node.jsが20系にアップデートされる可能性があります。

※記事執筆時点ではリリースタイムラインページではv18.19と記載されていますが、Electron v29.0.0-beta.3ではNode.js 20.9.0が採用されています。

Windows版Node.jsのうち、以下のバージョンには、fsにおいてWTF-8の扱いにバグがあり、サロゲートペアが含まれるファイルパスを開こうとすると存在しない扱いになります。

  • v18.18.0
  • v20.4.0 以上、20系対応バージョン未定

実験

試してみましょう。起動したらOpenFileDialogを表示し、選択されたファイルを開くだけのElectronアプリを作ってみます。

index.js

const { app, BrowserWindow, dialog } = require('electron/main')
const fs = require('node:fs')

app.whenReady().then(() => {
  const win = new BrowserWindow({
    width: 800,
    height: 600
  })

  dialog.showOpenDialog().then(result => {
    if (!result.canceled) {
      const data = fs.readFileSync(result.filePaths[0])
      console.log(data)
      win.loadURL('data:text/plain;charset=utf-8,' + data.toString('utf-8'))
    }
  })
})

app.on('window-all-closed', () => {
  app.quit()
})

package.json

{
  "scripts": {
    "start": "electron ."
  },
  "dependencies": {
    "electron": "^29.0.0-beta.3"
  }
}

このアプリで適当なテキストファイルを開いてみると、あいうえお.txtは正常に開けますが🍣.txtno such file or directoryになります。

(node:11932) UnhandledPromiseRejectionWarning: Error: ENOENT: no such file or directory, open 'C:\Users\baba\Desktop\坤.txt'
    at Object.open (node:internal/fs/sync:78:18)
    at Object.openSync (node:fs:565:17)
    at Object.func [as openSync] (node:electron/js2c/node_init:2:2214)
    at Object.readFileSync (node:fs:445:35)
    at t.readFileSync (node:electron/js2c/node_init:2:9771)
    at C:\Users\baba\workspace\tmp\electrontest\index.js:12:23
(Use `electron --trace-warnings ...` to show where the warning was created)
(node:11932) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)

原因

Node.jsが依存するlibuvの特定バージョンでWTF-8の扱いにバグがあることが原因です。

Node.jsの特定バージョンでは、問題のlibuv 1.46が使われています。

  • Current(21)
    • 21.0.0(2023/10/18リリース)時点で問題があることを確認。おそらくlibuv 1.46にした20.x(4 <= x <= 10)からフォークしている。
    • 21.5.0(2023/12/20リリース)でlibuv 1.47に更新されているので、21.5.0以降は問題ない。
  • LTS(20)
    • 20.3.0(2023/6/8リリース)はlibuv 1.45を使っているので、問題ない( https://github.com/nodejs/node/pull/50036 で触れている別の問題があるのかもしれないが未確認)。
    • 20.4.0(2023/7/6リリース)はlibuv 1.46を使っているので、問題がある。
    • 記事執筆時点で最新の20.11.0(2024/01/10リリース)でもlibuv 1.46のままで、問題が残っている。
    • つまり、20系は20.4.0以降、最新版まで問題がある。
  • LTS(18)
    • 18.17.0(2023/7/19リリース)や18.17.1(2023/8/10リリース)は問題がなかった、おそらくlibuv 1.44を使っている。
    • 18.18.0(2023/9/19リリース)はlibuv 1.46を使っているので、問題がある。
    • 18.18.1(2023/10/11リリース)でlibuv 1.46への更新がrevertされて1.44に戻っているので、18.18.1や18.18.2は問題ない。
    • 18.19.0(2023/11/30リリース)ではリリースノートで特に触れていないが、動作確認したところ問題ないので、libuv 1.44の状態と思われる。
    • つまり、18系は18.18.0だけが問題あると考えられる。

ElectronではNode.jsを同梱しています。

  • 最新Stableの28.2.0では、Node.js 18.18.2を使っているので問題ない。
  • 最新Betaの29.0.0-beta.3では、Node.js 20.9.0を使っているので問題がある。

対応

Node.js 18系はすぐに修正が取り込まれたのですが、Active LTSである20系にはなかなか取り込まれません。20.11.0のときにリリースタスクで言及もされたのに、スルーされてしまったようです。

現時点では、Node.js 20系の次の更新で修正が取り込まれることを祈ることになりますが、Electronに含まれるNode.jsのバージョンを利用者が選ぶことはできないので、あまり長期間取り込まれないと困ったことになりそうです(Electronをずっと更新しないわけにもいかない)。

あまり良い回避策は思いつきませんでした。

さすがにVSCodeがElectron 29を使う頃に直っていなければ問題になって報告が殺到すると思われますが、VSCodeはInsiderでもElectron 27だったので、かなり保守的に使っているようです。

生産性がない Any update on this? のコメント爆撃をしても仕方ありませんが、リアクションを付けたりして注目しておきたいと思います。

関連記事

electron-builder による Windows インストーラー (NSIS) 作成でやりたかったことの実現方法まとめ


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。