前言
我们在用脚手架初始化项目的时候,往往会进行一些命令交互,用过vue或者react的用脚手架新建项目的应该都进行过命令交互,vue创建的时候会让你选择vue2还是vue3,也有多选要什么配置,也有输入y或者n选择是否用history路由等,这些简单的交互其实用inquire这个包都能实现,但是最近自己在做一个小工具的时候,想要进行文件和文件夹的选择,这时我发现inquire里并没有这个交互功能,所以便自己尝试去在inquire这个库的基础上实现文件选择和文件夹选择这两种类型的交互。
插件效果
通过该插件,我们可以在控制台通过方向键来选择文件和文件夹,具体效果如下:
插件实现
Inquirer.js
Inquirer.js试图为NodeJs做一个可嵌入式的美观的命令行界面。
如下图:
inquirer原有参数
- type
表示提问的类型,包括:input、confirm、 list、rawlist、expand、checkbox、password、editor。
- name
存储当前输入的值。
- message
问题的描述。
- default
默认值。
- choices
列表选项,在某些type下可用,并且包含一个分隔符(separator);
- validate
对用户的回答进行校验。
- filter
对用户的回答进行过滤处理,返回处理后的值。
- when
根据前面问题的回答,判断当前问题是否需要被回答。
- pageSize
修改某些type类型下的渲染行数。
- prefix
修改message默认前缀。
- suffix
修改message默认后缀。
二次封装
基于inquirer原有功能及参数,增加一些扩展功能及参数
新增参数
- notNull
是否不能为空,默认为false,设置为true后该参数不能输入空,并且会有不能为空的提示,必须输入字符后才可以回车确认并进行下一步,如下图:
{ type:"input", message:"请输入你的姓名:", name:"name", notNull:true }
- type
在原有类型中新增两种类型:file、folder,分别为文件选择器和目录选择器,效果如下图:
{ type:"file", message:"请选择文件:", name:"fileName", default:"", }, { type:"folder", message:"请选择文件夹:", name:"folderName", default:"", pathType:'absolute' },
- pathType
此项为新增配置,设置目录和文件选择器选中路径输出的格式,默认为相对路径,可以设置为absolute,此时会输出绝对路径,效果如下图:
{ type:"file", message:"请选择文件:", name:"fileName", default:"", }, { type:"folder", message:"请选择文件夹:", name:"folderName", default:"", pathType:'absolute' },
代码实现
获取指定路径下的文件列表
使用fs
中的readdirSync
方法可以获取指定目录下的文件列表,具体代码如下:
getFileList = (dirPath)=>{ const list = fs.readdirSync(dirPath); return ['../(返回上一级)',...list]; }
获取指定路径下的目录列表
使用fs
中的readdirSync
方法可以获取指定目录下的文件列表,通过isDirectory
方法可以判断文件是否为目录文件,具体代码如下:
getFolderList = (dirPath)=>{ const list = fs.readdirSync(dirPath); let resList = []; list.map(item=>{ const fullPath = path.join(dirPath,item); if(fs.statSync(fullPath).isDirectory()){ resList.push(item + '(进入文件夹)'); resList.push(item + '(选择文件夹)'); } }); return ['../(返回上一级)',...resList]; }
交互类型响应控制
新增的file
和folder
类型使用自己重新封装的方法,其他依旧使用Inquirer中的响应方法,具体代码如下:
run(option){ if(option.type === 'file'){ return this.chooseFile(option); }else if(option.type === 'folder'){ return this.chooseFolder(option); }else{ if(option.notNull){ const flag = option.message.slice(-1); if([":",":"].includes(flag)){ option.message = option.message.slice(0,-1) + '(不能为空)' + flag; } } return this.defaultType(option); } }
选择文件
- 选择的为
返回上一级
,则将当前目录回退一级:
this.clear(2); return this.chooseFile(option,path.join(dirPath,'/../'));
- 选择的是目录则进入选择的目录:
return path.join(dirPath, answer[option.name]);
- 选择的是文件则返回选择的文件路径并结束操作:
this.clear(2); return this.chooseFile(option,fullPath);
完整代码如下:
chooseFile(option,dirPath = './'){ option.type = 'list'; option.suffix = "(当前浏览目录:" + path.join(__dirname,dirPath) + ')'; option.pageSize = fs.readdirSync('./').length + 1; option.choices = [...this.getFileList(dirPath)]; const answer = await inquirer.prompt([ option ]); if(answer[option.name] == '../(返回上一级)'){ this.clear(2); return this.chooseFile(option,path.join(dirPath,'/../')); }else{ const fullPath = path.join(dirPath, answer[option.name]); if(!fs.statSync(fullPath).isFile()){ this.clear(2); return this.chooseFile(option,fullPath); }else{ return path.join(dirPath, answer[option.name]); } } }
选择目录
如下图,这里使用后缀说明来区分选择文件夹和进入文件夹:
选择的为返回上一级
,则将当前目录回退一级:
this.clear(2); return this.chooseFile(option,path.join(dirPath,'/../'));
选择的是进入文件夹,则进入该目录,这里需要将加入用于区分的后缀去掉再返回:
return path.join(dirPath, answer[option.name].slice(0,-7));
- 选择的是选择文件夹则返回选择的文件夹路径并结束操作:
this.clear(2); return this.chooseFile(option,fullPath);
完整代码如下:
chooseFile(option,dirPath = './'){ option.type = 'list'; option.suffix = "(当前浏览目录:" + path.join(__dirname,dirPath) + ')'; option.pageSize = fs.readdirSync('./').length + 1; option.choices = [...this.getFileList(dirPath)]; const answer = await inquirer.prompt([ option ]); if(answer[option.name] == '../(返回上一级)'){ this.clear(2); return this.chooseFile(option,path.join(dirPath,'/../')); }else{ const fullPath = path.join(dirPath, answer[option.name]); if(!fs.statSync(fullPath).isFile()){ this.clear(2); return this.chooseFile(option,fullPath); }else{ return path.join(dirPath, answer[option.name]); } } }
基本类型调用Inquirer处理
这里增加了notNull
(是否不能为空)的参数,代码如下:
defaultType(option){ const answer = await inquirer.prompt([ option ]); if(option.notNull && answer[option.name] === ''){ this.clear(2); return this.defaultType(option); } return answer[option.name]; }
插件使用
1、安装依赖
npm install @jyeontu/j-inquirer
2、在代码中引用
const JInquirer = require('@jyeontu/j-inquirer');
3、示例代码
const JInquirer = require('@jyeontu/j-inquirer'); let options = [ { type:"input", message:"请输入你的姓名:", name:"name", notNull:true },{ type:"input", message:"请输入你的年龄:", name:"age", default:18, validate:(val)=>{ if(val < 0 || val > 150){ return "请输入0~150之间的数字"; } return true; } },{ type:"file", message:"请选择文件:", name:"fileName", default:"", },{ type:"folder", message:"请选择文件夹:", name:"folderName", default:"", pathType:'absolute' },{ type:"list", message:"请选择你喜欢的水果:", name:"fruit", default:"Apple", choices:[ "Apple", "pear", "Banana" ], },{ type:"expand", message:"请选择一个颜色:", name:"color", default:"red", choices:[ { key : 'R', value : "red" }, { key : 'B', value : "blue" }, { key : 'G', value : "green" } ] },{ type:"checkbox", message:"选择一至多种颜色:", name:"color2", choices:[ "red", "blue", "green", "pink", "orange" ] },{ type:"password", message:"请输入你的密码:", name:"pwd" },{ type:"editor", message:"写下你想写的东西:", name:"editor" } ]; let j = new JInquirer(options); let res = j.prompt().then(res=>{ console.log(res); });
到此这篇关于JavaScript基于inquirer封装一个控制台文件选择器的文章就介绍到这了,更多相关JS inquirer封装内容请搜索阿兔在线工具以前的文章或继续浏览下面的相关文章希望大家以后多多支持阿兔在线工具!