Brian Jenkins
Brian Jenkins

Reputation: 368

Node.js readline only lets me answer my first set of questions then hangs indefinitely. (Reproducible sample attached!)

I have this program that:

  1. recursively looks for repositories (see recurse())
  2. in each repository, looks for specific dotfiles that I periodically update (see update())
  3. prompts the user to confirm each update (see confirm())
function confirm(prompt, defaultOption = true) {
    console.warn("At confirm");
    return new Promise(function(resolve, reject) {
        console.warn("Question asked");

        readline.question(prompt, async function(answer) {
            console.warn("Question answered");

            if (/^y(es)?$/i.test(answer) || (answer === "" && defaultOption === true)) {
                resolve(true);
            } else if (/^n(o)?$/i.test(answer) || (answer === "" && defaultOption === false)) {
                resolve(false);
            } else {
                resolve(await confirm(prompt, defaultOption));
            }
        });
    });
}

async function update(directory = baseDirectory) {
    console.warn("At update");

    for (const file of [[".eslintrc.json"], ["tsconfig.json"], [".vscode", "extensions.json"], [".vscode", "settings.json"]]) {
        if (fs.existsSync(path.join(directory, file[file.length - 1]))) {
            console.warn("Before confirm");

            console.log("==== " + directory + " ====");

            if (argv["yes"] === true || (await confirm(directory + "\tOverwrite `" + file[file.length - 1] + "`? [Y/n] ")) === true) {
                fs.copyFileSync(path.join(kerplowDirectory, ...file), path.join(directory, ...file));
            }

            console.warn("After confirm");
        }
    }
}

(function recurse(directory = baseDirectory) {
    for (const file of fs.readdirSync(directory)) {
        if (fs.statSync(path.join(directory, file)).isDirectory()) {
            if (file === ".git") {
                console.warn("Before update");

                update(directory);

                console.warn("After update");
            }

            if (file !== "node_modules") {
                recurse(path.join(directory, file));
            }
        }
    }
})();

The problem is twofold:

  1. readline repeatedly asks the first question
  2. It only ever asks the first set of questions and then stops responding to input entirely

Example output:

At update
Before confirm
==== .bash_logout ====
At confirm
Question asked
.bash_logout    Overwrite `.eslintrc.json`? [YAfter update
Before update
At update
Before confirm
==== .bashrc ====
At confirm
Question asked
.bash_logout    Overwrite `.eslintrc.json`? [YAfter update
Before update
At update
Before confirm
==== .cache ====
At confirm
Question asked
.bash_logout    Overwrite `.eslintrc.json`? [YAfter update
Before update
At update
Before confirm
==== .config ====
At confirm
Question asked
.bash_logout    Overwrite `.eslintrc.json`? [YAfter update
Before update
At update
Before confirm
==== .npm ====
At confirm
Question asked
.bash_logout    Overwrite `.eslintrc.json`? [YAfter update
Before update
At update
Before confirm
==== .profile ====
At confirm
Question asked
.bash_logout    Overwrite `.eslintrc.json`? [YAfter update
Before update
At update
Before confirm
==== .upm ====
At confirm
Question asked
.bash_logout    Overwrite `.eslintrc.json`? [YAfter update
Before update
At update
Before confirm
==== config.json ====
At confirm
Question asked
.bash_logout    Overwrite `.eslintrc.json`? [YAfter update
Before update
At update
Before confirm
==== index.js ====
At confirm
Question asked
.bash_logout    Overwrite `.eslintrc.json`? [YAfter update

Question answered
After confirm
Before confirm
==== .bash_logout ====
At confirm
Question asked
.bash_logout    Overwrite `tsconfig.json`? [Y/n]
Question answered
After confirm
Before confirm
==== .bash_logout ====
At confirm
Question asked
.bash_logout    Overwrite `extensions.json`? [Y/n]
Question answered
After confirm
Before confirm
==== .bash_logout ====
At confirm
Question asked
.bash_logout    Overwrite `settings.json`? [Y/n]
Question answered
After confirm


<indefinite hang>

REPL:

https://repl.it/repls/LuminousRapidNaturaldocs

Upvotes: 0

Views: 316

Answers (1)

Sunil Chaudhary
Sunil Chaudhary

Reputation: 4743

Have tried to modified the file and fix the issues. I have copied code from the repl.

There were few issues:

  • You need to return from readline.question after resolve. It was because of this, first question was going in loop
  • recurse also needs to be async so that prompt is asked for one file at a time
const fs = require("fs");
const readline = require("readline").createInterface({
  "input": process.stdin,
  "output": process.stdout
});

function confirm(prompt, defaultOption = true) {
    console.warn("At confirm");

    return new Promise(function(resolve, reject) {
        console.warn("Question asked");

        readline.question(prompt, async function(answer) {
            console.warn("Question answered");

            if (/^y(es)?$/i.test(answer) || (answer === "" && defaultOption === true)) {
                resolve(true);
                return
            } else if (/^n(o)?$/i.test(answer) || (answer === "" && defaultOption === false)) {
                resolve(false);
                return
            } else {
                resolve(await confirm(prompt, defaultOption));
            }
        });
    });
}

async function update(directory) {
    console.warn("At update");

    for (const file of [[".eslintrc.json"], ["tsconfig.json"], [".vscode", "extensions.json"], [".vscode", "settings.json"]]) {
    //  if (fs.existsSync(path.join(directory, file[file.length - 1]))) {
            console.warn("Before confirm");

            console.log("==== " + directory + " ====");

            if (/* argv["yes"] === true || */ (await confirm(directory + "\tOverwrite `" + file[file.length - 1] + "`? [Y/n] ")) === true) {
    //          fs.copyFileSync(path.join(kerplowDirectory, ...file), path.join(directory, ...file));
                // break;
            }

            console.warn("After confirm");
    //  }
    }
    // return
}
console.log(fs.readdirSync(process.cwd()));

(async function recurse(directory) {
    for (const file of fs.readdirSync(directory)) {
    //  if (fs.statSync(path.join(directory, file)).isDirectory()) {
    //      if (file === ".git") {
                console.warn("Before update");

                await update(file);

                console.warn("After update");
    //      }

    //      if (file !== "node_modules") {
    //          recurse(path.join(directory, file));
    //      }
    //  }
    }
})(process.cwd());

The output which I get now is:

[ '.eslintrc.json', 'id.js', 'index.js', 'tsconfig.json' ]
Before update
At update
Before confirm
==== .eslintrc.json ====
At confirm
Question asked
.eslintrc.json  Overwrite `.eslintrc.json`? [Y/nY <= Screen waits here for Y/N
Question answered
After confirm
Before confirm
==== .eslintrc.json ====
At confirm
Question asked
.eslintrc.json  Overwrite `tsconfig.json`? [Y/nY <= Screen waits here for Y/N
Question answered
After confirm
Before confirm
==== .eslintrc.json ====
At confirm
Question asked
.eslintrc.json  Overwrite `extensions.json`? [Y/nY <= Screen waits here for Y/N
Question answered
After confirm
Before confirm
==== .eslintrc.json ====
At confirm
Question asked
.eslintrc.json  Overwrite `settings.json`? [Y/nY <= Screen waits here for Y/N

And it repeats for other files in the local directory

Let me know if this helps. Revert for any doubts.

Upvotes: 2

Related Questions