• 作成:

sentry-electron 0.5.5が`TypeError: module.require is not a function`で起動できない問題を調査しました(未解決)

@sentry/electron - npmのバグ調査をしました.

前に0.5.4が壊れているのをissueで報告しました.

I update to 0.5.4, error on MODULE_NOT_FOUND · Issue #81 · getsentry/sentry-electron

issueがcloseされたから問題は解決したと思ってv0.5.5にアップグレードしました.

他の問題が発生しました.

% y
yarn run v1.7.0
$ cross-env NODE_ENV=development electron .
App threw an error during load
TypeError: module.require is not a function
    at new ElectronFrontend (/home/ncaq/Desktop/foo/app/webpack:/node_modules/@sentry/electron/dist/dispatch.js:38:1)
    at Object.initAndBind (/home/ncaq/Desktop/foo/app/webpack:/node_modules/@sentry/core/dist/sdk.js:24:1)
    at init (/home/ncaq/Desktop/foo/app/webpack:/node_modules/@sentry/electron/dist/sdk.js:51:1)
    at Object../src/sentry.js (/home/ncaq/Desktop/foo/app/webpack:/src/sentry.js:3:1)
    at __webpack_require__ (/home/ncaq/Desktop/foo/app/webpack:/webpack/bootstrap:19:1)
    at Object.<anonymous> (/home/ncaq/Desktop/foo/app/webpack:/src/main.js:18:1)
    at Object../src/main.js (/home/ncaq/Desktop/foo/app/main.js:64808:30)
    at __webpack_require__ (/home/ncaq/Desktop/foo/app/webpack:/webpack/bootstrap:19:1)
    at /home/ncaq/Desktop/foo/app/webpack:/webpack/bootstrap:83:1
    at Object.<anonymous> (/home/ncaq/Desktop/foo/app/main.js:87:10)
A JavaScript error occurred in the main process

module.requireは存在しないということ.

とりあえず/node_modules/@sentry/electron/dist/dispatch.jsに行って, module.requirerequireに書き換えてみます.

起動はしました.

webpack環境だとmodule.requireは無効化されているのでしょう. そもそも何故requireじゃなくてmodule.requireが使われているのかわからない. 両者の違いはなんだ…?

その場所のコメントを読みます.

We dynamically load the frontend implementation for the current process type. In frontend bundlers such as webpack or rollup, those requires are resolved statically. For this reason, we use module.require for the main implementation here, which is only defined in the main process. The renderer implementation must use the default require.

In case process.type is not defined, dispatch defaults to the renderer implementation, which should be fine for most cases. False positives of this would be running @sentry/electron in a bare node process, which is acceptable.

sentry-electron/dispatch.ts at 326c7a0625f3ae650d390ddd0d667b0da7ea3f99 · getsentry/sentry-electron

現在のプロセスの種類向けのフロントエンド実装を動的にロードします. webpackやrollupなどのフロントエンド·バンドラーではrequireたちは静的に解決されます. このため, main実装ではmainプロセスのみに定義されているmodule.requireを使います. renderer実装ではデフォルトのrequireを使用します.

process.typeが実装されていない場合, dispatchのデフォルトはrendere実装になります. これはだいたいの場合において大丈夫です. この偽陽性で生のnodeで@sentry/electronが動くようになります.

動的ローディングしたいのは伝わってきました. module.requireとは何だ?

module.require(id) Added in: v0.5.1

  • id <string>
  • Returns: <Object> module.exports from the resolved module

The module.require method provides a way to load a module as if require() was called from the original module.

In order to do this, it is necessary to get a reference to the module object. Since require() returns the module.exports, and the module is typically only available within a specific module's code, it must be explicitly exported in order to be used.

Modules | Node.js v10.6.0 Documentation

module.requireメソッドは, 元のモジュールからrequire()が呼び出されたかのように, モジュールをロードする方法を提供します。

これを行うには, モジュールオブジェクトへの参照を取得する必要があります。 require()module.exportsを返すので, 通常は特定のモジュールのコード内でのみ使用可能です。これは明示的にエクスポートして使う必要があります。

???requiremodule.requireの違いが全くわからない. 調べても出てこないので諦めました. module.requireで生のrequireを呼び出そうとしていることは伝わってきますが.

NodeのREPLではmodule.requireは存在します.

> module.require
[Function]

そしてwebpackでビルドするとmodule.requireは消滅します.

試しにmain部分にmodule.require('path');を書いてみると, 同じくTypeError: module.require is not a functionになります.

webpackにmodule.requireを提供する意志があるのか検索しました.

module.require

i cannot provide it, because it is not possible to resolve via static dependency resolution. It will stay undefined. Align module-object to node's module-object · Issue #20 · webpack/webpack

私はそれを提供できません, なぜならこれは静的な依存関係の解決によっては解決できないからです。これは未定義のままです.

そりゃ動的ロードだから解決できないですね.

この変更が加わったのは fix: Dispatch correctly for bundlers like webpack by jan-auer · Pull Request #84 · getsentry/sentry-electron です.

前のissueである I update to 0.5.4, error on MODULE_NOT_FOUND · Issue #81 · getsentry/sentry-electron でもmodule.requireが動かないと警告していたのですがスルーされてしまいました.

とりあえずここを元に戻してしまおうとmodule.requirerequireに戻しましたが, すると元のエラーが発生するんですね.

% y
yarn run v1.7.0
$ cross-env NODE_ENV=development electron .
Installing Devtron from /home/ncaq/Desktop/foo/app
info: Added Extension: React Developer Tools
info: Added Extension: Redux DevTools
{ Error: Cannot find module '/home/ncaq/Desktop/foo/package.json'
    at webpackEmptyContext (/home/ncaq/Desktop/foo/app/webpack:/node_modules/@sentry/electron/dist/main sync:2:1)
    at getPackageJson (/home/ncaq/Desktop/foo/app/webpack:/node_modules/@sentry/electron/dist/main/context.js:80:1)
    at /home/ncaq/Desktop/foo/app/webpack:/node_modules/@sentry/electron/dist/main/context.js:240:25
    at Generator.next (<anonymous>)
    at fulfilled (/home/ncaq/Desktop/foo/app/webpack:/node_modules/@sentry/electron/dist/main/context.js:4:42)
    at <anonymous> code: 'MODULE_NOT_FOUND' }
{ Error: Cannot find module '/home/ncaq/Desktop/foo/package.json'
    at webpackEmptyContext (/home/ncaq/Desktop/foo/app/webpack:/node_modules/@sentry/electron/dist/main sync:2:1)
    at getPackageJson (/home/ncaq/Desktop/foo/app/webpack:/node_modules/@sentry/electron/dist/main/context.js:80:1)
    at /home/ncaq/Desktop/foo/app/webpack:/node_modules/@sentry/electron/dist/main/context.js:240:25
    at Generator.next (<anonymous>)
    at fulfilled (/home/ncaq/Desktop/foo/app/webpack:/node_modules/@sentry/electron/dist/main/context.js:4:42)
    at <anonymous> code: 'MODULE_NOT_FOUND' }
Done in 7.34s.

なるほど動的ロードする意味はあったというわけですね.

しかしmodule.requireを使ってしまうとエラーになるのは変わらないので, 何か別のもので置き換えることを試みました.

動的ロードと言えばdynamic importだろうということで書き換えようとしたのですが, initElectronFrontend.constructorasync関数ではなく, dynamic importはPromiseを返してくるからロードを待つ方法がわからないです.

awaitとかじゃなくて素直にロードをwaitする方法は無いのかな? ロードを待てないとElectronFrontend#innerがnullableになってしまい, 他のメソッドが正常に動かなくなる危険性も生まれます.

そもそもinitが完了するまで実行を待たないとおかしなことになります.

というかなんでJavaScriptのPromiseは素直にコンテキストを選ばずに終了を待てないんでしょう. 他の言語のThread系の機能は普通にそれ出来ますよね. 単純に待つAPIを実装できない理由はあるんでしょうか. 起動を並列化したい理由も無いですし, 単にPromiseの終了をwait出来れば全て解決するのですが. 本当に待てないのでしょうか? 私が知らないだけ?

EcmaScriptの提案にはトップレベルにimportが書けるものがあるようですが. tc39/proposal-top-level-await: top-level await proposal for ECMAScript (stage 2)

これのつらさは実行環境を見て, mainとrendererを大規模に分岐させようとしているから発生していると思うんですよね. 素直にmainInitrendererInit関数を用意すれば静的に依存パスが定まって楽になるのでは.

いくつか設計を修正する案は思いつきましたが時間切れなのでissue建てて報告だけしておきましょう.

use v0.5.5 with webpack failed TypeError: module.require is not a function · Issue #92 · getsentry/sentry-electron