mixcli 3.2.0 → 3.2.2
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/index.cjs +4 -0
- package/dist/index.cjs.map +1 -0
- package/dist/{index.d.mts → index.d.cts} +67 -94
- package/dist/index.d.ts +67 -94
- package/dist/index.js +3 -1132
- package/dist/index.js.map +1 -1
- package/package.json +24 -11
- package/readme.md +8 -12
- package/src/cli.ts +8 -15
- package/src/command.ts +452 -452
- package/src/finder.ts +142 -142
- package/src/index.ts +5 -4
- package/src/option.ts +39 -81
- package/src/prompt.ts +183 -110
- package/src/utils.ts +144 -158
- package/dist/index.mjs +0 -1088
- package/dist/index.mjs.map +0 -1
- package/src/oslocate.js +0 -148
package/src/prompt.ts
CHANGED
@@ -1,113 +1,190 @@
|
|
1
|
+
import { isPlainObject } from "flex-tools/typecheck/isPlainObject"
|
2
|
+
import { isNumber } from "flex-tools/typecheck/isNumber"
|
3
|
+
|
1
4
|
import { PromptObject } from "prompts"
|
2
5
|
import { outputDebug } from "./utils"
|
3
|
-
|
6
|
+
import { MixOption } from "./option"
|
4
7
|
|
5
|
-
export type PromptType = "text" | "password" | "invisible" | "number"| "confirm"| "list"
|
8
|
+
export type PromptType = "text" | "password" | "invisible" | "number"| "confirm"| "list"
|
9
|
+
| "toggle"| "select" | "multiselect" | "autocomplete" | "date" | "autocompleteMultiselect"
|
6
10
|
|
7
11
|
export type PromptParam = 'auto' | boolean | PromptType | PromptObject
|
8
12
|
export type InputPromptParam = PromptParam | ((value:any)=>PromptParam) | boolean
|
9
13
|
export type PromptParamDefaultValue = string | boolean | string[]
|
10
14
|
|
11
15
|
export const promptTypeMap:Record<string,string> = {
|
12
|
-
boolean:"confirm",
|
13
|
-
string:"text",
|
14
|
-
number:"number",
|
15
|
-
array:"list",
|
16
|
+
boolean: "confirm",
|
17
|
+
string : "text",
|
18
|
+
number : "number",
|
19
|
+
array : "list",
|
20
|
+
date : "date"
|
16
21
|
}
|
17
22
|
|
18
|
-
export const supportedPromptTypes = [
|
23
|
+
export const supportedPromptTypes = [
|
24
|
+
"text",
|
25
|
+
"password",
|
26
|
+
"invisible",
|
27
|
+
"number",
|
28
|
+
"confirm" ,
|
29
|
+
"list",
|
30
|
+
"toggle" ,
|
31
|
+
"select" ,
|
32
|
+
"multiselect" ,
|
33
|
+
"autocomplete" ,
|
34
|
+
"date" ,
|
35
|
+
"autocompleteMultiselect"
|
36
|
+
]
|
37
|
+
|
19
38
|
export interface PromptChoice {
|
20
|
-
title: string;
|
21
|
-
value
|
22
|
-
disabled
|
23
|
-
selected
|
39
|
+
title : string;
|
40
|
+
value? : any;
|
41
|
+
disabled? : boolean | undefined;
|
42
|
+
selected? : boolean | undefined;
|
24
43
|
description?: string | undefined;
|
25
44
|
}
|
26
45
|
|
27
|
-
|
28
|
-
|
29
|
-
export interface IPromptableOptions{
|
30
|
-
required?: boolean; // A value must be supplied when the option is specified.
|
31
|
-
optional?: boolean; // A value is optional when the option is specified.
|
32
|
-
default?:PromptParamDefaultValue
|
33
|
-
choices?:(PromptChoice | any)[] // 选项值的可选值
|
34
|
-
prompt?:InputPromptParam
|
35
|
-
validate?:(value: any) => boolean
|
36
|
-
}
|
37
|
-
|
38
|
-
|
39
|
-
export interface IPromptable{
|
40
|
-
name():string
|
41
|
-
description?:string
|
42
|
-
flags:string
|
43
|
-
promptChoices?:PromptChoice[]
|
44
|
-
argChoices?:string[]
|
45
|
-
variadic?:boolean
|
46
|
-
defaultValue?:PromptParamDefaultValue
|
47
|
-
input?:any
|
48
|
-
required?:boolean
|
49
|
-
validate?: (value: any) => boolean
|
50
|
-
getPrompt(inputValue?:any):PromptObject | undefined
|
51
|
-
}
|
52
|
-
|
53
|
-
/**
|
54
|
-
* 供command.option()使用的参数对象
|
55
|
-
*/
|
56
|
-
export interface PromptableObject{
|
57
|
-
|
58
|
-
|
59
|
-
}
|
60
|
-
|
46
|
+
|
47
|
+
export type PromptParams = Omit<PromptObject,'name'> | PromptType | boolean | 'auto' | undefined
|
61
48
|
|
62
49
|
/**
|
63
50
|
* 负责生成prompt对象
|
64
51
|
*
|
65
52
|
*/
|
66
|
-
export class
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
this._promptable = promptable
|
71
|
-
this.args= promptArgs===undefined ? 'auto' : promptArgs
|
53
|
+
export class MixOptionPrompt{
|
54
|
+
params?: PromptParams
|
55
|
+
constructor(public cliOption:MixOption,promptParams?:PromptParams){
|
56
|
+
this.params = promptParams
|
72
57
|
}
|
73
|
-
|
74
58
|
/**
|
75
59
|
* 返回输入的是否是有效的prompt类型
|
76
60
|
* @param type
|
77
61
|
* @returns
|
78
62
|
*/
|
79
|
-
|
63
|
+
isValidPromptType(type:any){
|
80
64
|
return supportedPromptTypes.includes(String(type))
|
81
65
|
}
|
82
66
|
/**
|
83
67
|
* 推断是否需要提示
|
84
68
|
*
|
69
|
+
* 1. 显式指定prompt=true或者提示类型,或者提示对象,则需要提示
|
70
|
+
*
|
71
|
+
*
|
85
72
|
*/
|
86
|
-
|
73
|
+
isNeedPrompt(input:any,defaultValue?:any){
|
87
74
|
|
88
|
-
const
|
75
|
+
const params = this.params
|
76
|
+
|
89
77
|
const inputValue = input || defaultValue
|
78
|
+
|
90
79
|
// 是否有输入值,即在命令行输入了值
|
91
80
|
const hasInput = !(inputValue === undefined)
|
81
|
+
|
92
82
|
// 1. 显式指定了_prompt为true,则需要提示,后续进行提示类型的推断,可能不会准确
|
93
|
-
if(
|
94
|
-
if(
|
83
|
+
if(params === true) return true
|
84
|
+
if(params === false) return false
|
85
|
+
if(params === 'auto') return !hasInput
|
95
86
|
|
96
87
|
// 2. 提供了一个prompt对象,并且没有在命令行输入值,则需要提示
|
97
|
-
if(
|
88
|
+
if(isPlainObject(params)){
|
98
89
|
return !hasInput
|
99
90
|
}
|
100
91
|
|
101
92
|
// 3. 指定了内置的prompt类型,如prompt='password',则使用password类型提示输入
|
102
|
-
if(typeof(
|
103
|
-
return
|
93
|
+
if(typeof(params) == 'string' && this.isValidPromptType(params)){
|
94
|
+
return true
|
104
95
|
}
|
96
|
+
|
97
|
+
// 4. 指定了可选值,但是没有输入值,则需要提示
|
98
|
+
const isOptional = /(\<s*\w\s*\>)/.test(this.cliOption.flags)
|
99
|
+
if(isOptional) return !hasInput
|
105
100
|
|
106
101
|
// 4. 判断输入是否有效,则显示提示
|
107
|
-
if(this.
|
102
|
+
if(this.cliOption.argChoices && this.cliOption.argChoices.indexOf(inputValue) == -1){
|
108
103
|
return true
|
109
104
|
}
|
110
105
|
return !hasInput
|
106
|
+
|
107
|
+
}
|
108
|
+
|
109
|
+
private _getChoices(){
|
110
|
+
let choices:(string | PromptChoice)[] | ((pre:any,answers:any)=>(string | PromptChoice)[]) = []
|
111
|
+
let choicesParam = this.cliOption.params?.choices
|
112
|
+
if(this.cliOption.argChoices){
|
113
|
+
choices = this.cliOption.argChoices.map(choice=>{
|
114
|
+
if(typeof(choice)=='string'){
|
115
|
+
return {title:choice,value:choice}
|
116
|
+
}else{
|
117
|
+
return choice
|
118
|
+
}
|
119
|
+
})
|
120
|
+
}else if(choicesParam){
|
121
|
+
choices = typeof(choicesParam)=='function' ? choicesParam : []
|
122
|
+
}else{
|
123
|
+
return []
|
124
|
+
}
|
125
|
+
return choices
|
126
|
+
}
|
127
|
+
|
128
|
+
/**
|
129
|
+
* 自动推断prompt类型
|
130
|
+
*
|
131
|
+
*
|
132
|
+
*
|
133
|
+
* @param inputValue 从命令行输入的值
|
134
|
+
*/
|
135
|
+
infer(inputValue?:any){
|
136
|
+
|
137
|
+
const { variadic, defaultValue } = this.cliOption
|
138
|
+
|
139
|
+
const input = inputValue || defaultValue
|
140
|
+
|
141
|
+
// 如果选择指定了"-p [value]或[value...]",则使用text类型,如果没有要求输入值,则使用confirm类型
|
142
|
+
let promptType:PromptType = 'text'
|
143
|
+
|
144
|
+
const params = this.params
|
145
|
+
|
146
|
+
if(this.isValidPromptType(params)){ // 显式指定了prompt类型,m则以指定的类型为准
|
147
|
+
promptType = params as PromptType
|
148
|
+
}else if(isPlainObject(params)){ // 显式指定了prompt对象
|
149
|
+
promptType = (params as PromptObject).type as PromptType
|
150
|
+
}else{ // 自动推断prompt类型
|
151
|
+
|
152
|
+
const isListType = /(\[\s*\w+\.\.\.\s*])|(\<\s*\w+\.\.\.\s*>)/.test(this.cliOption.flags)
|
153
|
+
const isTextType = /(\<s*\w+\s*\>)|(\[\w+\])/.test(this.cliOption.flags)
|
154
|
+
const isBooleanType = !/(\[\s*\w+s*])|(\<\s*\w+\s*>)/.test(this.cliOption.flags)
|
155
|
+
const isNumberType = isNumber(defaultValue)
|
156
|
+
const isDate = defaultValue && defaultValue instanceof Date
|
157
|
+
|
158
|
+
// 根据默认值的类型推断
|
159
|
+
const datatype:string = Array.isArray(input) ? 'array' :
|
160
|
+
input instanceof Date ? 'date' :
|
161
|
+
typeof(input)
|
162
|
+
|
163
|
+
const optionParams = this.cliOption.params
|
164
|
+
|
165
|
+
if(optionParams && optionParams.choices){
|
166
|
+
const choices = optionParams.choices
|
167
|
+
if(isBooleanType && (Array.isArray(choices) && choices.length==2)){
|
168
|
+
promptType = 'toggle'
|
169
|
+
}else{
|
170
|
+
promptType = variadic ? 'multiselect' : 'select'
|
171
|
+
}
|
172
|
+
}else if(isListType){ // 提供多个可选值时
|
173
|
+
promptType = 'list'
|
174
|
+
}else if(isDate){
|
175
|
+
promptType = 'date'
|
176
|
+
}else if(isNumberType){
|
177
|
+
promptType = 'number'
|
178
|
+
}else if(isTextType){ // 提供一个可选值时
|
179
|
+
promptType = 'text'
|
180
|
+
}else if(isBooleanType || typeof(defaultValue)==='boolean'){
|
181
|
+
promptType = 'confirm'
|
182
|
+
}else if(datatype in promptTypeMap){
|
183
|
+
promptType = promptTypeMap[datatype] as PromptType
|
184
|
+
}
|
185
|
+
}
|
186
|
+
outputDebug("选项<{}> -> 提示类型<{}>",[this.cliOption.name(),promptType])
|
187
|
+
return promptType
|
111
188
|
}
|
112
189
|
/**
|
113
190
|
* 返回生成prompt对象
|
@@ -115,66 +192,62 @@ export class PromptManager{
|
|
115
192
|
* @param inputValue 从命令行输入的值
|
116
193
|
*/
|
117
194
|
get(inputValue?:any){
|
118
|
-
|
195
|
+
|
196
|
+
const { description, defaultValue } = this.cliOption
|
197
|
+
|
119
198
|
let input = inputValue || defaultValue
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
199
|
+
|
200
|
+
// 1. 判断是否需要启用提示
|
201
|
+
if(!this.isNeedPrompt(input,defaultValue)) return
|
202
|
+
|
203
|
+
// 2. 推断prompt类型
|
204
|
+
const promptType = this.infer(inputValue)
|
205
|
+
|
124
206
|
const prompt = {
|
125
|
-
type:promptType,
|
126
|
-
name:this.
|
127
|
-
message:description,
|
207
|
+
type : promptType,
|
208
|
+
name : this.cliOption.attributeName(),
|
209
|
+
message: description,
|
128
210
|
initial: input,
|
129
|
-
...typeof(this.
|
211
|
+
...typeof(this.params) == 'object' ? this.params : {}
|
130
212
|
} as PromptObject
|
213
|
+
|
214
|
+
|
131
215
|
// 指定了验证函数,用来验证输入值是否有效
|
132
|
-
prompt.validate =
|
216
|
+
prompt.validate = this.cliOption.params?.validate
|
217
|
+
|
133
218
|
if(promptType=='multiselect') prompt.instructions=false
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
219
|
+
prompt.choices = prompt.choices || this._getChoices() as any
|
220
|
+
|
221
|
+
if(['select','multiselect'].includes(promptType)){
|
222
|
+
|
223
|
+
}else if(promptType=='toggle'){
|
224
|
+
if(Array.isArray(prompt.choices)){
|
225
|
+
if(!prompt.active) prompt.active = prompt.choices[0].value
|
226
|
+
if(!prompt.inactive) prompt.inactive = prompt.choices[1].value
|
227
|
+
}
|
141
228
|
}
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
infer(inputValue?:any){
|
150
|
-
const {argChoices,variadic,defaultValue} = this._promptable
|
151
|
-
let input = inputValue || defaultValue
|
152
|
-
// 如果选择指定了"-p [value]或[value...]",则使用text类型,如果没有要求输入值,则使用confirm类型
|
153
|
-
let promptType = /(\<[\w\.]+\>)|(\[[\w\.]+\])/.test(this._promptable.flags) ? 'text' : 'confirm'
|
154
|
-
let promptArg = this.args
|
155
|
-
if(this.isValid(promptArg)){ // 显式指定了prompt类型
|
156
|
-
promptType = promptArg as string
|
157
|
-
}else{ // 未显式指定prompt类型,需要按一定规则推断类型
|
158
|
-
if(typeof(promptArg)=='object'){
|
159
|
-
promptType = promptArg.type as string
|
160
|
-
}else{
|
161
|
-
if(argChoices){ // 提供多个可选值时
|
162
|
-
promptType = variadic ? 'multiselect' : 'select'
|
163
|
-
}else{
|
164
|
-
const datatype:string = Array.isArray(defaultValue) ? 'array' : typeof(defaultValue)
|
165
|
-
// 如果输入值班是数组,则使用list类型,允许使用逗号分隔的多个值
|
166
|
-
if(Array.isArray(input) || variadic){
|
167
|
-
promptType = "list"
|
168
|
-
}else{
|
169
|
-
if(datatype in promptTypeMap){
|
170
|
-
promptType = promptTypeMap[datatype]
|
171
|
-
}
|
229
|
+
|
230
|
+
if(input && typeof(prompt.initial)!='function'){
|
231
|
+
if(prompt.choices && Array.isArray(prompt.choices)){
|
232
|
+
if(promptType=='select'){
|
233
|
+
const index = Array.isArray(prompt.choices) ? prompt.choices.findIndex(item=>item.value==input) : -1
|
234
|
+
if(index!=-1){
|
235
|
+
prompt.initial = index
|
172
236
|
}
|
237
|
+
}else if(promptType=='multiselect'){
|
238
|
+
prompt.choices.forEach((item)=>{
|
239
|
+
if(Array.isArray(input) && input.includes(item.value)){
|
240
|
+
item.selected = true
|
241
|
+
}else if(item.value==input){
|
242
|
+
item.selected = true
|
243
|
+
}
|
244
|
+
})
|
173
245
|
}
|
174
246
|
}
|
175
247
|
}
|
176
|
-
|
177
|
-
return
|
248
|
+
|
249
|
+
return prompt
|
178
250
|
}
|
179
251
|
|
252
|
+
|
180
253
|
}
|
package/src/utils.ts
CHANGED
@@ -1,158 +1,144 @@
|
|
1
|
-
import
|
2
|
-
import
|
3
|
-
import
|
4
|
-
import
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
*
|
9
|
-
*
|
10
|
-
*
|
11
|
-
*
|
12
|
-
*
|
13
|
-
*
|
14
|
-
*
|
15
|
-
*
|
16
|
-
*
|
17
|
-
*
|
18
|
-
*
|
19
|
-
*
|
20
|
-
*
|
21
|
-
*
|
22
|
-
*
|
23
|
-
*
|
24
|
-
* @param
|
25
|
-
*
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
*
|
34
|
-
*
|
35
|
-
* @param
|
36
|
-
* @
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
let
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
*
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
command.option("--
|
58
|
-
command.option("--
|
59
|
-
}
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
*
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
if(!
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
}
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
}
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
}
|
136
|
-
|
137
|
-
}
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
}
|
143
|
-
|
144
|
-
|
145
|
-
export async function importModule(file:string){
|
146
|
-
let module
|
147
|
-
try{
|
148
|
-
module = require(file)
|
149
|
-
}catch(e:any){
|
150
|
-
try{
|
151
|
-
const cmd = await import(`file://${file}`)
|
152
|
-
module = cmd.default
|
153
|
-
}catch(e:any){
|
154
|
-
throw e
|
155
|
-
}
|
156
|
-
}
|
157
|
-
return module
|
158
|
-
}
|
1
|
+
import fs from "fs-extra"
|
2
|
+
import path from "node:path"
|
3
|
+
import { promisify } from "flex-tools/func/promisify"
|
4
|
+
import logsets from "logsets"
|
5
|
+
|
6
|
+
|
7
|
+
/**
|
8
|
+
*
|
9
|
+
* 在控制台输出一个字符串
|
10
|
+
* 本方法会将字符串中的每一行空格去掉
|
11
|
+
*
|
12
|
+
* @remarks
|
13
|
+
*
|
14
|
+
* outputStr(String.raw`
|
15
|
+
* a
|
16
|
+
* b`)
|
17
|
+
*
|
18
|
+
* 会输出
|
19
|
+
* a
|
20
|
+
* b
|
21
|
+
*
|
22
|
+
* 此功能可以用于输出多行字符串时,保持代码的缩进格式,而不会影响输出结果
|
23
|
+
*
|
24
|
+
* @param str : 要输出的字符串
|
25
|
+
* @param vars : 用于替换字符串中的变量
|
26
|
+
*
|
27
|
+
*/
|
28
|
+
export function outputStr(str:string,vars?:Record<string,any> | any[]){
|
29
|
+
logsets.log(fixIndent(str),vars)
|
30
|
+
}
|
31
|
+
|
32
|
+
/**
|
33
|
+
* 修正多行字符串的缩进
|
34
|
+
*
|
35
|
+
* @param text
|
36
|
+
* @param indent
|
37
|
+
* @returns
|
38
|
+
*/
|
39
|
+
export function fixIndent(text:string,indent?:boolean | number):string{
|
40
|
+
let indentValue = (indent==undefined || indent===true) ? 0 : (typeof(indent)=='number' ? indent : -1)
|
41
|
+
if(indentValue==-1) return text // 不修正缩进
|
42
|
+
let lines:string[] = text.split("\n")
|
43
|
+
let minSpaceCount = lines.reduce<number>((minCount,line,index)=>{
|
44
|
+
if(index==0) return minCount
|
45
|
+
const spaceCount = line.match(/^\s*/)?.[0].length || 0
|
46
|
+
return Math.min(minCount,spaceCount)
|
47
|
+
},9999)
|
48
|
+
lines = lines.map(line=>line.substring(minSpaceCount))
|
49
|
+
return lines.join("\n")
|
50
|
+
}
|
51
|
+
|
52
|
+
/**
|
53
|
+
* 增加内置选项
|
54
|
+
* @param command
|
55
|
+
*/
|
56
|
+
export function addBuiltInOptions(command:any){
|
57
|
+
command.option("--work-dirs <values...>","指定工作目录",{hidden:true,optional:true,required:true,prompt:false})
|
58
|
+
command.option("--disable-prompts","禁用所有交互提示",{hidden:true,prompt:false})
|
59
|
+
command.option("--debug-cli","显示调试信息",{hidden:true,prompt:false})
|
60
|
+
}
|
61
|
+
|
62
|
+
|
63
|
+
/**
|
64
|
+
* 是否命令行中包含了--debug-cli选项
|
65
|
+
*/
|
66
|
+
export function isDebug(){
|
67
|
+
return process.argv.includes("--debug-cli")
|
68
|
+
}
|
69
|
+
|
70
|
+
|
71
|
+
export function isDisablePrompts(){
|
72
|
+
return process.argv.includes("--disable-prompts")
|
73
|
+
}
|
74
|
+
|
75
|
+
/**
|
76
|
+
* 打印调试信息
|
77
|
+
* @param message
|
78
|
+
* @param args
|
79
|
+
*/
|
80
|
+
export function outputDebug(message:string,...args:any[]){
|
81
|
+
let vars = (args.length == 1 && typeof(args[0])=='function') ? args[0]() : args
|
82
|
+
if(isDebug()) logsets.log(`[MixCli] ${message}`,...vars)
|
83
|
+
}
|
84
|
+
|
85
|
+
export const fileExists = promisify(fs.exists,{
|
86
|
+
parseCallback:(results)=>{
|
87
|
+
return results[0]
|
88
|
+
}
|
89
|
+
})
|
90
|
+
export const readFile = promisify(fs.readFile)
|
91
|
+
export const writeFile = promisify(fs.writeFile)
|
92
|
+
export const mkdir = promisify(fs.mkdir)
|
93
|
+
|
94
|
+
/**
|
95
|
+
* 创建目录
|
96
|
+
*
|
97
|
+
*
|
98
|
+
*
|
99
|
+
* @param {String[]} dirs 要创建的目录列表,类型为字符串数组
|
100
|
+
* @param callback 创建目录过程中的回调函数,类型为异步函数,接收一个参数 dir,表示当前正在创建的目录
|
101
|
+
* @returns 该函数返回一个 Promise 对象,表示创建目录的操作是否完成
|
102
|
+
*/
|
103
|
+
export async function mkDirs(dirs:string[],{callback,base}:{callback?:Function,base?:string}){
|
104
|
+
if(!Array.isArray(dirs)) throw new Error("dirs参数必须为字符串数组")
|
105
|
+
for(let dir of dirs){
|
106
|
+
if(!path.isAbsolute(dir)) dir = path.join(base || process.cwd(),dir)
|
107
|
+
if(typeof(callback)=='function') callback(dir)
|
108
|
+
await mkdir(dir,{recursive:true})
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
export function showError(e:any){
|
113
|
+
if(isDebug()){
|
114
|
+
outputDebug("导入命令<>出错:{}",e.stack)
|
115
|
+
}else{
|
116
|
+
console.error(e)
|
117
|
+
}
|
118
|
+
|
119
|
+
}
|
120
|
+
|
121
|
+
// 编写一个函数,用于将使用-分隔的字符串转换为驼峰式
|
122
|
+
export function hyphenToCamelCase(str:string){
|
123
|
+
return str.replace(/-([a-z])/g,(_,letter)=>letter.toUpperCase())
|
124
|
+
}
|
125
|
+
|
126
|
+
export function getId(){
|
127
|
+
return Math.random().toString(36).substr(2)
|
128
|
+
}
|
129
|
+
|
130
|
+
|
131
|
+
export async function importModule(file:string){
|
132
|
+
let module
|
133
|
+
try{
|
134
|
+
module = require(file)
|
135
|
+
}catch(e:any){
|
136
|
+
try{
|
137
|
+
const cmd = await import(`file://${file}`)
|
138
|
+
module = cmd.default
|
139
|
+
}catch(e:any){
|
140
|
+
throw e
|
141
|
+
}
|
142
|
+
}
|
143
|
+
return module
|
144
|
+
}
|