I'm developing npm package (my_package
) that is using domain-specific C/C++ libraries (c-library
) via Node-API. Such setup has a reason, because this C/C++ library is being well maintained and rewriting it in JS/TS would not be feasible.
The problem I'm currently facing is: how do I distribute prebuilt modules of that library?
Normally I would make use of node-gyp
, but another constraint I'm having is that my npm package will be distributed via third-party platform, which installs additional modules/plugins by exec'ing npm install <my_package>
(and later using some magic to load them as UI and functionality addons). It is not guaranteed that node-gyp
is available on this third-party platform, or maybe some of their dependencies are missing (like python3) since there are different docker images of this "platform" with different builds and tools available.
Furthermore, building C/C++ library could take substantial amount of time on small VPS (where the platform is hosted), and as I would like installation process to be as straightforward as possible, I would like to avoid users having to spend their time here.
The binary itself is pretty big (15-20MB) and including all prebuilt binaries for all architectures into my_package
would also not be a good solution IMO.
Bottom line is: my npm package loads binary lib and it is being installed by running npm install <my_package>
, and I cannot influence that. I also cannot influence environment in which my package is installed therefore I cannot rely on node-gyp
. How do I deliver pre-compiled binary modules of the library to run on: linux-x64-glibc
, linux-x64-musl
, linux-arm64-glibc
, linux-arm64-musl
, darwin-x64
, darwin-arm64
?
I thought of different options:
- Publish arch-specific packages of
my_package
(i.e.my_package-linux-x64-glibc
,my_package-arm64-musl
etc). Include "hard-coded" arch-specific dependency toc-library
intopackage.json
ofmy_package
.
package.json (of `my_package-linux-x64-glibc`)
{
...
dependencies: {
`c-library-linux-x64-glibc`: "*"
}
...
}
I do not like it, because it makes installation process more complicated for the user: they need to be aware where their platform is hosted and install exact "variation" of my_package
that will run there. Kind of complicated.
- Publish
my_package
as one "generic" package. Include arch-specific dependency toc-library
inpackage.json
based on some conditioning which will be checked whennpm install my_package
is executed.
User would not have to deal with all these process/architecture things, but rather simply install one "facade" my_package
, that evaluates environment at runtime and pulls in prebuilt c-library
(for example c-library-linux-x64-musl
) depending on conditions.
package.json (of `my_package`)
{
...
dependencies: {
'linux-x64': {
`c-library-linux-x64-glibc`: "*"
}
}
...
}
Problem: I'm not able to include dependencies that are based on conditions in package.json
of my_package.
- Publish
my_package
as one "generic" package. Usepostinstall.js
lifecycle script to do runtime evaluation of environment and then exec"npm install c-library-" + process.platform + "-" + process.arch"
to fetch proper prebuilds.
This was a breakthrough idea, but apparently, because npm install c-library-...
is executed from within the npm install my_package
command's "scope", it is removing all the dependencies of my_package
. Due to the fact that npm install my_package
is updating package-lock.json
file after postinstall scripts have been executed. Postinstall is run on line 95, dependencies are saved/added starting from line 110:
95 info run [email protected] postinstall node_modules/my_package node postinstall.js install
96 info run [email protected] postinstall { code: 0, signal: null }
97 timing build:run:postinstall:node_modules/my_package Completed in 61ms
98 timing build:run:postinstall Completed in 61ms
99 timing build:deps Completed in 63ms
100 timing build Completed in 63ms
101 timing reify:build Completed in 63ms
102 timing reify:trash Completed in 0ms
103 timing reify:save Completed in 3ms
104 http fetch POST 200 http://0.0.0.0:4873/-/npm/v1/security/advisories/bulk 464ms
105 timing auditReport:getReport Completed in 468ms
106 silly audit report {}
107 timing auditReport:init Completed in 0ms
108 timing reify:audit Completed in 470ms
109 timing reify Completed in 2007ms
110 silly ADD node_modules/ms
111 silly ADD node_modules/typedi
- Use
node-pre-gyp
that would try to find prebuilt version ofc-library
somewhere in aws s3, if it is not found - fallback to compilation innode-gyp
way.
I have not tried that, and I'm a bit sceptical here because of different platforms/architectures and libc variants that need to be supported. I would also need to include source code of c-library
as git submodule of my_package
which is maybe not the best way to organise components.
Additionally, it does not look that node-pre-gyp
is being regularly maintained.
I would appreciate if someone could share their idea on the above and perhaps open my eyes on some other option that I have not still discovered.