Is there a way to alphabetize package.json without installing a package? - npm

I've been working on lots of old npm packages that have their dependencies all out of order. They're shrinkwrapped packages, so updating dependencies is a bit of work (testing and verifying that the dependency changes didn't break anything), but I'm manually moving some dependencies from the devDependencies key to the dependencies key, and I don't want to do anything except alphabetize them before I commit. Rather than doing it manually, is there an easy way to programmatically alphabetize them with npm?

The sort-package-json package sorts not only dependencies and devDependencies, but other keys as well. I know the original questions didn't ask about the other keys, but I think it's cool to have all keys sorted.
You can simply run:
npx sort-package-json
Example from the package page:
$ cd my-project
$ cat package.json
{
"dependencies": {
"sort-package-json": "1.0.0",
"sort-object-keys": "1.0.0"
},
"version": "1.0.0",
"name": "my-awesome-project"
}
$ npx sort-package-json
package.json is sorted!
$ cat package.json
{
"name": "my-awesome-project",
"version": "1.0.0",
"dependencies": {
"sort-object-keys": "1.0.0",
"sort-package-json": "1.0.0"
}
}
This does not remove the trailing newline like the npm-sort package mentioned by Wolfgang.
Multiple files
$ sort-package-json "my-package/package.json" "other-package/package.json"
$ sort-package-json "package.json" "packages/*/package.json"

Just run npm remove --save anything or npm remove --save-dev whatever and npm will sort that section, without actually touching any of the content. Of course you should make sure that the package name you pass it (which can be anything, spam your keyboard) isn't in your package.json.

In addition to martias answer, you can just run:
npx sort-package-json
This won't install it permanently. You need npm >5.2.

I have found the npm-sort package, which seems to work quite well, with the minor niggle that it removes the trailing newline from the package.json file.

If you're using WebStorm, just select the lines you want to sort and click Edit > Sort Lines.
But for VScode there is no internal solution, so use this plugin https://marketplace.visualstudio.com/items?itemName=ue.alphabetical-sorter

You might also want to take a look at fixpack, a CLI to update your package.json following their (slightly) opinionated order. You can however add a .fixpackrc to define your own rules, the defaults are:
name first
description second
version third
author fourth
all other keys in alphabetical order
dependencies and devDependencies sorted alphabetically
newline at the end of the file

you can simply remove or uninstall a non-existing package from your dependencies
npm remove kjkjhkjhkjhkj -f --save
or shorter
npm r -S
other solutions are good but have some drawbacks:
1- sorts other keys which may be unwanted behavior
2- installs external packages such as sort-package-json, even by using npx sort-package-json

in VS-code there's a good packagesorter ofr the whole json file..
there's also something out there called "sortier" which sorts more, and is awesome.

Related

NPM package.json convert * asterisk to version

I have a package.json from my template with several dependencies.
If I want to create a new project, I use my template.
But how to convert all * (asterisk sign / latest version) to a fixed version which is downloaded from npm. npm install --save does not work.
Before npm install (template package.json)
"devDependencies": {
"one": "*",
"two": "*",
"three": "*"
}
should convert to following by npm i --save.
"devDependencies": {
"one": "1.0.0",
"two": "2.0.0",
"three": "3.0.0"
}
How to overwrite the version string?
As per the documentation
(*) --> Matches any version
("") --> (just an empty string) Same as *
The workaround of this issue is shrinkwrap(Read documentation for further information). You can use this command npm shrinkwrap.That creats a npm-shrinkwrap.json file. When you run the command,You get the following comments on console (npm notice package-lock.json has been renamed to npm-shrinkwrap.json. npm-shrinkwrap.json will be used for future installations.). And that will gave the updated package version whatever you have in package.json.
Use npm list --depth=0 to see what versions were installed by installing 'latest' from your template. You should get something like this:
├── #angular-devkit/architect#0.1501.1
├── #angular-devkit/build-angular#15.1.1
├── #angular-eslint/builder#15.2.0
Copy, modify and paste output into your package.json. Then you should probably remove node_modules and check if running npm ci works properly.
Notes:
This is how you should specify wanted versions in package.json:
"react": "^16.0.0" // carat: allow 16.1.0
"react": "~16.0.0" // tilde: allow 16.0.1
"react": "16.0.0" // exact: only 16.0.0
If your template is fairly complicated, it is highly unlikly that such workflow would be convenient. I would advise having specific versions in the template carefully combined, so that they work. Then, once every couple of months, you should (again, carefully) updgrade libraries in your template. Otherwise, sooner or later you are going to install two latest version of some conflicting pakages.

Why does `npm install` add / remove caret (^) to / from version numbers?

I have a project that I work on with two different laptops. Sometimes I add extra packages to my project, so I have to use npm install <package-name> (duh). When I do that, I git push up the new package.json and package-lock.json files, and when I switch computers I have to git pull those changes, then run npm install again to get that package onto the other computer.
I recently noticed and started caring that one laptop kept adding carets (^) to the beginning of every package version number. For example:
One computer set package version #s to look like this:
"regexpu-core": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz",
"integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=",
"requires": {
"regenerate": "1.4.0",
"regjsgen": "0.2.0",
"regjsparser": "0.1.5"
}
},
The other set package version #s to look like this:
"regexpu-core": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz",
"integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=",
"requires": {
"regenerate": "^1.2.1",
"regjsgen": "^0.2.0",
"regjsparser": "^0.1.4"
}
},
I understand that carets (^) mean the version is not 100% precise, but I'm trying to figure out WHY my different laptops create different formats for package versions! I checked this SO question which has some great explanations for the differences between ~ and ^, but I didn't find anything explaining why npm would sometimes add and sometimes remove carets (^) altogether. I also looked at this npm issue on Github which recommended looking at npm config settings, but both of my laptops have the same settings:
npm config get save = true (both computers)
npm config get save-prefix = ^ (both computers)
npm config get save-exact = false (both computers)
One laptop was running npm version 5.6.0, but I just updated it to 6.5.0. The other computer was running version 6.4.1, but I also updated it to 6.5.0. I tried running npm install in my project on both computers, but still I find that one computer always removes ^ and the other always adds ^.
Please let me know if there's something I'm missing. Thanks for any help!
Edit: According to the discussion in issue #20434 this occurs by design using npm >=6.0.0.
Why does this happen?
#rarkins elaborately explains the reasoning for why this happens (and it’s advantages) in this comment. For convenience his comment is quoted below (verbatim):
Let's say that you use pinned versions of dependencies 'aaa', 'bbb' and 'ccc'. Let's say they each depend on 'zzz' like so:
aaa depends on zzz#^1.0.0
bbb depends on zzz#^1.1.0
ccc depends on zzz#^1.0.1
i.e. all three of them depend on a range of zzz, and not an exact version.
And let's say that the latest version of zzz is 1.5.0.
Both before and after this change, it's pretty obvious that the resolved version of zzz should be 1.5.0, so the only difference is how the package-lock.json is structured and documents this sub-dependency.
Before, the lock file would show that all three of them depend on zzz#1.5.0, and the resolved version of z is 1.5.0.
Now, it documents the actual "original" dependency versions (e.g. ^1.0.0, ^1.1.0, etc) for each dependency, but still shows the resolved version of z as 1.5.0.
Then consider what happens when zzz#1.5.1 is released:
Before, the lock file would need to update from z#1.5.0 to z#1.5.1 in all four places.
Now, the lock file only needs to update the resolved version of z to 1.5.1 while the dependencies can keep the ^1.0.0, ^1.1.0, and ^1.0.1 because they haven't changed.
As I mentioned previously in the thread, you still get the exact same node_modules in both cases. The advantages of the new approach are:
You get to see what the dependencies actually require (e.g. a range, and not an exact version). before, you could not tell if aaa actually required exactly zzz#1.5.0 or that it was instead zzz#^1.0.0.
Instead of four lines changing in the lock file, you get only one. It's less churn, and it's more clear what's happened.
As an aside, yarn uses a similar concept with yarn.lock. e.g. here's an example where #sindresorhus/is is pinned, but it's sub-dependency symbol-observable is not:
"#sindresorhus/is#0.10.0":
version "0.10.0"
resolved "https://registry.yarnpkg.com/#sindresorhus/is/-/is-0.10.0.tgz#f42dd6a9d12cd79fa6f53b27cf5bea3a30d2cafa"
dependencies:
symbol-observable "^1.2.0"
Original Answer:
After you git pull the revised package.json and package-lock.json onto computer two try deleting the node_modules directory before installing the packages again.
For example:
Firstly cd to your project directory on computer 2.
Delete the existing node_modules directory by running: rm -rf node_modules.
Then run: npm install
Or you can chain the two aforementioned commands using the && operator:
rm -rf node_modules && npm install

npm - getting rid of nested node_modules in one specific module

I'm kind of lost here!
I'm using a module which has another module nested in its' node_modules.
I.E.
my_project
node_modules
widely_used_module
parent_dependency
node_modules
widely_used_module
I have some fixes in my "own" widely_used_module (it could be just a minor version from the original distributor, but to be completely honest, in this case its' my fork on Github containing some critical fixes).
When I manually remove node_modules/parent_dependency/node_modules, parent_dependency starts to reference to my "widely used module" instead of its' own. But this of course gets overriden once I hit npm install again.
Can I somehow prevent a package to install its' own modules, or can I force a package to reference the root node_modules and ignore its' own?
Is that even the right approach to fixing such issues? I don't want to fork parent_dependency as well...
Thank you
Answering my own question;
Yarn has a built-in solution for this exact issue.
This could be achievable with NPM as well but yarn made it so easy to fix that I moved the project dependencies to be handled by yarn.
Full solution:
Installing yarn
Ran yarn in project's root path
Removed package.lock.json
Added resolutions to my package.json. In my case:
{
"dependencies": {
"...": "...",
"parent_dependency": "^x.y.z"
},
"devDependencies": {
"...": "..."
},
"resolutions": {
"parent_dependency/widely_used_module": "git+https://git#github.com/myuser/widely_used_module.git"
}
}
Ran yarn install.
Result: No more widely_used_module folder under parent_dependency.

NPM: To install a package along with its own devDeps

I am just getting myself familiar with command line tools like npm. I've been searching around for the answer but was not able to find a clear one.
What I am trying to do is to install materialize-css package into my test package, as well as its devDependencies, like "autoprefixer". This is materializeCSS's package.json file.
Here's what I do:
Under my newly created and blank folder "testProject", I use npm init to create a package.json file for my test package:
{
"name": "create_project",
"version": "1.0.0",
"description": "Setting up a project",
"main": "index.html",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "shenk wen",
"license": "MIT"
}
Then, I do
npm install materialize-css
I was expecting the above command would install all the dependencies and devDependencies of materialize-css, but only the dependencies is being installed. I read this question and the accepted answer suggests adding --dev to the command. But that seems not the answer I am looking for because --dev would only make materialize-css a devDependency of my test package, but not installing its own devDependencies. The other answers are not so straightforward. Is there any parameter I can use to achieve this? Or do I have to change the env_variable which I don't know how to?
In older npm versions, 'npm install --dev' installed all devDependencies of all your dependencies. This also used to work in a recursive fashion, so you end up downloading also devDependencies of devDependencies of your dependencies and so on. This resulted in enormously long install time / download size.
Also, the purpose of the feature is questionable: Why should you care about devDeps of your deps? For these reasons --dev was removed from npm:
https://github.com/npm/npm/issues/5554
Current behavior for 'npm install' is: install all deps and devDeps for the 'main' package (the one you 'npm install'-ed in the first place), but when recursing, install only deps (and no devDeps).
If you want to install & save the dependency to your package.json, you should use --save or --save-dev, I don't think --dev does this.
If you want the devDependencies of a module you've installed as a dependency to your project, you almost certainly want to git clone that module's repo or fork it instead. When you run npm install in your cloned repo, that will also install all of the module's devDependencies.
(I'm not a developer by trade and my npm-fu was a bit rusty, so I confused myself about what I was trying to do. Tomas Kulich's question "Why should you care about devDeps of your deps?" helped me realize the error of my ways.)

Install only one package from package.json?

Suppose that somewhere in my package.json I have:
"dependencies": {
"bower": "1.0.0",
// zillion other dependencies
}
Is there a way to make npm install only bower#1.0.0 from my package.json? Like so: npm install --only bower.
My goal is to make npm install and bower install run simultaneously.
As a workaround you may use something like:
$ node -pe "require('./package').dependencies.bower"
// → 1.0.0
$ npm install bower#$(node -pe "require('./package').dependencies.bower")
// npm install bower#1.0.0
// or with jq
$ npm install bower#$(< package.json jq -r '.dependencies.bower')
Where -e/--eval flag evaluates passed string and -p/--print prints result of eval.
💡 Please consider other answers as well since this one may be outdated.
As #atalantus noted in comment, the accepted answer doesn't work on newer version of NPM. Working solution for newer versions (verified on NPM 6.13.4) is:
npm install --no-package-lock --no-save bower#1.0.0
This will install bower and all its dependencies, but prevents installation of anything else you might have in package.json. It also doesn't create or modify existing package-lock.json.
From npm documentation:
The --no-package-lock argument will prevent npm from creating a package-lock.json file. When running with package-lock's disabled npm will not automatically prune your node modules when installing.
--no-save: Prevents saving to dependencies.
Combining this with Anton Rudeshko's approach to find out version in package.json, the final solution is:
VERSION_BOWER=`node -p -e "require('./package.json').dependencies.bower"`
npm install --no-package-lock --no-save bower#"$VERSION_BOWER"
Update 2023
Alternative solution I've just used is to temporarily modify package.json to only keep the dependency you need. That can be done quite easily with help of jq
Given package.json
{
"name": "sample",
"main": "src/index.js",
"dependencies": {
"bower": "1.0.0",
"foo": "^1.2.3",
"bar": "^4.5.6",
},
"devDependencies": {
"baz": "^10.20.30",
}
}
Let's first modify it with jq to only keep bower
cat package.json| jq 'del(.devDependencies) | .dependencies |= {bower:.bower}'
Gives us
{
"name": "sample",
"main": "src/index.js",
"dependencies": {
"bower": "1.0.0"
}
}
by deleting devDependencies completely and leaving only bower in dependencies.
Next up you would want to overwrite package.json with this new, version, but you can't do it directly like this
cat package.json | jq 'del(.devDependencies) | .dependencies |= {bower:.bower}' > package.json
because the file gets overwritten before it's fully read. Instead you need to buffer the output stream of jq command, simplest solution just storing it in variable, and then overwriting the original file
updated=`cat package.json | jq 'del(.devDependencies) | .dependencies |= {bower:.bower}'`
echo $updated > package.json
And package.json now reads:
cat package.json
{
"name": "sample",
"main": "src/index.js",
"dependencies": {
"bower": "1.0.0"
}
}
Run npm install now and you only install bower#1.0.0.
Obviously not always we can afford to modify the file, but you could also back-up the original file, etc. Main point is here how to easily modify json file thanks to jq rather than playing magic tricks with sed :-)
Seeing as with newer versions of NPM the previous solutions don't work anymore, I came up with this:
npm i -g --prefix=$(pwd) bower # $(pwd) = current directory, UNIX only
node bin/bower
Installing a package globally allows you to specify a "prefix" - a directory where the package will be installed. Here I just put the current directory as the folder I want the package to appear in. In CI, this will probably be your repo root.
Keep in mind this will ignore your package.json version and just download the latest one. If you need a specific version, just use bower#someversion instead.
An alternative solution is just rm package.json && npm i bower, or mv package.json _package.json && npm i bower if you want to restore it later