Reputation: 21830
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
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
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
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
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
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
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
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:
mkdir
throw if this is undesirable)Upvotes: 1
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
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
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
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
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
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
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
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
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
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
Reputation: 1900
I tried fs-extra and copy-dir to copy-folder-recursively. but I want it to
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
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
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
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
Reputation: 4539
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
Reputation: 31919
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
.
const {copyFolder} = require('./tools/');
return copyFolder(
yourSourcePath,
yourDestinationPath
)
.then(() => {
console.log('-> Backup completed.')
}) .catch((err) => {
console.log("-> [ERR] Could not copy the folder: ", err);
})
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
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!')
});
fs-extra: https://www.npmjs.com/package/fs-extra
Example: Node.js Tutorial - Node.js Copy a Folder
Upvotes: 23
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
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
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
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
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
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