@lppx/nlearn 1.1.1 → 1.1.3

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.
@@ -1,10 +1 @@
1
- #!/usr/bin/env node
2
1
  "use strict";
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- const commander_1 = require("commander");
5
- const program = new commander_1.Command();
6
- program
7
- .name('nlearn')
8
- .description('nodejs 生态学习工具')
9
- .version('1.0.0');
10
- program.parse(process.argv);
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const commander_1 = require("commander");
5
+ const program = new commander_1.Command();
6
+ program
7
+ .name('nlearn')
8
+ .description('nodejs 生态学习工具')
9
+ .version('1.0.0');
10
+ program.parse(process.argv);
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ /**
3
+ * Prompts 基础概念
4
+ * ================
5
+ * prompts 是一个轻量级、美观且用户友好的交互式命令行提示库
6
+ * 基于 Promise 和 async/await,无回调地狱
7
+ * 适用于 Node.js 14 及以上版本
8
+ */
9
+ var __importDefault = (this && this.__importDefault) || function (mod) {
10
+ return (mod && mod.__esModule) ? mod : { "default": mod };
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ const prompts_1 = __importDefault(require("prompts"));
14
+ // #region 示例1: 单个提示
15
+ async function singlePrompt() {
16
+ console.log('\n=== 单个提示示例 ===');
17
+ const response = await (0, prompts_1.default)({
18
+ type: 'text',
19
+ name: 'username',
20
+ message: '请输入你的用户名:'
21
+ });
22
+ console.log('你输入的用户名是:', response.username);
23
+ }
24
+ // #endregion
25
+ // #region 示例2: 多个提示链
26
+ async function promptChain() {
27
+ console.log('\n=== 多个提示链示例 ===');
28
+ const questions = [
29
+ {
30
+ type: 'text',
31
+ name: 'name',
32
+ message: '你叫什么名字?'
33
+ },
34
+ {
35
+ type: 'number',
36
+ name: 'age',
37
+ message: '你多大了?'
38
+ },
39
+ {
40
+ type: 'text',
41
+ name: 'hobby',
42
+ message: '你的爱好是什么?',
43
+ initial: '编程' // 默认值
44
+ }
45
+ ];
46
+ const response = await (0, prompts_1.default)(questions);
47
+ console.log('\n收集到的信息:');
48
+ console.log('姓名:', response.name);
49
+ console.log('年龄:', response.age);
50
+ console.log('爱好:', response.hobby);
51
+ }
52
+ // #endregion
53
+ // #region 示例3: 处理取消操作
54
+ async function handleCancel() {
55
+ console.log('\n=== 处理取消操作示例 ===');
56
+ console.log('提示: 按 Ctrl+C 或 ESC 可以取消输入\n');
57
+ const onCancel = (prompt) => {
58
+ console.log('\n用户取消了输入');
59
+ return true; // 返回 true 继续执行,false 则抛出错误
60
+ };
61
+ const response = await (0, prompts_1.default)({
62
+ type: 'text',
63
+ name: 'email',
64
+ message: '请输入你的邮箱:'
65
+ }, { onCancel });
66
+ if (response.email) {
67
+ console.log('你的邮箱是:', response.email);
68
+ }
69
+ else {
70
+ console.log('没有收集到邮箱信息');
71
+ }
72
+ }
73
+ // #endregion
74
+ // #region 示例4: 提交回调
75
+ async function onSubmitCallback() {
76
+ console.log('\n=== 提交回调示例 ===');
77
+ const questions = [
78
+ {
79
+ type: 'text',
80
+ name: 'firstName',
81
+ message: '名字:'
82
+ },
83
+ {
84
+ type: 'text',
85
+ name: 'lastName',
86
+ message: '姓氏:'
87
+ }
88
+ ];
89
+ const onSubmit = (prompt, answer, answers) => {
90
+ console.log(`✓ 已收集 ${prompt.name}: ${answer}`);
91
+ };
92
+ const response = await (0, prompts_1.default)(questions, { onSubmit });
93
+ console.log('\n完整信息:', response);
94
+ }
95
+ // #endregion
96
+ // #region 示例5: 初始值的使用
97
+ async function initialValues() {
98
+ console.log('\n=== 初始值示例 ===');
99
+ console.log('提示: 按 Tab 键可以自动补全到初始值\n');
100
+ const response = await (0, prompts_1.default)([
101
+ {
102
+ type: 'text',
103
+ name: 'website',
104
+ message: '你的网站:',
105
+ initial: 'https://example.com'
106
+ },
107
+ {
108
+ type: 'number',
109
+ name: 'port',
110
+ message: '服务器端口:',
111
+ initial: 3000
112
+ }
113
+ ]);
114
+ console.log('\n配置信息:');
115
+ console.log('网站:', response.website);
116
+ console.log('端口:', response.port);
117
+ }
118
+ // #endregion
119
+ if (require.main === module) {
120
+ const exampleNumber = process.argv[2];
121
+ const examples = {
122
+ '1': singlePrompt,
123
+ '2': promptChain,
124
+ '3': handleCancel,
125
+ '4': onSubmitCallback,
126
+ '5': initialValues
127
+ };
128
+ if (exampleNumber && examples[exampleNumber]) {
129
+ examples[exampleNumber]().catch(console.error);
130
+ }
131
+ else {
132
+ console.log('Prompts 基础概念示例');
133
+ console.log('===================\n');
134
+ console.log('使用方法: ts-node 01-基础概念.ts [示例编号]\n');
135
+ console.log('可用示例:');
136
+ console.log(' 1 - 单个提示');
137
+ console.log(' 2 - 多个提示链');
138
+ console.log(' 3 - 处理取消操作');
139
+ console.log(' 4 - 提交回调');
140
+ console.log(' 5 - 初始值的使用');
141
+ }
142
+ }
@@ -0,0 +1,201 @@
1
+ "use strict";
2
+ /**
3
+ * Prompts 提示类型
4
+ * ================
5
+ * prompts 支持多种提示类型,满足不同的输入需求
6
+ * 包括文本、数字、确认、选择、多选等
7
+ */
8
+ var __importDefault = (this && this.__importDefault) || function (mod) {
9
+ return (mod && mod.__esModule) ? mod : { "default": mod };
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const prompts_1 = __importDefault(require("prompts"));
13
+ // #region 示例1: 文本类型 (text, password, invisible)
14
+ async function textTypes() {
15
+ console.log('\n=== 文本类型示例 ===\n');
16
+ const response = await (0, prompts_1.default)([
17
+ {
18
+ type: 'text',
19
+ name: 'username',
20
+ message: '用户名:'
21
+ },
22
+ {
23
+ type: 'password',
24
+ name: 'password',
25
+ message: '密码:'
26
+ },
27
+ {
28
+ type: 'invisible',
29
+ name: 'secret',
30
+ message: '密钥 (完全不可见):'
31
+ }
32
+ ]);
33
+ console.log('\n收集到的信息:');
34
+ console.log('用户名:', response.username);
35
+ console.log('密码:', response.password ? '***' : '(未输入)');
36
+ console.log('密钥:', response.secret ? '***' : '(未输入)');
37
+ }
38
+ // #endregion
39
+ // #region 示例2: 数字类型 (number)
40
+ async function numberType() {
41
+ console.log('\n=== 数字类型示例 ===');
42
+ console.log('提示: 使用上下箭头可以增减数值\n');
43
+ const response = await (0, prompts_1.default)([
44
+ {
45
+ type: 'number',
46
+ name: 'age',
47
+ message: '你的年龄:',
48
+ initial: 18,
49
+ min: 0,
50
+ max: 120
51
+ },
52
+ {
53
+ type: 'number',
54
+ name: 'price',
55
+ message: '商品价格:',
56
+ initial: 99.99,
57
+ float: true, // 允许小数
58
+ round: 2, // 保留两位小数
59
+ increment: 0.1
60
+ }
61
+ ]);
62
+ console.log('\n输入结果:');
63
+ console.log('年龄:', response.age);
64
+ console.log('价格:', response.price);
65
+ }
66
+ // #endregion
67
+ // #region 示例3: 确认类型 (confirm)
68
+ async function confirmType() {
69
+ console.log('\n=== 确认类型示例 ===');
70
+ console.log('提示: 按 y/n 或 回车键确认\n');
71
+ const response = await (0, prompts_1.default)([
72
+ {
73
+ type: 'confirm',
74
+ name: 'agree',
75
+ message: '你同意服务条款吗?',
76
+ initial: true
77
+ },
78
+ {
79
+ type: 'confirm',
80
+ name: 'subscribe',
81
+ message: '订阅我们的新闻通讯?',
82
+ initial: false
83
+ }
84
+ ]);
85
+ console.log('\n选择结果:');
86
+ console.log('同意条款:', response.agree ? '是' : '否');
87
+ console.log('订阅通讯:', response.subscribe ? '是' : '否');
88
+ }
89
+ // #endregion
90
+ // #region 示例4: 切换类型 (toggle)
91
+ async function toggleType() {
92
+ console.log('\n=== 切换类型示例 ===');
93
+ console.log('提示: 使用左右箭头或空格切换\n');
94
+ const response = await (0, prompts_1.default)([
95
+ {
96
+ type: 'toggle',
97
+ name: 'notifications',
98
+ message: '启用通知',
99
+ initial: true,
100
+ active: '开启',
101
+ inactive: '关闭'
102
+ },
103
+ {
104
+ type: 'toggle',
105
+ name: 'darkMode',
106
+ message: '深色模式',
107
+ initial: false,
108
+ active: 'on',
109
+ inactive: 'off'
110
+ }
111
+ ]);
112
+ console.log('\n设置结果:');
113
+ console.log('通知:', response.notifications);
114
+ console.log('深色模式:', response.darkMode);
115
+ }
116
+ // #endregion
117
+ // #region 示例5: 单选类型 (select)
118
+ async function selectType() {
119
+ console.log('\n=== 单选类型示例 ===');
120
+ console.log('提示: 使用上下箭头选择,回车确认\n');
121
+ const response = await (0, prompts_1.default)({
122
+ type: 'select',
123
+ name: 'color',
124
+ message: '选择你喜欢的颜色',
125
+ choices: [
126
+ { title: '红色', description: '热情的颜色', value: '#ff0000' },
127
+ { title: '绿色', value: '#00ff00' },
128
+ { title: '蓝色', value: '#0000ff' },
129
+ { title: '黄色', value: '#ffff00', disabled: true } // 禁用选项
130
+ ],
131
+ initial: 1
132
+ });
133
+ console.log('\n你选择的颜色:', response.color);
134
+ }
135
+ // #endregion
136
+ // #region 示例6: 多选类型 (multiselect)
137
+ async function multiselectType() {
138
+ console.log('\n=== 多选类型示例 ===');
139
+ console.log('提示: 空格选择/取消,回车确认\n');
140
+ const response = await (0, prompts_1.default)({
141
+ type: 'multiselect',
142
+ name: 'languages',
143
+ message: '选择你会的编程语言',
144
+ choices: [
145
+ { title: 'JavaScript', value: 'js', selected: true },
146
+ { title: 'TypeScript', value: 'ts', selected: true },
147
+ { title: 'Python', value: 'py' },
148
+ { title: 'Java', value: 'java' },
149
+ { title: 'Go', value: 'go' },
150
+ { title: 'Rust', value: 'rust' }
151
+ ],
152
+ hint: '- 空格选择,回车提交',
153
+ max: 3, // 最多选择 3 个
154
+ instructions: false
155
+ });
156
+ console.log('\n你选择的语言:', response.languages);
157
+ }
158
+ // #endregion
159
+ // #region 示例7: 列表类型 (list)
160
+ async function listType() {
161
+ console.log('\n=== 列表类型示例 ===');
162
+ console.log('提示: 输入逗号分隔的值\n');
163
+ const response = await (0, prompts_1.default)({
164
+ type: 'list',
165
+ name: 'tags',
166
+ message: '输入标签 (用逗号分隔)',
167
+ initial: 'nodejs, typescript',
168
+ separator: ','
169
+ });
170
+ console.log('\n标签列表:', response.tags);
171
+ console.log('标签数量:', response.tags?.length || 0);
172
+ }
173
+ // #endregion
174
+ if (require.main === module) {
175
+ const exampleNumber = process.argv[2];
176
+ const examples = {
177
+ '1': textTypes,
178
+ '2': numberType,
179
+ '3': confirmType,
180
+ '4': toggleType,
181
+ '5': selectType,
182
+ '6': multiselectType,
183
+ '7': listType
184
+ };
185
+ if (exampleNumber && examples[exampleNumber]) {
186
+ examples[exampleNumber]().catch(console.error);
187
+ }
188
+ else {
189
+ console.log('Prompts 提示类型示例');
190
+ console.log('===================\n');
191
+ console.log('使用方法: ts-node 02-提示类型.ts [示例编号]\n');
192
+ console.log('可用示例:');
193
+ console.log(' 1 - 文本类型 (text, password, invisible)');
194
+ console.log(' 2 - 数字类型 (number)');
195
+ console.log(' 3 - 确认类型 (confirm)');
196
+ console.log(' 4 - 切换类型 (toggle)');
197
+ console.log(' 5 - 单选类型 (select)');
198
+ console.log(' 6 - 多选类型 (multiselect)');
199
+ console.log(' 7 - 列表类型 (list)');
200
+ }
201
+ }
@@ -0,0 +1,236 @@
1
+ "use strict";
2
+ /**
3
+ * Prompts 高级特性
4
+ * ================
5
+ * 包括验证、格式化、动态提示、自动完成等高级功能
6
+ */
7
+ var __importDefault = (this && this.__importDefault) || function (mod) {
8
+ return (mod && mod.__esModule) ? mod : { "default": mod };
9
+ };
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ const prompts_1 = __importDefault(require("prompts"));
12
+ // #region 示例1: 输入验证
13
+ async function validation() {
14
+ console.log('\n=== 输入验证示例 ===\n');
15
+ const response = await (0, prompts_1.default)([
16
+ {
17
+ type: 'text',
18
+ name: 'email',
19
+ message: '请输入邮箱:',
20
+ validate: (value) => {
21
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
22
+ return emailRegex.test(value) ? true : '请输入有效的邮箱地址';
23
+ }
24
+ },
25
+ {
26
+ type: 'number',
27
+ name: 'age',
28
+ message: '请输入年龄:',
29
+ validate: (value) => {
30
+ if (value < 18)
31
+ return '必须年满 18 岁';
32
+ if (value > 100)
33
+ return '年龄不能超过 100 岁';
34
+ return true;
35
+ }
36
+ },
37
+ {
38
+ type: 'password',
39
+ name: 'password',
40
+ message: '设置密码:',
41
+ validate: (value) => {
42
+ if (value.length < 6)
43
+ return '密码至少 6 个字符';
44
+ if (!/\d/.test(value))
45
+ return '密码必须包含数字';
46
+ return true;
47
+ }
48
+ }
49
+ ]);
50
+ console.log('\n验证通过的数据:');
51
+ console.log('邮箱:', response.email);
52
+ console.log('年龄:', response.age);
53
+ console.log('密码:', '***');
54
+ }
55
+ // #endregion
56
+ // #region 示例2: 格式化输出
57
+ async function formatting() {
58
+ console.log('\n=== 格式化输出示例 ===\n');
59
+ const response = await (0, prompts_1.default)([
60
+ {
61
+ type: 'text',
62
+ name: 'name',
63
+ message: '你的名字:',
64
+ format: (val) => val.trim().toUpperCase()
65
+ },
66
+ {
67
+ type: 'number',
68
+ name: 'price',
69
+ message: '输入价格:',
70
+ format: (val) => {
71
+ return new Intl.NumberFormat('zh-CN', {
72
+ style: 'currency',
73
+ currency: 'CNY'
74
+ }).format(val);
75
+ }
76
+ },
77
+ {
78
+ type: 'list',
79
+ name: 'tags',
80
+ message: '输入标签:',
81
+ separator: ',',
82
+ format: (val) => val.map(tag => tag.trim().toLowerCase())
83
+ }
84
+ ]);
85
+ console.log('\n格式化后的数据:');
86
+ console.log('名字:', response.name);
87
+ console.log('价格:', response.price);
88
+ console.log('标签:', response.tags);
89
+ }
90
+ // #endregion
91
+ // #region 示例3: 动态提示
92
+ async function dynamicPrompts() {
93
+ console.log('\n=== 动态提示示例 ===\n');
94
+ const response = await (0, prompts_1.default)([
95
+ {
96
+ type: 'confirm',
97
+ name: 'likePizza',
98
+ message: '你喜欢披萨吗?'
99
+ },
100
+ {
101
+ type: (prev) => prev ? 'text' : null, // 根据上一个答案决定是否显示
102
+ name: 'favoriteTopping',
103
+ message: '你最喜欢的披萨配料是什么?'
104
+ },
105
+ {
106
+ type: 'select',
107
+ name: 'role',
108
+ message: '选择你的角色:',
109
+ choices: [
110
+ { title: '开发者', value: 'dev' },
111
+ { title: '设计师', value: 'designer' },
112
+ { title: '产品经理', value: 'pm' }
113
+ ]
114
+ },
115
+ {
116
+ type: (prev) => prev === 'dev' ? 'multiselect' : null,
117
+ name: 'languages',
118
+ message: '你会哪些编程语言?',
119
+ choices: [
120
+ { title: 'JavaScript', value: 'js' },
121
+ { title: 'Python', value: 'py' },
122
+ { title: 'Java', value: 'java' }
123
+ ]
124
+ }
125
+ ]);
126
+ console.log('\n收集到的信息:');
127
+ console.log(JSON.stringify(response, null, 2));
128
+ }
129
+ // #endregion
130
+ // #region 示例4: 自动完成
131
+ async function autocomplete() {
132
+ console.log('\n=== 自动完成示例 ===');
133
+ console.log('提示: 输入关键字进行搜索\n');
134
+ const frameworks = [
135
+ { title: 'React', value: 'react' },
136
+ { title: 'Vue', value: 'vue' },
137
+ { title: 'Angular', value: 'angular' },
138
+ { title: 'Svelte', value: 'svelte' },
139
+ { title: 'Next.js', value: 'nextjs' },
140
+ { title: 'Nuxt.js', value: 'nuxtjs' },
141
+ { title: 'Express', value: 'express' },
142
+ { title: 'Nest.js', value: 'nestjs' }
143
+ ];
144
+ const response = await (0, prompts_1.default)({
145
+ type: 'autocomplete',
146
+ name: 'framework',
147
+ message: '选择一个框架:',
148
+ choices: frameworks,
149
+ limit: 5, // 最多显示 5 个结果
150
+ suggest: (input, choices) => {
151
+ return Promise.resolve(choices.filter(choice => choice.title.toLowerCase().includes(input.toLowerCase())));
152
+ }
153
+ });
154
+ console.log('\n你选择的框架:', response.framework);
155
+ }
156
+ // #endregion
157
+ // #region 示例5: 使用前一个答案
158
+ async function usePreviousAnswers() {
159
+ console.log('\n=== 使用前一个答案示例 ===\n');
160
+ const response = await (0, prompts_1.default)([
161
+ {
162
+ type: 'text',
163
+ name: 'projectName',
164
+ message: '项目名称:'
165
+ },
166
+ {
167
+ type: 'text',
168
+ name: 'description',
169
+ message: (prev, values) => `为 "${values.projectName}" 添加描述:`,
170
+ initial: (prev, values) => `${values.projectName} 项目`
171
+ },
172
+ {
173
+ type: 'confirm',
174
+ name: 'confirm',
175
+ message: (prev, values) => `确认创建项目 "${values.projectName}"?`
176
+ }
177
+ ]);
178
+ console.log('\n项目信息:');
179
+ console.log('名称:', response.projectName);
180
+ console.log('描述:', response.description);
181
+ console.log('已确认:', response.confirm);
182
+ }
183
+ // #endregion
184
+ // #region 示例6: 程序化注入答案 (用于测试)
185
+ async function injectAnswers() {
186
+ console.log('\n=== 程序化注入答案示例 ===');
187
+ console.log('(用于自动化测试)\n');
188
+ // 注入预设答案
189
+ prompts_1.default.inject(['张三', 25, true]);
190
+ const response = await (0, prompts_1.default)([
191
+ {
192
+ type: 'text',
193
+ name: 'name',
194
+ message: '姓名:'
195
+ },
196
+ {
197
+ type: 'number',
198
+ name: 'age',
199
+ message: '年龄:'
200
+ },
201
+ {
202
+ type: 'confirm',
203
+ name: 'agree',
204
+ message: '同意条款?'
205
+ }
206
+ ]);
207
+ console.log('自动填充的答案:');
208
+ console.log(JSON.stringify(response, null, 2));
209
+ }
210
+ // #endregion
211
+ if (require.main === module) {
212
+ const exampleNumber = process.argv[2];
213
+ const examples = {
214
+ '1': validation,
215
+ '2': formatting,
216
+ '3': dynamicPrompts,
217
+ '4': autocomplete,
218
+ '5': usePreviousAnswers,
219
+ '6': injectAnswers
220
+ };
221
+ if (exampleNumber && examples[exampleNumber]) {
222
+ examples[exampleNumber]().catch(console.error);
223
+ }
224
+ else {
225
+ console.log('Prompts 高级特性示例');
226
+ console.log('===================\n');
227
+ console.log('使用方法: ts-node 03-高级特性.ts [示例编号]\n');
228
+ console.log('可用示例:');
229
+ console.log(' 1 - 输入验证');
230
+ console.log(' 2 - 格式化输出');
231
+ console.log(' 3 - 动态提示');
232
+ console.log(' 4 - 自动完成');
233
+ console.log(' 5 - 使用前一个答案');
234
+ console.log(' 6 - 程序化注入答案 (用于测试)');
235
+ }
236
+ }
@@ -0,0 +1,243 @@
1
+ "use strict";
2
+ /**
3
+ * Prompts 实战案例
4
+ * ================
5
+ * 综合运用 prompts 的各种功能构建实用的命令行工具
6
+ */
7
+ var __importDefault = (this && this.__importDefault) || function (mod) {
8
+ return (mod && mod.__esModule) ? mod : { "default": mod };
9
+ };
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ const prompts_1 = __importDefault(require("prompts"));
12
+ // #region 示例1: 项目初始化工具
13
+ async function projectInitializer() {
14
+ console.log('\n=== 项目初始化工具 ===\n');
15
+ const response = await (0, prompts_1.default)([
16
+ {
17
+ type: 'text',
18
+ name: 'projectName',
19
+ message: '项目名称:',
20
+ validate: (value) => value.length > 0 ? true : '项目名称不能为空'
21
+ },
22
+ {
23
+ type: 'select',
24
+ name: 'template',
25
+ message: '选择项目模板:',
26
+ choices: [
27
+ { title: 'React', value: 'react' },
28
+ { title: 'Vue', value: 'vue' },
29
+ { title: 'Node.js', value: 'nodejs' },
30
+ { title: 'Express API', value: 'express' }
31
+ ]
32
+ },
33
+ {
34
+ type: 'multiselect',
35
+ name: 'features',
36
+ message: '选择需要的功能:',
37
+ choices: [
38
+ { title: 'TypeScript', value: 'typescript', selected: true },
39
+ { title: 'ESLint', value: 'eslint', selected: true },
40
+ { title: 'Prettier', value: 'prettier' },
41
+ { title: 'Jest', value: 'jest' },
42
+ { title: 'Docker', value: 'docker' }
43
+ ]
44
+ },
45
+ {
46
+ type: 'confirm',
47
+ name: 'git',
48
+ message: '初始化 Git 仓库?',
49
+ initial: true
50
+ }
51
+ ]);
52
+ console.log('\n项目配置:');
53
+ console.log(JSON.stringify(response, null, 2));
54
+ console.log('\n✓ 项目初始化完成!');
55
+ }
56
+ // #endregion
57
+ // #region 示例2: 用户注册表单
58
+ async function userRegistration() {
59
+ console.log('\n=== 用户注册表单 ===\n');
60
+ const response = await (0, prompts_1.default)([
61
+ {
62
+ type: 'text',
63
+ name: 'username',
64
+ message: '用户名:',
65
+ validate: (value) => {
66
+ if (value.length < 3)
67
+ return '用户名至少 3 个字符';
68
+ if (!/^[a-zA-Z0-9_]+$/.test(value))
69
+ return '只能包含字母、数字和下划线';
70
+ return true;
71
+ }
72
+ },
73
+ {
74
+ type: 'text',
75
+ name: 'email',
76
+ message: '邮箱:',
77
+ validate: (value) => {
78
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
79
+ return emailRegex.test(value) ? true : '请输入有效的邮箱地址';
80
+ }
81
+ },
82
+ {
83
+ type: 'password',
84
+ name: 'password',
85
+ message: '密码:',
86
+ validate: (value) => {
87
+ if (value.length < 8)
88
+ return '密码至少 8 个字符';
89
+ if (!/[A-Z]/.test(value))
90
+ return '密码必须包含大写字母';
91
+ if (!/[a-z]/.test(value))
92
+ return '密码必须包含小写字母';
93
+ if (!/\d/.test(value))
94
+ return '密码必须包含数字';
95
+ return true;
96
+ }
97
+ },
98
+ {
99
+ type: 'password',
100
+ name: 'confirmPassword',
101
+ message: '确认密码:',
102
+ validate: (value, values) => value === values.password ? true : '两次密码不一致'
103
+ },
104
+ {
105
+ type: 'confirm',
106
+ name: 'agree',
107
+ message: '同意服务条款?',
108
+ initial: false
109
+ }
110
+ ]);
111
+ if (response.agree) {
112
+ console.log('\n✓ 注册成功!');
113
+ console.log('用户名:', response.username);
114
+ console.log('邮箱:', response.email);
115
+ }
116
+ else {
117
+ console.log('\n✗ 必须同意服务条款才能注册');
118
+ }
119
+ }
120
+ // #endregion
121
+ // #region 示例3: 配置文件生成器
122
+ async function configGenerator() {
123
+ console.log('\n=== 配置文件生成器 ===\n');
124
+ const response = await (0, prompts_1.default)([
125
+ {
126
+ type: 'select',
127
+ name: 'env',
128
+ message: '选择环境:',
129
+ choices: [
130
+ { title: '开发环境', value: 'development' },
131
+ { title: '测试环境', value: 'staging' },
132
+ { title: '生产环境', value: 'production' }
133
+ ]
134
+ },
135
+ {
136
+ type: 'text',
137
+ name: 'host',
138
+ message: '数据库主机:',
139
+ initial: 'localhost'
140
+ },
141
+ {
142
+ type: 'number',
143
+ name: 'port',
144
+ message: '数据库端口:',
145
+ initial: 5432
146
+ },
147
+ {
148
+ type: 'text',
149
+ name: 'database',
150
+ message: '数据库名称:',
151
+ initial: (prev, values) => `myapp_${values.env}`
152
+ },
153
+ {
154
+ type: 'toggle',
155
+ name: 'ssl',
156
+ message: '启用 SSL:',
157
+ initial: (prev, values) => values.env === 'production',
158
+ active: '是',
159
+ inactive: '否'
160
+ }
161
+ ]);
162
+ const config = {
163
+ environment: response.env,
164
+ database: {
165
+ host: response.host,
166
+ port: response.port,
167
+ database: response.database,
168
+ ssl: response.ssl
169
+ }
170
+ };
171
+ console.log('\n生成的配置:');
172
+ console.log(JSON.stringify(config, null, 2));
173
+ }
174
+ // #endregion
175
+ // #region 示例4: 交互式问卷调查
176
+ async function survey() {
177
+ console.log('\n=== 开发者满意度调查 ===\n');
178
+ const response = await (0, prompts_1.default)([
179
+ {
180
+ type: 'select',
181
+ name: 'experience',
182
+ message: '你的编程经验:',
183
+ choices: [
184
+ { title: '新手 (< 1年)', value: 'beginner' },
185
+ { title: '初级 (1-3年)', value: 'junior' },
186
+ { title: '中级 (3-5年)', value: 'mid' },
187
+ { title: '高级 (5年+)', value: 'senior' }
188
+ ]
189
+ },
190
+ {
191
+ type: 'multiselect',
192
+ name: 'tools',
193
+ message: '你常用的工具:',
194
+ choices: [
195
+ { title: 'VS Code', value: 'vscode' },
196
+ { title: 'Git', value: 'git' },
197
+ { title: 'Docker', value: 'docker' },
198
+ { title: 'Postman', value: 'postman' },
199
+ { title: 'Chrome DevTools', value: 'devtools' }
200
+ ],
201
+ hint: '- 空格选择,回车提交'
202
+ },
203
+ {
204
+ type: 'number',
205
+ name: 'satisfaction',
206
+ message: '满意度评分 (1-10):',
207
+ min: 1,
208
+ max: 10,
209
+ initial: 7
210
+ },
211
+ {
212
+ type: 'text',
213
+ name: 'feedback',
214
+ message: '其他建议:',
215
+ initial: '无'
216
+ }
217
+ ]);
218
+ console.log('\n感谢你的反馈!');
219
+ console.log('调查结果:', JSON.stringify(response, null, 2));
220
+ }
221
+ // #endregion
222
+ if (require.main === module) {
223
+ const exampleNumber = process.argv[2];
224
+ const examples = {
225
+ '1': projectInitializer,
226
+ '2': userRegistration,
227
+ '3': configGenerator,
228
+ '4': survey
229
+ };
230
+ if (exampleNumber && examples[exampleNumber]) {
231
+ examples[exampleNumber]().catch(console.error);
232
+ }
233
+ else {
234
+ console.log('Prompts 实战案例');
235
+ console.log('===============\n');
236
+ console.log('使用方法: ts-node 04-实战案例.ts [示例编号]\n');
237
+ console.log('可用示例:');
238
+ console.log(' 1 - 项目初始化工具');
239
+ console.log(' 2 - 用户注册表单');
240
+ console.log(' 3 - 配置文件生成器');
241
+ console.log(' 4 - 交互式问卷调查');
242
+ }
243
+ }
@@ -0,0 +1,289 @@
1
+ "use strict";
2
+ /**
3
+ * Prompts 配合 Fuse.js 实现模糊补全
4
+ * ====================================
5
+ * 使用 Fuse.js 提供强大的模糊搜索能力,实现智能的自动完成功能
6
+ * 适用于 Node.js 14+ 版本
7
+ */
8
+ var __importDefault = (this && this.__importDefault) || function (mod) {
9
+ return (mod && mod.__esModule) ? mod : { "default": mod };
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const prompts_1 = __importDefault(require("prompts"));
13
+ const fuse_js_1 = __importDefault(require("fuse.js"));
14
+ // #region 示例1: 基础模糊搜索
15
+ async function basicFuzzySearch() {
16
+ console.log('\n=== 基础模糊搜索示例 ===');
17
+ console.log('提示: 输入部分关键字,支持拼写错误容错\n');
18
+ const cities = [
19
+ { title: '北京', value: 'beijing' },
20
+ { title: '上海', value: 'shanghai' },
21
+ { title: '广州', value: 'guangzhou' },
22
+ { title: '深圳', value: 'shenzhen' },
23
+ { title: '杭州', value: 'hangzhou' },
24
+ { title: '成都', value: 'chengdu' },
25
+ { title: '重庆', value: 'chongqing' },
26
+ { title: '西安', value: 'xian' }
27
+ ];
28
+ // 配置 Fuse.js
29
+ const fuse = new fuse_js_1.default(cities, {
30
+ keys: ['title', 'value'],
31
+ threshold: 0.4, // 0-1 之间,越小越严格
32
+ distance: 100
33
+ });
34
+ const response = await (0, prompts_1.default)({
35
+ type: 'autocomplete',
36
+ name: 'city',
37
+ message: '选择城市:',
38
+ choices: cities,
39
+ suggest: (input, choices) => {
40
+ if (!input)
41
+ return Promise.resolve(choices);
42
+ const results = fuse.search(input);
43
+ return Promise.resolve(results.map(r => r.item));
44
+ }
45
+ });
46
+ console.log('\n你选择的城市:', response.city);
47
+ }
48
+ // #endregion
49
+ // #region 示例2: 多字段模糊搜索
50
+ async function multiFieldSearch() {
51
+ console.log('\n=== 多字段模糊搜索示例 ===');
52
+ console.log('提示: 可以搜索名称、描述或标签\n');
53
+ const packages = [
54
+ {
55
+ title: 'React',
56
+ value: 'react',
57
+ description: '用于构建用户界面的 JavaScript 库',
58
+ tags: ['ui', 'frontend', 'facebook']
59
+ },
60
+ {
61
+ title: 'Vue',
62
+ value: 'vue',
63
+ description: '渐进式 JavaScript 框架',
64
+ tags: ['ui', 'frontend', 'progressive']
65
+ },
66
+ {
67
+ title: 'Express',
68
+ value: 'express',
69
+ description: 'Node.js Web 应用框架',
70
+ tags: ['backend', 'server', 'nodejs']
71
+ },
72
+ {
73
+ title: 'Lodash',
74
+ value: 'lodash',
75
+ description: '实用的 JavaScript 工具库',
76
+ tags: ['utility', 'helpers', 'functions']
77
+ },
78
+ {
79
+ title: 'Axios',
80
+ value: 'axios',
81
+ description: '基于 Promise 的 HTTP 客户端',
82
+ tags: ['http', 'ajax', 'request']
83
+ }
84
+ ];
85
+ const fuse = new fuse_js_1.default(packages, {
86
+ keys: [
87
+ { name: 'title', weight: 2 }, // 标题权重最高
88
+ { name: 'description', weight: 1 },
89
+ { name: 'tags', weight: 1.5 }
90
+ ],
91
+ threshold: 0.3,
92
+ includeScore: true
93
+ });
94
+ const response = await (0, prompts_1.default)({
95
+ type: 'autocomplete',
96
+ name: 'package',
97
+ message: '搜索 npm 包:',
98
+ choices: packages,
99
+ suggest: (input, choices) => {
100
+ if (!input)
101
+ return Promise.resolve(choices);
102
+ const results = fuse.search(input);
103
+ return Promise.resolve(results.map(r => ({
104
+ ...r.item,
105
+ description: `${r.item.description} (匹配度: ${(1 - (r.score || 0)).toFixed(2)})`
106
+ })));
107
+ }
108
+ });
109
+ console.log('\n你选择的包:', response.package);
110
+ }
111
+ // #endregion
112
+ // #region 示例3: 拼音搜索支持
113
+ async function pinyinSearch() {
114
+ console.log('\n=== 拼音搜索支持示例 ===');
115
+ console.log('提示: 可以使用拼音或中文搜索\n');
116
+ const fruits = [
117
+ { title: '苹果 (pingguo)', value: 'apple' },
118
+ { title: '香蕉 (xiangjiao)', value: 'banana' },
119
+ { title: '橙子 (chengzi)', value: 'orange' },
120
+ { title: '葡萄 (putao)', value: 'grape' },
121
+ { title: '西瓜 (xigua)', value: 'watermelon' },
122
+ { title: '草莓 (caomei)', value: 'strawberry' },
123
+ { title: '芒果 (mangguo)', value: 'mango' },
124
+ { title: '樱桃 (yingtao)', value: 'cherry' }
125
+ ];
126
+ const fuse = new fuse_js_1.default(fruits, {
127
+ keys: ['title'],
128
+ threshold: 0.4,
129
+ ignoreLocation: true // 忽略匹配位置
130
+ });
131
+ const response = await (0, prompts_1.default)({
132
+ type: 'autocomplete',
133
+ name: 'fruit',
134
+ message: '选择水果:',
135
+ choices: fruits,
136
+ limit: 6,
137
+ suggest: (input, choices) => {
138
+ if (!input)
139
+ return Promise.resolve(choices);
140
+ const results = fuse.search(input);
141
+ return Promise.resolve(results.map(r => r.item));
142
+ }
143
+ });
144
+ console.log('\n你选择的水果:', response.fruit);
145
+ }
146
+ // #endregion
147
+ // #region 示例4: 高级配置 - 精确匹配优先
148
+ async function advancedConfiguration() {
149
+ console.log('\n=== 高级配置示例 ===');
150
+ console.log('提示: 精确匹配会排在前面\n');
151
+ const commands = [
152
+ { title: 'git commit', value: 'commit', description: '提交更改' },
153
+ { title: 'git checkout', value: 'checkout', description: '切换分支' },
154
+ { title: 'git clone', value: 'clone', description: '克隆仓库' },
155
+ { title: 'git push', value: 'push', description: '推送到远程' },
156
+ { title: 'git pull', value: 'pull', description: '拉取远程更改' },
157
+ { title: 'git merge', value: 'merge', description: '合并分支' },
158
+ { title: 'git rebase', value: 'rebase', description: '变基操作' },
159
+ { title: 'git status', value: 'status', description: '查看状态' }
160
+ ];
161
+ const fuse = new fuse_js_1.default(commands, {
162
+ keys: ['title', 'description'],
163
+ threshold: 0.3,
164
+ distance: 100,
165
+ minMatchCharLength: 2, // 最小匹配字符数
166
+ shouldSort: true, // 按相关度排序
167
+ findAllMatches: false, // 找到第一个匹配即停止
168
+ useExtendedSearch: true // 启用扩展搜索
169
+ });
170
+ const response = await (0, prompts_1.default)({
171
+ type: 'autocomplete',
172
+ name: 'command',
173
+ message: '选择 Git 命令:',
174
+ choices: commands,
175
+ suggest: (input, choices) => {
176
+ if (!input)
177
+ return Promise.resolve(choices);
178
+ const results = fuse.search(input);
179
+ return Promise.resolve(results.map(r => ({
180
+ title: r.item.title,
181
+ value: r.item.value,
182
+ description: r.item.description
183
+ })));
184
+ }
185
+ });
186
+ console.log('\n你选择的命令:', response.command);
187
+ }
188
+ // #endregion
189
+ // #region 示例5: 动态数据源
190
+ async function dynamicDataSource() {
191
+ console.log('\n=== 动态数据源示例 ===');
192
+ console.log('提示: 模拟从 API 获取数据\n');
193
+ // 模拟 API 数据
194
+ const mockApiData = [
195
+ { id: 1, name: 'John Doe', email: 'john@example.com', role: 'Developer' },
196
+ { id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'Designer' },
197
+ { id: 3, name: 'Bob Johnson', email: 'bob@example.com', role: 'Manager' },
198
+ { id: 4, name: 'Alice Brown', email: 'alice@example.com', role: 'Developer' },
199
+ { id: 5, name: 'Charlie Wilson', email: 'charlie@example.com', role: 'Tester' }
200
+ ];
201
+ const choices = mockApiData.map(user => ({
202
+ title: `${user.name} (${user.role})`,
203
+ value: user.id,
204
+ description: user.email
205
+ }));
206
+ const fuse = new fuse_js_1.default(mockApiData, {
207
+ keys: ['name', 'email', 'role'],
208
+ threshold: 0.3
209
+ });
210
+ const response = await (0, prompts_1.default)({
211
+ type: 'autocomplete',
212
+ name: 'user',
213
+ message: '搜索用户:',
214
+ choices: choices,
215
+ suggest: (input) => {
216
+ if (!input)
217
+ return Promise.resolve(choices);
218
+ const results = fuse.search(input);
219
+ return Promise.resolve(results.map(r => ({
220
+ title: `${r.item.name} (${r.item.role})`,
221
+ value: r.item.id,
222
+ description: r.item.email
223
+ })));
224
+ }
225
+ });
226
+ console.log('\n你选择的用户 ID:', response.user);
227
+ }
228
+ // #endregion
229
+ // #region 示例6: 多选模糊搜索
230
+ async function multiSelectFuzzySearch() {
231
+ console.log('\n=== 多选模糊搜索示例 ===');
232
+ console.log('提示: 使用空格键选择多个选项\n');
233
+ const skills = [
234
+ { title: 'JavaScript', value: 'js' },
235
+ { title: 'TypeScript', value: 'ts' },
236
+ { title: 'Python', value: 'py' },
237
+ { title: 'Java', value: 'java' },
238
+ { title: 'Go', value: 'go' },
239
+ { title: 'Rust', value: 'rust' },
240
+ { title: 'C++', value: 'cpp' },
241
+ { title: 'Ruby', value: 'ruby' },
242
+ { title: 'PHP', value: 'php' },
243
+ { title: 'Swift', value: 'swift' }
244
+ ];
245
+ const fuse = new fuse_js_1.default(skills, {
246
+ keys: ['title'],
247
+ threshold: 0.3
248
+ });
249
+ const response = await (0, prompts_1.default)({
250
+ type: 'autocompleteMultiselect',
251
+ name: 'skills',
252
+ message: '选择你掌握的技能:',
253
+ choices: skills,
254
+ suggest: (input, choices) => {
255
+ if (!input)
256
+ return Promise.resolve(choices);
257
+ const results = fuse.search(input);
258
+ return Promise.resolve(results.map(r => r.item));
259
+ }
260
+ });
261
+ console.log('\n你选择的技能:', response.skills);
262
+ }
263
+ // #endregion
264
+ if (require.main === module) {
265
+ const exampleNumber = process.argv[2];
266
+ const examples = {
267
+ '1': basicFuzzySearch,
268
+ '2': multiFieldSearch,
269
+ '3': pinyinSearch,
270
+ '4': advancedConfiguration,
271
+ '5': dynamicDataSource,
272
+ '6': multiSelectFuzzySearch
273
+ };
274
+ if (exampleNumber && examples[exampleNumber]) {
275
+ examples[exampleNumber]().catch(console.error);
276
+ }
277
+ else {
278
+ console.log('Prompts 配合 Fuse.js 实现模糊补全');
279
+ console.log('===================================\n');
280
+ console.log('使用方法: ts-node 05-模糊补全.ts [示例编号]\n');
281
+ console.log('可用示例:');
282
+ console.log(' 1 - 基础模糊搜索');
283
+ console.log(' 2 - 多字段模糊搜索');
284
+ console.log(' 3 - 拼音搜索支持');
285
+ console.log(' 4 - 高级配置 - 精确匹配优先');
286
+ console.log(' 5 - 动态数据源');
287
+ console.log(' 6 - 多选模糊搜索');
288
+ }
289
+ }
package/package.json CHANGED
@@ -3,16 +3,16 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.1.1",
6
+ "version": "1.1.3",
7
7
  "description": "一个nodejs学习工具",
8
8
  "bin": {
9
- "nlearn": "dist/src/cli/cli.js",
10
- "nn": "dist/src/cli/cli.js"
9
+ "nlearn": "dist/src/cli/index.js",
10
+ "nn": "dist/src/cli/index.js"
11
11
  },
12
12
  "scripts": {
13
13
  "sync": "ts-node scripts/sync-repo.ts",
14
14
  "build": "tsc",
15
- "start": "node dist/src/cli/cli.js"
15
+ "start": "node dist/src/cli/index.js"
16
16
  },
17
17
  "author": "lipanpanx",
18
18
  "license": "MIT",
@@ -22,8 +22,10 @@
22
22
  "dependencies": {
23
23
  "@types/inquirer": "^9.0.9",
24
24
  "commander": "^14.0.2",
25
+ "fuse.js": "^7.1.0",
25
26
  "inquirer": "^9.3.8",
26
27
  "isomorphic-git": "^1.36.3",
28
+ "prompts": "^2.4.2",
27
29
  "yargs": "^18.0.0"
28
30
  },
29
31
  "engines": {
@@ -32,6 +34,7 @@
32
34
  "devDependencies": {
33
35
  "@types/commander": "^2.12.0",
34
36
  "@types/node": "^25.0.10",
37
+ "@types/prompts": "^2.4.9",
35
38
  "@types/yargs": "^17.0.35",
36
39
  "nodemon": "^3.1.11",
37
40
  "ts-node": "^10.9.2",