The structure of my project is as follows:
packages/
client/
package.json
server/
package.json
shared/
package.json
lerna.json
package.json
The package.json file of client contains the following dependencies:
{
"name": "#my-project/client",
"dependencies": {
// ...
"#my-project/shared": "*",
"cool-components": "^1.0.0',
// ...
}
}
#my-project/shared is the package name of the packages/shared directory.
I am willing to use npm link to develop the cool-components library, and to use npm link to use its code within #my-project/client.
Currently, I tried to run:
cd packages/client
npm link cool-components
And for unlinking:
npm unlink cool-components
But, I get an error claiming that #my-project/shared is not in the NPM registry.
This is absolutely understandable; When I run NPM commands in the scope of #my-project/client, it is not familiar with its sibling packages, and it tries to look for this package in the registry. This is the reason we should use lerna add in the root instead of npm install within each package.
Even though I understand the cause, I couldn't find a proper way to use npm link to develop another package while using it within my Lerna monorepo.
You add #my-project/shared dependency to #my-project/client with:
// this creates link between packages
lerna add #my-project/shared --scope=#my-project/client
Now if you check node modules inside #my-project/client you should see
#my-project/shared.
If you want to access a dependency in your node package, you use
require.resolve:
// require.resolve will get you the absolute path
const packagePath = require.resolve("#my-project/build/index.js");
// to use the this package, for exampe in express.static
// path.dirname will give you path until build directory
// so you are going to serve up that folder
app.use(express.static(path.dirname(packagePath)));
in client side, importing module from node modules path should work without any configuration. If it does not, try to resolve the node modules path to absolute path.
Assume you use a library like vue3-datepicker. You realize you need to customize something, and as as first step you want to use a custom fork of it.
The issue is, there is a build step when the package is pushed to npm's registry since the project doesn't use plain JavaScript, but may have vue or typescript files.
In this case, that would be npm run build:component, though that depends on the project.
Just installing the fork from github via:
yarn add <GitHub user name>/<GitHub repository name>#<branch/commit/tag>
hence doesn't suffice as then the ./dist folder doesn't exist.
You'll get really strange errors like:
error: [plugin: vite:dep-scan] Failed to resolve entry for package "vue3-datepicker". The package may have incorrect main/module/exports specified in its package.json: Failed to resolve entry for package "vue3-datepicker". The package may have incorrect main/module/exports specified in its package.json.
As a quick and dirty solution, I removed in my fork the ./dist/ folder from the .gitignore, ran the npm i && npm run build:component in my fork, and pushed it.
Huge downside is, the ./dist/ folder is now part of that repository, after each change in my fork I also have to build the files again and push those as well.
I rather have the build process triggered in my application using my fork. Is there a way from my application to say:
When you install that library, you have to run a certain script once you downloaded all the files?
The solution should be usable for both npm and yarn, in the sense that the fork my be installed by either one in different applications.
A quote from npm-install Docs
If the package being installed contains a prepare script, its dependencies and devDependencies will be installed, and the prepare script will be run, before the package is packaged and installed.
so in your fork's package.json you can add
"scripts": {
// ...
"build:component": "rollup -c build/rollup.config.js",
"prepare": "yarn build:component || npm run build:component"
}
If you want to trigger builds after installation, you can use the postinstall or a build script in your package.json. In this script, you can create directories and do other setups, using shell commands or javascript programs:
{
"scripts": {
"build": "mkdir dist && npm run build:component",
"build:component": "some command"
}
}
I have a bash script which I wrote for publishing modules to npm: publish.sh
As I still work on tweaking this script a lot, every time I change it, I need to make the changes in every copy of it in every npm module I am managing.
Is there a way to include this as a dependency in my package.json file so that I just need to run npm update; npm install in order to update it? Maybe the sh file would need to be executed by some wrapper javascript or something like that..?
"scripts": {
"start": "sh ./scripts/publish.sh",
...
}
If you need access to the script in a single project, you can put it in the "scripts" object in package.json.
"scripts": {
"publish": "/bin/sh publish.sh"
}
The above can be run with npm run publish. See npm run-script docs for more information.
If you literally want to add the shell script as a dependency that can be installed (from the public registry or a private registry) with npm install, you can absolutely do that. Lots of not-JavaScript executable things are available in the npm repository. You'll want to create a package.json for the dependency and specify the location of your shell script with the "bin" entry.
An example package you can look at if you get stuck is notes.sh. The source code is on GitHub. Look at the package.json to see how they specified the scripts to run in the "bin" entry. You'll still need to create a "script" entry in your other project to run the installed shell script (or you can run it as an npm hook) though, unless you're running it manually or spawning a child process in your code or something.
When I add this line to my package.json:
"react": "git://github.com/facebook/react.git#08e4420019f74b7c93e64f59c443970359102530"
...and then run npm install, I find node_modules/react-tools installed when I expect to see node_modules/react.
What am I doing wrong here?
The code at git://github.com/facebook/react.git is not the same code that gets installed when you npm install react. Instead, the code contains a series of build steps that are used to build the npm package. As far as I know, there is not a way to easily use a specific SHA of the React repo as an npm package; you would need to clone the repo, build the project, and copy it somewhere you can require it.
I have a downloaded module repo, I want to install it locally, not globally in another directory?
What is an easy way to do this?
you just provide one <folder> argument to npm install, argument should point toward the local folder instead of the package name:
npm install /path
From the npm-link documentation:
In the local module directory:
$ cd ./package-dir
$ npm link
In the directory of the project to use the module:
$ cd ./project-dir
$ npm link package-name
Or in one go using relative paths:
$ cd ./project-dir
$ npm link ../package-dir
This is equivalent to using two commands above under the hood.
Since asked and answered by the same person, I'll add a npm link as an alternative.
from docs:
This is handy for installing your own stuff, so that you can work on it and test it iteratively without having to continually rebuild.
cd ~/projects/node-bloggy # go into the dir of your main project
npm link ../node-redis # link the dir of your dependency
[Edit] As of NPM 2.0, you can declare local dependencies in package.json
"dependencies": {
"bar": "file:../foo/bar"
}
npm pack + package.json
This is what worked for me:
STEP 1: In module project, execute npm pack:
This will build a <package-name>-<version>.tar.gz file.
STEP 2: Move the file to the consumer project
Ideally you can put all such files in a tmp folder in your consumer-project root:
STEP 3: Refer it in your package.json:
"dependencies": {
"my-package": "file:/./tmp/my-package-1.3.3.tar.gz"
}
STEP 4: Install the packages:
npm install or npm i or yarn
Now, your package would be available in your consumer-project's node_modules folder.
Good Luck...
Neither of these approaches (npm link or package.json file dependency) work if the local module has peer dependencies that you only want to install in your project's scope.
For example:
/local/mymodule/package.json:
"name": "mymodule",
"peerDependencies":
{
"foo": "^2.5"
}
/dev/myproject/package.json:
"dependencies":
{
"mymodule": "file:/local/mymodule",
"foo": "^2.5"
}
In this scenario, npm sets up myproject's node_modules/ like this:
/dev/myproject/node_modules/
foo/
mymodule -> /local/mymodule
When node loads mymodule and it does require('foo'), node resolves the mymodule symlink, and then only looks in /local/mymodule/node_modules/ (and its ancestors) for foo, which it doen't find. Instead, we want node to look in /local/myproject/node_modules/, since that's where were running our project from, and where foo is installed.
So, we either need a way to tell node to not resolve this symlink when looking for foo, or we need a way to tell npm to install a copy of mymodule when the file dependency syntax is used in package.json. I haven't found a way to do either, unfortunately :(
Missing the main property?
As previous people have answered npm i --save ../location-of-your-packages-root-directory.
The ../location-of-your-packages-root-directory however must have two things in order for it to work.
package.json in that directory pointed towards
main property in the package.json must be set and working i.g. "main": "src/index.js", if the entry file for ../location-of-your-packages-root-directory is ../location-of-your-packages-root-directory/src/index.js
So I had a lot of problems with all of the solutions mentioned so far...
I have a local package that I want to always reference (rather than npm link) because it won't be used outside of this project (for now) and also won't be uploaded to an npm repository for wide use as of yet.
I also need it to work on Windows AND Unix, so sym-links aren't ideal.
Pointing to the tar.gz result of (npm package) works for the dependent npm package folder, however this causes issues with the npm cache if you want to update the package. It doesn't always pull in the new one from the referenced npm package when you update it, even if you blow away node_modules and re-do your npm-install for your main project.
so.. This is what worked well for me!
Main Project's Package.json File Snippet:
"name": "main-project-name",
"version": "0.0.0",
"scripts": {
"ng": "ng",
...
"preinstall": "cd ../some-npm-package-angular && npm install && npm run build"
},
"private": true,
"dependencies": {
...
"#com/some-npm-package-angular": "file:../some-npm-package-angular/dist",
...
}
This achieves 3 things:
Avoids the common error (at least with angular npm projects) "index.ts is not part of the compilation." - as it points to the built (dist) folder.
Adds a preinstall step to build the referenced npm client package to make sure the dist folder of our dependent package is built.
Avoids issues where referencing a tar.gz file locally may be cached by npm and not updated in the main project without lots of cleaning/troubleshooting/re-building/re-installing.
I hope this is clear, and helps someone out.
The tar.gz approach also sort of works..
npm install (file path) also sort of works.
This was all based off of a generated client from an openapi spec that we wanted to keep in a separate location (rather than using copy-pasta for individual files)
======
UPDATE:
======
There are additional errors with a regular development flow with the above solution, as npm's versioning scheme with local files is absolutely terrible. If your dependent package changes frequently, this whole scheme breaks because npm will cache your last version of the project and then blow up when the SHA hash doesn't match anymore with what was saved in your package-lock.json file, among other issues.
As a result, I recommend using the *.tgz approach with a version update for each change. This works by doing three things.
First:
For your dependent package, use the npm library "ng-packagr". This is automatically added to auto-generated client packages created by the angular-typescript code generator for OpenAPI 3.0.
As a result the project that I'm referencing has a "scripts" section within package.json that looks like this:
"scripts": {
"build": "ng-packagr -p ng-package.json",
"package": "npm install && npm run build && cd dist && npm pack"
},
And the project referencing this other project adds a pre-install step to make sure the dependent project is up to date and rebuilt before building itself:
"scripts": {
"preinstall": "npm run clean && cd ../some-npm-package-angular && npm run package"
},
Second
Reference the built tgz npm package from your main project!
"dependencies": {
"#com/some-npm-package-angular": "file:../some-npm-package-angular/dist/some-npm-package-angular-<packageVersion>.tgz",
...
}
Third
Update the dependent package's version EVERY TIME you update the dependent package. You'll also have to update the version in the main project.
If you do not do this, NPM will choke and use a cached version and explode when the SHA hash doesn't match. NPM versions file-based packages based on the filename changing. It won't check the package itself for an updated version in package.json, and the NPM team stated that they will not fix this, but people keep raising the issue: https://github.com/microsoft/WSL/issues/348
for now, just update the:
"version": "1.0.0-build5",
In the dependent package's package.json file, then update your reference to it in the main project to reference the new filename, ex:
"dependencies": {
"#com/some-npm-package-angular": "file:../some-npm-package-angular/dist/some-npm-package-angular-1.0.0-build5.tgz",
...
}
You get used to it. Just update the two package.json files - version then the ref to the new filename.
Hope that helps someone...
I came across different solution than above while installing custom build package for CKEditor5.
So I uploaded package to app root directory, than:
npm add file:./ckeditor5
In my package.json package is listed as a file:
"ckeditor5-custom-build": "file:ckeditor5",
I think this answer could be relevant to the topic on how to add local package.
For installing local module / package, that not yet on npm or you are developing an npm package and want to test it locally before publishing it. You can try this -
npm i yalc -g
Go to the module/package folder then -
yalc publish
Your packakge is ready to use, now go the project you want to install it -
yalc add <Your package name>
Package will be installed to you project. If you want to remove it -
yalc remove <Your package name>
For more recent versions of npm (I'm using 8.1.3 under macOS Big Sur), the sequence of commands is even easier...
cd /path-where-your-local-project-is/
npm init
This will ask you for some data related to your project and properly initialises your project.json file.
Once that is done, you can install additional modules with:
cd /path-where-your-local-project-is/
npm install --save-dev some-npm-module .
That's all you need!
Note: I believe that the trailing dot is not necessary if you're inside the project directory, but I also think that it doesn't hurt to add it :-)
(I wonder why the official docs still don't explain this...)