├── .gitignore
├── README.md
├── index.js
├── package-lock.json
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # why-npm-i-so-long
2 |
3 |

4 |
5 | Little utility to ease troubleshooting why installing npm dependencies takes too long.
6 |
7 | ## What is "publish size" vs "install size"?
8 |
9 | The "publish size" is the size of the source code published to npm. This number is easy to detect and should be pretty small.
10 |
11 | The "install size" is the size your hard drive will report after running npm install. This includes the package, all of the dependencies, and its dependency's dependencies...and so on.
12 |
13 | ## Use without installing
14 |
15 | ```sh
16 | npx why-npm-i-so-long path/to/package.json
17 | ```
18 |
19 | ## Installation
20 |
21 | ```sh
22 | npm install --global why-npm-i-so-long
23 | ```
24 |
25 | ## Usage
26 |
27 | See install size of dependencies
28 | ```sh
29 | why-npm-i-so-long path/to/package.json
30 | ```
31 | See install size of devDependencies
32 | ```sh
33 | why-npm-i-so-long path/to/package.json --dev
34 | ```
35 |
36 | ## Acknowledgments
37 |
38 | - [packagephobia](https://github.com/styfle/packagephobia)
39 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const fs = require('node:fs');
4 | const path = require('node:path');
5 | const {styleText} = require('node:util')
6 | const {log} = console;
7 |
8 | function program(...args) {
9 | const isDevIndex = args.indexOf('--dev');
10 | const isDev = isDevIndex !== -1;
11 | const pathToPkg = isDev ? args[isDevIndex === 0 ? 1 : 0] : args[0];
12 | const resolvedPath = path.resolve(pathToPkg);
13 |
14 | if (!fs.existsSync(resolvedPath)) return log('File does not exist');
15 | if (!resolvedPath.endsWith('.json'))
16 | return log('Provided file is not a json file');
17 |
18 | const pkg = require(resolvedPath);
19 | const {dependencies, devDependencies} = pkg;
20 |
21 | const depsToProcess = isDev ? devDependencies : dependencies;
22 |
23 | if (depsToProcess === undefined) {
24 | return log(`JSON does not have ${isDev ? 'devD' : 'd'}ependencies`);
25 | }
26 |
27 | Promise.all(
28 | Object.entries(depsToProcess).map(([name, version]) =>
29 | fetch(
30 | [
31 | 'https://packagephobia.now.sh/v2/api.json?p=',
32 | name,
33 | '@',
34 | version.replace(/[\^=<>~]/g, ''),
35 | ].join(''),
36 | ).then(res => res.json())
37 | .catch(() => ({name, install: {bytes: -1}, publish: {bytes: -1}})),
38 | ),
39 | )
40 | .then(sizes => {
41 | const title = `${pkg.name}'s ${isDev ? 'devD ' : 'd'}ependencies:\n`;
42 | log(styleText('bold', title));
43 |
44 | return sizes
45 | .sort((a, b) => a.install.bytes - b.install.bytes)
46 | .map(({name, version, install, publish}) => {
47 | if (install.bytes === 0) {
48 | return `${name} - unknown`;
49 | }
50 | if (install.bytes === -1) {
51 | return `${name} - api failed`;
52 | }
53 | return `
54 | ${styleText('bold', name)} @ ${version}
55 | \tinstall size\t${colorSize(install)}
56 | \tpublish size\t${colorSize(publish)}
57 | `.trim();
58 | })
59 | .join('\n\n');
60 | })
61 | .then(log);
62 | }
63 |
64 | function colorSize({bytes, pretty}) {
65 | return bytes > 1048576
66 | ? styleText(['bold', 'red'], pretty)
67 | : bytes > 307200
68 | ? styleText('yellow', pretty)
69 | : styleText('green', pretty);
70 | }
71 |
72 | program(...process.argv.slice(2));
73 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "why-npm-i-so-long",
3 | "version": "2.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "why-npm-i-so-long",
9 | "version": "2.0.0",
10 | "license": "MIT",
11 | "bin": {
12 | "why-npm-i-so-long": "index.js"
13 | },
14 | "engines": {
15 | "node": ">=20.12"
16 | }
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "why-npm-i-so-long",
3 | "version": "2.0.0",
4 | "description": "CLI to check dependencies size",
5 | "main": "index.js",
6 | "bin": "./index.js",
7 | "scripts": {
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "keywords": [
11 | "npm",
12 | "cli",
13 | "install",
14 | "size"
15 | ],
16 | "repository": {
17 | "type": "git",
18 | "url": "git+https://github.com/antonk52/why-npm-i-so-long.git"
19 | },
20 | "bugs": {
21 | "url": "https://github.com/antonk52/why-npm-i-so-long/issues"
22 | },
23 | "author": "antonk52",
24 | "license": "MIT",
25 | "engines": {
26 | "node": ">=20.12"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------