mixcli 3.2.0 → 3.2.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.
- 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
|
+
}
|