-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
114 lines (97 loc) · 2.89 KB
/
index.js
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
'use strict'
const chalk = require('chalk')
const ora = require('ora')
const fs = require('fs-extra')
const home = require('user-home')
const Path = require('path')
const Repl = require('repl')
const mandatory = name => {
throw new Error(`Missing option "${name}"`)
}
const defaultWelcome = ({ name = mandatory('name'), locals = {} }) => chalk`{dim
Welcome to {bold ${name}}’s REPL.
- Async operations hold the output until result
- Result of previous command (sync or async) is stored in {italic _}
- Global Node.js modules are available, {italic fs} is actually {italic fs-extra}
${locals && Object.keys(locals).length > 0 ? `- Additional variables: ${Object.keys(locals).join(', ')}
` : ''}
}`
const defaultHistoryFileName = ({ name = mandatory('name') }) =>
`.${name}_repl_history`
const init = ({
name,
historyFileName = defaultHistoryFileName({ name }),
historyFileDir = home,
historyFilePath = historyFileName && Path.join(historyFileDir, historyFileName),
historySize = 20,
locals = {},
welcome = defaultWelcome({ name, locals }),
stdout = process.stdout,
promptPrefix = ((x = mandatory('name')) => x)(name),
promptSuffix = '❯',
prompt = chalk`{dim ${promptPrefix}}{dim.bold ${promptSuffix}} `,
replOptions = {},
} = {}) => {
if (!replOptions.prompt) {
replOptions.prompt = prompt
}
const replLocals = Object.assign({ fs }, locals)
stdout.write(welcome + '\n')
const repl = Repl.start(replOptions)
repl.eval = asyncEval(repl.eval)
if (historyFilePath && historySize > 0) {
if (fs.existsSync(historyFilePath)) {
readHistory(repl, historyFilePath)
}
repl.on('exit', () => saveHistory(repl, historyFilePath, historySize))
}
Object.assign(repl.context, replLocals)
return repl
}
const asyncEval = _eval => (cmd, context, fileName, cb) =>
_eval(cmd, context, fileName, (err, value) => {
if (err) {
return cb(err)
}
if (value && typeof value.then === 'function') {
const spinner = ora(chalk.dim('Pending async operation…')).start()
value.then(
v => {
spinner.succeed(chalk.dim('Value available as _'))
cb(undefined, v)
},
err => {
spinner.fail(chalk.dim('Operation failed'))
cb(err)
},
)
} else {
cb(err, value)
}
})
const readHistory = (repl, historyFile) =>
fs
.readFileSync(historyFile, 'utf-8')
.split('\n')
.reverse()
.filter(line => line.trim())
.forEach(line => repl.history.push(line))
const saveHistory = (repl, historyFile, historySize) =>
fs.writeFileSync(
historyFile,
repl.history
.slice()
.reverse()
.slice(-historySize)
.map(line => line.trim())
.filter(line => line)
.join('\n'),
)
// Exposed API
module.exports = init
Object.assign(module.exports, {
init,
// Expose defaults to allow re-using them
defaultWelcome,
defaultHistoryFileName,
})