-
Notifications
You must be signed in to change notification settings - Fork 16
Expand file tree
/
Copy pathdefault-cli.ts
More file actions
234 lines (214 loc) · 6.49 KB
/
Copy pathdefault-cli.ts
File metadata and controls
234 lines (214 loc) · 6.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
import cac, { CAC } from 'cac'
import { fs } from './fs'
import pathLib from 'path'
import os from 'os'
import { logger } from './logger'
import { Is } from './utils'
import { getGlobalTaskManager } from './task-manager'
import requireStr from 'require-from-string'
export function initDefaultCli() {
const defaultCli = cac()
// generate default argv because cac cannot handle `foy command` + `foy options`
// parse default options
const defaultOptions = [
[`--config, -c <...files>`, 'The Foyfiles'],
[`--require, -r <...names>`, 'Require the given modules'],
[`--executor, -e [name]`, 'Custom executor to replace node, e.g. tsx/ts-node'],
[`--init, -i [ext]`, 'Generate the Foyfile, [ext] can be "ts" | "js", default is "js"'],
[`--completion [words]`, `Generate completion words`],
[`--completion-profile`, `Generate completion shell profile`],
[`--inspect`, `node inspect`],
[`--inspect-brk`, `node inspect`],
] as const
defaultOptions.forEach(([name, desc]) => {
defaultCli.option(name, desc)
})
let parsedArgv =defaultCli.parse(process.argv)
if (defaultCli.options.init) {
let ext = defaultCli.options.init
if (!Is.str(ext)) {
ext = 'js'
}
ext = ext.replace(/^\./, '')
const file = `./Foyfile.${ext}`
if (fs.existsSync(file)) {
throw new Error(`Foyfile already exists: ${pathLib.resolve(file)}`)
}
fs.writeFileSync(
file,
`${
ext === 'js'
? `const { task, desc, option, fs, logger } = require('foy')`
: `import { task, desc, option, fs, logger } from 'foy'`
}
task('build', async ctx => {
logger.info('Running build task')
// Your build tasks
await ctx.exec('echo 1')
})
`,
)
process.exit()
} else if (defaultCli.options.completionProfile) {
console.log(`
###-begin-foy-completions-###
#
# foy command completion script
#
# Installation: foy --completion-profile >> ~/.bashrc
# or foy --completion-profile >> ~/.zshrc
complete -F _foy_complete_func foy
_foy_complete_func()
{
local cur opts
COMPREPLY=()
cur="\${COMP_WORDS[COMP_CWORD]}"
opts="$(foy --completion "\${COMP_WORDS[COMP_CWORD-1]}")"
if [[ \${cur} == * ]] ; then
COMPREPLY=( $(compgen -W "\${opts}" -- \${cur}) )
return 0
fi
}
###-end-foy-completions-###
`)
process.exit()
}
function arrify(arr: any | any[]) {
if (!Array.isArray(arr)) {
return arr == null ? [] : [arr]
}
return arr
}
let foyFiles: string[] = arrify(defaultCli.options.config)
let registers: string[] = arrify(defaultCli.options.require)
if (foyFiles.length) {
foyFiles = foyFiles.map((c) => pathLib.resolve(process.cwd(), c))
// check custom foyfiles exists
for (const file of foyFiles) {
if (!fs.existsSync(file)) {
throw new TypeError(`Cannot find Foyfile: ${file}`)
}
}
} else {
// find default foyfiles
let findFoyfiles = (baseDir: string) => {
let cwdFoyfiles = fs.readdirSync(baseDir).filter((f) => f.startsWith('Foyfile.'))
if (cwdFoyfiles.length) {
if (cwdFoyfiles.length > 1) {
logger.warn(
`Find more than 1 Foyfile in current directory, only first one will be used: \n${cwdFoyfiles.join(
'\n',
)}`,
)
}
foyFiles = [pathLib.join(baseDir, cwdFoyfiles[0])]
}
}
findFoyfiles(process.cwd())
if (!foyFiles.length) {
let maxDepth = 5
let dir = process.cwd()
while (maxDepth-- && dir !== '/' && dir && !foyFiles.length) {
dir = pathLib.dirname(dir)
findFoyfiles(dir)
}
}
}
// add custom registers
// if (registers.length) {
// for (let mod of registers) {
// try {
// require(mod)
// } catch (error) {
// require(pathLib.resolve(process.cwd(), mod))
// }
// }
// }
// add ts-node registry for ts foyfile
function isESM() {
try {
let pkg = fs.readJsonSync('./package.json')
return pkg.type === 'module'
} catch (e) {
return false
}
}
const tsnodeOptions = {
transpileOnly: true,
compilerOptions: {
module: 'commonjs',
esModuleInterop: true,
moduleResolution: 'node',
sourceMap: true,
},
}
// if (foyFiles.some((f) => f.endsWith('.ts')) && !require.extensions['.ts']) {
// // make ts-node show errors if no register
// require('ts-node').register(tsnodeOptions)
// }
// {
// // Add global installed foy to module.paths if using global foy
// const Module = require('module')
// const nodeModulePaths = Module._nodeModulePaths
// const globalFoyPath = pathLib.join(__dirname, '..', '..')
// if (nodeModulePaths) {
// Module._nodeModulePaths = (...args) => {
// let paths = nodeModulePaths.apply(Module, args)
// if (Array.isArray(paths)) {
// paths.push(globalFoyPath)
// }
// return paths
// }
// }
// }
// load foyfiles
// {
// let tsnode: any
// for (const file of foyFiles) {
// if (isESM()) {
// // use tsnode to transpile ESM
// if (!tsnode) {
// tsnode = require('ts-node').create(tsnodeOptions)
// }
// const code = tsnode.compile(fs.readFileSync(file, 'utf-8'), file)
// requireStr(code, file)
// } else {
// require(file)
// }
// }
// }
const defaultHelpMsg = defaultOptions
.map(
([name, desc]) =>
` ${name}:${Array(30 - name.length)
.fill(' ')
.join('')}${desc}`,
)
.join('\n')
defaultCli.help(c=>{
console.log(c)
})
function outputCompletion(taskCli: CAC) {
let completVal = defaultCli.options.completion
if (!completVal) return
if (completVal === 'foy') {
completVal = ''
}
function getOptionComplets(cmd: string) {
return taskCli.commands
.find((c) => c.name === cmd)
?.options?.map((o) => '--' + o.name)
.join(' ')
}
const cmdComplets = taskCli.commands.map((c) => c.name)
// .concat(defaultOptions.map((d) => d[0].split(/[,\s]/)[0]).filter(v => !v.includes('completion')))
if (completVal && cmdComplets.includes(completVal)) {
console.log(getOptionComplets(completVal))
} else {
console.log(cmdComplets.join(' '))
}
process.exit()
}
const taskArgs = process.argv.slice(0, 2).concat(process.argv.slice(process.argv.findIndex(a=> a===parsedArgv.args[0])))
return { defaultHelpMsg, defaultCli, outputCompletion, registers, foyFiles, taskArgs }
}