'use strict';

const fs = require('fs');
const path = require('path');
const execSync = require('child_process').execSync;
const semver = require('semver');
const inquirer = require('inquirer');
const shellQuote = require('shell-quote');

const createNpmBump = ({
    remoteName = 'origin',
    branch = 'master',
    prefix = '',
} = {}) => {
    const UsageError = class extends Error {
        constructor(message) {
            super(message);
            this.name = 'UsageError';
        }
    };

    return releaseType => {
        if (!releaseType) {
            console.error('Error: Release type not provided! ' +
                'Type `npm-bump -h` for usage instructions.');
            process.exitCode = 1;
            return;
        }

        const isPrerelease = !['major', 'minor', 'patch'].includes(releaseType);

        const getHashFor = branchName => {
            try {
                return run(`git rev-parse --verify ${ quote(branchName) }`).trim();
            } catch (error) {
                throw new UsageError(`Git couldn't find the branch: "${
                    branchName }"; please ensure it exists`);
            }
        };

        const isFileTracked = file =>
            run(`git ls-files ${ quote(file) }`).trim().length > 0;

        const ensureCleanBranch = () => {
            if (getHashFor('HEAD') !== getHashFor(branch)) {
                throw new UsageError(`You need to be on the "${
                    branch }" branch to run this script`);
            }
            if (getHashFor(branch) !== getHashFor(`${ remoteName }/${ branch }`)) {
                throw new UsageError('You need to push your changes first');
            }
            if (run('git status -s').length) {
                throw new UsageError(
                    'You have uncommited changes! Commit them before running this script');
            }
        };

        const getRootPath = () => run('git rev-parse --show-cdup').trim();
        const getPackageJsonPath = () => path.join(process.cwd(), `${ getRootPath() }package.json`);
        const getPackageLockJsonPath = () => path.join(
            process.cwd(),
            `${ getRootPath() }package-lock.json`,
        );
        const quote = string => shellQuote.quote([string]);
        const run = (command, options) => execSync(command, {encoding: 'utf8', ...options});
        const writePackageJson = configObject =>
            fs.writeFileSync(getPackageJsonPath(),
                `${ JSON.stringify(configObject, null, 2) }\n`);
        const writePackageLockJson = configObject =>
            fs.writeFileSync(getPackageLockJsonPath(),
                `${ JSON.stringify(configObject, null, 2) }\n`);

        const doBump = () => {
            const packageJson = require(getPackageJsonPath());
            const oldVersion = packageJson.version;
            let packageLockJson;

            // Update package.json & package-lock.json with a new version.
            const newStableVersion = packageJson.version = isPrerelease ?
                semver.inc(oldVersion, 'pre', releaseType) :
                semver.inc(oldVersion, releaseType);

            writePackageJson(packageJson);

            if (fs.existsSync(getPackageLockJsonPath())) {
                packageLockJson = require(getPackageLockJsonPath());
                packageLockJson.version = newStableVersion;
                writePackageLockJson(packageLockJson);
            }

            // Tag a new release.
            console.log(`Version bumped from ${ oldVersion } to ${ newStableVersion }`);
            run(`git add ${ quote(getPackageJsonPath()) }`);
            if (packageLockJson && isFileTracked('package-lock.json')) {
                run(`git add ${ quote(getPackageLockJsonPath()) }`);
            }
            run(`git commit -m ${ quote(`${ prefix } Tag ${ newStableVersion }`) }`);
            run(`git tag ${ quote(newStableVersion) }`);

            // Bump to a new pre-release version but only if the version to publish is not
            // itself a pre-release; otherwise semver gets confused.
            if (!isPrerelease) {
                const newVersion = `${ semver.inc(packageJson.version, 'patch') }-pre`;

                packageJson.version = newVersion;
                writePackageJson(packageJson);
                run(`git add ${ quote(getPackageJsonPath()) }`);

                if (packageLockJson) {
                    packageLockJson.version = newVersion;
                    writePackageLockJson(packageLockJson);
                    if (isFileTracked('package-lock.json')) {
                        run(`git add ${ quote(getPackageLockJsonPath()) }`);
                    }
                }

                run(`git commit -m ${ quote(`${ prefix } Bump to ${ packageJson.version }`) }`);
            }

            const revertChanges = () => {
                run(`git tag -d ${ quote(newStableVersion) }`);
                run(`git reset --hard ${ quote(remoteName) }/${ quote(branch) }`);
                console.log('Changes reverted');
            };

            // All public changes are done here.
            inquirer
                .prompt([{
                    name: 'shouldProceed',
                    type: 'confirm',
                    message: 'Are you sure you want to publish the new version?',
                }])
                .prompt([{
                    name: 'otp',
                    type: 'confirm',
                    message: 'If you use two factor authentication, provide an npm one-time ' +
                        'password. Otherwise, leave empty.',
                }])
                .then(answers => {
                    if (answers.shouldProceed) {
                        // Push & publish the tag.
                        run(`git checkout ${ quote(newStableVersion) }`, {
                            stdio: [process.stdin, process.stdout, 'ignore'],
                        });
                        run(`npm publish ${ quote(getRootPath()) } ${
                            isPrerelease ? `--tag ${ quote(releaseType) }` : ''
                        } ${
                            answers.otp ? `--otp ${ answers.otp }` : ''
                        }`);
                        run(`git push ${ quote(remoteName) } ${ quote(newStableVersion) }`);

                        // Push the latest commit.
                        run(`git checkout ${ quote(branch) }`, {
                            stdio: [process.stdin, process.stdout, 'ignore'],
                        });

                        if (!isPrerelease) {
                            // Force-update the date to prevent two commits having
                            // the same time stamp.
                            const commitMsg = run('git show -s --format=%s');
                            run('git reset --soft HEAD^');
                            run(`git commit -m ${ quote(commitMsg) }`);
                        }

                        run(`git push ${ quote(remoteName) } ${ quote(branch) }`);
                    } else {
                        revertChanges();
                    }
                })
                .catch(err => {
                    console.error('error:', err);
                    process.exitCode = 1;
                    revertChanges();
                });
        };

        ensureCleanBranch();
        doBump();
    };
};

module.exports = createNpmBump();
module.exports.custom = createNpmBump;

@@ -5165,34 +5165,57 @@
        .prompt(%5B
-%7B
 %0A
+                    %7B%0A  
                 
@@ -5214,24 +5214,26 @@
             
+  
 name: 'shoul
@@ -5255,32 +5255,36 @@
                 
+    
 type: 'confirm',
@@ -5276,32 +5276,36 @@
 ype: 'confirm',%0A
+    
                 
@@ -5378,35 +5378,38 @@
                 
-%7D%5D)
+    %7D,
 %0A               
@@ -5413,19 +5413,18 @@
     
-.prompt(%5B%7B%0A
+    %7B%0A    
     
@@ -5464,32 +5464,36 @@
                 
+    
 type: 'confirm',
@@ -5485,32 +5485,36 @@
 ype: 'confirm',%0A
+    
                 
@@ -5589,24 +5589,28 @@
 ne-time ' +%0A
+    
             
@@ -5674,17 +5674,39 @@
         
-%7D
+    %7D,%0A                
 %5D)%0A     

Fix the prompt syntax