How do you automatically download external (C++) libraries when using native Node addons? - node.js

I'd like to include libpng in my native Node addon. How can I include it, so that when my library is installed, it will automatically download a specified version of libpng? Is it possible to use npm's package.json for this? If this is not possible, what is the accepted way of including an external library's source code in your repository?

I recommend that you create a gyp file to build the dependency library and add a script to your package.json to download it for you.
I often use my own native addon modules to demonstrate the answers I give to these questions. My own native addon module, node-dvbtee, demonstrates this.
You will notice the following inside package.json:
"scripts": {
"preinstall": "npm install mkdirp && scripts/prepare-build.sh && node scripts/configure-build.js",
"install": "node-gyp rebuild -j 8",
"test": "mocha"
},
What matters here is the preinstall section of the scripts section. It calls scripts/prepare-build.sh, which contains the following:
#!/bin/sh
cd "$(dirname "$0")"/..
if [ -e libdvbtee ]; then
echo libdvbtee sources present
else
git clone git://github.com/mkrufky/libdvbtee.git
fi
cd libdvbtee
if [ -e libdvbpsi/bootstrap ]; then
echo libdvbpsi sources present
else
rm -rf libdvbpsi
git clone git://github.com/mkrufky/libdvbpsi.git
cd libdvbpsi
touch .dont_del
cd ..
fi
As you can see, the above script checks to see if the libdvbtee directory is present. If not, it will clone it from github. After that, it checks to see if the full libdvbpsi sources are present. If not, it will clone them from github.
Now, for the gyp files:
My project has the gyp files stored in the deps directory.
libdvbpsi.gyp looks like this:
{
'target_defaults': {
'default_configuration': 'Debug',
'configurations': {
'Debug': {
'defines': [ 'DEBUG', '_DEBUG' ],
'msvs_settings': {
'VCCLCompilerTool': {
'RuntimeLibrary': 1, # static debug
},
},
},
'Release': {
'defines': [ 'NDEBUG' ],
'msvs_settings': {
'VCCLCompilerTool': {
'RuntimeLibrary': 0, # static release
},
},
}
},
'msvs_settings': {
'VCLinkerTool': {
'GenerateDebugInformation': 'true',
},
},
'include_dirs': [
'../libdvbtee/libdvbpsi/src',
'../libdvbtee/libdvbpsi/src/tables',
'../libdvbtee/libdvbpsi/src/descriptors',
'../libdvbtee/libdvbpsi'
],
'defines': [
'PIC',
'HAVE_CONFIG_H'
],
},
'targets': [
# libdvbpsi
{
'target_name': 'dvbpsi',
'product_prefix': 'lib',
'type': 'static_library',
'sources': [
'../libdvbtee/libdvbpsi/src/dvbpsi.c',
'../libdvbtee/libdvbpsi/src/psi.c',
'../libdvbtee/libdvbpsi/src/demux.c',
'../libdvbtee/libdvbpsi/src/descriptor.c',
'../libdvbtee/libdvbpsi/src/tables/pat.c',
'../libdvbtee/libdvbpsi/src/tables/pmt.c',
'../libdvbtee/libdvbpsi/src/tables/sdt.c',
'../libdvbtee/libdvbpsi/src/tables/eit.c',
# '../libdvbtee/libdvbpsi/src/tables/cat.c',
'../libdvbtee/libdvbpsi/src/tables/nit.c',
'../libdvbtee/libdvbpsi/src/tables/tot.c',
# '../libdvbtee/libdvbpsi/src/tables/sis.c',
# '../libdvbtee/libdvbpsi/src/tables/bat.c',
# '../libdvbtee/libdvbpsi/src/tables/rst.c',
'../libdvbtee/libdvbpsi/src/tables/atsc_vct.c',
'../libdvbtee/libdvbpsi/src/tables/atsc_stt.c',
'../libdvbtee/libdvbpsi/src/tables/atsc_eit.c',
'../libdvbtee/libdvbpsi/src/tables/atsc_ett.c',
'../libdvbtee/libdvbpsi/src/tables/atsc_mgt.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_02.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_03.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_04.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_05.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_06.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_07.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_08.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_09.c',
'../libdvbtee/libdvbpsi/src/descriptors/dr_0a.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_0b.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_0c.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_0d.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_0e.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_0f.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_10.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_11.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_12.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_13.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_14.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_1b.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_1c.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_24.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_40.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_41.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_42.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_43.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_44.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_45.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_47.c',
'../libdvbtee/libdvbpsi/src/descriptors/dr_48.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_49.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_4a.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_4b.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_4c.c',
'../libdvbtee/libdvbpsi/src/descriptors/dr_4d.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_4e.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_4f.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_50.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_52.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_53.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_54.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_55.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_56.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_58.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_59.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_5a.c',
'../libdvbtee/libdvbpsi/src/descriptors/dr_62.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_66.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_69.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_73.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_76.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_7c.c',
'../libdvbtee/libdvbpsi/src/descriptors/dr_81.c',
'../libdvbtee/libdvbpsi/src/descriptors/dr_83.c',
'../libdvbtee/libdvbpsi/src/descriptors/dr_86.c',
# '../libdvbtee/libdvbpsi/src/descriptors/dr_8a.c',
'../libdvbtee/libdvbpsi/src/descriptors/dr_a0.c',
'../libdvbtee/libdvbpsi/src/descriptors/dr_a1.c',
],
'conditions': [
['OS=="mac"',
{
'xcode_settings': {
'WARNING_CFLAGS': [
'-Wno-deprecated-declarations'
]
}
}
]
],
'cflags!': ['-Wdeprecated-declarations','-Wimplicit-function-declaration'],
'cflags+': ['-Wno-deprecated-declarations','-Wno-implicit-function-declaration','-std=c99'],
},
]
}
Of course, there are a lot of specifics in this gyp file that are specific to libdvbpsi and my use case. As such, you will notice that quite a few of the source files in the library are not actually needed for the version of it that we're going to build for my node.js addon module. The source files that we are not going to build are commented out by preceding that line with a hash # character.
We link this library to the node module we're currently building by including it in the dependency section of the node.js addon modules bindings.gyp. Here is the one used in my addon module:
{
"targets": [
{
"target_name": "dvbtee",
"sources": [
"src/node-dvbtee.cc",
"src/dvbtee-parser.cc"
],
"dependencies": [
'deps/libdvbtee.gyp:dvbtee_parser'
],
"include_dirs": [
"libdvbtee/usr/include",
"libdvbtee/libdvbtee",
"libdvbtee/libdvbtee/decode",
"libdvbtee/libdvbtee/decode/table",
"libdvbtee/libdvbtee/decode/descriptor",
"<!(node -e \"require('nan')\")"
],
'cflags': [ '-DDEBUG_CONSOLE=1' ],
'cflags_cc': [ '-DDEBUG_CONSOLE=1', '-Wno-deprecated-declarations' ],
'cflags!': [ '-fno-exceptions' ],
'cflags_cc!': [ '-fno-exceptions', '-Wdeprecated-declarations' ],
'conditions': [
['OS=="mac"', {
'xcode_settings': {
'WARNING_CFLAGS': [
'-Wno-deprecated-declarations'
],
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES'
}
}]
]
}
]
}
As you can see, deps/libdvbtee.gyp:dvbtee_parser is listed in the dependencies section, above. deps/libdvbtee.gyp:dvbtee_parser itself contains its own dependencies section:
"dependencies": [
'libdvbpsi.gyp:dvbpsi'
],
So, when npm install is executed, npm will run the preinstall script to fetch the sources, then it will build the custom libdvbpsi library based on libdvbpsi.gyp, then build the custom libdvbtee based on libdvbtee.gyp which depends on that custom libdvbpsi library, and finally it will build and link the node.js addon module that depends on the libdvbtee library build.
In my specific case, the libraries need to be configured before we attempt to build them. This step is required to write the config.h header file that these libraries depend on. I handle that step within the scripts/configure-build.js script which is run after downloading the sources. In most cases, you will want to simply run ./configure for each library, but that depends on the libraries that you're including.
This is a cross-platform solution, provided that the libraries you're building are themselves cross-platform.

You can add it in scrips section in package.json. But you have to be careful about which all devices your application will be executed. Such as ARM, Intel 32 bit or Intel 64 bit, or so. You have options, I am just adding some hints here and you can put your code accordingly. Here script will get executed during npm install command.
1. In script, you have to check the machine type and do download library accordingly.
//package.json
{
"scripts": {
"preinstall": ""
,"install": ""
,"test" : ""
}
}
In script, you have to check the machine type and do download library accordingly, something like using wget abc.so in install section of script. You have to do some scripting to take right lib for machine and put in right place.
In other way if you want, you can add build script, which will do download the source code and build in the system on the fly.
git clone git://xyz/abc.git
cd abc
./configure
make
make install.
You can also look for babel cli for source compilation. https://babeljs.io/docs/usage/cli/
And all these within scripts section, within preinstall or install or test.
In your case, you would prefer to go for 1st way.

Related

Webpack error after upgrading Node: "Module parse failed: Unexpected token"

I'm troubleshooting a webpack error.
Command: bin/webpack --colors --progress
Produces this error:
ERROR in ./node_modules/#flatfile/sdk/dist/index.js 351:361
Module parse failed: Unexpected token (351:361)
File was processed with these loaders:
* ./node_modules/babel-loader/lib/index.js
You may need an additional loader to handle the result of these loaders.
| class v extends i {
| constructor(e, t) {
> super(e), r(this, "code", "FF-UA-00"), r(this, "name", "UnauthorizedError"), r(this, "debug", "The JWT was not signed with a recognized private key or you did not provide the necessary information to identify the end user"), r(this, "userMessage", "There was an issue establishing a secure import session."), e && (this.debug = e), this.code = t ?? "FF-UA-00";
| }
| }
# ./app/javascript/src/app/pages/content_assets/Index.vue?vue&type=script&lang=ts& (./node_modules/babel-loader/lib??ref--8-0!./node_modules/vue-loader/lib??vue-loader-options!./app/javascript/src/app/pages/content_assets/Index.vue?vue&type=script&lang=ts&) 22:0-41 125:6-14
# ./app/javascript/src/app/pages/content_assets/Index.vue?vue&type=script&lang=ts&
# ./app/javascript/src/app/pages/content_assets/Index.vue
# ./app/javascript/packs/app.js
NOTES
I found what appears to be an identical issue reported in the Flatfile project: https://github.com/FlatFilers/sdk/issues/83
Looks like ES2020 was emitted to the /dist folder so my cra babel loader is not able to parse it, in order to fix it I need to include the path on my webpack config.
Node v16.13.1
We're using webpack with a Rails project via the webpacker package (#rails/webpacker": "5.4.3") which is depending on webpack#4.46.0.
When I change to Node v14x and rebuild node_modules (yarn install) webpack compiles successfully.
The line referenced in the error (351:361) does not exist when I go check the file in node_modules/
We have a yarn.lock file, which I delete and recreate before running yarn install. I also delete the node_modules directory to ensure a "fresh" download of the correct packages.
We have a babel.config.js file...
module.exports = function(api) {
var validEnv = ['development', 'test', 'production']
var currentEnv = api.env()
var isDevelopmentEnv = api.env('development')
var isProductionEnv = api.env('production')
var isTestEnv = api.env('test')
if (!validEnv.includes(currentEnv)) {
throw new Error(
'Please specify a valid `NODE_ENV` or ' +
'`BABEL_ENV` environment variables. Valid values are "development", ' +
'"test", and "production". Instead, received: ' +
JSON.stringify(currentEnv) +
'.'
)
}
return {
presets: [
isTestEnv && [
'#babel/preset-env',
{
targets: {
node: 'current'
}
}
],
(isProductionEnv || isDevelopmentEnv) && [
'#babel/preset-env',
{
forceAllTransforms: true,
useBuiltIns: 'entry',
corejs: 3,
modules: false,
exclude: ['transform-typeof-symbol']
}
],
["babel-preset-typescript-vue", { "allExtensions": true, "isTSX": true }]
].filter(Boolean),
plugins: [
'babel-plugin-macros',
'#babel/plugin-syntax-dynamic-import',
isTestEnv && 'babel-plugin-dynamic-import-node',
'#babel/plugin-transform-destructuring',
[
'#babel/plugin-proposal-class-properties',
{
loose: true
}
],
[
'#babel/plugin-proposal-object-rest-spread',
{
useBuiltIns: true
}
],
[
'#babel/plugin-transform-runtime',
{
helpers: false,
regenerator: true,
corejs: false
}
],
[
'#babel/plugin-transform-regenerator',
{
async: false
}
]
].filter(Boolean)
}
}
Ultimately I want to get webpack to compile. If you had advice about any of the following questions, it would help a lot.
Why would changing the Node version (only) cause different webpack behavior? We aren't changing the the webpack version or the version of the #flatfile package that's causing the error.
Why is the error pointing to a line that doesn't exist in the package? Is this evidence of some kind of caching problem?
Does the workaround mentioned in the linked GitHub issue shed light on my problem?
I'll take a stab at this.
I believe your issue is that webpack 4 does not support the nullish coalescing operator due to it's dependency on acorn 6. See this webpack issue and this PR comment.
You haven't specified the exact minor version of Node.js 14x that worked for you. I will assume it was a version that did not fully support the nullish coalescing operator, or at least a version that #babel/preset-env's target option understood to not support ??, so it was transpiled by babel and thus webpack didn't complain. You can see what versions of node support nullish coalescing on node.green.
I don't fully understand the point you are making here, so not focusing on this in the proposed solution.
I'm not sure what the proposed workaround is in the linked issue, maybe the comment about "include the path on my webpack config", but yes the issue does seem relevant as it is pointing out the nullish coalescing operator as the source of the issue.
You can try to solve this by
adding #babel/plugin-proposal-nullish-coalescing-operator to your babel config's plugins
updating your webpack config to run #flatfile/sdk through babel-loader to transpile the nullish coalescing operator:
{
test: /\.jsx?$/,
exclude: filename => {
return /node_modules/.test(filename) && !/#flatfile\/sdk\/dist/.test(filename)
},
use: ['babel-loader']
}
Another possibility is to upgrade webpacker to a version that depends upon webpack v5.
One final remark, when you say
We have a yarn.lock file, which I delete and recreate before running yarn install.
you probably should not be deleting the lock file before each install, as it negates the purpose of a lock file altogether.

Nuxt installation error : Rule can only have one resource source (provided resource and test + include + exclude)

I successfully installed Vuejs and Nodejs but got a problem when installing Nuxtjs. This is what I get. I already asked some friends but it didn't work. Thanks for your help ! :)
Error : Rule can only have one resource source (provided resource and test + include + exclude)
Rule can only have one resource source (provided resource and test + include + exclude) in {
"use": [
{
"loader": "C:\\Users\\User\\Desktop\\JS\\my-first-project\\node_modules\\babel-loader\\lib\\index.js",
"options": {
"configFile": false,
"babelrc": false,
"cacheDirectory": true,
"envName": "server",
"presets": [
[
"C:\\Users\\User\\Desktop\\JS\\my-first-project\\node_modules\\#nuxt\\babel-preset-app\\src\\index.js",
{
"corejs": {
"version": 3
}
}
]
]
},
"ident": "clonedRuleSet-30[0].rules[0].use[0]"
}
]
}
"use": [
{
"loader": "C:\\Users\\User\\Desktop\\JS\\my-first-project\\node_modules\\babel-loader\\lib\\index.js",
"options": {
"configFile": false,
"babelrc": false,
"cacheDirectory": true,
"envName": "server",
"presets": [
[
"C:\\Users\\User\\Desktop\\JS\\my-first-project\\node_modules\\#nuxt\\babel-preset-app\\src\\index.js",
{
"corejs": {
"version": 3
}
}
]
]
},
"ident": "clonedRuleSet-30[0].rules[0].use[0]"
}
]
}
at checkResourceSource (node_modules\#nuxt\webpack\node_modules\webpack\lib\RuleSet.js:167:11)
at Function.normalizeRule (node_modules\#nuxt\webpack\node_modules\webpack\lib\RuleSet.js:198:4)
at node_modules\#nuxt\webpack\node_modules\webpack\lib\RuleSet.js:110:20
at Array.map (<anonymous>)
at Function.normalizeRules (node_modules\#nuxt\webpack\node_modules\webpack\lib\RuleSet.js:109:17)
at new RuleSet (node_modules\#nuxt\webpack\node_modules\webpack\lib\RuleSet.js:104:24)
at new NormalModuleFactory (node_modules\#nuxt\webpack\node_modules\webpack\lib\NormalModuleFactory.js:115:18)
at Compiler.createNormalModuleFactory (node_modules\#nuxt\webpack\node_modules\webpack\lib\Compiler.js:636:31)
at Compiler.newCompilationParams (node_modules\#nuxt\webpack\node_modules\webpack\lib\Compiler.js:653:30)
at Compiler.compile (node_modules\#nuxt\webpack\node_modules\webpack\lib\Compiler.js:661:23)
at node_modules\#nuxt\webpack\node_modules\webpack\lib\Watching.js:77:18
at AsyncSeriesHook.eval [as callAsync] (eval at create (node_modules\tapable\lib\HookCodeFactory.js:33:10), <anonymous>:22:1)
at AsyncSeriesHook.lazyCompileHook (node_modules\tapable\lib\Hook.js:154:20)
at Watching._go (node_modules\#nuxt\webpack\node_modules\webpack\lib\Watching.js:41:32)
at node_modules\#nuxt\webpack\node_modules\webpack\lib\Watching.js:33:9
at Compiler.readRecords (node_modules\#nuxt\webpack\node_modules\webpack\lib\Compiler.js:529:11)
npm i -D webpack#^4.46.0 try this, it worked for me.
I've had the same issue today, it seems to be related to an npm dependencies resolution issue.
I have opened an issue in nuxt.js repository
In my project, the issue was present, cause of #nuxtjs/eslint-module, you can remove it and regen dependencies :
npm uninstall #nuxtjs/eslint-module
rm -rf node_modules package-lock.json
npm install
You will not longer have eslint feedbacks in your build command, but you can still use npm run lint, and you will be able to use nuxt until the issue will be fixed.
I ran into this same error while trying to upgrade one of my old NuxtJS projects (using sass) built on node version 12 to version 16.
To fix this, i also installed #nuxtjs/style-resources that matches my versions of sass-loader and node-sass.
To confirm, uninstall the ones you already have, and run
npm install --save-dev node-sass sass-loader#10 fibers #nuxtjs/style-resources
see this article for more
This happened to me when I installed the most recent version of copy-webpack-plugin in Nuxt v2 project. Apparently it doesn't use webpack5 so I had to downgrade copy-webpack-plugin to last compatible version e.g. copy-webpack-plugin#4.6.0

ESlint override rule by nested directory

I want to disable rule for all files inside nested directory. I found examples only for exact path or by file extension. But it is not what I want.
We use it for shared config and don't know where this directory will be. We have many of them.
I'm trying config like this:
{
overrides: [
{
files: [
'**/test/**/*',
],
rules: {
"import/no-extraneous-dependencies": "off"
}
},
],
}
But glob like **/test/**/* and many others didn't not work.
Can someone help to reach this goal?
The above code should work.
How were you testing this? If it's an extension like VSCode you may need to refresh things to see latest definitions loaded.
If you are using a eslint service like esprint you will also need to restart it to grab latest definitions.
Caching
Make sure that eslint is not configured to cache results to avoid having to cache bust when debugging things. eslint docs
Here's an example for a react-native app with multiple overrides
module.exports = {
...baseConfig,
overrides: [
typescriptOverrides,
e2eOverrides,
themeOverrides,
{
files: ['**/*.style.js'],
rules: {
'sort-keys': [
'error',
'asc',
{
caseSensitive: true,
natural: true,
},
],
},
},
{
files: ['**/*.test.js'],
rules: {
'arrow-body-style': 'off',
},
},
],
};
Debugging the glob matcher
Run eslint in debug mode and see all the files being run example DEBUG=eslint:cli-engine npx eslint src/**/*.test.js
You can test your glob patterns by running a ls command. Example: ls ./src/**/*.test.js will either return all the files or 'no matches found'.

node-gyp ignores (c++17) cflag

I try to configure & build a node.js C++ addon with this
binding.gyp file:
{
"targets": [
{
"target_name": "addon",
"sources": [ "addon.cpp" ],
"cflags": [
"-std=c++17"
]
}
]
}
But when I run node-gyp configure and node-gype rebuild
I always get messages like
warning: ‘if constexpr’ only available with -std=c++17 or -std=gnu++17
The build also fails, because I really depend on these c++17 features. What am I doing wrong?
cflags cflags_cc was not working for me, but with the setting in VCCLCompilerTool it works (on Windows):
{
'targets': [
{
'target_name': 'test-napi-native',
'sources': [ 'src/test_napi.cc' ],
'include_dirs': ["<!#(node -p \"require('node-addon-api').include\")"],
'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"],
'cflags': [ '-fno-exceptions' ],
'cflags_cc': [ '-fno-exceptions' ],
'xcode_settings': {
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
'CLANG_CXX_LIBRARY': 'libc++',
'MACOSX_DEPLOYMENT_TARGET': '10.7'
},
'msvs_settings': {
'VCCLCompilerTool': { "ExceptionHandling": 1, 'AdditionalOptions': [ '-std:c++17' ] }
}
}
]
}
Using "cflags_cc" (instead of "cflags") works.
This solved the problem.
The default is against your will
node-gyp has default settings.
argv.push('-I', addonGypi)
argv.push('-I', commonGypi)
argv.push('-Dlibrary=shared_library')
argv.push('-Dvisibility=default')
argv.push('-Dnode_root_dir=' + nodeDir)
if (process.platform === 'aix' || process.platform === 'os390' || process.platform === 'os400') {
argv.push('-Dnode_exp_file=' + nodeExpFile)
if (process.platform === 'os390' && zoslibIncDir) {
argv.push('-Dzoslib_include_dir=' + zoslibIncDir)
}
}
argv.push('-Dnode_gyp_dir=' + nodeGypDir)
// Do this to keep Cygwin environments happy, else the unescaped '\' gets eaten up,
// resulting in bad paths, Ex c:parentFolderfolderanotherFolder instead of c:\parentFolder\folder\anotherFolder
if (win) {
nodeLibFile = nodeLibFile.replace(/\\/g, '\\\\')
}
argv.push('-Dnode_lib_file=' + nodeLibFile)
argv.push('-Dmodule_root_dir=' + process.cwd())
argv.push('-Dnode_engine=' +
(gyp.opts.node_engine || process.jsEngine || 'v8'))
You need to override the default settings described in node-gyp/addon.gypi and node/common.gypi’s cflags (C & C++), cflags_cc (C++), ldflags (Linux), msvs_settings (Windows), and xcode_settings (OS X). All of them.
In your case, you only set the cflags. So, other default settings were left not overridden and thus in effect.
Specifically, you’d want to override the default settings about:
the C++ standard to conform,
cflags_cc
msvs_settings.VCCLCompilerTool.AdditionalOptions
xcode_settings.CLANG_CXX_LANGUAGE_STANDARD
floating point arithmetics,
msvs_settings.VCCLCompilerTool.FloatingPointModel
exceptions,
cflags_cc
msvs_settings.VCCLCompilerTool.ExceptionHandling
xcode_settings.GCC_ENABLE_CPP_EXCEPTIONS
xcode_settings.GCC_ENABLE_CPP_RTTI
optimization,
the warning level,
et cetera.
If you don’t, unintented default settings might be applied.
Why it defaults to this
Because the Node.js executable is built with the settings and node-gyp adopted the settings for the default settings for unknown reasons.
From the nodejs/node-gyp issue #26 — “Overriding default flags”:
edhemphill: Does anyone know why -fno-rtti was used as a default? Any particular reason?
TooTallNate: #edhemphill node-gyp uses the same configuration as when node itself it built, so the answer is because node itself doesn’t need rtti.

Packaging Keytar with an Electron app

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.
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);
window.require("electron").remote.require("keytar")
Since you are working on renderer process and want to use native api from system or main process.
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.

Resources