Skip to content

Commit

Permalink
feat: use enquirer
Browse files Browse the repository at this point in the history
  • Loading branch information
egoist committed Jan 9, 2019
1 parent 2a76503 commit c2e2c7d
Show file tree
Hide file tree
Showing 13 changed files with 131 additions and 166 deletions.
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const config = {
prompts() {
return [
{
type: 'text',
type: 'input',
name: 'name',
message: 'what is your name'
}
Expand Down Expand Up @@ -68,10 +68,8 @@ const kopy = require('kopy')

test('it works', async () => {
const generator = kopy(config)
const result = await generator.test({
name: 'kevin'
})
expect(result.fileList).toContain('index.js')
await generator.emulate()
expect(generator.answers).toEqual({ name: '' })
})
```

Expand Down
4 changes: 2 additions & 2 deletions __test__/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ test('simple', async () => {
prompts: [
{
name: 'name',
type: 'text',
type: 'input',
message: 'what is your name',
initial: 'kevin'
}
]
})
await generator.test()
await generator.emulate()
expect(generator.answers).toEqual({
name: 'kevin'
})
Expand Down
9 changes: 3 additions & 6 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const config = {
prompts() {
return [
{
type: 'text',
type: 'input',
name: 'name',
message: 'what is your name'
}
Expand Down Expand Up @@ -62,10 +62,7 @@ const kopy = require('kopy')

test('it works', async () => {
const generator = kopy(config)
await generator.test({
// Prompt answers
name: 'kevin'
})
expect(generator.fileList).toContain('index.js')
await generator.emulate()
expect(generator.answers).toEqual({ name: '' })
})
```
2 changes: 1 addition & 1 deletion docs/creating-generators.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const config = {
prompts() {
return [
{
type: 'text',
type: 'input',
name: 'name',
message: 'what is your name'
}
Expand Down
22 changes: 13 additions & 9 deletions docs/generator.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,18 +126,22 @@ interface Opts {

Check out [validateSchema.js](https://github.com/saojs/kopy/blob/master/lib/validateConfig.js) for the underlying schema we use to validate the config.

## generator.test
## generator.emulate

- Type: `(answers: Answers) => Promise<void>`
- Type: `(emulator?: Emulator) => Promise<void>`

Run the generator in test mode.
Emulate running the generator.

### answers
### emulator

- Type: `any[]` `{[name: string]: any}` `boolean`
- Type: `(prompt: enquirer.prompt) => void`

Set it to `true` to use inject initial value to prompts.
By default, running `generator.emulate()` will emulate using the initial values for prompts, the `emulator` it uses looks like:

Or an array with values to inject.

Or an object whose entry is prompt name.
```js
const defaultEmulator = prompt => {
prompt.on('prompt', prompt => {
prompt.submit()
})
}
```
4 changes: 3 additions & 1 deletion docs/prompts.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Prompts

Kopy prompts are a superset of [prompts](https://github.com/terkelg/prompts), we have following addtional properties.
Check out the documentation for [prompts](https://github.com/enquirer/enquirer#prompt-options) in Enquirer.

We also have a few addtional prompt options listed below.

## cache

Expand Down
9 changes: 3 additions & 6 deletions docs/testing-generators.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,16 @@ const config = {
prompts: [
{
name: 'name',
type: 'text',
type: 'input',
message: 'what is your name'
}
]
}

test('it works', async () => {
const generator = kopy(config)
const answers = {
name: 'kevin'
}
await generator.test(answers)
expect(generator.fileList).toContain('index.js')
await generator.emulate()
expect(generator.answers).toEqual({ name: '' })
})
```

Expand Down
2 changes: 1 addition & 1 deletion example/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ const kopy = require('../lib')
const generator = kopy(require('./saofile'))

generator
.run({ outDir: path.join(__dirname, 'dist'), injectAnswers: true })
.run({ outDir: path.join(__dirname, 'dist') })
.catch(generator.handleError)
2 changes: 1 addition & 1 deletion example/saofile.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module.exports = {
prompts() {
return [
{
type: 'text',
type: 'input',
name: 'name',
message: 'what is your name',
cache: true
Expand Down
18 changes: 12 additions & 6 deletions lib/Generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,19 @@ module.exports = class Generator {
}
}

test(answers = true, opts) {
emulate(
emulator = prompt => prompt.on('prompt', prompt => prompt.submit()),
opts
) {
return this.run(
Object.assign({}, opts, {
injectAnswers: answers,
test: true,
logLevel: 1
})
Object.assign(
{
emulator,
test: true,
logLevel: 1
},
opts
)
)
}

Expand Down
106 changes: 28 additions & 78 deletions lib/runPrompts.js
Original file line number Diff line number Diff line change
@@ -1,45 +1,46 @@
const prompts = require('prompts')
const { prompt } = require('enquirer')
const { fs } = require('majo')
const logger = require('./logger')
const KopyError = require('./KopyError')

module.exports = async (questions, generator) => {
const cacheData = await readCacheFile(generator)
const cacheAnswers =
generator.cacheIdentifier && cacheData[generator.cacheIdentifier]

if (cacheAnswers) {
questions = questions.map(q => {
questions = questions.map(q => {
if (cacheAnswers) {
const answer = cacheAnswers[q.name]
if (q.cache && answer !== undefined) {
q.initial = answer
}
return q
})
}
}

if (generator.opts.test) {
q.show = false
}

const { injectAnswers } = generator.opts
// Compability with older version
if (q.type === 'text') {
q.type = 'input'
}

return q
})

if (injectAnswers === true) {
// Use initial values as answers
logger.warn(
"We're automatically answering default value to all questions, which may have security implications."
)
prompts.inject(await getInitialValues(questions))
} else if (Array.isArray(injectAnswers)) {
prompts.inject(await getInitialValues(questions, injectAnswers))
} else if (typeof injectAnswers === 'object') {
prompts.inject(
await getInitialValues(
questions,
Object.keys(questions).map(q => {
return injectAnswers[q.name]
})
)
)
if (typeof generator.opts.emulator === 'function') {
generator.opts.emulator(prompt)
}

const answers = await prompts(questions)
const answers = await prompt(questions)

// Restore cursor
// When using prompt.show enquirer will hide prompts
// But seems it doesn't restore cursor when finished
if (generator.opts.test) {
const stream = process.stdout
if (stream.isTTY) {
stream.write('\u001B[?25h')
}
}

for (const q of questions) {
// In case some questions are skipped
Expand Down Expand Up @@ -69,54 +70,3 @@ async function setCacheAnswers(generator, cacheData, answers) {

await fs.writeFile(generator.cacheFile, JSON.stringify(newData), 'utf8')
}

async function getInitialValues(questions, initialValues) {
const values = [].concat(initialValues || [])
for (const [i, q] of questions.entries()) {
let initial = values[i]
if (initial === undefined) {
if (typeof q.initial === 'function') {
initial = await q.initial(values[i - 1], values, q)
} else {
initial = q.initial
}
}
values[i] = inferInitialValue(q, initial)
}
return values
}

function inferInitialValue(question, initial) {
switch (question.type) {
case 'text':
case 'password':
case 'invisible':
case 'autocomplete': {
return typeof initial === 'string' ? initial : ''
}
case 'list': {
initial = typeof initial === 'string' ? initial : ''
return initial.split(question.separator || ',').map(v => v.trim())
}
case 'number': {
return typeof initial === 'number' ? initial : 0
}
case 'confirm':
case 'toggle': {
return typeof initial === 'boolean' ? initial : true
}
case 'select': {
initial = typeof initial === 'number' ? initial : 0
const choice = question.choices.find((_, i) => {
return i === initial
})
return choice && choice.value
}
case 'multiselect': {
return question.choices.filter(c => c.selected).map(c => c.value)
}
default: {
throw new KopyError(`Unknown prompt type: ${question.type}`)
}
}
}
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@
"devDependencies": {
"@types/fs-extra": "^5.0.4",
"@types/micromatch": "^3.1.0",
"@types/prompts": "^1.2.0",
"builtin-modules": "^3.0.0",
"colorette": "1.0.7",
"commitizen": "^3.0.5",
"cross-spawn": "6.0.5",
"cz-conventional-changelog": "^2.1.0",
"enquirer": "^2.3.0",
"env-paths": "2.0.0",
"eslint-config-prettier": "^3.3.0",
"eslint-config-rem": "^4.0.0",
Expand All @@ -44,7 +44,6 @@
"micromatch": "3.1.10",
"ora": "3.0.0",
"prettier": "1.15.3",
"prompts": "2.0.1",
"rollup": "^1.0.2",
"rollup-plugin-alias": "^1.5.1",
"rollup-plugin-commonjs": "^9.2.0",
Expand Down
Loading

0 comments on commit c2e2c7d

Please sign in to comment.