[Pkg-javascript-commits] [node-ncp] 01/04: Imported Upstream version 0.5.1

Andrew Kelley andrewrk-guest at moszumanska.debian.org
Sat Jun 28 07:10:07 UTC 2014


This is an automated email from the git hooks/post-receive script.

andrewrk-guest pushed a commit to branch master
in repository node-ncp.

commit 34dc9d38e7d699d4b55b2ecc4db4f3e4aca1eb47
Author: Andrew Kelley <superjoe30 at gmail.com>
Date:   Sat Jun 28 06:25:48 2014 +0000

    Imported Upstream version 0.5.1
---
 .gitignore              |   4 +
 .travis.yml             |   6 ++
 LICENSE.md              |  21 +++++
 README.md               |  58 ++++++++++++
 bin/ncp                 |  48 ++++++++++
 lib/ncp.js              | 230 ++++++++++++++++++++++++++++++++++++++++++++++++
 package.json            |  30 +++++++
 test/fixtures/src/a     |   1 +
 test/fixtures/src/b     |   1 +
 test/fixtures/src/c     |   0
 test/fixtures/src/d     |   0
 test/fixtures/src/e     |   0
 test/fixtures/src/f     |   0
 test/fixtures/src/sub/a |   1 +
 test/fixtures/src/sub/b |   0
 test/ncp.js             |  86 ++++++++++++++++++
 16 files changed, 486 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9ecd205
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+node_modules
+.*.sw[op]
+.DS_Store
+test/fixtures/out
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..a6e2198
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,6 @@
+language: node_js
+
+node_js:
+  - 0.8
+  - "0.10"
+  - "0.11"
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..e2b9b41
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,21 @@
+# MIT License
+
+###Copyright (C) 2011 by Charlie McConnell
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..71dca8f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,58 @@
+# ncp - Asynchronous recursive file & directory copying
+
+[![Build Status](https://secure.travis-ci.org/AvianFlu/ncp.png)](http://travis-ci.org/AvianFlu/ncp)
+
+Think `cp -r`, but pure node, and asynchronous.  `ncp` can be used both as a CLI tool and programmatically.
+
+## Command Line usage
+
+Usage is simple: `ncp [source] [dest] [--limit=concurrency limit]
+[--filter=filter] --stopOnErr`
+
+The 'filter' is a Regular Expression - matched files will be copied.
+
+The 'concurrency limit' is an integer that represents how many pending file system requests `ncp` has at a time.
+
+'stoponerr' is a boolean flag that will tell `ncp` to stop immediately if any
+errors arise, rather than attempting to continue while logging errors. The default behavior is to complete as many copies as possible, logging errors along the way.
+
+If there are no errors, `ncp` will output `done.` when complete.  If there are errors, the error messages will be logged to `stdout` and to `./ncp-debug.log`, and the copy operation will attempt to continue.
+
+## Programmatic usage
+
+Programmatic usage of `ncp` is just as simple.  The only argument to the completion callback is a possible error.  
+
+```javascript
+var ncp = require('ncp').ncp;
+
+ncp.limit = 16;
+
+ncp(source, destination, function (err) {
+ if (err) {
+   return console.error(err);
+ }
+ console.log('done!');
+});
+```
+
+You can also call ncp like `ncp(source, destination, options, callback)`. 
+`options` should be a dictionary. Currently, such options are available:
+
+  * `options.filter` - a `RegExp` instance, against which each file name is
+  tested to determine whether to copy it or not, or a function taking single
+  parameter: copied file name, returning `true` or `false`, determining
+  whether to copy file or not.
+
+  * `options.transform` - a function: `function (read, write) { read.pipe(write) }`
+  used to apply streaming transforms while copying.
+
+  * `options.clobber` - boolean=true. if set to false, `ncp` will not overwrite 
+  destination files that already exist.
+
+  * `options.stopOnErr` - boolean=false.  If set to true, `ncp` will behave like `cp -r`,
+  and stop on the first error it encounters. By default, `ncp` continues copying, logging all
+  errors and returning an array.
+
+  * `options.errs` - stream. If `options.stopOnErr` is `false`, a stream can be provided, and errors will be written to this stream.
+
+Please open an issue if any bugs arise.  As always, I accept (working) pull requests, and refunds are available at `/dev/null`.
diff --git a/bin/ncp b/bin/ncp
new file mode 100755
index 0000000..388eaba
--- /dev/null
+++ b/bin/ncp
@@ -0,0 +1,48 @@
+#!/usr/bin/env node
+
+
+
+
+var ncp = require('../lib/ncp'),
+    args = process.argv.slice(2),
+    source, dest;
+
+if (args.length < 2) {
+  console.error('Usage: ncp [source] [destination] [--filter=filter] [--limit=concurrency limit]');
+  process.exit(1);
+}
+
+// parse arguments the hard way
+function startsWith(str, prefix) {
+  return str.substr(0, prefix.length) == prefix;
+}
+
+var options = {};
+args.forEach(function (arg) {
+  if (startsWith(arg, "--limit=")) {
+    options.limit = parseInt(arg.split('=', 2)[1], 10);
+  }
+  if (startsWith(arg, "--filter=")) {
+    options.filter = new RegExp(arg.split('=', 2)[1]);
+  }
+  if (startsWith(arg, "--stoponerr")) {
+    options.stopOnErr = true;
+  }
+});
+
+ncp.ncp(args[0], args[1], options, function (err) {
+  if (Array.isArray(err)) {
+    console.error('There were errors during the copy.');
+    err.forEach(function (err) {
+      console.error(err.stack || err.message);
+    });
+    process.exit(1);
+  }
+  else if (err) {
+    console.error('An error has occurred.');
+    console.error(err.stack || err.message);
+    process.exit(1);
+  }
+});
+
+
diff --git a/lib/ncp.js b/lib/ncp.js
new file mode 100644
index 0000000..f0d9f6d
--- /dev/null
+++ b/lib/ncp.js
@@ -0,0 +1,230 @@
+var fs = require('fs'),
+    path = require('path');
+
+module.exports = ncp;
+ncp.ncp = ncp;
+
+function ncp (source, dest, options, callback) {
+  var cback = callback;
+
+  if (!callback) {
+    cback = options;
+    options = {};
+  }
+
+  var basePath = process.cwd(),
+      currentPath = path.resolve(basePath, source),
+      targetPath = path.resolve(basePath, dest),
+      filter = options.filter,
+      transform = options.transform,
+      clobber = options.clobber !== false,
+      errs = null,
+      eventName = /^v0\.10\.\d+$/.test(process.version) ? 'finish' : 'close',
+      started = 0,
+      finished = 0,
+      running = 0,
+      limit = options.limit || ncp.limit || 16;
+
+  limit = (limit < 1) ? 1 : (limit > 512) ? 512 : limit;
+
+  startCopy(currentPath);
+  
+  function startCopy(source) {
+    started++;
+    if (filter) {
+      if (filter instanceof RegExp) {
+        if (!filter.test(source)) {
+          return cb(true);
+        }
+      }
+      else if (typeof filter === 'function') {
+        if (!filter(source)) {
+          return cb(true);
+        }
+      }
+    }
+    return getStats(source);
+  }
+
+  function defer(fn) {
+    if (typeof(setImmediate) === 'function')
+      return setImmediate(fn);
+    return process.nextTick(fn);
+  }
+
+  function getStats(source) {
+    if (running >= limit) {
+      return defer(function () {
+        getStats(source);
+      });
+    }
+    running++;
+    fs.lstat(source, function (err, stats) {
+      var item = {};
+      if (err) {
+        return onError(err);
+      }
+
+      // We need to get the mode from the stats object and preserve it.
+      item.name = source;
+      item.mode = stats.mode;
+
+      if (stats.isDirectory()) {
+        return onDir(item);
+      }
+      else if (stats.isFile()) {
+        return onFile(item);
+      }
+      else if (stats.isSymbolicLink()) {
+        // Symlinks don't really need to know about the mode.
+        return onLink(source);
+      }
+    });
+  }
+
+  function onFile(file) {
+    var target = file.name.replace(currentPath, targetPath);
+    isWritable(target, function (writable) {
+      if (writable) {
+        return copyFile(file, target);
+      }
+      if(clobber) {
+        rmFile(target, function () {
+          copyFile(file, target);
+        });
+      } else {
+        return cb();
+      }
+    });
+  }
+
+  function copyFile(file, target) {
+    var readStream = fs.createReadStream(file.name),
+        writeStream = fs.createWriteStream(target, { mode: file.mode });
+    if(transform) {
+      transform(readStream, writeStream,file);
+    } else {
+      readStream.pipe(writeStream);
+    }
+    writeStream.once(eventName, cb);
+  }
+
+  function rmFile(file, done) {
+    fs.unlink(file, function (err) {
+      if (err) {
+        return onError(err);
+      }
+      return done();
+    });
+  }
+
+  function onDir(dir) {
+    var target = dir.name.replace(currentPath, targetPath);
+    isWritable(target, function (writable) {
+      if (writable) {
+        return mkDir(dir, target);
+      }
+      copyDir(dir.name);
+    });
+  }
+
+  function mkDir(dir, target) {
+    fs.mkdir(target, dir.mode, function (err) {
+      if (err) {
+        return onError(err);
+      }
+      copyDir(dir.name);
+    });
+  }
+
+  function copyDir(dir) {
+    fs.readdir(dir, function (err, items) {
+      if (err) {
+        return onError(err);
+      }
+      items.forEach(function (item) {
+        startCopy(dir + '/' + item);
+      });
+      return cb();
+    });
+  }
+
+  function onLink(link) {
+    var target = link.replace(currentPath, targetPath);
+    fs.readlink(link, function (err, resolvedPath) {
+      if (err) {
+        return onError(err);
+      }
+      checkLink(resolvedPath, target);
+    });
+  }
+
+  function checkLink(resolvedPath, target) {
+    isWritable(target, function (writable) {
+      if (writable) {
+        return makeLink(resolvedPath, target);
+      }
+      fs.readlink(target, function (err, targetDest) {
+        if (err) {
+          return onError(err);
+        }
+        if (targetDest === resolvedPath) {
+          return cb();
+        }
+        return rmFile(target, function () {
+          makeLink(resolvedPath, target);
+        });
+      });
+    });
+  }
+
+  function makeLink(linkPath, target) {
+    fs.symlink(linkPath, target, function (err) {
+      if (err) {
+        return onError(err);
+      }
+      return cb();
+    });
+  }
+
+  function isWritable(path, done) {
+    fs.lstat(path, function (err) {
+      if (err) {
+        if (err.code === 'ENOENT') return done(true);
+        return done(false);
+      }
+      return done(false);
+    });
+  }
+
+  function onError(err) {
+    if (options.stopOnError) {
+      return cback(err);
+    }
+    else if (!errs && options.errs) {
+      errs = fs.createWriteStream(options.errs);
+    }
+    else if (!errs) {
+      errs = [];
+    }
+    if (typeof errs.write === 'undefined') {
+      errs.push(err);
+    }
+    else { 
+      errs.write(err.stack + '\n\n');
+    }
+    return cb();
+  }
+
+  function cb(skipped) {
+    if (!skipped) running--;
+    finished++;
+    if ((started === finished) && (running === 0)) {
+      if (cback !== undefined ) {
+        return errs ? cback(errs) : cback(null);
+      }
+    }
+  }
+}
+
+
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..d4d67e3
--- /dev/null
+++ b/package.json
@@ -0,0 +1,30 @@
+{
+    "name" : "ncp",
+    "version" : "0.5.1",
+    "author": "AvianFlu <charlie at charlieistheman.com>",
+    "description" : "Asynchronous recursive file copy utility.",
+    "bin": {
+      "ncp": "./bin/ncp"
+    },
+    "devDependencies" : {
+        "mocha": "1.15.x",
+        "rimraf" : "1.0.x",
+        "read-dir-files" : "0.0.x"
+    },
+    "main": "./lib/ncp.js",
+    "repository" : {
+        "type" : "git",
+        "url" : "https://github.com/AvianFlu/ncp.git"
+    },
+    "keywords" : [
+        "cli",
+        "copy"
+    ],
+    "license" : "MIT",
+    "engine" : {
+        "node" : ">=0.6"
+    },
+    "scripts" : {
+      "test": "mocha -R spec"
+    }
+}
diff --git a/test/fixtures/src/a b/test/fixtures/src/a
new file mode 100644
index 0000000..802992c
--- /dev/null
+++ b/test/fixtures/src/a
@@ -0,0 +1 @@
+Hello world
diff --git a/test/fixtures/src/b b/test/fixtures/src/b
new file mode 100644
index 0000000..9f6bb18
--- /dev/null
+++ b/test/fixtures/src/b
@@ -0,0 +1 @@
+Hello ncp
diff --git a/test/fixtures/src/c b/test/fixtures/src/c
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/src/d b/test/fixtures/src/d
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/src/e b/test/fixtures/src/e
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/src/f b/test/fixtures/src/f
new file mode 100644
index 0000000..e69de29
diff --git a/test/fixtures/src/sub/a b/test/fixtures/src/sub/a
new file mode 100644
index 0000000..cf291b5
--- /dev/null
+++ b/test/fixtures/src/sub/a
@@ -0,0 +1 @@
+Hello nodejitsu
diff --git a/test/fixtures/src/sub/b b/test/fixtures/src/sub/b
new file mode 100644
index 0000000..e69de29
diff --git a/test/ncp.js b/test/ncp.js
new file mode 100644
index 0000000..d9798f9
--- /dev/null
+++ b/test/ncp.js
@@ -0,0 +1,86 @@
+
+
+var assert = require('assert'),
+    path = require('path'),
+    rimraf = require('rimraf'),
+    readDirFiles = require('read-dir-files'),
+    ncp = require('../').ncp;
+
+
+var fixtures = path.join(__dirname, 'fixtures'),
+    src = path.join(fixtures, 'src'),
+    out = path.join(fixtures, 'out');
+
+describe('ncp', function () {
+  before(function (cb) {
+    rimraf(out, function() {
+      ncp(src, out, cb);
+    });
+  });
+
+  describe('when copying a directory of files', function () {
+    it('files are copied correctly', function (cb) {
+      readDirFiles(src, 'utf8', function (srcErr, srcFiles) {
+        readDirFiles(out, 'utf8', function (outErr, outFiles) {
+          assert.ifError(srcErr);
+          assert.deepEqual(srcFiles, outFiles);
+          cb();
+        });
+      });
+    });
+  });
+
+  describe('when copying files using filter', function () {
+    before(function (cb) {
+      var filter = function(name) {
+        return name.substr(name.length - 1) != 'a';
+      };
+      rimraf(out, function () {
+        ncp(src, out, {filter: filter}, cb);
+      });
+    });
+
+    it('files are copied correctly', function (cb) {
+      readDirFiles(src, 'utf8', function (srcErr, srcFiles) {
+        function filter(files) {
+          for (var fileName in files) {
+            var curFile = files[fileName];
+            if (curFile instanceof Object)
+              return filter(curFile);
+            if (fileName.substr(fileName.length - 1) == 'a')
+              delete files[fileName];
+          }
+        }
+        filter(srcFiles);
+        readDirFiles(out, 'utf8', function (outErr, outFiles) {
+          assert.ifError(outErr);
+          assert.deepEqual(srcFiles, outFiles);
+          cb();
+        });
+      });
+    });
+  });
+
+  describe('when using clobber=false', function () {
+    it('the copy is completed successfully', function (cb) {
+      ncp(src, out, function() {
+        ncp(src, out, {clobber: false}, function(err) {
+          assert.ifError(err);
+          cb();
+        });
+      });
+    });
+  });
+
+  describe('when using transform', function () {
+    it('file descriptors are passed correctly', function (cb) {
+      ncp(src, out, {
+         transform: function(read,write,file) {
+            assert.notEqual(file.name, undefined);
+            assert.strictEqual(typeof file.mode,'number');
+            read.pipe(write);
+         }
+      }, cb);
+    });
+  });
+});

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-ncp.git



More information about the Pkg-javascript-commits mailing list