vuejs3electron-buildervue-cli-4

Vue CLI with electron - Unexpected character (1:0) when using native modules


In some popular NodeJS libraries, e.g. ssh2 or node-pty, there is natively compiled code as part of the library. Creating the project with

vue create my-project
vue add electron-builder
yarn add ssh2

then importing and using ssh2's Client in the background process results in following errors during electron:build

ERROR  Failed to compile with 1 errors                                                                                                                                        5:29:10 PM

 error  in ./node_modules/cpu-features/build/Release/cpufeatures.node

Module parse failed: Unexpected character '�' (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
(Source code omitted for this binary file)

 @ ./node_modules/cpu-features/lib/index.js 1:16-60
 @ ./node_modules/ssh2/lib/protocol/constants.js
 @ ./node_modules/ssh2/lib/client.js
 @ ./node_modules/ssh2/lib/index.js
 ...

This error occurs with many other libs or transitive dependencies and the reason for it is absence of native-ext-loader on Webpack chain. I understand why it is not included by default, and I would like to see what is the best way to add it.


Solution

  • One solution I found is this:

    1. Add yarn add -D native-ext-loader (my version is 2.3.0 and electron is at 13.x)
    2. Adjust vue.config.js and add the chainWebpackMainProcess like this:
    const path = require('path')
    module.exports = {
      pluginOptions: {
        electronBuilder: {
          builderOptions: {
            // options placed here will be merged with default
            mac: {
              target: 'dmg',
              icon: 'build/icon.icns',
              asar: true
            }
          },
          preload: 'src/preload.ts',
          chainWebpackMainProcess(config) {
            config.module
              .rule("node")
              .test(/\.node$/)
              .use("native-ext-loader")
              .loader("native-ext-loader")
              .options(
                process.env.NODE_ENV === "development"
                  ? {
                    rewritePath: path.resolve(__dirname, "native"),
                  }
                  : {}
              )
          }
        }
      }
    }
    

    Both, electron:build and electron:serve are now working and ssh2 client is happily delivering the stdout to renderer via ipcMain. Not sure it is the most elegant way of solving it, though.

    UPDATE Jun 2023:

    With newest Vite and VueJS developments, I find it is easier and faster to follow Electron-Vite-Boilerplate template, if you use packages with native code. It takes care of using native extensions for Electron with Vite without the need for native-ext-loader. Instead, it uses externalize-deps from electron-vite package by the same author.