Update package.json in several places after "npm i" - javascript

I want to be able to detect any new package installed according to my package.json file, so when ever I do "npm i", it will automatically added to another section on my package.json file.
For example, if I do "npm i axios", It will update in 2 places on my package.json file:
on "dependencies" as usual, and on new section I created: "extDependencies".
Is there any way to detect any new installed packages?

Check this out: npm-scripts documentation
If you want to run a specific script at a specific lifecycle event for
ALL packages, then you can use a hook script.
Place an executable file at node_modules/.hooks/{eventname}, and it’ll
get run for all packages when they are going through that point in the
package lifecycle for any packages installed in that root.
Hook scripts are run exactly the same way as package.json scripts.
That is, they are in a separate child process, with the env described
above.
You could use this to create a postinstall script (bash, python, node.js, etc) that reads the npm_package_name and npm_package_version environment variables and then use those to update the package.json.

Related

How to install a npm package from github requiring a build step, e.g. when forking a library?

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"
}
}

Can I include bash/sh files as dependencies in package.json?

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.

Npm - how to manage one-repo, multi-package codebase

I’m building a node.js app that needs to be scalable and maintainable. The idea is to have one repository, but with more than one module inside it.
We are using local modules with npm, but we have a problem is that when we update a modules npm won’t update the dependent services.
here an example of the package.json
// ./app/package.json
{
"dependencies": {
"error-lib": "file:../modules/error-lib"
},
"scripts": {
"start": "node app.js",
"debug": "node --inspect-brk app.js",
},
"main": "app.js",
}
// ../modules/error-lib/pacjake.json
{
"name": "error-lib",
"version": "1.0.0",
"main": "index.ts"
}
I have one problem when a developer updates the error-lib module, an npm install command won't update the old error-lib inside app/node_modules.
I don't want to bump the version of the modules on every change because they follow the same versioning and release lifecycle.
And I don't want to rm -rf modules manually every time.
Any idea on how to automate a repo with many local modules?
TL;DR (Why one repo, many local modules)
We came from a Java background, where we have a single repo multi-module architecture managed by maven.
What we do in maven is to isolate services, common-library, data model in isolated modules.
We have only one repo for all the modules and service since everyone follows the same release cycle with a uniform versioning.
When a developer needs to apply some change he or she can update one of the modules or the services and maven will take care of every dependency and the code will be up to date and ready to rock.
But we want separate modules for many reasons:
This will force us to decouple the functionality and make code reusable since every module can virtually be placed in a separate repo at any time.
If some module will become usable outside of the repo can be externalized and maintained.
We want to achieve the same design for a new part in node.js
Looks like you need $ npm link instead, creating symlinks to your packages within ./app/node_modules. This way, the linked packages are always 'up-to-date'.
Linking a package is a 2-step process:
$ npm link at ../modules/error-lib creates a symbolic package in your global node_modules folder under the name error-lib, specified in ../modules/error-lib/package.json its "name" value.
$ npm link error-lib at ./app to create the second link inside ./app/node_modules to <global-node_modules>/error-lib.
more about npm link
Edit in response to your comment:
I've seen some drawbacks of npm link:
the dependency is not listed in the app package.json, so how I know which dependency I'm linking?
the node_modules is not committed on the repo, so the CI/CD will need to do the linking while listing modules/error-lib inside the app package.json I have a place to take track of the dependencies.
There is any best practice on how to automate npm link or to mitigate the drawbacks?
NPM packages are supposed to be distributed through the npm registry. This means that your error-lib package also should be distributed this way, and be installed as any other package. This will require you to list your package in the package.json file in advance, which is essentially what you want.
If you instead intend to keep everything in one place there is no use in using NPM at all. Just resolve your packages with relative import paths in your software.
Since you said that all the packages follow the same release cycle as your main software, and they live in the same repo, why would you resolve these packages with NPM while they seem more like just part of the software?
error-lib is either a package, or it is not. If it is, it ideally has its own CI/CD pipeline, as large or tiny as required, involving testing, building and distribution. This pipeline is then to be run before the pipeline of your dependent repositories.

NPM package for NPM scripts

I'm trying to write an npm package that will add a specific npm script to whatever package.json on which it is depended. Nothing in the npm package.json / script docs is bringing me remotely close.
I'm basically trying to do this:
I install an npm package (call it 'cool-thing')
cool-thing, by installing, adds an npm script to my existent package.json
I can call cool-thing on the command line and it will perform whatever action is specified in the package.json
Anyone know how I could do this?
I am assuming you are working on a package cool-parent which depends on package cool-thing because you want to run the executable that cool-thing provides.
Normally, you would manually modify the cool-parent package.json to add a script:
"scripts": {
"cool-thing": "cool-thing"
}
Now for user convenience, you would like 1) to automate this modification. Not only that, you want to 2) make the modification automatically after the package is installed as a dependency.
There are some drawbacks to part 2, the developer of cool-parent
might not want to add a script,
might already have a script called cool-thing,
might not have a package.json,
...
I consider making modification outside of the package itself during install time to be an undesirable side effect.
If you still want to do it, you can using a postinstall script in cool-thing. You would need to figure out the location of the package.json of the parent if there is one through working directories and perhaps environment variables that npm provides.

Installing a local module using npm?

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...)

Categories

Resources