User Input
This section shows how to handle user input through prompts and CLI arguments.
Prompting
As we have seen before, the prompt task allows to ask questions from the command line using the Inquirer library. It gets passed a list of Inquirer questions that can also be put together based on the current context. This can be used to e.g. ask questions conditionally or skip them if the value is already passed (e.g. in an automated test):
import {
PinionContext,
prompt,
renderTemplate,
toFile
} from '@featherscloud/pinion'
// Setup the Context to receive user input
interface Context extends PinionContext {
name: string
description: string
}
// The template uses Context variables.
function readme({ name, description }: Context) {
return `# ${name}
> ${description}
This is a readme generated by Pinion
Copyright (c) ${new Date().getFullYear()}
`
}
export function generate(init: Context) {
return Promise.resolve(init)
// Ask prompts (using Inquirer)
.then(
prompt((context) => {
// Only ask question if `name` or `description` are not passed
return {
name: {
type: 'input',
message: 'What is the name of your app?',
when: !context.name
},
description: {
type: 'input',
message: 'Write a short description',
when: !context.description
}
}
})
)
// Render the template
.then(renderTemplate(readme, toFile('readme.md')))
}
npx pinion generators/readme.tpl.ts
CLI arguments
The CLI arguments passed to a generator are available as context.argv
and include any arguments after the generator filename. For example when running something like
npx pinion generators/readme.ts --description hello something
context.argv
would be ['--description', 'hello', 'something']
. You can parse the list yourself and Pinion also ships with a commander task that uses the commander
module to create command-line interfaces. The following example adds --name
and --description
command line arguments and skips prompting the user if it is passed.
import {
Command,
PinionContext,
commander,
prompt,
renderTemplate,
toFile
} from '@featherscloud/pinion'
const program = new Command()
.description('A readme generator')
.option('-n, --name <name>', 'Name of your app')
.option('-d, --description <description>', 'The description for your app')
// Setup the Context to receive user input
interface Context extends PinionContext {
name: string
description: string
}
// The template uses Context variables.
function readme({ name, description }: Context) {
return `# ${name}
> ${description}
This is a readme generated by Pinion
Copyright (c) ${new Date().getFullYear()}
`
}
export function generate(init: Context) {
return Promise.resolve(init)
// Parse command line arguments
.then(commander(program))
// Ask prompts (using Inquirer)
.then(
prompt((context) => {
// Only ask question if `name` or `description` are not passed
return {
name: {
type: 'input',
message: 'What is the name of your app?',
when: !context.name
},
description: {
type: 'input',
message: 'Write a short description',
when: !context.description
}
}
})
)
// Render the template
.then(renderTemplate(readme, toFile('readme.md')))
}
npx pinion generators/readme.tpl.ts --name Test --description "The description from the command line"
What's next
This are all the steps necessary to build a generator. Head over to the API documentation for a full list of all available tasks. The composability chapter described how to call other generators, how to write tests and how they can be embedded in your own tools.