is enquirer a direct swap for inquirer?
Originally posted by @jondot in https://github.com/enquirer/enquirer/issues/25#issuecomment-437651726
@jondot, it depends on how strictly you mean "direct swap". I'll rephrase in a couple ways.
Can Enquirer do everything that Inquirer does?
Yes, and more.
Does Enquire have all of the same promps as Inquirer?
No, Enquirer does no have the expand prompt. But this would be really easy to create as a custom prompt. I'd consider adding it if there is interest, but I couldn't really think of a reason why someone would use it.
Does Enquirer use all of the same "question" property names as Inquirer?
No. Enquirer supports all of the same features, but we use different names for a couple of things.
| Inquirer | Enquirer |
|---|---|
default |
initial |
filter |
result |
transformer |
format |
when |
skip (opposite of when) |
pageSize |
limit |
suffix |
N/A |
Regarding suffix, Enquirer is much more customizable than Inquirer. You can customize header, prefix, message, separator, hint, error, and footer for the prompt. Additionally, each choice may have a custom pointer (the "arrow" that signifies which choice is currently focused), indicator (checkbox, radio button, etc. which indicates if the choice is enable/checked), message, hint or error message.
Hope this helps. I'm still working on docs.
Also forgot to mention, I think choice objects are a little different. As I recall, Inquirer supports name and value (name is displayed in the terminal, value is returned on answer).
Enquirer supports name, value and message. This allows you to use name as the "key", message to display in the terminal, and value is returned in the answer.
I think we should add a "migration guide" to help who switches from Inquirer to Enquirer.
Thanks all. Currently for the Hygen test suite swapping inquirer with enquirer was replacing just a single character (i replaced with e), and all tests keep passing :)
After reading this there may be other users out there already using more elaborate features of inquirer which will break once they hit the version that has the swap. In this case I'll have to introduce a 2.0.0 version and a breaking API alert (but I think it's worth it for the performance gains).
I have published a package for migrating from Inquirer to Enquirer: GitHub link: https://github.com/g-plane/enquirer-compat npm link: https://npm.im/enquirer-compat
Can I join the "enquirer" organization? If so, I will transfer that repository to this organization.
@g-plane great! I'll review in more depth and add you to the org later today. A couple of things that I noticed:
- Enquirer's equivalent to the checkbox prompt is multiselect
- Enquirer's equivalent to the
pageSizeoption islimit
Thanks! I will update the readme later.
I have published a package for migrating from Inquirer to Enquirer: GitHub link: g-plane/enquirer-compat npm link: npm.im/enquirer-compat
Can I join the "enquirer" organization? If so, I will transfer that repository to this organization.
I have released a new version of enquirer-compat, and now it supports the pageSize option and it can detect the checkbox prompt (convert it to multiselect).
@g-plane that's great! I looked over the package, nice work. however, my suggestion is that you only modify the "question" object and return that, so the user can still use Enquirer the same way.
For example, something like this:
function compat(questions) {
let arr = [].concat(questions || []);
let result = [];
for (let question of arr) {
result.push(normalize(question));
}
return result;
}
function normalize(question) {
let options = { ...question };
for (let key of Object.keys(question)) {
let value = question[key];
if (key === 'when' && typeof value === 'function') {
options.skip = async(...args) => !(await value(...args));
delete options[key];
continue;
}
// etc ...
}
return options;
}
Which can then be used like this:
const compat = require('enquirer-compat');
const { prompt } = require('enquirer');
const questions = [
{
type: 'select',
message: 'What is your favorite color?',
choices: ['Beige', 'Alabaster', 'Cream', 'Off-white']
}
];
prompt(compat(questions))
.then(answers => console.log('Answers:', answers))
.catch(console.error);
The advantage of this is that you only need to update the options object.
edit: I wasn't thinking about the prompt properties, just the options. I'll need to think about that part. I think we need to find a way to let the user control the instances.
@g-plane sent you an invite. (you might get two, I accidentally invited you to this repo only first, then I revoked that and reinvited you to the org).
@jonschlinkert Thanks a lot! I have a question: does Enquirer support a feature like Inquirer that the validate, result and format function can receive the current answers object as a parameter?
Repo was transferred. One more thing, the main goal of enquirer-compat is to provide the same API of Inquirer, however I am considering exporting a function that people can use the function to convert the questions array, while keeping the same API which behaves like Inquirer.
Have just noticed another issue when moving from inquirer to enquirer: the skip / when option doesn't work as expected. In inquirer, you receive an object containing previous answers keyed by the question name. In enquirer, you receive the current questions' name, current questions answer, and no this context.
The values given to skip mean that you can't decide to skip a question based on previous answers.
@ThisIsMissEm I thought I had seen some examples and tests showing how skip is used, but I haven't found what I'm looking for to be able to provide a link.
skip does receive a this context, and the current answers (when using the static Enquirer.prompt method), is found on this.state.answers.
You just need to ensure the skip function that you pass in is not using the "fat arrow" syntax, so it should look something like this:
const { prompt } = require('enquirer');
const questions = [
{ type: 'confirm', name: 'subscribe', message: 'Would you like to subscribe to our newsletter?' },
{
type: 'input',
name: 'email',
message: 'Email address',
skip() {
return this.state.answers.subscribe === false;
}
}
];
prompt(questions).then(console.log).catch(console.error);
After trying that, if you're still having a problem with .skip, feel free to open a new issue with an example of the code you're using.
Yeah, this.state was undefined.
I guess I may have been using arrow syntax without realising it. Would it be better as an API to just pass answers or state in via parameters to the function?
Would it be better as an API to just pass
answersorstatein via parameters to the function?
Yes, you can do this, however the answers object is shallow cloned to avoid mutation.
Here is how it works. Instead of doing this:
const { prompt } = require('enquirer');
You'll need to instantiate Enquirer:
const Enquirer = require('enquirer');
// you can optionally pass an "answers" object as the second argument when instantiating
const options = {};
const answers = { foo: 'bar' };
const enquirer = new Enquirer(options, answers);
(async () => {
await enquirer.prompt({
name: 'username',
type: 'input',
message: 'What is your username?'
});
// however, the answers object you pass won't be updated. it's more useful
// if you want to pre-populate values for skipping prompts, etc.
// You can get the updated answer values on `enquirer.answers`
console.log('ANSWERS:', enquirer.answers);
//=> ANSWERS: { foo: 'bar', username: 'jonschlinkert' }
})();

You can also do this:
const Enquirer = require('enquirer');
const enquirer = new Enquirer();
// Listen for 'prompt' - this is emitted when a new prompt is
// initialized, before the prompt is actually run
enquirer.on('prompt', prompt => {
// listen for the prompt the be submitted by the user
prompt.on('submit', value => {
console.log({ [prompt.name]: value });
});
// you can also listen for "cancel"
prompt.on('cancel', console.error);
});
enquirer.prompt({
name: 'username',
type: 'input',
message: 'What is your username?'
});
Or this, if you want something more terse:
const { prompt } = require('enquirer');
prompt.on('prompt', p => p.on('submit', value => console.log({ [p.name]: value })));
prompt({
name: 'username',
type: 'input',
message: 'What is your username?'
});
@jonschlinkert how does this work with typescript? I'm guessing:
interface ConfigureAnswers {
name: string;
email: string;
github: string;
}
const answers: Partial<ConfigureAnswers> = {};
const enquirer = new Enquirer<ConfigureAnswers>({}, answers);
(async () => {
await enquirer.prompt({
name: 'name',
type: 'input',
message: 'What is your username?'
});
// however, the answers object you pass won't be updated. it's more useful
// if you want to pre-populate values for skipping prompts, etc.
// You can get the updated answer values on `enquirer.answers`
console.log('ANSWERS:', enquirer.answers);
//=> ANSWERS: { name: 'jonschlinkert' }
})();
Though, what will the type be of enquirer.answers?
how does this work with typescript?
I don't use typescript. Sorry maybe someone else can help with that.
@jonschlinkert i maintain a popular repo and am getting grief for not supporting TypeScript. Mind you, our library works just fine with TypeScript, just need to load it in CommonJS format, big deal!?! Props for this candid answer 🤓