npm package.json OS specific dependency

33,007

Solution 1

There's a possible good way of doing this, depending on your setup.

npm package.json supports an os key,

and also optionalDependencies

  • os can be used to specify which OS a module can be installed on.
  • optionalDependencies are module dependencies that if they cannot be installed, npm skips them and continues installing.

In this way you can have your module have an optional dependency for each OS, and only the one which works will be loaded/installed ^.^

EDIT: As @Sebastien mentions below, this approach is dangerous. For any given OS, at least one of your dependencies is "required" and the rest "optional". Making all versions of the dependency optional means that if your installation fails for a legitimate reason, it will silently skip installation and you will be missing a dependency you really need.

Solution 2

I think the short answer is no. I can think of a couple of workarounds though - the simplest is to just add everything to package.json regardless of OS, and then require() the correct one at runtime.

If that doesn't work for you, you might be able to use an install script to get the result you're going for - https://docs.npmjs.com/misc/scripts

I haven't tested this but I think it would work:

Add something like this to your package.json:

,"scripts": {
  "install": "node install_dependencies.js"
}

And then add a install_dependencies.js file that checks the OS and runs the appropriate npm install ... commands.

Solution 3

Quoting @npm_support at:

https://twitter.com/npm_support/status/968195526989512705

2/2 If you'd like to avoid installation problems related to dependencies, one route is for you to write a wrapper that's required as a regular dependency, and to make sure that it has optionalDeps (and also ensure that the wrapper verifies you have everything needed to work).

But IMHO it looks more like a workaround than solving the problem for real.

I can understand that npm wants to preserve portability and avoid to deal with platform specifics, but it has to be done anyway and IMHO doing this at runtime is not optimal (specialty if one wants do optimize code size).

So today I have no optimal solution to share but an open discussion for proposal.

Can't "conditional dependencies" be supported in npm ?

The 1st thing that came to my mind was to to add a "override" section that will change (+add, -remove, =replace) current parsed sections.

For example:

dependencies: { "common-stuff": "*" } overrides: { "os: { linux: { dependencies: { "+best-linux-module" } } } }

And other option suggested by a developer I know, would be to introduce a provides keyword, then several modules could provide a same semantic than would be satisfied by resolver (a la debian), but it's generating similar overhead.

I am looking for a generic approach not only focused on OS support but also on other flavors of package (depending on engines for instance).

Do you know any related issue in NPM tracker ? if not I am considering to file a bug to be tracked at:

https://github.com/npm/npm/issues?q=dependencies+conditional

Feedback welcome on this idea.

Solution 4

There's also the bindings-shyp module:

https://www.npmjs.com/package/bindings-shyp

Helper module for loading your native module's .node file

This is a helper module for authors of Node.js native addon modules. It is basically the "swiss army knife" of require()ing your native module's .node file.

Throughout the course of Node's native addon history, addons have ended up being compiled in a variety of different places, depending on which build tool and which version of node was used. To make matters worse, now the gyp build tool can produce either a Release or Debug build, each being built into different locations.

This module checks all the possible locations that a native addon would be built at, and returns the first one that loads successfully.

Share:
33,007
sandeepmistry
Author by

sandeepmistry

Updated on July 05, 2022

Comments

  • sandeepmistry
    sandeepmistry almost 2 years

    Is there a way to specify OS specific dependencies in a npm package.json file?

    For example, I would only want to install 'dbus' (https://npmjs.org/package/dbus) as a dependency for my module if the user is running Linux. I would have a different dependency for Mac and Windows.

  • sandeepmistry
    sandeepmistry about 11 years
    The problem is my dependencies have native bindings that only compile on a specific OS, so I can't have them as explicit dependencies. Your suggestion for the npm install script works, I used os.platform() to detect which platform the user is using;
  • TinyTimZamboni
    TinyTimZamboni over 9 years
    install scripts are now considered an 'antipattern' source. Instead should be using a .gyp compilation file
  • Metalskin
    Metalskin over 9 years
    Just found this answer (after replying to your comment about antipatterns). This looks like a far better solution than using an install script! Now that I know this is available, you can disregard my comment (I'll try and delete/edit it if it's not too late)
  • Metalskin
    Metalskin over 9 years
    Just an observation that wasn't clear to me with the above. If using package.json to manage installation for a project and your not publishing, then using .gyp is not the solution. Refer to the post below by TinyTimZamboni, it's far more suitable for this scenario (stackoverflow.com/a/26069595/1125784).
  • TinyTimZamboni
    TinyTimZamboni over 9 years
    I kinda wish I could delete my comment too, since I ended up using install scripts heavily, along with this solution >.<
  • chen bin
    chen bin over 8 years
    can you show me a complete example? say fsevents is optional dependency on OSX which I do NOT care because my build script is running on LINUX.
  • TinyTimZamboni
    TinyTimZamboni over 8 years
    @chenbin I use it in this package.json file: github.com/appium/sample-apps/blob/master/package.json
  • Sébastien BATEZAT
    Sébastien BATEZAT over 8 years
    This is a way, maybe the better way currently supported by npm, but not a good way. If you run npm install on the right OS and one of your optional dependency fail - for an unknown reason - npm will skip it and will not throw any error. The result will be a not working environment. Optional dependency is designed for dependencies that are optionals, here we want to manage dependency that are compulsory on a specific OS.
  • TinyTimZamboni
    TinyTimZamboni over 8 years
    Ah right, as @Sebastien points out: making your dependency 'optional' now means that if the installation errors for a legitimate reason, your code will ignore it.
  • Michael
    Michael over 7 years
    @TinyTimZamboni It's problematic when compiling something with gyp requires different compiler options or environment variables based on the platform
  • TarranJones
    TarranJones about 7 years
    >Don't use install. Use a .gyp file for compilation, and prepublish for anything else. You should almost never have to explicitly set a preinstall or install script. If you are doing this, please consider if there is another option. The only valid use of install or preinstall scripts is for compilation which must be done on the target architecture.
  • jonperl
    jonperl over 4 years
    Beware: optional dependencies are still installed, they just are skipped if it fails to install coderwall.com/p/rmosmg/npm-package-json-optionaldependencies So I recommend using an install script instead stackoverflow.com/a/15670089/230462
  • gilly3
    gilly3 over 4 years
    Regarding the "antipattern" of an install script, per NPM: "The only valid use of install or preinstall scripts is for compilation which must be done on the target architecture." And that is exactly the use case this question is asking about. Using optionalDependencies has bitten me. Follow the advice in this answer. Resolve at runtime if possible, otherwise use an install script.
  • Ben Ellis
    Ben Ellis about 2 years
    Can you not just add a check to the post-install to ensure that at least one of the optional dependencies is available/installed successfully?