pipm 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -0
- package/bin/pipm.js +368 -0
- package/package.json +30 -0
package/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# pipm
|
|
2
|
+
|
|
3
|
+
Stop staring at that awkward, infinite loading spinner. **pipm** is a Python-inspired wrapper for **npm** that actually tells you what's happening behind the scenes.
|
|
4
|
+
|
|
5
|
+
## The "Is it stuck?" Problem
|
|
6
|
+
|
|
7
|
+
We've all been there: you run `npm install`, the spinner starts spinning, and then... nothing. For 5 minutes. You start wondering if your internet died, if your terminal crashed, or if `npm` is just having a mid-life crisis.
|
|
8
|
+
|
|
9
|
+
**pipm** turns that "silent wait" into a bit of literal fun. Instead of a vague spinner, you get a full dashboard showing you exactly what's moving under the hood.
|
|
10
|
+
|
|
11
|
+
## Cool Stuff it Does
|
|
12
|
+
|
|
13
|
+
* **Real Speed Monitoring:** No fake "estimated" percentages. It polls your system's network adapter directly to show you exactly how many MB/s your machine is pulling.
|
|
14
|
+
* **PIP-Style Progress Bar:** High-contrast Green (done) and Red (to-go) bar.
|
|
15
|
+
* **State-Aware Humor:** Keeps you engaged with 50+ status phrases. You'll get startup, working, and finishing jokes based on the actual stage of the install.
|
|
16
|
+
* **Zero Bloat:** Built with 100% native Node.js. No extra `node_modules` to download to make the tool work.
|
|
17
|
+
* **Flicker-Free:** Custom rendering that overwrites lines instead of clearing the screen, so it doesn't blink like an old neon sign.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
Just install it globally and you're good to go:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install -g .
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## How to use it
|
|
28
|
+
|
|
29
|
+
Use it exactly like you use `npm`. There's nothing new to learn.
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Standard install
|
|
33
|
+
pipm install
|
|
34
|
+
|
|
35
|
+
# Adding stuff
|
|
36
|
+
pipm add lodash express shadcn
|
|
37
|
+
|
|
38
|
+
# Anything else
|
|
39
|
+
pipm init
|
|
40
|
+
pipm run dev
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## How it works (The technical bit)
|
|
44
|
+
|
|
45
|
+
`pipm` is a wrapper, not a replacement. When you run a command, it:
|
|
46
|
+
1. Spawns the official `npm` process in the background.
|
|
47
|
+
2. Forces npm into `http` log mode so we can "see" every download request.
|
|
48
|
+
3. Calculates your network bandwidth by tracking your OS's `ReceivedBytes` (it works on Windows, Mac, and Linux).
|
|
49
|
+
4. Uses ANSI escape codes to redraw the UI in-place every few hundred milliseconds.
|
|
50
|
+
|
|
51
|
+
Basically, it's a personality-filled skin for the npm engine we already know and love.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
Enjoy the status fun to keep you engaged while you wait! :)
|
package/bin/pipm.js
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { spawn, execSync } = require('child_process');
|
|
4
|
+
const readline = require('readline');
|
|
5
|
+
|
|
6
|
+
const ESC = '\x1b';
|
|
7
|
+
const HIDE_CURSOR = `${ESC}[?25l`;
|
|
8
|
+
const SHOW_CURSOR = `${ESC}[?25h`;
|
|
9
|
+
|
|
10
|
+
const c = {
|
|
11
|
+
reset: `${ESC}[0m`,
|
|
12
|
+
cyan: `${ESC}[36m`,
|
|
13
|
+
gray: `${ESC}[90m`,
|
|
14
|
+
white: `${ESC}[97m`,
|
|
15
|
+
green: `${ESC}[32m`,
|
|
16
|
+
red: `${ESC}[31m`,
|
|
17
|
+
yellow: `${ESC}[33m`
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const phrases = {
|
|
21
|
+
startup: [
|
|
22
|
+
'Waking up the npm hamsters...',
|
|
23
|
+
'Shoveling coal into the server pipes...',
|
|
24
|
+
'Bribing the registry servers...',
|
|
25
|
+
'Warming up the download engines...',
|
|
26
|
+
'Assembling the code avengers...',
|
|
27
|
+
'Consulting the package oracle...',
|
|
28
|
+
'Summoning dependencies from the void...',
|
|
29
|
+
'Performing npm rain dance...',
|
|
30
|
+
'Priming the dependency pump...',
|
|
31
|
+
'Calibrating the package-o-meter...',
|
|
32
|
+
'Greasing the internet tubes...',
|
|
33
|
+
'Checking if the internet is plugged in...',
|
|
34
|
+
'Initializing the hyperdrive...',
|
|
35
|
+
'Knocking on the registry door...'
|
|
36
|
+
],
|
|
37
|
+
working: [
|
|
38
|
+
'Negotiating with the node_modules black hole...',
|
|
39
|
+
'Teaching the code how to behave...',
|
|
40
|
+
'Convincing packages to cooperate...',
|
|
41
|
+
'Downloading the internet (just the good parts)...',
|
|
42
|
+
'Herding digital cats...',
|
|
43
|
+
'Brewing some fresh dependencies...',
|
|
44
|
+
'Asking packages nicely to install...',
|
|
45
|
+
'Untangling the dependency spaghetti...',
|
|
46
|
+
'Waiting for the bits to align...',
|
|
47
|
+
'Compiling prayers to the JavaScript gods...',
|
|
48
|
+
'Reticulating splines...',
|
|
49
|
+
'Convincing electrons to flow faster...',
|
|
50
|
+
'Parsing the meaning of life...',
|
|
51
|
+
'Optimizing the speed of light...',
|
|
52
|
+
'Untangling quantum dependencies...',
|
|
53
|
+
'Feeding the code monkeys...',
|
|
54
|
+
'Polishing the bits and bytes...',
|
|
55
|
+
'Synchronizing with the cloud hamsters...',
|
|
56
|
+
'Wrangling 1s and 0s...',
|
|
57
|
+
'Balancing the load on the hamsters...',
|
|
58
|
+
'Dodging 404s...',
|
|
59
|
+
'Refactoring the universe...',
|
|
60
|
+
'Applying digital WD-40...',
|
|
61
|
+
'Compressing the space-time continuum...',
|
|
62
|
+
'Organizing the bits by color...',
|
|
63
|
+
'Spinning up the logic turbines...',
|
|
64
|
+
'Hunting for the missing semicolons...',
|
|
65
|
+
'Buffering the excitement...',
|
|
66
|
+
'Distilling the pure essence of JS...',
|
|
67
|
+
'Searching for the holy grail of dependencies...',
|
|
68
|
+
'Whispering sweet nothings to the terminal...',
|
|
69
|
+
'Trying to find where I put that variable...'
|
|
70
|
+
],
|
|
71
|
+
finishing: [
|
|
72
|
+
'Tidying up the node_modules room...',
|
|
73
|
+
'Double-checking the semicolon count...',
|
|
74
|
+
'Making sure the code is happy...',
|
|
75
|
+
'Finalizing the digital handshake...',
|
|
76
|
+
'Cleaning up the virtual workspace...',
|
|
77
|
+
'Telling the hamsters they did a good job...',
|
|
78
|
+
'Inspecting the dependency tree for splinters...',
|
|
79
|
+
'Removing the training wheels...',
|
|
80
|
+
'Signing the digital peace treaty...',
|
|
81
|
+
'Polishing the final bundle...',
|
|
82
|
+
'Putting the bits to bed...',
|
|
83
|
+
'Wrapping it all up in a digital bow...'
|
|
84
|
+
]
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
function getStatusPhrase(completedCount, isFinished = false) {
|
|
88
|
+
let category = 'working';
|
|
89
|
+
if (isFinished) {
|
|
90
|
+
category = 'finishing';
|
|
91
|
+
} else if (completedCount === 0) {
|
|
92
|
+
category = 'startup';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const list = phrases[category];
|
|
96
|
+
return list[Math.floor(Math.random() * list.length)];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function formatTime(seconds) {
|
|
100
|
+
const hrs = Math.floor(seconds / 3600);
|
|
101
|
+
const mins = Math.floor((seconds % 3600) / 60);
|
|
102
|
+
const secs = Math.floor(seconds % 60);
|
|
103
|
+
return `${hrs.toString().padStart(2, '0')}:${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function formatSpeed(bytesPerSec) {
|
|
107
|
+
if (bytesPerSec >= 1024 * 1024) {
|
|
108
|
+
return (bytesPerSec / (1024 * 1024)).toFixed(1) + ' MB/s';
|
|
109
|
+
} else if (bytesPerSec >= 1024) {
|
|
110
|
+
return (bytesPerSec / 1024).toFixed(1) + ' KB/s';
|
|
111
|
+
}
|
|
112
|
+
return bytesPerSec.toFixed(0) + ' B/s';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function extractPackageName(url) {
|
|
116
|
+
try {
|
|
117
|
+
const urlObj = new URL(url);
|
|
118
|
+
const pathname = urlObj.pathname;
|
|
119
|
+
|
|
120
|
+
if (pathname.includes('/-/')) {
|
|
121
|
+
const parts = pathname.split('/-/');
|
|
122
|
+
if (parts.length >= 2) return parts[1];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (pathname.startsWith('/@')) {
|
|
126
|
+
const parts = pathname.split('/');
|
|
127
|
+
if (parts.length >= 3) return `${parts[1]}/${parts[2]}`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const parts = pathname.split('/');
|
|
131
|
+
return parts.length >= 2 ? parts[1] : pathname;
|
|
132
|
+
} catch {
|
|
133
|
+
return url.substring(0, 50);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function getNetworkBytes() {
|
|
138
|
+
try {
|
|
139
|
+
if (process.platform === 'win32') {
|
|
140
|
+
const output = execSync('netstat -e', { stdio: ['pipe', 'pipe', 'ignore'], timeout: 500 }).toString();
|
|
141
|
+
const match = output.match(/Bytes\s+(\d+)/i);
|
|
142
|
+
return match ? parseInt(match[1]) : 0;
|
|
143
|
+
} else if (process.platform === 'darwin') {
|
|
144
|
+
const output = execSync('netstat -ibn', { stdio: ['pipe', 'pipe', 'ignore'], timeout: 500 }).toString();
|
|
145
|
+
const lines = output.split('\n');
|
|
146
|
+
let totalBytes = 0;
|
|
147
|
+
for (let i = 1; i < lines.length; i++) {
|
|
148
|
+
const parts = lines[i].trim().split(/\s+/);
|
|
149
|
+
if (parts.length >= 7) {
|
|
150
|
+
const val = parseInt(parts[6]);
|
|
151
|
+
if (!isNaN(val)) totalBytes += val;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return totalBytes;
|
|
155
|
+
} else {
|
|
156
|
+
const fs = require('fs');
|
|
157
|
+
if (fs.existsSync('/proc/net/dev')) {
|
|
158
|
+
const data = fs.readFileSync('/proc/net/dev', 'utf8');
|
|
159
|
+
const lines = data.split('\n');
|
|
160
|
+
let totalBytes = 0;
|
|
161
|
+
for (let i = 2; i < lines.length; i++) {
|
|
162
|
+
const parts = lines[i].trim().split(/\s+/);
|
|
163
|
+
if (parts.length > 1) {
|
|
164
|
+
totalBytes += parseInt(parts[1]) || 0;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return totalBytes;
|
|
168
|
+
}
|
|
169
|
+
return 0;
|
|
170
|
+
}
|
|
171
|
+
} catch (e) {
|
|
172
|
+
return 0;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function buildProgressBar(percent, width = 40) {
|
|
177
|
+
const p = Math.max(0, Math.min(100, percent));
|
|
178
|
+
const filledCount = Math.floor((p / 100) * width);
|
|
179
|
+
const remainingCount = width - filledCount;
|
|
180
|
+
|
|
181
|
+
const filledPart = `${c.green}${'\u2501'.repeat(filledCount)}${c.reset}`;
|
|
182
|
+
const emptyPart = `${c.red}${'\u2501'.repeat(remainingCount)}${c.reset}`;
|
|
183
|
+
|
|
184
|
+
return filledPart + emptyPart;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function pad(str, length) {
|
|
188
|
+
const s = String(str);
|
|
189
|
+
const visibleLength = s.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
190
|
+
if (visibleLength >= length) return s;
|
|
191
|
+
return s + ' '.repeat(length - visibleLength);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async function main() {
|
|
195
|
+
const args = process.argv.slice(2);
|
|
196
|
+
|
|
197
|
+
if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
|
|
198
|
+
const npm = spawn('npm', args, { stdio: 'inherit', shell: true });
|
|
199
|
+
npm.on('close', (code) => process.exit(code));
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const isInstall = ['install', 'i', 'add', 'ci'].includes(args[0]);
|
|
204
|
+
if (!isInstall) {
|
|
205
|
+
const npm = spawn('npm', args, { stdio: 'inherit', shell: true });
|
|
206
|
+
npm.on('close', (code) => process.exit(code));
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const displayCommand = 'npm ' + args.join(' ');
|
|
211
|
+
|
|
212
|
+
let completedRequests = 0;
|
|
213
|
+
let startTime = Date.now();
|
|
214
|
+
let phraseChangeTime = Date.now();
|
|
215
|
+
let currentStatus = getStatusPhrase(0);
|
|
216
|
+
let currentAction = 'Initializing...';
|
|
217
|
+
|
|
218
|
+
let displayProgress = 0;
|
|
219
|
+
let targetProgress = 0;
|
|
220
|
+
let isDownloading = false;
|
|
221
|
+
|
|
222
|
+
let lastNetworkBytes = getNetworkBytes();
|
|
223
|
+
let currentSpeed = 0;
|
|
224
|
+
|
|
225
|
+
const LINE_COUNT = 5;
|
|
226
|
+
const TERMINAL_WIDTH = process.stdout.columns || 80;
|
|
227
|
+
|
|
228
|
+
process.stdout.write(HIDE_CURSOR);
|
|
229
|
+
|
|
230
|
+
console.log('');
|
|
231
|
+
console.log(`${c.cyan}pipm${c.reset}${c.gray} |${c.reset} Running: ${c.gray}${displayCommand}${c.reset}`);
|
|
232
|
+
console.log('');
|
|
233
|
+
|
|
234
|
+
const npmArgs = [...args, '--loglevel', 'http'];
|
|
235
|
+
const npm = spawn('npm', npmArgs, {
|
|
236
|
+
shell: true,
|
|
237
|
+
stdio: ['inherit', 'pipe', 'pipe']
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
function render(complete = false) {
|
|
241
|
+
const totalElapsed = formatTime((Date.now() - startTime) / 1000);
|
|
242
|
+
const pkgText = completedRequests === 1 ? 'package' : 'packages';
|
|
243
|
+
|
|
244
|
+
const finalProgress = complete ? 100 : Math.floor(displayProgress);
|
|
245
|
+
const bar = buildProgressBar(finalProgress);
|
|
246
|
+
const icon = complete ? `${c.green}✓${c.reset}` : (isDownloading ? `${c.cyan}↓${c.reset}` : '·');
|
|
247
|
+
const speedText = (!complete && currentSpeed > 0) ? ` ${c.yellow}${formatSpeed(currentSpeed)}${c.reset}` : '';
|
|
248
|
+
|
|
249
|
+
readline.moveCursor(process.stdout, 0, -LINE_COUNT);
|
|
250
|
+
|
|
251
|
+
const line1 = `${c.cyan}pipm${c.reset} ${bar} ${icon} ${completedRequests} ${pkgText}${speedText}`;
|
|
252
|
+
process.stdout.write(pad(line1, TERMINAL_WIDTH) + '\n');
|
|
253
|
+
process.stdout.write(pad('', TERMINAL_WIDTH) + '\n');
|
|
254
|
+
|
|
255
|
+
const line3 = `${c.gray}Total Time:${c.reset} ${totalElapsed}`;
|
|
256
|
+
process.stdout.write(pad(line3, TERMINAL_WIDTH) + '\n');
|
|
257
|
+
|
|
258
|
+
const line4 = `${c.gray}Status:${c.reset} ${currentStatus}`;
|
|
259
|
+
process.stdout.write(pad(line4, TERMINAL_WIDTH) + '\n');
|
|
260
|
+
|
|
261
|
+
const line5 = `${c.gray}Action:${c.reset} ${currentAction}`;
|
|
262
|
+
process.stdout.write(pad(line5, TERMINAL_WIDTH) + '\n');
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
console.log(`${c.cyan}pipm${c.reset} ${buildProgressBar(0)} · 0 packages`);
|
|
266
|
+
console.log('');
|
|
267
|
+
console.log(`${c.gray}Total Time:${c.reset} 00:00:00`);
|
|
268
|
+
console.log(`${c.gray}Status:${c.reset} ${currentStatus}`);
|
|
269
|
+
console.log(`${c.gray}Action:${c.reset} Initializing...`);
|
|
270
|
+
|
|
271
|
+
const updateInterval = setInterval(() => {
|
|
272
|
+
if (Date.now() - phraseChangeTime > 5000) {
|
|
273
|
+
currentStatus = getStatusPhrase(completedRequests);
|
|
274
|
+
phraseChangeTime = Date.now();
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const nowBytes = getNetworkBytes();
|
|
278
|
+
if (nowBytes > lastNetworkBytes) {
|
|
279
|
+
currentSpeed = (nowBytes - lastNetworkBytes) / 0.2;
|
|
280
|
+
} else {
|
|
281
|
+
currentSpeed = 0;
|
|
282
|
+
}
|
|
283
|
+
lastNetworkBytes = nowBytes;
|
|
284
|
+
|
|
285
|
+
if (displayProgress < targetProgress) {
|
|
286
|
+
const step = Math.max(2, (targetProgress - displayProgress) * 0.1);
|
|
287
|
+
displayProgress = Math.min(targetProgress, displayProgress + step);
|
|
288
|
+
} else if (isDownloading && targetProgress < 95) {
|
|
289
|
+
targetProgress += 0.5;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
render();
|
|
293
|
+
}, 200);
|
|
294
|
+
|
|
295
|
+
npm.stdout.on('data', () => { });
|
|
296
|
+
|
|
297
|
+
npm.stderr.on('data', (data) => {
|
|
298
|
+
const lines = data.toString().split('\n');
|
|
299
|
+
for (const line of lines) {
|
|
300
|
+
const startMatch = line.match(/npm http fetch GET (https?:\/\/[^\s]+)/i);
|
|
301
|
+
if (startMatch && !line.match(/\s\d{3}\s/)) {
|
|
302
|
+
isDownloading = true;
|
|
303
|
+
displayProgress = 0;
|
|
304
|
+
targetProgress = 40;
|
|
305
|
+
currentAction = `Fetching ${extractPackageName(startMatch[1])}`;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const completeMatch = line.match(/npm http fetch (GET|POST) (\d+) (https?:\/\/[^\s]+)/i);
|
|
309
|
+
if (completeMatch) {
|
|
310
|
+
completedRequests++;
|
|
311
|
+
targetProgress = 100;
|
|
312
|
+
displayProgress = 100;
|
|
313
|
+
currentAction = `Fetched ${extractPackageName(completeMatch[3])}`;
|
|
314
|
+
|
|
315
|
+
setTimeout(() => {
|
|
316
|
+
displayProgress = 0;
|
|
317
|
+
targetProgress = 0;
|
|
318
|
+
isDownloading = false;
|
|
319
|
+
}, 200);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (isDownloading && targetProgress < 90) {
|
|
323
|
+
targetProgress = Math.min(90, targetProgress + 15);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
npm.on('close', (code) => {
|
|
329
|
+
clearInterval(updateInterval);
|
|
330
|
+
const elapsed = (Date.now() - startTime) / 1000;
|
|
331
|
+
currentStatus = getStatusPhrase(completedRequests, true);
|
|
332
|
+
currentAction = 'All packages installed';
|
|
333
|
+
displayProgress = 100;
|
|
334
|
+
currentSpeed = 0;
|
|
335
|
+
render(true);
|
|
336
|
+
|
|
337
|
+
process.stdout.write(SHOW_CURSOR);
|
|
338
|
+
|
|
339
|
+
console.log('');
|
|
340
|
+
if (code === 0) {
|
|
341
|
+
console.log(`${c.green}pipm${c.reset}${c.gray} |${c.reset} Installation complete (${completedRequests} packages in ${formatTime(elapsed)})`);
|
|
342
|
+
} else {
|
|
343
|
+
console.log(`${c.red}pipm${c.reset}${c.gray} |${c.reset} Installation failed with code ${code}`);
|
|
344
|
+
}
|
|
345
|
+
console.log('');
|
|
346
|
+
process.exit(code);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
npm.on('error', (err) => {
|
|
350
|
+
clearInterval(updateInterval);
|
|
351
|
+
process.stdout.write(SHOW_CURSOR);
|
|
352
|
+
console.error(`${c.red}Failed to start npm: ${err.message}${c.reset}`);
|
|
353
|
+
process.exit(1);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
process.on('SIGINT', () => {
|
|
357
|
+
clearInterval(updateInterval);
|
|
358
|
+
process.stdout.write(SHOW_CURSOR);
|
|
359
|
+
npm.kill('SIGINT');
|
|
360
|
+
process.exit(130);
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
main().catch((err) => {
|
|
365
|
+
process.stdout.write(SHOW_CURSOR);
|
|
366
|
+
console.error(`${c.red}Error: ${err.message}${c.reset}`);
|
|
367
|
+
process.exit(1);
|
|
368
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pipm",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A Python-inspired progress bar wrapper for npm that adds visibility and personality to your package installations",
|
|
5
|
+
"bin": {
|
|
6
|
+
"pipm": "bin/pipm.js"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"test": "node bin/pipm.js install --help"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"npm",
|
|
13
|
+
"pip",
|
|
14
|
+
"progress",
|
|
15
|
+
"cli",
|
|
16
|
+
"install",
|
|
17
|
+
"package-manager",
|
|
18
|
+
"wrapper"
|
|
19
|
+
],
|
|
20
|
+
"author": "",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=14.0.0"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"bin",
|
|
27
|
+
"README.md"
|
|
28
|
+
],
|
|
29
|
+
"dependencies": {}
|
|
30
|
+
}
|