UNPKG

14.6 kBJavaScriptView Raw
1#!/usr/bin/env node
2
3const path = require('path');
4const fs = require('fs');
5const program = require('commander');
6const npm = require('npm');
7const ini = require('ini');
8const echo = require('node-echo');
9const extend = require('extend');
10const open = require('open');
11const async = require('async');
12const request = require('request');
13const only = require('only');
14
15const registries = require('./registries.json');
16const PKG = require('./package.json');
17const NRMRC = path.join(process.env.HOME, '.nrmrc');
18
19const REGISTRY_ATTRS = [];
20const FIELD_AUTH = '_auth';
21const FIELD_ALWAYS_AUTH = 'always-auth';
22const FIELD_IS_CURRENT = 'is-current';
23const FIELD_REPOSITORY = 'repository';
24const IGNORED_ATTRS = [FIELD_IS_CURRENT, FIELD_REPOSITORY];
25
26
27program
28 .version(PKG.version);
29
30program
31 .command('ls')
32 .description('List all the registries')
33 .action(onList);
34
35program
36 .command('current')
37 .description('Show current registry name')
38 .action(showCurrent);
39
40program
41 .command('use <registry>')
42 .description('Change registry to registry')
43 .action(onUse);
44
45program
46 .command('add <registry> <url> [home]')
47 .description('Add one custom registry')
48 .action(onAdd);
49
50program
51 .command('set-auth <registry> [value]')
52 .option('-a, --always-auth', 'Set is always auth')
53 .option('-u, --username <username>', 'Your user name for this registry')
54 .option('-p, --password <password>', 'Your password for this registry')
55 .description('Set authorize information for a custom registry with a base64 encoded string or username and pasword')
56 .action(onSetAuth);
57
58program
59 .command('set-email <registry> <value>')
60 .description('Set email for a custom registry')
61 .action(onSetEmail);
62
63program
64 .command('set-hosted-repo <registry> <value>')
65 .description('Set hosted npm repository for a custom registry to publish packages')
66 .action(onSetRepository);
67
68program
69 .command('del <registry>')
70 .description('Delete one custom registry')
71 .action(onDel);
72
73program
74 .command('home <registry> [browser]')
75 .description('Open the homepage of registry with optional browser')
76 .action(onHome);
77
78program
79 .command('publish [<tarball>|<folder>]')
80 .option('-t, --tag [tag]', 'Add tag')
81 .option('-a, --access <public|restricted>', 'Set access')
82 .option('-o, --otp [otpcode]', 'Set otpcode')
83 .option('-dr, --dry-run', 'Set is dry run')
84 .description('Publish package to current registry if current registry is a custom registry.\n if you\'re not using custom registry, this command will run npm publish directly')
85 .action(onPublish);
86
87program
88 .command('test [registry]')
89 .description('Show response time for specific or all registries')
90 .action(onTest);
91
92program
93 .command('help', { isDefault: true })
94 .description('Print this help')
95 .action(function() {
96 program.outputHelp();
97 });
98
99program
100 .parse(process.argv);
101
102
103if (process.argv.length === 2) {
104 program.outputHelp();
105}
106
107/*//////////////// cmd methods /////////////////*/
108
109function onList() {
110 getCurrentRegistry(function(cur) {
111 var info = [''];
112 var allRegistries = getAllRegistry();
113
114 Object.keys(allRegistries).forEach(function(key) {
115 var item = allRegistries[key];
116 var prefix = item.registry === cur ? '* ' : ' ';
117 info.push(prefix + key + line(key, 12) + item.registry);
118 });
119
120 info.push('');
121 printMsg(info);
122 });
123}
124
125function showCurrent() {
126 getCurrentRegistry(function(cur) {
127 var allRegistries = getAllRegistry();
128 Object.keys(allRegistries).forEach(function(key) {
129 var item = allRegistries[key];
130 if (item.registry === cur) {
131 printMsg([key]);
132 return;
133 }
134 });
135 });
136}
137
138function config(attrArray, registry, index = 0) {
139 return new Promise((resolve, reject) => {
140 const attr = attrArray[index];
141 const command = registry.hasOwnProperty(attr) ? ['set', attr, String(registry[attr])] : ['delete', attr];
142 npm.commands.config(command, function(err, data) {
143 return err ? reject(err) : resolve(index + 1);
144 });
145 }).then(next => {
146 if (next < attrArray.length) {
147 return config(attrArray, registry, next);
148 } else {
149 return Promise.resolve();
150 }
151 });
152}
153
154function onUse(name) {
155 var allRegistries = getAllRegistry();
156 if (allRegistries.hasOwnProperty(name)) {
157 var registry = allRegistries[name];
158 npm.load(function(err) {
159 if (err) return exit(err);
160 const attrs = [].concat(REGISTRY_ATTRS);
161 for (let attr in registry) {
162 if (!REGISTRY_ATTRS.includes(attr) && !IGNORED_ATTRS.includes(attr)) {
163 attrs.push(attr);
164 }
165 }
166 config(attrs, registry).then(() => {
167 console.log(' ');
168 var newR = npm.config.get('registry');
169 var customRegistries = getCustomRegistry();
170 Object.keys(customRegistries).forEach(key => {
171 delete customRegistries[key][FIELD_IS_CURRENT];
172 });
173 if (customRegistries.hasOwnProperty(name) && customRegistries[name].registry === registry.registry) {
174 registry[FIELD_IS_CURRENT] = true;
175 customRegistries[name] = registry;
176 }
177 setCustomRegistry(customRegistries);
178 printMsg(['', ' Registry has been set to: ' + newR, '']);
179 }).catch(err => {
180 exit(err);
181 });
182 });
183 } else {
184 printMsg(['', ' Not find registry: ' + name, '']);
185 }
186}
187
188function onDel(name) {
189 var customRegistries = getCustomRegistry();
190 if (!customRegistries.hasOwnProperty(name)) return;
191 getCurrentRegistry(function(cur) {
192 if (cur === customRegistries[name].registry) {
193 onUse('npm');
194 }
195 delete customRegistries[name];
196 setCustomRegistry(customRegistries, function(err) {
197 if (err) return exit(err);
198 printMsg(['', ' delete registry ' + name + ' success', '']);
199 });
200 });
201}
202
203function onAdd(name, url, home) {
204 var customRegistries = getCustomRegistry();
205 if (customRegistries.hasOwnProperty(name)) return;
206 var config = customRegistries[name] = {};
207 if (url[url.length - 1] !== '/') url += '/'; // ensure url end with /
208 config.registry = url;
209 if (home) {
210 config.home = home;
211 }
212 setCustomRegistry(customRegistries, function(err) {
213 if (err) return exit(err);
214 printMsg(['', ' add registry ' + name + ' success', '']);
215 });
216}
217
218function onSetAuth(registry, value, cmd) {
219 const customRegistries = getCustomRegistry();
220 if (!customRegistries.hasOwnProperty(registry)) return;
221 const config = customRegistries[registry];
222 const attrs = [FIELD_AUTH];
223 if (value) {
224 config[FIELD_AUTH] = value;
225 } else if (cmd.username && cmd.password) {
226 config[FIELD_AUTH] = new Buffer(`${cmd.username}:${cmd.password}`).toString('base64');
227 } else {
228 return exit(new Error('your username & password or auth value is required'));
229 }
230 if (cmd[FIELD_ALWAYS_AUTH]) {
231 config[FIELD_ALWAYS_AUTH] = true;
232 attrs.push(FIELD_ALWAYS_AUTH);
233 }
234 new Promise(resolve => {
235 config[FIELD_IS_CURRENT] ? resolve(config(attrs, config)) : resolve();
236 }).then(() => {
237 customRegistries[registry] = config;
238 setCustomRegistry(customRegistries, function(err) {
239 if (err) return exit(err);
240 printMsg(['', ' set authorize info to registry ' + registry + ' success', '']);
241 });
242 }).catch(exit);
243}
244
245function onSetEmail(registry, value) {
246 const customRegistries = getCustomRegistry();
247 if (!customRegistries.hasOwnProperty(registry)) return;
248 const config = customRegistries[registry];
249 config.email = value;
250 new Promise(resolve => {
251 config[FIELD_IS_CURRENT] ? resolve(config(['email'], config)) : resolve();
252 }).then(() => {
253 customRegistries[registry] = config;
254 setCustomRegistry(customRegistries, function(err) {
255 if (err) return exit(err);
256 printMsg(['', ' set email to registry ' + registry + ' success', '']);
257 });
258 }).catch(exit);
259}
260
261function onSetRepository(registry, value) {
262 var customRegistries = getCustomRegistry();
263 if (!customRegistries.hasOwnProperty(registry)) return;
264 var config = customRegistries[registry];
265 config[FIELD_REPOSITORY] = value;
266 new Promise(resolve => {
267 config[FIELD_IS_CURRENT] ? resolve(config([FIELD_REPOSITORY], config)) : resolve();
268 }).then(() => {
269 setCustomRegistry(customRegistries, function(err) {
270 if (err) return exit(err);
271 printMsg(['', ` set ${FIELD_REPOSITORY} to registry [${registry}] success`, '']);
272 });
273 }).catch(exit);
274}
275
276function onHome(name, browser) {
277 var allRegistries = getAllRegistry();
278 var home = allRegistries[name] && allRegistries[name].home;
279 if (home) {
280 var args = [home];
281 if (browser) args.push(browser);
282 open.apply(null, args);
283 }
284}
285
286function onPublish(tarballOrFolder, cmd) {
287 getCurrentRegistry(registry => {
288 const customRegistries = getCustomRegistry();
289 let currentRegistry;
290 // find current using custom registry
291 Object.keys(customRegistries).forEach(function(key) {
292 const item = customRegistries[key];
293 if (item.registry === registry && item['is-current']) {
294 currentRegistry = item;
295 currentRegistry.name = key;
296 }
297 });
298 const attrs = ['registry'];
299 let command = '> npm publish';
300 const optionData = {};
301 cmd.options.forEach(option => {
302 const opt = option.long.substring(2);
303 const optionValue = cmd[opt];
304 if (optionValue) {
305 optionData[opt] = cmd[opt];
306 attrs.push(opt);
307 }
308 });
309 new Promise((resolve, reject) => {
310 if (currentRegistry) {
311 if (currentRegistry[FIELD_REPOSITORY]) {
312 printMsg(['',
313 ' current registry is a custom registry, publish to custom repository.',
314 ''
315 ]);
316 optionData.registry = currentRegistry[FIELD_REPOSITORY];
317 command += ` --registry ${currentRegistry[FIELD_REPOSITORY]}`;
318 Object.keys(optionData).forEach((key) => {
319 command += ` --${key} ${optionData[key]}`;
320 });
321 printMsg([command]);
322 resolve(config(attrs, optionData));
323 } else {
324 reject(new Error(` current using registry [${currentRegistry.name}] has no ${FIELD_REPOSITORY} field, can't execute publish.`));
325 }
326 } else {
327 printMsg(['',
328 ' current using registry is not a custom registry, will publish to npm official repository.',
329 ''
330 ]);
331 optionData.registry = registries.npm.registry;
332 // find current using registry
333 Object.keys(registries).forEach(function(key) {
334 const item = registries[key];
335 if (item.registry === registry) {
336 currentRegistry = item;
337 currentRegistry.name = key;
338 }
339 });
340 printMsg([command]);
341 resolve(config(attrs, optionData));
342 }
343 }).then(() => {
344 const callback = (err) => {
345 config(attrs, currentRegistry).then(() => {
346 err && exit(err);
347 printMsg(['', ` published to registry ${currentRegistry[FIELD_REPOSITORY]} successfully.`, '']);
348 }).catch(exit);
349 };
350 try {
351 tarballOrFolder ? npm.publish(tarballOrFolder, callback) : npm.publish(callback);
352 } catch (e) {
353 callback(err);
354 }
355 }).catch(err => {
356 printErr(err);
357 });
358 });
359}
360
361function onTest(registry) {
362 var allRegistries = getAllRegistry();
363
364 var toTest;
365
366 if (registry) {
367 if (!allRegistries.hasOwnProperty(registry)) {
368 return;
369 }
370 toTest = only(allRegistries, registry);
371 } else {
372 toTest = allRegistries;
373 }
374
375 async.map(Object.keys(toTest), function(name, cbk) {
376 var registry = toTest[name];
377 var start = +new Date();
378 request(registry.registry + 'pedding', function(error) {
379 cbk(null, {
380 name: name,
381 registry: registry.registry,
382 time: (+new Date() - start),
383 error: error ? true : false
384 });
385 });
386 }, function(err, results) {
387 getCurrentRegistry(function(cur) {
388 var msg = [''];
389 results.forEach(function(result) {
390 var prefix = result.registry === cur ? '* ' : ' ';
391 var suffix = result.error ? 'Fetch Error' : result.time + 'ms';
392 msg.push(prefix + result.name + line(result.name, 8) + suffix);
393 });
394 msg.push('');
395 printMsg(msg);
396 });
397 });
398}
399
400
401
402/*//////////////// helper methods /////////////////*/
403
404/*
405 * get current registry
406 */
407function getCurrentRegistry(cbk) {
408 npm.load(function(err, conf) {
409 if (err) return exit(err);
410 cbk(npm.config.get('registry'));
411 });
412}
413
414function getCustomRegistry() {
415 return fs.existsSync(NRMRC) ? ini.parse(fs.readFileSync(NRMRC, 'utf-8')) : {};
416}
417
418function setCustomRegistry(config, cbk) {
419 echo(ini.stringify(config), '>', NRMRC, cbk);
420}
421
422function getAllRegistry() {
423 return extend({}, registries, getCustomRegistry());
424}
425
426function printErr(err) {
427 console.error('an error occured: ' + err);
428}
429
430function printMsg(infos) {
431 infos.forEach(function(info) {
432 console.log(info);
433 });
434}
435
436/*
437 * print message & exit
438 */
439function exit(err) {
440 printErr(err);
441 process.exit(1);
442}
443
444function line(str, len) {
445 var line = new Array(Math.max(1, len - str.length)).join('-');
446 return ' ' + line + ' ';
447}