Electron cannot open shared object file from node_modules folder - node.js

On electron, the node module vosk needs to access some shared objects located in node_modules/vosk/lib/.
The issue I am having right now is that, when I do require('vosk') in my main.js and try to execute my AppImage file, I get:
A JavaScript error occurred in the main process
Uncaught Exception:
Error: Dynamic Linking Error: /tmp/.mount_CantooClaxGf/resources/app.asar/node_modules/vosk/lib/linux-x86_64/libvosk.so: Cannot open the shared object: It's not a folder
at new DynamicLibrary (/tmp/.mount_CantooClaxGf/resources/app.asar/node_modules/ffi-napi/lib/dynamic_library.js:75:11)
at Object.Library (/tmp/.mount_CantooClaxGf/resources/app.asar/node_modules/ffi-napi/lib/library.js:47:10)
at Object.<anonymous> (/tmp/.mount_CantooClaxGf/resources/app.asar/node_modules/vosk/index.js:24:21)
at Module._compile (internal/modules/cjs/loader.js:1145:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1166:10)
at Module.load (internal/modules/cjs/loader.js:981:32)
at Module._load (internal/modules/cjs/loader.js:881:14)
at Function.Module._load (electron/js2c/asar.js:769:28)
at Module.require (internal/modules/cjs/loader.js:1023:19)
at require (internal/modules/cjs/helpers.js:77:18)
I tried to add vosk to the files in the build:
"build": {
"files": [
"dist/**/*",
"src/assets/icons/*",
"electron.js",
"package.json",
"assets/models/*",
"node_modules/vosk/lib/*"
],
I can now see the files in the app.asar.unpacked/node_modules/vosk/lib/ folder, but when executing the app, I'm still having the same error.
I found this answer mentioning a hack, but it didn't solve my issue and I still have the exact same error.
How am I supposed to package the shared objects in a way that vosk will find them?

I could solve the issue with this config for electron, putting all the dependencies of vosk in the extraResources field:
"build": {
"extraResources": [
"node_modules/at-least-node/**/*",
"node_modules/builder-util-runtime/**/*",
"node_modules/debug/**/*",
"node_modules/ffi-napi/**/*",
"node_modules/fs-extra/**/*",
"node_modules/get-symbol-from-current-process-h/**/*",
"node_modules/get-uv-event-loop-napi-h/**/*",
"node_modules/graceful-fs/**/*",
"node_modules/jsonfile/**/*",
"node_modules/ms/**/*",
"node_modules/node-addon-api/**/*",
"node_modules/node-gyp-build/**/*",
"node_modules/ref-napi/**/*",
"node_modules/ref-struct-di/**/*",
"node_modules/sax/**/*",
"node_modules/universalify/**/*",
"assets/models/**/*"
],
"files": [
"dist/**/*",
"src/assets/icons/*",
"electron.js",
"package.json"
],
I also needed this lib.
It's now working as expected

I solved by updating my electron-builder configuration in package.json to be
{
"build": {
"asar": true,
"asarUnpack": [
"node_modules"
],
}
}
Then ensuring the unpacked path was used instead:
var dirPath = __dirname.includes('.asar') ? __dirname.replace('.asar', '.asar.unpacked') : __dirname;

Related

Typescript build "Cannot find module", but only on a ubuntu server

I'm working on a node script and writing it in typescript. I've had no problems with running builds on my laptop (mac) or from my raspberry pi running raspbian.
I've gotten to the point where I want to set the codebase up on a digital ocean server that's running ubuntu. I moved the codebase up and ran my build script. The build completes successfully, but when I launch the node process I get the error:
node dist/server/source-server/index.js
internal/modules/cjs/loader.js:883
throw err;
^
Error: Cannot find module 'express'
Require stack:
- /var/www/binary-operations-presentation/multi-player-server/dist/server/source-server/index.js
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:880:15)
at Function.Module._load (internal/modules/cjs/loader.js:725:27)
at Module.require (internal/modules/cjs/loader.js:952:19)
at require (internal/modules/cjs/helpers.js:88:18)
at Object.<anonymous> (/var/www/binary-operations-presentation/multi-player-server/dist/server/source-server/index.js:6:35)
at Module._compile (internal/modules/cjs/loader.js:1063:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
at Module.load (internal/modules/cjs/loader.js:928:32)
at Function.Module._load (internal/modules/cjs/loader.js:769:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12) {
code: 'MODULE_NOT_FOUND',
requireStack: [
'/var/www/binary-operations-presentation/multi-player-server/dist/server/source-server/index.js'
]
}
It's weird. I've run this codebase on multiple servers and none have had this issue. I did some searching around, but all of the answers I've found are saying "run npm install express" or "run npm install #types/express", both of which I have:
Plus, if I didn't have them installed, the typescript compiler build would have failed.
I compared the file structure on the ubuntu server to my raspberry pi server and aside from the files that are generated when the codebase actually runs, the directories are the same:
Both servers have the same tsconfig.json file:
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"sourceMap": true,
"outDir": "../dist/server",
"rootDirs": ["./", "../project-common/"],
"strict": true,
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true,
"resolveJsonModule": true
}
}
And both have the same dependency versions:
// from package.json
{
...
"devDependencies": {
"#types/uuid": "^8.3.0",
"#types/ws": "^7.4.0",
"killport": "^1.0.1",
"nodemon": "^2.0.7",
"tsc-watch": "^4.2.9",
"tslint": "^6.1.3",
"typescript": "^4.1.3"
},
"dependencies": {
"#types/express": "^4.17.11",
"#types/lodash.clonedeep": "^4.5.6",
"#types/websocket": "^1.0.1",
"express": "^4.17.1",
"lodash.clonedeep": "^4.5.0",
"uuid": "^8.3.2",
"ws": "^7.4.2"
}
}
Neither of the dist folders have the node_modules folder, and when I look at the transpiled index.js file for the server they both import express the same way:
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = __importDefault(require("express"));
const http_1 = __importDefault(require("http"));
and both of the index.js.map files refer to the same relative source directory:
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../source-server/index.ts"]
So I'm not sure why one server would be able to refer back to the node_modules folder but the other one wouldn't.
The only big difference (aside from the OS version, the server is running Ubuntu 20.04) that I see is that my raspberry pi is running node 15.x and the ubuntu is running node 14.x, though I don't know that that would necessarily cause this problem.
Any ideas?
UPDATE: same version of node
I tried making the version of node the same. I installed nvm on my raspberry pi, pulled in 14.6.0 (same version as the digital ocean server), rebuilt the project, and launched the server -> works fine.
so the node version doesn't seem to be a factor :/
So here's the snag and solution:
The gist is that the compiled version of the index file is firing a regular node require() function to pull in modules:
Which means it goes through a number of steps to find the modules. When it comes to possible node_modules modules, it looks for the node_modules folder in the current directory. If node doesn't find it, node will step up to the parent directory and look there, continuing to step back to the root directory if node_modules directories aren't found.
In my case, I previously had all of the server and client code at the same directory level (the directory where the new subfolders now live). When I separated the code into the subfolders, I forgot to blow away the old node_modules folder locally, which is why the dist folder still found the node_modules folder that now resided at the parent directory of dist. When I did a git pull on my raspberry pi, the folders updated, but because node_modules is part of my .gitignore, that folder at the parent level wasn't blown away.
But, in newer clones of the repository of course wouldn't have node_modules at that parent directory, or any other parent directory so they bombed out.
While talking with #cefn I realized that I did want to have the dist be a "everything needed to run this project" directory, so I added a step to my npm build script:
"scripts": {
"build": "tsc --build tsconfig.json && npm run build:move-indexes && npm run build:move-node_modules",
"build:move-indexes": "rsync -avz indexes ../dist/server/source-server",
"build:move-node_modules": "rsync -avz node_modules ../dist/server/source-server",
This way, every time I rebuild the source code, the server will sync the node_modules in the dist directory with the node_modules in my source directory.
Tried it out, and it works :)

ElectronJS build doesn't include needed folder

I'm working on a ElectronJS and ExpressJS project to build an application that runs an API in background and show a system tray with some features to control and monitor that API.
"electron": "^4.0.4",
"electron-builder": "^20.38.5",
I use electron-builder to generate an installer for macOS, and Windows (.exe with nsis and .dmg) packages containing my express API built app as it is.
That's mean, I build it separately and call it's entry point build/index.js in my electron's main.js, Despite, that hook and everything else is fine in development env. But when i run the build, it show me that error:
A JavaScript error occurred in the main process
Uncaught Exception:
Error: Cannot find module 'express'
at Module._resolveFilename (internal/modules/cjs/loader.js:584:15)
at Function.Module._resolveFilename (/Users/username/WebstormProjects/my-api-monitor/release/mac/API monitor.app/Contents/Resources/electron.asar/common/reset-search-paths.js:43:12)
at Function.Module._load (internal/modules/cjs/loader.js:510:25)
at Module.require (internal/modules/cjs/loader.js:640:17)
at require (internal/modules/cjs/helpers.js:20:18)
at Object.<anonymous> (/Users/username/WebstormProjects/my-api-monitor/release/mac/API monitor.app/Contents/Resources/app/api/build/index.js:19:16)
at Object.<anonymous> (/Users/username/WebstormProjects/my-api-monitor/release/mac/API monitor.app/Contents/Resources/app/api/build/index.js:208:3)
at Module._compile (internal/modules/cjs/loader.js:693:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:704:10)
at Module.load (internal/modules/cjs/loader.js:602:32)
I guess the problem is that the node_modules of my API in /build folder isn't packaged in the app, and i can't achieve that with the normal way documented in electron-builder readme.
For more details, here is my files tree.
Also, my electron-builder config. :
"asar": false,
"files": [
"main.js",
"api/**/*",
"resources/**/*"
],
"directories": {
"buildResources": "resources",
"output": "release"
}
Same issue with or without asar, i jsut keep it false to check if folders and files are included right.
Any idea will be helpful.

How to get semantic/gulp/webpack to work in electron-react-boilerplate

I am trying to port a react app to electron using electron-react-boilerplate but I'm using semantic-ui which suggested being set up with gulp. electron-react-boilerplate uses webpack to handle all of its packaging and I can't get webpack +gulp to work so everything will package in the electron app.
I'm trying this link that explains how to pipe webpack configs through gulp tasks but I'm getting an "unexpected token import" error from the webpack config.
.babelrc
{
"presets": ["es2015", "stage-0", "react"],
"plugins": ["add-module-exports"],
"env": {
"production": {
"presets": ["react-optimize"],
"plugins": ["babel-plugin-dev-expression"]
},
"development": {
"plugins": ["tcomb"],
"presets": ["react-hmre"]
},
"test": {
"plugins": [
["webpack-loaders", { "config": "webpack.config.test.js", "verbose": false }]
]
}
}
}
simple gulpfile.js straight from the link above:
var gulp = require('gulp');
var webpack = require('webpack-stream');
gulp.task('default', function() {
return gulp.src('src/entry.js')
.pipe(webpack())
.pipe(gulp.dest('dist/'));
});
the webpack dev file is here,
note some of the names are changed between this boilerplate version and my project but otherwise it's the same.
the error:
Configurator>gulp
[09:52:40] Using gulpfile C:\git\Configurator\gulpfile.js
[09:52:40] Starting 'default'...
[09:52:40] 'default' errored after 8.28 ms
[09:52:40] C:\git\Configurator\webpack.config.development.js:7
import webpack from 'webpack';
^^^^^^
SyntaxError: Unexpected token import
at Object.exports.runInThisContext (vm.js:53:16)
at Module._compile (module.js:513:28)
at Object.Module._extensions..js (module.js:550:10)
at Module.load (module.js:458:32)
at tryModuleLoad (module.js:417:12)
at Function.Module._load (module.js:409:3)
at Module.require (module.js:468:17)
at require (internal/module.js:20:19)
at Gulp.<anonymous> (C:\git\Configurator\gulpfile.js:7:18)
at module.exports (C:\git\Configurator\node_modules\orchestrator\lib\runTask.js:34:7)
the internet tells me that getting rid of the import errors should be as simple as using the 'es2015' preset in .babelrc but it's there and it's not helping.
I can get the dev server to work in the electron app with the semantic-ui stuff after the initial gulp build for semantic, but for some reason it doesn't build into the electron package when I try to package an installer to deploy this thing.
When I run the electron app dev server through webpack it works fine though, except I get two errors:
cannot set property exports of undefined
and
locals[0] does not appear to be a module object with hot module replacement API enabled
the latter stacktrace goes back to some semantic imports in one of my react files.
I'm totally at a loss as to how to make all this stuff work together.

Overriding `tsconfig.json` for ts-node in mocha

Is it possible to override which tsconfig.json ts-node uses when called from mocha?
My main tsconfig.json contains "module": "es2015", but I want to use "module": "commonjs" for ts-node only.
I tried this
mocha --compilers ts:ts-node/register,tsx:ts-node/register \
--compilerOptions '{"module":"commonjs"}' \
--require ts-node/register test/**/*.spec.ts*
but it did not work:
SyntaxError: Unexpected token import
at exports.runInThisContext (vm.js:53:16)
at Module._compile (module.js:387:25)
at Module.m._compile (/usr/lib/node_modules/ts-node/src/index.ts:406:23)
at Module._extensions..js (module.js:422:10)
at Object.require.extensions.(anonymous function) [as .tsx] (/usr/lib/node_modules/ts-node/src/index.ts:409:12)
at Module.load (module.js:357:32)
at Function.Module._load (module.js:314:12)
at Module.require (module.js:367:17)
at require (internal/module.js:16:19)
at /usr/lib/node_modules/mocha/lib/mocha.js:222:27
at Array.forEach (native)
at Mocha.loadFiles (/usr/lib/node_modules/mocha/lib/mocha.js:219:14)
at Mocha.run (/usr/lib/node_modules/mocha/lib/mocha.js:487:10)
at Object.<anonymous> (/usr/lib/node_modules/mocha/bin/_mocha:458:18)
at Module._compile (module.js:413:34)
at Object.Module._extensions..js (module.js:422:10)
at Module.load (module.js:357:32)
at Function.Module._load (module.js:314:12)
at Function.Module.runMain (module.js:447:10)
at startup (node.js:146:18)
at node.js:404:3
You need to set the configuration through the TS_NODE_COMPILER_OPTIONS environment variable
Example code on an unix machine:
TS_NODE_COMPILER_OPTIONS='{"module":"commonjs"}' \
mocha --require ts-node/register 'test/**/*.spec.{ts,tsx}'
Explanation extracted from the repository documentation
CLI and Programmatic Options
Environment variable denoted in parentheses.
-T, --transpile-only Use TypeScript's faster transpileModule (TS_NODE_TRANSPILE_ONLY, default: false)
-I, --ignore [pattern] Override the path patterns to skip compilation (TS_NODE_IGNORE, default: /node_modules/)
-P, --project [path] Path to TypeScript JSON project file (TS_NODE_PROJECT)
-C, --compiler [name] Specify a custom TypeScript compiler (TS_NODE_COMPILER, default: typescript)
-D, --ignore-diagnostics [code] Ignore TypeScript warnings by diagnostic code (TS_NODE_IGNORE_DIAGNOSTICS)
-O, --compiler-options [opts] JSON object to merge with compiler options (TS_NODE_COMPILER_OPTIONS)
--files Load files from tsconfig.json on startup (TS_NODE_FILES, default: false)
--pretty Use pretty diagnostic formatter (TS_NODE_PRETTY, default: false)
--skip-project Skip project config resolution and loading (TS_NODE_SKIP_PROJECT, default: false)
--skip-ignore Skip ignore checks (TS_NODE_SKIP_IGNORE, default: false)
--log-error Logs errors of types instead of exit the process (TS_NODE_LOG_ERROR, default: false)
--prefer-ts-exts Re-order file extensions so that TypeScript imports are preferred (TS_NODE_PREFER_TS_EXTS, default: false)
TypeScript allows you to override a configuration file. Rather than hard-code JSON in an environment variable as mentioned in the other solutions, specify the overridden configuration path in the environment. The TS_NODE_PROJECT environment variable can be used for this.
TS_NODE_PROJECT='./tsconfig.commonjs.json'
So if your main config is:
tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
}
}
You can create another configuration that overrides the module setting.
tsconfig.commonjs.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs"
}
}
When you run mocha, specify the overridden configuration to use:
"test": "TS_NODE_PROJECT='./tsconfig.commonjs.json' mocha -r ts-node/register test/**/*.spec.ts*"
This makes it very easy to further customize your tsconfig just for mocha testing. You can even run ts-node (outside of mocha) directly specifying that path:
ts-node -P tsconfig.commonjs.json -r myFile.ts
--compilerOptions wont' work.
What you need to do is customize how you register ts-node. My case was a little bit different from yours, I wanted it to use test/tsconfig.json, which contained settings needed by my test code. If I just used --require ts-node/register, it was using a default configuration that did not contain the settings needed to run my tests.
What I did was:
Create a file test/tshook.js. It contains:
require("ts-node").register({
project: "test/tsconfig.json",
});
I edited my test/mocha.opts to have:
--require test/tshook.js
test/**/*.ts
This should will pass the desired setting to ts-node:
require("ts-node").register({
compilerOptions: {
module: "commonjs",
},
});
In package.json - scripts section:
"test": "TS_NODE_PROJECT=src mocha"
picks up my tsconfig.json in the src directory of my project, overriding the default tsconfig.json.
OP can achieve same by using test instead of src
This worked for me on windows
set TS_NODE_COMPILER_OPTIONS={\"module\":\"commonjs\"} && mocha -r ts-node/register test/unit/*.test.ts
This was the error that prompted me to use that solution
(function (exports, require, module, __filename, __dirname) { import 'mocha';
You can also use ts-mocha (https://www.npmjs.com/package/ts-mocha)
Example
package.json
"test": "ts-mocha -p test/tsconfig.cjs.json test/**/*.test.ts"
test/tsconfig.cjs.json
{
"extends": "../tsconfig.json",
"compilerOptions": {
"module": "commonjs"
}
}
While there doesn't seem to be a unified linux/windows command line phrasing that works, you can set the compiler-options from the command line. In my case, much like the OP, I have a tsconfig.json with a module: esnext. I was able to override on the command line:
Ran on Windows, w/ts-node installed globally, but in different shell types:
bash/mingw64:
ts-node --compiler-options={"module":"commonJS"} xxx.ts
cmd:
ts-node --compiler-options={\"module\":\"commonJS\"} xxx.ts
On mac
"test": "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' mocha --require ts-node/register test/**/*.ts",
This doesn't address the core original question, but goes to the heart of getting mocha with typescript working, so I'll post it here in case it helps others.
We found that some ridiculous backslash escaping was needed to make this work on Windows. It works fine on Mac too, even though Mac doesn't need the same escaping.
Then, additional ts-node require directives were critical.
File package.json:
{
"scripts": {
...
"test": "cross-env TS_NODE_COMPILER_OPTIONS={\\\"module\\\":\\\"commonjs\\\"} mocha"
},
...
"mocha": {
"require": [
"ts-node/register",
"tsconfig-paths/register"
],
"watch-files": [
"./src/**/*.ts",
"./tests/**/*.spec.ts"
],
"spec": "./tests/**/*.spec.ts"
}

Cannot find module './collection/batch/unordered' - Node JS on Heroku

I have a problem that is driving me crazy for two days now. I have deployed a node app on heroku that worked well. Two days ago, I did an update of my modules (I am not 100% sure it is directly linked to my problem), and since I keep getting this message on my logs :
Error: Cannot find module './collection/batch/unordered'
at Function.Module._resolveFilename (module.js:338:15)
at Function.Module._load (module.js:280:25)
at Module.require (module.js:364:17)
at require (module.js:380:17)
at Object.<anonymous> (/app/node_modules/mongodb/lib/mongodb/collection.js:21:17)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Module.require (module.js:364:17)
Failed to load c++ bson extension, using pure JS version
Locally, the app works perfectly.
I tried to change versions back to what it was, without success.
So I checked in node_modules/mongodb/lib/mongodb, and indeed I did not find the "batch" folder and thus the unordered.js file, required by the node_modules/mongodb/lib/mongodb/collection.js file.
I tried to uninstall and re install mongodb. The batch folder and the unordered.js did appear. But the error still remains. And if I re deploy on Heroku, the batch folder is missing again.
I have strictly no idea what to try now, and I would deeply appreciate some help. Thank you !
EDIT :
Here is my packages.json :
{
"name": "Killerapp",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node app.js"
},
"engines": {
"node": "0.10.26"
},
"dependencies": {
"express": "3.5.1",
"ejs" : "~0.8.5",
"mongodb": "1.4.2",
"mongoose": "3.8.8",
"connect-mongo": "*",
"passport" : "~0.1.17",
"connect-flash" : "~0.1.1",
"bcrypt-nodejs" : "latest",
"moment" : "latest"
}
}
Is there a reason you have hardcoded a version of mongodb in your dependencies? I believe mongoose will pull in the right version...
Try removing mongodb from your package.json, remove your node_modules from git, then push to heroku.

Resources