node-serialport
node-serialport copied to clipboard
`No native build was found for platform` when using Electron Forge + Webpack
SerialPort Version
10.4.0
Node Version
14.19.1
Electron Version
17.2.0
Platform
Linux [redacted] 5.13.0-37-generic #42~20.04.1-Ubuntu SMP Tue Mar 15 15:44:28 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
Architecture
x64
Hardware or chipset of serialport
No response
What steps will reproduce the bug?
I'm having trouble using the Electron Forge Typescript + Webpack template to replicate this example: https://github.com/serialport/electron-serialport
yarn create electron-app my-new-app --template=typescript-webpack && cd my-new-appyarn add serialport tableifyyarn add --dev @types/tableify- Copy the contents of
renderer.jsintorenderer.ts - Copy the contents of
index.htmlintoindex.html - Add
webPreferences: { nodeIntegration: true, contextIsolation: false }to the options ofBrowserWindowinindex.ts target: 'electron-renderer'towebpack.renderer.config.jsnpx electron-rebuildyarn start
What happens?
This error appears on the console and Serialport fails to start.
Uncaught Error: No native build was found for platform=linux arch=x64 runtime=electron abi=101 uv=1 libc=glibc node=16.13.0 electron=17.2.0 webpack=true
loaded from: [redacted]/my-new-app/node_modules/electron/dist/resources/electron.asar
at Function.load.path (index.js?04e8:6:99)
at load (index.js?04e8:6:99)
at eval (load-bindings.js?bdc2:10:1)
at Object../node_modules/@serialport/bindings-cpp/dist/load-bindings.js (index.js:85:1)
at __webpack_require__ (index.js:841:33)
at fn (index.js:1028:21)
at eval (darwin.js?fd34:8:25)
at Object../node_modules/@serialport/bindings-cpp/dist/darwin.js (index.js:30:1)
at __webpack_require__ (index.js:841:33)
at fn (index.js:1028:21)
What should have happened?
Serialport should list all serial devices in the Electron window that opens exactly as with https://github.com/serialport/electron-serialport
Additional information
The same issue occurs when running a packaged Electron app using yarn make.
I have observed the same behaviour using Electron 12.0.9. (This is the project we are using Serialport in.)
A colleague has replicated the issue with Electron 12.0.9 on macOS with an M1 MacBook Pro.
I think the issue boils down to compatiblity with https://github.com/vercel/webpack-asset-relocator-loader
It is looking for one of the following:
require('bindings')(...)nbind.init(..)node-pre-gypinclude patterns
I haven't been able to cooerce Electron to load the .node native libraries by copying them to .webpack/renderer/native_modules/prebuilds/linux-x64 sadly.
@chetbox Did you ever get this working? The moment I added serialport to one of my electron projects, I encountered the same bindings issue. Before I go on a wild goose chase, I'd like to see if you were able to resolve this issue and provide some guidance
Not yet. We'll have to look when we absolutely must upgrade which is not yet. Chase away. Please update us if you make any progress.
I got the same error adding serialport to an electron forge + webpack project.
hey @PredictabilityIsGood @mimamuh any updates? same experience as you guys using electron-builder
You don't need to builder anymore (since v10) but you do need to tell webpack to ignore it https://webpack.js.org/configuration/externals/
@sh0shinsha I switched to electron-react-boilerplate which excludes the serialport libs from the bundler similar as @reconbot mentioned. So simply try to exclude serialport and I think it should work even with forge + webpack then.
I've tried adding serialport and @serialport/bindings-cpp to webpack's externals but I still get this message in the renderer after running electron-forge make:
Uncaught Error: Cannot find module 'serialport'
We're using the ASAR option to package a production app. On closer inspection it seems the ASAR has an empty node_modules folder. Is there anything I need to do to tell Electron Forge to include serialport in node_modules inside the ASAR?
Edit: Turning off the ASAR option still results in an empty node_modules folder when running electron-forge make.
Thanks for the replies everyone.
@chetbox same issue here, adding serialport to externals results in the same message: Cannot find module 'serialport'
@mimamuh I followed your guidance and did a fresh clone of electron-react-boilerplate 4.5.0 and added serialport: 10.4.0. After packaging I get the same error message as OP:

Have you managed to get serialport working with electron-react-boilerplate?
@sh0shinsha Yes, I got it managed to work with electron-react-boilerplate. Have you installed serialport like desribed here in the docs?. This might be your issue.
@chetbox Actually I had the same thought: In case you exclude serialport from webpacks' build process then you might have to include the excluded module yourself into electrons build process somehow in case electron (forge) doesn't do so automatically for you, which I doubt it does when I read the docs here. That might explain to me why it's not included in your ASAR folder?
At least electron-react-boilerplate handles it like that. I have to install these native modules into a separated node_modules folder so that it can be copied it into my app before it is packaged by electron. Check out these docs here, maybe it helps to understand the process.
@mimamuh you saved me mate! Following the steps in the docs you linked regarding how native modules are handled in electron-react-boilerplate set me on the right path.
I'll outline the steps I took to solve my No native build was found for platform error. I've modeled my project's webpack configs on the ones in electron-react-boilerplate, so these steps may be helpful for those using electron-react-boilerplate or those with a similar webpack setup:
externals: ['serialport']in webpack config -> in my case I just added it right towebpack.config.main.prodsince I'm not merging configs like in electron-react-boilerplatelibrary: { type: 'commonjs2' }in webpack config'soutput- (if you've already installed
serialport,npm uninstall serialportin your rootpackage.json) cd ./release/appthennpm install serialport
- (https://electron-react-boilerplate.js.org/docs/native-modules/#native-modules-in-electron-react-boilerplate)
- (https://github.com/electron-react-boilerplate/electron-react-boilerplate/blob/main/.erb/scripts/check-native-dep.js)
If you're using electron-react-boilerplate you should be all set, they handle module linking in the ./release/app/package.json: https://github.com/electron-react-boilerplate/electron-react-boilerplate/blob/main/release/app/package.json
In my case I set serialport: =10.4.0 in both the ./release/app/package.json and devDependencies in my root package.json since I'm using an npm workspace monorepo and not the exact folder structure of electron-react-boilerplate. If anyone has a better solution to this I would welcome it but won't pursue it further here since it is outside the scope of serialport.
Hope this helps, thanks everyone!
@sh0shinsha Yes, I got it managed to work with electron-react-boilerplate. Have you installed
serialportlike desribed here in the docs?. This might be your issue.@chetbox Actually I had the same thought: In case you exclude
serialportfromwebpacks'build process then you might have to include the excluded module yourself into electrons build process somehow in case electron (forge) doesn't do so automatically for you, which I doubt it does when I read the docs here. That might explain to me why it's not included in your ASAR folder?At least electron-react-boilerplate handles it like that. I have to install these native modules into a separated
node_modulesfolder so that it can be copied it into my app before it is packaged by electron. Check out these docs here, maybe it helps to understand the process.
Those instructions for native modules with electron-react-boilerplate work in development using serialport but not when the app is packaged. I'm not sure why and I don't understand how to debug this tooling well enough. Here's a repo with the changes if anyone wants to investigate: https://github.com/chetbox/electron-react-boilerplate
(Note that I had to comment out the renderer IPC code because it caused a crash with contextIsolation: false.
@chetbox it looks like serialport isn't present in your ./release/app/package.json. Let me know if the steps I outlined help.
@sh0shinsha yes, that's the key for Electron React Boilerplate. 👍🏼 Thanks for helping me understand how that build system works.
With Electron Forge I can get a packaged (non-ASAR) build working by copying serialport and its dependencies into the packaged electron-forge folder.
yarn package
cp -r node_modules/serialport out/my-new-app-linux-x64/resources/app/node_modules/
cp -r node_modules/@serialport out/my-new-app-linux-x64/resources/app/node_modules/
cp -r node_modules/debug out/my-new-app-linux-x64/resources/app/node_modules/
cp -r node_modules/ms out/my-new-app-linux-x64/resources/app/node_modules/
cp -r node_modules/node-gyp-build out/my-new-app-linux-x64/resources/app/node_modules/
./out/my-new-app-linux-x64/my-new-app
I assume this means the Electron Forge + Webpack build system needs to be better aware of how to handle native modules like this. I think the issue is with @electron-forge/plugin-webpack or with @vercel/webpack-asset-relocator-loader (as I mentioned above) not with serialport.
Using electron-builder.
This worked:
// webpack.config.js
externals: {
serialport: "commonjs2 serialport", // Ref: https://copyprogramming.com/howto/electron-and-serial-ports
},
@maneetgoyal Wonderful! This seems to have worked on my project as well.
Do you know specifically why this works? Even in this linked reference, the sentence is pretty vague.
Electron Forge is using electron-rebuild. With the simplest project which only dependents serialport, electron, and electron-rebuild without any bundle tool, It will create @serialport/bindings-cpp/build/Release/.forge-meta but won't build anything.
You don't need to builder anymore (since v10) but you do need to tell webpack to ignore it https://webpack.js.org/configuration/externals/
@reconbot Does it mean the prebuilt is also working on Electron? If yes, I guess we need a config file to tell the prebuilt path. I found https://github.com/serialport/bindings-cpp/commit/16f966233930bc7c7302d2b7a53d70282b42e165 and can't see the same thing after binding was moved to bindings-cpp.
@mimamuh you saved me mate! Following the steps in the docs you linked regarding how native modules are handled in electron-react-boilerplate set me on the right path.
I'll outline the steps I took to solve my
No native build was found for platformerror. I've modeled my project's webpack configs on the ones in electron-react-boilerplate, so these steps may be helpful for those using electron-react-boilerplate or those with a similar webpack setup:1. `externals: ['serialport']` in webpack config -> in my case I just added it right to `webpack.config.main.prod` since I'm not merging configs like in [electron-react-boilerplate](https://github.com/electron-react-boilerplate/electron-react-boilerplate) 2. `library: { type: 'commonjs2' }` in webpack config's `output` 3. (if you've already installed `serialport`, `npm uninstall serialport` in your root `package.json`) 4. `cd ./release/app` then `npm install serialport` * (https://electron-react-boilerplate.js.org/docs/native-modules/#native-modules-in-electron-react-boilerplate) * (https://github.com/electron-react-boilerplate/electron-react-boilerplate/blob/main/.erb/scripts/check-native-dep.js)If you're using electron-react-boilerplate you should be all set, they handle module linking in the
./release/app/package.json: https://github.com/electron-react-boilerplate/electron-react-boilerplate/blob/main/release/app/package.jsonIn my case I set
serialport: =10.4.0in both the./release/app/package.jsonanddevDependenciesin my rootpackage.jsonsince I'm using an npm workspace monorepo and not the exact folder structure of electron-react-boilerplate. If anyone has a better solution to this I would welcome it but won't pursue it further here since it is outside the scope ofserialport.Hope this helps, thanks everyone!
This was a good guide - it seems like the latest ERB has the following line in the webpack.config.base.ts:
import { dependencies as externals } from '../../release/app/package.json';
const configuration: webpack.Configuration = {
externals: [...Object.keys(externals || {})],
I stumbled across this thread because I'm still seeing the serialport binding issues despite the externals being already included. Doesn't change if i comment out the above and explicitly set serialport as the only external.
Output:
An unhandled error occurred inside electron-rebuild
node-gyp failed to rebuild '/Users/andrew/code/js/electron/my-cool-app/release/app/node_modules/@serialport/bindings-cpp'.
For more information, rerun with the DEBUG environment variable set to "electron-rebuild".
Error: `make` failed with exit code: 2
Error: node-gyp failed to rebuild '/Users/andrew/code/js/electron/my-cool-app/release/app/node_modules/@serialport/bindings-cpp'.
For more information, rerun with the DEBUG environment variable set to "electron-rebuild".
Error: `make` failed with exit code: 2
at NodeGyp.rebuildModule (/Users/andrew/code/js/electron/my-cool-app/node_modules/electron-rebuild/lib/src/module-type/node-gyp.js:120:19)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async ModuleRebuilder.rebuildNodeGypModule (/Users/andrew/code/js/electron/my-cool-app/node_modules/electron-rebuild/lib/src/module-rebuilder.js:98:9)
at async ModuleRebuilder.rebuild (/Users/andrew/code/js/electron/my-cool-app/node_modules/electron-rebuild/lib/src/module-rebuilder.js:128:14)
at async Rebuilder.rebuildModuleAt (/Users/andrew/code/js/electron/my-cool-app/node_modules/electron-rebuild/lib/src/rebuild.js:149:13)
at async Rebuilder.rebuild (/Users/andrew/code/js/electron/my-cool-app/node_modules/electron-rebuild/lib/src/rebuild.js:112:17)
at async /Users/andrew/code/js/electron/my-cool-app/node_modules/electron-rebuild/lib/src/cli.js:158:9
Error: Command failed: ../../node_modules/.bin/electron-rebuild --force --types prod,dev,optional --module-dir .
at checkExecSyncError (node:child_process:841:11)
at execSync (node:child_process:912:15)
at Object.<anonymous> (/Users/andrew/code/js/electron/my-cool-app/.erb/scripts/electron-rebuild.js:16:11)
at Module._compile (node:internal/modules/cjs/loader:1126:14)
at Module.m._compile (/Users/andrew/code/js/electron/my-cool-app/node_modules/ts-node/src/index.ts:1618:23)
at Module._extensions..js (node:internal/modules/cjs/loader:1180:10)
at Object.require.extensions.<computed> [as .js] (/Users/andrew/code/js/electron/my-cool-app/node_modules/ts-node/src/index.ts:1621:12)
at Module.load (node:internal/modules/cjs/loader:1004:32)
at Function.Module._load (node:internal/modules/cjs/loader:839:12) {
status: 255,
...
Same issue here, but I'm using the electron/electron-forge boilerplate, not the react one. This means there is only the .webpack/{main|renderer} folders after yarn start.
@andrewrt @mimamuh Is there a hack to make this work for non react boilerplates?
@petertorelli While it's hardly elegant, this note from @chetbox works for me (with externals: { serialport: 'serialport' } in webpack.main.config.js):
With Electron Forge I can get a packaged (non-ASAR) build working by copying
serialportand its dependencies into the packagedelectron-forgefolder.yarn package cp -r node_modules/serialport out/my-new-app-linux-x64/resources/app/node_modules/ cp -r node_modules/@serialport out/my-new-app-linux-x64/resources/app/node_modules/ cp -r node_modules/debug out/my-new-app-linux-x64/resources/app/node_modules/ cp -r node_modules/ms out/my-new-app-linux-x64/resources/app/node_modules/ cp -r node_modules/node-gyp-build out/my-new-app-linux-x64/resources/app/node_modules/ ./out/my-new-app-linux-x64/my-new-app
@chetbox @NoahAndrews How would this work with Electron Forge during development when there is no out directory created yet?
I added this to webpack.renderer.config.js
externals: {
serialport: "serialport",
},
And I'm still stuck with: Uncaught ReferenceError: serialport is not defined
Thanks.
@jvolker I meant to come back and clarify what we're actually doing, which is only based on @chetbox's solution. Thanks for the reminder. @petertorelli, maybe this is useful to you too.
We have a copy-to-package-directory.js file:
const fs = require('fs');
const path = require('path');
// https://stackoverflow.com/a/22185855/4651874
function copyRecursiveSync(src, dest) {
let exists = fs.existsSync(src);
let stats = exists && fs.statSync(src);
let isDirectory = exists && stats.isDirectory();
if (isDirectory) {
console.log(`Making directory ${dest}`);
fs.mkdirSync(dest, { recursive: true });
fs.readdirSync(src).forEach(function(childItemName) {
console.log(`Copying ${childItemName}`);
copyRecursiveSync(path.join(src, childItemName),
path.join(dest, childItemName));
});
} else {
console.log(`Copying ${src} to ${dest}`);
fs.copyFileSync(src, dest);
}
}
// Squirrel won't package up unexpected files unless they are in the "resources" folder
// See https://github.com/electron-userland/electron-forge/issues/135#issuecomment-991376807
module.exports = function(extractPath, electronVersion, platform, arch, done) {
// https://github.com/serialport/node-serialport/issues/2464#issuecomment-1122454950
copyRecursiveSync(path.join('node_modules', 'serialport'), path.join(extractPath, 'resources', 'app', 'node_modules', 'serialport'));
copyRecursiveSync(path.join('node_modules', '@serialport'), path.join(extractPath, 'resources', 'app', 'node_modules', '@serialport'));
copyRecursiveSync(path.join('node_modules', 'debug'), path.join(extractPath, 'resources', 'app', 'node_modules', 'debug'));
copyRecursiveSync(path.join('node_modules', 'ms'), path.join(extractPath, 'resources', 'app', 'node_modules', 'ms'));
copyRecursiveSync(path.join('node_modules', 'node-gyp-build'), path.join(extractPath, 'resources', 'app', 'node_modules', 'node-gyp-build'));
console.log("Done copying files");
done();
}
In package.json, we configure Forge to run that script:
"forge": {
"packagerConfig": {
"afterExtract": [
"copy-to-package-directory.js"
]
}
}
Thanks, @NoahAndrews.
Unfortunately, it doesn't solve the issue during development though, it seems. The "afterExtract" hook is not being triggered then.
I added this to
webpack.renderer.config.jsexternals: { serialport: "serialport", },
That probably needs to be in webpack.main.config.js.
Thanks. Yes, I can confirm this works using serialport in the main process. Is there any chance to get this working in renderer as well?
@jvolker - While it might work in this instance, that's waaaay too gnarly a hack to carry forward in a shipping project. I'm trying to migrate an Electron app we've been shipping since 2018 to Mac m1. Our "ancient" build environment is still stable so we continue to use it (electron 10 and builder 23) and just punt on m1 support for now (and we can't rely on rosetta due to libusb bandwidth issues). Ideally we're forcing electron-forge to build non-web apps for hardware front-ends (USB, VISA, serial), which is probably a bad idea to force a square peg into a round hole. What we need is a boilerplate packager that is designed for non-web hardware interfacing apps that use electron, and jettison the web stuff! :)
I was struggling with a No native build was found for platform error message when importing serialport in Electron's main process. I was using electron-forge to build my distributable. I also tried using electron-rebuild manually, to no avail.
I spent a couple of days digging through Stack Overflow posts and GitHub issues and did some experiments. In the end it looks like the issue is caused by electron-rebuild not deeming serialport's native node modules necessary to be rebuilt on certain platforms. In my case, electron-rebuild was rebuilding the serialport native node modules on my M1 MacBook Pro (arm64), but wasn't rebuilding these modules on a Raspberry Pi 4 Model B (armv7l).
What fixed it for me in the end (credit to the this GitHub comment) is running the following commands before Electron's packaging step:
rm --recursive node_modules/@serialport
npm install --no-save --build-from-source [email protected]
This will first delete all the contents of your node_modules/@serialport folder, which contains prebuilt native node modules for several platforms in the bindings-cpp package. Next, we build serialport's native node modules from scratch, so that we (hopefully) end up with a native node module that matches our current platform.
Make sure the version of serialport used in the above command matches the version you depend on!
@glenn-kroeze I'm using serialport from within a fork started by the renderer process (my app can run with or without a renderer, so the same fork can be called from within either, depending on whether a window is created. I support GUI and CLI modes for CI/CD.). This creates all kinds of problems in forge/webpack. I don't think this is a serialport issue at all, but a packager issue.
After struggling a lot with this issue, i was able to resolve using another approach. In my case we need to use usb and serialport dependencies.
First add externals: ['usb', 'serialport'], to webpack.main.config.ts
Then inside forge.config.ts we added a hook to include these modules on node_modules internal dependencies
hooks: {
packageAfterPrune: async (forgeConfig, buildPath) => {
console.log(buildPath);
const packageJson = JSON.parse(fs.readFileSync(path.resolve(buildPath, 'package.json')).toString());
packageJson.dependencies = {
serialport: '^10.5.0',
usb: '^2.8.0',
};
fs.writeFileSync(path.resolve(buildPath, 'package.json'), JSON.stringify(packageJson));
return new Promise((resolve, reject) => {
const npmInstall = spawn('yarn', ['install', '--production=true'], {
cwd: buildPath,
stdio: 'inherit',
shell: true,
});
npmInstall.on('close', code => {
if (code === 0) {
resolve();
} else {
reject(new Error('process finished with error code ' + code));
}
});
npmInstall.on('error', error => {
reject(error);
});
});
},
},
Not an elegant solution but works. We cannot use references from original package.json because these libraries are used from another internal library.