lostsource
lostsource

Reputation: 21830

Copy folder recursively in Node.js

Is there an easier way to copy a folder and all its content without manually doing a sequence of fs.readir, fs.readfile, fs.writefile recursively?

I am just wondering if I'm missing a function which would ideally work like this:

fs.copy("/path/to/source/folder", "/path/to/destination/folder");

Regarding this historic question. Note that fs.cp and fs.cpSync can copy folders recursively and are available in Node v16+

Upvotes: 313

Views: 356239

Answers (30)

Jax-p
Jax-p

Reputation: 8531

Since Node v16.7.0 it is possible to use fs.cp or fs.cpSync function.

fs.cp(src, dest, {recursive: true}, (err) => {/* callback */});
// Or
fs.cpSync(src, dest, {recursive: true});

Current stability (in Node v21.1.0) is Experimental.
It is no longer experimental as of v22.3.0.

Upvotes: 156

Aryan
Aryan

Reputation: 3626

UPDATE

As Of node.js v20.13.1 now we can use native file system that has Promise API and callback api with many different type of api's but some of them are still Experimental.

Copy Folder And File (latest-v22.x - Experimental)

const { cp } = require('fs').promises;

async function copyFilesAndDirectories() {
  try {
    // Copy a single file
    await cp('sourceFile.txt', 'destinationFile.txt');
    console.log('sourceFile.txt was copied to destinationFile.txt');

    // Copy a directory recursively
    await cp('sourceDir', 'destinationDir', { recursive: true });
    console.log('sourceDir was copied to destinationDir');
  } catch (err) {
    console.error('Error:', err.message);
  }
}

Other Method cpSync

const { cpSync } = require('fs');

function copyFilesAndDirectoriesSync(callback) {
  try {
    // Copy a single file
    cpSync('sourceFile.txt', 'destinationFile.txt');
    console.log('sourceFile.txt was copied to destinationFile.txt');

    // Copy a directory recursively
    cpSync('sourceDir', 'destinationDir', { recursive: true });
    console.log('sourceDir was copied to destinationDir');

    // If everything goes well, call the callback with no error
    callback(null);
  } catch (err) {
    // If there is an error, call the callback with the error
    callback(err);
  }
}

// Usage
copyFilesAndDirectoriesSync((err) => {
  if (err) {
    console.error('Error:', err.message);
  } else {
    console.log('All files and directories copied successfully');
  }
});

OLD

It looks like ncp and wrench both are no longer maintained. Probably the best option is to use fs-extra

The Developer of Wrench directs users to use fs-extra as he has deprecated his library

copySync & moveSync both will copy and move folders even if they have files or subfolders and you can easily move or copy files using it

const fse = require('fs-extra');

const srcDir = `path/to/file`;
const destDir = `path/to/destination/directory`;
                                 
// To copy a folder or file, select overwrite accordingly
try {
  fse.copySync(srcDir, destDir, { overwrite: true|false })
  console.log('success!')
} catch (err) {
  console.error(err)
}

OR

// To Move a folder or file, select overwrite accordingly
try {
  fs.moveSync(srcDir, destDir, { overwrite: true|false })
  console.log('success!')
} catch (err) {
  console.error(err)
}

Upvotes: 117

otto-null
otto-null

Reputation: 772

Late to the party, but for builds, it's sometimes nice to have options around what to do - this adds three modes:

  • create (fails if target exists)
  • merge (merges files into existing folders if found)
  • replace (hammer time: blows away the target if it exists already)
/**
 Copies the provided folder to a new location.
 In the default mode (`create`), files are copied to the target
 path by default, but other modes to merge and replace are supported
 by setting the `mode` value on the `options` parameter.

 @param {string} from Folder to copy from.
 @param {string} to Folder to copy to.
 @param {{mode:'merge'|'replace'|'create'; }} options Options for the copy:

   `mode` - operation to perform; one of:
     - `merge`: files are merged into any existing folder structure
     - `replace`: any existing folder structure is replaced
     - `create`: (default) the target folder must not yet exist
 */
function copyFolderSync(from, to, options) {
    const {mode} = {
        mode: 'create',
        ...options,
    };

    // If replace, ensure target is gone.
    if (mode === 'replace') {
        fs.rmSync(to, {force:true, recursive: true});
    }

    // If target does not exist, create it
    if (!fs.existsSync(to)) {
        fs.mkdirSync(to);
    }
    else if (mode === 'create') {
        // Can't overwrite - throw
        throw new Error(
            `Cannot copy to ${to} because it exists already.`);
    }
    else if (!fs.lstatSync(to).isDirectory()) {
        // Exists (so op !== 'replace') and is not a folder so can't merge
        throw new Error(
            `Cannot copy to ${to} because it exists already and is not a folder.`);
    }
    fs.readdirSync(from).forEach(itemName => {
        const itemInfo = fs.lstatSync(path.join(from, itemName));
        if (itemInfo.isFile()) {
            fs.copyFileSync(path.join(from, itemName), path.join(to, itemName));
        }
        else if (itemInfo.isDirectory()) {
            copyFolderSync(path.join(from, itemName),
                path.join(to, itemName), options);
        }
    });
}

Upvotes: 0

TamusJRoyce
TamusJRoyce

Reputation: 824

Inline version

node -e "const fs=require('fs');const p=require('path');function copy(src, dest) {if (!fs.existsSync(src)) {return;} if (fs.statSync(src).isFile()) {fs.copyFileSync(src, dest);}else{fs.mkdirSync(dest, {recursive: true});fs.readdirSync(src).forEach(f=>copy(p.join(src, f), p.join(dest, f)));}}const args=Array.from(process.argv); copy(args[args.length-2], args[args.length-1]);" dist temp\dest

or node 16.x+

node -e "const fs=require('fs');const args=Array.from(process.argv); fs.cpSync(args[args.length-2], args[args.length-1], {recursive: true});" 

Tested on "node 14.20.0", but assuming it works on node 10.x?

From user8894303 and mpen's answers: https://stackoverflow.com/a/52338335/458321

Be sure to \" escape quotes if using in package.json script

package.json:

  "scripts": {
    "rmrf": "node -e \"const fs=require('fs/promises');const args=Array.from(process.argv); Promise.allSettled(args.map(a => fs.rm(a, { recursive: true, force: true })));\"",
    "cp": "node -e \"const fs=require('fs');const args=Array.from(process.argv);if (args.length>2){ fs.cpSync(args[args.length-2], args[args.length-1], {recursive: true});}else{console.log('args missing', args);}\""
    "copy": "node -e \"const fs=require('fs');const p=require('path');function copy(src, dest) {if (!fs.existsSync(src)) {return;} if (fs.statSync(src).isFile()) {fs.copyFileSync(src, dest);}else{fs.mkdirSync(dest, {recursive: true});fs.readdirSync(src).forEach(f=>copy(p.join(src, f), p.join(dest, f)));}}const args=Array.from(process.argv);if (args.length>2){copy(args[args.length-2], args[args.length-1]);}else{console.log('args missing', args);}\"",
    "mkdir": "node -e \"const fs=require('fs');const args=Array.from(process.argv);fs.mkdirSync(args[args.length-1],{recursive:true});\"",
    "clean": "npm run rmrf -- temp && npm run mkdir -- temp && npm run copy -- dist temp"
  }

note: rmrf script requires node 14.20.x or 12.20.x?

bonus:

deno eval "import { existsSync, mkdirSync, copyFileSync, readdirSync, statSync } from 'node:fs';import { join } from 'node:path';function copy(src, dest) {if (!existsSync(src)) {return;} if (statSync(src).isFile()) {copyFileSync(src, dest);}else{mkdirSync(dest, {recursive: true});readdirSync(src).forEach(f=>copy(join(src, f), join(dest, f)));}}const args=Array.from(Deno.args);copy(args[0], args[1]);" dist temp\dest -- --allow-read --allow-write

deno support -> npm i deno-bin for deno-bin support in node

Upvotes: 0

Flavio Del Grosso
Flavio Del Grosso

Reputation: 590

This could be a possible solution using async generator function and iterating over with for await loop. This solution includes the possibility to filter out some directories passing them as an optional third array argument.

import path from 'path';
import { readdir, copy } from 'fs-extra';

async function* getFilesRecursive(srcDir: string, excludedDir?: PathLike[]): AsyncGenerator<string> {
  const directoryEntries: Dirent[] = await readdir(srcDir, { withFileTypes: true });
  if (!directoryEntries.length) yield srcDir; // If the directory is empty, return the directory path.
  for (const entry of directoryEntries) {
    const fileName = entry.name;
      const sourcePath = resolvePath(`${srcDir}/${fileName}`);
      if (entry.isDirectory()) {
        if (!excludedDir?.includes(sourcePath)) {
          yield* getFilesRecursive(sourcePath, excludedDir);
        }
      } else {
        yield sourcePath;
      }
  }
}

Then:

for await (const filePath of getFilesRecursive(path, ['dir1', 'dir2'])) {
   await copy(filePath, filePath.replace(path, path2));
}

Upvotes: 1

mpen
mpen

Reputation: 282825

This is pretty easy with Node.js 10:

const Path = require('path');
const FSP = require('fs').promises;

async function copyDir(src,dest) {
    const entries = await FSP.readdir(src, {withFileTypes: true});
    await FSP.mkdir(dest);
    for(let entry of entries) {
        const srcPath = Path.join(src, entry.name);
        const destPath = Path.join(dest, entry.name);
        if(entry.isDirectory()) {
            await copyDir(srcPath, destPath);
        } else {
            await FSP.copyFile(srcPath, destPath);
        }
    }
}

This assumes dest does not exist.

Upvotes: 18

ggorlen
ggorlen

Reputation: 56865

For older node versions that don't have fs.cp, I'm using this in a pinch to avoid requiring a third-party library:

const fs = require("fs").promises;
const path = require("path");

const cp = async (src, dest) => {
  const lstat = await fs.lstat(src).catch(err => false);

  if (!lstat) {
    return;
  }
  else if (await lstat.isFile()) {
    await fs.copyFile(src, dest);
  }
  else if (await lstat.isDirectory()) {
    await fs.mkdir(dest).catch(err => {});

    for (const f of await fs.readdir(src)) {
      await cp(path.join(src, f), path.join(dest, f));
    }
  }
};

// sample usage
(async () => {
  const src = "foo";
  const dst = "bar";

  for (const f of await fs.readdir(src)) {
    await cp(path.join(src, f), path.join(dst, f));
  }
})();

Advantages (or differentiators) relative to existing answers:

  • async
  • ignores symbolic links
  • doesn't throw if a directory already exists (don't catch the mkdir throw if this is undesirable)
  • fairly succinct

Upvotes: 1

Benny Powers
Benny Powers

Reputation: 5836

Since Node v16.7.0:

import { cp } from 'fs/promises';
await cp(
  new URL('../path/to/src/', import.meta.url),
  new URL('../path/to/dest/', import.meta.url), {
    recursive: true,
  }
);

Note carefully the use of recursive: true. This prevents an ERR_FS_EISDIR error.

Read more on the Node Filesystem documentation

Upvotes: 5

stpoa
stpoa

Reputation: 5573

TypeScript version

async function copyDir(source: string, destination: string): Promise<any> {
  const directoryEntries = await readdir(source, { withFileTypes: true });
  await mkdir(destination, { recursive: true });

  return Promise.all(
    directoryEntries.map(async (entry) => {
      const sourcePath = path.join(source, entry.name);
      const destinationPath = path.join(destination, entry.name);

      return entry.isDirectory()
        ? copyDir(sourcePath, destinationPath)
        : copyFile(sourcePath, destinationPath);
    })
  );
}

Upvotes: 3

solosodium
solosodium

Reputation: 733

The current top answer can be greatly simplified.

const path = require('path');
const fs = require('fs');

function recursiveCopySync(source, target) {
  if (fs.lstatSync(source).isDirectory()) {
    if (!fs.existsSync(target)) {
      fs.mkdirSync(target);
    }
    let files = fs.readdirSync(source);
    files.forEach((file) => {
      recursiveCopySync(path.join(source, file), path.join(target, file));
    });
  } else {
    if (fs.existsSync(source)) {
      fs.writeFileSync(target, fs.readFileSync(source));
    }
  }
}

Upvotes: 0

EladTal
EladTal

Reputation: 2856

I wrote this function for both copying (copyFileSync) or moving (renameSync) files recursively between directories:

// Copy files
copyDirectoryRecursiveSync(sourceDir, targetDir);
// Move files
copyDirectoryRecursiveSync(sourceDir, targetDir, true);


function copyDirectoryRecursiveSync(source, target, move) {
    if (!fs.lstatSync(source).isDirectory())
        return;

    var operation = move ? fs.renameSync : fs.copyFileSync;
    fs.readdirSync(source).forEach(function (itemName) {
        var sourcePath = path.join(source, itemName);
        var targetPath = path.join(target, itemName);

        if (fs.lstatSync(sourcePath).isDirectory()) {
            fs.mkdirSync(targetPath);
            copyDirectoryRecursiveSync(sourcePath, targetPath);
        }
        else {
            operation(sourcePath, targetPath);
        }
    });
}

Upvotes: 3

allx
allx

Reputation: 292

The one with symbolic link support:

const path = require("path");
const {
  existsSync,
  mkdirSync,
  readdirSync,
  lstatSync,
  copyFileSync,
  symlinkSync,
  readlinkSync,
} = require("fs");

export function copyFolderSync(src, dest) {
  if (!existsSync(dest)) {
    mkdirSync(dest);
  }

  readdirSync(src).forEach((entry) => {
    const srcPath = path.join(src, entry);
    const destPath = path.join(dest, entry);
    const stat = lstatSync(srcPath);

    if (stat.isFile()) {
      copyFileSync(srcPath, destPath);
    } else if (stat.isDirectory()) {
      copyFolderSync(srcPath, destPath);
    } else if (stat.isSymbolicLink()) {
      symlinkSync(readlinkSync(srcPath), destPath);
    }
  });
}

Upvotes: 7

Liakos
Liakos

Reputation: 572

Use shelljs

npm i -D shelljs

const bash = require('shelljs');
bash.cp("-rf", "/path/to/source/folder", "/path/to/destination/folder");

Upvotes: 3

Ahsan.Amin
Ahsan.Amin

Reputation: 774

If you want to copy all contents of source directory recursively then you need to pass recursive option as true and try catch is documented way by fs-extra for sync

As fs-extra is complete replacement of fs so you don't need to import the base module

const fs = require('fs-extra');
let sourceDir = '/tmp/src_dir';
let destDir = '/tmp/dest_dir';
try {
  fs.copySync(sourceDir, destDir, { recursive: true })
  console.log('success!')
} catch (err) {
  console.error(err)
}

Upvotes: 1

Donovan P
Donovan P

Reputation: 641

Be careful when picking your package. Some packages like copy-dir does not support copying large files more than 0X1FFFFFE8 characters (about 537 MB) long.

It will throw some error like:

buffer.js:630 Uncaught Error: Cannot create a string longer than 0x1fffffe8 characters

I have experienced something like this in one of my projects. Ultimately, I had to change the package I was using and adjust a lot of code. I would say that this is not a very pleasant experience.

If multiple source and multiple destination copies are desired, you can use better-copy and write something like this:

// Copy from multiple source into a directory
bCopy(['/path/to/your/folder1', '/path/to/some/file.txt'], '/path/to/destination/folder');

Or even:

// Copy from multiple source into multiple destination
bCopy(['/path/to/your/folder1', '/path/to/some/file.txt'], ['/path/to/destination/folder', '/path/to/another/folder']);

Upvotes: 3

Emilio Grisol&#237;a
Emilio Grisol&#237;a

Reputation: 1203

If you are on Linux, and performance is not an issue, you may use the exec function from child_process module, to execute a Bash command:

const { exec } = require('child_process');
exec('cp -r source dest', (error, stdout, stderr) => {...});

In some cases, I found this solution cleaner than downloading an entire module or even using the fs module.

Upvotes: 1

Freddy Daniel
Freddy Daniel

Reputation: 651

I know so many answers are already here, but no one answered it in a simple way.

Regarding fs-exra official documentation, you can do it very easy.

const fs = require('fs-extra')

// Copy file
fs.copySync('/tmp/myfile', '/tmp/mynewfile')

// Copy directory, even if it has subdirectories or files
fs.copySync('/tmp/mydir', '/tmp/mynewdir')

Upvotes: 15

fuweichin
fuweichin

Reputation: 1900

I tried fs-extra and copy-dir to copy-folder-recursively. but I want it to

  1. work normally (copy-dir throws an unreasonable error)
  2. provide two arguments in filter: filepath and filetype (fs-extra does't tell filetype)
  3. have a dir-to-subdir check and a dir-to-file check

So I wrote my own:

// Node.js module for Node.js 8.6+
var path = require("path");
var fs = require("fs");

function copyDirSync(src, dest, options) {
  var srcPath = path.resolve(src);
  var destPath = path.resolve(dest);
  if(path.relative(srcPath, destPath).charAt(0) != ".")
    throw new Error("dest path must be out of src path");
  var settings = Object.assign(Object.create(copyDirSync.options), options);
  copyDirSync0(srcPath, destPath, settings);
  function copyDirSync0(srcPath, destPath, settings) {
    var files = fs.readdirSync(srcPath);
    if (!fs.existsSync(destPath)) {
      fs.mkdirSync(destPath);
    }else if(!fs.lstatSync(destPath).isDirectory()) {
      if(settings.overwrite)
        throw new Error(`Cannot overwrite non-directory '${destPath}' with directory '${srcPath}'.`);
      return;
    }
    files.forEach(function(filename) {
      var childSrcPath = path.join(srcPath, filename);
      var childDestPath = path.join(destPath, filename);
      var type = fs.lstatSync(childSrcPath).isDirectory() ? "directory" : "file";
      if(!settings.filter(childSrcPath, type))
        return;
      if (type == "directory") {
        copyDirSync0(childSrcPath, childDestPath, settings);
      } else {
        fs.copyFileSync(childSrcPath, childDestPath, settings.overwrite ? 0 : fs.constants.COPYFILE_EXCL);
        if(!settings.preserveFileDate)
          fs.futimesSync(childDestPath, Date.now(), Date.now());
      }
    });
  }
}
copyDirSync.options = {
  overwrite: true,
  preserveFileDate: true,
  filter: function(filepath, type) {
    return true;
  }
};

And a similar function, mkdirs, which is an alternative to mkdirp:

function mkdirsSync(dest) {
  var destPath = path.resolve(dest);
  mkdirsSync0(destPath);
  function mkdirsSync0(destPath) {
    var parentPath = path.dirname(destPath);
    if(parentPath == destPath)
      throw new Error(`cannot mkdir ${destPath}, invalid root`);
    if (!fs.existsSync(destPath)) {
      mkdirsSync0(parentPath);
      fs.mkdirSync(destPath);
    }else if(!fs.lstatSync(destPath).isDirectory()) {
      throw new Error(`cannot mkdir ${destPath}, a file already exists there`);
    }
  }
}

Upvotes: 3

uyghurbeg
uyghurbeg

Reputation: 310

This is how I did it:

let fs = require('fs');
let path = require('path');

Then:

let filePath = // Your file path

let fileList = []
    var walkSync = function(filePath, filelist)
    {
        let files = fs.readdirSync(filePath);
        filelist = filelist || [];
        files.forEach(function(file)
        {
            if (fs.statSync(path.join(filePath, file)).isDirectory())
            {
                filelist = walkSync(path.join(filePath, file), filelist);
            }
            else
            {
                filelist.push(path.join(filePath, file));
            }
        });

        // Ignore hidden files
        filelist = filelist.filter(item => !(/(^|\/)\.[^\/\.]/g).test(item));

        return filelist;
    };

Then call the method:

This.walkSync(filePath, fileList)

Upvotes: 0

user8894303
user8894303

Reputation:

This is how I would do it personally:

function copyFolderSync(from, to) {
    fs.mkdirSync(to);
    fs.readdirSync(from).forEach(element => {
        if (fs.lstatSync(path.join(from, element)).isFile()) {
            fs.copyFileSync(path.join(from, element), path.join(to, element));
        } else {
            copyFolderSync(path.join(from, element), path.join(to, element));
        }
    });
}

It works for folders and files.

Upvotes: 39

MM Furkan
MM Furkan

Reputation: 53

The easiest approach for this problem is to use only the 'fs' and 'Path' module and some logic...

All files in the root folder copy with the new name if you want to just set the version number, i.e., " var v = 'Your Directory Name'"

In the file name prefix with content added with the file name.

var fs = require('fs-extra');
var path = require('path');

var c = 0;
var i = 0;
var v = "1.0.2";
var copyCounter = 0;
var directoryCounter = 0;
var directoryMakerCounter = 0;
var recursionCounter = -1;
var Flag = false;
var directoryPath = [];
var directoryName = [];
var directoryFileName = [];
var fileName;
var directoryNameStorer;
var dc = 0;
var route;

if (!fs.existsSync(v)) {
    fs.mkdirSync(v);
}

var basePath = path.join(__dirname, v);


function walk(dir) {

    fs.readdir(dir, function(err, items) {

        items.forEach(function(file) {

            file = path.resolve(dir, file);

            fs.stat(file, function(err, stat) {

                if(stat && stat.isDirectory()) {
                    directoryNameStorer = path.basename(file);
                    route = file;
                    route = route.replace("gd", v);

                    directoryFileName[directoryCounter] = route;
                    directoryPath[directoryCounter] = file;
                    directoryName[directoryCounter] = directoryNameStorer;

                    directoryCounter++;
                    dc++;

                    if (!fs.existsSync(basePath + "/" + directoryName[directoryMakerCounter])) {
                        fs.mkdirSync(directoryFileName[directoryMakerCounter]);
                        directoryMakerCounter++;
                    }
                }
                else {
                    fileName = path.basename(file);
                    if(recursionCounter >= 0) {
                        fs.copyFileSync(file, directoryFileName[recursionCounter] + "/" + v + "_" + fileName, err => {
                            if(err) return console.error(err);
                        });
                        copyCounter++;
                    }
                    else {
                        fs.copyFileSync(file, v + "/" + v + "_" + fileName, err => {
                            if(err) return console.error(err);
                        });
                        copyCounter++;
                    }
                }
                if(copyCounter + dc == items.length && directoryCounter > 0 && recursionCounter < directoryMakerCounter-1) {
                    console.log("COPY COUNTER:             " + copyCounter);
                    console.log("DC COUNTER:               " + dc);
                    recursionCounter++;
                    dc = 0;
                    copyCounter = 0;
                    console.log("ITEM DOT LENGTH:          " + items.length);
                    console.log("RECURSION COUNTER:        " + recursionCounter);
                    console.log("DIRECOTRY MAKER COUNTER:  " + directoryMakerCounter);
                    console.log(": START RECURSION:        " + directoryPath[recursionCounter]);
                    walk(directoryPath[recursionCounter]); //recursive call to copy sub-folder
                }
            })
        })
    });
}

walk('./gd', function(err, data) { // Just pass the root directory which you want to copy
    if(err)
        throw err;
    console.log("done");
})

Upvotes: -2

Luckylooke
Luckylooke

Reputation: 4539

Mallikarjun M, thank you!

fs-extra did the thing and it can even return a Promise if you do not provide a callback! :)

const path = require('path')
const fs = require('fs-extra')

let source = path.resolve( __dirname, 'folderA')
let destination = path.resolve( __dirname, 'folderB')

fs.copy(source, destination)
  .then(() => console.log('Copy completed!'))
  .catch( err => {
    console.log('An error occurred while copying the folder.')
    return console.error(err)
  })

Upvotes: 4

Mick
Mick

Reputation: 31919

Yes, ncp is cool though...

You might want/should promisify its function to make it super cool. While you're at it, add it to a tools file to reuse it.

Below is a working version which is Async and uses Promises.


File index.js

const {copyFolder} = require('./tools/');

return copyFolder(
    yourSourcePath,
    yourDestinationPath
)
.then(() => {
    console.log('-> Backup completed.')
}) .catch((err) => {
    console.log("-> [ERR] Could not copy the folder: ", err);
})

File tools.js

const ncp = require("ncp");

/**
 * Promise Version of ncp.ncp()
 *
 * This function promisifies ncp.ncp().
 * We take the asynchronous function ncp.ncp() with
 * callback semantics and derive from it a new function with
 * promise semantics.
 */
ncp.ncpAsync = function (sourcePath, destinationPath) {
  return new Promise(function (resolve, reject) {
      try {
          ncp.ncp(sourcePath, destinationPath, function(err){
              if (err) reject(err); else resolve();
          });
      } catch (err) {
          reject(err);
      }
  });
};

/**
 * Utility function to copy folders asynchronously using
 * the Promise returned by ncp.ncp().
 */
const copyFolder = (sourcePath, destinationPath) => {
    return ncp.ncpAsync(sourcePath, destinationPath, function (err) {
        if (err) {
            return console.error(err);
        }
    });
}
module.exports.copyFolder = copyFolder;

Upvotes: -2

arjun
arjun

Reputation: 1203

The fs-extra module works like a charm.

Install fs-extra:

$ npm install fs-extra

The following is the program to copy a source directory to a destination directory.

// Include the fs-extra package
var fs = require("fs-extra");

var source = 'folderA'
var destination = 'folderB'

// Copy the source folder to the destination
fs.copy(source, destination, function (err) {
    if (err){
        console.log('An error occurred while copying the folder.')
        return console.error(err)
    }
    console.log('Copy completed!')
});

References

fs-extra: https://www.npmjs.com/package/fs-extra

Example: Node.js Tutorial - Node.js Copy a Folder

Upvotes: 23

Abdennour TOUMI
Abdennour TOUMI

Reputation: 93173

For a Linux/Unix OS, you can use the shell syntax

const shell = require('child_process').execSync;

const src = `/path/src`;
const dist = `/path/dist`;

shell(`mkdir -p ${dist}`);
shell(`cp -r ${src}/* ${dist}`);

That's it!

Upvotes: 29

Shahar
Shahar

Reputation: 2347

I created a small working example that copies a source folder to another destination folder in just a few steps (based on shift66's answer using ncp):

Step 1 - Install ncp module:

npm install ncp --save

Step 2 - create copy.js (modify the srcPath and destPath variables to whatever you need):

var path = require('path');
var ncp = require('ncp').ncp;

ncp.limit = 16;

var srcPath = path.dirname(require.main.filename); // Current folder
var destPath = '/path/to/destination/folder'; // Any destination folder

console.log('Copying files...');
ncp(srcPath, destPath, function (err) {
  if (err) {
    return console.error(err);
  }
  console.log('Copying files complete.');
});

Step 3 - run

node copy.js

Upvotes: 9

ModerateJavaScriptDev
ModerateJavaScriptDev

Reputation: 430

This code will work just fine, recursively copying any folder to any location. But it is Windows only.

var child = require("child_process");
function copySync(from, to){
    from = from.replace(/\//gim, "\\");
    to = to.replace(/\//gim, "\\");
    child.exec("xcopy /y /q \"" + from + "\\*\" \"" + to + "\\\"");
}

It works perfectly for my textbased game for creating new players.

Upvotes: 1

Simon Zyx
Simon Zyx

Reputation: 7161

This is my approach to solve this problem without any extra modules. Just using the built-in fs and path modules.

Note: This does use the read / write functions of fs, so it does not copy any meta data (time of creation, etc.). As of Node.js 8.5 there is a copyFileSync function available which calls the OS copy functions and therefore also copies meta data. I did not test them yet, but it should work to just replace them. (See https://nodejs.org/api/fs.html#fs_fs_copyfilesync_src_dest_flags)

var fs = require('fs');
var path = require('path');

function copyFileSync( source, target ) {

    var targetFile = target;

    // If target is a directory, a new file with the same name will be created
    if ( fs.existsSync( target ) ) {
        if ( fs.lstatSync( target ).isDirectory() ) {
            targetFile = path.join( target, path.basename( source ) );
        }
    }

    fs.writeFileSync(targetFile, fs.readFileSync(source));
}

function copyFolderRecursiveSync( source, target ) {
    var files = [];

    // Check if folder needs to be created or integrated
    var targetFolder = path.join( target, path.basename( source ) );
    if ( !fs.existsSync( targetFolder ) ) {
        fs.mkdirSync( targetFolder );
    }

    // Copy
    if ( fs.lstatSync( source ).isDirectory() ) {
        files = fs.readdirSync( source );
        files.forEach( function ( file ) {
            var curSource = path.join( source, file );
            if ( fs.lstatSync( curSource ).isDirectory() ) {
                copyFolderRecursiveSync( curSource, targetFolder );
            } else {
                copyFileSync( curSource, targetFolder );
            }
        } );
    }
}

Upvotes: 87

Lindsey Simon
Lindsey Simon

Reputation: 1021

Here's a function that recursively copies a directory and its contents to another directory:

const fs = require("fs")
const path = require("path")

/**
 * Look ma, it's cp -R.
 * @param {string} src  The path to the thing to copy.
 * @param {string} dest The path to the new copy.
 */
var copyRecursiveSync = function(src, dest) {
  var exists = fs.existsSync(src);
  var stats = exists && fs.statSync(src);
  var isDirectory = exists && stats.isDirectory();
  if (isDirectory) {
    fs.mkdirSync(dest);
    fs.readdirSync(src).forEach(function(childItemName) {
      copyRecursiveSync(path.join(src, childItemName),
                        path.join(dest, childItemName));
    });
  } else {
    fs.copyFileSync(src, dest);
  }
};

Upvotes: 78

jamesmortensen
jamesmortensen

Reputation: 34038

Since I'm just building a simple Node.js script, I didn't want the users of the script to need to import a bunch of external modules and dependencies, so I put on my thinking cap and did a search for running commands from the Bash shell.

This Node.js code snippet recursively copies a folder called node-webkit.app to a folder called build:

child = exec("cp -r node-webkit.app build", function(error, stdout, stderr) {
    sys.print("stdout: " + stdout);
    sys.print("stderr: " + stderr);
    if(error !== null) {
        console.log("exec error: " + error);
    } else {

    }
});

Thanks to Lance Pollard at dzone for getting me started.

The above snippet is limited to Unix-based platforms, like macOS and Linux, but a similar technique may work for Windows.

Upvotes: 2

Related Questions