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
は正常に開けますが🍣.txt
はno 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の扱いにバグがあることが原因です。
- 2023年7月にリリースされたlibuv 1.46で、WTF-8を扱うコードが導入されたが、この変更にビットマスク忘れの凡ミスがあった。
- すぐに修正され、2023年11月にlibuv 1.47としてリリースされた。
- そのため、libuv 1.46を使っている場合にのみ問題となる。
Node.jsの特定バージョンでは、問題のlibuv 1.46が使われています。
- Current(21)
- LTS(20)
- 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) 作成でやりたかったことの実現方法まとめ