7

I'm using electron-builder (16.6.2) to package my electron application which includes keytar (3.0.2) as a prod dependency.

package.json file includes:

"scripts": {
    "postinstall": "install-app-deps",
    "compile:dev": "webpack-dev-server --hot --host 0.0.0.0 --config=./webpack.dev.config.js",
    "compile": "webpack --config webpack.build.config.js",
    "dist": "yarn compile && build"
},
"build": {
    "appId": "com.myproject",
    "asar": true,
    "files": [
      "bin",
      "node_modules",
      "main.js"
    ]
}

When I run the .app on the same system it runs fine. When I try running it on a different system (or deleting my node_modules) it fails to find keytar.node. When keytar is built, it includes a fully qualified path to that image for my system. I get the following error in the console:

Uncaught Error: Cannot open /Users/Kevin/Work/myproject/node_modules/keytar/build/Release/keytar.node
Error: dlopen(/Users/Kevin/Work/myproject/node_modules/keytar/build/Release/keytar.node, 
1): image not found

I must be missing a step in the build process.

Kevin
  • 101
  • 1
  • 6

3 Answers3

3

As it turns out, I was using keytar in the renderer process. I moved keytar into the main process (which doesn't go through Webpack / Babel) and gets packed correctly by electron-builder.

main.js

ipcMain.on('get-password', (event, user) => {
    event.returnValue = keytar.getPassword('ServiceName', user);
});

ipcMain.on('set-password', (event, user, pass) => {
    event.returnValue = keytar.replacePassword('ServiceName', user, pass);
});

Then from the renderer process I can call

const password = ipcRenderer.sendSync('get-password', user);

or

ipcRenderer.sendSync('set-password', user, pass);
Kevin
  • 101
  • 1
  • 6
  • I refactored my app to only call keytar in the main thread, using IPC from the renderer thread. However, since using webpack to transpile all code, my app runs fine until I interact with keytar, then I encounter: `TypeError: keytar.findPassword is not a function`, see [this Github issue](https://github.com/atom/node-keytar/issues/95). – Greg K Apr 13 '18 at 10:55
0

Update:
I found (as per the OP) that transpiling my main thread code (which uses keytar) resulted in calls to keytar functions returning TypeError: keytar.findPassword is not a function.

I had to use webpack-asset-relocator-loader to bundle keytar successfully:

npm i -DE @vercel/webpack-asset-relocator-loader

Add the following rule to your webpack.config.js:

module: {
  rules: [{
    test: /\.node$/,
    parser: { amd: false },
    use: {
      loader: "@vercel/webpack-asset-relocator-loader",
      options: {
        outputAssetBase: "native_modules"
      }
    }
  },
  // <other rules>
  ],
  // rest of config
}

Solution found in this Github issue.

The information below still stands for including binary assets in your webpack build.


If you have to transpile code that requires a binary file, you can add file-loader to your webpack config.

Install

npm i -D file-loader
or
yarn add -D file-loader

webpack config (to include a .dat file)

...,
module: {
  rules: [{
    ...
  }, {
    test: /\.dat$/,
    use: {
      loader: "file-loader"
    }
  }]
},
...

If you want to preserve the filename, you can pass name options to the loader:

use: {
  loader: "file-loader",
  options: {
    name: "[name].[ext]"
  }
}

More information on the file-loader Github repo.

Greg K
  • 10,770
  • 10
  • 45
  • 62
0
window.require("electron").remote.require("keytar")

Since you are working on renderer process and want to use native api from system or main process.

K.Dᴀᴠɪs
  • 9,945
  • 11
  • 33
  • 43
Karan Goel
  • 1
  • 1
  • 2