pax_global_header00006660000000000000000000000064130626172450014520gustar00rootroot0000000000000052 comment=8558616a681db87a77b2d9077ce5f855ceb46b87 move-concurrently-1.0.1/000077500000000000000000000000001306261724500152125ustar00rootroot00000000000000move-concurrently-1.0.1/.gitignore000066400000000000000000000000511306261724500171760ustar00rootroot00000000000000*~ .#* node_modules .nyc_output coverage move-concurrently-1.0.1/.travis.yml000066400000000000000000000001121306261724500173150ustar00rootroot00000000000000language: node_js sudo: false node_js: - "7" - "6" - "4" - "0.12" move-concurrently-1.0.1/LICENSE000066400000000000000000000013601306261724500162170ustar00rootroot00000000000000Copyright (c) 2017, Rebecca Turner Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. move-concurrently-1.0.1/README.md000066400000000000000000000040031306261724500164660ustar00rootroot00000000000000# move-concurrently Move files and directories. ``` const move = require('move-concurrently') move('/path/to/thing', '/new/path/thing').then(() => { // thing is now moved! }).catch(err => { // oh no! }) ``` Uses `rename` to move things as fast as possible. If you `move` across devices or on filesystems that don't support renaming large directories. That is, situations that result in `rename` returning the `EXDEV` error, then `move` will fallback to copy + delete. When recursively copying directories it will first try to rename the contents before falling back to copying. While this will be slightly slower in true cross-device scenarios, it is MUCH faster in cases where the filesystem can't handle directory renames. When copying ownership is maintained when running as root. Permissions are always maintained. On Windows, if symlinks are unavailable then junctions will be used. ## INTERFACE ### move(from, to, options) → Promise Recursively moves `from` to `to` and resolves its promise when finished. If `to` already exists then the promise will be rejected with an `EEXIST` error. Starts by trying to rename `from` to `to`. Options are: * maxConcurrency – (Default: `1`) The maximum number of concurrent copies to do at once. * isWindows - (Default: `process.platform === 'win32'`) If true enables Windows symlink semantics. This requires an extra `stat` to determine if the destination of a symlink is a file or directory. If symlinking a directory fails then we'll try making a junction instead. Options can also include dependency injection: * Promise - (Default: `global.Promise`) The promise implementation to use, defaults to Node's. * fs - (Default: `require('fs')`) The filesystem module to use. Can be used to use `graceful-fs` or to inject a mock. * writeStreamAtomic - (Default: `require('fs-write-stream-atomic')`) The implementation of `writeStreamAtomic` to use. Used to inject a mock. * getuid - (Default: `process.getuid`) A function that returns the current UID. Used to inject a mock. move-concurrently-1.0.1/appveyor.yml000066400000000000000000000005221306261724500176010ustar00rootroot00000000000000environment: matrix: - nodejs_version: "7" - nodejs_version: "6" - nodejs_version: "4" - nodejs_version: "0.12" platform: - x64 install: - ps: Install-Product node $env:nodejs_version $env:platform - npm config set spin false - npm install test_script: - npm test matrix: fast_finish: true build: off move-concurrently-1.0.1/move.js000066400000000000000000000037411306261724500165230ustar00rootroot00000000000000'use strict' module.exports = move var nodeFs = require('fs') var rimraf = require('rimraf') var validate = require('aproba') var copy = require('copy-concurrently') var RunQueue = require('run-queue') var extend = Object.assign || require('util')._extend function promisify (Promise, fn) { return function () { var args = [].slice.call(arguments) return new Promise(function (resolve, reject) { return fn.apply(null, args.concat(function (err, value) { if (err) { reject(err) } else { resolve(value) } })) }) } } function move (from, to, opts) { validate('SSO|SS', arguments) opts = extend({}, opts || {}) var Promise = opts.Promise || global.Promise var fs = opts.fs || nodeFs var rimrafAsync = promisify(Promise, rimraf) var renameAsync = promisify(Promise, fs.rename) opts.top = from var queue = new RunQueue({ maxConcurrency: opts.maxConcurrency, Promise: Promise }) opts.queue = queue opts.recurseWith = rename queue.add(0, rename, [from, to, opts]) return queue.run().then(function () { return remove(from) }, function (err) { // if the target already exists don't clobber it if (err.code === 'EEXIST' || err.code === 'EPERM') { return passThroughError() } else { return remove(to).then(passThroughError, passThroughError) } function passThroughError () { return Promise.reject(err) } }) function remove (target) { var opts = { unlink: fs.unlink, chmod: fs.chmod, stat: fs.stat, lstat: fs.lstat, rmdir: fs.rmdir, readdir: fs.readdir, glob: false } return rimrafAsync(target, opts) } function rename (from, to, opts, done) { return renameAsync(from, to).catch(function (err) { if (err.code !== 'EXDEV') { return Promise.reject(err) } else { return remove(to).then(function () { return copy.item(from, to, opts) }) } }) } } move-concurrently-1.0.1/package.json000066400000000000000000000021051306261724500174760ustar00rootroot00000000000000{ "name": "move-concurrently", "version": "1.0.1", "description": "Promises of moves of files or directories with rename, falling back to recursive rename/copy on EXDEV errors, with configurable concurrency and win32 junction support.", "main": "move.js", "scripts": { "test": "standard && tap --coverage test" }, "keywords": [ "move" ], "author": "Rebecca Turner (http://re-becca.org/)", "license": "ISC", "dependencies": { "copy-concurrently": "^1.0.0", "aproba": "^1.1.1", "fs-write-stream-atomic": "^1.0.8", "mkdirp": "^0.5.1", "rimraf": "^2.5.4", "run-queue": "^1.0.3" }, "devDependencies": { "standard": "^8.6.0", "tacks": "^1.2.6", "tap": "^10.1.1" }, "files": [ "move.js", "is-windows.js" ], "directories": { "test": "test" }, "repository": { "type": "git", "url": "git+https://github.com/npm/move-concurrently.git" }, "bugs": { "url": "https://github.com/npm/move-concurrently/issues" }, "homepage": "https://www.npmjs.com/package/move-concurrently" } move-concurrently-1.0.1/test/000077500000000000000000000000001306261724500161715ustar00rootroot00000000000000move-concurrently-1.0.1/test/integration.js000066400000000000000000000045471306261724500210640ustar00rootroot00000000000000'use strict' var fs = require('fs') var path = require('path') var test = require('tap').test var move = require('../move.js') var Tacks = require('tacks') var loadFromDir = require('tacks/load-from-dir') var areTheSame = require('tacks/tap.js').areTheSame var File = Tacks.File var Symlink = Tacks.Symlink var Dir = Tacks.Dir var basedir = path.join(__dirname, path.basename(__filename, '.js')) function testdir (dir) { return path.join(basedir, dir) } var testContent = { test: 'this' } var fixture = new Tacks(Dir({ 'test-dir': Dir({ subdir: Dir({ 'file1.json': File(testContent), 'file2.json': File(testContent) }), subdir2: Dir({ 'linky': Symlink(path.join('..', 'subdir')), 'file2.json': File(testContent), subsub: Dir({ 'aaaa': Symlink('bbbb'), 'bbbb': Dir(), 'zzzz.json': File(testContent) }) }) }), 'test-dir-symlink': Symlink('test-dir'), 'test-file.json': File(testContent), 'test-symlink.json': Symlink('test-file.json'), 'existing': File('') })) function readFile (file) { return JSON.parse(fs.readFileSync(testdir(file))) } function readSymlink (file) { return path.relative(basedir, path.resolve(basedir, fs.readlinkSync(testdir(file)))) } var testDirContent test('setup', function (t) { fixture.remove(basedir) fixture.create(basedir) testDirContent = loadFromDir(testdir('test-dir')) t.done() }) test('move', function (t) { t.plan(4) move(testdir('test-file.json'), testdir('move-test-file.json')).then(function () { t.isDeeply(readFile('move-test-file.json'), testContent, 'moved file content') }).catch(t.fail) move(testdir('test-symlink.json'), testdir('move-test-symlink.json')).then(function () { t.is(readSymlink('move-test-symlink.json'), 'test-file.json', 'moved symlink') }).catch(t.fail) move(testdir('test-dir-symlink'), testdir('move-test-dir-symlink')).then(function () { t.is(readSymlink('move-test-dir-symlink'), 'test-dir', 'moved dir symlink') }).catch(t.fail) move(testdir('test-dir'), testdir('move-test-dir')).then(function () { return move(testdir('move-test-dir'), testdir('test-dir')).then(function () { var moved = loadFromDir(testdir('test-dir')) areTheSame(t, moved, testDirContent, 'moved test directory') }) }).catch(t.fail) }) test('cleanup', function (t) { fixture.remove(basedir) t.done() }) move-concurrently-1.0.1/test/move.js000066400000000000000000000072501306261724500175010ustar00rootroot00000000000000'use strict' var stream = require('stream') var test = require('tap').test var validate = require('aproba') var extend = Object.assign || require('util')._extend function assign () { var args = [].slice.call(arguments) var base = args.shift() while (args.length) { base = extend(base, args.shift()) } return base } var move = require('../move.js') function enoent () { var err = new Error('ENOENT') err.code = 'ENOENT' return err } function eexist () { var err = new Error('EEXIST') err.code = 'EEXIST' return err } function eperm () { var err = new Error('EPERM') err.code = 'EPERM' return err } function exdev () { var err = new Error('EXDEV') err.code = 'EXDEV' return err } var isNothing = { isDirectory: function () { return false }, isSymbolicLink: function () { return false }, isFile: function () { return false }, isBlockDevice: function () { return false }, isCharacterDevice: function () { return false }, isFIFO: function () { return false }, isSocket: function () { return false } } var fileExists = assign({}, isNothing, {isFile: function () { return true }}) function nextTick (fn) { var args = [].slice.call(arguments, 1) process.nextTick(function () { fn.apply(null, args) }) } function rejects (t, code, msg, promise) { promise.then(function () { t.fail(msg) }).catch(function (err) { t.is(err.code, code, msg) }) } function resolves (t, msg, promise) { promise.then(function () { t.pass(msg) }).catch(function (err) { t.ifError(err, msg) }) } test('rename', function (t) { t.plan(5) var mockFs = { rename: function (from, to, cb) { validate('SSF', arguments) if (from === 'src:rename-ok') { nextTick(cb) } else if (from === 'src:dest-exists') { nextTick(cb, eexist()) } else if (from === 'src:dest-perm') { nextTick(cb, eperm()) } else if (from === 'src:src-does-not-exist') { nextTick(cb, enoent()) } else if (from === 'src:exdev') { nextTick(cb, exdev()) } else { t.fail('unexpected rename ' + from + ' → ' + to) nextTick(cb, enoent()) } }, lstat: function (to, cb) { validate('SF', arguments) if (to === 'src:rename-ok') { nextTick(cb, enoent()) } else if (to === 'dest:src-does-not-exist') { nextTick(cb, enoent()) } else if (to === 'src:exdev') { nextTick(cb, null, fileExists) } else if (to === 'dest:exdev') { nextTick(cb, enoent()) } else { t.fail('unexpected lstat ' + to) nextTick(cb, enoent()) } }, unlink: function (to, cb) { validate('SF', arguments) if (to === 'src:exdev') { nextTick(cb, eperm()) } else { t.fail('unexpected unlink ' + to) nextTick(cb, enoent()) } }, createReadStream: function () { var read = new stream.PassThrough() read.end('content') return read } } var mocks = { fs: mockFs, writeStreamAtomic: function (to, opts) { validate('SO', arguments) var write = new stream.PassThrough() write.on('data', function (chunk) { t.comment('WROTE: ' + chunk) }) write.on('finish', function () { write.emit('close') }) return write } } resolves(t, 'basic rename', move('src:rename-ok', 'dest:rename-ok', mocks)) rejects(t, 'ENOENT', 'source missing', move('src:src-does-not-exist', 'dest:src-does-not-exist', mocks)) rejects(t, 'EEXIST', 'dest exists', move('src:dest-exists', 'dest:dest-exists', mocks)) rejects(t, 'EPERM', 'dest perm error', move('src:dest-perm', 'dest:dest-perm', mocks)) resolves(t, 'fallback to copy', move('src:exdev', 'dest:exdev', mocks)) })