nry 0.0.0 → 0.0.1

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.
Files changed (5) hide show
  1. package/LICENSE +25 -0
  2. package/README.md +6 -2
  3. package/index.mjs +515 -0
  4. package/package.json +58 -20
  5. package/index.js +0 -24
package/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ # MIT License
2
+
3
+ Copyright (c) <2025> <🥜>
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
+
17
+ # MIT 许可证
18
+
19
+ 版权所有 (c) [2025] [花生亻]
20
+
21
+ 特此免费授予任何获得本软件及相关文档文件(以下简称“软件”)副本的人不受限制地处置该软件的权利,包括不受限制地使用、复制、修改、合并、发布、分发、再许可和/或出售该软件副本的权利,并允许向其提供该软件的人这样做,但须遵守以下条件:
22
+
23
+ 上述版权声明和本许可声明应包含在所有副本或软件的重要部分中。
24
+
25
+ 软件按“原样”提供,不附带任何形式的明示或暗示的保证,包括但不限于适销性、特定用途适用性和不侵权的保证。在任何情况下,作者或版权持有人均不对因合同、侵权或其他方式引起的任何索赔、损害或其他责任负责,无论是在与软件或软件的使用或其他交易有关的任何诉讼中。
package/README.md CHANGED
@@ -1,3 +1,7 @@
1
- # 这个名被我占用了
1
+ # nry
2
2
 
3
- 但是你如果有需要,可以 [邮我转让](mailto:earthnut.dev@outlook.com)
3
+ [![version](<https://img.shields.io/npm/v/nry.svg?logo=npm&logoColor=rgb(0,0,0)&label=版本号&labelColor=rgb(73,73,228)&color=rgb(0,0,0)>)](https://www.npmjs.com/package/nry) [![issues 提交](<https://img.shields.io/badge/issues-提交-rgb(255,0,63)?logo=github>)](https://github.com/earthnutDev/nry/issues)
4
+
5
+ ## 文档地址
6
+
7
+ 参看 [https://earthnut.dev/nry](https://earthnut.dev/nry)
package/index.mjs ADDED
@@ -0,0 +1,515 @@
1
+ #!/usr/bin/env node
2
+ import { Dog } from '@qqi/dev-log';
3
+ import { isFalse, isArray, isString, isBusinessEmptyString, isUndefined } from 'a-type-of-js';
4
+ import { QQI } from 'qqi';
5
+ import { escapeRegExp } from 'a-js-tools';
6
+ import { brightRedPen, brightCyanPen, greenPen, cyanPen, magentaPen, brightBlackPen, bluePen } from 'color-pen';
7
+ import { Command } from 'a-command';
8
+ import { typewrite, cursorShow, runOtherCode, _p } from 'a-node-tools';
9
+ import { Table } from 'colored-table';
10
+
11
+ const dog = new Dog({
12
+ name: 'nry',
13
+ type: false,
14
+ });
15
+ isFalse(dog.type);
16
+
17
+ const qqi = new QQI('nry');
18
+
19
+ /**
20
+ * 创建原始的数据
21
+ *
22
+ * @param [reset=false] 是否为重置,该情况下直接返回原始值进行覆盖
23
+ */
24
+ function getOriginData(reset = false) {
25
+ const originData = [
26
+ {
27
+ value: 'https://registry.npmjs.org',
28
+ label: '官方',
29
+ tip: 'https://registry.npmjs.org',
30
+ },
31
+ {
32
+ value: 'https://registry.npmmirror.com',
33
+ label: '淘宝',
34
+ tip: 'https://registry.npmmirror.com',
35
+ },
36
+ {
37
+ value: 'https://mirrors.tencent.com/npm',
38
+ label: '腾讯',
39
+ tip: 'https://mirrors.tencent.com/npm',
40
+ },
41
+ {
42
+ value: 'https://npmreg.proxy.ustclug.org',
43
+ label: '中科大',
44
+ tip: 'https://npmreg.proxy.ustclug.org',
45
+ },
46
+ {
47
+ value: 'https://registry.yarnpkg.com',
48
+ label: 'yarn',
49
+ tip: 'https://registry.yarnpkg.com',
50
+ },
51
+ ];
52
+ // 重置默认返回原始值
53
+ if (reset)
54
+ return originData;
55
+ // 读写受限返回原始值
56
+ if (!qqi.available)
57
+ return originData;
58
+ const localConfig = qqi.read('config');
59
+ if (isArray(localConfig) &&
60
+ localConfig.every(e => isString(e.value) &&
61
+ !isBusinessEmptyString(e.label) &&
62
+ isString(e.value) &&
63
+ !isBusinessEmptyString(e.value))) {
64
+ return localConfig;
65
+ }
66
+ // 默认返回原始值
67
+ return originData;
68
+ }
69
+
70
+ const command = new Command('nry');
71
+ command
72
+ .bind([
73
+ 'add <a> (用于添加一个自定义的 npm 源)',
74
+ 'edit <ed> (用于编辑源) ',
75
+ 'delete <del> (用于移除某个或多个自定义的项)',
76
+ 'list <ls> (用于展示当前的所有的源)',
77
+ 'manage <mg> (用于管理当前源在列表中的显隐)',
78
+ `reset <rs> (重制当前的源,将${brightRedPen `永久移除自定义源`}、${brightCyanPen `恢复已更改的源`})`,
79
+ ])
80
+ .run()
81
+ .isEnd(true);
82
+
83
+ /** 退出程序 */
84
+ async function exitProgram(str = '好的,即将退出') {
85
+ if (!isBusinessEmptyString(str))
86
+ await typewrite(str);
87
+ cursorShow();
88
+ return command.end();
89
+ }
90
+
91
+ /**
92
+ *
93
+ */
94
+ async function getValue(originData) {
95
+ const valueVerify = [
96
+ {
97
+ reg: /\s+/g,
98
+ info: '不允许出现空格',
99
+ inverse: true,
100
+ },
101
+ ];
102
+ originData.forEach(e => {
103
+ valueVerify.push({
104
+ reg: new RegExp(`^${escapeRegExp(e.value)}$`, 'mg'),
105
+ info: `${greenPen(e.value)}(${cyanPen(e.label)}) 已存在`,
106
+ inverse: true,
107
+ });
108
+ });
109
+ const value = await command.question({
110
+ text: '请输入自定义的 npm 的源地址:',
111
+ tip: 'https://registry.npmjs.org',
112
+ minLen: 5,
113
+ maxLen: 120,
114
+ verify: valueVerify,
115
+ required: false,
116
+ });
117
+ dog('用户输入的地址', value);
118
+ if (isUndefined(value))
119
+ return await exitProgram();
120
+ return value;
121
+ }
122
+
123
+ /**
124
+ *
125
+ */
126
+ function localAdd(item) {
127
+ // 读写受限
128
+ if (!qqi.available)
129
+ return false;
130
+ /** 新添加项 */
131
+ const writeItem = {
132
+ value: item.value,
133
+ label: item.label.toString(),
134
+ tip: item.value,
135
+ disable: false,
136
+ };
137
+ /// 本地原有数据
138
+ const localConfig = qqi.read('config');
139
+ /// 新添加的数据
140
+ localConfig.push(writeItem);
141
+ return qqi.write('config', localConfig);
142
+ }
143
+
144
+ /** 获取 label */
145
+ async function getLabel(originData) {
146
+ const valueVerify = [
147
+ {
148
+ reg: /\s+/g,
149
+ info: '不允许出现空格',
150
+ inverse: true,
151
+ },
152
+ ];
153
+ originData.forEach(e => {
154
+ valueVerify.push({
155
+ reg: new RegExp(`^${escapeRegExp(e.label)}$`, 'mg'),
156
+ info: `${greenPen(e.label)}(${cyanPen(e.value)}) 已存在`,
157
+ inverse: true,
158
+ });
159
+ });
160
+ const value = await command.question({
161
+ text: '请输入自定义的 npm 的别名:',
162
+ tip: '任意别名',
163
+ minLen: 1,
164
+ maxLen: 120,
165
+ verify: valueVerify,
166
+ defaultValue: '',
167
+ required: false,
168
+ });
169
+ dog('用户输入的别名为', value);
170
+ if (isUndefined(value))
171
+ return await exitProgram();
172
+ return value;
173
+ }
174
+
175
+ /** 获取当前配置的 本地的 registry */
176
+ async function getCurrentRegistry() {
177
+ const result = await runOtherCode('npm config get registry');
178
+ dog('获取当前的本地配置为', result);
179
+ if (result.success)
180
+ return result.data.replace(/\s/gm, '');
181
+ return undefined;
182
+ }
183
+
184
+ /** 展示项 */
185
+ async function list() {
186
+ /** 当前数据 */
187
+ const data = getOriginData();
188
+ const current = await getCurrentRegistry();
189
+ const table = new Table();
190
+ table.setHeader(['npm registry', '别名', '当前可见']);
191
+ data.forEach(e => table.addRow([
192
+ (current === e.value ? magentaPen `* ` : ' ').concat(e.value),
193
+ (current === e.value ? magentaPen `* ` : ' ').concat(greenPen(e.label)),
194
+ e.disable ? '❌' : '✅',
195
+ ]));
196
+ table(); // 渲染
197
+ _p(brightBlackPen `注:当前可见状态仅影响 npx nry 选择项`);
198
+ }
199
+
200
+ /** 添加新的项 */
201
+ async function addItem() {
202
+ if (!qqi.available)
203
+ return await exitProgram('读写受限,正在退出');
204
+ const originData = getOriginData();
205
+ const value = await getValue(originData);
206
+ const label = await getLabel(originData);
207
+ if (localAdd({
208
+ value,
209
+ label,
210
+ })) {
211
+ _p(brightBlackPen `当前的 npm registry 列表为`);
212
+ await list();
213
+ const tip = ['退出', '继续添加'];
214
+ const result = await command.question({
215
+ text: '添加完成,是否持续添加',
216
+ tip,
217
+ });
218
+ if (isUndefined(result) || result === tip[0])
219
+ return await exitProgram('');
220
+ return await addItem();
221
+ }
222
+ return await exitProgram(`写入${brightRedPen `失败`},原因未知`);
223
+ }
224
+
225
+ /**
226
+ * 获得要编辑的项
227
+ *
228
+ * @param [info='请选择想使用的新的 npm registry'] 展示的信息
229
+ * @param [filter=true] 是否过滤不可用值
230
+ * @param [allowCurrent=true] 是否允许当前值被选择。默认允许
231
+ * */
232
+ async function getTarget(info = '请选择想使用的新的 npm registry', filter = true, allowCurrent = true) {
233
+ /** 是否有当前值 */
234
+ let hasCurrentValue = false;
235
+ /** 数据 */
236
+ const originData = getOriginData();
237
+ const currentValue = await getCurrentRegistry();
238
+ if (!isUndefined(currentValue)) {
239
+ for (const e of originData) {
240
+ if (e.value === currentValue) {
241
+ e.label = e.label.toString() + `(${magentaPen `当前使用`})`;
242
+ e.disable = !allowCurrent;
243
+ hasCurrentValue = true;
244
+ break;
245
+ }
246
+ }
247
+ if (isFalse(hasCurrentValue)) {
248
+ const newItem = {
249
+ value: currentValue,
250
+ label: '当前使用值',
251
+ disable: !allowCurrent,
252
+ };
253
+ originData.push(newItem);
254
+ // 当前本地可写
255
+ if (qqi.available) {
256
+ localAdd({ value: currentValue, label: '曾用值' });
257
+ }
258
+ }
259
+ }
260
+ dog('处理完的数据', originData);
261
+ const data = filter
262
+ ? originData.filter(e => !e.disable || (e.disable && e.value === currentValue))
263
+ : originData.map(e => {
264
+ if (e.value === currentValue) {
265
+ e.disable = !allowCurrent;
266
+ }
267
+ else {
268
+ e.disable = false;
269
+ }
270
+ return e;
271
+ });
272
+ /** 选择交互 */
273
+ const result = await command.selection({
274
+ data,
275
+ info,
276
+ }, 'number');
277
+ dog('用户选择的项', result);
278
+ if (isUndefined(result))
279
+ return await exitProgram();
280
+ dog('当前用户选择为', data[result]);
281
+ return data[result];
282
+ }
283
+
284
+ /** 设置新的 */
285
+ async function setNewRegistry(item) {
286
+ const result = await runOtherCode(`npm config set registry ${item.value}`);
287
+ if (!result.success)
288
+ await exitProgram();
289
+ _p(`已将 npm registry 更改为 ${greenPen(item.value)}(${bluePen(item.label)})`);
290
+ }
291
+
292
+ /** 挑选一个域 */
293
+ async function choose() {
294
+ /** 获取靶 */
295
+ const target = await getTarget();
296
+ await setNewRegistry(target);
297
+ }
298
+
299
+ /** 用户启动命令参数 */
300
+ const commandParameters = {
301
+ add: false,
302
+ edit: false,
303
+ delete: false,
304
+ manage: false,
305
+ list: false,
306
+ noMatch: false,
307
+ reset: false,
308
+ };
309
+
310
+ /**
311
+ * 移除项
312
+ */
313
+ async function delItem() {
314
+ if (!qqi.available)
315
+ return await exitProgram('当前读写受限,即将退出程序');
316
+ const target = await getTarget('请选择要删除的项', false, false);
317
+ const { value, label } = target;
318
+ /// 获取本地的值,防止意外覆盖
319
+ const originData = getOriginData();
320
+ for (const i in originData) {
321
+ const index = Number(i);
322
+ const ele = originData[index];
323
+ if (ele.value === value && ele.label === label) {
324
+ originData.splice(index, 1);
325
+ break;
326
+ }
327
+ }
328
+ const result = qqi.write('config', originData);
329
+ if (result) {
330
+ _p('删除项后的列表为:');
331
+ await list();
332
+ const tip = ['退出', '继续删除'];
333
+ const result = await command.question({
334
+ text: '是否继续删除其他项',
335
+ tip,
336
+ });
337
+ if (isUndefined(result) || result === tip[0])
338
+ return await exitProgram('');
339
+ return await delItem();
340
+ }
341
+ }
342
+
343
+ /** 更改值 */
344
+ async function editValue(target) {
345
+ const result = await command.question({
346
+ text: '请更改为新的 npm registry 值',
347
+ tip: target.value,
348
+ defaultValue: target.value,
349
+ required: false,
350
+ });
351
+ dog('新值为', result);
352
+ /** 没有更改 */
353
+ if (isUndefined(result))
354
+ return;
355
+ target.value = target.tip = result;
356
+ }
357
+
358
+ /** 更改当前的 label 值 */
359
+ async function editLabel(target) {
360
+ const result = await command.question({
361
+ text: '请更改为新的 npm registry 别名',
362
+ tip: target.label,
363
+ defaultValue: target.label.toString(),
364
+ required: false,
365
+ });
366
+ dog('新值为', result);
367
+ /** 没有更改 */
368
+ if (isUndefined(result))
369
+ return;
370
+ target.label = result;
371
+ }
372
+
373
+ /** 编辑项 */
374
+ async function editItem() {
375
+ if (!qqi.available)
376
+ return await exitProgram('当前读写权限不足,即将退出程序');
377
+ const target = await getTarget('请选择你想要修改的项', false);
378
+ const { value: originValue, label: originLabel } = target;
379
+ dog('获取用户的选择', target);
380
+ dog('原始的值', originValue);
381
+ dog('原始的标签', originLabel);
382
+ await editValue(target);
383
+ await editLabel(target);
384
+ const { value, label } = target;
385
+ dog('更改后的值', value);
386
+ dog('更改的标签', label);
387
+ /// 获取本地的值,防止意外覆盖
388
+ const originData = getOriginData();
389
+ for (const ele of originData) {
390
+ if (ele.value === originValue && ele.label === originLabel) {
391
+ ele.value = value;
392
+ ele.label = label.toString();
393
+ break;
394
+ }
395
+ }
396
+ const result = qqi.write('config', originData);
397
+ if (result) {
398
+ _p('更改后的列表为:');
399
+ await list();
400
+ const tip = ['退出', '继续修改'];
401
+ const result = await command.question({
402
+ text: '是否继续修改其他项',
403
+ tip,
404
+ });
405
+ if (isUndefined(result) || result === tip[0])
406
+ return await exitProgram('');
407
+ return await editItem();
408
+ }
409
+ }
410
+
411
+ /** 当前可见性的更改 */
412
+ async function manageVisible() {
413
+ if (!qqi.available)
414
+ return await exitProgram('当前读写受限,即将退出程序。');
415
+ const originData = getOriginData();
416
+ /** 当前设置项 */
417
+ const currentValue = await getCurrentRegistry();
418
+ const result = await command.selection({
419
+ data: originData.map(e => ({
420
+ ...e,
421
+ checked: !e.disable,
422
+ disable: e.value === currentValue,
423
+ })),
424
+ kind: 'check',
425
+ }, 'number');
426
+ dog('交互结果', result);
427
+ if (isUndefined(result))
428
+ return await exitProgram('好的,将退出状态显隐管理');
429
+ originData.forEach((e, i) => {
430
+ e.disable = !result.includes(i);
431
+ });
432
+ dog('保存前的数据', originData);
433
+ qqi.write('config', originData);
434
+ }
435
+
436
+ /** 解析参数 */
437
+ function parseArg() {
438
+ /** 用户启动参数 */
439
+ const args = command.args;
440
+ /** 用户启用参数的 map 形式 */
441
+ const argsMap = args.$map;
442
+ dog('源解析值', args);
443
+ dog('源解析值没有匹配项', args.$isVoid);
444
+ [
445
+ 'add',
446
+ 'delete',
447
+ 'edit',
448
+ 'list',
449
+ 'manage',
450
+ 'reset',
451
+ ].forEach(e => (commandParameters[e] = isFalse(isUndefined(argsMap[e]))));
452
+ // 当没有匹配到项
453
+ commandParameters.noMatch = args.$isVoid;
454
+ dog('解析完的值', commandParameters);
455
+ }
456
+
457
+ /** 重制项 */
458
+ async function reset() {
459
+ if (!qqi.available)
460
+ return await exitProgram('当前读写权限受限,正在退出程序');
461
+ const tip = ['退出', '重置'];
462
+ const result = await command.question({
463
+ text: '请确认是否执行覆盖原数据,该操作无法复原',
464
+ tip,
465
+ });
466
+ if (isUndefined(result) || result === tip[0])
467
+ return await exitProgram('好的,这就退出重置');
468
+ const writeResponse = qqi.write('config', getOriginData(true));
469
+ if (writeResponse)
470
+ return await exitProgram('写入成功,退出程序');
471
+ return await exitProgram('重置未能完成,原因未知');
472
+ }
473
+
474
+ /** 主程序 */
475
+ async function main() {
476
+ parseArg();
477
+ dog('执行');
478
+ if (commandParameters.noMatch) {
479
+ dog('当前没有匹配值');
480
+ return await choose();
481
+ }
482
+ else if (commandParameters.reset) {
483
+ dog('重置项');
484
+ return await reset();
485
+ }
486
+ else if (commandParameters.list) {
487
+ dog('当前为展示项');
488
+ return await list();
489
+ }
490
+ else if (commandParameters.manage) {
491
+ dog('当前执行状态更改');
492
+ return await manageVisible();
493
+ }
494
+ else if (commandParameters.add) {
495
+ dog('添加新的项');
496
+ return await addItem();
497
+ }
498
+ else if (commandParameters.edit) {
499
+ dog('编辑项');
500
+ return await editItem();
501
+ }
502
+ else if (commandParameters.delete) {
503
+ dog('删除项');
504
+ return await delItem();
505
+ }
506
+ }
507
+
508
+ (async () => {
509
+ try {
510
+ await main();
511
+ }
512
+ catch (error) {
513
+ dog.error('系统级捕获 错误', error);
514
+ }
515
+ })();
package/package.json CHANGED
@@ -1,31 +1,69 @@
1
1
  {
2
+ "main": "index.cjs",
3
+ "module": "index.mjs",
4
+ "types": "index.d.ts",
2
5
  "name": "nry",
3
- "version": "0.0.0",
6
+ "version": "0.0.1",
4
7
  "type": "module",
5
- "main": "index.js",
6
- "author": "lmssee <lmssee@outlook.com> (https://lmssee.com)",
7
- "homepage": "https://earthnut.dev/vue",
8
- "license": "ISC",
9
- "description": "但是你如果有需要,可以邮我转让 <earthnut.dev@outlook.com>",
8
+ "description": "",
9
+ "license": "MIT",
10
+ "dependencies": {
11
+ "@qqi/dev-log": "^1.0.4",
12
+ "a-command": "^2.3.8",
13
+ "a-js-tools": "^1.0.0",
14
+ "a-node-tools": "^4.2.7",
15
+ "a-type-of-js": "^1.0.1",
16
+ "color-pen": "^2.0.10",
17
+ "colored-table": "^0.0.2",
18
+ "qqi": "^0.0.4"
19
+ },
20
+ "publishConfig": {
21
+ "access": "public",
22
+ "registry": "https://registry.npmjs.org/"
23
+ },
10
24
  "files": [
11
- "index.js"
25
+ "index.d.ts",
26
+ "index.mjs",
27
+ "index.cjs",
28
+ "src"
12
29
  ],
13
- "bugs": {
14
- "url": "https://github.com/lmssee/npm-earthnut-vue/issues"
30
+ "exports": {
31
+ ".": {
32
+ "import": {
33
+ "default": "./index.mjs",
34
+ "types": "./index.d.ts"
35
+ },
36
+ "require": {
37
+ "default": "./index.cjs",
38
+ "types": "./index.d.ts"
39
+ }
40
+ }
15
41
  },
16
- "keywords": [
17
- "earthnut"
18
- ],
19
42
  "repository": {
20
43
  "type": "git",
21
- "url": "git+https://github.com/lmssee/npm-earthnut-vue.git"
44
+ "url": "git+https://github.com/earthnutDev/nry.git"
22
45
  },
23
- "publishConfig": {
24
- "access": "public",
25
- "registry": "https://registry.npmjs.org"
46
+ "author": {
47
+ "name": "🥜",
48
+ "email": "earthnut.dev@outlook.com",
49
+ "url": "https://earthnut.dev"
26
50
  },
27
- "dependencies": {
28
- "a-node-tools": "^0.1.0",
29
- "color-pen": "^0.1.0"
51
+ "browserslist": [
52
+ "node>=18.0.0"
53
+ ],
54
+ "engines": {
55
+ "node": ">=18.0.0"
56
+ },
57
+ "keywords": [
58
+ "nry",
59
+ "earthnut"
60
+ ],
61
+ "homepage": "https://earthnut.dev",
62
+ "bugs": {
63
+ "url": "https://github.com/earthnutDev/nry/issues",
64
+ "email": "earthnut.dev@outlook.com"
65
+ },
66
+ "bin": {
67
+ "nry": "./bin.mjs"
30
68
  }
31
- }
69
+ }
package/index.js DELETED
@@ -1,24 +0,0 @@
1
- import { readFileToJsonSync } from "a-node-tools";
2
- import { default as pen } from "color-pen";
3
- import { writeFileSync } from "node:fs";
4
-
5
- const fileInfo = readFileToJsonSync("./info.json");
6
-
7
- const npmLists = fileInfo.objects
8
- .map((i) => i.package.name)
9
- .filter((name) => name.length < 4)
10
- .sort()
11
- .reduce((accumulator, currentName, i) => {
12
- // const pkgName = pen.hex(i % 255)(currentName);
13
- // if (i % 3 === 2) {
14
- // accumulator += pen(`${currentName.padEnd(36, " ")}\n${" ".repeat(4)}`);
15
- // } else {
16
- // accumulator += `${currentName.padEnd(36, " ")}`;
17
- // }
18
- accumulator += `${currentName.padEnd(3, " ")}\n`;
19
- return accumulator;
20
- }, " ".repeat(4));
21
-
22
- console.log(npmLists);
23
-
24
- writeFileSync("./npm-lists.txt", npmLists);