Search code examples
node.jsdirectoryshelljs

Using nodejs to copy a folder and add a suffix to its name when folder with matching name already exists in the destination


I'm calling a Feathers JS API to copy a folder with some files. Say my folder name is 'Website1'.

The normal behavior of Linux is, it appends the new folder name as 'Website1 copy' and further as 'Website1 another copy', 'Website1 3rd copy', and so on.

Can this be achieved with ShellJS?

My Code:

function after_clone_website(hook) {
  return new Promise((resolve, reject) => {
    let sourceProjectName = hook.params.query.sourceProjectName;
    let destinationProjectName = sourceProjectName + '_copy';

    let userDetailId = hook.params.query.userDetailId;

    let response = '';

    response = shell.cp('-Rf', config.path + userDetailId + '/' + 
        sourceProjectName, config.path + userDetailId + '/' +
        destinationProjectName);

    hook.result = response;
    resolve(hook)

  });
}

Solution

  • ShellJS does not include built-in logic to simulate the same behaviour of Linux. I.e. which is to append; ...copy, ...another copy, ...3rd copy, ...4th copy, etc, to the folder name when a folder at the destination path already exists with the same name as the source folder being copied).

    Solution:

    You could utilize the ShellJS test() method with the -e option to check whether each potential variation of the path exists. If it does exist then run your own custom logic to determine what the correct destination path value in the cp() method should be.

    What should that custom logic be?

    The following gist includes a custom copyDirAndRenameIfExists() function which accepts two arguments; the path to the source folder, and the path to the destination folder (very similar to how the ShellJS cp() function works).

    var path = require('path'),
        shell = require('shelljs');
    
    /**
     * Copies a given source folder to a given destination folder.
     *
     * To avoid potentially overwriting an exiting destination folder, (i.e. in the
     * scenario whereby a folder already exists in the destination folder with the
     * same name as the source folder), the source folder will be renamed following
     * normal Linux behaviour. I.e. One of the following values will be appended as
     * appropriate: `copy`, `another copy`, `3rd copy`, `4th copy`, etc.
     *
     * @param {String} srcDir - Path to the source directory to copy.
     * @param {String} destDir - Path to the destination directory.
     * @returns {Object} Object returned from invoking the shelljs `cp()` method.
     */
    function copyDirAndRenameIfExists(srcDir, destDir) {
        var dirName = path.basename(srcDir),
            newDirName = '',
            hasCopied = false,
            counter = 0,
            response = {};
    
        /**
         * Helper function suffixes cardinal number with relevent ordinal
         * number suffix. I.e. st, nd, rd, th
         * @param {Number} number - The number to suffix.
         * @returns {String} A number with its ordinal number suffix.
         */
        function addOrdinalSuffix(number) {
            var j = number % 10,
                k = number % 100;
    
            if (j === 1 && k !== 11) {
                return number + 'st';
            }
            if (j === 2 && k !== 12) {
                return number + 'nd';
            }
            if (j === 3 && k !== 13) {
                return number + 'rd';
            }
            return number + 'th';
        }
    
        /**
         * Helper function to get the appropriate folder name suffix.
         * @param {Number} num - The current loop counter.
         * @returns {String} The appropriate folder name suffix.
         */
        function getSuffix(number) {
            if (number === 1) {
                return ' copy';
            }
            if (number === 2) {
                return ' another copy';
            }
            return ' ' + addOrdinalSuffix(number) + ' copy';
        }
    
        /**
         * Helper function copies the source folder recursively to the destination
         * folder if the source directory does not already exist at the destination.
         */
        function copyDir(srcDir, destDir) {
            if (!shell.test('-e', destDir)) {
                response = shell.cp('-R', srcDir, destDir);
                hasCopied = true;
            }
        }
    
        // Continuously invokes the copyDir() function
        // until the source folder has been copied.
        do {
            if (counter === 0) {
                copyDir(srcDir, path.join(destDir, dirName));
            } else {
                newDirName = dirName + getSuffix(counter);
                copyDir(srcDir, path.join(destDir, newDirName));
            }
            counter += 1;
    
        } while (!hasCopied);
    
        return response;
    }
    

    Implementation

    1. Add the copyDirAndRenameIfExists() function from the gist provided (above) to your existing node program.

    2. Make sure you also require nodes built-in path module, (in addition to shelljs), at start of your program (if you don't have it already). I.e. Add the following:

        var path = require('path');
    
    1. Finally invoke the custom copyDirAndRenameIfExists() function by changing the line of code provided in your question that reads:

      response = shell.cp('-Rf', config.path + userDetailId + '/' +
          sourceProjectName, config.path + userDetailId + '/' + destinationProjectName);
      

      to the following instead:

      response = copyDirAndRenameIfExists(
        path.join(config.path, userDetailId, sourceProjectName),
        path.join(config.path, userDetailId, destinationProjectName)
      };