How to get node-sqlite3 working on Mac M1? - node.js

I'm using Rosetta 2 with Homebrew and have sqlite3 installed.
I added these to my ~/.zshrc so that the node compiler can find the brew installs:
export PATH="/usr/local/opt/sqlite/bin:$PATH"
export LDFLAGS="-L/usr/local/opt/sqlite/lib"
export CPPFLAGS="-I/usr/local/opt/sqlite/include"
I'm using installing using npm install sqlite3, building from source with --build-from-source flag, I'm also specifying the homebrew version of sqlite with --sqlite=/usr/local/opt/sqlite/
node-gyp goes to its fallback build node-pre-gyp install --fallback-to-build
After installation, I'm rebuilding it's native dependencies with electron-builder and install-app-deps
It even rebuilds to the platform and arch I'm looking for, darwin and x64. Which is promising.
• electron-builder version=23.0.3
• loaded parent configuration preset=react-cra
• rebuilding native dependencies dependencies=sqlite3#5.0.8 platform=darwin arch=x64
However, when run the app with npm run dev which runs concurrently " cross-env BROWSER=none npm start" "wait-on http://localhost:3000 && electron ."
It still is trying to find the arm64 version of it:
Error: Cannot find module '[..]/node_modules/sqlite3/lib/binding/napi-v6-darwin-unknown-arm64/node_sqlite3.node'
Even though I can confirm, it did build the x64 version because I have one located at:
/napi-v6-darwin-unknown-x64/node_sqlite3.node
Any help to get this working would be greatly appreciated, thanks!

After 4 days of digging, I have finally gotten it to work!!!
For anyone that might stumble on this:
The reason why sqlite3 won't behave is threefold:
When node sqlite3 is installed using npm install sqlite3, it fetches all dependencies and installs it. It then fetches the precompiled binary binding file for the target arch and platform. In my case we would want napi-v6-darwin-unknown-arm64 for ARM64 and darwin for Apple M1. There is no precompiled binary available yet for this Apple ARM64 and even if there is, the next paragraph will detail why it still won't work.
The problem is that it determines the the system's platform and architecture using the binary compiling package node-pre-gyp and this very savior of a Github issue details how node-pre-gyp is not handling ARM architecture detection properly and basically mixing everything up. Because it's not detecting properly, even if we build our own binding with --build-from-source when installing, it still won't work because it is compiling the wrong binding file for the wrong architecture. To make matters worse, if we don't use --build-from-source, it just simply fetches the Intel precompiled binding file. napi-v6-darwin-unknown-x64
Now for some reason, during runtime, it now detects the architecture properly and tries to look for the ARM64 version of the binding file, it won't find it and will throw the feared error 'module not found' for napi-v6-darwin-unknown-arm64. It obviously won't find it and even if it does, it will throw wrong architecture error because we have the Intel version on board napi-v6-darwin-unknown-x64.
So finally after digging at this for 4 days, here's how I got it working:
Uninstall sqlite3: npm uninstall sqlite3
Install a fresh sqlite3, build it from source, specify the target arch and use fallback build with node-pre-gyp just to be safe: npm install sqlite3 --build-from-source --target_arch=arm64 --fallback-to-build
Now the correct binding file is compiled for the correct platform and architecture, on runtime it will find it and will run!

Related

Compile node module with node-gyp for a specific NODE_MODULE_VERSION

I am trying to use an npm package fsuipc in an electron app. However, when I start the app I get the following error
The module 'node_modules\fsuipc\build\Release\fsuipc.node'
was compiled against a different Node.js version using
NODE_MODULE_VERSION 93. This version of Node.js requires
NODE_MODULE_VERSION 98.
I am trying to recompile it for version 98. I have 0 experience with node-gyp or similar, I just want it to work :(. I have tried doing node-gyp build --target=v16.13.1 but it still compiles to this mysterious version 93 which I can't seem to change. Any help is appreciated.
After two days of research and endless trial and error I have found a solution.
This may have been an error in the electron boiler plate that I was using but the bottom line is that I had to manually build the native package. From here I learned that I was compiling it using my current node.js version installed but because I was using electron it required building the module with electron headers. The steps I followed were these:
cd release/app && npm install fsuipc Native dependencies have to be installed in release/app.
cd node_modules/fsuipc && npm install Move to the package dir and install its required dependencies for building.
node-gyp rebuild --target=15.3.5 --arch=x64 --dist-url=https://electronjs.org/headers This was the key. The target has to be the VALID and FULL version name that you are using from this website (if you are using electron). From trial and error I solved some problems by specifying that I was building for x64. And in order for it to compile using electron specify the headers url (same for all electron versions).
cd ../../ && npm install By calling install in the release/app directory the modules are linked with the "normal" node_modules
Then just run!

Node Canvas throwing error when running project

Node Canvas is not working for me on m1
When I try to run my project I get this error message:
Error: dlopen(.../node_modules/canvas/build/Release/canvas.node, 0x0001): tried: '.../node_modules/canvas/build/Release/canvas.node' (mach-o file, but is an incompatible architecture (have 'arm64', need 'x86_64')), '/usr/local/lib/canvas.node' (no such file), '/usr/lib/canvas.node' (no such file)
Already did:
brew install pkg-config cairo pango libpng jpeg giflib librsvg
xcode-select --install
npm i canvas
With no errors
Not sure how to debug this as there are people with Node Canvas working on m1 laptops, please help.
The most important thing is to make sure your Node executable has been compiled for Apple Silicon, meaning there's a pre-built binary for darwin-arm64. You can see which versions have which binaries here (the following page has the binaries for Node version 16.13.1): https://nodejs.org/download/release/v16.13.1/.
Node v14 has no pre-built binaries for darwin-arm64 (see https://github.com/nodejs/node/issues/36161), because it can't be compiled natively to Apple Silicon.
I use volta (https://volta.sh/) to manage my Node versions, so if you install Node v16 with volta install node#16 and then run npm i canvas, you should get a correctly compiled canvas.node file.
If that fails, go to node_modules/canvas and run npm install --build-from-source, which will compile the C++ addon and output the corect canvas.node.
You can check this by running:
file node_modules/canvas/build/Release/canvas.node
# outputs: node_modules/canvas/build/Release/canvas.node: Mach-O 64-bit bundle arm64
If it says arm64 at the end, then you're good to go. If it still says x86_64, then something went wrong.
I use npm install --build-from-source. It work!
But there are new mistakes
dlopen(.../node_modules/canvas/build/Release/canvas.node, 0x0001): symbol not found in flat namespace '_cairo_fill'

What does Prebuilt node builds mean?

I am trying to install the this node-printer npm package. There is a pre built node build section under which there is:
npm install printer --target_arch=ia32
npm install printer --target_arch=x64
Googling pre build node built did not hep me much. Searching for arget_arch=ia32 took me electron github issue page but I could not understand what it meant either
target_arch option seems to be coming from node-pre-gyp which node-printer runs as install script after it is installed.
By "prebuilt", that means you won't have to build the C++ code yourself. It is pre-built (== built in advance) by the node-printer package.

Can't run node.js module mdns in node webkit application

I have a node webkit application that uses the mdns module for publishing a Bonjour service from a Mac (with Mavericks). When I run the server code with node server.js everything works OK, but when running the node webkit application that uses the same server code I get this error:
"Uncaught Error: dlopen(/Users/me/myfolder/node_modules/mdns/build/Release/dns_sd_bindings.node, 1): no suitable image found. Did find:
/Users/me/myfolder/node_modules/mdns/build/Release/dns_sd_bindings.node: mach-o, but wrong architecture", source: /Users/me/myfolder/node_modules/mdns/lib/dns_sd.js (35)
Apparently when you install the mdns module with npm it's built for an x86 architecture and I need it for i386, because node-webkit is built for i386 (I found out about this by reading this thread: http://forums.macrumors.com/showthread.php?t=879780). You can verify it by running this in a terminal:
$ lipo -info /Applications/node-webkit.app/Contents/MacOS/node-webkit
Non-fat file: /Applications/node-webkit.app/Contents/MacOS/node-webkit is architecture: i386
I found this link suggesting a solution: https://github.com/rogerwang/node-webkit/issues/296 for another module (node proxy). The suggested instructions are:
I managed to build a 32-bit version of node-proxy as follows:
I installed nw-gyp
I ran nw-gyp configure --target=0.3.6
I edited the generated file nodeproxy.target.mk in the build directory by replacing -arch x86_64by -arch i386
I ran nw-gyp build
But as I'm not used to building node modules manually, while following the instructions it wasn't clear to me in which folder I should run those steps (I assumed it's in the module folder inside node_modules:
a) when I install nw-gyp I don't get the nw-gyp command to use globally (I guess the -g option is missing in the instructions)
b) using instead gyp configure --target=0.3.6 gives me an error saying that there is no option target
c) I tried skipping the configure step (just to try) and the build command breaks with:
Could not automatically locate src directory. This isa temporary
Chromium feature that will be removed. Use--depth as a workaround.
But when trying to use --depth (of course) it requires an argument, I couldn't find out what to put there.
So... how should I build the mdns module for using it with node webkit? (either the 0.8.6 version or the 0.10.0, I can adapt).
I managed to make it work.
As I had already installed the mdns module, I already had the source code of the module in the folder node_modules/mdns inside my project folder.
So these are the steps I followed to build the mdns module for the i386 architecture:
1) Install nw-gyp by running: npm install -g nw-gyp
2) Enter in the node_modules/mdns folder of your node-webkit project
3) Run nw-gyp configure --target=0.8.6 (this target is the version of the node-webkit you have installed)
4) And finally run nw-gyp build
I got a lot of warnings of deprecated functions, but it was built OK and now my node-webkit application can publish the Bonjour service successfully.
Unfortunately this is not the best solution, though, because the next person that installs the project will have to do the same after the regular npm install... But at least it's something to get it working.

If a node module is written in C, and I install it on Mac, do I have to reinstall it to deploy to Linux?

I frequently hear from experienced node developers that checking in node_modules is good practice. But most developers write on Mac/Darwin x64, but deploy on Linux x64.
If a node module happens to be written in C, and I install it on OS X, wouldn't I have to rebuild it on Linux?
The answer is: it depends on the package
Most packages do require reinstall, as node gyp the compiler does not cross compile by default - thanks #tkone.
Some packages like node-sass dynamically download prebuilt binaries for the relevant platform (previously node-sass used to include binaries for all platforms, however this has changed recently).
The now current node-sass 3.4.2 doesn't include the binaries in the originally downloaded npm package (as found in the npm package cache under ~/.npm).
What it does is that its install.js script will download the platform specific binary and store it under vendor/{platform}-{arch}-{process.versions.module}.node. Once installed, the install.js script normally isn't invoked again by npm.
So when you check in node_modules, it will contain the binary only for your initial platform.
For the fun of it I moved away the downloaded binary, which should be the same as someone else checking out your node_modules on a different platform. When then run, node-sass is so smart to detect that the required binary doesn't exist. It exits gracefully, recommending to run npm rebuild node-sass (also hinting that this is usually necessary when you change the node version). This will download the binary for the current platform, and then all is fine.
However, that's totally specific to node-sass, other packages with binaries may behave entirely different.
(See Node.js release versions for an explanation of process.versions.modules aka. NODE_MODULES_VERSION that determines the last number in the binary download URL. The binary name is constructed in function getBinaryName() in ~/.npm/node-sass/{version}/package.tgz/lib/extension.js )

Resources