Possible causes for errors in packaging an Electron App

While trying to build and package your Electron application, you may occasionally encounter file load errors. Your application works perfectly in development but the distribution returns nothing more than a blank white page.

You may get error messages such as Unable to load preload script: … resources/app.asar/preload.js, Not allowed to load local resource: file://src/index.html or even Application entry file “dist/main.js” in the linux-unpacked/resources/app.asar” does not exist. Seems like a wrong configuration.

All of the above-mentioned error messages stem from file path problems. Your packaged Electron application requires absolute paths to the HTML file, modules and extra resources. And the paths in the distribution package are not always the same as those in the development.

There are a NodeJS variable and Electron functions to avoid this problem. In whichever file (or on whichever operating system) you invoke your NodeJS process, __dirname always returns the absolute path of where you start it. Electron app API allows you to get and set paths to your application resources.

Instead of using the relative path that can cause an error in production, you can set up the paths using __dirname in the main process.

const path = require('path');
const glob = require('glob');
const { app, BrowserWindow } = require('electron');

let mainWindow:any = null;

function initialize ():void {
  makeSingleInstance();

  function createWindow () {
    const windowOptions = {
      width: 1080,
      minWidth: 680,
      height: 840,
      title: app.getName(),
      webPreferences: {
        nodeIntegration: false
      }
    }

    mainWindow.loadURL(path.join('file://', __dirname, '/index.html'));
}

If your application won’t load the HTML file using __dirname, your problem presumably won’t be related to Electron.

When bundled by Webpack, the variable __dirname doesn’t automatically show the path to build resources but to your project root, or a single forward slash ‘/’. This may be the cause of your trouble. In this case, you just need to open your webpack config and set false to node: {__dirname: }.

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {

  ...
  context: __dirname,
  node: {
    __dirname: false
  },

}

Then __dirname will return the path to the current application directory. Neither setting “asar”: false nor specifying “devEngines” is required. app.getPath(name) works in the same manner. This function is really useful for saving application settings and user data. Quite apart from userData, where your app’s configuration files are stored, it returns the absolute paths to User’s home, Desktop, Downloads and Documents as well.

All the Electron Docs! | Electron

For your reference, here is my version information.

$ node --version
v12.16.3

$ yarn --version
1.22.5

$ npm --version
6.14.8

$ tsc --version
Version 2.9.2

$ webpack --version
4.44.1

$ grep electron package.json 
    "electron-debug": "^3.1.0",
    "electron-devtools-installer": "^3.1.1",
    "electron-log": "^4.2.4",
    "electron-store": "^6.0.0",
    "electron-updater": "^4.3.5",
    "electron": "^10.1.3",
    "electron-builder": "^22.8.1",

$ cat /etc/os-release 
NAME="Ubuntu"
VERSION="18.04.5 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.5 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic

盛夏の伊豆大島

生来的に引きこもることができない性格なので、長らく続けていた自粛生活の最中でも「おいで」と言われると反射的に出かけてしまいます。

そのことを今さら敢えて記述する必要もないかとも思われましたが、こうして東海汽船も観光キャンペーンを始めたので、今だからこそ書き残しておくのも悪くはないかと思われた伊豆大島の鮮明な記憶。

時を遡って7月末。来島自粛が解除されて6週間が過ぎ、長らく降り続いた雨が去った後。

雑な季節調整が入って気温が 30℃ を越えた超えた頃、新造貨物船 SALVIA III こと三代目さるびあ丸に自転車を載せて南海の島に向かいました。

竹芝の客船ターミナルは人気も疎らで、至るところに張り出された感染症対策の告知がただならぬ雰囲気を醸し出し

ていたのは最初だけで、出航1時間前になると家族連れ、釣り人、ダイビングサークルらしき大人数の集団が続々と集結して、ターミナル内で他人と間隔をあけることすら難しいほどに密な空間になりました。

どうやら伊豆大島や神津島に向かう船と八丈島へと向かう船に乗る船客がいるらしく、とくに後者は大人数の若者と派手なサーフボードが印象的で、真夏の島嶼部の本来の活況が目に浮かぶように思われました。

呆気にとられていると、しばしの間を置いて乗船が始まりました。乗り込むのは今年6月に新規就航したばかりのさるびあ丸です。

客船ターミナルから屋外に出て 2m の間隔を空けて乗船口に並び、検温を経て乗り込んだ新しい船は驚きと感動に満ちていました。

かつて階段の下に並べられていた大型手荷物(自転車やサーフボードなど)には専用の手荷物置き場が用意され、売り切れのまま放置されていたアメニティグッズの自販機は全てが刷新され、食堂も展望重視の窓がいくつも設置されていました。

客室も新築のようにきれいで快適です。ただし、食堂が充実したためか、カップ麺や冷凍食品のような軽食を扱う自販機はなくなっていました。

伊豆大島など近距離の島に向かう場合、夜間に出航して早朝に到着するうえに島内に早朝営業している飲食店などは存在しないため、船内で補給食を調達できないのは少し厳しいと思われました。




新しい船に夢中になって、なかなか寝つけなかったことなどお構いなしに朝はやってきます。夜間に東京港を出航した船の大島到着時刻は翌6時。この時期は朝日が昇った直後に到着です。

海上から眺める日の出はいつでも最高。

起床の放送が入ると間もなく大島で下船する船客で出入り口付近は大混雑です。

あらためて夏季の来島者の数に驚かされます。前回、大島を訪れた際は椿まつりの時期でそれなりに賑わいはあったものの、今回とは比較になりません。

大勢の団体客をさきに下船させて、自転車を抱えて一番最後に降り立ったことでようやく到着した伊豆大島。乗船時のような検温も宿泊施設の確認もなく、拍子抜けするほどあっさりと上陸できてしまいました。

じつは、このとき連れが二人分を予約してしまっていた関係で、帰りの船も宿泊施設も予約しておらず、それが原因で上陸を断られるのではないかと少しだけ考えていました。

そんな懸念をよそに船客の下船を終えた船は、つぎの目的地へと向けて早々と出航していきました。つぎは利島か新島か。最終的に式根島と神津島を経由して、また大島そして東京へと戻っていきます。

こちらは現地集合で待合せからの大島探索ライドに出発です。ご存知の通り、伊豆大島にはコンビニエンスストアがありません。しかし、朝 8:30 まで待てば売店の営業も始まり、それなりに補給食の調達も可能となります。

まずは動物園付属の椿資料館にて朝食のアイスクリームを確保。直後に訪れる裏砂漠の山岳地帯に向かうために必要な糖分と水分を摂取します。

大島の外周部では北東の動物園と南東の波浮港とのあいだが山がちで、大変ながらも走っていて気持ちが良い区間が続きます。その一方で途中休憩できる場所はありませんので事前準備も重要です。

糖分補給を終えて飲料水を購入したら、いよいよ裏砂漠へと足を進めます。

気温と湿度が高いため展望は幾分か悪くなっているものの、三原山まで見渡せる快晴と直射日光を遮る生い茂った木々、海から吹き付ける微風は悪くはありません。

午前中の澄んだ空気も相まって気分は最高です。ここまで来たのは、これで4回目になることもあって途中で立ち止まって撮影することもなく、上機嫌で裏砂漠を通り抜けて波浮港へと一気に降ります。

波浮港は内浦湾のような見事な噴火湾の内部にあり、時計回りで大島を一周する場合にはダウンヒルで全容を拝めるので一見の価値ありです。

この辺りには市街地が広がっていて島内では数少ない信号や交差点もあるので、個人で訪れる時には私は迂回することが多いのですが、地形も見事ですし、水面も美しいので立ち寄ってみるのも悪くありません。

コロッケで有名な鵜飼商店や明治時代の史跡である旧甚の丸邸はここにあります。




我々が立ち寄った際には地元の子どもたちが海に飛び込んでいたり、その横を悠然とフグが泳いでいるのを見かけたり、いかにも夏休み然とした情緒がありました。

ここからは大島の郊外景色を眺めつつ、若干の登りが続きます。

歴史のある波浮港と現在の中心市街地である元町とを結ぶ南部のこの区間は、さながら大島の幹線道路であり、交通量もそれなりにあり、周辺の景色も人が暮らしていることを感じさせる場所が多いです。差木地あたりには売店もあります。

そうした市街地や郊外風景を走り抜け、やがて民家が途切れたあたりに突如として現れるのが大島を象徴する地層大切断面です。

この辺りまでやって来ると利島や伊豆半島、富士山などが水平線上にはっきりと見えてくるものですが、さすがに気温30℃超では霞んでしまって遠景は拝めないようです。

つまりは冬にまた来ましょうということです。

大切断面からの切り通しを抜けると、ほどなくして御神火スカイラインの入り口が見えて、すぐに元町の市街地へと辿り着きます。

町役場や警察署、裁判所や都立高校などの施設が集まる大島の中心地です。

ここで名物の鼈甲寿司といきたいところですが、アレルギーで食せないうえにそもそも海産物おいしいなんて思わないので、そちらは相方に任せて昼食は唐揚御膳で決まりです。

大島で唐揚げを食する必然性はありませんけれども見た目どおりに美味でした。

その後は相方の体力などを考慮して、午後は温泉で汗を流すことに決まりました。感染症対策で利用できないのではないかとも危惧されたところは、施設利用前に検温して現住所を提出すればとくに問題ありませんでした。

なにやら島民とそれ以外で利用時間を分けるなどの対策をしているという話を伝聞で知ります。

感染症対策と自粛期間で大きく変わってしまったのではないかという諸々の心配とは無縁のように、元来の雰囲気やおおらかさをそのまま残している島内を見ていると安心感を覚えます。

その後は着替えて元町の市街地を散策してみたところ、こちらも繁忙期にも関わらず島民の親切さや人の良さは相変わらずです。行政区分としては東京都なので支援も受けられずに大変だと思うのですが、火山災害や台風から復興してきた島民は強いと頭の下がる思いです。

来たらお金を落とすということで万人受けしそうな地酒『御神火』や明日葉サンドを買い込んで荷物の中にしまい込みました。

こうしてあらためて考えてみると伊豆大島は実に見どころ豊富です。

とくに夏季は海水浴にキャンプにサーフィンと多種多様な目的で人が訪れるため、本来なら今よりもっと活気に満ちているであろうことが容易に想像されました。

そして結局、今回も火口まで行くことはなかったなとやり残したことをそのままにしてしまった気分です。まあ御神火スカイラインも復旧工事により途中通行止めだったので。

React コンポーネントに history がないときに確認すること

ReactJS を使ってシングルページアプリケーション(SPA)を作成する際、コンポーネント内に props.history が見当たらないことがあります。

$ tsc

src/components/User/Upsert.tsx:53:16 - 
error TS2339: Property 'history' does not exist on type 'Readonly<{}> & Readonly<{ children?: ReactNode; }>'.

53     this.props.history.push('/user/list');
                  ~~~~~~~
プロパティ 'history' は型 'Readonly<{}> & Readonly<{ children?: ReactNode; }>' に存在しません。

例えば CRUD (DBMS) 操作を行った直後にリダイレクトを行う際などに、このエラーが生じるとページ遷移も DOM の書き換えも行われません。

そこでエラー表示を少し調べてみると this.props.history が見当たらないことはありえないとか、withRouter を使ってリダイレクトする方法があるなど、いろいろな(自然)言語で好き勝手なことが書かれていることが分かります。

しかし、このエラー表示が生じた環境において TypeScript をお使いの場合、コンポーネント、つまり props や state の各変数の型定義が行われていないことを最初に疑った方が良いかも知れません。

つまり interface にてコンポーネント内の変数の型を自身で定義して React.Component <{},{}> に渡すと問題が解決する場合があります。

以下に具体例を提示します。

interface IProps {
  history:string[],
}

interface IState {
  params:Array<object>,
}

class Upsert extends React.Component<IProps,IState> {
  constructor(props){
    super(props);
    this.state ={
      params:[],
    }
  }

}
export default Upsert;

少し考えてみると自明なことなのですが、ドキュメントを読み返してみても原因らしきものが思い当たらなくて困惑しました。最終的に解決に役立ったのはこちらです。


TypeScript and React – Hello React and TypeScript
https://charles-bryant.gitbook.io/hello-react-and-typescript/typescriptandreact


実行環境は下記のとおりになります。

$ tsc --version
Version 2.9.2

$ node --version
v12.16.3

$ npm --version
6.14.8

$ grep react package.json 
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "react-router-dom": "^5.2.0",
    "react-scripts": "^3.4.1",
    "react-select": "^3.1.0",
    "@types/react": "^16.9.49",
    "@types/react-dom": "^16.9.8",
    "@types/react-router": "^5.1.8",
    "eslint-plugin-react": "^7.21.1",
    "react-devtools": "^4.8.2",

この分野は変化が激しく、毎年のように仕様が変わるので解決法をお探しのかたは記事の書かれた時期とバージョンに細心の注意を払ってください。

最後に繰り返しになりますが、私はスクリプト言語もフロントエンドも心の底から嫌いです。嫌いなので仕事では絶対に触りたくありません。