JavaScript基于inquirer封裝一個(gè)控制臺(tái)文件選擇器
前言
我們?cè)谟媚_手架初始化項(xiàng)目的時(shí)候,往往會(huì)進(jìn)行一些命令交互,用過(guò)vue或者react的用腳手架新建項(xiàng)目的應(yīng)該都進(jìn)行過(guò)命令交互,vue創(chuàng)建的時(shí)候會(huì)讓你選擇vue2還是vue3,也有多選要什么配置,也有輸入y或者n選擇是否用history路由等,這些簡(jiǎn)單的交互其實(shí)用inquire這個(gè)包都能實(shí)現(xiàn),但是最近自己在做一個(gè)小工具的時(shí)候,想要進(jìn)行文件和文件夾的選擇,這時(shí)我發(fā)現(xiàn)inquire里并沒(méi)有這個(gè)交互功能,所以便自己嘗試去在inquire這個(gè)庫(kù)的基礎(chǔ)上實(shí)現(xiàn)文件選擇和文件夾選擇這兩種類(lèi)型的交互。
插件效果
通過(guò)該插件,我們可以在控制臺(tái)通過(guò)方向鍵來(lái)選擇文件和文件夾,具體效果如下:

插件實(shí)現(xiàn)
Inquirer.js
Inquirer.js試圖為NodeJs做一個(gè)可嵌入式的美觀的命令行界面。
如下圖:

inquirer原有參數(shù)
- type
表示提問(wèn)的類(lèi)型,包括:input、confirm、 list、rawlist、expand、checkbox、password、editor。
- name
存儲(chǔ)當(dāng)前輸入的值。
- message
問(wèn)題的描述。
- default
默認(rèn)值。
- choices
列表選項(xiàng),在某些type下可用,并且包含一個(gè)分隔符(separator);
- validate
對(duì)用戶的回答進(jìn)行校驗(yàn)。
- filter
對(duì)用戶的回答進(jìn)行過(guò)濾處理,返回處理后的值。
- when
根據(jù)前面問(wèn)題的回答,判斷當(dāng)前問(wèn)題是否需要被回答。
- pageSize
修改某些type類(lèi)型下的渲染行數(shù)。
- prefix
修改message默認(rèn)前綴。
- suffix
修改message默認(rèn)后綴。
二次封裝
基于inquirer原有功能及參數(shù),增加一些擴(kuò)展功能及參數(shù)
新增參數(shù)
- notNull
是否不能為空,默認(rèn)為false,設(shè)置為true后該參數(shù)不能輸入空,并且會(huì)有不能為空的提示,必須輸入字符后才可以回車(chē)確認(rèn)并進(jìn)行下一步,如下圖:
{
type:"input",
message:"請(qǐng)輸入你的姓名:",
name:"name",
notNull:true
}
- type
在原有類(lèi)型中新增兩種類(lèi)型:file、folder,分別為文件選擇器和目錄選擇器,效果如下圖:
{
type:"file",
message:"請(qǐng)選擇文件:",
name:"fileName",
default:"",
},
{
type:"folder",
message:"請(qǐng)選擇文件夾:",
name:"folderName",
default:"",
pathType:'absolute'
},
- pathType
此項(xiàng)為新增配置,設(shè)置目錄和文件選擇器選中路徑輸出的格式,默認(rèn)為相對(duì)路徑,可以設(shè)置為absolute,此時(shí)會(huì)輸出絕對(duì)路徑,效果如下圖:
{
type:"file",
message:"請(qǐng)選擇文件:",
name:"fileName",
default:"",
},
{
type:"folder",
message:"請(qǐng)選擇文件夾:",
name:"folderName",
default:"",
pathType:'absolute'
},
代碼實(shí)現(xiàn)
獲取指定路徑下的文件列表
使用fs中的readdirSync方法可以獲取指定目錄下的文件列表,具體代碼如下:
getFileList = (dirPath)=>{
const list = fs.readdirSync(dirPath);
return ['../(返回上一級(jí))',...list];
}獲取指定路徑下的目錄列表
使用fs中的readdirSync方法可以獲取指定目錄下的文件列表,通過(guò)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 + '(進(jìn)入文件夾)');
resList.push(item + '(選擇文件夾)');
}
});
return ['../(返回上一級(jí))',...resList];
}交互類(lèi)型響應(yīng)控制
新增的file和folder類(lèi)型使用自己重新封裝的方法,其他依舊使用Inquirer中的響應(yīng)方法,具體代碼如下:
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);
}
}選擇文件
- 選擇的為
返回上一級(jí),則將當(dāng)前目錄回退一級(jí):
this.clear(2); return this.chooseFile(option,path.join(dirPath,'/../'));
- 選擇的是目錄則進(jìn)入選擇的目錄:
return path.join(dirPath, answer[option.name]);
- 選擇的是文件則返回選擇的文件路徑并結(jié)束操作:
this.clear(2); return this.chooseFile(option,fullPath);
完整代碼如下:
chooseFile(option,dirPath = './'){
option.type = 'list';
option.suffix = "(當(dāng)前瀏覽目錄:" + 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] == '../(返回上一級(jí))'){
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]);
}
}
}選擇目錄
如下圖,這里使用后綴說(shuō)明來(lái)區(qū)分選擇文件夾和進(jìn)入文件夾:

選擇的為返回上一級(jí),則將當(dāng)前目錄回退一級(jí):
this.clear(2); return this.chooseFile(option,path.join(dirPath,'/../'));
選擇的是進(jìn)入文件夾,則進(jìn)入該目錄,這里需要將加入用于區(qū)分的后綴去掉再返回:
return path.join(dirPath, answer[option.name].slice(0,-7));
- 選擇的是選擇文件夾則返回選擇的文件夾路徑并結(jié)束操作:
this.clear(2); return this.chooseFile(option,fullPath);
完整代碼如下:
chooseFile(option,dirPath = './'){
option.type = 'list';
option.suffix = "(當(dāng)前瀏覽目錄:" + 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] == '../(返回上一級(jí))'){
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]);
}
}
}基本類(lèi)型調(diào)用Inquirer處理
這里增加了notNull(是否不能為空)的參數(shù),代碼如下:
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:"請(qǐng)輸入你的姓名:",
name:"name",
notNull:true
},{
type:"input",
message:"請(qǐng)輸入你的年齡:",
name:"age",
default:18,
validate:(val)=>{
if(val < 0 || val > 150){
return "請(qǐng)輸入0~150之間的數(shù)字";
}
return true;
}
},{
type:"file",
message:"請(qǐng)選擇文件:",
name:"fileName",
default:"",
},{
type:"folder",
message:"請(qǐng)選擇文件夾:",
name:"folderName",
default:"",
pathType:'absolute'
},{
type:"list",
message:"請(qǐng)選擇你喜歡的水果:",
name:"fruit",
default:"Apple",
choices:[
"Apple",
"pear",
"Banana"
],
},{
type:"expand",
message:"請(qǐng)選擇一個(gè)顏色:",
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:"請(qǐng)輸入你的密碼:",
name:"pwd"
},{
type:"editor",
message:"寫(xiě)下你想寫(xiě)的東西:",
name:"editor"
}
];
let j = new JInquirer(options);
let res = j.prompt().then(res=>{
console.log(res);
});到此這篇關(guān)于JavaScript基于inquirer封裝一個(gè)控制臺(tái)文件選擇器的文章就介紹到這了,更多相關(guān)JS inquirer封裝內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS設(shè)計(jì)模式之觀察者模式實(shí)現(xiàn)實(shí)時(shí)改變頁(yè)面中金額數(shù)的方法
這篇文章主要介紹了JS設(shè)計(jì)模式之觀察者模式實(shí)現(xiàn)實(shí)時(shí)改變頁(yè)面中金額數(shù)的方法,結(jié)合實(shí)例形式對(duì)比分析了javascript基于觀察者模式實(shí)時(shí)改變頁(yè)面金額數(shù)的相關(guān)操作技巧,需要的朋友可以參考下2018-02-02
javascript將數(shù)字轉(zhuǎn)換整數(shù)金額大寫(xiě)的方法
這篇文章主要介紹了javascript將數(shù)字轉(zhuǎn)換整數(shù)金額大寫(xiě)的方法,通過(guò)自定義函數(shù)中的數(shù)組替換實(shí)現(xiàn)數(shù)字轉(zhuǎn)換整數(shù)金額大寫(xiě)的功能,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-01-01
JS中‘hello’與new String(‘hello’)引出的問(wèn)題詳解
這篇文章主要給大家介紹了關(guān)于JS中'hello'與new String('hello')引出的問(wèn)題的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-08-08
Javascript中產(chǎn)生固定結(jié)果的函數(shù)優(yōu)化技巧
分享一個(gè)Javascript編寫(xiě)函數(shù)的優(yōu)化技巧;適用的函數(shù)應(yīng)該要滿足以下條件;產(chǎn)生固定結(jié)果;復(fù)雜或較耗時(shí)等等,感興趣的朋友可以了解下2013-01-01
解析offsetHeight,clientHeight,scrollHeight之間的區(qū)別
這篇文章主要是對(duì)offsetHeight,clientHeight,scrollHeight之間的區(qū)別進(jìn)行了詳細(xì)介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-11-11
JavaScript實(shí)現(xiàn)向OL列表內(nèi)動(dòng)態(tài)添加LI元素的方法
這篇文章主要介紹了JavaScript實(shí)現(xiàn)向OL列表內(nèi)動(dòng)態(tài)添加LI元素的方法,實(shí)例分析了javascript操作html元素的技巧,需要的朋友可以參考下2015-03-03
JS基于構(gòu)造函數(shù)實(shí)現(xiàn)的菜單滑動(dòng)顯隱效果【測(cè)試可用】
這篇文章主要介紹了JS基于構(gòu)造函數(shù)實(shí)現(xiàn)的菜單滑動(dòng)顯隱效果,可實(shí)現(xiàn)基本的菜單折疊與展開(kāi)功能,涉及javascript響應(yīng)鼠標(biāo)事件動(dòng)態(tài)操作頁(yè)面元素的相關(guān)技巧,需要的朋友可以參考下2016-06-06
JavaScript 動(dòng)態(tài)創(chuàng)建VML的方法
JavaScript 動(dòng)態(tài)創(chuàng)建VML的方法,需要的朋友可以參考下。2009-10-10

