npm install: Use global package if it exists, rather than installing twice - node.js

When using npm install with a package.json file, how do I get it to use a globally installed package that meets the criteria instead of downloading and installing the package locally again?
I know about link, but is there a way to do what I'm describing?

Yarn seems to work much better with repeated dependencies. So try yarn install instead of npm install.

One way of doing it for a specific set of modules is removing these modules from the dependencies section and creating a prestart script that contains all the modules that you prefer having installed globally.
A simple example might look something like this:
"scripts": {
"test": "mocha",
"prestart": "npm i -g mocha mysql bluebird"
},
Instead of prestart you can use one of the other hooks such as preinstall and prepare.
Note that this won't work as-is with packages you want to publish and would require a bit more hacking.
Help on running scriipts: https://docs.npmjs.com/misc/scripts

Related

How to install extra bin files when `npm install` is given `-g` flag?

Is there some way to install only part of a NodeJS project when run via npm install, but to install additional features when npm install -g is used?
I have a library with a command-line interface, however the CLI is not useful to any downstream projects using my library. So when those projects pull in my library, I don't want them to pull down dependencies like chalk that are only used for the CLI they will never touch.
However if an end user decides to install my library globally on their system with npm install -g then I want the CLI installed, and via the bin section in package.json, placed in their path so they can run it like any other program.
I can't work out how to do this without splitting the CLI into a separate package. The options I have investigated are:
Put the CLI dependencies as devDependencies. This prevents chalk etc. from being installed in downstream projects, but the drawback here is that the user must npm install -g in development mode, which means they get the test framework and linting tools installed as well even though they will never use them.
Put the CLI as a separate NodeJS package/module. The drawback here is that it makes testing difficult (as often the CLI and library are modified at the same time and used for testing new features), and developers wanting to contribute to the library will have to stuff around with linking the two packages so although it would work, it's less than ideal from a workflow perspective.
Put the CLI in a folder inside the main package, and create another package.json in there just for the CLI, pulling in the main project via npm install ... This works until you get to the install point, when you realise that there is no way to install the CLI once the package has been published. npm install #my/library will only install from the package.json in the project root, there's no way to say "oh also install the package in the cli subdirectory too."
Ideally what I would like is this:
npm install #my/library - run by a developer wanting to use the library in their project. Adds the library only to their project's dependencies, ignores both CLI and any dependencies the CLI needs.
npm install -g #my/library - run by an end-user, installs library and CLI globally on their system, including the CLI dependencies, and adds the CLI to the user's path via the package.json bin section.
npm install --dev - used by developer contributing to the library to install the test framework so they can run the unit tests before submitting their code for inclusion.
Not having to split the CLI into a separate project.
Is this possible?
Is there some way to install only part of a NodeJS project when run via npm install, but to install additional features when npm install -g is used?
You can write a postinstall script that uses is-globally-installed (or another similar package) to check if the module is installed globally, and then run whatever is appropriate to install the CLI (perhaps npm install -g for a separate package that just has the CLI).
Well I came up with a workaround.
I put the CLI back to development-only (with its requirements in devDependencies so it could be used by local devs, but they wouldn't get pulled in to downstream projects using the library). I then created another package for the CLI, but all it was is a package.json file and nothing else.
What this package does is depend on the main library, plus only the dependencies needed by the CLI. Then it uses the bin section to point to the CLI inside the main library, so no code is needed in this package - it is literally just one file. Like so:
{
"name": "#my/library-cli", // new module to install the CLI
"bin": {
"myprog": "./node_modules/#my/library/bin/myprog.js" // the command is inside the dependency
},
"dependencies": {
"#my/library": "*", // require the library itself where the CLI code sits
"command-line-args": "*" // dependency needed by the CLI
}
}
The only drawback with this is that this new package's dependencies need to be kept in sync with the main library's devDependencies (at least the deps required by the CLI) but as they won't change often in my case I can live with that.
I did try the postinstall hook as #Trott suggested but it didn't seem to work:
"scripts": {
"postinstall": "[ $npm_config_global == 'true' ] && (cd cli && npm install -g)"
}
For some reason it was incredibly slow and seemed to get stuck in a loop, trying and failing to install over and over. I'm also not sure it would've respected being launched via a command like npm install -g --prefix /my/install/folder as that non-standard prefix may not get passed through to the child npm process.

All the npm package not installed globally can't be executed directly

Symptom:
I can't execute the npm packages directly that are not installed globally. But I can execute it by npm scripts. How to fix it?
For example:
I installed gulp under the project:
npm install gulp --save-dev
Then I try to execute it by
gulp
zsh: command not found: gulp
But if I add a npm script to package.json:
"scripts": {
"test": "mocha --require intelli-espower-loader && gulp test",
"start": "gulp"
},
Then run
npm start
It can get executed without problem.
P.S.
Same issue with the mocha package, I can't execute mocha directly but I can execute npm test without problem.
Help Wanted:
What I can do to fix that issue?
Notice:
I'm not saying that I want to execute them globally, I just want to execute them under the project.
I don't know where goes wrong, but they are executable not long ago, just don't work recently!
In addition to #Ion's answer: You might need to add the path to the environment variables. In windows OS, it would be %AppData%\npm. For packages installed locally, you should be able to run them like
.\node_modules\.bin\gulp
If you want to execute them globally install with the -g flag
npm install gulp -g
To run directly you could also do ./node_modules/.bin/gulp
That is because the package not installed globally doesn't create a reference in the root node_modules folder . If you want to specifically use the local installed version to run globally on terminal then go to you environment variables and set the path to ./node_modules/gulp/bin/gulp.js or alternatively configure your npm to use the mentioned location as the gulp and execute node_modules/.bin/gulp
You have two choices:
Install the executable modules globally:
npm install -g gulp
This is good if it's a tool you use often (for many different projects). For gulp specifically, the globally installed gulp will also check to see if the project has its own gulp installed (in node_modules) and run that version of gulp. This means that each project can have its own version of gulp. But not all tools have this feature.
Use npx:
npx gulp
Newer versions of npm ships with a command called npx which will search through your project's node_modules directory to find a module to execute. This means you don't need to install project specific tools globally. Apart form avoiding global installs of commands (which may or may not be a security issue) it also allows you to install project specific versions of tools.
If your npm installation does not have npx you can install it from npm itself:
npm install -g npx

Can I specify what package.json to use for npm install and npm start?

by default, npm install and npm start look for package.json.
Is there a way to specify to use a different package.json file?
========
Something like package.json, package_dev.json, package_staging.json, package_dev_features.json when run npm install, i can specify which package*.json to be used instead of the default package.json
========
The reason is, it may be easier to handle using different packages for development / testing.
=========
Final solution:
At the end, i just have one package.json for each environment (or branch). and set environment for dependencies . e.g. 'myLodash':"github.com/.../myLodash.git#testing"
Then when I merge from one environment to another, (e.g. from testing to staging), I merge the package.json. and then run a script to check & update the dependencies. Then push the updated package.json
it is a bit more works but it works. :)
=========
You may use the dependencies and devDependencies in your package json file.
For installing a module only in development, use this:
npm install --save-dev request
For installing a module in production, use this:
npm install --save request

What is the difference between --save and --save-dev?

What is the difference between:
npm install [package_name]
and:
npm install [package_name] --save
and:
npm install [package_name] --save-dev
What does this mean? And what is really the effect of --save and -dev keywords?
The difference between --save and --save-dev may not be immediately noticeable if you have tried them both on your own projects. So here are a few examples...
Let's say you were building an app that used the moment package to parse and display dates. Your app is a scheduler so it really needs this package to run, as in: cannot run without it. In this case you would use
npm install moment --save
This would create a new value in your package.json
"dependencies": {
...
"moment": "^2.17.1"
}
When you are developing, it really helps to use tools such as test suites and may need jasmine-core and karma. In this case you would use
npm install jasmine-core --save-dev
npm install karma --save-dev
This would also create a new value in your package.json
"devDependencies": {
...
"jasmine-core": "^2.5.2",
"karma": "^1.4.1",
}
You do not need the test suite to run the app in its normal state, so it is a --save-dev type dependency, nothing more. You can see how if you do not understand what is really happening, it is a bit hard to imagine.
Taken directly from NPM docs docs#dependencies
Dependencies
Dependencies are specified in a simple object that maps a package name
to a version range. The version range is a string that has one or
more space-separated descriptors. Dependencies can also be identified
with a tarball or git URL.
Please do not put test harnesses or transpilers in your dependencies
object. See devDependencies, below.
Even in the docs, it asks you to use --save-dev for modules such as test harnesses.
--save-dev is used to save the package for development purpose.
Example: unit tests, minification..
--save is used to save the
package required for the application to run.
By default, NPM simply installs a package under node_modules. When you're trying to install dependencies for your app/module, you would need to first install them, and then add them to the dependencies section of your package.json.
--save-dev adds the third-party package to the package's development dependencies. It won't be installed when someone runs npm install directly to install your package. It's typically only installed if someone clones your source repository first and then runs npm install in it.
--save adds the third-party package to the package's dependencies. It will be installed together with the package whenever someone runs npm install package.
Dev dependencies are those dependencies that are only needed for developing the package. That can include test runners, compilers, packagers, etc.
Both types of dependencies are stored in the package's package.json file. --save adds to dependencies, --save-dev adds to devDependencies
npm install documentation can be referred here.
--
Please note that --save is now the default option, since NPM 5. Therefore, it is not explicitly needed anymore. It is possible to run npm install without the --save to achieve the same result.
Let me give you an example,
You are a developer of a very SERIOUS npm library which uses different testing libraries to test the package.
Users download your library and want to use it in their code. Do they need to download your testing libraries as well? Maybe you use jest for testing and they use mocha. Do you want them to install jest as well? Just To run your library?
No. right? That's why they are in devDependencies.
When someone does, npm i yourPackage only the libraries required to RUN your library will be installed. Other libraries you used to bundle your code with or testing and mocking will not be installed because you put them in devDependencies. Pretty neat right?
So, Why do the developers need to expose the devDependancies?
Let's say your package is an open-source package and 100s of people are sending pull requests to your package. Then how they will test the package? They will git clone your repo and when they would do an npm i the dependencies as well as devDependencies.
Because they are not using your package. They are developing the package further, thus, in order to test your package they need to pass the existing test cases as well write new. So, they need to use your devDependencies which contain all the testing/building/mocking libraries that YOU used.
A perfect example of this is:
$ npm install typescript --save-dev
In this case, you'd want to have Typescript (a javascript-parseable coding language) available for development, but once the app is deployed, it is no longer necessary, as all of the code has been transpiled to javascript. As such, it would make no sense to include it in the published app. Indeed, it would only take up space and increase download times.
As suggested by #andreas-hultgren in this answer and according to the npm docs:
If someone is planning on downloading and using your module in their program, then they probably don't want or need to download and build the external test or documentation framework that you use.
However, for webapp development, Yeoman (a scaffolding tool that installs a peer-reviewed, pre-written package.json file amongst other things) places all packages in devDependencies and nothing in dependencies, so it appears that the use of --save-dev is a safe bet in webapp development, at least.
--save-dev saves semver spec into "devDependencies" array in your package descriptor file, --save saves it into "dependencies" instead.
--save-dev is used for modules used in development of the application,not require while running it in production environment
--save is used to add it in package.json and it is required for running of the application.
Example: express,body-parser,lodash,helmet,mysql all these are used while running the application use --save to put in dependencies while mocha,istanbul,chai,sonarqube-scanner all are used during development ,so put those in dev-dependencies .
npm link or npm install will also install the dev-dependency modules along with dependency modules in your project folder
Read Complete And Forget --save-dev Headache
Simplest answer is that --save-dev is useful when you are creating packages for other developers and want to host your package at NPM Registry like lodash, mongoose, express etc. When you are building or writing a Node Server there is no difference between --save and --save-dev because your Node Server implementation is private to you and you will never publish it on NPM.
How NPM Install Works
Whenever we install a new package using npm like npm install express then NPM installs that package to our system and put it into node_modules folder, now NPM will analyze the package.json file of newly installed package i.e express in this case, after analyzing NPM will install all those packages which were mentioned in dependencies section of package.json file of express package. After installing those packages on which express was dependent NPM again analyze the package.json file of all newly installed packages and again install the packages for them, this cycle goes on until all packages are available into node_modules folder to function properly. You can check package dependencies by running npm list in terminal where terminal should point location of your project directory.
How --save-dev Is Related To Above Explained Stuff
Suppose you want to create a new package like express, now while development of this new package you probably want to write some unit testing code and test the package with any other available testing package let's assume mocha in this case. Now you know mocha is only required to test the package not required to use the package. In this case you should install mocha using --save-dev flag, otherwise NPM will install it whenever a developer install your package using NPM. So if we want a dependency not installed when someone install our package from NPM we must install that package using --save-dev in development phase.
Last Thing
Do not mix --save-dev with collaboration development, if someone cloned your package code from a source version control system like github then NPM will surely install all devDependencies i.e package installed using --save-dev also.
Clear answers are already provided. But it's worth mentioning how devDependencies affects installing packages:
By default, npm install will install all modules listed as dependencies in package.json . With the --production flag (or when the NODE_ENV environment variable is set to production ), npm will not install modules listed in devDependencies .
See: https://docs.npmjs.com/cli/install
When you install an npm package using npm install <package-name>, you are installing it as a dependency.
The package is automatically listed in the package.json file, under the dependencies list (as of npm 5: before you had to manually specify --save).
ex. npm install lodash
After pressing enter check your package.json file.
"dependencies": {
"lodash": "4.x",
},
When you add the -D flag, or --save-dev, you are installing it as a development dependency, which adds it to the devDependencies list.
ex. npm install --save-dev lite-server
After pressing enter check your package.json file
"devDependencies": {
"lite-server": "^2.6.1"
},
Development dependencies are intended as development-only packages, that are unneeded in production. For example testing packages, webpack, or Babel.
When you go in production, if you type npm install and the folder contains a package.json file, they are installed, as npm assumes this is a development deploy.
You need to set the --production flag (npm install --production) to avoid installing those development dependencies.
All explanations here are great, but lacking a very important thing: How do you install production dependencies only? (without the development dependencies).
We separate dependencies from devDependencies by using --save or --save-dev.
To install all we use:
npm i
To install only production packages we should use:
npm i --only=production
You generally don't want to bloat production package with things that you only intend to use for Development purposes.
Use --save-dev (or -D) option to separate packages such as Unit Test frameworks (jest, jasmine, mocha, chai, etc.)
Any other packages that your app needs for Production, should be installed using --save (or -S).
npm install --save lodash //prod dependency
npm install -S moment // " "
npm install -S opentracing // " "
npm install -D jest //dev only dependency
npm install --save-dev typescript //dev only dependency
If you open the package.json file then you will see these entries listed under two different sections:
"dependencies": {
"lodash": "4.x",
"moment": "2.x",
"opentracing": "^0.14.1"
},
"devDependencies": {
"jest": "22.x",
"typescript": "^2.8.3"
},
--save-dev (only used in the development, not in production)
--save (production dependencies)
--global or -g (used globally i.e can be used anywhere in our local system)
People use npm on production to do wicked cool stuff, Node.js is an example of this, so you don't want all your dev tools being run.
If you are using gulp (or similar) to create build files to put on your server then it doesn't really matter.
Basically We Write
npm install package_name
But specially for Testing Purpose we don't need to run some package while Application is Running in Normal State so that Node introduce good way to solve this problem. Whenever we write
npm install package_name --save-dev
at that time this package is only installed for development purpose.
I want to add some of my ideas as
I think all differences will appear when someone uses your codes instead of using by yourself
For example, you write an HTTP library called node's request
In your library,
you used lodash to handle string and object, without lodash, your codes cannot run
If someone uses your HTTP library as a part of his code. Your codes will be compiled with his.
your codes need lodash, So you need to put in dependencies to compile
If you write a project like monaco-editor, which is a web editor,
you have bundled all your codes and your product env library using webpack, when build completed, only have a monaco-min.js
So someone doesn't care whether --save or --save-dependencies, only he needs is monaco-min.js
Summary:
If someone wants to compile your codes (use as a library),
put lodash which used by your codes into dependencies
If someone want to add more feature to your codes, he needs unit test and compiler, put these into dev-dependencies
as --save is default option for npm, so I use
npm i package
and for --save-dev, I use
npm i package -D
default option will install package as project dependency where as -D is for development dependencies like testing, lint etc. and install package for development process
you can find all the flags here https://docs.npmjs.com/cli/v8/commands/npm-install

How to include options passed to npm installer when including dependencies in package.json

I am very new to node.js so please pardon my ignorance on a simple question. I am adding dependencies to package.json for a node.js application and am wondering if it's possible to specify command line arguments that would normally be passed to npm install. For example, when installing the mongodb package from the command line, you might need to pass an option:
npm install mongodb --mongodb:native
Is their a way with the package.json syntax to specify that a package should be installed with command line options?
It's not perfect, but I have been able to get around this problem by adding an explicit npm install into the preinstall script of my package.json file. In this way, the mongodb package is added as a binary before npm gets a chance to do it incorrectly. Hope this helps
"scripts": {
"preinstall" : "npm install mongodb '--mongodb:native'"
}

Resources