Let's say I've already published a nodejs module on npm. It's simple, you install and import it, and given a string and a config object it returns a string.
Now, I wanted to make it available both as local and global module: I added a script to bin in package.json. The script imports and uses the local function and prints the result. To handle the argument and the config object I used npm yarg as a dependency.
The problem is this: if I want to install my module as local, the yarg dependency is not needed, because it is used only when the module is used as global. So it's a waste of space.
Is there a way to make the yarg dependency be installed only when my module is installed as global module and not local?
This is not a common practice by any means. There can be scenarios when a package installed locally can be usable as CLI executable, notably as a part of NPM script. All popular packages that are supposed to be used globally like npm or grunt can also be installed and run locally.
There is no reliable way to detect that a package was installed globally. This can be potentially done by detecting that a module resides in node_modules global location. There may be caveats that depend on specific system.
The footprint of yargs is 760 Kb, which is not crucial. If the footprint of CLI-specific functionality is considerable, CLI executable can be extracted to separate package that depends on original package.
Related
I'm developing a web app which uses a private package.
In some circumstances, I rather to use the local version of the package or different version of that package.
Is there any solution to indicate to use different version of package in package.json?
i.e:
npm install --local
while my package.json looks like:
...
"dependencies": {
...
"my_package": if(local) "address_to_local_package/" else "5.6.1"
...
}
npm does not accommodate this (and honestly, probably should not). This seems like the type of thing that is usually handled at runtime via the NODE_ENV environment variable or similar mechanism. The module changes behavior depending on whether NODE_ENV is set to "production" or "development". (But that's just convention. You can use values like "local" if you want.) So rather than installing different versions of the module, there's a single version that behaves differently based on the value of that environment variable.
If you really want different code bases installed entirely, it will take some effort but you can write a postinstall script for npm to run. Your module then becomes nothing more than a script and then the postinstall figures out what to actually install based on environment variables or maybe a command line flag. This seems brittle to me, though. I'd think hard about if you're solving the right problem here if you go this route. NODE_ENV seems more elegant and conventional.
I want to do something like this, where, I want to keep all my packages globally just like node package itself. So for example in my package.json I have a package name called "Highcharts" I want to install it globally I don't want to create a local node_modules folder and use it but I want to access it from outside so next time whenever I want to create a copy of my project folder I should be able to use highcharts directly without using npm install. Is it possible?
globally installed node_modules - > Users/user/AppData/Roaming/node_modules/highcharts
app
src
node_modules (I don't want to keep it)
package.json
tsconfig.json
angular.json
How to link these globally installed node_modules with the current app or any app which we want to create?
Any help will be appreciated. Thank you so much :)
local packages are installed in the project directory
global packages are installed in a single place in your system
Usually it is a good idea to have all npm packages required for your project installed locally (project folder). This makes sure, that you can have dozens of applications which are running a different versions of each package if needed.
export NODE_PATH='yourdir'/node_modules
Hello, if am getting right, you want to keep all dependencies global.
You can just run install with -g command. Those libraries will be available in node installation folder.
From the Node docs
If the NODE_PATH environment variable is set to a colon-delimited list of absolute paths, then node will search those paths for modules if they are not found elsewhere. (Note: On Windows, NODE_PATH is delimited by semicolons instead of colons.)
Additionally, node will search in the following locations:
1: $HOME/.node_modules
2: $HOME/.node_libraries
3: $PREFIX/lib/node
Where $HOME is the user's home directory, and $PREFIX is node's configured node_prefix.
These are mostly for historic reasons. You are highly encouraged to place your dependencies locally in node_modules folders. They will be loaded faster, and more reliably.
I hope I answered, you just need to manage the paths to node_modules wherever you have kept it.
Lets say we're developing a small javascript library L.
The code is in ES6. To use some utility function, like debounce, we install lodash as a dependency.
At build the webpack transpiles the code, bundling the tree shaked lodash code, and we end up with a nice little javascript file we want to publish and share as a npm package.
Now, the package.json file lists lodash as a dependency. But that is only true at build time, it is not really needed in production.
What's the proper way to handle this kind of situation?
Does it make sense to consider lodash a devDependency? As such, only webpack's externals would be "real" dependencies?
Or should we somehow tamper the package.json file before publishing it?
Do you know any real examples of projects handling this question?
Webpack "merges" the code of the project with that of the used code of the non-external dependencies into some bundle.js file. This file is then published to NPM, along with a package.json file, which lists all of the dependencies, independently of these being external or embedded.
All of the code of packages referenced in dependencies (or optionalDependencies, or peerDependencies) is expected to be "production" code.
While code in devDependencies is expected to be used only at "development" time, and is thus "development" code. Under this principle, I believe that it would be incorrect to declare non-external dependencies as development dependencies.
However, if all dependencies, embedded or external, are declared equally in the published package.json file, there is no way for a runtime environment to know which are the real dependencies that need to be made available to the package — the ones that the package will import at runtime and which better be available.
For the Node.js environment, bundles and Webpack are not normally used, so this never came to be an issue — all dependencies are always installed (never merged/bundled).
However, if you use the package.json file to drive some web-packages runtime environment, the way that dependencies are currently included in published package.json is not suitable.
You might want to take a look at Pika Web's devDependencies package.json property, which aims to solve a comparable problem (even though their mojo is "a future without Webpack"). They also introduce the concept of publishing a different package.json file than that which is checked-in (tampering the package.json before publishing, as you say).
Interestingly, it looks like a related tool, Pika Pack, caught the attention of NPM folks and is being considered to become part of NPM. So, maybe, NPM has become aware of the specific package format needs of web projects' workflows.
I installed globally eslint#4.2.0 and wanted to use some predefined configs. When I tried to install eslint-config-google it said that I don't have eslint>=4.1.0 (which I had of course, but installed globaly). The same problem occured when installing eslint-config-airbnb-base - ultimately predefined configs can't see globally installed eslint (and even eslint --init can't see it cause it installed another instance of eslint locally when I run it). Then I decided to install both eslint and configuration files locally, in my projects directory - works like a charm, but still I'd like to have these things in global scope so I wouldn't have to care about it each time I make a new directory for my projects.
Is it possible to make eslint and predifined configs work at global scope or at least have global eslint and local configuration file?
ultimately predefined configs can't see globally installed eslint
Actually, they can. Or rather, global ESLint can use global configs. It just doesn't know how to cross the barrier between global and local.
Then I decided to install both eslint and configuration files locally, in my projects directory - works like a charm
Good choice. This is going to be crucial when you eventually have projects with conflicting needs.
but still I'd like to have these things in global scope so I wouldn't have to care about it each time I make a new directory for my projects
That is what project scaffolding tools like Yeoman are for. Here is the one I use, generator-seth.
Whenever I start a new project, I simply type seth and answer a couple of questions and it's done with glorious magic under the hood.
Is it possible to make eslint and predifined configs work at global scope or at least have global eslint and local configuration file?
Yepp. You can have a local .eslintrc file and the global ESLint will respect it. You can run eslint --init to set this up (that will also install ESLint locally, but you don't need to use it).
But an even better way is to use XO, which is built on top of ESLint (and supports all ESLint rules). Unlike ESLint, XO knows how to automatically choose the appropriate installation to use. You can install it both locally and globally and its global CLI will defer to the local copy when one is detected. This is important because it means that each project can keep its own version of the linter while still allowing you to run XO globally. So if a breaking change comes out, your projects are not screwed all at once or broken when they have conflicting needs. The configuration for XO is kept locally either way, so unlike ESLint you do not have duplicate local and global configs.
I have recently encountered the same issue, here is my solution (OS: Windows):
First, I run eslint initial command to configure my preference:
$ eslint --init
? How would you like to use ESLint? To check syntax, find problems, and enforce code style
? What type of modules does your project use? CommonJS (require/exports)
? Which framework does your project use? None of these
? Where does your code run? Browser, Node
? How would you like to define a style for your project? Use a popular style guide
? Which style guide do you want to follow? Airbnb (https://github.com/airbnb/javascript)
? What format do you want your config file to be in? JavaScript
Checking peerDependencies of eslint-config-airbnb-base#latest
The config that you've selected requires the following dependencies:
eslint-config-airbnb-base#latest eslint#^4.19.1 || ^5.3.0 eslint-plugin-import#^2.17.2
? Would you like to install them now with npm? No
Second, I copy the ".eslintrc.js" file that eslint generated locally into my user home directory (C:\Users\username) to make it a global configuration file.
Finally, I use npm to install the required packages globally:
npm install -g eslint#^5.16.0 eslint-config-airbnb-base eslint-plugin-import
*Important*
Don't use npm install -g eslint because it will install the latest eslint version
(like 6.0.1) and therefore, the dependencies of eslint-config-airbnb-base and eslint-plugin-import will go wrong
And here is the article you may find more information if you want configure differently How to globally set up eslint in vscode.
2 manuals about gulp say that I need to install gulp first globally (with -g flag) and then one more time locally. Why do I need this?
When installing a tool globally it's to be used by a user as a command line utility anywhere, including outside of node projects. Global installs for a node project are bad because they make deployment more difficult.
npm 5.2+
The npx utility bundled with npm 5.2 solves this problem. With it you can invoke locally installed utilities like globally installed utilities (but you must begin the command with npx). For example, if you want to invoke a locally installed eslint, you can do:
npx eslint .
npm < 5.2
When used in a script field of your package.json, npm searches node_modules for the tool as well as globally installed modules, so the local install is sufficient.
So, if you are happy with (in your package.json):
"devDependencies": {
"gulp": "3.5.2"
}
"scripts": {
"test": "gulp test"
}
etc. and running with npm run test then you shouldn't need the global install at all.
Both methods are useful for getting people set up with your project since sudo isn't needed. It also means that gulp will be updated when the version is bumped in the package.json, so everyone will be using the same version of gulp when developing with your project.
Addendum:
It appears that gulp has some unusual behaviour when used globally. When used as a global install, gulp looks for a locally installed gulp to pass control to. Therefore a gulp global install requires a gulp local install to work. The answer above still stands though. Local installs are always preferable to global installs.
TLDR; Here's why:
The reason this works is because gulp tries to run your gulpfile.js using your locally installed version of gulp, see here. Hence the reason for a global and local install of gulp.
Essentially, when you install gulp locally the script isn't in your PATH and so you can't just type gulp and expect the shell to find the command. By installing it globally the gulp script gets into your PATH because the global node/bin/ directory is most likely on your path.
To respect your local dependencies though, gulp will use your locally installed version of itself to run the gulpfile.js.
You can link the globally installed gulp locally with
npm link gulp
The question "Why do we need to install gulp globally and locally?" can be broken down into the following two questions:
Why do I need to install gulp locally if I've already installed it globally?
Why do I need to install gulp globally if I've already installed it locally?
Several others have provided excellent answers to theses questions in isolation, but I thought it would be beneficial to consolidate the information in a unified answer.
Why do I need to install gulp locally if I've already installed it globally?
The rationale for installing gulp locally is comprised of several reasons:
Including the dependencies of your project locally ensures the version of gulp (or other dependencies) used is the originally intended version.
Node doesn't consider global modules by default when using require() (which you need to include gulp within your script). Ultimately, this is because the path to the global modules isn't added to NODE_PATH by default.
According to the Node development team, local modules load faster. I can't say why this is, but this would seem to be more relevant to node's use in production (i.e. run-time dependencies) than in development (i.e. dev dependencies). I suppose this is a legitimate reason as some may care about whatever minor speed advantage is gained loading local vs. global modules, but feel free to raise your eyebrow at this reason.
Why do I need to install gulp globally if I've already installed it locally?
The rationale for installing gulp globally is really just the convenience of having the gulp executable automatically found within your system path.
To avoid installing locally you can use npm link [package], but the link command as well as the install --global command doesn't seem to support the --save-dev option which means there doesn't appear to be an easy way to install gulp globally and then easily add whatever version that is to your local package.json file.
Ultimately, I believe it makes more sense to have the option of using global modules to avoid having to duplicate the installation of common tools across all your projects, especially in the case of development tools such as grunt, gulp, jshint, etc. Unfortunately it seems you end up fighting the tools a bit when you go against the grain.
Technically you don't need to install it globally if the node_modules folder in your local installation is in your PATH. Generally this isn't a good idea.
Alternatively if npm test references gulp then you can just type npm test and it'll run the local gulp.
I've never installed gulp globally -- I think it's bad form.
I'm not sure if our problem was directly related with installing gulp only locally. But we had to install a bunch of dependencies ourself. This lead to a "huge" package.json and we are not sure if it is really a great idea to install gulp only locally. We had to do so because of our build environment. But I wouldn't recommend installing gulp not globally if it isn't absolutely necessary. We faced similar problems as described in the following blog-post
None of these problems arise for any of our developers on their local machines because they all installed gulp globally. On the build system we had the described problems. If someone is interested I could dive deeper into this issue. But right now I just wanted to mention that it isn't an easy path to install gulp only locally.
Just because I haven't seen it here, if you are on MacOS or Linux, I suggest you add this to your PATH (in your bashrc etc):
node_modules/.bin
With this relative path entry, if you are sitting in the root folder of any node project, you can run any command line tool (eslint, gulp, etc. etc.) without worrying about "global installs" or npm run etc.
Once I did this, I've never installed a module globally.