I have a similar question to this, but not quite the same.
I would like for the user of my app to install it with whatever dependencies are needed for the way he would want to use it. So, for example, if they want to persist to MongoDB, then only Mongo-related libraries will be installed, but if they want to persist to Redis, then only Redis-related libraries will be installed. I don't want to make them download and install libraries they won't be using.
I know I can do that for development purposes with devDependencies
, but this goes farther than that. As the answer in the question above says, this is more closely related to Python's setuptools
extras_require
and Clojure's leiningen
profiles. Anything like that in npm? I really feel like devDependencies
should be a dev
profile of a more versatile way of specifying dependencies.
4 Answers 4
The codependency module may be what you're looking for, or anything that does something similar to:
- declare optional dependencies in
package.json
that aren't automatically installed bynpm install
, sayoptionalPeerDependencies
- a custom
require
-style function that knows aboutoptionalPeerDependencies
and does the right thing, including throwing/warning when nothing is found that fulfills a required class of modules (e.g. neitherredis
, normongo
, normysql
, etc. are installed). - document the expectation that consumers of this module install at least 1 of the optional peer modules
One variation would be if the module's core functionality works without any optional dependencies (e.g. plugin pattern), no error/warning when nothing is found that fulfills a peer dependency.
Another variation is doing the list above while accounting for production versus development dependencies, i.e. an analog for dependencies
and devDependencies
.
Perhaps combined with an on-demand require such that optional modules are required lazily, e.g.:
exports = {
Core : require('./core'),
get redis(){ return require('./redis'); },
get mongo(){ return require('./mongo'); }
}
-
I haven't needed this for a while, but I think it solves the problem I had. Thanks!imiric– imiric2015年02月22日 09:36:15 +00:00Commented Feb 22, 2015 at 9:36
-
2Yeah I figured it being months old you had probably figured it out or moved on. I found your question whilst searching for answers myself so this was mostly for posterity. More than once I've gone searching, only to find an answer from myself written a few years prior. So consider this enlightened self interest. Also, updated the answer to describe in general what the
codependency
module provides in the event that module evaporates from NPM and because links without excerpts are bad SO form.user65730– user657302015年02月24日 06:13:30 +00:00Commented Feb 24, 2015 at 6:13
If you want simple optional dependencies like plugins, e.g. if you install foo you will run it colorful but if not installed, you don't have any problem and see it in gray, then you could use optionalDependecies in the package.json:
{
"name": "watchit",
"version": "1.2.3",
"optionalDependencies": {
"foo": "^2.0.0"
}
}
And in the code:
try {
var foo = require('foo')
var fooVersion = require('foo/package.json').version
} catch (er) {
foo = null
}
if ( notGoodFooVersion(fooVersion) ) {
foo = null
}
// .. then later in your program ..
if (foo) {
foo.doFooThings()
}
Extracted from the package.json documentation.
-
2npm will install optional dependencies by default so this doesn't help OP's original use-case.villasv– villasv2020年09月15日 15:18:16 +00:00Commented Sep 15, 2020 at 15:18
What I do is configure an install script in my package.json, inside scripts
, like this:
"install": "node ./my-tools/my-install.js",
It will run right after npm install
finishes. I use it mostly for auto-generating a .env
file with defaults.
The my-install.js
script could run different commands, create files, ask for user input, so there you could say "Want Redis or Mongo?":
const exec = require('child_process').exec;
const readline = require('readline');
// Insert "Ask question script" here
// using readline core module
if ( option == 'mongo' )
exec('npm install mongoose');
if ( option == 'redis' )
exec('npm install redis');
This is a very quick answer, check out readline for reading user input properly and child process for running commands and processing output, etc.
Also notice that the install script could be whatever you wish (python, bash, etc)
-
9Asking for user input will screw up automated builds. Running
npm install
again inside of an install script may also trigger unintended behavior. I do not recommend this solution.Lambda Fairy– Lambda Fairy2018年10月03日 02:33:06 +00:00Commented Oct 3, 2018 at 2:33
npm really wasn't designed for this, as one of the hardest parts of dependency management is ensuring fast, reproducible builds that are easy and relatively failsafe. But I believe there is a use case, and there certainly was for me. So I wrote a package to do exactly what you are asking for.
My package is install-subset
, and can be installed globally with npm install -g install-subset
https://www.npmjs.com/package/install-subset
First, you build whitelists and blacklists for named install subsets in your package.json like this:
"subsets": {
"build": {
"whitelist": [
"babel-cli",
"dotenv"
]
},
"test": {
"blacklist": [
"eslint",
"lint-rules",
"prettier"
]
}
}
Then call it with, for example, install-subset test
This will temporarily rewrite your package.json to not install those packages blacklisted, then restore it, which depending on the packages can save a lot of time and bandwidth.
Also works with yarn, is open source and issues/PRs are welcome.
In many cases I use this on our ci server to lower build time, and on our latest React Native project, took our typical fresh developer install from 72 seconds to about 20 seconds.
MyPackage-Core
MyPackage-Db-Mongo
MyPackage-Db-Redis
etc... much they way people do bower modules that are meant to extend angularjs.package.json
that has been solved in other package managers.