Local dependency in package.json - node.js

I want to do something like this, so npm install also installs the package.json of ../somelocallib or more importantly its dependencies.
"dependencies": {
"express": "*",
"../somelocallib": "*"
}

npm >= 2.0.0
This feature was implemented in the version 2.0.0 of npm. Local paths can be saved using npm install -S or npm install --save, using any of these forms:
../foo/bar
~/foo/bar
./foo/bar
/foo/bar
Example package.json:
{
"name": "baz",
"dependencies": {
"bar": "file:../foo/bar"
}
}
npm ls:
app#0.0.1 /private/tmp/app
└── somelocallib#0.0.1 -> /private/tmp/somelocallib
npm < 2.0.0
Put somelocallib as dependency in your package.json as normal:
"dependencies": {
"somelocallib": "0.0.x"
}
Then run npm link ../somelocallib and npm will install the version you're working on as a symlink.
Reference: link(1)

It is now possible to specify local Node module installation paths in your package.json directly. From the docs:
Local Paths
As of version 2.0.0 you can provide a path to a local directory that contains a package. Local paths can be saved using npm install -S or npm install --save, using any of these forms:
../foo/bar
~/foo/bar
./foo/bar
/foo/bar
in which case they will be normalized to a relative path and added to your package.json. For example:
{
"name": "baz",
"dependencies": {
"bar": "file:../foo/bar"
}
}
This feature is helpful for local offline development and creating tests that require npm installing where you don't want to hit an external server, but should not be used when publishing packages to the public registry.

This works for me.
Place the following in your package.json file
"scripts": {
"preinstall": "npm install ../my-own-module/"
}

This is how you will add local dependencies:
npm install file:src/assets/js/FILE_NAME
Add it to package.json from NPM:
npm install --save file:src/assets/js/FILE_NAME
Directly add to package.json like this:
....
"angular2-autosize": "1.0.1",
"angular2-text-mask": "8.0.2",
"animate.css": "3.5.2",
"LIBRARY_NAME": "file:src/assets/js/FILE_NAME"
....

If you want to further automate this, because you are checking your module into version control, and don't want to rely upon devs remembering to npm link, you can add this to your package.json "scripts" section:
"scripts": {
"postinstall": "npm link ../somelocallib",
"postupdate": "npm link ../somelocallib"
}
This feels beyond hacky, but it seems to "work". Got the tip from this npm issue:
https://github.com/npm/npm/issues/1558#issuecomment-12444454

Master project
Here is the package.json you will use for the master project:
"dependencies": {
"express": "*",
"somelocallib": "file:./somelocallib"
}
There, ./somelocallib is the reference to the library folder as relative to the master project package.json.
Reference: https://docs.npmjs.com/cli/v7/configuring-npm/package-json#local-paths
Sub project
Handle your library dependencies.
In addition to running npm install, you will need to run (cd node_modules/somelocallib && npm install).
This is a known bug with NPM.
Reference: https://github.com/npm/npm/issues/1341 (seeking a more up-to-date reference)
Notes for Docker
Check in your master package.lock and your somelocallib/package.lock into your source code manager.
Then in your Dockerfile use:
FROM node:10
WORKDIR /app
# ...
COPY ./package.json ./package-lock.json ./
COPY somelocallib somelocallib
RUN npm ci
RUN (cd node_modules/zkp-utils/ && npm ci)
# ...
I use parenthesis in my (cd A && B) constructs to make the operation idempotent.

Two steps for a complete local development:
Provide the path to the local directory that contains the package.
{
"name": "baz",
"dependencies": {
"bar": "file:../foo/bar"
}
}
Symlink the package folder
cd ~/projects/node-redis # go into the package directory
npm link # creates global link
cd ~/projects/node-bloggy # go into some other package directory.
npm link redis # link-install the package

Here in 2020, working on a Windows 10, I tried with
"dependencies": {
"some-local-lib": "file:../../folderY/some-local-lib"
...
}
Then doing a npm install. The result is that a shortcut to the folder is created in node-modules.
This doesn't work. You need a hard link - which windows support, but
you have to do something extra in windows to create a hard symlink.
Since I don't really want a hard link, I tried using an url instead:
"dependencies": {
"some-local-lib": "file:///D:\\folderX\\folderY\\some-local-lib.tar"
....
}
And this works nicely.
The tar (you have to tar the stuff in the library's build / dist folder) gets extracted to a real folder in node-modules, and you can import like everything else.
Obviously the tar part is a bit annoying, but since 'some-local-lib' is a library (which has to be build anyway), I prefer this solution to creating a hard link or installing a local npm.

I know that npm install ../somelocallib works.
However, I don't know whether or not the syntax you show in the question will work from package.json...
Unfortunately, doc seems to only mention URL as a dependency.
Try file:///.../...tar.gz, pointing to a zipped local lib... and tell us if it works.

With yarn it can be done as
yarn add file:../somelocallib

Curious.....at least on Windows (my npm is 3.something) I needed to do:
"dependencies": {
"body-parser": "^1.17.1",
"module1": "../module1",
"module2": "../module2",
When I did an npm install ../module1 --save it resulted in absolute paths and not relative per the documentation.
I messed around a little more and determined that ../xxx was sufficient.
Specifically, I have the local node modules checked out to say d:\build\module1, d:\build\module2 and my node project (application) in d:\build\nodeApp.
To 'install', I:
d:\build\module1> rmdir "./node_modules" /q /s && npm install
d:\build\module2> rmdir "./node_modules" /q /s && npm install
d:\build\nodeApp> rmdir "./node_modules" /q /s && npm install
module1's package.json has a dependency of "module2": "../module2"; module2 has no local dependency; nodeApp has dependencies "module1": "../module1" and "module2": "../module2".
Not sure if this only works for me since all 3 folders (module1, module2 and nodeApp) sit on that same level.......

This worked for me: first, make sure the npm directories have the right user
sudo chown -R myuser ~/.npm
sudo chown -R myuser /usr/local/lib/node_modules
Then your in your package.json link the directory
"scripts": {
"preinstall": "npm ln mylib ../../path/to/mylib"
},
"dependencies": {
"mylib" : "*"
}

Actually, as of npm 2.0, there is support now local paths (see here).

There is great yalc that helps to manage local packages. It helped me with local lib that I later deploy. Just pack project with .yalc directory (with or without /node_modules). So just do:
npm install -g yalc
in directory lib/$ yalc publish
in project:
project/$ yalc add lib
project/$ npm install
that's it.
When You want to update stuff:
lib/$ yalc push //this will updated all projects that use your "lib"
project/$ npm install
Pack and deploy with Docker
tar -czvf <compresedFile> <directories and files...>
tar -czvf app.tar .yalc/ build/ src/ package.json package-lock.json
Note: Remember to add .yalc directory.
inDocker:
FROM node:lts-alpine3.9
ADD app.tar /app
WORKDIR /app
RUN npm install
CMD [ "node", "src/index.js" ]

I wanted to use a set of local dependencies written in TypeScript, and none of the answers here worked for me. npm install would simply refuse to build the dependencies.
I had to resort to using tsconfig.json to add the packages to my project without marking them as dependencies. My usecase is further complicated by the fact that some dependencies depend on each other, and I wanted all of them to come from the local folder.
Here is my solution:
// tsconfig.json
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"#tiptap/*": [
"tiptap/packages/*/src"
]
}
}
}
In the example above, I have a local project subfolder named tiptap/ and there are many packages in tiptap/packages/*. The "paths" option will rewrite all #tiptap/foo imports into ./tiptap/packages/foo/src, across both my own files and the files in tiptap/.
It's not a good solution, but it is the only thing that worked for me.

use workspaces
the disadvantage using the file:../path/to/your-library is that you either have to npm install or using npm link in order to to the changes to take effect in the packages that import your package.
if you using pnpm: a better solution is using workspace: protocol: workspace:../path/to/your-library. it will symlink the directory to your node_modules rather than copying it, so any changes at the source immediately take effect.
for example:
...
"dependencies": {
...
"my-package": "workspace:../../dist"
},
note: this solution is intended to be used in a workspace, so you may need to create pnpm-workspace.yaml (even an empty one) file in the root of your project.

In 2021 you need to use it like:
npm i my-pkg#file:./path-to-my-pkg.js
# To remove it later
npm un my-pkg
Use .js in the end if its file OR path to folder if its complete package with package.json.
Usage
const myPkg = require('my-pkg')
That works like charm!

Complete local development guide for yarn users:
First add dependency to your main project:
cd main-project
yarn add file:../path/to/your-library
Next, if you want to avoid re-building this dependency every time you change it's source:
cd your-library
yarn link
This will register a link to your-library. Next, use the link you just created in your main project.
cd main-project
yarn link your-library
Now every time you change code in your-library, you don't need to rebuild it and it will automatically be included in your main-project. Yarn link works by creating symlinks in your node_modules folder, read more about it here: https://classic.yarnpkg.com/lang/en/docs/cli/link/

Using Module Alias
Install the module-alias package:
npm i --save module-alias
Add paths to your package.json like this:
{ "_moduleAliases": { "#lib": "app/lib", "#models": "app/models" } }
In your entry-point file, before any require() calls:
require('module-alias/register')
You can now require files like this:
const Article = require('#models/article');

Related

NPM 7 workspaces - how to install new package in workspace?

If I have a NPM 7 workspace like this:
root
- submodule0
- submodule1
- submodule2
and I navigate to the submodule0 directory and run npm i somepackage it seems to "break" the workspace by creating a new package-lock.json in the submodule0 directory and installing all the dependencies there. In other words, it just does the old behavior that existed before I created the workspace.
I was hoping for a command similar to lerna where I can install a new package in submodule0 from the root. Something like:
npm i somepackage --scope submodule0
So far, the only workaround I can find is to edit the submodule0 package.json and add the somepackage manually. Then run npm i from the root. Obviously this is not ideal because I need to look up the #latest version, navigate to the subdirectory, open the package.json, etc. etc. as opposed to just typing one line in the root.
Workspace support for npm install and npm uninstall was added in npm v7.14.0. You can now just do:
npm i somepackage --workspace=submodule0
Uninstalling modules has been the biggest pain, so this is really exciting. The npm team seems to be slowly adding support to commands one by one. Follow updates here: https://github.com/npm/cli/blob/latest/CHANGELOG.md.
I'm also baffled with why npm workspaces has been released without this functionality.
My current workaround uses the add-dependencies package, which adds dependencies to a declared package.json file, whilst skipping the installation process.
npm i add-dependencies -g
Then, from top level of the monorepo, you can run:
npx add-dependencies ./submodule0/package.json somepackage && npm i
Hopefully a --workspace argument will be added to npm i soon to avoid this faff.
Please refer to the answer of mattwad above if you have NPM v7.14.0 or above
Original answer
I wasn't quite happy with the suggestions, but combined all of them to use it in a npm script without any dependencies:
{
"add": "npm install --package-lock-only --no-package-lock --prefix",
"postadd": "npm install"
}
This can be used like following: npm run add -- submodule0 somepackage
Add only into package.json
U can use this to install package only into package.json ( you don't need external dependencies )
npm i --prefix packages/test --save --package-lock-only --no-package-lock express
followed by npm i to install specified dependency into mono repository root node_modules
Lerna
Also can use lerna to use workspace name to install dependency into
package.json
{
"name": "mono",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"wsi": "function workspaceinstall() { ( scope=$1; shift; lerna exec --scope \"$scope\" -- npm install --package-lock-only --no-package-lock \"$#\") }; workspaceinstall"
},
"author": "",
"license": "ISC",
"workspaces": {
"packages": [
"packages/**"
]
}
}
lerna.json
{
"version": "1.0.0",
"npmClient": "npm",
"packages": ["packages/**"]
}
npm run wsi [workspace name] [dependency name to install]
npm run wsi #workspace/test express
npm run wsi #workspace/test express --save-prod
npm run wsi #workspace/test #types/express --save-dev
wsi script only modify package.json for provided workspace name, to actually install dependencies u have to run npm i
In my case, which is similar to yours, I deleted all dependencies from all the inner projects, deleted also the package-lock.json, and installed everything in the root.
/
node_modules
package.json >> all dependencies
package-lock.json >> the only lock file that exists in the repo
/packages
/A
package.json >> no dependencies
-- no package-lock.json
/B
package.json >> no dependencies
-- no package-lock.json
/C
package.json >> no dependencies
-- no package-lock.json
This way, the node_modules folder ONLY resides on the root, and also the package-lock.json file is in the root.
If I allowed to have each project it's own package-lock.json I started seeing installation and runtime errors (because each project could have its own node_modules and its own version of a dependency).
This is the best way I see it works.
After trying to use the npm install with the --prefix --save --package-lock-only --no-package-lock options, npm always give the the error E404 - Not Found for my own packages of the monorepo that are not yet published to a registry. So even when trying to install external packages it fails because of my current dependencies in the package.json.
To workaround this issue I ended up with a mix of the previous suggestions:
"scripts": {
"add": "add-dependencies $npm_config_scope/package.json",
"postadd": "npm i",
},
"devDependencies": {
"add-dependencies": "^1.1.0"
},
Then I can do:
npm run add --scope=packages/app express
npm run add --scope=packages/core eslint jest -D
This works fine for installing external packages. To install my own packages that lives inside the monorepo, I still have to manually edit the package.json, otherwise I get the package not found error.

Npm install from repo not running `prepare`

I have an npm package for common components hosted on an internal git server. For some reason when I call npm install in another project I want to consume this in it will not run the prepare hook. Obviously, this does not work since the npm package needs a /dist folder in node_modules to be able to consume the package.
I have already tried things such as using the deprecated prepublish hook and even that does not get called. I also tried to do postinstall to see if I could build after install, while that hook did get called it failed because the devDependencies were not installed
package.json
{
"name": "common-components",
"version": "0.1.0",
"scripts": {
"prepare": "npm run build",
"build": "ng build",
...
},
"private": true,
"dependencies": {
...
},
"devDependencies": {
...
},
}
command being used for install
npm install --save git+ssh://{URL-to-common-components-repo}}
I have read through the npm-scripts documentation https://docs.npmjs.com/misc/scripts thoroughly and it seems like they insist that prepare hook should always be called for this exact use-case
Updated 5/6/2019
Just as a note I found this bug on NPM community https://npm.community/t/using-npm-ci-does-not-run-prepare-script-for-git-modules/632/4.
I am using npm 6.4.1 which should work according to the bug
One thing to check that hit me on a package recently - if there is a .gitignore and not .npmignore npm may be ignoring your /dist folder. Adding an empty .npmignore worked in this case.
"If there’s no .npmignore file, but there is a .gitignore file, then
npm will ignore the stuff matched by the .gitignore file. If you want
to include something that is excluded by your .gitignore file, you can
create an empty .npmignore file to override it."
from https://docs.npmjs.com/misc/developers
For those that are wondering the status of this. I was unable to ever get it to work. What I ended up doing was hosting the components on a private npm registry and that works fine since the npm publish command will do the build and only publish the dist folder
If adding an empty .npmignore does not help, you can try specifying all files in dist explicitly in package.json#files. If this works you might want to consider using a wildcard pattern that matches the files in dist to simplify the package.json.
package.json
...
"files": [
"source",
"dist/cjs/main.js",
"dist/es/main.js"
]
}
see this comment to a similar issue in the npm/cli repository https://github.com/npm/cli/issues/1287#issuecomment-635021757
It's very likely that your dist/ folder is in your .gitignore file. According to this response from an npm-cli maintainer:
In order to be able to properly prepare a git repo npm will run the
extracted files through npm-packlist in order to get the expected
files that are going to be placed in your node_modules folder.
Further checking the documentation of npm-packlist, we find that npm-packlist will respect the .gitignore file if it has nothing else to go off of:
If there's no package.json with a files list, and there's no
.npmignore file, but there is a .gitignore file, then ignore all the
files in the .gitignore file.
This article further expands on the idea.
It seems to me that the best fix is to explicitly declare the files that your package needs (including dist/) in the files section of your package.json file. Then you have complete control over what's included and the package size is minimized.
If you are using root user to npm install the package then the prepare script might not be triggered. The reason was the prepare child process has no permission to run (user account was set to default of 'nobody' when using npm with root). You can read more here: https://github.com/npm/npm/issues/17346
To fix this, in the lib package, create an .npmrc file and add:
unsafe-perm: true
Adding main in the package.json fixed this issue for me.
"main": "./dist/index.js",
"scripts": {
"build": "babel src --out-dir dist",
"prepare": "npm run build",
"lint": "eslint ."
},
node v14.15.4 npm 6.14.11

node local dependency installs as shortcut and nested instead of flat

It seems this started when I updated node/npm but I didn't realized until now when I had to delete and re create my node_modules folder.
I have a React Native project which has the core module and one Examples project to showcase the module. The examples project references the module like this in my package.json:
"dependencies": {
"module-core": "file:../core"
},
When I run npm install in the Examples project I was getting this nodule_module structure:
node_modules
core
core_dependency_1
core_dependency_2
Now, I get this:
node_modules
core
node_modules
core_dependency_1
core_dependency_2
At first I thought it had to do with peerDepencies and how npm handled flat/nested dependencies but I have checked and it seems now the core folder is now a shortcut (I am using Windows).
This is breaking my gradle scripts because some are referenced like this:
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
I could fix this by renaming the links but that would make the build platform/environment dependent.
Also it breaks some other scripts like this one:
apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle"
That is because the project() resolves to my root folder and now I cannot use that either.
I am using npm 5.4.2 and node 8.8.1. Previously I had node 7.4.0.
Is there any flag or way to make npm install the localDependency and not treat it as a shortcut?
I finally found the answer. Npm Version 5 indeed changed the way the local dependencies are handled and it just makes npm link, which creates symbolic links or shortcuts in windows.
You can accomplish the same behavior as before with this:
npm install $(npm pack <folder> | tail -1)
Working for me in Windows 10 with git-bash
My final solution was having this package.json in the Example project that used the core:
{
"name": "core-examples",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"preinstall": "npm pack ../Core | tail -1",
},
"dependencies": {
"core": "file:core-0.0.0.tgz"
},
"jest": {
"preset": "react-native"
}
}
The preinstall script will generate the tgz file and then you can install as usual. This will avoid having to commit the tgz file to the repository.

How to "build" contents of a node application to a folder called "dist"

I have a node.js+express application. To deploy it to my server the partner is asking me to "build" the app into a folder called "dist" where all the files that need to be deployed to the server will exist. How can I implement such kind of a build.
Any hint or guidance would be appreciated.
You could create a script which does this in your package.json. You simply need to create the directory and copy everything required for running your application in production to it and no more.
//package.json
{
//...
"scripts": {
"dist": "mkdir -p dist && cp -R node_modules src server.js ... dist"
}
//...
}
Not the above is not cross-platform compatible. This is always the complex part of such build scripts. If this is an issue for you, I'd recommend looking at using available tooling such as gulp.
You can also use a NPM lifecycle hook to do this automatically as part of your install. Ensure you also run npm install --production rather than npm install to omit your dev dependencies.

How to save the installation of global package using package.json?

How to save the installation of global package using package.json?
I couldn't find any good solutions but I did this like below:
In package.json I added the following:
"scripts": {
"preinstall": "npm install babel babel-cli -g"
},
It would run and install the above packages globally before installing all the dependencies and devDependencies in package.json.
But the problem would be that I would not be able to find out, are those packages installed globally in any of the machines.
Please help, if anyone have any better solution of this.
It's a bad practice to force global install of a module. You can put babel and babel-cli in your devDependencies and then use them in your npm scripts :
{
"build" : "babel src -d build"
},
"devDependencies": {
"babel-cli": "^6.18.0"
}
If you are looking to run your package via CLI, you need to set up using bin approach.
Here's one of my libraries for example, fol. It is meant to be ran via command line only.
At the most minimal setup, you would wire both main and bin to the same JS file, for example, adding the following in package.json:
"main": "./bin/fol.js",
"bin": {
"fol": "./bin/fol.js"
},
Then, put the files into bin folder for the same of consistency, everybody will recognise that's CLI app stuff if it's in /bin/.

Resources